My code works, I don’t know why.

國王的耳朵是驢耳朵

GNU Build System: Autotools 初探

| Comments

身為組裝工,常常執行下面的指令

  • ./configure
  • make
  • 組裝

用了那麼久從來沒有思考過這是什麼樣的東西,慚愧之餘趕快惡補一下。


目錄


Overview

緣由:

雖然C語言號稱高移植性,然而遺憾的是因為System call, library等介面早期並沒有規範。這造成使用C語言開發多平台軟體的時候需要針對不同的平台做許多的檢查。後來GNU針對這部份提出了autotool工具減輕開發的負擔。

如果懶得看元件簡介,請直接看Overoview結論


Autotool 元件

從Wikipedia可以看到GNU Build system的項目中有提到三個元件: * Autoconf * Automake * Libtool


Autoconf - GNU Project - Free Software Foundation (FSF)

這邊可以看到configure負責產生

  • Makefile
    • 讓你編譯程式
  • C option用的header file (optional)
    • config.h
  • configure.status
    • 重新自動產生上面的資料
  • configure.cache (optional)
    • cache之前configure偵測的系統結果

套件中有幾個程式

  • autoscan
    • 產生configure.ac
  • autoconf
    • configure.ac中產生configure
    • 為何是呢?這和autoconf的版本有關
  • autoheader
    • configure.ac產生config.h.in
  • ifnames
    • 掃描C原始碼,抽出ifdef的資訊

Automake - GNU Project - Free Software Foundation (FSF)

套件中有兩個程式

  • automake
    • Makefile.am中產生Makefile.in,讓configure執行時可以產生Makefile
  • aclocal
    • configure.acconfigure.in產生aclocal.m4

GNU Libtool - The GNU Portable Library Tool

  • 提供抽象化的介面處理函式庫的平台差異問題,本篇暫不討論。

另外GNU 其他針對移植性輔助的套件有

  • GNU gettext
    • i18n套件,協助專案中的各國語言訊息開發。
  • pkg-config
    • 提供開發時的函式庫資訊,開發者不需要知道函式庫的路徑和旗標,直接問pkg-config就好。
  • GCC
    • 全名是GNU Compiler Collection

合體

從Wikipedia的圖:Autoconf-automake-process解釋的非常清楚。

回到最前面,我們可以觀察到autotool的目的就是

  • 產生平台上可以編譯的環境

為了達到這樣的目的,系統需要做到下面的功能

  • 檢查平台環境
  • 產生Makefile

因此,開發者最少要告訴autotools 所需平台環境產生Makefile 這兩項資訊。這也是configure.acMakefile.am存在的目的。也就是說,開發者需要自行設定configure.acMakefile.am

而這些錯綜複雜的關係可以描述如下

  • autotool先產生configure.ac (透過autoscan或是自幹)
  • autoreconfconfigure.acMakefile.am產生confgure, config.h.in, 和Makefile.in
  • autoreconf細節
    • configure.ac,呼叫aclocal產生aclocal.m4
    • aclocal是給autotools中自訂專案檢查編譯巨集用。
      • configure.acaclocal.m4,呼叫autoheader產生config.h.in
      • configure.acaclocal.m4,呼叫autoconf產生configure
      • configure.acaclocal.m4和各處的Makefile.am,呼叫automake產生Makefile.in

範例


測試環境

  • Ubuntu 12.04.4

測試程式

範例程式細節在這邊,檔案各別重新分配到src, include, libs這三個目錄。不想看code只要知道每個檔案都有參考到某個自訂的header file就好了。

測試程式樹狀架構
1
2
3
4
5
6
7
8
├── include
│   ├── liba.h
│   └── libb.h
├── libs
│   ├── liba.c
│   └── libb.c
└── src
    └── test.c

導入Autotools

簡述一下流程

  • 使用autoscan產生configure.ac範本
  • 手動更改configure.ac
  • Makefile.am,根目錄以及需要編譯的地方都要一份
  • autoreconf --install產生檔案
    • 替代方案:執行下面程式
      • aclocal
      • autoheader
      • automake --add-missing
      • autoconf
  • configure
  • make
  • make install安裝或make dist打包

configure.ac

執行autoscan後會產生configure.scan檔案,把這個檔案rename成configure.ac

configure.ac最初範本
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#                                               -*- Autoconf -*-
# Process this file with autoconf to produce a configure script.

AC_PREREQ([2.68])
AC_INIT([FULL-PACKAGE-NAME], [VERSION], [BUG-REPORT-ADDRESS])
AC_CONFIG_SRCDIR([libs/libb.c])
AC_CONFIG_HEADERS([config.h])

# Checks for programs.
AC_PROG_CC

# Checks for libraries.

# Checks for header files.
AC_CHECK_HEADERS([stdlib.h])

# Checks for typedefs, structures, and compiler characteristics.

# Checks for library functions.
AC_FUNC_MALLOC

AC_CONFIG_FILES([Makefile
                 libs/Makefile
                 src/Makefile])
