c++ - 使用相似规则构建多个可执行文件

标签 c++ build build-automation scons

我正在写 something就像 C++ 的交互式教程。本教程将由两部分组成:一个被编译成一个库(我正在使用 Scons 构建它),另一个(类(class))随教程一起提供,由最终用户编译。我目前正在寻找一种方便人们构建这些类(class)的好方法。

基本上,第二部分是一个包含所有类(class)的目录,每个类(class)都在自己的目录中。每节课至少会有一个lesson.cpp和一个main.cpp文件,可能还有其他的文件,具体有没有我不知道已发货——最终用户将创建这些。它看起来像这样:

all_lessons/
    helloworld/
        lesson.cpp
        main.cpp
    even_or_odd/
        lesson.cpp
        main.cpp
    calculator/
        lesson.cpp
        main.cpp
        user_created_add.cpp

这些中的每一个都需要根据几乎相同的规则进行编译,编译命令应该可以从类(class)目录之一(helloworld/ 等)运行。

由于项目的其余部分是使用 Scons 构建的,因此将它用于这一部分也是有意义的。然而,Scons 在它运行的目录中搜索 SConstruct 文件:在每个类(class)目录中放置一个 SConstruct 文件,加上一个 SConscript 是否可以接受 在给出一般规则的 all_lessons/ 目录中?这似乎违背了 Scons 期望组织项目的典型方式:这种方法的潜在缺陷是什么?我可以放一个 SConstruct 文件而不是 SConscript 文件,从而可以从任一目录构建(我猜是使用导出来避免无休止的递归)吗?

此外,有时我可能想用生成必要文件的 lesson.py 替换 lesson.cpp; Scons 是否允许我使用构建器轻松完成此操作,或者是否有更方便的框架?

最后,我想得到以下结果(或不同构建系统的等效结果):

all_lessons/
    SConstruct
    helloworld/
        SConstruct
        lesson.cpp
        main.cpp
    even_or_odd/
        SConstruct
        lesson.py
        main.cpp
    calculator/
        SConstruct
        lesson.cpp
        main.cpp
        user_created_add.cpp

all_lessons 目录中运行 scons all 需要:

  • 运行even_or_odd/lesson.py生成even_or_odd/lesson.cpp
  • 意识到 user_created_add.cpp 也需要编译。
  • 为每节课制作一个可执行文件。

even_or_odd/ 中运行 scons,或在 all_lessons/ 中运行 scons even_or_odd 应该会生成与上面一个(相同的编译标志)。

总结:

  1. Scons 是否适合/能够做到这一点?
  2. SConscript 文件位于 SConstruct 文件之上时,Scons 是否正常工作?
  3. Scons 是否可以很好地处理一个项目的多个 SConstrcut 文件,相互 SConscripting?
  4. Scons构建系统是否适合使用Python脚本生成C++文件?
  5. 使用不同的构建系统/编写我自己缺少的构建框架是否有任何优势?

当然,欢迎任何进一步的评论。

谢谢。

最佳答案

您实际上可以使用几行 GNU Make 来完成此操作。

下面是两个 makefile,它们允许从 all_lessons 目录和各个项目目录构建和清理。它假定该目录中的所有 C++ 源代码都包含一个以其目录命名的可执行文件。从顶级源目录 (all_lessons) 构建和清理时,它会构建并清理所有项目。从项目目录构建和清理时,它只会构建和清理项目的二进制文件。

这些 makefile 还会自动生成依赖项并且完全可并行化(make -j 友好)。

对于以下示例,我使用了与您相同的源文件结构:

$ find all_lessons
all_lessons
all_lessons/even_or_odd
all_lessons/even_or_odd/main.cpp
all_lessons/Makefile
all_lessons/helloworld
all_lessons/helloworld/lesson.cpp
all_lessons/helloworld/main.cpp
all_lessons/project.mk
all_lessons/calculator
all_lessons/calculator/lesson.cpp
all_lessons/calculator/user_created_add.cpp
all_lessons/calculator/main.cpp

为了能够从单个项目目录构建 project.mk 必须首先将符号链接(symbolic link)为 project/Makefile

[all_lessons]$ cd all_lessons/calculator/
[calculator]$ ln -s ../project.mk Makefile
[helloworld]$ cd ../helloworld/
[helloworld]$ ln -s ../project.mk Makefile
[even_or_odd]$ cd ../even_or_odd/
[even_or_odd]$ ln -s ../project.mk Makefile

让我们构建一个项目:

