makefile学习 (1)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/theArcticOcean/article/details/52744809

学习自《跟我一起写Makefile》,《linux网络编程》

生成可执行文件的过程分为编译,汇编,链接等阶段,make工程管理器可以根据文件的时间戳发现更新后的文件有选择的编译,通过读入规则文件来执行编译工作。在整个过程中,make工程管理器调用了gcc编译器。

编译和链接

Uinx下编译产生的.o文件是中间代码文件,object file. 只要源文件代码正确,那么我们就可以编译出相应的object file。到了链接阶段(链接函数和全局变量),链接器寻找object file,如果中间代码文件太多,则可以给其打包,生成Archive File,即.a文件,链接后生成可执行文件。
编译器只检查语法和函数、变量的声明,链接器则是寻找函数的实现。
makefile的规则:

target ... : prerequisites ...
    command
    ...
    ...

prerequisites(依赖文件)中如果有一个以上的文件比target文件要新的话,command所定义的命令就会被执行。make会比较targets文件和prerequisites文件的修改日期,如果prerequisites文件的日期要比targets文件的日期要新,或者target不存在的话,那么,make就会执行后续定义的命令。
clean不是一个文件,它只不过是一个动作名字。要执行其后的命令(不仅用于clean,其他lable同样适用),就要在make命令后明显得指出这个lable的名字。我们可以在一个makefile中定义程序的打包,程序的备份,等等。
注意:第一行的target是总的目标,后面的都是进行依赖处理。

gcc的-c和-o

gcc -c test.c #编译test.c文件得到test.o
gcc -c test.c -o test.o #同上
gcc -o test test.o #链接test.o可执行文件
gcc -o test test.c #将test.c编译并连接成可执行文件
gcc test.c -o test #同上

make工作方式

输入make命令后:
1. make会在当前目录下找名字叫“Makefile”或“makefile”的文件。
2. 如果找到,它会找文件中的第一个目标文件(target),并把这个文件作为最终的目标文件。
3. 如果target文件不存在,或是所依赖的后面的 .o 文件的文件修改时间要比target文件新,那么,他就会执行后面所定义的命令来生成target文件。
4. 如果target所依赖的.o文件也不存在,那么make会在当前文件中找目标为.o文件的依赖性,如果找到则再根据那一个规则生成.o文件。(这有点像一个堆栈的过程)

make只管文件的依赖性, 如果出现错误,比如最后被依赖的文件找不到,那么make就会直接退出,并报错

make变量

3 个预定义变量介绍:

1. $@ 表示要生成的目标  
2. $^ 表示全部的依赖文件  
3. $< 表示第一个依赖文件  

4个参数:

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

使用$(var)来代替变量var的值,用“$$”来表示$。
变量可以嵌套$($(x))
可以使用“+=”操作符给变量追加值。
更多变量说明,见:http://blog.csdn.net/thearcticocean/article/details/52729873 的makefile部分。

makefile的注释:

单行: 行首#
多行: 行首# 行尾\
比如:

#PWD := $(shell pwd)
#INC := $(PWD)/inc\
DEBUG := -g\
MAIN_OBJ := $(PWD)/*.o\
ADD_OBJ := $(PWD)/add/*.o\
SUB_OBJ := $(PWD)/sub/*.o\
OBJ := $(SUB_OBJ) $(ADD_OBJ)  $(MAIN_OBJ)\
\
main: $(OBJ)\
    cc  -o main $(OBJ) \
\
$(MAIN_OBJ) : $(PWD)/*.c $(INC)/*.h\
$(ADD_OBJ) : $(PWD)/add/*.c\
$SUB_OBJ) : $(PWD)/sub/*.c\

make自动推导

make的“隐晦规则”: 只要make看到一个[.o]文件,它就会自动的把[.c]文件加在依赖关系中,并推导出相关的命令。

关于tab缩进

在编写makefile的过程中,某一行出现红色那就要小心了,因为出现了错误。
编写makefile相关的vimrc配置:

set noexpandtab
set softtabstop=4

来自《linux网络编程》的一个加法、减法的工程例子:
这里写图片描述
文件目录稍作改动。
相关代码:
add_float.c

float add_float(float a, float b)
{
    return a+b;
}

add_int.c

int add_int(int a, int b)
{
    return a+b;
}

sub_float.c

float sub_float(float a, float b)
{
    return a-b;
}

sub_int.c:

int sub_int(int a, int b)
{
    return a-b;
}

add.h

#ifndef __ADD_H__
#define __ADD_H__
extern int add_int(int a, int b);
extern float add_float(float a, float b);

#endif /*__ADD_H__*/

