gun工具链与makefile

1 GNU的由来与发展

GNU 是由“GNU's Not Unix”所递规定义出的首字母缩写语。GNU计划是由Richard Stallman在1983年9月27日公开发起的。它的目标是创建一套完全自由的操作系统。RichardStallman最早是在net.unix-wizards新闻组上公布该消息,并附带一份《GNU宣言》解释为何发起该计划的文章,其中一个重要的理由就是要“重现当年软件界合作互助的团结精神”。GNU工程已经开发了一个被称为“GNU”的、对Unix向上兼容的完整的自由软件系统(free software system)。由Richard Stallman完成的最初的GNU工程的文档被称为“GNU宣言”。

每个计算机的使用者都需要一个操作系统;如果没有自由的操作系统,那么如果他不求助于私有软件,就甚至不能开始使用一台计算机。所以自由软件议事日程的第一项就是自由的操作系统。一个操作系统不仅仅是一个内核;它还包括编译器、编辑器、电子邮件软件,和许多其他东西。因此,创作一个完整的操作系统是一项十分庞大的工作。由于Unix的全局设计已经得到认证并且广泛流传,所以GNU开发者们决定使操作系统与Unix兼容。同时这种兼容性使Unix的使用者可以容易地转移到GNU上来。

在1991年Linux的第一个版本公开发行时,GNU计划已经完成除操作系统内核之外的大部分软件,比如GNU BashGCC等等。于是Linus的内核和GNU软件的合理组合,构成了GNU/Linux这一优异的操作系统:一个基于LinuxGNU系统。

GCC(GNU CompilerCollection)是GNU组织开发的一个编译器。目前支持的语言有 C、C++、Objective-C、Fortran、Java和Ada等。

自由软件可以走多远?这没有限制,除非诸如版权法之类的法律完全地禁止自由软件。最终的目的是,让自由软件完成计算机用户希望完成的所有工作--从而导致自由软件的过时。

 

2 编译器

2.2.1 GCC简介

Linux系统下的GCC是GNU推出的功能强大、性能优越的多平台编译器,是GNU的代表作品之一。GCC是可以在多种硬体平台上编译出可执行程序的超级编译器,其执行效率与一般的编译器相比平均效率要高20%~30%

最初,GCC只是一个C语言编译器,当时是“GNU C Compiler”的英文缩写。随着众多开发者的加入和GCC自身的发展,如今的GCC已经是一个包含众多语言的编译器了,其中包括 C,C++,Ada,Object-C和Java等。所以,GCC的全称也由原来的“GNU CCompiler”演变为现在的“GNU Compiler Collection”,即GNU编译器家族的意思。

2.2.2 GCC特点

GCC不仅是GNU/Linux上的标准编译器,而且它也是嵌入式系统开发的标准编译器,这是因为GCC支持各种不同的目标架构。本书将专注于FPGA平台的嵌入式系统开发,其中的软件部分运行在Microblaze或者PowerPC处理器上,为了使我们的应用程序能够运行在不同的目标机上,我们使用交叉编译工具对程序进行交叉编译。所谓交叉编译就是在某个主机平台上(比如PC上)编译出可在其他平台上(比如ARM上)运行代码的过程GCC提供了40种不同的结构体系。其中包括X86,RS6000,Arm,PowerPC等等,用户可以根据实际的项目平台来进行应用程序的开发。

GCC编译器能将C、C++语言源程序、汇编程序和目标程序编译、链接成可执行文件,如果没有给出可执行文件的名字,GCC将生成一个名为a.out的文件。在Linux系统中,可执行文件没有统一的后缀,系统从文件的属性来区分可执行文件和不可执行文件而GCC则通过后缀来区别输入文件的类别,下面我们来介绍GCC所遵循的部分约定规则:

.c为后缀的文件,C语言源代码文件;

.a为后缀的文件,是由目标文件构成的档案库文件;

.C、.cc或.cxx 为后缀的文件,是C++源代码文件;

.h为后缀的文件,是程序所包含的头文件;

.i 为后缀的文件,是已经预处理过的C源代码文件

