mirror of
https://github.com/ziglang/zig.git
synced 2025-02-14 16:40:17 +00:00
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".
This commit is contained in:
parent
250664bea4
commit
c2b1cd7c45
@ -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
|
||||
|
@ -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");
|
||||
|
@ -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);
|
||||
|
@ -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 });
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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();
|
||||
|
@ -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;
|
||||
}
|
||||
|
358
src/main.zig
358
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", .{});
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user