GNU大型專案構建和覆蓋率生成(第一篇)
目錄
- 0. 序言
- 1. 專案描述
- 2. 專案構建
- 2.1 編譯規則
- 2.2 構建過程
- 3. 覆蓋率分析
0. 序言
在開始正文之前,請允許我先說明一下本文的目的和寫作的動機,好讓讀者不惑。
我們知道,在Linux環境中,很多軟體的組織都遵循GNU軟體標準。不論是自己開發GNU軟體還是閱讀別人寫好的源程式,能瞭解、熟悉GNU專案的構建方式,對我們的工作會起到事半功倍的效果。本文的目的,就是從零開始,告訴大家怎麼構建一個GNU專案,如何閱讀GNU源程式。
文章會涉及到的工具有:
- automake
- autoconf
- aclocal
- gcc、gfotran
- makefile
- libtool
- gcov、 gcovr
在閱讀本文之前,上述工具需要安裝部署完成,同時期望讀者對一下的知識和技能有一定的瞭解和掌握:
- C、C++、Fortran語言
- 基本的編譯、連結過程和原理
- Makefile基本知識
- Linux-shell基本命令
不要被前面的要求嚇到,筆者也是從對上面知識一無所知,經過兩三週時間摸索,寫出了這篇文章的。文章內容涉及到的知識不夠深刻,但是基本上可以幫助像半個月前的筆者一樣茫然的入門者。文章中不嚴謹的表述或者表述、理解方面的錯誤,歡迎大家留言指正。
另外,本文只是告訴大家如何快速的完成一個GNU專案。其中涉及到的autotools工具和相關語言的細節,請大家閱讀文章後面的連結文章。
好了,我們開始正文!
1. 專案描述
這一章節,主要說明我們的目的:
我們最終要完成一個deep
風格的專案組織。專案使用c和Fortran混合程式設計,利用GNU-autotools來完成專案構建,利用gcov完成專案覆蓋率分析。最後形成通過覆蓋率分析優化後的專案釋出包。該釋出包能夠直接釋出在網路,供其他使用者下載、安裝並使用。
專案最終的目錄如下:
➜ csdemo tree . ├── AUTHORS ├── auto.sh ├── ChangeLog ├── data │ └── thch │ └── JANAF ├── doc │ └── README ├── examples ├── makeconfig │ └── make.global ├── NEWS ├── preprocessor ├── README ├── src │ ├── alge │ │ ├── alge.c │ │ └── alge.h │ ├── apps │ │ └──csrun.c │ ├── base │ │ ├── addfld.f90 │ │ └── paramx.f90 │ ├── bft │ │ ├── bft.c │ │ └──bft.h │ ├── cdo │ │ ├── cdo.c │ │ └──cdo.h │ ├── cogz │ │ └── matmul.f90 │ ├── comb │ │ ├── comb.c │ │ └──comb.h │ └── lib └── test
讀者可以在這裡下載原始碼。
下面我們就通過幾個章節的內容,詳細的說明如何從零開始生成這個專案。
2. 專案構建
我們將這個專案放置在csdemo
路徑下面:
這裡:data路徑下面存放專案的資料檔案,doc為專案的說明文件,examples存放使用者案例,test存放測試用例,preprocessor存放前處理相關程式,src存放主體程式程式碼。在src下面alge是c語言檔案,apps是使用者可執行程式,其他子資料夾分別完成一些特定的功能。
將下載到的原始碼分別複製進對應的路徑得到如上文的專案組織。下一步我們就要開始進行專案構建。
專案的構建一般可以通過一下幾步來完成:
- 在每個需要編譯的子路徑下面編寫編譯規則
Makefile.am
檔案。 - 在根目錄(專案根目錄)下執行
autoscan
命令,形成configure.scan
檔案 - 將
configure.scan
檔案命名為configure.ac
- 按照規則修改
configure.ac
檔案 - 執行
aclocal
檔案,形成m4
巨集命令(有時需要手動編寫m4
巨集) - 執行
autoheader
命令生成標頭檔案 - 執行
autoconf
形成config
檔案 - 執行
libtoolize --automake
宣告automake
巨集(這一步按情況選擇執行) - 執行
automake -a
命令,生成makefile.in
檔案 - 執行
./configure
命令,生成makefile
檔案
至此,一個典型的GNU專案構建完成。至於每一步怎麼操作,後文會詳細給出。
在完成上面操作之後,我們就可以釋出自己的軟體,使用者通過:
./configure
make
make install
輕鬆完成軟體的安裝和配置。後面的三個命令是不是很熟悉。
2.1 編譯規則
堅持往下走。我們先來完成第一步,原始碼的編譯。這一步您需要對gcc編譯和連結有一定的瞭解,同時如果您知道目標檔案、靜態庫、動態庫就更好了,他能幫助我們更好的完成本節的內容。
我們平時對於單個或者少量的原始檔直接用gcc
編譯連結,中等大小的專案我們可以手寫Makefile
,但是對於多檔案,手寫Makefile
仍然很繁瑣,這個時候我們就可以使用autotools
套件中的automake
來自動生成makefile
檔案。
- 我們在根目錄下建立檔案
Makefile.am
,編寫如下內容:
## ./Makefile.am ## 雙#表示註釋
SUBDIRS = src ## 遞迴子資料夾
## 需要打包釋出的資料夾和檔案
EXTRA_DIST = doc data examples preprocessor test
- 進入到
src
下面,建立Makefile.am
,編寫如下內容:
##./src/Makefile.am
SUBDIRS = alge base bft cdo comb cogz apps
EXTRA_DIST = lib
- 進入到
src/alge
下面,建立Makefile.am
,編寫如下內容:
##./src/alge/Makefile.am
noinst_LIBRARIES = libalge.a ## 生成靜態庫,字首noinst表示不安裝。
libalge_a_SOURCES = alge.c
AM_CPPFLAGS = -I$(top_srcdir)/src/alge ## AM_CPPFLAGS給出標頭檔案路徑
- 進入到
src/apps
下面,建立Makefile.am
,編寫如下內容:
##./src/apps/Makefile.am
bin_PROGRAMS = csrun
csrun_SOURCES = csrun.c
## 標頭檔案
csrun_CPPFLAGS = \
-I$(top_srcdir)/src/alge \
-I$(top_srcdir)/src/bft \
-I$(top_srcdir)/src/cdo \
-I$(top_srcdir)/src/comb
## 依賴靜態庫
csrun_LDADD = \
$(top_srcdir)/src/alge/libalge.a \
$(top_srcdir)/src/bft/libbft.a \
$(top_srcdir)/src/cdo/libcdo.a \
$(top_srcdir)/src/comb/libcomb.a \
$(top_srcdir)/src/base/libbase.a \
$(top_srcdir)/src/cogz/libcogz.a
AM_CFLAGS = -lgfortran
- 進入到
src/base
下面,建立Makefile.am
,編寫如下內容:
##./src/base/Makefile.am
noinst_LIBRARIES = libbase.a
libbase_a_SOURCES = addfld.f90
AM_FCFLAGS = -I$(top_srcdir)/src/base
- 進入到
src/bft
下面,建立Makefile.am
,編寫如下內容:
##./src/bft/Makefile.am
noinst_LIBRARIES = libbft.a
libbft_a_SOURCES = bft.c
AM_CPPFLAGS = -I$(top_srcdir)/src/bft
- 進入到
src/cdo
下面,建立Makefile.am
,編寫如下內容:
##./src/cdo/Makefile.am
noinst_LIBRARIES = libcdo.a
libcdo_a_SOURCES = cdo.c
AM_CPPFLAGS = -I$(top_srcdir)/src/cdo
- 進入到
src/cogz
下面,建立Makefile.am
,編寫如下內容:
##./src/cogz/Makefile.am
noinst_LIBRARIES = libcogz.a
libcogz_a_SOURCES = matmul.f90
- 進入到
src/comb
下面,建立Makefile.am
,編寫如下內容:
##./src/comb/Makefile.am
noinst_LIBRARIES = libcomb.a
libcomb_a_SOURCES = comb.c
AM_CPPFLAGS = -I$(top_srcdir)/src/comb
為了編寫方便,本文案例中都是用生成靜態庫的方式來完成模組目錄的編譯。當然,使用者也可以生成動態庫或者目標檔案(automake
文件中沒有明確的對目標檔案的支援,但其實可以使用automake
生成目標檔案)。
至此,我們完成了專案構建十步中的第一步。(不用怕,後面的基本都是輸入命令,不用編寫大量檔案。)
2.2 構建過程
- 在完成第一步工作的基礎上,在根目錄執行:
autoscan
在根目錄生成兩個檔案:configure.scan
、autoscan.log
。後者在我們執行這條命令出錯時幫助我們查詢問題,這裡主要關注第一個檔案。
- 將
configure.scan
重新命名為configure.ac
- 開啟
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([src/cdo/cdo.h])
AC_CONFIG_HEADERS([config.h])
# Checks for programs.
AC_PROG_CC
AC_PROG_MAKE_SET
# Checks for libraries.
# FIXME: Replace `main' with a function in `-lgfortran':
AC_CHECK_LIB([gfortran], [main])
# Checks for header files.
# Checks for typedefs, structures, and compiler characteristics.
# Checks for library functions.
AC_CONFIG_FILES([Makefile
src/Makefile
src/alge/Makefile
src/apps/Makefile
src/base/Makefile
src/bft/Makefile
src/cdo/Makefile
src/cogz/Makefile
src/comb/Makefile])
AC_OUTPUT
- 修改檔案成如下(修改的地方打上了註釋,一個
#
開始為註釋):
# -*- Autoconf -*-
# Process this file with autoconf to produce a configure script.
AC_PREREQ([2.69])
AC_INIT(csrun, 1.0, [email protected]) # 三個引數分別為:專案名稱、版本號、bug提交郵箱
AC_CONFIG_SRCDIR([src/apps/csrun.c])
AC_CONFIG_HEADERS([config.h])
AM_INIT_AUTOMAKE[foreign tar-pax] # 這行必須新增,用來指明與automake聯用
# Checks for programs.
AC_PROG_CC # 查詢c編譯器
AC_PROG_FC # 查詢Fortran編譯器
# Checks for libraries.
# Checks for header files.
AC_PROG_RANLIB # 啟用靜態庫編譯
# Checks for typedefs, structures, and compiler characteristics.
# Checks for library functions.
AC_CONFIG_FILES([Makefile
src/Makefile
src/apps/Makefile
src/alge/Makefile
src/base/Makefile
src/bft/Makefile
src/cdo/Makefile
src/comb/Makefile
src/cogz/Makefile]) # 這裡需要指明生成Makefile的路徑
AC_OUTPUT
- 執行
aclocal
命令,這個命令會生成一些m4
檔案。暗示不用過多關注它們。 - 執行
autoheader
,生成config.h
檔案 - 執行
autoconf
,生成configure
檔案 - 執行
automake -a
,生成Makefile.in
檔案(每個makefile.am對應一個Makefile.in) - 執行
./configure
- 執行三部曲.
至此,我們的專案已經構建完成!!
3. 覆蓋率分析
在本部分正文展開之前,讀者可能需要知道什麼是覆蓋率,為什麼需要覆蓋率分析等。這部分的知識可以參考相關文章。
我們利用gcc
提供的gcov
和Python提供的gcovr
兩個工具來分析覆蓋率。對於單個檔案,直接執行
gcov *.c
就可以得到其覆蓋率(具體的生成流程,請參考筆者關於覆蓋率的博文。)
對於大型的複雜專案,生成覆蓋率需要將gcov
和automake
結合使用。我們直接在編譯規則裡面完成對覆蓋率選項的新增。
即,在上文中每一個Makefile.am
檔案中新增如下命令:
AM_CFLAGS += -fprofile-arcs -ftest-coverage ## for c compile
AM_FCFLAGS += -fprofile-arcs -ftest-coverage ## for fortran compile
再執行:
automake -a
./configure
make
這個時候就會看到在相應的原始碼路徑生成對應的.gcon
等檔案。然後在每個目錄下面執行:
gcov *.c
就可以生成覆蓋率檔案。
但是,我們這樣做,需要手動到每個子路徑下面輸入這個命令,比較麻煩,藉助makefile將其簡化。
在每個Makefile.am中新增如下語句:
export MAKEINCLUDE=${top_srcdir}/makeconfig/make.global
include ${MAKEINCLUDE}
cleanall: cleanallsubdirs
-rm -f *.gcda *.gcov *.gcno
gcov: gcovsubdirs
@echo "generating base coverage ..."
gcov -f *.c *.f90
gcovr: gcovrsubdirs
gcovr -r . --html --html-details -o coverage.html
這個時候makefile會幫我們自動遞迴相關的檔案