nertc-electron-sdk
Version:
nertc-electron-sdk
413 lines (377 loc) • 14.7 kB
JavaScript
const { execSync } = require('child_process');
const path = require('path');
const requireWithFallback = (name) => {
try {
return require(name);
} catch (err) {
if (process.env.INIT_CWD) {
try {
return require(path.join(process.env.INIT_CWD, 'node_modules', name));
} catch (_) {}
}
throw err;
}
};
const fetch = requireWithFallback('node-fetch');
const tar = requireWithFallback('tar');
const glob = requireWithFallback('glob');
const download = requireWithFallback('download');
const fse = requireWithFallback('fs-extra');
const {Command} = requireWithFallback('commander');
const program = new Command();
const package_json = require(process.cwd() + '/package.json');
if (!package_json.node_pre_build) {
package_json.node_pre_build = {};
}
const name_addon = package_json.node_pre_build['name'] ?
package_json.node_pre_build['name'] :
(package_json.node_pre_build['name-addon'] ? package_json.node_pre_build['name-addon'] : package_json.name);
const name_sdk = package_json.node_pre_build['name'] ?
package_json.node_pre_build['name'] :
(package_json.node_pre_build['name-sdk'] ? package_json.node_pre_build['name-sdk'] : package_json.name);
const sdk_path = path.join(process.cwd(), package_json.node_pre_build['sdk-dir'] ? package_json.node_pre_build['sdk-dir'] : 'sdk');
const temp_path = path.join(process.cwd(), 'temporary');
const sdk_group = package_json.node_pre_build['sdk-group'];
const addon_group = package_json.node_pre_build['addon-group'];
const binary_dir = package_json.node_pre_build['binary-dir'] ? package_json.node_pre_build['binary-dir'] : 'build/Release';
const package_dir = package_json.node_pre_build['package-dir'] ? package_json.node_pre_build['package-dir'] : 'packages';
const platform = process.platform;
if (!sdk_group || !addon_group) {
console.error('[node_pre_build] please specify \'sdk-group\' and \'addon-group\' in field \'node_pre_build\'.');
}
// check if project has electron dependency
const node_modules = requireWithFallback('node_modules-path');
const { arch } = require('os');
let is_electron = false;
let electron_version;
let electron_path = node_modules('electron', process.cwd().split(path.sep));
if (!electron_path && process.env.INIT_CWD) {
electron_path = node_modules('electron', process.env.INIT_CWD.split(path.sep));
}
if (electron_path) {
is_electron = true;
electron_version = require(path.join(electron_path, 'electron', 'package.json')).version;
}
console.log(`[node_pre_build] is_electron: ${is_electron}, electron_version: ${electron_version}`)
function removeNativeSdk() {
if(fse.pathExistsSync(sdk_path)){
fse.rmdirSync(sdk_path, { recursive: true })
console.log(`[node_pre_build] delecte NertcSdk end`)
}
}
function copySDKToBinaryDir() {
const temp = glob.sync('/**/+(*.dll|*.framework|*.xcframework|*.dylib|*.so|*.node)', {
root: sdk_path
})
const files = []
temp.forEach((filepath) => {
console.log('pre copySync file:' + path.basename(filepath))
if(!filepath.includes('dSYM')){
files.push(filepath)
}
});
if (!fse.pathExistsSync(path.join(process.cwd(), binary_dir))) {
fse.mkdirSync(path.join(process.cwd(), binary_dir), {recursive: true});
}
files.forEach((filepath) => {
console.log('after copySync file:' + path.basename(filepath))
fse.copySync(filepath, path.join(process.cwd(), binary_dir, path.basename(filepath)))
});
console.log(`[node_pre_build] copySDKToBinaryDir end`)
}
function build(arch) {
console.log(`build arch:${arch}`)
const silent = false;
const gypPath = path.join(process.cwd(), '/node_modules/node-gyp/bin/node-gyp.js');
const gypExec = `node ${gypPath}`;
const run = (command) => {
execSync(command, { stdio: silent ? 'pipe' : 'inherit', shell: true });
};
run(`${gypExec} clean`);
if(platform === 'darwin') {
const command = [`${gypExec} configure`];
if (arch) {
command.push(`--arch=${arch}`);
}
command.push('-- -f xcode');
run(command.join(' '));
run(`xcodebuild -project ./build/binding.xcodeproj -configuration Release -arch x86_64 -arch arm64`);
} else {
const command = [`${gypExec} configure`];
command.push(`--arch=${arch}`);
run(command.join(' '));
run(`${gypExec} build`);
// shell.exec(`msbuild build/binding.sln /p:Configuration=Release`, {silent});
}
}
// ---- merged from support/5.7.4: smarter publish member selection ----
function getRcFromFilename(filename) {
if (!filename) {
return -1;
}
const match = filename.match(/-rc-(\d+)/i);
if (!match) {
return -1;
}
return Number.parseInt(match[1], 10);
}
function filterPublishMembers(list, options) {
const {
name,
platform,
arch,
} = options;
return (list || []).filter((member) => {
if (!member || !member.filename) {
return false;
}
return member.filename.includes(name) &&
member.filename.includes(platform) &&
member.filename.includes(arch);
});
}
function logPublishDebug(tag, groupName, versionBucket, list, options, selectedMember) {
const candidates = filterPublishMembers(list, options);
console.log(`[node_pre_build][${tag}] group:${groupName}, version_bucket:${versionBucket}, exact_version:${options.exactVersion}, platform:${options.platform}, arch:${options.arch}, require_exact:${options.requireExactVersion}`);
console.log(`[node_pre_build][${tag}] bucket_size:${(list || []).length}, candidate_size:${candidates.length}`);
console.log(`[node_pre_build][${tag}] candidates:${candidates.map((member) => member.filename).join(', ') || 'none'}`);
console.log(`[node_pre_build][${tag}] selected:${selectedMember ? selectedMember.filename : 'none'}`);
}
function pickPublishMember(list, options) {
const {
name,
platform,
arch,
exactVersion,
requireExactVersion,
} = options;
const candidates = filterPublishMembers(list, options);
if (!candidates.length) {
return null;
}
const exactHit = candidates.find((member) => member.filename.includes(exactVersion));
if (exactHit) {
return exactHit;
}
if (requireExactVersion) {
return null;
}
candidates.sort((left, right) => {
const rcDiff = getRcFromFilename(right.filename) - getRcFromFilename(left.filename);
if (rcDiff !== 0) {
return rcDiff;
}
const idDiff = (Number(right.id) || 0) - (Number(left.id) || 0);
if (idDiff !== 0) {
return idDiff;
}
const leftTime = Date.parse((left.updated_at || '').replace(/-/g, '/')) || 0;
const rightTime = Date.parse((right.updated_at || '').replace(/-/g, '/')) || 0;
return rightTime - leftTime;
});
return candidates[0];
}
// ---- end merged section ----
function downloadSDK(name_sdk, arch, publish_json) {
return new Promise((resolve, reject) => {
let sdk_list = [];
const exact_version = package_json.version;
const version_bucket = package_json.version.split('-')[0];
Object.keys(publish_json[sdk_group]).forEach((temp) => {
if (version_bucket === temp) {
sdk_list = publish_json[sdk_group][temp];
};
});
console.log(`[node_pre_build] downloadSDK name_sdk:${name_sdk}, platform:${platform}, arch:${arch}`)
const sdk_member = pickPublishMember(sdk_list, {
name: name_sdk,
platform,
arch,
exactVersion: exact_version,
requireExactVersion: false,
});
logPublishDebug('sdk', sdk_group, version_bucket, sdk_list, {
name: name_sdk,
platform,
arch,
exactVersion: exact_version,
requireExactVersion: false,
}, sdk_member);
const sdk_url = sdk_member ? sdk_member.cdnlink : undefined;
if (!sdk_url) {
return reject(new Error('[node_pre_build] Failed to get download url of the pre-built sdk'));
}
console.log(`[node_pre_build] selected sdk filename:${sdk_member.filename}`)
console.info(`[node_pre_build] Downloading prebuilt sdk from ${sdk_url} to ${sdk_path}`);
download(sdk_url, sdk_path, {
extract: true,
// strip: 1,
filter: (file) => {
return !file.path.includes('._');
},
}).then(() => {
console.info(`[node_pre_build] Downloading prebuilt sdk complete`);
return resolve();
}).catch((err) => {
console.log(`[node_pre_build] downloadSDK error:${err}`)
return reject(err);
});
});
}
function downloadAddon(name_addon, arch, fallBackToBuild, publish_json) {
return new Promise((resolve, reject) => {
let addon_list = [];
const exact_version = package_json.version;
const version_bucket = package_json.version.split('-')[0];
Object.keys(publish_json[addon_group]).forEach((temp) => {
if (version_bucket === temp) {
addon_list = publish_json[addon_group][temp];
};
});
const addon_member = pickPublishMember(addon_list, {
name: name_addon,
platform,
arch,
exactVersion: exact_version,
requireExactVersion: false,
});
logPublishDebug('addon', addon_group, version_bucket, addon_list, {
name: name_addon,
platform,
arch,
exactVersion: exact_version,
requireExactVersion: false,
}, addon_member);
const addon_url = addon_member ? addon_member.cdnlink : undefined;
if (!addon_url) {
if (!fallBackToBuild) {
return reject(new Error('[node_pre_build] Failed to get download url of the pre-built addon.'));
}
console.info('[node_pre_build] Failed to get download url of the pre-built addon, falling back to build.');
build(arch);
return resolve();
}
console.log(`[node_pre_build] selected addon filename:${addon_member.filename}`)
console.info(`[node_pre_build] Downloading prebuilt addon from ${addon_url}`);
download(addon_url, sdk_path, {
extract: true,
filter: (file) => {
return !file.path.includes('._');
},
}).then(() => {
copySDKToBinaryDir();
console.log(`[node_pre_build] downloadAddon copySDKToBinaryDir end`)
// removeNativeSdk();
}).catch((err) => {
console.log(`[node_pre_build] downloadAddon err:${err}`)
if (!fallBackToBuild) {
return reject(err);
}
console.info(`[node_pre_build] Failed to download pre-built addon from ${addon_url}, error ${err}, falling back to build.`);
build(arch);
return resolve();
});
});
}
function install(options) {
let arch = package_json.node_pre_build['arch'];
arch = options.arch ? options.arch : arch;
if (typeof arch === 'undefined') {
arch = process.env.npm_config_arch;
console.log(`install optins npm_config_arch:${arch}`)
if(typeof arch === 'undefined') {
if (platform === 'darwin') {
arch = 'universal'
} else {
arch = process.arch;
}
}
}
console.log(`install optins arch:${arch}`)
// fetch publish list
fetch('https://admin.netease.im/public-service/free/publish/list').then((res) => {return res.json()}).then((json) => {
let res_data = json.data;
return downloadSDK(name_sdk, arch, res_data).then(() => {
return downloadAddon(name_addon, arch, options.fallBackToBuild, res_data);
});
}).catch((err) => {
console.error(err);
});
}
// command-line options
// clean
program
.command('clean')
.description('Clean installed pre-built binary')
.action((options) => {
console.info(`[node_pre_build] removing ${sdk_path}.`);
fse.removeSync(sdk_path);
console.info(`[node_pre_build] removing ${temp_path}.`);
fse.removeSync(temp_path);
});
// install
program
.command('install')
.description('Install pre-built binary for module')
.option('-a, --arch <architecture>', 'architecture of the host machine.')
.option('--fall-back-to-build [build-script]', 'build when download pre-built binary failed.')
.action((options) => {
console.log(`[node_pre_build] start install`)
if (fse.pathExistsSync(sdk_path) && fse.readdirSync(sdk_path).length > 0) {
console.info(`[node_pre_build] sdk already installed in ${sdk_path}.`);
return;
}
install(options);
});
// reinstall
program
.command('reinstall')
.description('Reinstall pre-built binary for module')
.option('-a, --arch <architecture>', 'architecture of the host machine.')
.option('--fall-back-to-build [build-script]', 'build when download pre-built binary failed.')
.action((options) => {
console.info(`[node_pre_build] removing ${sdk_path}.`);
fse.removeSync(sdk_path);
console.info(`[node_pre_build] removing ${temp_path}.`);
fse.removeSync(temp_path);
install(options);
});
// build
program
.command('build')
.description('Build and pack your pre-built binaries.')
.option('-r, --runtime <runtime...>', 'array of runtimes to build for, such as [electron, node, nw].')
.option('-rv, --runtime-version <runtime-version...>', 'array of runtime versions to build for, support multiple versions.')
.option('-a, --arch <arch...>', 'array of architechtures to build for, such as [x64, ia32, arm64, arm].')
.option('-p, --pack', 'pack the binaries after build.')
.action((options) => {
let arch_array = options.arch ? options.arch : package_json.node_pre_build['arch'];
if (!Array.isArray(arch_array)) {
arch_array = [arch_array];
};
if (!fse.pathExistsSync(process.cwd() + '/' + package_dir)) {
fse.mkdirSync(process.cwd() + '/' + package_dir);
};
console.log(`---platform:${platform} arch:${arch_array[0]}-----`)
build(arch_array[0]);
if (!options.pack) {
return;
};
let package_name = `${process.cwd() + '/' + package_dir}/${name_addon}-v${package_json.version}-${platform}-${arch_array[0]}.tar.gz`
console.log(`package_name:${package_name}`)
tar.create({
gzip: true,
sync: true,
cwd: process.cwd() + '/' + binary_dir,
file: package_name,
filter: (path, stat) => {
if (path.match(/\.pdb|\.node|\.so/) !== null) {
console.info(`[node_pre_build] ${path} packed.`);
return true;
}
},
}, fse.readdirSync(process.cwd() + '/' + binary_dir));
});
// parse
program.parse();