| 1 | target: std.Target, |
| 2 | zig_backend: std.lang.CompilerBackend, |
| 3 | output_mode: std.lang.OutputMode, |
| 4 | link_mode: std.lang.LinkMode, |
| 5 | unwind_tables: std.lang.UnwindTables, |
| 6 | is_test: bool, |
| 7 | single_threaded: bool, |
| 8 | link_libc: bool, |
| 9 | link_libcpp: bool, |
| 10 | optimize_mode: std.lang.Optimize, |
| 11 | error_tracing: bool, |
| 12 | valgrind: bool, |
| 13 | sanitize_thread: bool, |
| 14 | fuzz: bool, |
| 15 | pic: bool, |
| 16 | pie: bool, |
| 17 | strip: bool, |
| 18 | code_model: std.lang.CodeModel, |
| 19 | omit_frame_pointer: bool, |
| 20 | wasi_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. |
| 24 | pub 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 | |
| 42 | pub 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 | |
| 48 | pub 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. |
| 292 | pub 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. |
| 314 | pub 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 | |
| 372 | const builtin = @import("builtin"); |
| 373 | const std = @import("std"); |
| 374 | const Allocator = std.mem.Allocator; |
| 375 | const Cache = std.Build.Cache; |
| 376 | const build_options = @import("build_options"); |
| 377 | const Module = @import("Module.zig"); |
| 378 | const assert = std.debug.assert; |
| 379 | const AstGen = std.zig.AstGen; |
| 380 | const File = @import("Zcu.zig").File; |
| 381 | const Compilation = @import("Compilation.zig"); |
| 382 | const log = std.log.scoped(.builtin); |