Browse Source

Merge topic 'issue-15653'

c3385dd8bd file(GENERATE): Support options to manipulate file permissions

Acked-by: Kitware Robot <[email protected]>
Merge-request: !5437
Brad King 5 years ago
parent
commit
3cf1a8c59b
29 changed files with 339 additions and 18 deletions
  1. 14 1
      Help/command/file.rst
  2. 6 0
      Help/release/dev/file-generate-permissions.rst
  3. 73 9
      Source/cmFileCommand.cxx
  4. 8 4
      Source/cmGeneratorExpressionEvaluationFile.cxx
  5. 3 1
      Source/cmGeneratorExpressionEvaluationFile.h
  6. 3 2
      Source/cmMakefile.cxx
  7. 1 1
      Source/cmMakefile.h
  8. 15 0
      Tests/RunCMake/File_Generate/CustomFilePermissions.cmake
  9. 36 0
      Tests/RunCMake/File_Generate/CustomFilePermissionsVerify.cmake
  10. 10 0
      Tests/RunCMake/File_Generate/NoSourcePermissions.cmake
  11. 36 0
      Tests/RunCMake/File_Generate/NoSourcePermissionsVerify.cmake
  12. 21 0
      Tests/RunCMake/File_Generate/RunCMakeTest.cmake
  13. 1 0
      Tests/RunCMake/File_Generate/SourcePermissions1-result.txt
  14. 5 0
      Tests/RunCMake/File_Generate/SourcePermissions1-stderr.txt
  15. 5 0
      Tests/RunCMake/File_Generate/SourcePermissions1.cmake
  16. 1 0
      Tests/RunCMake/File_Generate/SourcePermissions2-result.txt
  17. 5 0
      Tests/RunCMake/File_Generate/SourcePermissions2-stderr.txt
  18. 5 0
      Tests/RunCMake/File_Generate/SourcePermissions2.cmake
  19. 1 0
      Tests/RunCMake/File_Generate/SourcePermissions3-result.txt
  20. 5 0
      Tests/RunCMake/File_Generate/SourcePermissions3-stderr.txt
  21. 5 0
      Tests/RunCMake/File_Generate/SourcePermissions3.cmake
  22. 1 0
      Tests/RunCMake/File_Generate/SourcePermissions4-result.txt
  23. 4 0
      Tests/RunCMake/File_Generate/SourcePermissions4-stderr.txt
  24. 4 0
      Tests/RunCMake/File_Generate/SourcePermissions4.cmake
  25. 1 0
      Tests/RunCMake/File_Generate/SourcePermissions5-result.txt
  26. 4 0
      Tests/RunCMake/File_Generate/SourcePermissions5-stderr.txt
  27. 4 0
      Tests/RunCMake/File_Generate/SourcePermissions5.cmake
  28. 11 0
      Tests/RunCMake/File_Generate/UseSourcePermissions.cmake
  29. 51 0
      Tests/RunCMake/File_Generate/UseSourcePermissionsVerify.cmake

+ 14 - 1
Help/command/file.rst

@@ -479,7 +479,9 @@ modified.
 
   file(GENERATE OUTPUT output-file
        <INPUT input-file|CONTENT content>
-       [CONDITION expression] [TARGET target])
+       [CONDITION expression] [TARGET target]
+       [FILE_PERMISSIONS <permissions>...]
+       [NO_SOURCE_PERMISSIONS] [USE_SOURCE_PERMISSIONS])
 
 Generate an output file for each build configuration supported by the current
 :manual:`CMake Generator <cmake-generators(7)>`.  Evaluate
@@ -520,6 +522,17 @@ from the input content to produce the output content.  The options are:
   require a target for evaluation (e.g. ``$<COMPILE_FEATURES:...>``,
   ``$<TARGET_PROPERTY:prop>``).
 
+``FILE_PERMISSIONS <permissions>...``
+  Use user provided permissions for the generated file.
+
+``NO_SOURCE_PERMISSIONS``
+  The generated file permissions default to the standard 644 value
+  (-rw-r--r--).
+
+``USE_SOURCE_PERMISSIONS``
+  Transfer the file permissions of the original file to the generated file.
+  This option expects INPUT option.
+
 Exactly one ``CONTENT`` or ``INPUT`` option must be given.  A specific
 ``OUTPUT`` file may be named by at most one invocation of ``file(GENERATE)``.
 Generated files are modified and their timestamp updated on subsequent cmake