.ii为后缀的文件,是已经预处理过的C++源代码文件;

.m为后缀的文件,是Objective-C源代码文件;

.o为后缀的文件,是编译后的目标文件;

.s为后缀的文件,是汇编语言源代码文件

.S为后缀的文件,是经过预编译的汇编语言源代码文件。

2.2.3 GCC执行过程

  虽然我们称GCC是C语言的编译器,但使用GCC由C语言源代码文件生成可执行文件的过程不仅仅是编译的过程,而是要经历四个相互关联的步骤∶预处理(也称预编译,Preprocessing)、编译(Compilation)、汇编(Assembly)和链接(Linking),如图2-1所示。在对程序进行开发的过程中,我们可以通过添加参数对程序单独执行其中的某个过程。

图2-1 GCC执行过程

命令GCC首先调用cpp进行预处理,在预处理过程中,对源代码文件中的文件包含(include)、预编译语句(如宏定义define等)进行分析。接着调用cc1或g++进行编译,这个阶段根据输入文件生成以.o为后缀的目标文件。汇编过程是针对汇编语言的步骤,调用as进行工作,一般来讲,.s为后缀的汇编语言文件经过预编译和汇编之后都生成以.o为后缀的目标文件。当所有的目标文件都生成之后,GCC就调用ld命令来完成最后的关键性工作,这个阶段就是链接,当然,也可以使用GCC命令直接完成链接功能。在链接阶段,所有的目标文件被安排在可执行程序中的恰当的位置,同时,该程序所调用到的库函数也从各自所在的档案库中连到合适的地方

2.2.4 GCC基本用法与选项

 在使用GCC编译器的时候,我们必须给出一系列必要的调用参数和文件名称。GCC编译器的调用参数大约有100多个,但其中多数参数很少会用到,所以这里只介绍其中最基本、最常用的参数

 GCC最基本的用法是∶gcc [options] [filenames] ,其中options就是编译器所需要的参数,filenames给出相关的文件名称,表2-1列出了常用参数的意义。

选项

            解释

-ansi

只支持 ANSI 标准的 C 语法

-c 

只编译并生成目标文件

-DMACRO

以字符串“1”定义 MACRO 宏

-DMACRO=DEFN

以字符串“DEFN”定义 MACRO 宏

-E 

只运行 C 预编译器

-g

生成调试信息

-IDIRECTORY

指定额外的头文件搜索路径DIRECTORY

-LDIRECTORY

指定额外的函数库搜索路径DIRECTORY

            -lLIBRARY

连接时搜索指定的函数库LIBRARY

-o FILE

生成指定的输出文件

-O0   

不进行优化处理

-O -O1

优化生成代码

-O2

进一步优化

-O3

比 -O2 更进一步优化,包括 inline 函数

-static

禁止使用共享连接

-w

不生成任何警告信息

-Wall

生成所有警告信息

                           表2-1 GCC常用参数

上面我们简要介绍了GCC编译器最常用的功能和主要参数选项,更为详尽的资料可以参考http://gcc.gnu.org/。 假定我们有一个程序名为test.c的C语言源代码文件,要生成一个可执行文件,最简单的办法就是:

gcc test.c

 这时,预编译、编译链接一次完成,生成一个系统预设的名为a.out的可执行文件,对于稍为复杂的情况,比如有多个源代码文件、需要链接档案库或者有其他比较特别的要求,就要给定适当的调用选项参数。再看一个简单的例子。

 整个源代码程序由两个文件test1.c 和test2.c组成,程序中使用了系统提供的数学库,同时希望给出的可执行文件为test,这时的编译命令可以是∶

gcc test1.c test2.c -lm-o test

其中,-lm表示链接系统的数学库libm.a

2.2.5 Gdb调试器

  调试是所有程序员都会面临的问题。如何提高程序员的调试效率,更好更快的定位程序中的问题从而加快程序开发的进度,是大家共同面对的。就如读者熟知的Windows下的一些调试工具,如VC自带的如设置断点、单步跟踪等,都受到了广大用户的赞赏。那么,在Linux下有什么很好的调试工具呢?

  Gdb调试器是一款GNU开发组织并发布的UNIX/Linux下的程序调试工具。虽然,它没有图形化的友好界面,但是它强大的功能也足以与微软的VC工具等媲美。

