Skip to content

Commit

Permalink
Install: multiple platform-arch binaries in same tree
Browse files Browse the repository at this point in the history
  • Loading branch information
lovell committed Jul 14, 2021
1 parent 8c6d9fd commit 6c2e6c5
Show file tree
Hide file tree
Showing 12 changed files with 51 additions and 79 deletions.
9 changes: 5 additions & 4 deletions binding.gyp
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
{
'variables': {
'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': [{
'target_name': 'libvips-cpp',
Expand Down Expand Up @@ -66,7 +67,7 @@
}]
]
}, {
'target_name': 'sharp',
'target_name': 'sharp-<(platform_and_arch)',
'defines': [
'NAPI_VERSION=5'
],
Expand Down Expand Up @@ -147,7 +148,7 @@
'xcode_settings': {
'OTHER_LDFLAGS': [
# 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\''
]
}
}],
Expand All @@ -162,7 +163,7 @@
],
'ldflags': [
# 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\''
]
}
}]
Expand Down
3 changes: 3 additions & 0 deletions docs/changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ Requires libvips v8.11.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*

Requires libvips v8.10.6
Expand Down
22 changes: 4 additions & 18 deletions docs/install.md
Original file line number Diff line number Diff line change
Expand Up @@ -206,32 +206,18 @@ to `false` when using the `yarn` package manager.

## 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)
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),
run the following commands:
run the following additional command after `npm install`:

macOS:
```sh
rm -rf node_modules/sharp
npm install
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.
A 1536 MB function provides ~12x more CPU time than a 128 MB function.

Expand Down
12 changes: 6 additions & 6 deletions install/dll-copy.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,19 @@ const fs = require('fs');
const path = require('path');

const libvips = require('../lib/libvips');
const platform = require('../lib/platform');

const minimumLibvipsVersion = libvips.minimumLibvipsVersion;

const platform = process.env.npm_config_platform || process.platform;
if (platform === 'win32') {
const buildDir = path.join(__dirname, '..', 'build');
const buildReleaseDir = path.join(buildDir, 'Release');
const platformAndArch = platform();

if (platformAndArch.startsWith('win32')) {
const buildReleaseDir = path.join(__dirname, '..', 'build', 'Release');
libvips.log(`Creating ${buildReleaseDir}`);
try {
libvips.mkdirSync(buildDir);
libvips.mkdirSync(buildReleaseDir);
} 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}`);
try {
fs
Expand Down
4 changes: 1 addition & 3 deletions install/libvips.js
Original file line number Diff line number Diff line change
Expand Up @@ -55,9 +55,7 @@ const handleError = function (err) {
};

const extractTarball = function (tarPath, platformAndArch) {
const vendorPath = path.join(__dirname, '..', 'vendor');
libvips.mkdirSync(vendorPath);
const versionedVendorPath = path.join(vendorPath, minimumLibvipsVersion);
const versionedVendorPath = path.join(__dirname, '..', 'vendor', minimumLibvipsVersion, platformAndArch);
libvips.mkdirSync(versionedVendorPath);

const ignoreVendorInclude = hasSharpPrebuild.includes(platformAndArch) && !process.env.npm_config_build_from_source;
Expand Down
27 changes: 1 addition & 26 deletions lib/constructor.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,32 +5,7 @@ const stream = require('stream');
const is = require('./is');

require('./libvips').hasVendoredLibvips();

/* 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);
}
require('./sharp');

// Use NODE_DEBUG=sharp to enable libvips warnings
const debuglog = util.debuglog('sharp');
Expand Down
2 changes: 1 addition & 1 deletion lib/input.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

const color = require('color');
const is = require('./is');
const sharp = require('../build/Release/sharp.node');
const sharp = require('./sharp');

/**
* Extract input options, if any, from an object.
Expand Down
21 changes: 3 additions & 18 deletions lib/libvips.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ const spawnSyncOptions = {

const mkdirSync = function (dirPath) {
try {
fs.mkdirSync(dirPath);
fs.mkdirSync(dirPath, { recursive: true });
} catch (err) {
/* istanbul ignore if */
if (err.code !== 'EEXIST') {
Expand Down Expand Up @@ -67,23 +67,8 @@ const globalLibvipsVersion = function () {
};

const hasVendoredLibvips = function () {
const currentPlatformId = platform();
const vendorPath = path.join(__dirname, '..', 'vendor', minimumLibvipsVersion);
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 vendorPath = path.join(__dirname, '..', 'vendor', minimumLibvipsVersion, platform());
return fs.existsSync(vendorPath);
};

const pkgConfigPath = function () {
Expand Down
2 changes: 1 addition & 1 deletion lib/output.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
'use strict';

const is = require('./is');
const sharp = require('../build/Release/sharp.node');
const sharp = require('./sharp');

const formats = new Map([
['heic', 'heif'],
Expand Down
24 changes: 24 additions & 0 deletions lib/sharp.js
Original file line number Diff line number Diff line change
@@ -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);
}
2 changes: 1 addition & 1 deletion lib/utility.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ const events = require('events');
const detectLibc = require('detect-libc');

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.
Expand Down
2 changes: 1 addition & 1 deletion test/fixtures/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

const path = require('path');
const sharp = require('../../');
const maxColourDistance = require('../../build/Release/sharp')._maxColourDistance;
const maxColourDistance = require('../../lib/sharp')._maxColourDistance;

// Helpers
const getPath = function (filename) {
Expand Down

0 comments on commit 6c2e6c5

Please sign in to comment.