006_Makefile Study(1)

本文详细介绍了GCC的编译指令,包括预处理、编译和链接步骤,并通过示例展示了Makefile的基本语法,如目标、依赖和命令的定义。此外,还讲解了Makefile中的变量使用,包括系统变量和自定义变量。文章进一步探讨了动态和静态链接库的创建,以及Makefile中伪目标和模式匹配的应用,强调了动态和静态链接库的差异。最后,提到了如何在Makefile中处理公共头文件。
摘要由CSDN通过智能技术生成

000_Makefile Study(1)

一、编译指令学习

1、预处理(头文件引入/宏展开/指令的处理)
gcc -E *.c > *.txt 			# 将c文件预处理内容输入到文本文件中

2、编译
gcc -c *.c        			# 默认将*.c编译成*.o 

3、链接
gcc 入口文件.o 依赖文件.o -o 可执行程序名称  #生成可执行程序

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

二、Makefile基本语法

目标:依赖
Tab键 命令

目标:一般是指要编译的目标,也可以是一个动作
依赖:指执行当前目标所要依赖的选项,包括其它目标,某个具体文件或库等
一个目标可以有多个依赖

命令:该目标下要执行的具体命令,可以没有,也可以有多条。
	多条时,每条命令一行

make 常用选项
	make [-f file] [options] [target]
	
	Make 默认在当前目录中寻找GUNmakfile,makefile,Makefile的文件作为make的输入文件
	-f 可以指定除上述文件名之外的文件作为输入文件
	-v 显示版本号
	-n 只输出命令,但并不执行,一般用来测试
	-s 只执行命令,但不显示具体命令,此处可在命令中用@符抑制命令输出
	-w 显示执行前后的路径
	-C dir 指定makefile 所在的目录
	
	没有指定目标时,默认使用第一个目标
	如果指定,则执行对应的命令

2.1 示例——单一目标

a:
@echo "hello makefile"  #加@表示抑制输出,否则会把‘echo "hello makefile"’这句代码也输出出来

b:
@echo "hello makefile b" #同一个makefile文件中,默认只会执行第一句

2.2 示例——目标有依赖项

a:b #表示a依赖于b,那么b目标下面的命令就会先于a目标下面的命令执行
@echo "hello makefile"  #加@表示抑制输出,否则会把‘echo "hello makefile"’这句代码也输出出来

b:
@echo "hello makefile b" #同一个makefile文件中,默认只会执行第一句

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

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

三、makefile 中的变量

3.1 系统变量

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

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

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

3.3 自定义变量

定义:变量名=变量值
使用:$(变量名)/${变量名}
示例
CC = gcc  # CC是变量名,gcc是变量值
CFLAGS = -lm -Wall -g # 将一串命令定义为一个Flag

#如何使用变量
main: main.c
	$(CC) $(CFLAGS) main.c -o main

3.4 多个入口函数

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

四、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的可执行文件

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

5.1 伪目标

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

使用·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
示例——模式匹配
.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)

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

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

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

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

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

库:.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
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

七、makefile中编译静态库

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

库:.lib/.a

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

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 #生成可执行文件
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

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

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

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

8.1 示例1

#cpp文件
TARGET=c

include ../makefile
#c文件
# TARGET=z

include ../makefile
#公共头文件
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) 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值