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.
This commit is contained in:
Andrew Kelley 2023-01-24 17:24:16 -07:00
parent 3ff1f346b0
commit d703270545
5 changed files with 84 additions and 13 deletions

View File

@ -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,
}
};

View File

@ -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 {

View File

@ -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),

View File

@ -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);
}

View File

@ -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 {