aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChristian Kandeler <[email protected]>2025-05-09 11:21:10 +0200
committerChristian Kandeler <[email protected]>2025-05-10 06:07:35 +0000
commit270493dc6992d3d1c8ea11c333823ddf0b751cd1 (patch)
tree6bb334ab6b685878430d891215ff58cedcf98f7a
parent10cd7751abac29dcdf5486d402ab8f2328e3bb3d (diff)
Introduce new property Depends.minimal
It controls whether to build dependencies fully or only to the extent required by the depending product. It defaults to false, so dependencies will be fully built by default, restoring the pre-2.6 behavior. Amends 79d75e1c7ff04369a02abe72d3b077374917f2fe, which was done for better lupdate support, but broke user assumptions. Change-Id: I793141e94b7af8065e6070fb0acda46573a734fa Reviewed-by: Ivan Komissarov <[email protected]>
-rw-r--r--changelogs/changes-3.0.0.md3
-rw-r--r--doc/reference/items/language/depends.qdoc14
-rw-r--r--share/qbs/imports/qbs/base/QtLupdateRunner.qbs2
-rw-r--r--src/lib/corelib/api/project.cpp6
-rw-r--r--src/lib/corelib/buildgraph/buildgraph.cpp7
-rw-r--r--src/lib/corelib/buildgraph/buildgraphloader.cpp23
-rw-r--r--src/lib/corelib/buildgraph/depscanner.cpp2
-rw-r--r--src/lib/corelib/buildgraph/executor.cpp38
-rw-r--r--src/lib/corelib/buildgraph/inputartifactscanner.cpp4
-rw-r--r--src/lib/corelib/buildgraph/projectbuilddata.cpp10
-rw-r--r--src/lib/corelib/buildgraph/requesteddependencies.cpp2
-rw-r--r--src/lib/corelib/buildgraph/rulenode.cpp4
-rw-r--r--src/lib/corelib/buildgraph/rulesapplicator.cpp2
-rw-r--r--src/lib/corelib/language/builtindeclarations.cpp4
-rw-r--r--src/lib/corelib/language/item.h1
-rw-r--r--src/lib/corelib/language/language.cpp15
-rw-r--r--src/lib/corelib/language/language.h27
-rw-r--r--src/lib/corelib/loader/dependenciesresolver.cpp42
-rw-r--r--src/lib/corelib/loader/loaderutils.cpp4
-rw-r--r--src/lib/corelib/loader/loaderutils.h6
-rw-r--r--src/lib/corelib/loader/productresolver.cpp12
-rw-r--r--src/lib/corelib/loader/productsresolver.cpp6
-rw-r--r--src/lib/corelib/tools/persistence.cpp2
-rw-r--r--src/lib/corelib/tools/stringconstants.h1
-rw-r--r--tests/auto/blackbox/testdata-qt/lupdate/lupdate.qbs2
-rw-r--r--tests/auto/blackbox/testdata/partially-built-dependency/partially-built-dependency.qbs8
-rw-r--r--tests/auto/blackbox/tst_blackbox.cpp25
-rw-r--r--tests/auto/blackbox/tst_blackbox.h1
-rw-r--r--tests/auto/language/tst_language.cpp16
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 &currentDependsItem = 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();