diff --git a/Help/command/install.rst b/Help/command/install.rst index 48194bb6d8..a0e6a081f7 100644 --- a/Help/command/install.rst +++ b/Help/command/install.rst @@ -58,7 +58,7 @@ signatures that specify them. The common options are: ```` should be a relative path. An absolute path is allowed, but not recommended. - When a relative path is given it is interpreted relative to the value + 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`` mechanism explained in the :variable:`CMAKE_INSTALL_PREFIX` variable @@ -75,6 +75,11 @@ signatures that specify them. The common options are: If an absolute path (with a leading slash or drive letter) is given it is used verbatim. + .. versionchanged:: 3.31 + ```` will be normalized according to the same + :ref:`normalization rules ` as the + :command:`cmake_path` command. + ``PERMISSIONS ...`` Specify permissions for installed files. Valid permissions are ``OWNER_READ``, ``OWNER_WRITE``, ``OWNER_EXECUTE``, ``GROUP_READ``, @@ -396,6 +401,12 @@ Signatures If a relative path is specified, it is treated as relative to the :genex:`$`. + Unlike other ``DESTINATION`` arguments for the various ``install()`` + subcommands, paths given after ``INCLUDES DESTINATION`` are used as + given. They are not normalized, nor assumed to be normalized, although + it is recommended that they are given in normalized form (see + :ref:`Normalization`). + ``RUNTIME_DEPENDENCY_SET `` .. versionadded:: 3.21 @@ -815,6 +826,7 @@ Signatures the generated file will be called ``.cmake`` but the ``FILE`` option may be used to specify a different name. The value given to the ``FILE`` option must be a file name with the ``.cmake`` extension. + If a ``CONFIGURATIONS`` option is given then the file will only be installed when one of the named configurations is installed. Additionally, the generated import file will reference only the matching target diff --git a/Help/manual/cmake-policies.7.rst b/Help/manual/cmake-policies.7.rst index 85bb6ec225..3b97848302 100644 --- a/Help/manual/cmake-policies.7.rst +++ b/Help/manual/cmake-policies.7.rst @@ -57,6 +57,7 @@ Policies Introduced by CMake 3.31 .. toctree:: :maxdepth: 1 + CMP0177: install() DESTINATION paths are normalized. CMP0176: execute_process() ENCODING is UTF-8 by default. CMP0175: add_custom_command() rejects invalid arguments. CMP0174: cmake_parse_arguments(PARSE_ARGV) defines a variable for an empty string after a single-value keyword. diff --git a/Help/policy/CMP0177.rst b/Help/policy/CMP0177.rst new file mode 100644 index 0000000000..3eca9f3b31 --- /dev/null +++ b/Help/policy/CMP0177.rst @@ -0,0 +1,38 @@ +CMP0177 +------- + +.. versionadded:: 3.31 + +:command:`install` ``DESTINATION`` paths are normalized. + +The :command:`install` command has a number of different forms, and most of +them take a ``DESTINATION`` keyword, some in more than one place. +CMake 3.30 and earlier used the value given after the ``DESTINATION`` keyword +as provided with no transformations. The :command:`install(EXPORT)` form +assumes the path contains no ``..`` or ``.`` path components when computing +a path relative to the ``DESTINATION``, and if the project provided a path +that violated that assumption, the computed path would be incorrect. + +CMake 3.31 normalizes all ``DESTINATION`` values given in any form of the +:command:`install` command, except for the ``INCLUDES DESTINATION`` of the +:command:`install(TARGETS)` form. The normalization performed is the same +as for the :command:`cmake_path` command (see :ref:`Normalization`). + +The ``OLD`` behavior of this policy performs no translation on the +``DESTINATION`` values of any :command:`install` command. They are used +exactly as provided. If a destination path contains ``..`` or ``.`` path +components, :command:`install(EXPORT)` will use the same wrong paths as +CMake 3.30 and earlier. + +The ``NEW`` behavior will normalize all ``DESTINATION`` values except for +``INCLUDES DESTINATION``. If a destination path contains a generator +expression, it will be wrapped in a ``$`` +generator expression. + +This policy was introduced in CMake version 3.31. +It may be set by :command:`cmake_policy` or :command:`cmake_minimum_required`. +If it is not set, CMake will warn if it detects a path that would be different +if normalized, and uses ``OLD`` behavior. If a destination path contains a +generator expression, no such warning will be issued regardless of the value. + +.. include:: DEPRECATED.txt diff --git a/Help/release/dev/normalize-install-destination-paths.rst b/Help/release/dev/normalize-install-destination-paths.rst new file mode 100644 index 0000000000..74a76a67ab --- /dev/null +++ b/Help/release/dev/normalize-install-destination-paths.rst @@ -0,0 +1,6 @@ +normalize-install-destination-paths +----------------------------------- + +* All ``DESTINATION`` arguments in :command:`install` commands + are now :ref:`normalized `, with the exception + of ``INCLUDES DESTINATION`` arguments in the ``TARGETS`` form. diff --git a/Source/cmGeneratorExpression.cxx b/Source/cmGeneratorExpression.cxx index deb292dc0f..553ea1fb9c 100644 --- a/Source/cmGeneratorExpression.cxx +++ b/Source/cmGeneratorExpression.cxx @@ -375,14 +375,15 @@ std::string cmGeneratorExpression::Preprocess(const std::string& input, return std::string(); } -std::string::size_type cmGeneratorExpression::Find(const std::string& input) +cm::string_view::size_type cmGeneratorExpression::Find( + const cm::string_view& input) { - const std::string::size_type openpos = input.find("$<"); - if (openpos != std::string::npos && - input.find('>', openpos) != std::string::npos) { + const cm::string_view::size_type openpos = input.find("$<"); + if (openpos != cm::string_view::npos && + input.find('>', openpos) != cm::string_view::npos) { return openpos; } - return std::string::npos; + return cm::string_view::npos; } bool cmGeneratorExpression::IsValidTargetName(const std::string& input) diff --git a/Source/cmGeneratorExpression.h b/Source/cmGeneratorExpression.h index 4452f2dd09..134930804f 100644 --- a/Source/cmGeneratorExpression.h +++ b/Source/cmGeneratorExpression.h @@ -66,7 +66,7 @@ public: static void Split(const std::string& input, std::vector& output); - static std::string::size_type Find(const std::string& input); + static cm::string_view::size_type Find(const cm::string_view& input); static bool IsValidTargetName(const std::string& input); diff --git a/Source/cmInstallCommand.cxx b/Source/cmInstallCommand.cxx index 3289f476db..9a797a7732 100644 --- a/Source/cmInstallCommand.cxx +++ b/Source/cmInstallCommand.cxx @@ -20,6 +20,7 @@ #include "cmArgumentParser.h" #include "cmArgumentParserTypes.h" +#include "cmCMakePath.h" #include "cmExecutionStatus.h" #include "cmExperimental.h" #include "cmExportSet.h" @@ -455,7 +456,8 @@ bool HandleTargetsMode(std::vector const& args, runtimeDependenciesArgVector; std::string runtimeDependencySetArg; std::vector unknownArgs; - cmInstallCommandArguments genericArgs(helper.DefaultComponentName); + cmInstallCommandArguments genericArgs(helper.DefaultComponentName, + *helper.Makefile); genericArgs.Bind("TARGETS"_s, targetList); genericArgs.Bind("EXPORT"_s, exports); genericArgs.Bind("RUNTIME_DEPENDENCIES"_s, runtimeDependenciesArgVector); @@ -469,19 +471,31 @@ bool HandleTargetsMode(std::vector const& args, &unknownArgs) : RuntimeDependenciesArgs(); - cmInstallCommandArguments archiveArgs(helper.DefaultComponentName); - cmInstallCommandArguments libraryArgs(helper.DefaultComponentName); - cmInstallCommandArguments runtimeArgs(helper.DefaultComponentName); - cmInstallCommandArguments objectArgs(helper.DefaultComponentName); - cmInstallCommandArguments frameworkArgs(helper.DefaultComponentName); - cmInstallCommandArguments bundleArgs(helper.DefaultComponentName); - cmInstallCommandArguments privateHeaderArgs(helper.DefaultComponentName); - cmInstallCommandArguments publicHeaderArgs(helper.DefaultComponentName); - cmInstallCommandArguments resourceArgs(helper.DefaultComponentName); + cmInstallCommandArguments archiveArgs(helper.DefaultComponentName, + *helper.Makefile); + cmInstallCommandArguments libraryArgs(helper.DefaultComponentName, + *helper.Makefile); + cmInstallCommandArguments runtimeArgs(helper.DefaultComponentName, + *helper.Makefile); + cmInstallCommandArguments objectArgs(helper.DefaultComponentName, + *helper.Makefile); + cmInstallCommandArguments frameworkArgs(helper.DefaultComponentName, + *helper.Makefile); + cmInstallCommandArguments bundleArgs(helper.DefaultComponentName, + *helper.Makefile); + cmInstallCommandArguments privateHeaderArgs(helper.DefaultComponentName, + *helper.Makefile); + cmInstallCommandArguments publicHeaderArgs(helper.DefaultComponentName, + *helper.Makefile); + cmInstallCommandArguments resourceArgs(helper.DefaultComponentName, + *helper.Makefile); cmInstallCommandIncludesArgument includesArgs; std::vector fileSetArgs( - argVectors.FileSets.size(), { helper.DefaultComponentName }); - cmInstallCommandArguments cxxModuleBmiArgs(helper.DefaultComponentName); + argVectors.FileSets.size(), + { cmInstallCommandFileSetArguments(helper.DefaultComponentName, + *helper.Makefile) }); + cmInstallCommandArguments cxxModuleBmiArgs(helper.DefaultComponentName, + *helper.Makefile); // now parse the args for specific parts of the target (e.g. LIBRARY, // RUNTIME, ARCHIVE etc. @@ -501,7 +515,8 @@ bool HandleTargetsMode(std::vector const& args, // cmArgumentParser::Bind() binds to a specific address, but the // objects in the vector can move around. So we parse in an object with a // fixed address and then copy the data into the vector. - cmInstallCommandFileSetArguments fileSetArg(helper.DefaultComponentName); + cmInstallCommandFileSetArguments fileSetArg(helper.DefaultComponentName, + *helper.Makefile); fileSetArg.Parse(argVectors.FileSets[i], &unknownArgs); fileSetArgs[i] = std::move(fileSetArg); } @@ -1312,16 +1327,21 @@ bool HandleImportedRuntimeArtifactsMode(std::vector const& args, ArgumentParser::MaybeEmpty> targetList; std::string runtimeDependencySetArg; std::vector unknownArgs; - cmInstallCommandArguments genericArgs(helper.DefaultComponentName); + cmInstallCommandArguments genericArgs(helper.DefaultComponentName, + *helper.Makefile); genericArgs.Bind("IMPORTED_RUNTIME_ARTIFACTS"_s, targetList) .Bind("RUNTIME_DEPENDENCY_SET"_s, runtimeDependencySetArg); genericArgs.Parse(genericArgVector, &unknownArgs); bool success = genericArgs.Finalize(); - cmInstallCommandArguments libraryArgs(helper.DefaultComponentName); - cmInstallCommandArguments runtimeArgs(helper.DefaultComponentName); - cmInstallCommandArguments frameworkArgs(helper.DefaultComponentName); - cmInstallCommandArguments bundleArgs(helper.DefaultComponentName); + cmInstallCommandArguments libraryArgs(helper.DefaultComponentName, + *helper.Makefile); + cmInstallCommandArguments runtimeArgs(helper.DefaultComponentName, + *helper.Makefile); + cmInstallCommandArguments frameworkArgs(helper.DefaultComponentName, + *helper.Makefile); + cmInstallCommandArguments bundleArgs(helper.DefaultComponentName, + *helper.Makefile); // now parse the args for specific parts of the target (e.g. LIBRARY, // RUNTIME etc. @@ -1549,7 +1569,7 @@ bool HandleFilesMode(std::vector const& args, // This is the FILES mode. bool programs = (args[0] == "PROGRAMS"); - cmInstallCommandArguments ica(helper.DefaultComponentName); + cmInstallCommandArguments ica(helper.DefaultComponentName, *helper.Makefile); ArgumentParser::MaybeEmpty> files; ica.Bind(programs ? "PROGRAMS"_s : "FILES"_s, files); std::vector unknownArgs; @@ -1683,7 +1703,7 @@ bool HandleDirectoryMode(std::vector const& args, bool exclude_from_all = false; bool message_never = false; std::vector dirs; - const std::string* destination = nullptr; + cm::optional destination; std::string permissions_file; std::string permissions_dir; std::vector configurations; @@ -1841,7 +1861,33 @@ bool HandleDirectoryMode(std::vector const& args, } else if (doing == DoingConfigurations) { configurations.push_back(args[i]); } else if (doing == DoingDestination) { - destination = &args[i]; + // A trailing slash is meaningful for this form, but normalization + // preserves it if present + switch (status.GetMakefile().GetPolicyStatus(cmPolicies::CMP0177)) { + case cmPolicies::NEW: + destination = cmCMakePath(args[i]).Normal().String(); + break; + case cmPolicies::WARN: + // We can't be certain if a warning is appropriate if there are any + // generator expressions + if (cmGeneratorExpression::Find(args[i]) == cm::string_view::npos && + args[i] != cmCMakePath(args[i]).Normal().String()) { + status.GetMakefile().IssueMessage( + MessageType::AUTHOR_WARNING, + cmPolicies::GetPolicyWarning(cmPolicies::CMP0177)); + } + CM_FALLTHROUGH; + case cmPolicies::OLD: + destination = args[i]; + break; + case cmPolicies::REQUIRED_ALWAYS: + case cmPolicies::REQUIRED_IF_USED: + // We should never get here, only OLD, WARN, and NEW are used + status.GetMakefile().IssueMessage( + MessageType::FATAL_ERROR, + cmPolicies::GetRequiredPolicyError(cmPolicies::CMP0177)); + return false; + } doing = DoingNone; } else if (doing == DoingType) { if (allowedTypes.count(args[i]) == 0) { @@ -1911,7 +1957,7 @@ bool HandleDirectoryMode(std::vector const& args, } // Support installing an empty directory. - if (dirs.empty() && destination) { + if (dirs.empty() && destination.has_value()) { dirs.emplace_back(); } @@ -1919,15 +1965,13 @@ bool HandleDirectoryMode(std::vector const& args, if (dirs.empty()) { return true; } - std::string destinationStr; - if (!destination) { + if (!destination.has_value()) { if (type.empty()) { // A destination is required. status.SetError(cmStrCat(args[0], " given no DESTINATION!")); return false; } - destinationStr = helper.GetDestinationForType(nullptr, type); - destination = &destinationStr; + destination = helper.GetDestinationForType(nullptr, type); } else if (!type.empty()) { status.SetError(cmStrCat(args[0], " given both TYPE and DESTINATION " @@ -1959,7 +2003,7 @@ bool HandleExportAndroidMKMode(std::vector const& args, Helper helper(status); // This is the EXPORT mode. - cmInstallCommandArguments ica(helper.DefaultComponentName); + cmInstallCommandArguments ica(helper.DefaultComponentName, *helper.Makefile); std::string exp; std::string name_space; @@ -2052,7 +2096,7 @@ bool HandleExportMode(std::vector const& args, Helper helper(status); // This is the EXPORT mode. - cmInstallCommandArguments ica(helper.DefaultComponentName); + cmInstallCommandArguments ica(helper.DefaultComponentName, *helper.Makefile); std::string exp; std::string name_space; @@ -2178,7 +2222,7 @@ bool HandlePackageInfoMode(std::vector const& args, Helper helper(status); // This is the PACKAGE_INFO mode. - cmInstallCommandArguments ica(helper.DefaultComponentName); + cmInstallCommandArguments ica(helper.DefaultComponentName, *helper.Makefile); ArgumentParser::NonEmpty pkg; ArgumentParser::NonEmpty appendix; @@ -2191,7 +2235,6 @@ bool HandlePackageInfoMode(std::vector const& args, ArgumentParser::NonEmpty> defaultConfigs; ArgumentParser::NonEmpty cxxModulesDirectory; - // TODO: Support DESTINATION. ica.Bind("PACKAGE_INFO"_s, pkg); ica.Bind("EXPORT"_s, exportName); ica.Bind("APPENDIX"_s, appendix); @@ -2338,14 +2381,18 @@ bool HandleRuntimeDependencySetMode(std::vector const& args, // These generic args also contain the runtime dependency set std::string runtimeDependencySetArg; std::vector runtimeDependencyArgVector; - cmInstallCommandArguments genericArgs(helper.DefaultComponentName); + cmInstallCommandArguments genericArgs(helper.DefaultComponentName, + *helper.Makefile); genericArgs.Bind("RUNTIME_DEPENDENCY_SET"_s, runtimeDependencySetArg); genericArgs.Parse(genericArgVector, &runtimeDependencyArgVector); bool success = genericArgs.Finalize(); - cmInstallCommandArguments libraryArgs(helper.DefaultComponentName); - cmInstallCommandArguments runtimeArgs(helper.DefaultComponentName); - cmInstallCommandArguments frameworkArgs(helper.DefaultComponentName); + cmInstallCommandArguments libraryArgs(helper.DefaultComponentName, + *helper.Makefile); + cmInstallCommandArguments runtimeArgs(helper.DefaultComponentName, + *helper.Makefile); + cmInstallCommandArguments frameworkArgs(helper.DefaultComponentName, + *helper.Makefile); // Now also parse the file(GET_RUNTIME_DEPENDENCY) args std::vector unknownArgs; diff --git a/Source/cmInstallCommandArguments.cxx b/Source/cmInstallCommandArguments.cxx index 25656473a8..f371cff208 100644 --- a/Source/cmInstallCommandArguments.cxx +++ b/Source/cmInstallCommandArguments.cxx @@ -3,11 +3,19 @@ #include "cmInstallCommandArguments.h" #include +#include #include +#include #include +#include "cmCMakePath.h" +#include "cmGeneratorExpression.h" +#include "cmMakefile.h" +#include "cmMessageType.h" +#include "cmPolicies.h" #include "cmRange.h" +#include "cmStringAlgorithms.h" #include "cmSystemTools.h" // Table of valid permissions. @@ -20,10 +28,53 @@ const char* cmInstallCommandArguments::PermissionsTable[] = { const std::string cmInstallCommandArguments::EmptyString; cmInstallCommandArguments::cmInstallCommandArguments( - std::string defaultComponent) + std::string defaultComponent, cmMakefile& makefile) : DefaultComponentName(std::move(defaultComponent)) { - this->Bind("DESTINATION"_s, this->Destination); + std::function normalizeDest; + + switch (makefile.GetPolicyStatus(cmPolicies::CMP0177)) { + case cmPolicies::OLD: + normalizeDest = [this](cm::string_view arg) -> ArgumentParser::Continue { + this->Destination = std::string(arg.begin(), arg.end()); + return ArgumentParser::Continue::Yes; + }; + break; + case cmPolicies::WARN: + normalizeDest = + [this, &makefile](cm::string_view arg) -> ArgumentParser::Continue { + this->Destination = std::string(arg.begin(), arg.end()); + // We can't be certain if a warning is appropriate if there are any + // generator expressions + if (cmGeneratorExpression::Find(arg) == cm::string_view::npos && + arg != cmCMakePath(arg).Normal().String()) { + makefile.IssueMessage( + MessageType::AUTHOR_WARNING, + cmPolicies::GetPolicyWarning(cmPolicies::CMP0177)); + } + return ArgumentParser::Continue::Yes; + }; + break; + case cmPolicies::NEW: + normalizeDest = [this](cm::string_view arg) -> ArgumentParser::Continue { + if (cmGeneratorExpression::Find(arg) == cm::string_view::npos) { + this->Destination = cmCMakePath(arg).Normal().String(); + } else { + this->Destination = + cmStrCat("$'); + } + return ArgumentParser::Continue::Yes; + }; + break; + case cmPolicies::REQUIRED_ALWAYS: + case cmPolicies::REQUIRED_IF_USED: + // We should never get here, only OLD, WARN, and NEW are used + makefile.IssueMessage( + MessageType::FATAL_ERROR, + cmPolicies::GetRequiredPolicyError(cmPolicies::CMP0177)); + } + + this->Bind("DESTINATION"_s, normalizeDest); this->Bind("COMPONENT"_s, this->Component); this->Bind("NAMELINK_COMPONENT"_s, this->NamelinkComponent); this->Bind("EXCLUDE_FROM_ALL"_s, this->ExcludeFromAll); @@ -227,8 +278,8 @@ void cmInstallCommandIncludesArgument::Parse( } cmInstallCommandFileSetArguments::cmInstallCommandFileSetArguments( - std::string defaultComponent) - : cmInstallCommandArguments(std::move(defaultComponent)) + std::string defaultComponent, cmMakefile& makefile) + : cmInstallCommandArguments(std::move(defaultComponent), makefile) { this->Bind("FILE_SET"_s, this->FileSet); } diff --git a/Source/cmInstallCommandArguments.h b/Source/cmInstallCommandArguments.h index 6e46aaca57..cb3917ef88 100644 --- a/Source/cmInstallCommandArguments.h +++ b/Source/cmInstallCommandArguments.h @@ -10,10 +10,13 @@ #include "cmArgumentParser.h" #include "cmArgumentParserTypes.h" +class cmMakefile; + class cmInstallCommandArguments : public cmArgumentParser { public: - cmInstallCommandArguments(std::string defaultComponent); + cmInstallCommandArguments(std::string defaultComponent, + cmMakefile& makefile); void SetGenericArguments(cmInstallCommandArguments* args) { this->GenericArguments = args; @@ -78,7 +81,8 @@ private: class cmInstallCommandFileSetArguments : public cmInstallCommandArguments { public: - cmInstallCommandFileSetArguments(std::string defaultComponent); + cmInstallCommandFileSetArguments(std::string defaultComponent, + cmMakefile& makefile); void Parse(std::vector args, std::vector* unconsumedArgs); diff --git a/Source/cmPolicies.h b/Source/cmPolicies.h index 254c3234ee..88ced3fa72 100644 --- a/Source/cmPolicies.h +++ b/Source/cmPolicies.h @@ -541,7 +541,9 @@ class cmMakefile; SELECT(POLICY, CMP0175, "add_custom_command() rejects invalid arguments.", \ 3, 31, 0, cmPolicies::WARN) \ SELECT(POLICY, CMP0176, "execute_process() ENCODING is UTF-8 by default.", \ - 3, 31, 0, cmPolicies::WARN) + 3, 31, 0, cmPolicies::WARN) \ + SELECT(POLICY, CMP0177, "install() DESTINATION paths are normalized.", 3, \ + 31, 0, cmPolicies::WARN) #define CM_SELECT_ID(F, A1, A2, A3, A4, A5, A6) F(A1) #define CM_FOR_EACH_POLICY_ID(POLICY) \ diff --git a/Tests/RunCMake/install/CMP0177-NEW-all-check.cmake b/Tests/RunCMake/install/CMP0177-NEW-all-check.cmake new file mode 100644 index 0000000000..3524b6dc6c --- /dev/null +++ b/Tests/RunCMake/install/CMP0177-NEW-all-check.cmake @@ -0,0 +1,36 @@ +set(installBase ${RunCMake_TEST_BINARY_DIR}/root-all) + +foreach(i RANGE 1 5) + set(subdir shouldNotRemain${i}) + if(IS_DIRECTORY ${installBase}/${subdir}) + set(RunCMake_TEST_FAILED "Check failed.") + string(APPEND RunCMake_TEST_FAILURE_MESSAGE + "\nUnexpectedly created install path that should have disappeared with " + "normalization:\n" + " ${installBase}/${subdir}" + ) + endif() +endforeach() + +file(GLOB perConfigFiles ${installBase}/lib/cmake/pkg/pkg-config-*.cmake) +foreach(file IN LISTS perConfigFiles ITEMS ${installBase}/lib/cmake/pkg/pkg-config.cmake) + file(STRINGS ${file} matches REGEX shouldNotRemain) + if(NOT matches STREQUAL "") + set(RunCMake_TEST_FAILED "Check failed.") + string(APPEND RunCMake_TEST_FAILURE_MESSAGE + "\nNon-normalized path found in ${file}:" + ) + foreach(match IN LISTS matches) + string(APPEND RunCMake_TEST_FAILURE_MESSAGE "\n ${match}") + endforeach() + endif() +endforeach() + +if(NOT EXISTS "${installBase}/dirs/dir/empty.txt") + set(RunCMake_TEST_FAILED "Check failed.") + string(APPEND RunCMake_TEST_FAILURE_MESSAGE + "\nNon-normalized DIRECTORY destination not handled correctly. " + "Expected to find the following file, but it was missing:\n" + " ${installBase}/dirs/dir/empty.txt" + ) +endif() diff --git a/Tests/RunCMake/install/CMP0177-NEW-verify.cmake b/Tests/RunCMake/install/CMP0177-NEW-verify.cmake new file mode 100644 index 0000000000..7b8b2dcfa6 --- /dev/null +++ b/Tests/RunCMake/install/CMP0177-NEW-verify.cmake @@ -0,0 +1,2 @@ +enable_language(C) +find_package(pkg REQUIRED) diff --git a/Tests/RunCMake/install/CMP0177-NEW.cmake b/Tests/RunCMake/install/CMP0177-NEW.cmake new file mode 100644 index 0000000000..698d9ceca7 --- /dev/null +++ b/Tests/RunCMake/install/CMP0177-NEW.cmake @@ -0,0 +1,2 @@ +cmake_policy(SET CMP0177 NEW) +include(${CMAKE_CURRENT_LIST_DIR}/CMP0177.cmake) diff --git a/Tests/RunCMake/install/CMP0177-OLD-verify-result.txt b/Tests/RunCMake/install/CMP0177-OLD-verify-result.txt new file mode 100644 index 0000000000..d00491fd7e --- /dev/null +++ b/Tests/RunCMake/install/CMP0177-OLD-verify-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/install/CMP0177-OLD-verify-stderr.txt b/Tests/RunCMake/install/CMP0177-OLD-verify-stderr.txt new file mode 100644 index 0000000000..366660bac2 --- /dev/null +++ b/Tests/RunCMake/install/CMP0177-OLD-verify-stderr.txt @@ -0,0 +1,13 @@ +CMake Error at [^ +]*CMP0177-OLD-build/root-all/lib/cmake/pkg/pkg-config\.cmake:[0-9]+ \(message\): + The imported target "foo1" references the file ++ ".*/shouldNotRemain1/\.\./lib/(libfoo1\.a|foo1\.l(ib)?)" ++ but this file does not exist\. Possible reasons include: ++ \* The file was deleted, renamed, or moved to another location\. ++ \* An install or uninstall procedure did not complete successfully\. ++ \* The installation package was faulty and contained ++ ".*/Tests/RunCMake/install/CMP0177-OLD-build/root-all/lib/cmake/pkg/pkg-config\.cmake" ++ but not all the files it references\. ++Call Stack \(most recent call first\): + CMP0177-OLD-verify\.cmake:2 \(find_package\) + CMakeLists\.txt:3 \(include\) diff --git a/Tests/RunCMake/install/CMP0177-OLD-verify.cmake b/Tests/RunCMake/install/CMP0177-OLD-verify.cmake new file mode 100644 index 0000000000..7b8b2dcfa6 --- /dev/null +++ b/Tests/RunCMake/install/CMP0177-OLD-verify.cmake @@ -0,0 +1,2 @@ +enable_language(C) +find_package(pkg REQUIRED) diff --git a/Tests/RunCMake/install/CMP0177-OLD.cmake b/Tests/RunCMake/install/CMP0177-OLD.cmake new file mode 100644 index 0000000000..6389983440 --- /dev/null +++ b/Tests/RunCMake/install/CMP0177-OLD.cmake @@ -0,0 +1,2 @@ +cmake_policy(SET CMP0177 OLD) +include(${CMAKE_CURRENT_LIST_DIR}/CMP0177.cmake) diff --git a/Tests/RunCMake/install/CMP0177-WARN-stderr.txt b/Tests/RunCMake/install/CMP0177-WARN-stderr.txt new file mode 100644 index 0000000000..a7d8b2e549 --- /dev/null +++ b/Tests/RunCMake/install/CMP0177-WARN-stderr.txt @@ -0,0 +1,35 @@ +CMake Warning \(dev\) at CMP0177\.cmake:[0-9]+ \(install\): + Policy CMP0177 is not set: install\(\) DESTINATION paths are normalized\. Run + "cmake --help-policy CMP0177" for policy details\. Use the cmake_policy + command to set the policy and suppress this warning\. +Call Stack \(most recent call first\): + CMP0177-WARN\.cmake:1 \(include\) + CMakeLists\.txt:3 \(include\) +This warning is for project developers\. Use -Wno-dev to suppress it\. + +CMake Warning \(dev\) at CMP0177\.cmake:[0-9]+ \(install\): + Policy CMP0177 is not set: install\(\) DESTINATION paths are normalized\. Run + "cmake --help-policy CMP0177" for policy details\. Use the cmake_policy + command to set the policy and suppress this warning\. +Call Stack \(most recent call first\): + CMP0177-WARN\.cmake:1 \(include\) + CMakeLists\.txt:3 \(include\) +This warning is for project developers\. Use -Wno-dev to suppress it\. + +CMake Warning \(dev\) at CMP0177\.cmake:[0-9]+ \(install\): + Policy CMP0177 is not set: install\(\) DESTINATION paths are normalized\. Run + "cmake --help-policy CMP0177" for policy details\. Use the cmake_policy + command to set the policy and suppress this warning\. +Call Stack \(most recent call first\): + CMP0177-WARN\.cmake:1 \(include\) + CMakeLists\.txt:3 \(include\) +This warning is for project developers\. Use -Wno-dev to suppress it\. + +CMake Warning \(dev\) at CMP0177\.cmake:[0-9]+ \(install\): + Policy CMP0177 is not set: install\(\) DESTINATION paths are normalized\. Run + "cmake --help-policy CMP0177" for policy details\. Use the cmake_policy + command to set the policy and suppress this warning\. +Call Stack \(most recent call first\): + CMP0177-WARN\.cmake:1 \(include\) + CMakeLists\.txt:3 \(include\) +This warning is for project developers\. Use -Wno-dev to suppress it\. diff --git a/Tests/RunCMake/install/CMP0177-WARN-verify-result.txt b/Tests/RunCMake/install/CMP0177-WARN-verify-result.txt new file mode 100644 index 0000000000..d00491fd7e --- /dev/null +++ b/Tests/RunCMake/install/CMP0177-WARN-verify-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/install/CMP0177-WARN-verify-stderr.txt b/Tests/RunCMake/install/CMP0177-WARN-verify-stderr.txt new file mode 100644 index 0000000000..0eca815666 --- /dev/null +++ b/Tests/RunCMake/install/CMP0177-WARN-verify-stderr.txt @@ -0,0 +1,13 @@ +CMake Error at [^ +]*CMP0177-WARN-build/root-all/lib/cmake/pkg/pkg-config\.cmake:[0-9]+ \(message\): + The imported target "foo1" references the file ++ ".*/shouldNotRemain1/\.\./lib/(libfoo1\.a|foo1\.l(ib)?)" ++ but this file does not exist\. Possible reasons include: ++ \* The file was deleted, renamed, or moved to another location\. ++ \* An install or uninstall procedure did not complete successfully\. ++ \* The installation package was faulty and contained ++ ".*/Tests/RunCMake/install/CMP0177-WARN-build/root-all/lib/cmake/pkg/pkg-config\.cmake" ++ but not all the files it references\. ++Call Stack \(most recent call first\): + CMP0177-WARN-verify\.cmake:2 \(find_package\) + CMakeLists\.txt:3 \(include\) diff --git a/Tests/RunCMake/install/CMP0177-WARN-verify.cmake b/Tests/RunCMake/install/CMP0177-WARN-verify.cmake new file mode 100644 index 0000000000..7b8b2dcfa6 --- /dev/null +++ b/Tests/RunCMake/install/CMP0177-WARN-verify.cmake @@ -0,0 +1,2 @@ +enable_language(C) +find_package(pkg REQUIRED) diff --git a/Tests/RunCMake/install/CMP0177-WARN.cmake b/Tests/RunCMake/install/CMP0177-WARN.cmake new file mode 100644 index 0000000000..5bad118b93 --- /dev/null +++ b/Tests/RunCMake/install/CMP0177-WARN.cmake @@ -0,0 +1 @@ +include(${CMAKE_CURRENT_LIST_DIR}/CMP0177.cmake) diff --git a/Tests/RunCMake/install/CMP0177.cmake b/Tests/RunCMake/install/CMP0177.cmake new file mode 100644 index 0000000000..176367604d --- /dev/null +++ b/Tests/RunCMake/install/CMP0177.cmake @@ -0,0 +1,29 @@ +enable_language(C) + +add_library(foo1 STATIC obj1.c) +add_library(foo2 STATIC obj2.c) + +set_target_properties(foo2 PROPERTIES HIDDEN_DOUBLE_DOT "..") + +# All the shouldNotRemainX path components below should be normalized out when +# CMP0177 is set to NEW, and retained for OLD and WARN. + +install(TARGETS foo1 + EXPORT pkg + ARCHIVE DESTINATION shouldNotRemain1/../lib +) +install(TARGETS foo2 + EXPORT pkg + ARCHIVE DESTINATION shouldNotRemain2/$/lib +) +install(EXPORT pkg + DESTINATION shouldNotRemain3/deeper/../.././lib/cmake/pkg + FILE pkg-config.cmake +) +install(FILES obj1.c + DESTINATION shouldNotRemain4/anotherSubdir/../../files +) +install(DIRECTORY dir + # Trailing slash here is significant + DESTINATION shouldNotRemain5/../dirs/more/../ +) diff --git a/Tests/RunCMake/install/EXPORT-TargetTwice-pkg1.txt b/Tests/RunCMake/install/EXPORT-TargetTwice-pkg1.txt deleted file mode 100644 index 592f79c688..0000000000 --- a/Tests/RunCMake/install/EXPORT-TargetTwice-pkg1.txt +++ /dev/null @@ -1,5 +0,0 @@ -.+ -set_target_properties\(pkg1::foo PROPERTIES -.+INTERFACE_INCLUDE_DIRECTORIES "\${_IMPORT_PREFIX}/pkg1/inc" -\) -.+ diff --git a/Tests/RunCMake/install/EXPORT-TargetTwice-pkg2.txt b/Tests/RunCMake/install/EXPORT-TargetTwice-pkg2.txt deleted file mode 100644 index ebfc43e344..0000000000 --- a/Tests/RunCMake/install/EXPORT-TargetTwice-pkg2.txt +++ /dev/null @@ -1,5 +0,0 @@ -.+ -set_target_properties\(pkg2::foo PROPERTIES -.+INTERFACE_INCLUDE_DIRECTORIES "\${_IMPORT_PREFIX}/pkg2/inc" -\) -.+ diff --git a/Tests/RunCMake/install/RunCMakeTest.cmake b/Tests/RunCMake/install/RunCMakeTest.cmake index 295922c0d3..39c3373dd4 100644 --- a/Tests/RunCMake/install/RunCMakeTest.cmake +++ b/Tests/RunCMake/install/RunCMakeTest.cmake @@ -7,7 +7,9 @@ function(run_install_test case) set(RunCMake_TEST_NO_CLEAN 1) file(REMOVE_RECURSE "${RunCMake_TEST_BINARY_DIR}") file(MAKE_DIRECTORY "${RunCMake_TEST_BINARY_DIR}") + set(RunCMake_TEST_RAW_ARGS -DCMAKE_BUILD_TYPE:STRING=Debug) run_cmake(${case}) + unset(RunCMake_TEST_RAW_ARGS) set(RunCMake_TEST_OUTPUT_MERGE 1) run_cmake_command(${case}-build ${CMAKE_COMMAND} --build . --config Debug) unset(RunCMake_TEST_OUTPUT_MERGE) @@ -92,6 +94,12 @@ run_cmake(CMP0062-WARN) run_cmake(CMP0087-OLD) run_cmake(CMP0087-NEW) run_cmake(CMP0087-WARN) +foreach(policy IN ITEMS NEW OLD WARN) + run_install_test(CMP0177-${policy}) + run_cmake_with_options(CMP0177-${policy}-verify + -DCMAKE_PREFIX_PATH=${RunCMake_BINARY_DIR}/CMP0177-${policy}-build/root-all + ) +endforeach() run_cmake(TARGETS-ImportedGlobal) run_cmake(TARGETS-NAMELINK_COMPONENT-bad-all) run_cmake(TARGETS-NAMELINK_COMPONENT-bad-exc)