MSYS2 MinGW GCC環境をつくる(その3)

MSYS2 MinGW GCC 環境をつくる その 3 です。今回は GTK+アプリケーションの作成に必要なライブラリのインストールと構成をして 実際に簡単な GTK+アプリケーションを作成します。

MSYS2 環境を構築して MinGW gcc コンパイラをインストールする方法 Eclipse CDT をインストールして MinGW gcc コンパイラを使用する方法については前回および前々回の記事を参照してください。

GTK+とは

GTK+ The GIMP Toolkit GUI を作成するためのマルチプラットフォーム ツールキットです。小さなアプリケーションにも 大規模なシステムにも適合します。どのようなアプリケーションを作ることができるのかは 公式サイトで公開されているスクリーンショットを見ると分かりやすいと思います。

GTK+の末尾には + が付いていますが この + C++の + とは関係がありません。GTK+ C++ではなく C 言語でも使える C API となっています。もちろん C++からも C API を呼び出すことはできるので C++から GTK+を使うことはできます。

gtkmm とは

C++から GTK+ API を使うこともできますが どうせならオブジェクト指向のクラスライブラリのほうが嬉しいですよね。

そこで登場するのが gtkmm です。gtkmm GTK+の公式な C++インターフェースです。C++を使うのであれば GTK+を直接使うよりも gtkmm を使ったほうが絶対に便利です。

元々は gtk-- という名前でしたが -- minus minus mm として 現在の gtkmm という名前になりました。

2018 1 月時点で 主に使われている GTK+バージョンには 2.4 系と 3.0 系の 2 系統があります。今回は gtkmm 3.0 を使って GTK+アプリケーションを作っていきます。

必要なライブラリをインストールする

まずは必要なライブラリのインストールです。これまでに gcc をインストールしたのと同様 MSYS2 なら簡単に GTK+アプリケーション開発に必要なライブラリをインストールすることができます。

C:¥msys64¥mingw64.exe を起動して gtkmm パッケージを検索します。

gtkmmを含むパッケージを検索するコマンド
$ pacman -Ssq gtkmm

4 つのパッケージが見つかりました。

  • mingw-w64-i686-gtkmm gtkmm 2.4 32 ビット版
  • mingw-w64-i686-gtkmm3 gtkmm 3.0 32 ビット版
  • mingw-w64-x86_64-gtkmm gtkmm 2.4 64 ビット版
  • mingw-w64-x86_64-gtkmm3 gtkmm 3.0 64 ビット版

今回使用するのは mingw-w64-x86_64-gtkmm3 gtkmm 3.0 64 ビット版 になります。さっそく インストールしていきましょう。

gtkmm3(64ビット版)をインストールするコマンド
$ pacman -S mingw-w64-x86_64-gtkmm3

mingw-w64-x86_64-gtkmm3 が依存するパッケージが自動的に表示されます。中には mingw-w64-x86_64-gtk3-3.22.26-1 なども見えますね。

インストールを行いますか? の確認メッセージで Y を押してから Enter キーを押すと gtkmm と必要なパッケージがまとめてインストールされます。

パッケージ数が多いのでインストール完了まで少し時間がかかります。ゆっくりと待ちましょう。

pkg-config をインストールする

pkg-config も一緒にインストールしておきます。

pkg-configを含むパッケージを検索するコマンド
$ pacman -Ssq pkg-config

5 つのパッケージが見つかりました。もう慣れてきましたよね。MinGW 64 ビット版ということで mingw-w64-x86_64-pkg-config パッケージをインストールします。

mingw-w64-x86_64-pkg-configパッケージをインストールするコマンド
$ pacman -S mingw-w64-x86_64-pkg-config

インストールを行いますか? の確認メッセージが表示されるので Y を押してから Enter キーを押します。

これで pkg-config コマンドが使えるようになりました。

pkg-config とは

GTK+アプリケーションをビルドするには数多くのライブラリをリンクする必要があります。ヘッダーファイルやライブラリのパスが分散しているため コンパイルおよびリンク時にヘッダーファイルやライブラリを指定するのが非常に煩雑です。

