GCC编译器
编译流程
GCC在编译一份工程时分为 预处理、编译、汇编、链接 四步,当然日常中常用编译统称这四步,对应使用的GCC指令如下所示:
常用编译选项
- -E 只进行预处理步骤,输出预处理后的源码到标准输出,可以通过 > 重定向到文件,使用 “-E -dM” 的选项组合可以打印出文件中用到的所有宏定义以及它们所表示的值
- -c 执行预处理、编译、汇编但是不进行链接
- -o 指定输出文件
- -I (大写的i)指定要包含的头文件目录
- -L 指定链接时进行搜索的库文件目录这个目录会在添加-l(小写的L)选项时生效
- -l (小写的L)指定要链接的库文件,会在默认目录以及-L选项指定的目录中搜索
怎么编译多个文件
- 可以通过gcc -c 分别编译出每个文件的.o文件,然后gcc传入多个.o文件进行链接(makfile中常用)。
- 可以直接传入多个源文件,gcc会自动进行编译链接(不用makefile编译小项目时可以用)。
制作使用动态链接库
-
制作
gcc -c -o main.o main.c #先生成主程序的.o文件 gcc -c -o sub.o sub.c #要生成动态链接库的.o文件 gcc -shared -o libsub.so sub.o #生成动态链接库,这里可以使用多个.o文件生成一个库 # 使用-l来传入动态链接库,动态链接库的文件名为lib<libraryname>.so,传入时只需要传入<libraryname>这一部分,比如希望链接libsub.so文件,则传入-lsub # 使用-L dir来传入搜寻库文件时指定要搜索的目录,比如希望搜索/home/usr/则传入 -L /home/usr gcc -o test main.o -lsub -L <so文件所在目录>
-
运行
将动态链接库文件放置系统的/lib目录或/usr/lib目录,这两个是linux默认的库文件搜索目录,也可以放在某个目录下,然后执行如下指令:export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/myso #将/myso目录添加到系统链接库搜寻目录,注=两边不能有空格
然后就可以顺序执行引用了动态链接库的可执行文件了
制作使用静态链接库
-
制作
gcc -c -o main.o main.c #先生成主程序的.o文件(和动态库步骤一致) gcc -c -o sub.o sub.c #要生成静态链接库的.o文件 ar crs libsub.a sub.o #生成静态链接库,这里可以使用多个.o文件生成一个库 # 如果静态链接库不在当前目录下,需要指定他的绝对路径或相对路径,比如假设库在当前目录的mylib目录下 gcc -o test main.o ./mylib/libsub.a # 当然也可以跟链接动态库一样操作 # 使用-l来传入静态链接库,动态链接库的文件名为lib<libraryname>.a,传入时只需要传入<libraryname>这一部分,比如希望链接libsub.a文件,则传入-lsub # 使用-L dir来传入搜寻库文件时指定要搜索的目录,比如希望搜索./mylib则传入 -L ./mylib gcc -o test main.o -lsub -L ./mylib
-
使用
和普通可执行文件一样使用,静态链接库会直接嵌入可执行文件,当然这也导致了程序体积增大
其他常用选项
# -Wp,-MD这个选项到底意味着什么?不明白
gcc -Wp,-MD,abc.dep -c -o main.o main.c # 生成依赖文件abc.dep,makefile中会用到
Makefile
简单介绍
make指令通过读取makefile内的规则来对发生改变的源代码进行重新编译而不是编译所有文件,以提高编译速度。
Makefile格式
target : prerequiries
command
makefile的主体由以上格式组成,当prerequiries的修改时间比target晚或target文件不存在,则执行command语句。
make指令
执行make指令时,make默认会查找当前目录下名为Makefile的文件并生成遇到的第一个target。make指令的常用选项如下所示:
make -f Makefile.build # 使用 -f 选项指定要读取的文件名
make -C /dir # 使用 -C 选项指定要切换到的目录
make other_target # 可以指定从makefile文件内的特定target开始执行
Makefile变量
可以使用如下方法在Makefile文件中定义变量,Makefile中变量的本质是起了别名的字符串
VER1 := 123 // 定义即时变量VER1并赋值常量123
VER2 ?= ${VER1} // 定义延时变量VER2并赋值VER1变量的值
VER3 = ${VER2} // 定义延时变量VER3并赋值VER2变量的值
可以以${var_name}或$(var_name)的方式来引用变量,变量分为两种:即时变量和延时变量它们的区别主要体现在确定变量值的时间点不同,大体如下:
-
即时变量
即时变量的值在Makefile文件解析时就以及确定了,如下例子中定义即时变量VER2,它的值为VER1的值,由于Makefile解析到VER2时VER1的值为10,因此VER2的值保持为10,即使后续VER1被重新赋值为了20,VER2的值也不再更新了。VER1 := 10 VER2 := ${VER1} VER1 := 20 all : echo VER2 //打印结果为10
-
延时变量
延时变量的值在使用到它时才确定,如下例子中定义延时变量VER2,它的值为VER1的值,虽然Makefile解析到VER2时VER1的值为10,但是当我们使用VER2变量时VER1已经被重新赋值为了20,因此使用时展开VER2得到的值为20。VER1 := 10 VER2 = ${VER1} VER1 := 20 all : echo VER2 //打印结果为20
同时还存在一些特殊的预定义变量
test : a.o b.o c.o
echo $@ //目标的名字,会打印 all
echo $^ //构建所需文件列表的所有文件名字,会打印 a.o b.o c.o
echo $< //构建所需文件列表中第一个文件的名字,会打印 a.o
echo $? / 构建所需文件列表中更新过的文件,打印所有修改时间比test晚的文件
Makefile常用函数
函数格式:${function_name param1,param2,parma3,…} 或 $(function_name param1,param2,parma3,…)
函数名称与参数之间通过空格分割,多个参数之间通过逗号分割
-
${foreach var,list,text}
遍历list中的元素并赋值给var,之后将var按照text的所描述的形式修改obj := a.o b.o dep_files := ${foreach f, ${obj}, ${f}.d} // 最终 dep_files := a.o.d b.o.d
-
${wildcard pattern}
找出当前目录下符合指定pattern格式的所有文件src_files := ${wildcard *.c} //找出当前目录下所有.c结尾的文件并赋值给src_files // 假设当前目录下存在文件 a.c b.c d.c,则执行后 src_files := a.c b.c d.c
-
${filter pattern…,text}
保留text中所有符合pattern格式的字段,pattern可以有多个,用空格分割,会保留符合任何一个pattern的字段obj := a.c b.c d.c a/ b/ c/ DIR := ${filter %/, ${obj}} //运行结果 DIR := a/ b/ c/
-
${filter-out pattern…, text}
排除text中所有符合pattern格式的字段,pattern可以有多个,用空格分割,会排除符合任何一个pattern的字段obj := a.c b.c d.c a/ b/ c/ src := ${filter-out %/, ${obj}} //运行结果 src := a.c b.c d.c
-
${patsubst pattern, replacement, text}
寻找text中符合格式pattern的字段,将它们变成replacement所指的格式subdir := c/ d/ subdir := ${patsubst %/ %, ${subdir}} //变换结果为subdir := c d
其他知识点
-
变量导出
Makefile文件A调用Makefile文件B时,如果希望Makefile文件A中的变量在Makefile文件B中可见,则可以使用export命令将变量导出,如下:VER := hello export VER all : make -f ./sub_makefile // 在sub_makefile中可以直接使用VER变量
-
执行shell命令
在makefile中可以直接执行shell指令TOPDIR := ${shell pwd} // 执行shell指令pwd并将返回的结果赋值给变量TOPDIR
-
设置第一个目标
make默认从Makefile中的第一个目标开始执行,有时可以先将想开始执行的目标放文件开头”声明“一下,后面再完善:First_target : //什么都不干 ... //执行一些其他操作:包含其他makefile、声明变量、编写其他目标等 First_target : ${xxx} ${yyy} // 目标本体 command
-
假想目标
使用.PHONY将目标设置为假想目标,则不管目标文件是否存在,在执行目标时,目标内的command总是会得到执行.PHONY : clean clean : rm -f ${shell find -name "*.o"} rm -f ${TARGET}
通用Makefile
提取自韦东山老师的教程资料,可以自取,里面有例子和使用说明,花太多时间折腾Makefile实在没有必要。
链接:通用Makefile文件
提取码:vjbj