Makefile:无法获得使用patsubst的basename

时间:2014-10-18 23:15:45

标签: c++ makefile

我的Makefile:

compiler=g++
cflags=-g -Wall -I.
src=$(shell find . -name *.cc) #find all .cc files, with path name
srcBaseName=$(shell basename -a $(src)) # extract base names by stripping off the path
header=$(shell find . -name *.h)    # all header files
obj=$(patsubst %.cc, %.o, $(srcBaseName)) # Problematic line
bin=bin/myProgram

all:    $(bin)

$(bin): $(obj)
    $(compiler) $^ -o $@

%.o:    %.cc
    $(compiler) $(cflags) -c $^ -o $@


clean:
    rm $(obj) $(bin)

导致以下错误:

  

make:***没有规则要制作目标' SomeObjectFile.o',需要   '箱/ myProgram&#39 ;.停止。

有问题的一行:

obj=$(patsubst %.cc, %.o, $(srcBaseName))

如果我将$(srcBaseName)更改为$(src),那么一切都很好。但在这种情况下,.o文件将分散在相应的文件夹中,其中包含我不想要的.cc文件。

我希望有一个专用的(obj /)文件夹来存储所有.o文件。

我该怎么做?

首先尝试:

obj=$(patsubst %.cc, %.o, obj/$(srcBaseName))

第二次尝试:

obj=$(patsubst %.cc, %.o, obj\/$(srcBaseName))

他们为什么不工作?

/ ********************** 2015年2月16日编辑******************* *** /

根据答案中的建议,我将Makefile更新为:

compiler=g++

# source compilation flags
cflag=-g -Wall -std=gnu++0x -I./header/
# source link flags
lflag=
# lib used by proj
lib=


tflag=-g -Wall -std=gnu++0x
# test link flags
tlflag=
# test libs
testLib=lib/libgtest.a

# source code
src=$(shell find "./src" -name "*.cc")
srcBaseName=$(shell basename -a $(src))
obj=$(addprefix obj/, $(patsubst %.cc, %.o, $(srcBaseName)))
vpath %.cc $(dir $(src))

# header files
header=$(shell find "./header" -name "*.h")

# test files
testSrc=$(shell find "./test" -name "*.cc")
testSrcBase=$(shell basename -a $(testSrc))
testObj=$(addprefix obj/, $(patsubst %.cc, %.o, $(testSrcBase)))
vpath %.cc $(dir $(testSrc))

# binary files
bin=bin/Driver
testBin=bin/Test





all: prog test


prog: $(bin)
$(bin): $(obj)
    $(compiler) $(lflag) $^ $(lib) -o $@

#$(obj): $(src) $(header)
obj/%.o: %.cc $(header)
    $(compiler) $(cflag) -c $< -o $@



test: $(testBin)

$(testBin): $(testObj)
    $(compiler) $(tlflag) $^ $(testLib) -o $@

obj/%.o: %.cc
    $(compiler) $(tflag) -c $< -o $@




clean:
    rm $(obj) $(bin) $(testObj) $(testBin)

这是制作背后的意图:

制作编:

make应找到./src目录下的所有源文件(.cc),并在./obj目录中生成一个文件名相同的.o文件,对子目录的级别不敏感,以便我可以自由添加新的cc文件,无需更新Makefile。每个.o文件都取决于相应的(只是具有相同名称的文件,而不是全部).cc文件和所有标题( make不会自动知道cc文件包含哪些头文件而不解析文件;如果你有一个聪明的方法来实现这一点,请告诉我! )。例如,。/ src / sububirectory1 / sample1.cc应生成./obj/sample1.o和./obj/sample1.o取决于./src/subdirectory1/sample1.cc + ./header/sample1.h +。 /header/sample2.h + ...

进行测试:

它应该对./test文件夹中的测试源文件做类似的事情,除了没有涉及头文件。如果这个细节有帮助,我会使用Google测试。

但是,我的Makefile并没有按照预期的方式工作,因为它有以下问题:

