aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorIvan Komissarov <abbapoh@gmail.com>2023-12-21 01:02:15 +0300
committerIvan Komissarov <ABBAPOH@gmail.com>2024-01-10 10:25:04 +0000
commit167799f31c8ca3cfd79394b6c3fe8389c7df4de1 (patch)
tree00540557d51f669a1eaa8198bc583f760ce216fb
parent5fcdf43b3fb4e7c5fb85cbfba7f79b1d85dae410 (diff)
qbspkgconfig: Add support for the definePrefix option
On windows/msys, it is crucial to use autodetection for package paths - otherwise, Qbs is not able to detect e.g. protobuf package. The pkg-config has the --define-prefix switch that overrides the ${prefix} variables on the package with one based on the .pc file locaiton. Implement similar option and set it to true on Windows. Also fix setting sysroot to avoid duplication of sysroot in paths when prefix is autodetected. Change-Id: I2754e48a96cb2e5f01ecace616188782cb5b0d7a Reviewed-by: Christian Kandeler <christian.kandeler@qt.io>
-rw-r--r--doc/reference/module-providers/qbspkgconfig-module-provider.qdoc12
-rw-r--r--share/qbs/imports/qbs/Probes/QbsPkgConfigProbe.qbs9
-rw-r--r--share/qbs/imports/qbs/Probes/qbs-pkg-config-probe.js3
-rw-r--r--share/qbs/module-providers/qbspkgconfig.qbs3
-rw-r--r--src/lib/corelib/jsextensions/pkgconfigjs.cpp1
-rw-r--r--src/lib/pkgconfig/pcpackage.cpp71
-rw-r--r--src/lib/pkgconfig/pcpackage.h22
-rw-r--r--src/lib/pkgconfig/pcparser.cpp69
-rw-r--r--src/lib/pkgconfig/pcparser.h1
-rw-r--r--src/lib/pkgconfig/pkgconfig.h1
-rw-r--r--tests/auto/pkgconfig/testdata/lib/pkgconfig/prefix.pc13
-rw-r--r--tests/auto/pkgconfig/tst_pkgconfig.cpp64
-rw-r--r--tests/auto/pkgconfig/tst_pkgconfig.h4
13 files changed, 239 insertions, 34 deletions
diff --git a/doc/reference/module-providers/qbspkgconfig-module-provider.qdoc b/doc/reference/module-providers/qbspkgconfig-module-provider.qdoc
index 461536fbc..51f3bccb7 100644
--- a/doc/reference/module-providers/qbspkgconfig-module-provider.qdoc
+++ b/doc/reference/module-providers/qbspkgconfig-module-provider.qdoc
@@ -87,6 +87,18 @@
*/
/*!
+ \qmlproperty bool qbspkgconfig::definePrefix
+
+ If this property is \c true, then \QBS will override the ${prefix} variable in the packages
+ with a value that is guessed based on the location of the .pc file.
+
+ This option corresponds to the \c --define-prefix / \c --dont-define-prefix command line
+ options of the \c pkg-config tool.
+
+ \defaultvalue \c true on Windows, \c false otherwise
+*/
+
+/*!
\qmlproperty path qbspkgconfig::sysroot
Set this property if you need to overwrite the default search sysroot path used by
diff --git a/share/qbs/imports/qbs/Probes/QbsPkgConfigProbe.qbs b/share/qbs/imports/qbs/Probes/QbsPkgConfigProbe.qbs
index 47f162955..56b04227e 100644
--- a/share/qbs/imports/qbs/Probes/QbsPkgConfigProbe.qbs
+++ b/share/qbs/imports/qbs/Probes/QbsPkgConfigProbe.qbs
@@ -37,6 +37,7 @@ Probe {
property stringList _extraPaths
property stringList _libDirs
property bool _staticMode: false
+ property bool _definePrefix: false
property path _sysroot
@@ -51,7 +52,13 @@ Probe {
configure: {
var result = PkgConfigProbeConfigure.configure(
- _executableFilePath, _extraPaths, _libDirs, _staticMode, _sysroot, _mergeDependencies);
+ _executableFilePath,
+ _extraPaths,
+ _libDirs,
+ _staticMode,
+ _definePrefix,
+ _sysroot,
+ _mergeDependencies);
packages = result.packages;
packagesByModuleName = result.packagesByModuleName;
brokenPackages = result.brokenPackages;
diff --git a/share/qbs/imports/qbs/Probes/qbs-pkg-config-probe.js b/share/qbs/imports/qbs/Probes/qbs-pkg-config-probe.js
index 14cb3f81a..ed75a027c 100644
--- a/share/qbs/imports/qbs/Probes/qbs-pkg-config-probe.js
+++ b/share/qbs/imports/qbs/Probes/qbs-pkg-config-probe.js
@@ -81,7 +81,7 @@ function configureQt(pkg) {
}
function configure(
- executableFilePath, extraPaths, libDirs, staticMode, sysroot, mergeDependencies) {
+ executableFilePath, extraPaths, libDirs, staticMode, definePrefix, sysroot, mergeDependencies) {
var result = {};
result.packages = [];
@@ -92,6 +92,7 @@ function configure(
var options = {};
options.libDirs = libDirs;
options.sysroot = sysroot;
+ options.definePrefix = definePrefix;
if (options.sysroot)
options.allowSystemLibraryPaths = true;
options.staticMode = staticMode;
diff --git a/share/qbs/module-providers/qbspkgconfig.qbs b/share/qbs/module-providers/qbspkgconfig.qbs
index ca04f0613..aebed0ab3 100644
--- a/share/qbs/module-providers/qbspkgconfig.qbs
+++ b/share/qbs/module-providers/qbspkgconfig.qbs
@@ -40,6 +40,7 @@
import qbs.Environment
import qbs.File
import qbs.FileInfo
+import qbs.Host
import qbs.ModUtils
import qbs.PkgConfig
import qbs.ProviderUtils
@@ -54,6 +55,7 @@ ModuleProvider {
property stringList extraPaths
property stringList libDirs
property bool staticMode: false
+ property bool definePrefix: Host.os().includes("windows")
// We take the sysroot default from qbs.sysroot, except for Xcode toolchains, where
// the sysroot points into the Xcode installation and does not contain .pc files.
@@ -74,6 +76,7 @@ ModuleProvider {
_sysroot: parent.sysroot
_libDirs: parent.libDirs
_staticMode: parent.staticMode
+ _definePrefix: parent.definePrefix
}
isEager: false
diff --git a/src/lib/corelib/jsextensions/pkgconfigjs.cpp b/src/lib/corelib/jsextensions/pkgconfigjs.cpp
index d84c42d8e..e663c537f 100644
--- a/src/lib/corelib/jsextensions/pkgconfigjs.cpp
+++ b/src/lib/corelib/jsextensions/pkgconfigjs.cpp
@@ -241,6 +241,7 @@ PkgConfig::Options PkgConfigJs::convertOptions(const QProcessEnvironment &env, c
[](const QString &str){ return str.toStdString(); });
result.disableUninstalled = map.value(QStringLiteral("disableUninstalled"), true).toBool();
result.staticMode = map.value(QStringLiteral("staticMode"), false).toBool();
+ result.definePrefix = map.value(QStringLiteral("definePrefix"), false).toBool();
result.mergeDependencies = map.value(QStringLiteral("mergeDependencies"), true).toBool();
result.globalVariables =
variablesFromQVariantMap(map.value(QStringLiteral("globalVariables")).toMap());
diff --git a/src/lib/pkgconfig/pcpackage.cpp b/src/lib/pkgconfig/pcpackage.cpp
index 984936bd1..9aaefcfde 100644
--- a/src/lib/pkgconfig/pcpackage.cpp
+++ b/src/lib/pkgconfig/pcpackage.cpp
@@ -43,6 +43,8 @@
namespace qbs {
+using Internal::startsWith;
+
using ComparisonType = PcPackage::RequiredVersion::ComparisonType;
std::string_view PcPackage::Flag::typeToString(Type t)
@@ -123,6 +125,26 @@ std::optional<ComparisonType> PcPackage::RequiredVersion::comparisonFromString(s
return std::nullopt;
}
+// see https://github.com/pkgconf/pkgconf/blob/pkgconf-2.1.0/libpkgconf/tuple.c#L194
+bool PcPackage::shouldRewriteSysroot(std::string_view sysroot, std::string_view value)
+{
+ if (sysroot.empty() || value.empty())
+ return false;
+
+ if (value.front() != '/')
+ return false;
+
+ if (sysroot == "/")
+ return false;
+
+ if (startsWith(value, sysroot))
+ return false;
+
+ return true;
+}
+
+// TODO: pkg-config only prepends sysroot to flags; while pkgconf does this as a part of the
+// variable substitution and thus this affects all variables, not only flags
PcPackage PcPackage::prependSysroot(std::string_view sysroot) &&
{
PcPackage package(std::move(*this));
@@ -132,6 +154,8 @@ PcPackage PcPackage::prependSysroot(std::string_view sysroot) &&
if (sysroot.empty())
return flags;
for (auto &flag : flags) {
+ if (!shouldRewriteSysroot(sysroot, flag.value))
+ continue;
if (flag.type == Flag::Type::IncludePath
|| flag.type == Flag::Type::SystemIncludePath
|| flag.type == Flag::Type::DirAfterIncludePath
@@ -169,4 +193,51 @@ PcPackage PcPackage::removeSystemLibraryPaths(
return package;
}
+namespace Internal {
+
+std::string_view fileName(std::string_view filePath)
+{
+ const auto pos = filePath.rfind('/');
+ if (pos == std::string_view::npos) {
+#if defined(WIN32)
+ if (filePath.size() >= 2 && filePath[1] == ':')
+ return filePath.substr(2);
+#endif
+ return filePath;
+ }
+ return filePath.substr(pos + 1);
+}
+
+std::string_view completeBaseName(std::string_view filePath)
+{
+ filePath = fileName(filePath);
+ const auto pos = filePath.rfind('.');
+ return pos == std::string_view::npos
+ ? filePath
+ : filePath.substr(0, pos);
+}
+
+std::string_view parentPath(std::string_view path)
+{
+ if (path.empty())
+ return {};
+ auto pos = path.rfind('/');
+ if (pos == std::string_view::npos) {
+#if defined(WIN32)
+ if (path.size() >= 2 && path[1] == ':')
+ return path.substr(0, 2);
+#endif
+ return ".";
+ }
+ if (pos == 0)
+ return "/";
+#if defined(WIN32)
+ if (pos == 2 && path[1] == ':')
+ return path.substr(0, pos + 1);
+#endif
+ return path.substr(0, pos);
+};
+
+} // namespace Internal
+
} // namespace qbs
diff --git a/src/lib/pkgconfig/pcpackage.h b/src/lib/pkgconfig/pcpackage.h
index 9d86816aa..794e2fc40 100644
--- a/src/lib/pkgconfig/pcpackage.h
+++ b/src/lib/pkgconfig/pcpackage.h
@@ -112,12 +112,14 @@ public:
std::vector<RequiredVersion> requiresPublic;
std::vector<RequiredVersion> requiresPrivate;
std::vector<RequiredVersion> conflicts;
+ std::optional<std::string> oldPrefix;
using VariablesMap = std::map<std::string, std::string, std::less<>>;
VariablesMap variables;
bool uninstalled{false};
+ static bool shouldRewriteSysroot(std::string_view sysroot, std::string_view value);
PcPackage prependSysroot(std::string_view sysroot) &&;
PcPackage removeSystemLibraryPaths(const std::unordered_set<std::string> &libraryPaths) &&;
};
@@ -181,6 +183,26 @@ inline bool operator!=(const PcPackage::Flag &lhs, const PcPackage::Flag &rhs)
return !(lhs == rhs);
}
+namespace Internal {
+
+// fast versions (no allocations) of the QFileInfo methods
+std::string_view fileName(std::string_view filePath);
+std::string_view completeBaseName(std::string_view filePath);
+std::string_view parentPath(std::string_view path);
+
+inline bool startsWith(std::string_view haystack, std::string_view needle)
+{
+ return haystack.size() >= needle.size() && haystack.compare(0, needle.size(), needle) == 0;
+}
+
+inline bool endsWith(std::string_view haystack, std::string_view needle)
+{
+ return haystack.size() >= needle.size()
+ && haystack.compare(haystack.size() - needle.size(), needle.size(), needle) == 0;
+}
+
+} // Internal
+
} // namespace qbs
namespace std {
diff --git a/src/lib/pkgconfig/pcparser.cpp b/src/lib/pkgconfig/pcparser.cpp
index 388363af6..4469ca193 100644
--- a/src/lib/pkgconfig/pcparser.cpp
+++ b/src/lib/pkgconfig/pcparser.cpp
@@ -62,6 +62,11 @@ namespace std {
namespace qbs {
+using Internal::completeBaseName;
+using Internal::parentPath;
+using Internal::startsWith;
+using Internal::endsWith;
+
namespace {
// workaround for a missing ctor before c++20
@@ -195,17 +200,6 @@ std::optional<std::vector<std::string>> splitCommand(std::string_view s)
return result;
}
-bool startsWith(std::string_view haystack, std::string_view needle)
-{
- return haystack.size() >= needle.size() && haystack.compare(0, needle.size(), needle) == 0;
-}
-
-bool endsWith(std::string_view haystack, std::string_view needle)
-{
- return haystack.size() >= needle.size()
- && haystack.compare(haystack.size() - needle.size(), needle.size(), needle) == 0;
-}
-
[[noreturn]] void raizeUnknownComparisonException(const PcPackage &pkg, std::string_view verName, std::string_view comp)
{
std::string message;
@@ -397,17 +391,6 @@ PcPackage::RequiredVersion::ComparisonType comparisonFromString(
raizeUnknownComparisonException(pkg, verName, comp);
}
-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.rfind('.');
- return std::string(pos == std::string_view::npos
- ? std::string_view()
- : fileName.substr(0, pos));
-}
-
} // namespace
PcParser::PcParser(const PkgConfig &pkgConfig)
@@ -429,7 +412,7 @@ try
if (!file.is_open())
throw PcException(std::string("Can't open file ") + path);
- package.baseFileName = baseName(path);
+ package.baseFileName = std::string{completeBaseName(path)};
#if HAS_STD_FILESYSTEM
const auto fsPath = std::filesystem::path(path);
package.filePath = fsPath.generic_string();
@@ -445,7 +428,7 @@ try
parseLine(package, line);
return package;
} catch(const PcException &ex) {
- return PcBrokenPackage{path, baseName(path), ex.what()};
+ return PcBrokenPackage{path, std::string{completeBaseName(path)}, ex.what()};
}
std::string PcParser::trimAndSubstitute(const PcPackage &pkg, std::string_view str) const
@@ -488,6 +471,35 @@ std::string PcParser::trimAndSubstitute(const PcPackage &pkg, std::string_view s
return result;
}
+std::string PcParser::evaluateVariable(
+ PcPackage &pkg, std::string_view tag, std::string_view str) const
+{
+ static constexpr std::string_view prefixVariable = "prefix";
+ if (m_pkgConfig.options().definePrefix) {
+ if (tag == prefixVariable) {
+ std::string_view prefix = pkg.filePath;
+ prefix = parentPath(prefix);
+ if (completeBaseName(prefix) == "pkgconfig") {
+ prefix = parentPath(prefix);
+ prefix = parentPath(prefix);
+ }
+ pkg.oldPrefix = std::string(str);
+ if (!prefix.empty())
+ str = prefix;
+ return std::string(str);
+ } else if (pkg.oldPrefix
+ && str.size() > pkg.oldPrefix->size()
+ && str.substr(0, pkg.oldPrefix->size()) == *pkg.oldPrefix
+ && str[pkg.oldPrefix->size()] == '/') {
+ auto result = pkg.variables["prefix"];
+ result += str.substr(pkg.oldPrefix->size());
+ return trimAndSubstitute(pkg, result);
+ }
+ }
+
+ return trimAndSubstitute(pkg, str);
+}
+
void PcParser::parseStringField(
PcPackage &pkg,
std::string &field,
@@ -765,14 +777,7 @@ void PcParser::parseLine(PcPackage &pkg, std::string_view str)
str.remove_prefix(1); // cut '='
str = trimmed(str);
- // TODO: support guesstimating of the prefix variable (pkg-config's --define-prefix option)
- // from doc: "try to override the value of prefix for each .pc file found with a
- // guesstimated value based on the location of the .pc file"
- // https://gitlab.freedesktop.org/pkg-config/pkg-config/-/blob/pkg-config-0.29.2/parse.c#L998
- // This option is disabled by default, and Qbs doesn't allow to override it yet, so we can
- // ignore this feature for now
-
- const auto value = trimAndSubstitute(pkg, str);
+ const auto value = evaluateVariable(pkg, tag, str);
if (!pkg.variables.insert({std::string(tag), value}).second)
raizeDuplicateVariableException(pkg, tag);
}
diff --git a/src/lib/pkgconfig/pcparser.h b/src/lib/pkgconfig/pcparser.h
index ffdf86aaa..a8a5e5cd4 100644
--- a/src/lib/pkgconfig/pcparser.h
+++ b/src/lib/pkgconfig/pcparser.h
@@ -55,6 +55,7 @@ public:
private:
std::string trimAndSubstitute(const PcPackage &pkg, std::string_view str) const;
+ std::string evaluateVariable(PcPackage &pkg, std::string_view tag, std::string_view str) const;
void parseStringField(
PcPackage &pkg,
std::string &field,
diff --git a/src/lib/pkgconfig/pkgconfig.h b/src/lib/pkgconfig/pkgconfig.h
index c1cc634e3..d50363e4a 100644
--- a/src/lib/pkgconfig/pkgconfig.h
+++ b/src/lib/pkgconfig/pkgconfig.h
@@ -59,6 +59,7 @@ public:
bool disableUninstalled{true}; // PKG_CONFIG_DISABLE_UNINSTALLED
bool staticMode{false};
bool mergeDependencies{true};
+ bool definePrefix{false};
VariablesMap globalVariables;
VariablesMap systemVariables;
};
diff --git a/tests/auto/pkgconfig/testdata/lib/pkgconfig/prefix.pc b/tests/auto/pkgconfig/testdata/lib/pkgconfig/prefix.pc
new file mode 100644
index 000000000..64b980803
--- /dev/null
+++ b/tests/auto/pkgconfig/testdata/lib/pkgconfig/prefix.pc
@@ -0,0 +1,13 @@
+prefix=/usr
+exec_prefix=${prefix}
+libdir=${exec_prefix}/lib
+includedir=/usr/include
+usrdir=/usrdir
+
+Name: Prefix test
+Description: This tests prefix auto detection
+Version: 1.0.0
+Requires:
+Libs: -lprefix
+Libs.private: -lm
+Cflags: -I${includedir}
diff --git a/tests/auto/pkgconfig/tst_pkgconfig.cpp b/tests/auto/pkgconfig/tst_pkgconfig.cpp
index 220e54e7a..ac94e1c48 100644
--- a/tests/auto/pkgconfig/tst_pkgconfig.cpp
+++ b/tests/auto/pkgconfig/tst_pkgconfig.cpp
@@ -33,6 +33,7 @@
#include <tools/fileinfo.h>
#include <tools/hostosinfo.h>
#include <pkgconfig.h>
+#include <pcparser.h>
#include <jsextensions/pkgconfigjs.h>
#include <QJsonArray>
@@ -58,6 +59,48 @@ void TestPkgConfig::initTestCase()
qPrintable(errorMessage));
}
+void TestPkgConfig::fileName()
+{
+ QCOMPARE(qbs::Internal::fileName(""), "");
+ QCOMPARE(qbs::Internal::fileName("file.txt"), "file.txt");
+ QCOMPARE(qbs::Internal::fileName("/home/user/file.txt"), "file.txt");
+ QCOMPARE(qbs::Internal::fileName("/"), "");
+#if defined(Q_OS_WIN)
+ QCOMPARE(qbs::Internal::fileName("c:file.txt"), "file.txt");
+ QCOMPARE(qbs::Internal::fileName("c:"), "");
+#endif
+}
+
+void TestPkgConfig::completeBaseName()
+{
+ QCOMPARE(qbs::Internal::completeBaseName(""), "");
+ QCOMPARE(qbs::Internal::completeBaseName("file.txt"), "file");
+ QCOMPARE(qbs::Internal::completeBaseName("archive.tar.gz"), "archive.tar");
+ QCOMPARE(qbs::Internal::completeBaseName("/home/user/file.txt"), "file");
+#if defined(Q_OS_WIN)
+ QCOMPARE(qbs::Internal::completeBaseName("c:file.txt"), "file");
+ QCOMPARE(qbs::Internal::completeBaseName("c:archive.tar.gz"), "archive.tar");
+ QCOMPARE(qbs::Internal::completeBaseName("c:"), "");
+#endif
+}
+
+void TestPkgConfig::parentPath()
+{
+ QCOMPARE(qbs::Internal::parentPath(""), "");
+ QCOMPARE(qbs::Internal::parentPath("file.txt"), ".");
+ QCOMPARE(qbs::Internal::parentPath("/home/user/file.txt"), "/home/user");
+ QCOMPARE(qbs::Internal::parentPath("/home/user/"), "/home/user");
+ QCOMPARE(qbs::Internal::parentPath("/home"), "/");
+ QCOMPARE(qbs::Internal::parentPath("/"), "/");
+#if defined(Q_OS_WIN)
+ QCOMPARE(qbs::Internal::parentPath("c:/folder/file.txt"), "c:/folder");
+ QCOMPARE(qbs::Internal::parentPath("c:/folder/"), "c:/folder");
+ QCOMPARE(qbs::Internal::parentPath("c:/folder"), "c:/");
+ QCOMPARE(qbs::Internal::parentPath("c:/"), "c:/");
+ QCOMPARE(qbs::Internal::parentPath("c:"), "c:");
+#endif
+}
+
void TestPkgConfig::pkgConfig()
{
QFETCH(QString, pcFileName);
@@ -214,4 +257,25 @@ void TestPkgConfig::benchSystem()
}
}
+void TestPkgConfig::prefix()
+{
+ const auto prefixDir = m_workingDataDir;
+ const auto libDir = m_workingDataDir + "/lib";
+ const auto includeDir = m_workingDataDir + "/include";
+ const auto pkgconfigDir = libDir + "/pkgconfig";
+ Options options = qbs::Internal::PkgConfigJs::convertOptions(
+ QProcessEnvironment::systemEnvironment(), {});
+ options.definePrefix = true;
+ options.libDirs.push_back(pkgconfigDir.toStdString());
+ PkgConfig pkgConfig(std::move(options));
+ const auto &packageOr = pkgConfig.getPackage("prefix");
+ QVERIFY(packageOr.isValid());
+ const auto &package = packageOr.asPackage();
+ QCOMPARE(package.variables.at("prefix"), prefixDir.toStdString());
+ QCOMPARE(package.variables.at("exec_prefix"), prefixDir.toStdString());
+ QCOMPARE(package.variables.at("libdir"), libDir.toStdString());
+ QCOMPARE(package.variables.at("includedir"), includeDir.toStdString());
+ QCOMPARE(package.variables.at("usrdir"), "/usrdir");
+}
+
QTEST_MAIN(TestPkgConfig)
diff --git a/tests/auto/pkgconfig/tst_pkgconfig.h b/tests/auto/pkgconfig/tst_pkgconfig.h
index 687411862..47654e1ec 100644
--- a/tests/auto/pkgconfig/tst_pkgconfig.h
+++ b/tests/auto/pkgconfig/tst_pkgconfig.h
@@ -41,9 +41,13 @@ public:
private slots:
void initTestCase();
+ void fileName();
+ void completeBaseName();
+ void parentPath();
void pkgConfig();
void pkgConfig_data();
void benchSystem();
+ void prefix();
private:
const QString m_sourceDataDir;