| author | |
| committer | |
| log | fa26566867cddbb0cd067cbc1d41bd41e68002a1 |
| tree | 43bc4abdf7a328e36d54333df462108dc01c06f5 |
| parent | c57bf9904396c365191d25b9ce6410bb47daa96a |
10 files changed, 215 insertions(+), 327 deletions(-)
BRANCH_TODO-1| ... | ... | @@ -26,7 +26,6 @@ |
| 26 | 26 | - but artifact install steps also add paths for dyn libs on windows |
| 27 | 27 | * no more "artifact arg" to run step. if you want to run the post-install binary, get the lazy path |
| 28 | 28 | from the install step. |
| 29 | * -D options which are files need to be accounted for in the configure cache | |
| 30 | 29 | |
| 31 | 30 | |
| 32 | 31 | ## Release Notes |
build.zig+8-5| ... | ... | @@ -208,7 +208,8 @@ pub fn build(b: *std.Build) !void { |
| 208 | 208 | .single_threaded = single_threaded, |
| 209 | 209 | }); |
| 210 | 210 | exe.pie = pie; |
| 211 | exe.entitlements = entitlements; | |
| 211 | // https://codeberg.org/ziglang/zig/issues/32173 | |
| 212 | exe.entitlements = if (entitlements) |p| .{ .cwd_relative = p } else null; | |
| 212 | 213 | exe.use_new_linker = b.option(bool, "new-linker", "Use the new linker"); |
| 213 | 214 | |
| 214 | 215 | const use_llvm = b.option(bool, "use-llvm", "Use the llvm backend"); |
| ... | ... | @@ -1498,11 +1499,13 @@ fn generateLangRef(b: *std.Build) std.Build.LazyPath { |
| 1498 | 1499 | }), |
| 1499 | 1500 | }); |
| 1500 | 1501 | |
| 1501 | var dir = b.build_root.handle.openDir(io, "doc/langref", .{ .iterate = true }) catch |err| { | |
| 1502 | std.debug.panic("unable to open '{f}doc/langref' directory: {s}", .{ | |
| 1503 | b.build_root, @errorName(err), | |
| 1504 | }); | |
| 1502 | const langref_path: std.Build.Cache.Path = .{ | |
| 1503 | .root_dir = b.build_root, | |
| 1504 | .sub_path = "doc/langref", | |
| 1505 | 1505 | }; |
| 1506 | ||
| 1507 | var dir = langref_path.root_dir.handle.openDir(io, langref_path.sub_path, .{ .iterate = true }) catch |err| | |
| 1508 | std.debug.panic("unable to open directory {f}: {t}", .{ langref_path, err }); | |
| 1506 | 1509 | defer dir.close(io); |
| 1507 | 1510 | |
| 1508 | 1511 | var wf = b.addWriteFiles(); |
lib/compiler/Maker/Step/InstallDir.zig created+66| ... | ... | @@ -0,0 +1,66 @@ |
| 1 | const InstallDir = @This(); | |
| 2 | ||
| 3 | const std = @import("std"); | |
| 4 | const Configuration = std.Build.Configuration; | |
| 5 | ||
| 6 | const Step = @import("../Step.zig"); | |
| 7 | const Maker = @import("../../Maker.zig"); | |
| 8 | ||
| 9 | pub fn make( | |
| 10 | install_dir: *InstallDir, | |
| 11 | step_index: Configuration.Step.Index, | |
| 12 | maker: *Maker, | |
| 13 | progress_node: std.Progress.Node, | |
| 14 | ) !void { | |
| 15 | const graph = maker.graph; | |
| 16 | const arena = maker.graph.arena; // TODO don't leak into process arena | |
| 17 | const io = graph.io; | |
| 18 | const step = maker.stepByIndex(step_index); | |
| 19 | ||
| 20 | step.clearWatchInputs(); | |
| 21 | const dest_prefix = b.getInstallPath(install_dir.options.install_dir, install_dir.options.install_subdir); | |
| 22 | const src_dir_path = install_dir.options.source_dir.getPath3(b, step); | |
| 23 | const need_derived_inputs = try step.addDirectoryWatchInput(install_dir.options.source_dir); | |
| 24 | var src_dir = src_dir_path.root_dir.handle.openDir(io, src_dir_path.subPathOrDot(), .{ .iterate = true }) catch |err| { | |
| 25 | return step.fail("unable to open source directory '{f}': {t}", .{ src_dir_path, err }); | |
| 26 | }; | |
| 27 | defer src_dir.close(io); | |
| 28 | var it = try src_dir.walk(arena); | |
| 29 | var all_cached = true; | |
| 30 | next_entry: while (try it.next(io)) |entry| { | |
| 31 | for (install_dir.options.exclude_extensions) |ext| { | |
| 32 | if (std.mem.endsWith(u8, entry.path, ext)) continue :next_entry; | |
| 33 | } | |
| 34 | if (install_dir.options.include_extensions) |incs| { | |
| 35 | for (incs) |inc| { | |
| 36 | if (std.mem.endsWith(u8, entry.path, inc)) break; | |
| 37 | } else { | |
| 38 | continue :next_entry; | |
| 39 | } | |
| 40 | } | |
| 41 | ||
| 42 | const src_path = try install_dir.options.source_dir.join(arena, entry.path); | |
| 43 | const dest_path = b.pathJoin(&.{ dest_prefix, entry.path }); | |
| 44 | switch (entry.kind) { | |
| 45 | .directory => { | |
| 46 | if (need_derived_inputs) _ = try step.addDirectoryWatchInput(src_path); | |
| 47 | const p = try step.installDir(dest_path); | |
| 48 | all_cached = all_cached and p == .existed; | |
| 49 | }, | |
| 50 | .file => { | |
| 51 | for (install_dir.options.blank_extensions) |ext| { | |
| 52 | if (std.mem.endsWith(u8, entry.path, ext)) { | |
| 53 | try b.truncateFile(dest_path); | |
| 54 | continue :next_entry; | |
| 55 | } | |
| 56 | } | |
| 57 | ||
| 58 | const p = try step.installFile(src_path, dest_path); | |
| 59 | all_cached = all_cached and p == .fresh; | |
| 60 | }, | |
| 61 | else => continue, | |
| 62 | } | |
| 63 | } | |
| 64 | ||
| 65 | step.result_cached = all_cached; | |
| 66 | } |
lib/compiler/Maker/Step/InstallFile.zig+1| ... | ... | @@ -19,6 +19,7 @@ pub fn make( |
| 19 | 19 | const conf = &maker.scanned_config.configuration; |
| 20 | 20 | const conf_step = step_index.ptr(conf); |
| 21 | 21 | const conf_if = conf_step.extended.get(conf.extra).install_file; |
| 22 | ||
| 22 | 23 | try step.singleUnchangingWatchInput(maker, arena, conf_if.source.get(conf)); |
| 23 | 24 | const p = try maker.installLazyPathSub(arena, conf_if.source, conf_if.dest_dir, conf_if.dest_sub_path.slice(conf), step_index); |
| 24 | 25 | step.result_cached = p == .fresh; |
lib/compiler/Maker/Step/Options.zig created+84| ... | ... | @@ -0,0 +1,84 @@ |
| 1 | const Options = @This(); | |
| 2 | ||
| 3 | const std = @import("std"); | |
| 4 | const Configuration = std.Build.Configuration; | |
| 5 | ||
| 6 | const Step = @import("../Step.zig"); | |
| 7 | const Maker = @import("../../Maker.zig"); | |
| 8 | ||
| 9 | ||
| 10 | fn make( | |
| 11 | options: *Options, | |
| 12 | step_index: Configuration.Step.Index, | |
| 13 | maker: *Maker, | |
| 14 | progress_node: std.Progress.Node, | |
| 15 | ) !void { | |
| 16 | // This step completes so quickly that no progress reporting is necessary. | |
| 17 | _ = progress_node; | |
| 18 | ||
| 19 | const graph = maker.graph; | |
| 20 | const step = maker.stepByIndex(step_index); | |
| 21 | const io = graph.io; | |
| 22 | const cache_root = graph.local_cache_root; | |
| 23 | ||
| 24 | for (options.args.items) |arg| { | |
| 25 | options.addOption( | |
| 26 | []const u8, | |
| 27 | arg.name, | |
| 28 | arg.path.getPath2(b, step), | |
| 29 | ); | |
| 30 | } | |
| 31 | if (!step.inputs.populated()) for (options.args.items) |arg| { | |
| 32 | try step.addWatchInput(arg.path); | |
| 33 | }; | |
| 34 | ||
| 35 | const basename = "options.zig"; | |
| 36 | ||
| 37 | // Hash contents to file name. | |
| 38 | var hash = graph.cache.hash; | |
| 39 | // Random bytes to make unique. Refresh this with new random bytes when | |
| 40 | // implementation is modified in a non-backwards-compatible way. | |
| 41 | hash.add(@as(u32, 0xad95e922)); | |
| 42 | hash.addBytes(options.contents.items); | |
| 43 | const sub_path = "c" ++ fs.path.sep_str ++ hash.final() ++ fs.path.sep_str ++ basename; | |
| 44 | ||
| 45 | options.generated_file.path = try cache_root.join(arena, &.{sub_path}); | |
| 46 | ||
| 47 | // Optimize for the hot path. Stat the file, and if it already exists, | |
| 48 | // cache hit. | |
| 49 | if (cache_root.handle.access(io, sub_path, .{})) |_| { | |
| 50 | // This is the hot path, success. | |
| 51 | step.result_cached = true; | |
| 52 | return; | |
| 53 | } else |outer_err| switch (outer_err) { | |
| 54 | error.FileNotFound => { | |
| 55 | var atomic_file = cache_root.handle.createFileAtomic(io, sub_path, .{ | |
| 56 | .replace = false, | |
| 57 | .make_path = true, | |
| 58 | }) catch |err| return step.fail("failed to create temporary path for '{f}{s}': {t}", .{ | |
| 59 | cache_root, sub_path, err, | |
| 60 | }); | |
| 61 | defer atomic_file.deinit(io); | |
| 62 | ||
| 63 | atomic_file.file.writeStreamingAll(io, options.contents.items) catch |err| { | |
| 64 | return step.fail("failed to write options to temporary path for '{f}{s}': {t}", .{ | |
| 65 | cache_root, sub_path, err, | |
| 66 | }); | |
| 67 | }; | |
| 68 | ||
| 69 | atomic_file.link(io) catch |err| switch (err) { | |
| 70 | error.PathAlreadyExists => { | |
| 71 | step.result_cached = true; | |
| 72 | return; | |
| 73 | }, | |
| 74 | else => return step.fail("failed to link temporary file into '{f}{s}': {t}", .{ | |
| 75 | cache_root, sub_path, err, | |
| 76 | }), | |
| 77 | }; | |
| 78 | }, | |
| 79 | else => |e| return step.fail("unable to access options file '{f}{s}': {t}", .{ | |
| 80 | cache_root, sub_path, e, | |
| 81 | }), | |
| 82 | } | |
| 83 | } | |
| 84 |
lib/std/Build.zig+7-7| ... | ... | @@ -99,19 +99,19 @@ pub const Graph = struct { |
| 99 | 99 | return @enumFromInt(graph.generated_files.items.len - 1); |
| 100 | 100 | } |
| 101 | 101 | |
| 102 | pub fn dupeString(graph: *Graph, bytes: []const u8) []const u8 { | |
| 102 | pub fn dupeString(graph: *const Graph, bytes: []const u8) []const u8 { | |
| 103 | 103 | return graph.arena.dupe(u8, bytes) catch @panic("OOM"); |
| 104 | 104 | } |
| 105 | 105 | |
| 106 | pub fn dupePath(graph: *Graph, bytes: []const u8) []const u8 { | |
| 106 | pub fn dupePath(graph: *const Graph, bytes: []const u8) []const u8 { | |
| 107 | 107 | const arena = graph.arena; |
| 108 | if (builtin.os.tag != .windows) return graph.arena.dupe(u8, bytes) catch @panic("OOM"); | |
| 108 | if (builtin.os.tag != .windows) return arena.dupe(u8, bytes) catch @panic("OOM"); | |
| 109 | 109 | const the_copy = arena.dupe(u8, bytes) catch @panic("OOM"); |
| 110 | 110 | mem.replaceScalar(u8, the_copy, '/', '\\'); |
| 111 | 111 | return the_copy; |
| 112 | 112 | } |
| 113 | 113 | |
| 114 | pub fn dupeStrings(graph: *Graph, strings: []const []const u8) []const []const u8 { | |
| 114 | pub fn dupeStrings(graph: *const Graph, strings: []const []const u8) []const []const u8 { | |
| 115 | 115 | const arena = graph.arena; |
| 116 | 116 | const array = arena.alloc([]const u8, strings.len) catch @panic("OOM"); |
| 117 | 117 | for (array, strings) |*dest, source| dest.* = dupeString(graph, source); |
| ... | ... | @@ -2186,7 +2186,7 @@ pub const LazyPath = union(enum) { |
| 2186 | 2186 | /// |
| 2187 | 2187 | /// The `b` parameter is only used for its allocator. All *Build instances |
| 2188 | 2188 | /// share the same allocator. |
| 2189 | pub fn dupe(lazy_path: LazyPath, graph: *Graph) LazyPath { | |
| 2189 | pub fn dupe(lazy_path: LazyPath, graph: *const Graph) LazyPath { | |
| 2190 | 2190 | return switch (lazy_path) { |
| 2191 | 2191 | .src_path => |sp| .{ .src_path = .{ .owner = sp.owner, .sub_path = sp.owner.dupePath(sp.sub_path) } }, |
| 2192 | 2192 | .cwd_relative => |p| .{ .cwd_relative = graph.dupePath(p) }, |
| ... | ... | @@ -2245,9 +2245,9 @@ pub const InstallDir = union(enum) { |
| 2245 | 2245 | custom: []const u8, |
| 2246 | 2246 | |
| 2247 | 2247 | /// Duplicates the install directory including the path if set to custom. |
| 2248 | pub fn dupe(dir: InstallDir, builder: *Build) InstallDir { | |
| 2248 | pub fn dupe(dir: InstallDir, graph: *const Graph) InstallDir { | |
| 2249 | 2249 | if (dir == .custom) { |
| 2250 | return .{ .custom = builder.dupe(dir.custom) }; | |
| 2250 | return .{ .custom = graph.dupeString(dir.custom) }; | |
| 2251 | 2251 | } else { |
| 2252 | 2252 | return dir; |
| 2253 | 2253 | } |
lib/std/Build/Configuration.zig+21-2| ... | ... | @@ -1039,10 +1039,20 @@ pub const Step = extern struct { |
| 1039 | 1039 | |
| 1040 | 1040 | pub const InstallDir = struct { |
| 1041 | 1041 | flags: @This().Flags, |
| 1042 | source_dir: LazyPath.Index, | |
| 1043 | dest_dir: InstallDestDir, | |
| 1044 | dest_sub_path: Storage.FlagOptional(.flags, .dest_sub_path, String), | |
| 1045 | exclude_extensions: Storage.FlagLengthPrefixedList(.flags, .exclude_extensions, String), | |
| 1046 | include_extensions: Storage.FlagLengthPrefixedList(.flags, .include_extensions, String), | |
| 1047 | blank_extensions: Storage.FlagLengthPrefixedList(.flags, .blank_extensions, String), | |
| 1042 | 1048 | |
| 1043 | 1049 | pub const Flags = packed struct(u32) { |
| 1044 | 1050 | tag: Tag = .install_dir, |
| 1045 | _: u27 = 0, | |
| 1051 | dest_sub_path: bool, | |
| 1052 | exclude_extensions: bool, | |
| 1053 | include_extensions: bool, | |
| 1054 | blank_extensions: bool, | |
| 1055 | _: u23 = 0, | |
| 1046 | 1056 | }; |
| 1047 | 1057 | }; |
| 1048 | 1058 | |
| ... | ... | @@ -1069,10 +1079,19 @@ pub const Step = extern struct { |
| 1069 | 1079 | |
| 1070 | 1080 | pub const Options = struct { |
| 1071 | 1081 | flags: @This().Flags, |
| 1082 | generated_file: GeneratedFileIndex, | |
| 1083 | contents: Bytes, | |
| 1084 | args: Storage.FlagLengthPrefixedList(.flags, .args, Arg), | |
| 1085 | ||
| 1086 | pub const Arg = extern struct { | |
| 1087 | name: String, | |
| 1088 | path: LazyPath.Index, | |
| 1089 | }; | |
| 1072 | 1090 | |
| 1073 | 1091 | pub const Flags = packed struct(u32) { |
| 1074 | 1092 | tag: Tag = .options, |
| 1075 | _: u27 = 0, | |
| 1093 | args: bool, | |
| 1094 | _: u26 = 0, | |
| 1076 | 1095 | }; |
| 1077 | 1096 | }; |
| 1078 | 1097 |
lib/std/Build/Step/InstallDir.zig+11-64| ... | ... | @@ -1,9 +1,10 @@ |
| 1 | const InstallDir = @This(); | |
| 2 | ||
| 1 | 3 | const std = @import("std"); |
| 2 | 4 | const mem = std.mem; |
| 3 | 5 | const fs = std.fs; |
| 4 | 6 | const Step = std.Build.Step; |
| 5 | 7 | const LazyPath = std.Build.LazyPath; |
| 6 | const InstallDir = @This(); | |
| 7 | 8 | |
| 8 | 9 | step: Step, |
| 9 | 10 | options: Options, |
| ... | ... | @@ -28,83 +29,29 @@ pub const Options = struct { |
| 28 | 29 | /// `@import("test.zig")` would be a compile error. |
| 29 | 30 | blank_extensions: []const []const u8 = &.{}, |
| 30 | 31 | |
| 31 | fn dupe(opts: Options, b: *std.Build) Options { | |
| 32 | fn dupe(opts: Options, graph: *const std.Build.Graph) Options { | |
| 32 | 33 | return .{ |
| 33 | .source_dir = opts.source_dir.dupe(b), | |
| 34 | .install_dir = opts.install_dir.dupe(b), | |
| 35 | .install_subdir = b.dupe(opts.install_subdir), | |
| 36 | .exclude_extensions = b.dupeStrings(opts.exclude_extensions), | |
| 37 | .include_extensions = if (opts.include_extensions) |incs| b.dupeStrings(incs) else null, | |
| 38 | .blank_extensions = b.dupeStrings(opts.blank_extensions), | |
| 34 | .source_dir = opts.source_dir.dupe(graph), | |
| 35 | .install_dir = opts.install_dir.dupe(graph), | |
| 36 | .install_subdir = graph.dupeString(opts.install_subdir), | |
| 37 | .exclude_extensions = graph.dupeStrings(opts.exclude_extensions), | |
| 38 | .include_extensions = if (opts.include_extensions) |incs| graph.dupeStrings(incs) else null, | |
| 39 | .blank_extensions = graph.dupeStrings(opts.blank_extensions), | |
| 39 | 40 | }; |
| 40 | 41 | } |
| 41 | 42 | }; |
| 42 | 43 | |
| 43 | 44 | pub fn create(owner: *std.Build, options: Options) *InstallDir { |
| 44 | 45 | const install_dir = owner.allocator.create(InstallDir) catch @panic("OOM"); |
| 46 | const graph = owner.graph; | |
| 45 | 47 | install_dir.* = .{ |
| 46 | 48 | .step = Step.init(.{ |
| 47 | 49 | .tag = base_tag, |
| 48 | 50 | .name = owner.fmt("install {s}/", .{options.source_dir.getDisplayName()}), |
| 49 | 51 | .owner = owner, |
| 50 | .makeFn = make, | |
| 51 | 52 | }), |
| 52 | .options = options.dupe(owner), | |
| 53 | .options = options.dupe(graph), | |
| 53 | 54 | }; |
| 54 | 55 | options.source_dir.addStepDependencies(&install_dir.step); |
| 55 | 56 | return install_dir; |
| 56 | 57 | } |
| 57 | ||
| 58 | fn make(step: *Step, options: Step.MakeOptions) !void { | |
| 59 | _ = options; | |
| 60 | const b = step.owner; | |
| 61 | const io = b.graph.io; | |
| 62 | const install_dir: *InstallDir = @fieldParentPtr("step", step); | |
| 63 | step.clearWatchInputs(); | |
| 64 | const arena = b.allocator; | |
| 65 | const dest_prefix = b.getInstallPath(install_dir.options.install_dir, install_dir.options.install_subdir); | |
| 66 | const src_dir_path = install_dir.options.source_dir.getPath3(b, step); | |
| 67 | const need_derived_inputs = try step.addDirectoryWatchInput(install_dir.options.source_dir); | |
| 68 | var src_dir = src_dir_path.root_dir.handle.openDir(io, src_dir_path.subPathOrDot(), .{ .iterate = true }) catch |err| { | |
| 69 | return step.fail("unable to open source directory '{f}': {t}", .{ src_dir_path, err }); | |
| 70 | }; | |
| 71 | defer src_dir.close(io); | |
| 72 | var it = try src_dir.walk(arena); | |
| 73 | var all_cached = true; | |
| 74 | next_entry: while (try it.next(io)) |entry| { | |
| 75 | for (install_dir.options.exclude_extensions) |ext| { | |
| 76 | if (mem.endsWith(u8, entry.path, ext)) continue :next_entry; | |
| 77 | } | |
| 78 | if (install_dir.options.include_extensions) |incs| { | |
| 79 | for (incs) |inc| { | |
| 80 | if (mem.endsWith(u8, entry.path, inc)) break; | |
| 81 | } else { | |
| 82 | continue :next_entry; | |
| 83 | } | |
| 84 | } | |
| 85 | ||
| 86 | const src_path = try install_dir.options.source_dir.join(b.allocator, entry.path); | |
| 87 | const dest_path = b.pathJoin(&.{ dest_prefix, entry.path }); | |
| 88 | switch (entry.kind) { | |
| 89 | .directory => { | |
| 90 | if (need_derived_inputs) _ = try step.addDirectoryWatchInput(src_path); | |
| 91 | const p = try step.installDir(dest_path); | |
| 92 | all_cached = all_cached and p == .existed; | |
| 93 | }, | |
| 94 | .file => { | |
| 95 | for (install_dir.options.blank_extensions) |ext| { | |
| 96 | if (mem.endsWith(u8, entry.path, ext)) { | |
| 97 | try b.truncateFile(dest_path); | |
| 98 | continue :next_entry; | |
| 99 | } | |
| 100 | } | |
| 101 | ||
| 102 | const p = try step.installFile(src_path, dest_path); | |
| 103 | all_cached = all_cached and p == .fresh; | |
| 104 | }, | |
| 105 | else => continue, | |
| 106 | } | |
| 107 | } | |
| 108 | ||
| 109 | step.result_cached = all_cached; | |
| 110 | } |
lib/std/Build/Step/InstallFile.zig+10-7| ... | ... | @@ -1,17 +1,18 @@ |
| 1 | const InstallFile = @This(); | |
| 2 | ||
| 1 | 3 | const std = @import("std"); |
| 2 | 4 | const Step = std.Build.Step; |
| 3 | 5 | const LazyPath = std.Build.LazyPath; |
| 4 | 6 | const InstallDir = std.Build.InstallDir; |
| 5 | const InstallFile = @This(); | |
| 6 | 7 | const assert = std.debug.assert; |
| 7 | 8 | |
| 8 | pub const base_tag: Step.Tag = .install_file; | |
| 9 | ||
| 10 | 9 | step: Step, |
| 11 | 10 | source: LazyPath, |
| 12 | 11 | dir: InstallDir, |
| 13 | 12 | dest_rel_path: []const u8, |
| 14 | 13 | |
| 14 | pub const base_tag: Step.Tag = .install_file; | |
| 15 | ||
| 15 | 16 | pub fn create( |
| 16 | 17 | owner: *std.Build, |
| 17 | 18 | source: LazyPath, |
| ... | ... | @@ -19,16 +20,18 @@ pub fn create( |
| 19 | 20 | dest_rel_path: []const u8, |
| 20 | 21 | ) *InstallFile { |
| 21 | 22 | assert(dest_rel_path.len != 0); |
| 22 | const install_file = owner.allocator.create(InstallFile) catch @panic("OOM"); | |
| 23 | const graph = owner.graph; | |
| 24 | const arena = graph.arena; | |
| 25 | const install_file = arena.create(InstallFile) catch @panic("OOM"); | |
| 23 | 26 | install_file.* = .{ |
| 24 | 27 | .step = Step.init(.{ |
| 25 | 28 | .tag = base_tag, |
| 26 | 29 | .name = owner.fmt("install {s} to {s}", .{ source.getDisplayName(), dest_rel_path }), |
| 27 | 30 | .owner = owner, |
| 28 | 31 | }), |
| 29 | .source = source.dupe(owner), | |
| 30 | .dir = dir.dupe(owner), | |
| 31 | .dest_rel_path = owner.dupePath(dest_rel_path), | |
| 32 | .source = source.dupe(graph), | |
| 33 | .dir = dir.dupe(graph), | |
| 34 | .dest_rel_path = graph.dupePath(dest_rel_path), | |
| 32 | 35 | }; |
| 33 | 36 | source.addStepDependencies(&install_file.step); |
| 34 | 37 | return install_file; |
lib/std/Build/Step/Options.zig+7-241| ... | ... | @@ -9,15 +9,19 @@ const Step = std.Build.Step; |
| 9 | 9 | const LazyPath = std.Build.LazyPath; |
| 10 | 10 | const Configuration = std.Build.Configuration; |
| 11 | 11 | |
| 12 | pub const base_tag: Step.Tag = .options; | |
| 13 | ||
| 14 | 12 | step: Step, |
| 15 | 13 | generated_file: Configuration.GeneratedFileIndex, |
| 16 | ||
| 17 | 14 | contents: std.ArrayList(u8), |
| 18 | 15 | args: std.ArrayList(Arg), |
| 19 | 16 | encountered_types: std.StringHashMapUnmanaged(void), |
| 20 | 17 | |
| 18 | pub const base_tag: Step.Tag = .options; | |
| 19 | ||
| 20 | pub const Arg = struct { | |
| 21 | name: []const u8, | |
| 22 | path: LazyPath, | |
| 23 | }; | |
| 24 | ||
| 21 | 25 | pub fn create(owner: *std.Build) *Options { |
| 22 | 26 | const graph = owner.graph; |
| 23 | 27 | const arena = graph.arena; |
| ... | ... | @@ -28,7 +32,6 @@ pub fn create(owner: *std.Build) *Options { |
| 28 | 32 | .tag = base_tag, |
| 29 | 33 | .name = "options", |
| 30 | 34 | .owner = owner, |
| 31 | .makeFn = make, | |
| 32 | 35 | }), |
| 33 | 36 | .generated_file = graph.addGeneratedFile(&options.step), |
| 34 | 37 | .contents = .empty, |
| ... | ... | @@ -439,240 +442,3 @@ pub fn createModule(options: *Options) *std.Build.Module { |
| 439 | 442 | pub fn getOutput(options: *Options) LazyPath { |
| 440 | 443 | return .{ .generated = .{ .index = options.generated_file } }; |
| 441 | 444 | } |
| 442 | ||
| 443 | fn make(step: *Step, make_options: Step.MakeOptions) !void { | |
| 444 | // This step completes so quickly that no progress reporting is necessary. | |
| 445 | _ = make_options; | |
| 446 | ||
| 447 | const b = step.owner; | |
| 448 | const io = b.graph.io; | |
| 449 | const options: *Options = @fieldParentPtr("step", step); | |
| 450 | ||
| 451 | for (options.args.items) |item| { | |
| 452 | options.addOption( | |
| 453 | []const u8, | |
| 454 | item.name, | |
| 455 | item.path.getPath2(b, step), | |
| 456 | ); | |
| 457 | } | |
| 458 | if (!step.inputs.populated()) for (options.args.items) |item| { | |
| 459 | try step.addWatchInput(item.path); | |
| 460 | }; | |
| 461 | ||
| 462 | const basename = "options.zig"; | |
| 463 | ||
| 464 | // Hash contents to file name. | |
| 465 | var hash = b.graph.cache.hash; | |
| 466 | // Random bytes to make unique. Refresh this with new random bytes when | |
| 467 | // implementation is modified in a non-backwards-compatible way. | |
| 468 | hash.add(@as(u32, 0xad95e922)); | |
| 469 | hash.addBytes(options.contents.items); | |
| 470 | const sub_path = "c" ++ fs.path.sep_str ++ hash.final() ++ fs.path.sep_str ++ basename; | |
| 471 | ||
| 472 | options.generated_file.path = try b.cache_root.join(b.allocator, &.{sub_path}); | |
| 473 | ||
| 474 | // Optimize for the hot path. Stat the file, and if it already exists, | |
| 475 | // cache hit. | |
| 476 | if (b.cache_root.handle.access(io, sub_path, .{})) |_| { | |
| 477 | // This is the hot path, success. | |
| 478 | step.result_cached = true; | |
| 479 | return; | |
| 480 | } else |outer_err| switch (outer_err) { | |
| 481 | error.FileNotFound => { | |
| 482 | var atomic_file = b.cache_root.handle.createFileAtomic(io, sub_path, .{ | |
| 483 | .replace = false, | |
| 484 | .make_path = true, | |
| 485 | }) catch |err| return step.fail("failed to create temporary path for '{f}{s}': {t}", .{ | |
| 486 | b.cache_root, sub_path, err, | |
| 487 | }); | |
| 488 | defer atomic_file.deinit(io); | |
| 489 | ||
| 490 | atomic_file.file.writeStreamingAll(io, options.contents.items) catch |err| { | |
| 491 | return step.fail("failed to write options to temporary path for '{f}{s}': {t}", .{ | |
| 492 | b.cache_root, sub_path, err, | |
| 493 | }); | |
| 494 | }; | |
| 495 | ||
| 496 | atomic_file.link(io) catch |err| switch (err) { | |
| 497 | error.PathAlreadyExists => { | |
| 498 | step.result_cached = true; | |
| 499 | return; | |
| 500 | }, | |
| 501 | else => return step.fail("failed to link temporary file into '{f}{s}': {t}", .{ | |
| 502 | b.cache_root, sub_path, err, | |
| 503 | }), | |
| 504 | }; | |
| 505 | }, | |
| 506 | else => |e| return step.fail("unable to access options file '{f}{s}': {t}", .{ | |
| 507 | b.cache_root, sub_path, e, | |
| 508 | }), | |
| 509 | } | |
| 510 | } | |
| 511 | ||
| 512 | const Arg = struct { | |
| 513 | name: []const u8, | |
| 514 | path: LazyPath, | |
| 515 | }; | |
| 516 | ||
| 517 | test Options { | |
| 518 | if (builtin.os.tag == .wasi) return error.SkipZigTest; | |
| 519 | ||
| 520 | const io = std.testing.io; | |
| 521 | ||
| 522 | var arena = std.heap.ArenaAllocator.init(std.testing.allocator); | |
| 523 | defer arena.deinit(); | |
| 524 | ||
| 525 | const cwd = try std.process.currentPathAlloc(io, std.testing.allocator); | |
| 526 | defer std.testing.allocator.free(cwd); | |
| 527 | ||
| 528 | var graph: std.Build.Graph = .{ | |
| 529 | .io = io, | |
| 530 | .arena = arena.allocator(), | |
| 531 | .cache = .{ | |
| 532 | .io = io, | |
| 533 | .gpa = arena.allocator(), | |
| 534 | .manifest_dir = Io.Dir.cwd(), | |
| 535 | .cwd = cwd, | |
| 536 | }, | |
| 537 | .zig_exe = "test", | |
| 538 | .environ_map = std.process.Environ.Map.init(arena.allocator()), | |
| 539 | .global_cache_root = .{ .path = "test", .handle = Io.Dir.cwd() }, | |
| 540 | .host = .{ | |
| 541 | .query = .{}, | |
| 542 | .result = try std.zig.system.resolveTargetQuery(io, .{}), | |
| 543 | }, | |
| 544 | .zig_lib_directory = std.Build.Cache.Directory.cwd(), | |
| 545 | .time_report = false, | |
| 546 | }; | |
| 547 | ||
| 548 | var builder = try std.Build.create( | |
| 549 | &graph, | |
| 550 | .{ .path = "test", .handle = Io.Dir.cwd() }, | |
| 551 | .{ .path = "test", .handle = Io.Dir.cwd() }, | |
| 552 | &.{}, | |
| 553 | ); | |
| 554 | ||
| 555 | const options = builder.addOptions(); | |
| 556 | ||
| 557 | const KeywordEnum = enum { | |
| 558 | @"0.8.1", | |
| 559 | }; | |
| 560 | ||
| 561 | const NormalEnum = enum { | |
| 562 | foo, | |
| 563 | bar, | |
| 564 | }; | |
| 565 | ||
| 566 | const nested_array = [2][2]u16{ | |
| 567 | [2]u16{ 300, 200 }, | |
| 568 | [2]u16{ 300, 200 }, | |
| 569 | }; | |
| 570 | const nested_slice: []const []const u16 = &[_][]const u16{ &nested_array[0], &nested_array[1] }; | |
| 571 | ||
| 572 | const NormalStruct = struct { | |
| 573 | hello: ?[]const u8, | |
| 574 | world: bool = true, | |
| 575 | }; | |
| 576 | ||
| 577 | const NestedStruct = struct { | |
| 578 | normal_struct: NormalStruct, | |
| 579 | normal_enum: NormalEnum = .foo, | |
| 580 | }; | |
| 581 | ||
| 582 | options.addOption(usize, "option1", 1); | |
| 583 | options.addOption(?usize, "option2", null); | |
| 584 | options.addOption(?usize, "option3", 3); | |
| 585 | options.addOption(comptime_int, "option4", 4); | |
| 586 | options.addOption(comptime_float, "option5", 5.01); | |
| 587 | options.addOption([]const u8, "string", "zigisthebest"); | |
| 588 | options.addOption(?[]const u8, "optional_string", null); | |
| 589 | options.addOption([2][2]u16, "nested_array", nested_array); | |
| 590 | options.addOption([]const []const u16, "nested_slice", nested_slice); | |
| 591 | options.addOption(KeywordEnum, "keyword_enum", .@"0.8.1"); | |
| 592 | options.addOption(std.SemanticVersion, "semantic_version", try std.SemanticVersion.parse("0.1.2-foo+bar")); | |
| 593 | options.addOption(NormalEnum, "normal1_enum", NormalEnum.foo); | |
| 594 | options.addOption(NormalEnum, "normal2_enum", NormalEnum.bar); | |
| 595 | options.addOption(NormalStruct, "normal1_struct", NormalStruct{ | |
| 596 | .hello = "foo", | |
| 597 | }); | |
| 598 | options.addOption(NormalStruct, "normal2_struct", NormalStruct{ | |
| 599 | .hello = null, | |
| 600 | .world = false, | |
| 601 | }); | |
| 602 | options.addOption(NestedStruct, "nested_struct", NestedStruct{ | |
| 603 | .normal_struct = .{ .hello = "bar" }, | |
| 604 | }); | |
| 605 | ||
| 606 | try std.testing.expectEqualStrings( | |
| 607 | \\pub const option1: usize = 1; | |
| 608 | \\pub const option2: ?usize = null; | |
| 609 | \\pub const option3: ?usize = 3; | |
| 610 | \\pub const option4: comptime_int = 4; | |
| 611 | \\pub const option5: comptime_float = 5.01; | |
| 612 | \\pub const string: []const u8 = "zigisthebest"; | |
| 613 | \\pub const optional_string: ?[]const u8 = null; | |
| 614 | \\pub const nested_array: [2][2]u16 = [2][2]u16 { | |
| 615 | \\ [2]u16 { | |
| 616 | \\ 300, | |
| 617 | \\ 200, | |
| 618 | \\ }, | |
| 619 | \\ [2]u16 { | |
| 620 | \\ 300, | |
| 621 | \\ 200, | |
| 622 | \\ }, | |
| 623 | \\}; | |
| 624 | \\pub const nested_slice: []const []const u16 = &[_][]const u16 { | |
| 625 | \\ &[_]u16 { | |
| 626 | \\ 300, | |
| 627 | \\ 200, | |
| 628 | \\ }, | |
| 629 | \\ &[_]u16 { | |
| 630 | \\ 300, | |
| 631 | \\ 200, | |
| 632 | \\ }, | |
| 633 | \\}; | |
| 634 | \\pub const @"Build.Step.Options.decltest.Options.KeywordEnum" = enum (u0) { | |
| 635 | \\ @"0.8.1" = 0, | |
| 636 | \\}; | |
| 637 | \\pub const keyword_enum: @"Build.Step.Options.decltest.Options.KeywordEnum" = .@"0.8.1"; | |
| 638 | \\pub const semantic_version: @import("std").SemanticVersion = .{ | |
| 639 | \\ .major = 0, | |
| 640 | \\ .minor = 1, | |
| 641 | \\ .patch = 2, | |
| 642 | \\ .pre = "foo", | |
| 643 | \\ .build = "bar", | |
| 644 | \\}; | |
| 645 | \\pub const @"Build.Step.Options.decltest.Options.NormalEnum" = enum (u1) { | |
| 646 | \\ foo = 0, | |
| 647 | \\ bar = 1, | |
| 648 | \\}; | |
| 649 | \\pub const normal1_enum: @"Build.Step.Options.decltest.Options.NormalEnum" = .foo; | |
| 650 | \\pub const normal2_enum: @"Build.Step.Options.decltest.Options.NormalEnum" = .bar; | |
| 651 | \\pub const @"Build.Step.Options.decltest.Options.NormalStruct" = struct { | |
| 652 | \\ hello: ?[]const u8, | |
| 653 | \\ world: bool = true, | |
| 654 | \\}; | |
| 655 | \\pub const normal1_struct: @"Build.Step.Options.decltest.Options.NormalStruct" = .{ | |
| 656 | \\ .hello = "foo", | |
| 657 | \\ .world = true, | |
| 658 | \\}; | |
| 659 | \\pub const normal2_struct: @"Build.Step.Options.decltest.Options.NormalStruct" = .{ | |
| 660 | \\ .hello = null, | |
| 661 | \\ .world = false, | |
| 662 | \\}; | |
| 663 | \\pub const @"Build.Step.Options.decltest.Options.NestedStruct" = struct { | |
| 664 | \\ normal_struct: @"Build.Step.Options.decltest.Options.NormalStruct", | |
| 665 | \\ normal_enum: @"Build.Step.Options.decltest.Options.NormalEnum" = .foo, | |
| 666 | \\}; | |
| 667 | \\pub const nested_struct: @"Build.Step.Options.decltest.Options.NestedStruct" = .{ | |
| 668 | \\ .normal_struct = .{ | |
| 669 | \\ .hello = "bar", | |
| 670 | \\ .world = true, | |
| 671 | \\ }, | |
| 672 | \\ .normal_enum = .foo, | |
| 673 | \\}; | |
| 674 | \\ | |
| 675 | , options.contents.items); | |
| 676 | ||
| 677 | _ = try std.zig.Ast.parse(arena.allocator(), try options.contents.toOwnedSliceSentinel(arena.allocator(), 0), .zig); | |
| 678 | } |