mirror of
https://codeberg.org/ziglang/zig.git
synced 2025-12-06 05:44:20 +00:00
377 lines
13 KiB
Zig
377 lines
13 KiB
Zig
target: std.Target,
|
|
zig_backend: std.builtin.CompilerBackend,
|
|
output_mode: std.builtin.OutputMode,
|
|
link_mode: std.builtin.LinkMode,
|
|
unwind_tables: std.builtin.UnwindTables,
|
|
is_test: bool,
|
|
single_threaded: bool,
|
|
link_libc: bool,
|
|
link_libcpp: bool,
|
|
optimize_mode: std.builtin.OptimizeMode,
|
|
error_tracing: bool,
|
|
valgrind: bool,
|
|
sanitize_thread: bool,
|
|
fuzz: bool,
|
|
pic: bool,
|
|
pie: bool,
|
|
strip: bool,
|
|
code_model: std.builtin.CodeModel,
|
|
omit_frame_pointer: bool,
|
|
wasi_exec_model: std.builtin.WasiExecModel,
|
|
|
|
/// Compute an abstract hash representing this `Builtin`. This is *not* a hash
|
|
/// of the resulting file contents.
|
|
pub fn hash(opts: @This()) [std.Build.Cache.bin_digest_len]u8 {
|
|
var h: Cache.Hasher = Cache.hasher_init;
|
|
inline for (@typeInfo(@This()).@"struct".fields) |f| {
|
|
if (comptime std.mem.eql(u8, f.name, "target")) {
|
|
// This needs special handling.
|
|
std.hash.autoHash(&h, opts.target.cpu);
|
|
std.hash.autoHash(&h, opts.target.os.tag);
|
|
std.hash.autoHash(&h, opts.target.os.versionRange());
|
|
std.hash.autoHash(&h, opts.target.abi);
|
|
std.hash.autoHash(&h, opts.target.ofmt);
|
|
std.hash.autoHash(&h, opts.target.dynamic_linker);
|
|
} else {
|
|
std.hash.autoHash(&h, @field(opts, f.name));
|
|
}
|
|
}
|
|
return h.finalResult();
|
|
}
|
|
|
|
pub fn generate(opts: @This(), allocator: Allocator) Allocator.Error![:0]u8 {
|
|
var buffer = std.array_list.Managed(u8).init(allocator);
|
|
try append(opts, &buffer);
|
|
return buffer.toOwnedSliceSentinel(0);
|
|
}
|
|
|
|
pub fn append(opts: @This(), buffer: *std.array_list.Managed(u8)) Allocator.Error!void {
|
|
const target = opts.target;
|
|
const arch_family_name = @tagName(target.cpu.arch.family());
|
|
const zig_backend = opts.zig_backend;
|
|
|
|
@setEvalBranchQuota(4000);
|
|
try buffer.print(
|
|
\\const std = @import("std");
|
|
\\/// Zig version. When writing code that supports multiple versions of Zig, prefer
|
|
\\/// feature detection (i.e. with `@hasDecl` or `@hasField`) over version checks.
|
|
\\pub const zig_version = std.SemanticVersion.parse(zig_version_string) catch unreachable;
|
|
\\pub const zig_version_string = "{s}";
|
|
\\pub const zig_backend = std.builtin.CompilerBackend.{f};
|
|
\\
|
|
\\pub const output_mode: std.builtin.OutputMode = .{f};
|
|
\\pub const link_mode: std.builtin.LinkMode = .{f};
|
|
\\pub const unwind_tables: std.builtin.UnwindTables = .{f};
|
|
\\pub const is_test = {};
|
|
\\pub const single_threaded = {};
|
|
\\pub const abi: std.Target.Abi = .{f};
|
|
\\pub const cpu: std.Target.Cpu = .{{
|
|
\\ .arch = .{f},
|
|
\\ .model = &std.Target.{f}.cpu.{f},
|
|
\\ .features = std.Target.{f}.featureSet(&.{{
|
|
\\
|
|
, .{
|
|
build_options.version,
|
|
std.zig.fmtIdPU(@tagName(zig_backend)),
|
|
std.zig.fmtIdPU(@tagName(opts.output_mode)),
|
|
std.zig.fmtIdPU(@tagName(opts.link_mode)),
|
|
std.zig.fmtIdPU(@tagName(opts.unwind_tables)),
|
|
opts.is_test,
|
|
opts.single_threaded,
|
|
std.zig.fmtIdPU(@tagName(target.abi)),
|
|
std.zig.fmtIdPU(@tagName(target.cpu.arch)),
|
|
std.zig.fmtIdPU(arch_family_name),
|
|
std.zig.fmtIdPU(target.cpu.model.name),
|
|
std.zig.fmtIdPU(arch_family_name),
|
|
});
|
|
|
|
for (target.cpu.arch.allFeaturesList(), 0..) |feature, index_usize| {
|
|
const index = @as(std.Target.Cpu.Feature.Set.Index, @intCast(index_usize));
|
|
const is_enabled = target.cpu.features.isEnabled(index);
|
|
if (is_enabled) {
|
|
try buffer.print(" .{f},\n", .{std.zig.fmtIdPU(feature.name)});
|
|
}
|
|
}
|
|
try buffer.print(
|
|
\\ }}),
|
|
\\}};
|
|
\\pub const os: std.Target.Os = .{{
|
|
\\ .tag = .{f},
|
|
\\ .version_range = .{{
|
|
,
|
|
.{std.zig.fmtIdPU(@tagName(target.os.tag))},
|
|
);
|
|
|
|
switch (target.os.versionRange()) {
|
|
.none => try buffer.appendSlice(" .none = {} },\n"),
|
|
.semver => |semver| try buffer.print(
|
|
\\ .semver = .{{
|
|
\\ .min = .{{
|
|
\\ .major = {},
|
|
\\ .minor = {},
|
|
\\ .patch = {},
|
|
\\ }},
|
|
\\ .max = .{{
|
|
\\ .major = {},
|
|
\\ .minor = {},
|
|
\\ .patch = {},
|
|
\\ }},
|
|
\\ }}}},
|
|
\\
|
|
, .{
|
|
semver.min.major,
|
|
semver.min.minor,
|
|
semver.min.patch,
|
|
|
|
semver.max.major,
|
|
semver.max.minor,
|
|
semver.max.patch,
|
|
}),
|
|
.linux => |linux| try buffer.print(
|
|
\\ .linux = .{{
|
|
\\ .range = .{{
|
|
\\ .min = .{{
|
|
\\ .major = {},
|
|
\\ .minor = {},
|
|
\\ .patch = {},
|
|
\\ }},
|
|
\\ .max = .{{
|
|
\\ .major = {},
|
|
\\ .minor = {},
|
|
\\ .patch = {},
|
|
\\ }},
|
|
\\ }},
|
|
\\ .glibc = .{{
|
|
\\ .major = {},
|
|
\\ .minor = {},
|
|
\\ .patch = {},
|
|
\\ }},
|
|
\\ .android = {},
|
|
\\ }}}},
|
|
\\
|
|
, .{
|
|
linux.range.min.major,
|
|
linux.range.min.minor,
|
|
linux.range.min.patch,
|
|
|
|
linux.range.max.major,
|
|
linux.range.max.minor,
|
|
linux.range.max.patch,
|
|
|
|
linux.glibc.major,
|
|
linux.glibc.minor,
|
|
linux.glibc.patch,
|
|
|
|
linux.android,
|
|
}),
|
|
.hurd => |hurd| try buffer.print(
|
|
\\ .hurd = .{{
|
|
\\ .range = .{{
|
|
\\ .min = .{{
|
|
\\ .major = {},
|
|
\\ .minor = {},
|
|
\\ .patch = {},
|
|
\\ }},
|
|
\\ .max = .{{
|
|
\\ .major = {},
|
|
\\ .minor = {},
|
|
\\ .patch = {},
|
|
\\ }},
|
|
\\ }},
|
|
\\ .glibc = .{{
|
|
\\ .major = {},
|
|
\\ .minor = {},
|
|
\\ .patch = {},
|
|
\\ }},
|
|
\\ }}}},
|
|
\\
|
|
, .{
|
|
hurd.range.min.major,
|
|
hurd.range.min.minor,
|
|
hurd.range.min.patch,
|
|
|
|
hurd.range.max.major,
|
|
hurd.range.max.minor,
|
|
hurd.range.max.patch,
|
|
|
|
hurd.glibc.major,
|
|
hurd.glibc.minor,
|
|
hurd.glibc.patch,
|
|
}),
|
|
.windows => |windows| try buffer.print(
|
|
\\ .windows = .{{
|
|
\\ .min = {f},
|
|
\\ .max = {f},
|
|
\\ }}}},
|
|
\\
|
|
, .{ windows.min, windows.max }),
|
|
}
|
|
try buffer.appendSlice(
|
|
\\};
|
|
\\pub const target: std.Target = .{
|
|
\\ .cpu = cpu,
|
|
\\ .os = os,
|
|
\\ .abi = abi,
|
|
\\ .ofmt = object_format,
|
|
\\
|
|
);
|
|
|
|
if (target.dynamic_linker.get()) |dl| {
|
|
try buffer.print(
|
|
\\ .dynamic_linker = .init("{s}"),
|
|
\\}};
|
|
\\
|
|
, .{dl});
|
|
} else {
|
|
try buffer.appendSlice(
|
|
\\ .dynamic_linker = .none,
|
|
\\};
|
|
\\
|
|
);
|
|
}
|
|
|
|
// This is so that compiler_rt and libc.zig libraries know whether they
|
|
// will eventually be linked with libc. They make different decisions
|
|
// about what to export depending on whether another libc will be linked
|
|
// in. For example, compiler_rt will not export the __chkstk symbol if it
|
|
// knows libc will provide it, and likewise c.zig will not export memcpy.
|
|
const link_libc = opts.link_libc;
|
|
|
|
try buffer.print(
|
|
\\pub const object_format: std.Target.ObjectFormat = .{f};
|
|
\\pub const mode: std.builtin.OptimizeMode = .{f};
|
|
\\pub const link_libc = {};
|
|
\\pub const link_libcpp = {};
|
|
\\pub const have_error_return_tracing = {};
|
|
\\pub const valgrind_support = {};
|
|
\\pub const sanitize_thread = {};
|
|
\\pub const fuzz = {};
|
|
\\pub const position_independent_code = {};
|
|
\\pub const position_independent_executable = {};
|
|
\\pub const strip_debug_info = {};
|
|
\\pub const code_model: std.builtin.CodeModel = .{f};
|
|
\\pub const omit_frame_pointer = {};
|
|
\\
|
|
, .{
|
|
std.zig.fmtIdPU(@tagName(target.ofmt)),
|
|
std.zig.fmtIdPU(@tagName(opts.optimize_mode)),
|
|
link_libc,
|
|
opts.link_libcpp,
|
|
opts.error_tracing,
|
|
opts.valgrind,
|
|
opts.sanitize_thread,
|
|
opts.fuzz,
|
|
opts.pic,
|
|
opts.pie,
|
|
opts.strip,
|
|
std.zig.fmtIdPU(@tagName(opts.code_model)),
|
|
opts.omit_frame_pointer,
|
|
});
|
|
|
|
if (target.os.tag == .wasi) {
|
|
try buffer.print(
|
|
\\pub const wasi_exec_model: std.builtin.WasiExecModel = .{f};
|
|
\\
|
|
, .{std.zig.fmtIdPU(@tagName(opts.wasi_exec_model))});
|
|
}
|
|
|
|
if (opts.is_test) {
|
|
try buffer.appendSlice(
|
|
\\pub var test_functions: []const std.builtin.TestFn = &.{}; // overwritten later
|
|
\\
|
|
);
|
|
}
|
|
}
|
|
|
|
/// This essentially takes the place of `Zcu.PerThread.updateFile`, but for 'builtin' modules.
|
|
/// Instead of reading the file from disk, its contents are generated in-memory.
|
|
pub fn populateFile(opts: @This(), gpa: Allocator, file: *File) Allocator.Error!void {
|
|
assert(file.is_builtin);
|
|
assert(file.status == .never_loaded);
|
|
assert(file.source == null);
|
|
assert(file.tree == null);
|
|
assert(file.zir == null);
|
|
|
|
file.source = try opts.generate(gpa);
|
|
|
|
log.debug("parsing and generating 'builtin.zig'", .{});
|
|
|
|
file.tree = try std.zig.Ast.parse(gpa, file.source.?, .zig);
|
|
assert(file.tree.?.errors.len == 0); // builtin.zig must parse
|
|
|
|
file.zir = try AstGen.generate(gpa, file.tree.?);
|
|
assert(!file.zir.?.hasCompileErrors()); // builtin.zig must not have astgen errors
|
|
file.status = .success;
|
|
}
|
|
|
|
/// After `populateFile` succeeds, call this function to write the generated file out to disk
|
|
/// if necessary. This is useful for external tooling such as debuggers.
|
|
/// Assumes that `file.mod` is correctly set to the builtin module.
|
|
pub fn updateFileOnDisk(file: *File, comp: *Compilation) !void {
|
|
assert(file.is_builtin);
|
|
assert(file.status == .success);
|
|
assert(file.source != null);
|
|
|
|
const root_dir, const sub_path = file.path.openInfo(comp.dirs);
|
|
|
|
if (root_dir.statFile(sub_path)) |stat| {
|
|
if (stat.size != file.source.?.len) {
|
|
std.log.warn(
|
|
"the cached file '{f}' had the wrong size. Expected {d}, found {d}. " ++
|
|
"Overwriting with correct file contents now",
|
|
.{ file.path.fmt(comp), file.source.?.len, stat.size },
|
|
);
|
|
} else {
|
|
file.stat = .{
|
|
.size = stat.size,
|
|
.inode = stat.inode,
|
|
.mtime = stat.mtime,
|
|
};
|
|
return;
|
|
}
|
|
} else |err| switch (err) {
|
|
error.FileNotFound => {},
|
|
|
|
error.WouldBlock => unreachable, // not asking for non-blocking I/O
|
|
error.BadPathName => unreachable, // it's always "o/digest/builtin.zig"
|
|
error.NameTooLong => unreachable, // it's always "o/digest/builtin.zig"
|
|
|
|
// We don't expect the file to be a pipe, but can't mark `error.PipeBusy` as `unreachable`,
|
|
// because the user could always replace the file on disk.
|
|
else => |e| return e,
|
|
}
|
|
|
|
// `make_path` matters because the dir hasn't actually been created yet.
|
|
var af = try root_dir.atomicFile(sub_path, .{ .make_path = true, .write_buffer = &.{} });
|
|
defer af.deinit();
|
|
try af.file_writer.interface.writeAll(file.source.?);
|
|
af.finish() catch |err| switch (err) {
|
|
error.AccessDenied => switch (builtin.os.tag) {
|
|
.windows => {
|
|
// Very likely happened due to another process or thread
|
|
// simultaneously creating the same, correct builtin.zig file.
|
|
// This is not a problem; ignore it.
|
|
},
|
|
else => return err,
|
|
},
|
|
else => return err,
|
|
};
|
|
|
|
file.stat = .{
|
|
.size = file.source.?.len,
|
|
.inode = 0, // dummy value
|
|
.mtime = .zero, // dummy value
|
|
};
|
|
}
|
|
|
|
const builtin = @import("builtin");
|
|
const std = @import("std");
|
|
const Allocator = std.mem.Allocator;
|
|
const Cache = std.Build.Cache;
|
|
const build_options = @import("build_options");
|
|
const Module = @import("Package/Module.zig");
|
|
const assert = std.debug.assert;
|
|
const AstGen = std.zig.AstGen;
|
|
const File = @import("Zcu.zig").File;
|
|
const Compilation = @import("Compilation.zig");
|
|
const log = std.log.scoped(.builtin);
|