I am in the process of reorganizing the physical (on disk) layout of a large cross-platform C++ project with many third party dependencies, built using CMake.
Since we need to support Windows, a platform on which there is no well-established package manager, we decided a long time ago to include the third party libraries we rely on in the source tree. However, on the other platforms we support such as Linux and Mac OS X, many of these third party libraries are available as packages or are already present in the system and are easily found by CMake.
The current project layout is as follow:
root/
src/
3rd-party-lib1/ (build system modified to output to build/)
3rd-party-lib2/ (build system modified to output to build/)
project-module1/ (our own code)
project-module2/ (our own code)
build/ (CMake is invoked from here)
3rd-party-lib1-bin/
3rd-party-lib2-bin/
The third party libraries have been tweaked so that when built, they output their binaries to root/build/<lib>/
.
The problems with this layout are multiple:
src/
directory contains a mixture of our own code and third party code, which is confusing.src/
directory is very large. Because src/
contains third party libraries, it is very large compared to the actual amount of original source code, making backuping our own code slightly more complicated than required (we can't just archive the whole src/
directory anymore).An alternative layout would be as follow:
root/
3rdparty/
3rd-party-lib1/ (100% original, contains built artifacts)
3rd-party-lib2/ (100% original, contains built artifacts)
src/
project-module1/ (our own code)
project-module2/ (our own code)
build/ (CMake is invoked from here)
Our CMake files would need to be modified to look for third party header files and libraries in the right place for each library.
What are the best practices regarding the handling of third party libraries in native, cross platform projects? Which layout would lead to the most unsurprising build experience for our developers on their respective platforms? Concrete examples of successful layouts from existing projects are also welcome.
My experience suggests the following as best practices:
When a third-party open-source library is used completely as-is, commit a local copy of the downloaded compressed tarball within the main git repo, to avoid network connectivity issues from preventing software builds.
When a third-party open-source library is almost used as-is, but needs to be tweaked (this is common when cross-compiling: many packages need slight adjustments to their configuration steps), store the compressed tarball and a "unified diff" patch file within the main git repo, and apply the patch within the PATCH_COMMAND step of the ExternalProject_Add.
When a third-party open-source library is (to be) heavily modified or extended by your organization, use a separate git repository to hold pointers to the upstream repository (easiest when it also uses git, but upstream svn can be managed also). Commit your organization's changes to a distinct branch from the branch used to mirror upstream. If you wish, you can introduce a submodule relationship between the main git repo and this one, though because the DOWNLOAD_COMMAND can fetch directly from an arbitrary git repo, it is not technically necessary to do so.
It is reasonable to treat small, less-frequently third-party proprietary binaries for a single target platform by archiving them within the main git repo also. However, third-party binaries which are available for a variety of platforms, are large, or frequently evolve should be stored in their own git repo and fetched in via DOWNLOAD_COMMAND, as above.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With