mirror of
https://github.com/Kitware/CMake.git
synced 2026-08-12 22:17:06 +00:00
The conversion between internal list representations led to empty directories being left behind when processing generator expressions in the destination. This ensures the intended behavior of creating a destination directory but installing nothing into it when no <dirs> are specified, regardless of the presence or absence of genex. Further, an explicit empty string <dir> (via `""`, or an undefined variable) led to the current source directory being expanded as a file to install, leading to the generation of an infinitely recursive install command. Disallow this behavior, forcing projects to explicitly specify the current source directory if that is their intention. Fixes: #27568
124 lines
4.2 KiB
C++
124 lines
4.2 KiB
C++
/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
|
|
file LICENSE.rst or https://cmake.org/licensing for details. */
|
|
#include "cmInstallDirectoryGenerator.h"
|
|
|
|
#include <algorithm>
|
|
#include <utility>
|
|
|
|
#include "cmGeneratorExpression.h"
|
|
#include "cmInstallType.h"
|
|
#include "cmList.h"
|
|
#include "cmListFileCache.h"
|
|
#include "cmLocalGenerator.h"
|
|
#include "cmMakefile.h"
|
|
#include "cmStringAlgorithms.h"
|
|
#include "cmSystemTools.h"
|
|
|
|
cmInstallDirectoryGenerator::cmInstallDirectoryGenerator(
|
|
std::vector<std::string> const& dirs, std::string const& dest,
|
|
std::string file_permissions, std::string dir_permissions,
|
|
std::vector<std::string> const& configurations, std::string const& component,
|
|
MessageLevel message, bool exclude_from_all, std::string literal_args,
|
|
bool optional, cmListFileBacktrace backtrace)
|
|
: cmInstallGenerator(dest, configurations, component, message,
|
|
exclude_from_all, false, std::move(backtrace))
|
|
, Directories(dirs)
|
|
, FilePermissions(std::move(file_permissions))
|
|
, DirPermissions(std::move(dir_permissions))
|
|
, LiteralArguments(std::move(literal_args))
|
|
, Optional(optional)
|
|
{
|
|
// We need per-config actions if destination have generator expressions.
|
|
if (cmGeneratorExpression::Find(this->Destination) != std::string::npos) {
|
|
this->ActionsPerConfig = true;
|
|
}
|
|
|
|
// We need per-config actions if any directories have generator expressions.
|
|
if (!this->ActionsPerConfig) {
|
|
for (std::string const& dir : dirs) {
|
|
if (cmGeneratorExpression::Find(dir) != std::string::npos) {
|
|
this->ActionsPerConfig = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
cmInstallDirectoryGenerator::~cmInstallDirectoryGenerator() = default;
|
|
|
|
bool cmInstallDirectoryGenerator::Compute(cmLocalGenerator* lg)
|
|
{
|
|
this->LocalGenerator = lg;
|
|
return true;
|
|
}
|
|
|
|
std::vector<std::string> cmInstallDirectoryGenerator::GetDirectories(
|
|
std::string const& config) const
|
|
{
|
|
// If given only empty directories, collapse into a single specification to
|
|
// avoid redundant calls. This supports the use case of installing an empty
|
|
// directory into a destination when a directory is not specified.
|
|
if (std::all_of(this->Directories.begin(), this->Directories.end(),
|
|
[](std::string const& d) { return d.empty(); })) {
|
|
return std::vector<std::string>{ "" };
|
|
}
|
|
cmList directories;
|
|
if (this->ActionsPerConfig) {
|
|
for (std::string const& f : this->Directories) {
|
|
directories.append(
|
|
cmGeneratorExpression::Evaluate(f, this->LocalGenerator, config));
|
|
}
|
|
} else {
|
|
directories = this->Directories;
|
|
}
|
|
return std::move(directories.data());
|
|
}
|
|
|
|
void cmInstallDirectoryGenerator::GenerateScriptActions(std::ostream& os,
|
|
Indent indent)
|
|
{
|
|
if (this->ActionsPerConfig) {
|
|
this->cmInstallGenerator::GenerateScriptActions(os, indent);
|
|
} else {
|
|
this->AddDirectoryInstallRule(os, "", indent, this->Directories);
|
|
}
|
|
}
|
|
|
|
void cmInstallDirectoryGenerator::GenerateScriptForConfig(
|
|
std::ostream& os, std::string const& config, Indent indent)
|
|
{
|
|
std::vector<std::string> dirs = this->GetDirectories(config);
|
|
|
|
if (!(dirs.size() == 1 && dirs[0].empty())) {
|
|
// Make sure all dirs have absolute paths.
|
|
cmMakefile const& mf = *this->LocalGenerator->GetMakefile();
|
|
for (std::string& d : dirs) {
|
|
if (!cmSystemTools::FileIsFullPath(d)) {
|
|
d = cmStrCat(mf.GetCurrentSourceDirectory(), '/', d);
|
|
}
|
|
}
|
|
}
|
|
|
|
this->AddDirectoryInstallRule(os, config, indent, dirs);
|
|
}
|
|
|
|
void cmInstallDirectoryGenerator::AddDirectoryInstallRule(
|
|
std::ostream& os, std::string const& config, Indent indent,
|
|
std::vector<std::string> const& dirs)
|
|
{
|
|
// Write code to install the directories.
|
|
char const* no_rename = nullptr;
|
|
this->AddInstallRule(os, this->GetDestination(config),
|
|
cmInstallType_DIRECTORY, dirs, this->Optional,
|
|
this->FilePermissions.c_str(),
|
|
this->DirPermissions.c_str(), no_rename,
|
|
this->LiteralArguments.c_str(), indent);
|
|
}
|
|
|
|
std::string cmInstallDirectoryGenerator::GetDestination(
|
|
std::string const& config) const
|
|
{
|
|
return cmGeneratorExpression::Evaluate(this->Destination,
|
|
this->LocalGenerator, config);
|
|
}
|