简介

在Windows系统中,集成开发环境(IDE)综合了编辑器、编译器和调试器等等。但在Linux中,这些是分离的(当然,linux中也有IDE)。我们可以将Linux整体当做是一个集成开发环境,而makefile就能类比为VS的sln,一个项目文件,它将众多代码、依赖库组织起来,解决工程级别的编译问题。

在Linux中,若用Qt创建一个项目,Qt也是基于makefile组织工程的,它会帮你生成一个makefile。makefile其实就是一个项目配置属性,包含了编译属性(引入哪些头文件、引入哪些库、编译哪些文件)。

makefile文件

makefile文件的内容包含五分部内容

  1. 显示规则:直接明显的指出来,它要生成什么文件,依赖什么文件
  2. 隐式规则:若没有指明,则make会自动推导出规则
  3. 变量定义:在makefile中支持定义变量,使makefile配置性更强,可读性更强
  4. 文件引用:makefile可以拆成多个makefile,通过引用的方式建立连接
  5. 注释:使用#注释

makefile内要指明三个要素:

  1. 目标(target):最终要生成的东西,是要生成执行文件,还是.so
  2. 依赖:依赖的头文件、库(.o等)
  3. 指令:规定如何编译

例子:编译一个main.cpp的makefile,在Shell中执行make即可

  1. main:main.cpp
  2. #目标:依赖
  3. g++ main.cpp -o main -g
  4. #指令

make命令

如果提示make命令没有安装,则运行命令apt-get install make

make命令参数:

  1. 无参数:执行make命令,它会在当前目录下找makefile,并执行文件中的内部命令,将工程编译
  2. -f参数:执行make -f centosmake,指定了makefile文件为centosmake。此应用于一个工程需要多个makefile的场景(即在centos、ubuntu等系统上都要编译,则需要写多个makefile,此时就可以用-f来区分)

说明

  1. 如果文件夹下只有一个main.cpp,可以直接运行命令make main,可不用编写makefile,它默认执行g++ main.cpp -o main

    示例

    编写多文件包含动态链接库的makefile

    以版本迭代的方式展开知识

工程结构:

  • code
    • src
      • xlog
        • xlog.cpp
        • xlog.h
      • testxlog
        • testxlog.cpp 实例化了日志对象,引用到了xlog.h
        • makefile
        • run

【编写代码】

  1. // code/src/xlog/xlog.h
  2. class XLog{
  3. public: XLog();
  4. }
  5. // code/src/xlog/xlog.cpp
  6. #inlcude"xlog.h"
  7. #include<iostream>
  8. using namespace std;
  9. XLog::XLog(){
  10. cout << "Create Log" << endl;
  11. }
  12. // code/src/testxlog/testxlog.cpp
  13. #include<iostream>
  14. #include"xlog.h"
  15. using namespace std;
  16. XLog log;
  17. int main()
  18. {
  19. cout << "In TestXlog" << endl;
  20. return 0;
  21. }

【编写makefile】

  1. # 该文件路径:code/src/testxlog/makefile
  2. testxlog:testxlog.o libxlog.so
  3. #目标项:依赖项。如果依赖项没有编译,则会编译依赖项
  4. g++ testxlog.o -o testxlog -lxlog -L./
  5. #编译指令。在链接的时候不需要知道头文件的路径
  6. #-l表示链接xlog.so
  7. #-L指定动态库所在位置(windows会在当前路径下找动态库,但linux只会在环境路径找)
  8. testxlog.o:testxlog.cpp
  9. #编译testxlog的依赖项testxlog.o,它依赖于testxlog.cpp
  10. g++ testxlog.cpp -c -I../xlog
  11. #在编译的时候需要知道头文件的路径
  12. #-c表示只生成.o文件,不链接
  13. libxlog.so:../xlog/xlog.cpp ../xlog/xlog.h
  14. g++ ../xlog/xlog.cpp -o libxlog.so -shared -fPIC
  15. #-fPIC:动态链接库要加一个位置无关

【运行】
Linux与Windows不同,Linux只会去系统环境变量中找动态链接库,不会在当前目录下找动态链接库
针对此的解决方案:

  1. 在编译和运行两个阶段中,都指定动态链接库的位置
  2. 把动态链接库放到环境变量中
  3. 编写一个脚本,通过脚本编译
    1. # code/src/testxlog/run
    2. #!/bin/sh
    3. #表明此Shell为sh的语法。如果没有这一句,当默认Shell不是sh的时候会报错
    4. LD_LIBRARY_PATH=./
    5. #把此环境变量(运行时加载库的环境变量)设置为当前路径
    6. export LD_LIBRARY_PATH
    7. ./testxlog
    8. #在linux中如果要运行某个程序,需要指明当前路径,除非要拷贝到系统环境变量中

    优化:使用变量

    【优化一】将编译器抽出来,使用变量来代替
    1. CC=g++ #定义一个CC变量表示编译器
    2. testxlog:testxlog.o libxlog.so
    3. $(CC) testxlog.o -o testxlog -lxlog -L./
    4. testxlog.o:testxlog.cpp
    5. $(CC) testxlog.cpp -c -I../xlog
    6. libxlog.so:../xlog/xlog.cpp ../xlog/xlog.h
    7. $(CC) ../xlog/xlog.cpp -o libxlog.so -shared -fPIC
    运行前要先把之前生成的文件删除:
    1. # 清理刚才生成的文件
    2. rm *.o
    3. rm *.so
    4. rm testxlog
    5. # 重新编译
    6. make

