Создание нескольких исполняемых файлов с похожими правилами - PullRequest
5 голосов
/ 19 августа 2011

Я пишу что-то как интерактивный учебник для C ++.Учебное пособие будет состоять из двух частей: одна скомпилирована в библиотеку (для ее создания я использую Scons), а другая (уроки) поставляется с учебником, который должен быть скомпилирован конечным пользователем.В настоящее время я ищу хороший и простой способ для людей создавать эти уроки.

По сути, вторая часть - это каталог со всеми уроками в нем, каждый в своем собственном каталоге.Каждый урок будет содержать как минимум файл 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/ и т. д.).

Ввиду того, что остальная часть проекта построена с использованием Scons, имеет смысл использовать его и для этой части.Тем не менее, Scons ищет файл SConstruct в каталоге, из которого он запущен: допустимо ли помещать файл SConstruct в каждый каталог урока, плюс SConscript в каталог all_lessons/, который дает общие правила?Похоже, это идет вразрез с типичным способом, которым Scons ожидает организации проектов: каковы потенциальные подводные камни этого подхода?Могу ли я поместить файл SConstruct вместо файла SConscript и тем самым сделать возможным сборку из любого каталога (я полагаю, с помощью экспорта, чтобы избежать бесконечной рекурсии)?

Кроме того, я могу в какой-то момент захотетьзаменить lesson.cpp на lesson.py, который генерирует необходимые файлы;Позволят ли Scons мне легко это делать с компоновщиками, или есть фреймворк, который был бы более удобен?

В конце я хочу получить следующее (или эквивалентное для разных систем сборки):1023 *

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

Для запуска scons all в каталоге all_lessons потребуется:

  • Запуск even_or_odd/lesson.py для генерации even_or_odd/lesson.cpp.
  • Поймите, что *Также необходимо скомпилировать 1035 *.
  • Создать исполняемый файл для каждого урока.

Запуск scons в even_or_odd/ или scons even_or_odd в all_lessons/ должен привести кисполняемый файл, идентичный приведенному выше (те же флаги компиляции).

Резюме:

  1. Подходят ли Scons для этого?
  2. Работает ли Scons хорошо, когда SConscript файлов больше SConstruct файлов?
  3. Хорошо ли работает Scons с несколькими файлами SConstrcut для одного проекта, SC-сценарием друг друга?
  4. Подходит ли система компоновщика Scons для использования скриптов Pythonгенерировать файлы C ++?
  5. Есть ли преимущество использования другогосборка системы / написание моей собственной сборки, которую мне не хватает?

Любые дальнейшие комментарии, конечно, приветствуются.

Спасибо.

Ответы [ 5 ]

10 голосов
/ 06 сентября 2011

Вы можете сделать это с помощью нескольких строк GNU Make.

Ниже приведены два make-файла, которые позволяют создавать и очищать каталоги all_lessons и каталоги отдельных проектов.Предполагается, что все источники C ++ в этом каталоге содержат исполняемый файл, который получает имя в соответствии с его каталогом.При сборке и очистке из исходного каталога верхнего уровня (all_lessons) он создает и очищает все проекты.При сборке и очистке из каталога проекта он только создает и очищает двоичные файлы проекта.

Эти make-файлы также автоматически генерируют зависимости и полностью параллелизуемы (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 first

[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

Makefiles:

[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))
1 голос
/ 18 мая 2012

Как упражнение в изучении сконов, я попытался ответить на ваш вопрос. К сожалению, я не эксперт, поэтому не могу сказать вам, как лучше / лучше, но вот способ, который работает.

  1. Scons является подходящим для / способным к этому. (Это именно то, для чего нужны инструменты сборки.)
  2. Не применимо. (Я не знаю.)
  3. Scons , кажется, хорошо работает с несколькими файлами SConstrcut для одного проекта, SC, описывающими друг друга.
  4. Система компоновщика Scons может использовать скрипты Python для генерации файлов C ++.
  5. Другая система сборки? Каждому свое.

Используя определенную вами иерархию, в каждой папке есть файл SConstruct. Вы можете запустить scons в подпапке, чтобы построить этот проект, или на верхнем уровне, чтобы построить все проекты (не уверен, как вы будете использовать псевдоним "all" для сборки по умолчанию). Вы можете запустить scons -c, чтобы очистить проект, и scons автоматически определит, какие файлы он создал, и очистит их (включая сгенерированный lesson.cpp).

Однако, если вы хотите, чтобы флаги компилятора распространялись из файла верхнего уровня вниз, я думаю, что лучше использовать файлы SConscript - за исключением того, что я не уверен в том, что они будут компилироваться самостоятельно.

. / SConstruct

env = Environment()
env.SConscript(dirs=['calculator', 'even_or_odd', 'helloworld'], name='SConstruct')

. / Калькулятор / SConstruct и ./calculator/helloworld

env = Environment()
env.Program('program', Glob('*.cpp'))

. / Even_or_odd / SConstruct

env = Environment()

def add_compiler_builder(env):
    # filename transformation
    suffix = '.cpp'
    src_suffix = '.py'

    # define the build method
    rule = 'python $SOURCE $TARGET'

    bld = Builder(action = rule,
                  suffix = suffix,
                  src_suffix = src_suffix)
    env.Append(BUILDERS = {'Lesson' : bld})

    return env

add_compiler_builder(env)

env.Lesson('lesson.py')
env.Program('program', Glob('*.cpp'))

Использование SConscripts

Я преобразовываю SConstructs подпапки в SConscripts и могу вывести особенности построения кода из подпапок, но затем вам нужно запустить scons -u, чтобы встроить подпапку (для поиска вверх корневой SConstruct).

. / SConstruct

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'])

. / Helloworld / SConscript и т.д ...

Import('env')
env.default_build(env)
0 голосов
/ 18 мая 2012

Вот мой путь.

# 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'))

Теперь у вас есть:

  • env - ядро ​​Среда, содержащая общие правила и параметры для всех уроков
  • lessonEnv- клон ядра env, но если у вас есть lesson.scons в определенной директории урока, вы можете дополнительно настроить эту среду или переписать некоторые параметры.
0 голосов
/ 06 сентября 2011

Насколько я нашел, это лучшее доступное решение:

Каталог структурирован таким же образом, но вместо нескольких файлов 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 будут скомпилированы.Он также может использовать конструктор (либо вызывающий простую команду, либо полноценный), который сгенерирует урок, то есть можно даже просто сохранить урок в виде метаданных и сгенерировать его на месте.

Итак, чтобы ответить на вопросы:

  1. Да.
  2. Не знаю, но для этого это не требуется.
  3. Какнасколько я видел, нет (проблемы с путями относительно SConstruct, среди прочего).
  4. Да, с несколькими доступными опциями.
  5. Я не знаю.

Недостатки предложенного подхода: для этого требуется отдельно создать систему мета-сборки.Количество файлов, в которых можно указать параметры, выше, а файлы SConscript дают много места для ошибки.

0 голосов
/ 19 августа 2011

Обязательно ли запускать команду компиляции из каталога урока? Если нет, то я бы лично создал all_lessons / makefile со следующим содержимым:

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 $@

Все уроки можно затем создать с помощью команд "make" или "make all" в каталоге all_lessons или с помощью конкретного урока, например, с. "make helloworld / main".

...