diff --git a/build.zig b/build.zig index e50421365c46..81a38cf9c905 100644 --- a/build.zig +++ b/build.zig @@ -450,6 +450,7 @@ pub fn build(b: *std.Build) !void { .desc = "Run the behavior tests", .optimize_modes = optimization_modes, .include_paths = &.{}, + .windows_libs = &.{}, .skip_single_threaded = skip_single_threaded, .skip_non_native = skip_non_native, .skip_freebsd = skip_freebsd, @@ -472,6 +473,7 @@ pub fn build(b: *std.Build) !void { .desc = "Run the @cImport tests", .optimize_modes = optimization_modes, .include_paths = &.{"test/c_import"}, + .windows_libs = &.{}, .skip_single_threaded = true, .skip_non_native = skip_non_native, .skip_freebsd = skip_freebsd, @@ -492,6 +494,7 @@ pub fn build(b: *std.Build) !void { .desc = "Run the compiler_rt tests", .optimize_modes = optimization_modes, .include_paths = &.{}, + .windows_libs = &.{}, .skip_single_threaded = true, .skip_non_native = skip_non_native, .skip_freebsd = skip_freebsd, @@ -513,6 +516,7 @@ pub fn build(b: *std.Build) !void { .desc = "Run the zigc tests", .optimize_modes = optimization_modes, .include_paths = &.{}, + .windows_libs = &.{}, .skip_single_threaded = true, .skip_non_native = skip_non_native, .skip_freebsd = skip_freebsd, @@ -534,6 +538,12 @@ pub fn build(b: *std.Build) !void { .desc = "Run the standard library tests", .optimize_modes = optimization_modes, .include_paths = &.{}, + .windows_libs = &.{ + "advapi32", + "crypt32", + "iphlpapi", + "ws2_32", + }, .skip_single_threaded = skip_single_threaded, .skip_non_native = skip_non_native, .skip_freebsd = skip_freebsd, @@ -731,6 +741,12 @@ fn addCompilerMod(b: *std.Build, options: AddCompilerModOptions) *std.Build.Modu compiler_mod.addImport("aro", aro_mod); compiler_mod.addImport("aro_translate_c", aro_translate_c_mod); + if (options.target.result.os.tag == .windows) { + compiler_mod.linkSystemLibrary("advapi32", .{}); + compiler_mod.linkSystemLibrary("crypt32", .{}); + compiler_mod.linkSystemLibrary("ws2_32", .{}); + } + return compiler_mod; } @@ -1428,6 +1444,10 @@ fn generateLangRef(b: *std.Build) std.Build.LazyPath { }), }); + if (b.graph.host.result.os.tag == .windows) { + doctest_exe.root_module.linkSystemLibrary("advapi32", .{}); + } + var dir = b.build_root.handle.openDir("doc/langref", .{ .iterate = true }) catch |err| { std.debug.panic("unable to open '{}doc/langref' directory: {s}", .{ b.build_root, @errorName(err), diff --git a/src/Compilation.zig b/src/Compilation.zig index fb9ee617f53b..c890fd1925aa 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -2070,12 +2070,8 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil .emit_docs = try options.emit_docs.resolve(arena, &options, .docs), }; - errdefer { - for (comp.windows_libs.keys()) |windows_lib| gpa.free(windows_lib); - comp.windows_libs.deinit(gpa); - } - try comp.windows_libs.ensureUnusedCapacity(gpa, options.windows_lib_names.len); - for (options.windows_lib_names) |windows_lib| comp.windows_libs.putAssumeCapacity(try gpa.dupe(u8, windows_lib), {}); + comp.windows_libs = try std.StringArrayHashMapUnmanaged(void).init(gpa, options.windows_lib_names, &.{}); + errdefer comp.windows_libs.deinit(gpa); // Prevent some footguns by making the "any" fields of config reflect // the default Module settings. @@ -2306,6 +2302,13 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil if (comp.emit_bin != null and target.ofmt != .c) { if (!comp.skip_linker_dependencies) { + // These DLLs are always loaded into every Windows process. + if (target.os.tag == .windows and is_exe_or_dyn_lib) { + try comp.windows_libs.ensureUnusedCapacity(gpa, 2); + comp.windows_libs.putAssumeCapacity("kernel32", {}); + comp.windows_libs.putAssumeCapacity("ntdll", {}); + } + // If we need to build libc for the target, add work items for it. // We go through the work queue so that building can be done in parallel. // If linking against host libc installation, instead queue up jobs @@ -2399,7 +2402,7 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil // When linking mingw-w64 there are some import libs we always need. try comp.windows_libs.ensureUnusedCapacity(gpa, mingw.always_link_libs.len); - for (mingw.always_link_libs) |name| comp.windows_libs.putAssumeCapacity(try gpa.dupe(u8, name), {}); + for (mingw.always_link_libs) |name| comp.windows_libs.putAssumeCapacity(name, {}); } else { return error.LibCUnavailable; } @@ -2497,7 +2500,6 @@ pub fn destroy(comp: *Compilation) void { comp.c_object_work_queue.deinit(); comp.win32_resource_work_queue.deinit(); - for (comp.windows_libs.keys()) |windows_lib| gpa.free(windows_lib); comp.windows_libs.deinit(gpa); { @@ -7599,27 +7601,6 @@ fn getCrtPathsInner( }; } -pub fn addLinkLib(comp: *Compilation, lib_name: []const u8) !void { - // Avoid deadlocking on building import libs such as kernel32.lib - // This can happen when the user uses `build-exe foo.obj -lkernel32` and - // then when we create a sub-Compilation for zig libc, it also tries to - // build kernel32.lib. - if (comp.skip_linker_dependencies) return; - const target = &comp.root_mod.resolved_target.result; - if (target.os.tag != .windows or target.ofmt == .c) return; - - // This happens when an `extern "foo"` function is referenced. - // If we haven't seen this library yet and we're targeting Windows, we need - // to queue up a work item to produce the DLL import library for this. - const gop = try comp.windows_libs.getOrPut(comp.gpa, lib_name); - if (gop.found_existing) return; - { - errdefer _ = comp.windows_libs.pop(); - gop.key_ptr.* = try comp.gpa.dupe(u8, lib_name); - } - try comp.queueJob(.{ .windows_import_lib = gop.index }); -} - /// This decides the optimization mode for all zig-provided libraries, including /// compiler-rt, libcxx, libc, libunwind, etc. pub fn compilerRtOptMode(comp: Compilation) std.builtin.OptimizeMode { diff --git a/src/Sema.zig b/src/Sema.zig index 5b2e846ff020..c9ca2941da40 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -9418,14 +9418,6 @@ fn resolveGenericBody( return sema.resolveConstDefinedValue(block, src, result, reason); } -/// Given a library name, examines if the library name should end up in -/// `link.File.Options.windows_libs` table (for example, libc is always -/// specified via dedicated flag `link_libc` instead), -/// and puts it there if it doesn't exist. -/// It also dupes the library name which can then be saved as part of the -/// respective `Decl` (either `ExternFn` or `Var`). -/// The liveness of the duped library name is tied to liveness of `Zcu`. -/// To deallocate, call `deinit` on the respective `Decl` (`ExternFn` or `Var`). pub fn handleExternLibName( sema: *Sema, block: *Block, @@ -9475,11 +9467,6 @@ pub fn handleExternLibName( .{ lib_name, lib_name }, ); } - comp.addLinkLib(lib_name) catch |err| { - return sema.fail(block, src_loc, "unable to add link lib '{s}': {s}", .{ - lib_name, @errorName(err), - }); - }; } } diff --git a/src/libs/mingw.zig b/src/libs/mingw.zig index a8c7ab3c3b4c..c5713b2ff7ee 100644 --- a/src/libs/mingw.zig +++ b/src/libs/mingw.zig @@ -1012,6 +1012,7 @@ const mingw32_winpthreads_src = [_][]const u8{ "winpthreads" ++ path.sep_str ++ "thread.c", }; +// Note: kernel32 and ntdll are always linked even without targeting MinGW-w64. pub const always_link_libs = [_][]const u8{ "api-ms-win-crt-conio-l1-1-0", "api-ms-win-crt-convert-l1-1-0", @@ -1029,8 +1030,6 @@ pub const always_link_libs = [_][]const u8{ "api-ms-win-crt-time-l1-1-0", "api-ms-win-crt-utility-l1-1-0", "advapi32", - "kernel32", - "ntdll", "shell32", "user32", }; diff --git a/src/main.zig b/src/main.zig index e974621e5e92..045612240510 100644 --- a/src/main.zig +++ b/src/main.zig @@ -306,6 +306,7 @@ fn mainArgs(gpa: Allocator, arena: Allocator, args: []const []const u8) !void { return jitCmd(gpa, arena, cmd_args, .{ .cmd_name = "resinator", .root_src_path = "resinator/main.zig", + .windows_libs = &.{"advapi32"}, .depend_on_aro = true, .prepend_zig_lib_dir_path = true, .server = use_server, @@ -3631,7 +3632,6 @@ fn buildOutputType( } else if (target.os.tag == .windows) { try test_exec_args.appendSlice(arena, &.{ "--subsystem", "console", - "-lkernel32", "-lntdll", }); } @@ -3845,7 +3845,8 @@ fn createModule( .only_compiler_rt => continue, } - if (target.isMinGW()) { + // We currently prefer import libraries provided by MinGW-w64 even for MSVC. + if (target.os.tag == .windows) { const exists = mingw.libExists(arena, target, create_module.dirs.zig_lib, lib_name) catch |err| { fatal("failed to check zig installation for DLL import libs: {s}", .{ @errorName(err), @@ -5221,6 +5222,12 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !void { try root_mod.deps.put(arena, "@build", build_mod); + var windows_libs: std.StringArrayHashMapUnmanaged(void) = .empty; + + if (resolved_target.result.os.tag == .windows) { + try windows_libs.put(arena, "advapi32", {}); + } + const comp = Compilation.create(gpa, arena, .{ .dirs = dirs, .root_name = "build", @@ -5242,6 +5249,7 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !void { .cache_mode = .whole, .reference_trace = reference_trace, .debug_compile_errors = debug_compile_errors, + .windows_lib_names = windows_libs.keys(), }) catch |err| { fatal("unable to create compilation: {s}", .{@errorName(err)}); }; @@ -5345,6 +5353,7 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !void { const JitCmdOptions = struct { cmd_name: []const u8, root_src_path: []const u8, + windows_libs: []const []const u8 = &.{}, prepend_zig_lib_dir_path: bool = false, prepend_global_cache_path: bool = false, prepend_zig_exe_path: bool = false, @@ -5461,6 +5470,13 @@ fn jitCmd( try root_mod.deps.put(arena, "aro", aro_mod); } + var windows_libs: std.StringArrayHashMapUnmanaged(void) = .empty; + + if (resolved_target.result.os.tag == .windows) { + try windows_libs.ensureUnusedCapacity(arena, options.windows_libs.len); + for (options.windows_libs) |lib| windows_libs.putAssumeCapacity(lib, {}); + } + const comp = Compilation.create(gpa, arena, .{ .dirs = dirs, .root_name = options.cmd_name, @@ -5471,6 +5487,7 @@ fn jitCmd( .self_exe_path = self_exe_path, .thread_pool = &thread_pool, .cache_mode = .whole, + .windows_lib_names = windows_libs.keys(), }) catch |err| { fatal("unable to create compilation: {s}", .{@errorName(err)}); }; diff --git a/test/standalone/simple/build.zig b/test/standalone/simple/build.zig index ba4464cefcea..e9270c3588a9 100644 --- a/test/standalone/simple/build.zig +++ b/test/standalone/simple/build.zig @@ -50,6 +50,10 @@ pub fn build(b: *std.Build) void { }); if (case.link_libc) exe.root_module.link_libc = true; + if (resolved_target.result.os.tag == .windows) { + exe.root_module.linkSystemLibrary("advapi32", .{}); + } + _ = exe.getEmittedBin(); step.dependOn(&exe.step); @@ -66,6 +70,10 @@ pub fn build(b: *std.Build) void { }); if (case.link_libc) exe.root_module.link_libc = true; + if (resolved_target.result.os.tag == .windows) { + exe.root_module.linkSystemLibrary("advapi32", .{}); + } + const run = b.addRunArtifact(exe); step.dependOn(&run.step); } diff --git a/test/standalone/windows_argv/build.zig b/test/standalone/windows_argv/build.zig index df988d2371e0..9ace58088af0 100644 --- a/test/standalone/windows_argv/build.zig +++ b/test/standalone/windows_argv/build.zig @@ -47,6 +47,8 @@ pub fn build(b: *std.Build) !void { }), }); + fuzz.root_module.linkSystemLibrary("advapi32", .{}); + const fuzz_max_iterations = b.option(u64, "iterations", "The max fuzz iterations (default: 100)") orelse 100; const fuzz_iterations_arg = std.fmt.allocPrint(b.allocator, "{}", .{fuzz_max_iterations}) catch @panic("oom"); diff --git a/test/standalone/windows_bat_args/build.zig b/test/standalone/windows_bat_args/build.zig index b91b4fb3bbfd..e30f5b36c166 100644 --- a/test/standalone/windows_bat_args/build.zig +++ b/test/standalone/windows_bat_args/build.zig @@ -28,6 +28,8 @@ pub fn build(b: *std.Build) !void { }), }); + test_exe.root_module.linkSystemLibrary("advapi32", .{}); + const run = b.addRunArtifact(test_exe); run.addArtifactArg(echo_args); run.expectExitCode(0); @@ -44,6 +46,8 @@ pub fn build(b: *std.Build) !void { }), }); + fuzz.root_module.linkSystemLibrary("advapi32", .{}); + const fuzz_max_iterations = b.option(u64, "iterations", "The max fuzz iterations (default: 100)") orelse 100; const fuzz_iterations_arg = std.fmt.allocPrint(b.allocator, "{}", .{fuzz_max_iterations}) catch @panic("oom"); diff --git a/test/standalone/windows_spawn/build.zig b/test/standalone/windows_spawn/build.zig index 2b967b16d5b3..628b1a6900a4 100644 --- a/test/standalone/windows_spawn/build.zig +++ b/test/standalone/windows_spawn/build.zig @@ -28,6 +28,8 @@ pub fn build(b: *std.Build) void { }), }); + main.root_module.linkSystemLibrary("advapi32", .{}); + const run = b.addRunArtifact(main); run.addArtifactArg(hello); run.expectExitCode(0); diff --git a/test/tests.zig b/test/tests.zig index efc824c4eaa7..9068d6501221 100644 --- a/test/tests.zig +++ b/test/tests.zig @@ -2308,6 +2308,7 @@ const ModuleTestOptions = struct { desc: []const u8, optimize_modes: []const OptimizeMode, include_paths: []const []const u8, + windows_libs: []const []const u8, skip_single_threaded: bool, skip_non_native: bool, skip_freebsd: bool, @@ -2437,6 +2438,10 @@ pub fn addModuleTests(b: *std.Build, options: ModuleTestOptions) *Step { for (options.include_paths) |include_path| these_tests.addIncludePath(b.path(include_path)); + if (target.os.tag == .windows) { + for (options.windows_libs) |lib| these_tests.linkSystemLibrary(lib); + } + const qualified_name = b.fmt("{s}-{s}-{s}-{s}{s}{s}{s}{s}{s}{s}", .{ options.name, triple_txt, @@ -2732,6 +2737,10 @@ pub fn addIncrementalTests(b: *std.Build, test_step: *Step) !void { }), }); + if (b.graph.host.result.os.tag == .windows) { + incr_check.root_module.linkSystemLibrary("advapi32", .{}); + } + var dir = try b.build_root.handle.openDir("test/incremental", .{ .iterate = true }); defer dir.close();