configure_file: Add option for user defined permissions

User defined permissions and options to copy permissions are
implemented.

Fixes: #20866
This commit is contained in:
Asit Dhal
2020-10-04 19:44:31 +02:00
parent 5988a4deea
commit 03ad0e61c1
30 changed files with 266 additions and 28 deletions

View File

@@ -6,8 +6,9 @@ Copy a file to another location and modify its contents.
.. code-block:: cmake
configure_file(<input> <output>
[FILE_PERMISSIONS <permissions>...]
[COPYONLY] [ESCAPE_QUOTES] [@ONLY]
[NO_SOURCE_PERMISSIONS]
[NO_SOURCE_PERMISSIONS] [USE_SOURCE_PERMISSIONS]
[NEWLINE_STYLE [UNIX|DOS|WIN32|LF|CRLF] ])
Copies an ``<input>`` file to an ``<output>`` file and substitutes
@@ -72,6 +73,9 @@ The arguments are:
If the path names an existing directory the output file is placed
in that directory with the same file name as the input file.
``FILE_PERMISSIONS <permissions>...``
Use user provided permissions for the output file.
``COPYONLY``
Copy the file without replacing any variable references or other
content. This option may not be used with ``NEWLINE_STYLE``.
@@ -88,6 +92,9 @@ The arguments are:
The copied file permissions default to the standard 644 value
(-rw-r--r--).
``USE_SOURCE_PERMISSIONS``
Transfer the file permissions of the original file to the output file.
``NEWLINE_STYLE <style>``
Specify the newline style for the output file. Specify
``UNIX`` or ``LF`` for ``\n`` newlines, or specify

View File

@@ -0,0 +1,6 @@
configure_file-user-permissions
-------------------------------
* The :command:`configure_file` command gained a ``USE_SOURCE_PERMISSIONS``
and ``FILE_PERMISSIONS`` option to support copying of permissions of source
file and specifying user defined permissions.

View File

@@ -1329,7 +1329,7 @@ bool cmCPackGenerator::ConfigureFile(const std::string& inName,
bool copyOnly /* = false */)
{
return this->MakefileMap->ConfigureFile(inName, outName, copyOnly, true,
false, true) == 1;
false) == 1;
}
int cmCPackGenerator::CleanTemporaryDirectory()

View File

