From d2af3f8cbc63cca4a006e186131ce89bf4b3e9f7 Mon Sep 17 00:00:00 2001 From: Elias Rohrer Date: Tue, 11 Aug 2026 16:13:47 +0200 Subject: [PATCH] Prepare Python wheel releases Build Python-ABI-independent wheels for supported Linux and macOS targets so users can install LDK Node directly from PyPI. Keep build and publication available through local scripts. Require the complete artifact set on TestPyPI before production, and exercise the bindings on the oldest and newest supported CPython versions. Co-Authored-By: HAL 9000 --- .github/workflows/python.yml | 12 +- .gitignore | 12 ++ README.md | 1 + bindings/python/hatch_build.py | 17 +- bindings/python/pyproject.toml | 48 ++++- scripts/python_build_wheel.sh | 167 +++++++++++++--- scripts/python_publish_package.sh | 227 ++++++++++++++++++++-- scripts/uniffi_bindgen_generate_python.sh | 35 ++-- 8 files changed, 465 insertions(+), 54 deletions(-) diff --git a/.github/workflows/python.yml b/.github/workflows/python.yml index da555d192c..09fff05453 100644 --- a/.github/workflows/python.yml +++ b/.github/workflows/python.yml @@ -12,6 +12,7 @@ jobs: runs-on: ubuntu-latest env: + CARGO_TARGET_DIR: /tmp/cargo-target-ldk-node-python-ci LDK_NODE_PYTHON_DIR: bindings/python steps: @@ -20,6 +21,11 @@ jobs: - name: Install uv uses: astral-sh/setup-uv@v7 + with: + version: "0.12.3" + + - name: Install supported Python versions + run: uv python install 3.10 3.14 - name: Generate Python bindings run: ./scripts/uniffi_bindgen_generate_python.sh @@ -35,4 +41,8 @@ jobs: ESPLORA_ENDPOINT: "http://127.0.0.1:3002" run: | cd $LDK_NODE_PYTHON_DIR - uv run --group dev python -m unittest discover -s src/ldk_node + for python_version in 3.10 3.14; do + UV_PROJECT_ENVIRONMENT=".venv-$python_version" \ + uv run --python "$python_version" --group dev \ + python -m unittest discover -s src/ldk_node + done diff --git a/.gitignore b/.gitignore index 3d24bbceb5..df13df05d1 100644 --- a/.gitignore +++ b/.gitignore @@ -27,3 +27,15 @@ swift.swiftdoc /bindings/kotlin/ldk-node-android/lib/src/main/kotlin/org/lightningdevkit/ldknode/ldk_node.kt /bindings/kotlin/ldk-node-jvm/lib/src/main/kotlin/org/lightningdevkit/ldknode/ldk_node.kt /bindings/kotlin/ldk-node-jvm/lib/src/main/resources/ + +# Ignore generated Python bindings and build output +/bindings/python/.venv/ +/bindings/python/.venv-*/ +/bindings/python/dist/ +/bindings/python/uv.lock +/bindings/python/wheelhouse/ +/bindings/python/src/ldk_node/ldk_node.py +/bindings/python/src/ldk_node/libldk_node.so +/bindings/python/src/ldk_node/libldk_node.dylib +/bindings/python/__pycache__/ +/bindings/python/src/ldk_node/__pycache__/ diff --git a/README.md b/README.md index 289ada1792..e59dba569b 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,7 @@ [![Crate](https://img.shields.io/crates/v/ldk-node.svg?logo=rust)](https://crates.io/crates/ldk-node) [![Documentation](https://img.shields.io/static/v1?logo=read-the-docs&label=docs.rs&message=ldk-node&color=informational)](https://docs.rs/ldk-node) +[![PyPI](https://img.shields.io/pypi/v/ldk-node.svg?logo=python)](https://pypi.org/project/ldk-node/) [![Maven Central Android](https://img.shields.io/maven-central/v/org.lightningdevkit/ldk-node-android)](https://central.sonatype.com/artifact/org.lightningdevkit/ldk-node-android) [![Maven Central JVM](https://img.shields.io/maven-central/v/org.lightningdevkit/ldk-node-jvm)](https://central.sonatype.com/artifact/org.lightningdevkit/ldk-node-jvm) [![Security Audit](https://github.com/lightningdevkit/ldk-node/actions/workflows/audit.yml/badge.svg)](https://github.com/lightningdevkit/ldk-node/actions/workflows/audit.yml) diff --git a/bindings/python/hatch_build.py b/bindings/python/hatch_build.py index bd5f54d244..5a8b2b4cef 100644 --- a/bindings/python/hatch_build.py +++ b/bindings/python/hatch_build.py @@ -1,7 +1,22 @@ +import os +import sysconfig + from hatchling.builders.hooks.plugin.interface import BuildHookInterface +from hatchling.metadata.plugin.interface import MetadataHookInterface + + +class CustomMetadataHook(MetadataHookInterface): + def update(self, metadata): + readme_path = os.path.join(self.root, "README.md") + if not os.path.isfile(readme_path): + readme_path = os.path.normpath(os.path.join(self.root, "..", "..", "README.md")) + + with open(readme_path, encoding="utf-8") as readme_file: + metadata["readme"] = {"content-type": "text/markdown", "text": readme_file.read()} class CustomBuildHook(BuildHookInterface): def initialize(self, version, build_data): + platform_tag = sysconfig.get_platform().replace("-", "_").replace(".", "_") build_data["pure_python"] = False - build_data["infer_tag"] = True + build_data["tag"] = f"py3-none-{platform_tag}" diff --git a/bindings/python/pyproject.toml b/bindings/python/pyproject.toml index b77801d457..b3e3649bca 100644 --- a/bindings/python/pyproject.toml +++ b/bindings/python/pyproject.toml @@ -1,22 +1,29 @@ [build-system] -requires = ["hatchling"] +requires = ["hatchling==1.32.0"] build-backend = "hatchling.build" [project] name = "ldk_node" -version = "0.7.0" +version = "0.8.0" authors = [ { name="Elias Rohrer", email="dev@tnull.de" }, ] description = "A ready-to-go Lightning node library built using LDK and BDK." -readme = "../../README.md" -requires-python = ">=3.8" +dynamic = ["readme"] +requires-python = ">=3.10" classifiers = [ "Topic :: Software Development :: Libraries", "Topic :: Security :: Cryptography", "License :: OSI Approved :: MIT License", "License :: OSI Approved :: Apache Software License", + "Operating System :: MacOS", + "Operating System :: POSIX :: Linux", "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: 3.13", + "Programming Language :: Python :: 3.14", ] [project.urls] @@ -29,5 +36,38 @@ dev = ["requests"] [tool.hatch.build.targets.wheel] packages = ["src/ldk_node"] +exclude = ["src/ldk_node/test_ldk_node.py"] + +[tool.hatch.build.targets.sdist.force-include] +"../../README.md" = "README.md" + +[tool.hatch.metadata.hooks.custom] [tool.hatch.build.hooks.custom] + +[tool.cibuildwheel] +archs = ["native"] +build = "cp3{10,11,12,13,14}-{manylinux,macosx}_*" +build-frontend = "uv" +test-command = 'python -c "import ldk_node; ldk_node.default_config()"' + +[tool.cibuildwheel.linux] +before-all = [ + "curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --profile minimal --default-toolchain 1.95.0", + '. "$HOME/.cargo/env"', + "cd {package}/../.. && ./scripts/uniffi_bindgen_generate_python.sh", +] +environment-pass = ["CARGO_TARGET_DIR", "SOURCE_DATE_EPOCH"] +manylinux-x86_64-image = "manylinux_2_28" +manylinux-aarch64-image = "manylinux_2_28" + +[tool.cibuildwheel.macos] +before-all = "cd {package}/../.. && RUSTUP_TOOLCHAIN=1.95.0 ./scripts/uniffi_bindgen_generate_python.sh" + +[[tool.cibuildwheel.overrides]] +select = "*-macosx_x86_64" +environment = { MACOSX_DEPLOYMENT_TARGET = "10.12" } + +[[tool.cibuildwheel.overrides]] +select = "*-macosx_arm64" +environment = { MACOSX_DEPLOYMENT_TARGET = "11.0" } diff --git a/scripts/python_build_wheel.sh b/scripts/python_build_wheel.sh index 4bae18479c..56ee467878 100755 --- a/scripts/python_build_wheel.sh +++ b/scripts/python_build_wheel.sh @@ -1,31 +1,154 @@ #!/bin/bash -# Build a Python wheel for the current platform. +# Build and test one native Python wheel on the current host. # -# This script compiles the Rust library, generates Python bindings via UniFFI, -# and builds a platform-specific wheel using uv + hatchling. -# -# Run this on each target platform (Linux, macOS) to collect wheels, then use -# scripts/python_publish_package.sh to publish them. +# Run this script once on each supported native target: +# Linux x86_64, Linux aarch64, macOS x86_64, macOS arm64. +# Linux builds require Docker or Podman. macOS builds require Rust 1.95.0. -set -e +set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)" +OUTPUT_DIR="$REPO_ROOT/bindings/python/wheelhouse" +ALLOW_DIRTY=false +CIBUILDWHEEL_VERSION="4.1.0" + +usage() { + echo "Usage: $0 [--output-dir DIR] [--allow-dirty]" +} + +while [[ $# -gt 0 ]]; do + case "$1" in + --output-dir) + [[ $# -ge 2 ]] || { usage >&2; exit 2; } + OUTPUT_DIR="$2" + shift 2 + ;; + --allow-dirty) + ALLOW_DIRTY=true + shift + ;; + -h|--help) + usage + exit 0 + ;; + *) + echo "Unknown argument: $1" >&2 + usage >&2 + exit 2 + ;; + esac +done + +case "$OUTPUT_DIR" in + /*) ;; + *) OUTPUT_DIR="$REPO_ROOT/$OUTPUT_DIR" ;; +esac + +for command_name in git uv; do + if ! command -v "$command_name" >/dev/null 2>&1; then + echo "Required command not found: $command_name" >&2 + exit 1 + fi +done cd "$REPO_ROOT" -# Generate bindings and compile the native library -echo "Generating Python bindings..." -./scripts/uniffi_bindgen_generate_python.sh - -# Build the wheel -echo "Building wheel..." -cd bindings/python -uv build --wheel - -echo "" -echo "Wheel built successfully:" -ls -1 dist/*.whl -echo "" -echo "Collect wheels from all target platforms into dist/, then run:" -echo " ./scripts/python_publish_package.sh" +if [[ "$ALLOW_DIRTY" == false ]] && [[ -n "$(git status --porcelain --untracked-files=normal)" ]]; then + echo "Refusing to build from a dirty worktree; commit the release first." >&2 + echo "Use --allow-dirty only while developing the build configuration." >&2 + exit 1 +fi + +case "$(uname -s)" in + Linux) + case "${CIBW_CONTAINER_ENGINE:-}" in + podman*) CONTAINER_COMMAND=podman ;; + docker*|"") CONTAINER_COMMAND=docker ;; + *) + echo "Unsupported CIBW_CONTAINER_ENGINE: $CIBW_CONTAINER_ENGINE" >&2 + exit 1 + ;; + esac + + if ! command -v "$CONTAINER_COMMAND" >/dev/null 2>&1; then + if [[ -z "${CIBW_CONTAINER_ENGINE:-}" ]] && command -v podman >/dev/null 2>&1; then + export CIBW_CONTAINER_ENGINE=podman + else + echo "Linux wheel builds require Docker or Podman." >&2 + exit 1 + fi + fi + ;; + Darwin) + if ! command -v rustup >/dev/null 2>&1 || \ + ! rustup run 1.95.0 rustc --version >/dev/null 2>&1; then + echo "macOS wheel builds require the Rust 1.95.0 toolchain." >&2 + echo "Install it with: rustup toolchain install 1.95.0 --profile minimal" >&2 + exit 1 + fi + ;; + *) + echo "Unsupported operating system: $(uname -s)" >&2 + exit 1 + ;; +esac + +case "$(uname -m)" in + x86_64|amd64|aarch64|arm64) ;; + *) + echo "Unsupported architecture: $(uname -m)" >&2 + exit 1 + ;; +esac + +mkdir -p "$OUTPUT_DIR" +shopt -s nullglob +existing_wheels=("$OUTPUT_DIR"/*.whl) +if [[ ${#existing_wheels[@]} -ne 0 ]]; then + echo "Output directory already contains wheels: $OUTPUT_DIR" >&2 + exit 1 +fi + +CARGO_TARGET_DIR="$(mktemp -d /tmp/cargo-target-ldk-node-python-wheels.XXXXXX)" +export CARGO_TARGET_DIR +SOURCE_DATE_EPOCH="$(git show -s --format=%ct HEAD)" +export SOURCE_DATE_EPOCH + +cleanup() { + case "$CARGO_TARGET_DIR" in + /tmp/cargo-target-ldk-node-python-wheels.*) + rm -rf -- "$CARGO_TARGET_DIR" + ;; + esac +} +trap cleanup EXIT + +echo "Building Python wheel from commit $(git rev-parse HEAD)" +echo "Target: $(uname -s) $(uname -m)" + +uv tool run --from "cibuildwheel[uv]==$CIBUILDWHEEL_VERSION" cibuildwheel \ + bindings/python \ + --config-file bindings/python/pyproject.toml \ + --output-dir "$OUTPUT_DIR" + +built_wheels=("$OUTPUT_DIR"/*.whl) +if [[ ${#built_wheels[@]} -ne 1 ]]; then + echo "Expected exactly one wheel, found ${#built_wheels[@]}." >&2 + exit 1 +fi + +wheel_path="${built_wheels[0]}" +wheel_name="$(basename "$wheel_path")" +( + cd "$OUTPUT_DIR" + if command -v sha256sum >/dev/null 2>&1; then + sha256sum "$wheel_name" + else + shasum -a 256 "$wheel_name" + fi +) > "$wheel_path.sha256" + +echo "Wheel built and tested successfully:" +echo " $wheel_path" +echo " $wheel_path.sha256" diff --git a/scripts/python_publish_package.sh b/scripts/python_publish_package.sh index 971a4eddac..1b5c4e3541 100755 --- a/scripts/python_publish_package.sh +++ b/scripts/python_publish_package.sh @@ -1,26 +1,223 @@ #!/bin/bash -# Publish Python wheels to PyPI (or TestPyPI). +# Validate and publish a complete Python wheel release. # -# Usage: -# ./scripts/python_publish_package.sh # publish to PyPI -# ./scripts/python_publish_package.sh --index testpypi # publish to TestPyPI -# -# Before running, collect wheels from all target platforms into bindings/python/dist/. +# Build the four native wheels with python_build_wheel.sh and collect the +# wheels and their .sha256 files in one directory. Publish the unchanged set +# to TestPyPI first. Production publication verifies TestPyPI has files with +# the same hashes before uploading to PyPI. -set -e +set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)" -DIST_DIR="$REPO_ROOT/bindings/python/dist" +WHEEL_DIR="$REPO_ROOT/bindings/python/wheelhouse" +INDEX="" +VERSION="" +CHECK_ONLY=false + +usage() { + echo "Usage: $0 --index testpypi|pypi --version VERSION [--wheel-dir DIR] [--check-only]" +} + +while [[ $# -gt 0 ]]; do + case "$1" in + --index) + [[ $# -ge 2 ]] || { usage >&2; exit 2; } + INDEX="$2" + shift 2 + ;; + --version) + [[ $# -ge 2 ]] || { usage >&2; exit 2; } + VERSION="$2" + shift 2 + ;; + --wheel-dir) + [[ $# -ge 2 ]] || { usage >&2; exit 2; } + WHEEL_DIR="$2" + shift 2 + ;; + --check-only) + CHECK_ONLY=true + shift + ;; + -h|--help) + usage + exit 0 + ;; + *) + echo "Unknown argument: $1" >&2 + usage >&2 + exit 2 + ;; + esac +done + +if [[ "$INDEX" != testpypi && "$INDEX" != pypi ]]; then + echo "--index must be either testpypi or pypi." >&2 + exit 2 +fi + +if [[ ! "$VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+([a-zA-Z0-9.-]+)?$ ]]; then + echo "--version must be a Python package version such as 0.8.0." >&2 + exit 2 +fi + +case "$WHEEL_DIR" in + /*) ;; + *) WHEEL_DIR="$REPO_ROOT/$WHEEL_DIR" ;; +esac -if [ ! -d "$DIST_DIR" ] || [ -z "$(ls -A "$DIST_DIR"/*.whl 2>/dev/null)" ]; then - echo "Error: No wheels found in $DIST_DIR" - echo "Run ./scripts/python_build_wheel.sh on each target platform first." +if [[ ! -d "$WHEEL_DIR" ]]; then + echo "Wheel directory does not exist: $WHEEL_DIR" >&2 exit 1 fi -echo "Wheels to publish:" -ls -1 "$DIST_DIR"/*.whl -echo "" +if [[ "$CHECK_ONLY" == false && -z "${UV_PUBLISH_TOKEN:-}" ]]; then + echo "Set UV_PUBLISH_TOKEN to a token for $INDEX before publishing." >&2 + exit 1 +fi + +command -v uv >/dev/null 2>&1 || { echo "Required command not found: uv" >&2; exit 1; } + +uv run --no-project --python 3.10 python - "$VERSION" "$WHEEL_DIR" "$INDEX" <<'PY' +import email +import hashlib +import json +import re +import sys +import urllib.error +import urllib.request +import zipfile +from pathlib import Path + +version, wheel_dir_arg, index = sys.argv[1:] +wheel_dir = Path(wheel_dir_arg) + +platforms = ( + "manylinux_2_28_x86_64", + "manylinux_2_28_aarch64", + "macosx_10_12_x86_64", + "macosx_11_0_arm64", +) +expected = {f"ldk_node-{version}-py3-none-{platform}.whl" for platform in platforms} +actual = {path.name for path in wheel_dir.glob("*.whl")} + +if actual != expected: + missing = sorted(expected - actual) + unexpected = sorted(actual - expected) + if missing: + print(f"Missing wheels: {', '.join(missing)}", file=sys.stderr) + if unexpected: + print(f"Unexpected wheels: {', '.join(unexpected)}", file=sys.stderr) + raise SystemExit(1) + +source_archives = list(wheel_dir.glob("*.tar.gz")) + list(wheel_dir.glob("*.zip")) +if source_archives: + print("Source distributions must not be published for version 0.8.", file=sys.stderr) + raise SystemExit(1) + +digests = {} +for filename in sorted(expected): + wheel_path = wheel_dir / filename + digest = hashlib.sha256(wheel_path.read_bytes()).hexdigest() + checksum_path = wheel_dir / f"{filename}.sha256" + if not checksum_path.is_file(): + print(f"Missing checksum: {checksum_path.name}", file=sys.stderr) + raise SystemExit(1) + + checksum_fields = checksum_path.read_text(encoding="utf-8").split() + if len(checksum_fields) != 2 or checksum_fields != [digest, filename]: + print(f"Checksum mismatch: {checksum_path.name}", file=sys.stderr) + raise SystemExit(1) + + with zipfile.ZipFile(wheel_path) as wheel: + names = wheel.namelist() + metadata_paths = [name for name in names if name.endswith(".dist-info/METADATA")] + wheel_paths = [name for name in names if name.endswith(".dist-info/WHEEL")] + if len(metadata_paths) != 1 or len(wheel_paths) != 1: + print(f"Invalid wheel metadata layout: {filename}", file=sys.stderr) + raise SystemExit(1) + + metadata = email.message_from_bytes(wheel.read(metadata_paths[0])) + wheel_metadata = email.message_from_bytes(wheel.read(wheel_paths[0])) + normalized_name = re.sub(r"[-_.]+", "-", metadata["Name"]).lower() + expected_tag = filename.removeprefix(f"ldk_node-{version}-").removesuffix(".whl") + native_library = ( + "ldk_node/libldk_node.so" + if "manylinux" in filename + else "ldk_node/libldk_node.dylib" + ) + + if normalized_name != "ldk-node" or metadata["Version"] != version: + print(f"Name or version mismatch in {filename}", file=sys.stderr) + raise SystemExit(1) + if metadata["Requires-Python"] != ">=3.10": + print(f"Python requirement mismatch in {filename}", file=sys.stderr) + raise SystemExit(1) + if wheel_metadata["Root-Is-Purelib"].lower() != "false": + print(f"Native wheel marked as pure: {filename}", file=sys.stderr) + raise SystemExit(1) + if wheel_metadata.get_all("Tag", []) != [expected_tag]: + print(f"Wheel tag mismatch in {filename}", file=sys.stderr) + raise SystemExit(1) + if "ldk_node/ldk_node.py" not in names or native_library not in names: + print(f"Generated bindings missing from {filename}", file=sys.stderr) + raise SystemExit(1) + + digests[filename] = digest + +if index == "pypi": + testpypi_url = f"https://test.pypi.org/pypi/ldk-node/{version}/json" + try: + with urllib.request.urlopen(testpypi_url, timeout=30) as response: + testpypi = json.load(response) + except (OSError, urllib.error.URLError, json.JSONDecodeError) as error: + print(f"Unable to verify TestPyPI release: {error}", file=sys.stderr) + raise SystemExit(1) + + uploaded = { + item["filename"]: item.get("digests", {}).get("sha256") + for item in testpypi.get("urls", []) + } + mismatches = [ + filename + for filename, digest in digests.items() + if uploaded.get(filename) != digest + ] + if mismatches: + print( + "TestPyPI does not contain matching artifacts: " + ", ".join(mismatches), + file=sys.stderr, + ) + raise SystemExit(1) + +print("Validated release artifacts:") +for filename in sorted(expected): + print(f" {filename} sha256:{digests[filename]}") +PY + +if [[ "$CHECK_ONLY" == true ]]; then + echo "Artifact validation completed without publishing." + exit 0 +fi + +shopt -s nullglob +wheels=("$WHEEL_DIR"/*.whl) + +case "$INDEX" in + testpypi) + PUBLISH_URL="https://test.pypi.org/legacy/" + CHECK_URL="https://test.pypi.org/simple/" + ;; + pypi) + PUBLISH_URL="https://upload.pypi.org/legacy/" + CHECK_URL="https://pypi.org/simple/" + ;; +esac -uv publish "$@" "$DIST_DIR"/*.whl +echo "Publishing ${#wheels[@]} wheels to $INDEX." +uv publish \ + --publish-url "$PUBLISH_URL" \ + --check-url "$CHECK_URL" \ + --trusted-publishing never \ + "${wheels[@]}" diff --git a/scripts/uniffi_bindgen_generate_python.sh b/scripts/uniffi_bindgen_generate_python.sh index abc4088ffa..dc3e95ce3f 100755 --- a/scripts/uniffi_bindgen_generate_python.sh +++ b/scripts/uniffi_bindgen_generate_python.sh @@ -1,20 +1,33 @@ #!/bin/bash -BINDINGS_DIR="./bindings/python/src/ldk_node" -UNIFFI_BINDGEN_BIN="cargo run --manifest-path bindings/uniffi-bindgen/Cargo.toml" + +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)" +BINDINGS_DIR="$REPO_ROOT/bindings/python/src/ldk_node" +TARGET_DIR="${CARGO_TARGET_DIR:-$REPO_ROOT/target}" case " ${RUSTFLAGS:-} " in *" --cfg tokio_unstable "*|*" --cfg=tokio_unstable "*) ;; *) export RUSTFLAGS="${RUSTFLAGS:+$RUSTFLAGS }--cfg tokio_unstable" ;; esac -if [[ "$OSTYPE" == "linux-gnu"* ]]; then - DYNAMIC_LIB_PATH="./target/release-smaller/libldk_node.so" -else - DYNAMIC_LIB_PATH="./target/release-smaller/libldk_node.dylib" -fi +case "$(uname -s)" in + Linux) DYNAMIC_LIB_PATH="$TARGET_DIR/release-smaller/libldk_node.so" ;; + Darwin) DYNAMIC_LIB_PATH="$TARGET_DIR/release-smaller/libldk_node.dylib" ;; + *) + echo "Unsupported operating system: $(uname -s)" >&2 + exit 1 + ;; +esac -cargo build --profile release-smaller --features uniffi || exit 1 -$UNIFFI_BINDGEN_BIN generate bindings/ldk_node.udl --lib-file "$DYNAMIC_LIB_PATH" --language python -o "$BINDINGS_DIR" || exit 1 +cd "$REPO_ROOT" +mkdir -p "$BINDINGS_DIR" -mkdir -p $BINDINGS_DIR -cp "$DYNAMIC_LIB_PATH" "$BINDINGS_DIR" || exit 1 +cargo build --profile release-smaller --features uniffi +cargo run --manifest-path bindings/uniffi-bindgen/Cargo.toml -- \ + generate bindings/ldk_node.udl \ + --lib-file "$DYNAMIC_LIB_PATH" \ + --language python \ + -o "$BINDINGS_DIR" +cp "$DYNAMIC_LIB_PATH" "$BINDINGS_DIR"