summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIvan Komissarov <abbapoh@gmail.com>2021-05-14 00:55:15 +0200
committerIvan Komissarov <ABBAPOH@gmail.com>2021-10-14 10:03:50 +0000
commit3187199a17e013882b0d0ea7aff9c7b9411e4cd1 (patch)
tree5e82271f0702e037898c723bcdaf6ab426eba342
parente7cc3a2cf13db0f786727ce2469f7c66324f35a2 (diff)
downloadqbs-3187199a17e013882b0d0ea7aff9c7b9411e4cd1.tar.gz
Implement eager pkg-config provider
This implements provider that generates modules based on all .pc files present in system. This allows to get rid of the multi-shot providers such as fallback provider. Fixes: QBS-1614 Change-Id: Icf87ac609bc34bd26e8ed94ae547a7e649835a3a Reviewed-by: Christian Kandeler <christian.kandeler@qt.io>
-rw-r--r--doc/qbs.qdoc1
-rw-r--r--doc/reference/module-providers/qbspkgconfig-module-provider.qdoc99
-rw-r--r--doc/reference/reference.qdoc6
-rw-r--r--examples/pkgconfig-provider/main.c137
-rw-r--r--examples/pkgconfig-provider/pkgconfig-provider.qbs60
-rw-r--r--share/qbs/module-providers/qbspkgconfig.qbs216
-rw-r--r--share/share.qbs2
-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.cpp22
-rw-r--r--tests/auto/blackbox/tst_blackbox.h1
15 files changed, 630 insertions, 1 deletions
diff --git a/doc/qbs.qdoc b/doc/qbs.qdoc
index ac7bf20c2..f297ea2cf 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
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..80afe3667
--- /dev/null
+++ b/doc/reference/module-providers/qbspkgconfig-module-provider.qdoc
@@ -0,0 +1,99 @@
+/****************************************************************************
+**
+** 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_PATH} / \c{PKG_CONFIG_LIBDIR} variables
+ 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
+*/
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/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/qbspkgconfig.qbs b/share/qbs/module-providers/qbspkgconfig.qbs
new file mode 100644
index 000000000..ad6d64027
--- /dev/null
+++ b/share/qbs/module-providers/qbspkgconfig.qbs
@@ -0,0 +1,216 @@
+/****************************************************************************
+**
+** 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 libDirs
+ property bool staticMode: false
+ property path sysroot: {
+ if (qbs.targetOS.contains("macos"))
+ return "";
+ return qbs.sysroot;
+ }
+
+ 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 libsInfo = !staticMode ? getLibsInfo(pkg.libs) : getLibsInfo(pkg.libsPrivate);
+ 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.searchPaths = libDirs;
+ options.sysroot = sysroot;
+ if (options.sysroot && !options.searchPaths) {
+ options.searchPaths = [
+ sysroot + "/usr/lib/pkgconfig",
+ sysroot + "/usr/share/pkgconfig"
+ ];
+ }
+ if (!options.searchPaths) {
+ // 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.searchPaths = stdout ? stdout.split(':'): [];
+ }
+ }
+ }
+
+ var pkgConfig = new PkgConfig(options);
+ var brokenPackages = pkgConfig.brokenPackages();
+ 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);
+ }
+ }
+ var packages = pkgConfig.packages();
+ for (var packageName in packages) {
+ var moduleName = getModuleName(packageName);
+ var moduleInfo = getModuleInfo(packages[packageName], staticMode);
+ var deps = getModuleDependencies(packages[packageName], 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();
+ }
+ 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/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..44026fc86 100644
--- a/tests/auto/blackbox/tst_blackbox.cpp
+++ b/tests/auto/blackbox/tst_blackbox.cpp
@@ -6144,6 +6144,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();