Commit 2e5115b068

Marc Tiehuis <marctiehuis@gmail.com>
2018-03-31 08:04:01
Add run compiler command
'zig run file.zig' builds a file and stores the artifacts in the global cache. On successful compilation the binary is executed. 'zig run file.zig -- a b c' does the same, but passes the arguments a, b and c as runtime arguments to the program. Everything after an '--' are treated as runtime arguments. On a posix system, a shebang can be used to run a zig file directly. An example shebang would be '#!/usr/bin/zig run'. You may not be able pass extra compile arguments currently as part of the shebang. Linux for example treats all arguments after the first as a single argument which will result in an 'invalid command'. Currently there is no customisability for the cache path as a compile argument. For a posix system you can use `TMPDIR=. zig run file.zig` to override, in this case using the current directory for the run cache. The input file is always recompiled, even if it has changed. This is intended to be cached but further discussion/thought needs to go into this. Closes #466.
1 parent 67f1119
src/codegen.cpp
@@ -6286,7 +6286,7 @@ static ImportTableEntry *add_special_code(CodeGen *g, PackageTableEntry *package
         zig_panic("unable to open '%s': %s", buf_ptr(&path_to_code_src), err_str(err));
     }
     Buf *import_code = buf_alloc();
-    if ((err = os_fetch_file_path(abs_full_path, import_code))) {
+    if ((err = os_fetch_file_path(abs_full_path, import_code, false))) {
         zig_panic("unable to open '%s': %s", buf_ptr(&path_to_code_src), err_str(err));
     }
 
@@ -6374,7 +6374,7 @@ static void gen_root_source(CodeGen *g) {
     }
 
     Buf *source_code = buf_alloc();
-    if ((err = os_fetch_file_path(rel_full_path, source_code))) {
+    if ((err = os_fetch_file_path(rel_full_path, source_code, true))) {
         zig_panic("unable to open '%s': %s", buf_ptr(rel_full_path), err_str(err));
     }
 
@@ -6439,7 +6439,7 @@ static void gen_global_asm(CodeGen *g) {
     int err;
     for (size_t i = 0; i < g->assembly_files.length; i += 1) {
         Buf *asm_file = g->assembly_files.at(i);
-        if ((err = os_fetch_file_path(asm_file, &contents))) {
+        if ((err = os_fetch_file_path(asm_file, &contents,  false))) {
             zig_panic("Unable to read %s: %s", buf_ptr(asm_file), err_str(err));
         }
         buf_append_buf(&g->global_asm, &contents);
src/ir.cpp
@@ -14709,7 +14709,7 @@ static TypeTableEntry *ir_analyze_instruction_import(IrAnalyze *ira, IrInstructi
         return ira->codegen->builtin_types.entry_namespace;
     }
 
-    if ((err = os_fetch_file_path(abs_full_path, import_code))) {
+    if ((err = os_fetch_file_path(abs_full_path, import_code, true))) {
         if (err == ErrorFileNotFound) {
             ir_add_error_node(ira, source_node,
                     buf_sprintf("unable to find '%s'", buf_ptr(import_target_path)));
@@ -15570,7 +15570,7 @@ static TypeTableEntry *ir_analyze_instruction_embed_file(IrAnalyze *ira, IrInstr
     // load from file system into const expr
     Buf *file_contents = buf_alloc();
     int err;
-    if ((err = os_fetch_file_path(&file_path, file_contents))) {
+    if ((err = os_fetch_file_path(&file_path, file_contents, false))) {
         if (err == ErrorFileNotFound) {
             ir_add_error(ira, instruction->name, buf_sprintf("unable to find '%s'", buf_ptr(&file_path)));
             return ira->codegen->builtin_types.entry_invalid;
src/main.cpp
@@ -23,6 +23,7 @@ static int usage(const char *arg0) {
         "  build-exe [source]           create executable from source or object files\n"
         "  build-lib [source]           create library from source or object files\n"
         "  build-obj [source]           create object from source or assembly\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"
         "  test [source]                create and run a test build\n"
@@ -220,6 +221,7 @@ static Buf *resolve_zig_lib_dir(const char *zig_install_prefix_arg) {
 enum Cmd {
     CmdInvalid,
     CmdBuild,
+    CmdRun,
     CmdTest,
     CmdVersion,
     CmdZen,
@@ -329,6 +331,8 @@ int main(int argc, char **argv) {
     CliPkg *cur_pkg = allocate<CliPkg>(1);
     BuildMode build_mode = BuildModeDebug;
     ZigList<const char *> test_exec_args = {0};
+    int comptime_args_end = 0;
+    int runtime_args_start = argc;
 
     if (argc >= 2 && strcmp(argv[1], "build") == 0) {
         const char *zig_exe_path = arg0;
@@ -481,11 +485,15 @@ int main(int argc, char **argv) {
         return (term.how == TerminationIdClean) ? term.code : -1;
     }
 
-    for (int i = 1; i < argc; i += 1) {
+    for (int i = 1; i < argc; i += 1, comptime_args_end += 1) {
         char *arg = argv[i];
 
         if (arg[0] == '-') {
-            if (strcmp(arg, "--release-fast") == 0) {
+            if (strcmp(arg, "--") == 0) {
+                // ignore -- from both compile and runtime arg sets
+                runtime_args_start = i + 1;
+                break;
+            } else if (strcmp(arg, "--release-fast") == 0) {
                 build_mode = BuildModeFastRelease;
             } else if (strcmp(arg, "--release-safe") == 0) {
                 build_mode = BuildModeSafeRelease;
@@ -652,6 +660,9 @@ int main(int argc, char **argv) {
             } else if (strcmp(arg, "build-lib") == 0) {
                 cmd = CmdBuild;
                 out_type = OutTypeLib;
+            } else if (strcmp(arg, "run") == 0) {
+                cmd = CmdRun;
+                out_type = OutTypeExe;
             } else if (strcmp(arg, "version") == 0) {
                 cmd = CmdVersion;
             } else if (strcmp(arg, "zen") == 0) {
@@ -670,6 +681,7 @@ int main(int argc, char **argv) {
         } else {
             switch (cmd) {
                 case CmdBuild:
+                case CmdRun:
                 case CmdTranslateC:
                 case CmdTest:
                     if (!in_file) {
@@ -724,8 +736,8 @@ int main(int argc, char **argv) {
         }
     }
 
-
     switch (cmd) {
+    case CmdRun:
     case CmdBuild:
     case CmdTranslateC:
     case CmdTest:
@@ -733,7 +745,7 @@ int main(int argc, char **argv) {
             if (cmd == CmdBuild && !in_file && objects.length == 0 && asm_files.length == 0) {
                 fprintf(stderr, "Expected source file argument or at least one --object or --assembly argument.\n");
                 return usage(arg0);
-            } else if ((cmd == CmdTranslateC || cmd == CmdTest) && !in_file) {
+            } else if ((cmd == CmdTranslateC || cmd == CmdTest || cmd == CmdRun) && !in_file) {
                 fprintf(stderr, "Expected source file argument.\n");
                 return usage(arg0);
             } else if (cmd == CmdBuild && out_type == OutTypeObj && objects.length != 0) {
@@ -745,6 +757,10 @@ int main(int argc, char **argv) {
 
             bool need_name = (cmd == CmdBuild || cmd == CmdTranslateC);
 
+            if (cmd == CmdRun) {
+                out_name = "run";
+            }
+
             Buf *in_file_buf = nullptr;
 
             Buf *buf_out_name = (cmd == CmdTest) ? buf_create_from_str("test") :
@@ -769,9 +785,23 @@ int main(int argc, char **argv) {
             Buf *zig_root_source_file = (cmd == CmdTranslateC) ? nullptr : in_file_buf;
 
             Buf *full_cache_dir = buf_alloc();
-            os_path_resolve(buf_create_from_str("."),
-                    buf_create_from_str((cache_dir == nullptr) ? default_zig_cache_name : cache_dir),
-                    full_cache_dir);
+            Buf *run_exec_path = buf_alloc();
+            if (cmd == CmdRun) {
+                if (buf_out_name == nullptr) {
+                    buf_out_name = buf_create_from_str("run");
+                }
+
+                Buf *global_cache_dir = buf_alloc();
+                os_get_global_cache_directory(global_cache_dir);
+                os_path_join(global_cache_dir, buf_out_name, run_exec_path);
+                os_path_resolve(buf_create_from_str("."), global_cache_dir, full_cache_dir);
+
+                out_file = buf_ptr(run_exec_path);
+            } else {
+                os_path_resolve(buf_create_from_str("."),
+                        buf_create_from_str((cache_dir == nullptr) ? default_zig_cache_name : cache_dir),
+                        full_cache_dir);
+            }
 
             Buf *zig_lib_dir_buf = resolve_zig_lib_dir(zig_install_prefix);
 
@@ -855,7 +885,7 @@ int main(int argc, char **argv) {
 
             add_package(g, cur_pkg, g->root_package);
 
-            if (cmd == CmdBuild) {
+            if (cmd == CmdBuild || cmd == CmdRun) {
                 codegen_set_emit_file_type(g, emit_file_type);
 
                 for (size_t i = 0; i < objects.length; i += 1) {
@@ -868,6 +898,18 @@ int main(int argc, char **argv) {
                 codegen_link(g, out_file);
                 if (timing_info)
                     codegen_print_timing_report(g, stdout);
+
+                if (cmd == CmdRun) {
+                    ZigList<const char*> args = {0};
+                    for (int i = runtime_args_start; i < argc; ++i) {
+                        args.append(argv[i]);
+                    }
+
+                    Termination term;
+                    os_spawn_process(buf_ptr(run_exec_path), args, &term);
+                    return term.code;
+                }
+
                 return EXIT_SUCCESS;
             } else if (cmd == CmdTranslateC) {
                 codegen_translate_c(g, in_file_buf);
src/os.cpp
@@ -288,13 +288,39 @@ void os_path_resolve(Buf *ref_path, Buf *target_path, Buf *out_abs_path) {
     return;
 }
 
-int os_fetch_file(FILE *f, Buf *out_buf) {
+int os_fetch_file(FILE *f, Buf *out_buf, bool skip_shebang) {
     static const ssize_t buf_size = 0x2000;
     buf_resize(out_buf, buf_size);
     ssize_t actual_buf_len = 0;
+
+    bool first_read = true;
+
     for (;;) {
         size_t amt_read = fread(buf_ptr(out_buf) + actual_buf_len, 1, buf_size, f);
         actual_buf_len += amt_read;
+
+        if (skip_shebang && first_read && buf_starts_with_str(out_buf, "#!")) {
+            size_t i = 0;
+            while (true) {
+                if (i > buf_len(out_buf)) {
+                    zig_panic("shebang line exceeded %zd characters", buf_size);
+                }
+
+                size_t current_pos = i;
+                i += 1;
+
+                if (out_buf->list.at(current_pos) == '\n') {
+                    break;
+                }
+            }
+
+            ZigList<char> *list = &out_buf->list;
+            memmove(list->items, list->items + i, list->length - i);
+            list->length -= i;
+
+            actual_buf_len -= i;
+        }
+
         if (amt_read != buf_size) {
             if (feof(f)) {
                 buf_resize(out_buf, actual_buf_len);
@@ -305,6 +331,7 @@ int os_fetch_file(FILE *f, Buf *out_buf) {
         }
 
         buf_resize(out_buf, actual_buf_len + buf_size);
+        first_read = false;
     }
     zig_unreachable();
 }
@@ -374,8 +401,8 @@ static int os_exec_process_posix(const char *exe, ZigList<const char *> &args,
 
         FILE *stdout_f = fdopen(stdout_pipe[0], "rb");
         FILE *stderr_f = fdopen(stderr_pipe[0], "rb");
-        os_fetch_file(stdout_f, out_stdout);
-        os_fetch_file(stderr_f, out_stderr);
+        os_fetch_file(stdout_f, out_stdout, false);
+        os_fetch_file(stderr_f, out_stderr, false);
 
         fclose(stdout_f);
         fclose(stderr_f);
@@ -588,7 +615,7 @@ int os_copy_file(Buf *src_path, Buf *dest_path) {
     }
 }
 
-int os_fetch_file_path(Buf *full_path, Buf *out_contents) {
+int os_fetch_file_path(Buf *full_path, Buf *out_contents, bool skip_shebang) {
     FILE *f = fopen(buf_ptr(full_path), "rb");
     if (!f) {
         switch (errno) {
@@ -607,7 +634,7 @@ int os_fetch_file_path(Buf *full_path, Buf *out_contents) {
                 return ErrorFileSystem;
         }
     }
-    int result = os_fetch_file(f, out_contents);
+    int result = os_fetch_file(f, out_contents, skip_shebang);
     fclose(f);
     return result;
 }
@@ -780,6 +807,44 @@ int os_buf_to_tmp_file(Buf *contents, Buf *suffix, Buf *out_tmp_path) {
 #endif
 }
 
+#if defined(ZIG_OS_POSIX)
+int os_get_global_cache_directory(Buf *out_tmp_path) {
+    const char *tmp_dir = getenv("TMPDIR");
+    if (!tmp_dir) {
+        tmp_dir = P_tmpdir;
+    }
+
+    Buf *tmp_dir_buf = buf_create_from_str(tmp_dir);
+    Buf *cache_dirname_buf = buf_create_from_str("zig-cache");
+
+    buf_resize(out_tmp_path, 0);
+    os_path_join(tmp_dir_buf, cache_dirname_buf, out_tmp_path);
+
+    buf_deinit(tmp_dir_buf);
+    buf_deinit(cache_dirname_buf);
+    return 0;
+}
+#endif
+
+#if defined(ZIG_OS_WINDOWS)
+int os_get_global_cache_directory(Buf *out_tmp_path) {
+    char tmp_dir[MAX_PATH + 1];
+    if (GetTempPath(MAX_PATH, tmp_dir) == 0) {
+        zig_panic("GetTempPath failed");
+    }
+
+    Buf *tmp_dir_buf = buf_create_from_str(tmp_dir);
+    Buf *cache_dirname_buf = buf_create_from_str("zig-cache");
+
+    buf_resize(out_tmp_path, 0);
+    os_path_join(tmp_dir_buf, cache_dirname_buf, out_tmp_path);
+
+    buf_deinit(tmp_dir_buf);
+    buf_deinit(cache_dirname_buf);
+    return 0;
+}
+#endif
+
 int os_delete_file(Buf *path) {
     if (remove(buf_ptr(path))) {
         return ErrorFileSystem;
src/os.hpp
@@ -51,14 +51,16 @@ int os_path_real(Buf *rel_path, Buf *out_abs_path);
 void os_path_resolve(Buf *ref_path, Buf *target_path, Buf *out_abs_path);
 bool os_path_is_absolute(Buf *path);
 
+int os_get_global_cache_directory(Buf *out_tmp_path);
+
 int os_make_path(Buf *path);
 int os_make_dir(Buf *path);
 
 void os_write_file(Buf *full_path, Buf *contents);
 int os_copy_file(Buf *src_path, Buf *dest_path);
 
-int os_fetch_file(FILE *file, Buf *out_contents);
-int os_fetch_file_path(Buf *full_path, Buf *out_contents);
+int os_fetch_file(FILE *file, Buf *out_contents, bool skip_shebang);
+int os_fetch_file_path(Buf *full_path, Buf *out_contents, bool skip_shebang);
 
 int os_get_cwd(Buf *out_cwd);