1,如果我运行make test,则不会执行配方$(compiler) $(tflag) -c $< -o $@(tflag表示&#39;测试编译标记&#39;,它没有-I./ header / part; cflag表示&#39;源代码编译标志&#39;,它有-I./header/部分)。相反,执行假冒进程$(compiler) $(cflag) -c $< -o $@中的配方。这个观察来自于输出,其中&#39; -I。/ header /&#39;出现。我猜这是因为假冒测试中的cflag模式规则会覆盖虚假测试中的tflag一个?我依稀记得make picks是最匹配的模式规则 - 两者基本上是相同的(我的意图是当执行伪造时,特定假冒下的模式规则应该被执行,这似乎不可行? ),所以make总会选择第一个。通过交换Makefile中两个模式规则的顺序来验证这个结论,这次tflag规则总是被选中。 所以,一个自然要问的问题是,当手机执行时,如何在特定的假冒条件下执行模式规则?

2,假设在第1点做我想做的事情是不可行的,我开始考虑替代方案。我可以执行以下操作:#$(obj): $(src) $(header)以便我可以摆脱模式规则来解决模式规则。但是,这显然是不正确的,因为它说,$(obj)中的每个.o文件都依赖于所有src文件和所有头文件。 这是一个正确的方向吗?

非常感谢,期待收到您的回复。

以粗体和斜体突出显示了3个关键问题。

3 个答案:

答案 0 :(得分:1)

你的问题就在这一行:

%.o: %.cc

该行告诉make要创建一些/ path / file.o,你将使用一些/ path / file.cc。

如果您希望将所有.o文件放在一个目录中,但仍希望将源文件放在不同的目录中,则每个源目录都需要一个这样的规则。或者,您可以将所有目录添加到VPATH变量中,例如:

VPATH=$(dir $(src))

或者更好:

VPATH=$(dir $(SRC))

在Makefile中为变量使用大写字母是避免将它们与函数名混淆的好方法。

答案 1 :(得分:1)

我会尽力回答你的新问题:

1)当您使用gnu编译器时,可以在.c文件中自动查找.h文件的依赖项。您可以将这样的规则添加到Makefile:

# Where .d-files will be created for dependencies
DEP=dep

# Dependency files
DEPS = $(srcBaseName:%.cc=$(DEP)/%.d)

# Before compiling object file, also make sure dependency file is
# created to test the need for future recompilation
obj/%.o:    %.cc $(DEP)/%.d
    $(compiler) $(cflags) -c $< -o $@

# Use gnu compiler to create dependency files
$(DEPS): $(DEP)/%.d: %.cc $(filter-out $(wildcard $(DEP)), $(DEP))
    $(compiler) -MM $(cflags) -MT $@ $< > $@

# Create directories which might be needed
$(DEP) $(OBJ) $(BIN) $(MO):
    mkdir -p $@

# Let Makefile use any generated dependency files
ifneq ($(wildcard $(DEPS)),)
include $(wildcard $(DEPS))
endif

请注意,在编译规则中,我将$^替换为$<,因为我们不想编译依赖项文件。

2)我会避免两个看起来相同的模式规则。相反,我会根据目标更改cflag,如下所示:

ifeq ($(MAKECMDGOALS),debug)
CFLAGS += -g
else
CFLAGS += -O2
endif

我希望这些答案会引导你朝着正确的方向前进

答案 2 :(得分:0)

主要问题是make无法使用模式规则

%.o:    %.cc
    $(compiler) $(cflags) -c $^ -o $@

构建obj,因为它无法在.o文件和.cc文件之间找到与%匹配的公共词干。解决此问题的一种简单方法是通过make指令告诉vpath这些文件的位置,例如通过添加

vpath %.cc $(dir $(src))
vpath %.o obj/      #not a good idea for .o files though, see below

并将模式规则(使用vpath %.o所需)更改为

%.o:    %.cc
    $(compiler) $(cflags) -c $^ -o obj/$@

编辑:MadScientist提出了一个非常好的观点,我完全错过了,基于此,一个不涉及vpath %.o的更好的解决方案

vpath %.cc $(dir $(src))
obj=$(addprefix obj/,$(patsubst %.cc, %.o, $(srcBaseName)))
obj/%.o:    %.cc
    $(compiler) $(cflags) -c $^ -o $@