0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

Goで作るexeファイルにアイコンとかライセンス情報とか git の tag から得たバージョンなどを埋め込んでみる

0
Last updated at Posted at 2025-06-12

これまでのあらすじ

最近、これらに「いいね」をいただいたようなのですが、Go言語による代替ツールがあり、自分は現在そちらを使っているので、内容をアップデートしたいと思います。

josephspurrier/goversioninfo の概要

Go言語で作られたリソース生成ツールです。こちらは

  • RCファイルではなく、お馴染みの JSON ファイルで情報を指定する
  • Windows のプロパティーで参照できる情報(ファイルバージョン・プロダクトバージョンを全部指定できる)
  • アイコンを複数指定することができる(EXEファイルを直接表示する時はデフォルトになるが、ユーザがショートカットを作る際はどれかを選ぶことができる)

など多機能かつ使いやすくなっています。

josephspurrier/goversioninfo の使い方

こんな感じで呼び出します。

go run github.com/hymkor/goversioninfo/cmd/goversioninfo@master
      -icon=nyagos.ico,nyagos32x32.ico,nyagos16x16.ico
      -o nyagos.syso v.json

(スクロールして読みづらいので改行していますが、実際に使う時は1行にしてください)

  • これでアイコンなどのリソース情報を格納した .syso ファイルが生成される
    go build する時のディレクトリ上に置いておくと、取り込まれる
  • アイコンは -icon= オプションで指定する
  • バージョン情報はコマンドラインオプションでも指定できるが、その他もろものライセンス情報などもプロパティーに載せたい場合も多いので、次のように JSON で指定するとよい
v.json
{
	"FixedFileInfo":
	{
		"FileVersion": {
			"Major": 4,
			"Minor": 4,
			"Patch": 17,
			"Build": 0
		},
		"ProductVersion": {
			"Major": 4,
			"Minor": 4,
			"Patch": 17,
			"Build": 0
		},
		"FileFlagsMask": "3f",
		"FileFlags ": "00",
		"FileOS": "040004",
		"FileType": "01",
		"FileSubType": "00"
	},
	"StringFileInfo":
	{
		"Comments": "",
		"CompanyName": "NYAOS.ORG",
		"FileDescription": "Nihongo Yet Another GOing Shell",
		"FileVersion": "4.4.17_0-20-g3a8c748a",
		"InternalName": "",
		"LegalCopyright": "Copyright (C) 2014-2025 HAYAMA_Kaoru",
		"LegalTrademarks": "",
		"OriginalFilename": "NYAGOS.EXE",
		"PrivateBuild": "",
		"ProductName": "Nihongo Yet Another GOing Shell",
		"ProductVersion": "4.4.17_0-20-g3a8c748a",
		"SpecialBuild": ""
	},
	"VarFileInfo":
	{
		"Translation": {
			"LangID": "0411",
			"CharsetID": "04E4"
		}
	}
}

結果の説明

image.png

  • アイコンは左上に出ていますね
  • ファイルバージョンは個別ファイルのバージョン、製品バージョンは一度にリリースするファイルセット全体に対して付けられるバージョンだと考えればよいでしょう
  • バージョンには二つの表現があります
    • 一つは文字列表現。主に人間が目視で確認するためにあり、RC1 とか β とか、そういう語句を付加できます
    • もう一つは整数4つからなる表現。こちらはプログラムが大小関係などを比較する場合に使える形となっています
  • Windows のプロパティー画面では両方表示されるわけではなく、次のように表現が選ばれています
    • ファイルバージョンは整数表現
    • 製品バージョンは文字列表現

git tag との連携

Go言語ではセマンティックバージョンを git のタグで管理するのが主流です。バージョンを二カ所で管理するのは冗長になるので、できればそちらからバージョン情報を取り込んでEXEファイルのリソースへ反映できると便利です。

で、汎用的なものではないんですが、昔、自分のプロダクト用にそういうものを作りました。これは git describe --tags を内部から呼び出して、その情報を埋め込んだ JSON を標準出力に出すという go run 用のスクリプトです。