[even_or_odd]$ make
make -C .. project_dirs=even_or_odd all
make[1]: Entering directory `/home/max/src/all_lessons'
g++ -c -o even_or_odd/main.o -Wall -Wextra   -MD -MP -MF even_or_odd/main.d even_or_odd/main.cpp
g++ -o even_or_odd/even_or_odd even_or_odd/main.o  
make[1]: Leaving directory `/home/max/src/all_lessons'
[even_or_odd]$ ./even_or_odd
hello, even_or_odd

现在构建所有项目:

[even_or_odd]$ cd ..
[all_lessons]$ make
g++ -c -o calculator/lesson.o -Wall -Wextra   -MD -MP -MF calculator/lesson.d calculator/lesson.cpp
g++ -c -o calculator/user_created_add.o -Wall -Wextra   -MD -MP -MF calculator/user_created_add.d calculator/user_created_add.cpp
g++ -c -o calculator/main.o -Wall -Wextra   -MD -MP -MF calculator/main.d calculator/main.cpp
g++ -o calculator/calculator calculator/lesson.o calculator/user_created_add.o calculator/main.o  
g++ -c -o helloworld/lesson.o -Wall -Wextra   -MD -MP -MF helloworld/lesson.d helloworld/lesson.cpp
g++ -c -o helloworld/main.o -Wall -Wextra   -MD -MP -MF helloworld/main.d helloworld/main.cpp
g++ -o helloworld/helloworld helloworld/lesson.o helloworld/main.o  
[all_lessons]$ calculator/calculator 
hello, calculator
[all_lessons]$ helloworld/helloworld
hello, world

清理一个项目:

[all_lessons]$ cd helloworld/
[helloworld]$ make clean
make -C .. project_dirs=helloworld clean
make[1]: Entering directory `/home/max/src/all_lessons'
rm -f helloworld/lesson.o helloworld/main.o helloworld/main.d helloworld/lesson.d helloworld/helloworld
make[1]: Leaving directory `/home/max/src/all_lessons'

清理所有项目:

[helloworld]$ cd ..
[all_lessons]$ make clean
rm -f calculator/lesson.o calculator/user_created_add.o calculator/main.o even_or_odd/main.o helloworld/lesson.o helloworld/main.o calculator/user_created_add.d calculator/main.d calculator/lesson.d even_or_odd/main.d  calculator/calculator even_or_odd/even_or_odd helloworld/helloworld

生成文件:

[all_lessons]$ cat project.mk 
all :
% : forward_ # build any target by forwarding to the main makefile
    $(MAKE) -C .. project_dirs=$(notdir ${CURDIR}) $@
.PHONY : forward_

[all_lessons]$ cat Makefile 
# one directory per project, one executable per directory
project_dirs := $(shell find * -maxdepth 0 -type d )

# executables are named after its directory and go into the same directory
exes := $(foreach dir,${project_dirs},${dir}/${dir})

all : ${exes}

#  the rules

.SECONDEXPANSION:

objects = $(patsubst %.cpp,%.o,$(wildcard $(dir ${1})*.cpp))

# link
${exes} : % : $$(call objects,$$*) Makefile
    g++ -o $@ $(filter-out Makefile,$^) ${LDFLAGS} ${LDLIBS}

# compile .o and generate dependencies
%.o : %.cpp Makefile
    g++ -c -o $@ -Wall -Wextra ${CPPFLAGS} ${CXXFLAGS} -MD -MP -MF ${@:.o=.d} $<

.PHONY: clean

clean :
    rm -f $(foreach exe,${exes},$(call objects,${exe})) $(foreach dir,${project_dirs},$(wildcard ${dir}/*.d)) ${exes}

# include auto-generated dependency files
-include $(foreach dir,${project_dirs},$(wildcard ${dir}/*.d))

关于c++ - 使用相似规则构建多个可执行文件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7123431/

相关文章:

c++ - typedef 和非简单类型说明符

C++ : forward declaring const with internal linkage

android - 统一错误 : Exporting to Android

Opencv 静态构建、jpeg、png、tiff 不是静态链接的?

visual-studio-2010 - TFS 2010 - 根据环境排除部署时的特定文件?

c++ - 如何在 QML 文件中使用属性名称作为字符串查找 QML 上下文属性

c++ - std::strtol 和 std::stoi 之间有什么区别?

vue.js - 在 Nuxt.js 中仅构建特定页面

ios - 如何在 xcode 4 中完全自动化构建到 .ipa 文件

msbuild - TFS2010 中的门控 checkin