пятница, 1 июня 2018 г.

[prog.c++] Попытка подружить "модульный" Boost с MxxRu::externals

Начал чесать репу на тему создания такого mchain-а или mbox-а для SObjectizer-а, который бы позволил двум SObjectizer-приложениям на одной ноде взаимодействовать друг с другом через разделяемую память. Беглый поиск по Интернету показал, что выбор инструментов для этой задачи сравнительно небольшой: либо придется делать свой собственный велосипед, либо же нужно брать что-то из Boost, ACE, POCO или чего-то сравнимого по размеру.

Писать свой лисапед не захотелось, тем более, что сперва нужно сделать какой-то прототип нового mchain-а, ибо на данный момент реализация такого mchain-а выглядит как сплошное белое пятно. Хочется без лишних проволочек начать работать над прототипом, а уже потом, когда прототип заработает, решать, что же делать дальше.

Решил попробовать Boost.Interprocess. Но т.к. с "большим" Boost-ом иметь дело не хочется, то попробовал подтащить в небольшой демо-проектик Boost.Interprocess и все его зависимости из "модульного" Boost-а, который нынче доступен на github-е.

Методом проб и ошибок это удалось сделать (в очередной раз скажу, что Boost в виде большой говнопомойки не нужен и должен быть разрушен). Результат можно увидеть в этом репозитории. Ниже чуть-чуть прокомментирую то, что получилось.

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

Во-первых, нужно получить список Boost-овых "модулей", которые нужны для Boost.Interprocess. Что оказалось не совсем просто, т.к. удобного способа узнать полный список зависимостей конкретного "модуля" я не нашел. Boostdep, при простом его использовании, выдает только прямые зависимости, но не перечисляет косвенные. В общем, здесь все решилось экспериментальным путем и парой-тройкой обращений к Boostdep.

Во-вторых, нужно было сделать список Boost-овых "модуле" в файле externals.rb. Естественно, что когда этот список стал приобретать вид сплошной копипасты:

MxxRu::arch_externals :boost_assert do |e|
  e.url 'https://github.com/boostorg/assert/archive/boost-1.67.0.zip'

  e.map_dir 'include/boost' => 'dev/boostorg/assert'
end

MxxRu::arch_externals :boost_config do |e|
  e.url 'https://github.com/boostorg/config/archive/boost-1.67.0.zip'

  e.map_dir 'include/boost' => 'dev/boostorg/config'
end

MxxRu::arch_externals :boost_container do |e|
  e.url 'https://github.com/boostorg/container/archive/boost-1.67.0.zip'

  e.map_dir 'include/boost' => 'dev/boostorg/container'
end
...

То захотелось этот объем сократить. Сперва по такому пути:

BOOST_GITHUB_PREFIX = 'https://github.com/boostorg'
BOOST_VER = '1.67.0'

MxxRu::arch_externals :boost_assert do |e|
  e.url "#{BOOST_GITHUB_PREFIX}/assert/archive/boost-#{BOOST_VER}.zip"

  e.map_dir "include/boost" => "dev/boostorg/assert"
end

MxxRu::arch_externals :boost_config do |e|
  e.url "#{BOOST_GITHUB_PREFIX}/config/archive/boost-#{BOOST_VER}.zip"

  e.map_dir "include/boost" => "dev/boostorg/config"
end

MxxRu::arch_externals :boost_container do |e|
  e.url "#{BOOST_GITHUB_PREFIX}/container/archive/boost-#{BOOST_VER}.zip"

  e.map_dir "include/boost" => "dev/boostorg/container"
end
...

Поэтому был принят еще более радикальный способ сокращения копипасты:

BOOST_GITHUB_PREFIX = 'https://github.com/boostorg'
BOOST_VER = '1.67.0'
BOOST_MODULES = %w{
   assert
   config
   container
   container_hash
   core
   date_time
   detail
   functional
   integer
   interprocess
   intrusive
   move
   mpl
   numeric_conversion
   predef
   preprocessor
   smart_ptr
   static_assert
   throw_exception
   tuple
   type_traits
   unordered
   utility
   winapi
}

BOOST_MODULES.each do |md|
   key = "boost_#{md}".to_sym
   MxxRu::arch_externals key do |e|
      e.url "#{BOOST_GITHUB_PREFIX}/#{md}/archive/boost-#{BOOST_VER}.zip"

      e.map_dir "include/boost" => "dev/boostorg/#{md}"
   end
end

Т.е. создавался массив имен "модулей" из Boost-а, в итерации по которому затем и строился список зависимостей для MxxRu.

В-третьих, в файле build.rb нужно было задать пути для поиска заголовочных файлов "модулей" из Boost-а. Перечислять их все вручную было не интересно. Поэтому сперва была сделана вот такая попытка: определение BOOST_MODULES со списком "модулей" было помещено в отдельный файл boost_modules.rb. Этот файл затем подключался в externals.rb и в build.rb.

Но такой поход оказался несовместим с логикой работы MxxRu::externals. Для MxxRu::externals нужно, чтобы вся информация о зависимостях описывалась в externals.rb. Тогда при изменении externals.rb можно определить что именно изменилось, а что осталось прежним. Но если актуальный список модулей лежит не в externals.rb, а в boost_modules.rb, то MxxRu::externals не может отследить модификацию boost_modules.rb и, например, при удалении какого-то модуля из списка исходники этого модуля из рабочего каталога MxxRu::externals не удалит.

Поэтому было сделано так, что файл boost_modules.rb генерируется при обработке externals.rb. Для чего в externals.rb цикл по списку модулей был переписан следующим образом:

File.open('dev/_boost_modules_list.rb''w'do |f|
  f << "BOOST_MODULES = %w{\n"

  BOOST_MODULES.each do |md|
    f << " #{md}\n"

    key = "boost_#{md}".to_sym
    MxxRu::arch_externals key do |e|
      e.url "#{BOOST_GITHUB_PREFIX}/#{md}/archive/boost-#{BOOST_VER}.zip"

      e.map_dir "include/boost" => "dev/boostorg/#{md}"
    end
  end

  f << "}"
end

Вот в таком виде при запуске утилиты mxxruexternals все нужные зависимости подгружаются с github-а и это делает возможным дальнейшую сборку демо-проекта без установки всего Boost-а непосредственно в систему. Что и было проверено под Windows, Linux (Ubuntu 16.04) и FreeBSD 10.3.

Так что эксперимент завершился успешно, но остаточек остался. Ну и соблазн сделать свой инструмент для работы с shared memory вместо Boost.Interprocess (в зависимостях которого обнаружился даже MPL), стал практически непреодолимым.

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