Install: multiple platform-arch binaries in same tree

This commit is contained in:
Lovell Fuller 2021-06-13 09:32:16 +01:00
parent 8c6d9fdc62
commit 6c2e6c5432
12 changed files with 51 additions and 79 deletions

View File

@ -1,7 +1,8 @@
{ {
'variables': { 'variables': {
'vips_version': '<!(node -p "require(\'./lib/libvips\').minimumLibvipsVersion")', 'vips_version': '<!(node -p "require(\'./lib/libvips\').minimumLibvipsVersion")',
'sharp_vendor_dir': '<(module_root_dir)/vendor/<(vips_version)' 'platform_and_arch': '<!(node -p "require(\'./lib/platform\')()")',
'sharp_vendor_dir': '<(module_root_dir)/vendor/<(vips_version)/<(platform_and_arch)'
}, },
'targets': [{ 'targets': [{
'target_name': 'libvips-cpp', 'target_name': 'libvips-cpp',
@ -66,7 +67,7 @@
}] }]
] ]
}, { }, {
'target_name': 'sharp', 'target_name': 'sharp-<(platform_and_arch)',
'defines': [ 'defines': [
'NAPI_VERSION=5' 'NAPI_VERSION=5'
], ],
@ -147,7 +148,7 @@
'xcode_settings': { 'xcode_settings': {
'OTHER_LDFLAGS': [ 'OTHER_LDFLAGS': [
# Ensure runtime linking is relative to sharp.node # Ensure runtime linking is relative to sharp.node
'-Wl,-rpath,\'@loader_path/../../vendor/<(vips_version)/lib\'' '-Wl,-rpath,\'@loader_path/../../vendor/<(vips_version)/<(platform_and_arch)/lib\''
] ]
} }
}], }],
@ -162,7 +163,7 @@
], ],
'ldflags': [ 'ldflags': [
# Ensure runtime linking is relative to sharp.node # Ensure runtime linking is relative to sharp.node
'-Wl,-s -Wl,--disable-new-dtags -Wl,-rpath=\'$$ORIGIN/../../vendor/<(vips_version)/lib\'' '-Wl,-s -Wl,--disable-new-dtags -Wl,-rpath=\'$$ORIGIN/../../vendor/<(vips_version)/<(platform_and_arch)/lib\''
] ]
} }
}] }]

View File

