diff --git a/.ci.yaml b/.ci.yaml index 9d8f8f5439c93..42142d0bccbbf 100644 --- a/.ci.yaml +++ b/.ci.yaml @@ -586,18 +586,45 @@ targets: # to GCS", and conditionally on the "master" channel will also deploy the docs # to Firebase (https://main-api.flutter.dev/). # - # See "Linux docs_deploy_stable" for how the docs are deployed to stable API. + # See "Linux docs_generate_release" for how docs are built (but not published) + # and "Linux docs_deploy_stable" for how docs deployed for the stable branch + # (we do not deploy docs for beta). - name: Linux docs_publish recipe: flutter/docs presubmit: false backfill: false - # This means "allow this to be scheduled by the release/release_builder" - # recipe. Normally we'd use "schedule: true", but that *also* means "do not - # run this in normal presubmit/postsubmit", and we do want it to run in - # postsubmit for the "master" channel. Sorry. - # - # See https://github.com/flutter/flutter/issues/168709 for details. - schedule_during_release_override: true + enabled_branches: + - master + timeout: 60 + dimensions: + os: "Linux" + properties: + cores: "32" + dependencies: >- + [ + {"dependency": "dashing", "version": "0.4.0"}, + {"dependency": "firebase", "version": "v11.0.1"} + ] + tags: > + ["framework", "hostonly", "linux"] + validation: docs + validation_name: Docs + firebase_project: main-docs-flutter-prod + release_ref: refs/heads/master + drone_dimensions: + - os=Linux + + - name: Linux docs_generate_release + recipe: flutter/docs + # TODO(matanlurey): This has no effect, is used to allow creating a builder. + # Remove in the next PR. + # https://github.com/flutter/flutter/issues/168913 + bringup: true + scheduler: release + presubmit: false + postsubmit: false + enabled_branches: + - flutter-\d+\.\d+-candidate\.\d+ timeout: 60 dimensions: os: "Linux" @@ -612,11 +639,42 @@ targets: ["framework", "hostonly", "linux"] validation: docs validation_name: Docs + # TODO(matanlurey): Neither of these properties are actually used, since + # the branch name is not "master", but if they are removed, the recipe + # will fail. See https://github.com/flutter/flutter/issues/169108. firebase_project: main-docs-flutter-prod release_ref: refs/heads/master drone_dimensions: - os=Linux + + # This step runs on the release channel "stable", after the same commit SHA + # has been run and built by Linux flutter_release_builder as part of a release + # candidate branch (i.e. /flutter-\d+\.\d+-candidate\.\d+/) in the previous + # target, "Linux docs_generate_release". + - name: Linux docs_deploy_stable + recipe: flutter/docs + scheduler: release + presubmit: false + postsubmit: false + enabled_branches: + - stable + timeout: 60 + properties: + cores: "32" + dependencies: >- + [ + {"dependency": "dashing", "version": "0.4.0"}, + {"dependency": "firebase", "version": "v11.0.1"} + ] + tags: > + ["framework", "hostonly", "linux"] + validation: docs_deploy + validation_name: Docs_deploy + firebase_project: docs-flutter-dev + drone_dimensions: + - os=Linux + - name: Linux docs_test recipe: flutter/flutter_drone timeout: 90 # https://github.com/flutter/flutter/issues/120901 @@ -1520,10 +1578,12 @@ targets: test_timeout_secs: "2700" runIf: - packages/flutter_tools/templates/** + - packages/flutter_tools/gradle/** - .ci.yaml - engine/** - DEPS - dev/devicelab/bin/tasks/android_java11_dependency_smoke_tests.dart + - dev/devicelab/lib/framework/dependency_smoke_test_task_definition.dart - name: Linux android_java17_dependency_smoke_tests recipe: devicelab/devicelab_drone @@ -1542,10 +1602,12 @@ targets: test_timeout_secs: "2700" runIf: - packages/flutter_tools/templates/** + - packages/flutter_tools/gradle/** - .ci.yaml - engine/** - DEPS - dev/devicelab/bin/tasks/android_java17_dependency_smoke_tests.dart + - dev/devicelab/lib/framework/dependency_smoke_test_task_definition.dart - name: Linux tool_tests_commands recipe: flutter/flutter_drone @@ -6920,30 +6982,3 @@ targets: ["framework", "hostonly", "shard", "windows"] drone_dimensions: - os=Windows - - # This step runs on the release channel "stable", after the same commit SHA - # has been run and built by Linux flutter_release_builder as part of a release - # candidate branch (i.e. /flutter-\d+\.\d+-candidate\.\d+/) in the previous - # target, "Linux docs_publish". - - name: Linux docs_deploy_stable - recipe: flutter/docs - scheduler: release - bringup: true - enabled_branches: - - stable - presubmit: false - timeout: 60 - properties: - cores: "32" - dependencies: >- - [ - {"dependency": "dashing", "version": "0.4.0"}, - {"dependency": "firebase", "version": "v11.0.1"} - ] - tags: > - ["framework", "hostonly", "linux"] - validation: docs_deploy - validation_name: Docs_deploy - firebase_project: docs-flutter-dev - drone_dimensions: - - os=Linux diff --git a/.github/labeler.yml b/.github/labeler.yml deleted file mode 100644 index f6b637c200574..0000000000000 --- a/.github/labeler.yml +++ /dev/null @@ -1,264 +0,0 @@ -# Copyright 2013 The Flutter Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -# See https://github.com/actions/labeler/blob/main/README.md for docs. -# Use git ls-files '' to see the list of matching files. - -'a: accessibility': - - changed-files: - - any-glob-to-any-file: - - '**/accessibility/*' - - '**/*accessibility*' - - '**/semantics/*' - - '**/*semantics*' - -'a: animation': - - changed-files: - - any-glob-to-any-file: - - '**/animation/*' - - '**/*animation*' - -'a: desktop': - - changed-files: - - any-glob-to-any-file: - - '**/linux/**/*' - - '**/macos/**/*' - - '**/windows/**/*' - - engine/src/flutter/shell/platform/darwin/common/**/* - -'a: internationalization': - - changed-files: - - any-glob-to-any-file: - - packages/flutter_localizations/**/* - -'a: tests': - - changed-files: - - any-glob-to-any-file: - - packages/flutter_driver/**/* - - packages/flutter_goldens/**/* - - packages/flutter_test/**/* - - packages/integration_test/**/* - -'a: text input': - - changed-files: - - all-globs-to-any-file: - - '**/*text*' - - '!**/*context*' - - '!**/*texture*' - -'d: api docs': - - changed-files: - - any-glob-to-any-file: - - examples/api/**/* - -'d: docs/': - - changed-files: - - any-glob-to-any-file: - - docs/**/* - -'d: examples': - - changed-files: - - any-glob-to-any-file: - - examples/**/* - -'e: embedder': - - changed-files: - - any-glob-to-any-file: - - engine/src/flutter/shell/platform/embedder - -'e: impeller': - - changed-files: - - any-glob-to-any-file: - - engine/src/flutter/impeller/**/* - -engine: - - changed-files: - - any-glob-to-any-file: - - DEPS - - engine/**/* - - docs/engine/**/* - -'f: cupertino': - - changed-files: - - any-glob-to-any-file: - - '**/cupertino/*' - - '**/*cupertino*' - - docs/libraries/cupertino/**/* - -'f: focus': - - changed-files: - - any-glob-to-any-file: - - '**/focus/*' - - '**/*focus*' - -'f: gestures': - - changed-files: - - any-glob-to-any-file: - - '**/gestures/*' - - '**/*gestures*' - -'f: material design': - - changed-files: - - any-glob-to-any-file: - - '**/material/*' - - '**/*material*' - - docs/libraries/material/**/* - -'f: routes': - - changed-files: - - any-glob-to-any-file: - - '**/navigator/*' - - '**/*navigator*' - - '**/route/*' - - '**/*route*' - -'f: scrolling': - - changed-files: - - any-glob-to-any-file: - - '**/*scroll*' - - '**/scroll/*' - - '**/*sliver*' - - '**/sliver/*' - - '**/*viewport*' - - '**/viewport/*' - -framework: - - changed-files: - - any-glob-to-any-file: - - packages/flutter/**/* - - packages/flutter_driver/**/* - - packages/flutter_goldens/**/* - - packages/flutter_localizations/**/* - - packages/flutter_test/**/* - - packages/integration_test/**/* - - examples/api/**/* - - docs/about/**/* - - docs/contributing/**/* - - docs/libraries/**/* - -'f: integration_test': - - changed-files: - - any-glob-to-any-file: - - packages/integration_test/**/* - -package: - - changed-files: - - any-glob-to-any-file: - - docs/ecosystem/**/* - -platform-android: - - changed-files: - - any-glob-to-any-file: - - docs/platform/android/**/* - - engine/src/flutter/shell/platform/android/**/* - - packages/flutter_tools/*android*' - - packages/flutter_tools/gradle/**/* - -platform-ios: - - changed-files: - - any-glob-to-any-file: - - engine/src/flutter/shell/platform/darwin/common/**/* - - engine/src/flutter/shell/platform/darwin/ios/**/* - - packages/flutter_tools/lib/src/ios/**/* - -platform-fuchsia: - - changed-files: - - any-glob-to-any-file: - - engine/src/flutter/shell/platform/fuchsia/**/* - -platform-linux: - - changed-files: - - any-glob-to-any-file: - - engine/src/flutter/shell/platform/linux/**/* - -platform-macos: - - changed-files: - - any-glob-to-any-file: - - engine/src/flutter/src/flutter/shell/platform/darwin/common/**/* - - engine/src/flutter/shell/platform/darwin/macos/**/* - -platform-web: - - changed-files: - - any-glob-to-any-file: - - '**/web_sdk/**/*' - - engine/src/flutter/lib/web_ui/**/* - - packages/flutter_web_plugins - -platform-windows: - - changed-files: - - any-glob-to-any-file: - - engine/src/flutter/shell/platform/windows/**/* - -'customer: gallery': - - changed-files: - - any-glob-to-any-file: - - examples/flutter_gallery/**/* - -'c: tech-debt': - - changed-files: - - any-glob-to-any-file: - - '**/fix_data.yaml' - - '**/*.expect' - - '**/*test_fixes*' - -team: - - changed-files: - - any-glob-to-any-file: - - docs/about/**/* - - docs/contributing/**/* - - docs/postmortems/**/* - -team-android: - - changed-files: - - any-glob-to-any-file: - - docs/platform/android/**/* - -team-ecosystem: - - changed-files: - - any-glob-to-any-file: - - docs/ecosystem/**/* - -team-engine: - - changed-files: - - any-glob-to-any-file: - - docs/engine/**/* - -team-infra: - - changed-files: - - any-glob-to-any-file: - - docs/infra/**/* - -# Keep this synced with CODEOWNERS. -team-ios: - - changed-files: - - any-glob-to-any-file: - - engine/src/flutter/shell/platform/darwin/common/**/* - - engine/src/flutter/shell/platform/darwin/ios/framework/**/* - - packages/flutter_tools/**/ios/* - - packages/flutter_tools/**/macos/* - - packages/flutter_tools/**/*xcode* - - packages/flutter_tools/**/*ios* - - packages/flutter_tools/**/*macos* - -team-release: - - changed-files: - - any-glob-to-any-file: - - docs/releases/**/* - -team-tool: - - changed-files: - - any-glob-to-any-file: - - docs/tool/**/* - -team-web: - - changed-files: - - any-glob-to-any-file: - - docs/platforms/web/**/* - -tool: - - changed-files: - - any-glob-to-any-file: - - packages/flutter_tools/**/* - - packages/fuchsia_remote_debug_protocol/**/* - - docs/tool/**/* diff --git a/.github/workflows/build-docker.yml b/.github/workflows/build-docker.yml new file mode 100644 index 0000000000000..d15fb0f88456b --- /dev/null +++ b/.github/workflows/build-docker.yml @@ -0,0 +1,21 @@ +name: Build Docker + +on: [workflow_dispatch] + +jobs: + builder: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: docker/setup-buildx-action@v3 + - uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.repository_owner }} + password: ${{ secrets.GITHUB_TOKEN }} + - uses: docker/build-push-action@v5 + with: + context: engine/src/flutter/ci/tizen + file: engine/src/flutter/ci/tizen/Dockerfile + push: true + tags: ghcr.io/${{ github.repository_owner }}/build-engine:latest diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 0000000000000..61fd921e209ed --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,314 @@ +name: Build + +on: [push, pull_request] +jobs: + build: + runs-on: ubuntu-latest + container: + image: ghcr.io/flutter-tizen/build-engine:latest + credentials: + username: ${{ github.repository_owner }} + password: ${{ secrets.GITHUB_TOKEN }} + + strategy: + matrix: + arch: [arm, arm64, x86] + mode: [debug, release, profile] + include: + - arch: arm + triple: arm-linux-gnueabi + - arch: arm64 + triple: aarch64-linux-gnu + - arch: x86 + triple: i686-linux-gnu + exclude: + - arch: x86 + mode: release + - arch: x86 + mode: profile + + steps: + - uses: actions/checkout@v4 + with: + path: flutter + fetch-depth: 1 + + - uses: actions/cache@v4 + with: + path: /tizen_tools/sysroot + key: sysroot + + - name: Install depot_tools + run: | + git clone --depth=1 https://chromium.googlesource.com/chromium/tools/depot_tools.git + echo "$PWD/depot_tools" >> $GITHUB_PATH + export PATH="$PWD/depot_tools:$PATH" + # Install ninja + apt install ninja-build + + - name: Run gclient sync + run: | + cd flutter + gclient config --unmanaged https://github.com/flutter-tizen/flutter + sed -i "s/'flutter'/'.'/g" ./.gclient + gclient setdep --var=download_dart_sdk=False --var=download_android_deps=False --var=download_fuchsia_deps=False --deps-file=DEPS + gclient sync -v --no-history --shallow + + - name: Generate sysroot + run: flutter/engine/src/flutter/ci/tizen/generate_sysroot.py --out /tizen_tools/sysroot + + - name: Build + run: | + cd flutter + # Ignore unsupported linker option. + sed -i "/-Wl,--undefined-version/d" engine/src/build/config/compiler/BUILD.gn + + engine/src/flutter/tools/gn \ + --target-os linux \ + --linux-cpu ${{ matrix.arch }} \ + --no-goma \ + --target-toolchain /tizen_tools/toolchains \ + --target-sysroot /tizen_tools/sysroot/${{ matrix.arch }} \ + --target-triple ${{ matrix.triple }} \ + --runtime-mode ${{ matrix.mode }} \ + --enable-fontconfig \ + --disable-desktop-embeddings \ + --target-dir build + cd engine/src + ninja -C out/build flutter_engine_library + cp -f flutter/third_party/icu/flutter/icudtl.dat out/build + + - name: Build gen_snapshot + if: ${{ matrix.mode != 'debug' }} + run: | + cd flutter + ninja -C engine/src/out/build clang_x64/gen_snapshot + + - uses: actions/upload-artifact@v4 + with: + name: tizen-${{ matrix.arch }}-${{ matrix.mode }} + path: | + flutter/engine/src/out/build/libflutter_engine.so + flutter/engine/src/out/build/icudtl.dat + if-no-files-found: error + + - uses: actions/upload-artifact@v4 + if: ${{ github.event_name == 'push' }} + with: + name: tizen-${{ matrix.arch }}-${{ matrix.mode }}_symbols + path: flutter/engine/src/out/build/so.unstripped/libflutter_engine.so + if-no-files-found: error + + - uses: actions/upload-artifact@v4 + if: ${{ matrix.mode != 'debug' }} + with: + name: tizen-${{ matrix.arch }}-${{ matrix.mode }}_linux-x64 + path: flutter/engine/src/out/build/clang_x64/gen_snapshot + if-no-files-found: error + + windows-build: + runs-on: windows-latest + + strategy: + matrix: + arch: [arm, arm64] + mode: [release, profile] + + steps: + - name: Run git checkout + run: | + mkdir C:\workspace\flutter + cd C:\workspace\flutter + git config --global core.autocrlf false + git config --global core.filemode false + git config --global core.fscache true + git config --global core.preloadindex true + git config --global depot-tools.allowGlobalGitConfig true + git init --quiet + git remote add origin https://github.com/${{ github.repository }} + git fetch --depth 1 origin ${{ github.sha }} + git checkout FETCH_HEAD + + - name: Environment setup + run: | + Set-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Control\FileSystem" -Name "LongPathsEnabled" -Value 1 -Force + echo "DEPOT_TOOLS_WIN_TOOLCHAIN=0" >> $Env:GITHUB_ENV + echo "GYP_MSVS_OVERRIDE_PATH=C:\Program Files\Microsoft Visual Studio\2022\Enterprise" >> $Env:GITHUB_ENV + echo "WINDOWSSDKDIR=C:\Program Files (x86)\Windows Kits\10" >> $Env:GITHUB_ENV + + - name: Install depot_tools + run: | + Invoke-WebRequest -Uri https://storage.googleapis.com/chrome-infra/depot_tools.zip -OutFile depot_tools.zip + 7z x -y -o"C:\workspace\depot_tools" .\depot_tools.zip + echo "C:\workspace\depot_tools" >> $Env:GITHUB_PATH + + - name: Run gclient sync + working-directory: C:\workspace\flutter + shell: powershell + run: | + gclient config --unmanaged https://github.com/flutter-tizen/flutter + (Get-Content ".gclient") | ForEach-Object { $_ -replace "'flutter'","'.'" } | Set-Content ".gclient" + # TODO(jsuya) : pipes deprecated in python 3.13. (https://dart-review.googlesource.com/c/sdk/+/307620) + (Get-Content "engine/src/build/vs_toolchain.py") | ForEach-Object { $_ -replace 'import pipes','' } | Set-Content "engine/src/build/vs_toolchain.py" + gclient setdep --var=download_dart_sdk=False --var=download_android_deps=False --var=download_fuchsia_deps=False --deps-file=DEPS + gclient sync -v --no-history --shallow + + - name: Build + working-directory: C:\workspace\flutter\engine\src + run: | + python3 .\flutter\tools\gn ` + --linux ` + --linux-cpu=${{ matrix.arch }} ` + --runtime-mode=${{ matrix.mode }} ` + --no-goma ` + --target-dir build + ninja -C .\out\build gen_snapshot + + - uses: actions/upload-artifact@v4 + with: + name: tizen-${{ matrix.arch }}-${{ matrix.mode }}_windows-x64 + path: C:\workspace\flutter\engine\src\out\build\gen_snapshot.exe + if-no-files-found: error + + macos-build: + runs-on: macos-latest + + strategy: + matrix: + arch: [arm, arm64] + mode: [release, profile] + + steps: + - uses: actions/checkout@v4 + with: + path: flutter + fetch-depth: 1 + + - uses: actions/setup-python@v5 + with: + python-version: "3.11" + + - name: Install depot_tools + run: | + git clone --depth=1 https://chromium.googlesource.com/chromium/tools/depot_tools.git + echo "$PWD/depot_tools" >> $GITHUB_PATH + + - name: Run gclient sync + run: | + cd flutter + gclient config --unmanaged https://github.com/flutter-tizen/flutter + sed -i '' "s/'flutter'/'.'/g" .gclient + gclient setdep --var=download_dart_sdk=False --var=download_android_deps=False --var=download_fuchsia_deps=False --deps-file=DEPS + gclient sync -v --no-history --shallow + + - name: Build + run: | + cd flutter + # Change host_toolchain to mac/clang_arm64. + sed -i "" "s|//build/toolchain/linux:clang_$host_cpu|//build/toolchain/mac:clang_$host_cpu|g" engine/src/build/config/BUILDCONFIG.gn + + # Pass dummy values to prevent using the default (Linux) toolchain. + engine/src/flutter/tools/gn \ + --linux \ + --linux-cpu=${{ matrix.arch }} \ + --no-goma \ + --target-toolchain _ \ + --target-sysroot _ \ + --target-triple _ \ + --runtime-mode=${{ matrix.mode }} \ + --disable-desktop-embeddings \ + --target-dir build + cd engine/src + ninja -C out/build clang_arm64/gen_snapshot + + - uses: actions/upload-artifact@v4 + with: + name: tizen-${{ matrix.arch }}-${{ matrix.mode }}_darwin-arm64 + path: flutter/engine/src/out/build/clang_arm64/gen_snapshot + if-no-files-found: error + + macos-intel-build: + runs-on: macos-13 + + strategy: + matrix: + arch: [arm, arm64] + mode: [release, profile] + + steps: + - uses: actions/checkout@v4 + with: + path: flutter + fetch-depth: 1 + + - uses: actions/setup-python@v5 + with: + python-version: "3.11" + + - name: Install depot_tools + run: | + git clone --depth=1 https://chromium.googlesource.com/chromium/tools/depot_tools.git + echo "$PWD/depot_tools" >> $GITHUB_PATH + + - name: Run gclient sync + run: | + cd flutter + gclient config --unmanaged https://github.com/flutter-tizen/flutter + sed -i '' "s/'flutter'/'.'/g" .gclient + gclient setdep --var=download_dart_sdk=False --var=download_android_deps=False --var=download_fuchsia_deps=False --deps-file=DEPS + gclient sync -v --no-history --shallow + + - name: Build + run: | + cd flutter + # Change host_toolchain to mac/clang_x64. + sed -i "" "s|//build/toolchain/linux:clang_$host_cpu|//build/toolchain/mac:clang_$host_cpu|g" engine/src/build/config/BUILDCONFIG.gn + + # Pass dummy values to prevent using the default (Linux) toolchain. + engine/src/flutter/tools/gn \ + --linux \ + --linux-cpu=${{ matrix.arch }} \ + --no-goma \ + --target-toolchain _ \ + --target-sysroot _ \ + --target-triple _ \ + --runtime-mode=${{ matrix.mode }} \ + --disable-desktop-embeddings \ + --target-dir build + cd engine/src + ninja -C out/build clang_x64/gen_snapshot + + - uses: actions/upload-artifact@v4 + with: + name: tizen-${{ matrix.arch }}-${{ matrix.mode }}_darwin-x64 + path: flutter/engine/src/out/build/clang_x64/gen_snapshot + if-no-files-found: error + + release: + needs: [build] + if: ${{ github.event_name == 'push' }} + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + + - uses: actions/download-artifact@v4 + + - name: Create archives + run: | + for name in tizen-*; do + 7z a $name.zip ./$name/* + done + + - name: Set variables + run: | + echo "SHORT_SHA=$(git rev-parse --short $GITHUB_SHA)" >> $GITHUB_ENV + echo "VERSION=$(echo "${{ github.ref_name }}" | cut -d'-' -f2)" >> $GITHUB_ENV + + - uses: softprops/action-gh-release@v2 + with: + name: ${{ env.VERSION }} (${{ env.SHORT_SHA }}) + tag_name: ${{ env.SHORT_SHA }} + target_commitish: ${{ github.sha }} + files: tizen-*.zip + body: https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }} diff --git a/.github/workflows/easy-cp.yml b/.github/workflows/easy-cp.yml deleted file mode 100644 index 98e5e763aa9e1..0000000000000 --- a/.github/workflows/easy-cp.yml +++ /dev/null @@ -1,87 +0,0 @@ -# Copyright 2023 The Flutter Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -name: Cherry-pick Labeled PR to Release Branch - -on: - pull_request_target: - branches: master - types: [labeled] - -permissions: write-all - -jobs: - cherrypick_to_release: - name: cherrypick_to_release - runs-on: ubuntu-latest - if: | - (github.event.label.name == format('cp{0} beta', ':') || github.event.label.name == format('cp{0} stable', ':')) && - (github.event.pull_request.merged == true) - steps: - - name: Get Release Channel - run: | - echo "CHANNEL=$(echo ${{ github.event.label.name }} | cut -d ':' -f 2 | xargs)" >> $GITHUB_ENV - - name: Get Release Candidate Branch - run: | - RELEASE_BRANCH=$(curl https://raw.githubusercontent.com/flutter/flutter/$CHANNEL/bin/internal/release-candidate-branch.version) - echo "RELEASE_BRANCH=$(echo $RELEASE_BRANCH | tr -d '\n')" >> $GITHUB_ENV - - name: Get Cherry Pick PR - run: | - echo "COMMIT_SHA=$(echo ${{ github.event.pull_request.merge_commit_sha }})" >> $GITHUB_ENV - - name: Checkout Flutter Repo - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 - with: - repository: flutteractionsbot/flutter - path: flutter - ref: master - persist-credentials: false - # Checkout all history commits on master branch, so that the cp commit is a known object - fetch-depth: 0 - # use same name when checking out branch, since the marketplace action does a hard reset. - - name: Attempt CP - id: attempt-cp - working-directory: ./flutter - run: | - git config user.name "GitHub Actions Bot" - git config user.email "<>" - git remote add upstream https://github.com/flutter/flutter.git - git fetch upstream $RELEASE_BRANCH - git fetch upstream master - git checkout -b cp-${CHANNEL}-${COMMIT_SHA} --track upstream/$RELEASE_BRANCH - git cherry-pick $COMMIT_SHA - # TODO(xilaizhang): remove this step once the template is available on release branches. - - name: Get CP Template - run: | - curl -o PULL_REQUEST_CP_TEMPLATE.md https://raw.githubusercontent.com/flutter/flutter/main/.github/PR_TEMPLATE/PULL_REQUEST_CP_TEMPLATE.md - - name: Create PR on CP success - if: ${{ steps.attempt-cp.conclusion == 'success' }} - working-directory: ./flutter - id: create-pr - run: | - git push https://${{ env.GITHUB_TOKEN }}@github.com/flutteractionsbot/flutter cp-${CHANNEL}-${COMMIT_SHA} - { - echo 'PR_URL<> "$GITHUB_ENV" - env: - GITHUB_TOKEN: ${{ secrets.FLUTTERACTIONSBOT_CP_TOKEN }} - PR_TITLE: ${{ github.event.pull_request.title }} - - name: Leave Comment on CP success - if: ${{ steps.create-pr.conclusion == 'success' }} - run: | - echo $PR_URL - NEW_PR_NUMBER="${PR_URL##*/}" - SUCCESS_MSG=" @${{ github.actor }} please fill out the PR description above, afterwards the release team will review this request." - gh pr comment $NEW_PR_NUMBER -R flutter/flutter -b "${SUCCESS_MSG}" - env: - GITHUB_TOKEN: ${{ secrets.FLUTTERACTIONSBOT_CP_TOKEN }} - - name: Leave Comment on CP failure - if: ${{ failure() && steps.attempt-cp.conclusion == 'failure' }} - run: | - FAILURE_MSG="Failed to create CP due to merge conflicts.
" - FAILURE_MSG+="You will need to create the PR manually. See [the cherrypick wiki](https://github.com/flutter/flutter/blob/main/docs/releases/Flutter-Cherrypick-Process.md) for more info." - gh pr comment ${{ github.event.pull_request.number }} -R flutter/flutter -b "${FAILURE_MSG}" - env: - GITHUB_TOKEN: ${{ secrets.FLUTTERACTIONSBOT_CP_TOKEN }} diff --git a/.github/workflows/files-changed.yml b/.github/workflows/files-changed.yml deleted file mode 100644 index 9fc15957e0c93..0000000000000 --- a/.github/workflows/files-changed.yml +++ /dev/null @@ -1,41 +0,0 @@ -# Copyright 2013 The Flutter Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -name: Generate Changed Files JSON - -on: - pull_request: - types: - - opened - - synchronize - -jobs: - generate-json: - runs-on: ubuntu-latest - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Fetch base commit and origin/master - # Fetch what to compare the commit against - run: | - git fetch --no-tags --prune --depth=1 origin ${{ github.event.pull_request.base.sha }} - git fetch --no-tags --prune --depth=1 origin master - echo "FLUTTER_PREBUILT_ENGINE_VERSION=${{ github.event.pull_request.base.sha }}" >> "$GITHUB_ENV" - - - name: Initialize Dart SDK - # This downloads the version of the Dart SDK for the current platform. - run: | - ./bin/dart --version - cd dev/tools && ../../bin/dart pub get - - - name: Write changed files to a JSON file - run: | - ./bin/dart run dev/tools/bin/get_files_changed.dart --since="${{ github.event.pull_request.base.sha }}" > changed_files.json - - - name: Upload artifact - uses: actions/upload-artifact@v4 - with: - name: changed-files - path: changed_files.json diff --git a/.github/workflows/labeler.yml b/.github/workflows/labeler.yml deleted file mode 100644 index 3cea755bccd8c..0000000000000 --- a/.github/workflows/labeler.yml +++ /dev/null @@ -1,22 +0,0 @@ -# Copyright 2013 The Flutter Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -name: "Pull Request Labeler" -on: -- pull_request_target - -# Declare default permissions as read only. -permissions: read-all - -jobs: - triage: - if: ${{ github.repository == 'flutter/flutter' }} - permissions: - pull-requests: write - runs-on: ubuntu-latest - steps: - # Source available at https://github.com/actions/labeler/blob/main/README.md - - uses: actions/labeler@8558fd74291d67161a8a78ce36a881fa63b766a9 - with: - sync-labels: true diff --git a/.github/workflows/mirror.yml b/.github/workflows/mirror.yml deleted file mode 100644 index a568c78265469..0000000000000 --- a/.github/workflows/mirror.yml +++ /dev/null @@ -1,28 +0,0 @@ -# Copyright 2013 The Flutter Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -# Mirror master to main branches in the flutter repository. -on: - push: - branches: - - 'master' - -# Declare default permissions as read only. -permissions: read-all - -jobs: - mirror_job: - permissions: - pull-requests: write - runs-on: ubuntu-latest - if: ${{ github.repository == 'flutter/flutter' }} - name: Mirror master branch to main branch - steps: - - name: Mirror action step - id: mirror - uses: google/mirror-branch-action@30c52ee21f5d3bd7fb28b95501c11aae7f17eebb - with: - github-token: ${{ secrets.FLUTTERMIRRORINGBOT_TOKEN }} - source: 'master' - dest: 'main' diff --git a/CHANGELOG.md b/CHANGELOG.md index 1544980a5611a..86d0eaecaa2fd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -29,6 +29,33 @@ INTERNAL NOTE ## Flutter 3.32 Changes +### [3.32.4](https://github.com/flutter/flutter/releases/tag/3.32.4) + +- [flutter/170536](https://github.com/flutter/flutter/issues/170536) - Fixes a code-signing issue on Mac hosts when running `dart` tooling. + +### [3.32.3](https://github.com/flutter/flutter/releases/tag/3.32.3) + +- [flutter/170052](https://github.com/flutter/flutter/pull/170052) - Fixes "active" indicator for `NavigationBar` and `NavigationDrawer` +- [flutter/170013](https://github.com/flutter/flutter/pull/170013) - Fixes a memory leak in the Impeller Vulkan back end. +- [flutter/169912](https://github.com/flutter/flutter/pull/170003) - Fixes failures to build an Android AAB in release mode. + +### [3.32.2](https://github.com/flutter/flutter/releases/tag/3.32.2) + +- [flutter/169772](https://github.com/flutter/flutter/pull/169772) - Configuration changes for Flutter's CI to run tests on Linux instead of Windows when not otherwise required. +- [flutter/169630](https://github.com/flutter/flutter/pull/169630) - Fixes issue where flavored Android packages may not successfully build on Windows repeatedly until the next clean. +- [flutter/169912](https://github.com/flutter/flutter/pull/169912) - Splits Flutter CI task for publishing API docs into one build step and one deploy step. + +### [3.32.1](https://github.com/flutter/flutter/releases/tag/3.32.1) + +- [flutter/156793](https://github.com/flutter/flutter/issues/156793) - Fixes flaky crash when targeting web applications via IDEs using the DAP. +- [flutter/168849](https://github.com/flutter/flutter/issues/168849) - Fixes an issue rendering wide gamut images. +- [flutter/168846](https://github.com/flutter/flutter/issues/168846) - Fixes an issue displaying the wrong icons in the widget inspector for some apps. +- [flutter/167011](https://github.com/flutter/flutter/pull/167011) - Fixes Flutter Android builds for apps which use plugins with old Android Gradle Plugin versions. +- [flutter/169101](https://github.com/flutter/flutter/issues/169101) - Reduces the cost of running the (sometimes flaky) Linux fuchsia_test on release branches. +- [flutter/169318](https://github.com/flutter/flutter/issues/169318) - Fixed a bug where the flutter tool crash reporting did not include what plugins were being used by the current project. +- [flutter/169160](https://github.com/flutter/flutter/issues/169160) Fixed a bug where `appFlavor` is null after hot restarts or during `flutter test`. +- [flutter/156793](https://github.com/flutter/flutter/issues/156793) - Fix flaky crash when targeting web applications via IDEs using the DAP. + ### [3.32.0](https://github.com/flutter/flutter/releases/tag/3.32.0) Initial stable release. diff --git a/DEPS b/DEPS index b3d3cb6d0c3b6..8b62c85151937 100644 --- a/DEPS +++ b/DEPS @@ -14,7 +14,7 @@ vars = { 'flutter_git': 'https://flutter.googlesource.com', 'skia_git': 'https://skia.googlesource.com', 'llvm_git': 'https://llvm.googlesource.com', - 'skia_revision': 'ac01f9306a0c08acf128d37bbd7b3e199525cc40', + 'skia_revision': '1f39b47bf067193e98f92a75f7ae824129c18d03', # WARNING: DO NOT EDIT canvaskit_cipd_instance MANUALLY # See `lib/web_ui/README.md` for how to roll CanvasKit to a new version. @@ -56,7 +56,7 @@ vars = { # Dart is: https://github.com/dart-lang/sdk/blob/main/DEPS # You can use //tools/dart/create_updated_flutter_deps.py to produce # updated revision list of existing dependencies. - 'dart_revision': 'b04011c77cd93e6ab9144af37976733b558d716c', + 'dart_revision': '05589740efb305ceef593b3db6cab2910c17d480', # WARNING: DO NOT EDIT MANUALLY # The lines between blank lines above and below are generated by a script. See create_updated_flutter_deps.py @@ -127,7 +127,7 @@ vars = { "checkout_llvm": False, # Setup Git hooks by default. - 'setup_githooks': True, + 'setup_githooks': False, # When this is true, the Flutter Engine's configuration files and scripts for # RBE will be downloaded from CIPD. This option is only usable by Googlers. diff --git a/TESTOWNERS b/TESTOWNERS index 95b3c0e856070..2ff8f7a6bf938 100644 --- a/TESTOWNERS +++ b/TESTOWNERS @@ -304,8 +304,8 @@ /dev/devicelab/bin/tasks/android_display_cutout.dart @reidbaker @flutter/android ## Host only framework tests -# Linux docs_deploy_beta # Linux docs_deploy_stable +# Linux docs_generate_release # Linux docs_publish /dev/bots/docs.sh @Piinks @flutter/framework # Linux packages_autoroller diff --git a/bin/internal/engine.version b/bin/internal/engine.version index 7706e0df1349a..05cb68b5e3663 100644 --- a/bin/internal/engine.version +++ b/bin/internal/engine.version @@ -1 +1 @@ -18818009497c581ede5d8a3b8b833b81d00cebb7 +8cd19e509d6bece8ccd74aef027c4ca947363095 diff --git a/dev/devicelab/bin/tasks/android_java11_dependency_smoke_tests.dart b/dev/devicelab/bin/tasks/android_java11_dependency_smoke_tests.dart index 94c3d37a465ee..40883ebe88e72 100644 --- a/dev/devicelab/bin/tasks/android_java11_dependency_smoke_tests.dart +++ b/dev/devicelab/bin/tasks/android_java11_dependency_smoke_tests.dart @@ -40,11 +40,14 @@ List versionTuples = [ kotlinVersion: '1.7.10', compileSdkVersion: '34', ), + // minSdk bump required due to a bug in the default version of r8 used by AGP + // 7.4.0. See http://issuetracker.google.com/issues/357553178. VersionTuple( agpVersion: '7.4.0', gradleVersion: '7.5', kotlinVersion: '1.8.10', compileSdkVersion: '34', + minSdkVersion: '24', ), ]; diff --git a/dev/devicelab/lib/framework/dependency_smoke_test_task_definition.dart b/dev/devicelab/lib/framework/dependency_smoke_test_task_definition.dart index d333046fffe29..ee46e68bdaae5 100644 --- a/dev/devicelab/lib/framework/dependency_smoke_test_task_definition.dart +++ b/dev/devicelab/lib/framework/dependency_smoke_test_task_definition.dart @@ -61,6 +61,7 @@ distributionUrl=https\://services.gradle.org/distributions/gradle-GRADLE_REPLACE const String gradleReplacementString = 'GRADLE_REPLACE_ME'; final RegExp flutterCompileSdkString = RegExp(r'flutter\.compileSdkVersion|flutter\.compileSdk'); +final RegExp flutterMinSdkString = RegExp(r'flutter\.minSdkVersion|flutter\.minSdk'); /// A simple class containing a Kotlin, Gradle, and AGP version. class VersionTuple { @@ -69,12 +70,14 @@ class VersionTuple { required this.gradleVersion, required this.kotlinVersion, this.compileSdkVersion, + this.minSdkVersion, }); String agpVersion; String gradleVersion; String kotlinVersion; String? compileSdkVersion; + String? minSdkVersion; @override String toString() { @@ -108,10 +111,10 @@ Future buildFlutterApkWithSpecifiedDependencyVersions({ final String appPath = '${innerTempDir.absolute.path}/dependency_checker_app'; + final File appGradleBuild = getAndroidBuildFile( + localFileSystem.path.join(appPath, 'android', 'app'), + ); if (versions.compileSdkVersion != null) { - final File appGradleBuild = getAndroidBuildFile( - localFileSystem.path.join(appPath, 'android', 'app'), - ); final String appBuildContent = appGradleBuild.readAsStringSync().replaceFirst( flutterCompileSdkString, versions.compileSdkVersion!, @@ -119,6 +122,14 @@ Future buildFlutterApkWithSpecifiedDependencyVersions({ appGradleBuild.writeAsStringSync(appBuildContent); } + if (versions.minSdkVersion != null) { + final String appBuildContent = appGradleBuild.readAsStringSync().replaceFirst( + flutterMinSdkString, + versions.minSdkVersion!, + ); + appGradleBuild.writeAsStringSync(appBuildContent); + } + // Modify gradle version to passed in version. final File gradleWrapperProperties = localFileSystem.file( localFileSystem.path.join( @@ -144,6 +155,13 @@ Future buildFlutterApkWithSpecifiedDependencyVersions({ .replaceFirst(kgpReplacementString, versions.kotlinVersion); await gradleSettingsFile.writeAsString(settingsContent, flush: true); + section('Add a dependency on a plugin'); + await flutter( + 'pub', + options: ['add', 'shared_preferences_android:2.4.7'], // Chosen randomly. + workingDirectory: appPath, + ); + // Ensure that gradle files exists from templates. section( "Ensure 'flutter build apk' succeeds with Gradle ${versions.gradleVersion}, AGP ${versions.agpVersion}, and Kotlin ${versions.kotlinVersion}", diff --git a/engine/README.md b/engine/README.md index c01e1d6c440d5..f6c13417c869e 100644 --- a/engine/README.md +++ b/engine/README.md @@ -1,5 +1,7 @@ # Flutter Engine + + ## Setting up the Engine development environment See [here](https://github.com/flutter/flutter/blob/master/engine/src/flutter/docs/contributing/Setting-up-the-Engine-development-environment.md#getting-the-source) diff --git a/engine/src/flutter/.ci.yaml b/engine/src/flutter/.ci.yaml index 2c476704ac02d..848e612fe5342 100644 --- a/engine/src/flutter/.ci.yaml +++ b/engine/src/flutter/.ci.yaml @@ -163,6 +163,9 @@ targets: - name: Linux linux_fuchsia_tests recipe: engine_v2/engine_v2 timeout: 90 + enabled_branches: + # Don't run this on release branches + - master properties: config_name: linux_fuchsia_tests # Do not remove(https://github.com/flutter/flutter/issues/144644) @@ -500,7 +503,7 @@ targets: - os=Mac-14 - cpu=x86 - - name: Windows windows_android_aot_engine + - name: Linux windows_android_aot_engine recipe: engine_v2/engine_v2 timeout: 120 properties: @@ -510,9 +513,9 @@ targets: # Do not remove(https://github.com/flutter/flutter/issues/144644) # Scheduler will fail to get the platform drone_dimensions: - - os=Windows + - os=Linux - - name: Windows windows_host_engine + - name: Linux windows_host_engine recipe: engine_v2/engine_v2 timeout: 120 properties: @@ -522,7 +525,7 @@ targets: # Do not remove(https://github.com/flutter/flutter/issues/144644) # Scheduler will fail to get the platform drone_dimensions: - - os=Windows + - os=Linux - name: Windows windows_host_engine_test recipe: engine_v2/engine_v2 diff --git a/engine/src/flutter/ci/licenses_golden/licenses_skia b/engine/src/flutter/ci/licenses_golden/licenses_skia index 63ffe75d7de4a..622b0c51b1e12 100644 --- a/engine/src/flutter/ci/licenses_golden/licenses_skia +++ b/engine/src/flutter/ci/licenses_golden/licenses_skia @@ -1,4 +1,4 @@ -Signature: 6385fd7cd2f7be2e7ceee9d4b05eaf04 +Signature: 508e4fd642003d8487bf34df9a6b4ac2 ==================================================================================================== LIBRARY: etc1 diff --git a/engine/src/flutter/ci/tizen/.gitignore b/engine/src/flutter/ci/tizen/.gitignore new file mode 100644 index 0000000000000..c23e7e850155f --- /dev/null +++ b/engine/src/flutter/ci/tizen/.gitignore @@ -0,0 +1,3 @@ +/llvm-project*/ +/toolchains*/ +/sysroot/ diff --git a/engine/src/flutter/ci/tizen/Dockerfile b/engine/src/flutter/ci/tizen/Dockerfile new file mode 100644 index 0000000000000..f5d0f1fc5520f --- /dev/null +++ b/engine/src/flutter/ci/tizen/Dockerfile @@ -0,0 +1,32 @@ +############################### +### Stage for building LLVM ### +############################### + +FROM ubuntu:22.04 AS llvm + +ENV DEBIAN_FRONTEND=noninteractive + +RUN apt-get update && \ + apt-get install -y git zip build-essential cmake ninja-build clang-11 && \ + apt-get clean + +COPY build_llvm.sh . + +RUN /build_llvm.sh + + +############################## +### Create a release image ### +############################## + +FROM ubuntu:22.04 + +ENV DEBIAN_FRONTEND=noninteractive + +RUN apt-get update && \ + apt-get install -y binutils-arm-linux-gnueabi binutils-aarch64-linux-gnu binutils-i686-linux-gnu && \ + apt-get install -y git curl pkg-config ca-certificates python3 python3-pip rpm2cpio cpio && \ + apt-get clean + +# Copy build artifacts from the previous stage. +COPY --from=llvm /toolchains/ /tizen_tools/toolchains/ diff --git a/engine/src/flutter/ci/tizen/arm.patch b/engine/src/flutter/ci/tizen/arm.patch new file mode 100644 index 0000000000000..1dabb3f2803d4 --- /dev/null +++ b/engine/src/flutter/ci/tizen/arm.patch @@ -0,0 +1,15 @@ +diff --git a/usr/include/asm-arm/hwcap.h b/usr/include/asm-arm/hwcap.h +index da85060..adaf619 100644 +--- a/usr/include/asm-arm/hwcap.h ++++ b/usr/include/asm-arm/hwcap.h +@@ -26,5 +26,10 @@ + #define HWCAP_VFPD32 (1 << 19) /* set if VFP has 32 regs (not 16) */ + #define HWCAP_IDIV (HWCAP_IDIVA | HWCAP_IDIVT) + ++#define HWCAP2_AES (1 << 0) ++#define HWCAP2_PMULL (1 << 1) ++#define HWCAP2_SHA1 (1 << 2) ++#define HWCAP2_SHA2 (1 << 3) ++#define HWCAP2_CRC32 (1 << 4) + + #endif /* __ASMARM_HWCAP_H */ diff --git a/engine/src/flutter/ci/tizen/build_llvm.sh b/engine/src/flutter/ci/tizen/build_llvm.sh new file mode 100755 index 0000000000000..7d75d9c6a4252 --- /dev/null +++ b/engine/src/flutter/ci/tizen/build_llvm.sh @@ -0,0 +1,46 @@ +#!/bin/bash +# Copyright 2022 Samsung Electronics Co., Ltd. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +set -e + +SCRIPT_DIR="$(dirname "$(readlink -f "${BASH_SOURCE[0]}")")" +OUTPUT_DIR="$SCRIPT_DIR/toolchains" +cd "$SCRIPT_DIR" + +# Check out the LLVM project source code. +if [ -d llvm-project ]; then + echo "The directory already exists. Skipping download." + cd llvm-project +else + mkdir llvm-project + cd llvm-project + git init + git remote add origin https://github.com/llvm/llvm-project.git + git fetch --depth=1 origin llvmorg-18.1.8 + git checkout FETCH_HEAD +fi + +# Run the ninja build. +mkdir -p build && cd build +cmake -G Ninja \ + -DCLANG_VENDOR="Tizen" \ + -DLLVM_ENABLE_PROJECTS="clang" \ + -DLLVM_TARGETS_TO_BUILD="X86;ARM;AArch64" \ + -DCMAKE_C_COMPILER=clang-11 \ + -DCMAKE_CXX_FLAGS="-mssse3" \ + -DCMAKE_CXX_COMPILER=clang++-11 \ + -DCMAKE_BUILD_TYPE=Release \ + -DCMAKE_INSTALL_PREFIX="$OUTPUT_DIR" \ + ../llvm +ninja install -j$(nproc) + +# Create symbolic links to binutils. +# See build/toolchain/custom/BUILD.gn for more information. +cd "$OUTPUT_DIR/bin" +for name in ar readelf nm strip; do + ln -sf llvm-$name arm-linux-gnueabi-$name + ln -sf llvm-$name aarch64-linux-gnu-$name + ln -sf llvm-$name i686-linux-gnu-$name +done diff --git a/engine/src/flutter/ci/tizen/generate_sysroot.py b/engine/src/flutter/ci/tizen/generate_sysroot.py new file mode 100755 index 0000000000000..c4bdc51f347cf --- /dev/null +++ b/engine/src/flutter/ci/tizen/generate_sysroot.py @@ -0,0 +1,151 @@ +#!/usr/bin/env python3 +# Copyright 2022 Samsung Electronics Co., Ltd. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +import argparse +import os +import re +import shutil +import subprocess +import sys +import urllib.parse +import urllib.request +from pathlib import Path + +base_packages = [ + 'gcc', + 'glibc', + 'glibc-devel', + 'libgcc', + 'libstdc++', + 'linux-glibc-devel', + 'zlib-devel', +] + +unified_packages = [ + 'fontconfig', + 'fontconfig-devel', + 'freetype2-devel', + 'libpng-devel', +] + + +def generate_sysroot(sysroot: Path, api_version: float, arch: str, quiet=False): + if arch == 'arm': + tizen_arch = 'armv7l' + elif arch == 'arm64': + tizen_arch = 'aarch64' + elif arch == 'x86': + tizen_arch = 'i686' + else: + sys.exit('Unknown arch: ' + arch) + + base_repo = 'http://download.tizen.org/snapshots/TIZEN/Tizen-{}/Tizen-{}-Base/latest/repos/standard/packages'.format( + api_version, api_version + ) + unified_repo = 'http://download.tizen.org/snapshots/TIZEN/Tizen-{}/Tizen-{}-Unified/latest/repos/standard/packages'.format( + api_version, api_version + ) + + # Retrieve html documents. + documents = {} + for url in ['{}/{}'.format(base_repo, tizen_arch), '{}/{}'.format(base_repo, 'noarch'), + '{}/{}'.format(unified_repo, tizen_arch), '{}/{}'.format(unified_repo, 'noarch')]: + request = urllib.request.Request(url) + with urllib.request.urlopen(request) as response: + documents[url] = response.read().decode('utf-8') + + # Download packages. + download_path = sysroot / '.rpms' + download_path.mkdir(exist_ok=True) + existing_rpms = [f for f in download_path.iterdir() if f.suffix == '.rpm'] + + for package in base_packages + unified_packages: + quoted = urllib.parse.quote(package) + pattern = re.escape(quoted) + '-\\d+\\.[\\d_\\.]+-[\\d\\.]+\\..+\\.rpm' + + if any([re.match(pattern, rpm.name) for rpm in existing_rpms]): + continue + + for parent, doc in documents.items(): + match = re.search('.+?'.format(pattern), doc) + if match: + rpm_url = '{}/{}'.format(parent, match.group(1)) + break + + if match: + if not quiet: + print('Downloading ' + rpm_url) + urllib.request.urlretrieve(rpm_url, download_path / match.group(1)) + else: + sys.exit('Could not find a package named ' + package) + + # Extract files. + for rpm in [f for f in download_path.iterdir() if f.suffix == '.rpm']: + command = 'rpm2cpio {} | cpio -idum --quiet'.format(rpm) + subprocess.run(command, shell=True, cwd=sysroot, check=True) + + # Create symbolic links. + asm = sysroot / 'usr' / 'include' / 'asm' + if not asm.exists(): + os.symlink('asm-' + arch, asm) + pkgconfig = sysroot / 'usr' / 'lib' / 'pkgconfig' + if arch == 'arm64' and not pkgconfig.exists(): + os.symlink('../lib64/pkgconfig', pkgconfig) + + # Copy objects required by the linker, such as crtbeginS.o and libgcc.a. + if arch == 'arm64': + libpath = sysroot / 'usr' / 'lib64' + else: + libpath = sysroot / 'usr' / 'lib' + subprocess.run('cp gcc/*/*/*.o gcc/*/*/*.a .', shell=True, cwd=libpath, check=True) + + # Apply a patch if applicable. + patch = Path(__file__).parent / '{}.patch'.format(arch) + if patch.is_file(): + command = 'patch -p1 -s -d {} < {}'.format(sysroot, patch) + subprocess.run(command, shell=True, check=True) + + +def main(): + # Check dependencies. + for dep in ['rpm2cpio', 'cpio', 'git']: + if not shutil.which(dep): + sys.exit('{} is not installed. To install, run:\n' + ' sudo apt install {}'.format(dep, dep)) + + # Parse arguments. + parser = argparse.ArgumentParser(description='Tizen sysroot generator') + parser.add_argument('-o', '--out', metavar='PATH', type=str, help='Path to the output directory') + parser.add_argument('-f', '--force', action='store_true', help='Force re-downloading of packages') + parser.add_argument('-q', '--quiet', action='store_true', help='Suppress log output') + parser.add_argument( + '--api-version', + metavar='VER', + default=6.0, + type=float, + help='Target API version (defaults to 6.0)' + ) + args = parser.parse_args() + + if args.out: + outpath = Path(args.out) + else: + outpath = Path(__file__).parent / 'sysroot' + outpath.mkdir(exist_ok=True) + + for arch in ['arm', 'arm64', 'x86']: + sysroot = outpath / arch + if args.force and sysroot.is_dir(): + shutil.rmtree(sysroot) + sysroot.mkdir(exist_ok=True) + + if not args.quiet: + print('Generating sysroot for {}...'.format(arch)) + generate_sysroot(sysroot.resolve(), args.api_version, arch, args.quiet) + + +# Execute only if run as a script. +if __name__ == '__main__': + main() diff --git a/engine/src/flutter/common/config.gni b/engine/src/flutter/common/config.gni index 318d305bd37fc..f3b3c15534a2f 100644 --- a/engine/src/flutter/common/config.gni +++ b/engine/src/flutter/common/config.gni @@ -157,6 +157,4 @@ if (flutter_prebuilt_dart_sdk) { # TODO: We can't build the engine artifacts for arm (32-bit) right now; # see https://github.com/flutter/flutter/issues/74322. build_engine_artifacts = - flutter_build_engine_artifacts && !is_android && - (current_toolchain == host_toolchain || - (is_linux && !is_chromeos && current_cpu != "arm") || is_mac || is_win) + flutter_build_engine_artifacts && current_toolchain == host_toolchain diff --git a/engine/src/flutter/display_list/testing/BUILD.gn b/engine/src/flutter/display_list/testing/BUILD.gn index 8e1a0536126fd..37390f8aad457 100644 --- a/engine/src/flutter/display_list/testing/BUILD.gn +++ b/engine/src/flutter/display_list/testing/BUILD.gn @@ -31,7 +31,8 @@ surface_provider_include_software = !is_android && !is_ios # But, since benchmarks do not run on Windows and rendertests only # runs on SW by default, this restriction currently only limits the # ability to manually cross-check OpenGL on Windows for rendertests -surface_provider_include_gl = !is_fuchsia && !is_ios && !is_win && !is_mac +surface_provider_include_gl = + !is_fuchsia && !is_ios && !is_win && !is_mac && !is_linux # TODO (https://github.com/flutter/flutter/issues/107357): # impeller_enable_vulkan currently requires skia to not use VMA, which in turn diff --git a/engine/src/flutter/impeller/golden_tests/golden_playground_test_mac.cc b/engine/src/flutter/impeller/golden_tests/golden_playground_test_mac.cc index 12b5103283688..fd8445dbe2811 100644 --- a/engine/src/flutter/impeller/golden_tests/golden_playground_test_mac.cc +++ b/engine/src/flutter/impeller/golden_tests/golden_playground_test_mac.cc @@ -132,6 +132,11 @@ void GoldenPlaygroundTest::SetTypographerContext( void GoldenPlaygroundTest::TearDown() { ASSERT_FALSE(dlopen("/usr/local/lib/libMoltenVK.dylib", RTLD_NOLOAD)); + + auto context = GetContext(); + if (context) { + context->DisposeThreadLocalCachedResources(); + } } namespace { @@ -274,6 +279,9 @@ RuntimeStage::Map GoldenPlaygroundTest::OpenAssetAsRuntimeStage( } std::shared_ptr GoldenPlaygroundTest::GetContext() const { + if (!pimpl_->screenshotter) { + return nullptr; + } return pimpl_->screenshotter->GetPlayground().GetContext(); } diff --git a/engine/src/flutter/impeller/renderer/backend/vulkan/command_pool_vk.cc b/engine/src/flutter/impeller/renderer/backend/vulkan/command_pool_vk.cc index d6176b2c3b808..dfa80c252b486 100644 --- a/engine/src/flutter/impeller/renderer/backend/vulkan/command_pool_vk.cc +++ b/engine/src/flutter/impeller/renderer/backend/vulkan/command_pool_vk.cc @@ -171,10 +171,23 @@ static thread_local std::unique_ptr tls_command_pool_map; // with that context. static Mutex g_all_pools_map_mutex; static std::unordered_map< - const ContextVK*, - std::vector>> g_all_pools_map + uint64_t, + std::unordered_map>> g_all_pools_map IPLR_GUARDED_BY(g_all_pools_map_mutex); +CommandPoolRecyclerVK::CommandPoolRecyclerVK( + const std::shared_ptr& context) + : context_(context), context_hash_(context->GetHash()) {} + +// Visible for testing. +// Returns the number of pools in g_all_pools_map for the given context. +int CommandPoolRecyclerVK::GetGlobalPoolCount(const ContextVK& context) { + Lock all_pools_lock(g_all_pools_map_mutex); + auto it = g_all_pools_map.find(context.GetHash()); + return it != g_all_pools_map.end() ? it->second.size() : 0; +} + // TODO(matanlurey): Return a status_or<> instead of nullptr when we have one. std::shared_ptr CommandPoolRecyclerVK::Get() { auto const strong_context = context_.lock(); @@ -187,8 +200,7 @@ std::shared_ptr CommandPoolRecyclerVK::Get() { tls_command_pool_map.reset(new CommandPoolMap()); } CommandPoolMap& pool_map = *tls_command_pool_map.get(); - auto const hash = strong_context->GetHash(); - auto const it = pool_map.find(hash); + auto const it = pool_map.find(context_hash_); if (it != pool_map.end()) { return it->second; } @@ -201,11 +213,11 @@ std::shared_ptr CommandPoolRecyclerVK::Get() { auto const resource = std::make_shared( std::move(data->pool), std::move(data->buffers), context_); - pool_map.emplace(hash, resource); + pool_map.emplace(context_hash_, resource); { Lock all_pools_lock(g_all_pools_map_mutex); - g_all_pools_map[strong_context.get()].push_back(resource); + g_all_pools_map[context_hash_][std::this_thread::get_id()] = resource; } return resource; @@ -275,30 +287,33 @@ void CommandPoolRecyclerVK::Reclaim( RecycledData{.pool = std::move(pool), .buffers = std::move(buffers)}); } -CommandPoolRecyclerVK::~CommandPoolRecyclerVK() { - // Ensure all recycled pools are reclaimed before this is destroyed. - Dispose(); -} - void CommandPoolRecyclerVK::Dispose() { CommandPoolMap* pool_map = tls_command_pool_map.get(); if (pool_map) { - pool_map->clear(); + pool_map->erase(context_hash_); + } + + { + Lock all_pools_lock(g_all_pools_map_mutex); + auto found = g_all_pools_map.find(context_hash_); + if (found != g_all_pools_map.end()) { + found->second.erase(std::this_thread::get_id()); + } } } -void CommandPoolRecyclerVK::DestroyThreadLocalPools(const ContextVK* context) { +void CommandPoolRecyclerVK::DestroyThreadLocalPools() { // Delete the context's entry in this thread's command pool map. if (tls_command_pool_map.get()) { - tls_command_pool_map.get()->erase(context->GetHash()); + tls_command_pool_map.get()->erase(context_hash_); } // Destroy all other thread-local CommandPoolVK instances associated with // this context. Lock all_pools_lock(g_all_pools_map_mutex); - auto found = g_all_pools_map.find(context); + auto found = g_all_pools_map.find(context_hash_); if (found != g_all_pools_map.end()) { - for (auto& weak_pool : found->second) { + for (auto& [thread_id, weak_pool] : found->second) { auto pool = weak_pool.lock(); if (!pool) { continue; diff --git a/engine/src/flutter/impeller/renderer/backend/vulkan/command_pool_vk.h b/engine/src/flutter/impeller/renderer/backend/vulkan/command_pool_vk.h index 30f2b3c3688ae..1fc9d06b32fb4 100644 --- a/engine/src/flutter/impeller/renderer/backend/vulkan/command_pool_vk.h +++ b/engine/src/flutter/impeller/renderer/backend/vulkan/command_pool_vk.h @@ -103,8 +103,6 @@ class CommandPoolVK final { class CommandPoolRecyclerVK final : public std::enable_shared_from_this { public: - ~CommandPoolRecyclerVK(); - /// A unique command pool and zero or more recycled command buffers. struct RecycledData { vk::UniqueCommandPool pool; @@ -112,16 +110,13 @@ class CommandPoolRecyclerVK final }; /// @brief Clean up resources held by all per-thread command pools - /// associated with the given context. - /// - /// @param[in] context The context. - static void DestroyThreadLocalPools(const ContextVK* context); + /// associated with the context. + void DestroyThreadLocalPools(); /// @brief Creates a recycler for the given |ContextVK|. /// /// @param[in] context The context to create the recycler for. - explicit CommandPoolRecyclerVK(std::weak_ptr context) - : context_(std::move(context)) {} + explicit CommandPoolRecyclerVK(const std::shared_ptr& context); /// @brief Gets a command pool for the current thread. /// @@ -137,11 +132,15 @@ class CommandPoolRecyclerVK final std::vector&& buffers, bool should_trim = false); - /// @brief Clears all recycled command pools to let them be reclaimed. + /// @brief Clears this context's thread-local command pool. void Dispose(); + // Visible for testing. + static int GetGlobalPoolCount(const ContextVK& context); + private: std::weak_ptr context_; + uint64_t context_hash_; Mutex recycled_mutex_; std::vector recycled_ IPLR_GUARDED_BY(recycled_mutex_); diff --git a/engine/src/flutter/impeller/renderer/backend/vulkan/command_pool_vk_unittests.cc b/engine/src/flutter/impeller/renderer/backend/vulkan/command_pool_vk_unittests.cc index 42da1b896e50f..69b4e06c4ea6c 100644 --- a/engine/src/flutter/impeller/renderer/backend/vulkan/command_pool_vk_unittests.cc +++ b/engine/src/flutter/impeller/renderer/backend/vulkan/command_pool_vk_unittests.cc @@ -228,5 +228,24 @@ TEST(CommandPoolRecyclerVKTest, ExtraCommandBufferAllocationsTriggerTrim) { context->Shutdown(); } +TEST(CommandPoolRecyclerVKTest, RecyclerGlobalPoolMapSize) { + auto context = MockVulkanContextBuilder().Build(); + auto const recycler = context->GetCommandPoolRecycler(); + + // The global pool list for this context should initially be empty. + EXPECT_EQ(CommandPoolRecyclerVK::GetGlobalPoolCount(*context), 0); + + // Creating a pool for this thread should insert the pool into the global map. + auto pool = recycler->Get(); + EXPECT_EQ(CommandPoolRecyclerVK::GetGlobalPoolCount(*context), 1); + + // Disposing this thread's pool should remove it from the global map. + pool.reset(); + recycler->Dispose(); + EXPECT_EQ(CommandPoolRecyclerVK::GetGlobalPoolCount(*context), 0); + + context->Shutdown(); +} + } // namespace testing } // namespace impeller diff --git a/engine/src/flutter/impeller/renderer/backend/vulkan/context_vk.cc b/engine/src/flutter/impeller/renderer/backend/vulkan/context_vk.cc index 6066065ca2b34..a9a42ad35a560 100644 --- a/engine/src/flutter/impeller/renderer/backend/vulkan/context_vk.cc +++ b/engine/src/flutter/impeller/renderer/backend/vulkan/context_vk.cc @@ -134,7 +134,9 @@ ContextVK::~ContextVK() { if (device_holder_ && device_holder_->device) { [[maybe_unused]] auto result = device_holder_->device->waitIdle(); } - CommandPoolRecyclerVK::DestroyThreadLocalPools(this); + if (command_pool_recycler_) { + command_pool_recycler_->DestroyThreadLocalPools(); + } } Context::BackendType ContextVK::GetBackendType() const { @@ -421,7 +423,7 @@ void ContextVK::Setup(Settings settings) { } auto command_pool_recycler = - std::make_shared(weak_from_this()); + std::make_shared(shared_from_this()); if (!command_pool_recycler) { VALIDATION_LOG << "Could not create command pool recycler."; return; diff --git a/engine/src/flutter/impeller/tools/args.gni b/engine/src/flutter/impeller/tools/args.gni index adfc111ffce15..1b8d30d018b3d 100644 --- a/engine/src/flutter/impeller/tools/args.gni +++ b/engine/src/flutter/impeller/tools/args.gni @@ -17,7 +17,7 @@ declare_args() { enable_unittests) && target_os != "fuchsia" # Whether the Vulkan backend is enabled. - impeller_enable_vulkan = (is_linux || is_win || is_android || is_mac || + impeller_enable_vulkan = (is_win || is_android || is_mac || enable_unittests) && target_os != "fuchsia" } diff --git a/engine/src/flutter/impeller/tools/shaders.gni b/engine/src/flutter/impeller/tools/shaders.gni index 140b886552de4..3fbdbb1301342 100644 --- a/engine/src/flutter/impeller/tools/shaders.gni +++ b/engine/src/flutter/impeller/tools/shaders.gni @@ -111,22 +111,6 @@ template("impeller_shaders") { } analyze = analyze } - - gles3_shaders = "gles3_$target_name" - - impeller_shaders_gles(gles3_shaders) { - name = invoker.name - require_framebuffer_fetch = require_framebuffer_fetch - gles_language_version = 300 - is_300 = true - analyze = false - if (defined(invoker.gles_exclusions)) { - shaders = invoker.shaders - invoker.gles_exclusions - } else { - shaders = invoker.shaders - } - analyze = analyze - } } if (impeller_enable_vulkan) { @@ -156,10 +140,7 @@ template("impeller_shaders") { } if (enable_opengles) { - public_deps += [ - ":$gles3_shaders", - ":$gles_shaders", - ] + public_deps += [ ":$gles_shaders" ] } if (impeller_enable_vulkan) { diff --git a/engine/src/flutter/shell/config.gni b/engine/src/flutter/shell/config.gni index 3f498d2c044f7..9ad466439ee2e 100644 --- a/engine/src/flutter/shell/config.gni +++ b/engine/src/flutter/shell/config.gni @@ -17,6 +17,6 @@ declare_args() { test_enable_metal = shell_enable_metal # The Vulkan unittests are combined with the GL unittests. - test_enable_vulkan = is_fuchsia || shell_enable_gl + test_enable_vulkan = is_fuchsia test_enable_software = shell_enable_software } diff --git a/engine/src/flutter/shell/platform/embedder/embedder.h b/engine/src/flutter/shell/platform/embedder/embedder.h index 07f48914d223e..26d7d2b70c76c 100644 --- a/engine/src/flutter/shell/platform/embedder/embedder.h +++ b/engine/src/flutter/shell/platform/embedder/embedder.h @@ -309,6 +309,7 @@ typedef struct { } FlutterTransformation; typedef void (*VoidCallback)(void* /* user data */); +typedef bool (*BoolCallback)(void* /* user data */); typedef enum { /// Specifies an OpenGL texture target type. Textures are specified using @@ -416,6 +417,13 @@ typedef struct { uint32_t name; /// The texture format (example GL_RGBA8). uint32_t format; + /// The pixel data buffer. + const uint8_t* buffer; + /// The size of pixel buffer. + size_t buffer_size; + /// Callback invoked that the gpu surface texture start binding. + BoolCallback bind_callback; + /// User data to be returned on the invocation of the destruction callback. void* user_data; /// Callback invoked (on an engine managed thread) that asks the embedder to @@ -509,7 +517,6 @@ typedef struct { uint32_t format; } FlutterOpenGLSurface; -typedef bool (*BoolCallback)(void* /* user data */); typedef FlutterTransformation (*TransformationCallback)(void* /* user data */); typedef uint32_t (*UIntCallback)(void* /* user data */); typedef bool (*SoftwareSurfacePresentCallback)(void* /* user data */, diff --git a/engine/src/flutter/shell/platform/embedder/embedder_external_texture_gl.cc b/engine/src/flutter/shell/platform/embedder/embedder_external_texture_gl.cc index fe81731aff0d5..1a627ec864516 100644 --- a/engine/src/flutter/shell/platform/embedder/embedder_external_texture_gl.cc +++ b/engine/src/flutter/shell/platform/embedder/embedder_external_texture_gl.cc @@ -138,15 +138,73 @@ sk_sp EmbedderExternalTextureGL::ResolveTextureImpeller( return nullptr; } + if (texture->bind_callback != nullptr) { + return ResolveTextureImpellerSurface(aiks_context, std::move(texture)); + } else { + return ResolveTextureImpellerPixelbuffer(aiks_context, std::move(texture)); + } +} + +sk_sp EmbedderExternalTextureGL::ResolveTextureImpellerPixelbuffer( + impeller::AiksContext* aiks_context, + std::unique_ptr texture) { impeller::TextureDescriptor desc; desc.size = impeller::ISize(texture->width, texture->height); + desc.type = impeller::TextureType::kTexture2D; + desc.storage_mode = impeller::StorageMode::kDevicePrivate; + desc.format = impeller::PixelFormat::kR8G8B8A8UNormInt; + impeller::ContextGLES& context = + impeller::ContextGLES::Cast(*aiks_context->GetContext()); + std::shared_ptr image = + std::make_shared(context.GetReactor(), desc); + + image->MarkContentsInitialized(); + if (!image->SetContents(texture->buffer, texture->buffer_size)) { + if (texture->destruction_callback) { + texture->destruction_callback(texture->user_data); + } + return nullptr; + } + if (!image) { + // In case Skia rejects the image, call the release proc so that + // embedders can perform collection of intermediates. + if (texture->destruction_callback) { + texture->destruction_callback(texture->user_data); + } + FML_LOG(ERROR) << "Could not create external texture"; + return nullptr; + } + + if (texture->destruction_callback) { + texture->destruction_callback(texture->user_data); + } + + return impeller::DlImageImpeller::Make(image); +} + +sk_sp EmbedderExternalTextureGL::ResolveTextureImpellerSurface( + impeller::AiksContext* aiks_context, + std::unique_ptr texture) { + impeller::TextureDescriptor desc; + desc.size = impeller::ISize(texture->width, texture->height); + desc.storage_mode = impeller::StorageMode::kDevicePrivate; + desc.format = impeller::PixelFormat::kR8G8B8A8UNormInt; + desc.type = impeller::TextureType::kTextureExternalOES; impeller::ContextGLES& context = impeller::ContextGLES::Cast(*aiks_context->GetContext()); - impeller::HandleGLES handle = context.GetReactor()->CreateHandle( - impeller::HandleType::kTexture, texture->target); std::shared_ptr image = - impeller::TextureGLES::WrapTexture(context.GetReactor(), desc, handle); + std::make_shared(context.GetReactor(), desc); + image->MarkContentsInitialized(); + image->SetCoordinateSystem( + impeller::TextureCoordinateSystem::kUploadFromHost); + if (!image->Bind()) { + if (texture->destruction_callback) { + texture->destruction_callback(texture->user_data); + } + FML_LOG(ERROR) << "Could not bind texture"; + return nullptr; + } if (!image) { // In case Skia rejects the image, call the release proc so that @@ -157,15 +215,18 @@ sk_sp EmbedderExternalTextureGL::ResolveTextureImpeller( FML_LOG(ERROR) << "Could not create external texture"; return nullptr; } - if (texture->destruction_callback && - !context.GetReactor()->RegisterCleanupCallback( - handle, - [callback = texture->destruction_callback, - user_data = texture->user_data]() { callback(user_data); })) { - FML_LOG(ERROR) << "Could not register destruction callback"; + + if (!texture->bind_callback(texture->user_data)) { + if (texture->destruction_callback) { + texture->destruction_callback(texture->user_data); + } return nullptr; } + if (texture->destruction_callback) { + texture->destruction_callback(texture->user_data); + } + return impeller::DlImageImpeller::Make(image); } diff --git a/engine/src/flutter/shell/platform/embedder/embedder_external_texture_gl.h b/engine/src/flutter/shell/platform/embedder/embedder_external_texture_gl.h index 7dbc631d56ad1..6ecb6843208d1 100644 --- a/engine/src/flutter/shell/platform/embedder/embedder_external_texture_gl.h +++ b/engine/src/flutter/shell/platform/embedder/embedder_external_texture_gl.h @@ -39,6 +39,14 @@ class EmbedderExternalTextureGL : public flutter::Texture { impeller::AiksContext* aiks_context, const SkISize& size); + sk_sp ResolveTextureImpellerPixelbuffer( + impeller::AiksContext* aiks_context, + std::unique_ptr texture); + + sk_sp ResolveTextureImpellerSurface( + impeller::AiksContext* aiks_context, + std::unique_ptr texture); + // |flutter::Texture| void Paint(PaintContext& context, const DlRect& bounds, diff --git a/engine/src/flutter/testing/BUILD.gn b/engine/src/flutter/testing/BUILD.gn index 1a21ac33669be..48b902554e139 100644 --- a/engine/src/flutter/testing/BUILD.gn +++ b/engine/src/flutter/testing/BUILD.gn @@ -53,7 +53,7 @@ source_set("testing") { if (enable_unittests && is_linux) { # So that we can call gtk_init in main(). - configs += [ "//flutter/shell/platform/linux/config:gtk" ] + # configs += [ "//flutter/shell/platform/linux/config:gtk" ] } public_deps = [ ":testing_lib" ] diff --git a/engine/src/flutter/tools/gn b/engine/src/flutter/tools/gn index df1dbec1e9a6b..6e3ec29a2199d 100755 --- a/engine/src/flutter/tools/gn +++ b/engine/src/flutter/tools/gn @@ -89,18 +89,7 @@ def to_command_line(gn_args): def is_host_build(args): - # If target_os == None, then this is a host build. - if args.target_os is None: - return True - # For linux arm64 builds, we cross compile from x64 hosts, so the - # target_os='linux' and linux-cpu='arm64' - if args.target_os == 'linux' and args.linux_cpu == 'arm64': - return True - # The Mac and host targets are redundant. Again, necessary to disambiguate - # during cross-compilation. - if args.target_os == 'mac': - return True - return False + return args.target_os is None # Determines whether a prebuilt Dart SDK can be used instead of building one. @@ -502,7 +491,7 @@ def to_gn_args(args): # does not exist. Further, we set the 'host_cpu' so that it shares the # bitwidth of the 32-bit arm target. if sys.platform.startswith( - ('cygwin', 'win')) and args.target_os == 'android' and gn_args['target_cpu'] == 'arm': + ('cygwin', 'win')) and args.target_os != 'win' and gn_args['target_cpu'] == 'arm': gn_args['host_cpu'] = 'x86' gn_args['current_cpu'] = 'x86' @@ -599,7 +588,7 @@ def to_gn_args(args): else: gn_args['skia_use_gl'] = args.target_os != 'fuchsia' - if sys.platform == 'darwin' and args.target_os not in ['android', 'fuchsia']: + if sys.platform == 'darwin' and args.target_os not in ['android', 'fuchsia', 'linux']: # OpenGL is deprecated on macOS > 10.11. # This is not necessarily needed but enabling this until we have a way to # build a macOS metal only shell and a gl only shell. @@ -610,7 +599,7 @@ def to_gn_args(args): # Enable Vulkan on all platforms except for iOS. This is just # to save on mobile binary size, as there's no reason the Vulkan embedder # features can't work on these platforms. - if gn_args['target_os'] not in ['ios', 'mac']: + if gn_args['target_os'] not in ['ios', 'mac', 'linux']: gn_args['skia_use_vulkan'] = True gn_args['skia_use_vma'] = False gn_args['shell_enable_vulkan'] = True @@ -711,13 +700,17 @@ def to_gn_args(args): # Enable pointer compression on 64-bit mobile targets. iOS is excluded due to # its inability to allocate address space without allocating memory. - if args.target_os in ['android'] and gn_args['target_cpu'] in ['x64', 'arm64']: + if args.target_os in ['android', 'linux'] and gn_args['target_cpu'] in ['x64', 'arm64']: gn_args['dart_use_compressed_pointers'] = True if args.target_os == 'fuchsia': gn_args['gn_configs_path'] = '//flutter/build/config/fuchsia/gn_configs.gni' gn_args['fuchsia_gn_sdk'] = '//flutter/tools/fuchsia/gn-sdk' + # Don't use the default Linux sysroot when buliding for Linux on macOS. + if sys.platform == 'darwin' and args.target_os == 'linux': + gn_args['use_default_linux_sysroot'] = False + # Flags for Dart features: if args.use_mallinfo2: gn_args['dart_use_mallinfo2'] = args.use_mallinfo2 @@ -743,9 +736,8 @@ def to_gn_args(args): # gen_snapshot, but the build defines otherwise make it look like the build is # for a host Windows build and make GN think we will be building ANGLE. # Angle is not used on Mac hosts as there are no tests for the OpenGL backend. - if is_host_build(args) or (args.target_os == 'android' and get_host_os() == 'win'): - # Don't include git commit information. - gn_args['angle_enable_commit_id'] = False + if (is_host_build(args) and gn_args['host_os'] != 'mac') or (args.target_os == 'linux' and + get_host_os() == 'win'): # Do not build unnecessary parts of the ANGLE tree. gn_args['angle_build_all'] = False gn_args['angle_has_astc_encoder'] = False diff --git a/engine/src/flutter/txt/src/txt/platform_linux.cc b/engine/src/flutter/txt/src/txt/platform_linux.cc index e542d82e50477..add93f0836c9f 100644 --- a/engine/src/flutter/txt/src/txt/platform_linux.cc +++ b/engine/src/flutter/txt/src/txt/platform_linux.cc @@ -19,7 +19,7 @@ namespace txt { std::vector GetDefaultFontFamilies() { - return {"Ubuntu", "Cantarell", "DejaVu Sans", "Liberation Sans", "Arial"}; + return {"TizenDefaultFont", "SamsungOneUI"}; } sk_sp GetDefaultFontManager(uint32_t font_initialization_data) { diff --git a/packages/flutter/lib/src/cupertino/app.dart b/packages/flutter/lib/src/cupertino/app.dart index fc13d4e99ae7b..1deda1f9dd9bf 100644 --- a/packages/flutter/lib/src/cupertino/app.dart +++ b/packages/flutter/lib/src/cupertino/app.dart @@ -568,9 +568,9 @@ class _CupertinoAppState extends State { return _CupertinoInspectorButton.toggle( onPressed: onPressed, semanticLabel: semanticLabel, - // This icon is also used for the Material-styled button and for DevTools. - // It should be updated in all 3 places if changed. - icon: CupertinoIcons.cursor_rays, + // This unicode icon is also used for the Material-styled button and for + // DevTools. It should be updated in all 3 places if changed. + icon: const IconData(0x1F74A), toggledOn: selectionOnTapEnabled, ); } diff --git a/packages/flutter/lib/src/material/app.dart b/packages/flutter/lib/src/material/app.dart index d2083c0353946..7c3f9f61bce52 100644 --- a/packages/flutter/lib/src/material/app.dart +++ b/packages/flutter/lib/src/material/app.dart @@ -971,9 +971,9 @@ class _MaterialAppState extends State { return _MaterialInspectorButton.toggle( onPressed: onPressed, semanticLabel: semanticLabel, - // This icon is also used for the Cupertino-styled button and for DevTools. - // It should be updated in all 3 places if changed. - icon: CupertinoIcons.cursor_rays, + // This unicode icon is also used for the Cupertino-styled button and for + // DevTools. It should be updated in all 3 places if changed. + icon: const IconData(0x1F74A), isDarkTheme: _isDarkTheme(context), toggledOn: selectionOnTapEnabled, ); diff --git a/packages/flutter/lib/src/material/navigation_bar.dart b/packages/flutter/lib/src/material/navigation_bar.dart index 83217782071fd..9465e7ef96f37 100644 --- a/packages/flutter/lib/src/material/navigation_bar.dart +++ b/packages/flutter/lib/src/material/navigation_bar.dart @@ -16,7 +16,6 @@ import 'package:flutter/widgets.dart'; import 'color_scheme.dart'; import 'colors.dart'; import 'elevation_overlay.dart'; -import 'ink_decoration.dart'; import 'ink_well.dart'; import 'material.dart'; import 'material_localizations.dart'; @@ -869,7 +868,7 @@ class NavigationIndicator extends StatelessWidget { builder: (BuildContext context, Animation fadeAnimation) { return FadeTransition( opacity: fadeAnimation, - child: Ink( + child: Container( width: width, height: height, decoration: ShapeDecoration( diff --git a/packages/flutter/lib/src/material/tabs.dart b/packages/flutter/lib/src/material/tabs.dart index 084b3b0a7614b..c85ef0caf7748 100644 --- a/packages/flutter/lib/src/material/tabs.dart +++ b/packages/flutter/lib/src/material/tabs.dart @@ -1976,6 +1976,7 @@ class _TabBarState extends State { ), ), ); + wrappedTabs[index] = MergeSemantics(child: wrappedTabs[index]); if (!widget.isScrollable && effectiveTabAlignment == TabAlignment.fill) { wrappedTabs[index] = Expanded(child: wrappedTabs[index]); } diff --git a/packages/flutter/lib/src/semantics/semantics.dart b/packages/flutter/lib/src/semantics/semantics.dart index f4b5e0fe2093b..c8fedd4b4b18a 100644 --- a/packages/flutter/lib/src/semantics/semantics.dart +++ b/packages/flutter/lib/src/semantics/semantics.dart @@ -4300,6 +4300,9 @@ class SemanticsOwner extends ChangeNotifier { return null; } if (node.mergeAllDescendantsIntoThisNode) { + if (node._canPerformAction(action)) { + return node._actions[action]; + } SemanticsNode? result; node._visitDescendants((SemanticsNode child) { if (child._canPerformAction(action)) { diff --git a/packages/flutter/test/material/navigation_bar_test.dart b/packages/flutter/test/material/navigation_bar_test.dart index f7d16d5f56fd4..121c1ad8b0c68 100644 --- a/packages/flutter/test/material/navigation_bar_test.dart +++ b/packages/flutter/test/material/navigation_bar_test.dart @@ -1716,8 +1716,8 @@ Material _getMaterial(WidgetTester tester) { ShapeDecoration? _getIndicatorDecoration(WidgetTester tester) { return tester - .firstWidget( - find.descendant(of: find.byType(FadeTransition), matching: find.byType(Ink)), + .firstWidget( + find.descendant(of: find.byType(FadeTransition), matching: find.byType(Container)), ) .decoration as ShapeDecoration?; diff --git a/packages/flutter/test/material/navigation_bar_theme_test.dart b/packages/flutter/test/material/navigation_bar_theme_test.dart index e47a3ff752f53..9784a929aa071 100644 --- a/packages/flutter/test/material/navigation_bar_theme_test.dart +++ b/packages/flutter/test/material/navigation_bar_theme_test.dart @@ -367,8 +367,8 @@ Material _barMaterial(WidgetTester tester) { ShapeDecoration? _indicator(WidgetTester tester) { return tester - .firstWidget( - find.descendant(of: find.byType(FadeTransition), matching: find.byType(Ink)), + .firstWidget( + find.descendant(of: find.byType(FadeTransition), matching: find.byType(Container)), ) .decoration as ShapeDecoration?; diff --git a/packages/flutter/test/material/navigation_drawer_test.dart b/packages/flutter/test/material/navigation_drawer_test.dart index fde25e676a604..78b67436a51f5 100644 --- a/packages/flutter/test/material/navigation_drawer_test.dart +++ b/packages/flutter/test/material/navigation_drawer_test.dart @@ -123,16 +123,14 @@ void main() { await tester.tap(find.text('AC')); await tester.pump(); - // When no background color is set, only the non-visible indicator Ink is expected. - expect(findDestinationInk('AC'), findsOne); + expect(findDestinationInk('AC'), findsNothing); // Destination with a custom background color. await tester.tap(find.byIcon(Icons.access_alarm)); await tester.pump(); // A Material is added with the custom color. - expect(findDestinationInk('Alarm'), findsNWidgets(2)); - // The drawer destination Ink is the first one, the second is the indicator's one. + expect(findDestinationInk('Alarm'), findsOne); final BoxDecoration destinationDecoration = tester.firstWidget(findDestinationInk('Alarm')).decoration! as BoxDecoration; expect(destinationDecoration.color, color); @@ -511,8 +509,8 @@ InkWell? _getInkWell(WidgetTester tester) { ShapeDecoration? _getIndicatorDecoration(WidgetTester tester) { return tester - .firstWidget( - find.descendant(of: find.byType(FadeTransition), matching: find.byType(Ink)), + .firstWidget( + find.descendant(of: find.byType(FadeTransition), matching: find.byType(Container)), ) .decoration as ShapeDecoration?; diff --git a/packages/flutter/test/material/navigation_drawer_theme_test.dart b/packages/flutter/test/material/navigation_drawer_theme_test.dart index 4a30bc8cf3b76..9ad94a8e8f3f3 100644 --- a/packages/flutter/test/material/navigation_drawer_theme_test.dart +++ b/packages/flutter/test/material/navigation_drawer_theme_test.dart @@ -285,8 +285,8 @@ Material _getMaterial(WidgetTester tester) { ShapeDecoration? _getIndicatorDecoration(WidgetTester tester) { return tester - .firstWidget( - find.descendant(of: find.byType(FadeTransition), matching: find.byType(Ink)), + .firstWidget( + find.descendant(of: find.byType(FadeTransition), matching: find.byType(Container)), ) .decoration as ShapeDecoration?; diff --git a/packages/flutter/test/material/navigation_rail_test.dart b/packages/flutter/test/material/navigation_rail_test.dart index 5e0e10631a138..b60893aa3ac82 100644 --- a/packages/flutter/test/material/navigation_rail_test.dart +++ b/packages/flutter/test/material/navigation_rail_test.dart @@ -3076,7 +3076,6 @@ void main() { final RenderObject inkFeatures = tester.allRenderObjects.firstWhere( (RenderObject object) => object.runtimeType.toString() == '_RenderInkFeatures', ); - const Rect indicatorRect = Rect.fromLTRB(12.0, 0.0, 68.0, 32.0); const Rect includedRect = indicatorRect; final Rect excludedRect = includedRect.inflate(10); @@ -3102,7 +3101,7 @@ void main() { ) ..rect(rect: indicatorRect, color: const Color(0x0a6750a4)) ..rrect( - rrect: RRect.fromLTRBR(12.0, 0.0, 68.0, 32.0, const Radius.circular(16)), + rrect: RRect.fromLTRBR(12.0, 72.0, 68.0, 104.0, const Radius.circular(16)), color: const Color(0xffe8def8), ), ); @@ -3164,7 +3163,7 @@ void main() { ) ..rect(rect: indicatorRect, color: const Color(0x0a6750a4)) ..rrect( - rrect: RRect.fromLTRBR(12.0, 6.0, 68.0, 38.0, const Radius.circular(16)), + rrect: RRect.fromLTRBR(12.0, 58.0, 68.0, 90.0, const Radius.circular(16)), color: const Color(0xffe8def8), ), ); @@ -3230,7 +3229,7 @@ void main() { ) ..rect(rect: indicatorRect, color: const Color(0x0a6750a4)) ..rrect( - rrect: RRect.fromLTRBR(30.0, 24.0, 86.0, 56.0, const Radius.circular(16)), + rrect: RRect.fromLTRBR(30.0, 96.0, 86.0, 128.0, const Radius.circular(16)), color: const Color(0xffe8def8), ), ); @@ -3295,7 +3294,7 @@ void main() { ) ..rect(rect: indicatorRect, color: const Color(0x0a6750a4)) ..rrect( - rrect: RRect.fromLTRBR(0.0, 6.0, 50.0, 38.0, const Radius.circular(16)), + rrect: RRect.fromLTRBR(0.0, 58.0, 50.0, 90.0, const Radius.circular(16)), color: const Color(0xffe8def8), ), ); @@ -3362,7 +3361,7 @@ void main() { ) ..rect(rect: indicatorRect, color: const Color(0x0a6750a4)) ..rrect( - rrect: RRect.fromLTRBR(140.0, 24.0, 196.0, 56.0, const Radius.circular(16)), + rrect: RRect.fromLTRBR(140.0, 96.0, 196.0, 128.0, const Radius.circular(16)), color: const Color(0xffe8def8), ), ); @@ -3402,11 +3401,13 @@ void main() { ); // Default values from M3 specification. - const double railMinWidth = 80.0; const double indicatorHeight = 32.0; const double destinationWidth = 72.0; const double destinationHorizontalPadding = 8.0; const double indicatorWidth = destinationWidth - 2 * destinationHorizontalPadding; // 56.0 + const double verticalSpacer = 8.0; + const double verticalIconLabelSpacing = 4.0; + const double verticalDestinationSpacing = 12.0; // The navigation rail width is larger than default because of the first destination long label. final double railWidth = tester.getSize(find.byType(NavigationRail)).width; @@ -3417,7 +3418,13 @@ void main() { final Rect indicatorRect = Rect.fromLTRB(indicatorLeft, 0.0, indicatorRight, indicatorHeight); final Rect includedRect = indicatorRect; final Rect excludedRect = includedRect.inflate(10); - const double indicatorHorizontalPadding = (railMinWidth - indicatorWidth) / 2; // 12.0 + + // Compute the vertical position for the selected destination (the one with 'bookmark' icon). + const double labelHeight = 16; // fontSize is 12 and height is 1.3. + const double destinationHeight = + indicatorHeight + verticalIconLabelSpacing + labelHeight + verticalDestinationSpacing; + const double secondDestinationVerticalOffset = verticalSpacer + destinationHeight; + const double secondIndicatorVerticalOffset = secondDestinationVerticalOffset; expect( inkFeatures, @@ -3441,10 +3448,10 @@ void main() { ..rect(rect: indicatorRect, color: const Color(0x0a6750a4)) ..rrect( rrect: RRect.fromLTRBR( - indicatorHorizontalPadding, - 0.0, - indicatorHorizontalPadding + indicatorWidth, - indicatorHeight, + indicatorLeft, + secondIndicatorVerticalOffset, + indicatorRight, + secondIndicatorVerticalOffset + indicatorHeight, const Radius.circular(16), ), color: const Color(0xffe8def8), @@ -3497,6 +3504,9 @@ void main() { const double destinationWidth = 72.0; const double destinationHorizontalPadding = 8.0; const double indicatorWidth = destinationWidth - 2 * destinationHorizontalPadding; // 56.0 + const double verticalSpacer = 8.0; + const double verticalIconLabelSpacing = 4.0; + const double verticalDestinationSpacing = 12.0; // The navigation rail width is the default one because labels are short. final double railWidth = tester.getSize(find.byType(NavigationRail)).width; @@ -3516,8 +3526,13 @@ void main() { final Rect includedRect = indicatorRect; final Rect excludedRect = includedRect.inflate(10); - // Icon height is greater than indicator height so the indicator has a vertical offset. - const double secondIndicatorVerticalOffset = (iconSize - indicatorHeight) / 2; + // Compute the vertical position for the selected destination (the one with 'bookmark' icon). + const double labelHeight = 16; // fontSize is 12 and height is 1.3. + const double destinationHeight = + iconSize + verticalIconLabelSpacing + labelHeight + verticalDestinationSpacing; + const double secondDestinationVerticalOffset = verticalSpacer + destinationHeight; + const double indicatorOffset = (iconSize - indicatorHeight) / 2; + const double secondIndicatorVerticalOffset = secondDestinationVerticalOffset + indicatorOffset; expect( inkFeatures, @@ -3596,6 +3611,7 @@ void main() { const double destinationWidth = 72.0; const double destinationHorizontalPadding = 8.0; const double indicatorWidth = destinationWidth - 2 * destinationHorizontalPadding; // 56.0 + const double verticalSpacer = 8.0; const double verticalDestinationSpacingM3 = 12.0; // The navigation rail width is the default one because labels are short. @@ -3615,7 +3631,11 @@ void main() { final Rect excludedRect = includedRect.inflate(10); // Compute the vertical position for the selected destination (the one with 'bookmark' icon). - const double secondIndicatorVerticalOffset = verticalDestinationSpacingM3 / 2; + const double destinationHeight = indicatorHeight + verticalDestinationSpacingM3; + const double secondDestinationVerticalOffset = verticalSpacer + destinationHeight; + const double secondIndicatorVerticalOffset = + secondDestinationVerticalOffset + verticalDestinationSpacingM3 / 2; + const double secondDestinationHorizontalOffset = 800 - railMinExtendedWidth; // RTL. expect( inkFeatures, @@ -3641,9 +3661,9 @@ void main() { // Indicator for the selected destination (the one with 'bookmark' icon). ..rrect( rrect: RRect.fromLTRBR( - indicatorLeft, + secondDestinationHorizontalOffset + indicatorLeft, secondIndicatorVerticalOffset, - indicatorRight, + secondDestinationHorizontalOffset + indicatorRight, secondIndicatorVerticalOffset + indicatorHeight, const Radius.circular(16), ), @@ -6150,8 +6170,8 @@ Widget _buildWidget(Widget child, {bool useMaterial3 = true, bool isRTL = false} ShapeDecoration? _getIndicatorDecoration(WidgetTester tester) { return tester - .firstWidget( - find.descendant(of: find.byType(FadeTransition), matching: find.byType(Ink)), + .firstWidget( + find.descendant(of: find.byType(FadeTransition), matching: find.byType(Container)), ) .decoration as ShapeDecoration?; diff --git a/packages/flutter/test/material/navigation_rail_theme_test.dart b/packages/flutter/test/material/navigation_rail_theme_test.dart index 07236c9e63347..49581b8ba90b3 100644 --- a/packages/flutter/test/material/navigation_rail_theme_test.dart +++ b/packages/flutter/test/material/navigation_rail_theme_test.dart @@ -327,8 +327,8 @@ Material _railMaterial(WidgetTester tester) { ShapeDecoration? _indicatorDecoration(WidgetTester tester) { return tester - .firstWidget( - find.descendant(of: find.byType(NavigationIndicator), matching: find.byType(Ink)), + .firstWidget( + find.descendant(of: find.byType(NavigationIndicator), matching: find.byType(Container)), ) .decoration as ShapeDecoration?; diff --git a/packages/flutter/test/material/tabs_test.dart b/packages/flutter/test/material/tabs_test.dart index 0e71d0a9cac34..ba1bb8385d2b5 100644 --- a/packages/flutter/test/material/tabs_test.dart +++ b/packages/flutter/test/material/tabs_test.dart @@ -2,8 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import 'dart:ui'; - import 'package:flutter/foundation.dart'; import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; @@ -156,6 +154,22 @@ void main() { expect(tester.renderObject(find.byType(CustomPaint)).debugNeedsPaint, true); }); + testWidgets('tab semantics role test', (WidgetTester tester) async { + // Regressing test for https://github.com/flutter/flutter/issues/169175 + // Creates an image semantics node with zero size. + await tester.pumpWidget( + boilerplate( + child: DefaultTabController( + length: 1, + child: TabBar( + tabs: [Tab(icon: Semantics(image: true, child: const SizedBox.shrink()))], + ), + ), + ), + ); + expect(find.byType(Tab), findsOneWidget); + }); + testWidgets('Tab sizing - icon', (WidgetTester tester) async { await tester.pumpWidget( const MaterialApp( diff --git a/packages/flutter/test/semantics/semantics_test.dart b/packages/flutter/test/semantics/semantics_test.dart index f30ec100805c2..c5c103404f7db 100644 --- a/packages/flutter/test/semantics/semantics_test.dart +++ b/packages/flutter/test/semantics/semantics_test.dart @@ -918,6 +918,26 @@ void main() { expect(newNode.id, expectId); }); + test('performActionAt can hit test on merged semantics node', () { + bool tapped = false; + final SemanticsOwner owner = SemanticsOwner(onSemanticsUpdate: (SemanticsUpdate update) {}); + final SemanticsNode root = SemanticsNode.root(owner: owner) + ..rect = const Rect.fromLTRB(0.0, 0.0, 10.0, 10.0); + final SemanticsNode merged = SemanticsNode()..rect = const Rect.fromLTRB(0.0, 0.0, 10.0, 10.0); + final SemanticsConfiguration mergeConfig = + SemanticsConfiguration() + ..isSemanticBoundary = true + ..isMergingSemanticsOfDescendants = true + ..onTap = () => tapped = true; + final SemanticsConfiguration rootConfig = SemanticsConfiguration()..isSemanticBoundary = true; + + merged.updateWith(config: mergeConfig, childrenInInversePaintOrder: []); + root.updateWith(config: rootConfig, childrenInInversePaintOrder: [merged]); + + owner.performActionAt(const Offset(5, 5), SemanticsAction.tap); + expect(tapped, isTrue); + }); + test('Tags show up in debug properties', () { final SemanticsNode actionNode = SemanticsNode()..tags = {RenderViewport.useTwoPaneSemantics}; diff --git a/packages/flutter_tools/gradle/src/main/kotlin/FlutterPluginUtils.kt b/packages/flutter_tools/gradle/src/main/kotlin/FlutterPluginUtils.kt index 2b622591eb8ef..ff4b6c8b32090 100644 --- a/packages/flutter_tools/gradle/src/main/kotlin/FlutterPluginUtils.kt +++ b/packages/flutter_tools/gradle/src/main/kotlin/FlutterPluginUtils.kt @@ -517,8 +517,16 @@ object FlutterPluginUtils { getCompileSdkFromProject(project).toIntOrNull() ?: Int.MAX_VALUE var maxPluginCompileSdkVersion = projectCompileSdkVersion - val projectNdkVersion = - getAndroidExtension(project).ndkVersion + // TODO(gmackall): This should be updated to reflect newer templates. + // The default for AGP 4.1.0 used in old templates. + val ndkVersionIfUnspecified = "21.1.6352462" + + // TODO(gmackall): We can remove this elvis when our minimum AGP is >= 8.2. + // This value (ndkVersion) is nullable on AGP versions below that. + // See https://developer.android.com/reference/tools/gradle-api/8.1/com/android/build/api/dsl/CommonExtension#ndkVersion(). + @Suppress("USELESS_ELVIS") + val projectNdkVersion: String = + getAndroidExtension(project).ndkVersion ?: ndkVersionIfUnspecified var maxPluginNdkVersion = projectNdkVersion var numProcessedPlugins = pluginList.size val pluginsWithHigherSdkVersion = mutableListOf() @@ -543,8 +551,13 @@ object FlutterPluginUtils { ) ) } + + // TODO(gmackall): We can remove this elvis when our minimum AGP is >= 8.2. + // This value (ndkVersion) is nullable on AGP versions below that. + // See https://developer.android.com/reference/tools/gradle-api/8.1/com/android/build/api/dsl/CommonExtension#ndkVersion(). + @Suppress("USELESS_ELVIS") val pluginNdkVersion: String = - getAndroidExtension(pluginProject).ndkVersion + getAndroidExtension(pluginProject).ndkVersion ?: ndkVersionIfUnspecified maxPluginNdkVersion = VersionUtils.mostRecentSemanticVersion( pluginNdkVersion, diff --git a/packages/flutter_tools/lib/src/android/gradle.dart b/packages/flutter_tools/lib/src/android/gradle.dart index e0c644fd0cd22..c3baf95b8bbf1 100644 --- a/packages/flutter_tools/lib/src/android/gradle.dart +++ b/packages/flutter_tools/lib/src/android/gradle.dart @@ -698,14 +698,15 @@ class AndroidGradleBuilder implements AndroidBuilder { return false; } - // As long as libflutter.so.sym is present for at least one architecture, + // As long as libflutter.so.sym or libflutter.so.dbg is present for at least one architecture, // assume AGP succeeded in stripping. - if (result.stdout.contains('libflutter.so.sym')) { + if (result.stdout.contains('libflutter.so.sym') || + result.stdout.contains('libflutter.so.dbg')) { return true; } _logger.printTrace( - 'libflutter.so.sym not present when checking final appbundle for debug symbols.', + 'libflutter.so.sym or libflutter.so.dbg not present when checking final appbundle for debug symbols.', ); return false; } diff --git a/packages/flutter_tools/lib/src/build_system/depfile.dart b/packages/flutter_tools/lib/src/build_system/depfile.dart index a27e986e18850..9d90194c93d1f 100644 --- a/packages/flutter_tools/lib/src/build_system/depfile.dart +++ b/packages/flutter_tools/lib/src/build_system/depfile.dart @@ -65,16 +65,19 @@ class DepfileService { } void _writeFilesToBuffer(List files, StringBuffer buffer) { + final bool backslash = _fileSystem.path.style.separator == r'\'; for (final File outputFile in files) { - if (_fileSystem.path.style.separator == r'\') { - // backslashes and spaces in a depfile have to be escaped if the - // platform separator is a backslash. - final String path = outputFile.path.replaceAll(r'\', r'\\').replaceAll(r' ', r'\ '); - buffer.write(' $path'); + String path = _fileSystem.path.normalize(outputFile.path); + if (backslash) { + // Backslashes in a depfile have to be escaped if the platform separator is a backslash. + path = path.replaceAll(r'\', r'\\'); } else { - final String path = outputFile.path.replaceAll(r' ', r'\ '); - buffer.write(' $path'); + // Convert all path separators to forward slashes. + path = path.replaceAll(r'\', r'/'); } + // Escape spaces. + path = path.replaceAll(r' ', r'\ '); + buffer.write(' $path'); } } @@ -92,7 +95,8 @@ class DepfileService { // The tool doesn't write duplicates to these lists. This call is an attempt to // be resilient to the outputs of other tools which write or user edits to depfiles. .toSet() - .map(_fileSystem.file) + // Normalize the path before creating a file object. + .map((String path) => _fileSystem.file(_fileSystem.path.normalize(path))) .toList(); } } diff --git a/packages/flutter_tools/lib/src/build_system/targets/common.dart b/packages/flutter_tools/lib/src/build_system/targets/common.dart index 735c380b10f84..67731019a050d 100644 --- a/packages/flutter_tools/lib/src/build_system/targets/common.dart +++ b/packages/flutter_tools/lib/src/build_system/targets/common.dart @@ -6,16 +6,14 @@ import 'package:package_config/package_config.dart'; import '../../artifacts.dart'; import '../../base/build.dart'; -import '../../base/common.dart'; import '../../base/file_system.dart'; import '../../base/io.dart'; import '../../build_info.dart'; import '../../compile.dart'; import '../../dart/package_map.dart'; import '../../devfs.dart'; -import '../../globals.dart' as globals show platform, xcode; +import '../../globals.dart' as globals show xcode; import '../../project.dart'; -import '../../runner/flutter_command.dart'; import '../build_system.dart'; import '../depfile.dart'; import '../exceptions.dart'; @@ -310,15 +308,17 @@ class KernelSnapshot extends Target { if (flavor == null) { return; } - if (globals.platform.environment[kAppFlavor] != null) { - throwToolExit('$kAppFlavor is used by the framework and cannot be set in the environment.'); - } - if (dartDefines.any((String define) => define.startsWith(kAppFlavor))) { - throwToolExit( - '$kAppFlavor is used by the framework and cannot be ' - 'set using --${FlutterOptions.kDartDefinesOption} or --${FlutterOptions.kDartDefineFromFileOption}', - ); - } + + // It is possible there is a flavor already in dartDefines, from another + // part of the build process, but this should take precedence as it happens + // last (xcodebuild execution). + // + // See https://github.com/flutter/flutter/issues/169598. + + // If the flavor is already in the dart defines, remove it. + dartDefines.removeWhere((String define) => define.startsWith(kAppFlavor)); + + // Then, add it to the end. dartDefines.add('$kAppFlavor=$flavor'); } } diff --git a/packages/flutter_tools/lib/src/reporting/github_template.dart b/packages/flutter_tools/lib/src/reporting/github_template.dart index b7ca3ed67e2f8..d0d7f2f265791 100644 --- a/packages/flutter_tools/lib/src/reporting/github_template.dart +++ b/packages/flutter_tools/lib/src/reporting/github_template.dart @@ -143,26 +143,62 @@ ${_projectMetadataInformation()} ..writeln('**Creation channel**: ${metadata.versionChannel}') ..writeln('**Creation framework version**: ${metadata.versionRevision}'); - final File file = project.flutterPluginsFile; + final File file = project.flutterPluginsDependenciesFile; if (file.existsSync()) { - description.writeln('### Plugins'); - // Format is: - // camera=/path/to/.pub-cache/hosted/pub.dartlang.org/camera-0.5.7+2/ - for (final String plugin in project.flutterPluginsFile.readAsLinesSync()) { - final List pluginParts = plugin.split('='); - if (pluginParts.length != 2) { - continue; - } - // Write the last part of the path, which includes the plugin name and version. - // Example: camera-0.5.7+2 - final List pathParts = _fileSystem.path.split(pluginParts[1]); - description.writeln(pathParts.isEmpty ? pluginParts.first : pathParts.last); - } + _writePlugins(description, file); } - return description.toString(); } on Exception catch (exception) { return exception.toString(); } } + + void _writePlugins(StringBuffer description, File file) { + description.writeln('### Plugins'); + // Format is: + // { + // "plugins": { + // "ios": [ + // { + // "path": "/path/to/.pub-cache/hosted/pub.dartlang.org/camera-0.5.7+2/", + // } + // ] + // } + // } + final Object? json = jsonDecode(file.readAsStringSync()); + if (json is! Map) { + return; + } + final Object? plugins = json['plugins']; + if (plugins is! Map) { + return; + } + final Set pluginPaths = {}; + for (final Object? pluginList in plugins.values) { + if (pluginList is! List) { + continue; + } + for (final Object? plugin in pluginList) { + if (plugin is! Map) { + continue; + } + final String? path = plugin['path'] as String?; + if (path != null) { + pluginPaths.add(path); + } + } + } + if (pluginPaths.isEmpty) { + return; + } + for (final String path in pluginPaths) { + // Write the last part of the path, which includes the plugin name and version. + // Example: camera-0.5.7+2 + final List pathParts = _fileSystem.path.split(path); + if (pathParts.isEmpty) { + continue; + } + description.writeln(pathParts.last); + } + } } diff --git a/packages/flutter_tools/lib/src/runner/flutter_command.dart b/packages/flutter_tools/lib/src/runner/flutter_command.dart index 17ab07e321684..f9dceecb34a13 100644 --- a/packages/flutter_tools/lib/src/runner/flutter_command.dart +++ b/packages/flutter_tools/lib/src/runner/flutter_command.dart @@ -1438,6 +1438,18 @@ abstract class FlutterCommand extends Command { final String? cliFlavor = argParser.options.containsKey('flavor') ? stringArg('flavor') : null; final String? flavor = cliFlavor ?? defaultFlavor; + if (globals.platform.environment[kAppFlavor] != null) { + throwToolExit('$kAppFlavor is used by the framework and cannot be set in the environment.'); + } + if (dartDefines.any((String define) => define.startsWith(kAppFlavor))) { + throwToolExit( + '$kAppFlavor is used by the framework and cannot be ' + 'set using --${FlutterOptions.kDartDefinesOption} or --${FlutterOptions.kDartDefineFromFileOption}', + ); + } + if (flavor != null) { + dartDefines.add('$kAppFlavor=$flavor'); + } _addFlutterVersionToDartDefines(globals.flutterVersion, dartDefines); return BuildInfo( diff --git a/packages/flutter_tools/pubspec.yaml b/packages/flutter_tools/pubspec.yaml index 090aa0e8eae32..4e3488dad069a 100644 --- a/packages/flutter_tools/pubspec.yaml +++ b/packages/flutter_tools/pubspec.yaml @@ -12,7 +12,7 @@ dependencies: # https://github.com/flutter/flutter/blob/main/docs/infra/Updating-dependencies-in-Flutter.md archive: 3.6.1 args: 2.7.0 - dds: 5.0.0 + dds: 5.0.2 dwds: 24.3.10 code_builder: 4.10.1 completion: 1.0.1 @@ -78,7 +78,7 @@ dependencies: csslib: 1.0.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" dap: 1.4.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" dds_service_extensions: 2.0.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - devtools_shared: 11.2.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + devtools_shared: 11.2.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" dtd: 2.5.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" extension_discovery: 2.1.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" fixnum: 1.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -122,4 +122,4 @@ dartdoc: # Exclude this package from the hosted API docs. nodoc: true -# PUBSPEC CHECKSUM: c093 +# PUBSPEC CHECKSUM: 7396 diff --git a/packages/flutter_tools/test/general.shard/android/android_gradle_builder_test.dart b/packages/flutter_tools/test/general.shard/android/android_gradle_builder_test.dart index e20c3d6e067fc..996fe8372c981 100644 --- a/packages/flutter_tools/test/general.shard/android/android_gradle_builder_test.dart +++ b/packages/flutter_tools/test/general.shard/android/android_gradle_builder_test.dart @@ -851,6 +851,16 @@ void main() { /BUNDLE-METADATA/com.android.tools.build.debugsymbols/ /BUNDLE-METADATA/com.android.tools.build.debugsymbols/arm64-v8a/ /BUNDLE-METADATA/com.android.tools.build.debugsymbols/arm64-v8a/libflutter.so.sym +'''; + + // Output from `/tools/bin/apkanalyzer files list ` + // on an aab containing the debug info and symbol tables. + const String apkanalyzerOutputWithDebugInfoAndSymFiles = + apkanalyzerOutputWithoutSymFiles + + r''' +/BUNDLE-METADATA/com.android.tools.build.debugsymbols/ +/BUNDLE-METADATA/com.android.tools.build.debugsymbols/arm64-v8a/ +/BUNDLE-METADATA/com.android.tools.build.debugsymbols/arm64-v8a/libflutter.so.dbg '''; void createSharedGradleFiles() { @@ -942,6 +952,71 @@ void main() { overrides: {AndroidStudio: () => FakeAndroidStudio()}, ); + testUsingContext( + 'build succeeds when debug info and symbol tables present for at least one architecture', + () async { + final AndroidGradleBuilder builder = AndroidGradleBuilder( + java: FakeJava(), + logger: logger, + processManager: processManager, + fileSystem: fileSystem, + artifacts: Artifacts.test(), + analytics: fakeAnalytics, + gradleUtils: FakeGradleUtils(), + platform: FakePlatform(environment: {'HOME': '/home'}), + androidStudio: FakeAndroidStudio(), + ); + processManager.addCommand( + FakeCommand(command: List.of(commonCommandPortion)..add('bundleRelease')), + ); + + createSharedGradleFiles(); + final File aabFile = createAabFile(BuildMode.release); + final AndroidSdk sdk = AndroidSdk.locateAndroidSdk()!; + + processManager.addCommand( + FakeCommand( + command: [ + sdk.getCmdlineToolsPath(apkAnalyzerBinaryName)!, + 'files', + 'list', + aabFile.path, + ], + stdout: apkanalyzerOutputWithDebugInfoAndSymFiles, + ), + ); + + final FlutterProject project = FlutterProject.fromDirectoryTest( + fileSystem.currentDirectory, + ); + project.android.appManifestFile + ..createSync(recursive: true) + ..writeAsStringSync(minimalV2EmbeddingManifest); + + await builder.buildGradleApp( + project: project, + androidBuildInfo: const AndroidBuildInfo( + BuildInfo( + BuildMode.release, + null, + treeShakeIcons: false, + packageConfigPath: '.dart_tool/package_config.json', + ), + targetArchs: [ + AndroidArch.arm64_v8a, + AndroidArch.armeabi_v7a, + AndroidArch.x86_64, + ], + ), + target: 'lib/main.dart', + isBuildingBundle: true, + configOnly: false, + localGradleErrors: [], + ); + }, + overrides: {AndroidStudio: () => FakeAndroidStudio()}, + ); + testUsingContext( 'building a debug aab does not invoke apkanalyzer', () async { diff --git a/packages/flutter_tools/test/general.shard/build_system/depfile_test.dart b/packages/flutter_tools/test/general.shard/build_system/depfile_test.dart index f0efccaf5e3ef..3855c40141ee8 100644 --- a/packages/flutter_tools/test/general.shard/build_system/depfile_test.dart +++ b/packages/flutter_tools/test/general.shard/build_system/depfile_test.dart @@ -17,6 +17,7 @@ void main() { fileSystem = MemoryFileSystem.test(); depfileService = DepfileService(logger: BufferLogger.test(), fileSystem: fileSystem); }); + testWithoutContext('Can parse depfile from file', () { final File depfileSource = fileSystem.file('example.d')..writeAsStringSync(''' a.txt: b.txt @@ -48,22 +49,34 @@ a.txt c.txt d.txt: b.txt }); testWithoutContext('Can parse depfile with windows file paths', () { - fileSystem = MemoryFileSystem.test(style: FileSystemStyle.windows); - depfileService = DepfileService(logger: BufferLogger.test(), fileSystem: fileSystem); + final FileSystem fileSystem = MemoryFileSystem.test(style: FileSystemStyle.windows); + final DepfileService depfileService = DepfileService( + logger: BufferLogger.test(), + fileSystem: fileSystem, + ); final File depfileSource = fileSystem.file('example.d')..writeAsStringSync(r''' -C:\\a.txt: C:\\b.txt +C:\\a1.txt C:\\a2/a3.txt: C:\\b1.txt C:\\b2/b3.txt '''); final Depfile depfile = depfileService.parse(depfileSource); - expect(depfile.inputs.single.path, r'C:\b.txt'); - expect(depfile.outputs.single.path, r'C:\a.txt'); + expect(depfile.inputs.map((File e) => e.path).toList(), [ + r'C:\b1.txt', + r'C:\b2\b3.txt', + ]); + expect(depfile.outputs.map((File e) => e.path).toList(), [ + r'C:\a1.txt', + r'C:\a2\a3.txt', + ]); }); testWithoutContext( 'Can escape depfile with windows file paths and spaces in directory names', () { - fileSystem = MemoryFileSystem.test(style: FileSystemStyle.windows); - depfileService = DepfileService(logger: BufferLogger.test(), fileSystem: fileSystem); + final FileSystem fileSystem = MemoryFileSystem.test(style: FileSystemStyle.windows); + final DepfileService depfileService = DepfileService( + logger: BufferLogger.test(), + fileSystem: fileSystem, + ); final File inputFile = fileSystem.directory(r'Hello Flutter').childFile('a.txt').absolute ..createSync(recursive: true); @@ -73,8 +86,9 @@ C:\\a.txt: C:\\b.txt final File outputDepfile = fileSystem.file('depfile'); depfileService.writeToFile(depfile, outputDepfile); - expect(outputDepfile.readAsStringSync(), contains(r'C:\\Hello\ Flutter\\a.txt')); - expect(outputDepfile.readAsStringSync(), contains(r'C:\\Hello\ Flutter\\b.txt')); + final String output = outputDepfile.readAsStringSync(); + expect(output, contains(r'C:\\Hello\ Flutter\\a.txt')); + expect(output, contains(r'C:\\Hello\ Flutter\\b.txt')); }, ); @@ -88,8 +102,46 @@ C:\\a.txt: C:\\b.txt final File outputDepfile = fileSystem.file('depfile'); depfileService.writeToFile(depfile, outputDepfile); - expect(outputDepfile.readAsStringSync(), contains(r'/Hello\ Flutter/a.txt')); - expect(outputDepfile.readAsStringSync(), contains(r'/Hello\ Flutter/b.txt')); + final String output = outputDepfile.readAsStringSync(); + expect(output, contains(r'/Hello\ Flutter/a.txt')); + expect(output, contains(r'/Hello\ Flutter/b.txt')); + }); + + testWithoutContext('Can produce normalized paths', () { + final List<(FileSystemStyle style, String input, String output, List expects)> pairs = + <(FileSystemStyle style, String input, String output, List expects)>[ + ( + FileSystemStyle.posix, + r'Hello Flutter\a.txt', + r'Hello Flutter\b.txt', + [r'/Hello\ Flutter/a.txt', r'/Hello\ Flutter/b.txt'], + ), + ( + FileSystemStyle.windows, + r'Hello Flutter/a.txt', + r'Hello Flutter/b.txt', + [r'\\Hello\ Flutter\\a.txt', r'\\Hello\ Flutter\\b.txt'], + ), + ]; + + for (final (FileSystemStyle style, String input, String output, List expects) + in pairs) { + final FileSystem fileSystem = MemoryFileSystem.test(style: style); + final DepfileService depfileService = DepfileService( + logger: BufferLogger.test(), + fileSystem: fileSystem, + ); + final File inputFile = fileSystem.file(input).absolute..createSync(recursive: true); + final File outputFile = fileSystem.file(output).absolute..createSync(); + final Depfile depfile = Depfile([inputFile], [outputFile]); + final File outputDepfile = fileSystem.file('depfile'); + depfileService.writeToFile(depfile, outputDepfile); + + final String outputString = outputDepfile.readAsStringSync(); + for (final String path in expects) { + expect(outputString, contains(path)); + } + } }); testWithoutContext('Resilient to weird whitespace', () { diff --git a/packages/flutter_tools/test/general.shard/build_system/targets/common_test.dart b/packages/flutter_tools/test/general.shard/build_system/targets/common_test.dart index 8ceab06877d4a..5591d93710c09 100644 --- a/packages/flutter_tools/test/general.shard/build_system/targets/common_test.dart +++ b/packages/flutter_tools/test/general.shard/build_system/targets/common_test.dart @@ -13,6 +13,7 @@ import 'package:flutter_tools/src/build_system/exceptions.dart'; import 'package:flutter_tools/src/build_system/targets/common.dart'; import 'package:flutter_tools/src/build_system/targets/ios.dart'; import 'package:flutter_tools/src/compile.dart'; +import 'package:flutter_tools/src/convert.dart'; import 'package:flutter_tools/src/ios/xcodeproj.dart'; import 'package:test/fake.dart'; @@ -440,58 +441,65 @@ void main() { ); testUsingContext( - "tool exits when $kAppFlavor is already set in user's environment", + 'KernelSnapshot sets flavor in dartDefines from Xcode build configuration if ios app', () async { fileSystem.file('.dart_tool/package_config.json') ..createSync(recursive: true) ..writeAsStringSync('{"configVersion": 2, "packages":[]}'); - final Future buildResult = const KernelSnapshot().build( - androidEnvironment - ..defines[kTargetPlatform] = getNameForTargetPlatform(TargetPlatform.android) - ..defines[kBuildMode] = BuildMode.debug.cliName - ..defines[kFlavor] = 'strawberry' - ..defines[kTrackWidgetCreation] = 'false', + final String build = iosEnvironment.buildDir.path; + final String flutterPatchedSdkPath = artifacts.getArtifactPath( + Artifact.flutterPatchedSdkPath, + platform: TargetPlatform.ios, + mode: BuildMode.debug, ); - - expect( - buildResult, - throwsToolExit( - message: '$kAppFlavor is used by the framework and cannot be set in the environment.', + fileSystem.directory('/ios/Runner.xcodeproj').createSync(recursive: true); + processManager.addCommands([ + FakeCommand( + command: [ + artifacts.getArtifactPath(Artifact.engineDartAotRuntime), + artifacts.getArtifactPath(Artifact.frontendServerSnapshotForEngineDartSdk), + '--sdk-root', + '$flutterPatchedSdkPath/', + '--target=flutter', + '--no-print-incremental-dependencies', + '-D$kAppFlavor=chocolate', + ...buildModeOptions(BuildMode.debug, []), + '--no-link-platform', + '--packages', + '/.dart_tool/package_config.json', + '--output-dill', + '$build/app.dill', + '--depfile', + '$build/kernel_snapshot_program.d', + '--incremental', + '--initialize-from-dill', + '$build/app.dill', + '--verbosity=error', + 'file:///lib/main.dart', + ], + stdout: 'result $kBoundaryKey\n$kBoundaryKey\n$kBoundaryKey $build/app.dill 0\n', ), - ); - }, - overrides: { - Platform: () => FakePlatform(environment: {kAppFlavor: 'I was already set'}), - }, - ); + ]); - testUsingContext( - 'tool exits when $kAppFlavor is set in --dart-define or --dart-define-from-file', - () async { - fileSystem.file('.dart_tool/package_config.json') - ..createSync(recursive: true) - ..writeAsStringSync('{"configVersion": 2, "packages":[]}'); - final Future buildResult = const KernelSnapshot().build( - androidEnvironment - ..defines[kTargetPlatform] = getNameForTargetPlatform(TargetPlatform.android) + await const KernelSnapshot().build( + iosEnvironment + ..defines[kTargetPlatform] = getNameForTargetPlatform(TargetPlatform.ios) ..defines[kBuildMode] = BuildMode.debug.cliName ..defines[kFlavor] = 'strawberry' - ..defines[kDartDefines] = encodeDartDefines([kAppFlavor, 'strawberry']) + ..defines[kXcodeConfiguration] = 'Debug-chocolate' ..defines[kTrackWidgetCreation] = 'false', ); - expect( - buildResult, - throwsToolExit( - message: - '$kAppFlavor is used by the framework and cannot be set using --dart-define or --dart-define-from-file', - ), - ); + expect(processManager, hasNoRemainingExpectations); + }, + overrides: { + XcodeProjectInterpreter: + () => FakeXcodeProjectInterpreter(schemes: ['Runner', 'chocolate']), }, ); testUsingContext( - 'KernelSnapshot sets flavor in dartDefines from Xcode build configuration if ios app', + 'KernelSnapshot sets flavor in dartDefines from Xcode build configuration if macos app', () async { fileSystem.file('.dart_tool/package_config.json') ..createSync(recursive: true) @@ -499,10 +507,10 @@ void main() { final String build = iosEnvironment.buildDir.path; final String flutterPatchedSdkPath = artifacts.getArtifactPath( Artifact.flutterPatchedSdkPath, - platform: TargetPlatform.ios, + platform: TargetPlatform.darwin, mode: BuildMode.debug, ); - fileSystem.directory('/ios/Runner.xcodeproj').createSync(recursive: true); + fileSystem.directory('/macos/Runner.xcodeproj').createSync(recursive: true); processManager.addCommands([ FakeCommand( command: [ @@ -514,7 +522,6 @@ void main() { '--no-print-incremental-dependencies', '-D$kAppFlavor=chocolate', ...buildModeOptions(BuildMode.debug, []), - '--no-link-platform', '--packages', '/.dart_tool/package_config.json', '--output-dill', @@ -533,7 +540,7 @@ void main() { await const KernelSnapshot().build( iosEnvironment - ..defines[kTargetPlatform] = getNameForTargetPlatform(TargetPlatform.ios) + ..defines[kTargetPlatform] = getNameForTargetPlatform(TargetPlatform.darwin) ..defines[kBuildMode] = BuildMode.debug.cliName ..defines[kFlavor] = 'strawberry' ..defines[kXcodeConfiguration] = 'Debug-chocolate' @@ -549,7 +556,7 @@ void main() { ); testUsingContext( - 'KernelSnapshot sets flavor in dartDefines from Xcode build configuration if macos app', + 'KernelSnapshot does not add kAppFlavor twice to Dart defines', () async { fileSystem.file('.dart_tool/package_config.json') ..createSync(recursive: true) @@ -560,7 +567,6 @@ void main() { platform: TargetPlatform.darwin, mode: BuildMode.debug, ); - fileSystem.directory('/macos/Runner.xcodeproj').createSync(recursive: true); processManager.addCommands([ FakeCommand( command: [ @@ -570,7 +576,7 @@ void main() { '$flutterPatchedSdkPath/', '--target=flutter', '--no-print-incremental-dependencies', - '-D$kAppFlavor=chocolate', + '-D$kAppFlavor=strawberry', ...buildModeOptions(BuildMode.debug, []), '--packages', '/.dart_tool/package_config.json', @@ -592,16 +598,17 @@ void main() { iosEnvironment ..defines[kTargetPlatform] = getNameForTargetPlatform(TargetPlatform.darwin) ..defines[kBuildMode] = BuildMode.debug.cliName + ..defines[kDartDefines] = base64Encode(utf8.encode('FLUTTER_APP_FLAVOR=vanilla')) ..defines[kFlavor] = 'strawberry' - ..defines[kXcodeConfiguration] = 'Debug-chocolate' ..defines[kTrackWidgetCreation] = 'false', ); expect(processManager, hasNoRemainingExpectations); }, overrides: { - XcodeProjectInterpreter: - () => FakeXcodeProjectInterpreter(schemes: ['Runner', 'chocolate']), + Platform: () => macPlatform, + FileSystem: () => fileSystem, + ProcessManager: () => processManager, }, ); diff --git a/packages/flutter_tools/test/general.shard/github_template_test.dart b/packages/flutter_tools/test/general.shard/github_template_test.dart index 6821f2eed9d6a..6afabec1803f0 100644 --- a/packages/flutter_tools/test/general.shard/github_template_test.dart +++ b/packages/flutter_tools/test/general.shard/github_template_test.dart @@ -13,6 +13,33 @@ import 'package:flutter_tools/src/reporting/github_template.dart'; import '../src/common.dart'; import '../src/context.dart'; +const String _kPluginsFile = ''' +{ + "plugins": { + "ios": [ + { + "name": "camera", + "path": "/fake/pub.dartlang.org/camera-0.5.7+2/" + }, + { + "name": "device_info", + "path": "/fake/pub.dartlang.org/device_info-0.4.1+4/" + } + ], + "android": [ + { + "name": "camera", + "path": "/fake/pub.dartlang.org/camera-0.5.7+2/" + }, + { + "name": "device_info", + "path": "/fake/pub.dartlang.org/device_info-0.4.1+4/" + } + ] + } +} +'''; + void main() { late BufferLogger logger; late FileSystem fs; @@ -199,11 +226,8 @@ flutter: iosBundleIdentifier: com.example.failing.ios '''); - final File pluginsFile = projectDirectory.childFile('.flutter-plugins'); - pluginsFile.writeAsStringSync(''' -camera=/fake/pub.dartlang.org/camera-0.5.7+2/ -device_info=/fake/pub.dartlang.org/pub.dartlang.org/device_info-0.4.1+4/ - '''); + final File pluginsFile = projectDirectory.childFile('.flutter-plugins-dependencies'); + pluginsFile.writeAsStringSync(_kPluginsFile); final File metadataFile = projectDirectory.childFile('.metadata'); metadataFile.writeAsStringSync(''' diff --git a/packages/flutter_tools/test/general.shard/runner/flutter_command_test.dart b/packages/flutter_tools/test/general.shard/runner/flutter_command_test.dart index 00b603d354f2a..fbda6491f992a 100644 --- a/packages/flutter_tools/test/general.shard/runner/flutter_command_test.dart +++ b/packages/flutter_tools/test/general.shard/runner/flutter_command_test.dart @@ -1270,6 +1270,98 @@ flutter: ); }); + testUsingContext( + "tool exits when $kAppFlavor is already set in user's environemnt", + () async { + final CommandRunner runner = createTestCommandRunner( + _TestRunCommandThatOnlyValidates(), + ); + expect( + runner.run(['run', '--no-pub', '--no-hot']), + throwsToolExit( + message: '$kAppFlavor is used by the framework and cannot be set in the environment.', + ), + ); + }, + overrides: { + DeviceManager: + () => FakeDeviceManager()..attachedDevices = [FakeDevice('name', 'id')], + FileSystem: () { + final MemoryFileSystem fileSystem = MemoryFileSystem.test(); + fileSystem.file('lib/main.dart').createSync(recursive: true); + fileSystem.file('pubspec.yaml').createSync(); + return fileSystem; + }, + ProcessManager: FakeProcessManager.empty, + Platform: () => FakePlatform()..environment = {kAppFlavor: 'AlreadySet'}, + }, + ); + + testUsingContext( + 'tool exits when $kAppFlavor is set in --dart-define', + () async { + final CommandRunner runner = createTestCommandRunner( + _TestRunCommandThatOnlyValidates(), + ); + expect( + runner.run([ + 'run', + '--dart-define=$kAppFlavor=AlreadySet', + '--no-pub', + '--no-hot', + ]), + throwsToolExit( + message: '$kAppFlavor is used by the framework and cannot be set using --dart-define', + ), + ); + }, + overrides: { + DeviceManager: + () => FakeDeviceManager()..attachedDevices = [FakeDevice('name', 'id')], + FileSystem: () { + final MemoryFileSystem fileSystem = MemoryFileSystem.test(); + fileSystem.file('lib/main.dart').createSync(recursive: true); + fileSystem.file('pubspec.yaml').createSync(); + return fileSystem; + }, + ProcessManager: FakeProcessManager.empty, + }, + ); + + testUsingContext( + 'tool exits when $kAppFlavor is set in --dart-define-from-file', + () async { + final CommandRunner runner = createTestCommandRunner( + _TestRunCommandThatOnlyValidates(), + ); + expect( + runner.run([ + 'run', + '--dart-define-from-file=config.json', + '--no-pub', + '--no-hot', + ]), + throwsToolExit( + message: '$kAppFlavor is used by the framework and cannot be set using --dart-define', + ), + ); + }, + overrides: { + DeviceManager: + () => FakeDeviceManager()..attachedDevices = [FakeDevice('name', 'id')], + FileSystem: () { + final MemoryFileSystem fileSystem = MemoryFileSystem.test(); + fileSystem.file('lib/main.dart').createSync(recursive: true); + fileSystem.file('pubspec.yaml').createSync(); + fileSystem.file('config.json') + ..createSync() + ..writeAsStringSync('{"$kAppFlavor": "AlreadySet"}'); + return fileSystem; + }, + ProcessManager: FakeProcessManager.empty, + }, + ); + group('Flutter version', () { for (final String dartDefine in FlutterCommand.flutterVersionDartDefines) { testUsingContext( diff --git a/packages/flutter_tools/test/integration.shard/debug_adapter/flutter_adapter_test.dart b/packages/flutter_tools/test/integration.shard/debug_adapter/flutter_adapter_test.dart index c14ec35db808c..c85946ee8d6fd 100644 --- a/packages/flutter_tools/test/integration.shard/debug_adapter/flutter_adapter_test.dart +++ b/packages/flutter_tools/test/integration.shard/debug_adapter/flutter_adapter_test.dart @@ -272,16 +272,18 @@ The relevant error-causing widget was: ); }); - testWithoutContext('correctly outputs colored exceptions when supported', () async { - final BasicProjectThatThrows project = BasicProjectThatThrows(); - final String output = await getExceptionOutput(project, noDebug: false, ansiColors: true); - - // Frames in the stack trace that are the users own code will be unformatted, but - // frames from the framework are faint (starting with `\x1B[2m`). - - expect( - output, - contains(''' + testWithoutContext( + 'correctly outputs colored exceptions when supported', + () async { + final BasicProjectThatThrows project = BasicProjectThatThrows(); + final String output = await getExceptionOutput(project, noDebug: false, ansiColors: true); + + // Frames in the stack trace that are the users own code will be unformatted, but + // frames from the framework are faint (starting with `\x1B[2m`). + + expect( + output, + contains(''' ════════ Exception caught by widgets library ═══════════════════════════════════ The following _Exception was thrown building App(dirty): Exception: c @@ -298,8 +300,10 @@ When the exception was thrown, this was the stack: ^ source: package:flutter/src/widgets/framework.dart \x1B[2m#3 ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:1:1)\x1B[0m ^ source: package:flutter/src/widgets/framework.dart'''), - ); - }); + ); + }, + skip: true, // DAP URI parsing bug, https://github.com/dart-lang/sdk/issues/60797 + ); testWithoutContext('correctly outputs exceptions in noDebug mode', () async { final BasicProjectThatThrows project = BasicProjectThatThrows(); diff --git a/packages/flutter_tools/test/integration.shard/default_flavor_test.dart b/packages/flutter_tools/test/integration.shard/default_flavor_test.dart new file mode 100644 index 0000000000000..f84008a05d9c7 --- /dev/null +++ b/packages/flutter_tools/test/integration.shard/default_flavor_test.dart @@ -0,0 +1,74 @@ +// Copyright 2014 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +@Tags(['flutter-test-driver']) +library; + +import 'package:flutter_tools/src/base/file_system.dart'; + +import '../src/common.dart'; +import 'test_data/project.dart'; +import 'test_driver.dart'; +import 'test_utils.dart'; + +void main() { + final Project project = _DefaultFlavorProject(); + late Directory tempDir; + late FlutterTestTestDriver flutter; + + setUp(() async { + tempDir = createResolvedTempDirectorySync('default_flavor_test.'); + await project.setUpIn(tempDir); + flutter = FlutterTestTestDriver(tempDir); + }); + + tearDown(() async { + tryToDelete(tempDir); + }); + + testWithoutContext('Reads "default-flavor" in "flutter test"', () async { + await flutter.test(); + + // Without an assertion, this test always passes. + final int? exitCode = await flutter.done; + expect(exitCode, 0, reason: 'flutter test failed with exit code $exitCode'); + }); +} + +final class _DefaultFlavorProject extends Project { + @override + final String main = r''' + // Irrelevant to this test. + void main() {} + '''; + + @override + final String pubspec = r''' + name: test + environment: + sdk: ^3.7.0-0 + + flutter: + default-flavor: dev + + dependencies: + flutter: + sdk: flutter + dev_dependencies: + flutter_test: + sdk: flutter + '''; + + @override + final String test = r''' + import 'package:flutter/services.dart'; + import 'package:flutter_test/flutter_test.dart'; + + void main() { + test('receives default-flavor with flutter test', () async { + expect(appFlavor, 'dev'); + }); + } + '''; +} diff --git a/packages/flutter_tools/test/integration.shard/test_driver.dart b/packages/flutter_tools/test/integration.shard/test_driver.dart index 0dd6ab3356d5d..1f8af11a11a90 100644 --- a/packages/flutter_tools/test/integration.shard/test_driver.dart +++ b/packages/flutter_tools/test/integration.shard/test_driver.dart @@ -138,7 +138,10 @@ abstract final class FlutterTestDriver { _stderr.stream.listen((String message) => _debugPrint(message, topic: '<=stderr=')); } - Future get done async => _process?.exitCode; + /// Completes when process exits with the given exit code. + /// + /// If the process has never been started, complets with `null`. + Future get done async => _process?.exitCode; Future connectToVmService({bool pauseOnExceptions = false}) async { _vmService = await vmServiceConnectUri('$_vmServiceWsUri');