| ... | @@ -41,8 +41,8 @@ gpa: Allocator, | ... | @@ -41,8 +41,8 @@ gpa: Allocator, |
| 41 | arena_state: std.heap.ArenaAllocator.State, | 41 | arena_state: std.heap.ArenaAllocator.State, |
| 42 | bin_file: *link.File, | 42 | bin_file: *link.File, |
| 43 | c_object_table: std.AutoArrayHashMapUnmanaged(*CObject, void) = .{}, | 43 | c_object_table: std.AutoArrayHashMapUnmanaged(*CObject, void) = .{}, |
| 44 | stage1_lock: ?Cache.Lock = null, | 44 | /// This is a pointer to a local variable inside `update()`. |
| 45 | stage1_cache_manifest: *Cache.Manifest = undefined, | 45 | whole_cache_manifest: ?*Cache.Manifest = null, |
| 46 | | 46 | |
| 47 | link_error_flags: link.File.ErrorFlags = .{}, | 47 | link_error_flags: link.File.ErrorFlags = .{}, |
| 48 | | 48 | |
| ... | @@ -98,6 +98,9 @@ clang_argv: []const []const u8, | ... | @@ -98,6 +98,9 @@ clang_argv: []const []const u8, |
| 98 | cache_parent: *Cache, | 98 | cache_parent: *Cache, |
| 99 | /// Path to own executable for invoking `zig clang`. | 99 | /// Path to own executable for invoking `zig clang`. |
| 100 | self_exe_path: ?[]const u8, | 100 | self_exe_path: ?[]const u8, |
| | 101 | /// null means -fno-emit-bin. Contains the basename of the |
| | 102 | /// outputted binary file in case we don't know the directory yet. |
| | 103 | whole_bin_basename: ?[]const u8, |
| 101 | zig_lib_directory: Directory, | 104 | zig_lib_directory: Directory, |
| 102 | local_cache_directory: Directory, | 105 | local_cache_directory: Directory, |
| 103 | global_cache_directory: Directory, | 106 | global_cache_directory: Directory, |
| ... | @@ -612,6 +615,15 @@ pub const Directory = struct { | ... | @@ -612,6 +615,15 @@ pub const Directory = struct { |
| 612 | return std.fs.path.joinZ(allocator, paths); | 615 | return std.fs.path.joinZ(allocator, paths); |
| 613 | } | 616 | } |
| 614 | } | 617 | } |
| | 618 | |
| | 619 | /// Whether or not the handle should be closed, or the path should be freed |
| | 620 | /// is determined by usage, however this function is provided for convenience |
| | 621 | /// if it happens to be what the caller needs. |
| | 622 | pub fn closeAndFree(self: *Directory, gpa: Allocator) void { |
| | 623 | self.handle.close(); |
| | 624 | if (self.path) |p| gpa.free(p); |
| | 625 | self.* = undefined; |
| | 626 | } |
| 615 | }; | 627 | }; |
| 616 | | 628 | |
| 617 | pub const EmitLoc = struct { | 629 | pub const EmitLoc = struct { |
| ... | @@ -631,6 +643,7 @@ pub const ClangPreprocessorMode = enum { | ... | @@ -631,6 +643,7 @@ pub const ClangPreprocessorMode = enum { |
| 631 | }; | 643 | }; |
| 632 | | 644 | |
| 633 | pub const SystemLib = link.SystemLib; | 645 | pub const SystemLib = link.SystemLib; |
| | 646 | pub const CacheMode = link.CacheMode; |
| 634 | | 647 | |
| 635 | pub const InitOptions = struct { | 648 | pub const InitOptions = struct { |
| 636 | zig_lib_directory: Directory, | 649 | zig_lib_directory: Directory, |
| ... | @@ -668,6 +681,7 @@ pub const InitOptions = struct { | ... | @@ -668,6 +681,7 @@ pub const InitOptions = struct { |
| 668 | /// is externally modified - essentially anything other than zig-cache - then | 681 | /// is externally modified - essentially anything other than zig-cache - then |
| 669 | /// this flag would be set to disable this machinery to avoid false positives. | 682 | /// this flag would be set to disable this machinery to avoid false positives. |
| 670 | disable_lld_caching: bool = false, | 683 | disable_lld_caching: bool = false, |
| | 684 | cache_mode: CacheMode = .incremental, |
| 671 | object_format: ?std.Target.ObjectFormat = null, | 685 | object_format: ?std.Target.ObjectFormat = null, |
| 672 | optimize_mode: std.builtin.Mode = .Debug, | 686 | optimize_mode: std.builtin.Mode = .Debug, |
| 673 | keep_source_files_loaded: bool = false, | 687 | keep_source_files_loaded: bool = false, |
| ... | @@ -885,6 +899,8 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation { | ... | @@ -885,6 +899,8 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation { |
| 885 | break :blk build_options.is_stage1; | 899 | break :blk build_options.is_stage1; |
| 886 | }; | 900 | }; |
| 887 | | 901 | |
| | 902 | const cache_mode = if (use_stage1) CacheMode.whole else options.cache_mode; |
| | 903 | |
| 888 | // Make a decision on whether to use LLVM or our own backend. | 904 | // Make a decision on whether to use LLVM or our own backend. |
| 889 | const use_llvm = build_options.have_llvm and blk: { | 905 | const use_llvm = build_options.have_llvm and blk: { |
| 890 | if (options.use_llvm) |explicit| | 906 | if (options.use_llvm) |explicit| |
| ... | @@ -1219,18 +1235,26 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation { | ... | @@ -1219,18 +1235,26 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation { |
| 1219 | // modified between incremental updates. | 1235 | // modified between incremental updates. |
| 1220 | var hash = cache.hash; | 1236 | var hash = cache.hash; |
| 1221 | | 1237 | |
| 1222 | // Here we put the root source file path name, but *not* with addFile. We want the | 1238 | switch (cache_mode) { |
| 1223 | // hash to be the same regardless of the contents of the source file, because | 1239 | .incremental => { |
| 1224 | // incremental compilation will handle it, but we do want to namespace different | 1240 | // Here we put the root source file path name, but *not* with addFile. |
| 1225 | // source file names because they are likely different compilations and therefore this | 1241 | // We want the hash to be the same regardless of the contents of the |
| 1226 | // would be likely to cause cache hits. | 1242 | // source file, because incremental compilation will handle it, but we |
| 1227 | hash.addBytes(main_pkg.root_src_path); | 1243 | // do want to namespace different source file names because they are |
| 1228 | hash.addOptionalBytes(main_pkg.root_src_directory.path); | 1244 | // likely different compilations and therefore this would be likely to |
| 1229 | { | 1245 | // cause cache hits. |
| 1230 | var local_arena = std.heap.ArenaAllocator.init(gpa); | 1246 | hash.addBytes(main_pkg.root_src_path); |
| 1231 | defer local_arena.deinit(); | 1247 | hash.addOptionalBytes(main_pkg.root_src_directory.path); |
| 1232 | var seen_table = std.AutoHashMap(*Package, void).init(local_arena.allocator()); | 1248 | { |
| 1233 | try addPackageTableToCacheHash(&hash, &local_arena, main_pkg.table, &seen_table, .path_bytes); | 1249 | var seen_table = std.AutoHashMap(*Package, void).init(arena); |
| | 1250 | try addPackageTableToCacheHash(&hash, &arena_allocator, main_pkg.table, &seen_table, .path_bytes); |
| | 1251 | } |
| | 1252 | }, |
| | 1253 | .whole => { |
| | 1254 | // In this case, we postpone adding the input source file until |
| | 1255 | // we create the cache manifest, in update(), because we want to |
| | 1256 | // track it and packages as files. |
| | 1257 | }, |
| 1234 | } | 1258 | } |
| 1235 | hash.add(valgrind); | 1259 | hash.add(valgrind); |
| 1236 | hash.add(single_threaded); | 1260 | hash.add(single_threaded); |
| ... | @@ -1238,9 +1262,35 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation { | ... | @@ -1238,9 +1262,35 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation { |
| 1238 | hash.add(use_llvm); | 1262 | hash.add(use_llvm); |
| 1239 | hash.add(dll_export_fns); | 1263 | hash.add(dll_export_fns); |
| 1240 | hash.add(options.is_test); | 1264 | hash.add(options.is_test); |
| | 1265 | hash.add(options.test_evented_io); |
| | 1266 | hash.addOptionalBytes(options.test_filter); |
| | 1267 | hash.addOptionalBytes(options.test_name_prefix); |
| 1241 | hash.add(options.skip_linker_dependencies); | 1268 | hash.add(options.skip_linker_dependencies); |
| 1242 | hash.add(options.parent_compilation_link_libc); | 1269 | hash.add(options.parent_compilation_link_libc); |
| 1243 | | 1270 | |
| | 1271 | // In the case of incremental cache mode, this `zig_cache_artifact_directory` |
| | 1272 | // is computed based on a hash of non-linker inputs, and it is where all |
| | 1273 | // build artifacts are stored (even while in-progress). |
| | 1274 | // |
| | 1275 | // For whole cache mode, it is still used for builtin.zig so that the file |
| | 1276 | // path to builtin.zig can remain consistent during a debugging session at |
| | 1277 | // runtime. However, we don't know where to put outputs from the linker |
| | 1278 | // or stage1 backend object files until the final cache hash, which is available |
| | 1279 | // after the compilation is complete. |
| | 1280 | // |
| | 1281 | // Therefore, in whole cache mode, we additionally create a temporary cache |
| | 1282 | // directory for these two kinds of build artifacts, and then rename it |
| | 1283 | // into place after the final hash is known. However, we don't want |
| | 1284 | // to create the temporary directory here, because in the case of a cache hit, |
| | 1285 | // this would have been wasted syscalls to make the directory and then not |
| | 1286 | // use it (or delete it). |
| | 1287 | // |
| | 1288 | // In summary, for whole cache mode, we simulate `-fno-emit-bin` in this |
| | 1289 | // function, and `zig_cache_artifact_directory` is *wrong* except for builtin.zig, |
| | 1290 | // and then at the beginning of `update()` when we find out whether we need |
| | 1291 | // a temporary directory, we patch up all the places that the incorrect |
| | 1292 | // `zig_cache_artifact_directory` was passed to various components of the compiler. |
| | 1293 | |
| 1244 | const digest = hash.final(); | 1294 | const digest = hash.final(); |
| 1245 | const artifact_sub_dir = try std.fs.path.join(arena, &[_][]const u8{ "o", &digest }); | 1295 | const artifact_sub_dir = try std.fs.path.join(arena, &[_][]const u8{ "o", &digest }); |
| 1246 | var artifact_dir = try options.local_cache_directory.handle.makeOpenPath(artifact_sub_dir, .{}); | 1296 | var artifact_dir = try options.local_cache_directory.handle.makeOpenPath(artifact_sub_dir, .{}); |
| ... | @@ -1374,6 +1424,11 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation { | ... | @@ -1374,6 +1424,11 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation { |
| 1374 | }; | 1424 | }; |
| 1375 | } | 1425 | } |
| 1376 | | 1426 | |
| | 1427 | switch (cache_mode) { |
| | 1428 | .whole => break :blk null, |
| | 1429 | .incremental => {}, |
| | 1430 | } |
| | 1431 | |
| 1377 | if (module) |zm| { | 1432 | if (module) |zm| { |
| 1378 | break :blk link.Emit{ | 1433 | break :blk link.Emit{ |
| 1379 | .directory = zm.zig_cache_artifact_directory, | 1434 | .directory = zm.zig_cache_artifact_directory, |
| ... | @@ -1425,6 +1480,18 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation { | ... | @@ -1425,6 +1480,18 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation { |
| 1425 | }; | 1480 | }; |
| 1426 | }; | 1481 | }; |
| 1427 | | 1482 | |
| | 1483 | // This is so that when doing `CacheMode.whole`, the mechanism in update() |
| | 1484 | // can use it for communicating the result directory via `bin_file.emit`. |
| | 1485 | // This is used to distinguish between -fno-emit-bin and -femit-bin |
| | 1486 | // for `CacheMode.whole`. |
| | 1487 | const whole_bin_basename: ?[]const u8 = if (options.emit_bin) |x| |
| | 1488 | if (x.directory == null) |
| | 1489 | x.basename |
| | 1490 | else |
| | 1491 | null |
| | 1492 | else |
| | 1493 | null; |
| | 1494 | |
| 1428 | var system_libs: std.StringArrayHashMapUnmanaged(SystemLib) = .{}; | 1495 | var system_libs: std.StringArrayHashMapUnmanaged(SystemLib) = .{}; |
| 1429 | errdefer system_libs.deinit(gpa); | 1496 | errdefer system_libs.deinit(gpa); |
| 1430 | try system_libs.ensureTotalCapacity(gpa, options.system_lib_names.len); | 1497 | try system_libs.ensureTotalCapacity(gpa, options.system_lib_names.len); |
| ... | @@ -1512,7 +1579,8 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation { | ... | @@ -1512,7 +1579,8 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation { |
| 1512 | .skip_linker_dependencies = options.skip_linker_dependencies, | 1579 | .skip_linker_dependencies = options.skip_linker_dependencies, |
| 1513 | .parent_compilation_link_libc = options.parent_compilation_link_libc, | 1580 | .parent_compilation_link_libc = options.parent_compilation_link_libc, |
| 1514 | .each_lib_rpath = options.each_lib_rpath orelse options.is_native_os, | 1581 | .each_lib_rpath = options.each_lib_rpath orelse options.is_native_os, |
| 1515 | .disable_lld_caching = options.disable_lld_caching, | 1582 | .cache_mode = cache_mode, |
| | 1583 | .disable_lld_caching = options.disable_lld_caching or cache_mode == .whole, |
| 1516 | .subsystem = options.subsystem, | 1584 | .subsystem = options.subsystem, |
| 1517 | .is_test = options.is_test, | 1585 | .is_test = options.is_test, |
| 1518 | .wasi_exec_model = wasi_exec_model, | 1586 | .wasi_exec_model = wasi_exec_model, |
| ... | @@ -1529,6 +1597,7 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation { | ... | @@ -1529,6 +1597,7 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation { |
| 1529 | .local_cache_directory = options.local_cache_directory, | 1597 | .local_cache_directory = options.local_cache_directory, |
| 1530 | .global_cache_directory = options.global_cache_directory, | 1598 | .global_cache_directory = options.global_cache_directory, |
| 1531 | .bin_file = bin_file, | 1599 | .bin_file = bin_file, |
| | 1600 | .whole_bin_basename = whole_bin_basename, |
| 1532 | .emit_asm = options.emit_asm, | 1601 | .emit_asm = options.emit_asm, |
| 1533 | .emit_llvm_ir = options.emit_llvm_ir, | 1602 | .emit_llvm_ir = options.emit_llvm_ir, |
| 1534 | .emit_llvm_bc = options.emit_llvm_bc, | 1603 | .emit_llvm_bc = options.emit_llvm_bc, |
| ... | @@ -1725,20 +1794,11 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation { | ... | @@ -1725,20 +1794,11 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation { |
| 1725 | return comp; | 1794 | return comp; |
| 1726 | } | 1795 | } |
| 1727 | | 1796 | |
| 1728 | fn releaseStage1Lock(comp: *Compilation) void { | | |
| 1729 | if (comp.stage1_lock) |*lock| { | | |
| 1730 | lock.release(); | | |
| 1731 | comp.stage1_lock = null; | | |
| 1732 | } | | |
| 1733 | } | | |
| 1734 | | | |
| 1735 | pub fn destroy(self: *Compilation) void { | 1797 | pub fn destroy(self: *Compilation) void { |
| 1736 | const optional_module = self.bin_file.options.module; | 1798 | const optional_module = self.bin_file.options.module; |
| 1737 | self.bin_file.destroy(); | 1799 | self.bin_file.destroy(); |
| 1738 | if (optional_module) |module| module.deinit(); | 1800 | if (optional_module) |module| module.deinit(); |
| 1739 | | 1801 | |
| 1740 | self.releaseStage1Lock(); | | |
| 1741 | | | |
| 1742 | const gpa = self.gpa; | 1802 | const gpa = self.gpa; |
| 1743 | self.work_queue.deinit(); | 1803 | self.work_queue.deinit(); |
| 1744 | self.anon_work_queue.deinit(); | 1804 | self.anon_work_queue.deinit(); |
| ... | @@ -1815,22 +1875,135 @@ pub fn getTarget(self: Compilation) Target { | ... | @@ -1815,22 +1875,135 @@ pub fn getTarget(self: Compilation) Target { |
| 1815 | return self.bin_file.options.target; | 1875 | return self.bin_file.options.target; |
| 1816 | } | 1876 | } |
| 1817 | | 1877 | |
| | 1878 | fn restorePrevZigCacheArtifactDirectory(comp: *Compilation, directory: *Directory) void { |
| | 1879 | if (directory.path) |p| comp.gpa.free(p); |
| | 1880 | |
| | 1881 | // Restore the Module's previous zig_cache_artifact_directory |
| | 1882 | // This is only for cleanup purposes; Module.deinit calls close |
| | 1883 | // on the handle of zig_cache_artifact_directory. |
| | 1884 | if (comp.bin_file.options.module) |module| { |
| | 1885 | const builtin_pkg = module.main_pkg.table.get("builtin").?; |
| | 1886 | module.zig_cache_artifact_directory = builtin_pkg.root_src_directory; |
| | 1887 | } |
| | 1888 | } |
| | 1889 | |
| | 1890 | fn cleanupTmpArtifactDirectory( |
| | 1891 | comp: *Compilation, |
| | 1892 | tmp_artifact_directory: *?Directory, |
| | 1893 | tmp_dir_sub_path: []const u8, |
| | 1894 | ) void { |
| | 1895 | comp.gpa.free(tmp_dir_sub_path); |
| | 1896 | if (tmp_artifact_directory.*) |*directory| { |
| | 1897 | directory.handle.close(); |
| | 1898 | restorePrevZigCacheArtifactDirectory(comp, directory); |
| | 1899 | } |
| | 1900 | } |
| | 1901 | |
| 1818 | /// Detect changes to source files, perform semantic analysis, and update the output files. | 1902 | /// Detect changes to source files, perform semantic analysis, and update the output files. |
| 1819 | pub fn update(self: *Compilation) !void { | 1903 | pub fn update(comp: *Compilation) !void { |
| 1820 | const tracy_trace = trace(@src()); | 1904 | const tracy_trace = trace(@src()); |
| 1821 | defer tracy_trace.end(); | 1905 | defer tracy_trace.end(); |
| 1822 | | 1906 | |
| 1823 | self.clearMiscFailures(); | 1907 | comp.clearMiscFailures(); |
| | 1908 | |
| | 1909 | var man: Cache.Manifest = undefined; |
| | 1910 | defer if (comp.whole_cache_manifest != null) man.deinit(); |
| | 1911 | |
| | 1912 | var tmp_dir_sub_path: []const u8 = &.{}; |
| | 1913 | var tmp_artifact_directory: ?Directory = null; |
| | 1914 | defer cleanupTmpArtifactDirectory(comp, &tmp_artifact_directory, tmp_dir_sub_path); |
| | 1915 | |
| | 1916 | // If using the whole caching strategy, we check for *everything* up front, including |
| | 1917 | // C source files. |
| | 1918 | if (comp.bin_file.options.cache_mode == .whole) { |
| | 1919 | // We are about to obtain this lock, so here we give other processes a chance first. |
| | 1920 | comp.bin_file.releaseLock(); |
| | 1921 | |
| | 1922 | man = comp.cache_parent.obtain(); |
| | 1923 | try comp.addNonIncrementalStuffToCacheManifest(&man); |
| | 1924 | |
| | 1925 | const is_hit = man.hit() catch |err| { |
| | 1926 | // TODO properly bubble these up instead of emitting a warning |
| | 1927 | const i = man.failed_file_index orelse return err; |
| | 1928 | const file_path = man.files.items[i].path orelse return err; |
| | 1929 | std.log.warn("{s}: {s}", .{ @errorName(err), file_path }); |
| | 1930 | return err; |
| | 1931 | }; |
| | 1932 | if (is_hit) { |
| | 1933 | const digest = man.final(); |
| | 1934 | |
| | 1935 | // Communicate the output binary location to parent Compilations. |
| | 1936 | if (comp.whole_bin_basename) |basename| { |
| | 1937 | const new_sub_path = try std.fs.path.join(comp.gpa, &.{ |
| | 1938 | "o", &digest, basename, |
| | 1939 | }); |
| | 1940 | if (comp.bin_file.options.emit) |emit| { |
| | 1941 | comp.gpa.free(emit.sub_path); |
| | 1942 | } |
| | 1943 | comp.bin_file.options.emit = .{ |
| | 1944 | .directory = comp.local_cache_directory, |
| | 1945 | .sub_path = new_sub_path, |
| | 1946 | }; |
| | 1947 | } |
| | 1948 | |
| | 1949 | comp.emitOthers(); |
| | 1950 | |
| | 1951 | assert(comp.bin_file.lock == null); |
| | 1952 | comp.bin_file.lock = man.toOwnedLock(); |
| | 1953 | return; |
| | 1954 | } |
| | 1955 | comp.whole_cache_manifest = &man; |
| | 1956 | |
| | 1957 | // Initialize `bin_file.emit` with a temporary Directory so that compilation can |
| | 1958 | // continue on the same path as incremental, using the temporary Directory. |
| | 1959 | tmp_artifact_directory = d: { |
| | 1960 | const s = std.fs.path.sep_str; |
| | 1961 | const rand_int = std.crypto.random.int(u64); |
| | 1962 | |
| | 1963 | tmp_dir_sub_path = try std.fmt.allocPrint(comp.gpa, "tmp" ++ s ++ "{x}", .{rand_int}); |
| | 1964 | |
| | 1965 | const path = try comp.local_cache_directory.join(comp.gpa, &.{tmp_dir_sub_path}); |
| | 1966 | errdefer comp.gpa.free(path); |
| | 1967 | |
| | 1968 | const handle = try comp.local_cache_directory.handle.makeOpenPath(tmp_dir_sub_path, .{}); |
| | 1969 | errdefer handle.close(); |
| | 1970 | |
| | 1971 | break :d .{ |
| | 1972 | .path = path, |
| | 1973 | .handle = handle, |
| | 1974 | }; |
| | 1975 | }; |
| | 1976 | |
| | 1977 | // This updates the output directory for stage1 backend and linker outputs. |
| | 1978 | if (comp.bin_file.options.module) |module| { |
| | 1979 | module.zig_cache_artifact_directory = tmp_artifact_directory.?; |
| | 1980 | } |
| | 1981 | |
| | 1982 | // This resets the link.File to operate as if we called openPath() in create() |
| | 1983 | // instead of simulating -fno-emit-bin. |
| | 1984 | var options = comp.bin_file.options; |
| | 1985 | if (comp.whole_bin_basename) |basename| { |
| | 1986 | if (options.emit) |emit| { |
| | 1987 | comp.gpa.free(emit.sub_path); |
| | 1988 | } |
| | 1989 | options.emit = .{ |
| | 1990 | .directory = tmp_artifact_directory.?, |
| | 1991 | .sub_path = basename, |
| | 1992 | }; |
| | 1993 | } |
| | 1994 | comp.bin_file.destroy(); |
| | 1995 | comp.bin_file = try link.File.openPath(comp.gpa, options); |
| | 1996 | } |
| 1824 | | 1997 | |
| 1825 | // For compiling C objects, we rely on the cache hash system to avoid duplicating work. | 1998 | // For compiling C objects, we rely on the cache hash system to avoid duplicating work. |
| 1826 | // Add a Job for each C object. | 1999 | // Add a Job for each C object. |
| 1827 | try self.c_object_work_queue.ensureUnusedCapacity(self.c_object_table.count()); | 2000 | try comp.c_object_work_queue.ensureUnusedCapacity(comp.c_object_table.count()); |
| 1828 | for (self.c_object_table.keys()) |key| { | 2001 | for (comp.c_object_table.keys()) |key| { |
| 1829 | self.c_object_work_queue.writeItemAssumeCapacity(key); | 2002 | comp.c_object_work_queue.writeItemAssumeCapacity(key); |
| 1830 | } | 2003 | } |
| 1831 | | 2004 | |
| 1832 | const use_stage1 = build_options.is_stage1 and self.bin_file.options.use_stage1; | 2005 | const use_stage1 = build_options.is_stage1 and comp.bin_file.options.use_stage1; |
| 1833 | if (self.bin_file.options.module) |module| { | 2006 | if (comp.bin_file.options.module) |module| { |
| 1834 | module.compile_log_text.shrinkAndFree(module.gpa, 0); | 2007 | module.compile_log_text.shrinkAndFree(module.gpa, 0); |
| 1835 | module.generation += 1; | 2008 | module.generation += 1; |
| 1836 | | 2009 | |
| ... | @@ -1845,7 +2018,7 @@ pub fn update(self: *Compilation) !void { | ... | @@ -1845,7 +2018,7 @@ pub fn update(self: *Compilation) !void { |
| 1845 | // import_table here. | 2018 | // import_table here. |
| 1846 | // Likewise, in the case of `zig test`, the test runner is the root source file, | 2019 | // Likewise, in the case of `zig test`, the test runner is the root source file, |
| 1847 | // and so there is nothing to import the main file. | 2020 | // and so there is nothing to import the main file. |
| 1848 | if (use_stage1 or self.bin_file.options.is_test) { | 2021 | if (use_stage1 or comp.bin_file.options.is_test) { |
| 1849 | _ = try module.importPkg(module.main_pkg); | 2022 | _ = try module.importPkg(module.main_pkg); |
| 1850 | } | 2023 | } |
| 1851 | | 2024 | |
| ... | @@ -1854,34 +2027,34 @@ pub fn update(self: *Compilation) !void { | ... | @@ -1854,34 +2027,34 @@ pub fn update(self: *Compilation) !void { |
| 1854 | // to update it. | 2027 | // to update it. |
| 1855 | // We still want AstGen work items for stage1 so that we expose compile errors | 2028 | // We still want AstGen work items for stage1 so that we expose compile errors |
| 1856 | // that are implemented in stage2 but not stage1. | 2029 | // that are implemented in stage2 but not stage1. |
| 1857 | try self.astgen_work_queue.ensureUnusedCapacity(module.import_table.count()); | 2030 | try comp.astgen_work_queue.ensureUnusedCapacity(module.import_table.count()); |
| 1858 | for (module.import_table.values()) |value| { | 2031 | for (module.import_table.values()) |value| { |
| 1859 | self.astgen_work_queue.writeItemAssumeCapacity(value); | 2032 | comp.astgen_work_queue.writeItemAssumeCapacity(value); |
| 1860 | } | 2033 | } |
| 1861 | | 2034 | |
| 1862 | if (!use_stage1) { | 2035 | if (!use_stage1) { |
| 1863 | // Put a work item in for checking if any files used with `@embedFile` changed. | 2036 | // Put a work item in for checking if any files used with `@embedFile` changed. |
| 1864 | { | 2037 | { |
| 1865 | try self.embed_file_work_queue.ensureUnusedCapacity(module.embed_table.count()); | 2038 | try comp.embed_file_work_queue.ensureUnusedCapacity(module.embed_table.count()); |
| 1866 | var it = module.embed_table.iterator(); | 2039 | var it = module.embed_table.iterator(); |
| 1867 | while (it.next()) |entry| { | 2040 | while (it.next()) |entry| { |
| 1868 | const embed_file = entry.value_ptr.*; | 2041 | const embed_file = entry.value_ptr.*; |
| 1869 | self.embed_file_work_queue.writeItemAssumeCapacity(embed_file); | 2042 | comp.embed_file_work_queue.writeItemAssumeCapacity(embed_file); |
| 1870 | } | 2043 | } |
| 1871 | } | 2044 | } |
| 1872 | | 2045 | |
| 1873 | try self.work_queue.writeItem(.{ .analyze_pkg = std_pkg }); | 2046 | try comp.work_queue.writeItem(.{ .analyze_pkg = std_pkg }); |
| 1874 | if (self.bin_file.options.is_test) { | 2047 | if (comp.bin_file.options.is_test) { |
| 1875 | try self.work_queue.writeItem(.{ .analyze_pkg = module.main_pkg }); | 2048 | try comp.work_queue.writeItem(.{ .analyze_pkg = module.main_pkg }); |
| 1876 | } | 2049 | } |
| 1877 | } | 2050 | } |
| 1878 | } | 2051 | } |
| 1879 | | 2052 | |
| 1880 | try self.performAllTheWork(); | 2053 | try comp.performAllTheWork(); |
| 1881 | | 2054 | |
| 1882 | if (!use_stage1) { | 2055 | if (!use_stage1) { |
| 1883 | if (self.bin_file.options.module) |module| { | 2056 | if (comp.bin_file.options.module) |module| { |
| 1884 | if (self.bin_file.options.is_test and self.totalErrorCount() == 0) { | 2057 | if (comp.bin_file.options.is_test and comp.totalErrorCount() == 0) { |
| 1885 | // The `test_functions` decl has been intentionally postponed until now, | 2058 | // The `test_functions` decl has been intentionally postponed until now, |
| 1886 | // at which point we must populate it with the list of test functions that | 2059 | // at which point we must populate it with the list of test functions that |
| 1887 | // have been discovered and not filtered out. | 2060 | // have been discovered and not filtered out. |
| ... | @@ -1910,39 +2083,184 @@ pub fn update(self: *Compilation) !void { | ... | @@ -1910,39 +2083,184 @@ pub fn update(self: *Compilation) !void { |
| 1910 | } | 2083 | } |
| 1911 | } | 2084 | } |
| 1912 | | 2085 | |
| 1913 | if (self.totalErrorCount() != 0) { | 2086 | if (comp.totalErrorCount() != 0) { |
| 1914 | // Skip flushing. | 2087 | // Skip flushing. |
| 1915 | self.link_error_flags = .{}; | 2088 | comp.link_error_flags = .{}; |
| 1916 | return; | 2089 | return; |
| 1917 | } | 2090 | } |
| 1918 | | 2091 | |
| 1919 | // This is needed before reading the error flags. | 2092 | // This is needed before reading the error flags. |
| 1920 | try self.bin_file.flush(self); | 2093 | try comp.bin_file.flush(comp); |
| 1921 | self.link_error_flags = self.bin_file.errorFlags(); | 2094 | comp.link_error_flags = comp.bin_file.errorFlags(); |
| 1922 | | 2095 | |
| 1923 | if (!use_stage1) { | 2096 | if (!use_stage1) { |
| 1924 | if (self.bin_file.options.module) |module| { | 2097 | if (comp.bin_file.options.module) |module| { |
| 1925 | try link.File.C.flushEmitH(module); | 2098 | try link.File.C.flushEmitH(module); |
| 1926 | } | 2099 | } |
| 1927 | } | 2100 | } |
| 1928 | | 2101 | |
| 1929 | // Flush takes care of -femit-bin, but we still have -femit-llvm-ir, -femit-llvm-bc, and | 2102 | // Flush takes care of -femit-bin, but we still have -femit-llvm-ir, -femit-llvm-bc, and |
| 1930 | // -femit-asm to handle, in the case of C objects. | 2103 | // -femit-asm to handle, in the case of C objects. |
| 1931 | self.emitOthers(); | 2104 | comp.emitOthers(); |
| 1932 | | 2105 | |
| 1933 | // If there are any errors, we anticipate the source files being loaded | 2106 | // If there are any errors, we anticipate the source files being loaded |
| 1934 | // to report error messages. Otherwise we unload all source files to save memory. | 2107 | // to report error messages. Otherwise we unload all source files to save memory. |
| 1935 | // The ZIR needs to stay loaded in memory because (1) Decl objects contain references | 2108 | // The ZIR needs to stay loaded in memory because (1) Decl objects contain references |
| 1936 | // to it, and (2) generic instantiations, comptime calls, inline calls will need | 2109 | // to it, and (2) generic instantiations, comptime calls, inline calls will need |
| 1937 | // to reference the ZIR. | 2110 | // to reference the ZIR. |
| 1938 | if (self.totalErrorCount() == 0 and !self.keep_source_files_loaded) { | 2111 | if (!comp.keep_source_files_loaded) { |
| 1939 | if (self.bin_file.options.module) |module| { | 2112 | if (comp.bin_file.options.module) |module| { |
| 1940 | for (module.import_table.values()) |file| { | 2113 | for (module.import_table.values()) |file| { |
| 1941 | file.unloadTree(self.gpa); | 2114 | file.unloadTree(comp.gpa); |
| 1942 | file.unloadSource(self.gpa); | 2115 | file.unloadSource(comp.gpa); |
| | 2116 | } |
| | 2117 | } |
| | 2118 | } |
| | 2119 | |
| | 2120 | if (comp.whole_cache_manifest != null) { |
| | 2121 | const digest = man.final(); |
| | 2122 | |
| | 2123 | // Rename the temporary directory into place. |
| | 2124 | var directory = tmp_artifact_directory.?; |
| | 2125 | tmp_artifact_directory = null; |
| | 2126 | |
| | 2127 | directory.handle.close(); |
| | 2128 | defer restorePrevZigCacheArtifactDirectory(comp, &directory); |
| | 2129 | |
| | 2130 | const o_sub_path = try std.fs.path.join(comp.gpa, &[_][]const u8{ "o", &digest }); |
| | 2131 | defer comp.gpa.free(o_sub_path); |
| | 2132 | |
| | 2133 | try std.fs.rename( |
| | 2134 | comp.local_cache_directory.handle, |
| | 2135 | tmp_dir_sub_path, |
| | 2136 | comp.local_cache_directory.handle, |
| | 2137 | o_sub_path, |
| | 2138 | ); |
| | 2139 | |
| | 2140 | // Failure here only means an unnecessary cache miss. |
| | 2141 | man.writeManifest() catch |err| { |
| | 2142 | log.warn("failed to write cache manifest: {s}", .{@errorName(err)}); |
| | 2143 | }; |
| | 2144 | |
| | 2145 | // Communicate the output binary location to parent Compilations. |
| | 2146 | if (comp.whole_bin_basename) |basename| { |
| | 2147 | comp.bin_file.options.emit = .{ |
| | 2148 | .directory = comp.local_cache_directory, |
| | 2149 | .sub_path = try std.fs.path.join(comp.gpa, &.{ "o", &digest, basename }), |
| | 2150 | }; |
| | 2151 | } |
| | 2152 | |
| | 2153 | assert(comp.bin_file.lock == null); |
| | 2154 | comp.bin_file.lock = man.toOwnedLock(); |
| | 2155 | return; |
| | 2156 | } |
| | 2157 | } |
| | 2158 | |
| | 2159 | /// This is only observed at compile-time and used to emit a compile error |
| | 2160 | /// to remind the programmer to update multiple related pieces of code that |
| | 2161 | /// are in different locations. Bump this number when adding or deleting |
| | 2162 | /// anything from the link cache manifest. |
| | 2163 | pub const link_hash_implementation_version = 1; |
| | 2164 | |
| | 2165 | fn addNonIncrementalStuffToCacheManifest(comp: *Compilation, man: *Cache.Manifest) !void { |
| | 2166 | const gpa = comp.gpa; |
| | 2167 | const target = comp.getTarget(); |
| | 2168 | |
| | 2169 | var arena_allocator = std.heap.ArenaAllocator.init(gpa); |
| | 2170 | errdefer arena_allocator.deinit(); |
| | 2171 | const arena = arena_allocator.allocator(); |
| | 2172 | |
| | 2173 | comptime assert(link_hash_implementation_version == 1); |
| | 2174 | |
| | 2175 | if (comp.bin_file.options.module) |mod| { |
| | 2176 | const main_zig_file = try mod.main_pkg.root_src_directory.join(arena, &[_][]const u8{ |
| | 2177 | mod.main_pkg.root_src_path, |
| | 2178 | }); |
| | 2179 | _ = try man.addFile(main_zig_file, null); |
| | 2180 | { |
| | 2181 | var seen_table = std.AutoHashMap(*Package, void).init(arena); |
| | 2182 | |
| | 2183 | // Skip builtin.zig; it is useless as an input, and we don't want to have to |
| | 2184 | // write it before checking for a cache hit. |
| | 2185 | const builtin_pkg = mod.main_pkg.table.get("builtin").?; |
| | 2186 | try seen_table.put(builtin_pkg, {}); |
| | 2187 | |
| | 2188 | try addPackageTableToCacheHash(&man.hash, &arena_allocator, mod.main_pkg.table, &seen_table, .{ .files = man }); |
| | 2189 | } |
| | 2190 | } |
| | 2191 | |
| | 2192 | try man.addOptionalFile(comp.bin_file.options.linker_script); |
| | 2193 | try man.addOptionalFile(comp.bin_file.options.version_script); |
| | 2194 | try man.addListOfFiles(comp.bin_file.options.objects); |
| | 2195 | |
| | 2196 | for (comp.c_object_table.keys()) |key| { |
| | 2197 | _ = try man.addFile(key.src.src_path, null); |
| | 2198 | man.hash.addListOfBytes(key.src.extra_flags); |
| | 2199 | } |
| | 2200 | |
| | 2201 | man.hash.addOptionalEmitLoc(comp.emit_asm); |
| | 2202 | man.hash.addOptionalEmitLoc(comp.emit_llvm_ir); |
| | 2203 | man.hash.addOptionalEmitLoc(comp.emit_llvm_bc); |
| | 2204 | man.hash.addOptionalEmitLoc(comp.emit_analysis); |
| | 2205 | man.hash.addOptionalEmitLoc(comp.emit_docs); |
| | 2206 | |
| | 2207 | man.hash.addListOfBytes(comp.clang_argv); |
| | 2208 | |
| | 2209 | man.hash.addOptional(comp.bin_file.options.stack_size_override); |
| | 2210 | man.hash.addOptional(comp.bin_file.options.image_base_override); |
| | 2211 | man.hash.addOptional(comp.bin_file.options.gc_sections); |
| | 2212 | man.hash.add(comp.bin_file.options.eh_frame_hdr); |
| | 2213 | man.hash.add(comp.bin_file.options.emit_relocs); |
| | 2214 | man.hash.add(comp.bin_file.options.rdynamic); |
| | 2215 | man.hash.addListOfBytes(comp.bin_file.options.lib_dirs); |
| | 2216 | man.hash.addListOfBytes(comp.bin_file.options.rpath_list); |
| | 2217 | man.hash.add(comp.bin_file.options.each_lib_rpath); |
| | 2218 | man.hash.add(comp.bin_file.options.skip_linker_dependencies); |
| | 2219 | man.hash.add(comp.bin_file.options.z_nodelete); |
| | 2220 | man.hash.add(comp.bin_file.options.z_notext); |
| | 2221 | man.hash.add(comp.bin_file.options.z_defs); |
| | 2222 | man.hash.add(comp.bin_file.options.z_origin); |
| | 2223 | man.hash.add(comp.bin_file.options.z_noexecstack); |
| | 2224 | man.hash.add(comp.bin_file.options.z_now); |
| | 2225 | man.hash.add(comp.bin_file.options.z_relro); |
| | 2226 | man.hash.add(comp.bin_file.options.include_compiler_rt); |
| | 2227 | if (comp.bin_file.options.link_libc) { |
| | 2228 | man.hash.add(comp.bin_file.options.libc_installation != null); |
| | 2229 | if (comp.bin_file.options.libc_installation) |libc_installation| { |
| | 2230 | man.hash.addBytes(libc_installation.crt_dir.?); |
| | 2231 | if (target.abi == .msvc) { |
| | 2232 | man.hash.addBytes(libc_installation.msvc_lib_dir.?); |
| | 2233 | man.hash.addBytes(libc_installation.kernel32_lib_dir.?); |
| 1943 | } | 2234 | } |
| 1944 | } | 2235 | } |
| | 2236 | man.hash.addOptionalBytes(comp.bin_file.options.dynamic_linker); |
| 1945 | } | 2237 | } |
| | 2238 | man.hash.addOptionalBytes(comp.bin_file.options.soname); |
| | 2239 | man.hash.addOptional(comp.bin_file.options.version); |
| | 2240 | link.hashAddSystemLibs(&man.hash, comp.bin_file.options.system_libs); |
| | 2241 | man.hash.addOptional(comp.bin_file.options.allow_shlib_undefined); |
| | 2242 | man.hash.add(comp.bin_file.options.bind_global_refs_locally); |
| | 2243 | man.hash.add(comp.bin_file.options.tsan); |
| | 2244 | man.hash.addOptionalBytes(comp.bin_file.options.sysroot); |
| | 2245 | man.hash.add(comp.bin_file.options.linker_optimization); |
| | 2246 | |
| | 2247 | // WASM specific stuff |
| | 2248 | man.hash.add(comp.bin_file.options.import_memory); |
| | 2249 | man.hash.addOptional(comp.bin_file.options.initial_memory); |
| | 2250 | man.hash.addOptional(comp.bin_file.options.max_memory); |
| | 2251 | man.hash.addOptional(comp.bin_file.options.global_base); |
| | 2252 | |
| | 2253 | // Mach-O specific stuff |
| | 2254 | man.hash.addListOfBytes(comp.bin_file.options.framework_dirs); |
| | 2255 | man.hash.addListOfBytes(comp.bin_file.options.frameworks); |
| | 2256 | |
| | 2257 | // COFF specific stuff |
| | 2258 | man.hash.addOptional(comp.bin_file.options.subsystem); |
| | 2259 | man.hash.add(comp.bin_file.options.tsaware); |
| | 2260 | man.hash.add(comp.bin_file.options.nxcompat); |
| | 2261 | man.hash.add(comp.bin_file.options.dynamicbase); |
| | 2262 | man.hash.addOptional(comp.bin_file.options.major_subsystem_version); |
| | 2263 | man.hash.addOptional(comp.bin_file.options.minor_subsystem_version); |
| 1946 | } | 2264 | } |
| 1947 | | 2265 | |
| 1948 | fn emitOthers(comp: *Compilation) void { | 2266 | fn emitOthers(comp: *Compilation) void { |
| ... | @@ -2988,7 +3306,9 @@ pub fn cImport(comp: *Compilation, c_src: []const u8) !CImportResult { | ... | @@ -2988,7 +3306,9 @@ pub fn cImport(comp: *Compilation, c_src: []const u8) !CImportResult { |
| 2988 | | 3306 | |
| 2989 | const dep_basename = std.fs.path.basename(out_dep_path); | 3307 | const dep_basename = std.fs.path.basename(out_dep_path); |
| 2990 | try man.addDepFilePost(zig_cache_tmp_dir, dep_basename); | 3308 | try man.addDepFilePost(zig_cache_tmp_dir, dep_basename); |
| 2991 | if (build_options.is_stage1 and comp.bin_file.options.use_stage1) try comp.stage1_cache_manifest.addDepFilePost(zig_cache_tmp_dir, dep_basename); | 3309 | if (comp.whole_cache_manifest) |whole_cache_manifest| { |
| | 3310 | try whole_cache_manifest.addDepFilePost(zig_cache_tmp_dir, dep_basename); |
| | 3311 | } |
| 2992 | | 3312 | |
| 2993 | const digest = man.final(); | 3313 | const digest = man.final(); |
| 2994 | const o_sub_path = try std.fs.path.join(arena, &[_][]const u8{ "o", &digest }); | 3314 | const o_sub_path = try std.fs.path.join(arena, &[_][]const u8{ "o", &digest }); |
| ... | @@ -3351,13 +3671,13 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: *std.P | ... | @@ -3351,13 +3671,13 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: *std.P |
| 3351 | }; | 3671 | }; |
| 3352 | } | 3672 | } |
| 3353 | | 3673 | |
| 3354 | pub fn tmpFilePath(comp: *Compilation, arena: Allocator, suffix: []const u8) error{OutOfMemory}![]const u8 { | 3674 | pub fn tmpFilePath(comp: *Compilation, ally: Allocator, suffix: []const u8) error{OutOfMemory}![]const u8 { |
| 3355 | const s = std.fs.path.sep_str; | 3675 | const s = std.fs.path.sep_str; |
| 3356 | const rand_int = std.crypto.random.int(u64); | 3676 | const rand_int = std.crypto.random.int(u64); |
| 3357 | if (comp.local_cache_directory.path) |p| { | 3677 | if (comp.local_cache_directory.path) |p| { |
| 3358 | return std.fmt.allocPrint(arena, "{s}" ++ s ++ "tmp" ++ s ++ "{x}-{s}", .{ p, rand_int, suffix }); | 3678 | return std.fmt.allocPrint(ally, "{s}" ++ s ++ "tmp" ++ s ++ "{x}-{s}", .{ p, rand_int, suffix }); |
| 3359 | } else { | 3679 | } else { |
| 3360 | return std.fmt.allocPrint(arena, "tmp" ++ s ++ "{x}-{s}", .{ rand_int, suffix }); | 3680 | return std.fmt.allocPrint(ally, "tmp" ++ s ++ "{x}-{s}", .{ rand_int, suffix }); |
| 3361 | } | 3681 | } |
| 3362 | } | 3682 | } |
| 3363 | | 3683 | |
| ... | @@ -4424,6 +4744,7 @@ fn buildOutputFromZig( | ... | @@ -4424,6 +4744,7 @@ fn buildOutputFromZig( |
| 4424 | .global_cache_directory = comp.global_cache_directory, | 4744 | .global_cache_directory = comp.global_cache_directory, |
| 4425 | .local_cache_directory = comp.global_cache_directory, | 4745 | .local_cache_directory = comp.global_cache_directory, |
| 4426 | .zig_lib_directory = comp.zig_lib_directory, | 4746 | .zig_lib_directory = comp.zig_lib_directory, |
| | 4747 | .cache_mode = .whole, |
| 4427 | .target = target, | 4748 | .target = target, |
| 4428 | .root_name = root_name, | 4749 | .root_name = root_name, |
| 4429 | .main_pkg = &main_pkg, | 4750 | .main_pkg = &main_pkg, |
| ... | @@ -4501,10 +4822,7 @@ fn updateStage1Module(comp: *Compilation, main_progress_node: *std.Progress.Node | ... | @@ -4501,10 +4822,7 @@ fn updateStage1Module(comp: *Compilation, main_progress_node: *std.Progress.Node |
| 4501 | mod.main_pkg.root_src_path, | 4822 | mod.main_pkg.root_src_path, |
| 4502 | }); | 4823 | }); |
| 4503 | const zig_lib_dir = comp.zig_lib_directory.path.?; | 4824 | const zig_lib_dir = comp.zig_lib_directory.path.?; |
| 4504 | const builtin_zig_path = try directory.join(arena, &[_][]const u8{"builtin.zig"}); | | |
| 4505 | const target = comp.getTarget(); | 4825 | const target = comp.getTarget(); |
| 4506 | const id_symlink_basename = "stage1.id"; | | |
| 4507 | const libs_txt_basename = "libs.txt"; | | |
| 4508 | | 4826 | |
| 4509 | // The include_compiler_rt stored in the bin file options here means that we need | 4827 | // The include_compiler_rt stored in the bin file options here means that we need |
| 4510 | // compiler-rt symbols *somehow*. However, in the context of using the stage1 backend | 4828 | // compiler-rt symbols *somehow*. However, in the context of using the stage1 backend |
| ... | @@ -4516,115 +4834,6 @@ fn updateStage1Module(comp: *Compilation, main_progress_node: *std.Progress.Node | ... | @@ -4516,115 +4834,6 @@ fn updateStage1Module(comp: *Compilation, main_progress_node: *std.Progress.Node |
| 4516 | const include_compiler_rt = comp.bin_file.options.output_mode == .Obj and | 4834 | const include_compiler_rt = comp.bin_file.options.output_mode == .Obj and |
| 4517 | comp.bin_file.options.include_compiler_rt; | 4835 | comp.bin_file.options.include_compiler_rt; |
| 4518 | | 4836 | |
| 4519 | // We are about to obtain this lock, so here we give other processes a chance first. | | |
| 4520 | comp.releaseStage1Lock(); | | |
| 4521 | | | |
| 4522 | // Unlike with the self-hosted Zig module, stage1 does not support incremental compilation, | | |
| 4523 | // so we input all the zig source files into the cache hash system. We're going to keep | | |
| 4524 | // the artifact directory the same, however, so we take the same strategy as linking | | |
| 4525 | // does where we have a file which specifies the hash of the output directory so that we can | | |
| 4526 | // skip the expensive compilation step if the hash matches. | | |
| 4527 | var man = comp.cache_parent.obtain(); | | |
| 4528 | defer man.deinit(); | | |
| 4529 | | | |
| 4530 | _ = try man.addFile(main_zig_file, null); | | |
| 4531 | { | | |
| 4532 | var seen_table = std.AutoHashMap(*Package, void).init(arena_allocator.allocator()); | | |
| 4533 | try addPackageTableToCacheHash(&man.hash, &arena_allocator, mod.main_pkg.table, &seen_table, .{ .files = &man }); | | |
| 4534 | } | | |
| 4535 | man.hash.add(comp.bin_file.options.valgrind); | | |
| 4536 | man.hash.add(comp.bin_file.options.single_threaded); | | |
| 4537 | man.hash.add(target.os.getVersionRange()); | | |
| 4538 | man.hash.add(comp.bin_file.options.dll_export_fns); | | |
| 4539 | man.hash.add(comp.bin_file.options.function_sections); | | |
| 4540 | man.hash.add(include_compiler_rt); | | |
| 4541 | man.hash.add(comp.bin_file.options.is_test); | | |
| 4542 | man.hash.add(comp.bin_file.options.emit != null); | | |
| 4543 | man.hash.add(mod.emit_h != null); | | |
| 4544 | if (mod.emit_h) |emit_h| { | | |
| 4545 | man.hash.addEmitLoc(emit_h.loc); | | |
| 4546 | } | | |
| 4547 | man.hash.addOptionalEmitLoc(comp.emit_asm); | | |
| 4548 | man.hash.addOptionalEmitLoc(comp.emit_llvm_ir); | | |
| 4549 | man.hash.addOptionalEmitLoc(comp.emit_llvm_bc); | | |
| 4550 | man.hash.addOptionalEmitLoc(comp.emit_analysis); | | |
| 4551 | man.hash.addOptionalEmitLoc(comp.emit_docs); | | |
| 4552 | man.hash.add(comp.test_evented_io); | | |
| 4553 | man.hash.addOptionalBytes(comp.test_filter); | | |
| 4554 | man.hash.addOptionalBytes(comp.test_name_prefix); | | |
| 4555 | man.hash.addListOfBytes(comp.clang_argv); | | |
| 4556 | | | |
| 4557 | // Capture the state in case we come back from this branch where the hash doesn't match. | | |
| 4558 | const prev_hash_state = man.hash.peekBin(); | | |
| 4559 | const input_file_count = man.files.items.len; | | |
| 4560 | | | |
| 4561 | const hit = man.hit() catch |err| { | | |
| 4562 | const i = man.failed_file_index orelse return err; | | |
| 4563 | const file_path = man.files.items[i].path orelse return err; | | |
| 4564 | fatal("unable to build stage1 zig object: {s}: {s}", .{ @errorName(err), file_path }); | | |
| 4565 | }; | | |
| 4566 | if (hit) { | | |
| 4567 | const digest = man.final(); | | |
| 4568 | | | |
| 4569 | // We use an extra hex-encoded byte here to store some flags. | | |
| 4570 | var prev_digest_buf: [digest.len + 2]u8 = undefined; | | |
| 4571 | const prev_digest: []u8 = Cache.readSmallFile( | | |
| 4572 | directory.handle, | | |
| 4573 | id_symlink_basename, | | |
| 4574 | &prev_digest_buf, | | |
| 4575 | ) catch |err| blk: { | | |
| 4576 | log.debug("stage1 {s} new_digest={s} error: {s}", .{ | | |
| 4577 | mod.main_pkg.root_src_path, | | |
| 4578 | std.fmt.fmtSliceHexLower(&digest), | | |
| 4579 | @errorName(err), | | |
| 4580 | }); | | |
| 4581 | // Handle this as a cache miss. | | |
| 4582 | break :blk prev_digest_buf[0..0]; | | |
| 4583 | }; | | |
| 4584 | if (prev_digest.len >= digest.len + 2) hit: { | | |
| 4585 | if (!mem.eql(u8, prev_digest[0..digest.len], &digest)) | | |
| 4586 | break :hit; | | |
| 4587 | | | |
| 4588 | log.debug("stage1 {s} digest={s} match - skipping invocation", .{ | | |
| 4589 | mod.main_pkg.root_src_path, | | |
| 4590 | std.fmt.fmtSliceHexLower(&digest), | | |
| 4591 | }); | | |
| 4592 | var flags_bytes: [1]u8 = undefined; | | |
| 4593 | _ = std.fmt.hexToBytes(&flags_bytes, prev_digest[digest.len..]) catch { | | |
| 4594 | log.warn("bad cache stage1 digest: '{s}'", .{std.fmt.fmtSliceHexLower(prev_digest)}); | | |
| 4595 | break :hit; | | |
| 4596 | }; | | |
| 4597 | | | |
| 4598 | if (directory.handle.readFileAlloc(comp.gpa, libs_txt_basename, 10 * 1024 * 1024)) |libs_txt| { | | |
| 4599 | var it = mem.tokenize(u8, libs_txt, "\n"); | | |
| 4600 | while (it.next()) |lib_name| { | | |
| 4601 | try comp.stage1AddLinkLib(lib_name); | | |
| 4602 | } | | |
| 4603 | } else |err| switch (err) { | | |
| 4604 | error.FileNotFound => {}, // That's OK, it just means 0 libs. | | |
| 4605 | else => { | | |
| 4606 | log.warn("unable to read cached list of link libs: {s}", .{@errorName(err)}); | | |
| 4607 | break :hit; | | |
| 4608 | }, | | |
| 4609 | } | | |
| 4610 | comp.stage1_lock = man.toOwnedLock(); | | |
| 4611 | mod.stage1_flags = @bitCast(@TypeOf(mod.stage1_flags), flags_bytes[0]); | | |
| 4612 | return; | | |
| 4613 | } | | |
| 4614 | log.debug("stage1 {s} prev_digest={s} new_digest={s}", .{ | | |
| 4615 | mod.main_pkg.root_src_path, | | |
| 4616 | std.fmt.fmtSliceHexLower(prev_digest), | | |
| 4617 | std.fmt.fmtSliceHexLower(&digest), | | |
| 4618 | }); | | |
| 4619 | man.unhit(prev_hash_state, input_file_count); | | |
| 4620 | } | | |
| 4621 | | | |
| 4622 | // We are about to change the output file to be different, so we invalidate the build hash now. | | |
| 4623 | directory.handle.deleteFile(id_symlink_basename) catch |err| switch (err) { | | |
| 4624 | error.FileNotFound => {}, | | |
| 4625 | else => |e| return e, | | |
| 4626 | }; | | |
| 4627 | | | |
| 4628 | const stage2_target = try arena.create(stage1.Stage2Target); | 4837 | const stage2_target = try arena.create(stage1.Stage2Target); |
| 4629 | stage2_target.* = .{ | 4838 | stage2_target.* = .{ |
| 4630 | .arch = @enumToInt(target.cpu.arch) + 1, // skip over ZigLLVM_UnknownArch | 4839 | .arch = @enumToInt(target.cpu.arch) + 1, // skip over ZigLLVM_UnknownArch |
| ... | @@ -4637,9 +4846,9 @@ fn updateStage1Module(comp: *Compilation, main_progress_node: *std.Progress.Node | ... | @@ -4637,9 +4846,9 @@ fn updateStage1Module(comp: *Compilation, main_progress_node: *std.Progress.Node |
| 4637 | .llvm_target_abi = if (target_util.llvmMachineAbi(target)) |s| s.ptr else null, | 4846 | .llvm_target_abi = if (target_util.llvmMachineAbi(target)) |s| s.ptr else null, |
| 4638 | }; | 4847 | }; |
| 4639 | | 4848 | |
| 4640 | comp.stage1_cache_manifest = &man; | | |
| 4641 | | | |
| 4642 | const main_pkg_path = mod.main_pkg.root_src_directory.path orelse ""; | 4849 | const main_pkg_path = mod.main_pkg.root_src_directory.path orelse ""; |
| | 4850 | const builtin_pkg = mod.main_pkg.table.get("builtin").?; |
| | 4851 | const builtin_zig_path = try builtin_pkg.root_src_directory.join(arena, &.{builtin_pkg.root_src_path}); |
| 4643 | | 4852 | |
| 4644 | const stage1_module = stage1.create( | 4853 | const stage1_module = stage1.create( |
| 4645 | @enumToInt(comp.bin_file.options.optimize_mode), | 4854 | @enumToInt(comp.bin_file.options.optimize_mode), |
| ... | @@ -4740,19 +4949,8 @@ fn updateStage1Module(comp: *Compilation, main_progress_node: *std.Progress.Node | ... | @@ -4740,19 +4949,8 @@ fn updateStage1Module(comp: *Compilation, main_progress_node: *std.Progress.Node |
| 4740 | .have_dllmain_crt_startup = false, | 4949 | .have_dllmain_crt_startup = false, |
| 4741 | }; | 4950 | }; |
| 4742 | | 4951 | |
| 4743 | const inferred_lib_start_index = comp.bin_file.options.system_libs.count(); | | |
| 4744 | stage1_module.build_object(); | 4952 | stage1_module.build_object(); |
| 4745 | | 4953 | |
| 4746 | if (comp.bin_file.options.system_libs.count() > inferred_lib_start_index) { | | |
| 4747 | // We need to save the inferred link libs to the cache, otherwise if we get a cache hit | | |
| 4748 | // next time we will be missing these libs. | | |
| 4749 | var libs_txt = std.ArrayList(u8).init(arena); | | |
| 4750 | for (comp.bin_file.options.system_libs.keys()[inferred_lib_start_index..]) |key| { | | |
| 4751 | try libs_txt.writer().print("{s}\n", .{key}); | | |
| 4752 | } | | |
| 4753 | try directory.handle.writeFile(libs_txt_basename, libs_txt.items); | | |
| 4754 | } | | |
| 4755 | | | |
| 4756 | mod.stage1_flags = .{ | 4954 | mod.stage1_flags = .{ |
| 4757 | .have_c_main = stage1_module.have_c_main, | 4955 | .have_c_main = stage1_module.have_c_main, |
| 4758 | .have_winmain = stage1_module.have_winmain, | 4956 | .have_winmain = stage1_module.have_winmain, |
| ... | @@ -4763,34 +4961,6 @@ fn updateStage1Module(comp: *Compilation, main_progress_node: *std.Progress.Node | ... | @@ -4763,34 +4961,6 @@ fn updateStage1Module(comp: *Compilation, main_progress_node: *std.Progress.Node |
| 4763 | }; | 4961 | }; |
| 4764 | | 4962 | |
| 4765 | stage1_module.destroy(); | 4963 | stage1_module.destroy(); |
| 4766 | | | |
| 4767 | const digest = man.final(); | | |
| 4768 | | | |
| 4769 | // Update the small file with the digest. If it fails we can continue; it only | | |
| 4770 | // means that the next invocation will have an unnecessary cache miss. | | |
| 4771 | const stage1_flags_byte = @bitCast(u8, mod.stage1_flags); | | |
| 4772 | log.debug("stage1 {s} final digest={s} flags={x}", .{ | | |
| 4773 | mod.main_pkg.root_src_path, std.fmt.fmtSliceHexLower(&digest), stage1_flags_byte, | | |
| 4774 | }); | | |
| 4775 | var digest_plus_flags: [digest.len + 2]u8 = undefined; | | |
| 4776 | digest_plus_flags[0..digest.len].* = digest; | | |
| 4777 | assert(std.fmt.formatIntBuf(digest_plus_flags[digest.len..], stage1_flags_byte, 16, .lower, .{ | | |
| 4778 | .width = 2, | | |
| 4779 | .fill = '0', | | |
| 4780 | }) == 2); | | |
| 4781 | log.debug("saved digest + flags: '{s}' (byte = {}) have_winmain_crt_startup={}", .{ | | |
| 4782 | digest_plus_flags, stage1_flags_byte, mod.stage1_flags.have_winmain_crt_startup, | | |
| 4783 | }); | | |
| 4784 | Cache.writeSmallFile(directory.handle, id_symlink_basename, &digest_plus_flags) catch |err| { | | |
| 4785 | log.warn("failed to save stage1 hash digest file: {s}", .{@errorName(err)}); | | |
| 4786 | }; | | |
| 4787 | // Failure here only means an unnecessary cache miss. | | |
| 4788 | man.writeManifest() catch |err| { | | |
| 4789 | log.warn("failed to write cache manifest when linking: {s}", .{@errorName(err)}); | | |
| 4790 | }; | | |
| 4791 | // We hang on to this lock so that the output file path can be used without | | |
| 4792 | // other processes clobbering it. | | |
| 4793 | comp.stage1_lock = man.toOwnedLock(); | | |
| 4794 | } | 4964 | } |
| 4795 | | 4965 | |
| 4796 | fn stage1LocPath(arena: Allocator, opt_loc: ?EmitLoc, cache_directory: Directory) ![]const u8 { | 4966 | fn stage1LocPath(arena: Allocator, opt_loc: ?EmitLoc, cache_directory: Directory) ![]const u8 { |
| ... | @@ -4862,6 +5032,7 @@ pub fn build_crt_file( | ... | @@ -4862,6 +5032,7 @@ pub fn build_crt_file( |
| 4862 | .local_cache_directory = comp.global_cache_directory, | 5032 | .local_cache_directory = comp.global_cache_directory, |
| 4863 | .global_cache_directory = comp.global_cache_directory, | 5033 | .global_cache_directory = comp.global_cache_directory, |
| 4864 | .zig_lib_directory = comp.zig_lib_directory, | 5034 | .zig_lib_directory = comp.zig_lib_directory, |
| | 5035 | .cache_mode = .whole, |
| 4865 | .target = target, | 5036 | .target = target, |
| 4866 | .root_name = root_name, | 5037 | .root_name = root_name, |
| 4867 | .main_pkg = null, | 5038 | .main_pkg = null, |