diff options
author | Ivan Komissarov <abbapoh@gmail.com> | 2021-10-26 13:22:27 +0300 |
---|---|---|
committer | Ivan Komissarov <abbapoh@gmail.com> | 2021-10-26 13:22:27 +0300 |
commit | e811681b9024b42fe6d064f2e2e37392f6093b86 (patch) | |
tree | b27724cb432036c4f119309dc7d27381a26872c7 | |
parent | 05c46fbeb5256eb0935b49d1d05197a448e49830 (diff) | |
parent | 24044d6da5d3f3725adc134fdb7e71fe398381ff (diff) |
Merge branch '1.21' into master
Change-Id: I3417bbe182c23f8e6a9704b008eb8a5618e4d175
58 files changed, 4036 insertions, 118 deletions
diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 935605566..88e0bf13c 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -353,6 +353,12 @@ jobs: script: './scripts/test-qt-for-android.sh', } - { + name: 'Run Android tests (Qt 6.2.0)', + image: 'focal-android-620', + profile: '', + script: './scripts/test-qt-for-android.sh', + } + - { name: 'Run Android tests (ndk r19c)', image: 'focal-android-ndk-r19c', profile: '', @@ -598,7 +604,7 @@ jobs: runner: 'macos-11.0', target: 'desktop', toolchain: 'clang_64', - testProfile: 'xcode_12_5-macosx-x86_64', + testProfile: 'xcode_12_5_1-macosx-x86_64', qtVersion: '5.15.2', script: './scripts/test-qbs.sh', } @@ -607,7 +613,7 @@ jobs: runner: 'macos-11.0', target: 'desktop', toolchain: 'clang_64', - testProfile: 'xcode_12_5-macosx-x86_64', + testProfile: 'xcode_12_5_1-macosx-x86_64', qtVersion: '6.0.2', script: './scripts/test-qt.sh', } @@ -616,7 +622,7 @@ jobs: runner: 'macos-11.0', target: 'ios', toolchain: 'ios', - testProfile: 'xcode_12_5-iphoneos-arm64', + testProfile: 'xcode_12_5_1-iphoneos-arm64', qtVersion: '5.15.2', script: './scripts/test-qbs.sh', } @@ -625,7 +631,7 @@ jobs: runner: 'macos-11.0', target: 'ios', toolchain: 'ios', - testProfile: 'xcode_12_5-iphonesimulator-x86_64', + testProfile: 'xcode_12_5_1-iphonesimulator-x86_64', qtVersion: '5.15.2', script: './scripts/test-qbs.sh', } diff --git a/cmake/QbsDocumentation.cmake b/cmake/QbsDocumentation.cmake index a8ef1bb97..d9bdb7a09 100644 --- a/cmake/QbsDocumentation.cmake +++ b/cmake/QbsDocumentation.cmake @@ -201,13 +201,13 @@ function(_qbs_setup_qhelpgenerator_targets _qdocconf_file _html_outputdir) return() endif() + get_filename_component(_target "${_qdocconf_file}" NAME_WE) + set(_html_target "qbs_html_docs_${_target}") if (NOT TARGET ${_html_target}) return() endif() - get_filename_component(_target "${_qdocconf_file}" NAME_WE) - set(_qch_outputdir "${_arg_QCH_DIR}") file(MAKE_DIRECTORY "${_qch_outputdir}") diff --git a/doc/qbs.qdoc b/doc/qbs.qdoc index ac7bf20c2..b44ba5880 100644 --- a/doc/qbs.qdoc +++ b/doc/qbs.qdoc @@ -81,6 +81,7 @@ \li \l{List of Built-in Services} \li \l{Command-Line Interface} \li \l{List of Modules} + \li \l{List of Module Providers} \li \l{Command and JavaScriptCommand} \endlist @@ -911,7 +912,7 @@ For example, a profile for building C++ applications contains at least the installation path and the type of the compiler toolchain. A profile for building Qt applications contains the toolchain-specific properties as well - as \l{Qt-specific Module Provider Properties}{the path to the Qt installation}. + as \l{Qt::qmakeFilePaths}{the path to the Qt installation}. This topic describes profiles stored in the \QBS settings. In some cases it might be beneficial to keep profiles explicitly in the project sources. This @@ -1097,9 +1098,6 @@ } \endcode - The import statement gives us access to some built-in types and specifies the - used language version. - \a Application describes the product we want to build. In this case, an application. This is just a shortcut for writing \code @@ -1620,8 +1618,7 @@ \title Running Applications - By default, running an application also builds it and installs it to a - location from where it can be run on the desktop or on a device. + Qbs has the convenience \l{run}{qbs run} command that simplifies running applications. For example, entering the following command runs the Qt Creator application: @@ -1629,7 +1626,16 @@ qbs run --products qtcreator \endcode - This command also builds and installs the product, if necessary. + By default, running an application also builds it and installs it to a + location from where it can be run on the desktop or on a device. Also, this command + \l{Module::setupRunEnvironment}{sets up} the environment so that the application can find + dependent libraries. + + This is not the case when running the binary manually - you'll have to make sure that the + app will find its dependencies and/or is relocatable. You can achieve that by + \l{How do I make use of rpaths?}{configuring rpaths} properly or setting the appropriate + environment variable for the respective host system. + */ /*! diff --git a/doc/reference/module-providers/qbspkgconfig-module-provider.qdoc b/doc/reference/module-providers/qbspkgconfig-module-provider.qdoc new file mode 100644 index 000000000..debaa5992 --- /dev/null +++ b/doc/reference/module-providers/qbspkgconfig-module-provider.qdoc @@ -0,0 +1,124 @@ +/**************************************************************************** +** +** Copyright (C) 2021 Ivan Komissarov (abbapoh@gmail.com) +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:FDL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Free Documentation License Usage +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of +** this file. Please review the following information to ensure +** the GNU Free Documentation License version 1.3 requirements +** will be met: https://www.gnu.org/licenses/fdl-1.3.html. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! + \qmltype qbspkgconfig + \inqmlmodule QbsModuleProviders + \since 1.20 + + \brief Module provider based on the qbspkg-config library. + + \QBS uses a built-in parser of the \c{*.pc} files and does not require the presence of the + \c pkg-config tool in the system. However, if the \c pkg-config tool is present, \QBS will + use the same libDirs as the system pkg-config uses by default; otherwise, a built-in list of + paths is used. + + In order to enable usage of this provider in your Product, set the + \l{Product::qbsModuleProviders}{qbsModuleProviders} property as shown in the example below: + \snippet ../examples/pkgconfig-provider/pkgconfig-provider.qbs 0 +*/ + +/*! + \qmlproperty string qbspkgconfig::executableFilePath + + The path to the \c {pkg-config} executable. If not set, the pkg-config from PATH is used. + + \defaultvalue undefined +*/ + +/*! + \qmlproperty stringList qbspkgconfig::libDirs + + Set this if you need to overwrite the default search directories. + \note You do not need to set this for cross-compilation in order to point + to the sysroot. \QBS does that for you. + + This property is the equivalent of the \c{PKG_CONFIG_LIBDIR} variable + for the \c{pkg-config} tool. + + \nodefaultvalue +*/ + +/*! + \qmlproperty stringList qbspkgconfig::extraPaths + + Set this if you need to add extra search directories. + + This property is the equivalent of the \c{PKG_CONFIG_PATH} variable + for the \c{pkg-config} tool. + + \nodefaultvalue +*/ + +/*! + \qmlproperty bool qbspkgconfig::staticMode + + If this property is \c true, then \QBS will include "private" libs and dependencies of the + package. This property is the equivalent of the + \c{--static} option for the \c{pkg-config} tool. + + Set this if your product is to be linked statically. + + \defaultvalue \c false +*/ + +/*! + \qmlproperty path qbspkgconfig::sysroot + + Set this property if you need to overwrite the default search sysroot path used by + \c pkg-config. + + This can be useful if \c pkg-config files are located in the directory other than qbs.sysroot. + This is the case on macOS platform - all XCode profiles are sysrooted to the SDK + directory, but \c pkg-config is typically intalled using Brew and resides in the + \c /usr/local directory. + + Setting this property to \c undefined or empty (\c "") value will use pkg-config's default + search paths: + \code + qbs build module-providers.pkgconfig.sysroot:undefined + \endcode + + This property is the equivalent of the \c{PKG_CONFIG_SYSROOT_DIR} variable for the + \c{pkg-config} tool. + + \defaultvalue \c "" on macOS, \c qbs.sysroot on other platforms +*/ + +/*! + \qmlproperty bool qbspkgconfig::mergeDependencies + + Holds whether dependencies should be merged by pkg-config or \QBS. + + If set to true, dependencies are merged by pkg-config meaning each generated module + is self-contained and does not depend on other modules. If set to false, generated modules + may depend on other modules and property merging is done by \QBS. The latter approach gives + \QBS more information about dependencies, but may have performance implications during resolve + phase, e.g. when using ABSEIL library. + + \defaultvalue \c true +*/ diff --git a/doc/reference/module-providers/qt-module-provider.qdoc b/doc/reference/module-providers/qt-module-provider.qdoc new file mode 100644 index 000000000..cd77a0d12 --- /dev/null +++ b/doc/reference/module-providers/qt-module-provider.qdoc @@ -0,0 +1,76 @@ +/**************************************************************************** +** +** Copyright (C) 2021 Ivan Komissarov (abbapoh@gmail.com) +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:FDL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Free Documentation License Usage +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of +** this file. Please review the following information to ensure +** the GNU Free Documentation License version 1.3 requirements +** will be met: https://www.gnu.org/licenses/fdl-1.3.html. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! + \qmltype Qt + \inqmlmodule QbsModuleProviders + + \brief Module provider that generates Qt modules. + + Looking up a Qt installation happens via a \l{Module Providers}{module provider}. + By default, if a dependency to a Qt module is encountered, \QBS collects all Qt installations + it can find. This lookup happens by searching for \c qmake executables in the \c PATH + environment variable. Alternatively, you can explicitly tell \QBS which Qt + installations it should consider by setting the qmakeFilePaths + module provider \l{Parameterizing Module Providers}{property}. In that case, + the environment will be ignored. For instance, with the following Linux command line, + \QBS will build the project against a custom Qt instead of the standard one in \c{/usr/bin}: + \code + $ qbs moduleProviders.Qt.qmakeFilePaths:/opt/myqt/bin/qmake + \endcode + You can also set the module provider property in a profile. The simplest way to do + this is via the \l setup-qt tool. For examples of how to use this tool, see the + \l{Managing Qt Versions} section. + + This provider is activated automatically when encountering a dependency on the Qt + module and the \l{Product::qbsModuleProviders}{qbsModuleProviders} property + is \c undefined: + \code + CppApplication { + Depends { name: "Qt.core" } + files: "main.cpp" + } + \endcode + + Alternatively, you can activate this provider explicitly via the + \l{Product::qbsModuleProviders}{qbsModuleProviders} property: + \code + CppApplication { + Depends { name: "Qt.core" } + files: "main.cpp" + qbsModuleProviders: "Qt" + } + \endcode +*/ + +/*! + \qmlproperty stringList Qt::qmakeFilePaths + + List of paths to \c qmake executables. + + \defaultvalue undefined +*/ diff --git a/doc/reference/modules/qt-modules.qdoc b/doc/reference/modules/qt-modules.qdoc index 87e738b67..e3fee3c72 100644 --- a/doc/reference/modules/qt-modules.qdoc +++ b/doc/reference/modules/qt-modules.qdoc @@ -35,6 +35,8 @@ The \c{Qt.*} modules contain properties and rules for Qt. + Qt modules are generated by the \l[QML]{Qt} module provider. + \section1 Creating Dependencies to Qt Modules The Qt modules are grouped using the prefix \c Qt. If your product depends @@ -54,22 +56,6 @@ The Qt modules that have properties and relevant file tags are described in separate topics. - \section1 Qt-specific Module Provider Properties - - Looking up a Qt installation happens via a \l{Module Providers}{module provider}. - By default, if a dependency to a Qt module is encountered, \QBS collects all Qt installations - it can find. This lookup happens by searching for \c qmake executables in the \c PATH - environment variable. Alternatively, you can explicitly tell \QBS which Qt - installations it should consider by setting the \c Qt.qmakeFilePaths - \l{Parameterizing Module Providers}{module provider property}. In that case, - the environment will be ignored. For instance, with the following Linux command line, - \QBS will build the project against a custom Qt instead of the standard one in \c{/usr/bin}: - \code - $ qbs moduleProviders.Qt.qmakeFilePaths:/opt/myqt/bin/qmake - \endcode - You can also set the module provider property in a profile. The simplest way to do - this is via the \l setup-qt tool. - \section1 List of Submodules \table diff --git a/doc/reference/reference.qdoc b/doc/reference/reference.qdoc index 63b8c187d..f2992b287 100644 --- a/doc/reference/reference.qdoc +++ b/doc/reference/reference.qdoc @@ -75,6 +75,12 @@ */ /*! + \qmlmodule QbsModuleProviders + \title List of Module Providers + These are the module providers shipped with \QBS. +*/ + +/*! \group list-of-items \title List of All Items diff --git a/docker-compose.yml b/docker-compose.yml index 77a79f2b1..315330dbc 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -84,6 +84,17 @@ services: QT_VERSION: 6.0.0 ANDROID_NDK_VERSION: 23.0.7599858 + focal-android-620: + << : *linux + hostname: focal-android + image: ${DOCKER_USER:-qbsbuild}/qbsdev:focal-android-6.2.0-0 + build: + dockerfile: docker/focal/test-android.Dockerfile + context: . + args: + QT_VERSION: 6.2.0 + ANDROID_NDK_VERSION: 23.0.7599858 + focal-android-ndk-r19c: << : *linux hostname: focal-android diff --git a/docker/focal/test-android.Dockerfile b/docker/focal/test-android.Dockerfile index ca7223593..4f48dcf3f 100644 --- a/docker/focal/test-android.Dockerfile +++ b/docker/focal/test-android.Dockerfile @@ -115,7 +115,11 @@ RUN if [ "${QT_VERSION}" \< "5.14" ] || [ ! "${QT_VERSION}" \< "6.0.0" ]; then \ fi; \ if [ ! "${QT_VERSION}" \< "6.0.0" ]; then \ ./install-qt.sh --version ${QT_VERSION} qtbase qtdeclarative icu; \ - QT_COMPONENTS="qtbase qtdeclarative qttools qtquickcontrols2 qtquicktimeline"; \ + if [ "${QT_VERSION}" \< "6.1.0" ]; then \ + QT_COMPONENTS="qtbase qtdeclarative qttools qtquickcontrols2 qtquicktimeline svg"; \ + else \ + QT_COMPONENTS="qtbase qtdeclarative qttools qtquicktimeline svg"; \ + fi; \ else \ QT_COMPONENTS="qtbase qtdeclarative qttools qtimageformats"; \ fi; \ diff --git a/examples/pkgconfig-provider/main.c b/examples/pkgconfig-provider/main.c new file mode 100644 index 000000000..398b06842 --- /dev/null +++ b/examples/pkgconfig-provider/main.c @@ -0,0 +1,137 @@ +/**************************************************************************** +** +** MIT License + +** Copyright (c) 2017 Aleksander Alekseev + +** Permission is hereby granted, free of charge, to any person obtaining a copy +** of this software and associated documentation files (the "Software"), to deal +** in the Software without restriction, including without limitation the rights +** to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +** copies of the Software, and to permit persons to whom the Software is +** furnished to do so, subject to the following conditions: + +** The above copyright notice and this permission notice shall be included in all +** copies or substantial portions of the Software. + +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +** SOFTWARE. +** +****************************************************************************/ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <unistd.h> +#include <stdint.h> +#include <errno.h> +#include <limits.h> +#include <zlib.h> + +int main(int argc, char* argv[]) +{ + int res; + + if (argc < 2) + { + fprintf(stderr, "Usage: %s <fname>\n", argv[0]); + return 1; + } + + char* fname = argv[1]; + + struct stat file_stat; + res = stat(fname, &file_stat); + if (res == -1) + { + fprintf(stderr, "stat(...) failed, errno = %d\n", errno); + return 1; + } + + size_t temp_file_size = (size_t)file_stat.st_size; + if (temp_file_size >= INT_MAX) + { + fprintf(stderr, "Error: filze_size >= INT_MAX (%d)\n", INT_MAX); + return 1; + } + + int file_size = (int)temp_file_size; + int buff_size = file_size + 1; + void* file_buff = malloc(buff_size); + if (file_buff == NULL) + { + fprintf(stderr, "malloc(buff_size) failed, buff_size = %d\n", + file_size); + return 1; + } + + int fid = open(fname, O_RDONLY); + if (fid == -1) + { + fprintf(stderr, "open(...) failed, errno = %d\n", errno); + free(file_buff); + return 1; + } + + if (read(fid, file_buff, file_size) != file_size) + { + fprintf(stderr, "read(...) failed, errno = %d\n", errno); + free(file_buff); + close(fid); + return 1; + } + + close(fid); + + uLongf compress_buff_size = compressBound(file_size); + void* compress_buff = malloc(compress_buff_size); + if (compress_buff == NULL) + { + fprintf(stderr, + "malloc(compress_buff_size) failed, " + "compress_buff_size = %lu\n", + compress_buff_size); + free(file_buff); + return 1; + } + + uLongf compressed_size = compress_buff_size; + res = compress(compress_buff, &compressed_size, file_buff, file_size); + if (res != Z_OK) + { + fprintf(stderr, "compress(...) failed, res = %d\n", res); + free(compress_buff); + free(file_buff); + return 1; + } + + memset(file_buff, 0, buff_size); + uLongf decompressed_size = (uLongf)file_size; + res = uncompress(file_buff, &decompressed_size, + compress_buff, compressed_size); + if (res != Z_OK) + { + fprintf(stderr, "uncompress(...) failed, res = %d\n", res); + free(compress_buff); + free(file_buff); + return 1; + } + + printf( + "%s\n----------------\n" + "File size: %d, compress_buff_size: %lu, compressed_size: %lu, " + "decompressed_size: %lu\n", + (char*)file_buff, file_size, compress_buff_size, compressed_size, + decompressed_size); + + free(compress_buff); + free(file_buff); +} diff --git a/examples/pkgconfig-provider/pkgconfig-provider.qbs b/examples/pkgconfig-provider/pkgconfig-provider.qbs new file mode 100644 index 000000000..34ba32d03 --- /dev/null +++ b/examples/pkgconfig-provider/pkgconfig-provider.qbs @@ -0,0 +1,60 @@ +/**************************************************************************** +** +** Copyright (C) 2021 Ivan Komissarov (abbapoh@gmail.com) +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the examples of Qbs. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +//! [0] +CppApplication { + consoleApplication: true + Depends { name: "zlib"; required: false } + condition: zlib.present + name: "PkgConfigProviderExample" + files: "main.c" + qbsModuleProviders: ["qbspkgconfig"] +} +//! [0] diff --git a/share/qbs/module-providers/Qt/setup-qt.js b/share/qbs/module-providers/Qt/setup-qt.js index 422863b95..5298f8984 100644 --- a/share/qbs/module-providers/Qt/setup-qt.js +++ b/share/qbs/module-providers/Qt/setup-qt.js @@ -701,9 +701,13 @@ function doSetupLibraries(modInfo, qtProps, debugBuild, nonExistingPrlFiles, and && !modInfo.isStaticLibrary && qtProps.qtMajorVersion < 5; if (isNonStaticQt4OnWindows) prlFilePath = prlFilePath.slice(0, prlFilePath.length - 1); // The prl file base name does *not* contain the version number... + // qt for android versions 6.0 and 6.1 don't have the architecture suffix in the prl file if (androidAbi.length > 0 && modInfo.name !== "QtBootstrap" - && modInfo.name !== "QtQmlDevTools") { + && (modInfo.name !== "QtQmlDevTools" || modInfo.name === "QtQmlDevTools" + && Utilities.versionCompare(qtProps.qtVersion, "6.2") >= 0) + && (Utilities.versionCompare(qtProps.qtVersion, "6.0") < 0 + || Utilities.versionCompare(qtProps.qtVersion, "6.2") >= 0)) { prlFilePath += "_"; prlFilePath += androidAbi; } @@ -769,7 +773,10 @@ function doSetupLibraries(modInfo, qtProps, debugBuild, nonExistingPrlFiles, and if (++i < parts.length) frameworks.push(parts[i]); } else if (part === "-pthread") { - libs.push("pthread"); + // prl files for android have QMAKE_PRL_LIBS = -llog -pthread but the pthread + // functionality is included in libc. + if (androidAbi.length === 0) + libs.push("pthread"); } else if (part.startsWith('-')) { // Some other option console.debug("QMAKE_PRL_LIBS contains non-library option '" + part + "' in file '" + prlFilePath + "'"); @@ -786,7 +793,7 @@ function doSetupLibraries(modInfo, qtProps, debugBuild, nonExistingPrlFiles, and if (nonExistingPrlFiles.contains(prlFilePath)) return; nonExistingPrlFiles.push(prlFilePath); - if (!libFilePath && modInfo.mustExist) { + if (modInfo.mustExist) { console.warn("Could not open prl file '" + toNative(prlFilePath) + "' for module '" + modInfo.name + "' (" + e + "), and failed to deduce the library file path. " diff --git a/share/qbs/module-providers/Qt/templates/android_support.qbs b/share/qbs/module-providers/Qt/templates/android_support.qbs index 80315621e..ea19bba4a 100644 --- a/share/qbs/module-providers/Qt/templates/android_support.qbs +++ b/share/qbs/module-providers/Qt/templates/android_support.qbs @@ -16,6 +16,7 @@ Module { property bool verboseAndroidDeployQt: false property string _androidDeployQtFilePath: FileInfo.joinPaths(_qtBinaryDir, "bin", "androiddeployqt") + property string rccFilePath property string _qtBinaryDir property string _qtInstallDir // TODO: Remove in 1.21 @@ -208,7 +209,7 @@ Module { if (Utilities.versionCompare(product.Qt.android_support.version, "6.0") >= 0) { f.writeLine('"qml-importscanner-binary": "' + product.Qt.core.qmlImportScannerFilePath + '",'); - f.writeLine('"rcc-binary": "' + product.Qt.core.binPath + '/rcc' + '",'); + f.writeLine('"rcc-binary": "' + product.Qt.android_support.rccFilePath + '",'); if (inputs["qrc"] && inputs["qrc"].length > 0) { var qrcFiles = []; diff --git a/share/qbs/module-providers/Qt/templates/core.qbs b/share/qbs/module-providers/Qt/templates/core.qbs index e34274cfd..c3b26eb50 100644 --- a/share/qbs/module-providers/Qt/templates/core.qbs +++ b/share/qbs/module-providers/Qt/templates/core.qbs @@ -25,6 +25,7 @@ Module { Qt.android_support._qtBinaryDir: FileInfo.path(binPath) Qt.android_support._qtInstallDir: FileInfo.path(installPath) Qt.android_support.version: version + Qt.android_support.rccFilePath: Rcc.fullPath(product) } // qmlImportScanner is required by androiddeployqt even if the project doesn't // depend on qml. That's why the scannerName must be defined here and not in the diff --git a/share/qbs/module-providers/qbspkgconfig.qbs b/share/qbs/module-providers/qbspkgconfig.qbs new file mode 100644 index 000000000..52344d0b8 --- /dev/null +++ b/share/qbs/module-providers/qbspkgconfig.qbs @@ -0,0 +1,232 @@ +/**************************************************************************** +** +** Copyright (C) 2021 Ivan Komissarov (abbapoh@gmail.com) +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import qbs.Environment +import qbs.File +import qbs.FileInfo +import qbs.ModUtils +import qbs.PkgConfig +import qbs.Process +import qbs.TextFile + +ModuleProvider { + property string executableFilePath + property stringList extraPaths + property stringList libDirs + property bool staticMode: false + property path sysroot: { + if (qbs.targetOS.contains("macos")) + return ""; + return qbs.sysroot; + } + property bool mergeDependencies: true + + relativeSearchPaths: { + + // we need Probes in Providers... + function getPkgConfigExecutable(qbs) { + function splitNonEmpty(s, c) { return s.split(c).filter(function(e) { return e; }) } + function exeSuffix(qbs) { return qbs.hostOS.contains("windows") ? ".exe" : ""; } + + var pathValue = Environment.getEnv("PATH"); + if (!pathValue) + return undefined; + var dirs = splitNonEmpty(pathValue, qbs.pathListSeparator); + var suffix = exeSuffix(qbs); + var filePaths = []; + for (var i = 0; i < dirs.length; ++i) { + var candidate = FileInfo.joinPaths(dirs[i], "pkg-config" + suffix); + var canonicalCandidate = FileInfo.canonicalPath(candidate); + if (!canonicalCandidate || !File.exists(canonicalCandidate)) + continue; + return canonicalCandidate; + } + return undefined; + } + + function getModuleInfo(pkg, staticMode) { + var result = {}; + + var mapper = function(flag) { return flag.value; } + var typeFilter = function(type) { + return function(flag) { return flag.type === type; } + } + + function getLibsInfo(libs) { + var result = {}; + result.dynamicLibraries = libs.filter(typeFilter(PkgConfig.LibraryName)).map(mapper); + result.staticLibraries = + libs.filter(typeFilter(PkgConfig.StaticLibraryName)).map(mapper); + result.libraryPaths = libs.filter(typeFilter(PkgConfig.LibraryPath)).map(mapper); + result.frameworks = libs.filter(typeFilter(PkgConfig.Framework)).map(mapper); + result.frameworkPaths = + libs.filter(typeFilter(PkgConfig.FrameworkPath)).map(mapper); + result.driverLinkerFlags = + libs.filter(typeFilter(PkgConfig.LinkerFlag)).map(mapper); + return result; + } + + result.version = pkg.version; + result.includePaths = pkg.cflags.filter(typeFilter(PkgConfig.IncludePath)).map(mapper); + result.systemIncludePaths = + pkg.cflags.filter(typeFilter(PkgConfig.SystemIncludePath)).map(mapper); + result.defines = pkg.cflags.filter(typeFilter(PkgConfig.Define)).map(mapper); + result.commonCompilerFlags = + pkg.cflags.filter(typeFilter(PkgConfig.CompilerFlag)).map(mapper); + + var allLibs = pkg.libs; + if (staticMode) + allLibs = allLibs.concat(pkg.libsPrivate); + var libsInfo = getLibsInfo(allLibs); + for (var key in libsInfo) { + result[key] = libsInfo[key]; + } + + return result; + } + + function getModuleName(packageName) { return packageName.replace('.', '-'); } + + function getModuleDependencies(pkg, staticMode) { + var mapper = function(p) { + var result = {}; + for (var key in p) + result[key] = p[key]; + result.name = getModuleName(result.name); + return result; + } + var result = pkg.requires.map(mapper); + if (staticMode) + result = result.concat(pkg.requiresPrivate.map(mapper)); + return result; + } + + console.debug("Running pkgconfig provider.") + + var outputDir = FileInfo.joinPaths(outputBaseDir, "modules"); + File.makePath(outputDir); + + var options = {}; + options.libDirs = libDirs; + options.sysroot = sysroot; + options.staticMode = staticMode; + options.mergeDependencies = mergeDependencies; + options.extraPaths = extraPaths; + if (options.sysroot && !options.libDirs) { + options.libDirs = [ + sysroot + "/usr/lib/pkgconfig", + sysroot + "/usr/share/pkgconfig" + ]; + } + if (!options.libDirs) { + // if we have pkg-config installed, let's ask it for its search paths (since + // built-in search paths can differ between platforms) + var executable = executableFilePath ? executableFilePath : getPkgConfigExecutable(qbs); + if (executable) { + var p = new Process() + if (p.exec(executable, ['pkg-config', '--variable=pc_path']) === 0) { + var stdout = p.readStdOut().trim(); + // TODO: qbs.pathListSeparator? depends on what pkg-config prints on Windows + options.libDirs = stdout ? stdout.split(':'): []; + } + } + } + + var pkgConfig = new PkgConfig(options); + + var brokenPackages = []; + var packages = pkgConfig.packages(); + for (var packageName in packages) { + var pkg = packages[packageName]; + if (pkg.isBroken) { + brokenPackages.push(pkg); + continue; + } + var moduleName = getModuleName(packageName); + var moduleInfo = getModuleInfo(pkg, staticMode); + var deps = getModuleDependencies(pkg, staticMode); + + var moduleDir = FileInfo.joinPaths(outputDir, moduleName); + File.makePath(moduleDir); + var module = + new TextFile(FileInfo.joinPaths(moduleDir, "module.qbs"), TextFile.WriteOnly); + module.writeLine("Module {"); + module.writeLine(" version: " + ModUtils.toJSLiteral(moduleInfo.version)); + module.writeLine(" Depends { name: 'cpp' }"); + deps.forEach(function(dep) { + module.write(" Depends { name: '" + dep.name + "'"); + for (var k in dep) { + if (k === "name") + continue; + module.write("; " + k + ": " + ModUtils.toJSLiteral(dep[k])); + } + module.writeLine(" }"); + }) + function writeProperty(propertyName) { + var value = moduleInfo[propertyName]; + if (value.length !== 0) { // skip empty props for simplicity of the module file + module.writeLine( + " cpp." + propertyName + ":" + ModUtils.toJSLiteral(value)); + } + } + writeProperty("includePaths"); + writeProperty("systemIncludePaths"); + writeProperty("defines"); + writeProperty("commonCompilerFlags"); + writeProperty("dynamicLibraries"); + writeProperty("staticLibraries"); + writeProperty("libraryPaths"); + writeProperty("frameworks"); + writeProperty("frameworkPaths"); + writeProperty("driverLinkerFlags"); + module.writeLine("}"); + module.close(); + } + + if (brokenPackages.length !== 0) { + console.warn("Failed to load some pkg-config packages:"); + for (var i = 0; i < brokenPackages.length; ++i) { + console.warn(" " + brokenPackages[i].filePath + + ": " + brokenPackages[i].errorText); + } + } + + return ""; + } +} diff --git a/share/share.qbs b/share/share.qbs index b02e971f6..c5cf6893e 100644 --- a/share/share.qbs +++ b/share/share.qbs @@ -56,7 +56,7 @@ Product { Group { name: "Module providers" - files: ["qbs/module-providers/**/*"] + files: ["qbs/module-providers/*", "qbs/module-providers/**/*"] fileTags: ["qbs resources"] qbs.install: true qbs.installDir: qbsbuildconfig.resourcesInstallDir + "/share" diff --git a/src/lib/corelib/jsextensions/pkgconfigjs.cpp b/src/lib/corelib/jsextensions/pkgconfigjs.cpp index 4490a14a7..3bace6f06 100644 --- a/src/lib/corelib/jsextensions/pkgconfigjs.cpp +++ b/src/lib/corelib/jsextensions/pkgconfigjs.cpp @@ -40,6 +40,7 @@ #include "pkgconfigjs.h" #include <language/scriptengine.h> +#include <tools/version.h> #include <QtScript/qscriptengine.h> #include <QtScript/qscriptvalue.h> @@ -56,7 +57,7 @@ namespace { template<typename C, typename F> QVariantList convert(const C &c, F &&f) { QVariantList result; - result.reserve(c.size()); + result.reserve(int(c.size())); std::transform(c.begin(), c.end(), std::back_inserter(result), f); return result; } @@ -64,6 +65,7 @@ template<typename C, typename F> QVariantList convert(const C &c, F &&f) QVariantMap packageToMap(const PcPackage &package) { QVariantMap result; + result[QStringLiteral("isBroken")] = false; result[QStringLiteral("filePath")] = QString::fromStdString(package.filePath); result[QStringLiteral("baseFileName")] = QString::fromStdString(package.baseFileName); result[QStringLiteral("name")] = QString::fromStdString(package.name); @@ -82,9 +84,38 @@ QVariantMap packageToMap(const PcPackage &package) const auto requiredVersionToMap = [](const PcPackage::RequiredVersion &version) { + using Type = PcPackage::RequiredVersion::ComparisonType; QVariantMap result; result[QStringLiteral("name")] = QString::fromStdString(version.name); - result[QStringLiteral("version")] = QString::fromStdString(version.version); + const auto versionString = QString::fromStdString(version.version); + const auto qbsVersion = Version::fromString(QString::fromStdString(version.version)); + const auto nextQbsVersion = Version( + qbsVersion.majorVersion(), + qbsVersion.minorVersion(), + qbsVersion.patchLevel() + 1); + switch (version.comparison) { + case Type::LessThan: + result[QStringLiteral("versionBelow")] = versionString; + break; + case Type::GreaterThan: + result[QStringLiteral("versionAtLeast")] = nextQbsVersion.toString(); + break; + case Type::LessThanEqual: + result[QStringLiteral("versionBelow")] = nextQbsVersion.toString(); + break; + case Type::GreaterThanEqual: + result[QStringLiteral("versionAtLeast")] = versionString; + break; + case Type::Equal: + result[QStringLiteral("version")] = versionString; + break; + case Type::NotEqual: + result[QStringLiteral("versionBelow")] = versionString; + result[QStringLiteral("versionAtLeast")] = nextQbsVersion.toString(); + break; + case Type::AlwaysMatch: + break; + } result[QStringLiteral("comparison")] = QVariant::fromValue(qint32(version.comparison)); return result; }; @@ -103,11 +134,24 @@ QVariantMap packageToMap(const PcPackage &package) QVariantMap brokenPackageToMap(const PcBrokenPackage &package) { QVariantMap result; + result[QStringLiteral("isBroken")] = true; result[QStringLiteral("filePath")] = QString::fromStdString(package.filePath); + result[QStringLiteral("baseFileName")] = QString::fromStdString(package.baseFileName); result[QStringLiteral("errorText")] = QString::fromStdString(package.errorText); return result; } +QVariantMap packageVariantToMap(const PcPackageVariant &package) +{ + return package.visit([](const auto &value) { + using T = std::decay_t<decltype(value)>; + if constexpr (std::is_same_v<T, PcPackage>) + return packageToMap(value); + else + return brokenPackageToMap(value); + }); +} + PcPackage::VariablesMap envToVariablesMap(const QProcessEnvironment &env) { PcPackage::VariablesMap result; @@ -165,18 +209,19 @@ PkgConfigJs::PkgConfigJs( convertOptions(static_cast<ScriptEngine *>(engine)->environment(), options))) { Q_UNUSED(context); - for (const auto &package : m_pkgConfig->packages()) - m_packages.insert(QString::fromStdString(package.baseFileName), packageToMap(package)); - - for (const auto &package : m_pkgConfig->brokenPackages()) - m_brokenPackages.push_back(brokenPackageToMap(package)); + for (const auto &package : m_pkgConfig->packages()) { + m_packages.insert( + QString::fromStdString(package.getBaseFileName()), packageVariantToMap(package)); + } } PkgConfig::Options PkgConfigJs::convertOptions(const QProcessEnvironment &env, const QVariantMap &map) { PkgConfig::Options result; - result.searchPaths = - stringListToStdVector(map.value(QStringLiteral("searchPaths")).toStringList()); + result.libDirs = + stringListToStdVector(map.value(QStringLiteral("libDirs")).toStringList()); + result.extraPaths = + stringListToStdVector(map.value(QStringLiteral("extraPaths")).toStringList()); result.sysroot = map.value(QStringLiteral("sysroot")).toString().toStdString(); result.topBuildDir = map.value(QStringLiteral("topBuildDir")).toString().toStdString(); result.allowSystemLibraryPaths = @@ -189,6 +234,8 @@ PkgConfig::Options PkgConfigJs::convertOptions(const QProcessEnvironment &env, c std::back_inserter(result.systemLibraryPaths), [](const QString &str){ return str.toStdString(); }); result.disableUninstalled = map.value(QStringLiteral("disableUninstalled"), true).toBool(); + result.staticMode = map.value(QStringLiteral("staticMode"), false).toBool(); + result.mergeDependencies = map.value(QStringLiteral("mergeDependencies"), true).toBool(); result.globalVariables = variablesFromQVariantMap(map.value(QStringLiteral("globalVariables")).toMap()); result.systemVariables = envToVariablesMap(env); diff --git a/src/lib/corelib/jsextensions/pkgconfigjs.h b/src/lib/corelib/jsextensions/pkgconfigjs.h index 66575d8f3..462a8f2bd 100644 --- a/src/lib/corelib/jsextensions/pkgconfigjs.h +++ b/src/lib/corelib/jsextensions/pkgconfigjs.h @@ -66,11 +66,11 @@ public: StaticLibraryName = toUnderlying(PcPackage::Flag::Type::StaticLibraryName), Framework = toUnderlying(PcPackage::Flag::Type::Framework), FrameworkPath = toUnderlying(PcPackage::Flag::Type::FrameworkPath), - LinkerFlags = toUnderlying(PcPackage::Flag::Type::LinkerFlag), + LinkerFlag = toUnderlying(PcPackage::Flag::Type::LinkerFlag), IncludePath = toUnderlying(PcPackage::Flag::Type::IncludePath), SystemIncludePath = toUnderlying(PcPackage::Flag::Type::SystemIncludePath), Define = toUnderlying(PcPackage::Flag::Type::Define), - CompilerFlags = toUnderlying(PcPackage::Flag::Type::CompilerFlag), + CompilerFlag = toUnderlying(PcPackage::Flag::Type::CompilerFlag), }; Q_ENUM(FlagType); @@ -91,7 +91,6 @@ public: QScriptContext *context, QScriptEngine *engine, const QVariantMap &options = {}); Q_INVOKABLE QVariantMap packages() const { return m_packages; } - Q_INVOKABLE QVariantList brokenPackages() const { return m_brokenPackages; } // also used in tests static PkgConfig::Options convertOptions(const QProcessEnvironment &env, const QVariantMap &map); @@ -99,7 +98,6 @@ public: private: std::unique_ptr<PkgConfig> m_pkgConfig; QVariantMap m_packages; - QVariantList m_brokenPackages; }; } // namespace Internal diff --git a/src/lib/corelib/language/item.h b/src/lib/corelib/language/item.h index 16dd2016c..22cf6b810 100644 --- a/src/lib/corelib/language/item.h +++ b/src/lib/corelib/language/item.h @@ -83,6 +83,7 @@ public: bool isProduct; bool requiredValue = true; // base value of the required prop bool required; + bool fallbackEnabled = true; QVariantMap parameters; VersionRange versionRange; }; diff --git a/src/lib/corelib/language/moduleloader.cpp b/src/lib/corelib/language/moduleloader.cpp index 08528dcbe..6b800fa0b 100644 --- a/src/lib/corelib/language/moduleloader.cpp +++ b/src/lib/corelib/language/moduleloader.cpp @@ -2685,6 +2685,7 @@ void ModuleLoader::resolveDependsItem(DependsContext *dependsContext, Item *pare if (it != moduleResults->end()) { it->required = it->required || isRequired; it->requiredValue = it->requiredValue || isRequiredValue; + it->fallbackEnabled = it->fallbackEnabled && fallbackMode == FallbackMode::Enabled; it->versionRange.narrowDown(versionRange); continue; } @@ -2719,6 +2720,7 @@ void ModuleLoader::resolveDependsItem(DependsContext *dependsContext, Item *pare result.item = moduleItem; result.requiredValue = isRequiredValue; result.required = isRequired; + result.fallbackEnabled = fallbackMode == FallbackMode::Enabled; result.parameters = defaultParameters; result.versionRange = versionRange; moduleResults->push_back(result); @@ -3771,6 +3773,7 @@ void ModuleLoader::collectAllModules(Item *item, std::vector<Item::Module> *modu if (m.required) it->required = true; it->versionRange.narrowDown(m.versionRange); + it->fallbackEnabled = it->fallbackEnabled && m.fallbackEnabled; continue; } modules->push_back(m); @@ -3884,7 +3887,7 @@ void ModuleLoader::addTransitiveDependencies(ProductContext *ctx) if (module.isProduct) { ctx->item->addModule(module); } else { - const FallbackMode fallbackMode = m_parameters.fallbackProviderEnabled() + const FallbackMode fallbackMode = module.fallbackEnabled ? FallbackMode::Enabled : FallbackMode::Disabled; Item::Module dep; dep.item = loadModule(ctx, nullptr, ctx->item, ctx->item->location(), QString(), @@ -3899,6 +3902,7 @@ void ModuleLoader::addTransitiveDependencies(ProductContext *ctx) dep.name = module.name; dep.required = module.required; dep.versionRange = module.versionRange; + dep.fallbackEnabled = fallbackMode == FallbackMode::Enabled; ctx->item->addModule(dep); } } diff --git a/src/lib/corelib/language/modulemerger.cpp b/src/lib/corelib/language/modulemerger.cpp index c5deaae04..6ad8e2259 100644 --- a/src/lib/corelib/language/modulemerger.cpp +++ b/src/lib/corelib/language/modulemerger.cpp @@ -130,6 +130,7 @@ void ModuleMerger::start() m.item = m_mergedModule.item; m.required = m_mergedModule.required; m.versionRange = m_mergedModule.versionRange; + m.fallbackEnabled = m_mergedModule.fallbackEnabled; } modules << m; } @@ -207,6 +208,8 @@ void ModuleMerger::mergeModule(Item::PropertyMap *dstProps, const Item::Module & // Update dependency constraints if (dep->required) m_mergedModule.required = true; + // if one dep has fallback disabled, disable it for the merged module + m_mergedModule.fallbackEnabled = m_mergedModule.fallbackEnabled && dep->fallbackEnabled; m_mergedModule.versionRange.narrowDown(dep->versionRange); // We need to touch the unmerged module instances later once more diff --git a/src/lib/corelib/tools/qttools.cpp b/src/lib/corelib/tools/qttools.cpp index 4a82bc5e0..c2ef91f16 100644 --- a/src/lib/corelib/tools/qttools.cpp +++ b/src/lib/corelib/tools/qttools.cpp @@ -47,8 +47,9 @@ size_t hash<QVariant>::operator()(const QVariant &v) const noexcept { switch (v.userType()) { case QMetaType::UnknownType: return 0; + case QMetaType::Bool: return std::hash<bool>()(v.toBool()); case QMetaType::Int: return std::hash<int>()(v.toInt()); - case QMetaType::UInt: return std::hash<int>()(v.toUInt()); + case QMetaType::UInt: return std::hash<uint>()(v.toUInt()); case QMetaType::QString: return std::hash<QString>()(v.toString()); case QMetaType::QStringList: return std::hash<QStringList>()(v.toStringList()); case QMetaType::QVariantList: return std::hash<QVariantList>()(v.toList()); diff --git a/src/lib/pkgconfig/CMakeLists.txt b/src/lib/pkgconfig/CMakeLists.txt index a39ee5564..e64d934c8 100644 --- a/src/lib/pkgconfig/CMakeLists.txt +++ b/src/lib/pkgconfig/CMakeLists.txt @@ -8,12 +8,17 @@ set(SOURCES ) list_transform_prepend(SOLUTION_SOURCES solution/) -if(APPLE) +if(APPLE OR MINGW) set(HAS_STD_FILESYSTEM "0") else() set(HAS_STD_FILESYSTEM "1") endif() +set(QBS_PKGCONFIG_PUBLIC_DEPENDS "") +if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU" AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 8.0) + set(QBS_PKGCONFIG_PUBLIC_DEPENDS "stdc++fs") +endif() + add_qbs_library(qbspkgconfig STATIC DEFINES @@ -22,6 +27,6 @@ add_qbs_library(qbspkgconfig "HAS_STD_FILESYSTEM=${HAS_STD_FILESYSTEM}" PUBLIC_DEFINES "QBS_PC_WITH_QT_SUPPORT=1" - PUBLIC_DEPENDS Qt${QT_VERSION_MAJOR}::Core + PUBLIC_DEPENDS Qt${QT_VERSION_MAJOR}::Core ${QBS_PKGCONFIG_PUBLIC_DEPENDS} qbsvariant SOURCES ${SOURCES} ) diff --git a/src/lib/pkgconfig/pcpackage.h b/src/lib/pkgconfig/pcpackage.h index df6905185..340a4698f 100644 --- a/src/lib/pkgconfig/pcpackage.h +++ b/src/lib/pkgconfig/pcpackage.h @@ -40,12 +40,15 @@ #ifndef PC_PACKAGE_H #define PC_PACKAGE_H +#include <variant.h> + #include <map> #include <optional> #include <stdexcept> #include <string> #include <unordered_map> #include <unordered_set> +#include <utility> #include <vector> namespace qbs { @@ -123,15 +126,71 @@ class PcBrokenPackage { public: std::string filePath; + std::string baseFileName; std::string errorText; }; +class PcPackageVariant: public Variant::variant<PcPackage, PcBrokenPackage> +{ +public: + using Base = Variant::variant<PcPackage, PcBrokenPackage>; + using Base::Base; + + bool isValid() const noexcept { return index() == 0; } + bool isBroken() const noexcept { return index() == 1; } + + const PcPackage &asPackage() const { return Variant::get<PcPackage>(*this); } + PcPackage &asPackage() { return Variant::get<PcPackage>(*this); } + + const PcBrokenPackage &asBrokenPackage() const { return Variant::get<PcBrokenPackage>(*this); } + PcBrokenPackage &asBrokenPackage() { return Variant::get<PcBrokenPackage>(*this); } + + template<typename F> + decltype(auto) visit(F &&f) const + { + return Variant::visit(std::forward<F>(f), static_cast<const Base &>(*this)); + } + + template<typename F> + decltype(auto) visit(F &&f) + { + return Variant::visit(std::forward<F>(f), static_cast<Base &>(*this)); + } + + const std::string &getBaseFileName() const + { + return visit([](auto &&value) noexcept -> const std::string & { + return value.baseFileName; + }); + } +}; + class PcException: public std::runtime_error { public: explicit PcException(const std::string &message) : std::runtime_error(message) {} }; +inline bool operator==(const PcPackage::Flag &lhs, const PcPackage::Flag &rhs) +{ + return lhs.type == rhs.type && lhs.value == rhs.value; +} + +inline bool operator!=(const PcPackage::Flag &lhs, const PcPackage::Flag &rhs) +{ + return !(lhs == rhs); +} + } // namespace qbs +namespace std { +template<> struct hash<qbs::PcPackage::Flag> +{ + size_t operator()(const qbs::PcPackage::Flag &v) const noexcept + { + return hash<std::string>()(v.value) + size_t(v.type); + } +}; +} // namespace std + #endif // PC_PACKAGE_H diff --git a/src/lib/pkgconfig/pcparser.cpp b/src/lib/pkgconfig/pcparser.cpp index 7ce77618a..b3ba57c3d 100644 --- a/src/lib/pkgconfig/pcparser.cpp +++ b/src/lib/pkgconfig/pcparser.cpp @@ -42,7 +42,15 @@ #include "pkgconfig.h" #if HAS_STD_FILESYSTEM -#include <filesystem> +# if __has_include(<filesystem>) +# include <filesystem> +# else +# include <experimental/filesystem> +// We need the alias from std::experimental::filesystem to std::filesystem +namespace std { + namespace filesystem = experimental::filesystem; +} +# endif #else #include <QFileInfo> #endif @@ -385,7 +393,7 @@ std::string baseName(const std::string_view &filePath) auto pos = filePath.rfind('/'); const auto fileName = pos == std::string_view::npos ? std::string_view() : filePath.substr(pos + 1); - pos = fileName.find('.'); + pos = fileName.rfind('.'); return std::string(pos == std::string_view::npos ? std::string_view() : fileName.substr(0, pos)); @@ -399,7 +407,8 @@ PcParser::PcParser(const PkgConfig &pkgConfig) } -PcPackage PcParser::parsePackageFile(const std::string &path) +PcPackageVariant PcParser::parsePackageFile(const std::string &path) +try { PcPackage package; @@ -426,6 +435,8 @@ PcPackage PcParser::parsePackageFile(const std::string &path) while (readOneLine(file, line)) parseLine(package, line); return package; +} catch(const PcException &ex) { + return PcBrokenPackage{path, baseName(path), ex.what()}; } std::string PcParser::trimAndSubstitute(const PcPackage &pkg, std::string_view str) const @@ -753,8 +764,7 @@ void PcParser::parseLine(PcPackage &pkg, std::string_view str) // ignore this feature for now const auto value = trimAndSubstitute(pkg, str); - const auto [it, ok] = pkg.vars.insert({std::string(tag), value}); - if (!ok) + if (!pkg.vars.insert({std::string(tag), value}).second) raizeDuplicateVariableException(pkg, tag); } } diff --git a/src/lib/pkgconfig/pcparser.h b/src/lib/pkgconfig/pcparser.h index 8443629a6..ffdf86aaa 100644 --- a/src/lib/pkgconfig/pcparser.h +++ b/src/lib/pkgconfig/pcparser.h @@ -51,7 +51,7 @@ class PcParser public: explicit PcParser(const PkgConfig &pkgConfig); - PcPackage parsePackageFile(const std::string &path); + PcPackageVariant parsePackageFile(const std::string &path); private: std::string trimAndSubstitute(const PcPackage &pkg, std::string_view str) const; diff --git a/src/lib/pkgconfig/pkgconfig.cpp b/src/lib/pkgconfig/pkgconfig.cpp index 871dff99c..f017b365c 100644 --- a/src/lib/pkgconfig/pkgconfig.cpp +++ b/src/lib/pkgconfig/pkgconfig.cpp @@ -41,7 +41,15 @@ #include "pcparser.h" #if HAS_STD_FILESYSTEM -# include <filesystem> +# if __has_include(<filesystem>) +# include <filesystem> +# else +# include <experimental/filesystem> +// We need the alias from std::experimental::filesystem to std::filesystem +namespace std { + namespace filesystem = experimental::filesystem; +} +# endif #else # include <QtCore/QDir> # include <QtCore/QFileInfo> @@ -98,6 +106,50 @@ constexpr inline char listSeparator() noexcept #endif } +// based on https://stackoverflow.com/a/33135699/295518 +int compareVersions(std::string_view v1, std::string_view v2) +{ + for (size_t i = 0, j = 0; i < v1.length() || j < v2.length(); ) { + size_t acc1 = 0; + size_t acc2 = 0; + + while (i < v1.length() && v1[i] != '.') { + acc1 = acc1 * 10 + (v1[i] - '0'); + i++; + } + while (j < v2.length() && v2[j] != '.') { + acc2 = acc2 * 10 + (v2[j] - '0'); + j++; + } + + if (acc1 < acc2) + return -1; + if (acc1 > acc2) + return +1; + + ++i; + ++j; + } + return 0; +} + +using ComparisonType = PcPackage::RequiredVersion::ComparisonType; + +bool versionTest(ComparisonType comparison, std::string_view a, std::string_view b) +{ + switch (comparison) { + case ComparisonType::LessThan: return compareVersions(a, b) < 0; + case ComparisonType::GreaterThan: return compareVersions(a, b) > 0; + case ComparisonType::LessThanEqual: return compareVersions(a, b) <= 0; + case ComparisonType::GreaterThanEqual: return compareVersions(a, b) >= 0; + case ComparisonType::Equal: return compareVersions(a, b) == 0; + case ComparisonType::NotEqual: return compareVersions(a, b) != 0; + case ComparisonType::AlwaysMatch: return true; + } + + return false; +} + [[noreturn]] void raizeUnknownPackageException(std::string_view package) { std::string message; @@ -107,6 +159,13 @@ constexpr inline char listSeparator() noexcept throw PcException(message); } +template <class C> +C &operator<<(C &container, const C &other) +{ + container.insert(container.end(), other.cbegin(), other.cend()); + return container; +} + } // namespace PkgConfig::PkgConfig() @@ -117,8 +176,8 @@ PkgConfig::PkgConfig() PkgConfig::PkgConfig(Options options) : m_options(std::move(options)) { - if (m_options.searchPaths.empty()) - m_options.searchPaths = split(PKG_CONFIG_PC_PATH, listSeparator()); + if (m_options.libDirs.empty()) + m_options.libDirs = split(PKG_CONFIG_PC_PATH, listSeparator()); if (m_options.topBuildDir.empty()) m_options.topBuildDir = "$(top_builddir)"; // pkg-config sets this for automake =) @@ -133,19 +192,27 @@ PkgConfig::PkgConfig(Options options) m_options.globalVariables["pc_sysrootdir"] = m_options.sysroot; m_options.globalVariables["pc_top_builddir"] = m_options.topBuildDir; - std::tie(m_packages, m_brokenPackages) = findPackages(); + m_packages = findPackages(); } -const PcPackage &PkgConfig::getPackage(std::string_view baseFileName) const +const PcPackageVariant &PkgConfig::getPackage(std::string_view baseFileName) const { // heterogeneous comparator so we can search the package using string_view - const auto lessThan = [](const PcPackage &package, const std::string_view &name) + const auto lessThan = [](const PcPackageVariant &package, const std::string_view &name) { - return package.baseFileName < name; + return package.visit([name](auto &&value) noexcept { + return value.baseFileName < name; + }); + }; + + const auto testPackage = [baseFileName](const PcPackageVariant &package) { + return package.visit([baseFileName](auto &&value) noexcept { + return baseFileName != value.baseFileName; + }); }; const auto it = std::lower_bound(m_packages.begin(), m_packages.end(), baseFileName, lessThan); - if (it == m_packages.end() || baseFileName != it->baseFileName) + if (it == m_packages.end() || testPackage(*it)) raizeUnknownPackageException(baseFileName); return *it; } @@ -184,7 +251,7 @@ std::vector<std::string> getPcFilePaths(const std::vector<std::string> &searchPa std::vector<std::filesystem::path> paths; for (const auto &searchPath : searchPaths) { - if (!std::filesystem::directory_entry(searchPath).exists()) + if (!std::filesystem::exists(std::filesystem::directory_entry(searchPath).status())) continue; const auto dir = std::filesystem::directory_iterator(searchPath); std::copy_if( @@ -221,10 +288,163 @@ std::vector<std::string> getPcFilePaths(const std::vector<std::string> &searchPa } #endif -std::pair<PkgConfig::Packages, PkgConfig::BrokenPackages> PkgConfig::findPackages() const +PcBrokenPackage makeMissingDependency( + const PcPackage &package, const PcPackage::RequiredVersion &depVersion) +{ + std::string message; + message += "Package "; + message += package.name; + message += " requires package "; + message += depVersion.name; + message += " but it is not found"; + return PcBrokenPackage{ + package.filePath, package.baseFileName, std::move(message)}; +} + +PcBrokenPackage makeBrokenDependency( + const PcPackage &package, const PcPackage::RequiredVersion &depVersion) +{ + std::string message; + message += "Package "; + message += package.name; + message += " requires package "; + message += depVersion.name; + message += " but it is broken"; + return PcBrokenPackage{ + package.filePath, package.baseFileName, std::move(message)}; +} + +PcBrokenPackage makeVersionMismatchDependency( + const PcPackage &package, + const PcPackage &depPackage, + const PcPackage::RequiredVersion &depVersion) +{ + std::string message; + message += "Package "; + message += package.name; + message += " requires version "; + message += PcPackage::RequiredVersion::comparisonToString( + depVersion.comparison); + message += depVersion.version; + message += " but "; + message += depPackage.version; + message += " is present"; + return PcBrokenPackage{ + package.filePath, package.baseFileName, std::move(message)}; +} + +PkgConfig::Packages PkgConfig::mergeDependencies(const PkgConfig::Packages &packages) const +{ + std::unordered_map<std::string_view, const PcPackageVariant *> packageHash; + + struct MergedHashEntry + { + PcPackageVariant package; // merged package or broken package + std::vector<const PcPackage *> deps; // unmerged transitive deps, including Package itself + }; + std::unordered_map<std::string, MergedHashEntry> mergedHash; + + for (const auto &package: packages) + packageHash[package.getBaseFileName()] = &package; + + auto func = [&](const PcPackageVariant &package, auto &f) -> const MergedHashEntry & + { + const auto it = mergedHash.find(package.getBaseFileName()); + if (it != mergedHash.end()) + return it->second; + + auto &entry = mergedHash[package.getBaseFileName()]; + + auto visitor = [&](auto &&package) -> PcPackageVariant { + + using T = std::decay_t<decltype(package)>; + if constexpr (std::is_same_v<T, PcPackage>) { // NOLINT + + using Flags = std::vector<PcPackage::Flag>; + + // returns true if multiple copies of the flag can present in the same package + // we can't properly merge flags that have multiple parameters except for + // -framework which we handle correctly. + auto canHaveDuplicates = [](const PcPackage::Flag::Type &type) { + return type == PcPackage::Flag::Type::LinkerFlag + || type == PcPackage::Flag::Type::CompilerFlag; + }; + + std::unordered_set<PcPackage::Flag> visitedFlags; + // appends only those flags to the target that were not seen before (except for + // ones that can have duplicates) + auto mergeFlags = [&](Flags &target, const Flags &source) + { + for (const auto &flag: source) { + if (canHaveDuplicates(flag.type) || visitedFlags.insert(flag).second) + target.push_back(flag); + } + }; + + std::unordered_set<const PcPackage *> visitedDeps; + + PcPackage result; + // copy only meta info for now + result.filePath = package.filePath; + result.baseFileName = package.baseFileName; + result.name = package.name; + result.version = package.version; + result.description = package.description; + result.url = package.url; + + auto allDependencies = package.requiresPublic; + if (m_options.staticMode) + allDependencies << package.requiresPrivate; + + for (const auto &dependency: allDependencies) { + const auto it = packageHash.find(dependency.name); + if (it == packageHash.end()) + return makeMissingDependency(result, dependency); + + const auto childEntry = f(*it->second, f); + if (childEntry.package.isBroken()) + return makeBrokenDependency(result, dependency); + + const auto &mergedPackage = childEntry.package.asPackage(); + const bool versionOk = versionTest( + dependency.comparison, mergedPackage.version, dependency.version); + if (!versionOk) + return makeVersionMismatchDependency(result, mergedPackage, dependency); + + for (const auto *dep: childEntry.deps) { + if (visitedDeps.insert(dep).second) + entry.deps.push_back(dep); + } + } + + entry.deps.push_back(&package); + + for (const auto *dep: entry.deps) { + mergeFlags(result.libs, dep->libs); + mergeFlags(result.cflags, dep->cflags); + } + + return result; + } + return package; + }; + entry.package = package.visit(visitor); + + return entry; + }; + + for (auto &package: packages) + func(package, func); + + Packages result; + for (auto &[key, value]: mergedHash) + result.push_back(std::move(value.package)); + return result; +} + +PkgConfig::Packages PkgConfig::findPackages() const { Packages result; - BrokenPackages brokenResult; PcParser parser(*this); const auto systemLibraryPaths = !m_options.allowSystemLibraryPaths ? @@ -232,7 +452,10 @@ std::pair<PkgConfig::Packages, PkgConfig::BrokenPackages> PkgConfig::findPackage m_options.systemLibraryPaths.begin(), m_options.systemLibraryPaths.end()) : std::unordered_set<std::string>(); - const auto pcFilePaths = getPcFilePaths(m_options.searchPaths); + auto allSearchPaths = m_options.extraPaths; + allSearchPaths.insert( + allSearchPaths.end(), m_options.libDirs.begin(), m_options.libDirs.end()); + const auto pcFilePaths = getPcFilePaths(allSearchPaths); for (const auto &pcFilePath : pcFilePaths) { if (m_options.disableUninstalled) { @@ -240,26 +463,30 @@ std::pair<PkgConfig::Packages, PkgConfig::BrokenPackages> PkgConfig::findPackage continue; } - try { - result.emplace_back( - parser.parsePackageFile(pcFilePath) + auto pkg = parser.parsePackageFile(pcFilePath); + pkg.visit([&](auto &value) { + using T = std::decay_t<decltype(value)>; + if constexpr (std::is_same_v<T, PcPackage>) { // NOLINT + value = std::move(value) // Weird, but pkg-config removes libs first and only then appends // sysroot. Looks like sysroot has to be used with // allowSystemLibraryPaths: true .removeSystemLibraryPaths(systemLibraryPaths) - .prependSysroot(m_options.sysroot)); - } catch (const PcException &ex) { - // not sure if it's OK to use exceptions for handling errors like - brokenResult.push_back(PcBrokenPackage{pcFilePath, ex.what()}); - } + .prependSysroot(m_options.sysroot); + } + }); + result.emplace_back(std::move(pkg)); } - const auto lessThanPackage = [](const PcPackage &lhs, const PcPackage &rhs) + if (m_options.mergeDependencies) + result = mergeDependencies(result); + + const auto lessThanPackage = [](const PcPackageVariant &lhs, const PcPackageVariant &rhs) { - return lhs.baseFileName < rhs.baseFileName; + return lhs.getBaseFileName() < rhs.getBaseFileName(); }; std::sort(result.begin(), result.end(), lessThanPackage); - return {result, brokenResult}; + return result; } } // namespace qbs diff --git a/src/lib/pkgconfig/pkgconfig.h b/src/lib/pkgconfig/pkgconfig.h index 17b5ea9fa..6da1f053f 100644 --- a/src/lib/pkgconfig/pkgconfig.h +++ b/src/lib/pkgconfig/pkgconfig.h @@ -50,37 +50,38 @@ public: struct Options { using VariablesMap = PcPackage::VariablesMap; - std::vector<std::string> searchPaths; // PKG_CONFIG_PATH, PKG_CONFIG_LIBDIR + std::vector<std::string> libDirs; // PKG_CONFIG_LIBDIR + std::vector<std::string> extraPaths; // PKG_CONFIG_PATH std::string sysroot; // PKG_CONFIG_SYSROOT_DIR std::string topBuildDir; // PKG_CONFIG_TOP_BUILD_DIR bool allowSystemLibraryPaths{false}; // PKG_CONFIG_ALLOW_SYSTEM_LIBS std::vector<std::string> systemLibraryPaths; // PKG_CONFIG_SYSTEM_LIBRARY_PATH bool disableUninstalled{true}; // PKG_CONFIG_DISABLE_UNINSTALLED + bool staticMode{false}; + bool mergeDependencies{true}; VariablesMap globalVariables; VariablesMap systemVariables; }; - using Packages = std::vector<PcPackage>; - using BrokenPackages = std::vector<PcBrokenPackage>; + using Packages = std::vector<PcPackageVariant>; explicit PkgConfig(); explicit PkgConfig(Options options); const Options &options() const { return m_options; } const Packages &packages() const { return m_packages; } - const BrokenPackages &brokenPackages() const { return m_brokenPackages; } - const PcPackage &getPackage(std::string_view baseFileName) const; + const PcPackageVariant &getPackage(std::string_view baseFileName) const; std::string_view packageGetVariable(const PcPackage &pkg, std::string_view var) const; private: - std::pair<Packages, BrokenPackages> findPackages() const; + Packages findPackages() const; + Packages mergeDependencies(const Packages &packages) const; private: Options m_options; Packages m_packages; - BrokenPackages m_brokenPackages; }; } // namespace qbs diff --git a/src/lib/pkgconfig/pkgconfig.pro b/src/lib/pkgconfig/pkgconfig.pro index 7c1560ffd..dcabf0ba1 100644 --- a/src/lib/pkgconfig/pkgconfig.pro +++ b/src/lib/pkgconfig/pkgconfig.pro @@ -1,12 +1,13 @@ TARGET = qbspkgconfig include(../staticlibrary.pri) +include(../../shared/variant/variant.pri) DEFINES += \ PKG_CONFIG_PC_PATH=\\\"/usr/lib/pkgconfig:/usr/share/pkgconfig\\\" \ - PKG_CONFIG_SYSTEM_LIBRARY_PATH=\\\"/usr/${QBS_LIBDIR_NAME}/\\\" \ + PKG_CONFIG_SYSTEM_LIBRARY_PATH=\\\"/usr/$${QBS_LIBRARY_DIRNAME}/\\\" \ QBS_PC_WITH_QT_SUPPORT=1 -macos { +macos|win32-g++ { DEFINES += HAS_STD_FILESYSTEM=0 } else { DEFINES += HAS_STD_FILESYSTEM=1 diff --git a/src/lib/pkgconfig/pkgconfig.qbs b/src/lib/pkgconfig/pkgconfig.qbs index 25bcb3fdf..0be5065f5 100644 --- a/src/lib/pkgconfig/pkgconfig.qbs +++ b/src/lib/pkgconfig/pkgconfig.qbs @@ -4,6 +4,7 @@ import qbs.Utilities QbsStaticLibrary { Depends { name: "cpp" } Depends { name: "qbsbuildconfig" } + Depends { name: "qbsvariant" } property stringList pcPaths: { var result = []; @@ -56,6 +57,12 @@ QbsStaticLibrary { Export { Depends { name: "cpp" } + Depends { name: "qbsvariant" } cpp.defines: exportingProduct.publicDefines + cpp.staticLibraries: { + if (qbs.toolchainType === "gcc" && cpp.compilerVersionMajor === 7) + return ["stdc++fs"]; + return []; + } } } diff --git a/src/lib/pkgconfig/use_pkgconfig.pri b/src/lib/pkgconfig/use_pkgconfig.pri index baccff360..e0e485e46 100644 --- a/src/lib/pkgconfig/use_pkgconfig.pri +++ b/src/lib/pkgconfig/use_pkgconfig.pri @@ -1,4 +1,5 @@ include(../../library_dirname.pri) +include(../../shared/variant/variant.pri) isEmpty(QBSLIBDIR) { QBSLIBDIR = $${OUT_PWD}/../../../$${QBS_LIBRARY_DIRNAME} @@ -28,6 +29,16 @@ win32 { LIBS += $${QBSPKGCONFIG_LIB} } +gcc { + isEmpty(COMPILER_VERSION) { + COMPILER_VERSION = $$system($$QMAKE_CXX " -dumpversion") + COMPILER_MAJOR_VERSION = $$str_member($$COMPILER_VERSION) + equals(COMPILER_MAJOR_VERSION, 7) { + LIBS += -lstdc++fs + } + } +} + INCLUDEPATH += \ $$PWD diff --git a/src/shared/CMakeLists.txt b/src/shared/CMakeLists.txt index 7a340d53d..b77e01b4e 100644 --- a/src/shared/CMakeLists.txt +++ b/src/shared/CMakeLists.txt @@ -1 +1,2 @@ add_subdirectory(json) +add_subdirectory(variant) diff --git a/src/shared/variant/CMakeLists.txt b/src/shared/variant/CMakeLists.txt new file mode 100644 index 000000000..5b2ccfb11 --- /dev/null +++ b/src/shared/variant/CMakeLists.txt @@ -0,0 +1,4 @@ +add_qbs_library(qbsvariant + INTERFACE + SOURCES variant.h variant.hpp + ) diff --git a/src/shared/variant/LICENSE.md b/src/shared/variant/LICENSE.md new file mode 100644 index 000000000..36b7cd93c --- /dev/null +++ b/src/shared/variant/LICENSE.md @@ -0,0 +1,23 @@ +Boost Software License - Version 1.0 - August 17th, 2003 + +Permission is hereby granted, free of charge, to any person or organization +obtaining a copy of the software and accompanying documentation covered by +this license (the "Software") to use, reproduce, display, distribute, +execute, and transmit the Software, and to prepare derivative works of the +Software, and to permit third-parties to whom the Software is furnished to +do so, all subject to the following: + +The copyright notices in the Software and this entire statement, including +the above license grant, this restriction and the following disclaimer, +must be included in all copies of the Software, in whole or in part, and +all derivative works of the Software, unless such copies or derivative +works are solely in the form of machine-executable object code generated by +a source language processor. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT +SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE +FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/src/shared/variant/README.md b/src/shared/variant/README.md new file mode 100644 index 000000000..6644286d2 --- /dev/null +++ b/src/shared/variant/README.md @@ -0,0 +1,37 @@ +# MPark.Variant + +> __C++17__ `std::variant` for __C++11__/__14__/__17__ + +[![release][badge.release]][release] +[![header][badge.header]][header] +[![travis][badge.travis]][travis] +[![appveyor][badge.appveyor]][appveyor] +[![license][badge.license]][license] +[![godbolt][badge.godbolt]][godbolt] +[![wandbox][badge.wandbox]][wandbox] + +[badge.release]: https://img.shields.io/github/release/mpark/variant.svg +[badge.header]: https://img.shields.io/badge/single%20header-master-blue.svg +[badge.travis]: https://travis-ci.org/mpark/variant.svg?branch=master +[badge.appveyor]: https://ci.appveyor.com/api/projects/status/github/mpark/variant?branch=master&svg=true +[badge.license]: https://img.shields.io/badge/license-boost-blue.svg +[badge.godbolt]: https://img.shields.io/badge/try%20it-on%20godbolt-222266.svg +[badge.wandbox]: https://img.shields.io/badge/try%20it-on%20wandbox-5cb85c.svg + +[release]: https://github.com/mpark/variant/releases/latest +[header]: https://github.com/mpark/variant/blob/single-header/master/variant.hpp +[travis]: https://travis-ci.org/mpark/variant +[appveyor]: https://ci.appveyor.com/project/mpark/variant +[license]: https://github.com/mpark/variant/blob/master/LICENSE.md +[godbolt]: https://godbolt.org/g/1qYDAK +[wandbox]: https://wandbox.org/permlink/QV3gZ2KQQNwgoFIB + +## Single Header + +This branch provides a standalone `variant.hpp` file for each +[release](https://github.com/mpark/variant/releases). +Copy it and `#include` away! + +## License + +Distributed under the [Boost Software License, Version 1.0](LICENSE.md). diff --git a/src/shared/variant/variant.h b/src/shared/variant/variant.h new file mode 100644 index 000000000..ecaf81ca6 --- /dev/null +++ b/src/shared/variant/variant.h @@ -0,0 +1,63 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#pragma once + +/* + See std(::experimental)::variant. +*/ + +// std::variant from Apple's Clang supports methods that throw std::bad_optional_access only +// with deployment target >= macOS 10.14 +// TODO: Use std::variant everywhere when we can require macOS 10.14 +#if !defined(__apple_build_version__) +#include <variant> + +namespace qbs { +namespace Variant { +using std::get; +using std::get_if; +using std::holds_alternative; +using std::variant; +using std::variant_alternative_t; +using std::visit; +} // namespace Variant +} // namespace qbs + +#else +#include "variant.hpp" + +namespace qbs { +namespace Variant { +using mpark::get; +using mpark::get_if; +using mpark::holds_alternative; +using mpark::variant; +using mpark::variant_alternative_t; +using mpark::visit; +} // namespace Variant +} // namespace qbs + +#endif diff --git a/src/shared/variant/variant.hpp b/src/shared/variant/variant.hpp new file mode 100644 index 000000000..dca26986c --- /dev/null +++ b/src/shared/variant/variant.hpp @@ -0,0 +1,2465 @@ +// MPark.Variant +// +// Copyright Michael Park, 2015-2017 +// +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt) + +#ifndef MPARK_VARIANT_HPP +#define MPARK_VARIANT_HPP + +#if defined(__GNUC__) && __GNUC__ >= 9 +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdeprecated-copy" +#endif + +/* + variant synopsis + +namespace std { + + // 20.7.2, class template variant + template <class... Types> + class variant { + public: + + // 20.7.2.1, constructors + constexpr variant() noexcept(see below); + variant(const variant&); + variant(variant&&) noexcept(see below); + + template <class T> constexpr variant(T&&) noexcept(see below); + + template <class T, class... Args> + constexpr explicit variant(in_place_type_t<T>, Args&&...); + + template <class T, class U, class... Args> + constexpr explicit variant( + in_place_type_t<T>, initializer_list<U>, Args&&...); + + template <size_t I, class... Args> + constexpr explicit variant(in_place_index_t<I>, Args&&...); + + template <size_t I, class U, class... Args> + constexpr explicit variant( + in_place_index_t<I>, initializer_list<U>, Args&&...); + + // 20.7.2.2, destructor + ~variant(); + + // 20.7.2.3, assignment + variant& operator=(const variant&); + variant& operator=(variant&&) noexcept(see below); + + template <class T> variant& operator=(T&&) noexcept(see below); + + // 20.7.2.4, modifiers + template <class T, class... Args> + T& emplace(Args&&...); + + template <class T, class U, class... Args> + T& emplace(initializer_list<U>, Args&&...); + + template <size_t I, class... Args> + variant_alternative<I, variant>& emplace(Args&&...); + + template <size_t I, class U, class... Args> + variant_alternative<I, variant>& emplace(initializer_list<U>, Args&&...); + + // 20.7.2.5, value status + constexpr bool valueless_by_exception() const noexcept; + constexpr size_t index() const noexcept; + + // 20.7.2.6, swap + void swap(variant&) noexcept(see below); + }; + + // 20.7.3, variant helper classes + template <class T> struct variant_size; // undefined + + template <class T> + constexpr size_t variant_size_v = variant_size<T>::value; + + template <class T> struct variant_size<const T>; + template <class T> struct variant_size<volatile T>; + template <class T> struct variant_size<const volatile T>; + + template <class... Types> + struct variant_size<variant<Types...>>; + + template <size_t I, class T> struct variant_alternative; // undefined + + template <size_t I, class T> + using variant_alternative_t = typename variant_alternative<I, T>::type; + + template <size_t I, class T> struct variant_alternative<I, const T>; + template <size_t I, class T> struct variant_alternative<I, volatile T>; + template <size_t I, class T> struct variant_alternative<I, const volatile T>; + + template <size_t I, class... Types> + struct variant_alternative<I, variant<Types...>>; + + constexpr size_t variant_npos = -1; + + // 20.7.4, value access + template <class T, class... Types> + constexpr bool holds_alternative(const variant<Types...>&) noexcept; + + template <size_t I, class... Types> + constexpr variant_alternative_t<I, variant<Types...>>& + get(variant<Types...>&); + + template <size_t I, class... Types> + constexpr variant_alternative_t<I, variant<Types...>>&& + get(variant<Types...>&&); + + template <size_t I, class... Types> + constexpr variant_alternative_t<I, variant<Types...>> const& + get(const variant<Types...>&); + + template <size_t I, class... Types> + constexpr variant_alternative_t<I, variant<Types...>> const&& + get(const variant<Types...>&&); + + template <class T, class... Types> + constexpr T& get(variant<Types...>&); + + template <class T, class... Types> + constexpr T&& get(variant<Types...>&&); + + template <class T, class... Types> + constexpr const T& get(const variant<Types...>&); + + template <class T, class... Types> + constexpr const T&& get(const variant<Types...>&&); + + template <size_t I, class... Types> + constexpr add_pointer_t<variant_alternative_t<I, variant<Types...>>> + get_if(variant<Types...>*) noexcept; + + template <size_t I, class... Types> + constexpr add_pointer_t<const variant_alternative_t<I, variant<Types...>>> + get_if(const variant<Types...>*) noexcept; + + template <class T, class... Types> + constexpr add_pointer_t<T> + get_if(variant<Types...>*) noexcept; + + template <class T, class... Types> + constexpr add_pointer_t<const T> + get_if(const variant<Types...>*) noexcept; + + // 20.7.5, relational operators + template <class... Types> + constexpr bool operator==(const variant<Types...>&, const variant<Types...>&); + + template <class... Types> + constexpr bool operator!=(const variant<Types...>&, const variant<Types...>&); + + template <class... Types> + constexpr bool operator<(const variant<Types...>&, const variant<Types...>&); + + template <class... Types> + constexpr bool operator>(const variant<Types...>&, const variant<Types...>&); + + template <class... Types> + constexpr bool operator<=(const variant<Types...>&, const variant<Types...>&); + + template <class... Types> + constexpr bool operator>=(const variant<Types...>&, const variant<Types...>&); + + // 20.7.6, visitation + template <class Visitor, class... Variants> + constexpr see below visit(Visitor&&, Variants&&...); + + // 20.7.7, class monostate + struct monostate; + + // 20.7.8, monostate relational operators + constexpr bool operator<(monostate, monostate) noexcept; + constexpr bool operator>(monostate, monostate) noexcept; + constexpr bool operator<=(monostate, monostate) noexcept; + constexpr bool operator>=(monostate, monostate) noexcept; + constexpr bool operator==(monostate, monostate) noexcept; + constexpr bool operator!=(monostate, monostate) noexcept; + + // 20.7.9, specialized algorithms + template <class... Types> + void swap(variant<Types...>&, variant<Types...>&) noexcept(see below); + + // 20.7.10, class bad_variant_access + class bad_variant_access; + + // 20.7.11, hash support + template <class T> struct hash; + template <class... Types> struct hash<variant<Types...>>; + template <> struct hash<monostate>; + +} // namespace std + +*/ + +#include <cstddef> +#include <exception> +#include <functional> +#include <initializer_list> +#include <new> +#include <type_traits> +#include <utility> + +// MPark.Variant +// +// Copyright Michael Park, 2015-2017 +// +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt) + +#ifndef MPARK_CONFIG_HPP +#define MPARK_CONFIG_HPP + +// MSVC 2015 Update 3. +#if __cplusplus < 201103L && (!defined(_MSC_VER) || _MSC_FULL_VER < 190024210) +#error "MPark.Variant requires C++11 support." +#endif + +#ifndef __has_builtin +#define __has_builtin(x) 0 +#endif + +#ifndef __has_include +#define __has_include(x) 0 +#endif + +#ifndef __has_feature +#define __has_feature(x) 0 +#endif + +#if __has_builtin(__builtin_addressof) || \ + (defined(__GNUC__) && __GNUC__ >= 7) || defined(_MSC_VER) +#define MPARK_BUILTIN_ADDRESSOF +#endif + +#if __has_builtin(__builtin_unreachable) +#define MPARK_BUILTIN_UNREACHABLE +#endif + +#if __has_builtin(__type_pack_element) +#define MPARK_TYPE_PACK_ELEMENT +#endif + +#if defined(__cpp_constexpr) && __cpp_constexpr >= 201304 +#if !defined(_MSC_VER) || _MSC_VER < 1915 // compile issue in msvc 2017 update 8 +#define MPARK_CPP14_CONSTEXPR +#endif +#endif + +#if __has_feature(cxx_exceptions) || defined(__cpp_exceptions) || \ + (defined(_MSC_VER) && defined(_CPPUNWIND)) +#define MPARK_EXCEPTIONS +#endif + +#if defined(__cpp_generic_lambdas) || defined(_MSC_VER) +#define MPARK_GENERIC_LAMBDAS +#endif + +#if defined(__cpp_lib_integer_sequence) +#define MPARK_INTEGER_SEQUENCE +#endif + +#if defined(__cpp_return_type_deduction) || defined(_MSC_VER) +#define MPARK_RETURN_TYPE_DEDUCTION +#endif + +#if defined(__cpp_lib_transparent_operators) || defined(_MSC_VER) +#define MPARK_TRANSPARENT_OPERATORS +#endif + +#if defined(__cpp_variable_templates) || defined(_MSC_VER) +#define MPARK_VARIABLE_TEMPLATES +#endif + +#if !defined(__GLIBCXX__) || __has_include(<codecvt>) // >= libstdc++-5 +#define MPARK_TRIVIALITY_TYPE_TRAITS +#endif + +#endif // MPARK_CONFIG_HPP + +// MPark.Variant +// +// Copyright Michael Park, 2015-2017 +// +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt) + +#ifndef MPARK_IN_PLACE_HPP +#define MPARK_IN_PLACE_HPP + +#include <cstddef> + + +namespace mpark { + + struct in_place_t { explicit in_place_t() = default; }; + + template <std::size_t I> + struct in_place_index_t { explicit in_place_index_t() = default; }; + + template <typename T> + struct in_place_type_t { explicit in_place_type_t() = default; }; + +#ifdef MPARK_VARIABLE_TEMPLATES + constexpr in_place_t in_place{}; + + template <std::size_t I> constexpr in_place_index_t<I> in_place_index{}; + + template <typename T> constexpr in_place_type_t<T> in_place_type{}; +#endif + +} // namespace mpark + +#endif // MPARK_IN_PLACE_HPP + +// MPark.Variant +// +// Copyright Michael Park, 2015-2017 +// +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt) + +#ifndef MPARK_LIB_HPP +#define MPARK_LIB_HPP + +#include <memory> +#include <functional> +#include <type_traits> +#include <utility> + + +#define RETURN(...) \ + noexcept(noexcept(__VA_ARGS__)) -> decltype(__VA_ARGS__) { \ + return __VA_ARGS__; \ + } + +namespace mpark { + namespace lib { + template <typename T> + struct identity { using type = T; }; + + inline namespace cpp14 { + template <typename T, std::size_t N> + struct array { + constexpr const T &operator[](std::size_t index) const { + return data[index]; + } + + T data[N == 0 ? 1 : N]; + }; + + template <typename T> + using add_pointer_t = typename std::add_pointer<T>::type; + + template <typename... Ts> + using common_type_t = typename std::common_type<Ts...>::type; + + template <typename T> + using decay_t = typename std::decay<T>::type; + + template <bool B, typename T = void> + using enable_if_t = typename std::enable_if<B, T>::type; + + template <typename T> + using remove_const_t = typename std::remove_const<T>::type; + + template <typename T> + using remove_reference_t = typename std::remove_reference<T>::type; + + template <typename T> + inline constexpr T &&forward(remove_reference_t<T> &t) noexcept { + return static_cast<T &&>(t); + } + + template <typename T> + inline constexpr T &&forward(remove_reference_t<T> &&t) noexcept { + static_assert(!std::is_lvalue_reference<T>::value, + "can not forward an rvalue as an lvalue"); + return static_cast<T &&>(t); + } + + template <typename T> + inline constexpr remove_reference_t<T> &&move(T &&t) noexcept { + return static_cast<remove_reference_t<T> &&>(t); + } + +#ifdef MPARK_INTEGER_SEQUENCE + using std::integer_sequence; + using std::index_sequence; + using std::make_index_sequence; + using std::index_sequence_for; +#else + template <typename T, T... Is> + struct integer_sequence { + using value_type = T; + static constexpr std::size_t size() noexcept { return sizeof...(Is); } + }; + + template <std::size_t... Is> + using index_sequence = integer_sequence<std::size_t, Is...>; + + template <typename Lhs, typename Rhs> + struct make_index_sequence_concat; + + template <std::size_t... Lhs, std::size_t... Rhs> + struct make_index_sequence_concat<index_sequence<Lhs...>, + index_sequence<Rhs...>> + : identity<index_sequence<Lhs..., (sizeof...(Lhs) + Rhs)...>> {}; + + template <std::size_t N> + struct make_index_sequence_impl; + + template <std::size_t N> + using make_index_sequence = typename make_index_sequence_impl<N>::type; + + template <std::size_t N> + struct make_index_sequence_impl + : make_index_sequence_concat<make_index_sequence<N / 2>, + make_index_sequence<N - (N / 2)>> {}; + + template <> + struct make_index_sequence_impl<0> : identity<index_sequence<>> {}; + + template <> + struct make_index_sequence_impl<1> : identity<index_sequence<0>> {}; + + template <typename... Ts> + using index_sequence_for = make_index_sequence<sizeof...(Ts)>; +#endif + + // <functional> +#ifdef MPARK_TRANSPARENT_OPERATORS + using equal_to = std::equal_to<>; +#else + struct equal_to { + template <typename Lhs, typename Rhs> + inline constexpr auto operator()(Lhs &&lhs, Rhs &&rhs) const + RETURN(lib::forward<Lhs>(lhs) == lib::forward<Rhs>(rhs)) + }; +#endif + +#ifdef MPARK_TRANSPARENT_OPERATORS + using not_equal_to = std::not_equal_to<>; +#else + struct not_equal_to { + template <typename Lhs, typename Rhs> + inline constexpr auto operator()(Lhs &&lhs, Rhs &&rhs) const + RETURN(lib::forward<Lhs>(lhs) != lib::forward<Rhs>(rhs)) + }; +#endif + +#ifdef MPARK_TRANSPARENT_OPERATORS + using less = std::less<>; +#else + struct less { + template <typename Lhs, typename Rhs> + inline constexpr auto operator()(Lhs &&lhs, Rhs &&rhs) const + RETURN(lib::forward<Lhs>(lhs) < lib::forward<Rhs>(rhs)) + }; +#endif + +#ifdef MPARK_TRANSPARENT_OPERATORS + using greater = std::greater<>; +#else + struct greater { + template <typename Lhs, typename Rhs> + inline constexpr auto operator()(Lhs &&lhs, Rhs &&rhs) const + RETURN(lib::forward<Lhs>(lhs) > lib::forward<Rhs>(rhs)) + }; +#endif + +#ifdef MPARK_TRANSPARENT_OPERATORS + using less_equal = std::less_equal<>; +#else + struct less_equal { + template <typename Lhs, typename Rhs> + inline constexpr auto operator()(Lhs &&lhs, Rhs &&rhs) const + RETURN(lib::forward<Lhs>(lhs) <= lib::forward<Rhs>(rhs)) + }; +#endif + +#ifdef MPARK_TRANSPARENT_OPERATORS + using greater_equal = std::greater_equal<>; +#else + struct greater_equal { + template <typename Lhs, typename Rhs> + inline constexpr auto operator()(Lhs &&lhs, Rhs &&rhs) const + RETURN(lib::forward<Lhs>(lhs) >= lib::forward<Rhs>(rhs)) + }; +#endif + } // namespace cpp14 + + inline namespace cpp17 { + + // <type_traits> + template <bool B> + using bool_constant = std::integral_constant<bool, B>; + + template <typename...> + struct voider : identity<void> {}; + + template <typename... Ts> + using void_t = typename voider<Ts...>::type; + + namespace detail { + namespace swappable { + + using std::swap; + + template <typename T> + struct is_swappable { + private: + template <typename U, + typename = decltype(swap(std::declval<U &>(), + std::declval<U &>()))> + inline static std::true_type test(int); + + template <typename U> + inline static std::false_type test(...); + + public: + static constexpr bool value = decltype(test<T>(0))::value; + }; + + template <typename T, bool = is_swappable<T>::value> + struct is_nothrow_swappable { + static constexpr bool value = + noexcept(swap(std::declval<T &>(), std::declval<T &>())); + }; + + template <typename T> + struct is_nothrow_swappable<T, false> : std::false_type {}; + + } // namespace swappable + } // namespace detail + + using detail::swappable::is_swappable; + using detail::swappable::is_nothrow_swappable; + + // <functional> +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable : 4100) +#endif + template <typename F, typename... As> + inline constexpr auto invoke(F &&f, As &&... as) + RETURN(lib::forward<F>(f)(lib::forward<As>(as)...)) +#ifdef _MSC_VER +#pragma warning(pop) +#endif + + template <typename B, typename T, typename D> + inline constexpr auto invoke(T B::*pmv, D &&d) + RETURN(lib::forward<D>(d).*pmv) + + template <typename Pmv, typename Ptr> + inline constexpr auto invoke(Pmv pmv, Ptr &&ptr) + RETURN((*lib::forward<Ptr>(ptr)).*pmv) + + template <typename B, typename T, typename D, typename... As> + inline constexpr auto invoke(T B::*pmf, D &&d, As &&... as) + RETURN((lib::forward<D>(d).*pmf)(lib::forward<As>(as)...)) + + template <typename Pmf, typename Ptr, typename... As> + inline constexpr auto invoke(Pmf pmf, Ptr &&ptr, As &&... as) + RETURN(((*lib::forward<Ptr>(ptr)).*pmf)(lib::forward<As>(as)...)) + + namespace detail { + + template <typename Void, typename, typename...> + struct invoke_result {}; + + template <typename F, typename... Args> + struct invoke_result<void_t<decltype(lib::invoke( + std::declval<F>(), std::declval<Args>()...))>, + F, + Args...> + : identity<decltype( + lib::invoke(std::declval<F>(), std::declval<Args>()...))> {}; + + } // namespace detail + + template <typename F, typename... Args> + using invoke_result = detail::invoke_result<void, F, Args...>; + + template <typename F, typename... Args> + using invoke_result_t = typename invoke_result<F, Args...>::type; + + namespace detail { + + template <typename Void, typename, typename...> + struct is_invocable : std::false_type {}; + + template <typename F, typename... Args> + struct is_invocable<void_t<invoke_result_t<F, Args...>>, F, Args...> + : std::true_type {}; + + template <typename Void, typename, typename, typename...> + struct is_invocable_r : std::false_type {}; + + template <typename R, typename F, typename... Args> + struct is_invocable_r<void_t<invoke_result_t<F, Args...>>, + R, + F, + Args...> + : std::is_convertible<invoke_result_t<F, Args...>, R> {}; + + } // namespace detail + + template <typename F, typename... Args> + using is_invocable = detail::is_invocable<void, F, Args...>; + + template <typename R, typename F, typename... Args> + using is_invocable_r = detail::is_invocable_r<void, R, F, Args...>; + + // <memory> +#ifdef MPARK_BUILTIN_ADDRESSOF + template <typename T> + inline constexpr T *addressof(T &arg) { + return __builtin_addressof(arg); + } +#else + namespace detail { + + namespace has_addressof_impl { + + struct fail; + + template <typename T> + inline fail operator&(T &&); + + template <typename T> + inline static constexpr bool impl() { + return (std::is_class<T>::value || std::is_union<T>::value) && + !std::is_same<decltype(&std::declval<T &>()), fail>::value; + } + + } // namespace has_addressof_impl + + template <typename T> + using has_addressof = bool_constant<has_addressof_impl::impl<T>()>; + + template <typename T> + inline constexpr T *addressof(T &arg, std::true_type) { + return std::addressof(arg); + } + + template <typename T> + inline constexpr T *addressof(T &arg, std::false_type) { + return &arg; + } + + } // namespace detail + + template <typename T> + inline constexpr T *addressof(T &arg) { + return detail::addressof(arg, detail::has_addressof<T>{}); + } +#endif + + template <typename T> + inline constexpr T *addressof(const T &&) = delete; + + } // namespace cpp17 + + template <typename T> + struct remove_all_extents : identity<T> {}; + + template <typename T, std::size_t N> + struct remove_all_extents<array<T, N>> : remove_all_extents<T> {}; + + template <typename T> + using remove_all_extents_t = typename remove_all_extents<T>::type; + + template <std::size_t N> + using size_constant = std::integral_constant<std::size_t, N>; + + template <std::size_t I, typename T> + struct indexed_type : size_constant<I>, identity<T> {}; + + template <bool... Bs> + using all = std::is_same<integer_sequence<bool, true, Bs...>, + integer_sequence<bool, Bs..., true>>; + +#ifdef MPARK_TYPE_PACK_ELEMENT + template <std::size_t I, typename... Ts> + using type_pack_element_t = __type_pack_element<I, Ts...>; +#else + template <std::size_t I, typename... Ts> + struct type_pack_element_impl { + private: + template <typename> + struct set; + + template <std::size_t... Is> + struct set<index_sequence<Is...>> : indexed_type<Is, Ts>... {}; + + template <typename T> + inline static std::enable_if<true, T> impl(indexed_type<I, T>); + + inline static std::enable_if<false> impl(...); + + public: + using type = decltype(impl(set<index_sequence_for<Ts...>>{})); + }; + + template <std::size_t I, typename... Ts> + using type_pack_element = typename type_pack_element_impl<I, Ts...>::type; + + template <std::size_t I, typename... Ts> + using type_pack_element_t = typename type_pack_element<I, Ts...>::type; +#endif + +#ifdef MPARK_TRIVIALITY_TYPE_TRAITS + using std::is_trivially_copy_constructible; + using std::is_trivially_move_constructible; + using std::is_trivially_copy_assignable; + using std::is_trivially_move_assignable; +#else + template <typename T> + struct is_trivially_copy_constructible + : bool_constant< + std::is_copy_constructible<T>::value && __has_trivial_copy(T)> {}; + + template <typename T> + struct is_trivially_move_constructible : bool_constant<__is_trivial(T)> {}; + + template <typename T> + struct is_trivially_copy_assignable + : bool_constant< + std::is_copy_assignable<T>::value && __has_trivial_assign(T)> {}; + + template <typename T> + struct is_trivially_move_assignable : bool_constant<__is_trivial(T)> {}; +#endif + + template <typename T, bool> + struct dependent_type : T {}; + + template <typename Is, std::size_t J> + struct push_back; + + template <typename Is, std::size_t J> + using push_back_t = typename push_back<Is, J>::type; + + template <std::size_t... Is, std::size_t J> + struct push_back<index_sequence<Is...>, J> { + using type = index_sequence<Is..., J>; + }; + + } // namespace lib +} // namespace mpark + +#undef RETURN + +#endif // MPARK_LIB_HPP + + +namespace mpark { + +#ifdef MPARK_RETURN_TYPE_DEDUCTION + +#define AUTO auto +#define AUTO_RETURN(...) { return __VA_ARGS__; } + +#define AUTO_REFREF auto && +#define AUTO_REFREF_RETURN(...) { return __VA_ARGS__; } + +#define DECLTYPE_AUTO decltype(auto) +#define DECLTYPE_AUTO_RETURN(...) { return __VA_ARGS__; } + +#else + +#define AUTO auto +#define AUTO_RETURN(...) \ + -> lib::decay_t<decltype(__VA_ARGS__)> { return __VA_ARGS__; } + +#define AUTO_REFREF auto +#define AUTO_REFREF_RETURN(...) \ + -> decltype((__VA_ARGS__)) { \ + static_assert(std::is_reference<decltype((__VA_ARGS__))>::value, ""); \ + return __VA_ARGS__; \ + } + +#define DECLTYPE_AUTO auto +#define DECLTYPE_AUTO_RETURN(...) \ + -> decltype(__VA_ARGS__) { return __VA_ARGS__; } + +#endif + + class bad_variant_access : public std::exception { + public: + virtual const char *what() const noexcept { return "bad_variant_access"; } + }; + + [[noreturn]] inline void throw_bad_variant_access() { +#ifdef MPARK_EXCEPTIONS + throw bad_variant_access{}; +#else + std::terminate(); +#ifdef MPARK_BUILTIN_UNREACHABLE + __builtin_unreachable(); +#endif +#endif + } + + template <typename... Ts> + class variant; + + template <typename T> + struct variant_size; + +#ifdef MPARK_VARIABLE_TEMPLATES + template <typename T> + constexpr std::size_t variant_size_v = variant_size<T>::value; +#endif + + template <typename T> + struct variant_size<const T> : variant_size<T> {}; + + template <typename T> + struct variant_size<volatile T> : variant_size<T> {}; + + template <typename T> + struct variant_size<const volatile T> : variant_size<T> {}; + + template <typename... Ts> + struct variant_size<variant<Ts...>> : lib::size_constant<sizeof...(Ts)> {}; + + template <std::size_t I, typename T> + struct variant_alternative; + + template <std::size_t I, typename T> + using variant_alternative_t = typename variant_alternative<I, T>::type; + + template <std::size_t I, typename T> + struct variant_alternative<I, const T> + : std::add_const<variant_alternative_t<I, T>> {}; + + template <std::size_t I, typename T> + struct variant_alternative<I, volatile T> + : std::add_volatile<variant_alternative_t<I, T>> {}; + + template <std::size_t I, typename T> + struct variant_alternative<I, const volatile T> + : std::add_cv<variant_alternative_t<I, T>> {}; + + template <std::size_t I, typename... Ts> + struct variant_alternative<I, variant<Ts...>> { + static_assert(I < sizeof...(Ts), + "Index out of bounds in std::variant_alternative<>"); + using type = lib::type_pack_element_t<I, Ts...>; + }; + + constexpr std::size_t variant_npos = static_cast<std::size_t>(-1); + + namespace detail { + + constexpr std::size_t not_found = static_cast<std::size_t>(-1); + constexpr std::size_t ambiguous = static_cast<std::size_t>(-2); + +#ifdef MPARK_CPP14_CONSTEXPR + template <typename T, typename... Ts> + inline constexpr std::size_t find_index() { + constexpr lib::array<bool, sizeof...(Ts)> matches = { + {std::is_same<T, Ts>::value...} + }; + std::size_t result = not_found; + for (std::size_t i = 0; i < sizeof...(Ts); ++i) { + if (matches[i]) { + if (result != not_found) { + return ambiguous; + } + result = i; + } + } + return result; + } +#else + inline constexpr std::size_t find_index_impl(std::size_t result, + std::size_t) { + return result; + } + + template <typename... Bs> + inline constexpr std::size_t find_index_impl(std::size_t result, + std::size_t idx, + bool b, + Bs... bs) { + return b ? (result != not_found ? ambiguous + : find_index_impl(idx, idx + 1, bs...)) + : find_index_impl(result, idx + 1, bs...); + } + + template <typename T, typename... Ts> + inline constexpr std::size_t find_index() { + return find_index_impl(not_found, 0, std::is_same<T, Ts>::value...); + } +#endif + + template <std::size_t I> + using find_index_sfinae_impl = + lib::enable_if_t<I != not_found && I != ambiguous, + lib::size_constant<I>>; + + template <typename T, typename... Ts> + using find_index_sfinae = find_index_sfinae_impl<find_index<T, Ts...>()>; + + template <std::size_t I> + struct find_index_checked_impl : lib::size_constant<I> { + static_assert(I != not_found, "the specified type is not found."); + static_assert(I != ambiguous, "the specified type is ambiguous."); + }; + + template <typename T, typename... Ts> + using find_index_checked = find_index_checked_impl<find_index<T, Ts...>()>; + + struct valueless_t {}; + + enum class Trait { TriviallyAvailable, Available, Unavailable }; + + template <typename T, + template <typename> class IsTriviallyAvailable, + template <typename> class IsAvailable> + inline constexpr Trait trait() { + return IsTriviallyAvailable<T>::value + ? Trait::TriviallyAvailable + : IsAvailable<T>::value ? Trait::Available + : Trait::Unavailable; + } + +#ifdef MPARK_CPP14_CONSTEXPR + template <typename... Traits> + inline constexpr Trait common_trait(Traits... traits) { + Trait result = Trait::TriviallyAvailable; + for (Trait t : {traits...}) { + if (static_cast<int>(t) > static_cast<int>(result)) { + result = t; + } + } + return result; + } +#else + inline constexpr Trait common_trait_impl(Trait result) { return result; } + + template <typename... Traits> + inline constexpr Trait common_trait_impl(Trait result, + Trait t, + Traits... ts) { + return static_cast<int>(t) > static_cast<int>(result) + ? common_trait_impl(t, ts...) + : common_trait_impl(result, ts...); + } + + template <typename... Traits> + inline constexpr Trait common_trait(Traits... ts) { + return common_trait_impl(Trait::TriviallyAvailable, ts...); + } +#endif + + template <typename... Ts> + struct traits { + static constexpr Trait copy_constructible_trait = + common_trait(trait<Ts, + lib::is_trivially_copy_constructible, + std::is_copy_constructible>()...); + + static constexpr Trait move_constructible_trait = + common_trait(trait<Ts, + lib::is_trivially_move_constructible, + std::is_move_constructible>()...); + + static constexpr Trait copy_assignable_trait = + common_trait(copy_constructible_trait, + trait<Ts, + lib::is_trivially_copy_assignable, + std::is_copy_assignable>()...); + + static constexpr Trait move_assignable_trait = + common_trait(move_constructible_trait, + trait<Ts, + lib::is_trivially_move_assignable, + std::is_move_assignable>()...); + + static constexpr Trait destructible_trait = + common_trait(trait<Ts, + std::is_trivially_destructible, + std::is_destructible>()...); + }; + + namespace access { + + struct recursive_union { +#ifdef MPARK_RETURN_TYPE_DEDUCTION + template <typename V> + inline static constexpr auto &&get_alt(V &&v, in_place_index_t<0>) { + return lib::forward<V>(v).head_; + } + + template <typename V, std::size_t I> + inline static constexpr auto &&get_alt(V &&v, in_place_index_t<I>) { + return get_alt(lib::forward<V>(v).tail_, in_place_index_t<I - 1>{}); + } +#else + template <std::size_t I, bool Dummy = true> + struct get_alt_impl { + template <typename V> + inline constexpr AUTO_REFREF operator()(V &&v) const + AUTO_REFREF_RETURN(get_alt_impl<I - 1>{}(lib::forward<V>(v).tail_)) + }; + + template <bool Dummy> + struct get_alt_impl<0, Dummy> { + template <typename V> + inline constexpr AUTO_REFREF operator()(V &&v) const + AUTO_REFREF_RETURN(lib::forward<V>(v).head_) + }; + + template <typename V, std::size_t I> + inline static constexpr AUTO_REFREF get_alt(V &&v, in_place_index_t<I>) + AUTO_REFREF_RETURN(get_alt_impl<I>{}(lib::forward<V>(v))) +#endif + }; + + struct base { + template <std::size_t I, typename V> + inline static constexpr AUTO_REFREF get_alt(V &&v) + AUTO_REFREF_RETURN(recursive_union::get_alt( + data(lib::forward<V>(v)), in_place_index_t<I>{})) + }; + + struct variant { + template <std::size_t I, typename V> + inline static constexpr AUTO_REFREF get_alt(V &&v) + AUTO_REFREF_RETURN(base::get_alt<I>(lib::forward<V>(v).impl_)) + }; + + } // namespace access + + namespace visitation { + + struct base { + template <typename T> + inline static constexpr const T &at(const T &elem) { + return elem; + } + + template <typename T, std::size_t N, typename... Is> + inline static constexpr const lib::remove_all_extents_t<T> &at( + const lib::array<T, N> &elems, std::size_t i, Is... is) { + return at(elems[i], is...); + } + + template <typename F, typename... Fs> + inline static constexpr int visit_visitor_return_type_check() { + static_assert(lib::all<std::is_same<F, Fs>::value...>::value, + "`mpark::visit` requires the visitor to have a single " + "return type."); + return 0; + } + + template <typename... Fs> + inline static constexpr lib::array< + lib::common_type_t<lib::decay_t<Fs>...>, + sizeof...(Fs)> + make_farray(Fs &&... fs) { + using result = lib::array<lib::common_type_t<lib::decay_t<Fs>...>, + sizeof...(Fs)>; + return visit_visitor_return_type_check<lib::decay_t<Fs>...>(), + result{{lib::forward<Fs>(fs)...}}; + } + + template <std::size_t... Is> + struct dispatcher { + template <typename F, typename... Vs> + struct impl { + inline static constexpr DECLTYPE_AUTO dispatch(F f, Vs... vs) + DECLTYPE_AUTO_RETURN(lib::invoke( + static_cast<F>(f), + access::base::get_alt<Is>(static_cast<Vs>(vs))...)) + }; + }; + + template <typename F, typename... Vs, std::size_t... Is> + inline static constexpr AUTO make_dispatch(lib::index_sequence<Is...>) + AUTO_RETURN(&dispatcher<Is...>::template impl<F, Vs...>::dispatch) + + template <std::size_t I, typename F, typename... Vs> + inline static constexpr AUTO make_fdiagonal_impl() + AUTO_RETURN(make_dispatch<F, Vs...>( + lib::index_sequence<lib::indexed_type<I, Vs>::value...>{})) + + template <typename F, typename... Vs, std::size_t... Is> + inline static constexpr AUTO make_fdiagonal_impl( + lib::index_sequence<Is...>) + AUTO_RETURN(make_farray(make_fdiagonal_impl<Is, F, Vs...>()...)) + + template <typename F, typename V, typename... Vs> + inline static constexpr /* auto * */ auto make_fdiagonal() + -> decltype(make_fdiagonal_impl<F, V, Vs...>( + lib::make_index_sequence<lib::decay_t<V>::size()>{})) { + static_assert(lib::all<(lib::decay_t<V>::size() == + lib::decay_t<Vs>::size())...>::value, + "all of the variants must be the same size."); + return make_fdiagonal_impl<F, V, Vs...>( + lib::make_index_sequence<lib::decay_t<V>::size()>{}); + } + +#ifdef MPARK_RETURN_TYPE_DEDUCTION + template <typename F, typename... Vs, typename Is> + inline static constexpr auto make_fmatrix_impl(Is is) { + return make_dispatch<F, Vs...>(is); + } + + template <typename F, + typename... Vs, + typename Is, + std::size_t... Js, + typename... Ls> + inline static constexpr auto make_fmatrix_impl( + Is, lib::index_sequence<Js...>, Ls... ls) { + return make_farray(make_fmatrix_impl<F, Vs...>( + lib::push_back_t<Is, Js>{}, ls...)...); + } + + template <typename F, typename... Vs> + inline static constexpr auto make_fmatrix() { + return make_fmatrix_impl<F, Vs...>( + lib::index_sequence<>{}, + lib::make_index_sequence<lib::decay_t<Vs>::size()>{}...); + } +#else + template <typename F, typename... Vs> + struct make_fmatrix_impl { + template <typename...> + struct impl; + + template <typename Is> + struct impl<Is> { + inline constexpr AUTO operator()() const + AUTO_RETURN(make_dispatch<F, Vs...>(Is{})) + }; + + template <typename Is, std::size_t... Js, typename... Ls> + struct impl<Is, lib::index_sequence<Js...>, Ls...> { + inline constexpr AUTO operator()() const + AUTO_RETURN( + make_farray(impl<lib::push_back_t<Is, Js>, Ls...>{}()...)) + }; + }; + + template <typename F, typename... Vs> + inline static constexpr AUTO make_fmatrix() + AUTO_RETURN( + typename make_fmatrix_impl<F, Vs...>::template impl< + lib::index_sequence<>, + lib::make_index_sequence<lib::decay_t<Vs>::size()>...>{}()) +#endif + }; // namespace base + + template <typename F, typename... Vs> + using FDiagonal = decltype(base::make_fdiagonal<F, Vs...>()); + + template <typename F, typename... Vs> + struct fdiagonal { +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable : 4268) +#endif + static constexpr FDiagonal<F, Vs...> value = + base::make_fdiagonal<F, Vs...>(); +#ifdef _MSC_VER +#pragma warning(pop) +#endif + }; + + template <typename F, typename... Vs> + constexpr FDiagonal<F, Vs...> fdiagonal<F, Vs...>::value; + + template <typename F, typename... Vs> + using FMatrix = decltype(base::make_fmatrix<F, Vs...>()); + + template <typename F, typename... Vs> + struct fmatrix { +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable : 4268) +#endif + static constexpr FMatrix<F, Vs...> value = + base::make_fmatrix<F, Vs...>(); +#ifdef _MSC_VER +#pragma warning(pop) +#endif + }; + + template <typename F, typename... Vs> + constexpr FMatrix<F, Vs...> fmatrix<F, Vs...>::value; + + struct alt { + template <typename Visitor, typename... Vs> + inline static constexpr DECLTYPE_AUTO visit_alt_at(std::size_t index, + Visitor &&visitor, + Vs &&... vs) + DECLTYPE_AUTO_RETURN(base::at( + fdiagonal<Visitor &&, + decltype(as_base(lib::forward<Vs>(vs)))...>::value, + index)(lib::forward<Visitor>(visitor), + as_base(lib::forward<Vs>(vs))...)) + + template <typename Visitor, typename... Vs> + inline static constexpr DECLTYPE_AUTO visit_alt(Visitor &&visitor, + Vs &&... vs) + DECLTYPE_AUTO_RETURN(base::at( + fmatrix<Visitor &&, + decltype(as_base(lib::forward<Vs>(vs)))...>::value, + vs.index()...)(lib::forward<Visitor>(visitor), + as_base(lib::forward<Vs>(vs))...)) + }; + + struct variant { + private: + template <typename Visitor, typename... Values> + struct visit_exhaustive_visitor_check { + static_assert( + lib::is_invocable<Visitor, Values...>::value, + "`mpark::visit` requires the visitor to be exhaustive."); + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable : 4100) +#endif + inline constexpr DECLTYPE_AUTO operator()(Visitor &&visitor, + Values &&... values) const + DECLTYPE_AUTO_RETURN(lib::invoke(lib::forward<Visitor>(visitor), + lib::forward<Values>(values)...)) +#ifdef _MSC_VER +#pragma warning(pop) +#endif + }; + + template <typename Visitor> + struct value_visitor { + Visitor &&visitor_; + + template <typename... Alts> + inline constexpr DECLTYPE_AUTO operator()(Alts &&... alts) const + DECLTYPE_AUTO_RETURN( + visit_exhaustive_visitor_check< + Visitor, + decltype((lib::forward<Alts>(alts).value))...>{}( + lib::forward<Visitor>(visitor_), + lib::forward<Alts>(alts).value...)) + }; + + template <typename Visitor> + inline static constexpr AUTO make_value_visitor(Visitor &&visitor) + AUTO_RETURN(value_visitor<Visitor>{lib::forward<Visitor>(visitor)}) + + public: + template <typename Visitor, typename... Vs> + inline static constexpr DECLTYPE_AUTO visit_alt_at(std::size_t index, + Visitor &&visitor, + Vs &&... vs) + DECLTYPE_AUTO_RETURN( + alt::visit_alt_at(index, + lib::forward<Visitor>(visitor), + lib::forward<Vs>(vs).impl_...)) + + template <typename Visitor, typename... Vs> + inline static constexpr DECLTYPE_AUTO visit_alt(Visitor &&visitor, + Vs &&... vs) + DECLTYPE_AUTO_RETURN(alt::visit_alt(lib::forward<Visitor>(visitor), + lib::forward<Vs>(vs).impl_...)) + + template <typename Visitor, typename... Vs> + inline static constexpr DECLTYPE_AUTO visit_value_at(std::size_t index, + Visitor &&visitor, + Vs &&... vs) + DECLTYPE_AUTO_RETURN( + visit_alt_at(index, + make_value_visitor(lib::forward<Visitor>(visitor)), + lib::forward<Vs>(vs)...)) + + template <typename Visitor, typename... Vs> + inline static constexpr DECLTYPE_AUTO visit_value(Visitor &&visitor, + Vs &&... vs) + DECLTYPE_AUTO_RETURN( + visit_alt(make_value_visitor(lib::forward<Visitor>(visitor)), + lib::forward<Vs>(vs)...)) + }; + + } // namespace visitation + + template <std::size_t Index, typename T> + struct alt { + using value_type = T; + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable : 4244) +#endif + template <typename... Args> + inline explicit constexpr alt(in_place_t, Args &&... args) + : value(lib::forward<Args>(args)...) {} +#ifdef _MSC_VER +#pragma warning(pop) +#endif + + T value; + }; + + template <Trait DestructibleTrait, std::size_t Index, typename... Ts> + union recursive_union; + + template <Trait DestructibleTrait, std::size_t Index> + union recursive_union<DestructibleTrait, Index> {}; + +#define MPARK_VARIANT_RECURSIVE_UNION(destructible_trait, destructor) \ + template <std::size_t Index, typename T, typename... Ts> \ + union recursive_union<destructible_trait, Index, T, Ts...> { \ + public: \ + inline explicit constexpr recursive_union(valueless_t) noexcept \ + : dummy_{} {} \ + \ + template <typename... Args> \ + inline explicit constexpr recursive_union(in_place_index_t<0>, \ + Args &&... args) \ + : head_(in_place_t{}, lib::forward<Args>(args)...) {} \ + \ + template <std::size_t I, typename... Args> \ + inline explicit constexpr recursive_union(in_place_index_t<I>, \ + Args &&... args) \ + : tail_(in_place_index_t<I - 1>{}, lib::forward<Args>(args)...) {} \ + \ + recursive_union(const recursive_union &) = default; \ + recursive_union(recursive_union &&) = default; \ + \ + destructor \ + \ + recursive_union &operator=(const recursive_union &) = default; \ + recursive_union &operator=(recursive_union &&) = default; \ + \ + private: \ + char dummy_; \ + alt<Index, T> head_; \ + recursive_union<destructible_trait, Index + 1, Ts...> tail_; \ + \ + friend struct access::recursive_union; \ + } + + MPARK_VARIANT_RECURSIVE_UNION(Trait::TriviallyAvailable, + ~recursive_union() = default;); + MPARK_VARIANT_RECURSIVE_UNION(Trait::Available, + ~recursive_union() {}); + MPARK_VARIANT_RECURSIVE_UNION(Trait::Unavailable, + ~recursive_union() = delete;); + +#undef MPARK_VARIANT_RECURSIVE_UNION + + using index_t = unsigned int; + + template <Trait DestructibleTrait, typename... Ts> + class base { + public: + inline explicit constexpr base(valueless_t tag) noexcept + : data_(tag), index_(static_cast<index_t>(-1)) {} + + template <std::size_t I, typename... Args> + inline explicit constexpr base(in_place_index_t<I>, Args &&... args) + : data_(in_place_index_t<I>{}, lib::forward<Args>(args)...), + index_(I) {} + + inline constexpr bool valueless_by_exception() const noexcept { + return index_ == static_cast<index_t>(-1); + } + + inline constexpr std::size_t index() const noexcept { + return valueless_by_exception() ? variant_npos : index_; + } + + protected: + using data_t = recursive_union<DestructibleTrait, 0, Ts...>; + + friend inline constexpr base &as_base(base &b) { return b; } + friend inline constexpr const base &as_base(const base &b) { return b; } + friend inline constexpr base &&as_base(base &&b) { return lib::move(b); } + friend inline constexpr const base &&as_base(const base &&b) { return lib::move(b); } + + friend inline constexpr data_t &data(base &b) { return b.data_; } + friend inline constexpr const data_t &data(const base &b) { return b.data_; } + friend inline constexpr data_t &&data(base &&b) { return lib::move(b).data_; } + friend inline constexpr const data_t &&data(const base &&b) { return lib::move(b).data_; } + + inline static constexpr std::size_t size() { return sizeof...(Ts); } + + data_t data_; + index_t index_; + + friend struct access::base; + friend struct visitation::base; + }; + + struct dtor { +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable : 4100) +#endif + template <typename Alt> + inline void operator()(Alt &alt) const noexcept { alt.~Alt(); } +#ifdef _MSC_VER +#pragma warning(pop) +#endif + }; + +#if defined(_MSC_VER) && _MSC_VER < 1910 +#define INHERITING_CTOR(type, base) \ + template <typename... Args> \ + inline explicit constexpr type(Args &&... args) \ + : base(lib::forward<Args>(args)...) {} +#else +#define INHERITING_CTOR(type, base) using base::base; +#endif + + template <typename Traits, Trait = Traits::destructible_trait> + class destructor; + +#define MPARK_VARIANT_DESTRUCTOR(destructible_trait, definition, destroy) \ + template <typename... Ts> \ + class destructor<traits<Ts...>, destructible_trait> \ + : public base<destructible_trait, Ts...> { \ + using super = base<destructible_trait, Ts...>; \ + \ + public: \ + INHERITING_CTOR(destructor, super) \ + using super::operator=; \ + \ + destructor(const destructor &) = default; \ + destructor(destructor &&) = default; \ + definition \ + destructor &operator=(const destructor &) = default; \ + destructor &operator=(destructor &&) = default; \ + \ + protected: \ + destroy \ + } + + MPARK_VARIANT_DESTRUCTOR( + Trait::TriviallyAvailable, + ~destructor() = default;, + inline void destroy() noexcept { + this->index_ = static_cast<index_t>(-1); + }); + + MPARK_VARIANT_DESTRUCTOR( + Trait::Available, + ~destructor() { destroy(); }, + inline void destroy() noexcept { + if (!this->valueless_by_exception()) { + visitation::alt::visit_alt(dtor{}, *this); + } + this->index_ = static_cast<index_t>(-1); + }); + + MPARK_VARIANT_DESTRUCTOR( + Trait::Unavailable, + ~destructor() = delete;, + inline void destroy() noexcept = delete;); + +#undef MPARK_VARIANT_DESTRUCTOR + + template <typename Traits> + class constructor : public destructor<Traits> { + using super = destructor<Traits>; + + public: + INHERITING_CTOR(constructor, super) + using super::operator=; + + protected: +#ifndef MPARK_GENERIC_LAMBDAS + struct ctor { + template <typename LhsAlt, typename RhsAlt> + inline void operator()(LhsAlt &lhs_alt, RhsAlt &&rhs_alt) const { + constructor::construct_alt(lhs_alt, + lib::forward<RhsAlt>(rhs_alt).value); + } + }; +#endif + + template <std::size_t I, typename T, typename... Args> + inline static T &construct_alt(alt<I, T> &a, Args &&... args) { + ::new (static_cast<void *>(lib::addressof(a))) + alt<I, T>(in_place_t{}, lib::forward<Args>(args)...); + return a.value; + } + + template <typename Rhs> + inline static void generic_construct(constructor &lhs, Rhs &&rhs) { + lhs.destroy(); + if (!rhs.valueless_by_exception()) { + visitation::alt::visit_alt_at( + rhs.index(), +#ifdef MPARK_GENERIC_LAMBDAS + [](auto &lhs_alt, auto &&rhs_alt) { + constructor::construct_alt( + lhs_alt, lib::forward<decltype(rhs_alt)>(rhs_alt).value); + } +#else + ctor{} +#endif + , + lhs, + lib::forward<Rhs>(rhs)); + lhs.index_ = rhs.index_; + } + } + }; + + template <typename Traits, Trait = Traits::move_constructible_trait> + class move_constructor; + +#define MPARK_VARIANT_MOVE_CONSTRUCTOR(move_constructible_trait, definition) \ + template <typename... Ts> \ + class move_constructor<traits<Ts...>, move_constructible_trait> \ + : public constructor<traits<Ts...>> { \ + using super = constructor<traits<Ts...>>; \ + \ + public: \ + INHERITING_CTOR(move_constructor, super) \ + using super::operator=; \ + \ + move_constructor(const move_constructor &) = default; \ + definition \ + ~move_constructor() = default; \ + move_constructor &operator=(const move_constructor &) = default; \ + move_constructor &operator=(move_constructor &&) = default; \ + } + + MPARK_VARIANT_MOVE_CONSTRUCTOR( + Trait::TriviallyAvailable, + move_constructor(move_constructor &&that) = default;); + + MPARK_VARIANT_MOVE_CONSTRUCTOR( + Trait::Available, + move_constructor(move_constructor &&that) noexcept( + lib::all<std::is_nothrow_move_constructible<Ts>::value...>::value) + : move_constructor(valueless_t{}) { + this->generic_construct(*this, lib::move(that)); + }); + + MPARK_VARIANT_MOVE_CONSTRUCTOR( + Trait::Unavailable, + move_constructor(move_constructor &&) = delete;); + +#undef MPARK_VARIANT_MOVE_CONSTRUCTOR + + template <typename Traits, Trait = Traits::copy_constructible_trait> + class copy_constructor; + +#define MPARK_VARIANT_COPY_CONSTRUCTOR(copy_constructible_trait, definition) \ + template <typename... Ts> \ + class copy_constructor<traits<Ts...>, copy_constructible_trait> \ + : public move_constructor<traits<Ts...>> { \ + using super = move_constructor<traits<Ts...>>; \ + \ + public: \ + INHERITING_CTOR(copy_constructor, super) \ + using super::operator=; \ + \ + definition \ + copy_constructor(copy_constructor &&) = default; \ + ~copy_constructor() = default; \ + copy_constructor &operator=(const copy_constructor &) = default; \ + copy_constructor &operator=(copy_constructor &&) = default; \ + } + + MPARK_VARIANT_COPY_CONSTRUCTOR( + Trait::TriviallyAvailable, + copy_constructor(const copy_constructor &that) = default;); + + MPARK_VARIANT_COPY_CONSTRUCTOR( + Trait::Available, + copy_constructor(const copy_constructor &that) + : copy_constructor(valueless_t{}) { + this->generic_construct(*this, that); + }); + + MPARK_VARIANT_COPY_CONSTRUCTOR( + Trait::Unavailable, + copy_constructor(const copy_constructor &) = delete;); + +#undef MPARK_VARIANT_COPY_CONSTRUCTOR + + template <typename Traits> + class assignment : public copy_constructor<Traits> { + using super = copy_constructor<Traits>; + + public: + INHERITING_CTOR(assignment, super) + using super::operator=; + + template <std::size_t I, typename... Args> + inline /* auto & */ auto emplace(Args &&... args) + -> decltype(this->construct_alt(access::base::get_alt<I>(*this), + lib::forward<Args>(args)...)) { + this->destroy(); + auto &result = this->construct_alt(access::base::get_alt<I>(*this), + lib::forward<Args>(args)...); + this->index_ = I; + return result; + } + + protected: +#ifndef MPARK_GENERIC_LAMBDAS + template <typename That> + struct assigner { + template <typename ThisAlt, typename ThatAlt> + inline void operator()(ThisAlt &this_alt, ThatAlt &&that_alt) const { + self->assign_alt(this_alt, lib::forward<ThatAlt>(that_alt).value); + } + assignment *self; + }; +#endif + + template <std::size_t I, typename T, typename Arg> + inline void assign_alt(alt<I, T> &a, Arg &&arg) { + if (this->index() == I) { +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable : 4244) +#endif + a.value = lib::forward<Arg>(arg); +#ifdef _MSC_VER +#pragma warning(pop) +#endif + } else { + struct { + void operator()(std::true_type) const { + this_->emplace<I>(lib::forward<Arg>(arg_)); + } + void operator()(std::false_type) const { + this_->emplace<I>(T(lib::forward<Arg>(arg_))); + } + assignment *this_; + Arg &&arg_; + } impl{this, lib::forward<Arg>(arg)}; + impl(lib::bool_constant< + std::is_nothrow_constructible<T, Arg>::value || + !std::is_nothrow_move_constructible<T>::value>{}); + } + } + + template <typename That> + inline void generic_assign(That &&that) { + if (this->valueless_by_exception() && that.valueless_by_exception()) { + // do nothing. + } else if (that.valueless_by_exception()) { + this->destroy(); + } else { + visitation::alt::visit_alt_at( + that.index(), +#ifdef MPARK_GENERIC_LAMBDAS + [this](auto &this_alt, auto &&that_alt) { + this->assign_alt( + this_alt, lib::forward<decltype(that_alt)>(that_alt).value); + } +#else + assigner<That>{this} +#endif + , + *this, + lib::forward<That>(that)); + } + } + }; + + template <typename Traits, Trait = Traits::move_assignable_trait> + class move_assignment; + +#define MPARK_VARIANT_MOVE_ASSIGNMENT(move_assignable_trait, definition) \ + template <typename... Ts> \ + class move_assignment<traits<Ts...>, move_assignable_trait> \ + : public assignment<traits<Ts...>> { \ + using super = assignment<traits<Ts...>>; \ + \ + public: \ + INHERITING_CTOR(move_assignment, super) \ + using super::operator=; \ + \ + move_assignment(const move_assignment &) = default; \ + move_assignment(move_assignment &&) = default; \ + ~move_assignment() = default; \ + move_assignment &operator=(const move_assignment &) = default; \ + definition \ + } + + MPARK_VARIANT_MOVE_ASSIGNMENT( + Trait::TriviallyAvailable, + move_assignment &operator=(move_assignment &&that) = default;); + + MPARK_VARIANT_MOVE_ASSIGNMENT( + Trait::Available, + move_assignment & + operator=(move_assignment &&that) noexcept( + lib::all<(std::is_nothrow_move_constructible<Ts>::value && + std::is_nothrow_move_assignable<Ts>::value)...>::value) { + this->generic_assign(lib::move(that)); + return *this; + }); + + MPARK_VARIANT_MOVE_ASSIGNMENT( + Trait::Unavailable, + move_assignment &operator=(move_assignment &&) = delete;); + +#undef MPARK_VARIANT_MOVE_ASSIGNMENT + + template <typename Traits, Trait = Traits::copy_assignable_trait> + class copy_assignment; + +#define MPARK_VARIANT_COPY_ASSIGNMENT(copy_assignable_trait, definition) \ + template <typename... Ts> \ + class copy_assignment<traits<Ts...>, copy_assignable_trait> \ + : public move_assignment<traits<Ts...>> { \ + using super = move_assignment<traits<Ts...>>; \ + \ + public: \ + INHERITING_CTOR(copy_assignment, super) \ + using super::operator=; \ + \ + copy_assignment(const copy_assignment &) = default; \ + copy_assignment(copy_assignment &&) = default; \ + ~copy_assignment() = default; \ + definition \ + copy_assignment &operator=(copy_assignment &&) = default; \ + } + + MPARK_VARIANT_COPY_ASSIGNMENT( + Trait::TriviallyAvailable, + copy_assignment &operator=(const copy_assignment &that) = default;); + + MPARK_VARIANT_COPY_ASSIGNMENT( + Trait::Available, + copy_assignment &operator=(const copy_assignment &that) { + this->generic_assign(that); + return *this; + }); + + MPARK_VARIANT_COPY_ASSIGNMENT( + Trait::Unavailable, + copy_assignment &operator=(const copy_assignment &) = delete;); + +#undef MPARK_VARIANT_COPY_ASSIGNMENT + + template <typename... Ts> + class impl : public copy_assignment<traits<Ts...>> { + using super = copy_assignment<traits<Ts...>>; + + public: + INHERITING_CTOR(impl, super) + using super::operator=; + + template <std::size_t I, typename Arg> + inline void assign(Arg &&arg) { + this->assign_alt(access::base::get_alt<I>(*this), + lib::forward<Arg>(arg)); + } + + inline void swap(impl &that) { + if (this->valueless_by_exception() && that.valueless_by_exception()) { + // do nothing. + } else if (this->index() == that.index()) { + visitation::alt::visit_alt_at(this->index(), +#ifdef MPARK_GENERIC_LAMBDAS + [](auto &this_alt, auto &that_alt) { + using std::swap; + swap(this_alt.value, + that_alt.value); + } +#else + swapper{} +#endif + , + *this, + that); + } else { + impl *lhs = this; + impl *rhs = lib::addressof(that); + if (lhs->move_nothrow() && !rhs->move_nothrow()) { + std::swap(lhs, rhs); + } + impl tmp(lib::move(*rhs)); +#ifdef MPARK_EXCEPTIONS + // EXTENSION: When the move construction of `lhs` into `rhs` throws + // and `tmp` is nothrow move constructible then we move `tmp` back + // into `rhs` and provide the strong exception safety guarantee. + try { + this->generic_construct(*rhs, lib::move(*lhs)); + } catch (...) { + if (tmp.move_nothrow()) { + this->generic_construct(*rhs, lib::move(tmp)); + } + throw; + } +#else + this->generic_construct(*rhs, lib::move(*lhs)); +#endif + this->generic_construct(*lhs, lib::move(tmp)); + } + } + + private: +#ifndef MPARK_GENERIC_LAMBDAS + struct swapper { + template <typename ThisAlt, typename ThatAlt> + inline void operator()(ThisAlt &this_alt, ThatAlt &that_alt) const { + using std::swap; + swap(this_alt.value, that_alt.value); + } + }; +#endif + + inline constexpr bool move_nothrow() const { + return this->valueless_by_exception() || + lib::array<bool, sizeof...(Ts)>{ + {std::is_nothrow_move_constructible<Ts>::value...} + }[this->index()]; + } + }; + + template <std::size_t I, typename T> + struct overload_leaf { + using F = lib::size_constant<I> (*)(T); + operator F() const { return nullptr; } + }; + + template <typename... Ts> + struct overload_impl { + private: + template <typename> + struct impl; + + template <std::size_t... Is> + struct impl<lib::index_sequence<Is...>> : overload_leaf<Is, Ts>... {}; + + public: + using type = impl<lib::index_sequence_for<Ts...>>; + }; + + template <typename... Ts> + using overload = typename overload_impl<Ts...>::type; + + template <typename T, typename... Ts> + using best_match = lib::invoke_result_t<overload<Ts...>, T &&>; + + template <typename T> + struct is_in_place_index : std::false_type {}; + + template <std::size_t I> + struct is_in_place_index<in_place_index_t<I>> : std::true_type {}; + + template <typename T> + struct is_in_place_type : std::false_type {}; + + template <typename T> + struct is_in_place_type<in_place_type_t<T>> : std::true_type {}; + + } // detail + + template <typename... Ts> + class variant { + static_assert(0 < sizeof...(Ts), + "variant must consist of at least one alternative."); + + static_assert(lib::all<!std::is_array<Ts>::value...>::value, + "variant can not have an array type as an alternative."); + + static_assert(lib::all<!std::is_reference<Ts>::value...>::value, + "variant can not have a reference type as an alternative."); + + static_assert(lib::all<!std::is_void<Ts>::value...>::value, + "variant can not have a void type as an alternative."); + + public: + template < + typename Front = lib::type_pack_element_t<0, Ts...>, + lib::enable_if_t<std::is_default_constructible<Front>::value, int> = 0> + inline constexpr variant() + : impl_(in_place_index_t<0>{}) {} + + variant(const variant &) = default; + variant(variant &&) = default; + + template < + typename Arg, + typename Decayed = lib::decay_t<Arg>, + lib::enable_if_t<!std::is_same<Decayed, variant>::value, int> = 0, + lib::enable_if_t<!detail::is_in_place_index<Decayed>::value, int> = 0, + lib::enable_if_t<!detail::is_in_place_type<Decayed>::value, int> = 0, + std::size_t I = detail::best_match<Arg, Ts...>::value, + typename T = lib::type_pack_element_t<I, Ts...>, + lib::enable_if_t<std::is_constructible<T, Arg>::value, int> = 0> + inline constexpr variant(Arg &&arg) noexcept( + std::is_nothrow_constructible<T, Arg>::value) + : impl_(in_place_index_t<I>{}, lib::forward<Arg>(arg)) {} + + template < + std::size_t I, + typename... Args, + typename T = lib::type_pack_element_t<I, Ts...>, + lib::enable_if_t<std::is_constructible<T, Args...>::value, int> = 0> + inline explicit constexpr variant( + in_place_index_t<I>, + Args &&... args) noexcept(std::is_nothrow_constructible<T, + Args...>::value) + : impl_(in_place_index_t<I>{}, lib::forward<Args>(args)...) {} + + template < + std::size_t I, + typename Up, + typename... Args, + typename T = lib::type_pack_element_t<I, Ts...>, + lib::enable_if_t<std::is_constructible<T, + std::initializer_list<Up> &, + Args...>::value, + int> = 0> + inline explicit constexpr variant( + in_place_index_t<I>, + std::initializer_list<Up> il, + Args &&... args) noexcept(std:: + is_nothrow_constructible< + T, + std::initializer_list<Up> &, + Args...>::value) + : impl_(in_place_index_t<I>{}, il, lib::forward<Args>(args)...) {} + + template < + typename T, + typename... Args, + std::size_t I = detail::find_index_sfinae<T, Ts...>::value, + lib::enable_if_t<std::is_constructible<T, Args...>::value, int> = 0> + inline explicit constexpr variant( + in_place_type_t<T>, + Args &&... args) noexcept(std::is_nothrow_constructible<T, + Args...>::value) + : impl_(in_place_index_t<I>{}, lib::forward<Args>(args)...) {} + + template < + typename T, + typename Up, + typename... Args, + std::size_t I = detail::find_index_sfinae<T, Ts...>::value, + lib::enable_if_t<std::is_constructible<T, + std::initializer_list<Up> &, + Args...>::value, + int> = 0> + inline explicit constexpr variant( + in_place_type_t<T>, + std::initializer_list<Up> il, + Args &&... args) noexcept(std:: + is_nothrow_constructible< + T, + std::initializer_list<Up> &, + Args...>::value) + : impl_(in_place_index_t<I>{}, il, lib::forward<Args>(args)...) {} + + ~variant() = default; + + variant &operator=(const variant &) = default; + variant &operator=(variant &&) = default; + + template <typename Arg, + lib::enable_if_t<!std::is_same<lib::decay_t<Arg>, variant>::value, + int> = 0, + std::size_t I = detail::best_match<Arg, Ts...>::value, + typename T = lib::type_pack_element_t<I, Ts...>, + lib::enable_if_t<(std::is_assignable<T &, Arg>::value && + std::is_constructible<T, Arg>::value), + int> = 0> + inline variant &operator=(Arg &&arg) { + impl_.template assign<I>(lib::forward<Arg>(arg)); + return *this; + } + + template < + std::size_t I, + typename... Args, + typename T = lib::type_pack_element_t<I, Ts...>, + lib::enable_if_t<std::is_constructible<T, Args...>::value, int> = 0> + inline T &emplace(Args &&... args) { + return impl_.template emplace<I>(lib::forward<Args>(args)...); + } + + template < + std::size_t I, + typename Up, + typename... Args, + typename T = lib::type_pack_element_t<I, Ts...>, + lib::enable_if_t<std::is_constructible<T, + std::initializer_list<Up> &, + Args...>::value, + int> = 0> + inline T &emplace(std::initializer_list<Up> il, Args &&... args) { + return impl_.template emplace<I>(il, lib::forward<Args>(args)...); + } + + template < + typename T, + typename... Args, + std::size_t I = detail::find_index_sfinae<T, Ts...>::value, + lib::enable_if_t<std::is_constructible<T, Args...>::value, int> = 0> + inline T &emplace(Args &&... args) { + return impl_.template emplace<I>(lib::forward<Args>(args)...); + } + + template < + typename T, + typename Up, + typename... Args, + std::size_t I = detail::find_index_sfinae<T, Ts...>::value, + lib::enable_if_t<std::is_constructible<T, + std::initializer_list<Up> &, + Args...>::value, + int> = 0> + inline T &emplace(std::initializer_list<Up> il, Args &&... args) { + return impl_.template emplace<I>(il, lib::forward<Args>(args)...); + } + + inline constexpr bool valueless_by_exception() const noexcept { + return impl_.valueless_by_exception(); + } + + inline constexpr std::size_t index() const noexcept { + return impl_.index(); + } + + template <bool Dummy = true, + lib::enable_if_t< + lib::all<Dummy, + (lib::dependent_type<std::is_move_constructible<Ts>, + Dummy>::value && + lib::dependent_type<lib::is_swappable<Ts>, + Dummy>::value)...>::value, + int> = 0> + inline void swap(variant &that) noexcept( + lib::all<(std::is_nothrow_move_constructible<Ts>::value && + lib::is_nothrow_swappable<Ts>::value)...>::value) { + impl_.swap(that.impl_); + } + + private: + detail::impl<Ts...> impl_; + + friend struct detail::access::variant; + friend struct detail::visitation::variant; + }; + + template <std::size_t I, typename... Ts> + inline constexpr bool holds_alternative(const variant<Ts...> &v) noexcept { + return v.index() == I; + } + + template <typename T, typename... Ts> + inline constexpr bool holds_alternative(const variant<Ts...> &v) noexcept { + return holds_alternative<detail::find_index_checked<T, Ts...>::value>(v); + } + + namespace detail { + template <std::size_t I, typename V> + struct generic_get_impl { + constexpr generic_get_impl(int) {} + + constexpr AUTO_REFREF operator()(V &&v) const + AUTO_REFREF_RETURN( + access::variant::get_alt<I>(lib::forward<V>(v)).value) + }; + + template <std::size_t I, typename V> + inline constexpr AUTO_REFREF generic_get(V &&v) + AUTO_REFREF_RETURN(generic_get_impl<I, V>( + holds_alternative<I>(v) ? 0 : (throw_bad_variant_access(), 0))( + lib::forward<V>(v))) + } // namespace detail + + template <std::size_t I, typename... Ts> + inline constexpr variant_alternative_t<I, variant<Ts...>> &get( + variant<Ts...> &v) { + return detail::generic_get<I>(v); + } + + template <std::size_t I, typename... Ts> + inline constexpr variant_alternative_t<I, variant<Ts...>> &&get( + variant<Ts...> &&v) { + return detail::generic_get<I>(lib::move(v)); + } + + template <std::size_t I, typename... Ts> + inline constexpr const variant_alternative_t<I, variant<Ts...>> &get( + const variant<Ts...> &v) { + return detail::generic_get<I>(v); + } + + template <std::size_t I, typename... Ts> + inline constexpr const variant_alternative_t<I, variant<Ts...>> &&get( + const variant<Ts...> &&v) { + return detail::generic_get<I>(lib::move(v)); + } + + template <typename T, typename... Ts> + inline constexpr T &get(variant<Ts...> &v) { + return get<detail::find_index_checked<T, Ts...>::value>(v); + } + + template <typename T, typename... Ts> + inline constexpr T &&get(variant<Ts...> &&v) { + return get<detail::find_index_checked<T, Ts...>::value>(lib::move(v)); + } + + template <typename T, typename... Ts> + inline constexpr const T &get(const variant<Ts...> &v) { + return get<detail::find_index_checked<T, Ts...>::value>(v); + } + + template <typename T, typename... Ts> + inline constexpr const T &&get(const variant<Ts...> &&v) { + return get<detail::find_index_checked<T, Ts...>::value>(lib::move(v)); + } + + namespace detail { + + template <std::size_t I, typename V> + inline constexpr /* auto * */ AUTO generic_get_if(V *v) noexcept + AUTO_RETURN(v && holds_alternative<I>(*v) + ? lib::addressof(access::variant::get_alt<I>(*v).value) + : nullptr) + + } // namespace detail + + template <std::size_t I, typename... Ts> + inline constexpr lib::add_pointer_t<variant_alternative_t<I, variant<Ts...>>> + get_if(variant<Ts...> *v) noexcept { + return detail::generic_get_if<I>(v); + } + + template <std::size_t I, typename... Ts> + inline constexpr lib::add_pointer_t< + const variant_alternative_t<I, variant<Ts...>>> + get_if(const variant<Ts...> *v) noexcept { + return detail::generic_get_if<I>(v); + } + + template <typename T, typename... Ts> + inline constexpr lib::add_pointer_t<T> + get_if(variant<Ts...> *v) noexcept { + return get_if<detail::find_index_checked<T, Ts...>::value>(v); + } + + template <typename T, typename... Ts> + inline constexpr lib::add_pointer_t<const T> + get_if(const variant<Ts...> *v) noexcept { + return get_if<detail::find_index_checked<T, Ts...>::value>(v); + } + + template <typename... Ts> + inline constexpr bool operator==(const variant<Ts...> &lhs, + const variant<Ts...> &rhs) { + using detail::visitation::variant; + using lib::equal_to; +#ifdef MPARK_CPP14_CONSTEXPR + if (lhs.index() != rhs.index()) return false; + if (lhs.valueless_by_exception()) return true; + return variant::visit_value_at(lhs.index(), equal_to{}, lhs, rhs); +#else + return lhs.index() == rhs.index() && + (lhs.valueless_by_exception() || + variant::visit_value_at(lhs.index(), equal_to{}, lhs, rhs)); +#endif + } + + template <typename... Ts> + inline constexpr bool operator!=(const variant<Ts...> &lhs, + const variant<Ts...> &rhs) { + using detail::visitation::variant; + using lib::not_equal_to; +#ifdef MPARK_CPP14_CONSTEXPR + if (lhs.index() != rhs.index()) return true; + if (lhs.valueless_by_exception()) return false; + return variant::visit_value_at(lhs.index(), not_equal_to{}, lhs, rhs); +#else + return lhs.index() != rhs.index() || + (!lhs.valueless_by_exception() && + variant::visit_value_at(lhs.index(), not_equal_to{}, lhs, rhs)); +#endif + } + + template <typename... Ts> + inline constexpr bool operator<(const variant<Ts...> &lhs, + const variant<Ts...> &rhs) { + using detail::visitation::variant; + using lib::less; +#ifdef MPARK_CPP14_CONSTEXPR + if (rhs.valueless_by_exception()) return false; + if (lhs.valueless_by_exception()) return true; + if (lhs.index() < rhs.index()) return true; + if (lhs.index() > rhs.index()) return false; + return variant::visit_value_at(lhs.index(), less{}, lhs, rhs); +#else + return !rhs.valueless_by_exception() && + (lhs.valueless_by_exception() || lhs.index() < rhs.index() || + (lhs.index() == rhs.index() && + variant::visit_value_at(lhs.index(), less{}, lhs, rhs))); +#endif + } + + template <typename... Ts> + inline constexpr bool operator>(const variant<Ts...> &lhs, + const variant<Ts...> &rhs) { + using detail::visitation::variant; + using lib::greater; +#ifdef MPARK_CPP14_CONSTEXPR + if (lhs.valueless_by_exception()) return false; + if (rhs.valueless_by_exception()) return true; + if (lhs.index() > rhs.index()) return true; + if (lhs.index() < rhs.index()) return false; + return variant::visit_value_at(lhs.index(), greater{}, lhs, rhs); +#else + return !lhs.valueless_by_exception() && + (rhs.valueless_by_exception() || lhs.index() > rhs.index() || + (lhs.index() == rhs.index() && + variant::visit_value_at(lhs.index(), greater{}, lhs, rhs))); +#endif + } + + template <typename... Ts> + inline constexpr bool operator<=(const variant<Ts...> &lhs, + const variant<Ts...> &rhs) { + using detail::visitation::variant; + using lib::less_equal; +#ifdef MPARK_CPP14_CONSTEXPR + if (lhs.valueless_by_exception()) return true; + if (rhs.valueless_by_exception()) return false; + if (lhs.index() < rhs.index()) return true; + if (lhs.index() > rhs.index()) return false; + return variant::visit_value_at(lhs.index(), less_equal{}, lhs, rhs); +#else + return lhs.valueless_by_exception() || + (!rhs.valueless_by_exception() && + (lhs.index() < rhs.index() || + (lhs.index() == rhs.index() && + variant::visit_value_at(lhs.index(), less_equal{}, lhs, rhs)))); +#endif + } + + template <typename... Ts> + inline constexpr bool operator>=(const variant<Ts...> &lhs, + const variant<Ts...> &rhs) { + using detail::visitation::variant; + using lib::greater_equal; +#ifdef MPARK_CPP14_CONSTEXPR + if (rhs.valueless_by_exception()) return true; + if (lhs.valueless_by_exception()) return false; + if (lhs.index() > rhs.index()) return true; + if (lhs.index() < rhs.index()) return false; + return variant::visit_value_at(lhs.index(), greater_equal{}, lhs, rhs); +#else + return rhs.valueless_by_exception() || + (!lhs.valueless_by_exception() && + (lhs.index() > rhs.index() || + (lhs.index() == rhs.index() && + variant::visit_value_at( + lhs.index(), greater_equal{}, lhs, rhs)))); +#endif + } + + struct monostate {}; + + inline constexpr bool operator<(monostate, monostate) noexcept { + return false; + } + + inline constexpr bool operator>(monostate, monostate) noexcept { + return false; + } + + inline constexpr bool operator<=(monostate, monostate) noexcept { + return true; + } + + inline constexpr bool operator>=(monostate, monostate) noexcept { + return true; + } + + inline constexpr bool operator==(monostate, monostate) noexcept { + return true; + } + + inline constexpr bool operator!=(monostate, monostate) noexcept { + return false; + } + +#ifdef MPARK_CPP14_CONSTEXPR + namespace detail { + + inline constexpr bool all(std::initializer_list<bool> bs) { + for (bool b : bs) { + if (!b) { + return false; + } + } + return true; + } + + } // namespace detail + + template <typename Visitor, typename... Vs> + inline constexpr decltype(auto) visit(Visitor &&visitor, Vs &&... vs) { + return (detail::all({!vs.valueless_by_exception()...}) + ? (void)0 + : throw_bad_variant_access()), + detail::visitation::variant::visit_value( + lib::forward<Visitor>(visitor), lib::forward<Vs>(vs)...); + } +#else + namespace detail { + + template <std::size_t N> + inline constexpr bool all_impl(const lib::array<bool, N> &bs, + std::size_t idx) { + return idx >= N || (bs[idx] && all_impl(bs, idx + 1)); + } + + template <std::size_t N> + inline constexpr bool all(const lib::array<bool, N> &bs) { + return all_impl(bs, 0); + } + + } // namespace detail + + template <typename Visitor, typename... Vs> + inline constexpr DECLTYPE_AUTO visit(Visitor &&visitor, Vs &&... vs) + DECLTYPE_AUTO_RETURN( + (detail::all( + lib::array<bool, sizeof...(Vs)>{{!vs.valueless_by_exception()...}}) + ? (void)0 + : throw_bad_variant_access()), + detail::visitation::variant::visit_value(lib::forward<Visitor>(visitor), + lib::forward<Vs>(vs)...)) +#endif + + template <typename... Ts> + inline auto swap(variant<Ts...> &lhs, + variant<Ts...> &rhs) noexcept(noexcept(lhs.swap(rhs))) + -> decltype(lhs.swap(rhs)) { + lhs.swap(rhs); + } + + namespace detail { + + template <typename T, typename...> + using enabled_type = T; + + namespace hash { + + template <typename H, typename K> + constexpr bool meets_requirements() { + return std::is_copy_constructible<H>::value && + std::is_move_constructible<H>::value && + lib::is_invocable_r<std::size_t, H, const K &>::value; + } + + template <typename K> + constexpr bool is_enabled() { + using H = std::hash<K>; + return meets_requirements<H, K>() && + std::is_default_constructible<H>::value && + std::is_copy_assignable<H>::value && + std::is_move_assignable<H>::value; + } + + } // namespace hash + + } // namespace detail + +#undef AUTO +#undef AUTO_RETURN + +#undef AUTO_REFREF +#undef AUTO_REFREF_RETURN + +#undef DECLTYPE_AUTO +#undef DECLTYPE_AUTO_RETURN + +} // namespace mpark + +namespace std { + + template <typename... Ts> + struct hash<mpark::detail::enabled_type< + mpark::variant<Ts...>, + mpark::lib::enable_if_t<mpark::lib::all<mpark::detail::hash::is_enabled< + mpark::lib::remove_const_t<Ts>>()...>::value>>> { + using argument_type = mpark::variant<Ts...>; + using result_type = std::size_t; + + inline result_type operator()(const argument_type &v) const { + using mpark::detail::visitation::variant; + std::size_t result = + v.valueless_by_exception() + ? 299792458 // Random value chosen by the universe upon creation + : variant::visit_alt( +#ifdef MPARK_GENERIC_LAMBDAS + [](const auto &alt) { + using alt_type = mpark::lib::decay_t<decltype(alt)>; + using value_type = mpark::lib::remove_const_t< + typename alt_type::value_type>; + return hash<value_type>{}(alt.value); + } +#else + hasher{} +#endif + , + v); + return hash_combine(result, hash<std::size_t>{}(v.index())); + } + + private: +#ifndef MPARK_GENERIC_LAMBDAS + struct hasher { + template <typename Alt> + inline std::size_t operator()(const Alt &alt) const { + using alt_type = mpark::lib::decay_t<Alt>; + using value_type = + mpark::lib::remove_const_t<typename alt_type::value_type>; + return hash<value_type>{}(alt.value); + } + }; +#endif + + static std::size_t hash_combine(std::size_t lhs, std::size_t rhs) { + return lhs ^= rhs + 0x9e3779b9 + (lhs << 6) + (lhs >> 2); + } + }; + + template <> + struct hash<mpark::monostate> { + using argument_type = mpark::monostate; + using result_type = std::size_t; + + inline result_type operator()(const argument_type &) const noexcept { + return 66740831; // return a fundamentally attractive random value. + } + }; + +} // namespace std + +#if defined(__GNUC__) && __GNUC__ >= 9 +#pragma GCC diagnostic pop +#endif + +#endif // MPARK_VARIANT_HPP diff --git a/src/shared/variant/variant.pri b/src/shared/variant/variant.pri new file mode 100644 index 000000000..aba082dde --- /dev/null +++ b/src/shared/variant/variant.pri @@ -0,0 +1 @@ +INCLUDEPATH += $$PWD diff --git a/src/shared/variant/variant.qbs b/src/shared/variant/variant.qbs new file mode 100644 index 000000000..3a95553b7 --- /dev/null +++ b/src/shared/variant/variant.qbs @@ -0,0 +1,13 @@ +Product { + name: "qbsvariant" + files: [ + "LICENSE.md", + "README.md", + "variant.h", + "variant.hpp" + ] + Export { + Depends { name: "cpp" } + cpp.includePaths: "." + } +} diff --git a/src/src.qbs b/src/src.qbs index 757a16878..39cb18730 100644 --- a/src/src.qbs +++ b/src/src.qbs @@ -7,5 +7,6 @@ Project { "plugins/plugins.qbs", "shared/json/json.qbs", "shared/bundledqt/bundledqt.qbs", + "shared/variant/variant.qbs", ] } diff --git a/tests/auto/blackbox/testdata-android/qml-app/src/main/AndroidManifest.xml b/tests/auto/blackbox/testdata-android/qml-app/src/main/AndroidManifest.xml index 542794825..c8237c639 100644 --- a/tests/auto/blackbox/testdata-android/qml-app/src/main/AndroidManifest.xml +++ b/tests/auto/blackbox/testdata-android/qml-app/src/main/AndroidManifest.xml @@ -16,7 +16,6 @@ <!-- Application arguments --> <meta-data android:name="android.app.lib_name" android:value="-- %%INSERT_APP_LIB_NAME%% --"/> - <meta-data android:name="android.app.qt_sources_resource_id" android:resource="@array/qt_sources"/> <meta-data android:name="android.app.repository" android:value="default"/> <meta-data android:name="android.app.qt_libs_resource_id" android:resource="@array/qt_libs"/> <meta-data android:name="android.app.bundled_libs_resource_id" android:resource="@array/bundled_libs"/> @@ -31,8 +30,6 @@ <meta-data android:name="android.app.load_local_jars" android:value="-- %%INSERT_LOCAL_JARS%% --"/> <meta-data android:name="android.app.static_init_classes" android:value="-- %%INSERT_INIT_CLASSES%% --"/> <!-- Messages maps --> - <meta-data android:value="@string/ministro_not_found_msg" android:name="android.app.ministro_not_found_msg"/> - <meta-data android:value="@string/ministro_needed_msg" android:name="android.app.ministro_needed_msg"/> <meta-data android:value="@string/fatal_error_msg" android:name="android.app.fatal_error_msg"/> <!-- Messages maps --> diff --git a/tests/auto/blackbox/testdata/capnproto/capnproto_cpp_pkgconfig.qbs b/tests/auto/blackbox/testdata/capnproto/capnproto_cpp_pkgconfig.qbs new file mode 100644 index 000000000..58c7d568d --- /dev/null +++ b/tests/auto/blackbox/testdata/capnproto/capnproto_cpp_pkgconfig.qbs @@ -0,0 +1,17 @@ +CppApplication { + Depends { name: "capnproto.cpp"; required: false } + condition: { + var result = qbs.targetPlatform === qbs.hostPlatform; + if (!result) + console.info("targetPlatform differs from hostPlatform"); + if (!capnproto.cpp.present) + console.info("capnproto is not present"); + return result && capnproto.cpp.present; + } + cpp.minimumMacosVersion: "10.8" + files: [ + "capnproto_cpp.cpp", + "foo.capnp" + ] + qbsModuleProviders: "qbspkgconfig" +}
\ No newline at end of file diff --git a/tests/auto/blackbox/testdata/qbspkgconfig-module-provider/libdir/libA.pc b/tests/auto/blackbox/testdata/qbspkgconfig-module-provider/libdir/libA.pc new file mode 100644 index 000000000..077a05893 --- /dev/null +++ b/tests/auto/blackbox/testdata/qbspkgconfig-module-provider/libdir/libA.pc @@ -0,0 +1,6 @@ +Name: libA +Description: just a test +Version: 0.0.1 + +Cflags: -DTHE_MAGIC_DEFINE -I/usr/local/include +Libs: -L/usr/local/lib -llibA diff --git a/tests/auto/blackbox/testdata/qbspkgconfig-module-provider/libs/libA.cpp b/tests/auto/blackbox/testdata/qbspkgconfig-module-provider/libs/libA.cpp new file mode 100644 index 000000000..0c5274415 --- /dev/null +++ b/tests/auto/blackbox/testdata/qbspkgconfig-module-provider/libs/libA.cpp @@ -0,0 +1,14 @@ +#include "libA.h" + +#include <iostream> + +void foo() +{ + std::cout << "hello from foo: "; +#ifdef MYLIB_FRAMEWORK + std::cout << "bundled: yes"; +#else + std::cout << "bundled: no"; +#endif + std::cout << std::endl; +} diff --git a/tests/auto/blackbox/testdata/qbspkgconfig-module-provider/libs/libA.h b/tests/auto/blackbox/testdata/qbspkgconfig-module-provider/libs/libA.h new file mode 100644 index 000000000..ddaaf1609 --- /dev/null +++ b/tests/auto/blackbox/testdata/qbspkgconfig-module-provider/libs/libA.h @@ -0,0 +1,21 @@ +#pragma once + +#if defined(_WIN32) || defined(WIN32) +# define DECL_EXPORT __declspec(dllexport) +# define DECL_IMPORT __declspec(dllimport) +#else +# define DECL_EXPORT __attribute__((visibility("default"))) +# define DECL_IMPORT __attribute__((visibility("default"))) +# endif + +#if defined(LIBA_STATIC_LIBRARY) +# define LIBA_EXPORT +#else +# if defined(MYLIB_LIBRARY) +# define LIBA_EXPORT DECL_EXPORT +# else +# define LIBA_EXPORT DECL_IMPORT +# endif +#endif + +LIBA_EXPORT void foo(); diff --git a/tests/auto/blackbox/testdata/qbspkgconfig-module-provider/libs/libs.qbs b/tests/auto/blackbox/testdata/qbspkgconfig-module-provider/libs/libs.qbs new file mode 100644 index 000000000..9d482415b --- /dev/null +++ b/tests/auto/blackbox/testdata/qbspkgconfig-module-provider/libs/libs.qbs @@ -0,0 +1,29 @@ +import qbs.FileInfo + +Project { + property bool isBundle: false + + DynamicLibrary { + Depends { name: "cpp" } + Depends { name: "bundle" } + name: "libA" + bundle.isBundle: project.isBundle + bundle.publicHeaders: ["libA.h"] + files: "libA.cpp" + cpp.defines: { + var result = []; + if (project.isBundle) + result.push("MYLIB_FRAMEWORK"); + return result; + } + qbs.installPrefix: "" + install: true + installImportLib: true + installDir: "lib" + Group { + files: ["libA.h"] + qbs.install: !project.isBundle + qbs.installDir: FileInfo.joinPaths("include", product.name) + } + } +} diff --git a/tests/auto/blackbox/testdata/qbspkgconfig-module-provider/main.cpp b/tests/auto/blackbox/testdata/qbspkgconfig-module-provider/main.cpp new file mode 100644 index 000000000..5fa0f7eed --- /dev/null +++ b/tests/auto/blackbox/testdata/qbspkgconfig-module-provider/main.cpp @@ -0,0 +1,11 @@ +#include <libA/libA.h> + +#ifndef THE_MAGIC_DEFINE +#error "missing the magic define" +#endif + +int main() +{ + foo(); + return 0; +} diff --git a/tests/auto/blackbox/testdata/qbspkgconfig-module-provider/qbspkgconfig-module-provider.qbs b/tests/auto/blackbox/testdata/qbspkgconfig-module-provider/qbspkgconfig-module-provider.qbs new file mode 100644 index 000000000..d2b3654ae --- /dev/null +++ b/tests/auto/blackbox/testdata/qbspkgconfig-module-provider/qbspkgconfig-module-provider.qbs @@ -0,0 +1,6 @@ +CppApplication { + name: "p" + Depends { name: "libA" } + files: "main.cpp" + qbsModuleProviders: "qbspkgconfig" +} diff --git a/tests/auto/blackbox/tst_blackbox.cpp b/tests/auto/blackbox/tst_blackbox.cpp index 3cacd67e9..41f307632 100644 --- a/tests/auto/blackbox/tst_blackbox.cpp +++ b/tests/auto/blackbox/tst_blackbox.cpp @@ -811,6 +811,7 @@ void TestBlackbox::capnproto_data() QTest::addColumn<QString>("projectFile"); QTest::newRow("cpp") << QStringLiteral("capnproto_cpp.qbs"); + QTest::newRow("cpp-pkgconfig") << QStringLiteral("capnproto_cpp_pkgconfig.qbs"); QTest::newRow("greeter cpp (grpc)") << QStringLiteral("greeter_cpp.qbs"); QTest::newRow("relative import") << QStringLiteral("capnproto_relative_import.qbs"); QTest::newRow("absolute import") << QStringLiteral("capnproto_absolute_import.qbs"); @@ -6144,6 +6145,28 @@ void TestBlackbox::qbsModuleProvidersCompatibility_data() QTest::newRow("named") << QStringList("project.qbsModuleProviders:named_provider") << "from_named_provider"; } +void TestBlackbox::qbspkgconfigModuleProvider() +{ + QDir::setCurrent(testDataDir + "/qbspkgconfig-module-provider/libs"); + + const auto commonParams = QbsRunParameters(QStringLiteral("install"), { + QStringLiteral("qbs.installPrefix:/usr/local"), + QStringLiteral("--install-root"), + QStringLiteral("install-root") + }); + auto dynamicParams = commonParams; + dynamicParams.arguments << "config:library" << "projects.libs.isBundle:false"; + QCOMPARE(runQbs(dynamicParams), 0); + + QDir::setCurrent(testDataDir + "/qbspkgconfig-module-provider"); + + QbsRunParameters params; + params.arguments + << "moduleProviders.qbspkgconfig.libDirs:libdir" + << "moduleProviders.qbspkgconfig.sysroot:" + QDir::currentPath() + "/libs/install-root"; + QCOMPARE(runQbs(params), 0); +} + static QJsonObject getNextSessionPacket(QProcess &session, QByteArray &data) { int totalSize = -1; diff --git a/tests/auto/blackbox/tst_blackbox.h b/tests/auto/blackbox/tst_blackbox.h index 8728f2b10..2f443f681 100644 --- a/tests/auto/blackbox/tst_blackbox.h +++ b/tests/auto/blackbox/tst_blackbox.h @@ -265,6 +265,7 @@ private slots: void qbsModuleProvidersCliOverride_data(); void qbsModuleProvidersCompatibility(); void qbsModuleProvidersCompatibility_data(); + void qbspkgconfigModuleProvider(); void qbsSession(); void qbsVersion(); void qtBug51237(); diff --git a/tests/auto/blackbox/tst_blackboxandroid.cpp b/tests/auto/blackbox/tst_blackboxandroid.cpp index 0d66f60bb..684291eb1 100644 --- a/tests/auto/blackbox/tst_blackboxandroid.cpp +++ b/tests/auto/blackbox/tst_blackboxandroid.cpp @@ -524,7 +524,6 @@ void TestBlackboxAndroid::android_data() "modules.qbs.architecture:" + archsStringList.first(), aaptVersion(enableAapt2), packageType(generateAab)} << enableAapt2 << generateAab << isIncrementalBuild << enableD8; - auto qmlAppExpectedFiles = [&](bool generateAab, bool enableAapt2) { QByteArrayList expectedFile; if (singleArchQt) { @@ -614,7 +613,7 @@ void TestBlackboxAndroid::android_data() "lib/${ARCH}/libplugins_imageformats_qtiff_${ARCH}.so", "lib/${ARCH}/libplugins_imageformats_qwbmp_${ARCH}.so", "lib/${ARCH}/libplugins_imageformats_qwebp_${ARCH}.so"}, generateAab); - if (version >= qbs::Version(6, 0)) + if (version >= qbs::Version(6, 0)) { expectedFile << expandArchs(ndkArchsForQt, { "lib/${ARCH}/libQt6OpenGL_${ARCH}.so", "lib/${ARCH}/libQt6QuickControls2Impl_${ARCH}.so", @@ -627,8 +626,6 @@ void TestBlackboxAndroid::android_data() "lib/${ARCH}/libqml_QtQml_Models_modelsplugin_${ARCH}.so", "lib/${ARCH}/libqml_QtQml_WorkerScript_workerscriptplugin_${ARCH}.so", "lib/${ARCH}/libqml_QtQml_qmlplugin_${ARCH}.so", - "lib/${ARCH}/libqml_QtQuick_Window_quickwindow_${ARCH}.so", - "lib/${ARCH}/libqml_QtQuick_tooling_quicktooling_${ARCH}.so", "lib/${ARCH}/libqml_QtQuick_Controls_Basic_impl_qtquickcontrols2basicstyleimplplugin_${ARCH}.so", "lib/${ARCH}/libqml_QtQuick_Controls_Basic_qtquickcontrols2basicstyleplugin_${ARCH}.so", "lib/${ARCH}/libqml_QtQuick_Controls_Fusion_impl_qtquickcontrols2fusionstyleimplplugin_${ARCH}.so", @@ -649,6 +646,29 @@ void TestBlackboxAndroid::android_data() "lib/${ARCH}/libqml_QtQuick_Timeline_qtquicktimelineplugin_${ARCH}.so", "lib/${ARCH}/libqml_QtQuick_Layouts_qquicklayoutsplugin_${ARCH}.so", "lib/${ARCH}/libqml_QtQuick_qtquick2plugin_${ARCH}.so"}, generateAab); + if (version >= qbs::Version(6, 2)) + expectedFile << expandArchs(ndkArchsForQt, { + "lib/${ARCH}/libqml_QtQuick_Window_quickwindowplugin_${ARCH}.so", + "lib/${ARCH}/libqml_QtQuick_tooling_quicktoolingplugin_${ARCH}.so", + "lib/${ARCH}/libQt6QmlLocalStorage_${ARCH}.so", + "lib/${ARCH}/libQt6QmlXmlListModel_${ARCH}.so", + "lib/${ARCH}/libQt6QuickDialogs2QuickImpl_${ARCH}.so", + "lib/${ARCH}/libQt6QuickDialogs2Utils_${ARCH}.so", + "lib/${ARCH}/libQt6QuickDialogs2_${ARCH}.so", + "lib/${ARCH}/libQt6QuickLayouts_${ARCH}.so", + "lib/${ARCH}/libQt6QuickTimeline_${ARCH}.so", + "lib/${ARCH}/libplugins_networkinformation_qandroidnetworkinformation_${ARCH}.so", + "lib/${ARCH}/libplugins_tls_qcertonlybackend_${ARCH}.so", + "lib/${ARCH}/libplugins_tls_qopensslbackend_${ARCH}.so", + "lib/${ARCH}/libqml_QtQml_XmlListModel_qmlxmllistmodelplugin_${ARCH}.so", + "lib/${ARCH}/libqml_QtQuick_Dialogs_qtquickdialogsplugin_${ARCH}.so", + "lib/${ARCH}/libqml_QtQuick_Dialogs_quickimpl_qtquickdialogs2quickimplplugin_${ARCH}.so"}, + generateAab); + else + expectedFile << expandArchs(ndkArchsForQt, { + "lib/${ARCH}/libqml_QtQuick_Window_quickwindow_${ARCH}.so", + "lib/${ARCH}/libqml_QtQuick_tooling_quicktooling_${ARCH}.so"}, generateAab); + } } if (generateAab) expectedFile << "base/resources.pb" << "base/assets.pb" << "base/native.pb"; @@ -658,7 +678,6 @@ void TestBlackboxAndroid::android_data() expectedFile << "res/layout/splash.xml"; return expectedFile; }; - auto qmlAppCustomMetaDataExpectedFiles = [&](bool generateAab, bool enableAapt2) { QByteArrayList expectedFile; if (singleArchQt) { @@ -750,7 +769,7 @@ void TestBlackboxAndroid::android_data() "lib/${ARCH}/libplugins_imageformats_qtiff_${ARCH}.so", "lib/${ARCH}/libplugins_imageformats_qwbmp_${ARCH}.so", "lib/${ARCH}/libplugins_imageformats_qwebp_${ARCH}.so"}, generateAab); - if (version >= qbs::Version(6, 0)) + if (version >= qbs::Version(6, 0)) { expectedFile << expandArchs(ndkArchsForQt, { "lib/${ARCH}/libQt6OpenGL_${ARCH}.so", "lib/${ARCH}/libQt6QuickControls2Impl_${ARCH}.so", @@ -763,8 +782,6 @@ void TestBlackboxAndroid::android_data() "lib/${ARCH}/libqml_QtQml_Models_modelsplugin_${ARCH}.so", "lib/${ARCH}/libqml_QtQml_WorkerScript_workerscriptplugin_${ARCH}.so", "lib/${ARCH}/libqml_QtQml_qmlplugin_${ARCH}.so", - "lib/${ARCH}/libqml_QtQuick_Window_quickwindow_${ARCH}.so", - "lib/${ARCH}/libqml_QtQuick_tooling_quicktooling_${ARCH}.so", "lib/${ARCH}/libqml_QtQuick_Controls_Basic_impl_qtquickcontrols2basicstyleimplplugin_${ARCH}.so", "lib/${ARCH}/libqml_QtQuick_Controls_Basic_qtquickcontrols2basicstyleplugin_${ARCH}.so", "lib/${ARCH}/libqml_QtQuick_Controls_Fusion_impl_qtquickcontrols2fusionstyleimplplugin_${ARCH}.so", @@ -785,6 +802,29 @@ void TestBlackboxAndroid::android_data() "lib/${ARCH}/libqml_QtQuick_Timeline_qtquicktimelineplugin_${ARCH}.so", "lib/${ARCH}/libqml_QtQuick_Layouts_qquicklayoutsplugin_${ARCH}.so", "lib/${ARCH}/libqml_QtQuick_qtquick2plugin_${ARCH}.so"}, generateAab); + if (version >= qbs::Version(6, 2)) + expectedFile << expandArchs(ndkArchsForQt, { + "lib/${ARCH}/libqml_QtQuick_Window_quickwindowplugin_${ARCH}.so", + "lib/${ARCH}/libqml_QtQuick_tooling_quicktoolingplugin_${ARCH}.so", + "lib/${ARCH}/libQt6QmlLocalStorage_${ARCH}.so", + "lib/${ARCH}/libQt6QmlXmlListModel_${ARCH}.so", + "lib/${ARCH}/libQt6QuickDialogs2QuickImpl_${ARCH}.so", + "lib/${ARCH}/libQt6QuickDialogs2Utils_${ARCH}.so", + "lib/${ARCH}/libQt6QuickDialogs2_${ARCH}.so", + "lib/${ARCH}/libQt6QuickLayouts_${ARCH}.so", + "lib/${ARCH}/libQt6QuickTimeline_${ARCH}.so", + "lib/${ARCH}/libplugins_networkinformation_qandroidnetworkinformation_${ARCH}.so", + "lib/${ARCH}/libplugins_tls_qcertonlybackend_${ARCH}.so", + "lib/${ARCH}/libplugins_tls_qopensslbackend_${ARCH}.so", + "lib/${ARCH}/libqml_QtQml_XmlListModel_qmlxmllistmodelplugin_${ARCH}.so", + "lib/${ARCH}/libqml_QtQuick_Dialogs_qtquickdialogsplugin_${ARCH}.so", + "lib/${ARCH}/libqml_QtQuick_Dialogs_quickimpl_qtquickdialogs2quickimplplugin_${ARCH}.so"}, + generateAab); + else + expectedFile << expandArchs(ndkArchsForQt, { + "lib/${ARCH}/libqml_QtQuick_Window_quickwindow_${ARCH}.so", + "lib/${ARCH}/libqml_QtQuick_tooling_quicktooling_${ARCH}.so"}, generateAab); + } } if (generateAab) expectedFile << "base/resources.pb" << "base/assets.pb" << "base/native.pb"; @@ -817,7 +857,6 @@ void TestBlackboxAndroid::android_data() << (QStringList() << qmlAppCustomProperties << aaptVersion(enableAapt2) << packageType(generateAab)) << enableAapt2 << generateAab << isIncrementalBuild << enableD8; - enableAapt2 = true; QTest::newRow("qml app aapt2") << "qml-app" << QStringList("qmlapp") diff --git a/tests/auto/pkgconfig/testdata/base.name.json b/tests/auto/pkgconfig/testdata/base.name.json new file mode 100644 index 000000000..a10e905ac --- /dev/null +++ b/tests/auto/pkgconfig/testdata/base.name.json @@ -0,0 +1,20 @@ +{ + "Name": "Base Name test", + "Description": "Checks correct baseName detection", + "Version": "1.0.0", + "Vars": { + "prefix": "/usr", + "exec_prefix": "/usr", + "libdir": "/usr/lib", + "includedir": "/usr/include" + }, + "Libs": [ + {"Type": "LibraryName", "Value": "simple"} + ], + "LibsPrivate": [ + {"Type": "LibraryName", "Value": "m"} + ], + "Cflags": [ + {"Type": "IncludePath", "Value": "/usr/include"} + ] +} diff --git a/tests/auto/pkgconfig/testdata/base.name.pc b/tests/auto/pkgconfig/testdata/base.name.pc new file mode 100644 index 000000000..2bb3e275e --- /dev/null +++ b/tests/auto/pkgconfig/testdata/base.name.pc @@ -0,0 +1,12 @@ +prefix=/usr +exec_prefix=${prefix} +libdir=${exec_prefix}/lib +includedir=${prefix}/include + +Name: Base Name test +Description: Checks correct baseName detection +Version: 1.0.0 +Requires: +Libs: -lsimple +Libs.private: -lm +Cflags: -I${includedir} diff --git a/tests/auto/pkgconfig/testdata/private-dep.pc b/tests/auto/pkgconfig/testdata/private-dep.pc new file mode 100644 index 000000000..cb401391d --- /dev/null +++ b/tests/auto/pkgconfig/testdata/private-dep.pc @@ -0,0 +1,6 @@ +Name: Requires test package +Description: Dummy pkgconfig test package for testing Requires/Requires.private +Version: 1.0.0 +Libs: -L/private-dep/lib -lprivate-dep +Cflags: -I/private-dep/include + diff --git a/tests/auto/pkgconfig/testdata/public-dep.pc b/tests/auto/pkgconfig/testdata/public-dep.pc new file mode 100644 index 000000000..e450e46f1 --- /dev/null +++ b/tests/auto/pkgconfig/testdata/public-dep.pc @@ -0,0 +1,6 @@ +Name: Requires test package +Description: Dummy pkgconfig test package for testing Requires/Requires.private +Version: 1.0.0 +Requires.private: +Libs: -L/public-dep/lib -lpublic-dep +Cflags: -I/public-dep/include diff --git a/tests/auto/pkgconfig/testdata/requires-test-merged-static.json b/tests/auto/pkgconfig/testdata/requires-test-merged-static.json new file mode 100644 index 000000000..2c43b2d40 --- /dev/null +++ b/tests/auto/pkgconfig/testdata/requires-test-merged-static.json @@ -0,0 +1,22 @@ +{ + "Name": "Requires test package", + "Description": "Dummy pkgconfig test package for testing Requires/Requires.private", + "Version": "1.0.0", + "Libs": [ + {"Type": "LibraryPath", "Value": "/public-dep/lib"}, + {"Type": "LibraryName", "Value": "public-dep"}, + {"Type": "LibraryPath", "Value": "/private-dep/lib"}, + {"Type": "LibraryName", "Value": "private-dep"}, + {"Type": "LibraryPath", "Value": "/requires-test/lib"}, + {"Type": "LibraryName", "Value": "requires-test"} + ], + "Cflags": [ + {"Type": "IncludePath", "Value": "/public-dep/include"}, + {"Type": "IncludePath", "Value": "/private-dep/include"}, + {"Type": "IncludePath", "Value": "/requires-test/include"} + ], + "Requires": [ + ], + "RequiresPrivate": [ + ] +} diff --git a/tests/auto/pkgconfig/testdata/requires-test-merged.json b/tests/auto/pkgconfig/testdata/requires-test-merged.json new file mode 100644 index 000000000..88114ba30 --- /dev/null +++ b/tests/auto/pkgconfig/testdata/requires-test-merged.json @@ -0,0 +1,19 @@ +{ + "Name": "Requires test package", + "Description": "Dummy pkgconfig test package for testing Requires/Requires.private", + "Version": "1.0.0", + "Libs": [ + {"Type": "LibraryPath", "Value": "/public-dep/lib"}, + {"Type": "LibraryName", "Value": "public-dep"}, + {"Type": "LibraryPath", "Value": "/requires-test/lib"}, + {"Type": "LibraryName", "Value": "requires-test"} + ], + "Cflags": [ + {"Type": "IncludePath", "Value": "/public-dep/include"}, + {"Type": "IncludePath", "Value": "/requires-test/include"} + ], + "Requires": [ + ], + "RequiresPrivate": [ + ] +} diff --git a/tests/auto/pkgconfig/tst_pkgconfig.cpp b/tests/auto/pkgconfig/tst_pkgconfig.cpp index b05dd4923..542984378 100644 --- a/tests/auto/pkgconfig/tst_pkgconfig.cpp +++ b/tests/auto/pkgconfig/tst_pkgconfig.cpp @@ -60,22 +60,32 @@ void TestPkgConfig::initTestCase() void TestPkgConfig::pkgConfig() { - QFETCH(QString, fileName); + QFETCH(QString, pcFileName); + QFETCH(QString, jsonFileName); QFETCH(QVariantMap, optionsMap); - Options options = qbs::Internal::PkgConfigJs::convertOptions(QProcessEnvironment::systemEnvironment(), optionsMap); - options.searchPaths.push_back(m_workingDataDir.toStdString()); + if (jsonFileName.isEmpty()) + jsonFileName = pcFileName; + + if (!optionsMap.contains("mergeDependencies")) + optionsMap["mergeDependencies"] = false; + + Options options = qbs::Internal::PkgConfigJs::convertOptions( + QProcessEnvironment::systemEnvironment(), optionsMap); + options.libDirs.push_back(m_workingDataDir.toStdString()); PkgConfig pkgConfig(std::move(options)); - QFile jsonFile(m_workingDataDir + "/" + fileName + ".json"); + QFile jsonFile(m_workingDataDir + "/" + jsonFileName + ".json"); QVERIFY(jsonFile.open(QIODevice::ReadOnly)); QJsonParseError error{}; const auto json = QJsonDocument::fromJson(jsonFile.readAll(), &error).toVariant().toMap(); QCOMPARE(error.error, QJsonParseError::NoError); - const auto &package = pkgConfig.getPackage(fileName.toStdString()); - QCOMPARE(QString::fromStdString(package.baseFileName), fileName); + const auto &packageOr = pkgConfig.getPackage(pcFileName.toStdString()); + QVERIFY(packageOr.isValid()); + const auto &package = packageOr.asPackage(); + QCOMPARE(QString::fromStdString(package.baseFileName), pcFileName); QCOMPARE(QString::fromStdString(package.name), json.value("Name").toString()); QCOMPARE(QString::fromStdString(package.description), json.value("Description").toString()); QCOMPARE(QString::fromStdString(package.version), json.value("Version").toString()); @@ -153,19 +163,38 @@ void TestPkgConfig::pkgConfig() void TestPkgConfig::pkgConfig_data() { - QTest::addColumn<QString>("fileName"); + QTest::addColumn<QString>("pcFileName"); + QTest::addColumn<QString>("jsonFileName"); QTest::addColumn<QVariantMap>("optionsMap"); - QTest::newRow("non-l-required") << QStringLiteral("non-l-required") << QVariantMap(); - QTest::newRow("simple") << QStringLiteral("simple") << QVariantMap(); - QTest::newRow("requires-test") << QStringLiteral("requires-test") << QVariantMap(); - QTest::newRow("special-flags") << QStringLiteral("special-flags") << QVariantMap(); - QTest::newRow("system") << QStringLiteral("system") << QVariantMap(); + QTest::newRow("non-l-required") + << QStringLiteral("non-l-required") << QString() << QVariantMap(); + QTest::newRow("simple") + << QStringLiteral("simple") << QString() << QVariantMap(); + QTest::newRow("requires-test") + << QStringLiteral("requires-test") << QString() << QVariantMap(); + QTest::newRow("requires-test-merged") + << QStringLiteral("requires-test") + << QStringLiteral("requires-test-merged") + << QVariantMap({{"mergeDependencies", true}}); + QTest::newRow("requires-test-merged-static") + << QStringLiteral("requires-test") + << QStringLiteral("requires-test-merged-static") + << QVariantMap({{"mergeDependencies", true}, {"staticMode", true}}); + QTest::newRow("special-flags") + << QStringLiteral("special-flags") << QString() << QVariantMap(); + QTest::newRow("system") + << QStringLiteral("system") << QString() << QVariantMap(); QTest::newRow("sysroot") - << QStringLiteral("sysroot") << QVariantMap({{"sysroot", "/newroot"}}); - QTest::newRow("tilde") << QStringLiteral("tilde") << QVariantMap(); - QTest::newRow("variables") << QStringLiteral("variables") << QVariantMap(); - QTest::newRow("whitespace") << QStringLiteral("whitespace") << QVariantMap(); + << QStringLiteral("sysroot") << QString() << QVariantMap({{"sysroot", "/newroot"}}); + QTest::newRow("tilde") + << QStringLiteral("tilde") << QString() << QVariantMap(); + QTest::newRow("variables") + << QStringLiteral("variables") << QString() << QVariantMap(); + QTest::newRow("whitespace") + << QStringLiteral("whitespace") << QString() << QVariantMap(); + QTest::newRow("base.name") + << QStringLiteral("base.name") << QString() << QVariantMap(); } void TestPkgConfig::benchSystem() |