Managing 3rd party sources and binaries used by code under source control

Xyand picture Xyand · Jul 3, 2012 · Viewed 8.6k times · Source

I have a large code base under source control (was subversion, now git). To compile the code and run the tests I use a set of 3rd party libraries. These libraries can be divided into few categoriesL

  • Binaries only
  • 3rd party sources
  • 3rd party sources + local modifications

Each library has its {Windows, Linux} X {debug, release} X {32bit, 64bit} configurations. In addition these libraries evolve with time and different versions of my project use different versions/builds of these libraries.

My question is what is the best way to store these 3rd parties?

Here is my set of preferences:

  1. Keep the size of the project source repository small
  2. Keep the project source in sync with the 3rd parties so I can always compile and run and old version
  3. Simple to manage
  4. Cross platform

I tried and thought of several solutions but neither was satisfactory:

  1. Use a versioned script to fetch the binaries from a manually managed ftp server that holds all versions of the libraries. This works, but requires careful management of the directory structure on the server. It is error prone as someone might overwrite one of the binaries with a new build.
  2. SVN externals - At the time SVN externals could not refer to a specific tag. Today I am using git.
  3. Git submodules - Pulls the entire external repository which can be huge. Alternatively it requires managing a separate repository for every single library. The submodule points at a specific tag which means either I get all the externals, when I need only some, or I mimic some weird file system in the git tree.

It is clear to me that the 3rd party sources need to be stored in git in a vendor branch, but the binaries and headers are a different story.

Answer

Xyand picture Xyand · Jul 13, 2012

A fair solution for my problem is git-subtree which was recently merged into mainline git. It provides a fair balance between my requirements and the platform limitations. Now I have multiple repositories for the externals (each has a vendor branch as well as local changes branch) and each project repository takes parts of these externals into sub-folders. To keep things organized I maintain a 'bin' and 'lib' folders which contains soft links to the appropriate files/folders in the externals sub-folder.

git-subtree allows to merge a sub-tree from an external repository into a sub-folder. The sub-folder can be merged back and forth with the external repository.

Pros/Cons:

  1. Small repository - The repository is not as small as I would like it to be but it contains only the necessary parts from the external repositories. To save space I try to keep the external trees small. I consider it a good price to pay when in return I get simplicity and robustness; as loading and updating a project is a simple git pull and all project related data is contained in a single repository

  2. Project/Externals sync - As the project and externals are versioned in the same repository, I can checkout any branch/tag I want and expect it to be working.

  3. Simplicity - Day-by-day work is straight forward. Updating external repository, creating a new one or switching to a different version of the external may be tricky and requires special syntax. However this does happen too much. The best thing is that one can add a new external to this project first and only afterwards split it (using git-subtree) into its own repository.

  4. Cross platform - Well it's git

  5. Binaries - I decided to avoid holding binaries and provide Makefiles instead. I came to this decision as some of my externals depend on other externals which makes it very hard to build a binary that doesn't change very often. For some externals I do store the binaries due to very long build times.

Structure:

/root
   /External
      /External1 (git-subtree from [email protected]:External1 v1.0)
      /External2 (git-subtree from [email protected]:External2 v0.7)
   /lib
      /libExternal1.a -> ../External/External1/libExternal1.a
      /libExternal2.a -> ../External/External1/libExternal2.a
   /include
      /External1 -> ../External/External1/include
      /External2 -> ../External/External2/include