Skip to content

Commit c28c38d

Browse files
authored
Merge pull request #13514 from kcbanner/windows_build_fixes
Windows: Support building stage3, and bootstrapping via MSVC
2 parents 301a898 + 25e6187 commit c28c38d

7 files changed

Lines changed: 109 additions & 24 deletions

File tree

CMakeLists.txt

Lines changed: 20 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -89,9 +89,9 @@ endif()
8989

9090
set(ZIG_STATIC off CACHE BOOL "Attempt to build a static zig executable (not compatible with glibc)")
9191
set(ZIG_SHARED_LLVM off CACHE BOOL "Prefer linking against shared LLVM libraries")
92-
set(ZIG_STATIC_LLVM off CACHE BOOL "Prefer linking against static LLVM libraries")
93-
set(ZIG_STATIC_ZLIB off CACHE BOOL "Prefer linking against static zlib")
94-
set(ZIG_STATIC_ZSTD off CACHE BOOL "Prefer linking against static zstd")
92+
set(ZIG_STATIC_LLVM ${ZIG_STATIC} CACHE BOOL "Prefer linking against static LLVM libraries")
93+
set(ZIG_STATIC_ZLIB ${ZIG_STATIC} CACHE BOOL "Prefer linking against static zlib")
94+
set(ZIG_STATIC_ZSTD ${ZIG_STATIC} CACHE BOOL "Prefer linking against static zstd")
9595
set(ZIG_USE_CCACHE off CACHE BOOL "Use ccache")
9696

9797
if(ZIG_USE_CCACHE)
@@ -103,12 +103,6 @@ if(ZIG_USE_CCACHE)
103103
endif()
104104
endif()
105105

106-
if(ZIG_STATIC)
107-
set(ZIG_STATIC_LLVM ON)
108-
set(ZIG_STATIC_ZLIB ON)
109-
set(ZIG_STATIC_ZSTD ON)
110-
endif()
111-
112106
if (ZIG_SHARED_LLVM AND ZIG_STATIC_LLVM)
113107
message(SEND_ERROR "-DZIG_SHARED_LLVM and -DZIG_STATIC_LLVM cannot both be enabled simultaneously")
114108
endif()
@@ -138,13 +132,23 @@ find_package(clang 15)
138132
find_package(lld 15)
139133

140134
if(ZIG_STATIC_ZLIB)
141-
list(REMOVE_ITEM LLVM_LIBRARIES "-lz")
135+
if (MSVC)
136+
list(REMOVE_ITEM LLVM_LIBRARIES "z.lib")
137+
else()
138+
list(REMOVE_ITEM LLVM_LIBRARIES "-lz")
139+
endif()
140+
142141
find_library(ZLIB NAMES libz.a libzlibstatic.a z zlib libz NAMES_PER_DIR)
143142
list(APPEND LLVM_LIBRARIES "${ZLIB}")
144143
endif()
145144

146145
if(ZIG_STATIC_ZSTD)
147-
list(REMOVE_ITEM LLVM_LIBRARIES "-lzstd")
146+
if (MSVC)
147+
list(REMOVE_ITEM LLVM_LIBRARIES "zstd.lib")
148+
else()
149+
list(REMOVE_ITEM LLVM_LIBRARIES "-lzstd")
150+
endif()
151+
148152
find_library(ZSTD NAMES libzstd.a libzstdstatic.a zstd NAMES_PER_DIR)
149153
list(APPEND LLVM_LIBRARIES "${ZSTD}")
150154
endif()
@@ -168,6 +172,7 @@ foreach(CONFIG_TYPE ${CMAKE_CONFIGURATION_TYPES})
168172
string(TOUPPER ${CONFIG_TYPE} CONFIG_TYPE)
169173
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY_${CONFIG_TYPE} ${ZIG_CPP_LIB_DIR})
170174
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY_${CONFIG_TYPE} ${ZIG_CPP_LIB_DIR})
175+
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_${CONFIG_TYPE} ${CMAKE_BINARY_DIR})
171176
endforeach(CONFIG_TYPE CMAKE_CONFIGURATION_TYPES)
172177

