diff --git a/README.md b/README.md index f569dc4cb7..4e09f77508 100644 --- a/README.md +++ b/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 ``` diff --git a/example/hello_world/hello_libc.zig b/example/hello_world/hello_libc.zig index b5ab7f261b..4184c11067 100644 --- a/example/hello_world/hello_libc.zig +++ b/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; } diff --git a/src/analyze.hpp b/src/analyze.hpp index 024d96614b..b898b3b497 100644 --- a/src/analyze.hpp +++ b/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; diff --git a/src/codegen.cpp b/src/codegen.cpp index 699769f513..b0a7eae2c8 100644 --- a/src/codegen.cpp +++ b/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 *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 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); diff --git a/src/codegen.hpp b/src/codegen.hpp index 296c824b10..18baaa27ae 100644 --- a/src/codegen.hpp +++ b/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); diff --git a/src/config.h.in b/src/config.h.in index a3a31d31b2..c64c2b40f4 100644 --- a/src/config.h.in +++ b/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 diff --git a/src/main.cpp b/src/main.cpp index 2846a8c7ae..f7df0e9cb5 100644 --- a/src/main.cpp +++ b/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); diff --git a/src/os.cpp b/src/os.cpp index a131cc0ae7..b7af9a599d 100644 --- a/src/os.cpp +++ b/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); } diff --git a/test/run_tests.cpp b/test/run_tests.cpp index 8c64a54cea..214881f74e 100644 --- a/test/run_tests.cpp +++ b/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");