Commit 4c6f1e614a

Andrew Kelley <superjoe30@gmail.com>
2018-09-17 23:08:56
remove `zig build --init`. add `zig init-lib` and `zig init-exe`
init-lib creates a working static library with tests, and init-exe creates a working hello world with a `run` target. both now have test coverage with the new "cli tests" file. closes #1035
1 parent 9c9eefc
doc/langref.html.in
@@ -5877,7 +5877,7 @@ fn add(a: i32, b: i32) i32 {
       {#header_open|@OpaqueType#}
       <pre>{#syntax#}@OpaqueType() type{#endsyntax#}</pre>
       <p>
-      Creates a new type with an unknown size and alignment.
+      Creates a new type with an unknown (but non-zero) size and alignment.
       </p>
       <p>
       This is typically used for type safety when interacting with C code that does not expose struct details.
src/compiler.cpp
@@ -5,6 +5,9 @@
 static Buf saved_compiler_id = BUF_INIT;
 static Buf saved_app_data_dir = BUF_INIT;
 static Buf saved_stage1_path = BUF_INIT;
+static Buf saved_lib_dir = BUF_INIT;
+static Buf saved_special_dir = BUF_INIT;
+static Buf saved_std_dir = BUF_INIT;
 
 Buf *get_stage1_cache_path() {
     if (saved_stage1_path.list.length != 0) {
@@ -64,3 +67,101 @@ Error get_compiler_id(Buf **result) {
     return ErrorNone;
 }
 
+
+static bool test_zig_install_prefix(Buf *test_path, Buf *out_zig_lib_dir) {
+    Buf lib_buf = BUF_INIT;
+    buf_init_from_str(&lib_buf, "lib");
+
+    Buf zig_buf = BUF_INIT;
+    buf_init_from_str(&zig_buf, "zig");
+
+    Buf std_buf = BUF_INIT;
+    buf_init_from_str(&std_buf, "std");
+
+    Buf index_zig_buf = BUF_INIT;
+    buf_init_from_str(&index_zig_buf, "index.zig");
+
+    Buf test_lib_dir = BUF_INIT;
+    Buf test_zig_dir = BUF_INIT;
+    Buf test_std_dir = BUF_INIT;
+    Buf test_index_file = BUF_INIT;
+
+    os_path_join(test_path, &lib_buf, &test_lib_dir);
+    os_path_join(&test_lib_dir, &zig_buf, &test_zig_dir);
+    os_path_join(&test_zig_dir, &std_buf, &test_std_dir);
+    os_path_join(&test_std_dir, &index_zig_buf, &test_index_file);
+
+    int err;
+    bool exists;
+    if ((err = os_file_exists(&test_index_file, &exists))) {
+        exists = false;
+    }
+    if (exists) {
+        buf_init_from_buf(out_zig_lib_dir, &test_zig_dir);
+        return true;
+    }
+    return false;
+}
+
+static int find_zig_lib_dir(Buf *out_path) {
+    int err;
+
+    Buf self_exe_path = BUF_INIT;
+    buf_resize(&self_exe_path, 0);
+    if (!(err = os_self_exe_path(&self_exe_path))) {
+        Buf *cur_path = &self_exe_path;
+
+        for (;;) {
+            Buf *test_dir = buf_alloc();
+            os_path_dirname(cur_path, test_dir);
+
+            if (buf_eql_buf(test_dir, cur_path)) {
+                break;
+            }
+
+            if (test_zig_install_prefix(test_dir, out_path)) {
+                return 0;
+            }
+
+            cur_path = test_dir;
+        }
+    }
+
+    return ErrorFileNotFound;
+}
+
+Buf *get_zig_lib_dir(void) {
+    if (saved_lib_dir.list.length != 0) {
+        return &saved_lib_dir;
+    }
+    buf_resize(&saved_lib_dir, 0);
+
+    int err;
+    if ((err = find_zig_lib_dir(&saved_lib_dir))) {
+        fprintf(stderr, "Unable to find zig lib directory\n");
+        exit(EXIT_FAILURE);
+    }
+    return &saved_lib_dir;
+}
+
+Buf *get_zig_std_dir() {
+    if (saved_std_dir.list.length != 0) {
+        return &saved_std_dir;
+    }
+    buf_resize(&saved_std_dir, 0);
+
+    os_path_join(get_zig_lib_dir(), buf_create_from_str("std"), &saved_std_dir);
+
+    return &saved_std_dir;
+}
+
+Buf *get_zig_special_dir() {
+    if (saved_special_dir.list.length != 0) {
+        return &saved_special_dir;
+    }
+    buf_resize(&saved_special_dir, 0);
+
+    os_path_join(get_zig_std_dir(), buf_sprintf("special"), &saved_special_dir);
+
+    return &saved_special_dir;
+}
src/compiler.hpp
@@ -14,4 +14,8 @@
 Buf *get_stage1_cache_path();
 Error get_compiler_id(Buf **result);
 
+Buf *get_zig_lib_dir();
+Buf *get_zig_special_dir();
+Buf *get_zig_std_dir();
+
 #endif
src/main.cpp
@@ -25,6 +25,8 @@ static int usage(const char *arg0) {
         "  build-obj [source]           create object from source or assembly\n"
         "  builtin                      show the source code of that @import(\"builtin\")\n"
         "  id                           print the base64-encoded compiler id\n"
+        "  init-exe                     initialize a `zig build` application in the cwd\n"
+        "  init-lib                     initialize a `zig build` library in the cwd\n"
         "  run [source]                 create executable and run immediately\n"
         "  translate-c [source]         convert c code to zig code\n"
         "  targets                      list available compilation targets\n"
@@ -34,7 +36,7 @@ static int usage(const char *arg0) {
         "Compile Options:\n"
         "  --assembly [source]          add assembly file to build\n"
         "  --cache-dir [path]           override the cache directory\n"
-        "  --cache [auto|off|on]        build to the global cache and print output path to stdout\n"
+        "  --cache [auto|off|on]        build in global cache, print out paths to stdout\n"
         "  --color [auto|off|on]        enable or disable colored error messages\n"
         "  --emit [asm|bin|llvm-ir]     emit a specific file format as compilation output\n"
         "  -ftime-report                print timing diagnostics\n"
@@ -42,7 +44,7 @@ static int usage(const char *arg0) {
         "  --name [name]                override output name\n"
         "  --output [file]              override destination path\n"
         "  --output-h [file]            override generated header file path\n"
-        "  --pkg-begin [name] [path]    make package available to import and push current pkg\n"
+        "  --pkg-begin [name] [path]    make pkg available to import and push current pkg\n"
         "  --pkg-end                    pop current pkg\n"
         "  --release-fast               build with optimizations on and safety off\n"
         "  --release-safe               build with optimizations on and safety on\n"
@@ -52,17 +54,16 @@ static int usage(const char *arg0) {
         "  --target-arch [name]         specify target architecture\n"
         "  --target-environ [name]      specify target environment\n"
         "  --target-os [name]           specify target operating system\n"
-        "  --verbose-tokenize           turn on compiler debug output for tokenization\n"
-        "  --verbose-ast                turn on compiler debug output for parsing into an AST\n"
-        "  --verbose-link               turn on compiler debug output for linking\n"
-        "  --verbose-ir                 turn on compiler debug output for Zig IR\n"
-        "  --verbose-llvm-ir            turn on compiler debug output for LLVM IR\n"
-        "  --verbose-cimport            turn on compiler debug output for C imports\n"
+        "  --verbose-tokenize           enable compiler debug output for tokenization\n"
+        "  --verbose-ast                enable compiler debug output for AST parsing\n"
+        "  --verbose-link               enable compiler debug output for linking\n"
+        "  --verbose-ir                 enable compiler debug output for Zig IR\n"
+        "  --verbose-llvm-ir            enable compiler debug output for LLVM IR\n"
+        "  --verbose-cimport            enable compiler debug output for C imports\n"
         "  -dirafter [dir]              same as -isystem but do it last\n"
         "  -isystem [dir]               add additional search path for other .h files\n"
-        "  -mllvm [arg]                 additional arguments to forward to LLVM's option processing\n"
+        "  -mllvm [arg]                 forward an arg to LLVM's option processing\n"
         "Link Options:\n"
-        "  --ar-path [path]             set the path to ar\n"
         "  --dynamic-linker [path]      set the path to ld.so\n"
         "  --each-lib-rpath             add rpath for each used dynamic library\n"
         "  --libc-lib-dir [path]        directory where libc crt1.o resides\n"
@@ -142,78 +143,6 @@ static int print_target_list(FILE *f) {
     return EXIT_SUCCESS;
 }
 
-static bool test_zig_install_prefix(Buf *test_path, Buf *out_zig_lib_dir) {
-    Buf lib_buf = BUF_INIT;
-    buf_init_from_str(&lib_buf, "lib");
-
-    Buf zig_buf = BUF_INIT;
-    buf_init_from_str(&zig_buf, "zig");
-
-    Buf std_buf = BUF_INIT;
-    buf_init_from_str(&std_buf, "std");
-
-    Buf index_zig_buf = BUF_INIT;
-    buf_init_from_str(&index_zig_buf, "index.zig");
-
-    Buf test_lib_dir = BUF_INIT;
-    Buf test_zig_dir = BUF_INIT;
-    Buf test_std_dir = BUF_INIT;
-    Buf test_index_file = BUF_INIT;
-
-    os_path_join(test_path, &lib_buf, &test_lib_dir);
-    os_path_join(&test_lib_dir, &zig_buf, &test_zig_dir);
-    os_path_join(&test_zig_dir, &std_buf, &test_std_dir);
-    os_path_join(&test_std_dir, &index_zig_buf, &test_index_file);
-
-    int err;
-    bool exists;
-    if ((err = os_file_exists(&test_index_file, &exists))) {
-        exists = false;
-    }
-    if (exists) {
-        buf_init_from_buf(out_zig_lib_dir, &test_zig_dir);
-        return true;
-    }
-    return false;
-}
-
-static int find_zig_lib_dir(Buf *out_path) {
-    int err;
-
-    Buf self_exe_path = BUF_INIT;
-    buf_resize(&self_exe_path, 0);
-    if (!(err = os_self_exe_path(&self_exe_path))) {
-        Buf *cur_path = &self_exe_path;
-
-        for (;;) {
-            Buf *test_dir = buf_alloc();
-            os_path_dirname(cur_path, test_dir);
-
-            if (buf_eql_buf(test_dir, cur_path)) {
-                break;
-            }
-
-            if (test_zig_install_prefix(test_dir, out_path)) {
-                return 0;
-            }
-
-            cur_path = test_dir;
-        }
-    }
-
-    return ErrorFileNotFound;
-}
-
-static Buf *resolve_zig_lib_dir(void) {
-    int err;
-    Buf *result = buf_alloc();
-    if ((err = find_zig_lib_dir(result))) {
-        fprintf(stderr, "Unable to find zig lib directory\n");
-        exit(EXIT_FAILURE);
-    }
-    return result;
-}
-
 enum Cmd {
     CmdInvalid,
     CmdBuild,
@@ -277,6 +206,9 @@ static bool get_cache_opt(CacheOpt opt, bool default_value) {
 }
 
 int main(int argc, char **argv) {
+    char *arg0 = argv[0];
+    Error err;
+
     if (argc == 2 && strcmp(argv[1], "BUILD_INFO") == 0) {
         printf("%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n",
                 ZIG_CMAKE_BINARY_DIR,
@@ -290,8 +222,10 @@ int main(int argc, char **argv) {
         return 0;
     }
 
+    // Must be before all os.hpp function calls.
+    os_init();
+
     if (argc == 2 && strcmp(argv[1], "id") == 0) {
-        Error err;
         Buf *compiler_id;
         if ((err = get_compiler_id(&compiler_id))) {
             fprintf(stderr, "Unable to determine compiler id: %s\n", err_str(err));
@@ -301,9 +235,97 @@ int main(int argc, char **argv) {
         return EXIT_SUCCESS;
     }
 
-    os_init();
+    enum InitKind {
+        InitKindNone,
+        InitKindExe,
+        InitKindLib,
+    };
+    InitKind init_kind = InitKindNone;
+    if (argc >= 2) {
+        const char *init_cmd = argv[1];
+        if (strcmp(init_cmd, "init-exe") == 0) {
+            init_kind = InitKindExe;
+        } else if (strcmp(init_cmd, "init-lib") == 0) {
+            init_kind = InitKindLib;
+        }
+        if (init_kind != InitKindNone) {
+            if (argc >= 3) {
+                fprintf(stderr, "Unexpected extra argument: %s\n", argv[2]);
+                return usage(arg0);
+            }
+            Buf *cmd_template_path = buf_alloc();
+            os_path_join(get_zig_special_dir(), buf_create_from_str(init_cmd), cmd_template_path);
+            Buf *build_zig_path = buf_alloc();
+            os_path_join(cmd_template_path, buf_create_from_str("build.zig"), build_zig_path);
+            Buf *src_dir_path = buf_alloc();
+            os_path_join(cmd_template_path, buf_create_from_str("src"), src_dir_path);
+            Buf *main_zig_path = buf_alloc();
+            os_path_join(src_dir_path, buf_create_from_str("main.zig"), main_zig_path);
+
+            Buf *cwd = buf_alloc();
+            if ((err = os_get_cwd(cwd))) {
+                fprintf(stderr, "Unable to get cwd: %s\n", err_str(err));
+                return EXIT_FAILURE;
+            }
+            Buf *cwd_basename = buf_alloc();
+            os_path_split(cwd, nullptr, cwd_basename);
+
+            Buf *build_zig_contents = buf_alloc();
+            if ((err = os_fetch_file_path(build_zig_path, build_zig_contents, false))) {
+                fprintf(stderr, "Unable to read %s: %s\n", buf_ptr(build_zig_path), err_str(err));
+                return EXIT_FAILURE;
+            }
+            Buf *modified_build_zig_contents = buf_alloc();
+            for (size_t i = 0; i < buf_len(build_zig_contents); i += 1) {
+                char c = buf_ptr(build_zig_contents)[i];
+                if (c == '$') {
+                    buf_append_buf(modified_build_zig_contents, cwd_basename);
+                } else {
+                    buf_append_char(modified_build_zig_contents, c);
+                }
+            }
+
+            Buf *main_zig_contents = buf_alloc();
+            if ((err = os_fetch_file_path(main_zig_path, main_zig_contents, false))) {
+                fprintf(stderr, "Unable to read %s: %s\n", buf_ptr(main_zig_path), err_str(err));
+                return EXIT_FAILURE;
+            }
+
+            Buf *out_build_zig_path = buf_create_from_str("build.zig");
+            Buf *out_src_dir_path = buf_create_from_str("src");
+            Buf *out_main_zig_path = buf_alloc();
+            os_path_join(out_src_dir_path, buf_create_from_str("main.zig"), out_main_zig_path);
+
+            bool already_exists;
+            if ((err = os_file_exists(out_build_zig_path, &already_exists))) {
+                fprintf(stderr, "Unable test existence of %s: %s\n", buf_ptr(out_build_zig_path), err_str(err));
+                return EXIT_FAILURE;
+            }
+            if (already_exists) {
+                fprintf(stderr, "This file would be overwritten: %s\n", buf_ptr(out_build_zig_path));
+                return EXIT_FAILURE;
+            }
+
+            if ((err = os_make_dir(out_src_dir_path))) {
+                fprintf(stderr, "Unable to make directory: %s: %s\n", buf_ptr(out_src_dir_path), err_str(err));
+                return EXIT_FAILURE;
+            }
+            os_write_file(out_build_zig_path, modified_build_zig_contents);
+            os_write_file(out_main_zig_path, main_zig_contents);
+            fprintf(stderr, "Created %s\n", buf_ptr(out_build_zig_path));
+            fprintf(stderr, "Created %s\n", buf_ptr(out_main_zig_path));
+            if (init_kind == InitKindExe) {
+                fprintf(stderr, "\nNext, try `zig build --help` or `zig build run`\n");
+            } else if (init_kind == InitKindLib) {
+                fprintf(stderr, "\nNext, try `zig build --help` or `zig build test`\n");
+            } else {
+                zig_unreachable();
+            }
+
+            return EXIT_SUCCESS;
+        }
+    }
 
-    char *arg0 = argv[0];
     Cmd cmd = CmdInvalid;
     EmitFileType emit_file_type = EmitFileTypeBinary;
     const char *in_file = nullptr;
@@ -333,7 +355,6 @@ int main(int argc, char **argv) {
     ZigList<const char *> link_libs = {0};
     ZigList<const char *> forbidden_link_libs = {0};
     ZigList<const char *> frameworks = {0};
-    int err;
     const char *target_arch = nullptr;
     const char *target_os = nullptr;
     const char *target_environ = nullptr;
@@ -364,7 +385,6 @@ int main(int argc, char **argv) {
         const char *zig_exe_path = arg0;
         const char *build_file = "build.zig";
         bool asked_for_help = false;
-        bool asked_to_init = false;
 
         init_all_targets();
 
@@ -376,9 +396,6 @@ int main(int argc, char **argv) {
             if (strcmp(argv[i], "--help") == 0) {
                 asked_for_help = true;
                 args.append(argv[i]);
-            } else if (strcmp(argv[i], "--init") == 0) {
-                asked_to_init = true;
-                args.append(argv[i]);
             } else if (i + 1 < argc && strcmp(argv[i], "--build-file") == 0) {
                 build_file = argv[i + 1];
                 i += 1;
@@ -390,18 +407,10 @@ int main(int argc, char **argv) {
             }
         }
 
-        Buf *zig_lib_dir_buf = resolve_zig_lib_dir();
-
-        Buf *zig_std_dir = buf_alloc();
-        os_path_join(zig_lib_dir_buf, buf_create_from_str("std"), zig_std_dir);
-
-        Buf *special_dir = buf_alloc();
-        os_path_join(zig_std_dir, buf_sprintf("special"), special_dir);
-
         Buf *build_runner_path = buf_alloc();
-        os_path_join(special_dir, buf_create_from_str("build_runner.zig"), build_runner_path);
+        os_path_join(get_zig_special_dir(), buf_create_from_str("build_runner.zig"), build_runner_path);
 
-        CodeGen *g = codegen_create(build_runner_path, nullptr, OutTypeExe, BuildModeDebug, zig_lib_dir_buf);
+        CodeGen *g = codegen_create(build_runner_path, nullptr, OutTypeExe, BuildModeDebug, get_zig_lib_dir());
         g->enable_time_report = timing_info;
         buf_init_from_str(&g->cache_dir, cache_dir ? cache_dir : default_zig_cache_name);
         codegen_set_out_name(g, buf_create_from_str("build"));
@@ -437,7 +446,6 @@ int main(int argc, char **argv) {
                         "\n"
                         "General Options:\n"
                         "  --help                 Print this help and exit\n"
-                        "  --init                 Generate a build.zig template\n"
                         "  --build-file [file]    Override path to build.zig\n"
                         "  --cache-dir [path]     Override path to cache directory\n"
                         "  --verbose              Print commands before executing them\n"
@@ -463,22 +471,11 @@ int main(int argc, char **argv) {
                         "\n"
                 , zig_exe_path);
                 return EXIT_SUCCESS;
-            } else if (asked_to_init) {
-                Buf *build_template_path = buf_alloc();
-                os_path_join(special_dir, buf_create_from_str("build_file_template.zig"), build_template_path);
-
-                if ((err = os_copy_file(build_template_path, &build_file_abs))) {
-                    fprintf(stderr, "Unable to write build.zig template: %s\n", err_str(err));
-                    return EXIT_FAILURE;
-                } else {
-                    fprintf(stderr, "Wrote build.zig template\n");
-                    return EXIT_SUCCESS;
-                }
             }
 
             fprintf(stderr,
                     "No 'build.zig' file found.\n"
-                    "Initialize a 'build.zig' template file with `zig build --init`,\n"
+                    "Initialize a 'build.zig' template file with `zig init-lib` or `zig init-exe`,\n"
                     "or build an executable directly with `zig build-exe $FILENAME.zig`.\n"
                     "See: `zig build --help` or `zig help` for more options.\n"
                    );
@@ -773,8 +770,7 @@ int main(int argc, char **argv) {
 
     switch (cmd) {
     case CmdBuiltin: {
-        Buf *zig_lib_dir_buf = resolve_zig_lib_dir();
-        CodeGen *g = codegen_create(nullptr, target, out_type, build_mode, zig_lib_dir_buf);
+        CodeGen *g = codegen_create(nullptr, target, out_type, build_mode, get_zig_lib_dir());
         Buf *builtin_source = codegen_generate_builtin_source(g);
         if (fwrite(buf_ptr(builtin_source), 1, buf_len(builtin_source), stdout) != buf_len(builtin_source)) {
             fprintf(stderr, "unable to write to stdout: %s\n", strerror(ferror(stdout)));
@@ -832,9 +828,7 @@ int main(int argc, char **argv) {
             if (cmd == CmdRun && buf_out_name == nullptr) {
                 buf_out_name = buf_create_from_str("run");
             }
-            Buf *zig_lib_dir_buf = resolve_zig_lib_dir();
-
-            CodeGen *g = codegen_create(zig_root_source_file, target, out_type, build_mode, zig_lib_dir_buf);
+            CodeGen *g = codegen_create(zig_root_source_file, target, out_type, build_mode, get_zig_lib_dir());
             g->enable_time_report = timing_info;
             buf_init_from_str(&g->cache_dir, cache_dir ? cache_dir : default_zig_cache_name);
             codegen_set_out_name(g, buf_out_name);
src/os.cpp
@@ -1091,14 +1091,14 @@ Error os_fetch_file_path(Buf *full_path, Buf *out_contents, bool skip_shebang) {
     return result;
 }
 
-int os_get_cwd(Buf *out_cwd) {
+Error os_get_cwd(Buf *out_cwd) {
 #if defined(ZIG_OS_WINDOWS)
     char buf[4096];
     if (GetCurrentDirectory(4096, buf) == 0) {
         zig_panic("GetCurrentDirectory failed");
     }
     buf_init_from_str(out_cwd, buf);
-    return 0;
+    return ErrorNone;
 #elif defined(ZIG_OS_POSIX)
     char buf[PATH_MAX];
     char *res = getcwd(buf, PATH_MAX);
@@ -1106,7 +1106,7 @@ int os_get_cwd(Buf *out_cwd) {
         zig_panic("unable to get cwd: %s", strerror(errno));
     }
     buf_init_from_str(out_cwd, res);
-    return 0;
+    return ErrorNone;
 #else
 #error "missing os_get_cwd implementation"
 #endif
src/os.hpp
@@ -114,7 +114,7 @@ int os_copy_file(Buf *src_path, Buf *dest_path);
 Error ATTRIBUTE_MUST_USE os_fetch_file(FILE *file, Buf *out_contents, bool skip_shebang);
 Error ATTRIBUTE_MUST_USE os_fetch_file_path(Buf *full_path, Buf *out_contents, bool skip_shebang);
 
-int os_get_cwd(Buf *out_cwd);
+Error ATTRIBUTE_MUST_USE os_get_cwd(Buf *out_cwd);
 
 bool os_stderr_tty(void);
 void os_stderr_set_color(TermColor color);
std/special/init-exe/src/main.zig
@@ -0,0 +1,5 @@
+const std = @import("std");
+
+pub fn main() error!void {
+    std.debug.warn("All your base are belong to us.\n");
+}
std/special/init-exe/build.zig
@@ -0,0 +1,15 @@
+const Builder = @import("std").build.Builder;
+
+pub fn build(b: *Builder) void {
+    const mode = b.standardReleaseOptions();
+    const exe = b.addExecutable("$", "src/main.zig");
+    exe.setBuildMode(mode);
+
+    const run_step = b.step("run", "Run the app");
+    const run_cmd = b.addCommand(".", b.env_map, [][]const u8{exe.getOutputPath()});
+    run_step.dependOn(&run_cmd.step);
+    run_cmd.step.dependOn(&exe.step);
+
+    b.default_step.dependOn(&exe.step);
+    b.installArtifact(exe);
+}
std/special/init-lib/src/main.zig
@@ -0,0 +1,10 @@
+const std = @import("std");
+const assertOrPanic = std.debug.assertOrPanic;
+
+export fn add(a: i32, b: i32) i32 {
+    return a + b;
+}
+
+test "basic add functionality" {
+    assertOrPanic(add(3, 7) == 10);
+}
std/special/init-lib/build.zig
@@ -0,0 +1,16 @@
+const Builder = @import("std").build.Builder;
+
+pub fn build(b: *Builder) void {
+    const mode = b.standardReleaseOptions();
+    const lib = b.addStaticLibrary("$", "src/main.zig");
+    lib.setBuildMode(mode);
+
+    var main_tests = b.addTest("src/main.zig");
+    main_tests.setBuildMode(mode);
+
+    const test_step = b.step("test", "Run library tests");
+    test_step.dependOn(&main_tests.step);
+
+    b.default_step.dependOn(&lib.step);
+    b.installArtifact(lib);
+}
std/special/build_file_template.zig
@@ -1,10 +0,0 @@
-const Builder = @import("std").build.Builder;
-
-pub fn build(b: *Builder) void {
-    const mode = b.standardReleaseOptions();
-    const exe = b.addExecutable("YOUR_NAME_HERE", "src/main.zig");
-    exe.setBuildMode(mode);
-
-    b.default_step.dependOn(&exe.step);
-    b.installArtifact(exe);
-}
test/cli.zig
@@ -0,0 +1,92 @@
+const std = @import("std");
+const os = std.os;
+const assertOrPanic = std.debug.assertOrPanic;
+
+var a: *std.mem.Allocator = undefined;
+
+pub fn main() !void {
+    var direct_allocator = std.heap.DirectAllocator.init();
+    defer direct_allocator.deinit();
+
+    var arena = std.heap.ArenaAllocator.init(&direct_allocator.allocator);
+    defer arena.deinit();
+
+    var arg_it = os.args();
+
+    // skip my own exe name
+    _ = arg_it.skip();
+
+    a = &arena.allocator;
+
+    const zig_exe_rel = try (arg_it.next(a) orelse {
+        std.debug.warn("Expected first argument to be path to zig compiler\n");
+        return error.InvalidArgs;
+    });
+    const cache_root = try (arg_it.next(a) orelse {
+        std.debug.warn("Expected second argument to be cache root directory path\n");
+        return error.InvalidArgs;
+    });
+    const zig_exe = try os.path.resolve(a, zig_exe_rel);
+
+    try testZigInitLib(zig_exe, cache_root);
+    try testZigInitExe(zig_exe, cache_root);
+}
+
+fn unwrapArg(arg: UnwrapArgError![]u8) UnwrapArgError![]u8 {
+    return arg catch |err| {
+        warn("Unable to parse command line: {}\n", err);
+        return err;
+    };
+}
+
+fn printCmd(cwd: []const u8, argv: []const []const u8) void {
+    std.debug.warn("cd {} && ", cwd);
+    for (argv) |arg| {
+        std.debug.warn("{} ", arg);
+    }
+    std.debug.warn("\n");
+}
+
+fn exec(cwd: []const u8, argv: []const []const u8) !os.ChildProcess.ExecResult {
+    const max_output_size = 100 * 1024;
+    const result = os.ChildProcess.exec(a, argv, cwd, null, max_output_size) catch |err| {
+        std.debug.warn("The following command failed:\n");
+        printCmd(cwd, argv);
+        return err;
+    };
+    switch (result.term) {
+        os.ChildProcess.Term.Exited => |code| {
+            if (code != 0) {
+                std.debug.warn("The following command exited with error code {}:\n", code);
+                printCmd(cwd, argv);
+                std.debug.warn("stderr:\n{}\n", result.stderr);
+                return error.CommandFailed;
+            }
+        },
+        else => {
+            std.debug.warn("The following command terminated unexpectedly:\n");
+            printCmd(cwd, argv);
+            std.debug.warn("stderr:\n{}\n", result.stderr);
+            return error.CommandFailed;
+        },
+    }
+    return result;
+}
+
+fn testZigInitLib(zig_exe: []const u8, cache_root: []const u8) !void {
+    const dir_path = try os.path.join(a, cache_root, "clitest");
+    try os.deleteTree(a, dir_path);
+    try os.makeDir(dir_path);
+    _ = try exec(dir_path, [][]const u8{ zig_exe, "init-lib" });
+    const test_result = try exec(dir_path, [][]const u8{ zig_exe, "build", "test" });
+    assertOrPanic(std.mem.endsWith(u8, test_result.stderr, "All tests passed.\n"));
+}
+
+fn testZigInitExe(zig_exe: []const u8, cache_root: []const u8) !void {
+    const dir_path = try os.path.join(a, cache_root, "clitest");
+    try os.deleteTree(a, dir_path);
+    try os.makeDir(dir_path);
+    _ = try exec(dir_path, [][]const u8{ zig_exe, "init-exe" });
+    const run_result = try exec(dir_path, [][]const u8{ zig_exe, "build", "run" });
+    assertOrPanic(std.mem.eql(u8, run_result.stderr, "All your base are belong to us.\n"));
+}
test/tests.zig
@@ -103,6 +103,21 @@ pub fn addBuildExampleTests(b: *build.Builder, test_filter: ?[]const u8, modes:
     return cases.step;
 }
 
+pub fn addCliTests(b: *build.Builder, test_filter: ?[]const u8, modes: []const Mode) *build.Step {
+    const step = b.step("test-cli", "Test the command line interface");
+
+    const exe = b.addExecutable("test-cli", "test/cli.zig");
+    const run_cmd = b.addCommand(null, b.env_map, [][]const u8{
+        b.pathFromRoot(exe.getOutputPath()),
+        os.path.realAlloc(b.allocator, b.zig_exe) catch unreachable,
+        b.pathFromRoot(b.cache_root),
+    });
+    run_cmd.step.dependOn(&exe.step);
+
+    step.dependOn(&run_cmd.step);
+    return step;
+}
+
 pub fn addAssembleAndLinkTests(b: *build.Builder, test_filter: ?[]const u8, modes: []const Mode) *build.Step {
     const cases = b.allocator.create(CompareOutputContext{
         .b = b,
build.zig
@@ -112,6 +112,7 @@ pub fn build(b: *Builder) !void {
 
     test_step.dependOn(tests.addCompareOutputTests(b, test_filter, modes));
     test_step.dependOn(tests.addBuildExampleTests(b, test_filter, modes));
+    test_step.dependOn(tests.addCliTests(b, test_filter, modes));
     test_step.dependOn(tests.addCompileErrorTests(b, test_filter, modes));
     test_step.dependOn(tests.addAssembleAndLinkTests(b, test_filter, modes));
     test_step.dependOn(tests.addRuntimeSafetyTests(b, test_filter, modes));
CMakeLists.txt
@@ -597,7 +597,6 @@ set(ZIG_STD_FILES
     "sort.zig"
     "special/bootstrap.zig"
     "special/bootstrap_lib.zig"
-    "special/build_file_template.zig"
     "special/build_runner.zig"
     "special/builtin.zig"
     "special/compiler_rt/aulldiv.zig"
@@ -615,22 +614,26 @@ set(ZIG_STD_FILES
     "special/compiler_rt/fixunstfdi.zig"
     "special/compiler_rt/fixunstfsi.zig"
     "special/compiler_rt/fixunstfti.zig"
+    "special/compiler_rt/floattidf.zig"
+    "special/compiler_rt/floattisf.zig"
+    "special/compiler_rt/floattitf.zig"
     "special/compiler_rt/floatunditf.zig"
     "special/compiler_rt/floatunsitf.zig"
     "special/compiler_rt/floatuntidf.zig"
     "special/compiler_rt/floatuntisf.zig"
     "special/compiler_rt/floatuntitf.zig"
-    "special/compiler_rt/floattidf.zig"
-    "special/compiler_rt/floattisf.zig"
-    "special/compiler_rt/floattitf.zig"
-    "special/compiler_rt/muloti4.zig"
     "special/compiler_rt/index.zig"
+    "special/compiler_rt/muloti4.zig"
     "special/compiler_rt/truncXfYf2.zig"
     "special/compiler_rt/udivmod.zig"
     "special/compiler_rt/udivmoddi4.zig"
     "special/compiler_rt/udivmodti4.zig"
     "special/compiler_rt/udivti3.zig"
     "special/compiler_rt/umodti3.zig"
+    "special/init-exe/build.zig"
+    "special/init-exe/src/main.zig"
+    "special/init-lib/build.zig"
+    "special/init-lib/src/main.zig"
     "special/panic.zig"
     "special/test_runner.zig"
     "unicode.zig"