diff options
author | Joerg Bornemann <joerg.bornemann@theqtcompany.com> | 2015-05-19 18:37:31 +0200 |
---|---|---|
committer | Joerg Bornemann <joerg.bornemann@theqtcompany.com> | 2015-05-21 10:37:12 +0000 |
commit | 16404c356b0e160243de315c48975a82cc343893 (patch) | |
tree | fa2c77df7b989fc789b7bc4bca854bc652777939 | |
parent | 1b8c356e52a16ed1b4066e4a345e7da92cfdaaaa (diff) | |
download | qbs-16404c356b0e160243de315c48975a82cc343893.tar.gz |
fix property lookup
Merge the modules of the same name in the module hierarchy of a
product into one. This way we get correct dependency resolution
between properties.
Remove the awful hacks that we have introduced to solve QBS-736 and
QBS-706.
Instead of a hierarchy of QVariantMaps representing all module
values we store a flat map that contains the final (potentially
merged) module values. This leads to a significant speed up in
project loading and rule execution.
Task-number: QBS-736
Change-Id: I4dc6ce9566118a9f2bdc4536b1449164899db0e8
Reviewed-by: Christian Kandeler <christian.kandeler@theqtcompany.com>
-rw-r--r-- | src/lib/corelib/corelib.qbs | 2 | ||||
-rw-r--r-- | src/lib/corelib/language/evaluatorscriptclass.cpp | 4 | ||||
-rw-r--r-- | src/lib/corelib/language/item.h | 1 | ||||
-rw-r--r-- | src/lib/corelib/language/language.pri | 2 | ||||
-rw-r--r-- | src/lib/corelib/language/moduleloader.cpp | 51 | ||||
-rw-r--r-- | src/lib/corelib/language/moduleloader.h | 4 | ||||
-rw-r--r-- | src/lib/corelib/language/modulemerger.cpp | 195 | ||||
-rw-r--r-- | src/lib/corelib/language/modulemerger.h | 69 | ||||
-rw-r--r-- | src/lib/corelib/language/projectresolver.cpp | 195 | ||||
-rw-r--r-- | src/lib/corelib/language/projectresolver.h | 14 | ||||
-rw-r--r-- | src/lib/corelib/language/propertydeclaration.cpp | 6 | ||||
-rw-r--r-- | src/lib/corelib/language/propertydeclaration.h | 1 | ||||
-rw-r--r-- | src/lib/corelib/language/tst_language.cpp | 23 | ||||
-rw-r--r-- | src/lib/corelib/language/value.cpp | 25 | ||||
-rw-r--r-- | src/lib/corelib/language/value.h | 8 | ||||
-rw-r--r-- | src/lib/corelib/tools/propertyfinder.cpp | 78 | ||||
-rw-r--r-- | src/lib/corelib/tools/propertyfinder.h | 5 | ||||
-rw-r--r-- | tests/auto/blackbox/tst_blackbox.cpp | 1 |
18 files changed, 482 insertions, 202 deletions
diff --git a/src/lib/corelib/corelib.qbs b/src/lib/corelib/corelib.qbs index 8dca80dbd..ab2cadb80 100644 --- a/src/lib/corelib/corelib.qbs +++ b/src/lib/corelib/corelib.qbs @@ -229,6 +229,8 @@ QbsLibrary { "loader.h", "moduleloader.cpp", "moduleloader.h", + "modulemerger.cpp", + "modulemerger.h", "preparescriptobserver.cpp", "preparescriptobserver.h", "projectresolver.cpp", diff --git a/src/lib/corelib/language/evaluatorscriptclass.cpp b/src/lib/corelib/language/evaluatorscriptclass.cpp index 0fa00ae80..abdb782d1 100644 --- a/src/lib/corelib/language/evaluatorscriptclass.cpp +++ b/src/lib/corelib/language/evaluatorscriptclass.cpp @@ -205,6 +205,8 @@ private: } engine->currentContext()->pushScope(conditionFileScope); pushItemScopes(conditionScopeItem); + if (alternative->value->definingItem()) + pushItemScopes(alternative->value->definingItem()); engine->currentContext()->pushScope(conditionScope); const QScriptValue cr = engine->evaluate(alternative->condition); engine->currentContext()->popScope(); @@ -248,6 +250,8 @@ private: pushScope(data->evaluator->fileScope(value->file())); pushItemScopes(data->item); + if (value->definingItem()) + pushItemScopes(value->definingItem()); if (itemOfProperty && !itemOfProperty->isModuleInstance()) { // Own properties of module instances must not have the instance itself in the scope. pushScope(*object); diff --git a/src/lib/corelib/language/item.h b/src/lib/corelib/language/item.h index dd7141b17..82e5c377d 100644 --- a/src/lib/corelib/language/item.h +++ b/src/lib/corelib/language/item.h @@ -104,6 +104,7 @@ public: VariantValuePtr variantProperty(const QString &name) const; void setPropertyObserver(ItemObserver *observer) const; void setProperty(const QString &name, const ValuePtr &value); + void setProperties(const PropertyMap &props) { m_properties = props; } void removeProperty(const QString &name); void setPropertyDeclaration(const QString &name, const PropertyDeclaration &declaration); void setTypeName(const QString &name); diff --git a/src/lib/corelib/language/language.pri b/src/lib/corelib/language/language.pri index c4091f0e1..c0ac83e72 100644 --- a/src/lib/corelib/language/language.pri +++ b/src/lib/corelib/language/language.pri @@ -25,6 +25,7 @@ HEADERS += \ $$PWD/language.h \ $$PWD/loader.h \ $$PWD/moduleloader.h \ + $$PWD/modulemerger.h \ $$PWD/preparescriptobserver.h \ $$PWD/projectresolver.h \ $$PWD/property.h \ @@ -54,6 +55,7 @@ SOURCES += \ $$PWD/language.cpp \ $$PWD/loader.cpp \ $$PWD/moduleloader.cpp \ + $$PWD/modulemerger.cpp \ $$PWD/preparescriptobserver.cpp \ $$PWD/projectresolver.cpp \ $$PWD/propertydeclaration.cpp \ diff --git a/src/lib/corelib/language/moduleloader.cpp b/src/lib/corelib/language/moduleloader.cpp index 8f211e5bb..8241fe01a 100644 --- a/src/lib/corelib/language/moduleloader.cpp +++ b/src/lib/corelib/language/moduleloader.cpp @@ -456,6 +456,7 @@ void ModuleLoader::handleProduct(ProjectContext *projectContext, Item *item) dependsContext.productDependencies = &productContext.info.usedProducts; setScopeForDescendants(item, productContext.scope); resolveDependencies(&dependsContext, item); + addTransitiveDependencies(&productContext, productContext.item); checkItemCondition(item); foreach (Item *child, item->children()) { @@ -467,7 +468,8 @@ void ModuleLoader::handleProduct(ProjectContext *projectContext, Item *item) resolveProbe(item, child); } - mergeExportItems(&productContext); + Item *mergedExportItem = mergeExportItems(&productContext); + addTransitiveDependencies(&productContext, mergedExportItem); projectContext->result->productInfos.insert(item, productContext.info); m_reader->popExtraSearchPaths(); } @@ -565,7 +567,7 @@ static void mergeProperty(Item *dst, const QString &name, const ValuePtr &value) } } -void ModuleLoader::mergeExportItems(ModuleLoader::ProductContext *productContext) +Item *ModuleLoader::mergeExportItems(ModuleLoader::ProductContext *productContext) { Item *merged = Item::create(productContext->item->pool()); merged->setTypeName(QLatin1String("Export")); @@ -603,6 +605,7 @@ void ModuleLoader::mergeExportItems(ModuleLoader::ProductContext *productContext dependsContext.product = productContext; dependsContext.productDependencies = &productContext->info.usedProductsFromExportItem; resolveDependencies(&dependsContext, merged); + return merged; } void ModuleLoader::propagateModulesFromProduct(ProductContext *productContext, Item *item) @@ -1438,6 +1441,11 @@ QString ModuleLoader::fullModuleName(const QStringList &moduleName) return moduleName.join(QLatin1Char('.')); } +QStringList ModuleLoader::fromFullModuleName(const QString &name) +{ + return name.split(QLatin1Char('.')); +} + void ModuleLoader::overrideItemProperties(Item *item, const QString &buildConfigKey, const QVariantMap &buildConfig) { @@ -1459,6 +1467,45 @@ void ModuleLoader::overrideItemProperties(Item *item, const QString &buildConfig } } +static void collectAllModuleNames(Item *item, QVector<QStringList> *names) +{ + foreach (const Item::Module &m, item->modules()) { + if (names->contains(m.name)) + continue; + names->append(m.name); + collectAllModuleNames(m.item, names); + } +} + +static QVector<QStringList> allModuleNames(Item *item) +{ + QVector<QStringList> lst; + collectAllModuleNames(item, &lst); + return lst; +} + +void ModuleLoader::addTransitiveDependencies(ProductContext *ctx, Item *item) +{ + if (m_logger.traceEnabled()) + m_logger.qbsTrace() << "[MODLDR] addTransitiveDependencies"; + QVector<QStringList> transitiveDeps = allModuleNames(item); + std::sort(transitiveDeps.begin(), transitiveDeps.end()); + foreach (const Item::Module &m, item->modules()) { + auto it = std::lower_bound(transitiveDeps.begin(), transitiveDeps.end(), m.name); + if (it != transitiveDeps.end() && *it == m.name) + transitiveDeps.erase(it); + } + foreach (const QStringList &moduleName, transitiveDeps) { + Item::Module dep; + dep.item = loadModule(ctx, item, item->location(), QString(), moduleName, + false, true); + if (!dep.item) + continue; + dep.name = moduleName; + item->modules() += dep; + } +} + QString ModuleLoaderResult::ProductInfo::Dependency::uniqueName() const { return ResolvedProduct::uniqueName(name, profile); diff --git a/src/lib/corelib/language/moduleloader.h b/src/lib/corelib/language/moduleloader.h index 41f457503..eae4507cc 100644 --- a/src/lib/corelib/language/moduleloader.h +++ b/src/lib/corelib/language/moduleloader.h @@ -108,6 +108,7 @@ public: ModuleLoaderResult load(const SetupProjectParameters ¶meters); static QString fullModuleName(const QStringList &moduleName); + static QStringList fromFullModuleName(const QString &name); private: struct ItemCacheValue { @@ -167,7 +168,7 @@ private: const QSet<QString> &referencedFilePaths); void handleGroup(ProductContext *productContext, Item *group); void deferExportItem(ProductContext *productContext, Item *item); - void mergeExportItems(ProductContext *productContext); + Item *mergeExportItems(ProductContext *productContext); void propagateModulesFromProduct(ProductContext *productContext, Item *item); void resolveDependencies(DependsContext *productContext, Item *item); class ItemModuleList; @@ -201,6 +202,7 @@ private: static void setScopeForDescendants(Item *item, Item *scope); void overrideItemProperties(Item *item, const QString &buildConfigKey, const QVariantMap &buildConfig); + void addTransitiveDependencies(ProductContext *ctx, Item *item); ScriptEngine *m_engine; ItemPool *m_pool; diff --git a/src/lib/corelib/language/modulemerger.cpp b/src/lib/corelib/language/modulemerger.cpp new file mode 100644 index 000000000..58a9992c2 --- /dev/null +++ b/src/lib/corelib/language/modulemerger.cpp @@ -0,0 +1,195 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing +** +** This file is part of the Qt Build Suite. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms and +** conditions see http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#include "modulemerger.h" + +#include <logging/translator.h> +#include <tools/qbsassert.h> + +namespace qbs { +namespace Internal { + +ModuleMerger::ModuleMerger(const Logger &logger, Item *root, Item *moduleToMerge, + const QStringList &moduleName) + : m_logger(logger) + , m_rootItem(root) + , m_mergedModuleItem(moduleToMerge) + , m_moduleName(moduleName) +{ +} + +void ModuleMerger::start() +{ + Item::Module m; + m.item = m_rootItem; + const Item::PropertyMap props = dfs(m, Item::PropertyMap()); + Item::PropertyMap mergedProps = m_mergedModuleItem->properties(); + for (auto it = props.constBegin(); it != props.constEnd(); ++it) + mergedProps[it.key()] = it.value(); + m_mergedModuleItem->setProperties(mergedProps); +} + +Item::PropertyMap ModuleMerger::dfs(const Item::Module &m, Item::PropertyMap props) +{ + Item *moduleInstance = 0; + int numberOfOutprops = m.item->modules().count(); + foreach (const Item::Module &dep, m.item->modules()) { + if (dep.name == m_moduleName) { + --numberOfOutprops; + moduleInstance = dep.item; + pushScalarProperties(&props, moduleInstance); + break; + } + } + + QVector<Item::PropertyMap> outprops; + outprops.reserve(numberOfOutprops); + foreach (const Item::Module &dep, m.item->modules()) { + if (dep.item != moduleInstance) + outprops << dfs(dep, props); + } + + if (!outprops.isEmpty()) { + props = outprops.first(); + for (int i = 1; i < outprops.count(); ++i) + mergeOutProps(&props, outprops.at(i)); + } + + if (moduleInstance) + pullListProperties(&props, moduleInstance); + + return props; +} + +void ModuleMerger::pushScalarProperties(Item::PropertyMap *dst, Item *srcItem) +{ + Item *origSrcItem = srcItem; + do { + if (!m_seenInstancesTopDown.contains(srcItem)) { + m_seenInstancesTopDown.insert(srcItem); + for (auto it = srcItem->properties().constBegin(); + it != srcItem->properties().constEnd(); ++it) { + const ValuePtr &srcVal = it.value(); + if (srcVal->type() != Value::JSSourceValueType) + continue; + const PropertyDeclaration srcDecl = srcItem->propertyDeclaration(it.key()); + if (!srcDecl.isValid() || !srcDecl.isScalar()) + continue; + ValuePtr &v = (*dst)[it.key()]; + if (v) + continue; + JSSourceValuePtr clonedVal = srcVal->clone().dynamicCast<JSSourceValue>(); + m_decls[clonedVal] = srcDecl; + setDefiningItem(clonedVal, origSrcItem); + v = clonedVal; + } + } + srcItem = srcItem->prototype(); + } while (srcItem && srcItem->isModuleInstance()); +} + +void ModuleMerger::mergeOutProps(Item::PropertyMap *dst, const Item::PropertyMap &src) +{ + for (auto it = src.constBegin(); it != src.constEnd(); ++it) { + ValuePtr &v = (*dst)[it.key()]; + if (!v) { + v = it.value(); + QBS_ASSERT(it.value(), continue); + continue; + } + // possible conflict + JSSourceValuePtr dstVal = v.dynamicCast<JSSourceValue>(); + if (!dstVal) + continue; + JSSourceValuePtr srcVal = it.value().dynamicCast<JSSourceValue>(); + if (!srcVal) + continue; + + const PropertyDeclaration pd = m_decls.value(srcVal); + if (!pd.isValid()) + continue; + + if (pd.isScalar()) { + if (dstVal->sourceCode() != srcVal->sourceCode()) { + m_logger.qbsWarning() << Tr::tr("Conflicting scalar values at %1 and %2.").arg( + dstVal->location().toString(), + srcVal->location().toString()); + // TODO: yield error with a hint how to solve the conflict. + } + v = it.value(); + } else { + JSSourceValuePtr lastDstNext = dstVal; + while (lastDstNext->next()) { + lastDstNext = lastDstNext->next(); + } + lastDstNext->setNext(srcVal); + } + } +} + +void ModuleMerger::pullListProperties(Item::PropertyMap *dst, Item *instance) +{ + Item *origInstance = instance; + do { + if (!m_seenInstancesBottomUp.contains(instance)) { + m_seenInstancesBottomUp.insert(instance); + for (Item::PropertyMap::const_iterator it = instance->properties().constBegin(); + it != instance->properties().constEnd(); ++it) { + const ValuePtr &srcVal = it.value(); + if (srcVal->type() != Value::JSSourceValueType) + continue; + const PropertyDeclaration srcDecl = instance->propertyDeclaration(it.key()); + if (!srcDecl.isValid() || srcDecl.isScalar()) + continue; + JSSourceValuePtr clonedVal = srcVal->clone().dynamicCast<JSSourceValue>(); + m_decls[clonedVal] = srcDecl; + setDefiningItem(clonedVal, origInstance); + ValuePtr &v = (*dst)[it.key()]; + if (v) { + JSSourceValuePtr dstSrcVal = v.dynamicCast<JSSourceValue>(); + QBS_CHECK(!clonedVal->next()); + clonedVal->setNext(dstSrcVal); + } + v = clonedVal; + } + } + instance = instance->prototype(); + } while (instance && instance->isModuleInstance()); +} + +void ModuleMerger::setDefiningItem(const JSSourceValuePtr &v, Item *item) +{ + v->setDefiningItem(item); + foreach (const JSSourceValue::Alternative &a, v->alternatives()) + a.value->setDefiningItem(item); +} + +} // namespace Internal +} // namespace qbs diff --git a/src/lib/corelib/language/modulemerger.h b/src/lib/corelib/language/modulemerger.h new file mode 100644 index 000000000..f44c32d8e --- /dev/null +++ b/src/lib/corelib/language/modulemerger.h @@ -0,0 +1,69 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing +** +** This file is part of the Qt Build Suite. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms and +** conditions see http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#ifndef QBS_MODULEMERGER_H +#define QBS_MODULEMERGER_H + +#include "item.h" +#include <logging/logger.h> + +#include <QHash> +#include <QSet> +#include <QStringList> + +namespace qbs { +namespace Internal { + +class ModuleMerger { +public: + ModuleMerger(const Logger &logger, Item *root, Item *moduleToMerge, + const QStringList &moduleName); + void start(); + +private: + Item::PropertyMap dfs(const Item::Module &m, Item::PropertyMap props); + void pushScalarProperties(Item::PropertyMap *dst, Item *srcItem); + void mergeOutProps(Item::PropertyMap *dst, const Item::PropertyMap &src); + void pullListProperties(Item::PropertyMap *dst, Item *instance); + static void setDefiningItem(const JSSourceValuePtr &v, Item *item); + + const Logger &m_logger; + Item * const m_rootItem; + Item *m_mergedModuleItem; + const QStringList m_moduleName; + QHash<JSSourceValuePtr, PropertyDeclaration> m_decls; + QSet<const Item *> m_seenInstancesTopDown; + QSet<const Item *> m_seenInstancesBottomUp; +}; + +} // namespace Internal +} // namespace qbs + +#endif // QBS_MODULEMERGER_H diff --git a/src/lib/corelib/language/projectresolver.cpp b/src/lib/corelib/language/projectresolver.cpp index 46a5a5498..ba920d8e8 100644 --- a/src/lib/corelib/language/projectresolver.cpp +++ b/src/lib/corelib/language/projectresolver.cpp @@ -36,6 +36,7 @@ #include "filecontext.h" #include "item.h" #include "moduleloader.h" +#include "modulemerger.h" #include "propertymapinternal.h" #include "resolvedfilecontext.h" #include "scriptengine.h" @@ -858,6 +859,10 @@ void ProjectResolver::resolveExport(Item *item, ProjectContext *projectContext) const QString &productName = m_productContext->product->uniqueName(); m_exportsContext = &m_exports[productName]; m_exportsContext->item = item; + foreach (const Item::Module &module, item->modules()) { + ModuleMerger merger(m_logger, item, module.item, module.name); + merger.start(); + } m_exportsContext->moduleValues = evaluateModuleValues(item, false); ItemFuncMap mapping; @@ -871,15 +876,52 @@ void ProjectResolver::resolveExport(Item *item, ProjectContext *projectContext) m_exportsContext = 0; } -static void insertExportedConfig(const QString &usedProductName, - const QVariantMap &exportedConfig, - const PropertyMapPtr &propertyMap) +static void insertExportedModuleProperties(const Item::Module &moduleInProduct, + const Item::Module &moduleInExport, const QVariantMap &exported, QVariantMap *dstModule) +{ + for (auto it = exported.constBegin(); it != exported.constEnd(); ++it) { + QVariant &dst = (*dstModule)[it.key()]; + const PropertyDeclaration &pd = moduleInExport.item->propertyDeclaration(it.key()); + if (pd.isScalar()) { + // If this scalar property is not directly set in the product. + if (moduleInProduct.item && moduleInProduct.item->hasOwnProperty(it.key())) + continue; + dst = it.value(); + } else { + // TODO: Make the merge configurable: Order, Duplicates, ...? + QStringList lst = dst.toStringList() + it.value().toStringList(); + lst.removeDuplicates(); + dst = lst; + } + } +} + +static void insertExportedConfig(Item *productItem, Item *exportItem, + const QVariantMap &exportedConfig, const PropertyMapPtr &propertyMap) { + QHash<QStringList, Item::Module> productModules; + foreach (const Item::Module &m, productItem->modules()) + productModules[m.name] = m; + + QHash<QStringList, Item::Module> exportModules; + foreach (const Item::Module &m, exportItem->modules()) + exportModules[m.name] = m; + QVariantMap properties = propertyMap->value(); - QVariant &modulesEntry = properties[QLatin1String("modules")]; - QVariantMap modules = modulesEntry.toMap(); - modules.insert(usedProductName, exportedConfig); - modulesEntry = modules; + QVariant &modulesVariant = properties[QStringLiteral("modules")]; + QVariantMap modules = modulesVariant.toMap(); + const QVariantMap &exportedModules = exportedConfig.value(QStringLiteral("modules")).toMap(); + for (auto it = exportedModules.constBegin(); it != exportedModules.constEnd(); ++it) { + const QString &fullModuleName = it.key(); + const QStringList &moduleName = ModuleLoader::fromFullModuleName(fullModuleName); + QVariant &moduleVariant = modules[fullModuleName]; + QVariantMap module = moduleVariant.toMap(); + insertExportedModuleProperties(productModules.value(moduleName), + exportModules.value(moduleName), it.value().toMap(), + &module); + moduleVariant = module; + } + modulesVariant = modules; propertyMap->setValue(properties); } @@ -1095,12 +1137,14 @@ void ProjectResolver::resolveProductDependencies(ProjectContext *projectContext) if (exportedConfig.isEmpty()) continue; - insertExportedConfig(usedProductName, exportedConfig, rproduct->moduleProperties); + insertExportedConfig(productItem, ctx.item, exportedConfig, rproduct->moduleProperties); // insert the configuration of the Export item into the artifact configurations foreach (SourceArtifactPtr artifact, rproduct->allEnabledFiles()) { - if (artifact->properties != rproduct->moduleProperties) - insertExportedConfig(usedProductName, exportedConfig, artifact->properties); + if (artifact->properties != rproduct->moduleProperties) { + insertExportedConfig(productItem, ctx.item, exportedConfig, + artifact->properties); + } } } } @@ -1147,79 +1191,26 @@ void ProjectResolver::applyFileTaggers(const SourceArtifactPtr &artifact, } } -struct ModuleInfo { - QString fullName; - QVariantMap propertyMap; - QStringList ownProperties; -}; - -static void gatherModuleValues(Item *item, QVariantMap *parentModulesMap, - const QHash<Item *, ModuleInfo> &moduleInfo) -{ - foreach (const Item::Module &module, item->modules()) { - const ModuleInfo &mi = moduleInfo.value(module.item); - QVariantMap modulesMap; - gatherModuleValues(module.item, &modulesMap, moduleInfo); - QVariantMap propertyMap = mi.propertyMap; - propertyMap.insert(QLatin1String("modules"), modulesMap); - parentModulesMap->insert(mi.fullName, propertyMap); - parentModulesMap->insert(QLatin1Char('@') + mi.fullName, mi.ownProperties); - } -} - -static QStringList ownPropertiesSet(Item *item) -{ - QStringList names; - do { - names += item->properties().keys(); - item = item->prototype(); - } while (item && item->isModuleInstance()); - - std::sort(names.begin(), names.end()); - QStringList::iterator lastIt = std::unique(names.begin(), names.end()); - names.erase(lastIt, names.end()); - return names; -} - QVariantMap ProjectResolver::evaluateModuleValues(Item *item, bool lookupPrototype) const { - QHash<Item *, ModuleInfo> moduleInfo; - QHash<QString, EvalResult> globalResult; - - // Optimization in evaluateProperties() requires breadth-first traversal. - QList<Item::Module> modulesQueue = item->modules(); - while (!modulesQueue.isEmpty()) { - checkCancelation(); - const Item::Module module = modulesQueue.takeFirst(); + QVariantMap moduleValues; + foreach (const Item::Module &module, item->modules()) { const QString fullName = ModuleLoader::fullModuleName(module.name); - ModulePropertyEvalContext evalContext; - evalContext.globalResult = &globalResult; - evalContext.moduleName = fullName; - evalContext.ownProperties = ownPropertiesSet(module.item); - ModuleInfo mi; - mi.fullName = fullName; - mi.propertyMap = evaluateProperties(module.item, evalContext, lookupPrototype); - mi.ownProperties = evalContext.ownProperties; - moduleInfo.insert(module.item, mi); - modulesQueue << module.item->modules(); + moduleValues[fullName] = evaluateProperties(module.item, lookupPrototype); } - QVariantMap modules; - gatherModuleValues(item, &modules, moduleInfo); QVariantMap result; - result[QLatin1String("modules")] = modules; + result[QLatin1String("modules")] = moduleValues; return result; } -QVariantMap ProjectResolver::evaluateProperties(Item *item, - const ModulePropertyEvalContext &evalContext, bool lookupPrototype) const +QVariantMap ProjectResolver::evaluateProperties(Item *item, bool lookupPrototype) const { const QVariantMap tmplt; - return evaluateProperties(item, item, evalContext, tmplt, lookupPrototype); + return evaluateProperties(item, item, tmplt, lookupPrototype); } -QVariantMap ProjectResolver::evaluateProperties(Item *item, - Item *propertiesContainer, const ModulePropertyEvalContext &evalContext, +QVariantMap ProjectResolver::evaluateProperties(Item *item, Item *propertiesContainer, const QVariantMap &tmplt, bool lookupPrototype) const { QVariantMap result = tmplt; @@ -1227,7 +1218,6 @@ QVariantMap ProjectResolver::evaluateProperties(Item *item, it != propertiesContainer->properties().end(); ++it) { checkCancelation(); - const QString fullKey = evalContext.moduleName + QLatin1Char('.') + it.key(); switch (it.value()->type()) { case Value::ItemValueType: { @@ -1246,24 +1236,6 @@ QVariantMap ProjectResolver::evaluateProperties(Item *item, break; } - // Skip values that the PropertyFinder will never see due to them being shadowed - // by values in other, higher-precedence instances of the module. - const bool isPotentialGlobalEntry = evalContext.globalResult - && pd.type() != PropertyDeclaration::StringList - && pd.type() != PropertyDeclaration::PathList; - const bool isOwnProperty = std::binary_search(evalContext.ownProperties.constBegin(), - evalContext.ownProperties.constEnd(), it.key()); - if (isPotentialGlobalEntry) { - const QHash<QString, EvalResult>::ConstIterator globalIt - = evalContext.globalResult->find(fullKey); - if (globalIt != evalContext.globalResult->constEnd()) { - if (!isOwnProperty || globalIt->strongPrecedence) { - result[it.key()] = globalIt->value; - break; - } - } - } - bool cacheDisabled = false; const JSSourceValuePtr srcValue = it.value().staticCast<JSSourceValue>(); if (m_disableCachedEvaluation && (pd.type() == PropertyDeclaration::Path @@ -1273,7 +1245,7 @@ QVariantMap ProjectResolver::evaluateProperties(Item *item, m_evaluator->engine()->setPropertyCacheEnabled(false); } - const QScriptValue scriptValue = m_evaluator->value(item, it.key()); + const QScriptValue scriptValue = evaluateJSSourceValue(item, srcValue, it.key()); if (cacheDisabled) { m_evaluator->setCachingEnabled(true); m_evaluator->engine()->setPropertyCacheEnabled(true); @@ -1294,8 +1266,6 @@ QVariantMap ProjectResolver::evaluateProperties(Item *item, else if (pd.type() == PropertyDeclaration::StringList) v = v.toStringList(); result[it.key()] = v; - if (isPotentialGlobalEntry) - evalContext.globalResult->insert(fullKey, EvalResult(v, isOwnProperty)); break; } case Value::VariantValueType: @@ -1304,8 +1274,6 @@ QVariantMap ProjectResolver::evaluateProperties(Item *item, break; VariantValuePtr vvp = it.value().staticCast<VariantValue>(); result[it.key()] = vvp->value(); - if (evalContext.globalResult) - evalContext.globalResult->insert(fullKey, EvalResult(vvp->value(), true)); break; } case Value::BuiltinValueType: @@ -1314,15 +1282,48 @@ QVariantMap ProjectResolver::evaluateProperties(Item *item, } } return lookupPrototype && propertiesContainer->prototype() - ? evaluateProperties(item, propertiesContainer->prototype(), evalContext, result, true) + ? evaluateProperties(item, propertiesContainer->prototype(), result, true) : result; } +QScriptValue ProjectResolver::evaluateJSSourceValue(Item *item, const JSSourceValuePtr &sourceValue, + const QString &propertyName) const +{ + if (!sourceValue->next()) + return m_evaluator->value(item, propertyName); + + QScriptValueList lst; + for (JSSourceValuePtr sv = sourceValue; sv; sv = sv->next()) { + QScriptValue v = m_evaluator->property(sv->definingItem(), propertyName); + if (v.isError()) + return v; + if (!v.isUndefined()) + lst << v; + } + QScriptValue a = m_engine->newArray(); + quint32 k = 0; + for (int i = 0; i < lst.count(); ++i) { + const QScriptValue &v = lst.at(i); + if (v.isArray()) { + const quint32 vlen = v.property(QStringLiteral("length")).toInt32(); + for (quint32 j = 0; j < vlen; ++j) + a.setProperty(k++, v.property(j)); + } else { + a.setProperty(k++, v); + } + } + return a; +} + QVariantMap ProjectResolver::createProductConfig() const { + foreach (const Item::Module &module, m_productContext->item->modules()) { + ModuleMerger merger(m_logger, m_productContext->item, module.item, module.name); + merger.start(); + } + QVariantMap cfg = evaluateModuleValues(m_productContext->item); - cfg = evaluateProperties(m_productContext->item, m_productContext->item, - ModulePropertyEvalContext(), cfg); + cfg = evaluateProperties(m_productContext->item, m_productContext->item, cfg); return cfg; } diff --git a/src/lib/corelib/language/projectresolver.h b/src/lib/corelib/language/projectresolver.h index 753d2c975..6acc1bcd3 100644 --- a/src/lib/corelib/language/projectresolver.h +++ b/src/lib/corelib/language/projectresolver.h @@ -140,17 +140,11 @@ private: bool strongPrecedence; }; - struct ModulePropertyEvalContext { - ModulePropertyEvalContext() : globalResult(0) {} - QString moduleName; - QHash<QString, EvalResult> *globalResult; - QStringList ownProperties; - }; - QVariantMap evaluateProperties(Item *item, const ModulePropertyEvalContext &evalContext, - bool lookupPrototype = true) const; - QVariantMap evaluateProperties(Item *item, Item *propertiesContainer, - const ModulePropertyEvalContext &evalContext, const QVariantMap &tmplt, + QVariantMap evaluateProperties(Item *item, bool lookupPrototype = true) const; + QVariantMap evaluateProperties(Item *item, Item *propertiesContainer, const QVariantMap &tmplt, bool lookupPrototype = true) const; + QScriptValue evaluateJSSourceValue(Item *item, const JSSourceValuePtr &sourceValue, + const QString &propertyName) const; QVariantMap createProductConfig() const; QString convertPathProperty(const QString &path, const QString &dirPath) const; QStringList convertPathListProperty(const QStringList &paths, const QString &dirPath) const; diff --git a/src/lib/corelib/language/propertydeclaration.cpp b/src/lib/corelib/language/propertydeclaration.cpp index 9cd76b6fd..c79aed4ab 100644 --- a/src/lib/corelib/language/propertydeclaration.cpp +++ b/src/lib/corelib/language/propertydeclaration.cpp @@ -90,6 +90,12 @@ bool PropertyDeclaration::isValid() const return d && d->type != UnknownType; } +bool PropertyDeclaration::isScalar() const +{ + // ### Should be determined by a PropertyOption in the future. + return d->type != PathList && d->type != StringList; +} + PropertyDeclaration::Type PropertyDeclaration::propertyTypeFromString(const QString &typeName) { if (typeName == QLatin1String("bool")) diff --git a/src/lib/corelib/language/propertydeclaration.h b/src/lib/corelib/language/propertydeclaration.h index a18fc93fe..8071b201a 100644 --- a/src/lib/corelib/language/propertydeclaration.h +++ b/src/lib/corelib/language/propertydeclaration.h @@ -73,6 +73,7 @@ public: PropertyDeclaration &operator=(const PropertyDeclaration &other); bool isValid() const; + bool isScalar() const; static Type propertyTypeFromString(const QString &typeName); diff --git a/src/lib/corelib/language/tst_language.cpp b/src/lib/corelib/language/tst_language.cpp index 382906e82..a429473f9 100644 --- a/src/lib/corelib/language/tst_language.cpp +++ b/src/lib/corelib/language/tst_language.cpp @@ -462,8 +462,7 @@ void TestLanguage::exports() ResolvedProductPtr product; product = products.value("myapp"); QVERIFY(product); - QStringList propertyName = QStringList() << "modules" << "mylib.qbs_autotests" - << "modules" << "dummy" << "defines"; + QStringList propertyName = QStringList() << "modules" << "dummy" << "defines"; QVariant propertyValue = getConfigProperty(product->moduleProperties->value(), propertyName); QCOMPARE(propertyValue.toStringList(), QStringList() << "USE_MYLIB"); @@ -493,12 +492,10 @@ void TestLanguage::exports() product = products.value("myapp2"); QVERIFY(product); - propertyName = QStringList() << "modules" << "productWithInheritedExportItem.qbs_autotests" - << "modules" << "dummy" << "cxxFlags"; + propertyName = QStringList() << "modules" << "dummy" << "cxxFlags"; propertyValue = getConfigProperty(product->moduleProperties->value(), propertyName); QCOMPARE(propertyValue.toStringList(), QStringList() << "-bar"); - propertyName = QStringList() << "modules" << "productWithInheritedExportItem.qbs_autotests" - << "modules" << "dummy" << "defines"; + propertyName = QStringList() << "modules" << "dummy" << "defines"; propertyValue = getConfigProperty(product->moduleProperties->value(), propertyName); QCOMPARE(propertyValue.toStringList(), QStringList() << "ABC"); QCOMPARE(PropertyFinder().propertyValue(product->moduleProperties->value(), "dummy", @@ -923,7 +920,7 @@ void TestLanguage::moduleProperties_data() << (QStringList() << "THE_PRODUCT" << "QT_GUI" << "QT_CORE" << "QT_NETWORK"); QTest::newRow("merge_lists_and_values") << "defines" - << (QStringList() << "THE_PRODUCT" << "QT_GUI" << "QT_CORE" << "QT_NETWORK"); + << (QStringList() << "THE_PRODUCT" << "QT_NETWORK" << "QT_CORE" << "QT_GUI"); QTest::newRow("merge_lists_with_duplicates") << "cxxFlags" << (QStringList() << "-foo" << "BAR" << "-foo" << "BAZ"); @@ -940,8 +937,7 @@ void TestLanguage::moduleProperties() ResolvedProductPtr product = products.value(productName); QVERIFY(product); QVariantList values = PropertyFinder().propertyValues(product->moduleProperties->value(), - "dummy", propertyName, - PropertyFinder::DoMergeLists); + "dummy", propertyName); QStringList valueStrings; foreach (const QVariant &v, values) valueStrings += v.toString(); @@ -1134,16 +1130,13 @@ void TestLanguage::profileValuesAndOverriddenValues() QVERIFY(product); PropertyFinder pf; QVariantList values; - values = pf.propertyValues(product->moduleProperties->value(), - "dummy", "cxxFlags", PropertyFinder::DoMergeLists); + values = pf.propertyValues(product->moduleProperties->value(), "dummy", "cxxFlags"); QCOMPARE(values.length(), 1); QCOMPARE(values.first().toString(), QString("IN_PROFILE")); - values = pf.propertyValues(product->moduleProperties->value(), - "dummy", "defines", PropertyFinder::DoMergeLists); + values = pf.propertyValues(product->moduleProperties->value(), "dummy", "defines"); QCOMPARE(values.length(), 1); QCOMPARE(values.first().toString(), QString("IN_FILE")); - values = pf.propertyValues(product->moduleProperties->value(), - "dummy", "cFlags", PropertyFinder::DoMergeLists); + values = pf.propertyValues(product->moduleProperties->value(), "dummy", "cFlags"); QCOMPARE(values.length(), 1); QCOMPARE(values.first().toString(), QString("OVERRIDDEN")); } catch (const ErrorInfo &e) { diff --git a/src/lib/corelib/language/value.cpp b/src/lib/corelib/language/value.cpp index cd354ae13..62dbfed17 100644 --- a/src/lib/corelib/language/value.cpp +++ b/src/lib/corelib/language/value.cpp @@ -31,6 +31,8 @@ #include "value.h" #include "item.h" +#include <tools/qbsassert.h> + namespace qbs { namespace Internal { @@ -45,7 +47,7 @@ Value::~Value() JSSourceValue::JSSourceValue() - : Value(JSSourceValueType), m_line(-1), m_column(-1) + : Value(JSSourceValueType), m_line(-1), m_column(-1), m_definingItem(0) { } @@ -93,6 +95,27 @@ void JSSourceValue::setHasFunctionForm(bool b) m_flags &= ~HasFunctionForm; } +Item *JSSourceValue::definingItem() const +{ + return m_definingItem; +} + +void JSSourceValue::setDefiningItem(Item *item) +{ + m_definingItem = item; +} + +JSSourceValuePtr JSSourceValue::next() const +{ + return m_next; +} + +void JSSourceValue::setNext(const JSSourceValuePtr &next) +{ + QBS_ASSERT(next.data() != this, return); + m_next = next; +} + ItemValue::ItemValue(Item *item) : Value(ItemValueType) diff --git a/src/lib/corelib/language/value.h b/src/lib/corelib/language/value.h index 89328322f..fb130276a 100644 --- a/src/lib/corelib/language/value.h +++ b/src/lib/corelib/language/value.h @@ -128,6 +128,12 @@ public: void setAlternatives(const QList<Alternative> &alternatives) { m_alternatives = alternatives; } void addAlternative(const Alternative &alternative) { m_alternatives.append(alternative); } + Item *definingItem() const; + void setDefiningItem(Item *item); + + JSSourceValuePtr next() const; + void setNext(const JSSourceValuePtr &next); + private: QStringRef m_sourceCode; int m_line; @@ -136,6 +142,8 @@ private: Flags m_flags; JSSourceValuePtr m_baseValue; QList<Alternative> m_alternatives; + Item *m_definingItem; + JSSourceValuePtr m_next; }; class Item; diff --git a/src/lib/corelib/tools/propertyfinder.cpp b/src/lib/corelib/tools/propertyfinder.cpp index dccb205c6..ddb4f0bfe 100644 --- a/src/lib/corelib/tools/propertyfinder.cpp +++ b/src/lib/corelib/tools/propertyfinder.cpp @@ -31,22 +31,16 @@ #include "qbsassert.h" -#include <QQueue> #include <QStringList> namespace qbs { namespace Internal { QVariantList PropertyFinder::propertyValues(const QVariantMap &properties, - const QString &moduleName, const QString &key, MergeType mergeType) + const QString &moduleName, const QString &key) { - m_moduleName = moduleName; - m_key = key; - m_values.clear(); - findModuleValues(properties); - if (mergeType == DoMergeLists) - mergeLists(&m_values); - return m_values; + QVariant v = propertyValue(properties, moduleName, key); + return v.toList(); } QVariant PropertyFinder::propertyValue(const QVariantMap &properties, const QString &moduleName, @@ -55,61 +49,19 @@ QVariant PropertyFinder::propertyValue(const QVariantMap &properties, const QStr m_moduleName = moduleName; m_key = key; m_values.clear(); - findScalarModuleValue(properties); + findModuleValues(properties); return m_values.isEmpty() ? QVariant() : m_values.first(); } void PropertyFinder::findModuleValues(const QVariantMap &properties) { - QVariantMap moduleProperties = properties.value(QLatin1String("modules")).toMap(); - - // Direct hits come first. - const QVariantMap::Iterator modIt = moduleProperties.find(m_moduleName); - if (modIt != moduleProperties.end()) { + const QVariantMap moduleProperties = properties.value(QLatin1String("modules")).toMap(); + const QVariantMap::const_iterator modIt = moduleProperties.find(m_moduleName); + if (modIt != moduleProperties.constEnd()) { const QVariantMap moduleMap = modIt->toMap(); const QVariant property = moduleMap.value(m_key); addToList(property); - moduleProperties.erase(modIt); - } - - // These are the non-matching modules. - for (QVariantMap::ConstIterator it = moduleProperties.constBegin(); - it != moduleProperties.constEnd(); ++it) { - findModuleValues(it->toMap()); - } -} - -void PropertyFinder::findScalarModuleValue(const QVariantMap &properties) -{ - QQueue<QVariantMap> q; - q.enqueue(properties.value(QLatin1String("modules")).toMap()); - - while (!q.isEmpty()) { - QVariantMap moduleProperties = q.takeFirst(); - const QVariantMap::Iterator modIt = moduleProperties.find(m_moduleName); - if (modIt != moduleProperties.end()) { - const QVariantMap moduleMap = modIt->toMap(); - const QVariant property = moduleMap.value(m_key); - const QStringList ownPropertiesSet - = moduleProperties.value(QLatin1Char('@') + m_moduleName).toStringList(); - if (std::binary_search(ownPropertiesSet.constBegin(), ownPropertiesSet.constEnd(), - m_key)) { - // this is the one! - m_values.clear(); - addToList(property); - return; - } - addToList(property); - moduleProperties.erase(modIt); - } - - // These are the non-matching modules. - for (QVariantMap::ConstIterator it = moduleProperties.constBegin(); - it != moduleProperties.constEnd(); ++it) { - if (!it.key().startsWith(QLatin1Char('@'))) - q.enqueue(it->toMap().value(QLatin1String("modules")).toMap()); - } } } @@ -119,21 +71,5 @@ void PropertyFinder::addToList(const QVariant &value) m_values << value; } -void PropertyFinder::mergeLists(QVariantList *values) -{ - QVariantList::iterator it = values->begin(); - while (it != values->end()) { - if (it->canConvert<QVariantList>()) { - QVariantList sublist = it->toList(); - mergeLists(&sublist); - it = values->erase(it); - for (int k = sublist.count(); --k >= 0;) - it = values->insert(it, sublist.at(k)); - } else { - ++it; - } - } -} - } // namespace Internal } // namespace qbs diff --git a/src/lib/corelib/tools/propertyfinder.h b/src/lib/corelib/tools/propertyfinder.h index b78b8005c..41878702b 100644 --- a/src/lib/corelib/tools/propertyfinder.h +++ b/src/lib/corelib/tools/propertyfinder.h @@ -39,9 +39,8 @@ namespace Internal { class PropertyFinder { public: - enum MergeType { DoMergeLists, DoNotMergeLists }; QVariantList propertyValues(const QVariantMap &properties, const QString &moduleName, - const QString &key, MergeType mergeType = DoMergeLists); + const QString &key); // Note that this can still be a list if the property type itself is one. QVariant propertyValue(const QVariantMap &properties, const QString &moduleName, @@ -49,9 +48,7 @@ public: private: void findModuleValues(const QVariantMap &properties); - void findScalarModuleValue(const QVariantMap &properties); void addToList(const QVariant &value); - static void mergeLists(QVariantList *values); QString m_moduleName; QString m_key; diff --git a/tests/auto/blackbox/tst_blackbox.cpp b/tests/auto/blackbox/tst_blackbox.cpp index 86e02ba54..64762bb97 100644 --- a/tests/auto/blackbox/tst_blackbox.cpp +++ b/tests/auto/blackbox/tst_blackbox.cpp @@ -1669,7 +1669,6 @@ void TestBlackbox::nestedProperties() { QDir::setCurrent(testDataDir + "/nested-properties"); QCOMPARE(runQbs(), 0); - QEXPECT_FAIL(0, "QBS-736", Abort); QVERIFY2(m_qbsStdout.contains("value in higherlevel"), m_qbsStdout.constData()); } |