понедельник, 25 апреля 2016 г.

[prog] Mxx_ru-1.6.11 с первой версией поддержки CMake-проектов

Mxx_ru обновился до версии 1.6.11

Установить Mxx_ru можно командой gem install Mxx_ru

Обновить Mxx_ru можно командой gem update Mxx_ru

Так же Mxx_ru можно загрузить с SourceForge (gem-файл).

Из мелочей: в данной версии появились новые шаблоны externals и ext-cmake-prj для mxxrugen. А так же новый тип obj_placement-а под именем ToolsetRuntimeSubdirObjPlacement, который создает имена подкаталогов с результатами компиляции вида vc14_0_19_00_23918_x64 и gccmingw_5_3_0__x86_64_w64_mingw32.

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

Сперва добавляем загрузку нужной версии soci через MxxRu::externals:

MxxRu::arch_externals :soci do |e|
  e.url 'http://downloads.sourceforge.net/project/soci/soci/soci-3.2.3/soci-3.2.3.tar.gz'

  e.map_dir 'backends' => 'dev/soci'
  e.map_dir 'cmake' => 'dev/soci'
  e.map_dir 'core' => 'dev/soci'
  e.map_file 'CMakeLists.txt' => 'dev/soci/*'
end

Далее создаем Mxx_ru-ную обертку вокруг подпроекта soci и кладем ее в каталог dev/soci_mxxru. Получается что-то вроде:

