Zig is now also a Windows resource compiler

Here’s something I wrote about some different use-cases and information on how to use the recently-merged Windows resource compilation powers of the Zig compiler.


This is amazing work, thank you @squeek502 !


Some comments on the article:

  1. Perhaps you could suggest what to do when you have a non-UTF16 .rc file – how do you convert it so that it is accepted by zig rc?
  2. In one of the command-line examples you pass an option as -rcflags /c65001. Would -rcflags c65001 be better / nicer? This looks particularly weird in build.zig’s code, such as .flags = &.{"/c65001"}, where .flags = &.{"c65001"} seems (to me) more natural.
  3. Maybe you could accept - and / to introduce command-line options everywhere? And perhaps even be clever about what was actually used, so that -path /bin/foo is not interpreted as two options, but an option name and a path? I have been bitten in the past by / in the command line…

Anyway, again, wonderful work!

1 Like

It’s a little bit complicated. If the .rc file contains only characters within the Windows-1252 range, then converting to Windows-1252 would be the way to go (Windows-1252 is the default code page). If the .rc file has characters outside of that range, then you’d need to convert to UTF-8 and set the code page to UTF-8 (either via the /c65001 CLI option or via #pragma code_page(65001) in the .rc file).

I’ll likely update the article to include some info along these lines.

Note that -rcflags <flags> -- just passes whatever is in <flags> to the resinator CLI parser, so e.g. -rcflags /c65001 /DUNICODE -- would just pass the flags /c65001 /DUNICODE to resinator. The -rcflags <flags> -- model is based on the similar -cflags <flags> -- used by Zig for passing C compiler flags for .c files (you can use Build.Step.Compile.addCSourceFiles with some flags specified and then zig build --verbose to see -cflags <flags> -- in action).

The argument parsing matches how rc.exe works, so because drop-in compatibility is a goal, there’s not much wiggle room here. Note that any of /, -, or -- are accepted prefixes for all options/flags, though (-- being an accepted prefix is not true of rc.exe, but it doesn’t hurt drop-in compatibility). Another thing to note is that options can be combined together in a single argument, and that spaces are optional between an option and its value. For example, these are all equivalent command line options:

rc.exe /vnlnen-us file.rc
rc.exe /v /n /lnen-us file.rc
rc.exe /v /n /ln en-us file.rc
rc.exe -v -n -ln en-us file.rc

(this set of options means ‘enable verbose mode’, ‘append NUL to string table strings’, and ‘set default language to US English’)

As a fun little aside, I’m pretty proud of how resinator prints error messages for CLI argument errors given how CLI parsing works:


(sadly, these diagnostics will only show up when using zig rc, they won’t be quite as fancy when you’re compiling via zig build-exe or zig build since the Zig error messages are very geared towards source file-based errors)

EDIT: Another thing worth noting: resinator/zig rc tries to be helpful if it thinks an argument parsing failure is due to an absolute path being treated as a CLI option:

> resinator /some/absolute/path/option.rc
<cli>: error: the /s option is unsupported
 ... /some/absolute/path/option.rc
<cli>: error: missing input filename

<cli>: note: if this argument was intended to be the input filename, then -- should be specified in front of it to exclude it from option parsing
 ... /some/absolute/path/option.rc

Awesome to see this come to fruition! Great work!