• makefile的基本使用


    代码示例

    有 main.cpp 如下:

    #include 
    #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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    sub.h 如下:

    #ifndef SUB_H
    #define SUB_H
    
    int sub(int i1, int i2);
    
    #endif
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    sub.cpp 如下:

    #include "sub.h"
    
    int sub(int i1, int i2)
    {
        return i1 - i2;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    add.h 如下:

    #ifndef ADD_H
    #define ADD_H
    
    int add(int i1, int i2);
    
    #endif
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    add.cpp 如下:

    #include "add.h"
    
    int add(int i1, int i2)
    {
        return i1 + i2;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    编译以上文件并运行时,打印如下:

    feng@ubuntu:~/PersonalTest/makefileTest$ g++ main.cpp add.cpp sub.cpp -o main
    feng@ubuntu:~/PersonalTest/makefileTest$ ./main 
    i + j = 30
    i - j = -10
    
    • 1
    • 2
    • 3
    • 4

    makefile 的工作原理

    任何版本的 shell 都包含 make 命令,当我们执行 make 命令时,make 就会执行 makefile 定义的操作。

    makefile 的编写

    基础规则

    target : dependence
        command
    
    • 1
    • 2

    注意

    1. command 行要使用 tab 开头
    2. 如果发现 dependence 不存在时,指令无法执行,会向下查找指令生成 dependence。

    示例:

    target1:
    	echo "target1"
    
    target2:
    	echo "target2"
    
    • 1
    • 2
    • 3
    • 4
    • 5

    运行后打印如下:

    feng@ubuntu:~/PersonalTest/makefileTest$ make
    echo "target1"
    target1
    feng@ubuntu:~/PersonalTest/makefileTest$ make target2
    echo "target2"
    target2
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    为开篇的代码示例编写一个 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
    
    • 1
    • 2
    • 3
    • 4
    • 5

    注意:加上 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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    但是这样的 makefile 也存在一个问题,如果当前目录下存在一个名为 clean 的文件,此时执行 make clean 将会出现:

    feng@ubuntu:~/PersonalTest/makefileTest$ make clean 
    make: 'clean' is up to date.
    
    • 1
    • 2

    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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    当 dependence 不存在时

    将 makefile 修改为如下:

    main:./main.o ./add.o ./sub.o
    	g++ ./main.o ./add.o ./sub.o -o main
    
    .PHONY:clean
    clean:
    	rm main
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    执行后打印如下:

    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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    可以发现,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)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    模式匹配
    1. %代表每一个,*代表所有。
    2. @ 表示目标, @ 表示目标, @表示目标,<表示第一个依赖,$^表示全部的依赖

    makefile 简化如下:

    target=main
    obj=./main.o ./add.o ./sub.o
    cc=g++
    
    $(target):$(obj)
    	$(cc) $^ -o $@
    
    .PHONY:clean
    clean:
    	rm $(target) $(obj)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    函数使用
    1. wildcard函数:$(wildcard ./*.cpp), 获取当前目录下所有的cpp文件
    2. 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)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    在一个 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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    multi.cpp 内容如下:

    #include "multi.h"
    
    int multi(int i1, int i2)
    {
        return i1 * i2;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    编写一个 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)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    注意:

    1. .o -> 目标文件,由于这里要编译为动态库,因此加上了 -shared
    2. .cpp -> .o,需要加上 -fPIC -c 的指令,使其可以编译为动态库;

    main.cpp 内容修改为如下:

    #include 
    #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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    此时,为了链接 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)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    编译运行后打印如下:

    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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    注意export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:./lib 这是为了临时添加动态库链接路径,使 main 可以运行起来。

    如果在一个目录中,有一个同名的静态库和动态库。优先调用动态库。所以注意,不要让静态库和动态库同名。

  • 相关阅读:
    Hudi 数据湖的插入,更新,查询,分析操作示例
    C++sqrt函数题目
    数字化时代,企业为什么要做数字化转型?
    vite+vue3+ts项目搭建之集成Layout组件搭建、全局自动注册基础组件、缓存页面
    Nacos的动态配置源码解析
    【PyTorch】nn.Conv2d函数详解
    GreenPlum DB向GBase迁移_DATE类型
    Sentinel学习圣经:从入门到精通 Sentinel,最全详解 (40+图文全面总结)
    计算机视觉——使用OpenCV GrabCut算法从图像中移除背景
    【网页前端】CSS样式表进阶之图像的灵活使用与拓展知识
  • 原文地址:https://blog.csdn.net/ZefinNg/article/details/130904037