Browse Source

CPack: enable setting default dir creation permissions

Introduces CPACK_DEFAULT_DIRECTORY_INSTALL_PERMISSIONS
variable which adds support for functionality introduced
by CMAKE_DEFAULT_DIRECTORY_INSTALL_PERMISSIONS variable.

Fixes #17333

# Conflicts:
#	Help/release/dev/cmake-default-dir-install-permissions.rst
Domen Vrankar 8 years ago
parent
commit
7e896029cd

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

@@ -530,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

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

@@ -5,3 +5,8 @@ cmake-default-dir-install-permissions
   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``).

+ 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()

+ 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

+ 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()