@@ -3,11 +3,15 @@
#include "cmConfigureFileCommand.h"
#include <set>
#include <sstream>
#include <cm/string_view>
#include <cmext/string_view>
#include <sys/types.h>
#include "cmExecutionStatus.h"
#include "cmFSPermissions.h"
#include "cmMakefile.h"
#include "cmMessageType.h"
#include "cmNewLineStyle.h"
@@ -60,7 +64,19 @@ bool cmConfigureFileCommand(std::vector<std::string> const& args,
}
bool copyOnly = false;
bool escapeQuotes = false;
bool use_source_permissions = true;
bool useSourcePermissions = false;
bool noSourcePermissions = false;
bool filePermissions = false;
std::vector<std::string> filePermissionOptions;
enum class Doing
{
DoingNone,
DoingFilePermissions,
DoneFilePermissions
};
Doing doing = Doing::DoingNone;
static std::set<cm::string_view> noopOptions = {
/* Legacy. */
@@ -78,6 +94,9 @@ bool cmConfigureFileCommand(std::vector<std::string> const& args,
bool atOnly = false;
for (unsigned int i = 2; i < args.size(); ++i) {
if (args[i] == "COPYONLY") {
if (doing == Doing::DoingFilePermissions) {
doing = Doing::DoneFilePermissions;
}
copyOnly = true;
if (newLineStyle.IsValid()) {
status.SetError("COPYONLY could not be used in combination "
@@ -85,13 +104,49 @@ bool cmConfigureFileCommand(std::vector<std::string> const& args,
return false;
}
} else if (args[i] == "ESCAPE_QUOTES") {
if (doing == Doing::DoingFilePermissions) {
doing = Doing::DoneFilePermissions;
}
escapeQuotes = true;
} else if (args[i] == "@ONLY") {
if (doing == Doing::DoingFilePermissions) {
doing = Doing::DoneFilePermissions;
}
atOnly = true;
} else if (args[i] == "NO_SOURCE_PERMISSIONS") {
use_source_permissions = false;
if (doing == Doing::DoingFilePermissions) {
status.SetError(" given both FILE_PERMISSIONS and "
"NO_SOURCE_PERMISSIONS. Only one option allowed.");
return false;
}
noSourcePermissions = true;
} else if (args[i] == "USE_SOURCE_PERMISSIONS") {
if (doing == Doing::DoingFilePermissions) {
status.SetError(" given both FILE_PERMISSIONS and "
"USE_SOURCE_PERMISSIONS. Only one option allowed.");
return false;
}
useSourcePermissions = true;
} else if (args[i] == "FILE_PERMISSIONS") {
if (useSourcePermissions) {
status.SetError(" given both FILE_PERMISSIONS and "
"USE_SOURCE_PERMISSIONS. Only one option allowed.");
return false;
}
if (noSourcePermissions) {
status.SetError(" given both FILE_PERMISSIONS and "
"NO_SOURCE_PERMISSIONS. Only one option allowed.");
return false;
}
if (doing == Doing::DoingNone) {
doing = Doing::DoingFilePermissions;
filePermissions = true;
}
} else if (noopOptions.find(args[i]) != noopOptions.end()) {
/* Ignore no-op options. */
} else if (doing == Doing::DoingFilePermissions) {
filePermissionOptions.push_back(args[i]);
} else {
unknown_args += " ";
unknown_args += args[i];
@@ -104,9 +159,53 @@ bool cmConfigureFileCommand(std::vector<std::string> const& args,
status.GetMakefile().IssueMessage(MessageType::AUTHOR_WARNING, msg);
}
if (!status.GetMakefile().ConfigureFile(
inputFile, outputFile, copyOnly, atOnly, escapeQuotes,
use_source_permissions, newLineStyle)) {
if (useSourcePermissions && noSourcePermissions) {
status.SetError(" given both USE_SOURCE_PERMISSIONS and "
"NO_SOURCE_PERMISSIONS. Only one option allowed.");
return false;
}
mode_t permisiions = 0;
if (filePermissions) {
if (filePermissionOptions.empty()) {
status.SetError(" given FILE_PERMISSIONS without any options.");
return false;
}
std::vector<std::string> invalidOptions;
for (auto const& e : filePermissionOptions) {
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;
}
}
if (noSourcePermissions) {
permisiions |= cmFSPermissions::mode_owner_read;
permisiions |= cmFSPermissions::mode_owner_write;
permisiions |= cmFSPermissions::mode_group_read;
permisiions |= cmFSPermissions::mode_world_read;
}
if (!status.GetMakefile().ConfigureFile(inputFile, outputFile, copyOnly,
atOnly, escapeQuotes, permisiions,
newLineStyle)) {
status.SetError("Problem configuring file");
return false;
}

View File

@@ -127,7 +127,7 @@ bool cmCreateTestSourceList(std::vector<std::string> const& args,
mf.AddDefinition("CMAKE_FORWARD_DECLARE_TESTS", forwardDeclareCode);
mf.AddDefinition("CMAKE_FUNCTION_TABLE_ENTIRES", functionMapCode);
bool res = true;
if (!mf.ConfigureFile(configFile, driver, false, true, false, true)) {
if (!mf.ConfigureFile(configFile, driver, false, true, false)) {
res = false;
}

View File

@@ -3768,7 +3768,7 @@ void cmLocalGenerator::GenerateAppleInfoPList(cmGeneratorTarget* target,
cmLGInfoProp(mf, target, "MACOSX_BUNDLE_SHORT_VERSION_STRING");
cmLGInfoProp(mf, target, "MACOSX_BUNDLE_BUNDLE_VERSION");
cmLGInfoProp(mf, target, "MACOSX_BUNDLE_COPYRIGHT");
mf->ConfigureFile(inFile, fname, false, false, false, true);
mf->ConfigureFile(inFile, fname, false, false, false);
}
void cmLocalGenerator::GenerateFrameworkInfoPList(
@@ -3803,7 +3803,7 @@ void cmLocalGenerator::GenerateFrameworkInfoPList(
cmLGInfoProp(mf, target, "MACOSX_FRAMEWORK_IDENTIFIER");
cmLGInfoProp(mf, target, "MACOSX_FRAMEWORK_SHORT_VERSION_STRING");
cmLGInfoProp(mf, target, "MACOSX_FRAMEWORK_BUNDLE_VERSION");
mf->ConfigureFile(inFile, fname, false, false, false, true);
mf->ConfigureFile(inFile, fname, false, false, false);
}
namespace {

View File

@@ -29,15 +29,12 @@
#include "cmsys/FStream.hxx"
#include "cmsys/RegularExpression.hxx"
#include "cm_sys_stat.h"
#include "cmCommandArgumentParserHelper.h"
#include "cmCustomCommand.h"
#include "cmCustomCommandLines.h"
#include "cmExecutionStatus.h"
#include "cmExpandedCommandArgument.h" // IWYU pragma: keep
#include "cmExportBuildFileGenerator.h"
#include "cmFSPermissions.h"
#include "cmFileLockPool.h"
#include "cmFunctionBlocker.h"
#include "cmGeneratedFileStream.h"
@@ -72,8 +69,6 @@
class cmMessenger;
using namespace cmFSPermissions;
cmDirectoryId::cmDirectoryId(std::string s)
: String(std::move(s))
{
@@ -3850,8 +3845,7 @@ void cmMakefile::ConfigureString(const std::string& input, std::string& output,
int cmMakefile::ConfigureFile(const std::string& infile,
const std::string& outfile, bool copyonly,
bool atOnly, bool escapeQuotes,
bool use_source_permissions,
cmNewLineStyle newLine)
mode_t permissions, cmNewLineStyle newLine)
{
int res = 1;
if (!this->CanIWriteThisFile(outfile)) {
@@ -3873,12 +3867,8 @@ int cmMakefile::ConfigureFile(const std::string& infile,
// output files that now don't exist.
this->AddCMakeOutputFile(soutfile);
mode_t perm = 0;
if (!use_source_permissions) {
perm = perm | mode_owner_read | mode_owner_write | mode_group_read |
mode_world_read;
} else {
cmSystemTools::GetPermissions(sinfile, perm);
if (permissions == 0) {
cmSystemTools::GetPermissions(sinfile, permissions);
}
std::string::size_type pos = soutfile.rfind('/');
@@ -3893,7 +3883,7 @@ int cmMakefile::ConfigureFile(const std::string& infile,
cmSystemTools::GetLastSystemError());
return 0;
}
if (!cmSystemTools::SetPermissions(soutfile, perm)) {
if (!cmSystemTools::SetPermissions(soutfile, permissions)) {
this->IssueMessage(MessageType::FATAL_ERROR,
cmSystemTools::GetLastSystemError());
return 0;
@@ -3950,7 +3940,7 @@ int cmMakefile::ConfigureFile(const std::string& infile,
cmSystemTools::GetLastSystemError());
res = 0;
} else {
if (!cmSystemTools::SetPermissions(soutfile, perm)) {
if (!cmSystemTools::SetPermissions(soutfile, permissions)) {
this->IssueMessage(MessageType::FATAL_ERROR,
cmSystemTools::GetLastSystemError());
res = 0;

View File

@@ -20,6 +20,8 @@
#include "cmsys/RegularExpression.hxx"
#include "cm_sys_stat.h"
#include "cmAlgorithms.h"
#include "cmCustomCommandTypes.h"
#include "cmListFileCache.h"
@@ -659,8 +661,7 @@ public:
*/
int ConfigureFile(const std::string& infile, const std::string& outfile,
bool copyonly, bool atOnly, bool escapeQuotes,
bool use_source_permissions,
cmNewLineStyle = cmNewLineStyle());
mode_t permissions = 0, cmNewLineStyle = cmNewLineStyle());
/**
* Print a command's invocation

View File

@@ -676,7 +676,7 @@ void cmVisualStudio10TargetGenerator::Generate()
cmStrCat(this->DefaultArtifactDir, "\\nasm.props");
ConvertToWindowsSlash(propsLocal);
this->Makefile->ConfigureFile(propsTemplate, propsLocal, false, true,
true, true);
true);
Elem(e1, "Import").Attribute("Project", propsLocal);
}
}

View File

@@ -16,6 +16,13 @@ run_cmake(NewLineStyle-WrongArg)
run_cmake(NewLineStyle-ValidArg)
run_cmake(NewLineStyle-COPYONLY)
run_cmake(NoSourcePermissions)
run_cmake(SourcePermissionsInvalidArg-1)
run_cmake(SourcePermissionsInvalidArg-2)
run_cmake(SourcePermissionsInvalidArg-3)
run_cmake(SourcePermissionsInvalidArg-4)
run_cmake(SourcePermissionsInvalidArg-5)
run_cmake(UseSourcePermissions)
run_cmake(SourcePermissions)
if(RunCMake_GENERATOR MATCHES "Make")
# Use a single build tree for a few tests without cleaning.

View File

@@ -0,0 +1,34 @@
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/sourcefile.txt
${CMAKE_CURRENT_BINARY_DIR}/sourcefile-source-permissions.txt
FILE_PERMISSIONS
OWNER_READ OWNER_EXECUTE
GROUP_READ GROUP_EXECUTE
WORLD_READ
)
if (UNIX)
find_program(STAT_EXECUTABLE NAMES stat)
if(NOT STAT_EXECUTABLE)
return()
endif()
if (CMAKE_SYSTEM_NAME MATCHES "FreeBSD")
execute_process(COMMAND "${STAT_EXECUTABLE}" -f %Lp "${CMAKE_CURRENT_BINARY_DIR}/sourcefile-source-permissions.txt"
OUTPUT_VARIABLE output
)
elseif(CMAKE_SYSTEM_NAME MATCHES "Darwin")
execute_process(COMMAND "${STAT_EXECUTABLE}" -f %A "${CMAKE_CURRENT_BINARY_DIR}/sourcefile-source-permissions.txt"
OUTPUT_VARIABLE output
)
else()
execute_process(COMMAND "${STAT_EXECUTABLE}" -c %a "${CMAKE_CURRENT_BINARY_DIR}/sourcefile-source-permissions.txt"
OUTPUT_VARIABLE output
)
endif()
if (NOT output EQUAL "554")
message(FATAL_ERROR "configure file has different permissions than "
"desired, generated permissions: ${output}")
endif()
endif()

View File

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

View File

@@ -0,0 +1,5 @@
configure_file(sourcefile.txt
${CMAKE_CURRENT_BINARY_DIR}/sourcefile.txt
NO_SOURCE_PERMISSIONS
USE_SOURCE_PERMISSIONS
)

View File

@@ -0,0 +1,5 @@
CMake Error at SourcePermissionsInvalidArg-2.cmake:1 \(configure_file\):
configure_file given both FILE_PERMISSIONS and NO_SOURCE_PERMISSIONS. Only
one option allowed.
Call Stack \(most recent call first\):
CMakeLists.txt:3 \(include\)

View File

@@ -0,0 +1,5 @@
configure_file(sourcefile.txt
${CMAKE_CURRENT_BINARY_DIR}/sourcefile.txt
NO_SOURCE_PERMISSIONS
FILE_PERMISSIONS OWNER_READ
)

View File

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

View File

@@ -0,0 +1,5 @@
configure_file(sourcefile.txt
${CMAKE_CURRENT_BINARY_DIR}/sourcefile.txt
USE_SOURCE_PERMISSIONS
FILE_PERMISSIONS OWNER_READ
)

View File

@@ -0,0 +1,4 @@
CMake Error at SourcePermissionsInvalidArg-4.cmake:1 \(configure_file\):
configure_file given FILE_PERMISSIONS without any options.
Call Stack \(most recent call first\):
CMakeLists.txt:3 \(include\)

View File

@@ -0,0 +1,4 @@
configure_file(sourcefile.txt
${CMAKE_CURRENT_BINARY_DIR}/sourcefile.txt
FILE_PERMISSIONS
)

View File

@@ -0,0 +1,4 @@
CMake Error at SourcePermissionsInvalidArg-5.cmake:1 \(configure_file\):
configure_file given invalid permission "OWNER_RX","GROUP_RWX".
Call Stack \(most recent call first\):
CMakeLists.txt:3 \(include\)

View File

@@ -0,0 +1,6 @@
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/sourcefile.txt
${CMAKE_CURRENT_BINARY_DIR}/sourcefile-source-permissions.txt
FILE_PERMISSIONS
OWNER_READ OWNER_RX
GROUP_RWX
)

View File

@@ -0,0 +1,40 @@
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/sourcefile.txt
${CMAKE_CURRENT_BINARY_DIR}/sourcefile-use-source-permissions.txt
USE_SOURCE_PERMISSIONS
)
if (UNIX)
find_program(STAT_EXECUTABLE NAMES stat)
if(NOT STAT_EXECUTABLE)
return()
endif()
if (CMAKE_SYSTEM_NAME MATCHES "FreeBSD")
execute_process(COMMAND "${STAT_EXECUTABLE}" -f %Lp "${CMAKE_CURRENT_SOURCE_DIR}/sourcefile.txt"
OUTPUT_VARIABLE output1
)
execute_process(COMMAND "${STAT_EXECUTABLE}" -f %Lp "${CMAKE_CURRENT_BINARY_DIR}/sourcefile-use-source-permissions.txt"
OUTPUT_VARIABLE output2
)
elseif (CMAKE_SYSTEM_NAME MATCHES "Darwin")
execute_process(COMMAND "${STAT_EXECUTABLE}" -f %A "${CMAKE_CURRENT_SOURCE_DIR}/sourcefile.txt"
OUTPUT_VARIABLE output1
)
execute_process(COMMAND "${STAT_EXECUTABLE}" -f %A "${CMAKE_CURRENT_BINARY_DIR}/sourcefile-use-source-permissions.txt"
OUTPUT_VARIABLE output2
)
else()
execute_process(COMMAND "${STAT_EXECUTABLE}" -c %a "${CMAKE_CURRENT_SOURCE_DIR}/sourcefile.txt"
OUTPUT_VARIABLE output1
)
execute_process(COMMAND "${STAT_EXECUTABLE}" -c %a "${CMAKE_CURRENT_BINARY_DIR}/sourcefile-use-source-permissions.txt"
OUTPUT_VARIABLE output2
)
endif()
if (NOT output1 EQUAL output2)
message(FATAL_ERROR "configure file has different permissions source "
"permissions: ${output1} generated permissions: ${output2}")
endif()
endif()

View File

@@ -0,0 +1 @@
an empty file