这里使用的Makefile操控编译器
常见的组成部分
包含的常见的软件
默认的搜索路径
查看命令
echo | gcc -v -x c -E -
预处理阶段, 把.c结尾的文件的#开头的命令进行预处理, 直接把对应的文本内容放到对应的位置
编译器, 把预处理之后的文件修改为汇编文件
汇编器, 汇编文件转换为二进制文件
连接器, 把所有的.o文件连接在一起形成一个可执行文件
.a 静态库文件
.c C语言文件
.h 头文件
.i 预处理之后的文件
.o 目标文件
.s 汇编文件
.so 共享的库
gcc -E mian.c
这一个会进行预处理但是没有生成一个文件
gcc -E main.c -o main.i
使用-o指明输出文件的名字
gcc -S main.c
gcc -S main.c -o main.s
gcc -c main.c
gcc -c main.c -o main.o
gcc main.c
ar -r [lib库名.a] [.o] [.o]
gcc [.c] [.a] -o [输出的文件名]
gcc [.c] -o [输出的文件名] -l [库的名字] -L [库所在的位置]
注: windows下面为.lib文件, 但是测试的时候.a文件才可以使用
gcc -c -fpic [.c/.cpp] ...
编译成-o文件, 这样编译出来的文件使用都是相对地址
gcc -shared [.o] [.o] ... -o [lib.so]
创建动态库
gcc [.c/.cpp] -o [自定义可执行文件] -l[库名] -L[库路径] -wl,-rpath=[库路径]
链接动态库到可执行文件
注: windows下面为dll文件
静态库在编译时会被完整地复制到可执行文件中,因此它们会增加可执行文件的大小。在链接时刻,编译器会将静态库的代码与可执行文件的代码合并,生成一个完整的可执行文件。由于静态库已经被完整地复制到可执行文件中,因此程序在运行时不需要再加载静态库,这样可以提高程序的运行速度。但是,如果多个可执行文件都使用同一个静态库,那么静态库的代码会被复制多次,浪费空间。
动态库则是在程序运行时才被加载到内存中,因此它们不会增加可执行文件的大小。在链接时刻,编译器只会将动态库的引用信息添加到可执行文件中,而不会将动态库的代码复制到可执行文件中。程序在运行时会动态地加载动态库,并将其映射到内存中。由于多个可执行文件可以共享同一个动态库,因此动态库可以节省内存空间。但是,由于动态库需要在程序运行时才能加载,因此程序的启动速度可能会受到影响。
总的来说,静态库适用于需要高效运行的小型程序,而动态库适用于需要共享代码和节省内存空间的大型程序。
g++ -E main.c
g++ -E main.c -o maim.i
预处理文件
g++ -S main.c -o mian.s
生成汇编文件
g++ -c main.c -o main.o
生成预处理文件
g++ main.c -o main.exe
生成可执行文件
ar -cr [lib库的名字.a] [.o] [.o]
g++ [.c] [.a] -o [输出的文件名]
g++ [.c] -o [输出的文件名] -l[库的名字] -L[库所在的位置]
静态库文件
g++ -c -fpic [.c/.cpp] ...
编译成-o文件
g++ -shared [.o] [.o] ... -o [lib库的名字.lib]
创建动态库
g++ [.c/.cpp] -o [自定义可执行文件] -l[库名] -L[库路径] -wl,-rpath=[库路径]
链接动态库到可执行文件
targets : prerequisties
[tab键]command
这样的话会输出使用的命令, 可以在前面添加@使得输出隐藏
debug :
@echo hello
PS E:\桌面\c_test> make
hello
PS E:\桌面\c_test>
为了避免 target 和 Makefile 同级目录下 文件/文件夹
重名的这种情况,我们可以使用一个特殊的标记 .PHONY
来显式地指明一个目标是 “伪目标”,向 make 说明,不管是否有这个文件/文件夹,这个目标就是 “伪目标”
.PHONY : clean
如果有一个名字叫clean的文件在这个文件夹下面, 就不会执行这一个命令
这个时候就会输出
debug : world @echo hello world : @echo world clean : @rm *.o
PS E:\桌面\c_test> make clean make: 'clean' is up to date. PS E:\桌面\c_test>
只要有这个声明,不管是否有 “clean” 文件/文件夹,要运行 “clean” 这个目标,只有"make clean" 这个命令
注意
debug : world
@echo hello
world :
@echo world
clean :
@rm *.o
.PHONY : clean
这个时候就会执行这一个命令了
cpp := src/main.cpp
obj := objs/main.o
cpp := src/main.cpp
obj := objs/main.o
$(obj) : ${cpp}
@g++ -c $(cpp) -o $(obj)
compile : $(obj)
compiler := cpp
debug :
@echo $(compiler)
PS E:\桌面\c_test> make
cpp
变量会被转化为在上面输入的变量
$@
: 目标(target)的完整名称$<
: 第一个依赖文件(prerequisties)的名称$^
: 所有的依赖文件(prerequisties),以空格分开,不包含重复的依赖文件cpp := src/main.cpp
obj := objs/main.o
$(obj) : ${cpp}
@g++ -c $< -o $@
@echo $^
compile : $(obj)
.PHONY : compile
HOST_ARCH = arrch64
TARGET_ARCH = $(HOST_ARCH)
HOST_ARCH = amd64
debug :
@echo $(TARGET_ARCH)
PS E:\桌面\c_test> make debug
amd64
PS E:\桌面\c_test>
会随时更新使用=的变量
赋值了之后就不会进行更改
HOST_ARCH := arrch64
TARGET_ARCH := $(HOST_ARCH)
HOST_ARCH := amd64
debug :
@echo $(TARGET_ARCH)
@echo $(HOST_ARCH)
PS E:\桌面\c_test> make debug
arrch64
amd64
PS E:\桌面\c_test>
HOST_ARCH := arrch64
TARGET_ARCH := $(HOST_ARCH)
HOST_ARCH ?= amd64
debug :
@echo $(TARGET_ARCH)
@echo $(HOST_ARCH)
PS E:\桌面\c_test> make debug
arrch64
arrch64
HOST_ARCH := arrch64
TARGET_ARCH := $(HOST_ARCH)
HOST_ARCH += amd64
debug :
@echo $(TARGET_ARCH)
@echo $(HOST_ARCH)
PS E:\桌面\c_test> make debug
arrch64
arrch64 amd64
PS E:\桌面\c_test>
HOST_ARCH := arrch64 \
arm63 x86
TARGET_ARCH := $(HOST_ARCH)
HOST_ARCH += amd64
debug :
@echo $(TARGET_ARCH)
@echo $(HOST_ARCH)
PS E:\桌面\c_test> make debug
arrch64 arm63 x86
arrch64 arm63 x86 amd64
PS E:\桌面\c_test>
函数的调用
$(fn , argument) or ${fn, argument}
$(shell )
名称: shell
功能: 调用shell 使用commend
返回: shell执行commend的结果
pwd := $(shell pwd)
debug :
@echo $(pwd)
PS E:\桌面\c_test> make debug
/e/妗岄潰/c_test
PS E:\桌面\c_test>
$(subst ,,)
cpp_src := $(shell ls *.c)
cpp_objs := $(subst .c,.exe, $(cpp_src))
debug :
@echo $(cpp_src)
@echo $(cpp_objs)
PS E:\桌面\c_test> make
add.c main.c
add.exe main.exe
PS E:\桌面\c_test>
$(patsubst ,,)
通配符替换字符串
通配符%把要替换的文字分割开来, 可以一次性替换多个不同的字符串
cpp_src := $(shell pwd)
cpp_objs := $(patsubst /e%/c_test,/c%/jiao, $(cpp_src))
debug :
@echo $(cpp_src)
@echo $(cpp_objs)
.PHONY : debug
PS E:\桌面\c_test> make debug
/e/妗岄潰/c_test
/c/妗岄潰/jiao
PS E:\桌面\c_test>
$(foreach ,,)
library_paths := /datav/shared/100_du/03.08/lean/protobuf-3.11.4/lib \
/usr/local/cuda-10.1/lib64
library_paths := $(foreach item,$(library_paths),-L$(item))
debug :
@echo $(library_paths)
PS E:\桌面\c_test> make debug
-L/datav/shared/100_du/03.08/lean/protobuf-3.11.4/lib -L/usr/local/cuda-10.1/lib64
PS E:\桌面\c_test>
I_flag := $(include_paths:%=-I%)
library_paths := /datav/shared/100_du/03.08/lean/protobuf-3.11.4/lib \
/usr/local/cuda-10.1/lib64
# library_paths := $(foreach item,$(library_paths),-L$(item))
library_paths := $(library_paths:%=-L%)
debug :
@echo $(library_paths)
PS E:\桌面\c_test> make debug
-L/datav/shared/100_du/03.08/lean/protobuf-3.11.4/lib -L/usr/local/cuda-10.1/lib64
PS E:\桌面\c_test>
$(dir )
名称:取目录函数——dir。
功能:从文件名序列中取出目录部分。目录部分是指最后一个反斜杠(“/”)之前 的部分。如果没有反斜杠,那么返回“./”。
返回:返回文件名序列的目录部分。
.PHONY : debug
cpp_srcs := $(shell ls *.c)
# 在文件名前面加上路径
cpp_objs := $(foreach item,$(cpp_srcs),./output/$(item))
# 获取输出的文件的文件名
cpp_objs := $(subst .c,.o,$(cpp_objs))
debug :
@echo $(cpp_srcs)
@echo $(cpp_objs)
# 生成所有的.o文件
./output/%.o : %.c
@mkdir -p $(dir $@)
gcc -c $^ -o $@
compile : $(cpp_objs)
PS E:\桌面\c_test> make compile
gcc -c add.c -o output/add.o
gcc -c main.c -o output/main.o
PS E:\桌面\c_test>
使用makedir -p可以在没有文件夹的时候创建文件夹
In a Makefile, the % symbol is used as a wildcard character to match any string. It is often used in conjunction with pattern rules to specify a set of targets and dependencies that follow a certain pattern.
$(notdir )
如果输入的文件里面有文件夹的路径名, 就会去掉
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*))
a_libs := $(subst lib,,$(basename $(filter %.a,$(libs))))
so_libs := $(subst lib,,$(basename $(filter %.so,$(libs))))
会去除.o和.so的文件后缀名,在之后就是去掉文件的前缀
objs := objs/add.o objs/minus.o objs/main.o
cpp_objs := $(filter-out objs/main.o, $(objs))
cpp_srcs := $(wildcard src/*.cc src/*.cpp src/*.c)
.PHONY : debug compile
c_srcs := $(shell ls *.c)
# 添加输出的文件的文件夹名字
c_objs := $(foreach item,$(c_srcs),./output/$(item))
# 获取要输出的各种文件的名字
c_objs := $(subst .c,.o,$(c_objs))
c_i := $(subst .o,.i,$(c_objs))
c_s := $(subst .o,.s,$(c_objs))
run : output/result.exe
@.\$<
debug :
@echo $(c_objs)
@echo $(c_i)
output/%.o : %.c
@mkdir -p $(dir $@)
gcc -c $^ -o $@
output/%.i : %.c
@mkdir -p $(dir $@)
gcc -E $^ -o $@
output/%.s : %.c
@mkdir -p $(dir $@)
gcc -S $^ -o $@
output/result.exe : $(c_objs)
@mkdir -p $(dir $@)
gcc $^ -o $@
clean :
rm -rf output
compile : $(c_objs) $(c_i) $(c_s) output/result.exe
编译选项
-m64
: 指定编译为 64 位应用程序-std=
: 指定编译标准,例如:-std=c++11、-std=c++14-g
: 包含调试信息-w
: 不显示警告-O
: 优化等级,通常使用:-O3-I
: 加在头文件路径前fPIC
: (Position-Independent Code), 产生的没有绝对地址,全部使用相对地址,代码可以被加载到内存的任意位置,且可以正确的执行。这正是共享库所要求的,共享库被加载时,在内存的位置不是固定的链接选项
-l
: 加在库名前面-L
: 加在库路径前面-Wl,<选项>
: 将逗号分隔的 <选项> 传递给链接器-rpath=
: “运行” 的时候,去找的目录。运行的时候,要找 .so 文件,会从这个选项里指定的地方去找c_srcs := $(shell ls ./src//*.c)
c_objs := $(patsubst ./src%.c,./output%.o, $(c_srcs))
in_path := ./inc
I_flags := $(foreach item,$(in_path),-I$(item))
compile_options := -g -O1 -w $(I_flags)
output/%.o : src/%.c
@mkdir -p $(dir $@)
@gcc -c $^ -o $@ $(compile_options)
output/result : $(c_objs)
@mkdir -p $(dir $@)
@gcc $^ -o $@ $(compile_options)
run : output/result
@./output/result
clean :
@rm -rf ./output
gdb : output/result
gdb
debug :
@echo $(I_flags)
.PHOHY : debug run gdb clean