Verify platform of vendor binaries at install and run time

This commit is contained in:
Lovell Fuller 2017-09-26 19:06:40 +01:00
parent 57946ed672
commit 7b6c80327e
4 changed files with 131 additions and 67 deletions

View File

@ -10,24 +10,15 @@ const semver = require('semver');
const tar = require('tar');
const detectLibc = require('detect-libc');
const platform = require('./lib/platform');
// Use NPM-provided environment variable where available, falling back to require-based method for Electron
const minimumLibvipsVersion = process.env.npm_package_config_libvips || require('./package.json').config.libvips;
const distBaseUrl = process.env.SHARP_DIST_BASE_URL || `https://github.com/lovell/sharp-libvips/releases/download/v${minimumLibvipsVersion}/`;
const platform = process.env.npm_config_platform || process.platform;
const arch = process.env.npm_config_arch || process.arch;
// -- Helpers
// Does this file exist?
const isFile = function (file) {
try {
return fs.statSync(file).isFile();
} catch (err) {}
};
const unpack = function (tarPath, done) {
const vendorPath = path.join(__dirname, 'vendor');
fs.mkdirSync(vendorPath);
@ -41,33 +32,29 @@ const unpack = function (tarPath, done) {
.catch(error);
};
const platformId = function () {
const platformId = [platform];
if (arch === 'arm' || arch === 'armhf' || arch === 'arm64') {
const armVersion = (arch === 'arm64') ? '8' : process.env.npm_config_armv || process.config.variables.arm_version || '6';
platformId.push('armv' + armVersion);
} else {
platformId.push(arch);
}
return platformId.join('-');
};
// Error
const error = function (msg) {
if (msg instanceof Error) {
msg = msg.message;
}
process.stderr.write('ERROR: ' + msg + '\n');
process.stderr.write(`sharp: ${msg}\n`);
process.exit(1);
};
// -- Binary downloaders
module.exports.download_vips = function () {
// Has vips been installed locally?
const vipsHeaderPath = path.join(__dirname, 'vendor', 'include', 'vips', 'vips.h');
if (!isFile(vipsHeaderPath)) {
// Check for existing vendored binaries and verify platform matches
const currentPlatformId = platform();
try {
const vendorPlatformId = require(path.join(__dirname, 'vendor', 'platform.json'));
if (currentPlatformId === vendorPlatformId) {
return;
} else {
error(`'${vendorPlatformId}' binaries cannot be used on the '${currentPlatformId}' platform. Please remove the 'node_modules/sharp/vendor' directory and run 'npm install'.`);
}
} catch (err) {}
// Ensure Intel 64-bit or ARM
const arch = process.env.npm_config_arch || process.arch;
if (arch === 'ia32') {
error('Intel Architecture 32-bit systems require manual installation of libvips - please see http://sharp.dimens.io/page/install');
}
@ -80,9 +67,9 @@ module.exports.download_vips = function () {
error(`Use with glibc version ${detectLibc.version} requires manual installation of libvips - please see http://sharp.dimens.io/page/install`);
}
// Arch/platform-specific .tar.gz
const tarFilename = ['libvips', minimumLibvipsVersion, platformId()].join('-') + '.tar.gz';
const tarFilename = ['libvips', minimumLibvipsVersion, currentPlatformId].join('-') + '.tar.gz';
// Download to per-process temporary file
const tarPathTemp = path.join(os.tmpdir(), process.pid + '-' + tarFilename);
const tarPathTemp = path.join(os.tmpdir(), `${process.pid}-${tarFilename}`);
const tmpFile = fs.createWriteStream(tarPathTemp).on('finish', function () {
unpack(tarPathTemp, function () {
// Attempt to remove temporary file
@ -100,14 +87,13 @@ module.exports.download_vips = function () {
};
simpleGet(simpleGetOpt, function (err, response) {
if (err) {
error('Download of ' + url + ' failed: ' + err.message);
error(`${url} download failed: ${err.message}`);
}
if (response.statusCode !== 200) {
error(url + ' status code ' + response.statusCode);
error(`${url} download failed: status ${response.statusCode}`);
}
response.pipe(tmpFile);
});
}
};
module.exports.use_global_vips = function () {

View File

@ -6,8 +6,24 @@ const stream = require('stream');
const events = require('events');
const semver = require('semver');
const is = require('./is');
const platform = require('./platform');
const sharp = require('../build/Release/sharp.node');
// Vendor platform
(function () {
let vendorPlatformId;
try {
vendorPlatformId = require('../vendor/platform.json');
} catch (err) {
return;
}
const currentPlatformId = platform();
/* istanbul ignore if */
if (currentPlatformId !== vendorPlatformId) {
throw new Error(`'${vendorPlatformId}' binaries cannot be used on the '${currentPlatformId}' platform. Please remove the 'node_modules/sharp/vendor' directory and run 'npm rebuild'.`);
}
})();
// Versioning
let versions = {
vips: sharp.libvipsVersion()
@ -21,7 +37,7 @@ let versions = {
}
// Include versions of dependencies, if present
try {
versions = require('../vendor/lib/versions.json');
versions = require('../vendor/versions.json');
} catch (err) {}
})();

15
lib/platform.js Normal file
View File

@ -0,0 +1,15 @@
'use strict';
module.exports = function () {
const arch = process.env.npm_config_arch || process.arch;
const platform = process.env.npm_config_platform || process.platform;
const platformId = [platform];
if (arch === 'arm' || arch === 'armhf' || arch === 'arm64') {
const armVersion = (arch === 'arm64') ? '8' : process.env.npm_config_armv || process.config.variables.arm_version || '6';
platformId.push(`armv${armVersion}`);
} else {
platformId.push(arch);
}
return platformId.join('-');
};

47
test/unit/platform.js Normal file
View File

@ -0,0 +1,47 @@
'use strict';
const assert = require('assert');
const platform = require('../../lib/platform');
describe('Platform-detection', function () {
it('Can override arch with npm_config_arch', function () {
process.env.npm_config_arch = 'test';
assert.strictEqual('test', platform().split('-')[1]);
delete process.env.npm_config_arch;
});
it('Can override platform with npm_config_platform', function () {
process.env.npm_config_platform = 'test';
assert.strictEqual('test', platform().split('-')[0]);
delete process.env.npm_config_platform;
});
it('Can override ARM version via npm_config_armv', function () {
process.env.npm_config_arch = 'arm';
process.env.npm_config_armv = 'test';
assert.strictEqual('armvtest', platform().split('-')[1]);
delete process.env.npm_config_armv;
delete process.env.npm_config_arch;
});
it('Can detect ARM version via process.config', function () {
process.env.npm_config_arch = 'armhf';
const armVersion = process.config.variables.arm_version;
process.config.variables.arm_version = 'test';
assert.strictEqual('armvtest', platform().split('-')[1]);
process.config.variables.arm_version = armVersion;
delete process.env.npm_config_arch;
});
it('Defaults to ARMv6 for 32-bit', function () {
process.env.npm_config_arch = 'arm';
assert.strictEqual('armv6', platform().split('-')[1]);
delete process.env.npm_config_arch;
});
it('Defaults to ARMv8 for 64-bit', function () {
process.env.npm_config_arch = 'arm64';
assert.strictEqual('armv8', platform().split('-')[1]);
delete process.env.npm_config_arch;
});
});