authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2023-10-15 19:47:27-07:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2023-10-15 19:47:27-07:00
logca690ffa22ab4463e53b547d109a2b2081771e05
tree3557c38c0f5b1c792a97fc6a1838bd9952b97c0e
parentebde525ccee41a8eefbb868cf4df756f1a937dd2
parentb51147889f1c44d380818977a4dbefebffb8d91f
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #17448 from squeek502/win32-manifest

Add preliminary support for Windows `.manifest` files

4 files changed, 231 insertions(+), 32 deletions(-)

lib/std/Build.zig+14
...@@ -635,6 +635,12 @@ pub const ExecutableOptions = struct {...@@ -635,6 +635,12 @@ pub const ExecutableOptions = struct {
635 use_lld: ?bool = null,635 use_lld: ?bool = null,
636 zig_lib_dir: ?LazyPath = null,636 zig_lib_dir: ?LazyPath = null,
637 main_mod_path: ?LazyPath = null,637 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
639 /// Deprecated; use `main_mod_path`.645 /// Deprecated; use `main_mod_path`.
640 main_pkg_path: ?LazyPath = null,646 main_pkg_path: ?LazyPath = null,
...@@ -656,6 +662,7 @@ pub fn addExecutable(b: *Build, options: ExecutableOptions) *Step.Compile {...@@ -656,6 +662,7 @@ pub fn addExecutable(b: *Build, options: ExecutableOptions) *Step.Compile {
656 .use_lld = options.use_lld,662 .use_lld = options.use_lld,
657 .zig_lib_dir = options.zig_lib_dir orelse b.zig_lib_dir,663 .zig_lib_dir = options.zig_lib_dir orelse b.zig_lib_dir,
658 .main_mod_path = options.main_mod_path orelse options.main_pkg_path,664 .main_mod_path = options.main_mod_path orelse options.main_pkg_path,
665 .win32_manifest = options.win32_manifest,
659 });666 });
660}667}
661668
...@@ -706,6 +713,12 @@ pub const SharedLibraryOptions = struct {...@@ -706,6 +713,12 @@ pub const SharedLibraryOptions = struct {
706 use_lld: ?bool = null,713 use_lld: ?bool = null,
707 zig_lib_dir: ?LazyPath = null,714 zig_lib_dir: ?LazyPath = null,
708 main_mod_path: ?LazyPath = null,715 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
710 /// Deprecated; use `main_mod_path`.723 /// Deprecated; use `main_mod_path`.
711 main_pkg_path: ?LazyPath = null,724 main_pkg_path: ?LazyPath = null,
...@@ -727,6 +740,7 @@ pub fn addSharedLibrary(b: *Build, options: SharedLibraryOptions) *Step.Compile...@@ -727,6 +740,7 @@ pub fn addSharedLibrary(b: *Build, options: SharedLibraryOptions) *Step.Compile
727 .use_lld = options.use_lld,740 .use_lld = options.use_lld,
728 .zig_lib_dir = options.zig_lib_dir orelse b.zig_lib_dir,741 .zig_lib_dir = options.zig_lib_dir orelse b.zig_lib_dir,
729 .main_mod_path = options.main_mod_path orelse options.main_pkg_path,742 .main_mod_path = options.main_mod_path orelse options.main_pkg_path,
743 .win32_manifest = options.win32_manifest,
730 });744 });
731}745}
732746
lib/std/Build/Step/Compile.zig+26
...@@ -98,6 +98,10 @@ vcpkg_bin_path: ?[]const u8 = null,...@@ -98,6 +98,10 @@ vcpkg_bin_path: ?[]const u8 = null,
98/// none: Do not use any autodetected include paths.98/// none: Do not use any autodetected include paths.
99rc_includes: enum { any, msvc, gnu, none } = .any,99rc_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.
103win32_manifest: ?LazyPath = null,
104
101installed_path: ?[]const u8,105installed_path: ?[]const u8,
102106
103/// Base address for an executable image.107/// Base address for an executable image.
...@@ -319,6 +323,12 @@ pub const Options = struct {...@@ -319,6 +323,12 @@ pub const Options = struct {
319 use_lld: ?bool = null,323 use_lld: ?bool = null,
320 zig_lib_dir: ?LazyPath = null,324 zig_lib_dir: ?LazyPath = null,
321 main_mod_path: ?LazyPath = null,325 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
323 /// deprecated; use `main_mod_path`.333 /// deprecated; use `main_mod_path`.
324 main_pkg_path: ?LazyPath = null,334 main_pkg_path: ?LazyPath = null,
...@@ -525,6 +535,15 @@ pub fn create(owner: *std.Build, options: Options) *Compile {...@@ -525,6 +535,15 @@ pub fn create(owner: *std.Build, options: Options) *Compile {
525 lp.addStepDependencies(&self.step);535 lp.addStepDependencies(&self.step);
526 }536 }
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
528 if (self.kind == .lib) {547 if (self.kind == .lib) {
529 if (self.linkage != null and self.linkage.? == .static) {548 if (self.linkage != null and self.linkage.? == .static) {
530 self.out_lib_filename = self.out_filename;549 self.out_lib_filename = self.out_filename;
...@@ -957,6 +976,9 @@ pub fn addCSourceFile(self: *Compile, source: CSourceFile) void {...@@ -957,6 +976,9 @@ pub fn addCSourceFile(self: *Compile, source: CSourceFile) void {
957 source.file.addStepDependencies(&self.step);976 source.file.addStepDependencies(&self.step);
958}977}
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.
960pub fn addWin32ResourceFile(self: *Compile, source: RcSourceFile) void {982pub fn addWin32ResourceFile(self: *Compile, source: RcSourceFile) void {
961 // Only the PE/COFF format has a Resource Table, so for any other target983 // Only the PE/COFF format has a Resource Table, so for any other target
962 // the resource file is just ignored.984 // the resource file is just ignored.
...@@ -1593,6 +1615,10 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {...@@ -1593,6 +1615,10 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
1593 }1615 }
1594 }1616 }
15951617
1618 if (self.win32_manifest) |manifest_file| {
1619 try zig_args.append(manifest_file.getPath(b));
1620 }
1621
1596 if (transitive_deps.is_linking_libcpp) {1622 if (transitive_deps.is_linking_libcpp) {
1597 try zig_args.append("-lc++");1623 try zig_args.append("-lc++");
1598 }1624 }
src/Compilation.zig+173-32
...@@ -358,7 +358,10 @@ pub const CObject = struct {...@@ -358,7 +358,10 @@ pub const CObject = struct {
358358
359pub const Win32Resource = struct {359pub const Win32Resource = struct {
360 /// Relative to cwd. Owned by arena.360 /// Relative to cwd. Owned by arena.
361 src: RcSourceFile,361 src: union(enum) {
362 rc: RcSourceFile,
363 manifest: []const u8,
364 },
362 status: union(enum) {365 status: union(enum) {
363 new,366 new,
364 success: struct {367 success: struct {
...@@ -582,6 +585,7 @@ pub const InitOptions = struct {...@@ -582,6 +585,7 @@ pub const InitOptions = struct {
582 symbol_wrap_set: std.StringArrayHashMapUnmanaged(void) = .{},585 symbol_wrap_set: std.StringArrayHashMapUnmanaged(void) = .{},
583 c_source_files: []const CSourceFile = &[0]CSourceFile{},586 c_source_files: []const CSourceFile = &[0]CSourceFile{},
584 rc_source_files: []const RcSourceFile = &[0]RcSourceFile{},587 rc_source_files: []const RcSourceFile = &[0]RcSourceFile{},
588 manifest_file: ?[]const u8 = null,
585 rc_includes: RcIncludes = .any,589 rc_includes: RcIncludes = .any,
586 link_objects: []LinkObject = &[0]LinkObject{},590 link_objects: []LinkObject = &[0]LinkObject{},
587 framework_dirs: []const []const u8 = &[0][]const u8{},591 framework_dirs: []const []const u8 = &[0][]const u8{},
...@@ -1749,16 +1753,26 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {...@@ -1749,16 +1753,26 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
1749 comp.c_object_table.putAssumeCapacityNoClobber(c_object, {});1753 comp.c_object_table.putAssumeCapacityNoClobber(c_object, {});
1750 }1754 }
17511755
1752 // Add a `Win32Resource` for each `rc_source_files`.1756 // Add a `Win32Resource` for each `rc_source_files` and one for `manifest_file`.
1753 if (!build_options.only_core_functionality) {1757 if (!build_options.only_core_functionality) {
1754 try comp.win32_resource_table.ensureTotalCapacity(gpa, options.rc_source_files.len);1758 try comp.win32_resource_table.ensureTotalCapacity(gpa, options.rc_source_files.len + @intFromBool(options.manifest_file != null));
1755 for (options.rc_source_files) |rc_source_file| {1759 for (options.rc_source_files) |rc_source_file| {
1756 const win32_resource = try gpa.create(Win32Resource);1760 const win32_resource = try gpa.create(Win32Resource);
1757 errdefer gpa.destroy(win32_resource);1761 errdefer gpa.destroy(win32_resource);
17581762
1759 win32_resource.* = .{1763 win32_resource.* = .{
1760 .status = .{ .new = {} },1764 .status = .{ .new = {} },
1761 .src = rc_source_file,1765 .src = .{ .rc = rc_source_file },
1766 };
1767 comp.win32_resource_table.putAssumeCapacityNoClobber(win32_resource, {});
1768 }
1769 if (options.manifest_file) |manifest_path| {
1770 const win32_resource = try gpa.create(Win32Resource);
1771 errdefer gpa.destroy(win32_resource);
1772
1773 win32_resource.* = .{
1774 .status = .{ .new = {} },
1775 .src = .{ .manifest = manifest_path },
1762 };1776 };
1763 comp.win32_resource_table.putAssumeCapacityNoClobber(win32_resource, {});1777 comp.win32_resource_table.putAssumeCapacityNoClobber(win32_resource, {});
1764 }1778 }
...@@ -2477,8 +2491,15 @@ fn addNonIncrementalStuffToCacheManifest(comp: *Compilation, man: *Cache.Manifes...@@ -2477,8 +2491,15 @@ fn addNonIncrementalStuffToCacheManifest(comp: *Compilation, man: *Cache.Manifes
24772491
2478 if (!build_options.only_core_functionality) {2492 if (!build_options.only_core_functionality) {
2479 for (comp.win32_resource_table.keys()) |key| {2493 for (comp.win32_resource_table.keys()) |key| {
2480 _ = try man.addFile(key.src.src_path, null);2494 switch (key.src) {
2481 man.hash.addListOfBytes(key.src.extra_flags);2495 .rc => |rc_src| {
2496 _ = try man.addFile(rc_src.src_path, null);
2497 man.hash.addListOfBytes(rc_src.extra_flags);
2498 },
2499 .manifest => |manifest_path| {
2500 _ = try man.addFile(manifest_path, null);
2501 },
2502 }
2482 }2503 }
2483 }2504 }
24842505
...@@ -4172,7 +4193,10 @@ fn reportRetryableWin32ResourceError(...@@ -4172,7 +4193,10 @@ fn reportRetryableWin32ResourceError(
4172 try bundle.addRootErrorMessage(.{4193 try bundle.addRootErrorMessage(.{
4173 .msg = try bundle.printString("{s}", .{@errorName(err)}),4194 .msg = try bundle.printString("{s}", .{@errorName(err)}),
4174 .src_loc = try bundle.addSourceLocation(.{4195 .src_loc = try bundle.addSourceLocation(.{
4175 .src_path = try bundle.addString(win32_resource.src.src_path),4196 .src_path = try bundle.addString(switch (win32_resource.src) {
4197 .rc => |rc_src| rc_src.src_path,
4198 .manifest => |manifest_src| manifest_src,
4199 }),
4176 .line = 0,4200 .line = 0,
4177 .column = 0,4201 .column = 0,
4178 .span_start = 0,4202 .span_start = 0,
...@@ -4542,7 +4566,17 @@ fn updateWin32Resource(comp: *Compilation, win32_resource: *Win32Resource, win32...@@ -4542,7 +4566,17 @@ fn updateWin32Resource(comp: *Compilation, win32_resource: *Win32Resource, win32
4542 const tracy_trace = trace(@src());4566 const tracy_trace = trace(@src());
4543 defer tracy_trace.end();4567 defer tracy_trace.end();
45444568
4545 log.debug("updating win32 resource: {s}", .{win32_resource.src.src_path});4569 const src_path = switch (win32_resource.src) {
4570 .rc => |rc_src| rc_src.src_path,
4571 .manifest => |src_path| src_path,
4572 };
4573 const src_basename = std.fs.path.basename(src_path);
4574
4575 log.debug("updating win32 resource: {s}", .{src_path});
4576
4577 var arena_allocator = std.heap.ArenaAllocator.init(comp.gpa);
4578 defer arena_allocator.deinit();
4579 const arena = arena_allocator.allocator();
45464580
4547 if (win32_resource.clearStatus(comp.gpa)) {4581 if (win32_resource.clearStatus(comp.gpa)) {
4548 // There was previous failure.4582 // There was previous failure.
...@@ -4553,24 +4587,113 @@ fn updateWin32Resource(comp: *Compilation, win32_resource: *Win32Resource, win32...@@ -4553,24 +4587,113 @@ fn updateWin32Resource(comp: *Compilation, win32_resource: *Win32Resource, win32
4553 _ = comp.failed_win32_resources.swapRemove(win32_resource);4587 _ = comp.failed_win32_resources.swapRemove(win32_resource);
4554 }4588 }
45554589
4590 win32_resource_prog_node.activate();
4591 var child_progress_node = win32_resource_prog_node.start(src_basename, 0);
4592 child_progress_node.activate();
4593 defer child_progress_node.end();
4594
4556 var man = comp.obtainWin32ResourceCacheManifest();4595 var man = comp.obtainWin32ResourceCacheManifest();
4557 defer man.deinit();4596 defer man.deinit();
45584597
4559 _ = try man.addFile(win32_resource.src.src_path, null);4598 // For .manifest files, we ultimately just want to generate a .res with
4560 man.hash.addListOfBytes(win32_resource.src.extra_flags);4599 // the XML data as a RT_MANIFEST resource. This means we can skip preprocessing,
4600 // include paths, CLI options, etc.
4601 if (win32_resource.src == .manifest) {
4602 _ = try man.addFile(src_path, null);
45614603
4562 var arena_allocator = std.heap.ArenaAllocator.init(comp.gpa);4604 const res_basename = try std.fmt.allocPrint(arena, "{s}.res", .{src_basename});
4563 defer arena_allocator.deinit();
4564 const arena = arena_allocator.allocator();
45654605
4566 const rc_basename = std.fs.path.basename(win32_resource.src.src_path);4606 const digest = if (try man.hit()) man.final() else blk: {
4607 // The digest only depends on the .manifest file, so we can
4608 // get the digest now and write the .res directly to the cache
4609 const digest = man.final();
45674610
4568 win32_resource_prog_node.activate();4611 const o_sub_path = try std.fs.path.join(arena, &.{ "o", &digest });
4569 var child_progress_node = win32_resource_prog_node.start(rc_basename, 0);4612 var o_dir = try comp.local_cache_directory.handle.makeOpenPath(o_sub_path, .{});
4570 child_progress_node.activate();4613 defer o_dir.close();
4571 defer child_progress_node.end();
45724614
4573 const rc_basename_noext = rc_basename[0 .. rc_basename.len - std.fs.path.extension(rc_basename).len];4615 var output_file = o_dir.createFile(res_basename, .{}) catch |err| {
4616 const output_file_path = try comp.local_cache_directory.join(arena, &.{ o_sub_path, res_basename });
4617 return comp.failWin32Resource(win32_resource, "failed to create output file '{s}': {s}", .{ output_file_path, @errorName(err) });
4618 };
4619 var output_file_closed = false;
4620 defer if (!output_file_closed) output_file.close();
4621
4622 var diagnostics = resinator.errors.Diagnostics.init(arena);
4623 defer diagnostics.deinit();
4624
4625 var output_buffered_stream = std.io.bufferedWriter(output_file.writer());
4626
4627 // In .rc files, a " within a quoted string is escaped as ""
4628 const fmtRcEscape = struct {
4629 fn formatRcEscape(bytes: []const u8, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void {
4630 _ = fmt;
4631 _ = options;
4632 for (bytes) |byte| switch (byte) {
4633 '"' => try writer.writeAll("\"\""),
4634 '\\' => try writer.writeAll("\\\\"),
4635 else => try writer.writeByte(byte),
4636 };
4637 }
4638
4639 pub fn fmtRcEscape(bytes: []const u8) std.fmt.Formatter(formatRcEscape) {
4640 return .{ .data = bytes };
4641 }
4642 }.fmtRcEscape;
4643
4644 // 1 is CREATEPROCESS_MANIFEST_RESOURCE_ID which is the default ID used for RT_MANIFEST resources
4645 // 24 is RT_MANIFEST
4646 const input = try std.fmt.allocPrint(arena, "1 24 \"{s}\"", .{fmtRcEscape(src_path)});
4647
4648 resinator.compile.compile(arena, input, output_buffered_stream.writer(), .{
4649 .cwd = std.fs.cwd(),
4650 .diagnostics = &diagnostics,
4651 .ignore_include_env_var = true,
4652 .default_code_page = .utf8,
4653 }) catch |err| switch (err) {
4654 error.ParseError, error.CompileError => {
4655 // Delete the output file on error
4656 output_file.close();
4657 output_file_closed = true;
4658 // Failing to delete is not really a big deal, so swallow any errors
4659 o_dir.deleteFile(res_basename) catch {
4660 const output_file_path = try comp.local_cache_directory.join(arena, &.{ o_sub_path, res_basename });
4661 log.warn("failed to delete '{s}': {s}", .{ output_file_path, @errorName(err) });
4662 };
4663 return comp.failWin32ResourceCompile(win32_resource, input, &diagnostics, null);
4664 },
4665 else => |e| return e,
4666 };
4667
4668 try output_buffered_stream.flush();
4669
4670 break :blk digest;
4671 };
4672
4673 if (man.have_exclusive_lock) {
4674 man.writeManifest() catch |err| {
4675 log.warn("failed to write cache manifest when compiling '{s}': {s}", .{ src_path, @errorName(err) });
4676 };
4677 }
4678
4679 win32_resource.status = .{
4680 .success = .{
4681 .res_path = try comp.local_cache_directory.join(comp.gpa, &[_][]const u8{
4682 "o", &digest, res_basename,
4683 }),
4684 .lock = man.toOwnedLock(),
4685 },
4686 };
4687 return;
4688 }
4689
4690 // We now know that we're compiling an .rc file
4691 const rc_src = win32_resource.src.rc;
4692
4693 _ = try man.addFile(rc_src.src_path, null);
4694 man.hash.addListOfBytes(rc_src.extra_flags);
4695
4696 const rc_basename_noext = src_basename[0 .. src_basename.len - std.fs.path.extension(src_basename).len];
45744697
4575 const digest = if (try man.hit()) man.final() else blk: {4698 const digest = if (try man.hit()) man.final() else blk: {
4576 const rcpp_filename = try std.fmt.allocPrint(arena, "{s}.rcpp", .{rc_basename_noext});4699 const rcpp_filename = try std.fmt.allocPrint(arena, "{s}.rcpp", .{rc_basename_noext});
...@@ -4586,11 +4709,11 @@ fn updateWin32Resource(comp: *Compilation, win32_resource: *Win32Resource, win32...@@ -4586,11 +4709,11 @@ fn updateWin32Resource(comp: *Compilation, win32_resource: *Win32Resource, win32
4586 const out_res_path = try comp.tmpFilePath(arena, res_filename);4709 const out_res_path = try comp.tmpFilePath(arena, res_filename);
45874710
4588 var options = options: {4711 var options = options: {
4589 var resinator_args = try std.ArrayListUnmanaged([]const u8).initCapacity(comp.gpa, win32_resource.src.extra_flags.len + 4);4712 var resinator_args = try std.ArrayListUnmanaged([]const u8).initCapacity(comp.gpa, rc_src.extra_flags.len + 4);
4590 defer resinator_args.deinit(comp.gpa);4713 defer resinator_args.deinit(comp.gpa);
45914714
4592 resinator_args.appendAssumeCapacity(""); // dummy 'process name' arg4715 resinator_args.appendAssumeCapacity(""); // dummy 'process name' arg
4593 resinator_args.appendSliceAssumeCapacity(win32_resource.src.extra_flags);4716 resinator_args.appendSliceAssumeCapacity(rc_src.extra_flags);
4594 resinator_args.appendSliceAssumeCapacity(&.{ "--", out_rcpp_path, out_res_path });4717 resinator_args.appendSliceAssumeCapacity(&.{ "--", out_rcpp_path, out_res_path });
45954718
4596 var cli_diagnostics = resinator.cli.Diagnostics.init(comp.gpa);4719 var cli_diagnostics = resinator.cli.Diagnostics.init(comp.gpa);
...@@ -4619,7 +4742,7 @@ fn updateWin32Resource(comp: *Compilation, win32_resource: *Win32Resource, win32...@@ -4619,7 +4742,7 @@ fn updateWin32Resource(comp: *Compilation, win32_resource: *Win32Resource, win32
4619 .nostdinc = false, // handled by addCCArgs4742 .nostdinc = false, // handled by addCCArgs
4620 });4743 });
46214744
4622 try argv.append(win32_resource.src.src_path);4745 try argv.append(rc_src.src_path);
4623 try argv.appendSlice(&[_][]const u8{4746 try argv.appendSlice(&[_][]const u8{
4624 "-o",4747 "-o",
4625 out_rcpp_path,4748 out_rcpp_path,
...@@ -4693,7 +4816,7 @@ fn updateWin32Resource(comp: *Compilation, win32_resource: *Win32Resource, win32...@@ -4693,7 +4816,7 @@ fn updateWin32Resource(comp: *Compilation, win32_resource: *Win32Resource, win32
4693 },4816 },
4694 };4817 };
46954818
4696 var mapping_results = try resinator.source_mapping.parseAndRemoveLineCommands(arena, full_input, full_input, .{ .initial_filename = win32_resource.src.src_path });4819 var mapping_results = try resinator.source_mapping.parseAndRemoveLineCommands(arena, full_input, full_input, .{ .initial_filename = rc_src.src_path });
4697 defer mapping_results.mappings.deinit(arena);4820 defer mapping_results.mappings.deinit(arena);
46984821
4699 var final_input = resinator.comments.removeComments(mapping_results.result, mapping_results.result, &mapping_results.mappings);4822 var final_input = resinator.comments.removeComments(mapping_results.result, mapping_results.result, &mapping_results.mappings);
...@@ -4776,7 +4899,7 @@ fn updateWin32Resource(comp: *Compilation, win32_resource: *Win32Resource, win32...@@ -4776,7 +4899,7 @@ fn updateWin32Resource(comp: *Compilation, win32_resource: *Win32Resource, win32
4776 // the contents were the same, we hit the cache but the manifest is dirty and we need to update4899 // the contents were the same, we hit the cache but the manifest is dirty and we need to update
4777 // it to prevent doing a full file content comparison the next time around.4900 // it to prevent doing a full file content comparison the next time around.
4778 man.writeManifest() catch |err| {4901 man.writeManifest() catch |err| {
4779 log.warn("failed to write cache manifest when compiling '{s}': {s}", .{ win32_resource.src.src_path, @errorName(err) });4902 log.warn("failed to write cache manifest when compiling '{s}': {s}", .{ rc_src.src_path, @errorName(err) });
4780 };4903 };
4781 }4904 }
47824905
...@@ -5114,7 +5237,7 @@ pub fn addCCArgs(...@@ -5114,7 +5237,7 @@ pub fn addCCArgs(
5114 try argv.append("-fno-unwind-tables");5237 try argv.append("-fno-unwind-tables");
5115 }5238 }
5116 },5239 },
5117 .shared_library, .ll, .bc, .unknown, .static_library, .object, .def, .zig, .res => {},5240 .shared_library, .ll, .bc, .unknown, .static_library, .object, .def, .zig, .res, .manifest => {},
5118 .assembly, .assembly_with_cpp => {5241 .assembly, .assembly_with_cpp => {
5119 if (ext == .assembly_with_cpp) {5242 if (ext == .assembly_with_cpp) {
5120 const c_headers_dir = try std.fs.path.join(arena, &[_][]const u8{ comp.zig_lib_directory.path.?, "include" });5243 const c_headers_dir = try std.fs.path.join(arena, &[_][]const u8{ comp.zig_lib_directory.path.?, "include" });
...@@ -5340,7 +5463,10 @@ fn failWin32Resource(comp: *Compilation, win32_resource: *Win32Resource, comptim...@@ -5340,7 +5463,10 @@ fn failWin32Resource(comp: *Compilation, win32_resource: *Win32Resource, comptim
5340 try bundle.addRootErrorMessage(.{5463 try bundle.addRootErrorMessage(.{
5341 .msg = try bundle.printString(format, args),5464 .msg = try bundle.printString(format, args),
5342 .src_loc = try bundle.addSourceLocation(.{5465 .src_loc = try bundle.addSourceLocation(.{
5343 .src_path = try bundle.addString(win32_resource.src.src_path),5466 .src_path = try bundle.addString(switch (win32_resource.src) {
5467 .rc => |rc_src| rc_src.src_path,
5468 .manifest => |manifest_src| manifest_src,
5469 }),
5344 .line = 0,5470 .line = 0,
5345 .column = 0,5471 .column = 0,
5346 .span_start = 0,5472 .span_start = 0,
...@@ -5381,7 +5507,10 @@ fn failWin32ResourceCli(...@@ -5381,7 +5507,10 @@ fn failWin32ResourceCli(
5381 try bundle.addRootErrorMessage(.{5507 try bundle.addRootErrorMessage(.{
5382 .msg = try bundle.addString("invalid command line option(s)"),5508 .msg = try bundle.addString("invalid command line option(s)"),
5383 .src_loc = try bundle.addSourceLocation(.{5509 .src_loc = try bundle.addSourceLocation(.{
5384 .src_path = try bundle.addString(win32_resource.src.src_path),5510 .src_path = try bundle.addString(switch (win32_resource.src) {
5511 .rc => |rc_src| rc_src.src_path,
5512 .manifest => |manifest_src| manifest_src,
5513 }),
5385 .line = 0,5514 .line = 0,
5386 .column = 0,5515 .column = 0,
5387 .span_start = 0,5516 .span_start = 0,
...@@ -5427,7 +5556,7 @@ fn failWin32ResourceCompile(...@@ -5427,7 +5556,7 @@ fn failWin32ResourceCompile(
5427 win32_resource: *Win32Resource,5556 win32_resource: *Win32Resource,
5428 source: []const u8,5557 source: []const u8,
5429 diagnostics: *resinator.errors.Diagnostics,5558 diagnostics: *resinator.errors.Diagnostics,
5430 mappings: resinator.source_mapping.SourceMappings,5559 opt_mappings: ?resinator.source_mapping.SourceMappings,
5431) SemaError {5560) SemaError {
5432 @setCold(true);5561 @setCold(true);
54335562
...@@ -5451,19 +5580,26 @@ fn failWin32ResourceCompile(...@@ -5451,19 +5580,26 @@ fn failWin32ResourceCompile(
5451 .note => if (cur_err == null) continue,5580 .note => if (cur_err == null) continue,
5452 .err => {},5581 .err => {},
5453 }5582 }
5454 const corresponding_span = mappings.get(err_details.token.line_number);5583 const err_line, const err_filename = blk: {
5455 const corresponding_file = mappings.files.get(corresponding_span.filename_offset);5584 if (opt_mappings) |mappings| {
5585 const corresponding_span = mappings.get(err_details.token.line_number);
5586 const corresponding_file = mappings.files.get(corresponding_span.filename_offset);
5587 const err_line = corresponding_span.start_line;
5588 break :blk .{ err_line, corresponding_file };
5589 } else {
5590 break :blk .{ err_details.token.line_number, "<generated rc>" };
5591 }
5592 };
54565593
5457 const source_line_start = err_details.token.getLineStart(source);5594 const source_line_start = err_details.token.getLineStart(source);
5458 const column = err_details.token.calculateColumn(source, 1, source_line_start);5595 const column = err_details.token.calculateColumn(source, 1, source_line_start);
5459 const err_line = corresponding_span.start_line;
54605596
5461 msg_buf.clearRetainingCapacity();5597 msg_buf.clearRetainingCapacity();
5462 try err_details.render(msg_buf.writer(comp.gpa), source, diagnostics.strings.items);5598 try err_details.render(msg_buf.writer(comp.gpa), source, diagnostics.strings.items);
54635599
5464 const src_loc = src_loc: {5600 const src_loc = src_loc: {
5465 var src_loc: ErrorBundle.SourceLocation = .{5601 var src_loc: ErrorBundle.SourceLocation = .{
5466 .src_path = try bundle.addString(corresponding_file),5602 .src_path = try bundle.addString(err_filename),
5467 .line = @intCast(err_line - 1), // 1-based -> 0-based5603 .line = @intCast(err_line - 1), // 1-based -> 0-based
5468 .column = @intCast(column),5604 .column = @intCast(column),
5469 .span_start = 0,5605 .span_start = 0,
...@@ -5536,6 +5672,7 @@ pub const FileExt = enum {...@@ -5536,6 +5672,7 @@ pub const FileExt = enum {
5536 def,5672 def,
5537 rc,5673 rc,
5538 res,5674 res,
5675 manifest,
5539 unknown,5676 unknown,
55405677
5541 pub fn clangSupportsDepFile(ext: FileExt) bool {5678 pub fn clangSupportsDepFile(ext: FileExt) bool {
...@@ -5553,6 +5690,7 @@ pub const FileExt = enum {...@@ -5553,6 +5690,7 @@ pub const FileExt = enum {
5553 .def,5690 .def,
5554 .rc,5691 .rc,
5555 .res,5692 .res,
5693 .manifest,
5556 .unknown,5694 .unknown,
5557 => false,5695 => false,
5558 };5696 };
...@@ -5577,6 +5715,7 @@ pub const FileExt = enum {...@@ -5577,6 +5715,7 @@ pub const FileExt = enum {
5577 .def => ".def",5715 .def => ".def",
5578 .rc => ".rc",5716 .rc => ".rc",
5579 .res => ".res",5717 .res => ".res",
5718 .manifest => ".manifest",
5580 .unknown => "",5719 .unknown => "",
5581 };5720 };
5582 }5721 }
...@@ -5672,6 +5811,8 @@ pub fn classifyFileExt(filename: []const u8) FileExt {...@@ -5672,6 +5811,8 @@ pub fn classifyFileExt(filename: []const u8) FileExt {
5672 return .rc;5811 return .rc;
5673 } else if (std.ascii.endsWithIgnoreCase(filename, ".res")) {5812 } else if (std.ascii.endsWithIgnoreCase(filename, ".res")) {
5674 return .res;5813 return .res;
5814 } else if (std.ascii.endsWithIgnoreCase(filename, ".manifest")) {
5815 return .manifest;
5675 } else {5816 } else {
5676 return .unknown;5817 return .unknown;
5677 }5818 }
src/main.zig+18
...@@ -938,6 +938,7 @@ fn buildOutputType(...@@ -938,6 +938,7 @@ fn buildOutputType(
938 var rc_source_files = std.ArrayList(Compilation.RcSourceFile).init(arena);938 var rc_source_files = std.ArrayList(Compilation.RcSourceFile).init(arena);
939 var rc_includes: Compilation.RcIncludes = .any;939 var rc_includes: Compilation.RcIncludes = .any;
940 var res_files = std.ArrayList(Compilation.LinkObject).init(arena);940 var res_files = std.ArrayList(Compilation.LinkObject).init(arena);
941 var manifest_file: ?[]const u8 = null;
941 var link_objects = std.ArrayList(Compilation.LinkObject).init(arena);942 var link_objects = std.ArrayList(Compilation.LinkObject).init(arena);
942 var framework_dirs = std.ArrayList([]const u8).init(arena);943 var framework_dirs = std.ArrayList([]const u8).init(arena);
943 var frameworks: std.StringArrayHashMapUnmanaged(Framework) = .{};944 var frameworks: std.StringArrayHashMapUnmanaged(Framework) = .{};
...@@ -1627,6 +1628,11 @@ fn buildOutputType(...@@ -1627,6 +1628,11 @@ fn buildOutputType(
1627 Compilation.classifyFileExt(arg)) {1628 Compilation.classifyFileExt(arg)) {
1628 .object, .static_library, .shared_library => try link_objects.append(.{ .path = arg }),1629 .object, .static_library, .shared_library => try link_objects.append(.{ .path = arg }),
1629 .res => try res_files.append(.{ .path = arg }),1630 .res => try res_files.append(.{ .path = arg }),
1631 .manifest => {
1632 if (manifest_file) |other| {
1633 fatal("only one manifest file can be specified, found '{s}' after '{s}'", .{ arg, other });
1634 } else manifest_file = arg;
1635 },
1630 .assembly, .assembly_with_cpp, .c, .cpp, .h, .ll, .bc, .m, .mm, .cu => {1636 .assembly, .assembly_with_cpp, .c, .cpp, .h, .ll, .bc, .m, .mm, .cu => {
1631 try c_source_files.append(.{1637 try c_source_files.append(.{
1632 .src_path = arg,1638 .src_path = arg,
...@@ -1647,6 +1653,9 @@ fn buildOutputType(...@@ -1647,6 +1653,9 @@ fn buildOutputType(
1647 } else root_src_file = arg;1653 } else root_src_file = arg;
1648 },1654 },
1649 .def, .unknown => {1655 .def, .unknown => {
1656 if (std.ascii.eqlIgnoreCase(".xml", std.fs.path.extension(arg))) {
1657 std.log.warn("embedded manifest files must have the extension '.manifest'", .{});
1658 }
1650 fatal("unrecognized file extension of parameter '{s}'", .{arg});1659 fatal("unrecognized file extension of parameter '{s}'", .{arg});
1651 },1660 },
1652 }1661 }
...@@ -1734,6 +1743,11 @@ fn buildOutputType(...@@ -1734,6 +1743,11 @@ fn buildOutputType(
1734 .path = it.only_arg,1743 .path = it.only_arg,
1735 .must_link = must_link,1744 .must_link = must_link,
1736 }),1745 }),
1746 .manifest => {
1747 if (manifest_file) |other| {
1748 fatal("only one manifest file can be specified, found '{s}' after previously specified manifest '{s}'", .{ it.only_arg, other });
1749 } else manifest_file = it.only_arg;
1750 },
1737 .def => {1751 .def => {
1738 linker_module_definition_file = it.only_arg;1752 linker_module_definition_file = it.only_arg;
1739 },1753 },
...@@ -2601,6 +2615,9 @@ fn buildOutputType(...@@ -2601,6 +2615,9 @@ fn buildOutputType(
2601 try link_objects.append(res_file);2615 try link_objects.append(res_file);
2602 }2616 }
2603 } else {2617 } else {
2618 if (manifest_file != null) {
2619 fatal("manifest file is not allowed unless the target object format is coff (Windows/UEFI)", .{});
2620 }
2604 if (rc_source_files.items.len != 0) {2621 if (rc_source_files.items.len != 0) {
2605 fatal("rc files are not allowed unless the target object format is coff (Windows/UEFI)", .{});2622 fatal("rc files are not allowed unless the target object format is coff (Windows/UEFI)", .{});
2606 }2623 }
...@@ -3418,6 +3435,7 @@ fn buildOutputType(...@@ -3418,6 +3435,7 @@ fn buildOutputType(
3418 .symbol_wrap_set = symbol_wrap_set,3435 .symbol_wrap_set = symbol_wrap_set,
3419 .c_source_files = c_source_files.items,3436 .c_source_files = c_source_files.items,
3420 .rc_source_files = rc_source_files.items,3437 .rc_source_files = rc_source_files.items,
3438 .manifest_file = manifest_file,
3421 .rc_includes = rc_includes,3439 .rc_includes = rc_includes,
3422 .link_objects = link_objects.items,3440 .link_objects = link_objects.items,
3423 .framework_dirs = framework_dirs.items,3441 .framework_dirs = framework_dirs.items,