4223
- 收藏
- 点赞
- 分享
- 举报
GCC编译器在嵌入式linux中的应用
GCC是GNU组织的免费C编译器,Linux的很多发布缺省安装的就是这种。很多流行的自由软件源代码基本都能在GCC编译器下编译运行。 所以掌握GCC编译器的使用无论是对于编译系统内核还是自己的应用程序都是大有好处的。
下面通过一个具体的例子,学习如何使用GCC编译器。
在Linux操作系统中,对一个用标准C语言写的源程序进行编译,要使用GNU的gcc编译器。
例如下面一个非常简单的Hello源程序(hello.c):
/*******************************************************
* Institute of Automation, Chinese Academy of Sciences
* File Name: hello.c
* Description:introduce how to compile a source file with gcc
* Author: Xueyuan Nie
* Date:
*******************************************************/
void main()
{
printf("Hello the world\n");
}
要编译这个程序,我们只要在Linux的bash提示符下输入命令:
$ gcc -o hello hello.c
gcc 编译器就会生成一个hello的可执行文件。在hello.c的当前目录下执行./hello就可以看到程序的输出结果,在屏幕上打印出“Hello the world”的字符串来。
命令行中 gcc表示是用gcc来编译源程序;
-o outputfilename选项表示要求编译器生成文件名为outputfilename的可执行文件,如果不指定-o选项,则缺省文件名是 a.out。在这里生成指定文件名为hello的可执行文件,而hello.c是我们的源程序文件。
gcc 是一个多目标的工具。gcc最基本的用法是:
gcc [options] file... ,
其中的option是以-开始的各种选项,file是相关的文件名。在使用gcc的时候,必须要给出必要的选项和文件名。gcc的整个编译过程,实质上是分四步进行的,每一步完成一个特定的工作,这四步分别是:预处理,编译,汇编和链接。它具体完成哪一步,是由gcc后面的开关选项和文件类型决定的。
清楚的区别编译和连接是很重要的。编译器使用源文件编译产生某种形式的目标文件(object files)。在这个过程中,外部的符号引用并没有被解释或替换,然后我们使用链接器来链接这些目标文件和一些标准的头文件,最后生成一个可执行文件。 在这个过程中,一个目标文件中对别的文件中的符号的引用被解释,并报告不能被解释的引用,一般是以错误信息的形式报告出来。
gcc编译器有许多选项,但对于普通用户来说只要知道其中常用的几个就够了。在这里为读者列出几个最常用的选项:
-o选项表示要求编译器生成指定文件名的可执行文件;
-c选项表示只要求编译器进行编译,而不要进行链接,生成以源文件的文件名命名但把其后缀由.c或.cc变成.o的目标文件;
-g选项要求编译器在编译的时候提供以后对程序进行调试的信息;
-E选项表示编译器对源文件只进行预处理就停止,而不做编译,汇编和链接;
-S选项表示编译器只进行编译,而不做汇编和链接;
-O选项是编译器对程序提供的编译优化选项,在编译的时候使用该选项,可以使生成的执行文件的执行效率提高;
-Wall 选项指定产生全部的警告信息。
如果你的源代码中包含有某些函数,则在编译的时候要链接确定的库,比如代码中包含了某些数学函数,在Linux下,为了使用数学函数,必须和数学库链接,为此要加入-lm 选项。也许有读者会问,前面那个例子使用printf函数的时候为何没有链接库呢?在gcc中对于一些常用函数的实现,gcc编译器会自动去链接一些常用库,这样用户就没有必要自己去指定了。有时候在编译程序的时候还要指定库的路径,这个时候要用到编译器的-L选项指定路径。比如说我们有一个库在 /home/hoyt/mylib下,这样我们编译的时候还要加上 -L/home/hoyt/mylib。对于一些标准库来说,没有必要指出路径。只要它们在起缺省库的路径下就可以了,gcc在链接的时候会自动找到那些库的。
GNU编译器生成的目标文件缺省格式为elf(executive linked file)格式,这是Linux系统所采用的可执行链接文件的通用文件格式。elf格式由若干段(section)组成,如果没有特别指明,由标准c源代码生成的目标文件中包含以下段:.text(正文段) 包含程序的指令代码,.data(数据段)包含固定的数据,如常量,字符串等,.bss(未初始化数据段) 包含未初始化的变量和数组等。
读者若想知道更多的选项及其用法,可以查看gcc的帮助文档,那里有许多对其它选项的详细说明。
当改变了源文件hello.c后,需要重新编译它:
$gcc -c hello.c
然后重新链接生成:
$gcc –o hello.o
对于本例,因为只含有一个源文件,所以当改动了源码后,进行重新的编译链接的过程显得并不是太繁琐,但是,如果在一个工程中包含了若干的源码文件,而这些源码文件中的某个或某几个又被其他源码文件包含,那么,如果一个文件被改动,则包含它的那些源文件都要进行重新编译链接,工作量是可想而知的。 幸运的是,GNU提供了使这个步骤变得简单的工具,就是下面要介绍给大家的GNU Make 工具。
GNU Make
make是负责从项目的源代码中生成最终可执行文件和其他非源代码文件的工具。 make命令本身可带有四种参数:标志、宏定义、描述文件名和目标文件名。
其标准形式为:
make [flags] [macro definitions] [targets]
Unix系统下标志位flags选项及其含义为:
-f file 指定file文件为描述文件,如果file参数为 '-' 符,那么描述文件指向标准输入。如果没有 '-f' 参数,则系统将默认当前目录下名为makefile或者名为Makefile的文件为描述文件。在Linux中, GNU make 工具在当前工作目录中按照GNUmakefile、makefile、Makefile的顺序搜索 makefile文件。
-i 忽略命令执行返回的出错信息。
-s 沉默模式,在执行之前不输出相应的命令行信息。
-r 禁止使用隐含规则。
-n 非执行模式,输出所有执行命令,但并不执行。
-t 更新目标文件。
-q make操作将根据目标文件是否已经更新返回"0"或非"0"的状态信息。
-p 输出所有宏定义和目标文件描述。
-d Debug模式,输出有关文件和检测时间的详细信息。
Linux下make标志位的常用选项与Unix系统中稍有不同,下面只列出了不同部分:
-c dir 在读取 makefile 之前改变到指定的目录dir。
-I dir 当包含其他 makefile文件时,利用该选项指定搜索目录。
-h help文挡,显示所有的make选项。
-w 在处理 makefile 之前和之后,都显示工作目录。
通过命令行参数中的target ,可指定make要编译的目标,并且允许同时定义编译多个目标,操作时按照从左向右的顺序依次编译target选项中指定的目标文件。如果命令行中没有指定目标,则系统默认target指向描述文件中第一个目标文件。
make如何实现对源代码的操作是通过一个被称之为makefile的文件来完成的,在下面的小节里,主要向读者介绍一下makefile的相关知识。
7.2.2.1 makefile基本结构
GNU Make 的主要工作是读一个文本文件 makefile。makefile 是用bash语言写的,bash语言是很像BASIC语言的一种命令解释语言。这个文件里主要描述了有关哪些目标文件是从哪些依赖文件中产生的,是用何种命令来进行这个产生过程的。有了这些信息, make 会检查磁盘的文件,如果目标文件的日期(即该文件生成或最后修改的日期)至少比它的一个依赖文件日期早的话,make 就会执行相应的命令,以更新目标文件。
makefile一般被称为“makefile”或“Makefile”。还可以在 make 的命令行中指定别的文件名。如果没有特别指定的话,make就会寻找“makefile”或“Makefile”,所以为了简单起见,建议读者使用这两名字。如果要使用其他文件作为 makefile,则可利用类似下面的 make 命令选项指定 makefile 文件:
$ make -f makefilename
一个 makefile 主要含有一系列的规则,如下:
目标文件名 : 依赖文件名
(tab键) 命令
第一行称之为规则,第二行是执行规则的命令,必须要以tab键开始。
下面举一个简单的makefile的例子。
executable : main.o io.o
gcc main.o io.o -o executable
main.o : main.c
gcc -Wall -O -g -c main.c -o main.o
io.o : io.c
gcc -Wall -O -g -c io.c -o io.o
这是一个最简单的 makefile,make从第一条规则开始,executable是makefile最终要生成的目标文件。给出的规则说明executable依赖于两个目标文件main.o和io.o,只要executable 比它依赖的文件中的任何一个旧的话,下一行的命令就会被执行。但是,在检查文件 main.o 和 io.o 的日期之前,它会往下查找那些把 main.o 或 io.o 做为目标文件的规则。make先找到了关于 main.o 的规则,该目标文件的依赖文件是main.c。makefile后面的文件中再也找不到生成这个依赖文件的规则了。此时,make开始检查磁盘上这个依赖文件的日期,如果这个文件的日期比main.o 日期新的话,那么这个规则下面的命令 gcc -c main.c –o main.o 就会执行,以更新文件 main.o 。同样make对文件io.o 做类似的检查,它的依赖文件是io.c,对io.o的处理和main.o类似。
现在, 再回到第一个规则处,如果刚才两个规则中的任何一个被执行,最终的目标文件executable都需要重建(因为executable所依赖的其中一个 .o 文件就会比它新),因此链接命令就会被执行。
有了makefile,对任何一个源文件进行修改后,所有依赖于该文件的目标文件都会被重新编译(因为.o 文件依赖于.c 文件),进而最终可执行文件会被重新链接(因为它所依赖的.o 文件被改变了),再也不用手工去一个个修改了。
7.2.2.2 编写make
1、Makefile 宏定义
makefile 里的宏是大小写敏感的,一般都使用大写字母。它们几乎可以从任何地方被引用,可以代表很多类型,例如可以存储文件名列表,存储可执行文件名和编译器标志等。
要定义一个宏,在makefile中,任意一行的开始写下该宏名,后面跟一个等号,等号后面是要设定的这个宏的值。如果以后要引用到该宏时,使用 $ (宏名),或者是${宏名},注意宏名一定要写在圆或花括号之内。把上一小节所举的例子,用引入宏名的方法,可以写成下面的形式:
OBJS = main.o io.o
CC = gcc
CFLAGS = -Wall -O -g
executable: $(OBJS)
$(CC) $(OBJS) -o executable
main.o : main.c
$(CC) $(CFLAGS) -c main.c -o main.o
io.o : io.c
$(CC) $(CFLAGS) -c io.c -o io.o
在这个makefile中引入了三个宏定义,所以如果当这些宏中的某些值发生变化时,开发者只需在要修改的宏处,将其宏值修改为要求的值即可,makefile中用到这些宏的地方会自动变化。在make中还有一些已经定义好的内部变量,有几个较常用的变量是$@, $< ,$?,$*, $^ (注意:这些变量不需要括号括住)。
$@ 扩展为当前规则的目标文件名;
$< 扩展为当前规则依赖文件列表中的第一个依赖文件;
$? 扩展为所有的修改日期比当前规则的目标文件的创建日期更晚的依赖文件,该值只有在使用显式规则时才会被使用;
$* 扩展成当前规则中目标文件和依赖文件共享的文件名,不含扩展名;
$^ 扩展为整个依赖文件的列表(除掉了所有重复的文件名)。
利用这些变量,可以把上面的 makefile 写成:
OBJS = main.o io.o
CC = gcc
CFLAGS = -Wall -O -g
executable: $(OBJS)
$(CC) $^ -o $@
main.o : main.c
$(CC) $(CFLAGS) –c $< -o $@
io.o : io.c
$(CC) $(CFLAGS) -c $< -o $@
可以将宏变量应用到其他许多地方,尤其是当把它们和函数混合使用的时候,正确使用宏,会给开发者带来极大的便利。
2、隐含规则
请注意,在上面的例子里,几个产生 .o文件的命令都是以.c 文件作为依赖文件产生 .o 目标(obj)文件,这是一个标准的生成目标文件的步骤。如果把生成main.o和io.o的规则从makefile中删除,make 会查找它的隐含规则,然后会找到一个适当的命令去执行。实际上make已经知道该如何生成这些目标文件,它使用变量 CC 做为编译器,并且传递宏 CFLAGS 给 C 编译器(CXXFLAGS用于 C++ 编译器),CPPFLAGS(C预处理选项), TARGET_ARCH (就目前例子而言,还不用考虑这个宏),然后它加入开关选项 -c ,后面跟预定义宏 $<(第一个依赖文件名),最后是开关项 -o,后跟预定义宏$@ (目标文件名)。一个C编译的具体命令将 会是:
$(CC) $(CFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c $< -o $@
在make 工具中所包含的这些内置的或隐含的规则,定义了如何从不同的依赖文件建立特定类型的目标。Unix系统通常支持一种基于文件扩展名即文件名后缀的隐含规则。这种后缀规则定义了如何将一个具有特定文件名后缀的文件(例如.c文件),转换成为具有另一种文件名后缀的文件(例如.o文件):
系统中默认的常用文件扩展名及其含义为:
.o 目标文件
.c C源文件
.f FORTRAN源文件
.s 汇编源文件
.y Yacc-C源语法
.l Lex源语法
而GNU make 除了支持后缀规则外还支持另一种类型的隐含规则即模式规则。这种规则更加通用,因为可以利用模式规则定义更加复杂的依赖性规则。同时可用来定义目标和依赖文件之间的关系,例如下面的模式规则定义了如何将任意一个.c 文件转换为文件名相同的.o 文件:
%.o : %.c
$(CC) $(CFLAGS) $(CPPFLAGS) -c -o $@ $<
3、伪目标
如果需要最终产生两个和更多的可执行文件,但这些文件是相互独立的,也就是说任何一个目标文件的重建,不会影响其他目标文件。此时,可以通过使用所谓的伪目标来达到这一目的。一个伪目标和一个真正的目标文件的唯一区别在于,这个目标文件本身并不存在。因此, make总是会假设它需要被生成,当make把该伪目标文件的所有依赖文件都更新后,就会执行它的规则里的命令行。
举一个简单的例子,如果makefile 开始处输入
all : executable1 executable2
这里 executable1和executable2是最终希望生成的两个可执行文件。 make 把这个 'all' 做为它的主要目标,每次执行时都会尝试把 'all' 更新。但是,由于这行规则里并没有命令来作用在一个叫 'all' 的实际文件上(事实上, all 也不会实际生成),所以这个规则并不真的改变 'all' 的状态。可既然这个文件并不存在,所以 make 会尝试更新 all 规则,因此就检查它的依赖文件 executable1, exectable2 是否需要更新,如果需要,就把它们更新,从而达到生成两个目标文件的目的。 伪目标在makefile中广泛使用。
4、函数
makefile 里的函数跟它的宏很相似,在使用的时候,用一个 $ 符号开始后跟圆括号,在圆括号内包含函数名,空格后跟一系列由逗号分隔的参数。例如,在 GNU Make 里有一个名为 'wildcard' 的函 数,它只有一个参数,功能是展开成一列所有符合由其参数描述的文件名,文件间以空格间隔。可以像下面所示使用这个命令:
SOURCES = $(wildcard *.c)
这样会产生一个所有以 '.c' 结尾的文件的列表,然后存入变量 SOURCES 里。当然你不需要一定要把结果存入一个变量。
另一个有用的函数是 patsubst (patten substitude, 匹配替换的缩写) 函数。它需要3个参数:第一个是一个需要匹配的模式,第二个表示用什么来替换它,第三个是一个需要被处理的由空格分隔的字列。例如,处理那个经过上面定义后的变量,
OBJS = $(patsubst %.c,%.o,$(SOURCES))
这个语句将处理所有在 SOURCES宏中的文件名后缀是 '.c'的文件 ,用 '.o' 把 '.c' 取代。注意这里的 % 符号是通配符,匹配一个或多个字符,它每次所匹配的字符串叫做一个‘柄’(stem) 。在第二个参数里,% 被解释成用第一参数所匹配的那个柄。
感兴趣的读者如果需要更进一步的了解,请参考GNU Make 手册。
7.2.2.3 makefile的一个具体例子
在这里给读者举一个简单的makefile的例子,通过对这个makefile的讲解,来巩固前面介绍的相关知识。
INCLUDES =-I/home/nie/mysrc/include \
-I/home/nie/mysrc/extern/include \
-I/home/nie/mysrc/src \
-I/home/nie/mysrc/libsrc \
-I. \
-I..
EXT_CC_OPTS = -DEXT_MODE
CPP_REQ_DEFINES = -DMODEL=tune1 -DRT -DNUMST=2 \
-DTID01EQ=1 -DNCSTATES=0 \
-DMT=0 -DHAVESTDIO
RTM_CC_OPTS = -DUSE_RTMODEL
CFLAGS = -O -g
CFLAGS += $(CPP_REQ_DEFINES)
CFLAGS += $(EXT_CC_OPTS)
CFLAGS +=$(RTM_CC_OPTS)
SRCS = tune1.c rt_sim.c rt_nonfinite.c grt_main.c rt_logging.c \
ext_svr.c updown.c ext_svr_transport.c ext_work.c
OBJS = $(SRCS:.c=.o)
RM = rm –f
CC = gcc
LD = gcc
all: tune1
%.o : %.c
$(CC) -c -o $@ $(CFLAGS) $(INCLUDES) $<
tune1 : $(OBJS)
$(LD) -o $@ $(OBJS) -lm
clean :
$(RM) $(OBJS)
在这个makefile中首先定义了十个宏:
' INCLUDES =-I …'(省略号代表-I后面的内容),'-I dirname' 表示将dirname所指的目录加入到程序头文件目录列表中去,是在进行预处理过程中使用的参数;
' EXT_CC_OPTS = -DEXT_MODE ' 表示在程序中定义了宏EXT_MODE,等价于在源代码写入语句' #define EXT_MODE ' ;
接下来的两个宏定义CPP_REQ_DEFINES 和RTM_CC_OPTS起到和EXT_CC_OPTS类似的作用;
'CFLAGS =-O -g '是编译器的编译选项,表示在编译的过程中对代码进行基本优化,并产生能被GNU调试器(如gdb)使用的调试信息;
'CFLAGS += ' 表示对这个宏定义在原来的基础上增加新的内容;
' SRCS = …'代表了所有要编译的源代码文件列表;
' OBJS = $(SRCS:.c=.o)'表示把宏SRC所代表的所有以.c结尾的文件名用.o结尾的文件名替换,即表示各个源文件所对应的目标文件名;
' RM = rm –f ' 表示删除命令,-f是强制删除选项,使用该符号,在对文件进行删除时,没有提示;
' CC = gcc '表示编译器是用gcc;
' LD= gcc' 表示链接命令是用gcc;
all和clean是两个伪目标,在使用make命令的时候,如果不指明目标文件名,则是以在makefile 中出现的第一个目标作为最终目标,所以如果键入命令make,则伪目标all被作为最终的目标而执行,由于这个文件并不存在,所以 make 会尝试更新 all 规则,因此就检查它的依赖文件 tune1 是否需要更新,如果需要,就把它更新,这样伪目标下面的两条规则就会被执行,从而生成可执行文件tune1。如果要执行删除命令,只需要键入命令make clean,就会把所有以.o结尾的中间文件删除。
另外,请读者注意在本makefile的例子中多次用到' \',该符号用于在makefile中,如果一条语句过长时,可以用' \'放在这条语句的右边界,通过回车换行,使下面新一行的语句成为该语句的续行。
在makefile文件中,用符号' #'作为注释行语句的开始,以增强makefile文件的可读性。
本例假设makefile文件名为makefile,当然也可按照个人的喜好取其他文件名,如果文件名不是makefile,Makefile的话,在用make命令是,请使用make –f makefilename。
到此,希望读者能够掌握make 和makefile的基本使用。
下面通过一个具体的例子,学习如何使用GCC编译器。
在Linux操作系统中,对一个用标准C语言写的源程序进行编译,要使用GNU的gcc编译器。
例如下面一个非常简单的Hello源程序(hello.c):
/*******************************************************
* Institute of Automation, Chinese Academy of Sciences
* File Name: hello.c
* Description:introduce how to compile a source file with gcc
* Author: Xueyuan Nie
* Date:
*******************************************************/
void main()
{
printf("Hello the world\n");
}
要编译这个程序,我们只要在Linux的bash提示符下输入命令:
$ gcc -o hello hello.c
gcc 编译器就会生成一个hello的可执行文件。在hello.c的当前目录下执行./hello就可以看到程序的输出结果,在屏幕上打印出“Hello the world”的字符串来。
命令行中 gcc表示是用gcc来编译源程序;
-o outputfilename选项表示要求编译器生成文件名为outputfilename的可执行文件,如果不指定-o选项,则缺省文件名是 a.out。在这里生成指定文件名为hello的可执行文件,而hello.c是我们的源程序文件。
gcc 是一个多目标的工具。gcc最基本的用法是:
gcc [options] file... ,
其中的option是以-开始的各种选项,file是相关的文件名。在使用gcc的时候,必须要给出必要的选项和文件名。gcc的整个编译过程,实质上是分四步进行的,每一步完成一个特定的工作,这四步分别是:预处理,编译,汇编和链接。它具体完成哪一步,是由gcc后面的开关选项和文件类型决定的。
清楚的区别编译和连接是很重要的。编译器使用源文件编译产生某种形式的目标文件(object files)。在这个过程中,外部的符号引用并没有被解释或替换,然后我们使用链接器来链接这些目标文件和一些标准的头文件,最后生成一个可执行文件。 在这个过程中,一个目标文件中对别的文件中的符号的引用被解释,并报告不能被解释的引用,一般是以错误信息的形式报告出来。
gcc编译器有许多选项,但对于普通用户来说只要知道其中常用的几个就够了。在这里为读者列出几个最常用的选项:
-o选项表示要求编译器生成指定文件名的可执行文件;
-c选项表示只要求编译器进行编译,而不要进行链接,生成以源文件的文件名命名但把其后缀由.c或.cc变成.o的目标文件;
-g选项要求编译器在编译的时候提供以后对程序进行调试的信息;
-E选项表示编译器对源文件只进行预处理就停止,而不做编译,汇编和链接;
-S选项表示编译器只进行编译,而不做汇编和链接;
-O选项是编译器对程序提供的编译优化选项,在编译的时候使用该选项,可以使生成的执行文件的执行效率提高;
-Wall 选项指定产生全部的警告信息。
如果你的源代码中包含有某些函数,则在编译的时候要链接确定的库,比如代码中包含了某些数学函数,在Linux下,为了使用数学函数,必须和数学库链接,为此要加入-lm 选项。也许有读者会问,前面那个例子使用printf函数的时候为何没有链接库呢?在gcc中对于一些常用函数的实现,gcc编译器会自动去链接一些常用库,这样用户就没有必要自己去指定了。有时候在编译程序的时候还要指定库的路径,这个时候要用到编译器的-L选项指定路径。比如说我们有一个库在 /home/hoyt/mylib下,这样我们编译的时候还要加上 -L/home/hoyt/mylib。对于一些标准库来说,没有必要指出路径。只要它们在起缺省库的路径下就可以了,gcc在链接的时候会自动找到那些库的。
GNU编译器生成的目标文件缺省格式为elf(executive linked file)格式,这是Linux系统所采用的可执行链接文件的通用文件格式。elf格式由若干段(section)组成,如果没有特别指明,由标准c源代码生成的目标文件中包含以下段:.text(正文段) 包含程序的指令代码,.data(数据段)包含固定的数据,如常量,字符串等,.bss(未初始化数据段) 包含未初始化的变量和数组等。
读者若想知道更多的选项及其用法,可以查看gcc的帮助文档,那里有许多对其它选项的详细说明。
当改变了源文件hello.c后,需要重新编译它:
$gcc -c hello.c
然后重新链接生成:
$gcc –o hello.o
对于本例,因为只含有一个源文件,所以当改动了源码后,进行重新的编译链接的过程显得并不是太繁琐,但是,如果在一个工程中包含了若干的源码文件,而这些源码文件中的某个或某几个又被其他源码文件包含,那么,如果一个文件被改动,则包含它的那些源文件都要进行重新编译链接,工作量是可想而知的。 幸运的是,GNU提供了使这个步骤变得简单的工具,就是下面要介绍给大家的GNU Make 工具。
GNU Make
make是负责从项目的源代码中生成最终可执行文件和其他非源代码文件的工具。 make命令本身可带有四种参数:标志、宏定义、描述文件名和目标文件名。
其标准形式为:
make [flags] [macro definitions] [targets]
Unix系统下标志位flags选项及其含义为:
-f file 指定file文件为描述文件,如果file参数为 '-' 符,那么描述文件指向标准输入。如果没有 '-f' 参数,则系统将默认当前目录下名为makefile或者名为Makefile的文件为描述文件。在Linux中, GNU make 工具在当前工作目录中按照GNUmakefile、makefile、Makefile的顺序搜索 makefile文件。
-i 忽略命令执行返回的出错信息。
-s 沉默模式,在执行之前不输出相应的命令行信息。
-r 禁止使用隐含规则。
-n 非执行模式,输出所有执行命令,但并不执行。
-t 更新目标文件。
-q make操作将根据目标文件是否已经更新返回"0"或非"0"的状态信息。
-p 输出所有宏定义和目标文件描述。
-d Debug模式,输出有关文件和检测时间的详细信息。
Linux下make标志位的常用选项与Unix系统中稍有不同,下面只列出了不同部分:
-c dir 在读取 makefile 之前改变到指定的目录dir。
-I dir 当包含其他 makefile文件时,利用该选项指定搜索目录。
-h help文挡,显示所有的make选项。
-w 在处理 makefile 之前和之后,都显示工作目录。
通过命令行参数中的target ,可指定make要编译的目标,并且允许同时定义编译多个目标,操作时按照从左向右的顺序依次编译target选项中指定的目标文件。如果命令行中没有指定目标,则系统默认target指向描述文件中第一个目标文件。
make如何实现对源代码的操作是通过一个被称之为makefile的文件来完成的,在下面的小节里,主要向读者介绍一下makefile的相关知识。
7.2.2.1 makefile基本结构
GNU Make 的主要工作是读一个文本文件 makefile。makefile 是用bash语言写的,bash语言是很像BASIC语言的一种命令解释语言。这个文件里主要描述了有关哪些目标文件是从哪些依赖文件中产生的,是用何种命令来进行这个产生过程的。有了这些信息, make 会检查磁盘的文件,如果目标文件的日期(即该文件生成或最后修改的日期)至少比它的一个依赖文件日期早的话,make 就会执行相应的命令,以更新目标文件。
makefile一般被称为“makefile”或“Makefile”。还可以在 make 的命令行中指定别的文件名。如果没有特别指定的话,make就会寻找“makefile”或“Makefile”,所以为了简单起见,建议读者使用这两名字。如果要使用其他文件作为 makefile,则可利用类似下面的 make 命令选项指定 makefile 文件:
$ make -f makefilename
一个 makefile 主要含有一系列的规则,如下:
目标文件名 : 依赖文件名
(tab键) 命令
第一行称之为规则,第二行是执行规则的命令,必须要以tab键开始。
下面举一个简单的makefile的例子。
executable : main.o io.o
gcc main.o io.o -o executable
main.o : main.c
gcc -Wall -O -g -c main.c -o main.o
io.o : io.c
gcc -Wall -O -g -c io.c -o io.o
这是一个最简单的 makefile,make从第一条规则开始,executable是makefile最终要生成的目标文件。给出的规则说明executable依赖于两个目标文件main.o和io.o,只要executable 比它依赖的文件中的任何一个旧的话,下一行的命令就会被执行。但是,在检查文件 main.o 和 io.o 的日期之前,它会往下查找那些把 main.o 或 io.o 做为目标文件的规则。make先找到了关于 main.o 的规则,该目标文件的依赖文件是main.c。makefile后面的文件中再也找不到生成这个依赖文件的规则了。此时,make开始检查磁盘上这个依赖文件的日期,如果这个文件的日期比main.o 日期新的话,那么这个规则下面的命令 gcc -c main.c –o main.o 就会执行,以更新文件 main.o 。同样make对文件io.o 做类似的检查,它的依赖文件是io.c,对io.o的处理和main.o类似。
现在, 再回到第一个规则处,如果刚才两个规则中的任何一个被执行,最终的目标文件executable都需要重建(因为executable所依赖的其中一个 .o 文件就会比它新),因此链接命令就会被执行。
有了makefile,对任何一个源文件进行修改后,所有依赖于该文件的目标文件都会被重新编译(因为.o 文件依赖于.c 文件),进而最终可执行文件会被重新链接(因为它所依赖的.o 文件被改变了),再也不用手工去一个个修改了。
7.2.2.2 编写make
1、Makefile 宏定义
makefile 里的宏是大小写敏感的,一般都使用大写字母。它们几乎可以从任何地方被引用,可以代表很多类型,例如可以存储文件名列表,存储可执行文件名和编译器标志等。
要定义一个宏,在makefile中,任意一行的开始写下该宏名,后面跟一个等号,等号后面是要设定的这个宏的值。如果以后要引用到该宏时,使用 $ (宏名),或者是${宏名},注意宏名一定要写在圆或花括号之内。把上一小节所举的例子,用引入宏名的方法,可以写成下面的形式:
OBJS = main.o io.o
CC = gcc
CFLAGS = -Wall -O -g
executable: $(OBJS)
$(CC) $(OBJS) -o executable
main.o : main.c
$(CC) $(CFLAGS) -c main.c -o main.o
io.o : io.c
$(CC) $(CFLAGS) -c io.c -o io.o
在这个makefile中引入了三个宏定义,所以如果当这些宏中的某些值发生变化时,开发者只需在要修改的宏处,将其宏值修改为要求的值即可,makefile中用到这些宏的地方会自动变化。在make中还有一些已经定义好的内部变量,有几个较常用的变量是$@, $< ,$?,$*, $^ (注意:这些变量不需要括号括住)。
$@ 扩展为当前规则的目标文件名;
$< 扩展为当前规则依赖文件列表中的第一个依赖文件;
$? 扩展为所有的修改日期比当前规则的目标文件的创建日期更晚的依赖文件,该值只有在使用显式规则时才会被使用;
$* 扩展成当前规则中目标文件和依赖文件共享的文件名,不含扩展名;
$^ 扩展为整个依赖文件的列表(除掉了所有重复的文件名)。
利用这些变量,可以把上面的 makefile 写成:
OBJS = main.o io.o
CC = gcc
CFLAGS = -Wall -O -g
executable: $(OBJS)
$(CC) $^ -o $@
main.o : main.c
$(CC) $(CFLAGS) –c $< -o $@
io.o : io.c
$(CC) $(CFLAGS) -c $< -o $@
可以将宏变量应用到其他许多地方,尤其是当把它们和函数混合使用的时候,正确使用宏,会给开发者带来极大的便利。
2、隐含规则
请注意,在上面的例子里,几个产生 .o文件的命令都是以.c 文件作为依赖文件产生 .o 目标(obj)文件,这是一个标准的生成目标文件的步骤。如果把生成main.o和io.o的规则从makefile中删除,make 会查找它的隐含规则,然后会找到一个适当的命令去执行。实际上make已经知道该如何生成这些目标文件,它使用变量 CC 做为编译器,并且传递宏 CFLAGS 给 C 编译器(CXXFLAGS用于 C++ 编译器),CPPFLAGS(C预处理选项), TARGET_ARCH (就目前例子而言,还不用考虑这个宏),然后它加入开关选项 -c ,后面跟预定义宏 $<(第一个依赖文件名),最后是开关项 -o,后跟预定义宏$@ (目标文件名)。一个C编译的具体命令将 会是:
$(CC) $(CFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c $< -o $@
在make 工具中所包含的这些内置的或隐含的规则,定义了如何从不同的依赖文件建立特定类型的目标。Unix系统通常支持一种基于文件扩展名即文件名后缀的隐含规则。这种后缀规则定义了如何将一个具有特定文件名后缀的文件(例如.c文件),转换成为具有另一种文件名后缀的文件(例如.o文件):
系统中默认的常用文件扩展名及其含义为:
.o 目标文件
.c C源文件
.f FORTRAN源文件
.s 汇编源文件
.y Yacc-C源语法
.l Lex源语法
而GNU make 除了支持后缀规则外还支持另一种类型的隐含规则即模式规则。这种规则更加通用,因为可以利用模式规则定义更加复杂的依赖性规则。同时可用来定义目标和依赖文件之间的关系,例如下面的模式规则定义了如何将任意一个.c 文件转换为文件名相同的.o 文件:
%.o : %.c
$(CC) $(CFLAGS) $(CPPFLAGS) -c -o $@ $<
3、伪目标
如果需要最终产生两个和更多的可执行文件,但这些文件是相互独立的,也就是说任何一个目标文件的重建,不会影响其他目标文件。此时,可以通过使用所谓的伪目标来达到这一目的。一个伪目标和一个真正的目标文件的唯一区别在于,这个目标文件本身并不存在。因此, make总是会假设它需要被生成,当make把该伪目标文件的所有依赖文件都更新后,就会执行它的规则里的命令行。
举一个简单的例子,如果makefile 开始处输入
all : executable1 executable2
这里 executable1和executable2是最终希望生成的两个可执行文件。 make 把这个 'all' 做为它的主要目标,每次执行时都会尝试把 'all' 更新。但是,由于这行规则里并没有命令来作用在一个叫 'all' 的实际文件上(事实上, all 也不会实际生成),所以这个规则并不真的改变 'all' 的状态。可既然这个文件并不存在,所以 make 会尝试更新 all 规则,因此就检查它的依赖文件 executable1, exectable2 是否需要更新,如果需要,就把它们更新,从而达到生成两个目标文件的目的。 伪目标在makefile中广泛使用。
4、函数
makefile 里的函数跟它的宏很相似,在使用的时候,用一个 $ 符号开始后跟圆括号,在圆括号内包含函数名,空格后跟一系列由逗号分隔的参数。例如,在 GNU Make 里有一个名为 'wildcard' 的函 数,它只有一个参数,功能是展开成一列所有符合由其参数描述的文件名,文件间以空格间隔。可以像下面所示使用这个命令:
SOURCES = $(wildcard *.c)
这样会产生一个所有以 '.c' 结尾的文件的列表,然后存入变量 SOURCES 里。当然你不需要一定要把结果存入一个变量。
另一个有用的函数是 patsubst (patten substitude, 匹配替换的缩写) 函数。它需要3个参数:第一个是一个需要匹配的模式,第二个表示用什么来替换它,第三个是一个需要被处理的由空格分隔的字列。例如,处理那个经过上面定义后的变量,
OBJS = $(patsubst %.c,%.o,$(SOURCES))
这个语句将处理所有在 SOURCES宏中的文件名后缀是 '.c'的文件 ,用 '.o' 把 '.c' 取代。注意这里的 % 符号是通配符,匹配一个或多个字符,它每次所匹配的字符串叫做一个‘柄’(stem) 。在第二个参数里,% 被解释成用第一参数所匹配的那个柄。
感兴趣的读者如果需要更进一步的了解,请参考GNU Make 手册。
7.2.2.3 makefile的一个具体例子
在这里给读者举一个简单的makefile的例子,通过对这个makefile的讲解,来巩固前面介绍的相关知识。
INCLUDES =-I/home/nie/mysrc/include \
-I/home/nie/mysrc/extern/include \
-I/home/nie/mysrc/src \
-I/home/nie/mysrc/libsrc \
-I. \
-I..
EXT_CC_OPTS = -DEXT_MODE
CPP_REQ_DEFINES = -DMODEL=tune1 -DRT -DNUMST=2 \
-DTID01EQ=1 -DNCSTATES=0 \
-DMT=0 -DHAVESTDIO
RTM_CC_OPTS = -DUSE_RTMODEL
CFLAGS = -O -g
CFLAGS += $(CPP_REQ_DEFINES)
CFLAGS += $(EXT_CC_OPTS)
CFLAGS +=$(RTM_CC_OPTS)
SRCS = tune1.c rt_sim.c rt_nonfinite.c grt_main.c rt_logging.c \
ext_svr.c updown.c ext_svr_transport.c ext_work.c
OBJS = $(SRCS:.c=.o)
RM = rm –f
CC = gcc
LD = gcc
all: tune1
%.o : %.c
$(CC) -c -o $@ $(CFLAGS) $(INCLUDES) $<
tune1 : $(OBJS)
$(LD) -o $@ $(OBJS) -lm
clean :
$(RM) $(OBJS)
在这个makefile中首先定义了十个宏:
' INCLUDES =-I …'(省略号代表-I后面的内容),'-I dirname' 表示将dirname所指的目录加入到程序头文件目录列表中去,是在进行预处理过程中使用的参数;
' EXT_CC_OPTS = -DEXT_MODE ' 表示在程序中定义了宏EXT_MODE,等价于在源代码写入语句' #define EXT_MODE ' ;
接下来的两个宏定义CPP_REQ_DEFINES 和RTM_CC_OPTS起到和EXT_CC_OPTS类似的作用;
'CFLAGS =-O -g '是编译器的编译选项,表示在编译的过程中对代码进行基本优化,并产生能被GNU调试器(如gdb)使用的调试信息;
'CFLAGS += ' 表示对这个宏定义在原来的基础上增加新的内容;
' SRCS = …'代表了所有要编译的源代码文件列表;
' OBJS = $(SRCS:.c=.o)'表示把宏SRC所代表的所有以.c结尾的文件名用.o结尾的文件名替换,即表示各个源文件所对应的目标文件名;
' RM = rm –f ' 表示删除命令,-f是强制删除选项,使用该符号,在对文件进行删除时,没有提示;
' CC = gcc '表示编译器是用gcc;
' LD= gcc' 表示链接命令是用gcc;
all和clean是两个伪目标,在使用make命令的时候,如果不指明目标文件名,则是以在makefile 中出现的第一个目标作为最终目标,所以如果键入命令make,则伪目标all被作为最终的目标而执行,由于这个文件并不存在,所以 make 会尝试更新 all 规则,因此就检查它的依赖文件 tune1 是否需要更新,如果需要,就把它更新,这样伪目标下面的两条规则就会被执行,从而生成可执行文件tune1。如果要执行删除命令,只需要键入命令make clean,就会把所有以.o结尾的中间文件删除。
另外,请读者注意在本makefile的例子中多次用到' \',该符号用于在makefile中,如果一条语句过长时,可以用' \'放在这条语句的右边界,通过回车换行,使下面新一行的语句成为该语句的续行。
在makefile文件中,用符号' #'作为注释行语句的开始,以增强makefile文件的可读性。
本例假设makefile文件名为makefile,当然也可按照个人的喜好取其他文件名,如果文件名不是makefile,Makefile的话,在用make命令是,请使用make –f makefilename。
到此,希望读者能够掌握make 和makefile的基本使用。
我来回答
回答1个
时间排序
认可量排序
认可0
或将文件直接拖到这里
悬赏:
E币
网盘
* 网盘链接:
* 提取码:
悬赏:
E币
Markdown 语法
- 加粗**内容**
- 斜体*内容*
- 删除线~~内容~~
- 引用> 引用内容
- 代码`代码`
- 代码块```编程语言↵代码```
- 链接[链接标题](url)
- 无序列表- 内容
- 有序列表1. 内容
- 缩进内容
- 图片![alt](url)
相关问答
-
2013-11-29 23:02:26
-
2016-08-17 12:07:22
-
2018-07-06 16:02:29
-
2012-12-04 13:50:20
-
2020-09-25 17:10:11
-
2016-11-02 18:23:05
-
342019-07-19 19:18:52
-
222019-08-20 16:57:22
-
2008-08-23 12:37:29
-
2017-06-05 15:20:33
-
2021-01-02 22:44:08
-
2012-12-04 13:51:17
-
2015-01-13 22:56:29
-
2015-01-26 22:36:17
-
2013-11-18 14:12:50
-
02016-06-22 23:45:46
-
2017-09-08 09:44:42
-
2020-10-14 11:01:04
-
2009-06-26 08:48:33
无更多相似问答 去提问
点击登录
-- 积分
-- E币
提问
—
收益
—
被采纳
—
我要提问
切换马甲
上一页
下一页
悬赏问答
-
50如何获取vpss chn的图像修改后发送至vo
-
5FPGA通过Bt1120传YUV422数据过来,vi接收不到数据——3516dv500
-
50SS928 运行PQtools 拼接 推到设备里有一半画面会异常
-
53536AV100的sample_vdec输出到CVBS显示
-
10海思板子mpp怎么在vi阶段改变视频数据尺寸
-
10HI3559AV100 多摄像头同步模式
-
9海思ss928单路摄像头vio中加入opencv处理并显示
-
10EB-RV1126-BC-191板子运行自己编码的程序
-
10求HI3519DV500_SDK_V2.0.1.1
-
5有偿求HI3516DV500 + OV5647驱动
举报反馈
举报类型
- 内容涉黄/赌/毒
- 内容侵权/抄袭
- 政治相关
- 涉嫌广告
- 侮辱谩骂
- 其他
详细说明
提醒
你的问题还没有最佳答案,是否结题,结题后将扣除20%的悬赏金
取消
确认
提醒
你的问题还没有最佳答案,是否结题,结题后将根据回答情况扣除相应悬赏金(1回答=1E币)
取消
确认