1
0
mirror of https://github.com/Kitware/CMake.git synced 2025-10-14 02:08:27 +08:00

CPS: Add Symbolic Components

Adds support for "symbolic" components, which represent feature-level
capabilities of a package that do not correspond to actual build targets.
These are modeled as pseudo-targets, using the INTERFACE type as a base,
and can be queried via:

  get_target_property(... <tgt> "SYMBOLIC")

This enables consumers to declare requirements on optional features
(e.g., SSL support) even when they do not map to concrete targets.

Fixes: #27187
This commit is contained in:
Taylor Sasser
2025-09-01 08:52:15 -04:00
parent 03284e018f
commit d92b6c3e20
44 changed files with 312 additions and 16 deletions

View File

@@ -196,6 +196,30 @@ Interface Libraries
call are ``PRIVATE`` to the interface library and do not appear in its
:prop_tgt:`INTERFACE_SOURCES` target property.
.. signature::
add_library(<name> INTERFACE SYMBOLIC)
:target: INTERFACE-SYMBOLIC
.. versionadded:: 4.2
Add a symbolic :ref:`Interface Library <Interface Libraries>` target.
Symbolic interface libraries are useful for representing optional components
or features in a package. They have no usage requirements, do not compile
sources, and do not produce a library artifact on disk, but they may be
exported and installed. They can also be tested for existence with the
regular :command:`if(TARGET)` subcommand.
A symbolic interface library may be used as a linkable target to enforce the
presence of optional components in a dependency. For example, if a library
``libgui`` may or may not provide a feature ``widget``, a consumer package
can link against ``widget`` to express that it requires this component to be
available. This allows :command:`find_package` calls that declare required
components to be validated by linking against the corresponding symbolic
targets.
A symbolic interface library has the :prop_tgt:`SYMBOLIC` target property
set to true.
.. _`add_library imported libraries`:
Imported Libraries

View File

@@ -417,6 +417,7 @@ Properties on Targets
/prop_tgt/Swift_LANGUAGE_VERSION
/prop_tgt/Swift_MODULE_DIRECTORY
/prop_tgt/Swift_MODULE_NAME
/prop_tgt/SYMBOLIC
/prop_tgt/SYSTEM
/prop_tgt/TEST_LAUNCHER
/prop_tgt/TRANSITIVE_COMPILE_PROPERTIES

View File

@@ -0,0 +1,11 @@
SYMBOLIC
--------
.. versionadded:: 4.2
Read-only indication of whether a target is ``SYMBOLIC``.
Symbolic targets are created by calls to
:command:`add_library(INTERFACE SYMBOLIC) <add_library(INTERFACE-SYMBOLIC)>`.
They are useful for packages to represent additional **components** or
**feature selectors** that consumers can request via ``find_package()``.

View File

@@ -33,6 +33,7 @@ bool cmAddLibraryCommand(std::vector<std::string> const& args,
bool excludeFromAll = false;
bool importTarget = false;
bool importGlobal = false;
bool symbolicTarget = false;
auto s = args.begin();
@@ -117,6 +118,9 @@ bool cmAddLibraryCommand(std::vector<std::string> const& args,
} else if (*s == "EXCLUDE_FROM_ALL") {
++s;
excludeFromAll = true;
} else if (*s == "SYMBOLIC") {
++s;
symbolicTarget = true;
} else if (*s == "IMPORTED") {
++s;
importTarget = true;
@@ -223,9 +227,9 @@ bool cmAddLibraryCommand(std::vector<std::string> const& args,
}
/* ideally we should check whether for the linker language of the target
CMAKE_${LANG}_CREATE_SHARED_LIBRARY is defined and if not default to
STATIC. But at this point we know only the name of the target, but not
yet its linker language. */
CMAKE_${LANG}_CREATE_SHARED_LIBRARY is defined and if not default to
STATIC. But at this point we know only the name of the target, but not
yet its linker language. */
if ((type == cmStateEnums::SHARED_LIBRARY ||
type == cmStateEnums::MODULE_LIBRARY) &&
!mf.GetState()->GetGlobalPropertyAsBool("TARGET_SUPPORTS_SHARED_LIBS")) {
@@ -282,7 +286,8 @@ bool cmAddLibraryCommand(std::vector<std::string> const& args,
}
// Create the imported target.
mf.AddImportedTarget(libName, type, importGlobal);
cmTarget* target = mf.AddImportedTarget(libName, type, importGlobal);
target->SetSymbolic(symbolicTarget);
return true;
}
@@ -312,8 +317,15 @@ bool cmAddLibraryCommand(std::vector<std::string> const& args,
}
}
if (symbolicTarget && type != cmStateEnums::INTERFACE_LIBRARY) {
status.SetError(
"SYMBOLIC option may only be used with INTERFACE libraries");
return false;
}
std::vector<std::string> srcs(s, args.end());
mf.AddLibrary(libName, type, srcs, excludeFromAll);
cmTarget* target = mf.AddLibrary(libName, type, srcs, excludeFromAll);
target->SetSymbolic(symbolicTarget);
return true;
}

