Skip to content

Commit 5f15acc

Browse files
committed
Add preliminary support for Windows .manifest files
An embedded manifest file is really just XML data embedded as a RT_MANIFEST resource (ID = 24). Typically, the Windows-only 'Manifest Tool' (`mt.exe`) is used to embed manifest files, and `mt.exe` also seems to perform some transformation of the manifest data before embedding, but in testing it doesn't seem like the transformations are necessary to get the intended result. So, to handle embedding manifest files, Zig now takes the following approach: - Generate a .rc file with the contents `1 24 "path-to-manifest.manifest"` - Compile that generated .rc file into a .res file - Link the .res file into the final binary This effectively achieves the same thing as `mt.exe` minus the validation/transformations of the XML data that it performs. How this is used: On the command line: ``` zig build-exe main.zig main.manifest ``` (on the command line, specifying a .manifest file when the target object format is not COFF is an error) or in build.zig: ``` const exe = b.addExecutable(.{ .name = "manifest-test", .root_source_file = .{ .path = "main.zig" }, .target = target, .optimize = optimize, .win32_manifest = .{ .path = "main.manifest" }, }); ``` (in build.zig, the manifest file is ignored if the target object format is not COFF) Note: Currently, only one manifest file can be specified per compilation. This is because the ID of the manifest resource is currently always 1. Specifying multiple manifests could be supported if a way for the user to specify an ID for each manifest is added (manifest IDs must be a u16). Closes #17406 options
1 parent b0f031f commit 5f15acc

File tree

4 files changed

+228
-32
lines changed

4 files changed

+228
-32
lines changed

lib/std/Build.zig

+14
Original file line numberDiff line numberDiff line change
@@ -635,6 +635,12 @@ pub const ExecutableOptions = struct {
635635
use_lld: ?bool = null,
636636
zig_lib_dir: ?LazyPath = null,
637637
main_mod_path: ?LazyPath = null,
638+
/// Embed a `.manifest` file in the compilation if the object format supports it.
639+
/// https://learn.microsoft.com/en-us/windows/win32/sbscs/manifest-files-reference
640+
/// Manifest files must have the extension `.manifest`.
641+
/// Can be set regardless of target. The `.manifest` file will be ignored
642+
/// if the target object format does not support embedded manifests.
643+
win32_manifest: ?LazyPath = null,
638644

639645
/// Deprecated; use `main_mod_path`.
640646
main_pkg_path: ?LazyPath = null,
@@ -656,6 +662,7 @@ pub fn addExecutable(b: *Build, options: ExecutableOptions) *Step.Compile {
656662
.use_lld = options.use_lld,
657663
.zig_lib_dir = options.zig_lib_dir orelse b.zig_lib_dir,
658664
.main_mod_path = options.main_mod_path orelse options.main_pkg_path,
665+
.win32_manifest = options.win32_manifest,
659666
});
660667
}
661668

@@ -706,6 +713,12 @@ pub const SharedLibraryOptions = struct {
706713
use_lld: ?bool = null,
707714
zig_lib_dir: ?LazyPath = null,
708715
main_mod_path: ?LazyPath = null,
716+
/// Embed a `.manifest` file in the compilation if the object format supports it.
717+
/// https://learn.microsoft.com/en-us/windows/win32/sbscs/manifest-files-reference
718+
/// Manifest files must have the extension `.manifest`.
719+
/// Can be set regardless of target. The `.manifest` file will be ignored
720+
/// if the target object format does not support embedded manifests.
721+
win32_manifest: ?LazyPath = null,
709722

710723
/// Deprecated; use `main_mod_path`.
711724
main_pkg_path: ?LazyPath = null,
@@ -727,6 +740,7 @@ pub fn addSharedLibrary(b: *Build, options: SharedLibraryOptions) *Step.Compile
727740
.use_lld = options.use_lld,
728741
.zig_lib_dir = options.zig_lib_dir orelse b.zig_lib_dir,
729742
.main_mod_path = options.main_mod_path orelse options.main_pkg_path,
743+
.win32_manifest = options.win32_manifest,
730744
});
731745
}
732746

lib/std/Build/Step/Compile.zig

+26
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,10 @@ vcpkg_bin_path: ?[]const u8 = null,
9898
/// none: Do not use any autodetected include paths.
9999
rc_includes: enum { any, msvc, gnu, none } = .any,
100100

101+
/// (Windows) .manifest file to embed in the compilation
102+
/// Set via options; intended to be read-only after that.
103+
win32_manifest: ?LazyPath = null,
104+
101105
installed_path: ?[]const u8,
102106

103107
/// Base address for an executable image.
@@ -319,6 +323,12 @@ pub const Options = struct {
319323
use_lld: ?bool = null,
320324
zig_lib_dir: ?LazyPath = null,
321325
main_mod_path: ?LazyPath = null,
326+
/// Embed a `.manifest` file in the compilation if the object format supports it.
327+
/// https://learn.microsoft.com/en-us/windows/win32/sbscs/manifest-files-reference
328+
/// Manifest files must have the extension `.manifest`.
329+
/// Can be set regardless of target. The `.manifest` file will be ignored
330+
/// if the target object format does not support embedded manifests.
331+
win32_manifest: ?LazyPath = null,
322332

323333
/// deprecated; use `main_mod_path`.
324334
main_pkg_path: ?LazyPath = null,
@@ -525,6 +535,15 @@ pub fn create(owner: *std.Build, options: Options) *Compile {
525535
lp.addStepDependencies(&self.step);
526536
}
527537

538+
// Only the PE/COFF format has a Resource Table which is where the manifest
539+
// gets embedded, so for any other target the manifest file is just ignored.
540+
if (self.target.getObjectFormat() == .coff) {
541+
if (options.win32_manifest) |lp| {
542+
self.win32_manifest = lp.dupe(self.step.owner);
543+
lp.addStepDependencies(&self.step);
544+
}
545+
}
546+
528547
if (self.kind == .lib) {
529548
if (self.linkage != null and self.linkage.? == .static) {
530549
self.out_lib_filename = self.out_filename;
@@ -957,6 +976,9 @@ pub fn addCSourceFile(self: *Compile, source: CSourceFile) void {
957976
source.file.addStepDependencies(&self.step);
958977
}
959978

979+
/// Resource files must have the extension `.rc`.
980+
/// Can be called regardless of target. The .rc file will be ignored
981+
/// if the target object format does not support embedded resources.
960982
pub fn addWin32ResourceFile(self: *Compile, source: RcSourceFile) void {
961983
// Only the PE/COFF format has a Resource Table, so for any other target
962984
// the resource file is just ignored.
@@ -1593,6 +1615,10 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
15931615
}
15941616
}
15951617

1618+
if (self.win32_manifest) |manifest_file| {
1619+
try zig_args.append(manifest_file.getPath(b));
1620+
}
1621+
15961622
if (transitive_deps.is_linking_libcpp) {
15971623
try zig_args.append("-lc++");
15981624
}

0 commit comments

Comments
 (0)