@ -8,6 +8,9 @@ Requires libvips v8.11.0
* Drop support for Node.js 10, now requires Node.js >= 12.13.0. * Drop support for Node.js 10, now requires Node.js >= 12.13.0.
* Allow multiple platform-arch binaries in same `node_modules` installation tree.
[#2575](https://github.com/lovell/sharp/issues/2575)
## v0.28 - *bijou* ## v0.28 - *bijou*
Requires libvips v8.10.6 Requires libvips v8.10.6

View File

@ -206,32 +206,18 @@ to `false` when using the `yarn` package manager.
## AWS Lambda ## AWS Lambda
The binaries in the `node_modules` directory of the The `node_modules` directory of the
[deployment package](https://docs.aws.amazon.com/lambda/latest/dg/nodejs-package.html) [deployment package](https://docs.aws.amazon.com/lambda/latest/dg/nodejs-package.html)
must be for the Linux x64 platform. must include binaries for the Linux x64 platform.
When building your deployment package on machines other than Linux x64 (glibc), When building your deployment package on machines other than Linux x64 (glibc),
run the following commands: run the following additional command after `npm install`:
macOS:
```sh ```sh
rm -rf node_modules/sharp npm install
SHARP_IGNORE_GLOBAL_LIBVIPS=1 npm install --arch=x64 --platform=linux sharp SHARP_IGNORE_GLOBAL_LIBVIPS=1 npm install --arch=x64 --platform=linux sharp
``` ```
Windows:
```sh
rmdir /s /q node_modules/sharp
npm install --arch=x64 --platform=linux sharp
```
Alternatively a Docker container closely matching the Lambda runtime can be used:
```sh
rm -rf node_modules/sharp
docker run -v "$PWD":/var/task lambci/lambda:build-nodejs12.x npm install sharp
```
To get the best performance select the largest memory available. To get the best performance select the largest memory available.
A 1536 MB function provides ~12x more CPU time than a 128 MB function. A 1536 MB function provides ~12x more CPU time than a 128 MB function.

View File

@ -4,19 +4,19 @@ const fs = require('fs');
const path = require('path'); const path = require('path');
const libvips = require('../lib/libvips'); const libvips = require('../lib/libvips');
const platform = require('../lib/platform');
const minimumLibvipsVersion = libvips.minimumLibvipsVersion; const minimumLibvipsVersion = libvips.minimumLibvipsVersion;
const platform = process.env.npm_config_platform || process.platform; const platformAndArch = platform();
if (platform === 'win32') {
const buildDir = path.join(__dirname, '..', 'build'); if (platformAndArch.startsWith('win32')) {
const buildReleaseDir = path.join(buildDir, 'Release'); const buildReleaseDir = path.join(__dirname, '..', 'build', 'Release');
libvips.log(`Creating ${buildReleaseDir}`); libvips.log(`Creating ${buildReleaseDir}`);
try { try {
libvips.mkdirSync(buildDir);
libvips.mkdirSync(buildReleaseDir); libvips.mkdirSync(buildReleaseDir);
} catch (err) {} } catch (err) {}
const vendorLibDir = path.join(__dirname, '..', 'vendor', minimumLibvipsVersion, 'lib'); const vendorLibDir = path.join(__dirname, '..', 'vendor', minimumLibvipsVersion, platformAndArch, 'lib');
libvips.log(`Copying DLLs from ${vendorLibDir} to ${buildReleaseDir}`); libvips.log(`Copying DLLs from ${vendorLibDir} to ${buildReleaseDir}`);
try { try {
fs fs

View File

@ -55,9 +55,7 @@ const handleError = function (err) {
}; };
const extractTarball = function (tarPath, platformAndArch) { const extractTarball = function (tarPath, platformAndArch) {
const vendorPath = path.join(__dirname, '..', 'vendor'); const versionedVendorPath = path.join(__dirname, '..', 'vendor', minimumLibvipsVersion, platformAndArch);
libvips.mkdirSync(vendorPath);
const versionedVendorPath = path.join(vendorPath, minimumLibvipsVersion);
libvips.mkdirSync(versionedVendorPath); libvips.mkdirSync(versionedVendorPath);
const ignoreVendorInclude = hasSharpPrebuild.includes(platformAndArch) && !process.env.npm_config_build_from_source; const ignoreVendorInclude = hasSharpPrebuild.includes(platformAndArch) && !process.env.npm_config_build_from_source;

View File

@ -5,32 +5,7 @@ const stream = require('stream');
const is = require('./is'); const is = require('./is');
require('./libvips').hasVendoredLibvips(); require('./libvips').hasVendoredLibvips();
require('./sharp');
/* istanbul ignore next */
try {
require('../build/Release/sharp.node');
} catch (err) {
// Bail early if bindings aren't available
const help = ['', 'Something went wrong installing the "sharp" module', '', err.message, ''];
if (/NODE_MODULE_VERSION/.test(err.message)) {
help.push('- Ensure the version of Node.js used at install time matches that used at runtime');
} else if (/invalid ELF header/.test(err.message)) {
help.push(`- Ensure "${process.platform}" is used at install time as well as runtime`);
} else if (/dylib/.test(err.message) && /Incompatible library version/.test(err.message)) {
help.push('- Run "brew update && brew upgrade vips"');
} else {
help.push(
'- Remove the "node_modules/sharp" directory then run',
' "npm install --ignore-scripts=false --verbose sharp" and look for errors'
);
}
help.push(
'- Consult the installation documentation at https://sharp.pixelplumbing.com/install',
'- Search for this error at https://github.com/lovell/sharp/issues', ''
);
const error = help.join('\n');
throw new Error(error);
}
// Use NODE_DEBUG=sharp to enable libvips warnings // Use NODE_DEBUG=sharp to enable libvips warnings
const debuglog = util.debuglog('sharp'); const debuglog = util.debuglog('sharp');

View File

@ -2,7 +2,7 @@
const color = require('color'); const color = require('color');
const is = require('./is'); const is = require('./is');
const sharp = require('../build/Release/sharp.node'); const sharp = require('./sharp');
/** /**
* Extract input options, if any, from an object. * Extract input options, if any, from an object.

View File

@ -21,7 +21,7 @@ const spawnSyncOptions = {
const mkdirSync = function (dirPath) { const mkdirSync = function (dirPath) {
try { try {
fs.mkdirSync(dirPath); fs.mkdirSync(dirPath, { recursive: true });
} catch (err) { } catch (err) {
/* istanbul ignore if */ /* istanbul ignore if */
if (err.code !== 'EEXIST') { if (err.code !== 'EEXIST') {
@ -67,23 +67,8 @@ const globalLibvipsVersion = function () {
}; };
const hasVendoredLibvips = function () { const hasVendoredLibvips = function () {
const currentPlatformId = platform(); const vendorPath = path.join(__dirname, '..', 'vendor', minimumLibvipsVersion, platform());
const vendorPath = path.join(__dirname, '..', 'vendor', minimumLibvipsVersion); return fs.existsSync(vendorPath);
let vendorPlatformId;
try {
vendorPlatformId = require(path.join(vendorPath, 'platform.json'));
} catch (err) {}
/* istanbul ignore else */
if (vendorPlatformId) {
/* istanbul ignore else */
if (currentPlatformId === vendorPlatformId) {
return true;
} else {
throw new Error(`'${vendorPlatformId}' binaries cannot be used on the '${currentPlatformId}' platform. Please remove the 'node_modules/sharp' directory and run 'npm install' on the '${currentPlatformId}' platform.`);
}
} else {
return false;
}
}; };
const pkgConfigPath = function () { const pkgConfigPath = function () {

View File

@ -1,7 +1,7 @@
'use strict'; 'use strict';
const is = require('./is'); const is = require('./is');
const sharp = require('../build/Release/sharp.node'); const sharp = require('./sharp');
const formats = new Map([ const formats = new Map([
['heic', 'heif'], ['heic', 'heif'],

24
lib/sharp.js Normal file
View File

@ -0,0 +1,24 @@
'use strict';
const platformAndArch = require('./platform')();
/* istanbul ignore next */
try {
module.exports = require(`../build/Release/sharp-${platformAndArch}.node`);
} catch (err) {
// Bail early if bindings aren't available
const help = ['', 'Something went wrong installing the "sharp" module', '', err.message, '', 'Possible solutions:'];
if (/dylib/.test(err.message) && /Incompatible library version/.test(err.message)) {
help.push('- Update Homebrew: "brew update && brew upgrade vips"');
} else {
help.push(
'- Install with the --verbose flag and look for errors: "npm install --ignore-scripts=false --verbose sharp"',
`- Install for the current runtime: "npm install --platform=${process.platform} --arch=${process.arch} sharp"`
);
}
help.push(
'- Consult the installation documentation: https://sharp.pixelplumbing.com/install'
);
console.error(help.join('\n'));
process.exit(1);
}

View File

@ -4,7 +4,7 @@ const events = require('events');
const detectLibc = require('detect-libc'); const detectLibc = require('detect-libc');
const is = require('./is'); const is = require('./is');
const sharp = require('../build/Release/sharp.node'); const sharp = require('./sharp');
/** /**
* An Object containing nested boolean values representing the available input and output formats/methods. * An Object containing nested boolean values representing the available input and output formats/methods.

View File

@ -2,7 +2,7 @@
const path = require('path'); const path = require('path');
const sharp = require('../../'); const sharp = require('../../');
const maxColourDistance = require('../../build/Release/sharp')._maxColourDistance; const maxColourDistance = require('../../lib/sharp')._maxColourDistance;
// Helpers // Helpers
const getPath = function (filename) { const getPath = function (filename) {