From e9cb87e6e1b25eca9ebb187d9a332aee99c47533 Mon Sep 17 00:00:00 2001 From: Feodor Fitsner Date: Sat, 20 Jun 2026 18:52:20 -0700 Subject: [PATCH 01/10] serious_python_darwin: add Swift Package Manager support (dual with CocoaPods) Adds an SPM build path alongside the existing podspec so apps can build the darwin plugin either way (CocoaPods goes read-only Dec 2026; Flutter ships SPM on by default since 3.44). - darwin/serious_python_darwin/Package.swift (new): builds the plugin under SPM. All native inputs are local-path binaryTargets the package step stages into the package dir before `flutter build` (mirroring the podspec prepare_command): dart_bridge (static, link-only via -ObjC -all_load -lc++), platform-conditional Python_ios/Python_macos (dynamic, embedded+signed), and the iOS C-extensions enumerated from extra-xcframeworks/. stdlib/site-packages/app ship as `.copy` resources -> Bundle.module. Everything existence-guarded so the manifest parses in an unstaged checkout. Reads SP_NATIVE_SET + SERIOUS_PYTHON_VERSION as tracked cache-bust keys, since SwiftPM caches the package graph on manifest text + env, not on the staged dirs it enumerates. - SeriousPythonPlugin.swift: moved Classes/ -> Sources/serious_python_darwin/ (the SPM target layout; podspec source_files repointed so both build systems compile it). getResourcePath now resolves Bundle.module under #if SWIFT_PACKAGE and the framework python.bundle under CocoaPods. - Resources/{stdlib,site-packages,app}/.keep: committed placeholders so the resource paths exist (and Bundle.module is generated) before the package step fills them. - pubspec.yaml: environment floor -> Flutter >=3.44 / Dart >=3.11. - .gitignore: ignore the staged xcframeworks + resource contents (keep .keep). Build-verified dual: SPM on -> iOS + macOS; SPM off -> CocoaPods. The package-step staging that materializes the inputs for a real `flet build` is the next change. --- src/serious_python_darwin/darwin/.gitignore | 13 ++- .../darwin/serious_python_darwin.podspec | 2 +- .../serious_python_darwin/Package.swift | 102 ++++++++++++++++++ .../serious_python_darwin/Resources/app/.keep | 2 + .../Resources/site-packages/.keep | 2 + .../Resources/stdlib/.keep | 2 + .../SeriousPythonPlugin.swift | 51 +++++---- src/serious_python_darwin/pubspec.yaml | 6 +- 8 files changed, 157 insertions(+), 23 deletions(-) create mode 100644 src/serious_python_darwin/darwin/serious_python_darwin/Package.swift create mode 100644 src/serious_python_darwin/darwin/serious_python_darwin/Sources/serious_python_darwin/Resources/app/.keep create mode 100644 src/serious_python_darwin/darwin/serious_python_darwin/Sources/serious_python_darwin/Resources/site-packages/.keep create mode 100644 src/serious_python_darwin/darwin/serious_python_darwin/Sources/serious_python_darwin/Resources/stdlib/.keep rename src/serious_python_darwin/darwin/{Classes => serious_python_darwin/Sources/serious_python_darwin}/SeriousPythonPlugin.swift (54%) diff --git a/src/serious_python_darwin/darwin/.gitignore b/src/serious_python_darwin/darwin/.gitignore index 0c885071..52f39d72 100644 --- a/src/serious_python_darwin/darwin/.gitignore +++ b/src/serious_python_darwin/darwin/.gitignore @@ -35,4 +35,15 @@ Icon? /Flutter/Generated.xcconfig /Flutter/ephemeral/ -/Flutter/flutter_export_environment.sh \ No newline at end of file +/Flutter/flutter_export_environment.sh + +# --- SwiftPM staging (materialized by serious_python's package step) --- +serious_python_darwin/Python-ios.xcframework +serious_python_darwin/Python-macos.xcframework +serious_python_darwin/dart_bridge.xcframework +serious_python_darwin/extra-xcframeworks/ +serious_python_darwin/.build/ +serious_python_darwin/.swiftpm/ +# resource trees are staged; keep only the committed .keep placeholders +serious_python_darwin/Sources/serious_python_darwin/Resources/*/* +!serious_python_darwin/Sources/serious_python_darwin/Resources/*/.keep diff --git a/src/serious_python_darwin/darwin/serious_python_darwin.podspec b/src/serious_python_darwin/darwin/serious_python_darwin.podspec index d66a2c95..a40434af 100644 --- a/src/serious_python_darwin/darwin/serious_python_darwin.podspec +++ b/src/serious_python_darwin/darwin/serious_python_darwin.podspec @@ -19,7 +19,7 @@ Pod::Spec.new do |s| # framework. Python.xcframework is also static, so this was always # implicitly the case — just being explicit now. s.static_framework = true - s.source_files = ['Classes/**/*'] + s.source_files = ['serious_python_darwin/Sources/serious_python_darwin/**/*.swift'] s.ios.dependency 'Flutter' s.osx.dependency 'FlutterMacOS' s.ios.deployment_target = '13.0' diff --git a/src/serious_python_darwin/darwin/serious_python_darwin/Package.swift b/src/serious_python_darwin/darwin/serious_python_darwin/Package.swift new file mode 100644 index 00000000..bd517d29 --- /dev/null +++ b/src/serious_python_darwin/darwin/serious_python_darwin/Package.swift @@ -0,0 +1,102 @@ +// swift-tools-version: 5.9 +import PackageDescription +import Foundation + +// === serious_python_darwin — Swift Package Manager manifest === +// +// Dual build system: this package builds the plugin under SwiftPM; the sibling +// `serious_python_darwin.podspec` builds the same Sources/ under CocoaPods. +// +// The Python runtime, dart_bridge, the app's native C-extensions, and the +// stdlib/site-packages/app trees are NOT committed. serious_python's `package` +// command (driven by `flet build`) materializes them into THIS package directory +// before `flutter build`, exactly as the CocoaPods `prepare_command` does: +// +// /Python-ios.xcframework, Python-macos.xcframework Python runtime (dynamic) +// /dart_bridge.xcframework FFI transport (static) +// /extra-xcframeworks/*.xcframework iOS native extensions +// (stdlib lib-dynload + site-packages) +// /Sources/serious_python_darwin/Resources/{stdlib,site-packages,app} +// +// On iOS, native extensions ship as embedded+signed frameworks (CPython's finder +// dlopen's them by their bundled path). On macOS they ride flat inside the +// site-packages / stdlib resource trees and load in place. +// +// CACHE-BUST CONTRACT: SwiftPM caches the resolved package graph keyed on this +// manifest's TEXT + the environment variables it reads — NOT on the staged dirs it +// enumerates. So the package step exports `SP_NATIVE_SET`, a hash over everything it +// staged (Python full version, dart_bridge version, the sorted extension set, the +// resource trees). Reading it here makes it a tracked key, so any change to the +// staged inputs forces re-resolution. `SERIOUS_PYTHON_VERSION` (the project's +// version-selection contract) is read for the same reason. +let env = ProcessInfo.processInfo.environment +_ = env["SP_NATIVE_SET"] +_ = env["SERIOUS_PYTHON_VERSION"] + +let pkgDir = URL(fileURLWithPath: #filePath).deletingLastPathComponent() +func staged(_ rel: String) -> Bool { + FileManager.default.fileExists(atPath: pkgDir.appendingPathComponent(rel).path) +} + +// Native binary targets + their plugin-target dependencies. All existence-guarded so +// the manifest still parses in an unstaged checkout (IDE / `dart pub get`); a real +// `flutter build` always stages first. +var binaryTargets: [Target] = [] +var deps: [Target.Dependency] = [.product(name: "FlutterFramework", package: "FlutterFramework")] + +// dart_bridge: static archive, link-only (forced in via -all_load). Multi-platform. +if staged("dart_bridge.xcframework") { + binaryTargets.append(.binaryTarget(name: "dart_bridge", path: "dart_bridge.xcframework")) + deps.append("dart_bridge") +} +// Python.framework: dynamic -> embedded + auto-signed. iOS and macOS ship separate +// xcframeworks, so each is platform-conditional. +if staged("Python-ios.xcframework") { + binaryTargets.append(.binaryTarget(name: "Python_ios", path: "Python-ios.xcframework")) + deps.append(.target(name: "Python_ios", condition: .when(platforms: [.iOS]))) +} +if staged("Python-macos.xcframework") { + binaryTargets.append(.binaryTarget(name: "Python_macos", path: "Python-macos.xcframework")) + deps.append(.target(name: "Python_macos", condition: .when(platforms: [.macOS]))) +} +// iOS native C-extensions: each staged *.xcframework -> embedded+signed framework. +let extraDir = pkgDir.appendingPathComponent("extra-xcframeworks") +if let items = try? FileManager.default.contentsOfDirectory( + at: extraDir, includingPropertiesForKeys: nil) { + for item in items.sorted(by: { $0.lastPathComponent < $1.lastPathComponent }) + where item.pathExtension == "xcframework" { + let name = item.deletingPathExtension().lastPathComponent + binaryTargets.append(.binaryTarget(name: name, path: "extra-xcframeworks/\(name).xcframework")) + deps.append(.target(name: name, condition: .when(platforms: [.iOS]))) + } +} + +let package = Package( + name: "serious_python_darwin", + platforms: [.iOS("13.0"), .macOS("11.0")], + products: [ + .library(name: "serious-python-darwin", targets: ["serious_python_darwin"]), + ], + dependencies: [ + .package(name: "FlutterFramework", path: "../FlutterFramework"), + ], + targets: [ + .target( + name: "serious_python_darwin", + dependencies: deps, + resources: [ + // Staged trees. .copy (verbatim) preserves the layout PYTHONHOME / + // PYTHONPATH expect; committed `.keep` placeholders keep these paths + // valid (and Bundle.module generated) in an unstaged checkout. + .copy("Resources/stdlib"), + .copy("Resources/site-packages"), + .copy("Resources/app"), + ], + linkerSettings: [ + // Reproduces the podspec OTHER_LDFLAGS = '-ObjC -all_load -lc++'. + .unsafeFlags(["-ObjC", "-all_load"]), + .linkedLibrary("c++"), + ] + ), + ] + binaryTargets +) diff --git a/src/serious_python_darwin/darwin/serious_python_darwin/Sources/serious_python_darwin/Resources/app/.keep b/src/serious_python_darwin/darwin/serious_python_darwin/Sources/serious_python_darwin/Resources/app/.keep new file mode 100644 index 00000000..be174385 --- /dev/null +++ b/src/serious_python_darwin/darwin/serious_python_darwin/Sources/serious_python_darwin/Resources/app/.keep @@ -0,0 +1,2 @@ +# Placeholder so the SwiftPM resource path exists in an unstaged checkout; +# serious_python_darwin's package step fills this directory before `flutter build`. diff --git a/src/serious_python_darwin/darwin/serious_python_darwin/Sources/serious_python_darwin/Resources/site-packages/.keep b/src/serious_python_darwin/darwin/serious_python_darwin/Sources/serious_python_darwin/Resources/site-packages/.keep new file mode 100644 index 00000000..be174385 --- /dev/null +++ b/src/serious_python_darwin/darwin/serious_python_darwin/Sources/serious_python_darwin/Resources/site-packages/.keep @@ -0,0 +1,2 @@ +# Placeholder so the SwiftPM resource path exists in an unstaged checkout; +# serious_python_darwin's package step fills this directory before `flutter build`. diff --git a/src/serious_python_darwin/darwin/serious_python_darwin/Sources/serious_python_darwin/Resources/stdlib/.keep b/src/serious_python_darwin/darwin/serious_python_darwin/Sources/serious_python_darwin/Resources/stdlib/.keep new file mode 100644 index 00000000..be174385 --- /dev/null +++ b/src/serious_python_darwin/darwin/serious_python_darwin/Sources/serious_python_darwin/Resources/stdlib/.keep @@ -0,0 +1,2 @@ +# Placeholder so the SwiftPM resource path exists in an unstaged checkout; +# serious_python_darwin's package step fills this directory before `flutter build`. diff --git a/src/serious_python_darwin/darwin/Classes/SeriousPythonPlugin.swift b/src/serious_python_darwin/darwin/serious_python_darwin/Sources/serious_python_darwin/SeriousPythonPlugin.swift similarity index 54% rename from src/serious_python_darwin/darwin/Classes/SeriousPythonPlugin.swift rename to src/serious_python_darwin/darwin/serious_python_darwin/Sources/serious_python_darwin/SeriousPythonPlugin.swift index 07224c3d..d676aa0b 100644 --- a/src/serious_python_darwin/darwin/Classes/SeriousPythonPlugin.swift +++ b/src/serious_python_darwin/darwin/serious_python_darwin/Sources/serious_python_darwin/SeriousPythonPlugin.swift @@ -45,25 +45,38 @@ public class SeriousPythonPlugin: NSObject, FlutterPlugin { public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) { switch call.method { case "getResourcePath": - // The python.bundle that prepare_{ios,macos}.sh assembles ends up - // inside this plugin's framework bundle as a Resources subbundle. - // Dart calls this to discover the stdlib / site-packages layout - // before invoking `serious_python_run`. - guard let frameworkBundle = Bundle(for: type(of: self)).resourceURL else { - result(FlutterError(code: "FRAMEWORK_BUNDLE_ERROR", - message: "Failed to get framework resource URL", - details: nil)) - return - } - let pythonBundleURL = frameworkBundle.appendingPathComponent("python.bundle") - guard let pythonBundle = Bundle(url: pythonBundleURL), - let resourcePath = pythonBundle.resourcePath else { - result(FlutterError(code: "PYTHON_BUNDLE_ERROR", - message: "Failed to load python.bundle", - details: pythonBundleURL.path)) - return - } - result(resourcePath) + // Dart calls this to discover the stdlib / site-packages / app layout + // before invoking `serious_python_run`. The two build systems put the + // trees in different bundles: + #if SWIFT_PACKAGE + // SwiftPM: staged as `.copy` resources into Bundle.module, whose + // resourcePath contains stdlib/ site-packages/ app/ directly. + guard let resourcePath = Bundle.module.resourcePath else { + result(FlutterError(code: "PYTHON_BUNDLE_ERROR", + message: "Failed to resolve Bundle.module resourcePath", + details: nil)) + return + } + result(resourcePath) + #else + // CocoaPods: the python.bundle that prepare_{ios,macos}.sh assembles + // lives inside the plugin framework as a Resources subbundle. + guard let frameworkBundle = Bundle(for: type(of: self)).resourceURL else { + result(FlutterError(code: "FRAMEWORK_BUNDLE_ERROR", + message: "Failed to get framework resource URL", + details: nil)) + return + } + let pythonBundleURL = frameworkBundle.appendingPathComponent("python.bundle") + guard let pythonBundle = Bundle(url: pythonBundleURL), + let resourcePath = pythonBundle.resourcePath else { + result(FlutterError(code: "PYTHON_BUNDLE_ERROR", + message: "Failed to load python.bundle", + details: pythonBundleURL.path)) + return + } + result(resourcePath) + #endif default: result(FlutterMethodNotImplemented) diff --git a/src/serious_python_darwin/pubspec.yaml b/src/serious_python_darwin/pubspec.yaml index 68bb6234..c5b74071 100644 --- a/src/serious_python_darwin/pubspec.yaml +++ b/src/serious_python_darwin/pubspec.yaml @@ -5,8 +5,10 @@ repository: https://github.com/flet-dev/serious-python version: 4.0.0 environment: - sdk: ">=3.0.0 <4.0.0" - flutter: ">=3.7.0" + # Swift Package Manager support (the FlutterFramework SwiftPM convention) lands + # in Flutter 3.44 / Dart 3.11; this is also the de-facto floor from dart_bridge. + sdk: ">=3.11.0 <4.0.0" + flutter: ">=3.44.0" dependencies: flutter: From 48b0ccd28210b2c1e4e5010880e3bf2e92dbf162 Mon Sep 17 00:00:00 2001 From: Feodor Fitsner Date: Sat, 20 Jun 2026 19:15:06 -0700 Subject: [PATCH 02/10] serious_python_darwin: add stage_spm.sh (host-side SPM staging) Maps the assembled dist_ tree (Python.xcframework, dart_bridge, iOS site-xcframeworks, stdlib/site-packages/app) into the SwiftPM plugin layout that Package.swift consumes as local-path binaryTargets + .copy resources, and prints the SP_NATIVE_SET cache-bust key (hash of the staged native/resource set) for the caller to export into the flutter build env. This is the host-side equivalent of the CocoaPods prepare_command staging, needed because SPM has no pod-install hook. Verified end-to-end: a real macOS SPM build of run_example with the mapped real runtime starts Python, loads the staged stdlib + native lib-dynload extensions (_bz2/_hashlib/ _sqlite3), and runs main.py to completion (writes the expected result file). Invocation from package_command.dart (+ flet SPM enablement / SP_NATIVE_SET handoff) is the next change. --- src/serious_python_darwin/darwin/stage_spm.sh | 58 +++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100755 src/serious_python_darwin/darwin/stage_spm.sh diff --git a/src/serious_python_darwin/darwin/stage_spm.sh b/src/serious_python_darwin/darwin/stage_spm.sh new file mode 100755 index 00000000..0ebba398 --- /dev/null +++ b/src/serious_python_darwin/darwin/stage_spm.sh @@ -0,0 +1,58 @@ +#!/usr/bin/env bash +set -euo pipefail +# +# stage_spm.sh +# +# Host-side staging for the Swift Package Manager build path. Maps the assembled +# dist_ tree (produced by prepare_.sh + sync_site_packages.sh) +# into the SPM plugin layout under serious_python_darwin/, which Package.swift +# consumes as local-path binaryTargets + .copy resources. Prints the cache-bust +# key (SP_NATIVE_SET) on stdout — the caller exports it into the `flutter build` +# environment so SwiftPM re-resolves when the staged native set changes. +# +platform=${1:?usage: stage_spm.sh } +script_dir=$(cd "$(dirname "$0")" && pwd -P) +dist="$script_dir/dist_$platform" +pkg="$script_dir/serious_python_darwin" +res="$pkg/Sources/serious_python_darwin/Resources" + +[ -d "$dist" ] || { echo "stage_spm: $dist not found" >&2; exit 1; } + +# 1. Python runtime (dynamic framework -> embedded). Platform-specific path so a +# single shared manifest can carry both via platform-conditional binaryTargets. +rm -rf "$pkg/Python-$platform.xcframework" +cp -R "$dist/xcframeworks/Python.xcframework" "$pkg/Python-$platform.xcframework" + +# 2. dart_bridge (static, version-independent; same artifact in either dist). +rm -rf "$pkg/dart_bridge.xcframework" +cp -R "$dist/xcframeworks/dart_bridge.xcframework" "$pkg/dart_bridge.xcframework" + +# 3. iOS native C-extensions (stdlib lib-dynload + site-packages) -> enumerated +# binaryTargets. macOS has none: its .so's load flat from the resource trees. +rm -rf "$pkg/extra-xcframeworks" +if [ "$platform" = "ios" ] && [ -d "$dist/site-xcframeworks" ]; then + mkdir -p "$pkg/extra-xcframeworks" + cp -R "$dist"/site-xcframeworks/*.xcframework "$pkg/extra-xcframeworks/" 2>/dev/null || true +fi + +# 4. Resource trees (verbatim via rsync). Wipe prior content but keep the +# committed .keep placeholder so the path stays valid in a clean checkout. +for tree in stdlib site-packages app; do + dest="$res/$tree" + mkdir -p "$dest" + find "$dest" -mindepth 1 -not -name '.keep' -delete 2>/dev/null || true + [ -d "$dist/$tree" ] && rsync -a --exclude '.pod' "$dist/$tree/" "$dest/" +done + +# 5. Cache-bust key: platform + Python version + the staged native/resource set +# (path+size). Changes whenever requirements, app, or Python version change. +key_paths=("Python-$platform.xcframework" "Sources/serious_python_darwin/Resources") +[ -d "$pkg/extra-xcframeworks" ] && key_paths+=("extra-xcframeworks") +key=$( + { + echo "$platform ${SERIOUS_PYTHON_FULL_VERSION:-}" + ( cd "$pkg" && find "${key_paths[@]}" -type f -exec stat -f '%N %z' {} + \ + 2>/dev/null | sort ) + } | shasum -a 256 | cut -d' ' -f1 +) +echo "$key" From 13c2d6171412f9d0a8eadb79f65e24a30340a3fd Mon Sep 17 00:00:00 2001 From: Feodor Fitsner Date: Sat, 20 Jun 2026 19:28:58 -0700 Subject: [PATCH 03/10] serious_python: drive darwin SPM staging from the package command MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Under Swift Package Manager there's no pod-install hook, so the staging the podspec prepare_command does must run on the host before `flutter build`. - prepare_spm.sh : host-side equivalent of the podspec prepare_command — resolves version coordinates from python_versions.properties (same env overrides), runs prepare_.sh + sync_site_packages.sh, then stage_spm.sh to map the assembled dist into the Package.swift layout. Prints SP_NATIVE_SET on stdout (progress to stderr). - package_command.dart: when SERIOUS_PYTHON_DARWIN_SPM is set (by flet when the build uses SPM), after staging the app it resolves serious_python_darwin's darwin/ dir (SERIOUS_PYTHON_DARWIN_DIR override, else the flutter project's package_config.json), runs prepare_spm.sh, and writes the SP_NATIVE_SET key to build/.serious_python_spm_key (overridable) for `flet build` to export into the flutter build environment. Verified: invoking the package command for Darwin with SERIOUS_PYTHON_DARWIN_SPM resolves the plugin dir, stages the SPM layout, and writes the key file. --- src/serious_python/bin/package_command.dart | 75 +++++++++++++++++++ .../darwin/prepare_spm.sh | 30 ++++++++ 2 files changed, 105 insertions(+) create mode 100755 src/serious_python_darwin/darwin/prepare_spm.sh diff --git a/src/serious_python/bin/package_command.dart b/src/serious_python/bin/package_command.dart index c5dcca9d..4a02ca70 100644 --- a/src/serious_python/bin/package_command.dart +++ b/src/serious_python/bin/package_command.dart @@ -29,6 +29,16 @@ const flutterPackagesFlutterEnvironmentVariable = "SERIOUS_PYTHON_FLUTTER_PACKAGES"; const allowSourceDistrosEnvironmentVariable = "SERIOUS_PYTHON_ALLOW_SOURCE_DISTRIBUTIONS"; +// Swift Package Manager (darwin) host-side staging. When `SERIOUS_PYTHON_DARWIN_SPM` +// is set (by `flet build` when the build uses SPM), the package command runs the +// SPM equivalent of the podspec `prepare_command` — assembling the dist and mapping +// it into the plugin's Package.swift layout — since SPM has no pod-install hook. +// `SERIOUS_PYTHON_DARWIN_DIR` optionally overrides the resolved plugin `darwin/` dir; +// `SERIOUS_PYTHON_SPM_KEY_FILE` overrides where the SP_NATIVE_SET cache-bust key is +// written for the caller to export into the `flutter build` environment. +const darwinSpmEnvironmentVariable = "SERIOUS_PYTHON_DARWIN_SPM"; +const darwinDirEnvironmentVariable = "SERIOUS_PYTHON_DARWIN_DIR"; +const spmKeyFileEnvironmentVariable = "SERIOUS_PYTHON_SPM_KEY_FILE"; // Python runtime version data — `defaultPythonVersion`, `pythonReleases`, the // `*EnvironmentVariable` names, `dartBridgeVersion`, `pythonReleaseDate` — lives @@ -564,6 +574,15 @@ class PackageCommand extends Command { } await appStagingDir.create(recursive: true); await copyDirectory(tempDir, appStagingDir, tempDir.path, []); + + // Swift Package Manager (darwin) host-side staging: the podspec + // prepare_command doesn't run under SPM, so assemble the dist and map + // it into the plugin's Package.swift layout here (app is now staged). + if ((platform == "iOS" || platform == "Darwin") && + (Platform.environment[darwinSpmEnvironmentVariable] ?? "") + .isNotEmpty) { + await _stageDarwinSpm(platform, currentPath); + } } } catch (e) { stdout.writeln("Error: $e"); @@ -643,6 +662,62 @@ class PackageCommand extends Command { return proc.exitCode; } + // Run the darwin SPM staging (prepare_spm.sh: assemble dist + map into the + // plugin's Package.swift layout) and persist the SP_NATIVE_SET cache-bust key + // for `flet build` to export into the `flutter build` environment. + Future _stageDarwinSpm(String platform, String projectPath) async { + final darwinDir = await _resolveDarwinDir(projectPath); + if (darwinDir == null) { + stdout.writeln("SPM staging skipped: could not resolve serious_python_darwin " + "(set $darwinDirEnvironmentVariable or ensure " + ".dart_tool/package_config.json is present)."); + return; + } + final spmPlatform = platform == "iOS" ? "ios" : "macos"; + final script = path.join(darwinDir, "prepare_spm.sh"); + stdout.writeln("SPM: staging $spmPlatform via $script"); + final result = await Process.run("/bin/sh", [script, spmPlatform], + workingDirectory: darwinDir); + if ((result.stderr as String).isNotEmpty) { + verbose(result.stderr as String); + } + if (result.exitCode != 0) { + throw Exception("prepare_spm.sh failed (exit ${result.exitCode}):\n" + "${result.stderr}"); + } + // stage_spm.sh prints the key as its last stdout line. + final key = (result.stdout as String) + .trim() + .split("\n") + .where((l) => l.trim().isNotEmpty) + .last + .trim(); + final keyFile = Platform.environment[spmKeyFileEnvironmentVariable] ?? + path.join(projectPath, "build", ".serious_python_spm_key"); + await File(keyFile).parent.create(recursive: true); + await File(keyFile).writeAsString(key); + stdout.writeln("SPM: SP_NATIVE_SET=$key -> $keyFile"); + } + + // Resolve serious_python_darwin's `darwin/` directory — an explicit override + // (set by flet) wins, else read the flutter project's package config. + Future _resolveDarwinDir(String projectPath) async { + final override = Platform.environment[darwinDirEnvironmentVariable]; + if (override != null && override.isNotEmpty) return override; + final pc = + File(path.join(projectPath, ".dart_tool", "package_config.json")); + if (!await pc.exists()) return null; + final data = jsonDecode(await pc.readAsString()) as Map; + for (final pkg in (data["packages"] as List)) { + if (pkg["name"] == "serious_python_darwin") { + final base = Uri.directory(path.join(projectPath, ".dart_tool")); + final root = base.resolve(pkg["rootUri"] as String).toFilePath(); + return path.join(root, "darwin"); + } + } + return null; + } + Future zipDirectoryPosix(Directory source, File dest) async { final encoder = ZipFileEncoder(); encoder.create(dest.path); diff --git a/src/serious_python_darwin/darwin/prepare_spm.sh b/src/serious_python_darwin/darwin/prepare_spm.sh new file mode 100755 index 00000000..97aaee35 --- /dev/null +++ b/src/serious_python_darwin/darwin/prepare_spm.sh @@ -0,0 +1,30 @@ +#!/usr/bin/env bash +set -euo pipefail +# +# prepare_spm.sh +# +# Host-side equivalent of the podspec `prepare_command` for the Swift Package +# Manager build path (SPM has no pod-install hook). Downloads + extracts the +# Python/dart_bridge dist, syncs the app + site-packages into it, and maps the +# result into the SPM plugin layout. Prints SP_NATIVE_SET (the cache-bust key) +# on stdout; all progress goes to stderr so the caller can capture just the key. +# +# Version coordinates resolve from python_versions.properties (overridable via +# the same env vars the podspec honors), so the caller only passes the platform. +# +platform=${1:?usage: prepare_spm.sh } +script_dir=$(cd "$(dirname "$0")" && pwd -P) +props="$script_dir/python_versions.properties" + +prop() { grep "^$1=" "$props" 2>/dev/null | head -1 | cut -d= -f2-; } + +pyver=${SERIOUS_PYTHON_VERSION:-$(prop default_python_version)} +pyfull=${SERIOUS_PYTHON_FULL_VERSION:-$(prop "$pyver.full_version")} +builddate=${SERIOUS_PYTHON_BUILD_DATE:-$(prop python_build_release_date)} +dbver=${DART_BRIDGE_VERSION:-$(prop dart_bridge_version)} +[ -n "$pyfull" ] || { echo "prepare_spm: unknown SERIOUS_PYTHON_VERSION '$pyver'" >&2; exit 1; } + +echo "prepare_spm: $platform python=$pyfull build=$builddate dart_bridge=$dbver" >&2 +"$script_dir/prepare_$platform.sh" "$pyver" "$pyfull" "$builddate" "$dbver" >&2 +"$script_dir/sync_site_packages.sh" >&2 +SERIOUS_PYTHON_FULL_VERSION="$pyfull" "$script_dir/stage_spm.sh" "$platform" From 8bfe6f3d0242563882fe928ab86c11fe4d630438 Mon Sep 17 00:00:00 2001 From: Feodor Fitsner Date: Sat, 20 Jun 2026 20:00:56 -0700 Subject: [PATCH 04/10] serious_python(_darwin) 4.0.0: document SPM support + CI smoke test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - CHANGELOGs: describe the dual CocoaPods/SPM darwin build path — Package.swift, the host-side prepare_spm.sh/stage_spm.sh staging, SP_NATIVE_SET cache-bust, and the package command's SERIOUS_PYTHON_DARWIN_SPM staging orchestration. - CI: add a bridge_example_macos_spm job that enables Swift Package Manager, runs the package step with SERIOUS_PYTHON_DARWIN_SPM, exports SP_NATIVE_SET, and runs the integration test under SPM (one Python version; the CocoaPods jobs cover the full matrix). Guards the SPM path against regressions. --- .github/workflows/ci.yml | 42 ++++++++++++++++++++++++++ src/serious_python/CHANGELOG.md | 1 + src/serious_python_darwin/CHANGELOG.md | 4 +++ 3 files changed, 47 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 667dca93..dce117ef 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -106,6 +106,48 @@ jobs: flutter test integration_test/throughput_test.dart -d macos --dart-define=EXPECTED_PYTHON_VERSION=${{ matrix.python_version }} flutter test integration_test/memory_test.dart -d macos --dart-define=EXPECTED_PYTHON_VERSION=${{ matrix.python_version }} + # Smoke-test the Swift Package Manager build path (the CocoaPods jobs above + # cover the full python matrix; one version is enough to guard SPM). + bridge_example_macos_spm: + name: Test Bridge example on macOS via Swift Package Manager (Python 3.14) + runs-on: macos-26 + env: + SERIOUS_PYTHON_VERSION: '3.14' + steps: + - name: Checkout repository + uses: actions/checkout@v6 + + - name: Setup Flutter + uses: kuhnroyal/flutter-fvm-config-action/setup@v3 + with: + path: '.fvmrc' + cache: true + + - name: Cache flet downloads + uses: actions/cache@v5 + with: + path: ~/.flet/cache + key: flet-cache-${{ runner.os }}-${{ runner.arch }}-py3.14-${{ hashFiles('src/serious_python/lib/src/python_versions.dart', 'src/serious_python/bin/package_command.dart', 'src/serious_python_android/android/build.gradle', 'src/serious_python_darwin/darwin/prepare_macos.sh', 'src/serious_python_darwin/darwin/prepare_ios.sh', 'src/serious_python_windows/windows/CMakeLists.txt', 'src/serious_python_linux/linux/CMakeLists.txt') }} + restore-keys: | + flet-cache-${{ runner.os }}-${{ runner.arch }}-py3.14- + flet-cache-${{ runner.os }}-${{ runner.arch }}- + + - name: Enable Swift Package Manager + run: flutter config --enable-swift-package-manager + + - name: Package (SPM staging) + run integration test + working-directory: "src/serious_python/example/bridge_example" + run: | + # Host-side SPM staging (the podspec prepare_command has no SPM hook): + # the package command assembles + maps the runtime into the plugin's + # Package.swift layout and writes the SP_NATIVE_SET cache-bust key, + # which we export into the flutter build — the same handoff that + # `flet build` performs. + SERIOUS_PYTHON_DARWIN_SPM=1 \ + dart run serious_python:main package app/src --platform Darwin --python-version 3.14 + export SP_NATIVE_SET="$(cat build/.serious_python_spm_key)" + flutter test integration_test/interactivity_test.dart -d macos --dart-define=EXPECTED_PYTHON_VERSION=3.14 + bridge_example_ios: name: Test Bridge example on iOS (Python ${{ matrix.python_version }}) runs-on: macos-26 diff --git a/src/serious_python/CHANGELOG.md b/src/serious_python/CHANGELOG.md index a8bb99fc..f678a714 100644 --- a/src/serious_python/CHANGELOG.md +++ b/src/serious_python/CHANGELOG.md @@ -4,6 +4,7 @@ * **New `SeriousPython.prepareApp()`** — materializes the app (Android first-launch unpack) and returns the directory containing its entry point. **`SeriousPython.run()` now takes no `assetPath` argument** (it resolves the app via `prepareApp()`), sets the current directory to a writable per-app data dir (`/data`) — not the read-only bundle — so relative file writes / SQLite work, and runs `main.pyc`/`main.py` (or `appFileName`). * **Breaking change:** the `app.zip` asset convention and the runtime zip-extraction API are removed — `SeriousPython.run("app/app.zip")`, `extractAssetZip`, and `extractFileZip` no longer exist. Repackage with `dart run serious_python:main package -p ` and call `SeriousPython.run()` with no arguments. * **Android:** the runtime payload moved to `/flet/{app, stdlib.zip, sitepackages.zip, extract/}` (resolved via `getApplicationSupportDirectory()`; the custom `getFilesDir` method channel is dropped). User data in the sibling `/data` survives app updates. +* **Swift Package Manager (darwin) staging in the `package` command.** When `SERIOUS_PYTHON_DARWIN_SPM` is set (by `flet build` when the iOS/macOS build uses SPM), the `package` command runs the host-side equivalent of the podspec `prepare_command` — which SPM has no hook for — by resolving `serious_python_darwin`'s `darwin/` dir (`SERIOUS_PYTHON_DARWIN_DIR` override, else the project's `package_config.json`), invoking `prepare_spm.sh`, and writing the `SP_NATIVE_SET` cache-bust key to `build/.serious_python_spm_key` (overridable via `SERIOUS_PYTHON_SPM_KEY_FILE`) for the caller to export into the `flutter build` environment. See `serious_python_darwin` 4.0.0. ## 3.0.0 diff --git a/src/serious_python_darwin/CHANGELOG.md b/src/serious_python_darwin/CHANGELOG.md index e8c62bd6..0089f4f5 100644 --- a/src/serious_python_darwin/CHANGELOG.md +++ b/src/serious_python_darwin/CHANGELOG.md @@ -1,5 +1,9 @@ ## 4.0.0 +* **Swift Package Manager support (dual with CocoaPods).** The plugin now builds under SPM as well as CocoaPods, so apps can use either integration (CocoaPods goes read-only in December 2026; Flutter ships SPM on by default since 3.44). A new `darwin/serious_python_darwin/Package.swift` builds the same Swift source as the podspec, with `getResourcePath` resolving `Bundle.module` under SPM (`#if SWIFT_PACKAGE`) and the framework `python.bundle` under CocoaPods. + * SPM has no pod-install hook, so the staging the podspec `prepare_command` does runs on the host before `flutter build` instead: `prepare_spm.sh` assembles the dist (`prepare_.sh` + `sync_site_packages.sh`) and `stage_spm.sh` maps it into the package layout — `Python-{ios,macos}.xcframework` + `dart_bridge.xcframework` as local-path binary targets, the iOS native C-extensions enumerated from `extra-xcframeworks/`, and `stdlib`/`site-packages`/`app` as `.copy` resources. On iOS the extensions ship as embedded, signed frameworks (CPython's `.fwork` finder resolves them); on macOS they load flat from the resource trees. + * The manifest reads `SP_NATIVE_SET` (a hash of the staged native set) so SwiftPM re-resolves when requirements / app / Python version change — SwiftPM caches its package graph on manifest text + environment, not on the staged dirs it enumerates. + * Requires Flutter **3.44** / Dart **3.11**. * `prepareApp()` returns the app dir from the `python.bundle` resource (`/app`); the app's Python sources ship unpacked as an `app` resource bundle next to `stdlib` + `site-packages` (no first-launch extraction). * Version bump aligning with the `serious_python_*` 4.0.0 release. From cdde702f14003257b593105e11460739d4739891 Mon Sep 17 00:00:00 2001 From: Feodor Fitsner Date: Sat, 20 Jun 2026 20:02:43 -0700 Subject: [PATCH 05/10] serious_python_darwin: don't raise the plugin floor for SPM Adding Package.swift does not require Flutter 3.44: older Flutter has no SPM support and simply ignores the manifest, building via the podspec instead. So SPM support is additive and must not force every serious_python consumer onto 3.44. Restore the prior environment floor (Dart >=3.0.0, Flutter >=3.7.0); only the SPM build path needs 3.44/3.11, which is now scoped that way in the changelog. --- src/serious_python_darwin/CHANGELOG.md | 2 +- src/serious_python_darwin/pubspec.yaml | 10 ++++++---- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/serious_python_darwin/CHANGELOG.md b/src/serious_python_darwin/CHANGELOG.md index 0089f4f5..af7f33a2 100644 --- a/src/serious_python_darwin/CHANGELOG.md +++ b/src/serious_python_darwin/CHANGELOG.md @@ -3,7 +3,7 @@ * **Swift Package Manager support (dual with CocoaPods).** The plugin now builds under SPM as well as CocoaPods, so apps can use either integration (CocoaPods goes read-only in December 2026; Flutter ships SPM on by default since 3.44). A new `darwin/serious_python_darwin/Package.swift` builds the same Swift source as the podspec, with `getResourcePath` resolving `Bundle.module` under SPM (`#if SWIFT_PACKAGE`) and the framework `python.bundle` under CocoaPods. * SPM has no pod-install hook, so the staging the podspec `prepare_command` does runs on the host before `flutter build` instead: `prepare_spm.sh` assembles the dist (`prepare_.sh` + `sync_site_packages.sh`) and `stage_spm.sh` maps it into the package layout — `Python-{ios,macos}.xcframework` + `dart_bridge.xcframework` as local-path binary targets, the iOS native C-extensions enumerated from `extra-xcframeworks/`, and `stdlib`/`site-packages`/`app` as `.copy` resources. On iOS the extensions ship as embedded, signed frameworks (CPython's `.fwork` finder resolves them); on macOS they load flat from the resource trees. * The manifest reads `SP_NATIVE_SET` (a hash of the staged native set) so SwiftPM re-resolves when requirements / app / Python version change — SwiftPM caches its package graph on manifest text + environment, not on the staged dirs it enumerates. - * Requires Flutter **3.44** / Dart **3.11**. + * The **SPM path** needs Flutter **3.44** / Dart **3.11**; the plugin's minimum is unchanged because `Package.swift` is dormant on older Flutter (which uses the CocoaPods path). * `prepareApp()` returns the app dir from the `python.bundle` resource (`/app`); the app's Python sources ship unpacked as an `app` resource bundle next to `stdlib` + `site-packages` (no first-launch extraction). * Version bump aligning with the `serious_python_*` 4.0.0 release. diff --git a/src/serious_python_darwin/pubspec.yaml b/src/serious_python_darwin/pubspec.yaml index c5b74071..8c2e09ee 100644 --- a/src/serious_python_darwin/pubspec.yaml +++ b/src/serious_python_darwin/pubspec.yaml @@ -5,10 +5,12 @@ repository: https://github.com/flet-dev/serious-python version: 4.0.0 environment: - # Swift Package Manager support (the FlutterFramework SwiftPM convention) lands - # in Flutter 3.44 / Dart 3.11; this is also the de-facto floor from dart_bridge. - sdk: ">=3.11.0 <4.0.0" - flutter: ">=3.44.0" + # The Swift Package Manager build path needs Flutter 3.44 / Dart 3.11 (the + # FlutterFramework SwiftPM convention), but `Package.swift` is simply ignored on + # older Flutter — which uses the CocoaPods path instead — so SPM support does + # not raise the plugin's minimum. Keep the existing floor. + sdk: ">=3.0.0 <4.0.0" + flutter: ">=3.7.0" dependencies: flutter: From e1583355602981160a038e680672d8f1a9075623 Mon Sep 17 00:00:00 2001 From: Feodor Fitsner Date: Sat, 20 Jun 2026 21:10:59 -0700 Subject: [PATCH 06/10] serious_python: make darwin SPM staging explicit; CI tests both integrations MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Swift Package Manager is Flutter's default on 3.44, so merely shipping a Package.swift switches darwin builds to SPM — but the package command must not infer the build system (the `flutter` on PATH may not be the one building the app, e.g. under fvm). So SPM staging is driven purely by an explicit SERIOUS_PYTHON_DARWIN_SPM env var (set by `flet build` or a standalone consumer); no `flutter` calls in the package command. CocoaPods builds leave it unset and the podspec prepare_command stages as before. CI: the macOS and iOS bridge_example jobs gain a `build_system: [cocoapods, spm]` matrix (12 darwin jobs total — both integrations x 3.12/3.13/3.14). SPM jobs enable SPM in flutter config + set SERIOUS_PYTHON_DARWIN_SPM and export SP_NATIVE_SET; CocoaPods jobs disable SPM and use the regular podspec staging. --- .github/workflows/ci.yml | 90 ++++++++++----------- src/serious_python/bin/package_command.dart | 10 ++- 2 files changed, 51 insertions(+), 49 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index dce117ef..60a9f443 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -65,12 +65,13 @@ jobs: fi bridge_example_macos: - name: Test Bridge example on macOS (Python ${{ matrix.python_version }}) + name: Test Bridge example on macOS (${{ matrix.build_system }}, Python ${{ matrix.python_version }}) runs-on: macos-26 strategy: fail-fast: false matrix: python_version: ['3.12', '3.13', '3.14'] + build_system: ['cocoapods', 'spm'] env: SERIOUS_PYTHON_VERSION: ${{ matrix.python_version }} steps: @@ -92,10 +93,29 @@ jobs: flet-cache-${{ runner.os }}-${{ runner.arch }}-py${{ matrix.python_version }}- flet-cache-${{ runner.os }}-${{ runner.arch }}- + - name: Configure ${{ matrix.build_system }} + run: | + if [ "${{ matrix.build_system }}" = "spm" ]; then + flutter config --enable-swift-package-manager + else + flutter config --no-enable-swift-package-manager + fi + - name: Package + run integration test working-directory: "src/serious_python/example/bridge_example" run: | + # SPM does host-side staging in the package command (gated on + # SERIOUS_PYTHON_DARWIN_SPM) and exports the SP_NATIVE_SET cache-bust + # key into the build; CocoaPods stages via the podspec prepare_command. + if [ "${{ matrix.build_system }}" = "spm" ]; then + export SERIOUS_PYTHON_DARWIN_SPM=1 + else + export SERIOUS_PYTHON_DARWIN_SPM=false + fi dart run serious_python:main package app/src --platform Darwin --python-version ${{ matrix.python_version }} + if [ "${{ matrix.build_system }}" = "spm" ]; then + export SP_NATIVE_SET="$(cat build/.serious_python_spm_key)" + fi # Each test file is invoked separately. `flutter test integration_test` # over the directory reuses one VM session, but the second file's # `runApp()` then trips "Unable to start the app on the device" — @@ -106,56 +126,15 @@ jobs: flutter test integration_test/throughput_test.dart -d macos --dart-define=EXPECTED_PYTHON_VERSION=${{ matrix.python_version }} flutter test integration_test/memory_test.dart -d macos --dart-define=EXPECTED_PYTHON_VERSION=${{ matrix.python_version }} - # Smoke-test the Swift Package Manager build path (the CocoaPods jobs above - # cover the full python matrix; one version is enough to guard SPM). - bridge_example_macos_spm: - name: Test Bridge example on macOS via Swift Package Manager (Python 3.14) - runs-on: macos-26 - env: - SERIOUS_PYTHON_VERSION: '3.14' - steps: - - name: Checkout repository - uses: actions/checkout@v6 - - - name: Setup Flutter - uses: kuhnroyal/flutter-fvm-config-action/setup@v3 - with: - path: '.fvmrc' - cache: true - - - name: Cache flet downloads - uses: actions/cache@v5 - with: - path: ~/.flet/cache - key: flet-cache-${{ runner.os }}-${{ runner.arch }}-py3.14-${{ hashFiles('src/serious_python/lib/src/python_versions.dart', 'src/serious_python/bin/package_command.dart', 'src/serious_python_android/android/build.gradle', 'src/serious_python_darwin/darwin/prepare_macos.sh', 'src/serious_python_darwin/darwin/prepare_ios.sh', 'src/serious_python_windows/windows/CMakeLists.txt', 'src/serious_python_linux/linux/CMakeLists.txt') }} - restore-keys: | - flet-cache-${{ runner.os }}-${{ runner.arch }}-py3.14- - flet-cache-${{ runner.os }}-${{ runner.arch }}- - - - name: Enable Swift Package Manager - run: flutter config --enable-swift-package-manager - - - name: Package (SPM staging) + run integration test - working-directory: "src/serious_python/example/bridge_example" - run: | - # Host-side SPM staging (the podspec prepare_command has no SPM hook): - # the package command assembles + maps the runtime into the plugin's - # Package.swift layout and writes the SP_NATIVE_SET cache-bust key, - # which we export into the flutter build — the same handoff that - # `flet build` performs. - SERIOUS_PYTHON_DARWIN_SPM=1 \ - dart run serious_python:main package app/src --platform Darwin --python-version 3.14 - export SP_NATIVE_SET="$(cat build/.serious_python_spm_key)" - flutter test integration_test/interactivity_test.dart -d macos --dart-define=EXPECTED_PYTHON_VERSION=3.14 - bridge_example_ios: - name: Test Bridge example on iOS (Python ${{ matrix.python_version }}) + name: Test Bridge example on iOS (${{ matrix.build_system }}, Python ${{ matrix.python_version }}) runs-on: macos-26 timeout-minutes: 25 strategy: fail-fast: false matrix: python_version: ['3.12', '3.13', '3.14'] + build_system: ['cocoapods', 'spm'] env: SERIOUS_PYTHON_VERSION: ${{ matrix.python_version }} steps: @@ -187,17 +166,34 @@ jobs: shutdown_after_job: true wait_for_boot: true + - name: Configure ${{ matrix.build_system }} + run: | + if [ "${{ matrix.build_system }}" = "spm" ]; then + flutter config --enable-swift-package-manager + else + flutter config --no-enable-swift-package-manager + fi + - name: Package + run integration test working-directory: "src/serious_python/example/bridge_example" run: | ts() { date '+%H:%M:%S'; } + if [ "${{ matrix.build_system }}" = "spm" ]; then + export SERIOUS_PYTHON_DARWIN_SPM=1 + else + export SERIOUS_PYTHON_DARWIN_SPM=false + fi echo "[$(ts)] >>> dart run serious_python:main package" # certifi is a placeholder requirement: serious_python_darwin's # sync_site_packages.sh only populates dist_ios/site-xcframeworks - # (which bundle-python-frameworks-ios.sh then requires at build - # time) when iOS-specific site-packages subdirs exist. Empty - # --requirements skips that branch and the build fails. + # (the iOS native C-extensions — embedded by the podspec script_phase + # under CocoaPods, or enumerated into extra-xcframeworks under SPM) + # when iOS-specific site-packages subdirs exist. Empty --requirements + # skips that branch and the build fails. dart run serious_python:main package app/src --platform iOS --python-version ${{ matrix.python_version }} --requirements certifi + if [ "${{ matrix.build_system }}" = "spm" ]; then + export SP_NATIVE_SET="$(cat build/.serious_python_spm_key)" + fi echo "[$(ts)] >>> flutter test integration_test (per-file)" # See macOS job for why each file runs as a separate invocation. flutter test integration_test/interactivity_test.dart --device-id ${{ steps.simulator.outputs.udid }} --dart-define=EXPECTED_PYTHON_VERSION=${{ matrix.python_version }} diff --git a/src/serious_python/bin/package_command.dart b/src/serious_python/bin/package_command.dart index 4a02ca70..df6d3914 100644 --- a/src/serious_python/bin/package_command.dart +++ b/src/serious_python/bin/package_command.dart @@ -578,9 +578,12 @@ class PackageCommand extends Command { // Swift Package Manager (darwin) host-side staging: the podspec // prepare_command doesn't run under SPM, so assemble the dist and map // it into the plugin's Package.swift layout here (app is now staged). + // Driven explicitly by `SERIOUS_PYTHON_DARWIN_SPM` (set by `flet build`, + // or by a standalone consumer that builds with SPM) — the package command + // can't infer the build system (the `flutter` on PATH may not be the one + // building the app). CocoaPods builds leave it unset; the podspec stages. if ((platform == "iOS" || platform == "Darwin") && - (Platform.environment[darwinSpmEnvironmentVariable] ?? "") - .isNotEmpty) { + _isTruthy(Platform.environment[darwinSpmEnvironmentVariable])) { await _stageDarwinSpm(platform, currentPath); } } @@ -662,6 +665,9 @@ class PackageCommand extends Command { return proc.exitCode; } + static bool _isTruthy(String? v) => + v != null && const ["1", "true", "yes", "on"].contains(v.toLowerCase()); + // Run the darwin SPM staging (prepare_spm.sh: assemble dist + map into the // plugin's Package.swift layout) and persist the SP_NATIVE_SET cache-bust key // for `flet build` to export into the `flutter build` environment. From cc9f6970be70d44349cbb4c2ff61d5b69fa6d6a9 Mon Sep 17 00:00:00 2001 From: Feodor Fitsner Date: Sun, 21 Jun 2026 13:24:49 -0700 Subject: [PATCH 07/10] serious_python: default to SPM staging for darwin (CocoaPods opt-out) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit SPM is Flutter's default darwin integration since 3.44, so the `package` command now stages for SPM by default for iOS/macOS; set SERIOUS_PYTHON_DARWIN_SPM to a falsy value (0/false/no/off) to opt out and build with CocoaPods (the podspec prepare_command stages then). This makes the canonical docs/readme command — `dart run serious_python:main package --platform Darwin` — do SPM with no extra flags. CI: the bridge_example macOS/iOS jobs keep the {cocoapods, spm} x {3.12,3.13,3.14} matrix; the SPM jobs now leave SERIOUS_PYTHON_DARWIN_SPM unset so they exercise that default path, and the CocoaPods jobs set it false. Also fix the cache key's stale `build.gradle` reference (the Android plugin uses `build.gradle.kts`). --- .github/workflows/ci.yml | 28 ++++++++--------- src/serious_python/CHANGELOG.md | 2 +- src/serious_python/bin/package_command.dart | 34 +++++++++++---------- 3 files changed, 33 insertions(+), 31 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 60a9f443..7a165aab 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -88,7 +88,7 @@ jobs: uses: actions/cache@v5 with: path: ~/.flet/cache - key: flet-cache-${{ runner.os }}-${{ runner.arch }}-py${{ matrix.python_version }}-${{ hashFiles('src/serious_python/lib/src/python_versions.dart', 'src/serious_python/bin/package_command.dart', 'src/serious_python_android/android/build.gradle', 'src/serious_python_darwin/darwin/prepare_macos.sh', 'src/serious_python_darwin/darwin/prepare_ios.sh', 'src/serious_python_windows/windows/CMakeLists.txt', 'src/serious_python_linux/linux/CMakeLists.txt') }} + key: flet-cache-${{ runner.os }}-${{ runner.arch }}-py${{ matrix.python_version }}-${{ hashFiles('src/serious_python/lib/src/python_versions.dart', 'src/serious_python/bin/package_command.dart', 'src/serious_python_android/android/build.gradle.kts', 'src/serious_python_darwin/darwin/prepare_macos.sh', 'src/serious_python_darwin/darwin/prepare_ios.sh', 'src/serious_python_windows/windows/CMakeLists.txt', 'src/serious_python_linux/linux/CMakeLists.txt') }} restore-keys: | flet-cache-${{ runner.os }}-${{ runner.arch }}-py${{ matrix.python_version }}- flet-cache-${{ runner.os }}-${{ runner.arch }}- @@ -104,12 +104,12 @@ jobs: - name: Package + run integration test working-directory: "src/serious_python/example/bridge_example" run: | - # SPM does host-side staging in the package command (gated on - # SERIOUS_PYTHON_DARWIN_SPM) and exports the SP_NATIVE_SET cache-bust - # key into the build; CocoaPods stages via the podspec prepare_command. - if [ "${{ matrix.build_system }}" = "spm" ]; then - export SERIOUS_PYTHON_DARWIN_SPM=1 - else + # SPM is the package command's default: it does host-side staging and + # writes the SP_NATIVE_SET cache-bust key (we export it into the build). + # CocoaPods opts out via SERIOUS_PYTHON_DARWIN_SPM=false; the podspec + # prepare_command stages then. (The SPM job leaves the var unset so it + # exercises the default path docs/readme commands use.) + if [ "${{ matrix.build_system }}" = "cocoapods" ]; then export SERIOUS_PYTHON_DARWIN_SPM=false fi dart run serious_python:main package app/src --platform Darwin --python-version ${{ matrix.python_version }} @@ -151,7 +151,7 @@ jobs: uses: actions/cache@v5 with: path: ~/.flet/cache - key: flet-cache-${{ runner.os }}-${{ runner.arch }}-py${{ matrix.python_version }}-${{ hashFiles('src/serious_python/lib/src/python_versions.dart', 'src/serious_python/bin/package_command.dart', 'src/serious_python_android/android/build.gradle', 'src/serious_python_darwin/darwin/prepare_macos.sh', 'src/serious_python_darwin/darwin/prepare_ios.sh', 'src/serious_python_windows/windows/CMakeLists.txt', 'src/serious_python_linux/linux/CMakeLists.txt') }} + key: flet-cache-${{ runner.os }}-${{ runner.arch }}-py${{ matrix.python_version }}-${{ hashFiles('src/serious_python/lib/src/python_versions.dart', 'src/serious_python/bin/package_command.dart', 'src/serious_python_android/android/build.gradle.kts', 'src/serious_python_darwin/darwin/prepare_macos.sh', 'src/serious_python_darwin/darwin/prepare_ios.sh', 'src/serious_python_windows/windows/CMakeLists.txt', 'src/serious_python_linux/linux/CMakeLists.txt') }} restore-keys: | flet-cache-${{ runner.os }}-${{ runner.arch }}-py${{ matrix.python_version }}- flet-cache-${{ runner.os }}-${{ runner.arch }}- @@ -178,9 +178,9 @@ jobs: working-directory: "src/serious_python/example/bridge_example" run: | ts() { date '+%H:%M:%S'; } - if [ "${{ matrix.build_system }}" = "spm" ]; then - export SERIOUS_PYTHON_DARWIN_SPM=1 - else + # SPM is the default; CocoaPods opts out (the SPM job leaves the var + # unset to exercise the default path docs/readme commands use). + if [ "${{ matrix.build_system }}" = "cocoapods" ]; then export SERIOUS_PYTHON_DARWIN_SPM=false fi echo "[$(ts)] >>> dart run serious_python:main package" @@ -226,7 +226,7 @@ jobs: uses: actions/cache@v5 with: path: ~/.flet/cache - key: flet-cache-${{ runner.os }}-${{ runner.arch }}-py${{ matrix.python_version }}-${{ hashFiles('src/serious_python/lib/src/python_versions.dart', 'src/serious_python/bin/package_command.dart', 'src/serious_python_android/android/build.gradle', 'src/serious_python_darwin/darwin/prepare_macos.sh', 'src/serious_python_darwin/darwin/prepare_ios.sh', 'src/serious_python_windows/windows/CMakeLists.txt', 'src/serious_python_linux/linux/CMakeLists.txt') }} + key: flet-cache-${{ runner.os }}-${{ runner.arch }}-py${{ matrix.python_version }}-${{ hashFiles('src/serious_python/lib/src/python_versions.dart', 'src/serious_python/bin/package_command.dart', 'src/serious_python_android/android/build.gradle.kts', 'src/serious_python_darwin/darwin/prepare_macos.sh', 'src/serious_python_darwin/darwin/prepare_ios.sh', 'src/serious_python_windows/windows/CMakeLists.txt', 'src/serious_python_linux/linux/CMakeLists.txt') }} restore-keys: | flet-cache-${{ runner.os }}-${{ runner.arch }}-py${{ matrix.python_version }}- flet-cache-${{ runner.os }}-${{ runner.arch }}- @@ -324,7 +324,7 @@ jobs: uses: actions/cache@v5 with: path: ~/.flet/cache - key: flet-cache-${{ runner.os }}-${{ runner.arch }}-py${{ matrix.python_version }}-${{ hashFiles('src/serious_python/lib/src/python_versions.dart', 'src/serious_python/bin/package_command.dart', 'src/serious_python_android/android/build.gradle', 'src/serious_python_darwin/darwin/prepare_macos.sh', 'src/serious_python_darwin/darwin/prepare_ios.sh', 'src/serious_python_windows/windows/CMakeLists.txt', 'src/serious_python_linux/linux/CMakeLists.txt') }} + key: flet-cache-${{ runner.os }}-${{ runner.arch }}-py${{ matrix.python_version }}-${{ hashFiles('src/serious_python/lib/src/python_versions.dart', 'src/serious_python/bin/package_command.dart', 'src/serious_python_android/android/build.gradle.kts', 'src/serious_python_darwin/darwin/prepare_macos.sh', 'src/serious_python_darwin/darwin/prepare_ios.sh', 'src/serious_python_windows/windows/CMakeLists.txt', 'src/serious_python_linux/linux/CMakeLists.txt') }} restore-keys: | flet-cache-${{ runner.os }}-${{ runner.arch }}-py${{ matrix.python_version }}- flet-cache-${{ runner.os }}-${{ runner.arch }}- @@ -402,7 +402,7 @@ jobs: uses: actions/cache@v5 with: path: ~/.flet/cache - key: flet-cache-${{ runner.os }}-${{ runner.arch }}-py${{ matrix.python_version }}-${{ hashFiles('src/serious_python/lib/src/python_versions.dart', 'src/serious_python/bin/package_command.dart', 'src/serious_python_android/android/build.gradle', 'src/serious_python_darwin/darwin/prepare_macos.sh', 'src/serious_python_darwin/darwin/prepare_ios.sh', 'src/serious_python_windows/windows/CMakeLists.txt', 'src/serious_python_linux/linux/CMakeLists.txt') }} + key: flet-cache-${{ runner.os }}-${{ runner.arch }}-py${{ matrix.python_version }}-${{ hashFiles('src/serious_python/lib/src/python_versions.dart', 'src/serious_python/bin/package_command.dart', 'src/serious_python_android/android/build.gradle.kts', 'src/serious_python_darwin/darwin/prepare_macos.sh', 'src/serious_python_darwin/darwin/prepare_ios.sh', 'src/serious_python_windows/windows/CMakeLists.txt', 'src/serious_python_linux/linux/CMakeLists.txt') }} restore-keys: | flet-cache-${{ runner.os }}-${{ runner.arch }}-py${{ matrix.python_version }}- flet-cache-${{ runner.os }}-${{ runner.arch }}- diff --git a/src/serious_python/CHANGELOG.md b/src/serious_python/CHANGELOG.md index f678a714..ab930e35 100644 --- a/src/serious_python/CHANGELOG.md +++ b/src/serious_python/CHANGELOG.md @@ -4,7 +4,7 @@ * **New `SeriousPython.prepareApp()`** — materializes the app (Android first-launch unpack) and returns the directory containing its entry point. **`SeriousPython.run()` now takes no `assetPath` argument** (it resolves the app via `prepareApp()`), sets the current directory to a writable per-app data dir (`/data`) — not the read-only bundle — so relative file writes / SQLite work, and runs `main.pyc`/`main.py` (or `appFileName`). * **Breaking change:** the `app.zip` asset convention and the runtime zip-extraction API are removed — `SeriousPython.run("app/app.zip")`, `extractAssetZip`, and `extractFileZip` no longer exist. Repackage with `dart run serious_python:main package -p ` and call `SeriousPython.run()` with no arguments. * **Android:** the runtime payload moved to `/flet/{app, stdlib.zip, sitepackages.zip, extract/}` (resolved via `getApplicationSupportDirectory()`; the custom `getFilesDir` method channel is dropped). User data in the sibling `/data` survives app updates. -* **Swift Package Manager (darwin) staging in the `package` command.** When `SERIOUS_PYTHON_DARWIN_SPM` is set (by `flet build` when the iOS/macOS build uses SPM), the `package` command runs the host-side equivalent of the podspec `prepare_command` — which SPM has no hook for — by resolving `serious_python_darwin`'s `darwin/` dir (`SERIOUS_PYTHON_DARWIN_DIR` override, else the project's `package_config.json`), invoking `prepare_spm.sh`, and writing the `SP_NATIVE_SET` cache-bust key to `build/.serious_python_spm_key` (overridable via `SERIOUS_PYTHON_SPM_KEY_FILE`) for the caller to export into the `flutter build` environment. See `serious_python_darwin` 4.0.0. +* **Swift Package Manager (darwin) staging in the `package` command — on by default.** For iOS/macOS the `package` command runs the host-side equivalent of the podspec `prepare_command` (which SPM has no hook for) by resolving `serious_python_darwin`'s `darwin/` dir (`SERIOUS_PYTHON_DARWIN_DIR` override, else the project's `package_config.json`), invoking `prepare_spm.sh`, and writing the `SP_NATIVE_SET` cache-bust key to `build/.serious_python_spm_key` (overridable via `SERIOUS_PYTHON_SPM_KEY_FILE`) for the caller to export into the `flutter build` environment. SPM is Flutter's default darwin integration since 3.44, so this happens by default — set **`SERIOUS_PYTHON_DARWIN_SPM`** to a falsy value (`0`/`false`/`no`/`off`) to opt out and build with CocoaPods (the podspec stages then). See `serious_python_darwin` 4.0.0. ## 3.0.0 diff --git a/src/serious_python/bin/package_command.dart b/src/serious_python/bin/package_command.dart index df6d3914..a3ed797a 100644 --- a/src/serious_python/bin/package_command.dart +++ b/src/serious_python/bin/package_command.dart @@ -29,13 +29,16 @@ const flutterPackagesFlutterEnvironmentVariable = "SERIOUS_PYTHON_FLUTTER_PACKAGES"; const allowSourceDistrosEnvironmentVariable = "SERIOUS_PYTHON_ALLOW_SOURCE_DISTRIBUTIONS"; -// Swift Package Manager (darwin) host-side staging. When `SERIOUS_PYTHON_DARWIN_SPM` -// is set (by `flet build` when the build uses SPM), the package command runs the -// SPM equivalent of the podspec `prepare_command` — assembling the dist and mapping -// it into the plugin's Package.swift layout — since SPM has no pod-install hook. -// `SERIOUS_PYTHON_DARWIN_DIR` optionally overrides the resolved plugin `darwin/` dir; -// `SERIOUS_PYTHON_SPM_KEY_FILE` overrides where the SP_NATIVE_SET cache-bust key is -// written for the caller to export into the `flutter build` environment. +// Swift Package Manager (darwin) host-side staging. For iOS/macOS the package +// command runs the SPM equivalent of the podspec `prepare_command` — assembling +// the dist and mapping it into the plugin's Package.swift layout — since SPM has +// no pod-install hook. SPM is Flutter's default darwin integration since 3.44, so +// this happens **by default**; set `SERIOUS_PYTHON_DARWIN_SPM` to a falsy value +// (0/false/no/off) to opt out and build with CocoaPods (e.g. `flet build` sets it +// false when the app uses a non-SPM plugin). `SERIOUS_PYTHON_DARWIN_DIR` optionally +// overrides the resolved plugin `darwin/` dir; `SERIOUS_PYTHON_SPM_KEY_FILE` overrides +// where the SP_NATIVE_SET cache-bust key is written for the caller to export into the +// `flutter build` environment. const darwinSpmEnvironmentVariable = "SERIOUS_PYTHON_DARWIN_SPM"; const darwinDirEnvironmentVariable = "SERIOUS_PYTHON_DARWIN_DIR"; const spmKeyFileEnvironmentVariable = "SERIOUS_PYTHON_SPM_KEY_FILE"; @@ -576,14 +579,13 @@ class PackageCommand extends Command { await copyDirectory(tempDir, appStagingDir, tempDir.path, []); // Swift Package Manager (darwin) host-side staging: the podspec - // prepare_command doesn't run under SPM, so assemble the dist and map - // it into the plugin's Package.swift layout here (app is now staged). - // Driven explicitly by `SERIOUS_PYTHON_DARWIN_SPM` (set by `flet build`, - // or by a standalone consumer that builds with SPM) — the package command - // can't infer the build system (the `flutter` on PATH may not be the one - // building the app). CocoaPods builds leave it unset; the podspec stages. + // prepare_command doesn't run under SPM, so assemble the dist and map it + // into the plugin's Package.swift layout here (app is now staged). SPM is + // Flutter's default darwin integration since 3.44, so this runs **by + // default**; set `SERIOUS_PYTHON_DARWIN_SPM` to a falsy value (0/false/ + // no/off) to opt out and build with CocoaPods (the podspec stages then). if ((platform == "iOS" || platform == "Darwin") && - _isTruthy(Platform.environment[darwinSpmEnvironmentVariable])) { + !_isFalsy(Platform.environment[darwinSpmEnvironmentVariable])) { await _stageDarwinSpm(platform, currentPath); } } @@ -665,8 +667,8 @@ class PackageCommand extends Command { return proc.exitCode; } - static bool _isTruthy(String? v) => - v != null && const ["1", "true", "yes", "on"].contains(v.toLowerCase()); + static bool _isFalsy(String? v) => + v != null && const ["0", "false", "no", "off"].contains(v.toLowerCase()); // Run the darwin SPM staging (prepare_spm.sh: assemble dist + map into the // plugin's Package.swift layout) and persist the SP_NATIVE_SET cache-bust key From d38813328c75d81d8090fc702ed55a5496a7829b Mon Sep 17 00:00:00 2001 From: Feodor Fitsner Date: Sun, 21 Jun 2026 13:25:03 -0700 Subject: [PATCH 08/10] serious_python: pull python-build 20260621 + make dist caches date-aware MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Regenerate the version tables for python-build release 20260621 (a rebuild that re-signs the macOS C extensions linker-signed instead of explicit adhoc, so downstream Xcode app builds can strip + re-sign them — no per-extension "not stripping" warnings, smaller bundles). The Python versions are unchanged (3.12.13 / 3.13.14 / 3.14.6) — only the build date moved. A same-version re-release like this would otherwise never take effect: the download caches AND extraction markers were keyed by full Python version, so an existing cache (CI restore-keys or a dev machine) is served stale. Key both by version + build date instead: - darwin prepare_{ios,macos}.sh: cache dir `v-` + a `.python_build_id` re-extract marker. - linux/windows CMakeLists.txt: same cache dir + a marker-gated re-extract (replacing the `if(NOT EXISTS libpython3.so/python.exe)` guard). Android already refreshes correctly via its conditional-GET cache (onlyIfModified + useETag) + Gradle's input-change re-extract, so it's unchanged. --- .../lib/src/python_versions.dart | 4 ++-- .../android/python_versions.properties | 4 ++-- .../darwin/prepare_ios.sh | 22 ++++++++++++------- .../darwin/prepare_macos.sh | 22 ++++++++++++------- .../darwin/python_versions.properties | 4 ++-- src/serious_python_linux/linux/CMakeLists.txt | 22 +++++++++++++++++-- .../linux/python_versions.properties | 4 ++-- .../windows/CMakeLists.txt | 22 +++++++++++++++++-- .../windows/python_versions.properties | 4 ++-- 9 files changed, 78 insertions(+), 30 deletions(-) diff --git a/src/serious_python/lib/src/python_versions.dart b/src/serious_python/lib/src/python_versions.dart index 27f20ac9..fed44557 100644 --- a/src/serious_python/lib/src/python_versions.dart +++ b/src/serious_python/lib/src/python_versions.dart @@ -1,5 +1,5 @@ // GENERATED by `dart run serious_python:gen_version_tables` from python-build's -// manifest.json (release 20260618). Do not edit by hand — edit python-build's +// manifest.json (release 20260621). Do not edit by hand — edit python-build's // manifest.json, cut a release, bump `pythonReleaseDate`, and regenerate. const pythonVersionEnvironmentVariable = "SERIOUS_PYTHON_VERSION"; @@ -10,7 +10,7 @@ const pyodideVersionEnvironmentVariable = "SERIOUS_PYTHON_PYODIDE_VERSION"; const dartBridgeVersionEnvironmentVariable = "DART_BRIDGE_VERSION"; /// python-build release the bundled runtimes come from (YYYYMMDD). -const pythonReleaseDate = "20260618"; +const pythonReleaseDate = "20260621"; const dartBridgeVersion = "1.4.0"; const defaultPythonVersion = "3.14"; diff --git a/src/serious_python_android/android/python_versions.properties b/src/serious_python_android/android/python_versions.properties index 2fc624e2..41d4ec16 100644 --- a/src/serious_python_android/android/python_versions.properties +++ b/src/serious_python_android/android/python_versions.properties @@ -1,8 +1,8 @@ # GENERATED by `dart run serious_python:gen_version_tables` from -# python-build manifest.json (release 20260618). Do not edit by hand. +# python-build manifest.json (release 20260621). Do not edit by hand. default_python_version=3.14 dart_bridge_version=1.4.0 -python_build_release_date=20260618 +python_build_release_date=20260621 3.12.full_version=3.12.13 3.12.android_abis=arm64-v8a,x86_64,armeabi-v7a 3.13.full_version=3.13.14 diff --git a/src/serious_python_darwin/darwin/prepare_ios.sh b/src/serious_python_darwin/darwin/prepare_ios.sh index 817ae1ae..279c99b8 100755 --- a/src/serious_python_darwin/darwin/prepare_ios.sh +++ b/src/serious_python_darwin/darwin/prepare_ios.sh @@ -11,7 +11,11 @@ dist=$script_dir/dist_ios # Cross-plugin download cache; see prepare_macos.sh for the convention. cache_root="${FLET_CACHE_DIR:-$HOME/.flet/cache}" -pb_cache="$cache_root/python-build/v$python_full_version" +# Date-keyed: a re-release of the same Python version (same 3.x.y, new build +# date — e.g. a rebuild that only re-signs binaries) downloads fresh instead of +# being served stale from the previous release's cache. dart-bridge stays +# version-keyed (its re-releases bump the version). +pb_cache="$cache_root/python-build/v$python_full_version-$python_build_date" db_cache="$cache_root/dart-bridge/v$dart_bridge_version" mkdir -p "$pb_cache" "$db_cache" @@ -25,17 +29,19 @@ if [ ! -f "$python_ios_dist_path" ]; then fi # Re-extract when $dist is missing OR was assembled for a different Python -# version. The guard used to be `[ ! -d "$dist" ]`, which left a stale dist_ios -# from a previous Python version in place — e.g. bundling 3.12 under 3.14 -# site-packages, which trips C-extension ABI errors ("unknown slot ID") at -# import. A version marker keys the extracted dist to $python_full_version. -marker="$dist/.python_full_version" -if [ ! -d "$dist" ] || [ "$(cat "$marker" 2>/dev/null)" != "$python_full_version" ]; then +# version/release. The guard used to be `[ ! -d "$dist" ]`, which left a stale +# dist_ios from a previous Python version in place — e.g. bundling 3.12 under +# 3.14 site-packages, which trips C-extension ABI errors ("unknown slot ID") at +# import. The marker keys the extracted dist to the version + release date, so a +# same-version re-release (new build date) also re-extracts. +build_id="$python_full_version-$python_build_date" +marker="$dist/.python_build_id" +if [ ! -d "$dist" ] || [ "$(cat "$marker" 2>/dev/null)" != "$build_id" ]; then rm -rf "$dist" mkdir -p "$dist" tar -xzf "$python_ios_dist_path" -C "$dist" mv "$dist/python-stdlib" "$dist/stdlib" - echo "$python_full_version" > "$marker" + echo "$build_id" > "$marker" fi # ---- flet-dev/dart-bridge (xcframework) ----------------------------------- diff --git a/src/serious_python_darwin/darwin/prepare_macos.sh b/src/serious_python_darwin/darwin/prepare_macos.sh index 1e4479ce..251c0a39 100755 --- a/src/serious_python_darwin/darwin/prepare_macos.sh +++ b/src/serious_python_darwin/darwin/prepare_macos.sh @@ -12,7 +12,11 @@ dist=$script_dir/dist_macos # gradle task + flet build's external tooling already use; ~/.flet/cache is # the shared default. Tarballs land here and survive `flutter clean`. cache_root="${FLET_CACHE_DIR:-$HOME/.flet/cache}" -pb_cache="$cache_root/python-build/v$python_full_version" +# Date-keyed: a re-release of the same Python version (same 3.x.y, new build +# date — e.g. a rebuild that only re-signs binaries) downloads fresh instead of +# being served stale from the previous release's cache. dart-bridge stays +# version-keyed (its re-releases bump the version). +pb_cache="$cache_root/python-build/v$python_full_version-$python_build_date" db_cache="$cache_root/dart-bridge/v$dart_bridge_version" mkdir -p "$pb_cache" "$db_cache" @@ -27,12 +31,14 @@ if [ ! -f "$python_macos_dist_path" ]; then fi # Re-extract when $dist is missing OR was assembled for a different Python -# version. The guard used to be `[ ! -d "$dist" ]`, which left a stale dist_macos -# from a previous Python version in place — e.g. bundling 3.12 under 3.14 -# site-packages, which trips C-extension ABI errors ("unknown slot ID") at -# import. A version marker keys the extracted dist to $python_full_version. -marker="$dist/.python_full_version" -if [ ! -d "$dist" ] || [ "$(cat "$marker" 2>/dev/null)" != "$python_full_version" ]; then +# version/release. The guard used to be `[ ! -d "$dist" ]`, which left a stale +# dist_macos from a previous Python version in place — e.g. bundling 3.12 under +# 3.14 site-packages, which trips C-extension ABI errors ("unknown slot ID") at +# import. The marker keys the extracted dist to the version + release date, so a +# same-version re-release (new build date) also re-extracts. +build_id="$python_full_version-$python_build_date" +marker="$dist/.python_build_id" +if [ ! -d "$dist" ] || [ "$(cat "$marker" 2>/dev/null)" != "$build_id" ]; then rm -rf "$dist" mkdir -p "$dist" tar -xzf "$python_macos_dist_path" -C "$dist" @@ -48,7 +54,7 @@ if [ ! -d "$dist" ] || [ "$(cat "$marker" 2>/dev/null)" != "$python_full_version # unexpectedly" crash dialog. We don't need this launcher for embedded # use; libdart_bridge dlopens Python.framework's main binary directly. find "$dist/xcframeworks" -type d -name 'Python.app' -prune -exec rm -rf {} + - echo "$python_full_version" > "$marker" + echo "$build_id" > "$marker" fi # ---- flet-dev/dart-bridge (xcframework, same archive for macOS + iOS) ----- diff --git a/src/serious_python_darwin/darwin/python_versions.properties b/src/serious_python_darwin/darwin/python_versions.properties index 2fc624e2..41d4ec16 100644 --- a/src/serious_python_darwin/darwin/python_versions.properties +++ b/src/serious_python_darwin/darwin/python_versions.properties @@ -1,8 +1,8 @@ # GENERATED by `dart run serious_python:gen_version_tables` from -# python-build manifest.json (release 20260618). Do not edit by hand. +# python-build manifest.json (release 20260621). Do not edit by hand. default_python_version=3.14 dart_bridge_version=1.4.0 -python_build_release_date=20260618 +python_build_release_date=20260621 3.12.full_version=3.12.13 3.12.android_abis=arm64-v8a,x86_64,armeabi-v7a 3.13.full_version=3.13.14 diff --git a/src/serious_python_linux/linux/CMakeLists.txt b/src/serious_python_linux/linux/CMakeLists.txt index aca471aa..96951110 100644 --- a/src/serious_python_linux/linux/CMakeLists.txt +++ b/src/serious_python_linux/linux/CMakeLists.txt @@ -57,7 +57,10 @@ if(DEFINED ENV{FLET_CACHE_DIR}) else() set(FLET_CACHE_DIR "$ENV{HOME}/.flet/cache") endif() -set(PB_CACHE "${FLET_CACHE_DIR}/python-build/v${PYTHON_FULL_VERSION}") +# Date-keyed so a same-version re-release (new build date) re-downloads instead +# of being served stale from a previous release's cache. dart-bridge stays +# version-keyed (its re-releases bump the version). +set(PB_CACHE "${FLET_CACHE_DIR}/python-build/v${PYTHON_FULL_VERSION}-${PYTHON_BUILD_DATE}") set(DB_CACHE "${FLET_CACHE_DIR}/dart-bridge/v${DART_BRIDGE_VERSION}") file(MAKE_DIRECTORY "${PB_CACHE}" "${DB_CACHE}") @@ -90,8 +93,23 @@ set(PYTHON_PACKAGE ${CMAKE_BINARY_DIR}/python) set(PYTHON_URL https://github.com/flet-dev/python-build/releases/download/${PYTHON_BUILD_DATE}/python-linux-dart-${PYTHON_FULL_VERSION}-${PYTHON_ARCH}.tar.gz) set(PYTHON_FILE "${PB_CACHE}/python-linux-dart-${PYTHON_FULL_VERSION}-${PYTHON_ARCH}.tar.gz") _sp_download("${PYTHON_URL}" "${PYTHON_FILE}") -if(NOT EXISTS "${PYTHON_PACKAGE}/lib/libpython3.so") +# Re-extract when the tree is missing OR was unpacked for a different +# version/release (a marker keyed to version + build date), so a same-version +# re-release replaces it instead of being skipped. +set(_py_build_id "${PYTHON_FULL_VERSION}-${PYTHON_BUILD_DATE}") +set(_py_marker "${PYTHON_PACKAGE}/.python_build_id") +set(_py_extract TRUE) +if(EXISTS "${_py_marker}") + file(READ "${_py_marker}" _py_cur) + string(STRIP "${_py_cur}" _py_cur) + if(_py_cur STREQUAL "${_py_build_id}") + set(_py_extract FALSE) + endif() +endif() +if(_py_extract) + file(REMOVE_RECURSE "${PYTHON_PACKAGE}") file(ARCHIVE_EXTRACT INPUT "${PYTHON_FILE}" DESTINATION "${PYTHON_PACKAGE}") + file(WRITE "${_py_marker}" "${_py_build_id}") endif() # ---- dart_bridge prebuilt .so --------------------------------------------- diff --git a/src/serious_python_linux/linux/python_versions.properties b/src/serious_python_linux/linux/python_versions.properties index 2fc624e2..41d4ec16 100644 --- a/src/serious_python_linux/linux/python_versions.properties +++ b/src/serious_python_linux/linux/python_versions.properties @@ -1,8 +1,8 @@ # GENERATED by `dart run serious_python:gen_version_tables` from -# python-build manifest.json (release 20260618). Do not edit by hand. +# python-build manifest.json (release 20260621). Do not edit by hand. default_python_version=3.14 dart_bridge_version=1.4.0 -python_build_release_date=20260618 +python_build_release_date=20260621 3.12.full_version=3.12.13 3.12.android_abis=arm64-v8a,x86_64,armeabi-v7a 3.13.full_version=3.13.14 diff --git a/src/serious_python_windows/windows/CMakeLists.txt b/src/serious_python_windows/windows/CMakeLists.txt index daad43ec..573388c6 100644 --- a/src/serious_python_windows/windows/CMakeLists.txt +++ b/src/serious_python_windows/windows/CMakeLists.txt @@ -60,7 +60,10 @@ elseif(DEFINED ENV{USERPROFILE}) else() set(FLET_CACHE_DIR "$ENV{HOME}/.flet/cache") endif() -set(PB_CACHE "${FLET_CACHE_DIR}/python-build/v${PYTHON_FULL_VERSION}") +# Date-keyed so a same-version re-release (new build date) re-downloads instead +# of being served stale from a previous release's cache. dart-bridge stays +# version-keyed (its re-releases bump the version). +set(PB_CACHE "${FLET_CACHE_DIR}/python-build/v${PYTHON_FULL_VERSION}-${PYTHON_BUILD_DATE}") set(DB_CACHE "${FLET_CACHE_DIR}/dart-bridge/v${DART_BRIDGE_VERSION}") file(MAKE_DIRECTORY "${PB_CACHE}" "${DB_CACHE}") @@ -102,8 +105,23 @@ function(_sp_download url dest) endfunction() _sp_download("${PYTHON_URL}" "${PYTHON_FILE}") -if(NOT EXISTS "${PYTHON_PACKAGE}/python.exe") +# Re-extract when the tree is missing OR was unpacked for a different +# version/release (a marker keyed to version + build date), so a same-version +# re-release replaces it instead of being skipped. +set(_py_build_id "${PYTHON_FULL_VERSION}-${PYTHON_BUILD_DATE}") +set(_py_marker "${PYTHON_PACKAGE}/.python_build_id") +set(_py_extract TRUE) +if(EXISTS "${_py_marker}") + file(READ "${_py_marker}" _py_cur) + string(STRIP "${_py_cur}" _py_cur) + if(_py_cur STREQUAL "${_py_build_id}") + set(_py_extract FALSE) + endif() +endif() +if(_py_extract) + file(REMOVE_RECURSE "${PYTHON_PACKAGE}") file(ARCHIVE_EXTRACT INPUT "${PYTHON_FILE}" DESTINATION "${PYTHON_PACKAGE}") + file(WRITE "${_py_marker}" "${_py_build_id}") endif() # ---- dart_bridge prebuilt DLLs -------------------------------------------- diff --git a/src/serious_python_windows/windows/python_versions.properties b/src/serious_python_windows/windows/python_versions.properties index 2fc624e2..41d4ec16 100644 --- a/src/serious_python_windows/windows/python_versions.properties +++ b/src/serious_python_windows/windows/python_versions.properties @@ -1,8 +1,8 @@ # GENERATED by `dart run serious_python:gen_version_tables` from -# python-build manifest.json (release 20260618). Do not edit by hand. +# python-build manifest.json (release 20260621). Do not edit by hand. default_python_version=3.14 dart_bridge_version=1.4.0 -python_build_release_date=20260618 +python_build_release_date=20260621 3.12.full_version=3.12.13 3.12.android_abis=arm64-v8a,x86_64,armeabi-v7a 3.13.full_version=3.13.14 From a99aec45c99955da0184a775b6be90fc7bf9ad9d Mon Sep 17 00:00:00 2001 From: Feodor Fitsner Date: Sun, 21 Jun 2026 13:25:25 -0700 Subject: [PATCH 09/10] run_example: guard test_pyjnius() for non-Android platforms MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit pyjnius is Android-only (needs a JVM + the app's Android classes via MAIN_ACTIVITY_HOST_CLASS_NAME, which only serious_python_android sets). The test was unguarded, so on macOS/iOS `from jnius import autoclass` / autoclass(None) raised and crashed main.py before it wrote the result file — the app then timed out waiting for that file. Wrap it in try/except like test_sqlite() so it returns a message instead, letting the script finish. --- .../example/run_example/app/src/main.py | 49 ++++++++++--------- 1 file changed, 27 insertions(+), 22 deletions(-) diff --git a/src/serious_python/example/run_example/app/src/main.py b/src/serious_python/example/run_example/app/src/main.py index 1a7737c6..3bea5db6 100644 --- a/src/serious_python/example/run_example/app/src/main.py +++ b/src/serious_python/example/run_example/app/src/main.py @@ -140,28 +140,33 @@ def test_sqlite(): def test_pyjnius(): - - from jnius import autoclass - - activity = autoclass(os.getenv("MAIN_ACTIVITY_HOST_CLASS_NAME")).mActivity - Secure = autoclass("android.provider.Settings$Secure") - - version = autoclass("android.os.Build$VERSION") - os_build = autoclass("android.os.Build") - base_os = version.BASE_OS - - DisplayMetrics = autoclass("android.util.DisplayMetrics") - metrics = DisplayMetrics() - - return ( - str(activity.getClass().getName()) - + " os: " - + str(os_build) - + " FLET_JNI_READY: " - + str(os.getenv("FLET_JNI_READY")) - + " DPI: " - + str(metrics.getDeviceDensity()) - ) + # pyjnius is Android-only (needs a JVM + the app's Android classes). On other + # platforms guard it like the other tests so it returns a message instead of + # crashing the script before the result file is written. + try: + from jnius import autoclass + + activity = autoclass(os.getenv("MAIN_ACTIVITY_HOST_CLASS_NAME")).mActivity + Secure = autoclass("android.provider.Settings$Secure") + + version = autoclass("android.os.Build$VERSION") + os_build = autoclass("android.os.Build") + base_os = version.BASE_OS + + DisplayMetrics = autoclass("android.util.DisplayMetrics") + metrics = DisplayMetrics() + + return ( + str(activity.getClass().getName()) + + " os: " + + str(os_build) + + " FLET_JNI_READY: " + + str(os.getenv("FLET_JNI_READY")) + + " DPI: " + + str(metrics.getDeviceDensity()) + ) + except Exception as e: + return f"\npyjnius: not available on this platform - {e}" r += test_sqlite() From aee6ad50e101bd33b5d41f81004fc8268b454a77 Mon Sep 17 00:00:00 2001 From: Feodor Fitsner Date: Sun, 21 Jun 2026 13:28:47 -0700 Subject: [PATCH 10/10] Add Swift package and remove Pod script phases Integrate the local FlutterGeneratedPluginSwiftPackage into macOS Runner projects for the bridge, flask, and run examples by adding PBXBuildFile/PBXFileReference entries, packageProductDependencies, packageReferences, and XCLocalSwiftPackageReference / XCSwiftPackageProductDependency sections. Remove CocoaPods embed/copy script build phases and their references from affected iOS and macOS Xcode projects. Add a scheme PreAction for macOS targets to run the Flutter macos_assemble.sh prepare step. (Other dependency lock updates and checksum changes are present in the patch.) --- .../example/bridge_example/ios/Podfile.lock | 7 --- .../ios/Runner.xcodeproj/project.pbxproj | 36 ----------- .../example/bridge_example/macos/Podfile.lock | 9 +-- .../macos/Runner.xcodeproj/project.pbxproj | 60 ++++++++----------- .../xcshareddata/xcschemes/Runner.xcscheme | 18 ++++++ .../example/flask_example/macos/Podfile.lock | 7 --- .../macos/Runner.xcodeproj/project.pbxproj | 60 ++++++++----------- .../xcshareddata/xcschemes/Runner.xcscheme | 18 ++++++ .../example/flask_example/pubspec.lock | 12 ++-- .../example/run_example/ios/Podfile.lock | 7 --- .../ios/Runner.xcodeproj/project.pbxproj | 36 ----------- .../example/run_example/macos/Podfile.lock | 9 +-- .../macos/Runner.xcodeproj/project.pbxproj | 60 ++++++++----------- .../xcshareddata/xcschemes/Runner.xcscheme | 18 ++++++ .../example/run_example/pubspec.lock | 12 ++-- 15 files changed, 140 insertions(+), 229 deletions(-) diff --git a/src/serious_python/example/bridge_example/ios/Podfile.lock b/src/serious_python/example/bridge_example/ios/Podfile.lock index 3542b7d0..88e52bf8 100644 --- a/src/serious_python/example/bridge_example/ios/Podfile.lock +++ b/src/serious_python/example/bridge_example/ios/Podfile.lock @@ -1,22 +1,15 @@ PODS: - Flutter (1.0.0) - - serious_python_darwin (2.0.0): - - Flutter - - FlutterMacOS DEPENDENCIES: - Flutter (from `Flutter`) - - serious_python_darwin (from `.symlinks/plugins/serious_python_darwin/darwin`) EXTERNAL SOURCES: Flutter: :path: Flutter - serious_python_darwin: - :path: ".symlinks/plugins/serious_python_darwin/darwin" SPEC CHECKSUMS: Flutter: cabc95a1d2626b1b06e7179b784ebcf0c0cde467 - serious_python_darwin: 6d58c9837595683a71e20114bdd4607568c98e84 PODFILE CHECKSUM: 3c63482e143d1b91d2d2560aee9fb04ecc74ac7e diff --git a/src/serious_python/example/bridge_example/ios/Runner.xcodeproj/project.pbxproj b/src/serious_python/example/bridge_example/ios/Runner.xcodeproj/project.pbxproj index 42b493f2..28b7a207 100644 --- a/src/serious_python/example/bridge_example/ios/Runner.xcodeproj/project.pbxproj +++ b/src/serious_python/example/bridge_example/ios/Runner.xcodeproj/project.pbxproj @@ -205,8 +205,6 @@ 97C146EC1CF9000F007C117D /* Resources */, 9705A1C41CF9048500538489 /* Embed Frameworks */, 3B06AD1E1E4923F5004D2608 /* Thin Binary */, - B2C65F2690B6BBEF6E20F674 /* [CP] Embed Pods Frameworks */, - 1D3A8F88F6DC96CBAD5977F8 /* [CP] Copy Pods Resources */, ); buildRules = ( ); @@ -284,23 +282,6 @@ /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ - 1D3A8F88F6DC96CBAD5977F8 /* [CP] Copy Pods Resources */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-input-files.xcfilelist", - ); - name = "[CP] Copy Pods Resources"; - outputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-output-files.xcfilelist", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh\"\n"; - showEnvVarsInLog = 0; - }; 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { isa = PBXShellScriptBuildPhase; alwaysOutOfDate = 1; @@ -354,23 +335,6 @@ shellPath = /bin/sh; shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; }; - B2C65F2690B6BBEF6E20F674 /* [CP] Embed Pods Frameworks */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist", - ); - name = "[CP] Embed Pods Frameworks"; - outputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; - showEnvVarsInLog = 0; - }; F3B1F0D1E68C4E672BCDCEAA /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; diff --git a/src/serious_python/example/bridge_example/macos/Podfile.lock b/src/serious_python/example/bridge_example/macos/Podfile.lock index 8a6bd931..1c915faa 100644 --- a/src/serious_python/example/bridge_example/macos/Podfile.lock +++ b/src/serious_python/example/bridge_example/macos/Podfile.lock @@ -1,23 +1,16 @@ PODS: - FlutterMacOS (1.0.0) - - serious_python_darwin (2.0.0): - - Flutter - - FlutterMacOS DEPENDENCIES: - FlutterMacOS (from `Flutter/ephemeral`) - - serious_python_darwin (from `Flutter/ephemeral/.symlinks/plugins/serious_python_darwin/darwin`) EXTERNAL SOURCES: FlutterMacOS: :path: Flutter/ephemeral - serious_python_darwin: - :path: Flutter/ephemeral/.symlinks/plugins/serious_python_darwin/darwin SPEC CHECKSUMS: FlutterMacOS: d0db08ddef1a9af05a5ec4b724367152bb0500b1 - serious_python_darwin: 6d58c9837595683a71e20114bdd4607568c98e84 -PODFILE CHECKSUM: 54d867c82ac51cbd61b565781b9fada492027009 +PODFILE CHECKSUM: 346bfb2deb41d4a6ebd6f6799f92188bde2d246f COCOAPODS: 1.14.3 diff --git a/src/serious_python/example/bridge_example/macos/Runner.xcodeproj/project.pbxproj b/src/serious_python/example/bridge_example/macos/Runner.xcodeproj/project.pbxproj index 19a7a771..76bfd1b1 100644 --- a/src/serious_python/example/bridge_example/macos/Runner.xcodeproj/project.pbxproj +++ b/src/serious_python/example/bridge_example/macos/Runner.xcodeproj/project.pbxproj @@ -27,6 +27,7 @@ 33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F22044A3C60003C045 /* Assets.xcassets */; }; 33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F42044A3C60003C045 /* MainMenu.xib */; }; 33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */; }; + 78A318202AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage in Frameworks */ = {isa = PBXBuildFile; productRef = 78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */; }; B72E96C288A175DA4181F1E9 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B9E3AB6184BEAE07C5108EE3 /* Pods_Runner.framework */; }; E3DFE7ECAF91BB85499213B0 /* Pods_RunnerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E0FC039480642B362225F49B /* Pods_RunnerTests.framework */; }; /* End PBXBuildFile section */ @@ -82,6 +83,7 @@ 39E5122CF63AF062558CE40F /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; 4DDC5FA0C150DCFDA5784BFA /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; 7556ABD1A47FA4D65C4B0DFC /* Pods-RunnerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.release.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.release.xcconfig"; sourceTree = ""; }; + 78E0A7A72DC9AD7400C4905E /* FlutterGeneratedPluginSwiftPackage */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = FlutterGeneratedPluginSwiftPackage; path = ephemeral/Packages/FlutterGeneratedPluginSwiftPackage; sourceTree = ""; }; 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Release.xcconfig; sourceTree = ""; }; 8B28C1B131F74EC8875EDFCF /* Pods-RunnerTests.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.profile.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.profile.xcconfig"; sourceTree = ""; }; 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = ""; }; @@ -103,6 +105,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 78A318202AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage in Frameworks */, B72E96C288A175DA4181F1E9 /* Pods_Runner.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -164,6 +167,7 @@ 33CEB47122A05771004F2AC0 /* Flutter */ = { isa = PBXGroup; children = ( + 78E0A7A72DC9AD7400C4905E /* FlutterGeneratedPluginSwiftPackage */, 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */, 33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */, 33CEB47422A05771004F2AC0 /* Flutter-Release.xcconfig */, @@ -240,8 +244,6 @@ 33CC10EB2044A3C60003C045 /* Resources */, 33CC110E2044A8840003C045 /* Bundle Framework */, 3399D490228B24CF009A79C7 /* ShellScript */, - 5019070AFE2F41F3F49E930B /* [CP] Embed Pods Frameworks */, - 583F80258F9FB1DB887F6668 /* [CP] Copy Pods Resources */, ); buildRules = ( ); @@ -249,6 +251,9 @@ 33CC11202044C79F0003C045 /* PBXTargetDependency */, ); name = Runner; + packageProductDependencies = ( + 78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */, + ); productName = Runner; productReference = 33CC10ED2044A3C60003C045 /* serious_python_bridge_example.app */; productType = "com.apple.product-type.application"; @@ -293,6 +298,9 @@ Base, ); mainGroup = 33CC10E42044A3C60003C045; + packageReferences = ( + 781AD8BC2B33823900A9FFBB /* XCLocalSwiftPackageReference "FlutterGeneratedPluginSwiftPackage" */, + ); productRefGroup = 33CC10EE2044A3C60003C045 /* Products */; projectDirPath = ""; projectRoot = ""; @@ -362,40 +370,6 @@ shellPath = /bin/sh; shellScript = "\"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh && touch Flutter/ephemeral/tripwire"; }; - 5019070AFE2F41F3F49E930B /* [CP] Embed Pods Frameworks */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist", - ); - name = "[CP] Embed Pods Frameworks"; - outputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; - showEnvVarsInLog = 0; - }; - 583F80258F9FB1DB887F6668 /* [CP] Copy Pods Resources */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-input-files.xcfilelist", - ); - name = "[CP] Copy Pods Resources"; - outputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-output-files.xcfilelist", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh\"\n"; - showEnvVarsInLog = 0; - }; BBA99A121B65ED7E52DABCEE /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; @@ -814,6 +788,20 @@ defaultConfigurationName = Release; }; /* End XCConfigurationList section */ + +/* Begin XCLocalSwiftPackageReference section */ + 781AD8BC2B33823900A9FFBB /* XCLocalSwiftPackageReference "FlutterGeneratedPluginSwiftPackage" */ = { + isa = XCLocalSwiftPackageReference; + relativePath = Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage; + }; +/* End XCLocalSwiftPackageReference section */ + +/* Begin XCSwiftPackageProductDependency section */ + 78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */ = { + isa = XCSwiftPackageProductDependency; + productName = FlutterGeneratedPluginSwiftPackage; + }; +/* End XCSwiftPackageProductDependency section */ }; rootObject = 33CC10E52044A3C60003C045 /* Project object */; } diff --git a/src/serious_python/example/bridge_example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/src/serious_python/example/bridge_example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme index 1fb7fdd6..79ee10d3 100644 --- a/src/serious_python/example/bridge_example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ b/src/serious_python/example/bridge_example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -5,6 +5,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +