diff options
-rw-r--r-- | Source/cmFileCommand.cxx | 121 | ||||
-rw-r--r-- | Source/cmFileCommand.h | 1 | ||||
-rw-r--r-- | Source/cmSystemTools.cxx | 16 | ||||
-rw-r--r-- | Source/cmSystemTools.h | 5 |
4 files changed, 143 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; +} diff --git a/Source/cmFileCommand.h b/Source/cmFileCommand.h index fe05c98..12c5115 100644 --- a/Source/cmFileCommand.h +++ b/Source/cmFileCommand.h @@ -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, diff --git a/Source/cmSystemTools.cxx b/Source/cmSystemTools.cxx index be65853..a1c8c03 100644 --- a/Source/cmSystemTools.cxx +++ b/Source/cmSystemTools.cxx @@ -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; +} diff --git a/Source/cmSystemTools.h b/Source/cmSystemTools.h index c0999e7..15f27e5 100644 --- a/Source/cmSystemTools.h +++ b/Source/cmSystemTools.h @@ -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; |