goversioninfo: giving a Go binary a real Windows File Properties tab
Golang Microsoft Windows File Properties/Version Info and Icon Resource Generator
At a glance
- What is it?
- goversioninfo is a Go generator that writes a resource.syso file containing Windows version information, an optional icon and an optional manifest, so that `go build` embeds them into the executable and Explorer shows a populated Properties dialog. It is aimed at Go teams shipping Windows binaries that look unfinished because they have no version resource.
- Who is it for?
- Adopt goversioninfo when you ship Windows executables from Go and want a populated File Properties dialog, a real application icon and a version string that matches what your build pipeline already knows. Skip it if you only build for Linux or macOS, since the syso file exists solely to satisfy the Windows resource format, or if you already generate resources through another tool.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 73 days ago.
- What is it written in?
- Mainly Go, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.
Editorial analysis
A syso file is what makes Explorer show the version
goversioninfo is described as a Microsoft Windows File Properties, Version Info and Icon Resource Generator for the Go language. The package creates a syso file which contains Microsoft Windows Version Information and an optional icon.
The mechanism is worth understanding because it explains both the tool's simplicity and its constraints. Go's linker has a convention: if a file named `resource.syso` sits in the same directory as the `main()` function, `go build` picks it up and embeds the Windows resources into the executable. That is why the README describes the output as a file called resource.syso in the same directory as the Go source code.
So the flow is deliberately dull. A generator produces a file with the Windows resource layout, the Go toolchain links it in automatically, and the result is an executable whose Properties dialog shows File version, Product name, Company name and the rest, with an icon in Explorer instead of the default.
For a Go team this solves a specific and very visible gap. A Go binary built without a resource file works perfectly but looks anonymous, and the version information in the Properties tab is often what an administrator or a support engineer reads first. The README also notes that Go will embed an optional manifest, which is where Windows compatibility declarations and, in the past, Common Controls for the visual styles live.
Install once, then let go generate do the work
The tool installs with a single command:
go install github.com/josephspurrier/goversioninfo/cmd/goversioninfo@latestThe workflow then has three parts. You copy `testdata/resource/versioninfo.json` into your working directory and modify it with your own settings. You add a generate directive to the top of your Go source, where the icon and manifest flags are optional but can also be specified in the versioninfo.json file:
//go:generate goversioninfo -icon=testdata/resource/icon.ico -manifest=testdata/resource/goversioninfo.exe.manifestAnd then you run the Go commands in this order, so goversioninfo creates resource.syso in the same directory as the Go source code:
go generate
go buildThe order matters and it matters for a reason that is easy to get wrong. `go generate` runs the directive, which writes the syso file. `go build` then links that file in. If the syso file is stale, the build succeeds and the binary carries the old version, which is a silent failure mode rather than an error.
Putting the directive in the source file rather than in a build script is the idiomatic choice, because `go generate` is then part of the same workflow as `go build` and runs on the same directory. The tradeoff is that the resource file has to be regenerated whenever the version changes, which is a step that a CI pipeline has to remember.
Architecture follows GOARCH unless you override it
The `-64` and `-arm` flags default based on the `GOARCH` environment variable, falling back to the host architecture if `GOARCH` is not set. The README states the consequence: `go generate` will automatically produce a resource file matching the target architecture without needing to pass `-64` or `-arm` explicitly, and you can still override the defaults by passing the flags on the command line.
This is a real improvement over the usual approach, where a checked-in resource file has to match the target architecture or the build fails with a linker error about a mismatched object file. Deriving the architecture from the environment removes the most common way this breaks.
It also means the generated file is architecture specific, which has a consequence for build hygiene. A single generated `resource.syso` cannot satisfy a build matrix that produces both an amd64 and an arm64 binary, because the resource layout differs. The tool therefore has a separate flag for that case, `-platform-specific`, which outputs i386 and amd64 named resource.syso files and ignores `-o`.
That flag also shows how the tool models the problem. Rather than emitting one file for many targets, it names the output per architecture so the Go toolchain can find the right one in a per-architecture source directory, which is the layout the Go project convention expects.
One version number, written in two places
Windows version resources carry the version twice, and the README explains the two representations. The `FixedFileInfo` and `StringFileInfo` sections of the JSON config both contain `FileVersion` and `ProductVersion` fields. `FixedFileInfo` stores them as structured numeric components, `Major`, `Minor`, `Patch` and `Build`, while `StringFileInfo` stores them as free-form strings.
The tool resolves the duplication for you when `Build()` is called. If `FixedFileInfo` has a version set but the corresponding `StringFileInfo` string is empty, the string is generated, for example `"2.0.0.0"`. If `StringFileInfo` has a parseable version string but the corresponding `FixedFileInfo` fields are all zero, the struct is populated from the string. If both are already set, neither is modified, but a warning is logged if the numeric components do not match. And if a `StringFileInfo` version string cannot be parsed as a version number, for example `x.y.z` or `x.y.z.w`, a warning is logged.
The result is that you only need to specify version information in one place, and providing just `FixedFileInfo` is sufficient:
{
"FixedFileInfo": {
"FileVersion": {
"Major": 2,
"Minor": 0,
"Patch": 0,
"Build": 0
},
"ProductVersion": {
"Major": 2,
"Minor": 0,
"Patch": 0,
"Build": 0
}
}
}The mismatch warning is the useful part. Two versions that disagree is exactly the bug that shows up in a support ticket, and here it is reported at generation time rather than discovered by a user looking at the Properties tab.
Two different icons for two different places
Windows shows an icon in more than one place, and goversioninfo treats them separately.
By default, Windows uses the system default icon for the window title bar. To set a custom window icon, goversioninfo embeds an icon resource with the `IDI_APPLICATION` resource ID, 32512, which is the icon that Win32 applications load via `LoadIcon(hInstance, IDI_APPLICATION)`.
The configuration uses `IconPath` for the icon stored as a file resource and `ApplicationIconPath` for the title bar icon. When `IconPath` is set and `ApplicationIconPath` is not, the application icon defaults to the same file as `IconPath`. Setting them separately looks like this:
{
"IconPath": "icons/main.ico",
"ApplicationIconPath": "icons/small.ico"
}The same split exists on the command line, where the `-application-icon` flag takes an icon for `IDI_APPLICATION` and defaults to `-icon` if unset:
goversioninfo -icon=icons/main.ico -application-icon=icons/small.icoIf neither `IconPath` nor `ApplicationIconPath` is set, no application icon is embedded at all.
The two are separate because the constraints are different. The Explorer file icon can be a multi-resolution `.ico` with several sizes, while the title bar icon is drawn at 16 pixels and benefits from a simplified image. Using one file for both is the usual compromise, and the tool lets you avoid it.
The `-icon` flag also accepts a comma separated list of icon file names, which is how a multi-size ico file or several variants are supplied.
Reading the version back from inside the program
Most of the flags map one to one onto the strings in the Windows version resource, and the README gives the complete list. The `-company`, `-copyright`, `-description`, `-internal-name`, `-original-name`, `-private-build`, `-product-name`, `-product-version`, `-special-build` and `-trademark` flags each set a `StringFileInfo` entry, with `-comment` for `StringFileInfo.Comments`, `-file-version` and `-product-version` for the version strings, `-charset` for the charset ID and `-translation` for the translation ID.
Two flags stand out because they do something different. The `-example` flag dumps out an example versioninfo.json to stdout, which is the quickest way to see the full schema without copying the testdata file. And `-skip-versioninfo` skips version info reading when true, which allows setting just an icon.
The `-gofile` flag is the one with the most interesting reach. It generates a Go file to access version information internally, with `-gofilepackage` choosing the output package name, `main` by default, and requiring the `gofile` parameter. That lets a running program report its own version, for example in a `--version` flag or in a user agent string, without shelling out to read the executable's own resource table.
The output file name defaults to `resource.syso` through the `-o` flag, which is the name the Go linker looks for.
One naming trap is documented at the end of the README. In PowerShell, the version components are named differently than in versioninfo.json, where `FileMajorPart` corresponds to `FileVersion.Major`, `FileMinorPart` to `FileVersion.Minor`, `FileBuildPart` to `FileVersion.Patch` and `FilePrivatePart` to `FileVersion.B`. Scripts that read the resource through PowerShell will use the first set of names.
MIT terms, two dependencies, and a small Go module
The licence field on the repository says MIT, and the tree carries a LICENSE file. MIT is permissive with no copyleft obligation, which is the right shape for a build-time generator that most users never vendor.
The dependency footprint is unusually small, and that matters for a tool that runs inside your build. The go.mod file declares `go 1.19`, so the module works with any reasonably modern Go toolchain, and it requires `github.com/akavel/rsrc` at v0.10.2 and `github.com/stretchr/testify` at v1.6.1, with `github.com/davecgh/go-spew`, `github.com/pmezard/go-difflib` and `gopkg.in/yaml.v3` as indirect dependencies. rsrc is the piece that actually produces the Windows resource object, and testify is the test assertion library.
The repository layout is flat and readable: `goversioninfo.go` holds the core, `structbuild.go` the version struct handling, `icon.go` the icon resource work, `cli.go` the command line, with `cmd/` holding the command entry point, `testdata/` the fixtures including the example versioninfo.json and the manifest, and `example_test.go` and `goversioninfo_test.go` the tests. The `Makefile` documents the two operations it supports:
make test-go
make test-integrationThe integration target builds the application into `bin/` and then runs `testdata/bash/build.sh` with that directory on the PATH, so the generated resource is exercised by an actual build rather than only unit tested.
On maintenance, the tags are v1.7.0 and v1.6.0 both on 2026-05-11 and v1.5.0 on 2025-04-02, with the last push on 2026-08-11. The version numbers are still below 2.0, so the flag names and JSON schema are the things to check when you upgrade, and the CHANGELOG is the place to see them.
Editorial conclusion
Adopt goversioninfo when you ship Windows executables from Go and want a populated File Properties dialog, a real application icon and a version string that matches what your build pipeline already knows. Skip it if you only build for Linux or macOS, since the syso file exists solely to satisfy the Windows resource format, or if you already generate resources through another tool. Verify first that the syso file is regenerated whenever the version changes, since Go embeds it at link time and a stale file silently ships the old version, and confirm the architecture matches your target with the `-64` and `-arm` flags or the GOARCH detection. The licence field on the repository says MIT, the last push was on 2026-08-11, and the current release is v1.7.0.
Frequently asked questions
How do I add Windows version information to a Go binary?
Copy `testdata/resource/versioninfo.json` into your working directory and modify it, add a `//go:generate goversioninfo` directive to your Go source, then run `go generate` followed by `go build`. The generator writes resource.syso next to the main function and the Go linker embeds it automatically.
How do I install goversioninfo?
The README gives `go install github.com/josephspurrier/goversioninfo/cmd/goversioninfo@latest`. The module itself declares `go 1.19` and depends on `github.com/akavel/rsrc` and `github.com/stretchr/testify`.
Does goversioninfo handle ARM and 64-bit builds?
Yes. The `-64` and `-arm` flags default based on the GOARCH environment variable, falling back to the host architecture, so `go generate` produces a resource file matching the target architecture. The `-platform-specific` flag outputs i386 and amd64 named resource.syso files and ignores `-o`.
Do I have to specify the version in both FixedFileInfo and StringFileInfo?
No. When Build() is called, missing fields are filled in automatically: a set FixedFileInfo generates the matching StringFileInfo string, and a parseable StringFileInfo string populates a zeroed FixedFileInfo. If both are set and disagree, a warning is logged.
How do I set a separate window title bar icon?
Set `ApplicationIconPath` in the JSON configuration, or pass the `-application-icon` flag on the command line. When `IconPath` is set and `ApplicationIconPath` is not, the title bar icon defaults to the same file, and the tool embeds it with the `IDI_APPLICATION` resource ID of 32512.
Can a Go program report its own version?
Yes. The `-gofile` flag generates a Go file to access version information internally, with `-gofilepackage` choosing the package name and defaulting to `main`. The `-example` flag also dumps an example versioninfo.json to stdout, and `-skip-versioninfo` allows setting just an icon.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/josephspurrier-goversioninfo)Was this useful?