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,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`
4 * repair @cImport1 * repair @cImport
5 * make sure zig cc works2 * make sure zig cc works
6 - using it as a preprocessor (-E)3 - using it as a preprocessor (-E)
...@@ -22,13 +19,16 @@...@@ -22,13 +19,16 @@
22 * COFF LLD linking19 * COFF LLD linking
23 * WASM LLD linking20 * WASM LLD linking
24 * --main-pkg-path21 * --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)
25 * audit the CLI options for stage225 * audit the CLI options for stage2
26 * audit the base cache hash26 * audit the base cache hash
27 * implement proper parsing of LLD stderr/stdout and exposing compile errors27 * implement proper parsing of LLD stderr/stdout and exposing compile errors
28 * implement proper parsing of clang stderr/stdout and exposing compile errors28 * implement proper parsing of clang stderr/stdout and exposing compile errors
29 * On operating systems that support it, do an execve for `zig test` and `zig run` rather than child process.29 * On operating systems that support it, do an execve for `zig test` and `zig run` rather than child process.
30 * restore error messages for stage2_add_link_lib30 * restore error messages for stage2_add_link_lib
31 * update zig build to use new CLI31 * update std/build.zig to use new CLI
3232
33 * support cross compiling stage2 with `zig build`33 * support cross compiling stage2 with `zig build`
34 * implement proper compile errors for failing to build glibc crt files and shared libs34 * 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 {...@@ -2294,8 +2294,7 @@ pub const LibExeObjStep = struct {
2294 if (self.kind == Kind.Test) {2294 if (self.kind == Kind.Test) {
2295 try builder.spawnChild(zig_args.span());2295 try builder.spawnChild(zig_args.span());
2296 } else {2296 } else {
2297 try zig_args.append("--cache");2297 try zig_args.append("--enable-cache");
2298 try zig_args.append("on");
22992298
2300 const output_dir_nl = try builder.execFromStep(zig_args.span(), &self.step);2299 const output_dir_nl = try builder.execFromStep(zig_args.span(), &self.step);
2301 const build_output_dir = mem.trimRight(u8, output_dir_nl, "\r\n");2300 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;...@@ -19,6 +19,19 @@ pub const exit = os.exit;
19pub const changeCurDir = os.chdir;19pub const changeCurDir = os.chdir;
20pub const changeCurDirC = os.chdirC;20pub 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
22/// The result is a slice of `out_buffer`, from index `0`.35/// The result is a slice of `out_buffer`, from index `0`.
23pub fn getCwd(out_buffer: []u8) ![]u8 {36pub fn getCwd(out_buffer: []u8) ![]u8 {
24 return os.getcwd(out_buffer);37 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...@@ -161,16 +161,16 @@ fn usage(builder: *Builder, already_ran_build: bool, out_stream: anytype) !void
161 try fmt.allocPrint(allocator, "{} (default)", .{top_level_step.step.name})161 try fmt.allocPrint(allocator, "{} (default)", .{top_level_step.step.name})
162 else162 else
163 top_level_step.step.name;163 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 });
165 }165 }
166166
167 try out_stream.writeAll(167 try out_stream.writeAll(
168 \\168 \\
169 \\General Options:169 \\General Options:
170 \\ --help Print this help and exit170 \\ --help Print this help and exit
171 \\ --verbose Print commands before executing them171 \\ --verbose Print commands before executing them
172 \\ --prefix [path] Override default install prefix172 \\ --prefix [path] Override default install prefix
173 \\ --search-prefix [path] Add a path to look for binaries, libraries, headers173 \\ --search-prefix [path] Add a path to look for binaries, libraries, headers
174 \\174 \\
175 \\Project-Specific Options:175 \\Project-Specific Options:
176 \\176 \\
...@@ -185,7 +185,7 @@ fn usage(builder: *Builder, already_ran_build: bool, out_stream: anytype) !void...@@ -185,7 +185,7 @@ fn usage(builder: *Builder, already_ran_build: bool, out_stream: anytype) !void
185 Builder.typeIdName(option.type_id),185 Builder.typeIdName(option.type_id),
186 });186 });
187 defer allocator.free(name);187 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 });
189 }189 }
190 }190 }
191191
src/Cache.zig+24-13
...@@ -120,6 +120,13 @@ pub const HashHelper = struct {...@@ -120,6 +120,13 @@ pub const HashHelper = struct {
120 return copy.final();120 return copy.final();
121 }121 }
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
123 /// Returns a hex encoded hash of the inputs, mutating the state of the hasher.130 /// Returns a hex encoded hash of the inputs, mutating the state of the hasher.
124 pub fn final(hh: *HashHelper) [hex_digest_len]u8 {131 pub fn final(hh: *HashHelper) [hex_digest_len]u8 {
125 var bin_digest: [bin_digest_len]u8 = undefined;132 var bin_digest: [bin_digest_len]u8 = undefined;
...@@ -338,19 +345,7 @@ pub const CacheHash = struct {...@@ -338,19 +345,7 @@ pub const CacheHash = struct {
338 if (any_file_changed) {345 if (any_file_changed) {
339 // cache miss346 // cache miss
340 // keep the manifest file open347 // keep the manifest file open
341 // reset the hash348 self.unhit(bin_digest, input_file_count);
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 }
354 return false;349 return false;
355 }350 }
356351
...@@ -366,6 +361,22 @@ pub const CacheHash = struct {...@@ -366,6 +361,22 @@ pub const CacheHash = struct {
366 return true;361 return true;
367 }362 }
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
369 fn populateFileHash(self: *CacheHash, ch_file: *File) !void {380 fn populateFileHash(self: *CacheHash, ch_file: *File) !void {
370 const file = try fs.cwd().openFile(ch_file.path.?, .{});381 const file = try fs.cwd().openFile(ch_file.path.?, .{});
371 defer file.close();382 defer file.close();
src/Compilation.zig+49-11
...@@ -2200,20 +2200,34 @@ fn updateStage1Module(comp: *Compilation) !void {...@@ -2200,20 +2200,34 @@ fn updateStage1Module(comp: *Compilation) !void {
2200 ch.hash.add(comp.bin_file.options.function_sections);2200 ch.hash.add(comp.bin_file.options.function_sections);
2201 ch.hash.add(comp.is_test);2201 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
2203 if (try ch.hit()) {2207 if (try ch.hit()) {
2204 const digest = ch.final();2208 const digest = ch.final();
22052209
2206 var prev_digest_buf: [digest.len]u8 = undefined;2210 var prev_digest_buf: [digest.len]u8 = undefined;
2207 const prev_digest: []u8 = directory.handle.readLink(id_symlink_basename, &prev_digest_buf) catch |err| blk: {2211 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) });
2208 // Handle this as a cache miss.2213 // Handle this as a cache miss.
2209 break :blk prev_digest_buf[0..0];2214 break :blk prev_digest_buf[0..0];
2210 };2215 };
2211 if (mem.eql(u8, prev_digest, &digest)) {2216 if (mem.eql(u8, prev_digest, &digest)) {
2217 log.debug("stage1 {} digest={} match - skipping invocation", .{ mod.root_pkg.root_src_path, digest });
2212 comp.stage1_lock = ch.toOwnedLock();2218 comp.stage1_lock = ch.toOwnedLock();
2213 return;2219 return;
2214 }2220 }
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);
2215 }2223 }
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
2217 const stage2_target = try arena.create(stage1.Stage2Target);2231 const stage2_target = try arena.create(stage1.Stage2Target);
2218 stage2_target.* = .{2232 stage2_target.* = .{
2219 .arch = @enumToInt(target.cpu.arch) + 1, // skip over ZigLLVM_UnknownArch2233 .arch = @enumToInt(target.cpu.arch) + 1, // skip over ZigLLVM_UnknownArch
...@@ -2243,16 +2257,7 @@ fn updateStage1Module(comp: *Compilation) !void {...@@ -2243,16 +2257,7 @@ fn updateStage1Module(comp: *Compilation) !void {
2243 comp.is_test,2257 comp.is_test,
2244 ) orelse return error.OutOfMemory;2258 ) orelse return error.OutOfMemory;
22452259
2246 const stage1_pkg = try arena.create(stage1.Pkg);2260 const stage1_pkg = try createStage1Pkg(arena, "root", mod.root_pkg, null);
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 };
2256 const output_dir = comp.bin_file.options.directory.path orelse ".";2261 const output_dir = comp.bin_file.options.directory.path orelse ".";
2257 const test_filter = comp.test_filter orelse ""[0..0];2262 const test_filter = comp.test_filter orelse ""[0..0];
2258 const test_name_prefix = comp.test_name_prefix orelse ""[0..0];2263 const test_name_prefix = comp.test_name_prefix orelse ""[0..0];
...@@ -2303,10 +2308,12 @@ fn updateStage1Module(comp: *Compilation) !void {...@@ -2303,10 +2308,12 @@ fn updateStage1Module(comp: *Compilation) !void {
23032308
2304 const digest = ch.final();2309 const digest = ch.final();
23052310
2311 log.debug("stage1 {} final digest={}", .{ mod.root_pkg.root_src_path, digest });
2312
2306 // Update the dangling symlink with the digest. If it fails we can continue; it only2313 // Update the dangling symlink with the digest. If it fails we can continue; it only
2307 // means that the next invocation will have an unnecessary cache miss.2314 // means that the next invocation will have an unnecessary cache miss.
2308 directory.handle.symLink(&digest, id_symlink_basename, .{}) catch |err| {2315 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)});
2310 };2317 };
2311 // Again failure here only means an unnecessary cache miss.2318 // Again failure here only means an unnecessary cache miss.
2312 ch.writeManifest() catch |err| {2319 ch.writeManifest() catch |err| {
...@@ -2316,3 +2323,34 @@ fn updateStage1Module(comp: *Compilation) !void {...@@ -2316,3 +2323,34 @@ fn updateStage1Module(comp: *Compilation) !void {
2316 // other processes clobbering it.2323 // other processes clobbering it.
2317 comp.stage1_lock = ch.toOwnedLock();2324 comp.stage1_lock = ch.toOwnedLock();
2318}2325}
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 =...@@ -35,6 +35,7 @@ const usage =
35 \\35 \\
36 \\Commands:36 \\Commands:
37 \\37 \\
38 \\ build Build project from build.zig
38 \\ build-exe Create executable from source or object files39 \\ build-exe Create executable from source or object files
39 \\ build-lib Create library from source or object files40 \\ build-lib Create library from source or object files
40 \\ build-obj Create object from source or assembly41 \\ build-obj Create object from source or assembly
...@@ -42,6 +43,8 @@ const usage =...@@ -42,6 +43,8 @@ const usage =
42 \\ c++ Use Zig as a drop-in C++ compiler43 \\ c++ Use Zig as a drop-in C++ compiler
43 \\ env Print lib path, std path, compiler id and version44 \\ env Print lib path, std path, compiler id and version
44 \\ fmt Parse file and render in canonical zig format45 \\ 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
45 \\ libc Display native libc paths file or validate one48 \\ libc Display native libc paths file or validate one
46 \\ run Create executable and run immediately49 \\ run Create executable and run immediately
47 \\ translate-c Convert C code to Zig code50 \\ translate-c Convert C code to Zig code
...@@ -136,6 +139,8 @@ pub fn mainArgs(gpa: *Allocator, arena: *Allocator, args: []const []const u8) !v...@@ -136,6 +139,8 @@ pub fn mainArgs(gpa: *Allocator, arena: *Allocator, args: []const []const u8) !v
136 mem.eql(u8, cmd, "-cc1") or mem.eql(u8, cmd, "-cc1as"))139 mem.eql(u8, cmd, "-cc1") or mem.eql(u8, cmd, "-cc1as"))
137 {140 {
138 return punt_to_clang(arena, args);141 return punt_to_clang(arena, args);
142 } else if (mem.eql(u8, cmd, "build")) {
143 return cmdBuild(gpa, arena, cmd_args);
139 } else if (mem.eql(u8, cmd, "fmt")) {144 } else if (mem.eql(u8, cmd, "fmt")) {
140 return cmdFmt(gpa, cmd_args);145 return cmdFmt(gpa, cmd_args);
141 } else if (mem.eql(u8, cmd, "libc")) {146 } else if (mem.eql(u8, cmd, "libc")) {
...@@ -172,18 +177,18 @@ const usage_build_generic =...@@ -172,18 +177,18 @@ const usage_build_generic =
172 \\Supported file types:177 \\Supported file types:
173 \\ .zig Zig source code178 \\ .zig Zig source code
174 \\ .zir Zig Intermediate Representation code179 \\ .zir Zig Intermediate Representation code
175 \\ (planned) .o ELF object file180 \\ .o ELF object file
176 \\ (planned) .o MACH-O (macOS) object file181 \\ .o MACH-O (macOS) object file
177 \\ (planned) .obj COFF (Windows) object file182 \\ .obj COFF (Windows) object file
178 \\ (planned) .lib COFF (Windows) static library183 \\ .lib COFF (Windows) static library
179 \\ (planned) .a ELF static library184 \\ .a ELF static library
180 \\ (planned) .so ELF shared object (dynamic link)185 \\ .so ELF shared object (dynamic link)
181 \\ (planned) .dll Windows Dynamic Link Library186 \\ .dll Windows Dynamic Link Library
182 \\ (planned) .dylib MACH-O (macOS) dynamic library187 \\ .dylib MACH-O (macOS) dynamic library
183 \\ (planned) .s Target-specific assembly source code188 \\ .s Target-specific assembly source code
184 \\ (planned) .S Assembly with C preprocessor (requires LLVM extensions)189 \\ .S Assembly with C preprocessor (requires LLVM extensions)
185 \\ (planned) .c C source code (requires LLVM extensions)190 \\ .c C source code (requires LLVM extensions)
186 \\ (planned) .cpp C++ source code (requires LLVM extensions)191 \\ .cpp C++ source code (requires LLVM extensions)
187 \\ Other C++ extensions: .C .cc .cxx192 \\ Other C++ extensions: .C .cc .cxx
188 \\193 \\
189 \\General Options:194 \\General Options:
...@@ -195,6 +200,8 @@ const usage_build_generic =...@@ -195,6 +200,8 @@ const usage_build_generic =
195 \\ --show-builtin Output the source of @import("builtin") then exit200 \\ --show-builtin Output the source of @import("builtin") then exit
196 \\ --cache-dir [path] Override the local cache directory201 \\ --cache-dir [path] Override the local cache directory
197 \\ --global-cache-dir [path] Override the global cache directory202 \\ --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
198 \\205 \\
199 \\Compile Options:206 \\Compile Options:
200 \\ -target [name] <arch><sub>-<os>-<abi> see the targets command207 \\ -target [name] <arch><sub>-<os>-<abi> see the targets command
...@@ -357,6 +364,7 @@ pub fn buildOutputType(...@@ -357,6 +364,7 @@ pub fn buildOutputType(
357 var test_name_prefix: ?[]const u8 = null;364 var test_name_prefix: ?[]const u8 = null;
358 var override_local_cache_dir: ?[]const u8 = null;365 var override_local_cache_dir: ?[]const u8 = null;
359 var override_global_cache_dir: ?[]const u8 = null;366 var override_global_cache_dir: ?[]const u8 = null;
367 var override_lib_dir: ?[]const u8 = null;
360368
361 var system_libs = std.ArrayList([]const u8).init(gpa);369 var system_libs = std.ArrayList([]const u8).init(gpa);
362 defer system_libs.deinit();370 defer system_libs.deinit();
...@@ -412,7 +420,7 @@ pub fn buildOutputType(...@@ -412,7 +420,7 @@ pub fn buildOutputType(
412 if (mem.startsWith(u8, arg, "-")) {420 if (mem.startsWith(u8, arg, "-")) {
413 if (mem.eql(u8, arg, "-h") or mem.eql(u8, arg, "--help")) {421 if (mem.eql(u8, arg, "-h") or mem.eql(u8, arg, "--help")) {
414 try io.getStdOut().writeAll(usage_build_generic);422 try io.getStdOut().writeAll(usage_build_generic);
415 process.exit(0);423 return process.cleanExit();
416 } else if (mem.eql(u8, arg, "--")) {424 } else if (mem.eql(u8, arg, "--")) {
417 if (arg_mode == .run) {425 if (arg_mode == .run) {
418 runtime_args_start = i + 1;426 runtime_args_start = i + 1;
...@@ -547,6 +555,12 @@ pub fn buildOutputType(...@@ -547,6 +555,12 @@ pub fn buildOutputType(
547 if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg});555 if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg});
548 i += 1;556 i += 1;
549 override_global_cache_dir = args[i];557 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;
550 } else if (mem.eql(u8, arg, "--test-cmd-bin")) {564 } else if (mem.eql(u8, arg, "--test-cmd-bin")) {
551 try test_exec_args.append(null);565 try test_exec_args.append(null);
552 } else if (mem.eql(u8, arg, "--test-evented-io")) {566 } else if (mem.eql(u8, arg, "--test-evented-io")) {
...@@ -1102,12 +1116,22 @@ pub fn buildOutputType(...@@ -1102,12 +1116,22 @@ pub fn buildOutputType(
1102 var cleanup_emit_bin_dir: ?fs.Dir = null;1116 var cleanup_emit_bin_dir: ?fs.Dir = null;
1103 defer if (cleanup_emit_bin_dir) |*dir| dir.close();1117 defer if (cleanup_emit_bin_dir) |*dir| dir.close();
11041118
1119 const have_enable_cache = enable_cache orelse false;
1120
1105 const emit_bin_loc: ?Compilation.EmitLoc = switch (emit_bin) {1121 const emit_bin_loc: ?Compilation.EmitLoc = switch (emit_bin) {
1106 .no => null,1122 .no => null,
1107 .yes_default_path => Compilation.EmitLoc{1123 .yes_default_path => Compilation.EmitLoc{
1108 .directory = switch (arg_mode) {1124 .directory = blk: {
1109 .run, .zig_test => null,1125 switch (arg_mode) {
1110 else => .{ .path = null, .handle = fs.cwd() },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 }
1111 },1135 },
1112 .basename = try std.zig.binNameAlloc(1136 .basename = try std.zig.binNameAlloc(
1113 arena,1137 arena,
...@@ -1120,6 +1144,12 @@ pub fn buildOutputType(...@@ -1120,6 +1144,12 @@ pub fn buildOutputType(
1120 },1144 },
1121 .yes => |full_path| b: {1145 .yes => |full_path| b: {
1122 const basename = fs.path.basename(full_path);1146 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 }
1123 if (fs.path.dirname(full_path)) |dirname| {1153 if (fs.path.dirname(full_path)) |dirname| {
1124 const handle = try fs.cwd().openDir(dirname, .{});1154 const handle = try fs.cwd().openDir(dirname, .{});
1125 cleanup_emit_bin_dir = handle;1155 cleanup_emit_bin_dir = handle;
...@@ -1192,9 +1222,15 @@ pub fn buildOutputType(...@@ -1192,9 +1222,15 @@ pub fn buildOutputType(
1192 } else null;1222 } else null;
11931223
1194 const self_exe_path = try fs.selfExePathAlloc(arena);1224 const self_exe_path = try fs.selfExePathAlloc(arena);
1195 var zig_lib_directory = introspect.findZigLibDirFromSelfExe(arena, self_exe_path) catch |err| {1225 var zig_lib_directory: Compilation.Directory = if (override_lib_dir) |lib_dir|
1196 fatal("unable to find zig installation directory: {}\n", .{@errorName(err)});1226 .{
1197 };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 };
1198 defer zig_lib_directory.handle.close();1234 defer zig_lib_directory.handle.close();
11991235
1200 const random_seed = blk: {1236 const random_seed = blk: {
...@@ -1337,7 +1373,20 @@ pub fn buildOutputType(...@@ -1337,7 +1373,20 @@ pub fn buildOutputType(
1337 return cmdTranslateC(comp, arena);1373 return cmdTranslateC(comp, arena);
1338 }1374 }
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
1342 if (build_options.have_llvm and only_pp_or_asm) {1391 if (build_options.have_llvm and only_pp_or_asm) {
1343 // this may include dumping the output to stdout1392 // this may include dumping the output to stdout
...@@ -1389,7 +1438,7 @@ pub fn buildOutputType(...@@ -1389,7 +1438,7 @@ pub fn buildOutputType(
1389 else => process.exit(1),1438 else => process.exit(1),
1390 }1439 }
1391 if (!watch)1440 if (!watch)
1392 process.exit(0);1441 return process.cleanExit();
1393 },1442 },
1394 else => {},1443 else => {},
1395 }1444 }
...@@ -1413,7 +1462,7 @@ pub fn buildOutputType(...@@ -1413,7 +1462,7 @@ pub fn buildOutputType(
1413 if (output_mode == .Exe) {1462 if (output_mode == .Exe) {
1414 try comp.makeBinFileWritable();1463 try comp.makeBinFileWritable();
1415 }1464 }
1416 try updateModule(gpa, comp, zir_out_path);1465 try updateModule(gpa, comp, zir_out_path, hook);
1417 } else if (mem.eql(u8, actual_line, "exit")) {1466 } else if (mem.eql(u8, actual_line, "exit")) {
1418 break;1467 break;
1419 } else if (mem.eql(u8, actual_line, "help")) {1468 } else if (mem.eql(u8, actual_line, "help")) {
...@@ -1427,7 +1476,13 @@ pub fn buildOutputType(...@@ -1427,7 +1476,13 @@ pub fn buildOutputType(
1427 }1476 }
1428}1477}
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 {
1431 try comp.update();1486 try comp.update();
14321487
1433 var errors = try comp.getAllErrorsAlloc();1488 var errors = try comp.getAllErrorsAlloc();
...@@ -1437,6 +1492,15 @@ fn updateModule(gpa: *Allocator, comp: *Compilation, zir_out_path: ?[]const u8)...@@ -1437,6 +1492,15 @@ fn updateModule(gpa: *Allocator, comp: *Compilation, zir_out_path: ?[]const u8)
1437 for (errors.list) |full_err_msg| {1492 for (errors.list) |full_err_msg| {
1438 full_err_msg.renderToStdErr();1493 full_err_msg.renderToStdErr();
1439 }1494 }
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 ),
1440 }1504 }
14411505
1442 if (zir_out_path) |zop| {1506 if (zir_out_path) |zop| {
...@@ -1535,7 +1599,7 @@ pub fn cmdLibC(gpa: *Allocator, args: []const []const u8) !void {...@@ -1535,7 +1599,7 @@ pub fn cmdLibC(gpa: *Allocator, args: []const []const u8) !void {
1535 if (mem.eql(u8, arg, "--help")) {1599 if (mem.eql(u8, arg, "--help")) {
1536 const stdout = io.getStdOut().writer();1600 const stdout = io.getStdOut().writer();
1537 try stdout.writeAll(usage_libc);1601 try stdout.writeAll(usage_libc);
1538 process.exit(0);1602 return process.cleanExit();
1539 } else {1603 } else {
1540 fatal("unrecognized parameter: '{}'", .{arg});1604 fatal("unrecognized parameter: '{}'", .{arg});
1541 }1605 }
...@@ -1592,7 +1656,7 @@ pub fn cmdInit(...@@ -1592,7 +1656,7 @@ pub fn cmdInit(
1592 if (mem.startsWith(u8, arg, "-")) {1656 if (mem.startsWith(u8, arg, "-")) {
1593 if (mem.eql(u8, arg, "--help")) {1657 if (mem.eql(u8, arg, "--help")) {
1594 try io.getStdOut().writeAll(usage_init);1658 try io.getStdOut().writeAll(usage_init);
1595 process.exit(0);1659 return process.cleanExit();
1596 } else {1660 } else {
1597 fatal("unrecognized parameter: '{}'", .{arg});1661 fatal("unrecognized parameter: '{}'", .{arg});
1598 }1662 }
...@@ -1657,6 +1721,248 @@ pub fn cmdInit(...@@ -1657,6 +1721,248 @@ pub fn cmdInit(
1657 }1721 }
1658}1722}
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
1660pub const usage_fmt =1966pub const usage_fmt =
1661 \\Usage: zig fmt [file]...1967 \\Usage: zig fmt [file]...
1662 \\1968 \\
...@@ -1699,7 +2005,7 @@ pub fn cmdFmt(gpa: *Allocator, args: []const []const u8) !void {...@@ -1699,7 +2005,7 @@ pub fn cmdFmt(gpa: *Allocator, args: []const []const u8) !void {
1699 if (mem.eql(u8, arg, "--help")) {2005 if (mem.eql(u8, arg, "--help")) {
1700 const stdout = io.getStdOut().outStream();2006 const stdout = io.getStdOut().outStream();
1701 try stdout.writeAll(usage_fmt);2007 try stdout.writeAll(usage_fmt);
1702 process.exit(0);2008 return process.cleanExit();
1703 } else if (mem.eql(u8, arg, "--color")) {2009 } else if (mem.eql(u8, arg, "--color")) {
1704 if (i + 1 >= args.len) {2010 if (i + 1 >= args.len) {
1705 fatal("expected [auto|on|off] after --color", .{});2011 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 {...@@ -31,11 +31,11 @@ pub export fn main(argc: c_int, argv: [*]const [*:0]const u8) c_int {
31 defer arena_instance.deinit();31 defer arena_instance.deinit();
32 const arena = &arena_instance.allocator;32 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"});
35 for (args) |*arg, i| {35 for (args) |*arg, i| {
36 arg.* = mem.spanZ(argv[i]);36 arg.* = mem.spanZ(argv[i]);
37 }37 }
38 stage2.mainArgs(gpa, arena, args) catch |err| fatal("{}", .{err});38 stage2.mainArgs(gpa, arena, args) catch |err| fatal("{}", .{@errorName(err)});
39 return 0;39 return 0;
40}40}
4141