| ... | ... | @@ -48,6 +48,9 @@ arena: std.heap.ArenaAllocator, |
| 48 | 48 | /// Not every Compilation compiles .zig code! For example you could do `zig build-exe foo.o`. |
| 49 | 49 | /// TODO: rename to zcu: ?*Zcu |
| 50 | 50 | module: ?*Module, |
| 51 | /// Contains different state depending on whether the Compilation uses |
| 52 | /// incremental or whole cache mode. |
| 53 | cache_use: CacheUse, |
| 51 | 54 | /// All compilations have a root module because this is where some important |
| 52 | 55 | /// settings are stored, such as target and optimization mode. This module |
| 53 | 56 | /// might not have any .zig code associated with it, however. |
| ... | ... | @@ -66,7 +69,6 @@ implib_emit: ?Emit, |
| 66 | 69 | /// This is non-null when `-femit-docs` is provided. |
| 67 | 70 | docs_emit: ?Emit, |
| 68 | 71 | root_name: [:0]const u8, |
| 69 | | cache_mode: CacheMode, |
| 70 | 72 | include_compiler_rt: bool, |
| 71 | 73 | objects: []Compilation.LinkObject, |
| 72 | 74 | /// Needed only for passing -F args to clang. |
| ... | ... | @@ -82,9 +84,6 @@ no_builtin: bool, |
| 82 | 84 | c_object_table: std.AutoArrayHashMapUnmanaged(*CObject, void) = .{}, |
| 83 | 85 | win32_resource_table: if (build_options.only_core_functionality) void else std.AutoArrayHashMapUnmanaged(*Win32Resource, void) = |
| 84 | 86 | if (build_options.only_core_functionality) {} else .{}, |
| 85 | | /// This is a pointer to a local variable inside `update()`. |
| 86 | | whole_cache_manifest: ?*Cache.Manifest = null, |
| 87 | | whole_cache_manifest_mutex: std.Thread.Mutex = .{}, |
| 88 | 87 | |
| 89 | 88 | link_error_flags: link.File.ErrorFlags = .{}, |
| 90 | 89 | lld_errors: std.ArrayListUnmanaged(LldError) = .{}, |
| ... | ... | @@ -154,14 +153,6 @@ rc_source_files: []const RcSourceFile, |
| 154 | 153 | cache_parent: *Cache, |
| 155 | 154 | /// Path to own executable for invoking `zig clang`. |
| 156 | 155 | self_exe_path: ?[]const u8, |
| 157 | | /// null means -fno-emit-bin. |
| 158 | | /// This is mutable memory allocated into the Compilation-lifetime arena (`arena`) |
| 159 | | /// of exactly the correct size for "o/[digest]/[basename]". |
| 160 | | /// The basename is of the outputted binary file in case we don't know the directory yet. |
| 161 | | whole_bin_sub_path: ?[]u8, |
| 162 | | /// Same as `whole_bin_sub_path` but for implibs. |
| 163 | | whole_implib_sub_path: ?[]u8, |
| 164 | | whole_docs_sub_path: ?[]u8, |
| 165 | 156 | zig_lib_directory: Directory, |
| 166 | 157 | local_cache_directory: Directory, |
| 167 | 158 | global_cache_directory: Directory, |
| ... | ... | @@ -199,9 +190,6 @@ glibc_so_files: ?glibc.BuiltSharedObjects = null, |
| 199 | 190 | /// The key is the basename, and the value is the absolute path to the completed build artifact. |
| 200 | 191 | crt_files: std.StringHashMapUnmanaged(CRTFile) = .{}, |
| 201 | 192 | |
| 202 | | /// Keeping track of this possibly open resource so we can close it later. |
| 203 | | owned_link_dir: ?std.fs.Dir, |
| 204 | | |
| 205 | 193 | /// This is for stage1 and should be deleted upon completion of self-hosting. |
| 206 | 194 | /// Don't use this for anything other than stage1 compatibility. |
| 207 | 195 | color: Color = .auto, |
| ... | ... | @@ -869,7 +857,32 @@ pub const ClangPreprocessorMode = enum { |
| 869 | 857 | |
| 870 | 858 | pub const Framework = link.File.MachO.Framework; |
| 871 | 859 | pub const SystemLib = link.SystemLib; |
| 872 | | pub const CacheMode = link.CacheMode; |
| 860 | |
| 861 | pub const CacheMode = enum { incremental, whole }; |
| 862 | |
| 863 | pub const CacheUse = union(CacheMode) { |
| 864 | incremental: *Incremental, |
| 865 | whole: *Whole, |
| 866 | |
| 867 | pub const Whole = struct { |
| 868 | /// This is a pointer to a local variable inside `update()`. |
| 869 | cache_manifest: ?*Cache.Manifest = null, |
| 870 | cache_manifest_mutex: std.Thread.Mutex = .{}, |
| 871 | /// null means -fno-emit-bin. |
| 872 | /// This is mutable memory allocated into the Compilation-lifetime arena (`arena`) |
| 873 | /// of exactly the correct size for "o/[digest]/[basename]". |
| 874 | /// The basename is of the outputted binary file in case we don't know the directory yet. |
| 875 | bin_sub_path: ?[]u8, |
| 876 | /// Same as `whole_bin_sub_path` but for implibs. |
| 877 | implib_sub_path: ?[]u8, |
| 878 | docs_sub_path: ?[]u8, |
| 879 | }; |
| 880 | |
| 881 | pub const Incremental = struct { |
| 882 | /// Where build artifacts and incremental compilation metadata serialization go. |
| 883 | artifact_directory: Compilation.Directory, |
| 884 | }; |
| 885 | }; |
| 873 | 886 | |
| 874 | 887 | pub const LinkObject = struct { |
| 875 | 888 | path: []const u8, |
| ... | ... | @@ -1280,6 +1293,9 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation { |
| 1280 | 1293 | |
| 1281 | 1294 | const error_limit = options.error_limit orelse (std.math.maxInt(u16) - 1); |
| 1282 | 1295 | |
| 1296 | const each_lib_rpath = options.each_lib_rpath orelse |
| 1297 | options.root_mod.resolved_target.is_native_os; |
| 1298 | |
| 1283 | 1299 | // We put everything into the cache hash that *cannot be modified |
| 1284 | 1300 | // during an incremental update*. For example, one cannot change the |
| 1285 | 1301 | // target between updates, but one can change source files, so the |
| ... | ... | @@ -1319,73 +1335,7 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation { |
| 1319 | 1335 | cache.hash.add(options.config.wasi_exec_model); |
| 1320 | 1336 | // TODO audit this and make sure everything is in it |
| 1321 | 1337 | |
| 1322 | | const zcu: ?*Module = if (have_zcu) blk: { |
| 1323 | | // Options that are specific to zig source files, that cannot be |
| 1324 | | // modified between incremental updates. |
| 1325 | | var hash = cache.hash; |
| 1326 | | |
| 1327 | | switch (cache_mode) { |
| 1328 | | .incremental => { |
| 1329 | | // Here we put the root source file path name, but *not* with addFile. |
| 1330 | | // We want the hash to be the same regardless of the contents of the |
| 1331 | | // source file, because incremental compilation will handle it, but we |
| 1332 | | // do want to namespace different source file names because they are |
| 1333 | | // likely different compilations and therefore this would be likely to |
| 1334 | | // cause cache hits. |
| 1335 | | try addModuleTableToCacheHash(gpa, arena, &hash, options.root_mod, .path_bytes); |
| 1336 | | }, |
| 1337 | | .whole => { |
| 1338 | | // In this case, we postpone adding the input source file until |
| 1339 | | // we create the cache manifest, in update(), because we want to |
| 1340 | | // track it and packages as files. |
| 1341 | | }, |
| 1342 | | } |
| 1343 | | |
| 1344 | | // Synchronize with other matching comments: ZigOnlyHashStuff |
| 1345 | | hash.add(use_llvm); |
| 1346 | | hash.add(options.config.use_lib_llvm); |
| 1347 | | hash.add(dll_export_fns); |
| 1348 | | hash.add(options.config.is_test); |
| 1349 | | hash.add(options.config.test_evented_io); |
| 1350 | | hash.addOptionalBytes(options.test_filter); |
| 1351 | | hash.addOptionalBytes(options.test_name_prefix); |
| 1352 | | hash.add(options.skip_linker_dependencies); |
| 1353 | | hash.add(formatted_panics); |
| 1354 | | hash.add(options.emit_h != null); |
| 1355 | | hash.add(error_limit); |
| 1356 | | |
| 1357 | | // In the case of incremental cache mode, this `zig_cache_artifact_directory` |
| 1358 | | // is computed based on a hash of non-linker inputs, and it is where all |
| 1359 | | // build artifacts are stored (even while in-progress). |
| 1360 | | // |
| 1361 | | // For whole cache mode, it is still used for builtin.zig so that the file |
| 1362 | | // path to builtin.zig can remain consistent during a debugging session at |
| 1363 | | // runtime. However, we don't know where to put outputs from the linker |
| 1364 | | // until the final cache hash, which is available after the |
| 1365 | | // compilation is complete. |
| 1366 | | // |
| 1367 | | // Therefore, in whole cache mode, we additionally create a temporary cache |
| 1368 | | // directory for these two kinds of build artifacts, and then rename it |
| 1369 | | // into place after the final hash is known. However, we don't want |
| 1370 | | // to create the temporary directory here, because in the case of a cache hit, |
| 1371 | | // this would have been wasted syscalls to make the directory and then not |
| 1372 | | // use it (or delete it). |
| 1373 | | // |
| 1374 | | // In summary, for whole cache mode, we simulate `-fno-emit-bin` in this |
| 1375 | | // function, and `zig_cache_artifact_directory` is *wrong* except for builtin.zig, |
| 1376 | | // and then at the beginning of `update()` when we find out whether we need |
| 1377 | | // a temporary directory, we patch up all the places that the incorrect |
| 1378 | | // `zig_cache_artifact_directory` was passed to various components of the compiler. |
| 1379 | | |
| 1380 | | const digest = hash.final(); |
| 1381 | | const artifact_sub_dir = try std.fs.path.join(arena, &[_][]const u8{ "o", &digest }); |
| 1382 | | var artifact_dir = try options.local_cache_directory.handle.makeOpenPath(artifact_sub_dir, .{}); |
| 1383 | | errdefer artifact_dir.close(); |
| 1384 | | const zig_cache_artifact_directory: Directory = .{ |
| 1385 | | .handle = artifact_dir, |
| 1386 | | .path = try options.local_cache_directory.join(arena, &[_][]const u8{artifact_sub_dir}), |
| 1387 | | }; |
| 1388 | | |
| 1338 | const opt_zcu: ?*Module = if (have_zcu) blk: { |
| 1389 | 1339 | // Pre-open the directory handles for cached ZIR code so that it does not need |
| 1390 | 1340 | // to redundantly happen for each AstGen operation. |
| 1391 | 1341 | const zir_sub_dir = "z"; |
| ... | ... | @@ -1404,27 +1354,18 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation { |
| 1404 | 1354 | }; |
| 1405 | 1355 | |
| 1406 | 1356 | const emit_h: ?*Module.GlobalEmitH = if (options.emit_h) |loc| eh: { |
| 1407 | | const eh = try gpa.create(Module.GlobalEmitH); |
| 1357 | const eh = try arena.create(Module.GlobalEmitH); |
| 1408 | 1358 | eh.* = .{ .loc = loc }; |
| 1409 | 1359 | break :eh eh; |
| 1410 | 1360 | } else null; |
| 1411 | | errdefer if (emit_h) |eh| gpa.destroy(eh); |
| 1412 | | |
| 1413 | | // TODO when we implement serialization and deserialization of incremental |
| 1414 | | // compilation metadata, this is where we would load it. We have open a handle |
| 1415 | | // to the directory where the output either already is, or will be. |
| 1416 | | // However we currently do not have serialization of such metadata, so for now |
| 1417 | | // we set up an empty Module that does the entire compilation fresh. |
| 1418 | 1361 | |
| 1419 | 1362 | const zcu = try arena.create(Module); |
| 1420 | | errdefer zcu.deinit(); |
| 1421 | 1363 | zcu.* = .{ |
| 1422 | 1364 | .gpa = gpa, |
| 1423 | 1365 | .comp = comp, |
| 1424 | 1366 | .main_mod = options.main_mod orelse options.root_mod, |
| 1425 | 1367 | .root_mod = options.root_mod, |
| 1426 | 1368 | .std_mod = options.std_mod, |
| 1427 | | .zig_cache_artifact_directory = zig_cache_artifact_directory, |
| 1428 | 1369 | .global_zir_cache = global_zir_cache, |
| 1429 | 1370 | .local_zir_cache = local_zir_cache, |
| 1430 | 1371 | .emit_h = emit_h, |
| ... | ... | @@ -1432,155 +1373,33 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation { |
| 1432 | 1373 | .error_limit = error_limit, |
| 1433 | 1374 | }; |
| 1434 | 1375 | try zcu.init(); |
| 1435 | | |
| 1436 | 1376 | break :blk zcu; |
| 1437 | 1377 | } else blk: { |
| 1438 | 1378 | if (options.emit_h != null) return error.NoZigModuleForCHeader; |
| 1439 | 1379 | break :blk null; |
| 1440 | 1380 | }; |
| 1441 | | errdefer if (zcu) |u| u.deinit(); |
| 1442 | | |
| 1443 | | // For resource management purposes. |
| 1444 | | var owned_link_dir: ?std.fs.Dir = null; |
| 1445 | | errdefer if (owned_link_dir) |*dir| dir.close(); |
| 1446 | | |
| 1447 | | const bin_file_emit: ?Emit = blk: { |
| 1448 | | const emit_bin = options.emit_bin orelse break :blk null; |
| 1449 | | |
| 1450 | | if (emit_bin.directory) |directory| { |
| 1451 | | break :blk Emit{ |
| 1452 | | .directory = directory, |
| 1453 | | .sub_path = emit_bin.basename, |
| 1454 | | }; |
| 1455 | | } |
| 1456 | | |
| 1457 | | // In case of whole cache mode, `whole_bin_sub_path` is used to distinguish |
| 1458 | | // between -femit-bin and -fno-emit-bin. |
| 1459 | | switch (cache_mode) { |
| 1460 | | .whole => break :blk null, |
| 1461 | | .incremental => {}, |
| 1462 | | } |
| 1463 | | |
| 1464 | | if (zcu) |u| { |
| 1465 | | break :blk Emit{ |
| 1466 | | .directory = u.zig_cache_artifact_directory, |
| 1467 | | .sub_path = emit_bin.basename, |
| 1468 | | }; |
| 1469 | | } |
| 1470 | | |
| 1471 | | // We could use the cache hash as is no problem, however, we increase |
| 1472 | | // the likelihood of cache hits by adding the first C source file |
| 1473 | | // path name (not contents) to the hash. This way if the user is compiling |
| 1474 | | // foo.c and bar.c as separate compilations, they get different cache |
| 1475 | | // directories. |
| 1476 | | var hash = cache.hash; |
| 1477 | | if (options.c_source_files.len >= 1) { |
| 1478 | | hash.addBytes(options.c_source_files[0].src_path); |
| 1479 | | } else if (options.link_objects.len >= 1) { |
| 1480 | | hash.addBytes(options.link_objects[0].path); |
| 1481 | | } |
| 1482 | | |
| 1483 | | const digest = hash.final(); |
| 1484 | | const artifact_sub_dir = try std.fs.path.join(arena, &[_][]const u8{ "o", &digest }); |
| 1485 | | const artifact_dir = try options.local_cache_directory.handle.makeOpenPath(artifact_sub_dir, .{}); |
| 1486 | | owned_link_dir = artifact_dir; |
| 1487 | | const link_artifact_directory: Directory = .{ |
| 1488 | | .handle = artifact_dir, |
| 1489 | | .path = try options.local_cache_directory.join(arena, &[_][]const u8{artifact_sub_dir}), |
| 1490 | | }; |
| 1491 | | break :blk Emit{ |
| 1492 | | .directory = link_artifact_directory, |
| 1493 | | .sub_path = emit_bin.basename, |
| 1494 | | }; |
| 1495 | | }; |
| 1496 | | |
| 1497 | | const implib_emit: ?Emit = blk: { |
| 1498 | | const emit_implib = options.emit_implib orelse break :blk null; |
| 1499 | | |
| 1500 | | if (emit_implib.directory) |directory| { |
| 1501 | | break :blk Emit{ |
| 1502 | | .directory = directory, |
| 1503 | | .sub_path = emit_implib.basename, |
| 1504 | | }; |
| 1505 | | } |
| 1506 | | |
| 1507 | | // This is here for the same reason as in `bin_file_emit` above. |
| 1508 | | switch (cache_mode) { |
| 1509 | | .whole => break :blk null, |
| 1510 | | .incremental => {}, |
| 1511 | | } |
| 1512 | | |
| 1513 | | // Use the same directory as the bin. The CLI already emits an |
| 1514 | | // error if -fno-emit-bin is combined with -femit-implib. |
| 1515 | | break :blk Emit{ |
| 1516 | | .directory = bin_file_emit.?.directory, |
| 1517 | | .sub_path = emit_implib.basename, |
| 1518 | | }; |
| 1519 | | }; |
| 1381 | errdefer if (opt_zcu) |zcu| zcu.deinit(); |
| 1520 | 1382 | |
| 1521 | | const docs_emit: ?Emit = blk: { |
| 1522 | | const emit_docs = options.emit_docs orelse break :blk null; |
| 1523 | | |
| 1524 | | if (emit_docs.directory) |directory| { |
| 1525 | | break :blk .{ |
| 1526 | | .directory = directory, |
| 1527 | | .sub_path = emit_docs.basename, |
| 1528 | | }; |
| 1529 | | } |
| 1530 | | |
| 1531 | | // This is here for the same reason as in `bin_file_emit` above. |
| 1532 | | switch (cache_mode) { |
| 1533 | | .whole => break :blk null, |
| 1534 | | .incremental => {}, |
| 1535 | | } |
| 1536 | | |
| 1537 | | // Use the same directory as the bin, if possible. |
| 1538 | | if (bin_file_emit) |x| break :blk .{ |
| 1539 | | .directory = x.directory, |
| 1540 | | .sub_path = emit_docs.basename, |
| 1541 | | }; |
| 1542 | | |
| 1543 | | break :blk .{ |
| 1544 | | .directory = zcu.?.zig_cache_artifact_directory, |
| 1545 | | .sub_path = emit_docs.basename, |
| 1546 | | }; |
| 1547 | | }; |
| 1548 | | |
| 1549 | | // This is so that when doing `CacheMode.whole`, the mechanism in update() |
| 1550 | | // can use it for communicating the result directory via `bin_file.emit`. |
| 1551 | | // This is used to distinguish between -fno-emit-bin and -femit-bin |
| 1552 | | // for `CacheMode.whole`. |
| 1553 | | // This memory will be overwritten with the real digest in update() but |
| 1554 | | // the basename will be preserved. |
| 1555 | | const whole_bin_sub_path: ?[]u8 = try prepareWholeEmitSubPath(arena, options.emit_bin); |
| 1556 | | // Same thing but for implibs. |
| 1557 | | const whole_implib_sub_path: ?[]u8 = try prepareWholeEmitSubPath(arena, options.emit_implib); |
| 1558 | | const whole_docs_sub_path: ?[]u8 = try prepareWholeEmitSubPath(arena, options.emit_docs); |
| 1559 | | |
| 1560 | | var system_libs: std.StringArrayHashMapUnmanaged(SystemLib) = .{}; |
| 1383 | const system_libs = try std.StringArrayHashMapUnmanaged(SystemLib).init( |
| 1384 | gpa, |
| 1385 | options.system_lib_names, |
| 1386 | options.system_lib_infos, |
| 1387 | ); |
| 1561 | 1388 | errdefer system_libs.deinit(gpa); |
| 1562 | | try system_libs.ensureTotalCapacity(gpa, options.system_lib_names.len); |
| 1563 | | for (options.system_lib_names, 0..) |lib_name, i| { |
| 1564 | | system_libs.putAssumeCapacity(lib_name, options.system_lib_infos[i]); |
| 1565 | | } |
| 1566 | | |
| 1567 | | const each_lib_rpath = options.each_lib_rpath orelse |
| 1568 | | options.root_mod.resolved_target.is_native_os; |
| 1569 | 1389 | |
| 1570 | 1390 | comp.* = .{ |
| 1571 | 1391 | .gpa = gpa, |
| 1572 | 1392 | .arena = arena_allocator, |
| 1573 | | .module = zcu, |
| 1393 | .module = opt_zcu, |
| 1394 | .cache_use = undefined, // populated below |
| 1395 | .bin_file = null, // populated below |
| 1396 | .implib_emit = null, // handled below |
| 1397 | .docs_emit = null, // handled below |
| 1574 | 1398 | .root_mod = options.root_mod, |
| 1575 | 1399 | .config = options.config, |
| 1576 | | .bin_file = null, |
| 1577 | | .cache_mode = cache_mode, |
| 1578 | 1400 | .zig_lib_directory = options.zig_lib_directory, |
| 1579 | 1401 | .local_cache_directory = options.local_cache_directory, |
| 1580 | 1402 | .global_cache_directory = options.global_cache_directory, |
| 1581 | | .whole_bin_sub_path = whole_bin_sub_path, |
| 1582 | | .whole_implib_sub_path = whole_implib_sub_path, |
| 1583 | | .whole_docs_sub_path = whole_docs_sub_path, |
| 1584 | 1403 | .emit_asm = options.emit_asm, |
| 1585 | 1404 | .emit_llvm_ir = options.emit_llvm_ir, |
| 1586 | 1405 | .emit_llvm_bc = options.emit_llvm_bc, |
| ... | ... | @@ -1611,7 +1430,6 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation { |
| 1611 | 1430 | .verbose_llvm_cpu_features = options.verbose_llvm_cpu_features, |
| 1612 | 1431 | .verbose_link = options.verbose_link, |
| 1613 | 1432 | .disable_c_depfile = options.disable_c_depfile, |
| 1614 | | .owned_link_dir = owned_link_dir, |
| 1615 | 1433 | .color = options.color, |
| 1616 | 1434 | .reference_trace = options.reference_trace, |
| 1617 | 1435 | .formatted_panics = formatted_panics, |
| ... | ... | @@ -1622,8 +1440,6 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation { |
| 1622 | 1440 | .debug_compiler_runtime_libs = options.debug_compiler_runtime_libs, |
| 1623 | 1441 | .debug_compile_errors = options.debug_compile_errors, |
| 1624 | 1442 | .libcxx_abi_version = options.libcxx_abi_version, |
| 1625 | | .implib_emit = implib_emit, |
| 1626 | | .docs_emit = docs_emit, |
| 1627 | 1443 | .root_name = root_name, |
| 1628 | 1444 | .sysroot = sysroot, |
| 1629 | 1445 | .system_libs = system_libs, |
| ... | ... | @@ -1637,76 +1453,166 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation { |
| 1637 | 1453 | .no_builtin = options.no_builtin, |
| 1638 | 1454 | }; |
| 1639 | 1455 | |
| 1640 | | if (bin_file_emit) |emit| { |
| 1641 | | comp.bin_file = try link.File.open(arena, .{ |
| 1642 | | .comp = comp, |
| 1643 | | .emit = emit, |
| 1644 | | .linker_script = options.linker_script, |
| 1645 | | .z_nodelete = options.linker_z_nodelete, |
| 1646 | | .z_notext = options.linker_z_notext, |
| 1647 | | .z_defs = options.linker_z_defs, |
| 1648 | | .z_origin = options.linker_z_origin, |
| 1649 | | .z_nocopyreloc = options.linker_z_nocopyreloc, |
| 1650 | | .z_now = options.linker_z_now, |
| 1651 | | .z_relro = options.linker_z_relro, |
| 1652 | | .z_common_page_size = options.linker_z_common_page_size, |
| 1653 | | .z_max_page_size = options.linker_z_max_page_size, |
| 1654 | | .darwin_sdk_layout = libc_dirs.darwin_sdk_layout, |
| 1655 | | .frameworks = options.frameworks, |
| 1656 | | .wasi_emulated_libs = options.wasi_emulated_libs, |
| 1657 | | .lib_dirs = options.lib_dirs, |
| 1658 | | .rpath_list = options.rpath_list, |
| 1659 | | .symbol_wrap_set = options.symbol_wrap_set, |
| 1660 | | .function_sections = options.function_sections, |
| 1661 | | .data_sections = options.data_sections, |
| 1662 | | .allow_shlib_undefined = options.linker_allow_shlib_undefined, |
| 1663 | | .bind_global_refs_locally = options.linker_bind_global_refs_locally orelse false, |
| 1664 | | .compress_debug_sections = options.linker_compress_debug_sections orelse .none, |
| 1665 | | .module_definition_file = options.linker_module_definition_file, |
| 1666 | | .sort_section = options.linker_sort_section, |
| 1667 | | .import_symbols = options.linker_import_symbols, |
| 1668 | | .import_table = options.linker_import_table, |
| 1669 | | .export_table = options.linker_export_table, |
| 1670 | | .initial_memory = options.linker_initial_memory, |
| 1671 | | .max_memory = options.linker_max_memory, |
| 1672 | | .global_base = options.linker_global_base, |
| 1673 | | .export_symbol_names = options.linker_export_symbol_names, |
| 1674 | | .print_gc_sections = options.linker_print_gc_sections, |
| 1675 | | .print_icf_sections = options.linker_print_icf_sections, |
| 1676 | | .print_map = options.linker_print_map, |
| 1677 | | .tsaware = options.linker_tsaware, |
| 1678 | | .nxcompat = options.linker_nxcompat, |
| 1679 | | .dynamicbase = options.linker_dynamicbase, |
| 1680 | | .major_subsystem_version = options.major_subsystem_version, |
| 1681 | | .minor_subsystem_version = options.minor_subsystem_version, |
| 1682 | | .stack_size = options.stack_size, |
| 1683 | | .image_base = options.image_base, |
| 1684 | | .version_script = options.version_script, |
| 1685 | | .gc_sections = options.linker_gc_sections, |
| 1686 | | .eh_frame_hdr = link_eh_frame_hdr, |
| 1687 | | .emit_relocs = options.link_emit_relocs, |
| 1688 | | .rdynamic = options.rdynamic, |
| 1689 | | .soname = options.soname, |
| 1690 | | .compatibility_version = options.compatibility_version, |
| 1691 | | .dll_export_fns = dll_export_fns, |
| 1692 | | .each_lib_rpath = each_lib_rpath, |
| 1693 | | .build_id = build_id, |
| 1694 | | .disable_lld_caching = options.disable_lld_caching or cache_mode == .whole, |
| 1695 | | .subsystem = options.subsystem, |
| 1696 | | .debug_format = options.debug_format, |
| 1697 | | .hash_style = options.hash_style, |
| 1698 | | .enable_link_snapshots = options.enable_link_snapshots, |
| 1699 | | .install_name = options.install_name, |
| 1700 | | .entitlements = options.entitlements, |
| 1701 | | .pagezero_size = options.pagezero_size, |
| 1702 | | .headerpad_size = options.headerpad_size, |
| 1703 | | .headerpad_max_install_names = options.headerpad_max_install_names, |
| 1704 | | .dead_strip_dylibs = options.dead_strip_dylibs, |
| 1705 | | .force_undefined_symbols = options.force_undefined_symbols, |
| 1706 | | .pdb_source_path = options.pdb_source_path, |
| 1707 | | .pdb_out_path = options.pdb_out_path, |
| 1708 | | .entry_addr = null, // CLI does not expose this option (yet?) |
| 1709 | | }); |
| 1456 | const lf_open_opts: link.File.OpenOptions = .{ |
| 1457 | .comp = comp, |
| 1458 | .linker_script = options.linker_script, |
| 1459 | .z_nodelete = options.linker_z_nodelete, |
| 1460 | .z_notext = options.linker_z_notext, |
| 1461 | .z_defs = options.linker_z_defs, |
| 1462 | .z_origin = options.linker_z_origin, |
| 1463 | .z_nocopyreloc = options.linker_z_nocopyreloc, |
| 1464 | .z_now = options.linker_z_now, |
| 1465 | .z_relro = options.linker_z_relro, |
| 1466 | .z_common_page_size = options.linker_z_common_page_size, |
| 1467 | .z_max_page_size = options.linker_z_max_page_size, |
| 1468 | .darwin_sdk_layout = libc_dirs.darwin_sdk_layout, |
| 1469 | .frameworks = options.frameworks, |
| 1470 | .wasi_emulated_libs = options.wasi_emulated_libs, |
| 1471 | .lib_dirs = options.lib_dirs, |
| 1472 | .rpath_list = options.rpath_list, |
| 1473 | .symbol_wrap_set = options.symbol_wrap_set, |
| 1474 | .function_sections = options.function_sections, |
| 1475 | .data_sections = options.data_sections, |
| 1476 | .allow_shlib_undefined = options.linker_allow_shlib_undefined, |
| 1477 | .bind_global_refs_locally = options.linker_bind_global_refs_locally orelse false, |
| 1478 | .compress_debug_sections = options.linker_compress_debug_sections orelse .none, |
| 1479 | .module_definition_file = options.linker_module_definition_file, |
| 1480 | .sort_section = options.linker_sort_section, |
| 1481 | .import_symbols = options.linker_import_symbols, |
| 1482 | .import_table = options.linker_import_table, |
| 1483 | .export_table = options.linker_export_table, |
| 1484 | .initial_memory = options.linker_initial_memory, |
| 1485 | .max_memory = options.linker_max_memory, |
| 1486 | .global_base = options.linker_global_base, |
| 1487 | .export_symbol_names = options.linker_export_symbol_names, |
| 1488 | .print_gc_sections = options.linker_print_gc_sections, |
| 1489 | .print_icf_sections = options.linker_print_icf_sections, |
| 1490 | .print_map = options.linker_print_map, |
| 1491 | .tsaware = options.linker_tsaware, |
| 1492 | .nxcompat = options.linker_nxcompat, |
| 1493 | .dynamicbase = options.linker_dynamicbase, |
| 1494 | .major_subsystem_version = options.major_subsystem_version, |
| 1495 | .minor_subsystem_version = options.minor_subsystem_version, |
| 1496 | .stack_size = options.stack_size, |
| 1497 | .image_base = options.image_base, |
| 1498 | .version_script = options.version_script, |
| 1499 | .gc_sections = options.linker_gc_sections, |
| 1500 | .eh_frame_hdr = link_eh_frame_hdr, |
| 1501 | .emit_relocs = options.link_emit_relocs, |
| 1502 | .rdynamic = options.rdynamic, |
| 1503 | .soname = options.soname, |
| 1504 | .compatibility_version = options.compatibility_version, |
| 1505 | .dll_export_fns = dll_export_fns, |
| 1506 | .each_lib_rpath = each_lib_rpath, |
| 1507 | .build_id = build_id, |
| 1508 | .disable_lld_caching = options.disable_lld_caching or cache_mode == .whole, |
| 1509 | .subsystem = options.subsystem, |
| 1510 | .debug_format = options.debug_format, |
| 1511 | .hash_style = options.hash_style, |
| 1512 | .enable_link_snapshots = options.enable_link_snapshots, |
| 1513 | .install_name = options.install_name, |
| 1514 | .entitlements = options.entitlements, |
| 1515 | .pagezero_size = options.pagezero_size, |
| 1516 | .headerpad_size = options.headerpad_size, |
| 1517 | .headerpad_max_install_names = options.headerpad_max_install_names, |
| 1518 | .dead_strip_dylibs = options.dead_strip_dylibs, |
| 1519 | .force_undefined_symbols = options.force_undefined_symbols, |
| 1520 | .pdb_source_path = options.pdb_source_path, |
| 1521 | .pdb_out_path = options.pdb_out_path, |
| 1522 | .entry_addr = null, // CLI does not expose this option (yet?) |
| 1523 | }; |
| 1524 | |
| 1525 | switch (cache_mode) { |
| 1526 | .incremental => { |
| 1527 | // Options that are specific to zig source files, that cannot be |
| 1528 | // modified between incremental updates. |
| 1529 | var hash = cache.hash; |
| 1530 | |
| 1531 | // Synchronize with other matching comments: ZigOnlyHashStuff |
| 1532 | hash.add(use_llvm); |
| 1533 | hash.add(options.config.use_lib_llvm); |
| 1534 | hash.add(dll_export_fns); |
| 1535 | hash.add(options.config.is_test); |
| 1536 | hash.add(options.config.test_evented_io); |
| 1537 | hash.addOptionalBytes(options.test_filter); |
| 1538 | hash.addOptionalBytes(options.test_name_prefix); |
| 1539 | hash.add(options.skip_linker_dependencies); |
| 1540 | hash.add(formatted_panics); |
| 1541 | hash.add(options.emit_h != null); |
| 1542 | hash.add(error_limit); |
| 1543 | |
| 1544 | // Here we put the root source file path name, but *not* with addFile. |
| 1545 | // We want the hash to be the same regardless of the contents of the |
| 1546 | // source file, because incremental compilation will handle it, but we |
| 1547 | // do want to namespace different source file names because they are |
| 1548 | // likely different compilations and therefore this would be likely to |
| 1549 | // cause cache hits. |
| 1550 | try addModuleTableToCacheHash(gpa, arena, &hash, options.root_mod, .path_bytes); |
| 1551 | |
| 1552 | // In the case of incremental cache mode, this `artifact_directory` |
| 1553 | // is computed based on a hash of non-linker inputs, and it is where all |
| 1554 | // build artifacts are stored (even while in-progress). |
| 1555 | const digest = hash.final(); |
| 1556 | const artifact_sub_dir = "o" ++ std.fs.path.sep_str ++ digest; |
| 1557 | var artifact_dir = try options.local_cache_directory.handle.makeOpenPath(artifact_sub_dir, .{}); |
| 1558 | errdefer artifact_dir.close(); |
| 1559 | const artifact_directory: Directory = .{ |
| 1560 | .handle = artifact_dir, |
| 1561 | .path = try options.local_cache_directory.join(arena, &[_][]const u8{artifact_sub_dir}), |
| 1562 | }; |
| 1563 | |
| 1564 | const incremental = try arena.create(CacheUse.Incremental); |
| 1565 | incremental.* = .{ |
| 1566 | .artifact_directory = artifact_directory, |
| 1567 | }; |
| 1568 | comp.cache_use = .{ .incremental = incremental }; |
| 1569 | |
| 1570 | if (options.emit_bin) |emit_bin| { |
| 1571 | const emit: Emit = .{ |
| 1572 | .directory = emit_bin.directory orelse artifact_directory, |
| 1573 | .sub_path = emit_bin.basename, |
| 1574 | }; |
| 1575 | comp.bin_file = try link.File.open(arena, emit, lf_open_opts); |
| 1576 | } |
| 1577 | |
| 1578 | if (options.implib_emit) |emit_implib| { |
| 1579 | comp.implib_emit = .{ |
| 1580 | .directory = emit_implib.directory orelse artifact_directory, |
| 1581 | .sub_path = emit_implib.basename, |
| 1582 | }; |
| 1583 | } |
| 1584 | |
| 1585 | if (options.docs_emit) |emit_docs| { |
| 1586 | comp.docs_emit = .{ |
| 1587 | .directory = emit_docs.directory orelse artifact_directory, |
| 1588 | .sub_path = emit_docs.basename, |
| 1589 | }; |
| 1590 | } |
| 1591 | }, |
| 1592 | .whole => { |
| 1593 | // For whole cache mode, we don't know where to put outputs from |
| 1594 | // the linker until the final cache hash, which is available after |
| 1595 | // the compilation is complete. |
| 1596 | // |
| 1597 | // Therefore, bin_file is left null until the beginning of update(), |
| 1598 | // where it may find a cache hit, or use a temporary directory to |
| 1599 | // hold output artifacts. |
| 1600 | const whole = try arena.create(CacheUse.Whole); |
| 1601 | whole.* = .{ |
| 1602 | // This is kept here so that link.File.open can be called later. |
| 1603 | .lf_open_opts = lf_open_opts, |
| 1604 | // This is so that when doing `CacheMode.whole`, the mechanism in update() |
| 1605 | // can use it for communicating the result directory via `bin_file.emit`. |
| 1606 | // This is used to distinguish between -fno-emit-bin and -femit-bin |
| 1607 | // for `CacheMode.whole`. |
| 1608 | // This memory will be overwritten with the real digest in update() but |
| 1609 | // the basename will be preserved. |
| 1610 | .bin_sub_path = try prepareWholeEmitSubPath(arena, options.emit_bin), |
| 1611 | .implib_sub_path = try prepareWholeEmitSubPath(arena, options.emit_implib), |
| 1612 | .docs_sub_path = try prepareWholeEmitSubPath(arena, options.emit_docs), |
| 1613 | }; |
| 1614 | comp.cache_use = .{ .whole = whole }; |
| 1615 | }, |
| 1710 | 1616 | } |
| 1711 | 1617 | |
| 1712 | 1618 | break :comp comp; |
| ... | ... | @@ -1981,7 +1887,6 @@ pub fn destroy(self: *Compilation) void { |
| 1981 | 1887 | self.clearMiscFailures(); |
| 1982 | 1888 | |
| 1983 | 1889 | self.cache_parent.manifest_dir.close(); |
| 1984 | | if (self.owned_link_dir) |*dir| dir.close(); |
| 1985 | 1890 | |
| 1986 | 1891 | // This destroys `self`. |
| 1987 | 1892 | var arena_instance = self.arena; |
| ... | ... | @@ -2001,30 +1906,6 @@ pub fn getTarget(self: Compilation) Target { |
| 2001 | 1906 | return self.root_mod.resolved_target.result; |
| 2002 | 1907 | } |
| 2003 | 1908 | |
| 2004 | | fn restorePrevZigCacheArtifactDirectory(comp: *Compilation, directory: *Directory) void { |
| 2005 | | if (directory.path) |p| comp.gpa.free(p); |
| 2006 | | |
| 2007 | | // Restore the Module's previous zig_cache_artifact_directory |
| 2008 | | // This is only for cleanup purposes; Module.deinit calls close |
| 2009 | | // on the handle of zig_cache_artifact_directory. |
| 2010 | | if (comp.module) |module| { |
| 2011 | | const builtin_mod = module.main_mod.deps.get("builtin").?; |
| 2012 | | module.zig_cache_artifact_directory = builtin_mod.root.root_dir; |
| 2013 | | } |
| 2014 | | } |
| 2015 | | |
| 2016 | | fn cleanupTmpArtifactDirectory( |
| 2017 | | comp: *Compilation, |
| 2018 | | tmp_artifact_directory: *?Directory, |
| 2019 | | tmp_dir_sub_path: []const u8, |
| 2020 | | ) void { |
| 2021 | | comp.gpa.free(tmp_dir_sub_path); |
| 2022 | | if (tmp_artifact_directory.*) |*directory| { |
| 2023 | | directory.handle.close(); |
| 2024 | | restorePrevZigCacheArtifactDirectory(comp, directory); |
| 2025 | | } |
| 2026 | | } |
| 2027 | | |
| 2028 | 1909 | pub fn hotCodeSwap(comp: *Compilation, prog_node: *std.Progress.Node, pid: std.ChildProcess.Id) !void { |
| 2029 | 1910 | comp.bin_file.child_pid = pid; |
| 2030 | 1911 | try comp.makeBinFileWritable(); |
| ... | ... | @@ -2032,6 +1913,27 @@ pub fn hotCodeSwap(comp: *Compilation, prog_node: *std.Progress.Node, pid: std.C |
| 2032 | 1913 | try comp.makeBinFileExecutable(); |
| 2033 | 1914 | } |
| 2034 | 1915 | |
| 1916 | fn cleanupAfterUpdate(comp: *Compilation) void { |
| 1917 | switch (comp) { |
| 1918 | .incremental => return, |
| 1919 | .whole => |whole| { |
| 1920 | if (whole.cache_manifest) |man| { |
| 1921 | man.deinit(); |
| 1922 | whole.cache_manifest = null; |
| 1923 | } |
| 1924 | if (comp.bin_file) |lf| { |
| 1925 | lf.destroy(); |
| 1926 | comp.bin_file = null; |
| 1927 | } |
| 1928 | if (whole.tmp_artifact_directory) |directory| { |
| 1929 | directory.handle.close(); |
| 1930 | if (directory.path) |p| comp.gpa.free(p); |
| 1931 | whole.tmp_artifact_directory = null; |
| 1932 | } |
| 1933 | }, |
| 1934 | } |
| 1935 | } |
| 1936 | |
| 2035 | 1937 | /// Detect changes to source files, perform semantic analysis, and update the output files. |
| 2036 | 1938 | pub fn update(comp: *Compilation, main_progress_node: *std.Progress.Node) !void { |
| 2037 | 1939 | const tracy_trace = trace(@src()); |
| ... | ... | @@ -2041,92 +1943,91 @@ pub fn update(comp: *Compilation, main_progress_node: *std.Progress.Node) !void |
| 2041 | 1943 | comp.last_update_was_cache_hit = false; |
| 2042 | 1944 | |
| 2043 | 1945 | var man: Cache.Manifest = undefined; |
| 2044 | | defer if (comp.whole_cache_manifest != null) man.deinit(); |
| 1946 | defer cleanupAfterUpdate(comp); |
| 2045 | 1947 | |
| 2046 | | var tmp_dir_sub_path: []const u8 = &.{}; |
| 2047 | | var tmp_artifact_directory: ?Directory = null; |
| 2048 | | defer cleanupTmpArtifactDirectory(comp, &tmp_artifact_directory, tmp_dir_sub_path); |
| 1948 | var tmp_dir_rand_int: u64 = undefined; |
| 2049 | 1949 | |
| 2050 | 1950 | // If using the whole caching strategy, we check for *everything* up front, including |
| 2051 | 1951 | // C source files. |
| 2052 | | if (comp.cache_mode == .whole) { |
| 2053 | | // We are about to obtain this lock, so here we give other processes a chance first. |
| 2054 | | if (comp.bin_file) |lf| lf.releaseLock(); |
| 2055 | | |
| 2056 | | man = comp.cache_parent.obtain(); |
| 2057 | | comp.whole_cache_manifest = &man; |
| 2058 | | try comp.addNonIncrementalStuffToCacheManifest(&man); |
| 2059 | | |
| 2060 | | const is_hit = man.hit() catch |err| { |
| 2061 | | // TODO properly bubble these up instead of emitting a warning |
| 2062 | | const i = man.failed_file_index orelse return err; |
| 2063 | | const pp = man.files.items[i].prefixed_path orelse return err; |
| 2064 | | const prefix = man.cache.prefixes()[pp.prefix].path orelse ""; |
| 2065 | | std.log.warn("{s}: {s}{s}", .{ @errorName(err), prefix, pp.sub_path }); |
| 2066 | | return err; |
| 2067 | | }; |
| 2068 | | if (is_hit) { |
| 2069 | | comp.last_update_was_cache_hit = true; |
| 2070 | | log.debug("CacheMode.whole cache hit for {s}", .{comp.root_name}); |
| 2071 | | const digest = man.final(); |
| 2072 | | |
| 2073 | | comp.wholeCacheModeSetBinFilePath(&digest); |
| 1952 | switch (comp.cache_use) { |
| 1953 | .whole => |whole| { |
| 1954 | // We are about to obtain this lock, so here we give other processes a chance first. |
| 1955 | assert(comp.bin_file == null); |
| 1956 | |
| 1957 | man = comp.cache_parent.obtain(); |
| 1958 | whole.cache_manifest = &man; |
| 1959 | try comp.addNonIncrementalStuffToCacheManifest(&man); |
| 1960 | |
| 1961 | const is_hit = man.hit() catch |err| { |
| 1962 | // TODO properly bubble these up instead of emitting a warning |
| 1963 | const i = man.failed_file_index orelse return err; |
| 1964 | const pp = man.files.items[i].prefixed_path orelse return err; |
| 1965 | const prefix = man.cache.prefixes()[pp.prefix].path orelse ""; |
| 1966 | std.log.warn("{s}: {s}{s}", .{ @errorName(err), prefix, pp.sub_path }); |
| 1967 | return err; |
| 1968 | }; |
| 1969 | if (is_hit) { |
| 1970 | comp.last_update_was_cache_hit = true; |
| 1971 | log.debug("CacheMode.whole cache hit for {s}", .{comp.root_name}); |
| 1972 | const digest = man.final(); |
| 2074 | 1973 | |
| 2075 | | assert(comp.bin_file.lock == null); |
| 2076 | | comp.bin_file.lock = man.toOwnedLock(); |
| 2077 | | return; |
| 2078 | | } |
| 2079 | | log.debug("CacheMode.whole cache miss for {s}", .{comp.root_name}); |
| 1974 | comp.wholeCacheModeSetBinFilePath(&digest); |
| 2080 | 1975 | |
| 2081 | | // Initialize `bin_file.emit` with a temporary Directory so that compilation can |
| 2082 | | // continue on the same path as incremental, using the temporary Directory. |
| 2083 | | tmp_artifact_directory = d: { |
| 2084 | | const s = std.fs.path.sep_str; |
| 2085 | | const rand_int = std.crypto.random.int(u64); |
| 1976 | assert(comp.bin_file.lock == null); |
| 1977 | comp.bin_file.lock = man.toOwnedLock(); |
| 1978 | return; |
| 1979 | } |
| 1980 | log.debug("CacheMode.whole cache miss for {s}", .{comp.root_name}); |
| 2086 | 1981 | |
| 2087 | | tmp_dir_sub_path = try std.fmt.allocPrint(comp.gpa, "tmp" ++ s ++ "{x}", .{rand_int}); |
| 1982 | // Compile the artifacts to a temporary directory. |
| 1983 | const tmp_artifact_directory = d: { |
| 1984 | const s = std.fs.path.sep_str; |
| 1985 | tmp_dir_rand_int = std.crypto.random.int(u64); |
| 1986 | const tmp_dir_sub_path = "tmp" ++ s ++ Package.Manifest.hex64(tmp_dir_rand_int); |
| 2088 | 1987 | |
| 2089 | | const path = try comp.local_cache_directory.join(comp.gpa, &.{tmp_dir_sub_path}); |
| 2090 | | errdefer comp.gpa.free(path); |
| 1988 | const path = try comp.local_cache_directory.join(comp.gpa, &.{tmp_dir_sub_path}); |
| 1989 | errdefer comp.gpa.free(path); |
| 2091 | 1990 | |
| 2092 | | const handle = try comp.local_cache_directory.handle.makeOpenPath(tmp_dir_sub_path, .{}); |
| 2093 | | errdefer handle.close(); |
| 1991 | const handle = try comp.local_cache_directory.handle.makeOpenPath(tmp_dir_sub_path, .{}); |
| 1992 | errdefer handle.close(); |
| 2094 | 1993 | |
| 2095 | | break :d .{ |
| 2096 | | .path = path, |
| 2097 | | .handle = handle, |
| 1994 | break :d .{ |
| 1995 | .path = path, |
| 1996 | .handle = handle, |
| 1997 | }; |
| 2098 | 1998 | }; |
| 2099 | | }; |
| 1999 | whole.tmp_artifact_directory = tmp_artifact_directory; |
| 2100 | 2000 | |
| 2101 | | // This updates the output directory for linker outputs. |
| 2102 | | if (comp.module) |module| { |
| 2103 | | module.zig_cache_artifact_directory = tmp_artifact_directory.?; |
| 2104 | | } |
| 2001 | // Now that the directory is known, it is time to create the Emit |
| 2002 | // objects and call link.File.open. |
| 2105 | 2003 | |
| 2106 | | // This resets the link.File to operate as if we called openPath() in create() |
| 2107 | | // instead of simulating -fno-emit-bin. |
| 2108 | | var options = comp.bin_file.options.move(); |
| 2109 | | if (comp.whole_bin_sub_path) |sub_path| { |
| 2110 | | options.emit = .{ |
| 2111 | | .directory = tmp_artifact_directory.?, |
| 2112 | | .sub_path = std.fs.path.basename(sub_path), |
| 2113 | | }; |
| 2114 | | } |
| 2115 | | if (comp.whole_implib_sub_path) |sub_path| { |
| 2116 | | options.implib_emit = .{ |
| 2117 | | .directory = tmp_artifact_directory.?, |
| 2118 | | .sub_path = std.fs.path.basename(sub_path), |
| 2119 | | }; |
| 2120 | | } |
| 2121 | | if (comp.whole_docs_sub_path) |sub_path| { |
| 2122 | | options.docs_emit = .{ |
| 2123 | | .directory = tmp_artifact_directory.?, |
| 2124 | | .sub_path = std.fs.path.basename(sub_path), |
| 2125 | | }; |
| 2126 | | } |
| 2127 | | var old_bin_file = comp.bin_file; |
| 2128 | | comp.bin_file = try link.File.openPath(comp.gpa, options); |
| 2129 | | old_bin_file.destroy(); |
| 2004 | if (comp.whole_implib_sub_path) |sub_path| { |
| 2005 | comp.implib_emit = .{ |
| 2006 | .directory = tmp_artifact_directory, |
| 2007 | .sub_path = std.fs.path.basename(sub_path), |
| 2008 | }; |
| 2009 | } |
| 2010 | |
| 2011 | if (comp.whole_docs_sub_path) |sub_path| { |
| 2012 | comp.docs_emit = .{ |
| 2013 | .directory = tmp_artifact_directory, |
| 2014 | .sub_path = std.fs.path.basename(sub_path), |
| 2015 | }; |
| 2016 | } |
| 2017 | |
| 2018 | if (comp.whole_bin_sub_path) |sub_path| { |
| 2019 | const emit: Emit = .{ |
| 2020 | .directory = tmp_artifact_directory, |
| 2021 | .sub_path = std.fs.path.basename(sub_path), |
| 2022 | }; |
| 2023 | // It's a bit strange to use the Compilation arena allocator here |
| 2024 | // but in practice it won't leak much and usually whole cache mode |
| 2025 | // will be combined with exactly one call to update(). |
| 2026 | const arena = comp.arena.allocator(); |
| 2027 | comp.bin_file = try link.File.open(arena, emit, whole.lf_open_opts); |
| 2028 | } |
| 2029 | }, |
| 2030 | .incremental => {}, |
| 2130 | 2031 | } |
| 2131 | 2032 | |
| 2132 | 2033 | // For compiling C objects, we rely on the cache hash system to avoid duplicating work. |
| ... | ... | @@ -2231,80 +2132,43 @@ pub fn update(comp: *Compilation, main_progress_node: *std.Progress.Node) !void |
| 2231 | 2132 | // Flush takes care of -femit-bin, but we still have -femit-llvm-ir, -femit-llvm-bc, and |
| 2232 | 2133 | // -femit-asm to handle, in the case of C objects. |
| 2233 | 2134 | comp.emitOthers(); |
| 2135 | try comp.flush(main_progress_node); |
| 2136 | if (comp.totalErrorCount() != 0) return; |
| 2137 | try maybeGenerateAutodocs(comp, main_progress_node); |
| 2234 | 2138 | |
| 2235 | | if (comp.whole_cache_manifest != null) { |
| 2236 | | const digest = man.final(); |
| 2237 | | |
| 2238 | | // Rename the temporary directory into place. |
| 2239 | | var directory = tmp_artifact_directory.?; |
| 2240 | | tmp_artifact_directory = null; |
| 2241 | | |
| 2242 | | directory.handle.close(); |
| 2243 | | defer restorePrevZigCacheArtifactDirectory(comp, &directory); |
| 2244 | | |
| 2245 | | const o_sub_path = try std.fs.path.join(comp.gpa, &[_][]const u8{ "o", &digest }); |
| 2246 | | defer comp.gpa.free(o_sub_path); |
| 2247 | | |
| 2248 | | // Work around windows `AccessDenied` if any files within this directory are open |
| 2249 | | // by closing and reopening the file handles. |
| 2250 | | const need_writable_dance = builtin.os.tag == .windows and comp.bin_file.file != null; |
| 2251 | | if (need_writable_dance) { |
| 2252 | | // We cannot just call `makeExecutable` as it makes a false assumption that we have a |
| 2253 | | // file handle open only when linking an executable file. This used to be true when |
| 2254 | | // our linkers were incapable of emitting relocatables and static archive. Now that |
| 2255 | | // they are capable, we need to unconditionally close the file handle and re-open it |
| 2256 | | // in the follow up call to `makeWritable`. |
| 2257 | | comp.bin_file.file.?.close(); |
| 2258 | | comp.bin_file.file = null; |
| 2259 | | } |
| 2260 | | |
| 2261 | | try comp.bin_file.renameTmpIntoCache(comp.local_cache_directory, tmp_dir_sub_path, o_sub_path); |
| 2262 | | comp.wholeCacheModeSetBinFilePath(&digest); |
| 2139 | switch (comp.cache_use) { |
| 2140 | .whole => |whole| { |
| 2141 | const digest = man.final(); |
| 2263 | 2142 | |
| 2264 | | // Has to be after the `wholeCacheModeSetBinFilePath` above. |
| 2265 | | if (need_writable_dance) { |
| 2266 | | try comp.bin_file.makeWritable(); |
| 2267 | | } |
| 2143 | // Rename the temporary directory into place. |
| 2144 | // Close tmp dir and link.File to avoid open handle during rename. |
| 2145 | if (whole.tmp_artifact_directory) |tmp_directory| { |
| 2146 | tmp_directory.handle.close(); |
| 2147 | if (tmp_directory.path) |p| comp.gpa.free(p); |
| 2148 | whole.tmp_artifact_directory = null; |
| 2149 | } else unreachable; |
| 2150 | |
| 2151 | if (comp.bin_file) |lf| { |
| 2152 | lf.destroy(); |
| 2153 | comp.bin_file = null; |
| 2154 | } |
| 2268 | 2155 | |
| 2269 | | // This is intentionally sandwiched between renameTmpIntoCache() and writeManifest(). |
| 2270 | | if (comp.module) |module| { |
| 2271 | | // We need to set the zig_cache_artifact_directory for -femit-asm, -femit-llvm-ir, |
| 2272 | | // etc to know where to output to. |
| 2273 | | var artifact_dir = try comp.local_cache_directory.handle.openDir(o_sub_path, .{}); |
| 2274 | | defer artifact_dir.close(); |
| 2156 | const s = std.fs.path.sep_str; |
| 2157 | const tmp_dir_sub_path = "tmp" ++ s ++ Package.Manifest.hex64(tmp_dir_rand_int); |
| 2158 | const o_sub_path = "o" ++ s ++ digest; |
| 2275 | 2159 | |
| 2276 | | const dir_path = try comp.local_cache_directory.join(comp.gpa, &.{o_sub_path}); |
| 2277 | | defer comp.gpa.free(dir_path); |
| 2160 | try renameTmpIntoCache(comp.local_cache_directory, tmp_dir_sub_path, o_sub_path); |
| 2161 | comp.wholeCacheModeSetBinFilePath(&digest); |
| 2278 | 2162 | |
| 2279 | | module.zig_cache_artifact_directory = .{ |
| 2280 | | .handle = artifact_dir, |
| 2281 | | .path = dir_path, |
| 2163 | // Failure here only means an unnecessary cache miss. |
| 2164 | man.writeManifest() catch |err| { |
| 2165 | log.warn("failed to write cache manifest: {s}", .{@errorName(err)}); |
| 2282 | 2166 | }; |
| 2283 | 2167 | |
| 2284 | | try comp.flush(main_progress_node); |
| 2285 | | if (comp.totalErrorCount() != 0) return; |
| 2286 | | |
| 2287 | | // Note the placement of this logic is relying on the call to |
| 2288 | | // `wholeCacheModeSetBinFilePath` above. |
| 2289 | | try maybeGenerateAutodocs(comp, main_progress_node); |
| 2290 | | } else { |
| 2291 | | try comp.flush(main_progress_node); |
| 2292 | | if (comp.totalErrorCount() != 0) return; |
| 2293 | | } |
| 2294 | | |
| 2295 | | // Failure here only means an unnecessary cache miss. |
| 2296 | | man.writeManifest() catch |err| { |
| 2297 | | log.warn("failed to write cache manifest: {s}", .{@errorName(err)}); |
| 2298 | | }; |
| 2299 | | |
| 2300 | | assert(comp.bin_file.lock == null); |
| 2301 | | comp.bin_file.lock = man.toOwnedLock(); |
| 2302 | | } else { |
| 2303 | | try comp.flush(main_progress_node); |
| 2304 | | |
| 2305 | | if (comp.totalErrorCount() == 0) { |
| 2306 | | try maybeGenerateAutodocs(comp, main_progress_node); |
| 2307 | | } |
| 2168 | assert(comp.bin_file.lock == null); |
| 2169 | comp.bin_file.lock = man.toOwnedLock(); |
| 2170 | }, |
| 2171 | .incremental => {}, |
| 2308 | 2172 | } |
| 2309 | 2173 | |
| 2310 | 2174 | // Unload all source files to save memory. |
| ... | ... | @@ -2321,6 +2185,60 @@ pub fn update(comp: *Compilation, main_progress_node: *std.Progress.Node) !void |
| 2321 | 2185 | } |
| 2322 | 2186 | } |
| 2323 | 2187 | |
| 2188 | /// This function is called by the frontend before flush(). It communicates that |
| 2189 | /// `options.bin_file.emit` directory needs to be renamed from |
| 2190 | /// `[zig-cache]/tmp/[random]` to `[zig-cache]/o/[digest]`. |
| 2191 | /// The frontend would like to simply perform a file system rename, however, |
| 2192 | /// some linker backends care about the file paths of the objects they are linking. |
| 2193 | /// So this function call tells linker backends to rename the paths of object files |
| 2194 | /// to observe the new directory path. |
| 2195 | /// Linker backends which do not have this requirement can fall back to the simple |
| 2196 | /// implementation at the bottom of this function. |
| 2197 | /// This function is only called when CacheMode is `whole`. |
| 2198 | fn renameTmpIntoCache( |
| 2199 | cache_directory: Compilation.Directory, |
| 2200 | tmp_dir_sub_path: []const u8, |
| 2201 | o_sub_path: []const u8, |
| 2202 | ) !void { |
| 2203 | while (true) { |
| 2204 | if (builtin.os.tag == .windows) { |
| 2205 | // Work around windows `renameW` can't fail with `PathAlreadyExists` |
| 2206 | // See https://github.com/ziglang/zig/issues/8362 |
| 2207 | if (cache_directory.handle.access(o_sub_path, .{})) |_| { |
| 2208 | try cache_directory.handle.deleteTree(o_sub_path); |
| 2209 | continue; |
| 2210 | } else |err| switch (err) { |
| 2211 | error.FileNotFound => {}, |
| 2212 | else => |e| return e, |
| 2213 | } |
| 2214 | std.fs.rename( |
| 2215 | cache_directory.handle, |
| 2216 | tmp_dir_sub_path, |
| 2217 | cache_directory.handle, |
| 2218 | o_sub_path, |
| 2219 | ) catch |err| { |
| 2220 | log.err("unable to rename cache dir {s} to {s}: {s}", .{ tmp_dir_sub_path, o_sub_path, @errorName(err) }); |
| 2221 | return err; |
| 2222 | }; |
| 2223 | break; |
| 2224 | } else { |
| 2225 | std.fs.rename( |
| 2226 | cache_directory.handle, |
| 2227 | tmp_dir_sub_path, |
| 2228 | cache_directory.handle, |
| 2229 | o_sub_path, |
| 2230 | ) catch |err| switch (err) { |
| 2231 | error.PathAlreadyExists => { |
| 2232 | try cache_directory.handle.deleteTree(o_sub_path); |
| 2233 | continue; |
| 2234 | }, |
| 2235 | else => |e| return e, |
| 2236 | }; |
| 2237 | break; |
| 2238 | } |
| 2239 | } |
| 2240 | } |
| 2241 | |
| 2324 | 2242 | fn maybeGenerateAutodocs(comp: *Compilation, prog_node: *std.Progress.Node) !void { |
| 2325 | 2243 | const mod = comp.module orelse return; |
| 2326 | 2244 | // TODO: do this in a separate job during performAllTheWork(). The |
| ... | ... | @@ -2362,7 +2280,7 @@ fn wholeCacheModeSetBinFilePath(comp: *Compilation, digest: *const [Cache.hex_di |
| 2362 | 2280 | if (comp.whole_bin_sub_path) |sub_path| { |
| 2363 | 2281 | @memcpy(sub_path[digest_start..][0..digest.len], digest); |
| 2364 | 2282 | |
| 2365 | | comp.bin_file.options.emit = .{ |
| 2283 | comp.bin_file.?.emit = .{ |
| 2366 | 2284 | .directory = comp.local_cache_directory, |
| 2367 | 2285 | .sub_path = sub_path, |
| 2368 | 2286 | }; |
| ... | ... | @@ -2371,7 +2289,7 @@ fn wholeCacheModeSetBinFilePath(comp: *Compilation, digest: *const [Cache.hex_di |
| 2371 | 2289 | if (comp.whole_implib_sub_path) |sub_path| { |
| 2372 | 2290 | @memcpy(sub_path[digest_start..][0..digest.len], digest); |
| 2373 | 2291 | |
| 2374 | | comp.bin_file.options.implib_emit = .{ |
| 2292 | comp.implib_emit = .{ |
| 2375 | 2293 | .directory = comp.local_cache_directory, |
| 2376 | 2294 | .sub_path = sub_path, |
| 2377 | 2295 | }; |
| ... | ... | @@ -2380,7 +2298,7 @@ fn wholeCacheModeSetBinFilePath(comp: *Compilation, digest: *const [Cache.hex_di |
| 2380 | 2298 | if (comp.whole_docs_sub_path) |sub_path| { |
| 2381 | 2299 | @memcpy(sub_path[digest_start..][0..digest.len], digest); |
| 2382 | 2300 | |
| 2383 | | comp.bin_file.options.docs_emit = .{ |
| 2301 | comp.docs_emit = .{ |
| 2384 | 2302 | .directory = comp.local_cache_directory, |
| 2385 | 2303 | .sub_path = sub_path, |
| 2386 | 2304 | }; |