+ 6 - 0
Help/release/dev/file-generate-permissions.rst

@@ -0,0 +1,6 @@
+file-generate-permissions
+-------------------------
+
+* The :command:`file(GENERATE)` command gained ``NO_SOURCE_PERMISSIONS``,
+  ``USE_SOURCE_PERMISSIONS``, and ``FILE_PERMISSIONS`` options to support
+  permissions of the generated file.

+ 73 - 9
Source/cmFileCommand.cxx

@@ -2291,7 +2291,7 @@ void AddEvaluationFile(const std::string& inputName,
                        const std::string& targetName,
                        const std::string& outputExpr,
                        const std::string& condition, bool inputIsContent,
-                       cmExecutionStatus& status)
+                       mode_t permissions, cmExecutionStatus& status)
 {
   cmListFileBacktrace lfbt = status.GetMakefile().GetBacktrace();
 
@@ -2305,7 +2305,7 @@ void AddEvaluationFile(const std::string& inputName,
 
   status.GetMakefile().AddEvaluationFile(
     inputName, targetName, std::move(outputCge), std::move(conditionCge),
-    inputIsContent);
+    permissions, inputIsContent);
 }
 
 bool HandleGenerateCommand(std::vector<std::string> const& args,
@@ -2323,14 +2323,21 @@ bool HandleGenerateCommand(std::vector<std::string> const& args,
     std::string Content;
     std::string Condition;
     std::string Target;
+    bool NoSourcePermissions = false;
+    bool UseSourcePermissions = false;
+    std::vector<std::string> FilePermissions;
   };
 
-  static auto const parser = cmArgumentParser<Arguments>{}
-                               .Bind("OUTPUT"_s, &Arguments::Output)
-                               .Bind("INPUT"_s, &Arguments::Input)
-                               .Bind("CONTENT"_s, &Arguments::Content)
-                               .Bind("CONDITION"_s, &Arguments::Condition)
-                               .Bind("TARGET"_s, &Arguments::Target);
+  static auto const parser =
+    cmArgumentParser<Arguments>{}
+      .Bind("OUTPUT"_s, &Arguments::Output)
+      .Bind("INPUT"_s, &Arguments::Input)
+      .Bind("CONTENT"_s, &Arguments::Content)
+      .Bind("CONDITION"_s, &Arguments::Condition)
+      .Bind("TARGET"_s, &Arguments::Target)
+      .Bind("NO_SOURCE_PERMISSIONS"_s, &Arguments::NoSourcePermissions)
+      .Bind("USE_SOURCE_PERMISSIONS"_s, &Arguments::UseSourcePermissions)
+      .Bind("FILE_PERMISSIONS"_s, &Arguments::FilePermissions);
 
   std::vector<std::string> unparsedArguments;
   std::vector<std::string> keywordsMissingValues;
@@ -2399,8 +2406,65 @@ bool HandleGenerateCommand(std::vector<std::string> const& args,
     input = arguments.Content;
   }
 
+  if (arguments.NoSourcePermissions && arguments.UseSourcePermissions) {
+    status.SetError("given both NO_SOURCE_PERMISSIONS and "
+                    "USE_SOURCE_PERMISSIONS. Only one option allowed.");
+    return false;
+  }
+
+  if (!arguments.FilePermissions.empty()) {
+    if (arguments.NoSourcePermissions) {
+      status.SetError("given both NO_SOURCE_PERMISSIONS and "
+                      "FILE_PERMISSIONS. Only one option allowed.");
+      return false;
+    }
+    if (arguments.UseSourcePermissions) {
+      status.SetError("given both USE_SOURCE_PERMISSIONS and "
+                      "FILE_PERMISSIONS. Only one option allowed.");
+      return false;
+    }
+  }
+
+  if (arguments.UseSourcePermissions) {
+    if (inputIsContent) {
+      status.SetError("given USE_SOURCE_PERMISSIONS without a file INPUT.");
+      return false;
+    }
+  }
+
+  mode_t permisiions = 0;
+  if (arguments.NoSourcePermissions) {
+    permisiions |= cmFSPermissions::mode_owner_read;
+    permisiions |= cmFSPermissions::mode_owner_write;
+    permisiions |= cmFSPermissions::mode_group_read;
+    permisiions |= cmFSPermissions::mode_world_read;
+  }
+
+  if (!arguments.FilePermissions.empty()) {
+    std::vector<std::string> invalidOptions;
+    for (auto const& e : arguments.FilePermissions) {
+      if (!cmFSPermissions::stringToModeT(e, permisiions)) {
+        invalidOptions.push_back(e);
+      }
+    }
+    if (!invalidOptions.empty()) {
+      std::ostringstream oss;
+      oss << "given invalid permission ";
+      for (auto i = 0u; i < invalidOptions.size(); i++) {
+        if (i == 0u) {
+          oss << "\"" << invalidOptions[i] << "\"";
+        } else {
+          oss << ",\"" << invalidOptions[i] << "\"";
+        }
+      }
+      oss << ".";
+      status.SetError(oss.str());
+      return false;
+    }
+  }
+
   AddEvaluationFile(input, arguments.Target, arguments.Output,
-                    arguments.Condition, inputIsContent, status);
+                    arguments.Condition, inputIsContent, permisiions, status);
   return true;
 }
 

+ 8 - 4
Source/cmGeneratorExpressionEvaluationFile.cxx

@@ -21,13 +21,15 @@ cmGeneratorExpressionEvaluationFile::cmGeneratorExpressionEvaluationFile(
   std::string input, std::string target,
   std::unique_ptr<cmCompiledGeneratorExpression> outputFileExpr,
   std::unique_ptr<cmCompiledGeneratorExpression> condition,
-  bool inputIsContent, cmPolicies::PolicyStatus policyStatusCMP0070)
+  bool inputIsContent, mode_t permissions,
+  cmPolicies::PolicyStatus policyStatusCMP0070)
   : Input(std::move(input))
   , Target(std::move(target))
   , OutputFileExpr(std::move(outputFileExpr))
   , Condition(std::move(condition))
   , InputIsContent(inputIsContent)
   , PolicyStatusCMP0070(policyStatusCMP0070)
+  , Permissions(permissions)
 {
 }
 
@@ -111,14 +113,15 @@ void cmGeneratorExpressionEvaluationFile::CreateOutputFile(
 
 void cmGeneratorExpressionEvaluationFile::Generate(cmLocalGenerator* lg)
 {
-  mode_t perm = 0;
   std::string inputContent;
   if (this->InputIsContent) {
     inputContent = this->Input;
   } else {
     const std::string inputFileName = this->GetInputFileName(lg);
     lg->GetMakefile()->AddCMakeDependFile(inputFileName);
-    cmSystemTools::GetPermissions(inputFileName.c_str(), perm);
+    if (!this->Permissions) {
+      cmSystemTools::GetPermissions(inputFileName.c_str(), this->Permissions);
+    }
     cmsys::ifstream fin(inputFileName.c_str());
     if (!fin) {
       std::ostringstream e;
@@ -152,7 +155,8 @@ void cmGeneratorExpressionEvaluationFile::Generate(cmLocalGenerator* lg)
 
   for (std::string const& le : enabledLanguages) {
     for (std::string const& li : allConfigs) {
-      this->Generate(lg, li, le, inputExpression.get(), outputFiles, perm);
+      this->Generate(lg, li, le, inputExpression.get(), outputFiles,
+                     this->Permissions);
       if (cmSystemTools::GetFatalErrorOccured()) {
         return;
       }

+ 3 - 1
Source/cmGeneratorExpressionEvaluationFile.h

@@ -24,7 +24,8 @@ public:
     std::string input, std::string target,
     std::unique_ptr<cmCompiledGeneratorExpression> outputFileExpr,
     std::unique_ptr<cmCompiledGeneratorExpression> condition,
-    bool inputIsContent, cmPolicies::PolicyStatus policyStatusCMP0070);
+    bool inputIsContent, mode_t permissions,
+    cmPolicies::PolicyStatus policyStatusCMP0070);
 
   void Generate(cmLocalGenerator* lg);
 
@@ -59,4 +60,5 @@ private:
   std::vector<std::string> Files;
   const bool InputIsContent;
   cmPolicies::PolicyStatus PolicyStatusCMP0070;
+  mode_t Permissions;
 };

+ 3 - 2
Source/cmMakefile.cxx

@@ -865,13 +865,14 @@ void cmMakefile::EnforceDirectoryLevelRules() const
 void cmMakefile::AddEvaluationFile(
   const std::string& inputFile, const std::string& targetName,
   std::unique_ptr<cmCompiledGeneratorExpression> outputName,
-  std::unique_ptr<cmCompiledGeneratorExpression> condition,
+  std::unique_ptr<cmCompiledGeneratorExpression> condition, mode_t permissions,
   bool inputIsContent)
 {
   this->EvaluationFiles.push_back(
     cm::make_unique<cmGeneratorExpressionEvaluationFile>(
       inputFile, targetName, std::move(outputName), std::move(condition),
-      inputIsContent, this->GetPolicyStatus(cmPolicies::CMP0070)));
+      inputIsContent, permissions,
+      this->GetPolicyStatus(cmPolicies::CMP0070)));
 }
 
 const std::vector<std::unique_ptr<cmGeneratorExpressionEvaluationFile>>&

+ 1 - 1
Source/cmMakefile.h

@@ -899,7 +899,7 @@ public:
     const std::string& inputFile, const std::string& targetName,
     std::unique_ptr<cmCompiledGeneratorExpression> outputName,
     std::unique_ptr<cmCompiledGeneratorExpression> condition,
-    bool inputIsContent);
+    mode_t permissions, bool inputIsContent);
   const std::vector<std::unique_ptr<cmGeneratorExpressionEvaluationFile>>&
   GetEvaluationFiles() const;
 

