summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJoerg Bornemann <joerg.bornemann@theqtcompany.com>2015-05-19 18:37:31 +0200
committerJoerg Bornemann <joerg.bornemann@theqtcompany.com>2015-05-21 10:37:12 +0000
commit16404c356b0e160243de315c48975a82cc343893 (patch)
treefa2c77df7b989fc789b7bc4bca854bc652777939
parent1b8c356e52a16ed1b4066e4a345e7da92cfdaaaa (diff)
downloadqbs-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.qbs2
-rw-r--r--src/lib/corelib/language/evaluatorscriptclass.cpp4
-rw-r--r--src/lib/corelib/language/item.h1
-rw-r--r--src/lib/corelib/language/language.pri2
-rw-r--r--src/lib/corelib/language/moduleloader.cpp51
-rw-r--r--src/lib/corelib/language/moduleloader.h4
-rw-r--r--src/lib/corelib/language/modulemerger.cpp195
-rw-r--r--src/lib/corelib/language/modulemerger.h69
-rw-r--r--src/lib/corelib/language/projectresolver.cpp195
-rw-r--r--src/lib/corelib/language/projectresolver.h14
-rw-r--r--src/lib/corelib/language/propertydeclaration.cpp6
-rw-r--r--src/lib/corelib/language/propertydeclaration.h1
-rw-r--r--src/lib/corelib/language/tst_language.cpp23
-rw-r--r--src/lib/corelib/language/value.cpp25
-rw-r--r--src/lib/corelib/language/value.h8
-rw-r--r--src/lib/corelib/tools/propertyfinder.cpp78
-rw-r--r--src/lib/corelib/tools/propertyfinder.h5
-rw-r--r--tests/auto/blackbox/tst_blackbox.cpp1
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 &parameters);
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());
}