最近更新: 2010-05-20

Autoconf 檢查額外函數庫

日前處理一件工作,用 C 語言引用另一家廠商提供的 SDK 開發一套小工具。身為一個專業人員,按照 GNU 軟體開發指南,我們的 C 程式碼,應該要透過 Autoconf/Automake 完成建置前的組態工作。於是我進一步地使用 Autoconf 工具完成完成軟體建置環境的檢測與生產的組態文件。

然而我在將第三方廠商的 SDK 檢測動作加入 Autoconf 流程時,碰到了麻煩。為了處理這件事,花了我一整個工作天解決。令我不禁抱怨 GNU Autoconf manual 的內容編排內容實在很糟糕。本文記錄了在 Autoconf 中,如何檢查建置軟體時所需的額外標頭檔與函數庫。

檢測核心與機器規格

Autoconf 以 AC_OUTPUT 定義輸出文件。若我們需要在那些輸出文件中使用變數,則可以用 AC_SUBST 建立輸出變數,那些輸出變數可以在 Autoconf 的輸出文件中,透過 @輸出變數名稱@ 的寫法引用變數內容。AC_SUBST 的第二個參數若省略,表示以內部變數之現值作為輸出變數之值。

下列定義 KERNEL, MACHINEARCHITECTURE。我先利用 uname 取得核心與 CPU 硬體規格,指派為 KERNEL, MACHINE 之值。再以 AS_IF 判斷 MACHINE 的結果,指派 ARCHITECTURE 之值。因為這三個變數要持續延用於 Makefile.am 中,故再以 AC_SUBST 建立對應的輸出變數。

KERNEL=`uname -s`

MACHINE=`uname -m`

AS_IF([test "x$MACHINE" = xi686], [MACHINE="x86"])
AS_IF([test "x$MACHINE" = x86_64], [ARCHITECTURE="amd64"], [ARCHITECTURE="i386"])

# create output variables.

AC_SUBST(KERNEL)
AC_SUBST(MACHINE)
AC_SUBST(ARCHITECTURE)

以 AC_ARG_WITH 指定額外的軟體項目

經常下載 C 程式碼並使用 Autoconf 流程編譯程式碼的人,應該知道有許多案子的 configure 允許我們透過 --with-xxx 的參數,指定額外的項目。當我們編寫 Autoconf 文件時,需用 AC_ARG_WITH 實現這個工作。

假設第三方廠商提供的SDK名稱為 mylib ,我們想要在 configure 中增加一個 --with-mylib 的參數項目,以便他人指定 mylib 的安裝路徑。當我們增加 --with-mylib 選用參數時,Autoconf 會自動在內部定義一個 with_mylib 變數代表它。所以當你下達 configure --with-mylib=/opt/local/mylib 時,在 Autoconf 內部便定義 with_mylib 之值為 /opt/local/mylib 。

AC_ARG_WITH([mylib], 
    [AS_HELP_STRING([--with-mylib], [location of the MyLib installed path, defaults to /usr/local/mylib])],
    [],
    [with_mylib=/usr/local/mylib])

# according --with-mylib to define MYLIB_xxx variables.

MYLIB_CFLAGS="-I$with_mylib/include/$KERNEL"
MYLIB_LDFLAGS="-L$with_mylib/lib/${KERNEL}_${MACHINE}"
MYLIB_LIBS="-lMyCcore -lMyService -lMyTemplate"

AC_SUBST(MYLIB_CFLAGS)
AC_SUBST(MYLIB_LDFLAGS)
AC_SUBST(MYLIB_LIBS)

CFLAGS="$CFLAGS $MYLIB_CFLAGS"
LDFLAGS="$LDFLAGS $MYLIB_LDFLAGS"
LIBS="$LIBS $MYLIB_LIBS"

AC_CHECK_HEADERS 可以幫我們檢查標頭檔是否可在系統 INCLUDE 路徑中尋獲(預設為 /include:/usr/include)。AC_CHECK_LIB 則幫我們檢查函數庫檔案是否可在系統 LIBRARY 路徑中尋獲(預設為 /lib:/usr/lib)。但當我們的檔案並未被安裝在系統預設路徑時,那兩個巨集就幫不了我們。在此例中,我將 MyLib 安裝在 /usr/local/mylib 。此時我們就必須使用其他較複雜的方式,實現檢查動作。

  • Macro: AC_COMPILE_IFELSE (input, [action-if-true], [action-if-false])
    See: Running the Compiler
  • Macro: AC_LINK_IFELSE (input, [action-if-true], [action-if-false])
    See: Running the Linker
  • Macro: AC_MSG_RESULT
    AC_MSG_RESULT 僅輸出訊息,通常用於成功事件。
  • Macro: AC_MSG_ERROR
    AC_MSG_ERROR 在輸出訊息後,會中止 configure 流程,通常用於失敗事件