+ 15 - 0
Tests/RunCMake/File_Generate/CustomFilePermissions.cmake

@@ -0,0 +1,15 @@
+file(REMOVE "${CMAKE_CURRENT_BINARY_DIR}/customfilepermissions.txt")
+
+file(GENERATE OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/$<LOWER_CASE:$<CONFIG>>/customfilepermissions.txt"
+  INPUT "${CMAKE_CURRENT_SOURCE_DIR}/input.txt"
+  FILE_PERMISSIONS
+    OWNER_READ OWNER_WRITE OWNER_EXECUTE
+    GROUP_EXECUTE
+    WORLD_EXECUTE
+  )
+
+add_custom_target(checkCustomFilePermissions ALL
+  COMMAND ${CMAKE_COMMAND}
+    -DgeneratedFile=${CMAKE_CURRENT_BINARY_DIR}/$<LOWER_CASE:$<CONFIG>>/customfilepermissions.txt
+    -P "${CMAKE_CURRENT_SOURCE_DIR}/CustomFilePermissionsVerify.cmake"
+  )

+ 36 - 0
Tests/RunCMake/File_Generate/CustomFilePermissionsVerify.cmake

@@ -0,0 +1,36 @@
+if(NOT EXISTS "${generatedFile}")
+  message(SEND_ERROR "Missing file:\n  ${generatedFile}")
+endif()
+
+if (UNIX)
+  find_program(STAT_EXECUTABLE NAMES stat)
+  if(NOT STAT_EXECUTABLE)
+    return()
+  endif()
+
+  if (CMAKE_HOST_SYSTEM_NAME MATCHES "FreeBSD")
+    execute_process(COMMAND "${STAT_EXECUTABLE}" -f %Lp "${generatedFile}"
+      OUTPUT_VARIABLE output
+      OUTPUT_STRIP_TRAILING_WHITESPACE
+      COMMAND_ERROR_IS_FATAL ANY
+      )
+  elseif (CMAKE_HOST_SYSTEM_NAME MATCHES "Darwin")
+    execute_process(COMMAND "${STAT_EXECUTABLE}" -f %A "${generatedFile}"
+      OUTPUT_VARIABLE output
+      OUTPUT_STRIP_TRAILING_WHITESPACE
+      COMMAND_ERROR_IS_FATAL ANY
+      )
+  else()
+    execute_process(COMMAND "${STAT_EXECUTABLE}" -c %a "${generatedFile}"
+      OUTPUT_VARIABLE output
+      OUTPUT_STRIP_TRAILING_WHITESPACE
+      COMMAND_ERROR_IS_FATAL ANY
+      )
+  endif()
+
+  if (NOT output EQUAL "711")
+    message(SEND_ERROR "file generate has different permissions source "
+          "permissions: \"${output}\" desired permissions: \"711\"")
+  endif()
+
+endif()

