纯干货 | 测试人员掌握代码的重要性

作者: 恒生GTN | 来源:发表于2017-03-03 16:57 被阅读219次

    邀约作者:卢凯

    小编友善提示:文章约读10分钟,不如先Mark ^_^

    如果说程序员定义了未来,那测试人员就应该是不让定义的未来偏移原本设定的指示标,指引着未来需要在预期规划中发展。

    01

    这就好比一颗树苗如果想结出硕果,除草,施肥,喷药等维护工作是必不可少的。那对于测试来说这些帮助开发”除草“、“施肥”、“喷药”的工作技能我们必须掌握。在之前的一篇文章“测试人员掌握代码的重要性”中,我已经提及到一些测试人员掌握代码的必要性 ,其中有一点说到了错误的定位。

    那返回之前所说的错误的定位,错误定位我们一般会通过日志去排查,但有的时候,开发写的日志不够详细,或者说日志无法定位到问题,这个时候我们就可能需要自己写一些测试代码到源文件中,帮助我们定位问题,这个时候我们就需要了解一些编译的知识。当然还有很多理由,可以加强我的观点,“了解一些 编译知识是需要的”,比如,为了统计测试用例覆盖率,完善测试用例,我们就需要依赖相关工具,在编译时加入一些编译选项已生成相关的辅助信息便于我们统计覆盖率,类似gcov。

    这自然就引申出了这边文章想分享的内容,makefile的相关知识。我将会以O3投资系统的makefile为例子简要的说明一下makefile。

    首先我们可以把我们的软件实现分为3部,首先是源码编写,然后是编译 ,之后是调试,这是一个循环的工作,因为我们知道代码是永远存在bug的,修改源代码是必不可少的,那对于一个很简单的任务来说,可能我就两三个源文件的时候,我们感受不到编译工作的繁琐,但对于类似于O3这么一个庞然大物,包含上千个源文件,并且源文件又有很多依赖关系的时候,我们在手工输入gcc

    hello.c –o hello.o这种类型的命令是很不明智的,抛开程序最后编译的正确性来讲,这繁琐的工作就已经会让人累的够呛。这个时候引入make将很好的解决这个问题。Make的作用就是根据程序员录入的源文件依赖关系,并且根据时间戳的原理,简化我们编译的过程。

    接下里以O3投资管理系统为例,讲述makefile的运用。

    源文件包含了c,cpp和pc文件,所以编译过程有pc文件到c文件,cpp文件到.o文件,c文件到.o文件,这期间会调用到数据库的某些静态库文件,和头文件,也会调用到ASAR的库文件以及头文件,最后在通过链接ld进行连接生成我们的所需要的动态库.so文件。

    在App用户下的src目录下有一个全局的编译脚本makeall,这个文件的内容就是进入到各个服务下进行各自服务的编译,举一个例子,可以看到makeall文本中cd svr_asset;sh makethis $1 noboot。$1表示的意思就是在终端执行makeall后跟的选项,用这个值作为makethis的一个选项,noboot作为其第二个选项。

    进入观察makethis的内容,其中的关键语句是

    make-f $MAKEFILENAME_FBASE

    TIPS:Make默认情况下会读取makefile,Makefile文件,也可以通过-f参数直接制定读取的文件。

    Make读取$MAKEFILENAME_FBASE所指定的文件,这个变量的值在makethis文件头部已经定义,MAKEFILENAME_FBASE="Makefile_fbase"。所以命令展开就是make –f Makefile_fbase。

    到这里我们就知道了我们Makefile_fbase就是程序员编辑源代码之间的依赖关系的文件,make指令就是通过这个文件知道我们svr_asset下各文件的依赖关系,以及可能需要调用的外部文件。

    一个makefile,在我看来就是两个部分,一个是变量,一个是规则。这里我们先举一个简单的规则做一个说明,之后再对Makefile_fbase做详细解读。

    这一条规则总共分为三个部分。第一个是hello.o:,这个表明了我最终想生成的目标文件是hello.o;第二个是hello.c,这个表示我生成目标文件hello.o所需要的依赖文件,这;里就是hello.h;第三个是生成目标文件的命令。

    当我的当前目录下有hello.c,和makefile文件时,我在终端直接执行make。Make就会默认读取makefile以gcc hello.c –o hello.o命令生成我们的hello.o目标文件。我们将它扩展为通式就是

    Target:prereq1 prereq2

    Commands

    其中Target表示工作目标,prereq1 prereq2表示必要条件,Commands表示所要执行的命令。注意commands前面必须填充一个制表符,不然会报语法错误。

    这个时候我们转向我们的Makefile_fbase文件,就像我之前说的。Makefile有两部分,一部分是变量。通常这一部分都放到makefile的头部。观察我们Makefile_fbase文件,可以看到如下:

    这里我说明一下makefile变量的来源:

    1.makefile中定义,或者通过include命令引入其他文件,其他文件中定义的变量。

    2.运行make时输入的变量,如make CFLAGS=-g

    3.环境变量

    4.自动变量

    include../../Makefile_fbase.hs    include ./SubMakefile这两条命令会引入Makefile_fbase.hs和SubMakefile定义的变量。如Makefile_fbase.hs定义的CC,编译时采用的编译器, DBUSER编译proc文件时登陆数据库的连接串,COMMONINC头文件搜索路径,COMMONLIB库文件的搜索路径等,以及SubMakefile定义的START_FUC功能号字段,TMP_PROGS编译时需要编译的外部文件,ANAME目标文件名等。

    PREINCL=$(ORACLE_HOME)/precomp

    /public这里的$(ORACLE_HOME)就是取自于环境变量,所以我们会在.bahs_profile中export ORACLE_HOMEE=XXXXXX。这个时候PREINCL的值展开后就会是XXXXXX/precomp/public。这里还有一个环境变量TUXDIR这个是我们makefile中编译的时候,代表ASAR的目录,但是他并没有在相关文件中定义,所以我们可以将其在.bahs_profile定义export TUXDIR=/home/cres/workspace,当然也可以在文件中定义,这都没事。

    02

    那何为自动变量呢?

    以下面这条命令为例

    $(CC)$(CFLAGS) $(INCDIR) -c  $< -o $@

    其中$<,$@就是所谓的自动变量,它会在规则每次执行时都基于目标和依赖产生新值。

    这里我罗列一些常见的自动变量,其他的大家可以自己去查找资料。

    1.$@:表示工作目标的文件名

    2.$%:档案文件成员结构中的文件名元素。

    3.$<:第一个必要条件的文件名

    4.$?:时间戳在工作目标(时间戳)之后的所有必要条件,并以空格隔开这些必要条件。

    5.$^:所有必要条件的文件名,并以空格隔开这些文件名。这份列表已经删除重复的文件名,因为对大多数的应用而言,比如编译,复制等,并不会用到重复的文件名。

    6.$+:代表所有的必要条件文件名,并以空格隔开这些文件名。不过,$+包含了重复的文件名。此变量会在特殊的状况下被创建,比如将自变量传递给连接器时重复的值是有意义的。

    7.$*:工作目标的主文件名。一个文件名称是有两部分组成:主文件名和扩展名。

    TIPS变量的优先级

    1.命令行上的变量优先级高于文件中定义的变量,可以通过override关键字使变量取makefile文件中的值

    2.环境变量的优先级最低,可以通过—environment-overrides或者是-e命令选项,让环境变量的值覆盖makefile的变量值。

    自动变量的展开,我在下面规则的讲解中说明一个例子。

    03

    变量的类型说完了,上面大部分的内容也应该可以看懂了。但是OBJ1=$(PROGS:.c=.o)

    这个表示什么意思了,有人可能会问到。其实,这个只要知道变量的赋值便可以理解了。

    Makefile的变量赋值基本上有如下几种:

    1.立即赋值a:=b

    2.延迟赋值a=b

    3.条件赋值a?=b

    4.附加赋值a+=b

    5.替换赋值$(var:a=b)

    这里就不想展开讲了,给一个名字,大家想了解可以自行百度下。这里就说明一下替换赋值,$(var:a=b)”其意思是,把变量“var”中所有以“a”字串“结尾”的“a”替换成“b”字串。这里的“结尾”意思是“空格”或是“结束符”,进而$(PROGS:.c=.o)意思就是PROGS这个变量中凡是以,c结尾的都替换成.o结尾的。

    PROGS首先这个变量是从PROGS = $(FBASE_MAIN) $(TMP_PROGS)这个语句中赋值得到,那其实就是FBASE_MAIN,TMP_PROGS变量展开得到,这个我就展开TMP_PROGS来说明。这个变量是在Submakefile中定义的,我们可以在文件中看到它的赋值情况如下:

    OBJ2= $(OBJ1:.pc=.c)

    这个命令的意思就是将OBJ1变量展开后其中以.pc结尾的全部替换成.c之后的结果赋值给OBJ2。

    其实OBJ1,OBJ2,OBJ3,OBJ4在后续的规则中就会相应的表示c文件编译成.o对象文件,pc文件编译成c文件,cpp文件编译成对象文件,再将后续生成的由pc文件编译得到的c文件编译成对象文件。

    04

    Makefie_fbase中用到的变量的知识这里差不多都讲到了,那接下来就是说一下规则这点事情。

    首先先要弄清楚一点,一般来说,make的最终目标,或者说是默认目标是makefile的第一个目标,而其他目标应当是由这个目标引导出来的,这个是make的默认行为。

    像这么一个makefile文件里面有两个工作目标分别是count和count.o,那么make会以count作为它的默认目标,因为这个工作目标在makefile的前面,当count.c的时间戳大于count.o,这个时候执行make的时候,默认的工作目标就会根据文件的依赖关系找到第二个工作目标,已更新count.o,进而在编译更新count。

    当然我们也可以通过make执行特定的工作目标,比如上述的例子,我就可以执行make count.o。这样就会只执行count.o目标的规则了。

    而一般来讲,我们的makefile里面会有多个目标的,通常为了方便我们会定义伪目标。比如all,例如

    .PHONY:all

    all: prog1 prog2 prog3 prog4

    这个时候我们就可以使用make all来执行所有的目标。

    Makefile的规则应该可以简单归类成两种,他们分别是显式规则和隐含规则。

    显示规则意思就是指由程序员明显的指出,要生成的文件,所需要的依赖文件,以及生成目标文件的命令,如上面举的例子。

    hello.o:hello.c

    gcchello.c –o hello.o

    那隐式规则则需要由make自动推到出来要生成的文件,所需要的依赖文件,以及生成目标文件的命令。这里我们将介绍我们Makefile_fbase所需用到的隐式规则,后缀规则。

    TIPS

    文件名的组成由“主文件名”和”扩展名”组成,如hello.c,这个文件的主文件名就是hello,扩展名就是.c。

    05

    我们其实知道在linux系统上c源文件编译出来的对象文件就是.o文件,cpp源文件编译出来的对象文件也是.o文件,makefile的后缀规则就是利用这点,它的规则格式如下:

    .c.o:

    Commands

    这个推到成我们的显示规则就是,目标文件是.o文件,依赖文件是.c文件,执行的命令就是commands。就比如我目录下有hello.c.那运用这条规则。

    Hello.o:hello.c

    Commands

    我们想新加入一些后缀规则,举例来说就pc生成c文件,我们就可以用.SUFFIXES加入想加入的后缀。那有的时候我们害怕makefile默认的已知扩展名列表干扰我们的运行,那可以通过

    .SUFFIXES:

    这样一条命令清除原来的默认值,再添加自己想要增加的后缀名,如:

    .SUFFIXES:.pc .cpp .c .o

    06

    到目前为止,理解这个Makefile_fbase,所需要的知识应该是具备了。那我们运行一下这个makefile。Make会读入makefile以及用include引入的文件,再将变量全部展开,推到隐含规则,之后为每个目标建立依赖图,最后生成目标文件。

    像之前所说的,make会找到它的默认目标,Makefile_fbase中就指的是伪目标all。展开就是all: $(ANAME)》all:svr_asset。然后svr_asset也是一个伪目标,make会找到$(ANAME): $(OBJS),展开后就是svr_asset: fbase_main.o ../public/hsfbase_comm.o(后面就不列出了),接着make发现fbase_main.o也不存在,make就会找生成这个.o文件的规则,就找到了如下规则

    这里的两个自动变量$<和$@分别表示了fbase_main.cpp,fbase_main.o。

    然后返回到之前的规则继续运行,这时候发现../public/hsfbase_comm.o也不存在,同理也是按照上述的步骤进行编译,这个时候自动变 量$<和$@就分别表示../public/hsfbase_comm.cpp和../public/hsfbase_comm.o

    如果发现后面的必要条件是以.pc结尾的,make就会找到以下规则进行编译

    同理,如果是以.c结尾的,就会运行

    这条规则进行编译。当所有的展开项全部编译更新完成之后,这个时候就会执行

    这里具体就不展开了。当这个规则执行完之后,又反回到all: $(ANAME)规则,下面已经没有命令了,所以整个make工作就结束了。

    注意到这里,我从来没有说依赖文件和目标文件的时间戳问题,这个是因为在makethis这个脚本中先调用了make –f Makefile_fbase

    clean将所有编译的中间过程全部删除了。所以这就导致了我们的我们编译的时候源文件总是比目标文件新。

    07

    上述内容便是这边文章我想分享的东西,因为我也是个初学者,这其中肯定也会从在某些错误,如果有发现,请大家指出,我加以修改。还有一点就是make的知识 并不是只有这么一点,我这里所讲到的只是我们能读懂O3的makefile所需要的某些知识点,希望后续我能更加努力,研究的更深入,分享一些更值得研究的话题。谢谢!!

    相关文章

      网友评论

        本文标题:纯干货 | 测试人员掌握代码的重要性

        本文链接:https://www.haomeiwen.com/subject/yhghgttx.html