一文掌握MAKEFILE和GCC

CC :=g++
LD :=g++
SRCDIR := src
BUILDDIR := build
TARGET :=bin/target

SRCEXT:=cpp
SOURCES:=$(shell find $(SRCDIR) -type f -name *.$(SRCEXT))
OBJECTS:=$(patsubst $(SRCDIR)/%, $(BUILDDIR)/%,$(SOURCES:.$(SRCEXT)=.o))
DEP:=$(OBJECTS:%.o=%.d)
CFLAGS:= 
LDFLAGS:= 
INC:= -I include

$(TARGET):$(OBJECTS)
    @echo "Linking..."
    @echo "$(LD) $^ -o $(TARGET) $(LIB)"
    $(LD) -o $(TARGET)  $^ $(LDFLAGS) 

-include $(DEP)
$(BUILDDIR)/%.o: $(SRCDIR)/%.$(SRCEXT)
    @mkdir -p $(sort $(dir $(OBJECTS)))
    @echo "$(CC) $(CFLAGS) $(INC) -c -o $@ $<"
    $(CC) $(CFLAGS) $(INC) -MM -MT $@ -MF $(patsubst %.o, %.d, $@) $<
    $(CC) $(CFLAGS) $(INC) -c -o $@ $<

    #$(CC) $(CFLAGS)  -MMD -c -o $@ $<
clean:
    @echo "cleaning...";
    @echo "$(RM) -r $(BUILDDIR) $(TARGET)";
    $(RM) -r $(BUILDDIR) $(TARGET)
.PHONY:clean


├── bin
│   ├── target
│ 
├── build
│   ├── main.d
│   ├── main.o
│   ├── code.d
│   └── code.o
├── include
│   └── code.hpp
├── makefile
├── src
    ├── main.cpp
    └── code.cpp

-k:它的作用是让make命令在发现错误时仍然继续执行,而不是在检测到第一个错误时就停下来。
-n:它的作用是让make命令输出将要执行的操作步骤,而不真正执行这些操作
-f :它的作用是告诉make命令将哪个文件作为makefile文件。如果未使用这个选项,标准版本的make

= 是最基本的赋值,makefile展开后最终的值
:= 是覆盖之前的值,决定于它在makefile中的位置当前值
?= 是如果没有被赋值过就赋予等号后面的值
+= 是添加等号后面的值


.PHONY是一个伪目标,可以防止在Makefile中定义的只执行命令的目标和工作目录下的实际文件出现名字冲突,
    另一种是提交执行makefile时的效率
    当一个目标被声明为伪目标后,make在执行此规则时不会试图去查找隐含规则来创建这个目标
访问shell内变量两个$$,访问makefile变量需要加$()或者${}
执行shell两种方式$(shell find $(SRCDIR), `find $(SRCDIR)`
echo:会在shell中显示echo这条命令和这条命令的输出结果
@echo:不会在shell中显示echo这条命令,但是会显示命令的输出结果
加上“-”,即使这条命令出错,makefile也会继续执行后续命令的

make -C subdir

make -C /lib/modules/`uname -r`/build M=`pwd`/drivers/net/usb obj-m=GobiNet.o modules

make -C /usr/src/linux M=/usr/src/linux/drivers/net/ethernet/intel/igb modules

* :表示目标文件的名称,不包含目标文件的扩展名。
+ :表示所有的依赖文件,这些依赖文件之间以空格分开,按照出现的先后为顺序,其中可能包含重复的依赖文件。
< :表示依赖项中第一个依赖文件的名称
? :依赖项中,所有目标文件时间戳晚的文件(表示修改过),依赖文件间以空格分开
@ :目标项中目标文件的名称
^ :依赖项中,所有不重复的依赖文件,以空格分开

shell环境下:

$$: 代表shell本进程的PID(Process ID)
$?: 最后运行结束的进程的结束码(返回值)
$*: 所有的的参数列表,以"$1 $2 ...$n"的形式表示
$@: 所有的参数列表,以"$1" "$2" ..."$n"的形式表示
$#: 所有参数的个数
$0: 运行程序的文件名

Shell脚本在target里才有效,其它地方都被忽略掉了
把每一行Shell脚本当作一个独立的单元
make在调用Shell之前先进行预处理,即展开所有Makefile的变量和函数。这些变量和函数都以$开头
make预处理时,所有以$开头的,它都不会放过。要想引用Shell自己的变量,应该以$$开头。
另外要注意,Shell自己的变量是不需要括号的

main:main.o code1.o code2.o
gcc -o main main.o code1.o code2.o
main.o:main.c code1.h code2.h
gcc -c main.c
code1.o:code1.c code1.h
gcc -c code1.c
code2.o:code2.c code2.h
gcc -c code2.c

main:main.o code1.o code2.o
gcc -o $@ $^
.c.o:
gcc -c $<
这个规则表示所有的 .o文件都是依赖与相应的.c文件的


-E: 预处理,主要是进行宏展开等步骤,生成的文件微test.i
gcc -E test.c

-S: 编译,生成汇编代码,生成的文件为test.S
gcc -S test.c

-c: 汇编:生成机器码,生成的文件未test.o
gcc -c test.c

(-o): 链接:生成可执行文件
gcc test.c (-o test)

-I后面紧跟着用户设定的编译器头文件查找路径
-L后面紧跟着用户设定的编译器库文件查找路径
-Wall,打开gcc的所有警告。
-W选项类似-Wall,会显示警告,但是只显示编译器认为会出现错误的警告
-Werror,它要求gcc将所有的警告当成错误进行处理。
-D,其意义是添加宏定义

1、wildcard : 扩展通配符
    src=$(wildcard *.c ./sub/*.c)
    wildcard把 指定目录 ./ 和 ./sub/ 下的所有后缀是c的文件全部展开。
2、notdir : 去除路径
    dir=$(notdir $(src))
3、patsubst :替换通配符
    $(patsubst <pattern>,<replacement>,<text> ) 
    $(patsubst %.c,%.o,$(dir) )
    patsubst把$(dir)中的变量符合后缀是.c的全部替换成.o,
4、    foo := a.o b.o c.o;
    bar := $(foo:.o=.c)
    将变量“foo”以空格分开的值中的所有的字的尾字符“o”替换为“c”,其他部分不变。

    
1、“=”    make会将整个makefile展开后,再决定变量的值。也就是说,变量的值将会是整个makefile中最后被指定的值。
            x = foo
            y = $(x) bar
            x = xyz
        在上例中,y的值将会是 xyz bar ,而不是 foo bar 。

2、“:=”    表示变量的值决定于它在makefile中的位置,而不是整个makefile展开后的最终值。
            x := foo
            y := $(x) bar
            x := xyz
        在上例中,y的值将会是 foo bar ,而不是 xyz bar 了。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章