Commit c2b1cd7c45
Changed files (8)
lib/std/special/build_runner.zig
@@ -161,16 +161,16 @@ fn usage(builder: *Builder, already_ran_build: bool, out_stream: anytype) !void
try fmt.allocPrint(allocator, "{} (default)", .{top_level_step.step.name})
else
top_level_step.step.name;
- try out_stream.print(" {s:22} {}\n", .{ name, top_level_step.description });
+ try out_stream.print(" {s:<27} {}\n", .{ name, top_level_step.description });
}
try out_stream.writeAll(
\\
\\General Options:
- \\ --help Print this help and exit
- \\ --verbose Print commands before executing them
- \\ --prefix [path] Override default install prefix
- \\ --search-prefix [path] Add a path to look for binaries, libraries, headers
+ \\ --help Print this help and exit
+ \\ --verbose Print commands before executing them
+ \\ --prefix [path] Override default install prefix
+ \\ --search-prefix [path] Add a path to look for binaries, libraries, headers
\\
\\Project-Specific Options:
\\
@@ -185,7 +185,7 @@ fn usage(builder: *Builder, already_ran_build: bool, out_stream: anytype) !void
Builder.typeIdName(option.type_id),
});
defer allocator.free(name);
- try out_stream.print("{s:32} {}\n", .{ name, option.description });
+ try out_stream.print("{s:<29} {}\n", .{ name, option.description });
}
}
lib/std/build.zig
@@ -2294,8 +2294,7 @@ pub const LibExeObjStep = struct {
if (self.kind == Kind.Test) {
try builder.spawnChild(zig_args.span());
} else {
- try zig_args.append("--cache");
- try zig_args.append("on");
+ try zig_args.append("--enable-cache");
const output_dir_nl = try builder.execFromStep(zig_args.span(), &self.step);
const build_output_dir = mem.trimRight(u8, output_dir_nl, "\r\n");
lib/std/process.zig
@@ -19,6 +19,19 @@ pub const exit = os.exit;
pub const changeCurDir = os.chdir;
pub const changeCurDirC = os.chdirC;
+/// Indicate that we are now terminating with a successful exit code.
+/// In debug builds, this is a no-op, so that the calling code's
+/// cleanup mechanisms are tested and so that external tools that
+/// check for resource leaks can be accurate. In release builds, this
+/// calls exit(0), and does not return.
+pub fn cleanExit() void {
+ if (builtin.mode == .Debug) {
+ return;
+ } else {
+ exit(0);
+ }
+}
+
/// The result is a slice of `out_buffer`, from index `0`.
pub fn getCwd(out_buffer: []u8) ![]u8 {
return os.getcwd(out_buffer);
src/Cache.zig
@@ -120,6 +120,13 @@ pub const HashHelper = struct {
return copy.final();
}
+ pub fn peekBin(hh: HashHelper) [bin_digest_len]u8 {
+ var copy = hh;
+ var bin_digest: [bin_digest_len]u8 = undefined;
+ copy.hasher.final(&bin_digest);
+ return bin_digest;
+ }
+
/// Returns a hex encoded hash of the inputs, mutating the state of the hasher.
pub fn final(hh: *HashHelper) [hex_digest_len]u8 {
var bin_digest: [bin_digest_len]u8 = undefined;
@@ -338,19 +345,7 @@ pub const CacheHash = struct {
if (any_file_changed) {
// cache miss
// keep the manifest file open
- // reset the hash
- self.hash.hasher = hasher_init;
- self.hash.hasher.update(&bin_digest);
-
- // Remove files not in the initial hash
- for (self.files.items[input_file_count..]) |*file| {
- file.deinit(self.cache.gpa);
- }
- self.files.shrinkRetainingCapacity(input_file_count);
-
- for (self.files.items) |file| {
- self.hash.hasher.update(&file.bin_digest);
- }
+ self.unhit(bin_digest, input_file_count);
return false;
}
@@ -366,6 +361,22 @@ pub const CacheHash = struct {
return true;
}
+ pub fn unhit(self: *CacheHash, bin_digest: [bin_digest_len]u8, input_file_count: usize) void {
+ // Reset the hash.
+ self.hash.hasher = hasher_init;
+ self.hash.hasher.update(&bin_digest);
+
+ // Remove files not in the initial hash.
+ for (self.files.items[input_file_count..]) |*file| {
+ file.deinit(self.cache.gpa);
+ }
+ self.files.shrinkRetainingCapacity(input_file_count);
+
+ for (self.files.items) |file| {
+ self.hash.hasher.update(&file.bin_digest);
+ }
+ }
+
fn populateFileHash(self: *CacheHash, ch_file: *File) !void {
const file = try fs.cwd().openFile(ch_file.path.?, .{});
defer file.close();
src/Compilation.zig
@@ -2200,20 +2200,34 @@ fn updateStage1Module(comp: *Compilation) !void {
ch.hash.add(comp.bin_file.options.function_sections);
ch.hash.add(comp.is_test);
+ // Capture the state in case we come back from this branch where the hash doesn't match.
+ const prev_hash_state = ch.hash.peekBin();
+ const input_file_count = ch.files.items.len;
+
if (try ch.hit()) {
const digest = ch.final();
var prev_digest_buf: [digest.len]u8 = undefined;
const prev_digest: []u8 = directory.handle.readLink(id_symlink_basename, &prev_digest_buf) catch |err| blk: {
+ log.debug("stage1 {} new_digest={} readlink error: {}", .{ mod.root_pkg.root_src_path, digest, @errorName(err) });
// Handle this as a cache miss.
break :blk prev_digest_buf[0..0];
};
if (mem.eql(u8, prev_digest, &digest)) {
+ log.debug("stage1 {} digest={} match - skipping invocation", .{ mod.root_pkg.root_src_path, digest });
comp.stage1_lock = ch.toOwnedLock();
return;
}
+ log.debug("stage1 {} prev_digest={} new_digest={}", .{ mod.root_pkg.root_src_path, prev_digest, digest });
+ ch.unhit(prev_hash_state, input_file_count);
}
+ // We are about to change the output file to be different, so we invalidate the build hash now.
+ directory.handle.deleteFile(id_symlink_basename) catch |err| switch (err) {
+ error.FileNotFound => {},
+ else => |e| return e,
+ };
+
const stage2_target = try arena.create(stage1.Stage2Target);
stage2_target.* = .{
.arch = @enumToInt(target.cpu.arch) + 1, // skip over ZigLLVM_UnknownArch
@@ -2243,16 +2257,7 @@ fn updateStage1Module(comp: *Compilation) !void {
comp.is_test,
) orelse return error.OutOfMemory;
- const stage1_pkg = try arena.create(stage1.Pkg);
- stage1_pkg.* = .{
- .name_ptr = undefined,
- .name_len = 0,
- .path_ptr = undefined,
- .path_len = 0,
- .children_ptr = undefined,
- .children_len = 0,
- .parent = null,
- };
+ const stage1_pkg = try createStage1Pkg(arena, "root", mod.root_pkg, null);
const output_dir = comp.bin_file.options.directory.path orelse ".";
const test_filter = comp.test_filter orelse ""[0..0];
const test_name_prefix = comp.test_name_prefix orelse ""[0..0];
@@ -2303,10 +2308,12 @@ fn updateStage1Module(comp: *Compilation) !void {
const digest = ch.final();
+ log.debug("stage1 {} final digest={}", .{ mod.root_pkg.root_src_path, digest });
+
// Update the dangling symlink with the digest. If it fails we can continue; it only
// means that the next invocation will have an unnecessary cache miss.
directory.handle.symLink(&digest, id_symlink_basename, .{}) catch |err| {
- std.log.warn("failed to save linking hash digest symlink: {}", .{@errorName(err)});
+ std.log.warn("failed to save stage1 hash digest symlink: {}", .{@errorName(err)});
};
// Again failure here only means an unnecessary cache miss.
ch.writeManifest() catch |err| {
@@ -2316,3 +2323,34 @@ fn updateStage1Module(comp: *Compilation) !void {
// other processes clobbering it.
comp.stage1_lock = ch.toOwnedLock();
}
+
+fn createStage1Pkg(
+ arena: *Allocator,
+ name: []const u8,
+ pkg: *Package,
+ parent_pkg: ?*stage1.Pkg,
+) error{OutOfMemory}!*stage1.Pkg {
+ const child_pkg = try arena.create(stage1.Pkg);
+
+ const pkg_children = blk: {
+ var children = std.ArrayList(*stage1.Pkg).init(arena);
+ var it = pkg.table.iterator();
+ while (it.next()) |entry| {
+ try children.append(try createStage1Pkg(arena, entry.key, entry.value, child_pkg));
+ }
+ break :blk children.items;
+ };
+
+ const src_path = try pkg.root_src_directory.join(arena, &[_][]const u8{pkg.root_src_path});
+
+ child_pkg.* = .{
+ .name_ptr = name.ptr,
+ .name_len = name.len,
+ .path_ptr = src_path.ptr,
+ .path_len = src_path.len,
+ .children_ptr = pkg_children.ptr,
+ .children_len = pkg_children.len,
+ .parent = parent_pkg,
+ };
+ return child_pkg;
+}
src/main.zig
@@ -35,6 +35,7 @@ const usage =
\\
\\Commands:
\\
+ \\ build Build project from build.zig
\\ build-exe Create executable from source or object files
\\ build-lib Create library from source or object files
\\ build-obj Create object from source or assembly
@@ -42,6 +43,8 @@ const usage =
\\ c++ Use Zig as a drop-in C++ compiler
\\ env Print lib path, std path, compiler id and version
\\ fmt Parse file and render in canonical zig format
+ \\ init-exe Initialize a `zig build` application in the cwd
+ \\ init-lib Initialize a `zig build` library in the cwd
\\ libc Display native libc paths file or validate one
\\ run Create executable and run immediately
\\ translate-c Convert C code to Zig code
@@ -136,6 +139,8 @@ pub fn mainArgs(gpa: *Allocator, arena: *Allocator, args: []const []const u8) !v
mem.eql(u8, cmd, "-cc1") or mem.eql(u8, cmd, "-cc1as"))
{
return punt_to_clang(arena, args);
+ } else if (mem.eql(u8, cmd, "build")) {
+ return cmdBuild(gpa, arena, cmd_args);
} else if (mem.eql(u8, cmd, "fmt")) {
return cmdFmt(gpa, cmd_args);
} else if (mem.eql(u8, cmd, "libc")) {
@@ -172,18 +177,18 @@ const usage_build_generic =
\\Supported file types:
\\ .zig Zig source code
\\ .zir Zig Intermediate Representation code
- \\ (planned) .o ELF object file
- \\ (planned) .o MACH-O (macOS) object file
- \\ (planned) .obj COFF (Windows) object file
- \\ (planned) .lib COFF (Windows) static library
- \\ (planned) .a ELF static library
- \\ (planned) .so ELF shared object (dynamic link)
- \\ (planned) .dll Windows Dynamic Link Library
- \\ (planned) .dylib MACH-O (macOS) dynamic library
- \\ (planned) .s Target-specific assembly source code
- \\ (planned) .S Assembly with C preprocessor (requires LLVM extensions)
- \\ (planned) .c C source code (requires LLVM extensions)
- \\ (planned) .cpp C++ source code (requires LLVM extensions)
+ \\ .o ELF object file
+ \\ .o MACH-O (macOS) object file
+ \\ .obj COFF (Windows) object file
+ \\ .lib COFF (Windows) static library
+ \\ .a ELF static library
+ \\ .so ELF shared object (dynamic link)
+ \\ .dll Windows Dynamic Link Library
+ \\ .dylib MACH-O (macOS) dynamic library
+ \\ .s Target-specific assembly source code
+ \\ .S Assembly with C preprocessor (requires LLVM extensions)
+ \\ .c C source code (requires LLVM extensions)
+ \\ .cpp C++ source code (requires LLVM extensions)
\\ Other C++ extensions: .C .cc .cxx
\\
\\General Options:
@@ -195,6 +200,8 @@ const usage_build_generic =
\\ --show-builtin Output the source of @import("builtin") then exit
\\ --cache-dir [path] Override the local cache directory
\\ --global-cache-dir [path] Override the global cache directory
+ \\ --override-lib-dir [path] Override path to Zig installation lib directory
+ \\ --enable-cache Output to cache directory; print path to stdout
\\
\\Compile Options:
\\ -target [name] <arch><sub>-<os>-<abi> see the targets command
@@ -357,6 +364,7 @@ pub fn buildOutputType(
var test_name_prefix: ?[]const u8 = null;
var override_local_cache_dir: ?[]const u8 = null;
var override_global_cache_dir: ?[]const u8 = null;
+ var override_lib_dir: ?[]const u8 = null;
var system_libs = std.ArrayList([]const u8).init(gpa);
defer system_libs.deinit();
@@ -412,7 +420,7 @@ pub fn buildOutputType(
if (mem.startsWith(u8, arg, "-")) {
if (mem.eql(u8, arg, "-h") or mem.eql(u8, arg, "--help")) {
try io.getStdOut().writeAll(usage_build_generic);
- process.exit(0);
+ return process.cleanExit();
} else if (mem.eql(u8, arg, "--")) {
if (arg_mode == .run) {
runtime_args_start = i + 1;
@@ -547,6 +555,12 @@ pub fn buildOutputType(
if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg});
i += 1;
override_global_cache_dir = args[i];
+ } else if (mem.eql(u8, arg, "--override-lib-dir")) {
+ if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg});
+ i += 1;
+ override_lib_dir = args[i];
+ } else if (mem.eql(u8, arg, "--enable-cache")) {
+ enable_cache = true;
} else if (mem.eql(u8, arg, "--test-cmd-bin")) {
try test_exec_args.append(null);
} else if (mem.eql(u8, arg, "--test-evented-io")) {
@@ -1102,12 +1116,22 @@ pub fn buildOutputType(
var cleanup_emit_bin_dir: ?fs.Dir = null;
defer if (cleanup_emit_bin_dir) |*dir| dir.close();
+ const have_enable_cache = enable_cache orelse false;
+
const emit_bin_loc: ?Compilation.EmitLoc = switch (emit_bin) {
.no => null,
.yes_default_path => Compilation.EmitLoc{
- .directory = switch (arg_mode) {
- .run, .zig_test => null,
- else => .{ .path = null, .handle = fs.cwd() },
+ .directory = blk: {
+ switch (arg_mode) {
+ .run, .zig_test => break :blk null,
+ else => {
+ if (have_enable_cache) {
+ break :blk null;
+ } else {
+ break :blk .{ .path = null, .handle = fs.cwd() };
+ }
+ },
+ }
},
.basename = try std.zig.binNameAlloc(
arena,
@@ -1120,6 +1144,12 @@ pub fn buildOutputType(
},
.yes => |full_path| b: {
const basename = fs.path.basename(full_path);
+ if (have_enable_cache) {
+ break :b Compilation.EmitLoc{
+ .basename = basename,
+ .directory = null,
+ };
+ }
if (fs.path.dirname(full_path)) |dirname| {
const handle = try fs.cwd().openDir(dirname, .{});
cleanup_emit_bin_dir = handle;
@@ -1192,9 +1222,15 @@ pub fn buildOutputType(
} else null;
const self_exe_path = try fs.selfExePathAlloc(arena);
- var zig_lib_directory = introspect.findZigLibDirFromSelfExe(arena, self_exe_path) catch |err| {
- fatal("unable to find zig installation directory: {}\n", .{@errorName(err)});
- };
+ var zig_lib_directory: Compilation.Directory = if (override_lib_dir) |lib_dir|
+ .{
+ .path = lib_dir,
+ .handle = try fs.cwd().openDir(lib_dir, .{}),
+ }
+ else
+ introspect.findZigLibDirFromSelfExe(arena, self_exe_path) catch |err| {
+ fatal("unable to find zig installation directory: {}", .{@errorName(err)});
+ };
defer zig_lib_directory.handle.close();
const random_seed = blk: {
@@ -1337,7 +1373,20 @@ pub fn buildOutputType(
return cmdTranslateC(comp, arena);
}
- try updateModule(gpa, comp, zir_out_path);
+ const hook: AfterUpdateHook = blk: {
+ if (!have_enable_cache)
+ break :blk .none;
+
+ switch (emit_bin) {
+ .no => break :blk .none,
+ .yes_default_path => break :blk .{
+ .print = comp.bin_file.options.directory.path orelse ".",
+ },
+ .yes => |full_path| break :blk .{ .update = full_path },
+ }
+ };
+
+ try updateModule(gpa, comp, zir_out_path, hook);
if (build_options.have_llvm and only_pp_or_asm) {
// this may include dumping the output to stdout
@@ -1389,7 +1438,7 @@ pub fn buildOutputType(
else => process.exit(1),
}
if (!watch)
- process.exit(0);
+ return process.cleanExit();
},
else => {},
}
@@ -1413,7 +1462,7 @@ pub fn buildOutputType(
if (output_mode == .Exe) {
try comp.makeBinFileWritable();
}
- try updateModule(gpa, comp, zir_out_path);
+ try updateModule(gpa, comp, zir_out_path, hook);
} else if (mem.eql(u8, actual_line, "exit")) {
break;
} else if (mem.eql(u8, actual_line, "help")) {
@@ -1427,7 +1476,13 @@ pub fn buildOutputType(
}
}
-fn updateModule(gpa: *Allocator, comp: *Compilation, zir_out_path: ?[]const u8) !void {
+const AfterUpdateHook = union(enum) {
+ none,
+ print: []const u8,
+ update: []const u8,
+};
+
+fn updateModule(gpa: *Allocator, comp: *Compilation, zir_out_path: ?[]const u8, hook: AfterUpdateHook) !void {
try comp.update();
var errors = try comp.getAllErrorsAlloc();
@@ -1437,6 +1492,15 @@ fn updateModule(gpa: *Allocator, comp: *Compilation, zir_out_path: ?[]const u8)
for (errors.list) |full_err_msg| {
full_err_msg.renderToStdErr();
}
+ } else switch (hook) {
+ .none => {},
+ .print => |bin_path| try io.getStdOut().writer().print("{s}\n", .{bin_path}),
+ .update => |full_path| _ = try comp.bin_file.options.directory.handle.updateFile(
+ comp.bin_file.options.sub_path,
+ fs.cwd(),
+ full_path,
+ .{},
+ ),
}
if (zir_out_path) |zop| {
@@ -1535,7 +1599,7 @@ pub fn cmdLibC(gpa: *Allocator, args: []const []const u8) !void {
if (mem.eql(u8, arg, "--help")) {
const stdout = io.getStdOut().writer();
try stdout.writeAll(usage_libc);
- process.exit(0);
+ return process.cleanExit();
} else {
fatal("unrecognized parameter: '{}'", .{arg});
}
@@ -1592,7 +1656,7 @@ pub fn cmdInit(
if (mem.startsWith(u8, arg, "-")) {
if (mem.eql(u8, arg, "--help")) {
try io.getStdOut().writeAll(usage_init);
- process.exit(0);
+ return process.cleanExit();
} else {
fatal("unrecognized parameter: '{}'", .{arg});
}
@@ -1657,6 +1721,248 @@ pub fn cmdInit(
}
}
+pub const usage_build =
+ \\Usage: zig build [steps] [options]
+ \\
+ \\ Build a project from build.zig.
+ \\
+ \\Options:
+ \\ --help Print this help and exit
+ \\
+ \\
+;
+
+pub fn cmdBuild(gpa: *Allocator, arena: *Allocator, args: []const []const u8) !void {
+ // We want to release all the locks before executing the child process, so we make a nice
+ // big block here to ensure the cleanup gets run when we extract out our argv.
+ const lock_and_argv = lock_and_argv: {
+ const self_exe_path = try fs.selfExePathAlloc(arena);
+
+ var build_file: ?[]const u8 = null;
+ var override_lib_dir: ?[]const u8 = null;
+ var override_global_cache_dir: ?[]const u8 = null;
+ var override_local_cache_dir: ?[]const u8 = null;
+ var child_argv = std.ArrayList([]const u8).init(arena);
+
+ const argv_index_exe = child_argv.items.len;
+ _ = try child_argv.addOne();
+
+ try child_argv.append(self_exe_path);
+
+ const argv_index_build_file = child_argv.items.len;
+ _ = try child_argv.addOne();
+
+ const argv_index_cache_dir = child_argv.items.len;
+ _ = try child_argv.addOne();
+
+ {
+ var i: usize = 0;
+ while (i < args.len) : (i += 1) {
+ const arg = args[i];
+ if (mem.startsWith(u8, arg, "-")) {
+ if (mem.eql(u8, arg, "--build-file")) {
+ if (i + 1 >= args.len) fatal("expected argument after '{}'", .{arg});
+ i += 1;
+ build_file = args[i];
+ continue;
+ } else if (mem.eql(u8, arg, "--override-lib-dir")) {
+ if (i + 1 >= args.len) fatal("expected argument after '{}'", .{arg});
+ i += 1;
+ override_lib_dir = args[i];
+ try child_argv.appendSlice(&[_][]const u8{ arg, args[i] });
+ continue;
+ } else if (mem.eql(u8, arg, "--cache-dir")) {
+ if (i + 1 >= args.len) fatal("expected argument after '{}'", .{arg});
+ i += 1;
+ override_local_cache_dir = args[i];
+ try child_argv.appendSlice(&[_][]const u8{ arg, args[i] });
+ continue;
+ } else if (mem.eql(u8, arg, "--global-cache-dir")) {
+ if (i + 1 >= args.len) fatal("expected argument after '{}'", .{arg});
+ i += 1;
+ override_global_cache_dir = args[i];
+ try child_argv.appendSlice(&[_][]const u8{ arg, args[i] });
+ continue;
+ }
+ }
+ try child_argv.append(arg);
+ }
+ }
+
+ var zig_lib_directory: Compilation.Directory = if (override_lib_dir) |lib_dir|
+ .{
+ .path = lib_dir,
+ .handle = try fs.cwd().openDir(lib_dir, .{}),
+ }
+ else
+ introspect.findZigLibDirFromSelfExe(arena, self_exe_path) catch |err| {
+ fatal("unable to find zig installation directory: {}", .{@errorName(err)});
+ };
+ defer zig_lib_directory.handle.close();
+
+ const std_special = "std" ++ fs.path.sep_str ++ "special";
+ const special_dir_path = try zig_lib_directory.join(arena, &[_][]const u8{std_special});
+
+ var root_pkg: Package = .{
+ .root_src_directory = .{
+ .path = special_dir_path,
+ .handle = try zig_lib_directory.handle.openDir(std_special, .{}),
+ },
+ .root_src_path = "build_runner.zig",
+ };
+ defer root_pkg.root_src_directory.handle.close();
+
+ var cleanup_build_dir: ?fs.Dir = null;
+ defer if (cleanup_build_dir) |*dir| dir.close();
+
+ const cwd_path = try process.getCwdAlloc(arena);
+ const build_zig_basename = if (build_file) |bf| fs.path.basename(bf) else "build.zig";
+ const build_directory: Compilation.Directory = blk: {
+ if (build_file) |bf| {
+ if (fs.path.dirname(bf)) |dirname| {
+ const dir = try fs.cwd().openDir(dirname, .{});
+ cleanup_build_dir = dir;
+ break :blk .{ .path = dirname, .handle = dir };
+ }
+
+ break :blk .{ .path = null, .handle = fs.cwd() };
+ }
+ // Search up parent directories until we find build.zig.
+ var dirname: []const u8 = cwd_path;
+ while (true) {
+ const joined_path = try fs.path.join(arena, &[_][]const u8{ dirname, build_zig_basename });
+ if (fs.cwd().access(joined_path, .{})) |_| {
+ const dir = try fs.cwd().openDir(dirname, .{});
+ break :blk .{ .path = dirname, .handle = dir };
+ } else |err| switch (err) {
+ error.FileNotFound => {
+ dirname = fs.path.dirname(dirname) orelse {
+ std.log.info("{}", .{
+ \\Initialize a 'build.zig' template file with `zig init-lib` or `zig init-exe`,
+ \\or see `zig --help` for more options.
+ });
+ fatal("No 'build.zig' file found, in the current directory or any parent directories.", .{});
+ };
+ continue;
+ },
+ else => |e| return e,
+ }
+ }
+ };
+ child_argv.items[argv_index_build_file] = build_directory.path orelse cwd_path;
+
+ var build_pkg: Package = .{
+ .root_src_directory = build_directory,
+ .root_src_path = build_zig_basename,
+ };
+ try root_pkg.table.put(arena, "@build", &build_pkg);
+
+ var global_cache_directory: Compilation.Directory = l: {
+ const p = override_global_cache_dir orelse try introspect.resolveGlobalCacheDir(arena);
+ break :l .{
+ .handle = try fs.cwd().makeOpenPath(p, .{}),
+ .path = p,
+ };
+ };
+ defer global_cache_directory.handle.close();
+
+ var local_cache_directory: Compilation.Directory = l: {
+ if (override_local_cache_dir) |local_cache_dir_path| {
+ break :l .{
+ .handle = try fs.cwd().makeOpenPath(local_cache_dir_path, .{}),
+ .path = local_cache_dir_path,
+ };
+ }
+ const cache_dir_path = try build_directory.join(arena, &[_][]const u8{"zig-cache"});
+ break :l .{
+ .handle = try build_directory.handle.makeOpenPath("zig-cache", .{}),
+ .path = cache_dir_path,
+ };
+ };
+ defer local_cache_directory.handle.close();
+
+ child_argv.items[argv_index_cache_dir] = local_cache_directory.path orelse cwd_path;
+
+ gimmeMoreOfThoseSweetSweetFileDescriptors();
+
+ const cross_target: std.zig.CrossTarget = .{};
+ const target_info = try detectNativeTargetInfo(gpa, cross_target);
+
+ const exe_basename = try std.zig.binNameAlloc(arena, "build", target_info.target, .Exe, null, null);
+ const emit_bin: Compilation.EmitLoc = .{
+ .directory = null, // Use the local zig-cache.
+ .basename = exe_basename,
+ };
+ const random_seed = blk: {
+ var random_seed: u64 = undefined;
+ try std.crypto.randomBytes(mem.asBytes(&random_seed));
+ break :blk random_seed;
+ };
+ var default_prng = std.rand.DefaultPrng.init(random_seed);
+ const comp = Compilation.create(gpa, .{
+ .zig_lib_directory = zig_lib_directory,
+ .local_cache_directory = local_cache_directory,
+ .global_cache_directory = global_cache_directory,
+ .root_name = "build",
+ .target = target_info.target,
+ .is_native_os = cross_target.isNativeOs(),
+ .dynamic_linker = target_info.dynamic_linker.get(),
+ .output_mode = .Exe,
+ .root_pkg = &root_pkg,
+ .emit_bin = emit_bin,
+ .emit_h = null,
+ .optimize_mode = .Debug,
+ .self_exe_path = self_exe_path,
+ .rand = &default_prng.random,
+ }) catch |err| {
+ fatal("unable to create compilation: {}", .{@errorName(err)});
+ };
+ defer comp.destroy();
+
+ try updateModule(gpa, comp, null, .none);
+
+ child_argv.items[argv_index_exe] = try comp.bin_file.options.directory.join(arena, &[_][]const u8{exe_basename});
+
+ break :lock_and_argv .{
+ .child_argv = child_argv.items,
+ .lock = comp.bin_file.toOwnedLock(),
+ };
+ };
+ const child_argv = lock_and_argv.child_argv;
+ var lock = lock_and_argv.lock;
+ defer lock.release();
+
+ const child = try std.ChildProcess.init(child_argv, gpa);
+ defer child.deinit();
+
+ child.stdin_behavior = .Inherit;
+ child.stdout_behavior = .Inherit;
+ child.stderr_behavior = .Inherit;
+
+ var cmd = std.ArrayList(u8).init(arena);
+
+ const term = try child.spawnAndWait();
+ switch (term) {
+ .Exited => |code| {
+ if (code == 0) return process.cleanExit();
+ try cmd.writer().print("failed with exit code {}:\n", .{code});
+ },
+ else => {
+ try cmd.appendSlice("crashed:\n");
+ },
+ }
+
+ try cmd.append('\n');
+ for (child_argv[0 .. child_argv.len - 1]) |arg| {
+ try cmd.appendSlice(arg);
+ try cmd.append(' ');
+ }
+ try cmd.appendSlice(child_argv[child_argv.len - 1]);
+
+ if (true) // Working around erroneous stage1 compile error: unreachable code on child.deinit()
+ fatal("The following build command {}", .{cmd.items});
+}
+
pub const usage_fmt =
\\Usage: zig fmt [file]...
\\
@@ -1699,7 +2005,7 @@ pub fn cmdFmt(gpa: *Allocator, args: []const []const u8) !void {
if (mem.eql(u8, arg, "--help")) {
const stdout = io.getStdOut().outStream();
try stdout.writeAll(usage_fmt);
- process.exit(0);
+ return process.cleanExit();
} else if (mem.eql(u8, arg, "--color")) {
if (i + 1 >= args.len) {
fatal("expected [auto|on|off] after --color", .{});
src/stage1.zig
@@ -31,11 +31,11 @@ pub export fn main(argc: c_int, argv: [*]const [*:0]const u8) c_int {
defer arena_instance.deinit();
const arena = &arena_instance.allocator;
- const args = arena.alloc([]const u8, @intCast(usize, argc)) catch fatal("out of memory", .{});
+ const args = arena.alloc([]const u8, @intCast(usize, argc)) catch fatal("{}", .{"OutOfMemory"});
for (args) |*arg, i| {
arg.* = mem.spanZ(argv[i]);
}
- stage2.mainArgs(gpa, arena, args) catch |err| fatal("{}", .{err});
+ stage2.mainArgs(gpa, arena, args) catch |err| fatal("{}", .{@errorName(err)});
return 0;
}
BRANCH_TODO
@@ -1,6 +1,3 @@
- * skip LLD caching when bin directory is not in the cache (so we don't put `id.txt` into the cwd)
- (maybe make it an explicit option and have main.zig disable it)
- * `zig build`
* repair @cImport
* make sure zig cc works
- using it as a preprocessor (-E)
@@ -22,13 +19,16 @@
* COFF LLD linking
* WASM LLD linking
* --main-pkg-path
+ * --pkg-begin, --pkg-end
+ * skip LLD caching when bin directory is not in the cache (so we don't put `id.txt` into the cwd)
+ (maybe make it an explicit option and have main.zig disable it)
* audit the CLI options for stage2
* audit the base cache hash
* implement proper parsing of LLD stderr/stdout and exposing compile errors
* implement proper parsing of clang stderr/stdout and exposing compile errors
* On operating systems that support it, do an execve for `zig test` and `zig run` rather than child process.
* restore error messages for stage2_add_link_lib
- * update zig build to use new CLI
+ * update std/build.zig to use new CLI
* support cross compiling stage2 with `zig build`
* implement proper compile errors for failing to build glibc crt files and shared libs