使用类似规则构建多个可执行文件

Ant*_*lov 6 c++ build-automation build scons

我写的东西像C++的互动教程.本教程将由两部分组成:一部分编译成一个库(我使用Scons构建它),另一部分(课程)随最终用户编译的教程一起提供.我正在寻找一种方便人们建立这些课程的好方法.

基本上,第二部分是一个包含所有课程的目录,每个目录都在自己的目录中.每课至少有一个lesson.cppmain.cpp文件,还可以存在其他文件,其中存在的我不知道,直到它被运后-最终用户将创建这些.它看起来像这样:

all_lessons/
    helloworld/
        lesson.cpp
        main.cpp
    even_or_odd/
        lesson.cpp
        main.cpp
    calculator/
        lesson.cpp
        main.cpp
        user_created_add.cpp
Run Code Online (Sandbox Code Playgroud)

这些中的每一个都需要根据几乎相同的规则进行编译,并且编译命令应该可以从其中一个课程目录(helloworld/等等)运行.

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

另外,我可能在某一时刻要替换lesson.cpp一个lesson.py生成所需的文件; 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
Run Code Online (Sandbox Code Playgroud)

scons allall_lessons目录中运行需要:

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

运行sconseven_or_odd/,或者scons even_or_oddall_lessons/应该产生一个可执行与上面的一个(相同的编译标志).

摘要:

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

当然,任何进一步的评论都是受欢迎的.

谢谢.

Max*_*kin 11

你可以用几行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
Run Code Online (Sandbox Code Playgroud)

为了能够从individial项目目录建设project.mk必须被符号链接作为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
Run Code Online (Sandbox Code Playgroud)

让我们构建一个项目:

[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
Run Code Online (Sandbox Code Playgroud)

现在构建所有项目:

[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
Run Code Online (Sandbox Code Playgroud)

清理一个项目:

[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'
Run Code Online (Sandbox Code Playgroud)

清理所有项目:

[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
Run Code Online (Sandbox Code Playgroud)

makefile:

[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))
Run Code Online (Sandbox Code Playgroud)

  • @Nick:如果真的是makefile就没用了,不是吗?gcc使用`-MD`选项生成完整的依赖项,需要1行makefile来包含依赖项文件. (2认同)