本来は GTK+アプリケーションのコンパイル時に以下のような指定が必要になります。

g++ myapp.cpp -IC:/msys64/mingw64/include/gtkmm-3.0 -IC:/msys64/mingw64/include/gtk-3.0 ¥
-LC:/msys64/mingw64/lib -lgtkmm-3.0 -lgtk-3 -lgdk-3 -lgdi32 -lole32 

実際にはもっと多くのヘッダーファイル ライブラリが必要でもっと引数は長くなります。

大変ですよね。このコンパイル時の引数指定を簡略化してくるのが pkg-config です。

gtkmm-3.0のコンパイルに必要な引数を表示するコマンド
$ pkg-config gtkmm-3.0 --cflags --libs

このように引数に gtkmm-3.0 を指定して pkg-config を実行するとコンパイル時に必要な引数を出力してくれます。

pkg-config のオプション --cflags はコンパイル時に必要となるヘッダーファイルなどを出力します。オプション --libs はリンク時に必要となるライブラリなどを出力します。

pkg-config はたくさんの引数を手書きしなくて済むだけでなく プラットフォーム依存を解決する役割も持っています。たとえば MinGW Windows pkg-config gtkmm-3.0 --libs を実行した場合の出力には -lgdi32 -lole32 など Windows 固有のライブラリも出力されますが Ubuntu Linux pkg-config gtkmm-3.0 --libs を実行してもこれらのライブラリは出力されず 代わりに Ubuntu Linux 固有の出力がされます。

つまり pkg-config gtkmm-3.0 を使うと プラットフォームごとに適切なコンパイル引数が指定できるわけです。

具体的な使い方は Makefile を書くときに説明していきます。

簡単な GTK+アプリケーションを作る

さて 簡単な GTK+アプリケーションを作っていきましょう。

Eclipse CDT を起動して C++プロジェクト sample-gtkmm を作ります。C++プロジェクトの作り方や構成方法が分からなくなってしまった場合は前回の記事を読み返してみてください。

MyWindow クラスの作成

最初にウィンドウ クラスを作ります。クラスは src フォルダーを選択して 右クリック NewClass で作成することができます。

クラス作成ダイアログが表示されるので Class name MyWindow と入力して Finish をクリックします。Namespace のチェックは外しておきます

これで MyWindow クラスを構成するソースコード MyWindow.cpp とヘッダーファイル MyWindow.h の雛形が自動的に作成されます。

それぞれのファイルは以下のようになっています。

MyWindow.h
#ifndef MYWINDOW_H_ #define MYWINDOW_H_ class MyWindow { public: MyWindow(); virtual ~MyWindow(); }; #endif /* MYWINDOW_H_ */
MyWindow.cpp
#include "MyWindow.h" MyWindow::MyWindow() { // TODO Auto-generated constructor stub } MyWindow::~MyWindow() { // TODO Auto-generated destructor stub }

これらのファイルを以下の内容に変更します。

MyWindow.h(変更後)
#ifndef MYWINDOW_H_ #define MYWINDOW_H_ #include <gtkmm.h> class MyWindow : public Gtk::Window { public: MyWindow(); virtual ~MyWindow(); protected: Gtk::Button btn1; }; #endif /* MYWINDOW_H_ */
MyWindow.cpp(変更後)
#include "MyWindow.h" MyWindow::MyWindow() : btn1("Hello, World!!") { set_default_size(320, 240); add(btn1); show_all(); } MyWindow::~MyWindow() { }
まだコードの意味が分からないかもしれませんが まずは GTK+アプリケーションのビルドの仕方を説明させてください。GTK+アプリケーションの作り方やソースコードの解説は別の機会に。

ソースコードを変更した後は各所に赤い波線が表示されたり エラーを示すマークが表示されます。これは Eclipse CDT gtkmm を参照できていないために起こっています。後で解決方法を説明していきます。今はこのまま作業を続けて問題ありません。

main.cpp の作成

