使用类似规则构建多个可执行文件 我写的是C++的交互式教程。本教程将由两部分组成:一部分编译到库中(我正在使用Scons构建库),另一部分(课程)随教程一起提供,由最终用户编译。我目前正在为人们寻找一个好的、简单的方法来构建这些课程

使用类似规则构建多个可执行文件 我写的是C++的交互式教程。本教程将由两部分组成:一部分编译到库中(我正在使用Scons构建库),另一部分(课程)随教程一起提供,由最终用户编译。我目前正在为人们寻找一个好的、简单的方法来构建这些课程,c++,build,build-automation,scons,C++,Build,Build Automation,Scons,基本上,第二部分是一个包含所有课程的目录,每个课程都在自己的目录中。每节课至少有一个lesson.cpp和一个main.cpp文件,可能还有其他文件,在发货之前我不知道是否存在这些文件——最终用户将创建这些文件。它看起来像这样: all_lessons/ helloworld/ lesson.cpp main.cpp even_or_odd/ lesson.cpp main.cpp calculator/

基本上,第二部分是一个包含所有课程的目录,每个课程都在自己的目录中。每节课至少有一个
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
其中每一个都需要根据几乎相同的规则进行编译,编译命令应该可以从其中一个课程目录(
helloworld/
等)运行

鉴于项目的其余部分都是使用SCON构建的,因此在这一部分中使用它也是有意义的。但是,Scons会在其运行的目录中搜索
SConstruct
文件:是否可以在每个课程目录中放置
SConstruct
文件,并在
all_lessons/
目录中添加一个
SConscript
,以给出一般规则?这似乎与Scons期望项目组织的典型方式背道而驰:这种方法的潜在陷阱是什么?我是否可以放置一个sconstuct文件而不是SConscript文件,从而可以从任意一个目录进行构建(我猜是使用导出来避免无休止的递归)

另外,我可能在某个时候想用生成必要文件的
lessource.py
替换
lessource.cpp
;Scons是否允许我轻松地使用构建器来实现这一点,或者是否有一个更方便的框架

最后,我希望以以下内容结束(或使用不同构建系统的等效内容):

运行
all\u lessons
目录中的
scons all
需要:

  • 运行
    偶数或奇数/lesson.py
    生成
    偶数或奇数/lesson.cpp
  • 意识到
    user\u created\u add.cpp
    也需要编译
  • 为每节课生成一个可执行文件
偶数或奇数
中运行
scons
,或在
所有课程
中运行
scons偶数或奇数
时,应生成与上面相同的可执行文件(相同的编译标志)

总结:

  • 烤饼是否适合/能够做到这一点
  • SConscript
    文件位于
    SConstruct
    文件上方时,scon是否工作正常
  • SCON是否能够很好地处理一个项目中的多个
    sconstruct
    文件,并相互说明
  • 是使用Python脚本生成C++文件的sCONS构建系统吗?
  • 使用不同的构建系统/编写自己缺少的构建框架有什么好处吗
  • 当然,欢迎提出任何进一步的意见


    谢谢。

    编译命令必须从课程目录运行吗?如果不是这样,我将亲自创建包含以下内容的所有课程/制作文件:

    lessons = helloworld even_or_odd calculator
    
    all: $(lessons)
    
    # for each $lesson, the target is $lesson/main built from $lesson/main.cpp and $lesson/lesson.cpp
    # NB: the leading space on the second line *must* be a tab character
    $(lessons:%=%/main): %/main: %/main.cpp %/lesson.cpp
       g++ -W -Wall $+ -o $@
    

    然后,所有课程都可以使用All_lessons目录中的“make”或“make All”构建,或者使用例如“make helloworld/main”的特定课程构建。

    据我所知,这是可用的最佳解决方案:

    目录的结构与此相同,但不是有多个
    SConstruct
    文件,而是每个课程都有一个
    SConscript
    文件,其中默认值会根据需要被覆盖。必要时由外部脚本生成
    SConstruct
    文件,并调用SCons

    概述:

    all_lessons/
        helloworld/
            SConscript
            lesson.cpp
            main.cpp
        even_or_odd/
            SConscript
            lesson.py
            main.cpp
        calculator/
            SConscript
            lesson.cpp
            main.cpp
            user_created_add.cpp
    
    使用
    Glob
    SConscript
    文件可以编译扩展名为
    cpp
    的所有文件。它还可以使用生成课程的构建器(调用简单命令的构建器或完全成熟的构建器),这意味着甚至可以将课程存储为元数据并当场生成

    因此,要回答这些问题:

  • 我不知道,但这不是必须的
  • 就我所见,没有(与
    SConstruct
    相关的路径问题,以及其他问题)
  • 是的,有几种选择
  • 我不知道

  • 建议方法的缺点:这确实需要单独创建元构建系统。可以指定选项的文件数量更高,而且
    SConscript
    文件有很大的错误空间。

    您实际上可以使用几行GNU Make来完成这项工作

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

    这些makefile还自动生成依赖项,并且是完全可并行的(
    make-j
    friendly)

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

    $ 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
    必须首先符号链接为
    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
    
    [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'
    
    现在构建所有项目:

    [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
    
    [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
    
    清洁一个项目:

    [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
    
    [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'
    
    清理所有项目:

    [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
    
    [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))
    
    [所有课程]$cat project.mk
    全部:
    %:forward#通过转发到主makefile来构建任何目标
    美元(MAKE)-C。。项目_dirs=$(notdir${CURDIR})$@
    .冒牌货:转发_
    [所有课程]$cat Makefile
    
    def default_build(env):
        env.Program('program', Glob('*.cpp'))
    
    env = Environment()
    env.default_build = default_build
    
    Export('env')
    env.SConscript(dirs=['calculator', 'even_or_odd', 'helloworld'])
    
    Import('env')
    env.default_build(env)
    
    # SConstruct or SConscript
    
    def getSubdirs(dir) :   
        lst = [ name for name in os.listdir(dir) if os.path.isdir(os.path.join(dir, name)) and name[0] != '.' ]
        return lst
    
    env = Environment()
    path_to_lessons = '' # path to lessons
    # configure your environment, set common rules and parameters for all lessons
    for lesson in getSubdirs(path_to_lessons) :
        lessonEnv = env.Clone()
        # configure specific lesson, for example i'h ve checked SConscript file in lesson dir
        # and if it exist, execute it with lessonEnv and append env specific settings
        if File(os.path.join(path_to_lessons, lesson, 'lesson.scons')).exists() :
            SConscript(os.path.join(lesson, 'lesson.scons', export = ['lessonEnv'])
    
        # add lesson directory to include path
        lessonEnv.Append(CPPPATH = os.path.join(path_to_lessons, lesson));
    
        lessonEnv.Program(lesson, Glob(os.path.join(path_to_lessons, lesson, '*.cpp'))