diff options
| author | Joerg Bornemann <joerg.bornemann@digia.com> | 2013-07-18 13:45:57 +0200 |
|---|---|---|
| committer | Christian Kandeler <christian.kandeler@digia.com> | 2013-07-19 10:40:14 +0200 |
| commit | 8a059586cc109f97b2cfc456cb4262009191ce2b (patch) | |
| tree | 8773303ec78580e1225e991374f2cf80b2e7cd8b | |
| parent | e17ce94d5191411f939f9c9599f15db089a20fa1 (diff) | |
| download | qbs-8a059586cc109f97b2cfc456cb4262009191ce2b.tar.gz | |
split overridden values off the build configuration
Property values in profiles now serve as default values for module
values. Property values given on the command line override properties
that are specified in files.
This way it is possible to specify a property value in a profile but
override it in a file or on the command line.
The hierarchy is profile < file < command line.
Change-Id: I786ef5a215312779ed1b79ed64f856b82c612403
Reviewed-by: Christian Kandeler <christian.kandeler@digia.com>
| -rw-r--r-- | src/app/qbs/commandlinefrontend.cpp | 1 | ||||
| -rw-r--r-- | src/lib/language/loader.cpp | 4 | ||||
| -rw-r--r-- | src/lib/language/moduleloader.cpp | 34 | ||||
| -rw-r--r-- | src/lib/language/moduleloader.h | 10 | ||||
| -rw-r--r-- | src/lib/language/projectresolver.cpp | 9 | ||||
| -rw-r--r-- | src/lib/language/projectresolver.h | 3 | ||||
| -rw-r--r-- | src/lib/language/testdata/modules/dummy/dummy.qbs | 1 | ||||
| -rw-r--r-- | src/lib/language/testdata/profilevaluesandoverriddenvalues.qbs | 11 | ||||
| -rw-r--r-- | src/lib/language/tst_language.cpp | 40 | ||||
| -rw-r--r-- | src/lib/language/tst_language.h | 1 | ||||
| -rw-r--r-- | src/lib/tools/setupprojectparameters.cpp | 67 | ||||
| -rw-r--r-- | src/lib/tools/setupprojectparameters.h | 4 |
12 files changed, 149 insertions, 36 deletions
diff --git a/src/app/qbs/commandlinefrontend.cpp b/src/app/qbs/commandlinefrontend.cpp index 95a3b56f2..02768aa21 100644 --- a/src/app/qbs/commandlinefrontend.cpp +++ b/src/app/qbs/commandlinefrontend.cpp @@ -109,6 +109,7 @@ void CommandLineFrontend::start() if (!m_parser.buildBeforeInstalling()) params.setRestoreBehavior(SetupProjectParameters::RestoreOnly); foreach (const QVariantMap &buildConfig, m_parser.buildConfigurations()) { + params.setOverriddenValues(buildConfig); params.setBuildConfiguration(buildConfig); const ErrorInfo err = params.expandBuildConfiguration(m_settings); if (err.hasError()) diff --git a/src/lib/language/loader.cpp b/src/lib/language/loader.cpp index 520db3584..2e3fd90a9 100644 --- a/src/lib/language/loader.cpp +++ b/src/lib/language/loader.cpp @@ -101,10 +101,12 @@ TopLevelProjectPtr Loader::loadProject(const SetupProjectParameters ¶meters) ModuleLoaderResult loadResult = m_moduleLoader->load(parameters.projectFilePath(), + parameters.overriddenValuesTree(), parameters.buildConfigurationTree(), true); const TopLevelProjectPtr project = m_projectResolver->resolve(loadResult, - parameters.buildRoot(), parameters.buildConfigurationTree()); + parameters.buildRoot(), parameters.overriddenValuesTree(), + parameters.buildConfigurationTree()); // E.g. if the top-level project is disabled. if (m_progressObserver) diff --git a/src/lib/language/moduleloader.cpp b/src/lib/language/moduleloader.cpp index fda92d2e8..a2541717c 100644 --- a/src/lib/language/moduleloader.cpp +++ b/src/lib/language/moduleloader.cpp @@ -91,12 +91,14 @@ void ModuleLoader::setSearchPaths(const QStringList &searchPaths) m_moduleSearchPaths += FileInfo::resolvePath(path, moduleSearchSubDir); } -ModuleLoaderResult ModuleLoader::load(const QString &filePath, const QVariantMap &userProperties, - bool wrapWithProjectItem) +ModuleLoaderResult ModuleLoader::load(const QString &filePath, + const QVariantMap &overriddenProperties, const QVariantMap &buildConfigProperties, + bool wrapWithProjectItem) { if (m_logger.traceEnabled()) m_logger.qbsTrace() << "[MODLDR] load" << filePath; - m_userProperties = userProperties; + m_overriddenProperties = overriddenProperties; + m_buildConfigProperties = buildConfigProperties; ModuleLoaderResult result; m_pool = result.itemPool.data(); @@ -132,7 +134,7 @@ void ModuleLoader::handleProject(ModuleLoaderResult *loadResult, Item *item) ProductContext dummyProductContext; dummyProductContext.project = &projectContext; loadBaseModule(&dummyProductContext, item); - overrideItemProperties(item, QLatin1String("project"), m_userProperties); + overrideItemProperties(item, QLatin1String("project"), m_overriddenProperties); foreach (Item *child, item->children()) { child->setScope(projectContext.scope); @@ -547,6 +549,7 @@ Item *ModuleLoader::searchAndLoadModuleFile(ProductContext *productContext, searchPaths.append(m_moduleSearchPaths); bool triedToLoadModule = moduleName.count() > 1; + const QString fullName = fullModuleName(moduleName); foreach (const QString &path, searchPaths) { const QString dirPath = findExistingModulePath(path, moduleName); if (dirPath.isEmpty()) @@ -561,7 +564,7 @@ Item *ModuleLoader::searchAndLoadModuleFile(ProductContext *productContext, } foreach (const QString &filePath, moduleFileNames) { triedToLoadModule = true; - Item *module = loadModuleFile(productContext, + Item *module = loadModuleFile(productContext, fullName, moduleName.count() == 1 && moduleName.first() == QLatin1String("qbs"), filePath); @@ -571,14 +574,14 @@ Item *ModuleLoader::searchAndLoadModuleFile(ProductContext *productContext, } if (Q_UNLIKELY(triedToLoadModule)) - throw ErrorInfo(Tr::tr("Module %1 could not be loaded.").arg(fullModuleName(moduleName)), + throw ErrorInfo(Tr::tr("Module %1 could not be loaded.").arg(fullName), dependsItemLocation); return 0; } -Item *ModuleLoader::loadModuleFile(ProductContext *productContext, bool isBaseModule, - const QString &filePath) +Item *ModuleLoader::loadModuleFile(ProductContext *productContext, const QString &fullModuleName, + bool isBaseModule, const QString &filePath) { checkCancelation(); Item *module = productContext->moduleItemCache.value(filePath); @@ -606,6 +609,17 @@ Item *ModuleLoader::loadModuleFile(ProductContext *productContext, bool isBaseMo return 0; } + // Module properties that are defined in the profile are used as default values. + const QVariantMap profileModuleProperties + = m_buildConfigProperties.value(fullModuleName).toMap(); + for (QVariantMap::const_iterator vmit = profileModuleProperties.begin(); + vmit != profileModuleProperties.end(); ++vmit) + { + if (Q_UNLIKELY(!module->hasProperty(vmit.key()))) + throw ErrorInfo(Tr::tr("Unknown property: %1.%2").arg(fullModuleName, vmit.key())); + module->setProperty(vmit.key(), VariantValue::create(vmit.value())); + } + productContext->moduleItemCache.insert(filePath, module); productContext->project->moduleItemCache.insert(filePath, module); return module; @@ -760,7 +774,7 @@ void ModuleLoader::instantiateModule(ProductContext *productContext, Item *insta } // override module properties given on the command line - const QVariantMap userModuleProperties = m_userProperties.value(fullName).toMap(); + const QVariantMap userModuleProperties = m_overriddenProperties.value(fullName).toMap(); for (QVariantMap::const_iterator vmit = userModuleProperties.begin(); vmit != userModuleProperties.end(); ++vmit) { if (Q_UNLIKELY(!moduleInstance->hasProperty(vmit.key()))) { @@ -840,7 +854,7 @@ void ModuleLoader::checkCancelation() const { if (m_progressObserver && m_progressObserver->canceled()) { throw ErrorInfo(Tr::tr("Project resolving canceled for configuration %1.") - .arg(TopLevelProject::deriveId(m_userProperties))); + .arg(TopLevelProject::deriveId(m_buildConfigProperties))); } } diff --git a/src/lib/language/moduleloader.h b/src/lib/language/moduleloader.h index 2d936a643..6c196c414 100644 --- a/src/lib/language/moduleloader.h +++ b/src/lib/language/moduleloader.h @@ -98,8 +98,9 @@ public: void setSearchPaths(const QStringList &searchPaths); Evaluator *evaluator() const { return m_evaluator; } - ModuleLoaderResult load(const QString &filePath, const QVariantMap &userProperties, - bool wrapWithProjectItem = false); + ModuleLoaderResult load(const QString &filePath, + const QVariantMap &overriddenProperties, const QVariantMap &buildConfigProperties, + bool wrapWithProjectItem = false); static QString fullModuleName(const QStringList &moduleName); static void overrideItemProperties(Item *item, const QString &buildConfigKey, @@ -163,7 +164,7 @@ private: Item *searchAndLoadModuleFile(ProductContext *productContext, const CodeLocation &dependsItemLocation, const QStringList &moduleName, const QStringList &extraSearchPaths); - Item *loadModuleFile(ProductContext *productContext, bool isBaseModule, const QString &filePath); + Item *loadModuleFile(ProductContext *productContext, const QString &fullModuleName, bool isBaseModule, const QString &filePath); void loadBaseModule(ProductContext *productContext, Item *item); void instantiateModule(ProductContext *productContext, Item *instanceScope, Item *moduleInstance, Item *modulePrototype, const QStringList &moduleName); void createChildInstances(ProductContext *productContext, Item *instance, @@ -188,7 +189,8 @@ private: Evaluator *m_evaluator; QStringList m_moduleSearchPaths; QMap<QString, QStringList> m_moduleDirListCache; - QVariantMap m_userProperties; + QVariantMap m_overriddenProperties; + QVariantMap m_buildConfigProperties; }; } // namespace Internal diff --git a/src/lib/language/projectresolver.cpp b/src/lib/language/projectresolver.cpp index 231dab1c2..321877142 100644 --- a/src/lib/language/projectresolver.cpp +++ b/src/lib/language/projectresolver.cpp @@ -79,8 +79,8 @@ void ProjectResolver::setProgressObserver(ProgressObserver *observer) } TopLevelProjectPtr ProjectResolver::resolve(ModuleLoaderResult &loadResult, - const QString &buildRoot, - const QVariantMap &buildConfiguration) + const QString &buildRoot, const QVariantMap &overriddenProperties, + const QVariantMap &buildConfiguration) { QBS_ASSERT(FileInfo::isAbsolute(buildRoot), return TopLevelProjectPtr()); if (m_logger.traceEnabled()) @@ -89,6 +89,7 @@ TopLevelProjectPtr ProjectResolver::resolve(ModuleLoaderResult &loadResult, ProjectContext projectContext; projectContext.loadResult = &loadResult; m_buildRoot = buildRoot; + m_overriddenProperties = overriddenProperties; m_buildConfiguration = buildConfiguration; m_productContext = 0; m_moduleContext = 0; @@ -256,7 +257,7 @@ void ProjectResolver::resolveProduct(Item *item, ProjectContext *projectContext) item->setProperty("name", VariantValue::create(product->name)); } m_logger.qbsTrace() << "[PR] resolveProduct " << product->name; - ModuleLoader::overrideItemProperties(item, product->name, m_buildConfiguration); + ModuleLoader::overrideItemProperties(item, product->name, m_overriddenProperties); m_productsByName.insert(product->name, product); product->enabled = m_evaluator->boolValue(item, QLatin1String("condition"), true); product->additionalFileTags @@ -845,6 +846,8 @@ QVariantMap ProjectResolver::evaluateProperties(Item *item, } case Value::VariantValueType: { + if (result.contains(it.key())) + break; VariantValuePtr vvp = it.value().staticCast<VariantValue>(); result[it.key()] = vvp->value(); break; diff --git a/src/lib/language/projectresolver.h b/src/lib/language/projectresolver.h index b97633e50..d7d3fc4ab 100644 --- a/src/lib/language/projectresolver.h +++ b/src/lib/language/projectresolver.h @@ -57,7 +57,7 @@ public: void setProgressObserver(ProgressObserver *observer); TopLevelProjectPtr resolve(ModuleLoaderResult &loadResult, const QString &buildRoot, - const QVariantMap &buildConfiguration); + const QVariantMap &overriddenProperties, const QVariantMap &buildConfiguration); private: struct ProjectContext @@ -118,6 +118,7 @@ private: ScriptEngine *m_engine; ProgressObserver *m_progressObserver; QString m_buildRoot; + QVariantMap m_overriddenProperties; QVariantMap m_buildConfiguration; ProductContext *m_productContext; ModuleContext *m_moduleContext; diff --git a/src/lib/language/testdata/modules/dummy/dummy.qbs b/src/lib/language/testdata/modules/dummy/dummy.qbs index 55a410ade..031d6425f 100644 --- a/src/lib/language/testdata/modules/dummy/dummy.qbs +++ b/src/lib/language/testdata/modules/dummy/dummy.qbs @@ -3,5 +3,6 @@ import "dummy_base.qbs" as DummyBase DummyBase { property stringList defines property pathList includePaths + property stringList cFlags property stringList cxxFlags } diff --git a/src/lib/language/testdata/profilevaluesandoverriddenvalues.qbs b/src/lib/language/testdata/profilevaluesandoverriddenvalues.qbs new file mode 100644 index 000000000..d3a18bcb5 --- /dev/null +++ b/src/lib/language/testdata/profilevaluesandoverriddenvalues.qbs @@ -0,0 +1,11 @@ +import qbs 1.0 + +Project { + Application { + name: "product1" + Depends { name: "dummy" } + // dummy.cxxFlags is set via profile and is not overridden + dummy.defines: ["IN_FILE"] // set in profile, overridden in file + dummy.cFlags: ["IN_FILE"] // set in profile, overridden on command line + } +} diff --git a/src/lib/language/tst_language.cpp b/src/lib/language/tst_language.cpp index 225892070..15d426eec 100644 --- a/src/lib/language/tst_language.cpp +++ b/src/lib/language/tst_language.cpp @@ -895,6 +895,46 @@ void TestLanguage::pathProperties() QCOMPARE(exceptionCaught, false); } +void TestLanguage::profileValuesAndOverriddenValues() +{ + bool exceptionCaught = false; + try { + SetupProjectParameters parameters = defaultParameters; + QVariantMap buildConfig = parameters.buildConfiguration(); + buildConfig.insert("dummy.defines", "IN_PROFILE"); + buildConfig.insert("dummy.cFlags", "IN_PROFILE"); + buildConfig.insert("dummy.cxxFlags", "IN_PROFILE"); + parameters.setBuildConfiguration(buildConfig); + QVariantMap overriddenValues; + overriddenValues.insert("dummy.cFlags", "OVERRIDDEN"); + parameters.setOverriddenValues(overriddenValues); + parameters.setProjectFilePath(testProject("profilevaluesandoverriddenvalues.qbs")); + project = loader->loadProject(parameters); + QVERIFY(project); + QHash<QString, ResolvedProductPtr> products = productsFromProject(project); + ResolvedProductPtr product = products.value("product1"); + QVERIFY(product); + PropertyFinder pf; + QVariantList values; + values = pf.propertyValues(product->properties->value(), + "dummy", "cxxFlags", PropertyFinder::DoMergeLists); + QCOMPARE(values.length(), 1); + QCOMPARE(values.first().toString(), QString("IN_PROFILE")); + values = pf.propertyValues(product->properties->value(), + "dummy", "defines", PropertyFinder::DoMergeLists); + QCOMPARE(values.length(), 1); + QCOMPARE(values.first().toString(), QString("IN_FILE")); + values = pf.propertyValues(product->properties->value(), + "dummy", "cFlags", PropertyFinder::DoMergeLists); + QCOMPARE(values.length(), 1); + QCOMPARE(values.first().toString(), QString("OVERRIDDEN")); + } catch (const ErrorInfo &e) { + exceptionCaught = true; + qDebug() << e.toString(); + } + QCOMPARE(exceptionCaught, false); +} + void TestLanguage::productConditions() { bool exceptionCaught = false; diff --git a/src/lib/language/tst_language.h b/src/lib/language/tst_language.h index e820c3224..67193308b 100644 --- a/src/lib/language/tst_language.h +++ b/src/lib/language/tst_language.h @@ -92,6 +92,7 @@ private slots: void modules(); void outerInGroup(); void pathProperties(); + void profileValuesAndOverriddenValues(); void productConditions(); void productDirectories(); void propertiesBlocks_data(); diff --git a/src/lib/tools/setupprojectparameters.cpp b/src/lib/tools/setupprojectparameters.cpp index a0d8df6c8..34e8a2268 100644 --- a/src/lib/tools/setupprojectparameters.cpp +++ b/src/lib/tools/setupprojectparameters.cpp @@ -58,7 +58,9 @@ public: QString buildRoot; QStringList searchPaths; QStringList pluginPaths; + QVariantMap overriddenValues; QVariantMap buildConfiguration; + mutable QVariantMap overriddenValuesTree; mutable QVariantMap buildConfigurationTree; bool ignoreDifferentProjectFilePath; bool dryRun; @@ -162,6 +164,52 @@ void SetupProjectParameters::setPluginPaths(const QStringList &pluginPaths) } /*! + * Returns the overridden values of the build configuration. + */ +QVariantMap SetupProjectParameters::overriddenValues() const +{ + return d->overriddenValues; +} + +/*! + * Set the overridden values of the build configuration. + */ +void SetupProjectParameters::setOverriddenValues(const QVariantMap &values) +{ + // warn if somebody tries to set a build configuration tree: + for (QVariantMap::const_iterator i = values.constBegin(); + i != values.constEnd(); ++i) { + QBS_ASSERT(i.value().type() != QVariant::Map, return); + } + d->overriddenValues = values; + d->overriddenValuesTree.clear(); +} + +static void provideValuesTree(const QVariantMap &values, QVariantMap *valueTree) +{ + if (!valueTree->isEmpty() || values.isEmpty()) + return; + + valueTree->clear(); + for (QVariantMap::const_iterator it = values.constBegin(); it != values.constEnd(); ++it) { + QStringList nameElements = it.key().split(QLatin1Char('.')); + if (nameElements.count() > 2) { // ### workaround for submodules being represented internally as a single module of name "module/submodule" rather than two nested modules "module" and "submodule" + const QStringList allButLast = nameElements.mid(0, nameElements.length() - 1); + QStringList newElements(allButLast.join(QLatin1String("/"))); + newElements.append(nameElements.last()); + nameElements = newElements; + } + Internal::setConfigProperty(*valueTree, nameElements, it.value()); + } +} + +QVariantMap SetupProjectParameters::overriddenValuesTree() const +{ + provideValuesTree(d->overriddenValues, &d->overriddenValuesTree); + return d->overriddenValuesTree; +} + +/*! * \brief The collection of properties to use for resolving the project. */ QVariantMap SetupProjectParameters::buildConfiguration() const @@ -192,23 +240,8 @@ void SetupProjectParameters::setBuildConfiguration(const QVariantMap &buildConfi */ QVariantMap SetupProjectParameters::buildConfigurationTree() const { - if (!d->buildConfigurationTree.isEmpty() && !d->buildConfiguration.isEmpty()) - return d->buildConfigurationTree; - - QVariantMap result; - foreach (const QString &property, d->buildConfiguration.keys()) { - QStringList nameElements = property.split('.'); - if (nameElements.count() > 2) { // ### workaround for submodules being represented internally as a single module of name "module/submodule" rather than two nested modules "module" and "submodule" - QStringList allButLast = nameElements; - allButLast.removeLast(); - QStringList newElements(allButLast.join("/")); - newElements.append(nameElements.last()); - nameElements = newElements; - } - Internal::setConfigProperty(result, nameElements, d->buildConfiguration.value(property)); - } - d->buildConfigurationTree = result; - return result; + provideValuesTree(d->buildConfiguration, &d->buildConfigurationTree); + return d->buildConfigurationTree; } /*! diff --git a/src/lib/tools/setupprojectparameters.h b/src/lib/tools/setupprojectparameters.h index c262930f6..77e09ef9a 100644 --- a/src/lib/tools/setupprojectparameters.h +++ b/src/lib/tools/setupprojectparameters.h @@ -65,6 +65,10 @@ public: QStringList pluginPaths() const; void setPluginPaths(const QStringList &pluginPaths); + QVariantMap overriddenValues() const; + void setOverriddenValues(const QVariantMap &values); + QVariantMap overriddenValuesTree() const; + QVariantMap buildConfiguration() const; void setBuildConfiguration(const QVariantMap &buildConfiguration); QVariantMap buildConfigurationTree() const; |
