Gnu Makefile - 处理依赖关系

发布于 2024-08-05 20:23:50 字数 1032 浏览 7 评论 0原文

Unix 平台上的 C++ 程序员使用什么方法来创建和管理 Makefile?

我在我的项目中使用手工制作的 Makefile,但它们不处理头文件更改和其他依赖项。我在谷歌上搜索了一下,这里找到了一个很好的解决方案。

但我在 sed 命令中遇到了一个问题 -

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

问题出在第三个表达式“-e 's/ *\$$//”。 这不起作用。它应该删除结尾的反斜杠。我知道那里必须有双美元,因为这是 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)

除了这个问题的解决方案之外,我还想要一些针对我的第一个问题的提示/指针。

What approach do C++ programmers on Unix platform use to create and manage Makefiles?

I was using hand made Makefiles for my projects but they don't handle header file changes and other dependencies. I googled around and found a good solution here.

But I ran into a problem here in the sed command -

    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?

Here's the complete 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 $@ 
lt; $(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.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(12

萌酱 2024-08-12 20:23:50

gcc/g++ 可以使用 -M 系列选项为您生成依赖项。
下面的工作原理是指定如何在给定源文件的情况下生成 .depends 文件。通过执行 -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 
lt; -o $@

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

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

-include $(DEPS)

gcc/g++ can generate dependencies for you with the -M family of options.
The following works by specifying how to generate .depends files given a source file. By doing -include $(DEPS) $(DEPS) is recognized as a target and will be built/rebuilt when the source files change.

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 
lt; -o $@

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

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

-include $(DEPS)
不醒的梦 2024-08-12 20:23:50
  1. 我也使用这种方法并且对它赞不绝口。我手工编写 makefile,并在新项目中大量重复使用它们。
  2. 表达式“s/ *\\$//”将在 Make 上下文之外工作。在 makefile 中它不起作用,因为 Make 在将结果传递给 shell 之前尝试解释“$/”。因此,您必须在 makefile 中使用“s/ *\\$$//”(注意额外的 $),但这在 Make 上下文之外无法工作(因此测试它有点困难)疼痛)。

编辑:

我已经尝试过你的 makefile,并且 sed 语句似乎可以很好地删除尾部反斜杠。尝试一些更简单的方法,如下所示:

backslash:
    @echo " \\" > $@

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

编辑:
好吧,现在我迷上了。您能尝试这些实验并告诉我们结果吗?

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 '$'.
  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.
  2. .The expression "s/ *\\$//" will work outside the context of Make. Within a makefile it doesn't work because Make tries to interpret "$/" before handing the result to the 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).

EDIT:

I've tried your makefile, and that sed statement seems to remove trailing backslashes just fine. 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 '$'.
梦魇绽荼蘼 2024-08-12 20:23:50

在 make 文件中,您在依赖项行上列出的任何内容都是依赖项头文件或包含的其他文件。

关于 make 的 BSD 教程注意:您可以使用 GCC 的 -MM 开关自动生成标头依赖信息。

In a make file anything you list on the dependency line is a dependency header files or other files included.

BSD tutorial on make Note: you can auto generate header dependency info with the -MM switch of GCC.

流年已逝 2024-08-12 20:23:50

我一定是错过了什么。为什么生成依赖文件对您不起作用?

I must be missing something. Why doesn't generating dependency files work for you?

记忆里有你的影子 2024-08-12 20:23:50

我更喜欢使用 CMake,尽管它并不是严格意义上的问题解决方案。

它是一种项目描述语言,可以为您生成 Makefile、Visual Studio 项目、Eclipse 项目、KDevelop 等。所有依赖项都已为您完成:

CMakeLists.txt

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

在 lib/CMakeLists.txt

add_library(my_library file3.c file4.c)

这会从 file1.c file2.c 链接到 my_library 创建一个 my_exe。我发现这更简单。它还具有包发现之类的功能:

find_package(Qt4)

I prefer to use CMake, even though it's not strictly the solution to your problem.

It's a project description language that'll generate your Makefiles, Visual Studio Project, Eclipse Project, KDevelop, etc for you. All the dependencies are done for you:

CMakeLists.txt

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

In 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. I find this much simpler. It also has things like package discovery:

find_package(Qt4)
静若繁花 2024-08-12 20:23:50

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

如果 foo.ccmain.cc依赖于foo.h,则Makefile.depend的内容为:

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

最终结果是来自makedepend的依赖信息> 作为一系列规则注入到 Makefile 中。它类似于 使用 .d 每个 .cc 文件 都有一个文件,但将依赖信息保留在一个文件中,而不是分散在各处。

The makedepend utility is installed on many systems and can be quite useful for generating dependency information.

Here is an example Makefile that uses the include directive (plus a little Perl magic) to incorporate the output from 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:

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. 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.

¢蛋碎的人ぎ生 2024-08-12 20:23:50

在 Mozilla 的构建系统中,我们使用 GCC 的 -MD 开关来生成依赖文件:
http://mxr.mozilla.org/mozilla-central/source/配置.in#7134
然后我们使用一个名为 mddepend.pl 的脚本来检查已删除的头文件,这样
删除标头只会导致重建,而不是错误:
http://mxr.mozilla.org/mozilla-central/源/config/rules.mk#2066
http://mxr.mozilla.org/mozilla-central/ source/build/unix/mddepend.pl

该脚本生成一个包含所有依赖项的 .all.pp 文件,并为缺少的头文件添加额外的 foo.o: FORCE 依赖项。然后,我们只需将 .all.pp 文件包含在下面的 Rules.mk 中。

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

That script generates an .all.pp file containing all the dependencies, with extra foo.o: FORCE dependencies stuck in for missing header files. We then simply -include the .all.pp file in rules.mk right below there.

慵挽 2024-08-12 20:23:50

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

You can use qmake to generate Makefiles for a project even if that project is not using Qt.

荒芜了季节 2024-08-12 20:23:50

我使用 BSD make(pmake?),它为我做了很多工作(我的语言是 C,但我认为这里没有区别)。这是我常见的“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)

注意“bsd.prog.mk”包含 - 这处理所有构建、依赖、清理目标。特定于项目的 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

每次插入/删除任何 #include 指令时我都会 make dependent。

I use BSD make (pmake?) which does lot of work for me (my lang is C, but I think no difference here). This is my common 'local.prog.mk', I never change it:

.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. Project-specific BSDmakefiles are simple:

.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.

浅听莫相离 2024-08-12 20:23:50

使用 gcc 的 -MT 选项代替 sed 脚本来修改生成的依赖关系规则的目标。 这篇博文提供了更多信息。

Instead of the sed scripts, use gcc's -MT option to modify the target of the generated dependency rules. This blog post has more info.

情绪失控 2024-08-12 20:23:50

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

With a more modern version of GCC, you can add the -MP flag to have GCC generate empty rules for the headers itself.

任性一次 2024-08-12 20:23:50

我发现在构建依赖项文件时有用的最重要提示是将依赖项文件作为目标包含在生成的规则中:

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

因此,如果源或任何标头发生更改,make 将重新生成依赖项。包含标头的虚假目标 (GCC -MP) 应该允许在标头被删除时进行稳定的构建 - 缺少所需标头仍然是编译错误,而不是 make 依赖项错误。

假设依赖文件生成到与目标文件相同的目录中,则以下内容应适用于 Unix 上的 GCC:

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

$(OBJDIR)/%d : $(SRCDIR)/%.cpp
        mkdir -p $(@D)
        echo -n "$@ " > [email protected]
        $(CXX) $(CPPFLAGS) -MM -MP -MT $(@:.d=.o) 
lt; >> [email protected]
        mv [email protected] $@

(来自内存)

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. 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.

Assuming that dependency files are generated into the same directory as the object files, the following should work for GCC on Unix:

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

$(OBJDIR)/%d : $(SRCDIR)/%.cpp
        mkdir -p $(@D)
        echo -n "$@ " > [email protected]
        $(CXX) $(CPPFLAGS) -MM -MP -MT $(@:.d=.o) 
lt; >> [email protected]
        mv [email protected] $@

(from memory)

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文