aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorRyan Liptak <squeek502@hotmail.com>2023-09-11 23:05:48 -0700
committerRyan Liptak <squeek502@hotmail.com>2023-09-17 03:09:58 -0700
commit0168ed7bf1c7fc5010fa82eaf33ed1b3af817709 (patch)
tree8b5491dde63f2cae8a53db61f1a6be777cb19fa8 /src
parent4fac7a5263b9b14f63d2459f795ac9d2eee51c85 (diff)
downloadzig-0168ed7bf1c7fc5010fa82eaf33ed1b3af817709.tar.gz
zig-0168ed7bf1c7fc5010fa82eaf33ed1b3af817709.zip
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.
Diffstat (limited to 'src')
-rw-r--r--src/Compilation.zig86
-rw-r--r--src/main.zig16
2 files changed, 93 insertions, 9 deletions
diff --git a/src/Compilation.zig b/src/Compilation.zig
index 17d298b66d..6a078e8581 100644
--- a/src/Compilation.zig
+++ b/src/Compilation.zig
@@ -243,6 +243,17 @@ pub const RcSourceFile = struct {
extra_flags: []const []const u8 = &.{},
};
+pub const RcIncludes = enum {
+ /// Use MSVC if available, fall back to MinGW.
+ any,
+ /// Use MSVC include paths (MSVC install + Windows SDK, must be present on the system).
+ msvc,
+ /// Use MinGW include paths (distributed with Zig).
+ gnu,
+ /// Do not use any autodetected include paths.
+ none,
+};
+
const Job = union(enum) {
/// Write the constant value for a Decl to the output file.
codegen_decl: Module.Decl.Index,
@@ -568,6 +579,7 @@ pub const InitOptions = struct {
symbol_wrap_set: std.StringArrayHashMapUnmanaged(void) = .{},
c_source_files: []const CSourceFile = &[0]CSourceFile{},
rc_source_files: []const RcSourceFile = &[0]RcSourceFile{},
+ rc_includes: RcIncludes = .any,
link_objects: []LinkObject = &[0]LinkObject{},
framework_dirs: []const []const u8 = &[0][]const u8{},
frameworks: []const Framework = &.{},
@@ -1001,16 +1013,9 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
options.libc_installation,
);
- const rc_dirs = try detectLibCIncludeDirs(
+ const rc_dirs = try detectWin32ResourceIncludeDirs(
arena,
- options.zig_lib_directory.path.?,
- options.target,
- options.is_native_abi,
- // Set "link libc" to true here whenever there are rc files to compile, since
- // the .rc preprocessor will need to know the libc include dirs even if we
- // are not linking libc
- options.rc_source_files.len > 0,
- options.libc_installation,
+ options,
);
const sysroot = options.sysroot orelse libc_dirs.sysroot;
@@ -2450,6 +2455,8 @@ fn addNonIncrementalStuffToCacheManifest(comp: *Compilation, man: *Cache.Manifes
man.hash.addListOfBytes(key.src.extra_flags);
}
+ man.hash.addListOfBytes(comp.rc_include_dir_list);
+
cache_helpers.addOptionalEmitLoc(&man.hash, comp.emit_asm);
cache_helpers.addOptionalEmitLoc(&man.hash, comp.emit_llvm_ir);
cache_helpers.addOptionalEmitLoc(&man.hash, comp.emit_llvm_bc);
@@ -5156,6 +5163,67 @@ fn failCObjWithOwnedErrorMsg(
return error.AnalysisFail;
}
+/// The include directories used when preprocessing .rc files are separate from the
+/// target. Which include directories are used is determined by `options.rc_includes`.
+///
+/// Note: It 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 in the MinGW headers vs the MSVC headers, but any such
+/// differences would likely be a MinGW bug.
+fn detectWin32ResourceIncludeDirs(arena: Allocator, options: InitOptions) !LibCDirs {
+ // Set the includes to .none here when there are no rc files to compile
+ var includes = if (options.rc_source_files.len > 0) options.rc_includes else .none;
+ if (builtin.target.os.tag != .windows) {
+ switch (includes) {
+ // MSVC can't be found when the host isn't Windows, so short-circuit.
+ .msvc => return error.WindowsSdkNotFound,
+ // Skip straight to gnu since we won't be able to detect MSVC on non-Windows hosts.
+ .any => includes = .gnu,
+ .none, .gnu => {},
+ }
+ }
+ while (true) {
+ switch (includes) {
+ .any, .msvc => return detectLibCIncludeDirs(
+ arena,
+ options.zig_lib_directory.path.?,
+ .{
+ .cpu = options.target.cpu,
+ .os = options.target.os,
+ .abi = .msvc,
+ .ofmt = options.target.ofmt,
+ },
+ options.is_native_abi,
+ // The .rc preprocessor will need to know the libc include dirs even if we
+ // are not linking libc, so force 'link_libc' to true
+ true,
+ options.libc_installation,
+ ) catch |err| {
+ if (includes == .any) {
+ // fall back to mingw
+ includes = .gnu;
+ continue;
+ }
+ return err;
+ },
+ .gnu => return detectLibCFromBuilding(arena, options.zig_lib_directory.path.?, .{
+ .cpu = options.target.cpu,
+ .os = options.target.os,
+ .abi = .gnu,
+ .ofmt = options.target.ofmt,
+ }),
+ .none => return LibCDirs{
+ .libc_include_dir_list = &[0][]u8{},
+ .libc_installation = null,
+ .libc_framework_dir_list = &.{},
+ .sysroot = null,
+ },
+ }
+ }
+}
+
fn failWin32Resource(comp: *Compilation, win32_resource: *Win32Resource, comptime format: []const u8, args: anytype) SemaError {
@setCold(true);
var bundle: ErrorBundle.Wip = undefined;
diff --git a/src/main.zig b/src/main.zig
index f08e8f6433..2913ac2ea2 100644
--- a/src/main.zig
+++ b/src/main.zig
@@ -473,6 +473,11 @@ const usage_build_generic =
\\ --libc [file] Provide a file which specifies libc paths
\\ -cflags [flags] -- Set extra flags for the next positional C source files
\\ -rcflags [flags] -- Set extra flags for the next positional .rc source files
+ \\ -rcincludes=[type] Set the type of includes to use when compiling .rc source files
+ \\ any (default) Use msvc if available, fall back to gnu
+ \\ msvc Use msvc include paths (must be present on the system)
+ \\ gnu Use mingw include paths (distributed with Zig)
+ \\ none Do not use any autodetected include paths
\\
\\Link Options:
\\ -l[lib], --library [lib] Link against system library (only if actually used)
@@ -927,6 +932,7 @@ fn buildOutputType(
var symbol_wrap_set: std.StringArrayHashMapUnmanaged(void) = .{};
var c_source_files = std.ArrayList(Compilation.CSourceFile).init(arena);
var rc_source_files = std.ArrayList(Compilation.RcSourceFile).init(arena);
+ var rc_includes: Compilation.RcIncludes = .any;
var res_files = std.ArrayList(Compilation.LinkObject).init(arena);
var link_objects = std.ArrayList(Compilation.LinkObject).init(arena);
var framework_dirs = std.ArrayList([]const u8).init(arena);
@@ -1046,6 +1052,10 @@ fn buildOutputType(
if (mem.eql(u8, next_arg, "--")) break;
try extra_cflags.append(next_arg);
}
+ } else if (mem.eql(u8, arg, "-rcincludes")) {
+ rc_includes = parseRcIncludes(args_iter.nextOrFatal());
+ } else if (mem.startsWith(u8, arg, "-rcincludes=")) {
+ rc_includes = parseRcIncludes(arg["-rcincludes=".len..]);
} else if (mem.eql(u8, arg, "-rcflags")) {
extra_rcflags.shrinkRetainingCapacity(0);
while (true) {
@@ -3369,6 +3379,7 @@ fn buildOutputType(
.symbol_wrap_set = symbol_wrap_set,
.c_source_files = c_source_files.items,
.rc_source_files = rc_source_files.items,
+ .rc_includes = rc_includes,
.link_objects = link_objects.items,
.framework_dirs = framework_dirs.items,
.frameworks = resolved_frameworks.items,
@@ -6532,3 +6543,8 @@ fn accessFrameworkPath(
return false;
}
+
+fn parseRcIncludes(arg: []const u8) Compilation.RcIncludes {
+ return std.meta.stringToEnum(Compilation.RcIncludes, arg) orelse
+ fatal("unsupported rc includes type: '{s}'", .{arg});
+}