+ 10 - 0
Tests/RunCMake/File_Generate/NoSourcePermissions.cmake

@@ -0,0 +1,10 @@
+file(GENERATE OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/$<LOWER_CASE:$<CONFIG>>/nosourcepermissions.txt"
+  INPUT "${CMAKE_CURRENT_SOURCE_DIR}/input.txt"
+  NO_SOURCE_PERMISSIONS
+  )
+
+add_custom_target(checkNoSourcePermission ALL
+  COMMAND ${CMAKE_COMMAND}
+    -DgeneratedFile=${CMAKE_CURRENT_BINARY_DIR}/$<LOWER_CASE:$<CONFIG>>/nosourcepermissions.txt
+    -P "${CMAKE_CURRENT_SOURCE_DIR}/NoSourcePermissionsVerify.cmake"
+  )

+ 36 - 0
Tests/RunCMake/File_Generate/NoSourcePermissionsVerify.cmake

@@ -0,0 +1,36 @@
+if(NOT EXISTS "${generatedFile}")
+  message(SEND_ERROR "Missing generated file:\n  ${generatedFile}")
+endif()
+
+if (UNIX)
+  find_program(STAT_EXECUTABLE NAMES stat)
+  if(NOT STAT_EXECUTABLE)
+    return()
+  endif()
+
+  if (CMAKE_HOST_SYSTEM_NAME MATCHES "FreeBSD")
+    execute_process(COMMAND "${STAT_EXECUTABLE}" -f %Lp "${generatedFile}"
+      OUTPUT_VARIABLE output
+      OUTPUT_STRIP_TRAILING_WHITESPACE
+      COMMAND_ERROR_IS_FATAL ANY
+      )
+  elseif(CMAKE_HOST_SYSTEM_NAME MATCHES "Darwin")
+    execute_process(COMMAND "${STAT_EXECUTABLE}" -f %A "${generatedFile}"
+      OUTPUT_VARIABLE output
+      OUTPUT_STRIP_TRAILING_WHITESPACE
+      COMMAND_ERROR_IS_FATAL ANY
+      )
+  else()
+    execute_process(COMMAND "${STAT_EXECUTABLE}" -c %a "${generatedFile}"
+      OUTPUT_VARIABLE output
+      OUTPUT_STRIP_TRAILING_WHITESPACE
+      COMMAND_ERROR_IS_FATAL ANY
+      )
+  endif()
+
+  if (NOT output EQUAL "644")
+    message(SEND_ERROR "generated file has different permissions than "
+        "desired, generated permissions: \"${output}\"")
+  endif()
+
+endif()

+ 21 - 0
Tests/RunCMake/File_Generate/RunCMakeTest.cmake

@@ -123,3 +123,24 @@ set(RunCMake_TEST_NO_CLEAN 1)
 run_cmake_command(AdjacentInOut-nowork ${CMAKE_COMMAND} --build .)
 unset(RunCMake_TEST_BINARY_DIR)
 unset(RunCMake_TEST_NO_CLEAN)
+
+run_cmake(SourcePermissions1)
+run_cmake(SourcePermissions2)
+run_cmake(SourcePermissions3)
+run_cmake(SourcePermissions4)
+run_cmake(SourcePermissions5)
+
+function(run_cmake_and_verify_after_build case)
+  set(RunCMake_TEST_BINARY_DIR "${RunCMake_BINARY_DIR}/${case}-build")
+  file(REMOVE_RECURSE "${RunCMake_TEST_BINARY_DIR}")
+  file(MAKE_DIRECTORY "${RunCMake_TEST_BINARY_DIR}")
+  set(RunCMake_TEST_NO_CLEAN 1)
+  run_cmake(${case})
+  run_cmake_command("${case}-build" ${CMAKE_COMMAND} --build .)
+  unset(RunCMake_TEST_NO_CLEAN)
+  unset(RunCMake_TEST_BINARY_DIR)
+endfunction()
+
+run_cmake_and_verify_after_build(NoSourcePermissions)
+run_cmake_and_verify_after_build(UseSourcePermissions)
+run_cmake_and_verify_after_build(CustomFilePermissions)

