概念:GNU構建系統和Autotool

原文:概念:GNU構建系統和Autotoolhtml

常用Linux的開發人員或者運維人員,可能對configure->make->make install至關熟悉。事實上,這叫GNU構建系統,利用腳本和make程序在特定平臺上構建軟件。這種方式成爲一種習慣,被普遍使用。本文從用戶視角和開發者視角詳細說明,這種構建方式的細節,以及開發者如何利用autoconfautomake等工具(autotools)建立兼容GNU構建系統的項目。linux

爲了簡化可移植構建的難度,在早期有一套autotools工具幫助程序員構建軟件。咱們熟知的configure->make->make install三部曲,大多都是基於autotools來構建的。autotools是GNU程序的標準構建系統,因此其實咱們常常在使用三部曲。有些程序雖然也是這三部曲,但卻不是用autotools實現的,好比nginx的源碼就是做者本身編寫的構建程序。nginx

用戶視角

用戶經過configure->make->make install基於源碼安裝軟件。然而大部分用戶可能並不知道這個過程究竟作了些什麼。程序員

configure腳本是由軟件開發者維護併發布給用戶使用的shell腳本。這個腳本的做用是檢測系統環境,最終目的是生成Makefileconfig.hshell

make經過讀取Makefile文件,開始構建軟件。而make install能夠將軟件安裝到須要安裝的位置。緩存

如上圖,開發者在分發源碼包時,除了源代碼(.c .h...),還有許多用以支撐軟件構建的文件和工具,其中最重要的文件就是Makefile.inconfig.h.inconfigure腳本執行成功後,將爲每個*.in文件處理成對應的非*.in文件。安全

大部分狀況只生成Makefile和config.h,由於Makefile用於make程序識別並構建軟件,而config.h中定義的宏,有助於軟件經過預編譯來改變自身的代碼,以適應目標平臺某些特殊性。有些軟件在configure階段,還能夠生成其餘文件,這徹底取決於如軟件自己。併發

configure

當運行configure時,將看到相似以下的系統檢查,這些檢查的多少取決於軟件自己的須要,也就是由軟件開發者來定義和編寫的。運維

checking for a BSD-compatible install... /usr/bin/install -c
checking whether build environment is sane... yes
checking for a thread-safe mkdir -p... /bin/mkdir -p
checking for gawk... gawk
checking whether make sets $(MAKE)... yes
checking for gcc... gcc
checking for C compiler default output file name... a.out
...

通常來講,configure主要檢查當前目標平臺的程序、庫、頭文件、函數等的兼容性。這些檢查結果將做用於config.hMakefile文件的生成。從而影響最終的編譯。ide

用戶也能夠經過給configure配置參數來定製軟件須要包含或不須要包含的組件、安裝路徑等行爲。這些參數分爲5組,能夠經過執行./configure --help來查看,軟件提供哪些配置參數:

  • *安裝路徑相關配置。最多見的是--prefix

  • *程序名配置。例如--program-suffix可用於爲生成的程序添加後綴。

  • *跨平臺編譯。不太經常使用。

  • *動態庫靜態庫選項。用於控制是否生成某種類型的庫文件。

  • 程序組件選項。用於配置程序是否將某種功能編譯到程序中,通常形如--with-xxx。這多是最經常使用的配置,並且由軟件開發者來定義。

*表示這是幾乎全部軟件都支持的配置,由於這些配置是autotool生成的configure腳本默認支持的。)

configure在執行過程當中,除了生成Makefile外,還會生成的文件包括但不限於:

  • config.log 日誌文件

  • config.cache 緩存,以提升下一次configure的速度,需經過-C來指定纔會生成

  • config.status 實際調用編譯工具構建軟件的shell腳本

若是軟件經過libtool構建,還會生成libtool腳本。關於libtool腳本如何生成,請看開發者視角

configure常常會中途出錯,這通常是因爲當前平臺不具備構建該軟件所必需的依賴(庫、函數、頭文件、程序...)。此時,不要慌張,仔細查看輸出,解決這些依賴。

開發者視角

開發者除了編寫軟件自己的代碼外,還須要負責生成構建軟件所須要文件和工具。當我接觸到autotools後,我發現,雖然有工具的幫助,但這件事情依舊十分複雜。

對於C或C++程序員,在早期,構建跨平臺的應用程序是至關繁瑣的一件事情,並且對於經驗不足的程序員而言,甚至難度巨大。由於構建可移植的程序的必要前提是對各個平臺足夠了解,這每每要花上至關長的時間去積累。

Unix系統的分支複雜度很高,不一樣的商用版或開源版或多或少都有差別。這些差別主要體如今:系統組件、系統調用。咱們主要將Unix分爲以下幾個大類:IBM-AIX HP-UX Apple-DARWIN Solaris Linux FreeBSDUnix分支大全

所以,對於開發者而言,要麼本身編寫構建用的腳本,這每每須要極其紮實的shell能力和平臺熟悉度。另外一個選擇就是部分依賴工具。autoconfautomake就是這樣的工具。

autoreconf

