diff options
909 files changed, 109187 insertions, 25847 deletions
diff --git a/.clang-tidy b/.clang-tidy index 1ae91aafb..e737372e9 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -2,8 +2,9 @@ Checks: > -*, bugprone-*, + -bugprone-narrowing-conversions, + -bugprone-throw-keyword-missing, cppcoreguidelines-interfaces-global-init, - cppcoreguidelines-pro-type-cstyle-cast, cppcoreguidelines-pro-type-member-init, cppcoreguidelines-slicing, fuchsia-virtual-inheritance, @@ -42,6 +43,7 @@ Checks: > modernize-use-transparent-functors, modernize-use-using, performance-*, + -performance-no-int-to-ptr, readability-avoid-const-params-in-decls, readability-container-size-empty, readability-delete-null-pointer, @@ -69,205 +71,5 @@ WarningsAsErrors: > HeaderFilterRegex: '' AnalyzeTemporaryDtors: false CheckOptions: - - key: bugprone-argument-comment.StrictMode - value: '0' - - key: bugprone-assert-side-effect.AssertMacros - value: assert - - key: bugprone-assert-side-effect.CheckFunctionCalls - value: '0' - - key: bugprone-dangling-handle.HandleClasses - value: 'std::basic_string_view;std::experimental::basic_string_view' - - key: bugprone-exception-escape.FunctionsThatShouldNotThrow - value: '' - - key: bugprone-exception-escape.IgnoredExceptions - value: '' - - key: bugprone-misplaced-widening-cast.CheckImplicitCasts - value: '0' - - key: bugprone-sizeof-expression.WarnOnSizeOfCompareToConstant - value: '1' - - key: bugprone-sizeof-expression.WarnOnSizeOfConstant - value: '1' - - key: bugprone-sizeof-expression.WarnOnSizeOfIntegerExpression - value: '0' - - key: bugprone-sizeof-expression.WarnOnSizeOfThis - value: '1' - - key: bugprone-string-constructor.LargeLengthThreshold - value: '8388608' - - key: bugprone-string-constructor.WarnOnLargeLength - value: '1' - - key: bugprone-suspicious-enum-usage.StrictMode - value: '0' - - key: bugprone-suspicious-missing-comma.MaxConcatenatedTokens - value: '5' - - key: bugprone-suspicious-missing-comma.RatioThreshold - value: '0.200000' - - key: bugprone-suspicious-missing-comma.SizeThreshold - value: '5' - - key: bugprone-suspicious-string-compare.StringCompareLikeFunctions - value: '' - - key: bugprone-suspicious-string-compare.WarnOnImplicitComparison - value: '1' - - key: bugprone-suspicious-string-compare.WarnOnLogicalNotComparison - value: '0' - - key: bugprone-unused-return-value.CheckedFunctions - value: '::std::async;::std::launder;::std::remove;::std::remove_if;::std::unique;::std::unique_ptr::release;::std::basic_string::empty;::std::vector::empty' - - key: cert-dcl16-c.NewSuffixes - value: 'L;LL;LU;LLU' - - key: cppcoreguidelines-no-malloc.Allocations - value: '::malloc;::calloc' - - key: cppcoreguidelines-no-malloc.Deallocations - value: '::free' - - key: cppcoreguidelines-no-malloc.Reallocations - value: '::realloc' - - key: cppcoreguidelines-non-private-member-variables-in-classes.IgnoreClassesWithAllMemberVariablesBeingPublic - value: '1' - - key: cppcoreguidelines-pro-bounds-constant-array-index.GslHeader - value: '' - - key: cppcoreguidelines-pro-bounds-constant-array-index.IncludeStyle - value: '0' - - key: cppcoreguidelines-pro-type-member-init.IgnoreArrays - value: '0' - - key: cppcoreguidelines-special-member-functions.AllowMissingMoveFunctions - value: '0' - - key: cppcoreguidelines-special-member-functions.AllowSoleDefaultDtor - value: '0' - - key: google-build-namespaces.HeaderFileExtensions - value: ',h,hh,hpp,hxx' - - key: google-global-names-in-headers.HeaderFileExtensions - value: ',h,hh,hpp,hxx' - - key: google-readability-braces-around-statements.ShortStatementLines - value: '1' - - key: google-readability-function-size.BranchThreshold - value: '4294967295' - - key: google-readability-function-size.LineThreshold - value: '4294967295' - - key: google-readability-function-size.NestingThreshold - value: '4294967295' - - key: google-readability-function-size.ParameterThreshold - value: '4294967295' - - key: google-readability-function-size.StatementThreshold - value: '800' - - key: google-readability-function-size.VariableThreshold - value: '4294967295' - - key: google-readability-namespace-comments.ShortNamespaceLines - value: '10' - - key: google-readability-namespace-comments.SpacesBeforeComments - value: '2' - - key: google-runtime-int.SignedTypePrefix - value: int - - key: google-runtime-int.TypeSuffix - value: '' - - key: google-runtime-int.UnsignedTypePrefix - value: uint - - key: misc-definitions-in-headers.HeaderFileExtensions - value: ',h,hh,hpp,hxx' - - key: misc-definitions-in-headers.UseHeaderFileExtension - value: '1' - - key: misc-unused-parameters.StrictMode - value: '0' - - key: modernize-loop-convert.MaxCopySize - value: '16' - - key: modernize-loop-convert.MinConfidence - value: reasonable - - key: modernize-loop-convert.NamingStyle - value: CamelCase - - key: modernize-make-shared.IgnoreMacros - value: '1' - - key: modernize-make-shared.IncludeStyle - value: '0' - - key: modernize-make-shared.MakeSmartPtrFunction - value: 'std::make_shared' - - key: modernize-make-shared.MakeSmartPtrFunctionHeader - value: memory - - key: modernize-make-unique.IgnoreMacros - value: '1' - - key: modernize-make-unique.IncludeStyle - value: '0' - - key: modernize-make-unique.MakeSmartPtrFunction - value: 'std::make_unique' - - key: modernize-make-unique.MakeSmartPtrFunctionHeader - value: memory - - key: modernize-pass-by-value.IncludeStyle - value: llvm - - key: modernize-pass-by-value.ValuesOnly - value: '0' - - key: modernize-raw-string-literal.ReplaceShorterLiterals - value: '0' - - key: modernize-replace-auto-ptr.IncludeStyle - value: llvm - - key: modernize-replace-random-shuffle.IncludeStyle - value: llvm - - key: modernize-use-auto.MinTypeNameLength - value: '5' - - key: modernize-use-auto.RemoveStars - value: '0' - - key: modernize-use-emplace.ContainersWithPushBack - value: '::std::vector;::std::list;::std::deque' - - key: modernize-use-emplace.SmartPointers - value: '::std::shared_ptr;::std::unique_ptr;::std::auto_ptr;::std::weak_ptr' - - key: modernize-use-emplace.TupleMakeFunctions - value: '::std::make_pair;::std::make_tuple' - - key: modernize-use-emplace.TupleTypes - value: '::std::pair;::std::tuple' - - key: modernize-use-equals-default.IgnoreMacros - value: '1' - - key: modernize-use-equals-delete.IgnoreMacros - value: '1' - - key: modernize-use-noexcept.ReplacementString - value: '' - - key: modernize-use-noexcept.UseNoexceptFalse - value: '1' - - key: modernize-use-nullptr.NullMacros - value: 'NULL' - - key: modernize-use-transparent-functors.SafeMode - value: '0' - - key: modernize-use-using.IgnoreMacros - value: '1' - - key: performance-faster-string-find.StringLikeClasses - value: 'std::basic_string' - - key: performance-for-range-copy.AllowedTypes - value: '' - - key: performance-for-range-copy.WarnOnAllAutoCopies - value: '0' - - key: performance-inefficient-string-concatenation.StrictMode - value: '0' - - key: performance-inefficient-vector-operation.VectorLikeClasses - value: '::std::vector' - - key: performance-move-const-arg.CheckTriviallyCopyableMove - value: '1' - - key: performance-move-constructor-init.IncludeStyle - value: llvm - - key: performance-type-promotion-in-math-fn.IncludeStyle - value: llvm - - key: performance-unnecessary-copy-initialization.AllowedTypes - value: '' - - key: performance-unnecessary-value-param.AllowedTypes - value: '' - - key: performance-unnecessary-value-param.IncludeStyle - value: llvm - - key: readability-function-size.BranchThreshold - value: '4294967295' - - key: readability-function-size.LineThreshold - value: '4294967295' - - key: readability-function-size.NestingThreshold - value: '4294967295' - - key: readability-function-size.ParameterThreshold - value: '4294967295' - - key: readability-function-size.StatementThreshold - value: '800' - - key: readability-function-size.VariableThreshold - value: '4294967295' - - key: readability-identifier-naming.IgnoreFailedSplit - value: '0' - - key: readability-inconsistent-declaration-parameter-name.IgnoreMacros - value: '1' - - key: readability-inconsistent-declaration-parameter-name.Strict - value: '0' - - key: readability-redundant-smartptr-get.IgnoreMacros - value: '1' - - key: readability-simplify-boolean-expr.ChainedConditionalAssignment - value: '0' - - key: readability-simplify-boolean-expr.ChainedConditionalReturn - value: '0' ... diff --git a/.github/actions/download-mingw/action.yml b/.github/actions/download-mingw/action.yml index f8fb47f28..24d196785 100644 --- a/.github/actions/download-mingw/action.yml +++ b/.github/actions/download-mingw/action.yml @@ -4,7 +4,7 @@ inputs: version: description: 'MinGW version' required: false - default: '8.1.0' + default: '9.0.0' toolchain: description: 'Toolchain' required: false diff --git a/.github/actions/download-qt/action.yml b/.github/actions/download-qt/action.yml index e27358125..039f29ca6 100644 --- a/.github/actions/download-qt/action.yml +++ b/.github/actions/download-qt/action.yml @@ -4,7 +4,7 @@ inputs: version: description: 'Qt version' required: false - default: '5.15.2' + default: '6.6.0' target: description: 'Qt target (desktop, ios, android)' required: false @@ -17,6 +17,6 @@ runs: steps: - name: Install Qt run: | - QT_DIR=$(./scripts/install-qt.sh -d $HOME/Qt --version ${{ inputs.version }} --target ${{ inputs.target }} --toolchain ${{ inputs.toolchain }} qtbase qtdeclarative qttools qtscript qtscxml qt5compat) + QT_DIR=$(./scripts/install-qt.sh -d $HOME/Qt --version ${{ inputs.version }} --target ${{ inputs.target }} --toolchain ${{ inputs.toolchain }} qtbase qtdeclarative qttools qtscxml qt5compat) (cygpath -w ${QTC_DIR} 2>/dev/null || echo ${QT_DIR}) >> ${GITHUB_PATH} shell: bash diff --git a/.github/actions/download-qtc/action.yml b/.github/actions/download-qtc/action.yml index 292991b38..542a2cb28 100644 --- a/.github/actions/download-qtc/action.yml +++ b/.github/actions/download-qtc/action.yml @@ -4,7 +4,7 @@ inputs: version: description: 'Qt Creator version' required: false - default: '5.0.3' + default: '9.0.1' runs: using: "composite" steps: diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index bacf90555..ae91640d9 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -23,7 +23,7 @@ jobs: products.qbs_archive.targetName:qbs-linux-${{ github.run_id }} products.qbs_archive.includeTests:true', script: './scripts/build-qbs-with-qbs.sh', - cacheid: 'gcc', + cacheid: 'gcc-qt6', } env: BUILD_OPTIONS: ${{ matrix.config.options }} @@ -41,14 +41,14 @@ jobs: path: ~/.ccache key: ${{ runner.os }}-${{ matrix.config.cacheid }}-ccache-${{ steps.get-timestamp.outputs.timestamp }} restore-keys: ${{ runner.os }}-${{ matrix.config.cacheid }}-ccache- - - name: Pull the Focal Image - run: docker-compose pull focal + - name: Pull the Docker Image + run: docker-compose pull focal-qt6 - name: Print ccache stats - run: docker-compose run focal ccache -s + run: docker-compose run focal-qt6 ccache -s - name: Build Qbs - run: docker-compose run focal ${{ matrix.config.script }} + run: docker-compose run focal-qt6 ${{ matrix.config.script }} - name: Print ccache stats - run: docker-compose run focal ccache -s + run: docker-compose run focal-qt6 ccache -s - name: Upload artifacts uses: 'actions/upload-artifact@v2' with: @@ -58,7 +58,7 @@ jobs: build-linux-extra: name: ${{ matrix.config.name }} runs-on: ubuntu-latest - timeout-minutes: 60 + timeout-minutes: 90 strategy: fail-fast: false matrix: @@ -66,7 +66,7 @@ jobs: - { name: 'Build on Linux (clang_tidy)', script: './scripts/run-analyzer.sh', - image: 'focal', + image: 'focal-qt6', options: 'profile:qt-clang_64 modules.cpp.compilerWrapper:ccache', cacheid: 'clang', } @@ -77,29 +77,39 @@ jobs: cacheid: 'cmake', } - { - name: 'Build on Linux (QMake)', - script: './scripts/build-qbs-with-qmake.sh', - image: 'focal', - options: 'CONFIG+=ccache', - cacheid: 'qmake', + name: 'Build on Linux (gcc, ASAN)', + image: 'focal-qt6', + script: './scripts/build-qbs-with-qbs.sh', + options: 'modules.cpp.compilerWrapper:ccache + modules.qbsbuildconfig.enableAddressSanitizer:true + modules.qbs.debugInformation:true + modules.qbsbuildconfig.enableBundledQt:true', + cacheid: 'gcc-asan', } - { - name: 'Build on Linux (gcc, Qt 6)', + name: 'Build on Linux (gcc, UBSAN)', image: 'focal-qt6', script: './scripts/build-qbs-with-qbs.sh', options: 'modules.cpp.compilerWrapper:ccache - modules.qbsbuildconfig.enableAddressSanitizer:false + modules.qbsbuildconfig.enableUbSanitizer:true modules.qbs.debugInformation:true modules.qbsbuildconfig.enableBundledQt:true', - cacheid: 'gcc-qt6', + cacheid: 'gcc-ubsan', + } + - { + name: 'Build on Linux (gcc, Qt 5.15)', + image: 'focal', + script: './scripts/build-qbs-with-qbs.sh', + options: 'modules.cpp.compilerWrapper:ccache + modules.qbs.debugInformation:true + modules.qbsbuildconfig.enableBundledQt:true', + cacheid: 'gcc-qt5', } env: BUILD_OPTIONS: ${{ matrix.config.options }} QTEST_FUNCTION_TIMEOUT: 9000000 steps: - uses: actions/checkout@v1 - with: - submodules: true - name: Create .ccache dir run: mkdir -p ~/.ccache - name: prepare timestamp @@ -111,7 +121,7 @@ jobs: path: ~/.ccache key: ${{ runner.os }}-${{ matrix.config.cacheid }}-ccache-${{ steps.get-timestamp.outputs.timestamp }} restore-keys: ${{ runner.os }}-${{ matrix.config.cacheid }}-ccache- - - name: Pull the Focal Image + - name: Pull the Docker Image run: docker-compose pull ${{ matrix.config.image }} - name: Print ccache stats run: docker-compose run ${{ matrix.config.image }} ccache -s @@ -122,7 +132,7 @@ jobs: build-macos: name: Build on macOS - runs-on: macos-10.15 + runs-on: macos-11 timeout-minutes: 60 env: BUILD_OPTIONS: | @@ -160,7 +170,7 @@ jobs: run: | qbs setup-toolchains --detect qbs setup-qt --detect - qbs config profiles.qt.baseProfile xcode-macosx-x86_64 + qbs config profiles.qt.baseProfile xcode_13_2_1-macosx-x86_64 qbs config defaultProfile qt qbs config --list - name: Print ccache stats @@ -274,14 +284,13 @@ jobs: - name: Install Qt uses: ./.github/actions/download-qt with: - toolchain: win64_mingw81 + toolchain: win64_mingw - name: Install Qt Creator uses: ./.github/actions/download-qtc - name: Install MinGW uses: ./.github/actions/download-mingw - name: Setup Qbs run: | - qbs setup-toolchains --detect qbs setup-toolchains --type mingw $(which g++).exe mingw-qt qbs setup-qt $(which qmake).exe qt qbs config profiles.qt.baseProfile mingw-qt @@ -304,7 +313,7 @@ jobs: test-linux: name: ${{ matrix.config.name }} runs-on: ubuntu-latest - timeout-minutes: 60 + timeout-minutes: 90 needs: build-linux strategy: fail-fast: false @@ -323,42 +332,24 @@ jobs: script: './scripts/test-qbs.sh', } - { - name: 'Run Linux tests (gcc, Qt 6.0)', + name: 'Run Linux tests (gcc, Qt 6.2)', image: 'focal-qt6', profile: 'qt-gcc_64', script: './scripts/test-qt.sh', } - { - name: 'Run Linux tests (gcc, Qt 6.2 static)', + name: 'Run Linux tests (gcc, Qt 6.5 static)', image: 'focal-qt6-static', profile: 'qt-gcc_64', script: './scripts/test-qt.sh', } - { - name: 'Run Android tests (Qt 5.13)', - image: 'focal-android-513', - profile: '', - script: './scripts/test-qt-for-android.sh', - } - - { - name: 'Run Android tests (Qt 5.14)', - image: 'focal-android-514', - profile: '', - script: './scripts/test-qt-for-android.sh', - } - - { name: 'Run Android tests (Qt 5.15)', image: 'focal-android-515', profile: '', script: './scripts/test-qt-for-android.sh', } - { - name: 'Run Android tests (Qt 6.0.0)', - image: 'focal-android-600', - profile: '', - script: './scripts/test-qt-for-android.sh', - } - - { name: 'Run Android tests (Qt 6.2.0)', image: 'focal-android-620', profile: '', @@ -371,6 +362,18 @@ jobs: script: './scripts/test-qt-for-android.sh', } - { + name: 'Run Android tests (Qt 6.4.2)', + image: 'focal-android-642', + profile: '', + script: './scripts/test-qt-for-android.sh', + } + - { + name: 'Run Android tests (Qt 6.5.0)', + image: 'focal-android-650', + profile: '', + script: './scripts/test-qt-for-android.sh', + } + - { name: 'Run Android tests (ndk r19c)', image: 'focal-android-ndk-r19c', profile: '', @@ -429,7 +432,7 @@ jobs: path: ./ - name: Unpack artifact run: mkdir -p release/install-root/ && tar xzf qbs-linux-${{ github.run_id }}.tar.gz -C release/install-root/ - - name: Pull the Focal-Baremetal Image + - name: Pull the Docker Image run: docker-compose pull focal-baremetal - name: arm-none-eabi-gcc-9_2 run: QBS_AUTOTEST_PROFILE=arm-none-eabi-gcc-9_2 docker-compose run focal-baremetal scripts/test-baremetal.sh release/install-root/usr/local/bin @@ -608,68 +611,83 @@ jobs: QTEST_FUNCTION_TIMEOUT: 9000000 QBS_AUTOTEST_PROFILE: 'qt' QBS_TEST_SOURCE_ROOT: 'tests' + QBS_EXTRA_GRPC_LIBS: 'absl_cord,absl_cordz_handle,absl_cordz_info,absl_synchronization,grpc,gpr' strategy: fail-fast: false matrix: config: - { - name: 'Run macOS tests (Xcode 12.5)', - runner: 'macos-11.0', + name: 'Run macOS tests (Xcode 14.3)', + runner: 'macos-13', target: 'desktop', toolchain: 'clang_64', - testProfile: 'xcode_12_5_1-macosx-x86_64', - qtVersion: '5.15.2', + xcodeVersion: '14.3', + testProfile: 'xcode_14_3-macosx-x86_64', + qtVersion: '6.5.0', script: './scripts/test-qbs.sh', } - { - name: 'Run macOS tests (Xcode 12.5, Qt 6.0)', - runner: 'macos-11.0', + name: 'Run macOS tests (Xcode 14.3, Qt 5.15)', + runner: 'macos-13', target: 'desktop', toolchain: 'clang_64', - testProfile: 'xcode_12_5_1-macosx-x86_64', - qtVersion: '6.0.2', + xcodeVersion: '14.3', + testProfile: 'xcode_14_3-macosx-x86_64', + qtVersion: '5.15.2', script: './scripts/test-qt.sh', } - { - name: 'Run iOS tests (Xcode 12.5)', - runner: 'macos-11.0', + name: 'Run iOS tests (Xcode 14.3)', + runner: 'macos-13', target: 'ios', toolchain: 'ios', - testProfile: 'xcode_12_5_1-iphoneos-arm64', - qtVersion: '5.15.2', + xcodeVersion: '14.3', + testProfile: 'xcode_14_3-iphoneos-arm64', + qtVersion: '6.5.0', script: './scripts/test-qbs.sh', } - { - name: 'Run iOS-sim tests (Xcode 12.5)', - runner: 'macos-11.0', + name: 'Run iOS-sim tests (Xcode 14.3)', + runner: 'macos-13', target: 'ios', toolchain: 'ios', - testProfile: 'xcode_12_5_1-iphonesimulator-x86_64', - qtVersion: '5.15.2', + xcodeVersion: '14.3', + testProfile: 'xcode_14_3-iphonesimulator-x86_64', + qtVersion: '6.5.0', script: './scripts/test-qbs.sh', } - { - name: 'Run macOS tests (Xcode 11.6)', - runner: 'macos-10.15', + name: 'Run macOS tests (Xcode 14.2)', + runner: 'macos-13', target: 'desktop', toolchain: 'clang_64', - testProfile: 'xcode_11_6-macosx-x86_64', - qtVersion: '5.15.2', + xcodeVersion: '14.2', + testProfile: 'xcode_14_2-macosx-x86_64', + qtVersion: '6.5.0', script: './scripts/test-qbs.sh', } - { - name: 'Run macOS tests (Xcode 10.3)', - runner: 'macos-10.15', + name: 'Run macOS tests (Xcode 13.4)', + runner: 'macos-12', target: 'desktop', toolchain: 'clang_64', - testProfile: 'xcode_10_3-macosx-x86_64', - qtVersion: '5.15.2', + xcodeVersion: '13.4.1', + testProfile: 'xcode_13_4_1-macosx-x86_64', + qtVersion: '6.5.0', + script: './scripts/test-qbs.sh', + } + - { + name: 'Run macOS tests (Xcode-less)', + runner: 'macos-13', + target: 'desktop', + toolchain: 'clang_64', + xcodeVersion: '', + testProfile: 'xcode_13_4_1-macosx-x86_64', + qtVersion: '6.5.0', script: './scripts/test-qbs.sh', } steps: - uses: actions/checkout@v1 - with: - submodules: true - name: Download artifact uses: actions/download-artifact@v1 with: @@ -681,12 +699,22 @@ jobs: run: echo "./release/install-root/usr/local/bin" >> $GITHUB_PATH - name: Install required packages run: brew install capnp ccache grpc icoutils makensis protobuf p7zip + - name: Install Host Qt + if: matrix.config.toolchain == 'ios' + uses: ./.github/actions/download-qt + with: + target: 'desktop' + toolchain: 'clang_64' + version: ${{ matrix.config.qtVersion }} - name: Install Qt uses: ./.github/actions/download-qt with: target: ${{ matrix.config.target }} toolchain: ${{ matrix.config.toolchain }} version: ${{ matrix.config.qtVersion }} + - name: Select Xcode + run: sudo xcode-select --switch /Applications/Xcode_${{ matrix.config.xcodeVersion }}.app + if: matrix.config.xcodeVersion != '' - name: Setup Qbs run: | qbs setup-toolchains --detect @@ -694,6 +722,15 @@ jobs: qbs config profiles.qt.baseProfile ${{ matrix.config.testProfile }} qbs config defaultProfile qt qbs config --list + if: matrix.config.xcodeVersion != '' + - name: Setup Qbs (Xcode-less) + run: | + qbs setup-toolchains /usr/bin/clang++ ${{ matrix.config.testProfile }} + qbs setup-qt $(which qmake) qt + qbs config profiles.qt.baseProfile ${{ matrix.config.testProfile }} + qbs config defaultProfile qt + qbs config --list + if: matrix.config.xcodeVersion == '' - name: Run Tests run: | sudo chmod g+w /cores @@ -703,12 +740,12 @@ jobs: if: ${{ failure() }} run: | for f in $(find /cores -maxdepth 1 -name 'core.*' -print); do - lldb --core $f --batch --one-line "bt" + lldb --core $f --batch --one-line "bt all" done; test-windows: name: ${{ matrix.config.name }} - runs-on: windows-2019 + runs-on: windows-2022 timeout-minutes: 60 needs: build-windows strategy: @@ -716,19 +753,19 @@ jobs: matrix: config: - { - name: 'Run Windows tests (MSVC 2019)', + name: 'Run Windows tests (MSVC 2022)', target: 'desktop', toolchain: 'win64_msvc2019_64', - testProfile: 'MSVC2019-x64', - qtVersion: '5.15.2', + testProfile: 'MSVC2022-x64', + qtVersion: '6.5.0', script: './scripts/test-qbs.sh', } - { - name: 'Run Windows tests (MSVC 2019, Qt 6.0.2)', + name: 'Run Windows tests (MSVC 2022, Qt 5.15)', target: 'desktop', toolchain: 'win64_msvc2019_64', - testProfile: 'MSVC2019-x64', - qtVersion: '6.0.2', + testProfile: 'MSVC2022-x64', + qtVersion: '5.15.2', script: './scripts/test-qt.sh', } - { @@ -736,15 +773,15 @@ jobs: target: 'desktop', toolchain: 'win64_msvc2019_64', testProfile: 'clang-cl-x86_64', - qtVersion: '5.15.2', + qtVersion: '6.5.0', script: './scripts/test-qbs.sh', } - { name: 'Run Windows tests (MinGW)', target: 'desktop', - toolchain: 'win64_mingw81', + toolchain: 'win64_mingw', testProfile: 'mingw-qt', - qtVersion: '5.15.2', + qtVersion: '6.5.0', script: './scripts/test-qbs.sh', } env: @@ -769,6 +806,7 @@ jobs: run: echo "./release/install-root/bin" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append - name: Install required packages run: choco install -y pkgconfiglite --download-checksum=6004df17818f5a6dbf19cb335cc92702 + continue-on-error: true # pkgconfiglite installation is flaky - name: Install Qt uses: ./.github/actions/download-qt with: @@ -869,8 +907,6 @@ jobs: shell: bash - name: Update PATH run: echo "./release/install-root/bin" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append - - name: Install required packages - run: choco install -y pkgconfiglite --download-checksum=6004df17818f5a6dbf19cb335cc92702 - name: Install OpenWatcom uses: ./.github/actions/download-ow - name: Install DigitalMars diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 7c3396a1d..e89f79b21 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -21,6 +21,7 @@ jobs: options: 'qbs.installPrefix:"" modules.cpp.compilerWrapper:ccache modules.qbsbuildconfig.enableAddressSanitizer:false + project.withTests:false modules.qbsbuildconfig.enableUnitTests:false modules.qbsbuildconfig.enableBundledQt:true products.qbs_archive.targetName:qbs-linux-${{ github.run_id }}', @@ -64,12 +65,13 @@ jobs: build-macos: name: Build on macOS - runs-on: macos-10.15 + runs-on: macos-11 timeout-minutes: 60 env: BUILD_OPTIONS: | qbs.installPrefix:"" modules.cpp.compilerWrapper:ccache + project.withTests:false modules.qbsbuildconfig.enableUnitTests:false modules.qbsbuildconfig.enableAddressSanitizer:false modules.qbsbuildconfig.enableBundledQt:true @@ -98,13 +100,11 @@ jobs: toolchain: clang_64 - name: Install Qt Creator uses: ./.github/actions/download-qtc - with: - version: 5.0.3 - name: Setup Qbs run: | qbs setup-toolchains --detect qbs setup-qt --detect - qbs config profiles.qt.baseProfile xcode-macosx-x86_64 + qbs config profiles.qt.baseProfile xcode_13_2_1-macosx-x86_64 qbs config defaultProfile qt qbs config --list - name: Print ccache stats @@ -152,12 +152,12 @@ jobs: -p dist qbs.buildVariant:release modules.cpp.compilerWrapper:clcache + project.withTests:false modules.qbsbuildconfig.enableBundledQt:true modules.qbsbuildconfig.enableUnitTests:false modules.cpp.treatWarningsAsErrors:true project.withDocumentation:true - config:release-64 profile:qt64 - config:release profile:qt + config:release profile:qt64 - name: Print clcache stats run: docker-compose run --rm windows clcache -s - name: Get archive name @@ -171,7 +171,6 @@ jobs: path: | release/qbs.*.nupkg release/qbs-windows-*.zip - release-64/qbs-windows-*.zip create-archives: name: Create Archives @@ -179,6 +178,8 @@ jobs: needs: [build-linux, build-windows-with-docker] steps: - uses: actions/checkout@v1 + with: + submodules: true - name: Get version name id: get-version-name run: echo ::set-output name=version-name::$(cat VERSION) @@ -211,12 +212,11 @@ jobs: uses: actions/download-artifact@v1 with: name: qbs-windows-${{ github.run_id }} - path: ./tmp + path: ./tmp/release - name: Copy Windows artifacts run: | cp ./tmp/release/qbs.*.nupkg ./release - cp ./tmp/release/qbs-windows-x86-*.zip ./release - cp ./tmp/release-64/qbs-windows-x86_64-*.zip ./release + cp ./tmp/release/qbs-windows-x86_64-*.zip ./release - name: Copy changelog run: cp changelogs/changes-${{ steps.get-version-name.outputs.version-name }}.md release || echo "changelog not found" - name: Generate checksums @@ -236,7 +236,6 @@ jobs: release/qbs-src-*.tar.gz release/qbs-linux-*.tar.gz release/qbs.*.nupkg - release/qbs-windows-x86-*.zip release/qbs-windows-x86_64-*.zip release/changes-*.md release/md5sums.txt diff --git a/.gitmodules b/.gitmodules deleted file mode 100644 index f2f854c17..000000000 --- a/.gitmodules +++ /dev/null @@ -1,3 +0,0 @@ -[submodule "qtscript"] - path = src/shared/qtscript - url = ../../qt/qtscript.git diff --git a/CMakeLists.txt b/CMakeLists.txt index 4129bc911..f08ed0b0e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -45,17 +45,16 @@ find_package(Qt${QT_VERSION_MAJOR} OPTIONAL_COMPONENTS Script QUIET) if (Qt6_FOUND) find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Core5Compat REQUIRED) if(NOT TARGET Qt6Core5Compat) - set_property(TARGET Qt6::Core5Compat PROPERTY IMPORTED_GLOBAL TRUE) # hack for CMake < 1.18 + if(CMAKE_VERSION VERSION_LESS 3.18) + set_property(TARGET Qt6::Core5Compat PROPERTY IMPORTED_GLOBAL TRUE) # hack for CMake < 3.18 + endif() add_library(Qt6Core5Compat ALIAS Qt6::Core5Compat) endif() else() if(NOT TARGET Qt6Core5Compat) add_library(Qt6Core5Compat INTERFACE) endif() - if (NOT QBS_USE_BUNDLED_QT_SCRIPT AND Qt5Script_FOUND) - add_library(qbsscriptengine INTERFACE) - target_link_libraries(qbsscriptengine INTERFACE Qt5::Script) - endif() + find_package(Qt${QT_VERSION_MAJOR} OPTIONAL_COMPONENTS DocTools) endif() if (QBS_INSTALL_HTML_DOCS OR QBS_INSTALL_QCH_DOCS) diff --git a/RELEASE.md b/RELEASE.md new file mode 100644 index 000000000..c75069ae1 --- /dev/null +++ b/RELEASE.md @@ -0,0 +1,105 @@ +# Release Instructions + +This file contains instructions how to publish a Qbs release into various package systems. + +## Building packages + +Release packages are built automatically by the +[Build release packages](https://github.com/qbs/qbs/actions/workflows/release.yml) job on GitHub +actions. The job builds every commit, so you'll need to find a specific run triggered by +pushing a git tag instead of a commit. + +The only interesting artifact is qbs-release-v1.x.y - it contains Windows binary packages as +well as source packages. + +Packages should then be uploaded to the +[Qt Website](https://download.qt.io/official_releases/qbs/). + +## Chocolatey + +For updating Qbs in [Chocolatey](https://community.chocolatey.org/packages/qbs), you'll need +to be a maintainer of the Qbs project. You'll also will need an API key from your +[account](https://community.chocolatey.org/account) (make sure you have one). + +Get the qbs.1.x.y.nupkg file from the qbs-release-v1.x.y.zip archive and run the following command: + +``` +choco push --api-key <YOUR_API_KEY> qbs.1.x.y.nupkg" +``` + +Choco will upload the file, download binary packages from the Qt Website and publish Qbs +automatically. + +## Homebrew + +First, you'll need to [install](https://docs.brew.sh/Installation) Homewbrew. + +Second, you'll need to fork the [homebrew-core](https://github.com/Homebrew/homebrew-core) repo +to you GitHub account. + +Next you'll need to add your remote to the existing repo: +``` +$ brew update +$ cd "$(brew --repository homebrew/core)" +$ git remote add github git@github.com:<USERNAME>/homebrew-core.git +# Create a new git branch for your formula so your pull request is easy to +# modify if any changes come up during review. +$ git checkout -b <some-descriptive-name> origin/master +``` + +You'll need a SHA-256 sum for the qbs-src-1.x.y.tar.gz source package. SHA-256 sum can be +found in the sha256sums.txt file from the qbs-release-v1.x.y.zip. + +Check if the same upgrade has been already submitted by searching the open pull requests for +[Qbs](https://github.com/Homebrew/homebrew-core/pulls?q=is%3Apr+is%3Aopen+qbs). + +Run the following command, replacing the version and SHA-256 sum: +``` +$ brew bump-formula-pr --strict qbs \ + --url https://download.qt.io/official_releases/qbs/1.x.y/qbs-src-1.x.y.tar.gz \ + --sha256=<SHA-256> +``` + +The above can also be done manually by editing the formula source code and creating a pull request. + +Some useful commands to build/test formula locally: +``` +$ brew install --build-from-source qbs +$ brew test qbs +$ brew audit --strict qbs +``` + +## Mac Ports + +Fork [macports-ports](https://github.com/macports/macports-ports) to your GitHub account +and clone the repo. + +Run the following command in the repo dir (must be done only once): +``` +$ sudo /opt/local/bin/portindex +``` + +Make sure that /opt/local/etc/macports/sources.conf contains a line like +file:///Users/abbapoh/dev/macports-ports [default] +pointing to your local repo. + +Get the qbs-src-1.x.y.tar.gz source package from the archive or from the +[Qt Website](https://download.qt.io/official_releases/qbs/) and make note of the file size: +``` +$ ls -l qbs-src-1.x.y.tar.gz +``` +Get checksums: +``` +$ openssl sha256 qbs-src-1.x.y.tar.gz +$ openssl rmd160 qbs-src-1.x.y.tar.gz +``` + +Update the devel/qbs/Portfile file with the new url, filesize and checksums. + +Build the package: +``` +$ /opt/local/bin/port lint qbs +$ sudo /opt/local/bin/port -vst install qbs +$ /opt/local/bin/qbs --version # Check that this reports the right version +``` +Commit. Push. Create merge request. @@ -1 +1 @@ -1.23.0 +2.3.0 diff --git a/changelogs/changes-1.14.0.md b/changelogs/changes-1.14.0.md index 860578a15..d8a21fe51 100644 --- a/changelogs/changes-1.14.0.md +++ b/changelogs/changes-1.14.0.md @@ -24,12 +24,12 @@ * Various fixes and improvements in the Debian Docker image; updated to to Qt 5.11.3. # Contributors -* BogDan Vatra <bogdan@kde.org> -* Christian Kandeler <christian.kandeler@qt.io> -* Christian Stenger <christian.stenger@qt.io> -* Davide Pesavento <pesa@gentoo.org> -* Denis Shienkov <denis.shienkov@gmail.com> -* hjk <hjk@qt.io> -* Ivan Komissarov <ABBAPOH@gmail.com> -* Joerg Bornemann <joerg.bornemann@qt.io> -* Richard Weickelt <richard@weickelt.de> +* BogDan Vatra +* Christian Kandeler +* Christian Stenger +* Davide Pesavento +* Denis Shienkov +* hjk +* Ivan Komissarov +* Joerg Bornemann +* Richard Weickelt diff --git a/changelogs/changes-1.15.0.md b/changelogs/changes-1.15.0.md index cc95011a8..390fe99dc 100644 --- a/changelogs/changes-1.15.0.md +++ b/changelogs/changes-1.15.0.md @@ -35,10 +35,10 @@ Windows. # Contributors -* Alberto Mardegan <mardy@users.sourceforge.net> -* Christian Kandeler <christian.kandeler@qt.io> -* Denis Shienkov <denis.shienkov@gmail.com> -* Ivan Komissarov <ABBAPOH@gmail.com> -* Jochen Ulrich <jochenulrich@t-online.de> -* Joerg Bornemann <joerg.bornemann@qt.io> -* Richard Weickelt <richard@weickelt.de> +* Alberto Mardegan +* Christian Kandeler +* Denis Shienkov +* Ivan Komissarov +* Jochen Ulrich +* Joerg Bornemann +* Richard Weickelt diff --git a/changelogs/changes-1.23.0.md b/changelogs/changes-1.23.0.md new file mode 100644 index 000000000..e8234e3fe --- /dev/null +++ b/changelogs/changes-1.23.0.md @@ -0,0 +1,16 @@ +# C/C++ Support +* Added new module Sanitizers.address for simple cross-platform ASan configuration. + +# Qt Support +* Add the now-required /permissive- flag for MSVC automatically. + +# Android Support +* Support use of cmdline-tools in addition to SDK tools. +* Consider .jar files also in the product itself, not just in dependencies. + +# Contributors +* Christian Kandeler +* Dmitry Shachnev +* Ivan Komissarov +* Max Bespalov +* Orgad Shaneh diff --git a/changelogs/changes-1.23.1.md b/changelogs/changes-1.23.1.md new file mode 100644 index 000000000..65536bfeb --- /dev/null +++ b/changelogs/changes-1.23.1.md @@ -0,0 +1,19 @@ +# C/C++ Support +* Added support for c17 and c2x values in cpp.cLanguageVersion. +* Added support for cpp.cLanguageVersion for the MSVC toolchain. +* Fix passing linker scripts to iar and keil toolchains (QBS-1704). + +# Qt Support +* Adapted to new location of qscxmlc in Qt 6.3. +* Adapted to new location of qhelpgenerator in Qt 6.3. +* Fixed setting up Qt 6.3 with qbspkgconfig. +* Added QtScript module to the source tarballs (QBS-1703). + +# Other modules +* Fixed protobuf linking on macOS 11. +* Fixed handling empty variables in qbspkgconfig (QBS-1702) + +# Contributors +* Christian Kandeler +* Ivan Komissarov +* Orgad Shaneh diff --git a/changelogs/changes-1.23.2.md b/changelogs/changes-1.23.2.md new file mode 100644 index 000000000..56e8a7862 --- /dev/null +++ b/changelogs/changes-1.23.2.md @@ -0,0 +1,10 @@ +# Qt Support +* Set _ENABLE_EXTENDED_ALIGNED_STORAGE for MSVC + +# Documentation +* Fix installation with cmake + +# Contributors +* Christian Kandeler +* Ivan Komissarov +* Marius Gripsgard diff --git a/changelogs/changes-1.24.0.md b/changelogs/changes-1.24.0.md new file mode 100644 index 000000000..45250ec98 --- /dev/null +++ b/changelogs/changes-1.24.0.md @@ -0,0 +1,12 @@ +# General +* Users can now control if and when warnings for deprecated properties are emitted. +* Added FileInfo.executableSuffix(). + +# Qt Support +* Minimum windows version is now 10.0, like for cmake and qmake. + +# Contributors +* Christian Kandeler +* Ivan Komissarov +* Petr Mikhalicin +* Raphael Cotty diff --git a/changelogs/changes-1.24.1.md b/changelogs/changes-1.24.1.md new file mode 100644 index 000000000..acbf25fa3 --- /dev/null +++ b/changelogs/changes-1.24.1.md @@ -0,0 +1,9 @@ +# C/C++ Support +Fix macros and include paths retrieval for IAR + +# Build System +Add fix for cmake >= 3.18 + +# Contributors +* Denis Shienkov +* Eike Ziller diff --git a/changelogs/changes-2.0.0.md b/changelogs/changes-2.0.0.md new file mode 100644 index 000000000..14cacb761 --- /dev/null +++ b/changelogs/changes-2.0.0.md @@ -0,0 +1,22 @@ +# General +* Switched JavaScript engine from QtScript to QuickJS +* Removed the long-deprecated loadFile() and loadExtension() functions +* Removed the qmake project files + +# Qt support +* Adapt to androiddeployqt on Windows no longer accepting tool paths without suffix in 6.4 (QTBUG-111558) + +# BareMetal support +* Added support for HPPA architectures + +# Other modules +* Renamed "name" to "appName" in the freedesktop module to prevent clash with built-in property + +# Infrastructure +* Added USBSAN CI job + +# Contributors +* Christian Kandeler +* Ivan Komissarov +* Orgad Shaneh +* Pino Toscano diff --git a/changelogs/changes-2.0.1.md b/changelogs/changes-2.0.1.md new file mode 100644 index 000000000..7f28b3e55 --- /dev/null +++ b/changelogs/changes-2.0.1.md @@ -0,0 +1,27 @@ +# General +* Fixed crash when importing missing JavaScript file (QBS-1730). + +# C/C++ Support +* Fixed building applications with mingw toolchain and Qt6 (QBS-1724). + +# Apple Support +* Added support for Xcode 14.3. +* Fixed codesigning on macOS (QBS-1722). +* Fixed detecting Xcode via xcode-select tool. + +# Qt Support +* Fixed support for Qt 6.3 on iOS. +* Fixed install-qt.sh to properly support Qt for iOS. +* Do not setup Qt in qbspkgconfig when cross compiling (QBS-1717). + +# Build System +* Fixed qbsbuildconfig module. +* Fixed build with Qt6.5. +* Updated CI to test via Qt 6.5 on macOS and Windows. +* Updated CI to test via Xcode 14.2 on macOS. + +# Contributors +* Björn Schäpers +* Christian Kandeler +* Ivan Komissarov +* Kai Dohmen diff --git a/changelogs/changes-2.0.2.md b/changelogs/changes-2.0.2.md new file mode 100644 index 000000000..cec220db4 --- /dev/null +++ b/changelogs/changes-2.0.2.md @@ -0,0 +1,12 @@ +# General +* Fixed handling of non-string exceptions (QBS-1734). +* Fixed Utilities.versionCompare() for four-segment versions numbers (QBS-1733). + +# Qt Support +* Added special handling for unclean paths in prl files (QBS-1732). +* Fixed support for static plugins and Qt >= 6.5 (QBS-1732). + +# Contributors +* Christian Kandeler +* Ivan Komissarov +* Leon Buckel diff --git a/changelogs/changes-2.1.0.md b/changelogs/changes-2.1.0.md new file mode 100644 index 000000000..bec55631b --- /dev/null +++ b/changelogs/changes-2.1.0.md @@ -0,0 +1,28 @@ +# General +* Improved speed and correctness of project resolving. +* Fixed possible segmentation fault when quitting a session. +* Fixed regression in BinaryFile (QBS-1740). +* Added possibility to import and export Qbs settings in the JSON format (QBS-1685). + +# Modules +* Dependencies are no longer merged by default in the qbspkgconfig module provider (QBS-1710). +* Protobuf modules now export the desired c++ version (c++17 on macOS, c++14 otherwise). + +# Apple Support +* Updated dmgbuild to the upstream. + +# Documentation +* Added documentation for the path, filePath, product and project variables. +* Added sample codesign settings to the Cocoa Touch Application example. + +# Build System +* Updated Qt static Docker image to Qt 6.5.0 and Qbs 1.24. + +# Contributors +* Andrey Filipenkov +* Christian Kandeler +* Denis Shienkov +* Ivan Komissarov +* Marc Mutz +* Raphael Cotty +* Thiemo van Engelen diff --git a/changelogs/changes-2.1.1.md b/changelogs/changes-2.1.1.md new file mode 100644 index 000000000..d0cdaad4f --- /dev/null +++ b/changelogs/changes-2.1.1.md @@ -0,0 +1,8 @@ +# General +* Fixed Probe lookup in multiplexed products. +* Fixed excessively slow module merging in some circumstances. +* Fixed building QuickJS on x86 systems. + +# Contributors +* Christian Kandeler +* Ivan Komissarov diff --git a/changelogs/changes-2.1.2.md b/changelogs/changes-2.1.2.md new file mode 100644 index 000000000..b83ecdbf5 --- /dev/null +++ b/changelogs/changes-2.1.2.md @@ -0,0 +1,19 @@ +# General +* Fixed handling JS floating-point values for x86. +* Fixed scope pollution and potential crash when assigning to provider properties (QBS-1747). +* Fixed potential access to freed JSValues (QBS-1751). + +# Qt +* Fixed building against Qt with "profiling" build variant (QBS-1758). + +# Apple +* Fixed bundle module with Xcode-less profiles. +* Fixed ApplicationExtension with Xcode-less profiles. + +# Infrastructure +* Added CI job to be able to test XCode-less profiles on macOS. + +# Contributors +* Christian Kandeler +* Dmitry Shachnev +* Ivan Komissarov diff --git a/changelogs/changes-2.2.0.md b/changelogs/changes-2.2.0.md new file mode 100644 index 000000000..ffa867f0a --- /dev/null +++ b/changelogs/changes-2.2.0.md @@ -0,0 +1,36 @@ +# General +* Improved speed of project resolving by employing multiple CPU cores, if available. +* Improved speed of probes execution on macOS. +* Object and array (var and varList) properties are now immutable in Probe items. + +# Language +* Modules can now contain `Parameters` items. +* ModuleProviders can now contain `PropertyOptions` items. + +# C/C++ support +* Allow `"mold"` as value for `cpp.linkerVariant`. +* The systemIncludePaths property is now handled correctly for clang-cl. + +# Apple support +* Updated dmgbuild tool. This fixes bug that additional licenses are not shown in + the combobox in the resulting DMG image. + +# Qt support +* Only create qbs modules for those Qt modules that products actually need. +* Users can now opt out of using RPATH when linking on Linux. + +# Other +* Protobuf module now requires pkg-config or built-in runtime. +* Protobuf module now requires C++17 on all platforms. +* Capnproto module: the outputDir property is now mutable. +* Added support for Groups to the VisualStudio generator. +* pkgconfig module provider: mergeDependencies property is deprecated. + +# Contributors +* Christian Kandeler +* Dmitrii Meshkov +* Ivan Komissarov +* Nick Karg +* Serhii Olendarenko +* Thiemo van Engelen +* Thorbjørn Lindeijer diff --git a/changelogs/changes-2.2.1.md b/changelogs/changes-2.2.1.md new file mode 100644 index 000000000..323da791a --- /dev/null +++ b/changelogs/changes-2.2.1.md @@ -0,0 +1,12 @@ +# Language +* Fixed JavaScript Date() constructor on Windows (QBS-1768). + +# C/C++ support +* Worked around crash in cl.exe when retrieving built-in defines (QBS-1743). + +# CI +* Fixed building release packages. + +# Contributors +* Christian Kandeler +* Ivan Komissarov diff --git a/cmake/QbsBuildConfig.cmake b/cmake/QbsBuildConfig.cmake index a29f550f2..e0a7d76d7 100644 --- a/cmake/QbsBuildConfig.cmake +++ b/cmake/QbsBuildConfig.cmake @@ -2,7 +2,9 @@ option(WITH_TESTS "Build Tests" ON) option(WITH_UNIT_TESTS "Build Unit Tests" OFF) option(INSTALL_PUBLIC_HEADERS "Whether to install public headers" ON) option(QBS_ENABLE_RPATH "Whether to enable RPATH" ON) -option(QBS_USE_BUNDLED_QT_SCRIPT "Whether to use bundled QtScript module" OFF) + +include(CMakeDependentOption) +cmake_dependent_option(QBS_QUICKJS_LEAK_CHECK "Whether to check for quickjs leaks at the end" ON "CMAKE_BUILD_TYPE STREQUAL Debug" OFF) set(QBS_APP_INSTALL_DIR "bin" CACHE STRING "Relative install location for Qbs binaries.") # default paths @@ -24,6 +26,7 @@ set(QBS_PLUGINS_INSTALL_BASE "${QBS_LIBDIR_NAME}" CACHE STRING "Relative install set(QBS_RESOURCES_INSTALL_BASE "." CACHE STRING "Relative install location for Qbs resources.") set(QBS_HEADERS_INSTALL_DIR "include/qbs" CACHE STRING "Relative install location for Qbs headers.") set(QBS_DOC_INSTALL_DIR "${QBS_RESOURCES_INSTALL_BASE}/share/doc/qbs" CACHE STRING "Relative install location for Qbs documentation.") +set(QBS_DOC_HTML_DIR_NAME "html" CACHE STRING "The name of the dir with HTML files, appended to QBS_DOC_INSTALL_DIR.") set(QBS_PLUGINS_INSTALL_DIR "${QBS_PLUGINS_INSTALL_BASE}/qbs/plugins") set(QBS_RESOURCES_INSTALL_DIR "${QBS_RESOURCES_INSTALL_BASE}/share") @@ -82,6 +85,9 @@ set(DEFAULT_DEFINES "") if(WIN32) list(APPEND DEFAULT_DEFINES UNICODE _UNICODE _SCL_SECURE_NO_WARNINGS) endif() +if(WITH_TESTS) + list(APPEND DEFAULT_DEFINES QBS_WITH_TESTS) +endif() # CMake 3.10 doesn't have list(TRANSFORM) function(list_transform_prepend var prefix) @@ -172,6 +178,9 @@ function(add_qbs_library target_name) RUNTIME DESTINATION ${QBS_DLL_INSTALL_DIR} ) endif() + if(MSVC) + target_compile_options(${target_name} PUBLIC /EHsc) + endif() endfunction() function(add_qbs_plugin target_name) diff --git a/cmake/QbsDocumentation.cmake b/cmake/QbsDocumentation.cmake index a1ce44057..bdcec664f 100644 --- a/cmake/QbsDocumentation.cmake +++ b/cmake/QbsDocumentation.cmake @@ -1,6 +1,7 @@ # Options: option(QBS_INSTALL_HTML_DOCS "Whether to install Qbs HTML Documentation" OFF) option(QBS_INSTALL_QCH_DOCS "Whether to install Qbs QCH Documentation" OFF) +option(QBS_INSTALL_MAN_PAGE "Whether to install Qbs man page" OFF) # Get information on directories from qmake # as this is not yet exported by cmake. @@ -39,23 +40,6 @@ function(qt_query_qmake) endforeach() endfunction() -# Find programs: -function(_qbs_doc_find_program result_var) - if (NOT TARGET Qt${QT_VERSION_MAJOR}::qmake) - message(FATAL_ERROR "QDoc is only available in Qt projects") - endif() - - get_target_property(_qmake_binary Qt${QT_VERSION_MAJOR}::qmake IMPORTED_LOCATION) - get_filename_component(_qmake_dir "${_qmake_binary}" DIRECTORY) - find_program("_prg_${result_var}" ${ARGN} HINTS "${_qmake_dir}") - if ("_prg_${result_var}" STREQUAL "_prg_${result_var}-NOTFOUND") - set("_prg_${result_var}" "${result_var}-NOTFOUND") - message(WARNING "Could not find binary for ${result_var}") - endif() - - set(${result_var} "${_prg_${result_var}}" PARENT_SCOPE) -endfunction() - function(_qbs_setup_doc_targets) # Set up important targets: if (NOT TARGET qbs_html_docs) @@ -69,6 +53,11 @@ function(_qbs_setup_doc_targets) BuildQbsDocumentation ALL COMMENT "Build Qbs documentation") add_dependencies(BuildQbsDocumentation qbs_html_docs qbs_qch_docs) endif() + if (NOT TARGET qbs_docs) + add_custom_target( + qbs_docs ALL COMMENT "Build Qbs documentation") + add_dependencies(qbs_docs BuildQbsDocumentation) + endif() endfunction() function(_find_python_module module) @@ -108,6 +97,8 @@ function(_qbs_setup_qdoc_targets _qdocconf_file _retval) list(APPEND _env "${_export}=${${_export}}") endforeach() + get_target_property(_qdoc Qt${QT_VERSION_MAJOR}::qdoc IMPORTED_LOCATION) + set(_full_qdoc_command "${_qdoc}") if (_env) set(_full_qdoc_command "${CMAKE_COMMAND}" "-E" "env" ${_env} "${_qdoc}") @@ -119,8 +110,6 @@ function(_qbs_setup_qdoc_targets _qdocconf_file _retval) get_filename_component(_target "${_qdocconf_file}" NAME_WE) - # message(${_target}) - # set(_html_outputdir "${_arg_HTML_DIR}/${_target}${_arg_POSTFIX}") set(_html_outputdir "${_arg_HTML_DIR}") file(MAKE_DIRECTORY "${_html_outputdir}") @@ -183,6 +172,13 @@ function(_qbs_setup_qdoc_targets _qdocconf_file _retval) add_custom_target(${_html_target} DEPENDS "${_fixed_html_artifact}") add_dependencies(qbs_html_docs "${_html_target}") + # artifacts might be required for QCH-only installation, so we build them + # always, and skip HTML docs installation here + if (QBS_INSTALL_HTML_DOCS) + install(DIRECTORY "${_html_outputdir}" DESTINATION "${_arg_INSTALL_DIR}" + COMPONENT qbs_docs) + endif() + set("${_retval}" "${_html_outputdir}" PARENT_SCOPE) endfunction() @@ -228,17 +224,14 @@ function(_qbs_setup_qhelpgenerator_targets _qdocconf_file _html_outputdir) add_dependencies(qbs_qch_docs "${_qch_target}") install(FILES "${_qch_outputdir}/${_target}.qch" DESTINATION "${_arg_INSTALL_DIR}" - COMPONENT qbs_qch_docs) - install(DIRECTORY "${_qch_outputdir}/html" DESTINATION "${_arg_INSTALL_DIR}" - COMPONENT qbs_html_docs) + COMPONENT qbs_docs) endfunction() # Helper functions: function(_qbs_qdoc_build_qdocconf_file _qdocconf_file) _qbs_setup_doc_targets() - _qbs_doc_find_program(_qdoc NAMES qdoc qdoc-qt5) - if (_qdoc STREQUAL "_prg__qdoc-NOTFOUND") + if (NOT TARGET Qt${QT_VERSION_MAJOR}::qdoc) message(WARNING "No qdoc binary found: No documentation targets were generated") return() endif() @@ -285,10 +278,11 @@ function(add_qbs_documentation qdocconf_file) set(SRCDIR "${Qbs_SOURCE_DIR}/doc") set(_qch_params) + # if QBS_INSTALL_QCH_DOCS is No, qch generation will be skipped entirely if (QBS_INSTALL_QCH_DOCS) set(_qch_params QCH QCH_DIR "${CMAKE_CURRENT_BINARY_DIR}") endif() - set(_qdoc_params HTML_DIR "${CMAKE_CURRENT_BINARY_DIR}/html") + set(_qdoc_params HTML_DIR "${CMAKE_CURRENT_BINARY_DIR}/${QBS_DOC_HTML_DIR_NAME}") list(APPEND _qdoc_params INSTALL_DIR "${QBS_DOC_INSTALL_DIR}") # Set up environment for qdoc: diff --git a/doc/CMakeLists.txt b/doc/CMakeLists.txt index 5b8ac7636..04ff12470 100644 --- a/doc/CMakeLists.txt +++ b/doc/CMakeLists.txt @@ -26,3 +26,5 @@ add_qbs_documentation( ${_DOC_IMAGES_SOURCES} ${_DOC_TARGETS_SOURCES} ) + +add_subdirectory(man) diff --git a/doc/appendix/json-api.qdoc b/doc/appendix/json-api.qdoc index 68bb644cf..02ec6aef3 100644 --- a/doc/appendix/json-api.qdoc +++ b/doc/appendix/json-api.qdoc @@ -94,6 +94,7 @@ \header \li Property \li Type \row \li api-level \li int \row \li api-compat-level \li int + \row \li lsp-socket \li string \endtable The value of \c api-level is increased whenever the API is extended, for instance @@ -108,6 +109,10 @@ The value of \c api-compat-level is always less than or equal to the value of \c api-level. + The value of \c lsp-socket is a path to a local domain socket (on Unix) or + a named pipe (on Windows). It provides a server implementing the + \l{https://microsoft.github.io/language-server-protocol}{Language Server Protocol}. + \section1 Resolving a Project To instruct \QBS to load a project from disk, a request of type @@ -117,6 +122,7 @@ \row \li build-root \li \l FilePath \li yes \row \li configuration-name \li string \li no \row \li data-mode \li \l DataMode \li no + \row \li deprecation-warning-mode \li string \li no \row \li dry-run \li bool \li no \row \li environment \li \l Environment \li no \row \li error-handling-mode \li string \li no @@ -124,6 +130,7 @@ \row \li force-probe-execution \li bool \li no \row \li log-time \li bool \li no \row \li log-level \li \l LogLevel \li no + \row \li max-job-count \li int \li no \row \li module-properties \li list of strings \li no \row \li overridden-properties \li object \li no \row \li project-file-path \li FilePath \li if resolving from scratch diff --git a/doc/codeattributions.qdoc b/doc/codeattributions.qdoc index cdd58b159..092f7794f 100644 --- a/doc/codeattributions.qdoc +++ b/doc/codeattributions.qdoc @@ -180,7 +180,7 @@ modification, are permitted provided that the following conditions are met: notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of biplist nor the names of its contributors may be - used to endorse or promote products derived from this software without + used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" @@ -239,3 +239,50 @@ Copyright (c) 2008 Roberto Raggi // THE SOFTWARE. \endcode */ + +/*! + +\ingroup attributions-libs +\ingroup attributions-qbs +\page qbs-attribution-quickjs.html attribution +\target quickjs + +\title QuickJS +\brief MIT License + +The JavaScript engine used to evaluate all JavaScript code in \QBS project files. + +The sources can be found in src/shared/quickjs. + +\badcode +Copyright (c) 2017-2021 Fabrice Bellard +Copyright (c) 2017-2021 Charlie Gordon +\endcode + +\l{https://spdx.org/licenses/MIT.html}{MIT License}. + +\badcode +QuickJS Javascript Engine + +Copyright (c) 2017-2021 Fabrice Bellard +Copyright (c) 2017-2021 Charlie Gordon + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +\endcode +*/ diff --git a/doc/doc.pri b/doc/doc.pri deleted file mode 100644 index cd05a9450..000000000 --- a/doc/doc.pri +++ /dev/null @@ -1,18 +0,0 @@ -include(../src/install_prefix.pri) - -include(doc_shared.pri) - -DOC_OUTDIR_POSTFIX = /html -DOC_HTML_INSTALLDIR = $$QBS_INSTALL_PREFIX/share/doc/qbs -DOC_QCH_OUTDIR = $$OUT_PWD/doc -DOC_QCH_INSTALLDIR = $$QBS_INSTALL_PREFIX/share/doc/qbs - -include(doc_targets.pri) - -fixnavi.commands = \ - cd $$shell_path($$PWD) && \ - perl fixnavi.pl -Dqcmanual -Dqtquick \ - qbs.qdoc -QMAKE_EXTRA_TARGETS += fixnavi - -include(man/man.pri) diff --git a/doc/doc_shared.pri b/doc/doc_shared.pri deleted file mode 100644 index 3e4eccf48..000000000 --- a/doc/doc_shared.pri +++ /dev/null @@ -1,14 +0,0 @@ -include(../qbs_version.pri) - -qbsdoc_version.name = QBS_VERSION -qbsdoc_version.value = $$QBS_VERSION -qbsdoc_versiontag.name = QBS_VERSION_TAG -qbsdoc_versiontag.value = $$replace(QBS_VERSION, "[-.]", ) -qbsdoc_qtdocs.name = QT_INSTALL_DOCS -qbsdoc_qtdocs.value = $$[QT_INSTALL_DOCS/src] -QDOC_ENV += qbsdoc_version qbsdoc_versiontag qbsdoc_qtdocs - -build_online_docs: \ - DOC_FILES += $$PWD/qbs-online.qdocconf -else: \ - DOC_FILES += $$PWD/qbs.qdocconf diff --git a/doc/doc_targets.pri b/doc/doc_targets.pri deleted file mode 100644 index 0636be2b5..000000000 --- a/doc/doc_targets.pri +++ /dev/null @@ -1,86 +0,0 @@ -# Creates targets for building documentation -# (adapted from qt_docs.prf) -# -# Usage: Define variables (details below) and include this pri file afterwards. -# -# QDOC_ENV - environment variables to set for the qdoc call (see example below) -# DOC_INDEX_PATHS - list of paths where qdoc should search for index files of dependent -# modules (Qt index path is included by default) -# DOC_FILES - list of qdocconf files -# DOC_OUTDIR_POSTFIX - html is generated in $$OUT_PWD/<qdocconf_name>$$DOC_OUTDIR_POSTFIX -# DOC_HTML_INSTALLDIR - path were to install the directory of html files -# DOC_QCH_OUTDIR - path where to generated the qch files -# DOC_QCH_INSTALLDIR - path where to install the qch files -# DOC_TARGET_PREFIX - prefix for generated target names -# -# Example for QDOC_ENV: -# ver.name = VERSION -# ver.value = 1.0.2 -# foo.name = FOO -# foo.value = foo -# QDOC_ENV = ver foo - -isEmpty(DOC_FILES): error("Set DOC_FILES before including doc_targets.pri") -isEmpty(DOC_HTML_INSTALLDIR): error("Set DOC_HTML_INSTALLDIR before including doc_targets.pri") -isEmpty(DOC_QCH_OUTDIR): error("Set DOC_QCH_OUTDIR before including doc_targets.pri") -isEmpty(DOC_QCH_INSTALLDIR): error("Set DOC_QCH_INSTALLDIR before including doc_targets.pri") - -QT_TOOL_ENV = $$QDOC_ENV -qtPrepareTool(QDOC, qdoc) -QT_TOOL_ENV = - -!build_online_docs: qtPrepareTool(QHELPGENERATOR, qhelpgenerator) - -DOCS_BASE_OUTDIR = $$OUT_PWD/doc -DOC_INDEXES += -indexdir $$shell_quote($$[QT_INSTALL_DOCS]) -for (index_path, DOC_INDEX_PATHS): \ - DOC_INDEXES += -indexdir $$shell_quote($$index_path) - -DTP = $$DOC_TARGET_PREFIX -for (doc_file, DOC_FILES) { - !exists($$doc_file): error("Cannot find documentation specification file $$doc_file") - DOC_TARGET = $$replace(doc_file, ^(.*/)?(.*)\\.qdocconf$, \\2) - DOC_TARGETDIR = $$DOC_TARGET - DOC_OUTPUTDIR = $${DOCS_BASE_OUTDIR}/$${DOC_TARGETDIR}$${DOC_OUTDIR_POSTFIX} - - $${DTP}html_docs_$${DOC_TARGET}.commands = $$QDOC -outputdir $$shell_quote($$DOC_OUTPUTDIR) $$doc_file $$DOC_INDEXES - QMAKE_EXTRA_TARGETS += $${DTP}html_docs_$${DOC_TARGET} - - !isEmpty($${DTP}html_docs.commands): $${DTP}html_docs.commands += && - $${DTP}html_docs.commands += $$eval($${DTP}html_docs_$${DOC_TARGET}.commands) - - $${DTP}inst_html_docs.files += $$DOC_OUTPUTDIR - - !build_online_docs { - $${DTP}qch_docs_$${DOC_TARGET}.commands = $$QHELPGENERATOR $$shell_quote($$DOC_OUTPUTDIR/$${DOC_TARGET}.qhp) -o $$shell_quote($$DOC_QCH_OUTDIR/$${DOC_TARGET}.qch) - $${DTP}qch_docs_$${DOC_TARGET}.depends = $${DTP}html_docs_$${DOC_TARGET} - QMAKE_EXTRA_TARGETS += $${DTP}qch_docs_$${DOC_TARGET} - - !isEmpty($${DTP}qch_docs.commands): $${DTP}qch_docs.commands += && - $${DTP}qch_docs.commands += $$eval($${DTP}qch_docs_$${DOC_TARGET}.commands) - - $${DTP}inst_qch_docs.files += $$DOC_QCH_OUTDIR/$${DOC_TARGET}.qch - } -} - -!build_online_docs { - $${DTP}qch_docs.depends = $${DTP}html_docs - $${DTP}inst_qch_docs.path = $$DOC_QCH_INSTALLDIR - $${DTP}inst_qch_docs.CONFIG += no_check_exist no_default_install no_build - install_$${DTP}docs.depends = install_$${DTP}inst_qch_docs - $${DTP}docs.depends = $${DTP}qch_docs - INSTALLS += $${DTP}inst_qch_docs - QMAKE_EXTRA_TARGETS += $${DTP}qch_docs install_$${DTP}docs -} else { - $${DTP}docs.depends = $${DTP}html_docs -} - -$${DTP}inst_html_docs.path = $$DOC_HTML_INSTALLDIR -$${DTP}inst_html_docs.CONFIG += no_check_exist no_default_install directory -INSTALLS += $${DTP}inst_html_docs -install_$${DTP}docs.depends += install_$${DTP}inst_html_docs - -QMAKE_EXTRA_TARGETS += $${DTP}html_docs $${DTP}docs - -unset(DTP) - diff --git a/doc/howtos.qdoc b/doc/howtos.qdoc index e21a2588d..268bee7c0 100644 --- a/doc/howtos.qdoc +++ b/doc/howtos.qdoc @@ -390,19 +390,19 @@ Depends { name: "cpp" } cpp.includePaths: ["/somewhere/include"] Properties { - condition: qbs.targetOS.contains("android") + condition: qbs.targetOS.includes("android") cpp.dynamicLibraries: ["/somewhere/android/" + Android.ndk.abi + "/lib1.so"] } Properties { - condition: qbs.targetOS.contains("macos") + condition: qbs.targetOS.includes("macos") cpp.dynamicLibraries: ["/somewhere/macos/lib1.dylib"] } Properties { - condition: qbs.targetOS.contains("windows") && qbs.architecture === "x86" + condition: qbs.targetOS.includes("windows") && qbs.architecture === "x86" cpp.dynamicLibraries: ["/somewhere/windows_x86/lib1.lib"] } Properties { - condition: qbs.targetOS.contains("windows") && qbs.architecture === "x86_64" + condition: qbs.targetOS.includes("windows") && qbs.architecture === "x86_64" cpp.dynamicLibraries: ["/somewhere/windows_x86_64/lib1.lib"] } } @@ -532,7 +532,7 @@ CppApplication { // ... - readonly property bool isMsvc: qbs.toolchain.contains("msvc") + readonly property bool isMsvc: qbs.toolchain.includes("msvc") cpp.commonCompilerFlags: isMsvc ? "/wd4996" : "-Wno-deprecated-declarations" } @@ -755,7 +755,7 @@ \c qbs.targetOS has the wrong value: \code - readonly property bool unix: qbs.targetOS.contains("unix") + readonly property bool unix: qbs.targetOS.includes("unix") \endcode To find out the value of \c qbs.targetOS, use \c {console.info()}: @@ -763,7 +763,7 @@ \code readonly property bool unix: { console.info("qbs.targetOS: " + qbs.targetOS) - return qbs.targetOS.contains("unix") + return qbs.targetOS.includes("unix") } \endcode @@ -771,9 +771,9 @@ be useful if the property contains invalid or unsupported value: \code readonly property bool unix: { - if (qbs.targetOS.contains("darwin")) + if (qbs.targetOS.includes("darwin")) throw "Apple platforms are not supported"; - return qbs.targetOS.contains("unix") + return qbs.targetOS.includes("unix") } \endcode diff --git a/doc/man/CMakeLists.txt b/doc/man/CMakeLists.txt new file mode 100644 index 000000000..00d003d7f --- /dev/null +++ b/doc/man/CMakeLists.txt @@ -0,0 +1,3 @@ +if (QBS_INSTALL_MAN_PAGE) + install(FILES qbs.1 DESTINATION ${QBS_RESOURCES_INSTALL_DIR}/man/man1 COMPONENT qbs_docs) +endif() diff --git a/doc/man/man.pri b/doc/man/man.pri deleted file mode 100644 index 2e29f9112..000000000 --- a/doc/man/man.pri +++ /dev/null @@ -1,5 +0,0 @@ -qbs_no_man_install: return() - -man.files = $$PWD/qbs.1 -man.path = $${QBS_INSTALL_PREFIX}/share/man/man1 -INSTALLS += man diff --git a/doc/qbs.qdoc b/doc/qbs.qdoc index 7fcc7e68b..9ea88763e 100644 --- a/doc/qbs.qdoc +++ b/doc/qbs.qdoc @@ -67,6 +67,7 @@ \li \l{Generators} \li \l{Multiplexing} \li \l{Custom Modules and Items} + \li \l{Special Property Values} \li \l{Module Providers} \endlist \li \l{How-tos} @@ -461,7 +462,7 @@ To build \QBS from the source, you need: \list - \li Qt 5.14, or later + \li Qt 5.15, or later \li Windows: MinGW with GCC 4.9 or Microsoft Visual Studio 2015, or later \li Linux: GCC 4.9, or later, or Clang 3.9.0, or later @@ -556,7 +557,6 @@ \row \li qbs_no_dev_install \li Exclude header files from installation, that is, perform a non-developer build. \row \li qbs_no_man_install \li Exclude the man page from installation. - \row \li qbs_use_bundled_qtscript \li Use the bundled QtScript library. \endtable In addition, you can set the \c QBS_SYSTEM_SETTINGS_DIR environment variable @@ -643,11 +643,6 @@ \li Holds whether the Qt libraries that \QBS depends on will be bundled with \QBS during the \c install step. This option is only implemented on macOS. \row - \li useBundledQtScript - \li \c false - \li Use the bundled QtScript module instead of the one shipped with Qt. In that case, - QtScript should be checked out as a git submodule. - \row \li libDirName \li \c "lib" \li Directory name used by \c libInstallDir and \c importLibInstallDir properties. @@ -1067,6 +1062,7 @@ \li \l{Generators} \li \l{Multiplexing} \li \l{Custom Modules and Items} + \li \l{Special Property Values} \li \l{Module Providers} \endlist @@ -1213,27 +1209,17 @@ } \endcode - A module can implicitly depend on other modules. For example, the - \l{Qt.core} module depends on the \l{cpp} module. However, to set the - properties of a module, you must make the dependency explicit. + A module can depend on other modules. For example, the + \l{Qt.core} module depends on the \l{cpp} module. The module dependencies are transitive, + i.e. in a Product, all dependent modules are accessible: \code - // THIS DOES NOT WORK Application { name: "helloworld" files: ["main.cpp"] Depends { name: "Qt.core" } - cpp.optimization: "ludicrousSpeed" - // ERROR! We do not know about "cpp" here, - // though "Qt.core" depends on "cpp". - } - - // THIS WORKS - Application { - name: "helloworld" - files: ["main.cpp"] - Depends { name: "Qt.core" } - Depends { name: "cpp" } + // the "cpp" module is available since + // "Qt.core" depends on "cpp". cpp.optimization: "ludicrousSpeed" } \endcode @@ -1263,7 +1249,7 @@ This is how you do it: \code Group { - condition: qbs.targetOS.contains("windows") + condition: qbs.targetOS.includes("windows") files: [ "harddiskdeleter_win.cpp", "blowupmonitor_win.cpp", @@ -1271,7 +1257,7 @@ ] } Group { - condition: qbs.targetOS.contains("linux") + condition: qbs.targetOS.includes("linux") files: [ "harddiskdeleter_linux.cpp", "blowupmonitor_linux.cpp", @@ -1287,8 +1273,6 @@ \list \li \l{qbs::}{buildVariant} that specifies the name of the build variant for the current build. - \li \l{qbs::}{hostOS} that is set by \QBS internally and specifies the - operating system \QBS is running on. \li \l{qbs::}{targetOS} that specifies the operating system you want to build the project for. \endlist @@ -1667,7 +1651,7 @@ /*! \previouspage multiplexing.html \page custom-modules.html - \nextpage module-providers.html + \nextpage special-property-values.html \title Custom Modules and Items @@ -1739,6 +1723,163 @@ /*! \previouspage custom-modules.html + \page special-property-values.html + \nextpage module-providers.html + + \title Special Property Values + + Depending on the context, \QBS provides the following special values for use in property + bindings and JavaScript code: + + \list + \li \l base + \li \l exportingProduct + \li \l filePath + \li \l importingProduct + \li \l original + \li \l outer + \li \l path + \li \l product + \li \l project + \endlist + + \section2 \c base + This value is useful when making use of inheritance. It stands for the value of the respective + property in the item one level up in the inheritance chain. For instance: + \code + Product { // defined in MyProduct.qbs + Depends { name: "mymodule" } + mymodule.someProperty: ["value1"] + } + ------ some other file ------ + MyProduct { + mymodule.someProperty: base.concat(["value2"]) // => ["value1", "value2"] + } + \endcode + + \section2 \c exportingProduct + Within an \l Export item, you can use the \c exportingProduct variable to refer to + the product which defines the Export item: + + \code + Product { + Export { + Depends { name: "cpp" } + cpp.includePaths: exportingProduct.sourceDirectory + } + } + \endcode + + \section2 \c filePath + + This value holds the full file path to the \c .qbs file it appears in. This property is + rarely used, but might be useful when debugging: + \code + Product { + property bool dummy: { + console.info("I'm located at " + filePath); + } + } + \endcode + + \section2 \c importingProduct + Within an \l Export item, you can use the \c importingProduct variable to refer to + the product that pulls in the resulting module: + + \code + Product { + Export { + Depends { name: "cpp" } + cpp.includePaths: importingProduct.buildDirectory + } + } + \endcode + Usually, you should use the \l product variable instead for consistency with \l Module items. + + \section2 \c original + On the right-hand side of a module property binding, this refers to the value of the property + in the module itself (possibly overridden from a profile). Use it to set a module property + conditionally: + \code + Module { // This is mymodule + property string aProperty: "z" + } + ---------- + Product { + Depends { name: "mymodule" } + Depends { name: "myothermodule" } + // "y" if myothermodule.anotherProperty is "x", "z" otherwise: + mymodule.aProperty: myothermodule.anotherProperty === "x" ? "y" : original + } + \endcode + + \section2 \c outer + This value is used in nested items, where it refers to the value of the respective property + in the surrounding item. It is only valid in \l{Group} and \l{Properties} items: + \code + Product { + Depends { name: "mymodule" } + mymodule.someProperty: ["value1"] + Group { + name: "special files" + files: ["somefile1", "somefile2"] + mymodule.someProperty: outer.concat(["value"]) // => ["value1", "value2"] + } + } + \endcode + + \section2 \c path + + This value holds the path to the folder where the \c .qbs file is located. Use it to e.g. add + the product's directory to file paths: + \code + Product { + Depends { name: "cpp" } + cpp.includePaths: path + } + \endcode + + \section2 \c product + + This value holds the properties of the product that contains the current item or pulls in the + current module: + \code + Module { + Rule { + Artifact { + fileTags: product.type + filePath: { + var result = input.fileName; + // module properties are available as well + if (product.qbs.buildVariant === "debug") + result = result + "_debug"; + result = result + ".out"; + return result; + } + } + } + } + \endcode + Within the \l Export item, same as \l importingProduct. + + \section2 \c project + This value holds the properties of the project that references the current item or pulls in the + current module: + \code + Project { + property bool enableProduct: true + Product { + name: "theProduct" + condition: project.enableProduct + } + } + \endcode + If the nearest project in the project tree does not have the desired property, \QBS looks it + up in the parent project, potentially all the way up to the top-level project. +*/ + +/*! + \previouspage special-property-values.html \page module-providers.html \nextpage howtos.html diff --git a/doc/reference/cli/builtin/cli-resolve.qdoc b/doc/reference/cli/builtin/cli-resolve.qdoc index 273ce8403..4569980bd 100644 --- a/doc/reference/cli/builtin/cli-resolve.qdoc +++ b/doc/reference/cli/builtin/cli-resolve.qdoc @@ -56,6 +56,7 @@ \include cli-options.qdocinc settings-dir \include cli-options.qdocinc show-progress \include cli-options.qdocinc no-fallback-module-provider + \include cli-options.qdocinc deprecation-warnings \section1 Parameters diff --git a/doc/reference/cli/cli-options.qdocinc b/doc/reference/cli/cli-options.qdocinc index db8eeae48..ecb8f5c3e 100644 --- a/doc/reference/cli/cli-options.qdocinc +++ b/doc/reference/cli/cli-options.qdocinc @@ -331,6 +331,27 @@ //! [config-ui-system] +//! [deprecation-warnings] + + \section2 \c {--deprecation-warnings <mode>} + + Uses the specified deprecation warning mode, which controls what to do when deprecated + items or properties are encountered in the project. By default, a warning is emitted + if the item or property is scheduled for removal in the next minor version of \QBS. + Warnings can also be switched on or off unconditionally, and it can be specified that + project resolving should abort if deprecated constructs are present. + + Possible values of \c <mode> are: + + \list + \li \c error + \li \c on + \li \c before-removal (default value) + \li \c off + \endlist + +//! [deprecation-warnings] + //! [log-level] \section2 \c {--log-level <level>} diff --git a/doc/reference/items/convenience/appleapplicationdiskimage.qdoc b/doc/reference/items/convenience/appleapplicationdiskimage.qdoc index 8db7bc4db..d1fa15052 100644 --- a/doc/reference/items/convenience/appleapplicationdiskimage.qdoc +++ b/doc/reference/items/convenience/appleapplicationdiskimage.qdoc @@ -77,10 +77,11 @@ The base directory from which artifacts installed into the disk image will be copied. This directory is always considered to be relative to \l{qbs::installRoot} - {qbs.installRoot}. + {qbs.installRoot}/\l{qbs::installPrefix} + {qbs.installPrefix}. For example, if the application Example.app exists at - \c{qbs.installRoot/Applications/Example.app}, and the value of this property - is \c{"/Applications"}, the application will be located at\c{/Example.app} + \c{qbs.installRoot/qbs.installPrefix/Applications/Example.app}, and the value of this property + is \c{"/Applications"}, the application will be located at \c{/Example.app} relative to the disk image root. Therefore, its full path when the disk image is mounted would be something like \c{/Volumes/Example-1.0/Example.app}. diff --git a/doc/reference/items/language/depends.qdoc b/doc/reference/items/language/depends.qdoc index 0adba1474..7cc270483 100644 --- a/doc/reference/items/language/depends.qdoc +++ b/doc/reference/items/language/depends.qdoc @@ -127,7 +127,19 @@ The \l required and \l profiles properties are ignored if this property is set. - Product types attached via Module::additionalProductTypes are not considered. + \note This property does not work recursively. Consider this example: + \code + Product { + name: "A" + type: "x" + Depends { productTypes: "someTag" } + } + Product { + name: "B" + Depends { productTypes: "x" } + } + \endcode + It is not guaranteed that \c A will show up in \c B's dependencies. \nodefaultvalue */ diff --git a/doc/reference/items/language/export.qdoc b/doc/reference/items/language/export.qdoc index 96abde2ed..e3648fc3e 100644 --- a/doc/reference/items/language/export.qdoc +++ b/doc/reference/items/language/export.qdoc @@ -109,7 +109,7 @@ project against it), you would write something like the following: \code Export { - Depends { name: cpp" } + Depends { name: "cpp" } cpp.includePaths: [exportingProduct.sourceDirectory] prefixMapping: [{ prefix: exportingProduct.sourceDirectory, diff --git a/doc/reference/items/language/group.qdoc b/doc/reference/items/language/group.qdoc index a1676676e..fa6784ca6 100644 --- a/doc/reference/items/language/group.qdoc +++ b/doc/reference/items/language/group.qdoc @@ -48,21 +48,21 @@ } Group { name: "Windows files" - condition: qbs.targetOS.contains("windows") + condition: qbs.targetOS.includes("windows") files: "myclass_win_impl.cpp" } Group { name: "Unix files" - condition: qbs.targetOS.contains("unix") + condition: qbs.targetOS.includes("unix") files: "unixhelper.cpp" Group { name: "Linux files" - condition: qbs.targetOS.contains("linux") + condition: qbs.targetOS.includes("linux") files: "myclass_linux_impl.cpp" } Group { name: "FreeBSD files" - condition: qbs.targetOS.contains("freebsd") + condition: qbs.targetOS.includes("freebsd") files: "myclass_freebsd_impl.cpp" } } @@ -74,10 +74,13 @@ } } \endcode - When specifying files, you can use the wildcards "*", "?" and "[]", which have their usual meaning. - By default, matching files are only picked up directly from the parent directory, but you can tell \QBS to - consider the whole directory tree. It is also possible to exclude certain files from the list. - The pattern ** used in a pathname expansion context will match all files and zero or more + When specifying files, you can use the wildcards \c "*", \c "?" and \c "[]", which + have their + \l{https://en.wikipedia.org/wiki/Wildcard_character#File_and_directory_patterns}{usual meaning} + as in Unix Shell. By default, matching files are only picked up directly from the + parent directory, but you can tell \QBS to consider the whole directory tree. + It is also possible to exclude certain files from the list. + The pattern \c "**" used in a pathname expansion context will match all files and zero or more directories and subdirectories. For example: \snippet reference/items/language/group.qbs 0 @@ -114,13 +117,15 @@ */ /*! - \qmlproperty list Group::files + \qmlproperty pathList Group::files The files in the group. Mutually exclusive with \l{fileTagsFilter}. Relative paths are resolved using the parent directory of the file that contains the Group item. However, if the \l{prefix} property is set to an absolute path, then that one becomes the base directory. + The values can contain wildcards. + \defaultvalue An empty list */ @@ -146,7 +151,7 @@ */ /*! - \qmlproperty list Group::fileTagsFilter + \qmlproperty stringList Group::fileTagsFilter List of \l{Artifact::fileTags}{artifact.fileTags} to match. Any properties set in this group will be applied to the product's artifacts whose file tags @@ -170,7 +175,7 @@ */ /*! - \qmlproperty list Group::fileTags + \qmlproperty stringList Group::fileTags A list of file tags to attach to the group's files. These can then be matched by a \l{Rule}{rule}. @@ -196,10 +201,13 @@ */ /*! - \qmlproperty list Group::excludeFiles + \qmlproperty pathList Group::excludeFiles + + A list of files that are \e subtracted from the \l{files} list. - A list of files that are \e subtracted from the files list. Useful when - using wildcards. + The values can contain wildcards. + + This property is ignored if \l{fileTagsFilter} is set. \defaultvalue An empty list */ diff --git a/doc/reference/items/language/module.qdoc b/doc/reference/items/language/module.qdoc index f630f60fd..48ad583c8 100644 --- a/doc/reference/items/language/module.qdoc +++ b/doc/reference/items/language/module.qdoc @@ -169,64 +169,15 @@ \qml Module { property bool featureEnabled: - (product.type.contains("application")) ? true : false + (product.type.includes("application")) ? true : false } \endqml - \section1 Special Property Values - - For every property defined in a module, \QBS provides the following special - built-in values: - - \list - \li \l base - \li \l original - \li \l outer - \endlist - - \section2 \c base - This value is useful when making use of inheritance. It stands for the value of the respective - property in the item one level up in the inheritance chain. For instance: - \code - Product { // defined in MyProduct.qbs - Depends { name: "mymodule" } - mymodule.someProperty: ["value1"] - } - ------ some other file ------ - MyProduct { - mymodule.someProperty: base.concat(["value2"]) // => ["value1", "value2"] - } - \endcode - - \section2 \c original - This is the value of the property in the module itself (possibly overridden from a profile or - the command line). Use it to set a module property conditionally: - \code - Module { // This is mymodule - property string aProperty: "z" - } - ---------- - Product { - Depends { name: "mymodule" } - Depends { name: "myothermodule" } - mymodule.aProperty: myothermodule.anotherProperty === "x" ? "y" : original // => "y" if myothermodule.anotherProperty is "x", "z" otherwise - \endcode - - \section2 \c outer - This value is used in nested items, where it refers to the value of the respective property - in the surrounding item. It is only valid in \l{Group} and \l{Properties} items: - \code - Product { - Depends { name: "mymodule" } - mymodule.someProperty: ["value1"] - Group { - name: "special files" - files: ["somefile1", "somefile2"] - mymodule.someProperty: outer.concat(["value"]) // => ["value1", "value2"] - } - } - \endcode + \section2 Special Property Values + For every property defined in a module, \QBS provides the special + \l{Special Property Values#original}{original} value containing the value of the property in + the module itself (possibly overridden from a profile). \section1 Dependency Parameters diff --git a/doc/reference/items/language/moduleprovider.qdoc b/doc/reference/items/language/moduleprovider.qdoc index 81a09a66b..09cabb39b 100644 --- a/doc/reference/items/language/moduleprovider.qdoc +++ b/doc/reference/items/language/moduleprovider.qdoc @@ -67,6 +67,19 @@ */ /*! + \qmlproperty bool ModuleProvider::isEager + + Holds whether provider is eager. + + Eager provider is executed only once and should create multiple modules at once when executed). + A non-eager provider is executed multiple times, one time for each module \QBS tries to + instantiate. + + \sa ModuleProvider::moduleName + \default true +*/ + +/*! \qmlproperty string ModuleProvider::name The name of the module provider. @@ -83,6 +96,17 @@ */ /*! + \qmlproperty string ModuleProvider::moduleName + + This property is set by QBS for non-eager providers and contains the name of the module + that is currently being instantiated by the provider. + + For eager providers, the value of this property is \c undefined. + + \sa ModuleProvider::isEager +*/ + +/*! \qmlproperty string ModuleProvider::outputBaseDir The path under which the new modules should be created when \l relativeSearchPaths diff --git a/doc/reference/items/language/parameters.qdoc b/doc/reference/items/language/parameters.qdoc index 325f1680e..fb7653528 100644 --- a/doc/reference/items/language/parameters.qdoc +++ b/doc/reference/items/language/parameters.qdoc @@ -32,10 +32,10 @@ \ingroup list-of-items \keyword QML.Parameters - \brief Defines default values for dependency parameters within Export items. + \brief Defines default values for dependency parameters within Module and Export items. The Parameters item defines default values for dependency parameters within - \l{Export} items. + \l{Module} and \l{Export} items. Example: \code diff --git a/doc/reference/items/language/properties.qdoc b/doc/reference/items/language/properties.qdoc index 6e235d927..3a71a035b 100644 --- a/doc/reference/items/language/properties.qdoc +++ b/doc/reference/items/language/properties.qdoc @@ -44,7 +44,7 @@ \code Product { Properties { - condition: qbs.targetOS.contains("windows") + condition: qbs.targetOS.includes("windows") cpp.defines: ["ON_WINDOWS"] cpp.includePaths: ["extraWindowsIncludes"] } @@ -57,12 +57,12 @@ \code Product { Properties { - condition: qbs.targetOS.contains("windows") + condition: qbs.targetOS.includes("windows") cpp.defines: ["ON_WINDOWS"] cpp.includePaths: ["myWindowsIncludes"] } Properties { - condition: qbs.targetOS.contains("linux") + condition: qbs.targetOS.includes("linux") cpp.defines: ["ON_LINUX"] cpp.includePaths: ["myLinuxIncludes"] } @@ -73,32 +73,32 @@ \code Product { cpp.defines: { - if (qbs.targetOS.contains("windows")) + if (qbs.targetOS.includes("windows")) return ["ON_WINDOWS"]; - if (qbs.targetOS.contains("linux")) + if (qbs.targetOS.includes("linux")) return ["ON_LINUX"]; return ["ON_UNKNOWN_PLATFORM"]; } cpp.includePaths: { - if (qbs.targetOS.contains("windows")) + if (qbs.targetOS.includes("windows")) return ["myWindowsIncludes"]; - if (qbs.targetOS.contains("linux")) + if (qbs.targetOS.includes("linux")) return ["myLinuxIncludes"]; return base; } } \endcode - In Properties items, one can access the \l{Module#outer}{outer value} of a + In Properties items, one can access the \l{Special Property Values#outer}{outer} value of a property. \code Product { Properties { - condition: qbs.targetOS.contains("windows") + condition: qbs.targetOS.includes("windows") cpp.defines: outer.concat("ON_WINDOWS") // === ["FOO", "ON_WINDOWS"] } Properties { - condition: qbs.targetOS.contains("linux") + condition: qbs.targetOS.includes("linux") cpp.defines: ["ON_LINUX"] // === ["ON_LINUX"] } cpp.defines: ["FOO"] diff --git a/doc/reference/items/probe/binary-probe.qdoc b/doc/reference/items/probe/binary-probe.qdoc index 7c8cd2f42..919d0b89e 100644 --- a/doc/reference/items/probe/binary-probe.qdoc +++ b/doc/reference/items/probe/binary-probe.qdoc @@ -50,6 +50,7 @@ For example, BinaryProbe can be used to search for a protobuf compiler executable as follows: \code + // Assuming module is called "myproto" import qbs.File import qbs.Probes @@ -70,10 +71,10 @@ Rule { // rule input/outputs here... - // run executable + // run executable for the module called "myproto": prepare: { var args = // initialize arguments... - var cmd = new Command(executableFilePath, args); + var cmd = new Command(input.myproto.executableFilePath, args); cmd.highlight = "codegen"; cmd.description = "generating protobuf files for " + input.fileName; return [cmd]; diff --git a/doc/reference/items/probe/pkgconfig-probe.qdoc b/doc/reference/items/probe/pkgconfig-probe.qdoc index 15435ede4..22fd510ac 100644 --- a/doc/reference/items/probe/pkgconfig-probe.qdoc +++ b/doc/reference/items/probe/pkgconfig-probe.qdoc @@ -110,7 +110,7 @@ \qmlproperty string PkgConfigProbe::exactVersion The exact version of the required package. If set, pkg-config will ignore packages with - version greater than the value of this property. + version that is not equal to the value of this property. \nodefaultvalue */ @@ -119,7 +119,7 @@ \qmlproperty string PkgConfigProbe::maxVersion The maximum version of the required package. If set, pkg-config will ignore packages with - version that is not equal to the value of this property. + version greater than the value of this property. \nodefaultvalue */ diff --git a/doc/reference/jsextensions/jsextension-fileinfo.qdoc b/doc/reference/jsextensions/jsextension-fileinfo.qdoc index cb0eb7c43..d58c1a9e9 100644 --- a/doc/reference/jsextensions/jsextension-fileinfo.qdoc +++ b/doc/reference/jsextensions/jsextension-fileinfo.qdoc @@ -164,4 +164,11 @@ \endcode Returns the host operating system path separator. \funsince 1.22 + + \section2 executableSuffix + \code + FileInfo.executableSuffix(): string + \endcode + Returns the host operating system executable suffix. + \funsince 1.23 */ diff --git a/doc/reference/jsextensions/jsextension-host.qdoc b/doc/reference/jsextensions/jsextension-host.qdoc index 1dda85d17..c011e90eb 100644 --- a/doc/reference/jsextensions/jsextension-host.qdoc +++ b/doc/reference/jsextensions/jsextension-host.qdoc @@ -70,7 +70,7 @@ \section2 platform \code - Host.platform(): string[] + Host.platform(): string \endcode Returns the host operating system platform. @@ -111,21 +111,21 @@ \section2 osVersionMajor \code - Host.osVersionMajor(): string[] + Host.osVersionMajor(): number \endcode Returns the host operating system major version. \funsince 1.22 \section2 osVersionMinor \code - Host.osVersionMinor(): string[] + Host.osVersionMinor(): number \endcode Returns the host operating system minor version. \funsince 1.22 \section2 osVersionPatch \code - Host.osVersionPatch(): string[] + Host.osVersionPatch(): number \endcode Returns the host operating system patch level. \funsince 1.22 diff --git a/doc/reference/jsextensions/jsextensions-general.qdoc b/doc/reference/jsextensions/jsextensions-general.qdoc index eb5bbd945..495e90f39 100644 --- a/doc/reference/jsextensions/jsextensions-general.qdoc +++ b/doc/reference/jsextensions/jsextensions-general.qdoc @@ -57,12 +57,6 @@ \section1 Extensions to JavaScript Built-in Objects - \section2 Array.contains - \code - Array.contains(e: any): boolean - \endcode - Returns \c{true} if the array contains the element \c{e}. Returns \c{false} otherwise. - \section2 Array.containsAll \code Array.containsAll(other: any[]): boolean @@ -85,24 +79,6 @@ Duplicates that would originate from the concatenation are removed. The order of elements is preserved. - \section2 String.contains - \code - String.contains(s: string): boolean - \endcode - Returns \c{true} if the string contains the substring \c{s}. Returns \c{false} otherwise. - - \section2 startsWith - \code - String.startsWith(s: string): boolean - \endcode - Returns \c{true} if the string starts with the substring \c{s}. Returns \c{false} otherwise. - - \section2 endsWith - \code - String.endsWith(s: string): boolean - \endcode - Returns \c{true} if the string ends with the substring \c{s}. Returns \c{false} otherwise. - \section1 Console API diff --git a/doc/reference/module-providers/qbspkgconfig-module-provider.qdoc b/doc/reference/module-providers/qbspkgconfig-module-provider.qdoc index debaa5992..461536fbc 100644 --- a/doc/reference/module-providers/qbspkgconfig-module-provider.qdoc +++ b/doc/reference/module-providers/qbspkgconfig-module-provider.qdoc @@ -100,7 +100,7 @@ Setting this property to \c undefined or empty (\c "") value will use pkg-config's default search paths: \code - qbs build module-providers.pkgconfig.sysroot:undefined + qbs resolve moduleProviders.qbspkgconfig.sysroot:undefined \endcode This property is the equivalent of the \c{PKG_CONFIG_SYSROOT_DIR} variable for the @@ -111,14 +111,15 @@ /*! \qmlproperty bool qbspkgconfig::mergeDependencies + \obsolete Holds whether dependencies should be merged by pkg-config or \QBS. If set to true, dependencies are merged by pkg-config meaning each generated module is self-contained and does not depend on other modules. If set to false, generated modules - may depend on other modules and property merging is done by \QBS. The latter approach gives - \QBS more information about dependencies, but may have performance implications during resolve - phase, e.g. when using ABSEIL library. + may depend on other modules and property merging is done by \QBS. - \defaultvalue \c true + \defaultvalue \c false + + Deprecated in \QBS 2.2.0. */ diff --git a/doc/reference/modules/bundle-module.qdoc b/doc/reference/modules/bundle-module.qdoc index 4184672b4..fe9bf1354 100644 --- a/doc/reference/modules/bundle-module.qdoc +++ b/doc/reference/modules/bundle-module.qdoc @@ -465,6 +465,17 @@ */ /*! + \qmlproperty bool bundle::useBuiltinXcodeBuildSpecs + + Set this property to \c true to use Xcode \c .xcspec files shipped with \QBS. + + This property can be used as a workaround when using a new Xcode version that is not supported + by \QBS yet. + + \default false +*/ + +/*! \qmlproperty string bundle::versionsFolderPath \readonly \since Qbs 1.5 diff --git a/doc/reference/modules/capnprotocpp-module.qdoc b/doc/reference/modules/capnprotocpp-module.qdoc index c3c8660c9..7635edd9e 100644 --- a/doc/reference/modules/capnprotocpp-module.qdoc +++ b/doc/reference/modules/capnprotocpp-module.qdoc @@ -104,12 +104,10 @@ /*! \qmlproperty string capnproto.cpp::outputDir - \readonly The directory where the \c capnpc compiler generated files are placed. - The value of this property is automatically set by \QBS and cannot be - changed by the user. + \defaultvalue \c product.buildDirectory + "/capnp" */ /*! diff --git a/doc/reference/modules/cpp-module.qdoc b/doc/reference/modules/cpp-module.qdoc index 6eea7d9e3..271787fbc 100644 --- a/doc/reference/modules/cpp-module.qdoc +++ b/doc/reference/modules/cpp-module.qdoc @@ -837,7 +837,7 @@ If the value is left undefined, the compiler default will be used. If the list contains more than one value, the highest version is chosen. - Possible values include: \c{"c89"}, \c{"c99"}, \c{"c11"}. + Possible values include: \c{"c89"}, \c{"c99"}, \c{"c11"}, \c{"c17"}, \c{"c2x"}. \nodefaultvalue */ @@ -1402,18 +1402,18 @@ will result in the \c {-fuse-ld} option being emitted when linking with \c gcc, \c clang or \c clang-cl. Other toolchains do not support this property. - The possible values for \c clang and \c gcc are \c "bfd", \c "gold" and \c "lld", - the possible values for \c clang-cl are \c "link" and \c "lld". + The possible values for \c clang and \c gcc are \c "bfd", \c "gold", \c "lld" + and \c "mold", the possible values for \c clang-cl are \c "link" and \c "lld". The following example demonstrates how to change the linker for different toolchains: \code Properties { - condition: qbs.toolchain.contains("gcc") + condition: qbs.toolchain.includes("gcc") cpp.linkerVariant: "gold" } Properties { - condition: qbs.toolchain.contains("clang-cl") + condition: qbs.toolchain.includes("clang-cl") cpp.linkerVariant: "lld" } \endcode diff --git a/doc/reference/modules/freedesktop-module.qdoc b/doc/reference/modules/freedesktop-module.qdoc index 6c9d4a7fa..5b6b14d0f 100644 --- a/doc/reference/modules/freedesktop-module.qdoc +++ b/doc/reference/modules/freedesktop-module.qdoc @@ -119,7 +119,7 @@ */ /*! - \qmlproperty string freedesktop::name + \qmlproperty string freedesktop::appName The display name of the application which will be stored in the \c{.desktop} file. diff --git a/doc/reference/modules/protobufcpp-module.qdoc b/doc/reference/modules/protobufcpp-module.qdoc index 85851c4ae..eef189d0b 100644 --- a/doc/reference/modules/protobufcpp-module.qdoc +++ b/doc/reference/modules/protobufcpp-module.qdoc @@ -78,6 +78,14 @@ \li 1.18.0 \li This tag is attached to the header files generated by \c protoc compiler. \endtable + + \section2 Dependencies + + The \l protobuf.cpp module requires runtime libraries to be operational. It depends on the + \c "protobuflib" module which can be created by the \l qbspkgconfig module provider (the + corresponding packages are \c protobuf or \c protobuf-lite). If \l useGrpc is set to true, + the \l protobuf.cpp module also depends on the \c "grpcpp" module (corresponding package is + \c gprc++). */ /*! @@ -101,24 +109,6 @@ */ /*! - \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 pathList protobuf.cpp::importPaths The list of imports that are passed to the \c protoc tool via the \c --proto_path option. @@ -131,24 +121,6 @@ */ /*! - \qmlproperty string protobuf.cpp::includePath - - The path where protobuf C++ headers are located. Set this property to override the - default location. - - \defaultvalue \c auto-detected -*/ - -/*! - \qmlproperty string protobuf.cpp::libraryPath - - The path where the protobuf C++ library is located. Set this property to override the - default location. - - \defaultvalue \c auto-detected -*/ - -/*! \qmlproperty string protobuf.cpp::outputDir \readonly diff --git a/doc/reference/modules/qbs-module.qdoc b/doc/reference/modules/qbs-module.qdoc index 7ab8fd509..7fc1479d4 100644 --- a/doc/reference/modules/qbs-module.qdoc +++ b/doc/reference/modules/qbs-module.qdoc @@ -55,7 +55,7 @@ name: "Runtime resources" files: "*.qml" qbs.install: true - qbs.installDir: condition: qbs.targetOS.contains("unix") + qbs.installDir: condition: qbs.targetOS.includes("unix") ? "share/myapp" : "resources" } Group { @@ -64,7 +64,7 @@ qbs.install: true qbs.installDir: "bin" } - qbs.installPrefix: condition: qbs.targetOS.contains("unix") + qbs.installPrefix: condition: qbs.targetOS.includes("unix") ? "usr/local" : "MyApp" } \endcode @@ -354,6 +354,9 @@ \li \c{"hcs12"} \li 16-bit HC12 and S12 microcontroller family from Freescale Semiconductor \row + \li \c{"hppa"} + \li 64-bit PA-RISC processor architecture developed by Hewlett-Packard + \row \li \c{"ia64"} \li 64-bit ISA architecture of the Itanium family processors developed by Intel @@ -474,12 +477,12 @@ \code Properties { // flags for GCC - condition: qbs.toolchain.contains("gcc") + condition: qbs.toolchain.includes("gcc") cpp.commonCompilerFlags: ... } Properties { // flags for MSVC - condition: qbs.toolchain.contains("msvc") + condition: qbs.toolchain.includes("msvc") cpp.commonCompilerFlags: ... } \endcode @@ -495,7 +498,7 @@ use: \code - qbs.toolchain.contains("gcc") + qbs.toolchain.includes("gcc") \endcode since XCode, GCC and Clang belong to the \c "gcc" family. @@ -642,17 +645,17 @@ \code Group { // Includes all Unix-like platforms, such as: Linux, BSD, Apple platforms and others. - condition: qbs.targetOS.contains("unix") + condition: qbs.targetOS.includes("unix") files: ... } Group { // Includes all Apple platforms, such as macOS, iOS, and iOS Simulator. - condition: qbs.targetOS.contains("darwin") + condition: qbs.targetOS.includes("darwin") files: ... } Group { // Includes only macOS - condition: qbs.targetOS.contains("macos") + condition: qbs.targetOS.includes("macos") files: ... } \endcode @@ -665,7 +668,7 @@ use \code - qbs.targetOS.contains("darwin") + qbs.targetOS.includes("darwin") \endcode @@ -678,7 +681,7 @@ versus \code - qbs.targetOS.contains("linux") && !qbs.targetOS.contains("android") + qbs.targetOS.includes("linux") && !qbs.targetOS.includes("android") \endcode For the complete list of possible values, see diff --git a/doc/reference/modules/qt-core-module.qdoc b/doc/reference/modules/qt-core-module.qdoc index afc4a0f11..6aedf2a69 100644 --- a/doc/reference/modules/qt-core-module.qdoc +++ b/doc/reference/modules/qt-core-module.qdoc @@ -211,6 +211,32 @@ */ /*! + \qmlproperty string Qt.core::generatedHeadersDir + + The directory where tools that generate headers (such as \c moc or \c uic) put resulting + files. + + Normally, you don't need to change this property. The one use-case is when there are several + files with the same file name in the Product. The file name produced by \c moc is based + only on the source file's base file name (without the directory path) which leads to a conflict + in the mentioned case. You can resolve the conflict by setting this property to a non-default + value for one of the files. For example: + + \code + QtApplication { + files: ["my_cool_header.h", "main.cpp"] + Group { + name: "legacy" + files: "legacy/my_cool_header.h" + Qt.core.generatedHeadersDir: "qt.legacy.headers" + } + } + \endcode + + \defaultvalue \c product.buildDirectory + "/qt.headers" +*/ + +/*! \qmlproperty bool Qt.core::metaTypesInstallDir The directory to install the metatypes file into. If this property is empty or undefined, @@ -352,6 +378,14 @@ */ /*! + \qmlproperty string Qt.core::qdocOutputDir + + The directory name where the \c qdoc tool writes its output. + + \defaultvalue \c{product.buildDirectory + "/qdoc_html"} +*/ + +/*! \qmlproperty string Qt.core::qmBaseName The base name of the \c .qm file to be built from the \c .ts files in the @@ -363,6 +397,14 @@ */ /*! + \qmlproperty string Qt.core::qmDir + + The directory name where to put the \c .qm file to be built. + + \defaultvalue \l{Product::destinationDirectory}{product.destinationDirectory} +*/ + +/*! \qmlproperty string Qt.core::qtBuildVariant Specifies the type of Qt libraries to build against: \c "debug" or @@ -468,3 +510,11 @@ \defaultvalue \c{versionParts[2]} */ + +/*! + \qmlproperty bool Qt.core::useRPaths + + Whether to add \l{Qt.core::libPath}{Qt.core.libPath} to \l{cpp::rpaths}{cpp.rpaths}. + + \defaultvalue \c true on Linux, \c false everywhere else. +*/ diff --git a/docker-compose.yml b/docker-compose.yml index ed294dfd8..ffcaf6eee 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -18,104 +18,96 @@ x-default-service: &linux - SYS_PTRACE services: - focal: + focal-qt5: &focal-qt5 << : *linux - hostname: focal - image: ${DOCKER_USER:-qbsbuild}/qbsdev:focal-5.15.2_1.20.1-1 + hostname: focal-qt5 + image: ${DOCKER_USER:-qbsbuild}/qbsdev:focal-5.15.2_1.24.1-0 build: dockerfile: docker/focal/Dockerfile context: . args: QT_VERSION: 5.15.2 - QTCREATOR_VERSION: 5.0.3 + QTCREATOR_VERSION: 9.0.2 focal-qt6: << : *linux hostname: focal-qt6 - image: ${DOCKER_USER:-qbsbuild}/qbsdev:focal-qt6-6.2.0_1.20.1-1 + image: ${DOCKER_USER:-qbsbuild}/qbsdev:focal-qt6-6.5.0_1.24.1-0 build: dockerfile: docker/focal/Dockerfile context: . args: - QT_VERSION: 6.2.0 - QTCREATOR_VERSION: 5.0.3 + QT_VERSION: 6.5.0 + QTCREATOR_VERSION: 9.0.2 + + focal: + << : *focal-qt5 focal-qt6-static: << : *linux hostname: focal-qt6-static - image: ${DOCKER_USER:-qbsbuild}/qbsdev:focal-qt6-static-6.2.0_1.20.1-1 + image: ${DOCKER_USER:-qbsbuild}/qbsdev:focal-qt6-static-6.5.0_1.24.1-0 build: dockerfile: docker/focal/test-qt6-static.Dockerfile context: . args: - QT_VERSION: 6.2.0 - QTCREATOR_VERSION: 5.0.3 - - focal-android-513: - << : *linux - hostname: focal-android - image: ${DOCKER_USER:-qbsbuild}/qbsdev:focal-android-5.13.2-4 - build: - dockerfile: docker/focal/test-android.Dockerfile - context: . - args: - QT_VERSION: 5.13.2 - ANDROID_NDK_VERSION: 23.0.7599858 + QT_VERSION: 6.5.0 + QTCREATOR_VERSION: 9.0.2 - focal-android-514: + focal-android-515: << : *linux hostname: focal-android - image: ${DOCKER_USER:-qbsbuild}/qbsdev:focal-android-5.14.0-4 + image: ${DOCKER_USER:-qbsbuild}/qbsdev:focal-android-5.15.1-4 build: dockerfile: docker/focal/test-android.Dockerfile context: . args: - QT_VERSION: 5.14.0 + QT_VERSION: 5.15.1 ANDROID_NDK_VERSION: 23.0.7599858 - focal-android-515: + focal-android-620: << : *linux hostname: focal-android - image: ${DOCKER_USER:-qbsbuild}/qbsdev:focal-android-5.15.1-4 + image: ${DOCKER_USER:-qbsbuild}/qbsdev:focal-android-6.2.0-4 build: dockerfile: docker/focal/test-android.Dockerfile context: . args: - QT_VERSION: 5.15.1 + QT_VERSION: 6.2.0 ANDROID_NDK_VERSION: 23.0.7599858 - focal-android-600: + focal-android-630: << : *linux hostname: focal-android - image: ${DOCKER_USER:-qbsbuild}/qbsdev:focal-android-6.0.0-4 + image: ${DOCKER_USER:-qbsbuild}/qbsdev:focal-android-6.3.0-0 build: dockerfile: docker/focal/test-android.Dockerfile context: . args: - QT_VERSION: 6.0.0 + QT_VERSION: 6.3.0 ANDROID_NDK_VERSION: 23.0.7599858 - focal-android-620: + focal-android-642: << : *linux hostname: focal-android - image: ${DOCKER_USER:-qbsbuild}/qbsdev:focal-android-6.2.0-4 + image: ${DOCKER_USER:-qbsbuild}/qbsdev:focal-android-6.4.2-0 build: dockerfile: docker/focal/test-android.Dockerfile context: . args: - QT_VERSION: 6.2.0 + QT_VERSION: 6.4.2 ANDROID_NDK_VERSION: 23.0.7599858 - focal-android-630: + focal-android-650: << : *linux hostname: focal-android - image: ${DOCKER_USER:-qbsbuild}/qbsdev:focal-android-6.3.0-0 + image: ${DOCKER_USER:-qbsbuild}/qbsdev:focal-android-6.5.0-0 build: dockerfile: docker/focal/test-android.Dockerfile context: . args: - QT_VERSION: 6.3.0 - ANDROID_NDK_VERSION: 23.0.7599858 + QT_VERSION: 6.5.0 + ANDROID_NDK_VERSION: 25.1.8937393 focal-android-ndk-r19c: << : *linux @@ -158,7 +150,7 @@ services: focal-qt4: << : *linux hostname: focal-qt4 - image: ${DOCKER_USER:-qbsbuild}/qbsdev:focal-qt4-0 + image: ${DOCKER_USER:-qbsbuild}/qbsdev:focal-qt4-1 build: dockerfile: docker/focal/test-qt4.Dockerfile context: . @@ -175,13 +167,13 @@ services: QTCREATOR_VERSION: 5.0.3 windows: - image: ${DOCKER_USER:-qbsbuild}/qbsdev:windowsservercore-5.15.0_1.18.2-0 + image: ${DOCKER_USER:-qbsbuild}/qbsdev:windowsservercore-6.6.0_1.24.0-0 build: dockerfile: docker/windowsservercore/Dockerfile context: . args: - QT_VERSION: 5.15.0 - QBS_VERSION: 1.18.2 + QT_VERSION: 6.6.0 + QTCREATOR_VERSION: 9.0.1 working_dir: 'C:/qbs' environment: - BUILD_OPTIONS diff --git a/docker/focal/Dockerfile b/docker/focal/Dockerfile index df687f3a0..917d4d0a7 100644 --- a/docker/focal/Dockerfile +++ b/docker/focal/Dockerfile @@ -40,8 +40,8 @@ RUN apt-get update -qq && \ ca-certificates \ capnproto \ ccache \ - clang-8 \ - clang-tidy-8 \ + clang-12 \ + clang-tidy-12 \ cmake \ curl \ flex \ @@ -58,6 +58,7 @@ RUN apt-get update -qq && \ libprotobuf-dev \ libgrpc++-dev \ libxkbcommon-x11-0 \ + locales \ nanopb \ ninja-build \ nsis \ @@ -71,13 +72,20 @@ RUN apt-get update -qq && \ subversion \ unzip \ zip && \ - update-alternatives --install /usr/bin/clang clang /usr/bin/clang-8 100 && \ - update-alternatives --install /usr/bin/clang++ clang++ /usr/bin/clang++-8 100 && \ - update-alternatives --install /usr/bin/clang-check clang-check /usr/bin/clang-check-8 100 && \ + 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 pyyaml + pip install beautifulsoup4 lxml protobuf==3.19.1 pyyaml -ENV LLVM_INSTALL_DIR=/usr/lib/llvm-8 +# 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 # @@ -85,7 +93,7 @@ ENV LLVM_INSTALL_DIR=/usr/lib/llvm-8 # COPY scripts/install-qt.sh install-qt.sh -RUN ./install-qt.sh --version ${QT_VERSION} qtbase qtdeclarative qtscript qttools qtx11extras qtscxml qt5compat icu && \ +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 diff --git a/docker/focal/test-android.Dockerfile b/docker/focal/test-android.Dockerfile index 31f4eb773..5f479e4fc 100644 --- a/docker/focal/test-android.Dockerfile +++ b/docker/focal/test-android.Dockerfile @@ -37,10 +37,18 @@ RUN apt-get update -qq && \ curl \ libasan5 \ libglib2.0-0 \ + locales \ openjdk-8-jdk-headless \ p7zip-full \ unzip +# 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 JAVA_HOME=/usr/lib/jvm/java-8-openjdk-amd64 RUN echo "export JAVA_HOME=${JAVA_HOME}" > /etc/profile.d/android.sh && \ echo "export PATH=${JAVA_HOME}/bin:\${PATH}" >> /etc/profile.d/android.sh diff --git a/docker/focal/test-qt4.Dockerfile b/docker/focal/test-qt4.Dockerfile index c23a664d2..e49e255f9 100644 --- a/docker/focal/test-qt4.Dockerfile +++ b/docker/focal/test-qt4.Dockerfile @@ -36,5 +36,5 @@ RUN sudo add-apt-repository ppa:gezakovacs/ppa -y && \ apt-get update -qq && \ apt-get install -qq -y \ build-essential \ - libqt4-dev - + libqt4-dev \ + qt4-dev-tools diff --git a/docker/focal/test-qt6-static.Dockerfile b/docker/focal/test-qt6-static.Dockerfile index 61316e372..b7c0bbc62 100644 --- a/docker/focal/test-qt6-static.Dockerfile +++ b/docker/focal/test-qt6-static.Dockerfile @@ -43,6 +43,7 @@ RUN apt-get update -qq && \ build-essential \ git \ perl \ + clang-12 \ cmake \ python \ zlib1g-dev \ @@ -56,6 +57,7 @@ RUN apt-get update -qq && \ libvulkan-dev \ libicu-dev \ libb2-dev \ + libclang-12-dev \ libsystemd-dev \ libfontconfig1-dev \ libfreetype6-dev \ @@ -101,6 +103,7 @@ RUN apt-get update -qq && \ libxcb-damage0-dev \ libxcb-dpms0-dev \ libgstreamer1.0-dev \ + llvm-12-dev \ apt-transport-https ENV QT_HOME="/home/${USER_NAME}/qt" @@ -153,8 +156,8 @@ RUN apt-get update -qq && \ ca-certificates \ capnproto \ ccache \ - clang-8 \ - clang-tidy-8 \ + clang-12 \ + clang-tidy-12 \ cmake \ curl \ flex \ @@ -186,9 +189,9 @@ RUN apt-get update -qq && \ zip \ libb2-1 \ libpcre++ && \ - update-alternatives --install /usr/bin/clang clang /usr/bin/clang-8 100 && \ - update-alternatives --install /usr/bin/clang++ clang++ /usr/bin/clang++-8 100 && \ - update-alternatives --install /usr/bin/clang-check clang-check /usr/bin/clang-check-8 100 && \ + 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 pyyaml diff --git a/docker/leap/Dockerfile b/docker/leap/Dockerfile index 308f854ab..12f789e21 100644 --- a/docker/leap/Dockerfile +++ b/docker/leap/Dockerfile @@ -76,7 +76,7 @@ RUN zypper install -y \ # COPY scripts/install-qt.sh install-qt.sh -RUN ./install-qt.sh --version ${QT_VERSION} qtbase qtdeclarative qtscript qttools qtx11extras qtscxml qt5compat icu && \ +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 diff --git a/docker/windowsservercore/Dockerfile b/docker/windowsservercore/Dockerfile index 50a1400ef..8fce9fa59 100644 --- a/docker/windowsservercore/Dockerfile +++ b/docker/windowsservercore/Dockerfile @@ -6,7 +6,7 @@ LABEL Description="Windows Server Core development environment for Qbs with Qt, RUN reg add "HKLM\SOFTWARE\Microsoft\Windows\Windows Error Reporting" /v Disabled /t REG_DWORD /d 1 /f RUN reg add "HKLM\SOFTWARE\Microsoft\Windows\Windows Error Reporting" /v DontShowUI /t REG_DWORD /d 1 /f -# Install VS from the website since chocolatey has broken .NET 4.8 (dotnetfx package) which is a +# Install VS 2019 from the website since chocolatey has broken .NET 4.8 (dotnetfx package) which is a # dependency for the visualstudio2019buildtools package RUN powershell -NoProfile -ExecutionPolicy Bypass -Command \ Invoke-WebRequest "https://aka.ms/vs/16/release/vs_community.exe" \ @@ -14,18 +14,17 @@ RUN powershell -NoProfile -ExecutionPolicy Bypass -Command \ RUN "%TEMP%\vs_community.exe" --quiet --wait --norestart --noUpdateInstaller \ --add Microsoft.VisualStudio.Component.VC.Tools.x86.x64 \ - --add Microsoft.VisualStudio.Component.Windows10SDK.18362 + --add Microsoft.VisualStudio.Component.Windows10SDK.20348 RUN powershell -NoProfile -ExecutionPolicy Bypass -Command \ $Env:chocolateyVersion = '0.10.15' ; \ $Env:chocolateyUseWindowsCompression = 'false' ; \ "[Net.ServicePointManager]::SecurityProtocol = \"tls12, tls11, tls\"; iex ((New-Object System.Net.WebClient).DownloadString('http://chocolatey.org/install.ps1'))" && SET "PATH=%PATH%;%ALLUSERSPROFILE%\chocolatey\bin" -ARG QBS_VERSION -RUN choco install -y python && \ +RUN choco install -y python --version 3.9 && \ choco install -y 7zip --version 19.0 && \ choco install -y git --version 2.24.0 --params "/GitAndUnixToolsOnPath" && \ - choco install -y qbs --version %QBS_VERSION% + choco install -y vswhere # for building the documentation RUN pip install beautifulsoup4 lxml @@ -42,13 +41,14 @@ RUN certutil -generateSSTFromWU roots.sst && \ ARG QT_VERSION COPY scripts/install-qt.sh install-qt.sh -RUN bash -c "./install-qt.sh -d /c/Qt --version ${QT_VERSION} --toolchain win64_msvc2019_64 qtbase qtdeclarative qttools qtscript" +RUN bash -c "./install-qt.sh -d /c/Qt --version ${QT_VERSION} --toolchain win64_msvc2019_64 qtbase qtdeclarative qttools qt5compat" ENV QTDIR64=C:\\Qt\\${QT_VERSION}\\msvc2019_64 -RUN bash -c "./install-qt.sh -d /c/Qt --version ${QT_VERSION} --toolchain win32_msvc2019 qtbase qtdeclarative qttools qtscript" -ENV QTDIR=C:\\Qt\\${QT_VERSION}\\msvc2019 +########### Install Qbs ############# +ARG QTCREATOR_VERSION +RUN bash -c "./install-qt.sh -d /c/Qt --version ${QTCREATOR_VERSION} qtcreator" +RUN setx PATH "C:\\Qt\\Tools\\QtCreator\\bin;%PATH%" RUN qbs setup-toolchains --detect && \ qbs setup-qt %QTDIR64%/bin/qmake.exe qt64 && \ - qbs setup-qt %QTDIR%/bin/qmake.exe qt && \ qbs config defaultProfile qt64 diff --git a/examples/cocoa-touch-application/cocoa-touch-application.qbs b/examples/cocoa-touch-application/cocoa-touch-application.qbs index ec199d4a5..c837cacf1 100644 --- a/examples/cocoa-touch-application/cocoa-touch-application.qbs +++ b/examples/cocoa-touch-application/cocoa-touch-application.qbs @@ -64,6 +64,11 @@ CppApplication { cpp.automaticReferenceCounting: true cpp.frameworks: [ "UIKit", "Foundation", "CoreGraphics" ] + // sample code signing settings: + // codesign.enableCodeSigning: true + // codesign.provisioningProfile: "my provisioning profile name" + // codesign.signingIdentity: "Apple Development: My Team Name" + Group { prefix: "CocoaTouchApplication/" files: [ diff --git a/examples/code-generator/code-generator.qbs b/examples/code-generator/code-generator.qbs index 4d664c0ec..fdb0e1aac 100644 --- a/examples/code-generator/code-generator.qbs +++ b/examples/code-generator/code-generator.qbs @@ -62,7 +62,7 @@ Project { // Generate and build a hello-world application. CppApplication { - condition: qbs.targetOS === qbs.hostOS + condition: qbs.targetPlatform === qbs.hostPlatform name: "hello-world" Depends { name: "hwgen" } Rule { diff --git a/examples/grpc/grpc.qbs b/examples/grpc/grpc.qbs index c4f146ec0..370bcb292 100644 --- a/examples/grpc/grpc.qbs +++ b/examples/grpc/grpc.qbs @@ -32,11 +32,12 @@ import qbs.Utilities Project { condition: Utilities.versionCompare(qbs.version, "1.14") >= 0 + qbsModuleProviders: "qbspkgconfig" Application { Depends { name: "cpp" } Depends { name: "protobuf.cpp"; required: false } - condition: protobuf.cpp.present && qbs.targetOS === qbs.hostOS + condition: protobuf.cpp.present && qbs.targetPlatform === qbs.hostPlatform protobuf.cpp.useGrpc: true consoleApplication: true cpp.cxxLanguageVersion: "c++17" @@ -56,7 +57,7 @@ Project { Application { Depends { name: "cpp" } Depends { name: "protobuf.cpp"; required: false } - condition: protobuf.cpp.present && qbs.targetOS === qbs.hostOS + condition: protobuf.cpp.present && qbs.targetPlatform === qbs.hostPlatform protobuf.cpp.useGrpc: true consoleApplication: true cpp.cxxLanguageVersion: "c++17" diff --git a/examples/protobuf/addressbook_cpp/addressbook_cpp.qbs b/examples/protobuf/addressbook_cpp/addressbook_cpp.qbs index 667b30223..95fb1c1e3 100644 --- a/examples/protobuf/addressbook_cpp/addressbook_cpp.qbs +++ b/examples/protobuf/addressbook_cpp/addressbook_cpp.qbs @@ -5,7 +5,6 @@ CppApplication { condition: protobuf.cpp.present && qbs.targetPlatform === Host.platform() Depends { name: "cpp" } - cpp.cxxLanguageVersion: "c++11" cpp.minimumMacosVersion: "10.8" Depends { name: "protobuf.cpp"; required: false } @@ -15,4 +14,5 @@ CppApplication { "main.cpp", "README.md", ] + qbsModuleProviders: "qbspkgconfig" } diff --git a/qbs-resources/imports/QbsProduct.qbs b/qbs-resources/imports/QbsProduct.qbs index 5df694dd8..ea8516736 100644 --- a/qbs-resources/imports/QbsProduct.qbs +++ b/qbs-resources/imports/QbsProduct.qbs @@ -2,7 +2,7 @@ Product { Depends { name: "qbsbuildconfig" } Depends { name: "qbsversion" } Depends { name: "Qt.core"; versionAtLeast: minimumQtVersion } - property string minimumQtVersion: "5.14.0" + property string minimumQtVersion: "5.15.2" property bool install: true property string targetInstallDir cpp.defines: { diff --git a/qbs-resources/imports/QbsUnittest.qbs b/qbs-resources/imports/QbsUnittest.qbs index 5b54b9a51..c956d5480 100644 --- a/qbs-resources/imports/QbsUnittest.qbs +++ b/qbs-resources/imports/QbsUnittest.qbs @@ -6,16 +6,5 @@ QbsAutotest { name: "Qt.core5compat"; condition: Utilities.versionCompare(Qt.core.version, "6.0.0") >= 0 } - Depends { - name: "Qt.script" - condition: !qbsbuildconfig.useBundledQtScript - required: false - } - Depends { - name: "qbsscriptengine" - condition: qbsbuildconfig.useBundledQtScript || !Qt.script.present - } - property stringList bundledQtScriptIncludes: qbsbuildconfig.useBundledQtScript - || !Qt.script.present ? qbsscriptengine.includePaths : [] - cpp.includePaths: base.concat(bundledQtScriptIncludes) + Depends { name: "quickjs"; cpp.link: false } } diff --git a/qbs-resources/modules/qbsbuildconfig/qbsbuildconfig.qbs b/qbs-resources/modules/qbsbuildconfig/qbsbuildconfig.qbs index 4a50298fd..4c22b5eb7 100644 --- a/qbs-resources/modules/qbsbuildconfig/qbsbuildconfig.qbs +++ b/qbs-resources/modules/qbsbuildconfig/qbsbuildconfig.qbs @@ -14,7 +14,6 @@ Module { property bool enableRPath: true property bool installApiHeaders: true property bool enableBundledQt: false - property bool useBundledQtScript: false property bool staticBuild: false property string libDirName: "lib" property string appInstallDir: "bin" @@ -47,20 +46,23 @@ Module { property string pluginsInstallDir: libDirName + "/qbs/plugins" property string qmlTypeDescriptionsInstallDir: FileInfo.joinPaths(resourcesInstallDir, "share/qbs/qml-type-descriptions") + property bool dumpJsLeaks: qbs.buildVariant === "debug" Properties { condition: project.withCode && qbs.toolchain.contains("gcc") cpp.cxxFlags: { - var flags = []; + var flags = ["-Wno-missing-field-initializers"]; if (enableAddressSanitizer) flags.push("-fno-omit-frame-pointer"); function isClang() { return qbs.toolchain.contains("clang"); } function versionAtLeast(v) { return Utilities.versionCompare(cpp.compilerVersion, v) >= 0; }; + if (isClang()) + flags.push("-Wno-constant-logical-operand"); if ((!isClang() && versionAtLeast("9")) || (isClang() && !qbs.hostOS.contains("darwin") && versionAtLeast("10"))) { - flags.push("-Wno-deprecated-copy", "-Wno-constant-logical-operand"); + flags.push("-Wno-deprecated-copy"); } return flags; } @@ -68,8 +70,10 @@ Module { var flags = []; if (enableAddressSanitizer) flags.push("-fsanitize=address"); - if (enableUbSanitizer) + if (enableUbSanitizer) { flags.push("-fsanitize=undefined"); + flags.push("-fno-sanitize=vptr"); + } if (enableThreadSanitizer) flags.push("-fsanitize=thread"); return flags; @@ -77,7 +81,7 @@ Module { } Properties { condition: project.withCode && qbs.toolchain.contains("msvc") && product.Qt - && Utilities.versionCompare(Qt.core.version, "6.3") >= 0 + && Utilities.versionCompare(product.Qt.core.version, "6.3") >= 0 && Utilities.versionCompare(cpp.compilerVersion, "19.10") >= 0 && Utilities.versionCompare(qbs.version, "1.23") < 0 cpp.cxxFlags: "/permissive-" diff --git a/qbs.pro b/qbs.pro deleted file mode 100644 index 9777615f4..000000000 --- a/qbs.pro +++ /dev/null @@ -1,77 +0,0 @@ -requires(!cross_compile) -include(src/lib/bundledlibs.pri) - -defineTest(minQtVersion) { - maj = $$1 - min = $$2 - patch = $$3 - isEqual(QT_MAJOR_VERSION, $$maj) { - isEqual(QT_MINOR_VERSION, $$min) { - isEqual(QT_PATCH_VERSION, $$patch) { - return(true) - } - greaterThan(QT_PATCH_VERSION, $$patch) { - return(true) - } - } - greaterThan(QT_MINOR_VERSION, $$min) { - return(true) - } - } - greaterThan(QT_MAJOR_VERSION, $$maj) { - return(true) - } - return(false) -} - -!minQtVersion(5, 14, 0) { - message("Cannot build qbs with Qt version $${QT_VERSION}.") - error("Use at least Qt 5.14.0.") -} - -TEMPLATE = subdirs -pkgconfig.file = src/lib/pkgconfig/pkgconfig.pro -corelib.file = src/lib/corelib/corelib.pro -corelib.depends = pkgconfig -msbuildlib.subdir = src/lib/msbuild -msbuildlib.depends = corelib -src_app.subdir = src/app -src_app.depends = corelib -CONFIG(static, static|shared): src_app.depends += src_plugins -src_libexec.subdir = src/libexec -src_plugins.subdir = src/plugins -CONFIG(shared, static|shared): src_plugins.depends = corelib -src_plugins.depends += msbuildlib -tests.depends = static_res -static_res.file = static-res.pro -static_res.depends = src_app src_libexec src_plugins static.pro -qbs_use_bundled_qtscript { - scriptenginelib.file = src/lib/scriptengine/scriptengine.pro - corelib.depends += scriptenginelib - SUBDIRS += scriptenginelib -} -SUBDIRS += \ - pkgconfig \ - corelib\ - msbuildlib\ - src_app\ - src_libexec\ - src_plugins\ - static.pro\ - static_res - -!CONFIG(nomake_tests): SUBDIRS += tests - -OTHER_FILES += \ - doc/*.qdoc \ - doc/reference/*.qdoc \ - doc/reference/items/convenience/*.qdoc \ - doc/reference/items/language/*.qdoc \ - doc/reference/jsextensions/*.qdoc \ - doc/reference/modules/*.qdoc \ - doc/targets/*.qdoc* \ - doc/qbs.qdocconf \ - doc/config/qbs-project.qdocconf - -include(qbs_version.pri) -include(doc/doc.pri) @@ -48,11 +48,6 @@ Project { } Product { - name: "qmake project files for qbs" - files: ["**/*.pr[io]"] - } - - Product { name: "cmake project files for qbs" files: ["**/CMakeLists.txt"] } diff --git a/qbs_version.pri b/qbs_version.pri deleted file mode 100644 index 97c4c799e..000000000 --- a/qbs_version.pri +++ /dev/null @@ -1,4 +0,0 @@ -QBS_VERSION = $$cat($$PWD/VERSION) -QBS_VERSION_MAJ = $$section(QBS_VERSION, ., 0, 0) -QBS_VERSION_MIN = $$section(QBS_VERSION, ., 1, 1) -DEFINES += QBS_VERSION=\\\"$$QBS_VERSION\\\" diff --git a/scripts/address-sanitizer-suppressions.txt b/scripts/address-sanitizer-suppressions.txt index f3e1980b1..c4731b213 100644 --- a/scripts/address-sanitizer-suppressions.txt +++ b/scripts/address-sanitizer-suppressions.txt @@ -1,3 +1,2 @@ -leak:libQt5Script.so.5 leak:QThreadPrivate::QThreadPrivate leak:QArrayData::allocate diff --git a/scripts/build-qbs-with-cmake.sh b/scripts/build-qbs-with-cmake.sh index 820f30303..7f3fe3f5e 100755 --- a/scripts/build-qbs-with-cmake.sh +++ b/scripts/build-qbs-with-cmake.sh @@ -100,6 +100,6 @@ fi # (while true; do echo "" && sleep 590; done) & trap "kill $!; wait $! 2>/dev/null || true; killall sleep || true" EXIT -ctest -j $(nproc --all) +ctest -j $(nproc --all) --output-on-failure popd diff --git a/scripts/build-qbs-with-qbs.sh b/scripts/build-qbs-with-qbs.sh index 42d02c3cc..b26dcc7b5 100755 --- a/scripts/build-qbs-with-qbs.sh +++ b/scripts/build-qbs-with-qbs.sh @@ -58,7 +58,6 @@ fi # BUILD_OPTIONS="\ profile:${QBS_BUILD_PROFILE} \ - modules.qbsbuildconfig.enableAddressSanitizer:true \ modules.qbsbuildconfig.enableUnitTests:true \ modules.cpp.treatWarningsAsErrors:true \ modules.cpp.separateDebugInformation:true \ diff --git a/scripts/build-qbs-with-qmake.sh b/scripts/build-qbs-with-qmake.sh deleted file mode 100755 index 69ea75a21..000000000 --- a/scripts/build-qbs-with-qmake.sh +++ /dev/null @@ -1,83 +0,0 @@ -#!/usr/bin/env bash -############################################################################# -## -## Copyright (C) 2019 Richard Weickelt. -## 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$ -## -############################################################################# -set -e - -# -# It might be desired to keep settings for Qbs testing -# in a separate folder. -# -export QBS_AUTOTEST_SETTINGS_DIR="${QBS_AUTOTEST_SETTINGS_DIR:-/tmp/qbs-settings}" - -# -# Build all default products of Qbs -# -qmake -r qbs.pro \ - CONFIG+=qbs_enable_unit_tests \ - ${BUILD_OPTIONS} -make -j $(nproc --all) -make docs - -# -# Set up profiles for the freshly built Qbs if not -# explicitly specified otherwise -# -if [ -z "${QBS_AUTOTEST_PROFILE}" ]; then - - export QBS_AUTOTEST_PROFILE=autotestprofile - RUN_OPTIONS="\ - --settings-dir ${QBS_AUTOTEST_SETTINGS_DIR} \ - " - - ./bin/qbs setup-toolchains \ - ${RUN_OPTIONS} \ - --detect - - ./bin/qbs setup-qt \ - ${RUN_OPTIONS} \ - "${QMAKE_PATH:-$(which qmake)}" ${QBS_AUTOTEST_PROFILE} - - ./bin/qbs config \ - ${RUN_OPTIONS} \ - ${QBS_AUTOTEST_PROFILE}.baseProfile gcc - -fi - - -make check -j $(nproc --all) diff --git a/scripts/install-ow.sh b/scripts/install-ow.sh index 6b123fab8..c8db30b8f 100755 --- a/scripts/install-ow.sh +++ b/scripts/install-ow.sh @@ -38,6 +38,7 @@ ## ############################################################################# set -eu +set -o pipefail function show_help() { cat <<EOF @@ -114,15 +115,15 @@ DOWNLOAD_DIR=`mktemp -d 2>/dev/null || mktemp -d -t 'ow-tmp'` VERSION_MAJOR=`echo $VERSION | cut -d. -f1` VERSION_MINOR=`echo $VERSION | cut -d. -f2` -OW_URL="https://github.com/open-watcom/open-watcom-v${VERSION_MAJOR}/releases/download/Current-build/ow-snapshot.tar.gz" -OW_TARGZ="${DOWNLOAD_DIR}/ow.tar.gz" +OW_URL="https://github.com/open-watcom/open-watcom-v${VERSION_MAJOR}/releases/download/Current-build/ow-snapshot.tar.xz" +OW_TAR="${DOWNLOAD_DIR}/ow.tar.xz" echo "Downloading compiler from ${OW_URL}..." >&2 -curl --progress-bar -L -o ${OW_TARGZ} ${OW_URL} >&2 +curl --progress-bar -L -o ${OW_TAR} ${OW_URL} >&2 echo "Unpacking compiler to ${INSTALL_DIR}..." >&2 -7z x "${OW_TARGZ}" -so | 7z x -aoa -si -ttar -o"${INSTALL_DIR}" >/dev/null 2>&1 +7z x "${OW_TAR}" -so | 7z x -aoa -si -ttar -o"${INSTALL_DIR}" >/dev/null 2>&1 echo "${INSTALL_DIR}/${BIN_DIR}" -rm -f ${OW_TARGZ} +rm -f ${OW_TAR} diff --git a/scripts/install-qt.sh b/scripts/install-qt.sh index 8af82ccbc..5799fee02 100755 --- a/scripts/install-qt.sh +++ b/scripts/install-qt.sh @@ -45,7 +45,7 @@ usage: install-qt [options] [components] Examples ./install-qt.sh --version 5.13.1 qtbase - ./install-qt.sh --version 5.14.0 --target android --toolchain any qtbase qtscript + ./install-qt.sh --version 5.14.0 --target android --toolchain any qtbase Positional arguments components @@ -89,7 +89,7 @@ Options android any, android_armv7, android_arm64_v8a desktop - clang_64 (default), + clang_64 (default) ios ios @@ -261,7 +261,7 @@ function compute_url(){ echo "${BASE_URL}/${REMOTE_PATH}" return 0 elif [[ "${COMPONENT}" =~ "mingw" ]]; then - REMOTE_BASE="tools_mingw/qt.tools.${TOOLCHAIN}${VERSION//./}" + REMOTE_BASE="tools_mingw90/qt.tools.${TOOLCHAIN}${VERSION//./}" REMOTE_PATH="$(${CURL} ${BASE_URL}/${REMOTE_BASE}/ | grep -o -E "[[:alnum:]_.\-]*7z" | grep -v "meta" | head -1)" if [ ! -z "${REMOTE_PATH}" ]; then @@ -348,7 +348,7 @@ for COMPONENT in ${COMPONENTS}; do SUBDIR="${TOOLCHAIN/win32_/}" elif [[ "${TOOLCHAIN}" =~ "any" ]] && [[ "${TARGET_PLATFORM}" == "android" ]]; then SUBDIR="android" - elif [ "${HOST_OS}" == "mac_x64" ] && [ ! "${VERSION}" \< "6.1.2" ]; then + elif [[ "${HOST_OS}" == "mac_x64" ]] && [[ ! "${VERSION}" < "6.1.2" ]] && [[ "${TARGET_PLATFORM}" == "desktop" ]]; then SUBDIR="macos" else SUBDIR="${TOOLCHAIN}" @@ -356,11 +356,25 @@ for COMPONENT in ${COMPONENTS}; do if [ "${TARGET_PLATFORM}" == "android" ] && [ ! "${VERSION}" \< "6.0.0" ]; then CONF_FILE="${UNPACK_DIR}/${VERSION}/${SUBDIR}/bin/target_qt.conf" + ANDROID_QMAKE_FILE="${UNPACK_DIR}/${VERSION}/${SUBDIR}/bin/qmake" + if [ "${TOOLCHAIN}" == "android_armv7" ] && [ ! "${VERSION}" \< "6.4.2" ]; then + sed -i "s/\r//" "${CONF_FILE}" + sed -i "s|HostLibraryExecutables=.\/bin|HostLibraryExecutables=.\/libexec|g" "${CONF_FILE}" + chmod +x "${ANDROID_QMAKE_FILE}" + sed -i "s|\\\|\/|g" "${ANDROID_QMAKE_FILE}" + fi sed -i "s|target|../$TOOLCHAIN|g" "${CONF_FILE}" sed -i "/HostPrefix/ s|$|gcc_64|g" "${CONF_FILE}" - ANDROID_QMAKE_FILE="${UNPACK_DIR}/${VERSION}/${SUBDIR}/bin/qmake" QMAKE_FILE="${UNPACK_DIR}/${VERSION}/gcc_64/bin/qmake" sed -i "s|\/home\/qt\/work\/install\/bin\/qmake|$QMAKE_FILE|g" "${ANDROID_QMAKE_FILE}" + sed -i "s|\/Users\/qt\/work\/install\/bin\/qmake|$QMAKE_FILE|g" "${ANDROID_QMAKE_FILE}" + elif [ "${TARGET_PLATFORM}" == "ios" ] && [ ! "${VERSION}" \< "6.0.0" ]; then + CONF_FILE="${UNPACK_DIR}/${VERSION}/${SUBDIR}/bin/target_qt.conf" + sed -i.bak "s|HostData=target|HostData=../$TOOLCHAIN|g" "${CONF_FILE}" + sed -i.bak "s|HostPrefix=..\/..\/|HostPrefix=..\/..\/macos|g" "${CONF_FILE}" + IOS_QMAKE_FILE="${UNPACK_DIR}/${VERSION}/${SUBDIR}/bin/qmake" + QMAKE_FILE="${UNPACK_DIR}/${VERSION}/macos/bin/qmake" + sed -i.bak "s|\/Users\/qt\/work\/install\/bin\/qmake|${QMAKE_FILE}|g" "${IOS_QMAKE_FILE}" else CONF_FILE="${UNPACK_DIR}/${VERSION}/${SUBDIR}/bin/qt.conf" echo "[Paths]" > ${CONF_FILE} @@ -378,10 +392,14 @@ for COMPONENT in ${COMPONENTS}; do # adjust the PATH variable. echo $(dirname "${CONF_FILE}") elif [[ "${COMPONENT}" =~ "mingw" ]]; then + VERSION_DIR="${VERSION//./}" if [[ "${TOOLCHAIN}" =~ "win64_mingw" ]]; then - echo "${UNPACK_DIR}/Tools/mingw${VERSION//./}_64/bin" + if [[ "${VERSION}" == "9.0.0" ]]; then + VERSION_DIR="1120" + fi + echo "${UNPACK_DIR}/Tools/mingw${VERSION_DIR}_64/bin" elif [[ "${TOOLCHAIN}" =~ "win32_mingw" ]]; then - echo "${UNPACK_DIR}/Tools/mingw${VERSION//./}_32/bin" + echo "${UNPACK_DIR}/Tools/mingw${VERSION_DIR}_32/bin" fi elif [[ "${COMPONENT}" =~ "qtcreator" ]]; then if [ "${HOST_OS}" == "mac_x64" ]; then diff --git a/scripts/run-analyzer.sh b/scripts/run-analyzer.sh index 4293883a1..a410e8b4c 100755 --- a/scripts/run-analyzer.sh +++ b/scripts/run-analyzer.sh @@ -57,7 +57,8 @@ if [ -z "$RUN_CLANG_TIDY" ] || [ -z "$CLANG_TIDY" ]; then fi fi -CPU_COUNT=$("$(dirname "$0")"/cpu-count.sh) +SCRIPT_DIR=$(dirname "$0") +CPU_COUNT=$("${SCRIPT_DIR}/cpu-count.sh") BUILD_OPTIONS="\ ${QBS_BUILD_PROFILE:+profile:${QBS_BUILD_PROFILE}} \ @@ -88,7 +89,7 @@ import os import sys dbFile = sys.argv[1] -blacklist = ['json.cpp', 'qmljsgrammar.cpp'] +blacklist = ['json.cpp', 'qmljsgrammar.cpp', 'qmljsparser.cpp'] seenFiles = set() patched_db = [] with open(dbFile, 'r') as f: diff --git a/scripts/test-qbs.sh b/scripts/test-qbs.sh index 55dc6524e..3996c63bf 100755 --- a/scripts/test-qbs.sh +++ b/scripts/test-qbs.sh @@ -38,7 +38,7 @@ ## ############################################################################# -set -eu +set -eu -o pipefail # # Qbs is built with the address sanitizer enabled. @@ -48,12 +48,6 @@ export LSAN_OPTIONS="suppressions=$( cd "$(dirname "$0")" ; pwd -P )/address-san export PATH="$1:$PATH" -CPUS=$("$(dirname "$0")"/cpu-count.sh) - export QBS_AUTOTEST_PROFILE=${QBS_AUTOTEST_PROFILE:-qt} -echo "Running Qbs tests (${CPUS} jobs in parallel)." -find $1 -name "tst_*" \ - | grep -v tst_blackbox-joblimits \ - | xargs -I{} -n1 -P${CPUS} bash -c \ - 'export LOG=$(mktemp) ; $({} > ${LOG} 2>&1) ; export RESULT=$? ; cat ${LOG} ; exit ${RESULT}' -tst_blackbox-joblimits +echo "Running Qbs tests." +find $1 -name "tst*" | xargs -I{} -n1 bash -c "{}" diff --git a/share/CMakeLists.txt b/share/CMakeLists.txt index 2891f7272..e47bd74f0 100644 --- a/share/CMakeLists.txt +++ b/share/CMakeLists.txt @@ -8,22 +8,8 @@ endif() # below we copy some files that are required to run qbs from the build directory # emulating the same layout we will have after the installation -# copy & install dmgbuild -install( - PROGRAMS ../src/3rdparty/python/bin/dmgbuild - DESTINATION "${QBS_LIBEXEC_INSTALL_DIR}" - ) -set(_DMGBUILD_INSTALL_PATH ${CMAKE_BINARY_DIR}/${QBS_OUTPUT_PREFIX}${QBS_LIBEXEC_INSTALL_DIR}/dmgbuild) -add_custom_command( - OUTPUT ${_DMGBUILD_INSTALL_PATH} - COMMAND ${CMAKE_COMMAND} -E copy - ${CMAKE_CURRENT_SOURCE_DIR}/../src/3rdparty/python/bin/dmgbuild - ${CMAKE_BINARY_DIR}/${QBS_OUTPUT_PREFIX}${QBS_LIBEXEC_INSTALL_DIR} - COMMENT "Copying dmgbuild script" - ) - # copy & install python packages -set(_SITE_PACKAGES_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../src/3rdparty/python/lib/python2.7/site-packages/) +set(_SITE_PACKAGES_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../src/3rdparty/python/lib/python3.9/site-packages/) file(GLOB_RECURSE _SITE_PACKAGES_RELATIVE RELATIVE "${_SITE_PACKAGES_DIR}" "${_SITE_PACKAGES_DIR}/*.py") set(_SITE_PACKAGES_SRC ${_SITE_PACKAGES_RELATIVE}) @@ -85,7 +71,7 @@ add_custom_command( -f ${PROJECT_SOURCE_DIR}/qbs.qbs -d ${PROJECT_BINARY_DIR}/ config:resources-build - qbs.installPrefix:undefined + qbs.installPrefix:"" project.withCode:false project.withDocumentation:false profile:none @@ -102,7 +88,6 @@ add_custom_command( add_custom_target( BuildQbsResources ALL DEPENDS - "${_DMGBUILD_INSTALL_PATH}" ${_SITE_PACKAGES_DST} ${_QBS_RESOURCES_DST} "${_QMLTYPES_INSTALL_PATH}" diff --git a/share/qbs/imports/qbs/DarwinTools/darwin-tools.js b/share/qbs/imports/qbs/DarwinTools/darwin-tools.js index 15f2c54ec..9f7d92e59 100644 --- a/share/qbs/imports/qbs/DarwinTools/darwin-tools.js +++ b/share/qbs/imports/qbs/DarwinTools/darwin-tools.js @@ -182,7 +182,7 @@ var PropertyListVariableExpander = (function () { return { "syntax": syntax, "index": idx }; } - function expandString(key, str, env) { + function expandString(key, str, env, seenVars) { if (!str) return str; var repl = indexOfReplacementStart(syntaxes, str); @@ -194,7 +194,14 @@ var PropertyListVariableExpander = (function () { var varParts = str.slice(i + repl.syntax.open.length, j).split(':'); var varName = varParts[0]; var varFormatter = varParts[1]; - var varValue = expandString(key, env[varName], env); + var envValue = env[varName]; + // if we end up expanding the same variable again, break the recursion + if (seenVars.indexOf(varName) !== -1) + envValue = ""; + else + seenVars.push(varName); + var varValue = expandString(key, envValue, env, seenVars); + seenVars.pop(); if (undefined === varValue) { // skip replacement if ($this.undefinedVariableFunction) @@ -229,7 +236,7 @@ var PropertyListVariableExpander = (function () { } if (type !== "string") continue; - var expandedValue = expandString(key, value, env); + var expandedValue = expandString(key, value, env, []); if (expandedValue !== value) obj[key] = expandedValue; } diff --git a/share/qbs/imports/qbs/ModUtils/utils.js b/share/qbs/imports/qbs/ModUtils/utils.js index dfb1f7639..a1ede5344 100644 --- a/share/qbs/imports/qbs/ModUtils/utils.js +++ b/share/qbs/imports/qbs/ModUtils/utils.js @@ -597,6 +597,8 @@ function guessArchitecture(m) { architecture = "hcs12"; } else if (hasAnyOf(m, ["__e2k__"])) { architecture = "e2k"; + } else if (hasAnyOf(m, ["__hppa__"])) { + architecture = "hppa"; } } diff --git a/share/qbs/imports/qbs/Probes/AndroidSdkProbe.qbs b/share/qbs/imports/qbs/Probes/AndroidSdkProbe.qbs index 28375a469..5b777d3e2 100644 --- a/share/qbs/imports/qbs/Probes/AndroidSdkProbe.qbs +++ b/share/qbs/imports/qbs/Probes/AndroidSdkProbe.qbs @@ -35,7 +35,7 @@ import qbs.Host import "../../../modules/Android/sdk/utils.js" as SdkUtils import "../../../modules/Android/android-utils.js" as AndroidUtils -BinaryProbe { +PathProbe { environmentPaths: Environment.getEnv("ANDROID_HOME") platformSearchPaths: { if (Host.os().contains("windows")) @@ -54,21 +54,17 @@ BinaryProbe { property string platform configure: { - var suffixes = nameSuffixes || [""]; var i, allPaths = (environmentPaths || []).concat(platformSearchPaths || []); candidatePaths = allPaths; for (i in allPaths) { - for (var j in suffixes) { - if (File.exists(FileInfo.joinPaths(allPaths[i], - "tools", "android" + suffixes[j]))) { - path = allPaths[i]; - buildToolsVersions = SdkUtils.availableBuildToolsVersions(path) - buildToolsVersion = buildToolsVersions[buildToolsVersions.length - 1]; - platforms = AndroidUtils.availablePlatforms(path) - platform = platforms[platforms.length - 1]; - found = true; - return; - } + if (File.exists(FileInfo.joinPaths(allPaths[i], "build-tools"))) { + path = allPaths[i]; + buildToolsVersions = SdkUtils.availableBuildToolsVersions(path) + buildToolsVersion = buildToolsVersions[buildToolsVersions.length - 1]; + platforms = AndroidUtils.availablePlatforms(path) + platform = platforms[platforms.length - 1]; + found = true; + return; } } } diff --git a/share/qbs/imports/qbs/Probes/ConanfileProbe.qbs b/share/qbs/imports/qbs/Probes/ConanfileProbe.qbs index 32fbcace2..097b1b6f1 100644 --- a/share/qbs/imports/qbs/Probes/ConanfileProbe.qbs +++ b/share/qbs/imports/qbs/Probes/ConanfileProbe.qbs @@ -31,7 +31,6 @@ import qbs.Process import qbs.File import qbs.FileInfo -import qbs.Host import qbs.TextFile import qbs.Utilities @@ -40,7 +39,7 @@ Probe { property stringList additionalArguments: [] property path conanfilePath property path packageReference - property path executable: "conan" + (Host.os().contains("windows") ? ".exe": "") + property path executable: "conan" + FileInfo.executableSuffix() property stringList generators: ["json"] property var options property var settings diff --git a/share/qbs/imports/qbs/Probes/QbsPkgConfigProbe.qbs b/share/qbs/imports/qbs/Probes/QbsPkgConfigProbe.qbs new file mode 100644 index 000000000..47f162955 --- /dev/null +++ b/share/qbs/imports/qbs/Probes/QbsPkgConfigProbe.qbs @@ -0,0 +1,61 @@ +/**************************************************************************** +** +** Copyright (C) 2021 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-pkg-config-probe.js" as PkgConfigProbeConfigure + +Probe { + // Inputs + + property string _executableFilePath + property stringList _extraPaths + property stringList _libDirs + property bool _staticMode: false + + property path _sysroot + + // TODO: deprecate in 2.2, remove in 2.3 + property bool _mergeDependencies: false + + // Output + property var packages + property var packagesByModuleName + property var brokenPackages + property varList qtInfos + + configure: { + var result = PkgConfigProbeConfigure.configure( + _executableFilePath, _extraPaths, _libDirs, _staticMode, _sysroot, _mergeDependencies); + packages = result.packages; + packagesByModuleName = result.packagesByModuleName; + brokenPackages = result.brokenPackages; + qtInfos = result.qtInfos; + found = true; + } +} diff --git a/share/qbs/imports/qbs/Probes/QmakeProbe.qbs b/share/qbs/imports/qbs/Probes/QmakeProbe.qbs new file mode 100644 index 000000000..c50c6c851 --- /dev/null +++ b/share/qbs/imports/qbs/Probes/QmakeProbe.qbs @@ -0,0 +1,41 @@ + +/**************************************************************************** +** +** Copyright (C) 2023 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 "qmake-probe.js" as QmakeProbeConfigure + +Probe { + property stringList qmakePaths + property varList qtInfos + + configure: { + qtInfos = QmakeProbeConfigure.configure(qmakePaths); + } +} diff --git a/share/qbs/imports/qbs/Probes/qbs-pkg-config-probe.js b/share/qbs/imports/qbs/Probes/qbs-pkg-config-probe.js new file mode 100644 index 000000000..a14827353 --- /dev/null +++ b/share/qbs/imports/qbs/Probes/qbs-pkg-config-probe.js @@ -0,0 +1,135 @@ +/**************************************************************************** +** +** Copyright (C) 2023 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. +** +****************************************************************************/ + +var Environment = require("qbs.Environment"); +var File = require("qbs.File"); +var FileInfo = require("qbs.FileInfo"); +var PkgConfig = require("qbs.PkgConfig"); +var ProviderUtils = require("qbs.ProviderUtils"); +var Process = require("qbs.Process"); +var QmakeProbeConfigure = require("qmake-probe.js"); + +// We should probably use BinaryProbe instead in the provider +function getPkgConfigExecutable() { + function splitNonEmpty(s, c) { return s.split(c).filter(function(e) { return e; }) } + + var pathValue = Environment.getEnv("PATH"); + if (!pathValue) + return undefined; + var dirs = splitNonEmpty(pathValue, FileInfo.pathListSeparator()); + for (var i = 0; i < dirs.length; ++i) { + var candidate = + FileInfo.joinPaths(dirs[i], "pkg-config" + FileInfo.executableSuffix()); + var canonicalCandidate = FileInfo.canonicalPath(candidate); + if (!canonicalCandidate || !File.exists(canonicalCandidate)) + continue; + return canonicalCandidate; + } + return undefined; +} + +function configureQt(pkg) { + var packageName = pkg.baseFileName; + if (packageName === "QtCore" + || packageName === "Qt5Core" + || packageName === "Qt6Core") { + var binDir = pkg.variables["bindir"] || pkg.variables["host_bins"]; + if (!binDir) { + if (packageName === "QtCore") { // Qt4 does not have host_bins + var mocLocation = pkg.variables["moc_location"]; + if (!mocLocation) { + console.warn("No moc_location variable in " + packageName); + return; + } + binDir = FileInfo.path(mocLocation); + } else { + console.warn("No 'bindir' or 'host_bins' variable in " + packageName); + return; + } + } + var suffix = FileInfo.executableSuffix(); + var qmakePaths = [FileInfo.joinPaths(binDir, "qmake" + suffix)]; + return QmakeProbeConfigure.configure(qmakePaths); + } +} + +function configure( + executableFilePath, extraPaths, libDirs, staticMode, sysroot, mergeDependencies) { + + var result = {}; + result.packages = []; + result.packagesByModuleName = {}; + result.brokenPackages = []; + result.qtInfos = {}; + + var options = {}; + options.libDirs = libDirs; + options.sysroot = sysroot; + if (options.sysroot) + options.allowSystemLibraryPaths = true; + options.staticMode = staticMode; + options.mergeDependencies = mergeDependencies; + options.extraPaths = extraPaths; + if (options.sysroot && !options.libDirs) { + options.libDirs = [ + options.sysroot + "/usr/lib/pkgconfig", + options.sysroot + "/usr/share/pkgconfig" + ]; + } + if (!options.libDirs) { + // if we have pkg-config installed, let's ask it for its search paths (since + // built-in search paths can differ between platforms) + var executable = executableFilePath ? executableFilePath : getPkgConfigExecutable(); + if (executable) { + var p = new Process() + if (p.exec(executable, ['pkg-config', '--variable=pc_path']) === 0) { + var stdout = p.readStdOut().trim(); + // TODO: pathListSeparator? depends on what pkg-config prints on Windows + options.libDirs = stdout ? stdout.split(':'): []; + } + } + } + var pkgConfig = new PkgConfig(options); + result.packages = pkgConfig.packages(); + for (var packageName in result.packages) { + var pkg = result.packages[packageName]; + var moduleName = ProviderUtils.pkgConfigToModuleName(packageName); + result.packagesByModuleName[moduleName] = pkg; + + if (packageName.startsWith("Qt")) { + if (!sysroot) { + var infos = configureQt(pkg); + if (infos !== undefined) + result.qtInfos = infos; + } + } + } + return result; +} diff --git a/share/qbs/imports/qbs/Probes/qmake-probe.js b/share/qbs/imports/qbs/Probes/qmake-probe.js new file mode 100644 index 000000000..ad0cb3fa7 --- /dev/null +++ b/share/qbs/imports/qbs/Probes/qmake-probe.js @@ -0,0 +1,1249 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +var Environment = require("qbs.Environment"); +var File = require("qbs.File"); +var FileInfo = require("qbs.FileInfo"); +var Host = require("qbs.Host"); +var Process = require("qbs.Process"); +var ProviderUtils = require("qbs.ProviderUtils"); +var TextFile = require("qbs.TextFile"); +var Utilities = require("qbs.Utilities"); + +function splitNonEmpty(s, c) { return s.split(c).filter(function(e) { return e; }); } + +function getQmakeFilePaths(qmakeFilePaths) { + if (qmakeFilePaths && qmakeFilePaths.length > 0) + return qmakeFilePaths; + console.info("Detecting Qt installations..."); + var filePaths = []; + var pathValue = Environment.getEnv("PATH"); + if (pathValue) { + var dirs = splitNonEmpty(pathValue, FileInfo.pathListSeparator()); + for (var i = 0; i < dirs.length; ++i) { + var candidate = FileInfo.joinPaths(dirs[i], "qmake" + FileInfo.executableSuffix()); + var canonicalCandidate = FileInfo.canonicalPath(candidate); + if (!canonicalCandidate || !File.exists(canonicalCandidate)) + continue; + if (FileInfo.completeBaseName(canonicalCandidate) !== "qtchooser") + candidate = canonicalCandidate; + if (!filePaths.contains(candidate)) { + console.info("Found Qt at '" + FileInfo.toNativeSeparators(candidate) + "'."); + filePaths.push(candidate); + } + } + } + if (filePaths.length === 0) { + console.warn("Could not find any qmake executables in PATH. Either make sure a qmake " + + "executable is present in PATH or set the moduleProviders.Qt.qmakeFilePaths property " + + "to point a qmake executable."); + } + return filePaths; +} + +function queryQmake(qmakeFilePath) { + var qmakeProcess = new Process; + qmakeProcess.exec(qmakeFilePath, ["-query"]); + if (qmakeProcess.exitCode() !== 0) { + throw "The qmake executable '" + FileInfo.toNativeSeparators(qmakeFilePath) + + "' failed with exit code " + qmakeProcess.exitCode() + "."; + } + var queryResult = {}; + while (!qmakeProcess.atEnd()) { + var line = qmakeProcess.readLine(); + var index = (line || "").indexOf(":"); + if (index !== -1) + queryResult[line.slice(0, index)] = line.slice(index + 1).trim(); + } + return queryResult; +} + +function pathQueryValue(queryResult, key) { + var p = queryResult[key]; + if (p) + return FileInfo.fromNativeSeparators(p); +} + +function readFileContent(filePath) { + var f = new TextFile(filePath, TextFile.ReadOnly); + var content = f.readAll(); + f.close(); + return content; +} + +// TODO: Don't do the split every time... +function configVariable(configContent, key) { + var configContentLines = configContent.split('\n'); + var regexp = new RegExp("^\\s*" + key + "\\s*\\+{0,1}=(.*)"); + for (var i = 0; i < configContentLines.length; ++i) { + var line = configContentLines[i]; + var match = regexp.exec(line); + if (match) + return match[1].trim(); + } +} + +function configVariableItems(configContent, key) { + return splitNonEmpty(configVariable(configContent, key), ' '); +} + +function msvcCompilerVersionForYear(year) { + var mapping = { + "2005": "14", "2008": "15", "2010": "16", "2012": "17", "2013": "18", "2015": "19", + "2017": "19.1", "2019": "19.2" + }; + return mapping[year]; +} + +function msvcCompilerVersionFromMkspecName(mkspecName) { + return msvcCompilerVersionForYear(mkspecName.slice(msvcPrefix().length)); +} + +function addQtBuildVariant(qtProps, buildVariantName) { + if (qtProps.qtConfigItems.contains(buildVariantName)) + qtProps.buildVariant.push(buildVariantName); +} + +function checkForStaticBuild(qtProps) { + if (qtProps.qtMajorVersion >= 5) + return qtProps.qtConfigItems.contains("static"); + if (qtProps.frameworkBuild) + return false; // there are no Qt4 static frameworks + var isWin = qtProps.mkspecName.startsWith("win"); + var libDir = isWin ? qtProps.binaryPath : qtProps.libraryPath; + var coreLibFiles = File.directoryEntries(libDir, File.Files) + .filter(function(fp) { return fp.contains("Core"); }); + if (coreLibFiles.length === 0) + throw "Could not determine whether Qt is a static build."; + for (var i = 0; i < coreLibFiles.length; ++i) { + if (Utilities.isSharedLibrary(coreLibFiles[i])) + return false; + } + return true; +} + +function guessMinimumWindowsVersion(qtProps) { + if (qtProps.mkspecName.startsWith("winrt-")) + return "10.0"; + if (!ProviderUtils.isDesktopWindowsQt(qtProps)) + return ""; + if (qtProps.qtMajorVersion >= 6) + return "10.0"; + if (qtProps.architecture === "x86_64" || qtProps.architecture === "ia64") + return "5.2" + var match = qtProps.mkspecName.match(/^win32-msvc(\d+)$/); + if (match) { + var msvcVersion = match[1]; + if (msvcVersion < 2012) + return "5.0"; + return "5.1"; + } + return qtProps.qtMajorVersion < 5 ? "5.0" : "5.1"; +} + +function fillEntryPointLibs(qtProps, debug) { + result = []; + var isMinGW = ProviderUtils.isMinGwQt(qtProps); + + // Some Linux distributions rename the qtmain library. + var qtMainCandidates = ["qtmain"]; + if (isMinGW && qtProps.qtMajorVersion === 5) + qtMainCandidates.push("qt5main"); + if (qtProps.qtMajorVersion === 6) + qtMainCandidates.push("Qt6EntryPoint"); + + for (var i = 0; i < qtMainCandidates.length; ++i) { + var baseNameCandidate = qtMainCandidates[i]; + var qtmain = qtProps.libraryPath + '/'; + if (isMinGW) + qtmain += "lib"; + qtmain += baseNameCandidate + qtProps.qtLibInfix; + if (debug && ProviderUtils.qtNeedsDSuffix(qtProps)) + qtmain += 'd'; + if (isMinGW) { + qtmain += ".a"; + } else { + qtmain += ".lib"; + if (Utilities.versionCompare(qtProps.qtVersion, "5.4.0") >= 0) + result.push("Shell32.lib"); + } + if (File.exists(qtmain)) { + result.push(qtmain); + break; + } + } + if (result.length === 0) { + console.warn("Could not find the qtmain library at '" + + FileInfo.toNativeSeparators(qtProps.libraryPath) + + "'. You will not be able to link Qt applications."); + } + return result; +} + +function getQtProperties(qmakeFilePath) { + var queryResult = queryQmake(qmakeFilePath); + var qtProps = {}; + qtProps.installPrefixPath = pathQueryValue(queryResult, "QT_INSTALL_PREFIX"); + qtProps.documentationPath = pathQueryValue(queryResult, "QT_INSTALL_DOCS"); + qtProps.includePath = pathQueryValue(queryResult, "QT_INSTALL_HEADERS"); + qtProps.libraryPath = pathQueryValue(queryResult, "QT_INSTALL_LIBS"); + qtProps.hostLibraryPath = pathQueryValue(queryResult, "QT_HOST_LIBS"); + qtProps.binaryPath = pathQueryValue(queryResult, "QT_HOST_BINS") + || pathQueryValue(queryResult, "QT_INSTALL_BINS"); + qtProps.installPath = pathQueryValue(queryResult, "QT_INSTALL_BINS"); + qtProps.documentationPath = pathQueryValue(queryResult, "QT_INSTALL_DOCS"); + qtProps.pluginPath = pathQueryValue(queryResult, "QT_INSTALL_PLUGINS"); + qtProps.qmlPath = pathQueryValue(queryResult, "QT_INSTALL_QML"); + qtProps.qmlImportPath = pathQueryValue(queryResult, "QT_INSTALL_IMPORTS"); + qtProps.qtVersion = queryResult.QT_VERSION; + + var mkspecsBaseSrcPath; + if (Utilities.versionCompare(qtProps.qtVersion, "5") >= 0) { + qtProps.mkspecBasePath = FileInfo.joinPaths(pathQueryValue(queryResult, "QT_HOST_DATA"), + "mkspecs"); + mkspecsBaseSrcPath = FileInfo.joinPaths(pathQueryValue(queryResult, "QT_HOST_DATA/src"), + "mkspecs"); + } else { + qtProps.mkspecBasePath = FileInfo.joinPaths + (pathQueryValue(queryResult, "QT_INSTALL_DATA"), "mkspecs"); + } + + if (Utilities.versionCompare(qtProps.qtVersion, "6") >= 0) { + qtProps.libExecPath = pathQueryValue(queryResult, "QT_HOST_LIBEXECS") + || pathQueryValue(queryResult, "QT_INSTALL_LIBEXECS"); + } + + // QML tools were only moved in Qt 6.2. + qtProps.qmlLibExecPath = Utilities.versionCompare(qtProps.qtVersion, "6.2") >= 0 + ? qtProps.libExecPath : qtProps.binaryPath; + + // qhelpgenerator was only moved in Qt 6.3. + qtProps.helpGeneratorLibExecPath = Utilities.versionCompare(qtProps.qtVersion, "6.3") >= 0 + ? qtProps.libExecPath : qtProps.binaryPath; + + if (!File.exists(qtProps.mkspecBasePath)) + throw "Cannot extract the mkspecs directory."; + + var qconfigContent = readFileContent(FileInfo.joinPaths(qtProps.mkspecBasePath, + "qconfig.pri")); + qtProps.qtMajorVersion = parseInt(configVariable(qconfigContent, "QT_MAJOR_VERSION")); + qtProps.qtMinorVersion = parseInt(configVariable(qconfigContent, "QT_MINOR_VERSION")); + qtProps.qtPatchVersion = parseInt(configVariable(qconfigContent, "QT_PATCH_VERSION")); + qtProps.qtNameSpace = configVariable(qconfigContent, "QT_NAMESPACE"); + qtProps.qtLibInfix = configVariable(qconfigContent, "QT_LIBINFIX") || ""; + qtProps.architecture = configVariable(qconfigContent, "QT_TARGET_ARCH") + || configVariable(qconfigContent, "QT_ARCH") || "x86"; + qtProps.configItems = configVariableItems(qconfigContent, "CONFIG"); + qtProps.qtConfigItems = configVariableItems(qconfigContent, "QT_CONFIG"); + + // retrieve the mkspec + if (qtProps.qtMajorVersion >= 5) { + qtProps.mkspecName = queryResult.QMAKE_XSPEC; + qtProps.mkspecPath = FileInfo.joinPaths(qtProps.mkspecBasePath, qtProps.mkspecName); + if (mkspecsBaseSrcPath && !File.exists(qtProps.mkspecPath)) + qtProps.mkspecPath = FileInfo.joinPaths(mkspecsBaseSrcPath, qtProps.mkspecName); + } else { + if (Host.os().contains("windows")) { + var baseDirPath = FileInfo.joinPaths(qtProps.mkspecBasePath, "default"); + var fileContent = readFileContent(FileInfo.joinPaths(baseDirPath, "qmake.conf")); + qtProps.mkspecPath = configVariable(fileContent, "QMAKESPEC_ORIGINAL"); + if (!File.exists(qtProps.mkspecPath)) { + // Work around QTBUG-28792. + // The value of QMAKESPEC_ORIGINAL is wrong for MinGW packages. Y u h8 me? + var match = fileContent.exec(/\binclude\(([^)]+)\/qmake\.conf\)/m); + if (match) { + qtProps.mkspecPath = FileInfo.cleanPath(FileInfo.joinPaths( + baseDirPath, match[1])); + } + } + } else { + qtProps.mkspecPath = FileInfo.canonicalPath( + FileInfo.joinPaths(qtProps.mkspecBasePath, "default")); + } + + // E.g. in qmake.conf for Qt 4.8/mingw we find this gem: + // QMAKESPEC_ORIGINAL=C:\\Qt\\Qt\\4.8\\mingw482\\mkspecs\\win32-g++ + qtProps.mkspecPath = FileInfo.cleanPath(qtProps.mkspecPath); + + qtProps.mkspecName = qtProps.mkspecPath; + var idx = qtProps.mkspecName.lastIndexOf('/'); + if (idx !== -1) + qtProps.mkspecName = qtProps.mkspecName.slice(idx + 1); + } + if (!File.exists(qtProps.mkspecPath)) + throw "mkspec '" + FileInfo.toNativeSeparators(qtProps.mkspecPath) + "' does not exist"; + + // Starting with qt 5.14, android sdk provides multi-abi + if (Utilities.versionCompare(qtProps.qtVersion, "5.14.0") >= 0 + && qtProps.mkspecPath.contains("android")) { + var qdeviceContent = readFileContent(FileInfo.joinPaths(qtProps.mkspecBasePath, + "qdevice.pri")); + qtProps.androidAbis = configVariable(qdeviceContent, "DEFAULT_ANDROID_ABIS").split(' '); + } + + // determine MSVC version + if (ProviderUtils.isMsvcQt(qtProps)) { + var msvcMajor = configVariable(qconfigContent, "QT_MSVC_MAJOR_VERSION"); + var msvcMinor = configVariable(qconfigContent, "QT_MSVC_MINOR_VERSION"); + var msvcPatch = configVariable(qconfigContent, "QT_MSVC_PATCH_VERSION"); + if (msvcMajor && msvcMinor && msvcPatch) + qtProps.msvcVersion = msvcMajor + "." + msvcMinor + "." + msvcPatch; + else + qtProps.msvcVersion = msvcCompilerVersionFromMkspecName(qtProps.mkspecName); + } + + // determine whether we have a framework build + qtProps.frameworkBuild = qtProps.mkspecPath.contains("macx") + && qtProps.configItems.contains("qt_framework"); + + // determine whether Qt is built with debug, release or both + qtProps.buildVariant = []; + addQtBuildVariant(qtProps, "debug"); + addQtBuildVariant(qtProps, "release"); + + qtProps.staticBuild = checkForStaticBuild(qtProps); + + // determine whether user apps require C++11 + if (qtProps.qtConfigItems.contains("c++11") && qtProps.staticBuild) + qtProps.configItems.push("c++11"); + + // Set the minimum operating system versions appropriate for this Qt version + qtProps.windowsVersion = guessMinimumWindowsVersion(qtProps); + if (qtProps.windowsVersion) { // Is target OS Windows? + if (qtProps.buildVariant.contains("debug")) + qtProps.entryPointLibsDebug = fillEntryPointLibs(qtProps, true); + if (qtProps.buildVariant.contains("release")) + qtProps.entryPointLibsRelease = fillEntryPointLibs(qtProps, false); + } else if (qtProps.mkspecPath.contains("macx")) { + if (qtProps.qtMajorVersion >= 5) { + var lines = getFileContentsRecursively(FileInfo.joinPaths(qtProps.mkspecPath, + "qmake.conf")); + for (var i = 0; i < lines.length; ++i) { + var line = lines[i].trim(); + match = line.match + (/^QMAKE_(MACOSX|IOS|TVOS|WATCHOS)_DEPLOYMENT_TARGET\s*=\s*(.*)\s*$/); + if (match) { + var platform = match[1]; + var version = match[2]; + if (platform === "MACOSX") + qtProps.macosVersion = version; + else if (platform === "IOS") + qtProps.iosVersion = version; + else if (platform === "TVOS") + qtProps.tvosVersion = version; + else if (platform === "WATCHOS") + qtProps.watchosVersion = version; + } + } + var isMac = qtProps.mkspecName !== "macx-ios-clang" + && qtProps.mkspecName !== "macx-tvos-clang" + && qtProps.mkspecName !== "macx-watchos-clang"; + if (isMac) { + // Qt 5.0.x placed the minimum version in a different file + if (!qtProps.macosVersion) + qtProps.macosVersion = "10.6"; + + // If we're using C++11 with libc++, make sure the deployment target is >= 10.7 + if (Utilities.versionCompare(qtProps.macosVersion, "10, 7") < 0 + && qtProps.qtConfigItems.contains("c++11")) { + qtProps.macosVersion = "10.7"; + } + } + } else if (qtProps.qtMajorVersion === 4 && qtProps.qtMinorVersion >= 6) { + var qconfigDir = qtProps.frameworkBuild + ? FileInfo.joinPaths(qtProps.libraryPath, "QtCore.framework", "Headers") + : FileInfo.joinPaths(qtProps.includePath, "Qt"); + try { + var qconfig = new TextFile(FileInfo.joinPaths(qconfigDir, "qconfig.h"), + TextFile.ReadOnly); + var qtCocoaBuild = false; + var ok = true; + do { + line = qconfig.readLine(); + if (line.match(/\s*#define\s+QT_MAC_USE_COCOA\s+1\s*/)) { + qtCocoaBuild = true; + break; + } + } while (!qconfig.atEof()); + qtProps.macosVersion = qtCocoaBuild ? "10.5" : "10.4"; + } + catch (e) {} + finally { + if (qconfig) + qconfig.close(); + } + if (!qtProps.macosVersion) { + throw "Could not determine whether Qt is using Cocoa or Carbon from '" + + FileInfo.toNativeSeparators(qconfig.filePath()) + "'."; + } + } + } else if (qtProps.mkspecPath.contains("android")) { + if (qtProps.qtMajorVersion >= 5) + qtProps.androidVersion = "2.3"; + else if (qtProps.qtMajorVersion === 4 && qtProps.qtMinorVersion >= 8) + qtProps.androidVersion = "1.6"; // Necessitas + } + return qtProps; +} + +function makePluginData() { + var pluginData = {}; + pluginData.type = undefined; + pluginData.className = undefined; + pluginData.autoLoad = true; + pluginData["extends"] = []; + return pluginData; +} + +function makeQtModuleInfo(name, qbsName, deps) { + var moduleInfo = {}; + moduleInfo.name = name; // As in the path to the headers and ".name" in the pri files. + if (moduleInfo.name === undefined) + moduleInfo.name = ""; + moduleInfo.qbsName = qbsName; // Lower-case version without "qt" prefix. + moduleInfo.dependencies = deps || []; // qbs names. + if (moduleInfo.qbsName && moduleInfo.qbsName !== "core" + && !moduleInfo.dependencies.contains("core")) { + moduleInfo.dependencies.unshift("core"); + } + moduleInfo.isPrivate = qbsName && qbsName.endsWith("-private"); + moduleInfo.hasLibrary = !moduleInfo.isPrivate; + moduleInfo.isStaticLibrary = false; + moduleInfo.isPlugin = false; + moduleInfo.mustExist = true; + moduleInfo.modulePrefix = ""; // empty value means "Qt". + moduleInfo.version = undefined; + moduleInfo.includePaths = []; + moduleInfo.compilerDefines = []; + moduleInfo.staticLibrariesDebug = []; + moduleInfo.staticLibrariesRelease = []; + moduleInfo.dynamicLibrariesDebug = []; + moduleInfo.dynamicLibrariesRelease = []; + moduleInfo.linkerFlagsDebug = []; + moduleInfo.linkerFlagsRelease = []; + moduleInfo.libFilePathDebug = undefined; + moduleInfo.libFilePathRelease = undefined; + moduleInfo.frameworksDebug = []; + moduleInfo.frameworksRelease = []; + moduleInfo.frameworkPathsDebug = []; + moduleInfo.frameworkPathsRelease = []; + moduleInfo.libraryPaths = []; + moduleInfo.libDir = ""; + moduleInfo.config = []; + moduleInfo.supportedPluginTypes = []; + moduleInfo.pluginData = makePluginData(); + return moduleInfo; +} + +function frameworkHeadersPath(qtModuleInfo, qtProps) { + return FileInfo.joinPaths(qtProps.libraryPath, qtModuleInfo.name + ".framework", "Headers"); +} + +function qt4ModuleIncludePaths(qtModuleInfo, qtProps) { + var paths = []; + if (ProviderUtils.qtIsFramework(qtModuleInfo, qtProps)) + paths.push(frameworkHeadersPath(qtModuleInfo, qtProps)); + else + paths.push(qtProps.includePath, FileInfo.joinPaths(qtProps.includePath, qtModuleInfo.name)); + return paths; +} + +// We erroneously called the "testlib" module "test" for quite a while. Let's not punish users +// for that. +function addTestModule(modules) { + var testModule = makeQtModuleInfo("QtTest", "test", ["testlib"]); + testModule.hasLibrary = false; + modules.push(testModule); +} + +// See above. +function addDesignerComponentsModule(modules) { + var module = makeQtModuleInfo("QtDesignerComponents", "designercomponents", + ["designercomponents-private"]); + module.hasLibrary = false; + modules.push(module); +} + +function guessLibraryFilePath(prlFilePath, libDir, qtProps) { + var baseName = FileInfo.baseName(prlFilePath); + var prefixCandidates = ["", "lib"]; + var suffixCandidates = ["so." + qtProps.qtVersion, "so", "a", "lib", "dll.a"]; + for (var i = 0; i < prefixCandidates.length; ++i) { + var prefix = prefixCandidates[i]; + for (var j = 0; j < suffixCandidates.length; ++j) { + var suffix = suffixCandidates[j]; + var candidate = FileInfo.joinPaths(libDir, prefix + baseName + '.' + suffix); + if (File.exists(candidate)) + return candidate; + } + } +} + +function doReplaceQtLibNamesWithFilePath(namePathMap, libList) { + for (var i = 0; i < libList.length; ++i) { + var lib = libList[i]; + var path = namePathMap[lib]; + if (path) + libList[i] = path; + } +} + +function replaceQtLibNamesWithFilePath(modules, qtProps) { + // We don't want to add the libraries for Qt modules via "-l", because of the + // danger that a wrong one will be picked up, e.g. from /usr/lib. Instead, + // we pull them in using the full file path. + var linkerNamesToFilePathsDebug = {}; + var linkerNamesToFilePathsRelease = {}; + for (var i = 0; i < modules.length; ++i) { + var m = modules[i]; + linkerNamesToFilePathsDebug[ + ProviderUtils.qtLibNameForLinker(m, qtProps, true)] = m.libFilePathDebug; + linkerNamesToFilePathsRelease[ + ProviderUtils.qtLibNameForLinker(m, qtProps, false)] = m.libFilePathRelease; + } + for (i = 0; i < modules.length; ++i) { + var module = modules[i]; + doReplaceQtLibNamesWithFilePath(linkerNamesToFilePathsDebug, module.dynamicLibrariesDebug); + doReplaceQtLibNamesWithFilePath(linkerNamesToFilePathsDebug, module.staticLibrariesDebug); + doReplaceQtLibNamesWithFilePath(linkerNamesToFilePathsRelease, + module.dynamicLibrariesRelease); + doReplaceQtLibNamesWithFilePath(linkerNamesToFilePathsRelease, + module.staticLibrariesRelease); + } +} + +function doSetupLibraries(modInfo, qtProps, debugBuild, nonExistingPrlFiles, androidAbi) { + if (!modInfo.hasLibrary) + return; // Can happen for Qt4 convenience modules, like "widgets". + + if (debugBuild) { + if (!qtProps.buildVariant.contains("debug")) + return; + var modulesNeverBuiltAsDebug = ["bootstrap", "qmldevtools"]; + for (var i = 0; i < modulesNeverBuiltAsDebug.length; ++i) { + var m = modulesNeverBuiltAsDebug[i]; + if (modInfo.qbsName === m || modInfo.qbsName === m + "-private") + return; + } + } else if (!qtProps.buildVariant.contains("release")) { + return; + } + + var libs = modInfo.isStaticLibrary + ? (debugBuild ? modInfo.staticLibrariesDebug : modInfo.staticLibrariesRelease) + : (debugBuild ? modInfo.dynamicLibrariesDebug : modInfo.dynamicLibrariesRelease); + var frameworks = debugBuild ? modInfo.frameworksDebug : modInfo.frameworksRelease; + var frameworkPaths = debugBuild ? modInfo.frameworkPathsDebug : modInfo.frameworkPathsRelease; + var flags = debugBuild ? modInfo.linkerFlagsDebug : modInfo.linkerFlagsRelease; + var libFilePath; + + if (qtProps.mkspecName.contains("ios") && modInfo.isStaticLibrary) { + libs.push("z", "m"); + if (qtProps.qtMajorVersion === 5 && qtProps.qtMinorVersion < 8) { + var platformSupportModule = makeQtModuleInfo("QtPlatformSupport", "platformsupport"); + libs.push(ProviderUtils.qtLibNameForLinker(platformSupportModule, qtProps, debugBuild)); + } + if (modInfo.name === "qios") { + flags.push("-force_load", FileInfo.joinPaths( + qtProps.pluginPath, "platforms", + ProviderUtils.qtLibBaseName( + modInfo, "libqios", debugBuild, qtProps) + ".a")); + } + } + var prlFilePath = modInfo.isPlugin + ? FileInfo.joinPaths(qtProps.pluginPath, modInfo.pluginData.type) + : (modInfo.libDir ? modInfo.libDir : qtProps.libraryPath); + var libDir = prlFilePath; + if (ProviderUtils.qtIsFramework(modInfo, qtProps)) { + prlFilePath = FileInfo.joinPaths( + prlFilePath, + ProviderUtils.qtLibraryBaseName(modInfo, qtProps, false) + ".framework"); + libDir = prlFilePath; + if (Utilities.versionCompare(qtProps.qtVersion, "5.14") >= 0) + prlFilePath = FileInfo.joinPaths(prlFilePath, "Resources"); + } + var baseName = ProviderUtils.qtLibraryBaseName(modInfo, qtProps, debugBuild); + if (!qtProps.mkspecName.startsWith("win") && !ProviderUtils.qtIsFramework(modInfo, qtProps)) + baseName = "lib" + baseName; + prlFilePath = FileInfo.joinPaths(prlFilePath, baseName); + var isNonStaticQt4OnWindows = qtProps.mkspecName.startsWith("win") + && !modInfo.isStaticLibrary && qtProps.qtMajorVersion < 5; + if (isNonStaticQt4OnWindows) + prlFilePath = prlFilePath.slice(0, prlFilePath.length - 1); // The prl file base name does *not* contain the version number... + // qt for android versions 6.0 and 6.1 don't have the architecture suffix in the prl file + if (androidAbi.length > 0 + && modInfo.name !== "QtBootstrap" + && (modInfo.name !== "QtQmlDevTools" || modInfo.name === "QtQmlDevTools" + && Utilities.versionCompare(qtProps.qtVersion, "6.2") >= 0) + && (Utilities.versionCompare(qtProps.qtVersion, "6.0") < 0 + || Utilities.versionCompare(qtProps.qtVersion, "6.2") >= 0)) { + prlFilePath += "_"; + prlFilePath += androidAbi; + } + + prlFilePath += ".prl"; + + try { + var prlFile = new TextFile(prlFilePath, TextFile.ReadOnly); + while (!prlFile.atEof()) { + var line = prlFile.readLine().trim(); + var equalsOffset = line.indexOf('='); + if (equalsOffset === -1) + continue; + if (line.startsWith("QMAKE_PRL_TARGET")) { + var isMingw = qtProps.mkspecName.startsWith("win") + && qtProps.mkspecName.contains("g++"); + var isQtVersionBefore56 = qtProps.qtMajorVersion < 5 + || (qtProps.qtMajorVersion === 5 && qtProps.qtMinorVersion < 6); + + // QMAKE_PRL_TARGET has a "lib" prefix, except for mingw. + // Of course, the exception has an exception too: For static libs, mingw *does* + // have the "lib" prefix. + var libFileName = ""; + if (isQtVersionBefore56 && qtProps.qtMajorVersion === 5 && isMingw + && !modInfo.isStaticLibrary) { + libFileName += "lib"; + } + + libFileName += line.slice(equalsOffset + 1).trim(); + if (isNonStaticQt4OnWindows) + libFileName += 4; // This is *not* part of QMAKE_PRL_TARGET... + if (isQtVersionBefore56) { + if (qtProps.mkspecName.contains("msvc")) { + libFileName += ".lib"; + } else if (isMingw) { + libFileName += ".a"; + if (!File.exists(FileInfo.joinPaths(libDir, libFileName))) + libFileName = libFileName.slice(0, -2) + ".dll"; + } + } + libFilePath = FileInfo.joinPaths(libDir, libFileName); + continue; + } + if (line.startsWith("QMAKE_PRL_CONFIG")) { + modInfo.config = splitNonEmpty(line.slice(equalsOffset + 1).trim(), ' '); + continue; + } + if (!line.startsWith("QMAKE_PRL_LIBS =")) + continue; + + var parts = extractPaths(line.slice(equalsOffset + 1).trim(), prlFilePath); + for (i = 0; i < parts.length; ++i) { + var part = parts[i]; + part = part.replace("$$[QT_INSTALL_LIBS]", qtProps.libraryPath); + part = part.replace("$$[QT_INSTALL_PLUGINS]", qtProps.pluginPath); + part = part.replace("$$[QT_INSTALL_PREFIX]", qtProps.installPrefixPath); + if (part.startsWith("-l")) { + libs.push(part.slice(2)); + } else if (part.startsWith("-L")) { + modInfo.libraryPaths.push(part.slice(2)); + } else if (part.startsWith("-F")) { + frameworkPaths.push(part.slice(2)); + } else if (part === "-framework") { + if (++i < parts.length) + frameworks.push(parts[i]); + } else if (part === "-pthread") { + // prl files for android have QMAKE_PRL_LIBS = -llog -pthread but the pthread + // functionality is included in libc. + if (androidAbi.length === 0) + libs.push("pthread"); + } else if (part.startsWith('-')) { // Some other option + console.debug("QMAKE_PRL_LIBS contains non-library option '" + part + + "' in file '" + prlFilePath + "'"); + flags.push(part); + } else if (part.startsWith("/LIBPATH:")) { + libraryPaths.push(part.slice(9).replace(/\\/g, '/')); + } else { // Assume it's a file path/name. + libs.push(part.replace(/\\/g, '/')); + } + } + } + } catch (e) { + // qt_ext_lib_extX.pri (usually) don't have a corresponding prl file. + // So the pri file variable QMAKE_LIBS_LIBX points to the library + if (modInfo.isExternal) { + libFilePath = debugBuild ? modInfo.staticLibrariesDebug[0] : + modInfo.staticLibrariesRelease[0]; + } + if (!libFilePath || !File.exists(libFilePath)) + libFilePath = guessLibraryFilePath(prlFilePath, libDir, qtProps); + if (nonExistingPrlFiles.contains(prlFilePath)) + return; + nonExistingPrlFiles.push(prlFilePath); + if (modInfo.mustExist) { + console.warn("Could not open prl file '" + + FileInfo.toNativeSeparators(prlFilePath) + "' for module '" + + modInfo.name + + "' (" + e + "), and failed to deduce the library file path. " + + " This module will likely not be usable by qbs."); + } + } + finally { + if (prlFile) + prlFile.close(); + } + + if (debugBuild) + modInfo.libFilePathDebug = libFilePath; + else + modInfo.libFilePathRelease = libFilePath; +} + +function setupLibraries(qtModuleInfo, qtProps, nonExistingPrlFiles, androidAbi) { + doSetupLibraries(qtModuleInfo, qtProps, true, nonExistingPrlFiles, androidAbi); + doSetupLibraries(qtModuleInfo, qtProps, false, nonExistingPrlFiles, androidAbi); +} + +function allQt4Modules(qtProps) { + // as per http://doc.qt.io/qt-4.8/modules.html + private stuff. + var modules = []; + + var core = makeQtModuleInfo("QtCore", "core"); + core.compilerDefines.push("QT_CORE_LIB"); + if (qtProps.qtNameSpace) + core.compilerDefines.push("QT_NAMESPACE=" + qtProps.qtNameSpace); + modules.push(core, + makeQtModuleInfo("QtCore", "core-private", ["core"]), + makeQtModuleInfo("QtGui", "gui"), + makeQtModuleInfo("QtGui", "gui-private", ["gui"]), + makeQtModuleInfo("QtMultimedia", "multimedia", ["gui", "network"]), + makeQtModuleInfo("QtMultimedia", "multimedia-private", ["multimedia"]), + makeQtModuleInfo("QtNetwork", "network"), + makeQtModuleInfo("QtNetwork", "network-private", ["network"]), + makeQtModuleInfo("QtOpenGL", "opengl", ["gui"]), + makeQtModuleInfo("QtOpenGL", "opengl-private", ["opengl"]), + makeQtModuleInfo("QtOpenVG", "openvg", ["gui"]), + makeQtModuleInfo("QtScript", "script"), + makeQtModuleInfo("QtScript", "script-private", ["script"]), + makeQtModuleInfo("QtScriptTools", "scripttools", ["script", "gui"]), + makeQtModuleInfo("QtScriptTools", "scripttools-private", ["scripttools"]), + makeQtModuleInfo("QtSql", "sql"), + makeQtModuleInfo("QtSql", "sql-private", ["sql"]), + makeQtModuleInfo("QtSvg", "svg", ["gui"]), + makeQtModuleInfo("QtSvg", "svg-private", ["svg"]), + makeQtModuleInfo("QtWebKit", "webkit", ["gui", "network"]), + makeQtModuleInfo("QtWebKit", "webkit-private", ["webkit"]), + makeQtModuleInfo("QtXml", "xml"), + makeQtModuleInfo("QtXml", "xml-private", ["xml"]), + makeQtModuleInfo("QtXmlPatterns", "xmlpatterns", ["network"]), + makeQtModuleInfo("QtXmlPatterns", "xmlpatterns-private", ["xmlpatterns"]), + makeQtModuleInfo("QtDeclarative", "declarative", ["gui", "script"]), + makeQtModuleInfo("QtDeclarative", "declarative-private", ["declarative"]), + makeQtModuleInfo("QtDesigner", "designer", ["gui", "xml"]), + makeQtModuleInfo("QtDesigner", "designer-private", ["designer"]), + makeQtModuleInfo("QtUiTools", "uitools"), + makeQtModuleInfo("QtUiTools", "uitools-private", ["uitools"]), + makeQtModuleInfo("QtHelp", "help", ["network", "sql"]), + makeQtModuleInfo("QtHelp", "help-private", ["help"]), + makeQtModuleInfo("QtTest", "testlib"), + makeQtModuleInfo("QtTest", "testlib-private", ["testlib"])); + if (qtProps.mkspecName.startsWith("win")) { + var axcontainer = makeQtModuleInfo("QAxContainer", "axcontainer"); + axcontainer.modulePrefix = "Q"; + axcontainer.isStaticLibrary = true; + axcontainer.includePaths.push(FileInfo.joinPaths(qtProps.includePath, "ActiveQt")); + modules.push(axcontainer); + + var axserver = makeQtModuleInfo("QAxServer", "axserver"); + axserver.modulePrefix = "Q"; + axserver.isStaticLibrary = true; + axserver.compilerDefines.push("QAXSERVER"); + axserver.includePaths.push(FileInfo.joinPaths(qtProps.includePath, "ActiveQt")); + modules.push(axserver); + } else { + modules.push(makeQtModuleInfo("QtDBus", "dbus")); + modules.push(makeQtModuleInfo("QtDBus", "dbus-private", ["dbus"])); + } + + var designerComponentsPrivate = makeQtModuleInfo( + "QtDesignerComponents", "designercomponents-private", + ["gui-private", "designer-private"]); + designerComponentsPrivate.hasLibrary = true; + modules.push(designerComponentsPrivate); + + var phonon = makeQtModuleInfo("Phonon", "phonon"); + phonon.includePaths = qt4ModuleIncludePaths(phonon, qtProps); + modules.push(phonon); + + // Set up include paths that haven't been set up before this point. + for (i = 0; i < modules.length; ++i) { + var module = modules[i]; + if (module.includePaths.length > 0) + continue; + module.includePaths = qt4ModuleIncludePaths(module, qtProps); + } + + // Set up compiler defines haven't been set up before this point. + for (i = 0; i < modules.length; ++i) { + module = modules[i]; + if (module.compilerDefines.length > 0) + continue; + module.compilerDefines.push("QT_" + module.qbsName.toUpperCase() + "_LIB"); + } + + // These are for the convenience of project file authors. It allows them + // to add a dependency to e.g. "Qt.widgets" without a version check. + var virtualModule = makeQtModuleInfo(undefined, "widgets", ["core", "gui"]); + virtualModule.hasLibrary = false; + modules.push(virtualModule); + virtualModule = makeQtModuleInfo(undefined, "quick", ["declarative"]); + virtualModule.hasLibrary = false; + modules.push(virtualModule); + virtualModule = makeQtModuleInfo(undefined, "concurrent"); + virtualModule.hasLibrary = false; + modules.push(virtualModule); + virtualModule = makeQtModuleInfo(undefined, "printsupport", ["core", "gui"]); + virtualModule.hasLibrary = false; + modules.push(virtualModule); + + addTestModule(modules); + addDesignerComponentsModule(modules); + + var modulesThatCanBeDisabled = [ + "xmlpatterns", "multimedia", "phonon", "svg", "webkit", "script", "scripttools", + "declarative", "gui", "dbus", "opengl", "openvg"]; + var nonExistingPrlFiles = []; + for (i = 0; i < modules.length; ++i) { + module = modules[i]; + var name = module.qbsName; + var privateIndex = name.indexOf("-private"); + if (privateIndex !== -1) + name = name.slice(0, privateIndex); + if (modulesThatCanBeDisabled.contains(name)) + module.mustExist = false; + if (qtProps.staticBuild) + module.isStaticLibrary = true; + setupLibraries(module, qtProps, nonExistingPrlFiles, ""); + } + replaceQtLibNamesWithFilePath(modules, qtProps); + + return modules; +} + +function getFileContentsRecursively(filePath) { + var file = new TextFile(filePath, TextFile.ReadOnly); + var lines = splitNonEmpty(file.readAll(), '\n'); + for (var i = 0; i < lines.length; ++i) { + var includeString = "include("; + var line = lines[i].trim(); + if (!line.startsWith(includeString)) + continue; + var offset = includeString.length; + var closingParenPos = line.indexOf(')', offset); + if (closingParenPos === -1) { + console.warn("Invalid include statement in '" + + FileInfo.toNativeSeparators(filePath) + "'"); + continue; + } + var includedFilePath = line.slice(offset, closingParenPos); + if (!FileInfo.isAbsolutePath(includedFilePath)) + includedFilePath = FileInfo.joinPaths(FileInfo.path(filePath), includedFilePath); + var includedContents = getFileContentsRecursively(includedFilePath); + var j = i; + for (var k = 0; k < includedContents.length; ++k) + lines.splice(++j, 0, includedContents[k]); + lines.splice(i--, 1); + } + file.close(); + return lines; +} + +function extractPaths(rhs, filePath) { + var paths = []; + var startIndex = 0; + for (;;) { + while (startIndex < rhs.length && rhs.charAt(startIndex) === ' ') + ++startIndex; + if (startIndex >= rhs.length) + break; + var endIndex; + if (rhs.charAt(startIndex) === '"') { + ++startIndex; + endIndex = rhs.indexOf('"', startIndex); + if (endIndex === -1) { + console.warn("Unmatched quote in file '" + + FileInfo.toNativeSeparators(filePath) + "'"); + break; + } + } else { + endIndex = rhs.indexOf(' ', startIndex + 1); + if (endIndex === -1) + endIndex = rhs.length; + } + paths.push(FileInfo.cleanPath(rhs.slice(startIndex, endIndex) + .replace("$$PWD", FileInfo.path(filePath)))); + startIndex = endIndex + 1; + } + return paths; +} + +function removeDuplicatedDependencyLibs(modules) { + var revDeps = {}; + var currentPath = []; + var getLibraries; + var getLibFilePath; + + function setupReverseDependencies(modules) { + var moduleByName = {}; + for (var i = 0; i < modules.length; ++i) + moduleByName[modules[i].qbsName] = modules[i]; + for (i = 0; i < modules.length; ++i) { + var module = modules[i]; + for (var j = 0; j < module.dependencies.length; ++j) { + var depmod = moduleByName[module.dependencies[j]]; + if (!depmod) + continue; + if (!revDeps[depmod.qbsName]) + revDeps[depmod.qbsName] = []; + revDeps[depmod.qbsName].push(module); + } + } + } + + function roots(modules) { + var result = []; + for (i = 0; i < modules.length; ++i) { + var module = modules[i] + if (module.dependencies.length === 0) + result.push(module); + } + return result; + } + + function traverse(module, libs) { + if (currentPath.contains(module)) + return; + currentPath.push(module); + var moduleLibraryLists = getLibraries(module); + for (var i = 0; i < moduleLibraryLists.length; ++i) { + var modLibList = moduleLibraryLists[i]; + for (j = modLibList.length - 1; j >= 0; --j) { + if (libs.contains(modLibList[j])) + modLibList.splice(j, 1); + } + } + + var libFilePath = getLibFilePath(module); + if (libFilePath) + libs.push(libFilePath); + for (i = 0; i < moduleLibraryLists.length; ++i) + libs = libs.concat(moduleLibraryLists[i]); + libs.sort(); + + var deps = revDeps[module.qbsName]; + for (i = 0; i < (deps || []).length; ++i) + traverse(deps[i], libs); + + currentPath.pop(); + } + + setupReverseDependencies(modules); + + // Traverse the debug variants of modules. + getLibraries = function(module) { + return [module.dynamicLibrariesDebug, module.staticLibrariesDebug]; + }; + getLibFilePath = function(module) { return module.libFilePathDebug; }; + var rootModules = roots(modules); + for (var i = 0; i < rootModules.length; ++i) + traverse(rootModules[i], []); + + // Traverse the release variants of modules. + getLibraries = function(module) { + return [module.dynamicLibrariesRelease, module.staticLibrariesRelease]; + }; + getLibFilePath = function(module) { return module.libFilePathRelease; }; + for (i = 0; i < rootModules.length; ++i) + traverse(rootModules[i], []); +} + +function allQt5Modules(qtProps, androidAbi) { + var nonExistingPrlFiles = []; + var modules = []; + var modulesDir = FileInfo.joinPaths(qtProps.mkspecBasePath, "modules"); + var modulePriFiles = File.directoryEntries(modulesDir, File.Files); + for (var i = 0; i < modulePriFiles.length; ++i) { + var priFileName = modulePriFiles[i]; + var priFilePath = FileInfo.joinPaths(modulesDir, priFileName); + var externalFileNamePrefix = "qt_ext_"; + var moduleFileNamePrefix = "qt_lib_"; + var pluginFileNamePrefix = "qt_plugin_"; + var moduleFileNameSuffix = ".pri"; + var fileHasExternalPrefix = priFileName.startsWith(externalFileNamePrefix); + var fileHasModulePrefix = priFileName.startsWith(moduleFileNamePrefix); + var fileHasPluginPrefix = priFileName.startsWith(pluginFileNamePrefix); + if (!fileHasPluginPrefix && !fileHasModulePrefix && !fileHasExternalPrefix + || !priFileName.endsWith(moduleFileNameSuffix)) { + continue; + } + var moduleInfo = makeQtModuleInfo(); + moduleInfo.isPlugin = fileHasPluginPrefix; + moduleInfo.isExternal = !moduleInfo.isPlugin && !fileHasModulePrefix; + var fileNamePrefix = moduleInfo.isPlugin ? pluginFileNamePrefix : moduleInfo.isExternal + ? externalFileNamePrefix : moduleFileNamePrefix; + moduleInfo.qbsName = priFileName.slice(fileNamePrefix.length, -moduleFileNameSuffix.length); + if (moduleInfo.isPlugin) { + moduleInfo.name = moduleInfo.qbsName; + moduleInfo.isStaticLibrary = true; + } + var moduleKeyPrefix = (moduleInfo.isPlugin ? "QT_PLUGIN" : "QT") + + '.' + moduleInfo.qbsName + '.'; + moduleInfo.qbsName = moduleInfo.qbsName.replace("_private", "-private"); + var hasV2 = false; + var hasModuleEntry = false; + var lines = getFileContentsRecursively(priFilePath); + if (moduleInfo.isExternal) { + moduleInfo.name = "qt" + moduleInfo.qbsName; + moduleInfo.isStaticLibrary = true; + for (var k = 0; k < lines.length; ++k) { + var extLine = lines[k].trim(); + var extFirstEqualsOffset = extLine.indexOf('='); + if (extFirstEqualsOffset === -1) + continue; + var extKey = extLine.slice(0, extFirstEqualsOffset).trim(); + var extValue = extLine.slice(extFirstEqualsOffset + 1).trim(); + if (!extKey.startsWith("QMAKE_") || !extValue) + continue; + + var elements = extKey.split('_'); + if (elements.length >= 3) { + if (elements[1] === "LIBS") { + extValue = extValue.replace("/home/qt/work/qt/qtbase/lib", + qtProps.libraryPath); + extValue = extValue.replace("$$[QT_INSTALL_LIBS]", qtProps.libraryPath); + extValue = extValue.replace("$$[QT_INSTALL_LIBS/get]", qtProps.libraryPath); + if (elements.length === 4 ) { + if (elements[3] === androidAbi) { + moduleInfo.staticLibrariesRelease.push(extValue); + moduleInfo.staticLibrariesDebug.push(extValue); + } + } else if (elements.length === 5 ) { + // That's for "x86_64" + var abi = elements[3] + '_' + elements[4]; + if (abi === androidAbi) { + moduleInfo.staticLibrariesRelease.push(extValue); + moduleInfo.staticLibrariesDebug.push(extValue); + } + } else { + moduleInfo.staticLibrariesRelease.push(extValue); + moduleInfo.staticLibrariesDebug.push(extValue); + } + } else if (elements[1] === "INCDIR") { + moduleInfo.includePaths.push(extValue.replace("$$[QT_INSTALL_HEADERS]", + qtProps.includePath)); + } + } + } + moduleInfo.compilerDefines.push("QT_" + moduleInfo.qbsName.toUpperCase() + "_LIB"); + moduleInfo.mustExist = false; + } else { + for (var j = 0; j < lines.length; ++j) { + var line = lines[j].trim(); + var firstEqualsOffset = line.indexOf('='); + if (firstEqualsOffset === -1) + continue; + var key = line.slice(0, firstEqualsOffset).trim(); + var value = line.slice(firstEqualsOffset + 1).trim(); + if (!key.startsWith(moduleKeyPrefix) || !value) + continue; + if (key.endsWith(".name")) { + moduleInfo.name = value; + } else if (key.endsWith(".module")) { + hasModuleEntry = true; + } else if (key.endsWith(".depends")) { + moduleInfo.dependencies = splitNonEmpty(value, ' '); + for (var k = 0; k < moduleInfo.dependencies.length; ++k) { + moduleInfo.dependencies[k] + = moduleInfo.dependencies[k].replace("_private", "-private"); + } + } else if (key.endsWith(".module_config")) { + var elems = splitNonEmpty(value, ' '); + for (k = 0; k < elems.length; ++k) { + var elem = elems[k]; + if (elem === "no_link") + moduleInfo.hasLibrary = false; + else if (elem === "staticlib") + moduleInfo.isStaticLibrary = true; + else if (elem === "internal_module") + moduleInfo.isPrivate = true; + else if (elem === "v2") + hasV2 = true; + } + } else if (key.endsWith(".includes")) { + moduleInfo.includePaths = extractPaths(value, priFilePath); + for (k = 0; k < moduleInfo.includePaths.length; ++k) { + moduleInfo.includePaths[k] = moduleInfo.includePaths[k] + .replace("$$QT_MODULE_INCLUDE_BASE", qtProps.includePath) + .replace("$$QT_MODULE_HOST_LIB_BASE", qtProps.hostLibraryPath) + .replace("$$QT_MODULE_LIB_BASE", qtProps.libraryPath); + } + } else if (key.endsWith(".libs")) { + var libDirs = extractPaths(value, priFilePath); + if (libDirs.length === 1) { + moduleInfo.libDir = libDirs[0] + .replace("$$QT_MODULE_HOST_LIB_BASE", qtProps.hostLibraryPath) + .replace("$$QT_MODULE_LIB_BASE", qtProps.libraryPath); + } else { + moduleInfo.libDir = qtProps.libraryPath; + } + } else if (key.endsWith(".DEFINES")) { + moduleInfo.compilerDefines = splitNonEmpty(value, ' '); + } else if (key.endsWith(".VERSION")) { + moduleInfo.version = value; + } else if (key.endsWith(".plugin_types")) { + moduleInfo.supportedPluginTypes = splitNonEmpty(value, ' '); + } else if (key.endsWith(".TYPE")) { + moduleInfo.pluginData.type = value; + } else if (key.endsWith(".EXTENDS")) { + moduleInfo.pluginData["extends"] = splitNonEmpty(value, ' '); + for (k = 0; k < moduleInfo.pluginData["extends"].length; ++k) { + if (moduleInfo.pluginData["extends"][k] === "-") { + moduleInfo.pluginData["extends"].splice(k, 1); + moduleInfo.pluginData.autoLoad = false; + break; + } + } + } else if (key.endsWith(".CLASS_NAME")) { + moduleInfo.pluginData.className = value; + } + } + } + if (hasV2 && !hasModuleEntry && !moduleInfo.isStaticLibrary) + moduleInfo.hasLibrary = false; + + // Fix include paths for Apple frameworks. + // The qt_lib_XXX.pri files contain wrong values for versions < 5.6. + if (!hasV2 && ProviderUtils.qtIsFramework(moduleInfo, qtProps)) { + moduleInfo.includePaths = []; + var baseIncDir = frameworkHeadersPath(moduleInfo, qtProps); + if (moduleInfo.isPrivate) { + baseIncDir = FileInfo.joinPaths(baseIncDir, moduleInfo.version); + moduleInfo.includePaths.push(baseIncDir, + FileInfo.joinPaths(baseIncDir, moduleInfo.name)); + } else { + moduleInfo.includePaths.push(baseIncDir); + } + } + + setupLibraries(moduleInfo, qtProps, nonExistingPrlFiles, androidAbi); + + modules.push(moduleInfo); + if (moduleInfo.qbsName === "testlib") + addTestModule(modules); + if (moduleInfo.qbsName === "designercomponents-private") + addDesignerComponentsModule(modules); + } + + replaceQtLibNamesWithFilePath(modules, qtProps); + removeDuplicatedDependencyLibs(modules); + + return modules; +} + +function getQtInfo(qmakeFilePath) { + if (!File.exists(qmakeFilePath)) { + throw "The specified qmake file path '" + + FileInfo.toNativeSeparators(qmakeFilePath) + "' does not exist."; + } + var qtProps = getQtProperties(qmakeFilePath); + var androidAbis = []; + if (qtProps.androidAbis !== undefined) { + // Multiple androidAbis detected: Qt >= 5.14 + androidAbis = qtProps.androidAbis; + } else { + // Single abi detected: Qt < 5.14 + androidAbis.push(''); + } + if (androidAbis.length > 1) + console.info("Qt with multiple abi detected: '" + androidAbis + "'"); + + var result = {}; + result.qtProps = qtProps; + result.abiInfos = []; + result.qmakeFilePath = qmakeFilePath; + for (a = 0; a < androidAbis.length; ++a) { + var abiInfo = {}; + if (androidAbis.length > 1) + console.info("Found abi '" + androidAbis[a] + "'..."); + abiInfo.androidAbi = androidAbis[a]; + var allModules = qtProps.qtMajorVersion < 5 + ? allQt4Modules(qtProps) : allQt5Modules(qtProps, androidAbis[a]);; + abiInfo.modules = {}; + for (var i = 0; i < allModules.length; ++i) { + var module = allModules[i]; + abiInfo.modules[module.qbsName] = module; + } + abiInfo.pluginsByType = {}; + abiInfo.nonEssentialPlugins = []; + for (var moduleName in abiInfo.modules) { + var m = abiInfo.modules[moduleName]; + if (m.isPlugin) { + if (!abiInfo.pluginsByType[m.pluginData.type]) + abiInfo.pluginsByType[m.pluginData.type] = []; + abiInfo.pluginsByType[m.pluginData.type].push(m.name); + if (!m.pluginData.autoLoad) + abiInfo.nonEssentialPlugins.push(m.name); + } + } + + result.abiInfos.push(abiInfo); + } + return result; +} + +function configure(qmakeFilePaths) { + var result = []; + qmakeFilePaths = getQmakeFilePaths(qmakeFilePaths); + if (!qmakeFilePaths || qmakeFilePaths.length === 0) + return result; + for (var i = 0; i < qmakeFilePaths.length; ++i) { + var qtInfo = {}; + try { + console.info("Getting info about Qt at '" + + FileInfo.toNativeSeparators(qmakeFilePaths[i]) + "'..."); + qtInfo = getQtInfo(qmakeFilePaths[i]); + result.push(qtInfo); + } catch (e) { + console.warn("Error getting info about Qt for '" + + FileInfo.toNativeSeparators(qmakeFilePaths[i]) + "': " + e); + throw e; + } + } + return result; +} diff --git a/share/qbs/imports/qbs/ProviderUtils/provider-utils.js b/share/qbs/imports/qbs/ProviderUtils/provider-utils.js new file mode 100644 index 000000000..06e703a7a --- /dev/null +++ b/share/qbs/imports/qbs/ProviderUtils/provider-utils.js @@ -0,0 +1,128 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Copyright (C) 2023 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. +** +****************************************************************************/ + +var Utilities = require("qbs.Utilities"); + +function pkgConfigToModuleName(packageName) { + return packageName.replace(/\./g, '-'); +} + +function msvcPrefix() { return "win32-msvc"; } + +function isMsvcQt(qtProps) { return qtProps.mkspecName.startsWith(msvcPrefix()); } + +function isMinGwQt(qtProps) { + return qtProps.mkspecName.startsWith("win32-g++") || qtProps.mkspecName.startsWith("mingw"); +} + +function isDesktopWindowsQt(qtProps) { + return qtProps.mkspecName.startsWith("win32-") || isMinGwQt(qtProps); +} + +function qtNeedsDSuffix(qtProps) { + return !isMinGwQt(qtProps) + || Utilities.versionCompare(qtProps.qtVersion, "5.14.0") < 0 + || qtProps.configItems.contains("debug_and_release"); +} + +function qtIsFramework(modInfo, qtProps) { + if (!qtProps.frameworkBuild || modInfo.isStaticLibrary) + return false; + var modulesNeverBuiltAsFrameworks = [ + "bootstrap", "openglextensions", "platformsupport", "qmldevtools", "harfbuzzng" + ]; + + if (qtProps.qtMajorVersion <= 5) { + modulesNeverBuiltAsFrameworks.push("uitools"); // is framework since qt6 + } + + return !modulesNeverBuiltAsFrameworks.contains(modInfo.qbsName); +} + +function qtLibBaseName(modInfo, libName, debugBuild, qtProps) { + var name = libName; + if (qtProps.mkspecName.startsWith("win")) { + if (debugBuild && qtNeedsDSuffix(qtProps)) + name += 'd'; + if (!modInfo.isStaticLibrary && qtProps.qtMajorVersion < 5) + name += qtProps.qtMajorVersion; + } + if (qtProps.mkspecName.contains("macx") + || qtProps.mkspecName.contains("ios") + || qtProps.mkspecName.contains("darwin")) { + if (!qtIsFramework(modInfo, qtProps) + && qtProps.buildVariant.contains("debug") + && (!qtProps.buildVariant.contains("release") || debugBuild)) { + name += "_debug"; + } + } + return name; +} + +function qtModuleNameWithoutPrefix(modInfo) { + if (modInfo.name === "Phonon") + return "phonon"; + if (!modInfo.modulePrefix && modInfo.name.startsWith("Qt")) + return modInfo.name.slice(2); // Strip off "Qt". + if (modInfo.name.startsWith(modInfo.modulePrefix)) + return modInfo.name.slice(modInfo.modulePrefix.length); + return modInfo.name; +} + +function qtLibraryBaseName(modInfo, qtProps, debugBuild) { + if (modInfo.isPlugin) + return qtLibBaseName(modInfo, modInfo.name, debugBuild, qtProps); + + // Some modules use a different naming scheme, so it doesn't get boring. + var libNameBroken = modInfo.name === "Enginio" + || modInfo.name === "DataVisualization" + || modInfo.name === "Phonon"; + + var libName = ""; + if (!modInfo.isExternal) { + libName += !modInfo.modulePrefix && !libNameBroken ? "Qt" : modInfo.modulePrefix; + if (qtProps.qtMajorVersion >= 5 && !qtIsFramework(modInfo, qtProps) && !libNameBroken) + libName += qtProps.qtMajorVersion; + } + libName += qtModuleNameWithoutPrefix(modInfo); + if (!modInfo.isExternal) + libName += qtProps.qtLibInfix; + return qtLibBaseName(modInfo, libName, debugBuild, qtProps); +} + +function qtLibNameForLinker(modInfo, qtProps, debugBuild) { + if (!modInfo.hasLibrary) + return undefined; + var libName = qtLibraryBaseName(modInfo, qtProps, debugBuild); + if (qtProps.mkspecName.contains("msvc")) + libName += ".lib"; + return libName; +} diff --git a/share/qbs/imports/qbs/base/AppleApplicationDiskImage.qbs b/share/qbs/imports/qbs/base/AppleApplicationDiskImage.qbs index 6491bf14a..462b96b7c 100644 --- a/share/qbs/imports/qbs/base/AppleApplicationDiskImage.qbs +++ b/share/qbs/imports/qbs/base/AppleApplicationDiskImage.qbs @@ -34,7 +34,8 @@ import qbs.ModUtils AppleDiskImage { property string sourceBase: "/Applications" - readonly property string absoluteSourceBase: FileInfo.joinPaths(qbs.installRoot, sourceBase) + readonly property string absoluteSourceBase: + FileInfo.joinPaths(qbs.installRoot, qbs.installPrefix, sourceBase) property stringList symlinks: ["/Applications:Applications"] readonly property string stageDirectory: FileInfo.joinPaths(destinationDirectory, "Volumes", dmg.volumeName) diff --git a/share/qbs/imports/qbs/base/ApplicationExtension.qbs b/share/qbs/imports/qbs/base/ApplicationExtension.qbs index eae5145ce..3d25d5f73 100644 --- a/share/qbs/imports/qbs/base/ApplicationExtension.qbs +++ b/share/qbs/imports/qbs/base/ApplicationExtension.qbs @@ -34,8 +34,12 @@ XPCService { type: base.concat(["applicationextension"]) property bool _useLegacyExtensionLibraries: - qbs.targetOS.contains("macos") && parseInt(xcode.sdkVersion.split(".")[1], 10) < 11 || - qbs.targetOS.contains("ios") && parseInt(xcode.sdkVersion.split(".")[0], 10) < 9 + qbs.targetOS.contains("macos") + && xcode.present + && parseInt(xcode.sdkVersion.split(".")[1], 10) < 11 + || qbs.targetOS.contains("ios") + && xcode.present + && parseInt(xcode.sdkVersion.split(".")[0], 10) < 9 cpp.entryPoint: "_NSExtensionMain" cpp.frameworkPaths: base.concat(_useLegacyExtensionLibraries diff --git a/share/qbs/module-providers/Qt/templates/QtModule.qbs b/share/qbs/imports/qbs/base/QtModule.qbs index 62e05327b..35421436f 100644 --- a/share/qbs/module-providers/Qt/templates/QtModule.qbs +++ b/share/qbs/imports/qbs/base/QtModule.qbs @@ -23,6 +23,7 @@ Module { // We have to pull in all plugins here, because dependency resolving happens // before module merging, and we don't know yet if someone set // Qt.pluginSupport.pluginsByType in the product. + // TODO: We might be able to use Qt.pluginSupport.pluginsByType now. // The real filtering is done later by the plugin module files themselves. var list = []; var allPlugins = Qt.plugin_support.allPluginsByType; diff --git a/share/qbs/module-providers/Qt/templates/QtPlugin.qbs b/share/qbs/imports/qbs/base/QtPlugin.qbs index 883e34465..883e34465 100644 --- a/share/qbs/module-providers/Qt/templates/QtPlugin.qbs +++ b/share/qbs/imports/qbs/base/QtPlugin.qbs diff --git a/share/qbs/module-providers/Qt/provider.qbs b/share/qbs/module-providers/Qt/provider.qbs index 33083c51d..0d036c04d 100644 --- a/share/qbs/module-providers/Qt/provider.qbs +++ b/share/qbs/module-providers/Qt/provider.qbs @@ -1,6 +1,14 @@ import "setup-qt.js" as SetupQt +import qbs.Probes ModuleProvider { + Probes.QmakeProbe { + id: probe + qmakePaths: qmakeFilePaths + } property stringList qmakeFilePaths - relativeSearchPaths: SetupQt.doSetup(qmakeFilePaths, outputBaseDir, path, qbs) + readonly property varList _qtInfos: probe.qtInfos + condition: moduleName.startsWith("Qt.") + isEager: false + relativeSearchPaths: SetupQt.doSetup(moduleName, _qtInfos, outputBaseDir, path) } diff --git a/share/qbs/module-providers/Qt/setup-qt.js b/share/qbs/module-providers/Qt/setup-qt.js index 72b21c395..9a314822b 100644 --- a/share/qbs/module-providers/Qt/setup-qt.js +++ b/share/qbs/module-providers/Qt/setup-qt.js @@ -37,1236 +37,13 @@ ** ****************************************************************************/ -var Environment = require("qbs.Environment"); var File = require("qbs.File"); var FileInfo = require("qbs.FileInfo"); -var Host = require("qbs.Host"); var ModUtils = require("qbs.ModUtils"); -var Process = require("qbs.Process"); +var ProviderUtils = require("qbs.ProviderUtils"); var TextFile = require("qbs.TextFile"); var Utilities = require("qbs.Utilities"); -function splitNonEmpty(s, c) { return s.split(c).filter(function(e) { return e; }); } -function toNative(p) { return FileInfo.toNativeSeparators(p); } -function exeSuffix(qbs) { return Host.os().contains("windows") ? ".exe" : ""; } - -function getQmakeFilePaths(qmakeFilePaths, qbs) { - if (qmakeFilePaths && qmakeFilePaths.length > 0) - return qmakeFilePaths; - console.info("Detecting Qt installations..."); - var filePaths = []; - var pathValue = Environment.getEnv("PATH"); - if (pathValue) { - var dirs = splitNonEmpty(pathValue, FileInfo.pathListSeparator()); - var suffix = exeSuffix(qbs); - for (var i = 0; i < dirs.length; ++i) { - var candidate = FileInfo.joinPaths(dirs[i], "qmake" + suffix); - var canonicalCandidate = FileInfo.canonicalPath(candidate); - if (!canonicalCandidate || !File.exists(canonicalCandidate)) - continue; - if (FileInfo.completeBaseName(canonicalCandidate) !== "qtchooser") - candidate = canonicalCandidate; - if (!filePaths.contains(candidate)) { - console.info("Found Qt at '" + toNative(candidate) + "'."); - filePaths.push(candidate); - } - } - } - if (filePaths.length === 0) { - console.warn("Could not find any qmake executables in PATH. Either make sure a qmake " - + "executable is present in PATH or set the moduleProviders.Qt.qmakeFilePaths property " - + "to point a qmake executable."); - } - return filePaths; -} - -function queryQmake(qmakeFilePath) { - var qmakeProcess = new Process; - qmakeProcess.exec(qmakeFilePath, ["-query"]); - if (qmakeProcess.exitCode() !== 0) { - throw "The qmake executable '" + toNative(qmakeFilePath) + "' failed with exit code " - + qmakeProcess.exitCode() + "."; - } - var queryResult = {}; - while (!qmakeProcess.atEnd()) { - var line = qmakeProcess.readLine(); - var index = (line || "").indexOf(":"); - if (index !== -1) - queryResult[line.slice(0, index)] = line.slice(index + 1).trim(); - } - return queryResult; -} - -function pathQueryValue(queryResult, key) { - var p = queryResult[key]; - if (p) - return FileInfo.fromNativeSeparators(p); -} - -function readFileContent(filePath) { - var f = new TextFile(filePath, TextFile.ReadOnly); - var content = f.readAll(); - f.close(); - return content; -} - -// TODO: Don't do the split every time... -function configVariable(configContent, key) { - var configContentLines = configContent.split('\n'); - var regexp = new RegExp("^\\s*" + key + "\\s*\\+{0,1}=(.*)"); - for (var i = 0; i < configContentLines.length; ++i) { - var line = configContentLines[i]; - var match = regexp.exec(line); - if (match) - return match[1].trim(); - } -} - -function configVariableItems(configContent, key) { - return splitNonEmpty(configVariable(configContent, key), ' '); -} - -function msvcPrefix() { return "win32-msvc"; } - -function isMsvcQt(qtProps) { return qtProps.mkspecName.startsWith(msvcPrefix()); } - -function msvcCompilerVersionForYear(year) { - var mapping = { - "2005": "14", "2008": "15", "2010": "16", "2012": "17", "2013": "18", "2015": "19", - "2017": "19.1", "2019": "19.2" - }; - return mapping[year]; -} - -function msvcCompilerVersionFromMkspecName(mkspecName) { - return msvcCompilerVersionForYear(mkspecName.slice(msvcPrefix().length)); -} - -function addQtBuildVariant(qtProps, buildVariantName) { - if (qtProps.qtConfigItems.contains(buildVariantName)) - qtProps.buildVariant.push(buildVariantName); -} - -function checkForStaticBuild(qtProps) { - if (qtProps.qtMajorVersion >= 5) - return qtProps.qtConfigItems.contains("static"); - if (qtProps.frameworkBuild) - return false; // there are no Qt4 static frameworks - var isWin = qtProps.mkspecName.startsWith("win"); - var libDir = isWin ? qtProps.binaryPath : qtProps.libraryPath; - var coreLibFiles = File.directoryEntries(libDir, File.Files) - .filter(function(fp) { return fp.contains("Core"); }); - if (coreLibFiles.length === 0) - throw "Could not determine whether Qt is a static build."; - for (var i = 0; i < coreLibFiles.length; ++i) { - if (Utilities.isSharedLibrary(coreLibFiles[i])) - return false; - } - return true; -} - -function isForMinGw(qtProps) { - return qtProps.mkspecName.startsWith("win32-g++") || qtProps.mkspecName.startsWith("mingw"); -} - -function targetsDesktopWindows(qtProps) { - return qtProps.mkspecName.startsWith("win32-") || isForMinGw(qtProps); -} - -function guessMinimumWindowsVersion(qtProps) { - if (qtProps.mkspecName.startsWith("winrt-")) - return "10.0"; - if (!targetsDesktopWindows(qtProps)) - return ""; - if (qtProps.architecture === "x86_64" || qtProps.architecture === "ia64") - return "5.2" - var match = qtProps.mkspecName.match(/^win32-msvc(\d+)$/); - if (match) { - var msvcVersion = match[1]; - if (msvcVersion < 2012) - return "5.0"; - return "5.1"; - } - return qtProps.qtMajorVersion < 5 ? "5.0" : "5.1"; -} - -function needsDSuffix(qtProps) { - return !isForMinGw(qtProps) || Utilities.versionCompare(qtProps.qtVersion, "5.14.0") < 0 - || qtProps.configItems.contains("debug_and_release"); -} - -function fillEntryPointLibs(qtProps, debug) { - result = []; - var isMinGW = isForMinGw(qtProps); - - // Some Linux distributions rename the qtmain library. - var qtMainCandidates = ["qtmain"]; - if (isMinGW && qtProps.qtMajorVersion === 5) - qtMainCandidates.push("qt5main"); - if (qtProps.qtMajorVersion === 6) - qtMainCandidates.push("Qt6EntryPoint"); - - for (var i = 0; i < qtMainCandidates.length; ++i) { - var baseNameCandidate = qtMainCandidates[i]; - var qtmain = qtProps.libraryPath + '/'; - if (isMinGW) - qtmain += "lib"; - qtmain += baseNameCandidate + qtProps.qtLibInfix; - if (debug && needsDSuffix(qtProps)) - qtmain += 'd'; - if (isMinGW) { - qtmain += ".a"; - } else { - qtmain += ".lib"; - if (Utilities.versionCompare(qtProps.qtVersion, "5.4.0") >= 0) - result.push("Shell32.lib"); - } - if (File.exists(qtmain)) { - result.push(qtmain); - break; - } - } - if (result.length === 0) { - console.warn("Could not find the qtmain library at '" + toNative(qtProps.libraryPath) - + "'. You will not be able to link Qt applications."); - } - return result; -} - -function abiToArchitecture(abi) { - switch (abi) { - case "armeabi-v7a": - return "armv7a"; - case "arm64-v8a": - return "arm64"; - case "x86": - case "x86_64": - default: - return abi; - } -} - -function getQtProperties(qmakeFilePath, qbs) { - var queryResult = queryQmake(qmakeFilePath); - var qtProps = {}; - qtProps.installPrefixPath = pathQueryValue(queryResult, "QT_INSTALL_PREFIX"); - qtProps.documentationPath = pathQueryValue(queryResult, "QT_INSTALL_DOCS"); - qtProps.includePath = pathQueryValue(queryResult, "QT_INSTALL_HEADERS"); - qtProps.libraryPath = pathQueryValue(queryResult, "QT_INSTALL_LIBS"); - qtProps.hostLibraryPath = pathQueryValue(queryResult, "QT_HOST_LIBS"); - qtProps.binaryPath = pathQueryValue(queryResult, "QT_HOST_BINS") - || pathQueryValue(queryResult, "QT_INSTALL_BINS"); - qtProps.installPath = pathQueryValue(queryResult, "QT_INSTALL_BINS"); - qtProps.documentationPath = pathQueryValue(queryResult, "QT_INSTALL_DOCS"); - qtProps.pluginPath = pathQueryValue(queryResult, "QT_INSTALL_PLUGINS"); - qtProps.qmlPath = pathQueryValue(queryResult, "QT_INSTALL_QML"); - qtProps.qmlImportPath = pathQueryValue(queryResult, "QT_INSTALL_IMPORTS"); - qtProps.qtVersion = queryResult.QT_VERSION; - - var mkspecsBaseSrcPath; - if (Utilities.versionCompare(qtProps.qtVersion, "5") >= 0) { - qtProps.mkspecBasePath = FileInfo.joinPaths(pathQueryValue(queryResult, "QT_HOST_DATA"), - "mkspecs"); - mkspecsBaseSrcPath = FileInfo.joinPaths(pathQueryValue(queryResult, "QT_HOST_DATA/src"), - "mkspecs"); - } else { - qtProps.mkspecBasePath = FileInfo.joinPaths - (pathQueryValue(queryResult, "QT_INSTALL_DATA"), "mkspecs"); - } - - if (Utilities.versionCompare(qtProps.qtVersion, "6") >= 0) { - qtProps.libExecPath = pathQueryValue(queryResult, "QT_HOST_LIBEXECS") - || pathQueryValue(queryResult, "QT_INSTALL_LIBEXECS"); - } - - // QML tools were only moved in Qt 6.2. - qtProps.qmlLibExecPath = Utilities.versionCompare(qtProps.qtVersion, "6.2") >= 0 - ? qtProps.libExecPath : qtProps.binaryPath - - if (!File.exists(qtProps.mkspecBasePath)) - throw "Cannot extract the mkspecs directory."; - - var qconfigContent = readFileContent(FileInfo.joinPaths(qtProps.mkspecBasePath, - "qconfig.pri")); - qtProps.qtMajorVersion = parseInt(configVariable(qconfigContent, "QT_MAJOR_VERSION")); - qtProps.qtMinorVersion = parseInt(configVariable(qconfigContent, "QT_MINOR_VERSION")); - qtProps.qtPatchVersion = parseInt(configVariable(qconfigContent, "QT_PATCH_VERSION")); - qtProps.qtNameSpace = configVariable(qconfigContent, "QT_NAMESPACE"); - qtProps.qtLibInfix = configVariable(qconfigContent, "QT_LIBINFIX") || ""; - qtProps.architecture = configVariable(qconfigContent, "QT_TARGET_ARCH") - || configVariable(qconfigContent, "QT_ARCH") || "x86"; - qtProps.configItems = configVariableItems(qconfigContent, "CONFIG"); - qtProps.qtConfigItems = configVariableItems(qconfigContent, "QT_CONFIG"); - - // retrieve the mkspec - if (qtProps.qtMajorVersion >= 5) { - qtProps.mkspecName = queryResult.QMAKE_XSPEC; - qtProps.mkspecPath = FileInfo.joinPaths(qtProps.mkspecBasePath, qtProps.mkspecName); - if (mkspecsBaseSrcPath && !File.exists(qtProps.mkspecPath)) - qtProps.mkspecPath = FileInfo.joinPaths(mkspecsBaseSrcPath, qtProps.mkspecName); - } else { - if (Host.os().contains("windows")) { - var baseDirPath = FileInfo.joinPaths(qtProps.mkspecBasePath, "default"); - var fileContent = readFileContent(FileInfo.joinPaths(baseDirPath, "qmake.conf")); - qtProps.mkspecPath = configVariable(fileContent, "QMAKESPEC_ORIGINAL"); - if (!File.exists(qtProps.mkspecPath)) { - // Work around QTBUG-28792. - // The value of QMAKESPEC_ORIGINAL is wrong for MinGW packages. Y u h8 me? - var match = fileContent.exec(/\binclude\(([^)]+)\/qmake\.conf\)/m); - if (match) { - qtProps.mkspecPath = FileInfo.cleanPath(FileInfo.joinPaths( - baseDirPath, match[1])); - } - } - } else { - qtProps.mkspecPath = FileInfo.canonicalPath( - FileInfo.joinPaths(qtProps.mkspecBasePath, "default")); - } - - // E.g. in qmake.conf for Qt 4.8/mingw we find this gem: - // QMAKESPEC_ORIGINAL=C:\\Qt\\Qt\\4.8\\mingw482\\mkspecs\\win32-g++ - qtProps.mkspecPath = FileInfo.cleanPath(qtProps.mkspecPath); - - qtProps.mkspecName = qtProps.mkspecPath; - var idx = qtProps.mkspecName.lastIndexOf('/'); - if (idx !== -1) - qtProps.mkspecName = qtProps.mkspecName.slice(idx + 1); - } - if (!File.exists(qtProps.mkspecPath)) - throw "mkspec '" + toNative(qtProps.mkspecPath) + "' does not exist"; - - // Starting with qt 5.14, android sdk provides multi-abi - if (Utilities.versionCompare(qtProps.qtVersion, "5.14.0") >= 0 - && qtProps.mkspecPath.contains("android")) { - var qdeviceContent = readFileContent(FileInfo.joinPaths(qtProps.mkspecBasePath, - "qdevice.pri")); - qtProps.androidAbis = configVariable(qdeviceContent, "DEFAULT_ANDROID_ABIS").split(' '); - } - - // determine MSVC version - if (isMsvcQt(qtProps)) { - var msvcMajor = configVariable(qconfigContent, "QT_MSVC_MAJOR_VERSION"); - var msvcMinor = configVariable(qconfigContent, "QT_MSVC_MINOR_VERSION"); - var msvcPatch = configVariable(qconfigContent, "QT_MSVC_PATCH_VERSION"); - if (msvcMajor && msvcMinor && msvcPatch) - qtProps.msvcVersion = msvcMajor + "." + msvcMinor + "." + msvcPatch; - else - qtProps.msvcVersion = msvcCompilerVersionFromMkspecName(qtProps.mkspecName); - } - - // determine whether we have a framework build - qtProps.frameworkBuild = qtProps.mkspecPath.contains("macx") - && qtProps.configItems.contains("qt_framework"); - - // determine whether Qt is built with debug, release or both - qtProps.buildVariant = []; - addQtBuildVariant(qtProps, "debug"); - addQtBuildVariant(qtProps, "release"); - - qtProps.staticBuild = checkForStaticBuild(qtProps); - - // determine whether user apps require C++11 - if (qtProps.qtConfigItems.contains("c++11") && qtProps.staticBuild) - qtProps.configItems.push("c++11"); - - // Set the minimum operating system versions appropriate for this Qt version - qtProps.windowsVersion = guessMinimumWindowsVersion(qtProps); - if (qtProps.windowsVersion) { // Is target OS Windows? - if (qtProps.buildVariant.contains("debug")) - qtProps.entryPointLibsDebug = fillEntryPointLibs(qtProps, true); - if (qtProps.buildVariant.contains("release")) - qtProps.entryPointLibsRelease = fillEntryPointLibs(qtProps, false); - } else if (qtProps.mkspecPath.contains("macx")) { - if (qtProps.qtMajorVersion >= 5) { - var lines = getFileContentsRecursively(FileInfo.joinPaths(qtProps.mkspecPath, - "qmake.conf")); - for (var i = 0; i < lines.length; ++i) { - var line = lines[i].trim(); - match = line.match - (/^QMAKE_(MACOSX|IOS|TVOS|WATCHOS)_DEPLOYMENT_TARGET\s*=\s*(.*)\s*$/); - if (match) { - var platform = match[1]; - var version = match[2]; - if (platform === "MACOSX") - qtProps.macosVersion = version; - else if (platform === "IOS") - qtProps.iosVersion = version; - else if (platform === "TVOS") - qtProps.tvosVersion = version; - else if (platform === "WATCHOS") - qtProps.watchosVersion = version; - } - } - var isMac = qtProps.mkspecName !== "macx-ios-clang" - && qtProps.mkspecName !== "macx-tvos-clang" - && qtProps.mkspecName !== "macx-watchos-clang"; - if (isMac) { - // Qt 5.0.x placed the minimum version in a different file - if (!qtProps.macosVersion) - qtProps.macosVersion = "10.6"; - - // If we're using C++11 with libc++, make sure the deployment target is >= 10.7 - if (Utilities.versionCompare(qtProps.macosVersion, "10, 7") < 0 - && qtProps.qtConfigItems.contains("c++11")) { - qtProps.macosVersion = "10.7"; - } - } - } else if (qtProps.qtMajorVersion === 4 && qtProps.qtMinorVersion >= 6) { - var qconfigDir = qtProps.frameworkBuild - ? FileInfo.joinPaths(qtProps.libraryPath, "QtCore.framework", "Headers") - : FileInfo.joinPaths(qtProps.includePath, "Qt"); - try { - var qconfig = new TextFile(FileInfo.joinPaths(qconfigDir, "qconfig.h"), - TextFile.ReadOnly); - var qtCocoaBuild = false; - var ok = true; - do { - line = qconfig.readLine(); - if (line.match(/\s*#define\s+QT_MAC_USE_COCOA\s+1\s*/)) { - qtCocoaBuild = true; - break; - } - } while (!qconfig.atEof()); - qtProps.macosVersion = qtCocoaBuild ? "10.5" : "10.4"; - } - catch (e) {} - finally { - if (qconfig) - qconfig.close(); - } - if (!qtProps.macosVersion) { - throw "Could not determine whether Qt is using Cocoa or Carbon from '" - + toNative(qconfig.filePath()) + "'."; - } - } - } else if (qtProps.mkspecPath.contains("android")) { - if (qtProps.qtMajorVersion >= 5) - qtProps.androidVersion = "2.3"; - else if (qtProps.qtMajorVersion === 4 && qtProps.qtMinorVersion >= 8) - qtProps.androidVersion = "1.6"; // Necessitas - } - return qtProps; -} - -function makePluginData() { - var pluginData = {}; - pluginData.type = undefined; - pluginData.className = undefined; - pluginData.autoLoad = true; - pluginData["extends"] = []; - return pluginData; -} - -function makeQtModuleInfo(name, qbsName, deps) { - var moduleInfo = {}; - moduleInfo.name = name; // As in the path to the headers and ".name" in the pri files. - if (moduleInfo.name === undefined) - moduleInfo.name = ""; - moduleInfo.qbsName = qbsName; // Lower-case version without "qt" prefix. - moduleInfo.dependencies = deps || []; // qbs names. - if (moduleInfo.qbsName && moduleInfo.qbsName !== "core" - && !moduleInfo.dependencies.contains("core")) { - moduleInfo.dependencies.unshift("core"); - } - moduleInfo.isPrivate = qbsName && qbsName.endsWith("-private"); - moduleInfo.hasLibrary = !moduleInfo.isPrivate; - moduleInfo.isStaticLibrary = false; - moduleInfo.isPlugin = false; - moduleInfo.mustExist = true; - moduleInfo.modulePrefix = ""; // empty value means "Qt". - moduleInfo.version = undefined; - moduleInfo.includePaths = []; - moduleInfo.compilerDefines = []; - moduleInfo.staticLibrariesDebug = []; - moduleInfo.staticLibrariesRelease = []; - moduleInfo.dynamicLibrariesDebug = []; - moduleInfo.dynamicLibrariesRelease = []; - moduleInfo.linkerFlagsDebug = []; - moduleInfo.linkerFlagsRelease = []; - moduleInfo.libFilePathDebug = undefined; - moduleInfo.libFilePathRelease = undefined; - moduleInfo.frameworksDebug = []; - moduleInfo.frameworksRelease = []; - moduleInfo.frameworkPathsDebug = []; - moduleInfo.frameworkPathsRelease = []; - moduleInfo.libraryPaths = []; - moduleInfo.libDir = ""; - moduleInfo.config = []; - moduleInfo.supportedPluginTypes = []; - moduleInfo.pluginData = makePluginData(); - return moduleInfo; -} - -function frameworkHeadersPath(qtModuleInfo, qtProps) { - return FileInfo.joinPaths(qtProps.libraryPath, qtModuleInfo.name + ".framework", "Headers"); -} - -function qt4ModuleIncludePaths(qtModuleInfo, qtProps) { - var paths = []; - if (isFramework(qtModuleInfo, qtProps)) - paths.push(frameworkHeadersPath(qtModuleInfo, qtProps)); - else - paths.push(qtProps.includePath, FileInfo.joinPaths(qtProps.includePath, qtModuleInfo.name)); - return paths; -} - -// We erroneously called the "testlib" module "test" for quite a while. Let's not punish users -// for that. -function addTestModule(modules) { - var testModule = makeQtModuleInfo("QtTest", "test", ["testlib"]); - testModule.hasLibrary = false; - modules.push(testModule); -} - -// See above. -function addDesignerComponentsModule(modules) { - var module = makeQtModuleInfo("QtDesignerComponents", "designercomponents", - ["designercomponents-private"]); - module.hasLibrary = false; - modules.push(module); -} - -function isFramework(modInfo, qtProps) { - if (!qtProps.frameworkBuild || modInfo.isStaticLibrary) - return false; - var modulesNeverBuiltAsFrameworks = [ - "bootstrap", "openglextensions", "platformsupport", "qmldevtools", "harfbuzzng" - ]; - - if (qtProps.qtMajorVersion <= 5) { - modulesNeverBuiltAsFrameworks.push("uitools"); // is framework since qt6 - } - - return !modulesNeverBuiltAsFrameworks.contains(modInfo.qbsName); -} - -function libBaseName(modInfo, libName, debugBuild, qtProps) { - var name = libName; - if (qtProps.mkspecName.startsWith("win")) { - if (debugBuild && needsDSuffix(qtProps)) - name += 'd'; - if (!modInfo.isStaticLibrary && qtProps.qtMajorVersion < 5) - name += qtProps.qtMajorVersion; - } - if (qtProps.mkspecName.contains("macx") - || qtProps.mkspecName.contains("ios") - || qtProps.mkspecName.contains("darwin")) { - if (!isFramework(modInfo, qtProps) - && qtProps.buildVariant.contains("debug") - && (!qtProps.buildVariant.contains("release") || debugBuild)) { - name += "_debug"; - } - } - return name; -} - -function moduleNameWithoutPrefix(modInfo) { - if (modInfo.name === "Phonon") - return "phonon"; - if (!modInfo.modulePrefix && modInfo.name.startsWith("Qt")) - return modInfo.name.slice(2); // Strip off "Qt". - if (modInfo.name.startsWith(modInfo.modulePrefix)) - return modInfo.name.slice(modInfo.modulePrefix.length); - return modInfo.name; -} - -function libraryBaseName(modInfo, qtProps, debugBuild) { - if (modInfo.isPlugin) - return libBaseName(modInfo, modInfo.name, debugBuild, qtProps); - - // Some modules use a different naming scheme, so it doesn't get boring. - var libNameBroken = modInfo.name === "Enginio" - || modInfo.name === "DataVisualization" - || modInfo.name === "Phonon"; - - var libName = ""; - if (!modInfo.isExternal) { - libName += !modInfo.modulePrefix && !libNameBroken ? "Qt" : modInfo.modulePrefix; - if (qtProps.qtMajorVersion >= 5 && !isFramework(modInfo, qtProps) && !libNameBroken) - libName += qtProps.qtMajorVersion; - } - libName += moduleNameWithoutPrefix(modInfo); - if (!modInfo.isExternal) - libName += qtProps.qtLibInfix; - return libBaseName(modInfo, libName, debugBuild, qtProps); -} - -function libNameForLinker(modInfo, qtProps, debugBuild) { - if (!modInfo.hasLibrary) - return undefined; - var libName = libraryBaseName(modInfo, qtProps, debugBuild); - if (qtProps.mkspecName.contains("msvc")) - libName += ".lib"; - return libName; -} - -function guessLibraryFilePath(prlFilePath, libDir, qtProps) { - var baseName = FileInfo.baseName(prlFilePath); - var prefixCandidates = ["", "lib"]; - var suffixCandidates = ["so." + qtProps.qtVersion, "so", "a", "lib", "dll.a"]; - for (var i = 0; i < prefixCandidates.length; ++i) { - var prefix = prefixCandidates[i]; - for (var j = 0; j < suffixCandidates.length; ++j) { - var suffix = suffixCandidates[j]; - var candidate = FileInfo.joinPaths(libDir, prefix + baseName + '.' + suffix); - if (File.exists(candidate)) - return candidate; - } - } -} - -function doReplaceQtLibNamesWithFilePath(namePathMap, libList) { - for (var i = 0; i < libList.length; ++i) { - var lib = libList[i]; - var path = namePathMap[lib]; - if (path) - libList[i] = path; - } -} - -function replaceQtLibNamesWithFilePath(modules, qtProps) { - // We don't want to add the libraries for Qt modules via "-l", because of the - // danger that a wrong one will be picked up, e.g. from /usr/lib. Instead, - // we pull them in using the full file path. - var linkerNamesToFilePathsDebug = {}; - var linkerNamesToFilePathsRelease = {}; - for (var i = 0; i < modules.length; ++i) { - var m = modules[i]; - linkerNamesToFilePathsDebug[libNameForLinker(m, qtProps, true)] = m.libFilePathDebug; - linkerNamesToFilePathsRelease[libNameForLinker(m, qtProps, false)] = m.libFilePathRelease; - } - for (i = 0; i < modules.length; ++i) { - var module = modules[i]; - doReplaceQtLibNamesWithFilePath(linkerNamesToFilePathsDebug, module.dynamicLibrariesDebug); - doReplaceQtLibNamesWithFilePath(linkerNamesToFilePathsDebug, module.staticLibrariesDebug); - doReplaceQtLibNamesWithFilePath(linkerNamesToFilePathsRelease, - module.dynamicLibrariesRelease); - doReplaceQtLibNamesWithFilePath(linkerNamesToFilePathsRelease, - module.staticLibrariesRelease); - } -} - -function doSetupLibraries(modInfo, qtProps, debugBuild, nonExistingPrlFiles, androidAbi) { - if (!modInfo.hasLibrary) - return; // Can happen for Qt4 convenience modules, like "widgets". - - if (debugBuild) { - if (!qtProps.buildVariant.contains("debug")) - return; - var modulesNeverBuiltAsDebug = ["bootstrap", "qmldevtools"]; - for (var i = 0; i < modulesNeverBuiltAsDebug.length; ++i) { - var m = modulesNeverBuiltAsDebug[i]; - if (modInfo.qbsName === m || modInfo.qbsName === m + "-private") - return; - } - } else if (!qtProps.buildVariant.contains("release")) { - return; - } - - var libs = modInfo.isStaticLibrary - ? (debugBuild ? modInfo.staticLibrariesDebug : modInfo.staticLibrariesRelease) - : (debugBuild ? modInfo.dynamicLibrariesDebug : modInfo.dynamicLibrariesRelease); - var frameworks = debugBuild ? modInfo.frameworksDebug : modInfo.frameworksRelease; - var frameworkPaths = debugBuild ? modInfo.frameworkPathsDebug : modInfo.frameworkPathsRelease; - var flags = debugBuild ? modInfo.linkerFlagsDebug : modInfo.linkerFlagsRelease; - var libFilePath; - - if (qtProps.mkspecName.contains("ios") && modInfo.isStaticLibrary) { - libs.push("z", "m"); - if (qtProps.qtMajorVersion === 5 && qtProps.qtMinorVersion < 8) { - var platformSupportModule = makeQtModuleInfo("QtPlatformSupport", "platformsupport"); - libs.push(libNameForLinker(platformSupportModule, qtProps, debugBuild)); - } - if (modInfo.name === "qios") { - flags.push("-force_load", FileInfo.joinPaths( - qtProps.pluginPath, "platforms", - libBaseName(modInfo, "libqios", debugBuild, qtProps) + ".a")); - } - } - var prlFilePath = modInfo.isPlugin - ? FileInfo.joinPaths(qtProps.pluginPath, modInfo.pluginData.type) - : (modInfo.libDir ? modInfo.libDir : qtProps.libraryPath); - var libDir = prlFilePath; - if (isFramework(modInfo, qtProps)) { - prlFilePath = FileInfo.joinPaths(prlFilePath, - libraryBaseName(modInfo, qtProps, false) + ".framework"); - libDir = prlFilePath; - if (Utilities.versionCompare(qtProps.qtVersion, "5.14") >= 0) - prlFilePath = FileInfo.joinPaths(prlFilePath, "Resources"); - } - var baseName = libraryBaseName(modInfo, qtProps, debugBuild); - if (!qtProps.mkspecName.startsWith("win") && !isFramework(modInfo, qtProps)) - baseName = "lib" + baseName; - prlFilePath = FileInfo.joinPaths(prlFilePath, baseName); - var isNonStaticQt4OnWindows = qtProps.mkspecName.startsWith("win") - && !modInfo.isStaticLibrary && qtProps.qtMajorVersion < 5; - if (isNonStaticQt4OnWindows) - prlFilePath = prlFilePath.slice(0, prlFilePath.length - 1); // The prl file base name does *not* contain the version number... - // qt for android versions 6.0 and 6.1 don't have the architecture suffix in the prl file - if (androidAbi.length > 0 - && modInfo.name !== "QtBootstrap" - && (modInfo.name !== "QtQmlDevTools" || modInfo.name === "QtQmlDevTools" - && Utilities.versionCompare(qtProps.qtVersion, "6.2") >= 0) - && (Utilities.versionCompare(qtProps.qtVersion, "6.0") < 0 - || Utilities.versionCompare(qtProps.qtVersion, "6.2") >= 0)) { - prlFilePath += "_"; - prlFilePath += androidAbi; - } - - prlFilePath += ".prl"; - - try { - var prlFile = new TextFile(prlFilePath, TextFile.ReadOnly); - while (!prlFile.atEof()) { - var line = prlFile.readLine().trim(); - var equalsOffset = line.indexOf('='); - if (equalsOffset === -1) - continue; - if (line.startsWith("QMAKE_PRL_TARGET")) { - var isMingw = qtProps.mkspecName.startsWith("win") - && qtProps.mkspecName.contains("g++"); - var isQtVersionBefore56 = qtProps.qtMajorVersion < 5 - || (qtProps.qtMajorVersion === 5 && qtProps.qtMinorVersion < 6); - - // QMAKE_PRL_TARGET has a "lib" prefix, except for mingw. - // Of course, the exception has an exception too: For static libs, mingw *does* - // have the "lib" prefix. - var libFileName = ""; - if (isQtVersionBefore56 && qtProps.qtMajorVersion === 5 && isMingw - && !modInfo.isStaticLibrary) { - libFileName += "lib"; - } - - libFileName += line.slice(equalsOffset + 1).trim(); - if (isNonStaticQt4OnWindows) - libFileName += 4; // This is *not* part of QMAKE_PRL_TARGET... - if (isQtVersionBefore56) { - if (qtProps.mkspecName.contains("msvc")) { - libFileName += ".lib"; - } else if (isMingw) { - libFileName += ".a"; - if (!File.exists(FileInfo.joinPaths(libDir, libFileName))) - libFileName = libFileName.slice(0, -2) + ".dll"; - } - } - libFilePath = FileInfo.joinPaths(libDir, libFileName); - continue; - } - if (line.startsWith("QMAKE_PRL_CONFIG")) { - modInfo.config = splitNonEmpty(line.slice(equalsOffset + 1).trim(), ' '); - continue; - } - if (!line.startsWith("QMAKE_PRL_LIBS =")) - continue; - - var parts = extractPaths(line.slice(equalsOffset + 1).trim(), prlFilePath); - for (i = 0; i < parts.length; ++i) { - var part = parts[i]; - part = part.replace("$$[QT_INSTALL_LIBS]", qtProps.libraryPath); - part = part.replace("$$[QT_INSTALL_PREFIX]", qtProps.installPrefixPath); - if (part.startsWith("-l")) { - libs.push(part.slice(2)); - } else if (part.startsWith("-L")) { - modInfo.libraryPaths.push(part.slice(2)); - } else if (part.startsWith("-F")) { - frameworkPaths.push(part.slice(2)); - } else if (part === "-framework") { - if (++i < parts.length) - frameworks.push(parts[i]); - } else if (part === "-pthread") { - // prl files for android have QMAKE_PRL_LIBS = -llog -pthread but the pthread - // functionality is included in libc. - if (androidAbi.length === 0) - libs.push("pthread"); - } else if (part.startsWith('-')) { // Some other option - console.debug("QMAKE_PRL_LIBS contains non-library option '" + part - + "' in file '" + prlFilePath + "'"); - flags.push(part); - } else if (part.startsWith("/LIBPATH:")) { - libraryPaths.push(part.slice(9).replace(/\\/g, '/')); - } else { // Assume it's a file path/name. - libs.push(part.replace(/\\/g, '/')); - } - } - } - } catch (e) { - // qt_ext_lib_extX.pri (usually) don't have a corresponding prl file. - // So the pri file variable QMAKE_LIBS_LIBX points to the library - if (modInfo.isExternal) { - libFilePath = debugBuild ? modInfo.staticLibrariesDebug[0] : - modInfo.staticLibrariesRelease[0]; - } - if (!libFilePath || !File.exists(libFilePath)) - libFilePath = guessLibraryFilePath(prlFilePath, libDir, qtProps); - if (nonExistingPrlFiles.contains(prlFilePath)) - return; - nonExistingPrlFiles.push(prlFilePath); - if (modInfo.mustExist) { - console.warn("Could not open prl file '" + toNative(prlFilePath) + "' for module '" - + modInfo.name - + "' (" + e + "), and failed to deduce the library file path. " - + " This module will likely not be usable by qbs."); - } - } - finally { - if (prlFile) - prlFile.close(); - } - - if (debugBuild) - modInfo.libFilePathDebug = libFilePath; - else - modInfo.libFilePathRelease = libFilePath; -} - -function setupLibraries(qtModuleInfo, qtProps, nonExistingPrlFiles, androidAbi) { - doSetupLibraries(qtModuleInfo, qtProps, true, nonExistingPrlFiles, androidAbi); - doSetupLibraries(qtModuleInfo, qtProps, false, nonExistingPrlFiles, androidAbi); -} - -function allQt4Modules(qtProps) { - // as per http://doc.qt.io/qt-4.8/modules.html + private stuff. - var modules = []; - - var core = makeQtModuleInfo("QtCore", "core"); - core.compilerDefines.push("QT_CORE_LIB"); - if (qtProps.qtNameSpace) - core.compilerDefines.push("QT_NAMESPACE=" + qtProps.qtNameSpace); - modules.push(core, - makeQtModuleInfo("QtCore", "core-private", ["core"]), - makeQtModuleInfo("QtGui", "gui"), - makeQtModuleInfo("QtGui", "gui-private", ["gui"]), - makeQtModuleInfo("QtMultimedia", "multimedia", ["gui", "network"]), - makeQtModuleInfo("QtMultimedia", "multimedia-private", ["multimedia"]), - makeQtModuleInfo("QtNetwork", "network"), - makeQtModuleInfo("QtNetwork", "network-private", ["network"]), - makeQtModuleInfo("QtOpenGL", "opengl", ["gui"]), - makeQtModuleInfo("QtOpenGL", "opengl-private", ["opengl"]), - makeQtModuleInfo("QtOpenVG", "openvg", ["gui"]), - makeQtModuleInfo("QtScript", "script"), - makeQtModuleInfo("QtScript", "script-private", ["script"]), - makeQtModuleInfo("QtScriptTools", "scripttools", ["script", "gui"]), - makeQtModuleInfo("QtScriptTools", "scripttools-private", ["scripttools"]), - makeQtModuleInfo("QtSql", "sql"), - makeQtModuleInfo("QtSql", "sql-private", ["sql"]), - makeQtModuleInfo("QtSvg", "svg", ["gui"]), - makeQtModuleInfo("QtSvg", "svg-private", ["svg"]), - makeQtModuleInfo("QtWebKit", "webkit", ["gui", "network"]), - makeQtModuleInfo("QtWebKit", "webkit-private", ["webkit"]), - makeQtModuleInfo("QtXml", "xml"), - makeQtModuleInfo("QtXml", "xml-private", ["xml"]), - makeQtModuleInfo("QtXmlPatterns", "xmlpatterns", ["network"]), - makeQtModuleInfo("QtXmlPatterns", "xmlpatterns-private", ["xmlpatterns"]), - makeQtModuleInfo("QtDeclarative", "declarative", ["gui", "script"]), - makeQtModuleInfo("QtDeclarative", "declarative-private", ["declarative"]), - makeQtModuleInfo("QtDesigner", "designer", ["gui", "xml"]), - makeQtModuleInfo("QtDesigner", "designer-private", ["designer"]), - makeQtModuleInfo("QtUiTools", "uitools"), - makeQtModuleInfo("QtUiTools", "uitools-private", ["uitools"]), - makeQtModuleInfo("QtHelp", "help", ["network", "sql"]), - makeQtModuleInfo("QtHelp", "help-private", ["help"]), - makeQtModuleInfo("QtTest", "testlib"), - makeQtModuleInfo("QtTest", "testlib-private", ["testlib"])); - if (qtProps.mkspecName.startsWith("win")) { - var axcontainer = makeQtModuleInfo("QAxContainer", "axcontainer"); - axcontainer.modulePrefix = "Q"; - axcontainer.isStaticLibrary = true; - axcontainer.includePaths.push(FileInfo.joinPaths(qtProps.includePath, "ActiveQt")); - modules.push(axcontainer); - - var axserver = makeQtModuleInfo("QAxServer", "axserver"); - axserver.modulePrefix = "Q"; - axserver.isStaticLibrary = true; - axserver.compilerDefines.push("QAXSERVER"); - axserver.includePaths.push(FileInfo.joinPaths(qtProps.includePath, "ActiveQt")); - modules.push(axserver); - } else { - modules.push(makeQtModuleInfo("QtDBus", "dbus")); - modules.push(makeQtModuleInfo("QtDBus", "dbus-private", ["dbus"])); - } - - var designerComponentsPrivate = makeQtModuleInfo( - "QtDesignerComponents", "designercomponents-private", - ["gui-private", "designer-private"]); - designerComponentsPrivate.hasLibrary = true; - modules.push(designerComponentsPrivate); - - var phonon = makeQtModuleInfo("Phonon", "phonon"); - phonon.includePaths = qt4ModuleIncludePaths(phonon, qtProps); - modules.push(phonon); - - // Set up include paths that haven't been set up before this point. - for (i = 0; i < modules.length; ++i) { - var module = modules[i]; - if (module.includePaths.length > 0) - continue; - module.includePaths = qt4ModuleIncludePaths(module, qtProps); - } - - // Set up compiler defines haven't been set up before this point. - for (i = 0; i < modules.length; ++i) { - module = modules[i]; - if (module.compilerDefines.length > 0) - continue; - module.compilerDefines.push("QT_" + module.qbsName.toUpperCase() + "_LIB"); - } - - // These are for the convenience of project file authors. It allows them - // to add a dependency to e.g. "Qt.widgets" without a version check. - var virtualModule = makeQtModuleInfo(undefined, "widgets", ["core", "gui"]); - virtualModule.hasLibrary = false; - modules.push(virtualModule); - virtualModule = makeQtModuleInfo(undefined, "quick", ["declarative"]); - virtualModule.hasLibrary = false; - modules.push(virtualModule); - virtualModule = makeQtModuleInfo(undefined, "concurrent"); - virtualModule.hasLibrary = false; - modules.push(virtualModule); - virtualModule = makeQtModuleInfo(undefined, "printsupport", ["core", "gui"]); - virtualModule.hasLibrary = false; - modules.push(virtualModule); - - addTestModule(modules); - addDesignerComponentsModule(modules); - - var modulesThatCanBeDisabled = [ - "xmlpatterns", "multimedia", "phonon", "svg", "webkit", "script", "scripttools", - "declarative", "gui", "dbus", "opengl", "openvg"]; - var nonExistingPrlFiles = []; - for (i = 0; i < modules.length; ++i) { - module = modules[i]; - var name = module.qbsName; - var privateIndex = name.indexOf("-private"); - if (privateIndex !== -1) - name = name.slice(0, privateIndex); - if (modulesThatCanBeDisabled.contains(name)) - module.mustExist = false; - if (qtProps.staticBuild) - module.isStaticLibrary = true; - setupLibraries(module, qtProps, nonExistingPrlFiles, ""); - } - replaceQtLibNamesWithFilePath(modules, qtProps); - - return modules; -} - -function getFileContentsRecursively(filePath) { - var file = new TextFile(filePath, TextFile.ReadOnly); - var lines = splitNonEmpty(file.readAll(), '\n'); - for (var i = 0; i < lines.length; ++i) { - var includeString = "include("; - var line = lines[i].trim(); - if (!line.startsWith(includeString)) - continue; - var offset = includeString.length; - var closingParenPos = line.indexOf(')', offset); - if (closingParenPos === -1) { - console.warn("Invalid include statement in '" + toNative(filePath) + "'"); - continue; - } - var includedFilePath = line.slice(offset, closingParenPos); - if (!FileInfo.isAbsolutePath(includedFilePath)) - includedFilePath = FileInfo.joinPaths(FileInfo.path(filePath), includedFilePath); - var includedContents = getFileContentsRecursively(includedFilePath); - var j = i; - for (var k = 0; k < includedContents.length; ++k) - lines.splice(++j, 0, includedContents[k]); - lines.splice(i--, 1); - } - file.close(); - return lines; -} - -function extractPaths(rhs, filePath) { - var paths = []; - var startIndex = 0; - for (;;) { - while (startIndex < rhs.length && rhs.charAt(startIndex) === ' ') - ++startIndex; - if (startIndex >= rhs.length) - break; - var endIndex; - if (rhs.charAt(startIndex) === '"') { - ++startIndex; - endIndex = rhs.indexOf('"', startIndex); - if (endIndex === -1) { - console.warn("Unmatched quote in file '" + toNative(filePath) + "'"); - break; - } - } else { - endIndex = rhs.indexOf(' ', startIndex + 1); - if (endIndex === -1) - endIndex = rhs.length; - } - paths.push(FileInfo.cleanPath(rhs.slice(startIndex, endIndex) - .replace("$$PWD", FileInfo.path(filePath)))); - startIndex = endIndex + 1; - } - return paths; -} - -function removeDuplicatedDependencyLibs(modules) { - var revDeps = {}; - var currentPath = []; - var getLibraries; - var getLibFilePath; - - function setupReverseDependencies(modules) { - var moduleByName = {}; - for (var i = 0; i < modules.length; ++i) - moduleByName[modules[i].qbsName] = modules[i]; - for (i = 0; i < modules.length; ++i) { - var module = modules[i]; - for (var j = 0; j < module.dependencies.length; ++j) { - var depmod = moduleByName[module.dependencies[j]]; - if (!depmod) - continue; - if (!revDeps[depmod.qbsName]) - revDeps[depmod.qbsName] = []; - revDeps[depmod.qbsName].push(module); - } - } - } - - function roots(modules) { - var result = []; - for (i = 0; i < modules.length; ++i) { - var module = modules[i] - if (module.dependencies.length === 0) - result.push(module); - } - return result; - } - - function traverse(module, libs) { - if (currentPath.contains(module)) - return; - currentPath.push(module); - var moduleLibraryLists = getLibraries(module); - for (var i = 0; i < moduleLibraryLists.length; ++i) { - var modLibList = moduleLibraryLists[i]; - for (j = modLibList.length - 1; j >= 0; --j) { - if (libs.contains(modLibList[j])) - modLibList.splice(j, 1); - } - } - - var libFilePath = getLibFilePath(module); - if (libFilePath) - libs.push(libFilePath); - for (i = 0; i < moduleLibraryLists.length; ++i) - libs = libs.concat(moduleLibraryLists[i]); - libs.sort(); - - var deps = revDeps[module.qbsName]; - for (i = 0; i < (deps || []).length; ++i) - traverse(deps[i], libs); - - currentPath.pop(); - } - - setupReverseDependencies(modules); - - // Traverse the debug variants of modules. - getLibraries = function(module) { - return [module.dynamicLibrariesDebug, module.staticLibrariesDebug]; - }; - getLibFilePath = function(module) { return module.libFilePathDebug; }; - var rootModules = roots(modules); - for (var i = 0; i < rootModules.length; ++i) - traverse(rootModules[i], []); - - // Traverse the release variants of modules. - getLibraries = function(module) { - return [module.dynamicLibrariesRelease, module.staticLibrariesRelease]; - }; - getLibFilePath = function(module) { return module.libFilePathRelease; }; - for (i = 0; i < rootModules.length; ++i) - traverse(rootModules[i], []); -} - -function allQt5Modules(qtProps, androidAbi) { - var nonExistingPrlFiles = []; - var modules = []; - var modulesDir = FileInfo.joinPaths(qtProps.mkspecBasePath, "modules"); - var modulePriFiles = File.directoryEntries(modulesDir, File.Files); - for (var i = 0; i < modulePriFiles.length; ++i) { - var priFileName = modulePriFiles[i]; - var priFilePath = FileInfo.joinPaths(modulesDir, priFileName); - var externalFileNamePrefix = "qt_ext_"; - var moduleFileNamePrefix = "qt_lib_"; - var pluginFileNamePrefix = "qt_plugin_"; - var moduleFileNameSuffix = ".pri"; - var fileHasExternalPrefix = priFileName.startsWith(externalFileNamePrefix); - var fileHasModulePrefix = priFileName.startsWith(moduleFileNamePrefix); - var fileHasPluginPrefix = priFileName.startsWith(pluginFileNamePrefix); - if (!fileHasPluginPrefix && !fileHasModulePrefix && !fileHasExternalPrefix - || !priFileName.endsWith(moduleFileNameSuffix)) { - continue; - } - var moduleInfo = makeQtModuleInfo(); - moduleInfo.isPlugin = fileHasPluginPrefix; - moduleInfo.isExternal = !moduleInfo.isPlugin && !fileHasModulePrefix; - var fileNamePrefix = moduleInfo.isPlugin ? pluginFileNamePrefix : moduleInfo.isExternal - ? externalFileNamePrefix : moduleFileNamePrefix; - moduleInfo.qbsName = priFileName.slice(fileNamePrefix.length, -moduleFileNameSuffix.length); - if (moduleInfo.isPlugin) { - moduleInfo.name = moduleInfo.qbsName; - moduleInfo.isStaticLibrary = true; - } - var moduleKeyPrefix = (moduleInfo.isPlugin ? "QT_PLUGIN" : "QT") - + '.' + moduleInfo.qbsName + '.'; - moduleInfo.qbsName = moduleInfo.qbsName.replace("_private", "-private"); - var hasV2 = false; - var hasModuleEntry = false; - var lines = getFileContentsRecursively(priFilePath); - if (moduleInfo.isExternal) { - moduleInfo.name = "qt" + moduleInfo.qbsName; - moduleInfo.isStaticLibrary = true; - for (var k = 0; k < lines.length; ++k) { - var extLine = lines[k].trim(); - var extFirstEqualsOffset = extLine.indexOf('='); - if (extFirstEqualsOffset === -1) - continue; - var extKey = extLine.slice(0, extFirstEqualsOffset).trim(); - var extValue = extLine.slice(extFirstEqualsOffset + 1).trim(); - if (!extKey.startsWith("QMAKE_") || !extValue) - continue; - - var elements = extKey.split('_'); - if (elements.length >= 3) { - if (elements[1] === "LIBS") { - extValue = extValue.replace("/home/qt/work/qt/qtbase/lib", - qtProps.libraryPath); - extValue = extValue.replace("$$[QT_INSTALL_LIBS]", qtProps.libraryPath); - extValue = extValue.replace("$$[QT_INSTALL_LIBS/get]", qtProps.libraryPath); - if (elements.length === 4 ) { - if (elements[3] === androidAbi) { - moduleInfo.staticLibrariesRelease.push(extValue); - moduleInfo.staticLibrariesDebug.push(extValue); - } - } else if (elements.length === 5 ) { - // That's for "x86_64" - var abi = elements[3] + '_' + elements[4]; - if (abi === androidAbi) { - moduleInfo.staticLibrariesRelease.push(extValue); - moduleInfo.staticLibrariesDebug.push(extValue); - } - } else { - moduleInfo.staticLibrariesRelease.push(extValue); - moduleInfo.staticLibrariesDebug.push(extValue); - } - } else if (elements[1] === "INCDIR") { - moduleInfo.includePaths.push(extValue.replace("$$[QT_INSTALL_HEADERS]", - qtProps.includePath)); - } - } - } - moduleInfo.compilerDefines.push("QT_" + moduleInfo.qbsName.toUpperCase() + "_LIB"); - moduleInfo.mustExist = false; - } else { - for (var j = 0; j < lines.length; ++j) { - var line = lines[j].trim(); - var firstEqualsOffset = line.indexOf('='); - if (firstEqualsOffset === -1) - continue; - var key = line.slice(0, firstEqualsOffset).trim(); - var value = line.slice(firstEqualsOffset + 1).trim(); - if (!key.startsWith(moduleKeyPrefix) || !value) - continue; - if (key.endsWith(".name")) { - moduleInfo.name = value; - } else if (key.endsWith(".module")) { - hasModuleEntry = true; - } else if (key.endsWith(".depends")) { - moduleInfo.dependencies = splitNonEmpty(value, ' '); - for (var k = 0; k < moduleInfo.dependencies.length; ++k) { - moduleInfo.dependencies[k] - = moduleInfo.dependencies[k].replace("_private", "-private"); - } - } else if (key.endsWith(".module_config")) { - var elems = splitNonEmpty(value, ' '); - for (k = 0; k < elems.length; ++k) { - var elem = elems[k]; - if (elem === "no_link") - moduleInfo.hasLibrary = false; - else if (elem === "staticlib") - moduleInfo.isStaticLibrary = true; - else if (elem === "internal_module") - moduleInfo.isPrivate = true; - else if (elem === "v2") - hasV2 = true; - } - } else if (key.endsWith(".includes")) { - moduleInfo.includePaths = extractPaths(value, priFilePath); - for (k = 0; k < moduleInfo.includePaths.length; ++k) { - moduleInfo.includePaths[k] = moduleInfo.includePaths[k] - .replace("$$QT_MODULE_INCLUDE_BASE", qtProps.includePath) - .replace("$$QT_MODULE_HOST_LIB_BASE", qtProps.hostLibraryPath) - .replace("$$QT_MODULE_LIB_BASE", qtProps.libraryPath); - } - } else if (key.endsWith(".libs")) { - var libDirs = extractPaths(value, priFilePath); - if (libDirs.length === 1) { - moduleInfo.libDir = libDirs[0] - .replace("$$QT_MODULE_HOST_LIB_BASE", qtProps.hostLibraryPath) - .replace("$$QT_MODULE_LIB_BASE", qtProps.libraryPath); - } else { - moduleInfo.libDir = qtProps.libraryPath; - } - } else if (key.endsWith(".DEFINES")) { - moduleInfo.compilerDefines = splitNonEmpty(value, ' '); - } else if (key.endsWith(".VERSION")) { - moduleInfo.version = value; - } else if (key.endsWith(".plugin_types")) { - moduleInfo.supportedPluginTypes = splitNonEmpty(value, ' '); - } else if (key.endsWith(".TYPE")) { - moduleInfo.pluginData.type = value; - } else if (key.endsWith(".EXTENDS")) { - moduleInfo.pluginData["extends"] = splitNonEmpty(value, ' '); - for (k = 0; k < moduleInfo.pluginData["extends"].length; ++k) { - if (moduleInfo.pluginData["extends"][k] === "-") { - moduleInfo.pluginData["extends"].splice(k, 1); - moduleInfo.pluginData.autoLoad = false; - break; - } - } - } else if (key.endsWith(".CLASS_NAME")) { - moduleInfo.pluginData.className = value; - } - } - } - if (hasV2 && !hasModuleEntry) - moduleInfo.hasLibrary = false; - - // Fix include paths for Apple frameworks. - // The qt_lib_XXX.pri files contain wrong values for versions < 5.6. - if (!hasV2 && isFramework(moduleInfo, qtProps)) { - moduleInfo.includePaths = []; - var baseIncDir = frameworkHeadersPath(moduleInfo, qtProps); - if (moduleInfo.isPrivate) { - baseIncDir = FileInfo.joinPaths(baseIncDir, moduleInfo.version); - moduleInfo.includePaths.push(baseIncDir, - FileInfo.joinPaths(baseIncDir, moduleInfo.name)); - } else { - moduleInfo.includePaths.push(baseIncDir); - } - } - - setupLibraries(moduleInfo, qtProps, nonExistingPrlFiles, androidAbi); - - modules.push(moduleInfo); - if (moduleInfo.qbsName === "testlib") - addTestModule(modules); - if (moduleInfo.qbsName === "designercomponents-private") - addDesignerComponentsModule(modules); - } - - replaceQtLibNamesWithFilePath(modules, qtProps); - removeDuplicatedDependencyLibs(modules); - return modules; -} - function extractQbsArchs(module, qtProps) { if (qtProps.mkspecName.startsWith("macx-")) { var archs = []; @@ -1328,7 +105,7 @@ function qbsTargetPlatformFromQtMkspec(qtProps) { return "solaris"; if (mkspec.startsWith("vxworks-")) return "vxworks"; - if (targetsDesktopWindows(qtProps) || mkspec.startsWith("winrt-")) + if (ProviderUtils.isDesktopWindowsQt(qtProps) || mkspec.startsWith("winrt-")) return "windows"; } @@ -1384,7 +161,7 @@ function defaultQpaPlugin(module, qtProps) { function libraryFileTag(module, qtProps) { if (module.isStaticLibrary) return "staticlibrary"; - return isMsvcQt(qtProps) || qtProps.mkspecName.startsWith("win32-g++") + return ProviderUtils.isMsvcQt(qtProps) || qtProps.mkspecName.startsWith("win32-g++") ? "dynamiclibrary_import" : "dynamiclibrary"; } @@ -1409,7 +186,20 @@ function findVariable(content, start) { } function minVersionJsString(minVersion) { - return !minVersion ? "original" : ModUtils.toJSLiteral(minVersion); + return !minVersion ? "" : ModUtils.toJSLiteral(minVersion); +} + +function abiToArchitecture(abi) { + switch (abi) { + case "armeabi-v7a": + return "armv7a"; + case "arm64-v8a": + return "arm64"; + case "x86": + case "x86_64": + default: + return abi; + } } function replaceSpecialValues(content, module, qtProps, abi) { @@ -1432,6 +222,7 @@ function replaceSpecialValues(content, module, qtProps, abi) { pluginPath: ModUtils.toJSLiteral(qtProps.pluginPath), incPath: ModUtils.toJSLiteral(qtProps.includePath), docPath: ModUtils.toJSLiteral(qtProps.documentationPath), + helpGeneratorLibExecPath: ModUtils.toJSLiteral(qtProps.helpGeneratorLibExecPath), mkspecName: ModUtils.toJSLiteral(qtProps.mkspecName), mkspecPath: ModUtils.toJSLiteral(qtProps.mkspecPath), version: ModUtils.toJSLiteral(qtProps.qtVersion), @@ -1439,7 +230,7 @@ function replaceSpecialValues(content, module, qtProps, abi) { availableBuildVariants: ModUtils.toJSLiteral(qtProps.buildVariant), staticBuild: ModUtils.toJSLiteral(qtProps.staticBuild), frameworkBuild: ModUtils.toJSLiteral(qtProps.frameworkBuild), - name: ModUtils.toJSLiteral(moduleNameWithoutPrefix(module)), + name: ModUtils.toJSLiteral(ProviderUtils.qtModuleNameWithoutPrefix(module)), has_library: ModUtils.toJSLiteral(module.hasLibrary), dependencies: ModUtils.toJSLiteral(module.dependencies), includes: ModUtils.toJSLiteral(module.includePaths), @@ -1456,18 +247,20 @@ function replaceSpecialValues(content, module, qtProps, abi) { frameworksRelease: ModUtils.toJSLiteral(module.frameworksRelease), libFilePathDebug: ModUtils.toJSLiteral(module.libFilePathDebug), libFilePathRelease: ModUtils.toJSLiteral(module.libFilePathRelease), - libNameForLinkerDebug: ModUtils.toJSLiteral(libNameForLinker(module, qtProps, true)), + libNameForLinkerDebug: + ModUtils.toJSLiteral(ProviderUtils.qtLibNameForLinker(module, qtProps, true)), pluginTypes: ModUtils.toJSLiteral(module.supportedPluginTypes), moduleConfig: ModUtils.toJSLiteral(module.config), - libNameForLinkerRelease: ModUtils.toJSLiteral(libNameForLinker(module, qtProps, false)), + libNameForLinkerRelease: + ModUtils.toJSLiteral(ProviderUtils.qtLibNameForLinker(module, qtProps, false)), entryPointLibsDebug: ModUtils.toJSLiteral(qtProps.entryPointLibsDebug), entryPointLibsRelease: ModUtils.toJSLiteral(qtProps.entryPointLibsRelease), - minWinVersion: minVersionJsString(qtProps.windowsVersion), - minMacVersion: minVersionJsString(qtProps.macosVersion), - minIosVersion: minVersionJsString(qtProps.iosVersion), - minTvosVersion: minVersionJsString(qtProps.tvosVersion), - minWatchosVersion: minVersionJsString(qtProps.watchosVersion), - minAndroidVersion: minVersionJsString(qtProps.androidVersion), + minWinVersion_optional: minVersionJsString(qtProps.windowsVersion), + minMacVersion_optional: minVersionJsString(qtProps.macosVersion), + minIosVersion_optional: minVersionJsString(qtProps.iosVersion), + minTvosVersion_optional: minVersionJsString(qtProps.tvosVersion), + minWatchosVersion_optional: minVersionJsString(qtProps.watchosVersion), + minAndroidVersion_optional: minVersionJsString(qtProps.androidVersion), }; var additionalContent = ""; @@ -1519,16 +312,15 @@ function replaceSpecialValues(content, module, qtProps, abi) { + indent + indent + "fileTags: [\"qt.core.metatypes\"]\n" + indent + "}"; } - if (module.hasLibrary && !isFramework(module, qtProps)) { + if (module.hasLibrary && !ProviderUtils.qtIsFramework(module, qtProps)) { if (additionalContent) additionalContent += "\n" + indent; additionalContent += "Group {\n"; if (module.isPlugin) { additionalContent += indent + indent - + "condition: Qt[\"" + module.qbsName + "\"].enableLinking\n"; + + "condition: enableLinking\n"; } - additionalContent += indent + indent + "files: [Qt[\"" + module.qbsName + "\"]" - + ".libFilePath]\n" + additionalContent += indent + indent + "files: libFilePath\n" + indent + indent + "filesAreTargets: true\n" + indent + indent + "fileTags: [\"" + libraryFileTag(module, qtProps) + "\"]\n" @@ -1538,9 +330,21 @@ function replaceSpecialValues(content, module, qtProps, abi) { for (var pos = findVariable(content, 0); pos[0] !== -1; pos = findVariable(content, pos[0])) { - var replacement = dict[content.slice(pos[0] + 1, pos[1])] || ""; - content = content.slice(0, pos[0]) + replacement + content.slice(pos[1] + 1); - pos[0] += replacement.length; + var varName = content.slice(pos[0] + 1, pos[1]); + var replacement = dict[varName] || ""; + if (!replacement && varName.endsWith("_optional")) { + var prevNewline = content.lastIndexOf('\n', pos[0]); + if (prevNewline === -1) + prevNewline = 0; + var nextNewline = content.indexOf('\n', pos[0]); + if (nextNewline === -1) + prevNewline = content.length; + content = content.slice(0, prevNewline) + content.slice(nextNewline); + pos[0] = prevNewline; + } else { + content = content.slice(0, pos[0]) + replacement + content.slice(pos[1] + 1); + pos[0] += replacement.length; + } } return content; } @@ -1549,7 +353,7 @@ function copyTemplateFile(fileName, targetDirectory, qtProps, abi, location, all pluginMap, nonEssentialPlugins) { if (!File.makePath(targetDirectory)) { - throw "Cannot create directory '" + toNative(targetDirectory) + "'."; + throw "Cannot create directory '" + FileInfo.toNativeSeparators(targetDirectory) + "'."; } var sourceFile = new TextFile(FileInfo.joinPaths(location, "templates", fileName), TextFile.ReadOnly); @@ -1571,67 +375,64 @@ function copyTemplateFile(fileName, targetDirectory, qtProps, abi, location, all targetFile.close(); } -function setupOneQt(qmakeFilePath, outputBaseDir, uniquify, location, qbs) { - if (!File.exists(qmakeFilePath)) - throw "The specified qmake file path '" + toNative(qmakeFilePath) + "' does not exist."; - var qtProps = getQtProperties(qmakeFilePath, qbs); - var androidAbis = []; - if (qtProps.androidAbis !== undefined) - // Multiple androidAbis detected: Qt >= 5.14 - androidAbis = qtProps.androidAbis; - else - // Single abi detected: Qt < 5.14 - androidAbis.push(''); - if (androidAbis.length > 1) - console.info("Qt with multiple abi detected: '" + androidAbis + "'"); +function setupOneQt(moduleName, qtInfo, outputBaseDir, uniquify, location) { + var qtProps = qtInfo.qtProps; var relativeSearchPaths = []; - for (a = 0; a < androidAbis.length; ++a) { - if (androidAbis.length > 1) - console.info("Configuring abi '" + androidAbis[a] + "'..."); - var modules = qtProps.qtMajorVersion < 5 ? allQt4Modules(qtProps) : - allQt5Modules(qtProps,androidAbis[a]); - var pluginsByType = {}; - var nonEssentialPlugins = []; - for (var i = 0; i < modules.length; ++i) { - var m = modules[i]; - if (m.isPlugin) { - if (!pluginsByType[m.pluginData.type]) - pluginsByType[m.pluginData.type] = []; - pluginsByType[m.pluginData.type].push(m.name); - if (!m.pluginData.autoLoad) - nonEssentialPlugins.push(m.name); - } - } - - var relativeSearchPath = uniquify ? Utilities.getHash(qmakeFilePath) : ""; - relativeSearchPath = FileInfo.joinPaths(relativeSearchPath, androidAbis[a]); + for (a = 0; a < qtInfo.abiInfos.length; ++a) { + var abiInfo = qtInfo.abiInfos[a]; + var androidAbi = abiInfo.androidAbi; + if (qtInfo.abiInfos.length > 1) + console.info("Configuring abi '" + androidAbi + "'..."); + + var relativeSearchPath = uniquify ? Utilities.getHash(qtInfo.qmakeFilePath) : ""; + relativeSearchPath = FileInfo.joinPaths(relativeSearchPath, androidAbi); var qbsQtModuleBaseDir = FileInfo.joinPaths(outputBaseDir, relativeSearchPath, "modules", "Qt"); - if (File.exists(qbsQtModuleBaseDir)) - File.remove(qbsQtModuleBaseDir); + // TODO: + // if (File.exists(qbsQtModuleBaseDir)) + // File.remove(qbsQtModuleBaseDir); var allFiles = []; - copyTemplateFile("QtModule.qbs", qbsQtModuleBaseDir, qtProps, androidAbis[a], location, - allFiles); - copyTemplateFile("QtPlugin.qbs", qbsQtModuleBaseDir, qtProps, androidAbis[a], location, - allFiles); - copyTemplateFile("plugin_support.qbs", - FileInfo.joinPaths(qbsQtModuleBaseDir, "plugin_support"), qtProps, - androidAbis[a], location, allFiles, undefined, pluginsByType, - nonEssentialPlugins); + if (moduleName === "plugin_support") { + copyTemplateFile("plugin_support.qbs", + FileInfo.joinPaths(qbsQtModuleBaseDir, "plugin_support"), qtProps, + androidAbi, location, allFiles, undefined, abiInfo.pluginsByType, + abiInfo.nonEssentialPlugins); + relativeSearchPaths.push(relativeSearchPath); + return relativeSearchPaths; + } else if (moduleName === "android_support") { + // Note that it's not strictly necessary to copy this one, as it has no variable content. + // But we do it anyway for consistency. + copyTemplateFile("android_support.qbs", + FileInfo.joinPaths(qbsQtModuleBaseDir, "android_support"), + qtProps, androidAbi, location, allFiles); + relativeSearchPaths.push(relativeSearchPath); + return relativeSearchPaths; + } else if (moduleName === "qmlcache") { + var qmlcacheStr = "qmlcache"; + if (File.exists(FileInfo.joinPaths(qtProps.qmlLibExecPath, + "qmlcachegen" + FileInfo.executableSuffix()))) { + copyTemplateFile(qmlcacheStr + ".qbs", + FileInfo.joinPaths(qbsQtModuleBaseDir, qmlcacheStr), qtProps, + androidAbi, location, allFiles); + } + relativeSearchPaths.push(relativeSearchPath); + return relativeSearchPaths; + } - for (i = 0; i < modules.length; ++i) { - var module = modules[i]; + if (abiInfo.modules[moduleName] !== undefined) { + var module = abiInfo.modules[moduleName]; var qbsQtModuleDir = FileInfo.joinPaths(qbsQtModuleBaseDir, module.qbsName); var moduleTemplateFileName; + if (module.qbsName === "core") { moduleTemplateFileName = "core.qbs"; - copyTemplateFile("moc.js", qbsQtModuleDir, qtProps, androidAbis[a], location, + copyTemplateFile("moc.js", qbsQtModuleDir, qtProps, androidAbi, location, allFiles); - copyTemplateFile("qdoc.js", qbsQtModuleDir, qtProps, androidAbis[a], location, + copyTemplateFile("qdoc.js", qbsQtModuleDir, qtProps, androidAbi, location, allFiles); - copyTemplateFile("rcc.js", qbsQtModuleDir, qtProps, androidAbis[a], location, + copyTemplateFile("rcc.js", qbsQtModuleDir, qtProps, androidAbi, location, allFiles); } else if (module.qbsName === "gui") { moduleTemplateFileName = "gui.qbs"; @@ -1639,62 +440,51 @@ function setupOneQt(qmakeFilePath, outputBaseDir, uniquify, location, qbs) { moduleTemplateFileName = "scxml.qbs"; } else if (module.qbsName === "dbus") { moduleTemplateFileName = "dbus.qbs"; - copyTemplateFile("dbus.js", qbsQtModuleDir, qtProps, androidAbis[a], location, + copyTemplateFile("dbus.js", qbsQtModuleDir, qtProps, androidAbi, location, allFiles); } else if (module.qbsName === "qml") { moduleTemplateFileName = "qml.qbs"; - copyTemplateFile("qml.js", qbsQtModuleDir, qtProps, androidAbis[a], location, + copyTemplateFile("qml.js", qbsQtModuleDir, qtProps, androidAbi, location, allFiles); - var qmlcacheStr = "qmlcache"; - if (File.exists(FileInfo.joinPaths(qtProps.qmlLibExecPath, - "qmlcachegen" + exeSuffix(qbs)))) { - copyTemplateFile(qmlcacheStr + ".qbs", - FileInfo.joinPaths(qbsQtModuleBaseDir, qmlcacheStr), qtProps, - androidAbis[a], location, allFiles); - } } else if (module.qbsName === "quick") { moduleTemplateFileName = "quick.qbs"; - copyTemplateFile("quick.js", qbsQtModuleDir, qtProps, androidAbis[a], location, + copyTemplateFile("quick.js", qbsQtModuleDir, qtProps, androidAbi, location, allFiles); - copyTemplateFile("rcc.js", qbsQtModuleDir, qtProps, androidAbis[a], location, + copyTemplateFile("rcc.js", qbsQtModuleDir, qtProps, androidAbi, location, allFiles); } else if (module.isPlugin) { moduleTemplateFileName = "plugin.qbs"; } else { moduleTemplateFileName = "module.qbs"; } - copyTemplateFile(moduleTemplateFileName, qbsQtModuleDir, qtProps, androidAbis[a], + copyTemplateFile(moduleTemplateFileName, qbsQtModuleDir, qtProps, androidAbi, location, allFiles, module); + relativeSearchPaths.push(relativeSearchPath); } - - // Note that it's not strictly necessary to copy this one, as it has no variable content. - // But we do it anyway for consistency. - copyTemplateFile("android_support.qbs", - FileInfo.joinPaths(qbsQtModuleBaseDir, "android_support"), - qtProps, androidAbis[a], location, allFiles); - relativeSearchPaths.push(relativeSearchPath) } return relativeSearchPaths; } -function doSetup(qmakeFilePaths, outputBaseDir, location, qbs) { - qmakeFilePaths = getQmakeFilePaths(qmakeFilePaths, qbs); - if (!qmakeFilePaths || qmakeFilePaths.length === 0) +function doSetup(moduleName, qtInfos, outputBaseDir, location) { + if (!qtInfos || qtInfos.length === 0) return []; - var uniquifySearchPath = qmakeFilePaths.length > 1; + var uniquifySearchPath = qtInfos.length > 1; var allSearchPaths = []; - for (var i = 0; i < qmakeFilePaths.length; ++i) { + moduleName = moduleName.substring(3); + for (var i = 0; i < qtInfos.length; ++i) { try { - console.info("Setting up Qt at '" + toNative(qmakeFilePaths[i]) + "'..."); - var searchPaths = setupOneQt(qmakeFilePaths[i], outputBaseDir, uniquifySearchPath, - location, qbs); + console.info("Setting up Qt module '" + moduleName + "' for Qt located at '" + + FileInfo.toNativeSeparators(qtInfos[i].qmakeFilePath) + "'."); + var searchPaths = setupOneQt(moduleName, qtInfos[i], outputBaseDir, uniquifySearchPath, + location); if (searchPaths.length > 0) { for (var j = 0; j < searchPaths.length; ++j ) allSearchPaths.push(searchPaths[j]); - console.info("Qt was set up successfully."); } } catch (e) { - console.warn("Error setting up Qt for '" + toNative(qmakeFilePaths[i]) + "': " + e); + console.warn("Error setting up Qt module '" + moduleName + "' for '" + + FileInfo.toNativeSeparators(qtInfos[i].qmakeFilePath) + "': " + e); + throw e; } } return allSearchPaths; diff --git a/share/qbs/module-providers/Qt/templates/android_support.qbs b/share/qbs/module-providers/Qt/templates/android_support.qbs index 68276070f..6d548f194 100644 --- a/share/qbs/module-providers/Qt/templates/android_support.qbs +++ b/share/qbs/module-providers/Qt/templates/android_support.qbs @@ -21,7 +21,7 @@ Module { property string _qtBinaryDir property string _qtInstallDir property bool _enableSdkSupport: product.type && product.type.contains("android.package") - && !consoleApplication + && !product.consoleApplication property bool _enableNdkSupport: !product.aggregate || product.multiplexConfigurationId property string _templatesBaseDir: FileInfo.joinPaths(_qtInstallDir, "src", "android") property string _deployQtOutDir: FileInfo.joinPaths(product.buildDirectory, "deployqt_out") @@ -220,9 +220,11 @@ Module { f.writeLine('"qml-import-paths": "' + product.qmlImportPaths.join(',') + '",'); if (Utilities.versionCompare(product.Qt.android_support.version, "6.0") >= 0) { - f.writeLine('"qml-importscanner-binary": "' + - product.Qt.core.qmlImportScannerFilePath + '",'); - f.writeLine('"rcc-binary": "' + product.Qt.android_support.rccFilePath + '",'); + f.writeLine('"qml-importscanner-binary": "' + + product.Qt.core.qmlImportScannerFilePath + FileInfo.executableSuffix() + + '",'); + f.writeLine('"rcc-binary": "' + product.Qt.android_support.rccFilePath + + FileInfo.executableSuffix() + '",'); if (inputs["qrc"] && inputs["qrc"].length > 0) { var qrcFiles = []; diff --git a/share/qbs/module-providers/Qt/templates/core.qbs b/share/qbs/module-providers/Qt/templates/core.qbs index c3c68164f..485402716 100644 --- a/share/qbs/module-providers/Qt/templates/core.qbs +++ b/share/qbs/module-providers/Qt/templates/core.qbs @@ -18,7 +18,7 @@ Module { && qbs.targetPlatform === targetPlatform + "-simulator" Depends { name: "cpp" } - Depends { name: "Sanitizers.address" } + Depends { name: "Sanitizers.address"; condition: config.contains("sanitize_address") } Depends { name: "Qt.android_support"; condition: qbs.targetOS.contains("android") } Properties { @@ -57,6 +57,7 @@ Module { property string qdocName: versionMajor >= 5 ? "qdoc" : "qdoc3" property stringList qdocEnvironment property path docPath: @docPath@ + property string helpGeneratorLibExecPath: @helpGeneratorLibExecPath@ property stringList helpGeneratorArgs: versionMajor >= 5 ? ["-platform", "minimal"] : [] property var versionParts: version ? version.split('.').map(function(item) { return parseInt(item, 10); }) : [] property int versionMajor: versionParts[0] @@ -75,6 +76,8 @@ Module { property string qtBuildVariant: { if (availableBuildVariants.contains(qbs.buildVariant)) return qbs.buildVariant; + if (qbs.buildVariant === "profiling" && availableBuildVariants.contains("release")) + return "release"; return availableBuildVariants.length > 0 ? availableBuildVariants[0] : ""; } @@ -106,6 +109,7 @@ Module { property string libFilePathRelease: @libFilePathRelease@ property string libFilePath: qtBuildVariant === "debug" ? libFilePathDebug : libFilePathRelease + property bool useRPaths: qbs.targetOS.contains("linux") && !qbs.targetOS.contains("android") property stringList coreLibPaths: @libraryPaths@ property bool hasLibrary: true @@ -120,8 +124,6 @@ Module { property stringList moduleConfig: @moduleConfig@ - Sanitizers.address.enabled: config.contains("sanitize_address") - Properties { condition: moduleConfig.contains("use_gold_linker") cpp.linkerVariant: "gold" @@ -150,6 +152,8 @@ Module { if (Utilities.versionCompare(version, "5.6.0") < 0) defines.push("main=qtmn"); } + if (qbs.toolchain.contains("msvc")) + defines.push("_ENABLE_EXTENDED_ALIGNED_STORAGE"); return defines; } cpp.driverFlags: { @@ -195,8 +199,7 @@ Module { return undefined; return frameworks; } - cpp.rpaths: qbs.targetOS.contains('linux') && !qbs.targetOS.contains("android") ? [libPath] : - undefined + cpp.rpaths: useRPaths ? libPath : undefined cpp.runtimeLibrary: qbs.toolchain.contains("msvc") ? config.contains("static_runtime") ? "static" : "dynamic" : original @@ -219,12 +222,12 @@ Module { return "libc++"; return original; } - cpp.minimumWindowsVersion: @minWinVersion@ - cpp.minimumMacosVersion: @minMacVersion@ - cpp.minimumIosVersion: @minIosVersion@ - cpp.minimumTvosVersion: @minTvosVersion@ - cpp.minimumWatchosVersion: @minWatchosVersion@ - cpp.minimumAndroidVersion: @minAndroidVersion@ + cpp.minimumWindowsVersion: @minWinVersion_optional@ + cpp.minimumMacosVersion: @minMacVersion_optional@ + cpp.minimumIosVersion: @minIosVersion_optional@ + cpp.minimumTvosVersion: @minTvosVersion_optional@ + cpp.minimumWatchosVersion: @minWatchosVersion_optional@ + cpp.minimumAndroidVersion: @minAndroidVersion_optional@ // Universal Windows Platform support cpp.windowsApiFamily: mkspecName.startsWith("winrt-") ? "pc" : undefined @@ -545,7 +548,8 @@ Module { args = args.concat(product.Qt.core.helpGeneratorArgs); args.push("-o"); args.push(output.filePath); - var cmd = new Command(product.Qt.core.binPath + "/qhelpgenerator", args); + var cmd = new Command( + product.Qt.core.helpGeneratorLibExecPath + "/qhelpgenerator", args); cmd.description = 'qhelpgenerator ' + input.fileName; cmd.highlight = 'filegen'; cmd.stdoutFilterFunction = function(output) { diff --git a/share/qbs/module-providers/Qt/templates/dbus.qbs b/share/qbs/module-providers/Qt/templates/dbus.qbs index 08e1e49e7..bbda5a4f2 100644 --- a/share/qbs/module-providers/Qt/templates/dbus.qbs +++ b/share/qbs/module-providers/Qt/templates/dbus.qbs @@ -1,6 +1,5 @@ import qbs.FileInfo import qbs.ModUtils -import "../QtModule.qbs" as QtModule import "dbus.js" as DBus QtModule { diff --git a/share/qbs/module-providers/Qt/templates/gui.qbs b/share/qbs/module-providers/Qt/templates/gui.qbs index 6ba5e48c4..db491eafe 100644 --- a/share/qbs/module-providers/Qt/templates/gui.qbs +++ b/share/qbs/module-providers/Qt/templates/gui.qbs @@ -1,7 +1,6 @@ import qbs.FileInfo import qbs.ModUtils import qbs.Utilities -import '../QtModule.qbs' as QtModule QtModule { qtModuleName: "Gui" diff --git a/share/qbs/module-providers/Qt/templates/module.qbs b/share/qbs/module-providers/Qt/templates/module.qbs index ccafe4122..9f0313ab5 100644 --- a/share/qbs/module-providers/Qt/templates/module.qbs +++ b/share/qbs/module-providers/Qt/templates/module.qbs @@ -1,5 +1,3 @@ -import '../QtModule.qbs' as QtModule - QtModule { qtModuleName: @name@ Depends { name: "Qt"; submodules: @dependencies@} diff --git a/share/qbs/module-providers/Qt/templates/plugin.qbs b/share/qbs/module-providers/Qt/templates/plugin.qbs index e73e2a4d9..34d4f4153 100644 --- a/share/qbs/module-providers/Qt/templates/plugin.qbs +++ b/share/qbs/module-providers/Qt/templates/plugin.qbs @@ -1,5 +1,3 @@ -import '../QtPlugin.qbs' as QtPlugin - QtPlugin { qtModuleName: @name@ Depends { name: "Qt"; submodules: @dependencies@} diff --git a/share/qbs/module-providers/Qt/templates/qml.js b/share/qbs/module-providers/Qt/templates/qml.js index 36f60f8a3..38462dcf7 100644 --- a/share/qbs/module-providers/Qt/templates/qml.js +++ b/share/qbs/module-providers/Qt/templates/qml.js @@ -39,16 +39,32 @@ function getPrlRhs(line) return line.split('=')[1].trim(); } -function getLibsForPlugin(pluginData, buildVariant, targetOS, toolchain, qtLibDir, qtDir) +function getLibsForPlugin(pluginData, product) { + var targetOS = product.qbs.targetOS; + var toolchain = product.qbs.toolchain; + var buildVariant = product.Qt.core.qtBuildVariant; + var qtLibDir = product.Qt.core.libPath; + var qtPluginDir = product.Qt.core.pluginPath; + var qtDir = product.Qt.core.installPrefixPath; + var qtQmlPath = product.Qt.qml.qmlPath; + if (!pluginData.path) return ""; var prlFileName = ""; if (!targetOS.contains("windows")) prlFileName += "lib"; prlFileName += pluginData.plugin; - if (buildVariant === "debug" && targetOS.contains("windows")) - prlFileName += "d"; + if (buildVariant === "debug") { + if (targetOS.contains("windows")) { + prlFileName += "d"; + } else if (product.Qt.core.versionMajor >= 6 && + (targetOS.contains("ios") + || targetOS.contains("tvos") + || targetOS.contains("watchos"))) { + prlFileName += "_debug"; + } + } prlFileName += ".prl"; var prlFilePath = FileInfo.joinPaths(pluginData.path, prlFileName); if (!File.exists(prlFilePath)) { @@ -74,8 +90,10 @@ function getLibsForPlugin(pluginData, buildVariant, targetOS, toolchain, qtLibDi otherLibsLine = otherLibsLine.replace(/-l([^ ]+)/g, "$1" + ".lib"); } otherLibsLine = otherLibsLine.replace(/\$\$\[QT_INSTALL_LIBS\]/g, qtLibDir); + otherLibsLine = otherLibsLine.replace(/\$\$\[QT_INSTALL_PLUGINS\]/g, qtPluginDir); otherLibsLine = otherLibsLine.replace(/\$\$\[QT_INSTALL_PREFIX\]/g, qtDir); - otherLibs = otherLibs.concat(otherLibsLine.split(' ')); + otherLibsLine = otherLibsLine.replace(/\$\$\[QT_INSTALL_QML\]/g, qtQmlPath); + otherLibs = otherLibs.concat(otherLibsLine.split(' ').map(FileInfo.cleanPath)); } } if (!pluginLib) diff --git a/share/qbs/module-providers/Qt/templates/qml.qbs b/share/qbs/module-providers/Qt/templates/qml.qbs index 747558f10..0a938e58d 100644 --- a/share/qbs/module-providers/Qt/templates/qml.qbs +++ b/share/qbs/module-providers/Qt/templates/qml.qbs @@ -1,7 +1,6 @@ import qbs.FileInfo import qbs.Host import qbs.TextFile -import '../QtModule.qbs' as QtModule import "qml.js" as Qml QtModule { @@ -170,12 +169,7 @@ QtModule { } if (cppFile) cppFile.writeLine("Q_IMPORT_PLUGIN(" + className + ")"); - var libs = Qml.getLibsForPlugin(scannerData[p], - product.Qt.core.qtBuildVariant, - product.qbs.targetOS, - product.qbs.toolchain, - product.Qt.core.libPath, - product.Qt.core.installPrefixPath); + var libs = Qml.getLibsForPlugin(scannerData[p], product); for (var i = 0; i < libs.length; ++i) { var lib = libs[i]; if (!lib.endsWith(product.cpp.objectSuffix) diff --git a/share/qbs/module-providers/Qt/templates/qmlcache.qbs b/share/qbs/module-providers/Qt/templates/qmlcache.qbs index 9a5956884..7047884c0 100644 --- a/share/qbs/module-providers/Qt/templates/qmlcache.qbs +++ b/share/qbs/module-providers/Qt/templates/qmlcache.qbs @@ -1,6 +1,5 @@ import qbs.File import qbs.FileInfo -import qbs.Host import qbs.Process import qbs.Utilities @@ -11,7 +10,7 @@ Module { throw "qmlcachegen unsupported for this target"; } property string qmlCacheGenPath: FileInfo.joinPaths(Qt.core.qmlLibExecPath, "qmlcachegen") - + (Host.os().contains("windows") ? ".exe" : "") + + FileInfo.executableSuffix() property bool supportsAllArchitectures: Utilities.versionCompare(Qt.core.version, "5.11") >= 0 property string installDir diff --git a/share/qbs/module-providers/Qt/templates/quick.js b/share/qbs/module-providers/Qt/templates/quick.js index e677143ea..d7e58984f 100644 --- a/share/qbs/module-providers/Qt/templates/quick.js +++ b/share/qbs/module-providers/Qt/templates/quick.js @@ -37,7 +37,7 @@ function scanQrc(product, qrcFilePath) { var result = []; var process = new Process(); try { - var rcc = FileInfo.joinPaths(Rcc.fullPath(product) + product.cpp.executableSuffix); + var rcc = FileInfo.joinPaths(Rcc.fullPath(product) + FileInfo.executableSuffix()); var exitCode = process.exec(rcc, ["--list", qrcFilePath], true); for (;;) { var line = process.readLine(); diff --git a/share/qbs/module-providers/Qt/templates/quick.qbs b/share/qbs/module-providers/Qt/templates/quick.qbs index 276246d6d..5fc4aa349 100644 --- a/share/qbs/module-providers/Qt/templates/quick.qbs +++ b/share/qbs/module-providers/Qt/templates/quick.qbs @@ -33,7 +33,6 @@ import qbs.FileInfo import qbs.Process import qbs.TextFile import qbs.Utilities -import '../QtModule.qbs' as QtModule import 'quick.js' as QC QtModule { @@ -75,7 +74,7 @@ QtModule { : Qt.core.binPath property string compilerBaseName: (_compilerIsQmlCacheGen ? "qmlcachegen" : "qtquickcompiler") property string compilerFilePath: FileInfo.joinPaths(_compilerBaseDir, - compilerBaseName + product.cpp.executableSuffix) + compilerBaseName + FileInfo.executableSuffix()) property bool compilerAvailable: File.exists(compilerFilePath); property bool useCompiler: compilerAvailable && !_compilerIsQmlCacheGen diff --git a/share/qbs/module-providers/Qt/templates/scxml.qbs b/share/qbs/module-providers/Qt/templates/scxml.qbs index fdd11e952..757041b52 100644 --- a/share/qbs/module-providers/Qt/templates/scxml.qbs +++ b/share/qbs/module-providers/Qt/templates/scxml.qbs @@ -1,10 +1,11 @@ import qbs.FileInfo import qbs.Utilities -import "../QtModule.qbs" as QtModule QtModule { qtModuleName: "Scxml" + property string _qscxmlcDir: Utilities.versionCompare(Qt.core.version, "6.3") >= 0 + ? Qt.core.libExecPath : Qt.core.binPath property string qscxmlcName: "qscxmlc" property string className property string namespace @@ -26,8 +27,7 @@ QtModule { prepare: { var compilerName = product.moduleProperty("Qt.scxml", "qscxmlcName"); - var compilerPath = FileInfo.joinPaths(input.moduleProperty("Qt.core", "binPath"), - compilerName); + var compilerPath = FileInfo.joinPaths(input.Qt.scxml._qscxmlcDir, compilerName); var args = ["--header", outputs["hpp"][0].filePath, "--impl", outputs["cpp"][0].filePath]; var cxx98 = input.moduleProperty("cpp", "cxxLanguageVersion") === "c++98"; diff --git a/share/qbs/module-providers/qbspkgconfig.qbs b/share/qbs/module-providers/qbspkgconfig.qbs index 7c31060f2..ca04f0613 100644 --- a/share/qbs/module-providers/qbspkgconfig.qbs +++ b/share/qbs/module-providers/qbspkgconfig.qbs @@ -40,9 +40,10 @@ import qbs.Environment import qbs.File import qbs.FileInfo -import qbs.Host import qbs.ModUtils import qbs.PkgConfig +import qbs.ProviderUtils +import qbs.Probes import qbs.Process import qbs.TextFile @@ -53,37 +54,31 @@ ModuleProvider { property stringList extraPaths property stringList libDirs property bool staticMode: false - property path sysroot: { - if (qbs.targetOS.contains("macos")) - return ""; - return qbs.sysroot; - } - property bool mergeDependencies: true - relativeSearchPaths: { + // We take the sysroot default from qbs.sysroot, except for Xcode toolchains, where + // the sysroot points into the Xcode installation and does not contain .pc files. + property path sysroot: qbs.toolchain && qbs.toolchain.includes("xcode") + ? undefined : qbs.sysroot - function exeSuffix(qbs) { return Host.os().contains("windows") ? ".exe" : ""; } + property bool mergeDependencies: false + PropertyOptions { + name: "mergeDependencies" + removalVersion: "2.3.0" + } - // we need Probes in Providers... - function getPkgConfigExecutable(qbs) { - function splitNonEmpty(s, c) { return s.split(c).filter(function(e) { return e; }) } + Probes.QbsPkgConfigProbe { + id: theProbe + // TODO: without explicit 'parent' we do not have access to the fake "qbs" scope + _executableFilePath: parent.executableFilePath + _extraPaths: parent.extraPaths + _sysroot: parent.sysroot + _libDirs: parent.libDirs + _staticMode: parent.staticMode + } - var pathValue = Environment.getEnv("PATH"); - if (!pathValue) - return undefined; - var dirs = splitNonEmpty(pathValue, FileInfo.pathListSeparator()); - var suffix = exeSuffix(qbs); - var filePaths = []; - for (var i = 0; i < dirs.length; ++i) { - var candidate = FileInfo.joinPaths(dirs[i], "pkg-config" + suffix); - var canonicalCandidate = FileInfo.canonicalPath(candidate); - if (!canonicalCandidate || !File.exists(canonicalCandidate)) - continue; - return canonicalCandidate; - } - return undefined; - } + isEager: false + relativeSearchPaths: { function getModuleInfo(pkg, staticMode) { var result = {}; @@ -125,14 +120,12 @@ ModuleProvider { return result; } - function getModuleName(packageName) { return packageName.replace('.', '-'); } - function getModuleDependencies(pkg, staticMode) { var mapper = function(p) { var result = {}; for (var key in p) result[key] = p[key]; - result.name = getModuleName(result.name); + result.name = ProviderUtils.pkgConfigToModuleName(result.name); return result; } var result = pkg.requires.map(mapper); @@ -141,132 +134,85 @@ ModuleProvider { return result; } - console.debug("Running pkgconfig provider.") + console.debug("Running pkgconfig provider for " + moduleName + "."); var outputDir = FileInfo.joinPaths(outputBaseDir, "modules"); File.makePath(outputDir); - var options = {}; - options.libDirs = libDirs; - options.sysroot = sysroot; - options.staticMode = staticMode; - options.mergeDependencies = mergeDependencies; - options.extraPaths = extraPaths; - if (options.sysroot && !options.libDirs) { - options.libDirs = [ - sysroot + "/usr/lib/pkgconfig", - sysroot + "/usr/share/pkgconfig" - ]; - } - if (!options.libDirs) { - // if we have pkg-config installed, let's ask it for its search paths (since - // built-in search paths can differ between platforms) - var executable = executableFilePath ? executableFilePath : getPkgConfigExecutable(qbs); - if (executable) { - var p = new Process() - if (p.exec(executable, ['pkg-config', '--variable=pc_path']) === 0) { - var stdout = p.readStdOut().trim(); - // TODO: pathListSeparator? depends on what pkg-config prints on Windows - options.libDirs = stdout ? stdout.split(':'): []; - } - } - } - - function setupQt(pkg) { - var packageName = pkg.baseFileName; - if (packageName === "QtCore" - || packageName === "Qt5Core" - || packageName === "Qt6Core") { - var hostBins = pkg.variables["host_bins"]; - if (!hostBins) { - if (packageName === "QtCore") { // Qt4 does not have host_bins - var mocLocation = pkg.variables["moc_location"]; - if (!mocLocation) { - console.warn("No moc_location variable in " + packageName); - return; - } - hostBins = FileInfo.path(mocLocation); - } else { - console.warn("No host_bins variable in " + packageName); - return; - } - } - var suffix = exeSuffix(qbs); - var qmakePaths = [FileInfo.joinPaths(hostBins, "qmake" + suffix)]; - var qtProviderDir = FileInfo.joinPaths(path, "Qt"); - SetupQt.doSetup(qmakePaths, outputBaseDir, qtProviderDir, qbs); - } - } - + // TODO: ponder how we can solve forward mapping with Packages so we can fill deps var moduleMapping = { "protobuf": "protobuflib", "grpc++": "grpcpp" } - - var pkgConfig = new PkgConfig(options); - - var brokenPackages = []; - var packages = pkgConfig.packages(); - for (var packageName in packages) { - var pkg = packages[packageName]; - if (pkg.isBroken) { - brokenPackages.push(pkg); - continue; - } - if (packageName.startsWith("Qt")) { - setupQt(pkg); - continue; + var reverseMapping = {} + for (var key in moduleMapping) + reverseMapping[moduleMapping[key]] = key + + if (moduleName.startsWith("Qt")) { + function setupQt(packageName, qtInfos) { + if (qtInfos === undefined) + return []; + var qtProviderDir = FileInfo.joinPaths(path, "Qt"); + return SetupQt.doSetup(packageName, qtInfos, outputBaseDir, qtProviderDir); } - var moduleName = moduleMapping[packageName] - ? moduleMapping[packageName] - : getModuleName(packageName); - var moduleInfo = getModuleInfo(pkg, staticMode); - var deps = getModuleDependencies(pkg, staticMode); - var moduleDir = FileInfo.joinPaths(outputDir, moduleName); - File.makePath(moduleDir); - var module = - new TextFile(FileInfo.joinPaths(moduleDir, "module.qbs"), TextFile.WriteOnly); - module.writeLine("Module {"); - module.writeLine(" version: " + ModUtils.toJSLiteral(moduleInfo.version)); - module.writeLine(" Depends { name: 'cpp' }"); - deps.forEach(function(dep) { - module.write(" Depends { name: '" + dep.name + "'"); - for (var k in dep) { - if (k === "name") - continue; - module.write("; " + k + ": " + ModUtils.toJSLiteral(dep[k])); - } - module.writeLine(" }"); - }) - function writeProperty(propertyName) { - var value = moduleInfo[propertyName]; - if (value.length !== 0) { // skip empty props for simplicity of the module file - module.writeLine( - " cpp." + propertyName + ":" + ModUtils.toJSLiteral(value)); - } + if (!sysroot) { + return setupQt(moduleName, theProbe.qtInfos); } - writeProperty("includePaths"); - writeProperty("systemIncludePaths"); - writeProperty("defines"); - writeProperty("commonCompilerFlags"); - writeProperty("dynamicLibraries"); - writeProperty("staticLibraries"); - writeProperty("libraryPaths"); - writeProperty("frameworks"); - writeProperty("frameworkPaths"); - writeProperty("driverLinkerFlags"); - module.writeLine("}"); - module.close(); + return []; } - if (brokenPackages.length !== 0) { - console.warn("Failed to load some pkg-config packages:"); - for (var i = 0; i < brokenPackages.length; ++i) { - console.warn(" " + brokenPackages[i].filePath - + ": " + brokenPackages[i].errorText); + var pkg; + pkg = theProbe.packages[reverseMapping[moduleName]]; + if (pkg === undefined) + pkg = theProbe.packagesByModuleName[moduleName]; + if (pkg === undefined) + return []; + + if (pkg.isBroken) { + console.warn("Failed to load " + moduleName + " as it's pkg-config package is broken"); + return []; + } + var moduleInfo = getModuleInfo(pkg, staticMode); + var deps = getModuleDependencies(pkg, staticMode); + + var moduleDir = FileInfo.joinPaths(outputDir, moduleName); + File.makePath(moduleDir); + var module = + new TextFile(FileInfo.joinPaths(moduleDir, "module.qbs"), TextFile.WriteOnly); + module.writeLine("Module {"); + module.writeLine(" version: " + ModUtils.toJSLiteral(moduleInfo.version)); + module.writeLine(" Depends { name: 'cpp' }"); + deps.forEach(function(dep) { + var depName = ProviderUtils.pkgConfigToModuleName( + moduleMapping[dep.name] ? moduleMapping[dep.name] : dep.name); + module.write(" Depends { name: '" + depName + "'"); + for (var k in dep) { + if (k === "name") + continue; + module.write("; " + k + ": " + ModUtils.toJSLiteral(dep[k])); + } + module.writeLine(" }"); + }) + function writeProperty(propertyName) { + var value = moduleInfo[propertyName]; + if (value.length !== 0) { // skip empty props for simplicity of the module file + module.writeLine( + " cpp." + propertyName + ":" + ModUtils.toJSLiteral(value)); } } + writeProperty("includePaths"); + writeProperty("systemIncludePaths"); + writeProperty("defines"); + writeProperty("commonCompilerFlags"); + writeProperty("dynamicLibraries"); + writeProperty("staticLibraries"); + writeProperty("libraryPaths"); + writeProperty("frameworks"); + writeProperty("frameworkPaths"); + writeProperty("driverLinkerFlags"); + module.writeLine("}"); + module.close(); return ""; } diff --git a/share/qbs/modules/Android/sdk/sdk.qbs b/share/qbs/modules/Android/sdk/sdk.qbs index d663a01e4..440448dcf 100644 --- a/share/qbs/modules/Android/sdk/sdk.qbs +++ b/share/qbs/modules/Android/sdk/sdk.qbs @@ -52,7 +52,7 @@ Module { Probes.PathProbe { id: bundletoolProbe - platformSearchPaths: [Android.sdk.sdkDir] + platformSearchPaths: [sdkDir] names: ["bundletool-all"] nameSuffixes: ["-0.11.0.jar", "-0.12.0.jar", "-0.13.0.jar", "-0.13.3.jar", "-0.13.4.jar", "-0.14.0.jar", "-0.15.0.jar", "-1.0.0.jar", "-1.1.0.jar", "-1.2.0.jar", "-1.3.0.jar", @@ -102,36 +102,36 @@ Module { Group { name: "java sources" - condition: Android.sdk.automaticSources - prefix: FileInfo.resolvePath(product.sourceDirectory, Android.sdk.sourcesDir + '/') + condition: product.Android.sdk.automaticSources + prefix: FileInfo.resolvePath(product.sourceDirectory, product.Android.sdk.sourcesDir + '/') files: "**/*.java" } Group { name: "android resources" - condition: Android.sdk.automaticSources + condition: product.Android.sdk.automaticSources fileTags: ["android.resources"] - prefix: FileInfo.resolvePath(product.sourceDirectory, Android.sdk.resourcesDir + '/') + prefix: FileInfo.resolvePath(product.sourceDirectory, product.Android.sdk.resourcesDir + '/') files: "**/*" } Group { name: "android assets" - condition: Android.sdk.automaticSources + condition: product.Android.sdk.automaticSources fileTags: ["android.assets"] - prefix: FileInfo.resolvePath(product.sourceDirectory, Android.sdk.assetsDir + '/') + prefix: FileInfo.resolvePath(product.sourceDirectory, product.Android.sdk.assetsDir + '/') files: "**/*" } Group { name: "manifest" - condition: Android.sdk.automaticSources + condition: product.Android.sdk.automaticSources fileTags: ["android.manifest"] - files: Android.sdk.manifestFile - && Android.sdk.manifestFile !== Android.sdk.defaultManifestFile - ? [Android.sdk.manifestFile] - : (File.exists(Android.sdk.defaultManifestFile) - ? [Android.sdk.defaultManifestFile] : []) + files: product.Android.sdk.manifestFile + && product.Android.sdk.manifestFile !== product.Android.sdk.defaultManifestFile + ? [product.Android.sdk.manifestFile] + : (File.exists(product.Android.sdk.defaultManifestFile) + ? [product.Android.sdk.defaultManifestFile] : []) } @@ -196,8 +196,8 @@ Module { java.languageVersion: platformJavaVersion java.runtimeVersion: platformJavaVersion java.bootClassPaths: androidJarFilePath - codesign.apksignerFilePath: Android.sdk.apksignerFilePath - codesign._packageName: Android.sdk.apkBaseName + (_generateAab ? ".aab" : ".apk") + codesign.apksignerFilePath: apksignerFilePath + codesign._packageName: apkBaseName + (_generateAab ? ".aab" : ".apk") codesign.useApksigner: !_generateAab } @@ -263,7 +263,7 @@ Module { property bool customManifestProcessing: false Group { - condition: !Android.sdk.customManifestProcessing + condition: !product.Android.sdk.customManifestProcessing fileTagsFilter: "android.manifest_processed" fileTags: "android.manifest_final" } diff --git a/share/qbs/modules/Android/sdk/utils.js b/share/qbs/modules/Android/sdk/utils.js index 2b111522e..232ee5a30 100644 --- a/share/qbs/modules/Android/sdk/utils.js +++ b/share/qbs/modules/Android/sdk/utils.js @@ -84,7 +84,7 @@ function prepareDex(project, product, inputs, outputs, input, output, explicitly return; dep.artifacts["java.jar"].forEach(function(artifact) { - if (!jarFiles.contains(artifact.filePath)) + if (!jarFiles.includes(artifact.filePath)) jarFiles.push(artifact.filePath); }); dep.dependencies.forEach(traverseJarDeps); @@ -93,7 +93,7 @@ function prepareDex(project, product, inputs, outputs, input, output, explicitly if (typeof product.artifacts["java.jar"] !== "undefined") { product.artifacts["java.jar"].forEach(function(artifact) { - if (!jarFiles.contains(artifact.filePath)) + if (!jarFiles.includes(artifact.filePath)) jarFiles.push(artifact.filePath); }); } @@ -166,7 +166,7 @@ function commonAaptPackageArgs(project, product, inputs, outputs, input, output, throw "File '" + resources[i].filePath + "' is tagged as an Android resource, " + "but is not located under a directory called 'res'."; } - if (!resourceDirs.contains(resDir)) + if (!resourceDirs.includes(resDir)) resourceDirs.push(resDir); } } @@ -181,7 +181,7 @@ function commonAaptPackageArgs(project, product, inputs, outputs, input, output, throw "File '" + assets[i].filePath + "' is tagged as an Android asset, " + "but is not located under a directory called 'assets'."; } - if (!assetDirs.contains(assetDir)) + if (!assetDirs.includes(assetDir)) assetDirs.push(assetDir); } } @@ -265,7 +265,7 @@ function prepareAapt2Link(project, product, inputs, outputs, input, output, expl throw "File '" + assets[i].filePath + "' is tagged as an Android asset, " + "but is not located under a directory called 'assets'."; } - if (!assetDirs.contains(assetDir)) + if (!assetDirs.includes(assetDir)) assetDirs.push(assetDir); } } @@ -429,7 +429,7 @@ function stlDeploymentData(product, inputs, type) return data; for (var i = 0; i < theInputs.length; ++i) { var currentInput = theInputs[i]; - if (uniqueFilePaths.contains(currentInput.filePath)) + if (uniqueFilePaths.includes(currentInput.filePath)) continue; uniqueFilePaths.push(currentInput.filePath); data.uniqueInputs.push(currentInput); diff --git a/share/qbs/modules/Exporter/pkgconfig/pkgconfig.js b/share/qbs/modules/Exporter/pkgconfig/pkgconfig.js index 52b4dffe3..50bba536c 100644 --- a/share/qbs/modules/Exporter/pkgconfig/pkgconfig.js +++ b/share/qbs/modules/Exporter/pkgconfig/pkgconfig.js @@ -33,7 +33,7 @@ var ModUtils = require("qbs.ModUtils"); function quote(value) { - if (value.contains(" ") || value.contains("'") || value.contains('"')) { + if (value.includes(" ") || value.includes("'") || value.includes('"')) { return '"' + value.replace(/(["'\\])/g, "\\$1") + '"'; } return value; @@ -105,12 +105,12 @@ function collectAutodetectedData(topLevelProduct) var libArtifacts; var isProduct = !productOrModule.present; var considerDynamicLibs = !isProduct || (productOrModule.type - && productOrModule.type.contains("dynamiclibrary")); + && productOrModule.type.includes("dynamiclibrary")); if (considerDynamicLibs) { libArtifacts = productOrModule.artifacts.dynamiclibrary; } else { var considerStaticLibs = !isProduct || (productOrModule.type - && productOrModule.type.contains("staticlibrary")); + && productOrModule.type.includes("staticlibrary")); if (considerStaticLibs) libArtifacts = productOrModule.artifacts.staticlibrary; } @@ -186,7 +186,7 @@ function collectAutodetectedData(topLevelProduct) exportedDepNames.push(exportedDeps[i].name); for (i = 0; i < (productOrModule.dependencies || []).length; ++i) { var dep = productOrModule.dependencies[i]; - if (exportedDepNames.contains(dep.name)) + if (exportedDepNames.includes(dep.name)) continue; privateDeps.push(dep); } @@ -197,22 +197,22 @@ function collectAutodetectedData(topLevelProduct) var depHasPkgConfig = dep.Exporter && dep.Exporter.pkgconfig; if (depHasPkgConfig) { var entry = FileInfo.completeBaseName(dep.Exporter.pkgconfig.fileName); - if (excludedDeps.contains(entry)) + if (excludedDeps.includes(entry)) return; - if (isPrivateDep && !data.requiresPrivate.contains(entry) - && !explicitRequiresPrivate.contains(entry)) { + if (isPrivateDep && !data.requiresPrivate.includes(entry) + && !explicitRequiresPrivate.includes(entry)) { data.requiresPrivate.push(entry); } - if (!isPrivateDep && !data.requires.contains(entry) - && !explicitRequires.contains(entry)) { + if (!isPrivateDep && !data.requires.includes(entry) + && !explicitRequires.includes(entry)) { data.requires.push(entry); } } else { - if (excludedDeps.contains(dep.name)) + if (excludedDeps.includes(dep.name)) return; - if (isPrivateDep && explicitRequiresPrivate.contains(dep.name)) + if (isPrivateDep && explicitRequiresPrivate.includes(dep.name)) return; - if (!isPrivateDep && explicitRequires.contains(dep.name)) + if (!isPrivateDep && explicitRequires.includes(dep.name)) return; collectAutodetectedDataRecursive(dep, isPrivateDep); } diff --git a/share/qbs/modules/Exporter/pkgconfig/pkgconfig.qbs b/share/qbs/modules/Exporter/pkgconfig/pkgconfig.qbs index c36afd953..feb0e017c 100644 --- a/share/qbs/modules/Exporter/pkgconfig/pkgconfig.qbs +++ b/share/qbs/modules/Exporter/pkgconfig/pkgconfig.qbs @@ -34,9 +34,9 @@ Module { auxiliaryInputs: { if (!autoDetect) return undefined; - if (product.type.contains("staticlibrary")) + if (product.type.includes("staticlibrary")) return ["staticlibrary"]; - if (product.type.contains("dynamiclibrary")) + if (product.type.includes("dynamiclibrary")) return ["dynamiclibrary"]; } diff --git a/share/qbs/modules/Exporter/qbs/qbsexporter.js b/share/qbs/modules/Exporter/qbs/qbsexporter.js index be46372c3..f09dd2d0c 100644 --- a/share/qbs/modules/Exporter/qbs/qbsexporter.js +++ b/share/qbs/modules/Exporter/qbs/qbsexporter.js @@ -74,7 +74,7 @@ function writeTargetArtifactGroups(product, output, moduleFile) var tag = product.Exporter.qbs._artifactTypes[i]; var artifactsForTag = product.artifacts[tag] || []; for (var j = 0; j < artifactsForTag.length; ++j) { - if (!relevantArtifacts.contains(artifactsForTag[j])) + if (!relevantArtifacts.includes(artifactsForTag[j])) relevantArtifacts.push(artifactsForTag[j]); } } @@ -82,7 +82,7 @@ function writeTargetArtifactGroups(product, output, moduleFile) var artifactCount = relevantArtifacts ? relevantArtifacts.length : 0; for (i = 0; i < artifactCount; ++i) { var artifact = relevantArtifacts[i]; - if (!artifact.fileTags.contains("installable")) + if (!artifact.fileTags.includes("installable")) continue; // Put all artifacts with the same set of file tags into the same group, so we don't @@ -172,7 +172,7 @@ function writeProperty(project, product, moduleInstallDir, prop, indentation, co var moduleName; if (isModuleProperty) { moduleName = prop.name.slice(0, separatorIndex); - if ((product.Exporter.qbs.excludedDependencies || []).contains(moduleName)) + if ((product.Exporter.qbs.excludedDependencies || []).includes(moduleName)) return; } line += prop.name + ": "; @@ -244,7 +244,7 @@ function isExcludedDependency(product, childItem) for (var i = 0; i < childItem.properties.length; ++i) { var prop = childItem.properties[i]; var unquotedRhs = prop.sourceCode.slice(1, -1); - if (prop.name === "name" && product.Exporter.qbs.excludedDependencies.contains(unquotedRhs)) + if (prop.name === "name" && product.Exporter.qbs.excludedDependencies.includes(unquotedRhs)) return true; } return false; @@ -264,7 +264,7 @@ function writeImportStatements(product, moduleFile) var imports = product.exports.imports; // We potentially use FileInfo ourselves when transforming paths in stringifyValue(). - if (!imports.contains("import qbs.FileInfo")) + if (!imports.includes("import qbs.FileInfo")) imports.push("import qbs.FileInfo"); for (var i = 0; i < imports.length; ++i) diff --git a/share/qbs/modules/Sanitizers/address/asan.qbs b/share/qbs/modules/Sanitizers/address/asan.qbs index 39605ef4e..9d8f5b97e 100644 --- a/share/qbs/modules/Sanitizers/address/asan.qbs +++ b/share/qbs/modules/Sanitizers/address/asan.qbs @@ -34,9 +34,9 @@ Module { Depends { name: "cpp" } property bool enabled: true - readonly property bool _supported: qbs.toolchain.contains("gcc") - || qbs.toolchain.contains("clang-cl") - || (qbs.toolchain.contains("msvc") + readonly property bool _supported: qbs.toolchain.includes("gcc") + || qbs.toolchain.includes("clang-cl") + || (qbs.toolchain.includes("msvc") && Utilities.versionCompare(cpp.compilerVersion, "19.28.29500.0") >= 0) readonly property bool _enabled: enabled && _supported @@ -53,7 +53,7 @@ Module { var flags = []; if (!_enabled) return flags; - if (qbs.toolchain.contains("msvc") && !qbs.toolchain.contains("clang-cl")) { + if (qbs.toolchain.includes("msvc") && !qbs.toolchain.includes("clang-cl")) { flags.push("/fsanitize=address"); if (detectUseAfterReturn !== "never") flags.push("/fsanitize-address-use-after-return"); @@ -63,8 +63,9 @@ Module { if (detectUseAfterScope) flags.push("-fsanitize-address-use-after-scope"); if (detectUseAfterReturn) { - if (qbs.toolchain.contains("llvm")) { - if (Utilities.versionCompare(cpp.compilerVersion, "13") >= 0) + if (qbs.toolchain.includes("llvm")) { + var minVersion = qbs.toolchain.contains("xcode") ? "14" : "13"; + if (Utilities.versionCompare(cpp.compilerVersion, minVersion) >= 0) flags.push("-fsanitize-address-use-after-return=" + detectUseAfterReturn); } else if (detectUseAfterReturn === "never") { flags.push("--param", "asan-use-after-return=0"); diff --git a/share/qbs/modules/archiver/archiver.qbs b/share/qbs/modules/archiver/archiver.qbs index 6da3f43ff..069e76717 100644 --- a/share/qbs/modules/archiver/archiver.qbs +++ b/share/qbs/modules/archiver/archiver.qbs @@ -54,7 +54,7 @@ Module { names: ["7z"] platformSearchPaths: { var paths = base; - if (Host.os().contains("windows")) { + if (Host.os().includes("windows")) { var env32 = Environment.getEnv("PROGRAMFILES(X86)"); var env64 = Environment.getEnv("PROGRAMFILES"); if (env64 === env32 && env64.endsWith(" (x86)")) @@ -212,7 +212,7 @@ Module { args.push("-0"); } else { compression = compression === "bz2" ? "bzip2" : compression; - if (["store", "deflate", "bzip2"].contains(compression)) + if (["store", "deflate", "bzip2"].includes(compression)) args.push("-Z", compression); if (compressionLevel) @@ -221,7 +221,7 @@ Module { args.push("-r", output.filePath, ".", "-i@" + input.filePath); args = args.concat(product.moduleProperty("archiver", "flags")); - } else if (["tar", "zip", "jar"].contains(binaryName)) { + } else if (["tar", "zip", "jar"].includes(binaryName)) { throw binaryName + ": unrecognized archive type: '" + type + "'"; } else if (binaryName) { throw "unrecognized archive tool: '" + binaryName + "'"; diff --git a/share/qbs/modules/bundle/BundleModule.qbs b/share/qbs/modules/bundle/BundleModule.qbs index 6526fa314..f640fd55f 100644 --- a/share/qbs/modules/bundle/BundleModule.qbs +++ b/share/qbs/modules/bundle/BundleModule.qbs @@ -47,13 +47,13 @@ Module { Probe { id: bundleSettingsProbe - condition: qbs.targetOS.contains("darwin") + condition: qbs.targetOS.includes("darwin") property string xcodeDeveloperPath: xcode.developerPath property var xcodeArchSettings: xcode._architectureSettings property string productTypeIdentifier: _productTypeIdentifier - property bool useXcodeBuildSpecs: _useXcodeBuildSpecs - property bool isMacOs: qbs.targetOS.contains("macos") + property bool useXcodeBuildSpecs: !useBuiltinXcodeBuildSpecs + property bool isMacOs: qbs.targetOS.includes("macos") property bool xcodePresent: xcode.present property string xcodeVersion: xcode.version @@ -74,9 +74,9 @@ Module { "LOCAL_LIBRARY_DIR": Environment.getEnv("HOME") + "/Library", // actually, this is cpp.targetAbi, but XCode does not set it for non-simulator builds // while Qbs set it to "macho". - "LLVM_TARGET_TRIPLE_SUFFIX": qbs.targetOS.contains("simulator") ? "-simulator" : "", + "LLVM_TARGET_TRIPLE_SUFFIX": qbs.targetOS.includes("simulator") ? "-simulator" : "", "SWIFT_PLATFORM_TARGET_PREFIX": isMacOs ? "macos" - : qbs.targetOS.contains("ios") ? "ios" : "", + : qbs.targetOS.includes("ios") ? "ios" : "", "TARGET_BUILD_DIR": product.buildDirectory, "WRAPPER_NAME": bundleName, "WRAPPER_EXTENSION": extension @@ -118,7 +118,7 @@ Module { additionalProductTypes: !(product.multiplexed || product.aggregate) || !product.multiplexConfigurationId ? ["bundle.content"] : [] - property bool isBundle: !product.consoleApplication && qbs.targetOS.contains("darwin") + property bool isBundle: !product.consoleApplication && qbs.targetOS.includes("darwin") readonly property bool isShallow: bundleSettingsProbe.xcodeSettings["SHALLOW_BUNDLE"] === "YES" @@ -152,7 +152,7 @@ Module { property var infoPlist property bool processInfoPlist: true property bool embedInfoPlist: product.consoleApplication && !isBundle - property string infoPlistFormat: qbs.targetOS.contains("macos") ? "same-as-input" : "binary1" + property string infoPlistFormat: qbs.targetOS.includes("macos") ? "same-as-input" : "binary1" property string localizedResourcesFolderSuffix: ".lproj" @@ -187,11 +187,18 @@ Module { readonly property string unlocalizedResourcesFolderPath: bundleSettingsProbe.xcodeSettings["UNLOCALIZED_RESOURCES_FOLDER_PATH"] readonly property string versionsFolderPath: bundleSettingsProbe.xcodeSettings["VERSIONS_FOLDER_PATH"] + property bool useBuiltinXcodeBuildSpecs: !_useXcodeBuildSpecs // true to use ONLY the qbs build specs + // private properties property string _productTypeIdentifier: Bundle.productTypeIdentifier(product.type) property stringList _productTypeIdentifierChain: bundleSettingsProbe.productTypeIdentifierChain - property bool _useXcodeBuildSpecs: true // false to use ONLY the qbs build specs + readonly property path _developerPath: xcode.developerPath + readonly property path _platformInfoPlist: xcode.platformInfoPlist + readonly property path _sdkSettingsPlist: xcode.sdkSettingsPlist + readonly property path _toolchainInfoPlist: xcode.toolchainInfoPlist + + property bool _useXcodeBuildSpecs: true // TODO: remove in 1.25 property var extraEnv: ({ "PRODUCT_BUNDLE_IDENTIFIER": identifier @@ -225,7 +232,7 @@ Module { } validate: { - if (!qbs.targetOS.contains("darwin")) + if (!qbs.targetOS.includes("darwin")) return; if (!bundleSettingsProbe.found) { var error = "Bundle product type " + _productTypeIdentifier + " is not supported."; @@ -271,7 +278,7 @@ Module { } Rule { - condition: qbs.targetOS.contains("darwin") + condition: qbs.targetOS.includes("darwin") multiplex: true requiresInputs: false // TODO: The resources property should probably be a tag instead. inputs: ["infoplist", "partial_infoplist"] @@ -279,8 +286,8 @@ Module { outputFileTags: ["bundle.input", "aggregate_infoplist"] outputArtifacts: { var artifacts = []; - var embed = ModUtils.moduleProperty(product, "embedInfoPlist"); - if (ModUtils.moduleProperty(product, "isBundle") || embed) { + var embed = product.bundle.embedInfoPlist; + if (product.bundle.isBundle || embed) { artifacts.push({ filePath: FileInfo.joinPaths( product.destinationDirectory, product.name + "-Info.plist"), @@ -288,7 +295,7 @@ Module { bundle: { _bundleFilePath: FileInfo.joinPaths( product.destinationDirectory, - ModUtils.moduleProperty(product, "infoPlistPath")), + product.bundle.infoPlistPath), } }); } @@ -299,20 +306,21 @@ Module { var cmd = new JavaScriptCommand(); cmd.description = "generating Info.plist for " + product.name; cmd.highlight = "codegen"; - cmd.infoPlist = ModUtils.moduleProperty(product, "infoPlist") || {}; - cmd.processInfoPlist = ModUtils.moduleProperty(product, "processInfoPlist"); - cmd.infoPlistFormat = ModUtils.moduleProperty(product, "infoPlistFormat"); - cmd.extraEnv = ModUtils.moduleProperty(product, "extraEnv"); - cmd.qmakeEnv = ModUtils.moduleProperty(product, "qmakeEnv"); + cmd.infoPlist = product.bundle.infoPlist || {}; + cmd.processInfoPlist = product.bundle.processInfoPlist; + cmd.infoPlistFormat = product.bundle.infoPlistFormat; + cmd.extraEnv = product.bundle.extraEnv; + cmd.qmakeEnv = product.bundle.qmakeEnv; + // TODO: bundle module should know nothing about cpp module cmd.buildEnv = product.moduleProperty("cpp", "buildEnv"); - cmd.developerPath = product.moduleProperty("xcode", "developerPath"); - cmd.platformInfoPlist = product.moduleProperty("xcode", "platformInfoPlist"); - cmd.sdkSettingsPlist = product.moduleProperty("xcode", "sdkSettingsPlist"); - cmd.toolchainInfoPlist = product.moduleProperty("xcode", "toolchainInfoPlist"); + cmd.developerPath = product.bundle._developerPath; + cmd.platformInfoPlist = product.bundle._platformInfoPlist; + cmd.sdkSettingsPlist = product.bundle._sdkSettingsPlist; + cmd.toolchainInfoPlist = product.bundle._toolchainInfoPlist; - cmd.osBuildVersion = product.moduleProperty("qbs", "hostOSBuildVersion"); + cmd.osBuildVersion = product.qbs.hostOSBuildVersion; cmd.sourceCode = function() { var plist, process, key, i; @@ -341,7 +349,7 @@ Module { if (processInfoPlist) { // Add default values to the aggregate plist if the corresponding keys // for those values are not already present - var defaultValues = ModUtils.moduleProperty(product, "defaultInfoPlist"); + var defaultValues = product.bundle.defaultInfoPlist; for (key in defaultValues) { if (defaultValues.hasOwnProperty(key) && !(key in aggregatePlist)) aggregatePlist[key] = defaultValues[key]; @@ -453,7 +461,7 @@ Module { infoPlistFormat = "xml1"; var validFormats = [ "xml1", "binary1", "json" ]; - if (!validFormats.contains(infoPlistFormat)) + if (!validFormats.includes(infoPlistFormat)) throw("Invalid Info.plist format " + infoPlistFormat + ". " + "Must be in [xml1, binary1, json]."); @@ -471,18 +479,18 @@ Module { } Rule { - condition: qbs.targetOS.contains("darwin") + condition: qbs.targetOS.includes("darwin") multiplex: true inputs: ["aggregate_infoplist"] outputFileTags: ["bundle.input", "pkginfo"] outputArtifacts: { var artifacts = []; - if (ModUtils.moduleProperty(product, "isBundle") && ModUtils.moduleProperty(product, "generatePackageInfo")) { + if (product.bundle.isBundle && product.bundle.generatePackageInfo) { artifacts.push({ filePath: FileInfo.joinPaths(product.destinationDirectory, "PkgInfo"), fileTags: ["bundle.input", "pkginfo"], - bundle: { _bundleFilePath: FileInfo.joinPaths(product.destinationDirectory, ModUtils.moduleProperty(product, "pkgInfoPath")) } + bundle: { _bundleFilePath: FileInfo.joinPaths(product.destinationDirectory, product.bundle.pkgInfoPath) } }); } return artifacts; @@ -512,7 +520,7 @@ Module { } Rule { - condition: qbs.targetOS.contains("darwin") + condition: qbs.targetOS.includes("darwin") multiplex: true inputs: ["bundle.input", "aggregate_infoplist", "pkginfo", "hpp", @@ -533,12 +541,12 @@ Module { "bundle.code-signature"] outputArtifacts: { var i, artifacts = []; - if (ModUtils.moduleProperty(product, "isBundle")) { + if (product.bundle.isBundle) { for (i in inputs["bundle.input"]) { - var fp = inputs["bundle.input"][i].moduleProperty("bundle", "_bundleFilePath"); + var fp = inputs["bundle.input"][i].bundle._bundleFilePath; if (!fp) throw("Artifact " + inputs["bundle.input"][i].filePath + " has no associated bundle file path"); - var extraTags = inputs["bundle.input"][i].fileTags.contains("application") + var extraTags = inputs["bundle.input"][i].fileTags.includes("application") ? ["bundle.application-executable"] : []; artifacts.push({ filePath: fp, @@ -550,20 +558,19 @@ Module { for (i in provprofiles) { artifacts.push({ filePath: FileInfo.joinPaths(product.destinationDirectory, - ModUtils.moduleProperty(product, - "contentsFolderPath"), + product.bundle.contentsFolderPath, provprofiles[i].fileName), fileTags: ["bundle.provisioningprofile", "bundle.content"] }); } - var packageType = ModUtils.moduleProperty(product, "packageType"); - var isShallow = ModUtils.moduleProperty(product, "isShallow"); + var packageType = product.bundle.packageType; + var isShallow = product.bundle.isShallow; if (packageType === "FMWK" && !isShallow) { - var publicHeaders = ModUtils.moduleProperty(product, "publicHeaders"); + var publicHeaders = product.bundle.publicHeaders; if (publicHeaders && publicHeaders.length) { artifacts.push({ - filePath: FileInfo.joinPaths(product.destinationDirectory, ModUtils.moduleProperty(product, "bundleName"), "Headers"), + filePath: FileInfo.joinPaths(product.destinationDirectory, product.bundle.bundleName, "Headers"), fileTags: ["bundle.symlink.headers", "bundle.content"] }); } @@ -571,23 +578,23 @@ Module { var privateHeaders = ModUtils.moduleProperty(product, "privateHeaders"); if (privateHeaders && privateHeaders.length) { artifacts.push({ - filePath: FileInfo.joinPaths(product.destinationDirectory, ModUtils.moduleProperty(product, "bundleName"), "PrivateHeaders"), + filePath: FileInfo.joinPaths(product.destinationDirectory, product.bundle.bundleName, "PrivateHeaders"), fileTags: ["bundle.symlink.private-headers", "bundle.content"] }); } artifacts.push({ - filePath: FileInfo.joinPaths(product.destinationDirectory, ModUtils.moduleProperty(product, "bundleName"), "Resources"), + filePath: FileInfo.joinPaths(product.destinationDirectory, product.bundle.bundleName, "Resources"), fileTags: ["bundle.symlink.resources", "bundle.content"] }); artifacts.push({ - filePath: FileInfo.joinPaths(product.destinationDirectory, ModUtils.moduleProperty(product, "bundleName"), product.targetName), + filePath: FileInfo.joinPaths(product.destinationDirectory, product.bundle.bundleName, product.targetName), fileTags: ["bundle.symlink.executable", "bundle.content"] }); artifacts.push({ - filePath: FileInfo.joinPaths(product.destinationDirectory, ModUtils.moduleProperty(product, "versionsFolderPath"), "Current"), + filePath: FileInfo.joinPaths(product.destinationDirectory, product.bundle.versionsFolderPath, "Current"), fileTags: ["bundle.symlink.version", "bundle.content"] }); } @@ -604,7 +611,7 @@ Module { } } - sources = ModUtils.moduleProperty(product, "resources"); + sources = product.bundle.resources; for (i in sources) { destination = BundleTools.destinationDirectoryForResource(product, {baseDir: FileInfo.path(sources[i]), fileName: FileInfo.fileName(sources[i])}); artifacts.push({ @@ -615,11 +622,11 @@ Module { var wrapperPath = FileInfo.joinPaths( product.destinationDirectory, - ModUtils.moduleProperty(product, "bundleName")); + product.bundle.bundleName); for (var i = 0; i < artifacts.length; ++i) artifacts[i].bundle = { wrapperPath: wrapperPath }; - if (Host.os().contains("darwin") && product.codesign + if (Host.os().includes("darwin") && product.codesign && product.codesign.enableCodeSigning) { artifacts.push({ filePath: FileInfo.joinPaths(product.bundle.contentsFolderPath, "_CodeSignature/CodeResources"), @@ -632,7 +639,7 @@ Module { prepare: { var i, cmd, commands = []; - var packageType = ModUtils.moduleProperty(product, "packageType"); + var packageType = product.bundle.packageType; var bundleType = "bundle"; if (packageType === "APPL") @@ -650,7 +657,7 @@ Module { var symlinks = outputs["bundle.symlink.version"]; for (i in symlinks) { - cmd = new Command("ln", ["-sfn", ModUtils.moduleProperty(product, "frameworkVersion"), + cmd = new Command("ln", ["-sfn", product.bundle.frameworkVersion, symlinks[i].filePath]); cmd.silent = true; commands.push(cmd); @@ -697,8 +704,8 @@ Module { } var bundleInputs = sortedArtifactList(inputs["bundle.input"], function (a, b) { - return a.moduleProperty("bundle", "_bundleFilePath").localeCompare( - b.moduleProperty("bundle", "_bundleFilePath")); + return a.bundle._bundleFilePath.localeCompare( + b.bundle._bundleFilePath); }); var bundleContents = sortedArtifactList(outputs["bundle.content.copied"]); for (i in bundleContents) { @@ -731,8 +738,8 @@ Module { cmd = new JavaScriptCommand(); cmd.description = "copying public headers"; cmd.highlight = "filegen"; - cmd.sources = ModUtils.moduleProperty(product, "publicHeaders"); - cmd.destination = FileInfo.joinPaths(product.destinationDirectory, ModUtils.moduleProperty(product, "publicHeadersFolderPath")); + cmd.sources = product.bundle.publicHeaders; + cmd.destination = FileInfo.joinPaths(product.destinationDirectory, product.bundle.publicHeadersFolderPath); cmd.sourceCode = function() { var i; for (var i in sources) { @@ -745,8 +752,8 @@ Module { cmd = new JavaScriptCommand(); cmd.description = "copying private headers"; cmd.highlight = "filegen"; - cmd.sources = ModUtils.moduleProperty(product, "privateHeaders"); - cmd.destination = FileInfo.joinPaths(product.destinationDirectory, ModUtils.moduleProperty(product, "privateHeadersFolderPath")); + cmd.sources = product.bundle.privateHeaders; + cmd.destination = FileInfo.joinPaths(product.destinationDirectory, product.bundle.privateHeadersFolderPath); cmd.sourceCode = function() { var i; for (var i in sources) { @@ -759,7 +766,7 @@ Module { cmd = new JavaScriptCommand(); cmd.description = "copying resources"; cmd.highlight = "filegen"; - cmd.sources = ModUtils.moduleProperty(product, "resources"); + cmd.sources = product.bundle.resources; cmd.sourceCode = function() { var i; for (var i in sources) { @@ -770,15 +777,17 @@ Module { if (cmd.sources && cmd.sources.length) commands.push(cmd); - if (product.moduleProperty("qbs", "hostOS").contains("darwin")) { + if (product.qbs.hostOS.includes("darwin")) { Array.prototype.push.apply(commands, Codesign.prepareSign( project, product, inputs, outputs, input, output)); if (bundleType === "application" - && product.moduleProperty("qbs", "targetOS").contains("macos")) { - cmd = new Command(ModUtils.moduleProperty(product, "lsregisterPath"), - ["-f", product.bundle.bundleName]); - cmd.description = "registering " + ModUtils.moduleProperty(product, "bundleName"); + && product.qbs.targetOS.includes("macos")) { + var bundlePath = FileInfo.joinPaths( + product.destinationDirectory, product.bundle.bundleName); + cmd = new Command(product.bundle.lsregisterPath, + ["-f", bundlePath]); + cmd.description = "registering " + product.bundle.bundleName; commands.push(cmd); } } diff --git a/share/qbs/modules/bundle/bundle.js b/share/qbs/modules/bundle/bundle.js index 7fb89974a..da9e2486a 100644 --- a/share/qbs/modules/bundle/bundle.js +++ b/share/qbs/modules/bundle/bundle.js @@ -83,7 +83,7 @@ var _productTypeIdentifiers = { function productTypeIdentifier(productType) { for (var k in _productTypeIdentifiers) { - if (productType.contains(k)) + if (productType.includes(k)) return _productTypeIdentifiers[k]; } return "com.apple.package-type.wrapper"; @@ -152,13 +152,15 @@ function _assign(target, source) { function macOSSpecsPaths(version, developerPath) { var result = []; - if (Utilities.versionCompare(version, "12.5") >= 0) { + if (Utilities.versionCompare(version, "14.3") >= 0) { + result.push(FileInfo.joinPaths( + developerPath, "Library", "Xcode", "Plug-ins", "XCBSpecifications.ideplugin", + "Contents", "Resources")); + } else if (Utilities.versionCompare(version, "12.5") >= 0) { result.push(FileInfo.joinPaths( developerPath, "..", "PlugIns", "XCBSpecifications.ideplugin", "Contents", "Resources")); - } - - if (Utilities.versionCompare(version, "12") >= 0) { + } else if (Utilities.versionCompare(version, "12") >= 0) { result.push(FileInfo.joinPaths( developerPath, "Platforms", "MacOSX.platform", "Developer", "Library", "Xcode", "PrivatePlugIns", "IDEOSXSupportCore.ideplugin", "Contents", "Resources")); @@ -311,7 +313,8 @@ var XcodeBuildSpecsReader = (function () { }; XcodeBuildSpecsReader.prototype.expandedSetting = function (typeIdentifier, baseSettings, settingName) { - var obj = baseSettings || {}; + var obj = {}; + _assign(obj, baseSettings); // todo: copy recursively obj = _assign(obj, this.settings(typeIdentifier, true)); if (obj) { for (var x in this._additionalSettings) { diff --git a/share/qbs/modules/capnproto/capnprotobase.qbs b/share/qbs/modules/capnproto/capnprotobase.qbs index e84d39433..56d542770 100644 --- a/share/qbs/modules/capnproto/capnprotobase.qbs +++ b/share/qbs/modules/capnproto/capnprotobase.qbs @@ -40,7 +40,7 @@ Module { property pathList importPaths: [] - readonly property string outputDir: product.buildDirectory + "/capnp" + property string outputDir: product.buildDirectory + "/capnp" Probes.BinaryProbe { id: compilerProbe diff --git a/share/qbs/modules/capnproto/cpp/capnprotocpp.qbs b/share/qbs/modules/capnproto/cpp/capnprotocpp.qbs index f33bc9a48..bccfca192 100644 --- a/share/qbs/modules/capnproto/cpp/capnprotocpp.qbs +++ b/share/qbs/modules/capnproto/cpp/capnprotocpp.qbs @@ -39,6 +39,7 @@ CapnProtoBase { Depends { name: "capnp-rpc"; condition: useRpc } pluginName: "capnpc-c++" + version: capnp.version cpp.systemIncludePaths: outputDir cpp.cxxLanguageVersion: "c++14" diff --git a/share/qbs/modules/cli/CLIModule.qbs b/share/qbs/modules/cli/CLIModule.qbs index df58942b5..3b12284ed 100644 --- a/share/qbs/modules/cli/CLIModule.qbs +++ b/share/qbs/modules/cli/CLIModule.qbs @@ -5,9 +5,9 @@ import qbs.ModUtils import "cli.js" as CLI Module { - Depends { name: "bundle"; condition: qbs.targetOS.contains("darwin") } + Depends { name: "bundle"; condition: qbs.targetOS.includes("darwin") } Properties { - condition: qbs.targetOS.contains("darwin") + condition: qbs.targetOS.includes("darwin") bundle.isBundle: false } @@ -89,7 +89,7 @@ Module { setupBuildEnvironment: { var v = new ModUtils.EnvironmentVariable("PATH", FileInfo.pathListSeparator(), - Host.os().contains("windows")); + Host.os().includes("windows")); v.prepend(product.cli.toolchainInstallPath); v.set(); } diff --git a/share/qbs/modules/cli/cli.js b/share/qbs/modules/cli/cli.js index 38833ac51..faa812201 100644 --- a/share/qbs/modules/cli/cli.js +++ b/share/qbs/modules/cli/cli.js @@ -59,16 +59,16 @@ function prepareCompiler(product, inputs, output) { "cli.fsharp": fsharpCompilerPath }; - var pathFunction = product.moduleProperty("qbs", "hostOS").contains("windows") + var pathFunction = product.moduleProperty("qbs", "hostOS").includes("windows") ? FileInfo.toWindowsSeparators : function (path) { return path; }; var outputDescription = "assembly"; - if (output.fileTags.contains("application")) { + if (output.fileTags.includes("application")) { args.push("/target:" + (product.consoleApplication === false ? "winexe" : "exe")); - } else if (output.fileTags.contains("dynamiclibrary")) { + } else if (output.fileTags.includes("dynamiclibrary")) { args.push("/target:library"); - } else if (output.fileTags.contains("cli.netmodule")) { + } else if (output.fileTags.includes("cli.netmodule")) { args.push("/target:module"); outputDescription = "netmodule"; } @@ -77,7 +77,7 @@ function prepareCompiler(product, inputs, output) { var keys = Object.keys(inputs); var language; for (i in keys) { - if (Object.keys(compilers).contains(keys[i])) { + if (Object.keys(compilers).includes(keys[i])) { if (language) throw("You cannot compile source files in more than one CLI language into a single " + outputDescription + "."); language = keys[i]; diff --git a/share/qbs/modules/cli/mono.qbs b/share/qbs/modules/cli/mono.qbs index 2ed65fd67..f16387dd7 100644 --- a/share/qbs/modules/cli/mono.qbs +++ b/share/qbs/modules/cli/mono.qbs @@ -3,7 +3,7 @@ import qbs.Host import qbs.Probes CLIModule { - condition: qbs.toolchain && qbs.toolchain.contains("mono") + condition: qbs.toolchain && qbs.toolchain.includes("mono") debugInfoSuffix: ".mdb" csharpCompilerName: "mcs" @@ -15,9 +15,9 @@ CLIModule { names: ["mono"] platformSearchPaths: { var paths = []; - if (Host.os().contains("macos")) + if (Host.os().includes("macos")) paths.push("/Library/Frameworks/Mono.framework/Commands"); - if (Host.os().contains("unix")) + if (Host.os().includes("unix")) paths.push("/usr/bin"); return paths; } diff --git a/share/qbs/modules/cli/windows-dotnet.qbs b/share/qbs/modules/cli/windows-dotnet.qbs index 6fde50b85..a4d27c207 100644 --- a/share/qbs/modules/cli/windows-dotnet.qbs +++ b/share/qbs/modules/cli/windows-dotnet.qbs @@ -1,7 +1,7 @@ import qbs.Utilities CLIModule { - condition: qbs.toolchain && qbs.toolchain.contains("dotnet") + condition: qbs.toolchain && qbs.toolchain.includes("dotnet") debugInfoSuffix: ".pdb" csharpCompilerName: "csc" diff --git a/share/qbs/modules/codesign/android.qbs b/share/qbs/modules/codesign/android.qbs index e149e033c..b1811dcfd 100644 --- a/share/qbs/modules/codesign/android.qbs +++ b/share/qbs/modules/codesign/android.qbs @@ -37,7 +37,7 @@ import qbs.Probes import "codesign.js" as CodeSign CodeSignModule { - condition: qbs.targetOS.contains("android") + condition: qbs.targetOS.includes("android") priority: 1 enableCodeSigning: true @@ -55,7 +55,7 @@ CodeSignModule { property string keytoolName: "keytool" property string debugKeystorePath: FileInfo.joinPaths( - Environment.getEnv(Host.os().contains("windows") + Environment.getEnv(Host.os().includes("windows") ? "USERPROFILE" : "HOME"), ".android", "debug.keystore") readonly property string debugKeystorePassword: "android" diff --git a/share/qbs/modules/codesign/apple.qbs b/share/qbs/modules/codesign/apple.qbs index 05232d7c1..0d1335d92 100644 --- a/share/qbs/modules/codesign/apple.qbs +++ b/share/qbs/modules/codesign/apple.qbs @@ -43,14 +43,14 @@ import "codesign.js" as CodeSign import "../xcode/xcode.js" as XcodeUtils CodeSignModule { - Depends { name: "xcode"; required: qbs.toolchain && qbs.toolchain.contains("xcode") } + Depends { name: "xcode"; required: qbs.toolchain && qbs.toolchain.includes("xcode") } Probes.BinaryProbe { id: codesignProbe names: [codesignName] } - condition: Host.os().contains("macos") && qbs.targetOS.contains("darwin") + condition: Host.os().includes("macos") && qbs.targetOS.includes("darwin") priority: 0 enableCodeSigning: _codeSigningRequired @@ -78,15 +78,15 @@ CodeSignModule { var isDebug = qbs.buildVariant !== "release"; - if (qbs.targetOS.contains("ios") || qbs.targetOS.contains("tvos") - || qbs.targetOS.contains("watchos")) { + if (qbs.targetOS.includes("ios") || qbs.targetOS.includes("tvos") + || qbs.targetOS.includes("watchos")) { switch (signingType) { case "app-store": return isDebug ? "iPhone Developer" : "iPhone Distribution"; } } - if (qbs.targetOS.contains("macos")) { + if (qbs.targetOS.includes("macos")) { switch (signingType) { case "app-store": return isDebug ? "Mac Developer" : "3rd Party Mac Developer Application"; @@ -161,27 +161,27 @@ CodeSignModule { readonly property bool _provisioningProfileAllowed: product.bundle && product.bundle.isBundle - && product.type.contains("application") + && product.type.includes("application") && xcode.platformType !== "simulator" // Required for tvOS, iOS, and watchOS (not simulators) // PROVISIONING_PROFILE_REQUIRED is specified only in Embedded-Device.xcspec in the // IDEiOSSupportCore IDE plugin, so we'll just write out the logic here manually readonly property bool _provisioningProfileRequired: - _provisioningProfileAllowed && !qbs.targetOS.contains("macos") + _provisioningProfileAllowed && !qbs.targetOS.includes("macos") // Not used on simulator platforms either but provisioning profiles aren't used there anyways readonly property string _provisioningProfilePlatform: { - if (qbs.targetOS.contains("macos")) + if (qbs.targetOS.includes("macos")) return "OSX"; - if (qbs.targetOS.contains("ios") || qbs.targetOS.contains("watchos")) + if (qbs.targetOS.includes("ios") || qbs.targetOS.includes("watchos")) return "iOS"; - if (qbs.targetOS.contains("tvos")) + if (qbs.targetOS.includes("tvos")) return "tvOS"; } readonly property string _embeddedProfileName: - (xcode._platformProps || {})["EMBEDDED_PROFILE_NAME"] + (xcode._platformProps || {})["EMBEDDED_PROFILE_NAME"] || "embedded.mobileprovision" setupBuildEnvironment: { var prefixes = product.xcode ? [ diff --git a/share/qbs/modules/codesign/codesign.js b/share/qbs/modules/codesign/codesign.js index 463e7cbb7..903d16f80 100644 --- a/share/qbs/modules/codesign/codesign.js +++ b/share/qbs/modules/codesign/codesign.js @@ -43,10 +43,12 @@ function findSigningIdentities(searchString, team) { var matchedIdentities = {}; for (var key in identities) { var identity = identities[key]; - if (team && ![identity.subjectInfo.O, identity.subjectInfo.OU].contains(team)) + if (team && ![identity.subjectInfo.O, identity.subjectInfo.OU].includes(team)) continue; - if (searchString === key || identity.subjectInfo.CN.startsWith(searchString)) + if (searchString === key + || (identity.subjectInfo.CN && identity.subjectInfo.CN.startsWith(searchString))) { matchedIdentities[key] = identity; + } } return matchedIdentities; } @@ -103,7 +105,7 @@ function findBestProvisioningProfile(product, files) { // Provisioning profiles are not normally used with ad-hoc code signing or non-apps // We do these checks down here only for the automatic selection but not above because // if the user explicitly selects a provisioning profile it should be used no matter what - if (actualSigningIdentity.SHA1 === "-" || !product.type.contains("application")) + if (actualSigningIdentity.SHA1 === "-" || !product.type.includes("application")) return undefined; // Filter out any provisioning profiles we know to be unsuitable from the start @@ -114,7 +116,7 @@ function findBestProvisioningProfile(product, files) { var certCommonNames = (data["DeveloperCertificates"] || []).map(function (cert) { return Utilities.certificateInfo(cert).subjectInfo.CN; }); - if (!certCommonNames.contains(actualSigningIdentity.subjectInfo.CN)) { + if (!certCommonNames.includes(actualSigningIdentity.subjectInfo.CN)) { console.log("Skipping provisioning profile with no matching certificate names for '" + actualSigningIdentity.subjectInfo.CN + "' (found " + certCommonNames.join(", ") + "): " @@ -124,7 +126,7 @@ function findBestProvisioningProfile(product, files) { } var platforms = data["Platform"] || []; - if (platforms.length > 0 && profilePlatform && !platforms.contains(profilePlatform)) { + if (platforms.length > 0 && profilePlatform && !platforms.includes(profilePlatform)) { console.log("Skipping provisioning profile for platform " + platforms.join(", ") + " (current platform " + profilePlatform + ")" + ": " + profile.filePath); @@ -132,7 +134,7 @@ function findBestProvisioningProfile(product, files) { } if (teamIdentifier - && !data["TeamIdentifier"].contains(teamIdentifier) + && !data["TeamIdentifier"].includes(teamIdentifier) && data["TeamName"] !== teamIdentifier) { console.log("Skipping provisioning profile for team " + data["TeamIdentifier"] + " (" + data["TeamName"] + ") (current team " + teamIdentifier + ")" @@ -223,7 +225,7 @@ function findBestSignToolSearchPaths(arch) { }); function addSearchPath(searchPath) { - if (File.exists(searchPath) && !searchPaths.contains(searchPath)) { + if (File.exists(searchPath) && !searchPaths.includes(searchPath)) { searchPaths.push(searchPath); return true; } diff --git a/share/qbs/modules/codesign/signtool.qbs b/share/qbs/modules/codesign/signtool.qbs index 0fc50f1b7..111f0a307 100644 --- a/share/qbs/modules/codesign/signtool.qbs +++ b/share/qbs/modules/codesign/signtool.qbs @@ -35,9 +35,9 @@ import qbs.Probes import "codesign.js" as CODESIGN CodeSignModule { - condition: qbs.targetOS.contains("windows") - && Host.os().contains("windows") - && qbs.toolchain.contains("msvc") + condition: qbs.targetOS.includes("windows") + && Host.os().includes("windows") + && qbs.toolchain.includes("msvc") _canSignArtifacts: true diff --git a/share/qbs/modules/cpp/CppModule.qbs b/share/qbs/modules/cpp/CppModule.qbs index 038de8617..bd1eaa242 100644 --- a/share/qbs/modules/cpp/CppModule.qbs +++ b/share/qbs/modules/cpp/CppModule.qbs @@ -29,7 +29,7 @@ ****************************************************************************/ // base for Cpp modules -import qbs.Host +import qbs.FileInfo import qbs.ModUtils import qbs.Utilities import qbs.WindowsUtils @@ -217,7 +217,7 @@ Module { property stringList knownArchitectures: [] property var toolchainDetails - property string compilerExtension: Host.os().contains("windows") ? ".exe" : "" + property string compilerExtension: FileInfo.executableSuffix() property string linkerMode: "automatic" PropertyOptions { diff --git a/share/qbs/modules/cpp/DarwinGCC.qbs b/share/qbs/modules/cpp/DarwinGCC.qbs index 81d8bded2..e20973acc 100644 --- a/share/qbs/modules/cpp/DarwinGCC.qbs +++ b/share/qbs/modules/cpp/DarwinGCC.qbs @@ -43,7 +43,7 @@ import "gcc.js" as Gcc UnixGCC { condition: false - Depends { name: "xcode"; required: qbs.toolchain && qbs.toolchain.contains("xcode") } + Depends { name: "xcode"; required: qbs.toolchain && qbs.toolchain.includes("xcode") } Probes.BinaryProbe { id: lipoProbe @@ -76,7 +76,7 @@ UnixGCC { dynamicLibrarySuffix: ".dylib" Properties { - condition: product.multiplexByQbsProperties.contains("buildVariants") + condition: product.multiplexByQbsProperties.includes("buildVariants") && qbs.buildVariants && qbs.buildVariants.length > 1 && (!product.aggregate || !!product.multiplexConfigurationId) && qbs.buildVariant !== "release" @@ -107,39 +107,42 @@ UnixGCC { property var defaultInfoPlist: { var dict = {}; - if (qbs.targetOS.contains("macos")) { + if (qbs.targetOS.includes("macos")) { dict["NSPrincipalClass"] = "NSApplication"; // needed for Retina display support + // QBS-1670: set this flag by default to avoid extensive GPU usage + dict["NSSupportsAutomaticGraphicsSwitching"] = true; + if (minimumMacosVersion) dict["LSMinimumSystemVersion"] = minimumMacosVersion; } - if (qbs.targetOS.contains("ios") && minimumIosVersion) + if (qbs.targetOS.includes("ios") && minimumIosVersion) dict["MinimumOSVersion"] = minimumIosVersion; - else if (qbs.targetOS.contains("tvos") && minimumTvosVersion) + else if (qbs.targetOS.includes("tvos") && minimumTvosVersion) dict["MinimumOSVersion"] = minimumTvosVersion; - else if (qbs.targetOS.contains("watchos") && minimumWatchosVersion) + else if (qbs.targetOS.includes("watchos") && minimumWatchosVersion) dict["MinimumOSVersion"] = minimumWatchosVersion; if (qbs.targetOS.containsAny(["ios", "tvos"])) { dict["LSRequiresIPhoneOS"] = true; if (xcode.platformType === "device") { - if (qbs.targetOS.contains("ios")) { + if (qbs.targetOS.includes("ios")) { if (qbs.architecture === "arm64") dict["UIRequiredDeviceCapabilities"] = ["arm64"]; else dict["UIRequiredDeviceCapabilities"] = ["armv7"]; } - if (qbs.targetOS.contains("tvos")) + if (qbs.targetOS.includes("tvos")) dict["UIRequiredDeviceCapabilities"] = ["arm64"]; } } if (xcode.present) { var targetDevices = DarwinTools.targetedDeviceFamily(xcode.targetDevices); - if (qbs.targetOS.contains("ios")) + if (qbs.targetOS.includes("ios")) dict["UIDeviceFamily"] = targetDevices; if (qbs.targetOS.containsAny(["ios", "watchos"])) { @@ -150,13 +153,13 @@ UnixGCC { "UIInterfaceOrientationLandscapeRight" ]; - if (targetDevices.contains("ipad")) + if (targetDevices.includes("ipad")) dict["UISupportedInterfaceOrientations~ipad"] = orientations; - if (targetDevices.contains("watch")) + if (targetDevices.includes("watch")) dict["UISupportedInterfaceOrientations"] = orientations.slice(0, 2); - if (targetDevices.contains("iphone")) { + if (targetDevices.includes("iphone")) { orientations.splice(1, 1); dict["UISupportedInterfaceOrientations"] = orientations; } @@ -196,13 +199,13 @@ UnixGCC { // Set the corresponding environment variable even if the minimum OS version is undefined, // because this indicates the default deployment target for that OS - if (qbs.targetOS.contains("ios") && minimumIosVersion) + if (qbs.targetOS.includes("ios") && minimumIosVersion) env["IPHONEOS_DEPLOYMENT_TARGET"] = minimumIosVersion; - if (qbs.targetOS.contains("macos") && minimumMacosVersion) + if (qbs.targetOS.includes("macos |