1

TL;DR I basically want a modular solution of this question. Not only one solution with everything in it, but also a solution for each executable alone.


In my question here I wanted to know how I should split my CMakeLists.txt among different folders. Some folders contain code that will be a static library and some code will be executables or dynamic libraries that build on those static libraries.

Folder structure looks like this:

/path/to/base/
  CMakeLists.txt
  app1/
    <src files for app1>
    CMakeLists.txt
  app2/
    <src files for app2>
    CMakeLists.txt
  lib/
    <src files for lib>
    CMakeLists.txt

If I take the approach of having a CMakeLists.txt in the parent folder to all my libraries and executables and from the include all the directories I have the problem that when I generate VS projects/solutions that my projects for each target contains all the projects - not only the ones necessary for my intended target.

Contents of CMakeLists.txt in the base folder:

cmake_minimum_required(VERSION 3.1)

add_subdirectory(lib)
add_subdirectory(app1)
add_subdirectory(app2)

and the CMakeLists.txt in the app1 folder (app2 is equivalent)

cmake_minimum_required(VERSION 3.1)

project(app1)

add_executable(app1 <src of app1>)

target_link_libraries(app1 lib)

and CMakeLists.txt of lib:

add_library(lib <src of lib>)

If I run cmake on the CMakeLists.txt from base the solution contains all the projects. And even if I open just one of the projects; it also contains everything. VS project of app1 also builds app2 - which I don't want.

If I only run cmake on the CMakeLists.txt of app1 I get a solution which doesn't contain app2 but it also doesn't contain lib, because that's only mentioned for linking inside the cmake file and not as a target (it's in the base file)

Philipp
  • 2,376
  • 5
  • 29
  • 47

1 Answers1

2

CMake will create a solution for each call to project, which will then include all the targets of the current directory and its subdirectories (regardless whether they were defined before or after the actual call to project).

Here's what a well-behaved CMake script should do: Each CMakeLists that you want to be able to act as a root of its own sub-tree within the larger project should start with a cmake_minimum_required call, followed by the project call. Writing project in a CMakeLists basically means: This is a fully self-contained component that can be built independently, even if you delete all the files from its parent directories. As such, it makes sense to have a separate solution for each such project.

With this in mind we can see why this doesn't work in your case: Your apps are not fully self-contained, they depend on lib. The solution to this is to write the build scripts for the apps as if lib was provided as a third-party component, with find_package calls and everything. In the compound build, you already have a target for lib, so the script invoked by the find_package call should be made to short-circuit to using that target and only actually go scavenging the system if it cannot find that existing target.

Also read up on how CMake's packaging system works, which provides some automation to handle such cases pretty elegantly.

ComicSansMS
  • 51,484
  • 14
  • 155
  • 166
  • Thanks for you answer. When you say "... with `find_package` calls and everything". Could you elaborate what 'and everything' entails? I will start reading the link you provided. – Philipp May 17 '18 at 08:59
  • @Philipp Just that you should not make any assumptions about *lib* that you wouldn't have if you weren't also its author. Treat it as you would any other third-party dependency that is not under your control. – ComicSansMS May 17 '18 at 09:30