Gnu make - 如何处理项目树外部的源代码

时间:2014-09-12 00:45:34

标签: linux makefile gnu-make

使用:

  • Linux as build host(kubuntu 14.04)
  • Gnu make 3.81
  • 编译一些C / C ++项目

我有一个像这样的目录树:

  • 存储库/
    • 框架/
      • 源/
        • Subdir1 /
        • Subdir2 /
          • Subdir3 /
        • 等等
      • 更多内容
    • 项目/
      • PROJECT1 /
        • 源/
          • SubdirA /
          • SubdirB /
            • SubdirX /
          • 等等
        • Out /←子目录结构在Out /下面是例如
          • 源/
            • SubdirA /
            • SubdirB /
              • SubdirX /
          • 框架/
            • 源/
              • Subdir1 /
              • Subdir2 /
                • Subdir3 /
        • 生成文件
      • Project2的/
        • 源/
          • ...
        • 输出/
          • ...
        • 生成文件
      • 等等
Framework / Source目录的内容只是一些通用源文件的连接,这些文件将在几个项目中使用。 Framework目录没有自己的makefile,例如不会建立一个lib。它只是,项目使用Framework目录中的一些源代码。

构建Project1时,我首先进入相关项目dir,然后调用make,例如:

cd ~/Repository/Projects/Project1
make

所有构建输出都必须放入Project1 / Out目录。 在Out目录下面,我的makefile镜像源树中的目录层次结构。

在我的makefile中,我做了类似的事情:

SRCS += $(shell find Source -name "*.c")
SRCS += $(shell find ../../Framework/Source -name "*.c")
BUILDDIR := Out
OBJS = $(addprefix $(BUILDDIR)/, $(addsuffix .o, $(basename $(SRCS))))
$(BUILDDIR)/%.o : %.c
    $(CC) $(CFLAGS) -c $< -o $@

这会导致在模式规则和../ ..部分期间,一些生成的目标文件将被放置在我的构建输出目录之外的问题,例如:

gcc -c ../../Framework/Source/*.c -o Out/../../Framework/Source/*.o

我的第一种方法是强制所有源文件名为绝对路径名:

OBJS = $(addprefix $(BUILDDIR)/, $(addsuffix .o, $(basename $(abspath $(SRCS)))))

这可行(即使使用realpath),但由于路径长+文件输出,它使我的构建输出非常难看。

我有第二种方法,我只是用。../替换.o文件的所有up/部分,它看起来像这样:

OBJS = $(addprefix $(BUILDDIR)/, $(addsuffix .o, $(subst ../,up/,$(basename $(SRCS)))))

但不幸的是,似乎。我必须复制我的所有模式规则,如:

$(BUILDDIR)/%.o : %.c
    $(CC) $(CFLAGS) -c $< -o $@
$(BUILDDIR)/up/up/%.o : ../../%.c
    $(CC) $(CFLAGS) -c $< -o $@

所以,我仍然在为这个问题寻找一个更好的解决方案。

由于某些原因,我有以下限制:

  • 没有符号链接
  • 没有递归调用
  • 不使用vpath
  • 没有图书馆
  • 没有git子模块...

任何有关更优雅方法的建议?

2 个答案:

答案 0 :(得分:2)

以下是解决方案:

COLLAPSED_SRC_PREFIXES_IN_BLD := ../../

SRCS += $(shell find Source -name "*.c")
SRCS += $(shell find ../../Framework/Source -name "*.c")

BUILDDIR := Out

.PRECIOUS: %/.
%/.:
    mkdir -p $@


define BLD_FROM_TO

$2/%.o: $1%.c Makefile | $$$$(@D)/.
    $(CC) $(CFLAGS) $$< $$@

endef


.SECONDEXPANSION:

$(foreach prefix, $(COLLAPSED_SRC_PREFIXES_IN_BLD), $(eval $(call BLD_FROM_TO,$(prefix), $(BUILDDIR))))
$(eval $(call BLD_FROM_TO,, $(BUILDDIR)))

答案 1 :(得分:0)

我的原始答案保留在下面以供参考,但讨论的共识似乎是我的答案是错误的。当你和我一样长时间存在时,就会有一个真实的环节&#34;你可以听到某些批评。人们只是忽略了那些缺乏真理之戒的批评,但这种批评却有这种批评。

奇怪的是,我已经在我自己的工作中使用递归Make多年,以适当的重构方式(所以我实际上不会保持多个或多或少相同规则的实例)的帮助include和符号链接到makefile(当然,让我的编译器自动发出标头依赖项),这一切似乎对我来说都很好。设置并不容易,每当我打开一个新的源子目录时都会有一些麻烦;但后来我相信使用多个源目录制作起来并不容易设置,所以我就是这样。

我不知道OP是否已经学会了他今天要学习的内容,但我似乎已经学会了一些东西。

我撤回了答案。感谢您阅读我的回答并对其进行了评论。

原始回答

Makefiles似乎总是很难。没有确定的,不可改变的规则,但有一些做法有所帮助。

  1. 通常,文件最好由makefile创建,而不是在文件的先决条件的目录中,而是在要创建文件的目录中。因此,您可以使用多个makefile,每个目录中都有一个makefile。例如,在您的示例中,您可以将makefile放在Out/目录中,然后将$(BUILDDIR)/%.o:规则移到该makefile上。

  2. 如果您还不知道,请学习制作-C选项,并开始广泛且一致地使用它。确切地说,cd ~/Repository/Projects/Project1之前make没有任何问题,除非cd的原因是您没有考虑使用-C。在命令行和makefile规则中一致地使用-C最终会使以更高效的方式思考关于makefile。它逐渐澄清了你对此事的看法,这很好。

  3. 此外,如果您还不知道,请学习使用Make $(MAKE)进行Make的递归调用。

  4. 在规则中一起使用$(MAKE)-C

  5. 您如何将这些元素组合成一个适合您的解决方案取决于您想要做的事情的细节,但上述建议将使您无法与Make的设计理念作斗争。不打架会让事情变得更轻松。由于Make的手册解释了功能,但没有很好地解释功能背后的设计理念,这很重要。

    人们可以写完整个章节,所以我会在这里停下来;但如果您希望我扩展一个特定的点,请随时告诉我。