package main

import (
	"fmt"
	"os"
	"os/exec"
	"regexp"
	"strconv"
	"strings"
)

var jsonTemplate = `{
	"FixedFileInfo":
	{
		"FileVersion": {
			"Major": %d,
			"Minor": %d,
			"Patch": %d,
			"Build": %d
		},
		"ProductVersion": {
			"Major": %d,
			"Minor": %d,
			"Patch": %d,
			"Build": %d
		},
		"FileFlagsMask": "3f",
		"FileFlags ": "00",
		"FileOS": "040004",
		"FileType": "01",
		"FileSubType": "00"
	},
	"StringFileInfo":
	{
		"Comments": "",
		"CompanyName": "NYAOS.ORG",
		"FileDescription": "Nihongo Yet Another GOing Shell",
		"FileVersion": "%s",
		"InternalName": "",
		"LegalCopyright": "Copyright (C) 2014-2025 HAYAMA_Kaoru",
		"LegalTrademarks": "",
		"OriginalFilename": "NYAGOS.EXE",
		"PrivateBuild": "",
		"ProductName": "Nihongo Yet Another GOing Shell",
		"ProductVersion": "%s",
		"SpecialBuild": ""
	},
	"VarFileInfo":
	{
		"Translation": {
			"LangID": "0411",
			"CharsetID": "04E4"
		}
	}
}
`

var de = regexp.MustCompile(`[-\._]`)

func versionStrToNum(versionString string) ([]int, error) {
	v := de.Split(versionString, -1)
	if len(v) < 4 {
		return nil, fmt.Errorf("%s: too short version string", versionString)
	}

	var vn [4]int
	var err error

	if vn[0], err = strconv.Atoi(v[0]); err != nil {
		return nil, fmt.Errorf("%s: invalid major version", versionString)
	}
	if vn[1], err = strconv.Atoi(v[1]); err != nil {
		return nil, fmt.Errorf("%s: invalid minor version", versionString)
	}
	if vn[2], err = strconv.Atoi(v[2]); err != nil {
		return nil, fmt.Errorf("%s: invalid patch version", versionString)
	}
	if vn[3], err = strconv.Atoi(v[3]); err != nil {
		return nil, fmt.Errorf("%s: invalid build version", versionString)
	}
	return vn[:], nil
}

func getVersionData() (string, []int, error) {
	bin, err := exec.Command("git", "describe", "--tags").Output()
	if err != nil {
		return "", nil, fmt.Errorf("Could not get version string from git (%w)", err)
	}
	str := strings.TrimSpace(string(bin))
	num, err := versionStrToNum(str)
	return str, num, err
}

func main() {
	fileVerStr, fileVerNum, err := getVersionData()
	if err != nil {
		fmt.Fprintln(os.Stderr, err.Error())
		os.Exit(1)
	}

	fmt.Printf(jsonTemplate,
		fileVerNum[0],
		fileVerNum[1],
		fileVerNum[2],
		fileVerNum[3],
		fileVerNum[0],
		fileVerNum[1],
		fileVerNum[2],
		fileVerNum[3],
		fileVerStr,
		fileVerStr)
}

自分は、これを他のソースの中に次のような go generate 行を入れて、最終的に必要となる .syso ファイルを作成しています。

//go:generate cmd /c go1.20.14.exe run mkversioninfo.go > v.json &&
    go1.20.14.exe run github.com/hymkor/goversioninfo/cmd/goversioninfo@master
    -icon=nyagos.ico,nyagos32x32.ico,nyagos16x16.ico
    -o ..\nyagos.syso v.json && del v.json

(スクロールして読みづらいので改行していますが、実際に使う時は1行にしてください)

ライセンスとか著作権とかハードコーディングされているので、そういうのを適当に書き換えて使っていただければ…と思います
(元のコードは BSD License 扱いにしていますし)

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?