aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-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();