Commit f5a3281877

Andrew Kelley <superjoe30@gmail.com>
2015-12-15 20:44:42
when linking with libc use the C runtime library
1 parent f2a9b40
example/hello_world/hello_libc.zig
@@ -6,7 +6,7 @@ extern {
     fn exit(__status: i32) -> unreachable;
 }
 
-export fn _start() -> unreachable {
+export fn main(argc: i32, argv: &&u8, env: &&u8) -> i32 {
     printf(c"Hello, world!\n");
-    exit(0);
+    return 0;
 }
src/analyze.hpp
@@ -164,7 +164,9 @@ struct CodeGen {
     unsigned pointer_size_bytes;
     bool is_static;
     bool strip_debug_symbols;
-    bool insert_bootstrap_code;
+    bool have_exported_main;
+    bool link_libc;
+    Buf *libc_path;
     CodeGenBuildType build_type;
     LLVMTargetMachineRef target_machine;
     LLVMZigDIFile *dummy_di_file;
src/codegen.cpp
@@ -58,6 +58,10 @@ void codegen_set_out_name(CodeGen *g, Buf *out_name) {
     g->root_out_name = out_name;
 }
 
+void codegen_set_libc_path(CodeGen *g, Buf *libc_path) {
+    g->libc_path = libc_path;
+}
+
 static LLVMValueRef gen_expr(CodeGen *g, AstNode *expr_node);
     
 
@@ -1517,6 +1521,18 @@ static void init(CodeGen *g, Buf *source_path) {
 
 }
 
+static bool directives_contains_link_libc(ZigList<AstNode*> *directives) {
+    for (int i = 0; i < directives->length; i += 1) {
+        AstNode *directive_node = directives->at(i);
+        if (buf_eql_str(&directive_node->data.directive.name, "link") &&
+            buf_eql_str(&directive_node->data.directive.param, "c"))
+        {
+            return true;
+        }
+    }
+    return false;
+}
+
 static ImportTableEntry *codegen_add_code(CodeGen *g, Buf *src_dirname, Buf *src_basename, Buf *source_code) {
     int err;
     Buf *full_path = buf_alloc();
@@ -1613,11 +1629,13 @@ static ImportTableEntry *codegen_add_code(CodeGen *g, Buf *src_dirname, Buf *src
             assert(proto_node->type == NodeTypeFnProto);
             Buf *proto_name = &proto_node->data.fn_proto.name;
 
-            bool is_exported = (proto_node->data.fn_proto.visib_mod != FnProtoVisibModPrivate);
+            bool is_private = (proto_node->data.fn_proto.visib_mod == FnProtoVisibModPrivate);
 
-            if (buf_eql_str(proto_name, "main") && is_exported) {
-                g->insert_bootstrap_code = true;
+            if (buf_eql_str(proto_name, "main") && !is_private) {
+                g->have_exported_main = true;
             }
+        } else if (top_level_decl->type == NodeTypeExternBlock) {
+            g->link_libc = directives_contains_link_libc(top_level_decl->data.extern_block.directives);
         }
     }
 
@@ -1633,7 +1651,7 @@ void codegen_add_root_code(CodeGen *g, Buf *src_dir, Buf *src_basename, Buf *sou
 
     g->root_import = codegen_add_code(g, src_dir, src_basename, source_code);
 
-    if (g->insert_bootstrap_code) {
+    if (g->have_exported_main && !g->link_libc && g->out_type != OutTypeLib) {
         Buf *bootstrap_dir = buf_create_from_str(ZIG_STD_DIR);
         Buf *bootstrap_basename = buf_create_from_str("bootstrap.zig");
         Buf path_to_bootstrap_src = BUF_INIT;
@@ -1788,6 +1806,22 @@ static void generate_h_file(CodeGen *g) {
         zig_panic("unable to close h file: %s", strerror(errno));
 }
 
+static void find_libc_path(CodeGen *g) {
+    if (g->libc_path && buf_len(g->libc_path))
+        return;
+    g->libc_path = buf_create_from_str(ZIG_LIBC_DIR);
+    if (g->libc_path && buf_len(g->libc_path))
+        return;
+    fprintf(stderr, "Unable to determine libc path. Consider using `--libc-path [path]`\n");
+    exit(1);
+}
+
+static const char *get_libc_file(CodeGen *g, const char *file) {
+    Buf *out_buf = buf_alloc();
+    os_path_join(g->libc_path, buf_create_from_str(file), out_buf);
+    return buf_ptr(out_buf);
+}
+
 void codegen_link(CodeGen *g, const char *out_file) {
     bool is_optimized = (g->build_type == CodeGenBuildTypeRelease);
     if (is_optimized) {
@@ -1826,6 +1860,9 @@ void codegen_link(CodeGen *g, const char *out_file) {
     }
 
     if (g->out_type == OutTypeObj) {
+        if (g->verbose) {
+            fprintf(stderr, "OK\n");
+        }
         return;
     }
 
@@ -1840,8 +1877,12 @@ void codegen_link(CodeGen *g, const char *out_file) {
 
     // invoke `ld`
     ZigList<const char *> args = {0};
+    const char *crt1o;
     if (g->is_static) {
         args.append("-static");
+        crt1o = "crt1.o";
+    } else {
+        crt1o = "Scrt1.o";
     }
 
     char *ZIG_NATIVE_DYNAMIC_LINKER = getenv("ZIG_NATIVE_DYNAMIC_LINKER");
@@ -1868,8 +1909,21 @@ void codegen_link(CodeGen *g, const char *out_file) {
     args.append("-o");
     args.append(out_file);
 
+    bool link_in_crt = (g->link_libc && g->out_type == OutTypeExe);
+
+    if (link_in_crt) {
+        find_libc_path(g);
+
+        args.append(get_libc_file(g, crt1o));
+        args.append(get_libc_file(g, "crti.o"));
+    }
+
     args.append((const char *)buf_ptr(&out_file_o));
 
+    if (link_in_crt) {
+        args.append(get_libc_file(g, "crtn.o"));
+    }
+
     auto it = g->link_table.entry_iterator();
     for (;;) {
         auto *entry = it.next();
@@ -1880,7 +1934,24 @@ void codegen_link(CodeGen *g, const char *out_file) {
         args.append(buf_ptr(arg));
     }
 
-    os_spawn_process("ld", args, false);
+    if (g->verbose) {
+        fprintf(stderr, "ld");
+        for (int i = 0; i < args.length; i += 1) {
+            fprintf(stderr, " %s", args.at(i));
+        }
+        fprintf(stderr, "\n");
+    }
+
+    int return_code;
+    Buf ld_stderr = BUF_INIT;
+    Buf ld_stdout = BUF_INIT;
+    os_exec_process("ld", args, &return_code, &ld_stderr, &ld_stdout);
+
+    if (return_code != 0) {
+        fprintf(stderr, "ld failed with return code %d\n", return_code);
+        fprintf(stderr, "%s\n", buf_ptr(&ld_stderr));
+        exit(1);
+    }
 
     if (g->out_type == OutTypeLib) {
         generate_h_file(g);
src/codegen.hpp
@@ -33,6 +33,7 @@ void codegen_set_verbose(CodeGen *codegen, bool verbose);
 void codegen_set_errmsg_color(CodeGen *codegen, ErrColor err_color);
 void codegen_set_out_type(CodeGen *codegen, OutType out_type);
 void codegen_set_out_name(CodeGen *codegen, Buf *out_name);
+void codegen_set_libc_path(CodeGen *codegen, Buf *libc_path);
 
 void codegen_add_root_code(CodeGen *g, Buf *source_dir, Buf *source_basename, Buf *source_code);
 
src/config.h.in
@@ -8,5 +8,6 @@
 
 #define ZIG_HEADERS_DIR "@CMAKE_INSTALL_PREFIX@/@C_HEADERS_DEST@"
 #define ZIG_STD_DIR "@CMAKE_INSTALL_PREFIX@/@ZIG_STD_DEST@"
+#define ZIG_LIBC_DIR "@ZIG_LIBC_DIR@"
 
 #endif
src/main.cpp
@@ -29,6 +29,7 @@ static int usage(const char *arg0) {
         "  --output [file]        override destination path\n"
         "  --verbose              turn on compiler debug output\n"
         "  --color [auto|off|on]  enable or disable colored error messages\n"
+        "  --libc-path [path]     set the C compiler data path\n"
         "Command: parseh target\n"
         "  -isystem [dir]         add additional search path for other .h files\n"
         "  -dirafter [dir]        same as -isystem but do it last\n"
@@ -52,6 +53,7 @@ struct Build {
     const char *out_name;
     bool verbose;
     ErrColor color;
+    const char *libc_path;
 };
 
 static int build(const char *arg0, int argc, char **argv) {
@@ -99,6 +101,8 @@ static int build(const char *arg0, int argc, char **argv) {
                     }
                 } else if (strcmp(arg, "--name") == 0) {
                     b.out_name = argv[i];
+                } else if (strcmp(arg, "--libc-path") == 0) {
+                    b.libc_path = argv[i];
                 } else {
                     return usage(arg0);
                 }
@@ -142,6 +146,8 @@ static int build(const char *arg0, int argc, char **argv) {
         codegen_set_out_type(g, b.out_type);
     if (b.out_name)
         codegen_set_out_name(g, buf_create_from_str(b.out_name));
+    if (b.libc_path)
+        codegen_set_libc_path(g, buf_create_from_str(b.libc_path));
     codegen_set_verbose(g, b.verbose);
     codegen_set_errmsg_color(g, b.color);
     codegen_add_root_code(g, &root_source_dir, &root_source_name, &root_source_code);
src/os.cpp
@@ -77,7 +77,9 @@ void os_path_split(Buf *full_path, Buf *out_dirname, Buf *out_basename) {
 
 void os_path_join(Buf *dirname, Buf *basename, Buf *out_full_path) {
     buf_init_from_buf(out_full_path, dirname);
-    buf_append_char(out_full_path, '/');
+    uint8_t c = *(buf_ptr(out_full_path) + buf_len(out_full_path) - 1);
+    if (c != '/')
+        buf_append_char(out_full_path, '/');
     buf_append_buf(out_full_path, basename);
 }
 
test/run_tests.cpp
@@ -100,43 +100,34 @@ static void add_compiling_test_cases(void) {
         #link("c")
         extern {
             fn puts(s: &const u8) -> i32;
-            fn exit(code: i32) -> unreachable;
         }
 
-        export fn _start() -> unreachable {
+        export fn main(argc: i32, argv: &&u8, env: &&u8) -> i32 {
             puts(c"Hello, world!");
-            exit(0);
+            return 0;
         }
     )SOURCE", "Hello, world!\n");
 
     add_simple_case("function call", R"SOURCE(
-        #link("c")
-        extern {
-            fn puts(s: &const u8) -> i32;
-            fn exit(code: i32) -> unreachable;
-        }
+        use "std.zig";
 
         fn empty_function_1() {}
         fn empty_function_2() { return; }
 
-        export fn _start() -> unreachable {
+        pub fn main(argc: isize, argv: &&u8, env: &&u8) -> i32 {
             empty_function_1();
             empty_function_2();
             this_is_a_function();
         }
 
         fn this_is_a_function() -> unreachable {
-            puts(c"OK");
+            print_str("OK\n" as string);
             exit(0);
         }
     )SOURCE", "OK\n");
 
     add_simple_case("comments", R"SOURCE(
-        #link("c")
-        extern {
-            fn puts(s: &const u8) -> i32;
-            fn exit(code: i32) -> unreachable;
-        }
+        use "std.zig";
 
         /**
          * multi line doc comment
@@ -145,9 +136,9 @@ static void add_compiling_test_cases(void) {
 
         /// this is a documentation comment
         /// doc comment line 2
-        export fn _start() -> unreachable {
-            puts(/* mid-line comment /* nested */ */ c"OK");
-            exit(0);
+        pub fn main(argc: isize, argv: &&u8, env: &&u8) -> i32 {
+            print_str(/* mid-line comment /* nested */ */ "OK\n" as string);
+            return 0;
         }
     )SOURCE", "OK\n");
 
@@ -156,7 +147,7 @@ static void add_compiling_test_cases(void) {
             use "libc.zig";
             use "foo.zig";
 
-            export fn _start() -> unreachable {
+            export fn main(argc: i32, argv: &&u8, env: &&u8) -> i32 {
                 private_function();
             }
 
@@ -190,180 +181,144 @@ static void add_compiling_test_cases(void) {
     }
 
     add_simple_case("if statements", R"SOURCE(
-        #link("c")
-        extern {
-            fn puts(s: &const u8) -> i32;
-            fn exit(code: i32) -> unreachable;
-        }
+        use "std.zig";
 
-        export fn _start() -> unreachable {
+        pub fn main(argc: isize, argv: &&u8, env: &&u8) -> i32 {
             if 1 != 0 {
-                puts(c"1 is true");
+                print_str("1 is true\n" as string);
             } else {
-                puts(c"1 is false");
+                print_str("1 is false\n" as string);
             }
             if 0 != 0 {
-                puts(c"0 is true");
+                print_str("0 is true\n" as string);
             } else if 1 - 1 != 0 {
-                puts(c"1 - 1 is true");
+                print_str("1 - 1 is true\n" as string);
             }
             if !(0 != 0) {
-                puts(c"!0 is true");
+                print_str("!0 is true\n" as string);
             }
-            exit(0);
+            return 0;
         }
     )SOURCE", "1 is true\n!0 is true\n");
 
     add_simple_case("params", R"SOURCE(
-        #link("c")
-        extern {
-            fn puts(s: &const u8) -> i32;
-            fn exit(code: i32) -> unreachable;
-        }
+        use "std.zig";
 
         fn add(a: i32, b: i32) -> i32 {
             a + b
         }
 
-        export fn _start() -> unreachable {
+        pub fn main(argc: isize, argv: &&u8, env: &&u8) -> i32 {
             if add(22, 11) == 33 {
-                puts(c"pass");
+                print_str("pass\n" as string);
             }
-            exit(0);
+            return 0;
         }
     )SOURCE", "pass\n");
 
     add_simple_case("goto", R"SOURCE(
-        #link("c")
-        extern {
-            fn puts(s: &const u8) -> i32;
-            fn exit(code: i32) -> unreachable;
-        }
+        use "std.zig";
 
         fn loop(a : i32) {
             if a == 0 {
                 goto done;
             }
-            puts(c"loop");
+            print_str("loop\n" as string);
             loop(a - 1);
 
         done:
             return;
         }
 
-        export fn _start() -> unreachable {
+        pub fn main(argc: isize, argv: &&u8, env: &&u8) -> i32 {
             loop(3);
-            exit(0);
+            return 0;
         }
     )SOURCE", "loop\nloop\nloop\n");
 
     add_simple_case("local variables", R"SOURCE(
-#link("c")
-extern {
-    fn puts(s: &const u8) -> i32;
-    fn exit(code: i32) -> unreachable;
-}
+use "std.zig";
 
-export fn _start() -> unreachable {
+pub fn main(argc: isize, argv: &&u8, env: &&u8) -> i32 {
     const a : i32 = 1;
     const b = 2 as i32;
     if (a + b == 3) {
-        puts(c"OK");
+        print_str("OK\n" as string);
     }
-    exit(0);
+    return 0;
 }
     )SOURCE", "OK\n");
 
     add_simple_case("bool literals", R"SOURCE(
-#link("c")
-extern {
-    fn puts(s: &const u8) -> i32;
-    fn exit(code: i32) -> unreachable;
-}
+use "std.zig";
 
-export fn _start() -> unreachable {
-    if (true)   { puts(c"OK 1"); }
-    if (false)  { puts(c"BAD 1"); }
-    if (!true)  { puts(c"BAD 2"); }
-    if (!false) { puts(c"OK 2"); }
-    exit(0);
+pub fn main(argc: isize, argv: &&u8, env: &&u8) -> i32 {
+    if (true)   { print_str("OK 1\n" as string); }
+    if (false)  { print_str("BAD 1\n" as string); }
+    if (!true)  { print_str("BAD 2\n" as string); }
+    if (!false) { print_str("OK 2\n" as string); }
+    return 0;
 }
     )SOURCE", "OK 1\nOK 2\n");
 
     add_simple_case("separate block scopes", R"SOURCE(
-#link("c")
-extern {
-    fn puts(s: &const u8) -> i32;
-    fn exit(code: i32) -> unreachable;
-}
+use "std.zig";
 
-export fn _start() -> unreachable {
+pub fn main(argc: isize, argv: &&u8, env: &&u8) -> i32 {
     if (true) {
         const no_conflict : i32 = 5;
-        if (no_conflict == 5) { puts(c"OK 1"); }
+        if (no_conflict == 5) { print_str("OK 1\n" as string); }
     }
 
     const c = {
         const no_conflict = 10 as i32;
         no_conflict
     };
-    if (c == 10) { puts(c"OK 2"); }
-    exit(0);
+    if (c == 10) { print_str("OK 2\n" as string); }
+    return 0;
 }
     )SOURCE", "OK 1\nOK 2\n");
 
     add_simple_case("void parameters", R"SOURCE(
-#link("c")
-extern {
-    fn puts(s: &const u8) -> i32;
-    fn exit(code: i32) -> unreachable;
-}
+use "std.zig";
 
-export fn _start() -> unreachable {
+pub fn main(argc: isize, argv: &&u8, env: &&u8) -> i32 {
     void_fun(1, void, 2);
-    exit(0);
+    return 0;
 }
 
 fn void_fun(a : i32, b : void, c : i32) {
     const v = b;
     const vv : void = if (a == 1) {v} else {};
-    if (a + c == 3) { puts(c"OK"); }
+    if (a + c == 3) { print_str("OK\n" as string); }
     return vv;
 }
     )SOURCE", "OK\n");
 
     add_simple_case("mutable local variables", R"SOURCE(
-#link("c")
-extern {
-    fn puts(s: &const u8) -> i32;
-    fn exit(code: i32) -> unreachable;
-}
+use "std.zig";
 
-export fn _start() -> unreachable {
+pub fn main(argc: isize, argv: &&u8, env: &&u8) -> i32 {
     var zero : i32;
-    if (zero == 0) { puts(c"zero"); }
+    if (zero == 0) { print_str("zero\n" as string); }
 
     var i = 0 as i32;
 loop_start:
     if i == 3 {
         goto done;
     }
-    puts(c"loop");
+    print_str("loop\n" as string);
     i = i + 1;
     goto loop_start;
 done:
-    exit(0);
+    return 0;
 }
     )SOURCE", "zero\nloop\nloop\nloop\n");
 
     add_simple_case("arrays", R"SOURCE(
-#link("c")
-extern {
-    fn puts(s: &const u8) -> i32;
-    fn exit(code: i32) -> unreachable;
-}
+use "std.zig";
 
-export fn _start() -> unreachable {
+pub fn main(argc: isize, argv: &&u8, env: &&u8) -> i32 {
     var array : [i32; 5];
 
     var i : i32 = 0;
@@ -391,10 +346,10 @@ loop_2_start:
 loop_2_end:
 
     if accumulator == 15 {
-        puts(c"OK");
+        print_str("OK\n" as string);
     }
 
-    exit(0);
+    return 0;
 }
     )SOURCE", "OK\n");
 
@@ -481,12 +436,11 @@ export fn main(argc : isize, argv : &&u8, env : &&u8) -> i32 {
 #link("c")
 extern {
     fn printf(__format: &const u8, ...) -> i32;
-    fn exit(__status: i32) -> unreachable;
 }
 
-export fn _start() -> unreachable {
+export fn main(argc : isize, argv : &&u8, env : &&u8) -> i32 {
     printf(c"0=%d\n", 0 as i32); // TODO: more tests
-    exit(0);
+    return 0;
 }
     )SOURCE", "0=0\n");
 
README.md
@@ -61,7 +61,7 @@ compromises backward compatibility.
 ```
 mkdir build
 cd build
-cmake .. -DCMAKE_INSTALL_PREFIX=$(pwd)
+cmake .. -DCMAKE_INSTALL_PREFIX=$(pwd) -DZIG_LIBC_DIR=path/to/libc/dir
 make
 make install
 ./run_tests
@@ -72,7 +72,7 @@ make install
 ```
 mkdir build
 cd build
-cmake .. -DCMAKE_BUILD_TYPE=Release
+cmake .. -DCMAKE_BUILD_TYPE=Release -DZIG_LIBC_DIR=path/to/libc/dir
 make
 sudo make install
 ```