aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChristian Kandeler <[email protected]>2025-05-16 12:45:14 +0200
committerChristian Kandeler <[email protected]>2025-05-16 12:45:14 +0200
commitaa2e23297cd77b2a8b9efe3338615a65283c4cb3 (patch)
tree9c88b1ecc7aa664e15ba7bf70585d96a5ac6189a
parent7148da60d5ae23998a8e51478bdfb2643a599b94 (diff)
parentf7e71c1fefd1caf9c1706b71677528e86b1a1969 (diff)
Merge 3.0 into master
-rw-r--r--changelogs/changes-3.0.0.md4
-rw-r--r--doc/reference/items/language/depends.qdoc14
-rw-r--r--doc/reference/modules/cpp-module.qdoc26
-rw-r--r--doc/tutorial.qdoc32
-rw-r--r--docker-compose.yml4
-rw-r--r--docker/noble/Dockerfile1
-rw-r--r--share/qbs/imports/qbs/Probes/MsvcProbe.qbs5
-rw-r--r--share/qbs/imports/qbs/base/QtLupdateRunner.qbs2
-rw-r--r--share/qbs/modules/cpp/CppModule.qbs10
-rw-r--r--share/qbs/modules/cpp/GenericGCC.qbs20
-rw-r--r--share/qbs/modules/cpp/gcc.js49
-rw-r--r--share/qbs/modules/cpp/msvc.js37
-rw-r--r--share/qbs/modules/cpp/windows-msvc.qbs20
-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/cxx-modules/import-std-compat/import-std-compat.qbs26
-rw-r--r--tests/auto/blackbox/testdata/cxx-modules/import-std-compat/main.cpp10
-rw-r--r--tests/auto/blackbox/testdata/cxx-modules/import-std/import-std.qbs26
-rw-r--r--tests/auto/blackbox/testdata/cxx-modules/import-std/main.cpp6
-rw-r--r--tests/auto/blackbox/testdata/partially-built-dependency/partially-built-dependency.qbs8
-rw-r--r--tests/auto/blackbox/tst_blackbox.cpp27
-rw-r--r--tests/auto/blackbox/tst_blackbox.h1
-rw-r--r--tests/auto/language/tst_language.cpp16
-rw-r--r--tutorial/chapter-10-3/main.cpp13
-rw-r--r--tutorial/chapter-10-3/myproject.qbs28
45 files changed, 509 insertions, 96 deletions
diff --git a/changelogs/changes-3.0.0.md b/changelogs/changes-3.0.0.md
index c718c110b..164a07910 100644
--- a/changelogs/changes-3.0.0.md
+++ b/changelogs/changes-3.0.0.md
@@ -4,8 +4,12 @@
* Errors during project resolving print a sort of stack trace now, giving users
a better idea about what is going wrong.
* The JavaScript backend was switched to `QuickJS-NG`, which is actively maintained.
+* Added support for C++ standard library modules - "import std;" and "import std.compat;".
# 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/doc/reference/modules/cpp-module.qdoc b/doc/reference/modules/cpp-module.qdoc
index 76b15ce7b..d35a87ea7 100644
--- a/doc/reference/modules/cpp-module.qdoc
+++ b/doc/reference/modules/cpp-module.qdoc
@@ -1998,3 +1998,29 @@
\defaultvalue \c{false}
*/
+
+/*!
+ \qmlproperty bool cpp::forceUseImportStd
+ \since Qbs 3.0
+
+ Set this property to \c true to force the use of the C++23 standard library module via
+ \c{import std;}.
+
+ \note This property is only effective with toolchains and standard libraries that support
+ the C++23 standard library module.
+
+ \defaultvalue \c{false}
+*/
+
+/*!
+ \qmlproperty bool cpp::forceUseImportStdCompat
+ \since Qbs 3.0
+
+ Set this property to \c true to force the use of the C++23 standard library compatibility
+ module via \c{import std.compat;}.
+
+ \note This property is only effective with toolchains and standard libraries that support
+ the C++23 standard library compatibility module.
+
+ \defaultvalue \c{false}
+*/
diff --git a/doc/tutorial.qdoc b/doc/tutorial.qdoc
index c076d054a..a04d3b3af 100644
--- a/doc/tutorial.qdoc
+++ b/doc/tutorial.qdoc
@@ -726,4 +726,36 @@
For more details, see the complete
\l{https://github.com/qbs/qbs/tree/2.5/tutorial/chapter-10-2}{example}.
+
+ \section1 Import std module
+
+ Starting with C++23, you can use the standard library as a module by using \c{import std;} or
+ \c{import std.compat;}.
+
+ In order to use \c{import std;}, you need to set the
+ \l{cpp::forceUseImportStd}{cpp.forceUseImportStd} property to \c{true}.
+
+ Here's a simple example that demonstrates the use of the standard library module:
+
+ \snippet ../tutorial/chapter-10-3/main.cpp 0
+
+ The project file needs to be configured to use C++23 and enable the standard library module:
+
+ \snippet ../tutorial/chapter-10-3/myproject.qbs 0
+
+ \note This feature requires a compiler and standard library that support the C++23 standard
+ library module. Currently, this feature is experimental and may not be available in all
+ toolchains.
+
+ The full product file may look like this:
+
+ \snippet ../tutorial/chapter-10-3/myproject.qbs 1
+
+ In order to use \c{import std.compat;}, you will also need to set the
+ \l{cpp::forceUseImportStdCompat}{cpp.forceUseImportStdCompat} to \c{true}:
+
+ \code
+ cpp.forceUseImportStd: true
+ cpp.forceUseImportCompatStd: true
+ \endcode
*/ \ No newline at end of file
diff --git a/docker-compose.yml b/docker-compose.yml
index 062986f4a..df7d85f8c 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -26,7 +26,7 @@ services:
noble-qt6: &noble-qt6
<< : *linux
hostname: noble-qt6
- image: ${DOCKER_USER:-qbsbuild}/qbsdev:noble-qt6-6.7.3_2.5.1-2
+ image: ${DOCKER_USER:-qbsbuild}/qbsdev:noble-qt6-6.7.3_2.5.1-3
build:
dockerfile: docker/noble/Dockerfile
context: .
@@ -40,7 +40,7 @@ services:
noble-qt5:
<< : *linux
hostname: noble-qt5
- image: ${DOCKER_USER:-qbsbuild}/qbsdev:noble-5.15.2_2.5.1-0
+ image: ${DOCKER_USER:-qbsbuild}/qbsdev:noble-5.15.2_2.5.1-1
build:
dockerfile: docker/noble/Dockerfile
context: .
diff --git a/docker/noble/Dockerfile b/docker/noble/Dockerfile
index 24b1f8286..a2e0ede7e 100644
--- a/docker/noble/Dockerfile
+++ b/docker/noble/Dockerfile
@@ -51,6 +51,7 @@ RUN apt-get update -qq && \
help2man \
icoutils \
libcapnp-dev \
+ libc++-18-dev \
libclang-rt-18-dev \
libdbus-1-3 \
libfreetype6 \
diff --git a/share/qbs/imports/qbs/Probes/MsvcProbe.qbs b/share/qbs/imports/qbs/Probes/MsvcProbe.qbs
index d3624e010..03959bfab 100644
--- a/share/qbs/imports/qbs/Probes/MsvcProbe.qbs
+++ b/share/qbs/imports/qbs/Probes/MsvcProbe.qbs
@@ -46,6 +46,7 @@ PathProbe {
property int versionMinor
property int versionPatch
property stringList includePaths
+ property string modulesPath
property var buildEnv
property var compilerDefinesByLanguage
@@ -87,6 +88,10 @@ PathProbe {
if (File.exists(inclPath))
includePaths = [inclPath];
+ var modsPath = FileInfo.joinPaths(clParentDir, "..", "..", "modules");
+ if (File.exists(modsPath))
+ modulesPath = modsPath;
+
if (preferredArchitecture && Utilities.canonicalArchitecture(preferredArchitecture)
!== Utilities.canonicalArchitecture(architecture)) {
throw "'" + preferredArchitecture +
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/share/qbs/modules/cpp/CppModule.qbs b/share/qbs/modules/cpp/CppModule.qbs
index a18bc8b4f..bb80b7046 100644
--- a/share/qbs/modules/cpp/CppModule.qbs
+++ b/share/qbs/modules/cpp/CppModule.qbs
@@ -64,6 +64,9 @@ Module {
property bool useObjcPrecompiledHeader: true
property bool useObjcxxPrecompiledHeader: true
property bool forceUseCxxModules: false
+ property bool forceUseImportStd: false
+ property bool forceUseImportStdCompat: false
+ property stringList stdModulesFiles
property string moduleOutputFlag // undocumented
property string moduleFileFlag // undocumented
@@ -429,6 +432,13 @@ Module {
property bool importPrivateLibraries: true
+ Group {
+ name: "std modules"
+ condition: stdModulesFiles !== undefined
+ files: stdModulesFiles
+ fileTags: "cppm"
+ }
+
// TODO: The following four rules could use a convenience base item if rule properties
// were available in Artifact items and prepare scripts.
Rule {
diff --git a/share/qbs/modules/cpp/GenericGCC.qbs b/share/qbs/modules/cpp/GenericGCC.qbs
index ed66f19c0..1936519cc 100644
--- a/share/qbs/modules/cpp/GenericGCC.qbs
+++ b/share/qbs/modules/cpp/GenericGCC.qbs
@@ -73,6 +73,26 @@ CppModule {
flags: targetDriverFlags.concat(sysrootFlags)
_sysroot: sysroot
}
+
+ stdModulesFiles: stdModulesProbe.found ? stdModulesProbe._stdModulesFiles : undefined
+ Probe {
+ // input
+ property bool _forceUseImportStd : forceUseImportStd
+ property bool _forceUseImportStdCompat : forceUseImportStdCompat
+ property stringList _toolchain: qbs.toolchain
+ property string _compilerPath : compilerPathByLanguage["cpp"]
+
+ // output
+ property stringList _stdModulesFiles
+
+ id: stdModulesProbe
+ condition: !_skipAllChecks
+ && stdModulesFiles === undefined
+ && !qbs.toolchain.includes("mingw")
+ && (forceUseImportStd || forceUseImportStdCompat)
+ configure: Gcc.configureStdModules()
+ }
+
property var probeEnv
Probes.BinaryProbe {
diff --git a/share/qbs/modules/cpp/gcc.js b/share/qbs/modules/cpp/gcc.js
index 09f7ee69f..6d84cbed9 100644
--- a/share/qbs/modules/cpp/gcc.js
+++ b/share/qbs/modules/cpp/gcc.js
@@ -1791,3 +1791,52 @@ function wasmArtifacts(product)
return artifacts;
}
+
+function configureStdModules() {
+ try {
+ var probe = new Process();
+ // check which stdlib is used? this actually works:
+ // clang-18 -print-file-name=libstdc++.modules.json --sysroot /opt/gcc-15/
+ const modulesJsonFiles = [
+ "libc++.modules.json",
+ "libstdc++.modules.json",
+ "../../c++/libc++.modules.json",
+ ];
+ for (var i = 0; i < modulesJsonFiles.length; ++i) {
+ const modulesJsonFile = modulesJsonFiles[i];
+ const result = probe.exec(_compilerPath, ["-print-file-name=" + modulesJsonFile], false);
+ if (result !== 0)
+ continue;
+ const path = probe.readStdOut().trim();
+ if (path !== modulesJsonFile && FileInfo.isAbsolutePath(path) && File.exists(path)) {
+ const jsonFile = new TextFile(path, TextFile.ReadOnly);
+
+ const json = JSON.parse(jsonFile.readAll());
+ jsonFile.close();
+ const modules = json.modules
+ .filter(function(module) {
+ const logicalName = module["logical-name"];
+ if (logicalName === "std" && (_forceUseImportStd || _forceUseImportStdCompat)) {
+ return true;
+ } else if (logicalName === "std.compat" && _forceUseImportStdCompat) {
+ return true;
+ }
+ return false;
+ })
+ .map(function(module) {
+ return FileInfo.joinPaths(FileInfo.path(path), module["source-path"]);
+ })
+ .filter(function(module) {
+ return File.exists(module);
+ });
+ if (modules.length > 0) {
+ found = true;
+ _stdModulesFiles = modules;
+ break;
+ }
+ }
+ }
+ } finally {
+ probe.close();
+ }
+}
diff --git a/share/qbs/modules/cpp/msvc.js b/share/qbs/modules/cpp/msvc.js
index 192d7b1cb..b079c3215 100644
--- a/share/qbs/modules/cpp/msvc.js
+++ b/share/qbs/modules/cpp/msvc.js
@@ -33,6 +33,7 @@ var Cpp = require("cpp.js");
var File = require("qbs.File");
var FileInfo = require("qbs.FileInfo");
var ModUtils = require("qbs.ModUtils");
+var TextFile = require("qbs.TextFile");
var Utilities = require("qbs.Utilities");
var WindowsUtils = require("qbs.WindowsUtils");
@@ -808,3 +809,39 @@ function appLinkerOutputArtifacts(product)
}
return artifacts;
}
+
+function configureStdModules() {
+ try {
+ const modulesJsonPath = FileInfo.joinPaths(_modulesDirPath, "modules.json");
+ if (File.exists(modulesJsonPath)) {
+ const jsonFile = new TextFile(modulesJsonPath, TextFile.ReadOnly);
+ const json = JSON.parse(jsonFile.readAll());
+ jsonFile.close();
+
+ const moduleSources = json["module-sources"];
+ if (moduleSources) {
+ const modules = moduleSources
+ .filter(function(module) {
+ if (module === "std.ixx" && (_forceUseImportStd || _forceUseImportStdCompat)) {
+ return true;
+ } else if (module === "std.compat.ixx" && _forceUseImportStdCompat) {
+ return true;
+ }
+ return false;
+ })
+ .map(function(module) {
+ return FileInfo.joinPaths(_modulesDirPath, module);
+ })
+ .filter(function(module) {
+ return File.exists(module);
+ });
+ if (modules.length > 0) {
+ found = true;
+ _stdModulesFiles = modules;
+ }
+ }
+ }
+ } catch (e) {
+ console.error("Error reading modules.json: " + e);
+ }
+}
diff --git a/share/qbs/modules/cpp/windows-msvc.qbs b/share/qbs/modules/cpp/windows-msvc.qbs
index a7b99c324..4bb9f3963 100644
--- a/share/qbs/modules/cpp/windows-msvc.qbs
+++ b/share/qbs/modules/cpp/windows-msvc.qbs
@@ -32,6 +32,7 @@
import qbs.Host
import qbs.Probes
import "windows-msvc-base.qbs" as MsvcBaseModule
+import 'msvc.js' as MSVC
MsvcBaseModule {
condition: Host.os().includes('windows') &&
@@ -75,4 +76,23 @@ MsvcBaseModule {
compiledModuleSuffix: ".ifc"
moduleOutputFlag: "-ifcOutput "
moduleFileFlag: "-reference %module%="
+
+ stdModulesFiles: stdModulesProbe.found ? stdModulesProbe._stdModulesFiles : undefined
+ Probe {
+ id: stdModulesProbe
+ condition: msvcProbe.found
+ && !_skipAllChecks
+ && stdModulesFiles === undefined
+ && (forceUseImportStd || forceUseImportStdCompat)
+
+ // input
+ property string _modulesDirPath: msvcProbe.modulesPath
+ property bool _forceUseImportStd : forceUseImportStd
+ property bool _forceUseImportStdCompat : forceUseImportStdCompat
+
+ // output
+ property stringList _stdModulesFiles
+
+ configure: MSVC.configureStdModules()
+ }
}
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/cxx-modules/import-std-compat/import-std-compat.qbs b/tests/auto/blackbox/testdata/cxx-modules/import-std-compat/import-std-compat.qbs
new file mode 100644
index 000000000..1f97b5679
--- /dev/null
+++ b/tests/auto/blackbox/testdata/cxx-modules/import-std-compat/import-std-compat.qbs
@@ -0,0 +1,26 @@
+// Checks simple case with a single module.
+CppApplication {
+ name: "import-std-compat"
+ condition: {
+ if (qbs.toolchainType === "msvc"
+ || ((qbs.toolchainType === "gcc" || qbs.toolchainType === "mingw")
+ && cpp.compilerVersionMajor >= 15)
+ || (qbs.toolchainType === "clang" && cpp.compilerVersionMajor >= 18)) {
+ return true;
+ }
+ console.info("Unsupported toolchainType " + qbs.toolchainType);
+ return false;
+ }
+ consoleApplication: true
+ files: [
+ "main.cpp"
+ ]
+ cpp.cxxLanguageVersion: "c++23"
+ cpp.forceUseCxxModules: true
+ cpp.forceUseImportStdCompat: true
+ Properties {
+ condition: qbs.toolchainType === "clang"
+ cpp.cxxFlags: ["-Wno-reserved-module-identifier"]
+ cpp.cxxStandardLibrary: "libc++"
+ }
+} \ No newline at end of file
diff --git a/tests/auto/blackbox/testdata/cxx-modules/import-std-compat/main.cpp b/tests/auto/blackbox/testdata/cxx-modules/import-std-compat/main.cpp
new file mode 100644
index 000000000..9ecdf7b38
--- /dev/null
+++ b/tests/auto/blackbox/testdata/cxx-modules/import-std-compat/main.cpp
@@ -0,0 +1,10 @@
+import std.compat;
+
+int main()
+{
+ std::cout << "Hello from std.compat!" << std::endl;
+
+ printf("Using printf from std.compat\n");
+
+ return 0;
+} \ No newline at end of file
diff --git a/tests/auto/blackbox/testdata/cxx-modules/import-std/import-std.qbs b/tests/auto/blackbox/testdata/cxx-modules/import-std/import-std.qbs
new file mode 100644
index 000000000..2b87bbfe9
--- /dev/null
+++ b/tests/auto/blackbox/testdata/cxx-modules/import-std/import-std.qbs
@@ -0,0 +1,26 @@
+// Checks simple case with a single module.
+CppApplication {
+ name: "import-std"
+ condition: {
+ if (qbs.toolchainType === "msvc"
+ || ((qbs.toolchainType === "gcc" || qbs.toolchainType === "mingw")
+ && cpp.compilerVersionMajor >= 15)
+ || (qbs.toolchainType === "clang" && cpp.compilerVersionMajor >= 18)) {
+ return true;
+ }
+ console.info("Unsupported toolchainType " + qbs.toolchainType);
+ return false;
+ }
+ consoleApplication: true
+ files: [
+ "main.cpp"
+ ]
+ cpp.cxxLanguageVersion: "c++23"
+ cpp.forceUseCxxModules: true
+ cpp.forceUseImportStd: true
+ Properties {
+ condition: qbs.toolchainType === "clang"
+ cpp.cxxFlags: ["-Wno-reserved-module-identifier"]
+ cpp.cxxStandardLibrary: "libc++"
+ }
+}
diff --git a/tests/auto/blackbox/testdata/cxx-modules/import-std/main.cpp b/tests/auto/blackbox/testdata/cxx-modules/import-std/main.cpp
new file mode 100644
index 000000000..521b58ec8
--- /dev/null
+++ b/tests/auto/blackbox/testdata/cxx-modules/import-std/main.cpp
@@ -0,0 +1,6 @@
+import std;
+
+int main()
+{
+ std::cout << "Hello World!" << std::endl;
+}
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..c1342061d 100644
--- a/tests/auto/blackbox/tst_blackbox.cpp
+++ b/tests/auto/blackbox/tst_blackbox.cpp
@@ -2136,6 +2136,8 @@ void TestBlackbox::cxxModules_data()
QTest::newRow("single-module") << "single-mod";
QTest::newRow("dot-in-name") << "dot-in-name";
QTest::newRow("export-import") << "export-import";
+ QTest::newRow("import-std") << "import-std";
+ QTest::newRow("import-std-compat") << "import-std-compat";
QTest::newRow("dependent-modules") << "dep-mods";
QTest::newRow("declaration-implementation") << "decl-impl";
QTest::newRow("library-module") << "lib-mod";
@@ -3447,14 +3449,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();
diff --git a/tutorial/chapter-10-3/main.cpp b/tutorial/chapter-10-3/main.cpp
new file mode 100644
index 000000000..88594765d
--- /dev/null
+++ b/tutorial/chapter-10-3/main.cpp
@@ -0,0 +1,13 @@
+//![0]
+// main.cpp
+
+import std;
+
+int main()
+{
+ std::vector<int> numbers = {1, 2, 3, 4, 5};
+ std::ranges::for_each(numbers, [](int n) { std::cout << n << ' '; });
+ std::cout << std::endl;
+ return 0;
+}
+//![0]
diff --git a/tutorial/chapter-10-3/myproject.qbs b/tutorial/chapter-10-3/myproject.qbs
new file mode 100644
index 000000000..f22e504e1
--- /dev/null
+++ b/tutorial/chapter-10-3/myproject.qbs
@@ -0,0 +1,28 @@
+//![1]
+// myproject.qbs
+CppApplication {
+ condition: {
+ if (qbs.toolchainType === "msvc"
+ || ((qbs.toolchainType === "gcc")
+ && cpp.compilerVersionMajor >= 15)
+ || (qbs.toolchainType === "clang" && cpp.compilerVersionMajor >= 18)) {
+ return true;
+ }
+ console.info("Unsupported toolchainType " + qbs.toolchainType);
+ return false;
+ }
+ consoleApplication: true
+ install: true
+ files: ["main.cpp" ]
+ //![0]
+ cpp.cxxLanguageVersion: "c++23"
+ cpp.forceUseCxxModules: true
+ cpp.forceUseImportStd: true
+ //![0]
+ Properties {
+ condition: qbs.toolchainType === "clang"
+ cpp.cxxFlags: ["-Wno-reserved-module-identifier"]
+ cpp.cxxStandardLibrary: "libc++"
+ }
+}
+//![1]