aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJoerg Bornemann <joerg.bornemann@theqtcompany.com>2015-07-16 12:44:27 +0200
committerJoerg Bornemann <joerg.bornemann@theqtcompany.com>2015-07-20 09:48:50 +0000
commit9d025655d5524433ac8ffbbac0f5ab7aa196d797 (patch)
tree9cd323d97b755afb8ec68ca87c6082fdbfa18465
parentdb0c52d932a62ed4744334e76efc1681aca7a25e (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>
-rw-r--r--src/lib/corelib/language/builtindeclarations.cpp14
-rw-r--r--src/lib/corelib/language/builtindeclarations.h1
-rw-r--r--src/lib/corelib/language/evaluator.h4
-rw-r--r--src/lib/corelib/language/evaluatorscriptclass.cpp72
-rw-r--r--src/lib/corelib/language/item.h3
-rw-r--r--src/lib/corelib/language/itemreader.cpp5
-rw-r--r--src/lib/corelib/language/itemreader.h3
-rw-r--r--src/lib/corelib/language/itemreaderastvisitor.cpp5
-rw-r--r--src/lib/corelib/language/moduleloader.cpp296
-rw-r--r--src/lib/corelib/language/moduleloader.h55
-rw-r--r--src/lib/corelib/language/modulemerger.cpp6
-rw-r--r--src/lib/corelib/language/projectresolver.cpp228
-rw-r--r--src/lib/corelib/language/projectresolver.h7
-rw-r--r--src/lib/corelib/language/testdata/exports.qbs3
-rw-r--r--src/lib/corelib/language/testdata/subdir/exports-mylib.qbs3
-rw-r--r--src/lib/corelib/language/testdata/subdir2/exports-mylib2.qbs13
-rw-r--r--src/lib/corelib/language/tst_language.cpp14
-rw-r--r--src/lib/corelib/language/value.cpp12
-rw-r--r--src/lib/corelib/language/value.h8
-rw-r--r--tests/auto/api/testdata/export-item-with-group/main.cpp1
-rw-r--r--tests/auto/api/testdata/export-item-with-group/project.qbs16
-rw-r--r--tests/auto/api/tst_api.cpp4
-rw-r--r--tests/auto/blackbox/tst_blackbox.cpp19
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 &parameters)
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.