| 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 | 139 | const is_wasmtime_enabled = b.option(bool, "enable-wasmtime", "Use Wasmtime to enable and run WASI libstd tests") orelse false; |
| 140 | 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 | 143 | test_stage2.addBuildOption(bool, "enable_qemu", is_qemu_enabled); |
| 143 | 144 | test_stage2.addBuildOption(bool, "enable_wine", is_wine_enabled); |
| 144 | 145 | test_stage2.addBuildOption(bool, "enable_wasmtime", is_wasmtime_enabled); |
lib/std/cache_hash.zig+73-35| ... | ... | @@ -5,7 +5,6 @@ |
| 5 | 5 | // and substantial portions of the software. |
| 6 | 6 | const std = @import("std.zig"); |
| 7 | 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 | 8 | const fs = std.fs; |
| 10 | 9 | const base64 = std.base64; |
| 11 | 10 | const ArrayList = std.ArrayList; |
| ... | ... | @@ -23,6 +22,14 @@ const BASE64_DIGEST_LEN = base64.Base64Encoder.calcSize(BIN_DIGEST_LEN); |
| 23 | 22 | |
| 24 | 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 | 33 | pub const File = struct { |
| 27 | 34 | path: ?[]const u8, |
| 28 | 35 | max_file_size: ?usize, |
| ... | ... | @@ -45,52 +52,82 @@ pub const File = struct { |
| 45 | 52 | |
| 46 | 53 | /// CacheHash manages project-local `zig-cache` directories. |
| 47 | 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 | 56 | pub const CacheHash = struct { |
| 50 | 57 | allocator: *Allocator, |
| 51 | hasher_init: Hasher, // initial state, that can be copied | |
| 52 | hasher: Hasher, // current state for incremental hashing | |
| 58 | /// Current state for incremental hashing. | |
| 59 | hasher: Hasher, | |
| 53 | 60 | manifest_dir: fs.Dir, |
| 54 | 61 | manifest_file: ?fs.File, |
| 55 | 62 | manifest_dirty: bool, |
| 63 | owns_manifest_dir: bool, | |
| 56 | 64 | files: ArrayList(File), |
| 57 | 65 | b64_digest: [BASE64_DIGEST_LEN]u8, |
| 58 | 66 | |
| 59 | 67 | /// Be sure to call release after successful initialization. |
| 60 | 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 | 69 | return CacheHash{ |
| 63 | 70 | .allocator = allocator, |
| 64 | .hasher_init = hasher_init, | |
| 65 | 71 | .hasher = hasher_init, |
| 66 | 72 | .manifest_dir = try dir.makeOpenPath(manifest_dir_path, .{}), |
| 67 | 73 | .manifest_file = null, |
| 68 | 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 | 93 | .files = ArrayList(File).init(allocator), |
| 70 | 94 | .b64_digest = undefined, |
| 71 | 95 | }; |
| 72 | 96 | } |
| 73 | 97 | |
| 74 | 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 | 100 | assert(self.manifest_file == null); |
| 77 | 101 | |
| 78 | self.hasher.update(val); | |
| 79 | self.hasher.update(&[_]u8{0}); | |
| 102 | self.hasher.update(mem.asBytes(&bytes.len)); | |
| 103 | self.hasher.update(bytes); | |
| 80 | 104 | } |
| 81 | 105 | |
| 82 | /// Convert the input value into bytes and record it as a dependency of the | |
| 83 | /// process being cached | |
| 84 | pub fn add(self: *CacheHash, val: anytype) void { | |
| 106 | pub fn addListOfBytes(self: *CacheHash, list_of_bytes: []const []const u8) void { | |
| 85 | 107 | assert(self.manifest_file == null); |
| 86 | 108 | |
| 87 | const valPtr = switch (@typeInfo(@TypeOf(val))) { | |
| 88 | .Int => &val, | |
| 89 | .Pointer => val, | |
| 90 | else => &val, | |
| 91 | }; | |
| 109 | self.add(list_of_bytes.items.len); | |
| 110 | for (list_of_bytes) |bytes| self.addBytes(bytes); | |
| 111 | } | |
| 112 | ||
| 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 | 133 | /// Add a file as a dependency of process being cached. When `CacheHash.hit` is |
| ... | ... | @@ -122,7 +159,7 @@ pub const CacheHash = struct { |
| 122 | 159 | .bin_digest = undefined, |
| 123 | 160 | }; |
| 124 | 161 | |
| 125 | self.addSlice(resolved_path); | |
| 162 | self.addBytes(resolved_path); | |
| 126 | 163 | |
| 127 | 164 | return idx; |
| 128 | 165 | } |
| ... | ... | @@ -143,7 +180,7 @@ pub const CacheHash = struct { |
| 143 | 180 | |
| 144 | 181 | base64_encoder.encode(self.b64_digest[0..], &bin_digest); |
| 145 | 182 | |
| 146 | self.hasher = self.hasher_init; | |
| 183 | self.hasher = hasher_init; | |
| 147 | 184 | self.hasher.update(&bin_digest); |
| 148 | 185 | |
| 149 | 186 | const manifest_file_path = try fmt.allocPrint(self.allocator, "{}.txt", .{self.b64_digest}); |
| ... | ... | @@ -244,7 +281,7 @@ pub const CacheHash = struct { |
| 244 | 281 | } |
| 245 | 282 | |
| 246 | 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 | 286 | if (!mem.eql(u8, &cache_hash_file.bin_digest, &actual_digest)) { |
| 250 | 287 | cache_hash_file.bin_digest = actual_digest; |
| ... | ... | @@ -262,7 +299,7 @@ pub const CacheHash = struct { |
| 262 | 299 | // cache miss |
| 263 | 300 | // keep the manifest file open |
| 264 | 301 | // reset the hash |
| 265 | self.hasher = self.hasher_init; | |
| 302 | self.hasher = hasher_init; | |
| 266 | 303 | self.hasher.update(&bin_digest); |
| 267 | 304 | |
| 268 | 305 | // Remove files not in the initial hash |
| ... | ... | @@ -310,7 +347,7 @@ pub const CacheHash = struct { |
| 310 | 347 | |
| 311 | 348 | // Hash while reading from disk, to keep the contents in the cpu cache while |
| 312 | 349 | // doing hashing. |
| 313 | var hasher = self.hasher_init; | |
| 350 | var hasher = hasher_init; | |
| 314 | 351 | var off: usize = 0; |
| 315 | 352 | while (true) { |
| 316 | 353 | // give me everything you've got, captain |
| ... | ... | @@ -323,7 +360,7 @@ pub const CacheHash = struct { |
| 323 | 360 | |
| 324 | 361 | ch_file.contents = contents; |
| 325 | 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 | 366 | self.hasher.update(&ch_file.bin_digest); |
| ... | ... | @@ -435,11 +472,12 @@ pub const CacheHash = struct { |
| 435 | 472 | file.deinit(self.allocator); |
| 436 | 473 | } |
| 437 | 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 | 481 | var buf: [1024]u8 = undefined; |
| 444 | 482 | |
| 445 | 483 | var hasher = hasher_init; |
| ... | ... | @@ -509,7 +547,7 @@ test "cache file and then recall it" { |
| 509 | 547 | |
| 510 | 548 | ch.add(true); |
| 511 | 549 | ch.add(@as(u16, 1234)); |
| 512 | ch.add("1234"); | |
| 550 | ch.addBytes("1234"); | |
| 513 | 551 | _ = try ch.addFile(temp_file, null); |
| 514 | 552 | |
| 515 | 553 | // There should be nothing in the cache |
| ... | ... | @@ -523,7 +561,7 @@ test "cache file and then recall it" { |
| 523 | 561 | |
| 524 | 562 | ch.add(true); |
| 525 | 563 | ch.add(@as(u16, 1234)); |
| 526 | ch.add("1234"); | |
| 564 | ch.addBytes("1234"); | |
| 527 | 565 | _ = try ch.addFile(temp_file, null); |
| 528 | 566 | |
| 529 | 567 | // Cache hit! We just "built" the same file |
| ... | ... | @@ -577,7 +615,7 @@ test "check that changing a file makes cache fail" { |
| 577 | 615 | var ch = try CacheHash.init(testing.allocator, cwd, temp_manifest_dir); |
| 578 | 616 | defer ch.release(); |
| 579 | 617 | |
| 580 | ch.add("1234"); | |
| 618 | ch.addBytes("1234"); | |
| 581 | 619 | const temp_file_idx = try ch.addFile(temp_file, 100); |
| 582 | 620 | |
| 583 | 621 | // There should be nothing in the cache |
| ... | ... | @@ -594,7 +632,7 @@ test "check that changing a file makes cache fail" { |
| 594 | 632 | var ch = try CacheHash.init(testing.allocator, cwd, temp_manifest_dir); |
| 595 | 633 | defer ch.release(); |
| 596 | 634 | |
| 597 | ch.add("1234"); | |
| 635 | ch.addBytes("1234"); | |
| 598 | 636 | const temp_file_idx = try ch.addFile(temp_file, 100); |
| 599 | 637 | |
| 600 | 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 | 666 | var ch = try CacheHash.init(testing.allocator, cwd, temp_manifest_dir); |
| 629 | 667 | defer ch.release(); |
| 630 | 668 | |
| 631 | ch.add("1234"); | |
| 669 | ch.addBytes("1234"); | |
| 632 | 670 | |
| 633 | 671 | // There should be nothing in the cache |
| 634 | 672 | testing.expectEqual(@as(?[BASE64_DIGEST_LEN]u8, null), try ch.hit()); |
| ... | ... | @@ -639,7 +677,7 @@ test "no file inputs" { |
| 639 | 677 | var ch = try CacheHash.init(testing.allocator, cwd, temp_manifest_dir); |
| 640 | 678 | defer ch.release(); |
| 641 | 679 | |
| 642 | ch.add("1234"); | |
| 680 | ch.addBytes("1234"); | |
| 643 | 681 | |
| 644 | 682 | digest2 = (try ch.hit()).?; |
| 645 | 683 | } |
| ... | ... | @@ -674,7 +712,7 @@ test "CacheHashes with files added after initial hash work" { |
| 674 | 712 | var ch = try CacheHash.init(testing.allocator, cwd, temp_manifest_dir); |
| 675 | 713 | defer ch.release(); |
| 676 | 714 | |
| 677 | ch.add("1234"); | |
| 715 | ch.addBytes("1234"); | |
| 678 | 716 | _ = try ch.addFile(temp_file1, null); |
| 679 | 717 | |
| 680 | 718 | // There should be nothing in the cache |
| ... | ... | @@ -688,7 +726,7 @@ test "CacheHashes with files added after initial hash work" { |
| 688 | 726 | var ch = try CacheHash.init(testing.allocator, cwd, temp_manifest_dir); |
| 689 | 727 | defer ch.release(); |
| 690 | 728 | |
| 691 | ch.add("1234"); | |
| 729 | ch.addBytes("1234"); | |
| 692 | 730 | _ = try ch.addFile(temp_file1, null); |
| 693 | 731 | |
| 694 | 732 | digest2 = (try ch.hit()).?; |
| ... | ... | @@ -707,7 +745,7 @@ test "CacheHashes with files added after initial hash work" { |
| 707 | 745 | var ch = try CacheHash.init(testing.allocator, cwd, temp_manifest_dir); |
| 708 | 746 | defer ch.release(); |
| 709 | 747 | |
| 710 | ch.add("1234"); | |
| 748 | ch.addBytes("1234"); | |
| 711 | 749 | _ = try ch.addFile(temp_file1, null); |
| 712 | 750 | |
| 713 | 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 | 465 | }; |
| 466 | 466 | |
| 467 | 467 | pub const ObjectFormat = enum { |
| 468 | /// TODO Get rid of this one. | |
| 469 | unknown, | |
| 470 | 468 | coff, |
| 471 | 469 | pe, |
| 472 | 470 | elf, |
lib/std/zig.zig+44-9| ... | ... | @@ -71,17 +71,52 @@ pub fn binNameAlloc( |
| 71 | 71 | target: std.Target, |
| 72 | 72 | output_mode: std.builtin.OutputMode, |
| 73 | 73 | link_mode: ?std.builtin.LinkMode, |
| 74 | object_format: ?std.Target.ObjectFormat, | |
| 74 | 75 | ) error{OutOfMemory}![]u8 { |
| 75 | switch (output_mode) { | |
| 76 | .Exe => return std.fmt.allocPrint(allocator, "{}{}", .{ root_name, target.exeFileExt() }), | |
| 77 | .Lib => { | |
| 78 | const suffix = switch (link_mode orelse .Static) { | |
| 79 | .Static => target.staticLibSuffix(), | |
| 80 | .Dynamic => target.dynamicLibSuffix(), | |
| 81 | }; | |
| 82 | return std.fmt.allocPrint(allocator, "{}{}{}", .{ target.libPrefix(), root_name, suffix }); | |
| 76 | switch (object_format orelse target.getObjectFormat()) { | |
| 77 | .coff, .pe => switch (output_mode) { | |
| 78 | .Exe => { | |
| 79 | const suffix = switch (target.os.tag) { | |
| 80 | .uefi => ".efi", | |
| 81 | else => ".exe", | |
| 82 | }; | |
| 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 | 22 | const liveness = @import("liveness.zig"); |
| 23 | 23 | const astgen = @import("astgen.zig"); |
| 24 | 24 | const zir_sema = @import("zir_sema.zig"); |
| 25 | const build_options = @import("build_options"); | |
| 25 | 26 | |
| 26 | 27 | /// General-purpose allocator. Used for both temporary and long-term storage. |
| 27 | 28 | gpa: *Allocator, |
| 28 | /// Pointer to externally managed resource. | |
| 29 | root_pkg: *Package, | |
| 29 | /// Pointer to externally managed resource. `null` if there is no zig file being compiled. | |
| 30 | root_pkg: ?*Package, | |
| 30 | 31 | /// Module owns this resource. |
| 31 | 32 | /// The `Scope` is either a `Scope.ZIRModule` or `Scope.File`. |
| 32 | 33 | root_scope: *Scope, |
| ... | ... | @@ -48,22 +49,26 @@ export_owners: std.AutoArrayHashMapUnmanaged(*Decl, []*Export) = .{}, |
| 48 | 49 | /// Maps fully qualified namespaced names to the Decl struct for them. |
| 49 | 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 | 54 | link_error_flags: link.File.ErrorFlags = .{}, |
| 52 | 55 | |
| 53 | 56 | work_queue: std.fifo.LinearFifo(WorkItem, .Dynamic), |
| 54 | 57 | |
| 55 | 58 | /// We optimize memory usage for a compilation with no compile errors by storing the |
| 56 | 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 | 61 | /// Note that a Decl can succeed but the Fn it represents can fail. In this case, |
| 59 | 62 | /// a Decl can have a failed_decls entry but have analysis status of success. |
| 60 | 63 | failed_decls: std.AutoArrayHashMapUnmanaged(*Decl, *ErrorMsg) = .{}, |
| 61 | 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 | 66 | failed_files: std.AutoArrayHashMapUnmanaged(*Scope, *ErrorMsg) = .{}, |
| 64 | 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 | 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 | 73 | /// Incrementing integer used to compare against the corresponding Decl |
| 69 | 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 | 84 | /// Owned by Module. |
| 80 | 85 | root_name: []u8, |
| 81 | 86 | keep_source_files_loaded: bool, |
| 87 | use_clang: bool, | |
| 82 | 88 | |
| 83 | 89 | /// Error tags and their values, tag names are duped with mod.gpa. |
| 84 | 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 | 96 | pub const InnerError = error{ OutOfMemory, AnalysisFail }; |
| 87 | 97 | |
| 88 | 98 | const WorkItem = union(enum) { |
| ... | ... | @@ -95,6 +105,9 @@ const WorkItem = union(enum) { |
| 95 | 105 | /// The source file containing the Decl has been updated, and so the |
| 96 | 106 | /// Decl may need its line number information updated in the debug info. |
| 97 | 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 | 113 | pub const Export = struct { |
| ... | ... | @@ -230,6 +243,7 @@ pub const Decl = struct { |
| 230 | 243 | const src_decl = module.decls[self.src_index]; |
| 231 | 244 | return src_decl.inst.src; |
| 232 | 245 | }, |
| 246 | .none => unreachable, | |
| 233 | 247 | .file, .block => unreachable, |
| 234 | 248 | .gen_zir => unreachable, |
| 235 | 249 | .local_val => unreachable, |
| ... | ... | @@ -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 | 323 | /// Fn struct memory is owned by the Decl's TypedValue.Managed arena allocator. |
| 286 | 324 | pub const Fn = struct { |
| 287 | 325 | /// This memory owned by the Decl's TypedValue.Managed arena allocator. |
| ... | ... | @@ -361,6 +399,7 @@ pub const Scope = struct { |
| 361 | 399 | .zir_module => return &self.cast(ZIRModule).?.contents.module.arena.allocator, |
| 362 | 400 | .file => unreachable, |
| 363 | 401 | .container => unreachable, |
| 402 | .none => unreachable, | |
| 364 | 403 | } |
| 365 | 404 | } |
| 366 | 405 | |
| ... | ... | @@ -376,6 +415,7 @@ pub const Scope = struct { |
| 376 | 415 | .zir_module => null, |
| 377 | 416 | .file => null, |
| 378 | 417 | .container => null, |
| 418 | .none => unreachable, | |
| 379 | 419 | }; |
| 380 | 420 | } |
| 381 | 421 | |
| ... | ... | @@ -390,6 +430,7 @@ pub const Scope = struct { |
| 390 | 430 | .decl => return self.cast(DeclAnalysis).?.decl.scope, |
| 391 | 431 | .file => return &self.cast(File).?.root_container.base, |
| 392 | 432 | .zir_module, .container => return self, |
| 433 | .none => unreachable, | |
| 393 | 434 | } |
| 394 | 435 | } |
| 395 | 436 | |
| ... | ... | @@ -406,6 +447,7 @@ pub const Scope = struct { |
| 406 | 447 | .file => unreachable, |
| 407 | 448 | .zir_module => return self.cast(ZIRModule).?.fullyQualifiedNameHash(name), |
| 408 | 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 | 456 | switch (self.tag) { |
| 415 | 457 | .file => return self.cast(File).?.contents.tree, |
| 416 | 458 | .zir_module => unreachable, |
| 459 | .none => unreachable, | |
| 417 | 460 | .decl => return self.cast(DeclAnalysis).?.decl.scope.cast(Container).?.file_scope.contents.tree, |
| 418 | 461 | .block => return self.cast(Block).?.decl.scope.cast(Container).?.file_scope.contents.tree, |
| 419 | 462 | .gen_zir => return self.cast(GenZIR).?.decl.scope.cast(Container).?.file_scope.contents.tree, |
| ... | ... | @@ -434,6 +477,7 @@ pub const Scope = struct { |
| 434 | 477 | .zir_module => unreachable, |
| 435 | 478 | .file => unreachable, |
| 436 | 479 | .container => unreachable, |
| 480 | .none => unreachable, | |
| 437 | 481 | }; |
| 438 | 482 | } |
| 439 | 483 | |
| ... | ... | @@ -444,6 +488,7 @@ pub const Scope = struct { |
| 444 | 488 | .container => return @fieldParentPtr(Container, "base", base).file_scope.sub_file_path, |
| 445 | 489 | .file => return @fieldParentPtr(File, "base", base).sub_file_path, |
| 446 | 490 | .zir_module => return @fieldParentPtr(ZIRModule, "base", base).sub_file_path, |
| 491 | .none => unreachable, | |
| 447 | 492 | .block => unreachable, |
| 448 | 493 | .gen_zir => unreachable, |
| 449 | 494 | .local_val => unreachable, |
| ... | ... | @@ -456,6 +501,7 @@ pub const Scope = struct { |
| 456 | 501 | switch (base.tag) { |
| 457 | 502 | .file => return @fieldParentPtr(File, "base", base).unload(gpa), |
| 458 | 503 | .zir_module => return @fieldParentPtr(ZIRModule, "base", base).unload(gpa), |
| 504 | .none => {}, | |
| 459 | 505 | .block => unreachable, |
| 460 | 506 | .gen_zir => unreachable, |
| 461 | 507 | .local_val => unreachable, |
| ... | ... | @@ -470,6 +516,7 @@ pub const Scope = struct { |
| 470 | 516 | .container => return @fieldParentPtr(Container, "base", base).file_scope.getSource(module), |
| 471 | 517 | .file => return @fieldParentPtr(File, "base", base).getSource(module), |
| 472 | 518 | .zir_module => return @fieldParentPtr(ZIRModule, "base", base).getSource(module), |
| 519 | .none => unreachable, | |
| 473 | 520 | .gen_zir => unreachable, |
| 474 | 521 | .local_val => unreachable, |
| 475 | 522 | .local_ptr => unreachable, |
| ... | ... | @@ -483,6 +530,7 @@ pub const Scope = struct { |
| 483 | 530 | switch (base.tag) { |
| 484 | 531 | .container => return @fieldParentPtr(Container, "base", base).removeDecl(child), |
| 485 | 532 | .zir_module => return @fieldParentPtr(ZIRModule, "base", base).removeDecl(child), |
| 533 | .none => unreachable, | |
| 486 | 534 | .file => unreachable, |
| 487 | 535 | .block => unreachable, |
| 488 | 536 | .gen_zir => unreachable, |
| ... | ... | @@ -505,6 +553,10 @@ pub const Scope = struct { |
| 505 | 553 | scope_zir_module.deinit(gpa); |
| 506 | 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 | 560 | .block => unreachable, |
| 509 | 561 | .gen_zir => unreachable, |
| 510 | 562 | .local_val => unreachable, |
| ... | ... | @@ -527,6 +579,8 @@ pub const Scope = struct { |
| 527 | 579 | zir_module, |
| 528 | 580 | /// .zig source code. |
| 529 | 581 | file, |
| 582 | /// There is no .zig or .zir source code being compiled in this Module. | |
| 583 | none, | |
| 530 | 584 | /// struct, enum or union, every .file contains one of these. |
| 531 | 585 | container, |
| 532 | 586 | block, |
| ... | ... | @@ -622,7 +676,7 @@ pub const Scope = struct { |
| 622 | 676 | pub fn getSource(self: *File, module: *Module) ![:0]const u8 { |
| 623 | 677 | switch (self.source) { |
| 624 | 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 | 680 | module.gpa, |
| 627 | 681 | self.sub_file_path, |
| 628 | 682 | std.math.maxInt(u32), |
| ... | ... | @@ -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 | 701 | pub const ZIRModule = struct { |
| 642 | 702 | pub const base_tag: Tag = .zir_module; |
| 643 | 703 | base: Scope = Scope{ .tag = base_tag }, |
| ... | ... | @@ -720,7 +780,7 @@ pub const Scope = struct { |
| 720 | 780 | pub fn getSource(self: *ZIRModule, module: *Module) ![:0]const u8 { |
| 721 | 781 | switch (self.source) { |
| 722 | 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 | 784 | module.gpa, |
| 725 | 785 | self.sub_file_path, |
| 726 | 786 | std.math.maxInt(u32), |
| ... | ... | @@ -855,20 +915,81 @@ pub const AllErrors = struct { |
| 855 | 915 | pub const InitOptions = struct { |
| 856 | 916 | target: std.Target, |
| 857 | 917 | root_name: []const u8, |
| 858 | root_pkg: *Package, | |
| 918 | root_pkg: ?*Package, | |
| 859 | 919 | output_mode: std.builtin.OutputMode, |
| 860 | 920 | bin_file_dir: ?std.fs.Dir = null, |
| 861 | 921 | bin_file_path: []const u8, |
| 922 | emit_h: ?[]const u8 = null, | |
| 862 | 923 | link_mode: ?std.builtin.LinkMode = null, |
| 863 | 924 | object_format: ?std.builtin.ObjectFormat = null, |
| 864 | 925 | optimize_mode: std.builtin.Mode = .Debug, |
| 865 | 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 | 958 | pub fn init(gpa: *Allocator, options: InitOptions) !Module { |
| 869 | 959 | const root_name = try gpa.dupe(u8, options.root_name); |
| 870 | 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 | 993 | const bin_file_dir = options.bin_file_dir orelse std.fs.cwd(); |
| 873 | 994 | const bin_file = try link.File.openPath(gpa, bin_file_dir, options.bin_file_path, .{ |
| 874 | 995 | .root_name = root_name, |
| ... | ... | @@ -876,39 +997,130 @@ pub fn init(gpa: *Allocator, options: InitOptions) !Module { |
| 876 | 997 | .target = options.target, |
| 877 | 998 | .output_mode = options.output_mode, |
| 878 | 999 | .link_mode = options.link_mode orelse .Static, |
| 879 | .object_format = options.object_format orelse options.target.getObjectFormat(), | |
| 1000 | .object_format = ofmt, | |
| 880 | 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 | 1012 | errdefer bin_file.destroy(); |
| 883 | 1013 | |
| 884 | 1014 | const root_scope = blk: { |
| 885 | if (mem.endsWith(u8, options.root_pkg.root_src_path, ".zig")) { | |
| 886 | const root_scope = try gpa.create(Scope.File); | |
| 887 | root_scope.* = .{ | |
| 888 | .sub_file_path = options.root_pkg.root_src_path, | |
| 889 | .source = .{ .unloaded = {} }, | |
| 890 | .contents = .{ .not_available = {} }, | |
| 891 | .status = .never_loaded, | |
| 892 | .root_container = .{ | |
| 893 | .file_scope = root_scope, | |
| 1015 | if (options.root_pkg) |root_pkg| { | |
| 1016 | if (mem.endsWith(u8, root_pkg.root_src_path, ".zig")) { | |
| 1017 | const root_scope = try gpa.create(Scope.File); | |
| 1018 | root_scope.* = .{ | |
| 1019 | .sub_file_path = root_pkg.root_src_path, | |
| 1020 | .source = .{ .unloaded = {} }, | |
| 1021 | .contents = .{ .not_available = {} }, | |
| 1022 | .status = .never_loaded, | |
| 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 | 1036 | .decls = .{}, |
| 895 | }, | |
| 896 | }; | |
| 897 | break :blk &root_scope.base; | |
| 898 | } else if (mem.endsWith(u8, options.root_pkg.root_src_path, ".zir")) { | |
| 899 | const root_scope = try gpa.create(Scope.ZIRModule); | |
| 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; | |
| 1037 | }; | |
| 1038 | break :blk &root_scope.base; | |
| 1039 | } else { | |
| 1040 | unreachable; | |
| 1041 | } | |
| 908 | 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 | 1125 | return Module{ |
| 914 | 1126 | .gpa = gpa, |
| ... | ... | @@ -920,6 +1132,11 @@ pub fn init(gpa: *Allocator, options: InitOptions) !Module { |
| 920 | 1132 | .bin_file = bin_file, |
| 921 | 1133 | .work_queue = std.fifo.LinearFifo(WorkItem, .Dynamic).init(gpa), |
| 922 | 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 | 1152 | } |
| 936 | 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 | 1160 | for (self.failed_decls.items()) |entry| { |
| 939 | 1161 | entry.value.destroy(gpa); |
| 940 | 1162 | } |
| 941 | 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 | 1170 | for (self.failed_files.items()) |entry| { |
| 944 | 1171 | entry.value.destroy(gpa); |
| 945 | 1172 | } |
| ... | ... | @@ -969,6 +1196,7 @@ pub fn deinit(self: *Module) void { |
| 969 | 1196 | gpa.free(entry.key); |
| 970 | 1197 | } |
| 971 | 1198 | self.global_error_set.deinit(gpa); |
| 1199 | self.cache.release(); | |
| 972 | 1200 | self.* = undefined; |
| 973 | 1201 | } |
| 974 | 1202 | |
| ... | ... | @@ -995,7 +1223,15 @@ pub fn update(self: *Module) !void { |
| 995 | 1223 | |
| 996 | 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 | 1235 | // Until then we simulate a full cache miss. Source files could have been loaded for any reason; |
| 1000 | 1236 | // to force a refresh we unload now. |
| 1001 | 1237 | if (self.root_scope.cast(Scope.File)) |zig_file| { |
| ... | ... | @@ -1053,6 +1289,7 @@ pub fn makeBinFileWritable(self: *Module) !void { |
| 1053 | 1289 | |
| 1054 | 1290 | pub fn totalErrorCount(self: *Module) usize { |
| 1055 | 1291 | const total = self.failed_decls.items().len + |
| 1292 | self.failed_c_objects.items().len + | |
| 1056 | 1293 | self.failed_files.items().len + |
| 1057 | 1294 | self.failed_exports.items().len; |
| 1058 | 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 | 1302 | var errors = std.ArrayList(AllErrors.Message).init(self.gpa); |
| 1066 | 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 | 1311 | for (self.failed_files.items()) |entry| { |
| 1069 | 1312 | const scope = entry.key; |
| 1070 | 1313 | const err_msg = entry.value; |
| ... | ... | @@ -1085,8 +1328,14 @@ pub fn getAllErrorsAlloc(self: *Module) !AllErrors { |
| 1085 | 1328 | } |
| 1086 | 1329 | |
| 1087 | 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 | 1337 | try errors.append(.{ |
| 1089 | .src_path = self.root_pkg.root_src_path, | |
| 1338 | .src_path = global_err_src_path, | |
| 1090 | 1339 | .line = 0, |
| 1091 | 1340 | .column = 0, |
| 1092 | 1341 | .byte_offset = 0, |
| ... | ... | @@ -1175,6 +1424,41 @@ pub fn performAllTheWork(self: *Module) error{OutOfMemory}!void { |
| 1175 | 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 | 3445 | zir_module.status = .loaded_sema_failure; |
| 3162 | 3446 | self.failed_files.putAssumeCapacityNoClobber(scope, err_msg); |
| 3163 | 3447 | }, |
| 3448 | .none => unreachable, | |
| 3164 | 3449 | .file => unreachable, |
| 3165 | 3450 | .container => unreachable, |
| 3166 | 3451 | } |
src-self-hosted/link.zig+26-5| ... | ... | @@ -16,14 +16,36 @@ pub const Options = struct { |
| 16 | 16 | object_format: std.builtin.ObjectFormat, |
| 17 | 17 | optimize_mode: std.builtin.Mode, |
| 18 | 18 | root_name: []const u8, |
| 19 | root_pkg: *const Package, | |
| 19 | root_pkg: ?*const Package, | |
| 20 | 20 | /// Used for calculating how much space to reserve for symbols in case the binary file |
| 21 | 21 | /// does not already have a symbol table. |
| 22 | 22 | symbol_count_hint: u64 = 32, |
| 23 | 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 | 25 | program_code_size_hint: u64 = 256 * 1024, |
| 26 | 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 | 51 | pub const File = struct { |
| ... | ... | @@ -67,14 +89,13 @@ pub const File = struct { |
| 67 | 89 | /// and does not cause Illegal Behavior. This operation is not atomic. |
| 68 | 90 | pub fn openPath(allocator: *Allocator, dir: fs.Dir, sub_path: []const u8, options: Options) !*File { |
| 69 | 91 | switch (options.object_format) { |
| 70 | .unknown => unreachable, | |
| 71 | 92 | .coff, .pe => return Coff.openPath(allocator, dir, sub_path, options), |
| 72 | 93 | .elf => return Elf.openPath(allocator, dir, sub_path, options), |
| 73 | 94 | .macho => return MachO.openPath(allocator, dir, sub_path, options), |
| 74 | 95 | .wasm => return Wasm.openPath(allocator, dir, sub_path, options), |
| 75 | 96 | .c => return C.openPath(allocator, dir, sub_path, options), |
| 76 | .hex => return error.TODOImplementHex, | |
| 77 | .raw => return error.TODOImplementRaw, | |
| 97 | .hex => return error.HexObjectFormatUnimplemented, | |
| 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 | 25 | pub fn openPath(allocator: *Allocator, dir: fs.Dir, sub_path: []const u8, options: link.Options) !*File { |
| 26 | 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 | 31 | const file = try dir.createFile(sub_path, .{ .truncate = true, .read = true, .mode = link.determineMode(options) }); |
| 29 | 32 | errdefer file.close(); |
| 30 | 33 |
src-self-hosted/link/Coff.zig+3| ... | ... | @@ -113,6 +113,9 @@ pub const SrcFn = void; |
| 113 | 113 | pub fn openPath(allocator: *Allocator, dir: fs.Dir, sub_path: []const u8, options: link.Options) !*link.File { |
| 114 | 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 | 119 | const file = try dir.createFile(sub_path, .{ .truncate = false, .read = true, .mode = link.determineMode(options) }); |
| 117 | 120 | errdefer file.close(); |
| 118 | 121 |
src-self-hosted/link/Elf.zig+12-9| ... | ... | @@ -219,6 +219,9 @@ pub const SrcFn = struct { |
| 219 | 219 | pub fn openPath(allocator: *Allocator, dir: fs.Dir, sub_path: []const u8, options: link.Options) !*File { |
| 220 | 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 | 225 | const file = try dir.createFile(sub_path, .{ .truncate = false, .read = true, .mode = link.determineMode(options) }); |
| 223 | 226 | errdefer file.close(); |
| 224 | 227 | |
| ... | ... | @@ -235,7 +238,7 @@ pub fn openPath(allocator: *Allocator, dir: fs.Dir, sub_path: []const u8, option |
| 235 | 238 | |
| 236 | 239 | /// Returns error.IncrFailed if incremental update could not be performed. |
| 237 | 240 | fn openFile(allocator: *Allocator, file: fs.File, options: link.Options) !Elf { |
| 238 | switch (options.output_mode) { | |
| 241 | switch (options.effectiveOutputMode()) { | |
| 239 | 242 | .Exe => {}, |
| 240 | 243 | .Obj => {}, |
| 241 | 244 | .Lib => return error.IncrFailed, |
| ... | ... | @@ -264,7 +267,7 @@ fn openFile(allocator: *Allocator, file: fs.File, options: link.Options) !Elf { |
| 264 | 267 | /// Truncates the existing file contents and overwrites the contents. |
| 265 | 268 | /// Returns an error if `file` is not already open with +read +write +seek abilities. |
| 266 | 269 | fn createFile(allocator: *Allocator, file: fs.File, options: link.Options) !Elf { |
| 267 | switch (options.output_mode) { | |
| 270 | switch (options.effectiveOutputMode()) { | |
| 268 | 271 | .Exe => {}, |
| 269 | 272 | .Obj => {}, |
| 270 | 273 | .Lib => return error.TODOImplementWritingLibFiles, |
| ... | ... | @@ -861,8 +864,8 @@ pub fn flush(self: *Elf, module: *Module) !void { |
| 861 | 864 | }, |
| 862 | 865 | } |
| 863 | 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); | |
| 865 | const comp_dir_strp = try self.makeDebugString(self.base.options.root_pkg.root_src_dir_path); | |
| 867 | const name_strp = try self.makeDebugString(self.base.options.root_pkg.?.root_src_path); | |
| 868 | const comp_dir_strp = try self.makeDebugString(self.base.options.root_pkg.?.root_src_dir_path); | |
| 866 | 869 | const producer_strp = try self.makeDebugString(link.producer_string); |
| 867 | 870 | // Currently only one compilation unit is supported, so the address range is simply |
| 868 | 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 | 1034 | 0, // include_directories (none except the compilation unit cwd) |
| 1032 | 1035 | }); |
| 1033 | 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 | 1038 | di_buf.appendSliceAssumeCapacity(&[_]u8{ |
| 1036 | 1039 | 0, // null byte for the relative path name |
| 1037 | 1040 | 0, // directory_index |
| ... | ... | @@ -1195,7 +1198,7 @@ pub fn flush(self: *Elf, module: *Module) !void { |
| 1195 | 1198 | } |
| 1196 | 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 | 1202 | log.debug("flushing. no_entry_point_found = true\n", .{}); |
| 1200 | 1203 | self.error_flags.no_entry_point_found = true; |
| 1201 | 1204 | } else { |
| ... | ... | @@ -1255,7 +1258,7 @@ fn writeElfHeader(self: *Elf) !void { |
| 1255 | 1258 | |
| 1256 | 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 | 1262 | .Exe => elf.ET.EXEC, |
| 1260 | 1263 | .Obj => elf.ET.REL, |
| 1261 | 1264 | .Lib => switch (self.base.options.link_mode) { |
| ... | ... | @@ -2430,8 +2433,8 @@ fn dbgLineNeededHeaderBytes(self: Elf) u32 { |
| 2430 | 2433 | directory_count * 8 + file_name_count * 8 + |
| 2431 | 2434 | // These are encoded as DW.FORM_string rather than DW.FORM_strp as we would like |
| 2432 | 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 + | |
| 2434 | self.base.options.root_pkg.root_src_path.len); | |
| 2436 | self.base.options.root_pkg.?.root_src_dir_path.len + | |
| 2437 | self.base.options.root_pkg.?.root_src_path.len); | |
| 2435 | 2438 | } |
| 2436 | 2439 | |
| 2437 | 2440 | fn dbgInfoNeededHeaderBytes(self: Elf) u32 { |
src-self-hosted/link/MachO.zig+3| ... | ... | @@ -137,6 +137,9 @@ pub const SrcFn = struct { |
| 137 | 137 | pub fn openPath(allocator: *Allocator, dir: fs.Dir, sub_path: []const u8, options: link.Options) !*File { |
| 138 | 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 | 143 | const file = try dir.createFile(sub_path, .{ .truncate = false, .read = true, .mode = link.determineMode(options) }); |
| 141 | 144 | errdefer file.close(); |
| 142 | 145 |
src-self-hosted/link/Wasm.zig+3| ... | ... | @@ -52,6 +52,9 @@ funcs: std.ArrayListUnmanaged(*Module.Decl) = .{}, |
| 52 | 52 | pub fn openPath(allocator: *Allocator, dir: fs.Dir, sub_path: []const u8, options: link.Options) !*link.File { |
| 53 | 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 | 58 | // TODO: read the file and keep vaild parts instead of truncating |
| 56 | 59 | const file = try dir.createFile(sub_path, .{ .truncate = true, .read = true }); |
| 57 | 60 | errdefer file.close(); |
src-self-hosted/main.zig+87-19| ... | ... | @@ -13,6 +13,7 @@ const Package = @import("Package.zig"); |
| 13 | 13 | const zir = @import("zir.zig"); |
| 14 | 14 | const build_options = @import("build_options"); |
| 15 | 15 | const warn = std.log.warn; |
| 16 | const introspect = @import("introspect.zig"); | |
| 16 | 17 | |
| 17 | 18 | fn fatal(comptime format: []const u8, args: anytype) noreturn { |
| 18 | 19 | std.log.emerg(format, args); |
| ... | ... | @@ -231,7 +232,6 @@ pub fn buildOutputType( |
| 231 | 232 | var root_src_file: ?[]const u8 = null; |
| 232 | 233 | var version: std.builtin.Version = .{ .major = 0, .minor = 0, .patch = 0 }; |
| 233 | 234 | var strip = false; |
| 234 | var emit_h = true; | |
| 235 | 235 | var watch = false; |
| 236 | 236 | var debug_tokenize = false; |
| 237 | 237 | var debug_ast_tree = false; |
| ... | ... | @@ -248,6 +248,7 @@ pub fn buildOutputType( |
| 248 | 248 | var target_dynamic_linker: ?[]const u8 = null; |
| 249 | 249 | var target_ofmt: ?[]const u8 = null; |
| 250 | 250 | var output_mode: std.builtin.OutputMode = undefined; |
| 251 | var emit_h: Emit = undefined; | |
| 251 | 252 | var ensure_libc_on_non_freestanding = false; |
| 252 | 253 | var ensure_libcpp_on_non_freestanding = false; |
| 253 | 254 | var have_libc = false; |
| ... | ... | @@ -269,6 +270,9 @@ pub fn buildOutputType( |
| 269 | 270 | var linker_z_nodelete = false; |
| 270 | 271 | var linker_z_defs = false; |
| 271 | 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 | 277 | var system_libs = std.ArrayList([]const u8).init(gpa); |
| 274 | 278 | defer system_libs.deinit(); |
| ... | ... | @@ -296,6 +300,10 @@ pub fn buildOutputType( |
| 296 | 300 | |
| 297 | 301 | if (arg_mode == .build) { |
| 298 | 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 | 308 | const args = all_args[2..]; |
| 301 | 309 | var i: usize = 0; |
| ... | ... | @@ -416,6 +424,18 @@ pub fn buildOutputType( |
| 416 | 424 | want_pic = true; |
| 417 | 425 | } else if (mem.eql(u8, arg, "-fno-PIC")) { |
| 418 | 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 | 439 | } else if (mem.eql(u8, arg, "-rdynamic")) { |
| 420 | 440 | rdynamic = true; |
| 421 | 441 | } else if (mem.eql(u8, arg, "-femit-bin")) { |
| ... | ... | @@ -430,6 +450,12 @@ pub fn buildOutputType( |
| 430 | 450 | emit_zir = .{ .yes = arg["-femit-zir=".len..] }; |
| 431 | 451 | } else if (mem.eql(u8, arg, "-fno-emit-zir")) { |
| 432 | 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 | 459 | } else if (mem.eql(u8, arg, "-dynamic")) { |
| 434 | 460 | link_mode = .Dynamic; |
| 435 | 461 | } else if (mem.eql(u8, arg, "-static")) { |
| ... | ... | @@ -491,7 +517,7 @@ pub fn buildOutputType( |
| 491 | 517 | } |
| 492 | 518 | } |
| 493 | 519 | } else { |
| 494 | emit_h = false; | |
| 520 | emit_h = .no; | |
| 495 | 521 | strip = true; |
| 496 | 522 | ensure_libc_on_non_freestanding = true; |
| 497 | 523 | ensure_libcpp_on_non_freestanding = arg_mode == .cpp; |
| ... | ... | @@ -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 | 903 | const object_format: ?std.Target.ObjectFormat = blk: { |
| 886 | 904 | const ofmt = target_ofmt orelse break :blk null; |
| 887 | 905 | if (mem.eql(u8, ofmt, "elf")) { |
| ... | ... | @@ -909,11 +927,14 @@ pub fn buildOutputType( |
| 909 | 927 | .no => { |
| 910 | 928 | fatal("-fno-emit-bin not supported yet", .{}); |
| 911 | 929 | }, |
| 912 | .yes_default_path => if (object_format != null and object_format.? == .c) | |
| 913 | try std.fmt.allocPrint(arena, "{}.c", .{root_name}) | |
| 914 | else | |
| 915 | try std.zig.binNameAlloc(arena, root_name, target_info.target, output_mode, link_mode), | |
| 916 | ||
| 930 | .yes_default_path => try std.zig.binNameAlloc( | |
| 931 | arena, | |
| 932 | root_name, | |
| 933 | target_info.target, | |
| 934 | output_mode, | |
| 935 | link_mode, | |
| 936 | object_format, | |
| 937 | ), | |
| 917 | 938 | .yes => |p| p, |
| 918 | 939 | }; |
| 919 | 940 | |
| ... | ... | @@ -930,10 +951,25 @@ pub fn buildOutputType( |
| 930 | 951 | .yes => |p| p, |
| 931 | 952 | }; |
| 932 | 953 | |
| 933 | const root_pkg = try Package.create(gpa, fs.cwd(), ".", src_path); | |
| 934 | defer root_pkg.destroy(); | |
| 954 | const root_pkg = if (root_src_file) |src_path| try Package.create(gpa, fs.cwd(), ".", src_path) else null; | |
| 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 | 973 | .root_name = root_name, |
| 938 | 974 | .target = target_info.target, |
| 939 | 975 | .output_mode = output_mode, |
| ... | ... | @@ -944,7 +980,39 @@ pub fn buildOutputType( |
| 944 | 980 | .object_format = object_format, |
| 945 | 981 | .optimize_mode = build_mode, |
| 946 | 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 | 1016 | defer module.deinit(); |
| 949 | 1017 | |
| 950 | 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 | 9 | const enable_wine: bool = build_options.enable_wine; |
| 10 | 10 | const enable_wasmtime: bool = build_options.enable_wasmtime; |
| 11 | 11 | const glibc_multi_install_dir: ?[]const u8 = build_options.glibc_multi_install_dir; |
| 12 | const introspect = @import("introspect.zig"); | |
| 12 | 13 | |
| 13 | 14 | const cheader = @embedFile("link/cbe.h"); |
| 14 | 15 | |
| ... | ... | @@ -435,7 +436,10 @@ pub const TestContext = struct { |
| 435 | 436 | const root_pkg = try Package.create(allocator, tmp.dir, ".", tmp_src_path); |
| 436 | 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 | 444 | var module = try Module.init(allocator, .{ |
| 441 | 445 | .root_name = "test_case", |
| ... | ... | @@ -450,7 +454,8 @@ pub const TestContext = struct { |
| 450 | 454 | .bin_file_path = bin_name, |
| 451 | 455 | .root_pkg = root_pkg, |
| 452 | 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 | 460 | defer module.deinit(); |
| 456 | 461 | |
| ... | ... | @@ -693,23 +698,23 @@ pub const TestContext = struct { |
| 693 | 698 | } |
| 694 | 699 | |
| 695 | 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 { | |
| 699 | return bus.RAM[addr]; | |
| 700 | } | |
| 701 | pub fn read16(bus: @This(), addr: u16) u16 { | |
| 702 | return std.mem.readIntLittle(u16, bus.RAM[addr..][0..2]); | |
| 703 | } | |
| 703 | pub fn read8(bus: @This(), addr: u16) u8 { | |
| 704 | return bus.RAM[addr]; | |
| 705 | } | |
| 706 | pub fn read16(bus: @This(), addr: u16) u16 { | |
| 707 | return std.mem.readIntLittle(u16, bus.RAM[addr..][0..2]); | |
| 708 | } | |
| 704 | 709 | |
| 705 | pub fn write8(bus: *@This(), addr: u16, val: u8) void { | |
| 706 | bus.RAM[addr] = val; | |
| 707 | } | |
| 710 | pub fn write8(bus: *@This(), addr: u16, val: u8) void { | |
| 711 | bus.RAM[addr] = val; | |
| 712 | } | |
| 708 | 713 | |
| 709 | pub fn write16(bus: *@This(), addr: u16, val: u16) void { | |
| 710 | std.mem.writeIntLittle(u16, bus.RAM[addr..][0..2], val); | |
| 711 | } | |
| 712 | }){ | |
| 714 | pub fn write16(bus: *@This(), addr: u16, val: u16) void { | |
| 715 | std.mem.writeIntLittle(u16, bus.RAM[addr..][0..2], val); | |
| 716 | } | |
| 717 | }){ | |
| 713 | 718 | .bus = .{}, |
| 714 | 719 | }; |
| 715 | 720 |