1 、基本框架
Target:Dependence
cmd
//test.c:
#include<iostream>
int main(void){
std::cout<<"hello world"<<tsd::endl;
}
Makefile:
APP=myapp
COMPILER=g++
all : $(APP)
$(APP) : test.o
$(COMPILER) -o $(APP) test.o
test.o : test.c
$(COMPILER) -c test.c
命令:
make
./myapp
2、伪目标
.Phony:all
伪目标不是文件,但是为了防止防止make不执行此命令,可以用.Phony指定
3、自动变量
自动变量都是以$开头,加上一些特殊的符号,表示特殊的变量
- $%、$<、$^、$?
/**helloworld.c**/ #include<stdio.h> int main() { foo("hello world\n"); }
/**foo.c**/ #include<stdio.h> int foo() { printf("hello world\n"); }
#Makefile helloworld:helloworld.c foo.c gcc -o $@ $^ all:helloworld
从上述例子中看出, $%表示目标、$^表示所有依赖
修改makefile
#Makefile helloworld:helloworld.c foo.c gcc -o $@ $< all:helloworld
可以看出$<表示第一个依赖
修改foo.c,修改Makefile:#Makefile helloworld:helloworld.c foo.c @echo $? gcc -o $@ $^ all:helloworld
/**foo.c**/ #include<stdio.h> int foo() { printf("hello world\n");//test }
可以看到$?表示更新了的依赖项
4、常见语法
- 条件语句
ifeq (a,b)
statement
endif
如果a和b相同,则执行statement
eg:#Makefile Version=1.0 ifeq ($(Version),1.0) NOTE=V1.0 endif helloworld:helloworld.c foo.c @echo $(NOTE) gcc -o $@ $^ all:helloworld
- 循环语句
$(foreach n ,list,cmd)
从list分别取出成员n,然后执行cmd。
eg:#Makefile helloworld:helloworld.c foo.c @echo $(foreach n, $^,$(n)) gcc -o $@ $^ all:helloworld
5、常用函数
- wildcard
$(wildcard pattern)
返回符合模式的文件
eg:#Makefile SRC := $(wildcard *.c) helloworld:$(SRC) @echo $^ gcc -o $@ $^ all:helloworld
- patsubst和subst
$(subst from,to,text)
$(patsubst from,to,text)
patsubst和subst都可以进行替换,不同的是patsubst可以将符合模式的进行替换,而subst只能进行明文替换
#Makefile SRC := $(wildcard *.c) OBJ := $(patsubst %.c,%.o,$(SRC)) helloworld:$(OBJ) @echo $^ gcc -o $@ $^ all:helloworld
- filter和filter-out
filter表示过滤符合pattern的项,filter-out表示过滤出符合pattern的项的剩余的项
eg:过滤如下文件列表的test.c#Makefile SRC := $(wildcard *.c) FILTER_SRC := $(filter-out test%,$(SRC)) OBJ := $(patsubst %.c,%.o,$(FILTER_SRC)) .Phony : all clean helloworld:$(OBJ) @echo $(SRC) @echo $^ gcc -o $@ $^ all:helloworld clean: rm helloworld helloworld.o foo.o -f
6、常用代码框架管理
1.代码文件替换
代码结构:project中的代码需要替换主线中的代码,在这个demo中,只需要编译主线中的helloworld.c,project分支中的foo.c和bar.c,不编译主线的foo.c和bar.c
/**helloworld.c**/
#include<stdio.h>
extern int foo();
extern int bar();
int main()
{
foo();
bar();
}
/**foo.c**/
#include<stdio.h>
int bar()
{
printf("hello bar\n");//test
}
/**foo.c**/
#include<stdio.h>
int bar()
{
printf("hello bar\n");//test
}
/**project/foo.c**/
#include<stdio.h>
int bar()
{
printf("project hello bar\n");//test
}
/**project/foo.c**/
#include<stdio.h>
int foo()
{
printf("project hello foo\n");//test
}
#Makefile
TARGET := helloworld
SRC := $(wildcard *.c)
PROJECT_SRC := $(wildcard ./project/*.c)
PROJECT_BASENAME := $(notdir $(PROJECT_SRC))
FILTER_SRC := $(filter-out %$(PROJECT_BASENAME),$(SRC))
ALL_SRC := $(FILTER_SRC)
ALL_SRC += $(PROJECT_SRC)
OBJ := $(patsubst %.c,%.o,$(ALL_SRC))
.Phony : all clean
helloworld:$(OBJ)
@echo $(SRC)
@echo $(PROJECT_SRC)
@echo $(PROJECT_BASENAME)
@echo $^
gcc -o $@ $^
all:helloworld
clean:
rm $(TARGET) $(OBJ) -f
使用如上makefile,关键在于两个函数,notdir函数取出路径文件中的文件名,filter-out过滤主线中包含此文件的主线文件,然后再加上分支代码,就组成了最后的代码机构。
2.对每个文件都进行一次编译
eg:如下面helloworld.c和helloworld1.c都需要编译成可执行文件,
/**helloworld.c**/
#include<stdio.h>
int main()
{
printf("helloworld\n");
}
/**helloworld1.c**/
#include<stdio.h>
int main()
{
printf("helloworld1\n");
}
#Makefile
SRC := $(wildcard *.c)
OBJ := $(patsubst %.c,%.o,$(SRC))
TARGET := $(patsubst %.o,%,$(OBJ))
.Phony : all clean
all:$(TARGET)
%:%.o
gcc -o $@ $^
clean:
rm $(TARGET) $(OBJ) -f
这里主要用到了通配符%做目标的做法,要使用此语法,一定需要依赖项中包含了匹配的成员。
当然,还有第二种做法
#Makefile
SRC := $(wildcard *.c)
OBJ := $(patsubst %.c,%.o,$(SRC))
TARGET := $(patsubst %.o,%,$(OBJ))
.Phony : all clean
all:$(TARGET)
$(TARGET):%:%.o
gcc -o $@ $^
clean:
rm $(TARGET) $(OBJ) -f
可以看到虽然结果一样,但是执行命令不一样,使用$(TARGET):%:%.o后,会先生成.o文件,而使用%:%.o则会直接生成可执行文件