cmFileCommand: Add CREATE_LINK subcommand
This brings the functionality of `cmake -E create_symlink` and more to scripts. The default behavior is to create hard links. The `SYMBOLIC` argument can be used to create symlinks instead. The `COPY_ON_ERROR` argument enables a fallback to copying the file in case the link fails. The `RESULT <var>` retrieves the error message generated by the system. It is set to "0" on success. Fixes: #16926
This commit is contained in:
parent
c59eae7ebc
commit
81650e488c
@ -185,6 +185,9 @@ bool cmFileCommand::InitialPass(std::vector<std::string> const& args,
|
||||
if (subCommand == "READ_SYMLINK") {
|
||||
return this->HandleReadSymlinkCommand(args);
|
||||
}
|
||||
if (subCommand == "CREATE_LINK") {
|
||||
return this->HandleCreateLinkCommand(args);
|
||||
}
|
||||
|
||||
std::string e = "does not recognize sub-command " + subCommand;
|
||||
this->SetError(e);
|
||||
@ -3670,3 +3673,121 @@ bool cmFileCommand::HandleReadSymlinkCommand(
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool cmFileCommand::HandleCreateLinkCommand(
|
||||
std::vector<std::string> const& args)
|
||||
{
|
||||
if (args.size() < 3) {
|
||||
this->SetError("CREATE_LINK must be called with at least two additional "
|
||||
"arguments");
|
||||
return false;
|
||||
}
|
||||
|
||||
cmCommandArgumentsHelper argHelper;
|
||||
cmCommandArgumentGroup group;
|
||||
|
||||
cmCAString linkArg(&argHelper, "CREATE_LINK");
|
||||
cmCAString fileArg(&argHelper, nullptr);
|
||||
cmCAString newFileArg(&argHelper, nullptr);
|
||||
|
||||
cmCAString resultArg(&argHelper, "RESULT", &group);
|
||||
cmCAEnabler copyOnErrorArg(&argHelper, "COPY_ON_ERROR", &group);
|
||||
cmCAEnabler symbolicArg(&argHelper, "SYMBOLIC", &group);
|
||||
|
||||
linkArg.Follows(nullptr);
|
||||
fileArg.Follows(&linkArg);
|
||||
newFileArg.Follows(&fileArg);
|
||||
group.Follows(&newFileArg);
|
||||
|
||||
std::vector<std::string> unconsumedArgs;
|
||||
argHelper.Parse(&args, &unconsumedArgs);
|
||||
|
||||
if (!unconsumedArgs.empty()) {
|
||||
this->SetError("unknown argument: \"" + unconsumedArgs.front() + '\"');
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string fileName = fileArg.GetString();
|
||||
std::string newFileName = newFileArg.GetString();
|
||||
|
||||
// Output variable for storing the result.
|
||||
const std::string& resultVar = resultArg.GetString();
|
||||
|
||||
// The system error message generated in the operation.
|
||||
std::string result;
|
||||
|
||||
// Check if the paths are distinct.
|
||||
if (fileName == newFileName) {
|
||||
result = "CREATE_LINK cannot use same file and newfile";
|
||||
if (!resultVar.empty()) {
|
||||
this->Makefile->AddDefinition(resultVar, result.c_str());
|
||||
return true;
|
||||
}
|
||||
this->SetError(result);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Hard link requires original file to exist.
|
||||
if (!symbolicArg.IsEnabled() && !cmSystemTools::FileExists(fileName)) {
|
||||
result = "Cannot hard link \'" + fileName + "\' as it does not exist.";
|
||||
if (!resultVar.empty()) {
|
||||
this->Makefile->AddDefinition(resultVar, result.c_str());
|
||||
return true;
|
||||
}
|
||||
this->SetError(result);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if the new file already exists and remove it.
|
||||
if ((cmSystemTools::FileExists(newFileName) ||
|
||||
cmSystemTools::FileIsSymlink(newFileName)) &&
|
||||
!cmSystemTools::RemoveFile(newFileName)) {
|
||||
std::ostringstream e;
|
||||
e << "Failed to create link '" << newFileName
|
||||
<< "' because existing path cannot be removed: "
|
||||
<< cmSystemTools::GetLastSystemError() << "\n";
|
||||
|
||||
if (!resultVar.empty()) {
|
||||
this->Makefile->AddDefinition(resultVar, e.str().c_str());
|
||||
return true;
|
||||
}
|
||||
this->SetError(e.str());
|
||||
return false;
|
||||
}
|
||||
|
||||
// Whether the operation completed successfully.
|
||||
bool completed = false;
|
||||
|
||||
// Check if the command requires a symbolic link.
|
||||
if (symbolicArg.IsEnabled()) {
|
||||
completed = cmSystemTools::CreateSymlink(fileName, newFileName);
|
||||
} else {
|
||||
completed = cmSystemTools::CreateLink(fileName, newFileName);
|
||||
}
|
||||
|
||||
if (!completed) {
|
||||
// The link method did not succeed. Get the error message.
|
||||
result = "Link failed: " + cmSystemTools::GetLastSystemError();
|
||||
|
||||
// Check if copy-on-error is enabled in the arguments.
|
||||
if (copyOnErrorArg.IsEnabled()) {
|
||||
completed =
|
||||
cmSystemTools::cmCopyFile(fileName.c_str(), newFileName.c_str());
|
||||
if (!completed) {
|
||||
result = "Copy failed: " + cmSystemTools::GetLastSystemError();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check if the operation was successful.
|
||||
if (completed) {
|
||||
result = "0";
|
||||
}
|
||||
|
||||
if (!resultVar.empty()) {
|
||||
this->Makefile->AddDefinition(resultVar, result.c_str());
|
||||
return true;
|
||||
}
|
||||
|
||||
return completed;
|
||||
}
|
||||
|
@ -61,6 +61,7 @@ protected:
|
||||
bool HandleLockCommand(std::vector<std::string> const& args);
|
||||
bool HandleSizeCommand(std::vector<std::string> const& args);
|
||||
bool HandleReadSymlinkCommand(std::vector<std::string> const& args);
|
||||
bool HandleCreateLinkCommand(std::vector<std::string> const& args);
|
||||
|
||||
private:
|
||||
void AddEvaluationFile(const std::string& inputName,
|
||||
|
@ -3134,3 +3134,19 @@ bool cmSystemTools::CreateSymlink(const std::string& origName,
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool cmSystemTools::CreateLink(const std::string& origName,
|
||||
const std::string& newName)
|
||||
{
|
||||
uv_fs_t req;
|
||||
int err =
|
||||
uv_fs_link(nullptr, &req, origName.c_str(), newName.c_str(), nullptr);
|
||||
if (err) {
|
||||
std::string e =
|
||||
"failed to create link '" + newName + "': " + uv_strerror(err);
|
||||
cmSystemTools::Error(e.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -530,6 +530,11 @@ public:
|
||||
static bool CreateSymlink(const std::string& origName,
|
||||
const std::string& newName);
|
||||
|
||||
/** Create a hard link if the platform supports it. Returns whether
|
||||
creation succeeded. */
|
||||
static bool CreateLink(const std::string& origName,
|
||||
const std::string& newName);
|
||||
|
||||
private:
|
||||
static bool s_ForceUnixPaths;
|
||||
static bool s_RunCommandHideConsole;
|
||||
|
Loading…
Reference in New Issue
Block a user