【问题标题】:Simple Non-recursive Makefile with object files in separate directory具有单独目录中的目标文件的简单非递归 Makefile
【发布时间】:2014-10-21 22:28:33
【问题描述】:

我已经搜索并尝试了一些示例来使用非递归 makefile 完成一个简单的项目。过去,我使用简单的单目录代码库,但现在我正在为多个工程师构建一个环境:-)

+--app
|  +-- obj/
|  +-- Makefile
|  +-- first.c
|  +-- second.c
|
+--lib1
|  +-- obj/
|  +-- Makefile
|  +-- foo.c
|  +-- bar.c
|
+--lib2
|  +-- obj/
|  +-- Makefile
|  +-- sample.c

没什么特别的——只有两个目录(可能稍后第三个),库代码和多个应用程序都在“app”目录中。我想将 .o 和 .d 文件保存在单独的 obj/ 目录中以保持清洁。

  1. 我希望能够在每个子目录(如 lib1 和 lib2)中进行“制作”以验证库。它们将分别生成 libabc.a 和 libxyz.a。
  2. 我编写了一些简单的 Makefile,但我的规则不起作用,我试图理解 GNU make 手册但迷路了。

lib1/Makefile:

lib_src = foo.c bar.c
lib_obj = $(patsubst %.c,obj/%.o,$(lib_src))
libabc.a: $(lib_obj)
    @echo [Archive... $(@F)]
    @$(AR) -cr libabc.a $^
obj/%.c : %.c 
    $(CC) $(CFLAGS) -c -o $@ $<

应用程序/Makefile:

ALL_APP = first second
% : %.c libabc.a libxyz.a
     $(CC) $(CLFAGS) $^ -o $@
include ../lib1/Makefile
include ../lib2/Makefile

现在,我无法在每个 Makefile 中定义相同的目标(显然)。就像我无法在 lib1/Makefile 和 app/Makefile 中定义一个干净,因为 lib1 包含在那里。虽然我希望我可以单独在 lib1 中进行 make clean,但这是有道理的。

现在,当我在“app”中制作时,没有规则可以制作 obj/foo.o。我猜是因为路径都是假的。 "obj/" 指的是 lib1/obj/ 但随着 Makefile 的包含,这一切都丢失了。

我做错了什么,我可以像上面那样使用非常简单的 Makefile 来构建我的项目。大多数在线示例都相当复杂,因为它们试图实现更多(我相信)。

提前致谢(对于已经讨论过多次的话题,我们深表歉意)。如果可以避免的话,我宁愿现在不学习 automake 和 cmake。我希望我的项目足够简单,不需要使用这些强大的工具。

最好的问候,

【问题讨论】:

  • 我不认为在您的应用程序生成文件中包含库生成文件是正确的解决方案。这是我要做的:为每个目录编写一个功能齐全的独立 makefile。然后为您的应用程序编写一个简单的构建脚本,为您的每个库调用 make,最后为您的应用程序调用 make。您还可以让构建脚本将构建的库复制到您的应用目录中。
  • @BrandonYates 这或多或少是递归的,但又删除了一步,因为你甚至不让 make 进行递归构建。
  • 谢谢布兰登。我认为调用 make 不是一个好主意,而包含 Makefile 是一种更好的方法。彼得米勒写了一篇论文解释了原因。独立调用每个 Makefile 更难管理依赖关系。
  • 顺便说一句:这篇论文被称为Recursive Make Considered Harmful
  • 构建这种非递归的makefile系统(具有包含独立makefile的能力)并非易事。我已经完成了自己的实现,我花了几年的时间来完善它。你可以在这里找到它github.com/igagis/prorab/blob/master/wiki/…

标签: c makefile gnu-make non-recursive


【解决方案1】:

让我们从lib1/Makefile开始:

lib_src = foo.c bar.c
lib_obj = $(patsubst %.c,obj/%.o,$(lib_src))

libabc.a: $(lib_obj)
    @echo [Archive... $(@F)]
    @$(AR) -cr libabc.a $^

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

我们引入变量HERE,并做一些小改动:

HERE := ../lib1

lib_src := foo.c bar.c
lib_obj := $(patsubst %.c,$(HERE)/obj/%.o,$(lib_src))

$(HERE)/libabc.a: $(lib_obj)
    @echo [Archive... $(@F)]
    @$(AR) -cr $@ $^

$(HERE)/obj/%.c : $(HERE)/%.c 
    $(CC) $(CFLAGS) -c -o $@ $<

当从lib1/ 中调用时,此生成文件仍将像以前一样工作。但是一旦我们对lib2/Makefile进行了相应的修改,我们就可以修改app/Makefile

ALL_APP = first second
% : %.c ../lib1/libabc.a ../lib2/libxyz.a
     $(CC) $(CLFAGS) $^ -o $@

include ../lib1/Makefile
include ../lib2/Makefile

现在是clean 规则。我们重命名lib1/Makefile => lib1/lib1.maklib2/Makefile => lib2/lib2.mak,写一个新的lib1/Makefile

include lib1.mak

clean:
    @rm -f lib*.a obj/*

lib2/中做同样的事情,并修改app/Makefile

...

include ../lib1/lib1.mak
include ../lib1/lib1.mak

clean:
    @rm -f $(ALL_APP)
    @$(MAKE) -C ../lib1 clean
    @$(MAKE) -C ../lib2 clean

(我们可以不使用递归,但会更复杂,这样使用递归 Make 确实没有什么问题。)

一些进一步的改进是可能的。例如,以这种方式将路径硬编码到lib1/lib1.maklib2/lib2.mak 是不好的,这可以修复。不过一天就够了。

【讨论】:

    猜你喜欢
    • 2023-03-22
    • 2015-08-14
    • 2011-05-05
    • 1970-01-01
    • 2019-05-29
    • 1970-01-01
    • 1970-01-01
    • 2013-08-09
    • 1970-01-01
    相关资源
    最近更新 更多