UNIX下C程序的编译与调试

更新时间:2023-10-30 17:03:01 阅读量: 综合文库 文档下载

说明:文章内容仅供预览,部分内容可能不全。下载后的文档,内容与下面显示的完全一致。下载之前请确认下面内容是否您想要的,是否完整无缺。

C语言编程培训教程——第三章 UNIX下C程序的编译与调试

三、UNIX下C程序的编译与调试

3.1 CC编译器

我们用cc来编译生成目标文件,或者生成可执行文件。其实cc可以生成包括.o目标文件、.i预处理文件等在内的中间过程文件。中间过程文件可以由后缀指出其类型:

.c:C源文件。默认处理过程是预处理、编译、汇编。 .C:C++源文件。默认处理过程是预处理、编译、汇编。 .cc:C++源文件。默认处理过程是预处理、编译、汇编。 .cxx:C++源文件。默认处理过程是预处理、编译、汇编。 .m:Objective-C源文件。默认处理过程是预处理、编译、汇编。 .i:经过预处理的C文件。默认处理过程是编译、汇编。 .ii:经过预处理的C++文件。默认处理过程是编译、汇编。 .s:汇编器的源文件。默认处理过程是汇编。

.S:汇编器的源文件。默认处理过程是预处理、汇编。 .h:预处理器需要的头文件。一般不出现在命令行上。 其他后缀的文件将送给连接器(linker)。一般包括:

.o:目标文件。 .a:归档文件(库文件)。

链接过程是上面处理过程的最后一步,除非指定了-c、-S或-E任选项。

一般cc的命令格式如下:

cc [option | filename]

每个任选项由“-”开头,每个任选项分开使用。例如:任选项-dr与任选项-d -r完全不同。下面看一些常用的任选项:

-c

只编译不连接,生成.o文件。编译器对每个源文件输出与该文件相符的目标文件。常常用于编译不包含主程序的子程序文件。

-o outputfile

指定输出文件的名字outputfile。不管指定名字的文件是一个可执行

文件、目标文件、汇编文件还是预处理文件。缺省的输出可执行文件是a.out。如果使用了-c参数,则缺省的输出文件为将源程序文件名的后缀改为.o。

-g

在编译时产生额外的符号表(调试信息),同时将选项-lg传给连接程序(ld)以 使它连接g库(/usr/lib/libg.a),从而使程序可用dbx调试。

-Ipathname 在搜索include文件的目录列表中增加新的目录。除非源程序中指定

了绝对路径,否则cc在编译时将先后在源程序所在的路径、由-I指定的路径和缺省路径/usr/include中寻找源程序中#include的文件。

-llibrary

连接指定的函数库。比如用-lm连接数学库。被连接的库将是库函数路 径中的lib**.so或lib**.a,其中**为-l后的字符串。

东大阿尔派电信事业部 曾 波 2000年7月22日 第 1 页

C语言编程培训教程——第三章 UNIX下C程序的编译与调试

-Ldirectory 在缺省的库函数路径(usr/lib)外增加另外的库函数寻找路径。

-temp=directory 指定编译连接时采用目录directory作为存放临时文件的路径。

如不指定,则临时路径为/tmp,当/tmp较小或已经较满时可能无法编译较大的程序,此时需指定temp路径。

-w -W -O -S

在编译时不列出警告信息。 显示额外的警告消息。

优化执行代码,有的编译器还可以通过指定-O1、-O2、-O3等来指定优化的 程度。

在编译步骤后停止,不进行汇编。输出的是一个可由汇编器(assembler)汇 编的文件。一般情况生成的可汇编文件名由源文件(文件名后缀为“.c”、“I”等)的更改为.s为后缀的文件。

-E -v -I-

在预处理后停止,不进行编译。输出文件是预处理过的代码,并送往标准输 出。

在标准错误输出上显示编译器执行各步骤的命令。也显示版本信息。 在-I-任选项之前由-I指定的任何目录只用来搜索#include “头文件名”,而 不用来搜索#include <头文件名>。命令行中-I-后的-I任选项指出的目录可以用来搜索所有#include包含的头文件。

-Dmacro 定义宏macro,宏macro的值为字符串“1”。

-Dmacro=defn 定义宏macro,其值为defn。所有命令行上的-D任选项在-U任选项