【优化二】把头文件路径抽出来,用变量代替

  1. CC=g++ #定义一个CC变量表示编译器
  2. INCLUDE=-I../xlog
  3. testxlog:testxlog.o libxlog.so
  4. $(CC) testxlog.o -o testxlog -lxlog -L./
  5. testxlog.o:testxlog.cpp
  6. $(CC) testxlog.cpp -c $(INCLUDE)
  7. libxlog.so:../xlog/xlog.cpp ../xlog/xlog.h
  8. $(CC) ../xlog/xlog.cpp -o libxlog.so -shared -fPIC

【优化三】用一个变量CTAG,把一些配置项都加进来,统一放一起。到时候使用一个变量即可

  1. CC=g++
  2. #定义一个CC变量表示编译器
  3. INCLUDE=-I../xlog
  4. #头文件路径
  5. CTAG=-L./ $(INCLUDE) -g
  6. #配置项可加在一个变量中,统一放在一起
  7. #-L./ 动态库路径
  8. #include的路径
  9. #-g才可以调试
  10. testxlog:testxlog.o libxlog.so
  11. $(CC) testxlog.o -o testxlog -lxlog $(CTAG)
  12. testxlog.o:testxlog.cpp
  13. $(CC) testxlog.cpp -c $(CTAG)
  14. libxlog.so:../xlog/xlog.cpp ../xlog/xlog.h
  15. $(CC) ../xlog/xlog.cpp -o libxlog.so -shared -fPIC

优化:清理旧文件

【优化四】把清理旧文件的命令写在makefile文件中,达到每次编译都全部重新生成

  1. CC=g++
  2. INCLUDE=-I../xlog
  3. CTAG=-L./ $(INCLUDE) -g
  4. testxlog:testxlog.o libxlog.so
  5. $(CC) testxlog.o -o testxlog -lxlog $(CTAG)
  6. testxlog.o:testxlog.cpp
  7. $(CC) testxlog.cpp -c $(CTAG)
  8. libxlog.so:../xlog/xlog.cpp ../xlog/xlog.h
  9. $(CC) ../xlog/xlog.cpp -o libxlog.so -shared -fPIC
  10. clean:
  11. # 清理旧文件
  12. rm *.o -rf
  13. rm *.so -rf
  14. rm testxlog -rf

此时分别运行make clean(进入到clean的部分)、make两个命令,实现编译
image.png

优化:用自带变量简化代码

用系统自带的变量简化代码:

系统自带变量 说明
$@ 当前的目标
$+ 依赖项
  1. CC=g++
  2. INCLUDE=-I../xlog
  3. CTAG=-L./ $(INCLUDE) -g
  4. OUT=testxlog #OUT输出文件
  5. LIBS=-lxlog
  6. SOCC=$(CC) $+ -o $@ -shared -fPIC
  7. #编译SO文件,都可以用这个
  8. OCC=$(CC) $+ -c $(CTAG)
  9. #编译.o文件,都可以用这个
  10. #$+表示依赖项
  11. $(OUT):testxlog.o libxlog.so
  12. $(CC) testxlog.o -o $@ $(LIBS) $(CTAG)
  13. #$@表示当前的目标,在这里就是$(OUT),就是testxlog
  14. #如果这里用$+,它代表的是testxlog.o和libxlog.o,但是这里只需要testxlog.o,所以不能用$+
  15. testxlog.o:testxlog.cpp
  16. $(OCC)
  17. libxlog.so:../xlog/xlog.cpp ../xlog/xlog.h
  18. $(SOCC)
  19. clean:
  20. # 清理旧文件
  21. rm *.o -rf
  22. rm *.so -rf
  23. rm testxlog -rf

优化:安装和卸载

「背景」在编译成功后,需要运行./run,应用程序才能执行。有时候,我们希望用户直接打testxlog就能执行程序。此时,就要把命令testxlog安装到系统命令上来

  1. CC=g++
  2. INCLUDE=-I../xlog
  3. CTAG=-L./ $(INCLUDE) -g
  4. OUT=testxlog
  5. LIBS=-lxlog
  6. SOCC=$(CC) $+ -o $@ -shared -fPIC
  7. OCC=$(CC) $+ -c $(CTAG)
  8. $(OUT):testxlog.o libxlog.so
  9. $(CC) testxlog.o -o $@ $(LIBS) $(CTAG)
  10. testxlog.o:testxlog.cpp
  11. $(OCC)
  12. libxlog.so:../xlog/xlog.cpp ../xlog/xlog.h
  13. $(SOCC)
  14. install:$(out) #依赖项是$(out),要保证OUT生成了,才能运行以下命令
  15. cp *.so /usr/lib #把so放到系统的库目录下(cp即是拷贝的意思)
  16. cp $(OUT) /usr/bin
  17. uninstall: #卸载
  18. rm /usr/lib/libxlog.so -rf #将系统目录下的本文件删除
  19. rm /usr/bin/$(OUT)
  20. clean:
  21. rm *.o -rf
  22. rm *.so -rf
  23. rm testxlog -rf

编译后运行命令make install,之后运行testxlog就可以执行程序。卸载则可以用make uninstall
image.png