Browse Source

Merge topic 'cmake-default-dir-install-permissions'

7e896029 CPack: enable setting default dir creation permissions
a4c82916 CPack test: expand output checking fallback
670ad047 Move file/dir permissions code to common file.
deeba85f CMake: enable setting default dir creation permissions

Acked-by: Kitware Robot <[email protected]>
Merge-request: !1433
Brad King 8 năm trước cách đây
mục cha
commit
05d8892e61

+ 2 - 0
Help/manual/cmake-variables.7.rst

@@ -159,6 +159,7 @@ Variables that Change Behavior
    /variable/CMAKE_INCLUDE_DIRECTORIES_PROJECT_BEFORE
    /variable/CMAKE_INCLUDE_PATH
    /variable/CMAKE_INSTALL_DEFAULT_COMPONENT_NAME
+   /variable/CMAKE_INSTALL_DEFAULT_DIRECTORY_PERMISSIONS
    /variable/CMAKE_INSTALL_MESSAGE
    /variable/CMAKE_INSTALL_PREFIX
    /variable/CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT
@@ -529,6 +530,7 @@ Variables for CPack
    /variable/CPACK_COMPONENT_INCLUDE_TOPLEVEL_DIRECTORY
    /variable/CPACK_ERROR_ON_ABSOLUTE_INSTALL_DESTINATION
    /variable/CPACK_INCLUDE_TOPLEVEL_DIRECTORY
+   /variable/CPACK_INSTALL_DEFAULT_DIRECTORY_PERMISSIONS
    /variable/CPACK_INSTALL_SCRIPT
    /variable/CPACK_PACKAGING_INSTALL_PREFIX
    /variable/CPACK_SET_DESTDIR

+ 12 - 0
Help/release/dev/cmake-default-dir-install-permissions.rst

@@ -0,0 +1,12 @@
+cmake-default-dir-install-permissions
+-------------------------------------
+
+* The :variable:`CMAKE_INSTALL_DEFAULT_DIRECTORY_PERMISSIONS` variable was added
+  to enable setting of default permissions for directories created implicitly
+  during installation of files by :command:`install` and
+  :command:`file(INSTALL)`.
+
+* The :variable:`CPACK_INSTALL_DEFAULT_DIRECTORY_PERMISSIONS` variable was added
+  which serves the same purpose during packaging as the
+  :variable:`CMAKE_INSTALL_DEFAULT_DIRECTORY_PERMISSIONS` variable serves during
+  installation (e.g. ``make install``).

+ 29 - 0
Help/variable/CMAKE_INSTALL_DEFAULT_DIRECTORY_PERMISSIONS.rst

@@ -0,0 +1,29 @@
+CMAKE_INSTALL_DEFAULT_DIRECTORY_PERMISSIONS
+-------------------------------------------
+
+Default permissions for directories created implicitly during installation
+of files by :command:`install` and :command:`file(INSTALL)`.
+
+If ``make install`` is invoked and directories are implicitly created they
+get permissions set by :variable:`CMAKE_INSTALL_DEFAULT_DIRECTORY_PERMISSIONS`
+variable or platform specific default permissions if the variable is not set.
+
+Implicitly created directories are created if they are not explicitly installed
+by :command:`install` command but are needed to install a file on a certain
+path. Example of such locations are directories created due to the setting of
+:variable:`CMAKE_INSTALL_PREFIX`.
+
+Expected content of the :variable:`CMAKE_INSTALL_DEFAULT_DIRECTORY_PERMISSIONS`
+variable is a list of permissions that can be used by :command:`install` command
+`PERMISSIONS` section.
+
+Example usage:
+
+::
+
+ set(CMAKE_INSTALL_DEFAULT_DIRECTORY_PERMISSIONS
+      OWNER_READ
+      OWNER_WRITE
+      OWNER_EXECUTE
+      GROUP_READ
+    )

+ 11 - 0
Help/variable/CPACK_INSTALL_DEFAULT_DIRECTORY_PERMISSIONS.rst

@@ -0,0 +1,11 @@
+CPACK_INSTALL_DEFAULT_DIRECTORY_PERMISSIONS
+-------------------------------------------
+
+Default permissions for implicitly created directories during packaging.
+
+This variable serves the same purpose during packaging as the
+:variable:`CMAKE_INSTALL_DEFAULT_DIRECTORY_PERMISSIONS` variable
+serves during installation (e.g. ``make install``).
+
+If `include(CPack)` is used then by default this variable is set to the content
+of :variable:`CMAKE_INSTALL_DEFAULT_DIRECTORY_PERMISSIONS`.