173178
include_directories(${LLVM_INCLUDE_DIRS})
@@ -722,9 +727,9 @@ set(HOST_TARGET_TRIPLE "${HOST_TARGET_ARCH}-${HOST_TARGET_OS}")
722727

723728
if(MSVC)
724729
set(ZIG_WASM2C_COMPILE_FLAGS "")
725-
set(ZIG1_COMPILE_FLAGS "/std:c99 /Os")
726-
set(ZIG2_COMPILE_FLAGS "/std:c99 /O0")
727-
set(ZIG2_LINK_FLAGS "/STACK:16777216")
730+
set(ZIG1_COMPILE_FLAGS "/Os")
731+
set(ZIG2_COMPILE_FLAGS "/Od")
732+
set(ZIG2_LINK_FLAGS "/STACK:16777216 /FORCE:MULTIPLE")
728733
else()
729734
set(ZIG_WASM2C_COMPILE_FLAGS "-std=c99 -O2")
730735
set(ZIG1_COMPILE_FLAGS "-std=c99 -Os")
@@ -838,7 +843,7 @@ if(ZIG_SINGLE_THREADED)
838843
else()
839844
set(ZIG_SINGLE_THREADED_ARG "")
840845
endif()
841-
if(ZIG_STATIC)
846+
if(ZIG_STATIC AND NOT MSVC)
842847
set(ZIG_STATIC_ARG "-Duse-zig-libcxx")
843848
else()
844849
set(ZIG_STATIC_ARG "")

build.zig

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -628,8 +628,16 @@ fn addStaticLlvmOptionsToExe(exe: *std.build.LibExeObjStep) !void {
628628
exe.linkSystemLibrary("z");
629629
exe.linkSystemLibrary("zstd");
630630

631-
// This means we rely on clang-or-zig-built LLVM, Clang, LLD libraries.
632-
exe.linkSystemLibrary("c++");
631+
if (exe.target.getOs().tag != .windows or exe.target.getAbi() != .msvc) {
632+
// This means we rely on clang-or-zig-built LLVM, Clang, LLD libraries.
633+
exe.linkSystemLibrary("c++");
634+
}
635+
636+
if (exe.target.getOs().tag == .windows) {
637+
exe.linkSystemLibrary("version");
638+
exe.linkSystemLibrary("uuid");
639+
exe.linkSystemLibrary("ole32");
640+
}
633641
}
634642

