| author | |
| committer | |
| log | 4056bb92e6d6ca3d2ab8f49b4ac83c01fb25bd11 |
| tree | 3992ca209d9759c7a041c1c9f58e8572081051da |
| parent | 472ee184862415d1f2651d81d248f3000032932d |
* std.cache_hash exposes Hasher type
* std.cache_hash makes hasher_init a global const
* std.cache_hash supports cloning so that clones can share the same
open manifest dir handle as well as fork from shared hasher state
* start to populate the cache_hash for stage2 builds
* remove a footgun from std.cache_hash add function
* get rid of std.Target.ObjectFormat.unknown
* rework stage2 logic for resolving output artifact names by adding
object_format as an optional parameter to std.zig.binNameAlloc
* support -Denable-llvm in stage2 tests
* Module supports the use case when there are no .zig files
* introduce c_object_table and failed_c_objects to Module
* propagate many new kinds of data from CLI into Module and into
linker.Options
* introduce -fLLVM, -fLLD, -fClang and their -fno- counterparts.
closes #6251.
- add logic for choosing when to use LLD or zig's self-hosted linker
* stub code for implementing invoking Clang to build C objects
* add -femit-h, -femit-h=foo, and -fno-emit-h CLI options13 files changed, 595 insertions(+), 129 deletions(-)
build.zig+1| ... | @@ -139,6 +139,7 @@ pub fn build(b: *Builder) !void { | ... | @@ -139,6 +139,7 @@ pub fn build(b: *Builder) !void { |
| 139 | const is_wasmtime_enabled = b.option(bool, "enable-wasmtime", "Use Wasmtime to enable and run WASI libstd tests") orelse false; | 139 | const is_wasmtime_enabled = b.option(bool, "enable-wasmtime", "Use Wasmtime to enable and run WASI libstd tests") orelse false; |
| 140 | const glibc_multi_dir = b.option([]const u8, "enable-foreign-glibc", "Provide directory with glibc installations to run cross compiled tests that link glibc"); | 140 | const glibc_multi_dir = b.option([]const u8, "enable-foreign-glibc", "Provide directory with glibc installations to run cross compiled tests that link glibc"); |
| 141 | 141 | ||
| 142 | test_stage2.addBuildOption(bool, "have_llvm", enable_llvm); | ||
| 142 | test_stage2.addBuildOption(bool, "enable_qemu", is_qemu_enabled); | 143 | test_stage2.addBuildOption(bool, "enable_qemu", is_qemu_enabled); |
| 143 | test_stage2.addBuildOption(bool, "enable_wine", is_wine_enabled); | 144 | test_stage2.addBuildOption(bool, "enable_wine", is_wine_enabled); |
| 144 | test_stage2.addBuildOption(bool, "enable_wasmtime", is_wasmtime_enabled); | 145 | test_stage2.addBuildOption(bool, "enable_wasmtime", is_wasmtime_enabled); |
lib/std/cache_hash.zig+73-35| ... | @@ -5,7 +5,6 @@ | ... | @@ -5,7 +5,6 @@ |
| 5 | // and substantial portions of the software. | 5 | // and substantial portions of the software. |
| 6 | const std = @import("std.zig"); | 6 | const std = @import("std.zig"); |
| 7 | const crypto = std.crypto; | 7 | const crypto = std.crypto; |
| 8 | const Hasher = crypto.auth.siphash.SipHash128(1, 3); // provides enough collision resistance for the CacheHash use cases, while being one of our fastest options right now | ||
| 9 | const fs = std.fs; | 8 | const fs = std.fs; |
| 10 | const base64 = std.base64; | 9 | const base64 = std.base64; |
| 11 | const ArrayList = std.ArrayList; | 10 | const ArrayList = std.ArrayList; |
| ... | @@ -23,6 +22,14 @@ const BASE64_DIGEST_LEN = base64.Base64Encoder.calcSize(BIN_DIGEST_LEN); | ... | @@ -23,6 +22,14 @@ const BASE64_DIGEST_LEN = base64.Base64Encoder.calcSize(BIN_DIGEST_LEN); |
| 23 | 22 | ||
| 24 | const MANIFEST_FILE_SIZE_MAX = 50 * 1024 * 1024; | 23 | const MANIFEST_FILE_SIZE_MAX = 50 * 1024 * 1024; |
| 25 | 24 | ||
| 25 | /// The type used for hashing file contents. Currently, this is SipHash128(1, 3), because it | ||
| 26 | /// provides enough collision resistance for the CacheHash use cases, while being one of our | ||
| 27 | /// fastest options right now. | ||
| 28 | pub const Hasher = crypto.auth.siphash.SipHash128(1, 3); | ||
| 29 | |||
| 30 | /// Initial state, that can be copied. | ||
| 31 | pub const hasher_init: Hasher = Hasher.init(&[_]u8{0} ** Hasher.minimum_key_length); | ||
| 32 | |||
| 26 | pub const File = struct { | 33 | pub const File = struct { |
| 27 | path: ?[]const u8, | 34 | path: ?[]const u8, |
| 28 | max_file_size: ?usize, | 35 | max_file_size: ?usize, |
| ... | @@ -45,52 +52,82 @@ pub const File = struct { | ... | @@ -45,52 +52,82 @@ pub const File = struct { |
| 45 | 52 | ||
| 46 | /// CacheHash manages project-local `zig-cache` directories. | 53 | /// CacheHash manages project-local `zig-cache` directories. |
| 47 | /// This is not a general-purpose cache. | 54 | /// This is not a general-purpose cache. |
| 48 | /// It was designed to be fast and simple, not to withstand attacks using specially-crafted input. | 55 | /// It is designed to be fast and simple, not to withstand attacks using specially-crafted input. |
| 49 | pub const CacheHash = struct { | 56 | pub const CacheHash = struct { |
| 50 | allocator: *Allocator, | 57 | allocator: *Allocator, |
| 51 | hasher_init: Hasher, // initial state, that can be copied | 58 | /// Current state for incremental hashing. |
| 52 | hasher: Hasher, // current state for incremental hashing | 59 | hasher: Hasher, |
| 53 | manifest_dir: fs.Dir, | 60 | manifest_dir: fs.Dir, |
| 54 | manifest_file: ?fs.File, | 61 | manifest_file: ?fs.File, |
| 55 | manifest_dirty: bool, | 62 | manifest_dirty: bool, |
| 63 | owns_manifest_dir: bool, | ||
| 56 | files: ArrayList(File), | 64 | files: ArrayList(File), |
| 57 | b64_digest: [BASE64_DIGEST_LEN]u8, | 65 | b64_digest: [BASE64_DIGEST_LEN]u8, |
| 58 | 66 | ||
| 59 | /// Be sure to call release after successful initialization. | 67 | /// Be sure to call release after successful initialization. |
| 60 | pub fn init(allocator: *Allocator, dir: fs.Dir, manifest_dir_path: []const u8) !CacheHash { | 68 | pub fn init(allocator: *Allocator, dir: fs.Dir, manifest_dir_path: []const u8) !CacheHash { |
| 61 | const hasher_init = Hasher.init(&[_]u8{0} ** Hasher.minimum_key_length); | ||
| 62 | return CacheHash{ | 69 | return CacheHash{ |
| 63 | .allocator = allocator, | 70 | .allocator = allocator, |
| 64 | .hasher_init = hasher_init, | ||
| 65 | .hasher = hasher_init, | 71 | .hasher = hasher_init, |
| 66 | .manifest_dir = try dir.makeOpenPath(manifest_dir_path, .{}), | 72 | .manifest_dir = try dir.makeOpenPath(manifest_dir_path, .{}), |
| 67 | .manifest_file = null, | 73 | .manifest_file = null, |
| 68 | .manifest_dirty = false, | 74 | .manifest_dirty = false, |
| 75 | .owns_manifest_dir = true, | ||
| 76 | .files = ArrayList(File).init(allocator), | ||
| 77 | .b64_digest = undefined, | ||
| 78 | }; | ||
| 79 | } | ||
| 80 | |||
| 81 | /// Allows one to fork a CacheHash instance into another one, which does not require an additional | ||
| 82 | /// directory handle to be opened. The new instance inherits the hash state. | ||
| 83 | pub fn clone(self: CacheHash) CacheHash { | ||
| 84 | assert(self.manifest_file == null); | ||
| 85 | assert(files.items.len == 0); | ||
| 86 | return .{ | ||
| 87 | .allocator = self.allocator, | ||
| 88 | .hasher = self.hasher, | ||
| 89 | .manifest_dir = self.manifest_dir, | ||
| 90 | .manifest_file = null, | ||
| 91 | .manifest_dirty = false, | ||
| 92 | .owns_manifest_dir = false, | ||
| 69 | .files = ArrayList(File).init(allocator), | 93 | .files = ArrayList(File).init(allocator), |
| 70 | .b64_digest = undefined, | 94 | .b64_digest = undefined, |
| 71 | }; | 95 | }; |
| 72 | } | 96 | } |
| 73 | 97 | ||
| 74 | /// Record a slice of bytes as an dependency of the process being cached | 98 | /// Record a slice of bytes as an dependency of the process being cached |
| 75 | pub fn addSlice(self: *CacheHash, val: []const u8) void { | 99 | pub fn addBytes(self: *CacheHash, bytes: []const u8) void { |
| 76 | assert(self.manifest_file == null); | 100 | assert(self.manifest_file == null); |
| 77 | 101 | ||
| 78 | self.hasher.update(val); | 102 | self.hasher.update(mem.asBytes(&bytes.len)); |
| 79 | self.hasher.update(&[_]u8{0}); | 103 | self.hasher.update(bytes); |
| 80 | } | 104 | } |
| 81 | 105 | ||
| 82 | /// Convert the input value into bytes and record it as a dependency of the | 106 | pub fn addListOfBytes(self: *CacheHash, list_of_bytes: []const []const u8) void { |
| 83 | /// process being cached | ||
| 84 | pub fn add(self: *CacheHash, val: anytype) void { | ||
| 85 | assert(self.manifest_file == null); | 107 | assert(self.manifest_file == null); |
| 86 | 108 | ||
| 87 | const valPtr = switch (@typeInfo(@TypeOf(val))) { | 109 | self.add(list_of_bytes.items.len); |
| 88 | .Int => &val, | 110 | for (list_of_bytes) |bytes| self.addBytes(bytes); |
| 89 | .Pointer => val, | 111 | } |
| 90 | else => &val, | 112 | |
| 91 | }; | 113 | /// Convert the input value into bytes and record it as a dependency of the process being cached. |
| 114 | pub fn add(self: *CacheHash, x: anytype) void { | ||
| 115 | assert(self.manifest_file == null); | ||
| 116 | |||
| 117 | switch (@TypeOf(x)) { | ||
| 118 | std.builtin.Version => { | ||
| 119 | self.add(x.major); | ||
| 120 | self.add(x.minor); | ||
| 121 | self.add(x.patch); | ||
| 122 | return; | ||
| 123 | }, | ||
| 124 | else => {}, | ||
| 125 | } | ||
| 92 | 126 | ||
| 93 | self.addSlice(mem.asBytes(valPtr)); | 127 | switch (@typeInfo(@TypeOf(x))) { |
| 128 | .Bool, .Int, .Enum, .Array => self.addBytes(mem.asBytes(&x)), | ||
| 129 | else => @compileError("unable to hash type " ++ @typeName(@TypeOf(x))), | ||
| 130 | } | ||
| 94 | } | 131 | } |
| 95 | 132 | ||
| 96 | /// Add a file as a dependency of process being cached. When `CacheHash.hit` is | 133 | /// Add a file as a dependency of process being cached. When `CacheHash.hit` is |
| ... | @@ -122,7 +159,7 @@ pub const CacheHash = struct { | ... | @@ -122,7 +159,7 @@ pub const CacheHash = struct { |
| 122 | .bin_digest = undefined, | 159 | .bin_digest = undefined, |
| 123 | }; | 160 | }; |
| 124 | 161 | ||
| 125 | self.addSlice(resolved_path); | 162 | self.addBytes(resolved_path); |
| 126 | 163 | ||
| 127 | return idx; | 164 | return idx; |
| 128 | } | 165 | } |
| ... | @@ -143,7 +180,7 @@ pub const CacheHash = struct { | ... | @@ -143,7 +180,7 @@ pub const CacheHash = struct { |
| 143 | 180 | ||
| 144 | base64_encoder.encode(self.b64_digest[0..], &bin_digest); | 181 | base64_encoder.encode(self.b64_digest[0..], &bin_digest); |
| 145 | 182 | ||
| 146 | self.hasher = self.hasher_init; | 183 | self.hasher = hasher_init; |
| 147 | self.hasher.update(&bin_digest); | 184 | self.hasher.update(&bin_digest); |
| 148 | 185 | ||
| 149 | const manifest_file_path = try fmt.allocPrint(self.allocator, "{}.txt", .{self.b64_digest}); | 186 | const manifest_file_path = try fmt.allocPrint(self.allocator, "{}.txt", .{self.b64_digest}); |
| ... | @@ -244,7 +281,7 @@ pub const CacheHash = struct { | ... | @@ -244,7 +281,7 @@ pub const CacheHash = struct { |
| 244 | } | 281 | } |
| 245 | 282 | ||
| 246 | var actual_digest: [BIN_DIGEST_LEN]u8 = undefined; | 283 | var actual_digest: [BIN_DIGEST_LEN]u8 = undefined; |
| 247 | try hashFile(this_file, &actual_digest, self.hasher_init); | 284 | try hashFile(this_file, &actual_digest); |
| 248 | 285 | ||
| 249 | if (!mem.eql(u8, &cache_hash_file.bin_digest, &actual_digest)) { | 286 | if (!mem.eql(u8, &cache_hash_file.bin_digest, &actual_digest)) { |
| 250 | cache_hash_file.bin_digest = actual_digest; | 287 | cache_hash_file.bin_digest = actual_digest; |
| ... | @@ -262,7 +299,7 @@ pub const CacheHash = struct { | ... | @@ -262,7 +299,7 @@ pub const CacheHash = struct { |
| 262 | // cache miss | 299 | // cache miss |
| 263 | // keep the manifest file open | 300 | // keep the manifest file open |
| 264 | // reset the hash | 301 | // reset the hash |
| 265 | self.hasher = self.hasher_init; | 302 | self.hasher = hasher_init; |
| 266 | self.hasher.update(&bin_digest); | 303 | self.hasher.update(&bin_digest); |
| 267 | 304 | ||
| 268 | // Remove files not in the initial hash | 305 | // Remove files not in the initial hash |
| ... | @@ -310,7 +347,7 @@ pub const CacheHash = struct { | ... | @@ -310,7 +347,7 @@ pub const CacheHash = struct { |
| 310 | 347 | ||
| 311 | // Hash while reading from disk, to keep the contents in the cpu cache while | 348 | // Hash while reading from disk, to keep the contents in the cpu cache while |
| 312 | // doing hashing. | 349 | // doing hashing. |
| 313 | var hasher = self.hasher_init; | 350 | var hasher = hasher_init; |
| 314 | var off: usize = 0; | 351 | var off: usize = 0; |
| 315 | while (true) { | 352 | while (true) { |
| 316 | // give me everything you've got, captain | 353 | // give me everything you've got, captain |
| ... | @@ -323,7 +360,7 @@ pub const CacheHash = struct { | ... | @@ -323,7 +360,7 @@ pub const CacheHash = struct { |
| 323 | 360 | ||
| 324 | ch_file.contents = contents; | 361 | ch_file.contents = contents; |
| 325 | } else { | 362 | } else { |
| 326 | try hashFile(file, &ch_file.bin_digest, self.hasher_init); | 363 | try hashFile(file, &ch_file.bin_digest); |
| 327 | } | 364 | } |
| 328 | 365 | ||
| 329 | self.hasher.update(&ch_file.bin_digest); | 366 | self.hasher.update(&ch_file.bin_digest); |
| ... | @@ -435,11 +472,12 @@ pub const CacheHash = struct { | ... | @@ -435,11 +472,12 @@ pub const CacheHash = struct { |
| 435 | file.deinit(self.allocator); | 472 | file.deinit(self.allocator); |
| 436 | } | 473 | } |
| 437 | self.files.deinit(); | 474 | self.files.deinit(); |
| 438 | self.manifest_dir.close(); | 475 | if (self.owns_manifest_dir) |
| 476 | self.manifest_dir.close(); | ||
| 439 | } | 477 | } |
| 440 | }; | 478 | }; |
| 441 | 479 | ||
| 442 | fn hashFile(file: fs.File, bin_digest: []u8, hasher_init: anytype) !void { | 480 | fn hashFile(file: fs.File, bin_digest: []u8) !void { |
| 443 | var buf: [1024]u8 = undefined; | 481 | var buf: [1024]u8 = undefined; |
| 444 | 482 | ||
| 445 | var hasher = hasher_init; | 483 | var hasher = hasher_init; |
| ... | @@ -509,7 +547,7 @@ test "cache file and then recall it" { | ... | @@ -509,7 +547,7 @@ test "cache file and then recall it" { |
| 509 | 547 | ||
| 510 | ch.add(true); | 548 | ch.add(true); |
| 511 | ch.add(@as(u16, 1234)); | 549 | ch.add(@as(u16, 1234)); |
| 512 | ch.add("1234"); | 550 | ch.addBytes("1234"); |
| 513 | _ = try ch.addFile(temp_file, null); | 551 | _ = try ch.addFile(temp_file, null); |
| 514 | 552 | ||
| 515 | // There should be nothing in the cache | 553 | // There should be nothing in the cache |
| ... | @@ -523,7 +561,7 @@ test "cache file and then recall it" { | ... | @@ -523,7 +561,7 @@ test "cache file and then recall it" { |
| 523 | 561 | ||
| 524 | ch.add(true); | 562 | ch.add(true); |
| 525 | ch.add(@as(u16, 1234)); | 563 | ch.add(@as(u16, 1234)); |
| 526 | ch.add("1234"); | 564 | ch.addBytes("1234"); |
| 527 | _ = try ch.addFile(temp_file, null); | 565 | _ = try ch.addFile(temp_file, null); |
| 528 | 566 | ||
| 529 | // Cache hit! We just "built" the same file | 567 | // Cache hit! We just "built" the same file |
| ... | @@ -577,7 +615,7 @@ test "check that changing a file makes cache fail" { | ... | @@ -577,7 +615,7 @@ test "check that changing a file makes cache fail" { |
| 577 | var ch = try CacheHash.init(testing.allocator, cwd, temp_manifest_dir); | 615 | var ch = try CacheHash.init(testing.allocator, cwd, temp_manifest_dir); |
| 578 | defer ch.release(); | 616 | defer ch.release(); |
| 579 | 617 | ||
| 580 | ch.add("1234"); | 618 | ch.addBytes("1234"); |
| 581 | const temp_file_idx = try ch.addFile(temp_file, 100); | 619 | const temp_file_idx = try ch.addFile(temp_file, 100); |
| 582 | 620 | ||
| 583 | // There should be nothing in the cache | 621 | // There should be nothing in the cache |
| ... | @@ -594,7 +632,7 @@ test "check that changing a file makes cache fail" { | ... | @@ -594,7 +632,7 @@ test "check that changing a file makes cache fail" { |
| 594 | var ch = try CacheHash.init(testing.allocator, cwd, temp_manifest_dir); | 632 | var ch = try CacheHash.init(testing.allocator, cwd, temp_manifest_dir); |
| 595 | defer ch.release(); | 633 | defer ch.release(); |
| 596 | 634 | ||
| 597 | ch.add("1234"); | 635 | ch.addBytes("1234"); |
| 598 | const temp_file_idx = try ch.addFile(temp_file, 100); | 636 | const temp_file_idx = try ch.addFile(temp_file, 100); |
| 599 | 637 | ||
| 600 | // A file that we depend on has been updated, so the cache should not contain an entry for it | 638 | // A file that we depend on has been updated, so the cache should not contain an entry for it |
| ... | @@ -628,7 +666,7 @@ test "no file inputs" { | ... | @@ -628,7 +666,7 @@ test "no file inputs" { |
| 628 | var ch = try CacheHash.init(testing.allocator, cwd, temp_manifest_dir); | 666 | var ch = try CacheHash.init(testing.allocator, cwd, temp_manifest_dir); |
| 629 | defer ch.release(); | 667 | defer ch.release(); |
| 630 | 668 | ||
| 631 | ch.add("1234"); | 669 | ch.addBytes("1234"); |
| 632 | 670 | ||
| 633 | // There should be nothing in the cache | 671 | // There should be nothing in the cache |
| 634 | testing.expectEqual(@as(?[BASE64_DIGEST_LEN]u8, null), try ch.hit()); | 672 | testing.expectEqual(@as(?[BASE64_DIGEST_LEN]u8, null), try ch.hit()); |
| ... | @@ -639,7 +677,7 @@ test "no file inputs" { | ... | @@ -639,7 +677,7 @@ test "no file inputs" { |
| 639 | var ch = try CacheHash.init(testing.allocator, cwd, temp_manifest_dir); | 677 | var ch = try CacheHash.init(testing.allocator, cwd, temp_manifest_dir); |
| 640 | defer ch.release(); | 678 | defer ch.release(); |
| 641 | 679 | ||
| 642 | ch.add("1234"); | 680 | ch.addBytes("1234"); |
| 643 | 681 | ||
| 644 | digest2 = (try ch.hit()).?; | 682 | digest2 = (try ch.hit()).?; |
| 645 | } | 683 | } |
| ... | @@ -674,7 +712,7 @@ test "CacheHashes with files added after initial hash work" { | ... | @@ -674,7 +712,7 @@ test "CacheHashes with files added after initial hash work" { |
| 674 | var ch = try CacheHash.init(testing.allocator, cwd, temp_manifest_dir); | 712 | var ch = try CacheHash.init(testing.allocator, cwd, temp_manifest_dir); |
| 675 | defer ch.release(); | 713 | defer ch.release(); |
| 676 | 714 | ||
| 677 | ch.add("1234"); | 715 | ch.addBytes("1234"); |
| 678 | _ = try ch.addFile(temp_file1, null); | 716 | _ = try ch.addFile(temp_file1, null); |
| 679 | 717 | ||
| 680 | // There should be nothing in the cache | 718 | // There should be nothing in the cache |
| ... | @@ -688,7 +726,7 @@ test "CacheHashes with files added after initial hash work" { | ... | @@ -688,7 +726,7 @@ test "CacheHashes with files added after initial hash work" { |
| 688 | var ch = try CacheHash.init(testing.allocator, cwd, temp_manifest_dir); | 726 | var ch = try CacheHash.init(testing.allocator, cwd, temp_manifest_dir); |
| 689 | defer ch.release(); | 727 | defer ch.release(); |
| 690 | 728 | ||
| 691 | ch.add("1234"); | 729 | ch.addBytes("1234"); |
| 692 | _ = try ch.addFile(temp_file1, null); | 730 | _ = try ch.addFile(temp_file1, null); |
| 693 | 731 | ||
| 694 | digest2 = (try ch.hit()).?; | 732 | digest2 = (try ch.hit()).?; |
| ... | @@ -707,7 +745,7 @@ test "CacheHashes with files added after initial hash work" { | ... | @@ -707,7 +745,7 @@ test "CacheHashes with files added after initial hash work" { |
| 707 | var ch = try CacheHash.init(testing.allocator, cwd, temp_manifest_dir); | 745 | var ch = try CacheHash.init(testing.allocator, cwd, temp_manifest_dir); |
| 708 | defer ch.release(); | 746 | defer ch.release(); |
| 709 | 747 | ||
| 710 | ch.add("1234"); | 748 | ch.addBytes("1234"); |
| 711 | _ = try ch.addFile(temp_file1, null); | 749 | _ = try ch.addFile(temp_file1, null); |
| 712 | 750 | ||
| 713 | // A file that we depend on has been updated, so the cache should not contain an entry for it | 751 | // A file that we depend on has been updated, so the cache should not contain an entry for it |
lib/std/target.zig-2| ... | @@ -465,8 +465,6 @@ pub const Target = struct { | ... | @@ -465,8 +465,6 @@ pub const Target = struct { |
| 465 | }; | 465 | }; |
| 466 | 466 | ||
| 467 | pub const ObjectFormat = enum { | 467 | pub const ObjectFormat = enum { |
| 468 | /// TODO Get rid of this one. | ||
| 469 | unknown, | ||
| 470 | coff, | 468 | coff, |
| 471 | pe, | 469 | pe, |
| 472 | elf, | 470 | elf, |
lib/std/zig.zig+44-9| ... | @@ -71,17 +71,52 @@ pub fn binNameAlloc( | ... | @@ -71,17 +71,52 @@ pub fn binNameAlloc( |
| 71 | target: std.Target, | 71 | target: std.Target, |
| 72 | output_mode: std.builtin.OutputMode, | 72 | output_mode: std.builtin.OutputMode, |
| 73 | link_mode: ?std.builtin.LinkMode, | 73 | link_mode: ?std.builtin.LinkMode, |
| 74 | object_format: ?std.Target.ObjectFormat, | ||
| 74 | ) error{OutOfMemory}![]u8 { | 75 | ) error{OutOfMemory}![]u8 { |
| 75 | switch (output_mode) { | 76 | switch (object_format orelse target.getObjectFormat()) { |
| 76 | .Exe => return std.fmt.allocPrint(allocator, "{}{}", .{ root_name, target.exeFileExt() }), | 77 | .coff, .pe => switch (output_mode) { |
| 77 | .Lib => { | 78 | .Exe => { |
| 78 | const suffix = switch (link_mode orelse .Static) { | 79 | const suffix = switch (target.os.tag) { |
| 79 | .Static => target.staticLibSuffix(), | 80 | .uefi => ".efi", |
| 80 | .Dynamic => target.dynamicLibSuffix(), | 81 | else => ".exe", |
| 81 | }; | 82 | }; |
| 82 | return std.fmt.allocPrint(allocator, "{}{}{}", .{ target.libPrefix(), root_name, suffix }); | 83 | return std.fmt.allocPrint(allocator, "{}{}", .{ root_name, suffix }); |
| 84 | }, | ||
| 85 | .Lib => { | ||
| 86 | const suffix = switch (link_mode orelse .Static) { | ||
| 87 | .Static => ".lib", | ||
| 88 | .Dynamic => ".dll", | ||
| 89 | }; | ||
| 90 | return std.fmt.allocPrint(allocator, "{}{}{}", .{ target.libPrefix(), root_name, suffix }); | ||
| 91 | }, | ||
| 92 | .Obj => return std.fmt.allocPrint(allocator, "{}.obj", .{root_name}), | ||
| 93 | }, | ||
| 94 | .elf => switch (output_mode) { | ||
| 95 | .Exe => return allocator.dupe(u8, root_name), | ||
| 96 | .Lib => { | ||
| 97 | const suffix = switch (link_mode orelse .Static) { | ||
| 98 | .Static => ".a", | ||
| 99 | .Dynamic => ".so", | ||
| 100 | }; | ||
| 101 | return std.fmt.allocPrint(allocator, "{}{}{}", .{ target.libPrefix(), root_name, suffix }); | ||
| 102 | }, | ||
| 103 | .Obj => return std.fmt.allocPrint(allocator, "{}.o", .{root_name}), | ||
| 104 | }, | ||
| 105 | .macho => switch (output_mode) { | ||
| 106 | .Exe => return allocator.dupe(u8, root_name), | ||
| 107 | .Lib => { | ||
| 108 | const suffix = switch (link_mode orelse .Static) { | ||
| 109 | .Static => ".a", | ||
| 110 | .Dynamic => ".dylib", | ||
| 111 | }; | ||
| 112 | return std.fmt.allocPrint(allocator, "{}{}{}", .{ target.libPrefix(), root_name, suffix }); | ||
| 113 | }, | ||
| 114 | .Obj => return std.fmt.allocPrint(allocator, "{}.o", .{root_name}), | ||
| 83 | }, | 115 | }, |
| 84 | .Obj => return std.fmt.allocPrint(allocator, "{}{}", .{ root_name, target.oFileExt() }), | 116 | .wasm => return std.fmt.allocPrint(allocator, "{}.wasm", .{root_name}), |
| 117 | .c => return std.fmt.allocPrint(allocator, "{}.c", .{root_name}), | ||
| 118 | .hex => return std.fmt.allocPrint(allocator, "{}.ihex", .{root_name}), | ||
| 119 | .raw => return std.fmt.allocPrint(allocator, "{}.bin", .{root_name}), | ||
| 85 | } | 120 | } |
| 86 | } | 121 | } |
| 87 | 122 |
src-self-hosted/Module.zig+319-34| ... | @@ -22,11 +22,12 @@ const trace = @import("tracy.zig").trace; | ... | @@ -22,11 +22,12 @@ const trace = @import("tracy.zig").trace; |
| 22 | const liveness = @import("liveness.zig"); | 22 | const liveness = @import("liveness.zig"); |
| 23 | const astgen = @import("astgen.zig"); | 23 | const astgen = @import("astgen.zig"); |
| 24 | const zir_sema = @import("zir_sema.zig"); | 24 | const zir_sema = @import("zir_sema.zig"); |
| 25 | const build_options = @import("build_options"); | ||
| 25 | 26 | ||
| 26 | /// General-purpose allocator. Used for both temporary and long-term storage. | 27 | /// General-purpose allocator. Used for both temporary and long-term storage. |
| 27 | gpa: *Allocator, | 28 | gpa: *Allocator, |
| 28 | /// Pointer to externally managed resource. | 29 | /// Pointer to externally managed resource. `null` if there is no zig file being compiled. |
| 29 | root_pkg: *Package, | 30 | root_pkg: ?*Package, |
| 30 | /// Module owns this resource. | 31 | /// Module owns this resource. |
| 31 | /// The `Scope` is either a `Scope.ZIRModule` or `Scope.File`. | 32 | /// The `Scope` is either a `Scope.ZIRModule` or `Scope.File`. |
| 32 | root_scope: *Scope, | 33 | root_scope: *Scope, |
| ... | @@ -48,22 +49,26 @@ export_owners: std.AutoArrayHashMapUnmanaged(*Decl, []*Export) = .{}, | ... | @@ -48,22 +49,26 @@ export_owners: std.AutoArrayHashMapUnmanaged(*Decl, []*Export) = .{}, |
| 48 | /// Maps fully qualified namespaced names to the Decl struct for them. | 49 | /// Maps fully qualified namespaced names to the Decl struct for them. |
| 49 | decl_table: std.ArrayHashMapUnmanaged(Scope.NameHash, *Decl, Scope.name_hash_hash, Scope.name_hash_eql, false) = .{}, | 50 | decl_table: std.ArrayHashMapUnmanaged(Scope.NameHash, *Decl, Scope.name_hash_hash, Scope.name_hash_eql, false) = .{}, |
| 50 | 51 | ||
| 52 | c_object_table: std.AutoArrayHashMapUnmanaged(*CObject, void) = .{}, | ||
| 53 | |||
| 51 | link_error_flags: link.File.ErrorFlags = .{}, | 54 | link_error_flags: link.File.ErrorFlags = .{}, |
| 52 | 55 | ||
| 53 | work_queue: std.fifo.LinearFifo(WorkItem, .Dynamic), | 56 | work_queue: std.fifo.LinearFifo(WorkItem, .Dynamic), |
| 54 | 57 | ||
| 55 | /// We optimize memory usage for a compilation with no compile errors by storing the | 58 | /// We optimize memory usage for a compilation with no compile errors by storing the |
| 56 | /// error messages and mapping outside of `Decl`. | 59 | /// error messages and mapping outside of `Decl`. |
| 57 | /// The ErrorMsg memory is owned by the decl, using Module's allocator. | 60 | /// The ErrorMsg memory is owned by the decl, using Module's general purpose allocator. |
| 58 | /// Note that a Decl can succeed but the Fn it represents can fail. In this case, | 61 | /// Note that a Decl can succeed but the Fn it represents can fail. In this case, |
| 59 | /// a Decl can have a failed_decls entry but have analysis status of success. | 62 | /// a Decl can have a failed_decls entry but have analysis status of success. |
| 60 | failed_decls: std.AutoArrayHashMapUnmanaged(*Decl, *ErrorMsg) = .{}, | 63 | failed_decls: std.AutoArrayHashMapUnmanaged(*Decl, *ErrorMsg) = .{}, |
| 61 | /// Using a map here for consistency with the other fields here. | 64 | /// Using a map here for consistency with the other fields here. |
| 62 | /// The ErrorMsg memory is owned by the `Scope`, using Module's allocator. | 65 | /// The ErrorMsg memory is owned by the `Scope`, using Module's general purpose allocator. |
| 63 | failed_files: std.AutoArrayHashMapUnmanaged(*Scope, *ErrorMsg) = .{}, | 66 | failed_files: std.AutoArrayHashMapUnmanaged(*Scope, *ErrorMsg) = .{}, |
| 64 | /// Using a map here for consistency with the other fields here. | 67 | /// Using a map here for consistency with the other fields here. |
| 65 | /// The ErrorMsg memory is owned by the `Export`, using Module's allocator. | 68 | /// The ErrorMsg memory is owned by the `Export`, using Module's general purpose allocator. |
| 66 | failed_exports: std.AutoArrayHashMapUnmanaged(*Export, *ErrorMsg) = .{}, | 69 | failed_exports: std.AutoArrayHashMapUnmanaged(*Export, *ErrorMsg) = .{}, |
| 70 | /// The ErrorMsg memory is owned by the `CObject`, using Module's general purpose allocator. | ||
| 71 | failed_c_objects: std.AutoArrayHashMapUnmanaged(*CObject, *ErrorMsg) = .{}, | ||
| 67 | 72 | ||
| 68 | /// Incrementing integer used to compare against the corresponding Decl | 73 | /// Incrementing integer used to compare against the corresponding Decl |
| 69 | /// field to determine whether a Decl's status applies to an ongoing update, or a | 74 | /// field to determine whether a Decl's status applies to an ongoing update, or a |
| ... | @@ -79,10 +84,15 @@ deletion_set: std.ArrayListUnmanaged(*Decl) = .{}, | ... | @@ -79,10 +84,15 @@ deletion_set: std.ArrayListUnmanaged(*Decl) = .{}, |
| 79 | /// Owned by Module. | 84 | /// Owned by Module. |
| 80 | root_name: []u8, | 85 | root_name: []u8, |
| 81 | keep_source_files_loaded: bool, | 86 | keep_source_files_loaded: bool, |
| 87 | use_clang: bool, | ||
| 82 | 88 | ||
| 83 | /// Error tags and their values, tag names are duped with mod.gpa. | 89 | /// Error tags and their values, tag names are duped with mod.gpa. |
| 84 | global_error_set: std.StringHashMapUnmanaged(u16) = .{}, | 90 | global_error_set: std.StringHashMapUnmanaged(u16) = .{}, |
| 85 | 91 | ||
| 92 | c_source_files: []const []const u8, | ||
| 93 | clang_argv: []const []const u8, | ||
| 94 | cache: std.cache_hash.CacheHash, | ||
| 95 | |||
| 86 | pub const InnerError = error{ OutOfMemory, AnalysisFail }; | 96 | pub const InnerError = error{ OutOfMemory, AnalysisFail }; |
| 87 | 97 | ||
| 88 | const WorkItem = union(enum) { | 98 | const WorkItem = union(enum) { |
| ... | @@ -95,6 +105,9 @@ const WorkItem = union(enum) { | ... | @@ -95,6 +105,9 @@ const WorkItem = union(enum) { |
| 95 | /// The source file containing the Decl has been updated, and so the | 105 | /// The source file containing the Decl has been updated, and so the |
| 96 | /// Decl may need its line number information updated in the debug info. | 106 | /// Decl may need its line number information updated in the debug info. |
| 97 | update_line_number: *Decl, | 107 | update_line_number: *Decl, |
| 108 | /// Invoke the Clang compiler to create an object file, which gets linked | ||
| 109 | /// with the Module. | ||
| 110 | c_object: *CObject, | ||
| 98 | }; | 111 | }; |
| 99 | 112 | ||
| 100 | pub const Export = struct { | 113 | pub const Export = struct { |
| ... | @@ -230,6 +243,7 @@ pub const Decl = struct { | ... | @@ -230,6 +243,7 @@ pub const Decl = struct { |
| 230 | const src_decl = module.decls[self.src_index]; | 243 | const src_decl = module.decls[self.src_index]; |
| 231 | return src_decl.inst.src; | 244 | return src_decl.inst.src; |
| 232 | }, | 245 | }, |
| 246 | .none => unreachable, | ||
| 233 | .file, .block => unreachable, | 247 | .file, .block => unreachable, |
| 234 | .gen_zir => unreachable, | 248 | .gen_zir => unreachable, |
| 235 | .local_val => unreachable, | 249 | .local_val => unreachable, |
| ... | @@ -282,6 +296,30 @@ pub const Decl = struct { | ... | @@ -282,6 +296,30 @@ pub const Decl = struct { |
| 282 | } | 296 | } |
| 283 | }; | 297 | }; |
| 284 | 298 | ||
| 299 | pub const CObject = struct { | ||
| 300 | /// Relative to cwd. Owned by arena. | ||
| 301 | src_path: []const u8, | ||
| 302 | /// Owned by arena. | ||
| 303 | extra_flags: []const []const u8, | ||
| 304 | arena: std.heap.ArenaAllocator.State, | ||
| 305 | status: union(enum) { | ||
| 306 | new, | ||
| 307 | /// This is the output object path. Owned by gpa. | ||
| 308 | success: []u8, | ||
| 309 | /// There will be a corresponding ErrorMsg in Module.failed_c_objects. | ||
| 310 | /// This is the C source file contents (used for printing error messages). Owned by gpa. | ||
| 311 | failure: []u8, | ||
| 312 | }, | ||
| 313 | |||
| 314 | pub fn destroy(self: *CObject, gpa: *Allocator) void { | ||
| 315 | switch (self.status) { | ||
| 316 | .new => {}, | ||
| 317 | .failure, .success => |data| gpa.free(data), | ||
| 318 | } | ||
| 319 | self.arena.promote(gpa).deinit(); | ||
| 320 | } | ||
| 321 | }; | ||
| 322 | |||
| 285 | /// Fn struct memory is owned by the Decl's TypedValue.Managed arena allocator. | 323 | /// Fn struct memory is owned by the Decl's TypedValue.Managed arena allocator. |
| 286 | pub const Fn = struct { | 324 | pub const Fn = struct { |
| 287 | /// This memory owned by the Decl's TypedValue.Managed arena allocator. | 325 | /// This memory owned by the Decl's TypedValue.Managed arena allocator. |
| ... | @@ -361,6 +399,7 @@ pub const Scope = struct { | ... | @@ -361,6 +399,7 @@ pub const Scope = struct { |
| 361 | .zir_module => return &self.cast(ZIRModule).?.contents.module.arena.allocator, | 399 | .zir_module => return &self.cast(ZIRModule).?.contents.module.arena.allocator, |
| 362 | .file => unreachable, | 400 | .file => unreachable, |
| 363 | .container => unreachable, | 401 | .container => unreachable, |
| 402 | .none => unreachable, | ||
| 364 | } | 403 | } |
| 365 | } | 404 | } |
| 366 | 405 | ||
| ... | @@ -376,6 +415,7 @@ pub const Scope = struct { | ... | @@ -376,6 +415,7 @@ pub const Scope = struct { |
| 376 | .zir_module => null, | 415 | .zir_module => null, |
| 377 | .file => null, | 416 | .file => null, |
| 378 | .container => null, | 417 | .container => null, |
| 418 | .none => unreachable, | ||
| 379 | }; | 419 | }; |
| 380 | } | 420 | } |
| 381 | 421 | ||
| ... | @@ -390,6 +430,7 @@ pub const Scope = struct { | ... | @@ -390,6 +430,7 @@ pub const Scope = struct { |
| 390 | .decl => return self.cast(DeclAnalysis).?.decl.scope, | 430 | .decl => return self.cast(DeclAnalysis).?.decl.scope, |
| 391 | .file => return &self.cast(File).?.root_container.base, | 431 | .file => return &self.cast(File).?.root_container.base, |
| 392 | .zir_module, .container => return self, | 432 | .zir_module, .container => return self, |
| 433 | .none => unreachable, | ||
| 393 | } | 434 | } |
| 394 | } | 435 | } |
| 395 | 436 | ||
| ... | @@ -406,6 +447,7 @@ pub const Scope = struct { | ... | @@ -406,6 +447,7 @@ pub const Scope = struct { |
| 406 | .file => unreachable, | 447 | .file => unreachable, |
| 407 | .zir_module => return self.cast(ZIRModule).?.fullyQualifiedNameHash(name), | 448 | .zir_module => return self.cast(ZIRModule).?.fullyQualifiedNameHash(name), |
| 408 | .container => return self.cast(Container).?.fullyQualifiedNameHash(name), | 449 | .container => return self.cast(Container).?.fullyQualifiedNameHash(name), |
| 450 | .none => unreachable, | ||
| 409 | } | 451 | } |
| 410 | } | 452 | } |
| 411 | 453 | ||
| ... | @@ -414,6 +456,7 @@ pub const Scope = struct { | ... | @@ -414,6 +456,7 @@ pub const Scope = struct { |
| 414 | switch (self.tag) { | 456 | switch (self.tag) { |
| 415 | .file => return self.cast(File).?.contents.tree, | 457 | .file => return self.cast(File).?.contents.tree, |
| 416 | .zir_module => unreachable, | 458 | .zir_module => unreachable, |
| 459 | .none => unreachable, | ||
| 417 | .decl => return self.cast(DeclAnalysis).?.decl.scope.cast(Container).?.file_scope.contents.tree, | 460 | .decl => return self.cast(DeclAnalysis).?.decl.scope.cast(Container).?.file_scope.contents.tree, |
| 418 | .block => return self.cast(Block).?.decl.scope.cast(Container).?.file_scope.contents.tree, | 461 | .block => return self.cast(Block).?.decl.scope.cast(Container).?.file_scope.contents.tree, |
| 419 | .gen_zir => return self.cast(GenZIR).?.decl.scope.cast(Container).?.file_scope.contents.tree, | 462 | .gen_zir => return self.cast(GenZIR).?.decl.scope.cast(Container).?.file_scope.contents.tree, |
| ... | @@ -434,6 +477,7 @@ pub const Scope = struct { | ... | @@ -434,6 +477,7 @@ pub const Scope = struct { |
| 434 | .zir_module => unreachable, | 477 | .zir_module => unreachable, |
| 435 | .file => unreachable, | 478 | .file => unreachable, |
| 436 | .container => unreachable, | 479 | .container => unreachable, |
| 480 | .none => unreachable, | ||
| 437 | }; | 481 | }; |
| 438 | } | 482 | } |
| 439 | 483 | ||
| ... | @@ -444,6 +488,7 @@ pub const Scope = struct { | ... | @@ -444,6 +488,7 @@ pub const Scope = struct { |
| 444 | .container => return @fieldParentPtr(Container, "base", base).file_scope.sub_file_path, | 488 | .container => return @fieldParentPtr(Container, "base", base).file_scope.sub_file_path, |
| 445 | .file => return @fieldParentPtr(File, "base", base).sub_file_path, | 489 | .file => return @fieldParentPtr(File, "base", base).sub_file_path, |
| 446 | .zir_module => return @fieldParentPtr(ZIRModule, "base", base).sub_file_path, | 490 | .zir_module => return @fieldParentPtr(ZIRModule, "base", base).sub_file_path, |
| 491 | .none => unreachable, | ||
| 447 | .block => unreachable, | 492 | .block => unreachable, |
| 448 | .gen_zir => unreachable, | 493 | .gen_zir => unreachable, |
| 449 | .local_val => unreachable, | 494 | .local_val => unreachable, |
| ... | @@ -456,6 +501,7 @@ pub const Scope = struct { | ... | @@ -456,6 +501,7 @@ pub const Scope = struct { |
| 456 | switch (base.tag) { | 501 | switch (base.tag) { |
| 457 | .file => return @fieldParentPtr(File, "base", base).unload(gpa), | 502 | .file => return @fieldParentPtr(File, "base", base).unload(gpa), |
| 458 | .zir_module => return @fieldParentPtr(ZIRModule, "base", base).unload(gpa), | 503 | .zir_module => return @fieldParentPtr(ZIRModule, "base", base).unload(gpa), |
| 504 | .none => {}, | ||
| 459 | .block => unreachable, | 505 | .block => unreachable, |
| 460 | .gen_zir => unreachable, | 506 | .gen_zir => unreachable, |
| 461 | .local_val => unreachable, | 507 | .local_val => unreachable, |
| ... | @@ -470,6 +516,7 @@ pub const Scope = struct { | ... | @@ -470,6 +516,7 @@ pub const Scope = struct { |
| 470 | .container => return @fieldParentPtr(Container, "base", base).file_scope.getSource(module), | 516 | .container => return @fieldParentPtr(Container, "base", base).file_scope.getSource(module), |
| 471 | .file => return @fieldParentPtr(File, "base", base).getSource(module), | 517 | .file => return @fieldParentPtr(File, "base", base).getSource(module), |
| 472 | .zir_module => return @fieldParentPtr(ZIRModule, "base", base).getSource(module), | 518 | .zir_module => return @fieldParentPtr(ZIRModule, "base", base).getSource(module), |
| 519 | .none => unreachable, | ||
| 473 | .gen_zir => unreachable, | 520 | .gen_zir => unreachable, |
| 474 | .local_val => unreachable, | 521 | .local_val => unreachable, |
| 475 | .local_ptr => unreachable, | 522 | .local_ptr => unreachable, |
| ... | @@ -483,6 +530,7 @@ pub const Scope = struct { | ... | @@ -483,6 +530,7 @@ pub const Scope = struct { |
| 483 | switch (base.tag) { | 530 | switch (base.tag) { |
| 484 | .container => return @fieldParentPtr(Container, "base", base).removeDecl(child), | 531 | .container => return @fieldParentPtr(Container, "base", base).removeDecl(child), |
| 485 | .zir_module => return @fieldParentPtr(ZIRModule, "base", base).removeDecl(child), | 532 | .zir_module => return @fieldParentPtr(ZIRModule, "base", base).removeDecl(child), |
| 533 | .none => unreachable, | ||
| 486 | .file => unreachable, | 534 | .file => unreachable, |
| 487 | .block => unreachable, | 535 | .block => unreachable, |
| 488 | .gen_zir => unreachable, | 536 | .gen_zir => unreachable, |
| ... | @@ -505,6 +553,10 @@ pub const Scope = struct { | ... | @@ -505,6 +553,10 @@ pub const Scope = struct { |
| 505 | scope_zir_module.deinit(gpa); | 553 | scope_zir_module.deinit(gpa); |
| 506 | gpa.destroy(scope_zir_module); | 554 | gpa.destroy(scope_zir_module); |
| 507 | }, | 555 | }, |
| 556 | .none => { | ||
| 557 | const scope_none = @fieldParentPtr(None, "base", base); | ||
| 558 | gpa.destroy(scope_none); | ||
| 559 | }, | ||
| 508 | .block => unreachable, | 560 | .block => unreachable, |
| 509 | .gen_zir => unreachable, | 561 | .gen_zir => unreachable, |
| 510 | .local_val => unreachable, | 562 | .local_val => unreachable, |
| ... | @@ -527,6 +579,8 @@ pub const Scope = struct { | ... | @@ -527,6 +579,8 @@ pub const Scope = struct { |
| 527 | zir_module, | 579 | zir_module, |
| 528 | /// .zig source code. | 580 | /// .zig source code. |
| 529 | file, | 581 | file, |
| 582 | /// There is no .zig or .zir source code being compiled in this Module. | ||
| 583 | none, | ||
| 530 | /// struct, enum or union, every .file contains one of these. | 584 | /// struct, enum or union, every .file contains one of these. |
| 531 | container, | 585 | container, |
| 532 | block, | 586 | block, |
| ... | @@ -622,7 +676,7 @@ pub const Scope = struct { | ... | @@ -622,7 +676,7 @@ pub const Scope = struct { |
| 622 | pub fn getSource(self: *File, module: *Module) ![:0]const u8 { | 676 | pub fn getSource(self: *File, module: *Module) ![:0]const u8 { |
| 623 | switch (self.source) { | 677 | switch (self.source) { |
| 624 | .unloaded => { | 678 | .unloaded => { |
| 625 | const source = try module.root_pkg.root_src_dir.readFileAllocOptions( | 679 | const source = try module.root_pkg.?.root_src_dir.readFileAllocOptions( |
| 626 | module.gpa, | 680 | module.gpa, |
| 627 | self.sub_file_path, | 681 | self.sub_file_path, |
| 628 | std.math.maxInt(u32), | 682 | std.math.maxInt(u32), |
| ... | @@ -638,6 +692,12 @@ pub const Scope = struct { | ... | @@ -638,6 +692,12 @@ pub const Scope = struct { |
| 638 | } | 692 | } |
| 639 | }; | 693 | }; |
| 640 | 694 | ||
| 695 | /// For when there is no top level scope because there are no .zig files being compiled. | ||
| 696 | pub const None = struct { | ||
| 697 | pub const base_tag: Tag = .none; | ||
| 698 | base: Scope = Scope{ .tag = base_tag }, | ||
| 699 | }; | ||
| 700 | |||
| 641 | pub const ZIRModule = struct { | 701 | pub const ZIRModule = struct { |
| 642 | pub const base_tag: Tag = .zir_module; | 702 | pub const base_tag: Tag = .zir_module; |
| 643 | base: Scope = Scope{ .tag = base_tag }, | 703 | base: Scope = Scope{ .tag = base_tag }, |
| ... | @@ -720,7 +780,7 @@ pub const Scope = struct { | ... | @@ -720,7 +780,7 @@ pub const Scope = struct { |
| 720 | pub fn getSource(self: *ZIRModule, module: *Module) ![:0]const u8 { | 780 | pub fn getSource(self: *ZIRModule, module: *Module) ![:0]const u8 { |
| 721 | switch (self.source) { | 781 | switch (self.source) { |
| 722 | .unloaded => { | 782 | .unloaded => { |
| 723 | const source = try module.root_pkg.root_src_dir.readFileAllocOptions( | 783 | const source = try module.root_pkg.?.root_src_dir.readFileAllocOptions( |
| 724 | module.gpa, | 784 | module.gpa, |
| 725 | self.sub_file_path, | 785 | self.sub_file_path, |
| 726 | std.math.maxInt(u32), | 786 | std.math.maxInt(u32), |
| ... | @@ -855,20 +915,81 @@ pub const AllErrors = struct { | ... | @@ -855,20 +915,81 @@ pub const AllErrors = struct { |
| 855 | pub const InitOptions = struct { | 915 | pub const InitOptions = struct { |
| 856 | target: std.Target, | 916 | target: std.Target, |
| 857 | root_name: []const u8, | 917 | root_name: []const u8, |
| 858 | root_pkg: *Package, | 918 | root_pkg: ?*Package, |
| 859 | output_mode: std.builtin.OutputMode, | 919 | output_mode: std.builtin.OutputMode, |
| 860 | bin_file_dir: ?std.fs.Dir = null, | 920 | bin_file_dir: ?std.fs.Dir = null, |
| 861 | bin_file_path: []const u8, | 921 | bin_file_path: []const u8, |
| 922 | emit_h: ?[]const u8 = null, | ||
| 862 | link_mode: ?std.builtin.LinkMode = null, | 923 | link_mode: ?std.builtin.LinkMode = null, |
| 863 | object_format: ?std.builtin.ObjectFormat = null, | 924 | object_format: ?std.builtin.ObjectFormat = null, |
| 864 | optimize_mode: std.builtin.Mode = .Debug, | 925 | optimize_mode: std.builtin.Mode = .Debug, |
| 865 | keep_source_files_loaded: bool = false, | 926 | keep_source_files_loaded: bool = false, |
| 927 | clang_argv: []const []const u8 = &[0][]const u8{}, | ||
| 928 | lib_dirs: []const []const u8 = &[0][]const u8{}, | ||
| 929 | rpath_list: []const []const u8 = &[0][]const u8{}, | ||
| 930 | c_source_files: []const []const u8 = &[0][]const u8{}, | ||
| 931 | link_objects: []const []const u8 = &[0][]const u8{}, | ||
| 932 | framework_dirs: []const []const u8 = &[0][]const u8{}, | ||
| 933 | frameworks: []const []const u8 = &[0][]const u8{}, | ||
| 934 | system_libs: []const []const u8 = &[0][]const u8{}, | ||
| 935 | have_libc: bool = false, | ||
| 936 | have_libcpp: bool = false, | ||
| 937 | want_pic: ?bool = null, | ||
| 938 | want_sanitize_c: ?bool = null, | ||
| 939 | use_llvm: ?bool = null, | ||
| 940 | use_lld: ?bool = null, | ||
| 941 | use_clang: ?bool = null, | ||
| 942 | rdynamic: bool = false, | ||
| 943 | strip: bool = false, | ||
| 944 | linker_script: ?[]const u8 = null, | ||
| 945 | version_script: ?[]const u8 = null, | ||
| 946 | disable_c_depfile: bool = false, | ||
| 947 | override_soname: ?[]const u8 = null, | ||
| 948 | linker_optimization: ?[]const u8 = null, | ||
| 949 | linker_gc_sections: ?bool = null, | ||
| 950 | linker_allow_shlib_undefined: ?bool = null, | ||
| 951 | linker_bind_global_refs_locally: ?bool = null, | ||
| 952 | linker_z_nodelete: bool = false, | ||
| 953 | linker_z_defs: bool = false, | ||
| 954 | stack_size_override: u64 = 0, | ||
| 955 | compiler_id: [16]u8, | ||
| 866 | }; | 956 | }; |
| 867 | 957 | ||
| 868 | pub fn init(gpa: *Allocator, options: InitOptions) !Module { | 958 | pub fn init(gpa: *Allocator, options: InitOptions) !Module { |
| 869 | const root_name = try gpa.dupe(u8, options.root_name); | 959 | const root_name = try gpa.dupe(u8, options.root_name); |
| 870 | errdefer gpa.free(root_name); | 960 | errdefer gpa.free(root_name); |
| 871 | 961 | ||
| 962 | const ofmt = options.object_format orelse options.target.getObjectFormat(); | ||
| 963 | |||
| 964 | // Make a decision on whether to use LLD or our own linker. | ||
| 965 | const use_lld = if (options.use_lld) |explicit| explicit else blk: { | ||
| 966 | if (!build_options.have_llvm) | ||
| 967 | break :blk false; | ||
| 968 | |||
| 969 | if (ofmt == .c) | ||
| 970 | break :blk false; | ||
| 971 | |||
| 972 | // Our linker can't handle objects or most advanced options yet. | ||
| 973 | if (options.link_objects.len != 0 or | ||
| 974 | options.c_source_files.len != 0 or | ||
| 975 | options.frameworks.len != 0 or | ||
| 976 | options.system_libs.len != 0 or | ||
| 977 | options.have_libc or options.have_libcpp or | ||
| 978 | options.linker_script != null or options.version_script != null) | ||
| 979 | { | ||
| 980 | break :blk true; | ||
| 981 | } | ||
| 982 | break :blk false; | ||
| 983 | }; | ||
| 984 | |||
| 985 | // Make a decision on whether to use LLVM or our own backend. | ||
| 986 | const use_llvm = if (options.use_llvm) |explicit| explicit else blk: { | ||
| 987 | // We would want to prefer LLVM for release builds when it is available, however | ||
| 988 | // we don't have an LLVM backend yet :) | ||
| 989 | // We would also want to prefer LLVM for architectures that we don't have self-hosted support for too. | ||
| 990 | break :blk false; | ||
| 991 | }; | ||
| 992 | |||
| 872 | const bin_file_dir = options.bin_file_dir orelse std.fs.cwd(); | 993 | const bin_file_dir = options.bin_file_dir orelse std.fs.cwd(); |
| 873 | const bin_file = try link.File.openPath(gpa, bin_file_dir, options.bin_file_path, .{ | 994 | const bin_file = try link.File.openPath(gpa, bin_file_dir, options.bin_file_path, .{ |
| 874 | .root_name = root_name, | 995 | .root_name = root_name, |
| ... | @@ -876,39 +997,130 @@ pub fn init(gpa: *Allocator, options: InitOptions) !Module { | ... | @@ -876,39 +997,130 @@ pub fn init(gpa: *Allocator, options: InitOptions) !Module { |
| 876 | .target = options.target, | 997 | .target = options.target, |
| 877 | .output_mode = options.output_mode, | 998 | .output_mode = options.output_mode, |
| 878 | .link_mode = options.link_mode orelse .Static, | 999 | .link_mode = options.link_mode orelse .Static, |
| 879 | .object_format = options.object_format orelse options.target.getObjectFormat(), | 1000 | .object_format = ofmt, |
| 880 | .optimize_mode = options.optimize_mode, | 1001 | .optimize_mode = options.optimize_mode, |
| 1002 | .use_lld = use_lld, | ||
| 1003 | .use_llvm = use_llvm, | ||
| 1004 | .objects = options.link_objects, | ||
| 1005 | .frameworks = options.frameworks, | ||
| 1006 | .framework_dirs = options.framework_dirs, | ||
| 1007 | .system_libs = options.system_libs, | ||
| 1008 | .lib_dirs = options.lib_dirs, | ||
| 1009 | .rpath_list = options.rpath_list, | ||
| 1010 | .strip = options.strip, | ||
| 881 | }); | 1011 | }); |
| 882 | errdefer bin_file.destroy(); | 1012 | errdefer bin_file.destroy(); |
| 883 | 1013 | ||
| 884 | const root_scope = blk: { | 1014 | const root_scope = blk: { |
| 885 | if (mem.endsWith(u8, options.root_pkg.root_src_path, ".zig")) { | 1015 | if (options.root_pkg) |root_pkg| { |
| 886 | const root_scope = try gpa.create(Scope.File); | 1016 | if (mem.endsWith(u8, root_pkg.root_src_path, ".zig")) { |
| 887 | root_scope.* = .{ | 1017 | const root_scope = try gpa.create(Scope.File); |
| 888 | .sub_file_path = options.root_pkg.root_src_path, | 1018 | root_scope.* = .{ |
| 889 | .source = .{ .unloaded = {} }, | 1019 | .sub_file_path = root_pkg.root_src_path, |
| 890 | .contents = .{ .not_available = {} }, | 1020 | .source = .{ .unloaded = {} }, |
| 891 | .status = .never_loaded, | 1021 | .contents = .{ .not_available = {} }, |
| 892 | .root_container = .{ | 1022 | .status = .never_loaded, |
| 893 | .file_scope = root_scope, | 1023 | .root_container = .{ |
| 1024 | .file_scope = root_scope, | ||
| 1025 | .decls = .{}, | ||
| 1026 | }, | ||
| 1027 | }; | ||
| 1028 | break :blk &root_scope.base; | ||
| 1029 | } else if (mem.endsWith(u8, root_pkg.root_src_path, ".zir")) { | ||
| 1030 | const root_scope = try gpa.create(Scope.ZIRModule); | ||
| 1031 | root_scope.* = .{ | ||
| 1032 | .sub_file_path = root_pkg.root_src_path, | ||
| 1033 | .source = .{ .unloaded = {} }, | ||
| 1034 | .contents = .{ .not_available = {} }, | ||
| 1035 | .status = .never_loaded, | ||
| 894 | .decls = .{}, | 1036 | .decls = .{}, |
| 895 | }, | 1037 | }; |
| 896 | }; | 1038 | break :blk &root_scope.base; |
| 897 | break :blk &root_scope.base; | 1039 | } else { |
| 898 | } else if (mem.endsWith(u8, options.root_pkg.root_src_path, ".zir")) { | 1040 | unreachable; |
| 899 | const root_scope = try gpa.create(Scope.ZIRModule); | 1041 | } |
| 900 | root_scope.* = .{ | ||
| 901 | .sub_file_path = options.root_pkg.root_src_path, | ||
| 902 | .source = .{ .unloaded = {} }, | ||
| 903 | .contents = .{ .not_available = {} }, | ||
| 904 | .status = .never_loaded, | ||
| 905 | .decls = .{}, | ||
| 906 | }; | ||
| 907 | break :blk &root_scope.base; | ||
| 908 | } else { | 1042 | } else { |
| 909 | unreachable; | 1043 | const root_scope = try gpa.create(Scope.None); |
| 1044 | root_scope.* = .{}; | ||
| 1045 | break :blk &root_scope.base; | ||
| 1046 | } | ||
| 1047 | }; | ||
| 1048 | |||
| 1049 | // We put everything into the cache hash except for the root source file, because we want to | ||
| 1050 | // find the same binary and incrementally update it even if the file contents changed. | ||
| 1051 | const cache_dir = if (options.root_pkg) |root_pkg| root_pkg.root_src_dir else std.fs.cwd(); | ||
| 1052 | var cache = try std.cache_hash.CacheHash.init(gpa, cache_dir, "zig-cache"); | ||
| 1053 | errdefer cache.release(); | ||
| 1054 | |||
| 1055 | // Now we will prepare hash state initializations to avoid redundantly computing hashes. | ||
| 1056 | // First we add common things between things that apply to zig source and all c source files. | ||
| 1057 | cache.add(options.compiler_id); | ||
| 1058 | cache.add(options.optimize_mode); | ||
| 1059 | cache.add(options.target.cpu.arch); | ||
| 1060 | cache.addBytes(options.target.cpu.model.name); | ||
| 1061 | cache.add(options.target.cpu.features.ints); | ||
| 1062 | cache.add(options.target.os.tag); | ||
| 1063 | switch (options.target.os.tag) { | ||
| 1064 | .linux => { | ||
| 1065 | cache.add(options.target.os.version_range.linux.range.min); | ||
| 1066 | cache.add(options.target.os.version_range.linux.range.max); | ||
| 1067 | cache.add(options.target.os.version_range.linux.glibc); | ||
| 1068 | }, | ||
| 1069 | .windows => { | ||
| 1070 | cache.add(options.target.os.version_range.windows.min); | ||
| 1071 | cache.add(options.target.os.version_range.windows.max); | ||
| 1072 | }, | ||
| 1073 | .freebsd, | ||
| 1074 | .macosx, | ||
| 1075 | .ios, | ||
| 1076 | .tvos, | ||
| 1077 | .watchos, | ||
| 1078 | .netbsd, | ||
| 1079 | .openbsd, | ||
| 1080 | .dragonfly, | ||
| 1081 | => { | ||
| 1082 | cache.add(options.target.os.version_range.semver.min); | ||
| 1083 | cache.add(options.target.os.version_range.semver.max); | ||
| 1084 | }, | ||
| 1085 | else => {}, | ||
| 1086 | } | ||
| 1087 | cache.add(options.target.abi); | ||
| 1088 | cache.add(ofmt); | ||
| 1089 | // TODO PIC (see detect_pic from codegen.cpp) | ||
| 1090 | cache.add(bin_file.options.link_mode); | ||
| 1091 | cache.add(options.strip); | ||
| 1092 | |||
| 1093 | // Make a decision on whether to use Clang for translate-c and compiling C files. | ||
| 1094 | const use_clang = if (options.use_clang) |explicit| explicit else blk: { | ||
| 1095 | if (build_options.have_llvm) { | ||
| 1096 | // Can't use it if we don't have it! | ||
| 1097 | break :blk false; | ||
| 910 | } | 1098 | } |
| 1099 | // It's not planned to do our own translate-c or C compilation. | ||
| 1100 | break :blk true; | ||
| 911 | }; | 1101 | }; |
| 1102 | var c_object_table = std.AutoArrayHashMapUnmanaged(*CObject, void){}; | ||
| 1103 | errdefer { | ||
| 1104 | for (c_object_table.items()) |entry| entry.key.destroy(gpa); | ||
| 1105 | c_object_table.deinit(gpa); | ||
| 1106 | } | ||
| 1107 | // Add a `CObject` for each `c_source_files`. | ||
| 1108 | try c_object_table.ensureCapacity(gpa, options.c_source_files.len); | ||
| 1109 | for (options.c_source_files) |c_source_file| { | ||
| 1110 | var local_arena = std.heap.ArenaAllocator.init(gpa); | ||
| 1111 | errdefer local_arena.deinit(); | ||
| 1112 | |||
| 1113 | const c_object = try local_arena.allocator.create(CObject); | ||
| 1114 | const src_path = try local_arena.allocator.dupe(u8, c_source_file); | ||
| 1115 | |||
| 1116 | c_object.* = .{ | ||
| 1117 | .status = .{ .new = {} }, | ||
| 1118 | .src_path = src_path, | ||
| 1119 | .extra_flags = &[0][]const u8{}, | ||
| 1120 | .arena = local_arena.state, | ||
| 1121 | }; | ||
| 1122 | c_object_table.putAssumeCapacityNoClobber(c_object, {}); | ||
| 1123 | } | ||
| 912 | 1124 | ||
| 913 | return Module{ | 1125 | return Module{ |
| 914 | .gpa = gpa, | 1126 | .gpa = gpa, |
| ... | @@ -920,6 +1132,11 @@ pub fn init(gpa: *Allocator, options: InitOptions) !Module { | ... | @@ -920,6 +1132,11 @@ pub fn init(gpa: *Allocator, options: InitOptions) !Module { |
| 920 | .bin_file = bin_file, | 1132 | .bin_file = bin_file, |
| 921 | .work_queue = std.fifo.LinearFifo(WorkItem, .Dynamic).init(gpa), | 1133 | .work_queue = std.fifo.LinearFifo(WorkItem, .Dynamic).init(gpa), |
| 922 | .keep_source_files_loaded = options.keep_source_files_loaded, | 1134 | .keep_source_files_loaded = options.keep_source_files_loaded, |
| 1135 | .use_clang = use_clang, | ||
| 1136 | .clang_argv = options.clang_argv, | ||
| 1137 | .c_source_files = options.c_source_files, | ||
| 1138 | .cache = cache, | ||
| 1139 | .c_object_table = c_object_table, | ||
| 923 | }; | 1140 | }; |
| 924 | } | 1141 | } |
| 925 | 1142 | ||
| ... | @@ -935,11 +1152,21 @@ pub fn deinit(self: *Module) void { | ... | @@ -935,11 +1152,21 @@ pub fn deinit(self: *Module) void { |
| 935 | } | 1152 | } |
| 936 | self.decl_table.deinit(gpa); | 1153 | self.decl_table.deinit(gpa); |
| 937 | 1154 | ||
| 1155 | for (self.c_object_table.items()) |entry| { | ||
| 1156 | entry.key.destroy(gpa); | ||
| 1157 | } | ||
| 1158 | self.c_object_table.deinit(gpa); | ||
| 1159 | |||
| 938 | for (self.failed_decls.items()) |entry| { | 1160 | for (self.failed_decls.items()) |entry| { |
| 939 | entry.value.destroy(gpa); | 1161 | entry.value.destroy(gpa); |
| 940 | } | 1162 | } |
| 941 | self.failed_decls.deinit(gpa); | 1163 | self.failed_decls.deinit(gpa); |
| 942 | 1164 | ||
| 1165 | for (self.failed_c_objects.items()) |entry| { | ||
| 1166 | entry.value.destroy(gpa); | ||
| 1167 | } | ||
| 1168 | self.failed_c_objects.deinit(gpa); | ||
| 1169 | |||
| 943 | for (self.failed_files.items()) |entry| { | 1170 | for (self.failed_files.items()) |entry| { |
| 944 | entry.value.destroy(gpa); | 1171 | entry.value.destroy(gpa); |
| 945 | } | 1172 | } |
| ... | @@ -969,6 +1196,7 @@ pub fn deinit(self: *Module) void { | ... | @@ -969,6 +1196,7 @@ pub fn deinit(self: *Module) void { |
| 969 | gpa.free(entry.key); | 1196 | gpa.free(entry.key); |
| 970 | } | 1197 | } |
| 971 | self.global_error_set.deinit(gpa); | 1198 | self.global_error_set.deinit(gpa); |
| 1199 | self.cache.release(); | ||
| 972 | self.* = undefined; | 1200 | self.* = undefined; |
| 973 | } | 1201 | } |
| 974 | 1202 | ||
| ... | @@ -995,7 +1223,15 @@ pub fn update(self: *Module) !void { | ... | @@ -995,7 +1223,15 @@ pub fn update(self: *Module) !void { |
| 995 | 1223 | ||
| 996 | self.generation += 1; | 1224 | self.generation += 1; |
| 997 | 1225 | ||
| 998 | // TODO Use the cache hash file system to detect which source files changed. | 1226 | // For compiling C objects, we rely on the cache hash system to avoid duplicating work. |
| 1227 | // TODO Look into caching this data in memory to improve performance. | ||
| 1228 | // Add a WorkItem for each C object. | ||
| 1229 | try self.work_queue.ensureUnusedCapacity(self.c_object_table.items().len); | ||
| 1230 | for (self.c_object_table.items()) |entry| { | ||
| 1231 | self.work_queue.writeItemAssumeCapacity(.{ .c_object = entry.key }); | ||
| 1232 | } | ||
| 1233 | |||
| 1234 | // TODO Detect which source files changed. | ||
| 999 | // Until then we simulate a full cache miss. Source files could have been loaded for any reason; | 1235 | // Until then we simulate a full cache miss. Source files could have been loaded for any reason; |
| 1000 | // to force a refresh we unload now. | 1236 | // to force a refresh we unload now. |
| 1001 | if (self.root_scope.cast(Scope.File)) |zig_file| { | 1237 | if (self.root_scope.cast(Scope.File)) |zig_file| { |
| ... | @@ -1053,6 +1289,7 @@ pub fn makeBinFileWritable(self: *Module) !void { | ... | @@ -1053,6 +1289,7 @@ pub fn makeBinFileWritable(self: *Module) !void { |
| 1053 | 1289 | ||
| 1054 | pub fn totalErrorCount(self: *Module) usize { | 1290 | pub fn totalErrorCount(self: *Module) usize { |
| 1055 | const total = self.failed_decls.items().len + | 1291 | const total = self.failed_decls.items().len + |
| 1292 | self.failed_c_objects.items().len + | ||
| 1056 | self.failed_files.items().len + | 1293 | self.failed_files.items().len + |
| 1057 | self.failed_exports.items().len; | 1294 | self.failed_exports.items().len; |
| 1058 | return if (total == 0) @boolToInt(self.link_error_flags.no_entry_point_found) else total; | 1295 | return if (total == 0) @boolToInt(self.link_error_flags.no_entry_point_found) else total; |
| ... | @@ -1065,6 +1302,12 @@ pub fn getAllErrorsAlloc(self: *Module) !AllErrors { | ... | @@ -1065,6 +1302,12 @@ pub fn getAllErrorsAlloc(self: *Module) !AllErrors { |
| 1065 | var errors = std.ArrayList(AllErrors.Message).init(self.gpa); | 1302 | var errors = std.ArrayList(AllErrors.Message).init(self.gpa); |
| 1066 | defer errors.deinit(); | 1303 | defer errors.deinit(); |
| 1067 | 1304 | ||
| 1305 | for (self.failed_c_objects.items()) |entry| { | ||
| 1306 | const c_object = entry.key; | ||
| 1307 | const err_msg = entry.value; | ||
| 1308 | const source = c_object.status.failure; | ||
| 1309 | try AllErrors.add(&arena, &errors, c_object.src_path, source, err_msg.*); | ||
| 1310 | } | ||
| 1068 | for (self.failed_files.items()) |entry| { | 1311 | for (self.failed_files.items()) |entry| { |
| 1069 | const scope = entry.key; | 1312 | const scope = entry.key; |
| 1070 | const err_msg = entry.value; | 1313 | const err_msg = entry.value; |
| ... | @@ -1085,8 +1328,14 @@ pub fn getAllErrorsAlloc(self: *Module) !AllErrors { | ... | @@ -1085,8 +1328,14 @@ pub fn getAllErrorsAlloc(self: *Module) !AllErrors { |
| 1085 | } | 1328 | } |
| 1086 | 1329 | ||
| 1087 | if (errors.items.len == 0 and self.link_error_flags.no_entry_point_found) { | 1330 | if (errors.items.len == 0 and self.link_error_flags.no_entry_point_found) { |
| 1331 | const global_err_src_path = blk: { | ||
| 1332 | if (self.root_pkg) |root_pkg| break :blk root_pkg.root_src_path; | ||
| 1333 | if (self.c_source_files.len != 0) break :blk self.c_source_files[0]; | ||
| 1334 | if (self.bin_file.options.objects.len != 0) break :blk self.bin_file.options.objects[0]; | ||
| 1335 | break :blk "(no file)"; | ||
| 1336 | }; | ||
| 1088 | try errors.append(.{ | 1337 | try errors.append(.{ |
| 1089 | .src_path = self.root_pkg.root_src_path, | 1338 | .src_path = global_err_src_path, |
| 1090 | .line = 0, | 1339 | .line = 0, |
| 1091 | .column = 0, | 1340 | .column = 0, |
| 1092 | .byte_offset = 0, | 1341 | .byte_offset = 0, |
| ... | @@ -1175,6 +1424,41 @@ pub fn performAllTheWork(self: *Module) error{OutOfMemory}!void { | ... | @@ -1175,6 +1424,41 @@ pub fn performAllTheWork(self: *Module) error{OutOfMemory}!void { |
| 1175 | decl.analysis = .codegen_failure_retryable; | 1424 | decl.analysis = .codegen_failure_retryable; |
| 1176 | }; | 1425 | }; |
| 1177 | }, | 1426 | }, |
| 1427 | .c_object => |c_object| { | ||
| 1428 | // Free the previous attempt. | ||
| 1429 | switch (c_object.status) { | ||
| 1430 | .new => {}, | ||
| 1431 | .success => |o_file_path| { | ||
| 1432 | self.gpa.free(o_file_path); | ||
| 1433 | c_object.status = .{ .new = {} }; | ||
| 1434 | }, | ||
| 1435 | .failure => |source| { | ||
| 1436 | self.failed_c_objects.removeAssertDiscard(c_object); | ||
| 1437 | self.gpa.free(source); | ||
| 1438 | |||
| 1439 | c_object.status = .{ .new = {} }; | ||
| 1440 | }, | ||
| 1441 | } | ||
| 1442 | if (!build_options.have_llvm) { | ||
| 1443 | try self.failed_c_objects.ensureCapacity(self.gpa, self.failed_c_objects.items().len + 1); | ||
| 1444 | self.failed_c_objects.putAssumeCapacityNoClobber(c_object, try ErrorMsg.create( | ||
| 1445 | self.gpa, | ||
| 1446 | 0, | ||
| 1447 | "clang not available: compiler not built with LLVM extensions enabled", | ||
| 1448 | .{}, | ||
| 1449 | )); | ||
| 1450 | c_object.status = .{ .failure = "" }; | ||
| 1451 | continue; | ||
| 1452 | } | ||
| 1453 | try self.failed_c_objects.ensureCapacity(self.gpa, self.failed_c_objects.items().len + 1); | ||
| 1454 | self.failed_c_objects.putAssumeCapacityNoClobber(c_object, try ErrorMsg.create( | ||
| 1455 | self.gpa, | ||
| 1456 | 0, | ||
| 1457 | "TODO: implement invoking clang to compile C source files", | ||
| 1458 | .{}, | ||
| 1459 | )); | ||
| 1460 | c_object.status = .{ .failure = "" }; | ||
| 1461 | }, | ||
| 1178 | }; | 1462 | }; |
| 1179 | } | 1463 | } |
| 1180 | 1464 | ||
| ... | @@ -3161,6 +3445,7 @@ fn failWithOwnedErrorMsg(self: *Module, scope: *Scope, src: usize, err_msg: *Err | ... | @@ -3161,6 +3445,7 @@ fn failWithOwnedErrorMsg(self: *Module, scope: *Scope, src: usize, err_msg: *Err |
| 3161 | zir_module.status = .loaded_sema_failure; | 3445 | zir_module.status = .loaded_sema_failure; |
| 3162 | self.failed_files.putAssumeCapacityNoClobber(scope, err_msg); | 3446 | self.failed_files.putAssumeCapacityNoClobber(scope, err_msg); |
| 3163 | }, | 3447 | }, |
| 3448 | .none => unreachable, | ||
| 3164 | .file => unreachable, | 3449 | .file => unreachable, |
| 3165 | .container => unreachable, | 3450 | .container => unreachable, |
| 3166 | } | 3451 | } |
src-self-hosted/link.zig+26-5| ... | @@ -16,14 +16,36 @@ pub const Options = struct { | ... | @@ -16,14 +16,36 @@ pub const Options = struct { |
| 16 | object_format: std.builtin.ObjectFormat, | 16 | object_format: std.builtin.ObjectFormat, |
| 17 | optimize_mode: std.builtin.Mode, | 17 | optimize_mode: std.builtin.Mode, |
| 18 | root_name: []const u8, | 18 | root_name: []const u8, |
| 19 | root_pkg: *const Package, | 19 | root_pkg: ?*const Package, |
| 20 | /// Used for calculating how much space to reserve for symbols in case the binary file | 20 | /// Used for calculating how much space to reserve for symbols in case the binary file |
| 21 | /// does not already have a symbol table. | 21 | /// does not already have a symbol table. |
| 22 | symbol_count_hint: u64 = 32, | 22 | symbol_count_hint: u64 = 32, |
| 23 | /// Used for calculating how much space to reserve for executable program code in case | 23 | /// Used for calculating how much space to reserve for executable program code in case |
| 24 | /// the binary file deos not already have such a section. | 24 | /// the binary file does not already have such a section. |
| 25 | program_code_size_hint: u64 = 256 * 1024, | 25 | program_code_size_hint: u64 = 256 * 1024, |
| 26 | entry_addr: ?u64 = null, | 26 | entry_addr: ?u64 = null, |
| 27 | /// Set to `true` to omit debug info. | ||
| 28 | strip: bool = false, | ||
| 29 | /// If this is true then this link code is responsible for outputting an object | ||
| 30 | /// file and then using LLD to link it together with the link options and other objects. | ||
| 31 | /// Otherwise (depending on `use_llvm`) this link code directly outputs and updates the final binary. | ||
| 32 | use_lld: bool = false, | ||
| 33 | /// If this is true then this link code is responsible for making an LLVM IR Module, | ||
| 34 | /// outputting it to an object file, and then linking that together with link options and | ||
| 35 | /// other objects. | ||
| 36 | /// Otherwise (depending on `use_lld`) this link code directly outputs and updates the final binary. | ||
| 37 | use_llvm: bool = false, | ||
| 38 | |||
| 39 | objects: []const []const u8 = &[0][]const u8{}, | ||
| 40 | framework_dirs: []const []const u8 = &[0][]const u8{}, | ||
| 41 | frameworks: []const []const u8 = &[0][]const u8{}, | ||
| 42 | system_libs: []const []const u8 = &[0][]const u8{}, | ||
| 43 | lib_dirs: []const []const u8 = &[0][]const u8{}, | ||
| 44 | rpath_list: []const []const u8 = &[0][]const u8{}, | ||
| 45 | |||
| 46 | pub fn effectiveOutputMode(options: Options) std.builtin.OutputMode { | ||
| 47 | return if (options.use_lld) .Obj else options.output_mode; | ||
| 48 | } | ||
| 27 | }; | 49 | }; |
| 28 | 50 | ||
| 29 | pub const File = struct { | 51 | pub const File = struct { |
| ... | @@ -67,14 +89,13 @@ pub const File = struct { | ... | @@ -67,14 +89,13 @@ pub const File = struct { |
| 67 | /// and does not cause Illegal Behavior. This operation is not atomic. | 89 | /// and does not cause Illegal Behavior. This operation is not atomic. |
| 68 | pub fn openPath(allocator: *Allocator, dir: fs.Dir, sub_path: []const u8, options: Options) !*File { | 90 | pub fn openPath(allocator: *Allocator, dir: fs.Dir, sub_path: []const u8, options: Options) !*File { |
| 69 | switch (options.object_format) { | 91 | switch (options.object_format) { |
| 70 | .unknown => unreachable, | ||
| 71 | .coff, .pe => return Coff.openPath(allocator, dir, sub_path, options), | 92 | .coff, .pe => return Coff.openPath(allocator, dir, sub_path, options), |
| 72 | .elf => return Elf.openPath(allocator, dir, sub_path, options), | 93 | .elf => return Elf.openPath(allocator, dir, sub_path, options), |
| 73 | .macho => return MachO.openPath(allocator, dir, sub_path, options), | 94 | .macho => return MachO.openPath(allocator, dir, sub_path, options), |
| 74 | .wasm => return Wasm.openPath(allocator, dir, sub_path, options), | 95 | .wasm => return Wasm.openPath(allocator, dir, sub_path, options), |
| 75 | .c => return C.openPath(allocator, dir, sub_path, options), | 96 | .c => return C.openPath(allocator, dir, sub_path, options), |
| 76 | .hex => return error.TODOImplementHex, | 97 | .hex => return error.HexObjectFormatUnimplemented, |
| 77 | .raw => return error.TODOImplementRaw, | 98 | .raw => return error.RawObjectFormatUnimplemented, |
| 78 | } | 99 | } |
| 79 | } | 100 | } |
| 80 | 101 |
src-self-hosted/link/C.zig+3| ... | @@ -25,6 +25,9 @@ error_msg: *Module.ErrorMsg = undefined, | ... | @@ -25,6 +25,9 @@ error_msg: *Module.ErrorMsg = undefined, |
| 25 | pub fn openPath(allocator: *Allocator, dir: fs.Dir, sub_path: []const u8, options: link.Options) !*File { | 25 | pub fn openPath(allocator: *Allocator, dir: fs.Dir, sub_path: []const u8, options: link.Options) !*File { |
| 26 | assert(options.object_format == .c); | 26 | assert(options.object_format == .c); |
| 27 | 27 | ||
| 28 | if (options.use_llvm) return error.LLVM_HasNoCBackend; | ||
| 29 | if (options.use_lld) return error.LLD_HasNoCBackend; | ||
| 30 | |||
| 28 | const file = try dir.createFile(sub_path, .{ .truncate = true, .read = true, .mode = link.determineMode(options) }); | 31 | const file = try dir.createFile(sub_path, .{ .truncate = true, .read = true, .mode = link.determineMode(options) }); |
| 29 | errdefer file.close(); | 32 | errdefer file.close(); |
| 30 | 33 |
src-self-hosted/link/Coff.zig+3| ... | @@ -113,6 +113,9 @@ pub const SrcFn = void; | ... | @@ -113,6 +113,9 @@ pub const SrcFn = void; |
| 113 | pub fn openPath(allocator: *Allocator, dir: fs.Dir, sub_path: []const u8, options: link.Options) !*link.File { | 113 | pub fn openPath(allocator: *Allocator, dir: fs.Dir, sub_path: []const u8, options: link.Options) !*link.File { |
| 114 | assert(options.object_format == .coff); | 114 | assert(options.object_format == .coff); |
| 115 | 115 | ||
| 116 | if (options.use_llvm) return error.LLVM_BackendIsTODO_ForCoff; // TODO | ||
| 117 | if (options.use_lld) return error.LLD_LinkingIsTODO_ForCoff; // TODO | ||
| 118 | |||
| 116 | const file = try dir.createFile(sub_path, .{ .truncate = false, .read = true, .mode = link.determineMode(options) }); | 119 | const file = try dir.createFile(sub_path, .{ .truncate = false, .read = true, .mode = link.determineMode(options) }); |
| 117 | errdefer file.close(); | 120 | errdefer file.close(); |
| 118 | 121 |
src-self-hosted/link/Elf.zig+12-9| ... | @@ -219,6 +219,9 @@ pub const SrcFn = struct { | ... | @@ -219,6 +219,9 @@ pub const SrcFn = struct { |
| 219 | pub fn openPath(allocator: *Allocator, dir: fs.Dir, sub_path: []const u8, options: link.Options) !*File { | 219 | pub fn openPath(allocator: *Allocator, dir: fs.Dir, sub_path: []const u8, options: link.Options) !*File { |
| 220 | assert(options.object_format == .elf); | 220 | assert(options.object_format == .elf); |
| 221 | 221 | ||
| 222 | if (options.use_llvm) return error.LLVM_BackendIsTODO_ForELF; // TODO | ||
| 223 | if (options.use_lld) return error.LLD_LinkingIsTODOForELF; // TODO | ||
| 224 | |||
| 222 | const file = try dir.createFile(sub_path, .{ .truncate = false, .read = true, .mode = link.determineMode(options) }); | 225 | const file = try dir.createFile(sub_path, .{ .truncate = false, .read = true, .mode = link.determineMode(options) }); |
| 223 | errdefer file.close(); | 226 | errdefer file.close(); |
| 224 | 227 | ||
| ... | @@ -235,7 +238,7 @@ pub fn openPath(allocator: *Allocator, dir: fs.Dir, sub_path: []const u8, option | ... | @@ -235,7 +238,7 @@ pub fn openPath(allocator: *Allocator, dir: fs.Dir, sub_path: []const u8, option |
| 235 | 238 | ||
| 236 | /// Returns error.IncrFailed if incremental update could not be performed. | 239 | /// Returns error.IncrFailed if incremental update could not be performed. |
| 237 | fn openFile(allocator: *Allocator, file: fs.File, options: link.Options) !Elf { | 240 | fn openFile(allocator: *Allocator, file: fs.File, options: link.Options) !Elf { |
| 238 | switch (options.output_mode) { | 241 | switch (options.effectiveOutputMode()) { |
| 239 | .Exe => {}, | 242 | .Exe => {}, |
| 240 | .Obj => {}, | 243 | .Obj => {}, |
| 241 | .Lib => return error.IncrFailed, | 244 | .Lib => return error.IncrFailed, |
| ... | @@ -264,7 +267,7 @@ fn openFile(allocator: *Allocator, file: fs.File, options: link.Options) !Elf { | ... | @@ -264,7 +267,7 @@ fn openFile(allocator: *Allocator, file: fs.File, options: link.Options) !Elf { |
| 264 | /// Truncates the existing file contents and overwrites the contents. | 267 | /// Truncates the existing file contents and overwrites the contents. |
| 265 | /// Returns an error if `file` is not already open with +read +write +seek abilities. | 268 | /// Returns an error if `file` is not already open with +read +write +seek abilities. |
| 266 | fn createFile(allocator: *Allocator, file: fs.File, options: link.Options) !Elf { | 269 | fn createFile(allocator: *Allocator, file: fs.File, options: link.Options) !Elf { |
| 267 | switch (options.output_mode) { | 270 | switch (options.effectiveOutputMode()) { |
| 268 | .Exe => {}, | 271 | .Exe => {}, |
| 269 | .Obj => {}, | 272 | .Obj => {}, |
| 270 | .Lib => return error.TODOImplementWritingLibFiles, | 273 | .Lib => return error.TODOImplementWritingLibFiles, |
| ... | @@ -861,8 +864,8 @@ pub fn flush(self: *Elf, module: *Module) !void { | ... | @@ -861,8 +864,8 @@ pub fn flush(self: *Elf, module: *Module) !void { |
| 861 | }, | 864 | }, |
| 862 | } | 865 | } |
| 863 | // Write the form for the compile unit, which must match the abbrev table above. | 866 | // Write the form for the compile unit, which must match the abbrev table above. |
| 864 | const name_strp = try self.makeDebugString(self.base.options.root_pkg.root_src_path); | 867 | const name_strp = try self.makeDebugString(self.base.options.root_pkg.?.root_src_path); |
| 865 | const comp_dir_strp = try self.makeDebugString(self.base.options.root_pkg.root_src_dir_path); | 868 | const comp_dir_strp = try self.makeDebugString(self.base.options.root_pkg.?.root_src_dir_path); |
| 866 | const producer_strp = try self.makeDebugString(link.producer_string); | 869 | const producer_strp = try self.makeDebugString(link.producer_string); |
| 867 | // Currently only one compilation unit is supported, so the address range is simply | 870 | // Currently only one compilation unit is supported, so the address range is simply |
| 868 | // identical to the main program header virtual address and memory size. | 871 | // identical to the main program header virtual address and memory size. |
| ... | @@ -1031,7 +1034,7 @@ pub fn flush(self: *Elf, module: *Module) !void { | ... | @@ -1031,7 +1034,7 @@ pub fn flush(self: *Elf, module: *Module) !void { |
| 1031 | 0, // include_directories (none except the compilation unit cwd) | 1034 | 0, // include_directories (none except the compilation unit cwd) |
| 1032 | }); | 1035 | }); |
| 1033 | // file_names[0] | 1036 | // file_names[0] |
| 1034 | di_buf.appendSliceAssumeCapacity(self.base.options.root_pkg.root_src_path); // relative path name | 1037 | di_buf.appendSliceAssumeCapacity(self.base.options.root_pkg.?.root_src_path); // relative path name |
| 1035 | di_buf.appendSliceAssumeCapacity(&[_]u8{ | 1038 | di_buf.appendSliceAssumeCapacity(&[_]u8{ |
| 1036 | 0, // null byte for the relative path name | 1039 | 0, // null byte for the relative path name |
| 1037 | 0, // directory_index | 1040 | 0, // directory_index |
| ... | @@ -1195,7 +1198,7 @@ pub fn flush(self: *Elf, module: *Module) !void { | ... | @@ -1195,7 +1198,7 @@ pub fn flush(self: *Elf, module: *Module) !void { |
| 1195 | } | 1198 | } |
| 1196 | self.shdr_table_dirty = false; | 1199 | self.shdr_table_dirty = false; |
| 1197 | } | 1200 | } |
| 1198 | if (self.entry_addr == null and self.base.options.output_mode == .Exe) { | 1201 | if (self.entry_addr == null and self.base.options.effectiveOutputMode() == .Exe) { |
| 1199 | log.debug("flushing. no_entry_point_found = true\n", .{}); | 1202 | log.debug("flushing. no_entry_point_found = true\n", .{}); |
| 1200 | self.error_flags.no_entry_point_found = true; | 1203 | self.error_flags.no_entry_point_found = true; |
| 1201 | } else { | 1204 | } else { |
| ... | @@ -1255,7 +1258,7 @@ fn writeElfHeader(self: *Elf) !void { | ... | @@ -1255,7 +1258,7 @@ fn writeElfHeader(self: *Elf) !void { |
| 1255 | 1258 | ||
| 1256 | assert(index == 16); | 1259 | assert(index == 16); |
| 1257 | 1260 | ||
| 1258 | const elf_type = switch (self.base.options.output_mode) { | 1261 | const elf_type = switch (self.base.options.effectiveOutputMode()) { |
| 1259 | .Exe => elf.ET.EXEC, | 1262 | .Exe => elf.ET.EXEC, |
| 1260 | .Obj => elf.ET.REL, | 1263 | .Obj => elf.ET.REL, |
| 1261 | .Lib => switch (self.base.options.link_mode) { | 1264 | .Lib => switch (self.base.options.link_mode) { |
| ... | @@ -2430,8 +2433,8 @@ fn dbgLineNeededHeaderBytes(self: Elf) u32 { | ... | @@ -2430,8 +2433,8 @@ fn dbgLineNeededHeaderBytes(self: Elf) u32 { |
| 2430 | directory_count * 8 + file_name_count * 8 + | 2433 | directory_count * 8 + file_name_count * 8 + |
| 2431 | // These are encoded as DW.FORM_string rather than DW.FORM_strp as we would like | 2434 | // These are encoded as DW.FORM_string rather than DW.FORM_strp as we would like |
| 2432 | // because of a workaround for readelf and gdb failing to understand DWARFv5 correctly. | 2435 | // because of a workaround for readelf and gdb failing to understand DWARFv5 correctly. |
| 2433 | self.base.options.root_pkg.root_src_dir_path.len + | 2436 | self.base.options.root_pkg.?.root_src_dir_path.len + |
| 2434 | self.base.options.root_pkg.root_src_path.len); | 2437 | self.base.options.root_pkg.?.root_src_path.len); |
| 2435 | } | 2438 | } |
| 2436 | 2439 | ||
| 2437 | fn dbgInfoNeededHeaderBytes(self: Elf) u32 { | 2440 | fn dbgInfoNeededHeaderBytes(self: Elf) u32 { |
src-self-hosted/link/MachO.zig+3| ... | @@ -137,6 +137,9 @@ pub const SrcFn = struct { | ... | @@ -137,6 +137,9 @@ pub const SrcFn = struct { |
| 137 | pub fn openPath(allocator: *Allocator, dir: fs.Dir, sub_path: []const u8, options: link.Options) !*File { | 137 | pub fn openPath(allocator: *Allocator, dir: fs.Dir, sub_path: []const u8, options: link.Options) !*File { |
| 138 | assert(options.object_format == .macho); | 138 | assert(options.object_format == .macho); |
| 139 | 139 | ||
| 140 | if (options.use_llvm) return error.LLVM_BackendIsTODO_ForMachO; // TODO | ||
| 141 | if (options.use_lld) return error.LLD_LinkingIsTODO_ForMachO; // TODO | ||
| 142 | |||
| 140 | const file = try dir.createFile(sub_path, .{ .truncate = false, .read = true, .mode = link.determineMode(options) }); | 143 | const file = try dir.createFile(sub_path, .{ .truncate = false, .read = true, .mode = link.determineMode(options) }); |
| 141 | errdefer file.close(); | 144 | errdefer file.close(); |
| 142 | 145 |
src-self-hosted/link/Wasm.zig+3| ... | @@ -52,6 +52,9 @@ funcs: std.ArrayListUnmanaged(*Module.Decl) = .{}, | ... | @@ -52,6 +52,9 @@ funcs: std.ArrayListUnmanaged(*Module.Decl) = .{}, |
| 52 | pub fn openPath(allocator: *Allocator, dir: fs.Dir, sub_path: []const u8, options: link.Options) !*link.File { | 52 | pub fn openPath(allocator: *Allocator, dir: fs.Dir, sub_path: []const u8, options: link.Options) !*link.File { |
| 53 | assert(options.object_format == .wasm); | 53 | assert(options.object_format == .wasm); |
| 54 | 54 | ||
| 55 | if (options.use_llvm) return error.LLVM_BackendIsTODO_ForWasm; // TODO | ||
| 56 | if (options.use_lld) return error.LLD_LinkingIsTODO_ForWasm; // TODO | ||
| 57 | |||
| 55 | // TODO: read the file and keep vaild parts instead of truncating | 58 | // TODO: read the file and keep vaild parts instead of truncating |
| 56 | const file = try dir.createFile(sub_path, .{ .truncate = true, .read = true }); | 59 | const file = try dir.createFile(sub_path, .{ .truncate = true, .read = true }); |
| 57 | errdefer file.close(); | 60 | errdefer file.close(); |
src-self-hosted/main.zig+87-19| ... | @@ -13,6 +13,7 @@ const Package = @import("Package.zig"); | ... | @@ -13,6 +13,7 @@ const Package = @import("Package.zig"); |
| 13 | const zir = @import("zir.zig"); | 13 | const zir = @import("zir.zig"); |
| 14 | const build_options = @import("build_options"); | 14 | const build_options = @import("build_options"); |
| 15 | const warn = std.log.warn; | 15 | const warn = std.log.warn; |
| 16 | const introspect = @import("introspect.zig"); | ||
| 16 | 17 | ||
| 17 | fn fatal(comptime format: []const u8, args: anytype) noreturn { | 18 | fn fatal(comptime format: []const u8, args: anytype) noreturn { |
| 18 | std.log.emerg(format, args); | 19 | std.log.emerg(format, args); |
| ... | @@ -231,7 +232,6 @@ pub fn buildOutputType( | ... | @@ -231,7 +232,6 @@ pub fn buildOutputType( |
| 231 | var root_src_file: ?[]const u8 = null; | 232 | var root_src_file: ?[]const u8 = null; |
| 232 | var version: std.builtin.Version = .{ .major = 0, .minor = 0, .patch = 0 }; | 233 | var version: std.builtin.Version = .{ .major = 0, .minor = 0, .patch = 0 }; |
| 233 | var strip = false; | 234 | var strip = false; |
| 234 | var emit_h = true; | ||
| 235 | var watch = false; | 235 | var watch = false; |
| 236 | var debug_tokenize = false; | 236 | var debug_tokenize = false; |
| 237 | var debug_ast_tree = false; | 237 | var debug_ast_tree = false; |
| ... | @@ -248,6 +248,7 @@ pub fn buildOutputType( | ... | @@ -248,6 +248,7 @@ pub fn buildOutputType( |
| 248 | var target_dynamic_linker: ?[]const u8 = null; | 248 | var target_dynamic_linker: ?[]const u8 = null; |
| 249 | var target_ofmt: ?[]const u8 = null; | 249 | var target_ofmt: ?[]const u8 = null; |
| 250 | var output_mode: std.builtin.OutputMode = undefined; | 250 | var output_mode: std.builtin.OutputMode = undefined; |
| 251 | var emit_h: Emit = undefined; | ||
| 251 | var ensure_libc_on_non_freestanding = false; | 252 | var ensure_libc_on_non_freestanding = false; |
| 252 | var ensure_libcpp_on_non_freestanding = false; | 253 | var ensure_libcpp_on_non_freestanding = false; |
| 253 | var have_libc = false; | 254 | var have_libc = false; |
| ... | @@ -269,6 +270,9 @@ pub fn buildOutputType( | ... | @@ -269,6 +270,9 @@ pub fn buildOutputType( |
| 269 | var linker_z_nodelete = false; | 270 | var linker_z_nodelete = false; |
| 270 | var linker_z_defs = false; | 271 | var linker_z_defs = false; |
| 271 | var stack_size_override: u64 = 0; | 272 | var stack_size_override: u64 = 0; |
| 273 | var use_llvm: ?bool = null; | ||
| 274 | var use_lld: ?bool = null; | ||
| 275 | var use_clang: ?bool = null; | ||
| 272 | 276 | ||
| 273 | var system_libs = std.ArrayList([]const u8).init(gpa); | 277 | var system_libs = std.ArrayList([]const u8).init(gpa); |
| 274 | defer system_libs.deinit(); | 278 | defer system_libs.deinit(); |
| ... | @@ -296,6 +300,10 @@ pub fn buildOutputType( | ... | @@ -296,6 +300,10 @@ pub fn buildOutputType( |
| 296 | 300 | ||
| 297 | if (arg_mode == .build) { | 301 | if (arg_mode == .build) { |
| 298 | output_mode = arg_mode.build; | 302 | output_mode = arg_mode.build; |
| 303 | emit_h = switch (output_mode) { | ||
| 304 | .Exe => .no, | ||
| 305 | .Obj, .Lib => .yes_default_path, | ||
| 306 | }; | ||
| 299 | 307 | ||
| 300 | const args = all_args[2..]; | 308 | const args = all_args[2..]; |
| 301 | var i: usize = 0; | 309 | var i: usize = 0; |
| ... | @@ -416,6 +424,18 @@ pub fn buildOutputType( | ... | @@ -416,6 +424,18 @@ pub fn buildOutputType( |
| 416 | want_pic = true; | 424 | want_pic = true; |
| 417 | } else if (mem.eql(u8, arg, "-fno-PIC")) { | 425 | } else if (mem.eql(u8, arg, "-fno-PIC")) { |
| 418 | want_pic = false; | 426 | want_pic = false; |
| 427 | } else if (mem.eql(u8, arg, "-fLLVM")) { | ||
| 428 | use_llvm = true; | ||
| 429 | } else if (mem.eql(u8, arg, "-fno-LLVM")) { | ||
| 430 | use_llvm = false; | ||
| 431 | } else if (mem.eql(u8, arg, "-fLLD")) { | ||
| 432 | use_lld = true; | ||
| 433 | } else if (mem.eql(u8, arg, "-fno-LLD")) { | ||
| 434 | use_lld = false; | ||
| 435 | } else if (mem.eql(u8, arg, "-fClang")) { | ||
| 436 | use_clang = true; | ||
| 437 | } else if (mem.eql(u8, arg, "-fno-Clang")) { | ||
| 438 | use_clang = false; | ||
| 419 | } else if (mem.eql(u8, arg, "-rdynamic")) { | 439 | } else if (mem.eql(u8, arg, "-rdynamic")) { |
| 420 | rdynamic = true; | 440 | rdynamic = true; |
| 421 | } else if (mem.eql(u8, arg, "-femit-bin")) { | 441 | } else if (mem.eql(u8, arg, "-femit-bin")) { |
| ... | @@ -430,6 +450,12 @@ pub fn buildOutputType( | ... | @@ -430,6 +450,12 @@ pub fn buildOutputType( |
| 430 | emit_zir = .{ .yes = arg["-femit-zir=".len..] }; | 450 | emit_zir = .{ .yes = arg["-femit-zir=".len..] }; |
| 431 | } else if (mem.eql(u8, arg, "-fno-emit-zir")) { | 451 | } else if (mem.eql(u8, arg, "-fno-emit-zir")) { |
| 432 | emit_zir = .no; | 452 | emit_zir = .no; |
| 453 | } else if (mem.eql(u8, arg, "-femit-h")) { | ||
| 454 | emit_h = .yes_default_path; | ||
| 455 | } else if (mem.startsWith(u8, arg, "-femit-h=")) { | ||
| 456 | emit_h = .{ .yes = arg["-femit-h=".len..] }; | ||
| 457 | } else if (mem.eql(u8, arg, "-fno-emit-h")) { | ||
| 458 | emit_h = .no; | ||
| 433 | } else if (mem.eql(u8, arg, "-dynamic")) { | 459 | } else if (mem.eql(u8, arg, "-dynamic")) { |
| 434 | link_mode = .Dynamic; | 460 | link_mode = .Dynamic; |
| 435 | } else if (mem.eql(u8, arg, "-static")) { | 461 | } else if (mem.eql(u8, arg, "-static")) { |
| ... | @@ -491,7 +517,7 @@ pub fn buildOutputType( | ... | @@ -491,7 +517,7 @@ pub fn buildOutputType( |
| 491 | } | 517 | } |
| 492 | } | 518 | } |
| 493 | } else { | 519 | } else { |
| 494 | emit_h = false; | 520 | emit_h = .no; |
| 495 | strip = true; | 521 | strip = true; |
| 496 | ensure_libc_on_non_freestanding = true; | 522 | ensure_libc_on_non_freestanding = true; |
| 497 | ensure_libcpp_on_non_freestanding = arg_mode == .cpp; | 523 | ensure_libcpp_on_non_freestanding = arg_mode == .cpp; |
| ... | @@ -874,14 +900,6 @@ pub fn buildOutputType( | ... | @@ -874,14 +900,6 @@ pub fn buildOutputType( |
| 874 | } | 900 | } |
| 875 | } | 901 | } |
| 876 | 902 | ||
| 877 | if (system_libs.items.len != 0) { | ||
| 878 | fatal("linking against system libraries not yet supported", .{}); | ||
| 879 | } | ||
| 880 | |||
| 881 | const src_path = root_src_file orelse { | ||
| 882 | fatal("expected at least one file argument", .{}); | ||
| 883 | }; | ||
| 884 | |||
| 885 | const object_format: ?std.Target.ObjectFormat = blk: { | 903 | const object_format: ?std.Target.ObjectFormat = blk: { |
| 886 | const ofmt = target_ofmt orelse break :blk null; | 904 | const ofmt = target_ofmt orelse break :blk null; |
| 887 | if (mem.eql(u8, ofmt, "elf")) { | 905 | if (mem.eql(u8, ofmt, "elf")) { |
| ... | @@ -909,11 +927,14 @@ pub fn buildOutputType( | ... | @@ -909,11 +927,14 @@ pub fn buildOutputType( |
| 909 | .no => { | 927 | .no => { |
| 910 | fatal("-fno-emit-bin not supported yet", .{}); | 928 | fatal("-fno-emit-bin not supported yet", .{}); |
| 911 | }, | 929 | }, |
| 912 | .yes_default_path => if (object_format != null and object_format.? == .c) | 930 | .yes_default_path => try std.zig.binNameAlloc( |
| 913 | try std.fmt.allocPrint(arena, "{}.c", .{root_name}) | 931 | arena, |
| 914 | else | 932 | root_name, |
| 915 | try std.zig.binNameAlloc(arena, root_name, target_info.target, output_mode, link_mode), | 933 | target_info.target, |
| 916 | 934 | output_mode, | |
| 935 | link_mode, | ||
| 936 | object_format, | ||
| 937 | ), | ||
| 917 | .yes => |p| p, | 938 | .yes => |p| p, |
| 918 | }; | 939 | }; |
| 919 | 940 | ||
| ... | @@ -930,10 +951,25 @@ pub fn buildOutputType( | ... | @@ -930,10 +951,25 @@ pub fn buildOutputType( |
| 930 | .yes => |p| p, | 951 | .yes => |p| p, |
| 931 | }; | 952 | }; |
| 932 | 953 | ||
| 933 | const root_pkg = try Package.create(gpa, fs.cwd(), ".", src_path); | 954 | const root_pkg = if (root_src_file) |src_path| try Package.create(gpa, fs.cwd(), ".", src_path) else null; |
| 934 | defer root_pkg.destroy(); | 955 | defer if (root_pkg) |pkg| pkg.destroy(); |
| 935 | 956 | ||
| 936 | var module = try Module.init(gpa, .{ | 957 | const emit_h_path: ?[]const u8 = switch (emit_h) { |
| 958 | .yes => |p| p, | ||
| 959 | .no => null, | ||
| 960 | .yes_default_path => try std.fmt.allocPrint(arena, "{}.h", .{root_name}), | ||
| 961 | }; | ||
| 962 | |||
| 963 | // TODO Remove this, we'll have this error emitted lazily only if the features would end | ||
| 964 | // up actually getting used. | ||
| 965 | //if (!build_options.have_llvm) { | ||
| 966 | // if ((use_llvm orelse false) or (use_lld orelse false) or (use_clang orelse false)) | ||
| 967 | // fatal("-fLLVM, -fLLD, and -fClang unavailable: compiler not built with LLVM extensions enabled", .{}); | ||
| 968 | //} | ||
| 969 | |||
| 970 | const compiler_id = try introspect.resolveCompilerId(gpa); | ||
| 971 | |||
| 972 | var module = Module.init(gpa, .{ | ||
| 937 | .root_name = root_name, | 973 | .root_name = root_name, |
| 938 | .target = target_info.target, | 974 | .target = target_info.target, |
| 939 | .output_mode = output_mode, | 975 | .output_mode = output_mode, |
| ... | @@ -944,7 +980,39 @@ pub fn buildOutputType( | ... | @@ -944,7 +980,39 @@ pub fn buildOutputType( |
| 944 | .object_format = object_format, | 980 | .object_format = object_format, |
| 945 | .optimize_mode = build_mode, | 981 | .optimize_mode = build_mode, |
| 946 | .keep_source_files_loaded = zir_out_path != null, | 982 | .keep_source_files_loaded = zir_out_path != null, |
| 947 | }); | 983 | .clang_argv = clang_argv.items, |
| 984 | .lib_dirs = lib_dirs.items, | ||
| 985 | .rpath_list = rpath_list.items, | ||
| 986 | .c_source_files = c_source_files.items, | ||
| 987 | .link_objects = link_objects.items, | ||
| 988 | .framework_dirs = framework_dirs.items, | ||
| 989 | .frameworks = frameworks.items, | ||
| 990 | .system_libs = system_libs.items, | ||
| 991 | .emit_h = emit_h_path, | ||
| 992 | .have_libc = have_libc, | ||
| 993 | .have_libcpp = have_libcpp, | ||
| 994 | .want_pic = want_pic, | ||
| 995 | .want_sanitize_c = want_sanitize_c, | ||
| 996 | .use_llvm = use_llvm, | ||
| 997 | .use_lld = use_lld, | ||
| 998 | .use_clang = use_clang, | ||
| 999 | .rdynamic = rdynamic, | ||
| 1000 | .linker_script = linker_script, | ||
| 1001 | .version_script = version_script, | ||
| 1002 | .disable_c_depfile = disable_c_depfile, | ||
| 1003 | .override_soname = override_soname, | ||
| 1004 | .linker_optimization = linker_optimization, | ||
| 1005 | .linker_gc_sections = linker_gc_sections, | ||
| 1006 | .linker_allow_shlib_undefined = linker_allow_shlib_undefined, | ||
| 1007 | .linker_bind_global_refs_locally = linker_bind_global_refs_locally, | ||
| 1008 | .linker_z_nodelete = linker_z_nodelete, | ||
| 1009 | .linker_z_defs = linker_z_defs, | ||
| 1010 | .stack_size_override = stack_size_override, | ||
| 1011 | .compiler_id = compiler_id, | ||
| 1012 | .strip = strip, | ||
| 1013 | }) catch |err| { | ||
| 1014 | fatal("unable to initialize module: {}", .{@errorName(err)}); | ||
| 1015 | }; | ||
| 948 | defer module.deinit(); | 1016 | defer module.deinit(); |
| 949 | 1017 | ||
| 950 | const stdin = std.io.getStdIn().inStream(); | 1018 | const stdin = std.io.getStdIn().inStream(); |
src-self-hosted/test.zig+21-16| ... | @@ -9,6 +9,7 @@ const enable_qemu: bool = build_options.enable_qemu; | ... | @@ -9,6 +9,7 @@ const enable_qemu: bool = build_options.enable_qemu; |
| 9 | const enable_wine: bool = build_options.enable_wine; | 9 | const enable_wine: bool = build_options.enable_wine; |
| 10 | const enable_wasmtime: bool = build_options.enable_wasmtime; | 10 | const enable_wasmtime: bool = build_options.enable_wasmtime; |
| 11 | const glibc_multi_install_dir: ?[]const u8 = build_options.glibc_multi_install_dir; | 11 | const glibc_multi_install_dir: ?[]const u8 = build_options.glibc_multi_install_dir; |
| 12 | const introspect = @import("introspect.zig"); | ||
| 12 | 13 | ||
| 13 | const cheader = @embedFile("link/cbe.h"); | 14 | const cheader = @embedFile("link/cbe.h"); |
| 14 | 15 | ||
| ... | @@ -435,7 +436,10 @@ pub const TestContext = struct { | ... | @@ -435,7 +436,10 @@ pub const TestContext = struct { |
| 435 | const root_pkg = try Package.create(allocator, tmp.dir, ".", tmp_src_path); | 436 | const root_pkg = try Package.create(allocator, tmp.dir, ".", tmp_src_path); |
| 436 | defer root_pkg.destroy(); | 437 | defer root_pkg.destroy(); |
| 437 | 438 | ||
| 438 | const bin_name = try std.zig.binNameAlloc(arena, "test_case", target, case.output_mode, null); | 439 | const ofmt: ?std.builtin.ObjectFormat = if (case.cbe) .c else null; |
| 440 | const bin_name = try std.zig.binNameAlloc(arena, "test_case", target, case.output_mode, null, ofmt); | ||
| 441 | |||
| 442 | const compiler_id = try introspect.resolveCompilerId(arena); | ||
| 439 | 443 | ||
| 440 | var module = try Module.init(allocator, .{ | 444 | var module = try Module.init(allocator, .{ |
| 441 | .root_name = "test_case", | 445 | .root_name = "test_case", |
| ... | @@ -450,7 +454,8 @@ pub const TestContext = struct { | ... | @@ -450,7 +454,8 @@ pub const TestContext = struct { |
| 450 | .bin_file_path = bin_name, | 454 | .bin_file_path = bin_name, |
| 451 | .root_pkg = root_pkg, | 455 | .root_pkg = root_pkg, |
| 452 | .keep_source_files_loaded = true, | 456 | .keep_source_files_loaded = true, |
| 453 | .object_format = if (case.cbe) .c else null, | 457 | .object_format = ofmt, |
| 458 | .compiler_id = compiler_id, | ||
| 454 | }); | 459 | }); |
| 455 | defer module.deinit(); | 460 | defer module.deinit(); |
| 456 | 461 | ||
| ... | @@ -693,23 +698,23 @@ pub const TestContext = struct { | ... | @@ -693,23 +698,23 @@ pub const TestContext = struct { |
| 693 | } | 698 | } |
| 694 | 699 | ||
| 695 | var interpreter = spu.Interpreter(struct { | 700 | var interpreter = spu.Interpreter(struct { |
| 696 | RAM: [0x10000]u8 = undefined, | 701 | RAM: [0x10000]u8 = undefined, |
| 697 | 702 | ||
| 698 | pub fn read8(bus: @This(), addr: u16) u8 { | 703 | pub fn read8(bus: @This(), addr: u16) u8 { |
| 699 | return bus.RAM[addr]; | 704 | return bus.RAM[addr]; |
| 700 | } | 705 | } |
| 701 | pub fn read16(bus: @This(), addr: u16) u16 { | 706 | pub fn read16(bus: @This(), addr: u16) u16 { |
| 702 | return std.mem.readIntLittle(u16, bus.RAM[addr..][0..2]); | 707 | return std.mem.readIntLittle(u16, bus.RAM[addr..][0..2]); |
| 703 | } | 708 | } |
| 704 | 709 | ||
| 705 | pub fn write8(bus: *@This(), addr: u16, val: u8) void { | 710 | pub fn write8(bus: *@This(), addr: u16, val: u8) void { |
| 706 | bus.RAM[addr] = val; | 711 | bus.RAM[addr] = val; |
| 707 | } | 712 | } |
| 708 | 713 | ||
| 709 | pub fn write16(bus: *@This(), addr: u16, val: u16) void { | 714 | pub fn write16(bus: *@This(), addr: u16, val: u16) void { |
| 710 | std.mem.writeIntLittle(u16, bus.RAM[addr..][0..2], val); | 715 | std.mem.writeIntLittle(u16, bus.RAM[addr..][0..2], val); |
| 711 | } | 716 | } |
| 712 | }){ | 717 | }){ |
| 713 | .bus = .{}, | 718 | .bus = .{}, |
| 714 | }; | 719 | }; |
| 715 | 720 |