aboutsummaryrefslogtreecommitdiffstats
path: root/src/lib/corelib/language/projectresolver.cpp
diff options
context:
space:
mode:
authorChristian Kandeler <christian.kandeler@qt.io>2023-02-10 12:27:03 +0100
committerChristian Kandeler <christian.kandeler@qt.io>2023-04-20 08:09:46 +0000
commitfb52fed84a1510a7de0172e643d6fd66a780e2e8 (patch)
tree9fcf258fa8e8d8279a3c98d8e6fa37f3ae5c3dc0 /src/lib/corelib/language/projectresolver.cpp
parente178052c763dbe18304a101d6f96e79881081e1a (diff)
Rewrite ModuleLoader
=================== Problem description =================== The ModuleLoader class as it exists before this patch has a number of serious problems: - It instantiates modules over and over again everywhere a Depends item appears. The different instances are later merged in a hopelessly obscure and error-prone way. - It seriously pollutes the module scopes so that sloppily written project files appear to work even though they shouldn't. - Dependencies between products are handled twice: Once using the normal module instantiation code (with the Export item acting like a Module item), and also with a parallel mechanism that does strange, seemingly redundant things especially regarding transitive dependencies, which appear to introduce enormous run-time overhead. It is also split up between ModuleLoader and ProjectResolver, adding even more confusion. - The code is messy, seriously under-documented and hardly understood even by its original authors. It presents a huge obstacle to potential contributors. ================= Patch description ================= - Each module is instantiated once per product. Property values are merged on the fly. Special handling for dependencies between products are kept to the absolutely required minimum. - There are no more extra passes for establishing inter-product dependencies. Instead, whenever an unhandled dependency is encountered, processing the current product is paused and resumed once the dependency is ready, with the product state getting saved and restored in between so no work is done twice. - The ModuleLoader class now really only locates and loads modules. The new main class is called ProjectTreeBuilder, and we have split off small, self-contained pieces wherever possible. This process will be continued in follow-up patches (see next section). ======= Outlook ======= The ProjectTreeBuilder ist still too large and should be split up further into small, easily digestible parts with clear responsibilities, until the top-level code becomes tidy and self-documenting. In the end, it can probably be merged with ProjectResolver and Loader. However, this first requires the tight coupling between ProjectTreeBuilder/ModuleProviderLoader/ProbesResolver/ProjectResolver to be broken; otherwise we can't produce clean interfaces. As this would involve touching a lot of otherwise unrelated code, it is out of scope for this patch. ================= Benchmarking data ================= We first present wall-time clock results gathered by running "qbs resolve --log-time" for qbs itself and Qt Creator on macOS and Windows. The numbers are the average of several runs, with outliers removed. Then the same for a simple QML project using a static Qt on Linux (this case is special because our current way of handling plugins causes a huge amount of modules to be loaded). Finally, we show the output of the qbs_benchmarker tool for resolving qbs and Qt Creator on Linux. The data shows a speed-up that is hardly noticeable for simple projects, but increases sharply with project complexity. This suggests that our new way of resolving does not suffer anymore from the non-linear slowdown when the number of dependencies gets large. Resolving qbs on Windows: Before this patch: ModuleLoader 3.6s, ProjectResolver 870ms With this patch: ProjectTreeBuilder 3.6s, ProjectResolver 840ms Resolving Qt Creator on Windows: Before this patch: ModuleLoader 17s, ProjectResolver 6.8s With this patch: ProjectTreeBuilder 10.0s, ProjectResolver 6.5s Resolving qbs on macOS: Before this patch: ModuleLoader 4.0s, ProjectResolver 2.3s With this patch: ProjectTreeBuilder 4.0s, ProjectResolver 2.3s Resolving Qt Creator on macOS: Before this patch: ModuleLoader 32.0s, ProjectResolver 15.6s With this patch: ProjectTreeBuilder 23.0s, ProjectResolver 15.3s Note that the above numbers are for an initial resolve, so they include the time for running Probes. The speed-up for re-resolving (with cached Probes) is even higher, in particular on macOS, where Probes take excessively long. Resolving with static Qt on Linux (QBS-1521): Before this patch: ModuleLoader 36s, ProjectResolver 18s With this patch: ProjectTreeBuilder 1.5s, ProjectResolver 14s Output of qbs_benchmarker for resolving qbs on Linux: Old instruction count: 10029744668 New instruction count: 9079802661 Relative change: -10 % Old peak memory usage: 69881840 Bytes New peak memory usage: 82434624 Bytes Relative change: +17 % Output of qbs_benchmarker for resolving Qt Creator on Linux: Old instruction count: 87364681688 New instruction count: 53634332869 Relative change: -39 % Old peak memory usage: 578458840 Bytes New peak memory usage: 567271960 Bytes Relative change: -2 % I don't know where the increased memory footprint for a small project comes from, but since it goes away for larger projects, it doesn't seem worth investigating. Fixes: QBS-1037 Task-number: QBS-1521 Change-Id: Ieeebce8a7ff68cdffc15d645e2342ece2426fa94 Reviewed-by: Ivan Komissarov <ABBAPOH@gmail.com> Reviewed-by: Joerg Bornemann <joerg.bornemann@qt.io>
Diffstat (limited to 'src/lib/corelib/language/projectresolver.cpp')
-rw-r--r--src/lib/corelib/language/projectresolver.cpp332
1 files changed, 103 insertions, 229 deletions
diff --git a/src/lib/corelib/language/projectresolver.cpp b/src/lib/corelib/language/projectresolver.cpp
index c2c8ba134..403fd35bc 100644
--- a/src/lib/corelib/language/projectresolver.cpp
+++ b/src/lib/corelib/language/projectresolver.cpp
@@ -117,7 +117,7 @@ struct ProjectResolver::ModuleContext
class CancelException { };
-ProjectResolver::ProjectResolver(Evaluator *evaluator, ModuleLoaderResult loadResult,
+ProjectResolver::ProjectResolver(Evaluator *evaluator, ProjectTreeBuilder::Result loadResult,
SetupProjectParameters setupParameters, Logger &logger)
: m_evaluator(evaluator)
, m_logger(logger)
@@ -261,7 +261,7 @@ TopLevelProjectPtr ProjectResolver::resolveTopLevelProject()
project->environment = m_engine->environment();
project->buildSystemFiles.unite(m_engine->imports());
makeSubProjectNamesUniqe(project);
- resolveProductDependencies(projectContext);
+ resolveProductDependencies();
collectExportedProductDependencies();
checkForDuplicateProductNames(project);
@@ -411,9 +411,32 @@ void ProjectResolver::resolveProduct(Item *item, ProjectContext *projectContext)
productContext.product = product;
product->location = item->location();
ProductContextSwitcher contextSwitcher(this, &productContext, m_progressObserver);
+ const auto errorFromDelayedError = [&] {
+ ProjectTreeBuilder::Result::ProductInfo &pi = m_loadResult.productInfos[item];
+ if (pi.delayedError.hasError()) {
+ ErrorInfo errorInfo;
+
+ // First item is "main error", gets prepended again in the catch clause.
+ const QList<ErrorItem> &items = pi.delayedError.items();
+ for (int i = 1; i < items.size(); ++i)
+ errorInfo.append(items.at(i));
+
+ pi.delayedError.clear();
+ return errorInfo;
+ }
+ return ErrorInfo();
+ };
+
+ // Even if we previously encountered an error, try to continue for as long as possible
+ // to provide IDEs with useful data (e.g. the list of files).
+ // If we encounter a follow-up error, suppress it and report the original one instead.
try {
resolveProductFully(item, projectContext);
- } catch (const ErrorInfo &e) {
+ if (const ErrorInfo error = errorFromDelayedError(); error.hasError())
+ throw error;
+ } catch (ErrorInfo e) {
+ if (const ErrorInfo error = errorFromDelayedError(); error.hasError())
+ e = error;
QString mainErrorString = !product->name.isEmpty()
? Tr::tr("Error while handling product '%1':").arg(product->name)
: Tr::tr("Error while handling product:");
@@ -445,21 +468,10 @@ void ProjectResolver::resolveProductFully(Item *item, ProjectContext *projectCon
product->multiplexConfigurationId
= m_evaluator->stringValue(item, StringConstants::multiplexConfigurationIdProperty());
qCDebug(lcProjectResolver) << "resolveProduct" << product->uniqueName();
- m_productsByName.insert(product->uniqueName(), product);
+ m_productsByItem.insert(item, product);
product->enabled = product->enabled
&& m_evaluator->boolValue(item, StringConstants::conditionProperty());
- ModuleLoaderResult::ProductInfo &pi = m_loadResult.productInfos[item];
- if (pi.delayedError.hasError()) {
- ErrorInfo errorInfo;
-
- // First item is "main error", gets prepended again in the catch clause.
- const QList<ErrorItem> &items = pi.delayedError.items();
- for (int i = 1; i < items.size(); ++i)
- errorInfo.append(items.at(i));
-
- pi.delayedError.clear();
- throw errorInfo;
- }
+ ProjectTreeBuilder::Result::ProductInfo &pi = m_loadResult.productInfos[item];
gatherProductTypes(product.get(), item);
product->targetName = m_evaluator->stringValue(item, StringConstants::targetNameProperty());
product->sourceDirectory = m_evaluator->stringValue(
@@ -526,8 +538,10 @@ void ProjectResolver::resolveProductFully(Item *item, ProjectContext *projectCon
void ProjectResolver::resolveModules(const Item *item, ProjectContext *projectContext)
{
JobLimits jobLimits;
- for (const Item::Module &m : item->modules())
- resolveModule(m.name, m.item, m.isProduct, m.parameters, jobLimits, projectContext);
+ for (const Item::Module &m : item->modules()) {
+ resolveModule(m.name, m.item, m.productInfo.has_value(), m.parameters, jobLimits,
+ projectContext);
+ }
for (int i = 0; i < jobLimits.count(); ++i) {
const JobLimit &moduleJobLimit = jobLimits.jobLimitAt(i);
if (m_productContext->product->jobLimits.getLimit(moduleJobLimit.pool()) == -1)
@@ -592,15 +606,9 @@ void ProjectResolver::resolveModule(const QualifiedId &moduleName, Item *item, b
void ProjectResolver::gatherProductTypes(ResolvedProduct *product, Item *item)
{
- product->fileTags = m_evaluator->fileTagsValue(item, StringConstants::typeProperty());
- for (const Item::Module &m : item->modules()) {
- if (m.item->isPresentModule()) {
- product->fileTags += m_evaluator->fileTagsValue(m.item,
- StringConstants::additionalProductTypesProperty());
- }
- }
- item->setProperty(StringConstants::typeProperty(),
- VariantValue::create(sorted(product->fileTags.toStringList())));
+ const VariantValuePtr type = item->variantProperty(StringConstants::typeProperty());
+ if (type)
+ product->fileTags = FileTags::fromStringList(type->value().toStringList());
}
SourceArtifactPtr ProjectResolver::createSourceArtifact(const ResolvedProductPtr &rproduct,
@@ -678,8 +686,7 @@ QVariantMap ProjectResolver::resolveAdditionalModuleProperties(const Item *group
= QualifiedId(fullPropName.mid(0, fullPropName.size() - 1)).toString();
propsPerModule[moduleName] << fullPropName.last();
}
- EvalCacheEnabler cachingEnabler(m_evaluator);
- m_evaluator->setPathPropertiesBaseDir(m_productContext->product->sourceDirectory);
+ EvalCacheEnabler cachingEnabler(m_evaluator, m_productContext->product->sourceDirectory);
for (const Item::Module &module : group->modules()) {
const QString &fullModName = module.name.toString();
const QStringList propsForModule = propsPerModule.take(fullModName);
@@ -691,7 +698,6 @@ QVariantMap ProjectResolver::resolveAdditionalModuleProperties(const Item *group
modulesMap.insert(fullModName,
evaluateProperties(module.item, module.item, reusableValues, true, true));
}
- m_evaluator->clearPathPropertiesBaseDir();
return modulesMap;
}
@@ -938,37 +944,27 @@ void ProjectResolver::collectExportedProductDependencies()
if (!exportingProduct->enabled)
continue;
Item * const importingProductItem = exportingProductInfo.second;
- std::vector<QString> directDepNames;
+
+ std::vector<std::pair<ResolvedProductPtr, QVariantMap>> directDeps;
for (const Item::Module &m : importingProductItem->modules()) {
- if (m.name.toString() == exportingProduct->name) {
- for (const Item::Module &dep : m.item->modules()) {
- if (dep.isProduct)
- directDepNames.push_back(dep.name.toString());
+ if (m.name.toString() != exportingProduct->name)
+ continue;
+ for (const Item::Module &dep : m.item->modules()) {
+ if (dep.productInfo) {
+ directDeps.emplace_back(m_productsByItem.value(dep.productInfo->item),
+ m.parameters);
}
- break;
}
}
- const ModuleLoaderResult::ProductInfo &importingProductInfo
- = mapValue(m_loadResult.productInfos, importingProductItem);
- const ProductDependencyInfos &depInfos
- = getProductDependencies(dummyProduct, importingProductInfo);
- for (const auto &dep : depInfos.dependencies) {
- if (dep.product == exportingProduct)
- continue;
-
- // Filter out indirect dependencies.
- // TODO: Depends items using "profile" or "productTypes" will not work.
- if (!contains(directDepNames, dep.product->name))
- continue;
-
+ for (const auto &dep : directDeps) {
if (!contains(exportingProduct->exportedModule.productDependencies,
- dep.product->uniqueName())) {
+ dep.first->uniqueName())) {
exportingProduct->exportedModule.productDependencies.push_back(
- dep.product->uniqueName());
+ dep.first->uniqueName());
}
- if (!dep.parameters.isEmpty()) {
- exportingProduct->exportedModule.dependencyParameters.insert(dep.product,
- dep.parameters);
+ if (!dep.second.isEmpty()) {
+ exportingProduct->exportedModule.dependencyParameters.insert(dep.first,
+ dep.second);
}
}
auto &productDeps = exportingProduct->exportedModule.productDependencies;
@@ -1406,64 +1402,6 @@ void ProjectResolver::resolveScanner(Item *item, ProjectResolver::ProjectContext
m_productContext->product->scanners.push_back(scanner);
}
-ProjectResolver::ProductDependencyInfos ProjectResolver::getProductDependencies(
- const ResolvedProductConstPtr &product, const ModuleLoaderResult::ProductInfo &productInfo)
-{
- ProductDependencyInfos result;
- result.dependencies.reserve(productInfo.usedProducts.size());
- for (const auto &dependency : productInfo.usedProducts) {
- QBS_CHECK(!dependency.name.isEmpty());
- if (dependency.profile == StringConstants::star()) {
- for (const ResolvedProductPtr &p : qAsConst(m_productsByName)) {
- if (p->name != dependency.name || p == product || !p->enabled
- || (dependency.limitToSubProject && !product->isInParentProject(p))) {
- continue;
- }
- result.dependencies.emplace_back(p, dependency.parameters);
- }
- } else {
- ResolvedProductPtr usedProduct = m_productsByName.value(dependency.uniqueName());
- const QString depDisplayName = ResolvedProduct::fullDisplayName(dependency.name,
- dependency.multiplexConfigurationId);
- if (!usedProduct) {
- throw ErrorInfo(Tr::tr("Product '%1' depends on '%2', which does not exist.")
- .arg(product->fullDisplayName(), depDisplayName),
- product->location);
- }
- if (!dependency.profile.isEmpty() && usedProduct->profile() != dependency.profile) {
- usedProduct.reset();
- for (const ResolvedProductPtr &p : qAsConst(m_productsByName)) {
- if (p->name == dependency.name && p->profile() == dependency.profile) {
- usedProduct = p;
- break;
- }
- }
- if (!usedProduct) {
- throw ErrorInfo(Tr::tr("Product '%1' depends on '%2', which does not exist "
- "for the requested profile '%3'.")
- .arg(product->fullDisplayName(), depDisplayName,
- dependency.profile),
- product->location);
- }
- }
- if (!usedProduct->enabled) {
- if (!dependency.isRequired)
- continue;
- ErrorInfo e;
- e.append(Tr::tr("Product '%1' depends on '%2',")
- .arg(product->name, usedProduct->name), product->location);
- e.append(Tr::tr("but product '%1' is disabled.").arg(usedProduct->name),
- usedProduct->location);
- if (m_setupParams.productErrorMode() == ErrorHandlingMode::Strict)
- throw e;
- result.hasDisabledDependency = true;
- }
- result.dependencies.emplace_back(usedProduct, dependency.parameters);
- }
- }
- return result;
-}
-
void ProjectResolver::matchArtifactProperties(const ResolvedProductPtr &product,
const std::vector<SourceArtifactPtr> &artifacts)
{
@@ -1494,14 +1432,26 @@ void ProjectResolver::printProfilingInfo()
class TempScopeSetter
{
public:
- TempScopeSetter(Item * item, Item *newScope) : m_item(item), m_oldScope(item->scope())
+ TempScopeSetter(const ValuePtr &value, Item *newScope) : m_value(value), m_oldScope(value->scope())
{
- item->setScope(newScope);
+ value->setScope(newScope, {});
}
- ~TempScopeSetter() { m_item->setScope(m_oldScope); }
+ ~TempScopeSetter() { if (m_value) m_value->setScope(m_oldScope, {}); }
+
+ TempScopeSetter(const TempScopeSetter &) = delete;
+ TempScopeSetter &operator=(const TempScopeSetter &) = delete;
+ TempScopeSetter &operator=(TempScopeSetter &&) = delete;
+
+ TempScopeSetter(TempScopeSetter &&other) noexcept
+ : m_value(std::move(other.m_value)), m_oldScope(other.m_oldScope)
+ {
+ other.m_value.reset();
+ other.m_oldScope = nullptr;
+ }
+
private:
- Item * const m_item;
- Item * const m_oldScope;
+ ValuePtr m_value;
+ Item *m_oldScope;
};
void ProjectResolver::collectPropertiesForExportItem(Item *productModuleInstance)
@@ -1509,8 +1459,8 @@ void ProjectResolver::collectPropertiesForExportItem(Item *productModuleInstance
if (!productModuleInstance->isPresentModule())
return;
Item * const exportItem = productModuleInstance->prototype();
- QBS_CHECK(exportItem && exportItem->type() == ItemType::Export);
- TempScopeSetter tempScopeSetter(exportItem, productModuleInstance->scope());
+ QBS_CHECK(exportItem);
+ QBS_CHECK(exportItem->type() == ItemType::Export);
const ItemDeclaration::Properties exportDecls = BuiltinDeclarations::instance()
.declarationsForType(ItemType::Export).properties();
ExportedModule &exportedModule = m_productContext->product->exportedModule;
@@ -1526,6 +1476,7 @@ void ProjectResolver::collectPropertiesForExportItem(Item *productModuleInstance
collectPropertiesForExportItem(it.key(), it.value(), productModuleInstance,
exportedModule.modulePropertyValues);
} else {
+ TempScopeSetter tss(it.value(), productModuleInstance);
evaluateProperty(exportItem, it.key(), it.value(), exportedModule.propertyValues,
false);
}
@@ -1538,7 +1489,7 @@ void ProjectResolver::collectPropertiesForModuleInExportItem(const Item::Module
if (!module.item->isPresentModule())
return;
ExportedModule &exportedModule = m_productContext->product->exportedModule;
- if (module.isProduct || module.name.first() == StringConstants::qbsModule())
+ if (module.productInfo || module.name.first() == StringConstants::qbsModule())
return;
const auto checkName = [module](const ExportedModuleDependency &d) {
return module.name.toString() == d.name;
@@ -1551,7 +1502,6 @@ void ProjectResolver::collectPropertiesForModuleInExportItem(const Item::Module
modulePrototype = modulePrototype->prototype();
if (!modulePrototype) // Can happen for broken products in relaxed mode.
return;
- TempScopeSetter tempScopeSetter(modulePrototype, module.item->scope());
const Item::PropertyMap &props = modulePrototype->properties();
ExportedModuleDependency dep;
dep.name = module.name.toString();
@@ -1565,107 +1515,25 @@ void ProjectResolver::collectPropertiesForModuleInExportItem(const Item::Module
collectPropertiesForModuleInExportItem(dep);
}
-static bool hasDependencyCycle(Set<ResolvedProduct *> *checked,
- Set<ResolvedProduct *> *branch,
- const ResolvedProductPtr &product,
- ErrorInfo *error)
-{
- if (branch->contains(product.get()))
- return true;
- if (checked->contains(product.get()))
- return false;
- checked->insert(product.get());
- branch->insert(product.get());
- for (const ResolvedProductPtr &dep : qAsConst(product->dependencies)) {
- if (hasDependencyCycle(checked, branch, dep, error)) {
- error->prepend(dep->name, dep->location);
- return true;
- }
- }
- branch->remove(product.get());
- return false;
-}
-
-using DependencyMap = QHash<ResolvedProduct *, Set<ResolvedProduct *>>;
-void gatherDependencies(ResolvedProduct *product, DependencyMap &dependencies)
-{
- if (dependencies.contains(product))
- return;
- // Hold locally because the QHash references aren't stable in Qt6.
- Set<ResolvedProduct *> productDeps = dependencies[product];
- for (const ResolvedProductPtr &dep : qAsConst(product->dependencies)) {
- productDeps << dep.get();
- gatherDependencies(dep.get(), dependencies);
- productDeps += dependencies.value(dep.get());
- }
- // Now that we gathered the dependencies, put them in the map.
- dependencies[product] = std::move(productDeps);
-}
-
-
-
-static DependencyMap allDependencies(const std::vector<ResolvedProductPtr> &products)
-{
- DependencyMap dependencies;
- for (const ResolvedProductPtr &product : products)
- gatherDependencies(product.get(), dependencies);
- return dependencies;
-}
-
-void ProjectResolver::resolveProductDependencies(const ProjectContext &projectContext)
+void ProjectResolver::resolveProductDependencies()
{
- // Resolve all inter-product dependencies.
- const std::vector<ResolvedProductPtr> allProducts = projectContext.project->allProducts();
- bool disabledDependency = false;
- for (const ResolvedProductPtr &rproduct : allProducts) {
- if (!rproduct->enabled)
- continue;
- Item *productItem = m_productItemMap.value(rproduct);
- const ModuleLoaderResult::ProductInfo &productInfo
- = mapValue(m_loadResult.productInfos, productItem);
- const ProductDependencyInfos &depInfos = getProductDependencies(rproduct, productInfo);
- if (depInfos.hasDisabledDependency)
- disabledDependency = true;
- for (const auto &dep : depInfos.dependencies) {
- if (!contains(rproduct->dependencies, dep.product))
- rproduct->dependencies.push_back(dep.product);
- if (!dep.parameters.empty())
- rproduct->dependencyParameters.insert(dep.product, dep.parameters);
- }
- }
-
- // Check for cyclic dependencies.
- Set<ResolvedProduct *> checked;
- for (const ResolvedProductPtr &rproduct : allProducts) {
- Set<ResolvedProduct *> branch;
- ErrorInfo error;
- if (hasDependencyCycle(&checked, &branch, rproduct, &error)) {
- error.prepend(rproduct->name, rproduct->location);
- error.prepend(Tr::tr("Cyclic dependencies detected."));
- throw error;
- }
- }
-
- // Mark all products as disabled that have a disabled dependency.
- if (disabledDependency && m_setupParams.productErrorMode() == ErrorHandlingMode::Relaxed) {
- const DependencyMap allDeps = allDependencies(allProducts);
- DependencyMap allDepsReversed;
- for (auto it = allDeps.constBegin(); it != allDeps.constEnd(); ++it) {
- for (ResolvedProduct *dep : qAsConst(it.value()))
- allDepsReversed[dep] << it.key();
- }
- for (auto it = allDepsReversed.constBegin(); it != allDepsReversed.constEnd(); ++it) {
- if (it.key()->enabled)
+ for (auto it = m_productsByItem.cbegin(); it != m_productsByItem.cend(); ++it) {
+ const ResolvedProductPtr &product = it.value();
+ for (const Item::Module &module : it.key()->modules()) {
+ if (!module.productInfo)
continue;
- for (ResolvedProduct * const dependingProduct : qAsConst(it.value())) {
- if (dependingProduct->enabled) {
- m_logger.qbsWarning() << Tr::tr("Disabling product '%1', because it depends on "
- "disabled product '%2'.")
- .arg(dependingProduct->name, it.key()->name);
- dependingProduct->enabled = false;
- }
- }
+ const ResolvedProductPtr &dep = m_productsByItem.value(module.productInfo->item);
+ QBS_CHECK(dep);
+ QBS_CHECK(dep != product);
+ it.value()->dependencies << dep;
+ it.value()->dependencyParameters.insert(dep, module.parameters); // TODO: Streamline this with normal module dependencies?
}
+
+ // TODO: We might want to keep the topological sorting and get rid of "module module dependencies".
+ std::sort(product->dependencies.begin(),product->dependencies.end(),
+ [](const ResolvedProductPtr &p1, const ResolvedProductPtr &p2) {
+ return p1->fullDisplayName() < p2->fullDisplayName();
+ });
}
}
@@ -1854,13 +1722,20 @@ void ProjectResolver::collectPropertiesForExportItem(const QualifiedId &moduleNa
{
QBS_CHECK(value->type() == Value::ItemValueType);
Item * const itemValueItem = std::static_pointer_cast<ItemValue>(value)->item();
- if (itemValueItem->type() == ItemType::ModuleInstance) {
+ if (itemValueItem->propertyDeclarations().isEmpty()) {
+ for (const Item::Module &module : moduleInstance->modules()) {
+ if (module.name == moduleName) {
+ itemValueItem->setPropertyDeclarations(module.item->propertyDeclarations());
+ break;
+ }
+ }
+ }
+ if (itemValueItem->type() == ItemType::ModuleInstancePlaceholder) {
struct EvalPreparer {
- EvalPreparer(Item *valueItem, Item *moduleInstance, const QualifiedId &moduleName)
- : valueItem(valueItem), oldScope(valueItem->scope()),
+ EvalPreparer(Item *valueItem, const QualifiedId &moduleName)
+ : valueItem(valueItem),
hadName(!!valueItem->variantProperty(StringConstants::nameProperty()))
{
- valueItem->setScope(moduleInstance);
if (!hadName) {
// Evaluator expects a name here.
valueItem->setProperty(StringConstants::nameProperty(),
@@ -1869,15 +1744,16 @@ void ProjectResolver::collectPropertiesForExportItem(const QualifiedId &moduleNa
}
~EvalPreparer()
{
- valueItem->setScope(oldScope);
if (!hadName)
valueItem->setProperty(StringConstants::nameProperty(), VariantValuePtr());
}
Item * const valueItem;
- Item * const oldScope;
const bool hadName;
};
- EvalPreparer ep(itemValueItem, moduleInstance, moduleName);
+ EvalPreparer ep(itemValueItem, moduleName);
+ std::vector<TempScopeSetter> tss;
+ for (const ValuePtr &v : itemValueItem->properties())
+ tss.emplace_back(v, moduleInstance);
moduleProps.insert(moduleName.toString(), evaluateProperties(itemValueItem, false, false));
return;
}
@@ -1892,12 +1768,10 @@ void ProjectResolver::collectPropertiesForExportItem(const QualifiedId &moduleNa
void ProjectResolver::createProductConfig(ResolvedProduct *product)
{
- EvalCacheEnabler cachingEnabler(m_evaluator);
- m_evaluator->setPathPropertiesBaseDir(m_productContext->product->sourceDirectory);
+ EvalCacheEnabler cachingEnabler(m_evaluator, m_productContext->product->sourceDirectory);
product->moduleProperties->setValue(evaluateModuleValues(m_productContext->item));
product->productProperties = evaluateProperties(m_productContext->item, m_productContext->item,
QVariantMap(), true, true);
- m_evaluator->clearPathPropertiesBaseDir();
}
void ProjectResolver::callItemFunction(const ItemFuncMap &mappings, Item *item,