MSYS2 MinGW GCC環境をつくる (その3)
  Windows, Linux, C/C++

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です。gtkmmGTK+の公式な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プロジェクトのBinariessample-gtkmm.exeが表示されます。

実行

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

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

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

main.cppMyWindow.cppMyWindow.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.cppMyWindow.cppMyWindow.hに表示されていたエラーも消えます。

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

コード補完

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

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

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

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