I am working on configuring the build environment for a C++ project that uses multiple libraries. These libraries are independent of each other, but they all depend on the same 3rd party library (GTest) for testing. I am having some trouble determining where I should put this 3rd party library so I will have the most flexibility and modularity in my project.

The general project structure looks like this:

       +---------------+
       | Main Project  |
       +-+-----+-----+-+
         |     |     |
    +----+     |     +----+
    |          |          |
+---v---+  +---v---+  +---v---+
| lib1  |  | lib2  |  | lib3  |
+---+---+  +---+---+  +---+---+
    |          |          |
    +----+     |     +----+
         |     |     |
       +-v-----v-----v-+
       |     GTest     |
       +---------------+

The way I see it, I have 3 options:

Build and install GTest somewhere on my system and have all 3 libraries link to the same GTest library.

  • The advantage of this is I only have one copy of GTest, and the libraries don't need to know anything about each-other or about the main project.

  • The disadvantage is there is no standard place to put it and anyone else who wants to use any of these libraries will have to know how to install GTest themselves and where they need to install it.

Add GTest as a dependency on the Main project (maybe in an "3rdparty" folder) and link all of the libraries to the main project copy of GTest

  • The advantage of this is I only have one copy of GTest and it is not installed in some arbitrary location

  • The disadvantage is now all of the libraries have to know about the main project and would not be able to be used without the main project present. (and I would prefer to have them completely independent and usable by themselves)

Add GTest as a dependency on each individual library (each library would have a "3rdparty" folder that contains GTest)

  • The advantage of this is that each library is independent of the others and they all contain their GTest dependency so users of the library wouldn't need to know how to install it themselves.

  • The disadvantage is that I would now have 3 separate copies of GTest, one for each library. This also adds some complexity overall.

Which of these approaches is the most preferable, if any? Are there any other better approaches I simply have not considered yet?

  • 1
    Where would you put GTest if it would be not a 3rd party library, but one of yours? – Doc Brown Nov 11 at 20:36
  • That is a good question. I guess all of these questions apply to that too. If I created another library that was used by lib1, lib2, and lib3, but I also wanted it to be able to be used entirely independent of the project it would have the same issues and I'm not sure where I would put it. – tjwrona1992 Nov 11 at 20:40
  • Where did you put, say, lib1? It is a dependency of Main Project but it might be used for other purposes too, so same issue here. – Goyo Nov 11 at 22:59
  • Currently it is in the same repository as Main Project and is treated as a subproject in CMake, but I am trying to refactor the codebase to make everything more modular and reusable outside of the project. – tjwrona1992 Nov 11 at 23:01

Build and install GTest somewhere on my system and have all 3 libraries link to the same GTest library.

I'm from the school of thought that says any code dependency, is also my code. Therefor treat it exactly like all the other code you are writing on the project, and commit it to the same source repository.

This enables repeatable Builds as the only information required to rebuild any version of the project is already in the code base. I would even check-in dependencies built by your other repositories. Being able to completely rebuild an identical binary helps a lot in roll-back and defect analysis.

It also helps avoid build environment issues where different global versions behave just slightly differently.

It does however increase the overall size of your source repositories as each repository must check-in all of there dependencies - but disk space is very cheap.

Add GTest as a dependency on the Main project (maybe in an "3rdparty" folder) and link all of the libraries to the main project copy of GTest

Do not do this if those libraries are in separate repositories. This makes a complicated coupling, as A is required to build B, which is required to build A. This is circular thinking and dragons eat chickens and their eggs regardless of the order they arrive.

If these libraries are sub-modules of the main project, this works and is a good solution. It helps to keep consistency, and provides a single upgrade point. The only sticking point is that all the sub-libraries and tools in the repository must be updated together. This could be great (everything uses the latest), but might also be a sticking point (can't upgrade it because library C is too hard to upgrade).

Add GTest as a dependency on each individual library (each library would have a "3rdparty" folder that contains GTest)

This is always the most flexible option, every module/sub-project/tool in the repository can be updated independently. But this can lead to dependency hell too. Particularly if the dependency is somehow published in the modules API.

In this case, the test library won't publish through the API, so it broadly doesn't matter if each library uses a different version. But from the test reporting perspective, the report format forms part of the API. If it changes between versions you might still have to upgrade all of them at once, or otherwise have to deal with two different error report formats in your build process.


My Preference would be to manage via a third-party folder (option 2). When a project gets to the point where a module needs a separate version of a shared dependency (option 3), its time for that module to move into its own repository/build process/etc...

At this point treat that module as if it were completely independent. It has its own dependencies/build/test/documentation/etc. The main project build/test/etc can only invoke the build/test scripts (like a build server does) and expects the deliverable in the "3rd-party" library directory.

There will be some transition time to get all of this going, but when introducing the local dependencies - always do all or nothing. Mixing the two systems (even during transition) leads to confusion about which dependency is for what, where it is, and the impacts of a change.

Your Answer

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Not the answer you're looking for? Browse other questions tagged or ask your own question.