檢查標頭檔的動作,我使用 AC_COMPILE_IFELSE 即時產生一段引用指定標頭檔的 C 程式碼,交由 Autoconf 調用 C 編譯器編譯。此處的編譯動作會套用我先前定義的 CFLAGS 之值作為編譯時的參數。我在 CFLAGS 中指示了 MyLib 標頭檔的路徑位置,若路徑錯誤,則編譯動作也會失敗,進入失敗處理流程。AC_CHECK_HEADERS 的問題在於它不會套用 CFLAGS 的內容,因此我們才要自行處理。

#AC_CHECK_HEADERS([my-core.h my-template.h],,

#    [AC_MSG_ERROR([MyLib header files were not found. 

#        Install MyLib and use --with-mylib to specify path.])])

AC_MSG_CHECKING([for MyLib header files])
AC_COMPILE_IFELSE(
    [[
    #include <my-core.h>

    #include <my-template.h>

    ]],
    [AC_MSG_RESULT([yes])],
    [AC_MSG_ERROR([MyLib header files were not found. 
Install MyLib and use --with-mylib to specify path.])]
    )

檢查函數庫檔的動作,我使用 AC_LINK_IFELSE 即時產生一段調用指定函數的 C 程式碼,交由 Autoconf 調用編譯器與連結器。此處的建置動作會套用我先前定義的 CFLAGS, LDFLAGSLIBS 之值作為建置時的參數。若路徑錯誤,則建置動作也會失敗,進而觸入失敗處理流程。

#AC_CHECK_LIB([MyCore MyService MyTemplate],,,

#    [AC_MSG_ERROR([MyLib libraries were not found. 

#        Install MyLib and use --with-mylib to specify path.])])

AC_MSG_CHECKING([for MyLib libraries])
AC_LINK_IFELSE(
    [AC_LANG_PROGRAM([[
    #include <my-core.h>

    #include <my-template.h>

    ]],
    [[
    MyCoreInitialize();
    TemplateCreate(NULL);
    ]])],
    [AC_MSG_RESULT([yes])],
    [AC_MSG_ERROR([MyLib libraries were not found. 
Install MyLib and use --with-mylib to specify path.])]
    )
configuer.ac 完整內容
AC_PREREQ(2.59)
AC_INIT([hello],[0.0.1],[xxx@xxx])
AC_CONFIG_SRCDIR([src/hello.c])
AM_INIT_AUTOMAKE([dist-bzip2])

CFLAGS="$CFLAGS"
AC_PROG_CC
AC_PROG_INSTALL
AC_PROG_LIBTOOL

KERNEL=`uname -s`
MACHINE=`uname -m`
AS_IF([test "x$MACHINE" = xi686], [MACHINE="x86"])
AS_IF([test "x$MACHINE" = x86_64], [ARCHITECTURE="amd64"], [ARCHITECTURE="i386"])

# create output variables.

AC_SUBST(KERNEL)
AC_SUBST(MACHINE)
AC_SUBST(ARCHITECTURE)


AC_ARG_WITH([mylib], 
    [AS_HELP_STRING([--with-mylib], [location of the MyLib installed path, defaults to /usr/local/mylib])],
    [],
    [with_mylib=/usr/local/mylib])

# according --with-mylib to define MYLIB_xxx variables.

MYLIB_CFLAGS="-I$with_mylib/include/$KERNEL"
MYLIB_LDFLAGS="-L$with_mylib/lib/${KERNEL}_${MACHINE}"
MYLIB_LIBS="-lMyCcore -lMyService -lMyTemplate"

AC_SUBST(MYLIB_CFLAGS)
AC_SUBST(MYLIB_LDFLAGS)
AC_SUBST(MYLIB_LIBS)

CFLAGS="$CFLAGS $MYLIB_CFLAGS"
LDFLAGS="$LDFLAGS $MYLIB_LDFLAGS"
LIBS="$LIBS $MYLIB_LIBS"


#AC_CHECK_HEADERS([my-core.h my-template.h],,

#    [AC_MSG_ERROR([MyLib header files were not found. 

#        Install MyLib and use --with-mylib to specify path.])])

AC_MSG_CHECKING([for MyLib header files])
AC_COMPILE_IFELSE(
    [[
    #include <my-core.h>

    #include <my-template.h>

    ]],
    [AC_MSG_RESULT([yes])],
    [AC_MSG_ERROR([MyLib header files were not found. 
Install MyLib and use --with-mylib to specify path.])]
    )

#AC_CHECK_LIB([MyCore MyService MyTemplate],,,

#    [AC_MSG_ERROR([MyLib libraries were not found. 

#        Install MyLib and use --with-mylib to specify path.])])

AC_MSG_CHECKING([for MyLib libraries])
AC_LINK_IFELSE(
    [AC_LANG_PROGRAM([[
    #include <my-core.h>

    #include <my-template.h>

    ]],
    [[
    MyCoreInitialize();
    TemplateCreate(NULL);
    ]])],
    [AC_MSG_RESULT([yes])],
    [AC_MSG_ERROR([MyLib libraries were not found. 
Install MyLib and use --with-mylib to specify path.])]
    )

AC_OUTPUT([
Makefile
data/Makefile
src/Makefile
])

參考文件

相關文章
樂多舊網址: http://blog.roodo.com/rocksaying/archives/12452111.html