Skip to content

Commit a7378aa

Browse files
committed
Each plugin gets a local committed copy of native_bridge/
The "shared src/native_bridge/ + per-plugin add_subdirectory('../../native_bridge')" design ran into two unrelated platform constraints: - CocoaPods 1.16 silently rejects source_files whose resolved target lies outside the pod's enclosing directory. - Flutter on Windows symlinks plugins via NTFS junction points, which CMake's get_filename_component(REALPATH) and file(REAL_PATH) don't traverse — relative add_subdirectory paths land at non-existent .plugin_symlinks neighbors. Two options were on the table. Sharing via cross-tree CMake references is fragile and platform-specific; per-plugin local copies are boring but robust. Go with per-plugin local copies — same model the darwin podspec already uses (FileUtils.cp into Classes/). Each platform plugin now ships its own native_bridge/ subdirectory and its CMakeLists uses add_subdirectory("native_bridge") — purely local, no path-traversal tricks. scripts/sync_native_bridge.sh copies from the monorepo-canonical src/native_bridge/ into each plugin's local tree and into darwin/Classes/. Run before commits when bridge C source changes. `serious_python_bridge/python/setup.py` keeps its `NATIVE_ROOT = "../../native_bridge"` since cibuildwheel only runs in the monorepo (where the canonical path exists) and end users never invoke setup.py themselves.
1 parent 801c228 commit a7378aa

35 files changed

Lines changed: 17275 additions & 21 deletions

scripts/sync_native_bridge.sh

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
#!/usr/bin/env bash
2+
#
3+
# Sync the canonical bridge C sources at src/native_bridge/ into each
4+
# serious_python_bridge/<plat>/ plugin's local committed copy.
5+
#
6+
# Why each plugin owns a copy:
7+
# - CocoaPods rejects symlinks/source_files that resolve outside the pod's
8+
# enclosing directory. The darwin podspec works around this with a Ruby
9+
# FileUtils.cp block (see darwin/serious_python_bridge.podspec), so the
10+
# committed darwin/Classes/ files are kept in sync that way.
11+
# - Flutter symlinks plugins into <example>/<plat>/flutter/ephemeral/
12+
# .plugin_symlinks/. On Windows those are NTFS junction points which
13+
# CMake's get_filename_component / file(REAL_PATH) don't traverse, so
14+
# "../../native_bridge" lands at a non-existent neighbor path.
15+
# - Result: each platform plugin's CMakeLists uses a LOCAL native_bridge/
16+
# subdir as add_subdirectory target. We sync those local copies from
17+
# the canonical src/native_bridge/ via this script.
18+
#
19+
# Run from anywhere; resolves to the repo root from $0's location.
20+
set -euo pipefail
21+
22+
cd "$(dirname "$0")/.."
23+
ROOT="$PWD"
24+
SRC="$ROOT/src/native_bridge"
25+
26+
if [ ! -d "$SRC" ]; then
27+
echo "error: canonical source not found at $SRC" >&2
28+
exit 1
29+
fi
30+
31+
for plat in linux windows android; do
32+
DEST="$ROOT/src/serious_python_bridge/$plat/native_bridge"
33+
echo "[sync] $SRC -> $DEST"
34+
rm -rf "$DEST"
35+
mkdir -p "$DEST"
36+
cp -R "$SRC/"* "$DEST/"
37+
done
38+
39+
# Apple: copy directly into Classes/ (mirrors the FileUtils.cp logic in
40+
# darwin/serious_python_bridge.podspec; pre-populating means CI doesn't need
41+
# to invoke pod install just to refresh sources).
42+
DARWIN_DEST="$ROOT/src/serious_python_bridge/darwin/Classes"
43+
echo "[sync] $SRC -> $DARWIN_DEST"
44+
cp "$SRC/dart_bridge.c" "$DARWIN_DEST/"
45+
cp "$SRC/dart_bridge_shim.c" "$DARWIN_DEST/"
46+
rm -rf "$DARWIN_DEST/dart_api"
47+
cp -R "$SRC/dart_api" "$DARWIN_DEST/"
48+
49+
echo "[sync] done. Diff against committed copies:"
50+
git -C "$ROOT" status --short src/serious_python_bridge/{linux,windows,android}/native_bridge src/serious_python_bridge/darwin/Classes/

src/serious_python_bridge/android/CMakeLists.txt

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,10 @@ set(PYTHON_ABI_DIR "${PYTHON_DIST_DIR}/${ANDROID_ABI}")
1818
set(FLET_BRIDGE_PYTHON_INCLUDE_DIRS "${PYTHON_ABI_DIR}/include/python${PYTHON_VERSION}")
1919
set(FLET_BRIDGE_PYTHON_LIBRARIES "${PYTHON_ABI_DIR}/lib/libpython3.so")
2020