之前处理。

-Umacro 取消宏macro的定义。该任选项在-D任选项之后处理,但在-include任

选项和-imarcos任选项之前处理。

-static -shared

1、如果所有程序都在一个文件myprog.c中,那么只需用

cc myprog.c

即可完成编译连接的全过程,所生成的执行文件是a.out。

举例:

当系统支持共享库链接时,才支持该任选项。该任选项阻止链接器链接 共享库。

产生一个可共享的目标,其他目标文件链接该目标形成可执行文件。

2、如果源程序由两个文件mymain.c和mysubs.c组成,程序中使用了系统的数学库,我们希望生成的执行文件叫myprog,则编译连接的命令为:

cc mymain.c mysubs.c –lm –o myprog

东大阿尔派电信事业部 曾 波 2000年7月22日 第 2 页

C语言编程培训教程——第三章 UNIX下C程序的编译与调试

3.2 dbx调试器

Unix系统下有若干调试工具软件,比较常用的有dbx。目前还常见的是GNU的gdb和X下dbx是一种源程序级的调试工具。虽然dbx不象在IDE(集成开发环境)中那样使用热键,但对于IDE中各种功能在dbx中都可以找到等同的命令。

要使用dbx调试程序,首先在编译生成执行文件时要加上调试信息,即使用-g任选项。否则dbx调试时,没有符号表,将只能看到汇编命令。例如:

cc -g example.c progmain.c -o myprog

这样在dbx中就可以按源代码跟踪。注意:dbx不仅可以用于调试C程序,对其他语言的程序也可以用dbx调试,而且可以对汇编程序跟踪。

3.2.1运行dbx

dbx命令的格式为:dbx [options] [progname corefile]] 例如: % dbx a.out

% dbx myprog core

指定core文件,可以查看程序是在执行什么语句时coredump的。dbx启动后,如果没有指定core文件,dbx会在当前目录中查看是否有core文件;如果没有就不使用core文件。如果在命令中没有指定progname或corefile,可以在启动后,用givenfile和corefile命令设定。启动dbx时,dbx会在用户目录下查找.dbxinit配置脚本;如果没有找到,则使用系统缺省的设置。在.dbxinit文件中,包含的是一些dbx命令,dbx启动时,首先执行这些命令。例如编辑.dbxinit文件为:

set $editor = vi set $lines = 20 alias n next

图形界面的调试工具ddd。

除了使用.dbxinit来设置dbx外,可以用环境变量DBXINIT来设置选项。dbx会将DBXINIT插入到命令行选项之前。下面看看部分dbx的命令行任选项:

-I dir 指定在dir目录搜索源程序文件。

-c file 指定file为启动时执行的命令文件,代替使用.dbxinit文件 -p PID#

3.2.2 dbx命令

进入dbx后就可以交互的使用dbx命令了。下面介绍一些常用的dbx命令:

givenfile [filename]

当该命令没有参数时,显示当前调试的程序文件名,当给出

filename参数时,读入新的filename程序的符号表,并等待调试。

调试指定进程号为PID的进程。

-p name 调试指定名字为name的进程(由ps可以看到)。

东大阿尔派电信事业部 曾 波 2000年7月22日 第 3 页

C语言编程培训教程——第三章 UNIX下C程序的编译与调试

corefile [filename] use dir dir dir

当该命令没有参数时,显示当前调试的core文件名,否则用

新的filename代替原来的core文件。

指定dir目录为唯一的源程序所在路径。 在源程序所在路径中新增一个dir路径

列出源代码的若干行。如果不指定参数,则列出当前行以下若干

file sourceprog.c 指定当前调试程序的源程序文件为sourceprog.c list [n1[:n2]]

行。仅指定n1时,列出从行号为n1后的若干行。指定参数n1:n2时,列出行号n1到行号n2之间的程序源代码。

whatis symbol /string ?string

用来检查程序中的变量和子程序的类型定义。

从当前行开始在源代码中向下查找string字符串,就象vi中的一样。查找从当前行开始在源代码中向上查找string字符串,就象vi中的一样。查找

到后,改变当前行为查找到的行。 到后,改变当前行为查找到的行。

edit source.c 使用指定的编辑器编辑source.c文件。

run [args] 执行被调试的程序(在遇到断点时停止)。参数args将传递给执行程序,相当于执行程序的命令行参数。

