diff --git a/CMakeLists.txt b/CMakeLists.txt index ece221632f34..dd3e016edb23 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -89,9 +89,9 @@ endif() set(ZIG_STATIC off CACHE BOOL "Attempt to build a static zig executable (not compatible with glibc)") set(ZIG_SHARED_LLVM off CACHE BOOL "Prefer linking against shared LLVM libraries") -set(ZIG_STATIC_LLVM off CACHE BOOL "Prefer linking against static LLVM libraries") -set(ZIG_STATIC_ZLIB off CACHE BOOL "Prefer linking against static zlib") -set(ZIG_STATIC_ZSTD off CACHE BOOL "Prefer linking against static zstd") +set(ZIG_STATIC_LLVM ${ZIG_STATIC} CACHE BOOL "Prefer linking against static LLVM libraries") +set(ZIG_STATIC_ZLIB ${ZIG_STATIC} CACHE BOOL "Prefer linking against static zlib") +set(ZIG_STATIC_ZSTD ${ZIG_STATIC} CACHE BOOL "Prefer linking against static zstd") set(ZIG_USE_CCACHE off CACHE BOOL "Use ccache") if(ZIG_USE_CCACHE) @@ -103,12 +103,6 @@ if(ZIG_USE_CCACHE) endif() endif() -if(ZIG_STATIC) - set(ZIG_STATIC_LLVM ON) - set(ZIG_STATIC_ZLIB ON) - set(ZIG_STATIC_ZSTD ON) -endif() - if (ZIG_SHARED_LLVM AND ZIG_STATIC_LLVM) message(SEND_ERROR "-DZIG_SHARED_LLVM and -DZIG_STATIC_LLVM cannot both be enabled simultaneously") endif() @@ -138,13 +132,23 @@ find_package(clang 15) find_package(lld 15) if(ZIG_STATIC_ZLIB) - list(REMOVE_ITEM LLVM_LIBRARIES "-lz") + if (MSVC) + list(REMOVE_ITEM LLVM_LIBRARIES "z.lib") + else() + list(REMOVE_ITEM LLVM_LIBRARIES "-lz") + endif() + find_library(ZLIB NAMES libz.a libzlibstatic.a z zlib libz NAMES_PER_DIR) list(APPEND LLVM_LIBRARIES "${ZLIB}") endif() if(ZIG_STATIC_ZSTD) - list(REMOVE_ITEM LLVM_LIBRARIES "-lzstd") + if (MSVC) + list(REMOVE_ITEM LLVM_LIBRARIES "zstd.lib") + else() + list(REMOVE_ITEM LLVM_LIBRARIES "-lzstd") + endif() + find_library(ZSTD NAMES libzstd.a libzstdstatic.a zstd NAMES_PER_DIR) list(APPEND LLVM_LIBRARIES "${ZSTD}") endif() @@ -168,6 +172,7 @@ foreach(CONFIG_TYPE ${CMAKE_CONFIGURATION_TYPES}) string(TOUPPER ${CONFIG_TYPE} CONFIG_TYPE) set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY_${CONFIG_TYPE} ${ZIG_CPP_LIB_DIR}) set(CMAKE_LIBRARY_OUTPUT_DIRECTORY_${CONFIG_TYPE} ${ZIG_CPP_LIB_DIR}) + set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_${CONFIG_TYPE} ${CMAKE_BINARY_DIR}) endforeach(CONFIG_TYPE CMAKE_CONFIGURATION_TYPES) include_directories(${LLVM_INCLUDE_DIRS}) @@ -722,9 +727,9 @@ set(HOST_TARGET_TRIPLE "${HOST_TARGET_ARCH}-${HOST_TARGET_OS}") if(MSVC) set(ZIG_WASM2C_COMPILE_FLAGS "") - set(ZIG1_COMPILE_FLAGS "/std:c99 /Os") - set(ZIG2_COMPILE_FLAGS "/std:c99 /O0") - set(ZIG2_LINK_FLAGS "/STACK:16777216") + set(ZIG1_COMPILE_FLAGS "/Os") + set(ZIG2_COMPILE_FLAGS "/Od") + set(ZIG2_LINK_FLAGS "/STACK:16777216 /FORCE:MULTIPLE") else() set(ZIG_WASM2C_COMPILE_FLAGS "-std=c99 -O2") set(ZIG1_COMPILE_FLAGS "-std=c99 -Os") @@ -838,7 +843,7 @@ if(ZIG_SINGLE_THREADED) else() set(ZIG_SINGLE_THREADED_ARG "") endif() -if(ZIG_STATIC) +if(ZIG_STATIC AND NOT MSVC) set(ZIG_STATIC_ARG "-Duse-zig-libcxx") else() set(ZIG_STATIC_ARG "") diff --git a/build.zig b/build.zig index c11329dbf317..999ebad08ec8 100644 --- a/build.zig +++ b/build.zig @@ -628,8 +628,16 @@ fn addStaticLlvmOptionsToExe(exe: *std.build.LibExeObjStep) !void { exe.linkSystemLibrary("z"); exe.linkSystemLibrary("zstd"); - // This means we rely on clang-or-zig-built LLVM, Clang, LLD libraries. - exe.linkSystemLibrary("c++"); + if (exe.target.getOs().tag != .windows or exe.target.getAbi() != .msvc) { + // This means we rely on clang-or-zig-built LLVM, Clang, LLD libraries. + exe.linkSystemLibrary("c++"); + } + + if (exe.target.getOs().tag == .windows) { + exe.linkSystemLibrary("version"); + exe.linkSystemLibrary("uuid"); + exe.linkSystemLibrary("ole32"); + } } fn addCxxKnownPath( @@ -673,6 +681,8 @@ fn addCMakeLibraryList(exe: *std.build.LibExeObjStep, list: []const u8) void { while (it.next()) |lib| { if (mem.startsWith(u8, lib, "-l")) { exe.linkSystemLibrary(lib["-l".len..]); + } else if (exe.target.isWindows() and mem.endsWith(u8, lib, ".lib") and !fs.path.isAbsolute(lib)) { + exe.linkSystemLibrary(lib[0 .. lib.len - ".lib".len]); } else { exe.addObjectFile(lib); } diff --git a/cmake/Findclang.cmake b/cmake/Findclang.cmake index 0949552cac21..4441664447f3 100644 --- a/cmake/Findclang.cmake +++ b/cmake/Findclang.cmake @@ -68,6 +68,10 @@ else() FIND_AND_ADD_CLANG_LIB(clangSupport) endif() +if (MSVC) + set(CLANG_LIBRARIES ${CLANG_LIBRARIES} "version.lib") +endif() + include(FindPackageHandleStandardArgs) find_package_handle_standard_args(clang DEFAULT_MSG CLANG_LIBRARIES CLANG_INCLUDE_DIRS) diff --git a/src/Compilation.zig b/src/Compilation.zig index e365d1dab227..f90b8f28d167 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -984,6 +984,7 @@ pub const InitOptions = struct { linker_dynamicbase: bool = false, linker_optimization: ?u8 = null, linker_compress_debug_sections: ?link.CompressDebugSections = null, + linker_module_definition_file: ?[]const u8 = null, major_subsystem_version: ?u32 = null, minor_subsystem_version: ?u32 = null, clang_passthrough_mode: bool = false, @@ -1041,6 +1042,11 @@ pub const InitOptions = struct { /// (Darwin) remove dylibs that are unreachable by the entry point or exported symbols dead_strip_dylibs: bool = false, libcxx_abi_version: libcxx.AbiVersion = libcxx.AbiVersion.default, + /// (Windows) PDB source path prefix to instruct the linker how to resolve relative + /// paths when consolidating CodeView streams into a single PDB file. + pdb_source_path: ?[]const u8 = null, + /// (Windows) PDB output path + pdb_out_path: ?[]const u8 = null, }; fn addPackageTableToCacheHash( @@ -1815,6 +1821,7 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation { .allow_shlib_undefined = options.linker_allow_shlib_undefined, .bind_global_refs_locally = options.linker_bind_global_refs_locally orelse false, .compress_debug_sections = options.linker_compress_debug_sections orelse .none, + .module_definition_file = options.linker_module_definition_file, .import_memory = options.linker_import_memory orelse false, .import_symbols = options.linker_import_symbols, .import_table = options.linker_import_table, @@ -1890,6 +1897,8 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation { .headerpad_max_install_names = options.headerpad_max_install_names, .dead_strip_dylibs = options.dead_strip_dylibs, .force_undefined_symbols = .{}, + .pdb_source_path = options.pdb_source_path, + .pdb_out_path = options.pdb_out_path, }); errdefer bin_file.destroy(); comp.* = .{ @@ -3297,8 +3306,8 @@ fn processOneJob(comp: *Compilation, job: Job) !void { // TODO Surface more error details. comp.lockAndSetMiscFailure( .windows_import_lib, - "unable to generate DLL import .lib file: {s}", - .{@errorName(err)}, + "unable to generate DLL import .lib file for {s}: {s}", + .{ link_lib, @errorName(err) }, ); }; }, @@ -4379,7 +4388,7 @@ pub fn addCCArgs( try argv.append("-fno-unwind-tables"); } }, - .shared_library, .ll, .bc, .unknown, .static_library, .object, .zig => {}, + .shared_library, .ll, .bc, .unknown, .static_library, .object, .def, .zig => {}, .assembly => { // The Clang assembler does not accept the list of CPU features like the // compiler frontend does. Therefore we must hard-code the -m flags for @@ -4524,6 +4533,7 @@ pub const FileExt = enum { object, static_library, zig, + def, unknown, pub fn clangSupportsDepFile(ext: FileExt) bool { @@ -4537,6 +4547,7 @@ pub const FileExt = enum { .object, .static_library, .zig, + .def, .unknown, => false, }; @@ -4629,6 +4640,8 @@ pub fn classifyFileExt(filename: []const u8) FileExt { return .object; } else if (mem.endsWith(u8, filename, ".cu")) { return .cu; + } else if (mem.endsWith(u8, filename, ".def")) { + return .def; } else { return .unknown; } diff --git a/src/link.zig b/src/link.zig index 0a526db0de43..222c0e4918c2 100644 --- a/src/link.zig +++ b/src/link.zig @@ -219,6 +219,16 @@ pub const Options = struct { /// (Darwin) remove dylibs that are unreachable by the entry point or exported symbols dead_strip_dylibs: bool = false, + /// (Windows) PDB source path prefix to instruct the linker how to resolve relative + /// paths when consolidating CodeView streams into a single PDB file. + pdb_source_path: ?[]const u8 = null, + + /// (Windows) PDB output path + pdb_out_path: ?[]const u8 = null, + + /// (Windows) .def file to specify when linking + module_definition_file: ?[]const u8 = null, + pub fn effectiveOutputMode(options: Options) std.builtin.OutputMode { return if (options.use_lld) .Obj else options.output_mode; } diff --git a/src/link/Coff/lld.zig b/src/link/Coff/lld.zig index 46b013054289..d705f62f5cc0 100644 --- a/src/link/Coff/lld.zig +++ b/src/link/Coff/lld.zig @@ -98,6 +98,8 @@ pub fn linkWithLLD(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Nod // strip does not need to go into the linker hash because it is part of the hash namespace man.hash.addOptional(self.base.options.major_subsystem_version); man.hash.addOptional(self.base.options.minor_subsystem_version); + man.hash.addOptional(self.base.options.version); + try man.addOptionalFile(self.base.options.module_definition_file); // We don't actually care whether it's a cache hit or miss; we just need the digest and the lock. _ = try man.hit(); @@ -166,12 +168,16 @@ pub fn linkWithLLD(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Nod try argv.append("-DEBUG"); const out_ext = std.fs.path.extension(full_out_path); - const out_pdb = try allocPrint(arena, "{s}.pdb", .{ + const out_pdb = self.base.options.pdb_out_path orelse try allocPrint(arena, "{s}.pdb", .{ full_out_path[0 .. full_out_path.len - out_ext.len], }); + try argv.append(try allocPrint(arena, "-PDB:{s}", .{out_pdb})); try argv.append(try allocPrint(arena, "-PDBALTPATH:{s}", .{out_pdb})); } + if (self.base.options.version) |version| { + try argv.append(try allocPrint(arena, "-VERSION:{}.{}", .{ version.major, version.minor })); + } if (self.base.options.lto) { switch (self.base.options.optimize_mode) { .Debug => {}, @@ -260,6 +266,10 @@ pub fn linkWithLLD(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Nod try argv.append(p); } + if (self.base.options.module_definition_file) |def| { + try argv.append(try allocPrint(arena, "-DEF:{s}", .{def})); + } + const resolved_subsystem: ?std.Target.SubSystem = blk: { if (self.base.options.subsystem) |explicit| break :blk explicit; switch (target.os.tag) { @@ -423,7 +433,7 @@ pub fn linkWithLLD(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Nod } } else { try argv.append("-NODEFAULTLIB"); - if (!is_lib) { + if (!is_lib and self.base.options.entry == null) { if (self.base.options.module) |module| { if (module.stage1_flags.have_winmain_crt_startup) { try argv.append("-ENTRY:WinMainCRTStartup"); @@ -486,6 +496,11 @@ pub fn linkWithLLD(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Nod continue; } } + if (target.abi == .msvc) { + argv.appendAssumeCapacity(lib_basename); + continue; + } + log.err("DLL import library for -l{s} not found", .{key}); return error.DllImportLibraryNotFound; } diff --git a/src/main.zig b/src/main.zig index 421164de1cec..5e2377637c51 100644 --- a/src/main.zig +++ b/src/main.zig @@ -742,6 +742,7 @@ fn buildOutputType( var linker_nxcompat = false; var linker_dynamicbase = false; var linker_optimization: ?u8 = null; + var linker_module_definition_file: ?[]const u8 = null; var test_evented_io = false; var test_no_exec = false; var entry: ?[]const u8 = null; @@ -781,6 +782,7 @@ fn buildOutputType( var headerpad_max_install_names: bool = false; var dead_strip_dylibs: bool = false; var reference_trace: ?u32 = null; + var pdb_out_path: ?[]const u8 = null; // e.g. -m3dnow or -mno-outline-atomics. They correspond to std.Target llvm cpu feature names. // This array is populated by zig cc frontend and then has to be converted to zig-style @@ -1404,7 +1406,7 @@ fn buildOutputType( root_src_file = arg; } }, - .unknown => { + .def, .unknown => { fatal("unrecognized file extension of parameter '{s}'", .{arg}); }, } @@ -1478,6 +1480,9 @@ fn buildOutputType( .must_link = must_link, }); }, + .def => { + linker_module_definition_file = it.only_arg; + }, .zig => { if (root_src_file) |other| { fatal("found another zig file '{s}' after root source file '{s}'", .{ it.only_arg, other }); @@ -1537,7 +1542,10 @@ fn buildOutputType( .no_stack_protector => want_stack_protector = 0, .unwind_tables => want_unwind_tables = true, .no_unwind_tables => want_unwind_tables = false, - .nostdlib => ensure_libc_on_non_freestanding = false, + .nostdlib => { + ensure_libc_on_non_freestanding = false; + ensure_libcpp_on_non_freestanding = false; + }, .nostdlib_cpp => ensure_libcpp_on_non_freestanding = false, .shared => { link_mode = .Dynamic; @@ -2116,6 +2124,24 @@ fn buildOutputType( next_arg, }); }; + } else if (mem.startsWith(u8, arg, "/subsystem:")) { + var split_it = mem.splitBackwards(u8, arg, ":"); + subsystem = try parseSubSystem(split_it.first()); + } else if (mem.startsWith(u8, arg, "/implib:")) { + var split_it = mem.splitBackwards(u8, arg, ":"); + emit_implib = .{ .yes = split_it.first() }; + emit_implib_arg_provided = true; + } else if (mem.startsWith(u8, arg, "/pdb:")) { + var split_it = mem.splitBackwards(u8, arg, ":"); + pdb_out_path = split_it.first(); + } else if (mem.startsWith(u8, arg, "/version:")) { + var split_it = mem.splitBackwards(u8, arg, ":"); + const version_arg = split_it.first(); + version = std.builtin.Version.parse(version_arg) catch |err| { + fatal("unable to parse /version '{s}': {s}", .{ arg, @errorName(err) }); + }; + + have_version = true; } else { warn("unsupported linker arg: {s}", .{arg}); } @@ -3015,6 +3041,7 @@ fn buildOutputType( .linker_dynamicbase = linker_dynamicbase, .linker_optimization = linker_optimization, .linker_compress_debug_sections = linker_compress_debug_sections, + .linker_module_definition_file = linker_module_definition_file, .major_subsystem_version = major_subsystem_version, .minor_subsystem_version = minor_subsystem_version, .link_eh_frame_hdr = link_eh_frame_hdr, @@ -3064,6 +3091,7 @@ fn buildOutputType( .headerpad_max_install_names = headerpad_max_install_names, .dead_strip_dylibs = dead_strip_dylibs, .reference_trace = reference_trace, + .pdb_out_path = pdb_out_path, }) catch |err| switch (err) { error.LibCUnavailable => { const target = target_info.target;