1、Makefile中通用部分做公共头文件
Makefile中,都是先展开所有变量,再调用指令
- = 赋值,但是用终值,就是不管变量调用写在赋值前还是赋值后,调用时都是取终值。
- := 也是赋值,但是只受当前行及之前的代码影响,不受后面的赋值影响。
例:产生一个以下目录结构:
[blctrl@main-machine p1]$ ls -R
.:
d1 d2 Makefile
./d1:
a.cpp b.cpp c.cpp Makefile
./d2:
Makefile s1.c s2.c s3.c
当前目录下有目录d1,d2以及一个Makefile文件,此Makefile文件内容如下:
SOURCE:=$(wildcard ./*.cpp ./*.c)
OBJS:=$(patsubst %.cpp, %.o, $(SOURCE))
OBJS:=$(patsubst %.c, %.o, $(OBJS))
$(TARGET):$(OBJS)
$(CXX) $^ -o $(TARGET)
#%.o:%.c
# $(CXX) $^ -o $@
clean:
$(RM) *.o $(TARGET)
show:
echo $(SOURCE)
echo $(OBJS)
.PHONY:clean show
d1目录下,有三个C++源文件,以及一个Makefile,内容如下:
/* a.cpp */
#include <stdio.h>
void function1()
{
printf("d1-a-function1\n");
}//
/* b.cpp */
#include <stdio.h>
void function2()
{
printf("d1-b-function2\n");
}//
/* c.cpp */
#include <stdio.h>
extern void function1();
extern void function2();
int main(int argc, char * argv[])
{
function1();
function2();
return 0;
}
makefile:用include包含了上级目录中的Makefile文件
TARGET:=c
ifndef TARGET
TARGET:=test
endif
include ../Makefile
d2目录下,有三个C源文件,以及一个Makefile,内容如下:
/* s1.c源文件 */
#include <stdio.h>
void function1()
{
printf("d2-a-function1\n");
}//
/* s2.c源文件 */
#include <stdio.h>
void function2()
{
printf("d2-b-function2\n");
}//
/* s3.c源文件*/
#include <stdio.h>
extern void function1();
extern void function2();
int main(int argc, char * argv[])
{
function1();
function2();
return 0;
}
makefile:用include包含了上级目录中的Makefile文件
TARGET:=s3
ifndef TARGET
TARGET:=test
endif
include ../Makefile
子目录d1和d2中的Makefile共用了上级目录中的Makefile来编译自己目录中的源文件,并且生成了可执行文件。
执行结果,编译都成功了:
[blctrl@main-machine d1]$ make
g++ -c -o b.o b.cpp
g++ -c -o a.o a.cpp
g++ -c -o c.o c.cpp
g++ b.o a.o c.o -o c
[blctrl@main-machine d1]$ ./c
d1-a-function1
d1-b-function2
[blctrl@main-machine d1]$ cd ../d2
[blctrl@main-machine d2]$ make
cc -c -o s3.o s3.c
cc -c -o s2.o s2.c
cc -c -o s1.o s1.c
g++ s3.o s2.o s1.o -o s3
[blctrl@main-machine d2]$ ./s3
d2-a-function1
d2-b-function2
2、Makefile中调用shell命令
例:在一个目录下新建一个Makefile文件,其内容如下:
FILE:=rec
A:=$(shell ls ../)
B:=$(shell pwd)
C:=$(shell if [ ! -f $(FILE) ];then touch $(FILE);fi)
a:
echo $(A)
echo $(B)
echo $(C)
clean:
$(RM) $(FILE)
在这个Makefile中定义了变量FILE,A,B,C,A中存储shell命令ls ../的结果,即是当前目录的上一级目录中包含的文件,B中存储pwd的结果,即当前路径,C中执行一条if语句,如果FILE变量中指定名称rec的文件不存在,则创建一个此名称的文件。执行结果如下:
[blctrl@main-machine d3]$ ls
Makefile
[blctrl@main-machine d3]$ make
echo d1 d2 d3 Makefile
d1 d2 d3 Makefile
echo /home/blctrl/CLionProjects/Firtst/practice/d3
/home/blctrl/CLionProjects/Firtst/practice/d3
echo
[blctrl@main-machine d3]$ ls
Makefile rec
3、Makefile的嵌套调用
创建一个以下目录结构:
[blctrl@main-machine call]$ ls -R
.:
d4 d5 Makefile Makefile-head
./d4:
a.cpp b.cpp c.cpp Makefile
./d5:
Makefile s1.c s2.c s3.c
d4和到d5中的源文件和头文件分别来自第一部分(Makefile中通用部分做公共头文件)中的d1和d2,但Makefile中做了略微修改,把include语句修改为
include ../Makefile-head
顶层目录Makefile名称也修改为Makefile-head,然后新建一个Makefile,内容如下:
all:
make -C ./d4
make -C ./d5
clean:
make -C ./d4 clean
make -C ./d5 clean
执行make进行编译,结果如下:
make的-C参数为make制定了工作目录。
[blctrl@main-machine call]$ make
make -C ./d4
make[1]: Entering directory '/home/blctrl/CLionProjects/Firtst/practice/call/d4'
g++ -c -o b.o b.cpp
g++ -c -o a.o a.cpp
g++ -c -o c.o c.cpp
g++ b.o a.o c.o -o c
make[1]: Leaving directory '/home/blctrl/CLionProjects/Firtst/practice/call/d4'
make -C ./d5
make[1]: Entering directory '/home/blctrl/CLionProjects/Firtst/practice/call/d5'
cc -c -o s3.o s3.c
cc -c -o s1.o s1.c
cc -c -o s2.o s2.c
g++ s3.o s1.o s2.o -o s3
make[1]: Leaving directory '/home/blctrl/CLionProjects/Firtst/practice/call/d5'
[blctrl@main-machine call]$ ls -R
.:
d4 d5 Makefile Makefile-head
./d4:
a.cpp a.o b.cpp b.o c c.cpp c.o Makefile
./d5:
Makefile s1.c s1.o s2.c s2.o s3 s3.c s3.o
[blctrl@main-machine call]$ make clean
make -C ./d4 clean
make[1]: Entering directory '/home/blctrl/CLionProjects/Firtst/practice/call/d4'
rm -f *.o c
make[1]: Leaving directory '/home/blctrl/CLionProjects/Firtst/practice/call/d4'
make -C ./d5 clean
make[1]: Entering directory '/home/blctrl/CLionProjects/Firtst/practice/call/d5'
rm -f *.o s3
make[1]: Leaving directory '/home/blctrl/CLionProjects/Firtst/practice/call/d5'
[blctrl@main-machine call]$ ls -R
.:
d4 d5 Makefile Makefile-head
./d4:
a.cpp b.cpp c.cpp Makefile
./d5:
Makefile s1.c s2.c s3.c
从以上编译结果可以看出make进入了指定的d4,d5目录进行了编译,并且产生了二进制对象文件和可执行文件。
修改以上Makefile,功能与以上Makefile相同,内容如下:
DIRS:=d4 d5
ALL:$(DIRS)
$(DIRS):
make -C $@
clean:
echo $(shell for dir in $(DIRS);do make -C $$dir clean;done)
.PHONY:clean d4 d5
注意:ALL执行要依赖$(DIR)中指定目标的存在,由于声明了其指定的目标是伪目标,因此执行make ALL时,就会执行这些伪目标指定的命令语句。
4、Makefile中条件判断
- ifeq:判断是否相等,相等返回true,否则返回false
- ifneq:判断不是否相等,不相等返回true,否则返回false
- ifdef:判断是否存在,存在返回true,否则返回false
- ifndef:判断是否不存在,不存在返回true,否则返回false
ifeq, ifneq, ifdef, ifndef与条件之间要有空格,不然会报错。
ifeq ($(A),123)
else
endif
没有elseif的用法,如果要实现elseif,就要写嵌套。命令行传参数make -f Makefile FLAG=argument,如果有Makefile,则可写出make FLAG=argument
例:在一个目录下建立一个内容如下的Makefile文件:
[blctrl@main-machine d6]$ cat Makefile
VAR:=hello
VAR1:=
VAR2:=
ifeq ($(VAR),hello)
VAR1:=Hello-Yes
else
VAR1:=Hello-No
endif
ifdef VAR
VAR2:=VAR-defined
else
VAR2:=VAR-undefined
endif
ifndef FLAG
FLAG=default
endif
all:
@echo $(VAR1)
@echo $(VAR2)
@echo $(FLAG)
分别不带参数和带参数来执行make程序:
[blctrl@main-machine d6]$ make
Hello-Yes
VAR-defined
default
[blctrl@main-machine d6]$ make FLAG=argument
Hello-Yes
VAR-defined
argument
5、Makefile中的循环
foreacn的使用
例:
TARGET:=f1 f2 f3
FILE:=$(foreach v,$(TARGET),$v_name)
all:
touch $(FILE)
clean:
$(RM) $(FILE)
.PHONY:clean
执行make,结果如下:
[blctrl@main-machine d7]$ ls
Makefile
[blctrl@main-machine d7]$ make
touch f1_name f2_name f3_name
[blctrl@main-machine d7]$ ls
f1_name f2_name f3_name Makefile
6、Makefile自定义函数
自定义函数,不是真正的函数,本质上是多行命令,放在外面了。没有返回值。
定义:
define FUNC
endef
调用:
$(call FUNC, abc, def, $(A))
例:在一目录下,创建一个Makefile文件,内容如下:
define FUNC
@echo name=$(0),arg1=$(1),arg2=$(2)
endef
VAR:=Hello Make
all:
$(call FUNC,abc,$(VAR))
在上面的目录中,执行make,结果:
[blctrl@main-machine d8]$ make
name=FUNC,arg1=abc,arg2=Hello Make