diff options
author | Joerg Bornemann <joerg.bornemann@theqtcompany.com> | 2015-01-29 14:10:14 +0100 |
---|---|---|
committer | Joerg Bornemann <joerg.bornemann@theqtcompany.com> | 2015-01-29 15:19:02 +0000 |
commit | db0dc474c2bea9a108f0ae4b7575cfab395556e2 (patch) | |
tree | 740314bb2424e27eb3944b7da2055e24582b44c3 | |
parent | 8debad775fc3858a42f5431fe80013ccc256f89b (diff) |
fix look-up of scalar properties
The property map of a product used to contain all modules of the
dependency tree as direct children. The same was true for the
product's Item. This setup was done in
ModuleLoader::createAdditionalModuleInstancesInProduct.
The whole approach led to problems when retrieving module values
from modules deeper in the dependency hierarchy. Basic idea of this
fix is to make ProjectResolver::resolveModule recursively walk the
module dependency tree instead of trying to gather information on the
root level.
Change-Id: Icf1325272f90d7924437f41ca6f403d7264ce84c
Task-number: QBS-726
Task-number: QBS-706
Reviewed-by: Christian Kandeler <christian.kandeler@theqtcompany.com>
-rw-r--r-- | src/lib/corelib/buildgraph/rulesapplicator.cpp | 49 | ||||
-rw-r--r-- | src/lib/corelib/language/language.cpp | 25 | ||||
-rw-r--r-- | src/lib/corelib/language/moduleloader.cpp | 29 | ||||
-rw-r--r-- | src/lib/corelib/language/moduleloader.h | 1 | ||||
-rw-r--r-- | src/lib/corelib/language/projectresolver.cpp | 36 | ||||
-rw-r--r-- | src/lib/corelib/language/projectresolver.h | 1 | ||||
-rw-r--r-- | src/lib/corelib/language/tst_language.cpp | 4 | ||||
-rw-r--r-- | tests/auto/blackbox/tst_blackbox.cpp | 3 |
8 files changed, 98 insertions, 50 deletions
diff --git a/src/lib/corelib/buildgraph/rulesapplicator.cpp b/src/lib/corelib/buildgraph/rulesapplicator.cpp index 23dbeff2c..94e4d481e 100644 --- a/src/lib/corelib/buildgraph/rulesapplicator.cpp +++ b/src/lib/corelib/buildgraph/rulesapplicator.cpp @@ -49,6 +49,7 @@ #include <tools/qbsassert.h> #include <QDir> +#include <QQueue> #include <QScriptValueIterator> namespace qbs { @@ -428,21 +429,49 @@ public: return; outputArtifact->properties = outputArtifact->properties->clone(); - QVariantMap artifactModulesCfg = outputArtifact->properties->value() - .value(QLatin1String("modules")).toMap(); + QVariantMap artifactCfg = outputArtifact->properties->value(); foreach (const NameValuePair &nvp, m_propertyValues) { - const QStringList &nameParts = nvp.first; - const QVariant &value = nvp.second; - if (!artifactModulesCfg.contains(nameParts.first())) { + const QStringList valuePath = findValuePath(artifactCfg, nvp.first); + if (valuePath.isEmpty()) { throw ErrorInfo(Tr::tr("Cannot set module property %1 on artifact %2.") - .arg(nameParts.join(QLatin1String(".")), + .arg(nvp.first.join(QLatin1String(".")), outputArtifact->filePath())); } - setConfigProperty(artifactModulesCfg, nameParts, value); + setConfigProperty(artifactCfg, valuePath, nvp.second); } - QVariantMap outputArtifactConfig = outputArtifact->properties->value(); - outputArtifactConfig.insert(QLatin1String("modules"), artifactModulesCfg); - outputArtifact->properties->setValue(outputArtifactConfig); + outputArtifact->properties->setValue(artifactCfg); + } + + QStringList findValuePath(const QVariantMap &cfg, const QStringList &nameParts) + { + QStringList tmp = nameParts; + const QString propertyName = tmp.takeLast(); + const QString moduleName = tmp.join(QLatin1Char('.')); + const QStringList modulePath = findModulePath(cfg, moduleName); + if (modulePath.isEmpty()) + return modulePath; + return QStringList(modulePath) << propertyName; + } + + QStringList findModulePath(const QVariantMap &cfg, const QString &moduleName) + { + typedef QPair<QVariantMap, QStringList> MapAndPath; + QQueue<MapAndPath> q; + q.enqueue(MapAndPath(cfg.value(QLatin1String("modules")).toMap(), + QStringList(QLatin1String("modules")))); + do { + const MapAndPath current = q.takeFirst(); + const QVariantMap &mod = current.first; + for (QVariantMap::const_iterator it = mod.constBegin(); it != mod.constEnd(); ++it) { + const QVariantMap m = it.value().toMap(); + const QStringList currentPath = QStringList(current.second) << it.key(); + if (it.key() == moduleName) + return currentPath; + q.enqueue(MapAndPath(m.value(QLatin1String("modules")).toMap(), + QStringList(currentPath) << QLatin1String("modules"))); + } + } while (!q.isEmpty()); + return QStringList(); } }; diff --git a/src/lib/corelib/language/language.cpp b/src/lib/corelib/language/language.cpp index 5a4bd4a5a..e90b31907 100644 --- a/src/lib/corelib/language/language.cpp +++ b/src/lib/corelib/language/language.cpp @@ -548,6 +548,29 @@ enum EnvType BuildEnv, RunEnv }; +static bool findModuleMapRecursively_impl(const QVariantMap &cfg, const QString &moduleName, + QVariantMap *result) +{ + for (QVariantMap::const_iterator it = cfg.constBegin(); it != cfg.constEnd(); ++it) { + if (it.key() == moduleName) { + *result = it.value().toMap(); + return true; + } + if (findModuleMapRecursively_impl(it.value().toMap().value("modules").toMap(), moduleName, + result)) { + return true; + } + } + return false; +} + +static QVariantMap findModuleMapRecursively(const QVariantMap &cfg, const QString &moduleName) +{ + QVariantMap result; + findModuleMapRecursively_impl(cfg, moduleName, &result); + return result; +} + static QProcessEnvironment getProcessEnvironment(ScriptEngine *engine, EnvType envType, const QList<ResolvedModuleConstPtr> &modules, const PropertyMapConstPtr &productConfiguration, @@ -627,7 +650,7 @@ static QProcessEnvironment getProcessEnvironment(ScriptEngine *engine, EnvType e } // expose the module's properties - QVariantMap moduleCfg = productModules.value(module->name).toMap(); + QVariantMap moduleCfg = findModuleMapRecursively(productModules, module->name); for (QVariantMap::const_iterator it = moduleCfg.constBegin(); it != moduleCfg.constEnd(); ++it) scope.setProperty(it.key(), engine->toScriptValue(it.value())); diff --git a/src/lib/corelib/language/moduleloader.cpp b/src/lib/corelib/language/moduleloader.cpp index b17900b26..bb321b441 100644 --- a/src/lib/corelib/language/moduleloader.cpp +++ b/src/lib/corelib/language/moduleloader.cpp @@ -434,7 +434,6 @@ void ModuleLoader::handleProduct(ProjectContext *projectContext, Item *item) setScopeForDescendants(item, productContext.scope); resolveDependencies(&dependsContext, item); checkItemCondition(item); - createAdditionalModuleInstancesInProduct(&productContext); foreach (Item *child, item->children()) { if (child->typeName() == QLatin1String("Group")) @@ -518,28 +517,6 @@ void ModuleLoader::handleSubProject(ModuleLoader::ProjectContext *projectContext QSet<QString>(referencedFilePaths) << subProjectFilePath); } -void ModuleLoader::createAdditionalModuleInstancesInProduct(ProductContext *productContext) -{ - Item::Modules modulesToCheck; - QSet<QStringList> modulesInProduct; - foreach (const Item::Module &module, productContext->item->modules()) { - modulesInProduct += module.name; - modulesToCheck += module.item->prototype()->modules(); - } - while (!modulesToCheck.isEmpty()) { - Item::Module module = modulesToCheck.takeFirst(); - if (modulesInProduct.contains(module.name)) - continue; - modulesInProduct += module.name; - modulesToCheck += module.item->prototype()->modules(); - Item *instance = Item::create(m_pool); - instantiateModule(productContext, productContext->item, instance, module.item->prototype(), - module.name); - module.item = instance; - productContext->item->modules().append(module); - } -} - void ModuleLoader::handleGroup(ProductContext *productContext, Item *item) { checkCancelation(); @@ -1036,11 +1013,7 @@ void ModuleLoader::instantiateModule(ProductContext *productContext, Item *insta } QBS_CHECK(obj == depinst); } - depinst->setPrototype(m.item); - depinst->setFile(m.item->file()); - depinst->setLocation(m.item->location()); - depinst->setTypeName(m.item->typeName()); - depinst->setScope(moduleInstance); + instantiateModule(productContext, moduleInstance, depinst, m.item, m.name); m.item = depinst; moduleInstance->modules() += m; } diff --git a/src/lib/corelib/language/moduleloader.h b/src/lib/corelib/language/moduleloader.h index c072fd59c..d36f4d568 100644 --- a/src/lib/corelib/language/moduleloader.h +++ b/src/lib/corelib/language/moduleloader.h @@ -165,7 +165,6 @@ private: void initProductProperties(const ProjectContext *project, Item *item); void handleSubProject(ProjectContext *projectContext, Item *item, const QSet<QString> &referencedFilePaths); - void createAdditionalModuleInstancesInProduct(ProductContext *productContext); void handleGroup(ProductContext *productContext, Item *group); void deferExportItem(ProductContext *productContext, Item *item); void mergeExportItems(ProductContext *productContext); diff --git a/src/lib/corelib/language/projectresolver.cpp b/src/lib/corelib/language/projectresolver.cpp index 14eacd2ea..0bf8b5ed3 100644 --- a/src/lib/corelib/language/projectresolver.cpp +++ b/src/lib/corelib/language/projectresolver.cpp @@ -50,6 +50,7 @@ #include <QFileInfo> #include <QDir> +#include <QQueue> #include <QSet> #include <algorithm> @@ -363,9 +364,7 @@ void ProjectResolver::resolveProduct(Item *item, ProjectContext *projectContext) foreach (Item *child, subItems) callItemFunction(mapping, child, projectContext); - foreach (const Item::Module &module, item->modules()) - resolveModule(module.name, module.item, projectContext); - + resolveModules(item, projectContext); product->fileTags += productContext.additionalFileTags; foreach (const ResolvedTransformerPtr &transformer, product->transformers) { @@ -384,12 +383,33 @@ void ProjectResolver::resolveProduct(Item *item, ProjectContext *projectContext) m_progressObserver->incrementProgressValue(); } +void ProjectResolver::resolveModules(const Item *item, ProjectContext *projectContext) +{ + // Breadth first search needed here, because the product might set properties on the cpp module, + // whose children must be evaluated in that context then. + QQueue<Item::Module> modules; + foreach (const Item::Module &m, item->modules()) + modules.enqueue(m); + QSet<QStringList> seen; + while (!modules.isEmpty()) { + const Item::Module m = modules.takeFirst(); + if (seen.contains(m.name)) + continue; + seen.insert(m.name); + resolveModule(m.name, m.item, projectContext); + foreach (const Item::Module &childModule, m.item->modules()) + modules.enqueue(childModule); + } +} + void ProjectResolver::resolveModule(const QStringList &moduleName, Item *item, ProjectContext *projectContext) { checkCancelation(); if (!m_evaluator->boolValue(item, QLatin1String("present"))) return; + + ModuleContext * const oldModuleContext = m_moduleContext; ModuleContext moduleContext; moduleContext.module = ResolvedModule::create(); m_moduleContext = &moduleContext; @@ -420,7 +440,7 @@ void ProjectResolver::resolveModule(const QStringList &moduleName, Item *item, foreach (Item *child, item->children()) callItemFunction(mapping, child, projectContext); - m_moduleContext = 0; + m_moduleContext = oldModuleContext; } SourceArtifactPtr ProjectResolver::createSourceArtifact(const ResolvedProductConstPtr &rproduct, @@ -1063,11 +1083,17 @@ void ProjectResolver::evaluateModuleValues(Item *item, QVariantMap *modulesMap, bool lookupPrototype) const { checkCancelation(); + QSet<QStringList> seenModuleNames; + for (Item::Modules::const_iterator it = item->modules().constBegin(); it != item->modules().constEnd(); ++it) { - QVariantMap depmods; const Item::Module &module = *it; + if (seenModuleNames.contains(module.name)) + continue; + seenModuleNames << module.name; + + QVariantMap depmods; evaluateModuleValues(module.item, &depmods, lookupPrototype); QVariantMap dep = evaluateProperties(module.item, lookupPrototype); dep.insert(QLatin1String("modules"), depmods); diff --git a/src/lib/corelib/language/projectresolver.h b/src/lib/corelib/language/projectresolver.h index d90c0507c..85edf1d07 100644 --- a/src/lib/corelib/language/projectresolver.h +++ b/src/lib/corelib/language/projectresolver.h @@ -104,6 +104,7 @@ private: void resolveProject(Item *item, ProjectContext *projectContext); void resolveSubProject(Item *item, ProjectContext *projectContext); void resolveProduct(Item *item, ProjectContext *projectContext); + void resolveModules(const Item *item, ProjectContext *projectContext); void resolveModule(const QStringList &moduleName, Item *item, ProjectContext *projectContext); void resolveGroup(Item *item, ProjectContext *projectContext); void resolveRule(Item *item, ProjectContext *projectContext); diff --git a/src/lib/corelib/language/tst_language.cpp b/src/lib/corelib/language/tst_language.cpp index d50592e03..9e314e543 100644 --- a/src/lib/corelib/language/tst_language.cpp +++ b/src/lib/corelib/language/tst_language.cpp @@ -873,10 +873,10 @@ void TestLanguage::moduleProperties_data() QTest::newRow("init") << QString() << QStringList(); QTest::newRow("merge_lists") << "defines" - << (QStringList() << "THE_PRODUCT" << "QT_CORE" << "QT_GUI" << "QT_NETWORK"); + << (QStringList() << "THE_PRODUCT" << "QT_GUI" << "QT_CORE" << "QT_NETWORK"); QTest::newRow("merge_lists_and_values") << "defines" - << (QStringList() << "THE_PRODUCT" << "QT_CORE" << "QT_GUI" << "QT_NETWORK"); + << (QStringList() << "THE_PRODUCT" << "QT_GUI" << "QT_CORE" << "QT_NETWORK"); QTest::newRow("merge_lists_with_duplicates") << "cxxFlags" << (QStringList() << "-foo" << "BAR" << "-foo" << "BAZ"); diff --git a/tests/auto/blackbox/tst_blackbox.cpp b/tests/auto/blackbox/tst_blackbox.cpp index 4bb6c5858..266436a20 100644 --- a/tests/auto/blackbox/tst_blackbox.cpp +++ b/tests/auto/blackbox/tst_blackbox.cpp @@ -673,9 +673,7 @@ void TestBlackbox::exportSimple() void TestBlackbox::exportWithRecursiveDepends() { QDir::setCurrent(testDataDir + "/exportWithRecursiveDepends"); - QEXPECT_FAIL("", "QBS-706", Abort); QbsRunParameters params; - params.expectFailure = true; // Remove when test no longer fails. QCOMPARE(runQbs(params), 0); } @@ -1908,7 +1906,6 @@ void TestBlackbox::nestedProperties() { QDir::setCurrent(testDataDir + "/nested-properties"); QCOMPARE(runQbs(), 0); - QEXPECT_FAIL(0, "QBS-726", Abort); QVERIFY2(m_qbsStdout.contains("value in higherlevel"), m_qbsStdout.constData()); } |