简介
在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
以版本迭代的方式展开知识
工程结构:
codesrcxlogxlog.cppxlog.h
testxlogtestxlog.cpp实例化了日志对象,引用到了xlog.hmakefilerun
【编写代码】
// code/src/xlog/xlog.hclass 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/makefiletestxlog: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.cppg++ testxlog.cpp -c -I../xlog#在编译的时候需要知道头文件的路径#-c表示只生成.o文件,不链接libxlog.so:../xlog/xlog.cpp ../xlog/xlog.hg++ ../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../xloglibxlog.so:../xlog/xlog.cpp ../xlog/xlog.h$(CC) ../xlog/xlog.cpp -o libxlog.so -shared -fPIC
# 清理刚才生成的文件rm *.orm *.sorm testxlog# 重新编译make
【优化二】把头文件路径抽出来,用变量代替
CC=g++ #定义一个CC变量表示编译器INCLUDE=-I../xlogtestxlog: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../xlogCTAG=-L./ $(INCLUDE) -gtestxlog: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 -fPICclean:# 清理旧文件rm *.o -rfrm *.so -rfrm testxlog -rf
此时分别运行make clean(进入到clean的部分)、make两个命令,实现编译
优化:用自带变量简化代码
用系统自带的变量简化代码:
| 系统自带变量 | 说明 |
|---|---|
$@ |
当前的目标 |
$+ |
依赖项 |
CC=g++INCLUDE=-I../xlogCTAG=-L./ $(INCLUDE) -gOUT=testxlog #OUT输出文件LIBS=-lxlogSOCC=$(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 -rfrm *.so -rfrm testxlog -rf
优化:安装和卸载
「背景」在编译成功后,需要运行./run,应用程序才能执行。有时候,我们希望用户直接打testxlog就能执行程序。此时,就要把命令testxlog安装到系统命令上来
CC=g++INCLUDE=-I../xlogCTAG=-L./ $(INCLUDE) -gOUT=testxlogLIBS=-lxlogSOCC=$(CC) $+ -o $@ -shared -fPICOCC=$(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/binuninstall: #卸载rm /usr/lib/libxlog.so -rf #将系统目录下的本文件删除rm /usr/bin/$(OUT)clean:rm *.o -rfrm *.so -rfrm testxlog -rf
编译后运行命令make install,之后运行testxlog就可以执行程序。卸载则可以用make uninstall
