【问题标题】:Gnu Makefile - Handling dependenciesGnu Makefile - 处理依赖项
【发布时间】:2017-03-30 22:07:26
【问题描述】:

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

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

但是我在 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)

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

【问题讨论】:

  • 嗯,我在 Makefile 中使用第三个表达式进行了 sed 调用,它删除了尾随反斜杠。更多信息?
  • Jefromi,已更新 - 添加了完整的 Makefile
  • 等一下,你用的是什么牌子的?
  • 我正在使用 GNU Make 3.81

标签: c++ unix makefile sed gnu


【解决方案1】:

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

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

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

-include $(DEPS)

【讨论】:

    【解决方案2】:
    1. 我也用that approach,赞不绝口。我手工编写我的 makefile,并在新项目中大量重复使用它们。
    2. . 表达式“s/ *\\$//”将在 Make 的上下文之外工作。在 makefile 中它不起作用,因为 Make 在将结果交给 shell 之前尝试解释 "$/"。所以你必须在makefile中使用“s/ *\\$$//”(注意额外的$),但这在Make的上下文之外之外不起作用(所以测试它是一个轻微的疼痛)。



    编辑:

    我试过你的makefile,sed语句似乎可以很好地删除尾随的反斜杠。尝试一些更简单的方法,例如:

    反斜杠: @echo " \\" > $@ 测试:反斜杠 @echo 没有 sed: @cat 反斜杠 @echo 与 sed: @sed -e 's/ *\\$$//' &lt 反斜杠



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

    将最后一个字符更改为 'z' : s/.$/z/ 将尾部反斜杠更改为 'z' : s/\\$/z/ 将尾部反斜杠更改为 'z' : sm\\$mzm 删除尾部反斜杠:s/\\$// 删除空格和尾部反斜杠:s/ *\\$// 使用“$”和“$$”在 Make 内部和外部尝试所有这些。

    【讨论】:

    • 改用 GCC 的头文件依赖生成器开关。 Visual Studio 也有一个。这样你就只依赖于实际包含的标题。
    • 感谢 Beta,我已经解决了我的问题。对不起,这是我的错。该 sed 表达式正在正常工作(它正在删除尾部反斜杠)。出于某种原因,我认为在删除尾随反斜杠之后,该命令中的其他表达式将附加这些行并在末尾添加一个冒号。在末尾添加冒号但不附加行,这不是必需的。
    【解决方案3】:

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

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

    【讨论】:

      【解决方案4】:

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

      【讨论】:

        【解决方案5】:

        我更喜欢使用 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)
        

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

        find_package(Qt4)
        

        【讨论】:

          【解决方案6】:

          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中。类似于the approach of using a .d file for each .cc file,但是依赖信息保存在一个文件中,而不是分散在各处。

          【讨论】:

            【解决方案7】:

            在 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.org/mozilla-central/source/build/unix/mddepend.pl

            该脚本会生成一个 .all.pp 文件,其中包含所有依赖项,额外的 foo.o: FORCE 依赖项会因缺少的头文件而卡住。然后,我们只需在下面的 rules.mk 中包含 .all.pp 文件。

            【讨论】:

              【解决方案8】:

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

              【讨论】:

                【解决方案9】:

                我使用 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”包含——它处理所有、构建、依赖、清理目标。项目特定的BSDmakefiles 很简单:

                .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 指令时都会依赖。

                【讨论】:

                  【解决方案10】:

                  代替 sed 脚本,使用 gcc 的 -MT 选项来修改生成的依赖规则的目标。 This blog post 有更多信息。

                  【讨论】:

                    【解决方案11】:

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

                    【讨论】:

                      【解决方案12】:

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

                      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 "$@ " > $@.tmp
                              $(CXX) $(CPPFLAGS) -MM -MP -MT $(@:.d=.o) $< >> $@.tmp
                              mv $@.tmp $@
                      

                      (凭记忆)

                      【讨论】:

                        猜你喜欢
                        • 1970-01-01
                        • 2021-10-03
                        • 1970-01-01
                        • 2018-01-15
                        • 2016-09-14
                        • 2015-05-09
                        • 1970-01-01
                        • 1970-01-01
                        • 1970-01-01
                        相关资源
                        最近更新 更多