$(Q) 变量 内核 Makefile 文件 238 行到 259 行的注释中知道,$(Q)
变量的作用是决定是否在执行命令时输出详细的命令信息。底下对其有定义: 定义的意思是,如果 KBUILD_VERBOSE 为 1,则
quiet 和 Q 为空,即执行命令时会输出命令执行的详细信息;否则 quiet 为 quiet_ ,Q 为 @ 。在
Makefile 中,如果一个命令前使用了 @
符号,那么在执行命令时将不输出命令的执行详细信息。 抛开内核 Makefile 文件的庞大,这里只依样画葫芦的做一下实验,下面是一个测试 Makefile : KBUILD_VERBOSE := 1 ifeq ($(KBUILD_VERBOSE), 1) quiet = Q = else quite = quiet_ Q = @ endif all: $(Q)mkdir -p /home/beyes/makefile_test/quite/test_quite Make
一下: beyes@debian:~/makefile_test/quite$ make mkdir -p /home/beyes/makefile_test/quite/test_quite 由执行结果看到,Makefile 中的 mkdir
命令执行过程整个输了出来。 再改一下 Makefile 中的 KBUILD_VERBOSE := 1 为KBUILD_VERBOSE := 0,那么再次执行 make 时,已经看不见任何输出。 = 和 := 符号的区别 = 和 := 都是变量赋值符号。但是它们有些区别: "=" 如果右值包含另一个变量,那么可以在后面定义这个变量。 ":=" 如果右值包含另一个变量,则只能引用已定义的变量。 下面看示例: drivers-y := drivers/ $(head-y) head-y = header/ all: @echo $(drivers-y) 输出: $ make drivers/ drivers-y 的后面跟着一个 $(head-y) 变量,这个变量在 drivers-y 之前并未定义过,但是由于这里使用了 := 符号,所以在 drivers-y 的下一行再定义 head-y 已然无效。 那么将上面的 drivers-y 中的符号改成 = 符号,那么便可以看到区别: drivers-y = drivers/ $(head-y) head-y = header/ all: @echo $(drivers-y) 输出: [beyes@SLinux Makefile]$ make drivers/ header/ = var = def
这里的意思是,如果 var 这个变量没有被定义过,那么它的值就被定义为 def 。如果被定义过,则 def 不会被赋值到 var 中: var = def all: @echo $(var) 输出: $ make def 如果是: var = defined var = def all: @echo $(var) 则输出: $ make defined +/- 符号 除了 '-' 符号外,还可以看到 '+' 符号,它的意思和 '-' 相反,表示不忽略。这就意味着,像 make 的命令行选项 -n(--just print), -t(touch) 并不影响之前带 '+' 符号的命令的执行。像 -n 选项,一般情况下,它只是在解析命令,而不真正执行它们,但是命令前使用了 '+' 之后, -n 选项就不能阻止命令被执行。如一个目录下有这几个文件: $ ls Makefile test2.txt test.txt Makefile 的内容为: all: @rm -f test.txt +@rm -f test2.txt 使用 -n 选项来运行 make: $ make -n rm -f test.txt rm -f test2.txt 再检查一下当前目录: $ ls Makefile test.txt 可见 test2.txt
已经被删除。 @: 在一些 Makefile 中可能会在伪目标下的命令中看到 @: 这个符号,其实这不是代表一个特殊变量。这里的 @ 符号和 @echo 中表示的意思(不显示命令执行的详细内容)一样,而冒号 ":" 实际上是 shell 中的内置符号, 它表示的是一种空命令,什么都不做,也就是执行到它时,它上面的命令都已经成功执行,最后成功退出。 $$ 在使用变量时,需要在变量前加 "$" 符号,但最好是用 () 或 {} 将变量括起来,比如 $(VAR) 或 ${VAR} 。如果要使用真实的 "$“ 字符,那么需要用两个 "$" 表示,即 "$$"。 比如下面的代码: $ cat Makefile all: @echo "$$HOME" @echo "$$BASH" @echo "$$PATH" 运行输出: $ make /home/beyes /bin/sh /usr/local/Trolltech/Qt-4.3.2/bin:/usr/lib/qt-3.3/bin:/usr/kerberos/bin:/usr/local/bin:/bin:/usr/bin:/home/beyes/bin 上面,$HOME,$BASH,$PATH 都是系统内置变量。引用这些变量时,就需要用 $$,如果只用一个 $ (如 @echo "$HOME"),那么你不会看到你所希望看到的结果。 $^,$+,$*,$(@D),$(@F),$(*D),$(*F),$(%D),$(%F), $(%D),$(%F),$( $(?D),$(?F) $^、$+: 先在一个目录下建立 3 个文件: $ echo "are" > test1.txt $ echo "you" > test2.txt $ echo "ok" > test3.txt 测试代码: all:test1.txt test2.txt test3.txt test1.txt $(shell cat $^ > integra1) $(shell cat $+ > integra2) 运行输出: beyes@debian:~/Makefile/prereq$ cat integra1 are you ok beyes@debian:~/Makefile/prereq$ cat integra2 are you ok are 由输出可以很清楚的看到这两个变量的区别。 $* $(@D) /home/beyes/Makefile/main.o: @echo $(@D) 运行输出: $ make /home/beyes/Makefile 如果目标中不包含斜杠,那么输出值为 '.' 表示当前目录。 $(@F) /home/beyes/Makefile/main.o: @echo $(@F) 运行输出: $ make main.o $(*D) 和 $(*F) 分别表示目标 “茎” 中的目录部分和文件名部分。如下示例: /home/beyes/Makefile/main.o: @echo $(*D) @echo $(*F) 运行输出: $ make /home/beyes/Makefile main $(%D) 和 $(%F) 当以 "archive(member)" 这样形式的静态库为目标时,分别表示库文件成员 "member" 名中的目录部分和文件名部分。它仅对这种形式的规则目标有效。如下示例: Mylib.a(/home/beyes/main/are.o): @echo $(%D) @echo $(%F) 运行输出: $ make /home/beyes/main are.o $( 分别表示规则中第一个依赖文件的目录部分和文件名部分。如下示例: /home/beyes/main/are.o: @echo hello something.o: @echo makefile all:/home/beyes/main/are.o something.o @echo $(<<span style="color: #000000;">D) @echo $( 运行输出: $ make all hello makefile /home/beyes/main are.o $(^D) 和 $(^F) 分别表示所有依赖文件的目录部分和文件部分(不存在同一文件)。如下示例: /home/beyes/main/are.o: @echo hello /home/beyes/something.o: @echo makefile all:/home/beyes/main/are.o /home/beyes/something.o @echo $(^D) @echo $(^F) 运行输出: $ make all hello makefile /home/beyes/main /home/beyes are.o something.o $(+D) 和 $(+F) 分别表示所有依赖文件的目录部分和文件部分(可存在重复文件)。 $(?D) 和
$(?F) error 函数 error 函数表示产生了一个致命错误,当它执行后,编译会停止。 $(error <错误消息>) 测试代码: KBUILD_VERBOSE := 1 TEST_MSG := "are you ok?" ifeq ($(KBUILD_VERBOSE), 1) $(error KBUILD_VERBOSE is $(KBUILD_VERBOSE), we will stop here) quite = Q = else quite = quiet_ Q = @ endif all: $(Q)echo $(TEST_MSG) 运行输出: beyes@debian:~/makefile_test/error$ make Makefile:7: *** KBUILD_VERBOSE is 1, we will stop here。 停止。 由输出可见,all: 目标下的 echo
语句并没有执行输出,整个编译在 error 函数执行后停止了。如果将上面的 KBUILD_VERBOSE 的值改为 0 ,那么再
make 时输出: beyes@debian:~/makefile_test/error$ make are you ok? $@, $^, $< , $? 符号 Makefile $@, $^, $<</p> $@ 表示目标文件 $^ 表示所有的依赖文件 $<<span style="color: #000000;"> 表示第一个依赖文件 $? 表示比目标还要新的依赖文件列表 如一个目录下有如下文件: $ ls hello.c hi.c main.c Makefile 按照 Makefile 规则规规矩矩的写: main: main.o hello.o hi.o gcc -o main main.o hello.o hi.o main.o: main.c cc -c main.c hello.o: hello.c cc -c hello.c hi.o: hi.c cc -c hi.c clean: rm *.o rm main 改为用上述符号进行替代: main: main.o hello.o hi.o gcc -o $@ $^ main.o: main.c cc -c $<<span style="color: #000000;"> hello.o: hello.c cc -c $<<span style="color: #000000;"> hi.o: hi.c cc -c $<<span style="color: #000000;"> clean: rm *.o rm main beyes@debian:~/makefile_test/semicolon/real$ make cc -c main.c cc -c hello.c cc -c hi.c gcc -o main main.o hello.o hi.o beyes@debian:~/makefile_test/semicolon/real$ ls hello.c hello.o hi.c hi.o main main.c main.o Makefile patsubst 和 filter 函数 filter 函数的使用形式如: $(filter , ) 该函数的功能是,以 模式过滤 字符串中的单词,保留符合 模式的单词。 该函数的返回值是符合 模式的字符串。 patsubst 函数的使用形式如: $(patsubst , , ) 该函数的功能是,查找 中的单词(这些单词以“空格”、“Tab”或“回车”,“换行”分隔)是否符合 中的模式,如果匹配,那么使用 替换。这里, 可以包括通配符 “%” (表示任意长字符串)。如果 中也包含 “%” 符号,那么 中的 “%” 所代表的字符串就是 中的的那个字符串。若要使用 % 字符,那么要使用 ‘\’ 符号进行转义,即 “\%” 。 函数的返回值是替换过的字符串。
init-y := init/ test drivers-y := drivers/ sound/ net-y := net/ test2 libs-y := lib/ core-y := usr/ vmlinux-dirs := $(patsubst %/,%,$(filter %/, $(init-y) $(core-y) $(drivers-y) $(net-y) $(libs-y) )) vmlinux-dirs2 := $(init-y) $(drivers-y) $(net-y) $(libs-y) $(core-y) all: @echo vmlinux-dirs2: $(vmlinux-dirs2) @echo vmlinux-dirs: $(vmlinux-dirs) 运行输出: linux-suse10:~/Makefile_test # make vmlinux-dirs2: init/ test drivers/ sound/ net/ test2 lib/ usr/ vmlinux-dirs: init usr drivers sound net lib 由上面的vmlinux-dirs2 输出可见,所有变量内容都被输出。 对vmlinux-dirs 的输出,则体现了上述两个函数的应用。首先 filter 过滤掉了没有以 / 符号作为结尾的字符串;然后再经过 patsubst 函数过滤掉了所有以 / 符号结尾字符串中的 / 符号。 filter-out -- 反过滤函数 格式: $(filter-out ,)
说明: 以 模式过滤 字符串中的单词,去除符合模式 的单词。返回不符合模式 的字符串。如果 是
一样或者是其子集,那么返回空。 objs = hello.c world.c are.c you.c ok.c new = hello.c world.c are.c you.c ok.c add.c all: @echo $(filter-out $(new), $(objs)) 运行输出时为空。当 new 改为和 objs 一样时,同样输出为空。当 new 改为hello.c world.c are.c you.c 时,输出 ok.c 。 再做一个实验,如果 new 中包含的字符串比 objs 中的还是少一个 ok.c ,但是字符串的顺序和 ojbs 中的不一样,那结果是不是仍然输出 ok.c 呢?答案是一定的!这一无关顺序的“智能”特性比较重要,像在内核 Makefile 的参数检查中(比较新旧编译选项是否一样)就体现了这一点,如在 script/Kbuild.include 中对arg-check 的定义体现了这一点: arg-check = $(strip $(filter-out $(cmd_$(1)), $(cmd_$@)) $(filter-out $(cmd_$@), $(cmd_$(1))) ) origin 函数 -- 告知变量的出生情况 origin 函数的作用是告诉你变量是哪里来的,其出生状况如何,他并不改变变量。其语法是: $(origin ) 上面, 为变量的名字,而不是引用,所以一般没有 $ 字符在前。origin 函数通过返回值来告诉你 的出生情况。下面用实例说明: 1. 当从来未定义过该变量时,origin 函数返回 "undefined" 。如下面的 Makefile 代码: all: @echo $(origin V) 运行输出: $ make undefined 2. 如果该变量为环境变量,那么返回 "enviroment" 。如下面的 Makefile 代码: all: @echo $(origin USER) 运行输出: $ make environment 其中 USER 这个变量为系统定义的当前用户,使用 env 命令可以看到。 3. 如果变量是个默认定义,那么返回 "default"。如下面的 Makefile 代码: all: @echo $(origin CC) 运行输出: $ make
default
4. 如果一个变量被定义在 Makefile 文件中,那么返回 "file" 。如下面的 Makefile 代码: V := 1
all:
@echo $(origin V)
运行输出: $ make file 5. 如果变量来自命令行,那么返回 "command line" 。如下面的 Makefile 代码: all: @echo $(origin MyVar) 运行方法: $ make MyVar="Are you ok?" command line 6. 如果变量被 override 被重新定义过,那么返回 "override"。如下面的 Makefile 代码: verride SHELL = /bin/sh all: @echo $(origin SHELL) 运行输出: $ make
override
上面,SHELL 原本是个环境变量,但在 Makefile 里被 override 指示符重定义过。 7. 如果变量是自动化变量(如 $@, $< 等),那么返回 "automatic" 。如下面的 Makefile 代码: all: @echo $(origin @) 运行输出: $ make automatic ifdef, ifndef, ifeq,
ifneq, 用内核 Makefile 中的一段代码说明: # To put more focus on warnings, be less verbose as default # Use 'make V=1' to see the full commands ifdef V ifeq ("$(origin V)", "command line") KBUILD_VERBOSE = $(V) endif endif ifndef KBUILD_VERBOSE KBUILD_VERBOSE =0 endif 代码中注释的意思是,为了能将精力集中在警告信息上面,默认上不输出详细而显得冗余的编译信息,如果想看到完整的命令执行情况,可以在
make 时使用参数 V=1 。 下面根据代码分析这 4 个符号的作用,实际上它们和 C 语言中的意思是一样的。 # Call a source code checker (by default, "sparse") as part of the # C compilation. # # Use 'make C=1' to enable checking of only re-compiled files. # Use 'make C=2' to enable checking of *all* source files, regardless # of whether they are re-compiled or not. # # See the file "Documentation/sparse.txt" for more details, including # where to get the "sparse" utility. ifdef C ifeq ("$(origin C)", "command line") KBUILD_CHECKSRC = $(C) endif endif ifndef KBUILD_CHECKSRC KBUILD_CHECKSRC =0 endif (M) 选项关于模块的编译: # Use make M=dir to specify directory of external module to build # Old syntax make ... SUBDIRS=$PWD is still supported # Setting the environment variable KBUILD_EXTMOD take precedence ifdef SUBDIRS KBUILD_EXTMOD ?= $(SUBDIRS) endif ifdef M ifeq ("$(origin M)", "command line") KBUILD_EXTMOD := $(M) endif endif 像我们经常编译驱动模块时会使用下面的命令:make -C /lib/modules/`uname -r`/build M=`pwd` modules 上面,使用 pwd 命令给出了要编译的模块所在的路径。 hostprogs-y := 1 hostprogs-m := ifneq ($(hostprogs-y)$(hostprogs-m),) testvar := "defined" endif all: @echo "$(testvar)" 运行输出: $ make defined 如果将上面代码中对 hostprogs-y 的定义设为空,那么输出将为空;而 hostprogs-y 和 hostprogs-m 这两个变量只要有一个味真,那么就认为整个 ifneq 判断成立。 网址:http://www.cnblogs.com/baiyw/p/3303758.html |
|
来自: 写意人生 > 《makefile解读》