diff --git a/lib/init-exe/build.zig b/lib/init-exe/build.zig deleted file mode 100644 index 1221984190c22d362f21a6f45098a8351a960bae..0000000000000000000000000000000000000000 --- a/lib/init-exe/build.zig +++ /dev/null @@ -1,70 +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 exe = b.addExecutable(.{ - .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 executable to be installed into the - // standard location when the user invokes the "install" step (the default - // step when running `zig build`). - b.installArtifact(exe); - - // This *creates* a Run step in the build graph, to be executed when another - // step is evaluated that depends on it. The next line below will establish - // such a dependency. - const run_cmd = b.addRunArtifact(exe); - - // By making the run step depend on the install step, it will be run from the - // installation directory rather than directly from within the cache directory. - // This is not necessary, however, if the application depends on other installed - // files, this ensures they will be present and in the expected location. - run_cmd.step.dependOn(b.getInstallStep()); - - // This allows the user to pass arguments to the application in the build - // command itself, like this: `zig build run -- arg1 arg2 etc` - if (b.args) |args| { - run_cmd.addArgs(args); - } - - // This creates a build step. It will be visible in the `zig build --help` menu, - // and can be selected like this: `zig build run` - // This will evaluate the `run` step rather than the default, which is "install". - const run_step = b.step("run", "Run the app"); - run_step.dependOn(&run_cmd.step); - - // Creates a step for unit testing. This only builds the test executable - // but does not run it. - const unit_tests = b.addTest(.{ - .root_source_file = .{ .path = "src/main.zig" }, - .target = target, - .optimize = optimize, - }); - - const run_unit_tests = b.addRunArtifact(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); -} diff --git a/lib/init-exe/src/main.zig b/lib/init-exe/src/main.zig deleted file mode 100644 index c8a3f67dd08ec6e7e58b6b3f27b20dc9dcb2de0b..0000000000000000000000000000000000000000 --- a/lib/init-exe/src/main.zig +++ /dev/null @@ -1,24 +0,0 @@ -const std = @import("std"); - -pub fn main() !void { - // Prints to stderr (it's a shortcut based on `std.io.getStdErr()`) - std.debug.print("All your {s} are belong to us.\n", .{"codebase"}); - - // stdout is for the actual output of your application, for example if you - // are implementing gzip, then only the compressed bytes should be sent to - // stdout, not any debugging messages. - const stdout_file = std.io.getStdOut().writer(); - var bw = std.io.bufferedWriter(stdout_file); - const stdout = bw.writer(); - - try stdout.print("Run `zig build test` to run the tests.\n", .{}); - - try bw.flush(); // don't forget to flush! -} - -test "simple test" { - var list = std.ArrayList(i32).init(std.testing.allocator); - defer list.deinit(); // try commenting this out and see if zig detects the memory leak! - try list.append(42); - try std.testing.expectEqual(@as(i32, 42), list.pop()); -} diff --git a/lib/init-lib/build.zig b/lib/init-lib/build.zig deleted file mode 100644 index 70592d896d302c7ec599a18e315bb88d22a310e5..0000000000000000000000000000000000000000 --- 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-lib/src/main.zig b/lib/init-lib/src/main.zig deleted file mode 100644 index ecfeade1a3ac1a5959545293ac3f34625b4743ce..0000000000000000000000000000000000000000 --- a/lib/init-lib/src/main.zig +++ /dev/null @@ -1,10 +0,0 @@ -const std = @import("std"); -const testing = std.testing; - -export fn add(a: i32, b: i32) i32 { - return a + b; -} - -test "basic add functionality" { - try testing.expect(add(3, 7) == 10); -} diff --git a/lib/init/build.zig b/lib/init/build.zig new file mode 100644 index 0000000000000000000000000000000000000000..e513acdf254e4a7774b5208ebb4fc399ca5b2bd1 --- /dev/null +++ b/lib/init/build.zig @@ -0,0 +1,91 @@ +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/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, + }); + + // This declares intent for the executable to be installed into the + // standard location when the user invokes the "install" step (the default + // step when running `zig build`). + b.installArtifact(exe); + + // This *creates* a Run step in the build graph, to be executed when another + // step is evaluated that depends on it. The next line below will establish + // such a dependency. + const run_cmd = b.addRunArtifact(exe); + + // By making the run step depend on the install step, it will be run from the + // installation directory rather than directly from within the cache directory. + // This is not necessary, however, if the application depends on other installed + // files, this ensures they will be present and in the expected location. + run_cmd.step.dependOn(b.getInstallStep()); + + // This allows the user to pass arguments to the application in the build + // command itself, like this: `zig build run -- arg1 arg2 etc` + if (b.args) |args| { + run_cmd.addArgs(args); + } + + // This creates a build step. It will be visible in the `zig build --help` menu, + // and can be selected like this: `zig build run` + // This will evaluate the `run` step rather than the default, which is "install". + const run_step = b.step("run", "Run the app"); + run_step.dependOn(&run_cmd.step); + + // Creates a step for unit testing. This only builds the test executable + // but does not run it. + 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_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_lib_unit_tests.step); + test_step.dependOn(&run_exe_unit_tests.step); +} diff --git a/lib/init/build.zig.zon b/lib/init/build.zig.zon new file mode 100644 index 0000000000000000000000000000000000000000..a16fa12185445ab1867c24d875de5b67f3b1e772 --- /dev/null +++ b/lib/init/build.zig.zon @@ -0,0 +1,61 @@ +.{ + .name = "$", + // This is a [Semantic Version](https://semver.org/). + // In a future version of Zig it will be used for package deduplication. + .version = "0.0.0", + + // This field is optional. + // This is currently advisory only; Zig does not yet do anything + // with this value. + //.minimum_zig_version = "0.11.0", + + // This field is optional. + // Each dependency must either provide a `url` and `hash`, or a `path`. + // `zig build --fetch` can be used to fetch all dependencies of a package, recursively. + // Once all dependencies are fetched, `zig build` no longer requires + // Internet connectivity. + .dependencies = .{ + // A future version of Zig will provide a `zig add ` subcommand + // for easily adding dependencies. + //.example = .{ + // // When updating this field to a new URL, be sure to delete the corresponding + // // `hash`, otherwise you are communicating that you expect to find the old hash at + // // the new URL. + // .url = "https://example.com/foo.tar.gz", + // + // // This is computed from the file contents of the directory of files that is + // // obtained after fetching `url` and applying the inclusion rules given by + // // `paths`. + // // + // // This field is the source of truth; packages do not come from an `url`; they + // // come from a `hash`. `url` is just one of many possible mirrors for how to + // // obtain a package matching this `hash`. + // // + // // Uses the [multihash](https://multiformats.io/multihash/) format. + // .hash = "...", + // + // // When this is provided, the package is found in a directory relative to the + // // build root. In this case the package's hash is irrelevant and therefore not + // // computed. + // .path = "foo", + //}, + }, + + // Specifies the set of files and directories that are included in this package. + // Only files and directories listed here are included in the `hash` that + // is computed for this package. + // Paths are relative to the build root. Use the empty string (`""`) to refer to + // the build root itself. + // A directory listed here means that all files within, recursively, are included. + .paths = .{ + // This makes *all* files, recursively, included in this package. It is generally + // better to explicitly list the files and directories instead, to insure that + // fetching from tarballs, file system paths, and version control all result + // in the same contents hash. + "", + // For example... + //"build.zig", + //"build.zig.zon", + //"src", + }, +} diff --git a/lib/init/src/main.zig b/lib/init/src/main.zig new file mode 100644 index 0000000000000000000000000000000000000000..c8a3f67dd08ec6e7e58b6b3f27b20dc9dcb2de0b --- /dev/null +++ b/lib/init/src/main.zig @@ -0,0 +1,24 @@ +const std = @import("std"); + +pub fn main() !void { + // Prints to stderr (it's a shortcut based on `std.io.getStdErr()`) + std.debug.print("All your {s} are belong to us.\n", .{"codebase"}); + + // stdout is for the actual output of your application, for example if you + // are implementing gzip, then only the compressed bytes should be sent to + // stdout, not any debugging messages. + const stdout_file = std.io.getStdOut().writer(); + var bw = std.io.bufferedWriter(stdout_file); + const stdout = bw.writer(); + + try stdout.print("Run `zig build test` to run the tests.\n", .{}); + + try bw.flush(); // don't forget to flush! +} + +test "simple test" { + var list = std.ArrayList(i32).init(std.testing.allocator); + defer list.deinit(); // try commenting this out and see if zig detects the memory leak! + try list.append(42); + try std.testing.expectEqual(@as(i32, 42), list.pop()); +} diff --git a/lib/init/src/root.zig b/lib/init/src/root.zig new file mode 100644 index 0000000000000000000000000000000000000000..ecfeade1a3ac1a5959545293ac3f34625b4743ce --- /dev/null +++ b/lib/init/src/root.zig @@ -0,0 +1,10 @@ +const std = @import("std"); +const testing = std.testing; + +export fn add(a: i32, b: i32) i32 { + return a + b; +} + +test "basic add functionality" { + try testing.expect(add(3, 7) == 10); +} diff --git a/lib/std/fs.zig b/lib/std/fs.zig index 97d689484e4f8ddea12f4659d0df0dae672bc5e4..8ae98b54f6fd43944be5dac7ec2949088fb069ed 100644 --- a/lib/std/fs.zig +++ b/lib/std/fs.zig @@ -2564,12 +2564,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 6bab14df567ccc8225940fa8466ebcc976675de7..8a01ef4cdb5de66fb800aa488c08473665233869 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,52 @@ 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", + "build.zig.zon", + "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 = diff --git a/test/tests.zig b/test/tests.zig index 2603ca767047b60fbb8975a332e6352639cdc951..0bdc15765aa2ec4868e1e297d5355d711f02a531 100644 --- a/test/tests.zig +++ b/test/tests.zig @@ -776,39 +776,17 @@ 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 build.zig.zon\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";