Commit 32a569e8 authored by Brad King's avatar Brad King Committed by Kitware Robot
Browse files

Merge topic 'refactor_cmfilecopier'

e2e8f6b1

 cmFileCommand: Factor out cmFileCopier and cmFileInstaller
Acked-by: Kitware Robot's avatarKitware Robot <kwrobot@kitware.com>
Merge-request: !2664
parents 7bc03aa6 e2e8f6b1
Pipeline #132204 failed with stage
in 0 seconds
......@@ -224,6 +224,10 @@ set(SRCS
cmFileAPICodemodel.h
cmFileAPICMakeFiles.cxx
cmFileAPICMakeFiles.h
cmFileCopier.cxx
cmFileCopier.h
cmFileInstaller.cxx
cmFileInstaller.h
cmFileLock.cxx
cmFileLock.h
cmFileLockPool.cxx
......
This diff is collapsed.
This diff is collapsed.
/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
file Copyright.txt or https://cmake.org/licensing for details. */
#ifndef cmFileCopier_h
#define cmFileCopier_h
#include "cmConfigure.h" // IWYU pragma: keep
#include "cmFileTimeComparison.h"
#include "cm_sys_stat.h"
#include "cmsys/RegularExpression.hxx"
#include <string>
#include <vector>
class cmFileCommand;
class cmMakefile;
// File installation helper class.
struct cmFileCopier
{
cmFileCopier(cmFileCommand* command, const char* name = "COPY");
virtual ~cmFileCopier();
bool Run(std::vector<std::string> const& args);
protected:
cmFileCommand* FileCommand;
cmMakefile* Makefile;
const char* Name;
bool Always;
cmFileTimeComparison FileTimes;
// Whether to install a file not matching any expression.
bool MatchlessFiles;
// Permissions for files and directories installed by this object.
mode_t FilePermissions;
mode_t DirPermissions;
// Properties set by pattern and regex match rules.
struct MatchProperties
{
bool Exclude = false;
mode_t Permissions = 0;
};
struct MatchRule
{
cmsys::RegularExpression Regex;
MatchProperties Properties;
std::string RegexString;
MatchRule(std::string const& regex)
: Regex(regex)
, RegexString(regex)
{
}
};
std::vector<MatchRule> MatchRules;
// Get the properties from rules matching this input file.
MatchProperties CollectMatchProperties(const std::string& file);
bool SetPermissions(const std::string& toFile, mode_t permissions);
// Translate an argument to a permissions bit.
bool CheckPermissions(std::string const& arg, mode_t& permissions);
bool InstallSymlink(const std::string& fromFile, const std::string& toFile);
bool InstallFile(const std::string& fromFile, const std::string& toFile,
MatchProperties match_properties);
bool InstallDirectory(const std::string& source,
const std::string& destination,
MatchProperties match_properties);
virtual bool Install(const std::string& fromFile, const std::string& toFile);
virtual std::string const& ToName(std::string const& fromName);
enum Type
{
TypeFile,
TypeDir,
TypeLink
};
virtual void ReportCopy(const std::string&, Type, bool) {}
virtual bool ReportMissing(const std::string& fromFile);
MatchRule* CurrentMatchRule;
bool UseGivenPermissionsFile;
bool UseGivenPermissionsDir;
bool UseSourcePermissions;
std::string Destination;
std::string FilesFromDir;
std::vector<std::string> Files;
int Doing;
virtual bool Parse(std::vector<std::string> const& args);
enum
{
DoingNone,
DoingError,
DoingDestination,
DoingFilesFromDir,
DoingFiles,
DoingPattern,
DoingRegex,
DoingPermissionsFile,
DoingPermissionsDir,
DoingPermissionsMatch,
DoingLast1
};
virtual bool CheckKeyword(std::string const& arg);
virtual bool CheckValue(std::string const& arg);
void NotBeforeMatch(std::string const& arg);
void NotAfterMatch(std::string const& arg);
virtual void DefaultFilePermissions();
virtual void DefaultDirectoryPermissions();
bool GetDefaultDirectoryPermissions(mode_t** mode);
};
#endif
/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
file Copyright.txt or https://cmake.org/licensing for details. */
#include "cmFileInstaller.h"
#include "cmFSPermissions.h"
#include "cmFileCommand.h"
#include "cmMakefile.h"
#include "cmSystemTools.h"
#include "cm_sys_stat.h"
#include <sstream>
using namespace cmFSPermissions;
cmFileInstaller::cmFileInstaller(cmFileCommand* command)
: cmFileCopier(command, "INSTALL")
, InstallType(cmInstallType_FILES)
, Optional(false)
, MessageAlways(false)
, MessageLazy(false)
, MessageNever(false)
, DestDirLength(0)
{
// Installation does not use source permissions by default.
this->UseSourcePermissions = false;
// Check whether to copy files always or only if they have changed.
std::string install_always;
if (cmSystemTools::GetEnv("CMAKE_INSTALL_ALWAYS", install_always)) {
this->Always = cmSystemTools::IsOn(install_always);
}
// Get the current manifest.
this->Manifest =
this->Makefile->GetSafeDefinition("CMAKE_INSTALL_MANIFEST_FILES");
}
cmFileInstaller::~cmFileInstaller()
{
// Save the updated install manifest.
this->Makefile->AddDefinition("CMAKE_INSTALL_MANIFEST_FILES",
this->Manifest.c_str());
}
void cmFileInstaller::ManifestAppend(std::string const& file)
{
if (!this->Manifest.empty()) {
this->Manifest += ";";
}
this->Manifest += file.substr(this->DestDirLength);
}
std::string const& cmFileInstaller::ToName(std::string const& fromName)
{
return this->Rename.empty() ? fromName : this->Rename;
}
void cmFileInstaller::ReportCopy(const std::string& toFile, Type type,
bool copy)
{
if (!this->MessageNever && (copy || !this->MessageLazy)) {
std::string message = (copy ? "Installing: " : "Up-to-date: ");
message += toFile;
this->Makefile->DisplayStatus(message, -1);
}
if (type != TypeDir) {
// Add the file to the manifest.
this->ManifestAppend(toFile);
}
}
bool cmFileInstaller::ReportMissing(const std::string& fromFile)
{
return (this->Optional || this->cmFileCopier::ReportMissing(fromFile));
}
bool cmFileInstaller::Install(const std::string& fromFile,
const std::string& toFile)
{
// Support installing from empty source to make a directory.
if (this->InstallType == cmInstallType_DIRECTORY && fromFile.empty()) {
return this->InstallDirectory(fromFile, toFile, MatchProperties());
}
return this->cmFileCopier::Install(fromFile, toFile);
}
void cmFileInstaller::DefaultFilePermissions()
{
this->cmFileCopier::DefaultFilePermissions();
// Add execute permissions based on the target type.
switch (this->InstallType) {
case cmInstallType_SHARED_LIBRARY:
case cmInstallType_MODULE_LIBRARY:
if (this->Makefile->IsOn("CMAKE_INSTALL_SO_NO_EXE")) {
break;
}
CM_FALLTHROUGH;
case cmInstallType_EXECUTABLE:
case cmInstallType_PROGRAMS:
this->FilePermissions |= mode_owner_execute;
this->FilePermissions |= mode_group_execute;
this->FilePermissions |= mode_world_execute;
break;
default:
break;
}
}
bool cmFileInstaller::Parse(std::vector<std::string> const& args)
{
if (!this->cmFileCopier::Parse(args)) {
return false;
}
if (!this->Rename.empty()) {
if (!this->FilesFromDir.empty()) {
this->FileCommand->SetError("INSTALL option RENAME may not be "
"combined with FILES_FROM_DIR.");
return false;
}
if (this->InstallType != cmInstallType_FILES &&
this->InstallType != cmInstallType_PROGRAMS) {
this->FileCommand->SetError("INSTALL option RENAME may be used "
"only with FILES or PROGRAMS.");
return false;
}
if (this->Files.size() > 1) {
this->FileCommand->SetError("INSTALL option RENAME may be used "
"only with one file.");
return false;
}
}
if (!this->HandleInstallDestination()) {
return false;
}
if (((this->MessageAlways ? 1 : 0) + (this->MessageLazy ? 1 : 0) +
(this->MessageNever ? 1 : 0)) > 1) {
this->FileCommand->SetError("INSTALL options MESSAGE_ALWAYS, "
"MESSAGE_LAZY, and MESSAGE_NEVER "
"are mutually exclusive.");
return false;
}
return true;
}
bool cmFileInstaller::CheckKeyword(std::string const& arg)
{
if (arg == "TYPE") {
if (this->CurrentMatchRule) {
this->NotAfterMatch(arg);
} else {
this->Doing = DoingType;
}
} else if (arg == "FILES") {
if (this->CurrentMatchRule) {
this->NotAfterMatch(arg);
} else {
this->Doing = DoingFiles;
}
} else if (arg == "RENAME") {
if (this->CurrentMatchRule) {
this->NotAfterMatch(arg);
} else {
this->Doing = DoingRename;
}
} else if (arg == "OPTIONAL") {
if (this->CurrentMatchRule) {
this->NotAfterMatch(arg);
} else {
this->Doing = DoingNone;
this->Optional = true;
}
} else if (arg == "MESSAGE_ALWAYS") {
if (this->CurrentMatchRule) {
this->NotAfterMatch(arg);
} else {
this->Doing = DoingNone;
this->MessageAlways = true;
}
} else if (arg == "MESSAGE_LAZY") {
if (this->CurrentMatchRule) {
this->NotAfterMatch(arg);
} else {
this->Doing = DoingNone;
this->MessageLazy = true;
}
} else if (arg == "MESSAGE_NEVER") {
if (this->CurrentMatchRule) {
this->NotAfterMatch(arg);
} else {
this->Doing = DoingNone;
this->MessageNever = true;
}
} else if (arg == "PERMISSIONS") {
if (this->CurrentMatchRule) {
this->Doing = DoingPermissionsMatch;
} else {
// file(INSTALL) aliases PERMISSIONS to FILE_PERMISSIONS
this->Doing = DoingPermissionsFile;
this->UseGivenPermissionsFile = true;
}
} else if (arg == "DIR_PERMISSIONS") {
if (this->CurrentMatchRule) {
this->NotAfterMatch(arg);
} else {
// file(INSTALL) aliases DIR_PERMISSIONS to DIRECTORY_PERMISSIONS
this->Doing = DoingPermissionsDir;
this->UseGivenPermissionsDir = true;
}
} else if (arg == "COMPONENTS" || arg == "CONFIGURATIONS" ||
arg == "PROPERTIES") {
std::ostringstream e;
e << "INSTALL called with old-style " << arg << " argument. "
<< "This script was generated with an older version of CMake. "
<< "Re-run this cmake version on your build tree.";
this->FileCommand->SetError(e.str());
this->Doing = DoingError;
} else {
return this->cmFileCopier::CheckKeyword(arg);
}
return true;
}
bool cmFileInstaller::CheckValue(std::string const& arg)
{
switch (this->Doing) {
case DoingType:
if (!this->GetTargetTypeFromString(arg)) {
this->Doing = DoingError;
}
break;
case DoingRename:
this->Rename = arg;
break;
default:
return this->cmFileCopier::CheckValue(arg);
}
return true;
}
bool cmFileInstaller::GetTargetTypeFromString(const std::string& stype)
{
if (stype == "EXECUTABLE") {
this->InstallType = cmInstallType_EXECUTABLE;
} else if (stype == "FILE") {
this->InstallType = cmInstallType_FILES;
} else if (stype == "PROGRAM") {
this->InstallType = cmInstallType_PROGRAMS;
} else if (stype == "STATIC_LIBRARY") {
this->InstallType = cmInstallType_STATIC_LIBRARY;
} else if (stype == "SHARED_LIBRARY") {
this->InstallType = cmInstallType_SHARED_LIBRARY;
} else if (stype == "MODULE") {
this->InstallType = cmInstallType_MODULE_LIBRARY;
} else if (stype == "DIRECTORY") {
this->InstallType = cmInstallType_DIRECTORY;
} else {
std::ostringstream e;
e << "Option TYPE given unknown value \"" << stype << "\".";
this->FileCommand->SetError(e.str());
return false;
}
return true;
}
bool cmFileInstaller::HandleInstallDestination()
{
std::string& destination = this->Destination;
// allow for / to be a valid destination
if (destination.size() < 2 && destination != "/") {
this->FileCommand->SetError("called with inappropriate arguments. "
"No DESTINATION provided or .");
return false;
}
std::string sdestdir;
if (cmSystemTools::GetEnv("DESTDIR", sdestdir) && !sdestdir.empty()) {
cmSystemTools::ConvertToUnixSlashes(sdestdir);
char ch1 = destination[0];
char ch2 = destination[1];
char ch3 = 0;
if (destination.size() > 2) {
ch3 = destination[2];
}
int skip = 0;
if (ch1 != '/') {
int relative = 0;
if (((ch1 >= 'a' && ch1 <= 'z') || (ch1 >= 'A' && ch1 <= 'Z')) &&
ch2 == ':') {
// Assume windows
// let's do some destdir magic:
skip = 2;
if (ch3 != '/') {
relative = 1;
}
} else {
relative = 1;
}
if (relative) {
// This is relative path on unix or windows. Since we are doing
// destdir, this case does not make sense.
this->FileCommand->SetError(
"called with relative DESTINATION. This "
"does not make sense when using DESTDIR. Specify "
"absolute path or remove DESTDIR environment variable.");
return false;
}
} else {
if (ch2 == '/') {
// looks like a network path.
std::string message =
"called with network path DESTINATION. This "
"does not make sense when using DESTDIR. Specify local "
"absolute path or remove DESTDIR environment variable."
"\nDESTINATION=\n";
message += destination;
this->FileCommand->SetError(message);
return false;
}
}
destination = sdestdir + (destination.c_str() + skip);
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 = &default_dir_mode_v;
if (!this->GetDefaultDirectoryPermissions(&default_dir_mode)) {
return false;
}
if (this->InstallType != cmInstallType_DIRECTORY) {
if (!cmSystemTools::FileExists(destination)) {
if (!cmSystemTools::MakeDirectory(destination, default_dir_mode)) {
std::string errstring = "cannot create directory: " + destination +
". Maybe need administrative privileges.";
this->FileCommand->SetError(errstring);
return false;
}
}
if (!cmSystemTools::FileIsDirectory(destination)) {
std::string errstring =
"INSTALL destination: " + destination + " is not a directory.";
this->FileCommand->SetError(errstring);
return false;
}
}
return true;
}
/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
file Copyright.txt or https://cmake.org/licensing for details. */
#ifndef cmFileInstaller_h
#define cmFileInstaller_h
#include "cmConfigure.h" // IWYU pragma: keep
#include "cmFileCopier.h"
#include "cmInstallType.h"
#include <string>
#include <vector>
class cmFileCommand;
struct cmFileInstaller : public cmFileCopier
{
cmFileInstaller(cmFileCommand* command);
~cmFileInstaller() override;
protected:
cmInstallType InstallType;
bool Optional;
bool MessageAlways;
bool MessageLazy;
bool MessageNever;
int DestDirLength;
std::string Rename;
std::string Manifest;
void ManifestAppend(std::string const& file);
std::string const& ToName(std::string const& fromName) override;
void ReportCopy(const std::string& toFile, Type type, bool copy) override;
bool ReportMissing(const std::string& fromFile) override;
bool Install(const std::string& fromFile,
const std::string& toFile) override;
bool Parse(std::vector<std::string> const& args) override;
enum
{
DoingType = DoingLast1,
DoingRename,
DoingLast2
};
bool CheckKeyword(std::string const& arg) override;
bool CheckValue(std::string const& arg) override;
void DefaultFilePermissions() override;
bool GetTargetTypeFromString(const std::string& stype);
bool HandleInstallDestination();
};
#endif
......@@ -302,6 +302,8 @@ CMAKE_CXX_SOURCES="\
cmExprParserHelper \
cmExternalMakefileProjectGenerator \
cmFileCommand \
cmFileCopier \
cmFileInstaller \
cmFileTimeComparison \
cmFindBase \
cmFindCommon \
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment