Files
ai-box/thirdpart/install.js

234 lines
8.5 KiB
JavaScript

const fs = require("fs");
const os = require("os");
const path = require("path");
const { spawnSync } = require("child_process");
const args = process.argv.slice(2);
const THIRDPARTY_DIR = __dirname;
function findArg(argname, isBoolean) {
let prefix = `--${argname}`;
let idx = args.findIndex(arg => arg == prefix);
if (idx >= 0) {
if (isBoolean) return true;
else return args[idx + 1];
}
idx = args.findIndex(arg => arg.startsWith(prefix))
if (idx >= 0) return args[idx].substring(prefix.length + 1);
return null;
}
function assert(cond, message) {
if (!cond) {
console.error(message);
process.exit(1);
}
}
const buildOptions = {
withMNN: findArg("with-mnn", true) ?? false,
withONNX: findArg("with-onnx", true) ?? false,
buildType: findArg("build-type", false) ?? "Release",
proxy: findArg("proxy"),
generator: findArg("generator"),
}
const spawnOption = {
stdio: "inherit", env: {
...process.env, ...buildOptions.proxy ? {
"http_proxy": buildOptions.proxy,
"https_proxy": buildOptions.proxy,
} : {}
}
};
function P(path) { return path.replace(/\\/g, "/"); }
function checkFile(...items) {
return fs.existsSync(path.resolve(...items));
}
function downloadSource(name, repo, branch, recursive, checkedFile) {
const sourceRootDir = path.join(THIRDPARTY_DIR, "_source");
fs.mkdirSync(sourceRootDir, { recursive: true });
const sourceDir = path.join(sourceRootDir, name);
if (checkFile(sourceDir, checkedFile)) return sourceDir;
console.log(`${name}源代码不存在,正在下载...`);
assert(spawnSync("git", [
"clone",
...recursive ? ["--recursive"] : [],
repo, "-b", branch,
sourceDir
], { ...spawnOption }).status == 0, `下载${name}源代码失败`);
return sourceDir;
}
function cmakeBuildFromSource(name, repo, branch, downloader, buildArgs, cmakeCreator) {
const sourceDir = downloader ? downloader(name, repo, branch) : downloadSource(name, repo, branch, false, "CMakeLists.txt");
const buildDir = path.join(sourceDir, "build", buildOptions.buildType);
const installDir = path.join(THIRDPARTY_DIR, name, buildOptions.buildType);
const configFile = path.join(installDir, "config.cmake");
if (typeof buildArgs == "function") buildArgs = buildArgs(sourceDir, buildDir, installDir);
//编译源代码
if (checkFile(configFile)) return
console.log(`${name}目标不存在,正在编译...`);
fs.rmSync(buildDir, { recursive: true, force: true });
fs.mkdirSync(buildDir, { recursive: true });
assert(spawnSync("cmake", [
"-G", buildOptions.generator ?? "Ninja", sourceDir,
"-DCMAKE_BUILD_TYPE=" + buildOptions.buildType,
"-DCMAKE_INSTALL_PREFIX=" + installDir,
...(buildArgs ?? []).map(arg => arg.startsWith("-D") ? arg : `-D${arg}`),
], { ...spawnOption, cwd: buildDir }).status == 0, `配置${name}失败`);
assert(spawnSync("cmake", [
"--build", ".",
"--parallel", os.cpus().length.toString(),
"--config", buildOptions.buildType,
"--target", "install",
], { ...spawnOption, cwd: buildDir }).status == 0, `编译${name}失败`);
fs.writeFileSync(configFile, cmakeCreator(installDir));
}
async function downloadFromURL(name, url, resolver) {
const cacheDir = path.join(THIRDPARTY_DIR, "_cache");
fs.mkdirSync(cacheDir, { recursive: true });
const saveName = path.join(cacheDir, path.basename(url));
const outputDir = path.join(cacheDir, name);
fs.rmSync(outputDir, { force: true, recursive: true });
if (!checkFile(saveName)) {
console.log(`开始下载${name}, 地址:${url}`);
const result = spawnSync("curl", ["-o", saveName + ".cache", "-L", url, "-s", "-w", "%{http_code}"], { ...spawnOption, stdio: "pipe" });
assert(result.status == 0 && result.stdout.toString() == "200", `下载${name}失败`);
fs.renameSync(saveName + ".cache", saveName);
}
let currentName = saveName;
if (saveName.endsWith(".bz2")) {
currentName = saveName.substring(0, saveName.length - 4);
const input = fs.createReadStream(saveName);
const out = fs.createWriteStream(currentName);
const bz2 = require("unbzip2-stream")();
await new Promise((resolve, reject) => {
input.pipe(bz2).pipe(out);
out.on("close", resolve);
input.on("error", reject);
bz2.on("error", reject);
out.on("error", reject);
});
}
const format = path.extname(currentName);
assert([".tar", ".gz", ".tgz", ".zip"].includes(format), "仅支持.tar, .gz, .tgz, .zip格式的压缩文件");
const method = {
".tar": require("compressing").tar,
".gz": require("compressing").gzip,
".tgz": require("compressing").tgz,
".zip": require("compressing").zip,
}[format];
await method.uncompress(currentName, outputDir);
await resolver(outputDir);
fs.rmSync(outputDir, { recursive: true, force: true });
}
async function main() {
//MNN
if (buildOptions.withMNN) cmakeBuildFromSource("MNN", "https://github.com/alibaba/MNN.git", "3.0.4", null, [
"-DMNN_BUILD_SHARED_LIBS=OFF",
"-DMNN_AVX512=ON",
"-DMNN_BUILD_TOOLS=ON",
"-DMNN_BUILD_CONVERTER=OFF",
"-DMNN_WIN_RUNTIME_MT=ON",
], (root) => [
`set(MNN_INCLUDE_DIR ${JSON.stringify(P(path.join(root, "include")))})`,
`set(MNN_LIB_DIR ${JSON.stringify(P(path.join(root, "lib")))})`,
`if(WIN32)`,
` set(MNN_LIBS \${MNN_LIB_DIR}/MNN.lib)`,
`else()`,
` set(MNN_LIBS \${MNN_LIB_DIR}/libMNN.a)`,
`endif()`,
].join("\n"));
//ONNXRuntime
if (buildOptions.withONNX && !checkFile(THIRDPARTY_DIR, "ONNXRuntime/config.cmake")) {
let url = "";
switch (os.platform()) {
case "win32": {
switch (os.arch()) {
case "x64":
url = "https://huggingface.co/csukuangfj/onnxruntime-libs/resolve/main/onnxruntime-win-x64-static_lib-MinSizeRel-1.20.1.tar.bz2";
break;
case "arm64":
url = "https://huggingface.co/csukuangfj/onnxruntime-libs/resolve/main/onnxruntime-win-arm64-static_lib-1.20.1.tar.bz2";
break;
default: assert(false, `不支持的架构${os.arch()}`);
}
break;
}
case "linux": {
switch (os.arch()) {
case "x64":
url = "https://huggingface.co/csukuangfj/onnxruntime-libs/resolve/main/onnxruntime-linux-x64-static_lib-1.20.1-glibc2_17.zip";
break;
case "arm64":
url = "https://huggingface.co/csukuangfj/onnxruntime-libs/resolve/main/onnxruntime-linux-aarch64-static_lib-1.20.1-glibc2_17.zip";
break;
default: assert(false, `不支持的架构${os.arch()}`);
}
break;
}
case "darwin": {
switch (os.arch()) {
case "x64":
url = "https://huggingface.co/csukuangfj/onnxruntime-libs/resolve/main/onnxruntime-osx-x86_64-static_lib-1.20.1.zip";
break;
case "arm64":
url = "https://huggingface.co/csukuangfj/onnxruntime-libs/resolve/main/onnxruntime-osx-arm64-static_lib-1.20.1.zip";
break;
default: assert(false, `不支持的架构${os.arch()}`);
}
break;
}
default: assert(false, `不支持的平台${os.platform()}`);
}
await downloadFromURL("ONNXRuntime", url, (savedir) => {
const dirname = fs.readdirSync(savedir)[0];
fs.cpSync(path.join(savedir, dirname), path.join(THIRDPARTY_DIR, "ONNXRuntime"), { recursive: true });
});
fs.writeFileSync(path.join(THIRDPARTY_DIR, "ONNXRuntime/config.cmake"), [
`set(ONNXRuntime_INCLUDE_DIR ${JSON.stringify(P(path.join(THIRDPARTY_DIR, "ONNXRuntime/include")))})`,
`set(ONNXRuntime_LIB_DIR ${JSON.stringify(P(path.join(THIRDPARTY_DIR, "ONNXRuntime/lib")))})`,
`if(WIN32)`,
` set(ONNXRuntime_LIBS \${ONNXRuntime_LIB_DIR}/onnxruntime.lib)`,
`else()`,
` set(ONNXRuntime_LIBS \${ONNXRuntime_LIB_DIR}/libonnxruntime.a)`,
`endif()`,
].join("\n"));
}
// if (buildOptions.withONNX) cmakeBuildFromSource("ONNXRuntime", "https://github.com/csukuangfj/onnxruntime-build.git", "main", (name, repo, branch) => {
// const sourceDir = downloadSource(name, repo, branch, false, "README.md")
// if (!checkFile(sourceDir, "onnxruntime/README.md")) {
// fs.rmSync(path.join(sourceDir, "onnxruntime"), { recursive: true, force: true });
// downloadSource("ONNXRuntime/onnxruntime", "https://github.com/microsoft/onnxruntime.git", "v1.20.2", true, "README.md")
// }
// return sourceDir;
// }, (sourceDir, buildDir, installDir) => [
// `-DONNXRUNTIME_SOURCE_DIR=${path.join(sourceDir, "onnxruntime")}`
// ], (root) => [
// `set(OnnxRuntime_INCLUDE_DIR ${JSON.stringify(path.join(root, "include"))})`,
// `set(OnnxRuntime_LIB_DIR ${JSON.stringify(path.join(root, "lib"))})`,
// `set(OnnxRuntime_LIBS onnxruntime)`,
// ].join("\n"))
}
main();