rerun 按上次的执行参数,再次执行被调试的程序。 stop at [filename:]n1 [if exp]

在行号n1所在语句处设置断点,如果指定

filename参数则表示哪个源文件的n1行。例如:stop at “source.c”:55。参数if exp是条件控制,即当exp为真时,才设置断点。例如:stop at 55 if i ==22,即当程序中变量i等于22时,在55行设置断点。

stop in procname [if exp] 在进入子程序procname()后设置断点。条件控制的使用与stop at相同。

when [at n1]|[in procname] [if exp] {dbx命令} 程序处或if exp条件满足时,执行dbx命令。

assign var = value

给程序中的变量赋值。

print exp 显示变量或表达式的值。

alias [str1] [str2] 定义别名。如果参数为空,则列出已有别名定义。例如:alias r run执行后,可以用r代替run命令。

unalias aliasname 取消别名aliasname的设置。 history

类似于C Shell中的history命令。

从当前的停止处继续执行程序。当使用参数时,表示继

cont [to procname|n1]

当运行到n1行或procname()子

续执行到指定的子程序或者行号处。

dump [proc] 不带参数时,列出所有进程的全局变量信息。指定子程序时,列出proc中的变量信息。

down [n] 程序执行的堆栈移到下一层(或n层)。

东大阿尔派电信事业部 曾 波 2000年7月22日 第 4 页

C语言编程培训教程——第三章 UNIX下C程序的编译与调试

func proc 移到指定的子程序中。 goto n1 next [n] 条语句。

step [n]

向下执行一条(或n条)语句。注意step跟踪进入子程序中。

在指定行n1或指定子程序proc

trace {[var] at n1 [if exp]|[var] in proc [if exp]} printregs 显示当前寄存器的值。

help [command] 不带参数时,显示dbx所有的命令。指定参数时,显示指定命令的帮助信息。

quit

退出dbx。

转移到指定的行。

向下执行一条(或n条)语句。注意next不进入子程序中,把子程序当成一

中跟踪或条件exp为真跟踪程序的运行(或程序运行时的变量变化)。

东大阿尔派电信事业部 曾 波 2000年7月22日 第 5 页

C语言编程培训教程——第三章 UNIX下C程序的编译与调试

3.3 make

设计make程序起初是为了避免不必要的重复编译。使用make不仅可以在一个工程的开发make阶段避免对同样命令的多次键入,而且对维护程序文件也十分有用,尤其对于依赖关系,可以非常有效地维护任何有相互依赖性的多个文件集合。它可以用来维护C、C++或HTML源代码,对于有依赖关系的文件集都可以起到一定的维护能力。它提供了编写文件依赖性的一个规则,和对已经改变了的文件执行什么命令来维护或编译。它提供了维护文件的一个强大的、非过程的和基于模板的方法,用户只需告诉make要做什么并提供一些规则,然后由make来做剩下的工作。这里介绍make的常规用法和目的,即使用make和makefile来维护我们开发过程中的文件集合。

使用make来维护.o文件和可执行文件,有利于一致地编译程序。它根据程序中模块的修改情况,自动判断应对哪些模块重新编译,保证软件由最新的模块构成。

3.3.1 makefile规则

make命令执行的操作依赖于当前目录中的名为makefile或Makefile的文件(缺省的文件名,用户也可以指定别的文件名),这一文件描述系统中各模块之间的相互依赖关系,以及要产生程序的目标文件所要执行的命令。make的基本思想是为每一模块都设置一个时间标记,根据时间标记和依赖关系来决定哪些文件需要更新。

makefile是一个make的规则描述脚本。它描述能生成什么文件集并怎样生成,它允许有四种类型行:目标行、命令行、宏定义行和make伪指令行(如“incldude”)。makefile文件中注释以“#”开头。当一行写不下时,可以用“\\”字符转入下一行。

目标行

目标行告诉make建立什么。它由一个目标名表后面跟冒号“:”,再跟一个依赖性表组成。目标名表可以包含多个目标的名字,一般情况下只列出一个目标名。目标名表不能为空。依赖性表,指出目标和哪些其他文件或目标具有依赖关系。依赖性表可以为空。下面是目标行的一个例子:

example: depfile deptarget

