原網址: http://markuskimius.wikidot.com/programming:tut:autotools
原文理解 & 操作
編寫 hello.c
#include <stdio.h>
int main()
{
printf("Hello\n");
return 0;
}
Makefile
all: hello
clean:
rm -f hello *.o
產生 configure.ac
autoscan
mv configure.{scan,ac}
產生 configure
autoconf
重命名 Makefile
為 Makefile.in
, 把後者當成 template 產生 Makefile
mv Makefile{,in}
產生 Makefile
& build .c
file
./configure
checking for gcc... gcc
checking whether the C compiler works... yes
checking for C compiler default output file name... a.out
checking for suffix of executables...
checking whether we are cross compiling... no
checking for suffix of object files... o
checking whether we are using the GNU C compiler... yes
checking whether gcc accepts -g... yes
checking for gcc option to accept ISO C89... none needed
configure: creating ./config.status
config.status: creating Makefile
config.status: error: cannot find input file: `config.h.in'
ls
autom4te.cache autoscan.log config.log config.status configure configure.ac hello.c Makefile Makefile.in
make clean all
rm -f hello *.o
cc hello.c -o hello
如果在 autoscan
的階段, Makefile
不存在的話, autoscan
產生的 configure.ac
如下
# -*- Autoconf -*-
# Process this file with autoconf to produce a configure script.
AC_PREREQ([2.69])
AC_INIT([FULL-PACKAGE-NAME], [VERSION], [BUG-REPORT-ADDRESS])
AC_CONFIG_SRCDIR([hello.c])
AC_CONFIG_HEADERS([config.h])
# Checks for programs.
AC_PROG_CC
# Checks for libraries.
# Checks for header files.
# Checks for typedefs, structures, and compiler characteristics.
# Checks for library functions.
AC_OUTPUT
存在的話, configure.ac
內容如下
# -*- Autoconf -*-
# Process this file with autoconf to produce a configure script.
AC_PREREQ([2.69])
AC_INIT([FULL-PACKAGE-NAME], [VERSION], [BUG-REPORT-ADDRESS])
AC_CONFIG_SRCDIR([hello.c])
AC_CONFIG_HEADERS([config.h])
# Checks for programs.
AC_PROG_CC
# Checks for libraries.
# Checks for header files.
# Checks for typedefs, structures, and compiler characteristics.
# Checks for library functions.
AC_CONFIG_FILES([Makefile])
AC_OUTPUT
多了這行, 代表會去找 Makefile.in
作為 template 產生 Makefile
AC_CONFIG_FILES([Makefile])
另外, 在 configure
時說少了 config.h.in
config.status: error: cannot find input file: `config.h.in'
可在執行過 configure
後用 autoheader
產生
./configure
...
...
...
./autoheader
另外, 如果 configure
不去找 config.h.in
的話 (早期的 autoconf 的行為是這樣的), 在 configure.ac
中加上這行
AC_CONFIG_HEADER(config.h)
在 source code 改成這樣以後, 還可以讓 autoscan
幫我們檢查有沒有需要加到 configure.ac
的檢查
* hello.c: A program to show the time since the Epoch */
#include <stdio.h>
#include <sys/time.h>
int main(int argc, char* argv[])
{
double sec;
struct timeval tv;
gettimeofday(&tv, NULL);
sec = tv.tv_sec;
sec += tv.tv_usec / 1000000.0;
printf("%f\n", sec);
return 0;
}
重新 scan 一次
$ autoscan
configure.ac: warning: missing AC_CHECK_FUNCS([gettimeofday]) wanted by: hello.c:11
configure.ac: warning: missing AC_CHECK_HEADERS([sys/time.h]) wanted by: hello.c:4
# 記得把新產生的 configure.scan 更名為 configure.ac
$ mv configure.{scan,ac}
$ autoconf
# 有了新的相容性檢查, 自然 config.h.in 中的開關數量也會不同, 所以 config.h.in 也要更新
$ autoheader
這樣, configure.ac
會更自動更新, config.h.in
也會自動加上新的開關. 再次運行 configure
$ ./configure
checking for gcc... gcc
checking whether the C compiler works... yes
checking for C compiler default output file name... a.out
checking for suffix of executables...
checking whether we are cross compiling... no
checking for suffix of object files... o
checking whether we are using the GNU C compiler... yes
checking whether gcc accepts -g... yes
checking for gcc option to accept ISO C89... none needed
checking how to run the C preprocessor... gcc -E
checking for grep that handles long lines and -e... /usr/bin/grep
checking for egrep... /usr/bin/grep -E
checking for ANSI C header files... yes
checking for sys/types.h... yes
checking for sys/stat.h... yes
checking for stdlib.h... yes
checking for string.h... yes
checking for memory.h... yes
checking for strings.h... yes
checking for inttypes.h... yes
checking for stdint.h... yes
checking for unistd.h... yes
checking sys/time.h usability... yes
checking sys/time.h presence... yes
checking for sys/time.h... yes
checking for gettimeofday... yes
configure: creating ./config.status
config.status: creating Makefile
config.status: creating config.h
檢查項目更多了. 有了新的 configure.ac
及新的 config.h.in
, 我們就可以通過裡面的 HAVE_GETTIMEOFDAY
確認是否有 gettimeofday()
這個 function, HAVE_SYS_TIME_H
確認是否有 sys/time.h
這個 header. 更新後的 source code 如下
/* hello.c: A program to show the time since the Epoch */
#include <stdio.h>
#include "config.h"
#ifdef HAVE_SYS_TIME_H
#include <sys/time.h>
#else
#include <time.h>
#endif
double get_sec_since_epoch()
{
double sec;
#ifdef HAVE_GETTIMEOFDAY
struct timeval tv;
gettimeofday(&tv, NULL);
sec = tv.tv_sec;
sec += tv.tv_usec / 1000000.0;
#else
sec = time(NULL);
#endif
return sec;
}
int main(int argc, char* argv[])
{
printf("%f\n", get_sec_since_epoch());
return 0;
}
改完 source code 以後, 可以再運行 autoscan
以確保沒有 portability 的問題
$ autoscan
$
接下來, 配合 Automake 強化 Makefile
. 概念差不多
- 建立
Makefile.am
Makefile.am
作為 template 給 Automake 產生Makefile.in
Makefile.in
作為 template 在 configure 過程產生Makefile
可惜的是, Makefile.am
沒法自動產生, 內容如下
bin_PROGRAM=hello
hello_SOURCES=hello.c
執行 automake
(回報的 error message 會因版本不同而不同)
$ automake
configure.ac: error: no proper invocation of AM_INIT_AUTOMAKE was found.
configure.ac: You should verify that configure.ac invokes AM_INIT_AUTOMAKE,
configure.ac: that aclocal.m4 is present in the top-level directory,
configure.ac: and that aclocal.m4 was recently regenerated (using aclocal)
Makefile.am: error: required file './INSTALL' not found
Makefile.am: 'automake --add-missing' can install 'INSTALL'
Makefile.am: error: required file './NEWS' not found
Makefile.am: error: required file './README' not found
Makefile.am: error: required file './AUTHORS' not found
Makefile.am: error: required file './ChangeLog' not found
Makefile.am: error: required file './COPYING' not found
Makefile.am: 'automake --add-missing' can install 'COPYING'
Makefile.am: error: required file './depcomp' not found
Makefile.am: 'automake --add-missing' can install 'depcomp'
/usr/share/automake-1.16/am/depend2.am: error: am__fastdepCC does not appear in AM_CONDITIONAL
/usr/share/automake-1.16/am/depend2.am: The usual way to define 'am__fastdepCC' is to add 'AC_PROG_CC'
/usr/share/automake-1.16/am/depend2.am: to 'configure.ac' and run 'aclocal' and 'autoconf' again
/usr/share/automake-1.16/am/depend2.am: error: AMDEP does not appear in AM_CONDITIONAL
/usr/share/automake-1.16/am/depend2.am: The usual way to define 'AMDEP' is to add one of the compiler tests
/usr/share/automake-1.16/am/depend2.am: AC_PROG_CC, AC_PROG_CXX, AC_PROG_OBJC, AC_PROG_OBJCXX,
/usr/share/automake-1.16/am/depend2.am: AM_PROG_AS, AM_PROG_GCJ, AM_PROG_UPC
/usr/share/automake-1.16/am/depend2.am: to 'configure.ac' and run 'aclocal' and 'autoconf' again
automake
會需要 configure.ac
的配合, 所以請我們要加上 AM_INIT_AUTOMAKE, 但, 因為 autoconf
裡面並沒有相對應的 macro, 所以, 還需要 aclocal
為我們產生 aclocal.m4
提供這些 macros.
先來處理第一個問題, 這個跟原文的錯誤訊息不太一樣, 需要自行解決. 在 configure.ac
中加上 AM_INIT_AUTOMAKE
...
AM_INIT_AUTOMAKE
...
再次執行 automake
, 錯誤還是一樣, 因為我們沒有給它正確的參數, 參考這頁.
雖說 AM_INIT_AUTOMAKE
需要一個 list 參數, 但這個值其實可以自動從 AC_INIT
中取得, 修改 configure.ac
中的 AC_INIT
參數後能解決這個問題. 從
AC_INIT([FULL-PACKAGE-NAME], [VERSION], [BUG-REPORT-ADDRESS])
改為下面這行, 然後再次執行 automake
, 可以發現不再回報上面那個錯誤.
AC_INIT([hello], [0.1], [http://example.com])
另外, 看起來原文中提到的 AC_CONFIG_HEADER
要換成 AM_CONFIG_HEADER
這個部份在我系統上的版本不需變動.
接下來, 處理一堆 missing 的 files. 可從 /usr/share/automake-1.16/
目錄下 copy 迥來. INSTALL
裡說明了怎麼安裝這個 package 的步驟, 會需要修改一下. COPYING
則是有關 license 的宣告, 也需要替換. 或是, 執行 automake
時加上參數讓它自動加上這些 missing 的檔案
$ automake --add-missing
Makefile.am: error: required file './NEWS' not found
Makefile.am: error: required file './README' not found
Makefile.am: error: required file './AUTHORS' not found
Makefile.am: error: required file './ChangeLog' not found
剩下的, 就只能手動產生了
$ touch NEWS README AUTHORS ChangeLog
再執行一次 automake
產生 Makefile.in
$ automake
$
因為修改了 configure.ac
, 我們需要再次執行 autoconf
, 但因為加了它不知道的 macros, 我們得先執行 aclocal
產生出 aclocal.m4
$ aclocal
然後再次執行 autoconf
更新 configure
=> ./configure
=> make
$ autoconf
$ ./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... /usr/bin/mkdir -p
checking for gawk... no
checking for mawk... mawk
checking whether make sets $(MAKE)... yes
checking whether make supports nested variables... yes
checking for gcc... gcc
checking whether the C compiler works... yes
checking for C compiler default output file name... a.out
checking for suffix of executables...
checking whether we are cross compiling... no
checking for suffix of object files... o
checking whether we are using the GNU C compiler... yes
checking whether gcc accepts -g... yes
checking for gcc option to accept ISO C89... none needed
checking whether gcc understands -c and -o together... yes
checking whether make supports the include directive... yes (GNU style)
checking dependency style of gcc... gcc3
checking how to run the C preprocessor... gcc -E
checking for grep that handles long lines and -e... /usr/bin/grep
checking for egrep... /usr/bin/grep -E
checking for ANSI C header files... yes
checking for sys/types.h... yes
checking for sys/stat.h... yes
checking for stdlib.h... yes
checking for string.h... yes
checking for memory.h... yes
checking for strings.h... yes
checking for inttypes.h... yes
checking for stdint.h... yes
checking for unistd.h... yes
checking sys/time.h usability... yes
checking sys/time.h presence... yes
checking for sys/time.h... yes
checking for gettimeofday... yes
checking that generated files are newer than configure... done
configure: creating ./config.status
config.status: creating Makefile
config.status: creating config.h
config.status: executing depfiles commands
$ make
make all-am
make[1]: Entering directory '/home/derekdai/Projects/test-autotools'
gcc -DHAVE_CONFIG_H -I. -g -O2 -MT hello.o -MD -MP -MF .deps/hello.Tpo -c -o hello.o hello.c
mv -f .deps/hello.Tpo .deps/hello.Po
gcc -g -O2 -o hello hello.o
make[1]: Leaving directory '/home/derekdai/Projects/test-autotools'
這個 PDF 也不錯 Embedded Linux Conference 2016GNU Autotools: a tutorial, 裡面說到
- configure.ac 本質上也就是 shell 的語法, 但會通過 M4 的 macro 展開過程 (成為完整的 shell script)
- Makefile.am 其實就是 Makefile, 用的也是 Makefile 的語法, 只不過,
automake
會展開及補全 (依特定的 name 或 name pattern 比對).
碰到的問題
autoconf 提供了 AC_CONFIG_FILE()
, 配合 AC_SUBST()
將指定的變數在 config file (例如 ).
但, 參考了 $prefix
的變數展開時間點其實非常的晚, 且相關變數在 configure
(configure
跟 configure.ac
本質上是 shell script) 中初始如下
# Installation directory options.
# These are left unexpanded so users can "make install exec_prefix=/foo"
# and all the variables that are supposed to be based on exec_prefix
# by default will actually change.
# Use braces instead of parens because sh, perl, etc. also accept them.
# (The list follows the same order as the GNU Coding Standards.)
bindir='${exec_prefix}/bin'
sbindir='${exec_prefix}/sbin'
libexecdir='${exec_prefix}/libexec'
datarootdir='${prefix}/share'
datadir='${datarootdir}'
sysconfdir='${prefix}/etc'
sharedstatedir='${prefix}/com'
localstatedir='${prefix}/var'
runstatedir='${localstatedir}/run'
includedir='${prefix}/include'
oldincludedir='/usr/include'
docdir='${datarootdir}/doc/${PACKAGE_TARNAME}'
infodir='${datarootdir}/info'
htmldir='${docdir}'
dvidir='${docdir}'
pdfdir='${docdir}'
psdir='${docdir}'
libdir='${exec_prefix}/lib'
localedir='${datarootdir}/locale'
mandir='${datarootdir}/man'
所以如 bindir
展開時結果會是 ${exec_prefix}/bin
而非 /usr/bin
, 因為是被單引號所括起來的.
這時, 如果使用 AC_CONFIG_FILE()
並在 config file 中參考了 libexecdir
, 例如 @[email protected]
, 那麼最終這個 config file 帶的值會是 ${exec_prefix}/libexec
而非 /usr/libexec
.
並且, 因為在 make
的過程中能指定 $prefix
, 所以不應在 configure
的過程中試圖展開任何參考了 $prefix
的變數 (每一次 make
的執行都可能變動 $prefix
).
那麼, 如果要在產生的 config 中寫入最終展開的路徑, 而非含帶變數的路徑, 怎麼做呢? 這篇有討論到, 在 make
中通常會對變數進行遞迴展開, 並且 $prefix
最終值是在 make
運行過程中確定的, 所以, 方案之一是用 make
+ sed
自行替換, 且因為每次 make
的執行都可能變動 $prefix
, 所以如果不保在上次的 $prefix
跟這次的進行比對的話, 就需要配合 .PHONE
讓 rule 每次都被執行, 舉個例孑.
我想用 org.freedesktop.portal.IBus.service.in
產生 org.freedesktop.portal.IBus.service
[D-BUS Service]
Name=org.freedesktop.portal.IBus
Exec=SCIM_LIBEXECDIR/scim-launcher -f ibus -e all
其中, SCIM_LIBEXECDIR
是在 configure.ac
中初始為
SCIM_LIBEXECDIR="$libdir/scim$SCIM_EPOCH"
因為 AC_CONFIG_FILES()
不可用, 我們改由在 Makefile.am
中以自訂 rule 來完成
.PHONY: gen_dbus_service
gen_dbus_service:
sed -e "s^SCIM_LIBEXECDIR^$(SCIM_LIBEXECDIR)^" $(top_srcdir)/org.freedesktop.portal.IBus.service.in >org.freedesktop.portal.IBus.service
之所以不循 @[email protected]
的慣例是因為 Makefile.am
=> Makefile.in
, 然後在 Makefile.in
被 configure
轉換成 Makefile
時是以 AC_CONFIG_FILES()
完成的, 此時如果寫成 @[email protected]
, 那結果的 Makefile
中 sed
命令中的 @[email protected]
就會被展開, 最終就沒法正確 match 到 org.freedesktop.portal.IBus.service.in
中的 @[email protected]
.