Add macOS configuration files and implement license flavor patching

- Created AppInfo.xcconfig, Debug.xcconfig, Release.xcconfig, and Warnings.xcconfig for macOS Runner target.
- Added entitlements files for DebugProfile and Release configurations to enable app sandboxing.
- Implemented Info.plist for application metadata and MainFlutterWindow.swift for window management.
- Added unit tests in RunnerTests.swift and widget tests in widget_test.dart.
- Updated pubspec.lock and pubspec.yaml to include flutter_lints and updated dependencies.
- Enhanced verify_aot_patch.dart for better AOT patching support and added utility functions.
- Included web assets: favicon, icons, index.html, and manifest.json for web deployment.

Signed-off-by: Tony <tonylu@tony-cloud.com>
This commit is contained in:
Tony
2026-06-25 01:59:41 +08:00
parent 391cb3e2d2
commit d8e7cfb237
117 changed files with 3937 additions and 78 deletions
+26 -4
View File
@@ -55,12 +55,34 @@ run bash -lc "cd '$ROOT/shorebird/packages/shorebird_cli' && '$DART_BIN' pub get
run bash -lc "cd '$ROOT/shorebird/packages/open_aot_patch_tools' && '$DART_BIN' pub get && '$DART_BIN' test"
run bash -lc "cd '$ROOT/shorebird-server' && '$GO_BIN' test ./..."
AOT_ARGS="$ROOT/dart-sdk-new/out/ReleaseX64AotPatch/args.gn"
if [[ -f "$AOT_ARGS" ]]; then
run bash -lc "cd '$ROOT/testapps/license_flavor_patch_test' && '$FLUTTER_BIN' pub get && '$DART_BIN' run tool/verify_aot_patch.dart"
AOT_PATCH_BUILD_DIR="${AOT_PATCH_BUILD_DIR:-}"
if [[ -z "$AOT_PATCH_BUILD_DIR" ]]; then
for candidate in \
"$ROOT/dart-sdk-new/xcodebuild/ReleaseARM64" \
"$ROOT/dart-sdk-new/out/ReleaseARM64AotPatch" \
"$ROOT/dart-sdk-new/out/ReleaseX64AotPatch"; do
if [[ -f "$candidate/args.gn" ]]; then
AOT_PATCH_BUILD_DIR="$candidate"
break
fi
done
fi
if [[ -n "$AOT_PATCH_BUILD_DIR" && -f "$AOT_PATCH_BUILD_DIR/args.gn" ]]; then
run bash -lc "cd '$ROOT/testapps/license_flavor_patch_test' && '$FLUTTER_BIN' pub get && AOT_PATCH_BUILD_DIR='$AOT_PATCH_BUILD_DIR' '$DART_BIN' run tool/verify_aot_patch.dart"
else
echo
echo "==> skipping license/flavor AOT verification; missing $AOT_ARGS"
echo "==> skipping license/flavor AOT verification; missing AOT patch args.gn"
fi
if [[ "$PLATFORM" == "macos" ]]; then
if [[ -f "$ROOT/flutter/engine/src/out/ios_release/args.gn" &&
-f "$ROOT/flutter/engine/src/out/host_release_arm64/args.gn" ]]; then
run "$ROOT/scripts/verify_ios_interpreter_route.sh"
else
echo
echo "==> skipping iOS interpreter route verification; missing ios_release or host_release_arm64 args.gn"
fi
fi
if [[ "$PLATFORM" == "macos" && "${RUN_IOS_SMOKE:-0}" == "1" ]]; then
+37 -2
View File
@@ -2,21 +2,56 @@
set -euo pipefail
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
DART_SRC="${DART_SRC:-$ROOT/dart-sdk-new}"
DART_TARGET="$ROOT/flutter/engine/src/flutter/third_party/dart"
UPDATER_SRC="${UPDATER_SRC:-$ROOT/updater}"
UPDATER_URL="${UPDATER_URL:-https://github.com/shorebirdtech/updater.git}"
TARGET="$ROOT/flutter/engine/src/flutter/third_party/updater"
is_git_checkout() {
git -C "$1" rev-parse --git-dir >/dev/null 2>&1
}
echo "[open-source-sync] dart source: $DART_SRC"
echo "[open-source-sync] dart target: $DART_TARGET"
echo "[open-source-sync] updater source: $UPDATER_SRC"
echo "[open-source-sync] target: $TARGET"
mkdir -p "$(dirname "$DART_TARGET")"
mkdir -p "$(dirname "$TARGET")"
if [[ -L "$DART_TARGET" ]]; then
echo "[open-source-sync] Dart target is already a symlink."
elif is_git_checkout "$DART_TARGET"; then
echo "[open-source-sync] Dart target is already a git checkout."
elif [[ -e "$DART_TARGET" ]]; then
echo "Dart target exists but is not a symlink or git checkout: $DART_TARGET" >&2
exit 1
elif is_git_checkout "$DART_SRC"; then
rel_target="$(python3 - "$DART_TARGET" "$DART_SRC" <<'PY'
import os
import sys
print(os.path.relpath(sys.argv[2], os.path.dirname(sys.argv[1])))
PY
)"
ln -s "$rel_target" "$DART_TARGET"
echo "[open-source-sync] linked Dart SDK checkout into Flutter engine."
else
echo "Dart source checkout is missing: $DART_SRC" >&2
exit 1
fi
if [[ ! -f "$DART_TARGET/runtime/vm/dart_api_impl.h" ]]; then
echo "Dart checkout is missing runtime/vm/dart_api_impl.h" >&2
exit 1
fi
if [[ -L "$TARGET" ]]; then
echo "[open-source-sync] updater target is already a symlink."
exit 0
fi
if [[ -d "$TARGET/.git" ]]; then
if is_git_checkout "$TARGET"; then
echo "[open-source-sync] updating existing updater checkout."
git -C "$TARGET" fetch --tags origin
git -C "$TARGET" checkout "${UPDATER_REVISION:-main}"
@@ -26,7 +61,7 @@ if [[ -d "$TARGET/.git" ]]; then
elif [[ -e "$TARGET" ]]; then
echo "target exists but is not a symlink or git checkout: $TARGET" >&2
exit 1
elif [[ -d "$UPDATER_SRC/.git" ]]; then
elif is_git_checkout "$UPDATER_SRC"; then
rel_target="$(python3 - "$TARGET" "$UPDATER_SRC" <<'PY'
import os
import sys
+245
View File
@@ -0,0 +1,245 @@
#!/usr/bin/env bash
set -euo pipefail
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
IOS_ENGINE_DIR="${IOS_ENGINE_DIR:-$ROOT/flutter/engine/src/out/ios_release}"
HOST_ENGINE_DIR="${HOST_ENGINE_DIR:-$ROOT/flutter/engine/src/out/host_release_arm64}"
IOS_APP_BUNDLE="${IOS_APP_BUNDLE:-}"
IOS_IPA="${IOS_IPA:-}"
IOS_PATCH_ARTIFACT="${IOS_PATCH_ARTIFACT:-}"
APP_STORE_STRICT="${APP_STORE_STRICT:-0}"
CHECKED_APP_BUNDLE=""
CHECKED_IPA=""
CHECKED_PATCH_ARTIFACT=""
ENTITLEMENTS_CHECKED=0
APP_STORE_STRICT_CHECKED=0
CLEANUP_DIR=""
cleanup() {
if [[ -n "$CLEANUP_DIR" && -d "$CLEANUP_DIR" ]]; then
rm -rf "$CLEANUP_DIR"
fi
}
trap cleanup EXIT
fail() {
echo "error: $*" >&2
exit 1
}
read_gn_value() {
local file="$1"
local key="$2"
awk -v key="$key" '
$1 == key && $2 == "=" {
value = $0
sub("^[[:space:]]*" key "[[:space:]]*=[[:space:]]*", "", value)
print value
found = 1
exit
}
END {
if (!found) {
exit 1
}
}
' "$file"
}
require_gn_value() {
local file="$1"
local key="$2"
local expected="$3"
local actual
if ! actual="$(read_gn_value "$file" "$key")"; then
fail "$file is missing required GN arg $key"
fi
if [[ "$actual" != "$expected" ]]; then
fail "$file has $key = $actual; expected $expected"
fi
}
verify_ios_engine_args() {
local args_file="$IOS_ENGINE_DIR/args.gn"
[[ -f "$args_file" ]] || fail "missing iOS engine args: $args_file"
require_gn_value "$args_file" target_os '"ios"'
require_gn_value "$args_file" dart_dynamic_modules false
require_gn_value "$args_file" dart_enable_shorebird_interpreter true
require_gn_value "$args_file" shorebird_use_interpreter true
require_gn_value "$args_file" shorebird_enable_aot_patching false
}
verify_host_engine_args() {
local args_file="$HOST_ENGINE_DIR/args.gn"
[[ -f "$args_file" ]] || fail "missing host engine args: $args_file"
require_gn_value "$args_file" target_os '"mac"'
require_gn_value "$args_file" dart_dynamic_modules false
require_gn_value "$args_file" dart_enable_shorebird_interpreter true
require_gn_value "$args_file" shorebird_use_interpreter true
}
app_bundle_from_ipa() {
[[ -f "$IOS_IPA" ]] || fail "missing iOS IPA: $IOS_IPA"
command -v unzip >/dev/null 2>&1 || fail "unzip is required to inspect IOS_IPA"
CLEANUP_DIR="$(mktemp -d "${TMPDIR:-/tmp}/ios-route.XXXXXX")"
unzip -qq "$IOS_IPA" -d "$CLEANUP_DIR"
local payload_dir="$CLEANUP_DIR/Payload"
[[ -d "$payload_dir" ]] || fail "IPA does not contain a Payload directory"
local app_bundle
app_bundle="$(find "$payload_dir" -maxdepth 1 -type d -name "*.app" -print -quit)"
[[ -n "$app_bundle" ]] || fail "IPA does not contain a Payload/*.app bundle"
IOS_APP_BUNDLE="$app_bundle"
CHECKED_IPA="$IOS_IPA"
}
verify_entitlements() {
local app_bundle="$1"
local entitlements
if ! command -v codesign >/dev/null 2>&1; then
if [[ "$APP_STORE_STRICT" == "1" ]]; then
fail "codesign is required for APP_STORE_STRICT=1 entitlement checks"
fi
echo "warning: codesign not found; skipping entitlement checks" >&2
return 0
fi
if ! entitlements="$(codesign -d --entitlements :- "$app_bundle" 2>/dev/null)"; then
if [[ "$APP_STORE_STRICT" == "1" ]]; then
fail "failed to read app entitlements with codesign: $app_bundle"
fi
echo "warning: failed to read app entitlements; skipping entitlement checks" >&2
return 0
fi
local forbidden_entitlements=(
"com.apple.security.cs.allow-jit"
"com.apple.security.cs.allow-unsigned-executable-memory"
"com.apple.security.cs.disable-executable-page-protection"
"com.apple.security.cs.dynamic-codesigning"
)
local entitlement
for entitlement in "${forbidden_entitlements[@]}"; do
if grep -Fq "<key>$entitlement</key>" <<<"$entitlements"; then
fail "app entitlement enables executable-memory behavior: $entitlement"
fi
done
ENTITLEMENTS_CHECKED=1
if [[ "$APP_STORE_STRICT" == "1" ]]; then
local compact_entitlements
compact_entitlements="$(printf '%s' "$entitlements" | tr -d '\n\r\t ')"
if grep -Fq "<key>get-task-allow</key><true/>" <<<"$compact_entitlements"; then
fail "APP_STORE_STRICT=1 rejects development entitlement get-task-allow=true"
fi
APP_STORE_STRICT_CHECKED=1
fi
}
verify_app_bundle() {
if [[ -n "$IOS_APP_BUNDLE" && -n "$IOS_IPA" ]]; then
fail "set only one of IOS_APP_BUNDLE or IOS_IPA"
fi
if [[ -n "$IOS_IPA" ]]; then
app_bundle_from_ipa
fi
[[ -n "$IOS_APP_BUNDLE" ]] || return 0
[[ -d "$IOS_APP_BUNDLE" ]] || fail "missing iOS app bundle: $IOS_APP_BUNDLE"
CHECKED_APP_BUNDLE="$IOS_APP_BUNDLE"
local bundled_patch
bundled_patch="$(
find "$IOS_APP_BUNDLE" \
\( -path "*/shorebird_updater/*" -o -name "dlc.vmcode" \) \
-print -quit
)"
if [[ -n "$bundled_patch" ]]; then
fail "reviewed app bundle contains a seeded patch payload: $bundled_patch"
fi
verify_entitlements "$IOS_APP_BUNDLE"
}
file_magic_hex() {
od -An -tx1 -N4 "$1" | tr -d ' \n'
}
verify_patch_artifact() {
[[ -n "$IOS_PATCH_ARTIFACT" ]] || return 0
[[ -f "$IOS_PATCH_ARTIFACT" ]] || fail "missing iOS patch artifact: $IOS_PATCH_ARTIFACT"
local magic
magic="$(file_magic_hex "$IOS_PATCH_ARTIFACT")"
case "$magic" in
feedface|cefaedfe|feedfacf|cffaedfe)
fail "iOS interpreter route must not use a Mach-O native patch artifact: $IOS_PATCH_ARTIFACT"
;;
7f454c46)
fail "iOS interpreter route must not use an ELF native patch artifact: $IOS_PATCH_ARTIFACT"
;;
esac
local compact_json
compact_json="$(LC_ALL=C tr -d '[:space:]' < "$IOS_PATCH_ARTIFACT")"
if [[ "$compact_json" != \{* ]]; then
fail "iOS patch artifact must be the encrypted open JSON wrapper, not a raw native/code payload"
fi
if ! grep -Fq '"format":"open-aot-vmcode-encrypted-v1"' <<<"$compact_json"; then
fail "iOS patch artifact is not an open encrypted VM code artifact"
fi
if grep -Fq '"runtime_mode":"dart-dynamic-modules"' <<<"$compact_json" ||
grep -Fq '"runtime_mode":"dynamic-modules"' <<<"$compact_json"; then
fail "iOS patch artifact uses DART_DYNAMIC_MODULES runtime mode"
fi
if ! grep -Fq '"runtime_mode":"dart-bytecode-interpreter"' <<<"$compact_json"; then
fail "iOS patch artifact must declare runtime_mode dart-bytecode-interpreter"
fi
if ! grep -Fq '"target_os":"ios"' <<<"$compact_json"; then
fail "iOS patch artifact must target iOS"
fi
if ! grep -Fq '"target_arch":"arm64"' <<<"$compact_json"; then
fail "iOS patch artifact must target arm64"
fi
if ! grep -Fq '"payload_kind":"full-snapshot"' <<<"$compact_json"; then
fail "current iOS interpreter mapper requires payload_kind full-snapshot"
fi
CHECKED_PATCH_ARTIFACT="$IOS_PATCH_ARTIFACT"
}
verify_ios_engine_args
verify_host_engine_args
verify_app_bundle
verify_patch_artifact
cat <<EOF
iOS interpreter route verified:
iOS engine args: $IOS_ENGINE_DIR/args.gn
host engine args: $HOST_ENGINE_DIR/args.gn
dynamic modules: disabled
iOS patch mode: Dart bytecode interpreter
EOF
if [[ -n "$CHECKED_IPA" ]]; then
echo " IPA: inspected Payload/*.app"
fi
if [[ -n "$CHECKED_APP_BUNDLE" ]]; then
echo " app bundle: no bundled shorebird patch payload"
fi
if [[ "$ENTITLEMENTS_CHECKED" == "1" ]]; then
echo " entitlements: no JIT/unsigned-executable-memory entitlement"
fi
if [[ "$APP_STORE_STRICT_CHECKED" == "1" ]]; then
echo " App Store strict: get-task-allow is not true"
fi
if [[ -n "$CHECKED_PATCH_ARTIFACT" ]]; then
echo " patch artifact: encrypted interpreter full-snapshot for ios/arm64"
fi
+64 -2
View File
@@ -3,13 +3,21 @@ set -euo pipefail
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
PLATFORM="${1:-}"
INCLUDE_ENGINE_DEPS="${INCLUDE_ENGINE_DEPS:-0}"
case "$PLATFORM" in
linux)
TARGET_OS='["linux"]'
DART_DOWNLOAD_ANDROID_DEPS="False"
;;
macos)
TARGET_OS='["mac", "ios"]'
if [[ "$INCLUDE_ENGINE_DEPS" == "1" ]]; then
TARGET_OS='["mac", "ios", "android"]'
DART_DOWNLOAD_ANDROID_DEPS="True"
else
TARGET_OS='["mac", "ios"]'
DART_DOWNLOAD_ANDROID_DEPS="False"
fi
;;
*)
echo "usage: $0 <linux|macos>" >&2
@@ -27,7 +35,7 @@ solutions = [
"custom_deps": {},
"custom_vars": {
"dart_root": "dart-sdk-new",
"download_android_deps": False,
"download_android_deps": $DART_DOWNLOAD_ANDROID_DEPS,
"checkout_javascript_engines": False,
"checkout_benchmarks_internal": False,
"checkout_flute": False,
@@ -38,3 +46,57 @@ target_os = $TARGET_OS
EOF
echo "Wrote $ROOT/.gclient for $PLATFORM with target_os = $TARGET_OS"
if [[ "$INCLUDE_ENGINE_DEPS" == "1" ]]; then
cat > "$ROOT/flutter/.gclient" <<EOF
solutions = [
{
"name": ".",
"url": "https://git.tonycloud.org/flutter/flutter.git",
"deps_file": "DEPS",
"managed": False,
"custom_deps": {
"engine/src/flutter/third_party/dart": None,
"engine/src/flutter/third_party/dart/third_party/binaryen/src": None,
"engine/src/flutter/third_party/dart/third_party/devtools": None,
"engine/src/flutter/third_party/dart/third_party/perfetto/src": None,
"engine/src/flutter/third_party/dart/third_party/pkg/core": None,
"engine/src/flutter/third_party/dart/third_party/pkg/dart_style": None,
"engine/src/flutter/third_party/dart/third_party/pkg/dartdoc": None,
"engine/src/flutter/third_party/dart/third_party/pkg/ecosystem": None,
"engine/src/flutter/third_party/dart/third_party/pkg/http": None,
"engine/src/flutter/third_party/dart/third_party/pkg/i18n": None,
"engine/src/flutter/third_party/dart/third_party/pkg/leak_tracker": None,
"engine/src/flutter/third_party/dart/third_party/pkg/native": None,
"engine/src/flutter/third_party/dart/third_party/pkg/protobuf": None,
"engine/src/flutter/third_party/dart/third_party/pkg/pub": None,
"engine/src/flutter/third_party/dart/third_party/pkg/shelf": None,
"engine/src/flutter/third_party/dart/third_party/pkg/sync_http": None,
"engine/src/flutter/third_party/dart/third_party/pkg/tar": None,
"engine/src/flutter/third_party/dart/third_party/pkg/test": None,
"engine/src/flutter/third_party/dart/third_party/pkg/tools": None,
"engine/src/flutter/third_party/dart/third_party/pkg/vector_math": None,
"engine/src/flutter/third_party/dart/third_party/pkg/web": None,
"engine/src/flutter/third_party/dart/third_party/pkg/webdev": None,
"engine/src/flutter/third_party/dart/third_party/pkg/webdriver": None,
"engine/src/flutter/third_party/dart/third_party/pkg/webkit_inspection_protocol": None,
"engine/src/flutter/third_party/dart/tools/sdks/dart-sdk": None,
"engine/src/flutter/third_party/updater": None,
},
"custom_vars": {
"download_dart_sdk": False,
"download_android_deps": True,
"download_emsdk": True,
"setup_githooks": False,
"use_rbe": False,
},
},
]
target_os = $TARGET_OS
EOF
echo "Wrote $ROOT/flutter/.gclient for Flutter engine dependencies"
else
rm -f "$ROOT/flutter/.gclient"
fi
echo "Flutter engine dependency sync: $INCLUDE_ENGINE_DEPS"