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);