WIP: Remove prebuild dependency

This commit is contained in:
Lovell Fuller 2025-06-13 12:41:45 +01:00
parent 5374b036f3
commit 0af907b6d3
7 changed files with 164 additions and 169 deletions

View File

@ -4,15 +4,12 @@ on:
- pull_request - pull_request
permissions: {} permissions: {}
jobs: jobs:
github-runner: build-native:
permissions: permissions:
contents: write contents: read
name: ${{ matrix.platform }} - Node.js ${{ matrix.nodejs_version_major }} ${{ matrix.prebuild && '- prebuild' }} name: ${{ matrix.platform }} - Node.js ${{ matrix.nodejs_version_major }} ${{ matrix.prebuild && '- prebuild' }}
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
container: container: ${{ matrix.container }}
image: ${{ matrix.container }}
volumes:
- /:/host
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix:
@ -62,15 +59,6 @@ jobs:
nodejs_version: "^20.3.0" nodejs_version: "^20.3.0"
nodejs_version_major: 20 nodejs_version_major: 20
platform: linux-arm64 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 - os: macos-13
nodejs_arch: x64 nodejs_arch: x64
nodejs_version: "^18.17.0" nodejs_version: "^18.17.0"
@ -147,21 +135,11 @@ jobs:
nodejs_version_major: 22 nodejs_version_major: 22
platform: win32-arm64 platform: win32-arm64
steps: 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) - name: Dependencies (Rocky Linux glibc)
if: contains(matrix.container, 'rockylinux') if: contains(matrix.container, 'rockylinux')
run: | run: |
dnf install -y gcc-toolset-11-gcc-c++ make git python3.12 fontconfig google-noto-sans-fonts dnf install -y gcc-toolset-14-gcc-c++ make git python3.12 fontconfig google-noto-sans-fonts
echo "/opt/rh/gcc-toolset-11/root/usr/bin" >> $GITHUB_PATH echo "/opt/rh/gcc-toolset-14/root/usr/bin" >> $GITHUB_PATH
- name: Dependencies (Linux musl) - name: Dependencies (Linux musl)
if: contains(matrix.container, 'alpine') if: contains(matrix.container, 'alpine')
run: apk add build-base git python3 font-noto --update-cache run: apk add build-base git python3 font-noto --update-cache
@ -182,24 +160,65 @@ jobs:
run: npm install --build-from-source run: npm install --build-from-source
- name: Test - name: Test
run: npm test run: npm test
- name: Test packaging - name: Populate npm package
run: | if: matrix.prebuild
npm run package-from-local-build run: npm run package-from-local-build
npm pkg set "optionalDependencies.@img/sharp-${{ matrix.platform }}=file:./npm/${{ matrix.platform }}" - uses: actions/upload-artifact@v4
npm run clean if: matrix.prebuild
npm install --ignore-scripts with:
npm test name: ${{ matrix.platform }}
- name: Prebuild path: npm/${{ matrix.platform }}
if: matrix.prebuild && startsWith(github.ref, 'refs/tags/') retention-days: 1
env: if-no-files-found: error
prebuild_upload: ${{ secrets.GITHUB_TOKEN }} build-linuxmusl-arm-64:
run: |
node -e "require('fs').cpSync('package.json', 'src/package.json')"
cd src
npx prebuild
github-runner-qemu:
permissions: 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 name: ${{ matrix.platform }} - Node.js ${{ matrix.nodejs_version_major }} - prebuild
runs-on: ubuntu-24.04 runs-on: ubuntu-24.04
strategy: strategy:
@ -233,8 +252,6 @@ jobs:
with: with:
arch: ${{ matrix.run_on_arch }} arch: ${{ matrix.run_on_arch }}
distro: ${{ matrix.distro }} distro: ${{ matrix.distro }}
env: |
prebuild_upload: "${{ startsWith(github.ref, 'refs/tags/') && secrets.GITHUB_TOKEN || '' }}"
run: | run: |
apt-get update apt-get update
apt-get install -y curl g++ git libatomic1 make python3 xz-utils apt-get install -y curl g++ git libatomic1 make python3 xz-utils
@ -244,14 +261,15 @@ jobs:
npm install --build-from-source npm install --build-from-source
npx mocha --no-config --spec=test/unit/io.js --timeout=30000 npx mocha --no-config --spec=test/unit/io.js --timeout=30000
npm run package-from-local-build npm run package-from-local-build
npm pkg set "optionalDependencies.@img/sharp-${{ matrix.platform }}=file:./npm/${{ matrix.platform }}" - uses: actions/upload-artifact@v4
npm run clean with:
npm install --ignore-scripts name: ${{ matrix.platform }}
npx mocha --no-config --spec=test/unit/io.js --timeout=30000 path: npm/${{ matrix.platform }}
[[ -n $prebuild_upload ]] && cd src && ln -s ../package.json && npx prebuild || true retention-days: 1
github-runner-emscripten: if-no-files-found: error
build-emscripten:
permissions: permissions:
contents: write contents: read
name: wasm32 - prebuild name: wasm32 - prebuild
runs-on: ubuntu-24.04 runs-on: ubuntu-24.04
container: "emscripten/emsdk:4.0.10" container: "emscripten/emsdk:4.0.10"
@ -275,17 +293,35 @@ jobs:
test "$EMSCRIPTEN_VERSION_LIBVIPS" = "$EMSCRIPTEN_VERSION_SHARP" test "$EMSCRIPTEN_VERSION_LIBVIPS" = "$EMSCRIPTEN_VERSION_SHARP"
- name: Test - name: Test
run: emmake npm test run: emmake npm test
- name: Test packaging - name: Populate npm package
run: | run: emmake npm run package-from-local-build
emmake npm run package-from-local-build - uses: actions/upload-artifact@v4
npm pkg set "optionalDependencies.@img/sharp-wasm32=file:./npm/wasm32" with:
npm run clean name: wasm32
rm -rf node_modules/@img/sharp-linux-x64 path: npm/wasm32
npm install --cpu=wasm32 retention-days: 1
npm test if-no-files-found: error
- name: Prebuild release:
if: startsWith(github.ref, 'refs/tags/') permissions:
env: contents: write
npm_config_nodedir: emscripten runs-on: ubuntu-24.04
prebuild_upload: ${{ secrets.GITHUB_TOKEN }} needs:
run: cd src && ln -s ../package.json && emmake npx prebuild --platform=emscripten --arch=wasm32 --strip=0 - 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 --directory npm --exclude=from-local-build.js .
- 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') }}

