Merge pull request #25158 from castholm/subsystem

Misc. Windows subsystem refactorings
This commit is contained in:
Ryan Liptak 2025-11-07 18:56:52 -08:00 committed by GitHub
commit 74d2536715
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
11 changed files with 75 additions and 147 deletions

View File

@ -171,7 +171,7 @@ lto: ?std.zig.LtoMode = null,
dll_export_fns: ?bool = null, dll_export_fns: ?bool = null,
subsystem: ?std.Target.SubSystem = null, subsystem: ?std.zig.Subsystem = null,
/// (Windows) When targeting the MinGW ABI, use the unicode entry point (wmain/wWinMain) /// (Windows) When targeting the MinGW ABI, use the unicode entry point (wmain/wWinMain)
mingw_unicode_entry_point: bool = false, mingw_unicode_entry_point: bool = false,
@ -1764,16 +1764,7 @@ fn getZigArgs(compile: *Compile, fuzz: bool) ![][]const u8 {
if (compile.subsystem) |subsystem| { if (compile.subsystem) |subsystem| {
try zig_args.append("--subsystem"); try zig_args.append("--subsystem");
try zig_args.append(switch (subsystem) { try zig_args.append(@tagName(subsystem));
.Console => "console",
.Windows => "windows",
.Posix => "posix",
.Native => "native",
.EfiApplication => "efi_application",
.EfiBootServiceDriver => "efi_boot_service_driver",
.EfiRom => "efi_rom",
.EfiRuntimeDriver => "efi_runtime_driver",
});
} }
if (compile.mingw_unicode_entry_point) { if (compile.mingw_unicode_entry_point) {

View File

@ -1138,16 +1138,8 @@ pub fn toCoffMachine(target: *const Target) std.coff.IMAGE.FILE.MACHINE {
}; };
} }
pub const SubSystem = enum { /// Deprecated; use 'std.zig.Subsystem' instead. To be removed after 0.16.0 is tagged.
Console, pub const SubSystem = std.zig.Subsystem;
Windows,
Posix,
Native,
EfiApplication,
EfiBootServiceDriver,
EfiRom,
EfiRuntimeDriver,
};
pub const Cpu = struct { pub const Cpu = struct {
/// Architecture /// Architecture

View File

@ -6,32 +6,6 @@ const root = @import("root");
pub const assembly = @import("builtin/assembly.zig"); pub const assembly = @import("builtin/assembly.zig");
/// `explicit_subsystem` is missing when the subsystem is automatically detected,
/// so Zig standard library has the subsystem detection logic here. This should generally be
/// used rather than `explicit_subsystem`.
/// On non-Windows targets, this is `null`.
pub const subsystem: ?std.Target.SubSystem = blk: {
if (@hasDecl(builtin, "explicit_subsystem")) break :blk builtin.explicit_subsystem;
switch (builtin.os.tag) {
.windows => {
if (builtin.is_test) {
break :blk std.Target.SubSystem.Console;
}
if (@hasDecl(root, "main") or
@hasDecl(root, "WinMain") or
@hasDecl(root, "wWinMain") or
@hasDecl(root, "WinMainCRTStartup") or
@hasDecl(root, "wWinMainCRTStartup"))
{
break :blk std.Target.SubSystem.Windows;
} else {
break :blk std.Target.SubSystem.Console;
}
},
else => break :blk null,
}
};
/// This data structure is used by the Zig language code generation and /// This data structure is used by the Zig language code generation and
/// therefore must be kept in sync with the compiler implementation. /// therefore must be kept in sync with the compiler implementation.
pub const StackTrace = struct { pub const StackTrace = struct {

View File

@ -734,24 +734,21 @@ pub fn call_wWinMain() std.os.windows.INT {
// - u32 in PEB.ProcessParameters.dwShowWindow // - u32 in PEB.ProcessParameters.dwShowWindow
// Since STARTUPINFO is the bottleneck for the allowed values, we use `u16` as the // Since STARTUPINFO is the bottleneck for the allowed values, we use `u16` as the
// type which can coerce into i32/c_int/u32 depending on how the user defines their wWinMain // type which can coerce into i32/c_int/u32 depending on how the user defines their wWinMain
// (the Win32 docs show wWinMain with `int` as the type for nCmdShow). // (the Win32 docs show wWinMain with `int` as the type for nShowCmd).
const nCmdShow: u16 = nCmdShow: { const nShowCmd: u16 = nShowCmd: {
// This makes Zig match the nCmdShow behavior of a C program with a WinMain symbol: // This makes Zig match the nShowCmd behavior of a C program with a WinMain symbol:
// - With STARTF_USESHOWWINDOW set in STARTUPINFO.dwFlags of the CreateProcess call: // - With STARTF_USESHOWWINDOW set in STARTUPINFO.dwFlags of the CreateProcess call:
// - Compiled with subsystem:console -> nCmdShow is always SW_SHOWDEFAULT // - nShowCmd is STARTUPINFO.wShowWindow from the parent CreateProcess call
// - Compiled with subsystem:windows -> nCmdShow is STARTUPINFO.wShowWindow from
// the parent CreateProcess call
// - With STARTF_USESHOWWINDOW unset: // - With STARTF_USESHOWWINDOW unset:
// - nCmdShow is always SW_SHOWDEFAULT // - nShowCmd is always SW_SHOWDEFAULT
const SW_SHOWDEFAULT = 10; const SW_SHOWDEFAULT = 10;
const STARTF_USESHOWWINDOW = 1; const STARTF_USESHOWWINDOW = 1;
// root having a wWinMain means that std.builtin.subsystem will always have a non-null value. if (peb.ProcessParameters.dwFlags & STARTF_USESHOWWINDOW != 0) {
if (std.builtin.subsystem.? == .Windows and peb.ProcessParameters.dwFlags & STARTF_USESHOWWINDOW != 0) { break :nShowCmd @truncate(peb.ProcessParameters.dwShowWindow);
break :nCmdShow @truncate(peb.ProcessParameters.dwShowWindow);
} }
break :nCmdShow SW_SHOWDEFAULT; break :nShowCmd SW_SHOWDEFAULT;
}; };
// second parameter hPrevInstance, MSDN: "This parameter is always NULL" // second parameter hPrevInstance, MSDN: "This parameter is always NULL"
return root.wWinMain(hInstance, null, lpCmdLine, nCmdShow); return root.wWinMain(hInstance, null, lpCmdLine, nShowCmd);
} }

View File

@ -349,6 +349,34 @@ pub const BuildId = union(enum) {
pub const LtoMode = enum { none, full, thin }; pub const LtoMode = enum { none, full, thin };
pub const Subsystem = enum {
console,
windows,
posix,
native,
efi_application,
efi_boot_service_driver,
efi_rom,
efi_runtime_driver,
/// Deprecated; use '.console' instead. To be removed after 0.16.0 is tagged.
pub const Console: Subsystem = .console;
/// Deprecated; use '.windows' instead. To be removed after 0.16.0 is tagged.
pub const Windows: Subsystem = .windows;
/// Deprecated; use '.posix' instead. To be removed after 0.16.0 is tagged.
pub const Posix: Subsystem = .posix;
/// Deprecated; use '.native' instead. To be removed after 0.16.0 is tagged.
pub const Native: Subsystem = .native;
/// Deprecated; use '.efi_application' instead. To be removed after 0.16.0 is tagged.
pub const EfiApplication: Subsystem = .efi_application;
/// Deprecated; use '.efi_boot_service_driver' instead. To be removed after 0.16.0 is tagged.
pub const EfiBootServiceDriver: Subsystem = .efi_boot_service_driver;
/// Deprecated; use '.efi_rom' instead. To be removed after 0.16.0 is tagged.
pub const EfiRom: Subsystem = .efi_rom;
/// Deprecated; use '.efi_runtime_driver' instead. To be removed after 0.16.0 is tagged.
pub const EfiRuntimeDriver: Subsystem = .efi_runtime_driver;
};
/// Renders a `std.Target.Cpu` value into a textual representation that can be parsed /// Renders a `std.Target.Cpu` value into a textual representation that can be parsed
/// via the `-mcpu` flag passed to the Zig compiler. /// via the `-mcpu` flag passed to the Zig compiler.
/// Appends the result to `buffer`. /// Appends the result to `buffer`.

View File

@ -1766,7 +1766,7 @@ pub const CreateOptions = struct {
reference_trace: ?u32 = null, reference_trace: ?u32 = null,
test_filters: []const []const u8 = &.{}, test_filters: []const []const u8 = &.{},
test_runner_path: ?[]const u8 = null, test_runner_path: ?[]const u8 = null,
subsystem: ?std.Target.SubSystem = null, subsystem: ?std.zig.Subsystem = null,
mingw_unicode_entry_point: bool = false, mingw_unicode_entry_point: bool = false,
/// (Zig compiler development) Enable dumping linker's state as JSON. /// (Zig compiler development) Enable dumping linker's state as JSON.
enable_link_snapshots: bool = false, enable_link_snapshots: bool = false,

View File

@ -448,7 +448,7 @@ pub const File = struct {
allow_shlib_undefined: ?bool, allow_shlib_undefined: ?bool,
allow_undefined_version: bool, allow_undefined_version: bool,
enable_new_dtags: ?bool, enable_new_dtags: ?bool,
subsystem: ?std.Target.SubSystem, subsystem: ?std.zig.Subsystem,
linker_script: ?[]const u8, linker_script: ?[]const u8,
version_script: ?[]const u8, version_script: ?[]const u8,
soname: ?[]const u8, soname: ?[]const u8,

View File

@ -19,7 +19,7 @@ const Coff = struct {
minor_subsystem_version: u16, minor_subsystem_version: u16,
lib_directories: []const Cache.Directory, lib_directories: []const Cache.Directory,
module_definition_file: ?[]const u8, module_definition_file: ?[]const u8,
subsystem: ?std.Target.SubSystem, subsystem: ?std.zig.Subsystem,
/// These flags are populated by `codegen.llvm.updateExports` to allow us to guess the subsystem. /// These flags are populated by `codegen.llvm.updateExports` to allow us to guess the subsystem.
lld_export_flags: struct { lld_export_flags: struct {
c_main: bool, c_main: bool,
@ -554,7 +554,7 @@ fn coffLink(lld: *Lld, arena: Allocator) !void {
try argv.append(try allocPrint(arena, "-DEF:{s}", .{def})); try argv.append(try allocPrint(arena, "-DEF:{s}", .{def}));
} }
const resolved_subsystem: ?std.Target.SubSystem = blk: { const resolved_subsystem: ?std.zig.Subsystem = blk: {
if (coff.subsystem) |explicit| break :blk explicit; if (coff.subsystem) |explicit| break :blk explicit;
switch (target.os.tag) { switch (target.os.tag) {
.windows => { .windows => {
@ -565,13 +565,13 @@ fn coffLink(lld: *Lld, arena: Allocator) !void {
coff.lld_export_flags.winmain_crt_startup or coff.lld_export_flags.winmain_crt_startup or
coff.lld_export_flags.wwinmain_crt_startup) coff.lld_export_flags.wwinmain_crt_startup)
{ {
break :blk .Console; break :blk .console;
} }
if (coff.lld_export_flags.winmain or coff.lld_export_flags.wwinmain) if (coff.lld_export_flags.winmain or coff.lld_export_flags.wwinmain)
break :blk .Windows; break :blk .windows;
} }
}, },
.uefi => break :blk .EfiApplication, .uefi => break :blk .efi_application,
else => {}, else => {},
} }
break :blk null; break :blk null;
@ -580,60 +580,23 @@ fn coffLink(lld: *Lld, arena: Allocator) !void {
const Mode = enum { uefi, win32 }; const Mode = enum { uefi, win32 };
const mode: Mode = mode: { const mode: Mode = mode: {
if (resolved_subsystem) |subsystem| { if (resolved_subsystem) |subsystem| {
const subsystem_suffix = try allocPrint(arena, ",{d}.{d}", .{ try argv.append(try allocPrint(arena, "-SUBSYSTEM:{s},{d}.{d}", .{
coff.major_subsystem_version, coff.minor_subsystem_version, @tagName(subsystem),
}); coff.major_subsystem_version,
coff.minor_subsystem_version,
switch (subsystem) { }));
.Console => { break :mode switch (subsystem) {
try argv.append(try allocPrint(arena, "-SUBSYSTEM:console{s}", .{ .console,
subsystem_suffix, .windows,
})); .posix,
break :mode .win32; .native,
}, => .win32,
.EfiApplication => { .efi_application,
try argv.append(try allocPrint(arena, "-SUBSYSTEM:efi_application{s}", .{ .efi_boot_service_driver,
subsystem_suffix, .efi_rom,
})); .efi_runtime_driver,
break :mode .uefi; => .uefi,
}, };
.EfiBootServiceDriver => {
try argv.append(try allocPrint(arena, "-SUBSYSTEM:efi_boot_service_driver{s}", .{
subsystem_suffix,
}));
break :mode .uefi;
},
.EfiRom => {
try argv.append(try allocPrint(arena, "-SUBSYSTEM:efi_rom{s}", .{
subsystem_suffix,
}));
break :mode .uefi;
},
.EfiRuntimeDriver => {
try argv.append(try allocPrint(arena, "-SUBSYSTEM:efi_runtime_driver{s}", .{
subsystem_suffix,
}));
break :mode .uefi;
},
.Native => {
try argv.append(try allocPrint(arena, "-SUBSYSTEM:native{s}", .{
subsystem_suffix,
}));
break :mode .win32;
},
.Posix => {
try argv.append(try allocPrint(arena, "-SUBSYSTEM:posix{s}", .{
subsystem_suffix,
}));
break :mode .win32;
},
.Windows => {
try argv.append(try allocPrint(arena, "-SUBSYSTEM:windows{s}", .{
subsystem_suffix,
}));
break :mode .win32;
},
}
} else if (target.os.tag == .uefi) { } else if (target.os.tag == .uefi) {
break :mode .uefi; break :mode .uefi;
} else { } else {

View File

@ -893,7 +893,7 @@ fn buildOutputType(
var override_global_cache_dir: ?[]const u8 = try EnvVar.ZIG_GLOBAL_CACHE_DIR.get(arena); var override_global_cache_dir: ?[]const u8 = try EnvVar.ZIG_GLOBAL_CACHE_DIR.get(arena);
var override_lib_dir: ?[]const u8 = try EnvVar.ZIG_LIB_DIR.get(arena); var override_lib_dir: ?[]const u8 = try EnvVar.ZIG_LIB_DIR.get(arena);
var clang_preprocessor_mode: Compilation.ClangPreprocessorMode = .no; var clang_preprocessor_mode: Compilation.ClangPreprocessorMode = .no;
var subsystem: ?std.Target.SubSystem = null; var subsystem: ?std.zig.Subsystem = null;
var major_subsystem_version: ?u16 = null; var major_subsystem_version: ?u16 = null;
var minor_subsystem_version: ?u16 = null; var minor_subsystem_version: ?u16 = null;
var mingw_unicode_entry_point: bool = false; var mingw_unicode_entry_point: bool = false;
@ -1135,7 +1135,7 @@ fn buildOutputType(
} }
n_jobs = num; n_jobs = num;
} else if (mem.eql(u8, arg, "--subsystem")) { } else if (mem.eql(u8, arg, "--subsystem")) {
subsystem = try parseSubSystem(args_iter.nextOrFatal()); subsystem = try parseSubsystem(args_iter.nextOrFatal());
} else if (mem.eql(u8, arg, "-O")) { } else if (mem.eql(u8, arg, "-O")) {
mod_opts.optimize_mode = parseOptimizeMode(args_iter.nextOrFatal()); mod_opts.optimize_mode = parseOptimizeMode(args_iter.nextOrFatal());
} else if (mem.cutPrefix(u8, arg, "-fentry=")) |rest| { } else if (mem.cutPrefix(u8, arg, "-fentry=")) |rest| {
@ -2415,7 +2415,7 @@ fn buildOutputType(
} else if (mem.eql(u8, arg, "-rpath") or mem.eql(u8, arg, "--rpath") or mem.eql(u8, arg, "-R")) { } else if (mem.eql(u8, arg, "-rpath") or mem.eql(u8, arg, "--rpath") or mem.eql(u8, arg, "-R")) {
try create_module.rpath_list.append(arena, linker_args_it.nextOrFatal()); try create_module.rpath_list.append(arena, linker_args_it.nextOrFatal());
} else if (mem.eql(u8, arg, "--subsystem")) { } else if (mem.eql(u8, arg, "--subsystem")) {
subsystem = try parseSubSystem(linker_args_it.nextOrFatal()); subsystem = try parseSubsystem(linker_args_it.nextOrFatal());
} else if (mem.eql(u8, arg, "-I") or } else if (mem.eql(u8, arg, "-I") or
mem.eql(u8, arg, "--dynamic-linker") or mem.eql(u8, arg, "--dynamic-linker") or
mem.eql(u8, arg, "-dynamic-linker")) mem.eql(u8, arg, "-dynamic-linker"))
@ -2743,7 +2743,7 @@ fn buildOutputType(
try symbol_wrap_set.put(arena, next_arg, {}); try symbol_wrap_set.put(arena, next_arg, {});
} else if (mem.startsWith(u8, arg, "/subsystem:")) { } else if (mem.startsWith(u8, arg, "/subsystem:")) {
var split_it = mem.splitBackwardsScalar(u8, arg, ':'); var split_it = mem.splitBackwardsScalar(u8, arg, ':');
subsystem = try parseSubSystem(split_it.first()); subsystem = try parseSubsystem(split_it.first());
} else if (mem.startsWith(u8, arg, "/implib:")) { } else if (mem.startsWith(u8, arg, "/implib:")) {
var split_it = mem.splitBackwardsScalar(u8, arg, ':'); var split_it = mem.splitBackwardsScalar(u8, arg, ':');
emit_implib = .{ .yes = split_it.first() }; emit_implib = .{ .yes = split_it.first() };
@ -6657,26 +6657,10 @@ fn warnAboutForeignBinaries(
} }
} }
fn parseSubSystem(next_arg: []const u8) !std.Target.SubSystem { fn parseSubsystem(arg: []const u8) !std.zig.Subsystem {
if (mem.eql(u8, next_arg, "console")) { return std.meta.stringToEnum(std.zig.Subsystem, arg) orelse
return .Console;
} else if (mem.eql(u8, next_arg, "windows")) {
return .Windows;
} else if (mem.eql(u8, next_arg, "posix")) {
return .Posix;
} else if (mem.eql(u8, next_arg, "native")) {
return .Native;
} else if (mem.eql(u8, next_arg, "efi_application")) {
return .EfiApplication;
} else if (mem.eql(u8, next_arg, "efi_boot_service_driver")) {
return .EfiBootServiceDriver;
} else if (mem.eql(u8, next_arg, "efi_rom")) {
return .EfiRom;
} else if (mem.eql(u8, next_arg, "efi_runtime_driver")) {
return .EfiRuntimeDriver;
} else {
fatal("invalid: --subsystem: '{s}'. Options are:\n{s}", .{ fatal("invalid: --subsystem: '{s}'. Options are:\n{s}", .{
next_arg, arg,
\\ console \\ console
\\ windows \\ windows
\\ posix \\ posix
@ -6687,7 +6671,6 @@ fn parseSubSystem(next_arg: []const u8) !std.Target.SubSystem {
\\ efi_runtime_driver \\ efi_runtime_driver
\\ \\
}); });
}
} }
/// Model a header searchlist as a group. /// Model a header searchlist as a group.

View File

@ -31,7 +31,7 @@ pub fn build(b: *std.Build) void {
.target = target, .target = target,
}), }),
}); });
exe.subsystem = .Console; exe.subsystem = .console;
exe.root_module.linkSystemLibrary("kernel32", .{}); exe.root_module.linkSystemLibrary("kernel32", .{});
exe.root_module.linkSystemLibrary("ntdll", .{}); exe.root_module.linkSystemLibrary("ntdll", .{});
exe.root_module.addObject(obj); exe.root_module.addObject(obj);

View File

@ -53,7 +53,7 @@ pub fn build(b: *std.Build) void {
.link_libc = true, .link_libc = true,
}), }),
}); });
// Note: `exe.subsystem = .Windows;` is not necessary // Note: `exe.subsystem = .windows;` is not necessary
exe.root_module.addCSourceFile(.{ .file = b.path("winmain.c") }); exe.root_module.addCSourceFile(.{ .file = b.path("winmain.c") });
_ = exe.getEmittedBin(); _ = exe.getEmittedBin();
@ -71,7 +71,7 @@ pub fn build(b: *std.Build) void {
}), }),
}); });
exe.mingw_unicode_entry_point = true; exe.mingw_unicode_entry_point = true;
// Note: `exe.subsystem = .Windows;` is not necessary // Note: `exe.subsystem = .windows;` is not necessary
exe.root_module.addCSourceFile(.{ .file = b.path("wwinmain.c") }); exe.root_module.addCSourceFile(.{ .file = b.path("wwinmain.c") });
_ = exe.getEmittedBin(); _ = exe.getEmittedBin();