Глава 6
Дополнительные возможности и особенности

6.1 Аргумент mxx-show-cmd

По-умолчанию, Mxx_ru в процессе своей работы не отображает информации о своих действиях — работает по принципу черного ящика. В больших проектах или при отладке проектных файлах может быть интересно или полезно знать, какие именно команды запускаются. Если передать интерпретатору Ruby аргумент --mxx-show-cmd, то на стандартный поток вывода будут отображаться названия запускаемых команд и их параметры. Например, при компиляции примера из 3.4 на стр.  22 компилятором GNU C++ под Cygwin, аргумент --mxx-show-cmd приведет к следующей печати:


  ruby build.rb --mxx-show-cmd
  <<< g++ -c -o say/o/say.o -I. say/say.cpp >>>
  <<< ar -r  lib/libsay.a say/o/say.o >>>
  <<< g++ -c -o inout/o/inout.o -DINOUT_PRJ -I. inout/inout.cpp >>>
  <<< g++ -shared --shared-libgcc -o ./libinout.so -Llib inout/o/inout.o \
  -lstdc++ -lsay -Wl,--out-implib=lib/libinout.a,--export-all-symbols >>>
  <<< g++ -c -o main/o/main.o -I. main/main.cpp >>>
  <<< g++ --shared-libgcc -o ./exe_dll_lib.exe -Llib main/o/main.o -lstdc++ \
  -linout  >>>

6.2 Аргумент mxx-keep-tmps

Для формирования командных строк некоторых инструментов на некоторых платформах требуется создание т.н. response-файлов. Это временные текстовые файлы, которые содержат значения параметров для запускаемого инструмента. В этих случаях Mxx_ru создает временные файлы с уникальными именами, осуществляет запуск необходимых инструментов и затем, после окончания обработки проектного файла, уничтожает созданные временные файлы. Например, компиляция примера из 3.4 на стр.  22 компилятром Visual C++ приводит к запуску следующих команд:


  ruby build.rb --mxx-show-cmd
  <<< cl @tmpmxx_ru.3804.1 >>>
  say.cpp
  <<< lib @tmpmxx_ru.3804.2 >>>
  <<< cl @tmpmxx_ru.3804.3 >>>
  inout.cpp
  <<< link @tmpmxx_ru.3804.4 >>>
     Creating library lib/inout.lib and object lib/inout.exp
  <<< cl @tmpmxx_ru.3804.5 >>>
  main.cpp
  <<< link @tmpmxx_ru.3804.6 >>>

Здесь файлы с именами tmpmxx_ru.[0-9]+.[0-9]+ — это временные файлы, созданные Mxx_ru.

Иногда, при отладке проектов или новых toolset бывает полезно сохранить временные файлы для последующего анализа. Достигается это указанием интерпретатору Ruby аргумента --mxx-keep-tmps:


  ruby build.rb --mxx-keep-tmps

6.3 Аргумент mxx-show-tmps

Аргумент --mxx-show-tmps является аналогом аргумента --mxx-show-cmd, но для временных response-файлов. Если этот аргумент передан интрепритатору Ruby, то Mxx_ru после формирования очередного временного файла отображает на стандартный поток вывода содержимое этого файла. Так, компиляция примера из 3.4 на стр.  22 компилятором Visual C++ с аргументами --mxx-show-cmd, --mxx-show-tmps приведет к следующему результату:


  <<<[tmpmxx_ru.896.1]  -c -TP -Fosay/o/say.obj -nologo -ML -I. -GX \
  say/say.cpp>>>
  <<< cl @tmpmxx_ru.896.1 >>>
  say.cpp
  <<<[tmpmxx_ru.896.2]  /NOLOGO /OUT:lib/say.lib say/o/say.obj>>>
  <<< lib @tmpmxx_ru.896.2 >>>
  <<<[tmpmxx_ru.896.3]  -c -TP -Foinout/o/inout.obj -nologo -ML -LD \
  -DINOUT_PRJ -DINOUT_MSWIN -I. -GX inout/inout.cpp>>>
  <<< cl @tmpmxx_ru.896.3 >>>
  inout.cpp
  <<<[tmpmxx_ru.896.4]  /DLL /NOLOGO /SUBSYSTEM:CONSOLE /OUT:./inout.dll \
  /IMPLIB:lib/inout.lib /LIBPATH:lib inout/o/inout.obj say.lib  >>>
  <<< link @tmpmxx_ru.896.4 >>>
     Creating library lib/inout.lib and object lib/inout.exp
  <<<[tmpmxx_ru.896.5]  -c -TP -Fomain/o/main.obj -nologo -ML -I. -GX \
  main/main.cpp>>>
  <<< cl @tmpmxx_ru.896.5 >>>
  main.cpp
  <<<[tmpmxx_ru.896.6]  /NOLOGO /SUBSYSTEM:CONSOLE /OUT:./exe_dll_lib.exe \
  /LIBPATH:lib main/o/main.obj inout.lib  >>>
  <<< link @tmpmxx_ru.896.6 >>>

6.4 Аргумент mxx-dry-run

Если интерпретатору Ruby передан аргумент --mxx-dry-run, то Mxx_ru выполняет имитацию построения проекта. Т.е. обрабатываются все проекты, формируются командные строки для запуска всех инструментов, включая создание response-файлов, но сами инструменты не запускаются.

Этот аргумент удобно использовать для отладки проектных файлов и новых toolset в сочетании с аргументами --mxx-show-cmd, --mxx-show-tmps, --mxx-keep-tmps.

6.5 Исключения

Язык Ruby является объектно-ориентированным языком, в котором широко используется механизм исключений. Например, когда интерпретатор Ruby встречает в скрипте синтаксическую ошибку, то генерируется исключение. Сам Mxx_ru так же использует исключения для информирования об обнаруженных им ошибках.

Особенностью использования Mxx_ru является то, что в выбранной архитектуре проектных файлов не все иключения можно перехватить средствами Mxx_ru. В результате, например, из-за синтаксической ошибке в проектном файле, интерпретатор может выдать подробный stack-trace:


  mxx_ru/abstract_target.rb:187:in ‘require’: ./say/prj.rb:9: \
  syntax error (SyntaxError)
   from mxx_ru/abstract_target.rb:187:in ‘required_prj’
   from ./inout/prj.rb:8
   from ./inout/prj.rb:4:in ‘instance_eval’
   from mxx_ru/cpp/target.rb:1256:in ‘instance_eval’
   from mxx_ru/cpp/target.rb:1256:in ‘initialize’
   from ./inout/prj.rb:4:in ‘new’
   from ./inout/prj.rb:4
   from mxx_ru/abstract_target.rb:187:in ‘require’
    ... 11 levels...
   from mxx_ru/cpp/composite.rb:21:in ‘instance_eval’
   from mxx_ru/cpp/composite.rb:21:in ‘initialize’
   from build.rb:4:in ‘new’
   from build.rb:4

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