View File

@ -1,6 +0,0 @@
{
"runtime": "napi",
"include-regex": "(sharp-.+\\.node|libvips-.+\\.dll)",
"prerelease": true,
"strip": true
}

View File

@ -123,7 +123,7 @@ const yarnLocator = () => {
/* istanbul ignore next */ /* istanbul ignore next */
const spawnRebuild = () => const spawnRebuild = () =>
spawnSync(`node-gyp rebuild --directory=src ${isEmscripten() ? '--nodedir=emscripten' : ''}`, { spawnSync(`node-gyp rebuild --verbose --directory=src ${isEmscripten() ? '--nodedir=emscripten' : ''}`, {
...spawnSyncOptions, ...spawnSyncOptions,
stdio: 'inherit' stdio: 'inherit'
}).status; }).status;

View File

@ -1,73 +0,0 @@
// Copyright 2013 Lovell Fuller and others.
// SPDX-License-Identifier: Apache-2.0
'use strict';
// Populate contents of all packages with the current GitHub release
const { readFile, writeFile, appendFile, copyFile, rm } = require('node:fs/promises');
const path = require('node:path');
const { Readable } = require('node:stream');
const { pipeline } = require('node:stream/promises');
const { createGunzip } = require('node:zlib');
const { extract } = require('tar-fs');
const { workspaces } = require('./package.json');
const { version } = require('../package.json');
const mapTarballEntry = (header) => {
header.name = path.basename(header.name);
return header;
};
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.
`;
workspaces.map(async platform => {
const prebuildPlatform = platform === 'wasm32' ? 'emscripten-wasm32' : platform;
const url = `https://github.com/lovell/sharp/releases/download/v${version}/sharp-v${version}-napi-v9-${prebuildPlatform}.tar.gz`;
const dir = path.join(__dirname, platform);
const response = await fetch(url);
if (!response.ok) {
console.log(`Skipping ${platform}: ${response.statusText}`);
return;
}
// Extract prebuild tarball
const lib = path.join(dir, 'lib');
await rm(lib, { force: true, recursive: true });
await pipeline(
Readable.fromWeb(response.body),
createGunzip(),
extract(lib, { map: mapTarballEntry })
);
// Generate README
const { name, description } = require(`./${platform}/package.json`);
await writeFile(path.join(dir, 'README.md'), `# \`${name}\`\n\n${description}.\n${licensing}`);
// Copy Apache-2.0 LICENSE
await copyFile(path.join(__dirname, '..', 'LICENSE'), path.join(dir, '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 = path.join(require(`@img/sharp-libvips-${libvipsPlatform}/lib`), '..');
// Copy versions.json
await copyFile(path.join(sharpLibvipsDir, 'versions.json'), path.join(dir, 'versions.json'));
// Append third party licensing to README
const readme = await readFile(path.join(sharpLibvipsDir, 'README.md'), { encoding: 'utf-8' });
const thirdParty = readme.substring(readme.indexOf('\nThis software contains'));
appendFile(path.join(dir, 'README.md'), thirdParty);
}
});