爲了生成configure腳本和Makefile.in等文件,開發者須要建立並維護一個configure.ac文件(在早期,一般叫configure.in文件,雖然沒有區別,但強烈建議使用.ac,由於.in文件每每意味着被configure腳本識別爲模板文件並生成直接參與最終構建的文件,configure.in在命名上有歧義),以及一系列的Makefile.amautoreconf程序可以自動按照合理的順序調用autoconf automake aclocal等程序。

configure.ac

configure.ac用於生成configure腳本。autoconf工具用來完成這一步。下面是一個configure.ac的例子:

AC_PREREQ([2.63])
AC_INIT([st], [1.0], [zhoupingtkbjb@163.com])
AC_CONFIG_SRCDIR([src/main.c])
AC_CONFIG_HEADERS([src/config.h])

AM_INIT_AUTOMAKE([foreign])


# Checks for programs.
AC_PROG_CC
AC_PROG_LIBTOOL

# Checks for libraries.

# Checks for header files.

# Checks for typedefs, structures, and compiler characteristics.

# Checks for library functions.

AC_CONFIG_FILES([Makefile
                 src/Makefile
                 src/a/Makefile
                 src/b/Makefile])
AC_OUTPUT

其中以AC_開頭的相似函數調用同樣的代碼,實際是一些被稱爲「宏」的調用。這裏的宏與C中的宏概念相似,會被替換展開。m4是一個經典的宏工具,autoconf正是構建在m4之上,能夠理解爲autoconf預先實現了大量的,用於檢測系統可移植性的宏,這些宏在展開後就是大量的shell腳本。因此編寫configure.ac須要對這些宏熟練掌握,而且合理調用。有時,甚至能夠本身實現本身的宏。

autoscan和configure.scan

能夠經過調用autoscan命令獲得一個初始化的configure.scan文件,而後重命名爲configure.ac後,在此基礎上編輯configure.acautoscan會掃描源碼,並生成一些通用的宏調用、輸入的聲明以及輸出的聲明。儘管autoscan十分方便,可是沒人可以在構建以前,就把代碼徹底寫好,所以autoscan一般用於初始化configure.ac

autoheader和config.h

autoheader命令掃描configure.ac中的內容,並肯定須要如何生成config.h.in。每當configure.ac有所變化,均可以經過再次執行autoheader更新config.h.in。在configure.ac經過AC_CONFIG_HEADERS([config.h])告訴autoheader應當生成config.h.in的路徑。在實際的編譯階段,生成的編譯命令會加上-DHAVE_CONFIG_H定義宏,因而在代碼中,咱們能夠經過下面代碼安全的引用config.h

/bin/sh ../../libtool --tag=CC   --mode=compile gcc -DHAVE_CONFIG_H ...
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

config.h包含了大量的宏定義,其中包括軟件包的名字等信息,程序能夠直接使用這些宏;更重要的是,程序能夠根據其中的對目標平臺的可移植性相關的宏,經過條件編譯,動態的調整編譯行爲。

automake和Makfile.am

手工編寫Makefile是一件至關煩瑣的事情,並且,若是項目複雜的話,編寫難度將愈來愈大。於是,automake工具應運而生。咱們能夠編寫像下面這樣的Makefile.am文件,並依靠automake來生成Makefile.in

SUBDIRS = a b
bin_PROGRAMS    = st
st_SOURCES        = main.c
st_LDADD        = $(top_builddir)/src/a/liba.la $(top_builddir)/src/b/libb.la

這裏經過SUBDIRS聲明瞭兩個子目錄,子目錄的中的構建須要靠a/Makefile.amb/Makefile.am來進行,這樣多目錄組織起來就方便多了。

bin_PROGRAMS聲明一個可執行文件目標,st_SOURCES指定這個目標所依賴的源代碼文件。另外,st_LDADD聲明瞭可執行文件在鏈接時,須要依賴的Libtool庫文件。

經過這個Makefile.am文件生成的Makefile.in文件至關大,不便貼出,可是能夠想象,Makefile.in要比咱們手工編寫的Makefile文件複雜的多。

automake的出現晚於autoconf,因此automake是做爲autoconf的擴展來實現的。經過在configure.ac中聲明AM_INIT_AUTOMAKE告訴autoconf須要配置和調用automake

aclocal

上面提到,configure.ac實際是依靠宏展開來獲得configure的。所以,可否成功生成取決於,宏定義可否找到。autoconf會從自身安裝路徑下來尋找事先定義好了宏。然而對於像automakelibtoolgettext等第三方擴展宏,甚至是開發者自行編寫的宏就一無所知了。因而,存在這個工具aclocal,將在configure.ac同一目錄下生成aclocal.m4,在掃描configure.ac的過程當中,將第三方擴展和開發者本身編寫的宏定義複製進去。這樣,autoconf在遇到不認識的宏時,就會從aclocal.m4中查找。

下面這張圖更爲詳細的展示了整個工具鏈是如何互相配合的。

libtool

