您现在的位置是:首页 >技术交流 >makefile的基本使用网站首页技术交流
makefile的基本使用
代码示例
有 main.cpp 如下:
#include <iostream>
#include "add.h"
#include "sub.h"
int main(int argc, char *argv[])
{
int i = 10, j = 20;
std::cout << "i + j = " << add(i, j) << std::endl;
std::cout << "i - j = " << sub(i, j) << std::endl;
return 0;
}
sub.h 如下:
#ifndef SUB_H
#define SUB_H
int sub(int i1, int i2);
#endif
sub.cpp 如下:
#include "sub.h"
int sub(int i1, int i2)
{
return i1 - i2;
}
add.h 如下:
#ifndef ADD_H
#define ADD_H
int add(int i1, int i2);
#endif
add.cpp 如下:
#include "add.h"
int add(int i1, int i2)
{
return i1 + i2;
}
编译以上文件并运行时,打印如下:
feng@ubuntu:~/PersonalTest/makefileTest$ g++ main.cpp add.cpp sub.cpp -o main
feng@ubuntu:~/PersonalTest/makefileTest$ ./main
i + j = 30
i - j = -10
makefile 的工作原理
任何版本的 shell 都包含 make 命令,当我们执行 make 命令时,make 就会执行 makefile 定义的操作。
makefile 的编写
基础规则
target : dependence
command
注意:
- command 行要使用 tab 开头;
- 如果发现 dependence 不存在时,指令无法执行,会向下查找指令生成 dependence。
示例:
target1:
echo "target1"
target2:
echo "target2"
运行后打印如下:
feng@ubuntu:~/PersonalTest/makefileTest$ make
echo "target1"
target1
feng@ubuntu:~/PersonalTest/makefileTest$ make target2
echo "target2"
target2
为开篇的代码示例编写一个 makefile :
main:./main.cpp ./add.cpp ./sub.cpp
g++ ./main.cpp ./add.cpp ./sub.cpp -o main
clean:
rm main ./main.o ./add.o ./sub.o
注意:加上 clean 是为了通过 makefile 实现清除目的。
运行后打印如下:
feng@ubuntu:~/PersonalTest/makefileTest$ make
g++ ./main.cpp ./add.cpp ./sub.cpp -o main
feng@ubuntu:~/PersonalTest/makefileTest$ ./main
i + j = 30
i - j = -10
feng@ubuntu:~/PersonalTest/makefileTest$ make clean
rm main
但是这样的 makefile 也存在一个问题,如果当前目录下存在一个名为 clean
的文件,此时执行 make clean
将会出现:
feng@ubuntu:~/PersonalTest/makefileTest$ make clean
make: 'clean' is up to date.
makefile 认为 clean 已存在,所以无需再做更新。
因此,需要在 makefile 中利用 .PHONY
将 clean 声明为目标,而不是文件。如下:
main:./main.cpp ./add.cpp ./sub.cpp
g++ ./main.cpp ./add.cpp ./sub.cpp -o main
.PHONY:clean
clean:
rm main ./main.o ./add.o ./sub.o
当 dependence 不存在时
将 makefile 修改为如下:
main:./main.o ./add.o ./sub.o
g++ ./main.o ./add.o ./sub.o -o main
.PHONY:clean
clean:
rm main
执行后打印如下:
feng@ubuntu:~/PersonalTest/makefileTest$ make
g++ -c -o main.o main.cpp
g++ -c -o add.o add.cpp
g++ -c -o sub.o sub.cpp
g++ ./main.o ./add.o ./sub.o -o main
feng@ubuntu:~/PersonalTest/makefileTest$ ./main
i + j = 30
i - j = -10
可以发现,makefile 中的 .0 文件并不存在,因此它会往下寻找,而下面也没有 .o 文件,因此会自动进行编译将 .cpp 编译为 .o 文件。(这里只是针对 .cpp --> .o,如果是其他依赖库,下文中又没有指出依赖库的编译方式等,势必会报错)
在 makefile 中使用变量
使用变量可以将上述 makefile 简化为如下:
target=main
obj=./main.o ./add.o ./sub.o
cc=g++
$(target):$(obj)
$(cc) $(obj) -o $(target)
.PHONY:clean
clean:
rm $(target) $(obj)
模式匹配
- %代表每一个,*代表所有。
- @ 表示目标, @ 表示目标, @表示目标,<表示第一个依赖,$^表示全部的依赖
makefile 简化如下:
target=main
obj=./main.o ./add.o ./sub.o
cc=g++
$(target):$(obj)
$(cc) $^ -o $@
.PHONY:clean
clean:
rm $(target) $(obj)
函数使用
- wildcard函数:
$(wildcard ./*.cpp)
, 获取当前目录下所有的cpp文件 - patsubst函数:
$(patsubst %.cpp,%.o,./*.cpp)
,将对应的cpp文件名全部替换为.o文件名(注意逗号格式)
makefile 简化如下:
target=main
src=$(wildcard ./*.cpp)
obj=$(patsubst %.cpp,%.o,$(src))
cc=g++
$(target):$(obj)
$(cc) $^ -o $@
.PHONY:clean
clean:
rm $(target) $(obj)
在一个 makefile 中引入其他 makefile
在当前 main.cpp 目录下,创建一个 libSrc、lib 目录,其中 libSrc 下包含 multi.h 和 multi.cpp 文件。
multi.h 内容如下:
#ifndef MULTI_H
#define MULTI_H
int multi(int i1, int i2);
#endif
multi.cpp 内容如下:
#include "multi.h"
int multi(int i1, int i2)
{
return i1 * i2;
}
编写一个 makefile,将 multi 编译为动态库,并移动到 lib 目录下,makefile 内容如下:
target=libmulti.so
src=$(wildcard *.cpp)
obj=$(patsubst %.cpp,%.o,$(src))
cc=g++
lib_path=../lib
$(target):$(obj)
$(cc) -shared $^ -o $@
mv $(target) $(lib_path)
%.o:$(src)
$(cc) -fPIC -c $^
.PHONY:clean
clean:
rm $(obj) $(lib_path)/$(target)
注意:
- .o -> 目标文件,由于这里要编译为动态库,因此加上了
-shared
; - .cpp -> .o,需要加上
-fPIC -c
的指令,使其可以编译为动态库;
main.cpp 内容修改为如下:
#include <iostream>
#include "add.h"
#include "sub.h"
#include "libSrc/multi.h"
int main(int argc, char *argv[])
{
int i = 10, j = 20;
std::cout << "i + j = " << add(i, j) << std::endl;
std::cout << "i - j = " << sub(i, j) << std::endl;
std::cout << "i * j = " << multi(i, j) << std::endl;
return 0;
}
此时,为了链接 multi.so 进行编译,main.cpp 目录下的 makefile 也应该修改为如下:
target=libmulti.so
src=$(wildcard *.cpp)
obj=$(patsubst %.cpp,%.o,$(src))
cc=g++
lib_path=../lib
$(target):$(obj)
$(cc) -shared $^ -o $@
mv $(target) $(lib_path)
%.o:$(src)
$(cc) -fPIC -c $^
.PHONY:clean
clean:
rm $(obj) $(lib_path)/$(target)
编译运行后打印如下:
feng@ubuntu:~/PersonalTest/makefileTest$ make
cd ./libSrc && make && cd ../
make[1]: Entering directory '/home/feng/PersonalTest/makefileTest/libSrc'
g++ -fPIC -c multi.cpp
g++ -shared multi.o -o libmulti.so
mv libmulti.so ../lib
make[1]: Leaving directory '/home/feng/PersonalTest/makefileTest/libSrc'
g++ sub.o main.o add.o -L./lib -lmulti -o main
feng@ubuntu:~/PersonalTest/makefileTest$ export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:./lib
feng@ubuntu:~/PersonalTest/makefileTest$ ./main
i + j = 30
i - j = -10
i * j = 200
feng@ubuntu:~/PersonalTest/makefileTest$ make clean
cd ./libSrc && make clean && cd ../
make[1]: Entering directory '/home/feng/PersonalTest/makefileTest/libSrc'
rm multi.o ../lib/libmulti.so
make[1]: Leaving directory '/home/feng/PersonalTest/makefileTest/libSrc'
rm main ./sub.o ./main.o ./add.o
注意:export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:./lib
这是为了临时添加动态库链接路径,使 main 可以运行起来。
如果在一个目录中,有一个同名的静态库和动态库。优先调用动态库。所以注意,不要让静态库和动态库同名。