aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChristian Kandeler <christian.kandeler@qt.io>2018-06-08 14:55:29 +0200
committerChristian Kandeler <christian.kandeler@qt.io>2018-06-08 15:13:47 +0200
commit073fda0ab536b5610ff1b9191db582791552509e (patch)
tree16f0bbacc116e93778e9490ba2c2d9efff738d35
parent9349866b37118db9179d1f0689e872ca1260f040 (diff)
parent5f71b2220f9ff6838799c407972309bff1e8fc96 (diff)
Merge 1.12 into master
-rw-r--r--changelogs/changes-1.12.0.md2
-rw-r--r--doc/man/qbs.16
-rw-r--r--doc/reference/items/language/export.qdoc2
-rw-r--r--doc/reference/modules/exporter-pkgconfig-module.qdoc210
-rw-r--r--qbs-resources/imports/QbsLibrary.qbs9
-rw-r--r--qbs-resources/modules/qbsbuildconfig/qbsbuildconfig.qbs3
-rw-r--r--share/qbs/modules/Exporter/pkgconfig/pkgconfig.js228
-rw-r--r--share/qbs/modules/Exporter/pkgconfig/pkgconfig.qbs91
-rw-r--r--share/qbs/modules/cpp/GenericGCC.qbs2
-rw-r--r--src/app/qbs-setup-qt/setupqt.cpp9
-rw-r--r--src/lib/corelib/buildgraph/executor.cpp21
-rw-r--r--src/lib/corelib/corelib.qbs4
-rw-r--r--src/lib/corelib/language/moduleloader.cpp5
-rw-r--r--src/lib/corelib/language/projectresolver.cpp16
-rw-r--r--src/lib/corelib/language/scriptengine.cpp7
-rw-r--r--src/lib/qtprofilesetup/templates/qmlcache.qbs26
-rw-r--r--src/lib/scriptengine/include/QtScript/qtscriptglobal.h46
-rw-r--r--src/lib/scriptengine/scriptengine.pro1
-rw-r--r--src/lib/scriptengine/scriptengine.qbs11
-rw-r--r--src/lib/scriptengine/use_scriptengine.pri1
-rw-r--r--tests/auto/blackbox/testdata-qt/cached-qml/cached-qml.qbs10
-rw-r--r--tests/auto/blackbox/testdata/changed-rule-inputs/changed-rule-inputs.qbs42
-rw-r--r--tests/auto/blackbox/testdata/exports-pkgconfig/TheFirstLib.pc10
-rw-r--r--tests/auto/blackbox/testdata/exports-pkgconfig/TheFirstLib_windows.pc10
-rw-r--r--tests/auto/blackbox/testdata/exports-pkgconfig/TheSecondLib.pc9
-rw-r--r--tests/auto/blackbox/testdata/exports-pkgconfig/boringstaticlib.cpp1
-rw-r--r--tests/auto/blackbox/testdata/exports-pkgconfig/exports-pkgconfig.qbs124
-rw-r--r--tests/auto/blackbox/testdata/exports-pkgconfig/firstlib.cpp3
-rw-r--r--tests/auto/blackbox/testdata/exports-pkgconfig/firstlib.h9
-rw-r--r--tests/auto/blackbox/testdata/exports-pkgconfig/modules/helper1/helper1.qbs7
-rw-r--r--tests/auto/blackbox/testdata/exports-pkgconfig/modules/helper2/helper2.qbs6
-rw-r--r--tests/auto/blackbox/testdata/exports-pkgconfig/modules/helper3/helper3.qbs6
-rw-r--r--tests/auto/blackbox/testdata/exports-pkgconfig/secondlib.cpp7
-rw-r--r--tests/auto/blackbox/testdata/exports-pkgconfig/secondlib.h9
-rw-r--r--tests/auto/blackbox/testdata/import-assignment/import-assignment.qbs23
-rw-r--r--tests/auto/blackbox/testdata/import-assignment/imports/MyImport/myimport.js2
-rw-r--r--tests/auto/blackbox/testdata/probes-and-shadow-products/probes-and-shadow-products.qbs13
-rw-r--r--tests/auto/blackbox/testdata/smart-relinking/smart-relinking.qbs8
-rw-r--r--tests/auto/blackbox/testdata/smart-relinking/staticlib.cpp1
-rw-r--r--tests/auto/blackbox/tst_blackbox.cpp66
-rw-r--r--tests/auto/blackbox/tst_blackbox.h4
-rw-r--r--tests/auto/blackbox/tst_blackboxqt.cpp6
42 files changed, 1048 insertions, 28 deletions
diff --git a/changelogs/changes-1.12.0.md b/changelogs/changes-1.12.0.md
index f84a64470..6c88d35b7 100644
--- a/changelogs/changes-1.12.0.md
+++ b/changelogs/changes-1.12.0.md
@@ -1,5 +1,7 @@
# General
* Added new module `Exporter.qbs` for creating qbs modules from products.
+* Added new module `Exporter.pkgconfig` for creating pkg-config metadata files.
+* Introduced the concept of system-level qbs settings.
* Added a Makefile generator.
* All command descriptions now contain the product name.
diff --git a/doc/man/qbs.1 b/doc/man/qbs.1
index cf5d5d767..32c112886 100644
--- a/doc/man/qbs.1
+++ b/doc/man/qbs.1
@@ -1,12 +1,12 @@
-.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.47.5.
-.TH QBS "1" "October 2017" "qbs 1.11.0" "User Commands"
+.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.47.6.
+.TH QBS "1" "June 2018" "qbs 1.12.0" "User Commands"
.SH NAME
qbs \- the Qbs build tool
.SH SYNOPSIS
.B qbs
[\fI\,command\/\fR] [\fI\,command parameters\/\fR]
.SH DESCRIPTION
-Qbs 1.11.0, a cross\-platform build tool.
+Qbs 1.12.0, a cross\-platform build tool.
.SS "Built-in commands:"
.TP
build
diff --git a/doc/reference/items/language/export.qdoc b/doc/reference/items/language/export.qdoc
index 3c89117e9..d8ed4c33d 100644
--- a/doc/reference/items/language/export.qdoc
+++ b/doc/reference/items/language/export.qdoc
@@ -91,5 +91,7 @@
\endcode
\defaultvalue \c undefined
+ \see Exporter.qbs
+ \see Exporter.pkgconfig
\since 1.12
*/
diff --git a/doc/reference/modules/exporter-pkgconfig-module.qdoc b/doc/reference/modules/exporter-pkgconfig-module.qdoc
new file mode 100644
index 000000000..9489b4622
--- /dev/null
+++ b/doc/reference/modules/exporter-pkgconfig-module.qdoc
@@ -0,0 +1,210 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** 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$
+**
+****************************************************************************/
+
+/*!
+ \contentspage index.html
+ \qmltype Exporter.pkgconfig
+ \inqmlmodule QbsModules
+ \since Qbs 1.12
+
+ \brief Provides support for generating pkg-config files.
+
+ The \c Exporter.pkgconfig module contains the properties and rules to create a
+ \l{https://www.freedesktop.org/wiki/Software/pkg-config}{pkg-config}
+ metadata (\c{.pc}) file for a \l Product.
+
+ By default, \QBS will attempt to derive some of the \c {.pc} file's contents from
+ the product's \l Export item. This behavior can be suppressed by setting the
+ \l autoDetect property to \c false.
+
+ \section2 Relevant File Tags
+ \target filetags-exporter-pkgconfig
+
+ \table
+ \header
+ \li Tag
+ \li Since
+ \li Description
+ \row
+ \li \c{"Exporter.pkgconfig.pc"}
+ \li 1.12.0
+ \li This tag is attached to the generated \c{.pc} file.
+ \endtable
+*/
+
+/*!
+ \qmlproperty bool Exporter.pkgconfig::autoDetect
+
+ If this property is enabled, then \QBS will try to derive various \c {.pc} file
+ entries from the contents of the product's \l Export item, including the dependencies
+ declared therein. Values for these fields can still be explicitly provided via the
+ respective properties, in which case they will be concatenated with the auto-detected ones.
+ If an exported dependency is known to correspond to a pkg-config module (either by
+ pulling in the \c{Exporter.pkgconfig} module or by appearing in the \l requiresEntry
+ property), it will end up in the \c Requires field of the \c{.pc} file, otherwise
+ its exported \l cpp properties will be collected for use in the \c Cflags and \c Libs fields.
+ The \l excludedDependencies property can be used to ignore specific products altogether.
+
+ \defaultvalue \c true
+*/
+
+/*!
+ \qmlproperty stringList Exporter.pkgconfig::cflagsEntry
+
+ The value of the \c Cflags field in the \c {.pc} file.
+
+ If \l autoDetect is enabled, then this value will be appended to the flags derived from
+ the product's \l Export item and the dependencies declared therein.
+
+ \defaultvalue \c {[]}
+*/
+
+/*!
+ \qmlproperty stringList Exporter.pkgconfig::conflictsEntry
+
+ The value of the \c Conflicts field in the \c {.pc} file.
+
+ \defaultvalue \c {[]}
+*/
+
+/*!
+ \qmlproperty stringList Exporter.pkgconfig::excludedDependencies
+
+ If \l autoDetect is enabled, the entries of this property will be matched against
+ the product's exported dependencies. In case such a dependency's name is present in the array,
+ \QBS will not traverse that dependency to collect entries for the
+ \c Cflags, \c Libs, \c Requires and \c {Requires.private} fields.
+
+ This list must not contain any values that are present in \l requiresEntry.
+
+ \defaultvalue \c undefined
+*/
+
+/*!
+ \qmlproperty string Exporter.pkgconfig::fileName
+
+ The file name of the generated pkg-config metadata file.
+
+ \defaultvalue \c {product.targetName + ".pc"}
+*/
+
+/*!
+ \qmlproperty stringList Exporter.pkgconfig::libsEntry
+
+ The value of the \c Libs field in the \c {.pc} file.
+
+ If \l autoDetect is enabled, then this value will be appended to the flags derived from
+ the product's \l Export item and the dependencies declared therein.
+
+ \defaultvalue \c {[]}
+*/
+
+/*!
+ \qmlproperty stringList Exporter.pkgconfig::libsPrivateEntry
+
+ The value of the \c Libs.Private field in the \c {.pc} file.
+
+ \defaultvalue \c {[]}
+*/
+
+/*!
+ \qmlproperty string Exporter.pkgconfig::nameEntry
+
+ The value of the \c Name field in the \c {.pc} file.
+
+ \defaultvalue \c {product.name}
+*/
+
+
+/*!
+ \qmlproperty stringList Exporter.pkgconfig::requiresEntry
+
+ The value of the \c Requires field in the \c {.pc} file.
+
+ If \l autoDetect is enabled, then those of the product's exported dependencies
+ that pull in the \c {Exporter.pkgconfig} module will also end up in the
+ \c Requires field, provided they are not listed in \l excludedDependencies.
+
+ If an exported dependency matches an entry of this array, \QBS will not traverse that
+ dependency to gather \l cpp properties for use in the \l cflagsEntry and \l libsEntry values,
+ as pkg-config takes care of that itself.
+
+ \defaultvalue \c {[]}
+*/
+
+/*!
+ \qmlproperty stringList Exporter.pkgconfig::requiresPrivateEntry
+
+ The value of the \c Requires.private field in the \c {.pc} file.
+
+ If \l autoDetect is enabled, then those of the product's non-exported dependencies
+ that pull in the \c {Exporter.pkgconfig} module will also end up in the
+ \c Requires.private field, provided they are not listed in \l excludedDependencies.
+
+ \defaultvalue \c {[]}
+*/
+
+/*!
+ \qmlproperty var Exporter.pkgconfig::transformFunction
+
+ A function with the signature
+ \c {function(product, moduleName, propertyName, value)}.
+ This can be useful to "fine-tune" property values if \l autoDetect is enabled, in case
+ they need amending for the purpose of pkg-config.
+ The \c product parameter represents the exporting product, the remaining parameters describe
+ the module property. The modified value of the module property shall be returned.
+
+ \defaultvalue \c undefined
+*/
+
+/*!
+ \qmlproperty string Exporter.pkgconfig::urlEntry
+
+ The value of the \c URL field in the \c {.pc} file.
+
+ \defaultvalue \c undefined
+*/
+
+/*!
+ \qmlproperty string Exporter.pkgconfig::versionEntry
+
+ The value of the \c Version field in the \c {.pc} file.
+
+ \defaultvalue \c {product.version}
+*/
+
+/*!
+ \qmlproperty string Exporter.pkgconfig::customVariables
+
+ Use this property to add arbitrary variable assignments into the \c .pc file.
+ The property is a map that will produce one assignment per entry.
+ The keys and values of the map represent the left-hand sides and right-hand
+ sides of these assignments, respectively. The values are strings that will
+ be written into the file verbatim.
+
+ \nodefaultvalue
+*/
diff --git a/qbs-resources/imports/QbsLibrary.qbs b/qbs-resources/imports/QbsLibrary.qbs
index f8bc70580..69e107871 100644
--- a/qbs-resources/imports/QbsLibrary.qbs
+++ b/qbs-resources/imports/QbsLibrary.qbs
@@ -19,10 +19,14 @@ QbsProduct {
property string visibilityType: staticBuild ? "static" : "dynamic"
property string headerInstallPrefix: "/include/qbs"
property bool hasExporter: Utilities.versionCompare(qbs.version, "1.12") >= 0
+ property bool generatePkgConfigFile: qbsbuildconfig.generatePkgConfigFiles && hasExporter
property bool generateQbsModule: install && qbsbuildconfig.generateQbsModules && hasExporter
property bool staticBuild: Qt.core.staticBuild || qbsbuildconfig.staticBuild
property stringList libType: [staticBuild ? "staticlibrary" : "dynamiclibrary"]
+
+ Depends { name: "Exporter.pkgconfig"; condition: generatePkgConfigFile }
Depends { name: "Exporter.qbs"; condition: generateQbsModule }
+
Group {
fileTagsFilter: libType.concat("dynamiclibrary_symlink")
.concat(qbs.buildVariant === "debug" ? ["debuginfo_dll"] : [])
@@ -37,6 +41,11 @@ QbsProduct {
qbs.installDir: qbsbuildconfig.importLibInstallDir
}
Group {
+ fileTagsFilter: "Exporter.pkgconfig.pc"
+ qbs.install: install
+ qbs.installDir: qbsbuildconfig.pkgConfigInstallDir
+ }
+ Group {
fileTagsFilter: "Exporter.qbs.module"
qbs.install: install
qbs.installDir: FileInfo.joinPaths(qbsbuildconfig.qbsModulesBaseDir, product.name)
diff --git a/qbs-resources/modules/qbsbuildconfig/qbsbuildconfig.qbs b/qbs-resources/modules/qbsbuildconfig/qbsbuildconfig.qbs
index 50a5ea79e..0627a5cf9 100644
--- a/qbs-resources/modules/qbsbuildconfig/qbsbuildconfig.qbs
+++ b/qbs-resources/modules/qbsbuildconfig/qbsbuildconfig.qbs
@@ -19,8 +19,11 @@ Module {
property bool installManPage: qbs.targetOS.contains("unix")
property bool installHtml: true
property bool installQch: false
+ property bool generatePkgConfigFiles: installApiHeaders && qbs.targetOS.contains("unix")
+ && !qbs.targetOS.contains("darwin")
property bool generateQbsModules: installApiHeaders
property string docInstallDir: "share/doc/qbs/html"
+ property string pkgConfigInstallDir: FileInfo.joinPaths(libDirName, "pkgconfig")
property string qbsModulesBaseDir: FileInfo.joinPaths(libDirName, "qbs", "modules")
property string relativeLibexecPath: "../" + libexecInstallDir
property string relativePluginsPath: "../" + libDirName
diff --git a/share/qbs/modules/Exporter/pkgconfig/pkgconfig.js b/share/qbs/modules/Exporter/pkgconfig/pkgconfig.js
new file mode 100644
index 000000000..a3109d61d
--- /dev/null
+++ b/share/qbs/modules/Exporter/pkgconfig/pkgconfig.js
@@ -0,0 +1,228 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: http://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 http://www.qt.io/terms-conditions. For further information
+** use the contact form at http://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 2.1 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, The Qt Company gives you certain additional
+** rights. These rights are described in The Qt Company LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+var FileInfo = require("qbs.FileInfo");
+var ModUtils = require("qbs.ModUtils");
+
+function quote(value)
+{
+ if (value.contains(" ") || value.contains("'") || value.contains('"')) {
+ return '"' + value.replace(/(["'\\])/g, "\\$1") + '"';
+ }
+ return value;
+}
+
+function writeEntry(product, file, key, propertyName, required, additionalValues)
+{
+ var value = product.Exporter.pkgconfig[propertyName];
+ if (additionalValues && additionalValues.length > 0)
+ value = (value || []).concat(additionalValues);
+ var valueIsNotEmpty = value && (!Array.isArray(value) || value.length > 0);
+ if (valueIsNotEmpty) {
+ if (Array.isArray(value))
+ value = value.join(' ');
+ file.writeLine(key + ": " + value);
+ } else if (required) {
+ throw "Failure creating " + FileInfo.fileName(file.filePath()) + ": The entry '" + key
+ + "' is required, but property Exporter.pkgconfig."
+ + propertyName + " is not set.";
+ }
+}
+
+function collectAutodetectedData(topLevelProduct)
+{
+ var data = {
+ libs: [],
+ cflags: [],
+ requires: [],
+ requiresPrivate: []
+ };
+ if (!topLevelProduct.Exporter.pkgconfig.autoDetect)
+ return data;
+
+ var excludedDeps = topLevelProduct.Exporter.pkgconfig.excludedDependencies || [];
+ var explicitRequires = topLevelProduct.Exporter.pkgconfig.requiresEntry || [];
+ var explicitRequiresPrivate = topLevelProduct.Exporter.pkgconfig.requiresPrivateEntry || [];
+
+ var transformFunc = topLevelProduct.Exporter.pkgconfig.transformFunction;
+
+ // Make use of the "prefix" convenience variable if applicable.
+ function quoteAndPrefixify(value)
+ {
+ var quotedValue = quote(value);
+ var installPrefix = topLevelProduct.qbs.installPrefix || "";
+ if (!topLevelProduct.Exporter.pkgconfig._usePrefix || typeof value !== "string"
+ || !value.startsWith(installPrefix)
+ || (value.length > installPrefix.length && value[installPrefix.length] !== '/')) {
+ return quotedValue;
+ }
+ return quotedValue.replace(product.qbs.installPrefix, "${prefix}");
+ }
+
+ function transformedValue(product, moduleName, propertyName)
+ {
+ var originalValue = product.exports[moduleName][propertyName];
+ var value = transformFunc
+ ? eval("(" + transformFunc + ")(product, moduleName, propertyName, originalValue)")
+ : originalValue;
+ if (Array.isArray(value))
+ value.forEach(function(v, i, a) { a[i] = quoteAndPrefixify(v); });
+ else if (value)
+ value = quoteAndPrefixify(value);
+ return value;
+ }
+
+ function collectLibs(productOrModule)
+ {
+ var libs = [];
+ var libArtifacts;
+ var isProduct = !productOrModule.present;
+ var considerDynamicLibs = !isProduct || (productOrModule.type
+ && productOrModule.type.contains("dynamiclibrary"));
+ if (considerDynamicLibs) {
+ libArtifacts = productOrModule.artifacts.dynamiclibrary;
+ } else {
+ var considerStaticLibs = !isProduct || (productOrModule.type
+ && productOrModule.type.contains("staticlibrary"));
+ if (considerStaticLibs)
+ libArtifacts = productOrModule.artifacts.staticlibrary;
+ }
+ for (var i = 0; i < (libArtifacts || []).length; ++i) {
+ var libArtifact = libArtifacts[i];
+ if (libArtifact.qbs.install) {
+ var installDir = FileInfo.path(ModUtils.artifactInstalledFilePath(libArtifact));
+ installDir = installDir.slice(libArtifact.qbs.installRoot.length);
+ libs.push("-L" + quoteAndPrefixify(FileInfo.cleanPath(installDir)),
+ "-l" + quote(productOrModule.targetName));
+ }
+ }
+ if (!productOrModule.exports.cpp)
+ return libs;
+ var libPaths = transformedValue(productOrModule, "cpp", "libraryPaths");
+ if (libPaths)
+ libs.push.apply(libs, libPaths.map(function(p) { return "-L" + p; }));
+ function libNamesToLibEntries(libNames) {
+ return libNames.map(function(libName) { return "-l" + libName; });
+ };
+ var dlls = transformedValue(productOrModule, "cpp", "dynamicLibraries");
+ if (dlls)
+ libs.push.apply(libs, libNamesToLibEntries(dlls));
+ var staticLibs = transformedValue(productOrModule, "cpp", "staticLibraries");
+ if (staticLibs)
+ libs.push.apply(libs, libNamesToLibEntries(staticLibs));
+ var lFlags = transformedValue(productOrModule, "cpp", "linkerFlags");
+ if (lFlags)
+ libs.push.apply(libs, lFlags);
+ lFlags = transformedValue(productOrModule, "cpp", "driverFlags");
+ if (lFlags)
+ libs.push.apply(libs, lFlags);
+ lFlags = transformedValue(productOrModule, "cpp", "driverLinkerFlags");
+ if (lFlags)
+ libs.push.apply(libs, lFlags);
+ return libs;
+ }
+
+ function collectCFlags(productOrModule)
+ {
+ if (!productOrModule.exports.cpp)
+ return [];
+ var flags = [];
+ var defs = transformedValue(productOrModule, "cpp", "defines");
+ if (defs)
+ flags.push.apply(flags, defs.map(function(d) { return "-D" + d; }));
+ var incPaths = transformedValue(productOrModule, "cpp", "includePaths");
+ if (incPaths)
+ flags.push.apply(flags, incPaths.map(function(p) { return "-I" + p; }));
+ var cflags = transformedValue(productOrModule, "cpp", "commonCompilerFlags");
+ if (cflags)
+ flags.push.apply(flags, cflags);
+ cflags = transformedValue(productOrModule, "cpp", "driverFlags");
+ if (cflags)
+ flags.push.apply(flags, cflags);
+ cflags = transformedValue(productOrModule, "cpp", "cxxFlags")
+ || transformedValue(productOrModule, "cpp", "cFlags");
+ if (cflags)
+ flags.push.apply(flags, cflags);
+ return flags;
+ }
+
+ function collectAutodetectedDataRecursive(productOrModule, privateContext)
+ {
+ if (!privateContext) {
+ data.libs.push.apply(data.libs, collectLibs(productOrModule));
+ data.cflags.push.apply(data.cflags, collectCFlags(productOrModule));
+ }
+ var exportedDeps = productOrModule.exports ? productOrModule.exports.dependencies : [];
+ var exportedDepNames = [];
+ var privateDeps = [];
+ for (var i = 0; i < exportedDeps.length; ++i)
+ exportedDepNames.push(exportedDeps[i].name);
+ for (i = 0; i < (productOrModule.dependencies || []).length; ++i) {
+ var dep = productOrModule.dependencies[i];
+ if (exportedDepNames.contains(dep.name))
+ continue;
+ privateDeps.push(dep);
+ }
+
+ function gatherData(dep) {
+ if (dep.name === "Exporter.pkgconfig")
+ return;
+ var depHasPkgConfig = dep.Exporter && dep.Exporter.pkgconfig;
+ if (depHasPkgConfig) {
+ var entry = FileInfo.completeBaseName(dep.Exporter.pkgconfig.fileName);
+ if (excludedDeps.contains(entry))
+ return;
+ if (isPrivateDep && !data.requiresPrivate.contains(entry)
+ && !explicitRequiresPrivate.contains(entry)) {
+ data.requiresPrivate.push(entry);
+ }
+ if (!isPrivateDep && !data.requires.contains(entry)
+ && !explicitRequires.contains(entry)) {
+ data.requires.push(entry);
+ }
+ } else {
+ if (excludedDeps.contains(dep.name))
+ return;
+ if (isPrivateDep && explicitRequiresPrivate.contains(dep.name))
+ return;
+ if (!isPrivateDep && explicitRequires.contains(dep.name))
+ return;
+ collectAutodetectedDataRecursive(dep, isPrivateDep);
+ }
+ }
+ var isPrivateDep = privateContext;
+ exportedDeps.forEach(gatherData);
+ isPrivateDep = true;
+ privateDeps.forEach(gatherData);
+ }
+
+ collectAutodetectedDataRecursive(topLevelProduct, false);
+ return data;
+}
diff --git a/share/qbs/modules/Exporter/pkgconfig/pkgconfig.qbs b/share/qbs/modules/Exporter/pkgconfig/pkgconfig.qbs
new file mode 100644
index 000000000..5fbda2bf4
--- /dev/null
+++ b/share/qbs/modules/Exporter/pkgconfig/pkgconfig.qbs
@@ -0,0 +1,91 @@
+import qbs
+import qbs.FileInfo
+import qbs.TextFile
+
+import "pkgconfig.js" as HelperFunctions
+
+Module {
+ property string fileName: product.targetName + ".pc"
+ property bool autoDetect: true
+ property var transformFunction // function(product, moduleName, propertyName, valueElement)
+ property stringList excludedDependencies
+
+ property string nameEntry: product.name
+ property string descriptionEntry: product.name
+ property string versionEntry: product.version
+ property string urlEntry
+ property stringList cflagsEntry: []
+ property stringList libsEntry: []
+ property stringList libsPrivateEntry: []
+ property stringList requiresEntry: []
+ property stringList requiresPrivateEntry: []
+ property stringList conflictsEntry: []
+
+ property var customVariables
+
+ property bool _usePrefix: autoDetect && qbs.installPrefix
+
+ additionalProductTypes: ["Exporter.pkgconfig.pc"]
+
+ Rule {
+ multiplex: true
+ requiresInputs: false
+
+ // Make sure all relevant library artifacts have been created by the time we run.
+ inputsFromDependencies: autoDetect
+ ? ["Exporter.pkgconfig.pc", "staticlibrary", "dynamiclibrary"]
+ : []
+ inputs: {
+ if (!product.Exporter.pkgconfig.autoDetect)
+ return undefined;
+ if (product.type.contains("staticlibrary"))
+ return ["staticlibrary"];
+ if (product.type.contains("dynamiclibrary"))
+ return ["dynamiclibrary"];
+ }
+
+ Artifact {
+ filePath: product.Exporter.pkgconfig.fileName
+ fileTags: ["Exporter.pkgconfig.pc"]
+ }
+ prepare: {
+ var cmd = new JavaScriptCommand();
+ cmd.description = "Creating " + output.fileName;
+ cmd.sourceCode = function() {
+ var f = new TextFile(output.filePath, TextFile.WriteOnly);
+ if (product.Exporter.pkgconfig._usePrefix)
+ f.writeLine("prefix=" + product.qbs.installPrefix + "\n");
+ var customVariables = product.Exporter.pkgconfig.customVariables;
+ if (customVariables) {
+ for (var customVar in customVariables)
+ f.writeLine(customVar + "=" + customVariables[customVar]);
+ f.writeLine("");
+ }
+ var autoDetectedData = HelperFunctions.collectAutodetectedData(product);
+ HelperFunctions.writeEntry(product, f, "Name", "nameEntry", true);
+ HelperFunctions.writeEntry(product, f, "Description", "descriptionEntry", true);
+ HelperFunctions.writeEntry(product, f, "Version", "versionEntry", true);
+ HelperFunctions.writeEntry(product, f, "URL", "urlEntry");
+ HelperFunctions.writeEntry(product, f, "Cflags", "cflagsEntry", false,
+ autoDetectedData.cflags);
+ HelperFunctions.writeEntry(product, f, "Libs", "libsEntry", false,
+ autoDetectedData.libs);
+ HelperFunctions.writeEntry(product, f, "Libs.private", "libsPrivateEntry");
+ HelperFunctions.writeEntry(product, f, "Requires", "requiresEntry", false,
+ autoDetectedData.requires);
+ HelperFunctions.writeEntry(product, f, "Requires.private", "requiresPrivateEntry",
+ false, autoDetectedData.requiresPrivate);
+ HelperFunctions.writeEntry(product, f, "Conflicts", "conflictsEntry");
+ };
+ return [cmd];
+ }
+ }
+
+ validate: {
+ if (requiresEntry && excludedDependencies
+ && requiresEntry.containsAny(excludedDependencies)) {
+ throw "The contents of Export.pkgconfig.requiresEntry and "
+ + "Export.pkgconfig.excludedDependencies must not overlap.";
+ }
+ }
+}
diff --git a/share/qbs/modules/cpp/GenericGCC.qbs b/share/qbs/modules/cpp/GenericGCC.qbs
index 5676d62e0..792e8ef4c 100644
--- a/share/qbs/modules/cpp/GenericGCC.qbs
+++ b/share/qbs/modules/cpp/GenericGCC.qbs
@@ -456,7 +456,7 @@ CppModule {
condition: product.cpp.shouldLink
multiplex: true
inputs: ["obj", "linkerscript"]
- inputsFromDependencies: ["dynamiclibrary", "staticlibrary"]
+ inputsFromDependencies: ["dynamiclibrary_symbols", "staticlibrary"]
outputFileTags: ["bundle.input", "staticlibrary", "c_staticlibrary", "cpp_staticlibrary"]
outputArtifacts: {
diff --git a/src/app/qbs-setup-qt/setupqt.cpp b/src/app/qbs-setup-qt/setupqt.cpp
index a611a8c07..097c9381a 100644
--- a/src/app/qbs-setup-qt/setupqt.cpp
+++ b/src/app/qbs-setup-qt/setupqt.cpp
@@ -392,7 +392,14 @@ static Match compatibility(const EnhancedQtEnvironment &env, const Profile &tool
// because it's especially important for this toolchain
const Version compilerVersion = Version::fromString(
toolchainProfile.value(QLatin1String("cpp.compilerVersion")).toString());
- if (env.msvcVersion.majorVersion() != compilerVersion.majorVersion()
+
+ static const Version vs2017Version{19, 10};
+ if (env.msvcVersion >= vs2017Version) {
+ if (env.msvcVersion.majorVersion() != compilerVersion.majorVersion()
+ || compilerVersion < vs2017Version) {
+ return MatchNone;
+ }
+ } else if (env.msvcVersion.majorVersion() != compilerVersion.majorVersion()
|| env.msvcVersion.minorVersion() != compilerVersion.minorVersion()) {
return MatchNone;
}
diff --git a/src/lib/corelib/buildgraph/executor.cpp b/src/lib/corelib/buildgraph/executor.cpp
index 24f484ad2..8713b9b41 100644
--- a/src/lib/corelib/buildgraph/executor.cpp
+++ b/src/lib/corelib/buildgraph/executor.cpp
@@ -771,8 +771,7 @@ void Executor::rescueOldBuildData(Artifact *artifact, bool *childrenAdded = 0)
return;
qCDebug(lcBuildGraph) << "Attempting to rescue data of artifact" << artifact->fileName();
- typedef std::pair<Artifact *, bool> ChildArtifactData;
- QList<ChildArtifactData> childrenToConnect;
+ std::vector<Artifact *> childrenToConnect;
bool canRescue = artifact->transformer->commands == rad.commands;
if (canRescue) {
ResolvedProductPtr pseudoProduct = ResolvedProduct::create();
@@ -796,10 +795,15 @@ void Executor::rescueOldBuildData(Artifact *artifact, bool *childrenAdded = 0)
removeGeneratedArtifactFromDisk(cd.childFilePath, m_logger);
}
}
- // TODO: Shouldn't addedByScanner always be true here? Otherwise the child would be
- // in the list already, no?
+ if (!cd.addedByScanner) {
+ // If an artifact has disappeared from the list of children, the commands
+ // might need to run again.
+ canRescue = false;
+ qCDebug(lcBuildGraph) << "Former child artifact" << cd.childFilePath <<
+ "is no longer in the list of children";
+ }
if (canRescue)
- childrenToConnect.push_back({child, cd.addedByScanner});
+ childrenToConnect.push_back(child);
}
for (const QString &depPath : rad.fileDependencies) {
const QList<FileResourceBase *> depList = m_project->buildData->lookupFiles(depPath);
@@ -865,10 +869,9 @@ void Executor::rescueOldBuildData(Artifact *artifact, bool *childrenAdded = 0)
artifact->knownOutOfDate = artifact->knownOutOfDate || rad.knownOutOfDate;
if (childrenAdded && !childrenToConnect.empty())
*childrenAdded = true;
- for (const ChildArtifactData &cad : qAsConst(childrenToConnect)) {
- safeConnect(artifact, cad.first);
- if (cad.second)
- artifact->childrenAddedByScanner << cad.first;
+ for (Artifact * const child : childrenToConnect) {
+ safeConnect(artifact, child);
+ artifact->childrenAddedByScanner << child;
}
qCDebug(lcBuildGraph) << "Data was rescued.";
} else {
diff --git a/src/lib/corelib/corelib.qbs b/src/lib/corelib/corelib.qbs
index fd25d30e0..08733b624 100644
--- a/src/lib/corelib/corelib.qbs
+++ b/src/lib/corelib/corelib.qbs
@@ -17,7 +17,9 @@ QbsLibrary {
Depends { condition: qbsbuildconfig.enableProjectFileUpdates; name: "Qt.gui" }
Depends { condition: staticBuild; productTypes: ["qbsplugin"] }
name: "qbscore"
- cpp.includePaths: base.concat([
+ property stringList bundledQtScriptIncludes: qbsbuildconfig.useBundledQtScript
+ || !Qt.script.present ? qbsscriptengine.includePaths : []
+ cpp.includePaths: base.concat(bundledQtScriptIncludes).concat([
".",
"../.." // for the plugin headers
])
diff --git a/src/lib/corelib/language/moduleloader.cpp b/src/lib/corelib/language/moduleloader.cpp
index 933c49abc..df47e36ea 100644
--- a/src/lib/corelib/language/moduleloader.cpp
+++ b/src/lib/corelib/language/moduleloader.cpp
@@ -581,6 +581,8 @@ void ModuleLoader::handleTopLevelProject(ModuleLoaderResult *loadResult, Item *p
for (ProductContext * const p : productSorter.sortedProducts()) {
try {
handleProduct(p);
+ if (p->name.startsWith(shadowProductPrefix()))
+ tlp.probes << p->info.probes;
} catch (const ErrorInfo &err) {
handleProductError(err, p);
}
@@ -3418,7 +3420,8 @@ void ModuleLoader::resolveProbe(ProductContext *productContext, Item *parent, It
const bool condition = m_evaluator->boolValue(probe, StringConstants::conditionProperty());
const QString &sourceCode = configureScript->sourceCode().toString();
ProbeConstPtr resolvedProbe;
- if (parent->type() == ItemType::Project) {
+ if (parent->type() == ItemType::Project
+ || productContext->name.startsWith(shadowProductPrefix())) {
resolvedProbe = findOldProjectProbe(probeId, condition, initialProperties, sourceCode);
} else {
const QString &uniqueProductName = productContext->uniqueName();
diff --git a/src/lib/corelib/language/projectresolver.cpp b/src/lib/corelib/language/projectresolver.cpp
index b7535c2eb..ff74f845a 100644
--- a/src/lib/corelib/language/projectresolver.cpp
+++ b/src/lib/corelib/language/projectresolver.cpp
@@ -1682,7 +1682,21 @@ void ProjectResolver::evaluateProperty(const Item *item, const QString &propName
// NOTE: Loses type information if scriptValue.isUndefined == true,
// as such QScriptValues become invalid QVariants.
- QVariant v = scriptValue.isFunction() ? scriptValue.toString() : scriptValue.toVariant();
+ QVariant v;
+ if (scriptValue.isFunction()) {
+ v = scriptValue.toString();
+ } else {
+ v = scriptValue.toVariant();
+ QVariantMap m = v.toMap();
+ if (m.contains(StringConstants::importScopeNamePropertyInternal())) {
+ QVariantMap tmp = m;
+ m = scriptValue.prototype().toVariant().toMap();
+ for (auto it = tmp.begin(); it != tmp.end(); ++it)
+ m.insert(it.key(), it.value());
+ v = m;
+ }
+ }
+
if (pd.type() == PropertyDeclaration::Path && v.isValid()) {
v = v.toString();
} else if (pd.type() == PropertyDeclaration::PathList
diff --git a/src/lib/corelib/language/scriptengine.cpp b/src/lib/corelib/language/scriptengine.cpp
index 568a927b2..e983ae945 100644
--- a/src/lib/corelib/language/scriptengine.cpp
+++ b/src/lib/corelib/language/scriptengine.cpp
@@ -152,6 +152,7 @@ void ScriptEngine::import(const FileContextBaseConstPtr &fileCtx, QScriptValue &
if (m_observeMode == ObserveMode::Enabled) {
for (QScriptValue &sv : m_requireResults)
observeImport(sv);
+ m_requireResults.clear();
}
m_currentDirPathStack.pop();
@@ -507,8 +508,7 @@ QScriptValue ScriptEngine::js_require(QScriptContext *context, QScriptEngine *qt
if (!values.empty()) {
const QScriptValue mergedValue = mergeExtensionObjects(values);
- if (engine->m_observeMode == ObserveMode::Enabled)
- engine->m_requireResults.push_back(mergedValue);
+ engine->m_requireResults.push_back(mergedValue);
engine->m_filePathsPerImport[mergedValue.objectId()] = filePaths;
return mergedValue;
}
@@ -533,8 +533,7 @@ QScriptValue ScriptEngine::js_require(QScriptContext *context, QScriptEngine *qt
const QString scopeName = scopeNamePrefix + QString::number(qHash(filePath), 16);
result.setProperty(StringConstants::importScopeNamePropertyInternal(), scopeName);
context->thisObject().setProperty(scopeName, result);
- if (engine->m_observeMode == ObserveMode::Enabled)
- engine->m_requireResults.push_back(result);
+ engine->m_requireResults.push_back(result);
engine->m_filePathsPerImport[result.objectId()] = { filePath };
} catch (const ErrorInfo &e) {
result = context->throwError(e.toString());
diff --git a/src/lib/qtprofilesetup/templates/qmlcache.qbs b/src/lib/qtprofilesetup/templates/qmlcache.qbs
index 246486c53..9111eb500 100644
--- a/src/lib/qtprofilesetup/templates/qmlcache.qbs
+++ b/src/lib/qtprofilesetup/templates/qmlcache.qbs
@@ -1,14 +1,22 @@
+import qbs.File
import qbs.FileInfo
import qbs.Process
+import qbs.Utilities
Module {
additionalProductTypes: ["qt.qml.qmlc", "qt.qml.jsc"]
- validate: qmlcachegenProbe.found
+ validate: {
+ if (!qmlcachegenProbe.found)
+ throw "qmlcachegen unsupported for this target";
+ }
property string qmlCacheGenPath: FileInfo.joinPaths(Qt.core.binPath, "qmlcachegen")
+ (qbs.hostOS.contains("windows") ? ".exe" : "")
+ property bool supportsAllArchitectures: Utilities.versionCompare(Qt.core.version, "5.11") >= 0
property string installDir
readonly property stringList _targetArgs: {
+ if (supportsAllArchitectures)
+ return [];
function translateArch(arch) {
if (arch === "x86")
return "i386";
@@ -24,15 +32,25 @@ Module {
Depends { name: "Qt.core" }
Probe {
id: qmlcachegenProbe
+
+ property string arch: qbs.architecture
+ property string _qmlCacheGenPath: qmlCacheGenPath
+ property stringList targetArgs: _targetArgs
+ property bool _supportsAllArchitectures: supportsAllArchitectures
+
configure: {
+ if (_supportsAllArchitectures) {
+ found = File.exists(_qmlCacheGenPath);
+ return;
+ }
var process = new Process();
found = false;
try {
- found = process.exec(qmlCacheGenPath,
- _targetArgs.concat("--check-if-supported")) == 0;
+ found = process.exec(_qmlCacheGenPath,
+ targetArgs.concat("--check-if-supported")) == 0;
if (!found) {
var msg = "QML cache generation was requested but is unsupported on "
- + "architecture '" + qbs.architecture + "'.";
+ + "architecture '" + arch + "'.";
console.warn(msg);
}
} finally {
diff --git a/src/lib/scriptengine/include/QtScript/qtscriptglobal.h b/src/lib/scriptengine/include/QtScript/qtscriptglobal.h
new file mode 100644
index 000000000..8b1e09bb1
--- /dev/null
+++ b/src/lib/scriptengine/include/QtScript/qtscriptglobal.h
@@ -0,0 +1,46 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** 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$
+**
+****************************************************************************/
+
+#ifndef QBS_QTSCRIPTGLOBAL_H
+#define QBS_QTSCRIPTGLOBAL_H
+
+#define Q_SCRIPT_EXPORT
+#define Q_SCRIPTTOOLS_EXPORT
+
+#endif // include guard
diff --git a/src/lib/scriptengine/scriptengine.pro b/src/lib/scriptengine/scriptengine.pro
index f5d4ad590..c5ad5f07a 100644
--- a/src/lib/scriptengine/scriptengine.pro
+++ b/src/lib/scriptengine/scriptengine.pro
@@ -61,6 +61,7 @@ DEFINES += JS_NO_EXPORT
}
INCLUDEPATH += \
+ $$PWD/include \
$$OUT_PWD/include \
$$OUT_PWD/include/QtScript/$$[QT_VERSION]/QtScript \
$$PWD/../../shared/qtscript/src/script \
diff --git a/src/lib/scriptengine/scriptengine.qbs b/src/lib/scriptengine/scriptengine.qbs
index c92bc1954..77e6ef7d2 100644
--- a/src/lib/scriptengine/scriptengine.qbs
+++ b/src/lib/scriptengine/scriptengine.qbs
@@ -17,12 +17,18 @@ Project {
type: ["staticlibrary"]
name: "qbsscriptengine"
+ generatePkgConfigFile: false
+ generateQbsModule: false
+
property bool useSystemMalloc: !qbs.targetOS.contains("macos")
&& !qbs.targetOS.contains("unix")
property string qtscriptPath: "../../shared/qtscript/src/"
cpp.includePaths: {
- var result = base.concat(["."]);
+ var result = base.concat(
+ ".",
+ "include"
+ );
var jscBaseDir = qtscriptPath + "3rdparty/javascriptcore";
result.push(jscBaseDir);
@@ -355,7 +361,8 @@ Project {
Export {
Depends { name: "QtScriptFwdHeaders" }
Depends { name: "cpp" }
- cpp.includePaths: QtScriptFwdHeaders.publicIncludePaths
+ property stringList includePaths: [product.sourceDirectory + "/include"]
+ .concat(QtScriptFwdHeaders.publicIncludePaths)
Properties {
condition: qbs.targetOS.contains("unix")
cpp.dynamicLibraries: base.concat(["pthread"])
diff --git a/src/lib/scriptengine/use_scriptengine.pri b/src/lib/scriptengine/use_scriptengine.pri
index 296b6e416..6450e171f 100644
--- a/src/lib/scriptengine/use_scriptengine.pri
+++ b/src/lib/scriptengine/use_scriptengine.pri
@@ -7,4 +7,5 @@
}
INCLUDEPATH += \
+ $$PWD/include \
$$shadowed($$PWD/include)
diff --git a/tests/auto/blackbox/testdata-qt/cached-qml/cached-qml.qbs b/tests/auto/blackbox/testdata-qt/cached-qml/cached-qml.qbs
index db9dcae0c..397c2a691 100644
--- a/tests/auto/blackbox/testdata-qt/cached-qml/cached-qml.qbs
+++ b/tests/auto/blackbox/testdata-qt/cached-qml/cached-qml.qbs
@@ -1,4 +1,5 @@
import qbs
+import qbs.Utilities
CppApplication {
name: "app"
@@ -26,4 +27,13 @@ CppApplication {
qbs.install: true
qbs.installDir: "data"
}
+
+ Probe {
+ id: qtVersionProbe
+ property string qtVersion: Qt.core.version
+ configure: {
+ console.info("qmlcachegen must work: "
+ + (Utilities.versionCompare(qtVersion, "5.11") >= 0))
+ }
+ }
}
diff --git a/tests/auto/blackbox/testdata/changed-rule-inputs/changed-rule-inputs.qbs b/tests/auto/blackbox/testdata/changed-rule-inputs/changed-rule-inputs.qbs
new file mode 100644
index 000000000..8aef7b9b6
--- /dev/null
+++ b/tests/auto/blackbox/testdata/changed-rule-inputs/changed-rule-inputs.qbs
@@ -0,0 +1,42 @@
+import qbs
+
+Project {
+ Product {
+ name: "p1"
+ type: "p1"
+ Rule {
+ alwaysRun: true
+ multiplex: true
+ Artifact {
+ fileTags: "p1"
+ filePath: "p1-dummy"
+ }
+ prepare: {
+ var cmd = new JavaScriptCommand();
+ cmd.description = "generating " + output.fileName;
+ cmd.sourceCode = function() {};
+ return cmd;
+ }
+ }
+ }
+ Product {
+ name: "p2"
+ type: "p2"
+ Depends { name: "p1" }
+ Rule {
+ requiresInputs: false
+ multiplex: true
+ inputsFromDependencies: "p1"
+ Artifact {
+ fileTags: "p2"
+ filePath: "p2-dummy"
+ }
+ prepare: {
+ var cmd = new JavaScriptCommand();
+ cmd.description = "generating " + output.fileName;
+ cmd.sourceCode = function() {};
+ return cmd;
+ }
+ }
+ }
+}
diff --git a/tests/auto/blackbox/testdata/exports-pkgconfig/TheFirstLib.pc b/tests/auto/blackbox/testdata/exports-pkgconfig/TheFirstLib.pc
new file mode 100644
index 000000000..6a83a6b37
--- /dev/null
+++ b/tests/auto/blackbox/testdata/exports-pkgconfig/TheFirstLib.pc
@@ -0,0 +1,10 @@
+prefix=/opt/the firstlib
+
+Name: TheFirstLib
+Description: TheFirstLib
+Version: 1.0
+URL: http://www.example.com/thefirstlib
+Cflags: -DTheFirstLib -I"${prefix}/include" -pthread -DHAVE_INDUSTRIAL_STRENGTH_HAIR_DRYER -I/otherdir/include1 -I/otherdir/include2
+Libs: -L"${prefix}/lib" -lTheFirstLib -pthread
+Requires: Qt5Core
+Requires.private: SomeHelper
diff --git a/tests/auto/blackbox/testdata/exports-pkgconfig/TheFirstLib_windows.pc b/tests/auto/blackbox/testdata/exports-pkgconfig/TheFirstLib_windows.pc
new file mode 100644
index 000000000..3c730a0ad
--- /dev/null
+++ b/tests/auto/blackbox/testdata/exports-pkgconfig/TheFirstLib_windows.pc
@@ -0,0 +1,10 @@
+prefix=/opt/the firstlib
+
+Name: TheFirstLib
+Description: TheFirstLib
+Version: 1.0
+URL: http://www.example.com/thefirstlib
+Cflags: -DTheFirstLib -I"${prefix}/include" -DHAVE_INDUSTRIAL_STRENGTH_HAIR_DRYER -I/otherdir/include1 -I/otherdir/include2
+Libs: -L"${prefix}/lib" -lTheFirstLib
+Requires: Qt5Core
+Requires.private: SomeHelper
diff --git a/tests/auto/blackbox/testdata/exports-pkgconfig/TheSecondLib.pc b/tests/auto/blackbox/testdata/exports-pkgconfig/TheSecondLib.pc
new file mode 100644
index 000000000..6c54b451e
--- /dev/null
+++ b/tests/auto/blackbox/testdata/exports-pkgconfig/TheSecondLib.pc
@@ -0,0 +1,9 @@
+config1=a b
+config2=c
+
+Name: TheSecondLib
+Description: The second lib
+Version: 2.0
+Cflags: -I/opt/thesecondlib/include
+Libs: -L/opt/thesecondlib/lib -lTheSecondLib
+Requires: TheFirstLib
diff --git a/tests/auto/blackbox/testdata/exports-pkgconfig/boringstaticlib.cpp b/tests/auto/blackbox/testdata/exports-pkgconfig/boringstaticlib.cpp
new file mode 100644
index 000000000..559a0ecfa
--- /dev/null
+++ b/tests/auto/blackbox/testdata/exports-pkgconfig/boringstaticlib.cpp
@@ -0,0 +1 @@
+int calculateLuckyNumber() { return 12 * 13; }
diff --git a/tests/auto/blackbox/testdata/exports-pkgconfig/exports-pkgconfig.qbs b/tests/auto/blackbox/testdata/exports-pkgconfig/exports-pkgconfig.qbs
new file mode 100644
index 000000000..674f78c94
--- /dev/null
+++ b/tests/auto/blackbox/testdata/exports-pkgconfig/exports-pkgconfig.qbs
@@ -0,0 +1,124 @@
+import qbs
+import qbs.FileInfo
+
+Project {
+ Product {
+ name: "dummy"
+ Export { Depends { name: "TheFirstLib" } }
+ }
+
+ Product {
+ name: "SomeHelper"
+ Depends { name: "Exporter.pkgconfig" }
+ Exporter.pkgconfig.versionEntry: "1.0"
+ }
+ StaticLibrary {
+ Depends { name: "cpp" }
+ name: "BoringStaticLib"
+ files: ["boringstaticlib.cpp"]
+ Export {
+ Depends { name: "cpp" }
+ cpp.defines: ["HAVE_INDUSTRIAL_STRENGTH_HAIR_DRYER"]
+ }
+ }
+ DynamicLibrary {
+ name: "TheFirstLib"
+ version: "1.0"
+
+ Depends { name: "SomeHelper" }
+ Depends { name: "Exporter.pkgconfig" }
+ Exporter.pkgconfig.excludedDependencies: ["Qt.core", "helper3"]
+ Exporter.pkgconfig.requiresEntry: "Qt5Core"
+ Exporter.pkgconfig.urlEntry: "http://www.example.com/thefirstlib"
+
+ Depends { name: "cpp" }
+ cpp.defines: ["FIRSTLIB"]
+
+ qbs.installPrefix: "/opt/the firstlib"
+
+ Export {
+ prefixMapping: [{prefix: "/somedir", replacement: "/otherdir"}]
+ Depends { name: "BoringStaticLib" }
+ Depends { name: "cpp" }
+ Depends { name: "Qt.core"; required: false }
+ Depends { name: "helper1" }
+ Depends { name: "helper3" }
+ property bool someCondition: qbs.hostOS.contains("windows") // hostOS for easier testing
+ property bool someOtherCondition: someCondition
+ Properties {
+ condition: !someOtherCondition
+ cpp.driverFlags: ["-pthread"]
+ }
+ cpp.defines: product.name
+ cpp.includePaths: [FileInfo.joinPaths(product.qbs.installPrefix, "include")]
+ Qt.core.mocName: "muck"
+ }
+
+ Group {
+ fileTagsFilter: ["dynamiclibrary", "dynamiclibrary_import"]
+ qbs.install: true
+ qbs.installDir: "lib"
+ }
+
+ Group {
+ name: "api_headers"
+ files: ["firstlib.h"]
+ qbs.install: true
+ qbs.installDir: "include"
+ }
+
+ files: ["firstlib.cpp"]
+ }
+ DynamicLibrary {
+ name: "TheSecondLib"
+ version: "2.0"
+
+ Depends { name: "Exporter.pkgconfig" }
+ Exporter.pkgconfig.descriptionEntry: "The second lib"
+ Exporter.pkgconfig.transformFunction: (function(product, moduleName, propertyName, value) {
+ if (moduleName === "cpp" && propertyName === "includePaths")
+ return value.filter(function(p) { return p !== product.sourceDirectory; });
+ return value;
+ })
+ Exporter.pkgconfig.customVariables: ({config1: "a b", config2: "c"})
+
+ Depends { name: "cpp" }
+ cpp.defines: ["SECONDLIB"]
+
+ qbs.installPrefix: ""
+
+ Depends { name: "TheFirstLib" }
+
+ Export {
+ Depends { name: "TheFirstLib" }
+ Depends { name: "dummy" }
+ Depends { name: "cpp" }
+ cpp.includePaths: ["/opt/thesecondlib/include", product.sourceDirectory]
+ property string hurz: importingProduct.name
+
+ Rule {
+ property int n: 5
+ Artifact {
+ filePath: "dummy"
+ fileTags: ["d1", "d2"]
+ cpp.warningsAreErrors: true
+ }
+ }
+ }
+
+ Group {
+ fileTagsFilter: ["dynamiclibrary", "dynamiclibrary_import"]
+ qbs.install: true
+ qbs.installDir: "/opt/thesecondlib/lib"
+ }
+
+ Group {
+ name: "api_headers"
+ files: ["secondlib.h"]
+ qbs.install: true
+ qbs.installDir: "/opt/thesecondlib/include"
+ }
+
+ files: ["secondlib.cpp"]
+ }
+}
diff --git a/tests/auto/blackbox/testdata/exports-pkgconfig/firstlib.cpp b/tests/auto/blackbox/testdata/exports-pkgconfig/firstlib.cpp
new file mode 100644
index 000000000..ab48afc94
--- /dev/null
+++ b/tests/auto/blackbox/testdata/exports-pkgconfig/firstlib.cpp
@@ -0,0 +1,3 @@
+#include "firstlib.h"
+
+void firstLib() { }
diff --git a/tests/auto/blackbox/testdata/exports-pkgconfig/firstlib.h b/tests/auto/blackbox/testdata/exports-pkgconfig/firstlib.h
new file mode 100644
index 000000000..1ccfb0868
--- /dev/null
+++ b/tests/auto/blackbox/testdata/exports-pkgconfig/firstlib.h
@@ -0,0 +1,9 @@
+#include "../dllexport.h"
+
+#ifdef FIRSTLIB
+# define FIRSTLIB_EXPORT DLL_EXPORT
+#else
+# define FIRSTLIB_EXPORT DLL_IMPORT
+#endif
+
+FIRSTLIB_EXPORT void firstLib();
diff --git a/tests/auto/blackbox/testdata/exports-pkgconfig/modules/helper1/helper1.qbs b/tests/auto/blackbox/testdata/exports-pkgconfig/modules/helper1/helper1.qbs
new file mode 100644
index 000000000..b753ec295
--- /dev/null
+++ b/tests/auto/blackbox/testdata/exports-pkgconfig/modules/helper1/helper1.qbs
@@ -0,0 +1,7 @@
+import qbs
+
+Module {
+ Depends { name: "cpp" }
+ Depends { name: "helper2" }
+ cpp.includePaths: "/somedir/include1"
+}
diff --git a/tests/auto/blackbox/testdata/exports-pkgconfig/modules/helper2/helper2.qbs b/tests/auto/blackbox/testdata/exports-pkgconfig/modules/helper2/helper2.qbs
new file mode 100644
index 000000000..9d24a812d
--- /dev/null
+++ b/tests/auto/blackbox/testdata/exports-pkgconfig/modules/helper2/helper2.qbs
@@ -0,0 +1,6 @@
+import qbs
+
+Module {
+ Depends { name: "cpp" }
+ cpp.includePaths: "/somedir/include2"
+}
diff --git a/tests/auto/blackbox/testdata/exports-pkgconfig/modules/helper3/helper3.qbs b/tests/auto/blackbox/testdata/exports-pkgconfig/modules/helper3/helper3.qbs
new file mode 100644
index 000000000..452191994
--- /dev/null
+++ b/tests/auto/blackbox/testdata/exports-pkgconfig/modules/helper3/helper3.qbs
@@ -0,0 +1,6 @@
+import qbs
+
+Module {
+ Depends { name: "cpp" }
+ cpp.includePaths: "/somedir/include3"
+}
diff --git a/tests/auto/blackbox/testdata/exports-pkgconfig/secondlib.cpp b/tests/auto/blackbox/testdata/exports-pkgconfig/secondlib.cpp
new file mode 100644
index 000000000..e1782f576
--- /dev/null
+++ b/tests/auto/blackbox/testdata/exports-pkgconfig/secondlib.cpp
@@ -0,0 +1,7 @@
+#include "secondlib.h"
+
+#ifndef HAVE_INDUSTRIAL_STRENGTH_HAIR_DRYER
+# error I CANT LIVE WITHOUT IT!
+#endif
+
+void secondLib() { }
diff --git a/tests/auto/blackbox/testdata/exports-pkgconfig/secondlib.h b/tests/auto/blackbox/testdata/exports-pkgconfig/secondlib.h
new file mode 100644
index 000000000..78b52f7fe
--- /dev/null
+++ b/tests/auto/blackbox/testdata/exports-pkgconfig/secondlib.h
@@ -0,0 +1,9 @@
+#include "../dllexport.h"
+
+#ifdef SECONDLIB
+# define SECONDLIB_EXPORT DLL_EXPORT
+#else
+# define SECONDLIB_EXPORT DLL_IMPORT
+#endif
+
+SECONDLIB_EXPORT void secondLib();
diff --git a/tests/auto/blackbox/testdata/import-assignment/import-assignment.qbs b/tests/auto/blackbox/testdata/import-assignment/import-assignment.qbs
new file mode 100644
index 000000000..2bff00f35
--- /dev/null
+++ b/tests/auto/blackbox/testdata/import-assignment/import-assignment.qbs
@@ -0,0 +1,23 @@
+import qbs
+import MyImport
+
+Product {
+ type: "outtype"
+ property var importValue: MyImport
+ Rule {
+ multiplex: true
+ Artifact {
+ fileTags: "outtype"
+ filePath: "dummy"
+ }
+ prepare: {
+ var cmd = new JavaScriptCommand;
+ cmd.silent = true;
+ cmd.sourceCode = function() {
+ console.info("key 1 = " + product.importValue.key1);
+ console.info("key 2 = " + product.importValue.key2);
+ };
+ return cmd;
+ }
+ }
+}
diff --git a/tests/auto/blackbox/testdata/import-assignment/imports/MyImport/myimport.js b/tests/auto/blackbox/testdata/import-assignment/imports/MyImport/myimport.js
new file mode 100644
index 000000000..5befd5151
--- /dev/null
+++ b/tests/auto/blackbox/testdata/import-assignment/imports/MyImport/myimport.js
@@ -0,0 +1,2 @@
+var key1 = "value1";
+var key2 = "value2";
diff --git a/tests/auto/blackbox/testdata/probes-and-shadow-products/probes-and-shadow-products.qbs b/tests/auto/blackbox/testdata/probes-and-shadow-products/probes-and-shadow-products.qbs
new file mode 100644
index 000000000..660c088a0
--- /dev/null
+++ b/tests/auto/blackbox/testdata/probes-and-shadow-products/probes-and-shadow-products.qbs
@@ -0,0 +1,13 @@
+import qbs
+
+Product {
+ name: "p"
+ multiplexByQbsProperties: "buildVariants"
+ qbs.buildVariants: ["debug", "release"]
+ Export {
+ Probe {
+ id: dummy
+ configure: { found = true; }
+ }
+ }
+}
diff --git a/tests/auto/blackbox/testdata/smart-relinking/smart-relinking.qbs b/tests/auto/blackbox/testdata/smart-relinking/smart-relinking.qbs
index 049ade899..44d8013b9 100644
--- a/tests/auto/blackbox/testdata/smart-relinking/smart-relinking.qbs
+++ b/tests/auto/blackbox/testdata/smart-relinking/smart-relinking.qbs
@@ -25,6 +25,14 @@ Project {
condition: tcProbe.found
name:"app"
Depends { name: "lib" }
+ Depends { name: "staticlib" }
files: ["main.cpp"]
}
+ StaticLibrary {
+ condition: tcProbe.found
+ name: "staticlib"
+ Depends { name: "lib" }
+ Depends { name: "cpp" }
+ files: "staticlib.cpp"
+ }
}
diff --git a/tests/auto/blackbox/testdata/smart-relinking/staticlib.cpp b/tests/auto/blackbox/testdata/smart-relinking/staticlib.cpp
new file mode 100644
index 000000000..24fd8c6e9
--- /dev/null
+++ b/tests/auto/blackbox/testdata/smart-relinking/staticlib.cpp
@@ -0,0 +1 @@
+static void myFunc() {}
diff --git a/tests/auto/blackbox/tst_blackbox.cpp b/tests/auto/blackbox/tst_blackbox.cpp
index 41ce32e40..9022bd975 100644
--- a/tests/auto/blackbox/tst_blackbox.cpp
+++ b/tests/auto/blackbox/tst_blackbox.cpp
@@ -716,6 +716,35 @@ void TestBlackbox::changedFiles()
QVERIFY2(m_qbsStdout.contains("file1.cpp"), m_qbsStdout.constData());
}
+void TestBlackbox::changedRuleInputs()
+{
+ QDir::setCurrent(testDataDir + "/changed-rule-inputs");
+
+ // Initial build.
+ QCOMPARE(runQbs(), 0);
+ QVERIFY2(m_qbsStdout.contains("generating p1-dummy"), m_qbsStdout.constData());
+ QVERIFY2(m_qbsStdout.contains("generating p2-dummy"), m_qbsStdout.constData());
+
+ // Re-build: p1 is always regenerated, and p2 has a dependency on it.
+ QCOMPARE(runQbs(), 0);
+ QVERIFY2(m_qbsStdout.contains("generating p1-dummy"), m_qbsStdout.constData());
+ QVERIFY2(m_qbsStdout.contains("generating p2-dummy"), m_qbsStdout.constData());
+
+ // Remove the dependency. p2 gets re-generated one last time, because its set of
+ // inputs changed.
+ WAIT_FOR_NEW_TIMESTAMP();
+ REPLACE_IN_FILE("changed-rule-inputs.qbs", "inputsFromDependencies: \"p1\"",
+ "inputsFromDependencies: \"p3\"");
+ QCOMPARE(runQbs(), 0);
+ QVERIFY2(m_qbsStdout.contains("generating p1-dummy"), m_qbsStdout.constData());
+ QVERIFY2(m_qbsStdout.contains("generating p2-dummy"), m_qbsStdout.constData());
+
+ // Now the artifacts are no longer connected, and p2 must not get rebuilt anymore.
+ QCOMPARE(runQbs(), 0);
+ QVERIFY2(m_qbsStdout.contains("generating p1-dummy"), m_qbsStdout.constData());
+ QVERIFY2(!m_qbsStdout.contains("generating p2-dummy"), m_qbsStdout.constData());
+}
+
void TestBlackbox::changeInDisabledProduct()
{
QDir::setCurrent(testDataDir + "/change-in-disabled-product");
@@ -2750,6 +2779,19 @@ void TestBlackbox::probeProperties()
QVERIFY2(m_qbsStdout.contains("probe2.filePath=" + dir + "/bin/tool"), m_qbsStdout.constData());
}
+void TestBlackbox::probesAndShadowProducts()
+{
+ QDir::setCurrent(testDataDir + "/probes-and-shadow-products");
+ QCOMPARE(runQbs(QStringList("--log-time")), 0);
+ QVERIFY2(m_qbsStdout.contains("2 probes encountered, 1 configure scripts executed"),
+ m_qbsStdout.constData());
+ WAIT_FOR_NEW_TIMESTAMP();
+ touch("probes-and-shadow-products.qbs");
+ QCOMPARE(runQbs(QStringList("--log-time")), 0);
+ QVERIFY2(m_qbsStdout.contains("2 probes encountered, 0 configure scripts executed"),
+ m_qbsStdout.constData());
+}
+
void TestBlackbox::probeInExportedModule()
{
QDir::setCurrent(testDataDir + "/probe-in-exported-module");
@@ -3330,6 +3372,22 @@ void TestBlackbox::exportToOutsideSearchPath()
m_qbsStderr.constData());
}
+void TestBlackbox::exportsPkgconfig()
+{
+ QDir::setCurrent(testDataDir + "/exports-pkgconfig");
+ QCOMPARE(runQbs(), 0);
+ QFile sourcePcFile(HostOsInfo::isWindowsHost() ? "TheFirstLib_windows.pc" : "TheFirstLib.pc");
+ QString generatedPcFilePath = relativeProductBuildDir("TheFirstLib") + "/TheFirstLib.pc";
+ QFile generatedPcFile(generatedPcFilePath);
+ QVERIFY2(sourcePcFile.open(QIODevice::ReadOnly), qPrintable(sourcePcFile.errorString()));
+ QVERIFY2(generatedPcFile.open(QIODevice::ReadOnly), qPrintable(generatedPcFile.errorString()));
+ QCOMPARE(generatedPcFile.readAll().replace("\r", ""), sourcePcFile.readAll().replace("\r", ""));
+ sourcePcFile.close();
+ generatedPcFile.close();
+ TEXT_FILE_COMPARE(relativeProductBuildDir("TheSecondLib") + "/TheSecondLib.pc",
+ "TheSecondLib.pc");
+}
+
void TestBlackbox::exportsQbs()
{
QDir::setCurrent(testDataDir + "/exports-qbs");
@@ -6260,6 +6318,14 @@ void TestBlackbox::ico()
}
}
+void TestBlackbox::importAssignment()
+{
+ QDir::setCurrent(testDataDir + "/import-assignment");
+ QCOMPARE(runQbs(QStringList("project.qbsSearchPaths:" + QDir::currentPath())), 0);
+ QVERIFY2(m_qbsStdout.contains("key 1 = value1") && m_qbsStdout.contains("key 2 = value2"),
+ m_qbsStdout.constData());
+}
+
void TestBlackbox::importChangeTracking()
{
QDir::setCurrent(testDataDir + "/import-change-tracking");
diff --git a/tests/auto/blackbox/tst_blackbox.h b/tests/auto/blackbox/tst_blackbox.h
index 3784a03b1..a2c021140 100644
--- a/tests/auto/blackbox/tst_blackbox.h
+++ b/tests/auto/blackbox/tst_blackbox.h
@@ -59,6 +59,7 @@ private slots:
void buildGraphVersions();
void changedFiles_data();
void changedFiles();
+ void changedRuleInputs();
void changeInDisabledProduct();
void changeInImportedFile();
void changeTrackingAndMultiplexing();
@@ -108,6 +109,7 @@ private slots:
void exportedPropertyInDisabledProduct_data();
void exportRule();
void exportToOutsideSearchPath();
+ void exportsPkgconfig();
void exportsQbs();
void externalLibs();
void fileDependencies();
@@ -117,6 +119,7 @@ private slots:
void generator_data();
void groupsInModules();
void ico();
+ void importAssignment();
void importChangeTracking();
void importInPropertiesCondition();
void importSearchPath();
@@ -198,6 +201,7 @@ private slots:
void preventFloatingPointValues();
void probeChangeTracking();
void probeProperties();
+ void probesAndShadowProducts();
void probeInExportedModule();
void probesAndArrayProperties();
void probesInNestedModules();
diff --git a/tests/auto/blackbox/tst_blackboxqt.cpp b/tests/auto/blackbox/tst_blackboxqt.cpp
index 93eb68f80..03e52590b 100644
--- a/tests/auto/blackbox/tst_blackboxqt.cpp
+++ b/tests/auto/blackbox/tst_blackboxqt.cpp
@@ -77,7 +77,11 @@ void TestBlackboxQt::cachedQml()
QDir::setCurrent(testDataDir + "/cached-qml");
QCOMPARE(runQbs(), 0);
QString dataDir = relativeBuildDir() + "/install-root/data";
- if (QFile::exists(dataDir + "/main.cpp")) {
+ QVERIFY2(m_qbsStdout.contains("qmlcachegen must work: true")
+ || m_qbsStdout.contains("qmlcachegen must work: false"),
+ m_qbsStdout.constData());
+ if (m_qbsStdout.contains("qmlcachegen must work: false")
+ && QFile::exists(dataDir + "/main.cpp")) {
// If C++ source files were installed then Qt.qmlcache is not available. See project file.
QSKIP("No QML cache files generated.");
}