首页 文章

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

提问于
浏览
1

使用:

  • Linux作为构建主机(kubuntu 14.04)

  • Gnu使3.81

  • 编译一些C / C项目

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

  • 存储库/

  • 框架/

  • 来源/

  • Subdir1 /

  • Subdir2 /

  • Subdir3 /

  • 等等

  • 更多

  • 项目/

  • Project1 /

  • 来源/

  • SubdirA /

  • SubdirB /

  • SubdirX /

  • 等等

  • Out /←子目录结构下面的Out /是

  • 来源/

  • SubdirA /

  • SubdirB /

  • SubdirX /

  • 框架/

  • 来源/

  • Subdir1 /

  • Subdir2 /

  • Subdir3 /

  • Makefile

  • Project2 /

  • 来源/

  • ......

  • 出/

  • ......

  • Makefile

  • 等等

Framework / Source目录的内容只是一些通用源文件的连接,这些文件将在几个项目中使用 . Framework目录没有自己的makefile,例如赢了't build a lib. It'只是,项目使用了Framework目录中的一些源代码 .

在构建Project1时,我首先进入相关的项目目录,然后调用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),但由于长路径文件名输出,它使我的构建输出相当丑陋 .

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

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

  • 没有libs

  • 没有git子模块......

有什么建议可以采用更优雅的方法吗?

2 回答

  • 0

    这是解决方案:

    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)))
    
  • 2

    我的原始答案保留在下面以供参考,但讨论的共识似乎是我的答案是错误的 . 如果你已经存在了很长时间,就像我一样,在某些批评中你可以听到某种“真理之戒” . 人们只是忽略了那些缺乏真理之戒的批评,但这种批评却有这种批评 .

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

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

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

    ORIGINAL ANSWER

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

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

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

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

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

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

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

相关问题