首先,打开Linux下的编辑器Vi或者Emacs,编辑如下代码。

/*test.c*/

#include <stdio.h>

int sum(int m);

int main()

{

     int i,n=0;

     sum(50);

     for(i=1; i<=50; i++)

      {

        n += i;

      }

     printf("The sum of 1-50 is %d \n", n );

 }

int sum(int m)

{

        int i,n=0;

        for(i=1; i<=m;i++)

           n += i;

        printf("The sum of 1-m is %d\n", n);

}

在保存退出后首先使用Gcc对test.c进行编译,注意一定要加上选项”-g”,这样编译出的可执行代码中才包含调试信息,否则之后Gdb无法载入该可执行文件。

[root@localhostGdb]# gcc -g test.c -o test

虽然这段程序没有错误,但调试完全正确的程序可以更加了解Gdb的使用流程。接下来就启动Gdb进行调试。注意,Gdb进行调试的是可执行文件,而不是如”.c”的源代码,因此,需要先通过Gcc编译生成可执行文件才能用Gdb进行调试。

[root@localhostGdb]# gdb test

GNU Gdb Red HatLinux (6.3.0.0-1.21rh)

Copyright 2004Free Software Foundation, Inc.

GDB is freesoftware, covered by the GNU General Public License, and you are

welcome to changeit and/or distribute copies of it under certain conditions.

Type "showcopying" to see the conditions.

There is absolutelyno warranty for GDB.  Type "show warranty" for details.

This GDB was configured as "i386-redhat-linux-gnu"...Usinghost libthread_db library "/lib/libthread_db.so.1".

(gdb)

可以看出,在Gdb的启动画面中指出了Gdb的版本号、使用的库文件等信息,接下来就进入了由“(gdb)”开头的命令行界面了。

(1)查看文件

在Gdb中键入”l”(list)就可以查看所载入的文件,如下所示:

(Gdb) l

1      #include <stdio.h>

2      int sum(int m);

3      int main()

4      {

5            int i,n=0;

6            sum(50);

7            for(i=1; i<=50; i++)

8             {

9               n += i;

10            }

(Gdb) l

11           printf("The sum of 1~50 is %d \n", n );

12      

13     }

14     int sum(int m)

15     {

16                int i,n=0;

17                for(i=1; i<=m;i++)

18                     n += i;

19                printf("The sum of 1~m is = %d\n", n);

20     }

 

可以看出,Gdb列出的源代码中明确地给出了对应的行号,这样就可以大大地方便代码的定位。

(2)设置断点

设置断点是调试程序中是一个非常重要的手段,它可以使程序到一定位置暂停它的运行。因此,程序员在该位置处可以方便地查看变量的值、堆栈情况等,从而找出代码的症结所在。

在Gdb中设置断点非常简单,只需在b”后加入对应的行号即可(这是最常用的方式,另外还有其他方式设置断点)。如下所示:

 

(Gdb) b 6

Breakpoint 1 at0x804846d: file test.c, line 6.

 

要注意的是,在Gdb中利用行号设置断点是指代码运行到对应行之前将其停止,如上例中,代码运行到第五行之前暂停(并没有运行第五行)。

(3)查看断点情况

在设置完断点之后,用户可以键入”info b来查看设置断点情况,在Gdb中可以设置多个断点。

 

(Gdb) info b

NumType           Disp EnbAddress    What

1  breakpoint     keep y   0x0804846d in main attest.c:6

 

(4)运行代码

接下来就可运行代码了,Gdb默认从首行开始运行代码,可键入”r”(run)即可(若想从程序中指定行开始运行,可在r后面加上行号)。

(Gdb) r

Starting program:/root/workplace/Gdb/test

Reading symbols from shared objectread from target memory...done.

Loaded systemsupplied DSO at 0x5fb000

 

Breakpoint 1,main () at test.c:6

