From f645022d16361865e24582d28f1e62312fbc73bb Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 20 Nov 2023 23:01:45 -0700 Subject: [PATCH] merge `zig init-exe` and `zig init-lib` into `zig init` Instead of `zig init-lib` and `zig init-exe`, now there is only `zig init`, which initializes any of the template files that do not already exist, and makes a package that contains both an executable and a static library. The idea is that the user can delete whatever they don't want. In fact, I think even more things should be added to the build.zig template. --- lib/init-lib/build.zig | 47 -------- lib/{init-exe => init}/build.zig | 29 ++++- lib/{init-exe => init}/src/main.zig | 0 .../src/main.zig => init/src/root.zig} | 0 lib/std/fs.zig | 26 ++++- src/main.zig | 104 +++++++++--------- test/tests.zig | 37 ++----- 7 files changed, 102 insertions(+), 141 deletions(-) delete mode 100644 lib/init-lib/build.zig rename lib/{init-exe => init}/build.zig (78%) rename lib/{init-exe => init}/src/main.zig (100%) rename lib/{init-lib/src/main.zig => init/src/root.zig} (100%) diff --git a/lib/init-lib/build.zig b/lib/init-lib/build.zig deleted file mode 100644 index 70592d896d..0000000000 --- a/lib/init-lib/build.zig +++ /dev/null @@ -1,47 +0,0 @@ -const std = @import("std"); - -// Although this function looks imperative, note that its job is to -// declaratively construct a build graph that will be executed by an external -// runner. -pub fn build(b: *std.Build) void { - // Standard target options allows the person running `zig build` to choose - // what target to build for. Here we do not override the defaults, which - // means any target is allowed, and the default is native. Other options - // for restricting supported target set are available. - const target = b.standardTargetOptions(.{}); - - // Standard optimization options allow the person running `zig build` to select - // between Debug, ReleaseSafe, ReleaseFast, and ReleaseSmall. Here we do not - // set a preferred release mode, allowing the user to decide how to optimize. - const optimize = b.standardOptimizeOption(.{}); - - const lib = b.addStaticLibrary(.{ - .name = "$", - // In this case the main source file is merely a path, however, in more - // complicated build scripts, this could be a generated file. - .root_source_file = .{ .path = "src/main.zig" }, - .target = target, - .optimize = optimize, - }); - - // This declares intent for the library to be installed into the standard - // location when the user invokes the "install" step (the default step when - // running `zig build`). - b.installArtifact(lib); - - // Creates a step for unit testing. This only builds the test executable - // but does not run it. - const main_tests = b.addTest(.{ - .root_source_file = .{ .path = "src/main.zig" }, - .target = target, - .optimize = optimize, - }); - - const run_main_tests = b.addRunArtifact(main_tests); - - // This creates a build step. It will be visible in the `zig build --help` menu, - // and can be selected like this: `zig build test` - // This will evaluate the `test` step rather than the default, which is "install". - const test_step = b.step("test", "Run library tests"); - test_step.dependOn(&run_main_tests.step); -} diff --git a/lib/init-exe/build.zig b/lib/init/build.zig similarity index 78% rename from lib/init-exe/build.zig rename to lib/init/build.zig index 1221984190..e513acdf25 100644 --- a/lib/init-exe/build.zig +++ b/lib/init/build.zig @@ -15,10 +15,22 @@ pub fn build(b: *std.Build) void { // set a preferred release mode, allowing the user to decide how to optimize. const optimize = b.standardOptimizeOption(.{}); - const exe = b.addExecutable(.{ + const lib = b.addStaticLibrary(.{ .name = "$", // In this case the main source file is merely a path, however, in more // complicated build scripts, this could be a generated file. + .root_source_file = .{ .path = "src/root.zig" }, + .target = target, + .optimize = optimize, + }); + + // This declares intent for the library to be installed into the standard + // location when the user invokes the "install" step (the default step when + // running `zig build`). + b.installArtifact(lib); + + const exe = b.addExecutable(.{ + .name = "$", .root_source_file = .{ .path = "src/main.zig" }, .target = target, .optimize = optimize, @@ -54,17 +66,26 @@ pub fn build(b: *std.Build) void { // Creates a step for unit testing. This only builds the test executable // but does not run it. - const unit_tests = b.addTest(.{ + const lib_unit_tests = b.addTest(.{ + .root_source_file = .{ .path = "src/root.zig" }, + .target = target, + .optimize = optimize, + }); + + const run_lib_unit_tests = b.addRunArtifact(lib_unit_tests); + + const exe_unit_tests = b.addTest(.{ .root_source_file = .{ .path = "src/main.zig" }, .target = target, .optimize = optimize, }); - const run_unit_tests = b.addRunArtifact(unit_tests); + const run_exe_unit_tests = b.addRunArtifact(exe_unit_tests); // Similar to creating the run step earlier, this exposes a `test` step to // the `zig build --help` menu, providing a way for the user to request // running the unit tests. const test_step = b.step("test", "Run unit tests"); - test_step.dependOn(&run_unit_tests.step); + test_step.dependOn(&run_lib_unit_tests.step); + test_step.dependOn(&run_exe_unit_tests.step); } diff --git a/lib/init-exe/src/main.zig b/lib/init/src/main.zig similarity index 100% rename from lib/init-exe/src/main.zig rename to lib/init/src/main.zig diff --git a/lib/init-lib/src/main.zig b/lib/init/src/root.zig similarity index 100% rename from lib/init-lib/src/main.zig rename to lib/init/src/root.zig diff --git a/lib/std/fs.zig b/lib/std/fs.zig index fd8116edaa..e2f4419eda 100644 --- a/lib/std/fs.zig +++ b/lib/std/fs.zig @@ -2562,12 +2562,28 @@ pub const Dir = struct { }; } - /// Writes content to the file system, creating a new file if it does not exist, truncating - /// if it already exists. - pub fn writeFile(self: Dir, sub_path: []const u8, data: []const u8) !void { - var file = try self.createFile(sub_path, .{}); + pub const WriteFileError = File.WriteError || File.OpenError; + + /// Deprecated: use `writeFile2`. + pub fn writeFile(self: Dir, sub_path: []const u8, data: []const u8) WriteFileError!void { + return writeFile2(self, .{ + .sub_path = sub_path, + .data = data, + .flags = .{}, + }); + } + + pub const WriteFileOptions = struct { + sub_path: []const u8, + data: []const u8, + flags: File.CreateFlags = .{}, + }; + + /// Writes content to the file system, using the file creation flags provided. + pub fn writeFile2(self: Dir, options: WriteFileOptions) WriteFileError!void { + var file = try self.createFile(options.sub_path, options.flags); defer file.close(); - try file.writeAll(data); + try file.writeAll(options.data); } pub const AccessError = os.AccessError; diff --git a/src/main.zig b/src/main.zig index 6bab14df56..954e00e7f1 100644 --- a/src/main.zig +++ b/src/main.zig @@ -86,8 +86,7 @@ const normal_usage = \\ \\ build Build project from build.zig \\ fetch Copy a package into global cache and print its hash - \\ init-exe Initialize a `zig build` application in the cwd - \\ init-lib Initialize a `zig build` library in the cwd + \\ init Initialize a Zig package in the current directory \\ \\ ast-check Look for simple compile errors in any set of files \\ build-exe Create executable from source or object files @@ -320,10 +319,8 @@ pub fn mainArgs(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi return cmdFetch(gpa, arena, cmd_args); } else if (mem.eql(u8, cmd, "libc")) { return cmdLibC(gpa, cmd_args); - } else if (mem.eql(u8, cmd, "init-exe")) { - return cmdInit(gpa, arena, cmd_args, .Exe); - } else if (mem.eql(u8, cmd, "init-lib")) { - return cmdInit(gpa, arena, cmd_args, .Lib); + } else if (mem.eql(u8, cmd, "init")) { + return cmdInit(gpa, arena, cmd_args); } else if (mem.eql(u8, cmd, "targets")) { const info = try detectNativeTargetInfo(.{}); const stdout = io.getStdOut().writer(); @@ -4835,8 +4832,7 @@ pub fn cmdLibC(gpa: Allocator, args: []const []const u8) !void { } pub const usage_init = - \\Usage: zig init-exe - \\ zig init-lib + \\Usage: zig init \\ \\ Initializes a `zig build` project in the current working \\ directory. @@ -4847,12 +4843,7 @@ pub const usage_init = \\ ; -pub fn cmdInit( - gpa: Allocator, - arena: Allocator, - args: []const []const u8, - output_mode: std.builtin.OutputMode, -) !void { +pub fn cmdInit(gpa: Allocator, arena: Allocator, args: []const []const u8) !void { _ = gpa; { var i: usize = 0; @@ -4877,14 +4868,12 @@ pub fn cmdInit( defer zig_lib_directory.handle.close(); const s = fs.path.sep_str; - const template_sub_path = switch (output_mode) { - .Obj => unreachable, - .Lib => "init-lib", - .Exe => "init-exe", - }; + const template_sub_path = "init"; var template_dir = zig_lib_directory.handle.openDir(template_sub_path, .{}) catch |err| { const path = zig_lib_directory.path orelse "."; - fatal("unable to open zig project template directory '{s}{s}{s}': {s}", .{ path, s, template_sub_path, @errorName(err) }); + fatal("unable to open zig project template directory '{s}{s}{s}': {s}", .{ + path, s, template_sub_path, @errorName(err), + }); }; defer template_dir.close(); @@ -4892,46 +4881,51 @@ pub fn cmdInit( const cwd_basename = fs.path.basename(cwd_path); const max_bytes = 10 * 1024 * 1024; - const build_zig_contents = template_dir.readFileAlloc(arena, "build.zig", max_bytes) catch |err| { - fatal("unable to read template file 'build.zig': {s}", .{@errorName(err)}); + const template_paths = [_][]const u8{ + "build.zig", + "src" ++ s ++ "main.zig", + "src" ++ s ++ "root.zig", }; - var modified_build_zig_contents = try std.ArrayList(u8).initCapacity(arena, build_zig_contents.len); - for (build_zig_contents) |c| { - if (c == '$') { - try modified_build_zig_contents.appendSlice(cwd_basename); - } else { - try modified_build_zig_contents.append(c); + var ok_count: usize = 0; + + for (template_paths) |template_path| { + if (fs.path.dirname(template_path)) |dirname| { + fs.cwd().makePath(dirname) catch |err| { + fatal("unable to make path '{s}': {s}", .{ dirname, @errorName(err) }); + }; + } + + const contents = template_dir.readFileAlloc(arena, template_path, max_bytes) catch |err| { + fatal("unable to read template file '{s}': {s}", .{ template_path, @errorName(err) }); + }; + var modified_contents = try std.ArrayList(u8).initCapacity(arena, contents.len); + for (contents) |c| { + if (c == '$') { + try modified_contents.appendSlice(cwd_basename); + } else { + try modified_contents.append(c); + } + } + + if (fs.cwd().writeFile2(.{ + .sub_path = template_path, + .data = modified_contents.items, + .flags = .{ .exclusive = true }, + })) |_| { + std.log.info("created {s}", .{template_path}); + ok_count += 1; + } else |err| switch (err) { + error.PathAlreadyExists => std.log.info("preserving already existing file: {s}", .{ + template_path, + }), + else => std.log.err("unable to write {s}: {s}\n", .{ template_path, @errorName(err) }), } } - const main_zig_contents = template_dir.readFileAlloc(arena, "src" ++ s ++ "main.zig", max_bytes) catch |err| { - fatal("unable to read template file 'main.zig': {s}", .{@errorName(err)}); - }; - if (fs.cwd().access("build.zig", .{})) |_| { - fatal("existing build.zig file would be overwritten", .{}); - } else |err| switch (err) { - error.FileNotFound => {}, - else => fatal("unable to test existence of build.zig: {s}\n", .{@errorName(err)}), - } - if (fs.cwd().access("src" ++ s ++ "main.zig", .{})) |_| { - fatal("existing src" ++ s ++ "main.zig file would be overwritten", .{}); - } else |err| switch (err) { - error.FileNotFound => {}, - else => fatal("unable to test existence of src" ++ s ++ "main.zig: {s}\n", .{@errorName(err)}), - } - var src_dir = try fs.cwd().makeOpenPath("src", .{}); - defer src_dir.close(); - try src_dir.writeFile("main.zig", main_zig_contents); - try fs.cwd().writeFile("build.zig", modified_build_zig_contents.items); - - std.log.info("Created build.zig", .{}); - std.log.info("Created src" ++ s ++ "main.zig", .{}); - - switch (output_mode) { - .Lib => std.log.info("Next, try `zig build --help` or `zig build test`", .{}), - .Exe => std.log.info("Next, try `zig build --help` or `zig build run`", .{}), - .Obj => unreachable, + if (ok_count == template_paths.len) { + std.log.info("see `zig build --help` for a menu of options", .{}); } + return cleanExit(); } pub const usage_build = diff --git a/test/tests.zig b/test/tests.zig index 2603ca7670..c19e8647de 100644 --- a/test/tests.zig +++ b/test/tests.zig @@ -776,39 +776,16 @@ pub fn addCliTests(b: *std.Build) *Step { const s = std.fs.path.sep_str; { - - // Test `zig init-lib`. + // Test `zig init`. const tmp_path = b.makeTempPath(); - const init_lib = b.addSystemCommand(&.{ b.zig_exe, "init-lib" }); - init_lib.setCwd(.{ .cwd_relative = tmp_path }); - init_lib.setName("zig init-lib"); - init_lib.expectStdOutEqual(""); - init_lib.expectStdErrEqual("info: Created build.zig\n" ++ - "info: Created src" ++ s ++ "main.zig\n" ++ - "info: Next, try `zig build --help` or `zig build test`\n"); - - const run_test = b.addSystemCommand(&.{ b.zig_exe, "build", "test" }); - run_test.setCwd(.{ .cwd_relative = tmp_path }); - run_test.setName("zig build test"); - run_test.expectStdOutEqual(""); - run_test.step.dependOn(&init_lib.step); - - const cleanup = b.addRemoveDirTree(tmp_path); - cleanup.step.dependOn(&run_test.step); - - step.dependOn(&cleanup.step); - } - - { - // Test `zig init-exe`. - const tmp_path = b.makeTempPath(); - const init_exe = b.addSystemCommand(&.{ b.zig_exe, "init-exe" }); + const init_exe = b.addSystemCommand(&.{ b.zig_exe, "init" }); init_exe.setCwd(.{ .cwd_relative = tmp_path }); - init_exe.setName("zig init-exe"); + init_exe.setName("zig init"); init_exe.expectStdOutEqual(""); - init_exe.expectStdErrEqual("info: Created build.zig\n" ++ - "info: Created src" ++ s ++ "main.zig\n" ++ - "info: Next, try `zig build --help` or `zig build run`\n"); + init_exe.expectStdErrEqual("info: created build.zig\n" ++ + "info: created src" ++ s ++ "main.zig\n" ++ + "info: created src" ++ s ++ "root.zig\n" ++ + "info: see `zig build --help` for a menu of options\n"); // Test missing output path. const bad_out_arg = "-femit-bin=does" ++ s ++ "not" ++ s ++ "exist" ++ s ++ "foo.exe";