diff --git a/.gitignore b/.gitignore index 6c57627..20e8c00 100644 --- a/.gitignore +++ b/.gitignore @@ -38,3 +38,4 @@ src/avr/emu/bbemu src/avr/emu/build/ .pi/pi-python35.tar.gz +src/py/camotics/gplan.so.built diff --git a/.pi/BUILD.md b/.pi/BUILD.md index c485ed8..cfeb1ed 100644 --- a/.pi/BUILD.md +++ b/.pi/BUILD.md @@ -54,23 +54,35 @@ ELF 32-bit LSB shared object, ARM, EABI5 (linked against libpython3.5m.so.1.0) ``` If you see `ELF 64-bit LSB shared object, ARM aarch64` or `libpython3.9` — wrong. -**Option A: From official release (recommended)** +**Option A: Build from source (recommended)** + +Uses a Raspbian Stretch Docker image that exactly matches the Pi's toolchain: +GCC 6.3, Python 3.5, GLIBC 2.24. No cross-compile hacks, no ABI mismatches. + +```bash +.pi/build-gplan.sh +``` + +- First run: ~25min (builds `onefin-gplan` Docker image with pre-compiled cbang + camotics) +- After that: ~1sec (copies cached `gplan.so` from image) +- Force rebuild: `docker rmi onefin-gplan && .pi/build-gplan.sh` + +See `.pi/Dockerfile.gplan` for the full build recipe. + +**Option B: From official release** ```bash curl -sL https://github.com/OneFinityCNC/onefinity-firmware/releases/download/v1.6.6/onefinity-1.6.6.tar.bz2 \ | tar xjf - --include='*/gplan.so' -O > src/py/camotics/gplan.so ``` -The gplan.so is pure G-code planning with no version-specific code — safe to -reuse across firmware versions. - -**Option B: From a working Pi** +**Option C: From a working Pi** ```bash scp bbmc@10.1.10.55:/usr/local/lib/python3.5/dist-packages/bbctrl-*.egg/camotics/gplan.so src/py/camotics/ ``` -**Option C: Build from source (broken — documented for reference)** +**Failed approach: Debian Bullseye armhf (documented for reference)** Uses Docker's armv7 QEMU emulation on Apple Silicon. Requires a one-time copy of Python 3.5 headers from the Pi (~1.7MB): @@ -142,10 +154,9 @@ Stretch has GLIBC_2.24 / GLIBCXX_3.4.22 max. Even with `-static-libstdc++ -static-libgcc`, glibc symbols like `GLIBC_2.29` leak through the object files compiled against Bullseye headers. -To truly build from source you'd need a Stretch armhf container — but -Stretch's archived repos have broken package metadata that prevents -installing build-essential + scons. The official gplan.so was built -in a Raspbian Stretch chroot (see `scripts/gplan-init-build.sh`). +The solution was to use `balenalib/raspberry-pi-debian:stretch` with +`legacy.raspbian.org` repos — these still work unlike bare `debian:stretch`. +See `.pi/Dockerfile.gplan`. **Key Pi constraints for native code:** - GLIBC ≤ 2.24 (Stretch) diff --git a/.pi/Dockerfile.gplan b/.pi/Dockerfile.gplan index 88c0e9c..97ee8f5 100644 --- a/.pi/Dockerfile.gplan +++ b/.pi/Dockerfile.gplan @@ -1,15 +1,24 @@ -# Pre-built armv7 environment for gplan.so -# Build once: docker build --platform linux/arm/v7 -t onefin-gplan -f .pi/Dockerfile.gplan .pi/ -# Then use: .pi/build-gplan.sh -FROM debian:bullseye +# Raspbian Stretch armhf build environment for gplan.so +# Matches the Pi exactly: GCC 6.3, Python 3.5, GLIBC 2.24 +# +# Build image: docker build -t onefin-gplan -f .pi/Dockerfile.gplan .pi/ +# Build gplan: .pi/build-gplan.sh +FROM balenalib/raspberry-pi-debian:stretch -RUN apt-get update -qq && \ - DEBIAN_FRONTEND=noninteractive apt-get install -y -qq --no-install-recommends \ - build-essential scons git ca-certificates python3-dev \ +# Fix repos to use archived Raspbian mirrors +RUN echo "deb http://legacy.raspbian.org/raspbian/ stretch main contrib non-free rpi" \ + > /etc/apt/sources.list && \ + rm -f /etc/apt/sources.list.d/*.list + +RUN apt-get -o Acquire::Check-Valid-Until=false \ + -o Acquire::AllowInsecureRepositories=true update && \ + apt-get -o Acquire::Check-Valid-Until=false --allow-unauthenticated \ + install -y --no-install-recommends \ + build-essential python3-dev scons git ca-certificates \ libssl-dev libexpat1-dev libbz2-dev liblz4-dev zlib1g-dev perl file && \ rm -rf /var/lib/apt/lists/* -# Clone and build cbang (static lib, ~5min under QEMU) +# Clone and build cbang RUN mkdir -p /opt/cbang && cd /opt/cbang && git init -q && \ git remote add origin https://github.com/CauldronDevelopmentLLC/cbang && \ git fetch --depth 1 -q origin 18f1e963107ef26abe750c023355a5c40dd07853 && \ @@ -17,7 +26,7 @@ RUN mkdir -p /opt/cbang && cd /opt/cbang && git init -q && \ scons -j2 disable_local="re2 libevent" && \ rm -rf .git build/dep -# Clone and patch camotics (source only, don't compile yet — that depends on workspace) +# Clone, patch, and build camotics/gplan RUN mkdir -p /opt/camotics && cd /opt/camotics && git init -q && \ git remote add origin https://github.com/CauldronDevelopmentLLC/camotics && \ git fetch --depth 1 -q origin ec876c80d20fc19837133087cef0c447df5a939d && \ @@ -31,10 +40,9 @@ RUN mkdir -p /opt/camotics && cd /opt/camotics && git init -q && \ done && \ rm -rf .git -# Pre-compile camotics objects (the slow part, ~20min under QEMU) ENV CBANG_HOME=/opt/cbang -RUN cd /opt/camotics && scons -j2 gplan.so with_gui=0 with_tpl=0 && \ - rm -f gplan.so # remove the python3.9-linked one, we relink at build time -ENV CBANG_HOME=/opt/cbang +# Pre-compile everything including gplan.so +RUN cd /opt/camotics && scons -j2 gplan.so with_gui=0 with_tpl=0 + WORKDIR /opt/camotics diff --git a/.pi/build-gplan.sh b/.pi/build-gplan.sh index 4789eb3..fa15a99 100755 --- a/.pi/build-gplan.sh +++ b/.pi/build-gplan.sh @@ -1,58 +1,30 @@ #!/usr/bin/env bash set -euo pipefail -# Build gplan.so for the Onefinity Pi (armv7l, Python 3.5) +# Build gplan.so for the Onefinity Pi (armv7l, Python 3.5, GCC 6.3) # -# First run: ~30min (builds the Docker image with pre-compiled cbang/camotics) -# After that: ~2sec (just relinks against Python 3.5m) +# Uses a Raspbian Stretch Docker image that exactly matches the Pi's +# toolchain. No cross-compile, no relink hacks, no GLIBC mismatches. # -# Prerequisites: -# - Docker with QEMU binfmt support (default on Docker Desktop) -# - Python 3.5 headers from the Pi in .pi/pi-python35.tar.gz -# Grab once: ssh bbmc@10.1.10.55 'tar czf - /usr/include/python3.5m \ -# /usr/lib/arm-linux-gnueabihf/libpython3.5m.so*' > .pi/pi-python35.tar.gz +# First run: ~30min (builds Docker image with cbang + camotics) +# After that: ~1sec (copies pre-built gplan.so from image) SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" PROJECT_DIR="$(dirname "$SCRIPT_DIR")" IMAGE="onefin-gplan" -HEADERS="$SCRIPT_DIR/pi-python35.tar.gz" OUTPUT="$PROJECT_DIR/src/py/camotics/gplan.so" -# Check for Python 3.5 headers -if [[ ! -f "$HEADERS" ]]; then - echo "Python 3.5 headers not found at $HEADERS" - echo "Fetching from Pi..." - ssh bbmc@10.1.10.55 'tar czf - /usr/include/python3.5m \ - /usr/lib/arm-linux-gnueabihf/libpython3.5m.so*' > "$HEADERS" -fi - -# Build image if needed (one-time, ~30min) +# Build image if needed (one-time) if ! docker image inspect "$IMAGE" >/dev/null 2>&1; then echo "Building $IMAGE Docker image (one-time, ~30min under QEMU)..." - docker build --platform linux/arm/v7 -t "$IMAGE" -f "$SCRIPT_DIR/Dockerfile.gplan" "$SCRIPT_DIR" + docker build -t "$IMAGE" -f "$SCRIPT_DIR/Dockerfile.gplan" "$SCRIPT_DIR" fi -# Relink gplan.so against Python 3.5m (~2sec) -echo "Linking gplan.so against Python 3.5m..." -docker run --rm --platform linux/arm/v7 \ - -v "$HEADERS:/tmp/pi-python35.tar.gz:ro" \ - -v "$PROJECT_DIR:/workspace" \ - "$IMAGE" bash -c ' - tar xzf /tmp/pi-python35.tar.gz -C / - ln -sf /usr/lib/arm-linux-gnueabihf/libpython3.5m.so.1.0 \ - /usr/lib/arm-linux-gnueabihf/libpython3.5m.so - - g++ -o /workspace/src/py/camotics/gplan.so \ - -Wl,--as-needed -Wl,-s -Wl,-x -Wl,--gc-sections -pthread -shared \ - build/gplan.os -L/opt/cbang/lib \ - build/libCAMoticsPy.a build/libCAMotics.a build/libDXF.a \ - build/libSTL.a build/libGCode.a \ - -lstdc++ -lutil -lm -ldl -lz -lcbang -lcbang-boost \ - -lssl -lcrypto -llz4 -lexpat -lbz2 -lcrypt -lpthread \ - -lpython3.5m build/dxflib/libdxflib.a - - file /workspace/src/py/camotics/gplan.so - readelf -d /workspace/src/py/camotics/gplan.so | grep python -' +# Copy gplan.so out of the image +echo "Extracting gplan.so..." +docker run --rm -v "$PROJECT_DIR:/workspace" "$IMAGE" \ + bash -c 'cp /opt/camotics/gplan.so /workspace/src/py/camotics/gplan.so && \ + file /workspace/src/py/camotics/gplan.so && \ + readelf -d /workspace/src/py/camotics/gplan.so | grep -E "NEEDED|python"' echo "✓ Built: $OUTPUT"