Skip to content

Commit c2022ca

Browse files
committed
[tfjs-node] Support external binaries
Modify the install.js to support external binaries while installing npm package. It reads the `custom-binary.json` configuration file to get URLs for the following resources: - 'tf-lib': libtensorflow and libtensorflow_framework shared libraries. Its value is a URL that points to a `tar.gz` or `zip` and contains the shared libaries and header files. - 'addon': pre-compiled node binding. Its value is a object contains three values: `host`, `remote_path` and `package_name`. These three values are concatenated into a URL that points to the pre-compiled node binding. If you only provide `tf-lib` and no `addon`. It compiles the node binding while installing the npm package. Signed-off-by: Yihong Wang <yh.wang@ibm.com>
1 parent ba5a3a7 commit c2022ca

File tree

2 files changed

+88
-63
lines changed

2 files changed

+88
-63
lines changed

tfjs-node/scripts/get-addon-name.js

Lines changed: 45 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -15,39 +15,61 @@
1515
* =============================================================================
1616
*/
1717
const os = require('os');
18+
const fs = require('fs');
19+
const join = require('path').join;
1820
const name = require('../package.json').name;
1921
const version = require('../package.json').version;
2022

2123
const platform = os.platform();
24+
const CUSTOM_BINARY_FILENAME = 'custom-binary.json';
25+
const customBinaries = loadCustomBinary();
2226

23-
const CPU_DARWIN = `CPU-darwin-${version}.tar.gz`;
24-
const CPU_LINUX = `CPU-linux-${version}.tar.gz`;
25-
const GPU_LINUX = `GPU-linux-${version}.tar.gz`;
26-
const CPU_WINDOWS = `CPU-windows-${version}.zip`;
27-
const GPU_WINDOWS = `GPU-windows-${version}.zip`;
28-
29-
let addonName;
27+
/** Map the os.arch() to arch string in a file name */
28+
const ARCH_MAPPING = { 'x64': 'x86_64' };
29+
/** Map the os.platform() to the platform value in a file name */
30+
const PLATFORM_MAPPING = {
31+
'darwin': 'darwin',
32+
'linux': 'linux',
33+
'win32': 'windows'
34+
};
35+
/** The extension of a compressed file */
36+
const PLATFORM_EXTENSION = os.platform() === 'win32' ? 'zip' : 'tar.gz';
37+
/**
38+
* Current supported type, platform and architecture combinations
39+
* `tf-lib` represents tensorflow shared libraries and `binding` represents
40+
* node binding.
41+
*/
42+
const ALL_SUPPORTED_COMBINATION = [
43+
'cpu-darwin-x86_64',
44+
'gpu-linux-x86_64',
45+
'cpu-linux-x86_64',
46+
'cpu-windows-x86_64',
47+
'gpu-windows-x86_64'
48+
];
3049

31-
if (name.includes('gpu')) {
32-
if (platform === 'linux') {
33-
addonName = GPU_LINUX;
34-
} else if (platform === 'win32') {
35-
addonName = GPU_WINDOWS;
36-
}
37-
} else {
38-
if (platform === 'linux') {
39-
addonName = CPU_LINUX;
40-
} else if (platform === 'darwin') {
41-
addonName = CPU_DARWIN;
42-
} else if (platform === 'win32') {
43-
addonName = CPU_WINDOWS;
44-
}
45-
}
50+
const type = name.includes('gpu')? 'GPU': 'CPU';
51+
const addonName = `${type}-${PLATFORM_MAPPING[platform]}-` +
52+
`${version}.${PLATFORM_EXTENSION}`;
4653

4754
// Print out the addon tarball name so that it can be used in bash script when
4855
// uploading the tarball to GCP bucket.
4956
console.log(addonName);
5057

58+
function loadCustomBinary() {
59+
const cfg = join(__dirname, CUSTOM_BINARY_FILENAME);
60+
return fs.existsSync(cfg) ? require(cfg) : {};
61+
}
62+
63+
function getCustomBinary(name) {
64+
return customBinaries[name];
65+
}
66+
5167
module.exports = {
52-
addonName: addonName
68+
addonName: addonName,
69+
customTFLibUri: customBinaries['tf-lib'],
70+
customAddon: customBinaries['addon'],
71+
ARCH_MAPPING,
72+
PLATFORM_MAPPING,
73+
PLATFORM_EXTENSION,
74+
ALL_SUPPORTED_COMBINATION
5375
};

tfjs-node/scripts/install.js

Lines changed: 43 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -30,23 +30,32 @@ const {
3030
modulePath
3131
} = require('./deps-constants.js');
3232
const resources = require('./resources');
33-
const {addonName} = require('./get-addon-name.js');
33+
const {
34+
addonName,
35+
customTFLibUri,
36+
customAddon,
37+
PLATFORM_MAPPING,
38+
ARCH_MAPPING,
39+
PLATFORM_EXTENSION,
40+
ALL_SUPPORTED_COMBINATION,
41+
} = require('./get-addon-name.js');
3442

3543
const exists = util.promisify(fs.exists);
3644
const mkdir = util.promisify(fs.mkdir);
3745
const rename = util.promisify(fs.rename);
3846
const rimrafPromise = util.promisify(rimraf);
3947

