Skip to content

Windows: Support building stage3, and bootstrapping via MSVC #13514

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 17 commits into from
Jan 6, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 20 additions & 15 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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()
Expand Down Expand Up @@ -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()
Expand All @@ -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})
Expand Down Expand Up @@ -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")
Expand Down Expand Up @@ -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 "")
Expand Down
14 changes: 12 additions & 2 deletions build.zig
Original file line number Diff line number Diff line change
Expand Up @@ -628,8 +628,16 @@ fn addStaticLlvmOptionsToExe(exe: *std.build.LibExeObjStep) !void {
exe.linkSystemLibrary("z");
exe.linkSystemLibrary("zstd");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ls zstd not supported when targeting windows-msvc?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just checked this again, and I think removing this conditionally was actually unnecessary. The key part of the zstd change was adding the ZIG_ENABLE_ZSTD flag to the cmake script - since the initial LLVM built during bootstrapping on the host is build without ZSTD enabled (since we haven't built it for the host yet), we don't want to require it during the build:

if(ZIG_STATIC_ZSTD AND ZIG_ENABLE_ZSTD)
    list(REMOVE_ITEM LLVM_SYSTEM_LIBRARIES "-lzstd")
    find_library(ZSTD NAMES libzstd.a libzstdstatic.a zstd NAMES_PER_DIR)
    list(APPEND LLVM_LIBRARIES "${ZSTD}")
endif()

Will remove -Ddisable-zstd and the change to this function.


// 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(
Expand Down Expand Up @@ -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);
}
Expand Down
4 changes: 4 additions & 0 deletions cmake/Findclang.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down
19 changes: 16 additions & 3 deletions src/Compilation.zig
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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(
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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.* = .{
Expand Down Expand Up @@ -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) },
);
};
},
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -4524,6 +4533,7 @@ pub const FileExt = enum {
object,
static_library,
zig,
def,
unknown,

pub fn clangSupportsDepFile(ext: FileExt) bool {
Expand All @@ -4537,6 +4547,7 @@ pub const FileExt = enum {
.object,
.static_library,
.zig,
.def,
.unknown,
=> false,
};
Expand Down Expand Up @@ -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;
}
Expand Down
10 changes: 10 additions & 0 deletions src/link.zig
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down
19 changes: 17 additions & 2 deletions src/link/Coff/lld.zig
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down Expand Up @@ -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 => {},
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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");
Expand Down Expand Up @@ -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;
}
Expand Down
32 changes: 30 additions & 2 deletions src/main.zig
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -1404,7 +1406,7 @@ fn buildOutputType(
root_src_file = arg;
}
},
.unknown => {
.def, .unknown => {
fatal("unrecognized file extension of parameter '{s}'", .{arg});
},
}
Expand Down Expand Up @@ -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 });
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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});
}
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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;
Expand Down