View File

@@ -292,7 +292,20 @@ void cmExportCMakeConfigGenerator::GenerateImportTargetCode(
os << "add_library(" << targetName << " OBJECT IMPORTED)\n";
break;
case cmStateEnums::INTERFACE_LIBRARY:
os << "add_library(" << targetName << " INTERFACE IMPORTED)\n";
if (target->IsSymbolic()) {
os << "if(CMAKE_VERSION VERSION_GREATER_EQUAL \""
<< CMake_VERSION_DEVEL(4, 2)
<< "\")\n"
" add_library("
<< targetName << " INTERFACE SYMBOLIC IMPORTED)\n"
<< "elseif(CMAKE_VERSION VERSION_GREATER_EQUAL 3.2.0)\n"
<< " add_library(" << targetName << " INTERFACE IMPORTED)\n"
<< " set_target_properties(" << targetName
<< " PROPERTIES SYMBOLIC TRUE)\n"
<< "endif()\n";
} else {
os << "add_library(" << targetName << " INTERFACE IMPORTED)\n";
}
break;
default: // should never happen
break;

View File

@@ -182,6 +182,7 @@ Json::Value* cmExportPackageInfoGenerator::GenerateImportTarget(
Json::Value& component = components[name];
Json::Value& type = component["type"];
switch (targetType) {
case cmStateEnums::EXECUTABLE:
type = "executable";
@@ -196,7 +197,7 @@ Json::Value* cmExportPackageInfoGenerator::GenerateImportTarget(
type = "module";
break;
case cmStateEnums::INTERFACE_LIBRARY:
type = "interface";
type = target->IsSymbolic() ? "symbolic" : "interface";
break;
default:
type = "unknown";

View File

@@ -1153,6 +1153,11 @@ bool cmGeneratorTarget::IsImportedGloballyVisible() const
return this->Target->IsImportedGloballyVisible();
}
bool cmGeneratorTarget::IsSymbolic() const
{
return this->Target->IsSymbolic();
}
bool cmGeneratorTarget::IsForeign() const
{
return this->Target->IsForeign();

View File

@@ -70,6 +70,7 @@ public:
bool IsImported() const;
bool IsImportedGloballyVisible() const;
bool IsForeign() const;
bool IsSymbolic() const;
bool CanCompileSources() const;
bool HasKnownRuntimeArtifactLocation(std::string const& config) const;
std::string const& GetLocation(std::string const& config) const;

View File

@@ -773,7 +773,9 @@ bool cmPackageInfoReader::ImportTargets(cmMakefile* makefile,
cmTarget* target = nullptr;
if (type == "symbolic"_s) {
// TODO
target = this->AddLibraryComponent(
makefile, cmStateEnums::INTERFACE_LIBRARY, fullName, *ci, package);
target->SetSymbolic(true);
} else if (type == "dylib"_s) {
target = this->AddLibraryComponent(
makefile, cmStateEnums::SHARED_LIBRARY, fullName, *ci, package);

View File

@@ -634,7 +634,13 @@ bool HandleTargetMode(cmExecutionStatus& status,
status.SetError("can not be used on an ALIAS target.");
return false;
}
if (cmTarget* target = status.GetMakefile().FindTargetToUse(name)) {
if (target->IsSymbolic()) {
status.SetError("can not be used on a SYMBOLIC target.");
return false;
}
// Handle the current target.
if (!HandleTarget(target, status.GetMakefile(), propertyName,
propertyValue, appendAsString, appendMode, remove)) {

View File

@@ -40,6 +40,10 @@ bool cmSetTargetPropertiesCommand(std::vector<std::string> const& args,
return false;
}
if (cmTarget* target = mf.FindTargetToUse(tname)) {
if (target->IsSymbolic()) {
status.SetError("can not be used on a SYMBOLIC target.");
return false;
}
// loop through all the props and set them
for (auto k = propsIter + 1; k != args.end(); k += 2) {
target->SetProperty(*k, *(k + 1));

View File

@@ -608,6 +608,7 @@ public:
bool IsAndroid;
bool BuildInterfaceIncludesAppended;
bool PerConfig;
bool IsSymbolic;
cmTarget::Visibility TargetVisibility;
std::set<BT<std::pair<std::string, bool>>> Utilities;
std::set<std::string> CodegenDependencies;
@@ -885,6 +886,7 @@ cmTarget::cmTarget(std::string const& name, cmStateEnums::TargetType type,
this->impl->IsAIX = false;
this->impl->IsApple = false;
this->impl->IsAndroid = false;
this->impl->IsSymbolic = false;
this->impl->TargetVisibility = vis;
this->impl->BuildInterfaceIncludesAppended = false;
this->impl->PerConfig = (perConfig == PerConfig::Yes);
@@ -1946,6 +1948,7 @@ MAKE_PROP(LINK_LIBRARIES);
MAKE_PROP(MANUALLY_ADDED_DEPENDENCIES);
MAKE_PROP(NAME);
MAKE_PROP(SOURCES);
MAKE_PROP(SYMBOLIC);
MAKE_PROP(TYPE);
MAKE_PROP(BINARY_DIR);
MAKE_PROP(SOURCE_DIR);
@@ -2047,6 +2050,7 @@ bool IsSettableProperty(cmMakefile* context, cmTarget* target,
{ "MANUALLY_ADDED_DEPENDENCIES", { ROC::All } },
{ "NAME", { ROC::All } },
{ "SOURCES", { ROC::Imported } },
{ "SYMBOLIC", { ROC::All } },
{ "TYPE", { ROC::All } },
{ "ALIAS_GLOBAL", { ROC::All, cmPolicies::CMP0160 } },
{ "BINARY_DIR", { ROC::All, cmPolicies::CMP0160 } },
@@ -2067,6 +2071,11 @@ bool IsSettableProperty(cmMakefile* context, cmTarget* target,
}
}
void cmTarget::SetSymbolic(bool const value)
{
this->impl->IsSymbolic = value;
}
void cmTarget::SetProperty(std::string const& prop, cmValue value)
{
if (!IsSettableProperty(this->impl->Makefile, this, prop)) {
@@ -2606,6 +2615,7 @@ cmValue cmTarget::GetProperty(std::string const& prop) const
propBINARY_DIR,
propSOURCE_DIR,
propSOURCES,
propSYMBOLIC,
propINTERFACE_LINK_LIBRARIES,
propINTERFACE_LINK_LIBRARIES_DIRECT,
propINTERFACE_LINK_LIBRARIES_DIRECT_EXCLUDE,
@@ -2626,6 +2636,10 @@ cmValue cmTarget::GetProperty(std::string const& prop) const
return cmValue(propertyIter->second.Value);
}
if (prop == propSYMBOLIC) {
return this->IsSymbolic() ? cmValue(propTRUE) : cmValue(propFALSE);
}
UsageRequirementProperty const* usageRequirements[] = {
&this->impl->IncludeDirectories,
&this->impl->CompileOptions,
@@ -2760,6 +2774,11 @@ bool cmTarget::IsApple() const
return this->impl->IsApple;
}
bool cmTarget::IsSymbolic() const
{
return this->impl->IsSymbolic;
}
bool cmTarget::IsNormal() const
{
switch (this->impl->TargetVisibility) {

View File

@@ -193,6 +193,8 @@ public:
//! Get the utilities used by this target
std::set<BT<std::pair<std::string, bool>>> const& GetUtilities() const;
void SetSymbolic(bool value);
//! Set/Get a property of this target file
void SetProperty(std::string const& prop, cmValue value);
void SetProperty(std::string const& prop, std::nullptr_t)
@@ -232,6 +234,7 @@ public:
bool IsForeign() const;
bool IsPerConfig() const;
bool IsRuntimeBinary() const;
bool IsSymbolic() const;
bool CanCompileSources() const;
bool GetMappedConfig(std::string const& desiredConfig, cmValue& loc,

View File

@@ -102,6 +102,11 @@ bool cmTargetLinkLibrariesCommand(std::vector<std::string> const& args,
return true;
}
if (target->IsSymbolic()) {
status.SetError("can not be used on a SYMBOLIC target.");
return false;
}
// Having a UTILITY library on the LHS is a bug.
if (target->GetType() == cmStateEnums::UTILITY) {
mf.IssueMessage(

View File

@@ -42,6 +42,10 @@ bool cmTargetPropCommandBase::HandleArguments(
this->HandleMissingTarget(args[0]);
return false;
}
if (this->Target->IsSymbolic()) {
this->SetError("can not be used on a SYMBOLIC target.");
return false;
}
bool const isRegularTarget =
(this->Target->GetType() == cmStateEnums::EXECUTABLE) ||
(this->Target->GetType() == cmStateEnums::STATIC_LIBRARY) ||

View File

@@ -0,0 +1,5 @@
add_library(gui INTERFACE)
add_library(widget INTERFACE SYMBOLIC)
install(TARGETS gui widget EXPORT gui-targets)
install(EXPORT gui-targets DESTINATION lib/cmake/gui FILE gui-config.cmake NAMESPACE gui::)

View File

@@ -0,0 +1,6 @@
enable_language(C)
find_package(gui REQUIRED)
add_library(baz INTERFACE)
target_link_libraries(baz INTERFACE gui::gui gui::widget)

View File

@@ -18,11 +18,14 @@ function(run_ExportImport_test case)
run_cmake_with_options(${case}-import
-Dfoo_DIR=${CMAKE_INSTALL_PREFIX}/lib/cmake/foo
-Dbar_DIR=${CMAKE_INSTALL_PREFIX}/lib/cmake/bar
-Dgui_DIR=${CMAKE_INSTALL_PREFIX}/lib/cmake/gui
)
endfunction()
run_ExportImport_test(SharedDep)
run_ExportImport_test(SpdxLicenseProperty)
run_ExportImport_test(InterfaceWithSymbolic)
function(run_ExportImportBuildInstall_test case)
set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/${case}-export-build)

View File

@@ -0,0 +1,7 @@
include(${CMAKE_CURRENT_LIST_DIR}/Assertions.cmake)
set(out_dir "${RunCMake_BINARY_DIR}/ExportSymbolicComponent-build")
file(READ "${out_dir}/foo.cps" content)
expect_value("${content}" "foo" "name")
expect_value("${content}" "symbolic" "components" "foo" "type")

View File

@@ -0,0 +1,4 @@
add_library(foo INTERFACE SYMBOLIC)
install(TARGETS foo EXPORT foo DESTINATION .)
export(EXPORT foo PACKAGE_INFO foo)

View File

@@ -38,6 +38,7 @@ run_cmake(Minimal)
run_cmake(MinimalVersion)
run_cmake(LowerCaseFile)
run_cmake(Requirements)
run_cmake(ExportSymbolicComponent)
run_cmake(TargetTypes)
run_cmake(DependsMultiple)
run_cmake(LinkOnly)
@@ -46,3 +47,4 @@ run_cmake(EmptyConfig)
run_cmake(FileSetHeaders)
run_cmake(DependencyVersionCMake)
run_cmake(DependencyVersionCps)
run_cmake(TransitiveSymbolicComponent)

View File

@@ -0,0 +1,16 @@
include(${CMAKE_CURRENT_LIST_DIR}/Assertions.cmake)
set(out_dir "${RunCMake_BINARY_DIR}/TransitiveSymbolicComponent-build")
file(READ "${out_dir}/bar.cps" content)
expect_value("${content}" "bar" "name")
expect_array("${content}" 1 "requires" "Symbolic" "components")
expect_value("${content}" "test" "requires" "Symbolic" "components" 0)
expect_value("${content}" "1.0" "requires" "Symbolic" "version")
expect_array("${content}" 1 "requires" "Symbolic" "hints")
expect_value("${content}" "${CMAKE_CURRENT_LIST_DIR}/cps" "requires" "Symbolic" "hints" 0)
string(JSON component GET "${content}" "components" "bar")
expect_array("${component}" 1 "requires")
expect_value("${component}" "Symbolic:test" "requires" 0)

View File

@@ -0,0 +1,14 @@
cmake_minimum_required(VERSION 4.0)
add_library(bar INTERFACE)
find_package(Symbolic REQUIRED CONFIG
COMPONENTS foo test
NO_DEFAULT_PATH
PATHS ${CMAKE_CURRENT_LIST_DIR}
)
target_link_libraries(bar INTERFACE Symbolic::test)
install(TARGETS bar EXPORT bar DESTINATION .)
export(EXPORT bar PACKAGE_INFO bar)

View File

@@ -0,0 +1,17 @@
{
"components" :
{
"foo" :
{
"type" : "interface"
},
"test" :
{
"type" : "symbolic"
}
},
"cps_path" : "@prefix@",
"cps_version" : "0.13.0",
"name" : "Symbolic",
"version": "1.0"
}

View File

@@ -0,0 +1,7 @@
include(${CMAKE_CURRENT_LIST_DIR}/Assertions.cmake)
set(out_dir "${RunCMake_BINARY_DIR}/InstallSymbolicComponent-build/CMakeFiles/Export/5058f1af8388633f609cadb75a75dc9d")
file(READ "${out_dir}/foo.cps" content)
expect_value("${content}" "foo" "name")
expect_value("${content}" "symbolic" "components" "foo" "type")

View File

@@ -0,0 +1,4 @@
add_library(foo INTERFACE SYMBOLIC)
install(TARGETS foo EXPORT foo)
install(PACKAGE_INFO foo EXPORT foo DESTINATION .)

View File

@@ -55,4 +55,6 @@ run_cmake(EmptyConfig)
run_cmake(FileSetHeaders)
run_cmake(DependencyVersionCMake)
run_cmake(DependencyVersionCps)
run_cmake(TransitiveSymbolicComponent)
run_cmake(InstallSymbolicComponent)
run_cmake_install(Destination)

View File

@@ -0,0 +1,15 @@
include(${CMAKE_CURRENT_LIST_DIR}/Assertions.cmake)
set(out_dir "${RunCMake_BINARY_DIR}/TransitiveSymbolicComponent-build/CMakeFiles/Export/5058f1af8388633f609cadb75a75dc9d")
file(READ "${out_dir}/bar.cps" content)
expect_value("${content}" "bar" "name")
expect_array("${content}" 1 "requires" "Symbolic" "components")
expect_value("${content}" "test" "requires" "Symbolic" "components" 0)
expect_value("${content}" "1.0" "requires" "Symbolic" "version")
expect_array("${content}" 1 "requires" "Symbolic" "hints")
expect_value("${content}" "${CMAKE_CURRENT_LIST_DIR}/cps" "requires" "Symbolic" "hints" 0)
string(JSON component GET "${content}" "components" "bar")
expect_array("${component}" 1 "requires")
expect_value("${component}" "Symbolic:test" "requires" 0)

View File

@@ -0,0 +1,14 @@
cmake_minimum_required(VERSION 4.0)
add_library(bar INTERFACE)
find_package(Symbolic REQUIRED CONFIG
COMPONENTS foo test
NO_DEFAULT_PATH
PATHS ${CMAKE_CURRENT_LIST_DIR}
)
target_link_libraries(bar INTERFACE Symbolic::test)
install(TARGETS bar EXPORT bar)
install(PACKAGE_INFO bar EXPORT bar DESTINATION .)

View File

@@ -0,0 +1,17 @@
{
"components" :
{
"foo" :
{
"type" : "interface"
},
"test" :
{
"type" : "symbolic"
}
},
"cps_path" : "@prefix@",
"cps_version" : "0.13.0",
"name" : "Symbolic",
"version": "1.0"
}

View File

@@ -0,0 +1,8 @@
add_library(TestSymbolicInterfaceLib INTERFACE SYMBOLIC)
get_target_property(IS_SYMBOLIC TestSymbolicInterfaceLib "SYMBOLIC")
if("${IS_SYMBOLIC}" STREQUAL "FALSE")
string(APPEND RunCMake_TEST_FAILED "Target: \"TestSymbolicInterfaceLib\" does not have the SYMBOLIC property")
set(RunCMake_TEST_FAILED "${RunCMake_TEST_FAILED}" PARENT_SCOPE)
endif()

View File

@@ -3,6 +3,7 @@ include(RunCMake)
run_cmake(CMP0073)
run_cmake(INTERFACEwithNoSources)
run_cmake(INTERFACEwithSYMBOLIC)
run_cmake(OBJECTwithNoSources)
run_cmake(STATICwithNoSources)
run_cmake(SHAREDwithNoSources)
@@ -19,6 +20,7 @@ run_cmake(UNKNOWNwithOnlyObjectSources)
run_cmake(INTERFACEwithNoSourcesButLinkObjects)
run_cmake(OBJECTwithNoSourcesButLinkObjects)
run_cmake(STATICwithNoSourcesButLinkObjects)
run_cmake(STATICwithSYMBOLIC)
run_cmake(SHAREDwithNoSourcesButLinkObjects)
run_cmake(MODULEwithNoSourcesButLinkObjects)
run_cmake(UNKNOWNwithNoSourcesButLinkObjects)

View File

@@ -0,0 +1 @@
1

View File

@@ -0,0 +1,4 @@
^CMake Error at STATICwithSYMBOLIC.cmake:[0-9]+ \(add_library\):
add_library SYMBOLIC option may only be used with INTERFACE libraries
Call Stack \(most recent call first\):
CMakeLists.txt:[0-9]+ \(include\)$

View File

@@ -0,0 +1 @@
add_library(TestStaticWithSymbolic STATIC SYMBOLIC)

View File

@@ -27,3 +27,4 @@ run_cmake(FindDependencyExportStatic)
run_cmake(FindDependencyExportShared)
#FIXME(#26622): The FindDependencyExportFetchContent test case exposes an assertion failure.
#run_cmake(FindDependencyExportFetchContent)
run_cmake(SymbolicComponent)

View File

@@ -0,0 +1,4 @@
file(READ "${RunCMake_TEST_BINARY_DIR}/foo.cmake" foo)
if("${foo}" MATCHES "add_library\(foo INTERFACE SYMBOLIC IMPORTED\)")
string(APPEND RunCMake_TEST_FAILED "Symbolic Component Foo was not exported\n")
endif()

View File

@@ -0,0 +1,2 @@
add_library(foo INTERFACE SYMBOLIC)
export(TARGETS foo FILE foo.cmake)

View File

@@ -54,9 +54,4 @@ run_cmake(MissingComponentDependency)
run_cmake(MissingTransitiveComponentCPS)
run_cmake(MissingTransitiveComponentCMake)
run_cmake(MissingTransitiveComponentDependency)
# Configuration selection tests
run_cmake_build(ConfigDefault)
run_cmake_build(ConfigFirst)
run_cmake_build(ConfigMapped)
run_cmake_build(ConfigMatchBuildType Test)
run_cmake(SymbolicComponents)

View File

@@ -0,0 +1,8 @@
cmake_minimum_required(VERSION 4.0)
include(Setup.cmake)
add_library(bar INTERFACE)
find_package(Symbolic REQUIRED COMPONENTS foo test)
target_link_libraries(bar INTERFACE Symbolic::foo Symbolic::test)

View File

@@ -0,0 +1,17 @@
{
"components" :
{
"foo" :
{
"type" : "interface"
},
"test" :
{
"type" : "symbolic"
}
},
"cps_path" : "@prefix@",
"cps_version" : "0.13.0",
"name" : "Symbolic",
"version": "1.0"
}

View File

@@ -97,7 +97,7 @@ foreach(policy IN ITEMS NEW OLD WARN)
run_install_test(CMP0177-${policy})
run_cmake_with_options(CMP0177-${policy}-verify
-DCMAKE_PREFIX_PATH=${RunCMake_BINARY_DIR}/CMP0177-${policy}-build/root-all
)
)
endforeach()
run_cmake(TARGETS-ImportedGlobal)
run_cmake(TARGETS-NAMELINK_COMPONENT-bad-all)
@@ -106,7 +106,7 @@ run_cmake(FILES-DESTINATION-TYPE)
run_cmake(DIRECTORY-DESTINATION-TYPE)
run_cmake(FILES-directory)
if(NOT WIN32)
run_cmake(FILES-symlink-to-directory)
run_cmake(FILES-symlink-to-directory)
endif()
set(RunCMake_TEST_OPTIONS "-DCMAKE_BUILD_TYPE:STRING=Debug")
@@ -131,6 +131,7 @@ run_install_test(TARGETS-OPTIONAL)
run_install_test(FILES-OPTIONAL)
run_install_test(DIRECTORY-OPTIONAL)
run_install_test(TARGETS-Defaults)
run_install_test(TARGETS-SymbolicComponent)
if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
run_install_test(TARGETS-NAMELINK-No-Tweak)

View File

@@ -0,0 +1,5 @@
file(READ "${RunCMake_TEST_BINARY_DIR}/root-all/foo.cmake" foo)
if("${foo}" MATCHES "add_library\(foo INTERFACE SYMBOLIC IMPORTED\)")
string(APPEND RunCMake_TEST_FAILED "Symbolic Component Foo was not exported\n")
endif()

View File

@@ -0,0 +1,3 @@
add_library(foo INTERFACE SYMBOLIC)
install(TARGETS foo EXPORT foo)
install(EXPORT foo DESTINATION . FILE foo.cmake)