cmTarget: Add cmFileSet and associated properties

This commit is contained in:
Kyle Edwards 2021-06-16 17:09:59 -04:00
parent 447fbf061a
commit f2a44a8afa
6 changed files with 553 additions and 1 deletions

View File

@ -261,6 +261,8 @@ set(SRCS
cmFileLockResult.h
cmFilePathChecksum.cxx
cmFilePathChecksum.h
cmFileSet.cxx
cmFileSet.h
cmFileTime.cxx
cmFileTime.h
cmFileTimeCache.cxx

151
Source/cmFileSet.cxx Normal file
View File

@ -0,0 +1,151 @@
/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
file Copyright.txt or https://cmake.org/licensing for details. */
#include "cmFileSet.h"
#include <sstream>
#include <string>
#include <utility>
#include <vector>
#include "cmGeneratorExpression.h"
#include "cmListFileCache.h"
#include "cmLocalGenerator.h"
#include "cmMessageType.h"
#include "cmStringAlgorithms.h"
#include "cmSystemTools.h"
#include "cmake.h"
cmFileSet::cmFileSet(std::string name, std::string type)
: Name(std::move(name))
, Type(std::move(type))
{
}
void cmFileSet::ClearDirectoryEntries()
{
this->DirectoryEntries.clear();
}
void cmFileSet::AddDirectoryEntry(BT<std::string> directories)
{
this->DirectoryEntries.push_back(std::move(directories));
}
void cmFileSet::ClearFileEntries()
{
this->FileEntries.clear();
}
void cmFileSet::AddFileEntry(BT<std::string> files)
{
this->FileEntries.push_back(std::move(files));
}
std::vector<std::unique_ptr<cmCompiledGeneratorExpression>>
cmFileSet::CompileFileEntries() const
{
std::vector<std::unique_ptr<cmCompiledGeneratorExpression>> result;
for (auto const& entry : this->FileEntries) {
for (auto const& ex : cmExpandedList(entry.Value)) {
cmGeneratorExpression ge(entry.Backtrace);
auto cge = ge.Parse(ex);
result.push_back(std::move(cge));
}
}
return result;
}
std::vector<std::unique_ptr<cmCompiledGeneratorExpression>>
cmFileSet::CompileDirectoryEntries() const
{
std::vector<std::unique_ptr<cmCompiledGeneratorExpression>> result;
for (auto const& entry : this->DirectoryEntries) {
for (auto const& ex : cmExpandedList(entry.Value)) {
cmGeneratorExpression ge(entry.Backtrace);
auto cge = ge.Parse(ex);
result.push_back(std::move(cge));
}
}
return result;
}
std::vector<std::string> cmFileSet::EvaluateDirectoryEntries(
const std::vector<std::unique_ptr<cmCompiledGeneratorExpression>>& cges,
cmLocalGenerator* lg, const std::string& config,
const cmGeneratorTarget* target,
cmGeneratorExpressionDAGChecker* dagChecker) const
{
std::vector<std::string> result;
for (auto const& cge : cges) {
auto entry = cge->Evaluate(lg, config, target, dagChecker);
auto dirs = cmExpandedList(entry);
for (std::string dir : dirs) {
if (!cmSystemTools::FileIsFullPath(dir)) {
dir = cmStrCat(lg->GetCurrentSourceDirectory(), '/', dir);
}
auto collapsedDir = cmSystemTools::CollapseFullPath(dir);
for (auto const& priorDir : result) {
auto collapsedPriorDir = cmSystemTools::CollapseFullPath(priorDir);
if (!cmSystemTools::SameFile(collapsedDir, collapsedPriorDir) &&
(cmSystemTools::IsSubDirectory(collapsedDir, collapsedPriorDir) ||
cmSystemTools::IsSubDirectory(collapsedPriorDir, collapsedDir))) {
lg->GetCMakeInstance()->IssueMessage(
MessageType::FATAL_ERROR,
cmStrCat(
"Base directories in file set cannot be subdirectories of each "
"other:\n ",
priorDir, "\n ", dir),
cge->GetBacktrace());
return {};
}
}
result.push_back(dir);
}
}
return result;
}
void cmFileSet::EvaluateFileEntry(
const std::vector<std::string>& dirs,
std::map<std::string, std::vector<std::string>>& filesPerDir,
const std::unique_ptr<cmCompiledGeneratorExpression>& cge,
cmLocalGenerator* lg, const std::string& config,
const cmGeneratorTarget* target,
cmGeneratorExpressionDAGChecker* dagChecker) const
{
auto files = cge->Evaluate(lg, config, target, dagChecker);
for (std::string file : cmExpandedList(files)) {
if (!cmSystemTools::FileIsFullPath(file)) {
file = cmStrCat(lg->GetCurrentSourceDirectory(), '/', file);
}
auto collapsedFile = cmSystemTools::CollapseFullPath(file);
bool found = false;
std::string relDir;
for (auto const& dir : dirs) {
auto collapsedDir = cmSystemTools::CollapseFullPath(dir);
if (cmSystemTools::IsSubDirectory(collapsedFile, collapsedDir)) {
found = true;
relDir = cmSystemTools::GetParentDirectory(
cmSystemTools::RelativePath(collapsedDir, collapsedFile));
break;
}
}
if (!found) {
std::ostringstream e;
e << "File:\n " << file
<< "\nmust be in one of the file set's base directories:";
for (auto const& dir : dirs) {
e << "\n " << dir;
}
lg->GetCMakeInstance()->IssueMessage(MessageType::FATAL_ERROR, e.str(),
cge->GetBacktrace());
return;
}
filesPerDir[relDir].push_back(file);
}
}

64
Source/cmFileSet.h Normal file
View File

@ -0,0 +1,64 @@
/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
file Copyright.txt or https://cmake.org/licensing for details. */
#pragma once
#include <map>
#include <memory>
#include <string>
#include <vector>
#include "cmListFileCache.h"
class cmCompiledGeneratorExpression;
struct cmGeneratorExpressionDAGChecker;
class cmGeneratorTarget;
class cmLocalGenerator;
class cmFileSet
{
public:
cmFileSet(std::string name, std::string type);
const std::string& GetName() const { return this->Name; }
const std::string& GetType() const { return this->Type; }
void ClearDirectoryEntries();
void AddDirectoryEntry(BT<std::string> directories);
const std::vector<BT<std::string>>& GetDirectoryEntries() const
{
return this->DirectoryEntries;
}
void ClearFileEntries();
void AddFileEntry(BT<std::string> files);
const std::vector<BT<std::string>>& GetFileEntries() const
{
return this->FileEntries;
}
std::vector<std::unique_ptr<cmCompiledGeneratorExpression>>
CompileFileEntries() const;
std::vector<std::unique_ptr<cmCompiledGeneratorExpression>>
CompileDirectoryEntries() const;
std::vector<std::string> EvaluateDirectoryEntries(
const std::vector<std::unique_ptr<cmCompiledGeneratorExpression>>& cges,
cmLocalGenerator* lg, const std::string& config,
const cmGeneratorTarget* target,
cmGeneratorExpressionDAGChecker* dagChecker = nullptr) const;
void EvaluateFileEntry(
const std::vector<std::string>& dirs,
std::map<std::string, std::vector<std::string>>& filesPerDir,
const std::unique_ptr<cmCompiledGeneratorExpression>& cge,
cmLocalGenerator* lg, const std::string& config,
const cmGeneratorTarget* target,
cmGeneratorExpressionDAGChecker* dagChecker = nullptr) const;
private:
std::string Name;
std::string Type;
std::vector<BT<std::string>> DirectoryEntries;
std::vector<BT<std::string>> FileEntries;
};

View File

@ -14,11 +14,13 @@
#include <cm/memory>
#include <cmext/algorithm>
#include <cmext/string_view>
#include "cmsys/RegularExpression.hxx"
#include "cmAlgorithms.h"
#include "cmCustomCommand.h"
#include "cmFileSet.h"
#include "cmGeneratorExpression.h"
#include "cmGeneratorTarget.h"
#include "cmGlobalGenerator.h"
@ -200,8 +202,11 @@ public:
std::vector<BT<std::string>> LinkOptionsEntries;
std::vector<BT<std::string>> LinkDirectoriesEntries;
std::vector<BT<std::string>> LinkImplementationPropertyEntries;
std::vector<BT<std::string>> HeaderSetsEntries;
std::vector<BT<std::string>> InterfaceHeaderSetsEntries;
std::vector<std::pair<cmTarget::TLLSignature, cmListFileContext>>
TLLCommands;
std::map<std::string, cmFileSet> FileSets;
cmListFileBacktrace Backtrace;
bool CheckImportedLibName(std::string const& prop,
@ -1110,6 +1115,16 @@ cmBTStringRange cmTarget::GetLinkImplementationEntries() const
return cmMakeRange(this->impl->LinkImplementationPropertyEntries);
}
cmBTStringRange cmTarget::GetHeaderSetsEntries() const
{
return cmMakeRange(this->impl->HeaderSetsEntries);
}
cmBTStringRange cmTarget::GetInterfaceHeaderSetsEntries() const
{
return cmMakeRange(this->impl->InterfaceHeaderSetsEntries);
}
namespace {
#define MAKE_PROP(PROP) const std::string prop##PROP = #PROP
MAKE_PROP(C_STANDARD);
@ -1139,6 +1154,10 @@ MAKE_PROP(BINARY_DIR);
MAKE_PROP(SOURCE_DIR);
MAKE_PROP(FALSE);
MAKE_PROP(TRUE);
MAKE_PROP(HEADER_DIRS);
MAKE_PROP(HEADER_SET);
MAKE_PROP(HEADER_SETS);
MAKE_PROP(INTERFACE_HEADER_SETS);
#undef MAKE_PROP
}
@ -1158,6 +1177,21 @@ std::string ConvertToString<cmValue>(cmValue value)
{
return std::string(*value);
}
template <typename ValueType>
bool StringIsEmpty(ValueType value);
template <>
bool StringIsEmpty<const char*>(const char* value)
{
return cmValue::IsEmpty(value);
}
template <>
bool StringIsEmpty<cmValue>(cmValue value)
{
return value.IsEmpty();
}
}
template <typename ValueType>
@ -1321,6 +1355,104 @@ void cmTarget::StoreProperty(const std::string& prop, ValueType value)
} else {
this->impl->LanguageStandardProperties.erase(prop);
}
} else if (prop == propHEADER_DIRS) {
auto* fileSet = this->GetFileSet("HEADERS");
if (!fileSet) {
this->impl->Makefile->IssueMessage(
MessageType::FATAL_ERROR,
"The default header set has not yet been created.");
return;
}
fileSet->ClearDirectoryEntries();
if (!StringIsEmpty(value)) {
fileSet->AddDirectoryEntry(
BT<std::string>(value, this->impl->Makefile->GetBacktrace()));
}
} else if (prop == propHEADER_SET) {
auto* fileSet = this->GetFileSet("HEADERS");
if (!fileSet) {
this->impl->Makefile->IssueMessage(
MessageType::FATAL_ERROR,
"The default header set has not yet been created.");
return;
}
fileSet->ClearFileEntries();
if (!StringIsEmpty(value)) {
fileSet->AddFileEntry(
BT<std::string>(value, this->impl->Makefile->GetBacktrace()));
}
} else if (cmHasLiteralPrefix(prop, "HEADER_DIRS_")) {
auto fileSetName = prop.substr(cmStrLen("HEADER_DIRS_"));
if (fileSetName.empty()) {
this->impl->Makefile->IssueMessage(MessageType::FATAL_ERROR,
"Header set name cannot be empty.");
return;
}
auto* fileSet = this->GetFileSet(fileSetName);
if (!fileSet) {
this->impl->Makefile->IssueMessage(
MessageType::FATAL_ERROR,
cmStrCat("Header set \"", fileSetName,
"\" has not yet been created."));
return;
}
fileSet->ClearDirectoryEntries();
if (!StringIsEmpty(value)) {
fileSet->AddDirectoryEntry(
BT<std::string>(value, this->impl->Makefile->GetBacktrace()));
}
} else if (cmHasLiteralPrefix(prop, "HEADER_SET_")) {
auto fileSetName = prop.substr(cmStrLen("HEADER_SET_"));
if (fileSetName.empty()) {
this->impl->Makefile->IssueMessage(MessageType::FATAL_ERROR,
"Header set name cannot be empty.");
return;
}
auto* fileSet = this->GetFileSet(fileSetName);
if (!fileSet) {
this->impl->Makefile->IssueMessage(
MessageType::FATAL_ERROR,
cmStrCat("Header set \"", fileSetName,
"\" has not yet been created."));
return;
}
fileSet->ClearFileEntries();
if (!StringIsEmpty(value)) {
fileSet->AddFileEntry(
BT<std::string>(value, this->impl->Makefile->GetBacktrace()));
}
} else if (prop == propHEADER_SETS) {
if (value) {
for (auto const& name : cmExpandedList(value)) {
if (!this->GetFileSet(name)) {
this->impl->Makefile->IssueMessage(
MessageType::FATAL_ERROR,
cmStrCat("Header set \"", name, "\" has not yet been created."));
return;
}
}
}
this->impl->HeaderSetsEntries.clear();
if (!StringIsEmpty(value)) {
this->impl->HeaderSetsEntries.emplace_back(
value, this->impl->Makefile->GetBacktrace());
}
} else if (prop == propINTERFACE_HEADER_SETS) {
if (value) {
for (auto const& name : cmExpandedList(value)) {
if (!this->GetFileSet(name)) {
this->impl->Makefile->IssueMessage(
MessageType::FATAL_ERROR,
cmStrCat("Header set \"", name, "\" has not yet been created."));
return;
}
}
}
this->impl->InterfaceHeaderSetsEntries.clear();
if (!StringIsEmpty(value)) {
this->impl->InterfaceHeaderSetsEntries.emplace_back(
value, this->impl->Makefile->GetBacktrace());
}
} else {
this->impl->Properties.SetProperty(prop, value);
}
@ -1415,6 +1547,82 @@ void cmTarget::AppendProperty(const std::string& prop,
prop == "OBJC_STANDARD" || prop == "OBJCXX_STANDARD") {
this->impl->Makefile->IssueMessage(
MessageType::FATAL_ERROR, prop + " property may not be appended.");
} else if (prop == "HEADER_DIRS") {
auto* fileSet = this->GetFileSet("HEADERS");
if (!fileSet) {
this->impl->Makefile->IssueMessage(
MessageType::FATAL_ERROR,
"The default header set has not yet been created.");
return;
}
fileSet->AddDirectoryEntry(
BT<std::string>(value, this->impl->Makefile->GetBacktrace()));
} else if (cmHasLiteralPrefix(prop, "HEADER_DIRS_")) {
auto fileSetName = prop.substr(cmStrLen("HEADER_DIRS_"));
if (fileSetName.empty()) {
this->impl->Makefile->IssueMessage(MessageType::FATAL_ERROR,
"Header set name cannot be empty.");
return;
}
auto* fileSet = this->GetFileSet(fileSetName);
if (!fileSet) {
this->impl->Makefile->IssueMessage(
MessageType::FATAL_ERROR,
cmStrCat("Header set \"", fileSetName,
"\" has not yet been created."));
return;
}
fileSet->AddDirectoryEntry(
BT<std::string>(value, this->impl->Makefile->GetBacktrace()));
} else if (prop == "HEADER_SET") {
auto* fileSet = this->GetFileSet("HEADERS");
if (!fileSet) {
this->impl->Makefile->IssueMessage(
MessageType::FATAL_ERROR,
"The default header set has not yet been created.");
return;
}
fileSet->AddFileEntry(
BT<std::string>(value, this->impl->Makefile->GetBacktrace()));
} else if (cmHasLiteralPrefix(prop, "HEADER_SET_")) {
auto fileSetName = prop.substr(cmStrLen("HEADER_SET_"));
if (fileSetName.empty()) {
this->impl->Makefile->IssueMessage(MessageType::FATAL_ERROR,
"Header set name cannot be empty.");
return;
}
auto* fileSet = this->GetFileSet(fileSetName);
if (!fileSet) {
this->impl->Makefile->IssueMessage(
MessageType::FATAL_ERROR,
cmStrCat("Header set \"", fileSetName,
"\" has not yet been created."));
return;
}
fileSet->AddFileEntry(
BT<std::string>(value, this->impl->Makefile->GetBacktrace()));
} else if (prop == "HEADER_SETS") {
for (auto const& name : cmExpandedList(value)) {
if (!this->GetFileSet(name)) {
this->impl->Makefile->IssueMessage(
MessageType::FATAL_ERROR,
cmStrCat("Header set \"", name, "\" has not yet been created."));
return;
}
}
this->impl->HeaderSetsEntries.emplace_back(
value, this->impl->Makefile->GetBacktrace());
} else if (prop == "INTERFACE_HEADER_SETS") {
for (auto const& name : cmExpandedList(value)) {
if (!this->GetFileSet(name)) {
this->impl->Makefile->IssueMessage(
MessageType::FATAL_ERROR,
cmStrCat("Header set \"", name, "\" has not yet been created."));
return;
}
}
this->impl->InterfaceHeaderSetsEntries.emplace_back(
value, this->impl->Makefile->GetBacktrace());
} else {
this->impl->Properties.AppendProperty(prop, value, asString);
}
@ -1633,7 +1841,11 @@ cmValue cmTarget::GetProperty(const std::string& prop) const
propNAME,
propBINARY_DIR,
propSOURCE_DIR,
propSOURCES
propSOURCES,
propHEADER_DIRS,
propHEADER_SET,
propHEADER_SETS,
propINTERFACE_HEADER_SETS,
};
if (specialProps.count(prop)) {
if (prop == propC_STANDARD || prop == propCXX_STANDARD ||
@ -1759,6 +1971,60 @@ cmValue cmTarget::GetProperty(const std::string& prop) const
.GetDirectory()
.GetCurrentSource());
}
if (prop == propHEADER_DIRS) {
auto const* fileSet = this->GetFileSet("HEADERS");
if (!fileSet) {
return nullptr;
}
static std::string output;
output = cmJoin(fileSet->GetDirectoryEntries(), ";"_s);
return cmValue(output);
}
if (prop == propHEADER_SET) {
auto const* fileSet = this->GetFileSet("HEADERS");
if (!fileSet) {
return nullptr;
}
static std::string output;
output = cmJoin(fileSet->GetFileEntries(), ";"_s);
return cmValue(output);
}
if (prop == propHEADER_SETS) {
static std::string output;
output = cmJoin(this->impl->HeaderSetsEntries, ";"_s);
return cmValue(output);
}
if (prop == propINTERFACE_HEADER_SETS) {
static std::string output;
output = cmJoin(this->impl->InterfaceHeaderSetsEntries, ";"_s);
return cmValue(output);
}
}
if (cmHasLiteralPrefix(prop, "HEADER_DIRS_")) {
std::string fileSetName = prop.substr(cmStrLen("HEADER_DIRS_"));
if (fileSetName.empty()) {
return nullptr;
}
auto const* fileSet = this->GetFileSet(fileSetName);
if (!fileSet) {
return nullptr;
}
static std::string output;
output = cmJoin(fileSet->GetDirectoryEntries(), ";"_s);
return cmValue(output);
}
if (cmHasLiteralPrefix(prop, "HEADER_SET_")) {
std::string fileSetName = prop.substr(cmStrLen("HEADER_SET_"));
if (fileSetName.empty()) {
return nullptr;
}
auto const* fileSet = this->GetFileSet(fileSetName);
if (!fileSet) {
return nullptr;
}
static std::string output;
output = cmJoin(fileSet->GetFileEntries(), ";"_s);
return cmValue(output);
}
cmValue retVal = this->impl->Properties.GetPropertyValue(prop);
@ -2015,6 +2281,59 @@ std::string cmTarget::ImportedGetFullPath(
return result;
}
const cmFileSet* cmTarget::GetFileSet(const std::string& name) const
{
auto it = this->impl->FileSets.find(name);
return it == this->impl->FileSets.end() ? nullptr : &it->second;
}
cmFileSet* cmTarget::GetFileSet(const std::string& name)
{
auto it = this->impl->FileSets.find(name);
return it == this->impl->FileSets.end() ? nullptr : &it->second;
}
std::pair<cmFileSet*, bool> cmTarget::GetOrCreateFileSet(
const std::string& name, const std::string& type)
{
auto result =
this->impl->FileSets.emplace(std::make_pair(name, cmFileSet(name, type)));
return std::make_pair(&result.first->second, result.second);
}
std::string cmTarget::GetFileSetsPropertyName(const std::string& type)
{
if (type == "HEADERS") {
return "HEADER_SETS";
}
return "";
}
std::string cmTarget::GetInterfaceFileSetsPropertyName(const std::string& type)
{
if (type == "HEADERS") {
return "INTERFACE_HEADER_SETS";
}
return "";
}
std::vector<std::string> cmTarget::GetAllInterfaceFileSets() const
{
std::vector<std::string> result;
auto inserter = std::back_inserter(result);
auto appendEntries = [=](const std::vector<BT<std::string>>& entries) {
for (auto const& entry : entries) {
auto expanded = cmExpandedList(entry.Value);
std::copy(expanded.begin(), expanded.end(), inserter);
}
};
appendEntries(this->impl->InterfaceHeaderSetsEntries);
return result;
}
bool cmTargetInternals::CheckImportedLibName(std::string const& prop,
std::string const& value) const
{

View File

@ -20,6 +20,7 @@
#include "cmValue.h"
class cmCustomCommand;
class cmFileSet;
class cmGlobalGenerator;
class cmInstallTargetGenerator;
class cmMakefile;
@ -260,6 +261,10 @@ public:
cmBTStringRange GetLinkImplementationEntries() const;
cmBTStringRange GetHeaderSetsEntries() const;
cmBTStringRange GetInterfaceHeaderSetsEntries() const;
std::string ImportedGetFullPath(const std::string& config,
cmStateEnums::ArtifactType artifact) const;
@ -268,6 +273,16 @@ public:
bool operator()(cmTarget const* t1, cmTarget const* t2) const;
};
const cmFileSet* GetFileSet(const std::string& name) const;
cmFileSet* GetFileSet(const std::string& name);
std::pair<cmFileSet*, bool> GetOrCreateFileSet(const std::string& name,
const std::string& type);
std::vector<std::string> GetAllInterfaceFileSets() const;
static std::string GetFileSetsPropertyName(const std::string& type);
static std::string GetInterfaceFileSetsPropertyName(const std::string& type);
private:
template <typename ValueType>
void StoreProperty(const std::string& prop, ValueType value);

View File

@ -341,6 +341,7 @@ CMAKE_CXX_SOURCES="\
cmFileCommand \
cmFileCopier \
cmFileInstaller \
cmFileSet \
cmFileTime \
cmFileTimeCache \
cmFileTimes \