+ 1 - 0
Tests/RunCMake/File_Generate/SourcePermissions1-result.txt

@@ -0,0 +1 @@
+1

+ 5 - 0
Tests/RunCMake/File_Generate/SourcePermissions1-stderr.txt

@@ -0,0 +1,5 @@
+CMake Error at SourcePermissions1.cmake:[0-9]+ \(file\):
+  file given both NO_SOURCE_PERMISSIONS and USE_SOURCE_PERMISSIONS.  Only one
+  option allowed.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:[0-9]+ \(include\)

+ 5 - 0
Tests/RunCMake/File_Generate/SourcePermissions1.cmake

@@ -0,0 +1,5 @@
+file(GENERATE OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/output-sourcepermission1.txt"
+  INPUT "${CMAKE_CURRENT_SOURCE_DIR}/input.txt"
+  NO_SOURCE_PERMISSIONS
+  USE_SOURCE_PERMISSIONS
+  )

+ 1 - 0
Tests/RunCMake/File_Generate/SourcePermissions2-result.txt

@@ -0,0 +1 @@
+1

+ 5 - 0
Tests/RunCMake/File_Generate/SourcePermissions2-stderr.txt

@@ -0,0 +1,5 @@
+CMake Error at SourcePermissions2.cmake:[0-9]+ \(file\):
+  file given both NO_SOURCE_PERMISSIONS and FILE_PERMISSIONS.  Only one
+  option allowed.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:[0-9]+ \(include\)

+ 5 - 0
Tests/RunCMake/File_Generate/SourcePermissions2.cmake

@@ -0,0 +1,5 @@
+file(GENERATE OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/output-sourcepermission2.txt"
+  INPUT "${CMAKE_CURRENT_SOURCE_DIR}/input.txt"
+  NO_SOURCE_PERMISSIONS
+  FILE_PERMISSIONS OWNER_READ
+  )

+ 1 - 0
Tests/RunCMake/File_Generate/SourcePermissions3-result.txt

@@ -0,0 +1 @@
+1

+ 5 - 0
Tests/RunCMake/File_Generate/SourcePermissions3-stderr.txt

@@ -0,0 +1,5 @@
+CMake Error at SourcePermissions3.cmake:[0-9]+ \(file\):
+  file given both USE_SOURCE_PERMISSIONS and FILE_PERMISSIONS.  Only one
+  option allowed.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:[0-9]+ \(include\)

+ 5 - 0
Tests/RunCMake/File_Generate/SourcePermissions3.cmake

@@ -0,0 +1,5 @@
+file(GENERATE OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/output-sourcepermission3.txt"
+  INPUT "${CMAKE_CURRENT_SOURCE_DIR}/input.txt"
+  USE_SOURCE_PERMISSIONS
+  FILE_PERMISSIONS OWNER_READ
+  )

+ 1 - 0
Tests/RunCMake/File_Generate/SourcePermissions4-result.txt

@@ -0,0 +1 @@
+1

+ 4 - 0
Tests/RunCMake/File_Generate/SourcePermissions4-stderr.txt

@@ -0,0 +1,4 @@
+CMake Error at SourcePermissions4.cmake:[0-9]+ \(file\):
+  file given USE_SOURCE_PERMISSIONS without a file INPUT.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:[0-9]+ \(include\)

+ 4 - 0
Tests/RunCMake/File_Generate/SourcePermissions4.cmake

@@ -0,0 +1,4 @@
+file(GENERATE OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/output-sourcepermission4.txt"
+  CONTENT "Input is content"
+  USE_SOURCE_PERMISSIONS
+  )

+ 1 - 0
Tests/RunCMake/File_Generate/SourcePermissions5-result.txt

@@ -0,0 +1 @@
+1

+ 4 - 0
Tests/RunCMake/File_Generate/SourcePermissions5-stderr.txt

