| 1 | const Options = @This(); |
| 2 | |
| 3 | const std = @import("std"); |
| 4 | const Io = std.Io; |
| 5 | const Configuration = std.Build.Configuration; |
| 6 | const Cache = std.Build.Cache; |
| 7 | |
| 8 | const Step = @import("../Step.zig"); |
| 9 | const Maker = @import("../../Maker.zig"); |
| 10 | |
| 11 | pub fn make( |
| 12 | options: *Options, |
| 13 | step_index: Configuration.Step.Index, |
| 14 | maker: *Maker, |
| 15 | progress_node: std.Progress.Node, |
| 16 | ) Step.ExtendedMakeError!void { |
| 17 | _ = options; |
| 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 | const arena = graph.arena; // TODO don't leak into the process arena |
| 24 | const conf = &maker.scanned_config.configuration; |
| 25 | const conf_step = step_index.ptr(conf); |
| 26 | const conf_options = conf_step.extended.get(conf.extra).options; |
| 27 | const contents = conf_options.contents.slice(conf); |
| 28 | |
| 29 | // This step operates under the assumption that all contents of the |
| 30 | // generated zig file are observable by dependant steps, as well as the |
| 31 | // contents of files added via Options.Arg. |
| 32 | |
| 33 | step.clearWatchInputs(maker); |
| 34 | |
| 35 | var man = graph.cache.obtain(); |
| 36 | defer man.deinit(); |
| 37 | |
| 38 | var args_bytes: std.ArrayList(u8) = .empty; |
| 39 | |
| 40 | for (conf_options.args.slice) |arg| { |
| 41 | const name = arg.name.slice(conf); |
| 42 | const lazy_path = arg.path.get(conf); |
| 43 | try step.addWatchInput(maker, arena, lazy_path); |
| 44 | const arg_path = try maker.resolveLazyPath(arena, lazy_path, step_index); |
| 45 | _ = try man.addFilePath(arg_path, null); |
| 46 | try args_bytes.print(arena, "pub const {f}: []const u8 = \"{f}\";\n", .{ |
| 47 | std.zig.fmtId(name), arg_path.fmtEscapeString(), |
| 48 | }); |
| 49 | } |
| 50 | |
| 51 | man.hash.addBytes(contents); |
| 52 | man.hash.addBytes(args_bytes.items); |
| 53 | |
| 54 | const basename = "options.zig"; |
| 55 | |
| 56 | if (try step.cacheHitWatched(maker, &man, progress_node)) { |
| 57 | const digest = man.final(); |
| 58 | maker.generatedPath(conf_options.generated_file).* = .{ |
| 59 | .root_dir = cache_root, |
| 60 | .sub_path = try Io.Dir.path.join(arena, &.{ "o", &digest, basename }), |
| 61 | }; |
| 62 | step.result_cached = true; |
| 63 | return; |
| 64 | } |
| 65 | |
| 66 | const digest = man.final(); |
| 67 | const out_path: Cache.Path = .{ |
| 68 | .root_dir = cache_root, |
| 69 | .sub_path = try Io.Dir.path.join(arena, &.{ "o", &digest, basename }), |
| 70 | }; |
| 71 | |
| 72 | var file: Io.File = out_path.root_dir.handle.createFile(io, out_path.sub_path, .{}) catch |err| switch (err) { |
| 73 | error.Canceled => |e| return e, |
| 74 | error.FileNotFound => f: { |
| 75 | out_path.root_dir.handle.createDirPath(io, Io.Dir.path.dirname(out_path.sub_path).?) catch |inner| switch (inner) { |
| 76 | error.Canceled => |e| return e, |
| 77 | else => |e| return step.fail(maker, "failed to create {f}: {t}", .{ out_path, e }), |
| 78 | }; |
| 79 | break :f out_path.root_dir.handle.createFile(io, out_path.sub_path, .{}) catch |inner| switch (inner) { |
| 80 | error.Canceled => |e| return e, |
| 81 | else => |e| return step.fail(maker, "failed to create {f}: {t}", .{ out_path, e }), |
| 82 | }; |
| 83 | }, |
| 84 | else => |e| return step.fail(maker, "failed to create {f}: {t}", .{ out_path, e }), |
| 85 | }; |
| 86 | defer file.close(io); |
| 87 | |
| 88 | // No buffer because we already have all contents buffered. |
| 89 | var file_writer = file.writer(io, &.{}); |
| 90 | var data: [2][]const u8 = .{ contents, args_bytes.items }; |
| 91 | file_writer.interface.writeVecAll(&data) catch |write_err| switch (write_err) { |
| 92 | error.WriteFailed => switch (file_writer.err.?) { |
| 93 | error.Canceled => |e| return e, |
| 94 | else => |e| return step.fail(maker, "failed to write to {f}: {t}", .{ out_path, e }), |
| 95 | }, |
| 96 | }; |
| 97 | |
| 98 | try step.writeManifestAndWatch(maker, &man); |
| 99 | |
| 100 | maker.generatedPath(conf_options.generated_file).* = out_path; |
| 101 | } |