diff options
-rw-r--r-- | changelogs/changes-3.0.0.md | 1 | ||||
-rw-r--r-- | doc/reference/modules/cpp-module.qdoc | 26 | ||||
-rw-r--r-- | doc/tutorial.qdoc | 32 | ||||
-rw-r--r-- | share/qbs/imports/qbs/Probes/MsvcProbe.qbs | 5 | ||||
-rw-r--r-- | share/qbs/modules/cpp/CppModule.qbs | 10 | ||||
-rw-r--r-- | share/qbs/modules/cpp/GenericGCC.qbs | 20 | ||||
-rw-r--r-- | share/qbs/modules/cpp/gcc.js | 49 | ||||
-rw-r--r-- | share/qbs/modules/cpp/msvc.js | 37 | ||||
-rw-r--r-- | share/qbs/modules/cpp/windows-msvc.qbs | 20 | ||||
-rw-r--r-- | tests/auto/blackbox/testdata/cxx-modules/import-std-compat/import-std-compat.qbs | 26 | ||||
-rw-r--r-- | tests/auto/blackbox/testdata/cxx-modules/import-std-compat/main.cpp | 10 | ||||
-rw-r--r-- | tests/auto/blackbox/testdata/cxx-modules/import-std/import-std.qbs | 26 | ||||
-rw-r--r-- | tests/auto/blackbox/testdata/cxx-modules/import-std/main.cpp | 6 | ||||
-rw-r--r-- | tests/auto/blackbox/tst_blackbox.cpp | 2 | ||||
-rw-r--r-- | tutorial/chapter-10-3/main.cpp | 13 | ||||
-rw-r--r-- | tutorial/chapter-10-3/myproject.qbs | 28 |
16 files changed, 311 insertions, 0 deletions
diff --git a/changelogs/changes-3.0.0.md b/changelogs/changes-3.0.0.md index 96cefeee1..164a07910 100644 --- a/changelogs/changes-3.0.0.md +++ b/changelogs/changes-3.0.0.md @@ -4,6 +4,7 @@ * 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 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/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/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/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/tst_blackbox.cpp b/tests/auto/blackbox/tst_blackbox.cpp index 7bc58b549..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"; 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] |