authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-09-22 22:18:19-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-09-22 22:18:19-07:00
logc2b1cd7c456e274c7ead96e597d147e2df7d317e
treebf924cc095e8bb77dfee6484d6bdc2fb0f7aa2e3
parent250664bea41cc5dde66e3054ca0c1b6e0feab9be

stage2: implement zig build

As part of this: * add std.process.cleanExit. closes #6395 - use it in several places * adjust the alignment of text in `zig build --help` menu * Cache: support the concept of "unhit" so that we properly keep track of the cache when we find out using the secondary hash that the cache "hit" was actually a miss. Use this to fix false negatives of caching of stage1 build artifacts. * fix not deleting the symlink hash for stage1 build artifacts causing false positives. * implement support for Package arguments in stage1 build artifacts * update and add missing usage text * add --override-lib-dir and --enable-cache CLI options - `--enable-cache` takes the place of `--cache on` * CLI supports -femit-bin=foo combined with --enable-cache to do an "update file" operation. --enable-cache without that argument will build the output into a cache directory and then print the path to stdout (matching master branch behavior). * errors surfacing from main() now print "error: Foo" instead of "error: error.Foo".

8 files changed, 431 insertions(+), 64 deletions(-)

BRANCH_TODO+4-4
......@@ -1,6 +1,3 @@
1 * skip LLD caching when bin directory is not in the cache (so we don't put `id.txt` into the cwd)
2 (maybe make it an explicit option and have main.zig disable it)
3 * `zig build`
41 * repair @cImport
52 * make sure zig cc works
63 - using it as a preprocessor (-E)
......@@ -22,13 +19,16 @@
2219 * COFF LLD linking
2320 * WASM LLD linking
2421 * --main-pkg-path
22 * --pkg-begin, --pkg-end
23 * skip LLD caching when bin directory is not in the cache (so we don't put `id.txt` into the cwd)
24 (maybe make it an explicit option and have main.zig disable it)
2525 * audit the CLI options for stage2
2626 * audit the base cache hash
2727 * implement proper parsing of LLD stderr/stdout and exposing compile errors
2828 * implement proper parsing of clang stderr/stdout and exposing compile errors
2929 * On operating systems that support it, do an execve for `zig test` and `zig run` rather than child process.
3030 * restore error messages for stage2_add_link_lib
31 * update zig build to use new CLI
31 * update std/build.zig to use new CLI
3232
3333 * support cross compiling stage2 with `zig build`
3434 * implement proper compile errors for failing to build glibc crt files and shared libs
lib/std/build.zig+1-2
......@@ -2294,8 +2294,7 @@ pub const LibExeObjStep = struct {
22942294 if (self.kind == Kind.Test) {
22952295 try builder.spawnChild(zig_args.span());
22962296 } else {
2297 try zig_args.append("--cache");
2298 try zig_args.append("on");
2297 try zig_args.append("--enable-cache");
22992298
23002299 const output_dir_nl = try builder.execFromStep(zig_args.span(), &self.step);
23012300 const build_output_dir = mem.trimRight(u8, output_dir_nl, "\r\n");
lib/std/process.zig+13
......@@ -19,6 +19,19 @@ pub const exit = os.exit;
1919pub const changeCurDir = os.chdir;
2020pub const changeCurDirC = os.chdirC;
2121
22/// Indicate that we are now terminating with a successful exit code.
23/// In debug builds, this is a no-op, so that the calling code's
24/// cleanup mechanisms are tested and so that external tools that
25/// check for resource leaks can be accurate. In release builds, this
26/// calls exit(0), and does not return.
27pub fn cleanExit() void {
28 if (builtin.mode == .Debug) {
29 return;
30 } else {
31 exit(0);
32 }
33}
34
2235/// The result is a slice of `out_buffer`, from index `0`.
2336pub fn getCwd(out_buffer: []u8) ![]u8 {
2437 return os.getcwd(out_buffer);
lib/std/special/build_runner.zig+6-6
......@@ -161,16 +161,16 @@ fn usage(builder: *Builder, already_ran_build: bool, out_stream: anytype) !void
161161 try fmt.allocPrint(allocator, "{} (default)", .{top_level_step.step.name})
162162 else
163163 top_level_step.step.name;
164 try out_stream.print(" {s:22} {}\n", .{ name, top_level_step.description });
164 try out_stream.print(" {s:<27} {}\n", .{ name, top_level_step.description });
165165 }
166166
167167 try out_stream.writeAll(
168168 \\
169169 \\General Options:
170 \\ --help Print this help and exit
171 \\ --verbose Print commands before executing them
172 \\ --prefix [path] Override default install prefix
173 \\ --search-prefix [path] Add a path to look for binaries, libraries, headers
170 \\ --help Print this help and exit
171 \\ --verbose Print commands before executing them
172 \\ --prefix [path] Override default install prefix
173 \\ --search-prefix [path] Add a path to look for binaries, libraries, headers
174174 \\
175175 \\Project-Specific Options:
176176 \\
......@@ -185,7 +185,7 @@ fn usage(builder: *Builder, already_ran_build: bool, out_stream: anytype) !void
185185 Builder.typeIdName(option.type_id),
186186 });
187187 defer allocator.free(name);
188 try out_stream.print("{s:32} {}\n", .{ name, option.description });
188 try out_stream.print("{s:<29} {}\n", .{ name, option.description });
189189 }
190190 }
191191
src/Cache.zig+24-13
......@@ -120,6 +120,13 @@ pub const HashHelper = struct {
120120 return copy.final();
121121 }
122122
123 pub fn peekBin(hh: HashHelper) [bin_digest_len]u8 {
124 var copy = hh;
125 var bin_digest: [bin_digest_len]u8 = undefined;
126 copy.hasher.final(&bin_digest);
127 return bin_digest;
128 }
129
123130 /// Returns a hex encoded hash of the inputs, mutating the state of the hasher.
124131 pub fn final(hh: *HashHelper) [hex_digest_len]u8 {
125132 var bin_digest: [bin_digest_len]u8 = undefined;
......@@ -338,19 +345,7 @@ pub const CacheHash = struct {
338345 if (any_file_changed) {
339346 // cache miss
340347 // keep the manifest file open
341 // reset the hash
342 self.hash.hasher = hasher_init;
343 self.hash.hasher.update(&bin_digest);
344
345 // Remove files not in the initial hash
346 for (self.files.items[input_file_count..]) |*file| {
347 file.deinit(self.cache.gpa);
348 }
349 self.files.shrinkRetainingCapacity(input_file_count);
350
351 for (self.files.items) |file| {
352 self.hash.hasher.update(&file.bin_digest);
353 }
348 self.unhit(bin_digest, input_file_count);
354349 return false;
355350 }
356351
......@@ -366,6 +361,22 @@ pub const CacheHash = struct {
366361 return true;
367362 }
368363
364 pub fn unhit(self: *CacheHash, bin_digest: [bin_digest_len]u8, input_file_count: usize) void {
365 // Reset the hash.
366 self.hash.hasher = hasher_init;
367 self.hash.hasher.update(&bin_digest);
368
369 // Remove files not in the initial hash.
370 for (self.files.items[input_file_count..]) |*file| {
371 file.deinit(self.cache.gpa);
372 }
373 self.files.shrinkRetainingCapacity(input_file_count);
374
375 for (self.files.items) |file| {
376 self.hash.hasher.update(&file.bin_digest);
377 }
378 }
379
369380 fn populateFileHash(self: *CacheHash, ch_file: *File) !void {
370381 const file = try fs.cwd().openFile(ch_file.path.?, .{});
371382 defer file.close();
src/Compilation.zig+49-11
......@@ -2200,20 +2200,34 @@ fn updateStage1Module(comp: *Compilation) !void {
22002200 ch.hash.add(comp.bin_file.options.function_sections);
22012201 ch.hash.add(comp.is_test);
22022202
2203 // Capture the state in case we come back from this branch where the hash doesn't match.
2204 const prev_hash_state = ch.hash.peekBin();
2205 const input_file_count = ch.files.items.len;
2206
22032207 if (try ch.hit()) {
22042208 const digest = ch.final();
22052209
22062210 var prev_digest_buf: [digest.len]u8 = undefined;
22072211 const prev_digest: []u8 = directory.handle.readLink(id_symlink_basename, &prev_digest_buf) catch |err| blk: {
2212 log.debug("stage1 {} new_digest={} readlink error: {}", .{ mod.root_pkg.root_src_path, digest, @errorName(err) });
22082213 // Handle this as a cache miss.
22092214 break :blk prev_digest_buf[0..0];
22102215 };
22112216 if (mem.eql(u8, prev_digest, &digest)) {
2217 log.debug("stage1 {} digest={} match - skipping invocation", .{ mod.root_pkg.root_src_path, digest });
22122218 comp.stage1_lock = ch.toOwnedLock();
22132219 return;
22142220 }
2221 log.debug("stage1 {} prev_digest={} new_digest={}", .{ mod.root_pkg.root_src_path, prev_digest, digest });
2222 ch.unhit(prev_hash_state, input_file_count);
22152223 }
22162224
2225 // We are about to change the output file to be different, so we invalidate the build hash now.
2226 directory.handle.deleteFile(id_symlink_basename) catch |err| switch (err) {
2227 error.FileNotFound => {},
2228 else => |e| return e,
2229 };
2230
22172231 const stage2_target = try arena.create(stage1.Stage2Target);
22182232 stage2_target.* = .{
22192233 .arch = @enumToInt(target.cpu.arch) + 1, // skip over ZigLLVM_UnknownArch
......@@ -2243,16 +2257,7 @@ fn updateStage1Module(comp: *Compilation) !void {
22432257 comp.is_test,
22442258 ) orelse return error.OutOfMemory;
22452259
2246 const stage1_pkg = try arena.create(stage1.Pkg);
2247 stage1_pkg.* = .{
2248 .name_ptr = undefined,
2249 .name_len = 0,
2250 .path_ptr = undefined,
2251 .path_len = 0,
2252 .children_ptr = undefined,
2253 .children_len = 0,
2254 .parent = null,
2255 };
2260 const stage1_pkg = try createStage1Pkg(arena, "root", mod.root_pkg, null);
22562261 const output_dir = comp.bin_file.options.directory.path orelse ".";
22572262 const test_filter = comp.test_filter orelse ""[0..0];
22582263 const test_name_prefix = comp.test_name_prefix orelse ""[0..0];
......@@ -2303,10 +2308,12 @@ fn updateStage1Module(comp: *Compilation) !void {
23032308
23042309 const digest = ch.final();
23052310
2311 log.debug("stage1 {} final digest={}", .{ mod.root_pkg.root_src_path, digest });
2312
23062313 // Update the dangling symlink with the digest. If it fails we can continue; it only
23072314 // means that the next invocation will have an unnecessary cache miss.
23082315 directory.handle.symLink(&digest, id_symlink_basename, .{}) catch |err| {
2309 std.log.warn("failed to save linking hash digest symlink: {}", .{@errorName(err)});
2316 std.log.warn("failed to save stage1 hash digest symlink: {}", .{@errorName(err)});
23102317 };
23112318 // Again failure here only means an unnecessary cache miss.
23122319 ch.writeManifest() catch |err| {
......@@ -2316,3 +2323,34 @@ fn updateStage1Module(comp: *Compilation) !void {
23162323 // other processes clobbering it.
23172324 comp.stage1_lock = ch.toOwnedLock();
23182325}
2326
2327fn createStage1Pkg(
2328 arena: *Allocator,
2329 name: []const u8,
2330 pkg: *Package,
2331 parent_pkg: ?*stage1.Pkg,
2332) error{OutOfMemory}!*stage1.Pkg {
2333 const child_pkg = try arena.create(stage1.Pkg);
2334
2335 const pkg_children = blk: {
2336 var children = std.ArrayList(*stage1.Pkg).init(arena);
2337 var it = pkg.table.iterator();
2338 while (it.next()) |entry| {
2339 try children.append(try createStage1Pkg(arena, entry.key, entry.value, child_pkg));
2340 }
2341 break :blk children.items;
2342 };
2343
2344 const src_path = try pkg.root_src_directory.join(arena, &[_][]const u8{pkg.root_src_path});
2345
2346 child_pkg.* = .{
2347 .name_ptr = name.ptr,
2348 .name_len = name.len,
2349 .path_ptr = src_path.ptr,
2350 .path_len = src_path.len,
2351 .children_ptr = pkg_children.ptr,
2352 .children_len = pkg_children.len,
2353 .parent = parent_pkg,
2354 };
2355 return child_pkg;
2356}
src/main.zig+332-26
......@@ -35,6 +35,7 @@ const usage =
3535 \\
3636 \\Commands:
3737 \\
38 \\ build Build project from build.zig
3839 \\ build-exe Create executable from source or object files
3940 \\ build-lib Create library from source or object files
4041 \\ build-obj Create object from source or assembly
......@@ -42,6 +43,8 @@ const usage =
4243 \\ c++ Use Zig as a drop-in C++ compiler
4344 \\ env Print lib path, std path, compiler id and version
4445 \\ fmt Parse file and render in canonical zig format
46 \\ init-exe Initialize a `zig build` application in the cwd
47 \\ init-lib Initialize a `zig build` library in the cwd
4548 \\ libc Display native libc paths file or validate one
4649 \\ run Create executable and run immediately
4750 \\ translate-c Convert C code to Zig code
......@@ -136,6 +139,8 @@ pub fn mainArgs(gpa: *Allocator, arena: *Allocator, args: []const []const u8) !v
136139 mem.eql(u8, cmd, "-cc1") or mem.eql(u8, cmd, "-cc1as"))
137140 {
138141 return punt_to_clang(arena, args);
142 } else if (mem.eql(u8, cmd, "build")) {
143 return cmdBuild(gpa, arena, cmd_args);
139144 } else if (mem.eql(u8, cmd, "fmt")) {
140145 return cmdFmt(gpa, cmd_args);
141146 } else if (mem.eql(u8, cmd, "libc")) {
......@@ -172,18 +177,18 @@ const usage_build_generic =
172177 \\Supported file types:
173178 \\ .zig Zig source code
174179 \\ .zir Zig Intermediate Representation code
175 \\ (planned) .o ELF object file
176 \\ (planned) .o MACH-O (macOS) object file
177 \\ (planned) .obj COFF (Windows) object file
178 \\ (planned) .lib COFF (Windows) static library
179 \\ (planned) .a ELF static library
180 \\ (planned) .so ELF shared object (dynamic link)
181 \\ (planned) .dll Windows Dynamic Link Library
182 \\ (planned) .dylib MACH-O (macOS) dynamic library
183 \\ (planned) .s Target-specific assembly source code
184 \\ (planned) .S Assembly with C preprocessor (requires LLVM extensions)
185 \\ (planned) .c C source code (requires LLVM extensions)
186 \\ (planned) .cpp C++ source code (requires LLVM extensions)
180 \\ .o ELF object file
181 \\ .o MACH-O (macOS) object file
182 \\ .obj COFF (Windows) object file
183 \\ .lib COFF (Windows) static library
184 \\ .a ELF static library
185 \\ .so ELF shared object (dynamic link)
186 \\ .dll Windows Dynamic Link Library
187 \\ .dylib MACH-O (macOS) dynamic library
188 \\ .s Target-specific assembly source code
189 \\ .S Assembly with C preprocessor (requires LLVM extensions)
190 \\ .c C source code (requires LLVM extensions)
191 \\ .cpp C++ source code (requires LLVM extensions)
187192 \\ Other C++ extensions: .C .cc .cxx
188193 \\
189194 \\General Options:
......@@ -195,6 +200,8 @@ const usage_build_generic =
195200 \\ --show-builtin Output the source of @import("builtin") then exit
196201 \\ --cache-dir [path] Override the local cache directory
197202 \\ --global-cache-dir [path] Override the global cache directory
203 \\ --override-lib-dir [path] Override path to Zig installation lib directory
204 \\ --enable-cache Output to cache directory; print path to stdout
198205 \\
199206 \\Compile Options:
200207 \\ -target [name] <arch><sub>-<os>-<abi> see the targets command
......@@ -357,6 +364,7 @@ pub fn buildOutputType(
357364 var test_name_prefix: ?[]const u8 = null;
358365 var override_local_cache_dir: ?[]const u8 = null;
359366 var override_global_cache_dir: ?[]const u8 = null;
367 var override_lib_dir: ?[]const u8 = null;
360368
361369 var system_libs = std.ArrayList([]const u8).init(gpa);
362370 defer system_libs.deinit();
......@@ -412,7 +420,7 @@ pub fn buildOutputType(
412420 if (mem.startsWith(u8, arg, "-")) {
413421 if (mem.eql(u8, arg, "-h") or mem.eql(u8, arg, "--help")) {
414422 try io.getStdOut().writeAll(usage_build_generic);
415 process.exit(0);
423 return process.cleanExit();
416424 } else if (mem.eql(u8, arg, "--")) {
417425 if (arg_mode == .run) {
418426 runtime_args_start = i + 1;
......@@ -547,6 +555,12 @@ pub fn buildOutputType(
547555 if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg});
548556 i += 1;
549557 override_global_cache_dir = args[i];
558 } else if (mem.eql(u8, arg, "--override-lib-dir")) {
559 if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg});
560 i += 1;
561 override_lib_dir = args[i];
562 } else if (mem.eql(u8, arg, "--enable-cache")) {
563 enable_cache = true;
550564 } else if (mem.eql(u8, arg, "--test-cmd-bin")) {
551565 try test_exec_args.append(null);
552566 } else if (mem.eql(u8, arg, "--test-evented-io")) {
......@@ -1102,12 +1116,22 @@ pub fn buildOutputType(
11021116 var cleanup_emit_bin_dir: ?fs.Dir = null;
11031117 defer if (cleanup_emit_bin_dir) |*dir| dir.close();
11041118
1119 const have_enable_cache = enable_cache orelse false;
1120
11051121 const emit_bin_loc: ?Compilation.EmitLoc = switch (emit_bin) {
11061122 .no => null,
11071123 .yes_default_path => Compilation.EmitLoc{
1108 .directory = switch (arg_mode) {
1109 .run, .zig_test => null,
1110 else => .{ .path = null, .handle = fs.cwd() },
1124 .directory = blk: {
1125 switch (arg_mode) {
1126 .run, .zig_test => break :blk null,
1127 else => {
1128 if (have_enable_cache) {
1129 break :blk null;
1130 } else {
1131 break :blk .{ .path = null, .handle = fs.cwd() };
1132 }
1133 },
1134 }
11111135 },
11121136 .basename = try std.zig.binNameAlloc(
11131137 arena,
......@@ -1120,6 +1144,12 @@ pub fn buildOutputType(
11201144 },
11211145 .yes => |full_path| b: {
11221146 const basename = fs.path.basename(full_path);
1147 if (have_enable_cache) {
1148 break :b Compilation.EmitLoc{
1149 .basename = basename,
1150 .directory = null,
1151 };
1152 }
11231153 if (fs.path.dirname(full_path)) |dirname| {
11241154 const handle = try fs.cwd().openDir(dirname, .{});
11251155 cleanup_emit_bin_dir = handle;
......@@ -1192,9 +1222,15 @@ pub fn buildOutputType(
11921222 } else null;
11931223
11941224 const self_exe_path = try fs.selfExePathAlloc(arena);
1195 var zig_lib_directory = introspect.findZigLibDirFromSelfExe(arena, self_exe_path) catch |err| {
1196 fatal("unable to find zig installation directory: {}\n", .{@errorName(err)});
1197 };
1225 var zig_lib_directory: Compilation.Directory = if (override_lib_dir) |lib_dir|
1226 .{
1227 .path = lib_dir,
1228 .handle = try fs.cwd().openDir(lib_dir, .{}),
1229 }
1230 else
1231 introspect.findZigLibDirFromSelfExe(arena, self_exe_path) catch |err| {
1232 fatal("unable to find zig installation directory: {}", .{@errorName(err)});
1233 };
11981234 defer zig_lib_directory.handle.close();
11991235
12001236 const random_seed = blk: {
......@@ -1337,7 +1373,20 @@ pub fn buildOutputType(
13371373 return cmdTranslateC(comp, arena);
13381374 }
13391375
1340 try updateModule(gpa, comp, zir_out_path);
1376 const hook: AfterUpdateHook = blk: {
1377 if (!have_enable_cache)
1378 break :blk .none;
1379
1380 switch (emit_bin) {
1381 .no => break :blk .none,
1382 .yes_default_path => break :blk .{
1383 .print = comp.bin_file.options.directory.path orelse ".",
1384 },
1385 .yes => |full_path| break :blk .{ .update = full_path },
1386 }
1387 };
1388
1389 try updateModule(gpa, comp, zir_out_path, hook);
13411390
13421391 if (build_options.have_llvm and only_pp_or_asm) {
13431392 // this may include dumping the output to stdout
......@@ -1389,7 +1438,7 @@ pub fn buildOutputType(
13891438 else => process.exit(1),
13901439 }
13911440 if (!watch)
1392 process.exit(0);
1441 return process.cleanExit();
13931442 },
13941443 else => {},
13951444 }
......@@ -1413,7 +1462,7 @@ pub fn buildOutputType(
14131462 if (output_mode == .Exe) {
14141463 try comp.makeBinFileWritable();
14151464 }
1416 try updateModule(gpa, comp, zir_out_path);
1465 try updateModule(gpa, comp, zir_out_path, hook);
14171466 } else if (mem.eql(u8, actual_line, "exit")) {
14181467 break;
14191468 } else if (mem.eql(u8, actual_line, "help")) {
......@@ -1427,7 +1476,13 @@ pub fn buildOutputType(
14271476 }
14281477}
14291478
1430fn updateModule(gpa: *Allocator, comp: *Compilation, zir_out_path: ?[]const u8) !void {
1479const AfterUpdateHook = union(enum) {
1480 none,
1481 print: []const u8,
1482 update: []const u8,
1483};
1484
1485fn updateModule(gpa: *Allocator, comp: *Compilation, zir_out_path: ?[]const u8, hook: AfterUpdateHook) !void {
14311486 try comp.update();
14321487
14331488 var errors = try comp.getAllErrorsAlloc();
......@@ -1437,6 +1492,15 @@ fn updateModule(gpa: *Allocator, comp: *Compilation, zir_out_path: ?[]const u8)
14371492 for (errors.list) |full_err_msg| {
14381493 full_err_msg.renderToStdErr();
14391494 }
1495 } else switch (hook) {
1496 .none => {},
1497 .print => |bin_path| try io.getStdOut().writer().print("{s}\n", .{bin_path}),
1498 .update => |full_path| _ = try comp.bin_file.options.directory.handle.updateFile(
1499 comp.bin_file.options.sub_path,
1500 fs.cwd(),
1501 full_path,
1502 .{},
1503 ),
14401504 }
14411505
14421506 if (zir_out_path) |zop| {
......@@ -1535,7 +1599,7 @@ pub fn cmdLibC(gpa: *Allocator, args: []const []const u8) !void {
15351599 if (mem.eql(u8, arg, "--help")) {
15361600 const stdout = io.getStdOut().writer();
15371601 try stdout.writeAll(usage_libc);
1538 process.exit(0);
1602 return process.cleanExit();
15391603 } else {
15401604 fatal("unrecognized parameter: '{}'", .{arg});
15411605 }
......@@ -1592,7 +1656,7 @@ pub fn cmdInit(
15921656 if (mem.startsWith(u8, arg, "-")) {
15931657 if (mem.eql(u8, arg, "--help")) {
15941658 try io.getStdOut().writeAll(usage_init);
1595 process.exit(0);
1659 return process.cleanExit();
15961660 } else {
15971661 fatal("unrecognized parameter: '{}'", .{arg});
15981662 }
......@@ -1657,6 +1721,248 @@ pub fn cmdInit(
16571721 }
16581722}
16591723
1724pub const usage_build =
1725 \\Usage: zig build [steps] [options]
1726 \\
1727 \\ Build a project from build.zig.
1728 \\
1729 \\Options:
1730 \\ --help Print this help and exit
1731 \\
1732 \\
1733;
1734
1735pub fn cmdBuild(gpa: *Allocator, arena: *Allocator, args: []const []const u8) !void {
1736 // We want to release all the locks before executing the child process, so we make a nice
1737 // big block here to ensure the cleanup gets run when we extract out our argv.
1738 const lock_and_argv = lock_and_argv: {
1739 const self_exe_path = try fs.selfExePathAlloc(arena);
1740
1741 var build_file: ?[]const u8 = null;
1742 var override_lib_dir: ?[]const u8 = null;
1743 var override_global_cache_dir: ?[]const u8 = null;
1744 var override_local_cache_dir: ?[]const u8 = null;
1745 var child_argv = std.ArrayList([]const u8).init(arena);
1746
1747 const argv_index_exe = child_argv.items.len;
1748 _ = try child_argv.addOne();
1749
1750 try child_argv.append(self_exe_path);
1751
1752 const argv_index_build_file = child_argv.items.len;
1753 _ = try child_argv.addOne();
1754
1755 const argv_index_cache_dir = child_argv.items.len;
1756 _ = try child_argv.addOne();
1757
1758 {
1759 var i: usize = 0;
1760 while (i < args.len) : (i += 1) {
1761 const arg = args[i];
1762 if (mem.startsWith(u8, arg, "-")) {
1763 if (mem.eql(u8, arg, "--build-file")) {
1764 if (i + 1 >= args.len) fatal("expected argument after '{}'", .{arg});
1765 i += 1;
1766 build_file = args[i];
1767 continue;
1768 } else if (mem.eql(u8, arg, "--override-lib-dir")) {
1769 if (i + 1 >= args.len) fatal("expected argument after '{}'", .{arg});
1770 i += 1;
1771 override_lib_dir = args[i];
1772 try child_argv.appendSlice(&[_][]const u8{ arg, args[i] });
1773 continue;
1774 } else if (mem.eql(u8, arg, "--cache-dir")) {
1775 if (i + 1 >= args.len) fatal("expected argument after '{}'", .{arg});
1776 i += 1;
1777 override_local_cache_dir = args[i];
1778 try child_argv.appendSlice(&[_][]const u8{ arg, args[i] });
1779 continue;
1780 } else if (mem.eql(u8, arg, "--global-cache-dir")) {
1781 if (i + 1 >= args.len) fatal("expected argument after '{}'", .{arg});
1782 i += 1;
1783 override_global_cache_dir = args[i];
1784 try child_argv.appendSlice(&[_][]const u8{ arg, args[i] });
1785 continue;
1786 }
1787 }
1788 try child_argv.append(arg);
1789 }
1790 }
1791
1792 var zig_lib_directory: Compilation.Directory = if (override_lib_dir) |lib_dir|
1793 .{
1794 .path = lib_dir,
1795 .handle = try fs.cwd().openDir(lib_dir, .{}),
1796 }
1797 else
1798 introspect.findZigLibDirFromSelfExe(arena, self_exe_path) catch |err| {
1799 fatal("unable to find zig installation directory: {}", .{@errorName(err)});
1800 };
1801 defer zig_lib_directory.handle.close();
1802
1803 const std_special = "std" ++ fs.path.sep_str ++ "special";
1804 const special_dir_path = try zig_lib_directory.join(arena, &[_][]const u8{std_special});
1805
1806 var root_pkg: Package = .{
1807 .root_src_directory = .{
1808 .path = special_dir_path,
1809 .handle = try zig_lib_directory.handle.openDir(std_special, .{}),
1810 },
1811 .root_src_path = "build_runner.zig",
1812 };
1813 defer root_pkg.root_src_directory.handle.close();
1814
1815 var cleanup_build_dir: ?fs.Dir = null;
1816 defer if (cleanup_build_dir) |*dir| dir.close();
1817
1818 const cwd_path = try process.getCwdAlloc(arena);
1819 const build_zig_basename = if (build_file) |bf| fs.path.basename(bf) else "build.zig";
1820 const build_directory: Compilation.Directory = blk: {
1821 if (build_file) |bf| {
1822 if (fs.path.dirname(bf)) |dirname| {
1823 const dir = try fs.cwd().openDir(dirname, .{});
1824 cleanup_build_dir = dir;
1825 break :blk .{ .path = dirname, .handle = dir };
1826 }
1827
1828 break :blk .{ .path = null, .handle = fs.cwd() };
1829 }
1830 // Search up parent directories until we find build.zig.
1831 var dirname: []const u8 = cwd_path;
1832 while (true) {
1833 const joined_path = try fs.path.join(arena, &[_][]const u8{ dirname, build_zig_basename });
1834 if (fs.cwd().access(joined_path, .{})) |_| {
1835 const dir = try fs.cwd().openDir(dirname, .{});
1836 break :blk .{ .path = dirname, .handle = dir };
1837 } else |err| switch (err) {
1838 error.FileNotFound => {
1839 dirname = fs.path.dirname(dirname) orelse {
1840 std.log.info("{}", .{
1841 \\Initialize a 'build.zig' template file with `zig init-lib` or `zig init-exe`,
1842 \\or see `zig --help` for more options.
1843 });
1844 fatal("No 'build.zig' file found, in the current directory or any parent directories.", .{});
1845 };
1846 continue;
1847 },
1848 else => |e| return e,
1849 }
1850 }
1851 };
1852 child_argv.items[argv_index_build_file] = build_directory.path orelse cwd_path;
1853
1854 var build_pkg: Package = .{
1855 .root_src_directory = build_directory,
1856 .root_src_path = build_zig_basename,
1857 };
1858 try root_pkg.table.put(arena, "@build", &build_pkg);
1859
1860 var global_cache_directory: Compilation.Directory = l: {
1861 const p = override_global_cache_dir orelse try introspect.resolveGlobalCacheDir(arena);
1862 break :l .{
1863 .handle = try fs.cwd().makeOpenPath(p, .{}),
1864 .path = p,
1865 };
1866 };
1867 defer global_cache_directory.handle.close();
1868
1869 var local_cache_directory: Compilation.Directory = l: {
1870 if (override_local_cache_dir) |local_cache_dir_path| {
1871 break :l .{
1872 .handle = try fs.cwd().makeOpenPath(local_cache_dir_path, .{}),
1873 .path = local_cache_dir_path,
1874 };
1875 }
1876 const cache_dir_path = try build_directory.join(arena, &[_][]const u8{"zig-cache"});
1877 break :l .{
1878 .handle = try build_directory.handle.makeOpenPath("zig-cache", .{}),
1879 .path = cache_dir_path,
1880 };
1881 };
1882 defer local_cache_directory.handle.close();
1883
1884 child_argv.items[argv_index_cache_dir] = local_cache_directory.path orelse cwd_path;
1885
1886 gimmeMoreOfThoseSweetSweetFileDescriptors();
1887
1888 const cross_target: std.zig.CrossTarget = .{};
1889 const target_info = try detectNativeTargetInfo(gpa, cross_target);
1890
1891 const exe_basename = try std.zig.binNameAlloc(arena, "build", target_info.target, .Exe, null, null);
1892 const emit_bin: Compilation.EmitLoc = .{
1893 .directory = null, // Use the local zig-cache.
1894 .basename = exe_basename,
1895 };
1896 const random_seed = blk: {
1897 var random_seed: u64 = undefined;
1898 try std.crypto.randomBytes(mem.asBytes(&random_seed));
1899 break :blk random_seed;
1900 };
1901 var default_prng = std.rand.DefaultPrng.init(random_seed);
1902 const comp = Compilation.create(gpa, .{
1903 .zig_lib_directory = zig_lib_directory,
1904 .local_cache_directory = local_cache_directory,
1905 .global_cache_directory = global_cache_directory,
1906 .root_name = "build",
1907 .target = target_info.target,
1908 .is_native_os = cross_target.isNativeOs(),
1909 .dynamic_linker = target_info.dynamic_linker.get(),
1910 .output_mode = .Exe,
1911 .root_pkg = &root_pkg,
1912 .emit_bin = emit_bin,
1913 .emit_h = null,
1914 .optimize_mode = .Debug,
1915 .self_exe_path = self_exe_path,
1916 .rand = &default_prng.random,
1917 }) catch |err| {
1918 fatal("unable to create compilation: {}", .{@errorName(err)});
1919 };
1920 defer comp.destroy();
1921
1922 try updateModule(gpa, comp, null, .none);
1923
1924 child_argv.items[argv_index_exe] = try comp.bin_file.options.directory.join(arena, &[_][]const u8{exe_basename});
1925
1926 break :lock_and_argv .{
1927 .child_argv = child_argv.items,
1928 .lock = comp.bin_file.toOwnedLock(),
1929 };
1930 };
1931 const child_argv = lock_and_argv.child_argv;
1932 var lock = lock_and_argv.lock;
1933 defer lock.release();
1934
1935 const child = try std.ChildProcess.init(child_argv, gpa);
1936 defer child.deinit();
1937
1938 child.stdin_behavior = .Inherit;
1939 child.stdout_behavior = .Inherit;
1940 child.stderr_behavior = .Inherit;
1941
1942 var cmd = std.ArrayList(u8).init(arena);
1943
1944 const term = try child.spawnAndWait();
1945 switch (term) {
1946 .Exited => |code| {
1947 if (code == 0) return process.cleanExit();
1948 try cmd.writer().print("failed with exit code {}:\n", .{code});
1949 },
1950 else => {
1951 try cmd.appendSlice("crashed:\n");
1952 },
1953 }
1954
1955 try cmd.append('\n');
1956 for (child_argv[0 .. child_argv.len - 1]) |arg| {
1957 try cmd.appendSlice(arg);
1958 try cmd.append(' ');
1959 }
1960 try cmd.appendSlice(child_argv[child_argv.len - 1]);
1961
1962 if (true) // Working around erroneous stage1 compile error: unreachable code on child.deinit()
1963 fatal("The following build command {}", .{cmd.items});
1964}
1965
16601966pub const usage_fmt =
16611967 \\Usage: zig fmt [file]...
16621968 \\
......@@ -1699,7 +2005,7 @@ pub fn cmdFmt(gpa: *Allocator, args: []const []const u8) !void {
16992005 if (mem.eql(u8, arg, "--help")) {
17002006 const stdout = io.getStdOut().outStream();
17012007 try stdout.writeAll(usage_fmt);
1702 process.exit(0);
2008 return process.cleanExit();
17032009 } else if (mem.eql(u8, arg, "--color")) {
17042010 if (i + 1 >= args.len) {
17052011 fatal("expected [auto|on|off] after --color", .{});
src/stage1.zig+2-2
......@@ -31,11 +31,11 @@ pub export fn main(argc: c_int, argv: [*]const [*:0]const u8) c_int {
3131 defer arena_instance.deinit();
3232 const arena = &arena_instance.allocator;
3333
34 const args = arena.alloc([]const u8, @intCast(usize, argc)) catch fatal("out of memory", .{});
34 const args = arena.alloc([]const u8, @intCast(usize, argc)) catch fatal("{}", .{"OutOfMemory"});
3535 for (args) |*arg, i| {
3636 arg.* = mem.spanZ(argv[i]);
3737 }
38 stage2.mainArgs(gpa, arena, args) catch |err| fatal("{}", .{err});
38 stage2.mainArgs(gpa, arena, args) catch |err| fatal("{}", .{@errorName(err)});
3939 return 0;
4040}
4141