diff --git a/BRANCH_TODO b/BRANCH_TODO index 10c0b23bae73bc79ac03d1b365e1bea29ab36fdd..c51f87fd6916014f8f61687a37e4b21109fb5805 100644 --- a/BRANCH_TODO +++ b/BRANCH_TODO @@ -17,6 +17,7 @@ * implement {q} or delete {q} uses * make the generated dependencies.zig be dependencies.zon and don't put absolute paths in there + - and adjust dependencyInner to not openDir() ## Followup Issues * reduce the size of Maker.Step.Extended (make Run smaller) probably by using an arena per make diff --git a/build.zig b/build.zig index 1b247074835009a59d202051e2d98d7d82d5bbf8..5400169113ebc1ffac82a3d54df215ea1238dc03 100644 --- a/build.zig +++ b/build.zig @@ -16,6 +16,8 @@ const IoMode = enum { threaded, evented }; const ValueInterpretMode = enum { direct, by_name }; pub fn build(b: *std.Build) !void { + const arena = b.graph.arena; + const only_c = b.option(bool, "only-c", "Translate the Zig compiler to C code, with only the C backend enabled") orelse false; const target = b.standardTargetOptions(.{ .default_target = .{ @@ -35,7 +37,7 @@ pub fn build(b: *std.Build) !void { const no_bin = b.option(bool, "no-bin", "skip emitting compiler binary") orelse false; const enable_superhtml = b.option(bool, "enable-superhtml", "Check langref output HTML validity") orelse false; - const langref_file = generateLangRef(b); + const langref_file = try generateLangRef(b); const install_langref = b.addInstallFileWithDir(langref_file, .prefix, "doc/langref.html"); const check_langref = superHtmlCheck(b, langref_file); if (enable_superhtml) install_langref.step.dependOn(check_langref); @@ -262,7 +264,7 @@ pub fn build(b: *std.Build) !void { var code: u8 = undefined; const git_describe_untrimmed = b.runAllowFail(&[_][]const u8{ "git", - "-C", b.build_root.path orelse ".", // affects the --git-dir argument + "-C", b.fmt("{f}", .{b.root}), // affects the --git-dir argument "--git-dir", ".git", // affected by the -C argument "describe", "--match", "*.*.*", // "--tags", "--abbrev=9", @@ -308,7 +310,7 @@ pub fn build(b: *std.Build) !void { }, } }; - const version = try b.allocator.dupeSentinel(u8, version_slice, 0); + const version = try arena.dupeSentinel(u8, version_slice, 0); exe_options.addOption([:0]const u8, "version", version); if (enable_llvm) { @@ -316,7 +318,7 @@ pub fn build(b: *std.Build) !void { const io = b.graph.io; const cwd: Io.Dir = .cwd(); if (findConfigH(b, config_h_path_option)) |config_h_path| { - const file_contents = cwd.readFileAlloc(io, config_h_path, b.allocator, .limited(max_config_h_bytes)) catch unreachable; + const file_contents = cwd.readFileAlloc(io, config_h_path, arena, .limited(max_config_h_bytes)) catch unreachable; break :blk parseConfigH(b, file_contents); } else { std.log.warn("config.h could not be located automatically. Consider providing it explicitly via \"-Dconfig_h\"", .{}); @@ -976,11 +978,12 @@ fn addCxxKnownPath( errtxt: ?[]const u8, need_cpp_includes: bool, ) !void { - if (!std.process.can_spawn) - return error.RequiredLibraryNotFound; + if (!std.process.can_spawn) return error.RequiredLibraryNotFound; + + const arena = b.graph.arena; const path_padded = run: { - var args = std.array_list.Managed([]const u8).init(b.allocator); + var args = std.array_list.Managed([]const u8).init(arena); try args.append(ctx.cxx_compiler); var it = std.mem.tokenizeAny(u8, ctx.cxx_compiler_arg1, &std.ascii.whitespace); while (it.next()) |arg| try args.append(arg); @@ -1049,6 +1052,7 @@ const CMakeConfig = struct { const max_config_h_bytes = 1 * 1024 * 1024; fn findConfigH(b: *std.Build, config_h_path_option: ?[]const u8) ?[]const u8 { + const arena = b.graph.arena; const io = b.graph.io; const cwd: Io.Dir = .cwd(); @@ -1073,7 +1077,7 @@ fn findConfigH(b: *std.Build, config_h_path_option: ?[]const u8) ?[]const u8 { if (config_h_or_err) |*file| { file.close(io); return fs.path.join( - b.allocator, + arena, &[_][]const u8{ check_dir, "config.h" }, ) catch unreachable; } else |e| switch (e) { @@ -1198,7 +1202,8 @@ fn parseConfigH(b: *std.Build, config_h_text: []const u8) ?CMakeConfig { } fn toNativePathSep(b: *std.Build, s: []const u8) []u8 { - const duplicated = b.allocator.dupe(u8, s) catch unreachable; + const arena = b.graph.arena; + const duplicated = arena.dupe(u8, s) catch unreachable; for (duplicated) |*byte| switch (byte.*) { '/' => byte.* = fs.path.sep, else => {}, @@ -1487,8 +1492,9 @@ const llvm_libs_xtensa = [_][]const u8{ "LLVMXtensaInfo", }; -fn generateLangRef(b: *std.Build) std.Build.LazyPath { +fn generateLangRef(b: *std.Build) !std.Build.LazyPath { const io = b.graph.io; + const arena = b.graph.arena; const doctest_exe = b.addExecutable(.{ .name = "doctest", @@ -1499,10 +1505,7 @@ fn generateLangRef(b: *std.Build) std.Build.LazyPath { }), }); - const langref_path: std.Build.Cache.Path = .{ - .root_dir = b.build_root, - .sub_path = "doc/langref", - }; + const langref_path = try b.root.join(arena, "doc/langref"); var dir = langref_path.root_dir.handle.openDir(io, langref_path.sub_path, .{ .iterate = true }) catch |err| std.debug.panic("unable to open directory {f}: {t}", .{ langref_path, err }); @@ -1518,17 +1521,22 @@ fn generateLangRef(b: *std.Build) std.Build.LazyPath { const out_basename = b.fmt("{s}.out", .{std.fs.path.stem(entry.name)}); const cmd = b.addRunArtifact(doctest_exe); - cmd.addArgs(&.{ - "--zig", b.graph.zig_exe, - // TODO: enhance doctest to use "--listen=-" rather than operating - // in a temporary directory - "--cache-root", b.cache_root.path orelse ".", - }); - cmd.addArgs(&.{ "--zig-lib-dir", b.fmt("{f}", .{b.graph.zig_lib_directory}) }); - cmd.addArgs(&.{"-i"}); + + cmd.addArg("--zig"); + cmd.addFileArg(.zig_exe); + + // TODO: enhance doctest to use "--listen=-" rather than operating in a + // temporary directory + cmd.addArg("--cache-root"); + cmd.addFileArg(.cache_root); + + cmd.addArg("--zig-lib-dir"); + cmd.addFileArg(.zig_lib); + + cmd.addArg("-i"); cmd.addFileArg(b.path(b.fmt("doc/langref/{s}", .{entry.name}))); - cmd.addArgs(&.{"-o"}); + cmd.addArg("-o"); _ = wf.addCopyFile(cmd.addOutputFileArg(out_basename), out_basename); } diff --git a/lib/compiler/Maker.zig b/lib/compiler/Maker.zig index 81fb2fa71c2080d5200d82897f324530cce39e78..e8c409980087043a114cd3dc91131b727e53f9bb 100644 --- a/lib/compiler/Maker.zig +++ b/lib/compiler/Maker.zig @@ -1779,6 +1779,7 @@ pub fn relativePath(maker: *const Maker, relative: Configuration.LazyPath.Relati const graph = maker.graph; const c = &maker.scanned_config.configuration; const sub_path = relative.sub_path.slice(c); + if (relative.flags.base == .zig_exe and sub_path.len != 0) @panic("TODO"); return switch (relative.flags.base) { .cwd => .{ .root_dir = .cwd(), @@ -1796,6 +1797,14 @@ pub fn relativePath(maker: *const Maker, relative: Configuration.LazyPath.Relati .root_dir = graph.build_root_directory, .sub_path = sub_path, }, + .zig_exe => .{ + .root_dir = .cwd(), + .sub_path = graph.zig_exe, + }, + .zig_lib => .{ + .root_dir = graph.zig_lib_directory, + .sub_path = sub_path, + }, }; } diff --git a/lib/compiler/Maker/Graph.zig b/lib/compiler/Maker/Graph.zig index 3a3136d8783c12e12424cceff48085aa6784f026..4e1c05b8c56ffdc554f9e52510a14a887c99b80a 100644 --- a/lib/compiler/Maker/Graph.zig +++ b/lib/compiler/Maker/Graph.zig @@ -75,10 +75,11 @@ pub fn handleVerbose( ) error{OutOfMemory}!void { if (!graph.verbose) return; const arena = graph.arena; - const text = try std.zig.allocPrintCmd(arena, cwd, if (opt_env) |env| .{ - .child = env, - .parent = &graph.environ_map, - } else null, argv); + const text = try std.zig.allocPrintCmd(arena, argv, .{ + .cwd = cwd, + .parent_env = &graph.environ_map, + .child_env = opt_env, + }); defer arena.free(text); std.log.scoped(.verbose).info("{s}", .{text}); } diff --git a/lib/compiler/Maker/Step.zig b/lib/compiler/Maker/Step.zig index 9d4257a2967ddb3ea44ceaf437b09cb87bc776b4..d67ee25ebc839cfb89c7fd30134a004ba301882e 100644 --- a/lib/compiler/Maker/Step.zig +++ b/lib/compiler/Maker/Step.zig @@ -70,6 +70,7 @@ pub const Extended = union(enum) { compile: Compile, config_header: Todo, fail: Todo, + find_program: Todo, fmt: Todo, install_artifact: InstallArtifact, install_dir: Todo, @@ -89,6 +90,7 @@ pub const Extended = union(enum) { .compile => .{ .compile = .{} }, .config_header => .{ .config_header = .{} }, .fail => .{ .fail = .{} }, + .find_program => .{ .find_program = .{} }, .fmt => .{ .fmt = .{} }, .install_artifact => .{ .install_artifact = .{} }, .install_dir => .{ .install_dir = .{} }, @@ -314,7 +316,7 @@ pub fn captureChildProcess( // If an error occurs, it's happened in this command: assert(s.result_failed_command == null); - s.result_failed_command = try std.zig.allocPrintCmd(gpa, .inherit, null, argv); + s.result_failed_command = try std.zig.allocPrintCmd(gpa, argv, .{}); try handleChildProcUnsupported(s, maker); try graph.handleVerbose(.inherit, null, argv); @@ -382,7 +384,7 @@ pub fn evalZigProcess( // If an error occurs, it's happened in this command: assert(s.result_failed_command == null); - s.result_failed_command = try std.zig.allocPrintCmd(gpa, .inherit, null, argv); + s.result_failed_command = try std.zig.allocPrintCmd(gpa, argv, .{}); if (s.getZigProcess()) |zp| update: { assert(watch); diff --git a/lib/compiler/Maker/Step/Run.zig b/lib/compiler/Maker/Step/Run.zig index c381c480cc1fc0a24101af657d6351ee0aad4918..30ffc13dc45d0e885c40129f4b11e41267606e74 100644 --- a/lib/compiler/Maker/Step/Run.zig +++ b/lib/compiler/Maker/Step/Run.zig @@ -2129,10 +2129,11 @@ fn spawnChildAndCollect( // If an error occurs, it's caused by this command: assert(step.result_failed_command == null); - step.result_failed_command = try std.zig.allocPrintCmd(arena, child_cwd, .{ - .child = environ_map, - .parent = &graph.environ_map, - }, argv); + step.result_failed_command = try std.zig.allocPrintCmd(arena, argv, .{ + .cwd = child_cwd, + .child_env = environ_map, + .parent_env = &graph.environ_map, + }); try step.handleChildProcUnsupported(maker); diff --git a/lib/compiler/Maker/WebServer.zig b/lib/compiler/Maker/WebServer.zig index 9ad6580868017b134d71bfef58d034e287f495ab..de30ca30730b0d859dd599ce6165344d606e1281 100644 --- a/lib/compiler/Maker/WebServer.zig +++ b/lib/compiler/Maker/WebServer.zig @@ -714,7 +714,7 @@ fn buildClientWasm(ws: *WebServer, arena: Allocator, optimize: std.builtin.Optim if (code != 0) { log.err( "the following command exited with error code {d}:\n{s}", - .{ code, try std.zig.allocPrintCmd(arena, .inherit, null, argv.items) }, + .{ code, try std.zig.allocPrintCmd(arena, argv.items, .{}) }, ); return error.WasmCompilationFailed; } @@ -722,21 +722,21 @@ fn buildClientWasm(ws: *WebServer, arena: Allocator, optimize: std.builtin.Optim .signal => |sig| { log.err( "the following command terminated with signal {t}:\n{s}", - .{ sig, try std.zig.allocPrintCmd(arena, .inherit, null, argv.items) }, + .{ sig, try std.zig.allocPrintCmd(arena, argv.items, .{}) }, ); return error.WasmCompilationFailed; }, .stopped => |sig| { log.err( "the following command stopped unexpectedly with signal {t}:\n{s}", - .{ sig, try std.zig.allocPrintCmd(arena, .inherit, null, argv.items) }, + .{ sig, try std.zig.allocPrintCmd(arena, argv.items, .{}) }, ); return error.WasmCompilationFailed; }, .unknown => { log.err( "the following command terminated unexpectedly:\n{s}", - .{try std.zig.allocPrintCmd(arena, .inherit, null, argv.items)}, + .{try std.zig.allocPrintCmd(arena, argv.items, .{})}, ); return error.WasmCompilationFailed; }, @@ -746,14 +746,14 @@ fn buildClientWasm(ws: *WebServer, arena: Allocator, optimize: std.builtin.Optim try result_error_bundle.renderToStderr(io, .{}, .auto); log.err("the following command failed with {d} compilation errors:\n{s}", .{ result_error_bundle.errorMessageCount(), - try std.zig.allocPrintCmd(arena, .inherit, null, argv.items), + try std.zig.allocPrintCmd(arena, argv.items, .{}), }); return error.WasmCompilationFailed; } const base_path = result orelse { log.err("child process failed to report result\n{s}", .{ - try std.zig.allocPrintCmd(arena, .inherit, null, argv.items), + try std.zig.allocPrintCmd(arena, argv.items, .{}), }); return error.WasmCompilationFailed; }; diff --git a/lib/compiler/configurer.zig b/lib/compiler/configurer.zig index 6b034e7cabfb82240e726fd88cc48a153197cc99..96d1072c392550b21109cf6d93f969490bd1749b 100644 --- a/lib/compiler/configurer.zig +++ b/lib/compiler/configurer.zig @@ -62,11 +62,23 @@ pub fn main(init: process.Init.Minimal) !void { assert(try graph.wip_configuration.addString("") == .empty); assert(try graph.wip_configuration.addString("root") == .root); - const builder = try std.Build.create(&graph, dependencies.root_deps); - - var color: Color = .auto; var arg_i: usize = 1; // Skip own executable name. + const build_root_sub_path = expectArgOrFatal(args, &arg_i, "--build-root"); + + const cwd: Io.Dir = .cwd(); + + const build_root: std.Build.Cache.Path = .{ + .root_dir = .{ + .handle = try cwd.openDir(io, build_root_sub_path, .{}), + .path = build_root_sub_path, + }, + }; + + const builder = try std.Build.create(&graph, build_root, dependencies.root_deps); + + var color: Color = .auto; + while (nextArg(args, &arg_i)) |arg| { if (mem.cutPrefix(u8, arg, "-D")) |option_contents| { if (option_contents.len == 0) @@ -98,6 +110,8 @@ pub fn main(init: process.Init.Minimal) !void { // but it is handled by the parent process. The build runner // only sees this flag. graph.system_package_mode = true; + } else if (mem.eql(u8, arg, "--verbose")) { + graph.verbose = true; } else { fatalWithHint("unrecognized argument: {s}", .{arg}); } @@ -183,6 +197,12 @@ const Serialize = struct { .sub_path = sub_path, })); }, + .relative => |relative| i: { + break :i try wc.addExtra(@as(Configuration.LazyPath.Relative, .{ + .flags = .{ .base = relative.base }, + .sub_path = relative.sub_path, + })); + }, .dependency => |dependency| i: { const sub_path = try wc.addString(dependency.sub_path); break :i try wc.addExtra(@as(Configuration.LazyPath.SourcePath, .{ @@ -840,6 +860,7 @@ fn serialize(b: *std.Build, wc: *Configuration.Wip, writer: *Io.Writer) !void { .install_dir => @panic("TODO"), .remove_dir => @panic("TODO"), .fail => @panic("TODO"), + .find_program => @panic("TODO"), .fmt => @panic("TODO"), .translate_c => @panic("TODO"), .write_file => @panic("TODO"), @@ -1038,6 +1059,13 @@ fn nextArg(args: []const [:0]const u8, idx: *usize) ?[:0]const u8 { return args[idx.*]; } +fn expectArgOrFatal(args: []const [:0]const u8, index_ptr: *usize, first: []const u8) []const u8 { + const next_arg = nextArg(args, index_ptr) orelse fatal("missing {q} argument", .{first}); + if (!mem.eql(u8, first, next_arg)) fatal("expected {q} instead of {q}", .{ first, next_arg }); + const arg = nextArg(args, index_ptr) orelse fatal("expected argument after {q}", .{first}); + return arg; +} + const ErrorStyle = enum { verbose, minimal, diff --git a/lib/std/Build.zig b/lib/std/Build.zig index b6b3263bfa5a57e0fdc89a24f1569932b8e866f4..8bc4f6af407979e3f48feb69cd803520154065bd 100644 --- a/lib/std/Build.zig +++ b/lib/std/Build.zig @@ -34,6 +34,8 @@ available_options_map: std.array_hash_map.String(AvailableOption) = .empty, invalid_user_input: bool, default_step: *Step, top_level_steps: std.StringArrayHashMapUnmanaged(*Step.TopLevel), +/// Path to the directory containing build.zig. +root: Cache.Path, debug_log_scopes: []const []const u8 = &.{}, /// Number of stack frames captured when a `StackTrace` is recorded for debug purposes, /// in particular at `Step` creation. @@ -85,6 +87,7 @@ pub const Graph = struct { dependency_cache: InitializedDepMap = .empty, allow_so_scripts: ?bool = null, time_report: bool = false, + verbose: bool = false, /// Similar to the `Io.Terminal.Mode` returned by `Io.lockStderr`, but also /// respects the '--color' flag. stderr_mode: ?Io.Terminal.Mode = null, @@ -117,6 +120,20 @@ pub const Graph = struct { for (array, strings) |*dest, source| dest.* = dupeString(graph, source); return array; } + + /// An absolute path or a path relative to the current working directory of + /// the build runner process. + /// + /// Use of this function indicates a dependency on the host system. + pub fn cwdRelativePath(graph: *Graph, sub_path: []const u8) LazyPath { + const wc = &graph.wip_configuration; + return .{ + .relative = .{ + .base = .cwd, + .sub_path = wc.addString(sub_path) catch @panic("OOM"), + }, + }; + } }; const AvailableDeps = []const struct { []const u8, []const u8 }; @@ -170,13 +187,6 @@ const InitializedDepContext = struct { } }; -pub const RunError = error{ - ReadFailure, - ExitCodeFailure, - ProcessTerminated, - ExecNotSupported, -} || std.process.SpawnError; - const UserInputOptionsMap = StringHashMap(UserInputOption); const AvailableOption = struct { @@ -204,6 +214,7 @@ const UserValue = union(enum) { pub fn create( graph: *Graph, + root: Cache.Path, available_deps: AvailableDeps, ) error{OutOfMemory}!*Build { const arena = graph.arena; @@ -211,6 +222,7 @@ pub fn create( const b = try arena.create(Build); b.* = .{ .graph = graph, + .root = root, .invalid_user_input = false, .allocator = arena, .user_input_options = UserInputOptionsMap.init(arena), @@ -247,6 +259,7 @@ pub fn create( fn createChild( parent: *Build, dep_name: []const u8, + root: Cache.Path, pkg_hash: []const u8, pkg_deps: AvailableDeps, user_input_options: UserInputOptionsMap, @@ -255,6 +268,7 @@ fn createChild( const child = try allocator.create(Build); child.* = .{ .graph = parent.graph, + .root = root, .allocator = allocator, .install_tls = .{ .step = .init(.{ @@ -1143,7 +1157,7 @@ pub fn step(b: *Build, name: []const u8, description: []const u8) *Step { .description = b.dupe(description), }; const gop = b.top_level_steps.getOrPut(b.allocator, name) catch @panic("OOM"); - if (gop.found_existing) std.debug.panic("A top-level step with name \"{s}\" already exists", .{name}); + if (gop.found_existing) panic("A top-level step with name \"{s}\" already exists", .{name}); gop.key_ptr.* = step_info.step.name; gop.value_ptr.* = step_info; @@ -1366,7 +1380,8 @@ pub fn addUserInputOption(b: *Build, name_raw: []const u8, value_raw: []const u8 } pub fn addUserInputFlag(b: *Build, name_raw: []const u8) error{OutOfMemory}!bool { - const name = b.dupe(name_raw); + const graph = b.graph; + const name = graph.dupeString(name_raw); const gop = try b.user_input_options.getOrPut(name); if (!gop.found_existing) { gop.value_ptr.* = .{ @@ -1388,7 +1403,7 @@ pub fn addUserInputFlag(b: *Build, name_raw: []const u8) error{OutOfMemory}!bool return true; }, .lazy_path => |lp| { - log.err("Flag '-D{s}' conflicts with option '-D{s}={s}'.", .{ name, name, lp.getDisplayName() }); + log.err("Flag '-D{s}' conflicts with option '-D{s}={f}'.", .{ name, name, lp.fmt(graph) }); return true; }, @@ -1538,7 +1553,7 @@ pub fn truncateFile(b: *Build, dest_path: []const u8) (Io.Dir.CreateDirError || /// References a file or directory relative to the source root. pub fn path(b: *Build, sub_path: []const u8) LazyPath { if (fs.path.isAbsolute(sub_path)) { - std.debug.panic("sub_path is expected to be relative to the build root, but was this absolute path: '{s}'. It is best avoid absolute paths, but if you must, it is supported by LazyPath.cwd_relative", .{ + panic("sub_path is expected to be relative to the build root, but was this absolute path: '{s}'. It is best avoid absolute paths, but if you must, it is supported by LazyPath.cwd_relative", .{ sub_path, }); } @@ -1560,117 +1575,164 @@ pub fn fmt(b: *Build, comptime format: []const u8, args: anytype) []u8 { return std.fmt.allocPrint(b.allocator, format, args) catch @panic("OOM"); } -fn supportedWindowsProgramExtension(ext: []const u8) bool { - inline for (@typeInfo(std.process.WindowsExtension).@"enum".fields) |field| { - if (std.ascii.eqlIgnoreCase(ext, "." ++ field.name)) return true; - } - return false; -} - -fn tryFindProgram(b: *Build, full_path: []const u8) ?[]const u8 { - const io = b.graph.io; - const arena = b.allocator; - - if (b.build_root.handle.realPathFileAlloc(io, full_path, arena)) |p| { - return p; - } else |err| switch (err) { - error.OutOfMemory => @panic("OOM"), - else => {}, - } - - if (builtin.os.tag == .windows) { - if (b.graph.environ_map.get("PATHEXT")) |PATHEXT| { - var it = mem.tokenizeScalar(u8, PATHEXT, fs.path.delimiter); - - while (it.next()) |ext| { - if (!supportedWindowsProgramExtension(ext)) continue; - - return b.build_root.handle.realPathFileAlloc( - io, - b.fmt("{s}{s}", .{ full_path, ext }), - arena, - ) catch |err| switch (err) { - error.OutOfMemory => @panic("OOM"), - else => continue, - }; - } - } - } - - return null; -} - -pub fn findProgram(b: *Build, names: []const []const u8, paths: []const []const u8) LazyPath { - _ = b; - _ = names; - _ = paths; - @panic("TODO rework findProgram to be based on LazyPath"); +/// Creates an anonymous `Step` that searches for an executable on the host that +/// has more than one possible name. +/// +/// Names are searched in order, observing search prefixes first and then PATH +/// environment variable. +/// +/// Returns the `LazyPath` of the found executable. The search only takes place +/// if the `LazyPath` will be used by a depending `Step`. +pub fn findProgram(b: *Build, names: []const []const u8) LazyPath { + const graph = b.graph; + const wc = &graph.wip_configuration; + const string_list = wc.addStringList(names) catch @panic("OOM"); + _ = string_list; + @panic("TODO"); } +/// Deprecated; use `runFallible`. pub fn runAllowFail( b: *Build, argv: []const []const u8, - out_code: *u8, - stderr_behavior: std.process.SpawnOptions.StdIo, -) RunError![]u8 { + exit_code: *u8, + stderr_behavior: process.SpawnOptions.StdIo, +) anyerror![]u8 { + if (!process.can_spawn) return error.ExecNotSupported; + switch (runFallible(b, argv, .{ + .stderr_behavior = stderr_behavior, + })) { + .success => |stdout| return stdout, + .spawn_failed => |err| return err, + .bad_exit_code => |code| { + exit_code.* = code; + return error.ExitCodeFailure; + }, + .crashed => { + exit_code.* = 255; + return error.ProcessTerminated; + }, + } +} + +pub const RunOptions = struct { + stderr_behavior: process.SpawnOptions.StdIo = .inherit, + /// Fail the configuration if stdout is larger than this. + stdout_limit: Io.Limit = .limited(1_000_000), + /// Set to change the current working directory when spawning the child + /// process. + cwd: process.Child.Cwd = .inherit, + /// Replaces the child environment when provided. The PATH value from here + /// is not used to resolve `argv[0]`; that resolution always uses parent + /// environment. + environ_map: ?*const process.Environ.Map = null, + expand_arg0: process.ArgExpansion = .no_expand, +}; + +pub const RunResult = union(enum) { + /// Thild process exited with code 0, writing this stdout. + success: []u8, + /// The child process could not be created. + spawn_failed: process.SpawnError, + /// The child process indicated failure. + bad_exit_code: u8, + /// The child process terminated abnormally. + crashed, +}; + +/// Executes the provided command immediately, allowing failure. +/// +/// If the program exits successfully, stdout is returned. Otherwise, returns +/// an indication of failure. +/// +/// See also: +/// * `run`. +pub fn runFallible(b: *Build, argv: []const []const u8, options: RunOptions) RunResult { assert(argv.len != 0); - if (!process.can_spawn) - return error.ExecNotSupported; - const graph = b.graph; const io = graph.io; const arena = graph.arena; - const max_output_size = 400 * 1024; + const print_opts: std.zig.AllocPrintCmdOptions = .{ + .cwd = options.cwd, + .child_env = options.environ_map, + .parent_env = &graph.environ_map, + }; + if (graph.verbose) { - const text = std.zig.allocPrintCmd(arena, .inherit, null, argv); + const text = std.zig.allocPrintCmd(arena, argv, print_opts) catch @panic("OOM"); std.log.scoped(.verbose).info("{s}", .{text}); } - var child = try std.process.spawn(io, .{ + var child = process.spawn(io, .{ .argv = argv, - .environ_map = &graph.environ_map, .stdin = .ignore, .stdout = .pipe, - .stderr = stderr_behavior, - }); + .stderr = options.stderr_behavior, + .cwd = options.cwd, + .environ_map = &graph.environ_map, + .expand_arg0 = options.expand_arg0, + }) catch |err| return .{ .spawn_failed = err }; var stdout_reader = child.stdout.?.readerStreaming(io, &.{}); - const stdout = stdout_reader.interface.allocRemaining(arena, .limited(max_output_size)) catch { - return error.ReadFailure; + const stdout = stdout_reader.interface.allocRemaining(arena, options.stdout_limit) catch |err| switch (err) { + error.ReadFailed => panic("failed to read from child: {t}", .{stdout_reader.err.?}), + else => |e| panic("failed to read from child: {t}", .{e}), }; - errdefer arena.free(stdout); - const term = try child.wait(io); - switch (term) { - .exited => |code| { - if (code != 0) { - out_code.* = @as(u8, @truncate(code)); - return error.ExitCodeFailure; - } - return stdout; - }, - .signal, .stopped => |sig| { - out_code.* = @as(u8, @truncate(@intFromEnum(sig))); - return error.ProcessTerminated; - }, - .unknown => |code| { - out_code.* = @as(u8, @truncate(code)); - return error.ProcessTerminated; + const term = child.wait(io) catch @panic("unexpected"); + + return switch (term) { + .exited => |code| switch (code) { + 0 => .{ .success = stdout }, + else => .{ .bad_exit_code = code }, }, - } + .signal, .stopped, .unknown => .crashed, + }; } -/// This is a helper function to be called from build.zig scripts, *not* from -/// inside step make() functions. If any errors occur, it fails the build with -/// a helpful message. +/// Executes the provided command immediately. +/// +/// If the program exits successfully, stdout is returned. Otherwise, fails the +/// build with a helpful message. +/// +/// See also: +/// * `runFallible`. pub fn run(b: *Build, argv: []const []const u8) []u8 { - var code: u8 = undefined; - return b.runAllowFail(argv, &code, .inherit) catch |err| process.fatal( - "the following command failed with {t}:\n{s}", - .{ err, Step.allocPrintCmd(b.allocator, .inherit, null, argv) catch @panic("OOM") }, - ); + const graph = b.graph; + const arena = graph.arena; + switch (b.runFallible(argv, .{ + .stderr_behavior = .inherit, + })) { + .success => |stdout| return stdout, + .spawn_failed => |err| process.fatal("the following command failed with {t}:\n{s}", .{ + err, std.zig.allocPrintCmd(arena, argv, .{}) catch @panic("OOM"), + }), + .bad_exit_code => |code| process.fatal("the following command exited with code {d}:\n{s}", .{ + code, std.zig.allocPrintCmd(arena, argv, .{}) catch @panic("OOM"), + }), + .crashed => process.fatal("the following command crashed:\n{s}", .{ + std.zig.allocPrintCmd(arena, argv, .{}) catch @panic("OOM"), + }), + } +} + +/// Adds additional paths, equivalent to the `--search-prefix` arguments +/// provided by the user. Paths added with this function have lower precedence +/// than the ones specified by the user on the command line. +/// +/// It is generally best practice to avoid calling this function, instead +/// relying on the user to provide these paths via the standard build system +/// interface. However, when integrating with other build systems, the user may +/// have already provided the information to the other build system, and thus +/// it is desirable to use that same information without requiring the user to +/// provide it again. +pub fn addSearchPrefix(b: *Build, search_prefix: []const u8) void { + _ = b; + _ = search_prefix; + @panic("TODO"); + //b.search_prefixes.append(b.allocator, b.dupePath(search_prefix)) catch @panic("OOM"); } pub const Dependency = struct { @@ -1727,8 +1789,8 @@ fn findPkgHashOrFatal(b: *Build, name: []const u8) []const u8 { if (mem.eql(u8, dep[0], name)) return dep[1]; } std.log.info("all dependencies used by build.zig must be declared in corresponding build.zig.zon", .{}); - if (b.pkg_hash.len == 0) std.debug.panic("no dependency named {s}", .{name}); - std.debug.panic("no dependency named {s} in {s} ({s})", .{ name, b.dep_prefix, b.pkg_hash }); + if (b.pkg_hash.len == 0) panic("no dependency named {s}", .{name}); + panic("no dependency named {s} in {s} ({s})", .{ name, b.dep_prefix, b.pkg_hash }); } inline fn findImportPkgHashOrFatal(b: *Build, comptime asking_build_zig: type, comptime dep_name: []const u8) []const u8 { @@ -1741,7 +1803,7 @@ inline fn findImportPkgHashOrFatal(b: *Build, comptime asking_build_zig: type, c if (@hasDecl(pkg, "build_zig") and pkg.build_zig == asking_build_zig) break .{ pkg_hash, pkg.deps }; } else .{ "", deps.root_deps }; if (!std.mem.eql(u8, b_pkg_hash, b.pkg_hash)) { - std.debug.panic("'{}' is not the struct that corresponds to '{s}'", .{ + panic("'{}' is not the struct that corresponds to '{s}'", .{ asking_build_zig, b.pathFromRoot("build.zig"), }); } @@ -1750,7 +1812,7 @@ inline fn findImportPkgHashOrFatal(b: *Build, comptime asking_build_zig: type, c }; const full_path = b.pathFromRoot("build.zig.zon"); - std.debug.panic("no dependency named '{s}' in '{s}'. All packages used in build.zig must be declared in this file", .{ dep_name, full_path }); + panic("no dependency named '{s}' in '{s}'. All packages used in build.zig must be declared in this file", .{ dep_name, full_path }); } fn markNeededLazyDep(b: *Build, pkg_hash: []const u8) void { @@ -1799,7 +1861,7 @@ pub fn dependency(b: *Build, name: []const u8, args: anytype) *Dependency { if (mem.eql(u8, decl.name, pkg_hash)) { const pkg = @field(deps.packages, decl.name); if (@hasDecl(pkg, "available")) { - std.debug.panic("dependency '{s}{s}' is marked as lazy in build.zig.zon which means it must use the lazyDependency function instead", .{ b.dep_prefix, name }); + panic("dependency '{s}{s}' is marked as lazy in build.zig.zon which means it must use the lazyDependency function instead", .{ b.dep_prefix, name }); } return dependencyInner(b, name, pkg.build_root, if (@hasDecl(pkg, "build_zig")) pkg.build_zig else null, pkg_hash, pkg.deps, args); } @@ -1866,7 +1928,7 @@ pub fn dependencyFromBuildZig( } const full_path = b.pathFromRoot("build.zig.zon"); - std.debug.panic("'{}' is not a build.zig struct of a dependency in '{s}'", .{ build_zig, full_path }); + panic("'{}' is not a build.zig struct of a dependency in '{s}'", .{ build_zig, full_path }); } fn userValuesAreSame(lhs: UserValue, rhs: UserValue) bool { @@ -1960,14 +2022,23 @@ fn dependencyInner( pkg_deps: AvailableDeps, args: anytype, ) *Dependency { - const user_input_options = userInputOptionsFromArgs(b.allocator, args); + const io = b.graph.io; + const arena = b.graph.arena; + const user_input_options = userInputOptionsFromArgs(arena, args); if (b.graph.dependency_cache.getContext(.{ .build_root_string = build_root_string, .user_input_options = user_input_options, - }, .{ .allocator = b.graph.arena })) |dep| - return dep; + }, .{ .allocator = arena })) |dep| return dep; - const sub_builder = b.createChild(name, pkg_hash, pkg_deps, user_input_options) catch + const dep_root: Cache.Path = .{ + .root_dir = .{ + .path = build_root_string, + .handle = Io.Dir.cwd().openDir(io, build_root_string, .{}) catch |err| + process.fatal("unable to open {s}: {t}", .{ build_root_string, err }), + }, + }; + + const sub_builder = b.createChild(name, dep_root, pkg_hash, pkg_deps, user_input_options) catch @panic("unhandled error"); if (build_zig) |bz| { sub_builder.runBuild(bz) catch @panic("unhandled error"); @@ -1977,13 +2048,13 @@ fn dependencyInner( } } - const dep = b.allocator.create(Dependency) catch @panic("OOM"); + const dep = arena.create(Dependency) catch @panic("OOM"); dep.* = .{ .builder = sub_builder }; b.graph.dependency_cache.putContext(b.graph.arena, .{ .build_root_string = build_root_string, .user_input_options = user_input_options, - }, dep, .{ .allocator = b.graph.arena }) catch @panic("OOM"); + }, dep, .{ .allocator = arena }) catch @panic("OOM"); return dep; } @@ -2046,14 +2117,7 @@ pub const LazyPath = union(enum) { sub_path: []const u8 = "", }, - /// An absolute path or a path relative to the current working directory of - /// the build runner process. - /// - /// This is uncommon but used for system environment paths such as `--zig-lib-dir` which - /// ignore the file system path of build.zig and instead are relative to the directory from - /// which `zig build` was invoked. - /// - /// Use of this tag indicates a dependency on the host system. + /// Deprecated; call `Graph.cwdRelativePath` instead. cwd_relative: []const u8, dependency: struct { @@ -2061,6 +2125,19 @@ pub const LazyPath = union(enum) { sub_path: []const u8, }, + relative: struct { + base: Configuration.Path.Base, + sub_path: Configuration.String = .empty, + }, + + /// Path to the Zig executable being used to execute "zig build". + pub const zig_exe: LazyPath = .{ .relative = .{ .base = .zig_exe } }; + /// Path to the "lib/" directory from the Zig installation being used to + /// execute "zig build". + pub const zig_lib: LazyPath = .{ .relative = .{ .base = .zig_lib } }; + /// Path to the project's local cache directory (usually called ".zig-cache"). + pub const cache_root: LazyPath = .{ .relative = .{ .base = .local_cache } }; + /// Returns a lazy path referring to the directory containing this path. /// /// The dirname is not allowed to escape the logical root for underlying path. @@ -2147,21 +2224,33 @@ pub const LazyPath = union(enum) { }; } - /// Returns a string that can be shown to represent the file source. - /// Either returns the path, `"generated"`, or `"dependency"`. - pub fn getDisplayName(lazy_path: LazyPath) []const u8 { - return switch (lazy_path) { - .src_path => |sp| sp.sub_path, - .cwd_relative => |p| p, - .generated => "generated", - .dependency => "dependency", - }; + pub const Format = struct { + graph: *const Graph, + lazy_path: *const LazyPath, + + pub fn format(f: Format, w: *Io.Writer) Io.Writer.Error!void { + switch (f.lazy_path.*) { + .src_path => |sp| try w.writeAll(sp.sub_path), + .cwd_relative => |p| try w.writeAll(p), + .generated => try w.writeAll("generated"), + .dependency => try w.writeAll("dependency"), + .relative => |r| { + const wc = &f.graph.wip_configuration; + try w.writeAll(@tagName(r.base)); + try w.writeAll(wc.stringSlice(r.sub_path)); + }, + } + } + }; + + pub fn fmt(lp: *const LazyPath, graph: *const Graph) Format { + return .{ .graph = graph, .lazy_path = lp }; } /// Adds dependencies this file source implies to the given step. pub fn addStepDependencies(lazy_path: LazyPath, other_step: *Step) void { switch (lazy_path) { - .src_path, .cwd_relative, .dependency => {}, + .src_path, .cwd_relative, .relative, .dependency => {}, .generated => |gen| { const graph = other_step.owner.graph; const generated_owner_step = graph.generated_files.items[@intFromEnum(gen.index)]; @@ -2190,6 +2279,7 @@ pub const LazyPath = union(enum) { return switch (lazy_path) { .src_path => |sp| .{ .src_path = .{ .owner = sp.owner, .sub_path = sp.owner.dupePath(sp.sub_path) } }, .cwd_relative => |p| .{ .cwd_relative = graph.dupePath(p) }, + .relative => |r| .{ .relative = r }, .generated => |gen| .{ .generated = .{ .index = gen.index, .up = gen.up, diff --git a/lib/std/Build/Configuration.zig b/lib/std/Build/Configuration.zig index ca1c82074aec6ffd2ed75b819f12799922690287..2e81421313f2300092d7deaae15601a40807849f 100644 --- a/lib/std/Build/Configuration.zig +++ b/lib/std/Build/Configuration.zig @@ -402,6 +402,12 @@ pub const Wip = struct { defer wip.next_generated_file_index += 1; return @enumFromInt(wip.next_generated_file_index); } + + /// Returned slice expires upon next append to the configuration. + pub fn stringSlice(wip: *const Wip, s: String) [:0]const u8 { + const start_slice = wip.string_bytes.items[@intFromEnum(s)..]; + return start_slice[0..std.mem.indexOfScalar(u8, start_slice, 0).? :0]; + } }; pub const SystemIntegration = extern struct { @@ -445,6 +451,7 @@ pub const Step = extern struct { compile: Compile, config_header: ConfigHeader, fail: Fail, + find_program: FindProgram, fmt: Fmt, install_artifact: InstallArtifact, install_dir: InstallDir, @@ -479,6 +486,7 @@ pub const Step = extern struct { compile, config_header, fail, + find_program, fmt, install_artifact, install_dir, @@ -1037,6 +1045,17 @@ pub const Step = extern struct { }; }; + pub const FindProgram = struct { + flags: @This().Flags, + names: StringList, + generated_file: GeneratedFileIndex, + + pub const Flags = packed struct(u32) { + tag: Tag = .find_program, + _: u27 = 0, + }; + }; + pub const InstallDir = struct { flags: @This().Flags, source_dir: LazyPath.Index, @@ -1516,6 +1535,8 @@ pub const Path = extern struct { local_cache, global_cache, build_root, + zig_exe, + zig_lib, }; pub fn toCachePath(path: Path, c: *const Configuration, arena: Allocator) std.Build.Cache.Path { diff --git a/lib/std/Build/Module.zig b/lib/std/Build/Module.zig index 1c271023efad2c4458f5185d88620a692b7581ae..cdc0c134a56f9d8810946563fbaf4e41a31673bc 100644 --- a/lib/std/Build/Module.zig +++ b/lib/std/Build/Module.zig @@ -118,10 +118,10 @@ pub const CSourceFile = struct { /// By default, determines language of each file individually based on its file extension language: ?CSourceLanguage = null, - pub fn dupe(file: CSourceFile, b: *std.Build) CSourceFile { + pub fn dupe(file: CSourceFile, graph: *const std.Build.Graph) CSourceFile { return .{ - .file = file.file.dupe(b), - .flags = b.dupeStrings(file.flags), + .file = file.file.dupe(graph), + .flags = graph.dupeStrings(file.flags), .language = file.language, }; } @@ -146,10 +146,12 @@ pub const RcSourceFile = struct { include_paths: []const LazyPath = &.{}, pub fn dupe(file: RcSourceFile, b: *std.Build) RcSourceFile { - const include_paths = b.allocator.alloc(LazyPath, file.include_paths.len) catch @panic("OOM"); - for (include_paths, file.include_paths) |*dest, lazy_path| dest.* = lazy_path.dupe(b); + const graph = b.owner.graph; + const arena = graph.arena; + const include_paths = arena.alloc(LazyPath, file.include_paths.len) catch @panic("OOM"); + for (include_paths, file.include_paths) |*dest, lazy_path| dest.* = lazy_path.dupe(graph); return .{ - .file = file.file.dupe(b), + .file = file.file.dupe(graph), .flags = b.dupeStrings(file.flags), .include_paths = include_paths, }; @@ -290,15 +292,18 @@ pub fn init( } pub fn create(owner: *std.Build, options: CreateOptions) *Module { - const m = owner.allocator.create(Module) catch @panic("OOM"); + const graph = owner.graph; + const arena = graph.arena; + const m = arena.create(Module) catch @panic("OOM"); m.init(owner, .{ .options = options }); return m; } /// Adds an existing module to be used with `@import`. pub fn addImport(m: *Module, name: []const u8, module: *Module) void { - const b = m.owner; - m.import_table.put(b.allocator, b.dupe(name), module) catch @panic("OOM"); + const graph = m.owner.graph; + const arena = graph.arena; + m.import_table.put(arena, graph.dupeString(name), module) catch @panic("OOM"); } /// Creates a new module and adds it to be used with `@import`. @@ -338,7 +343,8 @@ pub fn linkSystemLibrary( name: []const u8, options: LinkSystemLibraryOptions, ) void { - const b = m.owner; + const graph = m.owner.graph; + const arena = graph.arena; const target = m.requireKnownTarget(); if (std.zig.target.isLibCLibName(target, name)) { @@ -350,9 +356,9 @@ pub fn linkSystemLibrary( return; } - m.link_objects.append(b.allocator, .{ + m.link_objects.append(arena, .{ .system_lib = .{ - .name = b.dupe(name), + .name = graph.dupeString(name), .needed = options.needed, .weak = options.weak, .use_pkg_config = options.use_pkg_config, @@ -363,8 +369,9 @@ pub fn linkSystemLibrary( } pub fn linkFramework(m: *Module, name: []const u8, options: LinkFrameworkOptions) void { - const b = m.owner; - m.frameworks.put(b.allocator, b.dupe(name), options) catch @panic("OOM"); + const graph = m.owner.graph; + const arena = graph.arena; + m.frameworks.put(arena, graph.dupeString(name), options) catch @panic("OOM"); } pub const AddCSourceFilesOptions = struct { @@ -380,7 +387,8 @@ pub const AddCSourceFilesOptions = struct { /// Handy when you have many non-Zig source files and want them all to have the same flags. pub fn addCSourceFiles(m: *Module, options: AddCSourceFilesOptions) void { const b = m.owner; - const allocator = b.allocator; + const graph = m.owner.graph; + const arena = graph.arena; for (options.files) |path| { if (std.fs.path.isAbsolute(path)) { @@ -391,48 +399,50 @@ pub fn addCSourceFiles(m: *Module, options: AddCSourceFilesOptions) void { } } - const c_source_files = allocator.create(CSourceFiles) catch @panic("OOM"); + const c_source_files = arena.create(CSourceFiles) catch @panic("OOM"); c_source_files.* = .{ .root = options.root orelse b.path(""), .files = b.dupeStrings(options.files), .flags = b.dupeStrings(options.flags), .language = options.language, }; - m.link_objects.append(allocator, .{ .c_source_files = c_source_files }) catch @panic("OOM"); + m.link_objects.append(arena, .{ .c_source_files = c_source_files }) catch @panic("OOM"); } pub fn addCSourceFile(m: *Module, source: CSourceFile) void { - const b = m.owner; - const allocator = b.allocator; - const c_source_file = allocator.create(CSourceFile) catch @panic("OOM"); - c_source_file.* = source.dupe(b); - m.link_objects.append(allocator, .{ .c_source_file = c_source_file }) catch @panic("OOM"); + const graph = m.owner.graph; + const arena = graph.arena; + const c_source_file = arena.create(CSourceFile) catch @panic("OOM"); + c_source_file.* = source.dupe(graph); + m.link_objects.append(arena, .{ .c_source_file = c_source_file }) catch @panic("OOM"); } /// Resource files must have the extension `.rc`. /// Can be called regardless of target. The .rc file will be ignored /// if the target object format does not support embedded resources. pub fn addWin32ResourceFile(m: *Module, source: RcSourceFile) void { - const b = m.owner; - const allocator = b.allocator; + const graph = m.owner.graph; + const arena = graph.arena; const target = m.requireKnownTarget(); // Only the PE/COFF format has a Resource Table, so for any other target // the resource file is ignored. if (target.ofmt != .coff) return; - const rc_source_file = allocator.create(RcSourceFile) catch @panic("OOM"); - rc_source_file.* = source.dupe(b); - m.link_objects.append(allocator, .{ .win32_resource_file = rc_source_file }) catch @panic("OOM"); + const rc_source_file = arena.create(RcSourceFile) catch @panic("OOM"); + rc_source_file.* = source.dupe(graph); + m.link_objects.append(arena, .{ .win32_resource_file = rc_source_file }) catch @panic("OOM"); } pub fn addAssemblyFile(m: *Module, source: LazyPath) void { - const b = m.owner; - m.link_objects.append(b.allocator, .{ .assembly_file = source.dupe(b) }) catch @panic("OOM"); + const graph = m.owner.graph; + const arena = graph.arena; + m.link_objects.append(arena, .{ .assembly_file = source.dupe(graph) }) catch @panic("OOM"); } pub fn addObjectFile(m: *Module, object: LazyPath) void { - const b = m.owner; - m.link_objects.append(b.allocator, .{ .static_path = object.dupe(b) }) catch @panic("OOM"); + const graph = m.owner.graph; + const arena = graph.arena; + m.link_objects.append(arena, .{ .static_path = object.dupe(graph) }) catch @panic("OOM"); } pub fn addObject(m: *Module, object: *Step.Compile) void { @@ -446,55 +456,63 @@ pub fn linkLibrary(m: *Module, library: *Step.Compile) void { } pub fn addAfterIncludePath(m: *Module, lazy_path: LazyPath) void { - const b = m.owner; - m.include_dirs.append(b.allocator, .{ .path_after = lazy_path.dupe(b) }) catch @panic("OOM"); + const graph = m.owner.graph; + const arena = graph.arena; + m.include_dirs.append(arena, .{ .path_after = lazy_path.dupe(graph) }) catch @panic("OOM"); } pub fn addSystemIncludePath(m: *Module, lazy_path: LazyPath) void { - const b = m.owner; - m.include_dirs.append(b.allocator, .{ .path_system = lazy_path.dupe(b) }) catch @panic("OOM"); + const graph = m.owner.graph; + const arena = graph.arena; + m.include_dirs.append(arena, .{ .path_system = lazy_path.dupe(graph) }) catch @panic("OOM"); } pub fn addIncludePath(m: *Module, lazy_path: LazyPath) void { - const b = m.owner; - m.include_dirs.append(b.allocator, .{ .path = lazy_path.dupe(b) }) catch @panic("OOM"); + const graph = m.owner.graph; + const arena = graph.arena; + m.include_dirs.append(arena, .{ .path = lazy_path.dupe(graph) }) catch @panic("OOM"); } pub fn addConfigHeader(m: *Module, config_header: *Step.ConfigHeader) void { - const allocator = m.owner.allocator; - m.include_dirs.append(allocator, .{ .config_header_step = config_header }) catch @panic("OOM"); + const graph = m.owner.graph; + const arena = graph.arena; + m.include_dirs.append(arena, .{ .config_header_step = config_header }) catch @panic("OOM"); } pub fn addSystemFrameworkPath(m: *Module, directory_path: LazyPath) void { - const b = m.owner; - m.include_dirs.append(b.allocator, .{ .framework_path_system = directory_path.dupe(b) }) catch - @panic("OOM"); + const graph = m.owner.graph; + const arena = graph.arena; + m.include_dirs.append(arena, .{ .framework_path_system = directory_path.dupe(graph) }) catch @panic("OOM"); } pub fn addFrameworkPath(m: *Module, directory_path: LazyPath) void { - const b = m.owner; - m.include_dirs.append(b.allocator, .{ .framework_path = directory_path.dupe(b) }) catch - @panic("OOM"); + const graph = m.owner.graph; + const arena = graph.arena; + m.include_dirs.append(arena, .{ .framework_path = directory_path.dupe(graph) }) catch @panic("OOM"); } pub fn addEmbedPath(m: *Module, lazy_path: LazyPath) void { - const b = m.owner; - m.include_dirs.append(b.allocator, .{ .embed_path = lazy_path.dupe(b) }) catch @panic("OOM"); + const graph = m.owner.graph; + const arena = graph.arena; + m.include_dirs.append(arena, .{ .embed_path = lazy_path.dupe(graph) }) catch @panic("OOM"); } pub fn addLibraryPath(m: *Module, directory_path: LazyPath) void { - const b = m.owner; - m.lib_paths.append(b.allocator, directory_path.dupe(b)) catch @panic("OOM"); + const graph = m.owner.graph; + const arena = graph.arena; + m.lib_paths.append(arena, directory_path.dupe(graph)) catch @panic("OOM"); } pub fn addRPath(m: *Module, directory_path: LazyPath) void { - const b = m.owner; - m.rpaths.append(b.allocator, .{ .lazy_path = directory_path.dupe(b) }) catch @panic("OOM"); + const graph = m.owner.graph; + const arena = graph.arena; + m.rpaths.append(arena, .{ .lazy_path = directory_path.dupe(graph) }) catch @panic("OOM"); } pub fn addRPathSpecial(m: *Module, bytes: []const u8) void { - const b = m.owner; - m.rpaths.append(b.allocator, .{ .special = b.dupe(bytes) }) catch @panic("OOM"); + const graph = m.owner.graph; + const arena = graph.arena; + m.rpaths.append(arena, .{ .special = graph.dupeString(bytes) }) catch @panic("OOM"); } /// Equvialent to the following C code, applied to all C source files owned by @@ -505,19 +523,23 @@ pub fn addRPathSpecial(m: *Module, bytes: []const u8) void { /// `name` and `value` need not live longer than the function call. pub fn addCMacro(m: *Module, name: []const u8, value: []const u8) void { const b = m.owner; - m.c_macros.append(b.allocator, b.fmt("-D{s}={s}", .{ name, value })) catch @panic("OOM"); + const graph = m.owner.graph; + const arena = graph.arena; + m.c_macros.append(arena, b.fmt("-D{s}={s}", .{ name, value })) catch @panic("OOM"); } fn linkLibraryOrObject(m: *Module, other: *Step.Compile) void { - const allocator = m.owner.allocator; + const graph = m.owner.graph; + const arena = graph.arena; + _ = other.getEmittedBin(); // Indicate there is a dependency on the outputted binary. if (other.rootModuleTarget().os.tag == .windows and other.isDynamicLibrary()) { _ = other.getEmittedImplib(); // Indicate dependency on the outputted implib. } - m.link_objects.append(allocator, .{ .other_step = other }) catch @panic("OOM"); - m.include_dirs.append(allocator, .{ .other_step = other }) catch @panic("OOM"); + m.link_objects.append(arena, .{ .other_step = other }) catch @panic("OOM"); + m.include_dirs.append(arena, .{ .other_step = other }) catch @panic("OOM"); } fn requireKnownTarget(m: *Module) *const std.Target { diff --git a/lib/std/Build/Step/ConfigHeader.zig b/lib/std/Build/Step/ConfigHeader.zig index a1f613e1cb4471f39dfd7311db6d8b14cf52d433..8e76d39fa155de6d614c41b55ebd016e41063bc0 100644 --- a/lib/std/Build/Step/ConfigHeader.zig +++ b/lib/std/Build/Step/ConfigHeader.zig @@ -83,7 +83,7 @@ pub fn create(owner: *std.Build, options: Options) *ConfigHeader { } const name = if (options.style.getPath()) |s| - owner.fmt("configure {t} header {s} to {s}", .{ options.style, s.getDisplayName(), include_path }) + owner.fmt("configure {t} header {f} to {s}", .{ options.style, s.fmt(graph), include_path }) else owner.fmt("configure {t} header to {s}", .{ options.style, include_path }); diff --git a/lib/std/Build/Step/InstallDir.zig b/lib/std/Build/Step/InstallDir.zig index f1a904cba5d150a5c21b84bd4a69c1eb52721d63..02a436e3a752b965ef4fa6eb41ae64a809a68d87 100644 --- a/lib/std/Build/Step/InstallDir.zig +++ b/lib/std/Build/Step/InstallDir.zig @@ -47,7 +47,7 @@ pub fn create(owner: *std.Build, options: Options) *InstallDir { install_dir.* = .{ .step = Step.init(.{ .tag = base_tag, - .name = owner.fmt("install {s}/", .{options.source_dir.getDisplayName()}), + .name = owner.fmt("install {f}/", .{options.source_dir.fmt(graph)}), .owner = owner, }), .options = options.dupe(graph), diff --git a/lib/std/Build/Step/InstallFile.zig b/lib/std/Build/Step/InstallFile.zig index f143e43e4e0b30c1c088309adb43d7f1ef127c1c..7d1d11cf5ad4b1ca8d33cbdf6fece43075e62f6d 100644 --- a/lib/std/Build/Step/InstallFile.zig +++ b/lib/std/Build/Step/InstallFile.zig @@ -26,7 +26,7 @@ pub fn create( install_file.* = .{ .step = Step.init(.{ .tag = base_tag, - .name = owner.fmt("install {s} to {s}", .{ source.getDisplayName(), dest_rel_path }), + .name = owner.fmt("install {f} to {s}", .{ source.fmt(graph), dest_rel_path }), .owner = owner, }), .source = source.dupe(graph), diff --git a/lib/std/Build/Step/ObjCopy.zig b/lib/std/Build/Step/ObjCopy.zig index d7de00c0ef28400009daf1f521fec43ba90403dc..94e6a405e84ee6d1ff26ba2a4f330c7f40f01ad3 100644 --- a/lib/std/Build/Step/ObjCopy.zig +++ b/lib/std/Build/Step/ObjCopy.zig @@ -116,12 +116,12 @@ pub fn create( objcopy.* = ObjCopy{ .step = Step.init(.{ .tag = base_tag, - .name = owner.fmt("objcopy {s}", .{input_file.getDisplayName()}), + .name = owner.fmt("objcopy {f}", .{input_file.fmt(graph)}), .owner = owner, .makeFn = make, }), .input_file = input_file, - .basename = options.basename orelse input_file.getDisplayName(), + .basename = options.basename orelse std.fmt.allocPrint("{f}", .{input_file.fmt(graph)}) catch @panic("OOM"), .output_file = graph.addGeneratedFile(&objcopy.step), .output_file_debug = if (options.strip != .none and options.extract_to_separate_file) .init(graph.addGeneratedFile(&objcopy.step)) diff --git a/lib/std/zig.zig b/lib/std/zig.zig index 700978a94d51c0e4071c24156c9869d347e6fac4..18d5db1c1d9853d8aee522e24440fbcd2dd0fa22 100644 --- a/lib/std/zig.zig +++ b/lib/std/zig.zig @@ -1165,15 +1165,13 @@ pub const ClangCliParam = struct { } }; -pub fn allocPrintCmd( - gpa: Allocator, - cwd: std.process.Child.Cwd, - opt_env: ?struct { - child: *const std.process.Environ.Map, - parent: *const std.process.Environ.Map, - }, - argv: []const []const u8, -) Allocator.Error![]u8 { +pub const AllocPrintCmdOptions = struct { + cwd: std.process.Child.Cwd = .inherit, + parent_env: ?*const std.process.Environ.Map = null, + child_env: ?*const std.process.Environ.Map = null, +}; + +pub fn allocPrintCmd(gpa: Allocator, argv: []const []const u8, options: AllocPrintCmdOptions) Allocator.Error![]u8 { const shell = struct { fn escape(writer: *Io.Writer, string: []const u8, is_argv0: bool) !void { for (string) |c| { @@ -1212,18 +1210,17 @@ pub fn allocPrintCmd( var aw: Io.Writer.Allocating = .init(gpa); defer aw.deinit(); const writer = &aw.writer; - switch (cwd) { + switch (options.cwd) { .inherit => {}, .path => |path| writer.print("cd {s} && ", .{path}) catch return error.OutOfMemory, .dir => @panic("TODO"), } - if (opt_env) |env| { - var it = env.child.iterator(); - while (it.next()) |entry| { - const key = entry.key_ptr.*; - const value = entry.value_ptr.*; - if (env.parent.get(key)) |process_value| { - if (std.mem.eql(u8, value, process_value)) continue; + if (options.child_env) |child_env| { + for (child_env.keys(), child_env.values()) |key, value| { + if (options.parent_env) |parent_env| { + if (parent_env.get(key)) |process_value| { + if (std.mem.eql(u8, value, process_value)) continue; + } } writer.print("{s}=", .{key}) catch return error.OutOfMemory; shell.escape(writer, value, false) catch return error.OutOfMemory; diff --git a/src/main.zig b/src/main.zig index 0f048adaffefe7f6ff01cbfbc758f58cb918f893..9f4c8507459f7ba2592422689714ea84ef748cb9 100644 --- a/src/main.zig +++ b/src/main.zig @@ -4987,7 +4987,7 @@ fn cmdBuild( const argv_index_zig_lib_dir = make_argv.items.len - 1; make_argv.addManyAsArrayAssumeCapacity(2).* = .{ "--build-root", undefined }; - const argv_index_build_file = make_argv.items.len - 1; + const make_argv_index_build_root = make_argv.items.len - 1; make_argv.addManyAsArrayAssumeCapacity(2).* = .{ "--local-cache", undefined }; const argv_index_cache_dir = make_argv.items.len - 1; @@ -5001,6 +5001,9 @@ fn cmdBuild( make_argv.addManyAsArrayAssumeCapacity(2).* = .{ "--seed", default_seed }; const argv_index_seed = make_argv.items.len - 1; + configure_argv.addManyAsArrayAssumeCapacity(2).* = .{ "--build-root", undefined }; + const conf_argv_index_build_root = configure_argv.items.len - 1; + var color: Color = .auto; var n_jobs: ?u32 = null; @@ -5065,6 +5068,10 @@ fn cmdBuild( i += 1; override_global_cache_dir = args[i]; continue; + } else if (mem.eql(u8, arg, "--verbose")) { + // Intentionally is added both to make and configure but + // does not go into the cache hash. + configure_argv.appendAssumeCapacity(arg); } else if (mem.eql(u8, arg, "-freference-trace")) { reference_trace = 256; } else if (mem.eql(u8, arg, "--fetch")) { @@ -5283,10 +5290,12 @@ fn cmdBuild( defer _ = make_runner_task.cancel(io) catch {}; make_argv.items[argv_index_zig_lib_dir] = dirs.zig_lib.path orelse cwd_path; - make_argv.items[argv_index_build_file] = build_root.directory.path orelse cwd_path; + make_argv.items[make_argv_index_build_root] = build_root.directory.path orelse cwd_path; make_argv.items[argv_index_global_cache_dir] = dirs.global_cache.path orelse cwd_path; make_argv.items[argv_index_cache_dir] = dirs.local_cache.path orelse cwd_path; + configure_argv.items[conf_argv_index_build_root] = build_root.directory.path orelse cwd_path; + // Dummy http client that is not actually used when fetch_command is unsupported. // Prevents bootstrap from depending on a bunch of unnecessary stuff. var http_client: if (dev.env.supports(.fetch_command)) std.http.Client else struct {