mirror of
https://github.com/Kitware/CMake.git
synced 2026-01-04 12:49:36 -06:00
find_package: Find CPS dependencies
Implement finding dependencies of CPS packages. This is done by setting up additional `cmFindPackageCommand` instances which are used to look for a parent package's dependencies.
This commit is contained in:
committed by
Brad King
parent
e6d5a518b1
commit
42de87cbae
@@ -23,6 +23,7 @@
|
||||
|
||||
#include "cmAlgorithms.h"
|
||||
#include "cmDependencyProvider.h"
|
||||
#include "cmExecutionStatus.h"
|
||||
#include "cmExperimental.h"
|
||||
#include "cmList.h"
|
||||
#include "cmListFileCache.h"
|
||||
@@ -55,8 +56,6 @@
|
||||
# endif
|
||||
#endif
|
||||
|
||||
class cmExecutionStatus;
|
||||
|
||||
namespace {
|
||||
|
||||
using pdt = cmFindPackageCommand::PackageDescriptionType;
|
||||
@@ -560,6 +559,41 @@ void cmFindPackageCommand::AppendSearchPathGroups()
|
||||
this->LabeledPaths.emplace(PathLabel::SystemRegistry, cmSearchPath{ this });
|
||||
}
|
||||
|
||||
void cmFindPackageCommand::InheritOptions(cmFindPackageCommand* other)
|
||||
{
|
||||
this->RequiredCMakeVersion = other->RequiredCMakeVersion;
|
||||
this->LibraryArchitecture = other->LibraryArchitecture;
|
||||
this->UseLib32Paths = other->UseLib32Paths;
|
||||
this->UseLib64Paths = other->UseLib64Paths;
|
||||
this->UseLibx32Paths = other->UseLibx32Paths;
|
||||
this->NoUserRegistry = other->NoUserRegistry;
|
||||
this->NoSystemRegistry = other->NoSystemRegistry;
|
||||
this->UseRealPath = other->UseRealPath;
|
||||
this->SortOrder = other->SortOrder;
|
||||
this->SortDirection = other->SortDirection;
|
||||
|
||||
this->GlobalScope = other->GlobalScope;
|
||||
this->RegistryView = other->RegistryView;
|
||||
this->NoDefaultPath = other->NoDefaultPath;
|
||||
this->NoPackageRootPath = other->NoPackageRootPath;
|
||||
this->NoCMakePath = other->NoCMakePath;
|
||||
this->NoCMakeEnvironmentPath = other->NoCMakeEnvironmentPath;
|
||||
this->NoSystemEnvironmentPath = other->NoSystemEnvironmentPath;
|
||||
this->NoCMakeSystemPath = other->NoCMakeSystemPath;
|
||||
this->NoCMakeInstallPath = other->NoCMakeInstallPath;
|
||||
this->FindRootPathMode = other->FindRootPathMode;
|
||||
|
||||
this->SearchFrameworkLast = other->SearchFrameworkLast;
|
||||
this->SearchFrameworkFirst = other->SearchFrameworkFirst;
|
||||
this->SearchFrameworkOnly = other->SearchFrameworkOnly;
|
||||
this->SearchAppBundleLast = other->SearchAppBundleLast;
|
||||
this->SearchAppBundleFirst = other->SearchAppBundleFirst;
|
||||
this->SearchAppBundleOnly = other->SearchAppBundleOnly;
|
||||
this->SearchPathSuffixes = other->SearchPathSuffixes;
|
||||
|
||||
this->Quiet = other->Quiet;
|
||||
}
|
||||
|
||||
bool cmFindPackageCommand::InitialPass(std::vector<std::string> const& args)
|
||||
{
|
||||
if (args.empty()) {
|
||||
@@ -645,7 +679,7 @@ bool cmFindPackageCommand::InitialPass(std::vector<std::string> const& args)
|
||||
this->SortDirection = (*sd == "ASC") ? Asc : Dec;
|
||||
}
|
||||
|
||||
// Find what search path locations have been enabled/disable
|
||||
// Find what search path locations have been enabled/disable.
|
||||
this->SelectDefaultSearchModes();
|
||||
|
||||
// Find the current root path mode.
|
||||
@@ -1510,12 +1544,7 @@ bool cmFindPackageCommand::HandlePackageMode(
|
||||
if (this->CpsReader) {
|
||||
// The package has been found.
|
||||
found = true;
|
||||
|
||||
// Import targets.
|
||||
cmPackageInfoReader* const reader = this->CpsReader.get();
|
||||
result = reader->ImportTargets(this->Makefile, this->Status) &&
|
||||
this->ImportTargetConfigurations(this->FileFound, reader) &&
|
||||
this->ImportAppendices(this->FileFound);
|
||||
result = this->ReadPackage();
|
||||
} else if (this->ReadListFile(this->FileFound, DoPolicyScope)) {
|
||||
// The package has been found.
|
||||
found = true;
|
||||
@@ -1816,6 +1845,36 @@ bool cmFindPackageCommand::FindEnvironmentConfig()
|
||||
});
|
||||
}
|
||||
|
||||
cmFindPackageCommand::AppendixMap cmFindPackageCommand::FindAppendices(
|
||||
std::string const& base) const
|
||||
{
|
||||
AppendixMap appendices;
|
||||
|
||||
// Find package appendices.
|
||||
cmsys::Glob glob;
|
||||
glob.RecurseOff();
|
||||
if (glob.FindFiles(cmStrCat(cmSystemTools::GetFilenamePath(base), "/"_s,
|
||||
cmSystemTools::GetFilenameWithoutExtension(base),
|
||||
"[-:]*.[Cc][Pp][Ss]"_s))) {
|
||||
// Check glob results for valid appendices.
|
||||
for (std::string const& extra : glob.GetFiles()) {
|
||||
// Exclude configuration-specific files for now; we look at them later
|
||||
// when we load their respective configuration-agnostic appendices.
|
||||
if (extra.find('@') != std::string::npos) {
|
||||
continue;
|
||||
}
|
||||
|
||||
std::unique_ptr<cmPackageInfoReader> reader =
|
||||
cmPackageInfoReader::Read(extra, this->CpsReader.get());
|
||||
if (reader && reader->GetName() == this->Name) {
|
||||
appendices.emplace(extra, std::move(reader));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return appendices;
|
||||
}
|
||||
|
||||
bool cmFindPackageCommand::ReadListFile(const std::string& f,
|
||||
const PolicyScopeRule psr)
|
||||
{
|
||||
@@ -1833,53 +1892,114 @@ bool cmFindPackageCommand::ReadListFile(const std::string& f,
|
||||
return false;
|
||||
}
|
||||
|
||||
bool cmFindPackageCommand::ImportTargetConfigurations(
|
||||
std::string const& base, cmPackageInfoReader* parent)
|
||||
bool cmFindPackageCommand::ReadPackage()
|
||||
{
|
||||
// Find supplemental configuration files.
|
||||
cmsys::Glob glob;
|
||||
glob.RecurseOff();
|
||||
if (glob.FindFiles(cmStrCat(cmSystemTools::GetFilenamePath(base), "/"_s,
|
||||
cmSystemTools::GetFilenameWithoutExtension(base),
|
||||
"@*.[Cc][Pp][Ss]"_s))) {
|
||||
// Resolve any transitive dependencies.
|
||||
if (!FindPackageDependencies(this->FileFound, *this->CpsReader,
|
||||
this->Required)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Try to read supplemental data from each file found.
|
||||
for (std::string const& extra : glob.GetFiles()) {
|
||||
std::unique_ptr<cmPackageInfoReader> const& reader =
|
||||
cmPackageInfoReader::Read(extra, parent);
|
||||
if (reader && reader->GetName() == this->Name) {
|
||||
if (!reader->ImportTargetConfigurations(this->Makefile,
|
||||
this->Status)) {
|
||||
return false;
|
||||
}
|
||||
cmMakefile::CallRAII scope{ this->Makefile, this->FileFound, this->Status };
|
||||
|
||||
// Locate appendices.
|
||||
cmFindPackageCommand::AppendixMap appendices =
|
||||
this->FindAppendices(this->FileFound);
|
||||
|
||||
auto iter = appendices.begin();
|
||||
while (iter != appendices.end()) {
|
||||
bool providesRequiredComponents = false; // TODO
|
||||
bool required = providesRequiredComponents && this->Required;
|
||||
if (!this->FindPackageDependencies(iter->first, *iter->second, required)) {
|
||||
if (providesRequiredComponents) {
|
||||
return false;
|
||||
}
|
||||
iter = appendices.erase(iter);
|
||||
} else {
|
||||
++iter;
|
||||
}
|
||||
}
|
||||
|
||||
// Import targets from root file.
|
||||
if (!this->ImportPackageTargets(this->FileFound, *this->CpsReader)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Import targets from appendices.
|
||||
for (auto const& appendix : appendices) {
|
||||
cmMakefile::CallRAII appendixScope{ this->Makefile, appendix.first,
|
||||
this->Status };
|
||||
if (!this->ImportPackageTargets(appendix.first, *appendix.second)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool cmFindPackageCommand::ImportAppendices(std::string const& base)
|
||||
bool cmFindPackageCommand::FindPackageDependencies(
|
||||
std::string const& fileName, cmPackageInfoReader const& reader,
|
||||
bool required)
|
||||
{
|
||||
// Find package appendices.
|
||||
// Get package requirements.
|
||||
for (cmPackageRequirement const& dep : reader.GetRequirements()) {
|
||||
cmExecutionStatus status{ *this->Makefile };
|
||||
cmMakefile::CallRAII scope{ this->Makefile, fileName, status };
|
||||
|
||||
// For each requirement, set up a nested instance to find it.
|
||||
cmFindPackageCommand fp{ status };
|
||||
fp.InheritOptions(this);
|
||||
|
||||
fp.Name = dep.Name;
|
||||
fp.Required = required;
|
||||
fp.UseFindModules = false;
|
||||
fp.UseCpsFiles = true;
|
||||
|
||||
fp.Version = dep.Version;
|
||||
fp.VersionComplete = dep.Version;
|
||||
fp.VersionCount =
|
||||
parseVersion(fp.Version, fp.VersionMajor, fp.VersionMinor,
|
||||
fp.VersionPatch, fp.VersionTweak);
|
||||
|
||||
fp.Components = cmJoin(cmMakeRange(dep.Components), ";"_s);
|
||||
fp.RequiredComponents =
|
||||
std::set<std::string>{ dep.Components.begin(), dep.Components.end() };
|
||||
|
||||
// TODO set hints
|
||||
|
||||
// Try to find the requirement; fail if we can't.
|
||||
if (!fp.FindPackage() || fp.FileFound.empty()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// All requirements (if any) were found.
|
||||
return true;
|
||||
}
|
||||
|
||||
bool cmFindPackageCommand::ImportPackageTargets(std::string const& fileName,
|
||||
cmPackageInfoReader& reader)
|
||||
{
|
||||
// Import base file.
|
||||
if (!reader.ImportTargets(this->Makefile, this->Status)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Find supplemental configuration files.
|
||||
cmsys::Glob glob;
|
||||
glob.RecurseOff();
|
||||
if (glob.FindFiles(cmStrCat(cmSystemTools::GetFilenamePath(base), "/"_s,
|
||||
cmSystemTools::GetFilenameWithoutExtension(base),
|
||||
"[-:]*.[Cc][Pp][Ss]"_s))) {
|
||||
if (glob.FindFiles(
|
||||
cmStrCat(cmSystemTools::GetFilenamePath(fileName), "/"_s,
|
||||
cmSystemTools::GetFilenameWithoutExtension(fileName),
|
||||
"@*.[Cc][Pp][Ss]"_s))) {
|
||||
|
||||
// Try to read supplemental data from each appendix file found.
|
||||
// Try to read supplemental data from each file found.
|
||||
for (std::string const& extra : glob.GetFiles()) {
|
||||
// This loop should not consider configuration-specific files.
|
||||
if (extra.find('@') != std::string::npos) {
|
||||
continue;
|
||||
}
|
||||
|
||||
std::unique_ptr<cmPackageInfoReader> const& reader =
|
||||
cmPackageInfoReader::Read(extra, this->CpsReader.get());
|
||||
if (reader && reader->GetName() == this->Name) {
|
||||
if (!reader->ImportTargets(this->Makefile, this->Status) ||
|
||||
!this->ImportTargetConfigurations(extra, reader.get())) {
|
||||
std::unique_ptr<cmPackageInfoReader> configReader =
|
||||
cmPackageInfoReader::Read(extra, &reader);
|
||||
if (configReader && configReader->GetName() == this->Name) {
|
||||
if (!configReader->ImportTargetConfigurations(this->Makefile,
|
||||
this->Status)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -92,10 +92,12 @@ private:
|
||||
static PathLabel SystemRegistry;
|
||||
};
|
||||
|
||||
void InheritOptions(cmFindPackageCommand* other);
|
||||
|
||||
// Try to find a package, assuming most state has already been set up. This
|
||||
// is used for recursive dependency solving, particularly when importing
|
||||
// packages via CPS. Bypasses providers if argsForProvider is empty.
|
||||
bool FindPackage(std::vector<std::string> const& argsForProvider);
|
||||
bool FindPackage(std::vector<std::string> const& argsForProvider = {});
|
||||
|
||||
bool FindPackageUsingModuleMode();
|
||||
bool FindPackageUsingConfigMode();
|
||||
@@ -137,9 +139,17 @@ private:
|
||||
DoPolicyScope
|
||||
};
|
||||
bool ReadListFile(const std::string& f, PolicyScopeRule psr);
|
||||
bool ImportTargetConfigurations(std::string const& base,
|
||||
cmPackageInfoReader* parent);
|
||||
bool ImportAppendices(std::string const& base);
|
||||
bool ReadPackage();
|
||||
|
||||
using AppendixMap =
|
||||
std::map<std::string, std::unique_ptr<cmPackageInfoReader>>;
|
||||
AppendixMap FindAppendices(std::string const& base) const;
|
||||
bool FindPackageDependencies(std::string const& fileName,
|
||||
cmPackageInfoReader const& reader,
|
||||
bool required);
|
||||
|
||||
bool ImportPackageTargets(std::string const& fileName,
|
||||
cmPackageInfoReader& reader);
|
||||
void StoreVersionFound();
|
||||
void SetConfigDirCacheVariable(const std::string& value);
|
||||
|
||||
|
||||
@@ -464,6 +464,23 @@ std::vector<unsigned> cmPackageInfoReader::ParseVersion() const
|
||||
return result;
|
||||
}
|
||||
|
||||
std::vector<cmPackageRequirement> cmPackageInfoReader::GetRequirements() const
|
||||
{
|
||||
std::vector<cmPackageRequirement> requirements;
|
||||
|
||||
auto const& requirementObjects = this->Data["requires"];
|
||||
|
||||
for (auto ri = requirementObjects.begin(), re = requirementObjects.end();
|
||||
ri != re; ++ri) {
|
||||
cmPackageRequirement r{ ri.name(), (*ri)["version"].asString(),
|
||||
ReadList(*ri, "components"),
|
||||
ReadList(*ri, "hints") };
|
||||
requirements.emplace_back(std::move(r));
|
||||
}
|
||||
|
||||
return requirements;
|
||||
}
|
||||
|
||||
std::string cmPackageInfoReader::ResolvePath(std::string path) const
|
||||
{
|
||||
cmSystemTools::ConvertToUnixSlashes(path);
|
||||
|
||||
@@ -20,6 +20,14 @@ class cmExecutionStatus;
|
||||
class cmMakefile;
|
||||
class cmTarget;
|
||||
|
||||
struct cmPackageRequirement
|
||||
{
|
||||
std::string Name;
|
||||
std::string Version;
|
||||
std::vector<std::string> Components;
|
||||
std::vector<std::string> Hints;
|
||||
};
|
||||
|
||||
/** \class cmPackageInfoReader
|
||||
* \brief Read and parse CPS files.
|
||||
*
|
||||
@@ -41,6 +49,8 @@ public:
|
||||
/// version is specified.
|
||||
std::vector<unsigned> ParseVersion() const;
|
||||
|
||||
std::vector<cmPackageRequirement> GetRequirements() const;
|
||||
|
||||
/// Create targets for components specified in the CPS file.
|
||||
bool ImportTargets(cmMakefile* makefile, cmExecutionStatus& status);
|
||||
|
||||
|
||||
@@ -161,3 +161,27 @@ endif()
|
||||
if(C_CXXONLY_H)
|
||||
message(SEND_ERROR "cmincludetest/cxxonly.h unexpectedly found in C mode ?!")
|
||||
endif()
|
||||
|
||||
###############################################################################
|
||||
# Find a package that has dependencies.
|
||||
|
||||
find_package(Bar)
|
||||
if(NOT Bar_FOUND)
|
||||
message(SEND_ERROR "Bar not found !")
|
||||
elseif(NOT Dep1_FOUND)
|
||||
message(SEND_ERROR "Bar's Dep1 not found !")
|
||||
elseif(NOT Dep2_FOUND)
|
||||
message(SEND_ERROR "Bar's Dep2 not found !")
|
||||
elseif(NOT Dep3_FOUND)
|
||||
message(SEND_ERROR "Bar's Dep3 not found !")
|
||||
elseif(NOT TARGET Dep1::Target)
|
||||
message(SEND_ERROR "Dep1::Target missing !")
|
||||
elseif(NOT TARGET Dep2::Target)
|
||||
message(SEND_ERROR "Dep2::Target missing !")
|
||||
elseif(NOT TARGET Dep3::Target)
|
||||
message(SEND_ERROR "Dep3::Target missing !")
|
||||
elseif(NOT TARGET Bar::Target1)
|
||||
message(SEND_ERROR "Bar::Target1 missing !")
|
||||
elseif(NOT TARGET Bar::Target2)
|
||||
message(SEND_ERROR "Bar::Target2 missing !")
|
||||
endif()
|
||||
|
||||
3
Tests/FindPackageCpsTest/cmake/dep2-config.cmake
Normal file
3
Tests/FindPackageCpsTest/cmake/dep2-config.cmake
Normal file
@@ -0,0 +1,3 @@
|
||||
set(Dep2_FOUND TRUE)
|
||||
|
||||
add_library(Dep2::Target INTERFACE IMPORTED)
|
||||
13
Tests/FindPackageCpsTest/cps/bar-extra.cps
Normal file
13
Tests/FindPackageCpsTest/cps/bar-extra.cps
Normal file
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"cps_version": "0.13",
|
||||
"name": "Bar",
|
||||
"requires": {
|
||||
"Dep3": null
|
||||
},
|
||||
"components": {
|
||||
"Target2": {
|
||||
"type": "interface",
|
||||
"requires": [ "Dep3:Target" ]
|
||||
}
|
||||
}
|
||||
}
|
||||
15
Tests/FindPackageCpsTest/cps/bar.cps
Normal file
15
Tests/FindPackageCpsTest/cps/bar.cps
Normal file
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"cps_version": "0.13",
|
||||
"name": "Bar",
|
||||
"cps_path": "@prefix@/cps",
|
||||
"requires": {
|
||||
"Dep1": null,
|
||||
"Dep2": null
|
||||
},
|
||||
"components": {
|
||||
"Target1": {
|
||||
"type": "interface",
|
||||
"requires": [ "Dep1:Target", "Dep2:Target" ]
|
||||
}
|
||||
}
|
||||
}
|
||||
10
Tests/FindPackageCpsTest/cps/dep1.cps
Normal file
10
Tests/FindPackageCpsTest/cps/dep1.cps
Normal file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"cps_version": "0.13",
|
||||
"name": "Dep1",
|
||||
"cps_path": "@prefix@/cps",
|
||||
"components": {
|
||||
"Target": {
|
||||
"type": "interface"
|
||||
}
|
||||
}
|
||||
}
|
||||
10
Tests/FindPackageCpsTest/cps/dep3.cps
Normal file
10
Tests/FindPackageCpsTest/cps/dep3.cps
Normal file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"cps_version": "0.13",
|
||||
"name": "Dep3",
|
||||
"cps_path": "@prefix@/cps",
|
||||
"components": {
|
||||
"Target": {
|
||||
"type": "interface"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
1
|
||||
@@ -0,0 +1,25 @@
|
||||
CMake Warning \(dev\) at MissingTransitiveDependency\.cmake:[0-9]+ \(find_package\):
|
||||
CMake's support for importing package information in the Common Package
|
||||
Specification format \(via find_package\) is experimental\. It is meant only
|
||||
for experimentation and feedback to CMake developers.
|
||||
Call Stack \(most recent call first\):
|
||||
CMakeLists\.txt:[0-9]+ \(include\)
|
||||
This warning is for project developers. Use -Wno-dev to suppress it.
|
||||
+
|
||||
CMake Error in cps/[Ss]till[Ii]ncomplete\.cps:
|
||||
Could not find a package configuration file provided by "DoesNotExist" with
|
||||
any of the following names:
|
||||
|
||||
DoesNotExist\.cps
|
||||
doesnotexist\.cps
|
||||
DoesNotExistConfig\.cmake
|
||||
doesnotexist-config\.cmake
|
||||
|
||||
Add the installation prefix of "DoesNotExist" to CMAKE_PREFIX_PATH or set
|
||||
"DoesNotExist_DIR" to a directory containing one of the above files\. If
|
||||
"DoesNotExist" provides a separate development package or SDK, be sure it
|
||||
has been installed\.
|
||||
Call Stack \(most recent call first\):
|
||||
cps/[Ii]ncomplete\.cps
|
||||
MissingTransitiveDependency\.cmake:[0-9]+ \(find_package\)
|
||||
CMakeLists\.txt:[0-9]+ \(include\)
|
||||
@@ -0,0 +1,19 @@
|
||||
cmake_minimum_required(VERSION 3.31)
|
||||
|
||||
set(CMAKE_EXPERIMENTAL_FIND_CPS_PACKAGES "e82e467b-f997-4464-8ace-b00808fff261")
|
||||
|
||||
# Protect tests from running inside the default install prefix.
|
||||
set(CMAKE_INSTALL_PREFIX "${CMAKE_CURRENT_BINARY_DIR}/NotDefaultPrefix")
|
||||
|
||||
# Disable built-in search paths.
|
||||
set(CMAKE_FIND_USE_PACKAGE_ROOT_PATH OFF)
|
||||
set(CMAKE_FIND_USE_CMAKE_ENVIRONMENT_PATH OFF)
|
||||
set(CMAKE_FIND_USE_SYSTEM_ENVIRONMENT_PATH OFF)
|
||||
set(CMAKE_FIND_USE_CMAKE_SYSTEM_PATH OFF)
|
||||
set(CMAKE_FIND_USE_INSTALL_PREFIX OFF)
|
||||
|
||||
set(CMAKE_PREFIX_PATH ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
|
||||
###############################################################################
|
||||
# Test finding a package that is missing dependencies.
|
||||
find_package(Incomplete REQUIRED)
|
||||
@@ -22,6 +22,7 @@ run_cmake_with_options(MissingConfigDebugPkg --debug-find-pkg=NotHere)
|
||||
run_cmake(MissingConfigOneName)
|
||||
run_cmake(MissingConfigRequired)
|
||||
run_cmake(MissingConfigVersion)
|
||||
run_cmake(MissingTransitiveDependency)
|
||||
run_cmake(MixedModeOptions)
|
||||
run_cmake_with_options(ModuleModeDebugPkg --debug-find-pkg=Foo,Zot)
|
||||
run_cmake(PackageRoot)
|
||||
|
||||
9
Tests/RunCMake/find_package/cps/incomplete.cps
Normal file
9
Tests/RunCMake/find_package/cps/incomplete.cps
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"cps_version": "0.13",
|
||||
"name": "Incomplete",
|
||||
"cps_path": "@prefix@/cps",
|
||||
"requires": {
|
||||
"StillIncomplete": null
|
||||
},
|
||||
"components": {}
|
||||
}
|
||||
9
Tests/RunCMake/find_package/cps/stillincomplete.cps
Normal file
9
Tests/RunCMake/find_package/cps/stillincomplete.cps
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"cps_version": "0.13",
|
||||
"name": "StillIncomplete",
|
||||
"cps_path": "@prefix@/cps",
|
||||
"requires": {
|
||||
"DoesNotExist": null
|
||||
},
|
||||
"components": {}
|
||||
}
|
||||
Reference in New Issue
Block a user