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 = { withOpenCV: findArg("with-opencv", 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}`); await fetch(url).then(res => { console.log(res.status) }) 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() { //OpenCV if (buildOptions.withOpenCV) cmakeBuildFromSource("OpenCV", "https://github.com/opencv/opencv.git", "4.11.0", null, [ "-DBUILD_SHARED_LIBS=OFF", "-DBUILD_opencv_apps=OFF", "-DBUILD_opencv_js=OFF", "-DBUILD_opencv_python2=OFF", "-DBUILD_opencv_python3=OFF", "-DBUILD_ANDROID_PROJECTS=OFF", "-DBUILD_ANDROID_EXAMPLES=OFF", "-DBUILD_TESTS=OFF", "-DBUILD_FAT_JAVA_LIB=OFF", "-DBUILD_ANDROID_SERVICE=OFF", "-DBUILD_JAVA=OFF", "-DBUILD_PERF_TESTS=OFF" ], (root) => [ `set(OpenCV_STATIC ON)`, os.platform() == "win32" ? `include(${JSON.stringify(P(path.join(root, "OpenCVConfig.cmake")))})` : `include(${JSON.stringify(P(path.join(root, "lib/cmake/opencv4/OpenCVConfig.cmake")))})`, `set(OpenCV_INCLUDE_DIR \${OpenCV_INCLUDE_DIRS})`, os.platform() == "win32" ? "set(OpenCV_LIB_DIR ${OpenCV_LIB_PATH})" : `set(OpenCV_LIB_DIR ${JSON.stringify(P(path.join(root, "lib")))})`, // `set(OpenCV_LIBS OpenCV_LIBS)`, ].join("\n")); } main();