install(TARGETS): Add default destination for MODULE libraries

Installing a module target without a LIBRARY DESTINATION no longer
raises an error. On DLL platforms, the default destination is the
RUNTIME default. Otherwise, the LIBRARY default is used.

Closes: #27759
This commit is contained in:
Tom Osika
2026-08-13 10:55:07 -04:00
committed by Brad King
parent 7abf903927
commit 8072e80f4b
10 changed files with 107 additions and 27 deletions
+24 -12
View File
@@ -267,18 +267,21 @@ Signatures
derived from the names of the modules. An empty ``DESTINATION`` may be used
to suppress installing these files (for use in generic code).
For regular executables, static libraries and shared libraries, the
``DESTINATION`` argument is not required. For these target types, when
``DESTINATION`` is omitted, a default destination will be taken from the
appropriate variable from :module:`GNUInstallDirs`, or set to a built-in
default value if that variable is not defined. The same is true for file
sets of type ``HEADERS``, and the public and private headers associated with
the installed targets through the :prop_tgt:`PUBLIC_HEADER` and
For regular executables, static libraries, shared libraries and module
libraries, the ``DESTINATION`` argument is not required. For these target
types, when ``DESTINATION`` is omitted, a default destination will be taken
from the appropriate variable from :module:`GNUInstallDirs`, or set to a
built-in default value if that variable is not defined. The same is true for
file sets of type ``HEADERS``, and the public and private headers associated
with the installed targets through the :prop_tgt:`PUBLIC_HEADER` and
:prop_tgt:`PRIVATE_HEADER` target properties. A destination must always be
provided for module libraries, Apple bundles and frameworks. A destination
can be omitted for interface and object libraries, but they are handled
differently (see the discussion of this topic toward the end of this
section).
provided for Apple bundles and frameworks. A destination can be omitted for
interface and object libraries, but they are handled differently (see the
discussion of this topic toward the end of this section).
.. versionadded:: 4.5
A destination is no longer required for module libraries. Previously,
omitting ``LIBRARY DESTINATION`` for a module library was an error.
For shared libraries on DLL platforms, if neither ``RUNTIME`` nor ``ARCHIVE``
destinations are specified, both the ``RUNTIME`` and ``ARCHIVE`` components are
@@ -288,6 +291,13 @@ Signatures
destinations are specified, then both components are installed to their
respective destinations.
For module libraries on DLL platforms, the default destination is the
``RUNTIME`` default rather than the ``LIBRARY`` default. Only the default
destination differs: a module library is always a ``LIBRARY`` artifact, so
its ``DESTINATION``, ``COMPONENT``, ``PERMISSIONS`` and ``CONFIGURATIONS``
are taken from the ``LIBRARY`` arguments on every platform, and the
``RUNTIME`` arguments never apply to it.
The following table shows the target types with their associated variables and
built-in defaults that apply when no destination is given:
@@ -515,7 +525,9 @@ Signatures
runtime artifacts of imported targets. Projects may do this if they want to
bundle outside executables or modules inside their installation. The
``LIBRARY``, ``RUNTIME``, ``FRAMEWORK``, and ``BUNDLE`` arguments have the
same semantics that they do in the `TARGETS`_ mode. Only the runtime artifacts
same semantics that they do in the `TARGETS`_ mode, except that an imported
module library uses the default ``LIBRARY`` destination on all platforms,
including DLL platforms. Only the runtime artifacts
of imported targets are installed (except in the case of :prop_tgt:`FRAMEWORK`
libraries, :prop_tgt:`MACOSX_BUNDLE` executables, and :prop_tgt:`BUNDLE`
CFBundles.) For example, headers and import libraries associated with DLLs are
@@ -0,0 +1,8 @@
default-module-install-destination
----------------------------------
* The :command:`install(TARGETS)` command no longer requires a
``LIBRARY DESTINATION`` for :ref:`Module Libraries`. When no destination is
given, a module library is installed to the default ``RUNTIME``
destination on DLL platforms and to the default ``LIBRARY`` destination
on other platforms.
+15 -13
View File
@@ -972,21 +972,23 @@ bool HandleTargetsMode(std::vector<std::string> const& args,
} break;
case cm::TargetType::MODULE_LIBRARY: {
// Modules use LIBRARY properties.
std::string moduleDest;
if (!libraryArgs.GetDestination().empty()) {
libraryGenerator = CreateInstallTargetGenerator(
target, libraryArgs, false, helper.CaptureContext());
libraryGenerator->SetNamelinkMode(namelinkMode);
namelinkOnly =
(namelinkMode == cmInstallTargetGenerator::NamelinkModeOnly);
if (runtimeDependencySet) {
runtimeDependencySet->AddModule(libraryGenerator.get());
}
moduleDest = libraryArgs.GetDestination();
} else if (target.IsDLLPlatform()) {
moduleDest = helper.GetRuntimeDestination(nullptr);
} else {
status.SetError(
cmStrCat("TARGETS given no LIBRARY DESTINATION for module "
"target \"",
target.GetName(), "\"."));
return false;
moduleDest = helper.GetLibraryDestination(nullptr);
}
libraryGenerator = CreateInstallTargetGenerator(
target, libraryArgs, false, helper.CaptureContext(), moduleDest);
libraryGenerator->SetNamelinkMode(namelinkMode);
namelinkOnly =
(namelinkMode == cmInstallTargetGenerator::NamelinkModeOnly);
if (runtimeDependencySet) {
runtimeDependencySet->AddModule(libraryGenerator.get());
}
} break;
case cm::TargetType::OBJECT_LIBRARY: {
@@ -188,6 +188,7 @@ elseif (CMake_TEST_install_VARIANT STREQUAL "Targets")
run_install_test(TARGETS-InstallFromSubDir)
run_install_test(TARGETS-OPTIONAL)
run_install_test(TARGETS-Defaults)
run_install_test(TARGETS-Module-Destination)
run_install_test(TARGETS-SymbolicComponent)
if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
@@ -7,6 +7,7 @@ if(WIN32)
[[mybin]]
[[mybin/exe\.exe]]
[[mybin/(lib)?lib1\.dll]]
[[mybin/(lib)?lib5\.dll]]
[[myinclude]]
[[myinclude/obj3\.h]]
[[mylib]]
@@ -22,6 +23,7 @@ elseif(MSYS)
[[mybin]]
[[mybin/exe\.exe]]
[[mybin/msys-lib1\.dll]]
[[mybin/msys-lib5\.dll]]
[[myinclude]]
[[myinclude/obj3\.h]]
[[mylib]]
@@ -36,6 +38,7 @@ elseif(CYGWIN)
[[lib4/cyglib4\.dll]]
[[mybin]]
[[mybin/cyglib1\.dll]]
[[mybin/cyglib5\.dll]]
[[mybin/exe\.exe]]
[[myinclude]]
[[myinclude/obj3\.h]]
@@ -56,6 +59,7 @@ else()
[[mylib]]
[[mylib/liblib1\.(dylib|so)]]
[[mylib/liblib2\.a]]
[[mylib/liblib5\.so]]
)
endif()
check_installed("^${_check_files}$")
@@ -10,8 +10,9 @@ add_library(lib3 SHARED obj4.c)
set_property(TARGET lib3 PROPERTY PRIVATE_HEADER ${CMAKE_CURRENT_SOURCE_DIR}/obj4.h)
add_library(lib4 SHARED obj5.c)
set_property(TARGET lib4 PROPERTY PUBLIC_HEADER ${CMAKE_CURRENT_SOURCE_DIR}/obj5.h)
add_library(lib5 MODULE obj2.c)
install(TARGETS exe lib1 lib2)
install(TARGETS exe lib1 lib2 lib5)
install(TARGETS lib3
LIBRARY DESTINATION lib3
ARCHIVE DESTINATION lib3
@@ -3,6 +3,7 @@ if(WIN32)
[[bin]]
[[bin/exe\.exe]]
[[bin/(lib)?lib1\.dll]]
[[bin/(lib)?lib5\.dll]]
[[include]]
[[include/obj1\.h]]
[[include/obj2\.h]]
@@ -20,6 +21,7 @@ elseif(MSYS)
[[bin]]
[[bin/exe\.exe]]
[[bin/msys-lib1\.dll]]
[[bin/msys-lib5\.dll]]
[[include]]
[[include/obj1\.h]]
[[include/obj2\.h]]
@@ -36,6 +38,7 @@ elseif(CYGWIN)
set(_check_files
[[bin]]
[[bin/cyglib1\.dll]]
[[bin/cyglib5\.dll]]
[[bin/exe\.exe]]
[[include]]
[[include/obj1\.h]]
@@ -60,6 +63,7 @@ else()
[[lib]]
[[lib/liblib1\.(dylib|so)]]
[[lib/liblib2\.a]]
[[lib/liblib5\.so]]
[[lib3]]
[[lib3/liblib3\.(dylib|so)]]
[[lib4]]
@@ -10,13 +10,14 @@ add_library(lib3 SHARED obj4.c)
set_property(TARGET lib3 PROPERTY PRIVATE_HEADER ${CMAKE_CURRENT_SOURCE_DIR}/obj4.h)
add_library(lib4 SHARED obj5.c)
set_property(TARGET lib4 PROPERTY PUBLIC_HEADER ${CMAKE_CURRENT_SOURCE_DIR}/obj5.h)
add_library(lib5 MODULE obj2.c)
add_library(iface INTERFACE)
set_target_properties(iface PROPERTIES
PUBLIC_HEADER ${CMAKE_CURRENT_SOURCE_DIR}/obj1.h
PRIVATE_HEADER ${CMAKE_CURRENT_SOURCE_DIR}/obj2.h)
install(TARGETS exe lib1 lib2)
install(TARGETS exe lib1 lib2 lib5)
install(TARGETS lib3
LIBRARY DESTINATION lib3
ARCHIVE DESTINATION lib3
@@ -0,0 +1,34 @@
if(WIN32)
set(_check_files
[[bin]]
[[bin/(lib)?mod1\.dll]]
[[bin/(lib)?mod3\.dll]]
[[plugins]]
[[plugins/(lib)?mod2\.dll]]
)
elseif(MSYS)
set(_check_files
[[bin]]
[[bin/msys-mod1\.dll]]
[[bin/msys-mod3\.dll]]
[[plugins]]
[[plugins/msys-mod2\.dll]]
)
elseif(CYGWIN)
set(_check_files
[[bin]]
[[bin/cygmod1\.dll]]
[[bin/cygmod3\.dll]]
[[plugins]]
[[plugins/cygmod2\.dll]]
)
else()
set(_check_files
[[lib]]
[[lib/libmod1\.so]]
[[lib/libmod3\.so]]
[[plugins]]
[[plugins/libmod2\.so]]
)
endif()
check_installed("^${_check_files}$")
@@ -0,0 +1,13 @@
enable_language(C)
# A module library defaults to the runtime destination on DLL platforms and to
# the library destination elsewhere, but it is always a LIBRARY artifact:
# LIBRARY DESTINATION overrides the default on every platform, and RUNTIME
# DESTINATION never applies to it.
add_library(mod1 MODULE obj1.c)
add_library(mod2 MODULE obj2.c)
add_library(mod3 MODULE obj3.c)
install(TARGETS mod1)
install(TARGETS mod2 LIBRARY DESTINATION plugins)
install(TARGETS mod3 RUNTIME DESTINATION notused)