简介
在Windows系统中,集成开发环境(IDE)综合了编辑器、编译器和调试器等等。但在Linux中,这些是分离的(当然,linux中也有IDE)。我们可以将Linux整体当做是一个集成开发环境,而makefile就能类比为VS的sln,一个项目文件,它将众多代码、依赖库组织起来,解决工程级别的编译问题。
在Linux中,若用Qt创建一个项目,Qt也是基于makefile组织工程的,它会帮你生成一个makefile。makefile其实就是一个项目配置属性,包含了编译属性(引入哪些头文件、引入哪些库、编译哪些文件)。
makefile文件
makefile文件的内容包含五分部内容
- 显示规则:直接明显的指出来,它要生成什么文件,依赖什么文件
- 隐式规则:若没有指明,则make会自动推导出规则
- 变量定义:在makefile中支持定义变量,使makefile配置性更强,可读性更强
- 文件引用:makefile可以拆成多个makefile,通过引用的方式建立连接
- 注释:使用
#
注释
makefile内要指明三个要素:
- 目标(target):最终要生成的东西,是要生成执行文件,还是.so
- 依赖:依赖的头文件、库(.o等)
- 指令:规定如何编译
例子:编译一个main.cpp的makefile,在Shell中执行make
即可
main:main.cpp
#目标:依赖
g++ main.cpp -o main -g
#指令
make命令
如果提示make
命令没有安装,则运行命令apt-get install make
。
make命令参数:
- 无参数:执行
make
命令,它会在当前目录下找makefile
,并执行文件中的内部命令,将工程编译 -f
参数:执行make -f centosmake
,指定了makefile文件为centosmake
。此应用于一个工程需要多个makefile的场景(即在centos、ubuntu等系统上都要编译,则需要写多个makefile,此时就可以用-f来区分)
说明
- 如果文件夹下只有一个main.cpp,可以直接运行命令
make main
,可不用编写makefile,它默认执行g++ main.cpp -o main
示例
编写多文件包含动态链接库的makefile
以版本迭代的方式展开知识
工程结构:
code
src
xlog
xlog.cpp
xlog.h
testxlog
testxlog.cpp
实例化了日志对象,引用到了xlog.hmakefile
run
【编写代码】
// code/src/xlog/xlog.h
class XLog{
public: XLog();
}
// code/src/xlog/xlog.cpp
#inlcude"xlog.h"
#include<iostream>
using namespace std;
XLog::XLog(){
cout << "Create Log" << endl;
}
// code/src/testxlog/testxlog.cpp
#include<iostream>
#include"xlog.h"
using namespace std;
XLog log;
int main()
{
cout << "In TestXlog" << endl;
return 0;
}
【编写makefile】
# 该文件路径:code/src/testxlog/makefile
testxlog:testxlog.o libxlog.so
#目标项:依赖项。如果依赖项没有编译,则会编译依赖项
g++ testxlog.o -o testxlog -lxlog -L./
#编译指令。在链接的时候不需要知道头文件的路径
#-l表示链接xlog.so
#-L指定动态库所在位置(windows会在当前路径下找动态库,但linux只会在环境路径找)
testxlog.o:testxlog.cpp
#编译testxlog的依赖项testxlog.o,它依赖于testxlog.cpp
g++ testxlog.cpp -c -I../xlog
#在编译的时候需要知道头文件的路径
#-c表示只生成.o文件,不链接
libxlog.so:../xlog/xlog.cpp ../xlog/xlog.h
g++ ../xlog/xlog.cpp -o libxlog.so -shared -fPIC
#-fPIC:动态链接库要加一个位置无关
【运行】
Linux与Windows不同,Linux只会去系统环境变量中找动态链接库,不会在当前目录下找动态链接库
针对此的解决方案:
- 在编译和运行两个阶段中,都指定动态链接库的位置
- 把动态链接库放到环境变量中
- 编写一个脚本,通过脚本编译
# code/src/testxlog/run
#!/bin/sh
#表明此Shell为sh的语法。如果没有这一句,当默认Shell不是sh的时候会报错
LD_LIBRARY_PATH=./
#把此环境变量(运行时加载库的环境变量)设置为当前路径
export LD_LIBRARY_PATH
./testxlog
#在linux中如果要运行某个程序,需要指明当前路径,除非要拷贝到系统环境变量中
优化:使用变量
【优化一】将编译器抽出来,使用变量来代替
运行前要先把之前生成的文件删除:CC=g++ #定义一个CC变量表示编译器
testxlog:testxlog.o libxlog.so
$(CC) testxlog.o -o testxlog -lxlog -L./
testxlog.o:testxlog.cpp
$(CC) testxlog.cpp -c -I../xlog
libxlog.so:../xlog/xlog.cpp ../xlog/xlog.h
$(CC) ../xlog/xlog.cpp -o libxlog.so -shared -fPIC
# 清理刚才生成的文件
rm *.o
rm *.so
rm testxlog
# 重新编译
make
【优化二】把头文件路径抽出来,用变量代替
CC=g++ #定义一个CC变量表示编译器
INCLUDE=-I../xlog
testxlog:testxlog.o libxlog.so
$(CC) testxlog.o -o testxlog -lxlog -L./
testxlog.o:testxlog.cpp
$(CC) testxlog.cpp -c $(INCLUDE)
libxlog.so:../xlog/xlog.cpp ../xlog/xlog.h
$(CC) ../xlog/xlog.cpp -o libxlog.so -shared -fPIC
【优化三】用一个变量CTAG,把一些配置项都加进来,统一放一起。到时候使用一个变量即可
CC=g++
#定义一个CC变量表示编译器
INCLUDE=-I../xlog
#头文件路径
CTAG=-L./ $(INCLUDE) -g
#配置项可加在一个变量中,统一放在一起
#-L./ 动态库路径
#include的路径
#-g才可以调试
testxlog:testxlog.o libxlog.so
$(CC) testxlog.o -o testxlog -lxlog $(CTAG)
testxlog.o:testxlog.cpp
$(CC) testxlog.cpp -c $(CTAG)
libxlog.so:../xlog/xlog.cpp ../xlog/xlog.h
$(CC) ../xlog/xlog.cpp -o libxlog.so -shared -fPIC
优化:清理旧文件
【优化四】把清理旧文件的命令写在makefile文件中,达到每次编译都全部重新生成
CC=g++
INCLUDE=-I../xlog
CTAG=-L./ $(INCLUDE) -g
testxlog:testxlog.o libxlog.so
$(CC) testxlog.o -o testxlog -lxlog $(CTAG)
testxlog.o:testxlog.cpp
$(CC) testxlog.cpp -c $(CTAG)
libxlog.so:../xlog/xlog.cpp ../xlog/xlog.h
$(CC) ../xlog/xlog.cpp -o libxlog.so -shared -fPIC
clean:
# 清理旧文件
rm *.o -rf
rm *.so -rf
rm testxlog -rf
此时分别运行make clean
(进入到clean的部分)、make
两个命令,实现编译
优化:用自带变量简化代码
用系统自带的变量简化代码:
系统自带变量 | 说明 |
---|---|
$@ |
当前的目标 |
$+ |
依赖项 |
CC=g++
INCLUDE=-I../xlog
CTAG=-L./ $(INCLUDE) -g
OUT=testxlog #OUT输出文件
LIBS=-lxlog
SOCC=$(CC) $+ -o $@ -shared -fPIC
#编译SO文件,都可以用这个
OCC=$(CC) $+ -c $(CTAG)
#编译.o文件,都可以用这个
#$+表示依赖项
$(OUT):testxlog.o libxlog.so
$(CC) testxlog.o -o $@ $(LIBS) $(CTAG)
#$@表示当前的目标,在这里就是$(OUT),就是testxlog
#如果这里用$+,它代表的是testxlog.o和libxlog.o,但是这里只需要testxlog.o,所以不能用$+
testxlog.o:testxlog.cpp
$(OCC)
libxlog.so:../xlog/xlog.cpp ../xlog/xlog.h
$(SOCC)
clean:
# 清理旧文件
rm *.o -rf
rm *.so -rf
rm testxlog -rf
优化:安装和卸载
「背景」在编译成功后,需要运行./run
,应用程序才能执行。有时候,我们希望用户直接打testxlog
就能执行程序。此时,就要把命令testxlog
安装到系统命令上来
CC=g++
INCLUDE=-I../xlog
CTAG=-L./ $(INCLUDE) -g
OUT=testxlog
LIBS=-lxlog
SOCC=$(CC) $+ -o $@ -shared -fPIC
OCC=$(CC) $+ -c $(CTAG)
$(OUT):testxlog.o libxlog.so
$(CC) testxlog.o -o $@ $(LIBS) $(CTAG)
testxlog.o:testxlog.cpp
$(OCC)
libxlog.so:../xlog/xlog.cpp ../xlog/xlog.h
$(SOCC)
install:$(out) #依赖项是$(out),要保证OUT生成了,才能运行以下命令
cp *.so /usr/lib #把so放到系统的库目录下(cp即是拷贝的意思)
cp $(OUT) /usr/bin
uninstall: #卸载
rm /usr/lib/libxlog.so -rf #将系统目录下的本文件删除
rm /usr/bin/$(OUT)
clean:
rm *.o -rf
rm *.so -rf
rm testxlog -rf
编译后运行命令make install
,之后运行testxlog
就可以执行程序。卸载则可以用make uninstall