Autotools を使ったライブラリのビルドシステム

共有ライブラリ,動的ライブラリ,およびこれらを利用するプログラムについて,GNU autotools を使ったビルドシステムを構築します.

ソースコードはC++ による共有ライブラリの作成およびC++ による動的ライブラリの作成で作成したものを流用しています.
作成されるライブラリの機能は端末に「Hello world.」と出力するだけですが,これで動作確認ができるわけです.
ライブラリは,/usr/local/lib/hello というディレクトリを作成してインストールしています.これはインストール先を設定する練習です.
作成配布ファイルを作成し,利用する練習もしています.

ポイントは,ライブラリのパスの管理とライブラリに与える番号だと思います.
ライブラリを作成するということはプログラムを分割するということなので,実行ファイルからライブラリが「見える」ようにする必要があります.
ライブラリに与える番号には,ファイル名に付くバージョン番号と,ライブラリ本体に付くリリース番号およびインターフェース番号があります.
リリース番号とインターフェース番号を設定することにより,種々のバージョンのライブラリを共存させることが可能になります.

インフォメーション

目次(ページ内リンク)

  1. 共有ライブラリのソースファイル
  2. ライブラリインターフェースのバージョン管理
  3. 配布パッケージの作成
  4. 配布パッケージからのビルドとインストール
  5. 共有ライブラリを利用するプログラム
  6. 動的ライブラリの作成
  7. 動的ライブラリの利用

参考にしたページ

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)

  1. current:このライブラリが実装している最新のインターフェース番号
  2. revision:現在のインターフェースの実装番号
  3. age:このライブラリが実装しているインターフェースの最新と最古の差

マニュアルには,ライブラリのインターフェースバージョン情報を更新する際のルールが記されています.
多少変更して紹介します.

  1. 各 libtool ライブラリのインターフェースバージョン情報は,'0:0:0' で開始する
  2. インターフェースバージョン情報を更新するのは,ソフトウェアの一般公開の直前だけで充分
  3. インターフェースには変更がなく,ライブラリのソースコードが変更された場合,revision をインクリメントする('c:r:a' が 'c:r+1:a' になる)
  4. インターフェイスが追加,削除,または変更された場合は,current をインクリメントし,revision を 0 に設定する('c:r:a' が 'c+1:0:a' になる.a は下の規則に従う)
  5. 4 のとき,インタフェースが追加されていれば,age をインクリメントする('c+1:0:a' が 'c+1:0:a+1' になる)
  6. 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 に設定したのでした.

共有ライブラリのパスを設定

このライブラリを利用するプログラムががこのライブラリをロードできるように,パスを設定しておく必要があります.
例えば,
/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 のデモパッケージです.
Makefile.am

SUBDIRS = src
src/main.cc

#include <hello/hello.h>

int main() {
 hello();

 return 0;
}
src/Makefile.am

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." と表示されました.