authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2023-11-26 17:04:28-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2023-11-26 19:41:00-07:00
log0c0b69891ad0461a7cd14b09a68def350fbf128e
tree3df20c1ee3425f3c93cdd70ecf411be43dafdadc
parenta0c8d54823e2fd41a9bb2a917d97de667dc9f427

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

``` --save Add the fetched package to build.zig.zon --save=[name] Add the fetched package to build.zig.zon as name ```

3 files changed, 347 insertions(+), 111 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}
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}