authorgravatar for kcbanner@gmail.comCasey Banner <kcbanner@gmail.com> 2022-11-13 03:42:51-05:00
committergravatar for kcbanner@gmail.comCasey Banner <kcbanner@gmail.com> 2023-01-04 21:45:06-05:00
logb42442f5b4913938b04f580b1d13a1fd7318514d
treedb86066c6ea6dc6f34904c162a49bf450c6419bb
parenta03c8ef4bf526888518d13de1794c28375900cc2

windows: fixes to support using zig cc/c++ with CMake on Windows

Using zig cc with CMake on Windows was failing during compiler detection. -nostdinc was causing the crt not to be linked, and Coff/lld.zig assumed that wWinMainCRTStartup would be present in this case. -nostdlib did not prevent the default behaviour of linking libc++ when zig c++ was used. This caused libc++ to be built when CMake ran ABI detection using zig c++, which fails as libcxxabi cannot compile under MSVC. - Change the behaviour of COFF -nostdinc to set /entry to the function that the default CRT method for the specified subsystem would have called. - Fix -ENTRY being passed twice if it was specified explicitly and -nostdlib was present. - Add support for /pdb, /version, /implib, and /subsystem as linker args (passed by CMake) - Remove -Ddisable-zstd, no longer needed - Add -Ddisable-libcpp for use when bootstrapping on msvc

6 files changed, 72 insertions(+), 12 deletions(-)

