From 46237421b2cf251d598b638ffc7dad7fd62aaa4d Mon Sep 17 00:00:00 2001 From: ndonkoHenri Date: Sat, 20 Jun 2026 02:34:35 +0200 Subject: [PATCH 1/3] recipe: psycopg2 2.9.12 (+ flet-libpq 17.5) Adds the psycopg2 PostgreSQL driver (flet-dev/flet#3204) for iOS and Android, plus its native dependency flet-libpq. - flet-libpq 17.5: PostgreSQL's libpq, built from the release tarball (client library only). Ships the static libpq.a + libpgcommon/libpgport archives, headers and a relocatable pg_config shim, alongside a shared libpq.so. Cross-compile fixes: ac_cv_header_langinfo_h=no (Android API 24 hides nl_langinfo), strip quotes from CFLAGS/LDFLAGS (postgres bakes them into config_info.c string literals on iOS), and make all-lib + pre-touch libpq-refs-stamp (iOS _atexit false-positive). - psycopg2 2.9.12: a Cython C-extension that statically links libpq + OpenSSL into _psycopg.so. mobile.patch ships the *_shlib.a PIC archives, links OpenSSL + the extra archives unconditionally (psycopg2 only does so in finalize_darwin/finalize_linux, which the cross sys.platform matches neither), and reads pg_config from the PG_CONFIG env var. Tested: built green on Android arm64-v8a and iOS (device + arm64/x86_64 sim), Python 3.12. Live on-device round-trip (connect + SELECT against PostgreSQL 17.10) passes on an Android emulator and an iOS simulator. --- recipes/flet-libpq/build.sh | 131 ++++++++++++++++++++++++ recipes/flet-libpq/meta.yaml | 21 ++++ recipes/psycopg2/meta.yaml | 22 ++++ recipes/psycopg2/patches/mobile.patch | 40 ++++++++ recipes/psycopg2/tests/test_psycopg2.py | 32 ++++++ 5 files changed, 246 insertions(+) create mode 100755 recipes/flet-libpq/build.sh create mode 100644 recipes/flet-libpq/meta.yaml create mode 100644 recipes/psycopg2/meta.yaml create mode 100644 recipes/psycopg2/patches/mobile.patch create mode 100644 recipes/psycopg2/tests/test_psycopg2.py diff --git a/recipes/flet-libpq/build.sh b/recipes/flet-libpq/build.sh new file mode 100755 index 00000000..1e33171e --- /dev/null +++ b/recipes/flet-libpq/build.sh @@ -0,0 +1,131 @@ +#!/bin/bash +# flet-libpq: PostgreSQL's libpq client library for the psycopg recipes. Ships: +# - opt/lib/libpq.so shared lib for psycopg v3's ctypes loader +# - opt/lib/libpq.a + libpgcommon.a + libpgport.a static+PIC, for psycopg2 +# (compiled C-ext, static_libpq) +# - opt/include/... headers for the psycopg2 compile +# - opt/bin/pg_config relocatable shim psycopg2's setup.py queries +# TLS via the support-tree OpenSSL ($OPENSSL_DIR). +set -eu + +NAME=pq +SRCROOT="$PWD" + +# Point configure at the support-tree OpenSSL for TLS (libpq's --with-openssl). +export CPPFLAGS="${CPPFLAGS:-} -I$OPENSSL_DIR/include" +export LDFLAGS="${LDFLAGS:-} -L$OPENSSL_DIR/lib" + +# -fPIC so the static .a objects fold cleanly into psycopg2's _psycopg.so. +export CFLAGS="${CFLAGS:-} -fPIC" +common_args="\ + --without-readline \ + --without-zlib \ + --without-zstd \ + --without-lz4 \ + --without-icu \ + --without-gssapi \ + --with-openssl" + +build_libpq() { + # Build libpq + its in-tree deps without the server. generated-headers runs + # host perl/sed to emit errcodes.h etc. (libpgcommon needs them). + make -C src/backend generated-headers + make -C src/common -j "$CPU_COUNT" + make -C src/port -j "$CPU_COUNT" + # all-lib builds the static + shared libpq but skips libpq-refs-stamp, the + # "libpq must not call exit()" sanity check. It false-positives on darwin/iOS + # (_atexit is a normal undefined import from libSystem; the check greps + # GNU-nm-style output). Pre-touch the stamp so `make install` sees it done. + make -C src/interfaces/libpq -j "$CPU_COUNT" all-lib + touch src/interfaces/libpq/libpq-refs-stamp + make -C src/interfaces/libpq install + make -C src/include install # libpq-fe.h, postgres_ext.h, pg_config*.h, ... + + # psycopg2 (static_libpq) links libpq.a, which references symbols from + # libpgcommon/libpgport. libpq.a uses the PLAIN symbol names, which live in + # the *_shlib.a* variants (PostgreSQL namespaces the non-shlib archives' + # internals with a _private suffix); the _shlib archives are also PIC, so + # they fold cleanly into _psycopg.so. Ship those, renamed. + cp "$SRCROOT/src/common/libpgcommon_shlib.a" "$PREFIX/lib/libpgcommon.a" + cp "$SRCROOT/src/port/libpgport_shlib.a" "$PREFIX/lib/libpgport.a" +} + +if [ "$CROSS_VENV_SDK" = "android" ]; then + # Android bionic only declares nl_langinfo() at API >= 26; we target API 24, + # so its declaration is hidden and chklocale.c hits an implicit-declaration + # (int-to-pointer) error under clang 18. Tell configure langinfo.h is absent + # so libpq falls back to its non-langinfo encoding detection. + ac_cv_header_langinfo_h=no \ + ./configure --host=$HOST_TRIPLET --prefix=$PREFIX $common_args + build_libpq + + # Collapse the versioned .so + symlinks into a single unversioned libpq.so + # (Android extracts only files literally matching lib*.so; psycopg dlopens + # "libpq.so" by name). + cd "$PREFIX/lib" + real=$(readlink -f "lib$NAME.so") + tmp=$(mktemp); cp "$real" "$tmp" + rm -f "lib$NAME.so" "lib$NAME.so".* + mv "$tmp" "lib$NAME.so"; chmod 755 "lib$NAME.so" + cd - >/dev/null +else + # config.sub doesn't know Apple-mobile triplets — feed it an equivalent + # Darwin triplet (CC/CFLAGS from forge do the real targeting). + case $HOST_TRIPLET in + arm64-apple-ios) host=arm-apple-darwin23 ;; + arm64-apple-ios-simulator) host=aarch64-apple-darwin23 ;; + x86_64-apple-ios-simulator) host=x86_64-apple-darwin23 ;; + *) echo "Unknown iOS host triplet: $HOST_TRIPLET"; exit 1 ;; + esac + # forge's iOS CFLAGS/LDFLAGS embed framework search paths as -F "…" with + # literal quotes. PostgreSQL bakes the configure-time flags into + # config_info.c as C string literals (-DVAL_CFLAGS="…" etc.); the embedded + # quotes terminate the string early, so the path components parse as bare + # identifiers ("use of undeclared identifier 'Users'") and 3.12.13 as a + # float. Strip the quotes — the forge paths contain no spaces, so -F path + # still resolves. + export CFLAGS="$(printf '%s' "$CFLAGS" | tr -d '"')" + export CPPFLAGS="$(printf '%s' "$CPPFLAGS" | tr -d '"')" + export LDFLAGS="$(printf '%s' "$LDFLAGS" | tr -d '"')" + ./configure --host=$host --prefix=$PREFIX $common_args + build_libpq + + # PostgreSQL's Makefile.shlib emits a versioned darwin dylib; normalize to a + # single libpq.so the ctypes loader can dlopen. + cd "$PREFIX/lib" + real=$(ls lib$NAME.*.dylib lib$NAME.dylib 2>/dev/null | head -1) + if [ -n "$real" ]; then + cp "$real" "_tmp_lib$NAME" + rm -f "lib$NAME"*.dylib "lib$NAME.so" + mv "_tmp_lib$NAME" "lib$NAME.so" + install_name_tool -id "@rpath/lib$NAME.so" "lib$NAME.so" 2>/dev/null || true + fi + cd - >/dev/null +fi + +# pg_config shim: psycopg2's setup.py shells out to pg_config to locate libpq. +# The real cross-built pg_config is a target binary that can't run on the build +# host, so ship a relocatable shell shim that reports paths relative to itself +# (opt/bin/pg_config -> opt/include, opt/lib). +mkdir -p "$PREFIX/bin" +cat > "$PREFIX/bin/pg_config" <<'PGC' +#!/bin/sh +here=$(cd "$(dirname "$0")/.." && pwd) +case "$1" in + --includedir) echo "$here/include" ;; + --includedir-server) echo "$here/include/postgresql/server" ;; + --libdir|--pkglibdir) echo "$here/lib" ;; + --bindir) echo "$here/bin" ;; + --version) echo "PostgreSQL 17.5" ;; + --cppflags|--cflags|--cflags_sl|--ldflags|--ldflags_ex|--ldflags_sl|--libs) echo "" ;; + *) echo "" ;; +esac +PGC +chmod 755 "$PREFIX/bin/pg_config" + +# Keep opt/lib/{libpq.so,*.a}, opt/include, opt/bin/pg_config. Drop pkgconfig, +# libtool archives, versioned dylibs/sonames, and share/. +shopt -s nullglob +rm -rf "$PREFIX/share" +rm -rf "$PREFIX/lib/pkgconfig" "$PREFIX"/lib/*.la +rm -f "$PREFIX"/lib/lib$NAME.dylib "$PREFIX"/lib/lib$NAME.*.dylib diff --git a/recipes/flet-libpq/meta.yaml b/recipes/flet-libpq/meta.yaml new file mode 100644 index 00000000..530bac33 --- /dev/null +++ b/recipes/flet-libpq/meta.yaml @@ -0,0 +1,21 @@ +{% set version = "17.5" %} + +package: + name: flet-libpq + version: '{{ version }}' + +source: + # PostgreSQL ships a release tarball with a pre-generated ./configure + # (autotools); we build only src/interfaces/libpq (the client library). + url: https://ftp.postgresql.org/pub/source/v{{ version }}/postgresql-{{ version }}.tar.gz + +build: + number: 1 + script_env: + # OpenSSL (libpq TLS) comes from the python-build support tree, surfaced by + # the `openssl` host requirement at {platlib}/opt (same as cryptography). + OPENSSL_DIR: '{platlib}/opt' + +requirements: + host: + - openssl ^3.0.12 diff --git a/recipes/psycopg2/meta.yaml b/recipes/psycopg2/meta.yaml new file mode 100644 index 00000000..8d62c89b --- /dev/null +++ b/recipes/psycopg2/meta.yaml @@ -0,0 +1,22 @@ +package: + name: psycopg2 + version: 2.9.12 + +build: + number: 1 + script_env: + # psycopg2's setup.py shells out to pg_config to locate libpq. flet-libpq + # ships a relocatable shim at opt/bin/pg_config; mobile.patch teaches the + # setup to read its path from PG_CONFIG (it otherwise only checks PATH). + PG_CONFIG: '{platlib}/opt/bin/pg_config' + +requirements: + host: + # static_libpq=1 (mobile.patch) folds libpq.a + libpgcommon.a + libpgport.a + # into _psycopg.so; openssl resolves libpq's TLS symbols (-lssl -lcrypto, + # added by setup.py's finalize_darwin since the build host is macOS). + - flet-libpq 17.5 + - openssl ^3.0.12 + +patches: + - mobile.patch diff --git a/recipes/psycopg2/patches/mobile.patch b/recipes/psycopg2/patches/mobile.patch new file mode 100644 index 00000000..58e0a322 --- /dev/null +++ b/recipes/psycopg2/patches/mobile.patch @@ -0,0 +1,40 @@ +--- a/setup.py 2026-06-17 21:43:57 ++++ b/setup.py 2026-06-17 21:48:30 +@@ -79,6 +79,8 @@ + self.build_ext = build_ext + self.pg_config_exe = self.build_ext.pg_config + if not self.pg_config_exe: ++ self.pg_config_exe = os.environ.get("PG_CONFIG") # mobile-forge ++ if not self.pg_config_exe: + self.pg_config_exe = self.autodetect_pg_config_path() + if self.pg_config_exe is None: + sys.stderr.write(""" +@@ -362,6 +364,17 @@ + self.link_objects = [] + self.link_objects.append( + os.path.join(pg_config_helper.query("libdir"), "libpq.a")) ++ # mobile-forge: libpq.a references libpgcommon/libpgport symbols, ++ # and the whole static stack references OpenSSL (libpq TLS). Add ++ # both archives and -lssl/-lcrypto here so the link resolves on ++ # every target (the cross sys.platform matches no finalize_*). ++ for _a in ("libpgcommon.a", "libpgport.a"): ++ self.link_objects.append( ++ os.path.join(pg_config_helper.query("libdir"), _a)) ++ if self.libraries is None: ++ self.libraries = [] ++ self.libraries.append("ssl") ++ self.libraries.append("crypto") + else: + self.libraries.append("pq") + +--- a/setup.cfg 2026-06-17 21:43:57 ++++ b/setup.cfg 2026-06-17 21:48:30 +@@ -2,7 +2,7 @@ + define = PSYCOPG_DEBUG + pg_config = + have_ssl = 0 +-static_libpq = 0 ++static_libpq = 1 + libraries = + + [metadata] diff --git a/recipes/psycopg2/tests/test_psycopg2.py b/recipes/psycopg2/tests/test_psycopg2.py new file mode 100644 index 00000000..f4fff324 --- /dev/null +++ b/recipes/psycopg2/tests/test_psycopg2.py @@ -0,0 +1,32 @@ +import pytest + + +def test_import(): + """Importing psycopg2 loads its compiled _psycopg extension (libpq + OpenSSL + statically linked) on-device and resolves all its symbols. A real query + needs a PostgreSQL server, so this proves the wheel imports and the C + extension initializes.""" + import psycopg2 + import psycopg2._psycopg # the compiled extension + + assert psycopg2.__version__ + assert callable(psycopg2.connect) + + +def test_exception_api(): + """psycopg2 exposes the DB-API exception hierarchy callers catch.""" + import psycopg2 + + for exc in ("Error", "OperationalError", "DatabaseError", "InterfaceError"): + assert issubclass(getattr(psycopg2, exc), Exception) + + +def test_connect_refused(): + """Drives libpq's native connect path with no server needed: a closed local + port refuses immediately and psycopg2 must translate that into + OperationalError. Proves the statically-linked libpq actually *runs* on + device, not merely that the extension loaded.""" + import psycopg2 + + with pytest.raises(psycopg2.OperationalError): + psycopg2.connect(host="127.0.0.1", port=1, connect_timeout=2) From 7b09a640bdb74e9eb4072036d06c2b00cd647f37 Mon Sep 17 00:00:00 2001 From: ndonkoHenri Date: Sat, 20 Jun 2026 03:33:47 +0200 Subject: [PATCH 2/3] forge: add requirements.host_build for build-time-only native deps A native lib that a C-extension STATICALLY links is folded into the extension's own .so, so it is needed at build time but not at runtime. requirements.host, however, promotes every flet-* dep to the wheel's Requires-Dist, so the consuming app bundles the unused flet-lib* wheel (~2.7 MB android / ~3.9 MB iOS of dead weight per app). Add requirements.host_build: installed into the cross environment exactly like host (its opt/include + opt/lib wired into CFLAGS/LDFLAGS and pkg-config), but excluded from Requires-Dist (the promotion loop only walks "host"). psycopg2 adopts it for flet-libpq; pymssql -> flet-libfreetds is a natural follow-on. Also rewrites the stale requirements.build / requirements.host schema descriptions: they carried chaquopy-heritage claims (a "cmake" build keyword generating chaquopy.toolchain.cmake; "openssl"/"sqlite"/"python" host keyword special-cases; a $SRC_DIR/../requirements extraction path; a ">=" Requires-Dist pin) that the current builders do not implement. They now accurately describe the build vs cross environment, the install_root CFLAGS/LDFLAGS/pkg-config wiring, the {platlib}/{prefix} script_env placeholders, and the flet-*-only "==" Requires-Dist promotion. Verified: psycopg2 builds green on android arm64 with host_build, the wheel no longer lists flet-libpq in Requires-Dist, the app no longer bundles flet-libpq, and psycopg2 still connects + SELECTs against live PostgreSQL 17.10 on-device. --- recipes/psycopg2/meta.yaml | 14 +++++--- src/forge/build.py | 5 +++ src/forge/schema/meta-schema.yaml | 58 +++++++++++++++++++++---------- 3 files changed, 54 insertions(+), 23 deletions(-) diff --git a/recipes/psycopg2/meta.yaml b/recipes/psycopg2/meta.yaml index 8d62c89b..7525dfac 100644 --- a/recipes/psycopg2/meta.yaml +++ b/recipes/psycopg2/meta.yaml @@ -12,11 +12,17 @@ build: requirements: host: - # static_libpq=1 (mobile.patch) folds libpq.a + libpgcommon.a + libpgport.a - # into _psycopg.so; openssl resolves libpq's TLS symbols (-lssl -lcrypto, - # added by setup.py's finalize_darwin since the build host is macOS). - - flet-libpq 17.5 + # openssl resolves libpq's TLS symbols (-lssl -lcrypto, added by setup.py's + # finalize_darwin since the build host is macOS). It lives in the base flet + # runtime, so it isn't promoted to Requires-Dist. - openssl ^3.0.12 + host_build: + # flet-libpq is STATICALLY folded into _psycopg.so (static_libpq=1, + # mobile.patch links libpq.a + libpgcommon.a + libpgport.a) — so it's a + # build-time-only dependency. host_build extracts it for the link without + # adding it to the wheel's Requires-Dist, so a psycopg2 app does not bundle + # the (unused) flet-libpq wheel at runtime. + - flet-libpq 17.5 patches: - mobile.patch diff --git a/src/forge/build.py b/src/forge/build.py index c02566e3..2536c9d7 100644 --- a/src/forge/build.py +++ b/src/forge/build.py @@ -258,6 +258,11 @@ def prepare(self, clean=True): log(self.log_file, f"\n[{self.cross_venv}] Install forge host requirements") self.install_requirements("host") + # host_build deps install into the cross env like host deps (so the build + # can link them), but are NOT promoted to the wheel's Requires-Dist (that + # loop below only walks "host"). For statically-linked native libs. + log(self.log_file, f"\n[{self.cross_venv}] Install forge host_build requirements") + self.install_requirements("host_build") self.fix_host_tool_shims() log(self.log_file, f"\n[{self.cross_venv}] Install forge build requirements") diff --git a/src/forge/schema/meta-schema.yaml b/src/forge/schema/meta-schema.yaml index 6969f1ed..5e222ea5 100644 --- a/src/forge/schema/meta-schema.yaml +++ b/src/forge/schema/meta-schema.yaml @@ -124,12 +124,14 @@ properties: items: type: string description: |- - Requirements which must be installed in the build environment. - One of the following: - * ` `: A Python package. - * `cmake`: indicates that CMake is used in the build. A - `chaquopy.toolchain.cmake` file will be generated in the - build directory for use with `-DCMAKE_TOOLCHAIN_FILE`. + Build-time tools, installed into the BUILD environment — the + host-runnable side of the cross venv (via `build-pip`) — for programs + that must RUN on the build machine to produce the wheel, e.g. `cython`, + `cmake`, `ninja`, `meson`, `setuptools`. Source distributions are + allowed here (this environment is native to the build host). These are + NEVER recorded as runtime dependencies of the wheel (not added to + Requires-Dist). The package's own PEP 517 `build-system.requires` (from + its pyproject.toml) are installed automatically in addition to these. host: type: array @@ -137,19 +139,37 @@ properties: items: type: string description: |- - Requirements which must be available at runtime. One of the - following: - * ` `: a native Python package. A - compatible wheel file must exist in pypi/dist, and will be - extracted into $SRC_DIR/../requirements before the build - is run. A requirement specification for >= this version - will also be added to the final wheel. - * `python`: indicates that this is a Python package. This is - implied if `source` is `pypi` or unspecified. Python - includes and libraries will be added to the CFLAGS and - LDFLAGS, and the wheel build tag will be set accordingly. - * `openssl` / `sqlite`: the corresponding library will be - added to CFLAGS and LDFLAGS. + Native (cross-compiled) libraries the package links against, installed + into the CROSS/target environment — target binary wheels only + (`--only-binary`; a matching wheel must resolve from `dist/` or + pypi.flet.dev). Each unpacks an `opt/` tree under the cross env + (`install_root`); its `opt/include` and `opt/lib` are added + automatically to the build's CFLAGS/LDFLAGS, and `opt/lib/pkgconfig` to + the pkg-config search path. A recipe can also reach specific files via + the `{platlib}`/`{prefix}` placeholders in `build.script_env` + (e.g. `{platlib}/opt/bin/pg_config`). A `flet-*` host requirement is + ALSO written into the wheel's `Requires-Dist` (pinned `==` to its + version), so the consuming app bundles it at runtime — use `host` when + the package DYNAMICALLY links or `ctypes`-loads the lib at runtime + (e.g. psycopg v3 -> flet-libpq, pyzbar -> flet-libzbar). Non-`flet-*` + entries (e.g. `openssl`, part of the base Flet runtime) are used for the + build link only and are NOT promoted to Requires-Dist. For a lib that is + STATICALLY linked into the package instead, use `host_build`. + + host_build: + type: array + default: [] + items: + type: string + description: |- + Like `host` — a native target library installed into the cross + environment, with its `opt/include`/`opt/lib` wired into the build's + CFLAGS/LDFLAGS/pkg-config — but BUILD-TIME ONLY: it is NOT added to + the wheel's Requires-Dist. Use for a `flet-*` lib the package STATICALLY + links and folds into its own `.so` (e.g. psycopg2 -> flet-libpq, + pymssql -> flet-libfreetds): the lib is baked into the extension at + build time, so the consuming app must not also bundle the (unused) + `flet-*` wheel at runtime. additionalProperties: false From 4ac26c01c72d74ed863319aff6bc23dd5d65efaa Mon Sep 17 00:00:00 2001 From: ndonkoHenri Date: Sat, 20 Jun 2026 03:40:29 +0200 Subject: [PATCH 3/3] feat(build): enhance support-tree dep wheel management in GitHub Actions --- .github/workflows/build-wheels-version.yml | 35 +++++++++++++++++++--- setup.sh | 11 +++++++ 2 files changed, 42 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build-wheels-version.yml b/.github/workflows/build-wheels-version.yml index 798a4724..d6a623ff 100644 --- a/.github/workflows/build-wheels-version.yml +++ b/.github/workflows/build-wheels-version.yml @@ -279,10 +279,37 @@ jobs: - name: Drop support-tree dep wheels if: always() shell: bash - # Drop the CPython support-tree dep wheels produced by make_dep_wheels.py - # iOS deps: bzip2, libffi, mpdecimal, openssl, xz - # Android deps: bzip2, libffi, openssl, sqlite, xz - run: rm -f dist/bzip2-* dist/libffi-* dist/mpdecimal-* dist/openssl-* dist/sqlite-* dist/xz-* + env: + PLATFORM: ${{ matrix.platform }} + PYTHON_SHORT: ${{ matrix.python_short }} + # The CPython support tree bundles native libs (openssl, xz, zstd, …) that + # make_dep_wheels.py wraps into `--py3-none-.whl` so forge can + # resolve them as build deps — they must not be published as recipe wheels. + # Derive the set to drop from the support tree's VERSIONS manifest (the same + # source make_dep_wheels.py reads). + run: | + set -euo pipefail + if [[ "$PLATFORM" == "android" ]]; then + os_name="android"; support="${MOBILE_FORGE_ANDROID_SUPPORT_PATH:-}" + else + os_name="iOS"; support="${MOBILE_FORGE_IOS_SUPPORT_PATH:-}" + fi + versions="${support}/support/${PYTHON_SHORT}/${os_name}/VERSIONS" + if [[ ! -f "$versions" ]]; then + echo "::warning::No VERSIONS at '$versions' — skipping support-wheel drop" + exit 0 + fi + echo "Dropping support-tree dep wheels declared in $versions" + while IFS= read -r line || [[ -n "$line" ]]; do + [[ "$line" == *:* ]] || continue + key="$(printf '%s' "${line%%:*}" | tr '[:upper:]' '[:lower:]' | xargs)" + case "$key" in + "python version"|"build"|"min "*) continue ;; + esac + [[ -n "$key" ]] || continue + echo " - dropping dist/${key}-*" + rm -f "dist/${key}-"* + done < "$versions" - name: Summarize built wheels if: always() diff --git a/setup.sh b/setup.sh index f09aae29..b911bd0d 100755 --- a/setup.sh +++ b/setup.sh @@ -354,6 +354,17 @@ if [ ! -z "$MOBILE_FORGE_ANDROID_SUPPORT_PATH" ]; then echo "MOBILE_FORGE_ANDROID_SUPPORT_PATH: $MOBILE_FORGE_ANDROID_SUPPORT_PATH" fi +# In GitHub Actions, persist the resolved support-tree paths to the job environment so +# later workflow steps can read them — exports from a `source`d script don't survive +# across steps. Consumed by the "Drop support-tree dep wheels" step, which reads each +# tree's VERSIONS manifest to know which wheels to drop. No-op locally ($GITHUB_ENV unset). +if [ -n "${GITHUB_ENV:-}" ]; then + { + echo "MOBILE_FORGE_IOS_SUPPORT_PATH=$MOBILE_FORGE_IOS_SUPPORT_PATH" + echo "MOBILE_FORGE_ANDROID_SUPPORT_PATH=$MOBILE_FORGE_ANDROID_SUPPORT_PATH" + } >> "$GITHUB_ENV" +fi + echo echo "You can now build packages with forge; e.g.:" echo