From 0b7123f41d66bdda4da29d59623299d47b29aefb Mon Sep 17 00:00:00 2001 From: Carl Åstholm Date: Sat, 2 Mar 2024 22:59:00 +0100 Subject: std.Build: correct behavior of `Step.Compile.installHeader` Previously, `Step.Compile.installHeader` and friends would incorrectly modify the default `install` top-level step, when the intent was for headers to get bundled with and installed alongside an artifact. This change set implements the intended behavior. This carries with it some breaking changes; `installHeader` and `installConfigHeader` both have new signatures, and `installHeadersDirectory` and `installHeadersDirectoryOptions` have been merged into `installHeaders`. --- lib/std/Build/Step/Compile.zig | 155 +++++++++++++++++++++++++---------------- 1 file changed, 94 insertions(+), 61 deletions(-) (limited to 'lib/std/Build/Step/Compile.zig') diff --git a/lib/std/Build/Step/Compile.zig b/lib/std/Build/Step/Compile.zig index 0c37769e9c..caa3a9e34a 100644 --- a/lib/std/Build/Step/Compile.zig +++ b/lib/std/Build/Step/Compile.zig @@ -59,7 +59,7 @@ test_runner: ?[]const u8, test_server_mode: bool, wasi_exec_model: ?std.builtin.WasiExecModel = null, -installed_headers: ArrayList(*Step), +installed_headers: ArrayList(InstalledHeader), // keep in sync with src/Compilation.zig:RcIncludes /// Behavior of automatic detection of include directories when compiling .rc files. @@ -249,6 +249,70 @@ pub const Kind = enum { @"test", }; +pub const InstalledHeader = struct { + source: Source, + dest_rel_path: []const u8, + + pub const Source = union(enum) { + file: LazyPath, + directory: Directory, + + pub const Directory = struct { + path: LazyPath, + options: Directory.Options, + + pub const Options = struct { + /// File paths which end in any of these suffixes will be excluded + /// from installation. + exclude_extensions: []const []const u8 = &.{}, + /// Only file paths which end in any of these suffixes will be included + /// in installation. + /// `null` means all suffixes will be included. + /// `exclude_extensions` takes precedence over `include_extensions` + include_extensions: ?[]const []const u8 = &.{".h"}, + + pub fn dupe(self: Directory.Options, b: *std.Build) Directory.Options { + return .{ + .exclude_extensions = b.dupeStrings(self.exclude_extensions), + .include_extensions = if (self.include_extensions) |incs| + b.dupeStrings(incs) + else + null, + }; + } + }; + + pub fn dupe(self: Directory, b: *std.Build) Directory { + return .{ + .path = self.path.dupe(b), + .options = self.options.dupe(b), + }; + } + }; + + pub fn path(self: Source) LazyPath { + return switch (self) { + .file => |lp| lp, + .directory => |dir| dir.path, + }; + } + + pub fn dupe(self: Source, b: *std.Build) Source { + return switch (self) { + .file => |lp| .{ .file = lp.dupe(b) }, + .directory => |dir| .{ .directory = dir.dupe(b) }, + }; + } + }; + + pub fn dupe(self: InstalledHeader, b: *std.Build) InstalledHeader { + return .{ + .source = self.source.dupe(b), + .dest_rel_path = b.dupePath(self.dest_rel_path), + }; + } +}; + pub fn create(owner: *std.Build, options: Options) *Compile { const name = owner.dupe(options.name); if (mem.indexOf(u8, name, "/") != null or mem.indexOf(u8, name, "\\") != null) { @@ -308,7 +372,7 @@ pub fn create(owner: *std.Build, options: Options) *Compile { .out_lib_filename = undefined, .major_only_filename = null, .name_only_filename = null, - .installed_headers = ArrayList(*Step).init(owner.allocator), + .installed_headers = ArrayList(InstalledHeader).init(owner.allocator), .zig_lib_dir = null, .exec_cmd_args = null, .filters = options.filters, @@ -380,78 +444,47 @@ pub fn create(owner: *std.Build, options: Options) *Compile { return self; } -pub fn installHeader(cs: *Compile, src_path: []const u8, dest_rel_path: []const u8) void { - const b = cs.step.owner; - const install_file = b.addInstallHeaderFile(src_path, dest_rel_path); - b.getInstallStep().dependOn(&install_file.step); - cs.installed_headers.append(&install_file.step) catch @panic("OOM"); -} - -pub const InstallConfigHeaderOptions = struct { - install_dir: InstallDir = .header, - dest_rel_path: ?[]const u8 = null, -}; - -pub fn installConfigHeader( +pub fn installHeader( cs: *Compile, - config_header: *Step.ConfigHeader, - options: InstallConfigHeaderOptions, + source: LazyPath, + dest_rel_path: []const u8, ) void { - const dest_rel_path = options.dest_rel_path orelse config_header.include_path; const b = cs.step.owner; - const install_file = b.addInstallFileWithDir( - .{ .generated = &config_header.output_file }, - options.install_dir, - dest_rel_path, - ); - install_file.step.dependOn(&config_header.step); - b.getInstallStep().dependOn(&install_file.step); - cs.installed_headers.append(&install_file.step) catch @panic("OOM"); + cs.installed_headers.append(.{ + .source = .{ .file = source.dupe(b) }, + .dest_rel_path = b.dupePath(dest_rel_path), + }) catch @panic("OOM"); + source.addStepDependencies(&cs.step); } -pub fn installHeadersDirectory( - a: *Compile, - src_dir_path: []const u8, +pub fn installHeaders( + cs: *Compile, + source: LazyPath, dest_rel_path: []const u8, + options: InstalledHeader.Source.Directory.Options, ) void { - return installHeadersDirectoryOptions(a, .{ - .source_dir = .{ .path = src_dir_path }, - .install_dir = .header, - .install_subdir = dest_rel_path, - }); + const b = cs.step.owner; + cs.installed_headers.append(.{ + .source = .{ .directory = .{ + .path = source.dupe(b), + .options = options.dupe(b), + } }, + .dest_rel_path = b.dupePath(dest_rel_path), + }) catch @panic("OOM"); + source.addStepDependencies(&cs.step); } -pub fn installHeadersDirectoryOptions( - cs: *Compile, - options: std.Build.Step.InstallDir.Options, -) void { - const b = cs.step.owner; - const install_dir = b.addInstallDirectory(options); - b.getInstallStep().dependOn(&install_dir.step); - cs.installed_headers.append(&install_dir.step) catch @panic("OOM"); +pub fn installConfigHeader(cs: *Compile, config_header: *Step.ConfigHeader) void { + cs.installHeader(.{ .generated = &config_header.output_file }, config_header.include_path); } -pub fn installLibraryHeaders(cs: *Compile, l: *Compile) void { - assert(l.kind == .lib); +pub fn installLibraryHeaders(cs: *Compile, lib: *Compile) void { + assert(lib.kind == .lib); const b = cs.step.owner; - const install_step = b.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 = b.allocator.create(T) catch @panic("OOM"); - ptr.* = step.cast(T).?.*; - ptr.dest_builder = b; - break :blk &ptr.step; - }, - else => unreachable, - }; - cs.installed_headers.append(step_copy) catch @panic("OOM"); - install_step.dependOn(step_copy); + for (lib.installed_headers.items) |header| { + cs.installed_headers.append(header.dupe(b)) catch @panic("OOM"); + header.source.path().addStepDependencies(&cs.step); } - cs.installed_headers.appendSlice(l.installed_headers.items) catch @panic("OOM"); } pub fn addObjCopy(cs: *Compile, options: Step.ObjCopy.Options) *Step.ObjCopy { -- cgit v1.2.3 From 2c7be4f8dd55653be6cbf5d06f8f9c47e8d9276e Mon Sep 17 00:00:00 2001 From: Carl Åstholm Date: Sun, 3 Mar 2024 16:22:11 +0100 Subject: Create an include tree of installed headers for dependent modules --- lib/std/Build/Module.zig | 17 +--- lib/std/Build/Step/Compile.zig | 168 +++++++++++++++++++-------------- lib/std/Build/Step/InstallArtifact.zig | 12 +-- 3 files changed, 107 insertions(+), 90 deletions(-) (limited to 'lib/std/Build/Step/Compile.zig') diff --git a/lib/std/Build/Module.zig b/lib/std/Build/Module.zig index b8b6560019..8937f21e88 100644 --- a/lib/std/Build/Module.zig +++ b/lib/std/Build/Module.zig @@ -265,8 +265,7 @@ fn addShallowDependencies(m: *Module, dependee: *Module) void { for (dependee.link_objects.items) |link_object| switch (link_object) { .other_step => |compile| { addStepDependencies(m, dependee, &compile.step); - for (compile.installed_headers.items) |header| - addLazyPathDependenciesOnly(m, header.source.path()); + addLazyPathDependenciesOnly(m, compile.getEmittedIncludeTree()); }, .static_path, @@ -693,14 +692,9 @@ pub fn appendZigProcessFlags( if (other.generated_h) |header| { try zig_args.appendSlice(&.{ "-isystem", std.fs.path.dirname(header.getPath()).? }); } - for (other.installed_headers.items) |header| switch (header.source) { - .file => |lp| { - try zig_args.appendSlice(&.{ "-I", std.fs.path.dirname(lp.getPath2(b, asking_step)).? }); - }, - .directory => |dir| { - try zig_args.appendSlice(&.{ "-I", dir.path.getPath2(b, asking_step) }); - }, - }; + if (other.installed_headers_include_tree) |include_tree| { + try zig_args.appendSlice(&.{ "-I", include_tree.generated_directory.getPath() }); + } }, .config_header_step => |config_header| { try zig_args.appendSlice(&.{ "-I", std.fs.path.dirname(config_header.output_file.getPath()).? }); @@ -751,8 +745,7 @@ fn linkLibraryOrObject(m: *Module, other: *Step.Compile) void { m.link_objects.append(allocator, .{ .other_step = other }) catch @panic("OOM"); m.include_dirs.append(allocator, .{ .other_step = other }) catch @panic("OOM"); - for (other.installed_headers.items) |header| - addLazyPathDependenciesOnly(m, header.source.path()); + addLazyPathDependenciesOnly(m, other.getEmittedIncludeTree()); } fn requireKnownTarget(m: *Module) std.Target { diff --git a/lib/std/Build/Step/Compile.zig b/lib/std/Build/Step/Compile.zig index caa3a9e34a..2f9821e12d 100644 --- a/lib/std/Build/Step/Compile.zig +++ b/lib/std/Build/Step/Compile.zig @@ -59,7 +59,13 @@ test_runner: ?[]const u8, test_server_mode: bool, wasi_exec_model: ?std.builtin.WasiExecModel = null, -installed_headers: ArrayList(InstalledHeader), +installed_headers: ArrayList(HeaderInstallation), + +/// This step is used to create an include tree that dependent modules can add to their include +/// search paths. Installed headers are copied to this step. +/// This step is created the first time a module links with this artifact and is not +/// created otherwise. +installed_headers_include_tree: ?*Step.WriteFile = null, // keep in sync with src/Compilation.zig:RcIncludes /// Behavior of automatic detection of include directories when compiling .rc files. @@ -249,66 +255,62 @@ pub const Kind = enum { @"test", }; -pub const InstalledHeader = struct { - source: Source, - dest_rel_path: []const u8, +pub const HeaderInstallation = union(enum) { + file: File, + directory: Directory, - pub const Source = union(enum) { - file: LazyPath, - directory: Directory, - - pub const Directory = struct { - path: LazyPath, - options: Directory.Options, - - pub const Options = struct { - /// File paths which end in any of these suffixes will be excluded - /// from installation. - exclude_extensions: []const []const u8 = &.{}, - /// Only file paths which end in any of these suffixes will be included - /// in installation. - /// `null` means all suffixes will be included. - /// `exclude_extensions` takes precedence over `include_extensions` - include_extensions: ?[]const []const u8 = &.{".h"}, - - pub fn dupe(self: Directory.Options, b: *std.Build) Directory.Options { - return .{ - .exclude_extensions = b.dupeStrings(self.exclude_extensions), - .include_extensions = if (self.include_extensions) |incs| - b.dupeStrings(incs) - else - null, - }; - } + pub const File = struct { + source: LazyPath, + dest_rel_path: []const u8, + + pub fn dupe(self: File, b: *std.Build) File { + return .{ + .source = self.source.dupe(b), + .dest_rel_path = b.dupePath(self.dest_rel_path), }; + } + }; + + pub const Directory = struct { + source: LazyPath, + dest_rel_path: []const u8, + options: Directory.Options, + + pub const Options = struct { + /// File paths that end in any of these suffixes will be excluded from installation. + exclude_extensions: []const []const u8 = &.{}, + /// Only file paths that end in any of these suffixes will be included in installation. + /// `null` means that all suffixes will be included. + /// `exclude_extensions` takes precedence over `include_extensions`. + include_extensions: ?[]const []const u8 = &.{".h"}, - pub fn dupe(self: Directory, b: *std.Build) Directory { + pub fn dupe(self: Directory.Options, b: *std.Build) Directory.Options { return .{ - .path = self.path.dupe(b), - .options = self.options.dupe(b), + .exclude_extensions = b.dupeStrings(self.exclude_extensions), + .include_extensions = if (self.include_extensions) |incs| b.dupeStrings(incs) else null, }; } }; - pub fn path(self: Source) LazyPath { - return switch (self) { - .file => |lp| lp, - .directory => |dir| dir.path, - }; - } - - pub fn dupe(self: Source, b: *std.Build) Source { - return switch (self) { - .file => |lp| .{ .file = lp.dupe(b) }, - .directory => |dir| .{ .directory = dir.dupe(b) }, + pub fn dupe(self: Directory, b: *std.Build) Directory { + return .{ + .source = self.source.dupe(b), + .dest_rel_path = b.dupePath(self.dest_rel_path), + .options = self.options.dupe(b), }; } }; - pub fn dupe(self: InstalledHeader, b: *std.Build) InstalledHeader { - return .{ - .source = self.source.dupe(b), - .dest_rel_path = b.dupePath(self.dest_rel_path), + pub fn getSource(self: HeaderInstallation) LazyPath { + return switch (self) { + inline .file, .directory => |x| x.source, + }; + } + + pub fn dupe(self: HeaderInstallation, b: *std.Build) HeaderInstallation { + return switch (self) { + .file => |f| f.dupe(b), + .directory => |d| d.dupe(b), }; } }; @@ -372,7 +374,7 @@ pub fn create(owner: *std.Build, options: Options) *Compile { .out_lib_filename = undefined, .major_only_filename = null, .name_only_filename = null, - .installed_headers = ArrayList(InstalledHeader).init(owner.allocator), + .installed_headers = ArrayList(HeaderInstallation).init(owner.allocator), .zig_lib_dir = null, .exec_cmd_args = null, .filters = options.filters, @@ -444,49 +446,71 @@ pub fn create(owner: *std.Build, options: Options) *Compile { return self; } -pub fn installHeader( - cs: *Compile, - source: LazyPath, - dest_rel_path: []const u8, -) void { +pub fn installHeader(cs: *Compile, source: LazyPath, dest_rel_path: []const u8) void { const b = cs.step.owner; - cs.installed_headers.append(.{ - .source = .{ .file = source.dupe(b) }, + const installation: HeaderInstallation = .{ .file = .{ + .source = source.dupe(b), .dest_rel_path = b.dupePath(dest_rel_path), - }) catch @panic("OOM"); - source.addStepDependencies(&cs.step); + } }; + cs.installed_headers.append(installation) catch @panic("OOM"); + cs.addHeaderInstallationToIncludeTree(installation); + installation.getSource().addStepDependencies(&cs.step); } pub fn installHeaders( cs: *Compile, source: LazyPath, dest_rel_path: []const u8, - options: InstalledHeader.Source.Directory.Options, + options: HeaderInstallation.Directory.Options, ) void { const b = cs.step.owner; - cs.installed_headers.append(.{ - .source = .{ .directory = .{ - .path = source.dupe(b), - .options = options.dupe(b), - } }, + const installation: HeaderInstallation = .{ .directory = .{ + .source = source.dupe(b), .dest_rel_path = b.dupePath(dest_rel_path), - }) catch @panic("OOM"); - source.addStepDependencies(&cs.step); + .options = options.dupe(b), + } }; + cs.installed_headers.append(installation) catch @panic("OOM"); + cs.addHeaderInstallationToIncludeTree(installation); + installation.getSource().addStepDependencies(&cs.step); } pub fn installConfigHeader(cs: *Compile, config_header: *Step.ConfigHeader) void { - cs.installHeader(.{ .generated = &config_header.output_file }, config_header.include_path); + cs.installHeader(config_header.getOutput(), config_header.include_path); } pub fn installLibraryHeaders(cs: *Compile, lib: *Compile) void { assert(lib.kind == .lib); - const b = cs.step.owner; - for (lib.installed_headers.items) |header| { - cs.installed_headers.append(header.dupe(b)) catch @panic("OOM"); - header.source.path().addStepDependencies(&cs.step); + for (lib.installed_headers.items) |installation| { + cs.installed_headers.append(installation) catch @panic("OOM"); + cs.addHeaderInstallationToIncludeTree(installation); + installation.getSource().addStepDependencies(&cs.step); } } +fn addHeaderInstallationToIncludeTree(cs: *Compile, installation: HeaderInstallation) void { + if (cs.installed_headers_include_tree) |wf| switch (installation) { + .file => |file| { + _ = wf.addCopyFile(file.source, file.dest_rel_path); + }, + .directory => |dir| { + _ = dir; // TODO + }, + }; +} + +pub fn getEmittedIncludeTree(cs: *Compile) LazyPath { + if (cs.installed_headers_include_tree) |wf| return wf.getDirectory(); + const b = cs.step.owner; + const wf = b.addWriteFiles(); + cs.installed_headers_include_tree = wf; + for (cs.installed_headers.items) |installation| { + cs.addHeaderInstallationToIncludeTree(installation); + } + // The compile step itself does not need to depend on the write files step, + // only dependent modules do. + return wf.getDirectory(); +} + pub fn addObjCopy(cs: *Compile, options: Step.ObjCopy.Options) *Step.ObjCopy { const b = cs.step.owner; var copy = options; diff --git a/lib/std/Build/Step/InstallArtifact.zig b/lib/std/Build/Step/InstallArtifact.zig index 5f77487823..7ebe8fdaf0 100644 --- a/lib/std/Build/Step/InstallArtifact.zig +++ b/lib/std/Build/Step/InstallArtifact.zig @@ -177,10 +177,10 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { all_cached = all_cached and p == .fresh; } - for (self.artifact.installed_headers.items) |header| switch (header.source) { - .file => |lp| { - const full_src_path = lp.getPath2(b, step); - const full_h_path = b.getInstallPath(h_dir, header.dest_rel_path); + for (self.artifact.installed_headers.items) |installation| switch (installation) { + .file => |file| { + const full_src_path = file.source.getPath2(b, step); + const full_h_path = b.getInstallPath(h_dir, file.dest_rel_path); const p = fs.Dir.updateFile(cwd, full_src_path, cwd, full_h_path, .{}) catch |err| { return step.fail("unable to update file from '{s}' to '{s}': {s}", .{ full_src_path, full_h_path, @errorName(err), @@ -189,8 +189,8 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { all_cached = all_cached and p == .fresh; }, .directory => |dir| { - const full_src_dir_path = dir.path.getPath2(b, step); - const full_h_prefix = b.getInstallPath(h_dir, header.dest_rel_path); + const full_src_dir_path = dir.source.getPath2(b, step); + const full_h_prefix = b.getInstallPath(h_dir, dir.dest_rel_path); var src_dir = b.build_root.handle.openDir(full_src_dir_path, .{ .iterate = true }) catch |err| { return step.fail("unable to open source directory '{s}': {s}", .{ -- cgit v1.2.3 From e16db2988777e18c56925b4a458914b20a183c12 Mon Sep 17 00:00:00 2001 From: Carl Åstholm Date: Sun, 3 Mar 2024 17:14:02 +0100 Subject: Implement `WriteFile.addCopyDirectory` --- lib/std/Build/Step/Compile.zig | 5 +- lib/std/Build/Step/WriteFile.zig | 131 +++++++++++++++++++++++++++++++++++++-- 2 files changed, 131 insertions(+), 5 deletions(-) (limited to 'lib/std/Build/Step/Compile.zig') diff --git a/lib/std/Build/Step/Compile.zig b/lib/std/Build/Step/Compile.zig index 2f9821e12d..c046619b0b 100644 --- a/lib/std/Build/Step/Compile.zig +++ b/lib/std/Build/Step/Compile.zig @@ -493,7 +493,10 @@ fn addHeaderInstallationToIncludeTree(cs: *Compile, installation: HeaderInstalla _ = wf.addCopyFile(file.source, file.dest_rel_path); }, .directory => |dir| { - _ = dir; // TODO + _ = wf.addCopyDirectory(dir.source, dir.dest_rel_path, .{ + .exclude_extensions = dir.options.exclude_extensions, + .include_extensions = dir.options.include_extensions, + }); }, }; } diff --git a/lib/std/Build/Step/WriteFile.zig b/lib/std/Build/Step/WriteFile.zig index d0ac68377a..2ed7b1344c 100644 --- a/lib/std/Build/Step/WriteFile.zig +++ b/lib/std/Build/Step/WriteFile.zig @@ -15,9 +15,11 @@ const ArrayList = std.ArrayList; const WriteFile = @This(); step: Step, -/// The elements here are pointers because we need stable pointers for the -/// GeneratedFile field. + +// The elements here are pointers because we need stable pointers for the GeneratedFile field. files: std.ArrayListUnmanaged(*File), +directories: std.ArrayListUnmanaged(*Directory), + output_source_files: std.ArrayListUnmanaged(OutputSourceFile), generated_directory: std.Build.GeneratedFile, @@ -33,6 +35,33 @@ pub const File = struct { } }; +pub const Directory = struct { + source: std.Build.LazyPath, + sub_path: []const u8, + options: Options, + generated_dir: std.Build.GeneratedFile, + + pub const Options = struct { + /// File paths that end in any of these suffixes will be excluded from copying. + exclude_extensions: []const []const u8 = &.{}, + /// Only file paths that end in any of these suffixes will be included in copying. + /// `null` means that all suffixes will be included. + /// `exclude_extensions` takes precedence over `include_extensions`. + include_extensions: ?[]const []const u8 = &.{".h"}, + + pub fn dupe(self: Options, b: *std.Build) Options { + return .{ + .exclude_extensions = b.dupeStrings(self.exclude_extensions), + .include_extensions = if (self.include_extensions) |incs| b.dupeStrings(incs) else null, + }; + } + }; + + pub fn getPath(self: *Directory) std.Build.LazyPath { + return .{ .generated = &self.generated_dir }; + } +}; + pub const OutputSourceFile = struct { contents: Contents, sub_path: []const u8, @@ -53,6 +82,7 @@ pub fn create(owner: *std.Build) *WriteFile { .makeFn = make, }), .files = .{}, + .directories = .{}, .output_source_files = .{}, .generated_directory = .{ .step = &wf.step }, }; @@ -96,6 +126,28 @@ pub fn addCopyFile(wf: *WriteFile, source: std.Build.LazyPath, sub_path: []const return file.getPath(); } +pub fn addCopyDirectory( + wf: *WriteFile, + source: std.Build.LazyPath, + sub_path: []const u8, + options: Directory.Options, +) std.Build.LazyPath { + const b = wf.step.owner; + const gpa = b.allocator; + const dir = gpa.create(Directory) catch @panic("OOM"); + dir.* = .{ + .source = source.dupe(b), + .sub_path = b.dupePath(sub_path), + .options = options.dupe(b), + .generated_dir = .{ .step = &wf.step }, + }; + wf.directories.append(gpa, dir) catch @panic("OOM"); + + wf.maybeUpdateName(); + source.addStepDependencies(&wf.step); + return dir.getPath(); +} + /// A path relative to the package root. /// Be careful with this because it updates source files. This should not be /// used as part of the normal build process, but as a utility occasionally @@ -130,11 +182,16 @@ pub fn getDirectory(wf: *WriteFile) std.Build.LazyPath { } fn maybeUpdateName(wf: *WriteFile) void { - if (wf.files.items.len == 1) { + if (wf.files.items.len == 1 and wf.directories.items.len == 0) { // First time adding a file; update name. if (std.mem.eql(u8, wf.step.name, "WriteFile")) { wf.step.name = wf.step.owner.fmt("WriteFile {s}", .{wf.files.items[0].sub_path}); } + } else if (wf.directories.items.len == 1 and wf.files.items.len == 0) { + // First time adding a directory; update name. + if (std.mem.eql(u8, wf.step.name, "WriteFile")) { + wf.step.name = wf.step.owner.fmt("WriteFile {s}", .{wf.directories.items[0].sub_path}); + } } } @@ -209,6 +266,12 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { }, } } + for (wf.directories.items) |dir| { + man.hash.addBytes(dir.source.getPath2(b, step)); + man.hash.addBytes(dir.sub_path); + for (dir.options.exclude_extensions) |ext| man.hash.addBytes(ext); + if (dir.options.include_extensions) |incs| for (incs) |inc| man.hash.addBytes(inc); + } if (try step.cacheHit(&man)) { const digest = man.final(); @@ -233,6 +296,8 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { }; defer cache_dir.close(); + const cwd = fs.cwd(); + for (wf.files.items) |file| { if (fs.path.dirname(file.sub_path)) |dirname| { cache_dir.makePath(dirname) catch |err| { @@ -252,7 +317,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { .copy => |file_source| { const source_path = file_source.getPath(b); const prev_status = fs.Dir.updateFile( - fs.cwd(), + cwd, source_path, cache_dir, file.sub_path, @@ -279,6 +344,64 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { cache_path, file.sub_path, }); } + for (wf.directories.items) |dir| { + const full_src_dir_path = dir.source.getPath2(b, step); + const dest_dirname = dir.sub_path; + + if (dest_dirname.len != 0) { + cache_dir.makePath(dest_dirname) catch |err| { + return step.fail("unable to make path '{}{s}{c}{s}': {s}", .{ + b.cache_root, cache_path, fs.path.sep, dest_dirname, @errorName(err), + }); + }; + } + + var src_dir = b.build_root.handle.openDir(full_src_dir_path, .{ .iterate = true }) catch |err| { + return step.fail("unable to open source directory '{s}': {s}", .{ + full_src_dir_path, @errorName(err), + }); + }; + defer src_dir.close(); + + var it = try src_dir.walk(b.allocator); + next_entry: while (try it.next()) |entry| { + for (dir.options.exclude_extensions) |ext| { + if (std.mem.endsWith(u8, entry.path, ext)) continue :next_entry; + } + if (dir.options.include_extensions) |incs| { + for (incs) |inc| { + if (std.mem.endsWith(u8, entry.path, inc)) break; + } else { + continue :next_entry; + } + } + const full_src_entry_path = b.pathJoin(&.{ full_src_dir_path, entry.path }); + const dest_path = b.pathJoin(&.{ dest_dirname, entry.path }); + switch (entry.kind) { + .directory => try cache_dir.makePath(dest_path), + .file => { + const prev_status = fs.Dir.updateFile( + cwd, + full_src_entry_path, + cache_dir, + dest_path, + .{}, + ) catch |err| { + return step.fail("unable to update file from '{s}' to '{}{s}{c}{s}': {s}", .{ + full_src_entry_path, + b.cache_root, + cache_path, + fs.path.sep, + dest_path, + @errorName(err), + }); + }; + _ = prev_status; + }, + else => continue, + } + } + } try step.writeManifest(&man); } -- cgit v1.2.3 From 5af4e91e0036a5d83889d20ee1477a366c6c12ac Mon Sep 17 00:00:00 2001 From: Carl Åstholm Date: Mon, 4 Mar 2024 01:38:29 +0100 Subject: Oops, forgot to dupe installations in `installLibraryHeaders` Added test coverage for `installLibraryHeaders` --- lib/std/Build/Step/Compile.zig | 12 +++++++----- test/standalone/install_headers/build.zig | 22 +++++++++++++++++++--- 2 files changed, 26 insertions(+), 8 deletions(-) (limited to 'lib/std/Build/Step/Compile.zig') diff --git a/lib/std/Build/Step/Compile.zig b/lib/std/Build/Step/Compile.zig index c046619b0b..d7e8ec8ed3 100644 --- a/lib/std/Build/Step/Compile.zig +++ b/lib/std/Build/Step/Compile.zig @@ -309,8 +309,8 @@ pub const HeaderInstallation = union(enum) { pub fn dupe(self: HeaderInstallation, b: *std.Build) HeaderInstallation { return switch (self) { - .file => |f| f.dupe(b), - .directory => |d| d.dupe(b), + .file => |f| .{ .file = f.dupe(b) }, + .directory => |d| .{ .directory = d.dupe(b) }, }; } }; @@ -480,10 +480,12 @@ pub fn installConfigHeader(cs: *Compile, config_header: *Step.ConfigHeader) void pub fn installLibraryHeaders(cs: *Compile, lib: *Compile) void { assert(lib.kind == .lib); + const b = cs.step.owner; for (lib.installed_headers.items) |installation| { - cs.installed_headers.append(installation) catch @panic("OOM"); - cs.addHeaderInstallationToIncludeTree(installation); - installation.getSource().addStepDependencies(&cs.step); + const installation_copy = installation.dupe(b); + cs.installed_headers.append(installation_copy) catch @panic("OOM"); + cs.addHeaderInstallationToIncludeTree(installation_copy); + installation_copy.getSource().addStepDependencies(&cs.step); } } diff --git a/test/standalone/install_headers/build.zig b/test/standalone/install_headers/build.zig index 20fab2aaa6..69206fb59d 100644 --- a/test/standalone/install_headers/build.zig +++ b/test/standalone/install_headers/build.zig @@ -4,12 +4,14 @@ pub fn build(b: *std.Build) void { const test_step = b.step("test", "Test"); b.default_step = test_step; + const empty_c = b.addWriteFiles().add("empty.c", ""); + const libfoo = b.addStaticLibrary(.{ .name = "foo", .target = b.resolveTargetQuery(.{}), .optimize = .Debug, }); - libfoo.addCSourceFile(.{ .file = b.addWriteFiles().add("empty.c", "") }); + libfoo.addCSourceFile(.{ .file = empty_c }); const exe = b.addExecutable(.{ .name = "exe", @@ -23,8 +25,9 @@ pub fn build(b: *std.Build) void { \\#include \\#include \\#include + \\#include \\int main(void) { - \\ printf(FOO_A FOO_B FOO_D FOO_CONFIG_1 FOO_CONFIG_2); + \\ printf(FOO_A FOO_B FOO_D FOO_CONFIG_1 FOO_CONFIG_2 BAR_X); \\ return 0; \\} ) }); @@ -51,8 +54,20 @@ pub fn build(b: *std.Build) void { .FOO_CONFIG_2 = "2", })); + const libbar = b.addStaticLibrary(.{ + .name = "bar", + .target = b.resolveTargetQuery(.{}), + .optimize = .Debug, + }); + libbar.addCSourceFile(.{ .file = empty_c }); + libbar.installHeader(b.addWriteFiles().add("bar.h", + \\#define BAR_X "X" + \\ + ), "bar.h"); + libfoo.installLibraryHeaders(libbar); + const run_exe = b.addRunArtifact(exe); - run_exe.expectStdOutEqual("ABD12"); + run_exe.expectStdOutEqual("ABD12X"); test_step.dependOn(&run_exe.step); const install_exe = b.addInstallArtifact(libfoo, .{ @@ -75,6 +90,7 @@ pub fn build(b: *std.Build) void { "!custom/include/foo/sub_dir/c.ignore_me.h", "custom/include/foo/d.h", "custom/include/foo/config.h", + "custom/include/bar.h", }); run_check_exists.setCwd(.{ .cwd_relative = b.getInstallPath(.prefix, "") }); run_check_exists.expectExitCode(0); -- cgit v1.2.3 From d99e44a157dee9204c38e66bf7eba00c051690ba Mon Sep 17 00:00:00 2001 From: Carl Åstholm Date: Sun, 10 Mar 2024 12:17:33 +0100 Subject: Document added/updated functions Also renames `addHeaders` to `addHeadersDirectory` for clarity. --- lib/std/Build.zig | 7 ++++--- lib/std/Build/Step/Compile.zig | 14 +++++++++++++- lib/std/Build/Step/WriteFile.zig | 3 +++ test/standalone/install_headers/build.zig | 2 +- 4 files changed, 21 insertions(+), 5 deletions(-) (limited to 'lib/std/Build/Step/Compile.zig') diff --git a/lib/std/Build.zig b/lib/std/Build.zig index f0ef8b9a4a..50aa5c504c 100644 --- a/lib/std/Build.zig +++ b/lib/std/Build.zig @@ -1568,21 +1568,22 @@ pub fn addObjCopy(b: *Build, source: LazyPath, options: Step.ObjCopy.Options) *S return Step.ObjCopy.create(b, source, options); } -///`dest_rel_path` is relative to install prefix path +/// `dest_rel_path` is relative to install prefix path pub fn addInstallFile(b: *Build, source: LazyPath, dest_rel_path: []const u8) *Step.InstallFile { return b.addInstallFileWithDir(source, .prefix, dest_rel_path); } -///`dest_rel_path` is relative to bin path +/// `dest_rel_path` is relative to bin path pub fn addInstallBinFile(b: *Build, source: LazyPath, dest_rel_path: []const u8) *Step.InstallFile { return b.addInstallFileWithDir(source, .bin, dest_rel_path); } -///`dest_rel_path` is relative to lib path +/// `dest_rel_path` is relative to lib path pub fn addInstallLibFile(b: *Build, source: LazyPath, dest_rel_path: []const u8) *Step.InstallFile { return b.addInstallFileWithDir(source, .lib, dest_rel_path); } +/// `dest_rel_path` is relative to header path pub fn addInstallHeaderFile(b: *Build, source: LazyPath, dest_rel_path: []const u8) *Step.InstallFile { return b.addInstallFileWithDir(source, .header, dest_rel_path); } diff --git a/lib/std/Build/Step/Compile.zig b/lib/std/Build/Step/Compile.zig index d7e8ec8ed3..2883992c73 100644 --- a/lib/std/Build/Step/Compile.zig +++ b/lib/std/Build/Step/Compile.zig @@ -446,6 +446,9 @@ pub fn create(owner: *std.Build, options: Options) *Compile { return self; } +/// Marks the specified header for installation alongside this artifact. +/// When a module links with this artifact, all headers marked for installation are added to that +/// module's include search path. pub fn installHeader(cs: *Compile, source: LazyPath, dest_rel_path: []const u8) void { const b = cs.step.owner; const installation: HeaderInstallation = .{ .file = .{ @@ -457,7 +460,10 @@ pub fn installHeader(cs: *Compile, source: LazyPath, dest_rel_path: []const u8) installation.getSource().addStepDependencies(&cs.step); } -pub fn installHeaders( +/// Marks headers from the specified directory for installation alongside this artifact. +/// When a module links with this artifact, all headers marked for installation are added to that +/// module's include search path. +pub fn installHeadersDirectory( cs: *Compile, source: LazyPath, dest_rel_path: []const u8, @@ -474,10 +480,16 @@ pub fn installHeaders( installation.getSource().addStepDependencies(&cs.step); } +/// Marks the specified config header for installation alongside this artifact. +/// When a module links with this artifact, all headers marked for installation are added to that +/// module's include search path. pub fn installConfigHeader(cs: *Compile, config_header: *Step.ConfigHeader) void { cs.installHeader(config_header.getOutput(), config_header.include_path); } +/// Forwards all headers marked for installation from `lib` to this artifact. +/// When a module links with this artifact, all headers marked for installation are added to that +/// module's include search path. pub fn installLibraryHeaders(cs: *Compile, lib: *Compile) void { assert(lib.kind == .lib); const b = cs.step.owner; diff --git a/lib/std/Build/Step/WriteFile.zig b/lib/std/Build/Step/WriteFile.zig index f0ff6cfa70..310decdfe7 100644 --- a/lib/std/Build/Step/WriteFile.zig +++ b/lib/std/Build/Step/WriteFile.zig @@ -126,6 +126,9 @@ pub fn addCopyFile(wf: *WriteFile, source: std.Build.LazyPath, sub_path: []const return file.getPath(); } +/// Copy files matching the specified exclude/include patterns to the specified subdirectory +/// relative to this step's generated directory. +/// The returned value is a lazy path to the generated subdirectory. pub fn addCopyDirectory( wf: *WriteFile, source: std.Build.LazyPath, diff --git a/test/standalone/install_headers/build.zig b/test/standalone/install_headers/build.zig index 69206fb59d..965239aac5 100644 --- a/test/standalone/install_headers/build.zig +++ b/test/standalone/install_headers/build.zig @@ -32,7 +32,7 @@ pub fn build(b: *std.Build) void { \\} ) }); - libfoo.installHeaders(.{ .path = "include" }, "foo", .{ .exclude_extensions = &.{".ignore_me.h"} }); + libfoo.installHeadersDirectory(.{ .path = "include" }, "foo", .{ .exclude_extensions = &.{".ignore_me.h"} }); libfoo.installHeader(b.addWriteFiles().add("d.h", \\#define FOO_D "D" \\ -- cgit v1.2.3 From eee5400b7dc37845ea5f42e0841320953e7852b2 Mon Sep 17 00:00:00 2001 From: Carl Åstholm Date: Fri, 15 Mar 2024 19:59:02 +0100 Subject: Account for dependency boundaries when duping headers This is a temporary workaround that can be revered if/when 'path' lazy paths are updated to encode which build root they are relative to. --- lib/std/Build/Step/Compile.zig | 31 +++++++++++++++++++++++++++---- test/standalone/install_headers/build.zig | 4 ++-- 2 files changed, 29 insertions(+), 6 deletions(-) (limited to 'lib/std/Build/Step/Compile.zig') diff --git a/lib/std/Build/Step/Compile.zig b/lib/std/Build/Step/Compile.zig index 2883992c73..4a5364176a 100644 --- a/lib/std/Build/Step/Compile.zig +++ b/lib/std/Build/Step/Compile.zig @@ -264,8 +264,20 @@ pub const HeaderInstallation = union(enum) { dest_rel_path: []const u8, pub fn dupe(self: File, b: *std.Build) File { + // 'path' lazy paths are relative to the build root of some step, inferred from the step + // in which they are used. This means that we can't dupe such paths, because they may + // come from dependencies with their own build roots and duping the paths as is might + // cause the build script to search for the file relative to the wrong root. + // As a temporary workaround, we convert build root-relative paths to absolute paths. + // If/when the build-root relative paths are updated to encode which build root they are + // relative to, this workaround should be removed. + const duped_source: LazyPath = switch (self.source) { + .path => |root_rel| .{ .cwd_relative = b.pathFromRoot(root_rel) }, + else => self.source.dupe(b), + }; + return .{ - .source = self.source.dupe(b), + .source = duped_source, .dest_rel_path = b.dupePath(self.dest_rel_path), }; } @@ -293,8 +305,20 @@ pub const HeaderInstallation = union(enum) { }; pub fn dupe(self: Directory, b: *std.Build) Directory { + // 'path' lazy paths are relative to the build root of some step, inferred from the step + // in which they are used. This means that we can't dupe such paths, because they may + // come from dependencies with their own build roots and duping the paths as is might + // cause the build script to search for the file relative to the wrong root. + // As a temporary workaround, we convert build root-relative paths to absolute paths. + // If/when the build-root relative paths are updated to encode which build root they are + // relative to, this workaround should be removed. + const duped_source: LazyPath = switch (self.source) { + .path => |root_rel| .{ .cwd_relative = b.pathFromRoot(root_rel) }, + else => self.source.dupe(b), + }; + return .{ - .source = self.source.dupe(b), + .source = duped_source, .dest_rel_path = b.dupePath(self.dest_rel_path), .options = self.options.dupe(b), }; @@ -492,9 +516,8 @@ pub fn installConfigHeader(cs: *Compile, config_header: *Step.ConfigHeader) void /// module's include search path. pub fn installLibraryHeaders(cs: *Compile, lib: *Compile) void { assert(lib.kind == .lib); - const b = cs.step.owner; for (lib.installed_headers.items) |installation| { - const installation_copy = installation.dupe(b); + const installation_copy = installation.dupe(lib.step.owner); cs.installed_headers.append(installation_copy) catch @panic("OOM"); cs.addHeaderInstallationToIncludeTree(installation_copy); installation_copy.getSource().addStepDependencies(&cs.step); diff --git a/test/standalone/install_headers/build.zig b/test/standalone/install_headers/build.zig index 965239aac5..4c9bbb501a 100644 --- a/test/standalone/install_headers/build.zig +++ b/test/standalone/install_headers/build.zig @@ -70,7 +70,7 @@ pub fn build(b: *std.Build) void { run_exe.expectStdOutEqual("ABD12X"); test_step.dependOn(&run_exe.step); - const install_exe = b.addInstallArtifact(libfoo, .{ + const install_libfoo = b.addInstallArtifact(libfoo, .{ .dest_dir = .{ .override = .{ .custom = "custom" } }, .h_dir = .{ .override = .{ .custom = "custom/include" } }, .implib_dir = .disabled, @@ -94,6 +94,6 @@ pub fn build(b: *std.Build) void { }); run_check_exists.setCwd(.{ .cwd_relative = b.getInstallPath(.prefix, "") }); run_check_exists.expectExitCode(0); - run_check_exists.step.dependOn(&install_exe.step); + run_check_exists.step.dependOn(&install_libfoo.step); test_step.dependOn(&run_check_exists.step); } -- cgit v1.2.3