-
makefile 中 .PYONY 的目的是什么
默认情况下,Makefile目标是「文件目标」- 它们用于从其他文件构建文件. 假设它的目标是一个文件,这使得编写Makefile相对容易.foo: bar create_one_from_the_other foo bar但是,有时你希望
Makefile运行一些命令而不是构建一个在文件系统中的实际文件. 例如常用的目标文件:clean,all.
但有时情况并非如此,你可能在主目录中有一个名为clean的文件,这时候Make会被混淆,因为默认情况下,clean目标将与此文件关联,而Make只会在这个文件根据它的依赖发现不是最新的时候才会执行.这些特殊目标称为「虚假目标」,你可以明确告诉
Make它们与文件无关..PHONY: clean clean: rm -rf *.o现在
make clean将按预期运行,即使你有一个名为clean的文件.就
Make而言,「虚假目标」是一个总是过时的目标,因此无论何时make <pyony_target>, 它都会运行,独立于文件系统的状态.
一些常用的「虚假目标」:all,install,clean,distclean,TAGS,info,check. -
GNU Makefile 变量赋值 =, ?=, := 和 += 之间有什么区别
Lazy Set
VARIABLE = value变量的正常设置 - 当使用变量时,变量会「递归扩展」,而不是在声明变量的时候扩展.
Immediate Set
VARIABLE := value通过简单扩展内部值来设置变量 - 其中的值在声明时扩展.(不会「递归扩展」).
Set If Absent
VARIABLE ?= value如果变量没有定义则设置变量.
Append
VARIABLE += value将提供的值附加到现有的值(如果变量不存在,则设置为该值).
-
从命令行传递其他变量给 make
你有几个选择可以从makefile外部设置变量.-
环境变量 - 每一个环境变量都转化为具有相同名称和值的
makefile变量.
你可能还想设置
-e选项(或者--environments-override),并且你的环境变量会覆盖makefile中的赋值(除非这些赋值本身使用override)
指令. 但是不推荐使用它.
而是推荐使用更好更灵活的?=条件赋值(只有在尚没定义变量时才有效).FOO ?= default_value_if_not_set_in_environment注意,某些变量不是从环境变量继承的
-
MAKE从脚本的名字得到的 -
SHELL要么设置在makefile中,要么默认为/bin/sh
-
从命令行 -
make可以将变量赋值作为命令行的一部分,与目标混合.
make target FOO=bar但是,除非在赋值中使用
override指令,否则忽略makefile中FOO变量的所有值.-
从父 make 导出 - 如果从
makefile调用make, 通常不应该这样显式地写这样的变量赋值
# Don't do this! target: $(MAKE) -C target CC=$(CC) CFLAGS=$(CFLAGS)相反,更好的方法是导出这些变量.
导出变量使其进入每个shell调用的环境,并且使用这些命令将会调用这些环境变量,如上所述.# Do like this CFLAGS=-g export CFLAGS target: $(MAKE) -C target你也可以导出全部变量,直接使用
export不带参数. -
环境变量 - 每一个环境变量都转化为具有相同名称和值的
-
makefile 中 $@ 和 $^ 是什么意思
$@是要生成的目标文件的名称,而$^是第一个先决条件(通常是源文件). 例如:all: library.cpp main.cpp$@相当于all
$<相当于library.cpp
$^相当于library.cpp main.cpp -
makefile:4: *** missing separator. Stop
makefile与制表符有非常愚蠢的关系.
每个规则的所有操作都由制表符标识,而不是4个空格,只有tab才能标识为制表符.
去检查是否使用制表符,使用cat -e -t -v makefile_name.
它显示了带有^I的tab和带有$的行结尾,两者确保了依赖关系正确结束以及制表符标识规则的操作以使它们易于识别为make实用程序起到至关重要的作用.Kaizen ~/so_test $ cat -e -t -v mk.t all:ll$ ## here the $ is end of line ... $ ll:ll.c $ ^Igcc -c -Wall -Werror -02 c.c ll.c -o ll $@ $<$ ## the ^I above means a tab was there before the action part, so this line is ok . $ clean :$ \rm -fr ll$ ## see here there is no ^I which means , tab is not present .... ## in this case you need to open the file again and edit/ensure a tab ## starts the action part -
什么是 Makefile.am 和 Makefile.in
Makefile.am是程序员定义的文件,由automake用于生成Makefile.in文件(.am代表automake).
通常在源码包中可以看到configure脚本使用Makefile.in生成Makefile.configure脚本本身是从名为configure.ac或configure.in(不建议使用)的程序员定义的文件生成的.
我更喜欢.ac(用于autoconf),因为它将与生成的Makefile.in文件区分开来,这样我就可以使用make dist-clean等规则来运行
rm -f *.in.
由于它是生成的文件,因此通常不会存储在版本系统(Git,Svn)中,而是存储在.ac文件中. -
如何在 makefile 中编写 cd 命令
它实际上是执行命令,将目录更改为some_directory, 但是,这是在子进程shell中执行的,并且既不影响make也不影响你正在使用的shell.如果你想在
some_directory中执行更多任务,则需要添加分号并附加其他命令.
请注意,你不能使用换行符,因为它们被make解释为规则的结尾,因此为清晰起见而使用的任何换行都需要通过反斜杠进行转义.all: cd some_dir; echo "I'm in some_dir"; \ gcc -Wall -o myTest myTest.c一个常见的用法是在子目录中调用
make. 这有一个命令行选项,所以你不必自己调用cd.all: $(MAKE) -C some_dir all将更改为
some_dir目录并执行目标为all的Makefile.
作为最佳实践,使用$(MAKE)而不是直接调用make, 因为它会注意调用正确的make实例(例如,如果你为构建环境使用特殊的make版本),并提供使用某些开关(例如,-t)运行时略有不同的行为. -
如何在 makefile 中打印变量
你可以使用此方法(使用名为var的变量)在读取makefile时打印出变量.$(info $$var is [${var}])你可以将此构造添加到任何配方中,以查看将传递给
shell的内容.PYONY: all all: ; $(info $$var is [${var}])echo Hello World现在,这里发生的是将整个配方(
$(info $$var is [${var}])echo Hello World)存储为单个递归扩展变量. 当make决定运行配方时(例如,当你告诉它构建all时),它会扩展变量,然后将每个结果行分别传递给shell.所以,令人懊恼的细节:
- 扩展
$(info $$var is [${var}])echo Hello World - 要做到这点,首先扩展
$(info $$var is [${var}])-
$$扩展成$ -
${var}扩展成:)(取决你变量本身的值) - 副作用是
$var is [:)]出现在标准输出(屏幕)上 -
$(info ...)扩展成空
-
-
Make还剩下echo Hello World-
Make首先输出echo Hello World到标准输出(屏幕),让你知道它要求shell做什么
-
-
shell输出Hello World到标准输出(屏幕)上
- 扩展
-
如何在 makefile 中编写循环
假设你使用的是UNIX类型平台,如果运行./a.out.for number in 1 2 3 4 ; do \ ./a.out $$number ; \ done测试如下
target: for number in 1 2 3 4 ; do \ echo $$number ; \ done输出
1 2 3 4对于更大的范围,使用
target: number=1 ; while [[ $$number -le 10 ]] ; do \ echo $$number ; \ ((number = number + 1)) ; \ done嵌套循环
target: num1=1 ; while [[ $$num1 -le 4 ]] ; do \ num2=1 ; while [[ $$num2 -le 3 ]] ; do \ echo $$num1 $$num2 ; \ ((num2 = num2 + 1)) ; \ done ; \ ((num1 = num1 + 1)) ; \ done输出
1 1 1 2 1 3 2 1 2 2 2 3 3 1 3 2 3 3 4 1 4 2 4 3 -
在 Unix 中,我可以在一个目录中运行 make 而不先 cd 到那个目录吗
make -C /path/to/dir -
如何将命令的输出分配给 Makefile 变量
使用Make内建函数shell, 例如:MY_VAR = $(shell echo whatever)$ make MY_VAR IS whatever$ cat Makefile MY_VAR := $(shell echo whatever) all: @echo MY_VAR IS $(MY_VAR) -
如何配置 makefile 为调试和发布版本
使用特定于目标的变量值,例如CXXFLAGS = -g3 -gdwarf2 CCFLAGS = -g3 -gdwarf2 all: executable debug: CXXFLAGS += -DDEBUG -g debug: CCFLAGS += -DDEBUG -g debug: executable executable: CommandParser.tab.o CommandParser.yy.o Command.o $(CXX) -o output CommandParser.yy.o CommandParser.tab.o Command.o -lfl CommandParser.yy.o: CommandParser.l flex -o CommandParser.yy.c CommandParser.l $(CC) -c CommandParser.yy.c请记住使用
$(CXX)或$(CC)作为编译命令.
然后,make debug将会有额外的标识,如:-DDEBUG和-g, 而make不会有. -
如何获取 Makefile 的当前相对目录
你可以使用shell函数:current_dir = $(shell pwd).
或者你需要绝对路径,可以shell结合notdir使用,current_dir = $(notdir $(shell pwd)).
上面的方法,仅make在Makefile当前目录执行有效.注意: 这将返回当前工作目录,而不是
Makefile的父目录.
例如,如果运行cd /; make -f /home/username/project/Makefile,current_dir返回的是/而不是/home/username/project/以下代码适用于任何目录调用的
Makefilemkfile_path := $(abspath $(lastword $(MAKEFILE_LIST))) current_dir := $(notdir $(patsubst %/,%,$(dir $(mkfile_path)))) -
如果没有指定目标,make 如何知道要构建的默认目标
默认情况下,make首先处理不以.开头的第一个目标(又叫「默认目标」).
也可以使用.DEFAULT_GOAL指定默认目标(make<= 3.80 将不起作用).DEFAULT_GOAL := mytarget










网友评论