diff --git a/example/mix_o_files/build.zig b/example/mix_o_files/build.zig index a4e7fbbf8..623ec63de 100644 --- a/example/mix_o_files/build.zig +++ b/example/mix_o_files/build.zig @@ -3,10 +3,10 @@ const Builder = @import("std").build.Builder; pub fn build(b: *Builder) void { const obj = b.addObject("base64", "base64.zig"); - const exe = b.addCExecutable("test"); - exe.addCompileFlags([][]const u8{"-std=c99"}); - exe.addSourceFile("test.c"); + const exe = b.addExecutable("test", null); + exe.addCSourceFile("test.c",[][]const u8{"-std=c99"}); exe.addObject(obj); + exe.linkSystemLibrary("c"); b.default_step.dependOn(&exe.step); diff --git a/example/shared_library/build.zig b/example/shared_library/build.zig index 05648cf9e..5eaa4f440 100644 --- a/example/shared_library/build.zig +++ b/example/shared_library/build.zig @@ -3,10 +3,10 @@ const Builder = @import("std").build.Builder; pub fn build(b: *Builder) void { const lib = b.addSharedLibrary("mathtest", "mathtest.zig", b.version(1, 0, 0)); - const exe = b.addCExecutable("test"); - exe.addCompileFlags([][]const u8{"-std=c99"}); - exe.addSourceFile("test.c"); + const exe = b.addExecutable("test", null); + exe.addCSourceFile("test.c", [][]const u8{"-std=c99"}); exe.linkLibrary(lib); + exe.linkSystemLibrary("c"); b.default_step.dependOn(&exe.step); diff --git a/src/all_types.hpp b/src/all_types.hpp index f29411476..8099f99f4 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -1611,6 +1611,11 @@ enum ValgrindSupport { ValgrindSupportEnabled, }; +struct CFile { + ZigList args; + const char *source_path; +}; + // When adding fields, check if they should be added to the hash computation in build_with_cache struct CodeGen { //////////////////////////// Runtime State @@ -1738,6 +1743,7 @@ struct CodeGen { Buf triple_str; Buf global_asm; Buf *out_h_path; + Buf *out_lib_path; Buf artifact_dir; Buf output_file_path; Buf o_file_output_path; @@ -1788,6 +1794,7 @@ struct CodeGen { bool verbose_ir; bool verbose_llvm_ir; bool verbose_cimport; + bool verbose_cc; bool error_during_imports; bool generate_error_name_table; bool enable_cache; @@ -1805,6 +1812,7 @@ struct CodeGen { ZigList forbidden_libs; ZigList link_objects; ZigList assembly_files; + ZigList c_source_files; ZigList lib_dirs; ZigLibCInstallation *libc; diff --git a/src/cache_hash.cpp b/src/cache_hash.cpp index 4526a83c2..85bad3dd2 100644 --- a/src/cache_hash.cpp +++ b/src/cache_hash.cpp @@ -414,6 +414,39 @@ Error cache_add_file(CacheHash *ch, Buf *path) { return cache_add_file_fetch(ch, resolved_path, nullptr); } +Error cache_add_dep_file(CacheHash *ch, Buf *dep_file_path, bool verbose) { + Error err; + Buf *contents = buf_alloc(); + if ((err = os_fetch_file_path(dep_file_path, contents, false))) { + if (verbose) { + fprintf(stderr, "unable to read .d file: %s\n", err_str(err)); + } + return ErrorReadingDepFile; + } + SplitIterator it = memSplit(buf_to_slice(contents), str("\n")); + // skip first line + SplitIterator_next(&it); + for (;;) { + Optional> opt_line = SplitIterator_next(&it); + if (!opt_line.is_some) + break; + if (opt_line.value.len == 0) + continue; + SplitIterator line_it = memSplit(opt_line.value, str(" \t")); + Slice filename; + if (!SplitIterator_next(&line_it).unwrap(&filename)) + continue; + Buf *filename_buf = buf_create_from_slice(filename); + if ((err = cache_add_file(ch, filename_buf))) { + if (verbose) { + fprintf(stderr, "unable to add %s to cache: %s\n", buf_ptr(filename_buf), err_str(err)); + } + return err; + } + } + return ErrorNone; +} + static Error write_manifest_file(CacheHash *ch) { Error err; Buf contents = BUF_INIT; @@ -464,3 +497,4 @@ void cache_release(CacheHash *ch) { os_file_close(ch->manifest_file); } + diff --git a/src/cache_hash.hpp b/src/cache_hash.hpp index db1c42ec0..d74c8623c 100644 --- a/src/cache_hash.hpp +++ b/src/cache_hash.hpp @@ -56,6 +56,8 @@ Error ATTRIBUTE_MUST_USE cache_hit(CacheHash *ch, Buf *out_b64_digest); // If you did not get a cache hit, call this function for every file // that is depended on, and then finish with cache_final. Error ATTRIBUTE_MUST_USE cache_add_file(CacheHash *ch, Buf *path); +// This opens a file created by -MD -MF args to Clang +Error ATTRIBUTE_MUST_USE cache_add_dep_file(CacheHash *ch, Buf *path, bool verbose); // This variant of cache_add_file returns the file contents. // Also the file path argument must be already resolved. diff --git a/src/codegen.cpp b/src/codegen.cpp index 54da63e9f..4a8b376e2 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -175,6 +175,10 @@ void codegen_set_output_h_path(CodeGen *g, Buf *h_path) { g->out_h_path = h_path; } +void codegen_set_output_lib_path(CodeGen *g, Buf *lib_path) { + g->out_lib_path = lib_path; +} + void codegen_set_output_path(CodeGen *g, Buf *path) { g->wanted_output_file_path = path; } @@ -7885,8 +7889,8 @@ static void detect_libc(CodeGen *g) { fprintf(stderr, "Unable to save %s: %s\n", buf_ptr(native_libc_tmp), strerror(errno)); exit(1); } - if (rename(buf_ptr(native_libc_tmp), buf_ptr(native_libc_txt)) == -1) { - fprintf(stderr, "Unable to create %s: %s\n", buf_ptr(native_libc_txt), strerror(errno)); + if ((err = os_rename(native_libc_tmp, native_libc_txt))) { + fprintf(stderr, "Unable to create %s: %s\n", buf_ptr(native_libc_txt), err_str(err)); exit(1); } } @@ -8123,6 +8127,143 @@ static void gen_global_asm(CodeGen *g) { } } +static void gen_c_object(CodeGen *g, Buf *self_exe_path, CFile *c_file) { + Error err; + + Buf *c_source_file = buf_create_from_str(c_file->source_path); + Buf *c_source_basename = buf_alloc(); + os_path_split(c_source_file, nullptr, c_source_basename); + Buf *out_obj_name = buf_sprintf("%s%s", buf_ptr(c_source_basename), target_o_file_ext(g->zig_target)); + Buf *out_obj_path = buf_alloc(); + os_path_join(&g->cache_dir, out_obj_name, out_obj_path); + Buf *out_dep_name = buf_sprintf("%s.d", buf_ptr(c_source_file)); + Buf *out_dep_path = buf_alloc(); + os_path_join(&g->cache_dir, out_dep_name, out_dep_path); + + Termination term; + ZigList args = {}; + args.append("cc"); + + if (g->enable_cache) { + args.append("-MD"); + args.append("-MF"); + args.append(buf_ptr(out_dep_path)); + } + + args.append("-isystem"); + args.append(buf_ptr(g->zig_c_headers_dir)); + + if (g->libc != nullptr) { + args.append("-isystem"); + args.append(buf_ptr(&g->libc->include_dir)); + } + + if (g->zig_target->is_native) { + args.append("-march=native"); + } else { + args.append("-target"); + args.append(buf_ptr(&g->triple_str)); + } + + if (!g->strip_debug_symbols) { + args.append("-g"); + } + switch (g->build_mode) { + case BuildModeDebug: + if (g->libc_link_lib != nullptr) { + args.append("-fstack-protector-strong"); + args.append("--param"); + args.append("ssp-buffer-size=4"); + } else { + args.append("-fno-stack-protector"); + } + break; + case BuildModeSafeRelease: + args.append("-O2"); + if (g->libc_link_lib != nullptr) { + args.append("-D_FORTIFY_SOURCE=2"); + args.append("-fstack-protector-strong"); + args.append("--param"); + args.append("ssp-buffer-size=4"); + } else { + args.append("-fno-stack-protector"); + } + break; + case BuildModeFastRelease: + args.append("-O2"); + args.append("-fno-stack-protector"); + break; + case BuildModeSmallRelease: + args.append("-Os"); + args.append("-fno-stack-protector"); + break; + } + + args.append("-o"); + args.append(buf_ptr(out_obj_path)); + + args.append("-c"); + args.append(buf_ptr(c_source_file)); + + if (!g->disable_pic && target_supports_fpic(g->zig_target)) { + args.append("-fPIC"); + } + + for (size_t arg_i = 0; arg_i < g->clang_argv_len; arg_i += 1) { + args.append(g->clang_argv[arg_i]); + } + + for (size_t arg_i = 0; arg_i < c_file->args.length; arg_i += 1) { + args.append(c_file->args.at(arg_i)); + } + + if (g->verbose_cc) { + fprintf(stderr, "zig"); + for (size_t arg_i = 0; arg_i < args.length; arg_i += 1) { + fprintf(stderr, " %s", args.at(arg_i)); + } + fprintf(stderr, "\n"); + } + + os_spawn_process(buf_ptr(self_exe_path), args, &term); + if (term.how != TerminationIdClean || term.code != 0) { + fprintf(stderr, "`zig cc` failed\n"); + exit(1); + } + + g->link_objects.append(out_obj_path); + + if (g->enable_cache) { + // add the files depended on to the cache system + if ((err = cache_add_file(&g->cache_hash, c_source_file))) { + fprintf(stderr, "unable to add %s to cache: %s\n", buf_ptr(c_source_file), err_str(err)); + exit(1); + } + if ((err = cache_add_dep_file(&g->cache_hash, out_dep_path, true))) { + fprintf(stderr, "failed to add C source dependencies to cache: %s\n", err_str(err)); + exit(1); + } + } +} + +static void gen_c_objects(CodeGen *g) { + Error err; + + if (g->c_source_files.length == 0) + return; + + Buf *self_exe_path = buf_alloc(); + if ((err = os_self_exe_path(self_exe_path))) { + fprintf(stderr, "Unable to get self exe path: %s\n", err_str(err)); + exit(1); + } + + for (size_t c_file_i = 0; c_file_i < g->c_source_files.length; c_file_i += 1) { + CFile *c_file = g->c_source_files.at(c_file_i); + gen_c_object(g, self_exe_path, c_file); + } +} + void codegen_add_object(CodeGen *g, Buf *object_path) { g->link_objects.append(object_path); } @@ -8637,6 +8778,13 @@ static Error check_cache(CodeGen *g, Buf *manifest_dir, Buf *digest) { cache_list_of_buf(ch, g->forbidden_libs.items, g->forbidden_libs.length); cache_list_of_file(ch, g->link_objects.items, g->link_objects.length); cache_list_of_file(ch, g->assembly_files.items, g->assembly_files.length); + for (size_t c_file_i = 0; c_file_i < g->c_source_files.length; c_file_i += 1) { + CFile *c_file = g->c_source_files.at(c_file_i); + cache_file(ch, buf_create_from_str(c_file->source_path)); + for (size_t opt_i = 0; opt_i < c_file->args.length; opt_i += 1) { + cache_buf(ch, buf_create_from_str(c_file->args.at(opt_i))); + } + } cache_int(ch, g->emit_file_type); cache_int(ch, g->build_mode); cache_int(ch, g->out_type); @@ -8788,6 +8936,7 @@ void codegen_build_and_link(CodeGen *g) { gen_global_asm(g); gen_root_source(g); + gen_c_objects(g); if (g->enable_cache) { if ((err = cache_final(&g->cache_hash, &digest))) { @@ -8805,16 +8954,25 @@ void codegen_build_and_link(CodeGen *g) { resolve_out_paths(g); codegen_add_time_event(g, "Code Generation"); - do_code_gen(g); - codegen_add_time_event(g, "LLVM Emit Output"); - zig_llvm_emit_output(g); + if (g->out_type == OutTypeObj && g->c_source_files.length == 1) { + assert(g->link_objects.length == 1); + if ((err = os_rename(g->link_objects.pop(), &g->o_file_output_path))) { + fprintf(stderr, "unable to move object to '%s': %s\n", + buf_ptr(&g->o_file_output_path), err_str(err)); + exit(1); + } + } else { + do_code_gen(g); + codegen_add_time_event(g, "LLVM Emit Output"); + zig_llvm_emit_output(g); - if (g->out_h_path != nullptr) { - codegen_add_time_event(g, "Generate .h"); - gen_h_file(g); - } - if (g->out_type != OutTypeObj && g->emit_file_type == EmitFileTypeBinary) { - codegen_link(g); + if (g->out_h_path != nullptr) { + codegen_add_time_event(g, "Generate .h"); + gen_h_file(g); + } + if (g->out_type != OutTypeObj && g->emit_file_type == EmitFileTypeBinary) { + codegen_link(g); + } } } diff --git a/src/codegen.hpp b/src/codegen.hpp index 035b759ec..4f62cc4cb 100644 --- a/src/codegen.hpp +++ b/src/codegen.hpp @@ -41,6 +41,7 @@ void codegen_set_test_filter(CodeGen *g, Buf *filter); void codegen_set_test_name_prefix(CodeGen *g, Buf *prefix); void codegen_set_lib_version(CodeGen *g, size_t major, size_t minor, size_t patch); void codegen_set_output_h_path(CodeGen *g, Buf *h_path); +void codegen_set_output_lib_path(CodeGen *g, Buf *lib_path); void codegen_set_output_path(CodeGen *g, Buf *path); void codegen_add_time_event(CodeGen *g, const char *name); void codegen_print_timing_report(CodeGen *g, FILE *f); diff --git a/src/error.cpp b/src/error.cpp index 9e01a86d8..c81cfd168 100644 --- a/src/error.cpp +++ b/src/error.cpp @@ -36,6 +36,7 @@ const char *err_str(Error err) { case ErrorCacheUnavailable: return "cache unavailable"; case ErrorPathTooLong: return "path too long"; case ErrorCCompilerCannotFindFile: return "C compiler cannot find file"; + case ErrorReadingDepFile: return "failed to read .d file"; } return "(invalid error)"; } diff --git a/src/error.hpp b/src/error.hpp index 0e14f37a6..a580da547 100644 --- a/src/error.hpp +++ b/src/error.hpp @@ -38,6 +38,7 @@ enum Error { ErrorCacheUnavailable, ErrorPathTooLong, ErrorCCompilerCannotFindFile, + ErrorReadingDepFile, }; const char *err_str(Error err); diff --git a/src/ir.cpp b/src/ir.cpp index ea2fcb289..929f19558 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -18670,12 +18670,6 @@ static IrInstruction *ir_analyze_instruction_type_name(IrAnalyze *ira, IrInstruc } static IrInstruction *ir_analyze_instruction_c_import(IrAnalyze *ira, IrInstructionCImport *instruction) { - if (ira->codegen->enable_cache) { - ir_add_error(ira, &instruction->base, - buf_sprintf("TODO @cImport is incompatible with --cache on. The cache system currently is unable to detect subsequent changes in .h files.")); - return ira->codegen->invalid_instruction; - } - AstNode *node = instruction->base.source_node; assert(node->type == NodeTypeFnCallExpr); AstNode *block_node = node->data.fn_call_expr.params.at(0); diff --git a/src/link.cpp b/src/link.cpp index 2068b8efb..5f611ae5a 100644 --- a/src/link.cpp +++ b/src/link.cpp @@ -554,6 +554,7 @@ static void construct_linker_job_coff(LinkJob *lj) { bool is_library = g->out_type == OutTypeLib; switch (g->subsystem) { case TargetSubsystemAuto: + add_nt_link_args(lj, is_library); break; case TargetSubsystemConsole: lj->args.append("/SUBSYSTEM:console"); diff --git a/src/main.cpp b/src/main.cpp index 14a885f08..1c154250f 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -18,7 +18,7 @@ #include static int print_error_usage(const char *arg0) { - fprintf(stderr, "See `%s help` for detailed usage information\n", arg0); + fprintf(stderr, "See `%s --help` for detailed usage information\n", arg0); return EXIT_FAILURE; } @@ -34,7 +34,6 @@ static int print_full_usage(const char *arg0, FILE *file, int return_code) { " builtin show the source code of that @import(\"builtin\")\n" " cc C compiler\n" " fmt parse files and render in canonical zig format\n" - " help show this usage information\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" @@ -48,6 +47,7 @@ static int print_full_usage(const char *arg0, FILE *file, int return_code) { "\n" "Compile Options:\n" " --assembly [source] add assembly file to build\n" + " --c-source [options] [file] compile C source code\n" " --cache-dir [path] override the cache directory\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" @@ -60,6 +60,7 @@ static int print_full_usage(const char *arg0, FILE *file, int return_code) { " --name [name] override output name\n" " --output [file] override destination path\n" " --output-h [file] generate header file\n" + " --output-lib [file] override import library path\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" @@ -77,6 +78,7 @@ static int print_full_usage(const char *arg0, FILE *file, int return_code) { " --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" + " --verbose-cc enable compiler debug output for C compilation\n" " -dirafter [dir] same as -isystem but do it last\n" " -isystem [dir] add additional search path for other .h files\n" " -mllvm [arg] forward an arg to LLVM's option processing\n" @@ -181,7 +183,6 @@ enum Cmd { CmdNone, CmdBuild, CmdBuiltin, - CmdHelp, CmdRun, CmdTargets, CmdTest, @@ -375,6 +376,7 @@ int main(int argc, char **argv) { const char *in_file = nullptr; const char *out_file = nullptr; const char *out_file_h = nullptr; + const char *out_file_lib = nullptr; bool strip = false; bool is_static = false; OutType out_type = OutTypeUnknown; @@ -385,6 +387,7 @@ int main(int argc, char **argv) { bool verbose_ir = false; bool verbose_llvm_ir = false; bool verbose_cimport = false; + bool verbose_cc = false; ErrColor color = ErrColorAuto; CacheOpt enable_cache = CacheOptAuto; const char *libc_txt = nullptr; @@ -404,6 +407,7 @@ int main(int argc, char **argv) { ZigList rpath_list = {0}; bool each_lib_rpath = false; ZigList objects = {0}; + ZigList c_source_files = {0}; ZigList asm_files = {0}; const char *test_filter = nullptr; const char *test_name_prefix = nullptr; @@ -512,6 +516,7 @@ int main(int argc, char **argv) { " --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" + " --verbose-cc Enable compiler debug output for C compilation\n" "\n" , zig_exe_path); return EXIT_SUCCESS; @@ -521,7 +526,7 @@ int main(int argc, char **argv) { "No 'build.zig' file found.\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" + "See: `zig build --help` or `zig --help` for more options.\n" ); return EXIT_FAILURE; } @@ -587,9 +592,9 @@ int main(int argc, char **argv) { build_mode = BuildModeSmallRelease; } else if (strcmp(arg, "--help") == 0) { if (cmd == CmdLibC) { - return print_libc_usage(arg0, stderr, EXIT_FAILURE); + return print_libc_usage(arg0, stdout, EXIT_SUCCESS); } else { - return print_full_usage(arg0, stderr, EXIT_FAILURE); + return print_full_usage(arg0, stdout, EXIT_SUCCESS); } } else if (strcmp(arg, "--strip") == 0) { strip = true; @@ -607,6 +612,8 @@ int main(int argc, char **argv) { verbose_llvm_ir = true; } else if (strcmp(arg, "--verbose-cimport") == 0) { verbose_cimport = true; + } else if (strcmp(arg, "--verbose-cc") == 0) { + verbose_cc = true; } else if (strcmp(arg, "-rdynamic") == 0) { rdynamic = true; } else if (strcmp(arg, "--each-lib-rpath") == 0) { @@ -656,6 +663,8 @@ int main(int argc, char **argv) { out_file = argv[i]; } else if (strcmp(arg, "--output-h") == 0) { out_file_h = argv[i]; + } else if (strcmp(arg, "--output-lib") == 0) { + out_file_lib = argv[i]; } else if (strcmp(arg, "--color") == 0) { if (strcmp(argv[i], "auto") == 0) { color = ErrColorAuto; @@ -714,6 +723,19 @@ int main(int argc, char **argv) { forbidden_link_libs.append(argv[i]); } else if (strcmp(arg, "--object") == 0) { objects.append(argv[i]); + } else if (strcmp(arg, "--c-source") == 0) { + CFile *c_file = allocate(1); + for (;;) { + if (argv[i][0] == '-') { + c_file->args.append(argv[i]); + i += 1; + continue; + } else { + c_file->source_path = argv[i]; + c_source_files.append(c_file); + break; + } + } } else if (strcmp(arg, "--assembly") == 0) { asm_files.append(argv[i]); } else if (strcmp(arg, "--cache-dir") == 0) { @@ -792,8 +814,6 @@ int main(int argc, char **argv) { } else if (strcmp(arg, "build-lib") == 0) { cmd = CmdBuild; out_type = OutTypeLib; - } else if (strcmp(arg, "help") == 0) { - cmd = CmdHelp; } else if (strcmp(arg, "run") == 0) { cmd = CmdRun; out_type = OutTypeExe; @@ -835,7 +855,6 @@ int main(int argc, char **argv) { } break; case CmdBuiltin: - case CmdHelp: case CmdVersion: case CmdZen: case CmdTargets: @@ -910,15 +929,43 @@ int main(int argc, char **argv) { case CmdTranslateC: case CmdTest: { - 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"); + if (cmd == CmdBuild && !in_file && objects.length == 0 && asm_files.length == 0 && + c_source_files.length == 0) + { + fprintf(stderr, + "Expected at least one of these things:\n" + " * Zig root source file argument\n" + " * --object argument\n" + " * --assembly argument\n" + " * --c-source argument\n"); return print_error_usage(arg0); } else if ((cmd == CmdTranslateC || cmd == CmdTest || cmd == CmdRun) && !in_file) { fprintf(stderr, "Expected source file argument.\n"); return print_error_usage(arg0); - } else if (cmd == CmdBuild && out_type == OutTypeObj && objects.length != 0) { - fprintf(stderr, "When building an object file, --object arguments are invalid.\n"); - return print_error_usage(arg0); + } else if (cmd == CmdBuild && out_type == OutTypeObj) { + if (objects.length != 0) { + fprintf(stderr, + "When building an object file, --object arguments are invalid.\n" + "Consider building a static library instead.\n"); + return print_error_usage(arg0); + } + size_t zig_root_src_count = in_file ? 1 : 0; + if (zig_root_src_count + c_source_files.length > 1) { + fprintf(stderr, + "When building an object file, only one of these allowed:\n" + " * Zig root source file argument\n" + " * --c-source argument\n" + "Consider building a static library instead.\n"); + return print_error_usage(arg0); + } + if (c_source_files.length != 0 && asm_files.length != 0) { + fprintf(stderr, + "When building an object file, only one of these allowed:\n" + " * --assembly argument\n" + " * --c-source argument\n" + "Consider building a static library instead.\n"); + return print_error_usage(arg0); + } } assert(cmd != CmdBuild || out_type != OutTypeUnknown); @@ -945,6 +992,13 @@ int main(int argc, char **argv) { } } + if (need_name && buf_out_name == nullptr && c_source_files.length == 1) { + Buf basename = BUF_INIT; + os_path_split(buf_create_from_str(c_source_files.at(0)->source_path), nullptr, &basename); + buf_out_name = buf_alloc(); + os_path_extname(&basename, buf_out_name, nullptr); + } + if (need_name && buf_out_name == nullptr) { fprintf(stderr, "--name [name] not provided and unable to infer\n\n"); return print_error_usage(arg0); @@ -996,6 +1050,7 @@ int main(int argc, char **argv) { g->verbose_ir = verbose_ir; g->verbose_llvm_ir = verbose_llvm_ir; g->verbose_cimport = verbose_cimport; + g->verbose_cc = verbose_cc; codegen_set_errmsg_color(g, color); g->system_linker_hack = system_linker_hack; @@ -1043,11 +1098,14 @@ int main(int argc, char **argv) { codegen_set_output_path(g, buf_create_from_str(out_file)); if (out_file_h != nullptr && (out_type == OutTypeObj || out_type == OutTypeLib)) codegen_set_output_h_path(g, buf_create_from_str(out_file_h)); + if (out_file_lib != nullptr && out_type == OutTypeLib && !is_static) + codegen_set_output_lib_path(g, buf_create_from_str(out_file_lib)); add_package(g, cur_pkg, g->root_package); if (cmd == CmdBuild || cmd == CmdRun || cmd == CmdTest) { + g->c_source_files = c_source_files; for (size_t i = 0; i < objects.length; i += 1) { codegen_add_object(g, buf_create_from_str(objects.at(i))); } @@ -1147,8 +1205,6 @@ int main(int argc, char **argv) { zig_unreachable(); } } - case CmdHelp: - return print_full_usage(arg0, stdout, EXIT_SUCCESS); case CmdVersion: printf("%s\n", ZIG_VERSION_STRING); return EXIT_SUCCESS; @@ -1158,7 +1214,6 @@ int main(int argc, char **argv) { case CmdTargets: return print_target_list(stdout); case CmdNone: - fprintf(stderr, "Zig programming language\n"); - return print_error_usage(arg0); + return print_full_usage(arg0, stderr, EXIT_FAILURE); } } diff --git a/src/os.cpp b/src/os.cpp index 95febca9b..f52325af0 100644 --- a/src/os.cpp +++ b/src/os.cpp @@ -1232,6 +1232,18 @@ static Error os_buf_to_tmp_file_posix(Buf *contents, Buf *suffix, Buf *out_tmp_p } #endif +Buf *os_tmp_filename(Buf *prefix, Buf *suffix) { + Buf *result = buf_create_from_buf(prefix); + + const char base64[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_"; + assert(array_length(base64) == 64 + 1); + for (size_t i = 0; i < 12; i += 1) { + buf_append_char(result, base64[rand() % 64]); + } + buf_append_buf(result, suffix); + return result; +} + #if defined(ZIG_OS_WINDOWS) static Error os_buf_to_tmp_file_windows(Buf *contents, Buf *suffix, Buf *out_tmp_path) { char tmp_dir[MAX_PATH + 1]; diff --git a/src/os.hpp b/src/os.hpp index db357c22a..c4fae1d62 100644 --- a/src/os.hpp +++ b/src/os.hpp @@ -121,6 +121,7 @@ Error ATTRIBUTE_MUST_USE os_get_cwd(Buf *out_cwd); bool os_stderr_tty(void); void os_stderr_set_color(TermColor color); +Buf *os_tmp_filename(Buf *prefix, Buf *suffix); Error os_buf_to_tmp_file(Buf *contents, Buf *suffix, Buf *out_tmp_path); Error os_delete_file(Buf *path); diff --git a/src/target.cpp b/src/target.cpp index 01e8b90ce..49a174294 100644 --- a/src/target.cpp +++ b/src/target.cpp @@ -1049,3 +1049,9 @@ bool target_requires_libc(const ZigTarget *target) { // since this is the stable syscall interface. return (target_is_darwin(target) || target->os == OsFreeBSD || target->os == OsNetBSD); } + +bool target_supports_fpic(const ZigTarget *target) { + // This is not whether the target supports Position Independent Code, but whether the -fPIC + // C compiler argument is valid. + return target->os != OsWindows; +} diff --git a/src/target.hpp b/src/target.hpp index 620c9b266..dceeee2ec 100644 --- a/src/target.hpp +++ b/src/target.hpp @@ -141,5 +141,6 @@ bool target_allows_addr_zero(const ZigTarget *target); bool target_has_valgrind_support(const ZigTarget *target); bool target_is_darwin(const ZigTarget *target); bool target_requires_libc(const ZigTarget *target); +bool target_supports_fpic(const ZigTarget *target); #endif diff --git a/src/translate_c.cpp b/src/translate_c.cpp index 3fcdf139f..c80bc6d72 100644 --- a/src/translate_c.cpp +++ b/src/translate_c.cpp @@ -4776,6 +4776,15 @@ Error parse_h_file(ImportTableEntry *import, ZigList *errors, const clang_argv.append("-x"); clang_argv.append("c"); + Buf *out_dep_path = nullptr; + if (codegen->enable_cache) { + Buf *prefix = buf_sprintf("%s" OS_SEP, buf_ptr(&codegen->cache_dir)); + out_dep_path = os_tmp_filename(prefix, buf_create_from_str(".d")); + clang_argv.append("-MD"); + clang_argv.append("-MF"); + clang_argv.append(buf_ptr(out_dep_path)); + } + if (c->codegen->zig_target->is_native) { char *ZIG_PARSEC_CFLAGS = getenv("ZIG_NATIVE_PARSEC_CFLAGS"); if (ZIG_PARSEC_CFLAGS) { @@ -4912,6 +4921,17 @@ Error parse_h_file(ImportTableEntry *import, ZigList *errors, const return ErrorCCompileErrors; } + if (codegen->enable_cache) { + Error err; + assert(out_dep_path != nullptr); + if ((err = cache_add_dep_file(&codegen->cache_hash, out_dep_path, codegen->verbose_cimport))) { + if (codegen->verbose_cimport) { + fprintf(stderr, "translate-c: aborting due to failed cache operation: %s\n", err_str(err)); + } + return err; + } + } + c->ctx = ZigClangASTUnit_getASTContext(ast_unit); c->source_manager = ZigClangASTUnit_getSourceManager(ast_unit); c->root = trans_create_node(c, NodeTypeContainerDecl); diff --git a/std/buf_set.zig b/std/buf_set.zig index 7ccd94c17..484b4953b 100644 --- a/std/buf_set.zig +++ b/std/buf_set.zig @@ -32,6 +32,10 @@ pub const BufSet = struct { } } + pub fn exists(self: BufSet, key: []const u8) bool { + return self.hash_map.get(key) != null; + } + pub fn delete(self: *BufSet, key: []const u8) void { const entry = self.hash_map.remove(key) orelse return; self.free(entry.key); diff --git a/std/build.zig b/std/build.zig index 946a447a1..0d03b0325 100644 --- a/std/build.zig +++ b/std/build.zig @@ -31,6 +31,7 @@ pub const Builder = struct { verbose_tokenize: bool, verbose_ast: bool, verbose_link: bool, + verbose_cc: bool, verbose_ir: bool, verbose_llvm_ir: bool, verbose_cimport: bool, @@ -99,6 +100,7 @@ pub const Builder = struct { .verbose_tokenize = false, .verbose_ast = false, .verbose_link = false, + .verbose_cc = false, .verbose_ir = false, .verbose_llvm_ir = false, .verbose_cimport = false, @@ -157,7 +159,7 @@ pub const Builder = struct { return LibExeObjStep.createExecutable(self, name, root_src, true); } - pub fn addObject(self: *Builder, name: []const u8, root_src: []const u8) *LibExeObjStep { + pub fn addObject(self: *Builder, name: []const u8, root_src: ?[]const u8) *LibExeObjStep { return LibExeObjStep.createObject(self, name, root_src); } @@ -169,10 +171,8 @@ pub const Builder = struct { return LibExeObjStep.createStaticLibrary(self, name, root_src); } - pub fn addTest(self: *Builder, root_src: []const u8) *TestStep { - const test_step = self.allocator.create(TestStep) catch unreachable; - test_step.* = TestStep.init(self, root_src); - return test_step; + pub fn addTest(self: *Builder, root_src: []const u8) *LibExeObjStep { + return LibExeObjStep.createTest(self, "test", root_src); } pub fn addAssemble(self: *Builder, name: []const u8, src: []const u8) *LibExeObjStep { @@ -181,22 +181,6 @@ pub const Builder = struct { return obj_step; } - pub fn addCStaticLibrary(self: *Builder, name: []const u8) *LibExeObjStep { - return LibExeObjStep.createCStaticLibrary(self, name); - } - - pub fn addCSharedLibrary(self: *Builder, name: []const u8, ver: Version) *LibExeObjStep { - return LibExeObjStep.createCSharedLibrary(self, name, ver); - } - - pub fn addCExecutable(self: *Builder, name: []const u8) *LibExeObjStep { - return LibExeObjStep.createCExecutable(self, name); - } - - pub fn addCObject(self: *Builder, name: []const u8, src: []const u8) *LibExeObjStep { - return LibExeObjStep.createCObject(self, name, src); - } - /// ::argv is copied. pub fn addCommand(self: *Builder, cwd: ?[]const u8, env_map: *const BufMap, argv: []const []const u8) *CommandStep { return CommandStep.create(self, cwd, env_map, argv); @@ -663,14 +647,6 @@ pub const Builder = struct { return fmt_lib.allocPrint(self.allocator, format, args) catch unreachable; } - fn getCCExe(self: *Builder) []const u8 { - if (builtin.environ == builtin.Environ.msvc) { - return "cl.exe"; - } else { - return os.getEnvVarOwned(self.allocator, "CC") catch |err| if (err == error.EnvironmentVariableNotFound) ([]const u8)("cc") else debug.panic("Unable to get environment variable: {}", err); - } - } - pub fn findProgram(self: *Builder, names: []const []const u8, paths: []const []const u8) ![]const u8 { // TODO report error for ambiguous situations const exe_extension = (Target{ .Native = {} }).exeFileExt(); @@ -825,6 +801,11 @@ const Pkg = struct { path: []const u8, }; +const CSourceFile = struct { + source_path: []const u8, + args: []const []const u8, +}; + pub const LibExeObjStep = struct { step: Step, builder: *Builder, @@ -834,6 +815,7 @@ pub const LibExeObjStep = struct { linker_script: ?[]const u8, out_filename: []const u8, output_path: ?[]const u8, + output_lib_path: ?[]const u8, static: bool, version: Version, object_files: ArrayList([]const u8), @@ -844,32 +826,34 @@ pub const LibExeObjStep = struct { strip: bool, full_path_libs: ArrayList([]const u8), need_flat_namespace_hack: bool, - is_zig: bool, - cflags: ArrayList([]const u8), include_dirs: ArrayList([]const u8), lib_paths: ArrayList([]const u8), - disable_libc: bool, frameworks: BufSet, verbose_link: bool, + verbose_cc: bool, c_std: Builder.CStd, + override_std_dir: ?[]const u8, + exec_cmd_args: ?[]const ?[]const u8, + name_prefix: []const u8, + filter: ?[]const u8, - // zig only stuff root_src: ?[]const u8, output_h_path: ?[]const u8, out_h_filename: []const u8, + out_lib_filename: []const u8, assembly_files: ArrayList([]const u8), packages: ArrayList(Pkg), build_options_contents: std.Buffer, system_linker_hack: bool, - // C only stuff - source_files: ArrayList([]const u8), + c_source_files: ArrayList(*CSourceFile), object_src: []const u8, const Kind = enum { Exe, Lib, Obj, + Test, }; pub fn createSharedLibrary(builder: *Builder, name: []const u8, root_src: ?[]const u8, ver: Version) *LibExeObjStep { @@ -878,46 +862,27 @@ pub const LibExeObjStep = struct { return self; } - pub fn createCSharedLibrary(builder: *Builder, name: []const u8, version: Version) *LibExeObjStep { - const self = builder.allocator.create(LibExeObjStep) catch unreachable; - self.* = initC(builder, name, Kind.Lib, version, false); - return self; - } - pub fn createStaticLibrary(builder: *Builder, name: []const u8, root_src: ?[]const u8) *LibExeObjStep { const self = builder.allocator.create(LibExeObjStep) catch unreachable; self.* = initExtraArgs(builder, name, root_src, Kind.Lib, true, builder.version(0, 0, 0)); return self; } - pub fn createCStaticLibrary(builder: *Builder, name: []const u8) *LibExeObjStep { - const self = builder.allocator.create(LibExeObjStep) catch unreachable; - self.* = initC(builder, name, Kind.Lib, builder.version(0, 0, 0), true); - return self; - } - - pub fn createObject(builder: *Builder, name: []const u8, root_src: []const u8) *LibExeObjStep { + pub fn createObject(builder: *Builder, name: []const u8, root_src: ?[]const u8) *LibExeObjStep { const self = builder.allocator.create(LibExeObjStep) catch unreachable; self.* = initExtraArgs(builder, name, root_src, Kind.Obj, false, builder.version(0, 0, 0)); return self; } - pub fn createCObject(builder: *Builder, name: []const u8, src: []const u8) *LibExeObjStep { - const self = builder.allocator.create(LibExeObjStep) catch unreachable; - self.* = initC(builder, name, Kind.Obj, builder.version(0, 0, 0), false); - self.object_src = src; - return self; - } - pub fn createExecutable(builder: *Builder, name: []const u8, root_src: ?[]const u8, static: bool) *LibExeObjStep { const self = builder.allocator.create(LibExeObjStep) catch unreachable; self.* = initExtraArgs(builder, name, root_src, Kind.Exe, static, builder.version(0, 0, 0)); return self; } - pub fn createCExecutable(builder: *Builder, name: []const u8) *LibExeObjStep { + pub fn createTest(builder: *Builder, name: []const u8, root_src: []const u8) *LibExeObjStep { const self = builder.allocator.create(LibExeObjStep) catch unreachable; - self.* = initC(builder, name, Kind.Exe, builder.version(0, 0, 0), false); + self.* = initExtraArgs(builder, name, root_src, Kind.Test, false, builder.version(0, 0, 0)); return self; } @@ -926,6 +891,7 @@ pub const LibExeObjStep = struct { .strip = false, .builder = builder, .verbose_link = false, + .verbose_cc = false, .build_mode = builtin.Mode.Debug, .static = static, .kind = kind, @@ -937,70 +903,30 @@ pub const LibExeObjStep = struct { .frameworks = BufSet.init(builder.allocator), .step = Step.init(name, builder.allocator, make), .output_path = null, + .output_lib_path = null, .output_h_path = null, .version = ver, .out_filename = undefined, .out_h_filename = builder.fmt("{}.h", name), + .out_lib_filename = undefined, .major_only_filename = undefined, .name_only_filename = undefined, .object_files = ArrayList([]const u8).init(builder.allocator), .assembly_files = ArrayList([]const u8).init(builder.allocator), .packages = ArrayList(Pkg).init(builder.allocator), - .is_zig = true, .full_path_libs = ArrayList([]const u8).init(builder.allocator), .need_flat_namespace_hack = false, - .cflags = ArrayList([]const u8).init(builder.allocator), - .source_files = undefined, + .c_source_files = ArrayList(*CSourceFile).init(builder.allocator), .include_dirs = ArrayList([]const u8).init(builder.allocator), .lib_paths = ArrayList([]const u8).init(builder.allocator), .object_src = undefined, - .disable_libc = true, .build_options_contents = std.Buffer.initSize(builder.allocator, 0) catch unreachable, .c_std = Builder.CStd.C99, .system_linker_hack = false, - }; - self.computeOutFileNames(); - return self; - } - - fn initC(builder: *Builder, name: []const u8, kind: Kind, version: Version, static: bool) LibExeObjStep { - var self = LibExeObjStep{ - .builder = builder, - .name = name, - .kind = kind, - .version = version, - .static = static, - .target = Target.Native, - .cflags = ArrayList([]const u8).init(builder.allocator), - .source_files = ArrayList([]const u8).init(builder.allocator), - .object_files = ArrayList([]const u8).init(builder.allocator), - .step = Step.init(name, builder.allocator, make), - .link_libs = BufSet.init(builder.allocator), - .frameworks = BufSet.init(builder.allocator), - .full_path_libs = ArrayList([]const u8).init(builder.allocator), - .include_dirs = ArrayList([]const u8).init(builder.allocator), - .lib_paths = ArrayList([]const u8).init(builder.allocator), - .output_path = null, - .out_filename = undefined, - .major_only_filename = undefined, - .name_only_filename = undefined, - .object_src = undefined, - .build_mode = builtin.Mode.Debug, - .strip = false, - .need_flat_namespace_hack = false, - .disable_libc = false, - .is_zig = false, - .linker_script = null, - .c_std = Builder.CStd.C99, - .system_linker_hack = false, - - .root_src = undefined, - .verbose_link = false, - .output_h_path = undefined, - .out_h_filename = undefined, - .assembly_files = undefined, - .packages = undefined, - .build_options_contents = undefined, + .override_std_dir = null, + .exec_cmd_args = null, + .name_prefix = "", + .filter = null, }; self.computeOutFileNames(); return self; @@ -1014,23 +940,37 @@ pub const LibExeObjStep = struct { Kind.Exe => { self.out_filename = self.builder.fmt("{}{}", self.name, self.target.exeFileExt()); }, + Kind.Test => { + self.out_filename = self.builder.fmt("test{}", self.target.exeFileExt()); + }, Kind.Lib => { if (self.static) { - self.out_filename = self.builder.fmt("lib{}.a", self.name); + switch (self.target.getOs()) { + builtin.Os.windows => { + self.out_filename = self.builder.fmt("{}.lib", self.name); + }, + else => { + self.out_filename = self.builder.fmt("lib{}.a", self.name); + }, + } + self.out_lib_filename = self.out_filename; } else { switch (self.target.getOs()) { builtin.Os.ios, builtin.Os.macosx => { self.out_filename = self.builder.fmt("lib{}.{d}.{d}.{d}.dylib", self.name, self.version.major, self.version.minor, self.version.patch); self.major_only_filename = self.builder.fmt("lib{}.{d}.dylib", self.name, self.version.major); self.name_only_filename = self.builder.fmt("lib{}.dylib", self.name); + self.out_lib_filename = self.out_filename; }, builtin.Os.windows => { self.out_filename = self.builder.fmt("{}.dll", self.name); + self.out_lib_filename = self.builder.fmt("{}.lib", self.name); }, else => { self.out_filename = self.builder.fmt("lib{}.so.{d}.{d}.{d}", self.name, self.version.major, self.version.minor, self.version.patch); self.major_only_filename = self.builder.fmt("lib{}.so.{d}", self.name, self.version.major); self.name_only_filename = self.builder.fmt("lib{}.so", self.name); + self.out_lib_filename = self.out_filename; }, } } @@ -1065,7 +1005,11 @@ pub const LibExeObjStep = struct { self.step.dependOn(&lib.step); - self.full_path_libs.append(lib.getOutputPath()) catch unreachable; + if (lib.static or self.target.isWindows()) { + self.object_files.append(lib.getOutputLibPath()) catch unreachable; + } else { + self.full_path_libs.append(lib.getOutputPath()) catch unreachable; + } // TODO should be some kind of isolated directory that only has this header in it self.include_dirs.append(self.builder.cache_root) catch unreachable; @@ -1081,24 +1025,44 @@ pub const LibExeObjStep = struct { } pub fn linkSystemLibrary(self: *LibExeObjStep, name: []const u8) void { - assert(self.kind != Kind.Obj); self.link_libs.put(name) catch unreachable; } - pub fn addSourceFile(self: *LibExeObjStep, file: []const u8) void { - assert(self.kind != Kind.Obj); - assert(!self.is_zig); - self.source_files.append(file) catch unreachable; + pub fn setNamePrefix(self: *LibExeObjStep, text: []const u8) void { + assert(self.kind == Kind.Test); + self.name_prefix = text; + } + + pub fn setFilter(self: *LibExeObjStep, text: ?[]const u8) void { + assert(self.kind == Kind.Test); + self.filter = text; + } + + pub fn addCSourceFile(self: *LibExeObjStep, file: []const u8, args: []const []const u8) void { + const c_source_file = self.builder.allocator.create(CSourceFile) catch unreachable; + c_source_file.* = CSourceFile{ + .source_path = file, + .args = args, + }; + self.c_source_files.append(c_source_file) catch unreachable; } pub fn setVerboseLink(self: *LibExeObjStep, value: bool) void { self.verbose_link = value; } + pub fn setVerboseCC(self: *LibExeObjStep, value: bool) void { + self.verbose_cc = value; + } + pub fn setBuildMode(self: *LibExeObjStep, mode: builtin.Mode) void { self.build_mode = mode; } + pub fn overrideStdDir(self: *LibExeObjStep, dir_path: []const u8) void { + self.override_std_dir = dir_path; + } + pub fn setOutputPath(self: *LibExeObjStep, file_path: []const u8) void { self.output_path = file_path; @@ -1115,6 +1079,22 @@ pub const LibExeObjStep = struct { ) catch unreachable; } + pub fn setOutputLibPath(self: *LibExeObjStep, file_path: []const u8) void { + assert(self.kind == Kind.Lib); + if (self.static) + return self.setOutputPath(file_path); + + self.output_lib_path = file_path; + } + + pub fn getOutputLibPath(self: *LibExeObjStep) []const u8 { + assert(self.kind == Kind.Lib); + return if (self.output_lib_path) |output_lib_path| output_lib_path else os.path.join( + self.builder.allocator, + [][]const u8{ self.builder.cache_root, self.out_lib_filename }, + ) catch unreachable; + } + pub fn setOutputHPath(self: *LibExeObjStep, file_path: []const u8) void { self.output_h_path = file_path; @@ -1149,17 +1129,15 @@ pub const LibExeObjStep = struct { self.object_files.append(obj.getOutputPath()) catch unreachable; - // TODO make this lazy instead of stateful - if (!obj.disable_libc) { - self.disable_libc = false; - } - // TODO should be some kind of isolated directory that only has this header in it self.include_dirs.append(self.builder.cache_root) catch unreachable; + + if (obj.link_libs.exists("c")) { + self.link_libs.put("c") catch unreachable; + } } pub fn addBuildOption(self: *LibExeObjStep, comptime T: type, name: []const u8, value: T) void { - assert(self.is_zig); const out = &std.io.BufferOutStream.init(&self.build_options_contents).stream; out.print("pub const {} = {};\n", name, value) catch unreachable; } @@ -1173,23 +1151,15 @@ pub const LibExeObjStep = struct { } pub fn addPackagePath(self: *LibExeObjStep, name: []const u8, pkg_index_path: []const u8) void { - assert(self.is_zig); - self.packages.append(Pkg{ .name = name, .path = pkg_index_path, }) catch unreachable; } - pub fn addCompileFlags(self: *LibExeObjStep, flags: []const []const u8) void { - for (flags) |flag| { - self.cflags.append(flag) catch unreachable; - } - } - - pub fn setNoStdLib(self: *LibExeObjStep, disable: bool) void { - assert(!self.is_zig); - self.disable_libc = disable; + pub fn setExecCmd(self: *LibExeObjStep, args: []const ?[]const u8) void { + assert(self.kind == Kind.Test); + self.exec_cmd_args = args; } pub fn enableSystemLinkerHack(self: *LibExeObjStep) void { @@ -1198,15 +1168,11 @@ pub const LibExeObjStep = struct { fn make(step: *Step) !void { const self = @fieldParentPtr(LibExeObjStep, "step", step); - return if (self.is_zig) self.makeZig() else self.makeC(); - } - - fn makeZig(self: *LibExeObjStep) !void { const builder = self.builder; - assert(self.is_zig); - - if (self.root_src == null and self.object_files.len == 0 and self.assembly_files.len == 0) { + if (self.root_src == null and self.object_files.len == 0 and + self.assembly_files.len == 0 and self.c_source_files.len == 0) + { warn("{}: linker needs 1 or more objects to link\n", self.step.name); return error.NeedAnObject; } @@ -1220,6 +1186,7 @@ pub const LibExeObjStep = struct { Kind.Lib => "build-lib", Kind.Exe => "build-exe", Kind.Obj => "build-obj", + Kind.Test => "test", }; zig_args.append(cmd) catch unreachable; @@ -1227,6 +1194,14 @@ pub const LibExeObjStep = struct { zig_args.append(builder.pathFromRoot(root_src)) catch unreachable; } + for (self.c_source_files.toSliceConst()) |c_source_file| { + try zig_args.append("--c-source"); + for (c_source_file.args) |arg| { + try zig_args.append(arg); + } + try zig_args.append(self.builder.pathFromRoot(c_source_file.source_path)); + } + if (self.build_options_contents.len() > 0) { const build_options_file = try os.path.join( builder.allocator, @@ -1239,6 +1214,16 @@ pub const LibExeObjStep = struct { try zig_args.append("--pkg-end"); } + if (self.filter) |filter| { + try zig_args.append("--test-filter"); + try zig_args.append(filter); + } + + if (self.name_prefix.len != 0) { + try zig_args.append("--test-name-prefix"); + try zig_args.append(self.name_prefix); + } + for (self.object_files.toSliceConst()) |object_file| { zig_args.append("--object") catch unreachable; zig_args.append(builder.pathFromRoot(object_file)) catch unreachable; @@ -1255,6 +1240,7 @@ pub const LibExeObjStep = struct { if (builder.verbose_ir) zig_args.append("--verbose-ir") catch unreachable; if (builder.verbose_llvm_ir) zig_args.append("--verbose-llvm-ir") catch unreachable; if (builder.verbose_link or self.verbose_link) zig_args.append("--verbose-link") catch unreachable; + if (builder.verbose_cc or self.verbose_cc) zig_args.append("--verbose-cc") catch unreachable; if (self.strip) { zig_args.append("--strip") catch unreachable; @@ -1274,6 +1260,12 @@ pub const LibExeObjStep = struct { zig_args.append("--output") catch unreachable; zig_args.append(output_path) catch unreachable; + if (self.kind == Kind.Lib and !self.static) { + const output_lib_path = builder.pathFromRoot(self.getOutputLibPath()); + zig_args.append("--output-lib") catch unreachable; + zig_args.append(output_lib_path) catch unreachable; + } + if (self.kind != Kind.Exe) { const output_h_path = self.getOutputHPath(); zig_args.append("--output-h") catch unreachable; @@ -1293,7 +1285,7 @@ pub const LibExeObjStep = struct { zig_args.append("--ver-patch") catch unreachable; zig_args.append(builder.fmt("{}", self.version.patch)) catch unreachable; } - if (self.kind == Kind.Exe and self.static) { + if ((self.kind == Kind.Exe or self.kind == Kind.Test) and self.static) { zig_args.append("--static") catch unreachable; } @@ -1325,11 +1317,16 @@ pub const LibExeObjStep = struct { } } - if (!self.disable_libc) { - zig_args.append("--library") catch unreachable; - zig_args.append("c") catch unreachable; + if (self.exec_cmd_args) |exec_cmd_args| { + for (exec_cmd_args) |cmd_arg| { + if (cmd_arg) |arg| { + try zig_args.append("--test-cmd"); + try zig_args.append(arg); + } else { + try zig_args.append("--test-cmd-bin"); + } + } } - for (self.packages.toSliceConst()) |pkg| { zig_args.append("--pkg-begin") catch unreachable; zig_args.append(pkg.name) catch unreachable; @@ -1363,8 +1360,14 @@ pub const LibExeObjStep = struct { } for (self.full_path_libs.toSliceConst()) |full_path_lib| { - zig_args.append("--library") catch unreachable; - zig_args.append(builder.pathFromRoot(full_path_lib)) catch unreachable; + try zig_args.append("--library"); + try zig_args.append(builder.pathFromRoot(full_path_lib)); + + const full_path_lib_abs = builder.pathFromRoot(full_path_lib); + if (os.path.dirname(full_path_lib_abs)) |dirname| { + try zig_args.append("-rpath"); + try zig_args.append(dirname); + } } if (self.target.isDarwin()) { @@ -1379,558 +1382,16 @@ pub const LibExeObjStep = struct { try zig_args.append("--system-linker-hack"); } - try builder.spawnChild(zig_args.toSliceConst()); - - if (self.kind == Kind.Lib and !self.static and self.target.wantSharedLibSymLinks()) { - try doAtomicSymLinks(builder.allocator, output_path, self.major_only_filename, self.name_only_filename); - } - } - - fn appendCompileFlags(self: *LibExeObjStep, args: *ArrayList([]const u8)) void { - if (!self.strip) { - args.append("-g") catch unreachable; - } - switch (self.build_mode) { - builtin.Mode.Debug => { - if (self.disable_libc) { - args.append("-fno-stack-protector") catch unreachable; - } else { - args.append("-fstack-protector-strong") catch unreachable; - args.append("--param") catch unreachable; - args.append("ssp-buffer-size=4") catch unreachable; - } - }, - builtin.Mode.ReleaseSafe => { - args.append("-O2") catch unreachable; - if (self.disable_libc) { - args.append("-fno-stack-protector") catch unreachable; - } else { - args.append("-D_FORTIFY_SOURCE=2") catch unreachable; - args.append("-fstack-protector-strong") catch unreachable; - args.append("--param") catch unreachable; - args.append("ssp-buffer-size=4") catch unreachable; - } - }, - builtin.Mode.ReleaseFast, builtin.Mode.ReleaseSmall => { - args.append("-O2") catch unreachable; - args.append("-fno-stack-protector") catch unreachable; - }, - } - - for (self.include_dirs.toSliceConst()) |dir| { - args.append("-I") catch unreachable; - args.append(self.builder.pathFromRoot(dir)) catch unreachable; - } - - for (self.cflags.toSliceConst()) |cflag| { - args.append(cflag) catch unreachable; - } - - if (self.disable_libc) { - args.append("-nostdlib") catch unreachable; - } - } - - fn makeC(self: *LibExeObjStep) !void { - const builder = self.builder; - - const cc = builder.getCCExe(); - - assert(!self.is_zig); - - var cc_args = ArrayList([]const u8).init(builder.allocator); - defer cc_args.deinit(); - - cc_args.append(cc) catch unreachable; - - const is_darwin = self.target.isDarwin(); - - const c_std_arg = switch (self.c_std) { - Builder.CStd.C89 => "-std=c89", - Builder.CStd.C99 => "-std=c99", - Builder.CStd.C11 => "-std=c11", - }; - try cc_args.append(c_std_arg); - - switch (self.kind) { - Kind.Obj => { - cc_args.append("-c") catch unreachable; - cc_args.append(builder.pathFromRoot(self.object_src)) catch unreachable; - - const output_path = builder.pathFromRoot(self.getOutputPath()); - cc_args.append("-o") catch unreachable; - cc_args.append(output_path) catch unreachable; - - self.appendCompileFlags(&cc_args); - - try builder.spawnChild(cc_args.toSliceConst()); - }, - Kind.Lib => { - for (self.source_files.toSliceConst()) |source_file| { - cc_args.resize(0) catch unreachable; - cc_args.append(cc) catch unreachable; - - if (!self.static) { - cc_args.append("-fPIC") catch unreachable; - } - - const abs_source_file = builder.pathFromRoot(source_file); - cc_args.append("-c") catch unreachable; - cc_args.append(abs_source_file) catch unreachable; - - const cache_o_src = os.path.join( - builder.allocator, - [][]const u8{ builder.cache_root, source_file }, - ) catch unreachable; - if (os.path.dirname(cache_o_src)) |cache_o_dir| { - try builder.makePath(cache_o_dir); - } - const cache_o_file = builder.fmt("{}{}", cache_o_src, self.target.oFileExt()); - cc_args.append("-o") catch unreachable; - cc_args.append(builder.pathFromRoot(cache_o_file)) catch unreachable; - - self.appendCompileFlags(&cc_args); - - try builder.spawnChild(cc_args.toSliceConst()); - - self.object_files.append(cache_o_file) catch unreachable; - } - - if (self.static) { - // ar - cc_args.resize(0) catch unreachable; - cc_args.append("ar") catch unreachable; - - cc_args.append("qc") catch unreachable; - - const output_path = builder.pathFromRoot(self.getOutputPath()); - cc_args.append(output_path) catch unreachable; - - for (self.object_files.toSliceConst()) |object_file| { - cc_args.append(builder.pathFromRoot(object_file)) catch unreachable; - } - - try builder.spawnChild(cc_args.toSliceConst()); - - // ranlib - cc_args.resize(0) catch unreachable; - cc_args.append("ranlib") catch unreachable; - cc_args.append(output_path) catch unreachable; - - try builder.spawnChild(cc_args.toSliceConst()); - } else { - cc_args.resize(0) catch unreachable; - cc_args.append(cc) catch unreachable; - - if (is_darwin) { - cc_args.append("-dynamiclib") catch unreachable; - - cc_args.append("-Wl,-headerpad_max_install_names") catch unreachable; - - cc_args.append("-compatibility_version") catch unreachable; - cc_args.append(builder.fmt("{}.0.0", self.version.major)) catch unreachable; - - cc_args.append("-current_version") catch unreachable; - cc_args.append(builder.fmt("{}.{}.{}", self.version.major, self.version.minor, self.version.patch)) catch unreachable; - - const install_name = builder.pathFromRoot(os.path.join( - builder.allocator, - [][]const u8{ builder.cache_root, self.major_only_filename }, - ) catch unreachable); - cc_args.append("-install_name") catch unreachable; - cc_args.append(install_name) catch unreachable; - } else { - cc_args.append("-fPIC") catch unreachable; - cc_args.append("-shared") catch unreachable; - - const soname_arg = builder.fmt("-Wl,-soname,lib{}.so.{d}", self.name, self.version.major); - defer builder.allocator.free(soname_arg); - cc_args.append(soname_arg) catch unreachable; - } - - const output_path = builder.pathFromRoot(self.getOutputPath()); - cc_args.append("-o") catch unreachable; - cc_args.append(output_path) catch unreachable; - - for (self.object_files.toSliceConst()) |object_file| { - cc_args.append(builder.pathFromRoot(object_file)) catch unreachable; - } - - if (!is_darwin) { - const rpath_arg = builder.fmt("-Wl,-rpath,{}", try os.path.realAlloc( - builder.allocator, - builder.pathFromRoot(builder.cache_root), - )); - defer builder.allocator.free(rpath_arg); - try cc_args.append(rpath_arg); - - try cc_args.append("-rdynamic"); - } - - for (self.full_path_libs.toSliceConst()) |full_path_lib| { - cc_args.append(builder.pathFromRoot(full_path_lib)) catch unreachable; - } - - { - var it = self.link_libs.iterator(); - while (it.next()) |entry| { - cc_args.append(builder.fmt("-l{}", entry.key)) catch unreachable; - } - } - - if (is_darwin and !self.static) { - var it = self.frameworks.iterator(); - while (it.next()) |entry| { - cc_args.append("-framework") catch unreachable; - cc_args.append(entry.key) catch unreachable; - } - } - - try builder.spawnChild(cc_args.toSliceConst()); - - if (self.target.wantSharedLibSymLinks()) { - try doAtomicSymLinks(builder.allocator, output_path, self.major_only_filename, self.name_only_filename); - } - } - }, - Kind.Exe => { - for (self.source_files.toSliceConst()) |source_file| { - cc_args.resize(0) catch unreachable; - cc_args.append(cc) catch unreachable; - - const abs_source_file = builder.pathFromRoot(source_file); - cc_args.append("-c") catch unreachable; - cc_args.append(abs_source_file) catch unreachable; - - const cache_o_src = os.path.join( - builder.allocator, - [][]const u8{ builder.cache_root, source_file }, - ) catch unreachable; - if (os.path.dirname(cache_o_src)) |cache_o_dir| { - try builder.makePath(cache_o_dir); - } - const cache_o_file = builder.fmt("{}{}", cache_o_src, self.target.oFileExt()); - cc_args.append("-o") catch unreachable; - cc_args.append(builder.pathFromRoot(cache_o_file)) catch unreachable; - - for (self.cflags.toSliceConst()) |cflag| { - cc_args.append(cflag) catch unreachable; - } - - for (self.include_dirs.toSliceConst()) |dir| { - cc_args.append("-I") catch unreachable; - cc_args.append(builder.pathFromRoot(dir)) catch unreachable; - } - - try builder.spawnChild(cc_args.toSliceConst()); - - self.object_files.append(cache_o_file) catch unreachable; - } - - cc_args.resize(0) catch unreachable; - cc_args.append(cc) catch unreachable; - - for (self.object_files.toSliceConst()) |object_file| { - cc_args.append(builder.pathFromRoot(object_file)) catch unreachable; - } - - const output_path = builder.pathFromRoot(self.getOutputPath()); - cc_args.append("-o") catch unreachable; - cc_args.append(output_path) catch unreachable; - - const rpath_arg = builder.fmt("-Wl,-rpath,{}", try os.path.realAlloc( - builder.allocator, - builder.pathFromRoot(builder.cache_root), - )); - defer builder.allocator.free(rpath_arg); - try cc_args.append(rpath_arg); - - try cc_args.append("-rdynamic"); - - { - var it = self.link_libs.iterator(); - while (it.next()) |entry| { - cc_args.append(builder.fmt("-l{}", entry.key)) catch unreachable; - } - } - - if (is_darwin) { - if (self.need_flat_namespace_hack) { - cc_args.append("-Wl,-flat_namespace") catch unreachable; - } - cc_args.append("-Wl,-search_paths_first") catch unreachable; - } - - for (self.full_path_libs.toSliceConst()) |full_path_lib| { - cc_args.append(builder.pathFromRoot(full_path_lib)) catch unreachable; - } - - if (is_darwin) { - var it = self.frameworks.iterator(); - while (it.next()) |entry| { - cc_args.append("-framework") catch unreachable; - cc_args.append(entry.key) catch unreachable; - } - } - - try builder.spawnChild(cc_args.toSliceConst()); - }, - } - } -}; - -pub const TestStep = struct { - step: Step, - builder: *Builder, - root_src: []const u8, - build_mode: builtin.Mode, - verbose: bool, - link_libs: BufSet, - name_prefix: []const u8, - filter: ?[]const u8, - target: Target, - exec_cmd_args: ?[]const ?[]const u8, - include_dirs: ArrayList([]const u8), - lib_paths: ArrayList([]const u8), - packages: ArrayList(Pkg), - object_files: ArrayList([]const u8), - output_path: ?[]const u8, - system_linker_hack: bool, - override_std_dir: ?[]const u8, - - pub fn init(builder: *Builder, root_src: []const u8) TestStep { - const step_name = builder.fmt("test {}", root_src); - return TestStep{ - .step = Step.init(step_name, builder.allocator, make), - .builder = builder, - .root_src = root_src, - .build_mode = builtin.Mode.Debug, - .verbose = false, - .name_prefix = "", - .filter = null, - .link_libs = BufSet.init(builder.allocator), - .target = Target{ .Native = {} }, - .exec_cmd_args = null, - .include_dirs = ArrayList([]const u8).init(builder.allocator), - .lib_paths = ArrayList([]const u8).init(builder.allocator), - .packages = ArrayList(Pkg).init(builder.allocator), - .object_files = ArrayList([]const u8).init(builder.allocator), - .output_path = null, - .system_linker_hack = false, - .override_std_dir = null, - }; - } - - pub fn addLibPath(self: *TestStep, path: []const u8) void { - self.lib_paths.append(path) catch unreachable; - } - - pub fn addPackagePath(self: *TestStep, name: []const u8, pkg_index_path: []const u8) void { - self.packages.append(Pkg{ - .name = name, - .path = pkg_index_path, - }) catch unreachable; - } - - pub fn setVerbose(self: *TestStep, value: bool) void { - self.verbose = value; - } - - pub fn addIncludeDir(self: *TestStep, path: []const u8) void { - self.include_dirs.append(path) catch unreachable; - } - - pub fn setBuildMode(self: *TestStep, mode: builtin.Mode) void { - self.build_mode = mode; - } - - pub fn overrideStdDir(self: *TestStep, dir_path: []const u8) void { - self.override_std_dir = dir_path; - } - - pub fn setOutputPath(self: *TestStep, file_path: []const u8) void { - self.output_path = file_path; - - // catch a common mistake - if (mem.eql(u8, self.builder.pathFromRoot(file_path), self.builder.pathFromRoot("."))) { - debug.panic("setOutputPath wants a file path, not a directory\n"); - } - } - - pub fn getOutputPath(self: *TestStep) []const u8 { - if (self.output_path) |output_path| { - return output_path; - } else { - const basename = self.builder.fmt("test{}", self.target.exeFileExt()); - return os.path.join( - self.builder.allocator, - [][]const u8{ self.builder.cache_root, basename }, - ) catch unreachable; - } - } - - pub fn linkSystemLibrary(self: *TestStep, name: []const u8) void { - self.link_libs.put(name) catch unreachable; - } - - pub fn setNamePrefix(self: *TestStep, text: []const u8) void { - self.name_prefix = text; - } - - pub fn setFilter(self: *TestStep, text: ?[]const u8) void { - self.filter = text; - } - - pub fn addObject(self: *TestStep, obj: *LibExeObjStep) void { - assert(obj.kind == LibExeObjStep.Kind.Obj); - - self.step.dependOn(&obj.step); - - self.object_files.append(obj.getOutputPath()) catch unreachable; - - // TODO should be some kind of isolated directory that only has this header in it - self.include_dirs.append(self.builder.cache_root) catch unreachable; - } - - pub fn addObjectFile(self: *TestStep, path: []const u8) void { - self.object_files.append(path) catch unreachable; - } - - pub fn setTarget(self: *TestStep, target_arch: builtin.Arch, target_os: builtin.Os, target_environ: builtin.Environ) void { - self.target = Target{ - .Cross = CrossTarget{ - .arch = target_arch, - .os = target_os, - .environ = target_environ, - }, - }; - } - - pub fn setExecCmd(self: *TestStep, args: []const ?[]const u8) void { - self.exec_cmd_args = args; - } - - pub fn enableSystemLinkerHack(self: *TestStep) void { - self.system_linker_hack = true; - } - - fn make(step: *Step) !void { - const self = @fieldParentPtr(TestStep, "step", step); - const builder = self.builder; - - var zig_args = ArrayList([]const u8).init(builder.allocator); - defer zig_args.deinit(); - - try zig_args.append(builder.zig_exe); - - try zig_args.append("test"); - try zig_args.append(builder.pathFromRoot(self.root_src)); - - if (self.verbose) { - try zig_args.append("--verbose"); - } - - switch (self.build_mode) { - builtin.Mode.Debug => {}, - builtin.Mode.ReleaseSafe => try zig_args.append("--release-safe"), - builtin.Mode.ReleaseFast => try zig_args.append("--release-fast"), - builtin.Mode.ReleaseSmall => try zig_args.append("--release-small"), - } - - const output_path = builder.pathFromRoot(self.getOutputPath()); - try zig_args.append("--output"); - try zig_args.append(output_path); - - switch (self.target) { - Target.Native => {}, - Target.Cross => |cross_target| { - try zig_args.append("--target-arch"); - try zig_args.append(@tagName(cross_target.arch)); - - try zig_args.append("--target-os"); - try zig_args.append(@tagName(cross_target.os)); - - try zig_args.append("--target-environ"); - try zig_args.append(@tagName(cross_target.environ)); - }, - } - - if (self.filter) |filter| { - try zig_args.append("--test-filter"); - try zig_args.append(filter); - } - - if (self.name_prefix.len != 0) { - try zig_args.append("--test-name-prefix"); - try zig_args.append(self.name_prefix); - } - - for (self.object_files.toSliceConst()) |object_file| { - try zig_args.append("--object"); - try zig_args.append(builder.pathFromRoot(object_file)); - } - - { - var it = self.link_libs.iterator(); - while (true) { - const entry = it.next() orelse break; - try zig_args.append("--library"); - try zig_args.append(entry.key); - } - } - - if (self.exec_cmd_args) |exec_cmd_args| { - for (exec_cmd_args) |cmd_arg| { - if (cmd_arg) |arg| { - try zig_args.append("--test-cmd"); - try zig_args.append(arg); - } else { - try zig_args.append("--test-cmd-bin"); - } - } - } - - for (self.include_dirs.toSliceConst()) |include_path| { - try zig_args.append("-isystem"); - try zig_args.append(builder.pathFromRoot(include_path)); - } - - for (builder.include_paths.toSliceConst()) |include_path| { - try zig_args.append("-isystem"); - try zig_args.append(builder.pathFromRoot(include_path)); - } - - for (builder.rpaths.toSliceConst()) |rpath| { - try zig_args.append("-rpath"); - try zig_args.append(rpath); - } - - for (self.lib_paths.toSliceConst()) |lib_path| { - try zig_args.append("--library-path"); - try zig_args.append(lib_path); - } - - for (builder.lib_paths.toSliceConst()) |lib_path| { - try zig_args.append("--library-path"); - try zig_args.append(lib_path); - } - - for (self.packages.toSliceConst()) |pkg| { - zig_args.append("--pkg-begin") catch unreachable; - zig_args.append(pkg.name) catch unreachable; - zig_args.append(builder.pathFromRoot(pkg.path)) catch unreachable; - zig_args.append("--pkg-end") catch unreachable; - } - - if (self.system_linker_hack) { - try zig_args.append("--system-linker-hack"); - } if (self.override_std_dir) |dir| { try zig_args.append("--override-std-dir"); try zig_args.append(builder.pathFromRoot(dir)); } try builder.spawnChild(zig_args.toSliceConst()); + + if (self.kind == Kind.Lib and !self.static and self.target.wantSharedLibSymLinks()) { + try doAtomicSymLinks(builder.allocator, output_path, self.major_only_filename, self.name_only_filename); + } } }; @@ -1976,6 +1437,7 @@ const InstallArtifactStep = struct { pub fn create(builder: *Builder, artifact: *LibExeObjStep) *Self { const dest_dir = switch (artifact.kind) { LibExeObjStep.Kind.Obj => unreachable, + LibExeObjStep.Kind.Test => unreachable, LibExeObjStep.Kind.Exe => builder.exe_dir, LibExeObjStep.Kind.Lib => builder.lib_dir, }; @@ -2012,6 +1474,7 @@ const InstallArtifactStep = struct { builtin.Os.windows => {}, else => switch (self.artifact.kind) { LibExeObjStep.Kind.Obj => unreachable, + LibExeObjStep.Kind.Test => unreachable, LibExeObjStep.Kind.Exe => u32(0o755), LibExeObjStep.Kind.Lib => if (self.artifact.static) u32(0o666) else u32(0o755), }, diff --git a/test/build_examples.zig b/test/build_examples.zig index 66a6ecda1..c8a47bb09 100644 --- a/test/build_examples.zig +++ b/test/build_examples.zig @@ -7,12 +7,8 @@ pub fn addCases(cases: *tests.BuildExamplesContext) void { cases.addC("example/hello_world/hello_libc.zig"); cases.add("example/cat/main.zig"); cases.add("example/guess_number/main.zig"); - if (!is_windows) { - // TODO get this test passing on windows - // See https://github.com/ziglang/zig/issues/538 - cases.addBuildFile("example/shared_library/build.zig"); - cases.addBuildFile("example/mix_o_files/build.zig"); - } + cases.addBuildFile("example/shared_library/build.zig"); + cases.addBuildFile("example/mix_o_files/build.zig"); if (builtin.os != builtin.Os.macosx) { // TODO https://github.com/ziglang/zig/issues/1126 cases.addBuildFile("test/standalone/issue_339/build.zig"); diff --git a/test/stage1/c_abi/build.zig b/test/stage1/c_abi/build.zig index e0a108aeb..f3d5c64c0 100644 --- a/test/stage1/c_abi/build.zig +++ b/test/stage1/c_abi/build.zig @@ -3,9 +3,10 @@ const Builder = @import("std").build.Builder; pub fn build(b: *Builder) void { const rel_opts = b.standardReleaseOptions(); - const c_obj = b.addCObject("cfuncs", "cfuncs.c"); + const c_obj = b.addObject("cfuncs", null); + c_obj.addCSourceFile("cfuncs.c", [][]const u8{"-std=c99"}); c_obj.setBuildMode(rel_opts); - c_obj.setNoStdLib(true); + c_obj.linkSystemLibrary("c"); const main = b.addTest("main.zig"); main.setBuildMode(rel_opts);