flutter/packages/flutter_tools/bin/macos_assemble.sh
Victoria Ashworth 6d19fa3bfa
Add Swift Package Manager as new opt-in feature for iOS and macOS (#146256)
This PR adds initial support for Swift Package Manager (SPM). Users must opt in. Only compatible with Xcode 15+.

Fixes https://github.com/flutter/flutter/issues/146369.

## Included Features

This PR includes the following features:
* Enabling SPM via config 
`flutter config --enable-swift-package-manager`
* Disabling SPM via config (will disable for all projects) 
`flutter config --no-enable-swift-package-manager`
* Disabling SPM via pubspec.yaml (will disable for the specific project)
```
flutter:
  disable-swift-package-manager: true
```
* Migrating existing apps to add SPM integration if using a Flutter plugin with a Package.swift
  * Generates a Swift Package (named `FlutterGeneratedPluginSwiftPackage`) that handles Flutter SPM-compatible plugin dependencies. Generated package is added to the Xcode project.
* Error parsing of common errors that may occur due to using CocoaPods and Swift Package Manager together
* Tool will print warnings when using all Swift Package plugins and encourage you to remove CocoaPods

This PR also converts `integration_test` and `integration_test_macos` plugins to be both Swift Packages and CocoaPod Pods.

## How it Works
The Flutter CLI will generate a Swift Package called `FlutterGeneratedPluginSwiftPackage`, which will have local dependencies on all Swift Package compatible Flutter plugins.  

The `FlutterGeneratedPluginSwiftPackage` package will be added to the Xcode project via altering of the `project.pbxproj`. 

In addition, a "Pre-action" script will be added via altering of the `Runner.xcscheme`. This script will invoke the flutter tool to copy the Flutter/FlutterMacOS framework to the `BUILT_PRODUCTS_DIR` directory before the build starts. This is needed because plugins need to be linked to the Flutter framework and fortunately Swift Package Manager automatically uses `BUILT_PRODUCTS_DIR` as a framework search path.

CocoaPods will continue to run and be used to support non-Swift Package compatible Flutter plugins.

## Not Included Features

It does not include the following (will be added in future PRs):
* Create plugin template
* Create app template
* Add-to-App integration
2024-04-18 21:12:36 +00:00

211 lines
8.7 KiB
Bash
Executable file

#!/usr/bin/env bash
# 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.
# TODO(zanderso): refactor this and xcode_backend.sh into one script
# once iOS is using 'assemble'.
RunCommand() {
if [[ -n "$VERBOSE_SCRIPT_LOGGING" ]]; then
echo "$*"
fi
"$@"
return $?
}
EchoError() {
echo "$@" 1>&2
}
ParseFlutterBuildMode() {
# Use FLUTTER_BUILD_MODE if it's set, otherwise use the Xcode build configuration name
# This means that if someone wants to use an Xcode build config other than Debug/Profile/Release,
# they _must_ set FLUTTER_BUILD_MODE so we know what type of artifact to build.
local build_mode="$(echo "${FLUTTER_BUILD_MODE:-${CONFIGURATION}}" | tr "[:upper:]" "[:lower:]")"
case "$build_mode" in
*release*) build_mode="release";;
*profile*) build_mode="profile";;
*debug*) build_mode="debug";;
*)
EchoError "========================================================================"
EchoError "ERROR: Unknown FLUTTER_BUILD_MODE: ${build_mode}."
EchoError "Valid values are 'Debug', 'Profile', or 'Release' (case insensitive)."
EchoError "This is controlled by the FLUTTER_BUILD_MODE environment variable."
EchoError "If that is not set, the CONFIGURATION environment variable is used."
EchoError ""
EchoError "You can fix this by either adding an appropriately named build"
EchoError "configuration, or adding an appropriate value for FLUTTER_BUILD_MODE to the"
EchoError ".xcconfig file for the current build configuration (${CONFIGURATION})."
EchoError "========================================================================"
exit -1;;
esac
echo "${build_mode}"
}
BuildApp() {
# Set the working directory to the project root
local project_path="${SOURCE_ROOT}/.."
RunCommand pushd "${project_path}" > /dev/null
# Set the target file.
local target_path="lib/main.dart"
if [[ -n "$FLUTTER_TARGET" ]]; then
target_path="${FLUTTER_TARGET}"
fi
# Use FLUTTER_BUILD_MODE if it's set, otherwise use the Xcode build configuration name
# This means that if someone wants to use an Xcode build config other than Debug/Profile/Release,
# they _must_ set FLUTTER_BUILD_MODE so we know what type of artifact to build.
local build_mode="$(ParseFlutterBuildMode)"
if [[ -n "$LOCAL_ENGINE" ]]; then
if [[ $(echo "$LOCAL_ENGINE" | tr "[:upper:]" "[:lower:]") != *"$build_mode"* ]]; then
EchoError "========================================================================"
EchoError "ERROR: Requested build with Flutter local engine at '${LOCAL_ENGINE}'"
EchoError "This engine is not compatible with FLUTTER_BUILD_MODE: '${build_mode}'."
EchoError "You can fix this by updating the LOCAL_ENGINE environment variable, or"
EchoError "by running:"
EchoError " flutter build macos --local-engine=host_${build_mode} --local-engine-host=host_${build_mode}"
EchoError "or"
EchoError " flutter build macos --local-engine=host_${build_mode}_unopt --local-engine-host=host_${build_mode}_unopt"
EchoError "========================================================================"
exit -1
fi
fi
if [[ -n "$LOCAL_ENGINE_HOST" ]]; then
if [[ $(echo "$LOCAL_ENGINE_HOST" | tr "[:upper:]" "[:lower:]") != *"$build_mode"* ]]; then
EchoError "========================================================================"
EchoError "ERROR: Requested build with Flutter local engine at '${LOCAL_ENGINE_HOST}'"
EchoError "This engine is not compatible with FLUTTER_BUILD_MODE: '${build_mode}'."
EchoError "You can fix this by updating the LOCAL_ENGINE_HOST environment variable, or"
EchoError "by running:"
EchoError " flutter build macos --local-engine=host_${build_mode} --local-engine-host=host_${build_mode}"
EchoError "or"
EchoError " flutter build macos --local-engine=host_${build_mode}_unopt --local-engine-host=host_${build_mode}_unopt"
EchoError "========================================================================"
exit -1
fi
fi
# The path where the input/output xcfilelists are stored. These are used by xcode
# to conditionally skip this script phase if neither have changed.
local ephemeral_dir="${SOURCE_ROOT}/Flutter/ephemeral"
local build_inputs_path="${ephemeral_dir}/FlutterInputs.xcfilelist"
local build_outputs_path="${ephemeral_dir}/FlutterOutputs.xcfilelist"
# Construct the "flutter assemble" argument array. Arguments should be added
# as quoted string elements of the flutter_args array, otherwise an argument
# (like a path) with spaces in it might be interpreted as two separate
# arguments.
local flutter_args=("${FLUTTER_ROOT}/bin/flutter")
if [[ -n "$VERBOSE_SCRIPT_LOGGING" ]]; then
flutter_args+=('--verbose')
fi
if [[ -n "$FLUTTER_ENGINE" ]]; then
flutter_args+=("--local-engine-src-path=${FLUTTER_ENGINE}")
fi
if [[ -n "$LOCAL_ENGINE" ]]; then
flutter_args+=("--local-engine=${LOCAL_ENGINE}")
fi
if [[ -n "$LOCAL_ENGINE_HOST" ]]; then
flutter_args+=("--local-engine-host=${LOCAL_ENGINE_HOST}")
fi
flutter_args+=(
"assemble"
"--no-version-check"
"-dTargetPlatform=darwin"
"-dDarwinArchs=${ARCHS}"
"-dTargetFile=${target_path}"
"-dBuildMode=${build_mode}"
"-dTreeShakeIcons=${TREE_SHAKE_ICONS}"
"-dDartObfuscation=${DART_OBFUSCATION}"
"-dSplitDebugInfo=${SPLIT_DEBUG_INFO}"
"-dTrackWidgetCreation=${TRACK_WIDGET_CREATION}"
"-dAction=${ACTION}"
"-dFrontendServerStarterPath=${FRONTEND_SERVER_STARTER_PATH}"
"--DartDefines=${DART_DEFINES}"
"--ExtraGenSnapshotOptions=${EXTRA_GEN_SNAPSHOT_OPTIONS}"
"--ExtraFrontEndOptions=${EXTRA_FRONT_END_OPTIONS}"
"--build-inputs=${build_inputs_path}"
"--build-outputs=${build_outputs_path}"
"--output=${BUILT_PRODUCTS_DIR}"
)
if [[ -n "$FLAVOR" ]]; then
flutter_args+=("-dFlavor=${FLAVOR}")
fi
if [[ -n "$PERFORMANCE_MEASUREMENT_FILE" ]]; then
flutter_args+=("--performance-measurement-file=${PERFORMANCE_MEASUREMENT_FILE}")
fi
if [[ -n "$BUNDLE_SKSL_PATH" ]]; then
flutter_args+=("-dBundleSkSLPath=${BUNDLE_SKSL_PATH}")
fi
if [[ -n "$CODE_SIZE_DIRECTORY" ]]; then
flutter_args+=("-dCodeSizeDirectory=${CODE_SIZE_DIRECTORY}")
fi
# Run flutter assemble with the build mode specific target that was passed in.
# If no target was passed it, default to build mode specific
# macos_bundle_flutter_assets target.
if [[ -n "$1" ]]; then
flutter_args+=("${build_mode}$1")
else
flutter_args+=("${build_mode}_macos_bundle_flutter_assets")
fi
RunCommand "${flutter_args[@]}"
}
PrepareFramework() {
BuildApp "_unpack_macos"
}
# Adds the App.framework as an embedded binary, the flutter_assets as
# resources, and the native assets.
EmbedFrameworks() {
# Embed App.framework from Flutter into the app (after creating the Frameworks directory
# if it doesn't already exist).
local xcode_frameworks_dir="${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}"
RunCommand mkdir -p -- "${xcode_frameworks_dir}"
RunCommand rsync -av --delete --filter "- .DS_Store" "${BUILT_PRODUCTS_DIR}/App.framework" "${xcode_frameworks_dir}"
# Embed the actual FlutterMacOS.framework that the Flutter app expects to run against,
# which could be a local build or an arch/type specific build.
# Copy Xcode behavior and don't copy over headers or modules.
RunCommand rsync -av --delete --filter "- .DS_Store" --filter "- Headers" --filter "- Modules" "${BUILT_PRODUCTS_DIR}/FlutterMacOS.framework" "${xcode_frameworks_dir}/"
# Sign the binaries we moved.
if [[ -n "${EXPANDED_CODE_SIGN_IDENTITY:-}" ]]; then
RunCommand codesign --force --verbose --sign "${EXPANDED_CODE_SIGN_IDENTITY}" -- "${xcode_frameworks_dir}/App.framework/App"
RunCommand codesign --force --verbose --sign "${EXPANDED_CODE_SIGN_IDENTITY}" -- "${xcode_frameworks_dir}/FlutterMacOS.framework/FlutterMacOS"
fi
# Copy the native assets. These do not have to be codesigned here because,
# they are already codesigned in buildNativeAssetsMacOS.
local project_path="${SOURCE_ROOT}/.."
if [[ -n "$FLUTTER_APPLICATION_PATH" ]]; then
project_path="${FLUTTER_APPLICATION_PATH}"
fi
local native_assets_path="${project_path}/${FLUTTER_BUILD_DIR}/native_assets/macos/"
if [[ -d "$native_assets_path" ]]; then
RunCommand rsync -av --filter "- .DS_Store" --filter "- native_assets.yaml" "${native_assets_path}" "${xcode_frameworks_dir}"
fi
}
# Main entry point.
if [[ $# == 0 ]]; then
# Unnamed entry point defaults to build.
BuildApp
else
case $1 in
"build")
BuildApp ;;
"embed")
EmbedFrameworks ;;
"prepare")
PrepareFramework ;;
esac
fi