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....@@ -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}
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}