Help with unexpected behavior when installing

I have some really weird behavior happening. I have a CMake build that creates a dynamic library and a static one (which is used by Google Test). I instruct install to only install the dynamic library. So that works, but what is weird is that it compiles the static one as well (and correctly does not install it).

Here is the setup:

  add_library("native-build" SHARED ${ARG_BUILD_SOURCES} ${ARG_NATIVE_BUILD_SOURCES})
  target_link_libraries("native-build" PUBLIC z-re-sdk-lib)
  set_target_properties("native-build" PROPERTIES OUTPUT_NAME ${RE_ID})
  # ...

  add_library("native-test-lib" STATIC ${ARG_BUILD_SOURCES} ${ARG_NATIVE_BUILD_SOURCES})
  set_target_properties("native-test-lib" PROPERTIES OUTPUT_NAME ${RE_ID})
  # ...

  install(
      TARGETS "native-build"
      DESTINATION ${INSTALL_DIR}
  )

and here are some runs:

# cmake --build . --config Debug --target native-build
[ 50%] Built target z-re-sdk-lib
[100%] Built target native-build

# cmake --build . --config Debug --target native-test-lib
[100%] Built target native-test-lib

# cmake --build . --config Debug --target install
[ 30%] Built target z-re-sdk-lib
[ 61%] Built target native-test-lib
[100%] Built target native-build
Install the project...
-- Install configuration: "Debug"
-- Up-to-date: /Users/ypujante/Library/Application Support/Propellerhead Software/RackExtensions_Dev/TestInstal/TestInstal.dylib
...

Any idea what is going on? It seems that somehow native-test-lib gets added to the dependencies for install yet does not get installed (as can be seen from the output of install and I checked the installation folder and it is not present).

How can I troubleshoot this issue?

Thanks
Yan

The install target depends on all by default. You can set CMAKE_SKIP_INSTALL_ALL_DEPENDENCY to skip this link or use the EXCLUDE_FROM_ALL property to remove native-test-lib from the all set.

I tried CMAKE_SKIP_INSTALL_ALL_DEPENDENCY but then it does not build anything at all so install fails. Using EXCLUDE_FROM_ALL on the dependencies that need to be excluded work.

I guess this is something that is really not intuitive and behaves backward from what you expect: you define a list of things to install… and they get installed properly but the rest still builds. If you remove the dependency on all (CMAKE_SKIP_INSTALL_ALL_DEPENDENCY), then nothing gets built even what is specified in install().

Thanks for the help anyway.

The built-in targets have very course dependencies. There’s an issue to fix that, but it hasn’t seen work. Component installations also make the graph more complicated. The first problem that comes to my mind is knowing which commands make what is needed for install(FILES) (or install(DIRECTORY)) when the paths are side effects of other commands. Long ago, CMake made the decision to just say "install will first build the project" and now has some release valves on that, but there hasn’t been any concerted effort to meaningfully improve the situation.

Thanks for the detailed explanation. I think without doing any work to change how CMake behaves in regards to install, it would be useful to change the documentation to make it more clear, as the behavior is counter intuitive. For example, if you look at https://cmake.org/cmake/help/latest/command/install.html, there isn’t even a mention about CMAKE_SKIP_INSTALL_ALL_DEPENDENCY and EXCLUDE_FROM_ALL is always discussed in regards to the install calls, not what happens if you set it as a property of a target.

After simplifying my build and using FetchContent_MakeAvailable(googletest) as suggested in the GoogleTest documentation, to my horror, running the install target for my project, also ended up installing GoogleTest library (includes and libs) locally (under /usr/local)!!!

This is so NOT the expected behavior.

I can see that the generated cmake_install.cmake now contains:

if(NOT CMAKE_INSTALL_LOCAL_ONLY)
  # Include the install script for each subdirectory.
  include("/Volumes/Vault/Downloads/com.acme.Kooza2-plugin/build/googletest-build/cmake_install.cmake")

endif()

How do I tell CMake to not even include this section in the cmake_install.cmake?

I find out you can set the option INSTALL_GTEST to OFF to disable installing the tests, but then I am back with the same issue I was having originally which is that all GoogleTest targets ends up being part of the All target and so they get built on install…

I suppose there is no FetchContent_MakeAvailable(googletest EXCLUDE_FROM_ALL) version of the call so I am reverting to including the directory myself since this way offers the option to exclude it:

add_subdirectory(${googletest_SOURCE_DIR} ${googletest_BINARY_DIR} EXCLUDE_FROM_ALL)

You have at least a couple of choices. There is an EXCLUDE_FROM_ALL directory property that you can set before calling FetchContent_MakeAvailable(). It will affect all directories you pull in from the current directory scope, but if you’re adding all your dependencies from a dedicated directory (e.g. <top-of-source-tree>/dependencies), this should be workable. Do not use EXCLUDE_FROM_ALL in the top level directory due to limitations discussed in issue 20168. Issue 20167 is also relevant.

The method I prefer though is to solve this issue using install components. Change the default install component just before you call FetchContent_MakeAvailable(googletest) using CMAKE_INSTALL_DEFAULT_COMPONENT_NAME. Then, simply don’t include that component in your packages.

You could combine the two methods I guess. Personally, I never felt the building of googletest targets was a significant contributor to the overall build time of our projects, but it will obviously depend on your own project.

It is not so much about the build time. My projects are audio plugins, so they need to be installed to be manually tested (copying the macOS bundle or windows dll to a specific location so that the DAW can load them). Sometimes I modify code in a way that breaks the compilation of the tests and I just want to manually test because I am not sure that the changes are worth it before I go and fix the tests. Having the tests unnecessarily compiled as part of the install phase, breaks this workflow because now install cannot happen…

The solution I mentioned works fine (add_subdirectory ... EXCLUDE_FROM_ALL). It is just very unfortunate that the default behavior of CMake combined with the example provided on the GoogleTest CMake documentation page leads to having GoogleTest installed when that should pretty much never be the case…

I would suggest contacting the GoogleTest maintainers to improve the documentation there to mention this behavior.

Already done Fix CMake documentation to prevent GoogleTest install · Issue #3486 · google/googletest · GitHub :wink:

2 Likes