GNU大型項目構建和覆蓋率生成(第一篇)



0. 序言

在開始正文之前,請允許我先說明一下本文的目的和寫作的動機,好讓讀者不惑。

我們知道,在Linux環境中,很多軟件的組織都遵循GNU軟件標准。不論是自己開發GNU軟件還是閱讀別人寫好的源程序,能了解、熟悉GNU項目的構建方式,對我們的工作會起到事半功倍的效果。本文的目的,就是從零開始,告訴大家怎么構建一個GNU項目,如何閱讀GNU源程序。

文章會涉及到的工具有:

  • automake
  • autoconf
  • aclocal
  • gcc、gfotran
  • makefile
  • libtool
  • gcov、 gcovr

在閱讀本文之前,上述工具需要安裝部署完成,同時期望讀者對一下的知識和技能有一定的了解和掌握:

  1. C、C++、Fortran語言
  2. 基本的編譯、鏈接過程和原理
  3. Makefile基本知識
  4. 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是用戶可執行程序,其他子文件夾分別完成一些特定的功能。

將下載到的源碼分別復制進對應的路徑得到如上文的項目組織。下一步我們就要開始進行項目構建。

項目的構建一般可以通過一下幾步來完成:

  1. 在每個需要編譯的子路徑下面編寫編譯規則Makefile.am文件。
  2. 在根目錄(項目根目錄)下執行autoscan命令,形成configure.scan文件
  3. configure.scan文件命名為configure.ac
  4. 按照規則修改configure.ac文件
  5. 執行aclocal文件,形成m4宏命令(有時需要手動編寫m4宏)
  6. 執行autoheader命令生成頭文件
  7. 執行autoconf形成config文件
  8. 執行libtoolize --automake聲明automake宏(這一步按情況選擇執行)
  9. 執行automake -a命令,生成makefile.in文件
  10. 執行./configure命令,生成makefile文件

至此,一個典型的GNU項目構建完成。至於每一步怎么操作,后文會詳細給出。

在完成上面操作之后,我們就可以發布自己的軟件,用戶通過:

./configure

make

make install

輕松完成軟件的安裝和配置。后面的三個命令是不是很熟悉。

2.1 編譯規則

堅持往下走。我們先來完成第一步,源碼的編譯。這一步您需要對gcc編譯和鏈接有一定的了解,同時如果您知道目標文件、靜態庫、動態庫就更好了,他能幫助我們更好的完成本節的內容。

我們平時對於單個或者少量的源文件直接用gcc編譯鏈接,中等大小的項目我們可以手寫Makefile,但是對於多文件,手寫Makefile仍然很繁瑣,這個時候我們就可以使用autotools套件中的automake來自動生成makefile文件。

  1. 我們在根目錄下建立文件Makefile.am,編寫如下內容:
## ./Makefile.am   ## 雙#表示注釋
SUBDIRS          = src      ## 遞歸子文件夾
## 需要打包發布的文件夾和文件
EXTRA_DIST       = doc data examples preprocessor test
  1. 進入到src下面,建立Makefile.am,編寫如下內容:
##./src/Makefile.am
SUBDIRS          = alge base bft cdo comb cogz apps
EXTRA_DIST       = lib
  1. 進入到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給出頭文件路徑
  1. 進入到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
  1. 進入到src/base下面,建立Makefile.am,編寫如下內容:
##./src/base/Makefile.am
noinst_LIBRARIES = libbase.a
libbase_a_SOURCES = addfld.f90

AM_FCFLAGS      = -I$(top_srcdir)/src/base
  1. 進入到src/bft下面,建立Makefile.am,編寫如下內容:
##./src/bft/Makefile.am
noinst_LIBRARIES   = libbft.a
libbft_a_SOURCES = bft.c

AM_CPPFLAGS      = -I$(top_srcdir)/src/bft
  1. 進入到src/cdo下面,建立Makefile.am,編寫如下內容:
##./src/cdo/Makefile.am
noinst_LIBRARIES   = libcdo.a
libcdo_a_SOURCES = cdo.c

AM_CPPFLAGS      = -I$(top_srcdir)/src/cdo
  1. 進入到src/cogz下面,建立Makefile.am,編寫如下內容:
##./src/cogz/Makefile.am
noinst_LIBRARIES = libcogz.a
libcogz_a_SOURCES = matmul.f90
  1. 進入到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 構建過程

  1. 在完成第一步工作的基礎上,在根目錄執行:
autoscan

在根目錄生成兩個文件:configure.scanautoscan.log。后者在我們執行這條命令出錯時幫助我們查找問題,這里主要關注第一個文件。

  1. configure.scan重命名為configure.ac
  2. 打開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
  1. 修改文件成如下(修改的地方打上了注釋,一個#開始為注釋):
#                                               -*- Autoconf -*-
# Process this file with autoconf to produce a configure script.

AC_PREREQ([2.69])
AC_INIT(csrun, 1.0, mx@mx.com)  # 三個參數分別為:項目名稱、版本號、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
  1. 執行aclocal命令,這個命令會生成一些m4文件。暗示不用過多關注它們。
  2. 執行autoheader,生成config.h文件
  3. 執行autoconf,生成configure文件
  4. 執行automake -a,生成Makefile.in文件(每個makefile.am對應一個Makefile.in)
  5. 執行./configure
  6. 執行三部曲.

至此,我們的項目已經構建完成!!


3. 覆蓋率分析

在本部分正文展開之前,讀者可能需要知道什么是覆蓋率,為什么需要覆蓋率分析等。這部分的知識可以參考相關文章。

我們利用gcc提供的gcov和Python提供的gcovr兩個工具來分析覆蓋率。對於單個文件,直接執行

gcov *.c

就可以得到其覆蓋率(具體的生成流程,請參考筆者關於覆蓋率的博文。)

對於大型的復雜項目,生成覆蓋率需要將gcovautomake結合使用。我們直接在編譯規則里面完成對覆蓋率選項的添加。

即,在上文中每一個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會幫我們自動遞歸相關的文件夾。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM