简体   繁体   English

Gnu Makefile - 处理依赖项

[英]Gnu Makefile - Handling dependencies

What approach do C++ programmers on Unix platform use to create and manage Makefiles? Unix平台上的C ++程序员使用什么方法来创建和管理Makefile?

I was using hand made Makefiles for my projects but they don't handle header file changes and other dependencies. 我在我的项目中使用手工制作的Makefile,但它们不处理头文件更改和其他依赖项。 I googled around and found a good solution here . 我google了一下,在这里找到了一个很好的解决方案。

But I ran into a problem here in the sed command - 但是我在sed命令中遇到了一个问题 -

    sed -e 's/#.*//' -e 's/^[^:]*: *//' -e 's/ *\\$$//' \
        -e '/^$$/ d' -e 's/$$/ :/' < $*.d >> $*.P; \

The problem is with the 3rd expression "-e 's/ *\\$$//'. It doesn't work. Its supposed to remove trailing backslashes. I understand that there has to be double dollar there since this is part of a Makefile. Can someone tell me what wrong here? 问题在于第3个表达式“-e's / * \\ $$ //”。它不起作用。它应该删除尾随反斜杠。我知道必须有双倍的美元,因为这是一个Makefile。有人能告诉我这里有什么问题吗?

Here's the complete Makefile - 这是完整的Makefile -

CC=g++
CFLAGS=-g -Wall
LIBS=-lpthread

OBJS=file1.o file2.o
TARGET=testProg

$(TARGET) : $(OBJS)
        $(CC) -o $@ $^ $(CFLAGS) $(LIBS)

%.o : %.cpp
        $(CC) -MMD -c -o $@ $< $(CFLAGS)
        @cp $*.d $*.P; \
            sed -e 's/#.*//' -e 's/^[^:]*: *//' -e 's/ *\\$$//' \
                -e '/^$$/ d' -e 's/$$/ :/' < $*.d >> $*.P; \
            rm -f $*.d

-include $(OBJS:%.o=%.P)

clean :
        rm -f $(TARGET) $(OBJS)

all : $(TARGET)

Apart from the solution to this problem, I would also like some tips/pointers to my 1st question. 除了解决这个问题之外,我还想了解第一个问题的一些提示/指示。

gcc/g++ can generate dependencies for you with the -M family of options. gcc / g ++可以使用-M系列选项为您生成依赖项。 The following works by specifying how to generate .depends files given a source file. 以下工作通过指定如何在给定源文件的情况下生成.depends文件。 By doing -include $(DEPS) $(DEPS) is recognized as a target and will be built/rebuilt when the source files change. 通过执行-include $(DEPS) $(DEPS)被识别为目标,并将在源文件更改时构建/重建。

CXX      = g++
CXXFLAGS = -Wall -O3
LDFLAGS  =

TARGET = testcpp
SRCS   = main.cc x.cc foo.cc
OBJS   = $(SRCS:.cc=.o)
DEPS   = $(SRCS:.cc=.depends)


.PHONY: clean all

all: $(TARGET)

$(TARGET): $(OBJS)
        $(CXX) $(CXXFLAGS) $(LDFLAGS) $(OBJS) -o $(TARGET)

.cc.o:
        $(CXX) $(CXXFLAGS) -c $< -o $@

%.depends: %.cc
        $(CXX) -M $(CXXFLAGS) $< > $@

clean:
        rm -f $(OBJS) $(DEPS) $(TARGET)

-include $(DEPS)
  1. I use that approach too and can't praise it highly enough. 我也使用这种方法 ,不能高度赞扬它。 And I write my makefiles by hand and reuse them a lot on new projects. 我手工编写makefile并在新项目中重复使用它们。
  2. .The expression "s/ *\\\\$//" will work outside the context of Make. 。表达式“s / * \\\\ $ //”将在Make的上下文之外工作。 Within a makefile it doesn't work because Make tries to interpret "$/" before handing the result to the shell. 在makefile中它不起作用,因为Make在将结果传递给shell之前尝试解释“$ /”。 So you must use "s/ *\\\\$$//" (note the extra $) within the makefile, but this won't work outside the context of Make (so testing it is a slight pain). 所以你必须在makefile中使用“s / * \\\\ $$ //”(注意额外的$),但这不会 Make的上下文之外工作(所以测试它是一个轻微的痛苦)。



EDIT: 编辑:

I've tried your makefile, and that sed statement seems to remove trailing backslashes just fine. 我已经尝试过你的makefile,那个sed语句好像删除尾部反斜杠就好了。 Try something simpler, like this: 尝试一些更简单的东西,比如:

backslash:
    @echo " \\" > $@

test: backslash
    @echo without sed:
    @cat backslash
    @echo with sed:
    @sed -e 's/ *\\$$//' < backslash



EDIT: All right, now I'm hooked. 编辑:好的,现在我迷上了。 Could you try these experiments and tell us the results? 你能试试这些实验并告诉我们结果吗?

Change the last character to 'z'      :  s/.$/z/
Change a trailing backslash to 'z'    :  s/\\$/z/
Change a trailing backslash to 'z'    :  sm\\$mzm
Delete a trailing backslash           :  s/\\$//
Delete spaces and a trailing backslash:  s/ *\\$//
Try all of these inside and outside of Make, with '$' and '$$'.

In a make file anything you list on the dependency line is a dependency header files or other files included. 在make文件中,您在依赖行上列出的任何内容都是依赖项头文件或包含的其他文件。

BSD tutorial on make Note: you can auto generate header dependency info with the -MM switch of GCC. 有关生成的BSD教程注意:您可以使用GCC的-MM开关自动生成标头依赖关系信息。

I must be missing something. 我肯定错过了什么。 Why doesn't generating dependency files work for you? 为什么不生成依赖文件对您有用?

I prefer to use CMake, even though it's not strictly the solution to your problem. 我更喜欢使用CMake,尽管它不是严格解决问题的方法。

It's a project description language that'll generate your Makefiles, Visual Studio Project, Eclipse Project, KDevelop, etc for you. 它是一种项目描述语言,可以为您生成Makefile,Visual Studio项目,Eclipse项目,KDevelop等。 All the dependencies are done for you: 所有依赖项都为您完成:

CMakeLists.txt 的CMakeLists.txt

add_executable(my_exe file1.c file2.c)
target_link_libraries(my_exe my_library)
add_subdirectory(lib)

In lib/CMakeLists.txt 在lib / CMakeLists.txt中

add_library(my_library file3.c file4.c)

This creates a my_exe from file1.c file2.c linked against my_library. 这将从file1.c file2.c创建一个my_exe,链接到my_library。 I find this much simpler. 我发现这更简单了。 It also has things like package discovery: 它还有包发现之类的东西:

find_package(Qt4)

The makedepend utility is installed on many systems and can be quite useful for generating dependency information. makedepend实用程序安装在许多系统上,对于生成依赖项信息非常有用。

Here is an example Makefile that uses the include directive (plus a little Perl magic) to incorporate the output from makedepend: 下面是一个示例Makefile,它使用include指令(加上一点Perl魔术)来合并makedepend的输出:

# the name of the executable that we'll build
TARGET = foo_prog
# our .cc source files
SRCS = foo.cc main.cc

# the .o versions of our source files
OBJS := $(patsubst %.cc, %.o, $(filter %.cc, $(SRCS)))
# some flags for compiling
CXXFLAGS = -Wall -Werror

# In order to build $(TARGET), we first build each of the $(OBJS).
# Then we use the given command to link those $(OBJS) into our
# $(TARGET) executable.  $^ is a shortcut for $(OBJS).  $@ is a
# shortcut for $(TARGET).
#
# The default compile rule will compile each of the $(OBJS) for us.
$(TARGET): $(OBJS)
        $(CXX) $(CXXFLAGS) $^ -o $@

# Use "make clean" to remove all of the support files.
clean:
        rm -f $(OBJS) $(TARGET) Makefile.depend *~

# This automatically uses the 'makedepend' utility to add any
# dependencies that our source files have, namely .h files.  This way,
# if the .h files change, the code will be re-compiled.
include Makefile.depend
Makefile.depend: $(SRCS)
        makedepend -f- -Y $(SRCS) 2> /dev/null | \
        perl -p -e "s/(^.*?:)/Makefile.depend \1/" > Makefile.depend

If both foo.cc and main.cc depend on foo.h , then the contents of Makefile.depend would be: 如果foo.ccmain.cc依赖于foo.h ,那么Makefile.depend的内容将是:

Makefile.depend foo.o: foo.h
Makefile.depend main.o: foo.h

The end result is that the dependency information from makedepend is injected into the Makefile as a series of rules. 最终结果是makedepend的依赖信息作为​​一系列规则被注入Makefile。 It's similar to the approach of using a .d file for each .cc file , but keeps the dependency information in one file instead of scattered all over the place. 它类似于为每个.cc文件使用.d文件的方法 ,但是将依赖关系信息保存在一个文件中而不是散布在整个地方。

In Mozilla's build system, we use GCC's -MD switch to generate the dependency files: http://mxr.mozilla.org/mozilla-central/source/configure.in#7134 and then we use a script called mddepend.pl to check for removed header files, such that removing a header simply causes a rebuild, not an error: http://mxr.mozilla.org/mozilla-central/source/config/rules.mk#2066 http://mxr.mozilla.org/mozilla-central/source/build/unix/mddepend.pl 在Mozilla的构建系统中,我们使用GCC的-MD开关来生成依赖文件: http ://mxr.mozilla.org/mozilla-central/source/configure.in#7134然后我们使用一个名为mddepend.pl的脚本来检查用于去除头文件,使得去除的报头简单地导致重建,而不是错误: http://mxr.mozilla.org/mozilla-central/source/config/rules.mk#2066 HTTP://mxr.mozilla。组织/ Mozilla的中央/源极/构建/ UNIX / mddepend.pl

That script generates an .all.pp file containing all the dependencies, with extra foo.o: FORCE dependencies stuck in for missing header files. 该脚本生成一个包含所有依赖项的.all.pp文件,其中包含额外的foo.o: FORCE依赖项,用于丢失头文件。 We then simply -include the .all.pp file in rules.mk right below there. 然后我们简单地在下面的rules.mk中包含.all.pp文件。

即使该项目未使用Qt,您也可以使用qmake为项目生成Makefile。

I use BSD make (pmake?) which does lot of work for me (my lang is C, but I think no difference here). 我使用BSD make(pmake?)为我做了很多工作(我的郎是C,但我觉得这里没什么区别)。 This is my common 'local.prog.mk', I never change it: 这是我常见的'local.prog.mk',我永远不会改变它:

.PHONY: tags .depend

# .depend depends only on $(SRCS) in bsd.dep.mk, so we can't track changes of
# header's own dependencies properly. so .depend is .PHONY target here.

CSTD    ?=c99
WARNS   ?=9
.if !empty(PC_LIST)
PC_CF   !=pkg-config --cflags $(PC_LIST)
PC_LD   !=pkg-config --libs   $(PC_LIST)
.endif
CFLAGS  +=$(PC_CF) -fgnu89-inline
.if !defined(NO_DEBUG)
CFLAGS  +=-O0 -ggdb3
.endif
LDFLAGS +=$(PC_LD)
CTAGS   =exctags

NO_MAN=
NO_OBJ=
CLEANFILES+=$(PROG).core

.include <bsd.prog.mk>
$(PROG): $(SUBDIR)
build: clean cleandepend depend all
run: $(PROG)
    ./$(PROG)

Note 'bsd.prog.mk' inclusion -- this handles all, build, depend, clean targets. 注意'bsd.prog.mk'包含 - 它处理所有,构建,依赖,清理目标。 Project-specific BSDmakefile s are simple: 特定BSDmakefile项目的BSDmakefile很简单:

.SILENT:

PROG    =hello
SRCS    =hello.c world.c
PC_LIST =gtk+-2.0 gnet-2.0

.include "../local.prog.mk"

proto:
    cproto -siv `pkg-config --cflags $(PC_LIST)` $(SRCS) > prototypes
CLEANFILES+=prototypes

I just make depend every time I insert/remove any #include directives. 我每次插入/删除任何#include指令时都会依赖。

使用更现代版本的GCC,您可以添加-MP标志以使GCC为标头本身生成空规则。

Instead of the sed scripts, use gcc's -MT option to modify the target of the generated dependency rules. 使用gcc的-MT选项来修改生成的依赖关系规则的目标,而不是sed脚本。 This blog post has more info. 这篇博文有更多信息。

I top tip that I have found useful when building dependency files is to include the dependency file as a target in the generated rule: 我建议依赖文件将依赖文件作为生成规则中的目标包含在内时,我发现有用的顶尖提示:

file.d file.o : file.c header.h header2.h ...

Thus make will regenerate the dependencies if the source or any of the headers change. 因此,如果源或任何标头发生更改, make将重新生成依赖项。 Including phony targets for the headers (GCC -MP ) should then allow stable builds when headers are removed - the absense of required header remains a compilation error, not a make dependency error. 包括标头的伪目标(GCC -MP )应该允许在删除标头时进行稳定构建 - 所需标头的-MP仍然是编译错误,而不是make依赖性错误。

Assuming that dependency files are generated into the same directory as the object files, the following should work for GCC on Unix: 假设依赖文件生成到与目标文件相同的目录中,以下内容应适用于Unix上的GCC:

-include $(OBJ:.o=.d)

$(OBJDIR)/%d : $(SRCDIR)/%.cpp
        mkdir -p $(@D)
        echo -n "$@ " > $@.tmp
        $(CXX) $(CPPFLAGS) -MM -MP -MT $(@:.d=.o) $< >> $@.tmp
        mv $@.tmp $@

(from memory) (从记忆里)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM