authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2023-11-27 04:47:44-05:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2023-11-27 04:47:44-05:00
log89572c63424eb8d9da90f2676b55c660ece57688
tree3df20c1ee3425f3c93cdd70ecf411be43dafdadc
parent19af8aac82510d9adc35fa11db9c57676d4abd23
parent0c0b69891ad0461a7cd14b09a68def350fbf128e
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #18138 from ziglang/fetch-save

`zig fetch`: add `--save` flag

6 files changed, 412 insertions(+), 138 deletions(-)

doc/build.zig.zon.md+1-1
...@@ -61,7 +61,7 @@ String....@@ -61,7 +61,7 @@ String.
6161
62When this is provided, the package is found in a directory relative to the62When this is provided, the package is found in a directory relative to the
63build root. In this case the package's hash is irrelevant and therefore not63build root. In this case the package's hash is irrelevant and therefore not
64computed.64computed. This field and `url` are mutually exclusive.
6565
66### `paths`66### `paths`
6767
lib/init/build.zig.zon+4-3
...@@ -15,8 +15,7 @@...@@ -15,8 +15,7 @@
15 // Once all dependencies are fetched, `zig build` no longer requires15 // Once all dependencies are fetched, `zig build` no longer requires
16 // Internet connectivity.16 // Internet connectivity.
17 .dependencies = .{17 .dependencies = .{
18 // A future version of Zig will provide a `zig add <url>` subcommand18 // See `zig fetch --save <url>` for a command-line interface for adding dependencies.
19 // for easily adding dependencies.
20 //.example = .{19 //.example = .{
21 // // When updating this field to a new URL, be sure to delete the corresponding20 // // When updating this field to a new URL, be sure to delete the corresponding
22 // // `hash`, otherwise you are communicating that you expect to find the old hash at21 // // `hash`, otherwise you are communicating that you expect to find the old hash at
...@@ -36,7 +35,7 @@...@@ -36,7 +35,7 @@
36 //35 //
37 // // When this is provided, the package is found in a directory relative to the36 // // When this is provided, the package is found in a directory relative to the
38 // // build root. In this case the package's hash is irrelevant and therefore not37 // // build root. In this case the package's hash is irrelevant and therefore not
39 // // computed.38 // // computed. This field and `url` are mutually exclusive.
40 // .path = "foo",39 // .path = "foo",
41 //},40 //},
42 },41 },
...@@ -57,5 +56,7 @@...@@ -57,5 +56,7 @@
57 //"build.zig",56 //"build.zig",
58 //"build.zig.zon",57 //"build.zig.zon",
59 //"src",58 //"src",
59 //"LICENSE",
60 //"README.md",
60 },61 },
61}62}
lib/std/zig/render.zig+20-4
...@@ -26,6 +26,8 @@ pub const Fixups = struct {...@@ -26,6 +26,8 @@ pub const Fixups = struct {
26 omit_nodes: std.AutoHashMapUnmanaged(Ast.Node.Index, void) = .{},26 omit_nodes: std.AutoHashMapUnmanaged(Ast.Node.Index, void) = .{},
27 /// These expressions will be replaced with the string value.27 /// These expressions will be replaced with the string value.
28 replace_nodes_with_string: std.AutoHashMapUnmanaged(Ast.Node.Index, []const u8) = .{},28 replace_nodes_with_string: std.AutoHashMapUnmanaged(Ast.Node.Index, []const u8) = .{},
29 /// The string value will be inserted directly after the node.
30 append_string_after_node: std.AutoHashMapUnmanaged(Ast.Node.Index, []const u8) = .{},
29 /// These nodes will be replaced with a different node.31 /// These nodes will be replaced with a different node.
30 replace_nodes_with_node: std.AutoHashMapUnmanaged(Ast.Node.Index, Ast.Node.Index) = .{},32 replace_nodes_with_node: std.AutoHashMapUnmanaged(Ast.Node.Index, Ast.Node.Index) = .{},
31 /// Change all identifier names matching the key to be value instead.33 /// Change all identifier names matching the key to be value instead.
...@@ -40,6 +42,7 @@ pub const Fixups = struct {...@@ -40,6 +42,7 @@ pub const Fixups = struct {
40 f.gut_functions.count() +42 f.gut_functions.count() +
41 f.omit_nodes.count() +43 f.omit_nodes.count() +
42 f.replace_nodes_with_string.count() +44 f.replace_nodes_with_string.count() +
45 f.append_string_after_node.count() +
43 f.replace_nodes_with_node.count() +46 f.replace_nodes_with_node.count() +
44 f.rename_identifiers.count() +47 f.rename_identifiers.count() +
45 @intFromBool(f.rebase_imported_paths != null);48 @intFromBool(f.rebase_imported_paths != null);
...@@ -50,6 +53,7 @@ pub const Fixups = struct {...@@ -50,6 +53,7 @@ pub const Fixups = struct {
50 f.gut_functions.clearRetainingCapacity();53 f.gut_functions.clearRetainingCapacity();
51 f.omit_nodes.clearRetainingCapacity();54 f.omit_nodes.clearRetainingCapacity();
52 f.replace_nodes_with_string.clearRetainingCapacity();55 f.replace_nodes_with_string.clearRetainingCapacity();
56 f.append_string_after_node.clearRetainingCapacity();
53 f.replace_nodes_with_node.clearRetainingCapacity();57 f.replace_nodes_with_node.clearRetainingCapacity();
54 f.rename_identifiers.clearRetainingCapacity();58 f.rename_identifiers.clearRetainingCapacity();
5559
...@@ -61,6 +65,7 @@ pub const Fixups = struct {...@@ -61,6 +65,7 @@ pub const Fixups = struct {
61 f.gut_functions.deinit(gpa);65 f.gut_functions.deinit(gpa);
62 f.omit_nodes.deinit(gpa);66 f.omit_nodes.deinit(gpa);
63 f.replace_nodes_with_string.deinit(gpa);67 f.replace_nodes_with_string.deinit(gpa);
68 f.append_string_after_node.deinit(gpa);
64 f.replace_nodes_with_node.deinit(gpa);69 f.replace_nodes_with_node.deinit(gpa);
65 f.rename_identifiers.deinit(gpa);70 f.rename_identifiers.deinit(gpa);
66 f.* = undefined;71 f.* = undefined;
...@@ -912,6 +917,16 @@ fn renderExpression(r: *Render, node: Ast.Node.Index, space: Space) Error!void {...@@ -912,6 +917,16 @@ fn renderExpression(r: *Render, node: Ast.Node.Index, space: Space) Error!void {
912 }917 }
913}918}
914919
920/// Same as `renderExpression`, but afterwards looks for any
921/// append_string_after_node fixups to apply
922fn renderExpressionFixup(r: *Render, node: Ast.Node.Index, space: Space) Error!void {
923 const ais = r.ais;
924 try renderExpression(r, node, space);
925 if (r.fixups.append_string_after_node.get(node)) |bytes| {
926 try ais.writer().writeAll(bytes);
927 }
928}
929
915fn renderArrayType(930fn renderArrayType(
916 r: *Render,931 r: *Render,
917 array_type: Ast.full.ArrayType,932 array_type: Ast.full.ArrayType,
...@@ -2093,10 +2108,11 @@ fn renderStructInit(...@@ -2093,10 +2108,11 @@ fn renderStructInit(
2093 // Don't output a space after the = if expression is a multiline string,2108 // Don't output a space after the = if expression is a multiline string,
2094 // since then it will start on the next line.2109 // since then it will start on the next line.
2095 const nodes = tree.nodes.items(.tag);2110 const nodes = tree.nodes.items(.tag);
2096 const expr = nodes[struct_init.ast.fields[0]];2111 const field_node = struct_init.ast.fields[0];
2112 const expr = nodes[field_node];
2097 var space_after_equal: Space = if (expr == .multiline_string_literal) .none else .space;2113 var space_after_equal: Space = if (expr == .multiline_string_literal) .none else .space;
2098 try renderToken(r, struct_init.ast.lbrace + 3, space_after_equal); // =2114 try renderToken(r, struct_init.ast.lbrace + 3, space_after_equal); // =
2099 try renderExpression(r, struct_init.ast.fields[0], .comma);2115 try renderExpressionFixup(r, field_node, .comma);
21002116
2101 for (struct_init.ast.fields[1..]) |field_init| {2117 for (struct_init.ast.fields[1..]) |field_init| {
2102 const init_token = tree.firstToken(field_init);2118 const init_token = tree.firstToken(field_init);
...@@ -2105,7 +2121,7 @@ fn renderStructInit(...@@ -2105,7 +2121,7 @@ fn renderStructInit(
2105 try renderIdentifier(r, init_token - 2, .space, .eagerly_unquote); // name2121 try renderIdentifier(r, init_token - 2, .space, .eagerly_unquote); // name
2106 space_after_equal = if (nodes[field_init] == .multiline_string_literal) .none else .space;2122 space_after_equal = if (nodes[field_init] == .multiline_string_literal) .none else .space;
2107 try renderToken(r, init_token - 1, space_after_equal); // =2123 try renderToken(r, init_token - 1, space_after_equal); // =
2108 try renderExpression(r, field_init, .comma);2124 try renderExpressionFixup(r, field_init, .comma);
2109 }2125 }
21102126
2111 ais.popIndent();2127 ais.popIndent();
...@@ -2118,7 +2134,7 @@ fn renderStructInit(...@@ -2118,7 +2134,7 @@ fn renderStructInit(
2118 try renderToken(r, init_token - 3, .none); // .2134 try renderToken(r, init_token - 3, .none); // .
2119 try renderIdentifier(r, init_token - 2, .space, .eagerly_unquote); // name2135 try renderIdentifier(r, init_token - 2, .space, .eagerly_unquote); // name
2120 try renderToken(r, init_token - 1, .space); // =2136 try renderToken(r, init_token - 1, .space); // =
2121 try renderExpression(r, field_init, .comma_space);2137 try renderExpressionFixup(r, field_init, .comma_space);
2122 }2138 }
2123 }2139 }
21242140
src/Package/Fetch.zig+1-18
...@@ -520,24 +520,7 @@ fn loadManifest(f: *Fetch, pkg_root: Package.Path) RunError!void {...@@ -520,24 +520,7 @@ fn loadManifest(f: *Fetch, pkg_root: Package.Path) RunError!void {
520520
521 if (manifest.errors.len > 0) {521 if (manifest.errors.len > 0) {
522 const src_path = try eb.printString("{}{s}", .{ pkg_root, Manifest.basename });522 const src_path = try eb.printString("{}{s}", .{ pkg_root, Manifest.basename });
523 const token_starts = ast.tokens.items(.start);523 try manifest.copyErrorsIntoBundle(ast.*, src_path, eb);
524
525 for (manifest.errors) |msg| {
526 const start_loc = ast.tokenLocation(0, msg.tok);
527
528 try eb.addRootErrorMessage(.{
529 .msg = try eb.addString(msg.msg),
530 .src_loc = try eb.addSourceLocation(.{
531 .src_path = src_path,
532 .span_start = token_starts[msg.tok],
533 .span_end = @intCast(token_starts[msg.tok] + ast.tokenSlice(msg.tok).len),
534 .span_main = token_starts[msg.tok] + msg.off,
535 .line = @intCast(start_loc.line),
536 .column = @intCast(start_loc.column),
537 .source_line = try eb.addString(ast.source[start_loc.line_start..start_loc.line_end]),
538 }),
539 });
540 }
541 return error.FetchFailed;524 return error.FetchFailed;
542 }525 }
543}526}
src/Package/Manifest.zig+44-5
...@@ -11,6 +11,7 @@ pub const Dependency = struct {...@@ -11,6 +11,7 @@ pub const Dependency = struct {
11 location_tok: Ast.TokenIndex,11 location_tok: Ast.TokenIndex,
12 hash: ?[]const u8,12 hash: ?[]const u8,
13 hash_tok: Ast.TokenIndex,13 hash_tok: Ast.TokenIndex,
14 node: Ast.Node.Index,
1415
15 pub const Location = union(enum) {16 pub const Location = union(enum) {
16 url: []const u8,17 url: []const u8,
...@@ -55,7 +56,9 @@ comptime {...@@ -55,7 +56,9 @@ comptime {
5556
56name: []const u8,57name: []const u8,
57version: std.SemanticVersion,58version: std.SemanticVersion,
59version_node: Ast.Node.Index,
58dependencies: std.StringArrayHashMapUnmanaged(Dependency),60dependencies: std.StringArrayHashMapUnmanaged(Dependency),
61dependencies_node: Ast.Node.Index,
59paths: std.StringArrayHashMapUnmanaged(void),62paths: std.StringArrayHashMapUnmanaged(void),
60minimum_zig_version: ?std.SemanticVersion,63minimum_zig_version: ?std.SemanticVersion,
6164
...@@ -68,7 +71,7 @@ pub const ParseOptions = struct {...@@ -68,7 +71,7 @@ pub const ParseOptions = struct {
6871
69pub const Error = Allocator.Error;72pub const Error = Allocator.Error;
7073
71pub fn parse(gpa: Allocator, ast: std.zig.Ast, options: ParseOptions) Error!Manifest {74pub fn parse(gpa: Allocator, ast: Ast, options: ParseOptions) Error!Manifest {
72 const node_tags = ast.nodes.items(.tag);75 const node_tags = ast.nodes.items(.tag);
73 const node_datas = ast.nodes.items(.data);76 const node_datas = ast.nodes.items(.data);
74 assert(node_tags[0] == .root);77 assert(node_tags[0] == .root);
...@@ -85,7 +88,9 @@ pub fn parse(gpa: Allocator, ast: std.zig.Ast, options: ParseOptions) Error!Mani...@@ -85,7 +88,9 @@ pub fn parse(gpa: Allocator, ast: std.zig.Ast, options: ParseOptions) Error!Mani
8588
86 .name = undefined,89 .name = undefined,
87 .version = undefined,90 .version = undefined,
91 .version_node = 0,
88 .dependencies = .{},92 .dependencies = .{},
93 .dependencies_node = 0,
89 .paths = .{},94 .paths = .{},
90 .allow_missing_paths_field = options.allow_missing_paths_field,95 .allow_missing_paths_field = options.allow_missing_paths_field,
91 .minimum_zig_version = null,96 .minimum_zig_version = null,
...@@ -104,7 +109,9 @@ pub fn parse(gpa: Allocator, ast: std.zig.Ast, options: ParseOptions) Error!Mani...@@ -104,7 +109,9 @@ pub fn parse(gpa: Allocator, ast: std.zig.Ast, options: ParseOptions) Error!Mani
104 return .{109 return .{
105 .name = p.name,110 .name = p.name,
106 .version = p.version,111 .version = p.version,
112 .version_node = p.version_node,
107 .dependencies = try p.dependencies.clone(p.arena),113 .dependencies = try p.dependencies.clone(p.arena),
114 .dependencies_node = p.dependencies_node,
108 .paths = try p.paths.clone(p.arena),115 .paths = try p.paths.clone(p.arena),
109 .minimum_zig_version = p.minimum_zig_version,116 .minimum_zig_version = p.minimum_zig_version,
110 .errors = try p.arena.dupe(ErrorMessage, p.errors.items),117 .errors = try p.arena.dupe(ErrorMessage, p.errors.items),
...@@ -117,6 +124,33 @@ pub fn deinit(man: *Manifest, gpa: Allocator) void {...@@ -117,6 +124,33 @@ pub fn deinit(man: *Manifest, gpa: Allocator) void {
117 man.* = undefined;124 man.* = undefined;
118}125}
119126
127pub fn copyErrorsIntoBundle(
128 man: Manifest,
129 ast: Ast,
130 /// ErrorBundle null-terminated string index
131 src_path: u32,
132 eb: *std.zig.ErrorBundle.Wip,
133) Allocator.Error!void {
134 const token_starts = ast.tokens.items(.start);
135
136 for (man.errors) |msg| {
137 const start_loc = ast.tokenLocation(0, msg.tok);
138
139 try eb.addRootErrorMessage(.{
140 .msg = try eb.addString(msg.msg),
141 .src_loc = try eb.addSourceLocation(.{
142 .src_path = src_path,
143 .span_start = token_starts[msg.tok],
144 .span_end = @intCast(token_starts[msg.tok] + ast.tokenSlice(msg.tok).len),
145 .span_main = token_starts[msg.tok] + msg.off,
146 .line = @intCast(start_loc.line),
147 .column = @intCast(start_loc.column),
148 .source_line = try eb.addString(ast.source[start_loc.line_start..start_loc.line_end]),
149 }),
150 });
151 }
152}
153
120const hex_charset = "0123456789abcdef";154const hex_charset = "0123456789abcdef";
121155
122pub fn hex64(x: u64) [16]u8 {156pub fn hex64(x: u64) [16]u8 {
...@@ -153,14 +187,16 @@ pub fn hexDigest(digest: Digest) MultiHashHexDigest {...@@ -153,14 +187,16 @@ pub fn hexDigest(digest: Digest) MultiHashHexDigest {
153187
154const Parse = struct {188const Parse = struct {
155 gpa: Allocator,189 gpa: Allocator,
156 ast: std.zig.Ast,190 ast: Ast,
157 arena: Allocator,191 arena: Allocator,
158 buf: std.ArrayListUnmanaged(u8),192 buf: std.ArrayListUnmanaged(u8),
159 errors: std.ArrayListUnmanaged(ErrorMessage),193 errors: std.ArrayListUnmanaged(ErrorMessage),
160194
161 name: []const u8,195 name: []const u8,
162 version: std.SemanticVersion,196 version: std.SemanticVersion,
197 version_node: Ast.Node.Index,
163 dependencies: std.StringArrayHashMapUnmanaged(Dependency),198 dependencies: std.StringArrayHashMapUnmanaged(Dependency),
199 dependencies_node: Ast.Node.Index,
164 paths: std.StringArrayHashMapUnmanaged(void),200 paths: std.StringArrayHashMapUnmanaged(void),
165 allow_missing_paths_field: bool,201 allow_missing_paths_field: bool,
166 minimum_zig_version: ?std.SemanticVersion,202 minimum_zig_version: ?std.SemanticVersion,
...@@ -188,6 +224,7 @@ const Parse = struct {...@@ -188,6 +224,7 @@ const Parse = struct {
188 // things manually provides an opportunity to do any additional verification224 // things manually provides an opportunity to do any additional verification
189 // that is desirable on a per-field basis.225 // that is desirable on a per-field basis.
190 if (mem.eql(u8, field_name, "dependencies")) {226 if (mem.eql(u8, field_name, "dependencies")) {
227 p.dependencies_node = field_init;
191 try parseDependencies(p, field_init);228 try parseDependencies(p, field_init);
192 } else if (mem.eql(u8, field_name, "paths")) {229 } else if (mem.eql(u8, field_name, "paths")) {
193 have_included_paths = true;230 have_included_paths = true;
...@@ -196,6 +233,7 @@ const Parse = struct {...@@ -196,6 +233,7 @@ const Parse = struct {
196 p.name = try parseString(p, field_init);233 p.name = try parseString(p, field_init);
197 have_name = true;234 have_name = true;
198 } else if (mem.eql(u8, field_name, "version")) {235 } else if (mem.eql(u8, field_name, "version")) {
236 p.version_node = field_init;
199 const version_text = try parseString(p, field_init);237 const version_text = try parseString(p, field_init);
200 p.version = std.SemanticVersion.parse(version_text) catch |err| v: {238 p.version = std.SemanticVersion.parse(version_text) catch |err| v: {
201 try appendError(p, main_tokens[field_init], "unable to parse semantic version: {s}", .{@errorName(err)});239 try appendError(p, main_tokens[field_init], "unable to parse semantic version: {s}", .{@errorName(err)});
...@@ -264,6 +302,7 @@ const Parse = struct {...@@ -264,6 +302,7 @@ const Parse = struct {
264 .location_tok = 0,302 .location_tok = 0,
265 .hash = null,303 .hash = null,
266 .hash_tok = 0,304 .hash_tok = 0,
305 .node = node,
267 };306 };
268 var has_location = false;307 var has_location = false;
269308
...@@ -548,7 +587,7 @@ test "basic" {...@@ -548,7 +587,7 @@ test "basic" {
548 \\}587 \\}
549 ;588 ;
550589
551 var ast = try std.zig.Ast.parse(gpa, example, .zon);590 var ast = try Ast.parse(gpa, example, .zon);
552 defer ast.deinit(gpa);591 defer ast.deinit(gpa);
553592
554 try testing.expect(ast.errors.len == 0);593 try testing.expect(ast.errors.len == 0);
...@@ -591,7 +630,7 @@ test "minimum_zig_version" {...@@ -591,7 +630,7 @@ test "minimum_zig_version" {
591 \\}630 \\}
592 ;631 ;
593632
594 var ast = try std.zig.Ast.parse(gpa, example, .zon);633 var ast = try Ast.parse(gpa, example, .zon);
595 defer ast.deinit(gpa);634 defer ast.deinit(gpa);
596635
597 try testing.expect(ast.errors.len == 0);636 try testing.expect(ast.errors.len == 0);
...@@ -623,7 +662,7 @@ test "minimum_zig_version - invalid version" {...@@ -623,7 +662,7 @@ test "minimum_zig_version - invalid version" {
623 \\}662 \\}
624 ;663 ;
625664
626 var ast = try std.zig.Ast.parse(gpa, example, .zon);665 var ast = try Ast.parse(gpa, example, .zon);
627 defer ast.deinit(gpa);666 defer ast.deinit(gpa);
628667
629 try testing.expect(ast.errors.len == 0);668 try testing.expect(ast.errors.len == 0);
src/main.zig+342-107
...@@ -1255,7 +1255,7 @@ fn buildOutputType(...@@ -1255,7 +1255,7 @@ fn buildOutputType(
1255 override_lib_dir = args_iter.nextOrFatal();1255 override_lib_dir = args_iter.nextOrFatal();
1256 } else if (mem.eql(u8, arg, "--debug-log")) {1256 } else if (mem.eql(u8, arg, "--debug-log")) {
1257 if (!build_options.enable_logging) {1257 if (!build_options.enable_logging) {
1258 std.log.warn("Zig was compiled without logging enabled (-Dlog). --debug-log has no effect.", .{});1258 warn("Zig was compiled without logging enabled (-Dlog). --debug-log has no effect.", .{});
1259 _ = args_iter.nextOrFatal();1259 _ = args_iter.nextOrFatal();
1260 } else {1260 } else {
1261 try log_scopes.append(gpa, args_iter.nextOrFatal());1261 try log_scopes.append(gpa, args_iter.nextOrFatal());
...@@ -1279,7 +1279,7 @@ fn buildOutputType(...@@ -1279,7 +1279,7 @@ fn buildOutputType(
1279 listen = .stdio;1279 listen = .stdio;
1280 } else if (mem.eql(u8, arg, "--debug-link-snapshot")) {1280 } else if (mem.eql(u8, arg, "--debug-link-snapshot")) {
1281 if (!build_options.enable_link_snapshots) {1281 if (!build_options.enable_link_snapshots) {
1282 std.log.warn("Zig was compiled without linker snapshots enabled (-Dlink-snapshot). --debug-link-snapshot has no effect.", .{});1282 warn("Zig was compiled without linker snapshots enabled (-Dlink-snapshot). --debug-link-snapshot has no effect.", .{});
1283 } else {1283 } else {
1284 enable_link_snapshots = true;1284 enable_link_snapshots = true;
1285 }1285 }
...@@ -1558,7 +1558,7 @@ fn buildOutputType(...@@ -1558,7 +1558,7 @@ fn buildOutputType(
1558 };1558 };
1559 } else if (mem.eql(u8, arg, "--debug-compile-errors")) {1559 } else if (mem.eql(u8, arg, "--debug-compile-errors")) {
1560 if (!crash_report.is_enabled) {1560 if (!crash_report.is_enabled) {
1561 std.log.warn("Zig was compiled in a release mode. --debug-compile-errors has no effect.", .{});1561 warn("Zig was compiled in a release mode. --debug-compile-errors has no effect.", .{});
1562 } else {1562 } else {
1563 debug_compile_errors = true;1563 debug_compile_errors = true;
1564 }1564 }
...@@ -1662,7 +1662,7 @@ fn buildOutputType(...@@ -1662,7 +1662,7 @@ fn buildOutputType(
1662 },1662 },
1663 .def, .unknown => {1663 .def, .unknown => {
1664 if (std.ascii.eqlIgnoreCase(".xml", std.fs.path.extension(arg))) {1664 if (std.ascii.eqlIgnoreCase(".xml", std.fs.path.extension(arg))) {
1665 std.log.warn("embedded manifest files must have the extension '.manifest'", .{});1665 warn("embedded manifest files must have the extension '.manifest'", .{});
1666 }1666 }
1667 fatal("unrecognized file extension of parameter '{s}'", .{arg});1667 fatal("unrecognized file extension of parameter '{s}'", .{arg});
1668 },1668 },
...@@ -2793,7 +2793,7 @@ fn buildOutputType(...@@ -2793,7 +2793,7 @@ fn buildOutputType(
2793 continue;2793 continue;
2794 },2794 },
2795 .only_compiler_rt => {2795 .only_compiler_rt => {
2796 std.log.warn("ignoring superfluous library '{s}': this dependency is fulfilled instead by compiler-rt which zig unconditionally provides", .{lib_name});2796 warn("ignoring superfluous library '{s}': this dependency is fulfilled instead by compiler-rt which zig unconditionally provides", .{lib_name});
2797 continue;2797 continue;
2798 },2798 },
2799 }2799 }
...@@ -4851,7 +4851,6 @@ pub const usage_init =...@@ -4851,7 +4851,6 @@ pub const usage_init =
4851;4851;
48524852
4853pub fn cmdInit(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {4853pub fn cmdInit(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {
4854 _ = gpa;
4855 {4854 {
4856 var i: usize = 0;4855 var i: usize = 0;
4857 while (i < args.len) : (i += 1) {4856 while (i < args.len) : (i += 1) {
...@@ -4868,58 +4867,24 @@ pub fn cmdInit(gpa: Allocator, arena: Allocator, args: []const []const u8) !void...@@ -4868,58 +4867,24 @@ pub fn cmdInit(gpa: Allocator, arena: Allocator, args: []const []const u8) !void
4868 }4867 }
4869 }4868 }
4870 }4869 }
4871 const self_exe_path = try introspect.findZigExePath(arena);
4872 var zig_lib_directory = introspect.findZigLibDirFromSelfExe(arena, self_exe_path) catch |err| {
4873 fatal("unable to find zig installation directory: {s}\n", .{@errorName(err)});
4874 };
4875 defer zig_lib_directory.handle.close();
48764870
4877 const s = fs.path.sep_str;4871 var templates = findTemplates(gpa, arena);
4878 const template_sub_path = "init";4872 defer templates.deinit();
4879 var template_dir = zig_lib_directory.handle.openDir(template_sub_path, .{}) catch |err| {
4880 const path = zig_lib_directory.path orelse ".";
4881 fatal("unable to open zig project template directory '{s}{s}{s}': {s}", .{
4882 path, s, template_sub_path, @errorName(err),
4883 });
4884 };
4885 defer template_dir.close();
48864873
4887 const cwd_path = try process.getCwdAlloc(arena);4874 const cwd_path = try process.getCwdAlloc(arena);
4888 const cwd_basename = fs.path.basename(cwd_path);4875 const cwd_basename = fs.path.basename(cwd_path);
48894876
4890 const max_bytes = 10 * 1024 * 1024;4877 const s = fs.path.sep_str;
4891 const template_paths = [_][]const u8{4878 const template_paths = [_][]const u8{
4892 "build.zig",4879 Package.build_zig_basename,
4893 "build.zig.zon",4880 Package.Manifest.basename,
4894 "src" ++ s ++ "main.zig",4881 "src" ++ s ++ "main.zig",
4895 "src" ++ s ++ "root.zig",4882 "src" ++ s ++ "root.zig",
4896 };4883 };
4897 var ok_count: usize = 0;4884 var ok_count: usize = 0;
48984885
4899 for (template_paths) |template_path| {4886 for (template_paths) |template_path| {
4900 if (fs.path.dirname(template_path)) |dirname| {4887 if (templates.write(arena, fs.cwd(), cwd_basename, template_path)) |_| {
4901 fs.cwd().makePath(dirname) catch |err| {
4902 fatal("unable to make path '{s}': {s}", .{ dirname, @errorName(err) });
4903 };
4904 }
4905
4906 const contents = template_dir.readFileAlloc(arena, template_path, max_bytes) catch |err| {
4907 fatal("unable to read template file '{s}': {s}", .{ template_path, @errorName(err) });
4908 };
4909 var modified_contents = try std.ArrayList(u8).initCapacity(arena, contents.len);
4910 for (contents) |c| {
4911 if (c == '$') {
4912 try modified_contents.appendSlice(cwd_basename);
4913 } else {
4914 try modified_contents.append(c);
4915 }
4916 }
4917
4918 if (fs.cwd().writeFile2(.{
4919 .sub_path = template_path,
4920 .data = modified_contents.items,
4921 .flags = .{ .exclusive = true },
4922 })) |_| {
4923 std.log.info("created {s}", .{template_path});4888 std.log.info("created {s}", .{template_path});
4924 ok_count += 1;4889 ok_count += 1;
4925 } else |err| switch (err) {4890 } else |err| switch (err) {
...@@ -5057,14 +5022,14 @@ pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi...@@ -5057,14 +5022,14 @@ pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi
5057 try child_argv.appendSlice(args[i .. i + 2]);5022 try child_argv.appendSlice(args[i .. i + 2]);
5058 i += 1;5023 i += 1;
5059 if (!build_options.enable_logging) {5024 if (!build_options.enable_logging) {
5060 std.log.warn("Zig was compiled without logging enabled (-Dlog). --debug-log has no effect.", .{});5025 warn("Zig was compiled without logging enabled (-Dlog). --debug-log has no effect.", .{});
5061 } else {5026 } else {
5062 try log_scopes.append(gpa, args[i]);5027 try log_scopes.append(gpa, args[i]);
5063 }5028 }
5064 continue;5029 continue;
5065 } else if (mem.eql(u8, arg, "--debug-compile-errors")) {5030 } else if (mem.eql(u8, arg, "--debug-compile-errors")) {
5066 if (!crash_report.is_enabled) {5031 if (!crash_report.is_enabled) {
5067 std.log.warn("Zig was compiled in a release mode. --debug-compile-errors has no effect.", .{});5032 warn("Zig was compiled in a release mode. --debug-compile-errors has no effect.", .{});
5068 } else {5033 } else {
5069 debug_compile_errors = true;5034 debug_compile_errors = true;
5070 }5035 }
...@@ -5113,44 +5078,11 @@ pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi...@@ -5113,44 +5078,11 @@ pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi
5113 defer if (cleanup_build_dir) |*dir| dir.close();5078 defer if (cleanup_build_dir) |*dir| dir.close();
51145079
5115 const cwd_path = try process.getCwdAlloc(arena);5080 const cwd_path = try process.getCwdAlloc(arena);
5116 const build_zig_basename = if (build_file) |bf| fs.path.basename(bf) else Package.build_zig_basename;5081 const build_root = try findBuildRoot(arena, .{
5117 const build_root: Compilation.Directory = blk: {5082 .cwd_path = cwd_path,
5118 if (build_file) |bf| {5083 .build_file = build_file,
5119 if (fs.path.dirname(bf)) |dirname| {5084 });
5120 const dir = fs.cwd().openDir(dirname, .{}) catch |err| {5085 child_argv.items[argv_index_build_file] = build_root.directory.path orelse cwd_path;
5121 fatal("unable to open directory to build file from argument 'build-file', '{s}': {s}", .{ dirname, @errorName(err) });
5122 };
5123 cleanup_build_dir = dir;
5124 break :blk .{ .path = dirname, .handle = dir };
5125 }
5126
5127 break :blk .{ .path = null, .handle = fs.cwd() };
5128 }
5129 // Search up parent directories until we find build.zig.
5130 var dirname: []const u8 = cwd_path;
5131 while (true) {
5132 const joined_path = try fs.path.join(arena, &[_][]const u8{ dirname, build_zig_basename });
5133 if (fs.cwd().access(joined_path, .{})) |_| {
5134 const dir = fs.cwd().openDir(dirname, .{}) catch |err| {
5135 fatal("unable to open directory while searching for build.zig file, '{s}': {s}", .{ dirname, @errorName(err) });
5136 };
5137 break :blk .{ .path = dirname, .handle = dir };
5138 } else |err| switch (err) {
5139 error.FileNotFound => {
5140 dirname = fs.path.dirname(dirname) orelse {
5141 std.log.info("{s}", .{
5142 \\Initialize a 'build.zig' template file with `zig init-lib` or `zig init-exe`,
5143 \\or see `zig --help` for more options.
5144 });
5145 fatal("No 'build.zig' file found, in the current directory or any parent directories.", .{});
5146 };
5147 continue;
5148 },
5149 else => |e| return e,
5150 }
5151 }
5152 };
5153 child_argv.items[argv_index_build_file] = build_root.path orelse cwd_path;
51545086
5155 var global_cache_directory: Compilation.Directory = l: {5087 var global_cache_directory: Compilation.Directory = l: {
5156 const p = override_global_cache_dir orelse try introspect.resolveGlobalCacheDir(arena);5088 const p = override_global_cache_dir orelse try introspect.resolveGlobalCacheDir(arena);
...@@ -5170,9 +5102,9 @@ pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi...@@ -5170,9 +5102,9 @@ pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi
5170 .path = local_cache_dir_path,5102 .path = local_cache_dir_path,
5171 };5103 };
5172 }5104 }
5173 const cache_dir_path = try build_root.join(arena, &[_][]const u8{"zig-cache"});5105 const cache_dir_path = try build_root.directory.join(arena, &[_][]const u8{"zig-cache"});
5174 break :l .{5106 break :l .{
5175 .handle = try build_root.handle.makeOpenPath("zig-cache", .{}),5107 .handle = try build_root.directory.handle.makeOpenPath("zig-cache", .{}),
5176 .path = cache_dir_path,5108 .path = cache_dir_path,
5177 };5109 };
5178 };5110 };
...@@ -5215,8 +5147,8 @@ pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi...@@ -5215,8 +5147,8 @@ pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi
5215 };5147 };
52165148
5217 var build_mod: Package.Module = .{5149 var build_mod: Package.Module = .{
5218 .root = .{ .root_dir = build_root },5150 .root = .{ .root_dir = build_root.directory },
5219 .root_src_path = build_zig_basename,5151 .root_src_path = build_root.build_zig_basename,
5220 .fully_qualified_name = "root.@build",5152 .fully_qualified_name = "root.@build",
5221 };5153 };
5222 if (build_options.only_core_functionality) {5154 if (build_options.only_core_functionality) {
...@@ -6904,40 +6836,40 @@ const ClangSearchSanitizer = struct {...@@ -6904,40 +6836,40 @@ const ClangSearchSanitizer = struct {
6904 .I => {6836 .I => {
6905 if (m.I) return;6837 if (m.I) return;
6906 m.I = true;6838 m.I = true;
6907 if (m.isystem) std.log.warn(wtxt, .{ dir, "I", "isystem" });6839 if (m.isystem) warn(wtxt, .{ dir, "I", "isystem" });
6908 if (m.idirafter) std.log.warn(wtxt, .{ dir, "I", "idirafter" });6840 if (m.idirafter) warn(wtxt, .{ dir, "I", "idirafter" });
6909 if (m.iframework) std.log.warn(wtxt, .{ dir, "I", "iframework" });6841 if (m.iframework) warn(wtxt, .{ dir, "I", "iframework" });
6910 },6842 },
6911 .isystem => {6843 .isystem => {
6912 if (m.isystem) return;6844 if (m.isystem) return;
6913 m.isystem = true;6845 m.isystem = true;
6914 if (m.I) std.log.warn(wtxt, .{ dir, "isystem", "I" });6846 if (m.I) warn(wtxt, .{ dir, "isystem", "I" });
6915 if (m.idirafter) std.log.warn(wtxt, .{ dir, "isystem", "idirafter" });6847 if (m.idirafter) warn(wtxt, .{ dir, "isystem", "idirafter" });
6916 if (m.iframework) std.log.warn(wtxt, .{ dir, "isystem", "iframework" });6848 if (m.iframework) warn(wtxt, .{ dir, "isystem", "iframework" });
6917 },6849 },
6918 .iwithsysroot => {6850 .iwithsysroot => {
6919 if (m.iwithsysroot) return;6851 if (m.iwithsysroot) return;
6920 m.iwithsysroot = true;6852 m.iwithsysroot = true;
6921 if (m.iframeworkwithsysroot) std.log.warn(wtxt, .{ dir, "iwithsysroot", "iframeworkwithsysroot" });6853 if (m.iframeworkwithsysroot) warn(wtxt, .{ dir, "iwithsysroot", "iframeworkwithsysroot" });
6922 },6854 },
6923 .idirafter => {6855 .idirafter => {
6924 if (m.idirafter) return;6856 if (m.idirafter) return;
6925 m.idirafter = true;6857 m.idirafter = true;
6926 if (m.I) std.log.warn(wtxt, .{ dir, "idirafter", "I" });6858 if (m.I) warn(wtxt, .{ dir, "idirafter", "I" });
6927 if (m.isystem) std.log.warn(wtxt, .{ dir, "idirafter", "isystem" });6859 if (m.isystem) warn(wtxt, .{ dir, "idirafter", "isystem" });
6928 if (m.iframework) std.log.warn(wtxt, .{ dir, "idirafter", "iframework" });6860 if (m.iframework) warn(wtxt, .{ dir, "idirafter", "iframework" });
6929 },6861 },
6930 .iframework => {6862 .iframework => {
6931 if (m.iframework) return;6863 if (m.iframework) return;
6932 m.iframework = true;6864 m.iframework = true;
6933 if (m.I) std.log.warn(wtxt, .{ dir, "iframework", "I" });6865 if (m.I) warn(wtxt, .{ dir, "iframework", "I" });
6934 if (m.isystem) std.log.warn(wtxt, .{ dir, "iframework", "isystem" });6866 if (m.isystem) warn(wtxt, .{ dir, "iframework", "isystem" });
6935 if (m.idirafter) std.log.warn(wtxt, .{ dir, "iframework", "idirafter" });6867 if (m.idirafter) warn(wtxt, .{ dir, "iframework", "idirafter" });
6936 },6868 },
6937 .iframeworkwithsysroot => {6869 .iframeworkwithsysroot => {
6938 if (m.iframeworkwithsysroot) return;6870 if (m.iframeworkwithsysroot) return;
6939 m.iframeworkwithsysroot = true;6871 m.iframeworkwithsysroot = true;
6940 if (m.iwithsysroot) std.log.warn(wtxt, .{ dir, "iframeworkwithsysroot", "iwithsysroot" });6872 if (m.iwithsysroot) warn(wtxt, .{ dir, "iframeworkwithsysroot", "iwithsysroot" });
6941 },6873 },
6942 }6874 }
6943 try self.argv.append(arg);6875 try self.argv.append(arg);
...@@ -7097,6 +7029,8 @@ pub const usage_fetch =...@@ -7097,6 +7029,8 @@ pub const usage_fetch =
7097 \\ -h, --help Print this help and exit7029 \\ -h, --help Print this help and exit
7098 \\ --global-cache-dir [path] Override path to global Zig cache directory7030 \\ --global-cache-dir [path] Override path to global Zig cache directory
7099 \\ --debug-hash Print verbose hash information to stdout7031 \\ --debug-hash Print verbose hash information to stdout
7032 \\ --save Add the fetched package to build.zig.zon
7033 \\ --save=[name] Add the fetched package to build.zig.zon as name
7100 \\7034 \\
7101;7035;
71027036
...@@ -7111,6 +7045,7 @@ fn cmdFetch(...@@ -7111,6 +7045,7 @@ fn cmdFetch(
7111 var opt_path_or_url: ?[]const u8 = null;7045 var opt_path_or_url: ?[]const u8 = null;
7112 var override_global_cache_dir: ?[]const u8 = try EnvVar.ZIG_GLOBAL_CACHE_DIR.get(arena);7046 var override_global_cache_dir: ?[]const u8 = try EnvVar.ZIG_GLOBAL_CACHE_DIR.get(arena);
7113 var debug_hash: bool = false;7047 var debug_hash: bool = false;
7048 var save: union(enum) { no, yes, name: []const u8 } = .no;
71147049
7115 {7050 {
7116 var i: usize = 0;7051 var i: usize = 0;
...@@ -7125,10 +7060,12 @@ fn cmdFetch(...@@ -7125,10 +7060,12 @@ fn cmdFetch(
7125 if (i + 1 >= args.len) fatal("expected argument after '{s}'", .{arg});7060 if (i + 1 >= args.len) fatal("expected argument after '{s}'", .{arg});
7126 i += 1;7061 i += 1;
7127 override_global_cache_dir = args[i];7062 override_global_cache_dir = args[i];
7128 continue;
7129 } else if (mem.eql(u8, arg, "--debug-hash")) {7063 } else if (mem.eql(u8, arg, "--debug-hash")) {
7130 debug_hash = true;7064 debug_hash = true;
7131 continue;7065 } else if (mem.eql(u8, arg, "--save")) {
7066 save = .yes;
7067 } else if (mem.startsWith(u8, arg, "--save=")) {
7068 save = .{ .name = arg["--save=".len..] };
7132 } else {7069 } else {
7133 fatal("unrecognized parameter: '{s}'", .{arg});7070 fatal("unrecognized parameter: '{s}'", .{arg});
7134 }7071 }
...@@ -7214,7 +7151,97 @@ fn cmdFetch(...@@ -7214,7 +7151,97 @@ fn cmdFetch(
7214 progress.done = true;7151 progress.done = true;
7215 progress.refresh();7152 progress.refresh();
72167153
7217 try io.getStdOut().writeAll(hex_digest ++ "\n");7154 const name = switch (save) {
7155 .no => {
7156 try io.getStdOut().writeAll(hex_digest ++ "\n");
7157 return cleanExit();
7158 },
7159 .yes => n: {
7160 const fetched_manifest = fetch.manifest orelse
7161 fatal("unable to determine name; fetched package has no build.zig.zon file", .{});
7162 break :n fetched_manifest.name;
7163 },
7164 .name => |n| n,
7165 };
7166
7167 const cwd_path = try process.getCwdAlloc(arena);
7168
7169 var build_root = try findBuildRoot(arena, .{
7170 .cwd_path = cwd_path,
7171 });
7172 defer build_root.deinit();
7173
7174 // The name to use in case the manifest file needs to be created now.
7175 const init_root_name = fs.path.basename(build_root.directory.path orelse cwd_path);
7176 var manifest, var ast = try loadManifest(gpa, arena, .{
7177 .root_name = init_root_name,
7178 .dir = build_root.directory.handle,
7179 .color = color,
7180 });
7181 defer {
7182 manifest.deinit(gpa);
7183 ast.deinit(gpa);
7184 }
7185
7186 var fixups: Ast.Fixups = .{};
7187 defer fixups.deinit(gpa);
7188
7189 const new_node_init = try std.fmt.allocPrint(arena,
7190 \\.{{
7191 \\ .url = "{}",
7192 \\ .hash = "{}",
7193 \\ }}
7194 , .{
7195 std.zig.fmtEscapes(path_or_url),
7196 std.zig.fmtEscapes(&hex_digest),
7197 });
7198
7199 const new_node_text = try std.fmt.allocPrint(arena, ".{} = {s},\n", .{
7200 std.zig.fmtId(name), new_node_init,
7201 });
7202
7203 const dependencies_init = try std.fmt.allocPrint(arena, ".{{\n {s} }}", .{
7204 new_node_text,
7205 });
7206
7207 const dependencies_text = try std.fmt.allocPrint(arena, ".dependencies = {s},\n", .{
7208 dependencies_init,
7209 });
7210
7211 if (manifest.dependencies.get(name)) |dep| {
7212 if (dep.hash) |h| {
7213 switch (dep.location) {
7214 .url => |u| {
7215 if (mem.eql(u8, h, &hex_digest) and mem.eql(u8, u, path_or_url)) {
7216 std.log.info("existing dependency named '{s}' is up-to-date", .{name});
7217 process.exit(0);
7218 }
7219 },
7220 .path => {},
7221 }
7222 }
7223 warn("overwriting existing dependency named '{s}'", .{name});
7224 try fixups.replace_nodes_with_string.put(gpa, dep.node, new_node_init);
7225 } else if (manifest.dependencies.count() > 0) {
7226 // Add fixup for adding another dependency.
7227 const deps = manifest.dependencies.values();
7228 const last_dep_node = deps[deps.len - 1].node;
7229 try fixups.append_string_after_node.put(gpa, last_dep_node, new_node_text);
7230 } else if (manifest.dependencies_node != 0) {
7231 // Add fixup for replacing the entire dependencies struct.
7232 try fixups.replace_nodes_with_string.put(gpa, manifest.dependencies_node, dependencies_init);
7233 } else {
7234 // Add fixup for adding dependencies struct.
7235 try fixups.append_string_after_node.put(gpa, manifest.version_node, dependencies_text);
7236 }
7237
7238 var rendered = std.ArrayList(u8).init(gpa);
7239 defer rendered.deinit();
7240 try ast.renderToArrayList(&rendered, fixups);
7241
7242 build_root.directory.handle.writeFile(Package.Manifest.basename, rendered.items) catch |err| {
7243 fatal("unable to write {s} file: {s}", .{ Package.Manifest.basename, @errorName(err) });
7244 };
72187245
7219 return cleanExit();7246 return cleanExit();
7220}7247}
...@@ -7279,3 +7306,211 @@ fn defaultWasmEntryName(exec_model: ?std.builtin.WasiExecModel) []const u8 {...@@ -7279,3 +7306,211 @@ fn defaultWasmEntryName(exec_model: ?std.builtin.WasiExecModel) []const u8 {
7279 }7306 }
7280 return "_start";7307 return "_start";
7281}7308}
7309
7310const BuildRoot = struct {
7311 directory: Cache.Directory,
7312 build_zig_basename: []const u8,
7313 cleanup_build_dir: ?fs.Dir,
7314
7315 fn deinit(br: *BuildRoot) void {
7316 if (br.cleanup_build_dir) |*dir| dir.close();
7317 br.* = undefined;
7318 }
7319};
7320
7321const FindBuildRootOptions = struct {
7322 build_file: ?[]const u8 = null,
7323 cwd_path: ?[]const u8 = null,
7324};
7325
7326fn findBuildRoot(arena: Allocator, options: FindBuildRootOptions) !BuildRoot {
7327 const cwd_path = options.cwd_path orelse try process.getCwdAlloc(arena);
7328 const build_zig_basename = if (options.build_file) |bf|
7329 fs.path.basename(bf)
7330 else
7331 Package.build_zig_basename;
7332
7333 if (options.build_file) |bf| {
7334 if (fs.path.dirname(bf)) |dirname| {
7335 const dir = fs.cwd().openDir(dirname, .{}) catch |err| {
7336 fatal("unable to open directory to build file from argument 'build-file', '{s}': {s}", .{ dirname, @errorName(err) });
7337 };
7338 return .{
7339 .build_zig_basename = build_zig_basename,
7340 .directory = .{ .path = dirname, .handle = dir },
7341 .cleanup_build_dir = dir,
7342 };
7343 }
7344
7345 return .{
7346 .build_zig_basename = build_zig_basename,
7347 .directory = .{ .path = null, .handle = fs.cwd() },
7348 .cleanup_build_dir = null,
7349 };
7350 }
7351 // Search up parent directories until we find build.zig.
7352 var dirname: []const u8 = cwd_path;
7353 while (true) {
7354 const joined_path = try fs.path.join(arena, &[_][]const u8{ dirname, build_zig_basename });
7355 if (fs.cwd().access(joined_path, .{})) |_| {
7356 const dir = fs.cwd().openDir(dirname, .{}) catch |err| {
7357 fatal("unable to open directory while searching for build.zig file, '{s}': {s}", .{ dirname, @errorName(err) });
7358 };
7359 return .{
7360 .build_zig_basename = build_zig_basename,
7361 .directory = .{
7362 .path = dirname,
7363 .handle = dir,
7364 },
7365 .cleanup_build_dir = dir,
7366 };
7367 } else |err| switch (err) {
7368 error.FileNotFound => {
7369 dirname = fs.path.dirname(dirname) orelse {
7370 std.log.info("initialize {s} template file with 'zig init'", .{
7371 Package.build_zig_basename,
7372 });
7373 std.log.info("see 'zig --help' for more options", .{});
7374 fatal("no build.zig file found, in the current directory or any parent directories", .{});
7375 };
7376 continue;
7377 },
7378 else => |e| return e,
7379 }
7380 }
7381}
7382
7383const LoadManifestOptions = struct {
7384 root_name: []const u8,
7385 dir: fs.Dir,
7386 color: Color,
7387};
7388
7389fn loadManifest(
7390 gpa: Allocator,
7391 arena: Allocator,
7392 options: LoadManifestOptions,
7393) !struct { Package.Manifest, Ast } {
7394 const manifest_bytes = while (true) {
7395 break options.dir.readFileAllocOptions(
7396 arena,
7397 Package.Manifest.basename,
7398 Package.Manifest.max_bytes,
7399 null,
7400 1,
7401 0,
7402 ) catch |err| switch (err) {
7403 error.FileNotFound => {
7404 var templates = findTemplates(gpa, arena);
7405 defer templates.deinit();
7406
7407 templates.write(arena, options.dir, options.root_name, Package.Manifest.basename) catch |e| {
7408 fatal("unable to write {s}: {s}", .{
7409 Package.Manifest.basename, @errorName(e),
7410 });
7411 };
7412 continue;
7413 },
7414 else => |e| fatal("unable to load {s}: {s}", .{
7415 Package.Manifest.basename, @errorName(e),
7416 }),
7417 };
7418 };
7419 var ast = try Ast.parse(gpa, manifest_bytes, .zon);
7420 errdefer ast.deinit(gpa);
7421
7422 if (ast.errors.len > 0) {
7423 try printAstErrorsToStderr(gpa, ast, Package.Manifest.basename, options.color);
7424 process.exit(2);
7425 }
7426
7427 var manifest = try Package.Manifest.parse(gpa, ast, .{});
7428 errdefer manifest.deinit(gpa);
7429
7430 if (manifest.errors.len > 0) {
7431 var wip_errors: std.zig.ErrorBundle.Wip = undefined;
7432 try wip_errors.init(gpa);
7433 defer wip_errors.deinit();
7434
7435 const src_path = try wip_errors.addString(Package.Manifest.basename);
7436 try manifest.copyErrorsIntoBundle(ast, src_path, &wip_errors);
7437
7438 var error_bundle = try wip_errors.toOwnedBundle("");
7439 defer error_bundle.deinit(gpa);
7440 error_bundle.renderToStdErr(renderOptions(options.color));
7441
7442 process.exit(2);
7443 }
7444 return .{ manifest, ast };
7445}
7446
7447const Templates = struct {
7448 zig_lib_directory: Cache.Directory,
7449 dir: fs.Dir,
7450 buffer: std.ArrayList(u8),
7451
7452 fn deinit(templates: *Templates) void {
7453 templates.zig_lib_directory.handle.close();
7454 templates.dir.close();
7455 templates.buffer.deinit();
7456 templates.* = undefined;
7457 }
7458
7459 fn write(
7460 templates: *Templates,
7461 arena: Allocator,
7462 out_dir: fs.Dir,
7463 root_name: []const u8,
7464 template_path: []const u8,
7465 ) !void {
7466 if (fs.path.dirname(template_path)) |dirname| {
7467 out_dir.makePath(dirname) catch |err| {
7468 fatal("unable to make path '{s}': {s}", .{ dirname, @errorName(err) });
7469 };
7470 }
7471
7472 const max_bytes = 10 * 1024 * 1024;
7473 const contents = templates.dir.readFileAlloc(arena, template_path, max_bytes) catch |err| {
7474 fatal("unable to read template file '{s}': {s}", .{ template_path, @errorName(err) });
7475 };
7476 templates.buffer.clearRetainingCapacity();
7477 try templates.buffer.ensureUnusedCapacity(contents.len);
7478 for (contents) |c| {
7479 if (c == '$') {
7480 try templates.buffer.appendSlice(root_name);
7481 } else {
7482 try templates.buffer.append(c);
7483 }
7484 }
7485
7486 return out_dir.writeFile2(.{
7487 .sub_path = template_path,
7488 .data = templates.buffer.items,
7489 .flags = .{ .exclusive = true },
7490 });
7491 }
7492};
7493
7494fn findTemplates(gpa: Allocator, arena: Allocator) Templates {
7495 const self_exe_path = introspect.findZigExePath(arena) catch |err| {
7496 fatal("unable to find self exe path: {s}", .{@errorName(err)});
7497 };
7498 var zig_lib_directory = introspect.findZigLibDirFromSelfExe(arena, self_exe_path) catch |err| {
7499 fatal("unable to find zig installation directory: {s}", .{@errorName(err)});
7500 };
7501
7502 const s = fs.path.sep_str;
7503 const template_sub_path = "init";
7504 const template_dir = zig_lib_directory.handle.openDir(template_sub_path, .{}) catch |err| {
7505 const path = zig_lib_directory.path orelse ".";
7506 fatal("unable to open zig project template directory '{s}{s}{s}': {s}", .{
7507 path, s, template_sub_path, @errorName(err),
7508 });
7509 };
7510
7511 return .{
7512 .zig_lib_directory = zig_lib_directory,
7513 .dir = template_dir,
7514 .buffer = std.ArrayList(u8).init(gpa),
7515 };
7516}