| /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying |
| file LICENSE.rst or https://cmake.org/licensing for details. */ |
| #include "cmRule.h" |
| |
| #include <algorithm> |
| #include <iterator> |
| #include <memory> |
| #include <unordered_map> |
| #include <utility> |
| |
| #include <cm/string_view> |
| #include <cmext/algorithm> |
| #include <cmext/string_view> |
| |
| #include "cmsys/RegularExpression.hxx" |
| |
| #include "cmExecutionStatus.h" |
| #include "cmFileSet.h" |
| #include "cmFileSetMetadata.h" |
| #include "cmGlobalGenerator.h" |
| #include "cmList.h" |
| #include "cmListFileCache.h" |
| #include "cmMakefile.h" |
| #include "cmMessageType.h" |
| #include "cmPlaceholderExpander.h" |
| #include "cmRange.h" |
| #include "cmSourceFile.h" |
| #include "cmStringAlgorithms.h" |
| #include "cmTarget.h" |
| #include "cmValue.h" |
| |
| namespace { |
| cm::string_view const NAME = "NAME"_s; |
| cm::string_view const OUTPUT = "OUTPUT"_s; |
| cm::string_view const COMMAND = "COMMAND"_s; |
| cm::string_view const COMMAND_COUNT = "COMMAND_COUNT"_s; |
| cm::string_view const COMMAND_EXPAND_LISTS = "COMMAND_EXPAND_LISTS"_s; |
| cm::string_view const COMMENT = "COMMENT"_s; |
| cm::string_view const COMPILE_DEFINITIONS = "COMPILE_DEFINITIONS"_s; |
| cm::string_view const COMPILE_OPTIONS = "COMPILE_OPTIONS"_s; |
| cm::string_view const DEPENDS = "DEPENDS"_s; |
| cm::string_view const DEPENDS_EXPLICIT_ONLY = "DEPENDS_EXPLICIT_ONLY"_s; |
| cm::string_view const BYPRODUCTS = "BYPRODUCTS"_s; |
| cm::string_view const DEPFILE = "DEPFILE"_s; |
| cm::string_view const GLOBAL = "GLOBAL"_s; |
| cm::string_view const INCLUDE_DIRECTORIES = "INCLUDE_DIRECTORIES"_s; |
| cm::string_view const JOB_POOL_COMPILE = "JOB_POOL_COMPILE"_s; |
| cm::string_view const JOB_SERVER_AWARE = "JOB_SERVER_AWARE"_s; |
| cm::string_view const OUTPUT_FILE_SET = "OUTPUT_FILE_SET"_s; |
| cm::string_view const OUTPUT_FILE_SET_BASE_DIRS = |
| "OUTPUT_FILE_SET_BASE_DIRS"_s; |
| cm::string_view const PARENT_RULE = "PARENT_RULE"_s; |
| cm::string_view const FILE_SET_CONFIGURATORS = "FILE_SET_CONFIGURATORS"_s; |
| cm::string_view const SOURCE_CONFIGURATORS = "SOURCE_CONFIGURATORS"_s; |
| cm::string_view const USES_TERMINAL = "USES_TERMINAL"_s; |
| cm::string_view const VERBATIM = "VERBATIM"_s; |
| cm::string_view const WORKING_DIRECTORY = "WORKING_DIRECTORY"_s; |
| |
| cmsys::RegularExpression commandIndex("^COMMAND_[0-9]+$"); |
| |
| enum class ReadOnlyCondition |
| { |
| All, |
| Configuration, |
| Generation, |
| }; |
| |
| struct ReadOnlyProperty |
| { |
| ReadOnlyProperty(ReadOnlyCondition cond) |
| : Condition{ cond } |
| { |
| } |
| |
| ReadOnlyCondition Condition; |
| |
| std::string Message(cm::string_view prop, cmRule const* rule) const |
| { |
| std::string msg; |
| switch (this->Condition) { |
| case ReadOnlyCondition::All: |
| msg = " property is read-only for rules (\""; |
| break; |
| case ReadOnlyCondition::Configuration: |
| msg = " property can't be set during configuration for rules (\""; |
| break; |
| case ReadOnlyCondition::Generation: |
| msg = " property can't be set during generation for rules (\""; |
| break; |
| } |
| return cmStrCat('"', prop, "\" ", msg, rule->GetName(), "\")\n"); |
| } |
| |
| bool IsReadOnly(cm::string_view prop, cmRule const* rule) const |
| { |
| if ((rule->InGeneration() && |
| this->Condition == ReadOnlyCondition::Configuration) || |
| (!rule->InGeneration() && |
| this->Condition == ReadOnlyCondition::Generation)) { |
| return false; |
| } |
| |
| rule->GetMakefile().IssueMessage(MessageType::FATAL_ERROR, |
| this->Message(prop, rule)); |
| |
| return true; |
| } |
| }; |
| |
| bool IsSettableProperty(cm::string_view prop, cmRule const* rule) |
| { |
| using ROC = ReadOnlyCondition; |
| static std::unordered_map<cm::string_view, ReadOnlyProperty> const |
| readOnlyProps{ { NAME, { ROC::All } }, |
| { OUTPUT, { ROC::All } }, |
| { COMMAND, { ROC::All } }, |
| { COMMAND_COUNT, { ROC::All } }, |
| { COMMAND_EXPAND_LISTS, { ROC::Generation } }, |
| { COMMENT, { ROC::Generation } }, |
| { COMPILE_DEFINITIONS, { ROC::Generation } }, |
| { COMPILE_OPTIONS, { ROC::Generation } }, |
| { DEPENDS, { ROC::All } }, |
| { DEPENDS_EXPLICIT_ONLY, { ROC::Generation } }, |
| { BYPRODUCTS, { ROC::All } }, |
| { DEPFILE, { ROC::All } }, |
| { GLOBAL, { ROC::Generation } }, |
| { INCLUDE_DIRECTORIES, { ROC::Generation } }, |
| { JOB_POOL_COMPILE, { ROC::Generation } }, |
| { JOB_SERVER_AWARE, { ROC::Generation } }, |
| { OUTPUT_FILE_SET, { ROC::Generation } }, |
| { OUTPUT_FILE_SET_BASE_DIRS, { ROC::Generation } }, |
| { PARENT_RULE, { ROC::All } }, |
| { FILE_SET_CONFIGURATORS, { ROC::All } }, |
| { SOURCE_CONFIGURATORS, { ROC::All } }, |
| { USES_TERMINAL, { ROC::Generation } }, |
| { VERBATIM, { ROC::Generation } }, |
| { WORKING_DIRECTORY, { ROC::Generation } } }; |
| |
| auto it = |
| readOnlyProps.find(commandIndex.find(prop.data()) ? COMMAND : prop); |
| |
| if (it != readOnlyProps.end()) { |
| return !(it->second.IsReadOnly(prop, rule)); |
| } |
| |
| if (rule->InGeneration()) { |
| rule->GetMakefile().IssueMessage( |
| MessageType::FATAL_ERROR, |
| cmStrCat("rule properties cannot be changed during generation (\"", |
| rule->GetName(), "\")\n")); |
| return false; |
| } |
| |
| return true; |
| } |
| |
| class FileSetNamePlaceholderExpander : public cmPlaceholderExpander |
| { |
| public: |
| using VariableMap = std::unordered_map<std::string, std::string>; |
| VariableMap Values; |
| |
| bool InError = false; |
| |
| private: |
| std::string ExpandVariable(std::string const& variable) override |
| { |
| if (cm::contains(this->Values, variable)) { |
| return this->Values[variable]; |
| } |
| this->InError = true; |
| // If there is no variable defined, mark unresolved variable by '<' and '>' |
| return cmStrCat('<', variable, '>'); |
| } |
| }; |
| } |
| |
| cmRule::cmRule(cmMakefile& makefile, std::string name, cm::RuleScope scope) |
| : Makefile(&makefile) |
| , Name(std::move(name)) |
| , Scope(scope) |
| { |
| // set some useful properties |
| this->SetProperty("VERBATIM", cmValue::True); |
| this->SetProperty("COMMAND_EXPAND_LISTS", cmValue::True); |
| this->SetProperty("OUTPUT_FILE_SET", |
| "__cmake_rule_<RULE>_<TARGET>_<FILE_SET>_outputs;SOURCES"); |
| this->SetProperty("OUTPUT_FILE_SET_BASE_DIRS", "<CURRENT_BINARY_DIR>"); |
| } |
| |
| cmRule::cmRule(cmRule const& parent, cmMakefile& makefile, std::string name, |
| cm::RuleScope scope) |
| : Makefile(&makefile) |
| , Name(std::move(name)) |
| , Scope(scope) |
| , ConfiguratorsChain(parent.ConfiguratorsChain) |
| , Properties(parent.Properties) |
| , IncludeDirectories(parent.IncludeDirectories) |
| , CompileOptions(parent.CompileOptions) |
| , CompileDefinitions(parent.CompileDefinitions) |
| , Generation(parent.Generation) |
| { |
| } |
| std::string const& cmRule::GetParentName() const |
| { |
| static std::string empty; |
| |
| return empty; |
| } |
| |
| void cmRule::SetConfigurator(ConfiguratorType type, std::string configurator, |
| ChainConfigurators chain) |
| { |
| this->Configurators[type] = std::move(configurator); |
| if (chain == ChainConfigurators::Yes) { |
| this->ConfiguratorsChain[type].emplace_back(&this->GetName(), |
| &this->Configurators[type]); |
| } else { |
| this->ConfiguratorsChain[type].assign( |
| 1, |
| ConfiguratorSet::value_type{ &this->GetName(), |
| &this->Configurators[type] }); |
| } |
| } |
| cmRule::ConfiguratorSet const& cmRule::GetConfigurators( |
| ConfiguratorType type) const |
| { |
| static ConfiguratorSet emptySet; |
| |
| if (this->HasConfigurators(type)) { |
| return this->ConfiguratorsChain.at(type); |
| } |
| return emptySet; |
| } |
| |
| bool cmRule::HasConfigurators(ConfiguratorType type) const |
| { |
| return cm::contains(this->ConfiguratorsChain, type); |
| } |
| |
| cmFileSet* cmRule::GetOutputFileSet(cmTarget* target, |
| cmFileSet const* fileSet) const |
| { |
| cmList outputFileSet{ this->GetProperty(std::string{ OUTPUT_FILE_SET }) }; |
| FileSetNamePlaceholderExpander expander; |
| expander.Values["RULE"] = this->GetName(); |
| expander.Values["TARGET"] = target->GetName(); |
| expander.Values["FILE_SET"] = fileSet->GetName(); |
| |
| std::string fsName{ outputFileSet[0] }; |
| expander.ExpandVariables(fsName); |
| if (expander.InError) { |
| this->GetMakefile().IssueMessage( |
| MessageType::FATAL_ERROR, |
| cmStrCat("Output File Set name, for rule \"", this->GetName(), |
| "\", did not expand correctly:\n \"", fsName, "\".")); |
| return nullptr; |
| } |
| |
| auto result = target->GetOrCreateFileSet(fsName, outputFileSet[1], |
| fileSet->GetVisibility()); |
| if (!result.second && result.first->GetType() != outputFileSet[1]) { |
| this->GetMakefile().IssueMessage( |
| MessageType::FATAL_ERROR, |
| cmStrCat("The output file set \"", fsName, "\", for the target \"", |
| target->GetName(), "\", has the type \"", |
| result.first->GetType(), "\" instead of \"", outputFileSet[1], |
| "\", as specified by the rule \"", this->GetName(), "\".")); |
| return nullptr; |
| } |
| |
| if (result.first->GetDirectoryEntries().empty()) { |
| if (cmValue baseDirs = |
| this->GetProperty(std::string{ OUTPUT_FILE_SET_BASE_DIRS })) { |
| expander.Values["CURRENT_SOURCE_DIR"] = |
| fileSet->GetMakefile()->GetCurrentSourceDirectory(); |
| expander.Values["CURRENT_BINARY_DIR"] = |
| fileSet->GetMakefile()->GetCurrentBinaryDirectory(); |
| expander.Values["OUTPUT_CURRENT_SOURCE_DIR"] = |
| result.first->GetMakefile()->GetCurrentSourceDirectory(); |
| expander.Values["OUTPUT_CURRENT_BINARY_DIR"] = |
| result.first->GetMakefile()->GetCurrentBinaryDirectory(); |
| std::string dirs{ baseDirs }; |
| expander.ExpandVariables(dirs); |
| result.first->AddDirectoryEntry(BT<std::string>{ dirs }); |
| } else { |
| // Add default base directory if none are specified |
| result.first->AddDirectoryEntry(BT<std::string>{ |
| fileSet->GetMakefile()->GetCurrentBinaryDirectory() }); |
| } |
| } |
| |
| return result.first; |
| } |
| |
| cmBTStringRange cmRule::GetIncludeDirectories() const |
| { |
| return cmMakeRange(this->IncludeDirectories); |
| } |
| |
| cmBTStringRange cmRule::GetCompileOptions() const |
| { |
| return cmMakeRange(this->CompileOptions); |
| } |
| |
| cmBTStringRange cmRule::GetCompileDefinitions() const |
| { |
| return cmMakeRange(this->CompileDefinitions); |
| } |
| |
| void cmRule::SetProperty(std::string const& prop, cmValue value) |
| { |
| if (!IsSettableProperty(prop, this)) { |
| return; |
| } |
| |
| if (prop == GLOBAL) { |
| if (!value.IsOn()) { |
| this->GetMakefile().IssueMessage( |
| MessageType::FATAL_ERROR, |
| cmStrCat("GLOBAL property can't be set to FALSE on rules (\"", |
| this->GetName(), "\")")); |
| return; |
| } |
| /* no need to change anything if value does not change */ |
| if (!this->IsGloballyVisible()) { |
| this->Scope = cm::RuleScope::Global; |
| this->GetMakefile().GetGlobalGenerator()->IndexRule(this); |
| } |
| } else if (prop == OUTPUT_FILE_SET) { |
| cmList fileSet{ value }; |
| if (fileSet.size() != 2) { |
| this->GetMakefile().IssueMessage( |
| MessageType::FATAL_ERROR, |
| "OUTPUT_FILE_SET property require a list of 2 elements:\n " |
| "\"name;type\""); |
| return; |
| } |
| if (!cm::FileSetMetadata::IsKnownType(fileSet[1])) { |
| this->GetMakefile().IssueMessage( |
| MessageType::FATAL_ERROR, |
| cmStrCat( |
| "specified file set type is erroneous. The supported types are: ", |
| cmJoin(cm::FileSetMetadata::GetKnownTypes(), ", "), '.')); |
| return; |
| } |
| this->Properties.SetProperty(prop, value); |
| } else if (prop == INCLUDE_DIRECTORIES) { |
| this->IncludeDirectories.clear(); |
| if (value) { |
| cmListFileBacktrace lfbt = this->GetMakefile().GetBacktrace(); |
| this->IncludeDirectories.emplace_back(value, lfbt); |
| } |
| } else if (prop == COMPILE_OPTIONS) { |
| this->CompileOptions.clear(); |
| if (value) { |
| cmListFileBacktrace lfbt = this->GetMakefile().GetBacktrace(); |
| this->CompileOptions.emplace_back(value, lfbt); |
| } |
| } else if (prop == COMPILE_DEFINITIONS) { |
| this->CompileDefinitions.clear(); |
| if (value) { |
| cmListFileBacktrace lfbt = this->GetMakefile().GetBacktrace(); |
| this->CompileDefinitions.emplace_back(value, lfbt); |
| } |
| } else { |
| this->Properties.SetProperty(prop, value); |
| } |
| } |
| |
| void cmRule::AppendProperty(std::string const& prop, std::string const& value, |
| bool asString) |
| { |
| if (!IsSettableProperty(prop, this)) { |
| return; |
| } |
| |
| if (prop == GLOBAL) { |
| this->GetMakefile().IssueMessage( |
| MessageType::FATAL_ERROR, |
| cmStrCat("GLOBAL property can't be appended, only set on rules (\"", |
| this->GetName(), "\")\n")); |
| return; |
| } |
| |
| if (prop == INCLUDE_DIRECTORIES) { |
| if (!value.empty()) { |
| cmListFileBacktrace lfbt = this->GetMakefile().GetBacktrace(); |
| this->IncludeDirectories.emplace_back(value, lfbt); |
| } |
| } else if (prop == COMPILE_OPTIONS) { |
| if (!value.empty()) { |
| cmListFileBacktrace lfbt = this->GetMakefile().GetBacktrace(); |
| this->CompileOptions.emplace_back(value, lfbt); |
| } |
| } else if (prop == COMPILE_DEFINITIONS) { |
| if (!value.empty()) { |
| cmListFileBacktrace lfbt = this->GetMakefile().GetBacktrace(); |
| this->CompileDefinitions.emplace_back(value, lfbt); |
| } |
| } else { |
| this->Properties.AppendProperty(prop, value, asString); |
| } |
| } |
| |
| cmValue cmRule::GetProperty(std::string const& prop) const |
| { |
| static std::string value; |
| |
| if (prop == NAME) { |
| return cmValue{ this->GetName() }; |
| } |
| if (prop == OUTPUT) { |
| value = cmList::to_string(this->GetOutputs()); |
| return cmValue{ value }; |
| } |
| if (prop == COMMAND) { |
| value = cmList::to_string(this->GetCommands()[0]); |
| return cmValue{ value }; |
| } |
| if (prop == COMMAND_COUNT) { |
| value = std::to_string(this->GetCommands().size()); |
| return cmValue{ value }; |
| } |
| if (commandIndex.find(prop)) { |
| auto index = std::stoul(prop.substr(8)); |
| value = index >= this->GetCommands().size() |
| ? "NOTFOUND" |
| : cmList::to_string(this->GetCommands()[index]); |
| return cmValue{ value }; |
| } |
| if (prop == DEPENDS) { |
| value = cmList::to_string(this->GetDepends()); |
| return cmValue{ value }; |
| } |
| if (prop == BYPRODUCTS) { |
| value = cmList::to_string(this->GetByproducts()); |
| return cmValue{ value }; |
| } |
| if (prop == DEPFILE) { |
| return cmValue{ this->GetDepfile() }; |
| } |
| if (prop == GLOBAL) { |
| return this->IsGloballyVisible() ? cmValue::True : cmValue::False; |
| } |
| if (prop == PARENT_RULE) { |
| return cmValue{ this->GetParentName() }; |
| } |
| if (prop == FILE_SET_CONFIGURATORS || prop == SOURCE_CONFIGURATORS) { |
| ConfiguratorType type = prop == FILE_SET_CONFIGURATORS |
| ? ConfiguratorType::FileSet |
| : ConfiguratorType::Source; |
| |
| if (this->HasConfigurators(type)) { |
| auto cfgs_set = this->GetConfigurators(type); |
| std::vector<std::string> cfgs; |
| cfgs.reserve(cfgs_set.size()); |
| |
| std::transform(cfgs_set.cbegin(), cfgs_set.cend(), |
| std::back_inserter(cfgs), |
| [](ConfiguratorSet::value_type cfg) -> std::string { |
| return *cfg.second; |
| }); |
| |
| value = cmList::to_string(cfgs); |
| return cmValue{ value }; |
| } |
| |
| return nullptr; |
| } |
| |
| // Check for the properties with backtraces. |
| if (prop == INCLUDE_DIRECTORIES) { |
| if (this->IncludeDirectories.empty()) { |
| return nullptr; |
| } |
| |
| value = cmList::to_string(this->IncludeDirectories); |
| return cmValue{ value }; |
| } |
| |
| if (prop == COMPILE_OPTIONS) { |
| if (this->CompileOptions.empty()) { |
| return nullptr; |
| } |
| |
| value = cmList::to_string(this->CompileOptions); |
| return cmValue{ value }; |
| } |
| |
| if (prop == COMPILE_DEFINITIONS) { |
| if (this->CompileDefinitions.empty()) { |
| return nullptr; |
| } |
| |
| value = cmList::to_string(this->CompileDefinitions); |
| return cmValue{ value }; |
| } |
| |
| return this->Properties.GetPropertyValue(prop); |
| } |
| |
| void cmRule::CheckProperty(std::string const& prop, cmMakefile& context) const |
| { |
| // Certain properties need checking. |
| if (prop == GLOBAL) { |
| auto const& rules = context.GetOwnedRules(); |
| auto it = std::find_if(rules.begin(), rules.end(), |
| [&](std::unique_ptr<cmRule> const& rule) -> bool { |
| return this == rule.get(); |
| }); |
| if (it == rules.end()) { |
| context.IssueMessage( |
| MessageType::FATAL_ERROR, |
| cmStrCat("Attempt to promote rule \"", this->GetName(), |
| "\" to global scope (by setting GLOBAL) " |
| "which is not created in this directory.")); |
| } |
| } |
| } |
| |
| namespace { |
| cmsys::RegularExpression PatternRegex{ "(^[A-Z][A-Z0-9_]+)=(.*)$" }; |
| |
| void UpdatePatterns(cmRule::PatternSet& patterns, cmList const& newPatterns) |
| { |
| for (auto const& pattern : newPatterns) { |
| if (PatternRegex.find(pattern)) { |
| auto it = std::find_if(patterns.begin(), patterns.end(), |
| [](cmRule::Pattern const& item) { |
| return item.Name == PatternRegex.match(1); |
| }); |
| if (it == patterns.end()) { |
| patterns.emplace_back(PatternRegex.match(1), PatternRegex.match(2)); |
| } else { |
| it->Value = PatternRegex.match(2); |
| } |
| } |
| } |
| } |
| |
| bool ConfigureRule(cmRule::ConfiguratorSet const& configurators, |
| cmMakefile* mf, cmRule::PatternSet& patterns, |
| std::string const& rule, std::string const& target, |
| std::string const& fileSet, |
| std::string const& outputFileSet, |
| std::string const& source = {}) |
| { |
| cm::string_view patternsVariable{ "_CMAKE_RULE_PATTERNS"_s }; |
| |
| for (auto const& item : configurators) { |
| // The validator command will be executed in an isolated scope. |
| cmMakefile::ScopePushPop varScope(mf); |
| cmMakefile::PolicyPushPop polScope(mf); |
| static_cast<void>(varScope); |
| static_cast<void>(polScope); |
| |
| std::vector<cmListFileArgument> args{ |
| cmListFileArgument{ rule, cmListFileArgument::Unquoted, 0 }, |
| cmListFileArgument{ target, cmListFileArgument::Unquoted, 0 }, |
| cmListFileArgument{ fileSet, cmListFileArgument::Unquoted, 0 }, |
| cmListFileArgument{ outputFileSet, cmListFileArgument::Unquoted, 0 } |
| }; |
| if (!source.empty()) { |
| args.emplace_back(source, cmListFileArgument::Quoted, 0); |
| } |
| args.emplace_back(patternsVariable, cmListFileArgument::Unquoted, 0); |
| |
| cmListFileFunction command(*item.second, 0, 0, args); |
| cmExecutionStatus status(*mf); |
| if (!mf->ExecuteCommand(command, status)) { |
| mf->IssueMessage(MessageType::FATAL_ERROR, |
| cmStrCat("Erroneous execution of the CONFIGURATOR \"", |
| *item.second, "\", from the RULE \"", |
| *item.first, "\", for the FILE_SET \"", |
| fileSet, "\" of TARGET \"", target, "\".")); |
| return false; |
| } |
| UpdatePatterns( |
| patterns, cmList{ mf->GetDefinition(std::string{ patternsVariable }) }); |
| } |
| |
| return true; |
| } |
| } |
| |
| bool cmRule::Instantiate(cmTarget const* target, cmFileSet const* fileSet, |
| cmFileSet const* outputFileSet, |
| PatternSet& patterns) const |
| { |
| this->SwitchMode(); |
| |
| // First, take patterns from RULE_PATTERNS file set property, if any |
| if (cmValue fsPatterns = fileSet->GetProperty("RULE_PATTERNS")) { |
| UpdatePatterns(patterns, cmList{ fsPatterns, cmList::EmptyElements::No }); |
| } |
| |
| if (this->HasConfigurators(ConfiguratorType::FileSet)) { |
| // Finalize file set level configuration by calling user's commands |
| return ConfigureRule(this->GetConfigurators(ConfiguratorType::FileSet), |
| fileSet->GetMakefile(), patterns, this->GetName(), |
| target->GetName(), fileSet->GetName(), |
| outputFileSet->GetName()); |
| } |
| |
| return true; |
| } |
| |
| bool cmRule::Instantiate(cmTarget const* target, cmFileSet const* fileSet, |
| cmFileSet const* outputFileSet, cmSourceFile* source, |
| PatternSet& patterns) const |
| { |
| // First, take patterns from <RULE>_PATTERNS source file property, if any |
| if (cmValue sfPatterns = |
| source->GetProperty(cmStrCat(this->GetName(), "_PATTERNS"))) { |
| UpdatePatterns(patterns, cmList{ sfPatterns, cmList::EmptyElements::No }); |
| } |
| |
| if (this->HasConfigurators(ConfiguratorType::Source)) { |
| // Before custom commands generation, finalize source level |
| // configuration by calling user's commands |
| return ConfigureRule(this->GetConfigurators(ConfiguratorType::Source), |
| fileSet->GetMakefile(), patterns, this->GetName(), |
| target->GetName(), fileSet->GetName(), |
| outputFileSet->GetName(), source->GetFullPath()); |
| } |
| |
| return true; |
| } |