Сделал очередной релиз своей кроссплатформенной системы сборки C/C++ проектов: Mxx_ru версии 1.6.12
Установить Mxx_ru можно командой gem install Mxx_ru
Обновить Mxx_ru можно командой gem update Mxx_ru
Так же Mxx_ru можно загрузить с SourceForge (gem-файл).
Единственное, но важное, обновление версии 1.6.12 по сравнению с предшествующей версией 1.6.11 -- это появление класса PrjAwareRuntimeSubdirObjPlacement. Этот ObjPlacement позволяет получить возможность сборки одних и тех же исходников в lib или в so/dll прямо "из коробки".
Для того, чтобы пояснить нужность PrjAwareRuntimeSubdirObjPlacement требуется сперва объяснить роль ObjPlacement в MxxRu...
ObjPlacement в MxxRu отвечает за то, где будут располагаться промежуточные и финальные результаты компиляции/линковки. Т.е. куда пойдут объектники, куда библиотеки, куда бинарники и т.д. Если проект собирается всего одним компилятором, то смысла париться по поводу расположения результатов компиляции нет. Разве что может быть интересно, чтобы результирующие бинарники шли куда-то в подкаталог target, а объектники не валялись рядом с исходниками.
А вот если проект собирается разными компиляторами (например, несколькими версиями GCC, или GCC и VC++, или GCC и clang-ом), да еще и в разных режимах (debug/release), то тут уж приходится следить за тем, где лежат все эти файлы и не подхватит ли по ошибке текущий компилятор пару объектников, собранных совсем другим компилятором, с совсем другими параметрами. Тут уж в дело вступают ObjPlacement объекты.
Обычно создается всего один объект ObjPlacement, который в build.rb назначается в качестве глобального для всего проекта. Это означает, что все подпроекты будут размещать свои промежуточные и результирующие файлы именно там, где определит этот самый глобальный ObjPlacement. На практике этого достаточно, хотя MxxRu поддерживает и возможность задать собственный ObjPlacement для отдельного проекта (но этой возможностью не пользуется, т.к. она только лишней головной боли в больших проектах добавляет).
MxxRu предоставляет пользователю несколько готовых реализаций ObjPlacement. По умолчанию используется дефолтная реализация, которая удобна для мелких проектиков, которые собираются только одним компилятором без каких-либо вариаций. Так, все объектники идут в подкаталоги с именем o, создающиеся рядом с исходным файлом. Т.е., если компилируется файл some/prj/f.cpp, то объектник для него попадет в some/prj/o. Финальные результаты компиляции (т.е. исполнимые файлы, статические и динамические библиотеки) просто сбрасываются в текущий каталог.
Понятное дело, для чего-то более менее-сложного нужно что-то другое. Тут в дело вступает RuntimeSubdirObjPlacement. Этот ObjPlacement создает новую структуру каталогов в указанном месте. Так, если пользователь создал RuntimeSubdirObjPlacement и указал, что результирующим каталогом должен быть каталог target, то будет создан каталог target, в нем будет создан каталог release или debug, а уже в них будут созданы подкаталоги для объектников. Так, если мы компилируем файл some/prj/f.cpp в release-режиме, то объектник для него пойдет в target/release/some/prj.
RuntimeSubdirObjPlacement хорош до тех пор, пока мы работаем всего с одним компилятором. Например, пользуемся только Visual C++ одной конкретной версии. Или только GCC конкретной версии. Однако, если мы хотим использовать несколько разных компиляторов или разных версий одного компилятора, то нам нужно подумать о том, как разнести выхлопы разных компиляторов по разным местам. Мы можем либо в build.rb задавать для RuntimeSubdirObjPlacement разные имена (например, target-vc14, target-gcc-5.4.0, target-clang-3.8.0). Либо же можно воспользоваться ToolsetRuntimeSubdirObjPlacement.
Класс ToolsetRuntimeSubdirObjPlacement сам создает в указанном для него каталоге специальные покаталоги с именами, соответствующими текущему тулсету. Так, если мы создаем ToolsetRuntimeSubdirObjPlacement и передаем ему параметр target в качестве целевого каталога, то этот ObjPlacement будет создавать подкаталоги вида target/gcc_4_8_2__x86_64_w64_mingw32/release или target/vc14_0_19_00_24210_x64/debug. И уже в них будет создавать нужную систему подкаталогов. Так, объектник для файла some/prj/f.cpp может попасть в каталог target/gcc_4_8_2__x86_64_w64_mingw32/release/some/prj.
В общем, ToolsetRuntimeSubdirObjPlacement -- это хорошая штука, но до тех пор, пока мы не сталкиваемся с необходимость собрать из одних и тех же исходников как статическую, так и динамическую библиотеку. Тут проблема возникает в том, что если в эту библиотеку входит файл some/prj/f.cpp, то он должен компилироваться два раза -- один раз для статической библиотеки, второй раз для динамической. Соответственно, должно быть два объектных файла. Но это будет два объектных файла с одним и тем же именем f.o (или f.obj). И попадут эти файлы в один и тот же каталог, что не есть хорошо, т.к. в одном каталоге не может быть двух файлов с одинаковым именем.
Как раз новый класс, PrjAwareRuntimeSubdirObjPlacement, решает эту проблему. Он генерирует более сложное дерево каталогов для промежуточных результатов. На каком-то уровне этого дерева происходит развилка по имени проектного файла. Так, если у нас есть проектные файлы prj/static.rb и prj/dynamic.rb, то внутри этого дерева будет подкаталог prj_static_rb, внутри которого будут находиться все объектники, собранные для проекта prj/static.rb. И будет подкаталог prj_dynamic_rb, внутри которого будут все объектники для проекта prj/dynamic.rb. Это дает возможность в проектных файлах static.rb и dynamic.rb перечислять одни и те же исходные файлы. Это не страшно, т.к. объектники будут разнесены по разным подкаталогам.
Примеры этого дела можно посмотреть в соседнем посте.
Раньше аналогичного результата надо было добиваться вручную, через управление ObjPlacement в своих prj.rb-файлах. Теперь эта функциональность доступна в MxxRu сразу, из коробки, как говорят.
Использовать PrjAwareRuntimeSubdirObjPlacement можно вот так:
#!/usr/bin/ruby gem 'Mxx_ru', '>= 1.6.12' require 'mxx_ru/cpp' MxxRu::Cpp::composite_target( MxxRu::BUILD_ROOT ) { global_include_path "." global_obj_placement MxxRu::Cpp::PrjAwareRuntimeSubdirObjPlacement.new( 'target' ) default_runtime_mode MxxRu::Cpp::RUNTIME_RELEASE required_prj '...' ... } |
В этом случае будут формироваться каталоги вида target/release/...
Или вот так:
#!/usr/bin/ruby gem 'Mxx_ru', '>= 1.6.12' require 'mxx_ru/cpp' MxxRu::Cpp::composite_target( MxxRu::BUILD_ROOT ) { global_include_path "." global_obj_placement MxxRu::Cpp::PrjAwareRuntimeSubdirObjPlacement.new( 'target', MxxRu::Cpp::PrjAwareRuntimeSubdirObjPlacement::USE_COMPILER_ID ) default_runtime_mode MxxRu::Cpp::RUNTIME_RELEASE required_prj '...' ... } |
В последнем случе будут формироваться имена каталогов вида target/gcc_4_8_2__x86_64_w64_mingw32/release/...
Комментариев нет:
Отправить комментарий