目录
- 1. C
- 1.1 编译C
- 1.2 创建静态库
- 1.3 创建动态库
- 2. C++
- 3. Makefile
- 3.1 变量
- 3.2 常用函数
- 3.3 makefile编译文件
1. C
linux下常见的C语言项目相关的文件如下图所示。
1.1 编译C
通常使用GCC来编译C文件。编译过程为源文件.c文件 -> 预编译成.i文件 -> 编译成汇编语言.s -> 汇编成.o文件 -> 链接成可执行文件。编译命令为gcc -参数 .c -o 输出文件名称
- 预处理:将头文件拷贝进.c文件内容中,执行预编译命令。采用gcc -E命令
gcc -E [.c源文件] -o [输出文件名.i]
- 编译成汇编:将c语言代码编译为对应的汇编指令。采用gcc -S命令
gcc -S [.c源文件] -o [输出文件名.s]
- 编译成目标文件:二进制文件
gcc -c [.c源文件] [.c源文件] [...] -o [文件名]
- 编译成可执行文件:
gcc [.c源文件] [.c源文件] [...] -o [输出文件名]
gcc [.c源文件] [.c源文件] [...] -o [输出文件名] -l[库名] -L[库所在路径]
1.2 创建静态库
程序库是已写好的、供使用的可复用代码,每个程序都要依赖很多基础的底层库。从本质上,库是一种可执行代码的二进制形式。静态库可以在编译c项目时,将引用的库一起链接到可执行文件中,可执行文件在运行时不再需要库的支持,但可执行文件会变大。
- 将库的.c源文件编译成.o目标文件
gcc -c [.c] -o [自定义文件名]
gcc -c [.c] [.c] ...
- 将.o目标文件编译成静态库
ar -r [lib自定义库名.a] [.o] [.o] ...
- 将C程序和静态库链接,编译成可执行文件。库名为lib***.a的***。
gcc [.c] -o [输出文件名] -l[库名] -L[库所在路径]
1.3 创建动态库
动态库在程序编译时不会链接到目标代码中,而是在程序运行时才被调用,可执行文件比静态链接的可执行文件要小。不同的程序可以共享相同的动态库。
- 将库的.c源文件编译成.o目标文件(要加入-fpic选项)
gcc -c -fpic [.c/.cpp][.c/.cpp]...
- 将.o目标文件编译成动态库
gcc -shared [.o][.o]... -o [lib自定义库名.so]
#将1和2合并为一步
gcc -fpic -shared [.c源文件] [.c源文件] [...] -o lib[库名].so
- 将C程序和动态库链接,编译成可执行文件。库名为lib***.so的***。
gcc [.c/.cpp] -o [自定义可执行文件名] -l[库名] -L[库路径] -Wl,-rpath=[库路径]
2. C++
编译C++的流程与编译C几乎一致,只不过用的时g++命令。将上述编译.c的gcc改为g++即可编译cpp文件。静态库与动态库同理。
3. Makefile
makefile是自动化编译的一款工具。实际中C/C++工程项目可能十分庞大,直接用手一条一条的敲gcc/g++效率极低。在makefile文件中编写编译流程可以简化编译工作。
- make 会在当前目录下找到一个名字叫 Makefile 或 makefile 的文件
- 如果找到,它会找文件中第一个目标文件(target),并把这个文件作为最终的目标文件
- 如果 target 文件不存在,或是 target 文件依赖的 .o 文件(prerequities)的文件修改时间要比 target 这个文件新,就会执行后面所定义的命令 command 来生成 target 这个文件
- 如果 target 依赖的 .o 文件(prerequisties)也存在,make 会在当前文件中找到 target 为 .o 文件的依赖性,如果找到,再根据那个规则生成 .o 文件
#makefile格式
targets : prerequisties
[tab键]command
targets为生成的目标文件;
prerequisties为为了生成targets目标而所需的依赖目标文件;
conmmand为需要运行的命令
为了避免 target 和 Makefile 同级目录下 文件/文件夹 重名的这种情况,我们可以使用一个特殊的标记 .PHONY 来显式地指明一个目标是 “伪目标”,如下例中的clean
.PHONY : clean
3.1 变量
- 变量定义用:=
cpp := src/main.cpp
obj := objs/main.o
- 使用变量()或{}
cpp := src/main.cpp
obj := objs/main.o
$(obj) : ${cpp} #cpp中的字符串代表的依赖文件,obj为生成的目标文件
@g++ -c $(cpp) -o $(obj)
#等价于g++ -c src/main.cpp -o objs/main.o
compile : $(obj) #compile为伪目标,可执行make compile命令
- 预定义变量:
$@: 目标(target)的完整名称
$<: 第一个依赖文件(prerequisties)的名称
$^: 所有的依赖文件(prerequisties),以空格分开,不包含重复的依赖文件
cpp := src/main.cpp
obj := objs/main.o
$(obj) : ${cpp}
@g++ -c $< -o $@
#等价于g++ -c src/main.cpp -o objs/main.o
@echo $^
#执行shell命令echo,打印出cpp的内容
compile : $(obj)
.PHONY : compile
- 特殊符号:\为续行符,*通配符(匹配任意字符串,可用与目录名和文件名中),%通配符(匹配任意字符串,并将匹配到的字符串作为变量使用)
3.2 常用函数
函数执行格式通常为$(fn, arguments) or ${fn, arguments}
- shell:调用shell命令,返回shell命令执行结果,使用格式为
$(shell <command> <arguments>)
# shell 指令,src 文件夹下找到 .cpp 文件
cpp_srcs := $(shell find src -name "*.cpp")
# shell 指令, 获取计算机架构
HOST_ARCH := $(shell uname -m)
- subst:把字串 <text> 中的 <from> 字符串替换成 <to>,使用格式为
$(subst <from>,<to>,<text>)
cpp_srcs := $(shell find src -name "*.cpp")
cpp_objs := $(subst src/,objs/,$(cpp_objs))
- patsubst:从 text 中取出 patttern(其中%为通配符), 替换成 replacement,使用格式为 $(patsubst <pattern>,<replacement>,<text>)
cpp_srcs := $(shell find src -name "*.cpp") #shell指令,src文件夹下找到.cpp文件
cpp_objs := $(patsubst %.cpp,%.o,$(cpp_srcs)) #cpp_srcs变量下cpp文件替换成 .o文件
- foreach:把字串<list>中的元素逐一取出来,执行<text>包含的表达式,使用格式为 $(foreach <var>,<list>,<text>)
library_paths := /lib \
/usr/local/lib64
#每一个元素前面加上-I
I_flag := $(foreach item,$(library_paths),-I$(item))
#等价于
I_flag := $(include_paths:%=-I%)
- dir:从文件路径+文件名字符串序列中取出目录部分,使用格式为
$(dir <names…>)
$(dir src/foo.c hacks) # 返回值是“src/ ./”。
- notdir:与dir函数相反,去除目录部分,只保留文件名
#嵌套使用,先用shell中的find命令寻找lib开头的文件,然后去掉这些文件的目录前缀
libs := $(notdir $(shell find /usr/lib -name lib*))
- filter:从字符串序列中,过滤出满足条件的
libs := $(notdir $(shell find /usr/lib -name lib*))
a_libs := $(filter %.a,$(libs)) #过滤出静态库
so_libs := $(filter %.so,$(libs)) #过滤出动态库
- basename:去掉文件名后缀
libs := $(notdir $(shell find /usr/lib -name lib*))
#去掉后缀和lib前缀,得到库名
a_libs := $(subst lib,,$(basename $(filter %.a,$(libs))))
so_libs := $(subst lib,,$(basename $(filter %.so,$(libs))))
- filter-out:去除不要的字符串
objs := objs/add.o objs/minus.o objs/main.o
cpp_objs := $(filter-out objs/main.o, $(objs))
3.3 makefile编译文件
编译选项:
-m64: 指定编译为 64 位应用程序
-std=: 指定编译标准,例如:-std=c++11、-std=c++14
-g: 包含调试信息
-w: 不显示警告
-O: 优化等级,通常使用:-O3
-I: 加在头文件路径前
fPIC: (Position-Independent Code), 产生的没有绝对地址,全部使用相对地址,代码可以被加载到内存的任意位置,且可以正确的执行。这正是共享库所要求的,共享库被加载时,在内存的位置不是固定的
链接选项:
-l: 加在库名前面
-L: 加在库路径前面
-Wl,<选项>: 将逗号分隔的 <选项> 传递给链接器
-rpath=: “运行” 的时候,去找的目录。运行的时候,要找 .so 文件,会从这个选项里指定的地方去找
cpp_srcs := $(shell find src -name *.cpp)
cpp_objs := $(patsubst src/%.cpp,objs/%.o,$(cpp_srcs))
include_dirs :=/home/include
I_options := $(include_dirs:%=-I%)
compile_options := -g -O3 -w $(I_options)
lib_dirs := /home/lib #库路径
link_libs := xxx #库名
L_options := $(lib_dirs:%=-L%)
l_options := $(link_libs:%=-l%)
link_options := $(l_options) $(L_options) $(I_options)
#每个.cpp生成一个对应的目标文件
objs/%.o : src/%.cpp
#创建文件夹,@表示make的时候不打印make信息
@mkdir -p $(dir $@)
@g++ -c $^ -o $@ $(compile_options)
workspace/exec : $(cpp_objs)
@mkdir workspace/exec
@g++ $^ -o $@ $(link_options)
# 输入make run即可生成run伪目标
run : workspace
@./$<
# 输入make clean即可生成clean伪目标
clean :
@rm -rf objs workspace/exec
debug :
@echo $(as_files)
.PHONY : debug run clean