sub.h

#ifndef __SUB_H__
#define __SUB_H__
extern float sub_float(float a, float b);
extern int sub_int(int a, int b);
#endif /*__SUB_H__*/

main.c

#include <stdio.h>
#include "./inc/add.h"
#include "./inc/sub.h"

int main(void)
{
    int input = 0;
    int a = 10, b = 12;
    float x= 1.23456,y = 9.87654321;

    printf("int a+b IS:%d  %d\n",a+b,add_int(a,b));
    printf("int a-b IS:%d  %d\n",a-b,sub_int(a,b));
    printf("float x+y IS:%f  %f\n",x+y,add_float(x,y));
    printf("float x-y IS:%f  %f\n",x-y,sub_float(x,y)); 
    return 0;   
}

现在,写一个简单的makefile,使得产生的.o文件都在mkfl下,然后链接即可。

PWD := $(shell pwd)
INC := $(PWD)/inc
ADD := $(PWD)/add
SUB := $(PWD)/sub
DEBUG := -g
OBJ := $(PWD)/*.o

.PHONY: all
all: lib
    gcc -o main $(OBJ)

.PHONY: lib
lib: 
    cc -c $(ADD)/*.c  
    cc -c $(SUB)/*.c
    cc -c $(PWD)/main.c $(INC)/*.h


.PHONY:clean
clean:
#make clean -C $(PWD)
    rm -f main *.o 

清洁文件

删除命令:
-rm -f main *.o
在rm命令前面加了一个小减号的意思就是,也许某些文件出现问题,但不要管,继续做后面的事。clean从来都是放在文件的最后

makefile的规则

显式规则:各种依赖关系和命令
隐式规则:利用make的自动推导

特定的makefile文件

例如写了一个makefile_special
那么可以提示make寻找并解释它 make -f makefile_special

makefile的嵌套

include <filename> 可以在本makefile中引入其他makefile。
在include前面加上’-‘也能暂时屏蔽掉错误信息,继续执行后面的东西。

通配符的展开和替换

如果想要通配符在变量中展开,那么我们需要关键字 wildcard(拓展通配符).
确定所有的.o文件 object := $(wildcard *.o)
如果想要进行通配集合的替换,那么需要关键字patsubst
确定所有.c文件对应的.o文件 object := $(pubsubst %.c, %.o, $(wildcard *.c))
[%为makefile的规则通配符]

文件搜索

vpath <pattern> <directories>
关键字vpath用于搜索符合pattern模式的文件,且在指定的路径下进行搜索。
比如 vpath %.c ./src
在src文件夹中查找所有的.c文件。
如果需要指定多个路径,那么我们可以一一列出。

vpath %.c dir1
vpath %.c dir2
vpath %.c dir3

那么make将会先搜索dir1, 让然后是dir2, 最后是dir3

伪目标

使用.PHONY来指定伪目标,它不是文件,仅仅是一个标签。我们可以显示的指定来进行make。
比如上面加减例子中的makefile,如果使用命令make lib,那么我们将生成所有的.o文件,但是可执行文件main是不会产生的。
利用伪目标生成多个可执行文件:

all : enjoy hello love
.PHONY: all

enjoy: enjoy.o
    cc enjoy.c -o enjoy
hello: hello.o
    cc hello.c -o hello 
love: love.o
    cc love.c -o love

.PHONY: clean
clean:
    rm -f *.o
    rm -f enjoy hello love

第一行指明终极目标,后面又指出这个all是个伪目标,所以不会产生all文件。但是其依赖的三个文件被创建出来了。
当然,你也可以写一个shell脚本,来几行gcc -o ...,这样也行。

自动搜索依赖头文件

gcc -M main.c #这会将库文件也打出来
gcc -MM main.c #仅输出include的头文件
还是以上面的加减例子做说明

cc -MM main.c
main.o: main.c inc/add.h inc/sub.h

[.d]文件中就存放对应[.c]文件的依赖关系

没有更多推荐了,返回首页