Why do we need forks?¶
Forks put the burden of pushing new branches/releases from upstream, merging and resolving conflicts by maintainers and at the first view look like a bad, aggressively intrusive choice. But in practice it’s the clearest, robust and universal solution for all the issues related to integration of package into Hunter.
Note
Forks are not requirement. Hunterization changes can be pushed upstream without affecting main functionality, see compatibility for details. And if package has no dependencies it can be used as is in Hunter, see examples.
Note
As already noted here all the issues that are not related to hunterization should be pushed upstream. Including most of the issues described in this section.
Solution for bundled sources¶
Take a look at this example:
Here package rabit
has bundled dependencies dmlc
. In fact dmlc
folder is a separated project and lives here:
Assuming that we can’t change the order of include paths (local includes
should have higher priority than external because different version of same
package itself can be installed in system) there is no “soft” solution here
and the only way to integrate package is to remove dmlc
folder from
sources. In practice it means forking the project and applying “remove folder”
patch. Note that it really doesn’t depend on the package manager, build system
or C++ compiler. All C++ compilers works similar to
> c++ -I/path/to/local -I/path/to/external ...
Meaning #include <dmlc/timer.h>
will always fall to the choice of picking
local files.
Set of patch files¶
Another way to avoid forks is to keep needed *.patch
files in Hunter and
apply them to upstream releases before running build instructions. Such approach
used by Homebrew and Gentoo
for example. In practice such set of patches can be quite big, e.g. 19 commits
for package OpenCV
(add HunterGate
module, lock version in
HunterGate
, adding hunter_add_package
calls, applying ANDROID_*
variables introduced by new CMake version and general improvements):
Note that Hunter keep all available OpenCV
versions in
cmake/projects/OpenCV/hunter.cmake
file:
At this moment there are 29 versions of OpenCV
available for users, hence
it will be 19 x 29 = 551 *.patch
files to maintain. Some of them can be
shared between versions, some of them can be fused with each other, etc.
If such approach will be chosen we will end up with system for maintaining
patches, but there is no need to reinvent the wheel, such system already
exist and called Git
. Assuming the fact that Hunter project hosted on
GitHub and GitHub offer free unlimited repositories for public projects there
are no real reasons to choose *.patch
approach over forks. The use of
the forks allow us to rebase, merge, cherry-pick, discuss and review the patches
easily.
High cohesion¶
High cohesion means that you should keep parts of a code base that are related
to each other in a single place [1]. The fact that version v1.0
of package
Foo
works fine with Hunter archive v0.10
is perfectly expressed by
adding child commit Add Hunter v0.10
to parent commit Foo v1.0
. Change
of dependencies from version to version is another example.
Foo
version v1.0
:
if(WIN32)
find_package(boo CONFIG REQUIRED)
endif()
find_package(bar CONFIG REQUIRED)
Foo
version v2.0
:
if(FOO_WITH_BAZ)
find_package(baz CONFIG REQUIRED)
endif()
find_package(bar CONFIG REQUIRED)
It’s hard to make a mistake in both cases:
--- /home/docs/checkouts/readthedocs.org/user_builds/hunter/checkouts/stable/docs/faq/foo-v1.0.cmake
+++ /home/docs/checkouts/readthedocs.org/user_builds/hunter/checkouts/stable/docs/faq/foo-v1.0-hunter.cmake
@@ -1,5 +1,7 @@
if(WIN32)
+ hunter_add_package(boo)
find_package(boo CONFIG REQUIRED)
endif()
+hunter_add_package(bar)
find_package(bar CONFIG REQUIRED)
--- /home/docs/checkouts/readthedocs.org/user_builds/hunter/checkouts/stable/docs/faq/foo-v2.0.cmake
+++ /home/docs/checkouts/readthedocs.org/user_builds/hunter/checkouts/stable/docs/faq/foo-v2.0-hunter.cmake
@@ -1,5 +1,7 @@
if(FOO_WITH_BAZ)
+ hunter_add_package(baz)
find_package(baz CONFIG REQUIRED)
endif()
+hunter_add_package(bar)
find_package(bar CONFIG REQUIRED)
It will be much easier to miss something while trying to support any fork-free approach:
if(FOO_VERSION VERSION_EQUAL 1.0 AND WIN32)
magic_download(boo)
endif()
if(FOO_VERSION VERSION_EQUAL 2.0 AND FOO_WITH_BAZ)
magic_download(baz)
endif()
magic_download(bar)
Any non-CMake custom build scheme suffers from this problem since build instructions have to know everything about all versions available, e.g. see Boost components .
[1] | http://enterprisecraftsmanship.com/2015/09/02/cohesion-coupling-difference/ |
Rejected/pending CMake patches¶
Having CMake build instructions in package is the easiest way to integrate package into Hunter (but not the only one) however not all developers of the upstream projects are ready to accept CMake code because it may put burden on maintaining another build system (if CMake added as extra build system), learning new build system (if you want to substitute existing system with CMake) or increase CMake minimum version to introduce new code. https://github.com/hunter-packages is a central place where CMake friendly code can leave and shared with others.
Removing usage of FindXXX.cmake¶
Overwhelming majority of projects use FindXXX.cmake
(or even something like
find_library
) instead of recommended XXXConfig.cmake
approach,
effectively making project non-relocatable. It’s not a problem for the package
managers that are using single-root directory (e.g. /usr/lib
for
apt-get
on Ubuntu and /usr/local/lib
for brew
on OSX) but since
Hunter allow to have
multiple custom configurations
it will not work.
CGold
Lock URL/SHA1 in HunterGate¶
Even if all the issues will be resolved and
‘hunter_add_package’ will be called by hook inside ‘find_package’
it’s still will be convenient to save latest successful 3rd parties
configuration for debugging purposes. In terms of Hunter it means attaching
URL/SHA1 arguments of HunterGate
to some commit.