From e3424332d3fa1264e1f6861b76bb0d1b2996728d Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Sat, 4 May 2024 14:29:17 -0400 Subject: Build: cleanup * `doc/langref` formatting * upgrade `.{ .path = "..." }` to `b.path("...")` * avoid using arguments named `self` * make `Build.Step.Id` usage more consistent * add `Build.pathResolve` * use `pathJoin` and `pathResolve` everywhere * make sure `Build.LazyPath.getPath2` returns an absolute path --- lib/std/Build/Step/WriteFile.zig | 116 +++++++++++++++++++-------------------- 1 file changed, 58 insertions(+), 58 deletions(-) (limited to 'lib/std/Build/Step/WriteFile.zig') diff --git a/lib/std/Build/Step/WriteFile.zig b/lib/std/Build/Step/WriteFile.zig index 0bec300957..875ddbfdbe 100644 --- a/lib/std/Build/Step/WriteFile.zig +++ b/lib/std/Build/Step/WriteFile.zig @@ -23,15 +23,15 @@ directories: std.ArrayListUnmanaged(*Directory), output_source_files: std.ArrayListUnmanaged(OutputSourceFile), generated_directory: std.Build.GeneratedFile, -pub const base_id = .write_file; +pub const base_id: Step.Id = .write_file; pub const File = struct { generated_file: std.Build.GeneratedFile, sub_path: []const u8, contents: Contents, - pub fn getPath(self: *File) std.Build.LazyPath { - return .{ .generated = &self.generated_file }; + pub fn getPath(file: *File) std.Build.LazyPath { + return .{ .generated = &file.generated_file }; } }; @@ -49,16 +49,16 @@ pub const Directory = struct { /// `exclude_extensions` takes precedence over `include_extensions`. include_extensions: ?[]const []const u8 = null, - pub fn dupe(self: Options, b: *std.Build) Options { + pub fn dupe(opts: 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, + .exclude_extensions = b.dupeStrings(opts.exclude_extensions), + .include_extensions = if (opts.include_extensions) |incs| b.dupeStrings(incs) else null, }; } }; - pub fn getPath(self: *Directory) std.Build.LazyPath { - return .{ .generated = &self.generated_dir }; + pub fn getPath(dir: *Directory) std.Build.LazyPath { + return .{ .generated = &dir.generated_dir }; } }; @@ -73,10 +73,10 @@ pub const Contents = union(enum) { }; pub fn create(owner: *std.Build) *WriteFile { - const wf = owner.allocator.create(WriteFile) catch @panic("OOM"); - wf.* = .{ + const write_file = owner.allocator.create(WriteFile) catch @panic("OOM"); + write_file.* = .{ .step = Step.init(.{ - .id = .write_file, + .id = base_id, .name = "WriteFile", .owner = owner, .makeFn = make, @@ -84,22 +84,22 @@ pub fn create(owner: *std.Build) *WriteFile { .files = .{}, .directories = .{}, .output_source_files = .{}, - .generated_directory = .{ .step = &wf.step }, + .generated_directory = .{ .step = &write_file.step }, }; - return wf; + return write_file; } -pub fn add(wf: *WriteFile, sub_path: []const u8, bytes: []const u8) std.Build.LazyPath { - const b = wf.step.owner; +pub fn add(write_file: *WriteFile, sub_path: []const u8, bytes: []const u8) std.Build.LazyPath { + const b = write_file.step.owner; const gpa = b.allocator; const file = gpa.create(File) catch @panic("OOM"); file.* = .{ - .generated_file = .{ .step = &wf.step }, + .generated_file = .{ .step = &write_file.step }, .sub_path = b.dupePath(sub_path), .contents = .{ .bytes = b.dupe(bytes) }, }; - wf.files.append(gpa, file) catch @panic("OOM"); - wf.maybeUpdateName(); + write_file.files.append(gpa, file) catch @panic("OOM"); + write_file.maybeUpdateName(); return file.getPath(); } @@ -110,19 +110,19 @@ pub fn add(wf: *WriteFile, sub_path: []const u8, bytes: []const u8) std.Build.La /// include sub-directories, in which case this step will ensure the /// required sub-path exists. /// This is the option expected to be used most commonly with `addCopyFile`. -pub fn addCopyFile(wf: *WriteFile, source: std.Build.LazyPath, sub_path: []const u8) std.Build.LazyPath { - const b = wf.step.owner; +pub fn addCopyFile(write_file: *WriteFile, source: std.Build.LazyPath, sub_path: []const u8) std.Build.LazyPath { + const b = write_file.step.owner; const gpa = b.allocator; const file = gpa.create(File) catch @panic("OOM"); file.* = .{ - .generated_file = .{ .step = &wf.step }, + .generated_file = .{ .step = &write_file.step }, .sub_path = b.dupePath(sub_path), .contents = .{ .copy = source }, }; - wf.files.append(gpa, file) catch @panic("OOM"); + write_file.files.append(gpa, file) catch @panic("OOM"); - wf.maybeUpdateName(); - source.addStepDependencies(&wf.step); + write_file.maybeUpdateName(); + source.addStepDependencies(&write_file.step); return file.getPath(); } @@ -130,24 +130,24 @@ pub fn addCopyFile(wf: *WriteFile, source: std.Build.LazyPath, sub_path: []const /// relative to this step's generated directory. /// The returned value is a lazy path to the generated subdirectory. pub fn addCopyDirectory( - wf: *WriteFile, + write_file: *WriteFile, source: std.Build.LazyPath, sub_path: []const u8, options: Directory.Options, ) std.Build.LazyPath { - const b = wf.step.owner; + const b = write_file.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 }, + .generated_dir = .{ .step = &write_file.step }, }; - wf.directories.append(gpa, dir) catch @panic("OOM"); + write_file.directories.append(gpa, dir) catch @panic("OOM"); - wf.maybeUpdateName(); - source.addStepDependencies(&wf.step); + write_file.maybeUpdateName(); + source.addStepDependencies(&write_file.step); return dir.getPath(); } @@ -156,13 +156,13 @@ pub fn addCopyDirectory( /// used as part of the normal build process, but as a utility occasionally /// run by a developer with intent to modify source files and then commit /// those changes to version control. -pub fn addCopyFileToSource(wf: *WriteFile, source: std.Build.LazyPath, sub_path: []const u8) void { - const b = wf.step.owner; - wf.output_source_files.append(b.allocator, .{ +pub fn addCopyFileToSource(write_file: *WriteFile, source: std.Build.LazyPath, sub_path: []const u8) void { + const b = write_file.step.owner; + write_file.output_source_files.append(b.allocator, .{ .contents = .{ .copy = source }, .sub_path = sub_path, }) catch @panic("OOM"); - source.addStepDependencies(&wf.step); + source.addStepDependencies(&write_file.step); } /// A path relative to the package root. @@ -170,9 +170,9 @@ pub fn addCopyFileToSource(wf: *WriteFile, source: std.Build.LazyPath, sub_path: /// used as part of the normal build process, but as a utility occasionally /// run by a developer with intent to modify source files and then commit /// those changes to version control. -pub fn addBytesToSource(wf: *WriteFile, bytes: []const u8, sub_path: []const u8) void { - const b = wf.step.owner; - wf.output_source_files.append(b.allocator, .{ +pub fn addBytesToSource(write_file: *WriteFile, bytes: []const u8, sub_path: []const u8) void { + const b = write_file.step.owner; + write_file.output_source_files.append(b.allocator, .{ .contents = .{ .bytes = bytes }, .sub_path = sub_path, }) catch @panic("OOM"); @@ -180,20 +180,20 @@ pub fn addBytesToSource(wf: *WriteFile, bytes: []const u8, sub_path: []const u8) /// Returns a `LazyPath` representing the base directory that contains all the /// files from this `WriteFile`. -pub fn getDirectory(wf: *WriteFile) std.Build.LazyPath { - return .{ .generated = &wf.generated_directory }; +pub fn getDirectory(write_file: *WriteFile) std.Build.LazyPath { + return .{ .generated = &write_file.generated_directory }; } -fn maybeUpdateName(wf: *WriteFile) void { - if (wf.files.items.len == 1 and wf.directories.items.len == 0) { +fn maybeUpdateName(write_file: *WriteFile) void { + if (write_file.files.items.len == 1 and write_file.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}); + if (std.mem.eql(u8, write_file.step.name, "WriteFile")) { + write_file.step.name = write_file.step.owner.fmt("WriteFile {s}", .{write_file.files.items[0].sub_path}); } - } else if (wf.directories.items.len == 1 and wf.files.items.len == 0) { + } else if (write_file.directories.items.len == 1 and write_file.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}); + if (std.mem.eql(u8, write_file.step.name, "WriteFile")) { + write_file.step.name = write_file.step.owner.fmt("WriteFile {s}", .{write_file.directories.items[0].sub_path}); } } } @@ -201,14 +201,14 @@ fn maybeUpdateName(wf: *WriteFile) void { fn make(step: *Step, prog_node: *std.Progress.Node) !void { _ = prog_node; const b = step.owner; - const wf: *WriteFile = @fieldParentPtr("step", step); + const write_file: *WriteFile = @fieldParentPtr("step", step); // Writing to source files is kind of an extra capability of this // WriteFile - arguably it should be a different step. But anyway here // it is, it happens unconditionally and does not interact with the other // files here. var any_miss = false; - for (wf.output_source_files.items) |output_source_file| { + for (write_file.output_source_files.items) |output_source_file| { if (fs.path.dirname(output_source_file.sub_path)) |dirname| { b.build_root.handle.makePath(dirname) catch |err| { return step.fail("unable to make path '{}{s}': {s}", .{ @@ -226,7 +226,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { any_miss = true; }, .copy => |file_source| { - const source_path = file_source.getPath(b); + const source_path = file_source.getPath2(b, step); const prev_status = fs.Dir.updateFile( fs.cwd(), source_path, @@ -258,18 +258,18 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { // in a non-backwards-compatible way. man.hash.add(@as(u32, 0xd767ee59)); - for (wf.files.items) |file| { + for (write_file.files.items) |file| { man.hash.addBytes(file.sub_path); switch (file.contents) { .bytes => |bytes| { man.hash.addBytes(bytes); }, .copy => |file_source| { - _ = try man.addFile(file_source.getPath(b), null); + _ = try man.addFile(file_source.getPath2(b, step), null); }, } } - for (wf.directories.items) |dir| { + for (write_file.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); @@ -278,19 +278,19 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { if (try step.cacheHit(&man)) { const digest = man.final(); - for (wf.files.items) |file| { + for (write_file.files.items) |file| { file.generated_file.path = try b.cache_root.join(b.allocator, &.{ "o", &digest, file.sub_path, }); } - wf.generated_directory.path = try b.cache_root.join(b.allocator, &.{ "o", &digest }); + write_file.generated_directory.path = try b.cache_root.join(b.allocator, &.{ "o", &digest }); return; } const digest = man.final(); const cache_path = "o" ++ fs.path.sep_str ++ digest; - wf.generated_directory.path = try b.cache_root.join(b.allocator, &.{ "o", &digest }); + write_file.generated_directory.path = try b.cache_root.join(b.allocator, &.{ "o", &digest }); var cache_dir = b.cache_root.handle.makeOpenPath(cache_path, .{}) catch |err| { return step.fail("unable to make path '{}{s}': {s}", .{ @@ -301,7 +301,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { const cwd = fs.cwd(); - for (wf.files.items) |file| { + for (write_file.files.items) |file| { if (fs.path.dirname(file.sub_path)) |dirname| { cache_dir.makePath(dirname) catch |err| { return step.fail("unable to make path '{}{s}{c}{s}': {s}", .{ @@ -318,7 +318,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { }; }, .copy => |file_source| { - const source_path = file_source.getPath(b); + const source_path = file_source.getPath2(b, step); const prev_status = fs.Dir.updateFile( cwd, source_path, @@ -347,7 +347,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { cache_path, file.sub_path, }); } - for (wf.directories.items) |dir| { + for (write_file.directories.items) |dir| { const full_src_dir_path = dir.source.getPath2(b, step); const dest_dirname = dir.sub_path; -- cgit v1.2.3 From dee9f82f69db0d034251b844e0bc4083a1b25fdd Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Sat, 4 May 2024 15:12:24 -0400 Subject: Run: add output directory arguments This allows running commands that take an output directory argument. The main thing that was needed for this feature was generated file subpaths, to allow access to the files in a generated directory. Additionally, a minor change was required to so that the correct directory is created for output directory args. --- lib/std/Build.zig | 167 ++++++++++++----------- lib/std/Build/Step/Compile.zig | 6 +- lib/std/Build/Step/ConfigHeader.zig | 5 +- lib/std/Build/Step/ObjCopy.zig | 4 +- lib/std/Build/Step/Options.zig | 2 +- lib/std/Build/Step/Run.zig | 139 +++++++++++++++---- lib/std/Build/Step/TranslateC.zig | 2 +- lib/std/Build/Step/WriteFile.zig | 6 +- test/standalone/build.zig.zon | 3 + test/standalone/run_output_paths/build.zig | 40 ++++++ test/standalone/run_output_paths/create_file.zig | 19 +++ test/standalone/windows_resources/build.zig | 2 +- 12 files changed, 274 insertions(+), 121 deletions(-) create mode 100644 test/standalone/run_output_paths/build.zig create mode 100644 test/standalone/run_output_paths/create_file.zig (limited to 'lib/std/Build/Step/WriteFile.zig') diff --git a/lib/std/Build.zig b/lib/std/Build.zig index 474dce3e12..6c8ba75730 100644 --- a/lib/std/Build.zig +++ b/lib/std/Build.zig @@ -2131,28 +2131,23 @@ test dirnameAllowEmpty { /// A reference to an existing or future path. pub const LazyPath = union(enum) { - /// Deprecated; use the `path` function instead. - path: []const u8, - /// A source file path relative to build root. src_path: struct { owner: *std.Build, sub_path: []const u8, }, - /// A file that is generated by an interface. Those files usually are - /// not available until built by a build step. - generated: *const GeneratedFile, - - /// One of the parent directories of a file generated by an interface. - /// The path is not available until built by a build step. - generated_dirname: struct { - generated: *const GeneratedFile, + generated: struct { + file: *const GeneratedFile, /// The number of parent directories to go up. - /// 0 means the directory of the generated file, - /// 1 means the parent of that directory, and so on. - up: usize, + /// 0 means the generated file itself. + /// 1 means the directory of the generated file. + /// 2 means the parent of that directory, and so on. + up: usize = 0, + + /// Applied after `up`. + sub_path: []const u8 = "", }, /// An absolute path or a path relative to the current working directory of @@ -2168,12 +2163,6 @@ pub const LazyPath = union(enum) { sub_path: []const u8, }, - /// Deprecated. Call `path` instead. - pub fn relative(sub_path: []const u8) LazyPath { - std.log.warn("deprecated. call std.Build.path instead", .{}); - return .{ .path = sub_path }; - } - /// Returns a lazy path referring to the directory containing this path. /// /// The dirname is not allowed to escape the logical root for underlying path. @@ -2183,8 +2172,6 @@ pub const LazyPath = union(enum) { /// the dirname is not allowed to traverse outside of zig-cache. pub fn dirname(lazy_path: LazyPath) LazyPath { return switch (lazy_path) { - .generated => |gen| .{ .generated_dirname = .{ .generated = gen, .up = 0 } }, - .generated_dirname => |gen| .{ .generated_dirname = .{ .generated = gen.generated, .up = gen.up + 1 } }, .src_path => |sp| .{ .src_path = .{ .owner = sp.owner, .sub_path = dirnameAllowEmpty(sp.sub_path) orelse { @@ -2192,12 +2179,15 @@ pub const LazyPath = union(enum) { @panic("misconfigured build script"); }, } }, - .path => |sub_path| .{ - .path = dirnameAllowEmpty(sub_path) orelse { - dumpBadDirnameHelp(null, null, "dirname() attempted to traverse outside the build root\n", .{}) catch {}; - @panic("misconfigured build script"); - }, - }, + .generated => |generated| .{ .generated = if (dirnameAllowEmpty(generated.sub_path)) |sub_dirname| .{ + .file = generated.file, + .up = generated.up, + .sub_path = sub_dirname, + } else .{ + .file = generated.file, + .up = generated.up + 1, + .sub_path = "", + } }, .cwd_relative => |rel_path| .{ .cwd_relative = dirnameAllowEmpty(rel_path) orelse { // If we get null, it means one of two things: @@ -2234,14 +2224,34 @@ pub const LazyPath = union(enum) { }; } + pub fn path(lazy_path: LazyPath, b: *Build, sub_path: []const u8) LazyPath { + return switch (lazy_path) { + .src_path => |src| .{ .src_path = .{ + .owner = src.owner, + .sub_path = b.pathResolve(&.{ src.sub_path, sub_path }), + } }, + .generated => |gen| .{ .generated = .{ + .file = gen.file, + .up = gen.up, + .sub_path = b.pathResolve(&.{ gen.sub_path, sub_path }), + } }, + .cwd_relative => |cwd_relative| .{ + .cwd_relative = b.pathResolve(&.{ cwd_relative, sub_path }), + }, + .dependency => |dep| .{ .dependency = .{ + .dependency = dep.dependency, + .sub_path = b.pathResolve(&.{ dep.sub_path, sub_path }), + } }, + }; + } + /// Returns a string that can be shown to represent the file source. - /// Either returns the path or `"generated"`. + /// Either returns the path, `"generated"`, or `"dependency"`. pub fn getDisplayName(lazy_path: LazyPath) []const u8 { return switch (lazy_path) { - .src_path => |src_path| src_path.sub_path, - .path, .cwd_relative => |sub_path| sub_path, + .src_path => |sp| sp.sub_path, + .cwd_relative => |p| p, .generated => "generated", - .generated_dirname => "generated", .dependency => "dependency", }; } @@ -2249,9 +2259,8 @@ pub const LazyPath = union(enum) { /// Adds dependencies this file source implies to the given step. pub fn addStepDependencies(lazy_path: LazyPath, other_step: *Step) void { switch (lazy_path) { - .src_path, .path, .cwd_relative, .dependency => {}, - .generated => |gen| other_step.dependOn(gen.step), - .generated_dirname => |gen| other_step.dependOn(gen.generated.step), + .src_path, .cwd_relative, .dependency => {}, + .generated => |gen| other_step.dependOn(gen.file.step), } } @@ -2268,47 +2277,48 @@ pub const LazyPath = union(enum) { /// run that is asking for the path. pub fn getPath2(lazy_path: LazyPath, src_builder: *Build, asking_step: ?*Step) []const u8 { switch (lazy_path) { - .path => |p| return src_builder.pathFromRoot(p), .src_path => |sp| return sp.owner.pathFromRoot(sp.sub_path), .cwd_relative => |p| return src_builder.pathFromCwd(p), - .generated => |gen| return gen.step.owner.pathFromRoot(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"); - }), - .generated_dirname => |gen| { - const cache_root_path = src_builder.cache_root.path orelse - (src_builder.cache_root.join(src_builder.allocator, &.{"."}) catch @panic("OOM")); - - const gen_step = gen.generated.step; - var p = getPath2(LazyPath{ .generated = gen.generated }, src_builder, asking_step); - var i: usize = 0; - while (i <= gen.up) : (i += 1) { - // path is absolute. - // dirname will return null only if we're at root. - // Typically, we'll stop well before that at the cache root. - p = fs.path.dirname(p) orelse { - dumpBadDirnameHelp(gen_step, asking_step, - \\dirname() reached root. - \\No more directories left to go up. - \\ - , .{}) catch {}; - @panic("misconfigured build script"); - }; - - if (mem.eql(u8, p, cache_root_path) and i < gen.up) { - // If we hit the cache root and there's still more to go, - // the script attempted to go too far. - dumpBadDirnameHelp(gen_step, asking_step, - \\dirname() attempted to traverse outside the cache root. - \\This is not allowed. - \\ - , .{}) catch {}; - @panic("misconfigured build script"); + .generated => |gen| { + var file_path: []const u8 = gen.file.step.owner.pathFromRoot(gen.file.path orelse { + std.debug.getStderrMutex().lock(); + const stderr = std.io.getStdErr(); + dumpBadGetPathHelp(gen.file.step, stderr, src_builder, asking_step) catch {}; + std.debug.getStderrMutex().unlock(); + @panic("misconfigured build script"); + }); + + if (gen.up > 0) { + const cache_root_path = src_builder.cache_root.path orelse + (src_builder.cache_root.join(src_builder.allocator, &.{"."}) catch @panic("OOM")); + + for (0..gen.up) |_| { + if (mem.eql(u8, file_path, cache_root_path)) { + // If we hit the cache root and there's still more to go, + // the script attempted to go too far. + dumpBadDirnameHelp(gen.file.step, asking_step, + \\dirname() attempted to traverse outside the cache root. + \\This is not allowed. + \\ + , .{}) catch {}; + @panic("misconfigured build script"); + } + + // path is absolute. + // dirname will return null only if we're at root. + // Typically, we'll stop well before that at the cache root. + file_path = fs.path.dirname(file_path) orelse { + dumpBadDirnameHelp(gen.file.step, asking_step, + \\dirname() reached root. + \\No more directories left to go up. + \\ + , .{}) catch {}; + @panic("misconfigured build script"); + }; } } - return p; + + return src_builder.pathResolve(&.{ file_path, gen.sub_path }); }, .dependency => |dep| return dep.dependency.builder.pathFromRoot(dep.sub_path), } @@ -2324,15 +2334,12 @@ pub const LazyPath = union(enum) { .owner = sp.owner, .sub_path = sp.owner.dupePath(sp.sub_path), } }, - .path => |p| .{ .path = b.dupePath(p) }, .cwd_relative => |p| .{ .cwd_relative = b.dupePath(p) }, - .generated => |gen| .{ .generated = gen }, - .generated_dirname => |gen| .{ - .generated_dirname = .{ - .generated = gen.generated, - .up = gen.up, - }, - }, + .generated => |gen| .{ .generated = .{ + .file = gen.file, + .up = gen.up, + .sub_path = b.dupePath(gen.sub_path), + } }, .dependency => |dep| .{ .dependency = dep }, }; } diff --git a/lib/std/Build/Step/Compile.zig b/lib/std/Build/Step/Compile.zig index d8f2c73311..f660ef64a6 100644 --- a/lib/std/Build/Step/Compile.zig +++ b/lib/std/Build/Step/Compile.zig @@ -806,14 +806,12 @@ pub fn setLibCFile(compile: *Compile, libc_file: ?LazyPath) void { } fn getEmittedFileGeneric(compile: *Compile, output_file: *?*GeneratedFile) LazyPath { - if (output_file.*) |g| { - return .{ .generated = g }; - } + if (output_file.*) |file| return .{ .generated = .{ .file = file } }; const arena = compile.step.owner.allocator; const generated_file = arena.create(GeneratedFile) catch @panic("OOM"); generated_file.* = .{ .step = &compile.step }; output_file.* = generated_file; - return .{ .generated = generated_file }; + return .{ .generated = .{ .file = generated_file } }; } /// Returns the path to the directory that contains the emitted binary file. diff --git a/lib/std/Build/Step/ConfigHeader.zig b/lib/std/Build/Step/ConfigHeader.zig index bf8f941b16..4a0e64e8d0 100644 --- a/lib/std/Build/Step/ConfigHeader.zig +++ b/lib/std/Build/Step/ConfigHeader.zig @@ -59,8 +59,7 @@ pub fn create(owner: *std.Build, options: Options) *ConfigHeader { if (options.style.getPath()) |s| default_include_path: { const sub_path = switch (s) { .src_path => |sp| sp.sub_path, - .path => |path| path, - .generated, .generated_dirname => break :default_include_path, + .generated => break :default_include_path, .cwd_relative => |sub_path| sub_path, .dependency => |dependency| dependency.sub_path, }; @@ -106,7 +105,7 @@ pub fn addValues(config_header: *ConfigHeader, values: anytype) void { } pub fn getOutput(config_header: *ConfigHeader) std.Build.LazyPath { - return .{ .generated = &config_header.output_file }; + return .{ .generated = .{ .file = &config_header.output_file } }; } fn addValuesInner(config_header: *ConfigHeader, values: anytype) !void { diff --git a/lib/std/Build/Step/ObjCopy.zig b/lib/std/Build/Step/ObjCopy.zig index 6e18ae3066..515736dbc1 100644 --- a/lib/std/Build/Step/ObjCopy.zig +++ b/lib/std/Build/Step/ObjCopy.zig @@ -84,10 +84,10 @@ pub fn create( pub const getOutputSource = getOutput; pub fn getOutput(objcopy: *const ObjCopy) std.Build.LazyPath { - return .{ .generated = &objcopy.output_file }; + return .{ .generated = .{ .file = &objcopy.output_file } }; } pub fn getOutputSeparatedDebug(objcopy: *const ObjCopy) ?std.Build.LazyPath { - return if (objcopy.output_file_debug) |*file| .{ .generated = file } else null; + return if (objcopy.output_file_debug) |*file| .{ .generated = .{ .file = file } } else null; } fn make(step: *Step, prog_node: *std.Progress.Node) !void { diff --git a/lib/std/Build/Step/Options.zig b/lib/std/Build/Step/Options.zig index fe1833249b..c4daed73ff 100644 --- a/lib/std/Build/Step/Options.zig +++ b/lib/std/Build/Step/Options.zig @@ -407,7 +407,7 @@ pub const getSource = getOutput; /// Returns the main artifact of this Build Step which is a Zig source file /// generated from the key-value pairs of the Options. pub fn getOutput(options: *Options) LazyPath { - return .{ .generated = &options.generated_file }; + return .{ .generated = .{ .file = &options.generated_file } }; } fn make(step: *Step, prog_node: *std.Progress.Node) !void { diff --git a/lib/std/Build/Step/Run.zig b/lib/std/Build/Step/Run.zig index ffe19425cb..3b223f9b13 100644 --- a/lib/std/Build/Step/Run.zig +++ b/lib/std/Build/Step/Run.zig @@ -125,7 +125,8 @@ pub const Arg = union(enum) { lazy_path: PrefixedLazyPath, directory_source: PrefixedLazyPath, bytes: []u8, - output: *Output, + output_file: *Output, + output_directory: *Output, }; pub const PrefixedLazyPath = struct { @@ -225,13 +226,13 @@ pub fn addPrefixedOutputFileArg( .basename = b.dupe(basename), .generated_file = .{ .step = &run.step }, }; - run.argv.append(b.allocator, .{ .output = output }) catch @panic("OOM"); + run.argv.append(b.allocator, .{ .output_file = output }) catch @panic("OOM"); if (run.rename_step_with_output_arg) { run.setName(b.fmt("{s} ({s})", .{ run.step.name, basename })); } - return .{ .generated = &output.generated_file }; + return .{ .generated = .{ .file = &output.generated_file } }; } /// Appends an input file to the command line arguments. @@ -270,6 +271,56 @@ pub fn addPrefixedFileArg(run: *Run, prefix: []const u8, lp: std.Build.LazyPath) lp.addStepDependencies(&run.step); } +/// Provides a directory path as a command line argument to the command being run. +/// +/// Returns a `std.Build.LazyPath` which can be used as inputs to other APIs +/// throughout the build system. +/// +/// Related: +/// * `addPrefixedOutputDirectoryArg` - same thing but prepends a string to the argument +/// * `addDirectoryArg` - for input directories given to the child process +pub fn addOutputDirectoryArg(run: *Run, basename: []const u8) std.Build.LazyPath { + return run.addPrefixedOutputDirectoryArg("", basename); +} + +/// Provides a directory path as a command line argument to the command being run. +/// Asserts `basename` is not empty. +/// +/// For example, a prefix of "-o" and basename of "output_dir" will result in +/// the child process seeing something like this: "-ozig-cache/.../output_dir" +/// +/// The child process will see a single argument, regardless of whether the +/// prefix or basename have spaces. +/// +/// The returned `std.Build.LazyPath` can be used as inputs to other APIs +/// throughout the build system. +/// +/// Related: +/// * `addOutputDirectoryArg` - same thing but without the prefix +/// * `addDirectoryArg` - for input directories given to the child process +pub fn addPrefixedOutputDirectoryArg( + run: *Run, + prefix: []const u8, + basename: []const u8, +) std.Build.LazyPath { + if (basename.len == 0) @panic("basename must not be empty"); + const b = run.step.owner; + + const output = b.allocator.create(Output) catch @panic("OOM"); + output.* = .{ + .prefix = b.dupe(prefix), + .basename = b.dupe(basename), + .generated_file = .{ .step = &run.step }, + }; + run.argv.append(b.allocator, .{ .output_directory = output }) catch @panic("OOM"); + + if (run.rename_step_with_output_arg) { + run.setName(b.fmt("{s} ({s})", .{ run.step.name, basename })); + } + + return .{ .generated = .{ .file = &output.generated_file } }; +} + /// deprecated: use `addDirectoryArg` pub const addDirectorySourceArg = addDirectoryArg; @@ -314,9 +365,9 @@ pub fn addPrefixedDepFileOutputArg(run: *Run, prefix: []const u8, basename: []co run.dep_output_file = dep_file; - run.argv.append(b.allocator, .{ .output = dep_file }) catch @panic("OOM"); + run.argv.append(b.allocator, .{ .output_file = dep_file }) catch @panic("OOM"); - return .{ .generated = &dep_file.generated_file }; + return .{ .generated = .{ .file = &dep_file.generated_file } }; } pub fn addArg(run: *Run, arg: []const u8) void { @@ -432,7 +483,7 @@ pub fn addCheck(run: *Run, new_check: StdIo.Check) void { pub fn captureStdErr(run: *Run) std.Build.LazyPath { assert(run.stdio != .inherit); - if (run.captured_stderr) |output| return .{ .generated = &output.generated_file }; + if (run.captured_stderr) |output| return .{ .generated = .{ .file = &output.generated_file } }; const output = run.step.owner.allocator.create(Output) catch @panic("OOM"); output.* = .{ @@ -441,13 +492,13 @@ pub fn captureStdErr(run: *Run) std.Build.LazyPath { .generated_file = .{ .step = &run.step }, }; run.captured_stderr = output; - return .{ .generated = &output.generated_file }; + return .{ .generated = .{ .file = &output.generated_file } }; } pub fn captureStdOut(run: *Run) std.Build.LazyPath { assert(run.stdio != .inherit); - if (run.captured_stdout) |output| return .{ .generated = &output.generated_file }; + if (run.captured_stdout) |output| return .{ .generated = .{ .file = &output.generated_file } }; const output = run.step.owner.allocator.create(Output) catch @panic("OOM"); output.* = .{ @@ -456,7 +507,7 @@ pub fn captureStdOut(run: *Run) std.Build.LazyPath { .generated_file = .{ .step = &run.step }, }; run.captured_stdout = output; - return .{ .generated = &output.generated_file }; + return .{ .generated = .{ .file = &output.generated_file } }; } /// Adds an additional input files that, when modified, indicates that this Run @@ -484,7 +535,7 @@ fn hasAnyOutputArgs(run: Run) bool { if (run.captured_stdout != null) return true; if (run.captured_stderr != null) return true; for (run.argv.items) |arg| switch (arg) { - .output => return true, + .output_file, .output_directory => return true, else => continue, }; return false; @@ -520,6 +571,7 @@ fn checksContainStderr(checks: []const StdIo.Check) bool { const IndexedOutput = struct { index: usize, + tag: @typeInfo(Arg).Union.tag_type.?, output: *Output, }; fn make(step: *Step, prog_node: *std.Progress.Node) !void { @@ -563,17 +615,18 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { _ = try man.addFile(file_path, null); }, - .output => |output| { + .output_file, .output_directory => |output| { man.hash.addBytes(output.prefix); man.hash.addBytes(output.basename); // Add a placeholder into the argument list because we need the // manifest hash to be updated with all arguments before the // object directory is computed. - try argv_list.append(""); try output_placeholders.append(.{ - .index = argv_list.items.len - 1, + .index = argv_list.items.len, + .tag = arg, .output = output, }); + _ = try argv_list.addOne(); }, } } @@ -599,11 +652,6 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { hashStdIo(&man.hash, run.stdio); - if (has_side_effects) { - try runCommand(run, argv_list.items, has_side_effects, null, prog_node); - return; - } - for (run.extra_file_dependencies) |file_path| { _ = try man.addFile(b.pathFromRoot(file_path), null); } @@ -611,7 +659,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { _ = try man.addFile(lazy_path.getPath2(b, step), null); } - if (try step.cacheHit(&man)) { + if (try step.cacheHit(&man) and !has_side_effects) { // cache hit, skip running command const digest = man.final(); @@ -628,13 +676,54 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { return; } + const dep_output_file = run.dep_output_file orelse { + // We already know the final output paths, use them directly. + const digest = man.final(); + + try populateGeneratedPaths( + arena, + output_placeholders.items, + run.captured_stdout, + run.captured_stderr, + b.cache_root, + &digest, + ); + + const output_dir_path = "o" ++ fs.path.sep_str ++ &digest; + for (output_placeholders.items) |placeholder| { + const output_sub_path = b.pathJoin(&.{ output_dir_path, placeholder.output.basename }); + const output_sub_dir_path = switch (placeholder.tag) { + .output_file => fs.path.dirname(output_sub_path).?, + .output_directory => output_sub_path, + else => unreachable, + }; + b.cache_root.handle.makePath(output_sub_dir_path) catch |err| { + return step.fail("unable to make path '{}{s}': {s}", .{ + b.cache_root, output_sub_dir_path, @errorName(err), + }); + }; + const output_path = placeholder.output.generated_file.path.?; + argv_list.items[placeholder.index] = if (placeholder.output.prefix.len == 0) + output_path + else + b.fmt("{s}{s}", .{ placeholder.output.prefix, output_path }); + } + + return runCommand(run, argv_list.items, has_side_effects, output_dir_path, prog_node); + }; + + // We do not know the final output paths yet, use temp paths to run the command. const rand_int = std.crypto.random.int(u64); const tmp_dir_path = "tmp" ++ fs.path.sep_str ++ std.Build.hex64(rand_int); for (output_placeholders.items) |placeholder| { const output_components = .{ tmp_dir_path, placeholder.output.basename }; const output_sub_path = b.pathJoin(&output_components); - const output_sub_dir_path = fs.path.dirname(output_sub_path).?; + const output_sub_dir_path = switch (placeholder.tag) { + .output_file => fs.path.dirname(output_sub_path).?, + .output_directory => output_sub_path, + else => unreachable, + }; b.cache_root.handle.makePath(output_sub_dir_path) catch |err| { return step.fail("unable to make path '{}{s}': {s}", .{ b.cache_root, output_sub_dir_path, @errorName(err), @@ -642,17 +731,15 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { }; const output_path = try b.cache_root.join(arena, &output_components); placeholder.output.generated_file.path = output_path; - const cli_arg = if (placeholder.output.prefix.len == 0) + argv_list.items[placeholder.index] = if (placeholder.output.prefix.len == 0) output_path else b.fmt("{s}{s}", .{ placeholder.output.prefix, output_path }); - argv_list.items[placeholder.index] = cli_arg; } try runCommand(run, argv_list.items, has_side_effects, tmp_dir_path, prog_node); - if (run.dep_output_file) |dep_output_file| - try man.addDepFilePost(std.fs.cwd(), dep_output_file.generated_file.getPath()); + try man.addDepFilePost(std.fs.cwd(), dep_output_file.generated_file.getPath()); const digest = man.final(); @@ -777,7 +864,7 @@ fn runCommand( run: *Run, argv: []const []const u8, has_side_effects: bool, - tmp_dir_path: ?[]const u8, + output_dir_path: []const u8, prog_node: *std.Progress.Node, ) !void { const step = &run.step; @@ -950,7 +1037,7 @@ fn runCommand( }, }) |stream| { if (stream.captured) |output| { - const output_components = .{ tmp_dir_path.?, output.basename }; + const output_components = .{ output_dir_path, output.basename }; const output_path = try b.cache_root.join(arena, &output_components); output.generated_file.path = output_path; diff --git a/lib/std/Build/Step/TranslateC.zig b/lib/std/Build/Step/TranslateC.zig index 30a3988d4e..cb1b48e3c0 100644 --- a/lib/std/Build/Step/TranslateC.zig +++ b/lib/std/Build/Step/TranslateC.zig @@ -59,7 +59,7 @@ pub const AddExecutableOptions = struct { }; pub fn getOutput(translate_c: *TranslateC) std.Build.LazyPath { - return .{ .generated = &translate_c.output_file }; + return .{ .generated = .{ .file = &translate_c.output_file } }; } /// Creates a step to build an executable from the translated source. diff --git a/lib/std/Build/Step/WriteFile.zig b/lib/std/Build/Step/WriteFile.zig index 875ddbfdbe..401c5b78ec 100644 --- a/lib/std/Build/Step/WriteFile.zig +++ b/lib/std/Build/Step/WriteFile.zig @@ -31,7 +31,7 @@ pub const File = struct { contents: Contents, pub fn getPath(file: *File) std.Build.LazyPath { - return .{ .generated = &file.generated_file }; + return .{ .generated = .{ .file = &file.generated_file } }; } }; @@ -58,7 +58,7 @@ pub const Directory = struct { }; pub fn getPath(dir: *Directory) std.Build.LazyPath { - return .{ .generated = &dir.generated_dir }; + return .{ .generated = .{ .file = &dir.generated_dir } }; } }; @@ -181,7 +181,7 @@ pub fn addBytesToSource(write_file: *WriteFile, bytes: []const u8, sub_path: []c /// Returns a `LazyPath` representing the base directory that contains all the /// files from this `WriteFile`. pub fn getDirectory(write_file: *WriteFile) std.Build.LazyPath { - return .{ .generated = &write_file.generated_directory }; + return .{ .generated = .{ .file = &write_file.generated_directory } }; } fn maybeUpdateName(write_file: *WriteFile) void { diff --git a/test/standalone/build.zig.zon b/test/standalone/build.zig.zon index 7ddde1611a..e1b856a4be 100644 --- a/test/standalone/build.zig.zon +++ b/test/standalone/build.zig.zon @@ -164,6 +164,9 @@ .dependencyFromBuildZig = .{ .path = "dependencyFromBuildZig", }, + .run_output_paths = .{ + .path = "run_output_paths", + }, }, .paths = .{ "build.zig", diff --git a/test/standalone/run_output_paths/build.zig b/test/standalone/run_output_paths/build.zig new file mode 100644 index 0000000000..f4c7254d8f --- /dev/null +++ b/test/standalone/run_output_paths/build.zig @@ -0,0 +1,40 @@ +const std = @import("std"); + +pub fn build(b: *std.Build) void { + const test_step = b.step("test", "Test it"); + b.default_step = test_step; + + const target = b.standardTargetOptions(.{}); + const optimize = b.standardOptimizeOption(.{}); + + const create_file_exe = b.addExecutable(.{ + .name = "create_file", + .root_source_file = b.path("create_file.zig"), + .target = target, + .optimize = optimize, + }); + + const create_first = b.addRunArtifact(create_file_exe); + const first_dir = create_first.addOutputDirectoryArg("first"); + create_first.addArg("hello1.txt"); + test_step.dependOn(&b.addCheckFile(first_dir.path(b, "hello1.txt"), .{ .expected_matches = &.{ + std.fs.path.sep_str ++ + \\first + \\hello1.txt + \\Hello, world! + \\ + , + } }).step); + + const create_second = b.addRunArtifact(create_file_exe); + const second_dir = create_second.addPrefixedOutputDirectoryArg("--dir=", "second"); + create_second.addArg("hello2.txt"); + test_step.dependOn(&b.addCheckFile(second_dir.path(b, "hello2.txt"), .{ .expected_matches = &.{ + std.fs.path.sep_str ++ + \\second + \\hello2.txt + \\Hello, world! + \\ + , + } }).step); +} diff --git a/test/standalone/run_output_paths/create_file.zig b/test/standalone/run_output_paths/create_file.zig new file mode 100644 index 0000000000..041ebc3e50 --- /dev/null +++ b/test/standalone/run_output_paths/create_file.zig @@ -0,0 +1,19 @@ +const std = @import("std"); + +pub fn main() !void { + var args = try std.process.argsWithAllocator(std.heap.page_allocator); + _ = args.skip(); + const dir_name = args.next().?; + const dir = try std.fs.cwd().openDir(if (std.mem.startsWith(u8, dir_name, "--dir=")) + dir_name["--dir=".len..] + else + dir_name, .{}); + const file_name = args.next().?; + const file = try dir.createFile(file_name, .{}); + try file.writer().print( + \\{s} + \\{s} + \\Hello, world! + \\ + , .{ dir_name, file_name }); +} diff --git a/test/standalone/windows_resources/build.zig b/test/standalone/windows_resources/build.zig index 15130d3b87..da88659b4d 100644 --- a/test/standalone/windows_resources/build.zig +++ b/test/standalone/windows_resources/build.zig @@ -36,7 +36,7 @@ fn add( .file = b.path("res/zig.rc"), .flags = &.{"/c65001"}, // UTF-8 code page .include_paths = &.{ - .{ .generated = &generated_h_step.generated_directory }, + .{ .generated = .{ .file = &generated_h_step.generated_directory } }, }, }); exe.rc_includes = switch (rc_includes) { -- cgit v1.2.3