diff options
38 files changed, 568 insertions, 50 deletions
diff --git a/changelogs/changes-1.9.0.md b/changelogs/changes-1.9.0.md index 55345c1cc..2e3cbfa09 100644 --- a/changelogs/changes-1.9.0.md +++ b/changelogs/changes-1.9.0.md @@ -41,6 +41,8 @@ upward (see the `ld64` man page for more information). * The property `cpp.useCxxPrecompiledHeader`, as well as the variants for the other languages, now defaults to true. +* The property `cpp.cxxLanguageVersion` now gets mapped to MSVC's `/std` option, + if applicable. # Apple * Added support for building macOS disk images. diff --git a/doc/codeattributions.qdoc b/doc/codeattributions.qdoc new file mode 100644 index 000000000..ee4e4b901 --- /dev/null +++ b/doc/codeattributions.qdoc @@ -0,0 +1,201 @@ +/*! + +\contentspage attributions.html +\ingroup attributions-libs +\ingroup attributions-qbs +\page qbs-attribution-ds_store.html attribution +\target ds_store + +\title ds_store +\brief MIT License + +Manipulate Finder .DS_Store files from Python + +Used in the qbs dmg module for building Apple disk images. + +The sources can be found in src/3rdparty/python/lib/python2.7/site-packages/ds_store. + +\l{https://github.com/al45tair/ds_store}{Project Homepage}, upstream version: 1.1.2 + + +\badcode +Copyright (c) 2014 Alastair Houghton +\endcode + +\l{https://spdx.org/licenses/MIT.html}{MIT License}. + +\badcode +Copyright (c) 2014 Alastair Houghton + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +\endcode +*/ + +/*! + +\contentspage attributions.html +\ingroup attributions-libs +\ingroup attributions-qbs +\page qbs-attribution-dmgbuild.html attribution +\target dmgbuild + +\title dmgbuild +\brief MIT License + +macOS command line utility to build disk images + +Used in the qbs dmg module for building Apple disk images. + +The sources can be found in src/3rdparty/python/lib/python2.7/site-packages/dmgbuild. + +\l{https://github.com/al45tair/dmgbuild}{Project Homepage}, upstream version: 1.3.1 + + +\badcode +Copyright (c) 2014 Alastair Houghton +\endcode + +\l{https://spdx.org/licenses/MIT.html}{MIT License}. + +\badcode +Copyright (c) 2014 Alastair Houghton + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +\endcode +*/ + +/*! + +\contentspage attributions.html +\ingroup attributions-libs +\ingroup attributions-qbs +\page qbs-attribution-mac_alias.html attribution +\target mac_alias + +\title mac_alias +\brief MIT License + +Generate/parse Mac OS Alias records from Python + +Used in the qbs dmg module for building Apple disk images. + +The sources can be found in src/3rdparty/python/lib/python2.7/site-packages/mac_alias. + +\l{https://github.com/al45tair/mac_alias}{Project Homepage}, upstream version: 2.0.6 + + +\badcode +Copyright (c) 2014 Alastair Houghton +\endcode + +\l{https://spdx.org/licenses/MIT.html}{MIT License}. + +\badcode +Copyright (c) 2014 Alastair Houghton + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +\endcode +*/ + +/*! + +\contentspage attributions.html +\ingroup attributions-libs +\ingroup attributions-qbs +\page qbs-attribution-biplist.html attribution +\target biplist + +\title biplist +\brief BSD 3-clause "New" or "Revised" License + +biplist is a library for reading/writing binary plists. + +Used in the qbs dmg module for building Apple disk images. + +The sources can be found in src/3rdparty/python/lib/python2.7/site-packages/biplist. + +\l{https://bitbucket.org/wooster/biplist}{Project Homepage}, upstream version: 1.0.2 + + +\badcode +Copyright (c) 2010, Andrew Wooster +\endcode + +\l{https://spdx.org/licenses/BSD-3-Clause.html}{BSD 3-clause "New" or "Revised" License}. + +\badcode +Copyright (c) 2010, Andrew Wooster +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of biplist nor the names of its contributors may be + used to endorse or promote products derived from this software without + specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +\endcode +*/ diff --git a/doc/qbs.qdoc b/doc/qbs.qdoc index 3e01e862f..abacf6d71 100644 --- a/doc/qbs.qdoc +++ b/doc/qbs.qdoc @@ -85,6 +85,7 @@ \endlist \li \l{Appendix A: Building Qbs} + \li \l{Appendix B: Code Attributions} \endlist */ @@ -123,6 +124,7 @@ \contentspage index.html \previouspage reference.html \page building-qbs.html + \nextpage attributions.html \title Appendix A: Building Qbs @@ -1121,3 +1123,14 @@ \c Product.name), the generator will typically suggest it in the error message. */ + +/*! + \contentspage index.html + \previouspage building-qbs.html + \page attributions.html + + \title Appendix B: Code Attributions + + \QBS contains third-party code, which we gratefully acknowledge: + \generatelist{groupsbymodule attributions-qbs} +*/ diff --git a/docker/windowsservercore/Dockerfile b/docker/windowsservercore/Dockerfile index 0e6e78e60..942ecbd2b 100644 --- a/docker/windowsservercore/Dockerfile +++ b/docker/windowsservercore/Dockerfile @@ -1,4 +1,4 @@ -FROM microsoft/windowsservercore:10.0.14393.1358 +FROM microsoft/windowsservercore:10.0.14393.1480 LABEL Description="Windows Server Core development environment for Qbs with Qt 5.9, Chocolatey and various dependencies for testing Qbs modules and functionality" # Disable crash dialog for release-mode runtimes @@ -22,7 +22,7 @@ RUN @powershell -NoProfile -ExecutionPolicy Bypass -Command \ $Env:chocolateyVersion = '0.10.5' ; \ $Env:chocolateyUseWindowsCompression = 'false' ; \ "iex ((New-Object System.Net.WebClient).DownloadString('https://chocolatey.org/install.ps1'))" && SET "PATH=%PATH%;%ALLUSERSPROFILE%\chocolatey\bin" -RUN choco install -y qbs --version 1.8.1 -RUN choco install -y unzip --version 6.0 -RUN choco install -y visualcpp-build-tools --version 14.0.25420.1 -RUN choco install -y zip --version 3.0 +RUN choco install -y qbs --version 1.8.1 && qbs --version +RUN choco install -y unzip --version 6.0 && unzip -v +RUN choco install -y visualcpp-build-tools --version 14.0.25420.1 && dir "%PROGRAMFILES(X86)%\Microsoft Visual C++ Build Tools" +RUN choco install -y zip --version 3.0 && zip -v diff --git a/share/qbs/modules/cpp/android-gcc.qbs b/share/qbs/modules/cpp/android-gcc.qbs index 5307c06a4..a21f56f2a 100644 --- a/share/qbs/modules/cpp/android-gcc.qbs +++ b/share/qbs/modules/cpp/android-gcc.qbs @@ -85,6 +85,13 @@ LinuxGCC { ? FileInfo.joinPaths(stlLibsDir, staticLibraryPrefix + Android.ndk.appStl + staticLibrarySuffix) : undefined + Group { + name: "Android STL" + condition: product.cpp.sharedStlFilePath + files: product.cpp.sharedStlFilePath ? [product.cpp.sharedStlFilePath] : [] + fileTags: ["android.unstripped-stl"] + } + toolchainInstallPath: FileInfo.joinPaths(Android.ndk.ndkDir, "toolchains", toolchainDir, "prebuilt", Android.ndk.hostArch, "bin") @@ -203,7 +210,22 @@ LinuxGCC { endianness: "little" Rule { + inputs: ["android.unstripped-stl"] + Artifact { + filePath: FileInfo.joinPaths("stripped-libs", input.fileName); + fileTags: ["android.stripped-stl"] + } + prepare: { + var args = ["--strip-unneeded", "-o", output.filePath, input.filePath]; + var cmd = new Command(product.cpp.stripPath, args); + cmd.description = "stripping " + input.fileName; + return [cmd]; + } + } + + Rule { inputs: ["dynamiclibrary"] + explicitlyDependsOn: ["android.stripped-stl"]; outputFileTags: ["android.nativelibrary", "android.gdbserver-info", "android.stl-info"] outputArtifacts: { var artifacts = [{ @@ -217,17 +239,14 @@ LinuxGCC { fileTags: ["android.gdbserver-info"] }); } - var stlFilePath = product.moduleProperty("cpp", "sharedStlFilePath"); - if (stlFilePath) + if (explicitlyDependsOn["android.stripped-stl"]) 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); @@ -246,8 +265,9 @@ LinuxGCC { infoFile.writeLine(targetPath); infoFile.close(); } - if (stlFilePath) { - var srcPath = stlFilePath; + var strippedStlList = explicitlyDependsOn["android.stripped-stl"]; + if (strippedStlList) { + var srcPath = strippedStlList[0].filePath; var targetPath = FileInfo.joinPaths(destDir, FileInfo.fileName(srcPath)); var infoFile = new TextFile(outputs["android.stl-info"][0].filePath, TextFile.WriteOnly); @@ -257,8 +277,6 @@ LinuxGCC { } } 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; diff --git a/share/qbs/modules/cpp/msvc.js b/share/qbs/modules/cpp/msvc.js index 4d12ee427..13e5d8821 100644 --- a/share/qbs/modules/cpp/msvc.js +++ b/share/qbs/modules/cpp/msvc.js @@ -57,6 +57,25 @@ function handleCpuFeatures(input, flags) { } } +function addLanguageVersionFlag(input, args) { + var cxxVersion = input.cpp.cxxLanguageVersion; + if (!cxxVersion) + return; + + // Visual C++ 2013, Update 3 + var hasStdOption = Utilities.versionCompare(input.cpp.compilerVersion, "18.00.30723") >= 0; + if (!hasStdOption) + return; + + var flag; + if (cxxVersion === "c++14") + flag = "/std:c++14"; + else if (cxxVersion !== "c++11" && cxxVersion !== "c++98") + flag = "/std:c++latest"; + if (flag) + args.push(flag); +} + function prepareCompiler(project, product, inputs, outputs, input, output, explicitlyDependsOn) { var i; var debugInformation = input.cpp.debugInformation; @@ -164,10 +183,12 @@ function prepareCompiler(project, product, inputs, outputs, input, output, expli args.push("/FI" + FileInfo.toWindowsSeparators(prefixHeaders[i])); // Language - if (tag === "cpp") + if (tag === "cpp") { args.push("/TP"); - else if (tag === "c") + addLanguageVersionFlag(input, args); + } else if (tag === "c") { args.push("/TC"); + } // Whether we're compiling a precompiled header or normal source file var pchOutput = outputs[tag + "_pch"] ? outputs[tag + "_pch"][0] : undefined; diff --git a/share/qbs/modules/cpp/windows-mingw.qbs b/share/qbs/modules/cpp/windows-mingw.qbs index fec31cf3f..2fe96495e 100644 --- a/share/qbs/modules/cpp/windows-mingw.qbs +++ b/share/qbs/modules/cpp/windows-mingw.qbs @@ -85,15 +85,17 @@ GenericGCC { } prepare: { + var inputList = inputs["native.pe.manifest"]; // TODO: Emulate manifest merging like Microsoft's mt.exe tool does - if (inputs.length !== 1) + if (inputList.length !== 1) { throw("The MinGW toolchain does not support manifest merging; " + "you may only specify a single manifest file to embed into your assembly."); + } var cmd = new JavaScriptCommand(); cmd.silent = true; cmd.productType = product.type; - cmd.inputFilePath = inputs[0].filePath; + cmd.inputFilePath = inputList[0].filePath; cmd.outputFilePath = output.filePath; cmd.sourceCode = function() { var tf; diff --git a/src/app/qbs-setup-android/qbs-setup-android.exe.manifest b/src/app/qbs-setup-android/qbs-setup-android.exe.manifest new file mode 100644 index 000000000..6b425b152 --- /dev/null +++ b/src/app/qbs-setup-android/qbs-setup-android.exe.manifest @@ -0,0 +1,13 @@ +<?xml version="1.0" encoding="UTF-8" standalone="yes"?> +<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0"> + <!-- Make sure Windows UAC does not believe qbs-setup-android is an installer. --> + <trustInfo xmlns="urn:schemas-microsoft-com:asm.v3"> + <security> + <requestedPrivileges> + <requestedExecutionLevel + level="asInvoker" + uiAccess="false"/> + </requestedPrivileges> + </security> + </trustInfo> +</assembly> diff --git a/src/app/qbs-setup-android/qbs-setup-android.qbs b/src/app/qbs-setup-android/qbs-setup-android.qbs index 0eb67f9c5..edadd5dd1 100644 --- a/src/app/qbs-setup-android/qbs-setup-android.qbs +++ b/src/app/qbs-setup-android/qbs-setup-android.qbs @@ -9,4 +9,9 @@ QbsApp { "commandlineparser.h", "main.cpp", ] + Group { + name: "MinGW specific files" + condition: qbs.toolchain.contains("mingw") + files: ["qbs-setup-android.exe.manifest", "qbs-setup-android.rc"] + } } diff --git a/src/app/qbs-setup-android/qbs-setup-android.rc b/src/app/qbs-setup-android/qbs-setup-android.rc new file mode 100644 index 000000000..20cd1ab11 --- /dev/null +++ b/src/app/qbs-setup-android/qbs-setup-android.rc @@ -0,0 +1,4 @@ +#define RT_MANIFEST 24 +#define CREATEPROCESS_MANIFEST_RESOURCE_ID 1 + +CREATEPROCESS_MANIFEST_RESOURCE_ID RT_MANIFEST "qbs-setup-android.exe.manifest" diff --git a/src/app/qbs-setup-toolchains/qbs-setup-toolchains.qbs b/src/app/qbs-setup-toolchains/qbs-setup-toolchains.qbs index 3536b51db..1b7cb6526 100644 --- a/src/app/qbs-setup-toolchains/qbs-setup-toolchains.qbs +++ b/src/app/qbs-setup-toolchains/qbs-setup-toolchains.qbs @@ -14,4 +14,9 @@ QbsApp { "xcodeprobe.cpp", "xcodeprobe.h", ] + Group { + name: "MinGW specific files" + condition: qbs.toolchain.contains("mingw") + files: ["qbs-setup-toolchains.exe.manifest", "qbs-setup-toolchains.rc"] + } } diff --git a/src/lib/corelib/buildgraph/executor.cpp b/src/lib/corelib/buildgraph/executor.cpp index 1c50f7234..c5f652db5 100644 --- a/src/lib/corelib/buildgraph/executor.cpp +++ b/src/lib/corelib/buildgraph/executor.cpp @@ -860,24 +860,27 @@ void Executor::potentiallyRunTransformer(const TransformerPtr &transformer) return; } - if (!mustExecuteTransformer(transformer)) { + const bool mustExecute = mustExecuteTransformer(transformer); + if (mustExecute || m_buildOptions.forceTimestampCheck()) { + for (Artifact * const output : qAsConst(transformer->outputs)) { + // Scan all input artifacts. If new dependencies were found during scanning, delay + // execution of this transformer. + InputArtifactScanner scanner(output, m_inputArtifactScanContext, m_logger); + AccumulatingTimer scanTimer(m_buildOptions.logElapsedTime() + ? &m_elapsedTimeScanners : nullptr); + scanner.scan(); + scanTimer.stop(); + if (scanner.newDependencyAdded() && checkForUnbuiltDependencies(output)) + return; + } + } + + if (!mustExecute) { qCDebug(lcExec) << "Up to date. Skipping."; finishTransformer(transformer); return; } - for (Artifact * const output : qAsConst(transformer->outputs)) { - // Scan all input artifacts. If new dependencies were found during scanning, delay - // execution of this transformer. - InputArtifactScanner scanner(output, m_inputArtifactScanContext, m_logger); - AccumulatingTimer scanTimer(m_buildOptions.logElapsedTime() - ? &m_elapsedTimeScanners : nullptr); - scanner.scan(); - scanTimer.stop(); - if (scanner.newDependencyAdded() && checkForUnbuiltDependencies(output)) - return; - } - if (m_buildOptions.executeRulesOnly()) finishTransformer(transformer); else diff --git a/src/lib/corelib/buildgraph/productinstaller.cpp b/src/lib/corelib/buildgraph/productinstaller.cpp index 1688dcc76..1b1e507bb 100644 --- a/src/lib/corelib/buildgraph/productinstaller.cpp +++ b/src/lib/corelib/buildgraph/productinstaller.cpp @@ -151,11 +151,14 @@ QString ProductInstaller::targetFilePath(const TopLevelProject *project, localAbsBasePath)); } - targetFilePath.remove(0, localAbsBasePath.length() + 1); + // Since there is a difference between X: and X:\\ on Windows, absolute paths can sometimes + // end with a slash, so only remove an extra character if there is no ending slash + targetFilePath.remove(0, localAbsBasePath.length() + + (localAbsBasePath.endsWith(QLatin1Char('/')) ? 0 : 1)); } targetFilePath.prepend(targetDir + QLatin1Char('/')); - return targetFilePath; + return QDir::cleanPath(targetFilePath); } void ProductInstaller::initInstallRoot(const TopLevelProject *project, diff --git a/src/lib/corelib/buildgraph/rulesapplicator.cpp b/src/lib/corelib/buildgraph/rulesapplicator.cpp index f4f57cb15..4a5954a73 100644 --- a/src/lib/corelib/buildgraph/rulesapplicator.cpp +++ b/src/lib/corelib/buildgraph/rulesapplicator.cpp @@ -354,6 +354,7 @@ Artifact *RulesApplicator::createOutputArtifact(const QString &filePath, const F if (m_rule->declaresInputs() && m_rule->requiresInputs) outputArtifact->clearTimestamp(); m_invalidatedArtifacts += outputArtifact; + m_transformer->rescueChangeTrackingData(outputArtifact->transformer); } else { QScopedPointer<Artifact> newArtifact(new Artifact); newArtifact->artifactType = Artifact::Generated; diff --git a/src/lib/corelib/buildgraph/transformer.cpp b/src/lib/corelib/buildgraph/transformer.cpp index 0e5b4a084..b6e02e708 100644 --- a/src/lib/corelib/buildgraph/transformer.cpp +++ b/src/lib/corelib/buildgraph/transformer.cpp @@ -260,6 +260,17 @@ void Transformer::createCommands(ScriptEngine *engine, const ScriptFunctionConst } } +void Transformer::rescueChangeTrackingData(const TransformerConstPtr &other) +{ + if (!other) + return; + propertiesRequestedInPrepareScript = other->propertiesRequestedInPrepareScript; + propertiesRequestedInCommands = other->propertiesRequestedInCommands; + propertiesRequestedFromArtifactInPrepareScript + = other->propertiesRequestedFromArtifactInPrepareScript; + propertiesRequestedFromArtifactInCommands = other->propertiesRequestedFromArtifactInCommands; +} + void Transformer::load(PersistentPool &pool) { pool.load(rule); diff --git a/src/lib/corelib/buildgraph/transformer.h b/src/lib/corelib/buildgraph/transformer.h index e4ffcb83b..b83321bda 100644 --- a/src/lib/corelib/buildgraph/transformer.h +++ b/src/lib/corelib/buildgraph/transformer.h @@ -82,6 +82,7 @@ public: void setupExplicitlyDependsOn(QScriptValue targetScriptValue); void createCommands(ScriptEngine *engine, const ScriptFunctionConstPtr &script, const QScriptValueList &args); + void rescueChangeTrackingData(const TransformerConstPtr &other); private: Transformer(); diff --git a/src/lib/corelib/corelib.pro b/src/lib/corelib/corelib.pro index 8e53891fd..c0026f14b 100644 --- a/src/lib/corelib/corelib.pro +++ b/src/lib/corelib/corelib.pro @@ -13,7 +13,6 @@ qbs_enable_project_file_updates: QT += gui INCLUDEPATH += $$PWD CONFIG += depend_includepath -DEFINES += QT_CREATOR QML_BUILD_STATIC_LIB # needed for QmlJS DEFINES += SRCDIR=\\\"$$PWD\\\" diff --git a/src/lib/corelib/corelib.qbs b/src/lib/corelib/corelib.qbs index 43efa24f1..570eb4605 100644 --- a/src/lib/corelib/corelib.qbs +++ b/src/lib/corelib/corelib.qbs @@ -18,7 +18,6 @@ QbsLibrary { cpp.defines: base.concat([ 'QBS_RELATIVE_LIBEXEC_PATH="' + qbsbuildconfig.relativeLibexecPath + '"', "QBS_VERSION=\"" + version + "\"", - "QT_CREATOR", "QML_BUILD_STATIC_LIB", // needed for QmlJS ]).concat(projectFileUpdateDefines).concat(enableUnitTestsDefines) Properties { diff --git a/src/lib/corelib/parser/qmljsastvisitor_p.h b/src/lib/corelib/parser/qmljsastvisitor_p.h index aa4471c6b..bec174c65 100644 --- a/src/lib/corelib/parser/qmljsastvisitor_p.h +++ b/src/lib/corelib/parser/qmljsastvisitor_p.h @@ -58,7 +58,7 @@ namespace QbsQmlJS { namespace AST { -class QML_PARSER_EXPORT QBS_AUTOTEST_EXPORT Visitor +class QBS_AUTOTEST_EXPORT Visitor { public: Visitor(); diff --git a/src/lib/corelib/parser/qmljsengine_p.h b/src/lib/corelib/parser/qmljsengine_p.h index 2fdd60b30..6ff53a6e9 100644 --- a/src/lib/corelib/parser/qmljsengine_p.h +++ b/src/lib/corelib/parser/qmljsengine_p.h @@ -88,7 +88,7 @@ public: QString message; }; -class QML_PARSER_EXPORT QBS_AUTOTEST_EXPORT Engine +class QBS_AUTOTEST_EXPORT Engine { Lexer *_lexer; Directives *_directives; diff --git a/src/lib/corelib/parser/qmljsglobal_p.h b/src/lib/corelib/parser/qmljsglobal_p.h index 5a875c01a..c3d198ea5 100644 --- a/src/lib/corelib/parser/qmljsglobal_p.h +++ b/src/lib/corelib/parser/qmljsglobal_p.h @@ -41,6 +41,15 @@ #include <QtCore/qglobal.h> +// Force QML_PARSER_EXPORT to be always empty. +#ifndef QT_CREATOR +# define QT_CREATOR +#endif +#ifdef QML_BUILD_STATIC_LIB +# undef QML_BUILD_STATIC_LIB +#endif +#define QML_BUILD_STATIC_LIB 1 + #ifdef QT_CREATOR # ifdef QMLJS_BUILD_DIR # define QML_PARSER_EXPORT Q_DECL_EXPORT diff --git a/src/lib/corelib/parser/qmljslexer_p.h b/src/lib/corelib/parser/qmljslexer_p.h index e0d61b226..e9dff1dd4 100644 --- a/src/lib/corelib/parser/qmljslexer_p.h +++ b/src/lib/corelib/parser/qmljslexer_p.h @@ -82,7 +82,7 @@ public: } }; -class QML_PARSER_EXPORT QBS_AUTOTEST_EXPORT Lexer: public QmlJSGrammar +class QBS_AUTOTEST_EXPORT Lexer: public QmlJSGrammar { public: enum { diff --git a/src/lib/corelib/parser/qmljsparser_p.h b/src/lib/corelib/parser/qmljsparser_p.h index 38fc1f5ce..fde61ea11 100644 --- a/src/lib/corelib/parser/qmljsparser_p.h +++ b/src/lib/corelib/parser/qmljsparser_p.h @@ -70,7 +70,7 @@ namespace QbsQmlJS { class Engine; -class QML_PARSER_EXPORT QBS_AUTOTEST_EXPORT Parser: protected QmlJSGrammar +class QBS_AUTOTEST_EXPORT Parser: protected QmlJSGrammar { public: union Value { diff --git a/src/lib/corelib/tools/fileinfo.cpp b/src/lib/corelib/tools/fileinfo.cpp index 27fe2b9e2..3e2f0d36c 100644 --- a/src/lib/corelib/tools/fileinfo.cpp +++ b/src/lib/corelib/tools/fileinfo.cpp @@ -131,6 +131,15 @@ bool FileInfo::exists(const QString &fp) return FileInfo(fp).exists(); } +// Whether a path is the special "current drive path" path type, +// which is neither truly relative nor absolute +static bool isCurrentDrivePath(const QString &path, HostOsInfo::HostOs hostOs) +{ + return hostOs == HostOsInfo::HostOsWindows + ? path.size() == 2 && path.at(1) == QLatin1Char(':') && path.at(0).isLetter() + : false; +} + // from creator/src/shared/proparser/ioutils.cpp bool FileInfo::isAbsolute(const QString &path, HostOsInfo::HostOs hostOs) { @@ -176,11 +185,12 @@ bool FileInfo::isPattern(const QStringRef &str) * This function assumes that both paths are clean, that is they don't contain * double slashes or redundant dot parts. */ -QString FileInfo::resolvePath(const QString &base, const QString &rel) +QString FileInfo::resolvePath(const QString &base, const QString &rel, HostOsInfo::HostOs hostOs) { - QBS_ASSERT(isAbsolute(base), qDebug("base: %s, rel: %s", qPrintable(base), qPrintable(rel)); + QBS_ASSERT(isAbsolute(base, hostOs) && !isCurrentDrivePath(rel, hostOs), + qDebug("base: %s, rel: %s", qPrintable(base), qPrintable(rel)); return QString()); - if (isAbsolute(rel)) + if (isAbsolute(rel, hostOs)) return rel; if (rel.size() == 1 && rel.at(0) == QLatin1Char('.')) return base; @@ -204,12 +214,13 @@ QString FileInfo::resolvePath(const QString &base, const QString &rel) int idx = r.lastIndexOf(QLatin1Char('/')); if (idx >= 0) r.truncate(idx); - return r; + s.clear(); + } + if (!s.isEmpty() || isCurrentDrivePath(r, hostOs)) { + r.reserve(r.length() + 1 + s.length()); + r += QLatin1Char('/'); + r += s; } - - r.reserve(r.length() + 1 + s.length()); - r += QLatin1Char('/'); - r += s; return r; } @@ -317,7 +328,7 @@ static QString resolveSymlinks(const QString &fileName) { QFileInfo fi(fileName); while (fi.isSymLink()) - fi.setFile(fi.symLinkTarget()); + fi.setFile(fi.dir(), fi.symLinkTarget()); return fi.absoluteFilePath(); } diff --git a/src/lib/corelib/tools/fileinfo.h b/src/lib/corelib/tools/fileinfo.h index 71d178265..2ab250c0a 100644 --- a/src/lib/corelib/tools/fileinfo.h +++ b/src/lib/corelib/tools/fileinfo.h @@ -75,7 +75,8 @@ public: static bool isAbsolute(const QString &fp, HostOsInfo::HostOs hostOs = HostOsInfo::hostOs()); static bool isPattern(const QStringRef &str); static bool isPattern(const QString &str); - static QString resolvePath(const QString &base, const QString &rel); + static QString resolvePath(const QString &base, const QString &rel, + HostOsInfo::HostOs hostOs = HostOsInfo::hostOs()); static bool globMatches(const QRegExp &pattern, const QString &subject); static bool isFileCaseCorrect(const QString &filePath); diff --git a/src/packages/chocolatey/qbs.nuspec b/src/packages/chocolatey/qbs.nuspec index 3926ec7db..42ccbae8b 100644 --- a/src/packages/chocolatey/qbs.nuspec +++ b/src/packages/chocolatey/qbs.nuspec @@ -5,7 +5,7 @@ <id>qbs</id> <title>Qbs</title> <authors>Qt Project</authors> - <owners>jakepetroules</owners> + <owners>qbs</owners> <summary>Build tool that helps simplify the build process for developing projects across multiple platforms.</summary> <description>Qbs is a tool that helps simplify the build process for developing projects across multiple platforms.</description> <projectUrl>https://wiki.qt.io/Qbs</projectUrl> diff --git a/tests/auto/blackbox/testdata/check-timestamps/check-timestamps.qbs b/tests/auto/blackbox/testdata/check-timestamps/check-timestamps.qbs new file mode 100644 index 000000000..32456444c --- /dev/null +++ b/tests/auto/blackbox/testdata/check-timestamps/check-timestamps.qbs @@ -0,0 +1,10 @@ +import qbs + +CppApplication { + name: "app" + files: [ + "file.cpp", + "file.h", + "main.cpp", + ] +} diff --git a/tests/auto/blackbox/testdata/check-timestamps/file.cpp b/tests/auto/blackbox/testdata/check-timestamps/file.cpp new file mode 100644 index 000000000..4752b89d5 --- /dev/null +++ b/tests/auto/blackbox/testdata/check-timestamps/file.cpp @@ -0,0 +1,3 @@ +#include "file.h" + +void f() { } diff --git a/tests/auto/blackbox/testdata/check-timestamps/file.h b/tests/auto/blackbox/testdata/check-timestamps/file.h new file mode 100644 index 000000000..789447c02 --- /dev/null +++ b/tests/auto/blackbox/testdata/check-timestamps/file.h @@ -0,0 +1 @@ +void f(); diff --git a/tests/auto/blackbox/testdata/check-timestamps/main.cpp b/tests/auto/blackbox/testdata/check-timestamps/main.cpp new file mode 100644 index 000000000..237c8ce18 --- /dev/null +++ b/tests/auto/blackbox/testdata/check-timestamps/main.cpp @@ -0,0 +1 @@ +int main() {} diff --git a/tests/auto/blackbox/testdata/cxx-language-version/cxx-language-version.qbs b/tests/auto/blackbox/testdata/cxx-language-version/cxx-language-version.qbs new file mode 100644 index 000000000..436c64c5e --- /dev/null +++ b/tests/auto/blackbox/testdata/cxx-language-version/cxx-language-version.qbs @@ -0,0 +1,30 @@ +import qbs + +CppApplication { + name: "app" + + files: ["main.cpp"] + + Probe { + id: compilerProbe + property stringList toolchain: qbs.toolchain + property string compilerVersion: cpp.compilerVersion + + configure: { + var isNewerMsvc; + var isOlderMsvc; + var isGcc; + if (toolchain.contains("msvc")) { + if (compilerVersion >= "18.00.30723") + isNewerMsvc = true; + else + isOlderMsvc = true; + } else { + isGcc = true; + } + console.info("is newer MSVC: " + isNewerMsvc); + console.info("is older MSVC: " + isOlderMsvc); + console.info("is GCC: " + isGcc); + } + } +} diff --git a/tests/auto/blackbox/testdata/cxx-language-version/main.cpp b/tests/auto/blackbox/testdata/cxx-language-version/main.cpp new file mode 100644 index 000000000..237c8ce18 --- /dev/null +++ b/tests/auto/blackbox/testdata/cxx-language-version/main.cpp @@ -0,0 +1 @@ +int main() {} diff --git a/tests/auto/blackbox/testdata/rescue-transformer-data/main.cpp b/tests/auto/blackbox/testdata/rescue-transformer-data/main.cpp new file mode 100644 index 000000000..237c8ce18 --- /dev/null +++ b/tests/auto/blackbox/testdata/rescue-transformer-data/main.cpp @@ -0,0 +1 @@ +int main() {} diff --git a/tests/auto/blackbox/testdata/rescue-transformer-data/modules/m/m.qbs b/tests/auto/blackbox/testdata/rescue-transformer-data/modules/m/m.qbs new file mode 100644 index 000000000..03fdf7a6b --- /dev/null +++ b/tests/auto/blackbox/testdata/rescue-transformer-data/modules/m/m.qbs @@ -0,0 +1,21 @@ +import qbs + +Module { + property bool p + + Rule { + multiplex: true + Artifact { + filePath: "dummy" + fileTags: ["out"] + } + prepare: { + var cmd = new JavaScriptCommand(); + cmd.description = "creating dummy"; + cmd.sourceCode = function() { + console.info("m.p: " + product.m.p); + }; + return [cmd]; + } + } +} diff --git a/tests/auto/blackbox/testdata/rescue-transformer-data/transformer-data-rescue.qbs b/tests/auto/blackbox/testdata/rescue-transformer-data/transformer-data-rescue.qbs new file mode 100644 index 000000000..2eafcc01d --- /dev/null +++ b/tests/auto/blackbox/testdata/rescue-transformer-data/transformer-data-rescue.qbs @@ -0,0 +1,7 @@ +import qbs + +CppApplication { + type: base.concat(["out"]) + Depends { name: "m" } + files: ["main.cpp"] +} diff --git a/tests/auto/blackbox/tst_blackbox.cpp b/tests/auto/blackbox/tst_blackbox.cpp index 65c0630fc..c4b6a061e 100644 --- a/tests/auto/blackbox/tst_blackbox.cpp +++ b/tests/auto/blackbox/tst_blackbox.cpp @@ -1242,6 +1242,83 @@ void TestBlackbox::conflictingArtifacts() QVERIFY2(m_qbsStderr.contains("Conflicting artifacts"), m_qbsStderr.constData()); } +void TestBlackbox::cxxLanguageVersion() +{ + QDir::setCurrent(testDataDir + "/cxx-language-version"); + rmDirR(relativeBuildDir()); + QFETCH(QString, version); + QFETCH(QVariantMap, requiredFlags); + QFETCH(QVariantMap, forbiddenFlags); + QbsRunParameters resolveParams; + resolveParams.command = "resolve"; + resolveParams.arguments << "--force-probe-execution"; + if (!version.isEmpty()) + resolveParams.arguments << ("modules.cpp.cxxLanguageVersion:" + version); + QCOMPARE(runQbs(resolveParams), 0); + QString mapKey; + if (m_qbsStdout.contains("is newer MSVC: true")) + mapKey = "msvc-new"; + else if (m_qbsStdout.contains("is older MSVC: true")) + mapKey = "msvc_old"; + else if (m_qbsStdout.contains("is GCC: true")) + mapKey = "gcc"; + QVERIFY2(!mapKey.isEmpty(), m_qbsStdout.constData()); + QbsRunParameters buildParams; + buildParams.expectFailure = mapKey == "gcc" && (version == "c++17" || version == "c++21"); + buildParams.arguments = QStringList({"--command-echo-mode", "command-line"}); + const int retVal = runQbs(buildParams); + if (!buildParams.expectFailure) + QCOMPARE(retVal, 0); + const QString requiredFlag = requiredFlags.value(mapKey).toString(); + if (!requiredFlag.isEmpty()) + QVERIFY2(m_qbsStdout.contains(requiredFlag.toLocal8Bit()), m_qbsStdout.constData()); + const QString forbiddenFlag = forbiddenFlags.value(mapKey).toString(); + if (!forbiddenFlag.isEmpty()) + QVERIFY2(!m_qbsStdout.contains(forbiddenFlag.toLocal8Bit()), m_qbsStdout.constData()); +} + +void TestBlackbox::cxxLanguageVersion_data() +{ + QTest::addColumn<QString>("version"); + QTest::addColumn<QVariantMap>("requiredFlags"); + QTest::addColumn<QVariantMap>("forbiddenFlags"); + + QTest::newRow("C++98") + << QString("c++98") + << QVariantMap({std::make_pair(QString("gcc"), QString("-std=c++98"))}) + << QVariantMap({std::make_pair(QString("msvc-old"), QString("/std:")), + std::make_pair(QString("msvc-new"), QString("/std:"))}); + QTest::newRow("C++11") + << QString("c++11") + << QVariantMap({std::make_pair(QString("gcc"), QString("-std=c++0x"))}) + << QVariantMap({std::make_pair(QString("msvc-old"), QString("/std:")), + std::make_pair(QString("msvc-new"), QString("/std:"))}); + QTest::newRow("C++14") + << QString("c++14") + << QVariantMap({std::make_pair(QString("gcc"), QString("-std=c++1y")), + std::make_pair(QString("msvc-new"), QString("/std:c++14")) + }) + << QVariantMap({std::make_pair(QString("msvc-old"), QString("/std:"))}); + QTest::newRow("C++17") + << QString("c++17") + << QVariantMap({std::make_pair(QString("gcc"), QString("-std=c++17")), + std::make_pair(QString("msvc-new"), QString("/std:c++latest")) + }) + << QVariantMap({std::make_pair(QString("msvc-old"), QString("/std:"))}); + QTest::newRow("C++21") + << QString("c++21") + << QVariantMap({std::make_pair(QString("gcc"), QString("-std=c++21")), + std::make_pair(QString("msvc-new"), QString("/std:c++latest")) + }) + << QVariantMap({std::make_pair(QString("msvc-old"), QString("/std:"))}); + QTest::newRow("default") + << QString() + << QVariantMap() + << QVariantMap({std::make_pair(QString("gcc"), QString("-std=")), + std::make_pair(QString("msvc-old"), QString("/std:")), + std::make_pair(QString("msvc-new"), QString("/std:"))}); +} + void TestBlackbox::cpuFeatures() { QDir::setCurrent(testDataDir + "/cpu-features"); @@ -3503,6 +3580,23 @@ void TestBlackbox::requireDeprecated() m_qbsStderr.constData()); } +void TestBlackbox::rescueTransformerData() +{ + QDir::setCurrent(testDataDir + "/rescue-transformer-data"); + QCOMPARE(runQbs(), 0); + QVERIFY2(m_qbsStdout.contains("compiling main.cpp") && m_qbsStdout.contains("m.p: undefined"), + m_qbsStdout.constData()); + WAIT_FOR_NEW_TIMESTAMP(); + touch("main.cpp"); + QCOMPARE(runQbs(), 0); + QVERIFY2(m_qbsStdout.contains("compiling main.cpp") && !m_qbsStdout.contains("m.p: "), + m_qbsStdout.constData()); + QCOMPARE(runQbs(QbsRunParameters("resolve", QStringList("modules.m.p:true"))), 0); + QCOMPARE(runQbs(), 0); + QVERIFY2(!m_qbsStdout.contains("compiling main.cpp") && m_qbsStdout.contains("m.p: true"), + m_qbsStdout.constData()); +} + void TestBlackbox::multipleChanges() { QDir::setCurrent(testDataDir + "/multiple-changes"); @@ -4200,6 +4294,20 @@ void TestBlackbox::checkProjectFilePath() QVERIFY2(m_qbsStdout.contains("main2.cpp"), m_qbsStdout.constData()); } +void TestBlackbox::checkTimestamps() +{ + QDir::setCurrent(testDataDir + "/check-timestamps"); + QCOMPARE(runQbs(), 0); + QVERIFY2(m_qbsStdout.contains("compiling file.cpp"), m_qbsStdout.constData()); + QVERIFY2(m_qbsStdout.contains("compiling main.cpp"), m_qbsStdout.constData()); + QVERIFY(QFile::remove(relativeBuildGraphFilePath())); + WAIT_FOR_NEW_TIMESTAMP(); + touch("file.h"); + QCOMPARE(runQbs(QStringList("--check-timestamps")), 0); + QVERIFY2(m_qbsStdout.contains("compiling file.cpp"), m_qbsStdout.constData()); + QVERIFY2(!m_qbsStdout.contains("compiling main.cpp"), m_qbsStdout.constData()); +} + class TemporaryDefaultProfileRemover { public: diff --git a/tests/auto/blackbox/tst_blackbox.h b/tests/auto/blackbox/tst_blackbox.h index 35e5ffe97..b08f43dd8 100644 --- a/tests/auto/blackbox/tst_blackbox.h +++ b/tests/auto/blackbox/tst_blackbox.h @@ -55,6 +55,7 @@ private slots: void changeInImportedFile(); void changeTrackingAndMultiplexing(); void checkProjectFilePath(); + void checkTimestamps(); void clean(); void cli(); void combinedSources(); @@ -65,6 +66,8 @@ private slots: void conditionalFileTagger(); void configure(); void conflictingArtifacts(); + void cxxLanguageVersion(); + void cxxLanguageVersion_data(); void cpuFeatures(); void dependenciesProperty(); void dependencyProfileMismatch(); @@ -168,6 +171,7 @@ private slots: void reproducibleBuild_data(); void require(); void requireDeprecated(); + void rescueTransformerData(); void responseFiles(); void ruleConditions(); void ruleCycle(); diff --git a/tests/auto/tools/tst_tools.cpp b/tests/auto/tools/tst_tools.cpp index 9cfbb08e3..dca88f438 100644 --- a/tests/auto/tools/tst_tools.cpp +++ b/tests/auto/tools/tst_tools.cpp @@ -88,12 +88,21 @@ void TestTools::testFileInfo() QCOMPARE(FileInfo::path("C:/fileInDriveRoot"), QString("C:/")); QVERIFY(!FileInfo::isAbsolute("bla/lol")); QVERIFY(FileInfo::isAbsolute("/bla/lol")); - if (HostOsInfo::isWindowsHost()) + if (HostOsInfo::isWindowsHost()) { QVERIFY(FileInfo::isAbsolute("C:\\bla\\lol")); + QVERIFY(FileInfo::isAbsolute("C:\\")); + QVERIFY(FileInfo::isAbsolute("C:/")); + QVERIFY(!FileInfo::isAbsolute("C:")); + } QCOMPARE(FileInfo::resolvePath("/abc/lol", "waffl"), QString("/abc/lol/waffl")); QCOMPARE(FileInfo::resolvePath("/abc/def/ghi/jkl/", "../foo/bar"), QString("/abc/def/ghi/foo/bar")); QCOMPARE(FileInfo::resolvePath("/abc/def/ghi/jkl/", "../../foo/bar"), QString("/abc/def/foo/bar")); QCOMPARE(FileInfo::resolvePath("/abc", "../../../foo/bar"), QString("/foo/bar")); + if (HostOsInfo::isWindowsHost()) { + QCOMPARE(FileInfo::resolvePath("C:/share", ".."), QString("C:/")); + QCOMPARE(FileInfo::resolvePath("C:/share", "D:/"), QString("D:/")); + QCOMPARE(FileInfo::resolvePath("C:/share", "D:"), QString()); // should soft-assert + } QCOMPARE(FileInfo("/does/not/exist").lastModified(), FileTime()); } |