فهرست منبع

install: Fix bugs around empty directories

The conversion between internal list representations led to empty
directories being left behind when processing generator expressions in
the destination. This ensures the intended behavior of creating a
destination directory but installing nothing into it when no <dirs> are
specified, regardless of the presence or absence of genex.

Further, an explicit empty string <dir> (via `""`, or an undefined
variable) led to the current source directory being expanded as a file
to install, leading to the generation of an infinitely recursive
install command. Disallow this behavior, forcing projects to explicitly
specify the current source directory if that is their intention.

Fixes: #27568
Tyler Yankee 2 هفته پیش
والد
کامیت
4e7e6928cb

+ 3 - 2
Source/cmInstallCommand.cxx

@@ -1791,10 +1791,11 @@ bool HandleDirectoryMode(std::vector<std::string> const& args,
       exclude_from_all = true;
       doing = DoingNone;
     } else if (doing == DoingDirs) {
-      // Convert this directory to a full path.
+      // If the given directory is not a full path, convert it to one by
+      // assuming it's relative to the current source directory.
       std::string dir = args[i];
       std::string::size_type gpos = cmGeneratorExpression::Find(dir);
-      if (gpos != 0 && !cmSystemTools::FileIsFullPath(dir)) {
+      if (!dir.empty() && gpos != 0 && !cmSystemTools::FileIsFullPath(dir)) {
         dir =
           cmStrCat(helper.Makefile->GetCurrentSourceDirectory(), '/', args[i]);
       }

+ 15 - 5
Source/cmInstallDirectoryGenerator.cxx

@@ -2,6 +2,7 @@
    file LICENSE.rst or https://cmake.org/licensing for details.  */
 #include "cmInstallDirectoryGenerator.h"
 
+#include <algorithm>
 #include <utility>
 
 #include "cmGeneratorExpression.h"
@@ -54,6 +55,13 @@ bool cmInstallDirectoryGenerator::Compute(cmLocalGenerator* lg)
 std::vector<std::string> cmInstallDirectoryGenerator::GetDirectories(
   std::string const& config) const
 {
+  // If given only empty directories, collapse into a single specification to
+  // avoid redundant calls. This supports the use case of installing an empty
+  // directory into a destination when a directory is not specified.
+  if (std::all_of(this->Directories.begin(), this->Directories.end(),
+                  [](std::string const& d) { return d.empty(); })) {
+    return std::vector<std::string>{ "" };
+  }
   cmList directories;
   if (this->ActionsPerConfig) {
     for (std::string const& f : this->Directories) {
@@ -81,11 +89,13 @@ void cmInstallDirectoryGenerator::GenerateScriptForConfig(
 {
   std::vector<std::string> dirs = this->GetDirectories(config);
 
-  // Make sure all dirs have absolute paths.
-  cmMakefile const& mf = *this->LocalGenerator->GetMakefile();
-  for (std::string& d : dirs) {
-    if (!cmSystemTools::FileIsFullPath(d)) {
-      d = cmStrCat(mf.GetCurrentSourceDirectory(), '/', d);
+  if (!(dirs.size() == 1 && dirs[0].empty())) {
+    // Make sure all dirs have absolute paths.
+    cmMakefile const& mf = *this->LocalGenerator->GetMakefile();
+    for (std::string& d : dirs) {
+      if (!cmSystemTools::FileIsFullPath(d)) {
+        d = cmStrCat(mf.GetCurrentSourceDirectory(), '/', d);
+      }
     }
   }
 

+ 1 - 1
Tests/RunCMake/install/DIRECTORY-PATTERN-all-check.cmake

@@ -1 +1 @@
-check_installed([[^dir1;dir1/empty\.c;dir1/empty\.h;dir2;dir2/pattern;dir2/pattern/empty\.txt;dir3;dir3/empty\.c;dir3/empty\.h;dir3/empty\.txt;dir4;dir4/empty\.c;dir4/empty\.h;dir4/empty\.txt;empty$]])
+check_installed([[^dir1;dir1/empty\.c;dir1/empty\.h;dir2;dir2/pattern;dir2/pattern/empty\.txt;dir3;dir3/empty\.c;dir3/empty\.h;dir3/empty\.txt;dir4;dir4/empty\.c;dir4/empty\.h;dir4/empty\.txt;empty;empty_str;empty_with_genex$]])

+ 10 - 0
Tests/RunCMake/install/DIRECTORY-PATTERN.cmake

@@ -34,3 +34,13 @@ install(
   DIRECTORY
   DESTINATION empty
   )
+
+install(
+  DIRECTORY "" ""
+  DESTINATION empty_str
+  )
+
+install(
+  DIRECTORY
+  DESTINATION $<LOWER_CASE:EMPTY_WITH_GENEX>
+  )