aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorIvan Komissarov <abbapoh@gmail.com>2021-10-26 13:22:27 +0300
committerIvan Komissarov <abbapoh@gmail.com>2021-10-26 13:22:27 +0300
commite811681b9024b42fe6d064f2e2e37392f6093b86 (patch)
treeb27724cb432036c4f119309dc7d27381a26872c7
parent05c46fbeb5256eb0935b49d1d05197a448e49830 (diff)
parent24044d6da5d3f3725adc134fdb7e71fe398381ff (diff)
Merge branch '1.21' into master
-rw-r--r--.github/workflows/main.yml14
-rw-r--r--cmake/QbsDocumentation.cmake4
-rw-r--r--doc/qbs.qdoc20
-rw-r--r--doc/reference/module-providers/qbspkgconfig-module-provider.qdoc124
-rw-r--r--doc/reference/module-providers/qt-module-provider.qdoc76
-rw-r--r--doc/reference/modules/qt-modules.qdoc18
-rw-r--r--doc/reference/reference.qdoc6
-rw-r--r--docker-compose.yml11
-rw-r--r--docker/focal/test-android.Dockerfile6
-rw-r--r--examples/pkgconfig-provider/main.c137
-rw-r--r--examples/pkgconfig-provider/pkgconfig-provider.qbs60
-rw-r--r--share/qbs/module-providers/Qt/setup-qt.js13
-rw-r--r--share/qbs/module-providers/Qt/templates/android_support.qbs3
-rw-r--r--share/qbs/module-providers/Qt/templates/core.qbs1
-rw-r--r--share/qbs/module-providers/qbspkgconfig.qbs232
-rw-r--r--share/share.qbs2
-rw-r--r--src/lib/corelib/jsextensions/pkgconfigjs.cpp65
-rw-r--r--src/lib/corelib/jsextensions/pkgconfigjs.h6
-rw-r--r--src/lib/corelib/language/item.h1
-rw-r--r--src/lib/corelib/language/moduleloader.cpp6
-rw-r--r--src/lib/corelib/language/modulemerger.cpp3
-rw-r--r--src/lib/corelib/tools/qttools.cpp3
-rw-r--r--src/lib/pkgconfig/CMakeLists.txt9
-rw-r--r--src/lib/pkgconfig/pcpackage.h59
-rw-r--r--src/lib/pkgconfig/pcparser.cpp20
-rw-r--r--src/lib/pkgconfig/pcparser.h2
-rw-r--r--src/lib/pkgconfig/pkgconfig.cpp273
-rw-r--r--src/lib/pkgconfig/pkgconfig.h15
-rw-r--r--src/lib/pkgconfig/pkgconfig.pro5
-rw-r--r--src/lib/pkgconfig/pkgconfig.qbs7
-rw-r--r--src/lib/pkgconfig/use_pkgconfig.pri11
-rw-r--r--src/shared/CMakeLists.txt1
-rw-r--r--src/shared/variant/CMakeLists.txt4
-rw-r--r--src/shared/variant/LICENSE.md23
-rw-r--r--src/shared/variant/README.md37
-rw-r--r--src/shared/variant/variant.h63
-rw-r--r--src/shared/variant/variant.hpp2465
-rw-r--r--src/shared/variant/variant.pri1
-rw-r--r--src/shared/variant/variant.qbs13
-rw-r--r--src/src.qbs1
-rw-r--r--tests/auto/blackbox/testdata-android/qml-app/src/main/AndroidManifest.xml3
-rw-r--r--tests/auto/blackbox/testdata/capnproto/capnproto_cpp_pkgconfig.qbs17
-rw-r--r--tests/auto/blackbox/testdata/qbspkgconfig-module-provider/libdir/libA.pc6
-rw-r--r--tests/auto/blackbox/testdata/qbspkgconfig-module-provider/libs/libA.cpp14
-rw-r--r--tests/auto/blackbox/testdata/qbspkgconfig-module-provider/libs/libA.h21
-rw-r--r--tests/auto/blackbox/testdata/qbspkgconfig-module-provider/libs/libs.qbs29
-rw-r--r--tests/auto/blackbox/testdata/qbspkgconfig-module-provider/main.cpp11
-rw-r--r--tests/auto/blackbox/testdata/qbspkgconfig-module-provider/qbspkgconfig-module-provider.qbs6
-rw-r--r--tests/auto/blackbox/tst_blackbox.cpp23
-rw-r--r--tests/auto/blackbox/tst_blackbox.h1
-rw-r--r--tests/auto/blackbox/tst_blackboxandroid.cpp57
-rw-r--r--tests/auto/pkgconfig/testdata/base.name.json20
-rw-r--r--tests/auto/pkgconfig/testdata/base.name.pc12
-rw-r--r--tests/auto/pkgconfig/testdata/private-dep.pc6
-rw-r--r--tests/auto/pkgconfig/testdata/public-dep.pc6
-rw-r--r--tests/auto/pkgconfig/testdata/requires-test-merged-static.json22
-rw-r--r--tests/auto/pkgconfig/testdata/requires-test-merged.json19
-rw-r--r--tests/auto/pkgconfig/tst_pkgconfig.cpp61
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()