| author | |
| committer | |
| log | 12cb5b92851f601c44d48deadea9934146edcffa |
| tree | 12d722c76642c4f501e027c56a0e51f5b7a1f0a3 |
| parent | 36b65ab59e5e514ad06a11cde96b87c565d86ac8 |
| parent | 355c6260015292642badf0e8b786e676fbb8c961 |
Reviewed-on: https://codeberg.org/ziglang/zig/pulls/311386 files changed, 343 insertions(+), 358 deletions(-)
lib/compiler/build_runner.zig+26-23| ... | ... | @@ -1573,6 +1573,23 @@ fn printUsage(b: *std.Build, w: *Writer) !void { |
| 1573 | 1573 | \\ -fsys=[name] Enable a system integration |
| 1574 | 1574 | \\ -fno-sys=[name] Disable a system integration |
| 1575 | 1575 | \\ |
| 1576 | \\ -fdarling, -fno-darling Integration with system-installed Darling to | |
| 1577 | \\ execute macOS programs on Linux hosts | |
| 1578 | \\ (default: no) | |
| 1579 | \\ -fqemu, -fno-qemu Integration with system-installed QEMU to execute | |
| 1580 | \\ foreign-architecture programs on Linux hosts | |
| 1581 | \\ (default: no) | |
| 1582 | \\ --libc-runtimes [path] Enhances QEMU integration by providing dynamic libc | |
| 1583 | \\ (e.g. glibc or musl) built for multiple foreign | |
| 1584 | \\ architectures, allowing execution of non-native | |
| 1585 | \\ programs that link with libc. | |
| 1586 | \\ -frosetta, -fno-rosetta Rely on Rosetta to execute x86_64 programs on | |
| 1587 | \\ ARM64 macOS hosts. (default: no) | |
| 1588 | \\ -fwasmtime, -fno-wasmtime Integration with system-installed wasmtime to | |
| 1589 | \\ execute WASI binaries. (default: no) | |
| 1590 | \\ -fwine, -fno-wine Integration with system-installed Wine to execute | |
| 1591 | \\ Windows programs on Linux hosts. (default: no) | |
| 1592 | \\ | |
| 1576 | 1593 | \\ Available System Integrations: Enabled: |
| 1577 | 1594 | \\ |
| 1578 | 1595 | ); |
| ... | ... | @@ -1592,33 +1609,16 @@ fn printUsage(b: *std.Build, w: *Writer) !void { |
| 1592 | 1609 | try w.writeAll( |
| 1593 | 1610 | \\ |
| 1594 | 1611 | \\General Options: |
| 1612 | \\ -h, --help Print this help and exit | |
| 1613 | \\ -l, --list-steps Print available steps | |
| 1614 | \\ | |
| 1595 | 1615 | \\ -p, --prefix [path] Where to install files (default: zig-out) |
| 1596 | 1616 | \\ --prefix-lib-dir [path] Where to install libraries |
| 1597 | 1617 | \\ --prefix-exe-dir [path] Where to install executables |
| 1598 | 1618 | \\ --prefix-include-dir [path] Where to install C header files |
| 1599 | \\ | |
| 1600 | 1619 | \\ --release[=mode] Request release mode, optionally specifying a |
| 1601 | 1620 | \\ preferred optimization mode: fast, safe, small |
| 1602 | 1621 | \\ |
| 1603 | \\ -fdarling, -fno-darling Integration with system-installed Darling to | |
| 1604 | \\ execute macOS programs on Linux hosts | |
| 1605 | \\ (default: no) | |
| 1606 | \\ -fqemu, -fno-qemu Integration with system-installed QEMU to execute | |
| 1607 | \\ foreign-architecture programs on Linux hosts | |
| 1608 | \\ (default: no) | |
| 1609 | \\ --libc-runtimes [path] Enhances QEMU integration by providing dynamic libc | |
| 1610 | \\ (e.g. glibc or musl) built for multiple foreign | |
| 1611 | \\ architectures, allowing execution of non-native | |
| 1612 | \\ programs that link with libc. | |
| 1613 | \\ -frosetta, -fno-rosetta Rely on Rosetta to execute x86_64 programs on | |
| 1614 | \\ ARM64 macOS hosts. (default: no) | |
| 1615 | \\ -fwasmtime, -fno-wasmtime Integration with system-installed wasmtime to | |
| 1616 | \\ execute WASI binaries. (default: no) | |
| 1617 | \\ -fwine, -fno-wine Integration with system-installed Wine to execute | |
| 1618 | \\ Windows programs on Linux hosts. (default: no) | |
| 1619 | \\ | |
| 1620 | \\ -h, --help Print this help and exit | |
| 1621 | \\ -l, --list-steps Print available steps | |
| 1622 | 1622 | \\ --verbose Print commands before executing them |
| 1623 | 1623 | \\ --color [auto|off|on] Enable or disable colored error messages |
| 1624 | 1624 | \\ --error-style [style] Control how build errors are printed |
| ... | ... | @@ -1641,9 +1641,6 @@ fn printUsage(b: *std.Build, w: *Writer) !void { |
| 1641 | 1641 | \\ --skip-oom-steps Instead of failing, skip steps that would exceed --maxrss |
| 1642 | 1642 | \\ --test-timeout <timeout> Limit execution time of unit tests, terminating if exceeded. |
| 1643 | 1643 | \\ The timeout must include a unit: ns, us, ms, s, m, h |
| 1644 | \\ --fetch[=mode] Fetch dependency tree (optionally choose laziness) and exit | |
| 1645 | \\ needed (Default) Lazy dependencies are fetched as needed | |
| 1646 | \\ all Lazy dependencies are always fetched | |
| 1647 | 1644 | \\ --watch Continuously rebuild when source files are modified |
| 1648 | 1645 | \\ --debounce <ms> Delay before rebuilding after changed file detected |
| 1649 | 1646 | \\ --webui[=ip] Enable the web interface on the given IP address |
| ... | ... | @@ -1656,6 +1653,12 @@ fn printUsage(b: *std.Build, w: *Writer) !void { |
| 1656 | 1653 | \\ -fincremental Enable incremental compilation |
| 1657 | 1654 | \\ -fno-incremental Disable incremental compilation |
| 1658 | 1655 | \\ |
| 1656 | \\Package Management Options: | |
| 1657 | \\ --fetch[=mode] Fetch dependency tree (optionally choose laziness) and exit | |
| 1658 | \\ needed (Default) Lazy dependencies are fetched as needed | |
| 1659 | \\ all Lazy dependencies are always fetched | |
| 1660 | \\ --fork=[path] Override one or more projects from dependency tree | |
| 1661 | \\ | |
| 1659 | 1662 | \\Advanced Options: |
| 1660 | 1663 | \\ -freference-trace[=num] How many lines of reference trace should be shown per compile error |
| 1661 | 1664 | \\ -fno-reference-trace Disable reference trace |
src/Package.zig+50-58| ... | ... | @@ -6,10 +6,6 @@ pub const Fetch = @import("Package/Fetch.zig"); |
| 6 | 6 | pub const build_zig_basename = "build.zig"; |
| 7 | 7 | pub const Manifest = @import("Package/Manifest.zig"); |
| 8 | 8 | |
| 9 | pub const multihash_len = 1 + 1 + Hash.Algo.digest_length; | |
| 10 | pub const multihash_hex_digest_len = 2 * multihash_len; | |
| 11 | pub const MultiHashHexDigest = [multihash_hex_digest_len]u8; | |
| 12 | ||
| 13 | 9 | pub const Fingerprint = packed struct(u64) { |
| 14 | 10 | id: u32, |
| 15 | 11 | checksum: u32, |
| ... | ... | @@ -77,20 +73,6 @@ pub const Hash = struct { |
| 77 | 73 | return std.mem.eql(u8, &a.bytes, &b.bytes); |
| 78 | 74 | } |
| 79 | 75 | |
| 80 | /// Distinguishes whether the legacy multihash format is being stored here. | |
| 81 | pub fn isOld(h: *const Hash) bool { | |
| 82 | if (h.bytes.len < 2) return false; | |
| 83 | const their_multihash_func = std.fmt.parseInt(u8, h.bytes[0..2], 16) catch return false; | |
| 84 | if (@as(MultihashFunction, @enumFromInt(their_multihash_func)) != multihash_function) return false; | |
| 85 | if (h.toSlice().len != multihash_hex_digest_len) return false; | |
| 86 | return std.mem.indexOfScalar(u8, &h.bytes, '-') == null; | |
| 87 | } | |
| 88 | ||
| 89 | test isOld { | |
| 90 | const h: Hash = .fromSlice("1220138f4aba0c01e66b68ed9e1e1e74614c06e4743d88bc58af4f1c3dd0aae5fea7"); | |
| 91 | try std.testing.expect(h.isOld()); | |
| 92 | } | |
| 93 | ||
| 94 | 76 | /// Produces "$name-$semver-$hashplus". |
| 95 | 77 | /// * name is the name field from build.zig.zon, asserted to be at most 32 |
| 96 | 78 | /// bytes and assumed be a valid zig identifier |
| ... | ... | @@ -137,55 +119,65 @@ pub const Hash = struct { |
| 137 | 119 | _ = std.fmt.bufPrint(result.bytes[i..], "{x}", .{&bin_digest}) catch unreachable; |
| 138 | 120 | return result; |
| 139 | 121 | } |
| 140 | }; | |
| 141 | 122 | |
| 142 | pub const MultihashFunction = enum(u16) { | |
| 143 | identity = 0x00, | |
| 144 | sha1 = 0x11, | |
| 145 | @"sha2-256" = 0x12, | |
| 146 | @"sha2-512" = 0x13, | |
| 147 | @"sha3-512" = 0x14, | |
| 148 | @"sha3-384" = 0x15, | |
| 149 | @"sha3-256" = 0x16, | |
| 150 | @"sha3-224" = 0x17, | |
| 151 | @"sha2-384" = 0x20, | |
| 152 | @"sha2-256-trunc254-padded" = 0x1012, | |
| 153 | @"sha2-224" = 0x1013, | |
| 154 | @"sha2-512-224" = 0x1014, | |
| 155 | @"sha2-512-256" = 0x1015, | |
| 156 | @"blake2b-256" = 0xb220, | |
| 157 | _, | |
| 158 | }; | |
| 123 | pub fn projectId(hash: *const Hash) ProjectId { | |
| 124 | const bytes = hash.toSlice(); | |
| 125 | const name = std.mem.sliceTo(bytes, '-'); | |
| 126 | const encoded_hashplus = bytes[bytes.len - 44 ..]; | |
| 127 | var hashplus: [33]u8 = undefined; | |
| 128 | std.base64.url_safe_no_pad.Decoder.decode(&hashplus, encoded_hashplus) catch unreachable; | |
| 129 | const fingerprint_id = std.mem.readInt(u32, hashplus[0..4], .little); | |
| 130 | return .init(name, fingerprint_id); | |
| 131 | } | |
| 159 | 132 | |
| 160 | pub const multihash_function: MultihashFunction = switch (Hash.Algo) { | |
| 161 | std.crypto.hash.sha2.Sha256 => .@"sha2-256", | |
| 162 | else => unreachable, | |
| 163 | }; | |
| 133 | test projectId { | |
| 134 | const hash: Hash = .fromSlice("pulseaudio-16.1.1-9-mk_62MZkNwBaFwiZ7ZVrYRIf_3dTqqJR5PbMRCJzSuLw"); | |
| 135 | const project_id = hash.projectId(); | |
| 164 | 136 | |
| 165 | pub fn multiHashHexDigest(digest: Hash.Digest) MultiHashHexDigest { | |
| 166 | const hex_charset = std.fmt.hex_charset; | |
| 137 | var expected_name: [32]u8 = @splat(0); | |
| 138 | expected_name[0.."pulseaudio".len].* = "pulseaudio".*; | |
| 139 | try std.testing.expectEqualSlices(u8, &expected_name, &project_id.padded_name); | |
| 167 | 140 | |
| 168 | var result: MultiHashHexDigest = undefined; | |
| 141 | try std.testing.expectEqual(0xd8fa4f9a, project_id.fingerprint_id); | |
| 142 | } | |
| 169 | 143 | |
| 170 | result[0] = hex_charset[@intFromEnum(multihash_function) >> 4]; | |
| 171 | result[1] = hex_charset[@intFromEnum(multihash_function) & 15]; | |
| 144 | test "projectId with dashes in the base64" { | |
| 145 | const hash: Hash = .fromSlice("dvui-0.4.0-dev-AQFJmayi2gAKE7FeJoF61v5U1IV9-SupoEcFutIZYpkC"); | |
| 146 | const project_id = hash.projectId(); | |
| 172 | 147 | |
| 173 | result[2] = hex_charset[Hash.Algo.digest_length >> 4]; | |
| 174 | result[3] = hex_charset[Hash.Algo.digest_length & 15]; | |
| 148 | var expected_name: [32]u8 = @splat(0); | |
| 149 | expected_name[0.."dvui".len].* = "dvui".*; | |
| 150 | try std.testing.expectEqualSlices(u8, &expected_name, &project_id.padded_name); | |
| 175 | 151 | |
| 176 | for (digest, 0..) |byte, i| { | |
| 177 | result[4 + i * 2] = hex_charset[byte >> 4]; | |
| 178 | result[5 + i * 2] = hex_charset[byte & 15]; | |
| 152 | try std.testing.expectEqual(0x99490101, project_id.fingerprint_id); | |
| 179 | 153 | } |
| 180 | return result; | |
| 181 | } | |
| 154 | }; | |
| 182 | 155 | |
| 183 | comptime { | |
| 184 | // We avoid unnecessary uleb128 code in hexDigest by asserting here the | |
| 185 | // values are small enough to be contained in the one-byte encoding. | |
| 186 | assert(@intFromEnum(multihash_function) < 127); | |
| 187 | assert(Hash.Algo.digest_length < 127); | |
| 188 | } | |
| 156 | /// Minimum information required to identify whether a package is an artifact | |
| 157 | /// of a given project. | |
| 158 | pub const ProjectId = struct { | |
| 159 | /// Bytes after name.len are set to zero. | |
| 160 | padded_name: [32]u8, | |
| 161 | fingerprint_id: u32, | |
| 162 | ||
| 163 | pub fn init(name: []const u8, fingerprint_id: u32) ProjectId { | |
| 164 | var padded_name: [32]u8 = @splat(0); | |
| 165 | @memcpy(padded_name[0..name.len], name); | |
| 166 | return .{ | |
| 167 | .padded_name = padded_name, | |
| 168 | .fingerprint_id = fingerprint_id, | |
| 169 | }; | |
| 170 | } | |
| 171 | ||
| 172 | pub fn eql(a: *const ProjectId, b: *const ProjectId) bool { | |
| 173 | return a.fingerprint_id == b.fingerprint_id and std.mem.eql(u8, &a.padded_name, &b.padded_name); | |
| 174 | } | |
| 175 | ||
| 176 | pub fn hash(a: *const ProjectId) u64 { | |
| 177 | const x: u64 = @bitCast(a.padded_name[0..8].*); | |
| 178 | return std.hash.int(x | a.fingerprint_id); | |
| 179 | } | |
| 180 | }; | |
| 189 | 181 | |
| 190 | 182 | test Hash { |
| 191 | 183 | const example_digest: Hash.Digest = .{ |
src/Package/Fetch.zig+79-252| ... | ... | @@ -76,8 +76,9 @@ use_latest_commit: bool, |
| 76 | 76 | /// Relative to the build root of the root package. |
| 77 | 77 | package_root: Cache.Path, |
| 78 | 78 | error_bundle: ErrorBundle.Wip, |
| 79 | manifest: ?Manifest, | |
| 79 | manifest: Manifest, | |
| 80 | 80 | manifest_ast: std.zig.Ast, |
| 81 | have_manifest: bool, | |
| 81 | 82 | computed_hash: ComputedHash, |
| 82 | 83 | /// Fetch logic notices whether a package has a build.zig file and sets this flag. |
| 83 | 84 | has_build_zig: bool, |
| ... | ... | @@ -142,6 +143,9 @@ pub const JobQueue = struct { |
| 142 | 143 | /// Set of hashes that will be additionally fetched even if they are marked |
| 143 | 144 | /// as lazy. |
| 144 | 145 | unlazy_set: UnlazySet = .{}, |
| 146 | /// Identifies paths that override all packages in the tree with matching | |
| 147 | /// project ids. | |
| 148 | fork_set: ForkSet = .{}, | |
| 145 | 149 | |
| 146 | 150 | pub const Mode = enum { |
| 147 | 151 | /// Non-lazy dependencies are always fetched. |
| ... | ... | @@ -152,6 +156,38 @@ pub const JobQueue = struct { |
| 152 | 156 | }; |
| 153 | 157 | pub const Table = std.AutoArrayHashMapUnmanaged(Package.Hash, *Fetch); |
| 154 | 158 | pub const UnlazySet = std.AutoArrayHashMapUnmanaged(Package.Hash, void); |
| 159 | pub const ForkSet = std.ArrayHashMapUnmanaged(Fork, void, Fork.Context, false); | |
| 160 | ||
| 161 | pub const Fork = struct { | |
| 162 | path: Cache.Path, | |
| 163 | manifest_ast: std.zig.Ast, | |
| 164 | manifest: Package.Manifest, | |
| 165 | uses: usize, | |
| 166 | ||
| 167 | pub const Context = struct { | |
| 168 | pub fn hash(_: @This(), a: Fork) u32 { | |
| 169 | const project_id: Package.ProjectId = .init(a.manifest.name, a.manifest.id); | |
| 170 | return @truncate(project_id.hash()); | |
| 171 | } | |
| 172 | ||
| 173 | pub fn eql(_: @This(), a: Fork, b: Fork, _: usize) bool { | |
| 174 | const a_project_id: Package.ProjectId = .init(a.manifest.name, a.manifest.id); | |
| 175 | const b_project_id: Package.ProjectId = .init(b.manifest.name, b.manifest.id); | |
| 176 | return a_project_id.eql(&b_project_id); | |
| 177 | } | |
| 178 | }; | |
| 179 | ||
| 180 | pub const Adapter = struct { | |
| 181 | pub fn hash(_: @This(), a: Package.ProjectId) u32 { | |
| 182 | return @truncate(a.hash()); | |
| 183 | } | |
| 184 | ||
| 185 | pub fn eql(_: @This(), a_project_id: Package.ProjectId, b: Fork, _: usize) bool { | |
| 186 | const b_project_id: Package.ProjectId = .init(b.manifest.name, b.manifest.id); | |
| 187 | return a_project_id.eql(&b_project_id); | |
| 188 | } | |
| 189 | }; | |
| 190 | }; | |
| 155 | 191 | |
| 156 | 192 | pub fn deinit(jq: *JobQueue) void { |
| 157 | 193 | const io = jq.io; |
| ... | ... | @@ -248,7 +284,8 @@ pub const JobQueue = struct { |
| 248 | 284 | , .{std.zig.fmtString(hash_slice)}); |
| 249 | 285 | } |
| 250 | 286 | |
| 251 | if (fetch.manifest) |*manifest| { | |
| 287 | if (fetch.have_manifest) { | |
| 288 | const manifest = &fetch.manifest; | |
| 252 | 289 | try buf.appendSlice( |
| 253 | 290 | \\ pub const deps: []const struct { []const u8, []const u8 } = &.{ |
| 254 | 291 | \\ |
| ... | ... | @@ -283,7 +320,8 @@ pub const JobQueue = struct { |
| 283 | 320 | ); |
| 284 | 321 | |
| 285 | 322 | const root_fetch = jq.all_fetches.items[0]; |
| 286 | const root_manifest = &root_fetch.manifest.?; | |
| 323 | assert(root_fetch.have_manifest); | |
| 324 | const root_manifest = &root_fetch.manifest; | |
| 287 | 325 | |
| 288 | 326 | for (root_manifest.dependencies.keys(), root_manifest.dependencies.values()) |name, dep| { |
| 289 | 327 | const h = depDigest(root_fetch.package_root, jq.global_cache, dep) orelse continue; |
| ... | ... | @@ -536,6 +574,19 @@ pub fn run(f: *Fetch) RunError!void { |
| 536 | 574 | var resource_buffer: [init_resource_buffer_size]u8 = undefined; |
| 537 | 575 | |
| 538 | 576 | if (remote.hash) |expected_hash| { |
| 577 | const expected_project_id: Package.ProjectId = expected_hash.projectId(); | |
| 578 | if (job_queue.fork_set.getKeyPtrAdapted(expected_project_id, @as(JobQueue.Fork.Adapter, .{}))) |fork| { | |
| 579 | log.debug("using fork {f} for {s}", .{ fork.path, fork.manifest.name }); | |
| 580 | fork.uses += 1; | |
| 581 | f.package_root = fork.path; | |
| 582 | f.manifest_ast = fork.manifest_ast; | |
| 583 | f.manifest = fork.manifest; | |
| 584 | f.have_manifest = true; | |
| 585 | try checkBuildFileExistence(f); | |
| 586 | if (!job_queue.recursive) return; | |
| 587 | return queueJobsForDeps(f); | |
| 588 | } | |
| 589 | ||
| 539 | 590 | const package_root = try job_queue.root_pkg_path.join(arena, expected_hash.toSlice()); |
| 540 | 591 | if (package_root.root_dir.handle.access(io, package_root.sub_path, .{})) |_| { |
| 541 | 592 | assert(f.lazy_status != .unavailable); |
| ... | ... | @@ -673,7 +724,7 @@ fn runResource( |
| 673 | 724 | try loadManifest(f, pkg_path); |
| 674 | 725 | |
| 675 | 726 | const filter: Filter = .{ |
| 676 | .include_paths = if (f.manifest) |m| m.paths else .{}, | |
| 727 | .include_paths = if (f.have_manifest) f.manifest.paths else .{}, | |
| 677 | 728 | }; |
| 678 | 729 | |
| 679 | 730 | // Ignore errors that were excluded by manifest, such as failure to |
| ... | ... | @@ -728,21 +779,11 @@ fn runResource( |
| 728 | 779 | |
| 729 | 780 | if (remote_hash) |declared_hash| { |
| 730 | 781 | const hash_tok = f.hash_tok.unwrap().?; |
| 731 | if (declared_hash.isOld()) { | |
| 732 | const actual_hex = Package.multiHashHexDigest(f.computed_hash.digest); | |
| 733 | if (!std.mem.eql(u8, declared_hash.toSlice(), &actual_hex)) { | |
| 734 | return f.fail(hash_tok, try eb.printString( | |
| 735 | "hash mismatch: manifest declares '{s}' but the fetched package has '{s}'", | |
| 736 | .{ declared_hash.toSlice(), actual_hex }, | |
| 737 | )); | |
| 738 | } | |
| 739 | } else { | |
| 740 | if (!computed_package_hash.eql(&declared_hash)) { | |
| 741 | return f.fail(hash_tok, try eb.printString( | |
| 742 | "hash mismatch: manifest declares '{s}' but the fetched package has '{s}'", | |
| 743 | .{ declared_hash.toSlice(), computed_package_hash.toSlice() }, | |
| 744 | )); | |
| 745 | } | |
| 782 | if (!computed_package_hash.eql(&declared_hash)) { | |
| 783 | return f.fail(hash_tok, try eb.printString( | |
| 784 | "hash mismatch: manifest declares '{s}' but the fetched package has '{s}'", | |
| 785 | .{ declared_hash.toSlice(), computed_package_hash.toSlice() }, | |
| 786 | )); | |
| 746 | 787 | } |
| 747 | 788 | } else if (!f.omit_missing_hash_error) { |
| 748 | 789 | const notes_len = 1; |
| ... | ... | @@ -766,7 +807,8 @@ fn runResource( |
| 766 | 807 | |
| 767 | 808 | pub fn computedPackageHash(f: *const Fetch) Package.Hash { |
| 768 | 809 | const saturated_size = std.math.cast(u32, f.computed_hash.total_size) orelse std.math.maxInt(u32); |
| 769 | if (f.manifest) |man| { | |
| 810 | if (f.have_manifest) { | |
| 811 | const man = &f.manifest; | |
| 770 | 812 | var version_buffer: [32]u8 = undefined; |
| 771 | 813 | const version: []const u8 = std.fmt.bufPrint(&version_buffer, "{f}", .{man.version}) catch &version_buffer; |
| 772 | 814 | return .init(f.computed_hash.digest, man.name, version, man.id, saturated_size); |
| ... | ... | @@ -801,45 +843,28 @@ fn loadManifest(f: *Fetch, pkg_root: Cache.Path) RunError!void { |
| 801 | 843 | const io = f.job_queue.io; |
| 802 | 844 | const eb = &f.error_bundle; |
| 803 | 845 | const arena = f.arena.allocator(); |
| 804 | const manifest_bytes = pkg_root.root_dir.handle.readFileAllocOptions( | |
| 846 | const manifest_path = try pkg_root.join(arena, Manifest.basename); | |
| 847 | ||
| 848 | Manifest.load( | |
| 805 | 849 | io, |
| 806 | try fs.path.join(arena, &.{ pkg_root.sub_path, Manifest.basename }), | |
| 807 | 850 | arena, |
| 808 | .limited(Manifest.max_bytes), | |
| 809 | .@"1", | |
| 810 | 0, | |
| 851 | manifest_path, | |
| 852 | &f.manifest_ast, | |
| 853 | eb, | |
| 854 | &f.manifest, | |
| 855 | f.allow_missing_paths_field, | |
| 811 | 856 | ) catch |err| switch (err) { |
| 812 | 857 | error.FileNotFound => return, |
| 858 | error.Canceled => |e| return e, | |
| 859 | error.ErrorsBundled => return error.FetchFailed, | |
| 813 | 860 | else => |e| { |
| 814 | const file_path = try pkg_root.join(arena, Manifest.basename); | |
| 815 | 861 | try eb.addRootErrorMessage(.{ |
| 816 | .msg = try eb.printString("unable to load package manifest '{f}': {t}", .{ file_path, e }), | |
| 862 | .msg = try eb.printString("unable to load package manifest '{f}': {t}", .{ manifest_path, e }), | |
| 817 | 863 | }); |
| 818 | 864 | return error.FetchFailed; |
| 819 | 865 | }, |
| 820 | 866 | }; |
| 821 | ||
| 822 | const ast = &f.manifest_ast; | |
| 823 | ast.* = try std.zig.Ast.parse(arena, manifest_bytes, .zon); | |
| 824 | ||
| 825 | if (ast.errors.len > 0) { | |
| 826 | const file_path = try std.fmt.allocPrint(arena, "{f}" ++ fs.path.sep_str ++ Manifest.basename, .{pkg_root}); | |
| 827 | try std.zig.putAstErrorsIntoBundle(arena, ast.*, file_path, eb); | |
| 828 | return error.FetchFailed; | |
| 829 | } | |
| 830 | ||
| 831 | const rng: std.Random.IoSource = .{ .io = io }; | |
| 832 | ||
| 833 | f.manifest = try Manifest.parse(arena, ast.*, rng.interface(), .{ | |
| 834 | .allow_missing_paths_field = f.allow_missing_paths_field, | |
| 835 | }); | |
| 836 | const manifest = &f.manifest.?; | |
| 837 | ||
| 838 | if (manifest.errors.len > 0) { | |
| 839 | const src_path = try eb.printString("{f}" ++ fs.path.sep_str ++ "{s}", .{ pkg_root, Manifest.basename }); | |
| 840 | try manifest.copyErrorsIntoBundle(ast.*, src_path, eb); | |
| 841 | return error.FetchFailed; | |
| 842 | } | |
| 867 | f.have_manifest = true; | |
| 843 | 868 | } |
| 844 | 869 | |
| 845 | 870 | fn queueJobsForDeps(f: *Fetch) RunError!void { |
| ... | ... | @@ -848,7 +873,8 @@ fn queueJobsForDeps(f: *Fetch) RunError!void { |
| 848 | 873 | assert(f.job_queue.recursive); |
| 849 | 874 | |
| 850 | 875 | // If the package does not have a build.zig.zon file then there are no dependencies. |
| 851 | const manifest = f.manifest orelse return; | |
| 876 | if (!f.have_manifest) return; | |
| 877 | const manifest = &f.manifest; | |
| 852 | 878 | |
| 853 | 879 | const new_fetches, const prog_names = nf: { |
| 854 | 880 | const parent_arena = f.arena.allocator(); |
| ... | ... | @@ -953,8 +979,9 @@ fn queueJobsForDeps(f: *Fetch) RunError!void { |
| 953 | 979 | |
| 954 | 980 | .package_root = undefined, |
| 955 | 981 | .error_bundle = undefined, |
| 956 | .manifest = null, | |
| 982 | .manifest = undefined, | |
| 957 | 983 | .manifest_ast = undefined, |
| 984 | .have_manifest = false, | |
| 958 | 985 | .computed_hash = undefined, |
| 959 | 986 | .has_build_zig = false, |
| 960 | 987 | .oom_flag = false, |
| ... | ... | @@ -1931,7 +1958,7 @@ const Filter = struct { |
| 1931 | 1958 | include_paths: std.StringArrayHashMapUnmanaged(void) = .empty, |
| 1932 | 1959 | |
| 1933 | 1960 | /// sub_path is relative to the package root. |
| 1934 | pub fn includePath(self: Filter, sub_path: []const u8) bool { | |
| 1961 | pub fn includePath(self: *const Filter, sub_path: []const u8) bool { | |
| 1935 | 1962 | if (self.include_paths.count() == 0) return true; |
| 1936 | 1963 | if (self.include_paths.contains("")) return true; |
| 1937 | 1964 | if (self.include_paths.contains(".")) return true; |
| ... | ... | @@ -2202,206 +2229,6 @@ const UnpackResult = struct { |
| 2202 | 2229 | } |
| 2203 | 2230 | }; |
| 2204 | 2231 | |
| 2205 | test "set executable bit based on file content" { | |
| 2206 | if (!Io.File.Permissions.has_executable_bit) return error.SkipZigTest; | |
| 2207 | const gpa = std.testing.allocator; | |
| 2208 | const io = std.testing.io; | |
| 2209 | ||
| 2210 | var tmp = std.testing.tmpDir(.{}); | |
| 2211 | defer tmp.cleanup(); | |
| 2212 | ||
| 2213 | const tarball_name = "executables.tar.gz"; | |
| 2214 | try saveEmbedFile(io, tarball_name, tmp.dir); | |
| 2215 | const tarball_path = try std.fmt.allocPrint(gpa, ".zig-cache/tmp/{s}/{s}", .{ tmp.sub_path, tarball_name }); | |
| 2216 | defer gpa.free(tarball_path); | |
| 2217 | ||
| 2218 | // $ tar -tvf executables.tar.gz | |
| 2219 | // drwxrwxr-x 0 executables/ | |
| 2220 | // -rwxrwxr-x 170 executables/hello | |
| 2221 | // lrwxrwxrwx 0 executables/hello_ln -> hello | |
| 2222 | // -rw-rw-r-- 0 executables/file1 | |
| 2223 | // -rw-rw-r-- 17 executables/script_with_shebang_without_exec_bit | |
| 2224 | // -rwxrwxr-x 7 executables/script_without_shebang | |
| 2225 | // -rwxrwxr-x 17 executables/script | |
| 2226 | ||
| 2227 | var fb: TestFetchBuilder = undefined; | |
| 2228 | var fetch = try fb.build(gpa, io, tmp.dir, tarball_path); | |
| 2229 | defer fb.deinit(); | |
| 2230 | ||
| 2231 | try fetch.run(); | |
| 2232 | try std.testing.expectEqualStrings( | |
| 2233 | "1220fecb4c06a9da8673c87fe8810e15785f1699212f01728eadce094d21effeeef3", | |
| 2234 | &Package.multiHashHexDigest(fetch.computed_hash.digest), | |
| 2235 | ); | |
| 2236 | ||
| 2237 | var out = try fb.packageDir(); | |
| 2238 | defer out.close(io); | |
| 2239 | const S = std.posix.S; | |
| 2240 | // expect executable bit not set | |
| 2241 | try std.testing.expect((try out.statFile(io, "file1", .{})).permissions.toMode() & S.IXUSR == 0); | |
| 2242 | try std.testing.expect((try out.statFile(io, "script_without_shebang", .{})).permissions.toMode() & S.IXUSR == 0); | |
| 2243 | // expect executable bit set | |
| 2244 | try std.testing.expect((try out.statFile(io, "hello", .{})).permissions.toMode() & S.IXUSR != 0); | |
| 2245 | try std.testing.expect((try out.statFile(io, "script", .{})).permissions.toMode() & S.IXUSR != 0); | |
| 2246 | try std.testing.expect((try out.statFile(io, "script_with_shebang_without_exec_bit", .{})).permissions.toMode() & S.IXUSR != 0); | |
| 2247 | try std.testing.expect((try out.statFile(io, "hello_ln", .{})).permissions.toMode() & S.IXUSR != 0); | |
| 2248 | ||
| 2249 | // | |
| 2250 | // $ ls -al zig-cache/tmp/OCz9ovUcstDjTC_U/zig-global-cache/p/1220fecb4c06a9da8673c87fe8810e15785f1699212f01728eadce094d21effeeef3 | |
| 2251 | // -rw-rw-r-- 1 0 Apr file1 | |
| 2252 | // -rwxrwxr-x 1 170 Apr hello | |
| 2253 | // lrwxrwxrwx 1 5 Apr hello_ln -> hello | |
| 2254 | // -rwxrwxr-x 1 17 Apr script | |
| 2255 | // -rw-rw-r-- 1 7 Apr script_without_shebang | |
| 2256 | // -rwxrwxr-x 1 17 Apr script_with_shebang_without_exec_bit | |
| 2257 | } | |
| 2258 | ||
| 2259 | fn saveEmbedFile(io: Io, comptime tarball_name: []const u8, dir: Io.Dir) !void { | |
| 2260 | //const tarball_name = "duplicate_paths_excluded.tar.gz"; | |
| 2261 | const tarball_content = @embedFile("Fetch/testdata/" ++ tarball_name); | |
| 2262 | var tmp_file = try dir.createFile(io, tarball_name, .{}); | |
| 2263 | defer tmp_file.close(io); | |
| 2264 | try tmp_file.writeStreamingAll(io, tarball_content); | |
| 2265 | } | |
| 2266 | ||
| 2267 | // Builds Fetch with required dependencies, clears dependencies on deinit(). | |
| 2268 | const TestFetchBuilder = struct { | |
| 2269 | http_client: std.http.Client, | |
| 2270 | global_cache_directory: Cache.Directory, | |
| 2271 | local_cache_path: Cache.Path, | |
| 2272 | job_queue: Fetch.JobQueue, | |
| 2273 | fetch: Fetch, | |
| 2274 | ||
| 2275 | fn build( | |
| 2276 | self: *TestFetchBuilder, | |
| 2277 | allocator: std.mem.Allocator, | |
| 2278 | io: Io, | |
| 2279 | cache_parent_dir: std.Io.Dir, | |
| 2280 | path_or_url: []const u8, | |
| 2281 | ) !*Fetch { | |
| 2282 | const global_cache_dir = try cache_parent_dir.createDirPathOpen(io, "zig-global-cache", .{}); | |
| 2283 | const package_root_dir = try cache_parent_dir.createDirPathOpen(io, "local-project-root", .{}); | |
| 2284 | ||
| 2285 | self.http_client = .{ .allocator = allocator, .io = io }; | |
| 2286 | self.global_cache_directory = .{ .handle = global_cache_dir, .path = "zig-global-cache" }; | |
| 2287 | self.local_cache_path = .{ | |
| 2288 | .root_dir = .{ .handle = package_root_dir, .path = "local-project-root" }, | |
| 2289 | .sub_path = ".zig-cache", | |
| 2290 | }; | |
| 2291 | ||
| 2292 | self.job_queue = .{ | |
| 2293 | .io = io, | |
| 2294 | .http_client = &self.http_client, | |
| 2295 | .global_cache = self.global_cache_directory, | |
| 2296 | .local_cache = self.local_cache_path, | |
| 2297 | .root_pkg_path = .{ | |
| 2298 | .root_dir = .{ .handle = package_root_dir, .path = "local-project-root" }, | |
| 2299 | .sub_path = "zig-pkg", | |
| 2300 | }, | |
| 2301 | .recursive = false, | |
| 2302 | .read_only = false, | |
| 2303 | .debug_hash = false, | |
| 2304 | .mode = .needed, | |
| 2305 | .prog_node = std.Progress.Node.none, | |
| 2306 | }; | |
| 2307 | ||
| 2308 | self.fetch = .{ | |
| 2309 | .arena = std.heap.ArenaAllocator.init(allocator), | |
| 2310 | .location = .{ .path_or_url = path_or_url }, | |
| 2311 | .location_tok = 0, | |
| 2312 | .hash_tok = .none, | |
| 2313 | .name_tok = 0, | |
| 2314 | .lazy_status = .eager, | |
| 2315 | .parent_package_root = .{ .root_dir = .{ .handle = package_root_dir, .path = null } }, | |
| 2316 | .parent_manifest_ast = null, | |
| 2317 | .prog_node = std.Progress.Node.none, | |
| 2318 | .job_queue = &self.job_queue, | |
| 2319 | .omit_missing_hash_error = true, | |
| 2320 | .allow_missing_paths_field = false, | |
| 2321 | .use_latest_commit = true, | |
| 2322 | ||
| 2323 | .package_root = undefined, | |
| 2324 | .error_bundle = undefined, | |
| 2325 | .manifest = null, | |
| 2326 | .manifest_ast = undefined, | |
| 2327 | .computed_hash = undefined, | |
| 2328 | .has_build_zig = false, | |
| 2329 | .oom_flag = false, | |
| 2330 | .latest_commit = null, | |
| 2331 | ||
| 2332 | .module = null, | |
| 2333 | }; | |
| 2334 | return &self.fetch; | |
| 2335 | } | |
| 2336 | ||
| 2337 | fn deinit(self: *TestFetchBuilder) void { | |
| 2338 | const io = self.job_queue.io; | |
| 2339 | self.fetch.deinit(); | |
| 2340 | self.job_queue.deinit(); | |
| 2341 | self.fetch.prog_node.end(); | |
| 2342 | self.global_cache_directory.handle.close(io); | |
| 2343 | self.http_client.deinit(); | |
| 2344 | } | |
| 2345 | ||
| 2346 | fn packageDir(self: *TestFetchBuilder) !Io.Dir { | |
| 2347 | const io = self.job_queue.io; | |
| 2348 | const root = self.fetch.package_root; | |
| 2349 | return try root.root_dir.handle.openDir(io, root.sub_path, .{ .iterate = true }); | |
| 2350 | } | |
| 2351 | ||
| 2352 | // Test helper, asserts thet package dir constains expected_files. | |
| 2353 | // expected_files must be sorted. | |
| 2354 | fn expectPackageFiles(self: *TestFetchBuilder, expected_files: []const []const u8) !void { | |
| 2355 | const io = self.job_queue.io; | |
| 2356 | const gpa = std.testing.allocator; | |
| 2357 | ||
| 2358 | var package_dir = try self.packageDir(); | |
| 2359 | defer package_dir.close(io); | |
| 2360 | ||
| 2361 | var actual_files: std.ArrayList([]u8) = .empty; | |
| 2362 | defer actual_files.deinit(gpa); | |
| 2363 | defer for (actual_files.items) |file| gpa.free(file); | |
| 2364 | var walker = try package_dir.walk(gpa); | |
| 2365 | defer walker.deinit(); | |
| 2366 | while (try walker.next(io)) |entry| { | |
| 2367 | if (entry.kind != .file) continue; | |
| 2368 | const path = try gpa.dupe(u8, entry.path); | |
| 2369 | errdefer gpa.free(path); | |
| 2370 | std.mem.replaceScalar(u8, path, std.fs.path.sep, '/'); | |
| 2371 | try actual_files.append(gpa, path); | |
| 2372 | } | |
| 2373 | std.mem.sortUnstable([]u8, actual_files.items, {}, struct { | |
| 2374 | fn lessThan(_: void, a: []u8, b: []u8) bool { | |
| 2375 | return std.mem.lessThan(u8, a, b); | |
| 2376 | } | |
| 2377 | }.lessThan); | |
| 2378 | ||
| 2379 | try std.testing.expectEqual(expected_files.len, actual_files.items.len); | |
| 2380 | for (expected_files, 0..) |file_name, i| { | |
| 2381 | try std.testing.expectEqualStrings(file_name, actual_files.items[i]); | |
| 2382 | } | |
| 2383 | try std.testing.expectEqualDeep(expected_files, actual_files.items); | |
| 2384 | } | |
| 2385 | ||
| 2386 | // Test helper, asserts that fetch has failed with `msg` error message. | |
| 2387 | fn expectFetchErrors(self: *TestFetchBuilder, notes_len: usize, msg: []const u8) !void { | |
| 2388 | const gpa = std.testing.allocator; | |
| 2389 | ||
| 2390 | var errors = try self.fetch.error_bundle.toOwnedBundle(""); | |
| 2391 | defer errors.deinit(gpa); | |
| 2392 | ||
| 2393 | const em = errors.getErrorMessage(errors.getMessages()[0]); | |
| 2394 | try std.testing.expectEqual(1, em.count); | |
| 2395 | if (notes_len > 0) { | |
| 2396 | try std.testing.expectEqual(notes_len, em.notes_len); | |
| 2397 | } | |
| 2398 | var aw: Io.Writer.Allocating = .init(gpa); | |
| 2399 | defer aw.deinit(); | |
| 2400 | try errors.renderToWriter(.{}, &aw.writer); | |
| 2401 | try std.testing.expectEqualStrings(msg, aw.written()); | |
| 2402 | } | |
| 2403 | }; | |
| 2404 | ||
| 2405 | 2232 | test { |
| 2406 | 2233 | _ = Filter; |
| 2407 | 2234 | _ = FileType; |
src/Package/Fetch/testdata/executables.tar.gz deleted| Binary files a/src/Package/Fetch/testdata/executables.tar.gz and /dev/null differ |
src/Package/Manifest.zig+47-5| ... | ... | @@ -1,10 +1,13 @@ |
| 1 | 1 | const Manifest = @This(); |
| 2 | ||
| 2 | 3 | const std = @import("std"); |
| 4 | const Io = std.Io; | |
| 3 | 5 | const mem = std.mem; |
| 4 | 6 | const Allocator = std.mem.Allocator; |
| 5 | 7 | const assert = std.debug.assert; |
| 6 | 8 | const Ast = std.zig.Ast; |
| 7 | 9 | const testing = std.testing; |
| 10 | ||
| 8 | 11 | const Package = @import("../Package.zig"); |
| 9 | 12 | |
| 10 | 13 | pub const max_bytes = 10 * 1024 * 1024; |
| ... | ... | @@ -53,7 +56,7 @@ pub const ParseOptions = struct { |
| 53 | 56 | |
| 54 | 57 | pub const Error = Allocator.Error; |
| 55 | 58 | |
| 56 | pub fn parse(gpa: Allocator, ast: Ast, rng: std.Random, options: ParseOptions) Error!Manifest { | |
| 59 | pub fn parse(gpa: Allocator, ast: *const Ast, rng: std.Random, options: ParseOptions) Error!Manifest { | |
| 57 | 60 | const main_node_index = ast.nodeData(.root).node; |
| 58 | 61 | |
| 59 | 62 | var arena_instance = std.heap.ArenaAllocator.init(gpa); |
| ... | ... | @@ -61,7 +64,7 @@ pub fn parse(gpa: Allocator, ast: Ast, rng: std.Random, options: ParseOptions) E |
| 61 | 64 | |
| 62 | 65 | var p: Parse = .{ |
| 63 | 66 | .gpa = gpa, |
| 64 | .ast = ast, | |
| 67 | .ast = ast.*, | |
| 65 | 68 | .arena = arena_instance.allocator(), |
| 66 | 69 | .errors = .{}, |
| 67 | 70 | |
| ... | ... | @@ -578,6 +581,45 @@ const Parse = struct { |
| 578 | 581 | } |
| 579 | 582 | }; |
| 580 | 583 | |
| 584 | pub fn load( | |
| 585 | io: Io, | |
| 586 | arena: Allocator, | |
| 587 | manifest_path: std.Build.Cache.Path, | |
| 588 | ast: *std.zig.Ast, | |
| 589 | error_bundle: *std.zig.ErrorBundle.Wip, | |
| 590 | manifest: *Manifest, | |
| 591 | allow_missing_paths_field: bool, | |
| 592 | ) !void { | |
| 593 | const manifest_bytes = try manifest_path.root_dir.handle.readFileAllocOptions( | |
| 594 | io, | |
| 595 | manifest_path.sub_path, | |
| 596 | arena, | |
| 597 | .limited(max_bytes), | |
| 598 | .@"1", | |
| 599 | 0, | |
| 600 | ); | |
| 601 | ||
| 602 | ast.* = try std.zig.Ast.parse(arena, manifest_bytes, .zon); | |
| 603 | ||
| 604 | if (ast.errors.len > 0) { | |
| 605 | const file_path = try manifest_path.joinString(arena, ""); | |
| 606 | try std.zig.putAstErrorsIntoBundle(arena, ast.*, file_path, error_bundle); | |
| 607 | return error.ErrorsBundled; | |
| 608 | } | |
| 609 | ||
| 610 | const rng: std.Random.IoSource = .{ .io = io }; | |
| 611 | ||
| 612 | manifest.* = try parse(arena, ast, rng.interface(), .{ | |
| 613 | .allow_missing_paths_field = allow_missing_paths_field, | |
| 614 | }); | |
| 615 | ||
| 616 | if (manifest.errors.len > 0) { | |
| 617 | const src_path = try error_bundle.printString("{f}", .{manifest_path}); | |
| 618 | try manifest.copyErrorsIntoBundle(ast.*, src_path, error_bundle); | |
| 619 | return error.ErrorsBundled; | |
| 620 | } | |
| 621 | } | |
| 622 | ||
| 581 | 623 | test "basic" { |
| 582 | 624 | const gpa = testing.allocator; |
| 583 | 625 | |
| ... | ... | @@ -603,7 +645,7 @@ test "basic" { |
| 603 | 645 | |
| 604 | 646 | var rng = std.Random.DefaultPrng.init(0); |
| 605 | 647 | |
| 606 | var manifest = try Manifest.parse(gpa, ast, rng.random(), .{}); | |
| 648 | var manifest = try Manifest.parse(gpa, &ast, rng.random(), .{}); | |
| 607 | 649 | defer manifest.deinit(gpa); |
| 608 | 650 | |
| 609 | 651 | try testing.expect(manifest.errors.len == 0); |
| ... | ... | @@ -649,7 +691,7 @@ test "minimum_zig_version" { |
| 649 | 691 | |
| 650 | 692 | var rng = std.Random.DefaultPrng.init(0); |
| 651 | 693 | |
| 652 | var manifest = try Manifest.parse(gpa, ast, rng.random(), .{}); | |
| 694 | var manifest = try Manifest.parse(gpa, &ast, rng.random(), .{}); | |
| 653 | 695 | defer manifest.deinit(gpa); |
| 654 | 696 | |
| 655 | 697 | try testing.expect(manifest.errors.len == 0); |
| ... | ... | @@ -684,7 +726,7 @@ test "minimum_zig_version - invalid version" { |
| 684 | 726 | |
| 685 | 727 | var rng = std.Random.DefaultPrng.init(0); |
| 686 | 728 | |
| 687 | var manifest = try Manifest.parse(gpa, ast, rng.random(), .{}); | |
| 729 | var manifest = try Manifest.parse(gpa, &ast, rng.random(), .{}); | |
| 688 | 730 | defer manifest.deinit(gpa); |
| 689 | 731 | |
| 690 | 732 | try testing.expect(manifest.errors.len == 1); |
src/main.zig+141-20| ... | ... | @@ -4896,7 +4896,8 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, io: Io, args: []const []const u8, |
| 4896 | 4896 | var override_global_cache_dir: ?[]const u8 = EnvVar.ZIG_GLOBAL_CACHE_DIR.get(environ_map); |
| 4897 | 4897 | var override_local_cache_dir: ?[]const u8 = EnvVar.ZIG_LOCAL_CACHE_DIR.get(environ_map); |
| 4898 | 4898 | var override_build_runner: ?[]const u8 = EnvVar.ZIG_BUILD_RUNNER.get(environ_map); |
| 4899 | var child_argv = std.array_list.Managed([]const u8).init(arena); | |
| 4899 | var child_argv: std.ArrayList([]const u8) = .empty; | |
| 4900 | var forks: std.ArrayList(Fork) = .empty; | |
| 4900 | 4901 | var reference_trace: ?u32 = null; |
| 4901 | 4902 | var debug_compile_errors = false; |
| 4902 | 4903 | var verbose_link = (native_os != .wasi or builtin.link_libc) and |
| ... | ... | @@ -4917,24 +4918,24 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, io: Io, args: []const []const u8, |
| 4917 | 4918 | var debug_libc_paths_file: ?[]const u8 = null; |
| 4918 | 4919 | |
| 4919 | 4920 | const argv_index_exe = child_argv.items.len; |
| 4920 | _ = try child_argv.addOne(); | |
| 4921 | _ = try child_argv.addOne(arena); | |
| 4921 | 4922 | |
| 4922 | 4923 | const self_exe_path = try process.executablePathAlloc(io, arena); |
| 4923 | try child_argv.append(self_exe_path); | |
| 4924 | try child_argv.append(arena, self_exe_path); | |
| 4924 | 4925 | |
| 4925 | 4926 | const argv_index_zig_lib_dir = child_argv.items.len; |
| 4926 | _ = try child_argv.addOne(); | |
| 4927 | _ = try child_argv.addOne(arena); | |
| 4927 | 4928 | |
| 4928 | 4929 | const argv_index_build_file = child_argv.items.len; |
| 4929 | _ = try child_argv.addOne(); | |
| 4930 | _ = try child_argv.addOne(arena); | |
| 4930 | 4931 | |
| 4931 | 4932 | const argv_index_cache_dir = child_argv.items.len; |
| 4932 | _ = try child_argv.addOne(); | |
| 4933 | _ = try child_argv.addOne(arena); | |
| 4933 | 4934 | |
| 4934 | 4935 | const argv_index_global_cache_dir = child_argv.items.len; |
| 4935 | _ = try child_argv.addOne(); | |
| 4936 | _ = try child_argv.addOne(arena); | |
| 4936 | 4937 | |
| 4937 | try child_argv.appendSlice(&.{ | |
| 4938 | try child_argv.appendSlice(arena, &.{ | |
| 4938 | 4939 | "--seed", |
| 4939 | 4940 | try std.fmt.allocPrint(arena, "0x{x}", .{randInt(io, u32)}), |
| 4940 | 4941 | }); |
| ... | ... | @@ -4955,7 +4956,7 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, io: Io, args: []const []const u8, |
| 4955 | 4956 | // read this file in the parent to obtain the results, in the case the child |
| 4956 | 4957 | // exits with code 3. |
| 4957 | 4958 | const results_tmp_file_nonce = std.fmt.hex(randInt(io, u64)); |
| 4958 | try child_argv.append("-Z" ++ results_tmp_file_nonce); | |
| 4959 | try child_argv.append(arena, "-Z" ++ results_tmp_file_nonce); | |
| 4959 | 4960 | |
| 4960 | 4961 | var color: Color = .auto; |
| 4961 | 4962 | var n_jobs: ?u32 = null; |
| ... | ... | @@ -5000,11 +5001,24 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, io: Io, args: []const []const u8, |
| 5000 | 5001 | fatal("expected [needed|all] after '--fetch=', found '{s}'", .{ |
| 5001 | 5002 | sub_arg, |
| 5002 | 5003 | }); |
| 5004 | } else if (mem.cutPrefix(u8, arg, "--fork=")) |sub_arg| { | |
| 5005 | try forks.append(arena, .{ | |
| 5006 | .manifest_ast = undefined, | |
| 5007 | .manifest = undefined, | |
| 5008 | .error_bundle = undefined, | |
| 5009 | .arena_allocator = undefined, | |
| 5010 | .path = .{ | |
| 5011 | .root_dir = .cwd(), | |
| 5012 | .sub_path = sub_arg, | |
| 5013 | }, | |
| 5014 | .failed = false, | |
| 5015 | }); | |
| 5016 | continue; | |
| 5003 | 5017 | } else if (mem.eql(u8, arg, "--system")) { |
| 5004 | 5018 | if (i + 1 >= args.len) fatal("expected argument after '{s}'", .{arg}); |
| 5005 | 5019 | i += 1; |
| 5006 | 5020 | system_pkg_dir_path = args[i]; |
| 5007 | try child_argv.append("--system"); | |
| 5021 | try child_argv.append(arena, "--system"); | |
| 5008 | 5022 | continue; |
| 5009 | 5023 | } else if (mem.cutPrefix(u8, arg, "-freference-trace=")) |num| { |
| 5010 | 5024 | reference_trace = std.fmt.parseUnsigned(u32, num, 10) catch |err| { |
| ... | ... | @@ -5014,7 +5028,7 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, io: Io, args: []const []const u8, |
| 5014 | 5028 | reference_trace = null; |
| 5015 | 5029 | } else if (mem.eql(u8, arg, "--debug-log")) { |
| 5016 | 5030 | if (i + 1 >= args.len) fatal("expected argument after '{s}'", .{arg}); |
| 5017 | try child_argv.appendSlice(args[i .. i + 2]); | |
| 5031 | try child_argv.appendSlice(arena, args[i .. i + 2]); | |
| 5018 | 5032 | i += 1; |
| 5019 | 5033 | if (!build_options.enable_logging) { |
| 5020 | 5034 | warn("Zig was compiled without logging enabled (-Dlog). --debug-log has no effect.", .{}); |
| ... | ... | @@ -5070,7 +5084,7 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, io: Io, args: []const []const u8, |
| 5070 | 5084 | color = std.meta.stringToEnum(Color, args[i]) orelse { |
| 5071 | 5085 | fatal("expected [auto|on|off] after {s}, found '{s}'", .{ arg, args[i] }); |
| 5072 | 5086 | }; |
| 5073 | try child_argv.appendSlice(&.{ arg, args[i] }); | |
| 5087 | try child_argv.appendSlice(arena, &.{ arg, args[i] }); | |
| 5074 | 5088 | continue; |
| 5075 | 5089 | } else if (mem.cutPrefix(u8, arg, "-j")) |str| { |
| 5076 | 5090 | const num = std.fmt.parseUnsigned(u32, str, 10) catch |err| { |
| ... | ... | @@ -5090,11 +5104,11 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, io: Io, args: []const []const u8, |
| 5090 | 5104 | } else if (mem.eql(u8, arg, "--")) { |
| 5091 | 5105 | // The rest of the args are supposed to get passed onto |
| 5092 | 5106 | // build runner's `build.args` |
| 5093 | try child_argv.appendSlice(args[i..]); | |
| 5107 | try child_argv.appendSlice(arena, args[i..]); | |
| 5094 | 5108 | break; |
| 5095 | 5109 | } |
| 5096 | 5110 | } |
| 5097 | try child_argv.append(arg); | |
| 5111 | try child_argv.append(arena, arg); | |
| 5098 | 5112 | } |
| 5099 | 5113 | } |
| 5100 | 5114 | |
| ... | ... | @@ -5182,6 +5196,29 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, io: Io, args: []const []const u8, |
| 5182 | 5196 | defer http_client.deinit(); |
| 5183 | 5197 | |
| 5184 | 5198 | var unlazy_set: Package.Fetch.JobQueue.UnlazySet = .{}; |
| 5199 | var fork_set: Package.Fetch.JobQueue.ForkSet = .{}; | |
| 5200 | ||
| 5201 | { | |
| 5202 | // Populate fork_set. | |
| 5203 | var group: Io.Group = .init; | |
| 5204 | defer group.cancel(io); | |
| 5205 | ||
| 5206 | for (forks.items) |*fork| | |
| 5207 | group.async(io, Fork.load, .{ io, gpa, fork, color }); | |
| 5208 | ||
| 5209 | try group.await(io); | |
| 5210 | ||
| 5211 | for (forks.items) |*fork| { | |
| 5212 | if (fork.failed) process.exit(1); | |
| 5213 | try fork_set.put(arena, .{ | |
| 5214 | .path = fork.path, | |
| 5215 | .manifest_ast = fork.manifest_ast, | |
| 5216 | .manifest = fork.manifest, | |
| 5217 | .uses = 0, | |
| 5218 | }, {}); | |
| 5219 | } | |
| 5220 | } | |
| 5221 | defer Fork.deinitList(forks.items); | |
| 5185 | 5222 | |
| 5186 | 5223 | // This loop is re-evaluated when the build script exits with an indication that it |
| 5187 | 5224 | // could not continue due to missing lazy dependencies. |
| ... | ... | @@ -5235,6 +5272,9 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, io: Io, args: []const []const u8, |
| 5235 | 5272 | const fetch_prog_node = root_prog_node.start("Fetch Packages", 0); |
| 5236 | 5273 | defer fetch_prog_node.end(); |
| 5237 | 5274 | |
| 5275 | // Reset fork match counts. | |
| 5276 | for (fork_set.keys()) |*fork| fork.uses = 0; | |
| 5277 | ||
| 5238 | 5278 | var job_queue: Package.Fetch.JobQueue = .{ |
| 5239 | 5279 | .io = io, |
| 5240 | 5280 | .http_client = &http_client, |
| ... | ... | @@ -5245,6 +5285,7 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, io: Io, args: []const []const u8, |
| 5245 | 5285 | .recursive = true, |
| 5246 | 5286 | .debug_hash = false, |
| 5247 | 5287 | .unlazy_set = unlazy_set, |
| 5288 | .fork_set = fork_set, | |
| 5248 | 5289 | .mode = fetch_mode, |
| 5249 | 5290 | .prog_node = fetch_prog_node, |
| 5250 | 5291 | }; |
| ... | ... | @@ -5290,8 +5331,9 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, io: Io, args: []const []const u8, |
| 5290 | 5331 | |
| 5291 | 5332 | .package_root = undefined, |
| 5292 | 5333 | .error_bundle = undefined, |
| 5293 | .manifest = null, | |
| 5334 | .manifest = undefined, | |
| 5294 | 5335 | .manifest_ast = undefined, |
| 5336 | .have_manifest = false, | |
| 5295 | 5337 | .computed_hash = undefined, |
| 5296 | 5338 | .has_build_zig = true, |
| 5297 | 5339 | .oom_flag = false, |
| ... | ... | @@ -5309,6 +5351,26 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, io: Io, args: []const []const u8, |
| 5309 | 5351 | job_queue.group.async(io, Package.Fetch.workerRun, .{ &fetch, "root" }); |
| 5310 | 5352 | try job_queue.group.await(io); |
| 5311 | 5353 | |
| 5354 | { | |
| 5355 | // Ensure that forks were actually used. This is done | |
| 5356 | // before printing manifest errors because using a fork can | |
| 5357 | // prevent them. | |
| 5358 | var any_unused = false; | |
| 5359 | for (fork_set.keys()) |*fork| { | |
| 5360 | if (fork.uses == 0) { | |
| 5361 | std.log.err("fork {f} matched no {s} packages", .{ | |
| 5362 | fork.path, fork.manifest.name, | |
| 5363 | }); | |
| 5364 | any_unused = true; | |
| 5365 | } else { | |
| 5366 | std.log.info("fork {f} matched {d} {s} packages", .{ | |
| 5367 | fork.path, fork.uses, fork.manifest.name, | |
| 5368 | }); | |
| 5369 | } | |
| 5370 | } | |
| 5371 | if (any_unused) process.exit(1); | |
| 5372 | } | |
| 5373 | ||
| 5312 | 5374 | try job_queue.consolidateErrors(); |
| 5313 | 5375 | |
| 5314 | 5376 | if (fetch.error_bundle.root_list.items.len > 0) { |
| ... | ... | @@ -5369,7 +5431,8 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, io: Io, args: []const []const u8, |
| 5369 | 5431 | // dependencies' build.zig modules by name. |
| 5370 | 5432 | for (fetches) |f| { |
| 5371 | 5433 | const mod = f.module orelse continue; |
| 5372 | const man = f.manifest orelse continue; | |
| 5434 | if (!f.have_manifest) continue; | |
| 5435 | const man = &f.manifest; | |
| 5373 | 5436 | const dep_names = man.dependencies.keys(); |
| 5374 | 5437 | try mod.deps.ensureUnusedCapacity(arena, @intCast(dep_names.len)); |
| 5375 | 5438 | for (dep_names, man.dependencies.values()) |name, dep| { |
| ... | ... | @@ -5518,6 +5581,63 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, io: Io, args: []const []const u8, |
| 5518 | 5581 | } |
| 5519 | 5582 | } |
| 5520 | 5583 | |
| 5584 | const Fork = struct { | |
| 5585 | path: Path, | |
| 5586 | manifest_ast: std.zig.Ast, | |
| 5587 | manifest: Package.Manifest, | |
| 5588 | error_bundle: std.zig.ErrorBundle.Wip, | |
| 5589 | failed: bool, | |
| 5590 | arena_allocator: std.heap.ArenaAllocator, | |
| 5591 | ||
| 5592 | fn load(io: Io, gpa: Allocator, fork: *Fork, color: Color) Io.Cancelable!void { | |
| 5593 | loadFallible(io, gpa, fork, color) catch |err| switch (err) { | |
| 5594 | error.Canceled => |e| return e, | |
| 5595 | error.AlreadyReported => fork.failed = true, | |
| 5596 | else => |e| { | |
| 5597 | std.log.err("failed to load fork at {f}: {t}", .{ fork.path, e }); | |
| 5598 | fork.failed = true; | |
| 5599 | }, | |
| 5600 | }; | |
| 5601 | } | |
| 5602 | ||
| 5603 | fn loadFallible(io: Io, gpa: Allocator, fork: *Fork, color: Color) !void { | |
| 5604 | fork.arena_allocator = .init(gpa); | |
| 5605 | const arena = fork.arena_allocator.allocator(); | |
| 5606 | ||
| 5607 | var error_bundle: std.zig.ErrorBundle.Wip = undefined; | |
| 5608 | try error_bundle.init(gpa); | |
| 5609 | defer error_bundle.deinit(); | |
| 5610 | ||
| 5611 | const manifest_path = try fork.path.join(arena, Package.Manifest.basename); | |
| 5612 | ||
| 5613 | Package.Manifest.load( | |
| 5614 | io, | |
| 5615 | arena, | |
| 5616 | manifest_path, | |
| 5617 | &fork.manifest_ast, | |
| 5618 | &error_bundle, | |
| 5619 | &fork.manifest, | |
| 5620 | true, | |
| 5621 | ) catch |err| switch (err) { | |
| 5622 | error.Canceled => |e| return e, | |
| 5623 | error.ErrorsBundled => { | |
| 5624 | assert(error_bundle.root_list.items.len > 0); | |
| 5625 | var errors = try error_bundle.toOwnedBundle(""); | |
| 5626 | errors.renderToStderr(io, .{}, color) catch {}; | |
| 5627 | return error.AlreadyReported; | |
| 5628 | }, | |
| 5629 | else => |e| { | |
| 5630 | std.log.err("failed to load package manifest {f}: {t}", .{ manifest_path, e }); | |
| 5631 | return error.AlreadyReported; | |
| 5632 | }, | |
| 5633 | }; | |
| 5634 | } | |
| 5635 | ||
| 5636 | fn deinitList(forks: []Fork) void { | |
| 5637 | for (forks) |*fork| fork.arena_allocator.deinit(); | |
| 5638 | } | |
| 5639 | }; | |
| 5640 | ||
| 5521 | 5641 | const JitCmdOptions = struct { |
| 5522 | 5642 | cmd_name: []const u8, |
| 5523 | 5643 | root_src_path: []const u8, |
| ... | ... | @@ -7048,8 +7168,9 @@ fn cmdFetch( |
| 7048 | 7168 | |
| 7049 | 7169 | .package_root = undefined, |
| 7050 | 7170 | .error_bundle = undefined, |
| 7051 | .manifest = null, | |
| 7171 | .manifest = undefined, | |
| 7052 | 7172 | .manifest_ast = undefined, |
| 7173 | .have_manifest = false, | |
| 7053 | 7174 | .computed_hash = undefined, |
| 7054 | 7175 | .has_build_zig = false, |
| 7055 | 7176 | .oom_flag = false, |
| ... | ... | @@ -7085,9 +7206,9 @@ fn cmdFetch( |
| 7085 | 7206 | }, |
| 7086 | 7207 | .yes, .exact => |name| name: { |
| 7087 | 7208 | if (name) |n| break :name n; |
| 7088 | const fetched_manifest = fetch.manifest orelse | |
| 7209 | if (!fetch.have_manifest) | |
| 7089 | 7210 | fatal("unable to determine name; fetched package has no build.zig.zon file", .{}); |
| 7090 | break :name fetched_manifest.name; | |
| 7211 | break :name fetch.manifest.name; | |
| 7091 | 7212 | }, |
| 7092 | 7213 | }; |
| 7093 | 7214 | |
| ... | ... | @@ -7410,7 +7531,7 @@ fn loadManifest( |
| 7410 | 7531 | process.exit(2); |
| 7411 | 7532 | } |
| 7412 | 7533 | |
| 7413 | var manifest = try Package.Manifest.parse(gpa, ast, rng.interface(), .{}); | |
| 7534 | var manifest = try Package.Manifest.parse(gpa, &ast, rng.interface(), .{}); | |
| 7414 | 7535 | errdefer manifest.deinit(gpa); |
| 7415 | 7536 | |
| 7416 | 7537 | if (manifest.errors.len > 0) { |