🐈

ZigでEXEにアイコンを埋め込む

2024/03/31に公開

Windows版アイヌ語のIMEをZigで作っているときアイコンをIMEの実行ファイルに埋め込みたいので、そのやり方を調べてみた。

インストール

Zigのインストール方法(公式・日本語・古い)
https://zenn.dev/pgwalker/articles/afb0de9c08dae8

TL;DR

Windows Terminalを入れて)Powershellを開いて

# Scoopをインストール(
Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser
Invoke-RestMethod -Uri https://get.scoop.sh | Invoke-Expression

# Zigをインストール
scoop bucket add versions
scoop install versions/zig-dev

再起動します。

開発環境

VSCodeを入れます。ziglang.vscode-zigという拡張機能をインストールします(Ctrl+Shift+P、Install Extention)。出てきたZigの確認窓でPATHにあるものを設定、ZLSはインストールします。

プロジェクト設定

以下のコマンドを実行してプロジェクトを設定して、VSCodeで開きます。

mkdir zig-icon
cd zig-icon
zig init
code .

すると、自動的にファイルが生成され以下のようなフォルダー構造になります。

.
├── build.zig
├── build.zig.zon
└── src
    ├── main.zig
    └── root.zig

アセットを用意する

新しくresというフォルダーをsrcの横に作ります。

mkdir res
.
├── build.zig
├── build.zig.zon
├── res
└── src
    ├── main.zig
    └── root.zig

今回はZenn.dev様のアイコンをお借りさせていただきます。配布URLからダウンロードします。
https://zenn.dev/mediakit

scoop install imagemagick

curl -O zenn-logo.zip https://static.zenn.studio/images/mediakit/zenn-logo.zip
tar -xf .\zenn-logo.zip --exclude=__MACOSX/*

magick convert zenn-logo/logo-only.svg -resize 256x256 zenn-logo/logo-only.png
magick convert zenn-logo/logo-only.png -define icon:auto-resize=16,32,64,128,256 -compress zip res/zen-logo.ico

Remove-Item -Path "zenn-logo.zip", "zenn-logo" -Recurse -Force

すると以下のようにres/zen-logo.icoが生成されます。

.
├── build.zig
├── build.zig.zon
├── res
│   ├── zen-logo.ico
├── src
│   ├── main.zig
│   └── root.zig

続いてはリソースファイルres/resource.rcを用意します。

res/resource.rc
IDI_ICON ICON "zen-logo.ico"

ビルドスクリプトで読み込む

それから、build.zigを開きます。LIBを生成するため部分やコメントなどを消し、リソースすファイルをロードするコードexe.addWin32ResourceFile(.{ .file = .{ .path = "res/res.rc" } });を追加します。変更したものはこうなります。

build.zig
const std = @import("std");

pub fn build(b: *std.Build) void {
    const target = b.standardTargetOptions(.{});
    const optimize = b.standardOptimizeOption(.{});

    const exe = b.addExecutable(.{
        .name = "zig-icon",
        .root_source_file = .{ .path = "src/main.zig" },
        .target = target,
        .optimize = optimize,
    });

    // Add the resource file to the executable
    exe.addWin32ResourceFile(.{ .file = .{ .path = "res/res.rc" } });

    b.installArtifact(exe);

    const run_cmd = b.addRunArtifact(exe);
    run_cmd.step.dependOn(b.getInstallStep());

    if (b.args) |args| {
        run_cmd.addArgs(args);
    }
    const run_step = b.step("run", "Run the app");
    run_step.dependOn(&run_cmd.step);

    const exe_unit_tests = b.addTest(.{
        .root_source_file = .{ .path = "src/main.zig" },
        .target = target,
        .optimize = optimize,
    });
    const run_exe_unit_tests = b.addRunArtifact(exe_unit_tests);
    const test_step = b.step("test", "Run unit tests");
    test_step.dependOn(&run_exe_unit_tests.step);
}

コンパイル

ターミナルで以下のコマンドを実行します。

zig build run

するとアイコン付きのEXEファイルが生成されるはずです。

まとめ

いかがだったでしょうか?これで、アイコン付きのEXEファイルを楽しめますね!

参考

こちらの文章ではより詳しいリソースファイルの扱い方について紹介されています。
https://www.ryanliptak.com/blog/zig-is-a-windows-resource-compiler/

関連Issue及びZigのコンパイルに搭載される前のやり方。
https://github.com/ziglang/zig/issues/3702
https://github.com/squeek502/resinator

Discussion