Очень долго пользовался придуманной много лет назад файловой структурой в своих C++ных проектах. Эта структура вырабатывалась долго, методом проб и ошибок. Довольно подробно я ее описывал почти одинадцать лет назад в RSDN Magazine. С тех пор она верой и правдой служила моей команде. Но времена меняются, имеет смысл посмотреть по сторонам.
Если говорить кратко, то мы использовали в проектах вот такую организацию каталогов:
prj/
`- dev/
`- prj/
`- samples/
`- tests/
`- ...
`- doxygen/
`- ...
В каталоге dev/prj были собраны как заголовочные, так и исходные файлы. Там же лежали rb-файлы для сборки prj из исходников (мы использовали Mxx_ru в качестве кросс-платформенной, заточенной под C++, билд-системы).
Такой подход в сочетании с Subversion (мега-фича svn:externals) позволял легко и просто подключать в свой проект мелкие подпроекты в качестве зависимостей. Так, если есть проект A, который использует в качестве зависимостей проекты B, C, D и E, то для каталога A/dev задавалось свойство svn:externals вида:
http://some.host/svn/B/tags/version/dev/B B
http://some.host/svn/C/tags/version/dev/C C
http://some.host/svn/D/tags/version/dev/D D
http://some.host/svn/E/tags/version/dev/E E
В результате, при чекауте исходников проекта A файловая структура принимала вид:
A/
`- dev/
`- A/
`- B/
`- C/
`- D/
`- E/
`- samples/
`- tests/
`- ...
`- doxygen/
`- ...
Где каталоги B, C, D и E создавались и наполнялись самим Subversion-ом.
Мы находили в этой схеме много достоинств. Так, если зависимости в svn:externals были зафиксированы правильно (т.е. ссылки на tags, да еще и с указанием ревизии), то можно было поднять из репозитория точную версию любого проекта со всеми его зависимостями. На любой платформе, где был svn. Внутри проекта можно было все подпроекты компилировать и перекомпилировать в разных режимах и с нужными ключиками компилятора (при этом в соседнем каталоге могла лежать другая версия этого же проекта, но собранная с другими настройками). Плюс через svn export можно было взять нужную версию исходников со всеми зависимостей, но без svn-овской служебной информации, и упаковать все это в обычный тарболл.
Правда приходилось сторонние проекты адаптировать под такую структуру. Если проект был относительно небольшой, то это оказывалось не так уж и сложно (например, мы подобным образом поступали с pcre, libcurl, crypto++, POCO и даже ACE). Но вот с очень большими проектами, вроде Qt или Boost-а этот подход уже не работал :(
Теперь же ситуация очень изменилась. Subversion уже не торт, Git наше фсё :) Хотя по удобству прописывания зависимостей для проекта Git-овские submodules и subtree, как и Mercurial-овский subrepos и в подметки не годятся svn:externals :(
Посему приходится задумываться, как же теперь жить дальше, раз уж Subversion так не любят. Да и вообще, нонешние разработчики офигели настолько, что ленятся даже простые тарболлы для своих проектов собирать и куда-нибудь выкладывать. Вот вам ссылка на github, а дальше трахайтесь с той хренью, которая в master-е лежит, как хотите.
И вот с этим нужно что-то делать. Причем хотелось бы иметь способ, который бы позволял работать и с исходниками из DVCS, и с исходниками из тарболлов, и с исходниками из CVCS.
Пока есть следующая идея. Раз уж модные и молодежные DVCS не позволяют вытянуть из репозитория только содержимое конкретного каталога на конкретной ревизии и тащить приходится все, то можно поступать следующим образом:
структура проекта приобретает вид:
prj/ `- .externals/ `- dev/ `- prj/ `- .. `- doxygen/ `- ... `- externals.rb
- файлик prj/externals.rb содержит описание зависимостей для проекта и умеет эти зависимости подтаскивать. Например, для проекта A в A/externals.rb будет указано, что подпроекты B и C нужно брать с github-а, подпроект D -- в виде тарболла с такого-то URL, а подпроект E -- с bitbucket-а;
для подтягивания зависимостей запускается externals.rb, который извлекает зависимости из их месторасположения и укладывает в каталог prj/.externals. Укладывает именно в том виде, в котором исходники внешнего проекта удается достать. Так, если внешний проект B лежит на github.com/vasya-pupkin/B, то externals.rb запускает
git clone --depth 1 https://github.com/vasya-pupkin/B.git .externals/B.В итоге в prj/.externals образуется свалка всех внешних подпроектов со всей их требухой. Что-то вроде:
prj/ `- .externals/ `- B/ `- .git/ `- dev/ `- B/ `- samples/ `- tests/ `- ... `- C/ `- .git/ `- dev/ `- C/ `- ... `- ... `- ...
Соответственно, следующим шагом externals.rb копирует из prj/.externals только те части подпроектов, которые нам нужны, и помещает их в prj/dev. Так, содержимое prj/.externals/B/dev/B копируется в prj/dev/B, содержимое prj/.externals/C/dev/C в prj/dev/C и т.д. В итоге получаем нужную нам картинку:
prj/ `- .externals/ `- dev/ `- prj/ `- B/ `- C/ `- D/ `- E/ `- .. `- doxygen/ `- ... `- externals.rb
В принципе, что-то подобное можно делать с помощью CMake и его ExternalProject. Но, блин, CMake сам по себе мозги выворачивает, нужно долго выкуривать разрозненные куски описаний со всего Интернета, чтобы разобраться что и куда CMake размещает. А с ExternalProject эта задачка еще больше усложняется. Кроме того, CMake лишь генерирует Makefiles, что не очень удобно, если даже на одной платформе с проектом нужно работать несколькими компиляторами (особенно, когда нужно несколько версий одного и того же компилятора задействовать).
Ну а теперь собственно суть поста: народ, а поделитесь, плиз, своими подходами к организации файловой структуры C++ проектов. Как у все каталоги организованы? Как вы зависимости подтягиваете? Может какими-то готовыми инструментами пользуетесь? Если да, то какими и как впечатления?
В общем, любая информация будет интересна. А то собирать свой собственный велосипед -- это, конечно, увлекательная задачка, но как-то староват я уже для этого :(
Комментариев нет:
Отправить комментарий