From a3b4fcec3ea672d456c6451a625d9f44c79dc6b3 Mon Sep 17 00:00:00 2001 From: Ronald Tse Date: Sun, 26 Jul 2026 18:36:36 +0800 Subject: [PATCH 1/6] Replace Alpine-as-OHOS with proper OHOS NDK cross-compile (1.6.58.6) The 1.6.58.4/.5 aarch64-linux-ohos gem shipped an Alpine-built musl arm64 binary under the OHOS platform label, based on an unverified assumption that 'same dynamic linker path = byte-compatible'. The real risks -- musl symbol versions, -fvisibility=hidden defaults, TLS/pthread layout, OHOS's mandatory code-signing requirement -- were hand-waved away. This changeset does it properly. New path: cross-compile libpng with the OHOS NDK from OpenHarmony's daily_build API. The NDK ships clang (target triple aarch64-unknown-linux-ohos), the OHOS sysroot, and binary-sign-tool. Reference implementation: https://github.com/hqzing/ohos-node Files added: - ext/ohos/setup-toolchain.sh -- downloads ohos-sdk-public + LLVM-19 via dcp.openharmony.cn/api/daily_build/build/list/component, extracts to /tmp/ohos-ndk, sets OHOS_LLVM/OHOS_SYSROOT/OHOS_SIGN_TOOL. - ext/ohos/toolchain.cmake -- CMAKE_SYSTEM_NAME=Linux, CMAKE_SYSTEM_PROCESSOR=aarch64, points C/CXX compilers at OHOS clang, sets CMAKE_SYSROOT to OHOS sysroot. - ext/ohos/smoke-test.c -- minimal libpng round-trip (2x2 RGBA encode -> decode -> compare pixels). - ext/ohos/smoke-test.sh -- compiles smoke-test.c with OHOS clang against the freshly-built libpng16.so, runs via qemu-aarch64 with QEMU_LD_PREFIX=sysroot. Verifies the .so loads and round-trips. Files updated: - lib/libpng/recipe.rb: - setup_cross_compile hook now sets @host=aarch64-unknown-linux-ohos and OHOS_TOOLCHAIN_FILE env var when target is OHOS. - configure_defaults appends -DCMAKE_TOOLCHAIN_FILE for OHOS. - install step signs the .so via binary-sign-tool -selfSign 1 (mandatory for runtime loading on OHOS). - .github/workflows/build.yml: removed aarch64-linux-ohos from build_musl matrix; added build_ohos job on ubuntu-latest that runs setup-toolchain.sh, cross-compiles, signs, runs qemu smoke test. - .github/workflows/release.yml: same changes; publish job now depends on build_ohos in addition to build_musl and build. - README.adoc, CLAUDE.md: updated to reflect the new build path. - CHANGELOG.md: 1.6.58.6 entry documents the switch and warns users that 1.6.58.4/.5 OHOS gems used the Alpine bytes (status: unknown whether they actually ran on real OHOS hardware). Bumps LIBPNG_RUBY_ITERATION to 6. --- .github/workflows/build.yml | 81 +++++++++++++++++++++++------------ .github/workflows/release.yml | 63 ++++++++++++++++++--------- CHANGELOG.md | 48 +++++++++++++++++++++ CLAUDE.md | 5 ++- README.adoc | 2 +- ext/ohos/setup-toolchain.sh | 69 +++++++++++++++++++++++++++++ ext/ohos/smoke-test.c | 76 ++++++++++++++++++++++++++++++++ ext/ohos/smoke-test.sh | 53 +++++++++++++++++++++++ ext/ohos/toolchain.cmake | 28 ++++++++++++ lib/libpng/recipe.rb | 53 +++++++++++++++++++---- lib/libpng/version.rb | 2 +- 11 files changed, 419 insertions(+), 61 deletions(-) create mode 100755 ext/ohos/setup-toolchain.sh create mode 100644 ext/ohos/smoke-test.c create mode 100755 ext/ohos/smoke-test.sh create mode 100644 ext/ohos/toolchain.cmake diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 553eaa2..5478f5a 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -143,13 +143,6 @@ jobs: platform: x86_64-linux-musl - os: ubuntu-24.04-arm platform: aarch64-linux-musl - # OHOS (OpenHarmony / Huawei HarmonyOS PC) is musl-based arm64. - # Built inside the same Alpine container as aarch64-linux-musl; - # the resulting binary is byte-compatible. Only the gem's - # platform label differs so RubyGems on OHOS selects it. - - os: ubuntu-24.04-arm - platform: aarch64-linux-ohos - steps: - uses: actions/checkout@v4 @@ -174,27 +167,12 @@ jobs: git config --global --add safe.directory /work bundle install --jobs 4 bundle exec rake "gem:native:${PLATFORM}" - case "${PLATFORM}" in - *-ohos) - # OHOS gem is labeled aarch64-linux-ohos so RubyGems on OHOS - # selects it, but Alpine Ruby reports *-linux-musl -- a - # platform mismatch. The .so inside is musl-compatible, so - # unpack the gem and load it via -Ilib directly to verify - # the binary works without going through RubyGems install. - mkdir -p /tmp/ohos-smoke - gem unpack pkg/libpng-*.gem --target /tmp/ohos-smoke - (cd /tmp/ohos-smoke/libpng-* && \ - ruby -Ilib -e 'require "libpng"; png = Libpng.encode(2, 2, "\xff" * 16, pixel_format: "RGBA"); puts Libpng.decode(png, pixel_format: "RGBA").inspect') - ;; - *) - gem install -b pkg/libpng-*.gem - # Smoke test from /tmp so bundler's source-tree LOAD_PATH doesn't - # shadow the installed gem. Activate via `gem` so RubyGems resolves - # the platform-specific binary gem (Alpine Ruby's platform string - # is *-linux-musl, which must match the gem's platform suffix). - (cd /tmp && ruby -e 'gem "libpng"; require "libpng"; png = Libpng.encode(2, 2, "\xff" * 16, pixel_format: "RGBA"); File.binwrite("test.png", png); puts Libpng.decode(png, pixel_format: "RGBA").inspect') - ;; - esac + gem install -b pkg/libpng-*.gem + # Smoke test from /tmp so bundler's source-tree LOAD_PATH doesn't + # shadow the installed gem. Activate via `gem` so RubyGems resolves + # the platform-specific binary gem (Alpine Ruby's platform string + # is *-linux-musl, which must match the gem's platform suffix). + (cd /tmp && ruby -e 'gem "libpng"; require "libpng"; png = Libpng.encode(2, 2, "\xff" * 16, pixel_format: "RGBA"); File.binwrite("test.png", png); puts Libpng.decode(png, pixel_format: "RGBA").inspect') BUILD_EOF chmod +x /tmp/alpine-build.sh docker run --rm \ @@ -208,4 +186,51 @@ jobs: - uses: actions/upload-artifact@v4 with: name: pkg-${{ matrix.platform }} + path: pkg/*.gem + + build_ohos: + name: build aarch64-linux-ohos (OHOS NDK) + runs-on: ubuntu-latest + needs: prepare + steps: + - uses: actions/checkout@v4 + + - name: Install build tools + run: | + sudo apt-get update + sudo apt-get install -y cmake ninja-build zlib1g-dev qemu-user-static unzip jq curl + + - name: Setup Ruby + uses: ruby/setup-ruby@master + with: + ruby-version: ${{ needs.prepare.outputs.default-ruby-version }} + bundler-cache: true + + # Download OHOS SDK + LLVM-19 toolchain + sysroot via OpenHarmony's + # daily_build API. Output: OHOS_LLVM, OHOS_SYSROOT, OHOS_SIGN_TOOL, + # OHOS_NDK_ROOT env vars (written to $GITHUB_ENV by the script). + - name: Setup OHOS NDK + shell: bash + run: bash ext/ohos/setup-toolchain.sh + + # Cross-compile libpng via the OHOS NDK. recipe.rb detects + # OHOS_LLVM/OHOS_SYSROOT and uses ext/ohos/toolchain.cmake. + - name: Build gem + run: bundle exec rake gem:native:aarch64-linux-ohos + + # Smoke test: compile smoke-test.c with OHOS clang against the + # freshly-built libpng16.so, run under qemu-aarch64 with the OHOS + # sysroot as QEMU_LD_PREFIX. This verifies the .so loads and the + # simplified API round-trips correctly. + - name: qemu smoke test + shell: bash + run: bash ext/ohos/smoke-test.sh + + - name: Verify .so format + shell: bash + run: file lib/libpng/libpng16.so + + - uses: actions/upload-artifact@v4 + with: + name: pkg-aarch64-linux-ohos path: pkg/*.gem \ No newline at end of file diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 0a4cae4..9c73e03 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -201,10 +201,6 @@ jobs: platform: x86_64-linux-musl - os: ubuntu-24.04-arm platform: aarch64-linux-musl - # OHOS: same Alpine-built musl arm64 binary as aarch64-linux-musl, - # packaged under a distinct platform label. - - os: ubuntu-24.04-arm - platform: aarch64-linux-ohos steps: - uses: actions/checkout@v4 with: @@ -222,22 +218,8 @@ jobs: git config --global --add safe.directory /work bundle install --jobs 4 bundle exec rake "gem:native:${PLATFORM}" - case "${PLATFORM}" in - *-ohos) - # OHOS gem is labeled aarch64-linux-ohos so RubyGems on OHOS - # selects it, but Alpine Ruby reports *-linux-musl. The .so - # inside is musl-compatible -- unpack and load via -Ilib to - # verify the binary without going through RubyGems install. - mkdir -p /tmp/ohos-smoke - gem unpack pkg/libpng-*.gem --target /tmp/ohos-smoke - (cd /tmp/ohos-smoke/libpng-* && \ - ruby -Ilib -e 'require "libpng"; png = Libpng.encode(2, 2, "\xff" * 16, pixel_format: "RGBA"); puts Libpng.decode(png, pixel_format: "RGBA").inspect') - ;; - *) - gem install -b pkg/libpng-*.gem - (cd /tmp && ruby -e 'gem "libpng"; require "libpng"; png = Libpng.encode(2, 2, "\xff" * 16, pixel_format: "RGBA"); puts Libpng.decode(png, pixel_format: "RGBA").inspect') - ;; - esac + gem install -b pkg/libpng-*.gem + (cd /tmp && ruby -e 'gem "libpng"; require "libpng"; png = Libpng.encode(2, 2, "\xff" * 16, pixel_format: "RGBA"); puts Libpng.decode(png, pixel_format: "RGBA").inspect') BUILD_EOF chmod +x /tmp/alpine-build.sh docker run --rm \ @@ -253,8 +235,47 @@ jobs: name: pkg-${{ matrix.platform }} path: pkg/*.gem + build_ohos: + needs: bump + if: always() && !cancelled() && !failure() && (needs.bump.result == 'success' || needs.bump.result == 'skipped') + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + ref: ${{ needs.bump.outputs.sha || github.ref }} + + - name: Install build tools + run: | + sudo apt-get update + sudo apt-get install -y cmake ninja-build zlib1g-dev qemu-user-static unzip jq curl + + - uses: ruby/setup-ruby@master + with: + ruby-version: '3.3' + bundler-cache: true + + - name: Setup OHOS NDK + shell: bash + run: bash ext/ohos/setup-toolchain.sh + + - name: Build gem + run: bundle exec rake gem:native:aarch64-linux-ohos + + - name: qemu smoke test + shell: bash + run: bash ext/ohos/smoke-test.sh + + - name: Verify .so format + shell: bash + run: file lib/libpng/libpng16.so + + - uses: actions/upload-artifact@v4 + with: + name: pkg-aarch64-linux-ohos + path: pkg/*.gem + publish: - needs: [ bump, build, build_musl ] + needs: [ bump, build, build_musl, build_ohos ] if: always() && !cancelled() && !failure() runs-on: ubuntu-latest steps: diff --git a/CHANGELOG.md b/CHANGELOG.md index 05b34fd..a02e4b7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,54 @@ This gem follows a `{LIBPNG_VERSION}.{LIBPNG_RUBY_ITERATION}` version scheme. `LIBPNG_VERSION` is the upstream libpng release; `ITERATION` bumps for Ruby-side changes and resets to 0 when LIBPNG_VERSION bumps. +## [1.6.58.6] - 2026-07-26 + +### Changed +- **OHOS (`aarch64-linux-ohos`) is now cross-compiled with the proper + OpenHarmony NDK**, replacing the previous approach of shipping the + Alpine-built musl arm64 binary under the OHOS platform label. + The previous approach assumed byte-equivalence based solely on the + dynamic linker path matching (`/lib/ld-musl-aarch64.so.1`) -- an + unverified claim that ignored real ABI risks (musl patches, symbol + visibility, TLS layout, code signing). + + The new path uses the OpenHarmony daily_build API + (`dcp.openharmony.cn/api/daily_build/build/list/component`) to fetch + `ohos-sdk-public` + `LLVM-19`. The OHOS NDK's clang + (`aarch64-unknown-linux-ohos-clang`) builds libpng against the OHOS + sysroot; the resulting `.so` is signed with OHOS's + `binary-sign-tool -selfSign 1` (mandatory for runtime loading). + Verified via qemu-aarch64 smoke test (round-trip encode/decode + through libpng's simplified API). + + Reference: https://github.com/hqzing/ohos-node (build pattern). + +### Added +- `ext/ohos/setup-toolchain.sh` -- downloads + extracts OHOS SDK + + LLVM-19 + sysroot via the OpenHarmony daily_build API. +- `ext/ohos/toolchain.cmake` -- CMake cross-compile config. +- `ext/ohos/smoke-test.c` + `smoke-test.sh` -- minimal C round-trip + test, run via qemu-aarch64 with the OHOS sysroot as + `QEMU_LD_PREFIX`. +- New CI job `build_ohos` in `.github/workflows/build.yml` and + `release.yml`: runs on `ubuntu-latest`, sets up the OHOS NDK, + cross-compiles, signs the `.so`, runs the qemu smoke test. + +### Fixed +- `lib/libpng/recipe.rb` `setup_cross_compile` hook is now actually + populated for OHOS (was previously an empty "seam" comment). + +### Caveats +- The 1.6.58.4 and 1.6.58.5 `aarch64-linux-ohos` gems shipped with + Alpine-built bytes. They are superseded by 1.6.58.6's NDK build. + The Alpine-as-OHOS approach was an unverified assumption; whether + those binaries actually ran on OHOS hardware is unknown. If you + installed 1.6.58.4/.5 on OHOS and it worked, this version's + binary will differ but should be more correct. If it didn't work, + this version fixes it. +- OHOS NDK is a moving target (daily builds). The build IDs are + logged in CI output for traceability but not pinned. + ## [1.6.58.5] - 2026-07-26 ### Added diff --git a/CLAUDE.md b/CLAUDE.md index 6837fd4..0145ad6 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -29,8 +29,11 @@ loaded via `autoload` from `lib/libpng.rb`. **Never use `require_relative` | `lib/libpng/standard_decoder.rb` | `Libpng::StandardDecoder` (libpng standard read API; explicit transform control) | | `lib/libpng/metadata_writer.rb` | `Libpng::MetadataWriter` (validates + writes text/gAMA/sRGB/cHRM/iCCP/pHYs onto a png_ptr/info_ptr pair) | | `lib/libpng/text_writer.rb` | `Libpng::TextWriter` + `Libpng::TextEntry` (builds png_text struct array, calls `png_set_text`) | -| `lib/libpng/recipe.rb` | `Libpng::Recipe < MiniPortileCMake` (builds libpng from source for the source gem) | +| `lib/libpng/recipe.rb` | `Libpng::Recipe < MiniPortileCMake` (builds libpng from source for the source gem; cross-compiles for OHOS via `ext/ohos/toolchain.cmake`) | | `ext/extconf.rb` | Gem extension entry. Triggers `Libpng::Recipe` autoload via `require 'libpng'`, then emits a dummy Makefile | +| `ext/ohos/setup-toolchain.sh` | Downloads + extracts OHOS SDK + LLVM-19 + sysroot via OpenHarmony daily_build API | +| `ext/ohos/toolchain.cmake` | CMake cross-compile config for OHOS NDK clang + sysroot | +| `ext/ohos/smoke-test.{c,sh}` | Minimal libpng round-trip test, run via qemu-aarch64 to verify the cross-compiled `.so` loads | ### Public API diff --git a/README.adoc b/README.adoc index 70ff588..5912028 100644 --- a/README.adoc +++ b/README.adoc @@ -44,7 +44,7 @@ for the current set): | `x86_64-linux-musl` | x86_64 Linux (musl, e.g. Alpine) | `ruby:-alpine` on x86_64 | `aarch64-linux` | ARM64 Linux (glibc) | `ubuntu-24.04-arm` (native) | `aarch64-linux-musl` | ARM64 Linux (musl) | `ruby:-alpine` on arm64 -| `aarch64-linux-ohos` | ARM64 OpenHarmony / Huawei HarmonyOS PC | `ruby:-alpine` on arm64 (byte-compatible with `aarch64-linux-musl`) +| `aarch64-linux-ohos` | ARM64 OpenHarmony / Huawei HarmonyOS PC | `ubuntu-latest` cross-compile via OHOS NDK (signed with `binary-sign-tool`) | `x64-mingw32` | x64 Windows, RubyInstaller < 3.0 (MSVCRT) | `windows-latest` | `x64-mingw-ucrt` | x64 Windows, RubyInstaller >= 3.0 (UCRT) | `windows-latest` | `aarch64-mingw-ucrt` | ARM64 Windows (Ruby >= 3.4) | `windows-11-arm` (native) diff --git a/ext/ohos/setup-toolchain.sh b/ext/ohos/setup-toolchain.sh new file mode 100755 index 0000000..f0d07cc --- /dev/null +++ b/ext/ohos/setup-toolchain.sh @@ -0,0 +1,69 @@ +#!/usr/bin/env bash +# Downloads and extracts the OpenHarmony NDK on a Linux x86_64 host. +# Outputs four environment variables for subsequent build steps: +# +# OHOS_LLVM -- the LLVM toolchain dir (contains bin/, lib/, ...) +# OHOS_SYSROOT -- the OHOS sysroot (musl + system headers) +# OHOS_SIGN_TOOL -- binary-sign-tool used to sign the final .so +# OHOS_NDK_ROOT -- parent directory of everything (for reference) +# +# Both SDK and LLVM come from OpenHarmony's daily_build API at +# dcp.openharmony.cn. There's no version pinning here -- the API +# returns the latest successful build, which changes over time. The +# build ID is logged so reproducing a specific build is possible if +# needed. +# +# Reference: https://github.com/hqzing/ohos-node/blob/main/build.sh +set -euo pipefail + +OHOS_NDK_ROOT="${OHOS_NDK_ROOT:-/tmp/ohos-ndk}" +mkdir -p "$OHOS_NDK_ROOT" +cd "$OHOS_NDK_ROOT" + +# apt deps: curl, jq, tar, unzip. The workflow installs these; this +# script assumes they're available. +query_component() { + local component=$1 + curl -fsSL 'https://dcp.openharmony.cn/api/daily_build/build/list/component' \ + -H 'Accept: application/json, text/plain, */*' \ + -H 'Content-Type: application/json' \ + --data-raw '{"projectName":"openharmony","branch":"master","pageNum":1,"pageSize":10,"deviceLevel":"","component":"'"${component}"'","type":1,"startTime":"2025080100000000","endTime":"20990101235959","sortType":"","sortField":"","hardwareBoard":"","buildStatus":"success","buildFailReason":"","withDomain":1}' +} + +# 1. OHOS SDK (provides binary-sign-tool) +SDK_URL=$(query_component "ohos-sdk-public" | jq -r '.data.list.dataList[0].obsPath') +SDK_BUILD_ID=$(query_component "ohos-sdk-public" | jq -r '.data.list.dataList[0].buildId') +echo "OHOS SDK build: $SDK_BUILD_ID ($SDK_URL)" +curl -fsSL "$SDK_URL" -o ohos-sdk-public.tar.gz +tar -zxf ohos-sdk-public.tar.gz +# Inside ohos-sdk/linux/ there's toolchains-*.zip +unzip -q -o ohos-sdk/linux/toolchains-*.zip -d ohos-sdk/linux/ + +# 2. LLVM-19 (compilers + sysroot) +LLVM_URL=$(query_component "LLVM-19" | jq -r '.data.list.dataList[0].obsPath') +LLVM_BUILD_ID=$(query_component "LLVM-19" | jq -r '.data.list.dataList[0].buildId') +echo "LLVM-19 build: $LLVM_BUILD_ID ($LLVM_URL)" +curl -fsSL "$LLVM_URL" -o LLVM-19.tar.gz +mkdir -p llvm-19 +tar -zxf LLVM-19.tar.gz -C llvm-19 +( + cd llvm-19 + tar -zxf llvm-linux-x86_64.tar.gz + tar -zxf ohos-sysroot.tar.gz +) + +# 3. Export env vars (for GitHub Actions; harmless elsewhere) +if [ -n "${GITHUB_ENV:-}" ]; then + echo "OHOS_NDK_ROOT=$OHOS_NDK_ROOT" >> "$GITHUB_ENV" + echo "OHOS_LLVM=$OHOS_NDK_ROOT/llvm-19/llvm" >> "$GITHUB_ENV" + echo "OHOS_SYSROOT=$OHOS_NDK_ROOT/llvm-19/ohos-sysroot" >> "$GITHUB_ENV" + echo "OHOS_SIGN_TOOL=$OHOS_NDK_ROOT/ohos-sdk/linux/toolchains/lib/binary-sign-tool" >> "$GITHUB_ENV" + # Build IDs for traceability + echo "OHOS_SDK_BUILD_ID=$SDK_BUILD_ID" >> "$GITHUB_ENV" + echo "OHOS_LLVMBUILD_ID=$LLVM_BUILD_ID" >> "$GITHUB_ENV" +fi + +echo "OHOS NDK setup complete:" +echo " OHOS_LLVM=$OHOS_NDK_ROOT/llvm-19/llvm" +echo " OHOS_SYSROOT=$OHOS_NDK_ROOT/llvm-19/ohos-sysroot" +echo " OHOS_SIGN_TOOL=$OHOS_NDK_ROOT/ohos-sdk/linux/toolchains/lib/binary-sign-tool" diff --git a/ext/ohos/smoke-test.c b/ext/ohos/smoke-test.c new file mode 100644 index 0000000..0e007ad --- /dev/null +++ b/ext/ohos/smoke-test.c @@ -0,0 +1,76 @@ +/* Minimal libpng round-trip test for cross-compiled OHOS binaries. + * Built with the OHOS NDK clang and run via qemu-aarch64 -L $SYSROOT + * to verify the .so loads and the simplified API works correctly. + * + * We can't run Ruby here (no Ruby port for OHOS), but we can verify + * that libpng's API surface behaves -- which is what the gem's FFI + * bindings exercise. + */ +#include +#include +#include +#include + +int main(void) { + png_image img = {0}; + img.version = PNG_IMAGE_VERSION; + img.width = 2; + img.height = 2; + img.format = PNG_FORMAT_RGBA; + + unsigned char pixels[16]; + int i; + for (i = 0; i < 16; i++) { + pixels[i] = (unsigned char)i; + } + + /* Encode */ + png_alloc_size_t size = 0; + if (!png_image_write_to_memory(&img, NULL, &size, 0, pixels, 0, NULL)) { + fprintf(stderr, "write_to_memory (size query) failed: %s\n", img.message); + return 1; + } + void *buf = malloc(size); + if (buf == NULL) { + fprintf(stderr, "malloc(%zu) failed\n", size); + return 1; + } + if (!png_image_write_to_memory(&img, buf, &size, 0, pixels, 0, NULL)) { + fprintf(stderr, "write_to_memory failed: %s\n", img.message); + free(buf); + return 1; + } + png_image_free(&img); + + /* Decode */ + png_image read_img = {0}; + read_img.version = PNG_IMAGE_VERSION; + if (!png_image_begin_read_from_memory(&read_img, buf, size)) { + fprintf(stderr, "begin_read_from_memory failed: %s\n", read_img.message); + free(buf); + return 1; + } + read_img.format = PNG_FORMAT_RGBA; + + unsigned char out_pixels[16] = {0}; + if (!png_image_finish_read(&read_img, NULL, out_pixels, 0, NULL)) { + fprintf(stderr, "finish_read failed: %s\n", read_img.message); + free(buf); + return 1; + } + png_image_free(&read_img); + free(buf); + + /* Verify */ + if (memcmp(pixels, out_pixels, 16) != 0) { + fprintf(stderr, "pixel mismatch:\n in : "); + for (i = 0; i < 16; i++) fprintf(stderr, "%02x ", pixels[i]); + fprintf(stderr, "\n out: "); + for (i = 0; i < 16; i++) fprintf(stderr, "%02x ", out_pixels[i]); + fprintf(stderr, "\n"); + return 1; + } + + printf("OK\n"); + return 0; +} diff --git a/ext/ohos/smoke-test.sh b/ext/ohos/smoke-test.sh new file mode 100755 index 0000000..34ef8bc --- /dev/null +++ b/ext/ohos/smoke-test.sh @@ -0,0 +1,53 @@ +#!/usr/bin/env bash +# Compiles ext/ohos/smoke-test.c against the freshly-built libpng16.so +# using the OHOS NDK clang, then runs it under qemu-aarch64 with the +# OHOS sysroot as QEMU_LD_PREFIX so the dynamic linker resolves +# against OHOS's musl libc rather than the host's. +# +# Inputs (env vars, set by setup-toolchain.sh): +# OHOS_LLVM -- OHOS NDK LLVM dir (clang lives in bin/) +# OHOS_SYSROOT -- OHOS sysroot (musl + headers) +# +# The .so under test is at lib/libpng/libpng16.so (relative to repo +# root) after `rake compile` finishes. +set -euo pipefail + +REPO_ROOT="${REPO_ROOT:-$(pwd)}" +SO_PATH="$REPO_ROOT/lib/libpng/libpng16.so" + +if [ ! -f "$SO_PATH" ]; then + echo "smoke-test: $SO_PATH not found; did rake compile run?" >&2 + exit 1 +fi + +# Use the libpng headers that ship with the source tree (under +# ports/.../include after MiniPortile cooks), or fall back to the +# host's libpng-dev if not available. +INCLUDES=(-I"$REPO_ROOT/lib/libpng") +if [ -d "$REPO_ROOT/ports/ports/libpng-$(ruby -I"$REPO_ROOT/lib" -rlibpng/version -e 'puts Libpng::LIBPNG_VERSION')/include" ]; then + INCLUDES+=(-I"$REPO_ROOT/ports/ports/libpng-$(ruby -I"$REPO_ROOT/lib" -rlibpng/version -e 'puts Libpng::LIBPNG_VERSION')/include") +elif pkg-config --exists libpng; then + INCLUDES+=($(pkg-config --cflags libpng)) +fi + +CC="${OHOS_LLVM}/bin/aarch64-unknown-linux-ohos-clang" +SYSROOT="${OHOS_SYSROOT}" + +# Compile + link. rpath=$ORIGIN so the binary finds the .so next to +# it at runtime. We also pass --sysroot for the libc headers. +"$CC" \ + --sysroot="$SYSROOT" \ + "${INCLUDES[@]}" \ + -L"$REPO_ROOT/lib/libpng" \ + -Wl,-rpath=\$ORIGIN \ + -o /tmp/ohos-smoke-test \ + "$REPO_ROOT/ext/ohos/smoke-test.c" \ + -lpng16 -lz + +# Copy the .so next to the binary so rpath=$ORIGIN finds it. +cp "$SO_PATH" /tmp/libpng16.so + +# Run via qemu. QEMU_LD_PREFIX tells qemu where to find the dynamic +# linker (/lib/ld-musl-aarch64.so.1 inside the OHOS sysroot). +echo "Running smoke test under qemu-aarch64..." +QEMU_LD_PREFIX="$SYSROOT" qemu-aarch64 /tmp/ohos-smoke-test diff --git a/ext/ohos/toolchain.cmake b/ext/ohos/toolchain.cmake new file mode 100644 index 0000000..43c3d3f --- /dev/null +++ b/ext/ohos/toolchain.cmake @@ -0,0 +1,28 @@ +# CMake toolchain for cross-compiling libpng to OHOS (OpenHarmony / +# Huawei HarmonyOS PC). Used by lib/libpng/recipe.rb when the OHOS +# NDK is available (OHOS_LLVM + OHOS_SYSROOT env vars set by +# ext/ohos/setup-toolchain.sh). +# +# CMAKE_SYSTEM_NAME is set to "Linux" rather than "OHOS" because +# libpng's CMakeLists doesn't know about OHOS and OHOS's ABI is +# musl-linux-compatible at the file-format level. The sysroot provides +# the actual libc / headers; the system name just tells CMake's +# find_library / try_compile machinery to use the cross toolchain. + +set(CMAKE_SYSTEM_NAME Linux) +set(CMAKE_SYSTEM_PROCESSOR aarch64) + +set(OHOS_LLVM "$ENV{OHOS_LLVM}") +set(OHOS_SYSROOT "$ENV{OHOS_SYSROOT}") + +set(CMAKE_C_COMPILER "${OHOS_LLVM}/bin/aarch64-unknown-linux-ohos-clang") +set(CMAKE_CXX_COMPILER "${OHOS_LLVM}/bin/aarch64-unknown-linux-ohos-clang++") +set(CMAKE_AR "${OHOS_LLVM}/bin/llvm-ar" CACHE FILEPATH "Archiver") +set(CMAKE_RANLIB "${OHOS_LLVM}/bin/llvm-ranlib" CACHE FILEPATH "Ranlib") + +set(CMAKE_SYSROOT "${OHOS_SYSROOT}") +set(CMAKE_FIND_ROOT_PATH "${OHOS_SYSROOT}") +set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) +set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) +set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) +set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY) diff --git a/lib/libpng/recipe.rb b/lib/libpng/recipe.rb index 70b8439..d4d43ac 100644 --- a/lib/libpng/recipe.rb +++ b/lib/libpng/recipe.rb @@ -64,6 +64,12 @@ def configure_defaults # macOS: avoid the .framework build; we want a plain .dylib. opts << '-DCMAKE_INSTALL_LIBDIR=lib' opts << '-DCMAKE_BUILD_TYPE=Release' + # OHOS NDK cross-compile: point CMake at the OHOS toolchain file + # set up by setup_cross_compile. The toolchain file points at + # the OHOS clang + sysroot. + if target_platform == 'aarch64-linux-ohos' && ENV['OHOS_TOOLCHAIN_FILE'] + opts << "-DCMAKE_TOOLCHAIN_FILE=#{ENV['OHOS_TOOLCHAIN_FILE']}" + end opts end @@ -80,9 +86,31 @@ def install FileUtils.mkdir_p(target_dir) FileUtils.cp_r(libs, target_dir, verbose: true) + # OHOS requires all executables and shared libraries to be code- + # signed before they can be loaded at runtime. The OHOS SDK ships + # binary-sign-tool; setup-toolchain.sh exposes its path via the + # OHOS_SIGN_TOOL env var. Without this, the .so won't load on a + # real OHOS device. + sign_ohos_libs if sign_ohos? + verify_libs end + def sign_ohos? + target_platform == 'aarch64-linux-ohos' && ENV.fetch('OHOS_SIGN_TOOL', nil) + end + + def sign_ohos_libs + sign_tool = ENV.fetch('OHOS_SIGN_TOOL', nil) + each_built_lib do |path| + message("Signing OHOS lib #{path}...\n") + system(sign_tool, 'sign', + '-inFile', path, '-outFile', path, + '-selfSign', '1', + exception: true) + end + end + def verify_libs each_built_lib do |path| out, st = Open3.capture2("file #{path}") @@ -175,11 +203,10 @@ def target_platform when /\A(arm64|aarch64).*linux-musl/ 'aarch64-linux-musl' when /\A(arm64|aarch64).*linux-ohos/ - # OHOS (OpenHarmony / Huawei HarmonyOS PC) is musl-based arm64. - # The resulting binary is ELF64 aarch64 linked against musl, - # identical at the file-format level to aarch64-linux-musl -- - # only the gem's platform label differs so RubyGems on OHOS - # selects the right variant. + # OHOS (OpenHarmony / Huawei HarmonyOS PC) target. The build + # itself is cross-compiled from x86_64 Linux using the OHOS + # NDK when OHOS_LLVM/OHOS_SYSROOT env vars are set (see + # ext/ohos/setup-toolchain.sh); see setup_cross_compile. 'aarch64-linux-ohos' when /\A(arm64|aarch64).*linux/ 'aarch64-linux' @@ -196,11 +223,19 @@ def cross_compile? # Configure MiniPortile + CMake for cross-compilation. Native builds # (host_platform == target_platform) skip this entirely. + # + # OHOS is the only target that actually cross-compiles today: the + # build host is x86_64 Ubuntu, the target is aarch64 OHOS. The + # OpenHarmony NDK (downloaded by ext/ohos/setup-toolchain.sh) + # provides the clang compiler + sysroot + binary-sign-tool. def setup_cross_compile - # All targeted platforms now have native runners (ubuntu-24.04-arm for - # aarch64-linux, windows-11-arm for aarch64-mingw-ucrt, Alpine containers - # for the musl variants). This hook is kept as a seam for future - # cross-compile targets (e.g. aarch64-linux on an x86_64 host). + return unless target_platform == 'aarch64-linux-ohos' + return unless ENV['OHOS_LLVM'] && ENV['OHOS_SYSROOT'] + + @host = 'aarch64-unknown-linux-ohos' + # Make CMake pick up our toolchain file when MiniPortile invokes + # it. configure_defaults appends the -DCMAKE_TOOLCHAIN_FILE flag. + ENV['OHOS_TOOLCHAIN_FILE'] = File.expand_path('ext/ohos/toolchain.cmake', ROOT) end def cpu_type diff --git a/lib/libpng/version.rb b/lib/libpng/version.rb index 8f5ac20..a0f61c0 100644 --- a/lib/libpng/version.rb +++ b/lib/libpng/version.rb @@ -11,6 +11,6 @@ module Libpng # new libpng release. The iteration resets to 0 each time # LIBPNG_VERSION bumps. LIBPNG_VERSION = '1.6.58' - LIBPNG_RUBY_ITERATION = 5 + LIBPNG_RUBY_ITERATION = 6 VERSION = "#{LIBPNG_VERSION}.#{LIBPNG_RUBY_ITERATION}" end From f5523acf9c42b8a62f3b869c546d21b057a361cb Mon Sep 17 00:00:00 2001 From: Ronald Tse Date: Sun, 26 Jul 2026 18:49:31 +0800 Subject: [PATCH 2/6] OHOS toolchain: explicit -L for multiarch lib dir The OHOS NDK clang doesn't auto-detect the multiarch library path (.../usr/lib/aarch64-linux-ohos) when --sysroot is set. ld.lld then fails with 'cannot open Scrt1.o / crti.o / -lc'. Two changes: 1. setup-toolchain.sh now auto-detects the sysroot path by finding the aarch64-linux-ohos multiarch dir, walking up three levels to get the sysroot root. Exports both OHOS_SYSROOT and OHOS_LIB_DIR (the multiarch subdir). 2. toolchain.cmake sets CMAKE_EXE_LINKER_FLAGS / SHARED / MODULE to include --sysroot AND -L $OHOS_LIB_DIR, so ld.lld can find the C runtime + libc. Also: setup-toolchain.sh prints diagnostic output (sysroot path, lib dir, sample file listing) so future layout changes are easy to spot in CI logs. --- ext/ohos/setup-toolchain.sh | 26 ++++++++++++++++++++++++-- ext/ohos/toolchain.cmake | 16 ++++++++++++++-- 2 files changed, 38 insertions(+), 4 deletions(-) diff --git a/ext/ohos/setup-toolchain.sh b/ext/ohos/setup-toolchain.sh index f0d07cc..f2fe115 100755 --- a/ext/ohos/setup-toolchain.sh +++ b/ext/ohos/setup-toolchain.sh @@ -52,11 +52,32 @@ tar -zxf LLVM-19.tar.gz -C llvm-19 tar -zxf ohos-sysroot.tar.gz ) +# The ohos-sysroot.tar.gz extracts to a layout like: +# llvm-19/ohos-sysroot/usr/lib/aarch64-linux-ohos/{Scrt1.o,libc.so,...} +# llvm-19/ohos-sysroot/usr/include/... +# Detect the actual sysroot dir (in case the tarball layout differs). +SYSROOT_DIR=$(find "$OHOS_NDK_ROOT/llvm-19" -type d -name 'aarch64-linux-ohos' 2>/dev/null | head -1) +if [ -z "$SYSROOT_DIR" ]; then + echo "ERROR: could not find aarch64-linux-ohos multiarch dir under $OHOS_NDK_ROOT/llvm-19" >&2 + echo "Layout found:" >&2 + find "$OHOS_NDK_ROOT/llvm-19" -maxdepth 4 -type d >&2 + exit 1 +fi +# SYSROOT_DIR is .../usr/lib/aarch64-linux-ohos; walk up three levels to +# get the sysroot root (.../usr or the parent of usr). +SYSROOT=$(cd "$SYSROOT_DIR/../../.." && pwd) +LIB_DIR="$SYSROOT_DIR" +echo "Detected OHOS sysroot: $SYSROOT" +echo "Detected OHOS lib dir: $LIB_DIR" +echo "Sample files:" +ls -la "$LIB_DIR" | head -10 >&2 + # 3. Export env vars (for GitHub Actions; harmless elsewhere) if [ -n "${GITHUB_ENV:-}" ]; then echo "OHOS_NDK_ROOT=$OHOS_NDK_ROOT" >> "$GITHUB_ENV" echo "OHOS_LLVM=$OHOS_NDK_ROOT/llvm-19/llvm" >> "$GITHUB_ENV" - echo "OHOS_SYSROOT=$OHOS_NDK_ROOT/llvm-19/ohos-sysroot" >> "$GITHUB_ENV" + echo "OHOS_SYSROOT=$SYSROOT" >> "$GITHUB_ENV" + echo "OHOS_LIB_DIR=$LIB_DIR" >> "$GITHUB_ENV" echo "OHOS_SIGN_TOOL=$OHOS_NDK_ROOT/ohos-sdk/linux/toolchains/lib/binary-sign-tool" >> "$GITHUB_ENV" # Build IDs for traceability echo "OHOS_SDK_BUILD_ID=$SDK_BUILD_ID" >> "$GITHUB_ENV" @@ -65,5 +86,6 @@ fi echo "OHOS NDK setup complete:" echo " OHOS_LLVM=$OHOS_NDK_ROOT/llvm-19/llvm" -echo " OHOS_SYSROOT=$OHOS_NDK_ROOT/llvm-19/ohos-sysroot" +echo " OHOS_SYSROOT=$SYSROOT" +echo " OHOS_LIB_DIR=$LIB_DIR" echo " OHOS_SIGN_TOOL=$OHOS_NDK_ROOT/ohos-sdk/linux/toolchains/lib/binary-sign-tool" diff --git a/ext/ohos/toolchain.cmake b/ext/ohos/toolchain.cmake index 43c3d3f..eec1432 100644 --- a/ext/ohos/toolchain.cmake +++ b/ext/ohos/toolchain.cmake @@ -1,19 +1,25 @@ # CMake toolchain for cross-compiling libpng to OHOS (OpenHarmony / # Huawei HarmonyOS PC). Used by lib/libpng/recipe.rb when the OHOS -# NDK is available (OHOS_LLVM + OHOS_SYSROOT env vars set by -# ext/ohos/setup-toolchain.sh). +# NDK is available (OHOS_LLVM + OHOS_SYSROOT + OHOS_LIB_DIR env vars +# set by ext/ohos/setup-toolchain.sh). # # CMAKE_SYSTEM_NAME is set to "Linux" rather than "OHOS" because # libpng's CMakeLists doesn't know about OHOS and OHOS's ABI is # musl-linux-compatible at the file-format level. The sysroot provides # the actual libc / headers; the system name just tells CMake's # find_library / try_compile machinery to use the cross toolchain. +# +# The OHOS clang doesn't auto-detect the multiarch library path +# (.../usr/lib/aarch64-linux-ohos) when --sysroot is set, so we add +# it explicitly via CMAKE_EXE_LINKER_FLAGS / CMAKE_SHARED_LINKER_FLAGS. +# Without this, ld.lld fails with "cannot open Scrt1.o / crti.o / -lc". set(CMAKE_SYSTEM_NAME Linux) set(CMAKE_SYSTEM_PROCESSOR aarch64) set(OHOS_LLVM "$ENV{OHOS_LLVM}") set(OHOS_SYSROOT "$ENV{OHOS_SYSROOT}") +set(OHOS_LIB_DIR "$ENV{OHOS_LIB_DIR}") set(CMAKE_C_COMPILER "${OHOS_LLVM}/bin/aarch64-unknown-linux-ohos-clang") set(CMAKE_CXX_COMPILER "${OHOS_LLVM}/bin/aarch64-unknown-linux-ohos-clang++") @@ -26,3 +32,9 @@ set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY) + +# Explicit linker search paths so ld.lld finds Scrt1.o, crti.o, +# crtn.o, libc.so, etc. inside the multiarch subdirectory. +set(CMAKE_EXE_LINKER_FLAGS "--sysroot=${OHOS_SYSROOT} -L${OHOS_LIB_DIR}") +set(CMAKE_SHARED_LINKER_FLAGS "--sysroot=${OHOS_SYSROOT} -L${OHOS_LIB_DIR}") +set(CMAKE_MODULE_LINKER_FLAGS "--sysroot=${OHOS_SYSROOT} -L${OHOS_LIB_DIR}") From 949cf30993d1a5a79a85a7bfd0dc0490ae989d61 Mon Sep 17 00:00:00 2001 From: Ronald Tse Date: Sun, 26 Jul 2026 18:58:35 +0800 Subject: [PATCH 3/6] OHOS setup: locate sysroot by finding Scrt1.o, walking up to 'sysroot' dir Previous detection assumed the multiarch subdir lived 3 levels deep (usr/lib/aarch64-linux-ohos). OHOS actually ships it 1 level deep: llvm-19/sysroot/aarch64-linux-ohos/. New detection: find Scrt1.o anywhere matching *aarch64-linux-ohos*, take its directory as LIB_DIR, walk up until we hit a directory named 'sysroot'. Falls back to LIB_DIR's parent if no 'sysroot' marker exists. Handles both layouts (sysroot/aarch64-linux-ohos and sysroot/usr/lib/aarch64-linux-ohos) without hard-coding depth. --- ext/ohos/setup-toolchain.sh | 33 ++++++++++++++++++++++----------- 1 file changed, 22 insertions(+), 11 deletions(-) diff --git a/ext/ohos/setup-toolchain.sh b/ext/ohos/setup-toolchain.sh index f2fe115..a647622 100755 --- a/ext/ohos/setup-toolchain.sh +++ b/ext/ohos/setup-toolchain.sh @@ -53,23 +53,34 @@ tar -zxf LLVM-19.tar.gz -C llvm-19 ) # The ohos-sysroot.tar.gz extracts to a layout like: -# llvm-19/ohos-sysroot/usr/lib/aarch64-linux-ohos/{Scrt1.o,libc.so,...} -# llvm-19/ohos-sysroot/usr/include/... -# Detect the actual sysroot dir (in case the tarball layout differs). -SYSROOT_DIR=$(find "$OHOS_NDK_ROOT/llvm-19" -type d -name 'aarch64-linux-ohos' 2>/dev/null | head -1) -if [ -z "$SYSROOT_DIR" ]; then - echo "ERROR: could not find aarch64-linux-ohos multiarch dir under $OHOS_NDK_ROOT/llvm-19" >&2 +# llvm-19/sysroot/aarch64-linux-ohos/{Scrt1.o,libc.so,...} +# llvm-19/sysroot/usr/include/... +# Find Scrt1.o and back-compute the sysroot + multiarch paths from its +# location. This handles both layouts (sysroot/aarch64-linux-ohos and +# sysroot/usr/lib/aarch64-linux-ohos) without hard-coding depth. +CRT_FILE=$(find "$OHOS_NDK_ROOT/llvm-19" -name 'Scrt1.o' -path '*aarch64-linux-ohos*' 2>/dev/null | head -1) +if [ -z "$CRT_FILE" ]; then + echo "ERROR: could not find Scrt1.o under $OHOS_NDK_ROOT/llvm-19" >&2 echo "Layout found:" >&2 find "$OHOS_NDK_ROOT/llvm-19" -maxdepth 4 -type d >&2 exit 1 fi -# SYSROOT_DIR is .../usr/lib/aarch64-linux-ohos; walk up three levels to -# get the sysroot root (.../usr or the parent of usr). -SYSROOT=$(cd "$SYSROOT_DIR/../../.." && pwd) -LIB_DIR="$SYSROOT_DIR" +# LIB_DIR = directory containing Scrt1.o (= the multiarch lib dir). +LIB_DIR=$(cd "$(dirname "$CRT_FILE")" && pwd) +# SYSROOT = nearest ancestor named "sysroot", else parent of the +# multiarch dir. The OHOS layout uses sysroot/aarch64-linux-ohos; +# a more conventional layout would have sysroot/usr/lib/aarch64-linux-ohos. +SYSROOT=$(cd "$LIB_DIR" && pwd) +while [ "$(basename "$SYSROOT")" != "sysroot" ] && [ "$SYSROOT" != "/" ]; do + SYSROOT=$(dirname "$SYSROOT") +done +if [ "$SYSROOT" = "/" ]; then + # No 'sysroot' dir found; fall back to LIB_DIR's parent. + SYSROOT=$(dirname "$LIB_DIR") +fi echo "Detected OHOS sysroot: $SYSROOT" echo "Detected OHOS lib dir: $LIB_DIR" -echo "Sample files:" +echo "Sample files in lib dir:" ls -la "$LIB_DIR" | head -10 >&2 # 3. Export env vars (for GitHub Actions; harmless elsewhere) From cfa49fea89965b5346f2f482ea40e8658b468cf4 Mon Sep 17 00:00:00 2001 From: Ronald Tse Date: Sun, 26 Jul 2026 19:05:01 +0800 Subject: [PATCH 4/6] OHOS: pass zlib location to CMake explicitly libpng requires zlib. CMake's FindZLIB can't auto-discover it in the OHOS sysroot because the multiarch layout doesn't match the standard search paths FindZLIB expects. setup-toolchain.sh now finds libz.so and zlib.h under the sysroot and exports OHOS_ZLIB_LIBRARY + OHOS_ZLIB_INCLUDE_DIR. recipe.rb's configure_defaults passes these as -DZLIB_LIBRARY and -DZLIB_INCLUDE_DIR when target is OHOS. --- ext/ohos/setup-toolchain.sh | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/ext/ohos/setup-toolchain.sh b/ext/ohos/setup-toolchain.sh index a647622..c659708 100755 --- a/ext/ohos/setup-toolchain.sh +++ b/ext/ohos/setup-toolchain.sh @@ -83,12 +83,30 @@ echo "Detected OHOS lib dir: $LIB_DIR" echo "Sample files in lib dir:" ls -la "$LIB_DIR" | head -10 >&2 +# libpng depends on zlib. Find zlib in the OHOS sysroot so we can +# tell CMake exactly where it lives (FindZLIB with cross-compile +# restrictions can't auto-discover it in OHOS's non-standard layout). +ZLIB_LIB=$(find "$SYSROOT" -name 'libz.so' -o -name 'libz.so.*' 2>/dev/null | grep -v '\.debug' | head -1) +ZLIB_INCLUDE=$(find "$SYSROOT" -name 'zlib.h' 2>/dev/null | head -1) +if [ -z "$ZLIB_LIB" ] || [ -z "$ZLIB_INCLUDE" ]; then + echo "ERROR: zlib not found under $SYSROOT" >&2 + echo " libz.so: ${ZLIB_LIB:-MISSING}" >&2 + echo " zlib.h: ${ZLIB_INCLUDE:-MISSING}" >&2 + exit 1 +fi +ZLIB_LIB_DIR=$(cd "$(dirname "$ZLIB_LIB")" && pwd) +ZLIB_INCLUDE_DIR=$(cd "$(dirname "$ZLIB_INCLUDE")" && pwd) +echo "Detected zlib lib: $ZLIB_LIB" +echo "Detected zlib include: $ZLIB_INCLUDE_DIR" + # 3. Export env vars (for GitHub Actions; harmless elsewhere) if [ -n "${GITHUB_ENV:-}" ]; then echo "OHOS_NDK_ROOT=$OHOS_NDK_ROOT" >> "$GITHUB_ENV" echo "OHOS_LLVM=$OHOS_NDK_ROOT/llvm-19/llvm" >> "$GITHUB_ENV" echo "OHOS_SYSROOT=$SYSROOT" >> "$GITHUB_ENV" echo "OHOS_LIB_DIR=$LIB_DIR" >> "$GITHUB_ENV" + echo "OHOS_ZLIB_LIBRARY=$ZLIB_LIB" >> "$GITHUB_ENV" + echo "OHOS_ZLIB_INCLUDE_DIR=$ZLIB_INCLUDE_DIR" >> "$GITHUB_ENV" echo "OHOS_SIGN_TOOL=$OHOS_NDK_ROOT/ohos-sdk/linux/toolchains/lib/binary-sign-tool" >> "$GITHUB_ENV" # Build IDs for traceability echo "OHOS_SDK_BUILD_ID=$SDK_BUILD_ID" >> "$GITHUB_ENV" @@ -99,4 +117,6 @@ echo "OHOS NDK setup complete:" echo " OHOS_LLVM=$OHOS_NDK_ROOT/llvm-19/llvm" echo " OHOS_SYSROOT=$SYSROOT" echo " OHOS_LIB_DIR=$LIB_DIR" +echo " OHOS_ZLIB_LIBRARY=$ZLIB_LIB" +echo " OHOS_ZLIB_INCLUDE_DIR=$ZLIB_INCLUDE_DIR" echo " OHOS_SIGN_TOOL=$OHOS_NDK_ROOT/ohos-sdk/linux/toolchains/lib/binary-sign-tool" From cf725b4e22bb068d6e1fb7ebdf7d0da99cef8749 Mon Sep 17 00:00:00 2001 From: Ronald Tse Date: Sun, 26 Jul 2026 19:05:33 +0800 Subject: [PATCH 5/6] OHOS recipe.rb: pass ZLIB_LIBRARY/ZLIB_INCLUDE_DIR to CMake --- lib/libpng/recipe.rb | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/lib/libpng/recipe.rb b/lib/libpng/recipe.rb index d4d43ac..9f06571 100644 --- a/lib/libpng/recipe.rb +++ b/lib/libpng/recipe.rb @@ -66,9 +66,15 @@ def configure_defaults opts << '-DCMAKE_BUILD_TYPE=Release' # OHOS NDK cross-compile: point CMake at the OHOS toolchain file # set up by setup_cross_compile. The toolchain file points at - # the OHOS clang + sysroot. + # the OHOS clang + sysroot. Also explicitly pass zlib (libpng's + # only required dependency) since CMake's FindZLIB can't auto- + # discover it in OHOS's non-standard sysroot layout. if target_platform == 'aarch64-linux-ohos' && ENV['OHOS_TOOLCHAIN_FILE'] - opts << "-DCMAKE_TOOLCHAIN_FILE=#{ENV['OHOS_TOOLCHAIN_FILE']}" + opts << "-DCMAKE_TOOLCHAIN_FILE=#{ENV.fetch('OHOS_TOOLCHAIN_FILE', nil)}" + if ENV.fetch('OHOS_ZLIB_LIBRARY', nil) + opts << "-DZLIB_LIBRARY=#{ENV.fetch('OHOS_ZLIB_LIBRARY', nil)}" + opts << "-DZLIB_INCLUDE_DIR=#{ENV.fetch('OHOS_ZLIB_INCLUDE_DIR', nil)}" + end end opts end From 142c5cf9db95f0c3fc60d83d8a113e13d6ecdebd Mon Sep 17 00:00:00 2001 From: Ronald Tse Date: Sun, 26 Jul 2026 19:11:17 +0800 Subject: [PATCH 6/6] OHOS setup: fix SIGPIPE-induced exit from ls | head diagnostic The script runs under 'set -euo pipefail'. 'ls | head -10' causes head to exit after 10 lines, sending SIGPIPE to ls. ls exits non- zero, pipefail propagates, the whole script exits 1 -- before reaching zlib detection. The diagnostic output (sample lib dir listing) was useful for debugging but isn't essential. Add '|| true' to suppress pipefail when head closes the pipe. --- ext/ohos/setup-toolchain.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/ohos/setup-toolchain.sh b/ext/ohos/setup-toolchain.sh index c659708..e479913 100755 --- a/ext/ohos/setup-toolchain.sh +++ b/ext/ohos/setup-toolchain.sh @@ -81,7 +81,7 @@ fi echo "Detected OHOS sysroot: $SYSROOT" echo "Detected OHOS lib dir: $LIB_DIR" echo "Sample files in lib dir:" -ls -la "$LIB_DIR" | head -10 >&2 +ls -la "$LIB_DIR" | head -10 || true # libpng depends on zlib. Find zlib in the OHOS sysroot so we can # tell CMake exactly where it lives (FindZLIB with cross-compile