AC_OUTPUT

接下來就是更動configure.ac,更動完和原本的差別如下:

configure.scan和configure.ac 差別
1
2
3
4
5
6
7
8
9
10
5,6c5,6
< AC_INIT([FULL-PACKAGE-NAME], [VERSION], [BUG-REPORT-ADDRESS])
< AC_CONFIG_SRCDIR([libs/libb.c])
---
> AC_INIT([Test_Autotools], [0], [test])
> AM_INIT_AUTOMAKE([foreign -Wall -Werror])
8a9,14
> # Use static libary
> AC_PROG_RANLIB
>

簡單說明如下

  • 更改套件版本資訊
  • 刪除AC_CONFIG_SRCDIR
  • 設定automake
    • foreign表示這不是標準的GNU Coding Standard,因此不會檢查額外的檔案如NEWS, README, ChangeLog等。
    • 共用編譯參數
  • AC_PROG_RANLIB表示要使用static library

Makefile.am

前面提到,每個目錄都要有一個Makefile.am。關於Makefile.am語法,可以參考 Alexandre Duret Lutz: Autotools Tutorial大略了解。更詳細的部份可以看Automake手冊:8.3 Building a Shared Library

範例中Makefile.am如下:

  • ./Makefile.am: 基本上就是依照列出的順序編譯
./Makefile.am
1
SUBDIRS = libs src
  • libs/Makefile.am
libs/Makefile.am
1
2
3
4
5
6
AM_CFLAGS = -I../include
lib_LIBRARIES = liba.a libb.a
liba_a_SOURCES = liba.c
libb_a_SOURCES = libb.c

include_HEADERS = ../include/liba.h ../include/libb.h

最上面形式就是要裝到目錄_Keyword,所以表示我們要產生liba.alibb.a,安裝時放在/usr/local/lib下面、指定library由哪些原始程式檔案組成、同時要把header files放在/usr/local/include下。(configure沒指定預設prefix為/usr/local)

  • src/Makefile.am
src/Makefile.am
1
2
3
4
5
LDADD = ../libs/liba.a ../libs/libb.a
AM_CFLAGS = -I../include

bin_PROGRAMS = test
test_SOURCES = test.c

一樣的形式:要裝到目錄_Keyword。這邊宣告要安裝到/usr/loca/bin、要link liba.alibb.a、並且5指定執行檔的原始程式檔案。


經過上面的處理,我們多了三個Makefile.amconfigure.ac以及一些暫存檔案。新的樹狀目錄列表如下:

autoscan後測試程式樹狀架構
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
.
├── autom4te.cache
│   ├── output.0
│   ├── requests
│   └── traces.0
├── autoscan.log
├── configure.ac
├── include
│   ├── liba.h
│   └── libb.h
├── libs
│   ├── liba.c
│   ├── libb.c
│   └── Makefile.am
├── Makefile.am
└── src
    ├── Makefile.am
    └── test.c

產生configure並編譯

如前所述,直接使用autoreconf --install就可以了

autoreconf –install以及之後的測試程式樹狀架構
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
$ autoreconf --install
configure.ac:6: installing `./install-sh'
configure.ac:6: installing `./missing'
libs/Makefile.am: installing `./depcomp'

.
├── aclocal.m4
├── autom4te.cache
│   ├── output.0
│   ├── output.1
│   ├── output.2
│   ├── requests
│   ├── traces.0
│   ├── traces.1
│   └── traces.2
├── autoscan.log
├── config.h.in
├── configure
├── configure.ac
├── depcomp
├── include
│   ├── liba.h
│   └── libb.h
├── install-sh
├── libs
│   ├── liba.c
│   ├── libb.c
│   ├── Makefile.am
│   └── Makefile.in
├── Makefile.am
├── Makefile.in
├── missing
└── src
    ├── Makefile.am
    ├── Makefile.in
    └── test.c

./configure結果節錄如下

autoreconf –install以及之後的測試程式樹狀架構
1
2
3
4
5
6
7
8
9
10
11
$ ./configure --prefix=/tmp/build
checking for a BSD-compatible install... /usr/bin/install -c
...
checking for GNU libc compatible malloc... yes
configure: creating ./config.status
config.status: creating Makefile
config.status: creating src/Makefile
config.status: creating libs/Makefile
config.status: creating config.h
config.status: config.h is unchanged
config.status: executing depfiles commands

幾點要注意的:

  • config.status被產生,並且被執行時生出config.h以及各目錄的的Makefile
  • 這邊指定prefix為/tmp/build,因此make install可以看到被安裝目錄樹狀結構如下:
安裝目錄樹狀結構
1
2
3
4
5
6
7
8
9
.
├── bin
│   └── test
├── include
│   ├── liba.h
│   └── libb.h
└── lib
    ├── liba.a
    └── libb.a

參考資料及資源

強烈推荐 Alexandre Duret Lutz: Autotools Tutorial,主要的想法和資料都是從這邊出來。而且他寫的更加詳細、更加易懂。

Comments