次に プログラムのエントリーポイントとなる main.cpp を作成します。src フォルダーを選択して 右クリック NewSource File です。

ファイル名 main.cpp を入力して Finish をクリックします。

main.cpp ファイルを編集して以下の内容にします。

main.cpp
#include <gtkmm.h> #include "MyWindow.h" int main(int argc, char* argv[]) { auto app = Gtk::Application::create(argc, argv); MyWindow myWindow; return app->run(myWindow); }

main.cpp もエラー表示が出てしまいますが 今はこのままで問題ありません。

Makefile の作成

Project Explorer でプロジェクト名 sample-gtkmm を選択して 右クリック NewFile を選択します。

File name Makefile をと入力して Finish をクリックします。

Makefile を編集して以下の内容にしてください。

Makefile
TARGET=sample-gtkmm SRC_DIR=./src OBJ_DIR=./obj BIN_DIR=./bin CC = gcc CXX = g++ CPPFLAGS = CXXFLAGS = $(shell pkg-config gtkmm-3.0 --cflags) LDFLAGS = LOADLIBES = LDLIBS = $(shell pkg-config gtkmm-3.0 --libs) -lstdc++ EXTENSION:=.cpp SRC:=$(wildcard $(SRC_DIR)/**/*$(EXTENSION)) $(wildcard $(SRC_DIR)/*$(EXTENSION)) SRC_WITHOUT_PREFIX:=$(patsubst $(SRC_DIR)%,%,$(SRC)) OBJ:=$(addprefix $(OBJ_DIR),$(SRC_WITHOUT_PREFIX:$(EXTENSION)=.o)) $(BIN_DIR)/$(TARGET) : $(OBJ) @if [ ! -d $(BIN_DIR) ]; then mkdir $(BIN_DIR); fi $(CC) $(LDFLAGS) $^ $(LOADLIBES) $(LDLIBS) -o $@ $(OBJ_DIR)/%.o : $(SRC_DIR)/%$(EXTENSION) @if [ ! -d $(OBJ_DIR) ]; then mkdir $(OBJ_DIR); fi $(CXX) $(CXXFLAGS) $(CPPFLAGS) -c -o $@ $< .PHONY: all clean all: $(BIN_DIR)/$(TARGET) clean: @if [ -d $(OBJ_DIR) ]; then rm -fr $(OBJ_DIR); fi @if [ -d $(BIN_DIR) ]; then rm -fr $(BIN_DIR); fi

Makefile 10 行目と 13 行目に注目してください。

CXXFLAGS  = $(shell pkg-config gtkmm-3.0 --cflags)
LDLIBS    = $(shell pkg-config gtkmm-3.0 --libs) -lstdc++

pkg-config が出てきました。$(shell …) というのは make のコマンドで シェルでコマンドを実行してその出力が値になります。つまり pkg-config gtkmm-3.0 --cflags を実行して出力を CXXFLAGS 変数に設定している pkg-config gtkmm-3.0 --libs を実行してその出力に -lstdc++ を加えて LDLIBS 変数に設定している ということになります。

Makefile の後半で CXXFLAGS はコンパイル時の引数に指定されています。同様に LDLIBS はリンク時の引数に指定されています。

ビルド

Makefile を作成したら プロジェクトをビルドしてみましょう。Eclipse のメニューから ProjectBuild Project を選択します。

ビルド状況は Console ビューに表示されます。

pkg-config の出力が g++ コンパイル gcc リンク の引数に指定されていることが分かります。

ビルドが成功すると sample-gtkmm プロジェクトの Binaries sample-gtkmm.exe が表示されます。

実行

sample-gtkmm.exe を実行してみましょう。sample-gtkmm プロジェクトを選択して 右クリック Run AsLocal C/C++ Application を選択するか もしくは Ctrl + F11 を押します。

起動しました!成功です!

Eclipse gtkmm を参照できるようにする

main.cpp MyWindow.cpp MyWindow.h にエラー表示が残っているのに アプリケーションがビルドできて実行もできました。なんだか不思議ですね。

Makefile には gtkmm-3.0 に必要なヘッダーファイル ライブラリを指定する記述を追加していたのでビルドできました。ですが Eclipse Makefile の中に書かれている内容を検出できないため gtkmm のヘッダーファイルを見つけられずにエラー表示を出してしまします。

Eclipse にも gtkmm に必要なヘッダーファイルの場所を教えてあげる必要があります。

プロジェクト名 sample-gtkmm を選択して 右クリック Properties を選択するか もしくは Alt + Enter を押してプロジェクトのプロパティを表示します。

プロジェクトのプロパティが表示されたら C/C++ GeneralPath and Symbols を開いて Includes タブを表示します。

ここで Add... ボタンをクリックして必要なヘッダーファイルを追加していくと Eclipse gtkmm を参照できない問題が解決するのですが…。必要なヘッダーファイルが多く 1 つずつ追加していくのはとても大変です。

そこで pkg-config の出力を利用して Eclipse にヘッダーファイルを認識させる方法を使いたいと思います。

プロジェクトのプロパティで C/C++ BuildEnvironment を開きます。

この画面は出したままにしておいて 一度 pkg-config コマンドを実行します。

C:¥msys64¥mingw64.exe を起動して 次のコマンドを実行します。

CPLUS_INCLUDE_PATHを取得するコマンド
$ pkg-config --cflags-only-I gtkmm-3.0 | sed -e "s/^-I//" | sed -e "s/ -I/;/g"

このコマンドは pkg-config の出力を sed で加工して ヘッダーファイルのパスを ; セミコロン区切りで返します。

pkg-config のオプションは --cflags-only-I としています。--cflags とは異なり -mms-bitfields -pthread などの引数は含まれず 純粋にヘッダーファイルのパスのみが出力されます。

この出力部分をマウスクリック→ドラッグで範囲選択 右クリックして Copy を選択します。

これで出力内容がクリップボードにコピーされました。Eclipse に戻ります。sample-gtkmm プロジェクトのプロパティを開いて C/C++ BuildEnvironment を開いてるはずです。

Add... ボタンをクリックします。

新しい環境変数を設定するダイアログが表示されます。Name には CPLUS_INCLUDE_PATH と入力します。Value にはクリップボードにコピーした内容 pkg-config sed で加工して出力した内容 をペーストします。Value 欄にフォーカスして Ctrl + v を押せばペーストできます

Add to all configurations にも一応チェックを入れておきましょう。いまは構成が 1 つしかないのでチェックしなくても同じなのですが一応

最後に OK をクリックします。

新しい環境変数 CPLUS_INCLUDE_PATH が追加され 値として gtkmm 関連のヘッダーファイルのパスが入っています。

Apply をクリックします。

もう一度 sample-gtk プロジェクトのプロパティ C/C++ GeneralPath and Symbols に切り替えて Includes タブを表示します。

なんと! 先程まで空っぽだった Include directories にたくさんのディレクトリが並んでいます。Eclipse CDT には環境変数 CPLUS_INCLUDE_PATH を自動的に参照して構成してくれる機能があるのです。

Apply and Close をクリックしてプロパティを閉じます。

これで Eclipse gtkmm に関連するヘッダーファイルを見つけられるようになったので main.cpp MyWindow.cpp MyWindow.h に表示されていたエラーも消えます。

エラーが消えないときはファイル名をダブルクリックしてみてください。ファイルへのアクセスが発生しないと再チェックされないようです。

コード補完

Eclipse CDT がヘッダーファイルを認識するとエラーが消えるだけではありません。コード補完という嬉しい機能も使えるようになります。

たとえば 変数 btn1 の後ろに . ドット を入力すると メソッドの一覧が表示されて選択できるようになります。これは Eclipse CDT が変数 btn1 の型が Gtk::Button であることを認識して さらに Gtk::Button の定義をヘッダーファイルから参照してクラスのメンバーを自動的に表示してくれているわけです。

これで開発が捗りそうですね。

簡単な GTK+アプリケーション開発手順の紹介は以上です。