【问题标题】:How to ensure object files are compiled with correct settings in Makefile with multiple MAKE targets如何确保在具有多个 MAKE 目标的 Makefile 中使用正确的设置编译目标文件
【发布时间】:2010-07-02 17:00:09
【问题描述】:

我有一个从相同源文件构建多个目标的小项目。目标需要使用不同的编译标志构建源文件。这实际上是在 cygwin 上,所以我将使用它作为具体示例,尽管我认为这是一个通用问题。

所以这将是一个 Makefile 示例:

a: CFLAGS =

a: a.o c.o

b: CFLAGS = -mno-cygwin

b: b.o c.o

这在原则上是有效的,构建 a 将在未设置 CFLAGS 的情况下进行编译,而构建 b 将在 CFLAGS 设置为 -mno-cygwin 的情况下进行编译。但前提是 c.o 不存在。

这样做

> make a b

在原始目录中将首先使用空的 CFLAGS 编译 a.c 和 c.c。然后它将尝试使用 CFLAGS=-mno-cygwin 构建 b。但是由于 c.o 已经存在,它不会重新编译 c.c 导致链接器错误,因为目标文件需要具有相同的此标志设置。

同样,cygwin-flag 只是一个具体的例子,我正在寻找一个通用的解决方案。

我尝试的是引入一个额外的目标来检查当前的 CFLAGS,如果不匹配则删除所有目标文件:

ARCH    = `echo $(CFLAGS)`
checkTarget:
-@if test "`cat .arch`" != "$(ARCH)"; then \
    rm *.o; \
    /bin/echo -n $(ARCH) > .arch; \
fi

将此目标作为两个目标的依赖项插入可确保在需要时重新编译所有目标文件。

a: CFLAGS =

a: checkTarget a.o c.o

b: CFLAGS = -mno-cygwin

b: checkTarget b.o c.o

然而,这会重新编译 每个 目标文件,这是不必要的,并且在更大的项目中会成为问题。

有没有更好的方法来做到这一点?

编辑: 在单个答案的 cmets 中包含一个提示,您可以使目标文件依赖于 CFLAGS 的内容。我不知道该怎么做,除了将它们放到一个临时文件中并与以前的文件进行比较,然后将其复制到一个依赖文件上。有没有更好的办法?

【问题讨论】:

    标签: makefile


    【解决方案1】:

    我们需要的是两个(或更多)版本的c.o,使用不同的标志编译并链接到不同的可执行文件。您可以使用像 c_for_a.o 这样的人为文件名来做到这一点,但是将它们保存在不同的目录中会更整洁,for_a/for_b/

    a for_a/%: CFLAGS =
    
    a: a.o for_a/c.o
    
    b for_b/%: CFLAGS = -mno-cygwin
    
    b: b.o for_b/c.o
    

    (如果不清楚,我可以填写 Makefile 的其余部分。)

    编辑:
    如果您不想保留多个c.o,有时您将不得不重新编译 c.o。 checkTarget 的想法可能有一个小的改进。

    当您正在构建像b 这样的目标时,并且c.o 已经存在,那么c.o 是否是使用适合bCFLAGS 构建的并且您使用checkTarget 来记录这一点很重要信息。 但你并不关心CFLAGS 使用了什么,只关心它们是否是bCFLAGS。所以你不必在checkTarget 文件中记录任何内容,只需在构建新目标时更新即可:

    a: CFLAGS =
    
    b: CFLAGS = -mno-cygwin
    
    # You may be able to combine the a and b rules...
    a: a.o c.o checkTarget
        # build c.o
        touch checkTarget
        # build the target
    
    b: b.o c.o checkTarget
        # build c.o
        touch checkTarget
        # build the target
    
    # Need this to create checkTarget the first time
    checkTarget:
        @touch $@
    

    【讨论】:

    • 好答案。如果项目很大,比如 50 个目标文件,那么如果要重新编译的文件数量(示例中的 c.c)很少,这将起作用。如果大多数需要重新编译,我认为这种策略会变得笨拙。对这种情况有什么想法吗?
    • @Thomas Nilsson:这取决于您要优化的内容。如果您不想重新编译c.c,那么您必须保留它的不同版本,这是最好的方法。如果您不想保留多个c.o,但只想在必要时重新编译c.o,还有另一种方法,对您的checkTarget 稍作改进。我将编辑我的答案以包含它。
    • 嗯,如果我理解正确,checkTarget 是一个时间戳,例如如果您构建 b 但 b.o 和 c.o 存在,则 checkTarget 仍将触发构建规则。然后在命令中,您根据 CFLAGS 硬连线 c.o 的构建。如果这是您的意图,我并不完全满意(但话又说回来,这可能是不可能的 ;-),因为随着我的要求发生变化,我将不断更新显式构建命令。我希望有某种方法可以使每个 %.o 都依赖于用于编译它的 CFLAGS,并且只有在 CFLAGS 不相同时才会触发编译。
    • @Thomas Nilsson:是的,这也是可能的,但它使规则更加模糊。稍后我会再次编辑帖子。
    • 基本上,您必须将 CFLAGS 值合并(取决于)到目标文件名中,如果您有许多不同的可能值,您基本上希望对多个参数的规则进行参数化; make 对此没有很好的语法,但可以做到,例如使用 GNU make 的 $(foreach)/$(eval)/$(call) 函数。
    【解决方案2】:

    虽然有一些关于如何做我最初要求here 的建议(正如slowdog 所指出的那样),但我还是决定将Beta 的不同名称模式更进一步,并且按照Betathis question的回答中的第二条建议中的描述,将各种变体的所有对象放在子目录中。

    本质上使我的 Makefile 看起来像:

    A : AOBJDIR = .a
    AOBJECTS = $(addprefix $(AOBJDIR)/,$(ASRCS:.c=.o))
    
    $(AOBJECTS): $(AOBJDIR)/%o: %.c
        $(CC) $(CFLAGS) -MMD -o $@ -c $<
    
    $(AOBJDIR) :
        @mkdir $(AOBJDIR)
    
    -include $(AOBJECTS:.o=.d)
    
    a: $(AOBJDIR) $(AOBJECTS)
        $(LINK) ...
    

    因此,第一部分命名了一个用于“a”目标文件的子目录,并通过转换源文件名并添加子目录前缀来在该目录中创建目标文件列表。

    然后遵循这些目标文件的默认规则(为了很好的措施,生成依赖项)。

    接下来的两个是确保子目录存在,并且包含依赖信息。

    最后是对子目录本身有额外依赖的链接规则,以确保如果它不存在就创建它。

    在我的示例中,可以通过交换所有 a 来为 b 重复此块。因此,如果我能弄清楚如何将这个块打包成更通用的可以参数化的东西,我会很高兴。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-03-29
      • 1970-01-01
      • 1970-01-01
      • 2019-05-11
      • 1970-01-01
      • 2011-01-19
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多