Merge topic 'cps-warn'

dc0e837c54 CPS: Warn when CMake loads a CPS file from a non-conforming location

Acked-by: Kitware Robot <kwrobot@kitware.com>
Merge-request: !12605
This commit is contained in:
Brad King authored and Kitware Robot committed 2026-11-03 09:32:57 -05:00
commit 76d4b1fa80
15 files changed
+386 -17

No files matched your search

+8
View File
@@ -103,6 +103,12 @@ The command has a few modes by which it searches for packages:
version files is considerably more complicated than for Module mode
(see :ref:`search procedure`).
.. versionadded:: 4.6
CMake may search for CPS files in locations that do not conform with the
`Package Searching`_ procedures for CPS. The
:diagnostic:`CMD_CPS_NON_CONFORMING_LOCATION` diagnostic can be enabled to
detect when a CPS file is read from a non-conforming location.
The config and version files are typically installed as part of the
package, so they tend to be more reliable than Find modules. They usually
contain direct knowledge of the package contents, so no searching or
@@ -1035,3 +1041,5 @@ found.
.. _cps-version_schema: https://cps-org.github.io/cps/schema.html#version-schema
.. |cps-version_schema| replace:: ``version_schema``
.. _`Package Searching`: https://cps-org.github.io/cps/searching.html
@@ -0,0 +1,18 @@
CMD_CPS_NON_CONFORMING_LOCATION
-------------------------------
.. versionadded:: 4.6
.. diagnostic::
:default: ignore
:parent: CMD_AUTHOR
Warn if :command:`find_package` finds a CPS package from a location that does
not conform to the CPS specification for `Package Searching`_. This may
happen because CMake searches for packages in more locations than are
officially specified by CPS. If the severity level is ``SEND_ERROR`` or
higher, CMake will not cache the package location and subsequent configure
attempts will also fail. Otherwise, the diagnostic is only emitted when CMake
initially discovers the package.
.. _`Package Searching`: https://cps-org.github.io/cps/searching.html
+1
View File
@@ -87,6 +87,7 @@ The following categories are defined:
:maxdepth: 1
/diagnostic/CMD_AUTHOR
/diagnostic/CMD_CPS_NON_CONFORMING_LOCATION
/diagnostic/CMD_DEPRECATED
/diagnostic/CMD_EXPERIMENTAL
/diagnostic/CMD_INSTALL_ABSOLUTE_DESTINATION
+9
View File
@@ -529,6 +529,15 @@ they were added and a summary of the new features and changes is given below.
* The ``${configurePresetName}`` macro was added.
``14``
.. versionadded:: 4.6
* Changes to `Configure Presets <Configure Preset_>`_:
* The ``cpsNonConformingLocation`` field was added to
:preset:`configurePresets.warnings` and
:preset:`configurePresets.errors`.
Schema
======
@@ -10,6 +10,15 @@
:cmake-option:`-Wno-error=author` on the command line.
This may not be set to ``true`` if ``warnings.author`` is set to ``false``.
.. _`CMakePresets.configurePresets.errors.cpsNonConformingLocation`:
``cpsNonConformingLocation``
.. presets-versionadded:: 14
An optional boolean. Equivalent to passing :cmake-option:`-Werror=cps-non-conforming-location` or
:cmake-option:`-Wno-error=cps-non-conforming-location` on the command line.
This may not be set to ``true`` if ``warnings.cpsNonConformingLocation`` is set to ``false``.
.. _`CMakePresets.configurePresets.errors.deprecated`:
``deprecated``
+223 -8
View File
@@ -480,6 +480,50 @@
"version"
],
"additionalProperties": false
},
{
"description": "The presets specify the generator and the build directory, and optionally an array of variables and other arguments to pass to CMake.",
"properties": {
"$schema": {
"$ref": "#/definitions/$schema@v8.."
},
"version": {
"type": "integer",
"description": "A required integer representing the version of the JSON schema.",
"const": 14
},
"cmakeMinimumRequired": {
"$ref": "#/definitions/cmakeMinimumRequired@v10.."
},
"include": {
"$ref": "#/definitions/include@v4.."
},
"vendor": {
"$ref": "#/definitions/vendor@v1.."
},
"configurePresets": {
"$ref": "#/definitions/configurePresets@v14"
},
"buildPresets": {
"$ref": "#/definitions/buildPresets@v10.."
},
"testPresets": {
"$ref": "#/definitions/testPresets@v12.."
},
"packagePresets": {
"$ref": "#/definitions/packagePresets@v10.."
},
"workflowPresets": {
"$ref": "#/definitions/workflowPresets@v10.."
},
"$comment": {
"$ref": "#/definitions/$comment@v10.."
}
},
"required": [
"version"
],
"additionalProperties": false
}
],
"required": [
@@ -1017,6 +1061,86 @@
]
}
},
"configurePresets@v14": {
"type": "array",
"description": "An optional array of configure preset objects.",
"items": {
"type": "object",
"description": "A configure preset object.",
"unevaluatedProperties": false,
"properties": {
"name": {
"$ref": "#/definitions/configurePresets.name@v1.."
},
"hidden": {
"$ref": "#/definitions/configurePresets.hidden@v1.."
},
"inherits": {
"$ref": "#/definitions/configurePresets.inherits@v1.."
},
"condition": {
"$ref": "#/definitions/configurePresets.condition@v10.."
},
"vendor": {
"$ref": "#/definitions/configurePresets.vendor@v1.."
},
"displayName": {
"$ref": "#/definitions/configurePresets.displayName@v1.."
},
"description": {
"$ref": "#/definitions/configurePresets.description@v1.."
},
"generator": {
"$ref": "#/definitions/configurePresets.generator@v3.."
},
"architecture": {
"$ref": "#/definitions/configurePresets.architecture@v10.."
},
"toolset": {
"$ref": "#/definitions/configurePresets.toolset@v10.."
},
"toolchainFile": {
"$ref": "#/definitions/configurePresets.toolchainFile@v3.."
},
"graphviz": {
"$ref": "#/definitions/configurePresets.graphviz@v10.."
},
"binaryDir": {
"$ref": "#/definitions/configurePresets.binaryDir@v3.."
},
"installDir": {
"$ref": "#/definitions/configurePresets.installDir@v3.."
},
"cmakeExecutable": {
"$ref": "#/definitions/configurePresets.cmakeExecutable@v1.."
},
"cacheVariables": {
"$ref": "#/definitions/configurePresets.cacheVariables@v10.."
},
"environment": {
"$ref": "#/definitions/configurePresets.environment@v1.."
},
"warnings": {
"$ref": "#/definitions/configurePresets.warnings@v14"
},
"errors": {
"$ref": "#/definitions/configurePresets.errors@v14"
},
"debug": {
"$ref": "#/definitions/configurePresets.debug@v10.."
},
"trace": {
"$ref": "#/definitions/configurePresets.trace@v10.."
},
"$comment": {
"$ref": "#/definitions/$comment@v10.."
}
},
"required": [
"name"
]
}
},
"configurePresets.name@v1..": {
"type": "string",
"minLength": 1,
@@ -1451,13 +1575,56 @@
"$ref": "#/definitions/configurePresets.warnings.installAbsoluteDestination@v12.."
},
"nonTargetDirective": {
"$ref": "#/definitions/configurePresets.warnings.nonTargetDirective@v13"
"$ref": "#/definitions/configurePresets.warnings.nonTargetDirective@v13.."
},
"policy": {
"$ref": "#/definitions/configurePresets.warnings.policy@v12.."
},
"strict": {
"$ref": "#/definitions/configurePresets.warnings.strict@v13"
"$ref": "#/definitions/configurePresets.warnings.strict@v13.."
},
"uninitialized": {
"$ref": "#/definitions/configurePresets.warnings.uninitialized@v1.."
},
"unusedCli": {
"$ref": "#/definitions/configurePresets.warnings.unusedCli@v1.."
},
"systemVars": {
"$ref": "#/definitions/configurePresets.warnings.systemVars@v1.."
},
"$comment": {
"$ref": "#/definitions/$comment@v10.."
}
}
},
"configurePresets.warnings@v14": {
"type": "object",
"description": "An optional object specifying the warnings to enable.",
"unevaluatedProperties": false,
"properties": {
"author": {
"$ref": "#/definitions/configurePresets.warnings.author@v12.."
},
"cpsNonConformingLocation": {
"$ref": "#/definitions/configurePresets.warnings.cpsNonConformingLocation@v14"
},
"deprecated": {
"$ref": "#/definitions/configurePresets.warnings.deprecated@v1.."
},
"experimental": {
"$ref": "#/definitions/configurePresets.warnings.experimental@v12.."
},
"installAbsoluteDestination": {
"$ref": "#/definitions/configurePresets.warnings.installAbsoluteDestination@v12.."
},
"nonTargetDirective": {
"$ref": "#/definitions/configurePresets.warnings.nonTargetDirective@v13.."
},
"policy": {
"$ref": "#/definitions/configurePresets.warnings.policy@v12.."
},
"strict": {
"$ref": "#/definitions/configurePresets.warnings.strict@v13.."
},
"uninitialized": {
"$ref": "#/definitions/configurePresets.warnings.uninitialized@v1.."
@@ -1477,6 +1644,10 @@
"type": "boolean",
"description": "An optional boolean. Equivalent to passing -Wauthor or -Wno-author on the command line. This may not be set to false if errors.author is set to true."
},
"configurePresets.warnings.cpsNonConformingLocation@v14": {
"type": "boolean",
"description": "An optional boolean. Equivalent to passing -Wcps-non-conforming-location or -Wno-cps-non-conforming-location on the command line. This may not be set to false if errors.cpsNonConformingLocation is set to true."
},
"configurePresets.warnings.deprecated@v1..": {
"type": "boolean",
"description": "An optional boolean. Equivalent to passing -Wdeprecated or -Wno-deprecated on the command line. This may not be set to false if errors.deprecated is set to true."
@@ -1493,7 +1664,7 @@
"type": "boolean",
"description": "An optional boolean. Equivalent to passing -Winstall-absolute-destination or -Wno-install-absolute-destination on the command line. This may not be set to false if errors.installAbsoluteDestination is set to true."
},
"configurePresets.warnings.nonTargetDirective@v13": {
"configurePresets.warnings.nonTargetDirective@v13..": {
"type": "boolean",
"description": "An optional boolean. Equivalent to passing -Wnon-target-directive or -Wno-non-target-directive on the command line. This may not be set to false if errors.nonTargetDirective is set to true."
},
@@ -1501,7 +1672,7 @@
"type": "boolean",
"description": "An optional boolean. Equivalent to passing -Wpolicy or -Wno-policy on the command line. This may not be set to false if errors.policy is set to true."
},
"configurePresets.warnings.strict@v13": {
"configurePresets.warnings.strict@v13..": {
"type": "boolean",
"description": "An optional boolean. Equivalent to passing -Wstrict or -Wno-strict on the command line. This may not be set to false if errors.strict is set to true."
},
@@ -1595,13 +1766,53 @@
"$ref": "#/definitions/configurePresets.errors.installAbsoluteDestination@v12.."
},
"nonTargetDirective": {
"$ref": "#/definitions/configurePresets.errors.nonTargetDirective@v13"
"$ref": "#/definitions/configurePresets.errors.nonTargetDirective@v13.."
},
"policy": {
"$ref": "#/definitions/configurePresets.errors.policy@v12.."
},
"strict": {
"$ref": "#/definitions/configurePresets.errors.strict@v13"
"$ref": "#/definitions/configurePresets.errors.strict@v13.."
},
"uninitialized": {
"$ref": "#/definitions/configurePresets.errors.uninitialized@v12.."
},
"unusedCli": {
"$ref": "#/definitions/configurePresets.errors.unusedCli@v12.."
},
"$comment": {
"$ref": "#/definitions/$comment@v10.."
}
}
},
"configurePresets.errors@v14": {
"type": "object",
"description": "An optional object specifying the errors to enable.",
"unevaluatedProperties": false,
"properties": {
"author": {
"$ref": "#/definitions/configurePresets.errors.author@v12.."
},
"cpsNonConformingLocation": {
"$ref": "#/definitions/configurePresets.errors.cpsNonConformingLocation@v14"
},
"deprecated": {
"$ref": "#/definitions/configurePresets.errors.deprecated@v1.."
},
"experimental": {
"$ref": "#/definitions/configurePresets.errors.experimental@v12.."
},
"installAbsoluteDestination": {
"$ref": "#/definitions/configurePresets.errors.installAbsoluteDestination@v12.."
},
"nonTargetDirective": {
"$ref": "#/definitions/configurePresets.errors.nonTargetDirective@v13.."
},
"policy": {
"$ref": "#/definitions/configurePresets.errors.policy@v12.."
},
"strict": {
"$ref": "#/definitions/configurePresets.errors.strict@v13.."
},
"uninitialized": {
"$ref": "#/definitions/configurePresets.errors.uninitialized@v12.."
@@ -1618,6 +1829,10 @@
"type": "boolean",
"description": "An optional boolean. Equivalent to passing -Werror=author or -Wno-error=author on the command line. This may not be set to true if warnings.author is set to false."
},
"configurePresets.errors.cpsNonConformingLocation@v14": {
"type": "boolean",
"description": "An optional boolean. Equivalent to passing -Werror=cps-non-conforming-location or -Wno-error=cps-non-conforming-location on the command line. This may not be set to true if warnings.cpsNonConformingLocation is set to false."
},
"configurePresets.errors.deprecated@v1..": {
"type": "boolean",
"description": "An optional boolean. Equivalent to passing -Werror=deprecated or -Wno-error=deprecated on the command line. This may not be set to true if warnings.deprecated is set to false."
@@ -1634,7 +1849,7 @@
"type": "boolean",
"description": "An optional boolean. Equivalent to passing -Werror=install-absolute-destination or -Wno-error=install-absolute-destination on the command line. This may not be set to true if warnings.installAbsoluteDestination is set to false."
},
"configurePresets.errors.nonTargetDirective@v13": {
"configurePresets.errors.nonTargetDirective@v13..": {
"type": "boolean",
"description": "An optional boolean. Equivalent to passing -Werror=non-target-directive or -Wno-error=non-target-directive on the command line. This may not be set to true if warnings.nonTargetDirective is set to false."
},
@@ -1642,7 +1857,7 @@
"type": "boolean",
"description": "An optional boolean. Equivalent to passing -Werror=policy or -Wno-error=policy on the command line. This may not be set to true if warnings.policy is set to false."
},
"configurePresets.errors.strict@v13": {
"configurePresets.errors.strict@v13..": {
"type": "boolean",
"description": "An optional boolean. Equivalent to passing -Werror=strict or -Wno-error=strict on the command line. This may not be set to true if warnings.strict is set to false."
},
+1 -1
View File
@@ -1,5 +1,5 @@
---
version: 13
version: 14
types:
- id: condition
type: variant
@@ -10,6 +10,15 @@
:option:`-Wno-author <cmake -Wno->` on the command line.
This may not be set to ``false`` if ``errors.author`` is set to ``true``.
.. _`CMakePresets.configurePresets.warnings.cpsNonConformingLocation`:
``cpsNonConformingLocation``
.. presets-versionadded:: 14
An optional boolean. Equivalent to passing :option:`-Wcps-non-conforming-location <cmake -W>` or
:option:`-Wno-cps-non-conforming-location <cmake -Wno->` on the command line.
This may not be set to ``false`` if ``errors.cpsNonConformingLocation`` is set to ``true``.
.. _`CMakePresets.configurePresets.warnings.deprecated`:
``deprecated``
+1
View File
@@ -27,6 +27,7 @@
#define CM_FOR_EACH_DIAGNOSTIC_TABLE(ACTION, SELECT) \
SELECT(ACTION, Warn, CMD_NONE, CMD_AUTHOR, 12) \
SELECT(ACTION, Ignore, CMD_AUTHOR, CMD_CPS_NON_CONFORMING_LOCATION, 14) \
SELECT(ACTION, Warn, CMD_AUTHOR, CMD_DEPRECATED, 1) \
SELECT(ACTION, Warn, CMD_AUTHOR, CMD_EXPERIMENTAL, 12) \
SELECT(ACTION, Ignore, CMD_AUTHOR, CMD_STRICT, 13) \
+72 -8
View File
@@ -449,6 +449,17 @@ unsigned int parseVersion(std::string const& version, unsigned int& major,
version.c_str(), "%u.%u.%u.%u", &major, &minor, &patch, &tweak));
}
// Diagnostic details for CPS locations which are only conforming on specific
// platforms.
#ifndef _WIN32
cm::string_view const kCpsWindowsOnlyLocation =
"its directory is only CPS-conforming on Windows platforms"_s;
#endif
#ifndef __APPLE__
cm::string_view const kCpsAppleOnlyLocation =
"its directory is only CPS-conforming on Apple platforms"_s;
#endif
} // anonymous namespace
class cmFindPackageCommand::FlushDebugBufferOnExit
@@ -1976,15 +1987,18 @@ bool cmFindPackageCommand::FindConfig()
}
}
// Store the entry in the cache so it can be set by the user.
std::string init;
if (found) {
init = cmSystemTools::GetFilenamePath(this->FileFound);
} else {
init = this->Variable + "-NOTFOUND";
// If no file location error occurred, store the entry in the cache so it can
// be set by the user.
if (!this->FileLocationError) {
std::string init;
if (found) {
init = cmSystemTools::GetFilenamePath(this->FileFound);
} else {
init = this->Variable + "-NOTFOUND";
}
// We force the value since we do not get here if it was already set.
this->SetConfigDirCacheVariable(init);
}
// We force the value since we do not get here if it was already set.
this->SetConfigDirCacheVariable(init);
return found;
}
@@ -2003,6 +2017,26 @@ void cmFindPackageCommand::SetConfigDirCacheVariable(std::string const& value)
}
}
void cmFindPackageCommand::IssueCpsLocationDiagnostic(cm::string_view detail)
{
if (detail.empty()) {
detail = "its directory is not listed in the CPS specification for "
"package searching"_s;
}
constexpr auto category = cmDiagnostics::CMD_CPS_NON_CONFORMING_LOCATION;
this->Makefile->IssueDiagnostic(
category,
cmStrCat("find_package("_s, this->Name, ") accepted\n \""_s,
this->FileFound, "\"\nbut "_s, detail, '.'));
// If the diagnostic is treated as an error, mark the file location as
// erroneous.
if (this->Makefile->GetDiagnosticAction(category) >=
cmDiagnostics::SendError) {
this->FileLocationError = true;
}
}
bool cmFindPackageCommand::FindPrefixedConfig()
{
std::vector<std::string> const& prefixes = this->SearchPaths;
@@ -3333,28 +3367,43 @@ bool cmFindPackageCommand::SearchPrefix(std::string const& prefix)
// PREFIX/(Foo|foo|FOO)/(cps|CPS)/
if (TryGeneratedPaths(searchFn, pdt::Cps, prefix, cpsPkgDirGen, iCpsGen)) {
#ifndef _WIN32
this->IssueCpsLocationDiagnostic(kCpsWindowsOnlyLocation);
#endif
return true;
}
// PREFIX/(Foo|foo|FOO)/*/(cps|CPS)/
if (TryGeneratedPaths(searchFn, pdt::Cps, prefix, cpsPkgDirGen, iCpsGen,
anyDirGen)) {
#ifndef _WIN32
this->IssueCpsLocationDiagnostic(kCpsWindowsOnlyLocation);
#endif
return true;
}
// PREFIX/(cps|CPS)/(Foo|foo|FOO)/
if (TryGeneratedPaths(searchFn, pdt::Cps, prefix, iCpsGen, cpsPkgDirGen)) {
#ifndef _WIN32
this->IssueCpsLocationDiagnostic(kCpsWindowsOnlyLocation);
#endif
return true;
}
// PREFIX/(cps|CPS)/(Foo|foo|FOO)/*/
if (TryGeneratedPaths(searchFn, pdt::Cps, prefix, iCpsGen, cpsPkgDirGen,
anyDirGen)) {
#ifndef _WIN32
this->IssueCpsLocationDiagnostic(kCpsWindowsOnlyLocation);
#endif
return true;
}
// PREFIX/(cps|CPS)/ (useful on windows or in build trees)
if (TryGeneratedPaths(searchFn, pdt::Cps, prefix, iCpsGen)) {
// This location does not contain the package name and was removed from the
// CPS specification.
this->IssueCpsLocationDiagnostic();
return true;
}
@@ -3426,6 +3475,9 @@ bool cmFindPackageCommand::SearchPrefix(std::string const& prefix)
// PREFIX/(lib/ARCH|lib*|share)/cps/
if (TryGeneratedPaths(searchFn, pdt::Cps, prefix, commonGen, cpsGen)) {
// These locations do not contain the package name and were removed from
// the CPS specification.
this->IssueCpsLocationDiagnostic();
return true;
}
@@ -3485,11 +3537,19 @@ bool cmFindPackageCommand::SearchFrameworkPrefix(std::string const& prefix)
// <prefix>/Foo.framework/Versions/*/Resources/CPS/
if (TryGeneratedPaths(searchFn, pdt::Cps, prefix, fwGen, vGen, anyGen, rGen,
iCpsGen)) {
#ifndef __APPLE__
// Warn if reading CPS from a framework directory on non-Apple platforms.
this->IssueCpsLocationDiagnostic(kCpsAppleOnlyLocation);
#endif
return true;
}
// <prefix>/Foo.framework/Resources/CPS/
if (TryGeneratedPaths(searchFn, pdt::Cps, prefix, fwGen, rGen, iCpsGen)) {
#ifndef __APPLE__
// Warn if reading CPS from a framework directory on non-Apple platforms.
this->IssueCpsLocationDiagnostic(kCpsAppleOnlyLocation);
#endif
return true;
}
@@ -3530,6 +3590,10 @@ bool cmFindPackageCommand::SearchAppBundlePrefix(std::string const& prefix)
// <prefix>/Foo.app/Contents/Resources/CPS/
if (TryGeneratedPaths(searchFn, pdt::Cps, prefix, appGen, crGen,
cmCaseInsensitiveDirectoryListGenerator{ "cps"_s })) {
#ifndef __APPLE__
// Warn if reading CPS from an app bundle on non-Apple platforms.
this->IssueCpsLocationDiagnostic(kCpsAppleOnlyLocation);
#endif
return true;
}
+4
View File
@@ -174,6 +174,9 @@ private:
void StoreVersionFound();
void SetConfigDirCacheVariable(std::string const& value);
// Emit a diagnostic for a CPS file found in a non-conforming location.
void IssueCpsLocationDiagnostic(cm::string_view detail = {});
void PushFindPackageRootPathStack();
void PopFindPackageRootPathStack();
class PushPopRootPathStack;
@@ -252,6 +255,7 @@ private:
bool VersionExact = false;
std::string FileFound;
FoundPackageMode FileFoundMode = FoundPackageMode::None;
bool FileLocationError = false;
std::string VersionFound;
unsigned int VersionFoundMajor = 0;
unsigned int VersionFoundMinor = 0;
@@ -0,0 +1,11 @@
^CMake Warning \(cps-non-conforming-location\) at NonConformingLocation\.cmake:[0-9]+ \(find_package\):
find_package\(Trivial\) accepted
"[^"]*/Tests/RunCMake/find_package-CPS/cps/[Tt]rivial\.cps"
but its directory is not listed in the CPS specification for package
searching\.
Call Stack \(most recent call first\):
CMakeLists\.txt:3 \(include\)
This warning is for project developers\. Use -Wno-author or
-Wno-cps-non-conforming-location to suppress it\.$
@@ -0,0 +1,11 @@
cmake_minimum_required(VERSION 4.0)
include(Setup.cmake)
# Bump the diagnostic level up for this specific test. This diagnostic is set
# to ignore by default.
cmake_diagnostic(SET CMD_CPS_NON_CONFORMING_LOCATION WARN)
# Trivial is in PREFIX/cps/ which is no longer a CPS-conforming search
# location.
find_package(Trivial REQUIRED)
@@ -29,6 +29,9 @@ run_cmake(WrongName)
run_cmake(BadPrefix1)
run_cmake(BadPrefix2)
# CMD_CPS_NON_CONFORMING_LOCATION test
run_cmake(NonConformingLocation)
# Version-matching tests
run_cmake(ExactVersion)
run_cmake(CompatVersion)
@@ -0,0 +1,6 @@
{
"cps_version": "0.13",
"name": "Trivial",
"cps_path": "@prefix@/cps",
"components": {}
}