diff --git a/Help/command/install.rst b/Help/command/install.rst
index 7ed27e7546..0d6ccb6116 100644
--- a/Help/command/install.rst
+++ b/Help/command/install.rst
@@ -60,6 +60,11 @@ signatures that specify them. The common options are:
``
`` should be a relative path. An absolute path is allowed,
but not recommended.
+ .. versionadded:: 4.4
+ The :ref:`CMD_INSTALL_ABSOLUTE_DESTINATION `
+ diagnostic can be enabled to warn or error out when an absolute destination
+ is provided.
+
When a relative path is given, it is interpreted relative to the value
of the :variable:`CMAKE_INSTALL_PREFIX` variable.
The prefix can be relocated at install time using the ``DESTDIR``
diff --git a/Help/manual/cmake-diagnostics.7.rst b/Help/manual/cmake-diagnostics.7.rst
index 66a08d5409..5612299aae 100644
--- a/Help/manual/cmake-diagnostics.7.rst
+++ b/Help/manual/cmake-diagnostics.7.rst
@@ -88,3 +88,15 @@ Warn about variables that are declared on the command line, but not used.
Although the action of this warning category can be queried as usual, changes
made using the :command:`cmake_diagnostic` command have no effect.
+
+.. _CMD_INSTALL_ABSOLUTE_DESTINATION:
+
+``CMD_INSTALL_ABSOLUTE_DESTINATION`` (``-Winstall-absolute-destination``)
+-------------------------------------------------------------------------
+
+:Default: Ignore
+
+Warn when an :command:`install` command specifies an absolute
+``DESTINATION`` path. Absolute destinations are typically undesirable
+because they prevent the installation prefix from being overridden at
+install time.
diff --git a/Help/manual/cmake-presets.7.rst b/Help/manual/cmake-presets.7.rst
index e135cdf86f..4c672135ce 100644
--- a/Help/manual/cmake-presets.7.rst
+++ b/Help/manual/cmake-presets.7.rst
@@ -474,6 +474,9 @@ they were added and a summary of the new features and changes is given below.
* The ``uninitialized`` and ``unusedCli`` fields were added to
:preset:`configurePresets.errors`.
+ * The ``installAbsoluteDestination`` field was added to
+ :preset:`configurePresets.warnings` and :preset:`configurePresets.errors`.
+
* Changes to `Macro Expansion`_
* The `${fileDir} `_ macro now always expands to
diff --git a/Help/manual/presets/errors-properties.rst b/Help/manual/presets/errors-properties.rst
index 3f1216f9b3..7bf5dacba7 100644
--- a/Help/manual/presets/errors-properties.rst
+++ b/Help/manual/presets/errors-properties.rst
@@ -32,6 +32,15 @@
This may not be set to ``true``
if ``warnings.dev`` is set to ``false``.
+.. _`CMakePresets.configurePresets.errors.installAbsoluteDestination`:
+
+``installAbsoluteDestination``
+ .. presets-versionadded:: 12
+
+ An optional boolean. Equivalent to passing :cmake-option:`-Werror=install-absolute-destination` or
+ :cmake-option:`-Wno-error=install-absolute-destination` on the command line.
+ This may not be set to ``true`` if ``warnings.installAbsoluteDestination`` is set to ``false``.
+
.. _`CMakePresets.configurePresets.errors.uninitialized`:
``uninitialized``
diff --git a/Help/manual/presets/schema.json b/Help/manual/presets/schema.json
index 70f5024e5a..cd52447067 100644
--- a/Help/manual/presets/schema.json
+++ b/Help/manual/presets/schema.json
@@ -1286,6 +1286,9 @@
"deprecated": {
"$ref": "#/definitions/configurePresets.warnings.deprecated@v1.."
},
+ "installAbsoluteDestination": {
+ "$ref": "#/definitions/configurePresets.warnings.installAbsoluteDestination@v12"
+ },
"uninitialized": {
"$ref": "#/definitions/configurePresets.warnings.uninitialized@v1.."
},
@@ -1312,6 +1315,10 @@
"type": "boolean",
"description": "An optional boolean. Equivalent to passing -Wdev or -Wno-dev on the command line. This may not be set to false if errors.dev is set to true."
},
+ "configurePresets.warnings.installAbsoluteDestination@v12": {
+ "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.uninitialized@v1..": {
"type": "boolean",
"description": "An optional boolean. Equivalent to passing -Wuninitialized or -Wno-uninitialized on the command line. This may not be set to false if errors.uninitialized is set to true."
@@ -1364,6 +1371,9 @@
"deprecated": {
"$ref": "#/definitions/configurePresets.errors.deprecated@v1.."
},
+ "installAbsoluteDestination": {
+ "$ref": "#/definitions/configurePresets.errors.installAbsoluteDestination@v12"
+ },
"uninitialized": {
"$ref": "#/definitions/configurePresets.errors.uninitialized@v12"
},
@@ -1387,6 +1397,10 @@
"type": "boolean",
"description": "An optional boolean. Equivalent to passing -Werror=dev or -Wno-error=dev on the command line. This may not be set to true if warnings.dev is set to false."
},
+ "configurePresets.errors.installAbsoluteDestination@v12": {
+ "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.uninitialized@v12": {
"type": "boolean",
"description": "An optional boolean. Equivalent to passing -Werror=uninitialized or -Wno-error=uninitialized on the command line. This may not be set to true if warnings.uninitialized is set to false."
diff --git a/Help/manual/presets/warnings-properties.rst b/Help/manual/presets/warnings-properties.rst
index acfce8ffe5..0a311908ab 100644
--- a/Help/manual/presets/warnings-properties.rst
+++ b/Help/manual/presets/warnings-properties.rst
@@ -32,6 +32,15 @@
This may not be set to ``false``
if ``errors.dev`` is set to ``true``.
+.. _`CMakePresets.configurePresets.warnings.installAbsoluteDestination`:
+
+``installAbsoluteDestination``
+ .. presets-versionadded:: 12
+
+ An optional boolean. Equivalent to passing :option:`-Winstall-absolute-destination ` or
+ :option:`-Wno-install-absolute-destination ` on the command line.
+ This may not be set to ``false`` if ``errors.installAbsoluteDestination`` is set to ``true``.
+
.. _`CMakePresets.configurePresets.warnings.uninitialized`:
``uninitialized``
diff --git a/Help/release/dev/install-absolute-dest-configure-time.rst b/Help/release/dev/install-absolute-dest-configure-time.rst
new file mode 100644
index 0000000000..25a9c0add3
--- /dev/null
+++ b/Help/release/dev/install-absolute-dest-configure-time.rst
@@ -0,0 +1,10 @@
+install-absolute-dest-configure-time
+-------------------------------------
+
+* CMake gained the ability to diagnose :command:`install` commands that
+ specify an absolute ``DESTINATION`` path via the
+ :ref:`CMD_INSTALL_ABSOLUTE_DESTINATION `
+ diagnostic category. This diagnostic may be controlled with the
+ :option:`-Winstall-absolute-destination ` command-line option, the
+ ``installAbsoluteDestination`` field in a :manual:`cmake-presets(7)`
+ ``warnings`` object, or the :command:`cmake_diagnostic` command.
diff --git a/Help/variable/CMAKE_ERROR_ON_ABSOLUTE_INSTALL_DESTINATION.rst b/Help/variable/CMAKE_ERROR_ON_ABSOLUTE_INSTALL_DESTINATION.rst
index 38e9b7b365..84a27e5fe0 100644
--- a/Help/variable/CMAKE_ERROR_ON_ABSOLUTE_INSTALL_DESTINATION.rst
+++ b/Help/variable/CMAKE_ERROR_ON_ABSOLUTE_INSTALL_DESTINATION.rst
@@ -8,3 +8,11 @@ The fatal error is emitted before the installation of the offending
file takes place. This variable is used by CMake-generated
``cmake_install.cmake`` scripts. If one sets this variable to ``ON`` while
running the script, it may get fatal error messages from the script.
+
+.. versionadded:: 4.4
+
+ The :ref:`CMD_INSTALL_ABSOLUTE_DESTINATION `
+ diagnostic can be used to to generate errors for absolute install destinations
+ at generate time.
+
+See also :variable:`CMAKE_WARN_ON_ABSOLUTE_INSTALL_DESTINATION`.
diff --git a/Help/variable/CMAKE_WARN_ON_ABSOLUTE_INSTALL_DESTINATION.rst b/Help/variable/CMAKE_WARN_ON_ABSOLUTE_INSTALL_DESTINATION.rst
index 81c1158c26..fac680ea95 100644
--- a/Help/variable/CMAKE_WARN_ON_ABSOLUTE_INSTALL_DESTINATION.rst
+++ b/Help/variable/CMAKE_WARN_ON_ABSOLUTE_INSTALL_DESTINATION.rst
@@ -7,3 +7,11 @@ Ask ``cmake_install.cmake`` script to warn each time a file with absolute
This variable is used by CMake-generated ``cmake_install.cmake`` scripts.
If one sets this variable to ``ON`` while running the script, it may get
warning messages from the script.
+
+.. versionadded:: 4.4
+
+ The :ref:`CMD_INSTALL_ABSOLUTE_DESTINATION `
+ diagnostic can be used to to generate warnings for absolute install destinations
+ at generate time.
+
+See also :variable:`CMAKE_ERROR_ON_ABSOLUTE_INSTALL_DESTINATION`.
diff --git a/Source/cmDiagnostics.h b/Source/cmDiagnostics.h
index 3ab2239e1f..bad732b216 100644
--- a/Source/cmDiagnostics.h
+++ b/Source/cmDiagnostics.h
@@ -28,6 +28,7 @@
#define CM_FOR_EACH_DIAGNOSTIC_TABLE(ACTION, SELECT) \
SELECT(ACTION, Warn, CMD_NONE, CMD_AUTHOR, 12) \
SELECT(ACTION, Warn, CMD_AUTHOR, CMD_DEPRECATED, 1) \
+ SELECT(ACTION, Ignore, CMD_AUTHOR, CMD_INSTALL_ABSOLUTE_DESTINATION, 12) \
SELECT(ACTION, Ignore, CMD_NONE, CMD_UNINITIALIZED, 1) \
SELECT(ACTION, Warn, CMD_NONE, CMD_UNUSED_CLI, 1)
diff --git a/Source/cmExportInstallCMakeConfigGenerator.cxx b/Source/cmExportInstallCMakeConfigGenerator.cxx
index eae42b6ee5..2f87099323 100644
--- a/Source/cmExportInstallCMakeConfigGenerator.cxx
+++ b/Source/cmExportInstallCMakeConfigGenerator.cxx
@@ -284,22 +284,21 @@ std::string cmExportInstallCMakeConfigGenerator::GetFileSetDirectories(
auto configs =
gte->Makefile->GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig);
- cmGeneratorExpression ge(*gte->Makefile->GetCMakeInstance());
- auto cge =
- ge.Parse(te->FileSetGenerators.at(fileSet->GetName())->GetDestination());
-
for (auto const& config : configs) {
- auto unescapedDest = cge->Evaluate(gte->LocalGenerator, config, gte);
- auto dest = cmOutputConverter::EscapeForCMake(
- unescapedDest, cmOutputConverter::WrapQuotes::NoWrap);
- if (!cmSystemTools::FileIsFullPath(unescapedDest)) {
+ cmInstallFileSetGenerator::DestinationContext result =
+ te->FileSetGenerators.at(fileSet->GetName())
+ ->GetDestination(gte, config);
+
+ std::string dest = cmOutputConverter::EscapeForCMake(
+ result.UnescapedDestination, cmOutputConverter::WrapQuotes::NoWrap);
+ if (!cmSystemTools::FileIsFullPath(result.UnescapedDestination)) {
dest = cmStrCat("${_IMPORT_PREFIX}/", dest);
}
auto const& type = fileSet->GetType();
// C++ modules do not support interface file sets which are dependent upon
// the configuration.
- if (cge->GetHadContextSensitiveCondition() &&
+ if (result.HadContextSensitiveCondition &&
type == cm::FileSetMetadata::CXX_MODULES) {
auto* mf = this->IEGen->GetLocalGenerator()->GetMakefile();
std::ostringstream e;
@@ -311,7 +310,7 @@ std::string cmExportInstallCMakeConfigGenerator::GetFileSetDirectories(
return std::string{};
}
- if (cge->GetHadContextSensitiveCondition() && configs.size() != 1) {
+ if (result.HadContextSensitiveCondition && configs.size() != 1) {
resultVector.push_back(
cmStrCat("\"$<$:", dest, ">\""));
} else {
diff --git a/Source/cmExportInstallPackageInfoGenerator.cxx b/Source/cmExportInstallPackageInfoGenerator.cxx
index 78eb146299..202594a754 100644
--- a/Source/cmExportInstallPackageInfoGenerator.cxx
+++ b/Source/cmExportInstallPackageInfoGenerator.cxx
@@ -224,18 +224,14 @@ cmExportInstallPackageInfoGenerator::GetFileSetDirectory(
cmGeneratorTarget* gte, cmTargetExport const* te,
cmGeneratorFileSet const* fileSet, cm::optional const& config)
{
- cmGeneratorExpression ge(*gte->Makefile->GetCMakeInstance());
- auto cge =
- ge.Parse(te->FileSetGenerators.at(fileSet->GetName())->GetDestination());
+ cmInstallFileSetGenerator::DestinationContext result =
+ te->FileSetGenerators.at(fileSet->GetName())
+ ->GetDestination(gte, config.value_or(""));
- std::string const unescapedDest =
- cge->Evaluate(gte->LocalGenerator, config.value_or(""), gte);
- bool const isConfigDependent = cge->GetHadContextSensitiveCondition();
-
- if (config && !isConfigDependent) {
+ if (config && !result.HadContextSensitiveCondition) {
return {};
}
- if (!config && isConfigDependent) {
+ if (!config && result.HadContextSensitiveCondition) {
this->RequiresConfigFiles = true;
return {};
}
@@ -255,9 +251,9 @@ cmExportInstallPackageInfoGenerator::GetFileSetDirectory(
}
cm::optional dest = cmOutputConverter::EscapeForCMake(
- unescapedDest, cmOutputConverter::WrapQuotes::NoWrap);
+ result.UnescapedDestination, cmOutputConverter::WrapQuotes::NoWrap);
- if (!cmSystemTools::FileIsFullPath(unescapedDest)) {
+ if (!cmSystemTools::FileIsFullPath(result.UnescapedDestination)) {
dest = cmStrCat("@prefix@/"_s, *dest);
}
diff --git a/Source/cmExportInstallSbomGenerator.cxx b/Source/cmExportInstallSbomGenerator.cxx
index d944383902..f9641a1b48 100644
--- a/Source/cmExportInstallSbomGenerator.cxx
+++ b/Source/cmExportInstallSbomGenerator.cxx
@@ -216,15 +216,11 @@ cm::optional cmExportInstallSbomGenerator::GetFileSetDirectory(
cmGeneratorTarget* gte, cmTargetExport const* te,
cmGeneratorFileSet const* fileSet, cm::optional const& config)
{
- cmGeneratorExpression ge(*gte->Makefile->GetCMakeInstance());
- auto cge =
- ge.Parse(te->FileSetGenerators.at(fileSet->GetName())->GetDestination());
+ cmInstallFileSetGenerator::DestinationContext result =
+ te->FileSetGenerators.at(fileSet->GetName())
+ ->GetDestination(gte, config.value_or(""));
- std::string const unescapedDest =
- cge->Evaluate(gte->LocalGenerator, config.value_or(""), gte);
- bool const isConfigDependent = cge->GetHadContextSensitiveCondition();
-
- if (config && !isConfigDependent) {
+ if (config && !result.HadContextSensitiveCondition) {
return {};
}
@@ -241,9 +237,9 @@ cm::optional cmExportInstallSbomGenerator::GetFileSetDirectory(
}
cm::optional dest = cmOutputConverter::EscapeForCMake(
- unescapedDest, cmOutputConverter::WrapQuotes::NoWrap);
+ result.UnescapedDestination, cmOutputConverter::WrapQuotes::NoWrap);
- if (!cmSystemTools::FileIsFullPath(unescapedDest)) {
+ if (!cmSystemTools::FileIsFullPath(result.UnescapedDestination)) {
dest = cmStrCat("@prefix@/"_s, *dest);
}
diff --git a/Source/cmInstallCommand.cxx b/Source/cmInstallCommand.cxx
index 1fb9d4b5dd..8137d69b1b 100644
--- a/Source/cmInstallCommand.cxx
+++ b/Source/cmInstallCommand.cxx
@@ -1912,6 +1912,15 @@ bool HandleDirectoryMode(std::vector const& args,
cmInstallGenerator::MessageLevel message =
cmInstallGenerator::SelectMessageLevel(helper.Makefile, message_never);
+ // Check for an absolute destination.
+ if (cmGeneratorExpression::Find(*destination) == std::string::npos &&
+ cmSystemTools::FileIsFullPath(*destination)) {
+ helper.Makefile->IssueDiagnostic(
+ cmDiagnostics::CMD_INSTALL_ABSOLUTE_DESTINATION,
+ cmStrCat("INSTALL command given absolute DESTINATION path (",
+ *destination, ").\n"));
+ }
+
// Create the directory install generator.
helper.Makefile->AddInstallGenerator(
cm::make_unique(
diff --git a/Source/cmInstallCommandArguments.h b/Source/cmInstallCommandArguments.h
index 7bf861ea3e..c8b1ad94c7 100644
--- a/Source/cmInstallCommandArguments.h
+++ b/Source/cmInstallCommandArguments.h
@@ -22,7 +22,7 @@ public:
this->GenericArguments = args;
}
- // Compute destination path.and check permissions
+ // Compute destination path and check permissions.
bool Finalize();
std::string const& GetDestination() const;
diff --git a/Source/cmInstallDirectoryGenerator.cxx b/Source/cmInstallDirectoryGenerator.cxx
index cf1fac91fc..bd62dd372c 100644
--- a/Source/cmInstallDirectoryGenerator.cxx
+++ b/Source/cmInstallDirectoryGenerator.cxx
@@ -118,6 +118,9 @@ void cmInstallDirectoryGenerator::AddDirectoryInstallRule(
std::string cmInstallDirectoryGenerator::GetDestination(
std::string const& config) const
{
- return cmGeneratorExpression::Evaluate(this->Destination,
- this->LocalGenerator, config);
+ std::string dest = cmGeneratorExpression::Evaluate(
+ this->Destination, this->LocalGenerator, config);
+ cmInstallGenerator::CheckAbsoluteDestination(dest, this->LocalGenerator,
+ this->Backtrace);
+ return dest;
}
diff --git a/Source/cmInstallFileSetGenerator.cxx b/Source/cmInstallFileSetGenerator.cxx
index 85b8275ca3..e6e9f031e8 100644
--- a/Source/cmInstallFileSetGenerator.cxx
+++ b/Source/cmInstallFileSetGenerator.cxx
@@ -4,6 +4,7 @@
#include
#include