UNPKG

koffi

Version:

Fast and easy-to-use dynamic C FFI (foreign function interface) for Node.js

817 lines (778 loc) 29.9 kB
#!/usr/bin/env -S node --no-warnings var __create = Object.create; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __getProtoOf = Object.getPrototypeOf; var __hasOwnProp = Object.prototype.hasOwnProperty; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( // If the importer is in node compatibility mode or this is not an ESM // file that has been converted to a CommonJS file using a Babel- // compatible transform (i.e. "__esModule" has not been set), then set // "default" to the CommonJS "module.exports" for node compatibility. isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, mod )); // src/cnoke/cnoke.js var import_node_fs4 = __toESM(require("node:fs"), 1); // src/cnoke/src/builder.js var import_node_fs3 = __toESM(require("node:fs"), 1); var import_node_os = __toESM(require("node:os"), 1); var import_node_path = __toESM(require("node:path"), 1); var import_node_child_process = require("node:child_process"); // src/cnoke/src/abi.js var import_node_fs = __toESM(require("node:fs"), 1); function determineAbi() { let abi = process.arch.toString(); if (abi == "riscv32" || abi == "riscv64") { let buf = readFileHeader(process.execPath, 512); let header = decodeElfHeader(buf); let float_abi = header.e_flags & 6; switch (float_abi) { case 0: { } break; case 2: { abi += "f"; } break; case 4: { abi += "d"; } break; case 6: { abi += "q"; } break; } } else if (abi == "arm") { let buf = readFileHeader(process.execPath, 512); let header = decodeElfHeader(buf); if (header.e_flags & 1024) { abi += "hf"; } else if (header.e_flags & 512) { abi += "sf"; } else { throw new Error("Unknown ARM floating-point ABI"); } } return abi; } function readFileHeader(filename, read) { let fd = null; try { let fd2 = import_node_fs.default.openSync(filename); let buf = Buffer.allocUnsafe(read); let len = import_node_fs.default.readSync(fd2, buf); return buf.subarray(0, len); } finally { if (fd != null) import_node_fs.default.closeSync(fd); } } function decodeElfHeader(buf) { let header = {}; if (buf.length < 16) throw new Error("Truncated header"); if (buf[0] != 127 || buf[1] != 69 || buf[2] != 76 || buf[3] != 70) throw new Error("Invalid magic number"); if (buf[6] != 1) throw new Error("Invalid ELF version"); if (buf[5] != 1) throw new Error("Big-endian architectures are not supported"); header.e_machine = buf.readUInt16LE(18); switch (buf[4]) { case 1: { buf = buf.subarray(0, 68); if (buf.length < 68) throw new Error("Truncated ELF header"); header.ei_class = 32; header.e_flags = buf.readUInt32LE(36); } break; case 2: { buf = buf.subarray(0, 120); if (buf.length < 120) throw new Error("Truncated ELF header"); header.ei_class = 64; header.e_flags = buf.readUInt32LE(48); } break; default: throw new Error("Invalid ELF class"); } return header; } // src/cnoke/src/util.js var import_node_fs2 = __toESM(require("node:fs"), 1); function pathIsAbsolute(path2) { if (process.platform == "win32" && path2.match(/^[a-zA-Z]:/)) path2 = path2.substr(2); return isPathSeparator(path2[0]); } function isPathSeparator(c) { if (c == "/") return true; if (process.platform == "win32" && c == "\\") return true; return false; } function syncFiles(src_dir, dest_dir) { let keep = /* @__PURE__ */ new Set(); { let entries = import_node_fs2.default.readdirSync(src_dir, { withFileTypes: true }); for (let entry of entries) { if (!entry.isFile()) continue; keep.add(entry.name); import_node_fs2.default.copyFileSync(src_dir + `/${entry.name}`, dest_dir + `/${entry.name}`); } } { let entries = import_node_fs2.default.readdirSync(dest_dir, { withFileTypes: true }); for (let entry of entries) { if (!entry.isFile()) continue; if (keep.has(entry.name)) continue; import_node_fs2.default.unlinkSync(dest_dir + `/${entry.name}`); } } } function unlinkRecursive(path2) { try { import_node_fs2.default.rmSync(path2, { recursive: true, maxRetries: process.platform == "win32" ? 3 : 0 }); } catch (err) { if (err.code !== "ENOENT") throw err; } } function getNapiVersion(napi, major) { if (napi > 8) return null; const support = { 6: ["6.14.2", "6.14.2", "6.14.2"], 8: ["8.6.0", "8.10.0", "8.11.2"], 9: ["9.0.0", "9.3.0", "9.11.0"], 10: ["10.0.0", "10.0.0", "10.0.0", "10.16.0", "10.17.0", "10.20.0", "10.23.0"], 11: ["11.0.0", "11.0.0", "11.0.0", "11.8.0"], 12: ["12.0.0", "12.0.0", "12.0.0", "12.0.0", "12.11.0", "12.17.0", "12.19.0", "12.22.0"], 13: ["13.0.0", "13.0.0", "13.0.0", "13.0.0", "13.0.0"], 14: ["14.0.0", "14.0.0", "14.0.0", "14.0.0", "14.0.0", "14.0.0", "14.12.0", "14.17.0"], 15: ["15.0.0", "15.0.0", "15.0.0", "15.0.0", "15.0.0", "15.0.0", "15.0.0", "15.12.0"] }; const max = Math.max(...Object.keys(support).map((k) => parseInt(k, 10))); if (major > max) return major + ".0.0"; if (support[major] == null) return null; let required = support[major][napi - 1] || null; return required; } function compareVersions(ver1, ver2) { ver1 = String(ver1).replace(/-.*$/, "").split(".").reduce((acc, v, idx) => acc + parseInt(v, 10) * Math.pow(10, 2 * (5 - idx)), 0); ver2 = String(ver2).replace(/-.*$/, "").split(".").reduce((acc, v, idx) => acc + parseInt(v, 10) * Math.pow(10, 2 * (5 - idx)), 0); let cmp = Math.min(Math.max(ver1 - ver2, -1), 1); return cmp; } // src/cnoke/src/assets.js var FIND_CNOKE_CMAKE = `# SPDX-License-Identifier: MIT # SPDX-FileCopyrightText: 2026 Niels Martign\xE8ne <niels.martignene@protonmail.com> if(CMAKE_BUILD_TYPE STREQUAL "Release" OR CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo" OR CMAKE_BUILD_TYPE STREQUAL "MinSizeRel") set(USE_UNITY_BUILDS ON CACHE BOOL "Use single-TU builds (aka. Unity builds)") else() set(USE_UNITY_BUILDS OFF CACHE BOOL "Use single-TU builds (aka. Unity builds)") endif() function(add_node_addon) cmake_parse_arguments(ARG "" "NAME" "SOURCES" \${ARGN}) add_library(\${ARG_NAME} SHARED \${ARG_SOURCES} \${NODE_JS_SOURCES}) target_link_node(\${ARG_NAME}) set_target_properties(\${ARG_NAME} PROPERTIES PREFIX "" SUFFIX ".node") endfunction() function(target_link_node TARGET) target_include_directories(\${TARGET} PRIVATE \${NODE_JS_INCLUDE_DIRS}) if(NODE_JS_LINK_DEF) target_sources(\${TARGET} PRIVATE node.lib) endif() if(NODE_JS_LINK_LIB) target_link_libraries(\${TARGET} PRIVATE \${NODE_JS_LINK_LIB}) endif() target_compile_options(\${TARGET} PRIVATE \${NODE_JS_COMPILE_FLAGS}) if(NODE_JS_LINK_FLAGS) target_link_options(\${TARGET} PRIVATE \${NODE_JS_LINK_FLAGS}) endif() endfunction() if(WIN32) function(create_import_lib OUTPUT SRC) if (MSVC) add_custom_command(OUTPUT \${OUTPUT} COMMAND \${CMAKE_AR} \${CMAKE_STATIC_LINKER_FLAGS} /def:\${SRC} /out:"\${CMAKE_CURRENT_BINARY_DIR}/\${OUTPUT}" WORKING_DIRECTORY \${CMAKE_CURRENT_SOURCE_DIR} MAIN_DEPENDENCY \${SRC}) elseif(DEFINED NODE_JS_DLLTOOL_MACHINE) add_custom_command(OUTPUT \${OUTPUT} COMMAND \${CMAKE_DLLTOOL} -d \${SRC} -l "\${CMAKE_CURRENT_BINARY_DIR}/\${OUTPUT}" -m \${NODE_JS_DLLTOOL_MACHINE} WORKING_DIRECTORY \${CMAKE_CURRENT_SOURCE_DIR} MAIN_DEPENDENCY \${SRC}) else() add_custom_command(OUTPUT \${OUTPUT} COMMAND \${CMAKE_DLLTOOL} -d \${SRC} -l "\${CMAKE_CURRENT_BINARY_DIR}/\${OUTPUT}" WORKING_DIRECTORY \${CMAKE_CURRENT_SOURCE_DIR} MAIN_DEPENDENCY \${SRC}) endif() endfunction() endif() if(NODE_JS_LINK_DEF) create_import_lib(node.lib \${NODE_JS_LINK_DEF}) set(NODE_JS_LINK_LIB "\${CMAKE_CURRENT_BINARY_DIR}/node.lib") endif() if(USE_UNITY_BUILDS) function(enable_unity_build TARGET) cmake_parse_arguments(ARG "" "" "EXCLUDE" \${ARGN}) get_target_property(sources \${TARGET} SOURCES) string(GENEX_STRIP "\${sources}" sources) set(unity_file_c "\${CMAKE_CURRENT_BINARY_DIR}/\${TARGET}_unity.c") set(unity_file_cpp "\${CMAKE_CURRENT_BINARY_DIR}/\${TARGET}_unity.cpp") file(REMOVE \${unity_file_c} \${unity_file_cpp}) set(c_definitions "") set(cpp_definitions "") foreach(src \${sources}) if (src IN_LIST ARG_EXCLUDE) continue() endif() get_source_file_property(language \${src} LANGUAGE) get_property(definitions SOURCE \${src} PROPERTY COMPILE_DEFINITIONS) if(IS_ABSOLUTE \${src}) set(src_full \${src}) else() set(src_full "\${CMAKE_CURRENT_SOURCE_DIR}/\${src}") endif() if(language STREQUAL "C") set_source_files_properties(\${src} PROPERTIES HEADER_FILE_ONLY 1) file(APPEND \${unity_file_c} "#include \\"\${src_full}\\"\\n") if (definitions) set(c_definitions "\${c_definitions} \${definitions}") endif() elseif(language STREQUAL "CXX") set_source_files_properties(\${src} PROPERTIES HEADER_FILE_ONLY 1) file(APPEND \${unity_file_cpp} "#include \\"\${src_full}\\"\\n") if (definitions) set(cpp_definitions "\${cpp_definitions} \${definitions}") endif() endif() endforeach() if(EXISTS \${unity_file_c}) target_sources(\${TARGET} PRIVATE \${unity_file_c}) if(c_definitions) set_source_files_properties(\${unity_file_c} PROPERTIES COMPILE_DEFINITIONS \${c_definitions}) endif() endif() if(EXISTS \${unity_file_cpp}) target_sources(\${TARGET} PRIVATE \${unity_file_cpp}) if(cpp_definitions) set_source_files_properties(\${unity_file_cpp} PROPERTIES COMPILE_DEFINITIONS \${cpp_definitions}) endif() endif() target_compile_definitions(\${TARGET} PRIVATE -DUNITY_BUILD=1) endfunction() else() function(enable_unity_build TARGET) endfunction() endif() if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang" OR CMAKE_CXX_COMPILER_ID STREQUAL "GNU") if(CMAKE_SYSTEM_PROCESSOR MATCHES "(amd64|x86_64)") foreach(lang C CXX) set(CMAKE_\${lang}_FLAGS_RELEASE "\${CMAKE_\${lang}_FLAGS_RELEASE} -mpopcnt -msse4.1 -msse4.2 -mssse3 -mcx16") set(CMAKE_\${lang}_FLAGS_RELWITHDEBINFO "\${CMAKE_\${lang}_FLAGS_RELWITHDEBINFO} -mpopcnt -msse4.1 -msse4.2 -mssse3 -mcx16") endforeach() endif() endif() `; var WIN_DELAY_HOOK_C = `// SPDX-License-Identifier: MIT // SPDX-FileCopyrightText: 2026 Niels Martign\xE8ne <niels.martignene@protonmail.com> #include <stdlib.h> #if !defined(NOMINMAX) #define NOMINMAX #endif #if !defined(WIN32_LEAN_AND_MEAN) #define WIN32_LEAN_AND_MEAN #endif #include <windows.h> #include <delayimp.h> static HMODULE node_dll; static FARPROC WINAPI self_exe_hook(unsigned int event, DelayLoadInfo *info) { if (event == dliStartProcessing) { node_dll = GetModuleHandleA("node.dll"); if (!node_dll) { node_dll = GetModuleHandle(NULL); } return NULL; } if (event == dliNotePreLoadLibrary && !stricmp(info->szDll, "node.exe")) return (FARPROC)node_dll; return NULL; } #if defined(__MINGW32__) PfnDliHook __pfnDliNotifyHook2 = self_exe_hook; #else const PfnDliHook __pfnDliNotifyHook2 = self_exe_hook; #endif `; // src/cnoke/src/builder.js var DefaultOptions = { mode: "RelWithDebInfo" }; function Builder(config = {}) { let self = this; let host = `${process.platform}_${determineAbi()}`; let project_dir = config.project_dir; let package_dir = config.package_dir; if (project_dir == null) project_dir = process.cwd(); project_dir = project_dir.replace(/\\/g, "/"); if (package_dir == null) package_dir = findParentDirectory(project_dir, "package.json"); if (package_dir != null) package_dir = package_dir.replace(/\\/g, "/"); let runtime_version = config.runtime_version; let toolchain = config.toolchain || null; let prefer_clang = config.prefer_clang || false; let mode = config.mode || DefaultOptions.mode; let targets = config.targets || []; let verbose = config.verbose || false; let prebuild = config.prebuild || false; let defines = config.defines || []; if (runtime_version == null) runtime_version = process.version; if (runtime_version.startsWith("v")) runtime_version = runtime_version.substr(1); let options = null; let cache_dir = getCacheDirectory(); let build_dir = config.build_dir; let work_dir = null; let output_dir = null; if (build_dir == null) { let options2 = readCNokeOptions(); if (options2.output != null) { build_dir = expandPath(options2.output, options2.directory); } else { build_dir = project_dir + "/build"; } } build_dir = build_dir.replace(/\\/g, "/"); work_dir = build_dir + `/v${runtime_version}_${toolchain ?? "native"}/${mode}`; output_dir = work_dir + "/Output"; let cmake_bin = null; this.configure = async function(retry = true) { let options2 = readCNokeOptions(); let args = [project_dir]; checkCMake(); checkCompatibility(); console.log(`>> Node: ${runtime_version}`); console.log(`>> Toolchain: ${toolchain ?? "native"}`); import_node_fs3.default.mkdirSync(build_dir, { recursive: true, mode: 493 }); import_node_fs3.default.mkdirSync(work_dir, { recursive: true, mode: 493 }); import_node_fs3.default.mkdirSync(output_dir, { recursive: true, mode: 493 }); retry &= import_node_fs3.default.existsSync(work_dir + "/CMakeCache.txt"); args.push(`-DNODE_JS_EXECPATH=${process.execPath}`); if (options2.api == null) { let downloaded = false; if (!downloaded) throw new Error("Cannot download API headers"); } else { console.log(`>> Using local node-api headers`); let api_dir = expandPath(options2.api, project_dir); args.push(`-DNODE_JS_INCLUDE_DIRS=${api_dir}/include`); } import_node_fs3.default.writeFileSync(work_dir + "/FindCNoke.cmake", FIND_CNOKE_CMAKE); args.push(`-DCMAKE_MODULE_PATH=${work_dir}`); let win32 = (toolchain ?? host).startsWith("win32_"); let mingw = process.platform == "win32" && process.env.MSYSTEM != null; if (win32) { if (mingw) { args.push(`-DNODE_JS_LINK_LIB=node.dll`); } else { if (options2.api == null) { let downloaded = false; if (!downloaded) throw new Error("Cannot download Node import library"); } else { let api_dir = expandPath(options2.api, project_dir); args.push(`-DNODE_JS_LINK_DEF=${api_dir}/def/node_api.def`); } } import_node_fs3.default.writeFileSync(work_dir + "/win_delay_hook.c", WIN_DELAY_HOOK_C); args.push(`-DNODE_JS_SOURCES=${work_dir}/win_delay_hook.c`); } if (process.platform != "win32" || mingw) { if ((0, import_node_child_process.spawnSync)("ninja", ["--version"]).status === 0) { args.push("-G", "Ninja"); } else if (process.platform == "win32") { args.push("-G", "MinGW Makefiles"); } if (config.ccache && (0, import_node_child_process.spawnSync)("ccache", ["--version"]).status === 0) { args.push("-DCMAKE_C_COMPILER_LAUNCHER=ccache"); args.push("-DCMAKE_CXX_COMPILER_LAUNCHER=ccache"); } } if (prefer_clang) { if (process.platform == "win32" && !mingw) { args.push("-T", "ClangCL"); } else { args.push("-DCMAKE_C_COMPILER=clang"); args.push("-DCMAKE_CXX_COMPILER=clang++"); } } args.push(`-DCMAKE_BUILD_TYPE=${mode}`); for (let type of ["ARCHIVE", "RUNTIME", "LIBRARY"]) { for (let suffix of ["", "_DEBUG", "_RELEASE", "_RELWITHDEBINFO"]) args.push(`-DCMAKE_${type}_OUTPUT_DIRECTORY${suffix}=${output_dir}`); } for (let define of defines) args.push(`-D${define}`); args.push("--no-warn-unused-cli"); console.log(">> Running configuration"); let proc = (0, import_node_child_process.spawnSync)(cmake_bin, args, { cwd: work_dir, stdio: "inherit" }); if (proc.status !== 0) { unlinkRecursive(work_dir); if (retry) return self.configure(false); throw new Error("Failed to run configure step"); } }; this.build = async function() { checkCompatibility(); if (prebuild) { let valid = await checkPrebuild(); if (valid) { return; } else { console.error("Failed to load prebuilt binary, rebuilding from source"); } } checkCMake(); if (!import_node_fs3.default.existsSync(work_dir + "/CMakeCache.txt")) await self.configure(); if (process.env.MAKEFLAGS == null) process.env.MAKEFLAGS = "-j" + (import_node_os.default.cpus().length || 1); let args = [ "--build", work_dir, "--config", mode ]; if (verbose) args.push("--verbose"); for (let target of targets) args.push("--target", target); console.log(">> Running build"); let proc = (0, import_node_child_process.spawnSync)(cmake_bin, args, { stdio: "inherit" }); if (proc.status !== 0) throw new Error("Failed to run build step"); console.log(">> Copy target files"); syncFiles(output_dir, build_dir); }; async function checkPrebuild() { let proc = (0, import_node_child_process.spawnSync)(process.execPath, ["-e", "require(process.argv[1])", package_dir]); return proc.status === 0; } this.clean = function() { unlinkRecursive(build_dir); }; function findParentDirectory(dirname, basename) { if (process.platform == "win32") dirname = dirname.replace(/\\/g, "/"); do { if (import_node_fs3.default.existsSync(dirname + "/" + basename)) return dirname; dirname = import_node_path.default.dirname(dirname); } while (!dirname.endsWith("/")); return null; } function getCacheDirectory() { if (process.platform == "win32") { let cache_dir2 = process.env["LOCALAPPDATA"] || process.env["APPDATA"]; if (cache_dir2 == null) throw new Error("Missing LOCALAPPDATA and APPDATA environment variable"); cache_dir2 = import_node_path.default.join(cache_dir2, "cnoke"); return cache_dir2; } else { let cache_dir2 = process.env["XDG_CACHE_HOME"]; if (cache_dir2 == null) { let home = process.env["HOME"]; if (home == null) throw new Error("Missing HOME environment variable"); cache_dir2 = import_node_path.default.join(home, ".cache"); } cache_dir2 = import_node_path.default.join(cache_dir2, "cnoke"); return cache_dir2; } } function checkCMake() { if (cmake_bin != null) return; if (!import_node_fs3.default.existsSync(project_dir + "/CMakeLists.txt")) throw new Error("This directory does not appear to have a CMakeLists.txt file"); { let proc = (0, import_node_child_process.spawnSync)("cmake", ["--version"]); if (proc.status === 0) { cmake_bin = "cmake"; } else { if (process.platform == "win32") { let proc2 = (0, import_node_child_process.spawnSync)("reg", ["query", "HKEY_LOCAL_MACHINE\\SOFTWARE\\Kitware\\CMake", "/v", "InstallDir"]); if (proc2.status === 0) { let matches = proc2.stdout.toString("utf-8").match(/InstallDir[ \t]+REG_[A-Z_]+[ \t]+(.*)+/); if (matches != null) { let bin = import_node_path.default.join(matches[1].trim(), "bin\\cmake.exe"); if (import_node_fs3.default.existsSync(bin)) cmake_bin = bin; } } } if (cmake_bin == null) throw new Error("CMake does not seem to be available"); } } console.log(`>> Using CMake binary: ${cmake_bin}`); } function checkCompatibility() { let options2 = readCNokeOptions(); if (options2.node != null && compareVersions(runtime_version, options2.node) < 0) throw new Error(`Project ${options2.name} requires Node.js >= ${options2.node}`); if (options2.napi != null) { let major = parseInt(runtime_version, 10); let required = getNapiVersion(options2.napi, major); if (required == null) throw new Error(`Project ${options2.name} does not support the Node ${major}.x branch (old or missing Node-API)`); if (compareVersions(runtime_version, required) < 0) throw new Error(`Project ${options2.name} requires Node >= ${required} in the Node ${major}.x branch (with Node-API >= ${options2.napi})`); } } function readCNokeOptions() { if (options != null) return options; let directory = project_dir; let pkg = null; let cnoke = null; if (package_dir != null) { try { let json = import_node_fs3.default.readFileSync(package_dir + "/package.json", { encoding: "utf-8" }); pkg = JSON.parse(json); directory = package_dir; } catch (err) { if (err.code != "ENOENT") throw err; } } if (cnoke == null) cnoke = pkg?.cnoke ?? {}; options = { name: pkg?.name ?? import_node_path.default.basename(project_dir), version: pkg?.version ?? null, directory, ...cnoke }; return options; } function expandString(str, values = {}) { let expanded = str.replace(/{{ *([a-zA-Z_][a-zA-Z_0-9]*) *}}/g, (match, p1) => { switch (p1) { case "version": { let options2 = readCNokeOptions(); return options2.version || ""; } break; case "toolchain": return toolchain ?? host; default: { if (Object.hasOwn(values, p1)) { return values[p1]; } else { return match; } } break; } }); return expanded; } function expandPath(str, root) { let expanded = expandString(str); if (!pathIsAbsolute(expanded)) expanded = import_node_path.default.join(root, expanded); expanded = import_node_path.default.normalize(expanded); return expanded; } } // src/cnoke/cnoke.js var VALID_COMMANDS = ["build", "configure", "clean"]; main(); async function main() { let config = {}; let command = "build"; { let i = 2; if (process.argv.length >= 3 && process.argv[2][0] != "-") { let cmd = process.argv[2]; if (VALID_COMMANDS.includes(cmd)) { command = cmd; i++; } } for (; i < process.argv.length; i++) { let arg = process.argv[i]; let value = null; if (arg[0] == "-") { if (arg.length > 2 && arg[1] != "-") { value = arg.substr(2); arg = arg.substr(0, 2); } else if (arg[1] == "-") { let offset = arg.indexOf("="); if (offset > 2 && arg.length > offset + 1) { value = arg.substr(offset + 1); arg = arg.substr(0, offset); } } if (value == null && process.argv[i + 1] != null && process.argv[i + 1][0] != "-") { value = process.argv[i + 1]; i++; } } if (arg == "--help") { printUsage(); return; } else if (arg == "-D" || arg == "--directory") { if (value == null) throw new Error(`Missing value for ${arg}`); config.project_dir = import_node_fs4.default.realpathSync(value); } else if (arg == "-P" || arg == "--package") { if (value == null) throw new Error(`Missing value for ${arg}`); config.package_dir = import_node_fs4.default.realpathSync(value); } else if (arg == "-O" || arg == "--output") { if (value == null) throw new Error(`Missing value for ${arg}`); config.output_directory = value; } else if ((command == "build" || command == "configure") && arg == "--runtime") { if (value == null) throw new Error(`Missing value for ${arg}`); if (!value.match(/^[0-9]+\.[0-9]+\.[0-9]+$/)) throw new Error(`Malformed runtime version '${value}'`); config.runtime_version = value; } else if ((command == "build" || command == "configure") && arg == "--clang") { config.prefer_clang = true; } else if ((command == "build" || command == "configure") && (arg == "-t" || arg == "--toolchain")) { if (value == null) throw new Error(`Missing value for ${arg}`); config.toolchain = value; } else if (arg == "-c" || arg == "--config") { if (value == null) throw new Error(`Missing value for ${arg}`); switch (value.toLowerCase()) { case "relwithdebinfo": { config.mode = "RelWithDebInfo"; } break; case "debug": { config.mode = "Debug"; } break; case "release": { config.mode = "Release"; } break; default: { throw new Error(`Unknown value '${value}' for ${arg}`); } break; } } else if (arg == "--debug") { config.mode = "Debug"; } else if (arg == "--release") { config.mode = "Release"; } else if ((command == "build" || command == "configure") && arg == "--ccache") { config.ccache = true; } else if ((command == "build" || command == "configure") && (arg == "-d" || arg == "--define")) { if (value == null) throw new Error(`Missing value for ${arg}`); config.defines ??= []; config.defines.push(value); } else if (command == "build" && arg == "--prebuild") { config.prebuild = true; } else if (command == "build" && arg == "--target") { if (value == null) throw new Error(`Missing value for ${arg}`); config.targets = [value]; } else if (command == "build" && (arg == "-v" || arg == "--verbose")) { config.verbose = true; } else { if (arg[0] == "-") { throw new Error(`Unexpected argument '${arg}'`); } else { throw new Error(`Unexpected value '${arg}'`); } } } } try { let builder = new Builder(config); await builder[command](); } catch (err) { console.error(err); process.exit(1); } } function printUsage() { let help = `Usage: cnoke [command] [options...] Commands: configure Configure CMake build build Build project (configure if needed) clean Clean build files Options: -D, --directory <DIR> Change source directory (default: current working directory) -P, --package <DIR> Change package directory (default: current working directory) -O, --output <DIR> Set explicit output directory (default: ./build) -c, --config <CONFIG> Change build type: RelWithDebInfo, Debug, Release (default: ${DefaultOptions.mode}) --debug Shortcut for --config Debug --release Shortcut for --config Release --ccache Use ccache if available -t, --toolchain <TOOLCHAIN> Cross-compile for specific platform --runtime <VERSION> Change node version (default: ${process.version}) --clang Use Clang instead of default CMake compiler --prebuild Use prebuilt binary if available --target <TARGET> Only build the specified target -v, --verbose Show build commands while building The ARCH value is similar to process.arch, with the following differences: - arm is changed to arm32hf or arm32sf depending on the floating-point ABI used (hard-float, soft-float) - riscv32 is changed to riscv32sf, riscv32hf32, riscv32hf64 or riscv32hf128 depending on the floating-point ABI - riscv64 is changed to riscv64sf, riscv64hf32, riscv64hf64 or riscv64hf128 depending on the floating-point ABI`; console.log(help); }