UNPKG

@tensorflow/tfjs-node

Version:

This repository provides native TensorFlow execution in backend JavaScript applications under the Node.js runtime, accelerated by the TensorFlow C binary under the hood. It provides the same API as [TensorFlow.js](https://js.tensorflow.org/api/latest/).

217 lines (195 loc) 6.94 kB
/** * @license * Copyright 2019 Google LLC. All Rights Reserved. * 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 * * http://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 fs = require('fs'); let path = require('path'); const rimraf = require('rimraf'); const util = require('util'); const cp = require('child_process'); const os = require('os'); const { depsPath, depsLibPath, depsLibTensorFlowPath, LIBTENSORFLOW_VERSION, PLATFORM_MAPPING, ARCH_MAPPING, PLATFORM_EXTENSION, ALL_SUPPORTED_COMBINATION, modulePath, customTFLibUri, customAddon } = require('./deps-constants.js'); const resources = require('./resources'); const {addonName} = require('./get-addon-name.js'); const exists = util.promisify(fs.exists); const mkdir = util.promisify(fs.mkdir); const rename = util.promisify(fs.rename); const rimrafPromise = util.promisify(rimraf); const CDN_STORAGE = process.env.TFJS_NODE_CDN_STORAGE || process.env.npm_config_TFJS_NODE_CDN_STORAGE || process.env.CDN_STORAGE; const BASE_HOST = CDN_STORAGE || 'https://storage.googleapis.com/'; const BASE_URI = process.env.TFJS_NODE_BASE_URI || process.env.npm_config_TFJS_NODE_BASE_URI || `${BASE_HOST}tensorflow/libtensorflow/libtensorflow-`; const platform = os.platform(); // Use windows path if (platform === 'win32') { path = path.win32; } let libType = process.argv[2] === undefined ? 'cpu' : process.argv[2]; let system = `${libType}-${PLATFORM_MAPPING[platform]}-` + `${ARCH_MAPPING[os.arch()]}`; let forceDownload = process.argv[3] === undefined ? undefined : process.argv[3]; let packageJsonFile; function setPackageJsonFile() { packageJsonFile = JSON.parse(fs.readFileSync(`${__dirname}/../package.json`).toString()); } function updateAddonName() { if (customAddon !== undefined) { Object.assign(packageJsonFile['binary'], customAddon); } else { packageJsonFile['binary']['package_name'] = addonName; } const stringFile = JSON.stringify(packageJsonFile, null, 2); fs.writeFileSync((`${__dirname}/../package.json`), stringFile); } function revertAddonName(orig) { packageJsonFile['binary'] = orig; const stringFile = JSON.stringify(packageJsonFile, null, 2).concat('\n'); fs.writeFileSync((`${__dirname}/../package.json`), stringFile); } /** * Returns the libtensorflow hosted path of the current platform. */ function getPlatformLibtensorflowUri() { // Exception for mac+gpu user if (platform === 'darwin') { if (os.arch() === 'arm64') { return `${BASE_HOST}tf-builds/libtensorflow_r2_7_darwin_arm64_cpu.tar.gz`; } system = `cpu-${PLATFORM_MAPPING[platform]}-${ARCH_MAPPING[os.arch()]}`; } if (customTFLibUri !== undefined) { return customTFLibUri; } if (platform === 'linux') { if (os.arch() === 'arm') { return `${BASE_HOST}tf-builds/libtensorflow_r2_5_linux_arm7l.tar.gz`; } else if (os.arch() === 'arm64') { return `${BASE_HOST}tf-builds/libtensorflow_r2_7_linux_arm64.tar.gz`; } } if (ALL_SUPPORTED_COMBINATION.indexOf(system) === -1) { throw new Error(`Unsupported system: ${libType}-${platform}-${os.arch()}`); } return `${BASE_URI}${system}-${LIBTENSORFLOW_VERSION}.${PLATFORM_EXTENSION}`; } /** * Ensures a directory exists, creates as needed. */ async function ensureDir(dirPath) { if (!await exists(dirPath)) { await mkdir(dirPath); } } /** * Deletes the deps directory if it exists, and creates a fresh deps folder. */ async function cleanDeps() { if (await exists(depsPath)) { await rimrafPromise(depsPath); } await mkdir(depsPath); } /** * Downloads libtensorflow and notifies via a callback when unpacked. */ async function downloadLibtensorflow(callback) { // Ensure dependencies staged directory is available: await ensureDir(depsPath); console.warn('* Downloading libtensorflow'); console.log(getPlatformLibtensorflowUri()); resources.downloadAndUnpackResource( getPlatformLibtensorflowUri(), depsPath, async () => { if (platform === 'win32') { // Some windows libtensorflow zip files are missing structure and the // eager headers. Check, restructure, and download resources as // needed. if (!await exists(depsLibTensorFlowPath)) { // Verify that tensorflow.dll exists const libtensorflowDll = path.join(depsPath, 'tensorflow.dll'); if (!await exists(libtensorflowDll)) { throw new Error('Could not find libtensorflow.dll'); } await ensureDir(depsLibPath); await rename(libtensorflowDll, depsLibTensorFlowPath); } } // No other work is required on other platforms. if (callback !== undefined) { callback(); } }); } /** * Calls node-gyp for Node.js Tensorflow binding after lib is downloaded. */ async function build() { // Load package.json file setPackageJsonFile(); // Update addon name in package.json file const origBinary = JSON.parse(JSON.stringify(packageJsonFile['binary'])); updateAddonName(); console.error('* Building TensorFlow Node.js bindings'); let buildOption = '--fallback-to-build'; if (customTFLibUri !== undefined && customAddon === undefined) { // Has custom tensorflow shared libs but no addon. Then build it from source buildOption = '--build-from-source'; } cp.exec(`node-pre-gyp install ${buildOption}`, (err) => { if (err) { console.log('node-pre-gyp install failed with error: ' + err); process.exit(1); } if (platform === 'win32') { // Move libtensorflow to module path, where tfjs_binding.node locates. cp.exec('node scripts/deps-stage.js symlink ' + modulePath, (error) => { if (error) { console.error('symlink ' + modulePath + ' failed: ', error); process.exit(1); } }); } revertAddonName(origBinary); }); } /** * Ensures libtensorflow requirements are met for building the binding. */ async function run() { // First check if deps library exists: if (forceDownload !== 'download' && await exists(depsLibTensorFlowPath)) { // Library has already been downloaded, then compile and simlink: await build(); } else { // Library has not been downloaded, download, then compile and symlink: await cleanDeps(); await downloadLibtensorflow(build); } } run();