一、编译和链接
编译是用编译程序把源代码文件代码译成目标代码(ObjectFile文件)的过程。链接是用链接程序把目标文件合成执行文件的过程。在编译是,编译器只检测程序语法,和函数、变量是否被声明。如果函数未被声明,编译器会给出一个警告,但可以生成目标文件。而在链接时,链接器会在所有的目标文件中找寻函数的实现,如果找不到,就会链接错误码。
二、命令行编译
例如我利用4个文件main.c:
#include<stdio.h>
int main()
{ int sum;
sum = add(1, 2);
show(sum);
return 0;
}
f_add.c:
int add(int a, int b)
{
return a + b;
}
f_show.c:
void show(int value)
{
printf("sum = %d\n", value);
stamp();
}
和f_stamp.c:
void stamp()
{
printf("This is a stamp!\n");
}
可直接生成可执行文件a.out
gcc main.c f_add.c f_show.c f_stamp.c
如果想生成可执行文件calc,可以有如下过程:
首先生成目标文件*.o
$ gcc -c main.c f_add.c f_show.c f_stamp.c
如果没有其它多余文件,可以使用通配符(gcc -c *.c)。再链接成为可执行文件
$ gcc -o calc main.o f_add.o f_show.o f_stamp.o -lm
现在我们就可以通过./calc运行calc文件了。
三、Makefile规则
target: prerequisites...
<tab>command
其中target就是一个(或多个)目标文件。可以是ObjectFile,也可以是执行文件,还可以是一个标(Label)。prerequisites是生成那个target所需要的文件。Command是生成target时,make要执行的命令(任意的shell命令)。如果prerequisites中有一个以上的文件target文件要新的话,command所定义的命令就会被执行。这是Makefile的核心内容。
因此我们可以写出如下的Makefile
calc: main.o f_add.o f_show.o f_stamp.o
gcc -o calc main.o f_add.o f_show.o f_stamp.o -lm
main.o: main.c f_add.c f_show.c
gcc -c main.c f_add.c f_show.c
f_add.o: f_add.c
gcc -c f_add.c
f_show.o: f_show.c f_stamp.c
gcc -c f_show.c f_stamp.c
f_info: f_info.c
gcc -c f_stamp.c
clean:
rm calc main.o f_add.o f_show.o \
f_stamp.o
注意每行需要执行的命令要以<tab>开头。反斜杠\是换行符。可以用#开头进行注释。clean不是一个文件,只是一个动作。
我们输入make命令后,系统会:
a、读入Makefile或makefile文件
b、读入被include的其他Makefile
c、初始化文件中的变量
d、推到隐晦规则,并分析所有规则
e、为所有目标文件(包括target)创建依赖关系链
f、决定那些目标要重新生成。即当目标文件不存在或其依赖文件的修改时间比目标文件的更新。
g、执行生成命令
这就是make的分层次文件依赖性。如果我们修改了一个源文件如f_stamp.c,那么f_stamp.o将被重新编译,进而clac也会被重新链接生成。在Makefile中向clean这种没被target直接或间接关联的,其后所定义的命令将不被自动执行。但可以通过make clean来执行。
四、自动推导
make会把每一个目标文件file.o文件就会把其对应的file.c文件加入到file.o的依赖文件中。
五、Makefile中使用变量
可以用变量来简化Makefile,如在前例可简化为
Objects = main.o f_add.o f_show.o f_stamp.o
calc: $(Objects)
gcc -o calc $(objects)
clean:
rm calc $(Objects)
参考:陈皓. 《跟我一起写Makefile》
声明:A 包含B
B 来自C
C 是怎么来的。
就像一棵树,开枝散叶
再来:
21、makefile常用语法讲解(1)
1、make是一个解释makefile中指令的命令工具。Make工具最主要也是最基本的功能就是通过makefile文件来描述源程序之间的相互关系并自动维护编译工作。而makefile 文件需要按照某种语法进行编写,文件中需要说明如何编译各个源文件并连接生成可执行文件,并要求定义源文件之间的依赖关系。
Makefile 里主要包含了五种类型的语句/行:显式规则、隐式规则、变量定义、文件指示和注释。
make命令格式:make [-f Makefile] [option] [target]
2、编译和链接规则
1)如果这个工程没有编译过,那么我们的所有C文件都要编译并被链接。
2)如果这个工程的某几个C文件被修改,那么我们只编译被修改的C文件,并链接目标程序。
3)如果这个工程的头文件被改变了,那么我们需要编译引用了这几个头文件的C文件,并链接目标程序。
3、makefile的构成
(1)需要由make工具创建的目标体(target),通常是目标文件或可执行文件。
(2)要创建的目标体所依赖的文件(dependency_file)。
(3)创建每个目标体时需要运行的命令(command)。
格式如下:
target:dependency_files
<TAB>command
在这里面,变量一般都是字符串,他有点像c语言的宏。
makefile中的文件指示,包含3部分,一个是在一个Makefie中引用另一个Makefile,就像c语言的include一样;另一个是根据某些情况指定Makefile中的有效部分,就像C语言中的预编译#if一样;还有就是定义一个多行的命令。
注释:注释符用"#",可以用反斜框进行转义,如输入注释,“\#”。
4、makefile的书写
1)所有文件都在一个目录中
示例
st_work : main.o st_work.o fun.o
gcc main.o st_work.o fun.o -o st_work main (命令以Tab开头)
st_work.o : st_work.c
gcc -c st_work.c -o st_work
main.o : main.c st_work.h
gcc -c main.c -o main.o
fun.o : fun.c fun.h
gcc -c fun.c -o fun.o
clean:
rm -f st_work *.o
2)多目录的写法
我们这里,在工作目录下有4个文件夹 分别是 sources(源文件) obj (中间文件)headers(头文件) bin(目标文件)
sources里面有 main.c st_work.c fun.c
obj 里面最初没有文件
headers 里面有 fun.h st_work.h
最终目标取名为 st_work,它应存放到bin里面
预备知识:
gcc 的3个参数:
1. -o 指定目标文件
gcc sources/main.c -o bin/main
2. -c 编译的时候只生产目标文件不链接
gcc -c sources/main.c -o obj/main.o
3. -I 主要指定头文件的搜索路径
gcc -I headers -c main.c -o main.o
4. -l 指定静态库
gcc -lpthread ...
示例
bin/st_work : obj/main.o obj/st_work.o obj/fun.o
gcc obj/main.o obj/st_work.o obj/fun.o -o bin/st_work (命令以Tab开头)
obj/st_work.o : sources/st_work.c
gcc -I headers -c sources/st_work.c -o obj/st_work.o
obj/main.o : sources/main.c
gcc -I headers -c sources/main.c -o obj/main.o
obj/fun.o : sources/fun.c
gcc -I headers -c sources/fun.c -o obj/fun.o
clean:
rm -f bin/st_work obj/*.o
3)隐式规则的引入
3个预定义变量介绍:
1. $@ 表示要生成的目标
2. $^ 表示全部的依赖文件
3. $< 表示第一个依赖文件
bin/st_work : obj/main.o obj/st_work.o obj/fun.o
gcc $^ -o $@ (命令一定要用以Tab开头)
obj/st_work.o : sources/st_work.c
gcc -I headers -c $< -o $@
obj/main.o : sources/main.c
gcc -I headers -c $< -o $@
obj/fun.o : sources/fun.c
gcc -I headers -c $< -o $@
clean:
rm -f bin/st_work obj/*.o
|
跟我一起写Makefile:使用条件判断 |
---|
[编辑]使用条件判断
使用条件判断,可以让make根据运行时的不同情况选择不同的执行分支。条件表达式可以是比较变量的值,或是比较变量和常量的值。
[编辑]示例
下面的例子,判断$(CC)变量是否“gcc”,如果是的话,则使用GNU函数编译目标。
libs_for_gcc = -lgnu
normal_libs =
foo: $(objects)
ifeq ($(CC),gcc)
$(CC) -o foo $(objects) $(libs_for_gcc)
else
$(CC) -o foo $(objects) $(normal_libs)
endif
可见,在上面示例的这个规则中,目标“foo”可以根据变量“$(CC)”值来选取不同的函数库来编译程序。
我们可以从上面的示例中看到三个关键字:ifeq、else和endif。ifeq的意思表示条件语句的开始,并指定一个条件表达式,表达式包含两个参数,以逗号分隔,表达式以圆括号括起。else表示条件表达式为假的情况。endif表示一个条件语句的结束,任何一个条件表达式都应该以 endif结束。
当我们的变量$(CC)值是“gcc”时,目标foo的规则是:
foo: $(objects)
$(CC) -o foo $(objects) $(libs_for_gcc)
而当我们的变量$(CC)值不是“gcc”时(比如“cc”),目标foo的规则是:
foo: $(objects)
$(CC) -o foo $(objects) $(normal_libs)
当然,我们还可以把上面的那个例子写得更简洁一些:
libs_for_gcc = -lgnu
normal_libs =
ifeq ($(CC),gcc)
libs=$(libs_for_gcc)
else
libs=$(normal_libs)
endif
foo: $(objects)
$(CC) -o foo $(objects) $(libs)
[编辑]语法
条件表达式的语法为:
<conditional-directive>
<text-if-true>
endif
以及:
<conditional-directive>
<text-if-true>
else
<text-if-false>
endif
其中<conditional-directive>;表示条件关键字,如“ifeq”。这个关键字有四个。
第一个是我们前面所见过的“ifeq”
ifeq (<arg1>, <arg2>)
ifeq '<arg1>' '<arg2>'
ifeq "<arg1>" "<arg2>"
ifeq "<arg1>" '<arg2>'
ifeq '<arg1>' "<arg2>"
比较参数“arg1”和“arg2”的值是否相同。当然,参数中我们还可以使用make的函数。如:
ifeq ($(strip $(foo)),)
<text-if-empty>
endif
这个示例中使用了“strip”函数,如果这个函数的返回值是空(Empty),那么<text-if-empty>;就生效。
第二个条件关键字是“ifneq”。语法是:
ifneq (<arg1>, <arg2>)
ifneq '<arg1>' '<arg2>'
ifneq "<arg1>" "<arg2>"
ifneq "<arg1>" '<arg2>'
ifneq '<arg1>' "<arg2>"
其比较参数“arg1”和“arg2”的值是否相同,如果不同,则为真。和“ifeq”类似。
第三个条件关键字是“ifdef”。语法是:
ifdef <variable-name>
如果变量<variable-name>的值非空,那到表达式为真。否则,表达式为假。当然,<variable- name>同样可以是一个函数的返回值。注意,ifdef只是测试一个变量是否有值,其并不会把变量扩展到当前位置。还是来看两个例子:
示例一:
bar =
foo = $(bar)
ifdef foo
frobozz = yes
else
frobozz = no
endif
示例二:
foo =
ifdef foo
frobozz = yes
else
frobozz = no
endif
第一个例子中,“$(frobozz)”值是“yes”,第二个则是“no”。
第四个条件关键字是“ifndef”。其语法是:
ifndef <variable-name>
这个我就不多说了,和“ifdef”是相反的意思。
在<conditional-directive>这一行上,多余的空格是被允许的,但是不能以[Tab]键做为开始(不然就被认为是命令)。而注释符“#”同样也是安全的。“else”和“endif”也一样,只要不是以[Tab]键开始就行了。
特别注意的是,make是在读取Makefile时就计算条件表达式的值,并根据条件表达式的值来选择语句,所以,你最好不要把自动化变量(如“$@”等)放入条件表达式中,因为自动化变量是在运行时才有的。
而且,为了避免混乱,make不允许把整个条件语句分成两部分放在不同的文件中。