authorgravatar for squeek502@hotmail.comRyan Liptak <squeek502@hotmail.com> 2023-09-11 23:05:48-07:00
committergravatar for squeek502@hotmail.comRyan Liptak <squeek502@hotmail.com> 2023-09-17 03:09:58-07:00
log0168ed7bf1c7fc5010fa82eaf33ed1b3af817709
tree8b5491dde63f2cae8a53db61f1a6be777cb19fa8
parent4fac7a5263b9b14f63d2459f795ac9d2eee51c85

rc compilation: Use MSVC includes if present, fallback to mingw

The include directories used when preprocessing .rc files are now separate from the target, and by default will use the system MSVC include paths if the MSVC + Windows SDK are present, otherwise it will fall back to the MinGW includes distributed with Zig. This default behavior can be overridden by the `-rcincludes` option (possible values: any (the default), msvc, gnu, or none). This behavior is useful because Windows resource files may `#include` files that only exist with in the MSVC include dirs (e.g. in `<MSVC install directory>/atlmfc/include` which can contain other .rc files, images, icons, cursors, etc). So, by defaulting to the `any` behavior (MSVC if present, MinGW fallback), users will by default get behavior that is most-likely-to-work. It also should be okay that the include directories used when compiling .rc files differ from the include directories used when compiling the main binary, since the .res format is not dependent on anything ABI-related. The only relevant differences would be things like `#define` constants being different values in the MinGW headers vs the MSVC headers, but any such differences would likely be a MinGW bug.

3 files changed, 106 insertions(+), 9 deletions(-)

