如何使用 GNU make 编译多个简单项目
How to compile multiple simple projects with GNU make
我正在尝试实现编程书籍中的各种项目。我的意图是让每个项目练习都放在它自己的文件夹中,然后有一个 makefile 用类似 make all
的东西编译所有这些项目。文件夹结构是这样的:
.
├── Makefile
├── bin
│ ├── prog1
│ ├── prog2
│ └── prog3
└── src
├── prog1
│ ├── Makefile
│ └── main.c
├── prog2
│ ├── Makefile
│ └── main.c
└── prog3
├── Makefile
└── main.c
我想学习如何设置这样的结构。特别是顶部 makefile 访问 src
中所有文件夹的部分调用 make
,然后将可执行文件复制并重命名到 bin
文件夹中。
有不同的方法来解决这个问题,但像这样的方法应该适用于您的示例:
PROGS := bin/prog1 bin/prog2 bin/prog3
all: $(PROGS)
$(PROGS):
$(MAKE) -C src/$(@F)
mkdir -p $(@D)
cp src/$(@F)/main $@
.PHONY: clean
clean:
rm -f $(PROGS)
for t in $(PROGS); do make -C src/`basename $$t` clean; done
我们定义了我们要构建的目标列表 (PROGS
)。我们说这些目标是 all
的先决条件,然后我们继续定义它们应该如何构建,即:我们递归下降到 src/
加上目标的文件名部分到 运行 make那里。我们创建目标目录以确保它在那里,并从我们下降到目标路径的目录中复制 main
。
作为一个很好的衡量标准,还有一个 clean
目标可以在 src/
中递归地删除所有 PROGS
和 运行s make clean
。
您的布局示意图显示了每个练习的 makefile,以及您似乎实际上要询问的顶级 makefile。顶层 makefile 最好避免重复每个练习 makefile 的行为,因为这样的重复会给您带来额外的维护负担。此外,您很可能最终会进行涉及多个源文件的练习,并且可能会进行一些需要构建多个工件的练习。这就是每个练习 makefile 包含构建与之关联的练习所需的一切(进入练习特定目录)以及顶级 makefile 依赖于这些的更多原因。
遵循该方案将为顶级 makefile 留下明确定义的角色:执行每次练习构建(通过递归 运行 make
),并复制生成的二进制文件至 bin/
。这不是 唯一 设置协作 makefile 系统的方法,但它相当简单,并且可以让您专注于练习而不是构建系统。
然后,让我们假设,每个单独的练习都可以通过切换到其目录和 运行 make
来构建,结果是在同一目录中具有相同名称的可执行文件作为目录。也就是说,从顶级目录执行 cd src/prog2; make
会生成所需的可执行文件 src/prog2/prog2
。在那种情况下,顶层 makefile 只需要所有练习的名称和一些规则:
EXERCISES = prog1 prog2 prog3
BINARIES = $(EXERCISES:%=bin/%)
all: $(BINARIES)
$(BINARIES):
make -C src/$$(basename $@)
cp src/$$(basename $@)/$$(basename $@) $@
注意:它使用特定于 GNU 的 make
实现的功能来根据练习名称计算所需二进制文件的名称。我认为这是可以接受的,因为你标记了 [gnu-make],但无论如何,这是一个方便的功能,而不是必需的。
我正在尝试实现编程书籍中的各种项目。我的意图是让每个项目练习都放在它自己的文件夹中,然后有一个 makefile 用类似 make all
的东西编译所有这些项目。文件夹结构是这样的:
.
├── Makefile
├── bin
│ ├── prog1
│ ├── prog2
│ └── prog3
└── src
├── prog1
│ ├── Makefile
│ └── main.c
├── prog2
│ ├── Makefile
│ └── main.c
└── prog3
├── Makefile
└── main.c
我想学习如何设置这样的结构。特别是顶部 makefile 访问 src
中所有文件夹的部分调用 make
,然后将可执行文件复制并重命名到 bin
文件夹中。
有不同的方法来解决这个问题,但像这样的方法应该适用于您的示例:
PROGS := bin/prog1 bin/prog2 bin/prog3
all: $(PROGS)
$(PROGS):
$(MAKE) -C src/$(@F)
mkdir -p $(@D)
cp src/$(@F)/main $@
.PHONY: clean
clean:
rm -f $(PROGS)
for t in $(PROGS); do make -C src/`basename $$t` clean; done
我们定义了我们要构建的目标列表 (PROGS
)。我们说这些目标是 all
的先决条件,然后我们继续定义它们应该如何构建,即:我们递归下降到 src/
加上目标的文件名部分到 运行 make那里。我们创建目标目录以确保它在那里,并从我们下降到目标路径的目录中复制 main
。
作为一个很好的衡量标准,还有一个 clean
目标可以在 src/
中递归地删除所有 PROGS
和 运行s make clean
。
您的布局示意图显示了每个练习的 makefile,以及您似乎实际上要询问的顶级 makefile。顶层 makefile 最好避免重复每个练习 makefile 的行为,因为这样的重复会给您带来额外的维护负担。此外,您很可能最终会进行涉及多个源文件的练习,并且可能会进行一些需要构建多个工件的练习。这就是每个练习 makefile 包含构建与之关联的练习所需的一切(进入练习特定目录)以及顶级 makefile 依赖于这些的更多原因。
遵循该方案将为顶级 makefile 留下明确定义的角色:执行每次练习构建(通过递归 运行 make
),并复制生成的二进制文件至 bin/
。这不是 唯一 设置协作 makefile 系统的方法,但它相当简单,并且可以让您专注于练习而不是构建系统。
然后,让我们假设,每个单独的练习都可以通过切换到其目录和 运行 make
来构建,结果是在同一目录中具有相同名称的可执行文件作为目录。也就是说,从顶级目录执行 cd src/prog2; make
会生成所需的可执行文件 src/prog2/prog2
。在那种情况下,顶层 makefile 只需要所有练习的名称和一些规则:
EXERCISES = prog1 prog2 prog3
BINARIES = $(EXERCISES:%=bin/%)
all: $(BINARIES)
$(BINARIES):
make -C src/$$(basename $@)
cp src/$$(basename $@)/$$(basename $@) $@
注意:它使用特定于 GNU 的 make
实现的功能来根据练习名称计算所需二进制文件的名称。我认为这是可以接受的,因为你标记了 [gnu-make],但无论如何,这是一个方便的功能,而不是必需的。