View File

@ -3,24 +3,62 @@
'use strict'; 'use strict';
// Populate contents of a single npm/sharpen-sharp-<build-platform> package // Populate the npm package for the current platform with the local build
// with the local/CI build directory for local/CI prebuild testing
const fs = require('node:fs'); const { copyFileSync, cpSync, readFileSync, writeFileSync, appendFileSync } = require('node:fs');
const path = require('node:path'); const { basename, join } = require('node:path');
const { buildPlatformArch } = require('../lib/libvips'); const { buildPlatformArch } = require('../lib/libvips');
const platform = buildPlatformArch();
const dest = path.join(__dirname, platform);
// Use same config as prebuild to copy binary files const licensing = `
const release = path.join(__dirname, '..', 'src', 'build', 'Release'); ## Licensing
const prebuildrc = JSON.parse(fs.readFileSync(path.join(__dirname, '..', '.prebuildrc'), 'utf8'));
const include = new RegExp(prebuildrc['include-regex'], 'i'); Copyright 2013 Lovell Fuller and others.
fs.cpSync(release, path.join(dest, 'lib'), {
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, recursive: true,
filter: (file) => { filter: (file) => {
const name = path.basename(file); const name = basename(file);
return name === 'Release' || include.test(name); return name === 'Release' ||
(name.startsWith('sharp-') && name.includes('.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);
}

View File

@ -92,7 +92,7 @@
"Don Denton <don@happycollision.com>" "Don Denton <don@happycollision.com>"
], ],
"scripts": { "scripts": {
"install": "node install/check", "install": "node install/check.js",
"clean": "rm -rf src/build/ .nyc_output/ coverage/ test/fixtures/output.*", "clean": "rm -rf src/build/ .nyc_output/ coverage/ test/fixtures/output.*",
"test": "npm run test-lint && npm run test-unit && npm run test-licensing && npm run test-types", "test": "npm run test-lint && npm run test-unit && npm run test-licensing && npm run test-types",
"test-lint": "semistandard && cpplint", "test-lint": "semistandard && cpplint",
@ -100,8 +100,7 @@
"test-licensing": "license-checker --production --summary --onlyAllow=\"Apache-2.0;BSD;ISC;LGPL-3.0-or-later;MIT\"", "test-licensing": "license-checker --production --summary --onlyAllow=\"Apache-2.0;BSD;ISC;LGPL-3.0-or-later;MIT\"",
"test-leak": "./test/leak/leak.sh", "test-leak": "./test/leak/leak.sh",
"test-types": "tsd", "test-types": "tsd",
"package-from-local-build": "node npm/from-local-build", "package-from-local-build": "node npm/from-local-build.js",
"package-from-github-release": "node npm/from-github-release",
"docs-build": "node docs/build.mjs", "docs-build": "node docs/build.mjs",
"docs-serve": "cd docs && npm start", "docs-serve": "cd docs && npm start",
"docs-publish": "cd docs && npm run build && npx firebase-tools deploy --project pixelplumbing --only hosting:pixelplumbing-sharp" "docs-publish": "cd docs && npm run build && npx firebase-tools deploy --project pixelplumbing --only hosting:pixelplumbing-sharp"
@ -155,6 +154,7 @@
"@img/sharp-libvips-linuxmusl-x64": "1.2.0-rc.2", "@img/sharp-libvips-linuxmusl-x64": "1.2.0-rc.2",
"@img/sharp-linux-arm": "0.34.2", "@img/sharp-linux-arm": "0.34.2",
"@img/sharp-linux-arm64": "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-s390x": "0.34.2",
"@img/sharp-linux-x64": "0.34.2", "@img/sharp-linux-x64": "0.34.2",
"@img/sharp-linuxmusl-arm64": "0.34.2", "@img/sharp-linuxmusl-arm64": "0.34.2",
@ -181,8 +181,8 @@
"license-checker": "^25.0.1", "license-checker": "^25.0.1",
"mocha": "^11.6.0", "mocha": "^11.6.0",
"node-addon-api": "^8.3.1", "node-addon-api": "^8.3.1",
"node-gyp": "^11.2.0",
"nyc": "^17.1.0", "nyc": "^17.1.0",
"prebuild": "^13.0.1",
"semistandard": "^17.0.0", "semistandard": "^17.0.0",
"tar-fs": "^3.0.9", "tar-fs": "^3.0.9",
"tsd": "^0.32.0" "tsd": "^0.32.0"
@ -197,11 +197,6 @@
"funding": { "funding": {
"url": "https://opencollective.com/libvips" "url": "https://opencollective.com/libvips"
}, },
"binary": {
"napi_versions": [
9
]
},
"semistandard": { "semistandard": {
"env": [ "env": [
"mocha" "mocha"

View File

@ -163,6 +163,8 @@
}, },
'xcode_settings': { 'xcode_settings': {
'OTHER_LDFLAGS': [ 'OTHER_LDFLAGS': [
'-Wl,-s',
'-Wl,-dead_strip',
# Ensure runtime linking is relative to sharp.node # 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)/lib\'',
'-Wl,-rpath,\'@loader_path/../../../sharp-libvips-<(platform_and_arch)/<(sharp_libvips_version)/lib\'', '-Wl,-rpath,\'@loader_path/../../../sharp-libvips-<(platform_and_arch)/<(sharp_libvips_version)/lib\'',
@ -176,6 +178,9 @@
'defines': [ 'defines': [
'_GLIBCXX_USE_CXX11_ABI=1' '_GLIBCXX_USE_CXX11_ABI=1'
], ],
'cflags_cc': [
'<!(node -p "require(\'detect-libc\').isNonGlibcLinuxSync() ? \'\' : \'-flto=auto\'")'
],
'link_settings': { 'link_settings': {
'libraries': [ 'libraries': [
'-l:libvips-cpp.so.<(vips_version)' '-l:libvips-cpp.so.<(vips_version)'