lib/std/Build/Step/Compile.zig+13
...@@ -90,6 +90,14 @@ is_linking_libc: bool,...@@ -90,6 +90,14 @@ is_linking_libc: bool,
90is_linking_libcpp: bool,90is_linking_libcpp: bool,
91vcpkg_bin_path: ?[]const u8 = null,91vcpkg_bin_path: ?[]const u8 = null,
9292
93// keep in sync with src/Compilation.zig:RcIncludes
94/// Behavior of automatic detection of include directories when compiling .rc files.
95/// any: Use MSVC if available, fall back to MinGW.
96/// msvc: Use MSVC include paths (must be present on the system).
97/// gnu: Use MinGW include paths (distributed with Zig).
98/// none: Do not use any autodetected include paths.
99rc_includes: enum { any, msvc, gnu, none } = .any,
100
93installed_path: ?[]const u8,101installed_path: ?[]const u8,
94102
95/// Base address for an executable image.103/// Base address for an executable image.
...@@ -1949,6 +1957,11 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {...@@ -1949,6 +1957,11 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
1949 }1957 }
1950 }1958 }
19511959
1960 if (self.rc_includes != .any) {
1961 try zig_args.append("-rcincludes");
1962 try zig_args.append(@tagName(self.rc_includes));
1963 }
1964
1952 try addFlag(&zig_args, "valgrind", self.valgrind_support);1965 try addFlag(&zig_args, "valgrind", self.valgrind_support);
1953 try addFlag(&zig_args, "each-lib-rpath", self.each_lib_rpath);1966 try addFlag(&zig_args, "each-lib-rpath", self.each_lib_rpath);
19541967
src/Compilation.zig+77-9
...@@ -243,6 +243,17 @@ pub const RcSourceFile = struct {...@@ -243,6 +243,17 @@ pub const RcSourceFile = struct {
243 extra_flags: []const []const u8 = &.{},243 extra_flags: []const []const u8 = &.{},
244};244};
245245
246pub const RcIncludes = enum {
247 /// Use MSVC if available, fall back to MinGW.
248 any,
249 /// Use MSVC include paths (MSVC install + Windows SDK, must be present on the system).
250 msvc,
251 /// Use MinGW include paths (distributed with Zig).
252 gnu,
253 /// Do not use any autodetected include paths.
254 none,
255};
256
246const Job = union(enum) {257const Job = union(enum) {
247 /// Write the constant value for a Decl to the output file.258 /// Write the constant value for a Decl to the output file.
248 codegen_decl: Module.Decl.Index,259 codegen_decl: Module.Decl.Index,
...@@ -568,6 +579,7 @@ pub const InitOptions = struct {...@@ -568,6 +579,7 @@ pub const InitOptions = struct {
568 symbol_wrap_set: std.StringArrayHashMapUnmanaged(void) = .{},579 symbol_wrap_set: std.StringArrayHashMapUnmanaged(void) = .{},
569 c_source_files: []const CSourceFile = &[0]CSourceFile{},580 c_source_files: []const CSourceFile = &[0]CSourceFile{},
570 rc_source_files: []const RcSourceFile = &[0]RcSourceFile{},581 rc_source_files: []const RcSourceFile = &[0]RcSourceFile{},
582 rc_includes: RcIncludes = .any,
571 link_objects: []LinkObject = &[0]LinkObject{},583 link_objects: []LinkObject = &[0]LinkObject{},
572 framework_dirs: []const []const u8 = &[0][]const u8{},584 framework_dirs: []const []const u8 = &[0][]const u8{},
573 frameworks: []const Framework = &.{},585 frameworks: []const Framework = &.{},
...@@ -1001,16 +1013,9 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {...@@ -1001,16 +1013,9 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
1001 options.libc_installation,1013 options.libc_installation,
1002 );1014 );
10031015
1004 const rc_dirs = try detectLibCIncludeDirs(1016 const rc_dirs = try detectWin32ResourceIncludeDirs(
1005 arena,1017 arena,
1006 options.zig_lib_directory.path.?,1018 options,
1007 options.target,
1008 options.is_native_abi,
1009 // Set "link libc" to true here whenever there are rc files to compile, since
1010 // the .rc preprocessor will need to know the libc include dirs even if we
1011 // are not linking libc
1012 options.rc_source_files.len > 0,
1013 options.libc_installation,
1014 );1019 );
10151020
1016 const sysroot = options.sysroot orelse libc_dirs.sysroot;1021 const sysroot = options.sysroot orelse libc_dirs.sysroot;
...@@ -2450,6 +2455,8 @@ fn addNonIncrementalStuffToCacheManifest(comp: *Compilation, man: *Cache.Manifes...@@ -2450,6 +2455,8 @@ fn addNonIncrementalStuffToCacheManifest(comp: *Compilation, man: *Cache.Manifes
2450 man.hash.addListOfBytes(key.src.extra_flags);2455 man.hash.addListOfBytes(key.src.extra_flags);
2451 }2456 }
24522457
2458 man.hash.addListOfBytes(comp.rc_include_dir_list);
2459
2453 cache_helpers.addOptionalEmitLoc(&man.hash, comp.emit_asm);2460 cache_helpers.addOptionalEmitLoc(&man.hash, comp.emit_asm);
2454 cache_helpers.addOptionalEmitLoc(&man.hash, comp.emit_llvm_ir);2461 cache_helpers.addOptionalEmitLoc(&man.hash, comp.emit_llvm_ir);
2455 cache_helpers.addOptionalEmitLoc(&man.hash, comp.emit_llvm_bc);2462 cache_helpers.addOptionalEmitLoc(&man.hash, comp.emit_llvm_bc);
...@@ -5156,6 +5163,67 @@ fn failCObjWithOwnedErrorMsg(...@@ -5156,6 +5163,67 @@ fn failCObjWithOwnedErrorMsg(
5156 return error.AnalysisFail;5163 return error.AnalysisFail;
5157}5164}
51585165
5166/// The include directories used when preprocessing .rc files are separate from the
5167/// target. Which include directories are used is determined by `options.rc_includes`.
5168///
5169/// Note: It should be okay that the include directories used when compiling .rc
5170/// files differ from the include directories used when compiling the main
5171/// binary, since the .res format is not dependent on anything ABI-related. The
5172/// only relevant differences would be things like `#define` constants being
5173/// different in the MinGW headers vs the MSVC headers, but any such
5174/// differences would likely be a MinGW bug.
5175fn detectWin32ResourceIncludeDirs(arena: Allocator, options: InitOptions) !LibCDirs {
5176 // Set the includes to .none here when there are no rc files to compile
5177 var includes = if (options.rc_source_files.len > 0) options.rc_includes else .none;
5178 if (builtin.target.os.tag != .windows) {
5179 switch (includes) {
5180 // MSVC can't be found when the host isn't Windows, so short-circuit.
5181 .msvc => return error.WindowsSdkNotFound,
5182 // Skip straight to gnu since we won't be able to detect MSVC on non-Windows hosts.
5183 .any => includes = .gnu,
5184 .none, .gnu => {},
5185 }
5186 }
5187 while (true) {
5188 switch (includes) {
5189 .any, .msvc => return detectLibCIncludeDirs(
5190 arena,
5191 options.zig_lib_directory.path.?,
5192 .{
5193 .cpu = options.target.cpu,
5194 .os = options.target.os,
5195 .abi = .msvc,
5196 .ofmt = options.target.ofmt,
5197 },
5198 options.is_native_abi,
5199 // The .rc preprocessor will need to know the libc include dirs even if we
5200 // are not linking libc, so force 'link_libc' to true
5201 true,
5202 options.libc_installation,
5203 ) catch |err| {
5204 if (includes == .any) {
5205 // fall back to mingw
5206 includes = .gnu;
5207 continue;
5208 }
5209 return err;
5210 },
5211 .gnu => return detectLibCFromBuilding(arena, options.zig_lib_directory.path.?, .{
5212 .cpu = options.target.cpu,
5213 .os = options.target.os,
5214 .abi = .gnu,
5215 .ofmt = options.target.ofmt,
5216 }),
5217 .none => return LibCDirs{
5218 .libc_include_dir_list = &[0][]u8{},
5219 .libc_installation = null,
5220 .libc_framework_dir_list = &.{},
5221 .sysroot = null,
5222 },
5223 }
5224 }
5225}
5226
5159fn failWin32Resource(comp: *Compilation, win32_resource: *Win32Resource, comptime format: []const u8, args: anytype) SemaError {5227fn failWin32Resource(comp: *Compilation, win32_resource: *Win32Resource, comptime format: []const u8, args: anytype) SemaError {
5160 @setCold(true);5228 @setCold(true);
5161 var bundle: ErrorBundle.Wip = undefined;5229 var bundle: ErrorBundle.Wip = undefined;
src/main.zig+16
...@@ -473,6 +473,11 @@ const usage_build_generic =...@@ -473,6 +473,11 @@ const usage_build_generic =
473 \\ --libc [file] Provide a file which specifies libc paths473 \\ --libc [file] Provide a file which specifies libc paths
474 \\ -cflags [flags] -- Set extra flags for the next positional C source files474 \\ -cflags [flags] -- Set extra flags for the next positional C source files
475 \\ -rcflags [flags] -- Set extra flags for the next positional .rc source files475 \\ -rcflags [flags] -- Set extra flags for the next positional .rc source files
476 \\ -rcincludes=[type] Set the type of includes to use when compiling .rc source files
477 \\ any (default) Use msvc if available, fall back to gnu
478 \\ msvc Use msvc include paths (must be present on the system)
479 \\ gnu Use mingw include paths (distributed with Zig)
480 \\ none Do not use any autodetected include paths
476 \\481 \\
477 \\Link Options:482 \\Link Options:
478 \\ -l[lib], --library [lib] Link against system library (only if actually used)483 \\ -l[lib], --library [lib] Link against system library (only if actually used)
...@@ -927,6 +932,7 @@ fn buildOutputType(...@@ -927,6 +932,7 @@ fn buildOutputType(
927 var symbol_wrap_set: std.StringArrayHashMapUnmanaged(void) = .{};932 var symbol_wrap_set: std.StringArrayHashMapUnmanaged(void) = .{};
928 var c_source_files = std.ArrayList(Compilation.CSourceFile).init(arena);933 var c_source_files = std.ArrayList(Compilation.CSourceFile).init(arena);
929 var rc_source_files = std.ArrayList(Compilation.RcSourceFile).init(arena);934 var rc_source_files = std.ArrayList(Compilation.RcSourceFile).init(arena);
935 var rc_includes: Compilation.RcIncludes = .any;
930 var res_files = std.ArrayList(Compilation.LinkObject).init(arena);936 var res_files = std.ArrayList(Compilation.LinkObject).init(arena);
931 var link_objects = std.ArrayList(Compilation.LinkObject).init(arena);937 var link_objects = std.ArrayList(Compilation.LinkObject).init(arena);
932 var framework_dirs = std.ArrayList([]const u8).init(arena);938 var framework_dirs = std.ArrayList([]const u8).init(arena);
...@@ -1046,6 +1052,10 @@ fn buildOutputType(...@@ -1046,6 +1052,10 @@ fn buildOutputType(
1046 if (mem.eql(u8, next_arg, "--")) break;1052 if (mem.eql(u8, next_arg, "--")) break;
1047 try extra_cflags.append(next_arg);1053 try extra_cflags.append(next_arg);
1048 }1054 }
1055 } else if (mem.eql(u8, arg, "-rcincludes")) {
1056 rc_includes = parseRcIncludes(args_iter.nextOrFatal());
1057 } else if (mem.startsWith(u8, arg, "-rcincludes=")) {
1058 rc_includes = parseRcIncludes(arg["-rcincludes=".len..]);
1049 } else if (mem.eql(u8, arg, "-rcflags")) {1059 } else if (mem.eql(u8, arg, "-rcflags")) {
1050 extra_rcflags.shrinkRetainingCapacity(0);1060 extra_rcflags.shrinkRetainingCapacity(0);
1051 while (true) {1061 while (true) {
...@@ -3369,6 +3379,7 @@ fn buildOutputType(...@@ -3369,6 +3379,7 @@ fn buildOutputType(
3369 .symbol_wrap_set = symbol_wrap_set,3379 .symbol_wrap_set = symbol_wrap_set,
3370 .c_source_files = c_source_files.items,3380 .c_source_files = c_source_files.items,
3371 .rc_source_files = rc_source_files.items,3381 .rc_source_files = rc_source_files.items,
3382 .rc_includes = rc_includes,
3372 .link_objects = link_objects.items,3383 .link_objects = link_objects.items,
3373 .framework_dirs = framework_dirs.items,3384 .framework_dirs = framework_dirs.items,
3374 .frameworks = resolved_frameworks.items,3385 .frameworks = resolved_frameworks.items,
...@@ -6532,3 +6543,8 @@ fn accessFrameworkPath(...@@ -6532,3 +6543,8 @@ fn accessFrameworkPath(
65326543
6533 return false;6544 return false;
6534}6545}
6546
6547fn parseRcIncludes(arg: []const u8) Compilation.RcIncludes {
6548 return std.meta.stringToEnum(Compilation.RcIncludes, arg) orelse
6549 fatal("unsupported rc includes type: '{s}'", .{arg});
6550}