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,
9090is_linking_libcpp: bool,
9191vcpkg_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
93101installed_path: ?[]const u8,
94102
95103/// Base address for an executable image.
......@@ -1949,6 +1957,11 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
19491957 }
19501958 }
19511959
1960 if (self.rc_includes != .any) {
1961 try zig_args.append("-rcincludes");
1962 try zig_args.append(@tagName(self.rc_includes));
1963 }
1964
19521965 try addFlag(&zig_args, "valgrind", self.valgrind_support);
19531966 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 {
243243 extra_flags: []const []const u8 = &.{},
244244};
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
246257const Job = union(enum) {
247258 /// Write the constant value for a Decl to the output file.
248259 codegen_decl: Module.Decl.Index,
......@@ -568,6 +579,7 @@ pub const InitOptions = struct {
568579 symbol_wrap_set: std.StringArrayHashMapUnmanaged(void) = .{},
569580 c_source_files: []const CSourceFile = &[0]CSourceFile{},
570581 rc_source_files: []const RcSourceFile = &[0]RcSourceFile{},
582 rc_includes: RcIncludes = .any,
571583 link_objects: []LinkObject = &[0]LinkObject{},
572584 framework_dirs: []const []const u8 = &[0][]const u8{},
573585 frameworks: []const Framework = &.{},
......@@ -1001,16 +1013,9 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
10011013 options.libc_installation,
10021014 );
10031015
1004 const rc_dirs = try detectLibCIncludeDirs(
1016 const rc_dirs = try detectWin32ResourceIncludeDirs(
10051017 arena,
1006 options.zig_lib_directory.path.?,
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,
1018 options,
10141019 );
10151020
10161021 const sysroot = options.sysroot orelse libc_dirs.sysroot;
......@@ -2450,6 +2455,8 @@ fn addNonIncrementalStuffToCacheManifest(comp: *Compilation, man: *Cache.Manifes
24502455 man.hash.addListOfBytes(key.src.extra_flags);
24512456 }
24522457
2458 man.hash.addListOfBytes(comp.rc_include_dir_list);
2459
24532460 cache_helpers.addOptionalEmitLoc(&man.hash, comp.emit_asm);
24542461 cache_helpers.addOptionalEmitLoc(&man.hash, comp.emit_llvm_ir);
24552462 cache_helpers.addOptionalEmitLoc(&man.hash, comp.emit_llvm_bc);
......@@ -5156,6 +5163,67 @@ fn failCObjWithOwnedErrorMsg(
51565163 return error.AnalysisFail;
51575164}
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
51595227fn failWin32Resource(comp: *Compilation, win32_resource: *Win32Resource, comptime format: []const u8, args: anytype) SemaError {
51605228 @setCold(true);
51615229 var bundle: ErrorBundle.Wip = undefined;
src/main.zig+16
......@@ -473,6 +473,11 @@ const usage_build_generic =
473473 \\ --libc [file] Provide a file which specifies libc paths
474474 \\ -cflags [flags] -- Set extra flags for the next positional C source files
475475 \\ -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
476481 \\
477482 \\Link Options:
478483 \\ -l[lib], --library [lib] Link against system library (only if actually used)
......@@ -927,6 +932,7 @@ fn buildOutputType(
927932 var symbol_wrap_set: std.StringArrayHashMapUnmanaged(void) = .{};
928933 var c_source_files = std.ArrayList(Compilation.CSourceFile).init(arena);
929934 var rc_source_files = std.ArrayList(Compilation.RcSourceFile).init(arena);
935 var rc_includes: Compilation.RcIncludes = .any;
930936 var res_files = std.ArrayList(Compilation.LinkObject).init(arena);
931937 var link_objects = std.ArrayList(Compilation.LinkObject).init(arena);
932938 var framework_dirs = std.ArrayList([]const u8).init(arena);
......@@ -1046,6 +1052,10 @@ fn buildOutputType(
10461052 if (mem.eql(u8, next_arg, "--")) break;
10471053 try extra_cflags.append(next_arg);
10481054 }
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..]);
10491059 } else if (mem.eql(u8, arg, "-rcflags")) {
10501060 extra_rcflags.shrinkRetainingCapacity(0);
10511061 while (true) {
......@@ -3369,6 +3379,7 @@ fn buildOutputType(
33693379 .symbol_wrap_set = symbol_wrap_set,
33703380 .c_source_files = c_source_files.items,
33713381 .rc_source_files = rc_source_files.items,
3382 .rc_includes = rc_includes,
33723383 .link_objects = link_objects.items,
33733384 .framework_dirs = framework_dirs.items,
33743385 .frameworks = resolved_frameworks.items,
......@@ -6532,3 +6543,8 @@ fn accessFrameworkPath(
65326543
65336544 return false;
65346545}
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}