aboutsummaryrefslogtreecommitdiffstats
path: root/src/lib/corelib/language/projectresolver.cpp
diff options
context:
space:
mode:
authorChristian Kandeler <christian.kandeler@qt.io>2017-07-13 12:00:18 +0200
committerChristian Kandeler <christian.kandeler@qt.io>2018-03-14 08:52:50 +0000
commit149e20aca1e401ba18bbae602df2caa7dc68c493 (patch)
tree29b0b5a270696040b5b8c668834c2e6929ec621f /src/lib/corelib/language/projectresolver.cpp
parentd0f29502e0d4f3e21fbe0f0b7b56c62b4f85fde3 (diff)
Provide rules with information about the content of Export items
The product variable gets a new property "exports" that provides a "dependencies" array as well as property values the same way that the product variable itself does, but for exported dependencies and properties, respectively. In addition, meta data about these properties is provided via a "properties" array, and structural information can be retrieved via "childItems". This data is intended to be used by modules creating interface files from products. Task-number: QBS-268 Change-Id: I2bb106e1ca1b18abbd6fe60411bc81bda9ee35e7 Reviewed-by: Joerg Bornemann <joerg.bornemann@qt.io>
Diffstat (limited to 'src/lib/corelib/language/projectresolver.cpp')
-rw-r--r--src/lib/corelib/language/projectresolver.cpp482
1 files changed, 426 insertions, 56 deletions
diff --git a/src/lib/corelib/language/projectresolver.cpp b/src/lib/corelib/language/projectresolver.cpp
index df2c5fcb3..7028e28ad 100644
--- a/src/lib/corelib/language/projectresolver.cpp
+++ b/src/lib/corelib/language/projectresolver.cpp
@@ -40,6 +40,7 @@
#include "projectresolver.h"
#include "artifactproperties.h"
+#include "builtindeclarations.h"
#include "evaluator.h"
#include "filecontext.h"
#include "item.h"
@@ -49,20 +50,24 @@
#include "scriptengine.h"
#include "value.h"
+#include <jsextensions/jsextensions.h>
#include <jsextensions/moduleproperties.h>
#include <logging/categories.h>
#include <logging/translator.h>
#include <tools/error.h>
#include <tools/fileinfo.h>
+#include <tools/jsliterals.h>
#include <tools/profiling.h>
#include <tools/progressobserver.h>
#include <tools/scripttools.h>
#include <tools/qbsassert.h>
#include <tools/qttools.h>
#include <tools/setupprojectparameters.h>
+#include <tools/stlutils.h>
#include <tools/stringconstants.h>
#include <QtCore/qdir.h>
+#include <QtCore/qregularexpression.h>
#include <algorithm>
#include <queue>
@@ -255,6 +260,7 @@ TopLevelProjectPtr ProjectResolver::resolveTopLevelProject()
project->buildSystemFiles.unite(m_engine->imports());
makeSubProjectNamesUniqe(project);
resolveProductDependencies(projectContext);
+ collectExportedProductDependencies();
checkForDuplicateProductNames(project);
for (const ResolvedProductPtr &product : project->allProducts()) {
@@ -489,7 +495,8 @@ void ProjectResolver::resolveProductFully(Item *item, ProjectContext *projectCon
{ ItemType::Rule, &ProjectResolver::resolveRule },
{ ItemType::FileTagger, &ProjectResolver::resolveFileTagger },
{ ItemType::Group, &ProjectResolver::resolveGroup },
- { ItemType::Export, &ProjectResolver::ignoreItem },
+ { ItemType::Product, &ProjectResolver::resolveShadowProduct },
+ { ItemType::Export, &ProjectResolver::resolveExport },
{ ItemType::Probe, &ProjectResolver::ignoreItem },
{ ItemType::PropertyOptions, &ProjectResolver::ignoreItem }
};
@@ -654,7 +661,7 @@ QVariantMap ProjectResolver::resolveAdditionalModuleProperties(const Item *group
for (const QString &prop : qAsConst(propsForModule))
reusableValues.remove(prop);
modulesMap.insert(fullModName,
- evaluateProperties(module.item, module.item, reusableValues, true));
+ evaluateProperties(module.item, module.item, reusableValues, true, true));
}
m_evaluator->clearPathPropertiesBaseDir();
return modulesMap;
@@ -815,6 +822,254 @@ void ProjectResolver::resolveGroupFully(Item *item, ProjectResolver::ProjectCont
resolveGroup(childItem, projectContext);
}
+void ProjectResolver::adaptExportedPropertyValues()
+{
+ ExportedModule &m = m_productContext->product->exportedModule;
+ const QVariantList prefixList = m.propertyValues.take(
+ StringConstants::prefixMappingProperty()).toList();
+ if (prefixList.empty())
+ return;
+ QVariantMap prefixMap;
+ for (const QVariant &v : prefixList) {
+ const QVariantMap o = v.toMap();
+ prefixMap.insert(o.value(QLatin1String("prefix")).toString(),
+ o.value(QLatin1String("replacement")).toString());
+ }
+ static const auto stringMapper = [](const QVariantMap &mappings, const QString &value)
+ -> QString {
+ for (auto it = mappings.cbegin(); it != mappings.cend(); ++it) {
+ if (value.startsWith(it.key()))
+ return it.value().toString() + value.mid(it.key().size());
+ }
+ return value;
+ };
+ static const auto stringListMapper = [](const QVariantMap &mappings, const QStringList &value)
+ -> QStringList {
+ QStringList result;
+ result.reserve(value.size());
+ for (const QString &s : value) {
+ result.push_back(stringMapper(mappings, s));
+ }
+ return result;
+ };
+ static const std::function<QVariant(const QVariantMap &, const QVariant &)> mapper
+ = [](const QVariantMap &mappings, const QVariant &value) -> QVariant {
+ switch (static_cast<QMetaType::Type>(value.type())) {
+ case QMetaType::QString:
+ return stringMapper(mappings, value.toString());
+ case QMetaType::QStringList:
+ return stringListMapper(mappings, value.toStringList());
+ case QMetaType::QVariantMap: {
+ QVariantMap m = value.toMap();
+ for (auto it = m.begin(); it != m.end(); ++it)
+ it.value() = mapper(mappings, it.value());
+ return m;
+ }
+ default:
+ return value;
+ }
+ };
+ for (auto it = m.propertyValues.begin(); it != m.propertyValues.end(); ++it)
+ it.value() = mapper(prefixMap, it.value());
+ for (auto it = m.modulePropertyValues.begin(); it != m.modulePropertyValues.end(); ++it)
+ it.value() = mapper(prefixMap, it.value());
+ for (ExportedModuleDependency &dep : m.moduleDependencies) {
+ for (auto it = dep.moduleProperties.begin(); it != dep.moduleProperties.end(); ++it)
+ it.value() = mapper(prefixMap, it.value());
+ }
+}
+
+void ProjectResolver::collectExportedProductDependencies()
+{
+ ResolvedProductPtr dummyProduct = ResolvedProduct::create();
+ dummyProduct->enabled = false;
+ for (const auto &exportingProductInfo : qAsConst(m_productExportInfo)) {
+ const ResolvedProductPtr exportingProduct = exportingProductInfo.first;
+ if (!exportingProduct->enabled)
+ continue;
+ Item * const importingProductItem = exportingProductInfo.second;
+ std::vector<QString> directDepNames;
+ for (const Item::Module &m : importingProductItem->modules()) {
+ if (m.name.toString() == exportingProduct->name) {
+ for (const Item::Module &dep : m.item->modules()) {
+ if (dep.isProduct)
+ directDepNames.push_back(dep.name.toString());
+ }
+ break;
+ }
+ }
+ const ModuleLoaderResult::ProductInfo &importingProductInfo
+ = m_loadResult.productInfos.value(importingProductItem);
+ const ProductDependencyInfos &depInfos
+ = getProductDependencies(dummyProduct, importingProductInfo);
+ for (const auto &dep : depInfos.dependencies) {
+ if (dep.product == exportingProduct)
+ continue;
+
+ // Filter out indirect dependencies.
+ // TODO: Depends items using "profile" or "productTypes" will not work.
+ if (!contains(directDepNames, dep.product->name))
+ continue;
+
+ exportingProduct->exportedModule.productDependencies.insert(dep.product);
+ if (!dep.parameters.isEmpty()) {
+ exportingProduct->exportedModule.dependencyParameters.insert(dep.product,
+ dep.parameters);
+ }
+ }
+ }
+}
+
+void ProjectResolver::resolveShadowProduct(Item *item, ProjectResolver::ProjectContext *)
+{
+ for (const auto &m : item->modules()) {
+ if (m.name.toString() != m_productContext->product->name)
+ continue;
+ collectPropertiesForExportItem(m.item);
+ for (const auto &dep : m.item->modules())
+ collectPropertiesForModuleInExportItem(dep);
+ break;
+ }
+ adaptExportedPropertyValues();
+ m_productExportInfo.push_back(std::make_pair(m_productContext->product, item));
+}
+
+void ProjectResolver::setupExportedProperties(const Item *item, const QString &namePrefix,
+ std::vector<ExportedProperty> &properties)
+{
+ const auto &props = item->properties();
+ for (auto it = props.cbegin(); it != props.cend(); ++it) {
+ const QString qualifiedName = namePrefix.isEmpty()
+ ? it.key() : namePrefix + QLatin1Char('.') + it.key();
+ if (item->type() == ItemType::Export
+ && qualifiedName == StringConstants::prefixMappingProperty()) {
+ continue;
+ }
+ const ValuePtr &v = it.value();
+ if (v->type() == Value::ItemValueType) {
+ setupExportedProperties(std::static_pointer_cast<ItemValue>(v)->item(),
+ qualifiedName, properties);
+ continue;
+ }
+ ExportedProperty exportedProperty;
+ exportedProperty.fullName = qualifiedName;
+ exportedProperty.type = item->propertyDeclaration(it.key()).type();
+ if (v->type() == Value::VariantValueType) {
+ exportedProperty.sourceCode = toJSLiteral(
+ std::static_pointer_cast<VariantValue>(v)->value());
+ } else {
+ QBS_CHECK(v->type() == Value::JSSourceValueType);
+ const JSSourceValue * const sv = static_cast<JSSourceValue *>(v.get());
+ exportedProperty.sourceCode = sv->sourceCode().toString();
+ }
+ const ItemDeclaration itemDecl
+ = BuiltinDeclarations::instance().declarationsForType(item->type());
+ PropertyDeclaration propertyDecl;
+ for (const PropertyDeclaration &decl : itemDecl.properties()) {
+ if (decl.name() == it.key()) {
+ propertyDecl = decl;
+ exportedProperty.isBuiltin = true;
+ break;
+ }
+ }
+
+ // Do not add built-in properties that were left at their default value.
+ if (!exportedProperty.isBuiltin || m_evaluator->isNonDefaultValue(item, it.key()))
+ properties.push_back(exportedProperty);
+ }
+
+ // Order the list of properties, so the output won't look so random.
+ static const auto less = [](const ExportedProperty &p1, const ExportedProperty &p2) -> bool {
+ const int p1ComponentCount = p1.fullName.count(QLatin1Char('.'));
+ const int p2ComponentCount = p2.fullName.count(QLatin1Char('.'));
+ if (p1.isBuiltin && !p2.isBuiltin)
+ return true;
+ if (!p1.isBuiltin && p2.isBuiltin)
+ return false;
+ if (p1ComponentCount < p2ComponentCount)
+ return true;
+ if (p1ComponentCount > p2ComponentCount)
+ return false;
+ return p1.fullName < p2.fullName;
+ };
+ std::sort(properties.begin(), properties.end(), less);
+}
+
+static bool usesImport(const ExportedProperty &prop, const QRegularExpression &regex)
+{
+ return regex.match(prop.sourceCode).hasMatch();
+}
+
+static bool usesImport(const ExportedItem &item, const QRegularExpression &regex)
+{
+ return any_of(item.properties,
+ [regex](const ExportedProperty &p) { return usesImport(p, regex); })
+ || any_of(item.children,
+ [regex](const ExportedItemPtr &child) { return usesImport(*child, regex); });
+}
+
+static bool usesImport(const ExportedModule &module, const QString &name)
+{
+ // Imports are used in two ways:
+ // (1) var f = new TextFile(...);
+ // (2) var path = FileInfo.joinPaths(...)
+ const QString pattern
+ = QStringLiteral("(?:new[[:space:]]+%1[[:space:]]+\\()|(?:[^[:alnum:]_]?%1\\.)");
+ const QRegularExpression regex(pattern.arg(name)); // std::regex is much slower
+ return any_of(module.m_properties,
+ [regex](const ExportedProperty &p) { return usesImport(p, regex); })
+ || any_of(module.children,
+ [regex](const ExportedItemPtr &child) { return usesImport(*child, regex); });
+}
+
+static QString getLineAtLocation(const CodeLocation &loc, const QString &content)
+{
+ int pos = 0;
+ int currentLine = 1;
+ while (currentLine < loc.line()) {
+ while (content.at(pos++) != QLatin1Char('\n'))
+ ;
+ ++currentLine;
+ }
+ const int eolPos = content.indexOf(QLatin1Char('\n'), pos);
+ return content.mid(pos, eolPos - pos);
+}
+
+void ProjectResolver::resolveExport(Item *exportItem, ProjectContext *)
+{
+ ExportedModule &exportedModule = m_productContext->product->exportedModule;
+ setupExportedProperties(exportItem, QString(), exportedModule.m_properties);
+ for (const Item * const child : exportItem->children())
+ exportedModule.children.push_back(resolveExportChild(child, exportedModule));
+ for (const JsImport &jsImport : exportItem->file()->jsImports()) {
+ if (usesImport(exportedModule, jsImport.scopeName)) {
+ exportedModule.importStatements << getLineAtLocation(jsImport.location,
+ exportItem->file()->content());
+ }
+ }
+ for (const QString &builtinImport: JsExtensions::extensionNames()) {
+ if (usesImport(exportedModule, builtinImport))
+ exportedModule.importStatements << QStringLiteral("import qbs.") + builtinImport;
+ }
+}
+
+// TODO: This probably wouldn't be necessary if we had item serialization.
+std::unique_ptr<ExportedItem> ProjectResolver::resolveExportChild(const Item *item,
+ const ExportedModule &module)
+{
+ std::unique_ptr<ExportedItem> exportedItem(new ExportedItem);
+
+ // This is the type of the built-in base item. It may turn out that we need to support
+ // derived items under Export. In that case, we probably need a new Item member holding
+ // the original type name.
+ exportedItem->name = item->typeName();
+
+ for (const Item * const child : item->children())
+ exportedItem->children.push_back(resolveExportChild(child, module));
+ setupExportedProperties(item, QString(), exportedItem->properties);
+ return exportedItem;
+}
+
QString ProjectResolver::sourceCodeAsFunction(const JSSourceValueConstPtr &value,
const PropertyDeclaration &decl) const
@@ -1141,6 +1396,76 @@ void ProjectResolver::printProfilingInfo()
.arg(elapsedTimeString(m_elapsedTimeGroups));
}
+class TempScopeSetter
+{
+public:
+ TempScopeSetter(Item * item, Item *newScope) : m_item(item), m_oldScope(item->scope())
+ {
+ item->setScope(newScope);
+ }
+ ~TempScopeSetter() { m_item->setScope(m_oldScope); }
+private:
+ Item * const m_item;
+ Item * const m_oldScope;
+};
+
+void ProjectResolver::collectPropertiesForExportItem(Item *productModuleInstance)
+{
+ Item * const exportItem = productModuleInstance->prototype();
+ QBS_CHECK(exportItem && exportItem->type() == ItemType::Export);
+ TempScopeSetter tempScopeSetter(exportItem, productModuleInstance->scope());
+ const ItemDeclaration::Properties exportDecls = BuiltinDeclarations::instance()
+ .declarationsForType(ItemType::Export).properties();
+ ExportedModule &exportedModule = m_productContext->product->exportedModule;
+ const auto &props = exportItem->properties();
+ for (auto it = props.begin(); it != props.end(); ++it) {
+ const auto match
+ = [it](const PropertyDeclaration &decl) { return decl.name() == it.key(); };
+ if (it.key() != StringConstants::prefixMappingProperty() &&
+ std::find_if(exportDecls.begin(), exportDecls.end(), match) != exportDecls.end()) {
+ continue;
+ }
+ if (it.value()->type() == Value::ItemValueType) {
+ collectPropertiesForExportItem(it.key(), it.value(), productModuleInstance,
+ exportedModule.modulePropertyValues);
+ } else {
+ evaluateProperty(exportItem, it.key(), it.value(), exportedModule.propertyValues,
+ false);
+ }
+ }
+}
+
+// Collects module properties assigned to in other (higher-level) modules.
+void ProjectResolver::collectPropertiesForModuleInExportItem(const Item::Module &module)
+{
+ ExportedModule &exportedModule = m_productContext->product->exportedModule;
+ if (module.isProduct || module.name.first() == StringConstants::qbsModule())
+ return;
+ const auto checkName = [module](const ExportedModuleDependency &d) {
+ return module.name.toString() == d.name;
+ };
+ if (any_of(exportedModule.moduleDependencies, checkName))
+ return;
+
+ Item *modulePrototype = module.item->prototype();
+ while (modulePrototype && modulePrototype->type() != ItemType::Module)
+ modulePrototype = modulePrototype->prototype();
+ if (!modulePrototype) // Can happen for broken products in relaxed mode.
+ return;
+ TempScopeSetter tempScopeSetter(modulePrototype, module.item->scope());
+ const Item::PropertyMap &props = modulePrototype->properties();
+ ExportedModuleDependency dep;
+ dep.name = module.name.toString();
+ for (auto it = props.begin(); it != props.end(); ++it) {
+ if (it.value()->type() == Value::ItemValueType)
+ collectPropertiesForExportItem(it.key(), it.value(), module.item, dep.moduleProperties);
+ }
+ exportedModule.moduleDependencies.push_back(dep);
+
+ for (const auto &dep : module.item->modules())
+ collectPropertiesForModuleInExportItem(dep);
+}
+
static bool hasDependencyCycle(Set<ResolvedProduct *> *checked,
Set<ResolvedProduct *> *branch,
const ResolvedProductPtr &product,
@@ -1283,82 +1608,127 @@ QVariantMap ProjectResolver::evaluateModuleValues(Item *item, bool lookupPrototy
QVariantMap moduleValues;
for (const Item::Module &module : item->modules()) {
const QString fullName = module.name.toString();
- moduleValues[fullName] = evaluateProperties(module.item, lookupPrototype);
+ moduleValues[fullName] = evaluateProperties(module.item, lookupPrototype, true);
}
return moduleValues;
}
-QVariantMap ProjectResolver::evaluateProperties(Item *item, bool lookupPrototype)
+QVariantMap ProjectResolver::evaluateProperties(Item *item, bool lookupPrototype, bool checkErrors)
{
const QVariantMap tmplt;
- return evaluateProperties(item, item, tmplt, lookupPrototype);
+ return evaluateProperties(item, item, tmplt, lookupPrototype, checkErrors);
}
QVariantMap ProjectResolver::evaluateProperties(const Item *item, const Item *propertiesContainer,
- const QVariantMap &tmplt, bool lookupPrototype)
+ const QVariantMap &tmplt, bool lookupPrototype, bool checkErrors)
{
AccumulatingTimer propEvalTimer(m_setupParams.logElapsedTime()
? &m_elapsedTimeAllPropEval : nullptr);
QVariantMap result = tmplt;
for (QMap<QString, ValuePtr>::const_iterator it = propertiesContainer->properties().begin();
- it != propertiesContainer->properties().end(); ++it)
- {
+ it != propertiesContainer->properties().end(); ++it) {
checkCancelation();
- switch (it.value()->type()) {
- case Value::ItemValueType:
- {
- // Ignore items. Those point to module instances
- // and are handled in evaluateModuleValues().
+ evaluateProperty(item, it.key(), it.value(), result, checkErrors);
+ }
+ return lookupPrototype && propertiesContainer->prototype()
+ ? evaluateProperties(item, propertiesContainer->prototype(), result, true, checkErrors)
+ : result;
+}
+
+void ProjectResolver::evaluateProperty(const Item *item, const QString &propName,
+ const ValuePtr &propValue, QVariantMap &result, bool checkErrors)
+{
+ switch (propValue->type()) {
+ case Value::ItemValueType:
+ {
+ // Ignore items. Those point to module instances
+ // and are handled in evaluateModuleValues().
+ break;
+ }
+ case Value::JSSourceValueType:
+ {
+ if (result.contains(propName))
+ break;
+ const PropertyDeclaration pd = item->propertyDeclaration(propName);
+ if (pd.flags().testFlag(PropertyDeclaration::PropertyNotAvailableInConfig)) {
break;
}
- case Value::JSSourceValueType:
- {
- if (result.contains(it.key()))
- break;
- const PropertyDeclaration pd = item->propertyDeclaration(it.key());
- if (pd.flags().testFlag(PropertyDeclaration::PropertyNotAvailableInConfig)) {
- break;
- }
-
- const QScriptValue scriptValue = m_evaluator->property(item, it.key());
- if (Q_UNLIKELY(m_evaluator->engine()->hasErrorOrException(scriptValue))) {
- throw ErrorInfo(m_evaluator->engine()->lastError(scriptValue,
- it.value()->location()));
- }
+ const QScriptValue scriptValue = m_evaluator->property(item, propName);
+ if (checkErrors && Q_UNLIKELY(m_evaluator->engine()->hasErrorOrException(scriptValue))) {
+ throw ErrorInfo(m_evaluator->engine()->lastError(scriptValue,
+ propValue->location()));
+ }
- // NOTE: Loses type information if scriptValue.isUndefined == true,
- // as such QScriptValues become invalid QVariants.
- QVariant v = scriptValue.toVariant();
- if (pd.type() == PropertyDeclaration::Path && v.isValid()) {
- v = v.toString();
- } else if (pd.type() == PropertyDeclaration::PathList
- || pd.type() == PropertyDeclaration::StringList) {
- v = v.toStringList();
- } else if (pd.type() == PropertyDeclaration::VariantList) {
- v = v.toList();
- }
- result[it.key()] = v;
- break;
+ // NOTE: Loses type information if scriptValue.isUndefined == true,
+ // as such QScriptValues become invalid QVariants.
+ QVariant v = scriptValue.isFunction() ? scriptValue.toString() : scriptValue.toVariant();
+ if (pd.type() == PropertyDeclaration::Path && v.isValid()) {
+ v = v.toString();
+ } else if (pd.type() == PropertyDeclaration::PathList
+ || pd.type() == PropertyDeclaration::StringList) {
+ v = v.toStringList();
+ } else if (pd.type() == PropertyDeclaration::VariantList) {
+ v = v.toList();
}
- case Value::VariantValueType:
- {
- if (result.contains(it.key()))
- break;
- VariantValuePtr vvp = std::static_pointer_cast<VariantValue>(it.value());
- QVariant v = vvp->value();
+ result[propName] = v;
+ break;
+ }
+ case Value::VariantValueType:
+ {
+ if (result.contains(propName))
+ break;
+ VariantValuePtr vvp = std::static_pointer_cast<VariantValue>(propValue);
+ QVariant v = vvp->value();
- if (v.isNull() && !item->propertyDeclaration(it.key()).isScalar()) // QTBUG-51237
- v = QStringList();
+ if (v.isNull() && !item->propertyDeclaration(propName).isScalar()) // QTBUG-51237
+ v = QStringList();
- result[it.key()] = v;
- break;
- }
- }
+ result[propName] = v;
+ break;
+ }
+ }
+}
+
+void ProjectResolver::collectPropertiesForExportItem(const QualifiedId &moduleName,
+ const ValuePtr &value, Item *moduleInstance, QVariantMap &moduleProps)
+{
+ QBS_CHECK(value->type() == Value::ItemValueType);
+ Item * const itemValueItem = std::static_pointer_cast<ItemValue>(value)->item();
+ if (itemValueItem->type() == ItemType::ModuleInstance) {
+ struct EvalPreparer {
+ EvalPreparer(Item *valueItem, Item *moduleInstance, const QualifiedId &moduleName)
+ : valueItem(valueItem), oldScope(valueItem->scope()),
+ hadName(!!valueItem->variantProperty(StringConstants::nameProperty()))
+ {
+ valueItem->setScope(moduleInstance);
+ if (!hadName) {
+ // EvaluatorScriptClass expects a name here.
+ valueItem->setProperty(StringConstants::nameProperty(),
+ VariantValue::create(moduleName.toString()));
+ }
+ }
+ ~EvalPreparer()
+ {
+ valueItem->setScope(oldScope);
+ if (!hadName)
+ valueItem->setProperty(StringConstants::nameProperty(), VariantValuePtr());
+ }
+ Item * const valueItem;
+ Item * const oldScope;
+ const bool hadName;
+ };
+ EvalPreparer ep(itemValueItem, moduleInstance, moduleName);
+ moduleProps.insert(moduleName.toString(), evaluateProperties(itemValueItem, false, false));
+ return;
+ }
+ QBS_CHECK(itemValueItem->type() == ItemType::ModulePrefix);
+ const Item::PropertyMap &props = itemValueItem->properties();
+ for (auto it = props.begin(); it != props.end(); ++it) {
+ QualifiedId fullModuleName = moduleName;
+ fullModuleName << it.key();
+ collectPropertiesForExportItem(fullModuleName, it.value(), moduleInstance, moduleProps);
}
- return lookupPrototype && propertiesContainer->prototype()
- ? evaluateProperties(item, propertiesContainer->prototype(), result, true)
- : result;
}
void ProjectResolver::createProductConfig(ResolvedProduct *product)
@@ -1367,7 +1737,7 @@ void ProjectResolver::createProductConfig(ResolvedProduct *product)
m_evaluator->setPathPropertiesBaseDir(m_productContext->product->sourceDirectory);
product->moduleProperties->setValue(evaluateModuleValues(m_productContext->item));
product->productProperties = evaluateProperties(m_productContext->item, m_productContext->item,
- QVariantMap());
+ QVariantMap(), true, true);
m_evaluator->clearPathPropertiesBaseDir();
}