Autotools を使ったライブラリのビルドシステム
共有ライブラリ,動的ライブラリ,およびこれらを利用するプログラムについて,GNU autotools を使ったビルドシステムを構築します.
ソースコードはC++ による共有ライブラリの作成およびC++ による動的ライブラリの作成で作成したものを流用しています.
作成されるライブラリの機能は端末に「Hello world.」と出力するだけですが,これで動作確認ができるわけです.
ライブラリは,/usr/local/lib/hello というディレクトリを作成してインストールしています.これはインストール先を設定する練習です.
作成配布ファイルを作成し,利用する練習もしています.
ポイントは,ライブラリのパスの管理とライブラリに与える番号だと思います.
ライブラリを作成するということはプログラムを分割するということなので,実行ファイルからライブラリが「見える」ようにする必要があります.
ライブラリに与える番号には,ファイル名に付くバージョン番号と,ライブラリ本体に付くリリース番号およびインターフェース番号があります.
リリース番号とインターフェース番号を設定することにより,種々のバージョンのライブラリを共存させることが可能になります.
インフォメーション
目次(ページ内リンク)
- 共有ライブラリのソースファイル
- ライブラリインターフェースのバージョン管理
- 配布パッケージの作成
- 配布パッケージからのビルドとインストール
- 共有ライブラリを利用するプログラム
- 動的ライブラリの作成
- 動的ライブラリの利用
参考にしたページ
- Automake - GNU Project - Free Software Foundation
- Autotools FAQ
- GNU Autoconf - Creating Automatic Configuration Scripts - GNU Project - Free Software Foundation
- GNU Libtool - Portable Dynamic Shared Object Management - GNU Project - Free Software Foundation
Debian パッケージ
このページは,Debian GNU/Linux バージョン 13 "trixie" で作成しました.
インストールしている主なパッケージを示します.
| パッケージ名(バージョン) | 注 |
| g++ (14.2.0) | GNU C++ コンパイラ |
| make (4.4.1) | コンパイルを制御するユーティリティ |
| automake (1.17) | GNU標準準拠のMakefile生成ツール |
| autoconf (2.72) | configure スクリプト自動作成プログラム |
| libtool (2.5.4) | 汎用ライブラリサポートスクリプト |
共有ライブラリのソースファイル
ディレクトリとファイルの構成
ディレクトリとファイルの構成を示します.libamhello というディレクトリで作業します.ソースファイルを include と lib に分けて格納します.
libamhello
├── README
├── Makefile.am
├── configure.ac
├─ include
│ ├── hello.h
│ └── Makefile.am
└─ lib
├── hello.cc
└── Makefile.am
ルートディレクトリのファイル
README
これは GNU Autotools のデモパッケージです.
Makefile.am
SUBDIRS = include lib
dist_doc_data = README
ACLOCAL_AMFLAGS = -I m4
configure.ac
AC_CONFIG_MACRO_DIRS([m4])
#配布パッケージのファイル名等
AC_INIT([libhello],[10.9.8],[bug-automake@gnu.org])
AM_INIT_AUTOMAKE([-Wall -Werror foreign])
AC_CONFIG_SRCDIR([lib/hello.cc])
AC_CONFIG_HEADERS([config.h])
AC_PROG_CXX
AM_PROG_AR
LT_INIT([disable-static])
#ヘッダファイルとライブラリのインストール先の設定
includedir=/usr/local/include/hello
libdir=/usr/local/lib/hello
AC_CONFIG_FILES([Makefile
include/Makefile
lib/Makefile])
AC_OUTPUT
configure.ac の雛形は autoscan を使って作成できます.
煩雑になるのでこのセクションでは省略し,後述の「共有ライブラリを利用するプログラム」セクションで紹介します.
以下,いくつかのノートを.
AC_PROG_CXX
ビルドに C++ を使うという指示です.
AC_INIT([libhello],[10.9.8],[bug-automake@gnu.org])
配布パッケージのファイル名等を設定しています.
libhello はファイル名の語幹となります.10.9.8 というのは,確認用の無意味な数字ですが,ファイル名に付くバージョン番号となります.
すなわちこの設定では,配布パッケージのファイル名は libhello-10.9.8.tar.gz となります.
LT_INIT([disable-static])
libtool を静的ライブラリを作らないオプションで使うという指示です.参考にしたページに挙げた "GNU Libtool" の 5.4.1 The LT_INIT macro に記されています.
これがないと共有ライブラリと静的ライブラリを生成したので,付け加えました.
includedir=/usr/local/include/hello
libdir=/usr/local/lib/hello
ファイルのインストール先を指定している箇所です.指定法は参考にしたページに挙げた "GNU Autoconf" の,4.8.2 Installation Directory Variables に記されています.
設定しないと,ライブラリは /usr/local/lib に,ヘッダは /usr/local/include にインストールされます.
わざわざインストール先を指定しているのは,種々のライブラリを管理する練習としてやってみた,ということです.
include ディレクトリのファイル
include/hello.h
#ifndef ___HELLO
#define ___HELLO
void hello();
#endif
include/Makefile.am
include_HEADERS = hello.h
lib ディレクトリのファイル
lib/hello.cc
#include "hello.h"
#include <iostream>
void hello() {
std::cout << "Hello World." << std::endl;
}
lib/Makefile.am
AM_CPPFLAGS = -I$(top_srcdir)/include
libhello_la_SOURCES = hello.cc
lib_LTLIBRARIES = libhello.la
#下の reliease や version で与えている数字は動作確認用.意味のない数字
libhello_la_LDFLAGS = -release 5.6.7 -version-info 2:1:0
lib/Makefile.am の 2 行め以降は,libtool に対する指示です.
参考にしたページに挙げた "GNU Automake" の,8.3.2 Building Libtool Libraries を参考にしました.
lib_LTLIBRARIES = libhello.la
libhello.la というファイルが生成し,インストールされます.libhello.la は,ライブラリへのパス情報が記述されたテキストファイルです.
libhello_la_LDFLAGS
libtool がプログラムをリンクする際に使用するフラグです.
共有ライブラリを作成するなら -shared というオプションが必要そうな雰囲気ですが,なくてもライブラリが作成できて,利用もできました.
libhello_la_LDFLAGS = -release 5.6.7
インストールされるライブラリに与えられるリリース番号を設定しています.".so" の前に付く数字です.
ここの設定だと,"libhello-5.6.7.so." ということです.
"バージョン番号"."マイナー番号"."リリース番号" という認識でいいと思います.
libhello_la_LDFLAGS = version-info 2.1.0
ライブラリインターフェースバージョン管理システムにより,".so" の後の番号に使われるインターフェース番号を設定しています.
ここの設定だと,"libhello-5.6.7.so.2.0.1" となります.数字の順序が入れ替わります.
これについては,次のセクションでまとめています.
ライブラリインターフェースのバージョン管理
リリース番号とインターフェース番号により,種々のバージョンのライブラリを共存させることが可能になります.
参考にしたページに挙げた "GNU Libtool" の 7 Library interface versions に記されています.
ここでいうインターフェースとは
マニュアルにはインターフェースとして以下のものが挙げられています.
- グローバル変数:名前と型の両方
- グローバル関数:引数の型と数,戻り値の型,関数名
- 標準入力,標準出力,標準エラー,ファイルフォーマット
- ソケット,パイプ,その他のプロセス間通信プロトコル形式
「ライブラリのバージョンアップでインターフェースに変更があれば,-version-info フラグを使用して libtool にインターフェースのバージョン情報を指定する必要があります」とのことです.
マニュアルは C 言語用です.C++ の場合,クラスやクラスの公開メンバに変更がある場合も含まれると思います.
Libtool のインターフェースバージョン管理
libtool ライブラリのインターフェースバージョンは3つの整数で記述されます.
current:revision:age == c:r:a)
- current:このライブラリが実装している最新のインターフェース番号
- revision:現在のインターフェースの実装番号
- age:このライブラリが実装しているインターフェースの最新と最古の差
マニュアルには,ライブラリのインターフェースバージョン情報を更新する際のルールが記されています.
多少変更して紹介します.
- 各 libtool ライブラリのインターフェースバージョン情報は,'0:0:0' で開始する
- インターフェースバージョン情報を更新するのは,ソフトウェアの一般公開の直前だけで充分
- インターフェースには変更がなく,ライブラリのソースコードが変更された場合,revision をインクリメントする('c:r:a' が 'c:r+1:a' になる)
- インターフェイスが追加,削除,または変更された場合は,current をインクリメントし,revision を 0 に設定する('c:r:a' が 'c+1:0:a' になる.a は下の規則に従う)
- 4 のとき,インタフェースが追加されていれば,age をインクリメントする('c+1:0:a' が 'c+1:0:a+1' になる)
- 4 のとき,インターフェースが削除または変更されていれば,age を 0 に設定する('c+1:0:a' が 'c+1:0:0' になる)
配布パッケージの作成
そのままインストールしてもいいのですが,配布パッケージを作成する練習をしておきます.
~/libamhello$ autoreconf --install
~/libamhello$ ./configure
~/libamhello$ make
~/libamhello$ sudo make distcheck(スーパーユーザーでないとエラーになります)
成功すれば,配布パッケージ libhello-10.9.8.tar.gz が libamhello ディレクトリに作成されます.
ファイル名は,configure.ac の AC_INIT([libamhello], [10.9.8], [bug-automake@gnu.org]) に由来します.
よく使うコマンドを記しておくと,
sudo make install でインストール.配布パッケージを作成しないならこれでよい
sudo make install でアンインストール
sudo make distclean でクリーンアップ
配布パッケージからのビルドとインストール
常法どおりです.
~/libamhello$ tar -xzvf libhello-10.9.8.tar.gz
~/libamhello$ cd libhello-10.9.8
~/libamhello/libhello-10.9.8$ ./configure
~/libamhello/libhello-10.9.8$ make
~/libamhello/libhello-10.9.8$ sudo make install
ここの例では,新しいディレクトリ /usr/local/lib/hello を作成してライブラリをインストールしています.
インストール後,このディレクトリには,以下のファイルが作成されていました.上 3 個のファイルについては共有ライブラリ作成のための予備知識でまとめています.
5.6.7 とか 0.0.1 といった番号は,lib/Makefile.am に設定したのでした.
- libhello-5.6.7.so.2.0.1……real name
- libhello.so……linker name.libhello-5.6.7.so.2.0.1 へのシンボリックリンク
- libhello-5.6.7.so.2……soname.libhello-5.6.7.so.2.0.1 へのシンボリックリンク
- libhello.la……GNU libtoo が生成するライブラリへのパス情報が記述されたファイル
共有ライブラリのパスを設定
このライブラリを利用するプログラムががこのライブラリをロードできるように,パスを設定しておく必要があります.
例えば,
/etc/ld.so.conf にライブラリへのパス /usr/local/lib/hello 行を追加して,
~/libamhello$ sudo ldconfig
共有ライブラリを利用するプログラム
上のセクションでインストールしたライブラリを利用するプログラムを作成して,動作確認します.
Automake を使った実行ファイルの作成とだいたい同じ作業です.
src/Makefile.am にリンクオプションを記述する箇所のみ異なります.
ソースファイル
作業ディレクトリを amhello とします.以下のようにソースファイルを作成しました.
amhello
├── README
├── Makefile.am
└─ src
├── main.cc
└── Makefile.am
README
これは GNU Autotools のデモパッケージです.
SUBDIRS = src
#include <hello/hello.h>
int main() {
hello();
return 0;
}
bin_PROGRAMS = hello
hello_SOURCES = main.cc
hello_LDFLAGS = -L/usr/local/lib/hello
hello_LDADD = -lhello
3 行めと 4 行めは,上のセクションで作成したライブラリをリンクするための指示です.右辺は,g++ のリンクオプションそのままです.
ビルドと実行
ここでは,autoscan を使って configure.ac の雛形を作成します.
~/amhello$ autoscan
~/amhello$ cp configure.scan configure.ac
configure.ac の一部を修正します.
AC_INIT([FULL-PACKAGE-NAME], [VERSION], [BUG-REPORT-ADDRESS])
↓
AC_INIT([hello],[1.0],[bug-automake@gnu.org])
AM_INIT_AUTOMAKE([-Wall -Werror foreign])
ビルドします.
~/amhello$ autoreconf -i
~/amhello$ ./configure
~/amhello$ make
実行
上で練習した共有ライブラリの場合とと同じ手順で,実行ファイルの配布パッケージも作成できます.
わざわざするまでもなかろうということで,src ディレクトリに生成している実行ファイルの動作確認で済まします.
~/amhello$ src/hello
とすれば,端末に
Hello World.
と出力されました.
動的ライブラリの作成
ライブラリ本体のソースファイルは,C++ による動的ライブラリの作成の,「グローバル関数編」のコードを使って練習します.
ビルドシステムは,上で練習した共有ライブラリのビルドシステムを多少変更して使います.
共有ライブラリの場合とは 2 か所異なるだけです.
ディレクトリとファイルの構成
ディレクトリとファイルの構成を示します.共有ライブラリの場合と同じ名前の libamhello というディレクトリで作業します.ファイル名も同じです.
libamhello
├── README
├── Makefile.am
├── configure.ac
├─ include
│ ├── hello.h
│ └── Makefile.am
└─ lib
├── hello.cc
└── Makefile.am
ルートディレクトリのファイル
README
これは GNU Autotools のデモパッケージです.
Makefile.am
SUBDIRS = include lib
dist_doc_data = README
ACLOCAL_AMFLAGS = -I m4
configure.ac
共有ライブラリ場合は autoscan で雛形を作成するところから記しましたが,ここでは作成済みのファイルの内容を記します.
AC_CONFIG_MACRO_DIRS([m4])
AC_INIT([libhello],[10.9.8],[bug-automake@gnu.org])
AM_INIT_AUTOMAKE([-Wall -Werror foreign])
AC_CONFIG_SRCDIR([lib/hello.cc])
AC_CONFIG_HEADERS([config.h])
AC_PROG_CXX
AM_PROG_AR
LT_INIT([disable-static])
includedir=/usr/local/include/hello
libdir=/usr/local/lib/hello
AC_CONFIG_FILES([Makefile
include/Makefile
lib/Makefile])
AC_OUTPUT
include ディレクトリのファイル
include/hello.h
共有ライブラリの場合とは hello.h の extern "C" 宣言のみ異なります.
#ifndef ___HELLO
#define ___HELLO
extern "C" void hello();
#endif
include/Makefile.am
include_HEADERS = hello.h
lib ディレクトリのファイル
lib/hello.cc
#include "hello.h"
#include <iostream>
void hello() {
std::cout << "Hello World." << std::endl;
}
lib/Makefile.am
AM_CPPFLAGS = -I$(top_srcdir)/include
libhello_la_SOURCES = hello.cc
lib_LTLIBRARIES = libhello.la
#下の reliease や version で与えている数字は動作確認用.意味のない数字
libhello_la_LDFLAGS = -release 5.6.7 -version-info 2:1:0
ビルドとインストール
上記共有ライブラリでは,練習目的で配布パッケージを作成しましたが.ここでは,そのままインストールします.
ヘッダファイルは /usr/local/include/hello/ に,ライブラリは /usr/local/lib/hello/ にインストールされます.
~/libamhello$ autoreconf --install
~/libamhello$ ./configure
~/libamhello$ make
~/libamhello$ sudo make install
動的ライブラリのパスを設定
上記共有ライブラリのインストール時と同じ作業です.すでに作業としていれば不要です.
このライブラリを利用するプログラムががこのライブラリをロードできるように,パスを設定しておく必要があります.
例えば,
/etc/ld.so.conf にライブラリへのパス /usr/local/lib/hello 行を追加して,
~/libamhello$ sudo ldconfig
動的ライブラリの利用
グローバル関数編のライブラリを利用するプログラム
ソースファイル
上のセクションでインストールした動的ライブラリを利用するプログラムを作成します.
ディレクトリ構成やファイル名は上述の共有ライブラリの場合と同じです.
amhello
├── README
├── Makefile.am
├── configure.ac
└─ src
├── main.cc
└── Makefile.am
ルートディレクトリのファイル
README
これは GNU Autotools のデモパッケージです.
Makefile.am
SUBDIRS = src
configure.ac
共有ライブラリ場合は autoscan で雛形を作成するところから記しましたが,ここでは作成済みのファイルの内容を記します.
AC_PREREQ([2.72])
AC_INIT([hello],[1.0],[bug-automake@gnu.org])
AM_INIT_AUTOMAKE([-Wall -Werror foreign])
AC_CONFIG_SRCDIR([src/main.cc])
AC_CONFIG_HEADERS([config.h])
AC_PROG_CXX
AC_CHECK_LIB([hello],[hello])
AC_CONFIG_FILES([Makefile
src/Makefile])
AC_OUTPUT
src ディレクトリのファイル
src/main.cc
#include <hello/hello.h>
int main() {
hello();
return 0;
}
src/Makefile.am
共有ライブラリとの違いは,ライブラリをロードするための設定が動的ライブラリ用に変更されている点です.
-lhello -ldl とする必要があるようです.
bin_PROGRAMS = hello
hello_SOURCES = main.cc
hello_LDFLAGS = -L/usr/local/lib/hello
hello_LDADD = -lhello -ldl
ビルドとインストール
実行ファイルは /usr/local/bin/ にインストールされます.
~/libamhello$ autoreconf --install
~/libamhello$ ./configure
~/libamhello$ make
~/libamhello$ sudo make install
実行
~/libamhello$ helloとしたら,端末に "Hello World." と表示されました.