summaryrefslogtreecommitdiffstats
path: root/Source/cmFileCommand.cxx
diff options
context:
space:
mode:
authorTushar Maheshwari <tushar27192@gmail.com>2018-12-27 11:58:30 (GMT)
committerBrad King <brad.king@kitware.com>2019-01-16 15:03:35 (GMT)
commit81650e488c734702384ef903630838015a3f81b1 (patch)
tree7391c238c012b6e34ed73130d588bf80a404122e /Source/cmFileCommand.cxx
parentc59eae7ebc5423c2b06befd762f8639b0f23b7a0 (diff)
downloadCMake-81650e488c734702384ef903630838015a3f81b1.zip
CMake-81650e488c734702384ef903630838015a3f81b1.tar.gz
CMake-81650e488c734702384ef903630838015a3f81b1.tar.bz2
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
Diffstat (limited to 'Source/cmFileCommand.cxx')
-rw-r--r--Source/cmFileCommand.cxx121
1 files changed, 121 insertions, 0 deletions
diff --git a/Source/cmFileCommand.cxx b/Source/cmFileCommand.cxx
index db2fde8..2cdf827 100644
--- a/Source/cmFileCommand.cxx
+++ b/Source/cmFileCommand.cxx
@@ -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;
+}