Если же Mxx_ru в состоянии перехватить исключение, то Mxx_ru делает это и отображает на стандартный поток ошибок только описание перехваченного исключения. Например:


  <<< cl @tmpmxx_ru.2324.1 >>>
  say.cpp
  say\say.cpp(14) : fatal error C1075: end of file found before the left \
  brace ’{’ at ’say\say.cpp(11)’ was matched
  <<<[Mxx_ru::Build_ex] Build error: ’cl @tmpmxx_ru.2324.1’ returns ’512’>>>

6.6 Подключение в проект make-правил

Основная идея Mxx_ru состоит в том, чтобы упростить наиболее типовые действия при компиляции проектов. Поэтому Mxx_ru предоставляет набор готовых шаблонов, в которые необходимо лишь подставить собственные данные. Однако, бывают случаи когда возможностей шаблонов не хватает. Например, в текущей версии Mxx_ru нет поддержки средств локализации Qt-приложений. Если в Qt-проекте потребуется, скажем, сгенерировать .qm-файл из готового .ts-файла, то окажется, что в существующих шаблонах Mxx_ru нет инструмента для выполнения именно этого действия.

Хорошим выходом из этой ситуации стало бы создание для Mxx_ru необходимой возможности с тем, чтобы ее можно было использовать затем и в других Qt-проектах. Но очевидно, что не каждый использующий Mxx_ru разоработчик захочет дорабатывать Mxx_ru. В таких случаях остается только воспользоваться классом генератора Mxx_ru::Makestyle_generator, который позволяет подключить в проект произвольное make-правило:


1  ...
2  require ’mxx_ru/makestyle_generator’
3  ...
4  generator(
5   Mxx_ru::Makestyle_generator.new(
6   "etc/wms_ctl_1.ru.qm", "etc/wms_ctl_1.ru.ts",
7   "lrelease etc/wms_ctl_1.ru.ts -qm etc/wms_ctl_1.ru.qm" ) )

В данном примере создается make-правило по генерации файла etc/wms_ctl_1.ru.qm (цель make-правила) из файла etc/wms_ctl_1.ru.ts (зависимость для цели make-правила) при помощи команды lrelease из состава Qt. Когда запускается построение проекта, содержащего данное правило, генератор Mxx_ru::Makestyle_generator проверяет существование файла .qm. Если его нет, или он изменялся раньше, чем файл .ts, то запускается lrelease. Т.е. выполняется логика обычного make-правила в обычном make. При выполнении операции clean (очистки проекта) файл etc/wms_ctl_1.ru.qm уничтожается.

Однако, в отличии от обычных make-файлов, заданные с помощью генератора Mxx_ru::Makestyle_generator правила выполняются до основных действий проектного файла. И, кроме того, выполняются строго в том порядке, в котором были созданы генераторы. Поэтому следующий пример:


1  generator(
2   Mxx_ru::Makestyle_generator.new( "c", "b", "make_c" ) )
3  generator(
4   Mxx_ru::Makestyle_generator.new( "b", "a", "make_b" ) )

не будет работать при отсутствии файла b, т.к. первый генератор просто не будет знать, что файл b строится по правилам второго генератора. И это именно то поведение, которое задумывалось при реализации Mxx_ru::Makestyle_generator — Mxx_ru не является традиционным make, и все сложные операции должны выполняться посредством специально созданных для этих операций инструментов Mxx_ru. А генератор Mxx_ru::Makestyle_generator выступает лишь в качестве спасательного круга когда таких инструментов еще нет.

Конструктор класса Mxx_ru::Makestyle_generator получает четыре аргумента (последний аргумент опциональный):

a_target_files
имена файлов-целей make-правила.
a_dependencies
имена файлов зависимостей для файлов-целей.
a_build_cmds
список команд для построения файлов-целей.
a_clean_cmds
список команд для удаления файлов-целей при выполнении операции clean. Если этот аргумент не указан, то файлы-цели удаляются автоматически.

Если аргументу конструктора Mxx_ru::Makestyle_generator требуется передать всего одно значение, то его можно передать в виде строки (как было показано выше). Если же аргументу требуется передать несколько значений, то их нужно указывать как вектор:


1  generator(
2   Mxx_ru::Makestyle_generator.new(
3   # Имена файлов целей.
4   [ "a", "b", "c" ],
5   # Единственная зависимость для всех целей.
6   "d",
7   # Команды для построения целей.
8   [
9   "make_a",
10   "make_b_c"
11   ],
12   # Команды для удаления целей.
13   [
14   "destroy_a",
15   "destroy_b",
16   "destroy_c"
17   ]
18   ) )

При выполнении построения файлов-целей генератор Mxx_ru::Makestyle_generator поочередно запускает команды из аргумента a_build_cmds. Если очередная команда возвращает ненулевой код возврата, то построение целей прерывается исключением. При выполнении операции clean коды возвратов команд из a_clean_cmds игнорируются.

Hosted by uCoz