635643
fn addCxxKnownPath(
@@ -673,6 +681,8 @@ fn addCMakeLibraryList(exe: *std.build.LibExeObjStep, list: []const u8) void {
673681
while (it.next()) |lib| {
674682
if (mem.startsWith(u8, lib, "-l")) {
675683
exe.linkSystemLibrary(lib["-l".len..]);
684+
} else if (exe.target.isWindows() and mem.endsWith(u8, lib, ".lib") and !fs.path.isAbsolute(lib)) {
685+
exe.linkSystemLibrary(lib[0 .. lib.len - ".lib".len]);
676686
} else {
677687
exe.addObjectFile(lib);
678688
}

cmake/Findclang.cmake

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,10 @@ else()
6868
FIND_AND_ADD_CLANG_LIB(clangSupport)
6969
endif()
7070

71+
if (MSVC)
72+
set(CLANG_LIBRARIES ${CLANG_LIBRARIES} "version.lib")
73+
endif()
74+
7175
include(FindPackageHandleStandardArgs)
7276
find_package_handle_standard_args(clang DEFAULT_MSG CLANG_LIBRARIES CLANG_INCLUDE_DIRS)
7377

src/Compilation.zig

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -984,6 +984,7 @@ pub const InitOptions = struct {
984984
linker_dynamicbase: bool = false,
985985
linker_optimization: ?u8 = null,
986986
linker_compress_debug_sections: ?link.CompressDebugSections = null,
987+
linker_module_definition_file: ?[]const u8 = null,
987988
major_subsystem_version: ?u32 = null,
988989
minor_subsystem_version: ?u32 = null,
989990
clang_passthrough_mode: bool = false,
@@ -1041,6 +1042,11 @@ pub const InitOptions = struct {
10411042
/// (Darwin) remove dylibs that are unreachable by the entry point or exported symbols
10421043
dead_strip_dylibs: bool = false,
10431044
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,
10441050
};
10451051

10461052
fn addPackageTableToCacheHash(
@@ -1815,6 +1821,7 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
18151821
.allow_shlib_undefined = options.linker_allow_shlib_undefined,
18161822
.bind_global_refs_locally = options.linker_bind_global_refs_locally orelse false,
18171823
.compress_debug_sections = options.linker_compress_debug_sections orelse .none,
1824+
.module_definition_file = options.linker_module_definition_file,
18181825
.import_memory = options.linker_import_memory orelse false,
18191826
.import_symbols = options.linker_import_symbols,
18201827
.import_table = options.linker_import_table,
@@ -1890,6 +1897,8 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
18901897
.headerpad_max_install_names = options.headerpad_max_install_names,
18911898
.dead_strip_dylibs = options.dead_strip_dylibs,
18921899
.force_undefined_symbols = .{},
1900+
.pdb_source_path = options.pdb_source_path,
1901+
.pdb_out_path = options.pdb_out_path,
18931902
});
18941903
errdefer bin_file.destroy();
18951904
comp.* = .{
@@ -3297,8 +3306,8 @@ fn processOneJob(comp: *Compilation, job: Job) !void {
32973306
// TODO Surface more error details.
32983307
comp.lockAndSetMiscFailure(
32993308
.windows_import_lib,
3300-
"unable to generate DLL import .lib file: {s}",
3301-
.{@errorName(err)},
3309+
"unable to generate DLL import .lib file for {s}: {s}",
3310+
.{ link_lib, @errorName(err) },
33023311
);
33033312
};
33043313
},
@@ -4379,7 +4388,7 @@ pub fn addCCArgs(
43794388
try argv.append("-fno-unwind-tables");
43804389
}
43814390
},
4382-
.shared_library, .ll, .bc, .unknown, .static_library, .object, .zig => {},
4391+
.shared_library, .ll, .bc, .unknown, .static_library, .object, .def, .zig => {},
43834392
.assembly => {
43844393
// The Clang assembler does not accept the list of CPU features like the
43854394
// compiler frontend does. Therefore we must hard-code the -m flags for
@@ -4524,6 +4533,7 @@ pub const FileExt = enum {
45244533
object,
45254534
static_library,
45264535
zig,
4536+
def,
45274537
unknown,
45284538

45294539
pub fn clangSupportsDepFile(ext: FileExt) bool {
@@ -4537,6 +4547,7 @@ pub const FileExt = enum {
45374547
.object,
45384548
.static_library,
45394549
.zig,
4550+
.def,
45404551
.unknown,
45414552
=> false,
45424553
};
@@ -4629,6 +4640,8 @@ pub fn classifyFileExt(filename: []const u8) FileExt {
46294640
return .object;
46304641
} else if (mem.endsWith(u8, filename, ".cu")) {
46314642
return .cu;
4643+
} else if (mem.endsWith(u8, filename, ".def")) {
4644+
return .def;
46324645
} else {
46334646
return .unknown;
46344647
}

src/link.zig

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,16 @@ pub const Options = struct {
219219
/// (Darwin) remove dylibs that are unreachable by the entry point or exported symbols
220220
dead_strip_dylibs: bool = false,
221221

222+
/// (Windows) PDB source path prefix to instruct the linker how to resolve relative
223+
/// paths when consolidating CodeView streams into a single PDB file.
224+
pdb_source_path: ?[]const u8 = null,
225+
226+
/// (Windows) PDB output path
227+
pdb_out_path: ?[]const u8 = null,
228+
229+
/// (Windows) .def file to specify when linking
230+
module_definition_file: ?[]const u8 = null,
231+
222232
pub fn effectiveOutputMode(options: Options) std.builtin.OutputMode {
223233
return if (options.use_lld) .Obj else options.output_mode;
224234
}

src/link/Coff/lld.zig

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,8 @@ pub fn linkWithLLD(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Nod
9898
// strip does not need to go into the linker hash because it is part of the hash namespace
9999
man.hash.addOptional(self.base.options.major_subsystem_version);
100100
man.hash.addOptional(self.base.options.minor_subsystem_version);
101+
man.hash.addOptional(self.base.options.version);
102+
try man.addOptionalFile(self.base.options.module_definition_file);
101103

102104
// We don't actually care whether it's a cache hit or miss; we just need the digest and the lock.
103105
_ = try man.hit();
@@ -166,12 +168,16 @@ pub fn linkWithLLD(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Nod
166168
try argv.append("-DEBUG");
167169

168170
const out_ext = std.fs.path.extension(full_out_path);
169-
const out_pdb = try allocPrint(arena, "{s}.pdb", .{
171+
const out_pdb = self.base.options.pdb_out_path orelse try allocPrint(arena, "{s}.pdb", .{
170172
full_out_path[0 .. full_out_path.len - out_ext.len],
171173
});
174+
172175
try argv.append(try allocPrint(arena, "-PDB:{s}", .{out_pdb}));
173176
try argv.append(try allocPrint(arena, "-PDBALTPATH:{s}", .{out_pdb}));
174177
}
178+
if (self.base.options.version) |version| {
179+
try argv.append(try allocPrint(arena, "-VERSION:{}.{}", .{ version.major, version.minor }));
180+
}
175181
if (self.base.options.lto) {
176182
switch (self.base.options.optimize_mode) {
177183
.Debug => {},
@@ -260,6 +266,10 @@ pub fn linkWithLLD(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Nod
260266
try argv.append(p);
261267
}
262268

269+
if (self.base.options.module_definition_file) |def| {
270+
try argv.append(try allocPrint(arena, "-DEF:{s}", .{def}));
271+
}
272+
263273
const resolved_subsystem: ?std.Target.SubSystem = blk: {
264274
if (self.base.options.subsystem) |explicit| break :blk explicit;
265275
switch (target.os.tag) {
@@ -423,7 +433,7 @@ pub fn linkWithLLD(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Nod
423433
}
424434
} else {
425435
try argv.append("-NODEFAULTLIB");
426-
if (!is_lib) {
436+
if (!is_lib and self.base.options.entry == null) {
427437
if (self.base.options.module) |module| {
428438
if (module.stage1_flags.have_winmain_crt_startup) {
429439
try argv.append("-ENTRY:WinMainCRTStartup");
@@ -486,6 +496,11 @@ pub fn linkWithLLD(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Nod
486496
continue;
487497
}
488498
}
499+
if (target.abi == .msvc) {
500+
argv.appendAssumeCapacity(lib_basename);
501+
continue;
502+
}
503+
489504
log.err("DLL import library for -l{s} not found", .{key});
490505
return error.DllImportLibraryNotFound;
491506
}

src/main.zig

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -748,6 +748,7 @@ fn buildOutputType(
748748
var linker_nxcompat = false;
749749
var linker_dynamicbase = false;
750750
var linker_optimization: ?u8 = null;
751+
var linker_module_definition_file: ?[]const u8 = null;
751752
var test_evented_io = false;
752753
var test_no_exec = false;
753754
var entry: ?[]const u8 = null;
@@ -787,6 +788,7 @@ fn buildOutputType(
787788
var headerpad_max_install_names: bool = false;
788789
var dead_strip_dylibs: bool = false;
789790
var reference_trace: ?u32 = null;
791+
var pdb_out_path: ?[]const u8 = null;
790792

791793
// e.g. -m3dnow or -mno-outline-atomics. They correspond to std.Target llvm cpu feature names.
792794
// This array is populated by zig cc frontend and then has to be converted to zig-style
@@ -1410,7 +1412,7 @@ fn buildOutputType(
14101412
root_src_file = arg;
14111413
}
14121414
},
1413-
.unknown => {
1415+
.def, .unknown => {
14141416
fatal("unrecognized file extension of parameter '{s}'", .{arg});
14151417
},
14161418
}
@@ -1484,6 +1486,9 @@ fn buildOutputType(
14841486
.must_link = must_link,
14851487
});
14861488
},
1489+
.def => {
1490+
linker_module_definition_file = it.only_arg;
1491+
},
14871492
.zig => {
14881493
if (root_src_file) |other| {
14891494
fatal("found another zig file '{s}' after root source file '{s}'", .{ it.only_arg, other });
@@ -1543,7 +1548,10 @@ fn buildOutputType(
15431548
.no_stack_protector => want_stack_protector = 0,
15441549
.unwind_tables => want_unwind_tables = true,
15451550
.no_unwind_tables => want_unwind_tables = false,
1546-
.nostdlib => ensure_libc_on_non_freestanding = false,
1551+
.nostdlib => {
1552+
ensure_libc_on_non_freestanding = false;
1553+
ensure_libcpp_on_non_freestanding = false;
1554+
},
15471555
.nostdlib_cpp => ensure_libcpp_on_non_freestanding = false,
15481556
.shared => {
15491557
link_mode = .Dynamic;
@@ -2122,6 +2130,24 @@ fn buildOutputType(
21222130
next_arg,
21232131
});
21242132
};
2133+
} else if (mem.startsWith(u8, arg, "/subsystem:")) {
2134+
var split_it = mem.splitBackwards(u8, arg, ":");
2135+
subsystem = try parseSubSystem(split_it.first());
2136+
} else if (mem.startsWith(u8, arg, "/implib:")) {
2137+
var split_it = mem.splitBackwards(u8, arg, ":");
2138+
emit_implib = .{ .yes = split_it.first() };
2139+
emit_implib_arg_provided = true;
2140+
} else if (mem.startsWith(u8, arg, "/pdb:")) {
2141+
var split_it = mem.splitBackwards(u8, arg, ":");
2142+
pdb_out_path = split_it.first();
2143+
} else if (mem.startsWith(u8, arg, "/version:")) {
2144+
var split_it = mem.splitBackwards(u8, arg, ":");
2145+
const version_arg = split_it.first();
2146+
version = std.builtin.Version.parse(version_arg) catch |err| {
2147+
fatal("unable to parse /version '{s}': {s}", .{ arg, @errorName(err) });
2148+
};
2149+
2150+
have_version = true;
21252151
} else {
21262152
warn("unsupported linker arg: {s}", .{arg});
21272153
}
@@ -3021,6 +3047,7 @@ fn buildOutputType(
30213047
.linker_dynamicbase = linker_dynamicbase,
30223048
.linker_optimization = linker_optimization,
30233049
.linker_compress_debug_sections = linker_compress_debug_sections,
3050+
.linker_module_definition_file = linker_module_definition_file,
30243051
.major_subsystem_version = major_subsystem_version,
30253052
.minor_subsystem_version = minor_subsystem_version,
30263053
.link_eh_frame_hdr = link_eh_frame_hdr,
@@ -3070,6 +3097,7 @@ fn buildOutputType(
30703097
.headerpad_max_install_names = headerpad_max_install_names,
30713098
.dead_strip_dylibs = dead_strip_dylibs,
30723099
.reference_trace = reference_trace,
3100+
.pdb_out_path = pdb_out_path,
30733101
}) catch |err| switch (err) {
30743102
error.LibCUnavailable => {
30753103
const target = target_info.target;

0 commit comments

Comments
 (0)