+ 6 - 0
Modules/CPack.cmake

@@ -384,6 +384,12 @@ _cpack_set_default(CPACK_RESOURCE_FILE_WELCOME
 
 _cpack_set_default(CPACK_MODULE_PATH "${CMAKE_MODULE_PATH}")
 
+# Set default directory creation permissions mode
+if(CMAKE_INSTALL_DEFAULT_DIRECTORY_PERMISSIONS)
+  _cpack_set_default(CPACK_INSTALL_DEFAULT_DIRECTORY_PERMISSIONS
+    "${CMAKE_INSTALL_DEFAULT_DIRECTORY_PERMISSIONS}")
+endif()
+
 if(CPACK_NSIS_ENABLE_UNINSTALL_BEFORE_INSTALL)
   set(CPACK_NSIS_ENABLE_UNINSTALL_BEFORE_INSTALL ON)
 endif()

+ 2 - 0
Source/CMakeLists.txt

@@ -216,6 +216,8 @@ set(SRCS
   cmFileTimeComparison.cxx
   cmFileTimeComparison.h
   cmFortranParserImpl.cxx
+  cmFSPermissions.cxx
+  cmFSPermissions.h
   cmGeneratedFileStream.cxx
   cmGeneratorExpressionContext.cxx
   cmGeneratorExpressionContext.h

+ 44 - 10
Source/CPack/cmCPackGenerator.cxx

@@ -12,6 +12,7 @@
 #include "cmCPackComponentGroup.h"
 #include "cmCPackLog.h"
 #include "cmCryptoHash.h"
+#include "cmFSPermissions.h"
 #include "cmGeneratedFileStream.h"
 #include "cmGlobalGenerator.h"
 #include "cmMakefile.h"
@@ -201,6 +202,29 @@ int cmCPackGenerator::InstallProject()
     cmSystemTools::PutEnv("DESTDIR=");
   }
 
+  // prepare default created directory permissions
+  mode_t default_dir_mode_v = 0;
+  mode_t* default_dir_mode = nullptr;
+  const char* default_dir_install_permissions =
+    this->GetOption("CPACK_INSTALL_DEFAULT_DIRECTORY_PERMISSIONS");
+  if (default_dir_install_permissions && *default_dir_install_permissions) {
+    std::vector<std::string> items;
+    cmSystemTools::ExpandListArgument(default_dir_install_permissions, items);
+    for (const auto& arg : items) {
+      if (!cmFSPermissions::stringToModeT(arg, default_dir_mode_v)) {
+        cmCPackLogger(cmCPackLog::LOG_ERROR, "Invalid permission value '"
+                        << arg
+                        << "'."
+                           " CPACK_INSTALL_DEFAULT_DIRECTORY_PERMISSIONS "
+                           "value is invalid."
+                        << std::endl);
+        return 0;
+      }
+    }
+
+    default_dir_mode = &default_dir_mode_v;
+  }
+
   // If the CPackConfig file sets CPACK_INSTALL_COMMANDS then run them
   // as listed
   if (!this->InstallProjectViaInstallCommands(setDestDir,
@@ -218,15 +242,15 @@ int cmCPackGenerator::InstallProject()
   // If the CPackConfig file sets CPACK_INSTALLED_DIRECTORIES
   // then glob it and copy it to CPACK_TEMPORARY_DIRECTORY
   // This is used in Source packaging
-  if (!this->InstallProjectViaInstalledDirectories(setDestDir,
-                                                   tempInstallDirectory)) {
+  if (!this->InstallProjectViaInstalledDirectories(
+        setDestDir, tempInstallDirectory, default_dir_mode)) {
     return 0;
   }
 
   // If the project is a CMAKE project then run pre-install
   // and then read the cmake_install script to run it
-  if (!this->InstallProjectViaInstallCMakeProjects(setDestDir,
-                                                   bareTempInstallDirectory)) {
+  if (!this->InstallProjectViaInstallCMakeProjects(
+        setDestDir, bareTempInstallDirectory, default_dir_mode)) {
     return 0;
   }
 
@@ -274,7 +298,8 @@ int cmCPackGenerator::InstallProjectViaInstallCommands(
 }
 
 int cmCPackGenerator::InstallProjectViaInstalledDirectories(
-  bool setDestDir, const std::string& tempInstallDirectory)
+  bool setDestDir, const std::string& tempInstallDirectory,
+  const mode_t* default_dir_mode)
 {
   (void)setDestDir;
   (void)tempInstallDirectory;
@@ -385,7 +410,8 @@ int cmCPackGenerator::InstallProjectViaInstalledDirectories(
           // make sure directory exists for symlink
           std::string destDir =
             cmSystemTools::GetFilenamePath(symlinked.second);
-          if (!destDir.empty() && !cmSystemTools::MakeDirectory(destDir)) {
+          if (!destDir.empty() &&
+              !cmSystemTools::MakeDirectory(destDir, default_dir_mode)) {
             cmCPackLogger(cmCPackLog::LOG_ERROR, "Cannot create dir: "
                             << destDir << "\nTrying to create symlink: "
                             << symlinked.second << "--> " << symlinked.first
@@ -464,7 +490,8 @@ int cmCPackGenerator::InstallProjectViaInstallScript(
 }
 
 int cmCPackGenerator::InstallProjectViaInstallCMakeProjects(
-  bool setDestDir, const std::string& baseTempInstallDirectory)
+  bool setDestDir, const std::string& baseTempInstallDirectory,
+  const mode_t* default_dir_mode)
 {
   const char* cmakeProjects = this->GetOption("CPACK_INSTALL_CMAKE_PROJECTS");
   const char* cmakeGenerator = this->GetOption("CPACK_CMAKE_GENERATOR");
@@ -631,6 +658,13 @@ int cmCPackGenerator::InstallProjectViaInstallCMakeProjects(
           }
         }
 
+        const char* default_dir_inst_permissions =
+          this->GetOption("CPACK_INSTALL_DEFAULT_DIRECTORY_PERMISSIONS");
+        if (default_dir_inst_permissions && *default_dir_inst_permissions) {
+          mf.AddDefinition("CMAKE_INSTALL_DEFAULT_DIRECTORY_PERMISSIONS",
+                           default_dir_inst_permissions);
+        }
+
         if (!setDestDir) {
           tempInstallDirectory += this->GetPackagingInstallPrefix();
         }
@@ -690,7 +724,7 @@ int cmCPackGenerator::InstallProjectViaInstallCMakeProjects(
           cmCPackLogger(cmCPackLog::LOG_DEBUG, "- Creating directory: '"
                           << dir << "'" << std::endl);
 
-          if (!cmsys::SystemTools::MakeDirectory(dir.c_str())) {
+          if (!cmsys::SystemTools::MakeDirectory(dir, default_dir_mode)) {
             cmCPackLogger(
               cmCPackLog::LOG_ERROR,
               "Problem creating temporary directory: " << dir << std::endl);
@@ -700,8 +734,8 @@ int cmCPackGenerator::InstallProjectViaInstallCMakeProjects(
           mf.AddDefinition("CMAKE_INSTALL_PREFIX",
                            tempInstallDirectory.c_str());
 
-          if (!cmsys::SystemTools::MakeDirectory(
-                tempInstallDirectory.c_str())) {
+          if (!cmsys::SystemTools::MakeDirectory(tempInstallDirectory,
+                                                 default_dir_mode)) {
             cmCPackLogger(cmCPackLog::LOG_ERROR,
                           "Problem creating temporary directory: "
                             << tempInstallDirectory << std::endl);

+ 5 - 2
Source/CPack/cmCPackGenerator.h

@@ -12,6 +12,7 @@
 
 #include "cmCPackComponentGroup.h"
 #include "cmSystemTools.h"
+#include "cm_sys_stat.h"
 
 class cmCPackLog;
 class cmInstalledFile;
@@ -168,9 +169,11 @@ protected:
   virtual int InstallProjectViaInstallScript(
     bool setDestDir, const std::string& tempInstallDirectory);
   virtual int InstallProjectViaInstalledDirectories(
-    bool setDestDir, const std::string& tempInstallDirectory);
+    bool setDestDir, const std::string& tempInstallDirectory,
+    const mode_t* default_dir_mode);
   virtual int InstallProjectViaInstallCMakeProjects(
-    bool setDestDir, const std::string& tempInstallDirectory);
+    bool setDestDir, const std::string& tempInstallDirectory,
+    const mode_t* default_dir_mode);
 
   /**
    * The various level of support of

+ 34 - 0
Source/cmFSPermissions.cxx

@@ -0,0 +1,34 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+#include "cmFSPermissions.h"
+
+bool cmFSPermissions::stringToModeT(std::string const& arg,
+                                    mode_t& permissions)
+{
+  if (arg == "OWNER_READ") {
+    permissions |= mode_owner_read;
+  } else if (arg == "OWNER_WRITE") {
+    permissions |= mode_owner_write;
+  } else if (arg == "OWNER_EXECUTE") {
+    permissions |= mode_owner_execute;
+  } else if (arg == "GROUP_READ") {
+    permissions |= mode_group_read;
+  } else if (arg == "GROUP_WRITE") {
+    permissions |= mode_group_write;
+  } else if (arg == "GROUP_EXECUTE") {
+    permissions |= mode_group_execute;
+  } else if (arg == "WORLD_READ") {
+    permissions |= mode_world_read;
+  } else if (arg == "WORLD_WRITE") {
+    permissions |= mode_world_write;
+  } else if (arg == "WORLD_EXECUTE") {
+    permissions |= mode_world_execute;
+  } else if (arg == "SETUID") {
+    permissions |= mode_setuid;
+  } else if (arg == "SETGID") {
+    permissions |= mode_setgid;
+  } else {
+    return false;
+  }
+  return true;
+}

+ 45 - 0
Source/cmFSPermissions.h

@@ -0,0 +1,45 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+#ifndef cmFSPermissions_h
+#define cmFSPermissions_h
+
+#include "cmConfigure.h" // IWYU pragma: keep
+
+#include "cm_sys_stat.h"
+
+#include <string>
+
+namespace cmFSPermissions {
+
+// Table of permissions flags.
+#if defined(_WIN32) && !defined(__CYGWIN__)
+static const mode_t mode_owner_read = S_IREAD;
+static const mode_t mode_owner_write = S_IWRITE;
+static const mode_t mode_owner_execute = S_IEXEC;
+static const mode_t mode_group_read = 040;
+static const mode_t mode_group_write = 020;
+static const mode_t mode_group_execute = 010;
+static const mode_t mode_world_read = 04;
+static const mode_t mode_world_write = 02;
+static const mode_t mode_world_execute = 01;
+static const mode_t mode_setuid = 04000;
+static const mode_t mode_setgid = 02000;
+#else
+static const mode_t mode_owner_read = S_IRUSR;
+static const mode_t mode_owner_write = S_IWUSR;
+static const mode_t mode_owner_execute = S_IXUSR;
+static const mode_t mode_group_read = S_IRGRP;
+static const mode_t mode_group_write = S_IWGRP;
+static const mode_t mode_group_execute = S_IXGRP;
+static const mode_t mode_world_read = S_IROTH;
+static const mode_t mode_world_write = S_IWOTH;
+static const mode_t mode_world_execute = S_IXOTH;
+static const mode_t mode_setuid = S_ISUID;
+static const mode_t mode_setgid = S_ISGID;
+#endif
+
+bool stringToModeT(std::string const& arg, mode_t& permissions);
+
+} // ns
+
+#endif

+ 25 - 50
Source/cmFileCommand.cxx

@@ -20,6 +20,7 @@
 #include "cmAlgorithms.h"
 #include "cmCommandArgumentsHelper.h"
 #include "cmCryptoHash.h"
+#include "cmFSPermissions.h"
 #include "cmFileLockPool.h"
 #include "cmFileTimeComparison.h"
 #include "cmGeneratorExpression.h"
@@ -50,32 +51,7 @@
 
 class cmSystemToolsFileTime;
 
-// Table of permissions flags.
-#if defined(_WIN32) && !defined(__CYGWIN__)
-static mode_t mode_owner_read = S_IREAD;
-static mode_t mode_owner_write = S_IWRITE;
-static mode_t mode_owner_execute = S_IEXEC;
-static mode_t mode_group_read = 040;
-static mode_t mode_group_write = 020;
-static mode_t mode_group_execute = 010;
-static mode_t mode_world_read = 04;
-static mode_t mode_world_write = 02;
-static mode_t mode_world_execute = 01;
-static mode_t mode_setuid = 04000;
-static mode_t mode_setgid = 02000;
-#else
-static mode_t mode_owner_read = S_IRUSR;
-static mode_t mode_owner_write = S_IWUSR;
-static mode_t mode_owner_execute = S_IXUSR;
-static mode_t mode_group_read = S_IRGRP;
-static mode_t mode_group_write = S_IWGRP;
-static mode_t mode_group_execute = S_IXGRP;
-static mode_t mode_world_read = S_IROTH;
-static mode_t mode_world_write = S_IWOTH;
-static mode_t mode_world_execute = S_IXOTH;
-static mode_t mode_setuid = S_ISUID;
-static mode_t mode_setgid = S_ISGID;
-#endif
+using namespace cmFSPermissions;
 
 #if defined(_WIN32)
 // libcurl doesn't support file:// urls for unicode filenames on Windows.
@@ -1099,29 +1075,7 @@ protected:
   // Translate an argument to a permissions bit.
   bool CheckPermissions(std::string const& arg, mode_t& permissions)
   {
-    if (arg == "OWNER_READ") {
-      permissions |= mode_owner_read;
-    } else if (arg == "OWNER_WRITE") {
-      permissions |= mode_owner_write;
-    } else if (arg == "OWNER_EXECUTE") {
-      permissions |= mode_owner_execute;
-    } else if (arg == "GROUP_READ") {
-      permissions |= mode_group_read;
-    } else if (arg == "GROUP_WRITE") {
-      permissions |= mode_group_write;
-    } else if (arg == "GROUP_EXECUTE") {
-      permissions |= mode_group_execute;
-    } else if (arg == "WORLD_READ") {
-      permissions |= mode_world_read;
-    } else if (arg == "WORLD_WRITE") {
-      permissions |= mode_world_write;
-    } else if (arg == "WORLD_EXECUTE") {
-      permissions |= mode_world_execute;
-    } else if (arg == "SETUID") {
-      permissions |= mode_setuid;
-    } else if (arg == "SETGID") {
-      permissions |= mode_setgid;
-    } else {
+    if (!cmFSPermissions::stringToModeT(arg, permissions)) {
       std::ostringstream e;
       e << this->Name << " given invalid permission \"" << arg << "\".";
       this->FileCommand->SetError(e.str());
@@ -2015,9 +1969,30 @@ bool cmFileInstaller::HandleInstallDestination()
     this->DestDirLength = int(sdestdir.size());
   }
 
+  // check if default dir creation permissions were set
+  mode_t default_dir_mode_v = 0;
+  mode_t* default_dir_mode = nullptr;
+  const char* default_dir_install_permissions = this->Makefile->GetDefinition(
+    "CMAKE_INSTALL_DEFAULT_DIRECTORY_PERMISSIONS");
+  if (default_dir_install_permissions && *default_dir_install_permissions) {
+    std::vector<std::string> items;
+    cmSystemTools::ExpandListArgument(default_dir_install_permissions, items);
+    for (const auto& arg : items) {
+      if (!this->CheckPermissions(arg, default_dir_mode_v)) {
+        std::ostringstream e;
+        e << this->FileCommand->GetError()
+          << " Set with CMAKE_INSTALL_DEFAULT_DIRECTORY_PERMISSIONS variable.";
+        this->FileCommand->SetError(e.str());
+        return false;
+      }
+    }
+
+    default_dir_mode = &default_dir_mode_v;
+  }
+
   if (this->InstallType != cmInstallType_DIRECTORY) {
     if (!cmSystemTools::FileExists(destination.c_str())) {
-      if (!cmSystemTools::MakeDirectory(destination.c_str())) {
+      if (!cmSystemTools::MakeDirectory(destination, default_dir_mode)) {
         std::string errstring = "cannot create directory: " + destination +
           ". Maybe need administrative privileges.";
         this->FileCommand->SetError(errstring);

+ 14 - 0
Source/cmLocalGenerator.cxx

@@ -487,6 +487,20 @@ void cmLocalGenerator::GenerateInstallRules()
     /* clang-format on */
   }
 
+  // Write default directory permissions.
+  if (const char* defaultDirPermissions = this->Makefile->GetDefinition(
+        "CMAKE_INSTALL_DEFAULT_DIRECTORY_PERMISSIONS")) {
+    /* clang-format off */
+    fout <<
+      "# Set default install directory permissions.\n"
+      "if(NOT DEFINED CMAKE_INSTALL_DEFAULT_DIRECTORY_PERMISSIONS)\n"
+      "  set(CMAKE_INSTALL_DEFAULT_DIRECTORY_PERMISSIONS \""
+         << defaultDirPermissions << "\")\n"
+      "endif()\n"
+      "\n";
+    /* clang-format on */
+  }
+
   // Ask each install generator to write its code.
   std::vector<cmInstallGenerator*> const& installers =
     this->Makefile->GetInstallGenerators();

+ 2 - 0
Tests/RunCMake/CPack/CPackTestHelpers.cmake

@@ -68,6 +68,8 @@ function(run_cpack_test_common_ TEST_NAME types build SUBTEST_SUFFIX source PACK
         set(RunCMake-std${o}-file "tests/${TEST_NAME}/${TEST_TYPE}-${SUBTEST_SUFFIX}-std${o}.txt")
       elseif(EXISTS ${RunCMake_SOURCE_DIR}/tests/${TEST_NAME}/${TEST_TYPE}-std${o}.txt)
         set(RunCMake-std${o}-file "tests/${TEST_NAME}/${TEST_TYPE}-std${o}.txt")
+      elseif(SUBTEST_SUFFIX AND EXISTS ${RunCMake_SOURCE_DIR}/tests/${TEST_NAME}/${SUBTEST_SUFFIX}-std${o}.txt)
+        set(RunCMake-std${o}-file "tests/${TEST_NAME}/${SUBTEST_SUFFIX}-std${o}.txt")
       elseif(EXISTS ${RunCMake_SOURCE_DIR}/${TEST_TYPE}/default_expected_std${o}.txt)
         set(RunCMake-std${o}-file "${TEST_TYPE}/default_expected_std${o}.txt")
       endif()

+ 1 - 0
Tests/RunCMake/CPack/README.txt

@@ -167,6 +167,7 @@ NOTE: For subtests generator name can also be suffixed with subtest name and/or
         - generator name + packaging type
         - generator name + subtest name
         - generator name
+        - subtest name
         - default generator
       File name format: '<generator_name>-<packaging_type>-<subtest_name>-std<type>.txt'
                         where <type> can either be 'out' or 'err'.

+ 1 - 0
Tests/RunCMake/CPack/RunCMakeTest.cmake

@@ -7,6 +7,7 @@ include("${RunCMake_SOURCE_DIR}/CPackTestHelpers.cmake")
 run_cpack_test(CUSTOM_BINARY_SPEC_FILE "RPM" false "MONOLITHIC;COMPONENT")
 run_cpack_test(CUSTOM_NAMES "RPM;DEB;TGZ" true "COMPONENT")
 run_cpack_test(DEBUGINFO "RPM" true "COMPONENT")
+run_cpack_test_subtests(DEFAULT_PERMISSIONS "CMAKE_var_set;CPACK_var_set;both_set;invalid_CMAKE_var;invalid_CPACK_var" "RPM;DEB" false "MONOLITHIC;COMPONENT")
 run_cpack_test(DEPENDENCIES "RPM;DEB" true "COMPONENT")
 run_cpack_test(DIST "RPM" false "MONOLITHIC")
 run_cpack_test(EMPTY_DIR "RPM;DEB;TGZ" true "MONOLITHIC;COMPONENT")

+ 6 - 0
Tests/RunCMake/CPack/tests/DEFAULT_PERMISSIONS/ExpectedFiles.cmake

@@ -0,0 +1,6 @@
+if(${RunCMake_SUBTEST_SUFFIX} MATCHES "invalid_.*_var")
+  set(EXPECTED_FILES_COUNT "0")
+else()
+  set(EXPECTED_FILES_COUNT "1")
+  set(EXPECTED_FILE_CONTENT_1_LIST "/usr;/usr/foo;/usr/foo/CMakeLists.txt")
+endif()

+ 39 - 0
Tests/RunCMake/CPack/tests/DEFAULT_PERMISSIONS/VerifyResult.cmake

@@ -0,0 +1,39 @@
+if(NOT ${RunCMake_SUBTEST_SUFFIX} MATCHES "invalid_.*_var")
+  if(GENERATOR_TYPE STREQUAL "RPM")
+    function(checkContentPermissions_ FILE REGEX)
+      execute_process(COMMAND ${RPM_EXECUTABLE} -qp --dump ${FILE}
+                  WORKING_DIRECTORY "${CPACK_TEMPORARY_DIRECTORY}"
+                  OUTPUT_VARIABLE PERMISSIONS_
+                  ERROR_QUIET
+                  OUTPUT_STRIP_TRAILING_WHITESPACE)
+
+      if(NOT PERMISSIONS_ MATCHES "${REGEX}")
+        message(FATAL_ERROR "Permissions in '${FILE}'. Permissions: '${PERMISSIONS_}'")
+      endif()
+    endfunction()
+
+    if(${RunCMake_SUBTEST_SUFFIX} MATCHES "CMAKE_var_set")
+      checkContentPermissions_("${FOUND_FILE_1}"
+        "/usr/foo .*740 root root.*")
+    else()
+      checkContentPermissions_("${FOUND_FILE_1}"
+        "/usr/foo .*700 root root.*")
+    endif()
+  else() # DEB
+    function(checkContentPermissions_ FILE REGEX)
+      getPackageContent("${FILE}" PERMISSIONS_)
+
+      if(NOT PERMISSIONS_ MATCHES "${REGEX}")
+        message(FATAL_ERROR "Permissions in '${FILE}'. Permissions: '${PERMISSIONS_}'")
+      endif()
+    endfunction()
+
+    if(${RunCMake_SUBTEST_SUFFIX} MATCHES "CMAKE_var_set")
+      checkContentPermissions_("${FOUND_FILE_1}"
+        "drwxr----- root/root .* ./usr/\ndrwxr----- root/root .* ./usr/foo/\n.*")
+    else()
+      checkContentPermissions_("${FOUND_FILE_1}"
+        "drwx------ root/root .* ./usr/\ndrwx------ root/root .* ./usr/foo/\n.*")
+    endif()
+  endif()
+endif()

+ 1 - 0
Tests/RunCMake/CPack/tests/DEFAULT_PERMISSIONS/invalid_CMAKE_var-stderr.txt

@@ -0,0 +1 @@
+.*CPACK_INSTALL_DEFAULT_DIRECTORY_PERMISSIONS value is invalid.*

+ 1 - 0
Tests/RunCMake/CPack/tests/DEFAULT_PERMISSIONS/invalid_CPACK_var-stderr.txt

@@ -0,0 +1 @@
+.*CPACK_INSTALL_DEFAULT_DIRECTORY_PERMISSIONS value is invalid.*

+ 34 - 0
Tests/RunCMake/CPack/tests/DEFAULT_PERMISSIONS/test.cmake

@@ -0,0 +1,34 @@
+if(${RunCMake_SUBTEST_SUFFIX} MATCHES "CMAKE_var_set" OR
+  ${RunCMake_SUBTEST_SUFFIX} MATCHES "both_set")
+
+  set(CMAKE_INSTALL_DEFAULT_DIRECTORY_PERMISSIONS
+      OWNER_READ
+      OWNER_WRITE
+      OWNER_EXECUTE
+      GROUP_READ
+    )
+endif()
+
+if(${RunCMake_SUBTEST_SUFFIX} MATCHES "invalid_CMAKE_var")
+  list(APPEND CMAKE_INSTALL_DEFAULT_DIRECTORY_PERMISSIONS "INVALID")
+endif()
+
+if(${RunCMake_SUBTEST_SUFFIX} MATCHES "CPACK_var_set" OR
+  ${RunCMake_SUBTEST_SUFFIX} MATCHES "both_set")
+
+  set(CPACK_INSTALL_DEFAULT_DIRECTORY_PERMISSIONS
+      OWNER_READ
+      OWNER_WRITE
+      OWNER_EXECUTE
+    )
+endif()
+
+if(${RunCMake_SUBTEST_SUFFIX} MATCHES "invalid_CPACK_var")
+  list(APPEND CPACK_INSTALL_DEFAULT_DIRECTORY_PERMISSIONS "INVALID")
+endif()
+
+install(FILES CMakeLists.txt DESTINATION foo COMPONENT test)
+
+if(PACKAGING_TYPE STREQUAL "COMPONENT")
+  set(CPACK_COMPONENTS_ALL test)
+endif()

+ 1 - 0
bootstrap

@@ -311,6 +311,7 @@ CMAKE_CXX_SOURCES="\
   cmFindProgramCommand \
   cmForEachCommand \
   cmFunctionCommand \
+  cmFSPermissions \
   cmGeneratedFileStream \
   cmGeneratorExpression \
   cmGeneratorExpressionContext \