4个编译过程
gcc -E test.c -o test.i
: 预处理(Preprocessing)
gcc -S test.i -o test.s
: 编译(Compilation)
*.s
文件gcc -c test.s -o test.o
or as test.s -o test.o
: 汇编(Assembly)
*.o
文件gcc test.o -o test
or ld test.o -o test
: 链接(Linking)
a.out
文件gcc 常用选项
-o output
: 指定编译生成的文件名为 output-c
: 编译生成 ELF 格式的目标代码,但不进行最后的链接-fPIC
: 生成位置无关代码,编译动态库必须-Idir
: 指定头文件的搜索路径--sysroot
: 设置编译链接默认搜索头文件和库文件的根目录-ffunction-sections -fdata-sections
: 将每个函数和变量放入到各自独立的 section-static
: 链接时强制链接静态库,单个文件即可执行,不依赖动态链接库,具有较好的兼容性,缺点是生成的程序比较大-shared
: 编译生成动态库-lname
: 链接库 libname.so 或 libname.a,例如 -lpthread
是链接 libpthread.so 库-Ldir
: 指定库文件的搜索路径-Wl,-soname=name
: 指定生成动态库的名称,可以使用 readelf -d libxxx.so
读到这个名称-Wl,-rpath-link=dir
: 指定子库文件链接的库文件的搜索路径-Wl,--gc-sections
: 按 section 链接,和编译对应的选项合用,则链接器 ld 不会链接未使用的函数,从而减小可执行文件大小-Dname
: 定义宏 name,默认定义内容为 1
-Dname=value
: 定义宏 name,值为 value
,例如 -DMAX_MUM=1024
类似定义了 #define MAX_MUM 1024
-Dname=\"value\"
: 定义宏 name,值为 字符串value
,例如 -DAUTHOR=\"LengJing\"
类似定义了#define AUTHOR "LengJing"
-O0 -O1 -O2 -Os -O3
5个等级
* -O0
: 默认优化等级,不优化用于调试
* -O1
: 缺省优化等级 -O
,编译器会尝试减少代码大小和执行时间的优化,而不执行任何需要大量编译时间的优化
* -O2
: 增加了编译时间,编译器进一步优化了生成代码的性能,一般用于发布的程序
* -Os
: 编译器禁用了 -O2
增加生成代码大小的优化,强调大小而不是速度
* -O3
: 增加了生成代码的大小,编译器进行了函数展开和循环展开等优化,生成代码的性能不一定比 -O2
更好
* -g -ggdb
: 生成带有调试信息的目标文件,可用于 gdb 调试-w
: 关闭所有警告-Wall
: 使 gcc 产生尽可能多的警告信息-Wextra
: 使 gcc 产生额外的警告信息,老的名称是 -W
-Werror
: 使 gcc 在产生警告的地方停止编译-Wxxx
: 打开特定警告-Wno-xxx
: 关闭特定警告-v
: 列出详细编译过程链接过程
.so
结尾)(运行时动态加载)和静态链接库(通常以 .a
结尾)(编译时静态加载)-static
选项将强制使用静态链接库/usr/lib
(或修改环境变量) 才可以执行,生成的程序比较小,多个程序共享同一个库,占用较少的内存,库有变化升级时也只需要重新编译库-L
LD_LIBRARY_PATH
指定的动态库搜索路径/etc/ld.so.conf
中指定的动态库搜索路径/lib
/usr/lib
-L
LIBRARY_PATH
/lib
/usr/lib
/usr/local/lib
常用命令
make
: 执行当前目录下的 Makefile 的默认目标 (Makefile 文件查找顺序: GNUmakefile
makefile
和 Makefile
)make <target>
: 执行当前目录下的 Makefile 的指定目标 target,可以同时运行多个 target,但要注意多线程make -f <file>
: --file=file, --makefile=FILE
,执行当前目录下的指定 Makefile (file) 的默认目标make -C <dir>
: --directory=dir
,进入 dir 目录,然后执行其下的 Makefile 的默认目标,再返回当前目录,相当于 cd dir && make && cd -
make -s
: -silent, --quiet
,静默模式,不要打印运行的命令,子 Makefile 会继承此参数make -j <jobs>
: --jobs=jobs
,使用多核编译,例如 make -j8
使用8核编译,注: echo $(nproc)
的输出结果为 cpu 线程数make var=value
: 传入变量,在 Makefile 中使用变量 $(var)
的地方替换成它的值 value
override var = newvalue
override var += value2
一般命令
make -I <dir>
: --include-dir=dir
,为 Makefile 文件 include 指令包含的文件指定搜索路径make -e
: --environment-overrides
,默认 Makefile 文件定义的环境变量覆盖环境变量,此参数改为环境变量优先make -i
: --ignore-errors
,忽略错误继续执行make -k
: --keep-going
,忽略出错的目标和依赖它的目标,继续执行其它目标,子 Makefile 会继承此参数make -S
: --no-keep-going, --stop
,取消 -k
参数调试命令
make -n
: --just-print, --dry-run, --recon
,只打印不执行要执行的命令,可能命令还会执行,例如 shell 多条件判断make --trace
: 打印并执行要执行的命令和打印执行的原因make -w
: --print-directory
,打印执行的 Makefile 的工作目录make -W <file>
: --what-if=file, --new-file=file, --assume-new=file
,假装文件被更新,和 -n
命令合用看看文件更新会发生什么make -p
: --print-database
,打印读取 Makefile 产生的数据库(规则和变量值),然后照常执行$(info text)
$(warning text)
$(error text)
打印信息,其中 error 会停止执行 Makefile= ?=
: 延迟展开: 递归扩展,取最后的值:= ::=
: 立即展开: 简单扩展,取当前位置的值+=
: 如果该变量先前定义的是简单变量(使用 := ::= !=
赋值的变量)则立即展开,否则延迟展开!=
: shell 赋值运算符,立即展开define ... endef
),函数名立即展开ifeq ... else ifeq ... else ... endif
ifdef ... else ifdef ... else ... endif
),条件立即展开.
开头的目标不是默认目标,除非它包含一个或多个斜杠 '/'%
)的目标不是默认目标.PHONY: target1 target2 ...
避免与同名文件发生冲突,以及提高性能(为目标跳过隐式规则搜索)
.PHONY
声明伪目标外,还可以定义一个不带依赖和命令块的规则,规则的目标是伪目标main: main.o stack.o maze.o
gcc main.o stack.o maze.o -o main
main.o: main.c main.h stack.h maze.h
gcc -c main.c
stack.o: stack.c stack.h main.h
gcc -c stack.c
maze.o: maze.c maze.h main.h
gcc -c maze.c
.PHONY: clean
clean:
-rm main main.o stack.o maze.o
@echo "clean completed"
objects = main.o stack.o maze.o
main: $(objects)
gcc $(objects) -o main
$(objects): main.h
main.o stack.o: stack.h
main.o maze.o: maze.h
.PHONY: clean
clean:
-rm main $(objects)
@echo "clean completed"
xxx.o
的目标推导 $(CC) $(CPPFLAGS) $(CFLAGS) -c xxx.c
的命令xxx.o
的目标推导 $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c xxx.cpp
的命令 (源文件也可能是 xxx.cc xxx.C)main: main.o stack.o maze.o
gcc $^ -o $@
main.o: main.h stack.h maze.h
stack.o: stack.h main.h
maze.o: maze.h main.h
.PHONY: clean
clean:
-rm main *.o
@echo "clean completed"
%.o: %.c
解释为取 %.o
的模式 %
,并为其加上 .c
的后缀,即目标的 .o 换成 .c,这个 .c 文件当做该目标的依赖objects = $(patsubst %.c,%.o,$(wildcard *.c))
main: $(objects)
gcc $^ -o $@
$(objects): %.o: %.c
gcc -c $< -o $@
# 目录下对应的.c文件编译成.o
objects = $(patsubst %.c,%.o,$(wildcard *.c))
depends = $(patsubst %.o,%.d,$(objects))
main: $(objects)
gcc $^ -o $@
$(objects): %.o: %.c
gcc -MM -MT $@ -MF $(patsubst %.o,%.d,$@) $<
gcc -c $< -o $@
-include $(depends)
# 基本规则格式
target ... : prerequisites ...
command
command
...
# 静态模式规则
targets ...: target-pattern: dep-patterns ...
commands
...
规则说明
元素组成
@
表示不显示命令本身而只显示它的结果-
表示忽略命令执行失败
-
号,即使这条命令出错,make 也会继续执行后续命令make -i
.ONESHELL:
cd dir
;\
表示下一行和当前行是同一作用域和环境,此时相互之间变量可以共享,上一条命令的结果会应用在下一条命令目标: 依赖
在同一行时,可以使用分号 ;
分隔;一行多个命令可以用分号 ;
分隔,行尾有无分号均可$(MAKECMDGOALS)
获取 make 命令运行的 target$@
: 表示规则中的目标$<
: 表示规则中的第1个依赖$^
: 表示规则中的所有依赖组成一个列表,以空格分隔$?
: 表示规则中所有比目标新的依赖组成一个列表,以空格分隔$(@D)
: 表示规则中的目标文件名的目录部分(末尾没有斜杠),相当于 $(patsubst %/,%,$(dir $@))
$(@F)
: 表示规则中的目标文件名的文件部分,相当于 $(notdir $@)
$(<D)
$(<F)
: 表示规则中的第1个依赖的文件名的目录部分和文件部分,相当于 $(patsubst %/,%,$(dir $<)) $(notdir $<)
$(^D)
$(^F)
: 表示规则中的所有依赖的目录部分和文件部分,相当于 $(foreach v,$^,$(patsubst %/,%,$(dir $v)) $(notdir $v))
$(?D)
$(?F)
: 表示规则中的所有比目标新的依赖的目录部分和文件部分,相当于 $(foreach v,$?,$(patsubst %/,%,$(dir $v)) $(notdir $v))
objects = $(patsubst %.c,$(outpath)/%.o,$(wildcard *.c))
都放在 outpath 目录的规则: $(objects): $(outpath)/%.o: %.c
含模式规则(%
)的目标名
%_config
user_%_config
两个规则,make user_defaut_config
运行的是 user_%_config
目标%_config
user_%
两个规则,make user_defaut_config
运行的是 %_config
目标/
,而其它地方却可以匹配gcc -MM -MT xxx.o -MF xxx.d xxx.c
-MM
/ -M
: 根据源代码中的 #include 语句推导出依赖,-MM
/ -M
分别表示依赖 不包含/包含
标准库的头文件-MT xxx.o
: 指定规则中的目标-MT xxx.d
: 指定规则保存的文件,不指定时输出到终端高级特性(很少用到)
例子: 在命令块中使用 shell 的条件语句和循环语句
strip_elfs:
@dirs="/lib /bin /usr/lib /usr/bin /usr/local/lib /usr/local/bin"; \
for vdir in $${dirs}; do \
if [ -d $(FAKEROOT_DIR)$${vdir} ]; then \
for vfile in $$(ls $(FAKEROOT_DIR)$${vdir}); do \
if [ $$(file $(FAKEROOT_DIR)$${vdir}/$${vfile} | grep -c "not stripped") -eq 1 ]; then \
$(STRIP) $(FAKEROOT_DIR)$${vdir}/$${vfile}; \
fi; \
done; \
fi; \
done
包含 include
include x1.mk x2.mk ...
: 可以包含多个文件include *.mk
: 可以使用通配符 *
include $(depends)
: 可以使用变量-include ...
: 文件不存在不报错退出make -I <dir>
指定的目录下去寻找,否则在系统目录下查找
prefix/include
/usr/gnu/include
/usr/local/include
/usr/include
注释 #
#
号在 Makefile 中表示单行注释
define
指令内部外,注释可以出现在 Makefile 文件的任何地方,甚至在命令内部(这里 shell 决定什么是注释内容)换行 \
\换行
被转换为单个空格字符,完成后,\换行
周围的所有空格都会压缩为一个空格
$
,例如 var1 定义了 one$ word
,$
的值为空,即定义了 oneword
\换行
都被保留并传递给 shell,如何解释 \换行
取决于您的 shell
\换行
之后的下一行的第一个字符是制表符,这个制表符将会删除例子: 换行
var1 := one$\
word
var2 := two \
words
all:
@echo $(var1)
@echo $(var2)
@echo no\
space
@echo no\
space
@echo one \
space
@echo one\
space
@echo 'hello \
world'
@echo "hello \
world"
# 结果
oneword
two words
nospace
nospace
one space
one space
hello \
world
hello world
指定搜索目录
VPATH = dir1:dir2
: VPATH
变量为所有类型的文件指定搜索目录,多个目录之间要使用 空格
或是 :
隔开vpath
: vpath
指令为特定类型的文件创建搜索目录规则,文件类型使用 %
模式替换
vpath pattern dir1:dir2
: 为符合模式 pattern 的文件指定搜索目录 dir1 和 dir2,例如 vpath %.h include:src
vpath dir1:dir2
: 为所有类型的文件指定搜索目录 dir1 和 dir2vpath <pattern>
: 清除符合模式 pattern 的文件的搜索目录vpath
: 清除所有类型的文件的搜索目录使用条件
ifdef
ifndef
ifeq
ifneq
和 else
endif
类似C语言中的条件编译,这些关键字都要顶格写ifdef CONFIG_NAME1
command or variable_definition
...
else ifdef CONFIG_NAME2
command or variable_definition
...
else
command or variable_definition
...
endif
ifeq ($(CONFIG_NAME1),value1)
command or variable_definition
...
else ifeq ($(CONFIG_NAME2),value2)
command or variable_definition
...
else
command or variable_definition
...
endif
变量定义 变量名 赋值运算符 值
:
#
=
空格
的字符构成,且区分大小写undefine 变量名
变量使用 $(变量名)
$
符号,最好用小括号 ()
或是大括号 {}
把变量给包起来,否则只会将 $
后的单个字符视为变量名$
字符需要用 $$
来表示;如果启用了二次扩展真实的 $
字符需要用 $$$$
来表示扩展方式
var = $(dirname $(var))
这样的调用.SECONDEXPANSION:
启用二次扩展,此时$$(var)
则可以扩展为变量 var 的值变量赋值
=
: 延迟赋值,递归扩展
?=
: 默认赋值,变量如果先前没有被赋值才赋这个值:=
: 立即赋值,简单扩展
::=
: 同 :=
,于 2012 年被添加到 POSIX 标准中+=
: 追加赋值,扩展方式取决于前面赋值该变量的方式
变量赋多行值 define ... endef
=
define 变量名 赋值运算符
...
...
endef
a = 123
b := 456
c = 789
b += $(a)
c += $(a)
d := $(a)
e = $(a)
f = $(a) $(b)
$(a) = ABC
a = abc
$(a) = HIJ
all:
@echo a = $(a)
@echo b = $(b)
@echo c = $(c)
@echo d = $(d)
@echo e = $(e)
@echo f = $(f)
@echo 123 = $(123)
@echo abc = $(abc)
# 结果
a = abc
b = 456 123
c = 789 abc
d = 123
e = abc
f = abc 456 123
123 = ABC
abc = HIJ
VARIABLE-ASSIGNMENT
可以使用任何一个有效的赋值方式 =
:=
+=
?=
A: var_private := var_public
make var=value
定义的变量,需要在 VARIABLE-ASSIGNMENT
前加关键字 override
%
的目标VARIABLE-ASSIGNMENT
前加关键字 private
抑制继承TARGET ... : VARIABLE-ASSIGNMENT
命令块中变量 $${var}
$$var
可以写成 $${var}
,不可以写成 $$(var)
$$
来表示2 4 6 8 10
例子: 命令块中使用 shell 变量
all:
@var=1; \
var=$$(( $$var + 10 )); \
for i in $$(seq $$var); do \
if [ $$(( $$i % 2 )) -eq 0 ]; then \
echo -n "$$i "; \
fi; \
done; \
echo
引用替换
$(var:str1=str2)
${var:str1=str2}
{var:%str1=%str2}
%
开头,%
的意思是匹配零或若干字符var := file1.c file2.c file3.c
, 则 $(var:.c=.o)
的值为 file1.o file2.o file3.o
变量导出
export var1 var2 ...
: 传递变量到下级 Makefile 中,单独 export
表示传递所有变量
make -e
参数export var = value
export var := value
export var += value
: 定义并导出变量unexport var1 var2 ...
: 取消传递某些变量到下级 Makefile 中,单独 unexport
取消表示传递所有变量AR
AS
CC
CXX
CPP
(gcc -E) LEX
YACC
ASFLAGS
(as) CFLAGS
(c) CXXFLAGS
(c++) LDFLAGS(ld)RM
(rm -f)MAKEFILE_LIST
MAKEFILE_LIST
,其它的很少用到MAKEFILE_LIST
中MAKEFILES
指定、命令行指定、当前工作下的默认的以及使用指示符 include
指定包含MAKEFILE_LIST
作为目标的依赖,这样 Makefile 改变了就也可以重新编译例子: 获取 Makefile 文件所在的目录
#### Makefile 文件 ####
dir_name := $(patsubst %/,%,$(dir $(lastword $(MAKEFILE_LIST))))
all: cur_dir := $(dir_name)
all: a b
@echo cur_dir in \"$@\" is: $(cur_dir)
include dira/inc.mk
include $(shell pwd)/dirb/inc.mk
#### dira/inc.mk 文件 ####
dir_name := $(patsubst %/,%,$(dir $(lastword $(MAKEFILE_LIST))))
a: cur_dir := $(dir_name)
a:
@echo cur_dir in \"$@\" is: $(cur_dir)
#### dirb/inc.mk 文件 ####
dir_name := $(patsubst %/,%,$(dir $(lastword $(MAKEFILE_LIST))))
b: cur_dir := $(dir_name)
b:
@echo cur_dir in \"$@\" is: $(cur_dir)
# 结果
cur_dir in "a" is: dira
cur_dir in "b" is: /home/lengjing/test/dirb
cur_dir in "all" is: .
获取当前目录
$(patsubst %/,%,$(dir $(lastword $(MAKEFILE_LIST))))
: 读取 Makefile 时此 Makefile 相对解析时所在目录的路径$(PWD)
: 运行 make 命令时的当前目录的绝对路径$(shell pwd)
: 解析 Makefile 时的此 Makefile 所在目录的绝对路径
$(shell pwd)
会解析真实的文件路径(解符号链接),而命令行直接运行 pwd
不会解析$(shell pwd | other_cmd)
make -C dir
时会解符号链接,而直接 make
时不会解析例子: 不同函数获取到的当前目录
#### /tmp/Makefile 文件 ####
include test/Makefile
#### /tmp/test/Makefile`的内容 ####
DIR1 = $(patsubst %/,%,$(dir $(lastword $(MAKEFILE_LIST))))
DIR2 = $(PWD)
DIR3 = $(shell pwd)
all:
@echo DIR1 = $(DIR1)
@echo DIR2 = $(DIR2)
@echo DIR3 = $(DIR3)
# 结果
/tmp$ make
DIR1 = test
DIR2 = /tmp
DIR3 = /tmp
/tmp$ make -C test
make: Entering directory '/tmp/test'
DIR1 = .
DIR2 = /tmp
DIR3 = /tmp/test
make: Leaving directory '/tmp/test'
/tmp$ cd test && make && cd -
DIR1 = .
DIR2 = /tmp/test
DIR3 = /tmp/test
/tmp
函数调用: $(函数名 参数)
${函数名 参数}
例子: 替换空格为逗号
comma := ,
empty :=
space := $(empty) $(empty)
foo := a b c
bar := $(subst $(space),$(comma),$(foo))
# bar is now 'a,b,c'
shell 函数: $(shell cmdline)
$$(cmdline)
$(shell cmdline)
: 解析时展开,不允许顶格(即命令没有所属 target)直接调用,但可以用在变量的值、目标、依赖、命令块、函数参数中
$$(cmdline)
: 运行时展开,只能用在命令块中例子: 获取文件时间戳
all:
@echo $(shell stat -c %Y test)
@echo a >> test && sleep 1
@echo $(shell stat -c %Y test)
@echo b >> test && sleep 1
@echo $$(stat -c %Y test)
@echo c >> test && sleep 1
@echo `stat -c %Y test`
# 结果: 第2个时间戳和第1个相同,第3个和第4个的时间戳和前面的都不相同
自定义函数: define endef
define
开始,以 endef
结束,注意 define 和 endef 是顶格,前面没有tab键$(0)
表示函数名,$(1)
$(2)
... 表示第1个 、 第2个 ... 参数$(call funcname,var1,var2...)
: 调用自定义函数使用 call
例子: 自定义函数
define My_name
@echo "$(subst _, ,$(0)) is $(1) $(2)."
endef
all:
$(call My_name,Leng,Jing)
# output is now 'My name is Leng Jing.'
eval
函数的功能主要是: 根据其参数的关系、结构,对它们进行替换展开(把它理解为C语言中的宏定义 #define
),它存在两次解析扩展
$(x)
,应该使用 $$(x)
替换eval
函数的返回值是空,因此,它几乎可以放置在 Makefile 中的任何位置,而不会导致语法错误例子: 调用 eval 函数
my_dir
函数获取被 include 的 Makefile 的当前位置set_flags
函数批量定义了多个 CFLAGS_xx.o = -DDEBUG
变量,为源码单独设置编译参数
define my_dir
$(strip \
$(eval md_file_ := $$(lastword $$(MAKEFILE_LIST))) \
$(patsubst %/,%,$(dir $(md_file_)))
)
endef
local_dir := $(call my_dir)
define translate_obj
$(patsubst $(src)/%,%,$(patsubst %,%.o,$(basename $(1))))
endef
define set_flags
$(foreach v,$(2),$(eval $(1)_$(call translate_obj,$(v)) = $(3)))
endef
$(call set_flags,CFLAGS,a.c b.c,-DDEBUG)
$(subst from,to,text)
: 字符串替换
$(patsubst pattern,replacement,text)
: 单词模式替换
%
) "pattern" 的单词,用 "replacement" 替换它们,单词之间的空格被折叠成单个空格字符,前导和尾随空格被丢弃objects += $(patsubst %.c,%.o,$(wildcard *.c))
返回值是当前目录下的所有.c文件后缀换成.o的列表$(strip string)
: 空格删除压缩
$(strip a b c )
返回值是 "a b c"$(findstring find,in)
: 字符串查找
$(filter pattern ...,text)
: 单词过滤
%
) "pattern..." 的单词,可以有多个模式$(filter-out pattern ...,text)
: 单词反过滤
%
) "pattern..." 的单词,函数 filter 的反函数$(sort list)
: 单词排序去重
$(words text)
: 取单词总数
$(word $(words text),text)
返回值是 text中的最后1个单词$(word n,text)
: 取第n个单词
$(wordlist s,e,text)
: 取多个单词
$(firstword text)
: 取第1个单词
$(firstword foo bar)
返回值是 foo
$(lastword text)
: 取最后1个单词
$(lastword foo bar)
返回值是 bar
$(dir names ...
) : 取目录
./
$(dir src/foo.c main)
返回值是 src/ ./
$(notdir names ...)
: 取文件名
$(notdir src/foo.c main)
返回值是 foo.c main
$(suffix names ...)
: 取后缀
$(suffix src/foo.c src-1.0/bar.c main)
返回值是 .c .c
$(basename names ...)
: 取前缀
$(basename src/foo.c src-1.0/bar.c main)
返回值是 src/foo src-1.0/bar main
$(addsuffix suffix,names ...)
: 加后缀
$(addsuffix .c,foo bar)
返回值是 foo.c bar.c
$(addprefix prefix,names ...)
: 加前缀
$(addprefix src/,foo bar)
返回值是 src/foo src/bar
$(join list1,list2)
: 连接函数
$(join aaa bbb,111 222 333)
返回值是 "aaa111 bbb222 333
"$(wildcard pattern)
: 展开通配符
$(wildcard *.c)
返回值是 "foo.c bar.c
" (假设目录下有文件 foo.c bar.c foo.o bar.o Makefile)wildcard
$(realpath names ...
) : 获取绝对路径
/
开始,不含 ./
和 ../
,文件不存在也正常返回,会对符号链接解引用$(abspath names ...
) : 获取绝对路径
realpath
,只是不对符号链接解引用$(if condition,then-part[,else-part])
: 条件求值
condition
展开后非空,则条件为真,执行 then-part
部分,否则执行 else-part
(如果有)部分,返回值是执行分支的表达式值,无此分支时返回空$(or condition1[,condition2[,condition3…]])
: 短路或求值
conditionN
参数扩展为非空字符串,停止扩展且返回该值,否则继续 conditionN+1
参数扩展,所有 condition
都为空时返回空$(or condition1[,condition2[,condition3…]])
: 短路与求值
conditionN
参数扩展为空字符串,停止扩展且返回空,否则继续 conditionN+1
参数扩展,直到最后的 condition
不为空时,返回最后的 condition
的值$(foreach var,list,cmd)
: 循环求值
files := $(foreach dir,$(dirs),$(wildcard $(dir)/*.c))
返回值是 dirs 目录集下的所有 .c 文件列表$(file op filename[,text])
: 文件操作
<
读,此时没有 text ,>
清空写,>>
追加写$(value variable)
: 获取变量不扩展的值
$(origin variable)
: 获取变量的来源
undefined
default
environment
environment override
file
command line
override
automatic
$(flavor variable)
: 获取变量的风格
undefined
recursive
(递归变量) simple
(简单变量)KERN_MAKES := make $(BUILD_SILENT) $(BUILD_JOBS)
KERN_MAKES += $(if $(ARCH),ARCH=$(ARCH)) $(if $(CROSS_COMPILE),CROSS_COMPILE=$(CROSS_COMPILE))
KERN_MAKES += $(if $(KERNEL_OUT),O=$(KERNEL_OUT))
.PHONY: all clean install loadconfig menuconfig
all:
@mkdir -p $(KERNEL_OUT)
@$(KERN_MAKES) all
@echo "Build linux-kernel Done."
clean:
@$(KERN_MAKES) clean
@echo "Clean linux-kernel Done."
install:
@$(KERN_MAKES) $(if $(SYSROOT_DIR),INSTALL_MOD_PATH=$(SYSROOT_DIR)) modules_install
@echo "Install linux-kernel Done."
loadconfig:
@$(KERN_MAKES) $(KERNEL_CONF)
menuconfig:
@$(KERN_MAKES) menuconfig
MOD_MAKES := make $(BUILD_SILENT) $(BUILD_JOBS)
MOD_MAKES += -C $(KERNEL_SRC)
MOD_MAKES += $(if $(ARCH),ARCH=$(ARCH)) $(if $(CROSS_COMPILE),CROSS_COMPILE=$(CROSS_COMPILE))
MOD_MAKES += $(if $(KERNEL_OUT),O=$(KERNEL_OUT))
MOD_MAKES += $(if $(OUT_PATH),M=$(OUT_PATH) src=$(shell pwd),M=$(shell pwd))
.PHONY: all clean install
all:
@$(MOD_MAKES) $(if $(MOD_DEPS), KBUILD_EXTRA_SYMBOLS="$(wildcard $(patsubst %,$(SYSROOT_DIR)/usr/include/%/Module.symvers,$(MOD_DEPS)))") modules
@echo "Build $(MOD_NAME) Done."
clean:
@$(MOD_MAKES) clean
@echo "Clean $(MOD_NAME) Done."
install:
@make $(MOD_MAKES) $(if $(SYSROOT_DIR), INSTALL_MOD_PATH=$(SYSROOT_DIR)) modules_install
ifneq ($(INSTALL_HEADER), )
@mkdir -p $(SYSROOT_DIR)/usr/include/$(MOD_NAME)
@cp -fp $(OUT_PATH)/Module.symvers $(SYSROOT_DIR)/usr/include/$(MOD_NAME)
@cp -rfp $(INSTALL_HEADER) $(SYSROOT_DIR)/usr/include/$(MOD_NAME)
@echo "Install $(MOD_NAME) Done."
endif
obj-m := $(moda).o $(modb).o ...
$(moda)-y := srca1.o srca2.o ...
$(modb)-y := srcb1.o srcb2.o ...
...
Makefile 说明
KERNEL_SRC
: 内核的源码目录
/lib/modules/$(shell uname -r)/build
KERNEL_OUT
: 内核的编译输出目录SYSROOT_DIR
: 安装文件的根目录KERNEL_CONF
: 编译内核源码的配置,需要在 $(KERNEL_SRC)/arch/$(ARCH)/configs
找到此配置OUT_PATH
: 模块的编译输出目录MOD_NAME
: 当前模块的名字,决定头文件的安装位置MOD_DEPS
: 当前模块依赖的其它模块的 MOD_NAME,多个名字使用空格隔开INSTALL_HEADER
: 当前模块要安装的头文件modx
: 编译生成的模块名 modx.ko
srcxx.o
: 编译对应模块需要的源码的后缀 .c .S
换成 .o
BUILD_SILENT
: 设置该变量的值为 -s
,静默编译BUILD_JOBS
: 设置该变量的值为 -jn
,启用多线程编译,例如 -j8
启用8线程编译ARCH=xxx
: 设置要构建的体系结构,例如 arm64
,直接导出 ARCH
环境变量就不需要此设置CROSS_COMPILE=xxx
: 设置构建的交叉编译器,例如 arm-linux-
,直接导出 CROSS_COMPILE
环境变量就不需要此设置O=xxx
: 构建内核时指定输出目录,编译输出和源码同目录或直接导出 KBUILD_OUTPUT
环境变量就不需要此设置,O=xxx
优先级更高INSTALL_MOD_PATH=xxx
: 指定安装模块的根目录,直接导出 INSTALL_MOD_PATH
环境变量就不需要此设置
$(INSTALL_MOD_PATH)/lib/modules/$(KERNELRELEASE)/linux
$(INSTALL_MOD_PATH)/lib/modules/$(KERNELRELEASE)/extra
,可以使用 INSTALL_MOD_DIR
的值修改 extra
M=xxx
: 通知 kbuild 正在构建外部模块,设置编译输出目录,直接导出 KBUILD_EXTMOD
环境变量就不需要此设置,M=xxx
优先级更高src=xxx
: 设置源码目录,不设置时源码目录同 M=xxx
设置的值KBUILD_EXTRA_SYMBOLS = xxx
: 设置查找的符号导出文件 Module.symvers
,依赖其它模块时必须设置,没有依赖或直接导出 KBUILD_EXTRA_SYMBOLS
环境变量就不需要此设置编译外部模块说明
modules
构建模块,modules_install
安装模块,clean
清理编译src
目录下的 Kbuild(优先) 或 Makefile,源文件编译成目标文件 *.o
src
目录下的 Kbuild(优先) 或 Makefile,链接目标文件生成模块 *.ko
$(M)
下的编译脚本, 所以编译输出到非源码目录时,需要先将 Kbuild 或 Makefile 复制到 OUT_PATH 目录find
命令或 wildcard
函数查找源码时必须加上 $(src)/
前缀
$(src)
指向源码目录,$(obj)
指向编译输出目录KERNELRELEASE
在当前目录执行时没有值,在内核源码目录执行时被赋值,所以我们可以把 Kbuild 和 Makefile 写成一个 Makefile
ifneq ($(KERNELRELEASE),)
Kbuild 文件内容
else
Makefile 文件内容
endif
obj-m := modname.o
: 设置 obj-m
变量,指定要生成的模块名(注意后缀 .o
),生成 modname.ko
,可以同时指定多个模块modname-y := src1.o src2.o ...
: 设置生成模块使用的目标文件,内核 Makefile 会在 $(M)
指定的目录生成目标文件
modname.o
,那么这条语句要删除make -C $(KERNEL_SRC) M=$(shell pwd) modules
make -C $(KERNEL_SRC) O=(KERNEL_OUT) M=$(OUT_PATH) src=$(shell pwd) modules
编译标志说明
ccflags-y
asflags-y
ldflags-y
: 设置调用 cc(编译) as(汇编) ld(链接) 命令使用的标志
EXTRA_CFLAGS
EXTRA_AFLAGS
EXTRA_LDFLAGS
subdir-ccflags-y`` ubdir-asflags-y
: 设置调用 cc(编译) as(汇编) 命令使用的标志,这些标志还会影响子目录的 Kbuildccflags-remove-y
asflags-remove-y
: 删除调用 cc(编译) as(汇编) 命令使用的某些标志CFLAGS_xxx.o
AFLAGS_xxx.o
: 为生成特定目标 xxx.o 指定 cc(编译) as(汇编) 命令使用的标志Вы можете оставить комментарий после Вход в систему
Неприемлемый контент может быть отображен здесь и не будет показан на странице. Вы можете проверить и изменить его с помощью соответствующей функции редактирования.
Если вы подтверждаете, что содержание не содержит непристойной лексики/перенаправления на рекламу/насилия/вульгарной порнографии/нарушений/пиратства/ложного/незначительного или незаконного контента, связанного с национальными законами и предписаниями, вы можете нажать «Отправить» для подачи апелляции, и мы обработаем ее как можно скорее.
Опубликовать ( 0 )