1target: std.Target,
2zig_backend: std.lang.CompilerBackend,
3output_mode: std.lang.OutputMode,
4link_mode: std.lang.LinkMode,
5unwind_tables: std.lang.UnwindTables,
6is_test: bool,
7single_threaded: bool,
8link_libc: bool,
9link_libcpp: bool,
10optimize_mode: std.lang.Optimize,
11error_tracing: bool,
12valgrind: bool,
13sanitize_thread: bool,
14fuzz: bool,
15pic: bool,
16pie: bool,
17strip: bool,
18code_model: std.lang.CodeModel,
19omit_frame_pointer: bool,
20wasi_exec_model: std.lang.WasiExecModel,
21
22/// Compute an abstract hash representing this `Builtin`. This is *not* a hash
23/// of the resulting file contents.
24pub fn hash(opts: @This()) [std.Build.Cache.bin_digest_len]u8 {
25 var h: Cache.Hasher = Cache.hasher_init;
26 inline for (@typeInfo(@This()).@"struct".field_names) |f_name| {
27 if (comptime std.mem.eql(u8, f_name, "target")) {
28 // This needs special handling.
29 std.hash.autoHash(&h, opts.target.cpu);
30 std.hash.autoHash(&h, opts.target.os.tag);
31 std.hash.autoHash(&h, opts.target.os.versionRange());
32 std.hash.autoHash(&h, opts.target.abi);
33 std.hash.autoHash(&h, opts.target.ofmt);
34 std.hash.autoHash(&h, opts.target.dynamic_linker);
35 } else {
36 std.hash.autoHash(&h, @field(opts, f_name));
37 }
38 }
39 return h.finalResult();
40}
41
42pub fn generate(opts: @This(), allocator: Allocator) Allocator.Error![:0]u8 {
43 var buffer = std.array_list.Managed(u8).init(allocator);
44 try append(opts, &buffer);
45 return buffer.toOwnedSliceSentinel(0);
46}
47
48pub fn append(opts: @This(), buffer: *std.array_list.Managed(u8)) Allocator.Error!void {
49 const target = opts.target;
50 const arch_family_name = @tagName(target.cpu.arch.family());
51 const zig_backend = opts.zig_backend;
52
53 @setEvalBranchQuota(4000);
54 try buffer.print(
55 \\const std = @import("std");
56 \\pub const zig_version = std.SemanticVersion.parse(zig_version_string) catch unreachable;
57 \\pub const zig_version_string = "{s}";
58 \\pub const zig_backend = std.lang.CompilerBackend.{f};
59 \\
60 \\pub const output_mode: std.lang.OutputMode = .{f};
61 \\pub const link_mode: std.lang.LinkMode = .{f};
62 \\pub const unwind_tables: std.lang.UnwindTables = .{f};
63 \\pub const is_test = {};
64 \\pub const single_threaded = {};
65 \\/// Deprecated; to be removed in 0.18.0. Use `target.abi` instead.
66 \\pub const abi: std.Target.Abi = .{f};
67 \\/// Deprecated; to be removed in 0.18.0. Use `target.cpu` instead.
68 \\pub const cpu: std.Target.Cpu = .{{
69 \\ .arch = .{f},
70 \\ .model = &std.Target.{f}.cpu.{f},
71 \\ .features = std.Target.{f}.featureSet(&.{{
72 \\
73 , .{
74 build_options.version,
75 std.zig.fmtIdPU(@tagName(zig_backend)),
76 std.zig.fmtIdPU(@tagName(opts.output_mode)),
77 std.zig.fmtIdPU(@tagName(opts.link_mode)),
78 std.zig.fmtIdPU(@tagName(opts.unwind_tables)),
79 opts.is_test,
80 opts.single_threaded,
81 std.zig.fmtIdPU(@tagName(target.abi)),
82 std.zig.fmtIdPU(@tagName(target.cpu.arch)),
83 std.zig.fmtIdPU(arch_family_name),
84 std.zig.fmtIdPU(target.cpu.model.name),
85 std.zig.fmtIdPU(arch_family_name),
86 });
87
88 for (target.cpu.arch.allFeaturesList(), 0..) |feature, index_usize| {
89 const index = @as(std.Target.Cpu.Feature.Set.Index, @intCast(index_usize));
90 const is_enabled = target.cpu.features.isEnabled(index);
91 if (is_enabled) {
92 try buffer.print(" .{f},\n", .{std.zig.fmtIdPU(feature.name)});
93 }
94 }
95 try buffer.print(
96 \\ }}),
97 \\}};
98 \\/// Deprecated; to be removed in 0.18.0. Use `target.os` instead.
99 \\pub const os: std.Target.Os = .{{
100 \\ .tag = .{f},
101 \\ .version_range = .{{
102 ,
103 .{std.zig.fmtIdPU(@tagName(target.os.tag))},
104 );
105
106 switch (target.os.versionRange()) {
107 .none => try buffer.appendSlice(" .none = {} },\n"),
108 .semver => |semver| try buffer.print(
109 \\ .semver = .{{
110 \\ .min = .{{
111 \\ .major = {},
112 \\ .minor = {},
113 \\ .patch = {},
114 \\ }},
115 \\ .max = .{{
116 \\ .major = {},
117 \\ .minor = {},
118 \\ .patch = {},
119 \\ }},
120 \\ }}}},
121 \\
122 , .{
123 semver.min.major,
124 semver.min.minor,
125 semver.min.patch,
126
127 semver.max.major,
128 semver.max.minor,
129 semver.max.patch,
130 }),
131 .linux => |linux| try buffer.print(
132 \\ .linux = .{{
133 \\ .range = .{{
134 \\ .min = .{{
135 \\ .major = {},
136 \\ .minor = {},
137 \\ .patch = {},
138 \\ }},
139 \\ .max = .{{
140 \\ .major = {},
141 \\ .minor = {},
142 \\ .patch = {},
143 \\ }},
144 \\ }},
145 \\ .glibc = .{{
146 \\ .major = {},
147 \\ .minor = {},
148 \\ .patch = {},
149 \\ }},
150 \\ .android = {},
151 \\ }}}},
152 \\
153 , .{
154 linux.range.min.major,
155 linux.range.min.minor,
156 linux.range.min.patch,
157
158 linux.range.max.major,
159 linux.range.max.minor,
160 linux.range.max.patch,
161
162 linux.glibc.major,
163 linux.glibc.minor,
164 linux.glibc.patch,
165
166 linux.android,
167 }),
168 .hurd => |hurd| try buffer.print(
169 \\ .hurd = .{{
170 \\ .range = .{{
171 \\ .min = .{{
172 \\ .major = {},
173 \\ .minor = {},
174 \\ .patch = {},
175 \\ }},
176 \\ .max = .{{
177 \\ .major = {},
178 \\ .minor = {},
179 \\ .patch = {},
180 \\ }},
181 \\ }},
182 \\ .glibc = .{{
183 \\ .major = {},
184 \\ .minor = {},
185 \\ .patch = {},
186 \\ }},
187 \\ }}}},
188 \\
189 , .{
190 hurd.range.min.major,
191 hurd.range.min.minor,
192 hurd.range.min.patch,
193
194 hurd.range.max.major,
195 hurd.range.max.minor,
196 hurd.range.max.patch,
197
198 hurd.glibc.major,
199 hurd.glibc.minor,
200 hurd.glibc.patch,
201 }),
202 .windows => |windows| try buffer.print(
203 \\ .windows = .{{
204 \\ .min = {f},
205 \\ .max = {f},
206 \\ }}}},
207 \\
208 , .{ windows.min, windows.max }),
209 }
210 try buffer.appendSlice(
211 \\};
212 \\pub const target: std.Target = .{
213 \\ .cpu = cpu,
214 \\ .os = os,
215 \\ .abi = abi,
216 \\ .ofmt = object_format,
217 \\
218 );
219
220 if (target.dynamic_linker.get()) |dl| {
221 try buffer.print(
222 \\ .dynamic_linker = .init("{s}"),
223 \\}};
224 \\
225 , .{dl});
226 } else {
227 try buffer.appendSlice(
228 \\ .dynamic_linker = .none,
229 \\};
230 \\
231 );
232 }
233
234 // This is so that compiler_rt and libc.zig libraries know whether they
235 // will eventually be linked with libc. They make different decisions
236 // about what to export depending on whether another libc will be linked
237 // in. For example, compiler_rt will not export the __chkstk symbol if it
238 // knows libc will provide it, and likewise c.zig will not export memcpy.
239 const link_libc = opts.link_libc;
240
241 try buffer.print(
242 \\/// Deprecated; to be removed in 0.18.0. Use `target.ofmt` instead.
243 \\pub const object_format: std.Target.ObjectFormat = .{f};
244 \\/// Deprecated, to be removed after 0.18.0
245 \\pub const mode = optimize;
246 \\pub const optimize: std.lang.Optimize = .{f};
247 \\pub const link_libc = {};
248 \\pub const link_libcpp = {};
249 \\pub const have_error_return_tracing = {};
250 \\pub const valgrind_support = {};
251 \\pub const sanitize_thread = {};
252 \\pub const fuzz = {};
253 \\pub const position_independent_code = {};
254 \\pub const position_independent_executable = {};
255 \\pub const strip_debug_info = {};
256 \\pub const code_model: std.lang.CodeModel = .{f};
257 \\pub const omit_frame_pointer = {};
258 \\
259 , .{
260 std.zig.fmtIdPU(@tagName(target.ofmt)),
261 std.zig.fmtIdPU(@tagName(opts.optimize_mode)),
262 link_libc,
263 opts.link_libcpp,
264 opts.error_tracing,
265 opts.valgrind,
266 opts.sanitize_thread,
267 opts.fuzz,
268 opts.pic,
269 opts.pie,
270 opts.strip,
271 std.zig.fmtIdPU(@tagName(opts.code_model)),
272 opts.omit_frame_pointer,
273 });
274
275 if (target.os.tag == .wasi) {
276 try buffer.print(
277 \\pub const wasi_exec_model: std.lang.WasiExecModel = .{f};
278 \\
279 , .{std.zig.fmtIdPU(@tagName(opts.wasi_exec_model))});
280 }
281
282 if (opts.is_test) {
283 try buffer.appendSlice(
284 \\pub var test_functions: []const std.lang.TestFn = &.{}; // overwritten later
285 \\
286 );
287 }
288}
289
290/// This essentially takes the place of `Zcu.PerThread.updateFile`, but for 'builtin' modules.
291/// Instead of reading the file from disk, its contents are generated in-memory.
292pub fn populateFile(opts: @This(), gpa: Allocator, file: *File) Allocator.Error!void {
293 assert(file.is_builtin);
294 assert(file.status == .never_loaded);
295 assert(file.source == null);
296 assert(file.tree == null);
297 assert(file.zir == null);
298
299 file.source = try opts.generate(gpa);
300
301 log.debug("parsing and generating 'builtin.zig'", .{});
302
303 file.tree = try std.zig.Ast.parse(gpa, file.source.?, .{});
304 assert(file.tree.?.errors.len == 0); // builtin.zig must parse
305
306 file.zir = try AstGen.generate(gpa, file.tree.?);
307 assert(!file.zir.?.hasCompileErrors()); // builtin.zig must not have astgen errors
308 file.status = .success;
309}
310
311/// After `populateFile` succeeds, call this function to write the generated file out to disk
312/// if necessary. This is useful for external tooling such as debuggers.
313/// Assumes that `file.mod` is correctly set to the builtin module.
314pub fn updateFileOnDisk(file: *File, comp: *Compilation) !void {
315 assert(file.is_builtin);
316 assert(file.status == .success);
317 assert(file.source != null);
318
319 const root_dir, const sub_path = file.path.openInfo(comp.dirs);
320 const io = comp.io;
321
322 if (root_dir.statFile(io, sub_path, .{})) |stat| {
323 if (stat.size != file.source.?.len) {
324 std.log.warn(
325 "the cached file '{f}' had the wrong size. Expected {d}, found {d}. " ++
326 "Overwriting with correct file contents now",
327 .{ file.path.fmt(comp), file.source.?.len, stat.size },
328 );
329 } else {
330 file.stat = .{
331 .size = stat.size,
332 .inode = stat.inode,
333 .mtime = stat.mtime,
334 };
335 return;
336 }
337 } else |err| switch (err) {
338 error.FileNotFound => {},
339
340 error.WouldBlock => unreachable, // not asking for non-blocking I/O
341 error.BadPathName => unreachable, // it's always "o/digest/builtin.zig"
342 error.NameTooLong => unreachable, // it's always "o/digest/builtin.zig"
343
344 // We don't expect the file to be a pipe, but can't mark `error.PipeBusy` as `unreachable`,
345 // because the user could always replace the file on disk.
346 else => |e| return e,
347 }
348
349 // `make_path` matters because the dir hasn't actually been created yet.
350 var af = try root_dir.createFileAtomic(io, sub_path, .{ .make_path = true, .replace = true });
351 defer af.deinit(io);
352 try af.file.writeStreamingAll(io, file.source.?);
353 af.replace(io) catch |err| switch (err) {
354 error.AccessDenied => switch (builtin.os.tag) {
355 .windows => {
356 // Very likely happened due to another process or thread
357 // simultaneously creating the same, correct builtin.zig file.
358 // This is not a problem; ignore it.
359 },
360 else => return err,
361 },
362 else => return err,
363 };
364
365 file.stat = .{
366 .size = file.source.?.len,
367 .inode = 0, // dummy value
368 .mtime = .zero, // dummy value
369 };
370}
371
372const builtin = @import("builtin");
373const std = @import("std");
374const Allocator = std.mem.Allocator;
375const Cache = std.Build.Cache;
376const build_options = @import("build_options");
377const Module = @import("Module.zig");
378const assert = std.debug.assert;
379const AstGen = std.zig.AstGen;
380const File = @import("Zcu.zig").File;
381const Compilation = @import("Compilation.zig");
382const log = std.log.scoped(.builtin);