пятница, 9 августа 2013 г.

[prog] Способ управления расположением результатов компиляции в Mxx_ru

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

В Mxx_ru есть возможность управлять размещением результатов компиляции C/C++ проектов (как то: объектные файлы, исполнимые файлы, динамически-загружаемые библиотеки, статические библиотеки, библиотеки импорта, res-файлы). Это осуществляется посредством методов obj_placement и global_obj_placement, которые доступны внутри target-а. Для того, чтобы изменить способ размещения результатов компиляции нужно создать соответствующий объект и передать его в метод obj_placement/global_obj_placement. Например:

MxxRu::Cpp::exe_target {
   target 'hello_world'

   obj_placement MxxRu::Cpp::CustomSubdirObjPlacement.new(
      'bin32',
      'tmp32' )

   ...
}

В результате этого все объектные файлы, относящиеся к цели 'hello_world' будут помещены в подкаталог tmp32 текущего каталога. А исполнимый файл hello_world (или hello_world.exe) будет помещен в подкаталог bin32 текущего каталога.

Если бы вместо метода obj_placement был вызван метод global_obj_placement, то эти настройки применялись бы не только к цели hello_world, но и ко всем другим целям, которые строятся вместе с hello_world. Обычным местом для обращения к global_obj_placement является корневой проектный файл build.rb.

В текущих версиях Mxx_ru есть три готовых реализации ObjPlacement, которые готовы к использованию "из коробки":

  • SourceSubdirObjPlacement. В этом случае объектные файлы размещаются в подкаталогах рядом с исходными файлами;
  • CustomSubdirObjPlacement (упомянут выше). В этом случае отдельно от всего остального размещаются конечные результаты компиляции (exe/dll/lib/so/a-файлы) и промежуточные файлы (obj/o/res);
  • RuntimeSubdirObjPlacement. В этом случае создается отдельные деревья подкаталогов для разных режимов компиляции: для RELEASE, DEBUG и DEFAULT билдов.

По умолчанию используется вариант SourceSubdirObjPlacement. Потому, что для меня он очень долго был самым удобным: обычно на конкретной рабочей станции или на инстансе виртуальной машины я использовал всего один компилятор и собирал проекты всегда в RELEASE-режиме (крайне редко использовал DEBUG-сборки в последние 10-15 лет).

Но вот сейчас, в связи с возобновлением активного собственного участия в разработке SObjectizer, ситуация несколько изменилась: рабочая станция в распоряжении всего одна, а проверять проект нужно под разными компиляторами. В таких условиях SourceSubdirObjPlacement не подходит. Ведь для того, чтобы, например, после проверки 64-битовым VC++ перейти к проверке 64-х битовым MinGW, а затем еще и к 32-х битовому VC++, нужно делать полные clean-апы, а потом полные ребилды новым компилятором. Если нужно проверить небольшую правку в исходниках с прогоном unit-тестов, то это явно не вариант.

В таких условиях на помощь приходит RuntimeSubdirObjPlacement. Ему в конструктор можно передать имя корня дерева каталогов. Если это имя строить с учетом текущего тулсета, то как раз получается, что результаты работы разных компиляторов будут находиться в разных каталогах и не будут мешать друг другу. Я сейчас задействую эту возможность следующим образом:

version = toolset.tag( 'ver_hi''x' ) + '_' + toolset.tag( 'ver_lo''x' )
arch = ENV'Platform' ] ? ENV'Platform' ] : 'generic'
global_obj_placement MxxRu::Cpp::RuntimeSubdirObjPlacement.new(
 '_' + toolset.name + '_' + version + '_' + arch )

Такой вариант позволяет получать имена каталогов вроде '_vc_10_0_X64' и '_gcc_x_x_generic'.

Это работает, но выглядит не очень красиво. На данный момент у toolset-а нет метода, который бы построил какое-то довольно уникальное развернутое имя toolset-а. Штатный метод toolset.name возвращает сокращенные имена семейств инструментов, вроде vc или gcc. Поэтому сейчас я строю такое уникальное имя вручную на основании знаний о некоторых деталях реализации тулсетов и особенностей компиляторов.

Хочется эту ситуацию улучшить. Пока есть идея ввести в состав toolset-а метод describe или make_descriptive_name, который бы возвращал готовый результат, пригодный для передачи в RuntimeSubdirObjPlacement. Чтобы все эти специфические для компилятора детали обрабатывались во внутренностях toolset-а. Там больше возможностей для того, чтобы, скажем, запустить 'gcc -v', распарсить его результат и получить что-то вроде: gcc__4_7_1_tdm64_1__x86_64-w64-mingw32 или gcc__4_8_1_GCC__x86_64-pc-cygwin.

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

Комментариев нет: