authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-02-04 17:40:54-08:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-02-05 16:50:41-08:00
logdf64a3a36815fce6cc8671d047e52795655b3b9b
tree7df6db2ef13dc4fee6a0691a383f0c829ec62a7b
parent64dc1cdad8faf1fd8330d4b0f9149817b5205abf

build: packages now require fingerprint

also the name must be an enum literal. delete some .tar.gz test data. Test data should be in text form when it can be, and this could definitely be.

6 files changed, 7 insertions(+), 163 deletions(-)

src/Package/Fetch.zig-132
...@@ -60,8 +60,6 @@ omit_missing_hash_error: bool,...@@ -60,8 +60,6 @@ omit_missing_hash_error: bool,
60/// which specifies inclusion rules. This is intended to be true for the first60/// which specifies inclusion rules. This is intended to be true for the first
61/// fetch task and false for the recursive dependencies.61/// fetch task and false for the recursive dependencies.
62allow_missing_paths_field: bool,62allow_missing_paths_field: bool,
63allow_missing_fingerprint: bool,
64allow_name_string: bool,
65/// If true and URL points to a Git repository, will use the latest commit.63/// If true and URL points to a Git repository, will use the latest commit.
66use_latest_commit: bool,64use_latest_commit: bool,
6765
...@@ -675,8 +673,6 @@ fn loadManifest(f: *Fetch, pkg_root: Cache.Path) RunError!void {...@@ -675,8 +673,6 @@ fn loadManifest(f: *Fetch, pkg_root: Cache.Path) RunError!void {
675673
676 f.manifest = try Manifest.parse(arena, ast.*, rng.interface(), .{674 f.manifest = try Manifest.parse(arena, ast.*, rng.interface(), .{
677 .allow_missing_paths_field = f.allow_missing_paths_field,675 .allow_missing_paths_field = f.allow_missing_paths_field,
678 .allow_missing_fingerprint = f.allow_missing_fingerprint,
679 .allow_name_string = f.allow_name_string,
680 });676 });
681 const manifest = &f.manifest.?;677 const manifest = &f.manifest.?;
682678
...@@ -794,8 +790,6 @@ fn queueJobsForDeps(f: *Fetch) RunError!void {...@@ -794,8 +790,6 @@ fn queueJobsForDeps(f: *Fetch) RunError!void {
794 .job_queue = f.job_queue,790 .job_queue = f.job_queue,
795 .omit_missing_hash_error = false,791 .omit_missing_hash_error = false,
796 .allow_missing_paths_field = true,792 .allow_missing_paths_field = true,
797 .allow_missing_fingerprint = true,
798 .allow_name_string = true,
799 .use_latest_commit = false,793 .use_latest_commit = false,
800794
801 .package_root = undefined,795 .package_root = undefined,
...@@ -2049,130 +2043,6 @@ const UnpackResult = struct {...@@ -2049,130 +2043,6 @@ const UnpackResult = struct {
2049 }2043 }
2050};2044};
20512045
2052test "tarball with duplicate paths" {
2053 // This tarball has duplicate path 'dir1/file1' to simulate case sensitve
2054 // file system on any file sytstem.
2055 //
2056 // duplicate_paths/
2057 // duplicate_paths/dir1/
2058 // duplicate_paths/dir1/file1
2059 // duplicate_paths/dir1/file1
2060 // duplicate_paths/build.zig.zon
2061 // duplicate_paths/src/
2062 // duplicate_paths/src/main.zig
2063 // duplicate_paths/src/root.zig
2064 // duplicate_paths/build.zig
2065 //
2066
2067 const gpa = std.testing.allocator;
2068 const io = std.testing.io;
2069 var tmp = std.testing.tmpDir(.{});
2070 defer tmp.cleanup();
2071
2072 const tarball_name = "duplicate_paths.tar.gz";
2073 try saveEmbedFile(io, tarball_name, tmp.dir);
2074 const tarball_path = try std.fmt.allocPrint(gpa, ".zig-cache/tmp/{s}/{s}", .{ tmp.sub_path, tarball_name });
2075 defer gpa.free(tarball_path);
2076
2077 // Run tarball fetch, expect to fail
2078 var fb: TestFetchBuilder = undefined;
2079 var fetch = try fb.build(gpa, io, tmp.dir, tarball_path);
2080 defer fb.deinit();
2081 try std.testing.expectError(error.FetchFailed, fetch.run());
2082
2083 try fb.expectFetchErrors(1,
2084 \\error: unable to unpack tarball
2085 \\ note: unable to create file 'dir1/file1': PathAlreadyExists
2086 \\
2087 );
2088}
2089
2090test "tarball with excluded duplicate paths" {
2091 // Same as previous tarball but has build.zig.zon wich excludes 'dir1'.
2092 //
2093 // .paths = .{
2094 // "build.zig",
2095 // "build.zig.zon",
2096 // "src",
2097 // }
2098 //
2099
2100 const gpa = std.testing.allocator;
2101 const io = std.testing.io;
2102 var tmp = std.testing.tmpDir(.{});
2103 defer tmp.cleanup();
2104
2105 const tarball_name = "duplicate_paths_excluded.tar.gz";
2106 try saveEmbedFile(io, tarball_name, tmp.dir);
2107 const tarball_path = try std.fmt.allocPrint(gpa, ".zig-cache/tmp/{s}/{s}", .{ tmp.sub_path, tarball_name });
2108 defer gpa.free(tarball_path);
2109
2110 // Run tarball fetch, should succeed
2111 var fb: TestFetchBuilder = undefined;
2112 var fetch = try fb.build(gpa, io, tmp.dir, tarball_path);
2113 defer fb.deinit();
2114 try fetch.run();
2115
2116 const hex_digest = Package.multiHashHexDigest(fetch.computed_hash.digest);
2117 try std.testing.expectEqualStrings(
2118 "12200bafe035cbb453dd717741b66e9f9d1e6c674069d06121dafa1b2e62eb6b22da",
2119 &hex_digest,
2120 );
2121
2122 const expected_files: []const []const u8 = &.{
2123 "build.zig",
2124 "build.zig.zon",
2125 "src/main.zig",
2126 "src/root.zig",
2127 };
2128 try fb.expectPackageFiles(expected_files);
2129}
2130
2131test "tarball without root folder" {
2132 // Tarball with root folder. Manifest excludes dir1 and dir2.
2133 //
2134 // build.zig
2135 // build.zig.zon
2136 // dir1/
2137 // dir1/file2
2138 // dir1/file1
2139 // dir2/
2140 // dir2/file2
2141 // src/
2142 // src/main.zig
2143 //
2144
2145 const gpa = std.testing.allocator;
2146 const io = std.testing.io;
2147
2148 var tmp = std.testing.tmpDir(.{});
2149 defer tmp.cleanup();
2150
2151 const tarball_name = "no_root.tar.gz";
2152 try saveEmbedFile(io, tarball_name, tmp.dir);
2153 const tarball_path = try std.fmt.allocPrint(gpa, ".zig-cache/tmp/{s}/{s}", .{ tmp.sub_path, tarball_name });
2154 defer gpa.free(tarball_path);
2155
2156 // Run tarball fetch, should succeed
2157 var fb: TestFetchBuilder = undefined;
2158 var fetch = try fb.build(gpa, io, tmp.dir, tarball_path);
2159 defer fb.deinit();
2160 try fetch.run();
2161
2162 const hex_digest = Package.multiHashHexDigest(fetch.computed_hash.digest);
2163 try std.testing.expectEqualStrings(
2164 "12209f939bfdcb8b501a61bb4a43124dfa1b2848adc60eec1e4624c560357562b793",
2165 &hex_digest,
2166 );
2167
2168 const expected_files: []const []const u8 = &.{
2169 "build.zig",
2170 "build.zig.zon",
2171 "src/main.zig",
2172 };
2173 try fb.expectPackageFiles(expected_files);
2174}
2175
2176test "set executable bit based on file content" {2046test "set executable bit based on file content" {
2177 if (!Io.File.Permissions.has_executable_bit) return error.SkipZigTest;2047 if (!Io.File.Permissions.has_executable_bit) return error.SkipZigTest;
2178 const gpa = std.testing.allocator;2048 const gpa = std.testing.allocator;
...@@ -2288,8 +2158,6 @@ const TestFetchBuilder = struct {...@@ -2288,8 +2158,6 @@ const TestFetchBuilder = struct {
2288 .job_queue = &self.job_queue,2158 .job_queue = &self.job_queue,
2289 .omit_missing_hash_error = true,2159 .omit_missing_hash_error = true,
2290 .allow_missing_paths_field = false,2160 .allow_missing_paths_field = false,
2291 .allow_missing_fingerprint = true, // so we can keep using the old testdata .tar.gz
2292 .allow_name_string = true, // so we can keep using the old testdata .tar.gz
2293 .use_latest_commit = true,2161 .use_latest_commit = true,
22942162
2295 .package_root = undefined,2163 .package_root = undefined,
src/Package/Fetch/testdata/duplicate_paths.tar.gz deleted
Binary files a/src/Package/Fetch/testdata/duplicate_paths.tar.gz and /dev/null differ
src/Package/Fetch/testdata/duplicate_paths_excluded.tar.gz deleted
Binary files a/src/Package/Fetch/testdata/duplicate_paths_excluded.tar.gz and /dev/null differ
src/Package/Fetch/testdata/no_root.tar.gz deleted
Binary files a/src/Package/Fetch/testdata/no_root.tar.gz and /dev/null differ
src/Package/Manifest.zig+7-27
...@@ -49,10 +49,6 @@ arena_state: std.heap.ArenaAllocator.State,...@@ -49,10 +49,6 @@ arena_state: std.heap.ArenaAllocator.State,
4949
50pub const ParseOptions = struct {50pub const ParseOptions = struct {
51 allow_missing_paths_field: bool = false,51 allow_missing_paths_field: bool = false,
52 /// Deprecated, to be removed after 0.14.0 is tagged.
53 allow_name_string: bool = true,
54 /// Deprecated, to be removed after 0.14.0 is tagged.
55 allow_missing_fingerprint: bool = true,
56};52};
5753
58pub const Error = Allocator.Error;54pub const Error = Allocator.Error;
...@@ -77,8 +73,6 @@ pub fn parse(gpa: Allocator, ast: Ast, rng: std.Random, options: ParseOptions) E...@@ -77,8 +73,6 @@ pub fn parse(gpa: Allocator, ast: Ast, rng: std.Random, options: ParseOptions) E
77 .dependencies_node = .none,73 .dependencies_node = .none,
78 .paths = .{},74 .paths = .{},
79 .allow_missing_paths_field = options.allow_missing_paths_field,75 .allow_missing_paths_field = options.allow_missing_paths_field,
80 .allow_name_string = options.allow_name_string,
81 .allow_missing_fingerprint = options.allow_missing_fingerprint,
82 .minimum_zig_version = null,76 .minimum_zig_version = null,
83 .buf = .{},77 .buf = .{},
84 };78 };
...@@ -151,8 +145,6 @@ const Parse = struct {...@@ -151,8 +145,6 @@ const Parse = struct {
151 dependencies_node: Ast.Node.OptionalIndex,145 dependencies_node: Ast.Node.OptionalIndex,
152 paths: std.StringArrayHashMapUnmanaged(void),146 paths: std.StringArrayHashMapUnmanaged(void),
153 allow_missing_paths_field: bool,147 allow_missing_paths_field: bool,
154 allow_name_string: bool,
155 allow_missing_fingerprint: bool,
156 minimum_zig_version: ?std.SemanticVersion,148 minimum_zig_version: ?std.SemanticVersion,
157149
158 const InnerError = error{ ParseFailure, OutOfMemory };150 const InnerError = error{ ParseFailure, OutOfMemory };
...@@ -221,12 +213,10 @@ const Parse = struct {...@@ -221,12 +213,10 @@ const Parse = struct {
221 });213 });
222 }214 }
223 p.id = n.id;215 p.id = n.id;
224 } else if (!p.allow_missing_fingerprint) {216 } else {
225 try appendError(p, main_token, "missing top-level 'fingerprint' field; suggested value: 0x{x}", .{217 try appendError(p, main_token, "missing top-level 'fingerprint' field; suggested value: 0x{x}", .{
226 Package.Fingerprint.generate(rng, p.name).int(),218 Package.Fingerprint.generate(rng, p.name).int(),
227 });219 });
228 } else {
229 p.id = 0;
230 }220 }
231 }221 }
232222
...@@ -395,19 +385,6 @@ const Parse = struct {...@@ -395,19 +385,6 @@ const Parse = struct {
395 const ast = p.ast;385 const ast = p.ast;
396 const main_token = ast.nodeMainToken(node);386 const main_token = ast.nodeMainToken(node);
397387
398 if (p.allow_name_string and ast.nodeTag(node) == .string_literal) {
399 const name = try parseString(p, node);
400 if (!std.zig.isValidId(name))
401 return fail(p, main_token, "name must be a valid bare zig identifier (hint: switch from string to enum literal)", .{});
402
403 if (name.len > max_name_len)
404 return fail(p, main_token, "name '{f}' exceeds max length of {d}", .{
405 std.zig.fmtId(name), max_name_len,
406 });
407
408 return name;
409 }
410
411 if (ast.nodeTag(node) != .enum_literal)388 if (ast.nodeTag(node) != .enum_literal)
412 return fail(p, main_token, "expected enum literal", .{});389 return fail(p, main_token, "expected enum literal", .{});
413390
...@@ -606,7 +583,8 @@ test "basic" {...@@ -606,7 +583,8 @@ test "basic" {
606583
607 const example =584 const example =
608 \\.{585 \\.{
609 \\ .name = "foo",586 \\ .name = .foo,
587 \\ .fingerprint = 0x8c736521490b23df,
610 \\ .version = "3.2.1",588 \\ .version = "3.2.1",
611 \\ .paths = .{""},589 \\ .paths = .{""},
612 \\ .dependencies = .{590 \\ .dependencies = .{
...@@ -656,7 +634,8 @@ test "minimum_zig_version" {...@@ -656,7 +634,8 @@ test "minimum_zig_version" {
656634
657 const example =635 const example =
658 \\.{636 \\.{
659 \\ .name = "foo",637 \\ .name = .foo,
638 \\ .fingerprint = 0x8c736521490b23df,
660 \\ .version = "3.2.1",639 \\ .version = "3.2.1",
661 \\ .paths = .{""},640 \\ .paths = .{""},
662 \\ .minimum_zig_version = "0.11.1",641 \\ .minimum_zig_version = "0.11.1",
...@@ -690,7 +669,8 @@ test "minimum_zig_version - invalid version" {...@@ -690,7 +669,8 @@ test "minimum_zig_version - invalid version" {
690669
691 const example =670 const example =
692 \\.{671 \\.{
693 \\ .name = "foo",672 \\ .name = .foo,
673 \\ .fingerprint = 0x8c736521490b23df,
694 \\ .version = "3.2.1",674 \\ .version = "3.2.1",
695 \\ .minimum_zig_version = "X.11.1",675 \\ .minimum_zig_version = "X.11.1",
696 \\ .paths = .{""},676 \\ .paths = .{""},
src/main.zig-4
...@@ -5285,8 +5285,6 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, io: Io, args: []const []const u8,...@@ -5285,8 +5285,6 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, io: Io, args: []const []const u8,
5285 .job_queue = &job_queue,5285 .job_queue = &job_queue,
5286 .omit_missing_hash_error = true,5286 .omit_missing_hash_error = true,
5287 .allow_missing_paths_field = false,5287 .allow_missing_paths_field = false,
5288 .allow_missing_fingerprint = false,
5289 .allow_name_string = false,
5290 .use_latest_commit = false,5288 .use_latest_commit = false,
52915289
5292 .package_root = undefined,5290 .package_root = undefined,
...@@ -7044,8 +7042,6 @@ fn cmdFetch(...@@ -7044,8 +7042,6 @@ fn cmdFetch(
7044 .job_queue = &job_queue,7042 .job_queue = &job_queue,
7045 .omit_missing_hash_error = true,7043 .omit_missing_hash_error = true,
7046 .allow_missing_paths_field = false,7044 .allow_missing_paths_field = false,
7047 .allow_missing_fingerprint = true,
7048 .allow_name_string = true,
7049 .use_latest_commit = true,7045 .use_latest_commit = true,
70507046
7051 .package_root = undefined,7047 .package_root = undefined,