openvino-node
Version:
OpenVINO™ utils for using from Node.js environment
201 lines (170 loc) • 6.23 kB
JavaScript
const os = require('node:os');
const tar = require('tar-fs');
const path = require('node:path');
const gunzip = require('gunzip-maybe');
const fs = require('node:fs/promises');
const { createReadStream, existsSync } = require('node:fs');
const { downloadFile, checkIfPathExists, removeDirectory } = require('./utils');
class BinaryManager {
constructor(packageRoot, version, binaryConfig) {
this.packageRoot = packageRoot;
this.version = version;
this.binaryConfig = binaryConfig;
}
getPlatformLabel() {
return os.platform();
}
getArchLabel() {
return os.arch();
}
getExtension() {
return 'tar.gz';
}
getArchiveUrl() {
const {
host,
package_name: packageNameTemplate,
remote_path: remotePathTemplate,
} = this.binaryConfig;
const fullPathTemplate = `${remotePathTemplate}${packageNameTemplate}`;
const fullPath = fullPathTemplate
.replace(new RegExp('{version}', 'g'), this.version)
.replace(new RegExp('{platform}', 'g'), this.getPlatformLabel())
.replace(new RegExp('{arch}', 'g'), this.getArchLabel())
.replace(new RegExp('{extension}', 'g'), this.getExtension());
return new URL(fullPath, host).toString();
}
getDestinationPath() {
const modulePath = this.binaryConfig['module_path'];
return path.resolve(this.packageRoot, modulePath);
}
/**
* Prepares the binary by downloading and extracting the OpenVINO runtime
* archive.
*
* @param {string} packageRoot - The root directory of the package.
* @param {string} version - The version of the binary.
* @param {Object} binaryConfig - The configuration object for the binary.
* @param {Object} options - The options for preparing the binary.
* @param {boolean} options.force - Whether to force the download if the
* directory already exists.
* @param {boolean} options.ignoreIfExists - Whether to ignore the download
* if the directory already exists.
* @param {string} [options.proxy] - The proxy to use for downloading the
* file.
* @throws {Error} If the directory already exists and the force option
* is not set.
* @throws {Error} If the download or extraction fails.
* @returns {Promise<void>} A promise that resolves when the binary is
* prepared.
*/
static async prepareBinary(packageRoot, version, binaryConfig, options) {
const binaryManager = new this(packageRoot, version, binaryConfig);
const destinationPath = binaryManager.getDestinationPath();
const isRuntimeDirectoryExists = await checkIfPathExists(destinationPath);
if (isRuntimeDirectoryExists && !options.force) {
if (options.ignoreIfExists) {
console.warn(
`Directory '${destinationPath}' already exists. Skipping `
+ 'runtime downloading because "ignoreIfExists" flag is passed.'
);
return;
}
throw new Error(
`Directory '${destinationPath}' already exists. ` +
'To force runtime downloading use "force" flag.',
);
}
// Install binaries from directories if possible
if (process.env.npm_package_resolved?.startsWith('file:')) {
const binDir = path.join(
path.dirname(process.env.npm_package_resolved),
'bin'
).replace('file:', '');
if (existsSync(path.join(binDir, 'libopenvino.so')) ||
existsSync(path.join(binDir, 'openvino.dll'))) {
try {
await fs.cp(binDir, destinationPath, {'recursive': true});
return;
} catch(error) {
console.error(`Failed to copy bin directory: ${error}.`);
}
}
console.log('Do not find bin directory with openvino library.');
}
const archiveUrl = binaryManager.getArchiveUrl();
let tempDirectoryPath = null;
try {
tempDirectoryPath = await fs.mkdtemp(
path.join(os.tmpdir(), 'temp-ov-runtime-archive-')
);
const filename = path.basename(archiveUrl);
console.log('Downloading OpenVINO runtime archive...');
const archiveFilePath = await downloadFile(
archiveUrl,
tempDirectoryPath,
filename,
options.proxy,
);
console.log('OpenVINO runtime archive downloaded.');
await removeDirectory(destinationPath);
await this.unarchive(archiveFilePath, destinationPath);
console.log('The archive was successfully extracted.');
} catch(error) {
console.error(`Failed to download OpenVINO runtime: ${error}.`);
throw error;
} finally {
if (tempDirectoryPath) await removeDirectory(tempDirectoryPath);
}
}
/**
* Checks if the current platform and architecture are compatible.
*
* Supported platforms: 'win32', 'linux', 'darwin'.
* Supported architectures: 'arm64', 'armhf', 'x64'.
*
* If the platform or architecture is not supported, an error message
* is logged to the console.
*
* @returns {boolean} Returns true if the platform and architecture are
* compatible, otherwise false.
*/
static isCompatible() {
const missleadings = [];
const platform = os.platform();
if (!['win32', 'linux', 'darwin'].includes(platform))
missleadings.push(`Platform '${platform}' is not supported.`);
const arch = os.arch();
if (!['arm64', 'armhf', 'x64'].includes(arch))
missleadings.push(`Architecture '${arch}' is not supported.`);
if (platform === 'win32' && arch !== 'x64')
missleadings.push(`Version for windows and '${arch}' is not supported.`);
if (missleadings.length) {
console.error(missleadings.join(' '));
return false;
}
return true;
}
/**
* Unarchive tar and tar.gz archives.
*
* @function unarchive
* @param {string} archivePath - Path to archive.
* @param {string} dest - Path where to unpack.
* @returns {Promise<void>}
*/
static unarchive(archivePath, dest) {
return new Promise((resolve, reject) => {
createReadStream(archivePath)
.pipe(gunzip())
.pipe(tar.extract(dest)
.on('finish', () => {
resolve();
}).on('error', (err) => {
reject(err);
}),
);
});
}
}
module.exports = BinaryManager;