Jef*_*eff 11 makefile gnu-make
我有以下简化的makefile,我正在尝试根据不同的目标设置不同的路径.不幸的是,我没有得到我期望的结果.这是make版本3.81.
.SECONDEXPANSION:
all: Debug32
# Object directory set by target
Debug32: OBJDIR = objdir32
#OBJDIR = wrongdirectory
# ObjDir is empty here. :(
OBJS = $(addprefix $(OBJDIR)/,DirUtil.o)
$(OBJDIR)/%.o : %.cpp
echo Compile: $@
Debug32: $(OBJS)
$(OBJS): | $(OBJDIR)
$(OBJDIR):
echo mkdir $(OBJDIR) - $@
Run Code Online (Sandbox Code Playgroud)
结果如下,没有设置OBJDIR:
echo Compile: /DirUtil.o
Run Code Online (Sandbox Code Playgroud)
如果我取消注释"OBJDIR = wrongdirectory"行,我将得到以下结果,这是令人困惑的,因为我看到变量的两个值,我认为我应该只看到一个:
echo mkdir objdir32 - wrongdirectory -
echo Compile: wrongdirectory/DirUtil.o
Run Code Online (Sandbox Code Playgroud)
我假设变量在我认为应该扩展时没有扩展,但我无法弄清楚如何改变这种行为.
Gav*_*ith 14
从GNU信息手册
make文件的所有部分中的变量和函数在读取时都会扩展,除了配方,使用'='的变量定义的右侧,以及使用'define'指令的变量定义的主体.
特定于目标的变量仅适用于配方.内
$(OBJS): | $(OBJDIR)
Run Code Online (Sandbox Code Playgroud)
和
$(OBJDIR):
Run Code Online (Sandbox Code Playgroud)
它正在获得全局变量.
因此,当您运行时会发生什么make Debug32,它会将内容OBJS视为先决条件,从而导致上面的第一条规则.$(OBJDIR)已被全局值替换,这与第二个规则中的target-name匹配,该规则也以相同的方式替换.
但是,当我们得到食谱时:
echo mkdir $(OBJDIR) - $@
Run Code Online (Sandbox Code Playgroud)
$(OBJDIR) 尚未被替换,因此它获取特定于目标的变量值.
一个工作版本
.SECONDEXPANSION:
all: Debug32
# Object directory set by target
Debug32: OBJDIR = objdir32
OBJDIR = wrongdirectory
Debug32: OBJS = $(addprefix $(OBJDIR)/,obj.o)
OBJS = wrongobjs
Debug32: $$(OBJS)
echo OBJS are $(OBJS)
echo OBJDIR is $(OBJDIR)
%/obj.o: | %
touch $@
OBJDIRS = objdir32 wrongdirectory anotherdirectory
$(OBJDIRS):
# mkdir $(OBJDIR)
mkdir $@
Run Code Online (Sandbox Code Playgroud)
主要变化是$$在这一行中使用:
Debug32: $$(OBJS)
Run Code Online (Sandbox Code Playgroud)
只有一个$,我收到错误信息
make: *** No rule to make target `wrongobjs', needed by `Debug32'. Stop.
Run Code Online (Sandbox Code Playgroud)
然而,随着$$,我得到
echo OBJS are objdir32/obj.o
OBJS are objdir32/obj.o
echo OBJDIR is objdir32
OBJDIR is objdir32
Run Code Online (Sandbox Code Playgroud)
使用辅助扩展允许在先决条件中访问特定于目标的变量.
另一个变化是我创建OBJS了一个特定于目标的变量(因为它是).为了建立一个规则来构建OBJS它的价值,我不得不使用模式规则:
%/obj.o: | %
Run Code Online (Sandbox Code Playgroud)
为避免每个目标文件都有单独的行,您可以执行以下操作:
OBJ_BASENAMES=obj.o obj2.o obj3.o
$(addprefix %/,$(OBJ_BASENAMES)): | %
touch $@ # Replace with the proper recipe
Run Code Online (Sandbox Code Playgroud)
包含addprefix宏的行扩展为
%/obj.o %/obj2.o %/obj3.o: | %
Run Code Online (Sandbox Code Playgroud)
然后运行首先make anotherdirectory/obj2.o创建一个名为"anotherdirectory"的目录,并在其中创建一个名为"obj2.o"的文件.
请注意,必须列出所有可能的目录OBJDIRS.无法收集OBJDIR的所有特定于规则的值,因此列出它们是最佳选择.替代方案是% :构建任何目录的规则,该目录能够匹配和构建任何目标,这可能是有风险的.(如果放弃使用特定于目标的变量,还有另一种获取可以构建的目录列表的方法:使用具有可预测名称的变量Debug32_OBJDIR,并使用make函数生成其值列表.)
或者,不需要列出目标文件的通用规则:
SOURCE=$(basename $(notdir $@)).cpp
DIR=$(patsubst %/,%,$(dir $@))
%.o: $$(SOURCE) | $$(DIR)
touch $@ # Replace with proper recipe
Run Code Online (Sandbox Code Playgroud)
没有功能可以在每个目标的上下文中读取规则,替换目标特定变量并为每个目标获取新规则.不能使用特定于目标的变量以这种方式编写通用规则.