21-
add_subdirectory("../../native_bridge" "${CMAKE_CURRENT_BINARY_DIR}/shared")
21+
# See note in linux/CMakeLists.txt — each platform plugin keeps its own
22+
# committed copy of native_bridge/, synced from src/native_bridge/ via
23+
# scripts/sync_native_bridge.sh.
24+
add_subdirectory("native_bridge" "${CMAKE_CURRENT_BINARY_DIR}/shared")
2225

2326
# Android 15: 16 KB page size support.
2427
set_target_properties(flet_bridge PROPERTIES
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
# Cross-platform CMake target for libflet_bridge.
2+
#
3+
# Built from the same dart_bridge.c source used by the Python shim. Per-platform
4+
# wrappers under linux/, windows/, android/ include this file via add_subdirectory.
5+
#
6+
# Python integration:
7+
# - If FLET_BRIDGE_PYTHON_INCLUDE_DIRS + FLET_BRIDGE_PYTHON_LIBRARIES are set
8+
# before add_subdirectory, those are used (Android uses this path to point
9+
# at the python-android-dart tarball it downloads).
10+
# - Otherwise we use find_package(Python3 ... Development.Module) + the
11+
# Python3::Module imported target, which handles Linux/macOS/Windows link
12+
# conventions automatically.
13+
14+
cmake_minimum_required(VERSION 3.15)
15+
16+
project(flet_bridge VERSION 0.0.1 LANGUAGES C)
17+
18+
if(NOT DEFINED FLET_BRIDGE_PYTHON_INCLUDE_DIRS)
19+
find_package(Python3 REQUIRED COMPONENTS Development.Module)
20+
set(FLET_BRIDGE_PYTHON_INCLUDE_DIRS ${Python3_INCLUDE_DIRS})
21+
if(WIN32)
22+
# Python3::Module picks pythonXY.lib (or pythonXY_d.lib in Debug builds),
23+
# but python-build-standalone for Windows doesn't ship a Debug variant
24+
# so Flutter's debug builds fail with LNK1104. Link the abi3 stable-ABI
25+
# stub python3.lib instead — it's version-agnostic AND has no debug
26+
# variant requirement (matches our Py_LIMITED_API choice).
27+
get_filename_component(_flet_bridge_python_root "${Python3_INCLUDE_DIRS}" DIRECTORY)
28+
set(FLET_BRIDGE_PYTHON_LIBRARIES "${_flet_bridge_python_root}/libs/python3.lib")
29+
else()
30+
set(FLET_BRIDGE_PYTHON_LIBRARIES Python3::Module)
31+
endif()
32+
endif()
33+
34+
add_library(flet_bridge SHARED
35+
"dart_bridge.c"
36+
"dart_api/dart_api_dl.c"
37+
)
38+
39+
target_include_directories(flet_bridge PRIVATE
40+
"${CMAKE_CURRENT_SOURCE_DIR}"
41+
${FLET_BRIDGE_PYTHON_INCLUDE_DIRS}
42+
)
43+
44+
target_link_libraries(flet_bridge PRIVATE ${FLET_BRIDGE_PYTHON_LIBRARIES})
45+
46+
set_target_properties(flet_bridge PROPERTIES
47+
C_VISIBILITY_PRESET hidden
48+
OUTPUT_NAME "flet_bridge"
49+
)
50+
51+
target_compile_definitions(flet_bridge PRIVATE DART_SHARED_LIB)
52+
53+
if(WIN32)
54+
# Python's pyconfig.h emits `#pragma comment(lib, "pythonXY_d.lib")` in
55+
# Debug builds, which makes MSVC auto-link the Debug variant of libpython
56+
# even though we explicitly linked python3.lib above. python-build-standalone
57+
# doesn't ship a Debug lib, so the link fails (LNK1104). Py_NO_LINK_LIB
58+
# suppresses the pragma in Python 3.14+, but 3.12 and 3.13 pyconfig.h don't
59+
# honor it — use /NODEFAULTLIB:pythonXY_d.lib to explicitly exclude the
60+
# Debug lib at link time. Works on every supported version.
61+
target_compile_definitions(flet_bridge PRIVATE Py_NO_LINK_LIB)
62+
if(Python3_VERSION_MAJOR AND Python3_VERSION_MINOR)
63+
target_link_options(flet_bridge PRIVATE
64+
"/NODEFAULTLIB:python${Python3_VERSION_MAJOR}${Python3_VERSION_MINOR}_d.lib"
65+
)
66+
endif()
67+
endif()

0 commit comments

Comments
 (0)