diff options
169 files changed, 2180 insertions, 1469 deletions
@@ -10,12 +10,12 @@ Windows XP SP2 or later OS X 10.6 or later Linux (tested on Debian 6/7 and Ubuntu 13) -Building the sources requires Qt 5.1.0 or later. +Building the sources requires Qt 5.4.0 or later. Build Instructions ================== Prerequisites: - * Qt 5.1.0 or later + * Qt 5.4.0 or later * On Windows: - MinGW or Visual Studio * On OS X: Xcode diff --git a/doc/qbs.qdoc b/doc/qbs.qdoc index 3db14b6ee..574ed4a2f 100644 --- a/doc/qbs.qdoc +++ b/doc/qbs.qdoc @@ -118,13 +118,7 @@ \title System Requirements - To build \QBS from the source, you need the following: - - \list - - \li Qt >= 5.1.0 - - \endlist + To build \QBS from the source, you need Qt 5.4.0, or later */ diff --git a/doc/reference/commands.qdoc b/doc/reference/commands.qdoc index 232cab63c..c985e1818 100644 --- a/doc/reference/commands.qdoc +++ b/doc/reference/commands.qdoc @@ -35,12 +35,11 @@ \page commands.html \title Command and JavaScriptCommand - \brief Types of commands to be used in rules and transformers + \brief Types of commands to be used in rules A \e command is what \QBS executes at build time. It is represented in the language by an object of type \c Command, which runs a process, or \c JavaScriptCommand, which executes arbitrary - JavaScript code. A command is always created in the prepare script of a \c Rule or - \c Transformer. + JavaScript code. A command is always created in the prepare script of a \c Rule. \section1 Command @@ -69,7 +68,7 @@ \c outputs are available. As the example shows, arbitrary properties can be set on the command object and then used within the source code. This technique is typically used to forward values from the prepare script to the command. - The \l{Transformer Item} documentation shows a \c JavaScriptCommand in context. + The \l{Rule Item} documentation shows a \c JavaScriptCommand in context. \section1 Properties diff --git a/doc/reference/items/artifact.qdoc b/doc/reference/items/artifact.qdoc index 597594c05..606632a12 100644 --- a/doc/reference/items/artifact.qdoc +++ b/doc/reference/items/artifact.qdoc @@ -35,10 +35,9 @@ \ingroup list-of-items \title Artifact Item - \brief Describes a file produced by a \c Rule or \c Transformer. + \brief Describes a file produced by a \c Rule. - An \c Artifact represents a single file produced by a \l{Rule Item}{Rule} or - \l{Transformer Item}{Transformer}. + An \c Artifact represents a single file produced by a \l{Rule Item}{Rule}. For example, if a rule produces three files, it needs to contain three Artifact items. diff --git a/doc/reference/items/rule.qdoc b/doc/reference/items/rule.qdoc index a578c14ca..509839d2d 100644 --- a/doc/reference/items/rule.qdoc +++ b/doc/reference/items/rule.qdoc @@ -42,6 +42,39 @@ one or more artifacts (e.g. C++ linker). A \e {simplex rule} creates one transformer per matching input file (e.g. C++ compiler). + For instance, the following rule transforms text files: + \code + Rule { + inputs: ["txt_input"] + Artifact { + filePath: "output.txt" + fileTags: "txt_output" + } + prepare: { + var cmd = new JavaScriptCommand(); + cmd.description = "Processing '" + input.filePath + "'"; + cmd.highlight = "codegen"; + cmd.sourceCode = function() { + var file = new TextFile(input.filePath); + var content = file.readAll(); + file.close() + content = content.replace(/\r\n/g, "\n"); + file = new TextFile(output.filePath, TextFile.WriteOnly); + file.write(content); + file.close(); + } + return cmd; + } + } + \endcode + This example exhibits some interesting features of rules: + \list + \li If there is only one input file, the property \c input is available as syntactic sugar + for \c inputs[0]. + \li The filenames of the output artifacts are available as \c outputs. If there is only one + of these, it can be referred to it as \c output. + \endlist + As a real-world example of a simplex rule, here is a simplified version of \QBS' rule for transforming C++ sources into object files using gcc: \code @@ -114,8 +147,8 @@ \li auxiliaryInputs \li string list \li undefined - \li A list of file tags. This rule will be dependent on every other rule and - transformer that produces artifacts that are compatible with \a{auxiliaryInputs}. + \li A list of file tags. This rule will be dependent on every other rule + that produces artifacts that are compatible with \a{auxiliaryInputs}. Unlike \a{inputs}, the property \a{auxiliaryInputs} has no effect on the content of the \a{inputs} variable in the \a{prepare} script. \row diff --git a/doc/reference/items/subproject.qdoc b/doc/reference/items/subproject.qdoc index b7b53f2bd..e790e945a 100644 --- a/doc/reference/items/subproject.qdoc +++ b/doc/reference/items/subproject.qdoc @@ -31,7 +31,7 @@ \contentspage list-of-items.html \previouspage staticlibrary-item.html \page subproject-item.html - \nextpage transformer-item.html + \nextpage xpcservice-item.html \ingroup list-of-items \title SubProject Item diff --git a/doc/reference/items/transformer.qdoc b/doc/reference/items/transformer.qdoc deleted file mode 100644 index e496e62e9..000000000 --- a/doc/reference/items/transformer.qdoc +++ /dev/null @@ -1,115 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2015 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. -** -****************************************************************************/ -/*! - \contentspage list-of-items.html - \page transformer-item.html - \previouspage subproject-item.html - \nextpage xpcservice-item.html - \ingroup list-of-items - - \title Transformer Item - \brief Creates files, typically from other files. - - A \e transformer takes zero or more inputs and produces one or more output artifacts - from them. The following transformer creates one output file from one input file: - \code - Transformer { - inputs: "raw_input.txt" - Artifact { - filePath: "processed_input.txt" - fileTags: "processed_file" - } - prepare: { - var cmd = new JavaScriptCommand(); - cmd.description = "Processing '" + input.filePath + "'"; - cmd.highlight = "codegen"; - cmd.sourceCode = function() { - var file = new TextFile(input.filePath); - var content = file.readAll(); - file.close() - content = content.replace(/\r\n/g, "\n"); - file = new TextFile(output.filePath, TextFile.WriteOnly); - file.truncate(); - file.write(content); - file.close(); - } - return cmd; - } - } - \endcode - This example exhibits some interesting features of transformers: - \list - \li If there is only one input file, the property \c input is available as syntactic sugar - for \c inputs[0]. - \li The filenames of the output artifacts are available as \c outputs. If there is only one - of these, it can be referred to it as \c output. - \endlist - - A \c Transformer is always attached to a \c Product, possibly indirectly via a \c Module. - - \section1 Transformer Properties - - \table - \header - \li Property - \li Type - \li Default - \li Description - \row - \li inputs - \li stringList - \li empty list - \li The list of inputs to the transformer. - \row - \li prepare - \li list of Javascript commands - \li empty list - \li The commands that the transformer runs. These typically read from the input files and - write to the output files in some way. - \row - \li condition - \li bool - \li true - \li If true, the transformer is enabled, otherwise it does nothing. - \row - \li explicitlyDependsOn - \li stringList - \li \c{undefined} - \li A list of file tags. All output artifacts of this transformer will have a dependency - to all artifacts with the given file tags. - \row - \li alwaysRun - \li bool - \li false - \li If true, the transformer's commands are always executed, even if all output artifacts - are up to date. - \endtable - -*/ diff --git a/doc/reference/items/xpcservice.qdoc b/doc/reference/items/xpcservice.qdoc index cdbe8e5c8..a20f8d708 100644 --- a/doc/reference/items/xpcservice.qdoc +++ b/doc/reference/items/xpcservice.qdoc @@ -31,7 +31,7 @@ /*! \contentspage list-of-items.html \page xpcservice-item.html - \previouspage transformer-item.html + \previouspage subproject-item.html \ingroup list-of-items \title XPCService Item diff --git a/doc/reference/modules/cpp-module.qdoc b/doc/reference/modules/cpp-module.qdoc index 03b6393bb..5042e92f2 100644 --- a/doc/reference/modules/cpp-module.qdoc +++ b/doc/reference/modules/cpp-module.qdoc @@ -93,13 +93,6 @@ \li List of preprocessor macros that are used for all projects that are built for the current target platform. User project files usually do not set this property. \row - \li compilerDefines - \li \c{stringList} - \li 1.0 - \li \c{undefined} - \li List of preprocessor macros that are used for all projects that are using the current - toolchain. User project files usually do not set this property. - \row \li includePaths \li \c{pathList} \li 1.0 @@ -410,15 +403,6 @@ \li List of frameworks to be weakly linked. If the framework is part of your project, consider using a Depends item instead. \row - \li installNamePrefix - \li \c{string} - \li 1.0 - \li \c{undefined} - \li The prefix for the internal install name (LC_ID_DYLIB) of a dynamic library on Darwin - (OS X and iOS). Typically this should be set to \c{"@rpath"} on modern platforms that - support it, which includes OS X 10.5 and above, and all versions of iOS. - \b Deprecated: use \c{cpp.sonamePrefix} instead. - \row \li automaticReferenceCounting \li \c{bool} \li 1.4 @@ -631,6 +615,48 @@ If undefined, compiler defaults will be used. \endtable + \section1 Advanced Properties + + \table + \header + \li Property + \li Type + \li Since + \li Default + \li Description + \row + \li compilerDefines + \li \c{stringList} + \li 1.0 + \li \c{undefined} + \li List of preprocessor macros that are used for all projects that are using the current + toolchain. User project files usually do not set this property. + \row + \li compilerIncludePaths + \li \c{pathList} + \li 1.6 + \li determined automatically + \li List of #include search paths that are used for all projects that are using the current + toolchain. Determined automatically by probing the compiler. + User project files usually do not set this property. + \row + \li compilerFrameworkPaths + \li \c{pathList} + \li 1.6 + \li determined automatically + \li List of framework search paths that are used for all projects that are using the current + toolchain. Determined automatically by probing the compiler. + User project files usually do not set this property. + \row + \li compilerLibraryPaths + \li \c{pathList} + \li 1.6 + \li determined automatically + \li List of library search paths that are used for all projects that are using the current + toolchain. Determined automatically by probing the compiler. + User project files usually do not set this property. + \endtable + \section1 Relevant File Tags \table diff --git a/examples/cocoa-application/CocoaApplication.qbs b/examples/cocoa-application/CocoaApplication.qbs index 472f22231..a04acfb6b 100644 --- a/examples/cocoa-application/CocoaApplication.qbs +++ b/examples/cocoa-application/CocoaApplication.qbs @@ -45,9 +45,6 @@ CppApplication { cpp.precompiledHeader: "CocoaApplication/CocoaApplication-Prefix.pch" - // TODO: Remove in 1.6 - bundle.infoPlistFile: "CocoaApplication/CocoaApplication-Info.plist" - cpp.frameworks: ["Cocoa"] Group { @@ -55,7 +52,7 @@ CppApplication { files: [ "AppDelegate.h", "AppDelegate.m", - //"CocoaApplication-Info.plist", + "CocoaApplication-Info.plist", "CocoaApplication-Prefix.pch", "main.m" ] diff --git a/examples/cocoa-touch-application/CocoaTouchApplication.qbs b/examples/cocoa-touch-application/CocoaTouchApplication.qbs index c0660d564..4e09f81ca 100644 --- a/examples/cocoa-touch-application/CocoaTouchApplication.qbs +++ b/examples/cocoa-touch-application/CocoaTouchApplication.qbs @@ -45,9 +45,6 @@ CppApplication { cpp.precompiledHeader: "CocoaTouchApplication/CocoaTouchApplication-Prefix.pch" - // TODO: Remove in 1.6 - bundle.infoPlistFile: "CocoaTouchApplication/CocoaTouchApplication-Info.plist" - cpp.frameworks: [ "UIKit", "Foundation", "CoreGraphics" ] Group { @@ -55,7 +52,7 @@ CppApplication { files: [ "AppDelegate.h", "AppDelegate.m", - //"CocoaTouchApplication-Info.plist", + "CocoaTouchApplication-Info.plist", "CocoaTouchApplication-Prefix.pch", "Default-568h@2x.png", "Default.png", diff --git a/examples/collidingmice/main.cpp b/examples/collidingmice/main.cpp index 1d98a9449..3873be610 100644 --- a/examples/collidingmice/main.cpp +++ b/examples/collidingmice/main.cpp @@ -77,7 +77,7 @@ int main(int argc, char **argv) view.show(); QTimer timer; - QObject::connect(&timer, SIGNAL(timeout()), &scene, SLOT(advance())); + QObject::connect(&timer, &QTimer::timeout, &scene, &QGraphicsScene::advance); timer.start(1000 / 33); return app.exec(); diff --git a/qbs-resources/imports/QbsApp.qbs b/qbs-resources/imports/QbsApp.qbs index 530e9fddb..a6a53048e 100644 --- a/qbs-resources/imports/QbsApp.qbs +++ b/qbs-resources/imports/QbsApp.qbs @@ -13,7 +13,7 @@ QbsProduct { Group { fileTagsFilter: product.type qbs.install: true - qbs.installDir: project.appInstallDir + qbs.installDir: qbsbuildconfig.appInstallDir } Group { name: "logging" diff --git a/qbs-resources/imports/QbsAutotest.qbs b/qbs-resources/imports/QbsAutotest.qbs index 43db835a9..6e955971c 100644 --- a/qbs-resources/imports/QbsAutotest.qbs +++ b/qbs-resources/imports/QbsAutotest.qbs @@ -7,6 +7,7 @@ QtApplication { name: "tst_" + testName Depends { name: "Qt.test" } Depends { name: "qbscore" } + Depends { name: "qbsbuildconfig" } cpp.includePaths: "../../../src" cpp.cxxLanguageVersion: "c++11" destinationDirectory: "bin" diff --git a/qbs-resources/imports/QbsFunctions/functions.js b/qbs-resources/imports/QbsFunctions/functions.js index a58b917fa..7ffebd147 100644 --- a/qbs-resources/imports/QbsFunctions/functions.js +++ b/qbs-resources/imports/QbsFunctions/functions.js @@ -1,4 +1,4 @@ -function qbsVersion() { return "1.5.1"; } +function qbsVersion() { return "1.6.0"; } function versionIsAtLeast(actualVersion, expectedVersion) { diff --git a/qbs-resources/imports/QbsLibrary.qbs b/qbs-resources/imports/QbsLibrary.qbs index 4d05ea004..2d3e10fb1 100644 --- a/qbs-resources/imports/QbsLibrary.qbs +++ b/qbs-resources/imports/QbsLibrary.qbs @@ -7,9 +7,9 @@ QbsProduct { version: QbsFunctions.qbsVersion() type: Qt.core.staticBuild ? "staticlibrary" : "dynamiclibrary" targetName: (qbs.enableDebugCode && qbs.targetOS.contains("windows")) ? (name + 'd') : name - destinationDirectory: qbs.targetOS.contains("windows") ? "bin" : project.libDirName + destinationDirectory: qbs.targetOS.contains("windows") ? "bin" : qbsbuildconfig.libDirName cpp.defines: base.concat(type == "staticlibrary" ? ["QBS_STATIC_LIB"] : ["QBS_LIBRARY"]) - cpp.installNamePrefix: "@rpath" + cpp.sonamePrefix: qbs.targetOS.contains("darwin") ? "@rpath" : undefined cpp.visibility: "minimal" cpp.cxxLanguageVersion: "c++11" bundle.isBundle: false @@ -17,12 +17,14 @@ QbsProduct { Group { fileTagsFilter: product.type.concat("dynamiclibrary_symlink") qbs.install: true - qbs.installDir: project.libInstallDir + qbs.installDir: qbsbuildconfig.libInstallDir } Export { Depends { name: "cpp" } Depends { name: "Qt"; submodules: ["core"] } - cpp.rpaths: project.libRPaths + Depends { name: "qbsbuildconfig" } + + cpp.rpaths: qbsbuildconfig.libRPaths cpp.includePaths: [product.sourceDirectory] cpp.defines: product.type === "staticlibrary" ? ["QBS_STATIC_LIB"] : [] } diff --git a/qbs-resources/imports/QbsProduct.qbs b/qbs-resources/imports/QbsProduct.qbs index de5f7f12a..305e4c755 100644 --- a/qbs-resources/imports/QbsProduct.qbs +++ b/qbs-resources/imports/QbsProduct.qbs @@ -2,8 +2,9 @@ import qbs import QbsFunctions Product { + Depends { name: "qbsbuildconfig" } Depends { name: "Qt.core" } - property string minimumQtVersion: "5.1.0" + property string minimumQtVersion: "5.4.0" cpp.defines: { var res = ["QT_NO_CAST_FROM_ASCII", "QT_NO_PROCESS_COMBINED_ARGUMENT_START"]; if (qbs.toolchain.contains("msvc")) diff --git a/qbs-resources/modules/qbsbuildconfig/qbsbuildconfig.qbs b/qbs-resources/modules/qbsbuildconfig/qbsbuildconfig.qbs new file mode 100644 index 000000000..0ceca7dd4 --- /dev/null +++ b/qbs-resources/modules/qbsbuildconfig/qbsbuildconfig.qbs @@ -0,0 +1,25 @@ +import qbs + +Module { + property bool enableUnitTests: false + property bool enableProjectFileUpdates: false + property bool enableRPath: true + property bool installApiHeaders: true + property string libDirName: "lib" + property string appInstallDir: "bin" + property string libInstallDir: qbs.targetOS.contains("windows") ? "bin" : libDirName + property string libexecInstallDir: "libexec/qbs" + property string relativeLibexecPath: "../" + libexecInstallDir + property string relativePluginsPath: "../" + libDirName + property string relativeSearchPath: ".." + property stringList libRPaths: { + if (!enableRPath) + return undefined; + if (qbs.targetOS.contains("linux")) + return ["$ORIGIN/../" + libDirName]; + if (qbs.targetOS.contains("osx")) + return ["@loader_path/../" + libDirName] + } + property string resourcesInstallDir: "" + property string pluginsInstallDir: libDirName +} @@ -21,9 +21,9 @@ defineTest(minQtVersion) { return(false) } -!minQtVersion(5, 1, 0) { +!minQtVersion(5, 4, 0) { message("Cannot build qbs with Qt version $${QT_VERSION}.") - error("Use at least Qt 5.1.0.") + error("Use at least Qt 5.4.0.") } TEMPLATE = subdirs @@ -3,28 +3,7 @@ import qbs 1.0 Project { minimumQbsVersion: "1.4" qbsSearchPaths: ["qbs-resources"] - property bool enableUnitTests: false - property bool enableProjectFileUpdates: false - property bool enableRPath: true - property bool installApiHeaders: true property bool withExamples: false - property string libDirName: "lib" - property string appInstallDir: "bin" - property string libInstallDir: qbs.targetOS.contains("windows") ? "bin" : libDirName - property string libexecInstallDir: "libexec/qbs" - property string relativeLibexecPath: "../" + libexecInstallDir - property string relativePluginsPath: "../" + libDirName - property string relativeSearchPath: ".." - property stringList libRPaths: { - if (!enableRPath) - return undefined; - if (qbs.targetOS.contains("linux")) - return ["$ORIGIN/../" + libDirName]; - if (qbs.targetOS.contains("osx")) - return ["@loader_path/../" + libDirName] - } - property string resourcesInstallDir: "" - property string pluginsInstallDir: libDirName references: [ "dist/dist.qbs", diff --git a/qbs_version.pri b/qbs_version.pri index c7011efac..4977f48da 100644 --- a/qbs_version.pri +++ b/qbs_version.pri @@ -1,3 +1,3 @@ -QBS_VERSION = 1.5.1 +QBS_VERSION = 1.6.0 QBS_VERSION_MAJ = $$section(QBS_VERSION, ., 0, 0) DEFINES += QBS_VERSION=\\\"$$QBS_VERSION\\\" diff --git a/share/qbs/imports/qbs/DarwinTools/darwin-tools.js b/share/qbs/imports/qbs/DarwinTools/darwin-tools.js index 50605c84b..d33eb92e4 100644 --- a/share/qbs/imports/qbs/DarwinTools/darwin-tools.js +++ b/share/qbs/imports/qbs/DarwinTools/darwin-tools.js @@ -199,6 +199,7 @@ function expandPlistEnvironmentVariables(obj, env, warn) { // skip replacement if (warn) console.warn("undefined variable " + varName + " in variable expansion"); + i = j + repl.syntax.close.length; } else { changes = true; varValue = String(varValue); diff --git a/share/qbs/imports/qbs/ModUtils/utils.js b/share/qbs/imports/qbs/ModUtils/utils.js index f019bec6f..670c3894c 100644 --- a/share/qbs/imports/qbs/ModUtils/utils.js +++ b/share/qbs/imports/qbs/ModUtils/utils.js @@ -33,6 +33,7 @@ var File = loadExtension("qbs.File"); var FileInfo = loadExtension("qbs.FileInfo"); var Process = loadExtension("qbs.Process"); var TemporaryDir = loadExtension("qbs.TemporaryDir"); +var Utilities = loadExtension("qbs.Utilities"); function artifactInstalledFilePath(artifact, product) { var relativeInstallDir = artifact.moduleProperty("qbs", "installDir"); @@ -103,31 +104,26 @@ function languagePropertyName(propertyName, fileTag) { "c": { "flags": "cFlags", "platformFlags": "platformCFlags", - "precompiledHeader": "cPrecompiledHeader", // TODO: Remove in 1.6 "usePrecompiledHeader": "useCPrecompiledHeader" }, "cpp": { "flags": "cxxFlags", "platformFlags": "platformCxxFlags", - "precompiledHeader": "cxxPrecompiledHeader", // TODO: Remove in 1.6 "usePrecompiledHeader": "useCxxPrecompiledHeader" }, "objc": { "flags": "objcFlags", "platformFlags": "platformObjcFlags", - "precompiledHeader": "objcPrecompiledHeader", // TODO: Remove in 1.6 "usePrecompiledHeader": "useObjcPrecompiledHeader" }, "objcpp": { "flags": "objcxxFlags", "platformFlags": "platformObjcxxFlags", - "precompiledHeader": "objcxxPrecompiledHeader", // TODO: Remove in 1.6 "usePrecompiledHeader": "useObjcxxPrecompiledHeader" }, "common": { "flags": "commonCompilerFlags", - "platformFlags": "platformCommonCompilerFlags", - "precompiledHeader": "precompiledHeader" // TODO: Remove in 1.6 + "platformFlags": "platformCommonCompilerFlags" }, "asm": asm, "asm_cpp": asm @@ -478,3 +474,69 @@ var BlackboxOutputArtifactTracker = (function () { }; return BlackboxOutputArtifactTracker; })(); + +function guessArchitecture(m) { + function hasAnyOf(m, tokens) { + for (var i = 0; i < tokens.length; ++i) { + if (m[tokens[i]] !== undefined) + return true; + } + } + + var architecture; + if (m) { + // based on the search algorithm from qprocessordetection.h in qtbase + if (hasAnyOf(m, ["__arm__", "__TARGET_ARCH_ARM", "_M_ARM", "__aarch64__"])) { + if (hasAnyOf(m, ["__aarch64__"])) { + architecture = "arm64"; + } else { + architecture = "arm"; + + var foundSubarch = false; + for (var i = 7; i >= 4; --i) { + var codes = ["zk", "tej", "te", "t2"].concat([].concat.apply([], + new Array(26)).map(function(_, i) { return String.fromCharCode(122 - i); })); + for (var j = 0; j < codes.length; ++j) { + if (m["__ARM_ARCH_" + i + codes[j].toUpperCase() + "__"] !== undefined) { + architecture += "v" + i + codes[j].toLowerCase(); + foundSubarch = true; + break; + } + } + + if (i === 7 && m["_ARM_ARCH_7"] !== undefined) { + architecture += "v7"; + foundSubarch = true; + } + + if (foundSubarch) + break; + } + } + } else if (hasAnyOf(m, ["__i386", "__i386__", "_M_IX86"])) { + architecture = "x86"; + } else if (hasAnyOf(m, ["__x86_64", "__x86_64__", "__amd64", "_M_X64"])) { + architecture = "x86_64"; + } else if (hasAnyOf(m, ["__ia64", "__ia64__", "_M_IA64"])) { + architecture = "ia64"; + } else if (hasAnyOf(m, ["__mips", "__mips__", "_M_MRX000"])) { + architecture = "mips"; + if (hasAnyOf(m, ["_MIPS_ARCH_MIPS64", "__mips64"])) + architecture += "64"; + } else if (hasAnyOf(m, ["__ppc__", "__ppc", "__powerpc__", + "_ARCH_COM", "_ARCH_PWR", "_ARCH_PPC", "_M_MPPC", "_M_PPC"])) { + architecture = "ppc"; + if (hasAnyOf(m, ["__ppc64__", "__powerpc64__", "__64BIT__"])) + architecture += "64"; + } else if (hasAnyOf(m, ["__s390__"])) { + if (hasAnyOf(m, ["__s390x__"])) + architecture = "s390x"; + } else if (hasAnyOf(m, ["__sparc__"])) { + architecture = "sparc"; + if (hasAnyOf(m, ["__sparc64__"])) + architecture += "64"; + } + } + + return Utilities.canonicalArchitecture(architecture); +} diff --git a/share/qbs/imports/qbs/Probes/GccProbe.qbs b/share/qbs/imports/qbs/Probes/GccProbe.qbs new file mode 100644 index 000000000..35a1371be --- /dev/null +++ b/share/qbs/imports/qbs/Probes/GccProbe.qbs @@ -0,0 +1,96 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing +** +** This file is part of the Qt Build Suite. +** +** 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. +** +****************************************************************************/ + +import qbs +import qbs.ModUtils +import "../../../modules/cpp/gcc.js" as Gcc + +PathProbe { + // Inputs + property string compilerFilePath + property string preferredArchitecture + property string preferredMachineType + property stringList flags: [] + + property bool _haveArchFlag: qbs.targetOS.contains("darwin") + property string _nullDevice: qbs.nullDevice + property string _toolchain: qbs.toolchain + property string _pathListSeparator: qbs.pathListSeparator + property string _targetOS: qbs.targetOS + property string _sysroot: qbs.sysroot + + // Outputs + property string architecture + property int versionMajor + property int versionMinor + property int versionPatch + property stringList includePaths + property stringList libraryPaths + property stringList frameworkPaths + + configure: { + var args = flags; + if (_haveArchFlag) { + if (preferredArchitecture) + args.push("-arch", preferredArchitecture); + } else { + if (preferredArchitecture === "i386") + args.push("-m32"); + else if (preferredArchitecture === "x86_64") + args.push("-m64"); + + if (preferredMachineType) + args.push("-march=" + preferredMachineType); + } + + var macros = Gcc.dumpMacros(compilerFilePath, args, _nullDevice); + var defaultPaths = Gcc.dumpDefaultPaths(compilerFilePath, args, _nullDevice, + _pathListSeparator, _targetOS, _sysroot); + found = !!macros && !!defaultPaths; + + includePaths = defaultPaths.includePaths; + libraryPaths = defaultPaths.libraryPaths; + frameworkPaths = defaultPaths.frameworkPaths; + + // We have to dump the compiler's macros; -dumpmachine is not suitable because it is not + // always complete (for example, the subarch is not included for arm architectures). + architecture = ModUtils.guessArchitecture(macros) || preferredArchitecture; + + if (_toolchain.contains("clang")) { + versionMajor = macros["__clang_major__"]; + versionMinor = macros["__clang_minor__"]; + versionPatch = macros["__clang_patchlevel__"]; + } else { + versionMajor = macros["__GNUC__"]; + versionMinor = macros["__GNUC_MINOR__"]; + versionPatch = macros["__GNUC_PATCHLEVEL__"]; + } + } +} diff --git a/share/qbs/imports/qbs/base/Application.qbs b/share/qbs/imports/qbs/base/Application.qbs index 7deb5871d..fb9a429a7 100644 --- a/share/qbs/imports/qbs/base/Application.qbs +++ b/share/qbs/imports/qbs/base/Application.qbs @@ -36,13 +36,11 @@ Product { } property bool isForAndroid: qbs.targetOS.contains("android") - property stringList architectures: isForAndroid ? ["armv5"] : undefined + property stringList architectures: isForAndroid ? ["armv5te"] : undefined - Depends { name: "Android.ndk"; condition: isForAndroid } Depends { name: "bundle" } - Depends { name: "cpp"; condition: isForAndroid } - profiles: isForAndroid + profiles: architectures ? architectures.map(function(arch) { return project.profile + '-' + arch; }) : [project.profile] } diff --git a/share/qbs/imports/qbs/base/Library.qbs b/share/qbs/imports/qbs/base/Library.qbs index e754a24bf..ea5bd2959 100644 --- a/share/qbs/imports/qbs/base/Library.qbs +++ b/share/qbs/imports/qbs/base/Library.qbs @@ -36,13 +36,11 @@ Product { } property bool isForAndroid: qbs.targetOS.contains("android") - property stringList architectures: isForAndroid ? ["armv5"] : undefined + property stringList architectures: isForAndroid ? ["armv5te"] : undefined - Depends { name: "Android.ndk"; condition: isForAndroid } Depends { name: "bundle" } - Depends { name: "cpp"; condition: isForAndroid } - profiles: isForAndroid + profiles: architectures ? architectures.map(function(arch) { return project.profile + '-' + arch; }) : [project.profile] } diff --git a/share/qbs/modules/Android/ndk/ndk.qbs b/share/qbs/modules/Android/ndk/ndk.qbs index 8d8316acb..71cc4b38f 100644 --- a/share/qbs/modules/Android/ndk/ndk.qbs +++ b/share/qbs/modules/Android/ndk/ndk.qbs @@ -33,13 +33,10 @@ import qbs.File import qbs.FileInfo import qbs.ModUtils import qbs.Probes -import qbs.TextFile import "utils.js" as NdkUtils Module { - Depends { name: "cpp" } - Probes.AndroidNdkProbe { id: ndkProbe environmentPaths: [ndkDir].concat(base) @@ -69,13 +66,6 @@ Module { } property string hostArch: ndkProbe.hostArch - property string toolchainDir: { - if (qbs.toolchain && qbs.toolchain.contains("clang")) - return "llvm-" + toolchainVersionNumber; - if (["x86", "x86_64"].contains(abi)) - return abi + "-" + toolchainVersionNumber; - return cpp.toolchainPrefix + toolchainVersionNumber; - } property bool hardFloat property string ndkDir: ndkProbe.path @@ -87,18 +77,15 @@ Module { File.Dirs | File.NoDotAndDotDot) property stringList availableToolchainVersions: { - var prefix = ["x86", "x86_64"].contains(abi) ? (abi + "-") : cpp.toolchainPrefix; - if (qbs.toolchain.contains("clang")) - prefix = "llvm-"; - var tcs = availableToolchains; var versions = []; for (var i = 0; i < tcs.length; ++i) { - if (tcs[i].startsWith(prefix)) { - var v = tcs[i].substr(prefix.length); - var re = /^([0-9]+)\.([0-9]+)$/; - if (v.match(re)) - versions.push(v); + if ((qbs.toolchain.contains("clang") && tcs[i].startsWith("llvm-")) + || toolchainDirPrefixAbis.contains(tcs[i].split("-")[0])) { + var re = /\-((?:[0-9]+)\.(?:[0-9]+))$/; + var m = tcs[i].match(re); + if (m) + versions.push(m[1]); } } @@ -138,6 +125,15 @@ Module { return list; } + property stringList toolchainDirPrefixAbis: { + var list = ["arm"]; + if (platformVersion >= 9) + list.push("mipsel", "x86"); + if (platformVersion >= 21) + list.push("aarch64", "mips64el", "x86_64"); + return list; + } + property string toolchainVersionNumber: { var prefix = "clang"; if (toolchainVersion && toolchainVersion.startsWith(prefix)) @@ -145,198 +141,28 @@ Module { return toolchainVersion; } - property stringList defines: ["ANDROID"] property string buildProfile: (abi === "armeabi-v7a" && hardFloat) ? (abi + "-hard") : abi - property string cxxStlBaseDir: FileInfo.joinPaths(ndkDir, "sources/cxx-stl") - property string gabiBaseDir: FileInfo.joinPaths(cxxStlBaseDir, "gabi++") - property string stlPortBaseDir: FileInfo.joinPaths(cxxStlBaseDir, "stlport") - property string gnuStlBaseDir: FileInfo.joinPaths(cxxStlBaseDir, "gnu-libstdc++", - toolchainVersionNumber) - property string llvmStlBaseDir: FileInfo.joinPaths(cxxStlBaseDir, "llvm-libc++") - property string stlBaseDir: { - if (appStl.startsWith("gabi++_")) - return gabiBaseDir; - else if (appStl.startsWith("stlport_")) - return stlPortBaseDir; - else if (appStl.startsWith("gnustl_")) - return gnuStlBaseDir; - else if (appStl.startsWith("c++_")) - return llvmStlBaseDir; - return undefined; - } - property string stlLibsDir: { - if (stlBaseDir) { - var infix = buildProfile; - if (armMode === "thumb") - infix = FileInfo.joinPaths(infix, "thumb"); - return FileInfo.joinPaths(stlBaseDir, "libs", infix); - } - return undefined; - } - - property string sharedStlFilePath: (stlLibsDir && appStl.endsWith("_shared")) - ? FileInfo.joinPaths(stlLibsDir, cpp.dynamicLibraryPrefix + appStl + cpp.dynamicLibrarySuffix) - : undefined - property string staticStlFilePath: (stlLibsDir && appStl.endsWith("_static")) - ? FileInfo.joinPaths(stlLibsDir, cpp.staticLibraryPrefix + appStl + cpp.staticLibrarySuffix) - : undefined property string gdbserverFileName: "gdbserver" - property string armMode: abi.startsWith("armeabi") + property string armMode: abi && abi.startsWith("armeabi") ? (qbs.buildVariant === "debug" ? "arm" : "thumb") : undefined; PropertyOptions { - name: "armModeType" + name: "armMode" description: "Determines the instruction set for armeabi configurations." allowedValues: ["arm", "thumb"] } - cpp.toolchainInstallPath: FileInfo.joinPaths(ndkDir, "toolchains", toolchainDir, "prebuilt", - hostArch, "bin") - - cpp.toolchainPrefix: { - if (qbs.toolchain && qbs.toolchain.contains("clang")) - return undefined; - return [cpp.targetAbi === "androideabi" ? "arm" : cpp.targetArch, - cpp.targetSystem, cpp.targetAbi].join("-") + "-"; - } - - qbs.optimization: cpp.targetAbi === "androideabi" ? "small" : base - - cpp.enableExceptions: appStl !== "system" - cpp.enableRtti: appStl !== "system" - - cpp.commonCompilerFlags: NdkUtils.commonCompilerFlags(qbs.buildVariant, abi, hardFloat, armMode) - - cpp.linkerFlags: NdkUtils.commonLinkerFlags(abi, hardFloat) - - cpp.libraryPaths: { - var prefix = FileInfo.joinPaths(cpp.sysroot, "usr"); - var paths = []; - if (abi === "mips64" || abi === "x86_64") // no lib64 for arm64-v8a - paths.push(FileInfo.joinPaths(prefix, "lib64")); - paths.push(FileInfo.joinPaths(prefix, "lib")); - return paths; - } - - cpp.dynamicLibraries: { - var libs = ["c"]; - if (!hardFloat) - libs.push("m"); - if (sharedStlFilePath) - libs.push(sharedStlFilePath); - return libs; - } - cpp.staticLibraries: { - var libs = ["gcc"]; - if (hardFloat) - libs.push("m_hard"); - if (staticStlFilePath) - libs.push(staticStlFilePath); - return libs; - } - cpp.systemIncludePaths: { - var includes = []; - if (appStl === "system") { - includes.push(FileInfo.joinPaths(cxxStlBaseDir, "system", "include")); - } else if (appStl.startsWith("gabi++")) { - includes.push(FileInfo.joinPaths(gabiBaseDir, "include")); - } else if (appStl.startsWith("stlport")) { - includes.push(FileInfo.joinPaths(stlPortBaseDir, "stlport")); - } else if (appStl.startsWith("gnustl")) { - includes.push(FileInfo.joinPaths(gnuStlBaseDir, "include")); - includes.push(FileInfo.joinPaths(gnuStlBaseDir, "libs", buildProfile, "include")); - includes.push(FileInfo.joinPaths(gnuStlBaseDir, "include", "backward")); - } else if (appStl.startsWith("c++_")) { - includes.push(FileInfo.joinPaths(llvmStlBaseDir, "libcxx", "include")); - includes.push(FileInfo.joinPaths(llvmStlBaseDir + "abi", "libcxxabi", "include")); - } - return includes; - } - cpp.defines: { - var list = defines; - if (hardFloat) - list.push("_NDK_MATH_NO_SOFTFP=1"); - return list; - } - cpp.sysroot: FileInfo.joinPaths(ndkDir, "platforms", platform, - "arch-" + NdkUtils.abiNameToDirName(abi)) - - cpp.targetArch: { - if (qbs.architecture === "arm64") - return "aarch64"; - if (qbs.architecture === "armv5") - return qbs.architecture + "te"; - if (qbs.architecture === "x86") - return "i686"; - return qbs.architecture; - } - - cpp.targetVendor: "none" - cpp.targetSystem: "linux" - cpp.targetAbi: "android" + (["armeabi", "armeabi-v7a"].contains(abi) ? "eabi" : "") - - Rule { - inputs: ["dynamiclibrary"] - outputFileTags: ["android.nativelibrary", "android.gdbserver-info", "android.stl-info"] - outputArtifacts: { - var artifacts = [{ - filePath: FileInfo.joinPaths("stripped-libs", - inputs["dynamiclibrary"][0].fileName), - fileTags: ["android.nativelibrary"] - }]; - if (product.moduleProperty("qbs", "buildVariant") === "debug") { - artifacts.push({ - filePath: "android.gdbserver-info.txt", - fileTags: ["android.gdbserver-info"] - }); - } - var stlFilePath = ModUtils.moduleProperty(product, "sharedStlFilePath"); - if (stlFilePath) - artifacts.push({filePath: "android.stl-info.txt", fileTags: ["android.stl-info"]}); - return artifacts; - } - - prepare: { - var stlFilePath = ModUtils.moduleProperty(product, "sharedStlFilePath"); - var copyCmd = new JavaScriptCommand(); - copyCmd.silent = true; - copyCmd.stlFilePath = stlFilePath; - copyCmd.sourceCode = function() { - File.copy(inputs["dynamiclibrary"][0].filePath, - outputs["android.nativelibrary"][0].filePath); - var destDir = FileInfo.joinPaths("lib", ModUtils.moduleProperty(product, "abi")); - if (product.moduleProperty("qbs", "buildVariant") === "debug") { - var arch = ModUtils.moduleProperty(product, "abi"); - arch = NdkUtils.abiNameToDirName(arch); - var srcPath = FileInfo.joinPaths(ModUtils.moduleProperty(product, "ndkDir"), - "prebuilt/android-" + arch, "gdbserver/gdbserver"); - var targetPath = FileInfo.joinPaths(destDir, ModUtils.moduleProperty(product, - "gdbserverFileName")); - var infoFile = new TextFile(outputs["android.gdbserver-info"][0].filePath, - TextFile.WriteOnly); - infoFile.writeLine(srcPath); - infoFile.writeLine(targetPath); - infoFile.close(); - } - if (stlFilePath) { - var srcPath = stlFilePath; - var targetPath = FileInfo.joinPaths(destDir, FileInfo.fileName(srcPath)); - var infoFile = new TextFile(outputs["android.stl-info"][0].filePath, - TextFile.WriteOnly); - infoFile.writeLine(srcPath); - infoFile.writeLine(targetPath); - infoFile.close(); - } - } - var stripArgs = ["--strip-unneeded", outputs["android.nativelibrary"][0].filePath]; - if (stlFilePath) - stripArgs.push(stlFilePath); - var stripCmd = new Command(product.moduleProperty("cpp", "stripPath"), stripArgs); - stripCmd.description = "Stripping unneeded symbols from " - + outputs["android.nativelibrary"][0].fileName; - return [copyCmd, stripCmd]; - } + validate: { + var validator = new ModUtils.PropertyValidator("Android.ndk"); + validator.setRequiredProperty("abi", abi); + validator.setRequiredProperty("appStl", appStl); + validator.setRequiredProperty("toolchainVersion", toolchainVersion); + validator.setRequiredProperty("hostArch", hostArch); + validator.setRequiredProperty("ndkDir", ndkDir); + validator.setRequiredProperty("platform", platform); + validator.setRequiredProperty("toolchainVersionNumber", toolchainVersionNumber); + return validator.validate(); } } diff --git a/share/qbs/modules/Android/ndk/utils.js b/share/qbs/modules/Android/ndk/utils.js index 8c4f2cd70..ba7118dea 100644 --- a/share/qbs/modules/Android/ndk/utils.js +++ b/share/qbs/modules/Android/ndk/utils.js @@ -29,23 +29,36 @@ ****************************************************************************/ function abiNameToDirName(abiName) { - if (abiName.startsWith("armeabi")) + switch (abiName) { + case "armeabi": + case "armeabi-v7a": return "arm"; - if (abiName.startsWith("arm64")) + case "arm64-v8a": return "arm64"; - return abiName; + default: + return abiName; + } } function androidAbi(arch) { - if (arch === "x86" || arch === "x86_64") + switch (arch) { + case "arm64": + return "arm64-v8a"; + case "armv5": + case "armv5te": + return "armeabi"; + case "armv7": + case "armv7a": + return "armeabi-v7a"; + case "mips": + case "mipsel": + return "mips"; + case "mips64": + case "mips64el": + return "mips64"; + default: return arch; - return { - "arm64": "arm64-v8a", - "armv5": "armeabi", - "armv7": "armeabi-v7a", - "mipsel": "mips", - "mips64el": "mips64" - }[arch]; + } } function commonCompilerFlags(buildVariant, abi, hardFloat, armMode) { @@ -67,10 +80,10 @@ function commonCompilerFlags(buildVariant, abi, hardFloat, armMode) { flags.push("-fpic", "-fstack-protector", "-finline-limit=64"); if (abi === "armeabi") - flags.push("-march=armv5te", "-mtune=xscale", "-msoft-float"); + flags.push("-mtune=xscale", "-msoft-float"); if (abi === "armeabi-v7a") { - flags.push("-march=armv7-a", "-mfpu=vfpv3-d16"); + flags.push("-mfpu=vfpv3-d16"); flags.push(hardFloat ? "-mhard-float" : "-mfloat-abi=softfp"); } @@ -102,7 +115,7 @@ function commonLinkerFlags(abi, hardFloat) { var flags = ["-no-canonical-prefixes", "-Wl,-z,noexecstack", "-Wl,-z,relro", "-Wl,-z,now"]; if (abi === "armeabi-v7a") { - flags.push("-march=armv7-a", "-Wl,--fix-cortex-a8"); + flags.push("-Wl,--fix-cortex-a8"); if (hardFloat) flags.push("-Wl,-no-warn-mismatch"); } diff --git a/share/qbs/modules/bundle/BundleModule.qbs b/share/qbs/modules/bundle/BundleModule.qbs index f1a80a668..005f5f363 100644 --- a/share/qbs/modules/bundle/BundleModule.qbs +++ b/share/qbs/modules/bundle/BundleModule.qbs @@ -260,10 +260,6 @@ Module { patterns: ["Info.plist", "*-Info.plist"] } - // TODO: Remove in 1.6 (deprecated, backwards compatibility) - property path infoPlistFile - Group { name: "Info.plist"; files: bundle.infoPlistFile ? [bundle.infoPlistFile] : [] } - Rule { condition: qbs.targetOS.contains("darwin") multiplex: true diff --git a/share/qbs/modules/cpp/CppModule.qbs b/share/qbs/modules/cpp/CppModule.qbs index 7dabab95e..a32e40e95 100644 --- a/share/qbs/modules/cpp/CppModule.qbs +++ b/share/qbs/modules/cpp/CppModule.qbs @@ -35,12 +35,15 @@ import qbs.WindowsUtils Module { condition: false + property string compilerVersion: + [compilerVersionMajor, compilerVersionMinor, compilerVersionPatch].join(".") property int compilerVersionMajor property int compilerVersionMinor property int compilerVersionPatch property string warningLevel : 'all' // 'none', 'all' property bool treatWarningsAsErrors : false property string architecture: qbs.architecture + property string machineType // undocumented property string optimization: qbs.optimization property bool debugInformation: qbs.debugInformation property bool enableReproducibleBuilds: false @@ -51,15 +54,6 @@ Module { property bool useObjcPrecompiledHeader: false property bool useObjcxxPrecompiledHeader: false - // TODO: Remove these in 1.6 - property path cxxPrecompiledHeader: precompiledHeader - // ### default to undefined on non-Apple platforms for now - QBS-346 - property path precompiledHeader - property path cPrecompiledHeader: precompiledHeader - property path objcPrecompiledHeader: qbs.targetOS.contains("darwin") ? precompiledHeader : undefined - property path objcxxPrecompiledHeader: qbs.targetOS.contains("darwin") ? precompiledHeader : undefined - property path precompiledHeaderDir: product.buildDirectory - property stringList defines property stringList platformDefines: qbs.enableDebugCode ? [] : ["NDEBUG"] property stringList compilerDefines @@ -131,26 +125,14 @@ Module { be set." } - property string installNamePrefix - PropertyOptions { - name: "installNamePrefix" - description: "The prefix for the internal install name (LC_ID_DYLIB) of a dynamic library \ - on Darwin (OS X and iOS)." - } - property pathList includePaths property pathList systemIncludePaths + property pathList compilerIncludePaths property pathList libraryPaths + property pathList compilerLibraryPaths property pathList frameworkPaths property pathList systemFrameworkPaths - - // TODO: Remove in 1.6 (deprecated, backwards compatibility) - property pathList linkerScripts - Group { - name: "qbs_cpp_linkerscript" - files: cpp.linkerScripts || [] - fileTags: ["linkerscript"] - } + property pathList compilerFrameworkPaths property string assemblerName property string assemblerPath: assemblerName diff --git a/share/qbs/modules/cpp/DarwinGCC.qbs b/share/qbs/modules/cpp/DarwinGCC.qbs index 7232eb0d8..d0ce5a35e 100644 --- a/share/qbs/modules/cpp/DarwinGCC.qbs +++ b/share/qbs/modules/cpp/DarwinGCC.qbs @@ -38,7 +38,7 @@ import qbs.TextFile UnixGCC { condition: false - Depends { name: "xcode"; required: false } + Depends { name: "xcode"; required: qbs.toolchain && qbs.toolchain.contains("xcode") } targetVendor: "apple" targetSystem: "darwin" @@ -56,8 +56,6 @@ UnixGCC { ? FileInfo.joinPaths(xcode.toolchainPath, "usr", "bin") : base sysroot: xcode.present ? xcode.sdkPath : base - sonamePrefix: installNamePrefix // TODO: Remove in 1.6 (deprecated, backwards compatibility) - setupBuildEnvironment: { for (var key in buildEnv) { v = new ModUtils.EnvironmentVariable(key); diff --git a/share/qbs/modules/cpp/GenericGCC.qbs b/share/qbs/modules/cpp/GenericGCC.qbs index 82ce7bb85..e5fecb332 100644 --- a/share/qbs/modules/cpp/GenericGCC.qbs +++ b/share/qbs/modules/cpp/GenericGCC.qbs @@ -33,6 +33,7 @@ import qbs.File import qbs.FileInfo import qbs.ModUtils import qbs.PathTools +import qbs.Probes import qbs.Process import qbs.Utilities import qbs.UnixUtils @@ -48,13 +49,29 @@ CppModule { } } + Probes.GccProbe { + id: gccProbe + compilerFilePath: compilerPath + preferredArchitecture: targetArch + preferredMachineType: machineType + } + + qbs.architecture: gccProbe.found ? gccProbe.architecture : original + + compilerVersionMajor: gccProbe.versionMajor + compilerVersionMinor: gccProbe.versionMinor + compilerVersionPatch: gccProbe.versionPatch + + compilerIncludePaths: gccProbe.includePaths + compilerFrameworkPaths: gccProbe.frameworkPaths + compilerLibraryPaths: gccProbe.libraryPaths + property string target: [targetArch, targetVendor, targetSystem, targetAbi].join("-") property string targetArch: qbs.architecture === "x86" ? "i386" : qbs.architecture property string targetVendor: "unknown" property string targetSystem: "unknown" property string targetAbi: "unknown" - property stringList transitiveSOs property string toolchainPrefix property path toolchainInstallPath assemblerName: 'as' @@ -162,6 +179,66 @@ CppModule { return base; } + validate: { + var validator = new ModUtils.PropertyValidator("cpp"); + validator.setRequiredProperty("architecture", architecture, + "you might want to re-run 'qbs-setup-toolchains'"); + if (gccProbe.architecture) { + validator.addCustomValidator("architecture", architecture, function (value) { + return Utilities.canonicalArchitecture(architecture) === Utilities.canonicalArchitecture(gccProbe.architecture); + }, "'" + architecture + "' differs from the architecture produced by this compiler (" + + gccProbe.architecture +")"); + } else { + // This is a warning and not an error on the rare chance some new architecture comes + // about which qbs does not know about the macros of. But it *might* still work. + if (architecture) + console.warn("Unknown architecture '" + architecture + "' " + + "may not be supported by this compiler."); + } + + var validateFlagsFunction = function (value) { + if (value) { + for (var i = 0; i < value.length; ++i) { + if (["-target", "-triple", "-arch"].contains(value[i]) + || value[i].startsWith("-march=")) + return false; + } + } + return true; + } + + var msg = "'-target', '-triple', '-arch' and '-march' cannot appear in flags; set qbs.architecture instead"; + validator.addCustomValidator("assemblerFlags", assemblerFlags, validateFlagsFunction, msg); + validator.addCustomValidator("cppFlags", cppFlags, validateFlagsFunction, msg); + validator.addCustomValidator("cFlags", cFlags, validateFlagsFunction, msg); + validator.addCustomValidator("cxxFlags", cxxFlags, validateFlagsFunction, msg); + validator.addCustomValidator("objcFlags", objcFlags, validateFlagsFunction, msg); + validator.addCustomValidator("objcxxFlags", objcxxFlags, validateFlagsFunction, msg); + validator.addCustomValidator("commonCompilerFlags", commonCompilerFlags, validateFlagsFunction, msg); + validator.addCustomValidator("platformAssemblerFlags", platformAssemblerFlags, validateFlagsFunction, msg); + //validator.addCustomValidator("platformCppFlags", platformCppFlags, validateFlagsFunction, msg); + validator.addCustomValidator("platformCFlags", platformCFlags, validateFlagsFunction, msg); + validator.addCustomValidator("platformCxxFlags", platformCxxFlags, validateFlagsFunction, msg); + validator.addCustomValidator("platformObjcFlags", platformObjcFlags, validateFlagsFunction, msg); + validator.addCustomValidator("platformObjcxxFlags", platformObjcxxFlags, validateFlagsFunction, msg); + validator.addCustomValidator("platformCommonCompilerFlags", platformCommonCompilerFlags, validateFlagsFunction, msg); + + validator.setRequiredProperty("compilerVersion", compilerVersion); + validator.setRequiredProperty("compilerVersionMajor", compilerVersionMajor); + validator.setRequiredProperty("compilerVersionMinor", compilerVersionMinor); + validator.setRequiredProperty("compilerVersionPatch", compilerVersionPatch); + validator.addVersionValidator("compilerVersion", compilerVersion, 3, 3); + validator.addRangeValidator("compilerVersionMajor", compilerVersionMajor, 1); + validator.addRangeValidator("compilerVersionMinor", compilerVersionMinor, 0); + validator.addRangeValidator("compilerVersionPatch", compilerVersionPatch, 0); + + validator.setRequiredProperty("compilerIncludePaths", compilerIncludePaths); + validator.setRequiredProperty("compilerFrameworkPaths", compilerFrameworkPaths); + validator.setRequiredProperty("compilerLibraryPaths", compilerLibraryPaths); + + validator.validate(); + } + Rule { id: dynamicLibraryLinker multiplex: true @@ -188,7 +265,6 @@ CppModule { + PathTools.dynamicLibraryFilePath(product), fileTags: ["dynamiclibrary_copy"], alwaysUpdated: false, - cpp: { transitiveSOs: Gcc.collectTransitiveSos(inputs) } }; var artifacts = [lib, libCopy]; @@ -392,55 +468,6 @@ CppModule { } } - // TODO: Remove in 1.6 - Transformer { - condition: cPrecompiledHeader !== undefined - inputs: cPrecompiledHeader - Artifact { - filePath: product.name + "_c.gch" - fileTags: "c_pch" - } - prepare: { - return Gcc.prepareCompiler.apply(this, arguments); - } - } - - Transformer { - condition: cxxPrecompiledHeader !== undefined - inputs: cxxPrecompiledHeader - Artifact { - filePath: product.name + "_cpp.gch" - fileTags: "cpp_pch" - } - prepare: { - return Gcc.prepareCompiler.apply(this, arguments); - } - } - - Transformer { - condition: objcPrecompiledHeader !== undefined - inputs: objcPrecompiledHeader - Artifact { - filePath: product.name + "_objc.gch" - fileTags: "objc_pch" - } - prepare: { - return Gcc.prepareCompiler.apply(this, arguments); - } - } - - Transformer { - condition: objcxxPrecompiledHeader !== undefined - inputs: objcxxPrecompiledHeader - Artifact { - filePath: product.name + "_objcpp.gch" - fileTags: "objcpp_pch" - } - prepare: { - return Gcc.prepareCompiler.apply(this, arguments); - } - } - FileTagger { patterns: "*.s" fileTags: ["asm"] diff --git a/share/qbs/modules/cpp/linux-gcc.qbs b/share/qbs/modules/cpp/LinuxGCC.qbs index 2d633ae1d..84570a55c 100644 --- a/share/qbs/modules/cpp/linux-gcc.qbs +++ b/share/qbs/modules/cpp/LinuxGCC.qbs @@ -31,7 +31,7 @@ import qbs 1.0 UnixGCC { - condition: qbs.targetOS.contains('linux') && + condition: qbs.targetOS.contains('linux') && !qbs.targetOS.contains("android") && qbs.toolchain && qbs.toolchain.contains('gcc') rpaths: ['$ORIGIN'] diff --git a/share/qbs/modules/cpp/android-gcc.qbs b/share/qbs/modules/cpp/android-gcc.qbs new file mode 100644 index 000000000..c42f78578 --- /dev/null +++ b/share/qbs/modules/cpp/android-gcc.qbs @@ -0,0 +1,268 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing +** +** This file is part of the Qt Build Suite. +** +** 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. +** +****************************************************************************/ + +import qbs +import qbs.File +import qbs.FileInfo +import qbs.ModUtils +import qbs.TextFile +import "../../modules/Android/ndk/utils.js" as NdkUtils + +LinuxGCC { + Depends { name: "Android.ndk" } + + condition: qbs.targetOS.contains("android") && + qbs.toolchain && qbs.toolchain.contains("gcc") + rpaths: ['$ORIGIN'] + + property string toolchainDir: { + if (qbs.toolchain && qbs.toolchain.contains("clang")) + return "llvm-" + Android.ndk.toolchainVersionNumber; + if (["x86", "x86_64"].contains(Android.ndk.abi)) + return Android.ndk.abi + "-" + Android.ndk.toolchainVersionNumber; + return toolchainPrefix + Android.ndk.toolchainVersionNumber; + } + + property string cxxStlBaseDir: FileInfo.joinPaths(Android.ndk.ndkDir, "sources", "cxx-stl") + property string gabiBaseDir: FileInfo.joinPaths(cxxStlBaseDir, "gabi++") + property string stlPortBaseDir: FileInfo.joinPaths(cxxStlBaseDir, "stlport") + property string gnuStlBaseDir: FileInfo.joinPaths(cxxStlBaseDir, "gnu-libstdc++", + Android.ndk.toolchainVersionNumber) + property string llvmStlBaseDir: FileInfo.joinPaths(cxxStlBaseDir, "llvm-libc++") + property string stlBaseDir: { + if (Android.ndk.appStl.startsWith("gabi++_")) + return gabiBaseDir; + else if (Android.ndk.appStl.startsWith("stlport_")) + return stlPortBaseDir; + else if (Android.ndk.appStl.startsWith("gnustl_")) + return gnuStlBaseDir; + else if (Android.ndk.appStl.startsWith("c++_")) + return llvmStlBaseDir; + return undefined; + } + + property string stlLibsDir: { + if (stlBaseDir) { + var infix = Android.ndk.buildProfile; + if (Android.ndk.armMode === "thumb") + infix = FileInfo.joinPaths(infix, "thumb"); + return FileInfo.joinPaths(stlBaseDir, "libs", infix); + } + return undefined; + } + + property string sharedStlFilePath: (stlLibsDir && Android.ndk.appStl.endsWith("_shared")) + ? FileInfo.joinPaths(stlLibsDir, dynamicLibraryPrefix + Android.ndk.appStl + dynamicLibrarySuffix) + : undefined + property string staticStlFilePath: (stlLibsDir && Android.ndk.appStl.endsWith("_static")) + ? FileInfo.joinPaths(stlLibsDir, staticLibraryPrefix + Android.ndk.appStl + staticLibrarySuffix) + : undefined + + toolchainInstallPath: FileInfo.joinPaths(Android.ndk.ndkDir, "toolchains", + toolchainDir, "prebuilt", + Android.ndk.hostArch, "bin") + + toolchainPrefix: { + if (qbs.toolchain && qbs.toolchain.contains("clang")) + return undefined; + return [targetAbi === "androideabi" ? "arm" : targetArch, + targetSystem, targetAbi].join("-") + "-"; + } + + machineType: { + if (Android.ndk.abi === "armeabi") + return "armv5te"; + if (Android.ndk.abi === "armeabi-v7a") + return "armv7-a"; + } + + qbs.optimization: targetAbi === "androideabi" ? "small" : base + + enableExceptions: Android.ndk.appStl !== "system" + enableRtti: Android.ndk.appStl !== "system" + + commonCompilerFlags: NdkUtils.commonCompilerFlags(qbs.buildVariant, Android.ndk.abi, + Android.ndk.hardFloat, Android.ndk.armMode) + + linkerFlags: NdkUtils.commonLinkerFlags(Android.ndk.abi, Android.ndk.hardFloat) + + libraryPaths: { + var prefix = FileInfo.joinPaths(sysroot, "usr"); + var paths = []; + if (Android.ndk.abi === "mips64" || Android.ndk.abi === "x86_64") // no lib64 for arm64-v8a + paths.push(FileInfo.joinPaths(prefix, "lib64")); + paths.push(FileInfo.joinPaths(prefix, "lib")); + return paths; + } + + dynamicLibraries: { + var libs = ["c"]; + if (!Android.ndk.hardFloat) + libs.push("m"); + if (sharedStlFilePath) + libs.push(sharedStlFilePath); + return libs; + } + staticLibraries: { + var libs = ["gcc"]; + if (Android.ndk.hardFloat) + libs.push("m_hard"); + if (staticStlFilePath) + libs.push(staticStlFilePath); + return libs; + } + systemIncludePaths: { + var includes = []; + if (Android.ndk.appStl === "system") { + includes.push(FileInfo.joinPaths(cxxStlBaseDir, "system", "include")); + } else if (Android.ndk.appStl.startsWith("gabi++")) { + includes.push(FileInfo.joinPaths(gabiBaseDir, "include")); + } else if (Android.ndk.appStl.startsWith("stlport")) { + includes.push(FileInfo.joinPaths(stlPortBaseDir, "stlport")); + } else if (Android.ndk.appStl.startsWith("gnustl")) { + includes.push(FileInfo.joinPaths(gnuStlBaseDir, "include")); + includes.push(FileInfo.joinPaths(gnuStlBaseDir, "libs", Android.ndk.buildProfile, "include")); + includes.push(FileInfo.joinPaths(gnuStlBaseDir, "include", "backward")); + } else if (Android.ndk.appStl.startsWith("c++_")) { + includes.push(FileInfo.joinPaths(llvmStlBaseDir, "libcxx", "include")); + includes.push(FileInfo.joinPaths(llvmStlBaseDir + "abi", "libcxxabi", "include")); + } + return includes; + } + defines: { + var list = ["ANDROID"]; + if (Android.ndk.hardFloat) + list.push("_NDK_MATH_NO_SOFTFP=1"); + return list; + } + sysroot: FileInfo.joinPaths(Android.ndk.ndkDir, "platforms", Android.ndk.platform, + "arch-" + NdkUtils.abiNameToDirName(Android.ndk.abi)) + + targetArch: { + switch (qbs.architecture) { + case "arm64": + return "aarch64"; + case "armv5": + case "armv5te": + return "armv5te"; + case "armv7a": + case "x86_64": + return qbs.architecture; + case "x86": + return "i686"; + case "mips": + case "mipsel": + return "mipsel"; + case "mips64": + case "mips64el": + return "mips64el"; + } + } + + targetVendor: "none" + targetSystem: "linux" + targetAbi: "android" + (["armeabi", "armeabi-v7a"].contains(Android.ndk.abi) ? "eabi" : "") + + Rule { + inputs: ["dynamiclibrary"] + outputFileTags: ["android.nativelibrary", "android.gdbserver-info", "android.stl-info"] + outputArtifacts: { + var artifacts = [{ + filePath: FileInfo.joinPaths("stripped-libs", + inputs["dynamiclibrary"][0].fileName), + fileTags: ["android.nativelibrary"] + }]; + if (product.moduleProperty("qbs", "buildVariant") === "debug") { + artifacts.push({ + filePath: "android.gdbserver-info.txt", + fileTags: ["android.gdbserver-info"] + }); + } + var stlFilePath = product.moduleProperty("cpp", "sharedStlFilePath"); + if (stlFilePath) + artifacts.push({filePath: "android.stl-info.txt", fileTags: ["android.stl-info"]}); + return artifacts; + } + + prepare: { + var stlFilePath = product.moduleProperty("cpp", "sharedStlFilePath"); + var copyCmd = new JavaScriptCommand(); + copyCmd.silent = true; + copyCmd.stlFilePath = stlFilePath; + copyCmd.sourceCode = function() { + File.copy(inputs["dynamiclibrary"][0].filePath, + outputs["android.nativelibrary"][0].filePath); + var arch = product.moduleProperty("Android.ndk", "abi"); + var destDir = FileInfo.joinPaths("lib", arch); + if (product.moduleProperty("qbs", "buildVariant") === "debug") { + arch = NdkUtils.abiNameToDirName(arch); + var srcPath = FileInfo.joinPaths( + product.moduleProperty("Android.ndk", "ndkDir"), + "prebuilt/android-" + arch, "gdbserver/gdbserver"); + var targetPath = FileInfo.joinPaths(destDir, + product.moduleProperty("Android.ndk", "gdbserverFileName")); + var infoFile = new TextFile(outputs["android.gdbserver-info"][0].filePath, + TextFile.WriteOnly); + infoFile.writeLine(srcPath); + infoFile.writeLine(targetPath); + infoFile.close(); + } + if (stlFilePath) { + var srcPath = stlFilePath; + var targetPath = FileInfo.joinPaths(destDir, FileInfo.fileName(srcPath)); + var infoFile = new TextFile(outputs["android.stl-info"][0].filePath, + TextFile.WriteOnly); + infoFile.writeLine(srcPath); + infoFile.writeLine(targetPath); + infoFile.close(); + } + } + var stripArgs = ["--strip-unneeded", outputs["android.nativelibrary"][0].filePath]; + if (stlFilePath) + stripArgs.push(stlFilePath); + var stripCmd = new Command(product.moduleProperty("cpp", "stripPath"), stripArgs); + stripCmd.description = "Stripping unneeded symbols from " + + outputs["android.nativelibrary"][0].fileName; + return [copyCmd, stripCmd]; + } + } + + validate: { + var baseValidator = new ModUtils.PropertyValidator("qbs"); + baseValidator.addCustomValidator("architecture", targetArch, function (value) { + return value !== undefined; + }, "unknown Android architecture '" + qbs.architecture + "'."); + + var validator = new ModUtils.PropertyValidator("cpp"); + validator.setRequiredProperty("targetArch", targetArch); + + return baseValidator.validate() && validator.validate(); + } +} diff --git a/share/qbs/modules/cpp/gcc.js b/share/qbs/modules/cpp/gcc.js index ebe164d9c..dfecfaac2 100644 --- a/share/qbs/modules/cpp/gcc.js +++ b/share/qbs/modules/cpp/gcc.js @@ -77,6 +77,16 @@ function linkerFlags(product, inputs, output) { var isDarwin = product.moduleProperty("qbs", "targetOS").contains("darwin"); var i, args = additionalCompilerAndLinkerFlags(product); + // ### QBS-966 + if (product.moduleProperty("qbs", "targetOS").contains("android")) { + var sharedStlFilePath = product.moduleProperty("cpp", "sharedStlFilePath"); + if (sharedStlFilePath) + dynamicLibraries.push(sharedStlFilePath); + var staticStlFilePath = product.moduleProperty("cpp", "staticStlFilePath"); + if (staticStlFilePath) + staticLibraries.push(staticStlFilePath); + } + if (output.fileTags.contains("dynamiclibrary")) { args.push(isDarwin ? "-dynamiclib" : "-shared"); @@ -134,18 +144,6 @@ function linkerFlags(product, inputs, output) { for (i in rpaths) args = args.concat(escapeLinkerFlags(product, ["-rpath", rpaths[i]])); - if (product.moduleProperty("qbs", "targetOS").contains('linux')) { - var transitiveSOs = ModUtils.modulePropertiesFromArtifacts(product, - inputs.dynamiclibrary_copy, 'cpp', 'transitiveSOs') - var uniqueSOs = [].uniqueConcat(transitiveSOs) - for (i in uniqueSOs) { - // The real library is located one level up. - args = args.concat(escapeLinkerFlags(product, [ - "-rpath-link=" + - FileInfo.path(FileInfo.path(uniqueSOs[i]))])); - } - } - if (product.moduleProperty("cpp", "entryPoint")) args = args.concat(escapeLinkerFlags(product, ["-e", product.moduleProperty("cpp", "entryPoint")])); @@ -358,6 +356,10 @@ function compilerFlags(product, input, output) { else if (arch === 'i386') args.push('-m32'); + var march = product.moduleProperty("cpp", "machineType"); + if (march) + args.push("-march=" + march); + var minimumDarwinVersion = ModUtils.moduleProperty(product, "minimumDarwinVersion"); if (minimumDarwinVersion) { var flag = ModUtils.moduleProperty(product, "minimumDarwinVersionCompilerFlag"); @@ -468,14 +470,6 @@ function compilerFlags(product, input, output) { args.push('-include', pchFilePath); } - // TODO: Remove in 1.6 - if (!pchOutput && ModUtils.moduleProperty(input, 'precompiledHeader', tag)) { - pchFilePath = FileInfo.joinPaths( - ModUtils.moduleProperty(product, "precompiledHeaderDir"), - product.name + "_" + tag); - args.push('-include', pchFilePath); - } - var positionIndependentCode = input.moduleProperty('cpp', 'positionIndependentCode') if (effectiveType === EffectiveTypeEnum.LIB) { if (positionIndependentCode !== false && !product.moduleProperty("qbs", "toolchain").contains("mingw")) @@ -675,19 +669,6 @@ function concatLibs(libs, deplibs) { return r; } -function collectTransitiveSos(inputs) -{ - var result = []; - for (var i in inputs.dynamiclibrary_copy) { - var lib = inputs.dynamiclibrary_copy[i]; - var impliedLibs = ModUtils.moduleProperties(lib, 'transitiveSOs'); - var libsToAdd = [lib.filePath].concat(impliedLibs); - result = result.concat(libsToAdd); - } - result = concatLibs([], result); - return result; -} - function prepareLinker(project, product, inputs, outputs, input, output) { var i, primaryOutput, cmd, commands = []; @@ -895,3 +876,77 @@ function debugInfoArtifacts(product) { function isNumericProductVersion(version) { return version && version.match(/^([0-9]+\.){0,3}[0-9]+$/); } + +function dumpMacros(compilerFilePath, args, nullDevice) { + var p = new Process(); + try { + p.setEnv("LC_ALL", "C"); + p.exec(compilerFilePath, (args || []).concat(["-dM", "-E", "-x", "c", nullDevice])); + var map = {}; + p.readStdOut().trim().split("\n").map(function (line) { + var parts = line.split(" ", 3); + map[parts[1]] = parts[2]; + }); + return map; + } finally { + p.close(); + } +} + +function dumpDefaultPaths(compilerFilePath, args, nullDevice, pathListSeparator, targetOS, + sysroot) { + var p = new Process(); + try { + p.setEnv("LC_ALL", "C"); + args = args || []; + if (sysroot) { + if (targetOS.contains("darwin")) + args.push("-isysroot", sysroot); + else + args.push("--sysroot=" + sysroot); + } + p.exec(compilerFilePath, args.concat(["-v", "-E", "-x", "c++", nullDevice])); + var suffix = " (framework directory)"; + var includePaths = []; + var libraryPaths = []; + var frameworkPaths = []; + var addIncludes = false; + var lines = p.readStdErr().trim().split("\n").map(function (line) { return line.trim(); }); + for (var i = 0; i < lines.length; ++i) { + var line = lines[i]; + var prefix = "LIBRARY_PATH="; + if (line.startsWith(prefix)) { + libraryPaths = libraryPaths.concat(line.substr(prefix.length) + .split(pathListSeparator)); + } else if (line === "#include <...> search starts here:") { + addIncludes = true; + } else if (line === "End of search list.") { + addIncludes = false; + } else if (addIncludes) { + if (line.endsWith(suffix)) + frameworkPaths.push(line.substr(0, line.length - suffix.length)); + else + includePaths.push(line); + } + } + + sysroot = sysroot || ""; + + if (includePaths.length === 0) + includePaths.push(sysroot + "/usr/include", sysroot + "/usr/local/include"); + + if (libraryPaths.length === 0) + libraryPaths.push(sysroot + "/lib", sysroot + "/usr/lib"); + + if (frameworkPaths.length === 0) + frameworkPaths.push(sysroot + "/System/Library/Frameworks"); + + return { + "includePaths": includePaths, + "libraryPaths": libraryPaths, + "frameworkPaths": frameworkPaths + }; + } finally { + p.close(); + } +} diff --git a/share/qbs/modules/cpp/msvc.js b/share/qbs/modules/cpp/msvc.js index ad4bada21..501197775 100644 --- a/share/qbs/modules/cpp/msvc.js +++ b/share/qbs/modules/cpp/msvc.js @@ -150,27 +150,6 @@ function prepareCompiler(project, product, inputs, outputs, input, output) { else if (tag === "c") args.push("/TC"); - // precompiled header file - // TODO: Remove in 1.6 - var pch = ModUtils.moduleProperty(input, "precompiledHeader", tag); - if (pch) { - if (pchOutput) { - // create PCH - args.push("/Yc"); - args.push("/Fp" + FileInfo.toWindowsSeparators(pchOutput.filePath)); - args.push("/Fo" + FileInfo.toWindowsSeparators(objOutput.filePath)); - args.push(FileInfo.toWindowsSeparators(input.filePath)); - } else { - // use PCH - var pchHeaderName = FileInfo.toWindowsSeparators(pch); - var pchName = FileInfo.toWindowsSeparators(ModUtils.moduleProperty(product, "precompiledHeaderDir") - + "\\.obj\\" + product.name + "_" + tag + ".pch"); - args.push("/FI" + pchHeaderName); - args.push("/Yu" + pchHeaderName); - args.push("/Fp" + pchName); - } - } - var commands = []; var usePch = ModUtils.moduleProperty(input, "usePrecompiledHeader", tag); if (usePch) { diff --git a/share/qbs/modules/cpp/windows-msvc.qbs b/share/qbs/modules/cpp/windows-msvc.qbs index fcf8e36c0..be3d72769 100644 --- a/share/qbs/modules/cpp/windows-msvc.qbs +++ b/share/qbs/modules/cpp/windows-msvc.qbs @@ -73,7 +73,6 @@ CppModule { separateDebugInformation: true property bool generateManifestFile: true - property bool generateManifestFiles: generateManifestFile // TODO: Remove in 1.6 property path toolchainInstallPath architecture: qbs.architecture staticLibraryPrefix: "" @@ -128,48 +127,6 @@ CppModule { } } - // TODO: Remove in 1.6 - Transformer { - condition: cPrecompiledHeader !== undefined - inputs: cPrecompiledHeader - Artifact { - fileTags: ['obj'] - filePath: { - var completeBaseName = FileInfo.completeBaseName(product.moduleProperty("cpp", - "cPrecompiledHeader")); - return ".obj/" + Utilities.getHash(completeBaseName) + '_c.obj' - } - } - Artifact { - fileTags: ['c_pch'] - filePath: ".obj/" + product.name + '_c.pch' - } - prepare: { - return MSVC.prepareCompiler.apply(this, arguments); - } - } - - Transformer { - condition: cxxPrecompiledHeader !== undefined - inputs: cxxPrecompiledHeader - explicitlyDependsOn: ["c_pch"] // to prevent vc--0.pdb conflict - Artifact { - fileTags: ['obj'] - filePath: { - var completeBaseName = FileInfo.completeBaseName(product.moduleProperty("cpp", - "cxxPrecompiledHeader")); - return ".obj/" + Utilities.getHash(completeBaseName) + '_cpp.obj' - } - } - Artifact { - fileTags: ['cpp_pch'] - filePath: ".obj/" + product.name + '_cpp.pch' - } - prepare: { - return MSVC.prepareCompiler.apply(this, arguments); - } - } - Rule { id: compiler inputs: ["cpp", "c"] diff --git a/share/qbs/modules/java/JavaModule.qbs b/share/qbs/modules/java/JavaModule.qbs index 69845adc9..9fff42b09 100644 --- a/share/qbs/modules/java/JavaModule.qbs +++ b/share/qbs/modules/java/JavaModule.qbs @@ -180,6 +180,7 @@ Module { JavaUtils.javacArguments(product, inputs, JavaUtils.helperOverrideArgs(product, "javac"))); + cmd.ignoreDryRun = true; cmd.silent = true; return [cmd]; } diff --git a/share/qbs/modules/qbs/common.qbs b/share/qbs/modules/qbs/common.qbs index 3732b91a7..1756b8d98 100644 --- a/share/qbs/modules/qbs/common.qbs +++ b/share/qbs/modules/qbs/common.qbs @@ -71,7 +71,7 @@ Module { property string nullDevice: hostOS.contains("windows") ? "NUL" : "/dev/null" property path shellPath: hostOS.contains("windows") ? windowsShellPath : "/bin/sh" property string profile - property stringList toolchain + property stringList toolchain: [] property string architecture property bool install: false property path installSourceBase @@ -115,6 +115,35 @@ Module { }, "'" + architecture + "' is invalid. You must use the canonical name '" + Utilities.canonicalArchitecture(architecture) + "'"); + validator.addCustomValidator("toolchain", toolchain, function (value) { + if (toolchain === undefined) + return false; // cannot have null toolchain, empty is valid... for now + var canonical = Utilities.canonicalToolchain.apply(this, toolchain); + for (var i = 0; i < Math.max(canonical.length, toolchain.length); ++i) { + if (canonical[i] !== toolchain[i]) + return false; + } + return true; + }, "'" + toolchain + "' is invalid. You must use the canonical list '" + + Utilities.canonicalToolchain.apply(this, toolchain) + "'"); + + validator.addCustomValidator("toolchain", toolchain, function (value) { + // None of the pairs listed here may appear in the same toolchain list. + // Note that this check is applied AFTER canonicalization, so for example + // {"clang", "msvc"} need not be checked, since a toolchain containing clang is + // guaranteed to also contain gcc. + var pairs = [ + ["gcc", "msvc"], + ["llvm", "mingw"] + ]; + var canonical = Utilities.canonicalToolchain.apply(this, value); + for (var i = 0; i < pairs.length; ++i) { + if (canonical.contains(pairs[i][0]) && canonical.contains(pairs[i][1])) + return false; + } + return true; + }, "'" + toolchain + "' contains one or more mutually exclusive toolchain types."); + validator.validate(); } @@ -122,14 +151,17 @@ Module { property string windowsRegistryKey: "HKEY_LOCAL_MACHINE\\Software\\Microsoft\\Windows NT\\CurrentVersion" property path windowsSystemRoot: FileInfo.fromWindowsSeparators(Utilities.getNativeSetting(windowsRegistryKey, "SystemRoot")) property path windowsShellPath: FileInfo.fromWindowsSeparators(Environment.getEnv("COMSPEC")) || FileInfo.joinPaths(windowsSystemRoot, "System32", "cmd.exe") + property string windowsPathVariable: hostOS.contains("windows") ? "PATH" : "WINEPATH" property var commonRunEnvironment: { var env = Environment.currentEnv(); if (targetOS.contains("windows")) { var newEntry = FileInfo.toWindowsSeparators(FileInfo.joinPaths(installRoot, installPrefix)); - env["PATH"] = PathTools.prependOrSetPath(newEntry, env["PATH"], qbs.pathListSeparator); - } else if (hostOS.contains("darwin") && targetOS.contains("darwin")) { + env[windowsPathVariable] = PathTools.prependOrSetPath(newEntry, + env[windowsPathVariable], + qbs.pathListSeparator); + } else if (hostOS.contains("osx") && targetOS.contains("osx")) { env["DYLD_FRAMEWORK_PATH"] = PathTools.prependOrSetPath([ FileInfo.joinPaths(installRoot, installPrefix, "Library", "Frameworks"), FileInfo.joinPaths(installRoot, installPrefix, "lib"), @@ -140,9 +172,6 @@ Module { FileInfo.joinPaths(installRoot, installPrefix, "Library", "Frameworks"), FileInfo.joinPaths(installRoot, installPrefix) ].join(pathListSeparator), env["DYLD_LIBRARY_PATH"], qbs.pathListSeparator); - if (targetOS.contains("ios-simulator") && sysroot) - env["DYLD_ROOT_PATH"] = PathTools.prependOrSetPath(sysroot, env["DYLD_ROOT_PATH"], - qbs.pathListSeparator); } else if (hostOS.contains("unix") && targetOS.contains("unix")) { env["LD_LIBRARY_PATH"] = PathTools.prependOrSetPath( FileInfo.joinPaths(installRoot, installPrefix, "lib"), env["LD_LIBRARY_PATH"], diff --git a/share/qbs/modules/typescript/TypeScriptModule.qbs b/share/qbs/modules/typescript/TypeScriptModule.qbs index 732eb1f61..ece4d8653 100644 --- a/share/qbs/modules/typescript/TypeScriptModule.qbs +++ b/share/qbs/modules/typescript/TypeScriptModule.qbs @@ -227,6 +227,7 @@ Module { }; var jcmd = new JavaScriptCommand(); + jcmd.ignoreDryRun = true; jcmd.silent = true; jcmd.inputPaths = inputPaths.sort(sortFunc); jcmd.outputPaths = outputPaths.sort(sortFunc); @@ -240,6 +241,7 @@ Module { var args = ["--module", "commonjs", "--outDir", outDir].concat(outputPaths.filter(function (f) { return !f.endsWith(".json"); })); var cmd = new Command(ModUtils.moduleProperty(product, "compilerPath"), args); + cmd.ignoreDryRun = true; cmd.silent = true; return [jcmd, cmd]; } diff --git a/share/qbs/modules/xcode/xcode.qbs b/share/qbs/modules/xcode/xcode.qbs index e9cadbf02..f6994e3be 100644 --- a/share/qbs/modules/xcode/xcode.qbs +++ b/share/qbs/modules/xcode/xcode.qbs @@ -144,7 +144,7 @@ Module { } if (!_sdkSettings) { - throw "There is no matching SDK available for ' + sdk + '."; + throw "There is no matching SDK available for " + sdk + "."; } var validator = new ModUtils.PropertyValidator("xcode"); diff --git a/share/share.qbs b/share/share.qbs index d78dd9a98..7c60a8784 100644 --- a/share/share.qbs +++ b/share/share.qbs @@ -5,12 +5,13 @@ import qbs.FileInfo Product { name: "qbs resources" type: ["copied qbs resources"] + Depends { name: "qbsbuildconfig" } Group { name: "Modules and imports" files: ["qbs/**/*"] fileTags: ["qbs resources"] qbs.install: true - qbs.installDir: project.resourcesInstallDir + "/share" + qbs.installDir: qbsbuildconfig.resourcesInstallDir + "/share" qbs.installSourceBase: "." } @@ -19,14 +20,15 @@ Product { files: ["../examples/**/*"] fileTags: [] qbs.install: true - qbs.installDir: project.resourcesInstallDir + "/share/qbs" + qbs.installDir: qbsbuildconfig.resourcesInstallDir + "/share/qbs" qbs.installSourceBase: ".." } Rule { inputs: ["qbs resources"] Artifact { - filePath: FileInfo.joinPaths(project.buildDirectory, project.resourcesInstallDir, + filePath: FileInfo.joinPaths(project.buildDirectory, + product.moduleProperty("qbsbuildconfig", "resourcesInstallDir"), "share", FileInfo.relativePath(product.sourceDirectory, input.filePath)) fileTags: ["copied qbs resources"] } diff --git a/src/app/config-ui/config-ui.qbs b/src/app/config-ui/config-ui.qbs index 3f31765c1..de1de7150 100644 --- a/src/app/config-ui/config-ui.qbs +++ b/src/app/config-ui/config-ui.qbs @@ -15,7 +15,10 @@ QbsApp { Group { condition: qbs.targetOS.contains("osx") - files: ["fgapp.mm"] + files: [ + "fgapp.mm", + "Info.plist" + ] } Properties { @@ -25,5 +28,4 @@ QbsApp { Depends { name: "bundle" } bundle.isBundle: false - bundle.infoPlistFile: "Info.plist" } diff --git a/src/app/config-ui/mainwindow.cpp b/src/app/config-ui/mainwindow.cpp index 97e04bc18..2a3627e2f 100644 --- a/src/app/config-ui/mainwindow.cpp +++ b/src/app/config-ui/mainwindow.cpp @@ -49,9 +49,9 @@ MainWindow::MainWindow(const QString &settingsDir, QWidget *parent) m_model = new qbs::SettingsModel(settingsDir, this); ui->treeView->setModel(m_model); ui->treeView->setContextMenuPolicy(Qt::CustomContextMenu); - connect(ui->treeView, SIGNAL(expanded(QModelIndex)), SLOT(adjustColumns())); - connect(ui->treeView, SIGNAL(customContextMenuRequested(QPoint)), - SLOT(provideContextMenu(QPoint))); + connect(ui->treeView, &QTreeView::expanded, this, &MainWindow::adjustColumns); + connect(ui->treeView, &QWidget::customContextMenuRequested, + this, &MainWindow::provideContextMenu); adjustColumns(); QMenu * const fileMenu = menuBar()->addMenu(tr("&File")); @@ -59,20 +59,20 @@ MainWindow::MainWindow(const QString &settingsDir, QWidget *parent) QAction * const reloadAction = new QAction(tr("&Reload"), this); reloadAction->setShortcut(QKeySequence::Refresh); - connect(reloadAction, SIGNAL(triggered()), SLOT(reloadSettings())); + connect(reloadAction, &QAction::triggered, this, &MainWindow::reloadSettings); QAction * const saveAction = new QAction(tr("&Save"), this); saveAction->setShortcut(QKeySequence::Save); - connect(saveAction, SIGNAL(triggered()), SLOT(saveSettings())); + connect(saveAction, &QAction::triggered, this, &MainWindow::saveSettings); QAction * const expandAllAction = new QAction(tr("&Expand All"), this); expandAllAction->setShortcut(Qt::CTRL | Qt::Key_E); - connect(expandAllAction, SIGNAL(triggered()), SLOT(expandAll())); + connect(expandAllAction, &QAction::triggered, this, &MainWindow::expandAll); QAction * const collapseAllAction = new QAction(tr("C&ollapse All"), this); collapseAllAction->setShortcut(Qt::CTRL | Qt::Key_O); - connect(collapseAllAction, SIGNAL(triggered()), SLOT(collapseAll())); + connect(collapseAllAction, &QAction::triggered, this, &MainWindow::collapseAll); QAction * const exitAction = new QAction(tr("E&xit"), this); exitAction->setShortcut(QKeySequence::Quit); exitAction->setMenuRole(QAction::QuitRole); - connect(exitAction, SIGNAL(triggered()), SLOT(exit())); + connect(exitAction, &QAction::triggered, this, &MainWindow::exit); fileMenu->addAction(reloadAction); fileMenu->addAction(saveAction); diff --git a/src/app/config-ui/mainwindow.h b/src/app/config-ui/mainwindow.h index f8bba001d..8c6f1d43c 100644 --- a/src/app/config-ui/mainwindow.h +++ b/src/app/config-ui/mainwindow.h @@ -49,7 +49,7 @@ public: bool eventFilter(QObject *watched, QEvent *event); -private slots: +private: void adjustColumns(); void expandAll(); void collapseAll(); @@ -58,7 +58,6 @@ private slots: void exit(); void provideContextMenu(const QPoint &pos); -private: Ui::MainWindow *ui; qbs::SettingsModel *m_model; }; diff --git a/src/app/qbs-setup-android/android-setup.cpp b/src/app/qbs-setup-android/android-setup.cpp index 75997cf82..505d278f7 100644 --- a/src/app/qbs-setup-android/android-setup.cpp +++ b/src/app/qbs-setup-android/android-setup.cpp @@ -48,10 +48,10 @@ static QStringList expectedArchs() { return QStringList() << QStringLiteral("arm64") - << QStringLiteral("armv5") - << QStringLiteral("armv7") - << QStringLiteral("mipsel") - << QStringLiteral("mips64el") + << QStringLiteral("armv5te") + << QStringLiteral("armv7a") + << QStringLiteral("mips") + << QStringLiteral("mips64") << QStringLiteral("x86") << QStringLiteral("x86_64"); } diff --git a/src/app/qbs-setup-toolchains/compilerversion.cpp b/src/app/qbs-setup-toolchains/compilerversion.cpp index d008f20cf..8de5f3ba5 100644 --- a/src/app/qbs-setup-toolchains/compilerversion.cpp +++ b/src/app/qbs-setup-toolchains/compilerversion.cpp @@ -92,56 +92,6 @@ static QByteArray runProcess(const QString &exeFilePath, const QStringList &args return process.readAllStandardOutput().trimmed(); } -static Version getGccVersion(const QString &compilerFilePath, const QStringList &qbsToolchain) -{ - QByteArray majorKey; - QByteArray minorKey; - QByteArray patchKey; - if (qbsToolchain.contains(QLatin1String("clang"))) { - majorKey = "__clang_major__"; - minorKey = "__clang_minor__"; - patchKey = "__clang_patchlevel__"; - } else { - majorKey = "__GNUC__"; - minorKey = "__GNUC_MINOR__"; - patchKey = "__GNUC_PATCHLEVEL__"; - } - QTemporaryFile dummyFile; - if (!dummyFile.open()) - throw ErrorInfo(mkStr("Could not create temporary file (%1)").arg(dummyFile.errorString())); - dummyFile.close(); - const QStringList compilerArgs = QStringList() << QLatin1String("-E") << QLatin1String("-dM") - << QLatin1String("-x") << QLatin1String("c") - << QLatin1String("-c") << dummyFile.fileName(); - const QByteArray compilerOutput = runProcess(compilerFilePath, compilerArgs); - if (compilerOutput.isEmpty()) - throw ErrorInfo(mkStr("Could not extract version from compiler output.")); - const QList<QByteArray> lines = compilerOutput.split('\n'); - QList<QByteArray> keyParts = QList<QByteArray>() << majorKey << minorKey << patchKey; - QStringList versionParts = QStringList() << QString() << QString() << QString(); - int partsFound = 0; - foreach (const QByteArray &line, lines) { - for (int i = 0; i < keyParts.count() && partsFound < keyParts.count(); ++i) { - QString &versionPart = versionParts[i]; - if (!versionPart.isEmpty()) - continue; - const QByteArray cleanLine = line.simplified(); - const QByteArray keyPart = "#define " + keyParts.at(i) + ' '; - if (!cleanLine.startsWith(keyPart)) - continue; - versionPart = QString::fromLatin1(cleanLine.mid(keyPart.count())); - ++partsFound; - } - } - if (partsFound < keyParts.count()) - throw ErrorInfo(mkStr("Failed to extract version from compiler output.")); - const Version version = Version::fromString(versionParts.at(0) + QLatin1Char('.') - + versionParts.at(1) + QLatin1Char('.') + versionParts.at(2)); - if (!version.isValid()) - throw ErrorInfo(mkStr("Failed to extract version from compiler output.")); - return version; -} - class DummyFile { public: DummyFile(const QString &fp) : filePath(fp) { } @@ -205,15 +155,13 @@ void setCompilerVersion(const QString &compilerFilePath, const QStringList &qbsT Profile &profile, const QProcessEnvironment &compilerEnv) { try { - Version version; - if (qbsToolchain.contains(QLatin1String("gcc"))) - version = getGccVersion(compilerFilePath, qbsToolchain); - else if (qbsToolchain.contains(QLatin1String("msvc"))) - version = getMsvcVersion(compilerFilePath, compilerEnv); - if (version.isValid()) { - profile.setValue(QLatin1String("cpp.compilerVersionMajor"), version.majorVersion()); - profile.setValue(QLatin1String("cpp.compilerVersionMinor"), version.minorVersion()); - profile.setValue(QLatin1String("cpp.compilerVersionPatch"), version.patchLevel()); + if (qbsToolchain.contains(QLatin1String("msvc"))) { + const Version version = getMsvcVersion(compilerFilePath, compilerEnv); + if (version.isValid()) { + profile.setValue(QLatin1String("cpp.compilerVersionMajor"), version.majorVersion()); + profile.setValue(QLatin1String("cpp.compilerVersionMinor"), version.minorVersion()); + profile.setValue(QLatin1String("cpp.compilerVersionPatch"), version.patchLevel()); + } } } catch (const ErrorInfo &e) { qDebug("Warning: Failed to retrieve compiler version: %s", qPrintable(e.toString())); diff --git a/src/app/qbs-setup-toolchains/probe.cpp b/src/app/qbs-setup-toolchains/probe.cpp index 728c28739..793c96997 100644 --- a/src/app/qbs-setup-toolchains/probe.cpp +++ b/src/app/qbs-setup-toolchains/probe.cpp @@ -40,6 +40,7 @@ #include <tools/profile.h> #include <tools/scripttools.h> #include <tools/settings.h> +#include <tools/toolchains.h> #include <QDir> #include <QFileInfo> @@ -97,28 +98,16 @@ static QStringList validMinGWMachines() << QLatin1String("i586-mingw32msvc") << QLatin1String("amd64-mingw32msvc"); } -static QStringList completeToolchainList(const QString &toolchainName) -{ - QStringList toolchains(toolchainName); - if (toolchainName == QLatin1String("clang")) - toolchains << completeToolchainList(QLatin1String("llvm")); - else if (toolchainName == QLatin1String("llvm") || - toolchainName == QLatin1String("mingw")) { - toolchains << completeToolchainList(QLatin1String("gcc")); - } - return toolchains; -} - static QStringList toolchainTypeFromCompilerName(const QString &compilerName) { if (compilerName == QLatin1String("cl.exe")) - return completeToolchainList(QLatin1String("msvc")); + return canonicalToolchain(QLatin1String("msvc")); foreach (const QString &type, (QStringList() << QLatin1String("clang") << QLatin1String("llvm") << QLatin1String("mingw") << QLatin1String("gcc"))) if (compilerName.contains(type)) - return completeToolchainList(type); + return canonicalToolchain(type); if (compilerName == QLatin1String("g++")) - return completeToolchainList(QLatin1String("gcc")); + return canonicalToolchain(QLatin1String("gcc")); return QStringList(); } @@ -137,7 +126,7 @@ static QStringList standardCompilerFileNames() } static void setCommonProperties(Profile &profile, const QString &compilerFilePath, - const QStringList &toolchainTypes, const QString &architecture) + const QStringList &toolchainTypes) { const QFileInfo cfi(compilerFilePath); const QString compilerName = cfi.fileName(); @@ -151,7 +140,6 @@ static void setCommonProperties(Profile &profile, const QString &compilerFilePat profile.setValue(QLatin1String("cpp.toolchainInstallPath"), cfi.absolutePath()); profile.setValue(QLatin1String("qbs.toolchain"), toolchainTypes); - profile.setValue(QLatin1String("qbs.architecture"), canonicalArchitecture(architecture)); setCompilerVersion(compilerFilePath, toolchainTypes, profile); const QString suffix = compilerName.right(compilerName.size() - prefix.size()); @@ -196,21 +184,17 @@ static Profile createGccProfile(const QString &compilerFilePath, Settings *setti const QString &profileName = QString()) { const QString machineName = gccMachineName(compilerFilePath); - const QStringList compilerTriplet = machineName.split(QLatin1Char('-')); if (toolchainTypes.contains(QLatin1String("mingw"))) { if (!validMinGWMachines().contains(machineName)) { throw ErrorInfo(Tr::tr("Detected gcc platform '%1' is not supported.") .arg(machineName)); } - } else if (compilerTriplet.count() < 2) { - throw qbs::ErrorInfo(Tr::tr("Architecture of compiler for platform '%1' at '%2' not understood.") - .arg(machineName, compilerFilePath)); } Profile profile(!profileName.isEmpty() ? profileName : machineName, settings); profile.removeProfile(); - setCommonProperties(profile, compilerFilePath, toolchainTypes, compilerTriplet.first()); + setCommonProperties(profile, compilerFilePath, toolchainTypes); // Check whether auxiliary tools reside within the toolchain's install path. // This might not be the case when using icecc or another compiler wrapper. @@ -266,7 +250,7 @@ static void mingwProbe(Settings *settings, QList<Profile> &profiles) = findExecutable(HostOsInfo::appendExecutableSuffix(compilerName)); if (!gccPath.isEmpty()) profiles << createGccProfile(gccPath, settings, - completeToolchainList(QLatin1String("mingw"))); + canonicalToolchain(QLatin1String("mingw"))); } } @@ -311,7 +295,7 @@ void createProfile(const QString &profileName, const QString &toolchainType, if (toolchainType.isEmpty()) toolchainTypes = toolchainTypeFromCompilerName(compiler.fileName()); else - toolchainTypes = completeToolchainList(toolchainType); + toolchainTypes = canonicalToolchain(toolchainType); if (toolchainTypes.contains(QLatin1String("msvc"))) createMsvcProfile(profileName, compiler.absoluteFilePath(), settings); diff --git a/src/app/qbs/commandlinefrontend.cpp b/src/app/qbs/commandlinefrontend.cpp index 3297d7bbe..f2c0f1d1c 100644 --- a/src/app/qbs/commandlinefrontend.cpp +++ b/src/app/qbs/commandlinefrontend.cpp @@ -44,7 +44,6 @@ #include <QDir> #include <QFile> -#include <QMetaObject> #include <QProcessEnvironment> #include <QTimer> @@ -190,7 +189,7 @@ void CommandLineFrontend::start() // experimentally found to be acceptable. // Note that this polling approach is not problematic here, since we are doing work anyway, // so there's no danger of waking up the processor for no reason. - connect(m_cancelTimer, SIGNAL(timeout()), SLOT(checkCancelStatus())); + connect(m_cancelTimer, &QTimer::timeout, this, &CommandLineFrontend::checkCancelStatus); m_cancelTimer->start(2000); } catch (const ErrorInfo &error) { qbsError() << error.toString(); @@ -549,23 +548,23 @@ void CommandLineFrontend::connectBuildJob(AbstractJob *job) if (!bjob) return; - connect(bjob, SIGNAL(reportCommandDescription(QString,QString)), - this, SLOT(handleCommandDescriptionReport(QString,QString))); - connect(bjob, SIGNAL(reportProcessResult(qbs::ProcessResult)), - this, SLOT(handleProcessResultReport(qbs::ProcessResult))); + connect(bjob, &BuildJob::reportCommandDescription, + this, &CommandLineFrontend::handleCommandDescriptionReport); + connect(bjob, &BuildJob::reportProcessResult, + this, &CommandLineFrontend::handleProcessResultReport); } void CommandLineFrontend::connectJob(AbstractJob *job) { - connect(job, SIGNAL(finished(bool,qbs::AbstractJob*)), - SLOT(handleJobFinished(bool,qbs::AbstractJob*))); - connect(job, SIGNAL(taskStarted(QString,int,qbs::AbstractJob*)), - SLOT(handleNewTaskStarted(QString,int))); - connect(job, SIGNAL(totalEffortChanged(int,qbs::AbstractJob*)), - SLOT(handleTotalEffortChanged(int))); + connect(job, &AbstractJob::finished, + this, &CommandLineFrontend::handleJobFinished); + connect(job, &AbstractJob::taskStarted, + this, &CommandLineFrontend::handleNewTaskStarted); + connect(job, &AbstractJob::totalEffortChanged, + this, &CommandLineFrontend::handleTotalEffortChanged); if (m_parser.showProgress()) { - connect(job, SIGNAL(taskProgress(int,qbs::AbstractJob*)), - SLOT(handleTaskProgress(int,qbs::AbstractJob*))); + connect(job, &AbstractJob::taskProgress, + this, &CommandLineFrontend::handleTaskProgress); } } diff --git a/src/app/qbs/commandlinefrontend.h b/src/app/qbs/commandlinefrontend.h index 0783b189e..1b9f4d274 100644 --- a/src/app/qbs/commandlinefrontend.h +++ b/src/app/qbs/commandlinefrontend.h @@ -58,9 +58,9 @@ public: ~CommandLineFrontend(); void cancel(); - -private slots: void start(); + +private: void handleCommandDescriptionReport(const QString &highlight, const QString &message); void handleJobFinished(bool success, qbs::AbstractJob *job); void handleNewTaskStarted(const QString &description, int totalEffort); @@ -69,7 +69,6 @@ private slots: void handleProcessResultReport(const qbs::ProcessResult &result); void checkCancelStatus(); -private: typedef QHash<Project, QList<ProductData> > ProductMap; ProductMap productsToUse() const; diff --git a/src/app/qbs/main.cpp b/src/app/qbs/main.cpp index d7fe21148..e981201ba 100644 --- a/src/app/qbs/main.cpp +++ b/src/app/qbs/main.cpp @@ -78,7 +78,7 @@ int main(int argc, char *argv[]) ConsoleLogger::instance().setSettings(&settings); CommandLineFrontend clFrontend(parser, &settings); app.setCommandLineFrontend(&clFrontend); - QTimer::singleShot(0, &clFrontend, SLOT(start())); + QTimer::singleShot(0, &clFrontend, &CommandLineFrontend::start); return app.exec(); } catch (const ErrorInfo &error) { qbsError() << error.toString(); diff --git a/src/app/qbs/qbs.qbs b/src/app/qbs/qbs.qbs index e56e97960..0f88f1ddc 100644 --- a/src/app/qbs/qbs.qbs +++ b/src/app/qbs/qbs.qbs @@ -7,9 +7,9 @@ QbsApp { targetName: "qbs" cpp.defines: base.concat([ 'QBS_VERSION="' + QbsFunctions.qbsVersion() + '"', - 'QBS_RELATIVE_LIBEXEC_PATH="' + project.relativeLibexecPath + '"', - 'QBS_RELATIVE_SEARCH_PATH="' + project.relativeSearchPath + '"', - 'QBS_RELATIVE_PLUGINS_PATH="' + project.relativePluginsPath + '"' + 'QBS_RELATIVE_LIBEXEC_PATH="' + qbsbuildconfig.relativeLibexecPath + '"', + 'QBS_RELATIVE_SEARCH_PATH="' + qbsbuildconfig.relativeSearchPath + '"', + 'QBS_RELATIVE_PLUGINS_PATH="' + qbsbuildconfig.relativePluginsPath + '"', ]) files: [ "application.cpp", diff --git a/src/lib/corelib/api/internaljobs.cpp b/src/lib/corelib/api/internaljobs.cpp index 87ae69617..90ed5f630 100644 --- a/src/lib/corelib/api/internaljobs.cpp +++ b/src/lib/corelib/api/internaljobs.cpp @@ -157,21 +157,21 @@ InternalJobThreadWrapper::InternalJobThreadWrapper(InternalJob *synchronousJob, { synchronousJob->shareObserverWith(this); m_job->moveToThread(&m_thread); - connect(m_job, SIGNAL(finished(Internal::InternalJob*)), SLOT(handleFinished())); - connect(m_job, SIGNAL(newTaskStarted(QString,int,Internal::InternalJob*)), - SIGNAL(newTaskStarted(QString,int,Internal::InternalJob*))); - connect(m_job, SIGNAL(taskProgress(int,Internal::InternalJob*)), - SIGNAL(taskProgress(int,Internal::InternalJob*))); - connect(m_job, SIGNAL(totalEffortChanged(int,Internal::InternalJob*)), - SIGNAL(totalEffortChanged(int,Internal::InternalJob*))); - m_job->connect(this, SIGNAL(startRequested()), SLOT(start())); + connect(m_job, &InternalJob::finished, this, &InternalJobThreadWrapper::handleFinished); + connect(m_job, &InternalJob::newTaskStarted, + this, &InternalJob::newTaskStarted); + connect(m_job, &InternalJob::taskProgress, + this, &InternalJob::taskProgress); + connect(m_job, &InternalJob::totalEffortChanged, + this, &InternalJob::totalEffortChanged); + connect(this, &InternalJobThreadWrapper::startRequested, m_job, &InternalJob::start); } InternalJobThreadWrapper::~InternalJobThreadWrapper() { if (m_running) { QEventLoop loop; - loop.connect(m_job, SIGNAL(finished(Internal::InternalJob*)), SLOT(quit())); + connect(m_job, &InternalJob::finished, &loop, &QEventLoop::quit); cancel(); loop.exec(); } @@ -358,15 +358,15 @@ void InternalBuildJob::build(const TopLevelProjectPtr &project, QThread * const executorThread = new QThread(this); m_executor->moveToThread(executorThread); - connect(m_executor, SIGNAL(reportCommandDescription(QString,QString)), - this, SIGNAL(reportCommandDescription(QString,QString))); - connect(m_executor, SIGNAL(reportProcessResult(qbs::ProcessResult)), - this, SIGNAL(reportProcessResult(qbs::ProcessResult))); - - connect(executorThread, SIGNAL(started()), m_executor, SLOT(build())); - connect(m_executor, SIGNAL(finished()), SLOT(handleFinished())); - connect(m_executor, SIGNAL(destroyed()), executorThread, SLOT(quit())); - connect(executorThread, SIGNAL(finished()), this, SLOT(emitFinished())); + connect(m_executor, &Executor::reportCommandDescription, + this, &BuildGraphTouchingJob::reportCommandDescription); + connect(m_executor, &Executor::reportProcessResult, + this, &BuildGraphTouchingJob::reportProcessResult); + + connect(executorThread, &QThread::started, m_executor, &Executor::build); + connect(m_executor, &Executor::finished, this, &InternalBuildJob::handleFinished); + connect(m_executor, &QObject::destroyed, executorThread, &QThread::quit); + connect(executorThread, &QThread::finished, this, &InternalBuildJob::emitFinished); executorThread->start(); } diff --git a/src/lib/corelib/api/internaljobs.h b/src/lib/corelib/api/internaljobs.h index 38dd449e6..e87bcdc92 100644 --- a/src/lib/corelib/api/internaljobs.h +++ b/src/lib/corelib/api/internaljobs.h @@ -62,6 +62,7 @@ public: ~InternalJob(); void cancel(); + virtual void start() {} ErrorInfo error() const { return m_error; } void setError(const ErrorInfo &error) { m_error = error; } @@ -98,16 +99,15 @@ public: InternalJobThreadWrapper(InternalJob *synchronousJob, QObject *parent = 0); ~InternalJobThreadWrapper(); - void start(); + void start() override; InternalJob *synchronousJob() const { return m_job; } signals: void startRequested(); -private slots: +private: void handleFinished(); -private: QThread m_thread; InternalJob *m_job; bool m_running; @@ -125,10 +125,8 @@ public: TopLevelProjectPtr project() const; -private slots: - void start(); - private: + void start() override; void resolveProjectFromScratch(Internal::ScriptEngine *engine); void resolveBuildDataFromScratch(const RulesEvaluationContextPtr &evalContext); BuildGraphLoadResult restoreProject(const RulesEvaluationContextPtr &evalContext); @@ -175,11 +173,10 @@ public: void build(const TopLevelProjectPtr &project, const QList<ResolvedProductPtr> &products, const BuildOptions &buildOptions); -private slots: +private: void handleFinished(); void emitFinished(); -private: Executor *m_executor; }; @@ -193,10 +190,9 @@ public: void init(const TopLevelProjectPtr &project, const QList<ResolvedProductPtr> &products, const CleanOptions &options); -private slots: - void start(); - private: + void start() override; + CleanOptions m_options; }; @@ -211,10 +207,9 @@ public: void init(const TopLevelProjectPtr &project, const QList<ResolvedProductPtr> &products, const InstallOptions &options); -private slots: - void start(); - private: + void start() override; + TopLevelProjectPtr m_project; QList<ResolvedProductPtr> m_products; InstallOptions m_options; diff --git a/src/lib/corelib/api/jobs.cpp b/src/lib/corelib/api/jobs.cpp index 793232d92..da1629065 100644 --- a/src/lib/corelib/api/jobs.cpp +++ b/src/lib/corelib/api/jobs.cpp @@ -34,7 +34,7 @@ #include <language/language.h> #include <tools/qbsassert.h> -#include <QMetaObject> +#include <QTimer> namespace qbs { using namespace Internal; @@ -106,13 +106,13 @@ AbstractJob::AbstractJob(InternalJob *internalJob, QObject *parent) : QObject(parent), m_internalJob(internalJob) { m_internalJob->setParent(this); - connect(m_internalJob, SIGNAL(newTaskStarted(QString,int,Internal::InternalJob*)), - SLOT(handleTaskStarted(QString,int)), Qt::QueuedConnection); - connect(m_internalJob, SIGNAL(totalEffortChanged(int,Internal::InternalJob*)), - SLOT(handleTotalEffortChanged(int))); - connect(m_internalJob, SIGNAL(taskProgress(int,Internal::InternalJob*)), - SLOT(handleTaskProgress(int)), Qt::QueuedConnection); - connect(m_internalJob, SIGNAL(finished(Internal::InternalJob*)), SLOT(handleFinished())); + connect(m_internalJob, &InternalJob::newTaskStarted, + this, &AbstractJob::handleTaskStarted, Qt::QueuedConnection); + connect(m_internalJob, &InternalJob::totalEffortChanged, + this, &AbstractJob::handleTotalEffortChanged); + connect(m_internalJob, &InternalJob::taskProgress, + this, &AbstractJob::handleTaskProgress, Qt::QueuedConnection); + connect(m_internalJob, &InternalJob::finished, this, &AbstractJob::handleFinished); m_state = StateRunning; } @@ -122,8 +122,7 @@ bool AbstractJob::lockProject(const TopLevelProjectPtr &project) // synchronously. if (project->locked) { internalJob()->setError(tr("Cannot start a job while another one is in progress.")); - QMetaObject::invokeMethod(this, "finished", Qt::QueuedConnection, Q_ARG(bool, false), - Q_ARG(qbs::AbstractJob *, this)); + QTimer::singleShot(0, this, [this] { emit finished(false, this); }); return false; } project->locked = true; @@ -293,10 +292,10 @@ BuildJob::BuildJob(const Logger &logger, QObject *parent) : AbstractJob(new InternalBuildJob(logger), parent) { InternalBuildJob *job = static_cast<InternalBuildJob *>(internalJob()); - connect(job, SIGNAL(reportCommandDescription(QString,QString)), - this, SIGNAL(reportCommandDescription(QString,QString))); - connect(job, SIGNAL(reportProcessResult(qbs::ProcessResult)), - this, SIGNAL(reportProcessResult(qbs::ProcessResult))); + connect(job, &BuildGraphTouchingJob::reportCommandDescription, + this, &BuildJob::reportCommandDescription); + connect(job, &BuildGraphTouchingJob::reportProcessResult, + this, &BuildJob::reportProcessResult); } void BuildJob::build(const TopLevelProjectPtr &project, const QList<ResolvedProductPtr> &products, diff --git a/src/lib/corelib/api/jobs.h b/src/lib/corelib/api/jobs.h index d61b5f722..b6892ad0c 100644 --- a/src/lib/corelib/api/jobs.h +++ b/src/lib/corelib/api/jobs.h @@ -79,13 +79,12 @@ signals: void taskProgress(int newProgressValue, qbs::AbstractJob *job); void finished(bool success, qbs::AbstractJob *job); -private slots: +private: void handleTaskStarted(const QString &description, int maximumProgressValue); void handleTotalEffortChanged(int totalEffort); void handleTaskProgress(int newProgressValue); void handleFinished(); -private: void unlockProject(); virtual void finish() { } diff --git a/src/lib/corelib/api/project.cpp b/src/lib/corelib/api/project.cpp index a12ea1086..4a9404553 100644 --- a/src/lib/corelib/api/project.cpp +++ b/src/lib/corelib/api/project.cpp @@ -1029,8 +1029,7 @@ QList<InstallableFile> Project::installableFilesForProduct(const ProductData &pr } if (internalProduct->enabled) { QBS_CHECK(internalProduct->buildData); - foreach (const Artifact * const artifact, - ArtifactSet::fromNodeSet(internalProduct->buildData->nodes)) { + for (const Artifact *artifact : filterByType<Artifact>(internalProduct->buildData->nodes)) { if (artifact->artifactType == Artifact::SourceFile) continue; try { diff --git a/src/lib/corelib/api/runenvironment.cpp b/src/lib/corelib/api/runenvironment.cpp index bc7091cee..179237175 100644 --- a/src/lib/corelib/api/runenvironment.cpp +++ b/src/lib/corelib/api/runenvironment.cpp @@ -251,6 +251,7 @@ int RunEnvironment::doRunTarget(const QString &targetBin, const QStringList &arg } else if (QFileInfo(targetExecutable = findExecutable(QStringList() << QStringLiteral("ios-deploy"))).isExecutable()) { targetArguments = QStringList() + << QStringLiteral("--no-wifi") << QStringLiteral("--noninteractive") << QStringLiteral("--bundle") << QDir::cleanPath(bundlePath); diff --git a/src/lib/corelib/buildgraph/abstractcommandexecutor.h b/src/lib/corelib/buildgraph/abstractcommandexecutor.h index 11df366d5..d1eb13271 100644 --- a/src/lib/corelib/buildgraph/abstractcommandexecutor.h +++ b/src/lib/corelib/buildgraph/abstractcommandexecutor.h @@ -57,7 +57,6 @@ public: virtual void cancel() = 0; -public slots: void start(Transformer *transformer, const AbstractCommand *cmd); signals: diff --git a/src/lib/corelib/buildgraph/artifact.cpp b/src/lib/corelib/buildgraph/artifact.cpp index 9ae98c640..a2d743b22 100644 --- a/src/lib/corelib/buildgraph/artifact.cpp +++ b/src/lib/corelib/buildgraph/artifact.cpp @@ -65,7 +65,7 @@ Artifact::Artifact() Artifact::~Artifact() { - foreach (Artifact *p, parentArtifacts()) + for (Artifact *p : parentArtifacts()) p->childrenAddedByScanner.remove(this); } @@ -117,14 +117,14 @@ void Artifact::initialize() oldDataPossiblyPresent = true; } -ArtifactSet Artifact::parentArtifacts() const +const TypeFilter<Artifact> Artifact::parentArtifacts() const { - return ArtifactSet::fromNodeSet(parents); + return TypeFilter<Artifact>(parents); } -ArtifactSet Artifact::childArtifacts() const +const TypeFilter<Artifact> Artifact::childArtifacts() const { - return ArtifactSet::fromNodeSet(children); + return TypeFilter<Artifact>(children); } void Artifact::onChildDisconnected(BuildGraphNode *child) diff --git a/src/lib/corelib/buildgraph/artifact.h b/src/lib/corelib/buildgraph/artifact.h index d67cdceff..81dbcaf07 100644 --- a/src/lib/corelib/buildgraph/artifact.h +++ b/src/lib/corelib/buildgraph/artifact.h @@ -87,8 +87,8 @@ public: bool oldDataPossiblyPresent : 1; void initialize(); - ArtifactSet parentArtifacts() const; - ArtifactSet childArtifacts() const; + const TypeFilter<Artifact> parentArtifacts() const; + const TypeFilter<Artifact> childArtifacts() const; void onChildDisconnected(BuildGraphNode *child); private: diff --git a/src/lib/corelib/buildgraph/buildgraph.cpp b/src/lib/corelib/buildgraph/buildgraph.cpp index 32fcdade0..3761a84d7 100644 --- a/src/lib/corelib/buildgraph/buildgraph.cpp +++ b/src/lib/corelib/buildgraph/buildgraph.cpp @@ -184,7 +184,7 @@ static void setupProductScriptValue(ScriptEngine *engine, QScriptValue &productS void setupScriptEngineForFile(ScriptEngine *engine, const ResolvedFileContextConstPtr &fileContext, QScriptValue targetObject) { - engine->import(fileContext, targetObject, targetObject); + engine->import(fileContext, targetObject); JsExtensions::setupExtensions(fileContext->jsExtensions(), targetObject); } @@ -241,13 +241,14 @@ void connect(BuildGraphNode *p, BuildGraphNode *c) { QBS_CHECK(p != c); if (Artifact *ac = dynamic_cast<Artifact *>(c)) { - foreach (const Artifact * const child, ArtifactSet::fromNodeSet(p->children)) + for (const Artifact *child : filterByType<Artifact>(p->children)) { if (child != ac && child->filePath() == ac->filePath()) { throw ErrorInfo(QString::fromLocal8Bit("%1 already has a child artifact %2 as " "different object.").arg(p->toString(), ac->filePath()), CodeLocation(), true); } + } } p->children.insert(c); c->parents.insert(p); @@ -410,40 +411,16 @@ Artifact *createArtifact(const ResolvedProductPtr &product, return artifact; } -static QString productNameForErrorMessage(const ResolvedProduct *product) -{ - return product->profile == product->topLevelProject()->profile() - ? product->name : product->uniqueName(); -} - -static void checkForConflictingArtifacts(const ResolvedProduct *product, Artifact *artifact) -{ - foreach (const ResolvedProductConstPtr &otherProduct, product->project->allProducts()) { - if (otherProduct == product || !lookupArtifact(otherProduct, artifact->filePath())) - continue; - ErrorInfo error; - error.append(Tr::tr("Conflicting artifacts for file path '%1'.").arg(artifact->filePath())); - error.append(Tr::tr("The first artifact comes from product '%1'.") - .arg(productNameForErrorMessage(otherProduct.data())), otherProduct->location); - error.append(Tr::tr("The second artifact comes from product '%1'.") - .arg(productNameForErrorMessage(product)), product->location); - throw error; - } - -} - void insertArtifact(const ResolvedProductPtr &product, Artifact *artifact, const Logger &logger) { QBS_CHECK(!artifact->product); QBS_CHECK(!artifact->filePath().isEmpty()); QBS_CHECK(!product->buildData->nodes.contains(artifact)); - if (artifact->artifactType == Artifact::Generated) - checkForConflictingArtifacts(product.data(), artifact); - product->buildData->nodes.insert(artifact); - addArtifactToSet(artifact, product->buildData->artifactsByFileTag); artifact->product = product; product->topLevelProject()->buildData->insertIntoLookupTable(artifact); product->topLevelProject()->buildData->isDirty = true; + product->buildData->nodes.insert(artifact); + addArtifactToSet(artifact, product->buildData->artifactsByFileTag); if (logger.traceEnabled()) { logger.qbsTrace() << QString::fromLocal8Bit("[BG] insert artifact '%1'") @@ -504,7 +481,7 @@ static void doSanityChecksForProduct(const ResolvedProductConstPtr &product, QBS_CHECK(output->transformer == transformer); transformerOutputChildren.unite(ArtifactSet::fromNodeSet(output->children)); QSet<QString> childFilePaths; - foreach (const Artifact * const a, ArtifactSet::fromNodeSet(output->children)) { + for (const Artifact *a : filterByType<Artifact>(output->children)) { if (childFilePaths.contains(a->filePath())) { throw ErrorInfo(QString::fromLocal8Bit("There is more than one artifact for " "file '%1' in the child list for output '%2'.") diff --git a/src/lib/corelib/buildgraph/buildgraphloader.cpp b/src/lib/corelib/buildgraph/buildgraphloader.cpp index 0f10b6c02..bf6a12b0b 100644 --- a/src/lib/corelib/buildgraph/buildgraphloader.cpp +++ b/src/lib/corelib/buildgraph/buildgraphloader.cpp @@ -252,8 +252,7 @@ void BuildGraphLoader::trackProjectChanges() // If the product gets temporarily removed, its artifacts will get disconnected // and this structural information will no longer be directly available from them. - foreach (const Artifact * const a, - ArtifactSet::fromNodeSet(product->buildData->nodes)) { + for (const Artifact *a : filterByType<Artifact>(product->buildData->nodes)) { childLists.insert(a, ChildrenInfo(ArtifactSet::fromNodeSet(a->children), a->childrenAddedByScanner)); } @@ -591,7 +590,7 @@ bool BuildGraphLoader::checkTransformersForPropertyChanges(const ResolvedProduct { bool transformerChanges = false; QSet<TransformerConstPtr> seenTransformers; - foreach (Artifact *artifact, ArtifactSet::fromNodeSet(restoredProduct->buildData->nodes)) { + for (Artifact *artifact : filterByType<Artifact>(restoredProduct->buildData->nodes)) { const TransformerPtr transformer = artifact->transformer; if (!transformer || seenTransformers.contains(transformer)) continue; @@ -739,7 +738,7 @@ void BuildGraphLoader::replaceFileDependencyWithArtifact(const ResolvedProductPt foreach (const ResolvedProductPtr &product, fileDepProduct->topLevelProject()->allProducts()) { if (!product->buildData) continue; - foreach (Artifact *artifactInProduct, ArtifactSet::fromNodeSet(product->buildData->nodes)) { + for (Artifact *artifactInProduct : filterByType<Artifact>(product->buildData->nodes)) { if (artifactInProduct->fileDependencies.contains(filedep)) { artifactInProduct->fileDependencies.remove(filedep); loggedConnect(artifactInProduct, artifact, m_logger); @@ -805,8 +804,7 @@ void BuildGraphLoader::rescueOldBuildData(const ResolvedProductConstPtr &restore // FIXME: This complete block could go away if Transformers were also to create their // output artifacts at build time. QList<Artifact *> oldArtifactsCreatedByTransformers; - foreach (Artifact *artifact, - ArtifactSet::fromNodeSet(newlyResolvedProduct->buildData->nodes)) { + for (Artifact *artifact : filterByType<Artifact>(newlyResolvedProduct->buildData->nodes)) { if (!artifact->transformer) continue; if (m_logger.traceEnabled()) { @@ -847,8 +845,7 @@ void BuildGraphLoader::rescueOldBuildData(const ResolvedProductConstPtr &restore } // This is needed for artifacts created by rules, which happens later in the executor. - foreach (Artifact * const oldArtifact, - ArtifactSet::fromNodeSet(restoredProduct->buildData->nodes)) { + for (Artifact * const oldArtifact : filterByType<Artifact>(restoredProduct->buildData->nodes)) { if (!oldArtifact->transformer) continue; if (oldArtifactsCreatedByTransformers.contains(oldArtifact)) @@ -858,6 +855,12 @@ void BuildGraphLoader::rescueOldBuildData(const ResolvedProductConstPtr &restore RescuableArtifactData rad; rad.timeStamp = oldArtifact->timestamp(); rad.commands = oldArtifact->transformer->commands; + rad.propertiesRequestedInPrepareScript + = oldArtifact->transformer->propertiesRequestedInPrepareScript; + rad.propertiesRequestedInCommands + = oldArtifact->transformer->propertiesRequestedInCommands; + rad.propertiesRequestedFromArtifactInPrepareScript + = oldArtifact->transformer->propertiesRequestedFromArtifactInPrepareScript; const ChildrenInfo &childrenInfo = childLists.value(oldArtifact); foreach (Artifact * const child, childrenInfo.children) { rad.children << RescuableArtifactData::ChildData(child->product->name, diff --git a/src/lib/corelib/buildgraph/command.cpp b/src/lib/corelib/buildgraph/command.cpp index 210072ef4..8a97faa70 100644 --- a/src/lib/corelib/buildgraph/command.cpp +++ b/src/lib/corelib/buildgraph/command.cpp @@ -46,6 +46,7 @@ AbstractCommand::AbstractCommand() : m_description(defaultDescription()), m_extendedDescription(defaultExtendedDescription()), m_highlight(defaultHighLight()), + m_ignoreDryRun(defaultIgnoreDryRun()), m_silent(defaultIsSilent()) { } @@ -60,6 +61,7 @@ bool AbstractCommand::equals(const AbstractCommand *other) const && m_description == other->m_description && m_extendedDescription == other->m_extendedDescription && m_highlight == other->m_highlight + && m_ignoreDryRun == other->m_ignoreDryRun && m_silent == other->m_silent && m_properties == other->m_properties; } @@ -69,6 +71,7 @@ void AbstractCommand::fillFromScriptValue(const QScriptValue *scriptValue, const m_description = scriptValue->property(QLatin1String("description")).toString(); m_extendedDescription = scriptValue->property(QLatin1String("extendedDescription")).toString(); m_highlight = scriptValue->property(QLatin1String("highlight")).toString(); + m_ignoreDryRun = scriptValue->property(QLatin1String("ignoreDryRun")).toBool(); m_silent = scriptValue->property(QLatin1String("silent")).toBool(); m_codeLocation = codeLocation; @@ -76,6 +79,7 @@ void AbstractCommand::fillFromScriptValue(const QScriptValue *scriptValue, const << QLatin1String("description") << QLatin1String("extendedDescription") << QLatin1String("highlight") + << QLatin1String("ignoreDryRun") << QLatin1String("silent"); } @@ -84,6 +88,7 @@ void AbstractCommand::load(PersistentPool &pool) m_description = pool.idLoadString(); m_extendedDescription = pool.idLoadString(); m_highlight = pool.idLoadString(); + pool.stream() >> m_ignoreDryRun; pool.stream() >> m_silent; m_codeLocation.load(pool); m_properties = pool.loadVariantMap(); @@ -94,6 +99,7 @@ void AbstractCommand::store(PersistentPool &pool) const pool.storeString(m_description); pool.storeString(m_extendedDescription); pool.storeString(m_highlight); + pool.stream() << m_ignoreDryRun; pool.stream() << m_silent; m_codeLocation.store(pool); pool.store(m_properties); @@ -120,6 +126,8 @@ static QScriptValue js_CommandBase(QScriptContext *context, QScriptEngine *engin engine->toScriptValue(AbstractCommand::defaultExtendedDescription())); cmd.setProperty(QLatin1String("highlight"), engine->toScriptValue(AbstractCommand::defaultHighLight())); + cmd.setProperty(QLatin1String("ignoreDryRun"), + engine->toScriptValue(AbstractCommand::defaultIgnoreDryRun())); cmd.setProperty(QLatin1String("silent"), engine->toScriptValue(AbstractCommand::defaultIsSilent())); return cmd; @@ -163,6 +171,8 @@ static QScriptValue js_Command(QScriptContext *context, QScriptEngine *engine) engine->toScriptValue(commandPrototype->stderrFilePath())); cmd.setProperty(QLatin1String("environment"), engine->toScriptValue(commandPrototype->environment().toStringList())); + cmd.setProperty(QLatin1String("ignoreDryRun"), + engine->toScriptValue(commandPrototype->ignoreDryRun())); return cmd; } diff --git a/src/lib/corelib/buildgraph/command.h b/src/lib/corelib/buildgraph/command.h index 53a98d124..f3a9f2483 100644 --- a/src/lib/corelib/buildgraph/command.h +++ b/src/lib/corelib/buildgraph/command.h @@ -58,6 +58,7 @@ public: static QString defaultDescription() { return QString(); } static QString defaultExtendedDescription() { return QString(); } static QString defaultHighLight() { return QString(); } + static bool defaultIgnoreDryRun() { return false; } static bool defaultIsSilent() { return false; } virtual CommandType type() const = 0; @@ -67,6 +68,7 @@ public: const QString description() const { return m_description; } const QString extendedDescription() const { return m_extendedDescription; } const QString highlight() const { return m_highlight; } + bool ignoreDryRun() const { return m_ignoreDryRun; } bool isSilent() const { return m_silent; } CodeLocation codeLocation() const { return m_codeLocation; } @@ -86,6 +88,7 @@ private: QString m_description; QString m_extendedDescription; QString m_highlight; + bool m_ignoreDryRun; bool m_silent; CodeLocation m_codeLocation; QVariantMap m_properties; diff --git a/src/lib/corelib/buildgraph/depscanner.cpp b/src/lib/corelib/buildgraph/depscanner.cpp index a0bf1d6ed..04f5f6323 100644 --- a/src/lib/corelib/buildgraph/depscanner.cpp +++ b/src/lib/corelib/buildgraph/depscanner.cpp @@ -131,6 +131,7 @@ UserDependencyScanner::UserDependencyScanner(const ResolvedScannerConstPtr &scan { m_engine->setProcessEventsInterval(-1); // QBS-782 m_global = m_engine->newObject(); + m_global.setPrototype(m_engine->globalObject()); setupScriptEngineForFile(m_engine, m_scanner->scanScript->fileContext, m_global); } @@ -184,8 +185,7 @@ QStringList UserDependencyScanner::evaluate(Artifact *artifact, const ScriptFunc args.append(m_global.property(QString::fromLatin1("product"))); args.append(artifactConfig); - QScriptContext *ctx = m_engine->currentContext(); - ctx->pushScope(m_global); + m_engine->setGlobalObject(m_global); QScriptValue &function = script->scriptFunction; if (!function.isValid() || function.engine() != m_engine) { function = m_engine->evaluate(script->sourceCode); @@ -193,7 +193,7 @@ QStringList UserDependencyScanner::evaluate(Artifact *artifact, const ScriptFunc throw ErrorInfo(Tr::tr("Invalid scan script."), script->location); } QScriptValue result = function.call(QScriptValue(), args); - ctx->popScope(); + m_engine->setGlobalObject(m_global.prototype()); m_engine->clearRequestedProperties(); if (Q_UNLIKELY(m_engine->hasErrorOrException(result))) { QString msg = Tr::tr("evaluating scan script: ") + m_engine->lastErrorString(result); diff --git a/src/lib/corelib/buildgraph/executor.cpp b/src/lib/corelib/buildgraph/executor.cpp index b9d4cc330..dbb5225b3 100644 --- a/src/lib/corelib/buildgraph/executor.cpp +++ b/src/lib/corelib/buildgraph/executor.cpp @@ -82,7 +82,7 @@ Executor::Executor(const Logger &logger, QObject *parent) m_inputArtifactScanContext = new InputArtifactScannerContext(&m_scanResultCache); m_cancelationTimer->setSingleShot(false); m_cancelationTimer->setInterval(1000); - connect(m_cancelationTimer, SIGNAL(timeout()), SLOT(checkForCancellation())); + connect(m_cancelationTimer, &QTimer::timeout, this, &Executor::checkForCancellation); } Executor::~Executor() @@ -207,7 +207,7 @@ void Executor::doBuild() if (m_productsToBuild.isEmpty()) { m_logger.qbsTrace() << "No products to build, finishing."; - QTimer::singleShot(0, this, SLOT(finish())); // Don't call back on the caller. + QTimer::singleShot(0, this, &Executor::finish); // Don't call back on the caller. return; } @@ -236,7 +236,7 @@ void Executor::doBuild() initLeaves(); if (!scheduleJobs()) { m_logger.qbsTrace() << "Nothing to do at all, finishing."; - QTimer::singleShot(0, this, SLOT(finish())); // Don't call back on the caller. + QTimer::singleShot(0, this, &Executor::finish); // Don't call back on the caller. } if (m_progressObserver) m_cancelationTimer->start(); @@ -347,7 +347,7 @@ bool Executor::isUpToDate(Artifact *artifact) const return false; } - foreach (Artifact *childArtifact, ArtifactSet::fromNodeSet(artifact->children)) { + for (Artifact *childArtifact : filterByType<Artifact>(artifact->children)) { QBS_CHECK(childArtifact->timestamp().isValid()); if (m_doDebug) { m_logger.qbsDebug() << "[UTD] child timestamp " @@ -445,8 +445,7 @@ void Executor::executeRuleNode(RuleNode *ruleNode) if (ruleNode->rule()->acceptsAsInput(artifact)) changedInputArtifacts += artifact; } - foreach (Artifact *artifact, - ArtifactSet::fromNodeSet(ruleNode->product->buildData->nodes)) { + for (Artifact *artifact : filterByType<Artifact>(ruleNode->product->buildData->nodes)) { if (artifact->artifactType == Artifact::SourceFile) continue; if (artifact->timestampRetrieved && !isUpToDate(artifact) @@ -513,7 +512,8 @@ void Executor::finishJob(ExecutorJob *job, bool success) foreach (Artifact *artifact, transformer->outputs) { if (artifact->alwaysUpdated) { artifact->setTimestamp(FileTime::currentTime()); - if (m_buildOptions.forceOutputCheck() && !FileInfo(artifact->filePath()).exists()) { + if (m_buildOptions.forceOutputCheck() + && !m_buildOptions.dryRun() && !FileInfo(artifact->filePath()).exists()) { if (transformer->rule) { if (!transformer->rule->name.isEmpty()) { throw ErrorInfo(tr("Rule '%1' declares artifact '%2', " @@ -691,12 +691,12 @@ void Executor::addExecutorJobs() job->setDryRun(m_buildOptions.dryRun()); job->setEchoMode(m_buildOptions.echoMode()); m_availableJobs.append(job); - connect(job, SIGNAL(reportCommandDescription(QString,QString)), - this, SIGNAL(reportCommandDescription(QString,QString)), Qt::QueuedConnection); - connect(job, SIGNAL(reportProcessResult(qbs::ProcessResult)), - this, SIGNAL(reportProcessResult(qbs::ProcessResult)), Qt::QueuedConnection); - connect(job, SIGNAL(finished(qbs::ErrorInfo)), - this, SLOT(onJobFinished(qbs::ErrorInfo)), Qt::QueuedConnection); + connect(job, &ExecutorJob::reportCommandDescription, + this, &Executor::reportCommandDescription, Qt::QueuedConnection); + connect(job, &ExecutorJob::reportProcessResult, + this, &Executor::reportProcessResult, Qt::QueuedConnection); + connect(job, &ExecutorJob::finished, + this, &Executor::onJobFinished, Qt::QueuedConnection); } } @@ -749,8 +749,9 @@ void Executor::rescueOldBuildData(Artifact *artifact, bool *childrenAdded = 0) } if (canRescue) { + const TypeFilter<Artifact> childArtifacts(artifact->children); const int newChildCount = childrenToConnect.count() - + ArtifactSet::fromNodeSet(artifact->children).count(); + + std::distance(childArtifacts.begin(), childArtifacts.end()); QBS_CHECK(newChildCount >= rad.children.count()); if (newChildCount > rad.children.count()) { canRescue = false; @@ -764,6 +765,12 @@ void Executor::rescueOldBuildData(Artifact *artifact, bool *childrenAdded = 0) } if (canRescue) { + artifact->transformer->propertiesRequestedInPrepareScript + = rad.propertiesRequestedInPrepareScript; + artifact->transformer->propertiesRequestedInCommands + = rad.propertiesRequestedInCommands; + artifact->transformer->propertiesRequestedFromArtifactInPrepareScript + = rad.propertiesRequestedFromArtifactInPrepareScript; artifact->setTimestamp(rad.timeStamp); if (childrenAdded && !childrenToConnect.isEmpty()) *childrenAdded = true; @@ -918,8 +925,7 @@ void Executor::onJobFinished(const qbs::ErrorInfo &err) if (m_evalContext->isActive()) { m_logger.qbsDebug() << "Executor job finished while rule execution is pausing. " "Delaying slot execution."; - QMetaObject::invokeMethod(job, "finished", Qt::QueuedConnection, - Q_ARG(qbs::ErrorInfo, err)); + QTimer::singleShot(0, job, [job, err] { job->finished(err); }); return; } @@ -1057,7 +1063,7 @@ void Executor::prepareAllNodes() } foreach (const ResolvedProductPtr &product, m_productsToBuild) { QBS_CHECK(product->buildData); - foreach (Artifact * const artifact, ArtifactSet::fromNodeSet(product->buildData->nodes)) + for (Artifact * const artifact : filterByType<Artifact>(product->buildData->nodes)) prepareArtifact(artifact); } } diff --git a/src/lib/corelib/buildgraph/executor.h b/src/lib/corelib/buildgraph/executor.h index b3b925475..718098367 100644 --- a/src/lib/corelib/buildgraph/executor.h +++ b/src/lib/corelib/buildgraph/executor.h @@ -63,10 +63,9 @@ class Executor : public QObject, private BuildGraphVisitor { Q_OBJECT -public slots: +public: void build(); -public: Executor(const Logger &logger, QObject *parent = 0); ~Executor(); @@ -83,12 +82,11 @@ signals: void finished(); -private slots: +private: void onJobFinished(const qbs::ErrorInfo &err); void finish(); void checkForCancellation(); -private: // BuildGraphVisitor implementation bool visit(Artifact *artifact); bool visit(RuleNode *ruleNode); diff --git a/src/lib/corelib/buildgraph/executorjob.cpp b/src/lib/corelib/buildgraph/executorjob.cpp index 15b952373..c699c3853 100644 --- a/src/lib/corelib/buildgraph/executorjob.cpp +++ b/src/lib/corelib/buildgraph/executorjob.cpp @@ -49,16 +49,16 @@ ExecutorJob::ExecutorJob(const Logger &logger, QObject *parent) , m_processCommandExecutor(new ProcessCommandExecutor(logger, this)) , m_jsCommandExecutor(new JsCommandExecutor(logger, this)) { - connect(m_processCommandExecutor, SIGNAL(reportCommandDescription(QString,QString)), - this, SIGNAL(reportCommandDescription(QString,QString))); - connect(m_processCommandExecutor, SIGNAL(reportProcessResult(qbs::ProcessResult)), - this, SIGNAL(reportProcessResult(qbs::ProcessResult))); - connect(m_processCommandExecutor, SIGNAL(finished(qbs::ErrorInfo)), - this, SLOT(onCommandFinished(qbs::ErrorInfo))); - connect(m_jsCommandExecutor, SIGNAL(reportCommandDescription(QString,QString)), - this, SIGNAL(reportCommandDescription(QString,QString))); - connect(m_jsCommandExecutor, SIGNAL(finished(qbs::ErrorInfo)), - this, SLOT(onCommandFinished(qbs::ErrorInfo))); + connect(m_processCommandExecutor, &AbstractCommandExecutor::reportCommandDescription, + this, &ExecutorJob::reportCommandDescription); + connect(m_processCommandExecutor, &ProcessCommandExecutor::reportProcessResult, + this, &ExecutorJob::reportProcessResult); + connect(m_processCommandExecutor, &AbstractCommandExecutor::finished, + this, &ExecutorJob::onCommandFinished); + connect(m_jsCommandExecutor, &AbstractCommandExecutor::reportCommandDescription, + this, &ExecutorJob::reportCommandDescription); + connect(m_jsCommandExecutor, &AbstractCommandExecutor::finished, + this, &ExecutorJob::onCommandFinished); reset(); } diff --git a/src/lib/corelib/buildgraph/executorjob.h b/src/lib/corelib/buildgraph/executorjob.h index ea0831fee..f1f2b28ac 100644 --- a/src/lib/corelib/buildgraph/executorjob.h +++ b/src/lib/corelib/buildgraph/executorjob.h @@ -68,11 +68,10 @@ signals: void reportProcessResult(const qbs::ProcessResult &result); void finished(const qbs::ErrorInfo &error = ErrorInfo()); // !hasError() <=> command successful -private slots: +private: void runNextCommand(); void onCommandFinished(const qbs::ErrorInfo &err); -private: void setFinished(); void reset(); diff --git a/src/lib/corelib/buildgraph/jscommandexecutor.cpp b/src/lib/corelib/buildgraph/jscommandexecutor.cpp index 08c3e8735..14b58cf27 100644 --- a/src/lib/corelib/buildgraph/jscommandexecutor.cpp +++ b/src/lib/corelib/buildgraph/jscommandexecutor.cpp @@ -44,7 +44,6 @@ #include <tools/qbsassert.h> #include <QEventLoop> -#include <QMetaObject> #include <QThread> #include <QTimer> @@ -73,7 +72,7 @@ public: return m_result; } - Q_INVOKABLE void cancel() + void cancel() { QBS_ASSERT(m_scriptEngine, return); m_scriptEngine->abortEvaluation(); @@ -82,7 +81,7 @@ public: signals: void finished(); -public slots: +public: void start(const JavaScriptCommand *cmd, Transformer *transformer) { try { @@ -101,6 +100,7 @@ private: m_result.errorMessage.clear(); ScriptEngine * const scriptEngine = provideScriptEngine(); QScriptValue scope = scriptEngine->newObject(); + scope.setPrototype(scriptEngine->globalObject()); PrepareScriptObserver observer(scriptEngine); setupScriptEngineForFile(scriptEngine, transformer->rule->prepareScript->fileContext, scope); setupScriptEngineForProduct(scriptEngine, transformer->product(), transformer->rule->module, scope, @@ -113,10 +113,9 @@ private: scope.setProperty(it.key(), scriptEngine->toScriptValue(it.value())); } - QScriptContext *ctx = scriptEngine->currentContext(); - ctx->pushScope(scope); + scriptEngine->setGlobalObject(scope); scriptEngine->evaluate(cmd->sourceCode()); - ctx->popScope(); + scriptEngine->setGlobalObject(scope.prototype()); transformer->propertiesRequestedInCommands += scriptEngine->propertiesRequestedInScript(); scriptEngine->clearRequestedProperties(); @@ -153,9 +152,10 @@ JsCommandExecutor::JsCommandExecutor(const Logger &logger, QObject *parent) , m_running(false) { m_objectInThread->moveToThread(m_thread); - connect(m_objectInThread, SIGNAL(finished()), this, SLOT(onJavaScriptCommandFinished())); - connect(this, SIGNAL(startRequested(const JavaScriptCommand*,Transformer*)), - m_objectInThread, SLOT(start(const JavaScriptCommand*,Transformer*))); + connect(m_objectInThread, &JsCommandExecutorThreadObject::finished, + this, &JsCommandExecutor::onJavaScriptCommandFinished); + connect(this, &JsCommandExecutor::startRequested, + m_objectInThread, &JsCommandExecutorThreadObject::start); } JsCommandExecutor::~JsCommandExecutor() @@ -168,7 +168,9 @@ JsCommandExecutor::~JsCommandExecutor() void JsCommandExecutor::doReportCommandDescription() { - if (m_echoMode == CommandEchoModeCommandLine && !command()->extendedDescription().isEmpty()) { + if ((m_echoMode == CommandEchoModeCommandLine + || m_echoMode == CommandEchoModeCommandLineWithEnvironment) + && !command()->extendedDescription().isEmpty()) { emit reportCommandDescription(command()->highlight(), command()->extendedDescription()); return; } @@ -181,7 +183,7 @@ void JsCommandExecutor::waitForFinished() if (!m_running) return; QEventLoop loop; - loop.connect(m_objectInThread, SIGNAL(finished()), SLOT(quit())); + connect(m_objectInThread, &JsCommandExecutorThreadObject::finished, &loop, &QEventLoop::quit); loop.exec(); } @@ -190,8 +192,8 @@ void JsCommandExecutor::doStart() QBS_ASSERT(!m_running, return); m_thread->start(); - if (dryRun()) { - QTimer::singleShot(0, this, SIGNAL(finished())); // Don't call back on the caller. + if (dryRun() && !command()->ignoreDryRun()) { + QTimer::singleShot(0, this, [this] { emit finished(); }); // Don't call back on the caller. return; } @@ -202,7 +204,7 @@ void JsCommandExecutor::doStart() void JsCommandExecutor::cancel() { if (!dryRun()) - QMetaObject::invokeMethod(m_objectInThread, "cancel", Qt::QueuedConnection); + QTimer::singleShot(0, m_objectInThread, [this] { m_objectInThread->cancel(); }); } void JsCommandExecutor::onJavaScriptCommandFinished() diff --git a/src/lib/corelib/buildgraph/jscommandexecutor.h b/src/lib/corelib/buildgraph/jscommandexecutor.h index 82084c5cd..51f239b6b 100644 --- a/src/lib/corelib/buildgraph/jscommandexecutor.h +++ b/src/lib/corelib/buildgraph/jscommandexecutor.h @@ -52,10 +52,9 @@ public: signals: void startRequested(const JavaScriptCommand *cmd, Transformer *transformer); -private slots: +private: void onJavaScriptCommandFinished(); -private: void doReportCommandDescription(); void doStart(); void cancel(); diff --git a/src/lib/corelib/buildgraph/nodeset.h b/src/lib/corelib/buildgraph/nodeset.h index eb79b49f1..9b56567da 100644 --- a/src/lib/corelib/buildgraph/nodeset.h +++ b/src/lib/corelib/buildgraph/nodeset.h @@ -125,6 +125,70 @@ private: QSharedDataPointer<NodeSetData> d; }; +template <class T> +class TypeFilter +{ + const NodeSet &m_nodes; +public: + TypeFilter(const NodeSet &nodes) + : m_nodes(nodes) + { + } + + class const_iterator : public std::iterator<std::forward_iterator_tag, T *> + { + const NodeSet &m_nodes; + NodeSet::const_iterator m_it; + public: + const_iterator(const NodeSet &nodes, const NodeSet::const_iterator &it) + : m_nodes(nodes), m_it(it) + { + while (m_it != m_nodes.constEnd() && dynamic_cast<T *>(*m_it) == 0) + ++m_it; + } + + bool operator==(const const_iterator &rhs) + { + return m_it == rhs.m_it; + } + + bool operator!=(const const_iterator &rhs) + { + return !(*this == rhs); + } + + const_iterator &operator++() + { + for (;;) { + ++m_it; + if (m_it == m_nodes.constEnd() || dynamic_cast<T *>(*m_it)) + return *this; + } + } + + T *operator*() const + { + return static_cast<T *>(*m_it); + } + }; + + const_iterator begin() const + { + return const_iterator(m_nodes, m_nodes.constBegin()); + } + + const_iterator end() const + { + return const_iterator(m_nodes, m_nodes.constEnd()); + } +}; + +template <class T> +const TypeFilter<T> filterByType(const NodeSet &nodes) +{ + return TypeFilter<T>(nodes); +} + } // namespace Internal } // namespace qbs diff --git a/src/lib/corelib/buildgraph/processcommandexecutor.cpp b/src/lib/corelib/buildgraph/processcommandexecutor.cpp index c109c792e..67170c0e9 100644 --- a/src/lib/corelib/buildgraph/processcommandexecutor.cpp +++ b/src/lib/corelib/buildgraph/processcommandexecutor.cpp @@ -61,8 +61,10 @@ namespace Internal { ProcessCommandExecutor::ProcessCommandExecutor(const Logger &logger, QObject *parent) : AbstractCommandExecutor(logger, parent) { - connect(&m_process, SIGNAL(error(QProcess::ProcessError)), SLOT(onProcessError())); - connect(&m_process, SIGNAL(finished(int)), SLOT(onProcessFinished())); + connect(&m_process, static_cast<void (QProcess::*)(QProcess::ProcessError)>(&QProcess::error), + this, &ProcessCommandExecutor::onProcessError); + connect(&m_process, static_cast<void (QProcess::*)(int)>(&QProcess::finished), + this, &ProcessCommandExecutor::onProcessFinished); } void ProcessCommandExecutor::doSetup() @@ -72,6 +74,12 @@ void ProcessCommandExecutor::doSetup() transformer()->product()->buildEnvironment, logger()) .findExecutable(cmd->program(), cmd->workingDir()); + QProcessEnvironment env = m_buildEnvironment; + const QProcessEnvironment &additionalVariables = cmd->environment(); + foreach (const QString &key, additionalVariables.keys()) + env.insert(key, additionalVariables.value(key)); + m_commandEnvironment = env; + m_program = program; m_arguments = cmd->arguments(); m_shellInvocation = shellQuote(QDir::toNativeSeparators(m_program), m_arguments); @@ -83,16 +91,12 @@ void ProcessCommandExecutor::doStart() const ProcessCommand * const cmd = processCommand(); - QProcessEnvironment env = m_buildEnvironment; - const QProcessEnvironment &additionalVariables = cmd->environment(); - foreach (const QString &key, additionalVariables.keys()) - env.insert(key, additionalVariables.value(key)); - m_process.setProcessEnvironment(env); + m_process.setProcessEnvironment(m_commandEnvironment); QStringList arguments = m_arguments; - if (dryRun()) { - QTimer::singleShot(0, this, SIGNAL(finished())); // Don't call back on the caller. + if (dryRun() && !cmd->ignoreDryRun()) { + QTimer::singleShot(0, this, [this] { emit finished(); }); // Don't call back on the caller. return; } @@ -144,6 +148,7 @@ void ProcessCommandExecutor::doStart() logger().qbsDebug() << "[EXEC] Running external process; full command line is: " << m_shellInvocation; + const QProcessEnvironment &additionalVariables = cmd->environment(); logger().qbsTrace() << "[EXEC] Additional environment:" << additionalVariables.toStringList(); m_process.setWorkingDirectory(workingDir); m_process.start(m_program, arguments); @@ -152,7 +157,7 @@ void ProcessCommandExecutor::doStart() void ProcessCommandExecutor::cancel() { // We don't want this command to be reported as failing, since we explicitly terminated it. - disconnect(this, SIGNAL(reportProcessResult(qbs::ProcessResult)), 0, 0); + disconnect(this, &ProcessCommandExecutor::reportProcessResult, 0, 0); m_process.terminate(); if (!m_process.waitForFinished(1000)) @@ -167,14 +172,13 @@ QString ProcessCommandExecutor::filterProcessOutput(const QByteArray &_output, return output; QScriptValue scope = scriptEngine()->newObject(); + scope.setPrototype(scriptEngine()->globalObject()); for (QVariantMap::const_iterator it = command()->properties().constBegin(); it != command()->properties().constEnd(); ++it) { scope.setProperty(it.key(), scriptEngine()->toScriptValue(it.value())); } - ScriptContextScopePusher scopePusher(scriptEngine()->currentContext(), scope); - Q_UNUSED(scopePusher); - + TemporaryGlobalObjectSetter tgos(scope); QScriptValue filterFunction = scriptEngine()->evaluate(QLatin1String("var f = ") + filterFunctionSource + QLatin1String("; f")); @@ -304,13 +308,32 @@ void ProcessCommandExecutor::onProcessFinished() sendProcessOutput(); } +static QString environmentVariableString(const QString &key, const QString &value) +{ + QString str; + if (HostOsInfo::isAnyUnixHost()) + str += QStringLiteral("export "); + if (HostOsInfo::isWindowsHost()) + str += QStringLiteral("set "); + return str + shellQuote(key + QLatin1Char('=') + value) + QLatin1Char('\n'); +} + void ProcessCommandExecutor::doReportCommandDescription() { - if (m_echoMode == CommandEchoModeCommandLine) { + if (m_echoMode == CommandEchoModeCommandLine || + m_echoMode == CommandEchoModeCommandLineWithEnvironment) { + QString fullInvocation; + if (m_echoMode == CommandEchoModeCommandLineWithEnvironment) { + QStringList keys = m_commandEnvironment.keys(); + keys.sort(); + for (const QString &key : keys) + fullInvocation += environmentVariableString(key, m_commandEnvironment.value(key)); + } + fullInvocation += m_shellInvocation; emit reportCommandDescription(command()->highlight(), !command()->extendedDescription().isEmpty() ? command()->extendedDescription() - : m_shellInvocation); + : fullInvocation); return; } diff --git a/src/lib/corelib/buildgraph/processcommandexecutor.h b/src/lib/corelib/buildgraph/processcommandexecutor.h index aaeb5a20b..660d38a38 100644 --- a/src/lib/corelib/buildgraph/processcommandexecutor.h +++ b/src/lib/corelib/buildgraph/processcommandexecutor.h @@ -56,11 +56,10 @@ public: signals: void reportProcessResult(const qbs::ProcessResult &result); -private slots: +private: void onProcessError(); void onProcessFinished(); -private: void doSetup(); void doReportCommandDescription(); void doStart(); @@ -81,6 +80,7 @@ private: QProcess m_process; QProcessEnvironment m_buildEnvironment; + QProcessEnvironment m_commandEnvironment; QString m_responseFileName; }; diff --git a/src/lib/corelib/buildgraph/productbuilddata.cpp b/src/lib/corelib/buildgraph/productbuilddata.cpp index 5cfcb09dd..36e887b62 100644 --- a/src/lib/corelib/buildgraph/productbuilddata.cpp +++ b/src/lib/corelib/buildgraph/productbuilddata.cpp @@ -46,9 +46,9 @@ ProductBuildData::~ProductBuildData() qDeleteAll(nodes); } -ArtifactSet ProductBuildData::rootArtifacts() const +const TypeFilter<Artifact> ProductBuildData::rootArtifacts() const { - return ArtifactSet::fromNodeSet(roots); + return TypeFilter<Artifact>(roots); } static void loadArtifactSetByFileTag(PersistentPool &pool, diff --git a/src/lib/corelib/buildgraph/productbuilddata.h b/src/lib/corelib/buildgraph/productbuilddata.h index ee2f05359..09401acce 100644 --- a/src/lib/corelib/buildgraph/productbuilddata.h +++ b/src/lib/corelib/buildgraph/productbuilddata.h @@ -50,7 +50,7 @@ class ProductBuildData : public PersistentObject public: ~ProductBuildData(); - ArtifactSet rootArtifacts() const; + const TypeFilter<Artifact> rootArtifacts() const; NodeSet nodes; NodeSet roots; diff --git a/src/lib/corelib/buildgraph/productinstaller.cpp b/src/lib/corelib/buildgraph/productinstaller.cpp index d462943bd..5ecb0d3de 100644 --- a/src/lib/corelib/buildgraph/productinstaller.cpp +++ b/src/lib/corelib/buildgraph/productinstaller.cpp @@ -88,7 +88,7 @@ void ProductInstaller::install() QList<const Artifact *> artifactsToInstall; foreach (const ResolvedProductConstPtr &product, m_products) { QBS_CHECK(product->buildData); - foreach (const Artifact *artifact, ArtifactSet::fromNodeSet(product->buildData->nodes)) { + for (const Artifact *artifact : filterByType<Artifact>(product->buildData->nodes)) { if (artifact->properties->qbsPropertyValue(QLatin1String("install")).toBool()) artifactsToInstall += artifact; } diff --git a/src/lib/corelib/buildgraph/projectbuilddata.cpp b/src/lib/corelib/buildgraph/projectbuilddata.cpp index 002ba1729..4482ae9a7 100644 --- a/src/lib/corelib/buildgraph/projectbuilddata.cpp +++ b/src/lib/corelib/buildgraph/projectbuilddata.cpp @@ -156,12 +156,36 @@ QString ProjectBuildData::deriveBuildGraphFilePath(const QString &buildDir, cons return buildDir + QLatin1Char('/') + projectId + QLatin1String(".bg"); } +static QString productNameForErrorMessage(const ResolvedProduct *product) +{ + return product->profile == product->topLevelProject()->profile() + ? product->name : product->uniqueName(); +} + void ProjectBuildData::insertIntoLookupTable(FileResourceBase *fileres) { QList<FileResourceBase *> &lst = m_artifactLookupTable[fileres->fileName()][fileres->dirPath()]; - if (!lst.contains(fileres)) - lst.append(fileres); + const auto * const artifact = dynamic_cast<Artifact *>(fileres); + if (artifact && artifact->artifactType == Artifact::Generated) { + foreach (const auto *file, lst) { + const auto * const otherArtifact = dynamic_cast<const Artifact *>(file); + if (otherArtifact) { + ErrorInfo error; + error.append(Tr::tr("Conflicting artifacts for file path '%1'.") + .arg(artifact->filePath())); + error.append(Tr::tr("The first artifact comes from product '%1'.") + .arg(productNameForErrorMessage(otherArtifact->product.data())), + otherArtifact->product->location); + error.append(Tr::tr("The second artifact comes from product '%1'.") + .arg(productNameForErrorMessage(artifact->product.data())), + otherArtifact->product->location); + throw error; + } + } + } + QBS_CHECK(!lst.contains(fileres)); + lst.append(fileres); } void ProjectBuildData::removeFromLookupTable(FileResourceBase *fileres) @@ -243,7 +267,10 @@ void ProjectBuildData::removeArtifactAndExclusiveDependents(Artifact *artifact, if (removedArtifacts) removedArtifacts->insert(artifact); - foreach (Artifact *parent, ArtifactSet::fromNodeSet(artifact->parents)) { + // Iterate over a copy of the artifact's parents, because we'll change + // artifact->parents with the disconnect call. + const NodeSet parentsCopy = artifact->parents; + for (Artifact *parent : filterByType<Artifact>(parentsCopy)) { bool removeParent = false; disconnect(parent, artifact, logger); if (parent->children.isEmpty()) { diff --git a/src/lib/corelib/buildgraph/rescuableartifactdata.cpp b/src/lib/corelib/buildgraph/rescuableartifactdata.cpp index 85cc9a08a..464ea1135 100644 --- a/src/lib/corelib/buildgraph/rescuableartifactdata.cpp +++ b/src/lib/corelib/buildgraph/rescuableartifactdata.cpp @@ -56,6 +56,9 @@ void RescuableArtifactData::load(PersistentPool &pool) children << cd; } + propertiesRequestedInPrepareScript = restorePropertySet(pool); + propertiesRequestedInCommands = restorePropertySet(pool); + propertiesRequestedFromArtifactInPrepareScript = restorePropertyHash(pool); commands = loadCommandList(pool); } @@ -71,6 +74,9 @@ void RescuableArtifactData::store(PersistentPool &pool) const pool.stream() << cd.addedByScanner; } + storePropertySet(pool, propertiesRequestedInPrepareScript); + storePropertySet(pool, propertiesRequestedInCommands); + storePropertyHash(pool, propertiesRequestedFromArtifactInPrepareScript); storeCommandList(commands, pool); } diff --git a/src/lib/corelib/buildgraph/rescuableartifactdata.h b/src/lib/corelib/buildgraph/rescuableartifactdata.h index 96998f77c..b04f5d073 100644 --- a/src/lib/corelib/buildgraph/rescuableartifactdata.h +++ b/src/lib/corelib/buildgraph/rescuableartifactdata.h @@ -33,6 +33,7 @@ #include "forward_decls.h" +#include <language/property.h> #include <tools/filetime.h> #include <QHash> @@ -64,7 +65,13 @@ public: FileTime timeStamp; QList<ChildData> children; + + // Per-Transformer data QList<AbstractCommandPtr> commands; + PropertySet propertiesRequestedInPrepareScript; + PropertySet propertiesRequestedInCommands; + PropertyHash propertiesRequestedFromArtifactInPrepareScript; + }; typedef QHash<QString, RescuableArtifactData> AllRescuableArtifactData; diff --git a/src/lib/corelib/buildgraph/rulenode.cpp b/src/lib/corelib/buildgraph/rulenode.cpp index 3a6940f88..15abb9e67 100644 --- a/src/lib/corelib/buildgraph/rulenode.cpp +++ b/src/lib/corelib/buildgraph/rulenode.cpp @@ -73,7 +73,8 @@ void RuleNode::apply(const Logger &logger, const ArtifactSet &changedInputs, ArtifactSet allCompatibleInputs = currentInputArtifacts(); const ArtifactSet addedInputs = allCompatibleInputs - m_oldInputArtifacts; const ArtifactSet removedInputs = m_oldInputArtifacts - allCompatibleInputs; - result->upToDate = changedInputs.isEmpty() && addedInputs.isEmpty() && removedInputs.isEmpty(); + result->upToDate = changedInputs.isEmpty() && addedInputs.isEmpty() && removedInputs.isEmpty() + && m_rule->requiresInputs(); if (logger.traceEnabled()) { logger.qbsTrace() @@ -105,7 +106,7 @@ void RuleNode::apply(const Logger &logger, const ArtifactSet &changedInputs, if (!removedInputs.isEmpty()) { ArtifactSet outputArtifactsToRemove; foreach (Artifact *artifact, removedInputs) { - foreach (Artifact *parent, ArtifactSet::fromNodeSet(artifact->parents)) { + for (Artifact *parent : filterByType<Artifact>(artifact->parents)) { if (parent->transformer->rule != m_rule) { // parent was not created by our rule. continue; @@ -123,7 +124,7 @@ void RuleNode::apply(const Logger &logger, const ArtifactSet &changedInputs, } RulesApplicator::handleRemovedRuleOutputs(inputs, outputArtifactsToRemove, logger); } - if (!inputs.isEmpty()) { + if (!inputs.isEmpty() || !m_rule->requiresInputs()) { RulesApplicator applicator(product, logger); applicator.applyRule(m_rule, inputs); result->createdNodes = applicator.createdArtifacts(); @@ -165,7 +166,7 @@ ArtifactSet RuleNode::currentInputArtifacts() const continue; if (m_rule->inputsFromDependencies.isEmpty()) continue; - foreach (Artifact * const a, ArtifactSet::fromNodeSet(dep->buildData->nodes)) { + for (Artifact * const a : filterByType<Artifact>(dep->buildData->nodes)) { if (a->fileTags().matches(m_rule->inputsFromDependencies)) s += a; } diff --git a/src/lib/corelib/buildgraph/rulesapplicator.cpp b/src/lib/corelib/buildgraph/rulesapplicator.cpp index aaab572a0..bebbe3ada 100644 --- a/src/lib/corelib/buildgraph/rulesapplicator.cpp +++ b/src/lib/corelib/buildgraph/rulesapplicator.cpp @@ -53,6 +53,7 @@ #include <QDir> #include <QQueue> +#include <QScopedPointer> #include <QScriptValueIterator> namespace qbs { @@ -72,7 +73,7 @@ RulesApplicator::~RulesApplicator() void RulesApplicator::applyRule(const RuleConstPtr &rule, const ArtifactSet &inputArtifacts) { - if (inputArtifacts.isEmpty()) + if (inputArtifacts.isEmpty() && rule->requiresInputs()) return; m_createdArtifacts.clear(); @@ -86,6 +87,7 @@ void RulesApplicator::applyRule(const RuleConstPtr &rule, const ArtifactSet &inp m_mocScanner = new QtMocScanner(m_product, scope(), m_logger); } QScriptValue prepareScriptContext = engine()->newObject(); + prepareScriptContext.setPrototype(engine()->globalObject()); PrepareScriptObserver observer(engine()); setupScriptEngineForFile(engine(), m_rule->prepareScript->fileContext, scope()); setupScriptEngineForProduct(engine(), m_product, m_rule->module, prepareScriptContext, &observer); @@ -118,7 +120,7 @@ void RulesApplicator::handleRemovedRuleOutputs(const ArtifactSet &inputArtifacts } // parents of removed artifacts must update their transformers foreach (Artifact *removedArtifact, artifactsToRemove) { - foreach (Artifact *parent, removedArtifact->parentArtifacts()) + for (Artifact *parent : removedArtifact->parentArtifacts()) parent->product->registerArtifactWithChangedInputs(parent); } EmptyDirectoriesRemover(project, logger).removeEmptyParentDirectories(artifactsToRemove); @@ -206,7 +208,7 @@ void RulesApplicator::doApply(const ArtifactSet &inputArtifacts, QScriptValue &p // change the transformer outputs according to the bindings in Artifact QScriptValue scriptValue; if (!ruleArtifactArtifactMap.isEmpty()) - engine()->currentContext()->pushScope(prepareScriptContext); + engine()->setGlobalObject(prepareScriptContext); for (int i = ruleArtifactArtifactMap.count(); --i >= 0;) { const RuleArtifact *ra = ruleArtifactArtifactMap.at(i).first; if (ra->bindings.isEmpty()) @@ -238,7 +240,7 @@ void RulesApplicator::doApply(const ArtifactSet &inputArtifacts, QScriptValue &p outputArtifact->properties->setValue(outputArtifactConfig); } if (!ruleArtifactArtifactMap.isEmpty()) - engine()->currentContext()->popScope(); + engine()->setGlobalObject(prepareScriptContext.prototype()); m_transformer->setupOutputs(engine(), prepareScriptContext); m_transformer->createCommands(m_rule->prepareScript, evalContext(), @@ -252,7 +254,7 @@ ArtifactSet RulesApplicator::collectOldOutputArtifacts(const ArtifactSet &inputA { ArtifactSet result; foreach (Artifact *a, inputArtifacts) { - foreach (Artifact *p, a->parentArtifacts()) { + for (Artifact *p : a->parentArtifacts()) { QBS_CHECK(p->transformer); if (p->transformer->rule == m_rule && p->transformer->inputs.contains(a)) result += p; @@ -294,7 +296,8 @@ Artifact *RulesApplicator::createOutputArtifact(const QString &filePath, const F Artifact *outputArtifact = lookupArtifact(m_product, outputPath); if (outputArtifact) { - if (outputArtifact->transformer && outputArtifact->transformer->rule != m_rule) { + const Transformer * const transformer = outputArtifact->transformer.data(); + if (transformer && transformer->rule != m_rule) { QString e = Tr::tr("Conflicting rules for producing %1 %2 \n") .arg(outputArtifact->filePath(), QLatin1Char('[') + @@ -311,21 +314,35 @@ Artifact *RulesApplicator::createOutputArtifact(const QString &filePath, const F .arg(str); e += QString::fromLatin1(" was already defined in: %1:%2:%3 %4\n") - .arg(outputArtifact->transformer->rule->prepareScript->location.filePath()) - .arg(outputArtifact->transformer->rule->prepareScript->location.line()) - .arg(outputArtifact->transformer->rule->prepareScript->location.column()) + .arg(transformer->rule->prepareScript->location.filePath()) + .arg(transformer->rule->prepareScript->location.line()) + .arg(transformer->rule->prepareScript->location.column()) .arg(str); throw ErrorInfo(e); } - outputArtifact->clearTimestamp(); + if (transformer && !m_rule->multiplex && transformer->inputs != inputArtifacts) { + QBS_CHECK(inputArtifacts.count() == 1); + QBS_CHECK(transformer->inputs.count() == 1); + ErrorInfo error(Tr::tr("Conflicting instances of rule '%1':").arg(m_rule->toString()), + m_rule->prepareScript->location); + error.append(Tr::tr("Output artifact '%1' is to be produced from input " + "artifacts '%2' and '%3', but the rule is not a multiplex rule.") + .arg(outputArtifact->filePath(), + (*transformer->inputs.begin())->filePath(), + (*inputArtifacts.begin())->filePath())); + throw error; + } + if (m_rule->requiresInputs()) + outputArtifact->clearTimestamp(); m_invalidatedArtifacts += outputArtifact; } else { - outputArtifact = new Artifact; - outputArtifact->artifactType = Artifact::Generated; - outputArtifact->setFilePath(outputPath); - insertArtifact(m_product, outputArtifact, m_logger); - m_createdArtifacts += outputArtifact; + QScopedPointer<Artifact> newArtifact(new Artifact); + newArtifact->artifactType = Artifact::Generated; + newArtifact->setFilePath(outputPath); + insertArtifact(m_product, newArtifact.data(), m_logger); + m_createdArtifacts += newArtifact.data(); + outputArtifact = newArtifact.take(); } outputArtifact->setFileTags( diff --git a/src/lib/corelib/buildgraph/rulesevaluationcontext.cpp b/src/lib/corelib/buildgraph/rulesevaluationcontext.cpp index e195527d4..762016e49 100644 --- a/src/lib/corelib/buildgraph/rulesevaluationcontext.cpp +++ b/src/lib/corelib/buildgraph/rulesevaluationcontext.cpp @@ -49,6 +49,7 @@ RulesEvaluationContext::RulesEvaluationContext(const Logger &logger) : m_engine(new ScriptEngine(logger)), m_observer(0), m_initScopeCalls(0) { m_prepareScriptScope = m_engine->newObject(); + m_prepareScriptScope.setPrototype(m_engine->globalObject()); ProcessCommand::setupForJavaScript(m_prepareScriptScope); JavaScriptCommand::setupForJavaScript(m_prepareScriptScope); } @@ -86,11 +87,9 @@ void RulesEvaluationContext::initScope() if (m_initScopeCalls++ > 0) return; - m_engine->clearImportsCache(); - m_engine->pushContext(); m_scope = m_engine->newObject(); m_scope.setPrototype(m_prepareScriptScope); - m_engine->currentContext()->pushScope(m_scope); + m_engine->setGlobalObject(m_scope); } void RulesEvaluationContext::cleanupScope() @@ -100,8 +99,7 @@ void RulesEvaluationContext::cleanupScope() return; m_scope = QScriptValue(); - m_engine->currentContext()->popScope(); - m_engine->popContext(); + m_engine->setGlobalObject(m_prepareScriptScope.prototype()); } RulesEvaluationContext::Scope::Scope(RulesEvaluationContext *evalContext) diff --git a/src/lib/corelib/buildgraph/transformer.cpp b/src/lib/corelib/buildgraph/transformer.cpp index d0b58765e..6dba0ac1c 100644 --- a/src/lib/corelib/buildgraph/transformer.cpp +++ b/src/lib/corelib/buildgraph/transformer.cpp @@ -247,81 +247,26 @@ void Transformer::createCommands(const ScriptFunctionConstPtr &script, } } -static void restorePropertyList(PersistentPool &pool, PropertySet &list) -{ - int count; - pool.stream() >> count; - list.reserve(count); - while (--count >= 0) { - Property p; - p.moduleName = pool.idLoadString(); - p.propertyName = pool.idLoadString(); - int k; - pool.stream() >> p.value >> k; - p.kind = static_cast<Property::Kind>(k); - list += p; - } -} - void Transformer::load(PersistentPool &pool) { rule = pool.idLoadS<Rule>(); pool.loadContainer(inputs); pool.loadContainer(outputs); - restorePropertyList(pool, propertiesRequestedInPrepareScript); - restorePropertyList(pool, propertiesRequestedInCommands); - int count; - pool.stream() >> count; - propertiesRequestedFromArtifactInPrepareScript.reserve(count); - while (--count >= 0) { - const QString artifactName = pool.idLoadString(); - int listCount; - pool.stream() >> listCount; - PropertySet list; - list.reserve(listCount); - while (--listCount >= 0) { - Property p; - p.moduleName = pool.idLoadString(); - p.propertyName = pool.idLoadString(); - pool.stream() >> p.value; - p.kind = Property::PropertyInModule; - list += p; - } - propertiesRequestedFromArtifactInPrepareScript.insert(artifactName, list); - } + propertiesRequestedInPrepareScript = restorePropertySet(pool); + propertiesRequestedInCommands = restorePropertySet(pool); + propertiesRequestedFromArtifactInPrepareScript = restorePropertyHash(pool); commands = loadCommandList(pool); pool.stream() >> alwaysRun; } -static void storePropertyList(PersistentPool &pool, const PropertySet &list) -{ - pool.stream() << list.count(); - foreach (const Property &p, list) { - pool.storeString(p.moduleName); - pool.storeString(p.propertyName); - pool.stream() << p.value << static_cast<int>(p.kind); - } -} - void Transformer::store(PersistentPool &pool) const { pool.store(rule); pool.storeContainer(inputs); pool.storeContainer(outputs); - storePropertyList(pool, propertiesRequestedInPrepareScript); - storePropertyList(pool, propertiesRequestedInCommands); - pool.stream() << propertiesRequestedFromArtifactInPrepareScript.count(); - for (QHash<QString, PropertySet>::ConstIterator it = propertiesRequestedFromArtifactInPrepareScript.constBegin(); - it != propertiesRequestedFromArtifactInPrepareScript.constEnd(); ++it) { - pool.storeString(it.key()); - const PropertySet &properties = it.value(); - pool.stream() << properties.count(); - foreach (const Property &p, properties) { - pool.storeString(p.moduleName); - pool.storeString(p.propertyName); - pool.stream() << p.value; // kind is always PropertyInModule - } - } + storePropertySet(pool, propertiesRequestedInPrepareScript); + storePropertySet(pool, propertiesRequestedInCommands); + storePropertyHash(pool, propertiesRequestedFromArtifactInPrepareScript); storeCommandList(commands, pool); pool.stream() << alwaysRun; } diff --git a/src/lib/corelib/corelib.qbs b/src/lib/corelib/corelib.qbs index 51355a253..4c52af17b 100644 --- a/src/lib/corelib/corelib.qbs +++ b/src/lib/corelib/corelib.qbs @@ -4,15 +4,15 @@ import QbsFunctions QbsLibrary { Depends { name: "cpp" } Depends { name: "Qt"; submodules: ["core-private", "network", "script", "xml"] } - Depends { condition: project.enableProjectFileUpdates; name: "Qt.gui" } - Depends { condition: project.enableUnitTests; name: "Qt.test" } + Depends { condition: qbsbuildconfig.enableProjectFileUpdates; name: "Qt.gui" } + Depends { condition: qbsbuildconfig.enableUnitTests; name: "Qt.test" } name: "qbscore" cpp.includePaths: base.concat([ ".", "../.." // for the plugin headers ]) property stringList projectFileUpdateDefines: - project.enableProjectFileUpdates ? ["QBS_ENABLE_PROJECT_FILE_UPDATES"] : [] + qbsbuildconfig.enableProjectFileUpdates ? ["QBS_ENABLE_PROJECT_FILE_UPDATES"] : [] cpp.defines: base.concat([ "QBS_VERSION=\"" + version + "\"", "QT_CREATOR", "QML_BUILD_STATIC_LIB", // needed for QmlJS @@ -37,12 +37,12 @@ QbsLibrary { Group { name: product.name files: ["qbs.h"] - qbs.install: project.installApiHeaders + qbs.install: qbsbuildconfig.installApiHeaders qbs.installDir: headerInstallPrefix } Group { name: "project file updating" - condition: project.enableProjectFileUpdates + condition: qbsbuildconfig.enableProjectFileUpdates prefix: "api/" files: [ "changeset.cpp", @@ -74,7 +74,7 @@ QbsLibrary { } Group { name: "public api headers" - qbs.install: project.installApiHeaders + qbs.install: qbsbuildconfig.installApiHeaders qbs.installDir: headerInstallPrefix + "/api" prefix: "api/" files: [ @@ -159,7 +159,7 @@ QbsLibrary { } Group { name: "public buildgraph headers" - qbs.install: project.installApiHeaders + qbs.install: qbsbuildconfig.installApiHeaders qbs.installDir: headerInstallPrefix + "/buildgraph" files: "buildgraph/forward_decls.h" } @@ -214,8 +214,6 @@ QbsLibrary { "asttools.h", "builtindeclarations.cpp", "builtindeclarations.h", - "builtinvalue.cpp", - "builtinvalue.h", "deprecationinfo.h", "evaluationdata.h", "evaluator.cpp", @@ -258,6 +256,7 @@ QbsLibrary { "preparescriptobserver.h", "projectresolver.cpp", "projectresolver.h", + "property.cpp", "property.h", "propertydeclaration.cpp", "propertydeclaration.h", @@ -269,6 +268,8 @@ QbsLibrary { "resolvedfilecontext.h", "scriptengine.cpp", "scriptengine.h", + "scriptimporter.cpp", + "scriptimporter.h", "scriptpropertyobserver.h", "value.cpp", "value.h", @@ -276,7 +277,7 @@ QbsLibrary { } Group { name: "public language headers" - qbs.install: project.installApiHeaders + qbs.install: qbsbuildconfig.installApiHeaders qbs.installDir: headerInstallPrefix + "/language" files: "language/forward_decls.h" } @@ -292,7 +293,7 @@ QbsLibrary { } Group { name: "public logging headers" - qbs.install: project.installApiHeaders + qbs.install: qbsbuildconfig.installApiHeaders qbs.installDir: headerInstallPrefix + "/logging" files: "logging/ilogsink.h" } @@ -371,6 +372,7 @@ QbsLibrary { "setupprojectparameters.cpp", "shellutils.cpp", "shellutils.h", + "toolchains.cpp", "version.cpp", "version.h", "visualstudioversioninfo.cpp", @@ -398,8 +400,9 @@ QbsLibrary { "settings.h", "settingsmodel.h", "setupprojectparameters.h", + "toolchains.h", ] - qbs.install: project.installApiHeaders + qbs.install: qbsbuildconfig.installApiHeaders qbs.installDir: headerInstallPrefix + "/tools" } Group { @@ -433,11 +436,11 @@ QbsLibrary { "use_installed_corelib.pri", "../../../qbs_version.pri" ] - qbs.install: project.installApiHeaders + qbs.install: qbsbuildconfig.installApiHeaders qbs.installDir: headerInstallPrefix } Group { - condition: project.enableUnitTests + condition: qbsbuildconfig.enableUnitTests name: "tests" cpp.defines: outer.filter(function(def) { return def !== "QT_NO_CAST_FROM_ASCII"; }) files: [ diff --git a/src/lib/corelib/jsextensions/environmentextension.cpp b/src/lib/corelib/jsextensions/environmentextension.cpp index 8f03ec374..56c60653b 100644 --- a/src/lib/corelib/jsextensions/environmentextension.cpp +++ b/src/lib/corelib/jsextensions/environmentextension.cpp @@ -32,9 +32,7 @@ #include <language/scriptengine.h> #include <logging/translator.h> -#include <tools/error.h> #include <tools/fileinfo.h> -#include <tools/qbsassert.h> #include <QDir> #include <QFileInfo> @@ -151,37 +149,6 @@ QScriptValue EnvironmentExtension::js_currentEnv(QScriptContext *context, QScrip return envObject; } -static void printDeprecationWarning(const QString &message, const QScriptContext *context, - QScriptEngine *engine) -{ - ErrorInfo fullError(message, context->backtrace()); - ErrorInfo error; - if (fullError.items().count() == 1) { - error = fullError; - } else { - QBS_CHECK(fullError.items().count() >= 2); - error.append(fullError.items().first().description(), - fullError.items().at(1).codeLocation()); - } - static_cast<ScriptEngine *>(engine)->logger().printWarning(error); -} - -QScriptValue js_getEnvDeprecated(QScriptContext *context, QScriptEngine *qtengine) -{ - const QString message = Tr::tr("qbs.getEnv is deprecated and will be removed in Qbs 1.6. " - "Use Environment.getEnv instead."); - printDeprecationWarning(message, context, qtengine); - return EnvironmentExtension::js_getEnv(context, qtengine); -} - -QScriptValue js_currentEnvDeprecated(QScriptContext *context, QScriptEngine *qtengine) -{ - const QString message = Tr::tr("qbs.currentEnv is deprecated and will be removed in Qbs 1.6. " - "Use Environment.currentEnv instead."); - printDeprecationWarning(message, context, qtengine); - return EnvironmentExtension::js_currentEnv(context, qtengine); -} - } // namespace Internal } // namespace qbs diff --git a/src/lib/corelib/jsextensions/environmentextension.h b/src/lib/corelib/jsextensions/environmentextension.h index dd4b980de..4c2555a66 100644 --- a/src/lib/corelib/jsextensions/environmentextension.h +++ b/src/lib/corelib/jsextensions/environmentextension.h @@ -43,9 +43,6 @@ namespace Internal { void initializeJsExtensionEnvironment(QScriptValue extensionObject); -QScriptValue js_getEnvDeprecated(QScriptContext *context, QScriptEngine *engine); -QScriptValue js_currentEnvDeprecated(QScriptContext *context, QScriptEngine *engine); - } // namespace Internal } // namespace qbs diff --git a/src/lib/corelib/jsextensions/propertylist.mm b/src/lib/corelib/jsextensions/propertylist.mm index 0306310c5..711a15b2e 100644 --- a/src/lib/corelib/jsextensions/propertylist.mm +++ b/src/lib/corelib/jsextensions/propertylist.mm @@ -240,7 +240,7 @@ void PropertyListPrivate::readFromData(QScriptContext *context, QByteArray data) respondsToSelector:@selector(propertyListWithData:options:format:error:)]) { error = nil; errorString = nil; - plist = [NSPropertyListSerialization propertyListWithData:QByteArray_toNSData(data) + plist = [NSPropertyListSerialization propertyListWithData:data.toNSData() options:0 format:&format error:&error]; if (Q_UNLIKELY(!plist)) { @@ -251,7 +251,7 @@ void PropertyListPrivate::readFromData(QScriptContext *context, QByteArray data) { error = nil; errorString = nil; - plist = [NSPropertyListSerialization propertyListFromData:QByteArray_toNSData(data) + plist = [NSPropertyListSerialization propertyListFromData:data.toNSData() mutabilityOption:NSPropertyListImmutable format:&format errorDescription:&errorString]; @@ -262,7 +262,7 @@ void PropertyListPrivate::readFromData(QScriptContext *context, QByteArray data) if (!plist && NSClassFromString(@"NSJSONSerialization")) { error = nil; errorString = nil; - plist = [NSJSONSerialization JSONObjectWithData:QByteArray_toNSData(data) + plist = [NSJSONSerialization JSONObjectWithData:data.toNSData() options:0 error:&error]; if (Q_UNLIKELY(!plist)) { @@ -274,7 +274,7 @@ void PropertyListPrivate::readFromData(QScriptContext *context, QByteArray data) #endif if (Q_UNLIKELY(!plist)) { - context->throwError(QString_fromNSString(errorString)); + context->throwError(QString::fromNSString(errorString)); } else { QVariant obj = QPropertyListUtils::fromPropertyList(plist); if (!obj.isNull()) { @@ -351,10 +351,10 @@ QByteArray PropertyListPrivate::writeToData(QScriptContext *context, const QStri } if (Q_UNLIKELY(!data)) { - context->throwError(QString_fromNSString(errorString)); + context->throwError(QString::fromNSString(errorString)); } - return QByteArray_fromNSData(data); + return QByteArray::fromNSData(data); } } diff --git a/src/lib/corelib/jsextensions/propertylistutils.h b/src/lib/corelib/jsextensions/propertylistutils.h index 773201410..882b956e0 100644 --- a/src/lib/corelib/jsextensions/propertylistutils.h +++ b/src/lib/corelib/jsextensions/propertylistutils.h @@ -39,51 +39,6 @@ #error "This file must be included from Objective-C++" #endif -static inline QString QString_fromNSString(const NSString *string) -{ -#if QT_VERSION >= QT_VERSION_CHECK(5, 2, 0) - return QString::fromNSString(string); -#else - if (!string) - return QString(); - QString qstring; - qstring.resize([string length]); - [string getCharacters:reinterpret_cast<unichar*>(qstring.data()) - range:NSMakeRange(0, [string length])]; - return qstring; -#endif -} - -static inline NSString *QString_toNSString(const QString &qstring) -{ -#if QT_VERSION >= QT_VERSION_CHECK(5, 2, 0) - return qstring.toNSString(); -#else - return [NSString stringWithCharacters:reinterpret_cast<const UniChar*>(qstring.unicode()) - length:qstring.length()]; -#endif -} - -static inline QByteArray QByteArray_fromNSData(const NSData *data) -{ -#if QT_VERSION >= QT_VERSION_CHECK(5, 3, 0) - return QByteArray::fromNSData(data); -#else - if (!data) - return QByteArray(); - return QByteArray(reinterpret_cast<const char*>([data bytes]), [data length]); -#endif -} - -static inline NSData *QByteArray_toNSData(const QByteArray &qbytearray) -{ -#if QT_VERSION >= QT_VERSION_CHECK(5, 3, 0) - return qbytearray.toNSData(); -#else - return [NSData dataWithBytes:qbytearray.constData() length:qbytearray.size()]; -#endif -} - class QPropertyListUtils { Q_DISABLE_COPY(QPropertyListUtils) diff --git a/src/lib/corelib/jsextensions/propertylistutils.mm b/src/lib/corelib/jsextensions/propertylistutils.mm index 0f3ca9640..64ef3c06d 100644 --- a/src/lib/corelib/jsextensions/propertylistutils.mm +++ b/src/lib/corelib/jsextensions/propertylistutils.mm @@ -71,9 +71,9 @@ static QVariant fromObject(id obj) } else if ([obj isKindOfClass:[NSArray class]]) { value = fromArray(obj); } else if ([obj isKindOfClass:[NSString class]]) { - value = QString_fromNSString(obj); + value = QString::fromNSString(obj); } else if ([obj isKindOfClass:[NSData class]]) { - value = QByteArray_fromNSData(obj); + value = QByteArray::fromNSData(obj); } else if ([obj isKindOfClass:[NSDate class]]) { value = QDateTime_fromNSDate(obj); } else if ([obj isKindOfClass:[NSNumber class]]) { @@ -116,7 +116,7 @@ static QVariantMap fromDictionary(NSDictionary *dict) { QVariantMap map; for (NSString *key in dict) - map[QString_fromNSString(key)] = fromObject([dict objectForKey:key]); + map[QString::fromNSString(key)] = fromObject([dict objectForKey:key]); return map; } @@ -146,9 +146,9 @@ static id toObject(const QVariant &variant) } else if (variant.type() == QVariant::List) { return toArray(variant.toList()); } else if (variant.type() == QVariant::String) { - return QString_toNSString(variant.toString()); + return variant.toString().toNSString(); } else if (variant.type() == QVariant::ByteArray) { - return QByteArray_toNSData(variant.toByteArray()); + return variant.toByteArray().toNSData(); } else if (variant.type() == QVariant::Date || variant.type() == QVariant::DateTime) { return QDateTime_toNSDate(variant.toDateTime()); @@ -178,7 +178,7 @@ static NSDictionary *toDictionary(const QVariantMap &map) QMapIterator<QString, QVariant> i(map); while (i.hasNext()) { i.next(); - [dict setObject:toObject(i.value()) forKey:QString_toNSString(i.key())]; + [dict setObject:toObject(i.value()) forKey:i.key().toNSString()]; } return [NSDictionary dictionaryWithDictionary:dict]; } diff --git a/src/lib/corelib/jsextensions/utilitiesextension.cpp b/src/lib/corelib/jsextensions/utilitiesextension.cpp index f4ca809de..fb60da7fb 100644 --- a/src/lib/corelib/jsextensions/utilitiesextension.cpp +++ b/src/lib/corelib/jsextensions/utilitiesextension.cpp @@ -34,6 +34,7 @@ #include <logging/translator.h> #include <tools/architectures.h> #include <tools/fileinfo.h> +#include <tools/toolchains.h> #ifdef Q_OS_OSX #include <tools/applecodesignutils.h> @@ -55,6 +56,7 @@ class UtilitiesExtension : public QObject, QScriptable public: static QScriptValue js_ctor(QScriptContext *context, QScriptEngine *engine); static QScriptValue js_canonicalArchitecture(QScriptContext *context, QScriptEngine *engine); + static QScriptValue js_canonicalToolchain(QScriptContext *context, QScriptEngine *engine); static QScriptValue js_getHash(QScriptContext *context, QScriptEngine *engine); static QScriptValue js_getNativeSetting(QScriptContext *context, QScriptEngine *engine); static QScriptValue js_nativeSettingGroups(QScriptContext *context, QScriptEngine *engine); @@ -72,6 +74,8 @@ void initializeJsExtensionUtilities(QScriptValue extensionObject) engine->newFunction(&UtilitiesExtension::js_ctor)); environmentObj.setProperty(QStringLiteral("canonicalArchitecture"), engine->newFunction(UtilitiesExtension::js_canonicalArchitecture, 1)); + environmentObj.setProperty(QStringLiteral("canonicalToolchain"), + engine->newFunction(UtilitiesExtension::js_canonicalToolchain)); environmentObj.setProperty(QStringLiteral("getHash"), engine->newFunction(UtilitiesExtension::js_getHash, 1)); environmentObj.setProperty(QStringLiteral("getNativeSetting"), @@ -100,11 +104,24 @@ QScriptValue UtilitiesExtension::js_ctor(QScriptContext *context, QScriptEngine QScriptValue UtilitiesExtension::js_canonicalArchitecture(QScriptContext *context, QScriptEngine *engine) { - if (Q_UNLIKELY(context->argumentCount() != 1)) - return context->throwError(QScriptContext::SyntaxError, - QLatin1String("canonicalArchitecture expects 1 argument")); - const QString architecture = context->argument(0).toString(); - return engine->toScriptValue(canonicalArchitecture(architecture)); + const QScriptValue value = context->argument(0); + if (value.isUndefined() || value.isNull()) + return value; + + if (context->argumentCount() == 1 && value.isString()) + return engine->toScriptValue(canonicalArchitecture(value.toString())); + + return context->throwError(QScriptContext::SyntaxError, + QStringLiteral("canonicalArchitecture expects one argument of type string")); +} + +QScriptValue UtilitiesExtension::js_canonicalToolchain(QScriptContext *context, + QScriptEngine *engine) +{ + QStringList toolchain; + for (int i = 0; i < context->argumentCount(); ++i) + toolchain << context->argument(i).toString(); + return engine->toScriptValue(canonicalToolchain(toolchain)); } QScriptValue UtilitiesExtension::js_getHash(QScriptContext *context, QScriptEngine *engine) diff --git a/src/lib/corelib/language/builtindeclarations.cpp b/src/lib/corelib/language/builtindeclarations.cpp index cbe95c0ee..7c35cc163 100644 --- a/src/lib/corelib/language/builtindeclarations.cpp +++ b/src/lib/corelib/language/builtindeclarations.cpp @@ -428,6 +428,7 @@ void BuiltinDeclarations::addSubprojectItem() void BuiltinDeclarations::addTransformerItem() { ItemDeclaration item(ItemType::Transformer); + item.setDeprecationInfo(DeprecationInfo(Version(1, 7), Tr::tr("Use the 'Rule' item instead."))); item.setAllowedChildTypes(ItemDeclaration::TypeNames() << ItemType::Artifact); item << conditionProperty(); diff --git a/src/lib/corelib/language/evaluator.cpp b/src/lib/corelib/language/evaluator.cpp index 41be0d431..b87972320 100644 --- a/src/lib/corelib/language/evaluator.cpp +++ b/src/lib/corelib/language/evaluator.cpp @@ -36,6 +36,7 @@ #include "filetags.h" #include "item.h" #include "scriptengine.h" +#include "value.h" #include <jsextensions/jsextensions.h> #include <logging/translator.h> @@ -234,7 +235,7 @@ QScriptValue Evaluator::fileScope(const FileContextConstPtr &file) result = m_scriptEngine->newObject(); result.setProperty(QLatin1String("filePath"), file->filePath()); result.setProperty(QLatin1String("path"), file->dirPath()); - m_scriptEngine->import(file, result, result); + m_scriptEngine->import(file, result); JsExtensions::setupExtensions(file->jsExtensions(), result); return result; } diff --git a/src/lib/corelib/language/evaluatorscriptclass.cpp b/src/lib/corelib/language/evaluatorscriptclass.cpp index 451658876..bbfdf56a1 100644 --- a/src/lib/corelib/language/evaluatorscriptclass.cpp +++ b/src/lib/corelib/language/evaluatorscriptclass.cpp @@ -36,6 +36,7 @@ #include "item.h" #include "scriptengine.h" #include "propertydeclaration.h" +#include "value.h" #include <tools/architectures.h> #include <tools/fileinfo.h> #include <tools/hostosinfo.h> @@ -261,11 +262,6 @@ private: { *result = scriptClass->engine()->toScriptValue(variantValue->value()); } - - void handle(BuiltinValue *builtinValue) - { - *result = scriptClass->scriptValueForBuiltin(builtinValue->builtin()); - } }; bool debugProperties = false; @@ -281,8 +277,6 @@ EvaluatorScriptClass::EvaluatorScriptClass(ScriptEngine *scriptEngine, const Log , m_logger(logger) , m_valueCacheEnabled(false) { - m_getEnvBuiltin = scriptEngine->newFunction(js_getEnvDeprecated, 1); - m_currentEnvBuiltin = scriptEngine->newFunction(js_currentEnvDeprecated, 0); } QScriptClass::QueryFlags EvaluatorScriptClass::queryProperty(const QScriptValue &object, @@ -512,63 +506,5 @@ void EvaluatorScriptClass::setValueCacheEnabled(bool enabled) m_valueCacheEnabled = enabled; } -QScriptValue EvaluatorScriptClass::scriptValueForBuiltin(BuiltinValue::Builtin builtin) const -{ - switch (builtin) { - case BuiltinValue::GetEnvFunction: - return m_getEnvBuiltin; - case BuiltinValue::CurrentEnvFunction: - return m_currentEnvBuiltin; - } - QBS_ASSERT(!"unhandled builtin", ;); - return QScriptValue(); -} - -QScriptValue EvaluatorScriptClass::js_consoleError(QScriptContext *context, QScriptEngine *engine, - Logger *logger) -{ - if (Q_UNLIKELY(context->argumentCount() != 1)) - return context->throwError(QScriptContext::SyntaxError, - QLatin1String("error expects 1 argument")); - logger->qbsLog(LoggerError) << context->argument(0).toString(); - return engine->undefinedValue(); -} - -QScriptValue EvaluatorScriptClass::js_consoleWarn(QScriptContext *context, QScriptEngine *engine, - Logger *logger) -{ - if (Q_UNLIKELY(context->argumentCount() != 1)) - return context->throwError(QScriptContext::SyntaxError, - QLatin1String("error expects 1 argument")); - logger->qbsWarning() << context->argument(0).toString(); - return engine->undefinedValue(); -} - -QScriptValue EvaluatorScriptClass::js_consoleInfo(QScriptContext *context, QScriptEngine *engine, - Logger *logger) -{ - if (Q_UNLIKELY(context->argumentCount() != 1)) - return context->throwError(QScriptContext::SyntaxError, - QLatin1String("error expects 1 argument")); - logger->qbsInfo() << context->argument(0).toString(); - return engine->undefinedValue(); -} - -QScriptValue EvaluatorScriptClass::js_consoleDebug(QScriptContext *context, QScriptEngine *engine, - Logger *logger) -{ - if (Q_UNLIKELY(context->argumentCount() != 1)) - return context->throwError(QScriptContext::SyntaxError, - QLatin1String("error expects 1 argument")); - logger->qbsDebug() << context->argument(0).toString(); - return engine->undefinedValue(); -} - -QScriptValue EvaluatorScriptClass::js_consoleLog(QScriptContext *context, QScriptEngine *engine, - Logger *logger) -{ - return js_consoleDebug(context, engine, logger); -} - } // namespace Internal } // namespace qbs diff --git a/src/lib/corelib/language/evaluatorscriptclass.h b/src/lib/corelib/language/evaluatorscriptclass.h index 3816633b1..04cb77b81 100644 --- a/src/lib/corelib/language/evaluatorscriptclass.h +++ b/src/lib/corelib/language/evaluatorscriptclass.h @@ -31,13 +31,13 @@ #ifndef QBS_EVALUATORSCRIPTCLASS_H #define QBS_EVALUATORSCRIPTCLASS_H -#include "builtinvalue.h" #include "forward_decls.h" #include <logging/logger.h> #include <QScriptClass> #include <QStack> +#include <QSet> QT_BEGIN_NAMESPACE class QScriptContext; @@ -46,6 +46,7 @@ QT_END_NAMESPACE namespace qbs { namespace Internal { class EvaluationData; +class Item; class ScriptEngine; class EvaluatorScriptClass : public QScriptClass @@ -60,18 +61,6 @@ public: const QScriptString &name, uint id); void setValueCacheEnabled(bool enabled); - QScriptValue scriptValueForBuiltin(BuiltinValue::Builtin builtin) const; - - static QScriptValue js_consoleError(QScriptContext *context, QScriptEngine *engine, - Logger *logger); - static QScriptValue js_consoleWarn(QScriptContext *context, QScriptEngine *engine, - Logger *logger); - static QScriptValue js_consoleInfo(QScriptContext *context, QScriptEngine *engine, - Logger *logger); - static QScriptValue js_consoleDebug(QScriptContext *context, QScriptEngine *engine, - Logger *logger); - static QScriptValue js_consoleLog(QScriptContext *context, QScriptEngine *engine, - Logger *logger); private: QueryFlags queryItemProperty(const EvaluationData *data, @@ -98,8 +87,6 @@ private: QueryResult m_queryResult; Logger m_logger; bool m_valueCacheEnabled; - QScriptValue m_getEnvBuiltin; - QScriptValue m_currentEnvBuiltin; QStack<JSSourceValue *> m_sourceValueStack; QSet<Value *> m_currentNextChain; }; diff --git a/src/lib/corelib/language/forward_decls.h b/src/lib/corelib/language/forward_decls.h index 4abedf89e..3ae74fc99 100644 --- a/src/lib/corelib/language/forward_decls.h +++ b/src/lib/corelib/language/forward_decls.h @@ -51,10 +51,6 @@ class VariantValue; typedef QSharedPointer<VariantValue> VariantValuePtr; typedef QSharedPointer<const VariantValue> VariantValueConstPtr; -class BuiltinValue; -typedef QSharedPointer<BuiltinValue> BuiltinValuePtr; -typedef QSharedPointer<const BuiltinValue> BuiltinValueConstPtr; - class FileContext; typedef QSharedPointer<FileContext> FileContextPtr; typedef QSharedPointer<const FileContext> FileContextConstPtr; diff --git a/src/lib/corelib/language/item.cpp b/src/lib/corelib/language/item.cpp index e63ef7233..891252f48 100644 --- a/src/lib/corelib/language/item.cpp +++ b/src/lib/corelib/language/item.cpp @@ -241,7 +241,6 @@ static const char *valueType(const Value *v) case Value::JSSourceValueType: return "JS source"; case Value::ItemValueType: return "Item"; case Value::VariantValueType: return "Variant"; - case Value::BuiltinValueType: return "Built-in"; } return ""; // For dumb compilers. } @@ -269,8 +268,6 @@ void Item::dump(int indentation) const qDebug("%svalue: %s", nextIndent.constData(), qPrintable(it.value().staticCast<VariantValue>()->value().toString())); break; - case Value::BuiltinValueType: - break; } } if (!m_children.isEmpty()) diff --git a/src/lib/corelib/language/itemdeclaration.cpp b/src/lib/corelib/language/itemdeclaration.cpp index 4a5837f3a..e1fa662ec 100644 --- a/src/lib/corelib/language/itemdeclaration.cpp +++ b/src/lib/corelib/language/itemdeclaration.cpp @@ -38,13 +38,6 @@ ItemDeclaration::ItemDeclaration(ItemType type) { } -ItemDeclaration::ItemDeclaration(const qbs::Internal::ItemDeclaration &other) - : m_type(other.m_type) - , m_properties(other.m_properties) - , m_allowedChildTypes(other.m_allowedChildTypes) -{ -} - ItemDeclaration &ItemDeclaration::operator<<(const PropertyDeclaration &decl) { m_properties.append(decl); diff --git a/src/lib/corelib/language/itemdeclaration.h b/src/lib/corelib/language/itemdeclaration.h index 8e9ab933d..98bc583d4 100644 --- a/src/lib/corelib/language/itemdeclaration.h +++ b/src/lib/corelib/language/itemdeclaration.h @@ -31,6 +31,7 @@ #ifndef QBS_ITEMDECLARATION_H #define QBS_ITEMDECLARATION_H +#include "deprecationinfo.h" #include "itemtype.h" #include "propertydeclaration.h" @@ -44,7 +45,6 @@ class ItemDeclaration { public: ItemDeclaration(ItemType type = ItemType::Unknown); - ItemDeclaration(const ItemDeclaration &other); ItemType type() const { return m_type; } @@ -52,6 +52,9 @@ public: void setProperties(const Properties &props) { m_properties = props; } const Properties &properties() const { return m_properties; } + void setDeprecationInfo(const DeprecationInfo &di) { m_deprecationInfo = di; } + DeprecationInfo deprecationInfo() const { return m_deprecationInfo; } + ItemDeclaration &operator<<(const PropertyDeclaration &decl); typedef QSet<ItemType> TypeNames; @@ -63,6 +66,7 @@ private: ItemType m_type; Properties m_properties; TypeNames m_allowedChildTypes; + DeprecationInfo m_deprecationInfo; }; } // namespace Internal diff --git a/src/lib/corelib/language/itemreaderastvisitor.cpp b/src/lib/corelib/language/itemreaderastvisitor.cpp index e68bd897b..b7b86b002 100644 --- a/src/lib/corelib/language/itemreaderastvisitor.cpp +++ b/src/lib/corelib/language/itemreaderastvisitor.cpp @@ -96,7 +96,10 @@ bool ItemReaderASTVisitor::visit(AST::UiObjectDefinition *ast) QBS_CHECK(inheritorItem->type() <= ItemType::LastActualItem); item->setType(inheritorItem->type()); } else { - item->setType(BuiltinDeclarations::instance().typeForName(typeName, item->location())); + const ItemType itemType + = BuiltinDeclarations::instance().typeForName(typeName, item->location()); + checkDeprecationStatus(itemType, typeName, item->location()); + item->setType(itemType); if (item->type() == ItemType::Properties && item->parent() && item->parent()->type() == ItemType::SubProject) { item->setType(ItemType::PropertiesInSubProject); @@ -320,5 +323,29 @@ void ItemReaderASTVisitor::inheritItem(Item *dst, const Item *src) } } +void ItemReaderASTVisitor::checkDeprecationStatus(ItemType itemType, const QString &itemName, + const CodeLocation &itemLocation) +{ + const ItemDeclaration itemDecl = BuiltinDeclarations::instance().declarationsForType(itemType); + const DeprecationInfo &di = itemDecl.deprecationInfo(); + if (!di.isValid()) + return; + if (di.removalVersion() <= Version::qbsVersion()) { + QString message = Tr::tr("The item '%1' cannot be used anymore. " + "It was removed in qbs %2.") + .arg(itemName, di.removalVersion().toString()); + ErrorInfo error(message, itemLocation); + if (!di.additionalUserInfo().isEmpty()) + error.append(di.additionalUserInfo()); + throw error; + } + QString warning = Tr::tr("The item '%1' is deprecated and will be removed in " + "qbs %2.").arg(itemName, di.removalVersion().toString()); + ErrorInfo error(warning, itemLocation); + if (!di.additionalUserInfo().isEmpty()) + error.append(di.additionalUserInfo()); + m_logger.printWarning(error); +} + } // namespace Internal } // namespace qbs diff --git a/src/lib/corelib/language/itemreaderastvisitor.h b/src/lib/corelib/language/itemreaderastvisitor.h index 1c32e2ee6..9c6a4fcc2 100644 --- a/src/lib/corelib/language/itemreaderastvisitor.h +++ b/src/lib/corelib/language/itemreaderastvisitor.h @@ -32,6 +32,7 @@ #define QBS_ITEMREADERASTVISITOR_H #include "forward_decls.h" +#include "itemtype.h" #include <logging/logger.h> #include <parser/qmljsastvisitor_p.h> @@ -68,6 +69,8 @@ private: const QbsQmlJS::AST::SourceLocation &sourceLocation); Item *targetItemForBinding(const QStringList &binding, const JSSourceValueConstPtr &value); static void inheritItem(Item *dst, const Item *src); + void checkDeprecationStatus(ItemType itemType, const QString &itemName, + const CodeLocation &itemLocation); ItemReaderVisitorState &m_visitorState; const FileContextPtr m_file; diff --git a/src/lib/corelib/language/language.cpp b/src/lib/corelib/language/language.cpp index 221cb7fd7..dbb1ec694 100644 --- a/src/lib/corelib/language/language.cpp +++ b/src/lib/corelib/language/language.cpp @@ -48,6 +48,7 @@ #include <tools/error.h> #include <tools/fileinfo.h> #include <tools/persistence.h> +#include <tools/scripttools.h> #include <tools/qbsassert.h> #include <QCryptographicHash> @@ -371,6 +372,11 @@ bool Rule::isDynamic() const return outputArtifactsScript->isValid(); } +bool Rule::requiresInputs() const +{ + return !inputs.isEmpty() || !inputsFromDependencies.isEmpty(); +} + void Rule::load(PersistentPool &pool) { name = pool.idLoadString(); @@ -596,6 +602,8 @@ static QProcessEnvironment getProcessEnvironment(ScriptEngine *engine, EnvType e } QScriptValue scope = engine->newObject(); + scope.setPrototype(engine->globalObject()); + TemporaryGlobalObjectSetter tgos(scope); QSet<QString> seenModuleNames; QList<const ResolvedModule *> topSortedModules = topSortModules(moduleChildren, rootModules, seenModuleNames); @@ -616,7 +624,7 @@ static QProcessEnvironment getProcessEnvironment(ScriptEngine *engine, EnvType e } // handle imports - engine->import(setupScript->fileContext, scope, scope); + engine->import(setupScript->fileContext, scope); JsExtensions::setupExtensions(setupScript->fileContext->jsExtensions(), scope); // expose properties of direct module dependencies @@ -636,10 +644,7 @@ static QProcessEnvironment getProcessEnvironment(ScriptEngine *engine, EnvType e for (QVariantMap::const_iterator it = moduleCfg.constBegin(); it != moduleCfg.constEnd(); ++it) scope.setProperty(it.key(), engine->toScriptValue(it.value())); - QScriptContext *ctx = engine->currentContext(); - ctx->pushScope(scope); scriptValue = engine->evaluate(setupScript->sourceCode + QLatin1String("()")); - ctx->popScope(); if (Q_UNLIKELY(engine->hasErrorOrException(scriptValue))) { QString envTypeStr = (envType == BuildEnv ? QLatin1String("build") : QLatin1String("run")); @@ -744,7 +749,7 @@ QString ResolvedProduct::uniqueName() const static QStringList findGeneratedFiles(const Artifact *base, bool recursive, const FileTags &tags) { QStringList result; - foreach (const Artifact *parent, base->parentArtifacts()) { + for (const Artifact *parent : base->parentArtifacts()) { if (tags.isEmpty() || parent->fileTags().matches(tags)) result << parent->filePath(); if (recursive) @@ -760,7 +765,7 @@ QStringList ResolvedProduct::generatedFiles(const QString &baseFile, bool recurs if (!data) return QStringList(); - foreach (const Artifact *art, ArtifactSet::fromNodeSet(data->nodes)) { + for (const Artifact *art : filterByType<Artifact>(data->nodes)) { if (art->filePath() == baseFile) return findGeneratedFiles(art, recursive, tags); } @@ -1179,7 +1184,16 @@ template<typename T> bool listsAreEqual(const QList<T> &l1, const QList<T> &l2) QString keyFromElem(const SourceArtifactPtr &sa) { return sa->absoluteFilePath; } QString keyFromElem(const ResolvedTransformerPtr &t) { return t->transform->sourceCode; } -QString keyFromElem(const RulePtr &r) { return r->toString(); } +QString keyFromElem(const RulePtr &r) { + QString key = r->toString() + r->prepareScript->sourceCode; + if (r->outputArtifactsScript) + key += r->outputArtifactsScript->sourceCode; + foreach (const auto &a, r->artifacts) { + key += a->filePath; + } + return key; +} + QString keyFromElem(const ArtifactPropertiesPtr &ap) { QStringList lst = ap->fileTagsFilter().toStringList(); diff --git a/src/lib/corelib/language/language.h b/src/lib/corelib/language/language.h index 12e3ef9e9..f909f9fa2 100644 --- a/src/lib/corelib/language/language.h +++ b/src/lib/corelib/language/language.h @@ -284,6 +284,7 @@ public: FileTags staticOutputFileTags() const; FileTags collectedOutputFileTags() const; bool isDynamic() const; + bool requiresInputs() const; private: Rule() : multiplex(false), alwaysRun(false), ruleGraphId(-1) {} @@ -294,6 +295,7 @@ bool operator==(const Rule &r1, const Rule &r2); inline bool operator!=(const Rule &r1, const Rule &r2) { return !(r1 == r2); } bool ruleListsAreEqual(const QList<RulePtr> &l1, const QList<RulePtr> &l2); +// TODO: Remove this and all related code in 1.7. class ResolvedTransformer : public PersistentObject { public: diff --git a/src/lib/corelib/language/language.pri b/src/lib/corelib/language/language.pri index 28da484e1..ca8fad22c 100644 --- a/src/lib/corelib/language/language.pri +++ b/src/lib/corelib/language/language.pri @@ -6,7 +6,6 @@ HEADERS += \ $$PWD/astpropertiesitemhandler.h \ $$PWD/asttools.h \ $$PWD/builtindeclarations.h \ - $$PWD/builtinvalue.h \ $$PWD/deprecationinfo.h \ $$PWD/evaluationdata.h \ $$PWD/evaluator.h \ @@ -38,6 +37,7 @@ HEADERS += \ $$PWD/qualifiedid.h \ $$PWD/resolvedfilecontext.h \ $$PWD/scriptengine.h \ + $$PWD/scriptimporter.h \ $$PWD/scriptpropertyobserver.h \ $$PWD/value.h @@ -47,7 +47,6 @@ SOURCES += \ $$PWD/astpropertiesitemhandler.cpp \ $$PWD/asttools.cpp \ $$PWD/builtindeclarations.cpp \ - $$PWD/builtinvalue.cpp \ $$PWD/evaluator.cpp \ $$PWD/evaluatorscriptclass.cpp \ $$PWD/filecontext.cpp \ @@ -66,11 +65,13 @@ SOURCES += \ $$PWD/modulemerger.cpp \ $$PWD/preparescriptobserver.cpp \ $$PWD/projectresolver.cpp \ + $$PWD/property.cpp \ $$PWD/propertydeclaration.cpp \ $$PWD/propertymapinternal.cpp \ $$PWD/qualifiedid.cpp \ $$PWD/resolvedfilecontext.cpp \ $$PWD/scriptengine.cpp \ + $$PWD/scriptimporter.cpp \ $$PWD/value.cpp qbs_enable_unit_tests { diff --git a/src/lib/corelib/language/moduleloader.cpp b/src/lib/corelib/language/moduleloader.cpp index 0febedcdf..77f88694a 100644 --- a/src/lib/corelib/language/moduleloader.cpp +++ b/src/lib/corelib/language/moduleloader.cpp @@ -31,7 +31,6 @@ #include "moduleloader.h" #include "builtindeclarations.h" -#include "builtinvalue.h" #include "evaluator.h" #include "filecontext.h" #include "item.h" @@ -301,7 +300,6 @@ private: } void handle(VariantValue *) { /* only created internally - no need to check */ } - void handle(BuiltinValue *) { /* only created internally - no need to check */ } }; void ModuleLoader::handleTopLevelProject(ModuleLoaderResult *loadResult, Item *projectItem, @@ -1415,11 +1413,6 @@ static QStringList hostOS() void ModuleLoader::setupBaseModulePrototype(Item *prototype) { - prototype->setProperty(QLatin1String("getEnv"), - BuiltinValue::create(BuiltinValue::GetEnvFunction)); - prototype->setProperty(QLatin1String("currentEnv"), - BuiltinValue::create(BuiltinValue::CurrentEnvFunction)); - prototype->setProperty(QLatin1String("hostOS"), VariantValue::create(hostOS())); prototype->setProperty(QLatin1String("libexecPath"), VariantValue::create(m_parameters.libexecPath())); diff --git a/src/lib/corelib/language/projectresolver.cpp b/src/lib/corelib/language/projectresolver.cpp index 27a6b9a80..927f174a0 100644 --- a/src/lib/corelib/language/projectresolver.cpp +++ b/src/lib/corelib/language/projectresolver.cpp @@ -712,6 +712,14 @@ void ProjectResolver::resolveRule(Item *item, ProjectContext *projectContext) rule->explicitlyDependsOn = m_evaluator->fileTagsValue(item, QLatin1String("explicitlyDependsOn")); rule->module = m_moduleContext ? m_moduleContext->module : projectContext->dummyModule; + if (!rule->multiplex && !rule->requiresInputs()) { + const QString message = Tr::tr("Rule has no inputs, but is not a multiplex rule."); + ErrorInfo error(message, item->location()); + if (m_setupParams.productErrorMode() == ErrorHandlingMode::Strict) + throw error; + m_logger.printWarning(error); + return; + } if (m_productContext) m_productContext->product->rules += rule; else @@ -1125,9 +1133,6 @@ QVariantMap ProjectResolver::evaluateProperties(Item *item, Item *propertiesCont result[it.key()] = vvp->value(); break; } - case Value::BuiltinValueType: - // ignore - break; } } return lookupPrototype && propertiesContainer->prototype() diff --git a/src/lib/corelib/language/property.cpp b/src/lib/corelib/language/property.cpp new file mode 100644 index 000000000..a374d9d8f --- /dev/null +++ b/src/lib/corelib/language/property.cpp @@ -0,0 +1,107 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing +** +** This file is part of the Qt Build Suite. +** +** 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. +** +****************************************************************************/ + +#include "property.h" + +#include <tools/persistence.h> + +namespace qbs { +namespace Internal { + +void storePropertySet(PersistentPool &pool, const PropertySet &propertySet) +{ + pool.stream() << propertySet.count(); + foreach (const Property &p, propertySet) { + pool.storeString(p.moduleName); + pool.storeString(p.propertyName); + pool.stream() << p.value << static_cast<int>(p.kind); + } +} + +PropertySet restorePropertySet(PersistentPool &pool) +{ + int count; + pool.stream() >> count; + PropertySet propertySet; + propertySet.reserve(count); + while (--count >= 0) { + Property p; + p.moduleName = pool.idLoadString(); + p.propertyName = pool.idLoadString(); + int k; + pool.stream() >> p.value >> k; + p.kind = static_cast<Property::Kind>(k); + propertySet += p; + } + return propertySet; +} + +void storePropertyHash(PersistentPool &pool, const PropertyHash &propertyHash) +{ + pool.stream() << propertyHash.count(); + for (auto it = propertyHash.constBegin(); it != propertyHash.constEnd(); ++it) { + pool.storeString(it.key()); + const PropertySet &properties = it.value(); + pool.stream() << properties.count(); + foreach (const Property &p, properties) { + pool.storeString(p.moduleName); + pool.storeString(p.propertyName); + pool.stream() << p.value; // kind is always PropertyInModule + } + } +} + +PropertyHash restorePropertyHash(PersistentPool &pool) +{ + int count; + pool.stream() >> count; + PropertyHash propertyHash; + propertyHash.reserve(count); + while (--count >= 0) { + const QString artifactName = pool.idLoadString(); + int listCount; + pool.stream() >> listCount; + PropertySet list; + list.reserve(listCount); + while (--listCount >= 0) { + Property p; + p.moduleName = pool.idLoadString(); + p.propertyName = pool.idLoadString(); + pool.stream() >> p.value; + p.kind = Property::PropertyInModule; + list += p; + } + propertyHash.insert(artifactName, list); + } + return propertyHash; +} + +} // namespace Internal +} // namespace qbs diff --git a/src/lib/corelib/language/property.h b/src/lib/corelib/language/property.h index 8960369e9..cbdb6c6a5 100644 --- a/src/lib/corelib/language/property.h +++ b/src/lib/corelib/language/property.h @@ -36,6 +36,7 @@ namespace qbs { namespace Internal { +class PersistentPool; class Property { @@ -74,6 +75,12 @@ inline uint qHash(const Property &p) } typedef QSet<Property> PropertySet; +typedef QHash<QString, PropertySet> PropertyHash; + +void storePropertySet(PersistentPool &pool, const PropertySet &list); +PropertySet restorePropertySet(PersistentPool &pool); +void storePropertyHash(PersistentPool &pool, const PropertyHash &propertyHash); +PropertyHash restorePropertyHash(PersistentPool &pool); } // namespace Internal } // namespace qbs diff --git a/src/lib/corelib/language/scriptengine.cpp b/src/lib/corelib/language/scriptengine.cpp index a6a4419e3..f6ca2b116 100644 --- a/src/lib/corelib/language/scriptengine.cpp +++ b/src/lib/corelib/language/scriptengine.cpp @@ -30,10 +30,10 @@ #include "scriptengine.h" -#include "evaluatorscriptclass.h" #include "filecontextbase.h" #include "jsimports.h" #include "propertymapinternal.h" +#include "scriptimporter.h" #include "scriptpropertyobserver.h" #include <buildgraph/artifact.h> @@ -47,10 +47,10 @@ #include <QDirIterator> #include <QFile> #include <QFileInfo> -#include <QScriptProgram> #include <QScriptValueIterator> #include <QSet> #include <QTextStream> +#include <QTimer> namespace qbs { namespace Internal { @@ -81,7 +81,8 @@ uint qHash(const ScriptEngine::PropertyCacheKey &k, uint seed = 0) } ScriptEngine::ScriptEngine(const Logger &logger, QObject *parent) - : QScriptEngine(parent), m_propertyCacheEnabled(true), m_logger(logger) + : QScriptEngine(parent), m_scriptImporter(new ScriptImporter(this)), + m_propertyCacheEnabled(true), m_logger(logger) { setProcessEventsInterval(1000); // For the cancelation mechanism to work. m_cancelationError = currentContext()->throwValue(tr("Execution canceled")); @@ -99,10 +100,10 @@ ScriptEngine::ScriptEngine(const Logger &logger, QObject *parent) ScriptEngine::~ScriptEngine() { qDeleteAll(m_ownedVariantMaps); + delete (m_scriptImporter); } -void ScriptEngine::import(const FileContextBaseConstPtr &fileCtx, QScriptValue scope, - QScriptValue targetObject) +void ScriptEngine::import(const FileContextBaseConstPtr &fileCtx, QScriptValue &targetObject) { installImportFunctions(); m_currentDirPathStack.push(FileInfo::path(fileCtx->filePath())); @@ -110,7 +111,7 @@ void ScriptEngine::import(const FileContextBaseConstPtr &fileCtx, QScriptValue s const JsImports jsImports = fileCtx->jsImports(); for (JsImports::const_iterator it = jsImports.begin(); it != jsImports.end(); ++it) { - import(*it, scope, targetObject); + import(*it, targetObject); } m_currentDirPathStack.pop(); @@ -118,9 +119,8 @@ void ScriptEngine::import(const FileContextBaseConstPtr &fileCtx, QScriptValue s uninstallImportFunctions(); } -void ScriptEngine::import(const JsImport &jsImport, QScriptValue scope, QScriptValue targetObject) +void ScriptEngine::import(const JsImport &jsImport, QScriptValue &targetObject) { - QBS_ASSERT(!scope.isValid() || scope.isObject(), return); QBS_ASSERT(targetObject.isObject(), return); QBS_ASSERT(targetObject.engine() == this, return); @@ -134,8 +134,9 @@ void ScriptEngine::import(const JsImport &jsImport, QScriptValue scope, QScriptV } else { if (debugJSImports) qDebug() << "[ENGINE] " << jsImport.filePaths << " (cache miss)"; + jsImportValue = newObject(); foreach (const QString &filePath, jsImport.filePaths) - importFile(filePath, scope, &jsImportValue); + importFile(filePath, jsImportValue); m_jsImportCache.insert(jsImport, jsImportValue); } targetObject.setProperty(jsImport.scopeName, jsImportValue); @@ -235,83 +236,16 @@ void ScriptEngine::setEnvironment(const QProcessEnvironment &env) m_environment = env; } -QScriptValue ScriptEngine::importFile(const QString &filePath, const QScriptValue &scope, - QScriptValue *targetObject) +void ScriptEngine::importFile(const QString &filePath, QScriptValue &targetObject) { QFile file(filePath); if (Q_UNLIKELY(!file.open(QFile::ReadOnly))) throw ErrorInfo(tr("Cannot open '%1'.").arg(filePath)); const QString sourceCode = QTextStream(&file).readAll(); file.close(); - QScriptProgram program(sourceCode, filePath); - QScriptValue obj; - if (!targetObject) - obj = newObject(); m_currentDirPathStack.push(FileInfo::path(filePath)); - importProgram(program, scope, targetObject ? *targetObject : obj); + m_scriptImporter->importSourceCode(sourceCode, filePath, targetObject); m_currentDirPathStack.pop(); - return targetObject ? *targetObject : obj; -} - -void ScriptEngine::importProgram(const QScriptProgram &program, const QScriptValue &scope, - QScriptValue &targetObject) -{ - QSet<QString> globalPropertyNames; - { - QScriptValueIterator it(globalObject()); - while (it.hasNext()) { - it.next(); - globalPropertyNames += it.name(); - } - } - - pushContext(); - if (scope.isObject()) - currentContext()->pushScope(scope); - QScriptValue result = evaluate(program); - QScriptValue activationObject = currentContext()->activationObject(); - if (scope.isObject()) - currentContext()->popScope(); - popContext(); - if (Q_UNLIKELY(hasErrorOrException(result))) - throw ErrorInfo(tr("Error when importing '%1': %2").arg(program.fileName(), result.toString())); - - // If targetObject is already an object, it doesn't get overwritten but enhanced by the - // contents of the .js file. - // This is necessary for library imports that consist of multiple js files. - if (!targetObject.isObject()) - targetObject = newObject(); - - // Copy every property of the activation object to the target object. - // We do not just save a reference to the activation object, because QScriptEngine contains - // special magic for activation objects that leads to unanticipated results. - { - QScriptValueIterator it(activationObject); - while (it.hasNext()) { - it.next(); - if (debugJSImports) - qDebug() << "[ENGINE] Copying property " << it.name(); - targetObject.setProperty(it.name(), it.value()); - } - } - - // Copy new global properties to the target object and remove them from - // the global object. This is to support direct variable assignments - // without the 'var' keyword in JavaScript files. - QScriptValueIterator it(globalObject()); - while (it.hasNext()) { - it.next(); - if (globalPropertyNames.contains(it.name())) - continue; - - if (debugJSImports) { - qDebug() << "[ENGINE] inserting global property " - << it.name() << " " << it.value().toString(); - } - - targetObject.setProperty(it.name(), it.value()); - it.remove(); - } } static QString findExtensionDir(const QStringList &searchPaths, const QString &extensionPath) @@ -396,7 +330,9 @@ QScriptValue ScriptEngine::js_loadExtension(QScriptContext *context, QScriptEngi engine->m_logger.qbsDebug() << "[loadExtension] importing file " << filePath; } - values << engine->importFile(filePath, QScriptValue()); + QScriptValue obj = engine->newObject(); + engine->importFile(filePath, obj); + values << obj; } } catch (const ErrorInfo &e) { return context->throwError(e.toString()); @@ -423,7 +359,8 @@ QScriptValue ScriptEngine::js_loadFile(QScriptContext *context, QScriptEngine *q try { const QString filePath = FileInfo::resolvePath(engine->m_currentDirPathStack.top(), relativeFilePath); - result = engine->importFile(filePath, QScriptValue()); + result = engine->newObject(); + engine->importFile(filePath, result); } catch (const ErrorInfo &e) { result = context->throwError(e.toString()); } @@ -480,7 +417,7 @@ QScriptValueList ScriptEngine::argumentList(const QStringList &argumentNames, void ScriptEngine::cancel() { - QMetaObject::invokeMethod(this, "abort", Qt::QueuedConnection); + QTimer::singleShot(0, this, [this] { abort(); }); } void ScriptEngine::abort() @@ -514,28 +451,62 @@ private: QScriptValue m_descriptor; }; +static QScriptValue js_consoleError(QScriptContext *context, QScriptEngine *engine, Logger *logger) +{ + if (Q_UNLIKELY(context->argumentCount() != 1)) + return context->throwError(QScriptContext::SyntaxError, + QLatin1String("error expects 1 argument")); + logger->qbsLog(LoggerError) << context->argument(0).toString(); + return engine->undefinedValue(); +} + +static QScriptValue js_consoleWarn(QScriptContext *context, QScriptEngine *engine, Logger *logger) +{ + if (Q_UNLIKELY(context->argumentCount() != 1)) + return context->throwError(QScriptContext::SyntaxError, + QLatin1String("error expects 1 argument")); + logger->qbsWarning() << context->argument(0).toString(); + return engine->undefinedValue(); +} + +static QScriptValue js_consoleInfo(QScriptContext *context, QScriptEngine *engine, Logger *logger) +{ + if (Q_UNLIKELY(context->argumentCount() != 1)) + return context->throwError(QScriptContext::SyntaxError, + QLatin1String("error expects 1 argument")); + logger->qbsInfo() << context->argument(0).toString(); + return engine->undefinedValue(); +} + +static QScriptValue js_consoleDebug(QScriptContext *context, QScriptEngine *engine, Logger *logger) +{ + if (Q_UNLIKELY(context->argumentCount() != 1)) + return context->throwError(QScriptContext::SyntaxError, + QLatin1String("error expects 1 argument")); + logger->qbsDebug() << context->argument(0).toString(); + return engine->undefinedValue(); +} + +static QScriptValue js_consoleLog(QScriptContext *context, QScriptEngine *engine, Logger *logger) +{ + return js_consoleDebug(context, engine, logger); +} + void ScriptEngine::installQbsBuiltins() { globalObject().setProperty(QLatin1String("qbs"), m_qbsObject = newObject()); - installQbsFunction(QLatin1String("getEnv"), 1, js_getEnvDeprecated); - installQbsFunction(QLatin1String("currentEnv"), 0, js_currentEnvDeprecated); globalObject().setProperty(QLatin1String("console"), m_consoleObject = newObject()); installConsoleFunction(QLatin1String("debug"), - reinterpret_cast<FunctionWithArgSignature>( - EvaluatorScriptClass::js_consoleDebug)); + reinterpret_cast<FunctionWithArgSignature>(&js_consoleDebug)); installConsoleFunction(QLatin1String("error"), - reinterpret_cast<FunctionWithArgSignature>( - EvaluatorScriptClass::js_consoleError)); + reinterpret_cast<FunctionWithArgSignature>(&js_consoleError)); installConsoleFunction(QLatin1String("info"), - reinterpret_cast<FunctionWithArgSignature>( - EvaluatorScriptClass::js_consoleInfo)); + reinterpret_cast<FunctionWithArgSignature>(&js_consoleInfo)); installConsoleFunction(QLatin1String("log"), - reinterpret_cast<FunctionWithArgSignature>( - EvaluatorScriptClass::js_consoleLog)); + reinterpret_cast<FunctionWithArgSignature>(&js_consoleLog)); installConsoleFunction(QLatin1String("warn"), - reinterpret_cast<FunctionWithArgSignature>( - EvaluatorScriptClass::js_consoleWarn)); + reinterpret_cast<FunctionWithArgSignature>(&js_consoleWarn)); } void ScriptEngine::extendJavaScriptBuiltins() diff --git a/src/lib/corelib/language/scriptengine.h b/src/lib/corelib/language/scriptengine.h index 5e943f25f..acf8e8d71 100644 --- a/src/lib/corelib/language/scriptengine.h +++ b/src/lib/corelib/language/scriptengine.h @@ -48,6 +48,7 @@ namespace qbs { namespace Internal { class Artifact; class JsImport; +class ScriptImporter; class ScriptPropertyObserver; class ScriptEngine : public QScriptEngine @@ -59,9 +60,8 @@ public: void setLogger(const Logger &logger) { m_logger = logger; } Logger logger() const { return m_logger; } - void import(const FileContextBaseConstPtr &fileCtx, QScriptValue scope, - QScriptValue targetObject); - void import(const JsImport &jsImport, QScriptValue scope, QScriptValue targetObject); + void import(const FileContextBaseConstPtr &fileCtx, QScriptValue &targetObject); + void import(const JsImport &jsImport, QScriptValue &targetObject); void clearImportsCache(); void addPropertyRequestedInScript(const Property &property) { @@ -126,7 +126,7 @@ public: void cancel(); private: - Q_INVOKABLE void abort(); + void abort(); void installQbsBuiltins(); void extendJavaScriptBuiltins(); @@ -136,10 +136,7 @@ private: void installConsoleFunction(const QString &name, FunctionWithArgSignature f); void installImportFunctions(); void uninstallImportFunctions(); - QScriptValue importFile(const QString &filePath, const QScriptValue &scope, - QScriptValue *targetObject = nullptr); - void importProgram(const QScriptProgram &program, const QScriptValue &scope, - QScriptValue &targetObject); + void importFile(const QString &filePath, QScriptValue &targetObject); static QScriptValue js_loadExtension(QScriptContext *context, QScriptEngine *qtengine); static QScriptValue js_loadFile(QScriptContext *context, QScriptEngine *qtengine); @@ -161,6 +158,7 @@ private: friend bool operator==(const PropertyCacheKey &lhs, const PropertyCacheKey &rhs); friend uint qHash(const ScriptEngine::PropertyCacheKey &k, uint seed); + ScriptImporter *m_scriptImporter; QHash<JsImport, QScriptValue> m_jsImportCache; bool m_propertyCacheEnabled; QHash<PropertyCacheKey, QVariant> m_propertyCache; diff --git a/src/lib/corelib/language/scriptimporter.cpp b/src/lib/corelib/language/scriptimporter.cpp new file mode 100644 index 000000000..62577b343 --- /dev/null +++ b/src/lib/corelib/language/scriptimporter.cpp @@ -0,0 +1,161 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing +** +** This file is part of the Qt Build Suite. +** +** 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. +** +****************************************************************************/ + +#include "scriptimporter.h" + +#include <parser/qmljsastfwd_p.h> +#include <parser/qmljsastvisitor_p.h> +#include <parser/qmljslexer_p.h> +#include <parser/qmljsparser_p.h> +#include <tools/error.h> + +#include <QScriptValueIterator> + +namespace qbs { +namespace Internal { + +class IdentifierExtractor : private QbsQmlJS::AST::Visitor +{ +public: + void start(QbsQmlJS::AST::Node *node) + { + m_first = true; + m_barrier = false; + m_suffix += QLatin1String("\nreturn {"); + node->accept(this); + m_suffix += QLatin1String("}})()"); + } + + const QString &suffix() const { return m_suffix; } + +private: + bool visit(QbsQmlJS::AST::SourceElements *) override + { + // Only consider the top level of source elements. + if (m_barrier) + return false; + m_barrier = true; + return true; + } + + void endVisit(QbsQmlJS::AST::SourceElements *) override + { + m_barrier = false; + } + + bool visit(QbsQmlJS::AST::FunctionSourceElement *fse) override + { + add(fse->declaration->name); + return false; + } + + bool visit(QbsQmlJS::AST::VariableDeclaration *vd) override + { + add(vd->name); + return false; + } + + void add(const QStringRef &name) + { + if (m_first) { + m_first = false; + m_suffix.reserve(m_suffix.length() + name.length() * 2 + 1); + } else { + m_suffix.reserve(m_suffix.length() + name.length() * 2 + 2); + m_suffix += QLatin1Char(','); + } + m_suffix += name; + m_suffix += QLatin1Char(':'); + m_suffix += name; + } + + bool m_first; + bool m_barrier; + QString m_suffix; +}; + + +ScriptImporter::ScriptImporter(QScriptEngine *scriptEngine) + : m_engine(scriptEngine) +{ +} + +// ### merge with Evaluator::handleEvaluationError +static ErrorInfo errorInfoFromScriptValue(const QScriptValue &value, const QString &filePath) +{ + if (!value.isError()) + return ErrorInfo(value.toString(), CodeLocation(filePath)); + + return ErrorInfo(value.property(QStringLiteral("message")).toString(), + CodeLocation(value.property(QStringLiteral("fileName")).toString(), + value.property(QStringLiteral("lineNumber")).toInt32(), + false)); +} + +void ScriptImporter::importSourceCode(const QString &sourceCode, const QString &filePath, + QScriptValue &targetObject) +{ + Q_ASSERT(targetObject.isObject()); + // The targetObject doesn't get overwritten but enhanced by the contents of the .js file. + // This is necessary for library imports that consist of multiple js files. + + QString &code = m_sourceCodeCache[filePath]; + if (code.isEmpty()) { + QbsQmlJS::Engine engine; + QbsQmlJS::Lexer lexer(&engine); + lexer.setCode(sourceCode, 1, false); + QbsQmlJS::Parser parser(&engine); + if (!parser.parseProgram()) { + throw ErrorInfo(parser.errorMessage(), CodeLocation(filePath, parser.errorLineNumber(), + parser.errorColumnNumber())); + } + + IdentifierExtractor extractor; + extractor.start(parser.rootNode()); + code = QLatin1String("(function(){\n") + sourceCode + extractor.suffix(); + } + + QScriptValue result = m_engine->evaluate(code, filePath, 0); + if (m_engine->hasUncaughtException()) + throw errorInfoFromScriptValue(result, filePath); + copyProperties(result, targetObject); +} + +void ScriptImporter::copyProperties(const QScriptValue &src, QScriptValue &dst) +{ + QScriptValueIterator it(src); + while (it.hasNext()) { + it.next(); + dst.setProperty(it.name(), it.value()); + } +} + +} // namespace Internal +} // namespace qbs diff --git a/src/lib/corelib/language/builtinvalue.h b/src/lib/corelib/language/scriptimporter.h index fb3e470e9..e477961a2 100644 --- a/src/lib/corelib/language/builtinvalue.h +++ b/src/lib/corelib/language/scriptimporter.h @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2015 The Qt Company Ltd. +** Copyright (C) 2016 The Qt Company Ltd. ** Contact: http://www.qt.io/licensing ** ** This file is part of Qbs. @@ -28,38 +28,29 @@ ** ****************************************************************************/ -#ifndef QBS_BUILTINVALUE_H -#define QBS_BUILTINVALUE_H +#ifndef SCRIPTIMPORTER_H +#define SCRIPTIMPORTER_H -#include "value.h" +#include <QHash> +#include <QScriptEngine> namespace qbs { namespace Internal { -class BuiltinValue : public Value +class ScriptImporter { public: - enum Builtin - { - GetEnvFunction, - CurrentEnvFunction - }; - - static BuiltinValuePtr create(Builtin builtin); - - void apply(ValueHandler *handler) { handler->handle(this); } - ValuePtr clone() const; - - Builtin builtin() const { return m_builtin; } - void setBuiltin(const Builtin &builtin) { m_builtin = builtin; } + ScriptImporter(QScriptEngine *scriptEngine); + void importSourceCode(const QString &sourceCode, const QString &filePath, QScriptValue &targetObject); private: - BuiltinValue(Builtin builtin); + static void copyProperties(const QScriptValue &src, QScriptValue &dst); - Builtin m_builtin; + QScriptEngine *m_engine; + QHash<QString, QString> m_sourceCodeCache; }; } // namespace Internal } // namespace qbs -#endif // QBS_BUILTINVALUE_H +#endif // SCRIPTIMPORTER_H diff --git a/src/lib/corelib/language/tst_language.cpp b/src/lib/corelib/language/tst_language.cpp index 6ec955caf..14fe621cd 100644 --- a/src/lib/corelib/language/tst_language.cpp +++ b/src/lib/corelib/language/tst_language.cpp @@ -1949,7 +1949,7 @@ void TestLanguage::wildcards() QVERIFY(product); GroupPtr group; if (useGroup) { - QCOMPARE(product->groups.count(), HostOsInfo::isOsxHost() ? 4 : 3); + QCOMPARE(product->groups.count(), HostOsInfo::isOsxHost() ? 3 : 2); foreach (const GroupPtr &rg, product->groups) { if (rg->name == groupName) { group = rg; @@ -1957,7 +1957,7 @@ void TestLanguage::wildcards() } } } else { - QCOMPARE(product->groups.count(), HostOsInfo::isOsxHost() ? 3 : 2); + QCOMPARE(product->groups.count(), HostOsInfo::isOsxHost() ? 2 : 1); group = product->groups.first(); } QVERIFY(group); diff --git a/src/lib/corelib/language/value.h b/src/lib/corelib/language/value.h index 4373d595f..831e0d066 100644 --- a/src/lib/corelib/language/value.h +++ b/src/lib/corelib/language/value.h @@ -47,8 +47,7 @@ public: { JSSourceValueType, ItemValueType, - VariantValueType, - BuiltinValueType + VariantValueType }; Value(Type t, bool createdByPropertiesBlock); @@ -80,7 +79,6 @@ public: virtual void handle(JSSourceValue *value) = 0; virtual void handle(ItemValue *value) = 0; virtual void handle(VariantValue *value) = 0; - virtual void handle(BuiltinValue *value) = 0; }; class JSSourceValue : public Value diff --git a/src/lib/corelib/qbs.h b/src/lib/corelib/qbs.h index 2e370e5a2..4aa133fcf 100644 --- a/src/lib/corelib/qbs.h +++ b/src/lib/corelib/qbs.h @@ -48,5 +48,6 @@ #include "tools/settings.h" #include "tools/settingsmodel.h" #include "tools/setupprojectparameters.h" +#include "tools/toolchains.h" #endif // QBS_H diff --git a/src/lib/corelib/tools/commandechomode.cpp b/src/lib/corelib/tools/commandechomode.cpp index 8f09bdac8..268243af5 100644 --- a/src/lib/corelib/tools/commandechomode.cpp +++ b/src/lib/corelib/tools/commandechomode.cpp @@ -36,6 +36,8 @@ * \value CommandEchoModeSilent Indicates that no output will be printed. * \value CommandEchoModeSummary Indicates that descriptions will be printed. * \value CommandEchoModeCommandLine Indidcates that full command line invocations will be printed. + * \value CommandEchoModeCommandLineWithEnvironment Indidcates that full command line invocations, + * including environment variables, will be printed. */ namespace qbs { @@ -54,6 +56,8 @@ QString commandEchoModeName(CommandEchoMode mode) return QLatin1String("summary"); case CommandEchoModeCommandLine: return QLatin1String("command-line"); + case CommandEchoModeCommandLineWithEnvironment: + return QLatin1String("command-line-with-environment"); default: break; } diff --git a/src/lib/corelib/tools/commandechomode.h b/src/lib/corelib/tools/commandechomode.h index f38c6a9ab..e820b15a8 100644 --- a/src/lib/corelib/tools/commandechomode.h +++ b/src/lib/corelib/tools/commandechomode.h @@ -41,7 +41,8 @@ enum CommandEchoMode { CommandEchoModeSilent, CommandEchoModeSummary, CommandEchoModeCommandLine, - CommandEchoModeLast = CommandEchoModeCommandLine + CommandEchoModeCommandLineWithEnvironment, + CommandEchoModeLast = CommandEchoModeCommandLineWithEnvironment }; QBS_EXPORT CommandEchoMode defaultCommandEchoMode(); diff --git a/src/lib/corelib/tools/persistence.cpp b/src/lib/corelib/tools/persistence.cpp index 60a7ada09..b2afb89ca 100644 --- a/src/lib/corelib/tools/persistence.cpp +++ b/src/lib/corelib/tools/persistence.cpp @@ -41,7 +41,7 @@ namespace qbs { namespace Internal { -static const char QBS_PERSISTENCE_MAGIC[] = "QBSPERSISTENCE-85"; +static const char QBS_PERSISTENCE_MAGIC[] = "QBSPERSISTENCE-87"; PersistentPool::PersistentPool(const Logger &logger) : m_logger(logger) { diff --git a/src/lib/corelib/tools/qbsassert.h b/src/lib/corelib/tools/qbsassert.h index 2e6afcb91..e912ffb56 100644 --- a/src/lib/corelib/tools/qbsassert.h +++ b/src/lib/corelib/tools/qbsassert.h @@ -33,11 +33,6 @@ #include "qbs_export.h" -// TODO: Remove once we require 5.3. -#ifndef Q_NORETURN -#define Q_NORETURN -#endif - namespace qbs { namespace Internal { diff --git a/src/lib/corelib/tools/scripttools.cpp b/src/lib/corelib/tools/scripttools.cpp index e627bacf3..83175cd7f 100644 --- a/src/lib/corelib/tools/scripttools.cpp +++ b/src/lib/corelib/tools/scripttools.cpp @@ -139,5 +139,17 @@ QVariant getConfigProperty(const QVariantMap &cfg, const QStringList &name) return getConfigProperty(cfg.value(name.first()).toMap(), name.mid(1)); } +TemporaryGlobalObjectSetter::TemporaryGlobalObjectSetter(const QScriptValue &object) +{ + QScriptEngine *engine = object.engine(); + m_oldGlobalObject = engine->globalObject(); + engine->setGlobalObject(object); +} + +TemporaryGlobalObjectSetter::~TemporaryGlobalObjectSetter() +{ + m_oldGlobalObject.engine()->setGlobalObject(m_oldGlobalObject); +} + } // namespace Internal } // namespace qbs diff --git a/src/lib/corelib/tools/scripttools.h b/src/lib/corelib/tools/scripttools.h index fa6884490..774c4ac2e 100644 --- a/src/lib/corelib/tools/scripttools.h +++ b/src/lib/corelib/tools/scripttools.h @@ -68,27 +68,6 @@ QScriptValue toScriptValue(QScriptEngine *scriptEngine, const C &container) void setConfigProperty(QVariantMap &cfg, const QStringList &name, const QVariant &value); QVariant getConfigProperty(const QVariantMap &cfg, const QStringList &name); -/** - * @brief push/pop a scope on a QScriptContext the RAII way. - */ -class ScriptContextScopePusher -{ -public: - ScriptContextScopePusher(QScriptContext *scriptContext, const QScriptValue &value) - : m_scriptContext(scriptContext) - { - m_scriptContext->pushScope(value); - } - - ~ScriptContextScopePusher() - { - m_scriptContext->popScope(); - } - -private: - QScriptContext *m_scriptContext; -}; - template <class T> void attachPointerTo(QScriptValue &scriptValue, T *ptr) { @@ -104,6 +83,16 @@ T *attachedPointer(const QScriptValue &scriptValue) return reinterpret_cast<T *>(ptr); } +class TemporaryGlobalObjectSetter +{ +public: + TemporaryGlobalObjectSetter(const QScriptValue &object); + ~TemporaryGlobalObjectSetter(); + +private: + QScriptValue m_oldGlobalObject; +}; + } // namespace Internal } // namespace qbs diff --git a/src/lib/corelib/tools/toolchains.cpp b/src/lib/corelib/tools/toolchains.cpp new file mode 100644 index 000000000..e2c67f1e8 --- /dev/null +++ b/src/lib/corelib/tools/toolchains.cpp @@ -0,0 +1,95 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing +** +** This file is part of the Qt Build Suite. +** +** 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. +** +****************************************************************************/ + +#include "toolchains.h" +#include <QMap> +#include <QSet> + +namespace qbs { + +QStringList canonicalToolchain(const QStringList &toolchain) +{ + static const QStringList knownToolchains { + QStringLiteral("xcode"), + QStringLiteral("clang"), + QStringLiteral("llvm"), + QStringLiteral("mingw"), + QStringLiteral("gcc"), + QStringLiteral("msvc") + }; + + // Canonicalize each toolchain in the toolchain list, + // which gets us the aggregate canonicalized (unsorted) list + QStringList toolchains; + for (const QString &toolchainName : toolchain) + toolchains << canonicalToolchain(toolchainName); + toolchains.removeDuplicates(); + + // Find all known toolchains in the canonicalized list, + // removing them from the main list as we go. + QStringList usedKnownToolchains; + for (int i = 0; i < toolchains.size(); ++i) { + if (knownToolchains.contains(toolchains[i])) { + usedKnownToolchains << toolchains[i]; + toolchains.removeAt(i--); + } + } + + // Sort the list of known toolchains into their canonical order. + std::sort(usedKnownToolchains.begin(), usedKnownToolchains.end(), []( + const QString &a, + const QString &b) { + return knownToolchains.indexOf(a) < knownToolchains.indexOf(b); + }); + + // Re-add the known toolchains to the main list (the custom ones go first). + toolchains << usedKnownToolchains; + + // The toolchain list still needs further validation as it may contain mututally exclusive + // toolchain types (for example, llvm and msvc). + return toolchains; +} + +QStringList canonicalToolchain(const QString &name) +{ + const QString &toolchainName = name.toLower(); + QStringList toolchains(toolchainName); + if (toolchainName == QLatin1String("xcode")) + toolchains << canonicalToolchain(QLatin1String("clang")); + else if (toolchainName == QLatin1String("clang")) + toolchains << canonicalToolchain(QLatin1String("llvm")); + else if (toolchainName == QLatin1String("llvm") || + toolchainName == QLatin1String("mingw")) { + toolchains << canonicalToolchain(QLatin1String("gcc")); + } + return toolchains; +} + +} // namespace qbs diff --git a/src/lib/corelib/language/builtinvalue.cpp b/src/lib/corelib/tools/toolchains.h index 3fe201190..d66908148 100644 --- a/src/lib/corelib/language/builtinvalue.cpp +++ b/src/lib/corelib/tools/toolchains.h @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2015 The Qt Company Ltd. +** Copyright (C) 2016 The Qt Company Ltd. ** Contact: http://www.qt.io/licensing ** ** This file is part of Qbs. @@ -27,27 +27,17 @@ ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ****************************************************************************/ +#ifndef QBS_TOOLCHAINS_H +#define QBS_TOOLCHAINS_H -#include "builtinvalue.h" +#include "qbs_export.h" +#include <QStringList> namespace qbs { -namespace Internal { -BuiltinValue::BuiltinValue(Builtin builtin) - : Value(Value::BuiltinValueType, false) - , m_builtin(builtin) -{ -} +QBS_EXPORT QStringList canonicalToolchain(const QStringList &toolchain); +QBS_EXPORT QStringList canonicalToolchain(const QString &toolchainName); -BuiltinValuePtr BuiltinValue::create(Builtin builtin) -{ - return BuiltinValuePtr(new BuiltinValue(builtin)); -} - -ValuePtr BuiltinValue::clone() const -{ - return BuiltinValuePtr(new BuiltinValue(*this)); -} - -} // namespace Internal } // namespace qbs + +#endif // Include guard. diff --git a/src/lib/corelib/tools/tools.pri b/src/lib/corelib/tools/tools.pri index 618b08bb3..915ac750b 100644 --- a/src/lib/corelib/tools/tools.pri +++ b/src/lib/corelib/tools/tools.pri @@ -27,6 +27,7 @@ HEADERS += \ $$PWD/projectgeneratormanager.h \ $$PWD/propertyfinder.h \ $$PWD/shellutils.h \ + $$PWD/toolchains.h \ $$PWD/hostosinfo.h \ $$PWD/buildoptions.h \ $$PWD/installoptions.h \ @@ -71,6 +72,7 @@ SOURCES += \ $$PWD/qbsassert.cpp \ $$PWD/qttools.cpp \ $$PWD/settingscreator.cpp \ + $$PWD/toolchains.cpp \ $$PWD/version.cpp \ $$PWD/visualstudioversioninfo.cpp @@ -110,7 +112,8 @@ qbs_enable_unit_tests { $$PWD/generateoptions.h \ $$PWD/generatorpluginmanager.h \ $$PWD/installoptions.h \ - $$PWD/setupprojectparameters.h + $$PWD/setupprojectparameters.h \ + $$PWD/toolchains.h tools_headers.path = $${QBS_INSTALL_PREFIX}/include/qbs/tools INSTALLS += tools_headers } diff --git a/src/lib/qtprofilesetup/qtprofilesetup.qbs b/src/lib/qtprofilesetup/qtprofilesetup.qbs index 8d340ac9a..7f739e10f 100644 --- a/src/lib/qtprofilesetup/qtprofilesetup.qbs +++ b/src/lib/qtprofilesetup/qtprofilesetup.qbs @@ -11,7 +11,7 @@ QbsLibrary { "qtprofilesetup.h", "use_installed_qtprofilesetup.pri", ] - qbs.install: project.installApiHeaders + qbs.install: qbsbuildconfig.installApiHeaders qbs.installDir: headerInstallPrefix } diff --git a/src/lib/qtprofilesetup/templates/QtPlugin.qbs b/src/lib/qtprofilesetup/templates/QtPlugin.qbs index 25c30101c..9a916f695 100644 --- a/src/lib/qtprofilesetup/templates/QtPlugin.qbs +++ b/src/lib/qtprofilesetup/templates/QtPlugin.qbs @@ -7,8 +7,9 @@ QtModule { property string className - Transformer { + Rule { condition: isStaticLibrary + multiplex: true Artifact { filePath: product.targetName + "_qt_plugin_import_" + parent.parent.qtModuleName + ".cpp" diff --git a/src/lib/qtprofilesetup/templates/core.qbs b/src/lib/qtprofilesetup/templates/core.qbs index 57aef089a..bdde3c77e 100644 --- a/src/lib/qtprofilesetup/templates/core.qbs +++ b/src/lib/qtprofilesetup/templates/core.qbs @@ -75,7 +75,6 @@ Module { property string qdocOutputDir: FileInfo.joinPaths(generatedFilesDir, "html") property string qmDir: product.destinationDirectory property string qmBaseName: product.targetName - property string qmFilesDir: qmDir // TODO: Remove in 1.6 property bool lreleaseMultiplexMode: false cpp.defines: { diff --git a/src/lib/qtprofilesetup/templates/moc.js b/src/lib/qtprofilesetup/templates/moc.js index 3728d9fe0..6c4d74f27 100644 --- a/src/lib/qtprofilesetup/templates/moc.js +++ b/src/lib/qtprofilesetup/templates/moc.js @@ -39,9 +39,13 @@ function args(product, input, outputFileName) var includePaths = ModUtils.modulePropertiesFromArtifacts(product, [input], 'cpp', 'includePaths'); includePaths = includePaths.uniqueConcat(ModUtils.modulePropertiesFromArtifacts( product, [input], 'cpp', 'systemIncludePaths')); + includePaths = includePaths.uniqueConcat(ModUtils.modulePropertiesFromArtifacts( + product, [input], 'cpp', 'compilerIncludePaths')); var frameworkPaths = product.moduleProperties("cpp", "frameworkPaths"); frameworkPaths = frameworkPaths.uniqueConcat( product.moduleProperties("cpp", "systemFrameworkPaths")); + frameworkPaths = frameworkPaths.uniqueConcat( + product.moduleProperties("cpp", "compilerFrameworkPaths")); var args = []; args = args.concat( defines.map(function(item) { return '-D' + item; }), diff --git a/src/plugins/scanner/scannerplugin.qbs b/src/plugins/scanner/scannerplugin.qbs index 78448a145..2b34926b6 100644 --- a/src/plugins/scanner/scannerplugin.qbs +++ b/src/plugins/scanner/scannerplugin.qbs @@ -3,12 +3,13 @@ import qbs 1.0 DynamicLibrary { Depends { name: "cpp" } Depends { name: "Qt.core" } + Depends { name: "qbsbuildconfig" } cpp.cxxLanguageVersion: "c++11" - destinationDirectory: project.libDirName + "/qbs/plugins" + destinationDirectory: qbsbuildconfig.libDirName + "/qbs/plugins" Group { fileTagsFilter: ["dynamiclibrary"] qbs.install: true - qbs.installDir: project.pluginsInstallDir + "/qbs/plugins" + qbs.installDir: qbsbuildconfig.pluginsInstallDir + "/qbs/plugins" } bundle.isBundle: false } diff --git a/tests/auto/api/api.qbs b/tests/auto/api/api.qbs index 36ed6eb00..68e7cde46 100644 --- a/tests/auto/api/api.qbs +++ b/tests/auto/api/api.qbs @@ -5,10 +5,10 @@ QbsAutotest { files: ["../shared.h", "tst_api.h", "tst_api.cpp"] cpp.defines: base.concat([ 'SRCDIR="' + path + '"', - 'QBS_RELATIVE_LIBEXEC_PATH="' + project.relativeLibexecPath + '"', - 'QBS_RELATIVE_SEARCH_PATH="' + project.relativeSearchPath + '"', - 'QBS_RELATIVE_PLUGINS_PATH="' + project.relativePluginsPath + '"' - ]).concat(project.enableProjectFileUpdates ? ["QBS_ENABLE_PROJECT_FILE_UPDATES"] : []) + 'QBS_RELATIVE_LIBEXEC_PATH="' + qbsbuildconfig.relativeLibexecPath + '"', + 'QBS_RELATIVE_SEARCH_PATH="' + qbsbuildconfig.relativeSearchPath + '"', + 'QBS_RELATIVE_PLUGINS_PATH="' + qbsbuildconfig.relativePluginsPath + '"' + ]).concat(qbsbuildconfig.enableProjectFileUpdates ? ["QBS_ENABLE_PROJECT_FILE_UPDATES"] : []) Group { name: "testdata" diff --git a/tests/auto/api/testdata/explicitly-depends-on/project.qbs b/tests/auto/api/testdata/explicitly-depends-on/project.qbs index 3db802717..eda4e27df 100644 --- a/tests/auto/api/testdata/explicitly-depends-on/project.qbs +++ b/tests/auto/api/testdata/explicitly-depends-on/project.qbs @@ -8,7 +8,8 @@ Product { patterns: "*.txt" fileTags: ["txt"] } - Transformer { + Rule { + multiplex: true explicitlyDependsOn: "txt" Artifact { filePath: "test.mytype" diff --git a/tests/auto/api/testdata/infinite-loop-js/infinite-loop.qbs b/tests/auto/api/testdata/infinite-loop-js/infinite-loop.qbs index e98fda511..592c602ec 100644 --- a/tests/auto/api/testdata/infinite-loop-js/infinite-loop.qbs +++ b/tests/auto/api/testdata/infinite-loop-js/infinite-loop.qbs @@ -2,7 +2,8 @@ import qbs Product { type: "mytype" - Transformer { + Rule { + multiplex: true Artifact { filePath: "output.txt" fileTags: "mytype" diff --git a/tests/auto/api/testdata/qt5-plugin/project.qbs b/tests/auto/api/testdata/qt5-plugin/project.qbs index 3e0dd1c6a..834c386ad 100644 --- a/tests/auto/api/testdata/qt5-plugin/project.qbs +++ b/tests/auto/api/testdata/qt5-plugin/project.qbs @@ -14,19 +14,24 @@ DynamicLibrary { files: [ "echoplugin.h", "echoplugin.cpp", - "echoplugin.json.source" ] } Group { + condition: Qt.core.versionMajor >= 5 + files: ["echoplugin.json.source"] + fileTags: ["json_in"] + } + + Group { condition: Qt.core.versionMajor < 5 files: "echoplugin_dummy.cpp" } cpp.includePaths: buildDirectory - Transformer { + Rule { condition: Qt.core.versionMajor >= 5 - inputs: ["echoplugin.json.source"] + inputs: ["json_in"] Artifact { filePath: "echoplugin.json" fileTags: ["qt_plugin_metadata"] diff --git a/tests/auto/api/testdata/rule-conflict/main.cpp b/tests/auto/api/testdata/rule-conflict/main.cpp new file mode 100644 index 000000000..237c8ce18 --- /dev/null +++ b/tests/auto/api/testdata/rule-conflict/main.cpp @@ -0,0 +1 @@ +int main() {} diff --git a/tests/auto/api/testdata/rule-conflict/pch1.h b/tests/auto/api/testdata/rule-conflict/pch1.h new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/tests/auto/api/testdata/rule-conflict/pch1.h diff --git a/tests/auto/api/testdata/rule-conflict/pch2.h b/tests/auto/api/testdata/rule-conflict/pch2.h new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/tests/auto/api/testdata/rule-conflict/pch2.h diff --git a/tests/auto/api/testdata/rule-conflict/rule-conflict.qbs b/tests/auto/api/testdata/rule-conflict/rule-conflict.qbs new file mode 100644 index 000000000..d2e1b9144 --- /dev/null +++ b/tests/auto/api/testdata/rule-conflict/rule-conflict.qbs @@ -0,0 +1,11 @@ +import qbs + +CppApplication { + cpp.useCxxPrecompiledHeader: true + files: "main.cpp" + Group { + name: "pch files" + files: ["pch1.h", "pch2.h"] + fileTags: "cpp_pch_src" + } +} diff --git a/tests/auto/api/testdata/transformers/transformers.qbs b/tests/auto/api/testdata/transformers/transformers.qbs index b0884ffa6..1e5a0ec4a 100644 --- a/tests/auto/api/testdata/transformers/transformers.qbs +++ b/tests/auto/api/testdata/transformers/transformers.qbs @@ -9,12 +9,17 @@ Project { name: "HelloWorld" type: "application" consoleApplication: true - files: ["main.cpp"] + + Group { + files: ["main.cpp"] + fileTags: ["main"] + } Depends { name: "cpp" } - Transformer { + Rule { // no inputs -> just a generator + multiplex: true Artifact { filePath: "foo.txt" fileTags: "text" @@ -35,7 +40,8 @@ Project { } } - Transformer { + Rule { + multiplex: true // no inputs -> just a generator Artifact { filePath: "foo.xml" @@ -60,8 +66,8 @@ Project { } } - Transformer { - inputs: ["main.cpp"] // will be taken from the source dir + Rule { + inputs: ["main"] Artifact { filePath: "bar.txt" fileTags: "text" diff --git a/tests/auto/api/tst_api.cpp b/tests/auto/api/tst_api.cpp index 5ba952490..14b555b2a 100644 --- a/tests/auto/api/tst_api.cpp +++ b/tests/auto/api/tst_api.cpp @@ -36,6 +36,7 @@ #include <qbs.h> #include <tools/fileinfo.h> #include <tools/hostosinfo.h> +#include <tools/toolchains.h> #include <QCoreApplication> #include <QDir> @@ -76,7 +77,6 @@ class BuildDescriptionReceiver : public QObject public: QString descriptions; -private slots: void handleDescription(const QString &, const QString &description) { descriptions += description; } @@ -89,7 +89,6 @@ public: QString output; QVector<qbs::ProcessResult> results; -private slots: void handleProcessResult(const qbs::ProcessResult &result) { results << result; output += result.stdErr().join(QLatin1Char('\n')); @@ -103,7 +102,6 @@ class TaskReceiver : public QObject public: QString taskDescriptions; -private slots: void handleTaskStart(const QString &task) { taskDescriptions += task; } }; @@ -122,10 +120,10 @@ static bool waitForFinished(qbs::AbstractJob *job, int timeout = 0) if (job->state() == qbs::AbstractJob::StateFinished) return true; QEventLoop loop; - QObject::connect(job, SIGNAL(finished(bool,qbs::AbstractJob*)), &loop, SLOT(quit())); + QObject::connect(job, &qbs::AbstractJob::finished, &loop, &QEventLoop::quit); if (timeout > 0) { QTimer timer; - QObject::connect(&timer, SIGNAL(timeout()), &loop, SLOT(quit())); + QObject::connect(&timer, &QTimer::timeout, &loop, &QEventLoop::quit); timer.setSingleShot(true); timer.start(timeout); loop.exec(); @@ -330,9 +328,6 @@ void TestApi::buildProject_data() QTest::newRow("link static libs") << QString("link-static-lib") << relativeExecutableFilePath("HelloWorld"); - QTest::newRow("precompiled header") // TODO: Remove in 1.6 - << QString("precompiled-header") - << relativeExecutableFilePath("MyApp"); QTest::newRow("precompiled header new") << QString("precompiled-header-new") << relativeExecutableFilePath("MyApp"); @@ -420,8 +415,8 @@ void TestApi::buildSingleFile() m_logSink->setLogLevel(qbs::LoggerMaxLevel); QScopedPointer<qbs::BuildJob> buildJob(project.buildAllProducts(options)); BuildDescriptionReceiver receiver; - connect(buildJob.data(), SIGNAL(reportCommandDescription(QString,QString)), &receiver, - SLOT(handleDescription(QString,QString))); + connect(buildJob.data(), &qbs::BuildJob::reportCommandDescription, &receiver, + &BuildDescriptionReceiver::handleDescription); waitForFinished(buildJob.data()); QVERIFY2(!buildJob->error().hasError(), qPrintable(buildJob->error().toString())); QCOMPARE(receiver.descriptions.count("compiling"), 1); @@ -429,6 +424,96 @@ void TestApi::buildSingleFile() qPrintable(receiver.descriptions)); } +void TestApi::canonicalToolchainList() +{ + // All the known toolchain lists should be equal + QCOMPARE(qbs::canonicalToolchain(QStringList({"xcode", "clang", "llvm", "gcc"})), + QStringList({"xcode", "clang", "llvm", "gcc"})); + QCOMPARE(qbs::canonicalToolchain(QStringList({"clang", "llvm", "gcc"})), + QStringList({"clang", "llvm", "gcc"})); + QCOMPARE(qbs::canonicalToolchain(QStringList({"llvm", "gcc"})), + QStringList({"llvm", "gcc"})); + QCOMPARE(qbs::canonicalToolchain(QStringList({"mingw", "gcc"})), + QStringList({"mingw", "gcc"})); + QCOMPARE(qbs::canonicalToolchain(QStringList({"gcc"})), + QStringList({"gcc"})); + QCOMPARE(qbs::canonicalToolchain(QStringList({"msvc"})), + QStringList({"msvc"})); + + // Single names should canonicalize to the known lists + QCOMPARE(qbs::canonicalToolchain(QStringList({"xcode"})), + QStringList({"xcode", "clang", "llvm", "gcc"})); + QCOMPARE(qbs::canonicalToolchain(QStringList({"clang"})), + QStringList({"clang", "llvm", "gcc"})); + QCOMPARE(qbs::canonicalToolchain(QStringList({"llvm"})), + QStringList({"llvm", "gcc"})); + QCOMPARE(qbs::canonicalToolchain(QStringList({"mingw"})), + QStringList({"mingw", "gcc"})); + QCOMPARE(qbs::canonicalToolchain(QStringList({"gcc"})), + QStringList({"gcc"})); + QCOMPARE(qbs::canonicalToolchain(QStringList({"msvc"})), + QStringList({"msvc"})); + + // Missing some in the middle + QCOMPARE(qbs::canonicalToolchain(QStringList({"xcode", "llvm", "gcc"})), + QStringList({"xcode", "clang", "llvm", "gcc"})); + QCOMPARE(qbs::canonicalToolchain(QStringList({"xcode", "clang", "gcc"})), + QStringList({"xcode", "clang", "llvm", "gcc"})); + QCOMPARE(qbs::canonicalToolchain(QStringList({"xcode", "gcc"})), + QStringList({"xcode", "clang", "llvm", "gcc"})); + QCOMPARE(qbs::canonicalToolchain(QStringList({"clang", "llvm"})), + QStringList({"clang", "llvm", "gcc"})); + QCOMPARE(qbs::canonicalToolchain(QStringList({"clang", "gcc"})), + QStringList({"clang", "llvm", "gcc"})); + + // Sorted wrong, missing some in the middle + QCOMPARE(qbs::canonicalToolchain(QStringList({"gcc", "llvm", "clang", "xcode"})), + QStringList({"xcode", "clang", "llvm", "gcc"})); + QCOMPARE(qbs::canonicalToolchain(QStringList({"clang", "gcc", "llvm", "xcode"})), + QStringList({"xcode", "clang", "llvm", "gcc"})); + QCOMPARE(qbs::canonicalToolchain(QStringList({"llvm", "clang", "xcode", "gcc"})), + QStringList({"xcode", "clang", "llvm", "gcc"})); + QCOMPARE(qbs::canonicalToolchain(QStringList({"gcc", "llvm", "clang"})), + QStringList({"clang", "llvm", "gcc"})); + QCOMPARE(qbs::canonicalToolchain(QStringList({"gcc", "clang", "xcode"})), + QStringList({"xcode", "clang", "llvm", "gcc"})); + QCOMPARE(qbs::canonicalToolchain(QStringList({"gcc", "llvm"})), + QStringList({"llvm", "gcc"})); + QCOMPARE(qbs::canonicalToolchain(QStringList({"gcc", "mingw"})), + QStringList({"mingw", "gcc"})); + + // Duplicates + QCOMPARE(qbs::canonicalToolchain(QStringList({"gcc", "llvm", "clang", "xcode", "xcode", + "xcode"})), + QStringList({"xcode", "clang", "llvm", "gcc"})); + QCOMPARE(qbs::canonicalToolchain(QStringList({"clang", "gcc", "llvm", "clang", "xcode"})), + QStringList({"xcode", "clang", "llvm", "gcc"})); + QCOMPARE(qbs::canonicalToolchain(QStringList({"llvm", "clang", "clang", "xcode", "xcode", + "gcc"})), + QStringList({"xcode", "clang", "llvm", "gcc"})); + QCOMPARE(qbs::canonicalToolchain(QStringList({"llvm", "clang", "gcc", "llvm", "clang"})), + QStringList({"clang", "llvm", "gcc"})); + QCOMPARE(qbs::canonicalToolchain(QStringList({"xcode", "gcc", "clang", "gcc", "clang", + "xcode"})), + QStringList({"xcode", "clang", "llvm", "gcc"})); + QCOMPARE(qbs::canonicalToolchain(QStringList({"llvm", "gcc", "llvm", "llvm"})), + QStringList({"llvm", "gcc"})); + QCOMPARE(qbs::canonicalToolchain(QStringList({"gcc", "gcc", "gcc", "mingw"})), + QStringList({"mingw", "gcc"})); + + // Custom insanity + QCOMPARE(qbs::canonicalToolchain( + QStringList({"crazy", "gcc", "llvm", "clang", "xcode", "insane"})), + QStringList({"crazy", "insane", "xcode", "clang", "llvm", "gcc"})); + QCOMPARE(qbs::canonicalToolchain( + QStringList({"crazy", "gcc", "llvm", "clang", "xcode", "insane", "crazy"})), + QStringList({"crazy", "insane", "xcode", "clang", "llvm", "gcc"})); + QCOMPARE(qbs::canonicalToolchain( + QStringList({"crazy", "insane", "gcc", "trade", "llvm", "clang", "xcode", + "insane", "mark", "crazy"})), + QStringList({"crazy", "insane", "trade", "mark", "xcode", "clang", "llvm", "gcc"})); +} + void TestApi::checkOutputs() { QFETCH(bool, check); @@ -672,8 +757,8 @@ void TestApi::changeContent() BuildDescriptionReceiver rcvr; QScopedPointer<qbs::BuildJob> buildJob(project.buildAllProducts(buildOptions, defaultProducts(), this)); - connect(buildJob.data(), SIGNAL(reportCommandDescription(QString,QString)), &rcvr, - SLOT(handleDescription(QString,QString))); + connect(buildJob.data(), &qbs::BuildJob::reportCommandDescription, + &rcvr, &BuildDescriptionReceiver::handleDescription); waitForFinished(buildJob.data()); QVERIFY2(!buildJob->error().hasError(), qPrintable(buildJob->error().toString())); QVERIFY(rcvr.descriptions.contains("compiling file.cpp")); @@ -702,8 +787,8 @@ void TestApi::changeContent() // Now try building again and check if the newly resolved product behaves the same way. buildJob.reset(project.buildAllProducts(buildOptions, defaultProducts(), this)); - connect(buildJob.data(), SIGNAL(reportCommandDescription(QString,QString)), &rcvr, - SLOT(handleDescription(QString,QString))); + connect(buildJob.data(), &qbs::BuildJob::reportCommandDescription, + &rcvr, &BuildDescriptionReceiver::handleDescription); waitForFinished(buildJob.data()); QVERIFY2(!buildJob->error().hasError(), qPrintable(buildJob->error().toString())); QVERIFY(rcvr.descriptions.contains("compiling file.cpp")); @@ -741,8 +826,8 @@ void TestApi::changeContent() projectData = project.projectData(); rcvr.descriptions.clear(); buildJob.reset(project.buildAllProducts(buildOptions, defaultProducts(), this)); - connect(buildJob.data(), SIGNAL(reportCommandDescription(QString,QString)), &rcvr, - SLOT(handleDescription(QString,QString))); + connect(buildJob.data(), &qbs::BuildJob::reportCommandDescription, + &rcvr, &BuildDescriptionReceiver::handleDescription); waitForFinished(buildJob.data()); QVERIFY2(!buildJob->error().hasError(), qPrintable(buildJob->error().toString())); QVERIFY(rcvr.descriptions.contains("compiling main.cpp")); @@ -1083,7 +1168,7 @@ void TestApi::infiniteLoopBuilding() QVERIFY2(!setupJob->error().hasError(), qPrintable(setupJob->error().toString())); qbs::Project project = setupJob->project(); const QScopedPointer<qbs::BuildJob> buildJob(project.buildAllProducts(qbs::BuildOptions())); - QTimer::singleShot(1000, buildJob.data(), SLOT(cancel())); + QTimer::singleShot(1000, buildJob.data(), &qbs::AbstractJob::cancel); QVERIFY(waitForFinished(buildJob.data(), 600000)); } @@ -1100,7 +1185,7 @@ void TestApi::infiniteLoopResolving() = defaultSetupParameters("infinite-loop-resolving/project.qbs"); QScopedPointer<qbs::SetupProjectJob> setupJob(qbs::Project().setupProject(setupParams, m_logSink, 0)); - QTimer::singleShot(1000, setupJob.data(), SLOT(cancel())); + QTimer::singleShot(1000, setupJob.data(), &qbs::AbstractJob::cancel); QVERIFY(waitForFinished(setupJob.data(), 600000)); QVERIFY2(setupJob->error().toString().toLower().contains("cancel"), qPrintable(setupJob->error().toString())); @@ -1743,6 +1828,15 @@ void TestApi::resolveProjectDryRun_data() return resolveProject_data(); } +void TestApi::ruleConflict() +{ + const qbs::ErrorInfo errorInfo = doBuildProject("rule-conflict/rule-conflict.qbs"); + QVERIFY(errorInfo.hasError()); + const QString errorString = errorInfo.toString(); + QVERIFY2(errorString.contains("conflict") && errorString.contains("pch1.h") + && errorString.contains("pch2.h"), qPrintable(errorString)); +} + void TestApi::softDependency() { const qbs::ErrorInfo errorInfo = doBuildProject("soft-dependency/project.qbs"); @@ -1893,9 +1987,10 @@ void TestApi::uic() } -qbs::ErrorInfo TestApi::doBuildProject(const QString &projectFilePath, - QObject *buildDescriptionReceiver, QObject *procResultReceiver, QObject *taskReceiver, - const qbs::BuildOptions &options, const QVariantMap overriddenValues) +qbs::ErrorInfo TestApi::doBuildProject( + const QString &projectFilePath, BuildDescriptionReceiver *buildDescriptionReceiver, + ProcessResultReceiver *procResultReceiver, TaskReceiver *taskReceiver, + const qbs::BuildOptions &options, const QVariantMap overriddenValues) { qbs::SetupProjectParameters params = defaultSetupParameters(projectFilePath); params.setOverriddenValues(overriddenValues); @@ -1903,20 +1998,20 @@ qbs::ErrorInfo TestApi::doBuildProject(const QString &projectFilePath, const QScopedPointer<qbs::SetupProjectJob> setupJob(qbs::Project().setupProject(params, m_logSink, 0)); if (taskReceiver) { - connect(setupJob.data(), SIGNAL(taskStarted(QString,int,qbs::AbstractJob*)), taskReceiver, - SLOT(handleTaskStart(QString))); + connect(setupJob.data(), &qbs::AbstractJob::taskStarted, + taskReceiver, &TaskReceiver::handleTaskStart); } waitForFinished(setupJob.data()); if (setupJob->error().hasError()) return setupJob->error(); const QScopedPointer<qbs::BuildJob> buildJob(setupJob->project().buildAllProducts(options)); if (buildDescriptionReceiver) { - connect(buildJob.data(), SIGNAL(reportCommandDescription(QString,QString)), - buildDescriptionReceiver, SLOT(handleDescription(QString,QString))); + connect(buildJob.data(), &qbs::BuildJob::reportCommandDescription, + buildDescriptionReceiver, &BuildDescriptionReceiver::handleDescription); } if (procResultReceiver) { - connect(buildJob.data(), SIGNAL(reportProcessResult(qbs::ProcessResult)), - procResultReceiver, SLOT(handleProcessResult(qbs::ProcessResult))); + connect(buildJob.data(), &qbs::BuildJob::reportProcessResult, + procResultReceiver, &ProcessResultReceiver::handleProcessResult); } waitForFinished(buildJob.data()); return buildJob->error(); diff --git a/tests/auto/api/tst_api.h b/tests/auto/api/tst_api.h index 1644bd9ec..24f8f6f18 100644 --- a/tests/auto/api/tst_api.h +++ b/tests/auto/api/tst_api.h @@ -41,7 +41,10 @@ class ErrorInfo; class SetupProjectParameters; } +class BuildDescriptionReceiver; class LogSink; +class ProcessResultReceiver; +class TaskReceiver; class TestApi : public QObject { @@ -63,6 +66,7 @@ private slots: void buildProjectDryRun(); void buildProjectDryRun_data(); void buildSingleFile(); + void canonicalToolchainList(); #ifdef QBS_ENABLE_PROJECT_FILE_UPDATES void changeContent(); #endif @@ -116,6 +120,7 @@ private slots: void resolveProject_data(); void resolveProjectDryRun(); void resolveProjectDryRun_data(); + void ruleConflict(); void softDependency(); void sourceFileInBuildDir(); void subProjects(); @@ -128,9 +133,9 @@ private slots: private: qbs::SetupProjectParameters defaultSetupParameters(const QString &projectFilePath) const; qbs::ErrorInfo doBuildProject(const QString &projectFilePath, - QObject *buildDescriptionReceiver = 0, - QObject *procResultReceiver = 0, - QObject *taskReceiver = 0, + BuildDescriptionReceiver *buildDescriptionReceiver = 0, + ProcessResultReceiver *procResultReceiver = 0, + TaskReceiver *taskReceiver = 0, const qbs::BuildOptions &options = qbs::BuildOptions(), const QVariantMap overriddenValues = QVariantMap()); diff --git a/tests/auto/auto.qbs b/tests/auto/auto.qbs index 5e09463f4..aa5b3ff49 100644 --- a/tests/auto/auto.qbs +++ b/tests/auto/auto.qbs @@ -5,12 +5,9 @@ Project { references: [ "api/api.qbs", "blackbox/blackbox.qbs", - "cmdlineparser/cmdlineparser.qbs" - ].concat(unitTests) - - property pathList unitTests: enableUnitTests ? [ "buildgraph/buildgraph.qbs", + "cmdlineparser/cmdlineparser.qbs", "language/language.qbs", - "tools/tools.qbs" - ] : [] + "tools/tools.qbs", + ] } diff --git a/tests/auto/blackbox/testdata/android/multiple-apks-per-project/product1/product1.qbs b/tests/auto/blackbox/testdata/android/multiple-apks-per-project/product1/product1.qbs index 9dde6e174..1058b9dbf 100644 --- a/tests/auto/blackbox/testdata/android/multiple-apks-per-project/product1/product1.qbs +++ b/tests/auto/blackbox/testdata/android/multiple-apks-per-project/product1/product1.qbs @@ -2,14 +2,18 @@ import qbs Project { DynamicLibrary { + Depends { name: "Android.ndk" } + Depends { name: "cpp" } name: "p1lib1" files: ["src/main/jni/lib1.cpp"] Android.ndk.appStl: "stlport_shared" - architectures: ["mipsel", "x86"] + architectures: ["mips", "x86"] cpp.useRPaths: false } DynamicLibrary { + Depends { name: "Android.ndk" } + Depends { name: "cpp" } name: "p1lib2" files: ["src/main/jni/lib2.cpp"] Android.ndk.appStl: "stlport_shared" diff --git a/tests/auto/blackbox/testdata/android/multiple-apks-per-project/product2/product2.qbs b/tests/auto/blackbox/testdata/android/multiple-apks-per-project/product2/product2.qbs index ab7c4770e..f0418a425 100644 --- a/tests/auto/blackbox/testdata/android/multiple-apks-per-project/product2/product2.qbs +++ b/tests/auto/blackbox/testdata/android/multiple-apks-per-project/product2/product2.qbs @@ -2,12 +2,15 @@ import qbs Project { DynamicLibrary { + Depends { name: "cpp" } name: "p2lib1" files: ["src/main/jni/lib1.cpp"] cpp.useRPaths: false } DynamicLibrary { + Depends { name: "Android.ndk" } + Depends { name: "cpp" } name: "p2lib2" files: ["src/main/jni/lib2.cpp"] Android.ndk.appStl: "stlport_shared" diff --git a/tests/auto/blackbox/testdata/android/multiple-libs-per-apk/multiple-libs-per-apk.qbs b/tests/auto/blackbox/testdata/android/multiple-libs-per-apk/multiple-libs-per-apk.qbs index a8eb9fc8c..94eb33ce7 100644 --- a/tests/auto/blackbox/testdata/android/multiple-libs-per-apk/multiple-libs-per-apk.qbs +++ b/tests/auto/blackbox/testdata/android/multiple-libs-per-apk/multiple-libs-per-apk.qbs @@ -2,6 +2,8 @@ import qbs Project { DynamicLibrary { + Depends { name: "Android.ndk" } + Depends { name: "cpp" } name: "lib1" files: ["src/main/jni/lib1.cpp"] Android.ndk.appStl: "stlport_shared" @@ -9,6 +11,8 @@ Project { } DynamicLibrary { + Depends { name: "Android.ndk" } + Depends { name: "cpp" } name: "lib2" files: ["src/main/jni/lib2.cpp"] Android.ndk.appStl: "stlport_shared" diff --git a/tests/auto/blackbox/testdata/android/no-native/no-native.qbs b/tests/auto/blackbox/testdata/android/no-native/no-native.qbs index be0b932c7..2909adc0c 100644 --- a/tests/auto/blackbox/testdata/android/no-native/no-native.qbs +++ b/tests/auto/blackbox/testdata/android/no-native/no-native.qbs @@ -4,5 +4,5 @@ AndroidApk { name: "com.example.android.basicmediadecoder" sourceSetDir: Android.sdk.sdkDir - + "/samples/android-21/media/BasicMediaDecoder/Application/src/main" + + "/samples/android-BasicMediaDecoder/Application/src/main" } diff --git a/tests/auto/blackbox/testdata/android/teapot/teapot.qbs b/tests/auto/blackbox/testdata/android/teapot/teapot.qbs index 6701eaa49..e9bc5b4a6 100644 --- a/tests/auto/blackbox/testdata/android/teapot/teapot.qbs +++ b/tests/auto/blackbox/testdata/android/teapot/teapot.qbs @@ -1,10 +1,11 @@ import qbs Project { - property stringList architectures: ["arm64", "armv7", "x86_64", "mipsel"] + property stringList architectures: ["arm64", "armv7a", "x86_64", "mips"] StaticLibrary { architectures: project.architectures name: "native-glue" + Depends { name: "cpp" } Group { id: glue_sources prefix: Android.ndk.ndkDir + "/sources/android/native_app_glue/" @@ -21,6 +22,8 @@ Project { StaticLibrary { architectures: project.architectures name: "ndk-helper" + Depends { name: "Android.ndk" } + Depends { name: "cpp" } Depends { name: "native-glue" } Group { @@ -40,6 +43,7 @@ Project { StaticLibrary { architectures: project.architectures name: "cpufeatures" + Depends { name: "cpp" } Group { id: cpufeatures_sources prefix: Android.ndk.ndkDir + "/sources/android/cpufeatures/" @@ -56,13 +60,15 @@ Project { DynamicLibrary { name: "TeapotNativeActivity" architectures: project.architectures + Depends { name: "Android.ndk" } + Depends { name: "cpp" } Depends { name: "cpufeatures" } Depends { name: "native-glue" } Depends { name: "ndk-helper" } Group { name: "C++ sources" - prefix: Android.ndk.ndkDir + "/samples/Teapot/jni/" + prefix: Android.ndk.ndkDir + "/samples/Teapot/app/src/main/jni/" files: [ "TeapotNativeActivity.cpp", "TeapotRenderer.cpp", @@ -80,8 +86,7 @@ Project { AndroidApk { name: "com.sample.teapot" - sourceSetDir: Android.sdk.ndkDir + "/samples/Teapot" - legacyLayout: true + sourceSetDir: Android.sdk.ndkDir + "/samples/Teapot/app/src/main" Depends { productTypes: ["android.nativelibrary"] } } } diff --git a/tests/auto/blackbox/testdata/build-directories/project.qbs b/tests/auto/blackbox/testdata/build-directories/project.qbs index 11cfec5c4..dd8fc6c05 100644 --- a/tests/auto/blackbox/testdata/build-directories/project.qbs +++ b/tests/auto/blackbox/testdata/build-directories/project.qbs @@ -4,7 +4,8 @@ Project { Product { name: "p1" type: "blubb1" - Transformer { + Rule { + multiplex: true Artifact { filePath: "dummy1.txt" fileTags: product.type diff --git a/tests/auto/blackbox/testdata/dependenciesProperty/dependenciesProperty.qbs b/tests/auto/blackbox/testdata/dependenciesProperty/dependenciesProperty.qbs index d924e73ff..d2f3773ca 100644 --- a/tests/auto/blackbox/testdata/dependenciesProperty/dependenciesProperty.qbs +++ b/tests/auto/blackbox/testdata/dependenciesProperty/dependenciesProperty.qbs @@ -7,7 +7,8 @@ Project { type: "deps" name: "product1" Depends { name: "product2" } - Transformer { + Rule { + multiplex: true Artifact { fileTags: ["deps"] filePath: product.name + '.deps' diff --git a/tests/auto/blackbox/testdata/erroneous/nonexistentWorkingDir/project.qbs b/tests/auto/blackbox/testdata/erroneous/nonexistentWorkingDir/project.qbs index 6433d80d9..8cf1bfaad 100644 --- a/tests/auto/blackbox/testdata/erroneous/nonexistentWorkingDir/project.qbs +++ b/tests/auto/blackbox/testdata/erroneous/nonexistentWorkingDir/project.qbs @@ -2,7 +2,9 @@ import qbs Application { name: "kaputt" - Transformer { + type: ["nutritious"] + Rule { + multiplex: true Artifact { filePath: "Stulle" fileTags: ["nutritious"] diff --git a/tests/auto/blackbox/testdata/installed-transformer-output/qbs668.qbs b/tests/auto/blackbox/testdata/installed-transformer-output/qbs668.qbs index eeffe25fc..99acc5585 100644 --- a/tests/auto/blackbox/testdata/installed-transformer-output/qbs668.qbs +++ b/tests/auto/blackbox/testdata/installed-transformer-output/qbs668.qbs @@ -3,13 +3,15 @@ import qbs.TextFile Product { name: "install-test" + type: ["text"] Group { qbs.install: true qbs.installDir: "textfiles" fileTagsFilter: "text" } - Transformer { + Rule { + multiplex: true Artifact { filePath: "HelloWorld.txt" fileTags: ["text"] diff --git a/tests/auto/blackbox/testdata/jsextensions-file/file.qbs b/tests/auto/blackbox/testdata/jsextensions-file/file.qbs index 185ee4ac8..5d6ce07ca 100644 --- a/tests/auto/blackbox/testdata/jsextensions-file/file.qbs +++ b/tests/auto/blackbox/testdata/jsextensions-file/file.qbs @@ -5,7 +5,8 @@ import qbs.TextFile Product { type: ["dummy"] - Transformer { + Rule { + multiplex: true Artifact { filePath: "dummy.txt" fileTags: ["dummy"] diff --git a/tests/auto/blackbox/testdata/jsextensions-fileinfo/fileinfo.qbs b/tests/auto/blackbox/testdata/jsextensions-fileinfo/fileinfo.qbs index 26dc5a165..c42c73129 100644 --- a/tests/auto/blackbox/testdata/jsextensions-fileinfo/fileinfo.qbs +++ b/tests/auto/blackbox/testdata/jsextensions-fileinfo/fileinfo.qbs @@ -4,7 +4,8 @@ import qbs.TextFile Product { type: ["dummy"] - Transformer { + Rule { + multiplex: true Artifact { filePath: "dummy.txt" fileTags: ["dummy"] diff --git a/tests/auto/blackbox/testdata/jsextensions-process/process.qbs b/tests/auto/blackbox/testdata/jsextensions-process/process.qbs index 8ca6f7e69..da3a66812 100644 --- a/tests/auto/blackbox/testdata/jsextensions-process/process.qbs +++ b/tests/auto/blackbox/testdata/jsextensions-process/process.qbs @@ -8,7 +8,8 @@ Project { Product { Depends { name: "Qt.core" } type: ["dummy"] - Transformer { + Rule { + multiplex: true Artifact { filePath: "dummy.txt" fileTags: ["dummy"] diff --git a/tests/auto/blackbox/testdata/jsextensions-textfile/textfile.qbs b/tests/auto/blackbox/testdata/jsextensions-textfile/textfile.qbs index 6f24d19eb..456a10f0d 100644 --- a/tests/auto/blackbox/testdata/jsextensions-textfile/textfile.qbs +++ b/tests/auto/blackbox/testdata/jsextensions-textfile/textfile.qbs @@ -3,7 +3,8 @@ import qbs.TextFile Product { type: ["dummy"] - Transformer { + Rule { + multiplex: true Artifact { filePath: "dummy.txt" fileTags: ["dummy"] diff --git a/tests/auto/blackbox/testdata/linkerscripts/linkerscripts.qbs b/tests/auto/blackbox/testdata/linkerscripts/linkerscripts.qbs index 4423d68e0..4aa7d327d 100644 --- a/tests/auto/blackbox/testdata/linkerscripts/linkerscripts.qbs +++ b/tests/auto/blackbox/testdata/linkerscripts/linkerscripts.qbs @@ -10,7 +10,8 @@ DynamicLibrary { fileTags: ["linkerscript"] } - Transformer { + Rule { + multiplex: true outputs: ["custom"] Artifact { filePath: "dummy.txt" diff --git a/tests/auto/blackbox/testdata/output-artifact-auto-tagging/output-artifact-auto-tagging.qbs b/tests/auto/blackbox/testdata/output-artifact-auto-tagging/output-artifact-auto-tagging.qbs index e94775f1e..49a732e23 100644 --- a/tests/auto/blackbox/testdata/output-artifact-auto-tagging/output-artifact-auto-tagging.qbs +++ b/tests/auto/blackbox/testdata/output-artifact-auto-tagging/output-artifact-auto-tagging.qbs @@ -8,6 +8,7 @@ CppApplication { fileTags: ["cpp.in"] } Rule { + multiplex: true inputs: ["cpp.in"] outputFileTags: ["cpp"] outputArtifacts: [{ filePath: "main.cpp" }, { filePath: "broken.nomatch" }] diff --git a/tests/auto/blackbox/testdata/productproperties/header.qbs b/tests/auto/blackbox/testdata/productproperties/header.qbs index ee08de1e2..5c3e2db63 100644 --- a/tests/auto/blackbox/testdata/productproperties/header.qbs +++ b/tests/auto/blackbox/testdata/productproperties/header.qbs @@ -7,7 +7,8 @@ Product { files: "blubb_header.h.in" property string blubbProp: project.blubbProp - Transformer { + Rule { + multiplex: true Artifact { filePath: "blubb_header.h" fileTags: "hpp" diff --git a/tests/auto/blackbox/testdata/propertyChanges/project.qbs b/tests/auto/blackbox/testdata/propertyChanges/project.qbs index cd842c12e..8293d4ca7 100644 --- a/tests/auto/blackbox/testdata/propertyChanges/project.qbs +++ b/tests/auto/blackbox/testdata/propertyChanges/project.qbs @@ -31,10 +31,12 @@ Project { Product { name: "generated text file" + type: ["my_output"] property string fileContentPrefix: "prefix 1" - Transformer { - Artifact { filePath: "nothing" } + Rule { + multiplex: true + Artifact { filePath: "nothing"; fileTags: ["my_output"] } prepare: { var cmd = new JavaScriptCommand(); cmd.silent = true; @@ -43,8 +45,9 @@ Project { } } - Transformer { - Artifact { filePath: "generated.txt" } + Rule { + multiplex: true + Artifact { filePath: "generated.txt"; fileTags: ["my_output"] } prepare: { var cmd = new JavaScriptCommand(); cmd.description = "generating " + output.filePath; diff --git a/tests/auto/blackbox/testdata/rule-with-no-inputs/rule-with-no-inputs.qbs b/tests/auto/blackbox/testdata/rule-with-no-inputs/rule-with-no-inputs.qbs new file mode 100644 index 000000000..2ba97cfa6 --- /dev/null +++ b/tests/auto/blackbox/testdata/rule-with-no-inputs/rule-with-no-inputs.qbs @@ -0,0 +1,26 @@ +import qbs +import qbs.TextFile + +Product { + name: "theProduct" + type: ["output"] + property int version: 1 + Rule { + inputs: [] + multiplex: true + Artifact { + filePath: "output.out" + fileTags: ["output"] + } + prepare: { + var cmd = new JavaScriptCommand(); + cmd.description = "creating output"; + cmd.sourceCode = function() { + console.info(product.version); + var f = new TextFile(output.filePath, TextFile.WriteOnly); + f.close(); + } + return [cmd]; + } + } +} diff --git a/tests/auto/blackbox/testdata/successive-changes/input.in b/tests/auto/blackbox/testdata/successive-changes/input.in new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/tests/auto/blackbox/testdata/successive-changes/input.in diff --git a/tests/auto/blackbox/testdata/successive-changes/successive-changes.qbs b/tests/auto/blackbox/testdata/successive-changes/successive-changes.qbs new file mode 100644 index 000000000..f42ca4096 --- /dev/null +++ b/tests/auto/blackbox/testdata/successive-changes/successive-changes.qbs @@ -0,0 +1,30 @@ +import qbs +import qbs.TextFile + +Project { + property string version: "1" + Product { + name: "theProduct" + type: ["output"] + Group { + files: ["input.in"] + fileTags: ["input"] + } + Rule { + inputs: ["input"] + Artifact { + filePath: "output.out" + fileTags: ["output"] + } + prepare: { + var cmd = new JavaScriptCommand(); + cmd.description = "Creating output"; + cmd.sourceCode = function() { + var f = new TextFile(output.filePath, TextFile.WriteOnly); + f.write(project.version); + } + return [cmd]; + } + } + } +} diff --git a/tests/auto/blackbox/testdata/symlink-removal/symlink-removal.qbs b/tests/auto/blackbox/testdata/symlink-removal/symlink-removal.qbs index 8e74149d1..fc47c450f 100644 --- a/tests/auto/blackbox/testdata/symlink-removal/symlink-removal.qbs +++ b/tests/auto/blackbox/testdata/symlink-removal/symlink-removal.qbs @@ -3,7 +3,8 @@ import qbs.File Product { type: "removal" - Transformer { + Rule { + multiplex: true Artifact { filePath: "dummy" fileTags: product.type diff --git a/tests/auto/blackbox/testdata/versionscript/versionscript.qbs b/tests/auto/blackbox/testdata/versionscript/versionscript.qbs index 56260a022..7dd3654a3 100644 --- a/tests/auto/blackbox/testdata/versionscript/versionscript.qbs +++ b/tests/auto/blackbox/testdata/versionscript/versionscript.qbs @@ -10,7 +10,8 @@ DynamicLibrary { fileTags: ["versionscript"] } - Transformer { + Rule { + multiplex: true outputs: ["custom"] Artifact { filePath: "dummy.txt" diff --git a/tests/auto/blackbox/tst_blackbox.cpp b/tests/auto/blackbox/tst_blackbox.cpp index 6d0b024fd..0c326c628 100644 --- a/tests/auto/blackbox/tst_blackbox.cpp +++ b/tests/auto/blackbox/tst_blackbox.cpp @@ -431,6 +431,7 @@ void TestBlackbox::alwaysRun() rmDirR(relativeBuildDir()); QbsRunParameters params("build", QStringList() << "-f" << projectFile); QCOMPARE(runQbs(params), 0); + QVERIFY(projectFile.contains("transformer") == m_qbsStderr.contains("deprecated")); QVERIFY(m_qbsStdout.contains("yo")); QCOMPARE(runQbs(params), 0); QVERIFY(!m_qbsStdout.contains("yo")); @@ -504,7 +505,7 @@ void TestBlackbox::android_data() QTest::addColumn<QList<int>>("apkFileCounts"); QTest::newRow("teapot") << "teapot" << QStringList("com.sample.teapot") << (QList<int>() << 25); QTest::newRow("no native") << "no-native" - << QStringList("com.example.android.basicmediadecoder") << (QList<int>() << 23); + << QStringList("com.example.android.basicmediadecoder") << (QList<int>() << 22); QTest::newRow("multiple libs") << "multiple-libs-per-apk" << QStringList("twolibs") << (QList<int>() << 10); QTest::newRow("multiple apks") << "multiple-apks-per-project" @@ -1776,6 +1777,21 @@ void TestBlackbox::ruleCycle() QVERIFY(m_qbsStderr.contains("Cycle detected in rule dependencies")); } +void TestBlackbox::ruleWithNoInputs() +{ + QDir::setCurrent(testDataDir + "/rule-with-no-inputs"); + QVERIFY2(runQbs() == 0, m_qbsStderr.constData()); + QVERIFY2(m_qbsStdout.contains("creating output"), m_qbsStdout.constData()); + QVERIFY2(runQbs() == 0, m_qbsStderr.constData()); + QVERIFY2(!m_qbsStdout.contains("creating output"), m_qbsStdout.constData()); + QbsRunParameters params(QStringList() << "theProduct.version:1"); + QVERIFY2(runQbs(params) == 0, m_qbsStderr.constData()); + QVERIFY2(!m_qbsStdout.contains("creating output"), m_qbsStdout.constData()); + params.arguments = QStringList() << "theProduct.version:2"; + QVERIFY2(runQbs(params) == 0, m_qbsStderr.constData()); + QVERIFY2(m_qbsStdout.contains("creating output"), m_qbsStdout.constData()); +} + void TestBlackbox::overrideProjectProperties() { QDir::setCurrent(testDataDir + "/overrideProjectProperties"); @@ -2557,12 +2573,10 @@ void TestBlackbox::jsExtensionsPropertyList() QByteArray file1Contents = file1.readAll(); QCOMPARE(file3.readAll(), file1Contents); //QCOMPARE(file1Contents, file2.readAll()); // keys don't have guaranteed order -#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) QJsonParseError err1, err2; QCOMPARE(QJsonDocument::fromJson(file1Contents, &err1), QJsonDocument::fromJson(file2.readAll(), &err2)); QVERIFY(err1.error == QJsonParseError::NoError && err2.error == QJsonParseError::NoError); -#endif QFile file4("test.openstep.plist"); QVERIFY(file4.exists()); QFile file5("test3.json"); @@ -3107,6 +3121,7 @@ void TestBlackbox::radAfterIncompleteBuild() // Step 1: Have a directory where a file used to be. QbsRunParameters params(QStringList() << "-f" << projectFileName); QCOMPARE(runQbs(params), 0); + QVERIFY(projectFileName.contains("transformer") == m_qbsStderr.contains("deprecated")); WAIT_FOR_NEW_TIMESTAMP(); QFile projectFile(projectFileName); QVERIFY(projectFile.open(QIODevice::ReadWrite)); @@ -3171,6 +3186,22 @@ void TestBlackbox::subProfileChangeTracking() QVERIFY(m_qbsStdout.contains("main2.cpp")); } +void TestBlackbox::successiveChanges() +{ + QDir::setCurrent(testDataDir + "/successive-changes"); + QCOMPARE(runQbs(), 0); + + QbsRunParameters params(QStringList() << "theProduct.type:output,blubb"); + QCOMPARE(runQbs(params), 0); + + params.arguments << "project.version:2"; + QCOMPARE(runQbs(params), 0); + QFile output(relativeProductBuildDir("theProduct") + "/output.out"); + QVERIFY2(output.open(QIODevice::ReadOnly), qPrintable(output.errorString())); + const QByteArray version = output.readAll(); + QCOMPARE(version.constData(), "2"); +} + void TestBlackbox::installedApp() { QDir::setCurrent(testDataDir + "/installed_artifact"); diff --git a/tests/auto/blackbox/tst_blackbox.h b/tests/auto/blackbox/tst_blackbox.h index 9f2461b4e..18605ec9c 100644 --- a/tests/auto/blackbox/tst_blackbox.h +++ b/tests/auto/blackbox/tst_blackbox.h @@ -196,7 +196,9 @@ private slots: void responseFiles(); void ruleConditions(); void ruleCycle(); + void ruleWithNoInputs(); void subProfileChangeTracking(); + void successiveChanges(); void symlinkRemoval(); void renameDependency(); void separateDebugInfo(); diff --git a/tests/auto/buildgraph/buildgraph.qbs b/tests/auto/buildgraph/buildgraph.qbs index cc5885ca2..f6c1cd1f7 100644 --- a/tests/auto/buildgraph/buildgraph.qbs +++ b/tests/auto/buildgraph/buildgraph.qbs @@ -2,5 +2,6 @@ import qbs QbsAutotest { testName: "buildgraph" + condition: qbsbuildconfig.enableUnitTests files: "tst_buildgraph.cpp" } diff --git a/tests/auto/language/language.qbs b/tests/auto/language/language.qbs index ff9f518f2..9fda49ce6 100644 --- a/tests/auto/language/language.qbs +++ b/tests/auto/language/language.qbs @@ -2,5 +2,6 @@ import qbs QbsAutotest { testName: "language" + condition: qbsbuildconfig.enableUnitTests files: "tst_language.cpp" } diff --git a/tests/auto/tools/tools.qbs b/tests/auto/tools/tools.qbs index 54a6ea485..612a70a40 100644 --- a/tests/auto/tools/tools.qbs +++ b/tests/auto/tools/tools.qbs @@ -2,5 +2,6 @@ import qbs QbsAutotest { testName: "tools" + condition: qbsbuildconfig.enableUnitTests files: ["tst_tools.cpp"] } |