mesonかcmakeでGTKプログラムをビルドする(リソースもね)

自分は、GTKのプログラミングをする際には主にPythonを使っているのですが、たまにC言語で書いてみようか、と思う時があるのですよ。
まぁそれも仕事ではなく私事だし、その上気分転換的な感じなので、惰性的にビルド環境にcmakeを使っていたのですが、GTK的にはmeson推しのようなので、この度mesonを使ってみようかと思います。

あと、**リソースを使うことを前提とします。**
GTKのリソースについては、[以前書いた記事](https://qiita.com/katsuko0303/items/4bb2ae52020027d8dd73)を参考にしてください。

以下、ubuntu23.10での作業を前提としています。

## ソースコードの作成

まずは、ソースコードを用意します。
適当なディレクトリに、以下のファイルを用意します。

* `main.c`
* `main.ui`
* `app.gresource.xml`

```c:main.c
#include <gtk/gtk.h>

static void on_activate(GApplication *app, gpointer cb_data)
{
	GtkBuilder *builder;
	GtkWidget *window;

	builder = gtk_builder_new();
	gtk_builder_add_from_resource(builder, "/com/example/example01/main.ui", NULL);

	window = GTK_WIDGET(gtk_builder_get_object(builder, "window"));
	gtk_window_set_application(GTK_WINDOW(window), GTK_APPLICATION(app));
	gtk_widget_set_visible(window, TRUE);

	g_object_unref(G_OBJECT(builder));
}

int main(int argc, char *argv[])
{
	GtkApplication *app;

	app = gtk_application_new(NULL, 0);
	g_signal_connect(G_OBJECT(app), "activate", G_CALLBACK(on_activate), NULL);
	return g_application_run(G_APPLICATION(app), argc, argv);
}
```

```xml:main.ui
<?xml version="1.0" encoding="utf-8"?>
<interface>
	<object class="GtkWindow" id="window">
		<property name="default-width">300</property>
		<child>
			<object class="GtkLabel">
				<property name="label">Hello, world!</property>
			</object>
		</child>
	</object>
</interface>
```

```xml:app.gresource.xml
<?xml version="1.0" encoding="utf-8"?>
<gresources>
	<gresource prefix="/com/example/example01">
		<file preprocess="xml-stripblanks">main.ui</file>
	</gresource>
</gresources>
```

まずは、これらをmesonもcmakeも使わずにビルドしてみます。
ubuntuであれば、以下のコマンドで必要なパッケージをインストールします。

```
sudo apt install -y build-essential libgtk-4-dev libxml2-utils
```

以下のコマンドでビルドします。

```
glib-compile-resources app.gresource.xml --target resource.c --generate-source && \
  gcc main.c resource.c $(pkg-config --cflags --libs gtk4)
```

ビルドが成功すると、「`a.out`」という実行ファイルが作成されるので、実行してみます。

```
./a.out
```

とりあえず確認が取れたら、今回のビルドで作られた「`resource.c`」と「`a.out`」は削除しておきましょう。

## mesonでビルドする。

mesonでビルドしてみます。
まずは、mesonをインストールしなければなりません。
「aptを使ってインストールする」方法と「pipを使ってインストールする」方法がありますが、今回はaptを使います。

```
sudo apt install -y meson
```

mesonでビルドするには、「`meson.build`」というファイルを作成します。
以下の内容にします。

```meson:meson.build
# 「プロジェクト名」と「言語」を指定します。
# 「プロジェクト名」はご自由に。
project('example01', 'c')

# 依存しているライブラリを指定します。
# ライブラリ名は、pkg-configで指定するライブラリ名です。
# また、複数ある場合には、「`gtk4 gmodule-2.0`」というように、文字列でスペース区切りで指定します。
libs = dependency('gtk4')

# リソースをビルドするための指定をします。
# 「dependencies」に、「app.gresource.xml」で含まれているファイルを指定します。
gnome = import('gnome')
resources = gnome.compile_resources('resources', 'app.gresource.xml',
  dependencies: [files('main.ui')],
  source_dir: [meson.source_root()])

# ビルドする実行ファイルを指定します。
# 実行ファイル名は、今回は先に指定したプロジェクト名と同じ「example01」にしていますが、
# 必ず同じにする必要はありません。
executable('example01', ['main.c', resources],
  dependencies: [libs])

```

リソースをビルドするための`gnome.compile_resources`については、[ここ](https://mesonbuild.com/Gnome-module.html#gnomecompile_resources)に詳細が書かれています。

最初に一回、ビルド環境を構築します。

```
meson setup build
```

このコマンドにより、「build」というディレクトリが作成されます。
このディレクトリの名前は、別に何でも構いません。
このディレクトリに、ビルドの際に作成されるファイルが出力されます。
(一般的に、gitなどのバージョン管理をしないファイルです)

次に、ビルドを行います。

```
meson compile -C build
```

正常にビルドが行われれば、buildディレクトリに実行ファイル(今回は`executable`に`example01`と指定したので、`example01`というファイル)が作成されます。
これを実行して、動作を確認します。

```
./build/example01
```

ソースファイルを変更した際には、「`meson compile`」コマンドを実行してビルドすればよいです。

## ついでに、cmakeでビルドする。

ついでなんで、今までやっていたcmakeでのビルドもしてみましょうか。
先のmesonでのビルドを行った場合は、一旦buildディレクトリを削除しておいてください。

cmakeでビルドするための、「`CMakeLists.txt`」ファイルを作成します。

```cmake:CMakeLists.txt
CMAKE_MINIMUM_REQUIRED(VERSION 3.10)

PROJECT(example01)

FIND_PACKAGE(PkgConfig)
PKG_CHECK_MODULES(LIBS REQUIRED gtk4)

INCLUDE_DIRECTORIES(${LIBS_INCLUDE_DIRS})

ADD_CUSTOM_COMMAND(
	OUTPUT resource.c
	COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && glib-compile-resources app.gresource.xml --target ${CMAKE_CURRENT_BINARY_DIR}/resource.c --generate-source
	DEPENDS
		app.gresource.xml
		main.ui
)

ADD_EXECUTABLE(example01
	main.c
	resource.c
)
TARGET_LINK_LIBRARIES(example01 ${LIBS_LIBRARIES})
```

cmakeのパッケージをインストールします。

```
sudo apt install cmake
```

cmakeでビルドするためのセットアップを行います。
mesonと同様に、`build`というディレクトリを作り、そこにビルドの際に作成されるファイルが出力されるようにします。

```
mkdir build && pushd build && cmake .. && popd
```

ビルドを行います。

```
make -C build
```

mesonと同じように実行ファイルは「`example01`」にしてありますので、実行します。

```
./build/example01
```

## ハマりポイント

冒頭でも書いたとおり、GTKのリソースを使う点が今回の話のポイントなのですが、特に問題なのが **`glib-compile-resources`コマンドを実行するには`app.gresource.xml`があるディレクトリで実行しなければならない**点。
というのも、中に書かれているファイルパス(今回の例では、`main.ui`など)は、**`app.gresource.xml`からの相対パスではなく、実行時のカレントディレクトリからの相対パスとして処理されてしまう**のです。
なので、mesonもcmakeも、ソースディレクトリで実行するようにしています。