前言、寫這篇文章的由來
最近在學習韋東山嵌入式培訓視頻(3期項目實戰之USB攝像頭監控)時,在對dhcp源代碼configure時,報錯:cannot check for file existence when cross compiling。雖然按照視頻教程給出的辦法在“./configure --host=arm-linux”之后加上“ac_cv_file__dev_random=yes”,解決了問題。但在看到configure文件中那天書一樣的文字時,總不免很多疑惑:難道為了寫一個dhcp這樣的程序,還要寫個這么晦澀的configure代碼?dhcp的作者,難道如此牛逼么?
經過研究,終於發現,原來這個configure文件並不是直接由人工編寫出來的,而是由autoconf這個工具根據autoconf.ac或者autoconf.in自動生成的。而后者才是人工編寫出來的。而使用autoconf的目的,是為了使我們寫出來的程序(比如這個dhcp)能夠方便的移植到多種unix(或類unix)系統上。
結論雖然很簡單,但厘清其中的來龍去脈,還是花了不少時間。不過我覺得還是值得的,因為了解了來龍去脈之后,就掌握了套路。以后遇到類似的問題,就可以舉一反三,心中有數,而不用再像無頭蒼蠅那樣抓狂了。
一、實驗環境
1.1 虛擬機環境
a) Vmware版本:Vmware Workstation 12.5.7
b) Ubuntu版本:9.10
c) 內核版本:2.6.31.14
1.2 開發板環境
1.2.1 硬件
開發板:百問網JZ2440開發板
wifi網卡:RT3070
1.2.2 軟件
a) 內核版本: 3.4.2
b) toolchain版本:
arm-linux-gcc 4.3.2
c) dhcp版本:4.2.5-P1
二、交叉編譯dhcp,在configure時,報錯信息及解決過程簡記
1、./configure --host=arm-linux
報錯:configure:error: cannot check for file existence when cross compiling
經查,是configure文件line7695:
if test "${ac_cv_file__dev_random+set}" = set; then
echo $ECHO_N "(cached) $ECHO_C" >&6
else
test "$cross_compiling" = yes &&
{ { echo "$as_me:$LINENO: error: cannot check for file existence when cross compiling" >&5
echo "$as_me: error: cannot check for file existence when cross compiling" >&2;}
{ (exit 1); exit 1; }; }
解決辦法:在./configure --host=arm-linux 后面加上: ac_cv_file__dev_random=yes
2、make,報錯:
configure: error: cannot check for file existence when cross compiling
在configure.log里,找到這行:
configure:22156: error: cannot check for file existence when cross compiling
根據這個提示,在configure:22156,找到了:
if eval \${$as_ac_File+:} false; then :
$as_echo_n "(cached) " >&6
else
test "$cross_compiling" = yes &&
as_fn_error $? "cannot check for file existence when cross compiling" "$LINENO" 5
而$as_ac_File定義在:bind/bind-9.8.4-P2/configure:22149 :
as_ac_File=`$as_echo "ac_cv_file_$devrandom" | $as_tr_sh`
解決辦法:修改bind/bind-9.8.4-P2/Makefile:
Line55: ./configure --host=arm-linux ac_cv_file__dev_random=yes
三、原理初探
其實,./configure文件line7695那段天書,是autoconf根據./configure.ac自動生成的:
Line536: AC_CHECK_FILE(/dev/random, AC_DEFINE([HAVE_DEV_RANDOM], [1], [Define to 1 if you have the /dev/random file.]))
類似的,bind/bind-9.8.4-P2/configure的那段天書,也是autoconf根據bind/bind-9.8.4-P2/configure.in自動生成的:
line1087 : AC_CHECK_FILE($devrandom, AC_DEFINE_UNQUOTED(PATH_RANDOMDEV, "$devrandom"),)
關於AC_CHECK_FILE(file, [action-if-found], [action-if-not-found])的作用,簡單說,就是檢查$devrandom代表的文件是否存在,若是則執行action-if-found,否則執行action-if-not-found。
該宏的實現代碼在/usr/share/autoconf/autoconf/general.m4 ,line 2764
這段代碼中間的一大段,是關於cache變量的,目的是為了加速查找結果(詳見https://www.gnu.org/software/autoconf/manual/autoconf-2.64/html_node/Cache-Variable-Names.html#Cache-Variable-Names)
注:若要啟用cache變量,需要在執行configure命令時傳遞 -C或者--config-cache選項,此時可在configure同級目錄下發現config.cache文件,用於存放所有的cache變量,cache變量在內存中為shell變量,當configure啟動時,AC_INIT會調用AC_CACHE_LOAD從config.cache讀入到shell變量里。當configure退出時,會調用AC_CHECK_FILE存放到config.cache文件里)。cache變量名必須包含”_cv_”,意即cache value,若缺了它的話,就無法被cache了。
這段宏的大致解釋如下(假設調用場景就是bind/bind-9.8.4-P2/configure.in里的line1087:AC_CHECK_FILE(/dev/random, AC_DEFINE_UNQUOTED(PATH_RANDOMDEV,"$devrandom"),):
AC_DEFUN([AC_CHECK_FILE], [AC_DIAGNOSE([cross], [cannot check for file existence when cross compiling])dnl #打印一些信息 AS_VAR_PUSHDEF([ac_File], [ac_cv_file_$1])dnl #定義一個臨時宏ac_File,其展開結果是ac_cv_file__dev_random AC_CACHE_CHECK([for $1], [ac_File], #查找 是否有ac_cv_file__dev_random這個cache變量 [test "$cross_compiling" = yes && #若無則檢查$cross_compiling是否為yes AC_MSG_ERROR([cannot check for file existence when cross compiling]) #是則顯示cannot check for...,然后直接退出; if test -r "$1"; then #否則執行test -r"$1" 測試/dev/random是否可讀 AS_VAR_SET([ac_File], [yes]) #是則設置cache變量ac_cv_file__dev_random=yes Else AS_VAR_SET([ac_File], [no]) #否則設置cache變量ac_cv_file__dev_random=no fi]) AS_VAR_IF([ac_File], [yes], [$2], [$3]) #如果cache變量ac_cv_file__dev_random=yes,則執行AC_DEFINE_UNQUOTED(PATH_RANDOMDEV,"$devrandom") AS_VAR_POPDEF([ac_File])dnl #取消臨時宏ac_File ])# AC_CHECK_FILE
由此可知, AC_CHECK_FILE當找不到 ac_cv_file__dev_random這個cache變量時,會先判斷當前是否為交叉編譯,是則顯示cannot check for file existence when cross compiling,然后直接退出。
那么問題來了:為什么當AC_CHECK_FILE遇到交叉編譯的情況,會有這樣的行為特征呢?官網的解釋是:
Be aware that, like most Autoconf macros, they test a feature of the host machine, and therefore, they die when cross-compiling.
意思是說:AC_CHECK_FILE僅是用於檢查宿主機(而不是目標機)上的文件是否存在。所以當遇到交叉編譯的情況,它就沒轍了!
想來這個解釋也有道理,既然configure是運行在宿主機上的,那當然無法去檢查目標機上的文件了。但這樣一來,又回到了問題的原點:為什么dhcp的configure要檢查/dev/random這個文件?我推測應該是由於最終編譯出來的程序需要使用到/dev/random?(不然豈不是瞎折騰么?)
如果確實如此的話,那只要目標機上存在/dev/random(通過”ls /dev |grep random”查下來也確實存在),那即使跳過這個檢查,應該也沒影響。
由此,進一步的問題就是:怎樣讓configure跳過這個檢查?
解決辦法是:只要強制把ac_cv_file__dev_random這個cache變量設置為yes即可,具體來說,有兩個辦法:
辦法1)按照視頻的做法,修改bind/Makefile:
Line55: ./configure --host=arm-linux ac_cv_file__dev_random=yes
辦法2)可能通用性稍好一些
既然ac_cv_file__dev_random是cache 變量,而且./configure.ac和bind/bind-9.8.4-P2/configure.in都訪問了這個變量,那這兩個文件之間是不是能用某種機制來共享這個變量呢?答案是肯定的。在autoconf的官方網站上(https://www.gnu.org/software/autoconf/manual/autoconf-2.64/html_node/Cache-Files.html#Cache-Files)可以查到在 7.4.2 Cache Files里,關於cache files,有這么一段話:
When configure calls configure scripts in subdirectories, it uses the --cache-file argument so that they share the same cache。
也就是說,當父目錄中的configure調用子目錄中的configure時,前者可以通過傳遞給后者--cache-file=xxx來使后者共享cache。
由此,對於本文開頭所述的configure報錯的問題,第2種解決辦法是:
Step1)在執行根目錄下的configure時,加入-C 選項:
./configure --host=arm-linux ac_cv_file__dev_random=yes -C
Step2)修改bind/Makefile:
Line55: ./configure --host=arm-linux --disable-kqueue --cache-file=../../config.cache
四、后記
關於autoconf,如果要深究的話,又是一個大坑。不過對於本項目來說,只要求能把dhcp移植到開發板上,並且能跑起來即可,並不需要從頭開始寫一個dhcp。所以,暫時只需要對autoconf了解個大概就行了。本文可當作一個路標,以使在今后前行的路上遇到類似坑時,不至於懵圈。
五、參考資料
1)韋東山 《嵌入式linux視頻教程_三期項目實戰之USB攝像頭監控》
2)GNU autoconf 官網手冊(https://www.gnu.org/software/autoconf/manual)