From e76ce2c1d0d3988359267fd3030a81a52ec99f3f Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 25 Feb 2019 11:37:54 -0500 Subject: first class support for compiling C code New CLI parameter: --c-source [options] [file] It even works with `--cache on` when there are transitive dependencies. Instead of `builder.addCExecutable`, use `builder.addExecutable` and pass `null` for the root source file. Then use `builder.addCSourceFile`, which takes the path to the C code, and a list of C compiler args. Be sure to linkSystemLibrary("c") if you want libc headers to be available. Merge TestStep into LibExeObjStep. That was long overdue. --- src/main.cpp | 85 +++++++++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 67 insertions(+), 18 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 14a885f08a..a7f6e0b95d 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" @@ -77,6 +77,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 +182,6 @@ enum Cmd { CmdNone, CmdBuild, CmdBuiltin, - CmdHelp, CmdRun, CmdTargets, CmdTest, @@ -385,6 +385,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 +405,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 +514,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 +524,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 +590,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 +610,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) { @@ -714,6 +719,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 +810,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 +851,6 @@ int main(int argc, char **argv) { } break; case CmdBuiltin: - case CmdHelp: case CmdVersion: case CmdZen: case CmdTargets: @@ -910,15 +925,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 +988,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 +1046,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; @@ -1048,6 +1099,7 @@ int main(int argc, char **argv) { 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 +1199,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 +1208,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); } } -- cgit v1.2.3 From 525c2eaf5d49f537d4ccd48ab0c5bc1f52cc3204 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 25 Feb 2019 13:34:25 -0500 Subject: building DLLs on Windows works better --- src/all_types.hpp | 1 + src/codegen.cpp | 6 +++++- src/codegen.hpp | 1 + src/link.cpp | 1 + src/main.cpp | 6 ++++++ src/target.cpp | 6 ++++++ src/target.hpp | 1 + std/build.zig | 45 +++++++++++++++++++++++++++++++++++++++++++-- test/build_examples.zig | 8 ++------ 9 files changed, 66 insertions(+), 9 deletions(-) (limited to 'src/main.cpp') diff --git a/src/all_types.hpp b/src/all_types.hpp index baa0267238..8099f99f48 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -1743,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; diff --git a/src/codegen.cpp b/src/codegen.cpp index f14749bb28..3edff93be8 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; } @@ -8201,7 +8205,7 @@ static void gen_c_object(CodeGen *g, Buf *self_exe_path, CFile *c_file) { args.append("-c"); args.append(buf_ptr(c_source_file)); - if (!g->disable_pic) { + if (!g->disable_pic && target_supports_fpic(g->zig_target)) { args.append("-fPIC"); } diff --git a/src/codegen.hpp b/src/codegen.hpp index 035b759ec5..4f62cc4cbc 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/link.cpp b/src/link.cpp index 2068b8efb0..5f611ae5a6 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 a7f6e0b95d..1c154250ff 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -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" @@ -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; @@ -661,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; @@ -1094,6 +1098,8 @@ 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); diff --git a/src/target.cpp b/src/target.cpp index 01e8b90cea..49a1742945 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 620c9b2664..dceeee2eca 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/std/build.zig b/std/build.zig index 4386b730cd..0d03b0325f 100644 --- a/std/build.zig +++ b/std/build.zig @@ -815,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), @@ -839,6 +840,7 @@ pub const LibExeObjStep = struct { 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, @@ -901,10 +903,12 @@ 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), @@ -941,21 +945,32 @@ pub const LibExeObjStep = struct { }, 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; }, } } @@ -990,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; @@ -1060,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; @@ -1225,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; diff --git a/test/build_examples.zig b/test/build_examples.zig index 66a6ecda17..c8a47bb093 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"); -- cgit v1.2.3