From f093898e5db54b98e6320e427ecaa8163bf7d7d3 Mon Sep 17 00:00:00 2001 From: Lovell Fuller Date: Fri, 13 Jun 2025 12:41:45 +0100 Subject: [PATCH] WIP: Remove prebuild dependency --- .github/workflows/ci.yml | 167 ++++++++++++++++++++++++--------------- .prebuildrc | 6 -- lib/libvips.js | 2 +- npm/from-local-build.js | 64 ++++++++++++--- package.json | 8 +- src/binding.gyp | 2 + 6 files changed, 158 insertions(+), 91 deletions(-) delete mode 100644 .prebuildrc diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d67993b2..a01732ef 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -4,15 +4,12 @@ on: - pull_request permissions: {} jobs: - github-runner: + build-native: permissions: - contents: write + contents: read name: ${{ matrix.platform }} - Node.js ${{ matrix.nodejs_version_major }} ${{ matrix.prebuild && '- prebuild' }} runs-on: ${{ matrix.os }} - container: - image: ${{ matrix.container }} - volumes: - - /:/host + container: ${{ matrix.container }} strategy: fail-fast: false matrix: @@ -62,15 +59,6 @@ jobs: nodejs_version: "^20.3.0" nodejs_version_major: 20 platform: linux-arm64 - - os: ubuntu-24.04-arm - container: node:18-alpine3.17 - nodejs_version_major: 18 - platform: linuxmusl-arm64 - prebuild: true - - os: ubuntu-24.04-arm - container: node:20-alpine3.18 - nodejs_version_major: 20 - platform: linuxmusl-arm64 - os: macos-13 nodejs_arch: x64 nodejs_version: "^18.17.0" @@ -147,16 +135,6 @@ jobs: nodejs_version_major: 22 platform: win32-arm64 steps: - - name: Allow Linux musl containers on ARM64 runners # https://github.com/actions/runner/issues/801#issuecomment-2394425757 - if: matrix.platform == 'linuxmusl-arm64' - shell: sh - run: | - apk add nodejs - sed -i "s:ID=alpine:ID=NotpineForGHA:" /etc/os-release - cd /host/home/runner/runners/*/externals/ - rm -rf node20/* - mkdir node20/bin - ln -s /usr/bin/node node20/bin/node - name: Dependencies (Rocky Linux glibc) if: contains(matrix.container, 'rockylinux') run: | @@ -182,24 +160,65 @@ jobs: run: npm install --build-from-source - name: Test run: npm test - - name: Test packaging - run: | - npm run package-from-local-build - npm pkg set "optionalDependencies.@img/sharp-${{ matrix.platform }}=file:./npm/${{ matrix.platform }}" - npm run clean - npm install --ignore-scripts - npm test - - name: Prebuild - if: matrix.prebuild && startsWith(github.ref, 'refs/tags/') - env: - prebuild_upload: ${{ secrets.GITHUB_TOKEN }} - run: | - node -e "require('fs').cpSync('package.json', 'src/package.json')" - cd src - npx prebuild - github-runner-qemu: + - name: Populate npm package + if: matrix.prebuild + run: npm run package-from-local-build + - uses: actions/upload-artifact@v4 + if: matrix.prebuild + with: + name: ${{ matrix.platform }} + path: npm/${{ matrix.platform }} + retention-days: 1 + if-no-files-found: error + build-linuxmusl-arm-64: permissions: - contents: write + contents: read + name: linuxmusl-arm64 - Node.js ${{ matrix.nodejs_version_major }} ${{ matrix.prebuild && '- prebuild' }} + runs-on: ubuntu-24.04-arm + container: + image: ${{ matrix.container }} + volumes: + - /:/host + strategy: + fail-fast: false + matrix: + include: + - container: node:18-alpine3.17 + nodejs_version_major: 18 + prebuild: true + - container: node:20-alpine3.18 + nodejs_version_major: 20 + steps: + - name: Allow Linux musl containers on ARM64 runners # https://github.com/actions/runner/issues/801#issuecomment-2394425757 + shell: sh + run: | + apk add nodejs + sed -i "s:ID=alpine:ID=NotpineForGHA:" /etc/os-release + cd /host/home/runner/runners/*/externals/ + rm -rf node20/* + mkdir node20/bin + ln -s /usr/bin/node node20/bin/node + - name: Dependencies + run: apk add build-base git python3 font-noto --update-cache + - name: Checkout + uses: actions/checkout@v4 + - name: Install + run: npm install --build-from-source + - name: Test + run: npm test + - name: Populate npm package + if: matrix.prebuild + run: npm run package-from-local-build + - uses: actions/upload-artifact@v4 + if: matrix.prebuild + with: + name: linuxmusl-arm64 + path: npm/linuxmusl-arm64 + retention-days: 1 + if-no-files-found: error + build-qemu: + permissions: + contents: read name: ${{ matrix.platform }} - Node.js ${{ matrix.nodejs_version_major }} - prebuild runs-on: ubuntu-24.04 strategy: @@ -233,8 +252,6 @@ jobs: with: arch: ${{ matrix.run_on_arch }} distro: ${{ matrix.distro }} - env: | - prebuild_upload: "${{ startsWith(github.ref, 'refs/tags/') && secrets.GITHUB_TOKEN || '' }}" run: | apt-get update apt-get install -y curl g++ git libatomic1 make python3 xz-utils @@ -244,14 +261,15 @@ jobs: npm install --build-from-source npx mocha --no-config --spec=test/unit/io.js --timeout=30000 npm run package-from-local-build - npm pkg set "optionalDependencies.@img/sharp-${{ matrix.platform }}=file:./npm/${{ matrix.platform }}" - npm run clean - npm install --ignore-scripts - npx mocha --no-config --spec=test/unit/io.js --timeout=30000 - [[ -n $prebuild_upload ]] && cd src && ln -s ../package.json && npx prebuild || true - github-runner-emscripten: + - uses: actions/upload-artifact@v4 + with: + name: ${{ matrix.platform }} + path: npm/${{ matrix.platform }} + retention-days: 1 + if-no-files-found: error + build-emscripten: permissions: - contents: write + contents: read name: wasm32 - prebuild runs-on: ubuntu-24.04 container: "emscripten/emsdk:4.0.10" @@ -275,17 +293,36 @@ jobs: test "$EMSCRIPTEN_VERSION_LIBVIPS" = "$EMSCRIPTEN_VERSION_SHARP" - name: Test run: emmake npm test - - name: Test packaging - run: | - emmake npm run package-from-local-build - npm pkg set "optionalDependencies.@img/sharp-wasm32=file:./npm/wasm32" - npm run clean - rm -rf node_modules/@img/sharp-linux-x64 - npm install --cpu=wasm32 - npm test - - name: Prebuild - if: startsWith(github.ref, 'refs/tags/') - env: - npm_config_nodedir: emscripten - prebuild_upload: ${{ secrets.GITHUB_TOKEN }} - run: cd src && ln -s ../package.json && emmake npx prebuild --platform=emscripten --arch=wasm32 --strip=0 + - name: Populate npm package + run: emmake npm run package-from-local-build + - uses: actions/upload-artifact@v4 + with: + name: wasm32 + path: npm/wasm32 + retention-days: 1 + if-no-files-found: error + release: + permissions: + contents: write + name: Create release + runs-on: ubuntu-24.04 + needs: + - build-native + - build-linuxmusl-arm-64 + - build-qemu + - build-emscripten + steps: + - uses: actions/checkout@v4 + - uses: actions/download-artifact@v4 + with: + path: npm + - name: Create npm workspace tarball + run: tar -vcaf npm-workspace.tar.xz -C npm . + - name: Create GitHub release for tag + if: startsWith(github.ref, 'refs/tags/v') + uses: ncipollo/release-action@v1 + with: + artifacts: npm-workspace.tar.xz + artifactContentType: application/x-xz + prerelease: ${{ contains(github.ref, '-rc') }} + makeLatest: ${{ !contains(github.ref, '-rc') }} diff --git a/.prebuildrc b/.prebuildrc deleted file mode 100644 index 0a4ccd2e..00000000 --- a/.prebuildrc +++ /dev/null @@ -1,6 +0,0 @@ -{ - "runtime": "napi", - "include-regex": "(sharp-.+\\.node|libvips-.+\\.dll)", - "prerelease": true, - "strip": true -} diff --git a/lib/libvips.js b/lib/libvips.js index 1bab0ca5..d9814445 100644 --- a/lib/libvips.js +++ b/lib/libvips.js @@ -123,7 +123,7 @@ const yarnLocator = () => { /* istanbul ignore next */ const spawnRebuild = () => - spawnSync(`node-gyp rebuild --directory=src ${isEmscripten() ? '--nodedir=emscripten' : ''}`, { + spawnSync(`node-gyp rebuild --verbose --directory=src ${isEmscripten() ? '--nodedir=emscripten' : ''}`, { ...spawnSyncOptions, stdio: 'inherit' }).status; diff --git a/npm/from-local-build.js b/npm/from-local-build.js index ad7593ec..14d8b7eb 100644 --- a/npm/from-local-build.js +++ b/npm/from-local-build.js @@ -3,24 +3,62 @@ 'use strict'; -// Populate contents of a single npm/sharpen-sharp- package -// with the local/CI build directory for local/CI prebuild testing +// Populate the npm package for the current platform with the local build -const fs = require('node:fs'); -const path = require('node:path'); +const { copyFileSync, cpSync, readFileSync, writeFileSync, appendFileSync } = require('node:fs'); +const { basename, join } = require('node:path'); const { buildPlatformArch } = require('../lib/libvips'); -const platform = buildPlatformArch(); -const dest = path.join(__dirname, platform); -// Use same config as prebuild to copy binary files -const release = path.join(__dirname, '..', 'src', 'build', 'Release'); -const prebuildrc = JSON.parse(fs.readFileSync(path.join(__dirname, '..', '.prebuildrc'), 'utf8')); -const include = new RegExp(prebuildrc['include-regex'], 'i'); -fs.cpSync(release, path.join(dest, 'lib'), { +const licensing = ` +## Licensing + +Copyright 2013 Lovell Fuller and others. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at +[https://www.apache.org/licenses/LICENSE-2.0](https://www.apache.org/licenses/LICENSE-2.0) + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +`; + +const platform = buildPlatformArch(); +const destDir = join(__dirname, platform); +console.log(`Populating npm package for platform: ${platform}`); + +// Copy binaries +const releaseDir = join(__dirname, '..', 'src', 'build', 'Release'); +const libDir = join(destDir, 'lib'); +cpSync(releaseDir, libDir, { recursive: true, filter: (file) => { - const name = path.basename(file); - return name === 'Release' || include.test(name); + const name = basename(file); + return name === 'Release' || + (name.startsWith('sharp-') && name.endsWith('.node')) || + (name.startsWith('libvips-') && name.endsWith('.dll')); } }); + +// Generate README +const { name, description } = require(`./${platform}/package.json`); +writeFileSync(join(destDir, 'README.md'), `# \`${name}\`\n\n${description}.\n${licensing}`); + +// Copy Apache-2.0 LICENSE +copyFileSync(join(__dirname, '..', 'LICENSE'), join(destDir, 'LICENSE')); + +// Copy files for packages without an explicit sharp-libvips dependency (Windows, wasm) +if (platform.startsWith('win') || platform.startsWith('wasm')) { + const libvipsPlatform = platform === 'wasm32' ? 'dev-wasm32' : platform; + const sharpLibvipsDir = join(require(`@img/sharp-libvips-${libvipsPlatform}/lib`), '..'); + // Copy versions.json + copyFileSync(join(sharpLibvipsDir, 'versions.json'), join(destDir, 'versions.json')); + // Append third party licensing to README + const readme = readFileSync(join(sharpLibvipsDir, 'README.md'), { encoding: 'utf-8' }); + const thirdParty = readme.substring(readme.indexOf('\nThis software contains')); + appendFileSync(join(destDir, 'README.md'), thirdParty); +} diff --git a/package.json b/package.json index 93100990..bfbea3aa 100644 --- a/package.json +++ b/package.json @@ -155,6 +155,7 @@ "@img/sharp-libvips-linuxmusl-x64": "1.2.0-rc.2", "@img/sharp-linux-arm": "0.34.2", "@img/sharp-linux-arm64": "0.34.2", + "@img/sharp-linux-ppc64": "0.34.2", "@img/sharp-linux-s390x": "0.34.2", "@img/sharp-linux-x64": "0.34.2", "@img/sharp-linuxmusl-arm64": "0.34.2", @@ -181,8 +182,8 @@ "license-checker": "^25.0.1", "mocha": "^11.6.0", "node-addon-api": "^8.3.1", + "node-gyp": "^11.2.0", "nyc": "^17.1.0", - "prebuild": "^13.0.1", "semistandard": "^17.0.0", "tar-fs": "^3.0.9", "tsd": "^0.32.0" @@ -197,11 +198,6 @@ "funding": { "url": "https://opencollective.com/libvips" }, - "binary": { - "napi_versions": [ - 9 - ] - }, "semistandard": { "env": [ "mocha" diff --git a/src/binding.gyp b/src/binding.gyp index 0fbf515e..31815c6e 100644 --- a/src/binding.gyp +++ b/src/binding.gyp @@ -163,6 +163,8 @@ }, 'xcode_settings': { 'OTHER_LDFLAGS': [ + '-Wl,-s', + '-Wl,-dead_strip', # Ensure runtime linking is relative to sharp.node '-Wl,-rpath,\'@loader_path/../../sharp-libvips-<(platform_and_arch)/lib\'', '-Wl,-rpath,\'@loader_path/../../../sharp-libvips-<(platform_and_arch)/<(sharp_libvips_version)/lib\'',