Frameworks installed by install(RUNTIME_DEPENDENCY_SET) also copy headers


Installing the dependencies of an application that depends on Qt frameworks with install(TARGETS target RUNTIME_DEPENDENCIES) ends up installing not only the Qt framework library, but also the headers.

The generated install code looks like

    if(_CMAKE_TMP_dep MATCHES "^(.*/)?([^/]*\\.framework)/(.*)$")
      set(_CMAKE_TMP_dir "${CMAKE_MATCH_1}")
      set(_CMAKE_TMP_name "${CMAKE_MATCH_2}")
      set(_CMAKE_TMP_file "${CMAKE_MATCH_3}")
      set(_CMAKE_TMP_path "${_CMAKE_TMP_dir}${_CMAKE_TMP_name}")


which ends up installing a bunch of headers

-- Up-to-date: ~/installed/my_fw/QtCore.framework/Versions/A/Headers/6.2.0/QtCore/private/qabstractanimation_p.h
-- Up-to-date: ~/installed/my_fw/QtCore.framework/Versions/A/Headers/6.2.0/QtCore/private/qabstracteventdispatcher_p.h
-- Up-to-date: ~/installed/my_fw/QtCore.framework/Versions/A/Headers/6.2.0/QtCore/private/qabstractfileengine_p.h

Is this intended behavior or an oversight?

This is intended. If it’s a framework, CMake installs all files associated with the framework, because it assumes all those files are needed with the library. It makes no distinction between resource files, header files, or other types of files.

That kinda reduces the usefulness of the install(... RUNTIME_DEPENDENCIES) functionality for the common use case though, I think. If you’re using that command, you’re likely producing a package for distribution and are less likely to be expecting/wanting others to be able to link against the frameworks. If you were producing such an SDK that did want to support that, you’re probably consciously adding the full frameworks directly. The value of install(... RUNTIME_DEPENDENCIES) is in being able to supply all the things a target needs at runtime, and headers don’t typically fall into that category. Resources, on the other hand, would typically be needed.

Is there any existing literature on what happens when you only copy the runtime portions of a framework, and how to go about filtering it? (This page looks like it has some useful information on the topic.)

Filtering the files, in addition to possibly causing unforeseen and unwanted side effects, would have also required more implementation work. I went with the most conservative option of simply including everything.

At the very least, changing this now would require a policy.

I will also note that CMake currently doesn’t discriminate when installing its own frameworks either - it just does a blanket directory install, just like install(RUNTIME_DEPENDENCY_SET).

The framework embedding features that were added for the Xcode generator are a useful guide here. Xcode has options for whether or not to copy headers when embedding a framework (it also has separate options for code signing).

Yep, completely understand. My comment was more a reflection on where we’re at now compared with what users are likely to want. A workaround at the moment might be to remove the headers after the frameworks have been copied in (which sounds like what the framework embedding functionality in Xcode might be doing under the covers too). We may be able to add a similar keyword to the install(...RUNTIME_DEPENDENCIES) command to remove headers on copy, which shouldn’t require a policy.

Craig voiced my concern.
The command option is called RUNTIME_DEPENDENCIES , so i’d expect it install only the runtime dependency files of a framework (the framework, the resources, the plugins, the binary helpers).
I can totally understand that it wouldn’t be the focus of an initial implementation.

Would it be feasible to provide forwarding options that get transformed into the
[PATTERN <pattern> | REGEX <regex>] [EXCLUDE] options of an install(FILES) call? That way the filtering can be done without actually installing the headers.

I will also note that CMake currently doesn’t discriminate when installing its own frameworks either - it just does a blanket directory install, just like install(RUNTIME_DEPENDENCY_SET)

Installation of a CMake project framework can have multiple purposes though. One case would be an app and the framework is a runtime dependency.
Another case is an SDK, where the framework is used for further building.