From c3d279841b0981dec0721ccb876568f24cbfa833 Mon Sep 17 00:00:00 2001 From: Matthew Woehlke Date: Wed, 5 Mar 2025 14:47:49 -0500 Subject: find_package: CPS component requirements != CMake components Modify how CMake handles required components of a CPS transitive dependency to not pass them as COMPONENTS if a CMake-script package is found as the resolved dependency. This is necessary as many CMake-script package description files do not treat component requests as target requests (which, in CPS-land, they effectively are), but do implement logic to mark themselves 'not found' if requested components are missing. As a result, passing in the required targets as required components is likely to cause the dependency to be spuriously not found if it is only available via a CMake-script package configuration file. Fix this by introducing a new 'required targets' concept, and by passing CPS component requirements as both required targets and optional components. The latter serves as a hint for packages that might provide only a subset of themselves. The former is used to post-validate a CMake-script package, or is folded on-the-fly into required components when considering CPS packages. Note that this functionality is not exposed to the user at this time, and is only used when resolving transitive dependencies for a CPS package. --- Source/cmFindPackageCommand.cxx | 44 +++++++++++++++++----- Source/cmFindPackageCommand.h | 1 + Source/cmMakefile.cxx | 9 +++++ Source/cmMakefile.h | 2 + Tests/FindPackageCpsTest/cps/bar.cps | 8 +++- .../MissingTransitiveComponent-result.txt | 1 - .../MissingTransitiveComponent-stderr.txt | 17 --------- .../MissingTransitiveComponent.cmake | 7 ---- .../MissingTransitiveComponentCMake-result.txt | 1 + .../MissingTransitiveComponentCMake-stderr.txt | 11 ++++++ .../MissingTransitiveComponentCMake.cmake | 7 ++++ .../MissingTransitiveComponentCPS-result.txt | 1 + .../MissingTransitiveComponentCPS-stderr.txt | 18 +++++++++ .../MissingTransitiveComponentCPS.cmake | 7 ++++ Tests/RunCMake/find_package-CPS/RunCMakeTest.cmake | 3 +- .../cmake/cmaketestpackage-config.cmake | 1 + .../find_package-CPS/cps/transitivemissing.cps | 11 ------ .../cps/transitivemissingcmake.cps | 11 ++++++ .../find_package-CPS/cps/transitivemissingcps.cps | 11 ++++++ 19 files changed, 123 insertions(+), 48 deletions(-) delete mode 100644 Tests/RunCMake/find_package-CPS/MissingTransitiveComponent-result.txt delete mode 100644 Tests/RunCMake/find_package-CPS/MissingTransitiveComponent-stderr.txt delete mode 100644 Tests/RunCMake/find_package-CPS/MissingTransitiveComponent.cmake create mode 100644 Tests/RunCMake/find_package-CPS/MissingTransitiveComponentCMake-result.txt create mode 100644 Tests/RunCMake/find_package-CPS/MissingTransitiveComponentCMake-stderr.txt create mode 100644 Tests/RunCMake/find_package-CPS/MissingTransitiveComponentCMake.cmake create mode 100644 Tests/RunCMake/find_package-CPS/MissingTransitiveComponentCPS-result.txt create mode 100644 Tests/RunCMake/find_package-CPS/MissingTransitiveComponentCPS-stderr.txt create mode 100644 Tests/RunCMake/find_package-CPS/MissingTransitiveComponentCPS.cmake create mode 100644 Tests/RunCMake/find_package-CPS/cmake/cmaketestpackage-config.cmake delete mode 100644 Tests/RunCMake/find_package-CPS/cps/transitivemissing.cps create mode 100644 Tests/RunCMake/find_package-CPS/cps/transitivemissingcmake.cps create mode 100644 Tests/RunCMake/find_package-CPS/cps/transitivemissingcps.cps diff --git a/Source/cmFindPackageCommand.cxx b/Source/cmFindPackageCommand.cxx index 7dbf668..ce2f536 100644 --- a/Source/cmFindPackageCommand.cxx +++ b/Source/cmFindPackageCommand.cxx @@ -1525,6 +1525,7 @@ bool cmFindPackageCommand::HandlePackageMode( bool result = true; bool found = false; bool configFileSetFOUNDFalse = false; + std::vector missingTargets; if (fileFound) { if (this->Makefile->IsDefinitionSet(foundVar) && @@ -1559,6 +1560,17 @@ bool cmFindPackageCommand::HandlePackageMode( notFoundMessage = this->Makefile->GetSafeDefinition(notFoundMessageVar); } + + // Check whether the required targets are defined. + if (found && !this->RequiredTargets.empty()) { + for (std::string const& t : this->RequiredTargets) { + std::string qualifiedTarget = cmStrCat(this->Name, "::"_s, t); + if (!this->Makefile->FindImportedTarget(qualifiedTarget)) { + missingTargets.emplace_back(std::move(qualifiedTarget)); + found = false; + } + } + } } else { // The configuration file is invalid. result = false; @@ -1593,10 +1605,18 @@ bool cmFindPackageCommand::HandlePackageMode( if (!notFoundMessage.empty()) { e << " Reason given by package: \n" << notFoundMessage << "\n"; } - } - // If there are files in ConsideredConfigs, it means that FooConfig.cmake - // have been found, but they didn't have appropriate versions. - else if (!this->ConsideredConfigs.empty()) { + } else if (!missingTargets.empty()) { + e << "Found package configuration file:\n" + " " + << this->FileFound + << "\n" + "but the following required targets were not found:\n" + " " + << cmJoin(cmMakeRange(missingTargets), ", "_s); + } else if (!this->ConsideredConfigs.empty()) { + // If there are files in ConsideredConfigs, it means that + // FooConfig.cmake have been found, but they didn't have appropriate + // versions. auto duplicate_end = cmRemoveDuplicates(this->ConsideredConfigs); e << "Could not find a configuration file for package \"" << this->Name << "\" that " @@ -1903,7 +1923,7 @@ bool cmFindPackageCommand::ReadPackage() return false; } - auto const hasComponentsRequested = + bool const hasComponentsRequested = !this->RequiredComponents.empty() || !this->OptionalComponents.empty(); cmMakefile::CallRAII scope{ this->Makefile, this->FileFound, this->Status }; @@ -1994,8 +2014,9 @@ bool cmFindPackageCommand::FindPackageDependencies( fp.VersionPatch, fp.VersionTweak); fp.Components = cmJoin(cmMakeRange(dep.Components), ";"_s); - fp.RequiredComponents = + fp.OptionalComponents = std::set{ dep.Components.begin(), dep.Components.end() }; + fp.RequiredTargets = fp.OptionalComponents; // TODO set hints @@ -2793,10 +2814,14 @@ bool cmFindPackageCommand::CheckVersion(std::string const& config_file) } // Verify that all required components are available. + std::set requiredComponents = this->RequiredComponents; + requiredComponents.insert(this->RequiredTargets.begin(), + this->RequiredTargets.end()); + std::vector missingComponents; - std::set_difference(this->RequiredComponents.begin(), - this->RequiredComponents.end(), - allComponents.begin(), allComponents.end(), + std::set_difference(requiredComponents.begin(), + requiredComponents.end(), allComponents.begin(), + allComponents.end(), std::back_inserter(missingComponents)); if (!missingComponents.empty()) { result = false; @@ -2833,6 +2858,7 @@ bool cmFindPackageCommand::CheckVersion(std::string const& config_file) } this->CpsReader = std::move(reader); this->CpsAppendices = std::move(appendices); + this->RequiredComponents = std::move(requiredComponents); } } } else { diff --git a/Source/cmFindPackageCommand.h b/Source/cmFindPackageCommand.h index 096fc07..bac09ee 100644 --- a/Source/cmFindPackageCommand.h +++ b/Source/cmFindPackageCommand.h @@ -254,6 +254,7 @@ private: std::string Components; std::set RequiredComponents; std::set OptionalComponents; + std::set RequiredTargets; std::string DebugBuffer; struct ConfigName diff --git a/Source/cmMakefile.cxx b/Source/cmMakefile.cxx index 34d917c..beca10d 100644 --- a/Source/cmMakefile.cxx +++ b/Source/cmMakefile.cxx @@ -1728,6 +1728,15 @@ std::string const& cmMakefile::GetCurrentBinaryDirectory() const return this->StateSnapshot.GetDirectory().GetCurrentBinary(); } +cmTarget* cmMakefile::FindImportedTarget(std::string const& name) const +{ + auto const i = this->ImportedTargets.find(name); + if (i != this->ImportedTargets.end()) { + return i->second; + } + return nullptr; +} + std::vector cmMakefile::GetImportedTargets() const { std::vector tgts; diff --git a/Source/cmMakefile.h b/Source/cmMakefile.h index bfc3496..89b3c2b 100644 --- a/Source/cmMakefile.h +++ b/Source/cmMakefile.h @@ -477,6 +477,8 @@ public: } std::vector GetImportedTargets() const; + cmTarget* FindImportedTarget(std::string const& name) const; + cmTarget* FindLocalNonAliasTarget(std::string const& name) const; /** Find a target to use in place of the given name. The target diff --git a/Tests/FindPackageCpsTest/cps/bar.cps b/Tests/FindPackageCpsTest/cps/bar.cps index aeeebda..eb0e1d3 100644 --- a/Tests/FindPackageCpsTest/cps/bar.cps +++ b/Tests/FindPackageCpsTest/cps/bar.cps @@ -3,8 +3,12 @@ "name": "Bar", "cps_path": "@prefix@/cps", "requires": { - "Dep1": null, - "Dep2": null + "Dep1": { + "components": [ "Target" ] + }, + "Dep2": { + "components": [ "Target" ] + } }, "components": { "Target1": { diff --git a/Tests/RunCMake/find_package-CPS/MissingTransitiveComponent-result.txt b/Tests/RunCMake/find_package-CPS/MissingTransitiveComponent-result.txt deleted file mode 100644 index d00491f..0000000 --- a/Tests/RunCMake/find_package-CPS/MissingTransitiveComponent-result.txt +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/Tests/RunCMake/find_package-CPS/MissingTransitiveComponent-stderr.txt b/Tests/RunCMake/find_package-CPS/MissingTransitiveComponent-stderr.txt deleted file mode 100644 index 50c4afe..0000000 --- a/Tests/RunCMake/find_package-CPS/MissingTransitiveComponent-stderr.txt +++ /dev/null @@ -1,17 +0,0 @@ -CMake Error in cps/[Tt]ransitive[Mm]issing\.cps: - Could not find a configuration file for package "ComponentTest" that is - compatible with requested version ""\. - - The following configuration files were considered but not accepted: -( - [^ -]*/Tests/RunCMake/find_package-CPS/cps/[Cc]omponent[Tt]est\.cps, version: 1\.0)+ - -Call Stack \(most recent call first\): - MissingTransitiveComponent\.cmake:[0-9]+ \(find_package\) - CMakeLists\.txt:[0-9]+ \(include\) -+ -CMake Error at MissingTransitiveComponent\.cmake:[0-9]+ \(find_package\): - find_package could not find ComponentTest, required by TransitiveMissing\. -Call Stack \(most recent call first\): - CMakeLists\.txt:[0-9]+ \(include\) diff --git a/Tests/RunCMake/find_package-CPS/MissingTransitiveComponent.cmake b/Tests/RunCMake/find_package-CPS/MissingTransitiveComponent.cmake deleted file mode 100644 index e6951a9..0000000 --- a/Tests/RunCMake/find_package-CPS/MissingTransitiveComponent.cmake +++ /dev/null @@ -1,7 +0,0 @@ -cmake_minimum_required(VERSION 4.0) - -include(Setup.cmake) - -############################################################################### -# Test depending on components of another package which are unavailable. -find_package(TransitiveMissing REQUIRED) diff --git a/Tests/RunCMake/find_package-CPS/MissingTransitiveComponentCMake-result.txt b/Tests/RunCMake/find_package-CPS/MissingTransitiveComponentCMake-result.txt new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/Tests/RunCMake/find_package-CPS/MissingTransitiveComponentCMake-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/find_package-CPS/MissingTransitiveComponentCMake-stderr.txt b/Tests/RunCMake/find_package-CPS/MissingTransitiveComponentCMake-stderr.txt new file mode 100644 index 0000000..12253c1 --- /dev/null +++ b/Tests/RunCMake/find_package-CPS/MissingTransitiveComponentCMake-stderr.txt @@ -0,0 +1,11 @@ +CMake Error in cps/[Tt]ransitive[Mm]issing[Cc][Mm]ake\.cps: + Found package configuration file: +( + [^ +]*/Tests/RunCMake/find_package-CPS/cmake/cmaketestpackage-config.cmake)+ + + but the following required targets were not found:[ + ]+CMakeTestPackage::DoesNotExist +Call Stack \(most recent call first\): + MissingTransitiveComponentCMake\.cmake:[0-9]+ \(find_package\) + CMakeLists\.txt:[0-9]+ \(include\) diff --git a/Tests/RunCMake/find_package-CPS/MissingTransitiveComponentCMake.cmake b/Tests/RunCMake/find_package-CPS/MissingTransitiveComponentCMake.cmake new file mode 100644 index 0000000..459bf94 --- /dev/null +++ b/Tests/RunCMake/find_package-CPS/MissingTransitiveComponentCMake.cmake @@ -0,0 +1,7 @@ +cmake_minimum_required(VERSION 4.0) + +include(Setup.cmake) + +############################################################################### +# Test depending on components of another package which are unavailable. +find_package(TransitiveMissingCMake REQUIRED) diff --git a/Tests/RunCMake/find_package-CPS/MissingTransitiveComponentCPS-result.txt b/Tests/RunCMake/find_package-CPS/MissingTransitiveComponentCPS-result.txt new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/Tests/RunCMake/find_package-CPS/MissingTransitiveComponentCPS-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/find_package-CPS/MissingTransitiveComponentCPS-stderr.txt b/Tests/RunCMake/find_package-CPS/MissingTransitiveComponentCPS-stderr.txt new file mode 100644 index 0000000..1a2b9e8 --- /dev/null +++ b/Tests/RunCMake/find_package-CPS/MissingTransitiveComponentCPS-stderr.txt @@ -0,0 +1,18 @@ +CMake Error in cps/[Tt]ransitive[Mm]issing[Cc][Pp][Ss]\.cps: + Could not find a configuration file for package "ComponentTest" that is + compatible with requested version ""\. + + The following configuration files were considered but not accepted: +( + [^ +]*/Tests/RunCMake/find_package-CPS/cps/[Cc]omponent[Tt]est\.cps, version: 1\.0)+ + +Call Stack \(most recent call first\): + MissingTransitiveComponentCPS\.cmake:[0-9]+ \(find_package\) + CMakeLists\.txt:[0-9]+ \(include\) ++ +CMake Error at MissingTransitiveComponentCPS\.cmake:[0-9]+ \(find_package\): + find_package could not find ComponentTest, required by + TransitiveMissingCPS\. +Call Stack \(most recent call first\): + CMakeLists\.txt:[0-9]+ \(include\) diff --git a/Tests/RunCMake/find_package-CPS/MissingTransitiveComponentCPS.cmake b/Tests/RunCMake/find_package-CPS/MissingTransitiveComponentCPS.cmake new file mode 100644 index 0000000..feca4f7 --- /dev/null +++ b/Tests/RunCMake/find_package-CPS/MissingTransitiveComponentCPS.cmake @@ -0,0 +1,7 @@ +cmake_minimum_required(VERSION 4.0) + +include(Setup.cmake) + +############################################################################### +# Test depending on components of another package which are unavailable. +find_package(TransitiveMissingCPS REQUIRED) diff --git a/Tests/RunCMake/find_package-CPS/RunCMakeTest.cmake b/Tests/RunCMake/find_package-CPS/RunCMakeTest.cmake index 8df4f60..c77bed5 100644 --- a/Tests/RunCMake/find_package-CPS/RunCMakeTest.cmake +++ b/Tests/RunCMake/find_package-CPS/RunCMakeTest.cmake @@ -27,5 +27,6 @@ run_cmake(VersionLimit4) run_cmake(MissingTransitiveDependency) run_cmake(MissingComponent) run_cmake(MissingComponentDependency) -run_cmake(MissingTransitiveComponent) +run_cmake(MissingTransitiveComponentCPS) +run_cmake(MissingTransitiveComponentCMake) run_cmake(MissingTransitiveComponentDependency) diff --git a/Tests/RunCMake/find_package-CPS/cmake/cmaketestpackage-config.cmake b/Tests/RunCMake/find_package-CPS/cmake/cmaketestpackage-config.cmake new file mode 100644 index 0000000..deffa57 --- /dev/null +++ b/Tests/RunCMake/find_package-CPS/cmake/cmaketestpackage-config.cmake @@ -0,0 +1 @@ +# Test config file. diff --git a/Tests/RunCMake/find_package-CPS/cps/transitivemissing.cps b/Tests/RunCMake/find_package-CPS/cps/transitivemissing.cps deleted file mode 100644 index 2a294aa..0000000 --- a/Tests/RunCMake/find_package-CPS/cps/transitivemissing.cps +++ /dev/null @@ -1,11 +0,0 @@ -{ - "cps_version": "0.13", - "name": "TransitiveMissing", - "cps_path": "@prefix@/cps", - "requires": { - "ComponentTest": { - "components": [ "DoesNotExist" ] - } - }, - "components": {} -} diff --git a/Tests/RunCMake/find_package-CPS/cps/transitivemissingcmake.cps b/Tests/RunCMake/find_package-CPS/cps/transitivemissingcmake.cps new file mode 100644 index 0000000..f24a25b --- /dev/null +++ b/Tests/RunCMake/find_package-CPS/cps/transitivemissingcmake.cps @@ -0,0 +1,11 @@ +{ + "cps_version": "0.13", + "name": "TransitiveMissingCMake", + "cps_path": "@prefix@/cps", + "requires": { + "CMakeTestPackage": { + "components": [ "DoesNotExist" ] + } + }, + "components": {} +} diff --git a/Tests/RunCMake/find_package-CPS/cps/transitivemissingcps.cps b/Tests/RunCMake/find_package-CPS/cps/transitivemissingcps.cps new file mode 100644 index 0000000..199aeeb --- /dev/null +++ b/Tests/RunCMake/find_package-CPS/cps/transitivemissingcps.cps @@ -0,0 +1,11 @@ +{ + "cps_version": "0.13", + "name": "TransitiveMissingCPS", + "cps_path": "@prefix@/cps", + "requires": { + "ComponentTest": { + "components": [ "DoesNotExist" ] + } + }, + "components": {} +} -- cgit v0.12