diff options
29 files changed, 195 insertions, 94 deletions
diff --git a/changelogs/changes-3.0.0.md b/changelogs/changes-3.0.0.md index c718c110b..96cefeee1 100644 --- a/changelogs/changes-3.0.0.md +++ b/changelogs/changes-3.0.0.md @@ -6,6 +6,9 @@ * The JavaScript backend was switched to `QuickJS-NG`, which is actively maintained. # Language +* Introduced new property `minimal` to `Depends` item that controls whether the + dependency should get built in its entirety or only as far as needed by + the rules in the depending product. * Relative paths in `Export` items are now resolved relative to the importing product. * Top-level list property assignments no longer act as fallbacks for `Properties` items, but unconditionally contribute to the aggregate value of the property. diff --git a/doc/reference/items/language/depends.qdoc b/doc/reference/items/language/depends.qdoc index 937828b8c..b3620aba4 100644 --- a/doc/reference/items/language/depends.qdoc +++ b/doc/reference/items/language/depends.qdoc @@ -157,6 +157,20 @@ */ /*! + \qmlproperty bool Depends::minimal + + Setting this property to \c false instructs \QBS to build the respective product + only to the extent that is necessary for the depending product. + Otherwise (the default), the dependency gets fully built. + This property only has an effect if the user requested a subset of the project + to be built (that is, the -p option is used), the dependency is not in that subset + and no other product in the subset has a non-minimal dependency on it. + + \defaultvalue \c false + \since Qbs 3.0 +*/ + +/*! \qmlproperty string Depends::name The name of the dependent product or module. diff --git a/share/qbs/imports/qbs/base/QtLupdateRunner.qbs b/share/qbs/imports/qbs/base/QtLupdateRunner.qbs index 54f6559c4..32e0f6179 100644 --- a/share/qbs/imports/qbs/base/QtLupdateRunner.qbs +++ b/share/qbs/imports/qbs/base/QtLupdateRunner.qbs @@ -41,7 +41,7 @@ Product { property stringList extraArguments Depends { name: "Qt.core" } - Depends { productTypes: "qm"; limitToSubProject: product.limitToSubProject } + Depends { productTypes: "qm"; minimal: true; limitToSubProject: product.limitToSubProject } Rule { multiplex: true diff --git a/src/lib/corelib/api/project.cpp b/src/lib/corelib/api/project.cpp index d884f4f28..658e04814 100644 --- a/src/lib/corelib/api/project.cpp +++ b/src/lib/corelib/api/project.cpp @@ -685,10 +685,8 @@ void ProjectPrivate::retrieveProjectData(ProjectData &projectData, product.d->generatedArtifacts << ta; } } - for (const ResolvedProductPtr &resolvedDependentProduct - : std::as_const(resolvedProduct->dependencies)) { - product.d->dependencies << resolvedDependentProduct->fullDisplayName(); - } + for (const ProductDependency &dep : std::as_const(resolvedProduct->dependencies)) + product.d->dependencies << dep.product->fullDisplayName(); std::sort(product.d->type.begin(), product.d->type.end()); std::sort(product.d->groups.begin(), product.d->groups.end()); std::sort(product.d->generatedArtifacts.begin(), product.d->generatedArtifacts.end()); diff --git a/src/lib/corelib/buildgraph/buildgraph.cpp b/src/lib/corelib/buildgraph/buildgraph.cpp index d8a7cfa64..19b204707 100644 --- a/src/lib/corelib/buildgraph/buildgraph.cpp +++ b/src/lib/corelib/buildgraph/buildgraph.cpp @@ -52,13 +52,10 @@ #include <language/artifactproperties.h> #include <language/language.h> #include <language/preparescriptobserver.h> -#include <language/propertymapinternal.h> -#include <language/resolvedfilecontext.h> +#include <language/property.h> #include <language/scriptengine.h> #include <logging/categories.h> #include <logging/logger.h> -#include <language/property.h> -#include <logging/translator.h> #include <tools/error.h> #include <tools/fileinfo.h> #include <tools/qbsassert.h> @@ -281,7 +278,7 @@ private: std::back_inserter(productDeps), getProductForName); } } else { - productDeps = product->dependencies; + productDeps = product->depsAsProductList(); } for (const ResolvedProductPtr &dependency : std::as_const(productDeps)) { setupBaseProductScriptValue(engine, dependency.get()); diff --git a/src/lib/corelib/buildgraph/buildgraphloader.cpp b/src/lib/corelib/buildgraph/buildgraphloader.cpp index bef68fb77..fbd077271 100644 --- a/src/lib/corelib/buildgraph/buildgraphloader.cpp +++ b/src/lib/corelib/buildgraph/buildgraphloader.cpp @@ -42,16 +42,12 @@ #include "emptydirectoriesremover.h" #include "productbuilddata.h" #include "projectbuilddata.h" -#include "rulenode.h" #include "rulecommands.h" +#include "rulenode.h" +#include "rulesevaluationcontext.h" #include "transformer.h" -#include <buildgraph/rulesevaluationcontext.h> -#include <language/artifactproperties.h> #include <language/language.h> -#include <language/propertymapinternal.h> -#include <language/qualifiedid.h> -#include <language/resolvedfilecontext.h> #include <loader/projectresolver.h> #include <logging/categories.h> #include <logging/translator.h> @@ -74,6 +70,7 @@ #include <functional> #include <memory> #include <unordered_map> +#include <utility> namespace qbs { namespace Internal { @@ -247,8 +244,8 @@ static bool checkProductForChangedDependency(std::vector<ResolvedProductPtr> &ch return false; if (contains(changedProducts, product)) return true; - for (const ResolvedProductPtr &dep : std::as_const(product->dependencies)) { - if (checkProductForChangedDependency(changedProducts, seenProducts, dep)) { + for (const ProductDependency &dep : std::as_const(product->dependencies)) { + if (checkProductForChangedDependency(changedProducts, seenProducts, dep.product)) { changedProducts << product; return true; } @@ -749,13 +746,13 @@ static bool dependenciesAreEqual(const ResolvedProductConstPtr &p1, { if (p1->dependencies.size() != p2->dependencies.size()) return false; - Set<QString> names1; - Set<QString> names2; + Set<std::pair<QString, bool>> deps1; + Set<std::pair<QString, bool>> deps2; for (const auto &dep : std::as_const(p1->dependencies)) - names1 << dep->uniqueName(); + deps1 << std::make_pair(dep.product->uniqueName(), dep.minimal); for (const auto &dep : std::as_const(p2->dependencies)) - names2 << dep->uniqueName(); - return names1 == names2; + deps2 << std::make_pair(dep.product->uniqueName(), dep.minimal); + return deps1 == deps2; } bool BuildGraphLoader::checkProductForChanges(const ResolvedProductPtr &restoredProduct, diff --git a/src/lib/corelib/buildgraph/depscanner.cpp b/src/lib/corelib/buildgraph/depscanner.cpp index dc2a43ed9..15584c347 100644 --- a/src/lib/corelib/buildgraph/depscanner.cpp +++ b/src/lib/corelib/buildgraph/depscanner.cpp @@ -121,7 +121,7 @@ QStringList PluginDependencyScanner::collectModulesPaths(const ResolvedProduct * result << getModulesPath(product); // a module can also be in the dependent product for (const auto &dep : product->dependencies) { - const auto depProduct = dep.get(); + const auto depProduct = dep.product.get(); if (depProduct) result << getModulesPath(depProduct); } diff --git a/src/lib/corelib/buildgraph/executor.cpp b/src/lib/corelib/buildgraph/executor.cpp index 53750b56d..2955810bb 100644 --- a/src/lib/corelib/buildgraph/executor.cpp +++ b/src/lib/corelib/buildgraph/executor.cpp @@ -55,7 +55,6 @@ #include <buildgraph/transformer.h> #include <language/language.h> -#include <language/propertymapinternal.h> #include <language/scriptengine.h> #include <logging/categories.h> #include <logging/translator.h> @@ -65,12 +64,12 @@ #include <tools/profiling.h> #include <tools/progressobserver.h> #include <tools/qbsassert.h> -#include <tools/qttools.h> #include <tools/settings.h> #include <tools/stlutils.h> #include <tools/stringconstants.h> #include <QtCore/qdir.h> +#include <QtCore/qhash.h> #include <QtCore/qtimer.h> #include <algorithm> @@ -180,12 +179,27 @@ void Executor::setupAuxiliaryProducts() if (int(m_primaryProducts.size()) == int(m_allProducts.size())) return; - for (int i = 0; i < m_buildableProducts.size(); ++i) { - const ResolvedProductPtr product = m_buildableProducts.at(i); - for (const ResolvedProductPtr &dependency : std::as_const(product->dependencies)) { - if (!contains(m_buildableProducts, dependency)) - m_buildableProducts.push_back(dependency); - } + // Collect dependencies. Products that are not minimal dependencies via *all* dependency chains + // become primary products, i.e. they will get fully built. + QHash<ResolvedProductPtr, bool> dependencies; + const std::function<void(const ResolvedProductPtr &, bool)> addDependency = + [&](const ResolvedProductPtr &p, bool minimal) { + auto &buildFully = dependencies[p]; + if (!minimal) + buildFully = true; + for (const ProductDependency &dep : std::as_const(p->dependencies)) + addDependency(dep.product, minimal || dep.minimal); + }; + + for (const ResolvedProductPtr &primary : std::as_const(m_primaryProducts)) { + for (const ProductDependency &dep : std::as_const(primary->dependencies)) + addDependency(dep.product, dep.minimal); + } + + for (auto it = dependencies.cbegin(); it != dependencies.cend(); ++it) { + m_buildableProducts << it.key(); + if (it.value()) + m_primaryProducts << it.key(); } } @@ -204,8 +218,8 @@ public: { Set<ResolvedProductPtr> allDependencies; for (const ResolvedProductPtr &product : m_allProducts) { - for (const ResolvedProductPtr &dep : product->dependencies) - allDependencies += dep; + for (const ProductDependency &dep : product->dependencies) + allDependencies += dep.product; } const Set<ResolvedProductPtr> rootProducts = rangeTo<Set<ResolvedProductPtr>>(m_allProducts) - allDependencies; @@ -220,8 +234,8 @@ private: { if (!m_seenProducts.insert(product).second) return; - for (const ResolvedProductPtr &dependency : std::as_const(product->dependencies)) - traverse(dependency); + for (const ProductDependency &dependency : std::as_const(product->dependencies)) + traverse(dependency.product); if (!product->buildData) return; product->buildData->setBuildPriority(m_priority--); diff --git a/src/lib/corelib/buildgraph/inputartifactscanner.cpp b/src/lib/corelib/buildgraph/inputartifactscanner.cpp index c573d3ea4..d2e35b448 100644 --- a/src/lib/corelib/buildgraph/inputartifactscanner.cpp +++ b/src/lib/corelib/buildgraph/inputartifactscanner.cpp @@ -92,8 +92,8 @@ static void resolveDepencency(const RawScannedDependency &dependency, dependencyInProduct = foundArtifact; } else if (!productOfDependencyIsDependency) { dependencyInOtherProduct = foundArtifact; - productOfDependencyIsDependency - = contains(product->dependencies, dependencyInOtherProduct->product.lock()); + productOfDependencyIsDependency = product->hasDependency( + dependencyInOtherProduct->product.lock()); } break; } diff --git a/src/lib/corelib/buildgraph/projectbuilddata.cpp b/src/lib/corelib/buildgraph/projectbuilddata.cpp index e89000528..3f2f3dd78 100644 --- a/src/lib/corelib/buildgraph/projectbuilddata.cpp +++ b/src/lib/corelib/buildgraph/projectbuilddata.cpp @@ -48,7 +48,6 @@ #include "rulesevaluationcontext.h" #include "transformer.h" -#include <language/artifactproperties.h> #include <language/language.h> #include <language/preparescriptobserver.h> #include <language/scriptengine.h> @@ -57,7 +56,6 @@ #include <tools/error.h> #include <tools/fileinfo.h> #include <tools/qbsassert.h> -#include <tools/qttools.h> #include <tools/stlutils.h> #include <memory> @@ -69,7 +67,7 @@ static Set<ResolvedProductPtr> findDependentProducts(const ResolvedProductPtr &p { Set<ResolvedProductPtr> result; for (const ResolvedProductPtr &parent : product->topLevelProject()->allProducts()) { - if (contains(parent->dependencies, product)) + if (parent->hasDependency(product)) result += parent; } return result; @@ -394,11 +392,11 @@ void BuildDataResolver::resolveProductBuildData(const ResolvedProductPtr &produc ArtifactSetByFileTag artifactsPerFileTag; for (const auto &dependency : std::as_const(product->dependencies)) { - if (!dependency->enabled) { + if (!dependency.product->enabled) { QBS_CHECK(m_parameters.productErrorMode() == ErrorHandlingMode::Relaxed); continue; } - resolveProductBuildData(dependency); + resolveProductBuildData(dependency.product); } //add qbsFile artifact @@ -429,7 +427,7 @@ void BuildDataResolver::resolveProductBuildData(const ResolvedProductPtr &produc CreateRuleNodes crn(product); ruleGraph.accept(&crn); - connectRulesToDependencies(product, product->dependencies); + connectRulesToDependencies(product, product->depsAsProductList()); } static bool isRootRuleNode(RuleNode *ruleNode) diff --git a/src/lib/corelib/buildgraph/requesteddependencies.cpp b/src/lib/corelib/buildgraph/requesteddependencies.cpp index b95c8db94..3b166423b 100644 --- a/src/lib/corelib/buildgraph/requesteddependencies.cpp +++ b/src/lib/corelib/buildgraph/requesteddependencies.cpp @@ -49,7 +49,7 @@ static Set<QString> depNamesForProduct(const ResolvedProduct *p) { Set<QString> names; for (const auto &dep : p->dependencies) - names.insert(dep->uniqueName()); + names.insert(dep.product->uniqueName()); for (const auto &m : p->modules) { if (!m->isProduct) names.insert(m->name); diff --git a/src/lib/corelib/buildgraph/rulenode.cpp b/src/lib/corelib/buildgraph/rulenode.cpp index 1a3bc52f6..bf69f3526 100644 --- a/src/lib/corelib/buildgraph/rulenode.cpp +++ b/src/lib/corelib/buildgraph/rulenode.cpp @@ -261,9 +261,9 @@ ArtifactSet RuleNode::currentInputArtifacts() const } for (const auto &dep : std::as_const(product->dependencies)) { - if (!dep->buildData) + if (!dep.product->buildData) continue; - for (Artifact * const a : filterByType<Artifact>(dep->buildData->allNodes())) { + for (Artifact * const a : filterByType<Artifact>(dep.product->buildData->allNodes())) { if (a->fileTags().intersects(m_rule->inputsFromDependencies) && !a->fileTags().intersects(m_rule->excludedInputs)) s += a; diff --git a/src/lib/corelib/buildgraph/rulesapplicator.cpp b/src/lib/corelib/buildgraph/rulesapplicator.cpp index 9daf9f037..2a549cf1a 100644 --- a/src/lib/corelib/buildgraph/rulesapplicator.cpp +++ b/src/lib/corelib/buildgraph/rulesapplicator.cpp @@ -358,7 +358,7 @@ ArtifactSet RulesApplicator::collectAdditionalInputs(const FileTags &tags, const if (inputsSources == Dependencies) { for (const auto &depProduct : product->dependencies) { for (Artifact * const ta : - filterByType<Artifact>(depProduct->buildData->allNodes())) { + filterByType<Artifact>(depProduct.product->buildData->allNodes())) { if (ta->fileTags().contains(fileTag) && !ta->fileTags().intersects(rule->excludedInputs)) { artifacts << ta; diff --git a/src/lib/corelib/language/builtindeclarations.cpp b/src/lib/corelib/language/builtindeclarations.cpp index 27199dd16..5bdc04448 100644 --- a/src/lib/corelib/language/builtindeclarations.cpp +++ b/src/lib/corelib/language/builtindeclarations.cpp @@ -235,6 +235,10 @@ void BuiltinDeclarations::addDependsItem() item << nameProperty(); item << PropertyDeclaration(StringConstants::submodulesProperty(), PropertyDeclaration::StringList); + item << PropertyDeclaration( + StringConstants::minimalProperty(), + PropertyDeclaration::Boolean, + StringConstants::falseValue()); item << PropertyDeclaration(StringConstants::requiredProperty(), PropertyDeclaration::Boolean, StringConstants::trueValue()); item << PropertyDeclaration(StringConstants::versionAtLeastProperty(), diff --git a/src/lib/corelib/language/item.h b/src/lib/corelib/language/item.h index 4917cb236..5e03fc343 100644 --- a/src/lib/corelib/language/item.h +++ b/src/lib/corelib/language/item.h @@ -125,6 +125,7 @@ public: int maxDependsChainLength = 0; bool required = true; + bool minimal = false; }; using Modules = std::vector<Module>; using PropertyDeclarationMap = QMap<QString, PropertyDeclaration>; diff --git a/src/lib/corelib/language/language.cpp b/src/lib/corelib/language/language.cpp index 0644bf3c4..b7dab58ed 100644 --- a/src/lib/corelib/language/language.cpp +++ b/src/lib/corelib/language/language.cpp @@ -48,17 +48,15 @@ #include <buildgraph/buildgraph.h> #include <buildgraph/productbuilddata.h> #include <buildgraph/projectbuilddata.h> -#include <buildgraph/rulegraph.h> // TODO: Move to language? #include <buildgraph/transformer.h> #include <jsextensions/jsextensions.h> #include <language/value.h> #include <loader/loaderutils.h> #include <logging/categories.h> -#include <logging/translator.h> #include <tools/buildgraphlocker.h> -#include <tools/hostosinfo.h> #include <tools/error.h> #include <tools/fileinfo.h> +#include <tools/hostosinfo.h> #include <tools/qbsassert.h> #include <tools/qttools.h> #include <tools/scripttools.h> @@ -498,6 +496,17 @@ QString ResolvedProduct::cachedExecutablePath(const QString &origFilePath) const return m_executablePathCache.value(origFilePath); } +std::vector<ResolvedProductPtr> ResolvedProduct::depsAsProductList() const +{ + return transformed<std::vector<ResolvedProductPtr>>( + dependencies, [](const ProductDependency &dep) { return dep.product; }); +} + +bool ResolvedProduct::hasDependency(const ResolvedProductPtr &p) const +{ + return any_of(dependencies, [&p](const ProductDependency &dep) { return dep.product == p; }); +} + void ResolvedGroup::restoreWildcards(const QString &buildDir) { if (!files) diff --git a/src/lib/corelib/language/language.h b/src/lib/corelib/language/language.h index f15059efe..ed021da2a 100644 --- a/src/lib/corelib/language/language.h +++ b/src/lib/corelib/language/language.h @@ -583,6 +583,28 @@ inline bool operator!=(const ExportedModule &m1, const ExportedModule &m2) { ret class TopLevelProject; class ScriptEngine; +struct ProductDependency +{ + ProductDependency(const ResolvedProductPtr &p, bool minimal) + : product(p) + , minimal(minimal) + {} + ProductDependency() = default; + + template<PersistentPool::OpType opType> + void completeSerializationOp(PersistentPool &pool) + { + pool.serializationOp<opType>(product, minimal); + } + + ResolvedProductPtr product; + bool minimal = false; +}; +inline bool operator==(const ProductDependency &dep1, const ProductDependency &dep2) +{ + return dep1.product == dep2.product && dep1.minimal == dep2.minimal; +} + class QBS_AUTOTEST_EXPORT ResolvedProduct { public: @@ -602,7 +624,7 @@ public: QVariantMap productProperties; PropertyMapPtr moduleProperties; std::vector<RulePtr> rules; - std::vector<ResolvedProductPtr> dependencies; + std::vector<ProductDependency> dependencies; QHash<ResolvedProductConstPtr, QVariantMap> dependencyParameters; std::vector<FileTaggerConstPtr> fileTaggers; JobLimits jobLimits; @@ -649,6 +671,9 @@ public: void cacheExecutablePath(const QString &origFilePath, const QString &fullFilePath); QString cachedExecutablePath(const QString &origFilePath) const; + std::vector<ResolvedProductPtr> depsAsProductList() const; + bool hasDependency(const ResolvedProductPtr &p) const; + void load(PersistentPool &pool); void store(PersistentPool &pool); diff --git a/src/lib/corelib/loader/dependenciesresolver.cpp b/src/lib/corelib/loader/dependenciesresolver.cpp index d9ecbabea..ec23761d1 100644 --- a/src/lib/corelib/loader/dependenciesresolver.cpp +++ b/src/lib/corelib/loader/dependenciesresolver.cpp @@ -88,6 +88,7 @@ public: VersionRange versionRange; QVariantMap parameters; bool limitToSubProject = false; + bool minimal = false; bool requiredLocally = true; bool requiredGlobally = true; }; @@ -121,6 +122,7 @@ public: VersionRange versionRange; QVariantMap parameters; bool limitToSubProject = false; + bool minimal = false; bool requiredLocally = true; bool requiredGlobally = true; bool checkProduct = true; @@ -145,7 +147,7 @@ public: std::list<DependenciesResolvingState> stateStack; private: - std::pair<ProductDependency, ProductContext *> pendingDependency() const override; + std::pair<ProductDependencyType, ProductContext *> pendingDependency() const override; void setSearchPathsForProduct(LoaderState &loaderState); @@ -505,6 +507,7 @@ LoadModuleResult DependenciesResolver::loadModule( if (m_product.item) { Item::Module module = createModule(dependency, moduleItem, productDep); module.required = dependency.requiredGlobally; + module.minimal = dependency.minimal; addLoadContext(module); module.maxDependsChainLength = dependsChainLength(); m_product.item->addModule(module); @@ -544,6 +547,7 @@ void DependenciesResolver::updateModule( forwardParameterDeclarations(dependency.item, m_product.item->modules()); module.versionRange.narrowDown(dependency.versionRange); module.required |= dependency.requiredGlobally; + module.minimal &= dependency.minimal; if (dependsChainLength() > module.maxDependsChainLength) module.maxDependsChainLength = dependsChainLength(); } @@ -878,21 +882,23 @@ std::optional<EvaluatedDependsItem> DependenciesResolver::evaluateDependsItem(It adjustParametersScopes(item, item); forwardParameterDeclarations(item, m_product.item->modules()); const QVariantMap parameters = extractParameters(item); + const bool minimal = evaluator.boolValue(item, StringConstants::minimalProperty()); const FileTags productTypeTags = FileTags::fromStringList(productTypes); if (!productTypeTags.empty()) m_product.bulkDependencies.emplace_back(productTypeTags, item->location()); return EvaluatedDependsItem{ - item, - QualifiedId::fromString(name), - submodules, - productTypeTags, - multiplexIds, - profiles, - {minVersion, maxVersion}, - parameters, - limitToSubProject, - required}; + .item = item, + .name = QualifiedId::fromString(name), + .subModules = submodules, + .productTypes = productTypeTags, + .multiplexIds = multiplexIds, + .profiles = profiles, + .versionRange{minVersion, maxVersion}, + .parameters = parameters, + .limitToSubProject = limitToSubProject, + .minimal = minimal, + .requiredLocally = required}; } // Potentially multiplexes a dependency along Depends.productTypes, Depends.subModules and @@ -1039,6 +1045,7 @@ Item::Module DependenciesResolver::createModule( m.item = item; m.product = productDep; m.name = dependency.name; + m.minimal = dependency.minimal; m.required = dependency.requiredLocally; m.versionRange = dependency.versionRange; return m; @@ -1051,6 +1058,7 @@ FullyResolvedDependsItem::FullyResolvedDependsItem( , name(product->name) , versionRange(dependency.versionRange) , parameters(dependency.parameters) + , minimal(dependency.minimal) , checkProduct(false) {} @@ -1070,6 +1078,7 @@ FullyResolvedDependsItem::FullyResolvedDependsItem( , versionRange(dependency.versionRange) , parameters(dependency.parameters) , limitToSubProject(dependency.limitToSubProject) + , minimal(dependency.minimal) , requiredLocally(dependency.requiredLocally) , requiredGlobally(dependency.requiredGlobally) {} @@ -1145,14 +1154,15 @@ DependenciesContextImpl::DependenciesContextImpl(ProductContext &product, Loader FullyResolvedDependsItem::makeBaseDependency()); } -std::pair<ProductDependency, ProductContext *> DependenciesContextImpl::pendingDependency() const +std::pair<ProductDependencyType, ProductContext *> DependenciesContextImpl::pendingDependency() + const { QBS_CHECK(!stateStack.empty()); if (const auto ¤tDependsItem = stateStack.front().currentDependsItem; currentDependsItem && !currentDependsItem->productTypes.empty()) { qCDebug(lcLoaderScheduling) << "product" << m_product.displayName() << "to be delayed because of bulk dependency"; - return {ProductDependency::Bulk, nullptr}; + return {ProductDependencyType::Bulk, nullptr}; } if (!stateStack.front().pendingResolvedDependencies.empty()) { if (ProductContext * const dep = stateStack.front().pendingResolvedDependencies @@ -1161,17 +1171,17 @@ std::pair<ProductDependency, ProductContext *> DependenciesContextImpl::pendingD qCDebug(lcLoaderScheduling) << "product" << m_product.displayName() << "to be delayed because of dependency " "to unfinished product" << dep->displayName(); - return {ProductDependency::Single, dep}; + return {ProductDependencyType::Single, dep}; } else { qCDebug(lcLoaderScheduling) << "product" << m_product.displayName() << "to be re-scheduled, as dependency " << dep->displayName() << "appears to have finished in the meantime"; - return {ProductDependency::None, dep}; + return {ProductDependencyType::None, dep}; } } } - return {ProductDependency::None, nullptr}; + return {ProductDependencyType::None, nullptr}; } void DependenciesContextImpl::setSearchPathsForProduct(LoaderState &loaderState) diff --git a/src/lib/corelib/loader/loaderutils.cpp b/src/lib/corelib/loader/loaderutils.cpp index 3651060cf..77f238f67 100644 --- a/src/lib/corelib/loader/loaderutils.cpp +++ b/src/lib/corelib/loader/loaderutils.cpp @@ -822,10 +822,10 @@ bool ProductContext::dependenciesResolvingPending() const && !product && !delayedError.hasError(); } -std::pair<ProductDependency, ProductContext *> ProductContext::pendingDependency() const +std::pair<ProductDependencyType, ProductContext *> ProductContext::pendingDependency() const { return dependenciesContext ? dependenciesContext->pendingDependency() - : std::make_pair(ProductDependency::None, nullptr); + : std::make_pair(ProductDependencyType::None, nullptr); } TimingData &TimingData::operator+=(const TimingData &other) diff --git a/src/lib/corelib/loader/loaderutils.h b/src/lib/corelib/loader/loaderutils.h index 4a4aadfb8..9f9aeedc9 100644 --- a/src/lib/corelib/loader/loaderutils.h +++ b/src/lib/corelib/loader/loaderutils.h @@ -84,7 +84,7 @@ using ModulePropertiesPerGroup = std::unordered_map<const Item *, QualifiedIdSet using FileLocations = QHash<std::pair<QString, QString>, CodeLocation>; enum class Deferral { Allowed, NotAllowed }; -enum class ProductDependency { None, Single, Bulk }; +enum class ProductDependencyType { None, Single, Bulk }; class CancelException { }; @@ -146,7 +146,7 @@ class DependenciesContext { public: virtual ~DependenciesContext(); - virtual std::pair<ProductDependency, ProductContext *> pendingDependency() const = 0; + virtual std::pair<ProductDependencyType, ProductContext *> pendingDependency() const = 0; bool dependenciesResolved = false; }; @@ -158,7 +158,7 @@ public: QString displayName() const; void handleError(const ErrorInfo &error); bool dependenciesResolvingPending() const; - std::pair<ProductDependency, ProductContext *> pendingDependency() const; + std::pair<ProductDependencyType, ProductContext *> pendingDependency() const; QString name; QString buildDirectory; diff --git a/src/lib/corelib/loader/productresolver.cpp b/src/lib/corelib/loader/productresolver.cpp index 376731593..273efaaf3 100644 --- a/src/lib/corelib/loader/productresolver.cpp +++ b/src/lib/corelib/loader/productresolver.cpp @@ -1264,15 +1264,17 @@ void ProductResolverStage2::collectProductDependencies() const ResolvedProductPtr &dep = module.product->product; QBS_CHECK(dep); QBS_CHECK(dep != product); - product->dependencies << dep; + product->dependencies.emplace_back(dep, module.minimal); product->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(); - }); + std::sort( + product->dependencies.begin(), + product->dependencies.end(), + [](const ProductDependency &p1, const ProductDependency &p2) { + return p1.product->fullDisplayName() < p2.product->fullDisplayName(); + }); } void ExportsResolver::start() diff --git a/src/lib/corelib/loader/productsresolver.cpp b/src/lib/corelib/loader/productsresolver.cpp index b72736d75..33e163fcc 100644 --- a/src/lib/corelib/loader/productsresolver.cpp +++ b/src/lib/corelib/loader/productsresolver.cpp @@ -453,14 +453,14 @@ void ProductsResolver::handleFinishedThreads() << product.displayName(); const auto pending = product.pendingDependency(); switch (pending.first) { - case ProductDependency::Single: + case ProductDependencyType::Single: waitForSingleDependency(ProductWithLoaderState(product, &loaderState), *pending.second); break; - case ProductDependency::Bulk: + case ProductDependencyType::Bulk: waitForBulkDependency(ProductWithLoaderState(product, &loaderState)); break; - case ProductDependency::None: + case ProductDependencyType::None: // This can happen if the dependency has finished in between the check in // DependencyResolver and the one here. QBS_CHECK(pending.second); diff --git a/src/lib/corelib/tools/persistence.cpp b/src/lib/corelib/tools/persistence.cpp index 15550aec5..de504e1d3 100644 --- a/src/lib/corelib/tools/persistence.cpp +++ b/src/lib/corelib/tools/persistence.cpp @@ -48,7 +48,7 @@ namespace qbs { namespace Internal { -static const char QBS_PERSISTENCE_MAGIC[] = "QBSPERSISTENCE-137"; +static const char QBS_PERSISTENCE_MAGIC[] = "QBSPERSISTENCE-138"; NoBuildGraphError::NoBuildGraphError(const QString &filePath) : ErrorInfo(Tr::tr("Build graph not found for configuration '%1'. Expected location was '%2'.") diff --git a/src/lib/corelib/tools/stringconstants.h b/src/lib/corelib/tools/stringconstants.h index 114f5426e..5e6c6b253 100644 --- a/src/lib/corelib/tools/stringconstants.h +++ b/src/lib/corelib/tools/stringconstants.h @@ -118,6 +118,7 @@ public: QBS_STRING_CONSTANT(limitToSubProjectProperty, "limitToSubProject") QBS_STRING_CONSTANT(locationKey, "location") QBS_STRING_CONSTANT(messageKey, "message") + QBS_STRING_CONSTANT(minimalProperty, "minimal") QBS_STRING_CONSTANT(minimumQbsVersionProperty, "minimumQbsVersion") QBS_STRING_CONSTANT(moduleVar, "module") QBS_STRING_CONSTANT(moduleNameProperty, "moduleName") diff --git a/tests/auto/blackbox/testdata-qt/lupdate/lupdate.qbs b/tests/auto/blackbox/testdata-qt/lupdate/lupdate.qbs index 06ac80cdd..5d634f3e3 100644 --- a/tests/auto/blackbox/testdata-qt/lupdate/lupdate.qbs +++ b/tests/auto/blackbox/testdata-qt/lupdate/lupdate.qbs @@ -62,6 +62,7 @@ Project { } QtApplication { name: "qt2" + Depends { name: "qt1" } // Tests "minimal" propagation in Executor files: [ "qt2-dummy.cpp", "qt2-dummy2.cpp", @@ -71,6 +72,7 @@ Project { } CppApplication { name: "noqt" + Depends { name: "qt2" } // Tests "minimal" propagation in Executor files: "noqt-main.cpp" } Product { diff --git a/tests/auto/blackbox/testdata/partially-built-dependency/partially-built-dependency.qbs b/tests/auto/blackbox/testdata/partially-built-dependency/partially-built-dependency.qbs index f6a61406d..2ee5a4a41 100644 --- a/tests/auto/blackbox/testdata/partially-built-dependency/partially-built-dependency.qbs +++ b/tests/auto/blackbox/testdata/partially-built-dependency/partially-built-dependency.qbs @@ -2,11 +2,17 @@ import qbs.File import qbs.TextFile Project { + property bool minimalDependency Product { name: "p" type: "obj" Depends { name: "cpp" } - Depends { name: "dep" } + Depends { condition: project.minimalDependency === undefined; name: "dep" } + Depends { + condition: project.minimalDependency !== undefined + name: "dep" + minimal: project.minimalDependency + } Rule { inputsFromDependencies: "cpp" Artifact { diff --git a/tests/auto/blackbox/tst_blackbox.cpp b/tests/auto/blackbox/tst_blackbox.cpp index 069aa83b2..7bc58b549 100644 --- a/tests/auto/blackbox/tst_blackbox.cpp +++ b/tests/auto/blackbox/tst_blackbox.cpp @@ -3447,14 +3447,33 @@ void TestBlackbox::overrideProjectProperties() QCOMPARE(runQbs(params), 0); } +void TestBlackbox::partiallyBuiltDependency_data() +{ + QTest::addColumn<QByteArray>("mode"); + QTest::addColumn<bool>("expectBuilding"); + + QTest::newRow("default") << QByteArray("default") << true; + QTest::newRow("minimal") << QByteArray("minimal") << false; + QTest::newRow("full") << QByteArray("full") << true; +} + void TestBlackbox::partiallyBuiltDependency() { + QFETCH(QByteArray, mode); + QFETCH(bool, expectBuilding); + QDir::setCurrent(testDataDir + "/partially-built-dependency"); - QCOMPARE(runQbs(QbsRunParameters({"-p", "p"})), 0); + rmDirR(relativeBuildDir()); + QbsRunParameters params({"-p", "p"}); + if (mode == "minimal") + params.arguments << "project.minimalDependency:true"; + else if (mode == "full") + params.arguments << "project.minimalDependency:false"; + QCOMPARE(runQbs(params), 0); QCOMPARE(m_qbsStdout.count("generating main.cpp"), 1); QCOMPARE(m_qbsStdout.count("copying main.cpp"), 1); - QCOMPARE(m_qbsStdout.count("compiling main.cpp"), 1); - QVERIFY2(!m_qbsStdout.contains("linking"), m_qbsStdout.constData()); + QCOMPARE(m_qbsStdout.count("compiling main.cpp"), expectBuilding ? 2 : 1); + QVERIFY2(m_qbsStdout.contains("linking") == expectBuilding, m_qbsStdout.constData()); } void TestBlackbox::pathProbe_data() diff --git a/tests/auto/blackbox/tst_blackbox.h b/tests/auto/blackbox/tst_blackbox.h index 46b540a2d..ba7708388 100644 --- a/tests/auto/blackbox/tst_blackbox.h +++ b/tests/auto/blackbox/tst_blackbox.h @@ -241,6 +241,7 @@ private slots: void outputArtifactAutoTagging(); void outputRedirection(); void overrideProjectProperties(); + void partiallyBuiltDependency_data(); void partiallyBuiltDependency(); void pathProbe_data(); void pathProbe(); diff --git a/tests/auto/language/tst_language.cpp b/tests/auto/language/tst_language.cpp index 1a7275347..91fb1da89 100644 --- a/tests/auto/language/tst_language.cpp +++ b/tests/auto/language/tst_language.cpp @@ -617,9 +617,9 @@ void TestLanguage::dependencyOnAllProfiles() const ResolvedProductConstPtr mainProduct = productsFromProject(project).value("main"); QVERIFY(!!mainProduct); QCOMPARE(mainProduct->dependencies.size(), size_t { 2 }); - for (const ResolvedProductPtr &p : mainProduct->dependencies) { - QCOMPARE(p->name, QLatin1String("dep")); - QVERIFY(p->profile() == "p1" || p->profile() == "p2"); + for (const ProductDependency &p : mainProduct->dependencies) { + QCOMPARE(p.product->name, QLatin1String("dep")); + QVERIFY(p.product->profile() == "p1" || p.product->profile() == "p2"); } } catch (const ErrorInfo &e) { exceptionCaught = true; @@ -1249,9 +1249,9 @@ void TestLanguage::exports() product = products.value("A"); QVERIFY(!!product); - QVERIFY(contains(product->dependencies, products.value("B"))); - QVERIFY(contains(product->dependencies, products.value("C"))); - QVERIFY(contains(product->dependencies, products.value("D"))); + QVERIFY(contains(product->dependencies, ProductDependency{products.value("B"), false})); + QVERIFY(contains(product->dependencies, ProductDependency{products.value("C"), false})); + QVERIFY(contains(product->dependencies, ProductDependency{products.value("D"), false})); product = products.value("B"); QVERIFY(!!product); QVERIFY(product->dependencies.empty()); @@ -3408,8 +3408,8 @@ void TestLanguage::recursiveProductDependencies() QVERIFY(!!p3); const ResolvedProductPtr p4 = products.value("p4"); QVERIFY(!!p4); - QVERIFY(p1->dependencies == std::vector<ResolvedProductPtr>({p3, p4})); - QVERIFY(p2->dependencies == std::vector<ResolvedProductPtr>({p3, p4})); + QVERIFY(p1->depsAsProductList() == std::vector<ResolvedProductPtr>({p3, p4})); + QVERIFY(p2->depsAsProductList() == std::vector<ResolvedProductPtr>({p3, p4})); } catch (const ErrorInfo &e) { exceptionCaught = true; qDebug() << e.toString(); |