diff options
author | Christian Kandeler <christian.kandeler@qt.io> | 2024-03-01 12:18:19 +0100 |
---|---|---|
committer | Christian Kandeler <christian.kandeler@qt.io> | 2024-03-01 12:18:19 +0100 |
commit | ff13b8d47d36b87c2a0f305f4a0ab452b7889f6d (patch) | |
tree | 3ded6c202c7a413fbd511514920e48668a4099c5 | |
parent | f5a29eee3465e935523bef41bfc014054ce663c7 (diff) | |
parent | 75aca0dca12c6c94109e65ee035b6b533b33a3c5 (diff) |
Merge 2.3 into master
Change-Id: Ica03e06f5d3058dd78e80278764e8cb84723ff24
45 files changed, 800 insertions, 328 deletions
diff --git a/.clang-tidy b/.clang-tidy index e737372e9..92e6eeefe 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -2,6 +2,7 @@ Checks: > -*, bugprone-*, + -bugprone-easily-swappable-parameters, -bugprone-narrowing-conversions, -bugprone-throw-keyword-missing, cppcoreguidelines-interfaces-global-init, @@ -12,7 +13,7 @@ Checks: > google-build-namespaces, google-global-names-in-headers, google-objc-*, - google-readability-casting, + -google-readability-casting, google-readability-namespace-comments, google-runtime-operator, misc-definitions-in-headers, @@ -70,6 +71,3 @@ WarningsAsErrors: > HeaderFilterRegex: '' AnalyzeTemporaryDtors: false -CheckOptions: -... - diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 6ccd36047..20e936d8d 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -16,18 +16,6 @@ jobs: matrix: config: - { - name: 'Build on Linux (gcc)', - image: 'focal-qt6', - options: 'modules.cpp.compilerWrapper:ccache - modules.qbs.debugInformation:true - modules.qbsbuildconfig.enableBundledQt:true - products.qbs_archive.targetName:qbs-linux-${{ github.run_id }} - products.qbs_archive.includeTests:true', - script: './scripts/build-qbs-with-qbs.sh', - cacheid: 'gcc-qt6', - suffix: 'linux', - } - - { name: 'Build on Linux (Jammy, gcc)', image: 'jammy-qt6', options: 'modules.cpp.compilerWrapper:ccache @@ -80,19 +68,19 @@ jobs: - { name: 'Build on Linux (clang_tidy)', script: './scripts/run-analyzer.sh', - image: 'focal-qt6', + image: 'jammy-qt6', options: 'profile:qt-clang_64 modules.cpp.compilerWrapper:ccache', cacheid: 'clang', } - { name: 'Build on Linux (CMake)', script: './scripts/build-qbs-with-cmake.sh', - image: 'focal-qt6', + image: 'jammy-qt6', cacheid: 'cmake', } - { name: 'Build on Linux (gcc, ASAN)', - image: 'focal-qt6', + image: 'jammy-qt6', script: './scripts/build-qbs-with-qbs.sh', options: 'modules.cpp.compilerWrapper:ccache modules.qbsbuildconfig.enableAddressSanitizer:true @@ -102,7 +90,7 @@ jobs: } - { name: 'Build on Linux (gcc, UBSAN)', - image: 'focal-qt6', + image: 'jammy-qt6', script: './scripts/build-qbs-with-qbs.sh', options: 'modules.cpp.compilerWrapper:ccache modules.qbsbuildconfig.enableUbSanitizer:true @@ -367,7 +355,7 @@ jobs: - { name: 'Run Linux tests (gcc, Qt 5)', image: 'jammy-qt5', - suffix: 'linux', + suffix: 'linux-jammy', profile: 'qt-gcc_64', script: './scripts/test-qt.sh', } @@ -381,49 +369,49 @@ jobs: - { name: 'Run Android tests (Qt 5.15)', image: 'jammy-android-515', - suffix: 'linux', + suffix: 'linux-jammy', profile: '', script: './scripts/test-qt-for-android.sh', } - { name: 'Run Android tests (Qt 6.2)', image: 'jammy-android-62', - suffix: 'linux', + suffix: 'linux-jammy', profile: '', script: './scripts/test-qt-for-android.sh', } - { name: 'Run Android tests (Qt 6.5)', image: 'jammy-android-65', - suffix: 'linux', + suffix: 'linux-jammy', profile: '', script: './scripts/test-qt-for-android.sh', } - { name: 'Run Android tests (ndk r19c)', image: 'jammy-android-ndk-r19c', - suffix: 'linux', + suffix: 'linux-jammy', profile: '', script: './scripts/test-for-android.sh', } - { name: 'Run Android tests (ndk r21e)', image: 'jammy-android-ndk-r21e', - suffix: 'linux', + suffix: 'linux-jammy', profile: '', script: './scripts/test-for-android.sh', } - { name: 'Run Android tests (ndk r23)', image: 'jammy-android-ndk-r23', - suffix: 'linux', + suffix: 'linux-jammy', profile: '', script: './scripts/test-for-android.sh', } - { name: 'Run Linux tests (Qt 4.8.7)', image: 'jammy-qt4', - suffix: 'linux', + suffix: 'linux-jammy', profile: '', script: './scripts/test-qt4.sh', } @@ -458,10 +446,10 @@ jobs: - name: Download artifact uses: actions/download-artifact@v1 with: - name: qbs-linux-${{ github.run_id }}.tar.gz + name: qbs-linux-jammy-${{ github.run_id }}.tar.gz path: ./ - name: Unpack artifact - run: mkdir -p release/install-root/ && tar xzf qbs-linux-${{ github.run_id }}.tar.gz -C release/install-root/ + run: mkdir -p release/install-root/ && tar xzf qbs-linux-jammy-${{ github.run_id }}.tar.gz -C release/install-root/ - name: Pull the Docker Image run: docker-compose pull jammy-baremetal - name: arm-none-eabi-gcc-10_3 diff --git a/doc/config/macros.qdocconf b/doc/config/macros.qdocconf index 6811215c0..31b9b9a8d 100644 --- a/doc/config/macros.qdocconf +++ b/doc/config/macros.qdocconf @@ -51,6 +51,8 @@ macro.endfloat.HTML = "</div>" macro.clearfloat.HTML = "<br style=\"clear: both\" />" macro.emptyspan.HTML = "<span></span>" +macro.CMAKE = "CMake" + # Embed YouTube content by video ID - Example: \youtube dQw4w9WgXcQ # Also requires a <ID>.jpg thumbnail for offline docs. In .qdocconf, add: # diff --git a/doc/reference/modules/exporter-cmake.qdoc b/doc/reference/modules/exporter-cmake.qdoc new file mode 100644 index 000000000..2f4191a76 --- /dev/null +++ b/doc/reference/modules/exporter-cmake.qdoc @@ -0,0 +1,135 @@ +/**************************************************************************** +** +** Copyright (C) 2024 Raphael Cotty (raphael.cotty@gmail.com) +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! + \qmltype Exporter.cmake + \inqmlmodule QbsModules + \since Qbs 2.3 + + \brief Provides support for generating \CMAKE packages from dynamic, static and header library + products. + + The Exporter.cmake module contains the properties and rules to create a \CMAKE config + \l{https://cmake.org/cmake/help/v3.29/manual/cmake-packages.7.html#config-file-packages}{files} + from the \l Export item of a \l Product. + + For instance, suppose you are creating a library. To allow exporting to \CMAKE, you would write + something like the following: + \code + DynamicLibrary { + name: "mylibrary" + qbs.installPrefix: "/opt/mylibrary" + Depends { name: "Exporter.cmake" } + Exporter.cmake.packageName: "MyLibrary" + property string headersInstallDir: "include" + // ... + Group { + name: "API headers" + files: ["mylib.h"] + qbs.install: true + qbs.installDir: headersInstallDir + } + Group { + fileTagsFilter: ["Exporter.cmake.package"] + qbs.installDir: "lib/cmake/MyLibrary" + } + Export { + Depends { name: "cpp" } + cpp.includePaths: FileInfo.joinPaths( + exportingProduct.qbs.installRoot, + exportingProduct.qbs.installPrefix, + exportingProduct.headersInstallDir) + } + } + \endcode + To build against this library, from within your \CMAKE project, you simply + use \l{https://cmake.org/cmake/help/v3.29/command/find_package.html}{find_package}: + \code + find_package(MyLibrary PATHS REQUIRED) + add_executable(Consumer main.cpp) + target_link_libraries(Consumer MyLibrary) + \endcode + + \section2 Relevant File Tags + \target filetags-exporter-qbs + + \table + \header + \li Tag + \li Since + \li Description + \row + \li \c{"Exporter.cmake.package"} + \li 2.3.0 + \li This tag is attached to all generated module files. + \row + \li \c{"Exporter.cmake.configFile"} + \li 2.3.0 + \li This tag is attached to the generated config file. + \row + \li \c{"Exporter.cmake.versionFile"} + \li 2.3.0 + \li This tag is attached to the generated version file. + \endtable +*/ + +/*! + \qmlproperty string Exporter.cmake::configFileName + + The name of the generated config file. + + \defaultvalue \c{packageName + "Config.cmake"} +*/ + +/*! + \qmlproperty string Exporter.cmake::versionFileName + + The name of the generated version file. + + \defaultvalue \c{packageName + "ConfigVersion.cmake"} +*/ + +/*! + \qmlproperty string Exporter.cmake::packageName + + The name of the \CMAKE package. + + \defaultvalue \l{Product::targetName}{Product.targetName} +*/ + diff --git a/docker-compose.yml b/docker-compose.yml index 27d334647..d852cc5e6 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -54,17 +54,6 @@ services: QT_VERSION: 6.5.0 QTCREATOR_VERSION: 11.0.3 - focal-qt6: - << : *linux - hostname: focal-qt6 - image: ${DOCKER_USER:-qbsbuild}/qbsdev:focal-qt6-6.5.0_1.24.1-0 - build: - dockerfile: docker/focal/Dockerfile - context: . - args: - QT_VERSION: 6.5.0 - QTCREATOR_VERSION: 9.0.2 - jammy-android-515: << : *linux hostname: jammy-android diff --git a/docker/focal/Dockerfile b/docker/focal/Dockerfile deleted file mode 100644 index 917d4d0a7..000000000 --- a/docker/focal/Dockerfile +++ /dev/null @@ -1,117 +0,0 @@ -# -# Install Qt and Qbs for Linux -# -FROM ubuntu:focal -LABEL Description="Ubuntu development environment for Qbs with Qt and various dependencies for testing Qbs modules and functionality" -ARG QT_VERSION -ARG QTCREATOR_VERSION - -# Allow colored output on command line. -ENV TERM=xterm-color - -# -# Make it possible to change UID/GID in the entrypoint script. The docker -# container usually runs as root user on Linux hosts. When the Docker container -# mounts a folder on the host and creates files there, those files would be -# owned by root instead of the current user. Thus we create a user here who's -# UID will be changed in the entrypoint script to match the UID of the current -# host user. -# -ARG USER_UID=1000 -ARG USER_NAME=devel -RUN apt-get update -qq && \ - apt-get install -qq -y \ - ca-certificates \ - gosu \ - sudo && \ - groupadd -g ${USER_UID} ${USER_NAME} && \ - useradd -s /bin/bash -u ${USER_UID} -g ${USER_NAME} -o -c "" -m ${USER_NAME} && \ - usermod -a -G sudo ${USER_NAME} && \ - echo "%devel ALL = (ALL) NOPASSWD: ALL" >> /etc/sudoers - -COPY docker/focal/entrypoint.sh /sbin/entrypoint.sh -ENTRYPOINT ["/sbin/entrypoint.sh"] - -# Qbs build dependencies -RUN apt-get update -qq && \ - DEBIAN_FRONTEND="noninteractive" apt-get install -qq -y --no-install-recommends \ - bison \ - build-essential \ - ca-certificates \ - capnproto \ - ccache \ - clang-12 \ - clang-tidy-12 \ - cmake \ - curl \ - flex \ - git \ - help2man \ - icoutils \ - libcapnp-dev \ - libdbus-1-3 \ - libfreetype6 \ - libfontconfig1 \ - libgl1-mesa-dev \ - libgl1-mesa-glx \ - libnanopb-dev \ - libprotobuf-dev \ - libgrpc++-dev \ - libxkbcommon-x11-0 \ - locales \ - nanopb \ - ninja-build \ - nsis \ - pkg-config \ - protobuf-compiler \ - protobuf-compiler-grpc \ - psmisc \ - python3-pip \ - python3-setuptools \ - p7zip-full \ - subversion \ - unzip \ - zip && \ - update-alternatives --install /usr/bin/clang clang /usr/bin/clang-12 100 && \ - update-alternatives --install /usr/bin/clang++ clang++ /usr/bin/clang++-12 100 && \ - update-alternatives --install /usr/bin/clang-check clang-check /usr/bin/clang-check-12 100 && \ - update-alternatives --install /usr/bin/python python /usr/bin/python3 100 && \ - pip install beautifulsoup4 lxml protobuf==3.19.1 pyyaml - -# Set the locale -RUN sed -i '/en_US.UTF-8/s/^# //g' /etc/locale.gen && \ - locale-gen -ENV LANG en_US.UTF-8 -ENV LANGUAGE en_US:en -ENV LC_ALL en_US.UTF-8 - -ENV LLVM_INSTALL_DIR=/usr/lib/llvm-12 - - -# -# Install Qt and Qbs for Linux from qt.io -# -COPY scripts/install-qt.sh install-qt.sh - -RUN ./install-qt.sh --version ${QT_VERSION} qtbase qtdeclarative qttools qtx11extras qtscxml qt5compat icu && \ - ./install-qt.sh --version ${QTCREATOR_VERSION} qtcreator && \ - echo "export PATH=/opt/Qt/${QT_VERSION}/gcc_64/bin:/opt/Qt/Tools/QtCreator/bin:\${PATH}" > /etc/profile.d/qt.sh - -ENV PATH=/opt/Qt/${QT_VERSION}/gcc_64/bin:/opt/Qt/Tools/QtCreator/bin:${PATH} - - -# Configure Qbs -USER $USER_NAME -RUN qbs-setup-toolchains /usr/bin/g++ gcc && \ - qbs-setup-toolchains /usr/bin/clang clang && \ - qbs-setup-qt /opt/Qt/${QT_VERSION}/gcc_64/bin/qmake qt-gcc_64 && \ - qbs config profiles.qt-gcc_64.baseProfile gcc && \ - qbs-setup-qt /opt/Qt/${QT_VERSION}/gcc_64/bin/qmake qt-clang_64 && \ - qbs config profiles.qt-clang_64.baseProfile clang && \ - qbs config defaultProfile qt-gcc_64 - -# Switch back to root user for the entrypoint script. -USER root - -# Work-around for QTBUG-79020 -RUN echo "export QT_NO_GLIB=1" >> /etc/profile.d/qt.sh diff --git a/docker/focal/entrypoint.sh b/docker/focal/entrypoint.sh deleted file mode 100755 index 40bc5acb9..000000000 --- a/docker/focal/entrypoint.sh +++ /dev/null @@ -1,96 +0,0 @@ -#!/usr/bin/env bash -set -e - -############################################################################# -## -## Copyright (C) 2019 Richard Weickelt <richard@weickelt.de> -## Contact: https://www.qt.io/licensing/ -## -## This file is part of Qbs. -## -## $QT_BEGIN_LICENSE:LGPL$ -## Commercial License Usage -## Licensees holding valid commercial Qt licenses may use this file in -## accordance with the commercial license agreement provided with the -## Software or, alternatively, in accordance with the terms contained in -## a written agreement between you and The Qt Company. For licensing terms -## and conditions see https://www.qt.io/terms-conditions. For further -## information use the contact form at https://www.qt.io/contact-us. -## -## GNU Lesser General Public License Usage -## Alternatively, this file may be used under the terms of the GNU Lesser -## General Public License version 3 as published by the Free Software -## Foundation and appearing in the file LICENSE.LGPL3 included in the -## packaging of this file. Please review the following information to -## ensure the GNU Lesser General Public License version 3 requirements -## will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -## -## GNU General Public License Usage -## Alternatively, this file may be used under the terms of the GNU -## General Public License version 2.0 or (at your option) the GNU General -## Public license version 3 or any later version approved by the KDE Free -## Qt Foundation. The licenses are as published by the Free Software -## Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -## included in the packaging of this file. Please review the following -## information to ensure the GNU General Public License requirements will -## be met: https://www.gnu.org/licenses/gpl-2.0.html and -## https://www.gnu.org/licenses/gpl-3.0.html. -## -## $QT_END_LICENSE$ -## -############################################################################# - -# -# Entrypoint script when starting the container. The script checks the current -# working directory and changes the uid/gid of developer/users to match whatever -# is found in the working directory. This is useful to match the user and group -# of mounted volumes into the container - -# -# If not root, re-run script as root to fix ids -# -if [ "$(id -u)" != "0" ]; then - exec gosu root /sbin/entrypoint.sh "$@" -fi - -# -# Try to determine the uid of the working directory and adjust the current -# user's uid/gid accordingly. -# -USER_GID=${USER_GID:-$(stat -c "%g" .)} -USER_UID=${USER_UID:-$(stat -c "%u" .)} -USER_NAME=${USER_NAME:-devel} -USER_GROUP=${USER_GROUP:-devel} -EXEC="" -export HOME=/home/${USER_NAME} - -# -# This is a problem on Linux hosts when we mount a folder from the -# user file system and write artifacts into that. Thus, we downgrade -# the current user and make sure that the uid and gid matches the one -# of the mounted project folder. -# -# This work-around is not needed on Windows hosts as Windows doesn't -# have such a concept. -# -if [ "${USER_UID}" != "0" ]; then - if [ "$(id -u ${USER_NAME})" != "${USER_UID}" ]; then - usermod -o -u ${USER_UID} ${USER_NAME} - # After changing the user's uid, all files in user's home directory - # automatically get the new uid. - fi - current_gid=$(id -g ${USER_NAME}) - if [ "$(id -g ${USER_NAME})" != "${USER_GID}" ]; then - groupmod -o -g ${USER_GID} ${USER_GROUP} - # Set the new gid on all files in the home directory that still have the - # old gid. - find /home/${USER_NAME} -gid "${current_gid}" ! -type l -exec chgrp ${USER_GID} {} \; - fi -fi -EXEC="exec gosu ${USER_NAME}:${USER_GROUP}" - -if [ -z "$1" ]; then - ${EXEC} bash -l -else - ${EXEC} bash -l -c "$*" -fi diff --git a/examples/exporters/lib_a/lib_a.qbs b/examples/exporters/lib_a/lib_a.qbs index f058318e4..fedbb7074 100644 --- a/examples/exporters/lib_a/lib_a.qbs +++ b/examples/exporters/lib_a/lib_a.qbs @@ -58,5 +58,10 @@ MyLibrary { qbs.install: true qbs.installDir: headersInstallDir } + Export { + Depends { name: "cpp" } + cpp.dynamicLibraries: "z" + cpp.libraryPaths: "/opt/local/lib" + } } diff --git a/examples/exporters/qbs/imports/MyLibrary.qbs b/examples/exporters/qbs/imports/MyLibrary.qbs index 3dcfb2796..4607776b3 100644 --- a/examples/exporters/qbs/imports/MyLibrary.qbs +++ b/examples/exporters/qbs/imports/MyLibrary.qbs @@ -83,6 +83,13 @@ StaticLibrary { qbs.installDir: FileInfo.joinPaths(installDir, "pkgconfig") } + Depends { name: "Exporter.cmake" } + Group { + fileTagsFilter: ["Exporter.cmake.package"] + qbs.install: install + qbs.installDir: FileInfo.joinPaths(installDir, "cmake", product.name) + } + Depends { name: 'bundle' } bundle.isBundle: false } diff --git a/scripts/run-analyzer.sh b/scripts/run-analyzer.sh index a410e8b4c..9e53e50dd 100755 --- a/scripts/run-analyzer.sh +++ b/scripts/run-analyzer.sh @@ -45,12 +45,12 @@ LLVM_INSTALL_DIR=${LLVM_INSTALL_DIR:-""} # update-alternatives --install /usr/bin/run-clang-tidy.py run-clang-tidy.py /usr/bin/run-clang-tidy-4.0.py 1 CLANG_TIDY=`which clang-tidy` -RUN_CLANG_TIDY=`which run-clang-tidy.py` +RUN_CLANG_TIDY=`which run-clang-tidy` if [ -z "$RUN_CLANG_TIDY" ] || [ -z "$CLANG_TIDY" ]; then if [ ! -z "$LLVM_INSTALL_DIR" ]; then CLANG_TIDY="$LLVM_INSTALL_DIR/bin/clang-tidy" - RUN_CLANG_TIDY="$LLVM_INSTALL_DIR/share/clang/run-clang-tidy.py" + RUN_CLANG_TIDY="$LLVM_INSTALL_DIR/bin/run-clang-tidy" else echo "Can't find clang-tidy and/or run-clang-tidy.py in PATH, try setting LLVM_INSTALL_DIR" exit 1 diff --git a/share/qbs/modules/Exporter/cmake/cmakeexporter.js b/share/qbs/modules/Exporter/cmake/cmakeexporter.js new file mode 100644 index 000000000..093032f2e --- /dev/null +++ b/share/qbs/modules/Exporter/cmake/cmakeexporter.js @@ -0,0 +1,239 @@ +/**************************************************************************** +** +** Copyright (C) 2024 Raphaël Cotty <raphael.cotty@gmail.com> +** Copyright (C) 2024 Ivan Komissarov (abbapoh@gmail.com) +** Contact: http://www.qt.io/licensing +** +** This file is part of the Qbs. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms and +** conditions see http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +var FileInfo = require("qbs.FileInfo"); +var ModUtils = require("qbs.ModUtils"); +var ExporterHelpers = require("../exporter.js"); + +function tagListToString(tagList) +{ + return JSON.stringify(tagList); +} + +function collectAutodetectedData(project, topLevelProduct, outputs) +{ + var packageName = topLevelProduct.Exporter.cmake.packageName; + + var data = {}; + data.packageName = packageName; + data.installPrefixDir = "_" + packageName.toUpperCase() + "_INSTALL_PREFIX"; + data.packages = []; + + function quote(value) + { + return "\"" + value + "\""; + } + + function quoteAndPrefixify(propName) + { + function quoteAndPrefixifyHelper(value) { + var prefixToStrip = + ExporterHelpers.getPrefixToStrip(project, topLevelProduct, propName, value); + if (typeof value !== "string" + || !prefixToStrip + || (value.length > prefixToStrip.length + && value[prefixToStrip.length] !== '/')) { + return quote(value); + } + return quote("${" + data.installPrefixDir + "}" + value.slice(prefixToStrip.length)); + } + return quoteAndPrefixifyHelper; + } + + var installedOutputFilePath = ModUtils.artifactInstalledFilePath( + outputs["Exporter.cmake.configFile"][0]); + var installedOutputPathName = FileInfo.path(installedOutputFilePath); + + var installRootPath = FileInfo.joinPaths(topLevelProduct.qbs.installRoot, topLevelProduct.qbs.installPrefix); + data.installPrefix = FileInfo.relativePath(installedOutputPathName, installRootPath); + + var libArtifacts; + var libImportArtifacts; + var isProduct = !topLevelProduct.present; + var considerFramework = !isProduct || (topLevelProduct.type + && topLevelProduct.type.includes("bundle.content")) + && topLevelProduct.bundle + && topLevelProduct.bundle.isBundle + && topLevelProduct.qbs.targetOS.includes("darwin"); + var considerDynamicLibs = !isProduct || (topLevelProduct.type + && topLevelProduct.type.includes("dynamiclibrary")); + var considerStaticLibs = !isProduct || (topLevelProduct.type + && topLevelProduct.type.includes("staticlibrary")); + if (considerFramework) { + libArtifacts = topLevelProduct.artifacts["bundle.symlink.executable"]; + if (considerDynamicLibs) + data.type = "SHARED"; + else if (considerStaticLibs) + data.type = "STATIC"; + else + data.type = "INTERFACE"; + } else if (considerDynamicLibs) { + libArtifacts = topLevelProduct.artifacts.dynamiclibrary; + libImportArtifacts = topLevelProduct.artifacts.dynamiclibrary_import; + data.type = "SHARED"; + } else if (considerStaticLibs) { + libArtifacts = topLevelProduct.artifacts.staticlibrary; + data.type = "STATIC"; + } else { + data.type = "INTERFACE"; + } + + for (var i = 0; i < (libArtifacts || []).length; ++i) { + var libArtifact = libArtifacts[i]; + var libImportArtifact = (libImportArtifacts || [])[i]; + if (libArtifact.qbs.install) { + var installPath = ModUtils.artifactInstalledFilePath(libArtifact); + data.importedLocation = quoteAndPrefixify("installRoot")(installPath); + data.soName = topLevelProduct.targetName; + if (libImportArtifact && libImportArtifact.qbs.install) { + installPath = ModUtils.artifactInstalledFilePath(libImportArtifact); + data.importedImplibLocation = quoteAndPrefixify("installRoot")(installPath); + } + break; + } + } + var cpp = topLevelProduct.exports.cpp; + if (cpp) { + data.libraryPaths = (cpp.libraryPaths || []).map(quoteAndPrefixify("cpp.libraryPaths")); + + data.linkLibraries = []; + data.linkLibraries = data.linkLibraries.concat(cpp.dynamicLibraries || []); + data.linkLibraries = data.linkLibraries.concat(cpp.staticLibraries || []); + data.linkLibraries = data.linkLibraries.map(quoteAndPrefixify("cpp.dynamicLibraries")); + + data.linkOptions = []; + data.linkOptions = data.linkOptions.concat(cpp.driverLinkerFlags || []); + if ((cpp.linkerFlags || []).length > 0) { + data.linkOptions = + data.linkOptions.concat("LINKER:" + (cpp.linkerFlags || []).join(",")); + } + data.linkOptions = data.linkOptions.map(quote); + + data.includeDirectories = + (cpp.includePaths || []).map(quoteAndPrefixify("cpp.includePaths")); + data.compileDefinitions = (cpp.defines || []).map(quote); + + data.compileOptions = []; + data.compileOptions = data.compileOptions.concat(cpp.commonCompilerFlags || []); + data.compileOptions = data.compileOptions.concat(cpp.driverFlags || []); + data.compileOptions = data.compileOptions.concat(cpp.cxxFlags || []); + data.compileOptions = data.compileOptions.concat(cpp.cFlags || []); + data.compileOptions = data.compileOptions.map(quote); + } + + function gatherDeps(dep) { + if (dep.name === "Exporter.cmake") + return; + var depHasExporter = dep.Exporter && dep.Exporter.cmake; + if (!depHasExporter) + return; + data.packages.push(dep.Exporter.cmake.packageName); + } + + var exportedDeps = topLevelProduct.exports ? topLevelProduct.exports.dependencies : []; + exportedDeps.forEach(gatherDeps); + + return data; +} + +function writeConfigFile(project, product, outputs) +{ + var autoDetectedData = collectAutodetectedData(project, product, outputs); + var packageName = autoDetectedData.packageName; + + function writeCommand(command, lines) + { + if ((lines || []).length === 0) + return; + cmakeConfigFile.writeLine(command + "(" + packageName + " INTERFACE"); + for (i = 0; i < lines.length; i++) { + cmakeConfigFile.writeLine(" " + lines[i]); + } + cmakeConfigFile.writeLine(")"); + } + + var cmakeConfigFile = new TextFile(outputs["Exporter.cmake.configFile"][0].filePath, + TextFile.WriteOnly); + cmakeConfigFile.writeLine("# Generated by Qbs"); + + cmakeConfigFile.writeLine("cmake_minimum_required(VERSION 3.5)"); + + cmakeConfigFile.writeLine("if(TARGET " + packageName + ")"); + cmakeConfigFile.writeLine(" return()"); + cmakeConfigFile.writeLine("endif()"); + + cmakeConfigFile.writeLine("set(" + autoDetectedData.installPrefixDir + + " \"${CMAKE_CURRENT_LIST_DIR}/" + + autoDetectedData.installPrefix + "\")"); + + autoDetectedData.packages.forEach(function(packageName) { + cmakeConfigFile.writeLine("find_package(" + packageName + " REQUIRED SILENT)"); + }); + cmakeConfigFile.writeLine( + "add_library(" + packageName + " " + autoDetectedData.type + " IMPORTED)"); + var configuration = (product.qbs.buildVariant) ? + product.qbs.buildVariant.toUpperCase() : "NONE"; + cmakeConfigFile.writeLine("set_property(TARGET " + packageName + + " APPEND PROPERTY IMPORTED_CONFIGURATIONS " + + configuration + ")"); + + cmakeConfigFile.writeLine("set_target_properties(" + packageName + " PROPERTIES"); + cmakeConfigFile.writeLine(" IMPORTED_LINK_INTERFACE_LANGUAGES_" + configuration + + " CXX"); + if (autoDetectedData.type !== "INTERFACE") { + cmakeConfigFile.writeLine(" IMPORTED_LOCATION_" + configuration + " " + + autoDetectedData.importedLocation); + } + if (autoDetectedData.importedImplibLocation) { + cmakeConfigFile.writeLine(" IMPORTED_IMPLIB_" + configuration + " " + + autoDetectedData.importedImplibLocation); + } + cmakeConfigFile.writeLine(")"); + + writeCommand("target_link_directories", autoDetectedData.libraryPaths); + writeCommand("target_link_libraries", + autoDetectedData.linkLibraries.concat(autoDetectedData.packages)); + writeCommand("target_link_options", autoDetectedData.linkOptions); + writeCommand("target_include_directories", autoDetectedData.includeDirectories); + writeCommand("target_compile_definitions", autoDetectedData.compileDefinitions); + writeCommand("target_compile_options", autoDetectedData.compileOptions); + + cmakeConfigFile.close(); +} + +function writeVersionFile(product, outputs) +{ + var cmakeVersionFile = new TextFile( + outputs["Exporter.cmake.versionFile"][0].filePath, TextFile.WriteOnly); + cmakeVersionFile.writeLine("# Generated by Qbs"); + cmakeVersionFile.writeLine("set(PACKAGE_VERSION \"" + product.version + "\")"); + cmakeVersionFile.close(); +} diff --git a/share/qbs/modules/Exporter/cmake/cmakeexporter.qbs b/share/qbs/modules/Exporter/cmake/cmakeexporter.qbs new file mode 100644 index 000000000..a578e938b --- /dev/null +++ b/share/qbs/modules/Exporter/cmake/cmakeexporter.qbs @@ -0,0 +1,84 @@ +/**************************************************************************** +** +** Copyright (C) 2024 Raphaël Cotty <raphael.cotty@gmail.com> +** Copyright (C) 2024 Ivan Komissarov (abbapoh@gmail.com) +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import qbs.File +import qbs.FileInfo +import qbs.ModUtils +import qbs.TextFile + +import "cmakeexporter.js" as HelperFunctions + +Module { + property string configFileName: packageName + "Config.cmake" + property string versionFileName: packageName + "ConfigVersion.cmake" + property string packageName: product.targetName + + additionalProductTypes: ["Exporter.cmake.package"] + + Rule { + multiplex: true + requiresInputs: false + + auxiliaryInputs: { + if (product.type.includes("staticlibrary")) + return ["staticlibrary"]; + if (product.type.includes("dynamiclibrary")) + return ["dynamiclibrary"]; + } + + Artifact { + filePath: product.Exporter.cmake.configFileName + fileTags: ["Exporter.cmake.package", "Exporter.cmake.configFile"] + } + Artifact { + filePath: product.Exporter.cmake.versionFileName + fileTags: ["Exporter.cmake.package", "Exporter.cmake.versionFile"] + } + prepare: { + var cmd = new JavaScriptCommand(); + cmd.description = "generate cmake package files"; + cmd.sourceCode = function() { + HelperFunctions.writeConfigFile(project, product, outputs); + HelperFunctions.writeVersionFile(product, outputs); + } + return [cmd]; + } + } +} diff --git a/src/app/qbs/session.cpp b/src/app/qbs/session.cpp index ebc9015b2..2cdcf2b63 100644 --- a/src/app/qbs/session.cpp +++ b/src/app/qbs/session.cpp @@ -623,10 +623,11 @@ Session::ProductSelection Session::getProductSelection(const QJsonObject &reques { const QJsonValue productSelection = request.value(StringConstants::productsKey()); if (productSelection.isArray()) - return ProductSelection(getProductsByName(fromJson<QStringList>(productSelection))); - return ProductSelection(productSelection.toString() == QLatin1String("all") - ? Project::ProductSelectionWithNonDefault - : Project::ProductSelectionDefaultOnly); + return {getProductsByName(fromJson<QStringList>(productSelection))}; + return { + productSelection.toString() == QLatin1String("all") + ? Project::ProductSelectionWithNonDefault + : Project::ProductSelectionDefaultOnly}; } Session::FileUpdateData Session::prepareFileUpdate(const QJsonObject &request) diff --git a/src/lib/corelib/api/jobs.cpp b/src/lib/corelib/api/jobs.cpp index 7a845b0ac..10c96bfee 100644 --- a/src/lib/corelib/api/jobs.cpp +++ b/src/lib/corelib/api/jobs.cpp @@ -231,7 +231,7 @@ Project SetupProjectJob::project() const { auto const wrapper = qobject_cast<const InternalJobThreadWrapper *>(internalJob()); auto const job = qobject_cast<const InternalSetupProjectJob *>(wrapper->synchronousJob()); - return Project(job->project(), job->logger()); + return {job->project(), job->logger()}; } void SetupProjectJob::resolve(const Project &existingProject, diff --git a/src/lib/corelib/api/project.cpp b/src/lib/corelib/api/project.cpp index 0588b822c..53c711b49 100644 --- a/src/lib/corelib/api/project.cpp +++ b/src/lib/corelib/api/project.cpp @@ -797,8 +797,14 @@ RunEnvironment Project::getRunEnvironment(const ProductData &product, const QStringList &setupRunEnvConfig, Settings *settings) const { const ResolvedProductPtr resolvedProduct = d->internalProduct(product); - return RunEnvironment(resolvedProduct, d->internalProject, installOptions, environment, - setupRunEnvConfig, settings, d->logger); + return { + resolvedProduct, + d->internalProject, + installOptions, + environment, + setupRunEnvConfig, + settings, + d->logger}; } /*! diff --git a/src/lib/corelib/api/projectdata.cpp b/src/lib/corelib/api/projectdata.cpp index 34e679d6d..628fe2a5d 100644 --- a/src/lib/corelib/api/projectdata.cpp +++ b/src/lib/corelib/api/projectdata.cpp @@ -75,7 +75,7 @@ static QVariant getModuleProperty(const PropertyMap &properties, const QString & { const int lastDotIndex = fullPropertyName.lastIndexOf(QLatin1Char('.')); if (lastDotIndex == -1) - return QVariant(); + return {}; return properties.getModuleProperty(fullPropertyName.left(lastDotIndex), fullPropertyName.mid(lastDotIndex + 1)); } diff --git a/src/lib/corelib/api/runenvironment.cpp b/src/lib/corelib/api/runenvironment.cpp index adf0c4557..23d0359b0 100644 --- a/src/lib/corelib/api/runenvironment.cpp +++ b/src/lib/corelib/api/runenvironment.cpp @@ -363,16 +363,16 @@ int RunEnvironment::doRunTarget(const QString &targetBin, const QStringList &arg << arguments; } } else { - if (QFileInfo(targetExecutable = findExecutable(QStringList() - << QStringLiteral("iostool"))).isExecutable()) { + if (targetExecutable = findExecutable(QStringList{QStringLiteral("iostool")}); + QFileInfo(targetExecutable).isExecutable()) { targetArguments = QStringList() << QStringLiteral("-run") << QStringLiteral("-bundle") << QDir::cleanPath(bundlePath); if (!arguments.empty()) targetArguments << QStringLiteral("-extra-args") << arguments; - } else if (QFileInfo(targetExecutable = findExecutable(QStringList() - << QStringLiteral("ios-deploy"))).isExecutable()) { + } else if (targetExecutable = findExecutable(QStringList{QStringLiteral("ios-deploy")}); + QFileInfo(targetExecutable).isExecutable()) { targetArguments = QStringList() << QStringLiteral("--no-wifi") << QStringLiteral("--noninteractive") diff --git a/src/lib/corelib/buildgraph/inputartifactscanner.cpp b/src/lib/corelib/buildgraph/inputartifactscanner.cpp index fb582c457..0c73f599f 100644 --- a/src/lib/corelib/buildgraph/inputartifactscanner.cpp +++ b/src/lib/corelib/buildgraph/inputartifactscanner.cpp @@ -103,9 +103,14 @@ static void resolveDepencency(const RawScannedDependency &dependency, } // prioritize found artifacts - if ((result->file = dependencyInProduct) - || (result->file = dependencyInOtherProduct) - || (result->file = fileDependencyArtifact)) { + if (dependencyInProduct) + result->file = dependencyInProduct; + else if (dependencyInOtherProduct) + result->file = dependencyInOtherProduct; + else + result->file = fileDependencyArtifact; + + if (result->file) { result->filePath = result->file->filePath(); if (result->file == dependencyInOtherProduct && !productOfDependencyIsDependency) { diff --git a/src/lib/corelib/buildgraph/nodetreedumper.cpp b/src/lib/corelib/buildgraph/nodetreedumper.cpp index 8475a46cf..6ad597c70 100644 --- a/src/lib/corelib/buildgraph/nodetreedumper.cpp +++ b/src/lib/corelib/buildgraph/nodetreedumper.cpp @@ -120,7 +120,7 @@ bool NodeTreeDumper::doVisit(BuildGraphNode *node, const QString &nodeRepr) QByteArray NodeTreeDumper::indentation() const { - return QByteArray(m_indentation, ' '); + return {m_indentation, ' '}; } } // namespace Internal diff --git a/src/lib/corelib/jsextensions/utilitiesextension.cpp b/src/lib/corelib/jsextensions/utilitiesextension.cpp index e733d618b..cdcee59fa 100644 --- a/src/lib/corelib/jsextensions/utilitiesextension.cpp +++ b/src/lib/corelib/jsextensions/utilitiesextension.cpp @@ -782,7 +782,7 @@ static QStringList detectMachOArchs(QIODevice *device) if (strncmp(ar_header, ARMAG, SARMAG) == 0) { while (!device->atEnd()) { static_assert(sizeof(ar_hdr) == 60, "sizeof(ar_hdr) != 60"); - ar_hdr header; + ar_hdr header{}; if (device->read(reinterpret_cast<char *>(&header), sizeof(ar_hdr)) != sizeof(ar_hdr)) return {}; @@ -832,7 +832,7 @@ static QStringList detectMachOArchs(QIODevice *device) pos = device->pos(); - fat_header fatheader; + fat_header fatheader{}; fatheader.magic = readInt(device, nullptr, false); if (fatheader.magic == FAT_MAGIC || fatheader.magic == FAT_CIGAM || fatheader.magic == FAT_MAGIC_64 || fatheader.magic == FAT_CIGAM_64) { @@ -845,7 +845,7 @@ static QStringList detectMachOArchs(QIODevice *device) QStringList archs; for (uint32_t n = 0; n < fatheader.nfat_arch; ++n) { - fat_arch_64 fatarch; + fat_arch_64 fatarch{}; static_assert(sizeof(fat_arch_64) == 32, "sizeof(fat_arch_64) != 32"); static_assert(sizeof(fat_arch) == 20, "sizeof(fat_arch) != 20"); const qint64 expectedBytes = is64bit ? sizeof(fat_arch_64) : sizeof(fat_arch); @@ -875,7 +875,7 @@ static QStringList detectMachOArchs(QIODevice *device) return {}; bool swap = false; - mach_header header; + mach_header header{}; header.magic = readInt(device, nullptr, swap); switch (header.magic) { case MH_CIGAM: diff --git a/src/lib/corelib/language/builtindeclarations.cpp b/src/lib/corelib/language/builtindeclarations.cpp index 4a83279a8..491b005e8 100644 --- a/src/lib/corelib/language/builtindeclarations.cpp +++ b/src/lib/corelib/language/builtindeclarations.cpp @@ -166,25 +166,28 @@ void BuiltinDeclarations::insert(const ItemDeclaration &decl) static PropertyDeclaration conditionProperty() { - return PropertyDeclaration(StringConstants::conditionProperty(), PropertyDeclaration::Boolean, - StringConstants::trueValue()); + return { + StringConstants::conditionProperty(), + PropertyDeclaration::Boolean, + StringConstants::trueValue()}; } static PropertyDeclaration alwaysRunProperty() { - return PropertyDeclaration(StringConstants::alwaysRunProperty(), PropertyDeclaration::Boolean, - StringConstants::falseValue()); + return { + StringConstants::alwaysRunProperty(), + PropertyDeclaration::Boolean, + StringConstants::falseValue()}; } static PropertyDeclaration nameProperty() { - return PropertyDeclaration(StringConstants::nameProperty(), PropertyDeclaration::String); + return {StringConstants::nameProperty(), PropertyDeclaration::String}; } static PropertyDeclaration buildDirProperty() { - return PropertyDeclaration(StringConstants::buildDirectoryProperty(), - PropertyDeclaration::Path); + return {StringConstants::buildDirectoryProperty(), PropertyDeclaration::Path}; } static PropertyDeclaration prepareScriptProperty() diff --git a/src/lib/corelib/language/item.cpp b/src/lib/corelib/language/item.cpp index 29fa1417d..e5de8f195 100644 --- a/src/lib/corelib/language/item.cpp +++ b/src/lib/corelib/language/item.cpp @@ -142,7 +142,8 @@ ValuePtr Item::property(const QString &name) const ValuePtr value; const Item *item = this; do { - if ((value = item->m_properties.value(name))) + value = item->m_properties.value(name); + if (value) break; item = item->m_prototype; } while (item); @@ -172,7 +173,7 @@ ItemValuePtr Item::itemProperty(const QString &name, const Item *itemTemplate, if (v && v->type() == Value::ItemValueType) return std::static_pointer_cast<ItemValue>(v); if (!itemTemplate) - return ItemValuePtr(); + return {}; const bool createdByPropertiesBlock = itemValue && itemValue->createdByPropertiesBlock(); ItemValuePtr result = ItemValue::create(Item::create(&pool, itemTemplate->type()), createdByPropertiesBlock); @@ -184,7 +185,7 @@ JSSourceValuePtr Item::sourceProperty(const QString &name) const { ValuePtr v = property(name); if (!v || v->type() != Value::JSSourceValueType) - return JSSourceValuePtr(); + return {}; return std::static_pointer_cast<JSSourceValue>(v); } @@ -192,7 +193,7 @@ VariantValuePtr Item::variantProperty(const QString &name) const { ValuePtr v = property(name); if (!v || v->type() != Value::VariantValueType) - return VariantValuePtr(); + return {}; return std::static_pointer_cast<VariantValue>(v); } diff --git a/src/lib/corelib/language/qualifiedid.cpp b/src/lib/corelib/language/qualifiedid.cpp index 9eb0e9463..87248ac21 100644 --- a/src/lib/corelib/language/qualifiedid.cpp +++ b/src/lib/corelib/language/qualifiedid.cpp @@ -58,7 +58,7 @@ QualifiedId::QualifiedId(const QStringList &nameParts) QualifiedId QualifiedId::fromString(const QString &str) { - return QualifiedId(str.split(QLatin1Char('.'))); + return {str.split(QLatin1Char('.'))}; } QString QualifiedId::toString() const diff --git a/src/lib/corelib/language/scriptengine.cpp b/src/lib/corelib/language/scriptengine.cpp index 7847cb24d..9131db7f5 100644 --- a/src/lib/corelib/language/scriptengine.cpp +++ b/src/lib/corelib/language/scriptengine.cpp @@ -808,7 +808,7 @@ JSValue ScriptEngine::evaluate(JsValueOwner resultOwner, const QString &code, m_scopeChains << scopeChain; const QByteArray &codeStr = code.toUtf8(); - m_evalPositions.emplace(std::make_pair(filePath, line)); + m_evalPositions.emplace(filePath, line); const JSValue v = JS_EvalThis(m_context, globalObject(), codeStr.constData(), codeStr.length(), filePath.toUtf8().constData(), line, JS_EVAL_TYPE_GLOBAL); m_evalPositions.pop(); @@ -829,7 +829,7 @@ ScopedJsValueList ScriptEngine::argumentList(const QStringList &argumentNames, JSValueList result; for (const auto &name : argumentNames) result.push_back(getJsProperty(m_context, context, name)); - return ScopedJsValueList(m_context, result); + return {m_context, result}; } JSClassID ScriptEngine::registerClass(const char *name, JSClassCall *constructor, diff --git a/src/lib/corelib/loader/astpropertiesitemhandler.cpp b/src/lib/corelib/loader/astpropertiesitemhandler.cpp index cd6a32908..8183e6b79 100644 --- a/src/lib/corelib/loader/astpropertiesitemhandler.cpp +++ b/src/lib/corelib/loader/astpropertiesitemhandler.cpp @@ -159,8 +159,7 @@ static JSSourceValue::AltProperty getPropertyData(const Item *propertiesItem, co throw ErrorInfo(Tr::tr("Properties.condition must be provided."), propertiesItem->location()); } - return JSSourceValue::AltProperty(StringConstants::falseValue(), - propertiesItem->location()); + return {StringConstants::falseValue(), propertiesItem->location()}; } if (Q_UNLIKELY(value->type() != Value::JSSourceValueType)) { throw ErrorInfo(Tr::tr("Properties.%1 must be a value binding.").arg(name), @@ -180,7 +179,7 @@ static JSSourceValue::AltProperty getPropertyData(const Item *propertiesItem, co } const JSSourceValuePtr srcval = std::static_pointer_cast<JSSourceValue>(value); - return JSSourceValue::AltProperty(srcval->sourceCodeForEvaluation(), srcval->location()); + return {srcval->sourceCodeForEvaluation(), srcval->location()}; } void ASTPropertiesItemHandler::handlePropertiesBlock(const Item *propertiesItem) diff --git a/src/lib/corelib/loader/dependenciesresolver.cpp b/src/lib/corelib/loader/dependenciesresolver.cpp index 5df47217f..479318e73 100644 --- a/src/lib/corelib/loader/dependenciesresolver.cpp +++ b/src/lib/corelib/loader/dependenciesresolver.cpp @@ -432,7 +432,7 @@ LoadModuleResult DependenciesResolver::loadModule( dependency.parameters); } else if (dependency.product) { productDep = dependency.product; // We have already done the look-up. - } else if (!(productDep = findMatchingProduct(dependency))) { + } else if (productDep = findMatchingProduct(dependency); !productDep) { moduleItem = findMatchingModule(dependency); } @@ -1125,8 +1125,8 @@ DependenciesContextImpl::DependenciesContextImpl(ProductContext &product, Loader std::pair<ProductDependency, ProductContext *> DependenciesContextImpl::pendingDependency() const { QBS_CHECK(!stateStack.empty()); - if (stateStack.front().currentDependsItem - && !stateStack.front().currentDependsItem->productTypes.empty()) { + if (const auto ¤tDependsItem = stateStack.front().currentDependsItem; + currentDependsItem && !currentDependsItem->productTypes.empty()) { qCDebug(lcLoaderScheduling) << "product" << m_product.displayName() << "to be delayed because of bulk dependency"; return {ProductDependency::Bulk, nullptr}; diff --git a/src/lib/corelib/loader/loaderutils.cpp b/src/lib/corelib/loader/loaderutils.cpp index a6105ee50..05a077dbe 100644 --- a/src/lib/corelib/loader/loaderutils.cpp +++ b/src/lib/corelib/loader/loaderutils.cpp @@ -435,7 +435,9 @@ void TopLevelProjectContext::removeModuleFileFromDirectoryCache(const QString &f auto &moduleFiles = moduleFilesGuard.get(); const auto it = moduleFiles.find(FileInfo::path(filePath)); QBS_CHECK(it != moduleFiles.end()); - it->second->removeOne(filePath); + auto &files = it->second; + QBS_CHECK(files); + files->removeOne(filePath); } void TopLevelProjectContext::addUnknownProfilePropertyError(const Item *moduleProto, diff --git a/src/lib/corelib/loader/moduleproviderloader.cpp b/src/lib/corelib/loader/moduleproviderloader.cpp index bceed8ece..a9f5eb26f 100644 --- a/src/lib/corelib/loader/moduleproviderloader.cpp +++ b/src/lib/corelib/loader/moduleproviderloader.cpp @@ -168,6 +168,7 @@ ModuleProviderLoader::findOrCreateProviderInfo( const QualifiedId &moduleName, const QualifiedId &name, ModuleProviderLookup lookupType, const QVariantMap &qbsModule) { + QBS_CHECK(product.providerConfig); const QVariantMap config = product.providerConfig->value(name.toString()).toMap(); std::lock_guard lock(m_loaderState.topLevelProject().moduleProvidersCacheLock()); ModuleProvidersCacheKey cacheKey{name.toString(), {}, config, qbsModule, int(lookupType)}; diff --git a/src/lib/corelib/logging/logger.cpp b/src/lib/corelib/logging/logger.cpp index d4f51cd30..65d0cc27f 100644 --- a/src/lib/corelib/logging/logger.cpp +++ b/src/lib/corelib/logging/logger.cpp @@ -231,7 +231,7 @@ void Logger::printWarning(const ErrorInfo &warning) LogWriter Logger::qbsLog(LoggerLevel level, bool force) const { - return LogWriter(m_logSink, level, force); + return {m_logSink, level, force}; } } // namespace Internal diff --git a/src/lib/corelib/parser/qmljslexer.cpp b/src/lib/corelib/parser/qmljslexer.cpp index 684be9317..e148652ad 100644 --- a/src/lib/corelib/parser/qmljslexer.cpp +++ b/src/lib/corelib/parser/qmljslexer.cpp @@ -1071,7 +1071,7 @@ bool Lexer::scanDirectives(Directives *directives) const int lineNumber = tokenStartLine(); - if (! (_tokenKind == T_IDENTIFIER || _tokenKind == T_RESERVED_WORD)) + if (_tokenKind != T_IDENTIFIER && _tokenKind != T_RESERVED_WORD) return false; // expected a valid QML/JS directive const QString directiveName = tokenText(); @@ -1083,7 +1083,7 @@ bool Lexer::scanDirectives(Directives *directives) // it must be a pragma or an import directive. if (directiveName == QLatin1String("pragma")) { // .pragma library - if (! (lex() == T_IDENTIFIER && tokenText() == QLatin1String("library"))) + if (lex() != T_IDENTIFIER || tokenText() != QLatin1String("library")) return false; // expected `library // we found a .pragma library directive @@ -1126,7 +1126,7 @@ bool Lexer::scanDirectives(Directives *directives) // // recognize the mandatory `as' followed by the module name // - if (! (lex() == T_RESERVED_WORD && tokenText() == QLatin1String("as"))) + if (lex() != T_RESERVED_WORD || tokenText() != QLatin1String("as")) return false; // expected `as' if (lex() != T_IDENTIFIER) diff --git a/src/lib/corelib/tools/clangclinfo.cpp b/src/lib/corelib/tools/clangclinfo.cpp index d89c5cb9b..fd907ebf1 100644 --- a/src/lib/corelib/tools/clangclinfo.cpp +++ b/src/lib/corelib/tools/clangclinfo.cpp @@ -47,7 +47,7 @@ static std::vector<MSVCInstallInfo> compatibleMsvcs(Logger &logger) return true; bool ok = false; const int major = versions.at(0).toInt(&ok); - return !(ok && major >= 15); // support MSVC2017 and above + return !ok || major < 15; // support MSVC2017 and above }; Internal::removeIf(msvcs, filter); for (const auto &msvc: msvcs) { diff --git a/src/lib/corelib/tools/profiling.cpp b/src/lib/corelib/tools/profiling.cpp index 62815912d..b93d3fa17 100644 --- a/src/lib/corelib/tools/profiling.cpp +++ b/src/lib/corelib/tools/profiling.cpp @@ -104,7 +104,7 @@ void AccumulatingTimer::stop() QString elapsedTimeString(qint64 elapsedTimeInNs) { - qint64 ms = elapsedTimeInNs / (1000 * 1000); + qint64 ms = elapsedTimeInNs / (1000ll * 1000ll); qint64 s = ms/1000; ms -= s*1000; qint64 m = s/60; diff --git a/src/lib/corelib/tools/scripttools.cpp b/src/lib/corelib/tools/scripttools.cpp index 6262e7cf7..e89e4a0ad 100644 --- a/src/lib/corelib/tools/scripttools.cpp +++ b/src/lib/corelib/tools/scripttools.cpp @@ -123,7 +123,7 @@ ErrorInfo JsException::toErrorInfo() const ErrorInfo e(msg, stackTrace()); if (e.hasLocation() || !m_fallbackLocation.isValid()) return e; - return ErrorInfo(msg, m_fallbackLocation); + return {msg, m_fallbackLocation}; } void defineJsProperty(JSContext *ctx, JSValueConst obj, const QString &prop, JSValue val) diff --git a/src/lib/corelib/tools/settingsmodel.cpp b/src/lib/corelib/tools/settingsmodel.cpp index e3c995db3..4fa0e14da 100644 --- a/src/lib/corelib/tools/settingsmodel.cpp +++ b/src/lib/corelib/tools/settingsmodel.cpp @@ -281,10 +281,10 @@ bool SettingsModel::setData(const QModelIndex &index, const QVariant &value, int const QString valueString = value.toString(); QString *toChange = nullptr; if (index.column() == keyColumn() && !valueString.isEmpty() - && !node->parent->hasDirectChildWithName(valueString) - && !(node->parent->parent == &d->rootNode - && node->parent->name == Internal::StringConstants::profilesSettingsKey() - && valueString == Profile::fallbackName())) { + && !node->parent->hasDirectChildWithName(valueString) + && (node->parent->parent != &d->rootNode + || node->parent->name != Internal::StringConstants::profilesSettingsKey() + || valueString != Profile::fallbackName())) { toChange = &node->name; } else if (index.column() == valueColumn() && valueString != node->value) { toChange = &node->value; diff --git a/src/lib/pkgconfig/pcparser.cpp b/src/lib/pkgconfig/pcparser.cpp index 4469ca193..eb07aa5ef 100644 --- a/src/lib/pkgconfig/pcparser.cpp +++ b/src/lib/pkgconfig/pcparser.cpp @@ -524,9 +524,11 @@ void PcParser::parseLibs( raiseDuplicateFieldException(fieldName, pkg.filePath); const auto trimmed = trimAndSubstitute(pkg, str); + if (trimmed.empty()) + return; const auto argv = splitCommand(trimmed); - if (!trimmed.empty() && !argv) + if (!argv) throw PcException("Couldn't parse Libs field into an argument vector"); libs = doParseLibs(*argv); @@ -593,9 +595,11 @@ void PcParser::parseCFlags(PcPackage &pkg, std::string_view str) raiseDuplicateFieldException("Cflags", pkg.filePath); const auto command = trimAndSubstitute(pkg, str); + if (command.empty()) + return; const auto argv = splitCommand(command); - if (!command.empty() && !argv) + if (!argv) throw PcException("Couldn't parse Cflags field into an argument vector"); std::vector<PcPackage::Flag> cflags; @@ -722,10 +726,8 @@ void PcParser::parseLine(PcPackage &pkg, std::string_view str) size_t pos = 0; for (; pos < s.size(); ++pos) { auto p = s.data() + pos; - if (!((*p >= 'A' && *p <= 'Z') || - (*p >= 'a' && *p <= 'z') || - (*p >= '0' && *p <= '9') || - *p == '_' || *p == '.')) { + if ((*p < 'A' || *p > 'Z') && (*p < 'a' || *p > 'z') && (*p < '0' || *p > '9') + && *p != '_' && *p != '.') { break; } } diff --git a/src/plugins/scanner/cpp/Lexer.cpp b/src/plugins/scanner/cpp/Lexer.cpp index ebf843aca..6793080e9 100644 --- a/src/plugins/scanner/cpp/Lexer.cpp +++ b/src/plugins/scanner/cpp/Lexer.cpp @@ -564,7 +564,7 @@ void Lexer::scan_helper(Token *tok) do { yyinp(); - if (! (isalnum(_yychar) || _yychar == '_' || _yychar == '$')) + if (!isalnum(_yychar) && _yychar != '_' && _yychar != '$') break; } while (_yychar); diff --git a/src/shared/quickjs/.clang-tidy b/src/shared/quickjs/.clang-tidy index c5e5db244..b9209aae5 100644 --- a/src/shared/quickjs/.clang-tidy +++ b/src/shared/quickjs/.clang-tidy @@ -1,13 +1,3 @@ ---- -Checks: > - -bugprone-branch-clone, - -bugprone-easily-swappable-parameters, - -bugprone-implicit-widening-of-multiplication-result, - -bugprone-signed-char-misuse, - -bugprone-suspicious-memory-comparison, - -bugprone-sizeof-expression, - -misc-redundant-expression, - -misc-unused-parameters +Checks: '-*,misc-definitions-in-headers' InheritParentConfig: true -... diff --git a/tests/auto/blackbox/testdata/exports-cmake/Foo.cpp b/tests/auto/blackbox/testdata/exports-cmake/Foo.cpp new file mode 100644 index 000000000..ea334f9af --- /dev/null +++ b/tests/auto/blackbox/testdata/exports-cmake/Foo.cpp @@ -0,0 +1,5 @@ +#include "Foo.h" +int someFooWork() +{ + return 42; +} diff --git a/tests/auto/blackbox/testdata/exports-cmake/Foo.h b/tests/auto/blackbox/testdata/exports-cmake/Foo.h new file mode 100644 index 000000000..2f279f577 --- /dev/null +++ b/tests/auto/blackbox/testdata/exports-cmake/Foo.h @@ -0,0 +1,16 @@ +#ifndef FOO_H +#define FOO_H +#include <dllexport.h> + +#ifdef FOO_LIB_STATIC +#define FOO_LIB_EXPORT +#else +#ifdef FOO_LIB +#define FOO_LIB_EXPORT DLL_EXPORT +#else +#define FOO_LIB_EXPORT DLL_IMPORT +#endif +#endif + +FOO_LIB_EXPORT int someFooWork(); +#endif // FOO_H diff --git a/tests/auto/blackbox/testdata/exports-cmake/cmake/CMakeLists.txt b/tests/auto/blackbox/testdata/exports-cmake/cmake/CMakeLists.txt new file mode 100644 index 000000000..d874e0e92 --- /dev/null +++ b/tests/auto/blackbox/testdata/exports-cmake/cmake/CMakeLists.txt @@ -0,0 +1,7 @@ +cmake_minimum_required(VERSION 3.10) + +project(qbs_import) + +find_package(Bar PATHS REQUIRED) +add_executable(Consumer main.cpp) +target_link_libraries(Consumer Bar) diff --git a/tests/auto/blackbox/testdata/exports-cmake/cmake/main.cpp b/tests/auto/blackbox/testdata/exports-cmake/cmake/main.cpp new file mode 100644 index 000000000..1a1fa90ec --- /dev/null +++ b/tests/auto/blackbox/testdata/exports-cmake/cmake/main.cpp @@ -0,0 +1,6 @@ +#include <Foo.h> + +int main() +{ + return someFooWork(); +} diff --git a/tests/auto/blackbox/testdata/exports-cmake/exports-cmake.qbs b/tests/auto/blackbox/testdata/exports-cmake/exports-cmake.qbs new file mode 100644 index 000000000..6464af705 --- /dev/null +++ b/tests/auto/blackbox/testdata/exports-cmake/exports-cmake.qbs @@ -0,0 +1,70 @@ +import qbs.FileInfo + +Project { + property bool isStatic: false + property bool isBundle: false + + property string headersInstallDir: "include" + + Product { + name: "DllExport" + Depends { name: "Exporter.cmake" } + Group { + name: "API headers" + files: ["../dllexport.h"] + qbs.install: true + qbs.installDir: project.headersInstallDir + } + Group { + fileTagsFilter: ["Exporter.cmake.package"] + qbs.install: true + qbs.installDir: "/lib/cmake/DllExport" + } + Export { + Depends { name: "cpp" } + cpp.includePaths: FileInfo.joinPaths( + exportingProduct.qbs.installRoot, + exportingProduct.qbs.installPrefix, + project.headersInstallDir) + } + } + + Library { + type: project.isStatic ? "staticlibrary" : "dynamiclibrary" + Depends { name: "cpp" } + Depends { name: "DllExport" } + Depends { name: "Exporter.cmake" } + Exporter.cmake.packageName: "Bar" + name: "Foo" + files: ["Foo.cpp"] + version: "1.2.3" + cpp.includePaths: "." + cpp.defines: "FOO_LIB" + Group { + name: "API headers" + files: ["Foo.h"] + qbs.install: true + qbs.installDir: project.headersInstallDir + } + install: true + installImportLib: true + Group { + fileTagsFilter: ["Exporter.cmake.package"] + qbs.install: true + qbs.installDir: "/lib/cmake/Bar" + } + Export { + Depends { name: "cpp" } + cpp.includePaths: FileInfo.joinPaths( + exportingProduct.qbs.installRoot, + exportingProduct.qbs.installPrefix, + project.headersInstallDir) + cpp.defines: ["FOO=1"].concat(project.isStatic ? ["FOO_LIB_STATIC"] : []) + cpp.commonCompilerFlags: "-DOTHER_DEF=1" + cpp.linkerFlags: exportingProduct.qbs.toolchain.contains("gcc") ? ["-s"] : [] + } + + Depends { name: 'bundle' } + bundle.isBundle: qbs.targetOS.includes("darwin") && project.isBundle + } +} diff --git a/tests/auto/blackbox/testdata/exports-cmake/find-cmake.qbs b/tests/auto/blackbox/testdata/exports-cmake/find-cmake.qbs new file mode 100644 index 000000000..52f388966 --- /dev/null +++ b/tests/auto/blackbox/testdata/exports-cmake/find-cmake.qbs @@ -0,0 +1,46 @@ +import qbs.Probes + +Product { + Depends { name: "cpp" } + + Probes.BinaryProbe { + id: cmakeProbe + names: "cmake" + } + + Probes.BinaryProbe { + id: ninjaProbe + names: ["ninja"] + } + + property bool test: { + var data = { + cmakeFound: cmakeProbe.found, + cmakeFilePath: cmakeProbe.filePath, + crossCompiling: qbs.targetPlatform !== qbs.hostPlatform, + installPrefix: qbs.installPrefix + }; + data.buildEnv = {} + Object.assign(data.buildEnv, cpp.buildEnv); // deep copy buildEnv from a probe + if (qbs.toolchain.includes("gcc")) { + data.buildEnv["CC"] = cpp.cCompilerName; + data.buildEnv["CXX"] = cpp.cxxCompilerName; + } else { + data.buildEnv["CC"] = cpp.compilerName; + data.buildEnv["CXX"] = cpp.compilerName; + } + + if (ninjaProbe.found) { + data.generator = "Ninja"; + } else { + if (qbs.toolchain.includes("msvc")) { + data.generator = "NMake Makefiles" + } else if (qbs.toolchain.includes("mingw")) { + data.generator = "MinGW Makefiles"; + } else if (qbs.toolchain.includes("gcc")) { + data.generator = "Unix Makefiles"; + } + } + console.info("---" + JSON.stringify(data) + "---"); + } +} diff --git a/tests/auto/blackbox/tst_blackbox.cpp b/tests/auto/blackbox/tst_blackbox.cpp index 395e5aba6..cd611e76f 100644 --- a/tests/auto/blackbox/tst_blackbox.cpp +++ b/tests/auto/blackbox/tst_blackbox.cpp @@ -1386,9 +1386,9 @@ void TestBlackbox::variantSuffix_data() std::make_pair(QString("unix"), QStringList())}); } -static bool waitForProcessSuccess(QProcess &p) +static bool waitForProcessSuccess(QProcess &p, int msecs = 30000) { - if (!p.waitForStarted() || !p.waitForFinished()) { + if (!p.waitForStarted(msecs) || !p.waitForFinished(msecs)) { qDebug() << p.errorString(); return false; } @@ -4099,6 +4099,82 @@ void TestBlackbox::exportToOutsideSearchPath() m_qbsStderr.constData()); } +void TestBlackbox::exportsCMake() +{ + QFETCH(QStringList, arguments); + + QDir::setCurrent(testDataDir + "/exports-cmake"); + rmDirR(relativeBuildDir()); + QbsRunParameters findCMakeParams("resolve", {"-f", "find-cmake.qbs"}); + QCOMPARE(runQbs(findCMakeParams), 0); + const QString output = QString::fromLocal8Bit(m_qbsStdout); + const QRegularExpression pattern( + QRegularExpression::anchoredPattern(".*---(.*)---.*"), + QRegularExpression::DotMatchesEverythingOption); + const QRegularExpressionMatch match = pattern.match(output); + QVERIFY2(match.hasMatch(), qPrintable(output)); + QCOMPARE(pattern.captureCount(), 1); + const QString jsonString = match.captured(1); + const QJsonDocument jsonDoc = QJsonDocument::fromJson(jsonString.toUtf8()); + const QJsonObject jsonData = jsonDoc.object(); + + rmDirR(relativeBuildDir()); + const QStringList exporterArgs{"-f", "exports-cmake.qbs"}; + QbsRunParameters exporterRunParams("build", exporterArgs); + exporterRunParams.arguments << arguments; + QCOMPARE(runQbs(exporterRunParams), 0); + + if (!jsonData.value(u"cmakeFound").toBool()) { + QSKIP("cmake is not installed"); + return; + } + + if (jsonData.value(u"crossCompiling").toBool()) { + QSKIP("test is not applicable with cross-compile toolchains"); + return; + } + + const auto cmakeFilePath = jsonData.value(u"cmakeFilePath").toString(); + QVERIFY(!cmakeFilePath.isEmpty()); + + const auto generator = jsonData.value(u"generator").toString(); + if (generator.isEmpty()) { + QSKIP("cannot detect cmake generator"); + return; + } + + QProcessEnvironment env = QProcessEnvironment::systemEnvironment(); + const auto buildEnv = jsonData.value(u"buildEnv").toObject(); + for (auto it = buildEnv.begin(), end = buildEnv.end(); it != end; ++it) { + env.insert(it.key(), it.value().toString()); + } + + const auto installPrefix = jsonData.value(u"installPrefix").toString(); + const auto cmakePrefixPath = QFileInfo(relativeBuildDir()).absoluteFilePath() + "/install-root/" + + installPrefix + "/lib/cmake"; + const auto sourceDirectory = testDataDir + "/exports-cmake/cmake"; + QProcess configure; + configure.setProcessEnvironment(env); + configure.setWorkingDirectory(sourceDirectory); + configure.start( + cmakeFilePath, {".", "-DCMAKE_PREFIX_PATH=" + cmakePrefixPath, "-G" + generator}); + QVERIFY(waitForProcessSuccess(configure, 120000)); + + QProcess build; + build.setProcessEnvironment(env); + build.setWorkingDirectory(sourceDirectory); + build.start(cmakeFilePath, QStringList{"--build", "."}); + QVERIFY(waitForProcessSuccess(build)); +} + +void TestBlackbox::exportsCMake_data() +{ + QTest::addColumn<QStringList>("arguments"); + QTest::newRow("dynamic lib") << QStringList("project.isStatic: false"); + QTest::newRow("static lib") << QStringList("project.isStatic: true"); + QTest::newRow("framework") << QStringList("project.isBundle: true"); +} + void TestBlackbox::exportsPkgconfig() { QDir::setCurrent(testDataDir + "/exports-pkgconfig"); diff --git a/tests/auto/blackbox/tst_blackbox.h b/tests/auto/blackbox/tst_blackbox.h index 62676625e..701002620 100644 --- a/tests/auto/blackbox/tst_blackbox.h +++ b/tests/auto/blackbox/tst_blackbox.h @@ -126,6 +126,8 @@ private slots: void exportedPropertyInDisabledProduct_data(); void exportRule(); void exportToOutsideSearchPath(); + void exportsCMake(); + void exportsCMake_data(); void exportsPkgconfig(); void exportsQbs(); void externalLibs(); |