Makefile(自动依赖生成)

时间:2011-11-06 07:28:30

标签: makefile dependencies gnu

仅用于快速术语:

#basic makefile rule
target: dependencies
    recipe

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

例如,我希望能够解决这个问题:

#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)

进入这个:

#one of my targets
file.o: $(GENERATE)
    $(COMPILE)

我不太确定是否可能......

我所知道的:

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

g++ -MM file.cpp

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

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  

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

的'make'函数

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

我甚至尝试用这个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

所以遍布谷歌,似乎有两个解决方案。这两点我都没有完全掌握 From GNU Make Manual

Some Site that says the GNU Make Manual one is out-of-date

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

8 个答案:

答案 0 :(得分:25)

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

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

答案 1 :(得分: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)

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

# 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)

但是如果你想让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

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

答案 2 :(得分: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 $@

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

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

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

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

答案 3 :(得分:5)

对于记录,这就是我现在自动生成依赖关系的方式:

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

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

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

-include $(SRC:%.cpp=%.d)

编译器标志-MD和-MP有助于实现这一目的。

答案 4 :(得分:4)

首先,您可以拥有THE_CPP=$(patsubst %.o,%.cpp,$@)

然后,您可以运行make -p来了解make

的内置规则

通常的做法是将makefile依赖项生成为*.md个文件:

%.o: %.c
       $(COMPILE.c) $(OUTPUT_OPTION) $< -MMD -MF $(patsubst %.c,%.md,$@)

以及稍后在您的Makefile including them中使用类似

的内容
-include $(wildcard *.md)

但您也可以考虑使用其他构建器,如omake和许多其他构建器

答案 5 :(得分:1)

WOOO!我确实设法在Beta的帖子中获得了一个小型测试项目的代码 我应该注意,对于任何可能遇到此事的人, 如果你正在使用bash shell(我是),你需要在井号前面添加一个转义字符,以避免使表达式的其余部分成为注释。 (见第4行代码)

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

现在我想分享我在Managing Projects with GNU Make, 3rd Edition.中找到的信息,因为它指出了这个问题上的一些重要问题,并提供了我还没有完全掌握的代码。
书中出现的方法与Make manual page上的方法类似 它看起来像这样:

include $(subst .c,.d,$(SOURCES))

%.d: %.c
    $(CC) -M $(CPPFLAGS) $< > $@.$$$$; \
    sed 's,\($*\).o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; \
    rm -f $@.$$$$

这就是我认为正在发生的事情 马上,'make'想要为每个源文件包含一个“.d”文件 因为最初不存在.d文件,所以一次又一次地运行代码块以创建所有丢失的.d文件。
这意味着make会一次又一次地重复,直到每个.d文件都被创建并包含在makefile中 每个“.d”文件都是Beta所说的:一个具有一组依赖关系和无配方的目标。

如果更改了头文件,那么包含在其中的那些规则将需要首先更新依赖项。这就是让我失望的原因,如何才能再次调用代码块?它用于更新.d文件,因此如果.h文件改变了它的调用方式?除此之外,我意识到默认规则用于编译对象。对此解释的任何澄清/误解都表示赞赏。


本书后面指出了这种方法的问题,我认为在高级自动依赖生成实现中也存在问题。
问题1:效率低下。 'make'必须在每次创建.d文件时重新启动 问题2: make为所有丢失的.d文件生成警告消息 - 这主要是一种麻烦,可以通过在include语句前添加“ - ”来隐藏。
问题3:如果删除src文件因为不再需要它,那么下次尝试编译时'make'会崩溃,因为某些.d文件缺少src作为依赖项,并且因为那里没有规则重新创建那个src,make会拒绝继续下去。

他们说这些问题的解决方法是Tromey的方法,但代码看起来与网站上的代码截然不同。也许只是因为他们使用了一些宏,使它成为一个函数调用,并且写得略有不同。我还在调查,但想分享一些我迄今为止所做的一些发现。希望这会打开一个一点点更多讨论,让我更接近所有这一切的底部。

答案 6 :(得分:1)

我更喜欢使用find的$(shell ...)函数。以下是我的一个Makefile的示例:

SRCDIR = src
OBJDIR = obj
LIBDIR = lib
DOCDIR = doc

# Get Only the Internal Structure of Directories from SRCDIR
STRUCTURE := $(shell find $(SRCDIR) -type d)

#Filter-out hidden directories
STRUCTURE := $(filter-out $(shell find $(SRCDIR)/.* -type d),$(STRUCTURE))

# Get All Files From STRUCTURE
CODEFILES := $(addsuffix /*,$(STRUCTURE))
CODEFILES := $(wildcard $(CODEFILES))


## Filter Only Specific Files
SRCFILES := $(filter %.c,$(CODEFILES))
HDRFILES := $(filter %.h,$(CODEFILES))
OBJFILES := $(subst $(SRCDIR),$(OBJDIR),$(SRCFILES:%.c=%.o))
DOCFILES := $(addprefix $(DOCDIR)/,             \
            $(addsuffix .md,                    \
            $(basename $(SRCFILES))))


# Filter Out Function main for Libraries
LIBDEPS := $(filter-out $(OBJDIR)/main.o,$(OBJFILES))

在这种方法中,我首先获得所有内部目录结构,具有任何深度。然后我获取Structure中的所有文件。这时,我可以使用filter,filter-out,addsuffix等来获得我每次都需要的东西。

此示例涵盖* .c文件,但您也可以将其更改为* .cpp。

答案 7 :(得分:0)

在上一篇文章的评论中引用的Auto-Dependency Generation文章的内容的基础上,我创建了一个annotated makefile project,其中包含一个带有注释的generic Makefile,并实现了一个简单的项目3个.c文件和2个.h文件。请参阅下面的完整Makefile内容。简单的项目应该能够只自定义TODO部分

# See http://make.mad-scientist.net/papers/advanced-auto-dependency-generation/
# for the template used to start this file

# -- TODO: customize the list below for your project ---
# List of source .c files used with the project
SRCS := main.c file1.c file2.c

# The aplication generated 
APPNAME = depend-generation-test
# -- End of customization section ---

# Replace .c extension on SRCS to get objfiles using gnu make pattern rules and substitution references.
# See https://www.gnu.org/software/make/manual/html_node/Pattern-Intro.html#Pattern-Intro for pattern rules and 
# https://www.gnu.org/software/make/manual/html_node/Substitution-Refs.html#Substitution-Refs for substitution references overview
OBJFILES := $(SRCS:%.c=%.o)

# Build the app you've specified in APPNAME for the "all" or "default" target
all : $(APPNAME)
default : $(APPNAME)

# Remove all build intermediates and output file
clean : ; @rm -rf $(APPNAME) *.o

# Build the application by running the link step with all objfile inputs
$(APPNAME) : $(OBJFILES)
    $(CC) $(LDFLAGS) $^ -o $(APPNAME)

# Add all warnings/errors to cflags default.  This is not required but is a best practice
CFLAGS += -Wall -Werror

# The below content is from  http://make.mad-scientist.net/papers/advanced-auto-dependency-generation/
# with the following changes:
#   1) Added comments
#   2) Removed TARGET_ARCH from COMPILE.c since it's no longer listed in the [default rules](https://www.gnu.org/software/make/manual/html_node/Catalogue-of-Rules.html#Catalogue-of-Rules) and [isn't documented](https://lists.gnu.org/archive/html/help-make/2010-06/msg00005.html)
# Original content below is:
# Copyright © 1997-2019 Paul D. Smith Verbatim copying and distribution is permitted in any medium, provided this notice is preserved.

# The directory (hidden) where dependency files will be stored
DEPDIR := .deps
# Flags passed to gcc to automatically build dependencies when compiling
# See https://www.gnu.org/software/make/manual/html_node/Automatic-Variables.html for detail about variable names
# $@ references the target file of the rule and will be "main.o" when compiling "main.c"
# $* references the stem of the rule, and will be "main" when target is "main.o"
DEPFLAGS = -MT $@ -MMD -MP -MF $(DEPDIR)/$*.d

# Rules for compiling a C file, including DEPFLAGS along with Implicit GCC variables.
# See https://www.gnu.org/software/make/manual/html_node/Implicit-Variables.html
# and see https://www.gnu.org/software/make/manual/html_node/Catalogue-of-Rules.html#Catalogue-of-Rules
# for the default c rule
COMPILE.c = $(CC) $(DEPFLAGS) $(CFLAGS) $(CPPFLAGS) -c

# Delete the built-in rules for building object files from .c files
%.o : %.c
# Define a rule to build object files based on .c or dependency files by making the associated dependency file
# a prerequisite of the target.  Make the DEPDIR an order only prerequisite of the target, so it will be created when needed, meaning
# the targets won't get rebuilt when the timestamp on DEPDIR changes
# See https://www.gnu.org/software/make/manual/html_node/Prerequisite-Types.html for order only prerequesites overview.
%.o : %.c $(DEPDIR)/%.d | $(DEPDIR)
    $(COMPILE.c) $(OUTPUT_OPTION) $<

# Create the DEPDIR when it doesn't exist
$(DEPDIR): ; @mkdir -p $@

# Use pattern rules to build a list of DEPFILES
DEPFILES := $(SRCS:%.c=$(DEPDIR)/%.d)
# Mention each of the dependency files as a target, so make won't fail if the file doesn't exist
$(DEPFILES):

# Include all dependency files which exist, to include the relevant targets.
# See https://www.gnu.org/software/make/manual/html_node/Wildcard-Function.html for wildcard function documentation
include $(wildcard $(DEPFILES))