6                sum(50);

 

可以看到,程序运行到断点处就停止了。

(5)查看变量值

在程序停止运行之后,程序员所要做的工作是查看断点处的相关变量值。在Gdb中只需键入”p”+变量值即可,如下所示:

 

(Gdb) p n

$1 = 0

(Gdb) p i

$2 = 134518440

 

在此处,为什么变量”i”的值为如此奇怪的一个数字呢?原因就在于程序是在断点设置的对应行之前停止的,那么在此时,并没有把”i”的数值赋为零,而只是一个随机的数字。但变量”n”是在第四行赋值的,故在此时已经为零。

(6)单步运行

单步运行可以使用命令”n”(next)或”s”(step,它们之间的区别在于:若有函数调用的时候,”s”会进入该函数而”n”不会进入该函数。因此,”s”就类似于VC等工具中的”step in”,”n”类似与VC等工具中的”stepover”。它们的使用如下所示:

 

(Gdb) n

The sum of 1-m is 1275

7           for(i=1; i<=50; i++)

(Gdb) s

sum (m=50) at test.c:16

16             int i,n=0;

 

可见,使用”n”后,程序显示函数sum的运行结果并向下执行,而使用”s”后则进入到sum函数之中单步运行。

(7)恢复程序运行

在查看完所需变量及堆栈情况后,就可以使用命令”c”(continue)恢复程序的正常运行了。这时,它会把剩余还未执行的程序执行完,并显示剩余程序中的执行结果。以下是之前使用”n”命令恢复后的执行结果:

 (Gdb) c

Continuing.

The sum of 1-50 is :1275

 Program exited with code 031.

可以看出,程序在运行完后退出,之后程序处于“停止状态”。

2.3 自动编译

2.3.1 Make工程管理

到此为止,我们已经了解了如何在Linux下使用编辑器编写代码,如何使用Gcc把代码编译成可执行文件,还学习了如何使用Gdb来调试程序,那么,所有的工作看似已经完成了,为什么还需要Make这个工程管理器呢?

所谓工程管理器,顾名思义,是指管理较多的文件的。可以试想一下,有一个上百个文件的代码构成的项目,如果其中只有一个或少数几个文件进行了修改,按照之前所学的Gcc编译工具,就不得不把这所有的文件重新编译一遍,因为编译器并不知道哪些文件是最近更新的,而只知道需要包含这些文件才能把源代码编译成可执行文件。于是,程序员就不能不再重新输入数目如此庞大的文件名以完成最后的编译工作。

但是,仔细回想一下本书在2.2.3节中所阐述的编译过程,编译过程是分为编译、汇编、链接不同阶段的,其中编译阶段仅检查语法错误以及函数与变量的声明是否正确声明了,在链接阶段则主要完成是函数链接和全局变量的链接。因此,那些没有改动的源代码根本不需要重新编译,而只要把它们重新链接进去就可以了。所以,人们就希望有一个工程管理器能够自动识别更新了的文件代码,同时又不需要重复输入冗长的命令行,这样,Make工程管理器也就应运而生了。

实际上,Make工程管理器也就是个“自动编译管理器”,这里的“自动”是指它能够根据文件时间戳自动发现更新过的文件而减少编译的工作量,同时,它通过读入Makefile文件的内容来执行大量的编译工作。用户只需编写一次简单的编译语句就可以了。它大大提高了实际项目的工作效率,而且几乎所有Linux下的项目编程均会涉及到它。

2.3.2 Makefile结构

makefile描述了整个工程的编译规则,通过make命令自动化编译。

Make是一个解释makefile 中指令的命令工具,大多数的IDE都有这个命令,比如:

• Delphi的make,

• Visual C++的nmake

• Linux下GNU的make

 

Makefile是Make读入的惟一配置文件,因此本节的内容实际就是讲述Makefile的编写规则。在一个Makefile中通常包含如下内容

· target是一个目标文件,可以是ObjectFile,也可以是可执行文件,还可以是一个标签(Label

· prerequisites:要生成那个target所需要的文件或是目标

· Command:make需要执行的命令 。

它的格式为:

Target:prerequisites

Command

 

例如,有两个文件分别为hello.c和hello.h,创建的目标体为hello.o,执行的命令为gcc编译指令:gcc –c hello.c,那么,对应的Makefile就可以写为:

hello.o: hello.c hello.h

       gcc –c hello.c –ohello.o

 

接着就可以使用make了。使用make的格式为:make target,这样make就会自动读入Makefile(也可以是首字母小写makefile)并执行对应targetcommand语句,并会找到相应的依赖文件。如下所示:

[root@localhost makefile]# make hello.o

gcc –c hello.c –o hello.o

[root@localhost makefile]# ls

hello.c  hello.h  hello.o  Makefile

 可以看到,Makefile执行了“hello.o”对应的命令语句,并生成了“hello.o”目标体。

注意:每一个命令的第一个字符必须是tab”键,不可使用8个“space”键替代,否则make会显示出错信息

2.3.3 makefile变量

上面示例的Makefile在实际中是几乎不存在的,因为它过于简单,仅包含两个文件和一个命令,在这种情况下完全不必要编写Makefile而只需在Shell中直接输入即可,在实际中使用的Makefile往往是包含很多的文件和命令的,这也是Makefile产生的原因。下面就可给出稍微复杂一些的Makefile(2个头文件,5个C文件)进行讲解:

edit : main.okbd.o
       cc -o edit main.o kbd.o
main.o : main.c defs.h
       cc -c main.c
kbd.o : kbd.c defs.h command.h
       cc -c kbd.c
clean :
       rm edit main.o kbd.o

在这个Makefile中有三个目标体(target),分别为edit、main.o和kbd.o,其中第一个目标体的依赖文件就是后两个目标体。如果用户使用命令“makeedit”,则make管理器就是找到edit目标体开始执行。

这时,make会自动检查相关文件的时间戳。首先,在检查“main.o”、“kbd.o”和“edit”三个文件的时间戳之前,它会向下查找那些把“main.o”或“kbd.o”做为目标文件的时间戳。比如,“main.o”的依赖文件为:“main.c”、“defs.h”。如果这些文件中任何一个的时间戳比“main.o”新,则命令“gcc –Wall –O -g –c main.c -omain.o”将会执行,从而更新文件“main.o”。在更新完“main.o”或“kbd.o”之后,make会检查最初的“main.o”、“kbd.o”和“edit”三个文件,只要文件“main.o”或“kbd.o”中的任比文件时间戳比“edit”新,则第二行命令就会被执行。这样,make就完成了自动检查时间戳的工作,开始执行编译工作。这也就是Make工作的基本流程。

接下来,为了进一步简化编辑和维护Makefile,make允许在Makefile中创建和使用变量。变量是在Makefile中定义的名字,用来代替一个文本字符串,该文本字符串称为该变量的值。在具体要求下,这些值可以代替目标体、依赖文件、命令以及makefile文件中其它部分。在Makefile中的变量定义有两种方式:一种是递归展开方式,另一种是简单方式。

递归展开方式定义的变量是在引用在该变量时进行替换的,即如果该变量包含了对其他变量的应用,则在引用该变量时一次性将内嵌的变量全部展开,虽然这种类型的变量能够很好地完成用户的指令,但是它也有严重的缺点,如不能在变量后追加内容(因为语句:CFLAGS =$(CFLAGS) -O在变量扩展过程中可能导致无穷循环)。

为了避免上述问题,简单扩展型变量的值在定义处展开,并且只展开一次,因此它不包含任何对其它变量的引用,从而消除变量的嵌套引用。

递归展开方式的定义格式为:VAR=var

简单扩展方式的定义格式为:VAR:=var

Make中的变量使用均使用格式为:$(VAR)

      变量名是不包括“:”、“#”、“=”结尾空格的任何字符串。同时,变量名中包含字母、数字以及下划线以外的情况应尽量避免,因为它们可能在将来被赋予特别的含义。

变量名是大小写敏感的,例如变量名“foo”、“FOO”、和“Foo”代表不同的变量。

推荐在makefile内部使用小写字母作为变量名,预留大写字母作为控制隐含规则参数或用户重载命令选项参数的变量名。

下面给出了上例中用变量替换修改后的Makefile,这里用OBJS代替main.o和kbd.o,用CC代替Gcc,用CFLAGS代替“-Wall -O –g”。这样在以后修改时,就可以只修改变量定义,而不需要修改下面的定义实体,从而大大简化了Makefile维护的工作量。

经变量替换后的Makefile如下所示:

 

OBJS= main.o kbd.o

CC =cc

edit: $(OBJS)

       $(CC) $(OBJS) -o edit

main.o : main.c defs.h

      $(CC) -c main.c

kbd.o : kbd.c defs.hcommand.h

      $(CC) -c kbd.c

可以看到,此处变量是以递归展开方式定义的。

Makefile中的变量分为用户自定义变量、预定义变量、自动变量及环境变量。如上例中的OBJS就是用户自定义变量,自定义变量的值由用户自行设定,而预定义变量和自动变量为通常在Makefile都会出现的变量,其中部分有默认值,也就是常见的设定值,当然用户可以对其进行修改。

预定义变量包含了常见编译器、汇编器的名称及其编译选项。下表2-2列出了Makefile中常见预定义变量及其部分默认值。

 

命 令 格 式

含    义

AR

库文件维护程序的名称,默认值为ar

AS

汇编程序的名称,默认值为as

CC

C编译器的名称,默认值为cc

CPP

C预编译器的名称,默认值为$(CC) –E

CXX

C++编译器的名称,默认值为g++

FC

FORTRAN编译器的名称,默认值为f77

RM

文件删除程序的名称,默认值为rm –f

ARFLAGS

库文件维护程序的选项,无默认值

ASFLAGS

汇编程序的选项,无默认值

CFLAGS

C编译器的选项,无默认值

CPPFLAGS

C预编译的选项,无默认值

CXXFLAGS

C++编译器的选项,无默认值

FFLAGS

FORTRAN编译器的选项,无默认值

表2-2 Makefile中常见预定义变量

可以看出,上例中的CCCFLAGS是预定义变量,其中由于CC没有采用默认值,因此,需要把“CC=Gcc”明确列出来。

由于常见的Gcc编译语句中通常包含了目标文件和依赖文件,而这些文件在Makefile文件中目标体的一行已经有所体现,因此,为了进一步简化Makefile的编写,就引入了自动变量自动变量通常可以代表编译语句中出现目标文件和依赖文件等,并且具有本地含义(即下一语句中出现的相同变量代表的是下一语句的目标文件和依赖文件)。下表2-3列出了Makefile中常见自动变量。

 

命 令 格 式

含    义

$*

不包含扩展名的目标文件名称

$+

所有的依赖文件,以空格分开,并以出现的先后为序,可能包含重复的依赖文件

$<

第一个依赖文件的名称

$?

所有时间戳比目标文件晚的依赖文件,并以空格分开

$@

目标文件的完整名称

$^

所有不重复的依赖文件,以空格分开

$%

如果目标是归档成员,则该变量表示目标的归档成员名称

表2-3 Makefile中常见自动变量

自动变量的书写比较难记,但是在熟练了之后会非常的方便,请读者结合下例中的自动变量改写的Makefile进行记忆。

OBJS= main.o kbd.o

CC =cc

edit : $(OBJS)

      $(CC) $^ -o $@

main.o : main.c defs.h

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

kbd.o : kbd.c defs.hcommand.h

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

另外,在Makefile中还可以使用环境变量。使用环境变量的方法相对比较简单,make在启动时会自动读取系统当前已经定义了的环境变量,并且会创建与之具有相同名称和数值的变量。但是,如果用户在Makefile中定义了相同名称的变量,那么用户自定义变量将会覆盖同名的环境变量。

 

2.3.4 makefile规则

Makefile的规则是Make进行处理的依据,它包括了目标体、依赖文件及其之间的命令语句。一般的,Makefile中的一条语句就是一个规则。在上面的例子中,都显示地指出了Makefile中的规则关系,如“$(CC) $(CFLAGS) -c $< -o$@”,但为了简化Makefile的编写,make还定义了隐式规则和模式规则,下面就分别对其进行讲解。

 

1.隐式规则

隐含规则能够告诉make怎样使用传统的技术完成任务,这样,当用户使用它们时就不必详细指定编译的具体细节,而只需把目标文件列出即可。Make会自动搜索隐式规则目录来确定如何生成目标文件。如上例就可以写成:

 OBJS = main.okbd.o

CC = cc

edit: $(OBJS)

     $(CC) $^ -o $@

main.o : main.cdefs.h

kbd.o : kbd.cdefs.h command.h

 

 为什么可以省略后两句呢?Make具有自动推导文件以及文件依赖关系后面的命令,没必要在每一个.o文件后写同名的.c文件,以及编译命令,此既是make的“隐式规则”。

 

 

 

下表2-4给出了常见的隐式规则目录:

对应语言后缀名

规    则

C编译:.c变为.o

$(CC) –c $(CPPFLAGS) $(CFLAGS)

C++编译:.cc或.C变为.o

$(CXX) -c $(CPPFLAGS) $(CXXFLAGS)

Pascal编译:.p变为.o

$(PC) -c $(PFLAGS)

Fortran编译:.r变为-o

$(FC) -c $(FFLAGS)

表2-4 Makefile中常见隐式规则目录

 

2.模式规则

模式规则是用来定义相同处理规则的多个文件的。它不同于隐式规则,隐式规则仅仅能够用make默认的变量来进行操作,而模式规则还能引入用户自定义变量,为多个文件建立相同的规则,从而简化Makefile的编写

模式规则的格式类似于普通规则,这个规则中的相关文件前必须用“%标明。使用模式规则修改后的Makefile的编写如下:

OBJS = main.o kbd.o

CC = cc

edit: $(OBJS)

      $(CC) $^ -o $@

%.o : %.c

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

2.3.5 makefile规则

使用make管理器非常简单,只需在make命令的后面键入目标名即可建立指定的目标,如果直接运行make,则建立Makefile中的第一个目标

此外make还有丰富的命令行选项,可以完成各种不同的功能。下表2-5列出了常用的make命令行选项。

 

命令格式

含    义

-C dir

读入指定目录下的Makefile

-f file

读入当前目录下的file文件作为Makefile

-i

忽略所有的命令执行错误

-I dir

指定被包含的Makefile所在目录

-n

只打印要执行的命令,但不执行这些命令

-p

显示make变量数据库和隐含规则

-s

在执行命令时不显示命令

-w

如果make在执行过程中改变目录,则打印当前目录名

表2-5 make的命令行选项

2.3.6 使用autotools

Makefile可以帮助make完成它的使命,但要承认的是,编写Makefile确实不是一件轻松的事,尤其对于一个较大的项目而言更是如此。那么,有没有一种轻松的手段生成Makefile而同时又能让用户享受make的优越性呢?本节要讲的autotools系列工具正是为此而设的,它只需用户输入简单的目标文件、依赖文件、文件目录等就可以轻松地生成Makefile,这无疑是广大用户的所希望的。另外,这些工具还可以完成系统配置信息的收集,从而可以方便地处理各种移植性的问题。也正是基于此,现在Linux上的软件开发一般都用autotools来制作Makefile

autotools是系列工具,读者首先要确认系统是否装了以下工具(可以用which命令进行查看)。

aclocal

autoscan

autoconf

autoheader

automake

使用autotools主要就是利用各个工具的脚本文件以生成最后的Makefile。其总体流程是这样的:

使用aclocal生成一个“aclocal.m4”文件,该文件主要处理本地的宏定义;

改写“configure.scan”文件,并将其重命名为“configure.in”,并使用autoconf文件生成configure文件。

 

用户不再需要定制不同的规则,而只需要输入简单的文件及目录名即可,这样就大大方便了用户的使用。下面的图2-1总结了上述过程:

图2-1  autotools生成Makefile流程图

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值