diff options
author | Christian Kandeler <christian.kandeler@qt.io> | 2019-07-05 13:46:41 +0200 |
---|---|---|
committer | Christian Kandeler <christian.kandeler@qt.io> | 2019-07-05 13:46:41 +0200 |
commit | 886a98d6137692f89de80aab07278b7c6d9227d8 (patch) | |
tree | 094ee7dda5babafd7536b0e95c7ae535a62eca74 | |
parent | 590a58c3702de17ba7b44c9041dfe5d3e88bea31 (diff) | |
parent | 27b78ea67f82fca051666599603273c12ecb403b (diff) |
Merge 1.14 into master
Change-Id: I2796d53ca36ce90977f0a09ca5db089d8c85bd4e
51 files changed, 1041 insertions, 90 deletions
diff --git a/doc/reference/items/probe/path-probe.qdoc b/doc/reference/items/probe/path-probe.qdoc index 298ce9d95..eaaee9e54 100644 --- a/doc/reference/items/probe/path-probe.qdoc +++ b/doc/reference/items/probe/path-probe.qdoc @@ -58,6 +58,42 @@ */ /*! + \qmlproperty varList PathProbe::allResults + + This property contains the list of objects, each object representing a single found file: + \code + { + found: true, + candidatePaths: ["path1/to/file", "path2/to/file", ...] + filePath: "path/to/file" + fileName: "file" + path: "path/to" + } + \endcode + \sa {PathProbe::filePath}{filePath}, {PathProbe::fileName}{fileName}, + {PathProbe::path}{path} +*/ + +/*! + \qmlproperty var PathProbe::candidateFilter + + This property holds the function that can be used to filter out unsuitable candidates. + For example, when searching for a library, it might be necessary to check its architecture: + \code + PathProbe { + pathSuffixes: ["*.so", *.dll] + candidateFilter: { + function getLibraryArchitecture(file) { ... } + return function(file) { + return Utilities.isSharedLibrary(file) + && getLibraryArchitecture(file) == qbs.architecture; + } + } + } + \endcode +*/ + +/*! \qmlproperty stringList PathProbe::names The list of file names to search for. @@ -69,8 +105,7 @@ \qmlproperty stringList PathProbe::nameSuffixes The list of file suffixes to search for. These suffixes are appended to every file name passed - via the \l names property. If \l names is empty, the probe looks for any file that ends with the - given suffix. + via the \l names property. \nodefaultvalue */ @@ -161,3 +196,24 @@ \nodefaultvalue */ + +/*! + \qmlproperty varList PathProbe::selectors + + This property should be used to search for multiple files. It contains the list of selectors + each of which describes a single file to search for. A selector can be either a string, a + stringList, or a dictionary. + + The following example searches for three files and illustrates different ways to specify + selectors: + \code + selectors: [ + // 1st file with a single name + "header.h", + // 2nd file with possible name variants + ["config.h", "foo-config.h", "bar-config.h"], + // 3rd file with possible name and suffix variants + {names: ["footk", "footk-version"], nameSuffixes: [".h", ".hpp"]} + ] + \endcode +*/ diff --git a/doc/reference/modules/protobufcpp-module.qdoc b/doc/reference/modules/protobufcpp-module.qdoc index 63c56c6ca..b82ccc9f7 100644 --- a/doc/reference/modules/protobufcpp-module.qdoc +++ b/doc/reference/modules/protobufcpp-module.qdoc @@ -68,10 +68,33 @@ \li \c{*.proto} \li 1.13.0 \li Source files with this tag are considered inputs to the \c protoc compiler. + \row + \li \c{"protobuf.grpc"} + \li + \li 1.14.0 + \li Source files with this tag are considered as gRPC files. \endtable */ /*! + \qmlproperty string protobuf.cpp::grpcIncludePath + + The path where grpc++ headers are located. Set this property to override the + default location. + + \defaultvalue \c auto-detected +*/ + +/*! + \qmlproperty string protobuf.cpp::grpcLibraryPath + + The path where the grpc++ library is located. Set this property to override the + default location. + + \defaultvalue \c auto-detected +*/ + +/*! \qmlproperty string protobuf.cpp::protocBinary The command to invoke when compiling proto definition files. @@ -108,3 +131,67 @@ \defaultvalue \c auto-detected */ + +/*! + \qmlproperty bool protobuf.cpp::useGrpc + + Whether to use gRPC framework. + + Use this property to enable support for the modern open source high performance RPC + framework by Google, gRPC (\l{https://www.grpc.io}). + + A simple qbs file that uses grpc can be written as follows: + \code + CppApplication { + Depends { name: "protobuf.cpp" } + protobuf.cpp.useGrpc: true + files: ["main.cpp"] + Group { + files: "grpc.proto" + fileTags: "protobuf.grpc" + } + } + \endcode + + \note that \c protobuf.grpc tag should be assigned manually because gRPC uses same \c *.proto + files and \QBS can't detect whether to generate gRPC or \c protobuf. + + The following \c grpc.proto file... + \code + syntax = "proto3"; + + package Qbs; + + message Request { + string name = 1; + } + + message Response { + string name = 1; + } + + service Grpc { + rpc doWork(Request) returns (Response) {} + } + \endcode + + ...can be used in the C++ sources as follows: + \code + #include <grpc.grpc.pb.h> + + class Service final : public Qbs::Grpc::Service + { + grpc::Status doWork( + grpc::ServerContext* context, + const Qbs::Request* request, + Qbs::Response* reply) override + { + (void)context; + reply->set_name(request->name()); + return grpc::Status::OK; + } + }; + \endcode + + \defaultvalue \c false +*/ diff --git a/examples/cocoa-touch-application/CocoaTouchApplication.qbs b/examples/cocoa-touch-application/CocoaTouchApplication.qbs index f8db6768f..cf0a273d5 100644 --- a/examples/cocoa-touch-application/CocoaTouchApplication.qbs +++ b/examples/cocoa-touch-application/CocoaTouchApplication.qbs @@ -52,11 +52,13 @@ import qbs 1.0 CppApplication { + Depends { name: "xcode"; required: false } Depends { condition: product.condition; name: "ib" } - condition: qbs.hostOS.contains("macos") + condition: qbs.hostOS.contains("macos") && xcode.present name: "Cocoa Touch Application" qbs.targetPlatform: "ios" + qbs.architecture: "arm64" cpp.useObjcPrecompiledHeader: true cpp.minimumIosVersion: "8.0" diff --git a/examples/examples.qbs b/examples/examples.qbs index c6c1f4ac3..abb6d5d9a 100644 --- a/examples/examples.qbs +++ b/examples/examples.qbs @@ -58,6 +58,7 @@ Project { "code-generator/code-generator.qbs", "collidingmice/collidingmice.qbs", "compiled-qml/myapp.qbs", + "grpc/ping-pong-grpc.qbs", "helloworld-complex/hello.qbs", "helloworld-minimal/hello.qbs", "helloworld-qt/hello.qbs", diff --git a/examples/grpc/client.cpp b/examples/grpc/client.cpp new file mode 100644 index 000000000..55bcd866d --- /dev/null +++ b/examples/grpc/client.cpp @@ -0,0 +1,95 @@ +/**************************************************************************** +** +** Copyright (C) 2019 Ivan Komissarov +** Contact: abbapoh@gmail.com +** +** 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. +** +****************************************************************************/ + +#ifdef __GNUC__ + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wunused-parameter" +#endif // __GNUC__ + +#include <ping-pong-grpc.grpc.pb.h> + +#include <grpc/grpc.h> +#include <grpc++/channel.h> +#include <grpc++/client_context.h> +#include <grpc++/create_channel.h> + +#ifdef __GNUC__ + #pragma GCC diagnostic pop +#endif // __GNUC__ + +#include <iostream> +#include <memory> +#include <string> + +class Client +{ +public: + Client(std::shared_ptr<grpc::Channel> channel) + : m_stub(PP::MyApi::NewStub(channel)) {} + + int ping(int count) { + PP::Ping request; + request.set_count(count); + PP::Pong reply; + grpc::ClientContext context; + + const auto status = m_stub->pingPong(&context, request, &reply); + if (status.ok()) { + return reply.count(); + } else { + throw std::runtime_error("invalid status"); + } + } + +private: + std::unique_ptr<PP::MyApi::Stub> m_stub; +}; + +int main(int, char**) +{ + Client client( + grpc::CreateCustomChannel( + "localhost:50051", + grpc::InsecureChannelCredentials(), + grpc::ChannelArguments())); + + for (int i = 0; i < 1000; ++i) { + std::cout << "Sending ping " << i << "... "; + int result = client.ping(i); + if (result != i) { + std::cerr << "Invalid pong " << result << " for ping" << i << std::endl; + continue; + } + std::cout << "got pong " << result << std::endl; + } + + return 0; +} + diff --git a/examples/grpc/ping-pong-grpc.proto b/examples/grpc/ping-pong-grpc.proto new file mode 100644 index 000000000..da6d9490c --- /dev/null +++ b/examples/grpc/ping-pong-grpc.proto @@ -0,0 +1,15 @@ +syntax = "proto3"; + +package PP; + +message Ping { + int32 count = 1; +} + +message Pong { + int32 count = 1; +} + +service MyApi { + rpc pingPong(Ping) returns (Pong) {} +} diff --git a/examples/grpc/ping-pong-grpc.qbs b/examples/grpc/ping-pong-grpc.qbs new file mode 100644 index 000000000..8d8909350 --- /dev/null +++ b/examples/grpc/ping-pong-grpc.qbs @@ -0,0 +1,65 @@ +/**************************************************************************** +** +** Copyright (C) 2019 Ivan Komissarov +** Contact: abbapoh@gmail.com +** +** 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. +** +****************************************************************************/ + +import qbs.Utilities + +Project { + condition: Utilities.versionCompare(qbs.version, "1.14") >= 0 + + Application { + Depends { name: "cpp" } + Depends { name: "protobuf.cpp"; required: false } + condition: protobuf.cpp.present + protobuf.cpp.useGrpc: true + consoleApplication: true + cpp.cxxLanguageVersion: "c++17" + name: "client" + files: "client.cpp" + Group { + files: "ping-pong-grpc.proto" + fileTags: "protobuf.grpc" + } + } + + Application { + Depends { name: "cpp" } + Depends { name: "protobuf.cpp"; required: false } + condition: protobuf.cpp.present + protobuf.cpp.useGrpc: true + consoleApplication: true + cpp.cxxLanguageVersion: "c++17" + name: "server" + files: "client.cpp" + Group { + files: "ping-pong-grpc.proto" + fileTags: "protobuf.grpc" + } + } +} diff --git a/examples/grpc/server.cpp b/examples/grpc/server.cpp new file mode 100644 index 000000000..89d6f0096 --- /dev/null +++ b/examples/grpc/server.cpp @@ -0,0 +1,78 @@ +/**************************************************************************** +** +** Copyright (C) 2019 Ivan Komissarov +** Contact: abbapoh@gmail.com +** +** 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. +** +****************************************************************************/ + +#ifdef __GNUC__ + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wunused-parameter" +#endif // __GNUC__ + +#include <ping-pong-grpc.grpc.pb.h> + +#include <grpc/grpc.h> +#include <grpc++/server.h> +#include <grpc++/server_builder.h> +#include <grpc++/server_context.h> +#include <grpc++/security/server_credentials.h> + +#ifdef __GNUC__ + #pragma GCC diagnostic pop +#endif // __GNUC__ + +#include <iostream> +#include <memory> +#include <string> + +class Service final : public PP::MyApi::Service +{ + grpc::Status pingPong( + grpc::ServerContext* context, + const PP::Ping* request, + PP::Pong* reply) override + { + (void)context; + reply->set_count(request->count()); + return grpc::Status::OK; + } +}; + +int main(int, char**) +{ + std::string server_address("0.0.0.0:50051"); + Service service; + + grpc::ServerBuilder builder; + builder.AddListeningPort(server_address, grpc::InsecureServerCredentials()); + builder.RegisterService(&service); + std::unique_ptr<grpc::Server> server(builder.BuildAndStart()); + std::cout << "Server listening on " << server_address << std::endl; + server->Wait(); + return 0; +} + diff --git a/qbs-resources/modules/qbsbuildconfig/qbsbuildconfig.qbs b/qbs-resources/modules/qbsbuildconfig/qbsbuildconfig.qbs index bcdf4efd2..c2db0189f 100644 --- a/qbs-resources/modules/qbsbuildconfig/qbsbuildconfig.qbs +++ b/qbs-resources/modules/qbsbuildconfig/qbsbuildconfig.qbs @@ -1,5 +1,6 @@ import qbs import qbs.FileInfo +import qbs.Utilities Module { Depends { @@ -47,8 +48,17 @@ Module { "share/qbs/qml-type-descriptions") Properties { - condition: project.withCode && enableAddressSanitizer && qbs.toolchain.contains("gcc") - cpp.cxxFlags: "-fno-omit-frame-pointer" - cpp.driverFlags: "-fsanitize=address" + condition: project.withCode && qbs.toolchain.contains("gcc") + cpp.cxxFlags: { + var flags = []; + if (enableAddressSanitizer) + flags.push("-fno-omit-frame-pointer"); + if (!qbs.toolchain.contains("clang") + && Utilities.versionCompare(cpp.compilerVersion, "9") >= 0) { + flags.push("-Wno-deprecated-copy", "-Wno-init-list-lifetime"); + } + return flags; + } + cpp.driverFlags: enableAddressSanitizer ? ["-fsanitize=address"] : [] } } diff --git a/scripts/build-qbs-with-qbs.sh b/scripts/build-qbs-with-qbs.sh index 15a4649b5..5956f1cff 100755 --- a/scripts/build-qbs-with-qbs.sh +++ b/scripts/build-qbs-with-qbs.sh @@ -53,6 +53,7 @@ BUILD_OPTIONS="\ modules.qbsbuildconfig.enableAddressSanitizer:true \ modules.qbsbuildconfig.enableProjectFileUpdates:true \ modules.qbsbuildconfig.enableUnitTests:true \ + project.withExamples:true \ ${BUILD_OPTIONS} " diff --git a/share/qbs/imports/qbs/Probes/GccBinaryProbe.qbs b/share/qbs/imports/qbs/Probes/GccBinaryProbe.qbs index 6ebaff8be..693fb6a01 100644 --- a/share/qbs/imports/qbs/Probes/GccBinaryProbe.qbs +++ b/share/qbs/imports/qbs/Probes/GccBinaryProbe.qbs @@ -47,14 +47,17 @@ BinaryProbe { } configure: { - var result = PathProbeConfigure.configure(names, nameSuffixes, nameFilter, searchPaths, - pathSuffixes, platformSearchPaths, environmentPaths, - platformEnvironmentPaths, pathListSeparator); - found = result.found; - candidatePaths = result.candidatePaths; - path = result.path; - filePath = result.filePath; - fileName = result.fileName; + var selectors; + var _results = PathProbeConfigure.configure( + selectors, names, nameSuffixes, nameFilter, candidateFilter, searchPaths, + pathSuffixes, platformSearchPaths, environmentPaths, platformEnvironmentPaths, + pathListSeparator); + found = _results.found; + var resultFile = _results.files[0]; + candidatePaths = resultFile.candidatePaths; + path = resultFile.path; + filePath = resultFile.filePath; + fileName = resultFile.fileName; (nameSuffixes || [""]).forEach(function(suffix) { var end = _compilerName + suffix; if (fileName.endsWith(end)) diff --git a/share/qbs/imports/qbs/Probes/NpmProbe.qbs b/share/qbs/imports/qbs/Probes/NpmProbe.qbs index 4e8124d56..f6a99e826 100644 --- a/share/qbs/imports/qbs/Probes/NpmProbe.qbs +++ b/share/qbs/imports/qbs/Probes/NpmProbe.qbs @@ -47,26 +47,28 @@ NodeJsProbe { if (!interpreterPath) throw '"interpreterPath" must be specified'; - var result = PathProbeConfigure.configure(names, nameSuffixes, nameFilter, searchPaths, - pathSuffixes, platformSearchPaths, - environmentPaths, platformEnvironmentPaths, - pathListSeparator); + var selectors; + var results = PathProbeConfigure.configure( + selectors, names, nameSuffixes, nameFilter, candidateFilter, searchPaths, + pathSuffixes, platformSearchPaths, environmentPaths, platformEnvironmentPaths, + pathListSeparator); var v = new ModUtils.EnvironmentVariable("PATH", pathListSeparator, hostOS.contains("windows")); v.prepend(interpreterPath); - result.npmBin = result.found + var result = results.files[0]; + result.npmBin = results.found ? NodeJs.findLocation(result.filePath, "bin", v.value) : undefined; - result.npmRoot = result.found + result.npmRoot = results.found ? NodeJs.findLocation(result.filePath, "root", v.value) : undefined; - result.npmPrefix = result.found + result.npmPrefix = results.found ? NodeJs.findLocation(result.filePath, "prefix", v.value) : undefined; - found = result.found; + found = results.found; candidatePaths = result.candidatePaths; path = result.path; filePath = result.filePath; diff --git a/share/qbs/imports/qbs/Probes/PathProbe.qbs b/share/qbs/imports/qbs/Probes/PathProbe.qbs index 1235ce211..d0edea682 100644 --- a/share/qbs/imports/qbs/Probes/PathProbe.qbs +++ b/share/qbs/imports/qbs/Probes/PathProbe.qbs @@ -36,6 +36,8 @@ Probe { property stringList names property stringList nameSuffixes property var nameFilter + property var candidateFilter + property varList selectors property pathList pathPrefixes property pathList searchPaths property stringList pathSuffixes @@ -52,6 +54,8 @@ Probe { property string filePath property string fileName + property varList allResults + configure: { if (pathPrefixes) console.warn("PathProbe.pathPrefixes is deprecated, use searchPaths instead"); @@ -59,11 +63,14 @@ Probe { console.warn("PathProbe.platformPaths is deprecated, use platformSearchPaths instead"); var _searchPaths = ModUtils.concatAll(pathPrefixes, searchPaths); var _platformSearchPaths = ModUtils.concatAll(platformPaths, platformSearchPaths); - var result = PathProbeConfigure.configure(names, nameSuffixes, nameFilter, _searchPaths, - pathSuffixes, _platformSearchPaths, - environmentPaths, platformEnvironmentPaths, - pathListSeparator); - found = result.found; + var results = PathProbeConfigure.configure(selectors, names, nameSuffixes, nameFilter, + candidateFilter, _searchPaths, pathSuffixes, + _platformSearchPaths, environmentPaths, + platformEnvironmentPaths, pathListSeparator); + found = results.found; + allResults = results.files; + + var result = allResults[0]; candidatePaths = result.candidatePaths; path = result.path; filePath = result.filePath; diff --git a/share/qbs/imports/qbs/Probes/TypeScriptProbe.qbs b/share/qbs/imports/qbs/Probes/TypeScriptProbe.qbs index de28fa327..a35e555cc 100644 --- a/share/qbs/imports/qbs/Probes/TypeScriptProbe.qbs +++ b/share/qbs/imports/qbs/Probes/TypeScriptProbe.qbs @@ -57,16 +57,18 @@ BinaryProbe { if (!packageManagerRootPath) throw '"packageManagerRootPath" must be specified'; - var result = PathProbeConfigure.configure(names, nameSuffixes, nameFilter, searchPaths, - pathSuffixes, platformSearchPaths, - environmentPaths, platformEnvironmentPaths, - pathListSeparator); + var selectors; + var results = PathProbeConfigure.configure( + selectors, names, nameSuffixes, nameFilter, candidateFilter, searchPaths, + pathSuffixes, platformSearchPaths, environmentPaths, platformEnvironmentPaths, + pathListSeparator); var v = new ModUtils.EnvironmentVariable("PATH", pathListSeparator, hostOS.contains("windows")); v.prepend(interpreterPath); - result.version = result.found + var result = results.files[0]; + result.version = results.found ? TypeScript.findTscVersion(result.filePath, v.value) : undefined; if (FileInfo.fromNativeSeparators(packageManagerBinPath) !== result.path || @@ -74,7 +76,7 @@ BinaryProbe { result = { found: false }; } - found = result.found; + found = results.found; candidatePaths = result.candidatePaths; path = result.path; filePath = result.filePath; diff --git a/share/qbs/imports/qbs/Probes/path-probe.js b/share/qbs/imports/qbs/Probes/path-probe.js index a48a7e4fe..a997f77f2 100644 --- a/share/qbs/imports/qbs/Probes/path-probe.js +++ b/share/qbs/imports/qbs/Probes/path-probe.js @@ -33,17 +33,60 @@ var File = require("qbs.File"); var FileInfo = require("qbs.FileInfo"); var ModUtils = require("qbs.ModUtils"); -function configure(names, nameSuffixes, nameFilter, searchPaths, pathSuffixes, platformSearchPaths, - environmentPaths, platformEnvironmentPaths, pathListSeparator) { - var result = { found: false, candidatePaths: [] }; - if (!names) - throw '"names" must be specified'; - var _names = ModUtils.concatAll(names); - if (nameFilter) - _names = _names.map(function(n) { return nameFilter(n); }); - _names = ModUtils.concatAll.apply(undefined, _names.map(function(name) { - return (nameSuffixes || [""]).map(function(suffix) { return name + suffix; }); - })); +function asStringList(key, value) { + if (typeof(value) === "string") + return [value]; + if (Array.isArray(value)) + return value; + throw key + " must be a string or a stringList"; +} + +function canonicalSelectors(selectors) { + var mapper = function(selector) { + if (typeof(selector) === "string") + return {names : [selector]}; + if (Array.isArray(selector)) + return {names : selector}; + // dict + if (!selector.names) + throw '"names" must be specified'; + selector.names = asStringList("names", selector.names); + if (selector.nameSuffixes) + selector.nameSuffixes = asStringList("nameSuffixes", selector.nameSuffixes); + return selector; + }; + return selectors.map(mapper); +} + +function configure(selectors, names, nameSuffixes, nameFilter, candidateFilter, + searchPaths, pathSuffixes, platformSearchPaths, environmentPaths, + platformEnvironmentPaths, pathListSeparator) { + var result = { found: false, files: [] }; + if (!selectors && !names) + throw '"names" or "selectors" must be specified'; + + if (!selectors) { + selectors = [ + {names: names, nameSuffixes: nameSuffixes} + ]; + } else { + selectors = canonicalSelectors(selectors); + } + + if (nameFilter) { + selectors.forEach(function(selector) { + selector.names = selector.names.map(nameFilter); + }); + } + + selectors.forEach(function(selector) { + selector.names = ModUtils.concatAll.apply(undefined, selector.names.map(function(name) { + return (selector.nameSuffixes || [""]).map(function(suffix) { + return name + suffix; + }); + })); + }); + // FIXME: Suggest how to obtain paths from system var _paths = ModUtils.concatAll(searchPaths, platformSearchPaths); // FIXME: Add getenv support @@ -56,28 +99,38 @@ function configure(names, nameSuffixes, nameFilter, searchPaths, pathSuffixes, p var _suffixes = ModUtils.concatAll('', pathSuffixes); _paths = _paths.map(function(p) { return FileInfo.fromNativeSeparators(p); }); _suffixes = _suffixes.map(function(p) { return FileInfo.fromNativeSeparators(p); }); - for (i = 0; i < _names.length; ++i) { - for (var j = 0; j < _paths.length; ++j) { - for (var k = 0; k < _suffixes.length; ++k) { - var _filePath = FileInfo.joinPaths(_paths[j], _suffixes[k], _names[i]); - result.candidatePaths.push(_filePath); - if (File.exists(_filePath)) { - result.found = true; - result.filePath = _filePath; - // Manually specify the path components that constitute _filePath rather - // than using the FileInfo.path and FileInfo.fileName functions because we - // want to break _filePath into its constituent parts based on the input - // originally given by the user. For example, the FileInfo functions would - // produce a different result if any of the items in the names property - // contained more than a single path component. - result.fileName = _names[i]; - result.path = FileInfo.joinPaths(_paths[j], _suffixes[k]); - return result; + var findFile = function(selector) { + var file = { found: false, candidatePaths: [] }; + for (var i = 0; i < selector.names.length; ++i) { + for (var j = 0; j < _paths.length; ++j) { + for (var k = 0; k < _suffixes.length; ++k) { + var _filePath = FileInfo.joinPaths(_paths[j], _suffixes[k], selector.names[i]); + file.candidatePaths.push(_filePath); + if (File.exists(_filePath) + && (!candidateFilter || candidateFilter(_filePath))) { + file.found = true; + file.filePath = _filePath; + + // Manually specify the path components that constitute _filePath rather + // than using the FileInfo.path and FileInfo.fileName functions because we + // want to break _filePath into its constituent parts based on the input + // originally given by the user. For example, the FileInfo functions would + // produce a different result if any of the items in the names property + // contained more than a single path component. + file.fileName = selector.names[i]; + file.path = FileInfo.joinPaths(_paths[j], _suffixes[k]); + return file; + } } } } - } + + return file; + }; + + result.files = selectors.map(findFile); + result.found = result.files.reduce(function(acc, value) { return acc && value.found }, true); return result; } diff --git a/share/qbs/modules/cpp/iar.js b/share/qbs/modules/cpp/iar.js index 646826e8a..3098c88f6 100644 --- a/share/qbs/modules/cpp/iar.js +++ b/share/qbs/modules/cpp/iar.js @@ -64,7 +64,7 @@ function dumpMacros(compilerFilePath, tag) { var args = [ inFilePath, "--predef_macros", outFilePath ]; if (tag && tag === "cpp") - args.push("--ec++"); + args.push("--c++"); var p = new Process(); p.exec(compilerFilePath, args, true); @@ -84,7 +84,7 @@ function dumpDefaultPaths(compilerFilePath, tag) { var args = [ inFilePath, "--preinclude", "." ]; if (tag === "cpp") - args.push("--ec++"); + args.push("--c++"); var p = new Process(); // This process should return an error, don't throw diff --git a/share/qbs/modules/cpp/ios-gcc.qbs b/share/qbs/modules/cpp/ios-gcc.qbs index 735766650..0ff99679f 100644 --- a/share/qbs/modules/cpp/ios-gcc.qbs +++ b/share/qbs/modules/cpp/ios-gcc.qbs @@ -50,8 +50,8 @@ DarwinGCC { : "-iphoneos_version_min" libcxxAvailable: base - && minimumDarwinVersion - && Utilities.versionCompare(minimumDarwinVersion, "5") >= 0 + && (!minimumDarwinVersion + || Utilities.versionCompare(minimumDarwinVersion, "5") >= 0) platformObjcFlags: base.concat(simulatorObjcFlags) platformObjcxxFlags: base.concat(simulatorObjcFlags) diff --git a/share/qbs/modules/protobuf/cpp/protobufcpp.qbs b/share/qbs/modules/protobuf/cpp/protobufcpp.qbs index 2b6e94e83..0c511f2aa 100644 --- a/share/qbs/modules/protobuf/cpp/protobufcpp.qbs +++ b/share/qbs/modules/protobuf/cpp/protobufcpp.qbs @@ -2,6 +2,7 @@ import qbs import qbs.File import qbs.FileInfo import qbs.Probes +import qbs.ModUtils import "../protobufbase.qbs" as ProtobufBase import "../protobuf.js" as HelperFunctions @@ -9,23 +10,68 @@ ProtobufBase { property string includePath: includeProbe.path property string libraryPath: libraryProbe.path + property bool useGrpc: false + + property string grpcIncludePath: grpcIncludeProbe.path + property string grpcLibraryPath: grpcLibraryProbe.path + Depends { name: "cpp" } - cpp.libraryPaths: [libraryPath] - cpp.dynamicLibraries: qbs.targetOS.contains("unix") ? ["protobuf", "pthread"] : ["protobuf"] - cpp.includePaths: [outputDir, includePath] + property path grpcPluginPath: grpcPluginProbe.filePath + + Probes.BinaryProbe { + condition: useGrpc + id: grpcPluginProbe + names: "grpc_cpp_plugin" + } + + cpp.libraryPaths: { + var result = [libraryPath]; + if (useGrpc) + result.push(grpcLibraryPath); + return result; + } + cpp.dynamicLibraries: { + var result = ["protobuf"]; + if (qbs.targetOS.contains("unix")) + result.push("pthread"); + if (useGrpc) + result.push("grpc++"); + return result; + } + cpp.includePaths: { + var result = [outputDir, includePath]; + if (useGrpc) + result.push("grpcIncludePath"); + return result; + } Rule { - inputs: ["protobuf.input"] + inputs: ["protobuf.input", "protobuf.grpc"] outputFileTags: ["hpp", "cpp"] outputArtifacts: { - return [ - HelperFunctions.cppArtifact(input.protobuf.cpp, product, input, "hpp", ".pb.h"), - HelperFunctions.cppArtifact(input.protobuf.cpp, product, input, "cpp", ".pb.cc") - ]; + var result = [ + HelperFunctions.cppArtifact(input.protobuf.cpp, product, input, "hpp", ".pb.h"), + HelperFunctions.cppArtifact(input.protobuf.cpp, product, input, "cpp", ".pb.cc") + ]; + if (input.fileTags.contains("protobuf.grpc")) { + result.push( + HelperFunctions.cppArtifact(input.protobuf.cpp, product, input, "hpp", ".grpc.pb.h"), + HelperFunctions.cppArtifact(input.protobuf.cpp, product, input, "cpp", ".grpc.pb.cc")); + } + + return result; } - prepare: HelperFunctions.doPrepare(input.protobuf.cpp, product, input, outputs, "cpp") + prepare: { + var result = HelperFunctions.doPrepare( + input.protobuf.cpp, product, input, outputs, "cpp"); + if (input.fileTags.contains("protobuf.grpc")) { + result = ModUtils.concatAll(result, HelperFunctions.doPrepareGrpc( + input.protobuf.cpp, product, input, outputs, "cpp")); + } + return result; + } } validateFunc: { @@ -35,6 +81,15 @@ ProtobufBase { throw "Can't find cpp protobuf include files. Please set the includePath property."; if (!HelperFunctions.checkPath(libraryPath)) throw "Can't find cpp protobuf library. Please set the libraryPath property."; + + if (useGrpc) { + if (!File.exists(grpcPluginPath)) + throw "Can't find grpc_cpp_plugin plugin. Please set the grpcPluginPath property."; + if (!HelperFunctions.checkPath(grpcIncludePath)) + throw "Can't find grpc++ include files. Please set the grpcIncludePath property."; + if (!HelperFunctions.checkPath(grpcLibraryPath)) + throw "Can't find grpc++ library. Please set the grpcLibraryPath property."; + } } } @@ -47,4 +102,15 @@ ProtobufBase { id: libraryProbe names: "protobuf" } + + Probes.IncludeProbe { + id: grpcIncludeProbe + pathSuffixes: "grpc++" + names: "grpc++.h" + } + + Probes.LibraryProbe { + id: grpcLibraryProbe + names: "grpc++" + } } diff --git a/share/qbs/modules/protobuf/protobuf.js b/share/qbs/modules/protobuf/protobuf.js index 576a5ec07..511f5a6c6 100644 --- a/share/qbs/modules/protobuf/protobuf.js +++ b/share/qbs/modules/protobuf/protobuf.js @@ -109,3 +109,28 @@ function doPrepare(module, product, input, outputs, lang) cmd.description = "generating " + lang + " files for " + input.fileName; return [cmd]; } + +function doPrepareGrpc(module, product, input, outputs, lang) +{ + var outputDir = module.outputDir; + var args = []; + + args.push("--grpc_out", outputDir); + args.push("--plugin=protoc-gen-grpc=" + module.grpcPluginPath); + + var importPaths = module.importPaths; + if (importPaths.length === 0) + importPaths = [FileInfo.path(input.filePath)]; + importPaths.forEach(function(path) { + if (!FileInfo.isAbsolutePath(path)) + path = FileInfo.joinPaths(product.sourceDirectory, path); + args.push("--proto_path", path); + }); + + args.push(input.filePath); + + var cmd = new Command(module.protocBinary, args); + cmd.highlight = "codegen"; + cmd.description = "generating " + lang + " files for " + input.fileName; + return [cmd]; +} diff --git a/src/lib/corelib/tools/launchersocket.cpp b/src/lib/corelib/tools/launchersocket.cpp index 948fbca4f..4373b10b8 100644 --- a/src/lib/corelib/tools/launchersocket.cpp +++ b/src/lib/corelib/tools/launchersocket.cpp @@ -135,6 +135,7 @@ void LauncherSocket::handleError(const QString &error) void LauncherSocket::handleRequests() { + QBS_ASSERT(isReady(), return); std::lock_guard<std::mutex> locker(m_requestsMutex); for (const QByteArray &request : qAsConst(m_requests)) m_socket->write(request); diff --git a/src/lib/corelib/tools/qttools.h b/src/lib/corelib/tools/qttools.h index 4cb39527e..b465e3d9e 100644 --- a/src/lib/corelib/tools/qttools.h +++ b/src/lib/corelib/tools/qttools.h @@ -50,9 +50,11 @@ class QProcessEnvironment; QT_END_NAMESPACE namespace std { +#if (QT_VERSION < QT_VERSION_CHECK(5, 14, 0)) template<> struct hash<QString> { std::size_t operator()(const QString &s) const { return qHash(s); } }; +#endif template<typename T1, typename T2> struct hash<std::pair<T1, T2>> { diff --git a/src/lib/library.pri b/src/lib/library.pri index 4ae171c93..11427b097 100644 --- a/src/lib/library.pri +++ b/src/lib/library.pri @@ -32,7 +32,7 @@ VERSION = $${QBS_VERSION} linux { # Turn off absurd qmake's soname "logic" and directly add the linker flag. QMAKE_LFLAGS_SONAME = - QMAKE_LFLAGS = -Wl,-soname=lib$${TARGET}.so.$${QBS_VERSION_MAJ}.$${QBS_VERSION_MIN} + QMAKE_LFLAGS += -Wl,-soname=lib$${TARGET}.so.$${QBS_VERSION_MAJ}.$${QBS_VERSION_MIN} } win32 { diff --git a/static-res.pro b/static-res.pro index 86816bbf0..cbf9aa9d2 100644 --- a/static-res.pro +++ b/static-res.pro @@ -5,8 +5,9 @@ else: qbsbindir = bin envSpec = unix:qbs_disable_rpath { + include(src/library_dirname.pri) !isEmpty(QBS_DESTDIR): qbslibdir = $$QBS_DESTDIR - else: qbslibdir = $$OUT_PWD/lib + else: qbslibdir = $$OUT_PWD/$$QBS_LIBRARY_DIRNAME macos: envVar = DYLD_LIBRARY_PATH else: envVar = LD_LIBRARY_PATH oldVal = $$getenv($$envVar) diff --git a/tests/auto/blackbox/testdata-apple/xcode/xcode-project.qbs b/tests/auto/blackbox/testdata-apple/xcode/xcode-project.qbs index d7baf8c8e..fbab6d0b1 100644 --- a/tests/auto/blackbox/testdata-apple/xcode/xcode-project.qbs +++ b/tests/auto/blackbox/testdata-apple/xcode/xcode-project.qbs @@ -1,5 +1,5 @@ Project { - property stringList sdks: [] + property var sdks: {} Product { Depends { name: "xcode" } @@ -14,7 +14,27 @@ Project { console.info("Latest SDK version: " + xcode.latestSdkVersion); console.info("Available SDK names: " + xcode.availableSdkNames.join(", ")); console.info("Available SDK versions: " + xcode.availableSdkVersions.join(", ")); - console.info("Actual SDK list: " + project.sdks.join(", ")); + + var targetOsToKey = function(targetOS) { + if (targetOS.contains("ios")) + return "iphoneos"; + if (targetOS.contains("ios-simulator")) + return "iphonesimulator"; + if (targetOS.contains("macos")) + return "macosx"; + if (targetOS.contains("tvos")) + return "appletvos"; + if (targetOS.contains("tvos-simulator")) + return "appletvsimulator"; + if (targetOS.contains("watchos")) + return "watchos"; + if (targetOS.contains("watchos-simulator")) + return "watchossimulator"; + throw "Unsupported OS" + targetOS; + } + + var actualList = project.sdks[targetOsToKey(qbs.targetOS)]; + console.info("Actual SDK list: " + actualList.join(", ")); var msg = "Unexpected SDK list [" + xcode.availableSdkVersions.join(", ") + "]"; var testArraysEqual = function(a, b) { @@ -29,7 +49,7 @@ Project { } } - testArraysEqual(project.sdks, xcode.availableSdkVersions); + testArraysEqual(actualList, xcode.availableSdkVersions); } } } diff --git a/tests/auto/blackbox/testdata/grpc/grpc.cpp b/tests/auto/blackbox/testdata/grpc/grpc.cpp new file mode 100644 index 000000000..81995601d --- /dev/null +++ b/tests/auto/blackbox/testdata/grpc/grpc.cpp @@ -0,0 +1,49 @@ +/**************************************************************************** +** +** Copyright (C) 2019 Ivan Komissarov +** Contact: abbapoh@gmail.com +** +** 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. +** +****************************************************************************/ + +#include <grpc.grpc.pb.h> + +class ServiceImpl final : public Qbs::Grpc::Service +{ + grpc::Status doWork( + grpc::ServerContext* context, + const Qbs::Request* request, + Qbs::Response* reply) override + { + (void)context; + reply->set_name(request->name()); + return grpc::Status::OK; + } +}; + +int main(int, char**) +{ + return 0; +} diff --git a/tests/auto/blackbox/testdata/grpc/grpc.proto b/tests/auto/blackbox/testdata/grpc/grpc.proto new file mode 100644 index 000000000..631006bad --- /dev/null +++ b/tests/auto/blackbox/testdata/grpc/grpc.proto @@ -0,0 +1,15 @@ +syntax = "proto3"; + +package Qbs; + +message Request { + string name = 1; +} + +message Response { + string name = 1; +} + +service Grpc { + rpc doWork(Request) returns (Response) {} +} diff --git a/tests/auto/blackbox/testdata/grpc/grpc_cpp.qbs b/tests/auto/blackbox/testdata/grpc/grpc_cpp.qbs new file mode 100644 index 000000000..8ee3dd9c9 --- /dev/null +++ b/tests/auto/blackbox/testdata/grpc/grpc_cpp.qbs @@ -0,0 +1,26 @@ +import qbs + +CppApplication { + name: "grpc_cpp" + consoleApplication: true + condition: hasDependencies + + Depends { name: "cpp" } + cpp.cxxLanguageVersion: "c++11" + cpp.warningLevel: "none" + + Depends { name: "protobuf.cpp"; required: false } + protobuf.cpp.useGrpc: true + + property bool hasDependencies: { + console.info("has grpc: " + protobuf.cpp.present); + return protobuf.cpp.present; + } + + files: "grpc.cpp" + + Group { + files: "grpc.proto" + fileTags: "protobuf.grpc" + } +} diff --git a/tests/auto/blackbox/testdata/path-probe/BaseApp.qbs b/tests/auto/blackbox/testdata/path-probe/BaseApp.qbs new file mode 100644 index 000000000..84c00c240 --- /dev/null +++ b/tests/auto/blackbox/testdata/path-probe/BaseApp.qbs @@ -0,0 +1,76 @@ +/**************************************************************************** +** +** Copyright (C) 2019 Ivan Komissarov (abbapoh@gmail.com). +** 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. +** +****************************************************************************/ + +import qbs.Probes + +CppApplication { + + property varList inputSelectors + property varList inputNames + property varList inputNameSuffixes + property pathList inputSearchPaths + property var inputNameFilter + property var inputCandidateFilter + + property stringList outputFilePaths + + Probes.PathProbe { + id: probe + selectors: inputSelectors + names: inputNames + nameSuffixes: inputNameSuffixes + nameFilter: inputNameFilter + candidateFilter: inputCandidateFilter + searchPaths: inputSearchPaths + } + + property bool validate: { + var compareArrays = function(lhs, rhs) { + if (lhs.length !== rhs.length) + return false; + for (var i = 0; i < lhs.length; ++i) { + if (lhs[i] !== rhs[i]) + return false; + } + return true; + }; + + if (!probe.found) + throw "Probe failed to find files"; + + if (outputFilePaths) { + var actual = probe.allResults.map(function(file) { return file.filePath; }); + if (!compareArrays(actual, outputFilePaths)) + throw "Invalid filePaths: actual = " + actual + ", expected = " + outputFilePaths; + } + } + + files: ["main.cpp"] +} diff --git a/tests/auto/blackbox/testdata/path-probe/bin/super-tool.1 b/tests/auto/blackbox/testdata/path-probe/bin/super-tool.1 new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/tests/auto/blackbox/testdata/path-probe/bin/super-tool.1 diff --git a/tests/auto/blackbox/testdata/path-probe/bin/tool b/tests/auto/blackbox/testdata/path-probe/bin/tool new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/tests/auto/blackbox/testdata/path-probe/bin/tool diff --git a/tests/auto/blackbox/testdata/path-probe/bin/tool.1 b/tests/auto/blackbox/testdata/path-probe/bin/tool.1 new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/tests/auto/blackbox/testdata/path-probe/bin/tool.1 diff --git a/tests/auto/blackbox/testdata/path-probe/bin/tool.2 b/tests/auto/blackbox/testdata/path-probe/bin/tool.2 new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/tests/auto/blackbox/testdata/path-probe/bin/tool.2 diff --git a/tests/auto/blackbox/testdata/path-probe/bin/tool.3 b/tests/auto/blackbox/testdata/path-probe/bin/tool.3 new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/tests/auto/blackbox/testdata/path-probe/bin/tool.3 diff --git a/tests/auto/blackbox/testdata/path-probe/bin/tool.4 b/tests/auto/blackbox/testdata/path-probe/bin/tool.4 new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/tests/auto/blackbox/testdata/path-probe/bin/tool.4 diff --git a/tests/auto/blackbox/testdata/path-probe/candidate-filter.qbs b/tests/auto/blackbox/testdata/path-probe/candidate-filter.qbs new file mode 100644 index 000000000..a65256a68 --- /dev/null +++ b/tests/auto/blackbox/testdata/path-probe/candidate-filter.qbs @@ -0,0 +1,12 @@ +import qbs.FileInfo + +BaseApp { + inputNames: ["tool.1", "tool.2"] + inputSearchPaths: "bin" + outputFilePaths: ["bin/tool.2"] + inputCandidateFilter: { + return function(f) { + return FileInfo.fileName(f) == "tool.2"; + } + } +} diff --git a/tests/auto/blackbox/testdata/path-probe/main.cpp b/tests/auto/blackbox/testdata/path-probe/main.cpp new file mode 100644 index 000000000..76e819701 --- /dev/null +++ b/tests/auto/blackbox/testdata/path-probe/main.cpp @@ -0,0 +1 @@ +int main() { return 0; } diff --git a/tests/auto/blackbox/testdata/path-probe/mult-files-mult-suffixes.qbs b/tests/auto/blackbox/testdata/path-probe/mult-files-mult-suffixes.qbs new file mode 100644 index 000000000..b112db44d --- /dev/null +++ b/tests/auto/blackbox/testdata/path-probe/mult-files-mult-suffixes.qbs @@ -0,0 +1,8 @@ +BaseApp { + inputSelectors: [ + {names : "tool", nameSuffixes: [".1", ".2"]}, + {names : "super-tool", nameSuffixes: [".1"]}, + ] + inputSearchPaths: "bin" + outputFilePaths: ["bin/tool.1", "bin/super-tool.1"] +} diff --git a/tests/auto/blackbox/testdata/path-probe/mult-files-mult-variants.qbs b/tests/auto/blackbox/testdata/path-probe/mult-files-mult-variants.qbs new file mode 100644 index 000000000..60c56e6b4 --- /dev/null +++ b/tests/auto/blackbox/testdata/path-probe/mult-files-mult-variants.qbs @@ -0,0 +1,9 @@ +BaseApp { + inputSelectors: [ + "tool", + ["tool.1", "tool.2"], + {names : ["tool.3", "tool.4"]}, + ] + inputSearchPaths: "bin" + outputFilePaths: ["bin/tool", "bin/tool.1", "bin/tool.3"] +} diff --git a/tests/auto/blackbox/testdata/path-probe/mult-files-suffixes.qbs b/tests/auto/blackbox/testdata/path-probe/mult-files-suffixes.qbs new file mode 100644 index 000000000..5e4fc27ca --- /dev/null +++ b/tests/auto/blackbox/testdata/path-probe/mult-files-suffixes.qbs @@ -0,0 +1,8 @@ +BaseApp { + inputSelectors: [ + {names : "tool", nameSuffixes: ".2"}, + {names : "super-tool", nameSuffixes: ".1"}, + ] + inputSearchPaths: "bin" + outputFilePaths: ["bin/tool.2", "bin/super-tool.1"] +} diff --git a/tests/auto/blackbox/testdata/path-probe/mult-files.qbs b/tests/auto/blackbox/testdata/path-probe/mult-files.qbs new file mode 100644 index 000000000..08727ac01 --- /dev/null +++ b/tests/auto/blackbox/testdata/path-probe/mult-files.qbs @@ -0,0 +1,10 @@ +BaseApp { + inputSelectors: [ + "tool.1", + ["tool.2"], + {names : "tool.3"}, + {names : ["tool.4"]} + ] + inputSearchPaths: "bin" + outputFilePaths: ["bin/tool.1", "bin/tool.2", "bin/tool.3", "bin/tool.4"] +} diff --git a/tests/auto/blackbox/testdata/path-probe/name-filter.qbs b/tests/auto/blackbox/testdata/path-probe/name-filter.qbs new file mode 100644 index 000000000..406988fed --- /dev/null +++ b/tests/auto/blackbox/testdata/path-probe/name-filter.qbs @@ -0,0 +1,10 @@ +BaseApp { + inputNames: "tool" + inputSearchPaths: "bin" + inputNameFilter: { + return function(n) { + return n + ".2" + }; + } + outputFilePaths: ["bin/tool.2"] +} diff --git a/tests/auto/blackbox/testdata/path-probe/non-existent-selector.qbs b/tests/auto/blackbox/testdata/path-probe/non-existent-selector.qbs new file mode 100644 index 000000000..aaa27042c --- /dev/null +++ b/tests/auto/blackbox/testdata/path-probe/non-existent-selector.qbs @@ -0,0 +1,8 @@ +BaseApp { + inputSelectors: [ + "tool.1", + "nonexistent", + "tool.2", + ] + inputSearchPaths: "bin" +} diff --git a/tests/auto/blackbox/testdata/path-probe/non-existent.qbs b/tests/auto/blackbox/testdata/path-probe/non-existent.qbs new file mode 100644 index 000000000..f0c58fa6c --- /dev/null +++ b/tests/auto/blackbox/testdata/path-probe/non-existent.qbs @@ -0,0 +1,4 @@ +BaseApp { + inputNames: "nonexistent" + inputSearchPaths: "bin" +} diff --git a/tests/auto/blackbox/testdata/path-probe/single-file-mult-variants.qbs b/tests/auto/blackbox/testdata/path-probe/single-file-mult-variants.qbs new file mode 100644 index 000000000..992a0bea4 --- /dev/null +++ b/tests/auto/blackbox/testdata/path-probe/single-file-mult-variants.qbs @@ -0,0 +1,5 @@ +BaseApp { + inputNames: ["tool.1", "tool.2"] + inputSearchPaths: "bin" + outputFilePaths: ["bin/tool.1"] +} diff --git a/tests/auto/blackbox/testdata/path-probe/single-file-selector-array.qbs b/tests/auto/blackbox/testdata/path-probe/single-file-selector-array.qbs new file mode 100644 index 000000000..697665242 --- /dev/null +++ b/tests/auto/blackbox/testdata/path-probe/single-file-selector-array.qbs @@ -0,0 +1,5 @@ +BaseApp { + inputSelectors: ["tool"] + inputSearchPaths: "bin" + outputFilePaths: ["bin/tool"] +} diff --git a/tests/auto/blackbox/testdata/path-probe/single-file-selector.qbs b/tests/auto/blackbox/testdata/path-probe/single-file-selector.qbs new file mode 100644 index 000000000..d57700baf --- /dev/null +++ b/tests/auto/blackbox/testdata/path-probe/single-file-selector.qbs @@ -0,0 +1,5 @@ +BaseApp { + inputSelectors: "tool" + inputSearchPaths: "bin" + outputFilePaths: ["bin/tool"] +} diff --git a/tests/auto/blackbox/testdata/path-probe/single-file-suffixes.qbs b/tests/auto/blackbox/testdata/path-probe/single-file-suffixes.qbs new file mode 100644 index 000000000..4442e719a --- /dev/null +++ b/tests/auto/blackbox/testdata/path-probe/single-file-suffixes.qbs @@ -0,0 +1,6 @@ +BaseApp { + inputNames: "tool" + inputSearchPaths: "bin" + inputNameSuffixes: [".1", ".2"] + outputFilePaths: ["bin/tool.1"] +} diff --git a/tests/auto/blackbox/testdata/path-probe/single-file.qbs b/tests/auto/blackbox/testdata/path-probe/single-file.qbs new file mode 100644 index 000000000..3590e7664 --- /dev/null +++ b/tests/auto/blackbox/testdata/path-probe/single-file.qbs @@ -0,0 +1,5 @@ +BaseApp { + inputNames: "tool" + inputSearchPaths: "bin" + outputFilePaths: ["bin/tool"] +} diff --git a/tests/auto/blackbox/tst_blackbox.cpp b/tests/auto/blackbox/tst_blackbox.cpp index 84322a7c7..f87889155 100644 --- a/tests/auto/blackbox/tst_blackbox.cpp +++ b/tests/auto/blackbox/tst_blackbox.cpp @@ -2719,6 +2719,37 @@ void TestBlackbox::overrideProjectProperties() QCOMPARE(runQbs(params), 0); } +void TestBlackbox::pathProbe_data() +{ + QTest::addColumn<QString>("projectFile"); + QTest::addColumn<bool>("successExpected"); + QTest::newRow("non-existent") << QString("non-existent.qbs") << false; + QTest::newRow("non-existent-selector.qbs") << QString("non-existent-selector.qbs") << false; + QTest::newRow("single-file") << QString("single-file.qbs") << true; + QTest::newRow("single-file-selector") << QString("single-file-selector.qbs") << true; + QTest::newRow("single-file-selector-array") << QString("single-file-selector-array.qbs") << true; + QTest::newRow("single-file-mult-variants") << QString("single-file-mult-variants.qbs") << true; + QTest::newRow("mult-files") << QString("mult-files.qbs") << true; + QTest::newRow("mult-files-mult-variants") << QString("mult-files-mult-variants.qbs") << true; + QTest::newRow("single-file-suffixes") << QString("single-file-suffixes.qbs") << true; + QTest::newRow("mult-files-suffixes") << QString("mult-files-suffixes.qbs") << true; + QTest::newRow("mult-files-mult-suffixes") << QString("mult-files-mult-suffixes.qbs") << true; + QTest::newRow("name-filter") << QString("name-filter.qbs") << true; + QTest::newRow("candidate-filter") << QString("candidate-filter.qbs") << true; +} + +void TestBlackbox::pathProbe() +{ + QDir::setCurrent(testDataDir + "/path-probe"); + QFETCH(QString, projectFile); + QFETCH(bool, successExpected); + rmDirR(relativeBuildDir()); + + QbsRunParameters buildParams("build", QStringList{"-f", projectFile}); + buildParams.expectFailure = !successExpected; + QCOMPARE(runQbs(buildParams) == 0, successExpected); +} + void TestBlackbox::pchChangeTracking() { QDir::setCurrent(testDataDir + "/pch-change-tracking"); @@ -6792,6 +6823,29 @@ void TestBlackbox::groupsInModules() QCOMPARE(output.readAll().trimmed(), QByteArray("diamond")); } +void TestBlackbox::grpc_data() +{ + QTest::addColumn<QString>("projectFile"); + QTest::newRow("cpp") << QString("grpc_cpp.qbs"); +} + +void TestBlackbox::grpc() +{ + QDir::setCurrent(testDataDir + "/grpc"); + QFETCH(QString, projectFile); + rmDirR(relativeBuildDir()); + QbsRunParameters resolveParams("resolve", QStringList{"-f", projectFile}); + QCOMPARE(runQbs(resolveParams), 0); + const bool withGrpc = m_qbsStdout.contains("has grpc: true"); + const bool withoutGrpc = m_qbsStdout.contains("has grpc: false"); + QVERIFY2(withGrpc || withoutGrpc, m_qbsStdout.constData()); + if (withoutGrpc) + QSKIP("grpc module not present"); + + QbsRunParameters runParams; + QCOMPARE(runQbs(runParams), 0); +} + void TestBlackbox::ico() { QDir::setCurrent(testDataDir + "/ico"); diff --git a/tests/auto/blackbox/tst_blackbox.h b/tests/auto/blackbox/tst_blackbox.h index 5b3adcbe2..0a14c418c 100644 --- a/tests/auto/blackbox/tst_blackbox.h +++ b/tests/auto/blackbox/tst_blackbox.h @@ -119,6 +119,8 @@ private slots: void generator(); void generator_data(); void groupsInModules(); + void grpc_data(); + void grpc(); void ico(); void importAssignment(); void importChangeTracking(); @@ -205,6 +207,8 @@ private slots: void outOfDateMarking(); void outputArtifactAutoTagging(); void overrideProjectProperties(); + void pathProbe_data(); + void pathProbe(); void pchChangeTracking(); void perGroupDefineInExportItem(); void pkgConfigProbe(); diff --git a/tests/auto/blackbox/tst_blackboxapple.cpp b/tests/auto/blackbox/tst_blackboxapple.cpp index d9cabe270..96cd70b58 100644 --- a/tests/auto/blackbox/tst_blackboxapple.cpp +++ b/tests/auto/blackbox/tst_blackboxapple.cpp @@ -33,6 +33,7 @@ #include <tools/profile.h> #include <QtCore/qjsondocument.h> +#include <QtCore/qjsonobject.h> #include <QtXml/qdom.h> #include <regex> @@ -718,12 +719,38 @@ void TestBlackboxApple::infoPlist() params.arguments = QStringList() << "-f" << "infoplist.qbs"; QCOMPARE(runQbs(params), 0); - QFile infoplist(relativeProductBuildDir("infoplist") + "/infoplist.app/Contents/Info.plist"); + auto infoplistPath = relativeProductBuildDir("infoplist") + + "/infoplist.app/Contents/Info.plist"; + if (!QFile::exists(infoplistPath)) + infoplistPath = relativeProductBuildDir("infoplist") + "/infoplist.app/Info.plist"; + QVERIFY(QFile::exists(infoplistPath)); + QProcess plutil; + plutil.start("plutil", { + QStringLiteral("-convert"), + QStringLiteral("json"), + infoplistPath + }); + QVERIFY2(plutil.waitForStarted(), qPrintable(plutil.errorString())); + QVERIFY2(plutil.waitForFinished(), qPrintable(plutil.errorString())); + QVERIFY2(plutil.exitCode() == 0, qPrintable(plutil.readAllStandardError().constData())); + + QFile infoplist(infoplistPath); QVERIFY(infoplist.open(QIODevice::ReadOnly)); - const QByteArray fileContents = infoplist.readAll(); - QVERIFY2(fileContents.contains("<key>LSMinimumSystemVersion</key>"), fileContents.constData()); - QVERIFY2(fileContents.contains("<string>10.7</string>"), fileContents.constData()); - QVERIFY2(fileContents.contains("<key>NSPrincipalClass</key>"), fileContents.constData()); + QJsonParseError error; + const auto json = QJsonDocument::fromJson(infoplist.readAll(), &error); + QCOMPARE(error.error, QJsonParseError::NoError); + QVERIFY(json.isObject()); + // common values + QCOMPARE(json.object().value(QStringLiteral("CFBundleIdentifier")), + QStringLiteral("org.example.infoplist")); + QCOMPARE(json.object().value(QStringLiteral("CFBundleName")), QStringLiteral("infoplist")); + QCOMPARE(json.object().value(QStringLiteral("CFBundleExecutable")), + QStringLiteral("infoplist")); + + if (!json.object().contains(QStringLiteral("SDKROOT"))) { // macOS-specific values + QCOMPARE(json.object().value("LSMinimumSystemVersion"), QStringLiteral("10.7")); + QVERIFY(json.object().contains("NSPrincipalClass")); + } } void TestBlackboxApple::objcArc() @@ -758,16 +785,28 @@ void TestBlackboxApple::xcode() sdks.insert({ match[1], match[2] }); } - auto range = sdks.equal_range("macosx"); - QStringList sdkValues; - for (auto i = range.first; i != range.second; ++i) - sdkValues.push_back(QString::fromStdString(i->second)); + const auto getSdksByType = [&sdks]() + { + QStringList result; + std::string sdkType; + QStringList sdkValues; + for (const auto &sdk: sdks) { + if (!sdkType.empty() && sdkType != sdk.first) { + result.append(QStringLiteral("%1:['%2']") + .arg(QString::fromStdString(sdkType), sdkValues.join("','"))); + sdkValues.clear(); + } + sdkType = sdk.first; + sdkValues.append(QString::fromStdString(sdk.second)); + } + return result; + }; QDir::setCurrent(testDataDir + "/xcode"); QbsRunParameters params; params.arguments = (QStringList() << (QStringLiteral("modules.xcode.developerPath:") + developerPath) - << (QStringLiteral("project.sdks:['") + sdkValues.join("','") + "']")); + << (QStringLiteral("project.sdks:{") + getSdksByType().join(",") + "}")); QCOMPARE(runQbs(params), 0); } |