| 1 | const builtin = @import("builtin"); |
| 2 | |
| 3 | const std = @import("std"); |
| 4 | const Allocator = std.mem.Allocator; |
| 5 | const Color = std.zig.Color; |
| 6 | const Configuration = std.Build.Configuration; |
| 7 | const Io = std.Io; |
| 8 | const Step = std.Build.Step; |
| 9 | const assert = std.debug.assert; |
| 10 | const fatal = std.process.fatal; |
| 11 | const log = std.log; |
| 12 | const mem = std.mem; |
| 13 | const process = std.process; |
| 14 | const Serialize = std.Build.Serialize; |
| 15 | |
| 16 | pub const root = @import("@build"); |
| 17 | pub const dependencies = @import("@dependencies"); |
| 18 | |
| 19 | pub const std_options: std.Options = .{ |
| 20 | .side_channels_mitigations = .none, |
| 21 | .http_disable_tls = true, |
| 22 | }; |
| 23 | |
| 24 | pub fn main(init: process.Init.Minimal) !void { |
| 25 | var arena_allocator: std.heap.ArenaAllocator = .init(std.heap.page_allocator); |
| 26 | defer arena_allocator.deinit(); |
| 27 | const arena = arena_allocator.allocator(); |
| 28 | |
| 29 | // The configurer is always short-lived because all it does is serialize |
| 30 | // the configuration, which is picked up by a separate maker process. |
| 31 | var threaded: std.Io.Threaded = .init(arena, .{ |
| 32 | .environ = init.environ, |
| 33 | .argv0 = .init(init.args), |
| 34 | }); |
| 35 | defer threaded.deinit(); |
| 36 | const io = threaded.io(); |
| 37 | |
| 38 | const args = try init.args.toSlice(arena); |
| 39 | |
| 40 | var arg_i: usize = 1; // Skip own executable name. |
| 41 | |
| 42 | const zig_exe = expectArgOrFatal(args, &arg_i, "--zig"); |
| 43 | const build_root_sub_path = expectArgOrFatal(args, &arg_i, "--build-root"); |
| 44 | |
| 45 | var graph: std.Build.Graph = .{ |
| 46 | .io = io, |
| 47 | .arena = arena, |
| 48 | .environ_map = try init.environ.createMap(arena), |
| 49 | // TODO get this from parent process instead |
| 50 | .host = .{ |
| 51 | .query = .{}, |
| 52 | .result = try std.zig.system.resolveTargetQuery(io, .{}), |
| 53 | }, |
| 54 | .generated_files = .empty, |
| 55 | .zig_exe = zig_exe, |
| 56 | |
| 57 | // Created before running the user's configure script so that some things |
| 58 | // can be added during script execution such as strings. |
| 59 | // |
| 60 | // Use of arena here is load-bearing because `std.Build.dupe` is |
| 61 | // implemented by string internment, and then returning the interned |
| 62 | // slice. When the string bytes array is reallocated, that reference |
| 63 | // must stay alive. |
| 64 | .wip_configuration = .init(arena), |
| 65 | }; |
| 66 | assert(try graph.wip_configuration.addString("") == .empty); |
| 67 | assert(try graph.wip_configuration.addString("root") == .root); |
| 68 | |
| 69 | const cwd: Io.Dir = .cwd(); |
| 70 | |
| 71 | const build_root: std.Build.Cache.Path = if (std.mem.eql(u8, build_root_sub_path, ".")) .cwd() else .{ |
| 72 | .root_dir = .{ |
| 73 | .handle = try cwd.openDir(io, build_root_sub_path, .{}), |
| 74 | .path = build_root_sub_path, |
| 75 | }, |
| 76 | }; |
| 77 | |
| 78 | const builder = try std.Build.create(&graph, build_root, dependencies.root_deps); |
| 79 | |
| 80 | var color: Color = .auto; |
| 81 | |
| 82 | while (nextArg(args, &arg_i)) |arg| { |
| 83 | if (mem.cutPrefix(u8, arg, "-D")) |option_contents| { |
| 84 | if (option_contents.len == 0) |
| 85 | fatalWithHint("expected option name after '-D'", .{}); |
| 86 | if (mem.findScalar(u8, option_contents, '=')) |name_end| { |
| 87 | const option_name = option_contents[0..name_end]; |
| 88 | const option_value = option_contents[name_end + 1 ..]; |
| 89 | if (try builder.addUserInputOption(option_name, option_value)) { |
| 90 | log.info("to access the help menu: zig build -h", .{}); |
| 91 | process.exit(1); |
| 92 | } |
| 93 | } else { |
| 94 | if (try builder.addUserInputFlag(option_contents)) { |
| 95 | log.info("to access the help menu: zig build -h", .{}); |
| 96 | process.exit(1); |
| 97 | } |
| 98 | } |
| 99 | } else if (mem.cutPrefix(u8, arg, "-fsys=")) |name| { |
| 100 | try graph.system_integration_options.put(arena, name, .user_enabled); |
| 101 | } else if (mem.cutPrefix(u8, arg, "-fno-sys=")) |name| { |
| 102 | try graph.system_integration_options.put(arena, name, .user_disabled); |
| 103 | } else if (mem.eql(u8, arg, "--release")) { |
| 104 | graph.release_mode = .any; |
| 105 | } else if (mem.cutPrefix(u8, arg, "--release=")) |rest| { |
| 106 | graph.release_mode = std.meta.stringToEnum(std.Build.ReleaseMode, rest) orelse { |
| 107 | fatalWithHint("expected --release=[off|any|fast|safe|small]; found: {s}", .{arg}); |
| 108 | }; |
| 109 | } else if (mem.cutPrefix(u8, arg, "--color=")) |rest| { |
| 110 | color = std.meta.stringToEnum(Color, rest) orelse |
| 111 | fatalWithHint("expected --color=[auto|on|off]; found: {s}", .{arg}); |
| 112 | } else if (mem.eql(u8, arg, "--system")) { |
| 113 | // The usage text shows another argument after this parameter |
| 114 | // but it is handled by the parent process. The build runner |
| 115 | // only sees this flag. |
| 116 | graph.system_package_mode = true; |
| 117 | } else if (mem.eql(u8, arg, "--verbose")) { |
| 118 | graph.verbose = true; |
| 119 | } else if (mem.cutPrefix(u8, arg, "--cache-poison=")) |rest| { |
| 120 | // Already parsed and validated by Maker. |
| 121 | graph.cache_poison = std.meta.stringToEnum(std.Build.Graph.CachePoison, rest).?; |
| 122 | } else if (mem.eql(u8, arg, "--search-prefix")) { |
| 123 | try graph.search_prefixes.append(arena, nextArgOrFatal(args, &arg_i)); |
| 124 | } else { |
| 125 | fatalWithHint("unrecognized argument: {s}", .{arg}); |
| 126 | } |
| 127 | } |
| 128 | |
| 129 | const NO_COLOR = std.zig.EnvVar.NO_COLOR.isSet(&graph.environ_map); |
| 130 | const CLICOLOR_FORCE = std.zig.EnvVar.CLICOLOR_FORCE.isSet(&graph.environ_map); |
| 131 | |
| 132 | graph.stderr_mode = switch (color) { |
| 133 | .auto => try .detect(io, .stderr(), NO_COLOR, CLICOLOR_FORCE), |
| 134 | .on => .escape_codes, |
| 135 | .off => .no_color, |
| 136 | }; |
| 137 | |
| 138 | builder.runPackageScript(root); |
| 139 | |
| 140 | // Even though the root package's user input options are not serialized, |
| 141 | // this is done for consistency, since the rest of the dependency tree |
| 142 | // sorts user_input_options before calling validateUserInputDidItFail, |
| 143 | // which has user-visible behavior (the order of errors reported). |
| 144 | std.Build.PackageOptions.sort(&builder.user_input_options); |
| 145 | |
| 146 | // Make sure the package actually provides all the arguments specified. |
| 147 | for (builder.user_input_options.keys()) |name| { |
| 148 | if (!builder.available_options_map.contains(name)) { |
| 149 | log.err("invalid option: {q}", .{name}); |
| 150 | builder.invalid_user_input = true; |
| 151 | } |
| 152 | } |
| 153 | if (builder.invalid_user_input) { |
| 154 | for (builder.available_options_map.keys(), builder.available_options_map.values()) |name, *available| { |
| 155 | log.info("available option: {q}: {s}", .{ name, available.description }); |
| 156 | } |
| 157 | log.info("to access the help menu: zig build -h", .{}); |
| 158 | process.exit(1); |
| 159 | } |
| 160 | |
| 161 | try Serialize.packageOptions(builder, &graph.wip_configuration); |
| 162 | try Serialize.systemIntegrationOptions(&graph, &graph.wip_configuration); |
| 163 | |
| 164 | builder.serializeConfigurationExiting(); |
| 165 | } |
| 166 | |
| 167 | fn nextArg(args: []const [:0]const u8, idx: *usize) ?[:0]const u8 { |
| 168 | if (idx.* >= args.len) return null; |
| 169 | defer idx.* += 1; |
| 170 | return args[idx.*]; |
| 171 | } |
| 172 | |
| 173 | fn nextArgOrFatal(args: []const [:0]const u8, idx: *usize) [:0]const u8 { |
| 174 | return nextArg(args, idx) orelse { |
| 175 | fatalWithHint("expected argument after {q}", .{args[idx.* - 1]}); |
| 176 | }; |
| 177 | } |
| 178 | |
| 179 | fn expectArgOrFatal(args: []const [:0]const u8, index_ptr: *usize, first: []const u8) []const u8 { |
| 180 | const next_arg = nextArg(args, index_ptr) orelse fatal("missing {q} argument", .{first}); |
| 181 | if (!mem.eql(u8, first, next_arg)) fatal("expected {q} instead of {q}", .{ first, next_arg }); |
| 182 | const arg = nextArg(args, index_ptr) orelse fatal("expected argument after {q}", .{first}); |
| 183 | return arg; |
| 184 | } |
| 185 | |
| 186 | fn fatalWithHint(comptime f: []const u8, args: anytype) noreturn { |
| 187 | log.info("to access the help menu: zig build -h", .{}); |
| 188 | fatal(f, args); |
| 189 | } |