目录

  • 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 输出文件名称

  1. 预处理:将头文件拷贝进.c文件内容中,执行预编译命令。采用gcc -E命令
gcc -E [.c源文件] -o [输出文件名.i]
  1. 编译成汇编:将c语言代码编译为对应的汇编指令。采用gcc -S命令
gcc -S [.c源文件] -o [输出文件名.s]
  1. 编译成目标文件:二进制文件
gcc -c [.c源文件] [.c源文件] [...] -o [文件名]
  1. 编译成可执行文件:
gcc [.c源文件] [.c源文件] [...] -o [输出文件名]gcc [.c源文件] [.c源文件] [...] -o [输出文件名] -l[库名] -L[库所在路径]

1.2 创建静态库

程序库是已写好的、供使用的可复用代码,每个程序都要依赖很多基础的底层库。从本质上,库是一种可执行代码的二进制形式。静态库可以在编译c项目时,将引用的库一起链接到可执行文件中,可执行文件在运行时不再需要库的支持,但可执行文件会变大。

  1. 将库的.c源文件编译成.o目标文件
gcc -c [.c] -o [自定义文件名] gcc -c [.c] [.c] ...
  1. 将.o目标文件编译成静态库
ar -r [lib自定义库名.a] [.o] [.o] ...
  1. 将C程序和静态库链接,编译成可执行文件。库名为lib***.a的***。
gcc [.c] -o [输出文件名] -l[库名] -L[库所在路径]

1.3 创建动态库

动态库在程序编译时不会链接到目标代码中,而是在程序运行时才被调用,可执行文件比静态链接的可执行文件要小。不同的程序可以共享相同的动态库。

  1. 将库的.c源文件编译成.o目标文件(要加入-fpic选项)
gcc -c -fpic [.c/.cpp][.c/.cpp]... 
  1. 将.o目标文件编译成动态库
gcc -shared [.o][.o]... -o [lib自定义库名.so]#将1和2合并为一步gcc -fpic -shared [.c源文件] [.c源文件] [...] -o lib[库名].so
  1. 将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键]commandtargets为生成的目标文件;prerequisties为为了生成targets目标而所需的依赖目标文件;conmmand为需要运行的命令

为了避免 target 和 Makefile 同级目录下 文件/文件夹 重名的这种情况,我们可以使用一个特殊的标记 .PHONY 来显式地指明一个目标是 “伪目标”,如下例中的clean

.PHONY : clean

3.1 变量

  1. 变量定义用:=
cpp := src/main.cpp obj := objs/main.o
  1. 使用变量()或{}
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.ocompile : $(obj) #compile为伪目标,可执行make compile命令
  1. 预定义变量:
    $@: 目标(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
  1. 特殊符号:\为续行符,*通配符(匹配任意字符串,可用与目录名和文件名中),%通配符(匹配任意字符串,并将匹配到的字符串作为变量使用)

3.2 常用函数

函数执行格式通常为$(fn, arguments) or ${fn, arguments}

  1. shell:调用shell命令,返回shell命令执行结果,使用格式为
    $(shell )
# shell 指令,src 文件夹下找到 .cpp 文件cpp_srcs := $(shell find src -name "*.cpp") # shell 指令, 获取计算机架构HOST_ARCH := $(shell uname -m)
  1. subst:把字串 中的 字符串替换成 ,使用格式为
    $(subst ,,)
cpp_srcs := $(shell find src -name "*.cpp")cpp_objs := $(subst src/,objs/,$(cpp_objs)) 
  1. patsubst:从 text 中取出 patttern(其中%为通配符), 替换成 replacement,使用格式为 $(patsubst ,,)
cpp_srcs := $(shell find src -name "*.cpp") #shell指令,src文件夹下找到.cpp文件cpp_objs := $(patsubst %.cpp,%.o,$(cpp_srcs)) #cpp_srcs变量下cpp文件替换成 .o文件
  1. foreach:把字串中的元素逐一取出来,执行包含的表达式,使用格式为 $(foreach ,,)
library_paths := /lib \ /usr/local/lib64#每一个元素前面加上-II_flag := $(foreach item,$(library_paths),-I$(item))#等价于I_flag := $(include_paths:%=-I%)
  1. dir:从文件路径+文件名字符串序列中取出目录部分,使用格式为
    $(dir )
$(dir src/foo.c hacks)# 返回值是“src/ ./”。
  1. notdir:与dir函数相反,去除目录部分,只保留文件名
#嵌套使用,先用shell中的find命令寻找lib开头的文件,然后去掉这些文件的目录前缀libs := $(notdir $(shell find /usr/lib -name lib*))
  1. filter:从字符串序列中,过滤出满足条件的
libs:= $(notdir $(shell find /usr/lib -name lib*))a_libs:= $(filter %.a,$(libs)) #过滤出静态库so_libs := $(filter %.so,$(libs))#过滤出动态库
  1. 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))))
  1. filter-out:去除不要的字符串
objs := objs/add.o objs/minus.o objs/main.ocpp_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/includeI_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/execdebug :@echo $(as_files).PHONY : debug run clean