1const builtin = @import("builtin");
2
3const std = @import("std");
4const Allocator = std.mem.Allocator;
5const Color = std.zig.Color;
6const Configuration = std.Build.Configuration;
7const Io = std.Io;
8const Step = std.Build.Step;
9const assert = std.debug.assert;
10const fatal = std.process.fatal;
11const log = std.log;
12const mem = std.mem;
13const process = std.process;
14const Serialize = std.Build.Serialize;
15
16pub const root = @import("@build");
17pub const dependencies = @import("@dependencies");
18
19pub const std_options: std.Options = .{
20 .side_channels_mitigations = .none,
21 .http_disable_tls = true,
22};
23
24pub 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
167fn 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
173fn 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
179fn 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
186fn fatalWithHint(comptime f: []const u8, args: anytype) noreturn {
187 log.info("to access the help menu: zig build -h", .{});
188 fatal(f, args);
189}