diff options
author | Tushar Maheshwari <tushar27192@gmail.com> | 2018-12-27 11:58:30 (GMT) |
---|---|---|
committer | Brad King <brad.king@kitware.com> | 2019-01-16 15:03:35 (GMT) |
commit | 81650e488c734702384ef903630838015a3f81b1 (patch) | |
tree | 7391c238c012b6e34ed73130d588bf80a404122e /Source/cmFileCommand.cxx | |
parent | c59eae7ebc5423c2b06befd762f8639b0f23b7a0 (diff) | |
download | CMake-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.cxx | 121 |
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; +} |