• 006_Makefile Study(1)


    000_Makefile Study(1)

    一、编译指令学习

    1、预处理(头文件引入/宏展开/指令的处理)
    gcc -E *.c > *.txt 			# 将c文件预处理内容输入到文本文件中
    
    2、编译
    gcc -c *.c        			# 默认将*.c编译成*.o 
    
    3、链接
    gcc 入口文件.o 依赖文件.o -o 可执行程序名称  #生成可执行程序
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    1.1 示例——编译文件基础

    main.exe:main.o add.o jian.o
    	gcc main.o add.o jian.o -o main.exe
    main.o:main.c
    	gcc -c main.c -o main.o
    add.o:add.c
    	gcc -c add.c -o add.o
    jian.o:jian.c
    	gcc -c jian.c -o jian.o
    
    clean:
    	rm -f *.o main.exe
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    二、Makefile基本语法

    目标:依赖
    Tab键 命令
    
    目标:一般是指要编译的目标,也可以是一个动作
    依赖:指执行当前目标所要依赖的选项,包括其它目标,某个具体文件或库等
    一个目标可以有多个依赖
    
    命令:该目标下要执行的具体命令,可以没有,也可以有多条。
    	多条时,每条命令一行
    
    make 常用选项
    	make [-f file] [options] [target]
    	
    	Make 默认在当前目录中寻找GUNmakfile,makefile,Makefile的文件作为make的输入文件
    	-f 可以指定除上述文件名之外的文件作为输入文件
    	-v 显示版本号
    	-n 只输出命令,但并不执行,一般用来测试
    	-s 只执行命令,但不显示具体命令,此处可在命令中用@符抑制命令输出
    	-w 显示执行前后的路径
    	-C dir 指定makefile 所在的目录
    	
    	没有指定目标时,默认使用第一个目标
    	如果指定,则执行对应的命令
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    2.1 示例——单一目标

    a:
    @echo "hello makefile"  #加@表示抑制输出,否则会把‘echo "hello makefile"’这句代码也输出出来
    
    b:
    @echo "hello makefile b" #同一个makefile文件中,默认只会执行第一句
    
    • 1
    • 2
    • 3
    • 4
    • 5

    2.2 示例——目标有依赖项

    a:b #表示a依赖于b,那么b目标下面的命令就会先于a目标下面的命令执行
    @echo "hello makefile"  #加@表示抑制输出,否则会把‘echo "hello makefile"’这句代码也输出出来
    
    b:
    @echo "hello makefile b" #同一个makefile文件中,默认只会执行第一句
    
    • 1
    • 2
    • 3
    • 4
    • 5

    2.3 示例——单一目标多条指令

    a:
    	@echo "hello world a"
    	@ls ./
    	gcc -lstdc++ xxx.cpp  #gcc编译c++,执行这个makefile文件会生成a.out文件
    clean:
    	rm -rf *.out  #清除命令
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    三、makefile 中的变量

    3.1 系统变量

    $* 不包括扩展名的目标文件名称
    $+ 所有的依赖文件,以空格分隔
    $< 表示规则中的第一个条件,依次去匹配依赖
    $? 所有时间戳比目标文件晚的依赖文件,以空格分隔
    $@ 目标文件的完整名称
    $^ 所有不重复的依赖文件,以空格分隔
    $% 如果目标是归档成员,则该变量表示目标的归档成员名称
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    示例 ——使用通配符来解决问题
    main.exe:main.o add.o jian.o
    	gcc *.o -o $@
    %.o:%.c
    	gcc -c $< -o $@
    clean:
    	rm -f *.o main.exe
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    3.2 系统常量(实质就是一些宏定义)

    AS 汇编程序的名称 默认为as
    CC C编译器名称	默认为cc
    CPP C预编译器名称	默认为cc -E
    CXX C++编译器名称 默认为g++
    RM 	文件删除程序别名 默认rm -f
    
    • 1
    • 2
    • 3
    • 4
    • 5
    示例——利用系统常量来实现跨平台
    main.exe:main.o add.o jian.o
    	$(CC) *.o -o $@
    %.o:%.c
    	$(CC) -c $< -o $@
    clean:
    	$(RM) *.o main.exe
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    3.3 自定义变量

    定义:变量名=变量值
    使用:$(变量名)/${变量名}
    
    • 1
    • 2
    示例
    CC = gcc  # CC是变量名,gcc是变量值
    CFLAGS = -lm -Wall -g # 将一串命令定义为一个Flag
    
    #如何使用变量
    main: main.c
    	$(CC) $(CFLAGS) main.c -o main
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    3.4 多个入口函数

    #默认只执行能够生成可执行文件的第一条指令
    CC = gcc
    CFLAGS = -lm -Wall -g # 将一串命令定义为一个Flag
    all: test1 test2 #指定多个执行程序的名称
    
    • 1
    • 2
    • 3
    • 4

    四、gcc编译流程

    gcc -lstdc++ main.cpp  #直接从源代码到目标可执行文件了
    
    把过程拆分
    	预处理 gcc -E main.cpp > main.ii
    	编译   gcc -S main.ii [-o xxx.s] # 默认是main.s的汇编文件
    	汇编   gcc -c main.s #得到名为main.o(windows是.obj)的二进制文件
    	链接	 gcc -lstdc++ main.o #得到名为 a.out的可执行文件
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    五、Makefile中的伪目标和模式匹配

    5.1 伪目标

    .PHONY:目标名
    	声明目标为伪目标之后,makefile将不会判断目标是否存在或该目标是否需要更新
    %.o:%.cpp .o依赖于对应的.cpp
    wildcard   $(wildcard ./*.cpp) #获取当前目录下所有的.cpp文件
    patsubst   $(patsubst %.cpp, %.o, ./*.cpp) 将对应的cpp文件名替换成.o文件名
    
    • 1
    • 2
    • 3
    • 4
    • 5
    示例——伪目标使用场景

    使用·touch clean·生成clean文件,如果没有伪目标的声明,那么再执行make clean时将无法执行

    .PHONY:clean
    main.exe:main.o add.o jian.o
    	$(CC) *.o -o $@
    %.o:%.c
    	$(CC) -c $< -o $@
    clean:
    	$(RM) *.o main.exe
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    示例——模式匹配
    .PHONY:clean
    OBJ=$(patsubst %.c,%.o,$(wildcard ./*.c))
    TARGET=maiin
    
    $(TARGET):$(OBJ)
    	$(CC) *.o -o $@
    %.o:%.c
    	$(CC) -c $< -o $@
    clean:
    	$(RM) *.o main.exe
    show:
    	echo $(wildcard ./*.c)
    	echo ./*.c
    	echo $(patsubst %.c,%.o,./*.c)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    六、makefile编译动态链接库(.dll /.so 库文件)

    动态链接库:不会把代码编译到二进制文件中,而是运行时才去加载,所以只需要维护一个地址
    -fPIC 产生位置无关的代码
    -shared 共享
    -l(小L) 指定动态库
    -I 指定头文件目录,默认当前目录
    -L 手动指定库文件搜索目录,默认只链接共享目录
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    动态链接库: 好处时程序可以和库文件分离,可以分别发版,然后库文件可以被多处共享

    动态:运行时才去加载,动态加载

    链接:指库文件和二进制程序分离

    库:.dll/.so

    6.1 Linux 示例

    g++ -shared -fPIC animal.cpp -o libanimal.so #将animal.cpp编译成so库
    
    #testAnimal.cpp在命令行中的位置要放对,否则会出错误
    g++ testAnimal.cpp -lanimal -L./  -o testAnimal
    
    #指定动态库的寻找路径
    #error while loading shared libraries: xx.so: cannot open shared object file: No such file 错误的原因和解决办法
    # https://blog.csdn.net/my_angle2016/article/details/113524560
    export LD_LIBRARY_PATH=~/StudySpace/000_makefileStudy/002_demo:$LD_LIBRARY_PATH
    
    #执行文件
    ./testAnimal
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    testAnimal:libanimal.so
    	$(CXX) testAnimal.cpp -lanimal -L./ -o testAnimal
    
    #so库的名字就是原文件名前面加上lib
    libanimal.so:
    	$(CXX) -fPIC -shared animal.cpp -o libanimal.so
    
    clean:
    	$(RM) *.so testAnimal
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    七、makefile中编译静态库

    静态链接库:会把库中的代码编译到二进制文件中,当程序编译完成后,该库文件可以删除。动态链接库不行,动态链接库必须与程序同时部署,还要保证程序能加载得到库文件。与动态链接相比,静态库可以不用部署(已经被加载到程序里面了),而且运行时速度更快,但是会导致程序体积更大,并且库中的内容如果有更新,就需要重新编译生成程序。

    库:.lib/.a

    objdump -DC testAnimal > main.txt #反汇编
    
    • 1

    7.1 Linux示例

    g++ -c xxx.cpp -o xxx.o  #生成静态库所需要的.o文件
    ar -r libxxx.a xxx.o #生成静态库
    
    #指定静态库的查找路径
    export LS_LIBRARY_PATH=~/StudySpace/000_makefileStudy/002_demo:$LS_LIBRARY_PATH
    
    g++ testAnimal.cpp -lanimal -L./ -lxxx -L./ -o testAnimal #生成可执行文件
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    testAnimal:libanimal.so libdog.a
    	$(CXX) testAnimal.cpp -lanimal -L./ -ldog -L./ -o testAnimal
    
    libanimal.so:
    	$(CXX) -fPIC -shared animal.cpp -o libanimal.so
    
    libdog.a:
    	$(CXX) -c dog.cpp -o dog.o
    	ar -r libdog.a dog.o
    
    clean:
    	$(RM) *.a *.so testAnimal
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    八、makefile通用部分做公共头文件

    = 赋值操作。不管变量调用写在变量定义还是定义后,都是调用的变量最终值

    := 赋值操作。只受当前行及之前的代码影响,而不会受后面的赋值影响

    8.1 示例1

    #cpp文件
    TARGET=c
    
    include ../makefile
    
    • 1
    • 2
    • 3
    • 4
    #c文件
    # TARGET=z
    
    include ../makefile
    
    • 1
    • 2
    • 3
    • 4
    #公共头文件
    SOURCE=$(wildcard ./*.cpp ./*.c)
    OBJ=$(patsubst %.cpp,%.o,$(SOURCE)) #将对应的.cpp文件替换为.o文件
    OBJ:=$(patsubst %.c,%.o,$(OBJ))	#将对应的.cp文件替换为.o文件
    .PHONY:clean
    
    ifndef TARGET
    TARGET=test
    endif
    
    $(TARGET):$(OBJ)
    	$(CXX)  $^ -o $@
    
    clean:
    	rm -f *.o $(TARGET) 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
  • 相关阅读:
    Vue3.2+TypeScript管理模块记录1-登录模块
    小程序商品列表分页实现
    在script标签中引用tiptap
    我认为除了HelloWorld之外,Python的三大数据转换实例可以作为开始学习Python的入门语言。
    机械零件保养3d模拟演示打消客户购买顾虑
    手撕无头单链表
    企业全域风控管理的最优解
    电商类面试问题--01Elasticsearch与Mysql数据同步问题
    JavaScript模块化
    keras vscode没法补全问题
  • 原文地址:https://blog.csdn.net/Duckdan/article/details/128124374