libtool試圖解決不一樣平臺下,庫文件的差別。libtool實際是一個shell腳本,實際工做過程當中,調用了目標平臺的cc編譯器和連接器,以及給予合適的命令行參數。libtool能夠單獨使用,這裏只介紹與autotools集成使用相關的內容。

automake支持libtool構建聲明。在Makefile.am中,普通的庫文件目標寫做xxx_LIBRARIES

noinst_LIBRARIES = liba.a
liba_SOURCES = ao1.c ao2.c ao3.c

而對於一個libtool目標,寫做xxx_LTLIBRARIES,並以.la做爲後綴聲明庫文件。

noinst_LTLIBRARIES = liba.la
liba_la_SOURCES = ao1.c ao2.c ao3.c

在configure.ac中須要聲明LT_INIT

...
AM_INIT_AUTOMAKE([foreign])
LT_INIT
...

有時,若是須要用到libtool中的某些宏,則推薦將這些宏copy到項目中。首先,經過AC_CONFIG_MACRO_DIR([m4])指定使用m4目錄存放第三方宏;而後在最外層的Makefile.am中加入ACLOCAL_AMFLAGS = -I m4

all-in-one

上面討論了不少關於autoreconf的細節。實際上,現在咱們能夠直接調用autoreconf --install來自動調用上面提到的全部子命令。這裏--install參數試圖將輔助的腳本和宏copy到當前項目目錄中,下面是執行時的輸出:

autoreconf: Entering directory `.'
autoreconf: configure.ac: not using Gettext
autoreconf: running: aclocal 
autoreconf: configure.ac: tracing
autoreconf: running: libtoolize --copy
libtoolize: putting auxiliary files in `.'.
libtoolize: copying file `./ltmain.sh'
libtoolize: Consider adding `AC_CONFIG_MACRO_DIR([m4])' to configure.ac and
libtoolize: rerunning libtoolize, to keep the correct libtool macros in-tree.
libtoolize: Consider adding `-I m4' to ACLOCAL_AMFLAGS in Makefile.am.
autoreconf: running: /usr/bin/autoconf
autoreconf: running: /usr/bin/autoheader
autoreconf: running: automake --add-missing --copy --no-force
configure.ac:10: installing `./config.guess'
configure.ac:10: installing `./config.sub'
configure.ac:9: installing `./install-sh'
configure.ac:9: installing `./missing'
src/Makefile.am: installing `./depcomp'
autoreconf: Leaving directory `.'

當咱們以--install參數運行時,libtoolize --copy被調用,這將使得ltmain.sh被copy進來;接下來分別執行autoconfautoheaderautomake的參數爲--add-missing --copy --no-force,這將使得幾個輔助腳本和文件被安裝到目錄下。

這些輔助文件默認安裝在configure.ac同一個目錄下,若是你但願用另外一個目錄來存放他們,能夠配置AC_CONFIG_AUX_DIR,例如AC_CONFIG_AUX_DIR([build-aux])將使用build-aux目錄來存放輔助文件。

若是不使用--install參數,輔助文件要麼不copy,要麼以軟鏈的形式建立。推薦使用--install,由於這樣,其餘軟件維護能夠避免因爲構建工具版本不一致形成問題。

輔助文件

一個依靠GNU構建系統開發的軟件除了源碼以外,還有不少輔助的文件,有些是腳本,有些是文本文件。下面將逐一解釋這些文件:

  • aclocal.m4:上面提到了,這個宏定義文件裏面包含了第三方的宏定義,用於autoconf展開configure.ac

  • NEWS README AUTHORS ChangeLog:這些文件是GNU軟件的標配,不過在項目中不必定須要加入。若是項目中沒有這些文件,每次autoreconf會提示缺乏文件,不過這並不影響。若是不想看到這些錯誤提示,能夠用AM_INIT_AUTOMAKE([foreign])來配置automakeforeign參數就是告訴automake不要這麼較真:)

  • config.guess config.sub:automake產生,兩個用於目標平臺檢測的腳本

  • depcomp install-sh:由automake產生,用於完成編譯和安裝的腳本

  • missing:由automake產生

  • ltmain.sh:有libtoolize產生,該腳本用於在configure階段配置生成可運行於目標平臺的libtool腳本

  • ylwrap:由automake產生,若是檢測構建須要使用lex和yacc,那麼會產生這個包裝腳本

  • autogen.sh:在早期,autoreconf並不存在,軟件開發者每每須要本身編寫腳本,按照順序調用autoconf autoheader automake等工具程序。這個文件就是這樣的腳本。起這麼個名字多是習慣性的

總結

本文總概念上闡述了autotool系列工具是如何工做的。相好比今現成的IDE,GNU構建系統實際上是很是難用的,學習成本比較高。筆者認爲最爲效率的途徑是學習開源的程序,從中慢慢體會,慢慢吸取。另外,推薦幾個資料:

  • 《GNU Autoconf Automake and Libtool》

  • Autotools Tutorial

  • GNU網站上關於autoconf automake 和 libtool的手冊

筆者還有一些相關的筆記,後面再另外寫一篇文章總結一下。

相關文章
相關標籤/搜索