最近编译工程的时候又用到makefile了。
以前只写过一些很简单的makefile。 形如:
test.o : test.c test.h
gcc
之类。 在文件很少的情况下,可以这样用, 但是当文件很多的时候, 这样搞会把人折腾死的。
网上有不少介绍通用makefile的文章:
比如这篇:
说的还是很详细的。
其中最关键且最华丽丽的句子就是这句:
%.d: %.c
我把这些文章介绍的makefile拿到工程中实验了一下,确实好使, 但是产生了三个疑问:
1) 编译.o文件时,
2) 这些makefile都是针对源码只有 C文件的。 如果源码同时含有cpp文件该怎么办?
3) 上面几句晦涩的话到底是什么意思???
带着这几个问题, 做了一个很简单的测试程序:
一)
这个测试程序几个文件如下:(名字不用在意, 是之前测试osip留下的余孽)
test_osip.c:
#include <stdio.h>
#include "test_osip.h"
int main(){
test1();
test2();
test3();
test4();
}
test_osip1.c:
#include <stdio.h>
int test1(){
printf("test1\n");
}
test_osip2.c:
#include <stdio.h>
int test2(){
printf("test2\n");
}
test_osip3.cpp:
#include <stdio.h>
extern "C"{
int test3(){
printf("test3\n");
}
}
test_osip4.cpp:
#include <stdio.h>
extern "C"{
int test4(){
printf("test4\n");
}
}
test_osip.h
typedef struct{
int a;
int b;
}T_TEST;
Makefile:
CC=gcc
CFLAGS= -g -Wall
CPPFLAGS= -g
LDFLAGS= -lstdc++ -lpthread
SOURCES_CPP+= \
test_osip3.cpp \
test_osip4.cpp
SOURCES_C+= \
test_osip1.c \
test_osip2.c \
test_osip.c
OBJS_CPP:=$(patsubst %.cpp,%.o, $(SOURCES_CPP))
OBJS_C:= $(patsubst %.c,%.o, $(SOURCES_C))
OBJS:= $(OBJS_CPP) $(OBJS_C)
all: main
#下面这边都是获取依赖关系 ,属于约定俗成的写法
%.d: %.c
rm -f $@;
$(CC) -MM $< > $@.1111;
sed 's,/($*/)/.o[ :]*,/1.o $@ : ,g' < $@.1111
> $@;
@echo "$@ $< 77777"
%.d: %.cpp
rm -f $@;
$(CC) -MM $(CFLAGS) $< >
$@.1111
sed 's,/($*/)/.o[ :]*,/1.o $@ : ,g' < $@.1111
> $@
@echo "$@ $< 66666"
sinclude $(SOURCES_CPP:.cpp=.d)
sinclude $(SOURCES_C:.c=.d)
main:$(OBJS)
@echo $(SOURCES_CPP:.cpp=.d)
$(CC) $(OBJS) $(LDFLAGS) -o main
clean:
rm -f *.o main *.d
二)
终端输出:
hl@hl-VirtualBox:~/linphone/test_osip_linux$
make
rm -f test_osip.d;
gcc -MM test_osip.c > test_osip.d.1111;
sed 's,/(test_osip/)/.o[ :]*,/1.o test_osip.d : ,g'
< test_osip.d.1111 > test_osip.d;
test_osip.d test_osip.c 77777
rm -f test_osip2.d;
gcc -MM test_osip2.c > test_osip2.d.1111;
sed 's,/(test_osip2/)/.o[ :]*,/1.o test_osip2.d : ,g'
< test_osip2.d.1111 > test_osip2.d;
test_osip2.d test_osip2.c 77777
rm -f test_osip1.d;
gcc -MM test_osip1.c > test_osip1.d.1111;
sed 's,/(test_osip1/)/.o[ :]*,/1.o test_osip1.d : ,g'
< test_osip1.d.1111 > test_osip1.d;
test_osip1.d test_osip1.c 77777
rm -f test_osip4.d;
gcc -MM -g -Wall test_osip4.cpp >
test_osip4.d.1111
sed 's,/(test_osip4/)/.o[ :]*,/1.o test_osip4.d : ,g'
< test_osip4.d.1111 >
test_osip4.d
test_osip4.d test_osip4.cpp 66666
rm -f test_osip3.d;
gcc -MM -g -Wall test_osip3.cpp >
test_osip3.d.1111
sed 's,/(test_osip3/)/.o[ :]*,/1.o test_osip3.d : ,g'
< test_osip3.d.1111 >
test_osip3.d
test_osip3.d test_osip3.cpp 66666
g++
g++
gcc -g -Wall -g
test_osip1.c: In function 'test1':
test_osip1.c:5:1: warning: control reaches end of non-void
function [-Wreturn-type]
gcc -g -Wall -g
test_osip2.c: In function 'test2':
test_osip2.c:5:1: warning: control reaches end of non-void
function [-Wreturn-type]
gcc -g -Wall -g
test_osip.c: In function 'main':
test_osip.c:5:2: warning: implicit declaration of function
'test1' [-Wimplicit-function-declaration]
test_osip.c:6:2: warning: implicit declaration of function
'test2' [-Wimplicit-function-declaration]
test_osip.c:7:2: warning: implicit declaration of function
'test3' [-Wimplicit-function-declaration]
test_osip.c:8:2: warning: implicit declaration of function
'test4' [-Wimplicit-function-declaration]
test_osip.c:9:1: warning: control reaches end of non-void
function [-Wreturn-type]
test_osip3.d test_osip4.d
gcc test_osip3.o test_osip4.o test_osip1.o test_osip2.o
test_osip.o -lstdc++ -lpthread -o main
查看所有.d 文件和 .1111文件:
hl@hl-VirtualBox:~/linphone/test_osip_linux$ cat *.d
test_osip.o: test_osip.c test_osip.h
test_osip1.o: test_osip1.c
test_osip2.o: test_osip2.c
test_osip3.o: test_osip3.cpp
test_osip4.o: test_osip4.cpp
hl@hl-VirtualBox:~/linphone/test_osip_linux$ cat *.1111
test_osip.o: test_osip.c test_osip.h
test_osip1.o: test_osip1.c
test_osip2.o: test_osip2.c
test_osip3.o: test_osip3.cpp
test_osip4.o: test_osip4.cpp
hl@hl-VirtualBox:~/linphone/test_osip_linux$
三)
为什么会是这样?
我们还要从make的工作步骤说起:
这里推荐一下 《跟我一起写 Makefile》这篇博客,博客地址:
说的非常好
make的工作步骤
1、读入所有的Makefile。
2、读入被include的其它Makefile。
3、初始化文件中的变量。
4、推导隐晦规则,并分析所有规则。
5、为所有的目标文件创建依赖关系链。
6、根据依赖关系,决定哪些目标要重新生成。
7、执行生成命令。
对于我们这个makefile,其处理步骤是:
1) make看见sinclude
$(SOURCES_CPP:.cpp=.d)和
$(SOURCES_CPP:.cpp=.d)
这句的的意思是把SOURCES_CPP这个变量里面所有的.cpp替换成.d,也就是
test_osip3.d
test_osip4.d
2) make 发现有两个规则 %d : %c
rm -f test_osip.d;
gcc -MM test_osip.c
> test_osip.d.1111;
sed 's,/(test_osip/)/.o[ :]*,/1.o
test_osip.d : ,g' < test_osip.d.1111
> test_osip.d;
test_osip.d test_osip.c 77777
解释下这几句话的意思:
3).d文件生成完毕之后,就会被include进来, 这样一来,相当于makefile就多了几行这样的语句
test_osip.o: test_osip.c test_osip.h
test_osip1.o: test_osip1.c
test_osip2.o: test_osip2.c
test_osip3.o: test_osip3.cpp
test_osip4.o: test_osip4.cpp
如此一来, .o文件的依赖关系也出来了。
4) 接着make就开始去寻找依赖关系了,
不过紧接着问题就来了, 我们看到这些.o文件只有依赖文件而没有生成方式,make是怎么生成他们的?
这就是make的隐式规则了:
g++
g++
gcc -g
-Wall -g
可见对于cpp代码
,他会用CPPFLAGS
,而对于c代码,会用
5) 最后链接生成可执行文件
四
既然我们已经了解了makefile的脾气, 也就可以写出通用的makefile了:
CC=gcc
CFLAGS= -g -Wall
CPPFLAGS= -g
LDFLAGS= -lstdc++ -lpthread
SOURCES_CPP+= \
test_osip3.cpp \
test_osip4.cpp
SOURCES_C+= \
test_osip1.c \
test_osip2.c \
test_osip.c
OBJS_CPP:=$(patsubst %.cpp,%.o, $(SOURCES_CPP))
OBJS_C:= $(patsubst %.c,%.o, $(SOURCES_C))
OBJS:= $(OBJS_CPP) $(OBJS_C)
all: main
#下面这边都是获取依赖关系 ,属于约定俗成的写法
#注意rm 那一行最后不要加\
%.d: %.c
@rm -f $@;
@$(CC) -MM $< > $@.1111; \
sed 's,/($*/)/.o[ :]*,/1.o $@ : ,g' < $@.1111
> $@;
rm -f $@.1111
%.d: %.cpp
@rm -f $@;
@$(CC) -MM $< > $@.1111; \
sed 's,/($*/)/.o[ :]*,/1.o $@ : ,g' < $@.1111
> $@;
rm -f $@.1111
sinclude $(SOURCES_CPP:.cpp=.d)
sinclude $(SOURCES_C:.c=.d)
main:$(OBJS)
$(CC) $(OBJS) $(LDFLAGS) -o main
clean:
rm -f *.o main *.d
|
|