MxxRu::externals: Repositoryless Dependency Manager, Евгений Охотников
Опыт управления зависимостями в C++ проекте без использования пакетных менеджеров и централизованных репозиториев пакетов.
1. C++ CoreHard Autumn 2017
MxxRu::externals
Repositoryless Dependency Manager
Евгений Охотников
2. Откуда ноги растут?
Начало 2016-го года.
Нужно было уйти с Svn на Git или Hg.
Но Svn использовался и для управления зависимостями (svn:externals).
Аналоги svn:externals в Git и в Hg не понравились.
biicode умер, conan и hunter только-только появились, vcpkg еще не было...
Наиболее подходящим был CMake-овский ExternalProject_Add. Но сам CMake...
2
3. Чего хотелось достичь?
Взять любой чужой проект в исходниках и подключить к себе то, что из этого
проекта нужно.
Чужой проект должен жить и распространяться так, как удобно авторам
чужого проекта. Нельзя рассчитывать на то, что они сделают нужный тебе
пакет и зальют в нужный репозиторий.
Должны поддерживаться Git, Hg, Svn и архивы (zip, .tar.gz, .tar.bz2, .tar.xz)...
Работа под Windows, Linux, FreeBSD + MacOS (если повезет).
3
4. Ну и мы этого достигли
require 'mxx_ru/externals'
MxxRu::git_externals :asio do |e|
e.url 'https://github.com/chriskohlhoff/asio.git'
e.commit 'f5c570826d2ebf50eb38c44039181946a473148b'
e.map_dir 'asio/include' => 'dev/asio'
end
MxxRu::arch_externals :nodejs_http_parser do |e|
e.url 'https://github.com/nodejs/http-parser/archive/v2.7.1.tar.gz'
e.map_file 'http_parser.h' => 'dev/nodejs/http_parser/*'
e.map_file 'http_parser.c' => 'dev/nodejs/http_parser/*'
end
MxxRu::hg_externals :eigen do |ext|
ext.url = 'https://bitbucket.org/eigen/eigen'
ext.tag = '3.2.5'
ext.map_dir 'Eigen' => 'dev'
ext.map_file 'INSTALL' => 'INSTALL.eigen'
end
4
5. Забрать зависимость из Git
require 'mxx_ru/externals'
MxxRu::git_externals :asio do |e|
e.url 'https://github.com/chriskohlhoff/asio.git'
e.commit 'f5c570826d2ebf50eb38c44039181946a473148b'
e.map_dir 'asio/include' => 'dev/asio'
end
MxxRu::arch_externals :nodejs_http_parser do |e|
e.url 'https://github.com/nodejs/http-parser/archive/v2.7.1.tar.gz'
e.map_file 'http_parser.h' => 'dev/nodejs/http_parser/*'
e.map_file 'http_parser.c' => 'dev/nodejs/http_parser/*'
end
MxxRu::hg_externals :eigen do |ext|
ext.url = 'https://bitbucket.org/eigen/eigen'
ext.tag = '3.2.5'
ext.map_dir 'Eigen' => 'dev'
ext.map_file 'INSTALL' => 'INSTALL.eigen'
end
5
MxxRu::git_externals :asio do |e|
e.url 'https://github.com/chriskohlhoff/asio.git'
e.commit 'f5c570826d2ebf50eb38c44039181946a473148b'
e.map_dir 'asio/include' => 'dev/asio'
end
6. Забрать зависимость из Hg
require 'mxx_ru/externals'
MxxRu::git_externals :asio do |e|
e.url 'https://github.com/chriskohlhoff/asio.git'
e.commit 'f5c570826d2ebf50eb38c44039181946a473148b'
e.map_dir 'asio/include' => 'dev/asio'
end
MxxRu::arch_externals :nodejs_http_parser do |e|
e.url 'https://github.com/nodejs/http-parser/archive/v2.7.1.tar.gz'
e.map_file 'http_parser.h' => 'dev/nodejs/http_parser/*'
e.map_file 'http_parser.c' => 'dev/nodejs/http_parser/*'
end
MxxRu::hg_externals :eigen do |ext|
ext.url = 'https://bitbucket.org/eigen/eigen'
ext.tag = '3.2.5'
ext.map_dir 'Eigen' => 'dev/Eigen'
ext.map_file 'INSTALL' => 'INSTALL.eigen'
end
6
MxxRu::hg_externals :eigen do |ext|
ext.url = 'https://bitbucket.org/eigen/eigen'
ext.tag = '3.2.5'
ext.map_dir 'Eigen' => 'dev'
ext.map_file 'INSTALL' => 'INSTALL.eigen'
end
7. Забрать зависимость из архива
require 'mxx_ru/externals'
MxxRu::git_externals :asio do |e|
e.url 'https://github.com/chriskohlhoff/asio.git'
e.commit 'f5c570826d2ebf50eb38c44039181946a473148b'
e.map_dir 'asio/include' => 'dev/asio'
end
MxxRu::arch_externals :nodejs_http_parser do |e|
e.url 'https://github.com/nodejs/http-parser/archive/v2.7.1.tar.gz'
e.map_file 'http_parser.h' => 'dev/nodejs/http_parser/*'
e.map_file 'http_parser.c' => 'dev/nodejs/http_parser/*'
end
MxxRu::hg_externals :eigen do |ext|
ext.url = 'https://bitbucket.org/eigen/eigen'
ext.tag = '3.2.5'
ext.map_dir 'Eigen' => 'dev'
ext.map_file 'INSTALL' => 'INSTALL.eigen'
end
7
MxxRu::arch_externals :nodejs_http_parser do |e|
e.url 'https://github.com/nodejs/http-parser/archive/v2.7.1.tar.gz'
e.map_file 'http_parser.h' => 'dev/nodejs/http_parser/*'
e.map_file 'http_parser.c' => 'dev/nodejs/http_parser/*'
end
8. Как это работает?
1. Подготавливается нужная структура каталогов.
8
~/my_project
├── dev/
├── LICENSE
└── README.md
9. Как это работает?
2. В корень помещается externals.rb
9
~/my_project
├── dev/
├── externals.rb
├── LICENSE
└── README.md
require 'mxx_ru/externals'
MxxRu::git_externals :asio do |e|
e.url 'https://github.com/chriskohlhoff/asio.git'
e.commit 'f5c570826d2ebf50eb38c44039181946a473148b'
e.map_dir 'asio/include' => 'dev/asio'
end
MxxRu::arch_externals :nodejs_http_parser do |e|
e.url 'https://github.com/nodejs/http-parser/archive/v2.7.1.tar.gz'
e.map_file 'http_parser.h' => 'dev/nodejs/http_parser/*'
e.map_file 'http_parser.c' => 'dev/nodejs/http_parser/*'
end
MxxRu::hg_externals :eigen do |ext|
ext.url = 'https://bitbucket.org/eigen/eigen'
...
10. Как это работает?
3. В корне запускается mxxruexternals
10
~/my_project
├── dev/
├── externals.rb
├── LICENSE
└── README.md
$ cd ~/my_project
$ mxxruexternals
[Info] Generation of auxilary rakefile: ./mxxruexternals-temp20171011-19367-10hgydw
[Info] Launching rake with: rake -f ./mxxruexternals-temp20171011-19367-10hgydw install
mkdir -p .externals
git clone https://github.com/chriskohlhoff/asio.git .externals/asio.19369.10694860
Cloning into '.externals/asio.19369.10694860'...
11. Как это работает?
11
~/my_project
├── dev
...
└── .externals
├── asio
│ ├── asio
│ └── .git
├── eigen
│ ├── bench
...
│ └── .hg
└── nodejs_http_parser
└── contrib
mxxruexternals загружает все
зависимости в отдельный подкаталог
└── .externals
├── asio
│ ├── asio
│ └── .git
├── eigen
│ ├── bench
...
│ └── .hg
└── nodejs_http_parser
└── contrib
12. Как это работает?
12
~/my_project
├── dev
│ ├── asio
│ │ └── include
│ ├── Eigen
│ │ └── src
│ └── nodejs
│ └── http_parser
└── .externals
├── asio
│ ├── asio
│ └── .git
├── eigen
│ ├── bench
...
│ └── .hg
└── nodejs_http_parser
└── contrib
Затем mxxruexternals копирует только
то, что нам нужно и туда, куда нужно:
└── dev
├── asio
│ └── include
├── Eigen
│ └── src
└── nodejs
└── http_parser
13. Как это работает?
4. Получаем результат
13
~/my_project
├── dev
│ ├── asio
│ │ └── include
│ ├── Eigen
│ │ └── src
│ └── nodejs
│ └── http_parser
└── .externals
├── asio
│ ├── asio
│ └── .git
├── eigen
│ ├── bench
...
│ └── .hg
└── nodejs_http_parser
└── contrib
14. Ну и как оно?
Очень удобно!
Полтора года в активном использовании.
Пока не довелось попробовать с большими зависимостями вроде Boost, ICU,
Qt и т.п.
Что доводилось подключать чужого: ACE, Asio (standalone), Beast, C++REST
SDK, Catch, Pistache, RapidJSON, RestBed, SOCI, fmt, libmosquitto, spdlog
14
15. Как взять и попробовать?
Установить Ruby (и RubyGems)
Установить Mxx_ru:
gem install Mxx_ru
Поискать примеры и описания здесь: eao197.blogspot.com
До нормальной документации пока руки не дошли.
15
16. Будущее?
Не факт, что оно есть у самого MxxRu::externals...
Но может быть это кого-то подтолкнет к созданию чего-то более удачного.
Поскольку conan, hunter, cppan, vcpkg и пр. менеджеры с репозиториями
пакетов ‒ это хорошо, но вряд ли подходит для очень сегментированного
мира C++.
16