1
0
mirror of https://github.com/Kitware/CMake.git synced 2025-05-10 07:10:32 +08:00
CMake/Source/cmInstallExportGenerator.cxx
Matthew Woehlke 20fa4ce8d8 export: Factor out CMake-specific export generation (2/2)
In order to support generation of Common Package Specifications, the
mechanisms CMake uses to export package information need to be made more
abstract. The prior commits began this refactoring; this continues by
(actually) restructuring the classes used to generate the actual export files.
To minimize churn, this introduces virtual base classes and
diamond inheritance in order to separate logic which is format-agnostic
but depends on the export mode (build-tree versus install-tree) from
logic which is format-specific but mode-agnostic.

This could probably be refactored further to use helper classes instead,
and a future commit may do that, however an initial attempt to do that
was proving even more invasive, such that this approach was deemed more
manageable.

While we're at it, add 'const' in more places where possible.
2024-07-23 12:13:39 -04:00

251 lines
9.8 KiB
C++

/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
file Copyright.txt or https://cmake.org/licensing for details. */
#include "cmInstallExportGenerator.h"
#include <map>
#include <sstream>
#include <utility>
#include "cmCryptoHash.h"
#include "cmExportInstallFileGenerator.h"
#include "cmExportSet.h"
#include "cmInstallType.h"
#include "cmListFileCache.h"
#include "cmLocalGenerator.h"
#include "cmScriptGenerator.h"
#include "cmStringAlgorithms.h"
#include "cmSystemTools.h"
cmInstallExportGenerator::cmInstallExportGenerator(
cmExportSet* exportSet, std::string destination, std::string filePermissions,
std::vector<std::string> const& configurations, std::string component,
MessageLevel message, bool excludeFromAll, std::string filename,
std::string targetNamespace, std::string cxxModulesDirectory,
cmListFileBacktrace backtrace)
: cmInstallGenerator(std::move(destination), configurations,
std::move(component), message, excludeFromAll, false,
std::move(backtrace))
, ExportSet(exportSet)
, FilePermissions(std::move(filePermissions))
, FileName(std::move(filename))
, Namespace(std::move(targetNamespace))
, CxxModulesDirectory(std::move(cxxModulesDirectory))
{
exportSet->AddInstallation(this);
}
cmInstallExportGenerator::~cmInstallExportGenerator() = default;
bool cmInstallExportGenerator::Compute(cmLocalGenerator* lg)
{
this->LocalGenerator = lg;
return this->ExportSet->Compute(lg);
}
std::string cmInstallExportGenerator::TempDirCalculate() const
{
// Choose a temporary directory in which to generate the import
// files to be installed.
std::string path = cmStrCat(
this->LocalGenerator->GetCurrentBinaryDirectory(), "/CMakeFiles/Export");
if (this->Destination.empty()) {
return path;
}
cmCryptoHash hasher(cmCryptoHash::AlgoMD5);
path += '/';
// Replace the destination path with a hash to keep it short.
path += hasher.HashString(this->Destination);
return path;
}
void cmInstallExportGenerator::ComputeTempDir()
{
this->TempDir = this->TempDirCalculate();
}
std::string cmInstallExportGenerator::GetTempDir() const
{
if (this->TempDir.empty()) {
return this->TempDirCalculate();
}
return this->TempDir;
}
void cmInstallExportGenerator::GenerateScript(std::ostream& os)
{
// Skip empty sets.
if (this->ExportSet->GetTargetExports().empty()) {
std::ostringstream e;
e << "INSTALL(" << this->InstallSubcommand() << ") given unknown export \""
<< this->ExportSet->GetName() << "\"";
cmSystemTools::Error(e.str());
return;
}
// Create the temporary directory in which to store the files.
this->ComputeTempDir();
cmSystemTools::MakeDirectory(this->TempDir);
// Construct a temporary location for the file.
this->MainImportFile = cmStrCat(this->TempDir, '/', this->FileName);
// Generate the import file for this export set.
this->EFGen->SetExportFile(this->MainImportFile.c_str());
this->EFGen->SetNamespace(this->Namespace);
if (this->ConfigurationTypes->empty()) {
if (!this->ConfigurationName.empty()) {
this->EFGen->AddConfiguration(this->ConfigurationName);
} else {
this->EFGen->AddConfiguration("");
}
} else {
for (std::string const& c : *this->ConfigurationTypes) {
this->EFGen->AddConfiguration(c);
}
}
this->EFGen->GenerateImportFile();
// Perform the main install script generation.
this->cmInstallGenerator::GenerateScript(os);
}
void cmInstallExportGenerator::GenerateScriptConfigs(std::ostream& os,
Indent indent)
{
// Create the main install rules first.
this->cmInstallGenerator::GenerateScriptConfigs(os, indent);
// Now create a configuration-specific install rule for the import
// file of each configuration.
std::vector<std::string> files;
for (auto const& i : this->EFGen->GetConfigImportFiles()) {
files.push_back(i.second);
std::string config_test = this->CreateConfigTest(i.first);
os << indent << "if(" << config_test << ")\n";
this->AddInstallRule(os, this->Destination, cmInstallType_FILES, files,
false, this->FilePermissions.c_str(), nullptr,
nullptr, nullptr, indent.Next());
os << indent << "endif()\n";
files.clear();
}
// Now create a configuration-specific install rule for the C++ module import
// property file of each configuration.
auto const cxxModuleDestination =
cmStrCat(this->Destination, '/', this->CxxModulesDirectory);
auto const cxxModuleInstallFilePath = this->EFGen->GetCxxModuleFile();
auto const configImportFilesGlob = this->EFGen->GetConfigImportFileGlob();
if (!cxxModuleInstallFilePath.empty() && !configImportFilesGlob.empty()) {
auto const cxxModuleFilename =
cmSystemTools::GetFilenameName(cxxModuleInstallFilePath);
// Remove old per-configuration export files if the main changes.
std::string installedDir =
cmStrCat("$ENV{DESTDIR}",
ConvertToAbsoluteDestination(cxxModuleDestination), '/');
std::string installedFile = cmStrCat(installedDir, cxxModuleFilename);
os << indent << "if(EXISTS \"" << installedFile << "\")\n";
Indent indentN = indent.Next();
Indent indentNN = indentN.Next();
Indent indentNNN = indentNN.Next();
os << indentN << "file(DIFFERENT _cmake_export_file_changed FILES\n"
<< indentN << " \"" << installedFile << "\"\n"
<< indentN << " \"" << cxxModuleInstallFilePath << "\")\n";
os << indentN << "if(_cmake_export_file_changed)\n";
os << indentNN << "file(GLOB _cmake_old_config_files \"" << installedDir
<< configImportFilesGlob << "\")\n";
os << indentNN << "if(_cmake_old_config_files)\n";
os << indentNNN
<< "string(REPLACE \";\" \", \" _cmake_old_config_files_text "
"\"${_cmake_old_config_files}\")\n";
os << indentNNN << R"(message(STATUS "Old C++ module export file \")"
<< installedFile
<< "\\\" will be replaced. "
"Removing files [${_cmake_old_config_files_text}].\")\n";
os << indentNNN << "unset(_cmake_old_config_files_text)\n";
os << indentNNN << "file(REMOVE ${_cmake_old_config_files})\n";
os << indentNN << "endif()\n";
os << indentNN << "unset(_cmake_old_config_files)\n";
os << indentN << "endif()\n";
os << indentN << "unset(_cmake_export_file_changed)\n";
os << indent << "endif()\n";
// All of these files are siblings; get its location to know where the
// "anchor" file is.
files.push_back(cxxModuleInstallFilePath);
this->AddInstallRule(os, cxxModuleDestination, cmInstallType_FILES, files,
false, this->FilePermissions.c_str(), nullptr,
nullptr, nullptr, indent);
files.clear();
}
for (auto const& i : this->EFGen->GetConfigCxxModuleFiles()) {
files.push_back(i.second);
std::string config_test = this->CreateConfigTest(i.first);
os << indent << "if(" << config_test << ")\n";
this->AddInstallRule(os, cxxModuleDestination, cmInstallType_FILES, files,
false, this->FilePermissions.c_str(), nullptr,
nullptr, nullptr, indent.Next());
os << indent << "endif()\n";
files.clear();
}
for (auto const& i : this->EFGen->GetConfigCxxModuleTargetFiles()) {
std::string config_test = this->CreateConfigTest(i.first);
os << indent << "if(" << config_test << ")\n";
this->AddInstallRule(os, cxxModuleDestination, cmInstallType_FILES,
i.second, false, this->FilePermissions.c_str(),
nullptr, nullptr, nullptr, indent.Next());
os << indent << "endif()\n";
files.clear();
}
}
void cmInstallExportGenerator::GenerateScriptActions(std::ostream& os,
Indent indent)
{
auto const configImportFilesGlob = this->EFGen->GetConfigImportFileGlob();
if (!configImportFilesGlob.empty()) {
// Remove old per-configuration export files if the main changes.
std::string installedDir = cmStrCat(
"$ENV{DESTDIR}", ConvertToAbsoluteDestination(this->Destination), '/');
std::string installedFile = cmStrCat(installedDir, this->FileName);
os << indent << "if(EXISTS \"" << installedFile << "\")\n";
Indent indentN = indent.Next();
Indent indentNN = indentN.Next();
Indent indentNNN = indentNN.Next();
os << indentN << "file(DIFFERENT _cmake_export_file_changed FILES\n"
<< indentN << " \"" << installedFile << "\"\n"
<< indentN << " \"" << this->MainImportFile << "\")\n";
os << indentN << "if(_cmake_export_file_changed)\n";
os << indentNN << "file(GLOB _cmake_old_config_files \"" << installedDir
<< configImportFilesGlob << "\")\n";
os << indentNN << "if(_cmake_old_config_files)\n";
os << indentNNN
<< "string(REPLACE \";\" \", \" _cmake_old_config_files_text "
"\"${_cmake_old_config_files}\")\n";
os << indentNNN << R"(message(STATUS "Old export file \")" << installedFile
<< "\\\" will be replaced. "
"Removing files [${_cmake_old_config_files_text}].\")\n";
os << indentNNN << "unset(_cmake_old_config_files_text)\n";
os << indentNNN << "file(REMOVE ${_cmake_old_config_files})\n";
os << indentNN << "endif()\n";
os << indentNN << "unset(_cmake_old_config_files)\n";
os << indentN << "endif()\n";
os << indentN << "unset(_cmake_export_file_changed)\n";
os << indent << "endif()\n";
}
// Install the main export file.
std::vector<std::string> files;
files.push_back(this->MainImportFile);
this->AddInstallRule(os, this->Destination, cmInstallType_FILES, files,
false, this->FilePermissions.c_str(), nullptr, nullptr,
nullptr, indent);
}
std::string cmInstallExportGenerator::GetDestinationFile() const
{
return this->Destination + '/' + this->FileName;
}