CMakeLists.txt+1
...@@ -92,6 +92,7 @@ set(ZIG_SHARED_LLVM off CACHE BOOL "Prefer linking against shared LLVM libraries...@@ -92,6 +92,7 @@ set(ZIG_SHARED_LLVM off CACHE BOOL "Prefer linking against shared LLVM libraries
92set(ZIG_STATIC_LLVM off CACHE BOOL "Prefer linking against static LLVM libraries")92set(ZIG_STATIC_LLVM off CACHE BOOL "Prefer linking against static LLVM libraries")
93set(ZIG_STATIC_ZLIB off CACHE BOOL "Prefer linking against static zlib")93set(ZIG_STATIC_ZLIB off CACHE BOOL "Prefer linking against static zlib")
94set(ZIG_ENABLE_ZSTD on CACHE BOOL "Enable linking zstd")94set(ZIG_ENABLE_ZSTD on CACHE BOOL "Enable linking zstd")
95set(ZIG_ENABLE_LIBCPP on CACHE BOOL "Enable linking libcpp")
95set(ZIG_STATIC_ZSTD off CACHE BOOL "Prefer linking against static zstd")96set(ZIG_STATIC_ZSTD off CACHE BOOL "Prefer linking against static zstd")
96set(ZIG_USE_CCACHE off CACHE BOOL "Use ccache")97set(ZIG_USE_CCACHE off CACHE BOOL "Use ccache")
9798
build.zig+5-8
...@@ -99,7 +99,7 @@ pub fn build(b: *Builder) !void {...@@ -99,7 +99,7 @@ pub fn build(b: *Builder) !void {
99 const enable_macos_sdk = b.option(bool, "enable-macos-sdk", "Run tests requiring presence of macOS SDK and frameworks") orelse false;99 const enable_macos_sdk = b.option(bool, "enable-macos-sdk", "Run tests requiring presence of macOS SDK and frameworks") orelse false;
100 const enable_symlinks_windows = b.option(bool, "enable-symlinks-windows", "Run tests requiring presence of symlinks on Windows") orelse false;100 const enable_symlinks_windows = b.option(bool, "enable-symlinks-windows", "Run tests requiring presence of symlinks on Windows") orelse false;
101 const config_h_path_option = b.option([]const u8, "config_h", "Path to the generated config.h");101 const config_h_path_option = b.option([]const u8, "config_h", "Path to the generated config.h");
102 const disable_zstd = b.option(bool, "disable-zstd", "Skip linking zstd") orelse false;102 const disable_libcpp = b.option(bool, "disable-libcpp", "Skip building/linking libcpp") orelse false;
103103
104 if (!skip_install_lib_files) {104 if (!skip_install_lib_files) {
105 b.installDirectory(InstallDirectoryOptions{105 b.installDirectory(InstallDirectoryOptions{
...@@ -278,8 +278,8 @@ pub fn build(b: *Builder) !void {...@@ -278,8 +278,8 @@ pub fn build(b: *Builder) !void {
278 try addCmakeCfgOptionsToExe(b, cfg, test_cases, use_zig_libcxx);278 try addCmakeCfgOptionsToExe(b, cfg, test_cases, use_zig_libcxx);
279 } else {279 } else {
280 // Here we are -Denable-llvm but no cmake integration.280 // Here we are -Denable-llvm but no cmake integration.
281 try addStaticLlvmOptionsToExe(exe, !disable_zstd);281 try addStaticLlvmOptionsToExe(exe);
282 try addStaticLlvmOptionsToExe(test_cases, !disable_zstd);282 try addStaticLlvmOptionsToExe(test_cases);
283 }283 }
284 if (target.isWindows()) {284 if (target.isWindows()) {
285 inline for (.{ exe, test_cases }) |artifact| {285 inline for (.{ exe, test_cases }) |artifact| {
...@@ -607,7 +607,7 @@ fn addCmakeCfgOptionsToExe(...@@ -607,7 +607,7 @@ fn addCmakeCfgOptionsToExe(
607 }607 }
608}608}
609609
610fn addStaticLlvmOptionsToExe(exe: *std.build.LibExeObjStep, link_zstd: bool) !void {610fn addStaticLlvmOptionsToExe(exe: *std.build.LibExeObjStep) !void {
611 // Adds the Zig C++ sources which both stage1 and stage2 need.611 // Adds the Zig C++ sources which both stage1 and stage2 need.
612 //612 //
613 // We need this because otherwise zig_clang_cc1_main.cpp ends up pulling613 // We need this because otherwise zig_clang_cc1_main.cpp ends up pulling
...@@ -629,10 +629,7 @@ fn addStaticLlvmOptionsToExe(exe: *std.build.LibExeObjStep, link_zstd: bool) !vo...@@ -629,10 +629,7 @@ fn addStaticLlvmOptionsToExe(exe: *std.build.LibExeObjStep, link_zstd: bool) !vo
629 }629 }
630630
631 exe.linkSystemLibrary("z");631 exe.linkSystemLibrary("z");
632632 exe.linkSystemLibrary("zstd");
633 if (link_zstd) {
634 exe.linkSystemLibrary("zstd");
635 }
636633
637 if (exe.target.getOs().tag != .windows or exe.target.getAbi() != .msvc) {634 if (exe.target.getOs().tag != .windows or exe.target.getAbi() != .msvc) {
638 // This means we rely on clang-or-zig-built LLVM, Clang, LLD libraries.635 // This means we rely on clang-or-zig-built LLVM, Clang, LLD libraries.
src/Compilation.zig+7
...@@ -1042,6 +1042,11 @@ pub const InitOptions = struct {...@@ -1042,6 +1042,11 @@ pub const InitOptions = struct {
1042 /// (Darwin) remove dylibs that are unreachable by the entry point or exported symbols1042 /// (Darwin) remove dylibs that are unreachable by the entry point or exported symbols
1043 dead_strip_dylibs: bool = false,1043 dead_strip_dylibs: bool = false,
1044 libcxx_abi_version: libcxx.AbiVersion = libcxx.AbiVersion.default,1044 libcxx_abi_version: libcxx.AbiVersion = libcxx.AbiVersion.default,
1045 /// (Windows) PDB source path prefix to instruct the linker how to resolve relative
1046 /// paths when consolidating CodeView streams into a single PDB file.
1047 pdb_source_path: ?[]const u8 = null,
1048 /// (Windows) PDB output path
1049 pdb_out_path: ?[]const u8 = null,
1045};1050};
10461051
1047fn addPackageTableToCacheHash(1052fn addPackageTableToCacheHash(
...@@ -1892,6 +1897,8 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {...@@ -1892,6 +1897,8 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
1892 .headerpad_max_install_names = options.headerpad_max_install_names,1897 .headerpad_max_install_names = options.headerpad_max_install_names,
1893 .dead_strip_dylibs = options.dead_strip_dylibs,1898 .dead_strip_dylibs = options.dead_strip_dylibs,
1894 .force_undefined_symbols = .{},1899 .force_undefined_symbols = .{},
1900 .pdb_source_path = pdb_source_path,
1901 .pdb_out_path = options.pdb_out_path,
1895 });1902 });
1896 errdefer bin_file.destroy();1903 errdefer bin_file.destroy();
1897 comp.* = .{1904 comp.* = .{
src/link.zig+3
...@@ -223,6 +223,9 @@ pub const Options = struct {...@@ -223,6 +223,9 @@ pub const Options = struct {
223 /// paths when consolidating CodeView streams into a single PDB file.223 /// paths when consolidating CodeView streams into a single PDB file.
224 pdb_source_path: ?[]const u8 = null,224 pdb_source_path: ?[]const u8 = null,
225225
226 /// (Windows) PDB output path
227 pdb_out_path: ?[]const u8 = null,
228
226 /// (Windows) .def file to specify when linking229 /// (Windows) .def file to specify when linking
227 module_definition_file: ?[]const u8 = null,230 module_definition_file: ?[]const u8 = null,
228231
src/link/Coff/lld.zig+32-3
...@@ -166,12 +166,16 @@ pub fn linkWithLLD(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Nod...@@ -166,12 +166,16 @@ pub fn linkWithLLD(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Nod
166 try argv.append("-DEBUG");166 try argv.append("-DEBUG");
167167
168 const out_ext = std.fs.path.extension(full_out_path);168 const out_ext = std.fs.path.extension(full_out_path);
169 const out_pdb = try allocPrint(arena, "{s}.pdb", .{169 const out_pdb = self.base.options.pdb_out_path orelse try allocPrint(arena, "{s}.pdb", .{
170 full_out_path[0 .. full_out_path.len - out_ext.len],170 full_out_path[0 .. full_out_path.len - out_ext.len],
171 });171 });
172
172 try argv.append(try allocPrint(arena, "-PDB:{s}", .{out_pdb}));173 try argv.append(try allocPrint(arena, "-PDB:{s}", .{out_pdb}));
173 try argv.append(try allocPrint(arena, "-PDBALTPATH:{s}", .{out_pdb}));174 try argv.append(try allocPrint(arena, "-PDBALTPATH:{s}", .{out_pdb}));
174 }175 }
176 if (self.base.options.version) |version| {
177 try argv.append(try allocPrint(arena, "-VERSION:{}.{}", .{ version.major, version.minor }));
178 }
175 if (self.base.options.lto) {179 if (self.base.options.lto) {
176 switch (self.base.options.optimize_mode) {180 switch (self.base.options.optimize_mode) {
177 .Debug => {},181 .Debug => {},
...@@ -427,7 +431,7 @@ pub fn linkWithLLD(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Nod...@@ -427,7 +431,7 @@ pub fn linkWithLLD(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Nod
427 }431 }
428 } else {432 } else {
429 try argv.append("-NODEFAULTLIB");433 try argv.append("-NODEFAULTLIB");
430 if (!is_lib) {434 if (!is_lib and self.base.options.entry == null) {
431 if (self.base.options.module) |module| {435 if (self.base.options.module) |module| {
432 if (module.stage1_flags.have_winmain_crt_startup) {436 if (module.stage1_flags.have_winmain_crt_startup) {
433 try argv.append("-ENTRY:WinMainCRTStartup");437 try argv.append("-ENTRY:WinMainCRTStartup");
...@@ -435,7 +439,32 @@ pub fn linkWithLLD(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Nod...@@ -435,7 +439,32 @@ pub fn linkWithLLD(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Nod
435 try argv.append("-ENTRY:wWinMainCRTStartup");439 try argv.append("-ENTRY:wWinMainCRTStartup");
436 }440 }
437 } else {441 } else {
438 try argv.append("-ENTRY:wWinMainCRTStartup");442 // If the crt isn't being linked, it won't provide the CRT startup methods that
443 // call through to the user-provided entrypoint. Instead, choose the entry point
444 // that the CRT methods would have called. Note that this differs from the behaviour
445 // of link.exe (which still tries to use the CRT methods in this case), but this
446 // fixes CMake compiler checks when using zig cc on Windows, as Windows-Clang.cmake
447 // does not specify /entry:main
448
449 // TODO: I think the correct thing to do in this case would be to inspect the object
450 // being linked (like link.exe / lld-link does) and detect which symbols are available.
451 // This would allow detection of the w variants, as well as the crt methods.
452 if (resolved_subsystem) |subsystem| {
453 switch (subsystem) {
454 .Console => {
455 // The default is to call mainCRTStartup/wmainCRTStartup, which calls main/wmain
456 try argv.append("-ENTRY:main");
457 },
458 .Windows => {
459 // The default is to call WinMainCRTStartup/wWinMainCRTStartup, which calls WinMain/wWinMain
460 try argv.append("-ENTRY:WinMain");
461 },
462 else => {}
463 }
464 }
465
466 // when no /entry is specified, lld-link will infer it based on which functions
467 // are present in the object being linked - see lld/COFF/Driver.cpp#LinkerDriver::findDefaultEntry
439 }468 }
440 }469 }
441 }470 }
src/main.zig+24-1
...@@ -782,6 +782,7 @@ fn buildOutputType(...@@ -782,6 +782,7 @@ fn buildOutputType(
782 var headerpad_max_install_names: bool = false;782 var headerpad_max_install_names: bool = false;
783 var dead_strip_dylibs: bool = false;783 var dead_strip_dylibs: bool = false;
784 var reference_trace: ?u32 = null;784 var reference_trace: ?u32 = null;
785 var pdb_out_path: ?[]const u8 = null;
785786
786 // e.g. -m3dnow or -mno-outline-atomics. They correspond to std.Target llvm cpu feature names.787 // e.g. -m3dnow or -mno-outline-atomics. They correspond to std.Target llvm cpu feature names.
787 // This array is populated by zig cc frontend and then has to be converted to zig-style788 // This array is populated by zig cc frontend and then has to be converted to zig-style
...@@ -1541,7 +1542,10 @@ fn buildOutputType(...@@ -1541,7 +1542,10 @@ fn buildOutputType(
1541 .no_stack_protector => want_stack_protector = 0,1542 .no_stack_protector => want_stack_protector = 0,
1542 .unwind_tables => want_unwind_tables = true,1543 .unwind_tables => want_unwind_tables = true,
1543 .no_unwind_tables => want_unwind_tables = false,1544 .no_unwind_tables => want_unwind_tables = false,
1544 .nostdlib => ensure_libc_on_non_freestanding = false,1545 .nostdlib => {
1546 ensure_libc_on_non_freestanding = false;
1547 ensure_libcpp_on_non_freestanding = false;
1548 },
1545 .nostdlib_cpp => ensure_libcpp_on_non_freestanding = false,1549 .nostdlib_cpp => ensure_libcpp_on_non_freestanding = false,
1546 .shared => {1550 .shared => {
1547 link_mode = .Dynamic;1551 link_mode = .Dynamic;
...@@ -2120,6 +2124,24 @@ fn buildOutputType(...@@ -2120,6 +2124,24 @@ fn buildOutputType(
2120 next_arg,2124 next_arg,
2121 });2125 });
2122 };2126 };
2127 } else if (mem.startsWith(u8, arg, "/subsystem:")) {
2128 var split_it = mem.splitBackwards(u8, arg, ":");
2129 subsystem = try parseSubSystem(split_it.first());
2130 } else if (mem.startsWith(u8, arg, "/implib:")) {
2131 var split_it = mem.splitBackwards(u8, arg, ":");
2132 emit_implib = .{ .yes = split_it.first() };
2133 emit_implib_arg_provided = true;
2134 } else if (mem.startsWith(u8, arg, "/pdb:")) {
2135 var split_it = mem.splitBackwards(u8, arg, ":");
2136 pdb_out_path = split_it.first();
2137 } else if (mem.startsWith(u8, arg, "/version:")) {
2138 var split_it = mem.splitBackwards(u8, arg, ":");
2139 const version_arg = split_it.first();
2140 version = std.builtin.Version.parse(version_arg) catch |err| {
2141 fatal("unable to parse /version '{s}': {s}", .{ arg, @errorName(err) });
2142 };
2143
2144 have_version = true;
2123 } else {2145 } else {
2124 warn("unsupported linker arg: {s}", .{arg});2146 warn("unsupported linker arg: {s}", .{arg});
2125 }2147 }
...@@ -3069,6 +3091,7 @@ fn buildOutputType(...@@ -3069,6 +3091,7 @@ fn buildOutputType(
3069 .headerpad_max_install_names = headerpad_max_install_names,3091 .headerpad_max_install_names = headerpad_max_install_names,
3070 .dead_strip_dylibs = dead_strip_dylibs,3092 .dead_strip_dylibs = dead_strip_dylibs,
3071 .reference_trace = reference_trace,3093 .reference_trace = reference_trace,
3094 .pdb_out_path = pdb_out_path,
3072 }) catch |err| switch (err) {3095 }) catch |err| switch (err) {
3073 error.LibCUnavailable => {3096 error.LibCUnavailable => {
3074 const target = target_info.target;3097 const target = target_info.target;