From d7032705456a67cebeb17ad09a11e10d13f5fb21 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 24 Jan 2023 17:24:16 -0700 Subject: [PATCH] zig build system: add LibExeObjStep.installLibraryHeaders This function is needed when a library exposes one of its own library dependency's headers as part of its own public API. Also, improve error message when a file system error occurs during install file step. --- lib/build_runner.zig | 5 ++++ lib/std/build.zig | 40 ++++++++++++++++++++++++++++--- lib/std/build/InstallDirStep.zig | 23 +++++++++++------- lib/std/build/InstallFileStep.zig | 6 ++++- lib/std/build/LibExeObjStep.zig | 23 ++++++++++++++++++ 5 files changed, 84 insertions(+), 13 deletions(-) diff --git a/lib/build_runner.zig b/lib/build_runner.zig index f27542d0f5..4df2eb1d62 100644 --- a/lib/build_runner.zig +++ b/lib/build_runner.zig @@ -220,6 +220,11 @@ pub fn main() !void { return usageAndErr(builder, true, stderr_stream); }, error.UncleanExit => process.exit(1), + // This error is intended to indicate that the step has already + // logged an error message and so printing the error return trace + // here would be unwanted extra information, unless the user opts + // into it with a debug flag. + error.StepFailed => process.exit(1), else => return err, } }; diff --git a/lib/std/build.zig b/lib/std/build.zig index 7980bde418..8137b76846 100644 --- a/lib/std/build.zig +++ b/lib/std/build.zig @@ -316,9 +316,20 @@ pub const Builder = struct { // options to its dependencies. It is the programmatic way to give // command line arguments to a build.zig script. _ = args; - // TODO create a hash based on the args and the package hash, use this - // to compute the install prefix. - const install_prefix = b.pathJoin(&.{ b.cache_root, "pkg" }); + const Hasher = std.crypto.auth.siphash.SipHash128(1, 3); + // 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); + // 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 = b.pathJoin(&.{ b.cache_root, "i", &hash_basename }); b.resolveInstallPrefix(install_prefix, .{}); } @@ -1600,6 +1611,29 @@ pub const Step = struct { install_raw, options, custom, + + pub fn Type(comptime id: Id) type { + return switch (id) { + .top_level => Builder.TopLevelStep, + .lib_exe_obj => LibExeObjStep, + .install_artifact => InstallArtifactStep, + .install_file => InstallFileStep, + .install_dir => InstallDirStep, + .log => LogStep, + .remove_dir => RemoveDirStep, + .fmt => FmtStep, + .translate_c => TranslateCStep, + .write_file => WriteFileStep, + .run => RunStep, + .emulatable_run => EmulatableRunStep, + .check_file => CheckFileStep, + .check_object => CheckObjectStep, + .config_header => ConfigHeaderStep, + .install_raw => InstallRawStep, + .options => OptionsStep, + .custom => @compileError("no type available for custom step"), + }; + } }; pub fn init(id: Id, name: []const u8, allocator: Allocator, makeFn: MakeFn) Step { diff --git a/lib/std/build/InstallDirStep.zig b/lib/std/build/InstallDirStep.zig index 86f6995a88..0a41e1aaef 100644 --- a/lib/std/build/InstallDirStep.zig +++ b/lib/std/build/InstallDirStep.zig @@ -6,10 +6,14 @@ const Step = build.Step; const Builder = build.Builder; const InstallDir = std.build.InstallDir; const InstallDirStep = @This(); +const log = std.log; step: Step, builder: *Builder, options: Options, +/// This is used by the build system when a file being installed comes from one +/// package but is being installed by another. +override_source_builder: ?*Builder = null, pub const base_id = .install_dir; @@ -53,8 +57,14 @@ pub fn init( fn make(step: *Step) !void { const self = @fieldParentPtr(InstallDirStep, "step", step); const dest_prefix = self.builder.getInstallPath(self.options.install_dir, self.options.install_subdir); - const full_src_dir = self.builder.pathFromRoot(self.options.source_dir); - var src_dir = try std.fs.cwd().openIterableDir(full_src_dir, .{}); + const src_builder = self.override_source_builder orelse self.builder; + const full_src_dir = src_builder.pathFromRoot(self.options.source_dir); + var src_dir = std.fs.cwd().openIterableDir(full_src_dir, .{}) catch |err| { + log.err("InstallDirStep: unable to open source directory '{s}': {s}", .{ + full_src_dir, @errorName(err), + }); + return error.StepFailed; + }; defer src_dir.close(); var it = try src_dir.walk(self.builder.allocator); next_entry: while (try it.next()) |entry| { @@ -64,13 +74,8 @@ fn make(step: *Step) !void { } } - const full_path = self.builder.pathJoin(&.{ - full_src_dir, entry.path, - }); - - const dest_path = self.builder.pathJoin(&.{ - dest_prefix, entry.path, - }); + const full_path = self.builder.pathJoin(&.{ full_src_dir, entry.path }); + const dest_path = self.builder.pathJoin(&.{ dest_prefix, entry.path }); switch (entry.kind) { .Directory => try fs.cwd().makePath(dest_path), diff --git a/lib/std/build/InstallFileStep.zig b/lib/std/build/InstallFileStep.zig index 23ef342bca..37203e64c5 100644 --- a/lib/std/build/InstallFileStep.zig +++ b/lib/std/build/InstallFileStep.zig @@ -13,6 +13,9 @@ builder: *Builder, source: FileSource, dir: InstallDir, dest_rel_path: []const u8, +/// This is used by the build system when a file being installed comes from one +/// package but is being installed by another. +override_source_builder: ?*Builder = null, pub fn init( builder: *Builder, @@ -32,7 +35,8 @@ pub fn init( fn make(step: *Step) !void { const self = @fieldParentPtr(InstallFileStep, "step", step); + const src_builder = self.override_source_builder orelse self.builder; + const full_src_path = self.source.getPath(src_builder); const full_dest_path = self.builder.getInstallPath(self.dir, self.dest_rel_path); - const full_src_path = self.source.getPath(self.builder); try self.builder.updateFile(full_src_path, full_dest_path); } diff --git a/lib/std/build/LibExeObjStep.zig b/lib/std/build/LibExeObjStep.zig index d79bd937c0..213c5c6559 100644 --- a/lib/std/build/LibExeObjStep.zig +++ b/lib/std/build/LibExeObjStep.zig @@ -501,6 +501,29 @@ pub fn installHeadersDirectoryOptions( a.installed_headers.append(&install_dir.step) catch unreachable; } +pub fn installLibraryHeaders(a: *LibExeObjStep, l: *LibExeObjStep) void { + assert(l.kind == .lib); + const install_step = a.builder.getInstallStep(); + // Copy each element from installed_headers, modifying the builder + // to be the new parent's builder. + for (l.installed_headers.items) |step| { + const step_copy = switch (step.id) { + inline .install_file, .install_dir => |id| blk: { + const T = id.Type(); + const ptr = a.builder.allocator.create(T) catch unreachable; + ptr.* = step.cast(T).?.*; + ptr.override_source_builder = ptr.builder; + ptr.builder = a.builder; + break :blk &ptr.step; + }, + else => unreachable, + }; + a.installed_headers.append(step_copy) catch unreachable; + install_step.dependOn(step_copy); + } + a.installed_headers.appendSlice(l.installed_headers.items) catch unreachable; +} + /// Creates a `RunStep` with an executable built with `addExecutable`. /// Add command line arguments with `addArg`. pub fn run(exe: *LibExeObjStep) *RunStep {