diff options
Diffstat (limited to 'Source/cmFileInstaller.cxx')
-rw-r--r-- | Source/cmFileInstaller.cxx | 118 |
1 files changed, 118 insertions, 0 deletions
diff --git a/Source/cmFileInstaller.cxx b/Source/cmFileInstaller.cxx index c89be96..0d8ba2d 100644 --- a/Source/cmFileInstaller.cxx +++ b/Source/cmFileInstaller.cxx @@ -3,7 +3,12 @@ #include "cmFileInstaller.h" +#include <map> #include <sstream> +#include <utility> + +#include <cm/string_view> +#include <cmext/string_view> #include "cm_sys_stat.h" @@ -18,6 +23,7 @@ using namespace cmFSPermissions; cmFileInstaller::cmFileInstaller(cmExecutionStatus& status) : cmFileCopier(status, "INSTALL") , InstallType(cmInstallType_FILES) + , InstallMode(cmInstallMode::COPY) , Optional(false) , MessageAlways(false) , MessageLazy(false) @@ -82,6 +88,93 @@ bool cmFileInstaller::Install(const std::string& fromFile, return this->cmFileCopier::Install(fromFile, toFile); } +bool cmFileInstaller::InstallFile(const std::string& fromFile, + const std::string& toFile, + MatchProperties match_properties) +{ + if (this->InstallMode == cmInstallMode::COPY) { + return this->cmFileCopier::InstallFile(fromFile, toFile, match_properties); + } + + std::string newFromFile; + + if (this->InstallMode == cmInstallMode::REL_SYMLINK || + this->InstallMode == cmInstallMode::REL_SYMLINK_OR_COPY || + this->InstallMode == cmInstallMode::SYMLINK || + this->InstallMode == cmInstallMode::SYMLINK_OR_COPY) { + // Try to get a relative path. + std::string toDir = cmSystemTools::GetParentDirectory(toFile); + newFromFile = cmSystemTools::ForceToRelativePath(toDir, fromFile); + + // Double check that we can restore the original path. + std::string reassembled = + cmSystemTools::CollapseFullPath(newFromFile, toDir); + if (!cmSystemTools::ComparePath(reassembled, fromFile)) { + if (this->InstallMode == cmInstallMode::SYMLINK || + this->InstallMode == cmInstallMode::SYMLINK_OR_COPY) { + // User does not mind, silently proceed with absolute path. + newFromFile = fromFile; + } else if (this->InstallMode == cmInstallMode::REL_SYMLINK_OR_COPY) { + // User expects a relative symbolic link or a copy. + // Since an absolute symlink won't do, copy instead. + return this->cmFileCopier::InstallFile(fromFile, toFile, + match_properties); + } else { + // We cannot meet user's expectation (REL_SYMLINK) + auto e = cmStrCat(this->Name, + " cannot determine relative path for symlink to \"", + newFromFile, "\" at \"", toFile, "\"."); + this->Status.SetError(e); + return false; + } + } + } else { + newFromFile = fromFile; // stick with absolute path + } + + // Compare the symlink value to that at the destination if not + // always installing. + bool copy = true; + if (!this->Always) { + std::string oldSymlinkTarget; + if (cmSystemTools::ReadSymlink(toFile, oldSymlinkTarget)) { + if (newFromFile == oldSymlinkTarget) { + copy = false; + } + } + } + + // Inform the user about this file installation. + this->ReportCopy(toFile, TypeLink, copy); + + if (copy) { + // Remove the destination file so we can always create the symlink. + cmSystemTools::RemoveFile(toFile); + + // Create destination directory if it doesn't exist + cmSystemTools::MakeDirectory(cmSystemTools::GetFilenamePath(toFile)); + + // Create the symlink. + if (!cmSystemTools::CreateSymlink(newFromFile, toFile)) { + if (this->InstallMode == cmInstallMode::ABS_SYMLINK_OR_COPY || + this->InstallMode == cmInstallMode::REL_SYMLINK_OR_COPY || + this->InstallMode == cmInstallMode::SYMLINK_OR_COPY) { + // Failed to create a symbolic link, fall back to copying. + return this->cmFileCopier::InstallFile(newFromFile, toFile, + match_properties); + } + + auto e = cmStrCat(this->Name, " cannot create symlink to \"", + newFromFile, "\" at \"", toFile, + "\": ", cmSystemTools::GetLastSystemError(), "\"."); + this->Status.SetError(e); + return false; + } + } + + return true; +} + void cmFileInstaller::DefaultFilePermissions() { this->cmFileCopier::DefaultFilePermissions(); @@ -141,6 +234,31 @@ bool cmFileInstaller::Parse(std::vector<std::string> const& args) return false; } + static const std::map<cm::string_view, cmInstallMode> install_mode_dict{ + { "ABS_SYMLINK"_s, cmInstallMode::ABS_SYMLINK }, + { "ABS_SYMLINK_OR_COPY"_s, cmInstallMode::ABS_SYMLINK_OR_COPY }, + { "REL_SYMLINK"_s, cmInstallMode::REL_SYMLINK }, + { "REL_SYMLINK_OR_COPY"_s, cmInstallMode::REL_SYMLINK_OR_COPY }, + { "SYMLINK"_s, cmInstallMode::SYMLINK }, + { "SYMLINK_OR_COPY"_s, cmInstallMode::SYMLINK_OR_COPY } + }; + + std::string install_mode; + cmSystemTools::GetEnv("CMAKE_INSTALL_MODE", install_mode); + if (install_mode.empty() || install_mode == "COPY"_s) { + this->InstallMode = cmInstallMode::COPY; + } else { + auto it = install_mode_dict.find(install_mode); + if (it != install_mode_dict.end()) { + this->InstallMode = it->second; + } else { + auto e = cmStrCat("Unrecognized value '", install_mode, + "' for environment variable CMAKE_INSTALL_MODE"); + this->Status.SetError(e); + return false; + } + } + return true; } |