该目标行指出目标example与depfile和deptarget有依赖关系,如果depfile和deptarget之一修改,则应该重新生成目标。

examp1 examp2 examp3: deptarget1 deptarget2 depfile

该目标行指出目标名表中的examp1、examp2、examp3这三个各自独立的目标是用相同的依赖列表和规则生成的。

clean:

空的依赖列表说明目标clean没有其他依赖关系。

make执行时,按依赖深度优先顺序扫描处理遇到的目标。即当前目标的依赖表中有其他目标名,则立刻去扫描该目标,直到当前目标依赖表中的目标都扫描过后,才确定当前目标

东大阿尔派电信事业部 曾 波 2000年7月22日 第 6 页

C语言编程培训教程——第三章 UNIX下C程序的编译与调试

是否需要重新生成。如果有比当前目标更新的依赖目标,或依赖的目标不存在,则必须生成相关目标。注意依赖表为空,则总是生成目标。如果目标依赖的文件或目标名比当前目标旧,则不生成目标。

目标行后续的以Tab开始的行是指出目标生成规则。该Tab字符不能以空格代替。例如:

example.o: example.c example.h cc -c example.c

该例子指出目标example.o依赖于example.c和example.h。如果example.c或example.h其中之一改变了,就需要重新生成目标example.o,如果要重新生成目标,则要执行命令cc -c example.c如果example.c和example.h都没有改动,则不生成目标。

可以用文件名模式匹配来自动地为目标生成依赖表,可以使用shell通配符“*”、问号“?”和中括号“[]”。例如makefile所在的目录下,所有以.c结尾的文件:

main.c check.c treatment.c 则make下面二个目标的效果是等同的:

prog: main.c check.c treatment.c

prog: *.c

一般地,包含依赖元素的列表用于构造一个目标。如依赖性表可用来组成一个目标文件。目标文件能构成可执行文件:

Program:Part.o component.o module.o 或目标依赖的头文件和源文件: module.o: module.c module.h part.h 下面看一个简单的makefile的例子:

all: hello hello1 hello: hello.o

cc -o hello -L/usr/X11R6/lib -L/usr/lib -lXm -lXt -lX11 hello.o

hello1: hello1.o

cc -o hello1 -L/usr/X11R6/lib -L/usr/lib -lXm -lXt -lX11 hello1.o

hello.o: hello.c

cc -c hello.c

hello1.o: hello1.c

cc -c hello1.c

clean:

rm *.o hello hello1

make使用makefile文件时,从第一个目标开始扫描,从这个例子中我们看到,make程序将先看目标all的依赖关系表,查看依赖表中的依赖目标是否比目标新,如果目标存在并且不是旧的,则不生成新的目标。如果目标不存在则生成新的目标。这里因为没有all文件,所以查看依赖表。首先扫描hello,如果目标hello也不存在,则查看hello的依赖表。hello依赖hello.o,这里假设hello.o也不存在或比目标新,那么再扫描hello.o目标,而hello.o目标依赖hello.c文件,如果hello.c文件自上次make以来没有改动则hello.o不需要改动,这时,返回去,由cc -o

东大阿尔派电信事业部 曾 波 2000年7月22日 第 7 页

C语言编程培训教程——第三章 UNIX下C程序的编译与调试

hello-L/usr/X11R6/lib -L/usr/lib -lXm -lXt -lX11 hello.o生成目标hello,然后返回all目标再看依赖目标hello1。如果hello.c修改过则由命令cc -c hello.c来生成目标hello.o。这里的例子,没有make总是从第一个目标开始。使用隐含规则。在执行make时,所以目标clean不会自动被执行。可以用命令行make clean来生成目标,因为目标的依赖表为空,所以make clean,总是要求生成目标。而生成目标的命令是rm *.o hello hello1所以生成目标就是删除了hello.o、hello1.o、hello、hello1四个文件。

使用后缀规则的目标

make有自己规定的通用目标,也就是后缀规则。后缀规则是一次性告诉make如何建立某些类型目标。前例中有许多重复内容。例如,生成hello和hello1的命令类似,生成hello.o和hello1.o的命令类似。除了编译或链接的文件不一样,其他都一样。这时,我们就可以使用后缀规则。下面的.c.o规则告诉make怎样把以.c结尾的文件转换为以.o结尾的文件:

