mirror of
https://github.com/Kitware/CMake.git
synced 2026-08-11 09:35:12 +00:00
Export: Expand CPS default targets
CPS provides "default targets" as a user convenience which can be linked by specifying only the package name. CMake implements this via a utility `INTERFACE` target. However, because it is not namespaced, this is not a valid requirement, which prevents exporting anything linking to this target to CPS. Work around this by introducing a new (internal) target property which replaces a target with its link interface targets at export time.
This commit is contained in:
@@ -398,6 +398,21 @@ bool cmExportFileGenerator::AddTargetNamespace(std::string& input,
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<std::string> replace = tgt->Target->GetExportTargets();
|
||||
if (!replace.empty()) {
|
||||
std::vector<std::string> out;
|
||||
out.reserve(replace.size());
|
||||
|
||||
bool result = true;
|
||||
for (std::string& t : replace) {
|
||||
result = this->AddTargetNamespace(t, target, lg) && result;
|
||||
out.emplace_back(std::move(t));
|
||||
}
|
||||
|
||||
input = cmJoin(out, ";"_s);
|
||||
return result;
|
||||
}
|
||||
|
||||
cmFindPackageStack const& pkgStack = tgt->Target->GetFindPackageStack();
|
||||
if (!pkgStack.Empty() ||
|
||||
tgt->Target->GetProperty("EXPORT_FIND_PACKAGE_NAME")) {
|
||||
|
||||
@@ -1005,6 +1005,7 @@ bool cmPackageInfoReader::ImportTargets(cmMakefile* makefile,
|
||||
std::string const& fullName = cmStrCat(package, "::"_s, name);
|
||||
AppendProperty(makefile, target, "LINK_LIBRARIES"_s, {}, fullName);
|
||||
}
|
||||
target->SetExportPassthrough(true);
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
@@ -631,6 +631,7 @@ public:
|
||||
bool PerConfig = false;
|
||||
bool IsSymbolic = false;
|
||||
bool IsForTryCompile = false;
|
||||
bool IsExportPassthrough = false;
|
||||
cmTarget::Visibility TargetVisibility;
|
||||
std::set<BT<std::pair<std::string, bool>>> Utilities;
|
||||
std::set<std::string> CodegenDependencies;
|
||||
@@ -2150,6 +2151,12 @@ void cmTarget::SetSymbolic(bool const value)
|
||||
this->impl->IsSymbolic = value;
|
||||
}
|
||||
|
||||
void cmTarget::SetExportPassthrough(bool value)
|
||||
{
|
||||
assert(this->impl->TargetType == cm::TargetType::INTERFACE_LIBRARY);
|
||||
this->impl->IsExportPassthrough = value;
|
||||
}
|
||||
|
||||
void cmTarget::SetProperty(std::string const& prop, cmValue value)
|
||||
{
|
||||
if (!IsSettableProperty(this->impl->Makefile, this, prop)) {
|
||||
@@ -3007,6 +3014,14 @@ bool cmTarget::IsForTryCompile() const
|
||||
return this->impl->IsForTryCompile;
|
||||
}
|
||||
|
||||
std::vector<std::string> cmTarget::GetExportTargets() const
|
||||
{
|
||||
if (this->impl->IsExportPassthrough) {
|
||||
return cm::remove_BT(this->impl->InterfaceLinkLibraries.Entries);
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
char const* cmTarget::GetSuffixVariableInternal(
|
||||
cmStateEnums::ArtifactType artifact) const
|
||||
{
|
||||
|
||||
@@ -210,6 +210,8 @@ public:
|
||||
|
||||
void SetSymbolic(bool value);
|
||||
|
||||
void SetExportPassthrough(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)
|
||||
@@ -254,6 +256,13 @@ public:
|
||||
void SetIsForTryCompile();
|
||||
bool IsForTryCompile() const;
|
||||
|
||||
/**
|
||||
* Get the list of targets which should replace this target on export. This
|
||||
* equals the target's interface link libraries iff the target is an export
|
||||
* passthrough target. For all other targets, this returns an empty list.
|
||||
*/
|
||||
std::vector<std::string> GetExportTargets() const;
|
||||
|
||||
bool GetMappedConfig(std::string const& desiredConfig, cmValue& loc,
|
||||
cmValue& imp, std::string& suffix) const;
|
||||
|
||||
|
||||
@@ -1515,6 +1515,7 @@ add_RunCMake_test(AutoExportDll
|
||||
)
|
||||
|
||||
add_RunCMake_test(AndroidMK)
|
||||
add_RunCMake_test(ExportCpsDefaultComponents)
|
||||
add_RunCMake_test(ExportPackageInfo)
|
||||
add_RunCMake_test(ExportSbom)
|
||||
add_RunCMake_test(EnvSbom)
|
||||
|
||||
39
Tests/RunCMake/ExportCpsDefaultComponents/Assertions.cmake
Normal file
39
Tests/RunCMake/ExportCpsDefaultComponents/Assertions.cmake
Normal file
@@ -0,0 +1,39 @@
|
||||
macro(_expect entity op actual expected)
|
||||
if(NOT "${actual}" ${op} "${expected}")
|
||||
list(JOIN ARGN "." name)
|
||||
set(RunCMake_TEST_FAILED
|
||||
"Attribute '${name}' ${entity} '${actual}' does not match expected ${entity} '${expected}'" PARENT_SCOPE)
|
||||
return()
|
||||
endif()
|
||||
endmacro()
|
||||
|
||||
function(expect_value content expected_value)
|
||||
string(JSON actual_value GET "${content}" ${ARGN})
|
||||
_expect("value" STREQUAL "${actual_value}" "${expected_value}" ${ARGN})
|
||||
endfunction()
|
||||
|
||||
function(expect_array content expected_length)
|
||||
string(JSON actual_type TYPE "${content}" ${ARGN})
|
||||
_expect("type" STREQUAL "${actual_type}" "ARRAY" ${ARGN})
|
||||
|
||||
string(JSON actual_length LENGTH "${content}" ${ARGN})
|
||||
_expect("length" EQUAL "${actual_length}" "${expected_length}" ${ARGN})
|
||||
endfunction()
|
||||
|
||||
function(expect_object content)
|
||||
string(JSON actual_type TYPE "${content}" ${ARGN})
|
||||
_expect("type" STREQUAL "${actual_type}" "OBJECT" ${ARGN})
|
||||
endfunction()
|
||||
|
||||
function(expect_null content)
|
||||
string(JSON actual_type TYPE "${content}" ${ARGN})
|
||||
_expect("type" STREQUAL "${actual_type}" "NULL" ${ARGN})
|
||||
endfunction()
|
||||
|
||||
function(expect_missing content)
|
||||
string(JSON value ERROR_VARIABLE error GET "${content}" ${ARGN})
|
||||
if(NOT value MATCHES "^(.*-)?NOTFOUND$")
|
||||
set(RunCMake_TEST_FAILED
|
||||
"Attribute '${ARGN}' is unexpectedly present" PARENT_SCOPE)
|
||||
endif()
|
||||
endfunction()
|
||||
3
Tests/RunCMake/ExportCpsDefaultComponents/CMakeLists.txt
Normal file
3
Tests/RunCMake/ExportCpsDefaultComponents/CMakeLists.txt
Normal file
@@ -0,0 +1,3 @@
|
||||
cmake_minimum_required(VERSION 4.4.0)
|
||||
project(${RunCMake_TEST} NONE)
|
||||
include(${RunCMake_TEST}.cmake)
|
||||
@@ -0,0 +1,13 @@
|
||||
include(${CMAKE_CURRENT_LIST_DIR}/Assertions.cmake)
|
||||
|
||||
set(out_dir "${RunCMake_BINARY_DIR}/GenExExportCMake-build")
|
||||
|
||||
file(READ "${out_dir}/bar.cmake" bar_cmake)
|
||||
if(NOT "${bar_cmake}" MATCHES "add_library\\(bar INTERFACE IMPORTED\\)")
|
||||
string(APPEND RunCMake_TEST_FAILED
|
||||
"Interface library 'bar' was not exported\n")
|
||||
endif()
|
||||
if(NOT "${bar_cmake}" MATCHES "set_target_properties\\(bar PROPERTIES[ \n]+INTERFACE_LINK_LIBRARIES \"[\\][\$]<1:foo>\"[ \n]*\\)")
|
||||
string(APPEND RunCMake_TEST_FAILED
|
||||
"Interface library 'bar' has wrong link libraries\n")
|
||||
endif()
|
||||
@@ -0,0 +1,11 @@
|
||||
find_package(
|
||||
foo REQUIRED CONFIG
|
||||
NO_DEFAULT_PATH
|
||||
PATHS ${CMAKE_CURRENT_LIST_DIR}
|
||||
)
|
||||
|
||||
add_library(bar INTERFACE)
|
||||
target_link_libraries(bar INTERFACE $<1:foo>)
|
||||
|
||||
install(TARGETS bar EXPORT bar)
|
||||
export(EXPORT bar)
|
||||
@@ -0,0 +1 @@
|
||||
1
|
||||
@@ -0,0 +1,5 @@
|
||||
CMake Error in CMakeLists\.txt:
|
||||
Property "INTERFACE_LINK_LIBRARIES" of target "bar" contains a generator
|
||||
expression\. This is not allowed\.[
|
||||
]+
|
||||
CMake Generate step failed\. Build files cannot be regenerated correctly\.
|
||||
@@ -0,0 +1,11 @@
|
||||
find_package(
|
||||
foo REQUIRED CONFIG
|
||||
NO_DEFAULT_PATH
|
||||
PATHS ${CMAKE_CURRENT_LIST_DIR}
|
||||
)
|
||||
|
||||
add_library(bar INTERFACE)
|
||||
target_link_libraries(bar INTERFACE $<1:foo>)
|
||||
|
||||
install(TARGETS bar EXPORT bar)
|
||||
export(PACKAGE_INFO bar EXPORT bar)
|
||||
@@ -0,0 +1,27 @@
|
||||
include(${CMAKE_CURRENT_LIST_DIR}/Assertions.cmake)
|
||||
|
||||
set(out_dir "${RunCMake_BINARY_DIR}/NormalExport-build")
|
||||
|
||||
file(READ "${out_dir}/cps/bar/bar.cps" content)
|
||||
expect_value("${content}" "bar" "name")
|
||||
expect_value("${content}" "interface" "components" "bar" "type")
|
||||
|
||||
expect_value("${content}" "bar" "name")
|
||||
expect_array("${content}" 2 "requires" "foo" "components")
|
||||
expect_value("${content}" "foo1" "requires" "foo" "components" 0)
|
||||
expect_value("${content}" "foo2" "requires" "foo" "components" 1)
|
||||
|
||||
string(JSON component GET "${content}" "components" "bar")
|
||||
expect_array("${component}" 2 "requires")
|
||||
expect_value("${component}" "foo:foo1" "requires" 0)
|
||||
expect_value("${component}" "foo:foo2" "requires" 1)
|
||||
|
||||
file(READ "${out_dir}/bar.cmake" bar_cmake)
|
||||
if(NOT "${bar_cmake}" MATCHES "add_library\\(bar INTERFACE IMPORTED\\)")
|
||||
string(APPEND RunCMake_TEST_FAILED
|
||||
"Interface library 'bar' was not exported\n")
|
||||
endif()
|
||||
if(NOT "${bar_cmake}" MATCHES "set_target_properties\\(bar PROPERTIES[ \n]+INTERFACE_LINK_LIBRARIES \"foo::foo1;foo::foo2\"[ \n]*\\)")
|
||||
string(APPEND RunCMake_TEST_FAILED
|
||||
"Interface library 'bar' has wrong link libraries\n")
|
||||
endif()
|
||||
12
Tests/RunCMake/ExportCpsDefaultComponents/NormalExport.cmake
Normal file
12
Tests/RunCMake/ExportCpsDefaultComponents/NormalExport.cmake
Normal file
@@ -0,0 +1,12 @@
|
||||
find_package(
|
||||
foo REQUIRED CONFIG
|
||||
NO_DEFAULT_PATH
|
||||
PATHS ${CMAKE_CURRENT_LIST_DIR}
|
||||
)
|
||||
|
||||
add_library(bar INTERFACE)
|
||||
target_link_libraries(bar INTERFACE foo)
|
||||
|
||||
install(TARGETS bar EXPORT bar)
|
||||
export(EXPORT bar)
|
||||
export(PACKAGE_INFO bar EXPORT bar)
|
||||
@@ -0,0 +1,8 @@
|
||||
include(RunCMake)
|
||||
|
||||
# Test normal usage
|
||||
run_cmake(NormalExport)
|
||||
|
||||
# Test usage via generator expression
|
||||
run_cmake(GenExExportCMake)
|
||||
run_cmake(GenExExportCps)
|
||||
17
Tests/RunCMake/ExportCpsDefaultComponents/cps/foo.cps
Normal file
17
Tests/RunCMake/ExportCpsDefaultComponents/cps/foo.cps
Normal file
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"cps_version" : "0.13.0",
|
||||
"name" : "foo",
|
||||
"cps_path" : "@prefix@/cps",
|
||||
"default_components" : [ "foo1", "foo2" ],
|
||||
"components" :
|
||||
{
|
||||
"foo1" :
|
||||
{
|
||||
"type" : "interface"
|
||||
},
|
||||
"foo2" :
|
||||
{
|
||||
"type" : "interface"
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user