pahaa
я предполагал такую логику:
1) создаем репу myTrunk(develop)
2) кладем в него engineV1(это наш движок он шарится между всеми)
3) заливаем в git
4) например пишем сразу три игры, делаем три бренча(branch v1), (branch v2), (branch v3)
5) в каждой ветке пишем код специфичный для этой игры(myCore1, myCore2, myAnotherCore)
6) в случае правки общего кода и если это нужно(engineV1) реинтегрируем его в myTrunk
7) если нужно втянуть эти изменения в другие проекты делаем у них rebase(чтобы к ним подтянулись последние изменения из myTrunk)
8) завершая какой либо из проектов, делаем у него ребейз и реинтегрируем проектно-специфичный код(myCore1, myCore2, myAnotherCore) и/или общий код engineV1 в myTrunk, ну или вообще не делаем ни одного ни другого.
9) в myTrunk делаем рефакторинг готовим все для нового проекта
10) идем к п.4
xDimka
> ну почему же жесть, это называется форк.
технически - это форк. но по сути это абуз механизма форков. то, как весь гитхаб работает - это форк для:
1. внесения своих изменений в чужой проект. мы форкаем, вносим правки, а потом пулл-реквестами портируем изменения обратно в исходный репозиторий. собственно именно так разрабатывается линукс, для которого и был создан Git, и куча других проектов.
2. разработка своего собственного продукта, взяв за основу код исходного репозитория. обычно это просто развитие своей версии некой библиотеки, взаимодействие с которой по первому пункту неудобно или невозможно - например нужные изменения не соответствуют уровню качества исходника или его философии, или оригинальный разраб просто забросил библиотеку. но иногда благодаря форку появляется продукт конкурент. в подавляющем кол-ве случаев "класс" продукта после форка не меняется.
Cocos2dx, формально - это второй пункт. но он выбивается из тренда, т.к. на выходе мы получаем продукт другого "класса". я не говорю что это плохо - выглядит очень удобно в случае если движок хорошо заточен под определенные задачи (как раз тот случай). но это далеко не эталонный пример работы форков.
xDimka
но ведь я бы хотел оставить и myCore1 и myCore2, так чтобы любой из моих прототипов мог использовать любой из них.
а также оставить myRender1 и myRender2, чтобы любой прототип мог использовать любой из них с любым из myCore1 и myCore2.
как это сделать, не вливая myCore1, myCore2, myRender1 и myRender2 в транк?
kkolyan
>
> как это сделать, не вливая myCore1, myCore2, myRender1 и myRender2 в транк
Никак. В этом и суть, trunk или develop это сборная солянка для всего. Все выходит из неё и если что-то будет нужно то и возвращается в неё. Технически делается полный мерж всего бранча в myTrunk. После этого проще всего бранч пересоздать из myTrunk заново. Черри пики из бранчей в myTrunk недопускаются, только полный мерж. Из myTrunk в бранчи - допустимо, но разумней ребейз.
Вообщем мало помалу вот так и строится continuous integration.
xDimka
> в каждой ветке пишем код специфичный для этой игры(myCore1, myCore2,
> myAnotherCore)
Этот подход исключает вариант, когда некоторый код делится только с НЕКОТОРЫМИ проектами. У меня в примере не зря myCore был без цифры. Это код, специфичный для кор-механики, а не для игры. И я эту механику хочу затащить в 2 разные игры, но не в третью. У всех трёх игр разная мета, но у первых двух одинаковый кор-геймплей.
xDimka
ну да, в общем-то обычный trunk-based монолит.
но cкладывается впечатление, что и в C++ мире (не утверждаю что он однороден) распределенная инфраструктура если не преобладает, то по крайней мере очень популярна. кстати, в распределенной инфраструктуре continuous integration тоже отлично работает.
pahaa
> Этот подход исключает вариант, когда некоторый код делится только с НЕКОТОРЫМИ
> проектами. У меня в примере не зря myCore был без цифры. Это код, специфичный
> для кор-механики, а не для игры. И я эту механику хочу затащить в 2 разные
> игры, но не в третью. У всех трёх игр разная мета, но у первых двух одинаковый
> кор-геймплей.
В примере который я привёл, engine1 является общим для всех трёх. Если вам его не нужно использовать, не используйте. А то что он присутствует физически в вашем бренча как раз обеспечивает что все ваши интеграционные и юнит тесты проходят до того как вы вообще что-то будете заливать в ваш бренч для всех проектов.
kkolyan
> но cкладывается впечатление, что и в C++ мире (не утверждаю что он однороден)
> распределенная инфраструктура если не преобладает, то по крайней мере очень
> популярна. кстати, в распределенной инфраструктуре continuous integration тоже
> отлично работает.
В мире обычно превалирует product ориентированная модель. У продукта есть циклы жизни, планирование, разработка, поддержка. Игры в одном жанре, например шутеры от первого лица, как раз и будет для этой компании продуктом. То есть этот и все последующие шутеры от этой компании фактически это будут развитием одного первого шутера и всегда будут в одном репозиторий.
war_zes
>> смотреть на find_package и add_subdirectory.
>Очень хреновая вещь - потом мучаешься с разворачиванием всех зависимостей, ведь оно только ищет, оно не подгружает. За такое и ругают С++ - качаешь какой-нибудь проект, и затем по несколько часов начинаешь руками собирать все зависимости, чтобы CMake перестал ругаться и наконец-то сделал тебе солюшен или что там надо.
Я думаю, «ругают цпп» по существу в основном за то, что нужно знать довольно много разносторонней фигни, чтобы делать осмысленные вещи, а не ерунду. И, как следствие, довольно много ерунды в паблике, которая еще больше увеличивает порог входа – и так по кругу.
find_package – вещь не хреновая, просто она делает то, для чего предназначена (наверно), а не то, чего кому-то там бы хотелось: если локально разрабатывается несколько компонентов и много проектов, от них зависящих, то копипастить их по всей файловой системе средствами гитовских сабмодулей может оказатсья не лучшей идеей. С другой стороны, как раз add_subdirectory отлично комбинируется именно с сабмодулями.
И Cmake может сам скачивать зависимости при должном использовании.
Подкину еще ссылок, вдруг пригодятся кому
https://stackoverflow.com/questions/8153519/how-to-automatically-… orm-way-cmake
https://cmake.org/cmake/help/latest/guide/using-dependencies/index.html
https://foonathan.net/2016/03/cmake-install/
dirmenbitz
огромное спасибо за ссылки. особенно зашла последняя. прямо отвечает на многие тонкие вопросы, которые неуловимо вертятся в голове и не удовлетворяются поиском.
Пожалуй, буду использовать эту тему для отчетов о своих находках по теме. Крестонубам вроде меня, пришедшим из хипстерских сфер, будет полезно. Да и местные опытные крестовики, надеюсь поправят, если совсем ересь буду писать.
Итак, я выбрал подход с формирование инфраструктуры, состоящей из разных библиотек, распиханным по отдельным репозиториям. Буду называться их проектами, хотя каждый из этих проектов является только кирпичиком возможного продукта.
Каждый такой проект в общем случае имеет
Правила
1.1. Корневой CMakeLists.txt ничего не делает кроме инклюда других cmake-скриптов.
1.2. Каждому артефакту (создаваемым напрямую экзешникам и либам) отвидится своя папка со своим CMakeLists.txt, включаемым в проект с помощью add_subdirectory.
1.3. Скрипт сборки артефакта конфигурирует ровно 1 таргет, который может быть использован в других скриптах сборки проекта.
1.4. В скрипте сборки артефакта задаются исходные файлы для сборки (я включаю только CPP, т.к. хедеры подцепятся за ними сами)
1.5. В том же скрипте, при помощи target_link_libraries задается список требуемых артефактом библиотек (задаются только имена таргетов - никаких файловых путей здесь)
1.6. При помощи add_dependencies задается порядок сборки относительно других таргетов (если имеет значение)
1.7. Если этот артефакт является тулой, то скрипт должен объявить cmake-функцию, которую другие скрипты смогут вызвать не заботясь о месте расположения исполняемого файла.
1.8. А при помощи target_include_directories с опцией PUBLIC или INTERFACE указываем папки с хедерами, которые нужны зависимым артефактам. не очень интуитивно, т.к. эта же команда может использоваться чтобы указать, наоборот, нужные этому артефакту хедеры (в этом скрипте мы ни в коем случае такого не делаем - оставим это для скриптов импорта при интеграции с чужеродными либами).
2.1. Каждая подключаемая напрямую зависимость конфигурируется через отдельный cmake-скрипт - скрипт импорта (такие скрипты кладем в папку cmake и подключаем к проекту с помощью include)
2.2. Здесь скрипт уже может быть устроен по разному, но смысл в том, что он должен выдать в контекст сборки таргет (здесь уже может быть более одного) с проассоциированными с ним бинарниками и папками с хедерами таким образом, чтобы их автоматом подхватывал target_link_libraries, а также функции для удобного вызова всех предсотавляемых тулов.
2.3. Если либа какая-нибудь очень увесистая, то она должна быть установлена на локальную машину руками (например через vcpkg), откуда скрипт импорта их и подцепит.
2.4. Если либа достаточно легкая, то скрипт импорта выкачивает ее с помощью FetchContent.
2.5. Если либа при этом еще и имеет совместимый стиль CMake скриптов (например это другой проект нашей же инфраструктуры), то FetchContent сам же и включит либу в проект со всеми предоставляемыми таргетами.