diff options
author | Joerg Bornemann <joerg.bornemann@theqtcompany.com> | 2015-07-16 12:44:27 +0200 |
---|---|---|
committer | Joerg Bornemann <joerg.bornemann@theqtcompany.com> | 2015-07-20 09:48:50 +0000 |
commit | 9d025655d5524433ac8ffbbac0f5ab7aa196d797 (patch) | |
tree | 9cd323d97b755afb8ec68ca87c6082fdbfa18465 | |
parent | db0c52d932a62ed4744334e76efc1681aca7a25e (diff) |
Fix property evaluation of export items.
Make product dependencies first class modules.
Task-number: QBS-736
Task-number: QBS-814
Task-number: QBS-822
Task-number: QBS-830
Change-Id: Ia72a8c4e29eb78a114795299a753256a41208ace
Reviewed-by: Joerg Bornemann <joerg.bornemann@theqtcompany.com>
Reviewed-by: Christian Kandeler <christian.kandeler@theqtcompany.com>
23 files changed, 402 insertions, 390 deletions
diff --git a/src/lib/corelib/language/builtindeclarations.cpp b/src/lib/corelib/language/builtindeclarations.cpp index c4f4c72fb..8eb8fc3b8 100644 --- a/src/lib/corelib/language/builtindeclarations.cpp +++ b/src/lib/corelib/language/builtindeclarations.cpp @@ -188,12 +188,7 @@ void BuiltinDeclarations::addDependsItem() void BuiltinDeclarations::addExportItem() { - ItemDeclaration item(QLatin1String("Export")); - item.setAllowedChildTypes(ItemDeclaration::TypeNames() - << QLatin1String("Depends") - << QLatin1String("FileTagger") - << QLatin1String("Rule")); - insert(item); + addModuleLikeItem(QStringLiteral("Export")); } void BuiltinDeclarations::addFileTaggerItem() @@ -231,7 +226,12 @@ void BuiltinDeclarations::addGroupItem() void BuiltinDeclarations::addModuleItem() { - ItemDeclaration item(QLatin1String("Module")); + addModuleLikeItem(QStringLiteral("Module")); +} + +void BuiltinDeclarations::addModuleLikeItem(const QString &typeName) +{ + ItemDeclaration item(typeName); item.setAllowedChildTypes(ItemDeclaration::TypeNames() << QLatin1String("Group") << QLatin1String("Depends") diff --git a/src/lib/corelib/language/builtindeclarations.h b/src/lib/corelib/language/builtindeclarations.h index b9c8f9bda..99b71c38a 100644 --- a/src/lib/corelib/language/builtindeclarations.h +++ b/src/lib/corelib/language/builtindeclarations.h @@ -62,6 +62,7 @@ private: void addFileTaggerItem(); void addGroupItem(); void addModuleItem(); + void addModuleLikeItem(const QString &typeName); void addProbeItem(); void addProductItem(); void addProjectItem(); diff --git a/src/lib/corelib/language/evaluator.h b/src/lib/corelib/language/evaluator.h index 91a7fc339..9781f5956 100644 --- a/src/lib/corelib/language/evaluator.h +++ b/src/lib/corelib/language/evaluator.h @@ -68,11 +68,11 @@ public: void setCachingEnabled(bool enabled); + void handleEvaluationError(const Item *item, const QString &name, + const QScriptValue &scriptValue); private: void onItemPropertyChanged(Item *item); void onItemDestroyed(Item *item); - void handleEvaluationError(const Item *item, const QString &name, - const QScriptValue &scriptValue); bool evaluateProperty(QScriptValue *result, const Item *item, const QString &name, bool *propertyWasSet); diff --git a/src/lib/corelib/language/evaluatorscriptclass.cpp b/src/lib/corelib/language/evaluatorscriptclass.cpp index 3bb870de0..f627038ef 100644 --- a/src/lib/corelib/language/evaluatorscriptclass.cpp +++ b/src/lib/corelib/language/evaluatorscriptclass.cpp @@ -38,6 +38,7 @@ #include "scriptengine.h" #include "propertydeclaration.h" #include <tools/architectures.h> +#include <tools/fileinfo.h> #include <tools/hostosinfo.h> #include <tools/qbsassert.h> #include <tools/scripttools.h> @@ -52,29 +53,6 @@ namespace qbs { namespace Internal { -template <class T> -class ScopedPopper -{ - QStack<T> *m_stack; - bool m_mustPop; -public: - ScopedPopper(QStack<T> *s) - : m_stack(s), m_mustPop(false) - { - } - - ~ScopedPopper() - { - if (m_mustPop) - m_stack->pop(); - } - - void mustPop() - { - m_mustPop = true; - } -}; - class SVConverter : ValueHandler { EvaluatorScriptClass * const scriptClass; @@ -153,15 +131,6 @@ private: void handle(JSSourceValue *value) { - ScopedPopper<JSSourceValue *> popper(sourceValueStack); - if (value->sourceUsesProduct()) { - foreach (JSSourceValue *a, *sourceValueStack) - a->setSourceUsesProductFlag(); - } else { - sourceValueStack->push(value); - popper.mustPop(); - } - const Item *conditionScopeItem = 0; QScriptValue conditionScope; QScriptValue conditionFileScope; @@ -256,6 +225,8 @@ private: // Own properties of module instances must not have the instance itself in the scope. pushScope(*object); } + if (value->exportScope()) + pushScope(data->evaluator->scriptValue(value->exportScope())); pushScope(extraScope); *result = engine->evaluate(value->sourceCodeForEvaluation(), value->file()->filePath(), value->line()); @@ -393,6 +364,7 @@ void EvaluatorScriptClass::collectValuesFromNextChain(const EvaluationData *data for (ValuePtr next = value; next; next = next->next()) { QScriptValue v = data->evaluator->property(next->definingItem(), propertyName); + data->evaluator->handleEvaluationError(next->definingItem(), propertyName, v); if (v.isUndefined()) continue; lst << v; @@ -417,7 +389,14 @@ void EvaluatorScriptClass::collectValuesFromNextChain(const EvaluationData *data } } -inline void convertToPropertyType(const PropertyDeclaration::Type t, QScriptValue &v) +static QString overriddenSourceDirectory(const Item *item) +{ + const VariantValuePtr v = item->variantProperty(QLatin1String("_qbs_sourceDir")); + return v ? v->value().toString() : QString(); +} + +inline void convertToPropertyType(const Item *item, const PropertyDeclaration::Type t, + QScriptValue &v) { if (v.isUndefined()) return; @@ -435,11 +414,36 @@ inline void convertToPropertyType(const PropertyDeclaration::Type t, QScriptValu v = v.toNumber(); break; case PropertyDeclaration::Path: + { + if (!v.isString()) + v = v.toString(); + const QString srcDir = overriddenSourceDirectory(item); + if (!srcDir.isEmpty()) + v = v.engine()->toScriptValue(FileInfo::resolvePath(srcDir, v.toString())); + break; + } case PropertyDeclaration::String: if (!v.isString()) v = v.toString(); break; case PropertyDeclaration::PathList: + { + if (!v.isArray()) { + QScriptValue x = v.engine()->newArray(1); + x.setProperty(0, v.isString() ? v : v.toString()); + v = x; + } + const QString srcDir = overriddenSourceDirectory(item); + if (srcDir.isEmpty()) + break; + const quint32 c = v.property(QLatin1String("length")).toUInt32(); + for (quint32 i = 0; i < c; ++i) { + QScriptValue elem = v.property(i); + elem = v.engine()->toScriptValue(FileInfo::resolvePath(srcDir, elem.toString())); + v.setProperty(i, elem); + } + break; + } case PropertyDeclaration::StringList: if (!v.isArray()) { QScriptValue x = v.engine()->newArray(1); @@ -492,7 +496,7 @@ QScriptValue EvaluatorScriptClass::property(const QScriptValue &object, const QS converter.start(); const PropertyDeclaration decl = data->item->propertyDeclaration(name.toString()); - convertToPropertyType(decl.type(), result); + convertToPropertyType(data->item, decl.type(), result); } if (debugProperties) diff --git a/src/lib/corelib/language/item.h b/src/lib/corelib/language/item.h index 371cf106c..cdf4905cd 100644 --- a/src/lib/corelib/language/item.h +++ b/src/lib/corelib/language/item.h @@ -67,11 +67,12 @@ public: struct Module { Module() - : item(0), required(true) + : item(0), isProduct(false), required(true) {} QualifiedId name; Item *item; + bool isProduct; bool required; }; typedef QList<Module> Modules; diff --git a/src/lib/corelib/language/itemreader.cpp b/src/lib/corelib/language/itemreader.cpp index 44de6560c..f23b45cfd 100644 --- a/src/lib/corelib/language/itemreader.cpp +++ b/src/lib/corelib/language/itemreader.cpp @@ -121,6 +121,11 @@ void ItemReader::popExtraSearchPaths() m_extraSearchPaths.pop(); } +QStack<QStringList> ItemReader::extraSearchPathsStack() const +{ + return m_extraSearchPaths; +} + QStringList ItemReader::searchPaths() const { QStringList paths; diff --git a/src/lib/corelib/language/itemreader.h b/src/lib/corelib/language/itemreader.h index 8fffd3c03..de147ae9b 100644 --- a/src/lib/corelib/language/itemreader.h +++ b/src/lib/corelib/language/itemreader.h @@ -78,6 +78,9 @@ public: void setSearchPaths(const QStringList &searchPaths); void pushExtraSearchPaths(const QStringList &extraSearchPaths); void popExtraSearchPaths(); + QStack<QStringList> extraSearchPathsStack() const; + void setExtraSearchPathsStack(const QStack<QStringList> &s) { m_extraSearchPaths = s; } + void clearExtraSearchPathsStack() { m_extraSearchPaths.clear(); } QStringList searchPaths() const; Item *readFile(const QString &filePath); diff --git a/src/lib/corelib/language/itemreaderastvisitor.cpp b/src/lib/corelib/language/itemreaderastvisitor.cpp index 6d80ba6ba..ff8f48e6f 100644 --- a/src/lib/corelib/language/itemreaderastvisitor.cpp +++ b/src/lib/corelib/language/itemreaderastvisitor.cpp @@ -450,18 +450,15 @@ bool ItemReaderASTVisitor::visitStatement(AST::Statement *statement) m_sourceValue->setLocation(statement->firstSourceLocation().startLine, statement->firstSourceLocation().startColumn); - bool usesBase, usesOuter, usesProduct; + bool usesBase, usesOuter; IdentifierSearch idsearch; idsearch.add(QLatin1String("base"), &usesBase); idsearch.add(QLatin1String("outer"), &usesOuter); - idsearch.add(QLatin1String("product"), &usesProduct); idsearch.start(statement); if (usesBase) m_sourceValue->m_flags |= JSSourceValue::SourceUsesBase; if (usesOuter) m_sourceValue->m_flags |= JSSourceValue::SourceUsesOuter; - if (usesProduct) - m_sourceValue->m_flags |= JSSourceValue::SourceUsesProduct; return false; } diff --git a/src/lib/corelib/language/moduleloader.cpp b/src/lib/corelib/language/moduleloader.cpp index 5a670b09a..723916cd1 100644 --- a/src/lib/corelib/language/moduleloader.cpp +++ b/src/lib/corelib/language/moduleloader.cpp @@ -114,8 +114,10 @@ ModuleLoaderResult ModuleLoader::load(const SetupProjectParameters ¶meters) m_logger.qbsTrace() << "[MODLDR] load" << parameters.projectFilePath(); m_parameters = parameters; m_validItemPropertyNamesPerItem.clear(); + m_productModuleCache.clear(); m_modulePrototypeItemCache.clear(); m_disabledItems.clear(); + m_reader->clearExtraSearchPathsStack(); ModuleLoaderResult result; m_pool = result.itemPool.data(); @@ -204,8 +206,11 @@ private: void handleItem(Item *item) { - if (m_disabledItems.contains(item) || item->typeName() == QLatin1String("SubProject")) + if (m_disabledItems.contains(item) + || item->typeName() == QLatin1String("Export") + || item->typeName() == QLatin1String("SubProject")) { return; + } Item *oldParentItem = m_parentItem; for (Item::PropertyMap::const_iterator it = item->properties().constBegin(); @@ -228,19 +233,32 @@ private: void ModuleLoader::handleTopLevelProject(ModuleLoaderResult *loadResult, Item *item, const QString &buildDirectory, const QSet<QString> &referencedFilePaths) { - handleProject(loadResult, item, buildDirectory, referencedFilePaths); + TopLevelProjectContext tlp; + handleProject(loadResult, &tlp, item, buildDirectory, referencedFilePaths); + + foreach (ProjectContext *projectContext, tlp.projects) { + m_reader->setExtraSearchPathsStack(projectContext->searchPathsStack); + for (auto it = projectContext->products.begin(); it != projectContext->products.end(); ++it) + handleProduct(it); + } + + m_reader->clearExtraSearchPathsStack(); checkItemTypes(item); PropertyDeclarationCheck check(m_validItemPropertyNamesPerItem, m_disabledItems, m_parameters, m_logger); check(item); } -void ModuleLoader::handleProject(ModuleLoaderResult *loadResult, Item *item, - const QString &buildDirectory, const QSet<QString> &referencedFilePaths) +void ModuleLoader::handleProject(ModuleLoaderResult *loadResult, + TopLevelProjectContext *topLevelProjectContext, Item *item, const QString &buildDirectory, + const QSet<QString> &referencedFilePaths) { if (!checkItemCondition(item)) return; - ProjectContext projectContext; + auto *p = new ProjectContext; + auto &projectContext = *p; + topLevelProjectContext->projects << &projectContext; + projectContext.topLevelProject = topLevelProjectContext; projectContext.result = loadResult; projectContext.buildDirectory = buildDirectory; ProductContext dummyProductContext; @@ -251,8 +269,8 @@ void ModuleLoader::handleProject(ModuleLoaderResult *loadResult, Item *item, const QString projectName = m_evaluator->stringValue(item, QLatin1String("name")); if (!projectName.isEmpty()) overrideItemProperties(item, projectName, m_parameters.overriddenValuesTree()); - m_reader->pushExtraSearchPaths(readExtraSearchPaths(item) << item->file()->dirPath()); + projectContext.searchPathsStack = m_reader->extraSearchPathsStack(); projectContext.item = item; ItemValuePtr itemValue = ItemValue::create(item); projectContext.scope = Item::create(m_pool); @@ -287,12 +305,13 @@ void ModuleLoader::handleProject(ModuleLoaderResult *loadResult, Item *item, foreach (Item *child, item->children()) { if (child->typeName() == QLatin1String("Product")) { - handleProduct(&projectContext, child); + prepareProduct(&projectContext, child); } else if (child->typeName() == QLatin1String("SubProject")) { handleSubProject(&projectContext, child, referencedFilePaths); } else if (child->typeName() == QLatin1String("Project")) { copyProperties(item, child); - handleProject(loadResult, child, buildDirectory, referencedFilePaths); + handleProject(loadResult, topLevelProjectContext, child, buildDirectory, + referencedFilePaths); } } @@ -338,10 +357,10 @@ void ModuleLoader::handleProject(ModuleLoaderResult *loadResult, Item *item, Item * const subItem = irp.first; Item::addChild(projectContext.item, subItem); if (subItem->typeName() == QLatin1String("Product")) { - handleProduct(&projectContext, subItem); + prepareProduct(&projectContext, subItem); } else if (subItem->typeName() == QLatin1String("Project")) { copyProperties(item, subItem); - handleProject(loadResult, subItem, buildDirectory, + handleProject(loadResult, topLevelProjectContext, subItem, buildDirectory, QSet<QString>(referencedFilePaths) << irp.second); } else { throw ErrorInfo(Tr::tr("The top-level item of a file in a \"references\" list must be " @@ -349,7 +368,6 @@ void ModuleLoader::handleProject(ModuleLoaderResult *loadResult, Item *item, subItem->location()); } } - m_reader->popExtraSearchPaths(); } @@ -414,14 +432,16 @@ QList<Item *> ModuleLoader::multiplexProductItem(ProductContext *dummyContext, I return additionalProductItems; } -void ModuleLoader::handleProduct(ProjectContext *projectContext, Item *item) +void ModuleLoader::prepareProduct(ProjectContext *projectContext, Item *item) { checkCancelation(); if (m_logger.traceEnabled()) - m_logger.qbsTrace() << "[MODLDR] handleProduct " << item->file()->filePath(); + m_logger.qbsTrace() << "[MODLDR] prepareProduct " << item->file()->filePath(); initProductProperties(projectContext, item); ProductContext productContext; + productContext.name = m_evaluator->stringValue(item, QLatin1String("name")); + QBS_CHECK(!productContext.name.isEmpty()); bool profilePropertySet; productContext.profileName = m_evaluator->stringValue(item, QLatin1String("profile"), QString(), &profilePropertySet); @@ -440,45 +460,61 @@ void ModuleLoader::handleProduct(ProjectContext *projectContext, Item *item) } else { productContext.moduleProperties = it.value().toMap(); } + productContext.item = item; productContext.project = projectContext; + QVector<Item *> exportItems; + foreach (Item *child, item->children()) { + if (child->typeName() == QLatin1String("Export")) + exportItems << child; + } + + Item *mergedExportItem = mergeExportItems(&productContext, exportItems); + ProductModuleInfo &pmi = projectContext->topLevelProject->productModules[productContext.name]; + pmi.exportItem = mergedExportItem; + + ItemValuePtr itemValue = ItemValue::create(item); + productContext.scope = Item::create(m_pool); + productContext.scope->setProperty(QLatin1String("product"), itemValue); + productContext.scope->setFile(item->file()); + productContext.scope->setScope(productContext.project->scope); + setScopeForDescendants(item, productContext.scope); + + projectContext->products << productContext; +} + +void ModuleLoader::handleProduct(ProductContext *productContext) +{ + checkCancelation(); + Item *item = productContext->item; + if (m_logger.traceEnabled()) + m_logger.qbsTrace() << "[MODLDR] handleProduct " << item->file()->filePath(); + QStringList extraSearchPaths = readExtraSearchPaths(item); Settings settings(m_parameters.settingsDirectory()); const QStringList prefsSearchPaths - = Preferences(&settings, productContext.profileName).searchPaths(); + = Preferences(&settings, productContext->profileName).searchPaths(); foreach (const QString &p, prefsSearchPaths) { if (!m_moduleSearchPaths.contains(p) && FileInfo(p).exists()) extraSearchPaths << p; } m_reader->pushExtraSearchPaths(extraSearchPaths); - productContext.item = item; - ItemValuePtr itemValue = ItemValue::create(item); - productContext.scope = Item::create(m_pool); - productContext.scope->setProperty(QLatin1String("product"), itemValue); - productContext.scope->setFile(item->file()); - productContext.scope->setScope(projectContext->scope); DependsContext dependsContext; - dependsContext.product = &productContext; - dependsContext.productDependencies = &productContext.info.usedProducts; - setScopeForDescendants(item, productContext.scope); + dependsContext.product = productContext; + dependsContext.productDependencies = &productContext->info.usedProducts; resolveDependencies(&dependsContext, item); - addTransitiveDependencies(&productContext, productContext.item); + addTransitiveDependencies(productContext, productContext->item); checkItemCondition(item); - QVector<Item *> exportItems; foreach (Item *child, item->children()) { if (child->typeName() == QLatin1String("Group")) - handleGroup(&productContext, child); - else if (child->typeName() == QLatin1String("Export")) - exportItems << child; + handleGroup(productContext, child); else if (child->typeName() == QLatin1String("Probe")) resolveProbe(item, child); } - Item *mergedExportItem = mergeExportItems(&productContext, exportItems); - addTransitiveDependencies(&productContext, mergedExportItem); - projectContext->result->productInfos.insert(item, productContext.info); + productContext->project->result->productInfos.insert(item, productContext->info); m_reader->popExtraSearchPaths(); } @@ -544,7 +580,8 @@ void ModuleLoader::handleSubProject(ModuleLoader::ProjectContext *projectContext Item::addChild(item, loadedItem); item->setScope(projectContext->scope); - handleProject(projectContext->result, loadedItem, projectContext->buildDirectory, + handleProject(projectContext->result, projectContext->topLevelProject, loadedItem, + projectContext->buildDirectory, QSet<QString>(referencedFilePaths) << subProjectFilePath); } @@ -604,11 +641,7 @@ Item *ModuleLoader::mergeExportItems(ModuleLoader::ProductContext *productContex } productContext->item->setChildren(children); Item::addChild(productContext->item, merged); - - DependsContext dependsContext; - dependsContext.product = productContext; - dependsContext.productDependencies = &productContext->info.usedProductsFromExportItem; - resolveDependencies(&dependsContext, merged); + m_reader->builtins()->setupItemForBuiltinType(merged, m_logger); return merged; } @@ -658,15 +691,6 @@ void ModuleLoader::resolveDependencies(DependsContext *dependsContext, Item *ite dependsContext->productDependencies->append(productDependencies); } -static bool containsEmptyString(const QStringList &lst) -{ - foreach (const QString &str, lst) { - if (str.isEmpty()) - return true; - } - return false; -} - void ModuleLoader::resolveDependsItem(DependsContext *dependsContext, Item *item, Item *dependsItem, ItemModuleList *moduleResults, ProductDependencyResults *productResults) @@ -731,22 +755,23 @@ void ModuleLoader::resolveDependsItem(DependsContext *dependsContext, Item *item foreach (const QualifiedId &moduleName, moduleNames) { const bool isRequired = m_evaluator->boolValue(dependsItem, QLatin1String("required")); - Item *moduleItem = 0; - if (!containsEmptyString(moduleName)) { - // If the list of module name parts contains empty elements then the name - // looks like ".foo" or "foo...bar". This cannot be a module name, but it could - // be a product name. - moduleItem = loadModule(dependsContext->product, item, dependsItem->location(), - dependsItem->id(), moduleName, false, isRequired); + Item *moduleItem = loadModule(dependsContext->product, item, dependsItem->location(), + dependsItem->id(), moduleName, false, isRequired, + &result.isProduct); + if (!moduleItem) { + // ### 1.5: change error message to the more generic "Dependency '%1' not found.". + throw ErrorInfo(Tr::tr("Product dependency '%1' not found.").arg(moduleName.toString()), + dependsItem->location()); } - if (moduleItem) { + if (m_logger.traceEnabled()) + m_logger.qbsTrace() << "module loaded: " << moduleName.toString(); + result.name = moduleName; + result.item = moduleItem; + result.required = isRequired; + moduleResults->append(result); + if (result.isProduct) { if (m_logger.traceEnabled()) - m_logger.qbsTrace() << "module loaded: " << moduleName.toString(); - result.name = moduleName; - result.item = moduleItem; - result.required = isRequired; - moduleResults->append(result); - } else { + m_logger.qbsTrace() << "product dependency loaded: " << moduleName.toString(); const QString profilesKey = QLatin1String("profiles"); const QStringList profiles = m_evaluator->stringListValue(dependsItem, profilesKey); if (profiles.isEmpty()) { @@ -791,9 +816,35 @@ Item *ModuleLoader::moduleInstanceItem(Item *item, const QualifiedId &moduleName return instance; } +Item *ModuleLoader::loadProductModule(ModuleLoader::ProductContext *productContext, + const QString &moduleName) +{ + if (m_logger.traceEnabled()) + m_logger.qbsTrace() << "[MODLDR] loadProductModule name: " << moduleName; + Item *module = m_productModuleCache.value(moduleName); + if (module) { + if (m_logger.traceEnabled()) + m_logger.qbsTrace() << "[MODLDR] loadProductModule cache hit."; + return module; + } + ProductModuleInfo &pmi = productContext->project->topLevelProject->productModules[moduleName]; + module = pmi.exportItem; + if (module) { + if (m_logger.traceEnabled()) + m_logger.qbsTrace() << "[MODLDR] loadProductModule cache miss."; + DependsContext dependsContext; + dependsContext.product = productContext; + dependsContext.productDependencies = &pmi.productDependencies; + resolveDependencies(&dependsContext, module); + m_productModuleCache.insert(moduleName, module); + } + return module; +} + Item *ModuleLoader::loadModule(ProductContext *productContext, Item *item, const CodeLocation &dependsItemLocation, - const QString &moduleId, const QualifiedId &moduleName, bool isBaseModule, bool isRequired) + const QString &moduleId, const QualifiedId &moduleName, bool isBaseModule, bool isRequired, + bool *isProductDependency) { if (m_logger.traceEnabled()) m_logger.qbsTrace() << "[MODLDR] loadModule name: " << moduleName << ", id: " << moduleId; @@ -806,17 +857,23 @@ Item *ModuleLoader::loadModule(ProductContext *productContext, Item *item, return moduleInstance; } - QStringList moduleSearchPaths; - foreach (const QString &searchPath, m_reader->searchPaths()) - addExtraModuleSearchPath(moduleSearchPaths, searchPath); - bool cacheHit; - Item *modulePrototype = searchAndLoadModuleFile(productContext, dependsItemLocation, - moduleName, moduleSearchPaths, isRequired, &cacheHit); + *isProductDependency = true; + Item *modulePrototype = loadProductModule(productContext, moduleName.toString()); + if (!modulePrototype) { + *isProductDependency = false; + QStringList moduleSearchPaths; + foreach (const QString &searchPath, m_reader->searchPaths()) + addExtraModuleSearchPath(moduleSearchPaths, searchPath); + bool cacheHit; + modulePrototype = searchAndLoadModuleFile(productContext, dependsItemLocation, + moduleName, moduleSearchPaths, isRequired, &cacheHit); + if (isBaseModule && modulePrototype && !cacheHit) + setupBaseModulePrototype(modulePrototype); + } if (!modulePrototype) return 0; - if (!cacheHit && isBaseModule) - setupBaseModulePrototype(modulePrototype); - instantiateModule(productContext, item, moduleInstance, modulePrototype, moduleName); + instantiateModule(productContext, nullptr, item, moduleInstance, modulePrototype, + moduleName, *isProductDependency); if (moduleInstance->isPresentModule()) { resolveProbes(moduleInstance); try { @@ -833,8 +890,6 @@ Item *ModuleLoader::loadModule(ProductContext *productContext, Item *item, return moduleInstance; } -// It's not necessarily an error if we don't find a required module with the given name, -// because the dependency could refer to a product instead. Item *ModuleLoader::searchAndLoadModuleFile(ProductContext *productContext, const CodeLocation &dependsItemLocation, const QualifiedId &moduleName, const QStringList &extraSearchPaths, bool isRequired, bool *cacheHit) @@ -1001,7 +1056,8 @@ void ModuleLoader::loadBaseModule(ProductContext *productContext, Item *item) Item::Module baseModuleDesc; baseModuleDesc.name = baseModuleName; baseModuleDesc.item = loadModule(productContext, item, CodeLocation(), QString(), - baseModuleName, true, true); + baseModuleName, true, true, &baseModuleDesc.isProduct); + QBS_CHECK(!baseModuleDesc.isProduct); if (Q_UNLIKELY(!baseModuleDesc.item)) throw ErrorInfo(Tr::tr("Cannot load base qbs module.")); item->addModule(baseModuleDesc); @@ -1172,9 +1228,27 @@ static QList<Item *> collectItemsWithId(Item *item) return result; } -void ModuleLoader::instantiateModule(ProductContext *productContext, Item *instanceScope, - Item *moduleInstance, Item *modulePrototype, - const QualifiedId &moduleName) +static Item *productOf(Item *item) +{ + do { + item = item->parent(); + } while (item && item->typeName() != QLatin1String("Product")); + return item; +} + +static void copyNonOverriddenProperties(const Item *src, Item *dst) +{ + const Item::PropertyMap &srcProps = src->properties(); + for (auto it = srcProps.constBegin(); it != srcProps.constEnd(); ++it) { + if (it.value()->type() != Value::JSSourceValueType || dst->hasOwnProperty(it.key())) + continue; + dst->setProperty(it.key(), it.value()); + } +} + +void ModuleLoader::instantiateModule(ProductContext *productContext, Item *exportingProduct, + Item *instanceScope, Item *moduleInstance, Item *modulePrototype, + const QualifiedId &moduleName, bool isProduct) { const QString fullName = moduleName.toString(); moduleInstance->setPrototype(modulePrototype); @@ -1189,6 +1263,45 @@ void ModuleLoader::instantiateModule(ProductContext *productContext, Item *insta moduleScope->setScope(instanceScope); copyProperty(QLatin1String("project"), productContext->project->scope, moduleScope); copyProperty(QLatin1String("product"), productContext->scope, moduleScope); + + if (isProduct) { + exportingProduct = 0; + for (Item *exportItem = modulePrototype; exportItem; exportItem = exportItem->prototype()) { + exportingProduct = productOf(exportItem); + if (exportingProduct) + break; + } + QBS_CHECK(exportingProduct); + } + + if (exportingProduct) { + if (!isProduct) { + copyNonOverriddenProperties(modulePrototype, moduleInstance); + moduleInstance->setPrototype(modulePrototype->prototype()); + } + + Item *exportScope = Item::create(moduleInstance->pool()); + exportScope->setFile(instanceScope->file()); + exportScope->setScope(instanceScope); + exportScope->setProperty(QLatin1String("product"), ItemValue::create(exportingProduct)); + exportScope->setProperty(QLatin1String("project"), + ItemValue::create(exportingProduct->parent())); + + for (auto it = moduleInstance->properties().begin(); + it != moduleInstance->properties().end(); ++it) { + if (it.value()->type() != Value::JSSourceValueType) + continue; + const JSSourceValuePtr v = it.value().staticCast<JSSourceValue>(); + v->setExportScope(exportScope); + } + + PropertyDeclaration pd(QLatin1String("_qbs_sourceDir"), PropertyDeclaration::String, + PropertyDeclaration::PropertyNotAvailableInConfig); + moduleInstance->setPropertyDeclaration(pd.name(), pd); + ValuePtr v = exportingProduct->property(QLatin1String("sourceDirectory"))->clone(); + moduleInstance->setProperty(pd.name(), v); + } + moduleInstance->setScope(moduleScope); moduleInstance->setModuleInstanceFlag(true); @@ -1223,7 +1336,9 @@ void ModuleLoader::instantiateModule(ProductContext *productContext, Item *insta } QBS_CHECK(obj == depinst); } - instantiateModule(productContext, moduleInstance, depinst, m.item, m.name); + QBS_ASSERT(depinst != m.item, continue); + instantiateModule(productContext, isProduct ? exportingProduct : nullptr, moduleInstance, + depinst, m.item, m.name, m.isProduct); m.item = depinst; moduleInstance->addModule(m); } @@ -1485,18 +1600,31 @@ void ModuleLoader::addTransitiveDependencies(ProductContext *ctx, Item *item) QVector<Item::Module> transitiveDeps = allModules(item); std::sort(transitiveDeps.begin(), transitiveDeps.end()); foreach (const Item::Module &m, item->modules()) { + if (m.isProduct) { + ctx->info.usedProducts.append( + ctx->project->topLevelProject->productModules.value( + m.name.toString()).productDependencies); + } + auto it = std::lower_bound(transitiveDeps.begin(), transitiveDeps.end(), m); if (it != transitiveDeps.end() && it->name == m.name) transitiveDeps.erase(it); } foreach (const Item::Module &module, transitiveDeps) { - Item::Module dep; - dep.item = loadModule(ctx, item, item->location(), QString(), module.name, - false, module.required); - if (!dep.item) - continue; - dep.name = module.name; - item->addModule(dep); + if (module.isProduct) { + item->addModule(module); + ctx->info.usedProducts.append( + ctx->project->topLevelProject->productModules.value( + module.name.toString()).productDependencies); + } else { + Item::Module dep; + dep.item = loadModule(ctx, item, item->location(), QString(), module.name, + false, module.required, &dep.isProduct); + if (!dep.item) + continue; + dep.name = module.name; + item->addModule(dep); + } } } diff --git a/src/lib/corelib/language/moduleloader.h b/src/lib/corelib/language/moduleloader.h index 951dfb218..27fe83203 100644 --- a/src/lib/corelib/language/moduleloader.h +++ b/src/lib/corelib/language/moduleloader.h @@ -40,6 +40,7 @@ #include <QMap> #include <QSet> +#include <QStack> #include <QStringList> #include <QVariantMap> @@ -80,7 +81,6 @@ struct ModuleLoaderResult }; QList<Dependency> usedProducts; - QList<Dependency> usedProductsFromExportItem; }; QSharedPointer<ItemPool> itemPool; @@ -129,23 +129,48 @@ private: Item *scope; }; - class ProjectContext : public ContextBase - { - public: - ModuleLoaderResult *result; - QString buildDirectory; - }; + class ProjectContext; class ProductContext : public ContextBase { public: ProjectContext *project; ModuleLoaderResult::ProductInfo info; + QString name; QString profileName; QSet<FileContextConstPtr> filesWithExportItem; QVariantMap moduleProperties; }; + class TopLevelProjectContext; + + class ProjectContext : public ContextBase + { + public: + TopLevelProjectContext *topLevelProject; + ModuleLoaderResult *result; + QString buildDirectory; + QVector<ProductContext> products; + QStack<QStringList> searchPathsStack; + }; + + struct ProductModuleInfo + { + Item *exportItem = 0; + QList<ModuleLoaderResult::ProductInfo::Dependency> productDependencies; + }; + + class TopLevelProjectContext + { + Q_DISABLE_COPY(TopLevelProjectContext) + public: + TopLevelProjectContext() {} + ~TopLevelProjectContext() { qDeleteAll(projects); } + + QVector<ProjectContext *> projects; + QHash<QString, ProductModuleInfo> productModules; + }; + class DependsContext { public: @@ -157,10 +182,12 @@ private: void handleTopLevelProject(ModuleLoaderResult *loadResult, Item *item, const QString &buildDirectory, const QSet<QString> &referencedFilePaths); - void handleProject(ModuleLoaderResult *loadResult, Item *item, const QString &buildDirectory, - const QSet<QString> &referencedFilePaths); + void handleProject(ModuleLoaderResult *loadResult, + TopLevelProjectContext *topLevelProjectContext, Item *item, + const QString &buildDirectory, const QSet<QString> &referencedFilePaths); QList<Item *> multiplexProductItem(ProductContext *dummyContext, Item *productItem); - void handleProduct(ProjectContext *projectContext, Item *item); + void prepareProduct(ProjectContext *projectContext, Item *item); + void handleProduct(ProductContext *productContext); void initProductProperties(const ProjectContext *project, Item *item); void handleSubProject(ProjectContext *projectContext, Item *item, const QSet<QString> &referencedFilePaths); @@ -171,9 +198,10 @@ private: class ItemModuleList; void resolveDependsItem(DependsContext *dependsContext, Item *item, Item *dependsItem, ItemModuleList *moduleResults, ProductDependencyResults *productResults); Item *moduleInstanceItem(Item *item, const QualifiedId &moduleName); + Item *loadProductModule(ProductContext *productContext, const QString &moduleName); Item *loadModule(ProductContext *productContext, Item *item, const CodeLocation &dependsItemLocation, const QString &moduleId, - const QualifiedId &moduleName, bool isBaseModule, bool isRequired); + const QualifiedId &moduleName, bool isBaseModule, bool isRequired, bool *isModuleDependency); Item *searchAndLoadModuleFile(ProductContext *productContext, const CodeLocation &dependsItemLocation, const QualifiedId &moduleName, const QStringList &extraSearchPaths, bool isRequired, bool *cacheHit); @@ -181,7 +209,9 @@ private: bool isBaseModule, const QString &filePath, bool *cacheHit, bool *triedToLoad); void loadBaseModule(ProductContext *productContext, Item *item); void setupBaseModulePrototype(Item *prototype); - void instantiateModule(ProductContext *productContext, Item *instanceScope, Item *moduleInstance, Item *modulePrototype, const QualifiedId &moduleName); + void instantiateModule(ProductContext *productContext, Item *exportingProductItem, + Item *instanceScope, Item *moduleInstance, Item *modulePrototype, + const QualifiedId &moduleName, bool isProduct); void createChildInstances(ProductContext *productContext, Item *instance, Item *prototype, QHash<Item *, Item *> *prototypeInstanceMap) const; void resolveProbes(Item *item); @@ -209,6 +239,7 @@ private: Evaluator *m_evaluator; QStringList m_moduleSearchPaths; QMap<QString, QStringList> m_moduleDirListCache; + QHash<QString, Item *> m_productModuleCache; ModuleItemCache m_modulePrototypeItemCache; QHash<Item *, QSet<QString> > m_validItemPropertyNamesPerItem; QSet<Item *> m_disabledItems; diff --git a/src/lib/corelib/language/modulemerger.cpp b/src/lib/corelib/language/modulemerger.cpp index ea8a8f377..577f9098c 100644 --- a/src/lib/corelib/language/modulemerger.cpp +++ b/src/lib/corelib/language/modulemerger.cpp @@ -146,13 +146,15 @@ void ModuleMerger::mergeOutProps(Item::PropertyMap *dst, const Item::PropertyMap continue; if (pd.isScalar()) { - if (dstVal->sourceCode() != srcVal->sourceCode()) { + if (dstVal->sourceCode() != srcVal->sourceCode() + && dstVal->isInExportItem() == srcVal->isInExportItem()) { 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(); + if (!dstVal->isInExportItem()) + v = it.value(); } else { lastInNextChain(dstVal)->setNext(srcVal); } diff --git a/src/lib/corelib/language/projectresolver.cpp b/src/lib/corelib/language/projectresolver.cpp index 4a8839221..a9435166c 100644 --- a/src/lib/corelib/language/projectresolver.cpp +++ b/src/lib/corelib/language/projectresolver.cpp @@ -75,7 +75,6 @@ ProjectResolver::ProjectResolver(ModuleLoader *ldr, const BuiltinDeclarations *b , m_logger(logger) , m_engine(m_evaluator->engine()) , m_progressObserver(0) - , m_disableCachedEvaluation(false) { } @@ -375,7 +374,7 @@ void ProjectResolver::resolveProduct(Item *item, ProjectContext *projectContext) mapping["FileTagger"] = &ProjectResolver::resolveFileTagger; mapping["Transformer"] = &ProjectResolver::resolveTransformer; mapping["Group"] = &ProjectResolver::resolveGroup; - mapping["Export"] = &ProjectResolver::resolveExport; + mapping["Export"] = &ProjectResolver::ignoreItem; mapping["Probe"] = &ProjectResolver::ignoreItem; mapping["PropertyOptions"] = &ProjectResolver::ignoreItem; @@ -410,7 +409,7 @@ void ProjectResolver::resolveModules(const Item *item, ProjectContext *projectCo if (seen.contains(m.name)) continue; seen.insert(m.name); - resolveModule(m.name, m.item, projectContext); + resolveModule(m.name, m.item, m.isProduct, projectContext); foreach (const Item::Module &childModule, m.item->modules()) modules.enqueue(childModule); } @@ -420,7 +419,7 @@ void ProjectResolver::resolveModules(const Item *item, ProjectContext *projectCo }); } -void ProjectResolver::resolveModule(const QualifiedId &moduleName, Item *item, +void ProjectResolver::resolveModule(const QualifiedId &moduleName, Item *item, bool isProduct, ProjectContext *projectContext) { checkCancelation(); @@ -450,7 +449,8 @@ void ProjectResolver::resolveModule(const QualifiedId &moduleName, Item *item, module->moduleDependencies += m.name.toString(); } - m_productContext->product->modules += module; + if (!isProduct) + m_productContext->product->modules += module; ItemFuncMap mapping; mapping["Group"] = &ProjectResolver::resolveGroup; @@ -843,83 +843,8 @@ void ProjectResolver::resolveScanner(Item *item, ProjectResolver::ProjectContext m_productContext->product->scanners += scanner; } -void ProjectResolver::resolveExport(Item *item, ProjectContext *projectContext) -{ - Q_UNUSED(projectContext); - checkCancelation(); - 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_evaluator->setCachingEnabled(true); - m_exportsContext->moduleValues = evaluateModuleValues(item, false); - m_evaluator->setCachingEnabled(false); - - ItemFuncMap mapping; - mapping["Depends"] = &ProjectResolver::ignoreItem; - mapping["FileTagger"] = &ProjectResolver::resolveFileTagger; - mapping["Rule"] = &ProjectResolver::resolveRule; - - foreach (Item *child, item->children()) - callItemFunction(mapping, child, projectContext); - - m_exportsContext = 0; -} - -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<QualifiedId, Item::Module> productModules; - foreach (const Item::Module &m, productItem->modules()) - productModules[m.name] = m; - - QHash<QualifiedId, Item::Module> exportModules; - foreach (const Item::Module &m, exportItem->modules()) - exportModules[m.name] = m; - - QVariantMap properties = propertyMap->value(); - 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 QualifiedId &moduleName = QualifiedId::fromString(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); -} - QList<ResolvedProductPtr> ProjectResolver::getProductDependencies(const ResolvedProductConstPtr &product, - const Item *productItem, ModuleLoaderResult::ProductInfo *productInfo) + ModuleLoaderResult::ProductInfo *productInfo) { QList<ResolvedProductPtr> usedProducts; for (int i = productInfo->usedProducts.count() - 1; i >= 0; --i) { @@ -958,10 +883,7 @@ QList<ResolvedProductPtr> ProjectResolver::getProductDependencies(const Resolved } else { const ResolvedProductPtr &usedProduct = m_productsByName.value(dependency.uniqueName()); - if (Q_UNLIKELY(!usedProduct)) { - throw ErrorInfo(Tr::tr("Product dependency '%1' not found for profile '%2'.") - .arg(dependency.name, dependency.profile), productItem->location()); - } + QBS_ASSERT(usedProduct, continue); usedProducts << usedProduct; } } @@ -980,73 +902,6 @@ void ProjectResolver::matchArtifactProperties(const ResolvedProductPtr &product, } } -static void addUsedProducts(ModuleLoaderResult::ProductInfo *productInfo, - const ModuleLoaderResult::ProductInfo &usedProductInfo, - bool *productsAdded) -{ - int oldCount = productInfo->usedProducts.count(); - QSet<QString> usedProductNames; - foreach (const ModuleLoaderResult::ProductInfo::Dependency &usedProduct, - productInfo->usedProducts) - usedProductNames += usedProduct.uniqueName(); - foreach (const ModuleLoaderResult::ProductInfo::Dependency &usedProduct, - usedProductInfo.usedProductsFromExportItem) { - if (!usedProductNames.contains(usedProduct.uniqueName())) - productInfo->usedProducts += usedProduct; - } - *productsAdded = (oldCount != productInfo->usedProducts.count()); -} - -typedef QHash<Item *, ValuePtr> ValuePerItem; - -static void replaceProductRecursive(Item *item, const ItemValuePtr &productItemValue, - ValuePerItem *seen) -{ - if (seen->contains(item)) - return; - ValuePtr oldProductValue = item->property(QLatin1String("product")); - seen->insert(item, oldProductValue); - if (oldProductValue) - item->setProperty(QLatin1String("product"), productItemValue); - if (item->scope()) - replaceProductRecursive(item->scope(), productItemValue, seen); - foreach (const Item::Module &module, item->modules()) - replaceProductRecursive(module.item, productItemValue, seen); - foreach (Item *child, item->children()) - replaceProductRecursive(child, productItemValue, seen); -} - -static ValuePerItem replaceProduct(Item *item, const ItemValuePtr &productItemValue) -{ - ValuePerItem seen; - replaceProductRecursive(item, productItemValue, &seen); - return seen; -} - -static void restoreOldProducts(const ValuePerItem &vip) -{ - for (ValuePerItem::const_iterator it = vip.constBegin(); it != vip.constEnd(); ++it) { - const ValuePtr &oldProductValue = it.value(); - if (oldProductValue) - it.key()->setProperty(QLatin1String("product"), oldProductValue); - } -} - -static void copyProperties(const QVariantMap &src, QVariantMap &dst) -{ - for (QVariantMap::const_iterator it = src.constBegin(); it != src.constEnd(); ++it) { - const QVariant &v = it.value(); - if (v.type() == QVariant::Map) { - QVariant &dstValue = dst[it.key()]; - QVariantMap dstMap = dstValue.toMap(); - copyProperties(v.toMap(), dstMap); - dstValue = dstMap; - } else { - dst[it.key()] = v; - } - } -} - static bool hasDependencyCycle(QSet<ResolvedProduct *> *checked, QSet<ResolvedProduct *> *branch, const ResolvedProductPtr &product, @@ -1070,40 +925,16 @@ static bool hasDependencyCycle(QSet<ResolvedProduct *> *checked, void ProjectResolver::resolveProductDependencies(ProjectContext *projectContext) { - // Collect product dependencies from Export items. - bool productDependenciesAdded; - QList<ResolvedProductPtr> allProducts = projectContext->project->allProducts(); - do { - productDependenciesAdded = false; - foreach (const ResolvedProductPtr &rproduct, allProducts) { - if (!rproduct->enabled) - continue; - Item *productItem = m_productItemMap.value(rproduct); - ModuleLoaderResult::ProductInfo &productInfo - = projectContext->loadResult->productInfos[productItem]; - foreach (const ResolvedProductPtr &usedProduct, - getProductDependencies(rproduct, productItem, &productInfo)) { - Item *usedProductItem = m_productItemMap.value(usedProduct); - const ModuleLoaderResult::ProductInfo usedProductInfo - = projectContext->loadResult->productInfos.value(usedProductItem); - bool added; - addUsedProducts(&productInfo, usedProductInfo, &added); - if (added) - productDependenciesAdded = true; - } - } - } while (productDependenciesAdded); - // Resolve all inter-product dependencies. + QList<ResolvedProductPtr> allProducts = projectContext->project->allProducts(); foreach (const ResolvedProductPtr &rproduct, allProducts) { if (!rproduct->enabled) continue; Item *productItem = m_productItemMap.value(rproduct); - ItemValuePtr productItemValue = ItemValue::create(productItem); ModuleLoaderResult::ProductInfo &productInfo = projectContext->loadResult->productInfos[productItem]; foreach (const ResolvedProductPtr &usedProduct, - getProductDependencies(rproduct, productItem, &productInfo)) { + getProductDependencies(rproduct, &productInfo)) { rproduct->dependencies.insert(usedProduct); const QString &usedProductName = usedProduct->uniqueName(); const ExportsContext ctx = m_exports.value(usedProductName); @@ -1111,34 +942,6 @@ void ProjectResolver::resolveProductDependencies(ProjectContext *projectContext) rproduct->fileTaggers << ctx.fileTaggers; foreach (const RulePtr &rule, ctx.rules) rproduct->rules << rule; - - // Evaluate properties of the Export item using the importing product. - ProductContext productContext; - productContext.product = usedProduct; - m_productContext = &productContext; - const ValuePerItem oldProducts = replaceProduct(ctx.item, productItemValue); - m_disableCachedEvaluation = true; - QVariantMap exportedConfig = evaluateModuleValues(ctx.item); - m_disableCachedEvaluation = false; - restoreOldProducts(oldProducts); - - // Re-evaluate direct properties of the Export item using the exporting product. - copyProperties(ctx.moduleValues, exportedConfig); - m_productContext = 0; - - // insert the configuration of the Export item into the product's configuration - if (exportedConfig.isEmpty()) - continue; - - 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(productItem, ctx.item, exportedConfig, - artifact->properties); - } - } } } @@ -1229,20 +1032,7 @@ QVariantMap ProjectResolver::evaluateProperties(Item *item, Item *propertiesCont break; } - bool cacheDisabled = false; - const JSSourceValuePtr srcValue = it.value().staticCast<JSSourceValue>(); - if (m_disableCachedEvaluation && (pd.type() == PropertyDeclaration::Path - || srcValue->sourceUsesProduct())) { - cacheDisabled = true; - m_evaluator->setCachingEnabled(false); - m_evaluator->engine()->setPropertyCacheEnabled(false); - } - const QScriptValue scriptValue = m_evaluator->property(item, it.key()); - if (cacheDisabled) { - m_evaluator->setCachingEnabled(true); - m_evaluator->engine()->setPropertyCacheEnabled(true); - } if (Q_UNLIKELY(m_evaluator->engine()->hasErrorOrException(scriptValue))) throw ErrorInfo(scriptValue.toString(), it.value()->location()); diff --git a/src/lib/corelib/language/projectresolver.h b/src/lib/corelib/language/projectresolver.h index d3af506be..6787c459a 100644 --- a/src/lib/corelib/language/projectresolver.h +++ b/src/lib/corelib/language/projectresolver.h @@ -117,7 +117,8 @@ private: void resolveSubProject(Item *item, ProjectContext *projectContext); void resolveProduct(Item *item, ProjectContext *projectContext); void resolveModules(const Item *item, ProjectContext *projectContext); - void resolveModule(const QualifiedId &moduleName, Item *item, ProjectContext *projectContext); + void resolveModule(const QualifiedId &moduleName, Item *item, bool isProduct, + ProjectContext *projectContext); void resolveGroup(Item *item, ProjectContext *projectContext); void resolveRule(Item *item, ProjectContext *projectContext); void resolveRuleArtifact(const RulePtr &rule, Item *item); @@ -127,7 +128,6 @@ private: void resolveFileTagger(Item *item, ProjectContext *projectContext); void resolveTransformer(Item *item, ProjectContext *projectContext); void resolveScanner(Item *item, ProjectContext *projectContext); - void resolveExport(Item *item, ProjectContext *projectContext); void resolveProductDependencies(ProjectContext *projectContext); void postProcess(const ResolvedProductPtr &product, ProjectContext *projectContext) const; void applyFileTaggers(const ResolvedProductPtr &product) const; @@ -148,7 +148,7 @@ private: QStringList convertPathListProperty(const QStringList &paths, const QString &dirPath) const; ProjectContext createProjectContext(ProjectContext *parentProjectContext) const; QList<ResolvedProductPtr> getProductDependencies(const ResolvedProductConstPtr &product, - const Item *productItem, ModuleLoaderResult::ProductInfo *productInfo); + ModuleLoaderResult::ProductInfo *productInfo); static void matchArtifactProperties(const ResolvedProductPtr &product, const QList<SourceArtifactPtr> &artifacts); @@ -160,7 +160,6 @@ private: ProductContext *m_productContext; ModuleContext *m_moduleContext; ExportsContext *m_exportsContext; - bool m_disableCachedEvaluation; QMap<QString, ResolvedProductPtr> m_productsByName; QHash<QString, QList<ResolvedProductPtr> > m_productsByType; QHash<ResolvedProductPtr, Item *> m_productItemMap; diff --git a/src/lib/corelib/language/testdata/exports.qbs b/src/lib/corelib/language/testdata/exports.qbs index 0d467c522..0187817ff 100644 --- a/src/lib/corelib/language/testdata/exports.qbs +++ b/src/lib/corelib/language/testdata/exports.qbs @@ -11,7 +11,8 @@ Project { } references: [ - "subdir/exports-mylib.qbs" + "subdir/exports-mylib.qbs", + "subdir2/exports-mylib2.qbs" ] Application { diff --git a/src/lib/corelib/language/testdata/subdir/exports-mylib.qbs b/src/lib/corelib/language/testdata/subdir/exports-mylib.qbs index 58ac2e4a9..e3802de50 100644 --- a/src/lib/corelib/language/testdata/subdir/exports-mylib.qbs +++ b/src/lib/corelib/language/testdata/subdir/exports-mylib.qbs @@ -7,7 +7,8 @@ StaticLibrary { property string definePrefix: "USE_" Export { Depends { name: "dummy" } - dummy.defines: [definePrefix + product.name.toUpperCase()] + Depends { name: "mylib2" } + dummy.defines: [product.definePrefix + product.name.toUpperCase()] dummy.includePaths: ["./lib"] } } diff --git a/src/lib/corelib/language/testdata/subdir2/exports-mylib2.qbs b/src/lib/corelib/language/testdata/subdir2/exports-mylib2.qbs new file mode 100644 index 000000000..a0cec6eb0 --- /dev/null +++ b/src/lib/corelib/language/testdata/subdir2/exports-mylib2.qbs @@ -0,0 +1,13 @@ +import qbs + +StaticLibrary { + name: "mylib2" + Depends { name: "dummy" } + dummy.defines: ["BUILD_" + product.name.toUpperCase()] + property string definePrefix: "USE_" + Export { + Depends { name: "dummy" } + dummy.defines: [product.definePrefix + product.name.toUpperCase()] + dummy.includePaths: ["./lib"] + } +} diff --git a/src/lib/corelib/language/tst_language.cpp b/src/lib/corelib/language/tst_language.cpp index 72d077293..af5166f2c 100644 --- a/src/lib/corelib/language/tst_language.cpp +++ b/src/lib/corelib/language/tst_language.cpp @@ -460,19 +460,21 @@ void TestLanguage::exports() TopLevelProjectPtr project = loader->loadProject(defaultParameters); QVERIFY(project); QHash<QString, ResolvedProductPtr> products = productsFromProject(project); - QCOMPARE(products.count(), 11); + QCOMPARE(products.count(), 12); ResolvedProductPtr product; product = products.value("myapp"); QVERIFY(product); QStringList propertyName = QStringList() << "modules" << "dummy" << "defines"; QVariant propertyValue = getConfigProperty(product->moduleProperties->value(), propertyName); - QCOMPARE(propertyValue.toStringList(), QStringList() << "BUILD_MYAPP" << "USE_MYLIB"); + QCOMPARE(propertyValue.toStringList(), QStringList() << "BUILD_MYAPP" << "USE_MYLIB" + << "USE_MYLIB2"); propertyName = QStringList() << "modules" << "dummy" << "includePaths"; QVariantList propertyValues = getConfigProperty(product->moduleProperties->value(), propertyName).toList(); - QCOMPARE(propertyValues.count(), 2); + QCOMPARE(propertyValues.count(), 3); QVERIFY(propertyValues.at(0).toString().endsWith("/app")); QVERIFY(propertyValues.at(1).toString().endsWith("/subdir/lib")); + QVERIFY(propertyValues.at(2).toString().endsWith("/subdir2/lib")); QCOMPARE(PropertyFinder().propertyValue(product->moduleProperties->value(), "dummy", "productName").toString(), QString("myapp")); @@ -483,6 +485,12 @@ void TestLanguage::exports() propertyValue = getConfigProperty(product->moduleProperties->value(), propertyName); QCOMPARE(propertyValue.toStringList(), QStringList() << "BUILD_MYLIB"); + product = products.value("mylib2"); + QVERIFY(product); + propertyName = QStringList() << "modules" << "dummy" << "defines"; + propertyValue = getConfigProperty(product->moduleProperties->value(), propertyName); + QCOMPARE(propertyValue.toStringList(), QStringList() << "BUILD_MYLIB2"); + product = products.value("A"); QVERIFY(product); QVERIFY(product->dependencies.contains(products.value("B"))); diff --git a/src/lib/corelib/language/value.cpp b/src/lib/corelib/language/value.cpp index a8174270a..141e1759d 100644 --- a/src/lib/corelib/language/value.cpp +++ b/src/lib/corelib/language/value.cpp @@ -68,7 +68,7 @@ void Value::setNext(const ValuePtr &next) JSSourceValue::JSSourceValue() - : Value(JSSourceValueType), m_line(-1), m_column(-1) + : Value(JSSourceValueType), m_line(-1), m_column(-1), m_exportScope(nullptr) { } @@ -123,6 +123,16 @@ void JSSourceValue::setDefiningItem(Item *item) a.value->setDefiningItem(item); } +Item *JSSourceValue::exportScope() const +{ + return m_exportScope; +} + +void JSSourceValue::setExportScope(Item *extraScope) +{ + m_exportScope = extraScope; +} + ItemValue::ItemValue(Item *item) : Value(ItemValueType) diff --git a/src/lib/corelib/language/value.h b/src/lib/corelib/language/value.h index cac11bbd3..2dcc7ff17 100644 --- a/src/lib/corelib/language/value.h +++ b/src/lib/corelib/language/value.h @@ -91,7 +91,6 @@ class JSSourceValue : public Value NoFlags = 0x00, SourceUsesBase = 0x01, SourceUsesOuter = 0x02, - SourceUsesProduct = 0x04, HasFunctionForm = 0x08 }; Q_DECLARE_FLAGS(Flags, Flag) @@ -118,8 +117,7 @@ public: void setSourceUsesBaseFlag() { m_flags |= SourceUsesBase; } bool sourceUsesBase() const { return m_flags.testFlag(SourceUsesBase); } bool sourceUsesOuter() const { return m_flags.testFlag(SourceUsesOuter); } - void setSourceUsesProductFlag() { m_flags |= SourceUsesProduct; } - bool sourceUsesProduct() const { return m_flags.testFlag(SourceUsesProduct); } + bool isInExportItem() const { return m_exportScope; } bool hasFunctionForm() const { return m_flags.testFlag(HasFunctionForm); } void setHasFunctionForm(bool b); @@ -138,6 +136,9 @@ public: void setDefiningItem(Item *item); + Item *exportScope() const; + void setExportScope(Item *exportScope); + private: QStringRef m_sourceCode; int m_line; @@ -146,6 +147,7 @@ private: Flags m_flags; JSSourceValuePtr m_baseValue; QList<Alternative> m_alternatives; + Item *m_exportScope; }; class Item; diff --git a/tests/auto/api/testdata/export-item-with-group/main.cpp b/tests/auto/api/testdata/export-item-with-group/main.cpp new file mode 100644 index 000000000..8b8d58de0 --- /dev/null +++ b/tests/auto/api/testdata/export-item-with-group/main.cpp @@ -0,0 +1 @@ +int main() { } diff --git a/tests/auto/api/testdata/export-item-with-group/project.qbs b/tests/auto/api/testdata/export-item-with-group/project.qbs new file mode 100644 index 000000000..861da81f4 --- /dev/null +++ b/tests/auto/api/testdata/export-item-with-group/project.qbs @@ -0,0 +1,16 @@ +import qbs + +Project { + Product { + name: "dep" + Export { + Depends { name: "cpp" } + Group { files: ["main.cpp"] } + } + } + + Application { + name: "app" + Depends { name: "dep" } + } +} diff --git a/tests/auto/api/tst_api.cpp b/tests/auto/api/tst_api.cpp index 9365a06c1..03bc86195 100644 --- a/tests/auto/api/tst_api.cpp +++ b/tests/auto/api/tst_api.cpp @@ -331,6 +331,9 @@ void TestApi::buildProject_data() QTest::newRow("only default properties") << QString("two-default-property-values") << relativeProductBuildDir("two-default-property-values") + "/set"; + QTest::newRow("Export item with Group") + << QString("export-item-with-group") + << relativeExecutableFilePath("app"); QTest::newRow("QBS-728") << QString("QBS-728") << QString(); @@ -1024,7 +1027,6 @@ void TestApi::installableFiles() installOptions.setInstallRoot(QLatin1String("/tmp")); QList<qbs::InstallableFile> installableFiles = project.installableFilesForProduct(product, installOptions); - QEXPECT_FAIL(0, "QBS-830", Continue); QCOMPARE(installableFiles.count(), 2); foreach (const qbs::InstallableFile &f,installableFiles) { if (!f.sourceFilePath().endsWith("main.cpp")) { diff --git a/tests/auto/blackbox/tst_blackbox.cpp b/tests/auto/blackbox/tst_blackbox.cpp index fc73ba968..3b802000e 100644 --- a/tests/auto/blackbox/tst_blackbox.cpp +++ b/tests/auto/blackbox/tst_blackbox.cpp @@ -1765,7 +1765,6 @@ void TestBlackbox::nestedProperties() { QDir::setCurrent(testDataDir + "/nested-properties"); QCOMPARE(runQbs(), 0); - QEXPECT_FAIL(0, "QBS-736", Continue); QVERIFY2(m_qbsStdout.contains("value in higherlevel"), m_qbsStdout.constData()); } @@ -1865,28 +1864,26 @@ void TestBlackbox::propertyPrecedence() switchFileContents(nonleafFile, false); switchFileContents(depFile, true); QCOMPARE(runQbs(params), 0); - QVERIFY2(m_qbsStderr == "scalar prop: export\nlist prop: [\"leaf\",\"export\"]\n", + QVERIFY2(m_qbsStderr == "scalar prop: export\nlist prop: [\"export\",\"leaf\"]\n", m_qbsStderr.constData()); // Case 6: [cmdline=0,prod=0,export=1,nonleaf=0,profile=1] switchProfileContents(profile.p, &s, true); QCOMPARE(runQbs(params), 0); - QVERIFY2(m_qbsStderr == "scalar prop: export\nlist prop: [\"profile\",\"export\"]\n", + QVERIFY2(m_qbsStderr == "scalar prop: export\nlist prop: [\"export\",\"profile\"]\n", m_qbsStderr.constData()); // Case 7: [cmdline=0,prod=0,export=1,nonleaf=1,profile=0] switchProfileContents(profile.p, &s, false); switchFileContents(nonleafFile, true); QCOMPARE(runQbs(params), 0); - QEXPECT_FAIL(0, "QBS-814", Continue); - QVERIFY2(m_qbsStderr == "scalar prop: export\nlist prop: [\"nonleaf\",\"leaf\",\"export\"]\n", + QVERIFY2(m_qbsStderr == "scalar prop: export\nlist prop: [\"export\",\"nonleaf\",\"leaf\"]\n", m_qbsStderr.constData()); // Case 8: [cmdline=0,prod=0,export=1,nonleaf=1,profile=1] switchProfileContents(profile.p, &s, true); QCOMPARE(runQbs(params), 0); - QEXPECT_FAIL(0, "QBS-814", Continue); - QVERIFY2(m_qbsStderr == "scalar prop: export\nlist prop: [\"nonleaf\",\"profile\",\"export\"]\n", + QVERIFY2(m_qbsStderr == "scalar prop: export\nlist prop: [\"export\",\"nonleaf\",\"profile\"]\n", m_qbsStderr.constData()); // Case 9: [cmdline=0,prod=1,export=0,nonleaf=0,profile=0] @@ -1924,26 +1921,26 @@ void TestBlackbox::propertyPrecedence() switchFileContents(nonleafFile, false); switchFileContents(depFile, true); QCOMPARE(runQbs(params), 0); - QVERIFY2(m_qbsStderr == "scalar prop: product\nlist prop: [\"product\",\"leaf\",\"export\"]\n", + QVERIFY2(m_qbsStderr == "scalar prop: product\nlist prop: [\"product\",\"export\",\"leaf\"]\n", m_qbsStderr.constData()); // Case 14: [cmdline=0,prod=1,export=1,nonleaf=0,profile=1] switchProfileContents(profile.p, &s, true); QCOMPARE(runQbs(params), 0); - QVERIFY2(m_qbsStderr == "scalar prop: product\nlist prop: [\"product\",\"profile\",\"export\"]\n", + QVERIFY2(m_qbsStderr == "scalar prop: product\nlist prop: [\"product\",\"export\",\"profile\"]\n", m_qbsStderr.constData()); // Case 15: [cmdline=0,prod=1,export=1,nonleaf=1,profile=0] switchProfileContents(profile.p, &s, false); switchFileContents(nonleafFile, true); QCOMPARE(runQbs(params), 0); - QVERIFY2(m_qbsStderr == "scalar prop: product\nlist prop: [\"product\",\"nonleaf\",\"leaf\",\"export\"]\n", + QVERIFY2(m_qbsStderr == "scalar prop: product\nlist prop: [\"product\",\"export\",\"nonleaf\",\"leaf\"]\n", m_qbsStderr.constData()); // Case 16: [cmdline=0,prod=1,export=1,nonleaf=1,profile=1] switchProfileContents(profile.p, &s, true); QCOMPARE(runQbs(params), 0); - QVERIFY2(m_qbsStderr == "scalar prop: product\nlist prop: [\"product\",\"nonleaf\",\"profile\",\"export\"]\n", + QVERIFY2(m_qbsStderr == "scalar prop: product\nlist prop: [\"product\",\"export\",\"nonleaf\",\"profile\"]\n", m_qbsStderr.constData()); // Command line properties wipe everything, including lists. |