4048
const BASE_URI =
41-
'https://storage.googleapis.com/tensorflow/libtensorflow/libtensorflow-';
42-
const CPU_DARWIN = `cpu-darwin-x86_64-${LIBTENSORFLOW_VERSION}.tar.gz`;
43-
const CPU_LINUX = `cpu-linux-x86_64-${LIBTENSORFLOW_VERSION}.tar.gz`;
44-
const GPU_LINUX = `gpu-linux-x86_64-${LIBTENSORFLOW_VERSION}.tar.gz`;
45-
const CPU_WINDOWS = `cpu-windows-x86_64-${LIBTENSORFLOW_VERSION}.zip`;
46-
const GPU_WINDOWS = `gpu-windows-x86_64-${LIBTENSORFLOW_VERSION}.zip`;
49+
'https://storage.googleapis.com/tensorflow/libtensorflow/libtensorflow-';
4750

4851
const platform = os.platform();
52+
// Use windows path
53+
if (platform === 'win32') {
54+
path = path.win32;
55+
}
4956
let libType = process.argv[2] === undefined ? 'cpu' : process.argv[2];
57+
const system = `${libType}-${PLATFORM_MAPPING[platform]}-` +
58+
`${ARCH_MAPPING[os.arch()]}`;
5059
let forceDownload = process.argv[3] === undefined ? undefined : process.argv[3];
5160

5261
let packageJsonFile;
@@ -56,14 +65,20 @@ async function setPackageJsonFile() {
5665
JSON.parse(fs.readFileSync(`${__dirname}/../package.json`).toString());
5766
}
5867

59-
async function updateAddonName() {
60-
packageJsonFile['binary']['package_name'] = addonName;
68+
function updateAddonName() {
69+
const origBinary = JSON.parse(JSON.stringify(packageJsonFile['binary']));
70+
if (customAddon !== undefined) {
71+
Object.assign(packageJsonFile['binary'], customAddon);
72+
} else {
73+
packageJsonFile['binary']['package_name'] = addonName;
74+
}
6175
const stringFile = JSON.stringify(packageJsonFile, null, 2);
6276
fs.writeFileSync((`${__dirname}/../package.json`), stringFile);
77+
return origBinary;
6378
}
6479

65-
async function revertAddonName() {
66-
delete packageJsonFile['binary']['package_name'];
80+
function revertAddonName(orig) {
81+
packageJsonFile['binary'] = orig;
6782
const stringFile = JSON.stringify(packageJsonFile, null, 2).concat('\n');
6883
fs.writeFileSync((`${__dirname}/../package.json`), stringFile);
6984
}
@@ -72,33 +87,16 @@ async function revertAddonName() {
7287
* Returns the libtensorflow hosted path of the current platform.
7388
*/
7489
function getPlatformLibtensorflowUri() {
75-
let targetUri = BASE_URI;
76-
if (platform === 'linux') {
77-
if (os.arch() === 'arm') {
78-
// TODO(kreeger): Handle arm64 as well:
79-
targetUri =
80-
'https://storage.googleapis.com/tf-builds/libtensorflow_r1_14_linux_arm.tar.gz';
81-
} else {
82-
if (libType === 'gpu') {
83-
targetUri += GPU_LINUX;
84-
} else {
85-
targetUri += CPU_LINUX;
86-
}
87-
}
88-
} else if (platform === 'darwin') {
89-
targetUri += CPU_DARWIN;
90-
} else if (platform === 'win32') {
91-
// Use windows path
92-
path = path.win32;
93-
if (libType === 'gpu') {
94-
targetUri += GPU_WINDOWS;
95-
} else {
96-
targetUri += CPU_WINDOWS;
97-
}
98-
} else {
99-
throw new Error(`Unsupported platform: ${platform}`);
90+
91+
if (customTFLibUri !== undefined) {
92+
return customTFLibUri;
93+
}
94+
95+
if (ALL_SUPPORTED_COMBINATION.indexOf(system) === -1) {
96+
throw new Error(`Unsupported system: ${libType}-${platform}-${os.arch()}`);
10097
}
101-
return targetUri;
98+
99+
return `${BASE_URI}${system}-${LIBTENSORFLOW_VERSION}.${PLATFORM_EXTENSION}`;
102100
}
103101

104102
/**
@@ -157,15 +155,19 @@ async function downloadLibtensorflow(callback) {
157155
*/
158156
async function build() {
159157
console.error('* Building TensorFlow Node.js bindings');
160-
cp.exec('node-pre-gyp install --fallback-to-build', (err) => {
158+
let buildOption = '--fallback-to-build';
159+
if (customTFLibUri !== undefined && customAddon === undefined) {
160+
// Has custom tensorflow shared libs but no addon. Then build it from source
161+
buildOption = '--build-from-source';
162+
}
163+
cp.exec(`node-pre-gyp install ${buildOption}`, (err) => {
161164
if (err) {
162165
console.log('node-pre-gyp install failed with error: ' + err);
163166
}
164167
if (platform === 'win32') {
165168
// Move libtensorflow to module path, where tfjs_binding.node locates.
166169
cp.exec('node scripts/deps-stage.js symlink ' + modulePath);
167170
}
168-
revertAddonName();
169171
});
170172
}
171173

@@ -176,7 +178,7 @@ async function run() {
176178
// Load package.json file
177179
setPackageJsonFile();
178180
// Update addon name in package.json file
179-
await updateAddonName();
181+
const origBinary = updateAddonName();
180182
// First check if deps library exists:
181183
if (forceDownload !== 'download' && await exists(depsLibTensorFlowPath)) {
182184
// Library has already been downloaded, then compile and simlink:
@@ -186,6 +188,7 @@ async function run() {
186188
await cleanDeps();
187189
await downloadLibtensorflow(build);
188190
}
191+
revertAddonName(origBinary);
189192
}
190193

191194
run();

0 commit comments

Comments
 (0)