Makefile(自动依赖生成)

Tre*_*key 25 dependencies gnu makefile

只是为了快速术语:

#basic makefile rule
target: dependencies
    recipe
Run Code Online (Sandbox Code Playgroud)

问题:我想自动生成依赖项.

例如,我希望转此:

#one of my targets
file.o: file.cpp 1.h 2.h 3.h 4.h 5.h 6.h 7.h 8.h another.h lots.h evenMore.h
    $(COMPILE)
Run Code Online (Sandbox Code Playgroud)

进入:

#one of my targets
file.o: $(GENERATE)
    $(COMPILE)
Run Code Online (Sandbox Code Playgroud)

而且我不太确定它是否可能......

我所知道的:

我可以使用这个编译器标志:

g++ -MM file.cpp
Run Code Online (Sandbox Code Playgroud)

它将返回正确的目标和依赖.
所以从示例中,它将返回:

file.o: file.cpp 1.h 2.h 3.h 4.h 5.h 6.h 7.h 8.h another.h lots.h evenMore.h  
Run Code Online (Sandbox Code Playgroud)

但是,'make'不允许我在规则的目标或依赖部分中显式编写shell代码:(
我知道有一个'make'函数叫做shell

但我不能完全插入这个依赖并解析魔法,因为它依赖于表示目标的宏$ @或者至少我认为这就是问题所在

我甚至尝试用这个makefile函数替换"file.cpp"依赖项,但这也无效.

#it's suppose to turn the $@ (file.o) into file.cpp
THE_CPP := $(addsuffix $(.cpp),$(basename $@))

#one of my targets
file.o: $(THE_CPP) 1.h 2.h 3.h 4.h 5.h 6.h 7.h 8.h another.h lots.h evenMore.h
    $(COMPILE)
#this does not work
Run Code Online (Sandbox Code Playgroud)

所以在谷歌,似乎有两个解决方案.这两点我都没有完全掌握.
来自GNU Make Manual

一些说GNU制作手册的网站已经过时了

所以我的最终问题是:是否有可能按照我想要的方式进行,
如果没有,有人可以从其中一个网站中分解代码并详细向我解释它们是如何工作的.如果必须的话,我将以其中一种方式实现它,但是我很难在将其理解之前将一大块代码粘贴到我的makefile中

tea*_*bob 25

较新版本的GCC具有-MP选项,可与-MD一起使用.我只是将-MP和-MD添加到我的项目的CPPFLAGS变量中(我没有编写用于编译C++的自定义配方)并添加了"-include $(SRC:.cpp = .d)"行.

使用-MD和-MP给出一个依赖文件,其中包括依赖项(不必使用一些奇怪的sed)和虚拟目标(这样删除头文件不会导致错误).

  • 这是另一个关于make auto-dependencies的好页面,其中一个例子使用-MD和-MP http://www.microhowto.info/howto/automatically_generate_makefile_dependencies.html它还与GNU Make Manual中的方法进行了比较,正如所指出的那样,是一个过时的解决方案. (4认同)

Bet*_*eta 19

要在知道依赖项应该是什么时操作文件名,可以使用模式规则:

file.o: %.o : %.cpp 1.h 2.h 3.h 4.h 5.h 6.h 7.h 8.h another.h lots.h evenMore.h
    $(COMPILE)
Run Code Online (Sandbox Code Playgroud)

您可以将规则重用于其他目标:

# Note these two rules without recipes:
file.o: 1.h 2.h 3.h 4.h 5.h 6.h 7.h 8.h another.h lots.h evenMore.h
anotherFile.o: 4.h 9.h yetAnother.h

file.o anotherFile.o: %.o : %.cpp
    $(COMPILE)
Run Code Online (Sandbox Code Playgroud)

但是如果你想让Make自动找出依赖项列表,那么最好的方法(我所知道的)就是Advanced Auto-Dependency Generation.它看起来像这样:

%.o : %.cc
        @g++ -MD -c -o $@ $<
        @cp $*.d $*.P; \
             sed -e 's/#.*//' -e 's/^[^:]*: *//' -e 's/ *\\$$//' \
                 -e '/^$$/ d' -e 's/$$/ :/' < $*.d >> $*.P; \
             rm -f $*.d

-include *.P
Run Code Online (Sandbox Code Playgroud)

基本上,当它构建时file.o,它也会构建file.d.然后它运行file.d一个令人困惑的sed命令,将依赖项列表转换为没有配方的规则.最后一行是对include存在的任何此类规则的指令.这里的逻辑是微妙和巧妙的:你第一次构建时实际上并不需要依赖项foo.o,因为Make已经知道foo.o必须构建,因为它不存在.下次运行Make时,它将使用上次创建的依赖项列表.如果您更改其中一个文件,以便实际上存在一个不在列表中的新依赖项,Make仍将重建,foo.o因为您更改了一个依赖项的文件.尝试一下,它确实有效!

  • @Collin:我发布的内容没有涵盖,但是`%.h :;`规则将解决这个问题.**请注意,我发布的内容已过时**,因为g ++现在具有`-MMD`选项,可以避免使用sed命令. (3认同)

Zoc*_*oum 7

优秀的答案,但在我的构建中,我将.obj文件放在基于构建类型的子目录中(即:debug与r​​elease).因此,例如,如果我正在构建调试,我将所有目标文件放在build/debug文件夹中.试图让上面的多行sed命令使用正确的目标文件夹是一个令人头脑麻木的任务,但经过一些实验,我偶然发现了一个适合我构建的解决方案.希望它也能帮助别人.

这是一个片段:

# List my sources
CPP_SOURCES := foo.cpp bar.cpp

# If I'm debugging, change my output location
ifeq (1,$(DEBUG))
  OBJ_DIR:=./obj/debug
  CXXFLAGS+= -g -DDEBUG -O0 -std=c++0x
else
  CXXFLAGS+= -s -O2 
  OBJ_DIR:=./obj/release
endif

# destination path macro we'll use below
df = $(OBJ_DIR)/$(*F)

# create a list of auto dependencies
AUTODEPS:= $(patsubst %.cpp,$(OBJ_DIR)/%.d,$(CPP_SOURCES))

# include by auto dependencies
-include $(AUTODEPS)

.... other rules

# and last but not least my generic compiler rule
$(OBJ_DIR)/%.o: %.cpp 
    @# Build the dependency file
    @$(CXX) -MM -MP -MT $(df).o -MT $(df).d $(CXXFLAGS) $< > $(df).d
    @# Compile the object file
    @echo " C++ : " $< " => " $@
    @$(CXX) -c $< $(CXXFLAGS) -o $@
Run Code Online (Sandbox Code Playgroud)

现在了解详细信息:我的通用构建规则中第一次执行CXX是有趣的.请注意,我没有使用任何"sed"命令.较新版本的gcc可以完成我需要的一切(我正在使用gcc 4.7.2).

-MM构建主依赖关系规则,包括项目头但不包括系统头.如果我这样离开它,我的.obj文件将没有正确的路径.所以我使用-MT选项指定我的.obj目标的"真实"路径.(使用我创建的"df"宏).
我还使用第二个-MT选项来确保生成的依赖项文件(即:.d文件)具有正确的路径,并且它包含在目标列表中,因此具有与源文件相同的依赖项.

最后但并非最不重要的是包含-MP选项.这告诉gcc还为每个头做出存根规则,解决了如果我删除导致make生成错误的头的问题.

我怀疑,因为我使用gcc来生成所有依赖项而不是使用sed,所以我的构建速度更快(尽管我还没有证明,因为我的构建在这一点上相对较小).如果你看到我可以改进的方法,我总是乐于接受建议.请享用


Tre*_*key 5

作为记录,这是我现在自动生成依赖项的方式:

CPPFLAGS = -std=c++1y -MD -MP 

SRC = $(wildcard *.cpp)
all: main

main: $(SRC:%.cpp=%.o)
    g++ $(CPPFLAGS) -o $@ $^

-include $(SRC:%.cpp=%.d)
Run Code Online (Sandbox Code Playgroud)

编译器标志-MD和-MP可以帮助解决问题。