aboutsummaryrefslogtreecommitdiff
path: root/lib/std/Build.zig
diff options
context:
space:
mode:
authorAndrew Kelley <andrew@ziglang.org>2023-03-16 17:33:24 -0700
committerAndrew Kelley <andrew@ziglang.org>2023-03-16 17:33:24 -0700
commit1ed569e0b23c4432cd00604dcae89a17edc852a9 (patch)
tree090e0b3817a0caa4f3e7b99ec1d4d965f2bc7438 /lib/std/Build.zig
parent778ca2ae6bf025edb6babeec08c957be1fbb37a5 (diff)
parentb4d58e93ea4d0bbfe674f80d301279d302fe8fc8 (diff)
downloadzig-1ed569e0b23c4432cd00604dcae89a17edc852a9.tar.gz
zig-1ed569e0b23c4432cd00604dcae89a17edc852a9.zip
Merge remote-tracking branch 'origin/master' into llvm16
Diffstat (limited to 'lib/std/Build.zig')
-rw-r--r--lib/std/Build.zig488
1 files changed, 243 insertions, 245 deletions
diff --git a/lib/std/Build.zig b/lib/std/Build.zig
index 26919962e3..279dd765b5 100644
--- a/lib/std/Build.zig
+++ b/lib/std/Build.zig
@@ -32,14 +32,12 @@ pub const Step = @import("Build/Step.zig");
pub const CheckFileStep = @import("Build/CheckFileStep.zig");
pub const CheckObjectStep = @import("Build/CheckObjectStep.zig");
pub const ConfigHeaderStep = @import("Build/ConfigHeaderStep.zig");
-pub const EmulatableRunStep = @import("Build/EmulatableRunStep.zig");
pub const FmtStep = @import("Build/FmtStep.zig");
pub const InstallArtifactStep = @import("Build/InstallArtifactStep.zig");
pub const InstallDirStep = @import("Build/InstallDirStep.zig");
pub const InstallFileStep = @import("Build/InstallFileStep.zig");
pub const ObjCopyStep = @import("Build/ObjCopyStep.zig");
pub const CompileStep = @import("Build/CompileStep.zig");
-pub const LogStep = @import("Build/LogStep.zig");
pub const OptionsStep = @import("Build/OptionsStep.zig");
pub const RemoveDirStep = @import("Build/RemoveDirStep.zig");
pub const RunStep = @import("Build/RunStep.zig");
@@ -59,15 +57,12 @@ verbose_air: bool,
verbose_llvm_ir: bool,
verbose_cimport: bool,
verbose_llvm_cpu_features: bool,
-/// The purpose of executing the command is for a human to read compile errors from the terminal
-prominent_compile_errors: bool,
-color: enum { auto, on, off } = .auto,
reference_trace: ?u32 = null,
invalid_user_input: bool,
zig_exe: []const u8,
default_step: *Step,
env_map: *EnvMap,
-top_level_steps: ArrayList(*TopLevelStep),
+top_level_steps: std.StringArrayHashMapUnmanaged(*TopLevelStep),
install_prefix: []const u8,
dest_dir: ?[]const u8,
lib_dir: []const u8,
@@ -90,6 +85,7 @@ pkg_config_pkg_list: ?(PkgConfigError![]const PkgConfigPkg) = null,
args: ?[][]const u8 = null,
debug_log_scopes: []const []const u8 = &.{},
debug_compile_errors: bool = false,
+debug_pkg_config: bool = false,
/// Experimental. Use system Darling installation to run cross compiled macOS build artifacts.
enable_darling: bool = false,
@@ -198,7 +194,7 @@ pub fn create(
env_map.* = try process.getEnvMap(allocator);
const self = try allocator.create(Build);
- self.* = Build{
+ self.* = .{
.zig_exe = zig_exe,
.build_root = build_root,
.cache_root = cache_root,
@@ -211,13 +207,12 @@ pub fn create(
.verbose_llvm_ir = false,
.verbose_cimport = false,
.verbose_llvm_cpu_features = false,
- .prominent_compile_errors = false,
.invalid_user_input = false,
.allocator = allocator,
.user_input_options = UserInputOptionsMap.init(allocator),
.available_options_map = AvailableOptionsMap.init(allocator),
.available_options_list = ArrayList(AvailableOption).init(allocator),
- .top_level_steps = ArrayList(*TopLevelStep).init(allocator),
+ .top_level_steps = .{},
.default_step = undefined,
.env_map = env_map,
.search_prefixes = ArrayList([]const u8).init(allocator),
@@ -227,12 +222,21 @@ pub fn create(
.h_dir = undefined,
.dest_dir = env_map.get("DESTDIR"),
.installed_files = ArrayList(InstalledFile).init(allocator),
- .install_tls = TopLevelStep{
- .step = Step.initNoOp(.top_level, "install", allocator),
+ .install_tls = .{
+ .step = Step.init(.{
+ .id = .top_level,
+ .name = "install",
+ .owner = self,
+ }),
.description = "Copy build artifacts to prefix path",
},
- .uninstall_tls = TopLevelStep{
- .step = Step.init(.top_level, "uninstall", allocator, makeUninstall),
+ .uninstall_tls = .{
+ .step = Step.init(.{
+ .id = .top_level,
+ .name = "uninstall",
+ .owner = self,
+ .makeFn = makeUninstall,
+ }),
.description = "Remove build artifacts from prefix path",
},
.zig_lib_dir = null,
@@ -241,8 +245,8 @@ pub fn create(
.host = host,
.modules = std.StringArrayHashMap(*Module).init(allocator),
};
- try self.top_level_steps.append(&self.install_tls);
- try self.top_level_steps.append(&self.uninstall_tls);
+ try self.top_level_steps.put(allocator, self.install_tls.step.name, &self.install_tls);
+ try self.top_level_steps.put(allocator, self.uninstall_tls.step.name, &self.uninstall_tls);
self.default_step = &self.install_tls.step;
return self;
}
@@ -264,11 +268,20 @@ fn createChildOnly(parent: *Build, dep_name: []const u8, build_root: Cache.Direc
child.* = .{
.allocator = allocator,
.install_tls = .{
- .step = Step.initNoOp(.top_level, "install", allocator),
+ .step = Step.init(.{
+ .id = .top_level,
+ .name = "install",
+ .owner = child,
+ }),
.description = "Copy build artifacts to prefix path",
},
.uninstall_tls = .{
- .step = Step.init(.top_level, "uninstall", allocator, makeUninstall),
+ .step = Step.init(.{
+ .id = .top_level,
+ .name = "uninstall",
+ .owner = child,
+ .makeFn = makeUninstall,
+ }),
.description = "Remove build artifacts from prefix path",
},
.user_input_options = UserInputOptionsMap.init(allocator),
@@ -281,14 +294,12 @@ fn createChildOnly(parent: *Build, dep_name: []const u8, build_root: Cache.Direc
.verbose_llvm_ir = parent.verbose_llvm_ir,
.verbose_cimport = parent.verbose_cimport,
.verbose_llvm_cpu_features = parent.verbose_llvm_cpu_features,
- .prominent_compile_errors = parent.prominent_compile_errors,
- .color = parent.color,
.reference_trace = parent.reference_trace,
.invalid_user_input = false,
.zig_exe = parent.zig_exe,
.default_step = undefined,
.env_map = parent.env_map,
- .top_level_steps = ArrayList(*TopLevelStep).init(allocator),
+ .top_level_steps = .{},
.install_prefix = undefined,
.dest_dir = parent.dest_dir,
.lib_dir = parent.lib_dir,
@@ -306,6 +317,7 @@ fn createChildOnly(parent: *Build, dep_name: []const u8, build_root: Cache.Direc
.zig_lib_dir = parent.zig_lib_dir,
.debug_log_scopes = parent.debug_log_scopes,
.debug_compile_errors = parent.debug_compile_errors,
+ .debug_pkg_config = parent.debug_pkg_config,
.enable_darling = parent.enable_darling,
.enable_qemu = parent.enable_qemu,
.enable_rosetta = parent.enable_rosetta,
@@ -316,8 +328,8 @@ fn createChildOnly(parent: *Build, dep_name: []const u8, build_root: Cache.Direc
.dep_prefix = parent.fmt("{s}{s}.", .{ parent.dep_prefix, dep_name }),
.modules = std.StringArrayHashMap(*Module).init(allocator),
};
- try child.top_level_steps.append(&child.install_tls);
- try child.top_level_steps.append(&child.uninstall_tls);
+ try child.top_level_steps.put(allocator, child.install_tls.step.name, &child.install_tls);
+ try child.top_level_steps.put(allocator, child.uninstall_tls.step.name, &child.uninstall_tls);
child.default_step = &child.install_tls.step;
return child;
}
@@ -372,27 +384,24 @@ fn applyArgs(b: *Build, args: anytype) !void {
},
}
}
- const Hasher = std.crypto.auth.siphash.SipHash128(1, 3);
+
+ // Create an installation directory local to this package. This will be used when
+ // dependant packages require a standard prefix, such as include directories for C headers.
+ var hash = b.cache.hash;
// Random bytes to make unique. Refresh this with new random bytes when
// implementation is modified in a non-backwards-compatible way.
- var hash = Hasher.init("ZaEsvQ5ClaA2IdH9");
- hash.update(b.dep_prefix);
+ hash.add(@as(u32, 0xd8cb0055));
+ hash.addBytes(b.dep_prefix);
// TODO additionally update the hash with `args`.
-
- var digest: [16]u8 = undefined;
- hash.final(&digest);
- var hash_basename: [digest.len * 2]u8 = undefined;
- _ = std.fmt.bufPrint(&hash_basename, "{s}", .{std.fmt.fmtSliceHexLower(&digest)}) catch
- unreachable;
-
- const install_prefix = try b.cache_root.join(b.allocator, &.{ "i", &hash_basename });
+ const digest = hash.final();
+ const install_prefix = try b.cache_root.join(b.allocator, &.{ "i", &digest });
b.resolveInstallPrefix(install_prefix, .{});
}
-pub fn destroy(self: *Build) void {
- self.env_map.deinit();
- self.top_level_steps.deinit();
- self.allocator.destroy(self);
+pub fn destroy(b: *Build) void {
+ b.env_map.deinit();
+ b.top_level_steps.deinit(b.allocator);
+ b.allocator.destroy(b);
}
/// This function is intended to be called by lib/build_runner.zig, not a build.zig file.
@@ -441,6 +450,7 @@ pub const ExecutableOptions = struct {
target: CrossTarget = .{},
optimize: std.builtin.Mode = .Debug,
linkage: ?CompileStep.Linkage = null,
+ max_rss: usize = 0,
};
pub fn addExecutable(b: *Build, options: ExecutableOptions) *CompileStep {
@@ -452,6 +462,7 @@ pub fn addExecutable(b: *Build, options: ExecutableOptions) *CompileStep {
.optimize = options.optimize,
.kind = .exe,
.linkage = options.linkage,
+ .max_rss = options.max_rss,
});
}
@@ -460,6 +471,7 @@ pub const ObjectOptions = struct {
root_source_file: ?FileSource = null,
target: CrossTarget,
optimize: std.builtin.Mode,
+ max_rss: usize = 0,
};
pub fn addObject(b: *Build, options: ObjectOptions) *CompileStep {
@@ -469,6 +481,7 @@ pub fn addObject(b: *Build, options: ObjectOptions) *CompileStep {
.target = options.target,
.optimize = options.optimize,
.kind = .obj,
+ .max_rss = options.max_rss,
});
}
@@ -478,6 +491,7 @@ pub const SharedLibraryOptions = struct {
version: ?std.builtin.Version = null,
target: CrossTarget,
optimize: std.builtin.Mode,
+ max_rss: usize = 0,
};
pub fn addSharedLibrary(b: *Build, options: SharedLibraryOptions) *CompileStep {
@@ -489,6 +503,7 @@ pub fn addSharedLibrary(b: *Build, options: SharedLibraryOptions) *CompileStep {
.version = options.version,
.target = options.target,
.optimize = options.optimize,
+ .max_rss = options.max_rss,
});
}
@@ -498,6 +513,7 @@ pub const StaticLibraryOptions = struct {
target: CrossTarget,
optimize: std.builtin.Mode,
version: ?std.builtin.Version = null,
+ max_rss: usize = 0,
};
pub fn addStaticLibrary(b: *Build, options: StaticLibraryOptions) *CompileStep {
@@ -509,25 +525,27 @@ pub fn addStaticLibrary(b: *Build, options: StaticLibraryOptions) *CompileStep {
.version = options.version,
.target = options.target,
.optimize = options.optimize,
+ .max_rss = options.max_rss,
});
}
pub const TestOptions = struct {
name: []const u8 = "test",
- kind: CompileStep.Kind = .@"test",
root_source_file: FileSource,
target: CrossTarget = .{},
optimize: std.builtin.Mode = .Debug,
version: ?std.builtin.Version = null,
+ max_rss: usize = 0,
};
pub fn addTest(b: *Build, options: TestOptions) *CompileStep {
return CompileStep.create(b, .{
.name = options.name,
- .kind = options.kind,
+ .kind = .@"test",
.root_source_file = options.root_source_file,
.target = options.target,
.optimize = options.optimize,
+ .max_rss = options.max_rss,
});
}
@@ -536,6 +554,7 @@ pub const AssemblyOptions = struct {
source_file: FileSource,
target: CrossTarget,
optimize: std.builtin.Mode,
+ max_rss: usize = 0,
};
pub fn addAssembly(b: *Build, options: AssemblyOptions) *CompileStep {
@@ -545,22 +564,19 @@ pub fn addAssembly(b: *Build, options: AssemblyOptions) *CompileStep {
.root_source_file = null,
.target = options.target,
.optimize = options.optimize,
+ .max_rss = options.max_rss,
});
obj_step.addAssemblyFileSource(options.source_file.dupe(b));
return obj_step;
}
-pub const AddModuleOptions = struct {
- name: []const u8,
- source_file: FileSource,
- dependencies: []const ModuleDependency = &.{},
-};
-
-pub fn addModule(b: *Build, options: AddModuleOptions) void {
- b.modules.put(b.dupe(options.name), b.createModule(.{
- .source_file = options.source_file,
- .dependencies = options.dependencies,
- })) catch @panic("OOM");
+/// This function creates a module and adds it to the package's module set, making
+/// it available to other packages which depend on this one.
+/// `createModule` can be used instead to create a private module.
+pub fn addModule(b: *Build, name: []const u8, options: CreateModuleOptions) *Module {
+ const module = b.createModule(options);
+ b.modules.put(b.dupe(name), module) catch @panic("OOM");
+ return module;
}
pub const ModuleDependency = struct {
@@ -573,8 +589,9 @@ pub const CreateModuleOptions = struct {
dependencies: []const ModuleDependency = &.{},
};
-/// Prefer to use `addModule` which will make the module available to other
-/// packages which depend on this package.
+/// This function creates a private module, to be used by the current package,
+/// but not exposed to other packages depending on this one.
+/// `addModule` can be used instead to create a public module.
pub fn createModule(b: *Build, options: CreateModuleOptions) *Module {
const module = b.allocator.create(Module) catch @panic("OOM");
module.* = .{
@@ -608,16 +625,15 @@ pub fn addSystemCommand(self: *Build, argv: []const []const u8) *RunStep {
/// Creates a `RunStep` with an executable built with `addExecutable`.
/// Add command line arguments with methods of `RunStep`.
pub fn addRunArtifact(b: *Build, exe: *CompileStep) *RunStep {
- assert(exe.kind == .exe or exe.kind == .test_exe);
-
// It doesn't have to be native. We catch that if you actually try to run it.
// Consider that this is declarative; the run step may not be run unless a user
// option is supplied.
- const run_step = RunStep.create(b, b.fmt("run {s}", .{exe.step.name}));
+ const run_step = RunStep.create(b, b.fmt("run {s}", .{exe.name}));
run_step.addArtifactArg(exe);
- if (exe.kind == .test_exe) {
- run_step.addArg(b.zig_exe);
+ if (exe.kind == .@"test") {
+ run_step.stdio = .zig_test;
+ run_step.addArgs(&.{"--listen=-"});
}
if (exe.vcpkg_bin_path) |path| {
@@ -637,7 +653,11 @@ pub fn addConfigHeader(
options: ConfigHeaderStep.Options,
values: anytype,
) *ConfigHeaderStep {
- const config_header_step = ConfigHeaderStep.create(b, options);
+ var options_copy = options;
+ if (options_copy.first_ret_addr == null)
+ options_copy.first_ret_addr = @returnAddress();
+
+ const config_header_step = ConfigHeaderStep.create(b, options_copy);
config_header_step.addValues(values);
return config_header_step;
}
@@ -674,17 +694,8 @@ pub fn addWriteFile(self: *Build, file_path: []const u8, data: []const u8) *Writ
return write_file_step;
}
-pub fn addWriteFiles(self: *Build) *WriteFileStep {
- const write_file_step = self.allocator.create(WriteFileStep) catch @panic("OOM");
- write_file_step.* = WriteFileStep.init(self);
- return write_file_step;
-}
-
-pub fn addLog(self: *Build, comptime format: []const u8, args: anytype) *LogStep {
- const data = self.fmt(format, args);
- const log_step = self.allocator.create(LogStep) catch @panic("OOM");
- log_step.* = LogStep.init(self, data);
- return log_step;
+pub fn addWriteFiles(b: *Build) *WriteFileStep {
+ return WriteFileStep.create(b);
}
pub fn addRemoveDirTree(self: *Build, dir_path: []const u8) *RemoveDirStep {
@@ -693,32 +704,14 @@ pub fn addRemoveDirTree(self: *Build, dir_path: []const u8) *RemoveDirStep {
return remove_dir_step;
}
-pub fn addFmt(self: *Build, paths: []const []const u8) *FmtStep {
- return FmtStep.create(self, paths);
+pub fn addFmt(b: *Build, options: FmtStep.Options) *FmtStep {
+ return FmtStep.create(b, options);
}
pub fn addTranslateC(self: *Build, options: TranslateCStep.Options) *TranslateCStep {
return TranslateCStep.create(self, options);
}
-pub fn make(self: *Build, step_names: []const []const u8) !void {
- var wanted_steps = ArrayList(*Step).init(self.allocator);
- defer wanted_steps.deinit();
-
- if (step_names.len == 0) {
- try wanted_steps.append(self.default_step);
- } else {
- for (step_names) |step_name| {
- const s = try self.getTopLevelStepByName(step_name);
- try wanted_steps.append(s);
- }
- }
-
- for (wanted_steps.items) |s| {
- try self.makeOneStep(s);
- }
-}
-
pub fn getInstallStep(self: *Build) *Step {
return &self.install_tls.step;
}
@@ -727,7 +720,8 @@ pub fn getUninstallStep(self: *Build) *Step {
return &self.uninstall_tls.step;
}
-fn makeUninstall(uninstall_step: *Step) anyerror!void {
+fn makeUninstall(uninstall_step: *Step, prog_node: *std.Progress.Node) anyerror!void {
+ _ = prog_node;
const uninstall_tls = @fieldParentPtr(TopLevelStep, "step", uninstall_step);
const self = @fieldParentPtr(Build, "uninstall_tls", uninstall_tls);
@@ -742,37 +736,6 @@ fn makeUninstall(uninstall_step: *Step) anyerror!void {
// TODO remove empty directories
}
-fn makeOneStep(self: *Build, s: *Step) anyerror!void {
- if (s.loop_flag) {
- log.err("Dependency loop detected:\n {s}", .{s.name});
- return error.DependencyLoopDetected;
- }
- s.loop_flag = true;
-
- for (s.dependencies.items) |dep| {
- self.makeOneStep(dep) catch |err| {
- if (err == error.DependencyLoopDetected) {
- log.err(" {s}", .{s.name});
- }
- return err;
- };
- }
-
- s.loop_flag = false;
-
- try s.make();
-}
-
-fn getTopLevelStepByName(self: *Build, name: []const u8) !*Step {
- for (self.top_level_steps.items) |top_level_step| {
- if (mem.eql(u8, top_level_step.step.name, name)) {
- return &top_level_step.step;
- }
- }
- log.err("Cannot run step '{s}' because it does not exist", .{name});
- return error.InvalidStepName;
-}
-
pub fn option(self: *Build, comptime T: type, name_raw: []const u8, description_raw: []const u8) ?T {
const name = self.dupe(name_raw);
const description = self.dupe(description_raw);
@@ -909,11 +872,15 @@ pub fn option(self: *Build, comptime T: type, name_raw: []const u8, description_
pub fn step(self: *Build, name: []const u8, description: []const u8) *Step {
const step_info = self.allocator.create(TopLevelStep) catch @panic("OOM");
- step_info.* = TopLevelStep{
- .step = Step.initNoOp(.top_level, name, self.allocator),
+ step_info.* = .{
+ .step = Step.init(.{
+ .id = .top_level,
+ .name = name,
+ .owner = self,
+ }),
.description = self.dupe(description),
};
- self.top_level_steps.append(step_info) catch @panic("OOM");
+ self.top_level_steps.put(self.allocator, step_info.step.name, step_info) catch @panic("OOM");
return &step_info.step;
}
@@ -1181,50 +1148,18 @@ pub fn validateUserInputDidItFail(self: *Build) bool {
return self.invalid_user_input;
}
-pub fn spawnChild(self: *Build, argv: []const []const u8) !void {
- return self.spawnChildEnvMap(null, self.env_map, argv);
-}
-
-fn printCmd(cwd: ?[]const u8, argv: []const []const u8) void {
- if (cwd) |yes_cwd| std.debug.print("cd {s} && ", .{yes_cwd});
+fn allocPrintCmd(ally: Allocator, opt_cwd: ?[]const u8, argv: []const []const u8) ![]u8 {
+ var buf = ArrayList(u8).init(ally);
+ if (opt_cwd) |cwd| try buf.writer().print("cd {s} && ", .{cwd});
for (argv) |arg| {
- std.debug.print("{s} ", .{arg});
+ try buf.writer().print("{s} ", .{arg});
}
- std.debug.print("\n", .{});
+ return buf.toOwnedSlice();
}
-pub fn spawnChildEnvMap(self: *Build, cwd: ?[]const u8, env_map: *const EnvMap, argv: []const []const u8) !void {
- if (self.verbose) {
- printCmd(cwd, argv);
- }
-
- if (!std.process.can_spawn)
- return error.ExecNotSupported;
-
- var child = std.ChildProcess.init(argv, self.allocator);
- child.cwd = cwd;
- child.env_map = env_map;
-
- const term = child.spawnAndWait() catch |err| {
- log.err("Unable to spawn {s}: {s}", .{ argv[0], @errorName(err) });
- return err;
- };
-
- switch (term) {
- .Exited => |code| {
- if (code != 0) {
- log.err("The following command exited with error code {}:", .{code});
- printCmd(cwd, argv);
- return error.UncleanExit;
- }
- },
- else => {
- log.err("The following command terminated unexpectedly:", .{});
- printCmd(cwd, argv);
-
- return error.UncleanExit;
- },
- }
+fn printCmd(ally: Allocator, cwd: ?[]const u8, argv: []const []const u8) void {
+ const text = allocPrintCmd(ally, cwd, argv) catch @panic("OOM");
+ std.debug.print("{s}\n", .{text});
}
pub fn installArtifact(self: *Build, artifact: *CompileStep) void {
@@ -1283,12 +1218,7 @@ pub fn addInstallFileWithDir(
install_dir: InstallDir,
dest_rel_path: []const u8,
) *InstallFileStep {
- if (dest_rel_path.len == 0) {
- panic("dest_rel_path must be non-empty", .{});
- }
- const install_step = self.allocator.create(InstallFileStep) catch @panic("OOM");
- install_step.* = InstallFileStep.init(self, source.dupe(self), install_dir, dest_rel_path);
- return install_step;
+ return InstallFileStep.create(self, source.dupe(self), install_dir, dest_rel_path);
}
pub fn addInstallDirectory(self: *Build, options: InstallDirectoryOptions) *InstallDirStep {
@@ -1297,6 +1227,14 @@ pub fn addInstallDirectory(self: *Build, options: InstallDirectoryOptions) *Inst
return install_step;
}
+pub fn addCheckFile(
+ b: *Build,
+ file_source: FileSource,
+ options: CheckFileStep.Options,
+) *CheckFileStep {
+ return CheckFileStep.create(b, file_source, options);
+}
+
pub fn pushInstalledFile(self: *Build, dir: InstallDir, dest_rel_path: []const u8) void {
const file = InstalledFile{
.dir = dir,
@@ -1305,18 +1243,6 @@ pub fn pushInstalledFile(self: *Build, dir: InstallDir, dest_rel_path: []const u
self.installed_files.append(file.dupe(self)) catch @panic("OOM");
}
-pub fn updateFile(self: *Build, source_path: []const u8, dest_path: []const u8) !void {
- if (self.verbose) {
- log.info("cp {s} {s} ", .{ source_path, dest_path });
- }
- const cwd = fs.cwd();
- const prev_status = try fs.Dir.updateFile(cwd, source_path, cwd, dest_path, .{});
- if (self.verbose) switch (prev_status) {
- .stale => log.info("# installed", .{}),
- .fresh => log.info("# up-to-date", .{}),
- };
-}
-
pub fn truncateFile(self: *Build, dest_path: []const u8) !void {
if (self.verbose) {
log.info("truncate {s}", .{dest_path});
@@ -1400,7 +1326,7 @@ pub fn execAllowFail(
) ExecError![]u8 {
assert(argv.len != 0);
- if (!std.process.can_spawn)
+ if (!process.can_spawn)
return error.ExecNotSupported;
const max_output_size = 400 * 1024;
@@ -1433,59 +1359,27 @@ pub fn execAllowFail(
}
}
-pub fn execFromStep(self: *Build, argv: []const []const u8, src_step: ?*Step) ![]u8 {
- assert(argv.len != 0);
-
- if (self.verbose) {
- printCmd(null, argv);
- }
-
- if (!std.process.can_spawn) {
- if (src_step) |s| log.err("{s}...", .{s.name});
- log.err("Unable to spawn the following command: cannot spawn child process", .{});
- printCmd(null, argv);
- std.os.abort();
+/// This is a helper function to be called from build.zig scripts, *not* from
+/// inside step make() functions. If any errors occur, it fails the build with
+/// a helpful message.
+pub fn exec(b: *Build, argv: []const []const u8) []u8 {
+ if (!process.can_spawn) {
+ std.debug.print("unable to spawn the following command: cannot spawn child process\n{s}\n", .{
+ try allocPrintCmd(b.allocator, null, argv),
+ });
+ process.exit(1);
}
var code: u8 = undefined;
- return self.execAllowFail(argv, &code, .Inherit) catch |err| switch (err) {
- error.ExecNotSupported => {
- if (src_step) |s| log.err("{s}...", .{s.name});
- log.err("Unable to spawn the following command: cannot spawn child process", .{});
- printCmd(null, argv);
- std.os.abort();
- },
- error.FileNotFound => {
- if (src_step) |s| log.err("{s}...", .{s.name});
- log.err("Unable to spawn the following command: file not found", .{});
- printCmd(null, argv);
- std.os.exit(@truncate(u8, code));
- },
- error.ExitCodeFailure => {
- if (src_step) |s| log.err("{s}...", .{s.name});
- if (self.prominent_compile_errors) {
- log.err("The step exited with error code {d}", .{code});
- } else {
- log.err("The following command exited with error code {d}:", .{code});
- printCmd(null, argv);
- }
-
- std.os.exit(@truncate(u8, code));
- },
- error.ProcessTerminated => {
- if (src_step) |s| log.err("{s}...", .{s.name});
- log.err("The following command terminated unexpectedly:", .{});
- printCmd(null, argv);
- std.os.exit(@truncate(u8, code));
- },
- else => |e| return e,
+ return b.execAllowFail(argv, &code, .Inherit) catch |err| {
+ const printed_cmd = allocPrintCmd(b.allocator, null, argv) catch @panic("OOM");
+ std.debug.print("unable to spawn the following command: {s}\n{s}\n", .{
+ @errorName(err), printed_cmd,
+ });
+ process.exit(1);
};
}
-pub fn exec(self: *Build, argv: []const []const u8) ![]u8 {
- return self.execFromStep(argv, null);
-}
-
pub fn addSearchPrefix(self: *Build, search_prefix: []const u8) void {
self.search_prefixes.append(self.dupePath(search_prefix)) catch @panic("OOM");
}
@@ -1550,10 +1444,29 @@ pub fn dependency(b: *Build, name: []const u8, args: anytype) *Dependency {
const full_path = b.pathFromRoot("build.zig.zon");
std.debug.print("no dependency named '{s}' in '{s}'. All packages used in build.zig must be declared in this file.\n", .{ name, full_path });
- std.process.exit(1);
+ process.exit(1);
+}
+
+pub fn anonymousDependency(
+ b: *Build,
+ /// The path to the directory containing the dependency's build.zig file,
+ /// relative to the current package's build.zig.
+ relative_build_root: []const u8,
+ /// A direct `@import` of the build.zig of the dependency.
+ comptime build_zig: type,
+ args: anytype,
+) *Dependency {
+ const arena = b.allocator;
+ const build_root = b.build_root.join(arena, &.{relative_build_root}) catch @panic("OOM");
+ const name = arena.dupe(u8, relative_build_root) catch @panic("OOM");
+ for (name) |*byte| switch (byte.*) {
+ '/', '\\' => byte.* = '.',
+ else => continue,
+ };
+ return dependencyInner(b, name, build_root, build_zig, args);
}
-fn dependencyInner(
+pub fn dependencyInner(
b: *Build,
name: []const u8,
build_root_string: []const u8,
@@ -1566,7 +1479,7 @@ fn dependencyInner(
std.debug.print("unable to open '{s}': {s}\n", .{
build_root_string, @errorName(err),
});
- std.process.exit(1);
+ process.exit(1);
},
};
const sub_builder = b.createChild(name, build_root, args) catch @panic("unhandled error");
@@ -1610,7 +1523,7 @@ pub const GeneratedFile = struct {
pub fn getPath(self: GeneratedFile) []const u8 {
return self.path orelse std.debug.panic(
- "getPath() was called on a GeneratedFile that wasn't build yet. Is there a missing Step dependency on step '{s}'?",
+ "getPath() was called on a GeneratedFile that wasn't built yet. Is there a missing Step dependency on step '{s}'?",
.{self.step.name},
);
}
@@ -1650,12 +1563,23 @@ pub const FileSource = union(enum) {
}
/// Should only be called during make(), returns a path relative to the build root or absolute.
- pub fn getPath(self: FileSource, builder: *Build) []const u8 {
- const path = switch (self) {
- .path => |p| builder.pathFromRoot(p),
- .generated => |gen| gen.getPath(),
- };
- return path;
+ pub fn getPath(self: FileSource, src_builder: *Build) []const u8 {
+ return getPath2(self, src_builder, null);
+ }
+
+ /// Should only be called during make(), returns a path relative to the build root or absolute.
+ /// asking_step is only used for debugging purposes; it's the step being run that is asking for
+ /// the path.
+ pub fn getPath2(self: FileSource, src_builder: *Build, asking_step: ?*Step) []const u8 {
+ switch (self) {
+ .path => |p| return src_builder.pathFromRoot(p),
+ .generated => |gen| return gen.path orelse {
+ std.debug.getStderrMutex().lock();
+ const stderr = std.io.getStdErr();
+ dumpBadGetPathHelp(gen.step, stderr, src_builder, asking_step) catch {};
+ @panic("misconfigured build script");
+ },
+ }
}
/// Duplicates the file source for a given builder.
@@ -1667,6 +1591,54 @@ pub const FileSource = union(enum) {
}
};
+/// In this function the stderr mutex has already been locked.
+fn dumpBadGetPathHelp(
+ s: *Step,
+ stderr: fs.File,
+ src_builder: *Build,
+ asking_step: ?*Step,
+) anyerror!void {
+ const w = stderr.writer();
+ try w.print(
+ \\getPath() was called on a GeneratedFile that wasn't built yet.
+ \\ source package path: {s}
+ \\ Is there a missing Step dependency on step '{s}'?
+ \\
+ , .{
+ src_builder.build_root.path orelse ".",
+ s.name,
+ });
+
+ const tty_config = std.debug.detectTTYConfig(stderr);
+ tty_config.setColor(w, .Red) catch {};
+ try stderr.writeAll(" The step was created by this stack trace:\n");
+ tty_config.setColor(w, .Reset) catch {};
+
+ const debug_info = std.debug.getSelfDebugInfo() catch |err| {
+ try w.print("Unable to dump stack trace: Unable to open debug info: {s}\n", .{@errorName(err)});
+ return;
+ };
+ const ally = debug_info.allocator;
+ std.debug.writeStackTrace(s.getStackTrace(), w, ally, debug_info, tty_config) catch |err| {
+ try stderr.writer().print("Unable to dump stack trace: {s}\n", .{@errorName(err)});
+ return;
+ };
+ if (asking_step) |as| {
+ tty_config.setColor(w, .Red) catch {};
+ try stderr.writeAll(" The step that is missing a dependency on the above step was created by this stack trace:\n");
+ tty_config.setColor(w, .Reset) catch {};
+
+ std.debug.writeStackTrace(as.getStackTrace(), w, ally, debug_info, tty_config) catch |err| {
+ try stderr.writer().print("Unable to dump stack trace: {s}\n", .{@errorName(err)});
+ return;
+ };
+ }
+
+ tty_config.setColor(w, .Red) catch {};
+ try stderr.writeAll(" Hope that helps. Proceeding to panic.\n");
+ tty_config.setColor(w, .Reset) catch {};
+}
+
/// Allocates a new string for assigning a value to a named macro.
/// If the value is omitted, it is set to 1.
/// `name` and `value` need not live longer than the function call.
@@ -1706,9 +1678,7 @@ pub const InstallDir = union(enum) {
/// Duplicates the install directory including the path if set to custom.
pub fn dupe(self: InstallDir, builder: *Build) InstallDir {
if (self == .custom) {
- // Written with this temporary to avoid RLS problems
- const duped_path = builder.dupe(self.custom);
- return .{ .custom = duped_path };
+ return .{ .custom = builder.dupe(self.custom) };
} else {
return self;
}
@@ -1756,17 +1726,45 @@ pub fn serializeCpu(allocator: Allocator, cpu: std.Target.Cpu) ![]const u8 {
}
}
+/// This function is intended to be called in the `configure` phase only.
+/// It returns an absolute directory path, which is potentially going to be a
+/// source of API breakage in the future, so keep that in mind when using this
+/// function.
+pub fn makeTempPath(b: *Build) []const u8 {
+ const rand_int = std.crypto.random.int(u64);
+ const tmp_dir_sub_path = "tmp" ++ fs.path.sep_str ++ hex64(rand_int);
+ const result_path = b.cache_root.join(b.allocator, &.{tmp_dir_sub_path}) catch @panic("OOM");
+ fs.cwd().makePath(result_path) catch |err| {
+ std.debug.print("unable to make tmp path '{s}': {s}\n", .{
+ result_path, @errorName(err),
+ });
+ };
+ return result_path;
+}
+
+/// There are a few copies of this function in miscellaneous places. Would be nice to find
+/// a home for them.
+fn hex64(x: u64) [16]u8 {
+ const hex_charset = "0123456789abcdef";
+ var result: [16]u8 = undefined;
+ var i: usize = 0;
+ while (i < 8) : (i += 1) {
+ const byte = @truncate(u8, x >> @intCast(u6, 8 * i));
+ result[i * 2 + 0] = hex_charset[byte >> 4];
+ result[i * 2 + 1] = hex_charset[byte & 15];
+ }
+ return result;
+}
+
test {
_ = CheckFileStep;
_ = CheckObjectStep;
- _ = EmulatableRunStep;
_ = FmtStep;
_ = InstallArtifactStep;
_ = InstallDirStep;
_ = InstallFileStep;
_ = ObjCopyStep;
_ = CompileStep;
- _ = LogStep;
_ = OptionsStep;
_ = RemoveDirStep;
_ = RunStep;