master
1target: std.Target,
2zig_backend: std.builtin.CompilerBackend,
3output_mode: std.builtin.OutputMode,
4link_mode: std.builtin.LinkMode,
5unwind_tables: std.builtin.UnwindTables,
6is_test: bool,
7single_threaded: bool,
8link_libc: bool,
9link_libcpp: bool,
10optimize_mode: std.builtin.OptimizeMode,
11error_tracing: bool,
12valgrind: bool,
13sanitize_thread: bool,
14fuzz: bool,
15pic: bool,
16pie: bool,
17strip: bool,
18code_model: std.builtin.CodeModel,
19omit_frame_pointer: bool,
20wasi_exec_model: std.builtin.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".fields) |f| {
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 \\/// Zig version. When writing code that supports multiple versions of Zig, prefer
57 \\/// feature detection (i.e. with `@hasDecl` or `@hasField`) over version checks.
58 \\pub const zig_version = std.SemanticVersion.parse(zig_version_string) catch unreachable;
59 \\pub const zig_version_string = "{s}";
60 \\pub const zig_backend = std.builtin.CompilerBackend.{f};
61 \\
62 \\pub const output_mode: std.builtin.OutputMode = .{f};
63 \\pub const link_mode: std.builtin.LinkMode = .{f};
64 \\pub const unwind_tables: std.builtin.UnwindTables = .{f};
65 \\pub const is_test = {};
66 \\pub const single_threaded = {};
67 \\pub const abi: std.Target.Abi = .{f};
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 \\pub const os: std.Target.Os = .{{
99 \\ .tag = .{f},
100 \\ .version_range = .{{
101 ,
102 .{std.zig.fmtIdPU(@tagName(target.os.tag))},
103 );
104
105 switch (target.os.versionRange()) {
106 .none => try buffer.appendSlice(" .none = {} },\n"),
107 .semver => |semver| try buffer.print(
108 \\ .semver = .{{
109 \\ .min = .{{
110 \\ .major = {},
111 \\ .minor = {},
112 \\ .patch = {},
113 \\ }},
114 \\ .max = .{{
115 \\ .major = {},
116 \\ .minor = {},
117 \\ .patch = {},
118 \\ }},
119 \\ }}}},
120 \\
121 , .{
122 semver.min.major,
123 semver.min.minor,
124 semver.min.patch,
125
126 semver.max.major,
127 semver.max.minor,
128 semver.max.patch,
129 }),
130 .linux => |linux| try buffer.print(
131 \\ .linux = .{{
132 \\ .range = .{{
133 \\ .min = .{{
134 \\ .major = {},
135 \\ .minor = {},
136 \\ .patch = {},
137 \\ }},
138 \\ .max = .{{
139 \\ .major = {},
140 \\ .minor = {},
141 \\ .patch = {},
142 \\ }},
143 \\ }},
144 \\ .glibc = .{{
145 \\ .major = {},
146 \\ .minor = {},
147 \\ .patch = {},
148 \\ }},
149 \\ .android = {},
150 \\ }}}},
151 \\
152 , .{
153 linux.range.min.major,
154 linux.range.min.minor,
155 linux.range.min.patch,
156
157 linux.range.max.major,
158 linux.range.max.minor,
159 linux.range.max.patch,
160
161 linux.glibc.major,
162 linux.glibc.minor,
163 linux.glibc.patch,
164
165 linux.android,
166 }),
167 .hurd => |hurd| try buffer.print(
168 \\ .hurd = .{{
169 \\ .range = .{{
170 \\ .min = .{{
171 \\ .major = {},
172 \\ .minor = {},
173 \\ .patch = {},
174 \\ }},
175 \\ .max = .{{
176 \\ .major = {},
177 \\ .minor = {},
178 \\ .patch = {},
179 \\ }},
180 \\ }},
181 \\ .glibc = .{{
182 \\ .major = {},
183 \\ .minor = {},
184 \\ .patch = {},
185 \\ }},
186 \\ }}}},
187 \\
188 , .{
189 hurd.range.min.major,
190 hurd.range.min.minor,
191 hurd.range.min.patch,
192
193 hurd.range.max.major,
194 hurd.range.max.minor,
195 hurd.range.max.patch,
196
197 hurd.glibc.major,
198 hurd.glibc.minor,
199 hurd.glibc.patch,
200 }),
201 .windows => |windows| try buffer.print(
202 \\ .windows = .{{
203 \\ .min = {f},
204 \\ .max = {f},
205 \\ }}}},
206 \\
207 , .{ windows.min, windows.max }),
208 }
209 try buffer.appendSlice(
210 \\};
211 \\pub const target: std.Target = .{
212 \\ .cpu = cpu,
213 \\ .os = os,
214 \\ .abi = abi,
215 \\ .ofmt = object_format,
216 \\
217 );
218
219 if (target.dynamic_linker.get()) |dl| {
220 try buffer.print(
221 \\ .dynamic_linker = .init("{s}"),
222 \\}};
223 \\
224 , .{dl});
225 } else {
226 try buffer.appendSlice(
227 \\ .dynamic_linker = .none,
228 \\};
229 \\
230 );
231 }
232
233 // This is so that compiler_rt and libc.zig libraries know whether they
234 // will eventually be linked with libc. They make different decisions
235 // about what to export depending on whether another libc will be linked
236 // in. For example, compiler_rt will not export the __chkstk symbol if it
237 // knows libc will provide it, and likewise c.zig will not export memcpy.
238 const link_libc = opts.link_libc;
239
240 try buffer.print(
241 \\pub const object_format: std.Target.ObjectFormat = .{f};
242 \\pub const mode: std.builtin.OptimizeMode = .{f};
243 \\pub const link_libc = {};
244 \\pub const link_libcpp = {};
245 \\pub const have_error_return_tracing = {};
246 \\pub const valgrind_support = {};
247 \\pub const sanitize_thread = {};
248 \\pub const fuzz = {};
249 \\pub const position_independent_code = {};
250 \\pub const position_independent_executable = {};
251 \\pub const strip_debug_info = {};
252 \\pub const code_model: std.builtin.CodeModel = .{f};
253 \\pub const omit_frame_pointer = {};
254 \\
255 , .{
256 std.zig.fmtIdPU(@tagName(target.ofmt)),
257 std.zig.fmtIdPU(@tagName(opts.optimize_mode)),
258 link_libc,
259 opts.link_libcpp,
260 opts.error_tracing,
261 opts.valgrind,
262 opts.sanitize_thread,
263 opts.fuzz,
264 opts.pic,
265 opts.pie,
266 opts.strip,
267 std.zig.fmtIdPU(@tagName(opts.code_model)),
268 opts.omit_frame_pointer,
269 });
270
271 if (target.os.tag == .wasi) {
272 try buffer.print(
273 \\pub const wasi_exec_model: std.builtin.WasiExecModel = .{f};
274 \\
275 , .{std.zig.fmtIdPU(@tagName(opts.wasi_exec_model))});
276 }
277
278 if (opts.is_test) {
279 try buffer.appendSlice(
280 \\pub var test_functions: []const std.builtin.TestFn = &.{}; // overwritten later
281 \\
282 );
283 }
284}
285
286/// This essentially takes the place of `Zcu.PerThread.updateFile`, but for 'builtin' modules.
287/// Instead of reading the file from disk, its contents are generated in-memory.
288pub fn populateFile(opts: @This(), gpa: Allocator, file: *File) Allocator.Error!void {
289 assert(file.is_builtin);
290 assert(file.status == .never_loaded);
291 assert(file.source == null);
292 assert(file.tree == null);
293 assert(file.zir == null);
294
295 file.source = try opts.generate(gpa);
296
297 log.debug("parsing and generating 'builtin.zig'", .{});
298
299 file.tree = try std.zig.Ast.parse(gpa, file.source.?, .zig);
300 assert(file.tree.?.errors.len == 0); // builtin.zig must parse
301
302 file.zir = try AstGen.generate(gpa, file.tree.?);
303 assert(!file.zir.?.hasCompileErrors()); // builtin.zig must not have astgen errors
304 file.status = .success;
305}
306
307/// After `populateFile` succeeds, call this function to write the generated file out to disk
308/// if necessary. This is useful for external tooling such as debuggers.
309/// Assumes that `file.mod` is correctly set to the builtin module.
310pub fn updateFileOnDisk(file: *File, comp: *Compilation) !void {
311 assert(file.is_builtin);
312 assert(file.status == .success);
313 assert(file.source != null);
314
315 const root_dir, const sub_path = file.path.openInfo(comp.dirs);
316
317 if (root_dir.statFile(sub_path)) |stat| {
318 if (stat.size != file.source.?.len) {
319 std.log.warn(
320 "the cached file '{f}' had the wrong size. Expected {d}, found {d}. " ++
321 "Overwriting with correct file contents now",
322 .{ file.path.fmt(comp), file.source.?.len, stat.size },
323 );
324 } else {
325 file.stat = .{
326 .size = stat.size,
327 .inode = stat.inode,
328 .mtime = stat.mtime,
329 };
330 return;
331 }
332 } else |err| switch (err) {
333 error.FileNotFound => {},
334
335 error.WouldBlock => unreachable, // not asking for non-blocking I/O
336 error.BadPathName => unreachable, // it's always "o/digest/builtin.zig"
337 error.NameTooLong => unreachable, // it's always "o/digest/builtin.zig"
338
339 // We don't expect the file to be a pipe, but can't mark `error.PipeBusy` as `unreachable`,
340 // because the user could always replace the file on disk.
341 else => |e| return e,
342 }
343
344 // `make_path` matters because the dir hasn't actually been created yet.
345 var af = try root_dir.atomicFile(sub_path, .{ .make_path = true, .write_buffer = &.{} });
346 defer af.deinit();
347 try af.file_writer.interface.writeAll(file.source.?);
348 af.finish() catch |err| switch (err) {
349 error.AccessDenied => switch (builtin.os.tag) {
350 .windows => {
351 // Very likely happened due to another process or thread
352 // simultaneously creating the same, correct builtin.zig file.
353 // This is not a problem; ignore it.
354 },
355 else => return err,
356 },
357 else => return err,
358 };
359
360 file.stat = .{
361 .size = file.source.?.len,
362 .inode = 0, // dummy value
363 .mtime = .zero, // dummy value
364 };
365}
366
367const builtin = @import("builtin");
368const std = @import("std");
369const Allocator = std.mem.Allocator;
370const Cache = std.Build.Cache;
371const build_options = @import("build_options");
372const Module = @import("Package/Module.zig");
373const assert = std.debug.assert;
374const AstGen = std.zig.AstGen;
375const File = @import("Zcu.zig").File;
376const Compilation = @import("Compilation.zig");
377const log = std.log.scoped(.builtin);