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.
6161
6262When this is provided, the package is found in a directory relative to the
6363build root. In this case the package's hash is irrelevant and therefore not
64computed.
64computed. This field and `url` are mutually exclusive.
6565
6666### `paths`
6767
lib/init/build.zig.zon+4-3
......@@ -15,8 +15,7 @@
1515 // Once all dependencies are fetched, `zig build` no longer requires
1616 // Internet connectivity.
1717 .dependencies = .{
18 // A future version of Zig will provide a `zig add <url>` subcommand
19 // for easily adding dependencies.
18 // See `zig fetch --save <url>` for a command-line interface for adding dependencies.
2019 //.example = .{
2120 // // When updating this field to a new URL, be sure to delete the corresponding
2221 // // `hash`, otherwise you are communicating that you expect to find the old hash at
......@@ -36,7 +35,7 @@
3635 //
3736 // // When this is provided, the package is found in a directory relative to the
3837 // // 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.
4039 // .path = "foo",
4140 //},
4241 },
......@@ -57,5 +56,7 @@
5756 //"build.zig",
5857 //"build.zig.zon",
5958 //"src",
59 //"LICENSE",
60 //"README.md",
6061 },
6162}
lib/std/zig/render.zig+20-4
......@@ -26,6 +26,8 @@ pub const Fixups = struct {
2626 omit_nodes: std.AutoHashMapUnmanaged(Ast.Node.Index, void) = .{},
2727 /// These expressions will be replaced with the string value.
2828 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) = .{},
2931 /// These nodes will be replaced with a different node.
3032 replace_nodes_with_node: std.AutoHashMapUnmanaged(Ast.Node.Index, Ast.Node.Index) = .{},
3133 /// Change all identifier names matching the key to be value instead.
......@@ -40,6 +42,7 @@ pub const Fixups = struct {
4042 f.gut_functions.count() +
4143 f.omit_nodes.count() +
4244 f.replace_nodes_with_string.count() +
45 f.append_string_after_node.count() +
4346 f.replace_nodes_with_node.count() +
4447 f.rename_identifiers.count() +
4548 @intFromBool(f.rebase_imported_paths != null);
......@@ -50,6 +53,7 @@ pub const Fixups = struct {
5053 f.gut_functions.clearRetainingCapacity();
5154 f.omit_nodes.clearRetainingCapacity();
5255 f.replace_nodes_with_string.clearRetainingCapacity();
56 f.append_string_after_node.clearRetainingCapacity();
5357 f.replace_nodes_with_node.clearRetainingCapacity();
5458 f.rename_identifiers.clearRetainingCapacity();
5559
......@@ -61,6 +65,7 @@ pub const Fixups = struct {
6165 f.gut_functions.deinit(gpa);
6266 f.omit_nodes.deinit(gpa);
6367 f.replace_nodes_with_string.deinit(gpa);
68 f.append_string_after_node.deinit(gpa);
6469 f.replace_nodes_with_node.deinit(gpa);
6570 f.rename_identifiers.deinit(gpa);
6671 f.* = undefined;
......@@ -912,6 +917,16 @@ fn renderExpression(r: *Render, node: Ast.Node.Index, space: Space) Error!void {
912917 }
913918}
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
915930fn renderArrayType(
916931 r: *Render,
917932 array_type: Ast.full.ArrayType,
......@@ -2093,10 +2108,11 @@ fn renderStructInit(
20932108 // Don't output a space after the = if expression is a multiline string,
20942109 // since then it will start on the next line.
20952110 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];
20972113 var space_after_equal: Space = if (expr == .multiline_string_literal) .none else .space;
20982114 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
21012117 for (struct_init.ast.fields[1..]) |field_init| {
21022118 const init_token = tree.firstToken(field_init);
......@@ -2105,7 +2121,7 @@ fn renderStructInit(
21052121 try renderIdentifier(r, init_token - 2, .space, .eagerly_unquote); // name
21062122 space_after_equal = if (nodes[field_init] == .multiline_string_literal) .none else .space;
21072123 try renderToken(r, init_token - 1, space_after_equal); // =
2108 try renderExpression(r, field_init, .comma);
2124 try renderExpressionFixup(r, field_init, .comma);
21092125 }
21102126
21112127 ais.popIndent();
......@@ -2118,7 +2134,7 @@ fn renderStructInit(
21182134 try renderToken(r, init_token - 3, .none); // .
21192135 try renderIdentifier(r, init_token - 2, .space, .eagerly_unquote); // name
21202136 try renderToken(r, init_token - 1, .space); // =
2121 try renderExpression(r, field_init, .comma_space);
2137 try renderExpressionFixup(r, field_init, .comma_space);
21222138 }
21232139 }
21242140
src/Package/Fetch.zig+1-18
......@@ -520,24 +520,7 @@ fn loadManifest(f: *Fetch, pkg_root: Package.Path) RunError!void {
520520
521521 if (manifest.errors.len > 0) {
522522 const src_path = try eb.printString("{}{s}", .{ pkg_root, Manifest.basename });
523 const token_starts = ast.tokens.items(.start);
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 }
523 try manifest.copyErrorsIntoBundle(ast.*, src_path, eb);
541524 return error.FetchFailed;
542525 }
543526}
src/Package/Manifest.zig+44-5
......@@ -11,6 +11,7 @@ pub const Dependency = struct {
1111 location_tok: Ast.TokenIndex,
1212 hash: ?[]const u8,
1313 hash_tok: Ast.TokenIndex,
14 node: Ast.Node.Index,
1415
1516 pub const Location = union(enum) {
1617 url: []const u8,
......@@ -55,7 +56,9 @@ comptime {
5556
5657name: []const u8,
5758version: std.SemanticVersion,
59version_node: Ast.Node.Index,
5860dependencies: std.StringArrayHashMapUnmanaged(Dependency),
61dependencies_node: Ast.Node.Index,
5962paths: std.StringArrayHashMapUnmanaged(void),
6063minimum_zig_version: ?std.SemanticVersion,
6164
......@@ -68,7 +71,7 @@ pub const ParseOptions = struct {
6871
6972pub 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 {
7275 const node_tags = ast.nodes.items(.tag);
7376 const node_datas = ast.nodes.items(.data);
7477 assert(node_tags[0] == .root);
......@@ -85,7 +88,9 @@ pub fn parse(gpa: Allocator, ast: std.zig.Ast, options: ParseOptions) Error!Mani
8588
8689 .name = undefined,
8790 .version = undefined,
91 .version_node = 0,
8892 .dependencies = .{},
93 .dependencies_node = 0,
8994 .paths = .{},
9095 .allow_missing_paths_field = options.allow_missing_paths_field,
9196 .minimum_zig_version = null,
......@@ -104,7 +109,9 @@ pub fn parse(gpa: Allocator, ast: std.zig.Ast, options: ParseOptions) Error!Mani
104109 return .{
105110 .name = p.name,
106111 .version = p.version,
112 .version_node = p.version_node,
107113 .dependencies = try p.dependencies.clone(p.arena),
114 .dependencies_node = p.dependencies_node,
108115 .paths = try p.paths.clone(p.arena),
109116 .minimum_zig_version = p.minimum_zig_version,
110117 .errors = try p.arena.dupe(ErrorMessage, p.errors.items),
......@@ -117,6 +124,33 @@ pub fn deinit(man: *Manifest, gpa: Allocator) void {
117124 man.* = undefined;
118125}
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
120154const hex_charset = "0123456789abcdef";
121155
122156pub fn hex64(x: u64) [16]u8 {
......@@ -153,14 +187,16 @@ pub fn hexDigest(digest: Digest) MultiHashHexDigest {
153187
154188const Parse = struct {
155189 gpa: Allocator,
156 ast: std.zig.Ast,
190 ast: Ast,
157191 arena: Allocator,
158192 buf: std.ArrayListUnmanaged(u8),
159193 errors: std.ArrayListUnmanaged(ErrorMessage),
160194
161195 name: []const u8,
162196 version: std.SemanticVersion,
197 version_node: Ast.Node.Index,
163198 dependencies: std.StringArrayHashMapUnmanaged(Dependency),
199 dependencies_node: Ast.Node.Index,
164200 paths: std.StringArrayHashMapUnmanaged(void),
165201 allow_missing_paths_field: bool,
166202 minimum_zig_version: ?std.SemanticVersion,
......@@ -188,6 +224,7 @@ const Parse = struct {
188224 // things manually provides an opportunity to do any additional verification
189225 // that is desirable on a per-field basis.
190226 if (mem.eql(u8, field_name, "dependencies")) {
227 p.dependencies_node = field_init;
191228 try parseDependencies(p, field_init);
192229 } else if (mem.eql(u8, field_name, "paths")) {
193230 have_included_paths = true;
......@@ -196,6 +233,7 @@ const Parse = struct {
196233 p.name = try parseString(p, field_init);
197234 have_name = true;
198235 } else if (mem.eql(u8, field_name, "version")) {
236 p.version_node = field_init;
199237 const version_text = try parseString(p, field_init);
200238 p.version = std.SemanticVersion.parse(version_text) catch |err| v: {
201239 try appendError(p, main_tokens[field_init], "unable to parse semantic version: {s}", .{@errorName(err)});
......@@ -264,6 +302,7 @@ const Parse = struct {
264302 .location_tok = 0,
265303 .hash = null,
266304 .hash_tok = 0,
305 .node = node,
267306 };
268307 var has_location = false;
269308
......@@ -548,7 +587,7 @@ test "basic" {
548587 \\}
549588 ;
550589
551 var ast = try std.zig.Ast.parse(gpa, example, .zon);
590 var ast = try Ast.parse(gpa, example, .zon);
552591 defer ast.deinit(gpa);
553592
554593 try testing.expect(ast.errors.len == 0);
......@@ -591,7 +630,7 @@ test "minimum_zig_version" {
591630 \\}
592631 ;
593632
594 var ast = try std.zig.Ast.parse(gpa, example, .zon);
633 var ast = try Ast.parse(gpa, example, .zon);
595634 defer ast.deinit(gpa);
596635
597636 try testing.expect(ast.errors.len == 0);
......@@ -623,7 +662,7 @@ test "minimum_zig_version - invalid version" {
623662 \\}
624663 ;
625664
626 var ast = try std.zig.Ast.parse(gpa, example, .zon);
665 var ast = try Ast.parse(gpa, example, .zon);
627666 defer ast.deinit(gpa);
628667
629668 try testing.expect(ast.errors.len == 0);
src/main.zig+342-107
......@@ -1255,7 +1255,7 @@ fn buildOutputType(
12551255 override_lib_dir = args_iter.nextOrFatal();
12561256 } else if (mem.eql(u8, arg, "--debug-log")) {
12571257 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.", .{});
12591259 _ = args_iter.nextOrFatal();
12601260 } else {
12611261 try log_scopes.append(gpa, args_iter.nextOrFatal());
......@@ -1279,7 +1279,7 @@ fn buildOutputType(
12791279 listen = .stdio;
12801280 } else if (mem.eql(u8, arg, "--debug-link-snapshot")) {
12811281 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.", .{});
12831283 } else {
12841284 enable_link_snapshots = true;
12851285 }
......@@ -1558,7 +1558,7 @@ fn buildOutputType(
15581558 };
15591559 } else if (mem.eql(u8, arg, "--debug-compile-errors")) {
15601560 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.", .{});
15621562 } else {
15631563 debug_compile_errors = true;
15641564 }
......@@ -1662,7 +1662,7 @@ fn buildOutputType(
16621662 },
16631663 .def, .unknown => {
16641664 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'", .{});
16661666 }
16671667 fatal("unrecognized file extension of parameter '{s}'", .{arg});
16681668 },
......@@ -2793,7 +2793,7 @@ fn buildOutputType(
27932793 continue;
27942794 },
27952795 .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});
27972797 continue;
27982798 },
27992799 }
......@@ -4851,7 +4851,6 @@ pub const usage_init =
48514851;
48524852
48534853pub fn cmdInit(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {
4854 _ = gpa;
48554854 {
48564855 var i: usize = 0;
48574856 while (i < args.len) : (i += 1) {
......@@ -4868,58 +4867,24 @@ pub fn cmdInit(gpa: Allocator, arena: Allocator, args: []const []const u8) !void
48684867 }
48694868 }
48704869 }
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;
4878 const template_sub_path = "init";
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();
4871 var templates = findTemplates(gpa, arena);
4872 defer templates.deinit();
48864873
48874874 const cwd_path = try process.getCwdAlloc(arena);
48884875 const cwd_basename = fs.path.basename(cwd_path);
48894876
4890 const max_bytes = 10 * 1024 * 1024;
4877 const s = fs.path.sep_str;
48914878 const template_paths = [_][]const u8{
4892 "build.zig",
4893 "build.zig.zon",
4879 Package.build_zig_basename,
4880 Package.Manifest.basename,
48944881 "src" ++ s ++ "main.zig",
48954882 "src" ++ s ++ "root.zig",
48964883 };
48974884 var ok_count: usize = 0;
48984885
48994886 for (template_paths) |template_path| {
4900 if (fs.path.dirname(template_path)) |dirname| {
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 })) |_| {
4887 if (templates.write(arena, fs.cwd(), cwd_basename, template_path)) |_| {
49234888 std.log.info("created {s}", .{template_path});
49244889 ok_count += 1;
49254890 } else |err| switch (err) {
......@@ -5057,14 +5022,14 @@ pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi
50575022 try child_argv.appendSlice(args[i .. i + 2]);
50585023 i += 1;
50595024 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.", .{});
50615026 } else {
50625027 try log_scopes.append(gpa, args[i]);
50635028 }
50645029 continue;
50655030 } else if (mem.eql(u8, arg, "--debug-compile-errors")) {
50665031 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.", .{});
50685033 } else {
50695034 debug_compile_errors = true;
50705035 }
......@@ -5113,44 +5078,11 @@ pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi
51135078 defer if (cleanup_build_dir) |*dir| dir.close();
51145079
51155080 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;
5117 const build_root: Compilation.Directory = blk: {
5118 if (build_file) |bf| {
5119 if (fs.path.dirname(bf)) |dirname| {
5120 const dir = fs.cwd().openDir(dirname, .{}) catch |err| {
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;
5081 const build_root = try findBuildRoot(arena, .{
5082 .cwd_path = cwd_path,
5083 .build_file = build_file,
5084 });
5085 child_argv.items[argv_index_build_file] = build_root.directory.path orelse cwd_path;
51545086
51555087 var global_cache_directory: Compilation.Directory = l: {
51565088 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
51705102 .path = local_cache_dir_path,
51715103 };
51725104 }
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"});
51745106 break :l .{
5175 .handle = try build_root.handle.makeOpenPath("zig-cache", .{}),
5107 .handle = try build_root.directory.handle.makeOpenPath("zig-cache", .{}),
51765108 .path = cache_dir_path,
51775109 };
51785110 };
......@@ -5215,8 +5147,8 @@ pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi
52155147 };
52165148
52175149 var build_mod: Package.Module = .{
5218 .root = .{ .root_dir = build_root },
5219 .root_src_path = build_zig_basename,
5150 .root = .{ .root_dir = build_root.directory },
5151 .root_src_path = build_root.build_zig_basename,
52205152 .fully_qualified_name = "root.@build",
52215153 };
52225154 if (build_options.only_core_functionality) {
......@@ -6904,40 +6836,40 @@ const ClangSearchSanitizer = struct {
69046836 .I => {
69056837 if (m.I) return;
69066838 m.I = true;
6907 if (m.isystem) std.log.warn(wtxt, .{ dir, "I", "isystem" });
6908 if (m.idirafter) std.log.warn(wtxt, .{ dir, "I", "idirafter" });
6909 if (m.iframework) std.log.warn(wtxt, .{ dir, "I", "iframework" });
6839 if (m.isystem) warn(wtxt, .{ dir, "I", "isystem" });
6840 if (m.idirafter) warn(wtxt, .{ dir, "I", "idirafter" });
6841 if (m.iframework) warn(wtxt, .{ dir, "I", "iframework" });
69106842 },
69116843 .isystem => {
69126844 if (m.isystem) return;
69136845 m.isystem = true;
6914 if (m.I) std.log.warn(wtxt, .{ dir, "isystem", "I" });
6915 if (m.idirafter) std.log.warn(wtxt, .{ dir, "isystem", "idirafter" });
6916 if (m.iframework) std.log.warn(wtxt, .{ dir, "isystem", "iframework" });
6846 if (m.I) warn(wtxt, .{ dir, "isystem", "I" });
6847 if (m.idirafter) warn(wtxt, .{ dir, "isystem", "idirafter" });
6848 if (m.iframework) warn(wtxt, .{ dir, "isystem", "iframework" });
69176849 },
69186850 .iwithsysroot => {
69196851 if (m.iwithsysroot) return;
69206852 m.iwithsysroot = true;
6921 if (m.iframeworkwithsysroot) std.log.warn(wtxt, .{ dir, "iwithsysroot", "iframeworkwithsysroot" });
6853 if (m.iframeworkwithsysroot) warn(wtxt, .{ dir, "iwithsysroot", "iframeworkwithsysroot" });
69226854 },
69236855 .idirafter => {
69246856 if (m.idirafter) return;
69256857 m.idirafter = true;
6926 if (m.I) std.log.warn(wtxt, .{ dir, "idirafter", "I" });
6927 if (m.isystem) std.log.warn(wtxt, .{ dir, "idirafter", "isystem" });
6928 if (m.iframework) std.log.warn(wtxt, .{ dir, "idirafter", "iframework" });
6858 if (m.I) warn(wtxt, .{ dir, "idirafter", "I" });
6859 if (m.isystem) warn(wtxt, .{ dir, "idirafter", "isystem" });
6860 if (m.iframework) warn(wtxt, .{ dir, "idirafter", "iframework" });
69296861 },
69306862 .iframework => {
69316863 if (m.iframework) return;
69326864 m.iframework = true;
6933 if (m.I) std.log.warn(wtxt, .{ dir, "iframework", "I" });
6934 if (m.isystem) std.log.warn(wtxt, .{ dir, "iframework", "isystem" });
6935 if (m.idirafter) std.log.warn(wtxt, .{ dir, "iframework", "idirafter" });
6865 if (m.I) warn(wtxt, .{ dir, "iframework", "I" });
6866 if (m.isystem) warn(wtxt, .{ dir, "iframework", "isystem" });
6867 if (m.idirafter) warn(wtxt, .{ dir, "iframework", "idirafter" });
69366868 },
69376869 .iframeworkwithsysroot => {
69386870 if (m.iframeworkwithsysroot) return;
69396871 m.iframeworkwithsysroot = true;
6940 if (m.iwithsysroot) std.log.warn(wtxt, .{ dir, "iframeworkwithsysroot", "iwithsysroot" });
6872 if (m.iwithsysroot) warn(wtxt, .{ dir, "iframeworkwithsysroot", "iwithsysroot" });
69416873 },
69426874 }
69436875 try self.argv.append(arg);
......@@ -7097,6 +7029,8 @@ pub const usage_fetch =
70977029 \\ -h, --help Print this help and exit
70987030 \\ --global-cache-dir [path] Override path to global Zig cache directory
70997031 \\ --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
71007034 \\
71017035;
71027036
......@@ -7111,6 +7045,7 @@ fn cmdFetch(
71117045 var opt_path_or_url: ?[]const u8 = null;
71127046 var override_global_cache_dir: ?[]const u8 = try EnvVar.ZIG_GLOBAL_CACHE_DIR.get(arena);
71137047 var debug_hash: bool = false;
7048 var save: union(enum) { no, yes, name: []const u8 } = .no;
71147049
71157050 {
71167051 var i: usize = 0;
......@@ -7125,10 +7060,12 @@ fn cmdFetch(
71257060 if (i + 1 >= args.len) fatal("expected argument after '{s}'", .{arg});
71267061 i += 1;
71277062 override_global_cache_dir = args[i];
7128 continue;
71297063 } else if (mem.eql(u8, arg, "--debug-hash")) {
71307064 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..] };
71327069 } else {
71337070 fatal("unrecognized parameter: '{s}'", .{arg});
71347071 }
......@@ -7214,7 +7151,97 @@ fn cmdFetch(
72147151 progress.done = true;
72157152 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
72197246 return cleanExit();
72207247}
......@@ -7279,3 +7306,211 @@ fn defaultWasmEntryName(exec_model: ?std.builtin.WasiExecModel) []const u8 {
72797306 }
72807307 return "_start";
72817308}
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}