Makefile.am是一種比Makefile更高層次的編譯規則,能夠和configure.in文件一塊兒經過調用automake命令,生成Makefile.in文件,再調用./configure的時候,就將Makefile.in文件自動生成Makefile文件了。因此Makefile.am文件是比Makefile文件更高的抽象。apache
下面我根據本身的工做中的一些應用,來討論Makefile.am的編寫。我以爲主要是要注意的問題是將編譯什麼文件?這個文件會不會安裝?這個文件被安裝到什麼目錄下?能夠將文件編譯成可執行文件來安裝,也能夠編譯成靜態庫文件安裝,常見的文件編譯類型有下面幾種:session
bin_PROGRAMS = client client_SOURCES = key.c connect.c client.c main.c session.c hash.c client_CPPFLAGS = -DCONFIG_DIR=\"$(sysconfdir)\" -DLIBRARY_DIR=\"$(pkglibdir)\" client_LDFLAGS = -export-dynamic -lmemcached noinst_HEADERS = client.h INCLUDES = -I/usr/local/libmemcached/include/ client_LDADD = $(top_builddir)/sx/libsession.la \ $(top_builddir)/util/libutil.la
上面就是一個所有的Makefile.am文件,這個文件用於生成client可執行應用程序,引用了兩個靜態庫和MC等動態庫的鏈接。分析一下:memcached
bin_PROGRAMS
:表示指定要生成的可執行應用程序文件,這表示可執行文件在安裝時須要被安裝到系統中,若是隻是想編譯。不想被安裝到系統中,能夠用noinst_PROGRAMS來代替。
client_SOURCES
:表示生成可執行應用程序所用的源文件,這裏注意,client_是由前面的bin_PROGRAMS指定的,若是前面是生成example,那麼這裏就是example_SOURCES,其它的相似標識也是同樣。client_CPPFLAGS
:這和Makefile文件中同樣,表示C語言預處理器參數,這裏指定了DCONFIG_DIR,之後在程序中,就能夠直接使用CONFIG_DIR,不要把這個和另外一個CFLAGS混淆,後者表示編譯器參數。client_LDFLAGS
:這個表示在鏈接時所須要的庫文件選項標識。這個也就是對應一些如-l,-shared等選項。noinst_HEADERS
:這個表示該頭文件只是參加可執行文件的編譯,而不用安裝到安裝目錄下。若是須要安裝到系統中,能夠用include_HEADERS來代替。INCLUDES
:鏈接時所須要的頭文件。client_LDADD
:鏈接時所須要的庫文件,這裏表示須要兩個庫文件的支持,下面會看到這個庫文件又是怎麼用Makefile.am文件後成的。再談談關於上文中的全局變量引用,可能有人注意到$(top_builddir)
等全局變量(由於這個文件以前沒有定義),其實這個變量是Makefile.am系統定義的一個基本路徑變量,表示生成目標文件的最上層目錄,若是這個Makefile.am文件被其它的Makefile.am文件,這個會表示其它的目錄,而不是這個當前目錄。還可使用$(top_srcdir)
,這個表示工程的最頂層目錄,其實也是第一個Makefile.am的入口目錄,由於Makefile.am文件能夠被遞歸性的調用。ui
下面再說一下上文中出現的$(sysconfdir)
,在系統安裝時,咱們都記得先配置安裝路徑,如./configure --prefix=/install/apache
其實在調用這個以後,就定義了一個變量$(prefix)
,表示安裝的路徑,若是沒有指定安裝的路徑,會被安裝到默認的路徑,通常都是/usr/local。在定義$(prefix),還有一些預約義好的目錄,其實這一些定義均可以在頂層的Makefile文件中能夠看到,以下面一些值:url
bindir = $(prefix)/bin。
libdir = $(prefix)/lib。
datadir=$(prefix)/share。
sysconfdir=$(prefix)/etc。
includedir=$(prefix)/include。
這些量還能夠用於定義其它目錄,例如我想將client.h安裝到include/client目錄下,這樣寫Makefile.am文件:spa
clientincludedir=$(includedir)/client clientinclude_HEADERS=$(top_srcdir)/client/client.h
這就達到了個人目的,至關於定義了一個安裝類型,這種安裝類型是將文件安裝到include/client目錄下。
咱們本身也能夠定義新的安裝目錄下的路徑,如我在應用中簡單定義的:code
devicedir = ${prefix}/device device_DATA = package
這樣的話,package文件會做爲數據文件安裝到device目錄之下,這樣一個可執行文件就定義好了。注意,這也至關於定義了一種安裝類型:devicedir,因此你想怎麼安裝就怎麼安裝,後面的XXXXXdir,dir是固定不變的。server
編譯靜態庫和編譯動態庫是不同的,咱們先看靜態庫的例子,這個比較簡單。直接指定 XXXX_LTLIBRARIES或者XXXX_LIBRARIES就能夠了。若是不須要安裝到系統,將XXXX換成noinst就能夠。仍是再羅嗦一下:遞歸
通常推薦使用libtool庫編譯目標,由於automake包含libtool,這對於跨平臺可移植的庫來講,確定是一個福音。
看例子以下:編譯器
noinst_LTLIBRARIES = libutil.la noinst_HEADERS = inaddr.h util.h compat.h pool.h xhash.h url.h device.h libutil_la_SOURCES = access.c config.c datetime.c hex.c inaddr.c log.c device.c pool.c rate.c sha1.c stanza.c str.c xhash.c libutil_la_LIBADD = @LDFLAGS@
第一行的noinst_LTLIBRARIES
,這裏要注意的是LTLIBRARIES
,另外還有LIBRARIES
,兩個都表示庫文件。前者表示libtool庫,用法上基本是同樣的。若是須要安裝到系統中的話,用lib_LTLIBRARIES
。
注意:靜態庫編譯鏈接時須要其它的庫的話,採用XXXX_LIBADD
選項,而不是前面的XXXX_LDADD
。編譯靜態庫是比較簡單的,由於直接能夠指定其類型。
想要編譯XXX.so文件,須要用_PROGRAMS
類型,這裏一個關於安裝路徑要注意的問題是,咱們通常但願將動態庫安裝到lib目錄下,按照前面所討論的,只須要寫成lib_PROGRAMS
就能夠了,由於前面的lib表示安裝路徑,可是automake不容許這麼直接定義,能夠採用下面的辦法,也是將動態庫安裝到lib目錄下
projectlibdir=$(libdir) //新建一個目錄,就是該目錄就是lib目錄 projectlib_PROGRAMS=project.so project_so_SOURCES=xxx.C project_so_LDFLAGS=-shared -fpic //GCC編譯動態庫的選項
這是一個很重要的關鍵詞,咱們前面生成了一個一個的目標文件,可是一個大型的工程項目是由許多個可執行文件和庫文件組成,也就是包含多個目錄,每一個目錄下都有用於生成該目錄下的目標文件的Makefile.am文件,但頂層目錄是如何調用,才能使下面各個目錄分別生成本身的目標文件呢?就是SUBDIRS關鍵詞的用法了。
看一下個人工程項目,這是頂層的Makefile.am文件
EXTRA_DIST = Doxyfile.in README.win32 README.protocol contrib UPGRADE devicedir = ${prefix}/device device_DATA = package SUBDIRS = etc man if USE_LIBSUBST SUBDIRS += subst endif SUBDIRS += tools io sessions util client dispatch server hash storage sms
UBDIRS表示在處理目錄以前,要遞歸處理哪些子目錄,這裏還要注意處理的順序。好比個人client對sessions和utils這兩上目標文件有依賴,就在client以前須要處理這兩個目標文件。 EXTRA_DIST:將哪些文件一塊兒打包。