onnxruntime/js/node/script/build.ts

131 lines
4 KiB
TypeScript
Raw Normal View History

// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
import { spawnSync } from 'child_process';
import * as fs from 'fs-extra';
import minimist from 'minimist';
import * as os from 'os';
import * as path from 'path';
// command line flags
const buildArgs = minimist(process.argv.slice(2));
// --config=Debug|Release|RelWithDebInfo
const CONFIG: 'Debug' | 'Release' | 'RelWithDebInfo' =
buildArgs.config || (os.platform() === 'win32' ? 'RelWithDebInfo' : 'Release');
if (CONFIG !== 'Debug' && CONFIG !== 'Release' && CONFIG !== 'RelWithDebInfo') {
throw new Error(`unrecognized config: ${CONFIG}`);
}
// --arch=x64|ia32|arm64|arm
const ARCH: 'x64' | 'ia32' | 'arm64' | 'arm' = buildArgs.arch || os.arch();
if (ARCH !== 'x64' && ARCH !== 'ia32' && ARCH !== 'arm64' && ARCH !== 'arm') {
throw new Error(`unrecognized architecture: ${ARCH}`);
}
// --onnxruntime-build-dir=
const ONNXRUNTIME_BUILD_DIR = buildArgs['onnxruntime-build-dir'];
// --onnxruntime-generator=
const ONNXRUNTIME_GENERATOR = buildArgs['onnxruntime-generator'];
// --rebuild
const REBUILD = !!buildArgs.rebuild;
// --use_dml
const USE_DML = !!buildArgs.use_dml;
// --use_webgpu
const USE_WEBGPU = !!buildArgs.use_webgpu;
// --use_cuda
const USE_CUDA = !!buildArgs.use_cuda;
// --use_tensorrt
const USE_TENSORRT = !!buildArgs.use_tensorrt;
// --use_coreml
const USE_COREML = !!buildArgs.use_coreml;
// --use_qnn
const USE_QNN = !!buildArgs.use_qnn;
Fix delay load for WebGPU EP and DML EP (#23111) ### Description This change fixes the DLL delay load problem for the WebGPU EP and DirectML EP. See detailed explanation below. ### Problem When onnxruntime.dll uses delay loading for its dependencies, the dependencies are loaded using `LoadLibraryEx()`, which search the directory of process (.exe) instead of this library (onnxruntime.dll). This is a problem for usages of Node.js binding and python binding, because Windows will try to find the dependencies in the directory of node.exe or python.exe, which is not the directory of onnxruntime.dll. There was previous attempt to fix this by loading DirectML.dll in the initialization of onnxruntime nodejs binding, which works for DML EP but is not a good solution because it does not really "delay" the load. For WebGPU, the situation became worse because webgpu_dawn.dll depends on dxil.dll and dxcompiler.dll, which are explicitly dynamically loaded in the code using `LoadLibraryA()`. This has the same problem of the DLL search. ### Solutions For onnxruntime.dll loading its direct dependencies, it can be resolved by set the [`__pfnDliNotifyHook2` hook](https://learn.microsoft.com/en-us/cpp/build/reference/understanding-the-helper-function?view=msvc-170#structure-and-constant-definitions) to load from an absolute path that constructed from the onnxruntime.dll folder and the DLL name. For webgpu_dawn.dll loading dxil.dll and dxcompiler.dll, since they are explicitly loaded in the code, the hook does not work. Instead, it can be resolved by ~~using WIN32 API `SetDllDirectory()` to add the onnxruntime.dll folder to the search path.~~ preloading the 2 DLLs from the onnxruntime.dll folder .
2024-12-19 18:23:48 +00:00
// --dll_deps=
const DLL_DEPS = buildArgs.dll_deps;
// build path
const ROOT_FOLDER = path.join(__dirname, '..');
const BIN_FOLDER = path.join(ROOT_FOLDER, 'bin');
const BUILD_FOLDER = path.join(ROOT_FOLDER, 'build');
// if rebuild, clean up the dist folders
if (REBUILD) {
fs.removeSync(BIN_FOLDER);
fs.removeSync(BUILD_FOLDER);
}
const args = [
'cmake-js',
REBUILD ? 'reconfigure' : 'configure',
`--arch=${ARCH}`,
nodejs: Release Ort Env before main function returns (#17288) ### Description Release OrtEnv before main function returns. Before this change, OrtEnv is deleted when C/C++ runtime destructs all global variables in ONNX Runtime's core framework. The callstack is like this: ``` * frame #0: 0x00007fffee39f5a6 libonnxruntime.so.1.16.0`onnxruntime::Environment::~Environment(this=0x00007fffee39fbf2) at environment.h:20:7 frame #1: 0x00007fffee39f614 libonnxruntime.so.1.16.0`std::default_delete<onnxruntime::Environment>::operator()(this=0x00007ffff4c30e50, __ptr=0x0000000005404b00) const at unique_ptr.h:85:2 frame #2: 0x00007fffee39edca libonnxruntime.so.1.16.0`std::unique_ptr<onnxruntime::Environment, std::default_delete<onnxruntime::Environment>>::~unique_ptr(this=0x5404b00) at unique_ptr.h:361:17 frame #3: 0x00007fffee39e2ab libonnxruntime.so.1.16.0`OrtEnv::~OrtEnv(this=0x00007ffff4c30e50) at ort_env.cc:43:1 frame #4: 0x00007fffee39fa96 libonnxruntime.so.1.16.0`std::default_delete<OrtEnv>::operator()(this=0x00007fffefff8f78, __ptr=0x00007ffff4c30e50) const at unique_ptr.h:85:2 frame #5: 0x00007fffee39f394 libonnxruntime.so.1.16.0`std::unique_ptr<OrtEnv, std::default_delete<OrtEnv>>::~unique_ptr(this=0x7ffff4c30e50) at unique_ptr.h:361:17 frame #6: 0x00007ffff78574b5 libc.so.6`__run_exit_handlers + 261 frame #7: 0x00007ffff7857630 libc.so.6`exit + 32 frame #8: 0x00007ffff783feb7 libc.so.6`__libc_start_call_main + 135 frame #9: 0x00007ffff783ff60 libc.so.6`__libc_start_main@@GLIBC_2.34 + 128 frame #10: 0x0000000000abbdee node`_start + 46 ``` After this change, OrtEnv will be deleted before the main function returns and nodejs is still alive.
2023-08-25 06:07:02 +00:00
'--CDnapi_build_version=6',
`--CDCMAKE_BUILD_TYPE=${CONFIG}`,
];
if (ONNXRUNTIME_BUILD_DIR && typeof ONNXRUNTIME_BUILD_DIR === 'string') {
args.push(`--CDONNXRUNTIME_BUILD_DIR=${ONNXRUNTIME_BUILD_DIR}`);
}
if (ONNXRUNTIME_GENERATOR && typeof ONNXRUNTIME_GENERATOR === 'string') {
args.push(`--CDONNXRUNTIME_GENERATOR=${ONNXRUNTIME_GENERATOR}`);
}
if (USE_DML) {
args.push('--CDUSE_DML=ON');
}
if (USE_WEBGPU) {
args.push('--CDUSE_WEBGPU=ON');
}
if (USE_CUDA) {
args.push('--CDUSE_CUDA=ON');
}
if (USE_TENSORRT) {
args.push('--CDUSE_TENSORRT=ON');
}
if (USE_COREML) {
args.push('--CDUSE_COREML=ON');
}
if (USE_QNN) {
args.push('--CDUSE_QNN=ON');
}
Fix delay load for WebGPU EP and DML EP (#23111) ### Description This change fixes the DLL delay load problem for the WebGPU EP and DirectML EP. See detailed explanation below. ### Problem When onnxruntime.dll uses delay loading for its dependencies, the dependencies are loaded using `LoadLibraryEx()`, which search the directory of process (.exe) instead of this library (onnxruntime.dll). This is a problem for usages of Node.js binding and python binding, because Windows will try to find the dependencies in the directory of node.exe or python.exe, which is not the directory of onnxruntime.dll. There was previous attempt to fix this by loading DirectML.dll in the initialization of onnxruntime nodejs binding, which works for DML EP but is not a good solution because it does not really "delay" the load. For WebGPU, the situation became worse because webgpu_dawn.dll depends on dxil.dll and dxcompiler.dll, which are explicitly dynamically loaded in the code using `LoadLibraryA()`. This has the same problem of the DLL search. ### Solutions For onnxruntime.dll loading its direct dependencies, it can be resolved by set the [`__pfnDliNotifyHook2` hook](https://learn.microsoft.com/en-us/cpp/build/reference/understanding-the-helper-function?view=msvc-170#structure-and-constant-definitions) to load from an absolute path that constructed from the onnxruntime.dll folder and the DLL name. For webgpu_dawn.dll loading dxil.dll and dxcompiler.dll, since they are explicitly loaded in the code, the hook does not work. Instead, it can be resolved by ~~using WIN32 API `SetDllDirectory()` to add the onnxruntime.dll folder to the search path.~~ preloading the 2 DLLs from the onnxruntime.dll folder .
2024-12-19 18:23:48 +00:00
if (DLL_DEPS) {
args.push(`--CDORT_NODEJS_DLL_DEPS=${DLL_DEPS}`);
}
// set CMAKE_OSX_ARCHITECTURES for macOS build
if (os.platform() === 'darwin') {
if (ARCH === 'x64') {
args.push('--CDCMAKE_OSX_ARCHITECTURES=x86_64');
} else if (ARCH === 'arm64') {
args.push('--CDCMAKE_OSX_ARCHITECTURES=arm64');
} else {
throw new Error(`architecture not supported for macOS build: ${ARCH}`);
}
}
// In Windows, "npx cmake-js configure" uses a powershell script to detect the Visual Studio installation.
// The script uses the environment variable LIB. If an invalid path is specified in LIB, the script will fail.
// So we override the LIB environment variable to remove invalid paths.
const envOverride =
os.platform() === 'win32' && process.env.LIB
? { ...process.env, LIB: process.env.LIB.split(';').filter(fs.existsSync).join(';') }
: process.env;
// launch cmake-js configure
const procCmakejs = spawnSync('npx', args, { shell: true, stdio: 'inherit', cwd: ROOT_FOLDER, env: envOverride });
if (procCmakejs.status !== 0) {
if (procCmakejs.error) {
console.error(procCmakejs.error);
}
process.exit(procCmakejs.status === null ? undefined : procCmakejs.status);
}
// launch cmake to build
const procCmake = spawnSync('cmake', ['--build', '.', '--config', CONFIG], {
shell: true,
stdio: 'inherit',
cwd: BUILD_FOLDER,
});
if (procCmake.status !== 0) {
if (procCmake.error) {
console.error(procCmake.error);
}
process.exit(procCmake.status === null ? undefined : procCmake.status);
}