Commit f645022d16

Andrew Kelley <andrew@ziglang.org>
2023-11-21 07:01:45
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.
1 parent 8ca4a52
Changed files (7)
lib/init-exe/src/main.zig โ†’ lib/init/src/main.zig
File renamed without changes
lib/init-lib/src/main.zig โ†’ lib/init/src/root.zig
File renamed without changes
lib/init-exe/build.zig โ†’ 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);
 }
lib/init-lib/build.zig
@@ -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);
-}
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;
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 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);
+        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);
+            }
+        }
 
-    std.log.info("Created build.zig", .{});
-    std.log.info("Created src" ++ s ++ "main.zig", .{});
+        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) }),
+        }
+    }
 
-    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 =
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`.
-        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`.
+        // Test `zig init`.
         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";