zoukankan      html  css  js  c++  java
  • 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会帮我们自动递归相关的文件夹。

  • 相关阅读:
    CI框架 QQ接口(第三方登录接口PHP版)
    linux定时任务的设置
    WEB服务器:Apache、Tomcat、JBoss、WebLogic、Websphere、IIS的区别与关系
    php专业面试总结
    php常用数学函数
    UVA 1428 Ping pong
    UVA 11988 Broken Keyboard (a.k.a. Beiju Text)
    UVA 11991 Easy Problem from Rujia Liu?
    UVA 11995 I Can Guess the Data Structure!
    UVA 10375 Choose and divide
  • 原文地址:https://www.cnblogs.com/baowee/p/11491854.html
Copyright © 2011-2022 走看看