.c.o:

cc -c $<

在这个.c.o规则中用到了宏“$<”,它以当前的依赖文件名代入规则中。有关这些特殊的宏将在后面介绍。把.c.o规则用到前面的例子中可以简化为:

LDFLAGS=-L/usr/X11R6/lib -L/usr/lib -lXm -lXt -lX11 .SUFFIXES: .SUFFIXES: .c .o .c.o:

$(CC) -c $< all: hello hello1

hello hello1: hello.o hello1.o

$(CC) $(LDFLAGS) $@.o -o $@

这里也用到了宏定义,这些都将在后面介绍。现在我们看看用make时,屏幕的输出: %make

cc -c hello.c cc -c hello1.c

cc -L/usr/X11R6/lib -L/usr/lib -lXm -lXt -lX11 hello.o -o hello cc -L/usr/X11R6/lib -L/usr/lib -lXm -lXt -lX11 hello1.o -o hello1

这时,已经编译好了hello和hello1两个执行文件。如果这时再执行make将得到:

Nothing to be done for `all'.

这是因为目标all依赖的两个目标hello和hello1,因为目标all不存在,所以要检查它的依赖表,而它依赖的hello和hello1两个目标已经是最新的,它们依赖的hello.o和hello1.o都不比hello和hello1新,所以目标hello和hello1不需要再次生成,而all目标没有指定如何生成all(实际上我们也不需要生成目标all文件)。

make提供了许多默认缺省的后缀规则,那么make是如何使用后缀规则的呢?用下述方法能决定用哪个后缀规则:

东大阿尔派电信事业部 曾 波 2000年7月22日 第 8 页

C语言编程培训教程——第三章 UNIX下C程序的编译与调试

首先,make中文件类型由它的后缀(扩展名)定义,例如C语言源文件就用.c作为后缀,make搜索默认缺省的后缀规则表和用户指定的后缀规则目标文件(未链接的文件)以.o为后缀。

表,如果找到哪个可以建立所请求文件类型的规则,而且规定类型的文件存在,则应用规则。例如,如果一个makefile仅规定了两个规则,顺序为“.c.o”和“.cc.o”两个规则,则make生make首先试图用.c.o规则,成目标foo.o时,使用后缀规则的步骤为:因为目标foo.o的基名是foo,根据.c.o规则查找默认的目录下是否有foo.c,如果有则编译。如果foo.c不存在,则不能应用.c.o规则。这时试图用.cc.o规则,根据.cc.o规则查找默认的目录下是否有foo.cc,如果有则编译。如果foo.cc不存在,则报不能生成目标错误。

有的make不支持后缀规则的传递闭包,也就是说如果存在后缀规则从.x文件生成.y文件,make不会把这两个规则组合起来形成传递,还存在另外一个后缀规则从.y文件生成.z文件,也就是说make不会从.x文件生成.z文件。这时要指定作为目标的中间步骤。但有的版本的make支持后缀规则的传递,他们将它称为规则链接。

双后缀规则与单后缀规则

前面介绍的规则都是双后缀规则,即它们包括两个后缀,如.c.o,用来把一个C源文件编译为目标文件。双后缀规则描述的是如何由第一个后缀类型的文件生成第二个后缀类型的文件。例如:.c.o规则描述如何由.c文件生成.o文件。

单后缀规则描述了怎样由指定后缀的文件生成由它基名为名字的文件。例如使用单后缀规则.c,可以由example.c生成example文件。在单后缀规则中,事实上第二个后缀是空。

make有关C的标准后缀规则:

.c

$(LINK.c) -o $@ $< $(LDLIBS) .c.ln

$(LINK.c) $(POUTPUT OPTPUT OPTION) -i$< .c.o

$(COMPILE.c) $(OUTPUT OPTION) $< .c.a

$(COMPILE.c) -o $% $<$(AR) $(ARFLAGS) $@ $%$(RM) $%

特殊目标

make提供了几个特殊目标来设置make的行为,它们中的一些可以为它指定依赖表,但实际上指定的依赖表是特殊目标的参数,例如:.SUFFIXES的依赖表指出的是新增的后缀规则。下面列出了一些特殊目标:

.IGNORE:增加该目标后,make将忽略建立目标时命令行返回非零的错误码。当命令行返回非零值时,make缺省动作是停止并退出。

.SILENT:增加该目标后,make执行的命令行不再显示。缺省时,make显示命令行后执行命令行,但是对前面有“@”号的命令行不显示。

东大阿尔派电信事业部 曾 波 2000年7月22日 第 9 页

C语言编程培训教程——第三章 UNIX下C程序的编译与调试

.DEFAULT:如果没有应用别的规则,make就执行.DEFAULT的命令行。

.PRECIOUS:当收到一个信号或从shell命令返回的非零代码时make删除它所有已建立的文件。但有些文件即使出了错误,用户也不想让make删除,这些文件可以作为.PRECIOUS目标的参数。它可以在一个makefile中出现多次,每一次都累积文件列表。

.SUFFIXES:它为make指定新的后缀规则,新的后缀规则是作为.SUFFIXES的依赖表来给出的。.SUFFIXES可以在一个makefile中多次使用,每一次都将新的后缀规则加入以前的后缀规则中。例如:.SUFFIXES: .ms.tr。但.SUFFIXES的依赖表为空时,将重设置后缀规则表为空。后缀的顺序很重要。因为它是尝试规则的顺序。为了重新排列顺序,使用空依赖表的.SUFFIXES目标来清空当前后缀表,然后顺序使用它就能规定新顺序。

习惯上的目标名

在makefile中有一些常用目标名,它们通常不是文件,称为信息目标,最常用的一个是clean,命令make clean通常删除所有的建立文件,它们常常是一些程序和目标文件。对于makefiles建立和安装软件,常用的目标是install命令,makeinstall通常用来创建程序,安装联机和把程序拷到指定地主。另一个常用目标是all,当makefile建立几个目标时,make all一般建立所有的目标。

命令行

命令行用来定义生成目标的动作,简单地说就是shell命令行。在目标行中在分号“;”后面的任何文字都认为是一个命令,或者一行以Tab制表符开始也是命令。

在命令行可以使用宏,由make用正确的值来代替它,因此,命令行只看到宏的值。下一小节将介绍宏。一般情况下,命令行的命令会在标准输出中回显出来,除非命令前有@字符,或使用了.SILENT特殊目标,或在make指定-S命令行参数。注意,这样只是抑制命令本身回显,对于命令本身的输出并不抑制。注释可以出现在命令行后,以“#”字符开头。

在makefile中,宏的作用类似shell中的宏。使用宏可以减少用户的输入。宏还可以通过make的命令行参数或环境变量来修改。使用make的命令行参数或通过环境变量来修改宏的值,可以达到不用修改makefile就可以取得所要的效果。宏定义可以从四个地方获得:make内部缺省的宏;环境变量定义的宏;makefile文件中定义的宏;和make命令行参数中指出的宏。它们的处理顺序可以通过命令行选项来改变。

在makefile中定义宏的基本语法是:

name = value

其中宏的名字name是一个标识符,满足平时我们对标识符的要求,即可以由写字母“A-Z”和小写字母“a-z”、数字(0-9)、和下划线(-)组成。习惯上宏名字全部大写。等号“=”类

东大阿尔派电信事业部 曾 波 2000年7月22日 第 10 页

C语言编程培训教程——第三章 UNIX下C程序的编译与调试

似赋值语句中的操作符。在等号的周围的空格和制表符都被删除。value可以包含零个、一个或多个入口,例如:

CFLAGS= CC=cc

LDFLAGS=-lXm -lXt -lX11

若它太长可用反斜杠“\\”换行在下一行继续定义。如果该行继续,则换行符被翻译是空格并且所有后面的空白符(空格、tab、和换行)被删除。

可以将一个宏赋值给另一个宏,但这样的定义不能循环嵌套。 可以使用“$”字符和“()”或“{}”来引用宏。例如:

$(MARCO) ${MARCO}

它们等价的引用宏MARCO。

如果宏的名字只有一个字符,可以省略括号。例如:

$s $(s) ${s}

上面三个是等同的。

如果有makefile中需要使用“$”字符,可以用另一个“$”字符来转义。这有点象C语言中输出字符串时,两个连续的“\\”代表一个“\\”字符而不再是转义符。连续的“$”字符不表示宏,因为第一个“$”字符已经指出后面的“$”是字符而不再是指出宏定义的引用。

特殊的宏

为简单使用规则,make提供了几个特殊的宏。这种宏使用的很广泛,特别在隐含规则的定义中。这些特殊的宏也可以没有值,这取决于当调用宏时make处于什么状态。一些宏仅在后缀规则调用中有效,而有些宏则在目标规则调用中有效。这些宏是:

$@:整个当前目标名的值可以由宏“$@”来代替。在使用库目标时,宏的值是库,而不是放在库中的模块的名字。宏“$@”能用于目标和后缀规则。例如:宏“$@”可以替代目标libmy(foo.o)中的libmy

$%:对于库来说,当前模块的名字可以由宏“$%”来代替,所以该宏仅在当前目标是库时才有效。宏“$%”能用目标和后缀规则。例如:宏“$%”可以替代libmy(foo.o)中的模块名foo.o。

$?:该宏的值是比目标新的依赖表。宏“$?”能用于目标和后缀规则,然而“$?”代替了在一个目标规则中的许多可能的名字,而且也代替了在一个后缀规则中的一个名字。

$<:当前的源文件由“$<”来代替,根据被调用的显式规则,当前的源文件比当前的目标老的文件。例如:在前面有例子指出.c.o规则,在指出规则的生成命令时,用到了$(CC) -c $<,其中的/“$<”是所有要编译的.c文件。宏“$<”仅在后缀规则或.DEFAULT中有效。

东大阿尔派电信事业部 曾 波 2000年7月22日 第 11 页

C语言编程培训教程——第三章 UNIX下C程序的编译与调试

$*:当前目标的基名由宏“$*”来代替。前面已经对基名提到过多次,例如目标的名字是hello.o,则基名就是除去了后缀.o的hello。宏“$*”仅在调用引用规则时有效。

伪 指 令

makefile大部分由宏定义行,命令行和目标行组成。第四种类型是make伪指令行。make伪指令没有标准化,所以不同的make可能支持不同的伪指令集。使得makefile有一定的不兼容性,所以如果要考虑移植性问题,则要避免使用make伪指令。但有一些伪指令如include由于使用比较多,使得很多不同make都提供该伪指令。

伪指令include

该伪指令类似C语言中的#include,它允许一次编写常用的定义并包括它。include伪指令必须是在一行中,第一个元素必须是include,并且跟一个要包含的文件名,例如:include default.mk。注意 不要在include伪指令前加一个“#”字符,否则make会认为它是一个注释,而不会包括该文件。

伪指令“#”

“#”字符也是make的伪指令。它指出“#”字符后面的文字是注释。

用make命令加 –p参数可以打印出系统缺省定义的内部规则、预定义的宏以及产生某些种类后缀的文件的内部相关行。系统有一个文件描述这些规则,在我们现在的系统上是:

/usr/oracle/rdbms/lib/env_rdbms.mk

3.3.2 make的用法

make 命令的基本用法是:

make [-f makefile] [选项] [目标文件]

-f makefile 指定make所采用的描述文件的文件名,缺省为当前目录下的makefile或目标文件

指定要更新的目标文件名,如果不指定,则make从makefile文件中的第一

Makefile(按先后次序寻找)。 个目标开始检查和更新所有目标。

其它选项有:[-c | -C] [-F | -i | -k | -S] [-n | -p | -s]等,请用man命令自行查询。

东大阿尔派电信事业部 曾 波 2000年7月22日 第 12 页

C语言编程培训教程——第三章 UNIX下C程序的编译与调试

东大阿尔派电信事业部 曾 波 2000年7月22日 第 13 页

C语言编程培训教程——第三章 UNIX下C程序的编译与调试

Make使用的描述文件:makefile或Makefile或其它文件。

描述文件中的相关行包括目标、依赖模块和命令,描述三者之间的关系。一个相关行是

这样构成的:用冒号将目标和依赖模块分开,冒号左边是目标名(比如软件的目标程序名),冒号右边是目标所依赖的模块名,说明哪些文件发生改变时需重新产生目标;下面的命令行是重新产生目标所需使用的命令。相关行的具体格式为:

target ... [:/::] [dependency] … [; command] … target...[:/::] [dependency] …

command1 command2 … …

习惯上通常写成多行形式:

需要注意的是,如果相关行写在一行中,则各个command之间要用分号隔开,如果分成两行或多行书写,后续的行一定要顶头或以tab键为先导,行前不能是空格,否则make将不能识别该行内容。

在描述文件中,可以以#开头来注释语句。

一个描述文件中可以安排多个模块的相关行,生成多个可执行模块;同时,make的目标

也可以不是生成可执行程序,而是执行其它命令。 举例:

主源文件ruprov.c,runation.c,辅助源文件public.c。

描述文件makefile的内容如下:

include $(ORACLE_HOME)/rdbms/lib/env_rdbms.mk RDBMSLIB=$(ORACLE_HOME)/rdbms/lib/

LDFLAGS=-L$(LIBHOME) -L$(ORACLE_HOME)/rdbms/lib LLIBPSO=`cat $(ORACLE_HOME)/rdbms/lib/psoliblist` FC=f77 CPLPL=cxx

INCLUDE= -I$(ORACLE_HOME)/rdbms/demo -I$(ORACLE_HOME)/rdbms/public -I$(ORACLE_HO ME)/plsql/public -I$(ORACLE_HOME)/network/public CONFIG = $(ORACLE_HOME)/rdbms/lib/config.o SHARED_LDFLAG=$(LDFLAGS) -shared -o NONDEFER=false

OCISHAREDLIBS=$(LLIBCLNTSH)

OCISTATICLIBS=$(STATICTTLIBS) $(LLIBTHREAD) PSOLIBLIST=$(ORACLE_HOME)/rdbms/lib/psoliblist CLEANPSO=rm -f $(PSOLIBLIST); $(GENPSOLIB) DOLIB=$(ORACLE_HOME)/lib/liborcaccel.a

东大阿尔派电信事业部 曾 波 2000年7月22日 第 14 页

C语言编程培训教程——第三章 UNIX下C程序的编译与调试

DUMSDOLIB=$(ORACLE_HOME)/lib/liborcaccel_stub.a REALSDOLIB=/usr/lpp/orcaccel/liborcaccel.a PROC=$(ORACLE_HOME)/bin/proc

PCCINCLUDE= include=$(ORACLE_HOME)/precomp/public PCCI=-I$(ORACLE_HOME)/precomp/public USERID=scott/tiger

PCCPLSFLAGS= $(PCCINCLUDE) ireclen=132 oreclen=132 sqlcheck=full \\ ltype=none user=$(USERID) LLIBSQL= -lsql

PROLDLIBS= $(LLIBSQL) $(TTLIBS)

MAKEFILE = makefile .SUFFIXES: .o .cob .for .cc

COMPILE.C= $(ECHODO) $(CC) $(LDFLAGS) $(INCLUDE) -c $(OCISHAREDLIBS) MAKEEXE=$(ECHODO) $(CC) $(LDFLAGS) $(OCISHAREDLIBS) TIMECOP = cc ${CFLAGS} ${CPPFLAGS} -c

runation: runation.o public.o $(MAKEEXE) -o runation runation.o public.o -lcurses runation.o: runation.c $(COMPILE.C) runation.c public.o:public.c

$(COMPILE.C) public.c

#--------------------------------------------------------------------------- ruprov: ruprov.o public.o

$(MAKEEXE) -o ruprov ruprov.o public.o -lcurses ruprov.o: ruprov.c

$(COMPILE.C) ruprov.c

#---------------------------------------------------------------------------

执行:make ruprov 显示执行的命令如下:

(1)/usr/oracle/bin/echodo cc -L/usr/oracle/lib/ -L/usr/oracle/rdbms/lib -I/usr/oracle/rdbms/demo

-I/usr/oracle/rdbms/public -I/plsql/public -I/usr/oracle/network/public -c -lclntsh ruprov.c (2)cc -L/usr/oracle/lib/ -L/usr/oracle/rdbms/lib -I/usr/oracle/rdbms/demo -I/usr/oracle/rdbms/public

-I/plsql/public -I/usr/oracle/network/public -c -lclntsh ruprov.c (3)/usr/oracle/bin/echodo cc -L/usr/oracle/lib/ -L/usr/oracle/rdbms/lib -I/usr/oracle/rdbms/demo

东大阿尔派电信事业部 曾 波 2000年7月22日 第 15 页

本文来源:https://www.bwwdw.com/article/idi2.html

Top