@@ -0,0 +1,4 @@
+CMake Error at SourcePermissions5.cmake:[0-9]+ \(file\):
+  file given invalid permission "GROUP_RWX","USER_ALL".
+Call Stack \(most recent call first\):
+  CMakeLists.txt:[0-9]+ \(include\)

+ 4 - 0
Tests/RunCMake/File_Generate/SourcePermissions5.cmake

@@ -0,0 +1,4 @@
+file(GENERATE OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/output-sourcepermission5.txt"
+  INPUT "${CMAKE_CURRENT_SOURCE_DIR}/input.txt"
+  FILE_PERMISSIONS OWNER_READ GROUP_RWX USER_ALL
+  )

+ 11 - 0
Tests/RunCMake/File_Generate/UseSourcePermissions.cmake

@@ -0,0 +1,11 @@
+file(GENERATE OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/$<LOWER_CASE:$<CONFIG>>/usesourcepermissions.txt"
+  INPUT "${CMAKE_CURRENT_SOURCE_DIR}/input.txt"
+  USE_SOURCE_PERMISSIONS
+  )
+
+add_custom_target(checkUseSourcePermissions ALL
+  COMMAND ${CMAKE_COMMAND}
+    -DsourceFile=${CMAKE_CURRENT_SOURCE_DIR}/input.txt
+    -DgeneratedFile=${CMAKE_CURRENT_BINARY_DIR}/$<LOWER_CASE:$<CONFIG>>/usesourcepermissions.txt
+    -P "${CMAKE_CURRENT_SOURCE_DIR}/UseSourcePermissionsVerify.cmake"
+  )

+ 51 - 0
Tests/RunCMake/File_Generate/UseSourcePermissionsVerify.cmake

@@ -0,0 +1,51 @@
+if(NOT EXISTS "${generatedFile}")
+  message(SEND_ERROR "Missing generated file:\n  ${generatedFile}")
+endif()
+
+if (UNIX)
+  find_program(STAT_EXECUTABLE NAMES stat)
+  if(NOT STAT_EXECUTABLE)
+    return()
+  endif()
+
+  if (CMAKE_HOST_SYSTEM_NAME MATCHES "FreeBSD")
+    execute_process(COMMAND "${STAT_EXECUTABLE}" -f %Lp "${sourceFile}"
+      OUTPUT_VARIABLE output1
+      OUTPUT_STRIP_TRAILING_WHITESPACE
+      COMMAND_ERROR_IS_FATAL ANY
+      )
+    execute_process(COMMAND "${STAT_EXECUTABLE}" -f %Lp "${generatedFile}"
+      OUTPUT_VARIABLE output2
+      OUTPUT_STRIP_TRAILING_WHITESPACE
+      COMMAND_ERROR_IS_FATAL ANY
+      )
+  elseif (CMAKE_HOST_SYSTEM_NAME MATCHES "Darwin")
+    execute_process(COMMAND "${STAT_EXECUTABLE}" -f %A "${sourceFile}"
+      OUTPUT_VARIABLE output1
+      OUTPUT_STRIP_TRAILING_WHITESPACE
+      COMMAND_ERROR_IS_FATAL ANY
+      )
+    execute_process(COMMAND "${STAT_EXECUTABLE}" -f %A "${generatedFile}"
+      OUTPUT_VARIABLE output2
+      OUTPUT_STRIP_TRAILING_WHITESPACE
+      COMMAND_ERROR_IS_FATAL ANY
+      )
+  else()
+    execute_process(COMMAND "${STAT_EXECUTABLE}" -c %a "${sourceFile}"
+      OUTPUT_VARIABLE output1
+      OUTPUT_STRIP_TRAILING_WHITESPACE
+      COMMAND_ERROR_IS_FATAL ANY
+      )
+    execute_process(COMMAND "${STAT_EXECUTABLE}" -c %a "${generatedFile}"
+      OUTPUT_VARIABLE output2
+      OUTPUT_STRIP_TRAILING_WHITESPACE
+      COMMAND_ERROR_IS_FATAL ANY
+      )
+  endif()
+
+  if (NOT output1 EQUAL output2)
+    message(SEND_ERROR "generated file has a different permissions source "
+          "permissions: \"${output1}\" generated permissions: \"${output2}\"")
+  endif()
+
+endif()