| /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying | |
| file LICENSE.rst or https://cmake.org/licensing for details. */ | |
| #include "cmFileSet.h" | |
| #include <string> | |
| #include <unordered_map> | |
| #include <utility> | |
| #include <vector> | |
| #include <cm/optional> | |
| #include <cmext/algorithm> | |
| #include <cmext/string_view> | |
| #include "cmExperimental.h" | |
| #include "cmGeneratorExpression.h" | |
| #include "cmList.h" | |
| #include "cmListFileCache.h" | |
| #include "cmMakefile.h" | |
| #include "cmMessageType.h" | |
| #include "cmPolicies.h" | |
| #include "cmRange.h" | |
| #include "cmStringAlgorithms.h" | |
| #include "cmTarget.h" | |
| namespace Metadata = cm::FileSetMetadata; | |
| cmFileSet::cmFileSet(cmMakefile* makefile, cmTarget* target, std::string name, | |
| std::string type, Metadata::Visibility visibility) | |
| : Makefile(makefile) | |
| , Target(target) | |
| , Name(std::move(name)) | |
| , Type(std::move(type)) | |
| , Visibility(visibility) | |
| { | |
| } | |
| void cmFileSet::CopyEntries(cmFileSet const* fs) | |
| { | |
| cm::append(this->DirectoryEntries, fs->DirectoryEntries); | |
| cm::append(this->FileEntries, fs->FileEntries); | |
| } | |
| 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)); | |
| } | |
| cmBTStringRange cmFileSet::GetIncludeDirectories() const | |
| { | |
| return cmMakeRange(this->IncludeDirectories); | |
| } | |
| cmBTStringRange cmFileSet::GetInterfaceIncludeDirectories() const | |
| { | |
| return cmMakeRange(this->InterfaceIncludeDirectories); | |
| } | |
| cmBTStringRange cmFileSet::GetCompileOptions() const | |
| { | |
| return cmMakeRange(this->CompileOptions); | |
| } | |
| cmBTStringRange cmFileSet::GetInterfaceCompileOptions() const | |
| { | |
| return cmMakeRange(this->InterfaceCompileOptions); | |
| } | |
| cmBTStringRange cmFileSet::GetCompileDefinitions() const | |
| { | |
| return cmMakeRange(this->CompileDefinitions); | |
| } | |
| cmBTStringRange cmFileSet::GetInterfaceCompileDefinitions() const | |
| { | |
| return cmMakeRange(this->InterfaceCompileDefinitions); | |
| } | |
| namespace { | |
| enum class ReadOnlyCondition | |
| { | |
| All, | |
| Imported, | |
| NonImported, | |
| }; | |
| struct ReadOnlyProperty | |
| { | |
| ReadOnlyProperty(ReadOnlyCondition cond) | |
| : Condition{ cond } | |
| { | |
| } | |
| // ReadOnlyProperty(ReadOnlyCondition cond, cmPolicies::PolicyID id) | |
| // : Condition{ cond } | |
| // , Policy{ id } | |
| // { | |
| // } | |
| ReadOnlyCondition Condition; | |
| cm::optional<cmPolicies::PolicyID> Policy; | |
| std::string message(std::string const& prop, cmTarget* target, | |
| cmFileSet* fileSet) const | |
| { | |
| std::string msg; | |
| if (this->Condition == ReadOnlyCondition::All) { | |
| msg = cmStrCat(" property is read-only for the file set \"", | |
| fileSet->GetName(), " of the target \""); | |
| } else if (this->Condition == ReadOnlyCondition::Imported) { | |
| msg = " property can't be set on a file set attached to the imported " | |
| "target \""; | |
| } else if (this->Condition == ReadOnlyCondition::NonImported) { | |
| msg = | |
| " property can't be set on a file set attached to the non-imported " | |
| "target \""; | |
| } | |
| return cmStrCat(prop, msg, target->GetName(), "\"\n"); | |
| } | |
| bool isReadOnly(std::string const& prop, cmMakefile* context, | |
| cmTarget* target, cmFileSet* fileSet) const | |
| { | |
| auto importedTarget = target->IsImported(); | |
| bool matchingCondition = true; | |
| if ((!importedTarget && this->Condition == ReadOnlyCondition::Imported) || | |
| (importedTarget && | |
| this->Condition == ReadOnlyCondition::NonImported)) { | |
| matchingCondition = false; | |
| } | |
| if (!matchingCondition) { | |
| // Not read-only in this scenario | |
| return false; | |
| } | |
| bool readOnly = true; | |
| if (!this->Policy) { | |
| // No policy associated, so is always read-only | |
| context->IssueMessage(MessageType::FATAL_ERROR, | |
| this->message(prop, target, fileSet)); | |
| } | |
| return readOnly; | |
| } | |
| }; | |
| bool IsSettableProperty(cmMakefile* context, cmTarget* target, | |
| cmFileSet* fileSet, std::string const& prop) | |
| { | |
| using ROC = ReadOnlyCondition; | |
| static std::unordered_map<std::string, ReadOnlyProperty> const readOnlyProps{ | |
| { "TYPE", { ROC::All } }, { "SCOPE", { ROC::All } } | |
| }; | |
| auto it = readOnlyProps.find(prop); | |
| if (it != readOnlyProps.end()) { | |
| return !(it->second.isReadOnly(prop, context, target, fileSet)); | |
| } | |
| return true; | |
| } | |
| cm::string_view const BASE_DIRS = "BASE_DIRS"_s; | |
| cm::string_view const SOURCES = "SOURCES"_s; | |
| cm::string_view const INTERFACE_SOURCES = "INTERFACE_SOURCES"_s; | |
| cm::string_view const INCLUDE_DIRECTORIES = "INCLUDE_DIRECTORIES"_s; | |
| cm::string_view const INTERFACE_INCLUDE_DIRECTORIES = | |
| "INTERFACE_INCLUDE_DIRECTORIES"_s; | |
| cm::string_view const COMPILE_DEFINITIONS = "COMPILE_DEFINITIONS"_s; | |
| cm::string_view const INTERFACE_COMPILE_DEFINITIONS = | |
| "INTERFACE_COMPILE_DEFINITIONS"_s; | |
| cm::string_view const COMPILE_OPTIONS = "COMPILE_OPTIONS"_s; | |
| cm::string_view const INTERFACE_COMPILE_OPTIONS = | |
| "INTERFACE_COMPILE_OPTIONS"_s; | |
| cm::string_view const LANGUAGE = "LANGUAGE"_s; | |
| } | |
| void cmFileSet::SetProperty(std::string const& prop, cmValue value) | |
| { | |
| if (!IsSettableProperty(this->Makefile, this->Target, this, prop)) { | |
| return; | |
| } | |
| if (prop == BASE_DIRS) { | |
| this->ClearDirectoryEntries(); | |
| if (value) { | |
| cmListFileBacktrace lfbt = this->GetMakefile()->GetBacktrace(); | |
| this->AddDirectoryEntry(BT<std::string>{ value, lfbt }); | |
| } | |
| } else if (prop == SOURCES) { | |
| if (!this->IsForSelf()) { | |
| return; | |
| } | |
| this->ClearFileEntries(); | |
| if (value) { | |
| cmListFileBacktrace lfbt = this->GetMakefile()->GetBacktrace(); | |
| this->AddFileEntry(BT<std::string>{ value, lfbt }); | |
| } | |
| } else if (prop == INTERFACE_SOURCES) { | |
| if (!this->IsForInterface()) { | |
| return; | |
| } | |
| this->ClearFileEntries(); | |
| if (value) { | |
| cmListFileBacktrace lfbt = this->GetMakefile()->GetBacktrace(); | |
| this->AddFileEntry(BT<std::string>{ value, lfbt }); | |
| } | |
| } else if (prop == INCLUDE_DIRECTORIES) { | |
| if (!this->IsForSelf()) { | |
| return; | |
| } | |
| this->IncludeDirectories.clear(); | |
| if (value) { | |
| cmListFileBacktrace lfbt = this->GetMakefile()->GetBacktrace(); | |
| this->IncludeDirectories.emplace_back(value, lfbt); | |
| } | |
| } else if (prop == INTERFACE_INCLUDE_DIRECTORIES) { | |
| if (!this->IsForInterface()) { | |
| return; | |
| } | |
| this->InterfaceIncludeDirectories.clear(); | |
| if (value) { | |
| cmListFileBacktrace lfbt = this->GetMakefile()->GetBacktrace(); | |
| this->InterfaceIncludeDirectories.emplace_back(value, lfbt); | |
| } | |
| } else if (prop == COMPILE_OPTIONS) { | |
| if (!this->IsForSelf()) { | |
| return; | |
| } | |
| this->CompileOptions.clear(); | |
| if (value) { | |
| cmListFileBacktrace lfbt = this->GetMakefile()->GetBacktrace(); | |
| this->CompileOptions.emplace_back(value, lfbt); | |
| } | |
| } else if (prop == INTERFACE_COMPILE_OPTIONS) { | |
| if (!this->IsForInterface()) { | |
| return; | |
| } | |
| this->InterfaceCompileOptions.clear(); | |
| if (value) { | |
| cmListFileBacktrace lfbt = this->GetMakefile()->GetBacktrace(); | |
| this->InterfaceCompileOptions.emplace_back(value, lfbt); | |
| } | |
| } else if (prop == COMPILE_DEFINITIONS) { | |
| if (!this->IsForSelf()) { | |
| return; | |
| } | |
| this->CompileDefinitions.clear(); | |
| if (value) { | |
| cmListFileBacktrace lfbt = this->GetMakefile()->GetBacktrace(); | |
| this->CompileDefinitions.emplace_back(value, lfbt); | |
| } | |
| } else if (prop == INTERFACE_COMPILE_DEFINITIONS) { | |
| if (!this->IsForInterface()) { | |
| return; | |
| } | |
| this->InterfaceCompileDefinitions.clear(); | |
| if (value) { | |
| cmListFileBacktrace lfbt = this->GetMakefile()->GetBacktrace(); | |
| this->InterfaceCompileDefinitions.emplace_back(value, lfbt); | |
| } | |
| } else if (prop == LANGUAGE) { | |
| if (this->GetType() != cm::FileSetMetadata::SOURCES && | |
| this->GetType() != cm::FileSetMetadata::HEADERS) { | |
| this->Makefile->IssueMessage( | |
| MessageType::FATAL_ERROR, | |
| "LANGUAGE property may only be set on a SOURCES or HEADERS file set."); | |
| return; | |
| } | |
| if (this->GetType() == cm::FileSetMetadata::HEADERS && | |
| !cmExperimental::HasSupportEnabled(*this->Makefile, | |
| cmExperimental::Feature::SYCL)) { | |
| this->Makefile->IssueMessage( | |
| MessageType::FATAL_ERROR, | |
| "LANGUAGE property on a HEADERS file set is experimental and must be " | |
| "enabled by the CMAKE_EXPERIMENTAL_SYCL gate."); | |
| return; | |
| } | |
| if (value && cmGeneratorExpression::Find(*value) != std::string::npos) { | |
| this->Makefile->IssueMessage( | |
| MessageType::FATAL_ERROR, | |
| "LANGUAGE file set property does not support generator expressions."); | |
| return; | |
| } | |
| this->Properties.SetProperty(prop, value); | |
| } else { | |
| this->Properties.SetProperty(prop, value); | |
| } | |
| } | |
| void cmFileSet::AppendProperty(std::string const& prop, | |
| std::string const& value, bool asString) | |
| { | |
| if (!IsSettableProperty(this->Makefile, this->Target, this, prop)) { | |
| return; | |
| } | |
| if (prop == BASE_DIRS) { | |
| if (!value.empty()) { | |
| cmListFileBacktrace lfbt = this->GetMakefile()->GetBacktrace(); | |
| this->AddDirectoryEntry(BT<std::string>{ value, lfbt }); | |
| } | |
| } else if (prop == SOURCES) { | |
| if (!this->IsForSelf()) { | |
| return; | |
| } | |
| if (!value.empty()) { | |
| cmListFileBacktrace lfbt = this->GetMakefile()->GetBacktrace(); | |
| this->AddFileEntry(BT<std::string>{ value, lfbt }); | |
| } | |
| } else if (prop == INTERFACE_SOURCES) { | |
| if (!this->IsForInterface()) { | |
| return; | |
| } | |
| if (!value.empty()) { | |
| cmListFileBacktrace lfbt = this->GetMakefile()->GetBacktrace(); | |
| this->AddFileEntry(BT<std::string>{ value, lfbt }); | |
| } | |
| } else if (prop == INCLUDE_DIRECTORIES) { | |
| if (!this->IsForSelf()) { | |
| return; | |
| } | |
| if (!value.empty()) { | |
| cmListFileBacktrace lfbt = this->GetMakefile()->GetBacktrace(); | |
| this->IncludeDirectories.emplace_back(value, lfbt); | |
| } | |
| } else if (prop == INTERFACE_INCLUDE_DIRECTORIES) { | |
| if (!this->IsForInterface()) { | |
| return; | |
| } | |
| if (!value.empty()) { | |
| cmListFileBacktrace lfbt = this->GetMakefile()->GetBacktrace(); | |
| this->InterfaceIncludeDirectories.emplace_back(value, lfbt); | |
| } | |
| } else if (prop == COMPILE_OPTIONS) { | |
| if (!this->IsForSelf()) { | |
| return; | |
| } | |
| if (!value.empty()) { | |
| cmListFileBacktrace lfbt = this->GetMakefile()->GetBacktrace(); | |
| this->CompileOptions.emplace_back(value, lfbt); | |
| } | |
| } else if (prop == INTERFACE_COMPILE_OPTIONS) { | |
| if (!this->IsForInterface()) { | |
| return; | |
| } | |
| if (!value.empty()) { | |
| cmListFileBacktrace lfbt = this->GetMakefile()->GetBacktrace(); | |
| this->InterfaceCompileOptions.emplace_back(value, lfbt); | |
| } | |
| } else if (prop == COMPILE_DEFINITIONS) { | |
| if (!this->IsForSelf()) { | |
| return; | |
| } | |
| if (!value.empty()) { | |
| cmListFileBacktrace lfbt = this->GetMakefile()->GetBacktrace(); | |
| this->CompileDefinitions.emplace_back(value, lfbt); | |
| } | |
| } else if (prop == INTERFACE_COMPILE_DEFINITIONS) { | |
| if (!this->IsForInterface()) { | |
| return; | |
| } | |
| if (!value.empty()) { | |
| cmListFileBacktrace lfbt = this->GetMakefile()->GetBacktrace(); | |
| this->InterfaceCompileDefinitions.emplace_back(value, lfbt); | |
| } | |
| } else if (prop == LANGUAGE) { | |
| this->Makefile->IssueMessage( | |
| MessageType::FATAL_ERROR, | |
| "LANGUAGE file set property does not support appending values."); | |
| } else { | |
| this->Properties.AppendProperty(prop, value, asString); | |
| } | |
| } | |
| cmValue cmFileSet::GetProperty(std::string const& prop) const | |
| { | |
| // Check for the properties with backtraces. | |
| if (prop == BASE_DIRS) { | |
| static std::string output; | |
| output = cmList::to_string(this->GetDirectoryEntries()); | |
| return cmValue(output); | |
| } | |
| if (prop == SOURCES) { | |
| if (!this->IsForSelf() || this->GetFileEntries().empty()) { | |
| return nullptr; | |
| } | |
| static std::string output; | |
| output = cmList::to_string(this->GetFileEntries()); | |
| return cmValue(output); | |
| } | |
| if (prop == INTERFACE_SOURCES) { | |
| if (!this->IsForInterface() || this->GetFileEntries().empty()) { | |
| return nullptr; | |
| } | |
| static std::string output; | |
| output = cmList::to_string(this->GetFileEntries()); | |
| return cmValue(output); | |
| } | |
| if (prop == INCLUDE_DIRECTORIES) { | |
| if (this->IncludeDirectories.empty()) { | |
| return nullptr; | |
| } | |
| static std::string output; | |
| output = cmList::to_string(this->IncludeDirectories); | |
| return cmValue(output); | |
| } | |
| if (prop == INTERFACE_INCLUDE_DIRECTORIES) { | |
| if (this->InterfaceIncludeDirectories.empty()) { | |
| return nullptr; | |
| } | |
| static std::string output; | |
| output = cmList::to_string(this->InterfaceIncludeDirectories); | |
| return cmValue(output); | |
| } | |
| if (prop == COMPILE_OPTIONS) { | |
| if (this->CompileOptions.empty()) { | |
| return nullptr; | |
| } | |
| static std::string output; | |
| output = cmList::to_string(this->CompileOptions); | |
| return cmValue(output); | |
| } | |
| if (prop == INTERFACE_COMPILE_OPTIONS) { | |
| if (this->InterfaceCompileOptions.empty()) { | |
| return nullptr; | |
| } | |
| static std::string output; | |
| output = cmList::to_string(this->InterfaceCompileOptions); | |
| return cmValue(output); | |
| } | |
| if (prop == COMPILE_DEFINITIONS) { | |
| if (this->CompileDefinitions.empty()) { | |
| return nullptr; | |
| } | |
| static std::string output; | |
| output = cmList::to_string(this->CompileDefinitions); | |
| return cmValue(output); | |
| } | |
| if (prop == INTERFACE_COMPILE_DEFINITIONS) { | |
| if (this->InterfaceCompileDefinitions.empty()) { | |
| return nullptr; | |
| } | |
| static std::string output; | |
| output = cmList::to_string(this->InterfaceCompileDefinitions); | |
| return cmValue(output); | |
| } | |
| if (prop == "TYPE"_s) { | |
| return cmValue{ this->GetType() }; | |
| } | |
| if (prop == "SCOPE"_s) { | |
| static std::string scope = | |
| std::string{ Metadata::VisibilityToName(this->GetVisibility()) }; | |
| return cmValue{ scope }; | |
| } | |
| return this->Properties.GetPropertyValue(prop); | |
| } |