my_prj/
`- externals.rb
`- dev/            # Рабочий корень проекта.
   `- soci/        # Исходники soci, полученные с помощью MxxRu::externals.
      `- backends/
      `- cmake/
      `- core/
      `- CMakeLists.txt
   `- soci_mxxru/  # Обертка для soci.
      `- prj.rb
   ...

Внутри файла soci_mxxru/prj.rb имеем следующее:

require 'mxx_ru/cpp'

MxxRu::Cpp::ext_cmake_project {
  where 'soci'
  with WITH_BOOST:ON,
    WITH_ORACLE:OFF,
    SOCI_EMPTY:OFF,
    SOCI_SHARED:ON,
    SOCI_STATIC:OFF,
    SOCI_TESTS:OFF

  includedir_subfolder "soci"
  includedir_subfolder "soci/postgresql"

  include_path "/usr/include/postgresql"MxxRu::Cpp::Target::OPT_UPSPREAD

  lib 'soci_core'
  lib 'soci_postgresql'
}

Сейчас пойдем по частям, дабы разобраться, что там происходит. Только сперва поясню, что my_prj -- это Unix-only проект, для которого нужен soci только с поддержкой PostgresSQL. Необходимые для этого хозяйства зависимости (как то библиотека libpg и Boost) ставятся через штатный менеджер зависимостей конкретного Unix-а (в моем случае это Ubuntu с apt-get).

Итак, сперва мы указываем, где брать главный CMakeLists.txt для сборки soci: where 'soci'. Эта запись означает, что CMakeLists.txt лежит в подкаталоге soci рабочего корневого каталога проекта.

Далее в конструкции 'with' перечисляются все опции, которые нам бы пришлось указывать для cmake, если бы soci мы собирали вручную. Т.е. весь этот набор из 'WITH_BOOST', 'WITH_ORACLE', 'SOCI_EMPTY' и т.д., трансформируется в последовательность аргументов -D для cmake.

Благодаря синтаксису Ruby, перечень опций для with можно записать разными способами. Можно так, как показано выше. Можно вот так:

  with WITH_BOOST'ON',
    WITH_ORACLE'OFF',
    SOCI_EMPTY'OFF',
    SOCI_SHARED'ON',
    SOCI_STATIC'OFF',
    SOCI_TESTS'OFF'

Можно вот так:

  with :WITH_BOOST => 'ON',
    :WITH_ORACLE => 'OFF',
    :SOCI_EMPTY => 'OFF',
    :SOCI_SHARED => 'ON',
    :SOCI_STATIC => 'OFF',
    :SOCI_TESTS => 'OFF'

Ну или еще несколькими комбинациями. Тут уж как кому нравится.

Гораздо интереснее вот эти две строки:

  includedir_subfolder "soci"
  includedir_subfolder "soci/postgresql"

Они нужны вот для чего. Когда сборка посредством cmake завершается, выполняется make install. В результате чего заголовочные файлы копируются в некоторый подкаталог с именем include. При этом Mxx_ru автоматически добавляет каталог include в include_path. Но в случае с soci в include выстраивается своя собственная структура подкаталогов:

include/
`- soci/
    `- postgresql/

Для нормального использования soci нужно, чтобы include/soci, а так же include/soci/postgresql, попали в include_path. Для этого и нужны команды includedir_subfolder. Они указывают Mxx_ru что внутри includedir будут еще два подкаталога soci и soci/postgresql, которые так же должны попасть в include_path.

Инструкция include_path "/usr/include/postgresql", MxxRu::Cpp::Target::OPT_UPSPREAD предписывает явно добавить в include_path путь к заголовочным файлам libpg. Причем тут важно использование опции OPT_UPSPREAD. Это фишка Mxx_ru. Данная опция говорит о том, что данный путь к заголовкам libpg будет задан не для всех подпроектов моего проекта my_prj. А только для тех, кто прямо или косвенно зависит от soci_mxxru/prj.rb. Т.е. если я в неком проектном файле my_db_pool/prj.rb добавляю required_prj 'soci_mxxru/prj.rb', то при компиляции my_db_pool в опциях -I для компилятора будет фигурировать путь к libpg. А если в другом подпроекте my_logger/prj.rb я soci не использую, то при компиляции my_logger этот путь в опции -I добавляться не будет.

Кстати говоря, когда ext_cmake_project выполняет make install после сборки CMake-проекта, он добавляет результирующий include к include_path именно с опцией OPT_UPSPREAD. Аналогично работают и команды includedir_subfolders. Т.е. глобальный include_path всего моего проекта my_prj не будет захламлен путями к заголовочным файлам soci или libpg. Эти пути будут появляться только в тех подпроектах, которые от soci и libpg зависят.

Ну и финальные команды:

  lib 'soci_core'
  lib 'soci_postgresql'

Просто указывают, что все подпроекты, которые будут делать required_prj 'soci_mxxru/prj.rb' должны быть слинкованы с библиотеками soci_core и soci_postgresql.

Допустим, при сборке my_prj я сбрасываю результаты компиляции в подкаталог target/<build-mode> (где build-mode -- это либо release, либо debug). После компиляции в рабочем корне проекта у меня получится что-то вроде:

target/
└── release
    ├── include
    │   └── soci
    │       └── postgresql
    ├── ...
    ├── soci
    │   └── cmake_build_gcc_5_2_1__x86_64_linux_gnu_release
    │       ├── backends
    │       │   ├── CMakeFiles
    │       │   └── postgresql
    │       │       ├── CMakeFiles
    │       │       │   └── soci_postgresql.dir
    │       │       └── test
    │       │           └── CMakeFiles
    │       ├── CMakeFiles
    │       │   ├── 3.2.2
    │       │   │   ├── CompilerIdC
    │       │   │   └── CompilerIdCXX
    │       │   ├── check.dir
    │       │   └── CMakeTmp
    │       ├── core
    │       │   └── CMakeFiles
    │       │       └── soci_core.dir
    │       └── lib
    ...

По этой картинке станет более понятно, как же происходит работа с CMake-проектами.

Mxx_ru определяет, где именно будут оказываться результаты компиляции. За это в Mxx_ru отвечает такая штука, как obj_placement. В данном случае выбран такой obj_placement, который создает поддерево target/release.

В этом поддереве создаетя подкаталог для сборки soci. А в нем -- подкаталог, в котором будет работать cmake. В данном случае именя этого cmake_build-каталога выбрал сам Mxx_ru. Получилось, с учетом режима сборки и версии gcc, имя cmake_build_gcc_5_2_1__x86_64_linux_gnu_release.

Далее Mxx_ru переходит в этом cmake_build-каталог и запускает две команды:

cmake -DCMAKE_BUILD_TYPE=Release -DWITH_BOOST=ON ... -G "Unix Makefiles" path-to-my_prj-dev/soci
cmake --build . --target install

Т.е. cmake-сборка сначала конфигурируется с учетом режима сборки, типа компилятора, дополнительных опций и т.д. После чего выполняется сама сборка и последующий make install. При этом все .a/.so-файлы располагаются в target/release, а заголовочные файлы -- в target/release/include. При этом пути к библиотекам автоматически добавляются в lib_path, а пути к заголовочным файлам -- в include_path (ну об этом речь шла выше).

Если бы компиляция велась clang-ом в режиме debug, то в target была бы приблизительно такая же картинка, но с несколько другими именами:

target/
├── debug
│   ├── include
│   │   └── soci
│   │       └── postgresql
│   ├── ...
│   ├── soci
│   │   └── cmake_build_clang_3_6_2_1__x86_64_pc_linux_gnu_debug
│   │       ├── backends
│   │       │   ├── CMakeFiles
│   │       │   └── postgresql
│   │       │       ├── CMakeFiles
│   │       │       │   └── soci_postgresql.dir
│   │       │       └── test
│   │       │           └── CMakeFiles
│   │       ├── CMakeFiles
│   │       │   ├── 3.2.2
│   │       │   │   ├── CompilerIdC
│   │       │   │   └── CompilerIdCXX
│   │       │   ├── check.dir
│   │       │   └── CMakeTmp
│   │       ├── core
│   │       │   └── CMakeFiles
│   │       │       └── soci_core.dir
│   │       └── lib
│   ...

Если имена, которые Mxx_ru генерирует для cmake_build-каталога не нравятся (например, в результате пути к файлам оказываются слишком длинными, чего CMake категорически не любит), то можно задать собственное имя: build_dir_name 'cmakebld', например.

Так же, может потребоваться, чтобы заголовочные файлы после make install оказывались не в include, а в каком-то другом подкаталоге. Изменить это имя можно так: install_includedir 'my_headers'. Однако, сам my_headers всегда будет оказываться там, где на это укажет используемый в проекте obj_placement.

Вот в кратце, что сейчас позволяет делать ext_cmake_project. Это пока самая первая версия. Наверняка что-то еще будет меняться и добавляться по мере накопления опыта. Так что следите за анонсами ;)

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