diff options
author | Christian Kandeler <[email protected]> | 2025-05-16 12:45:14 +0200 |
---|---|---|
committer | Christian Kandeler <[email protected]> | 2025-05-16 12:45:14 +0200 |
commit | aa2e23297cd77b2a8b9efe3338615a65283c4cb3 (patch) | |
tree | 9c88b1ecc7aa664e15ba7bf70585d96a5ac6189a | |
parent | 7148da60d5ae23998a8e51478bdfb2643a599b94 (diff) | |
parent | f7e71c1fefd1caf9c1706b71677528e86b1a1969 (diff) |
Merge 3.0 into master
Change-Id: Icc8662af8196982259d37bb2d6dc8975ffabc357
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 ¤tDependsItem = stateStack.front().currentDependsItem; currentDependsItem && !currentDependsItem->productTypes.empty()) { qCDebug(lcLoaderScheduling) << "product" << m_product.displayName() << "to be delayed because of bulk dependency"; - return {ProductDependency::Bulk, nullptr}; + return {ProductDependencyType::Bulk, nullptr}; } if (!stateStack.front().pendingResolvedDependencies.empty()) { if (ProductContext * const dep = stateStack.front().pendingResolvedDependencies @@ -1161,17 +1171,17 @@ std::pair<ProductDependency, ProductContext *> DependenciesContextImpl::pendingD qCDebug(lcLoaderScheduling) << "product" << m_product.displayName() << "to be delayed because of dependency " "to unfinished product" << dep->displayName(); - return {ProductDependency::Single, dep}; + return {ProductDependencyType::Single, dep}; } else { qCDebug(lcLoaderScheduling) << "product" << m_product.displayName() << "to be re-scheduled, as dependency " << dep->displayName() << "appears to have finished in the meantime"; - return {ProductDependency::None, dep}; + return {ProductDependencyType::None, dep}; } } } - return {ProductDependency::None, nullptr}; + return {ProductDependencyType::None, nullptr}; } void DependenciesContextImpl::setSearchPathsForProduct(LoaderState &loaderState) diff --git a/src/lib/corelib/loader/loaderutils.cpp b/src/lib/corelib/loader/loaderutils.cpp index 3651060cf..77f238f67 100644 --- a/src/lib/corelib/loader/loaderutils.cpp +++ b/src/lib/corelib/loader/loaderutils.cpp @@ -822,10 +822,10 @@ bool ProductContext::dependenciesResolvingPending() const && !product && !delayedError.hasError(); } -std::pair<ProductDependency, ProductContext *> ProductContext::pendingDependency() const +std::pair<ProductDependencyType, ProductContext *> ProductContext::pendingDependency() const { return dependenciesContext ? dependenciesContext->pendingDependency() - : std::make_pair(ProductDependency::None, nullptr); + : std::make_pair(ProductDependencyType::None, nullptr); } TimingData &TimingData::operator+=(const TimingData &other) diff --git a/src/lib/corelib/loader/loaderutils.h b/src/lib/corelib/loader/loaderutils.h index 4a4aadfb8..9f9aeedc9 100644 --- a/src/lib/corelib/loader/loaderutils.h +++ b/src/lib/corelib/loader/loaderutils.h @@ -84,7 +84,7 @@ using ModulePropertiesPerGroup = std::unordered_map<const Item *, QualifiedIdSet using FileLocations = QHash<std::pair<QString, QString>, CodeLocation>; enum class Deferral { Allowed, NotAllowed }; -enum class ProductDependency { None, Single, Bulk }; +enum class ProductDependencyType { None, Single, Bulk }; class CancelException { }; @@ -146,7 +146,7 @@ class DependenciesContext { public: virtual ~DependenciesContext(); - virtual std::pair<ProductDependency, ProductContext *> pendingDependency() const = 0; + virtual std::pair<ProductDependencyType, ProductContext *> pendingDependency() const = 0; bool dependenciesResolved = false; }; @@ -158,7 +158,7 @@ public: QString displayName() const; void handleError(const ErrorInfo &error); bool dependenciesResolvingPending() const; - std::pair<ProductDependency, ProductContext *> pendingDependency() const; + std::pair<ProductDependencyType, ProductContext *> pendingDependency() const; QString name; QString buildDirectory; diff --git a/src/lib/corelib/loader/productresolver.cpp b/src/lib/corelib/loader/productresolver.cpp index 376731593..273efaaf3 100644 --- a/src/lib/corelib/loader/productresolver.cpp +++ b/src/lib/corelib/loader/productresolver.cpp @@ -1264,15 +1264,17 @@ void ProductResolverStage2::collectProductDependencies() const ResolvedProductPtr &dep = module.product->product; QBS_CHECK(dep); QBS_CHECK(dep != product); - product->dependencies << dep; + product->dependencies.emplace_back(dep, module.minimal); product->dependencyParameters.insert(dep, module.parameters); // TODO: Streamline this with normal module dependencies? } // TODO: We might want to keep the topological sorting and get rid of "module module dependencies". - std::sort(product->dependencies.begin(),product->dependencies.end(), - [](const ResolvedProductPtr &p1, const ResolvedProductPtr &p2) { - return p1->fullDisplayName() < p2->fullDisplayName(); - }); + std::sort( + product->dependencies.begin(), + product->dependencies.end(), + [](const ProductDependency &p1, const ProductDependency &p2) { + return p1.product->fullDisplayName() < p2.product->fullDisplayName(); + }); } void ExportsResolver::start() diff --git a/src/lib/corelib/loader/productsresolver.cpp b/src/lib/corelib/loader/productsresolver.cpp index b72736d75..33e163fcc 100644 --- a/src/lib/corelib/loader/productsresolver.cpp +++ b/src/lib/corelib/loader/productsresolver.cpp @@ -453,14 +453,14 @@ void ProductsResolver::handleFinishedThreads() << product.displayName(); const auto pending = product.pendingDependency(); switch (pending.first) { - case ProductDependency::Single: + case ProductDependencyType::Single: waitForSingleDependency(ProductWithLoaderState(product, &loaderState), *pending.second); break; - case ProductDependency::Bulk: + case ProductDependencyType::Bulk: waitForBulkDependency(ProductWithLoaderState(product, &loaderState)); break; - case ProductDependency::None: + case ProductDependencyType::None: // This can happen if the dependency has finished in between the check in // DependencyResolver and the one here. QBS_CHECK(pending.second); diff --git a/src/lib/corelib/tools/persistence.cpp b/src/lib/corelib/tools/persistence.cpp index 15550aec5..de504e1d3 100644 --- a/src/lib/corelib/tools/persistence.cpp +++ b/src/lib/corelib/tools/persistence.cpp @@ -48,7 +48,7 @@ namespace qbs { namespace Internal { -static const char QBS_PERSISTENCE_MAGIC[] = "QBSPERSISTENCE-137"; +static const char QBS_PERSISTENCE_MAGIC[] = "QBSPERSISTENCE-138"; NoBuildGraphError::NoBuildGraphError(const QString &filePath) : ErrorInfo(Tr::tr("Build graph not found for configuration '%1'. Expected location was '%2'.") diff --git a/src/lib/corelib/tools/stringconstants.h b/src/lib/corelib/tools/stringconstants.h index 114f5426e..5e6c6b253 100644 --- a/src/lib/corelib/tools/stringconstants.h +++ b/src/lib/corelib/tools/stringconstants.h @@ -118,6 +118,7 @@ public: QBS_STRING_CONSTANT(limitToSubProjectProperty, "limitToSubProject") QBS_STRING_CONSTANT(locationKey, "location") QBS_STRING_CONSTANT(messageKey, "message") + QBS_STRING_CONSTANT(minimalProperty, "minimal") QBS_STRING_CONSTANT(minimumQbsVersionProperty, "minimumQbsVersion") QBS_STRING_CONSTANT(moduleVar, "module") QBS_STRING_CONSTANT(moduleNameProperty, "moduleName") diff --git a/tests/auto/blackbox/testdata-qt/lupdate/lupdate.qbs b/tests/auto/blackbox/testdata-qt/lupdate/lupdate.qbs index 06ac80cdd..5d634f3e3 100644 --- a/tests/auto/blackbox/testdata-qt/lupdate/lupdate.qbs +++ b/tests/auto/blackbox/testdata-qt/lupdate/lupdate.qbs @@ -62,6 +62,7 @@ Project { } QtApplication { name: "qt2" + Depends { name: "qt1" } // Tests "minimal" propagation in Executor files: [ "qt2-dummy.cpp", "qt2-dummy2.cpp", @@ -71,6 +72,7 @@ Project { } CppApplication { name: "noqt" + Depends { name: "qt2" } // Tests "minimal" propagation in Executor files: "noqt-main.cpp" } Product { diff --git a/tests/auto/blackbox/testdata/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] |