From a36d34ab4b20e9bee88e0c4432c392077b5c2dc5 Mon Sep 17 00:00:00 2001 From: Nie Zhihe Date: Mon, 9 Feb 2026 00:23:41 +0800 Subject: [PATCH 01/16] fix: rename test to avoid hitting windows long path limit --- tests/ten_runtime/integration/nodejs/BUILD.gn | 2 +- .../BUILD.gn | 100 ------------------ .../start_graph_and_set_dests_nodejs/BUILD.gn | 100 ++++++++++++++++++ .../__init__.py | 0 .../client/client.cc | 0 .../manifest.json | 0 .../property.json | 0 .../extension/test_extension_1/manifest.json | 0 .../extension/test_extension_1/package.json | 0 .../extension/test_extension_1/property.json | 0 .../extension/test_extension_1/src/index.ts | 0 .../extension/test_extension_1/tsconfig.json | 0 .../extension/test_extension_2/manifest.json | 0 .../extension/test_extension_2/package.json | 0 .../extension/test_extension_2/property.json | 0 .../extension/test_extension_2/src/index.ts | 0 .../extension/test_extension_2/tsconfig.json | 0 .../extension/test_extension_3/manifest.json | 0 .../extension/test_extension_3/package.json | 0 .../extension/test_extension_3/property.json | 0 .../extension/test_extension_3/src/index.ts | 0 .../extension/test_extension_3/tsconfig.json | 0 .../extension/test_extension_4/manifest.json | 0 .../extension/test_extension_4/package.json | 0 .../extension/test_extension_4/property.json | 0 .../extension/test_extension_4/src/index.ts | 0 .../extension/test_extension_4/tsconfig.json | 0 .../test_case.py | 20 ++-- 28 files changed, 111 insertions(+), 111 deletions(-) delete mode 100644 tests/ten_runtime/integration/nodejs/start_graph_and_set_dests_from_new_graph_nodejs/BUILD.gn create mode 100644 tests/ten_runtime/integration/nodejs/start_graph_and_set_dests_nodejs/BUILD.gn rename tests/ten_runtime/integration/nodejs/{start_graph_and_set_dests_from_new_graph_nodejs => start_graph_and_set_dests_nodejs}/__init__.py (100%) rename tests/ten_runtime/integration/nodejs/{start_graph_and_set_dests_from_new_graph_nodejs => start_graph_and_set_dests_nodejs}/client/client.cc (100%) rename tests/ten_runtime/integration/nodejs/{start_graph_and_set_dests_from_new_graph_nodejs/start_graph_and_set_dests_from_new_graph_nodejs_app => start_graph_and_set_dests_nodejs/start_graph_and_set_dests_nodejs_app}/manifest.json (100%) rename tests/ten_runtime/integration/nodejs/{start_graph_and_set_dests_from_new_graph_nodejs/start_graph_and_set_dests_from_new_graph_nodejs_app => start_graph_and_set_dests_nodejs/start_graph_and_set_dests_nodejs_app}/property.json (100%) rename tests/ten_runtime/integration/nodejs/{start_graph_and_set_dests_from_new_graph_nodejs/start_graph_and_set_dests_from_new_graph_nodejs_app => start_graph_and_set_dests_nodejs/start_graph_and_set_dests_nodejs_app}/ten_packages/extension/test_extension_1/manifest.json (100%) rename tests/ten_runtime/integration/nodejs/{start_graph_and_set_dests_from_new_graph_nodejs/start_graph_and_set_dests_from_new_graph_nodejs_app => start_graph_and_set_dests_nodejs/start_graph_and_set_dests_nodejs_app}/ten_packages/extension/test_extension_1/package.json (100%) rename tests/ten_runtime/integration/nodejs/{start_graph_and_set_dests_from_new_graph_nodejs/start_graph_and_set_dests_from_new_graph_nodejs_app => start_graph_and_set_dests_nodejs/start_graph_and_set_dests_nodejs_app}/ten_packages/extension/test_extension_1/property.json (100%) rename tests/ten_runtime/integration/nodejs/{start_graph_and_set_dests_from_new_graph_nodejs/start_graph_and_set_dests_from_new_graph_nodejs_app => start_graph_and_set_dests_nodejs/start_graph_and_set_dests_nodejs_app}/ten_packages/extension/test_extension_1/src/index.ts (100%) rename tests/ten_runtime/integration/nodejs/{start_graph_and_set_dests_from_new_graph_nodejs/start_graph_and_set_dests_from_new_graph_nodejs_app => start_graph_and_set_dests_nodejs/start_graph_and_set_dests_nodejs_app}/ten_packages/extension/test_extension_1/tsconfig.json (100%) rename tests/ten_runtime/integration/nodejs/{start_graph_and_set_dests_from_new_graph_nodejs/start_graph_and_set_dests_from_new_graph_nodejs_app => start_graph_and_set_dests_nodejs/start_graph_and_set_dests_nodejs_app}/ten_packages/extension/test_extension_2/manifest.json (100%) rename tests/ten_runtime/integration/nodejs/{start_graph_and_set_dests_from_new_graph_nodejs/start_graph_and_set_dests_from_new_graph_nodejs_app => start_graph_and_set_dests_nodejs/start_graph_and_set_dests_nodejs_app}/ten_packages/extension/test_extension_2/package.json (100%) rename tests/ten_runtime/integration/nodejs/{start_graph_and_set_dests_from_new_graph_nodejs/start_graph_and_set_dests_from_new_graph_nodejs_app => start_graph_and_set_dests_nodejs/start_graph_and_set_dests_nodejs_app}/ten_packages/extension/test_extension_2/property.json (100%) rename tests/ten_runtime/integration/nodejs/{start_graph_and_set_dests_from_new_graph_nodejs/start_graph_and_set_dests_from_new_graph_nodejs_app => start_graph_and_set_dests_nodejs/start_graph_and_set_dests_nodejs_app}/ten_packages/extension/test_extension_2/src/index.ts (100%) rename tests/ten_runtime/integration/nodejs/{start_graph_and_set_dests_from_new_graph_nodejs/start_graph_and_set_dests_from_new_graph_nodejs_app => start_graph_and_set_dests_nodejs/start_graph_and_set_dests_nodejs_app}/ten_packages/extension/test_extension_2/tsconfig.json (100%) rename tests/ten_runtime/integration/nodejs/{start_graph_and_set_dests_from_new_graph_nodejs/start_graph_and_set_dests_from_new_graph_nodejs_app => start_graph_and_set_dests_nodejs/start_graph_and_set_dests_nodejs_app}/ten_packages/extension/test_extension_3/manifest.json (100%) rename tests/ten_runtime/integration/nodejs/{start_graph_and_set_dests_from_new_graph_nodejs/start_graph_and_set_dests_from_new_graph_nodejs_app => start_graph_and_set_dests_nodejs/start_graph_and_set_dests_nodejs_app}/ten_packages/extension/test_extension_3/package.json (100%) rename tests/ten_runtime/integration/nodejs/{start_graph_and_set_dests_from_new_graph_nodejs/start_graph_and_set_dests_from_new_graph_nodejs_app => start_graph_and_set_dests_nodejs/start_graph_and_set_dests_nodejs_app}/ten_packages/extension/test_extension_3/property.json (100%) rename tests/ten_runtime/integration/nodejs/{start_graph_and_set_dests_from_new_graph_nodejs/start_graph_and_set_dests_from_new_graph_nodejs_app => start_graph_and_set_dests_nodejs/start_graph_and_set_dests_nodejs_app}/ten_packages/extension/test_extension_3/src/index.ts (100%) rename tests/ten_runtime/integration/nodejs/{start_graph_and_set_dests_from_new_graph_nodejs/start_graph_and_set_dests_from_new_graph_nodejs_app => start_graph_and_set_dests_nodejs/start_graph_and_set_dests_nodejs_app}/ten_packages/extension/test_extension_3/tsconfig.json (100%) rename tests/ten_runtime/integration/nodejs/{start_graph_and_set_dests_from_new_graph_nodejs/start_graph_and_set_dests_from_new_graph_nodejs_app => start_graph_and_set_dests_nodejs/start_graph_and_set_dests_nodejs_app}/ten_packages/extension/test_extension_4/manifest.json (100%) rename tests/ten_runtime/integration/nodejs/{start_graph_and_set_dests_from_new_graph_nodejs/start_graph_and_set_dests_from_new_graph_nodejs_app => start_graph_and_set_dests_nodejs/start_graph_and_set_dests_nodejs_app}/ten_packages/extension/test_extension_4/package.json (100%) rename tests/ten_runtime/integration/nodejs/{start_graph_and_set_dests_from_new_graph_nodejs/start_graph_and_set_dests_from_new_graph_nodejs_app => start_graph_and_set_dests_nodejs/start_graph_and_set_dests_nodejs_app}/ten_packages/extension/test_extension_4/property.json (100%) rename tests/ten_runtime/integration/nodejs/{start_graph_and_set_dests_from_new_graph_nodejs/start_graph_and_set_dests_from_new_graph_nodejs_app => start_graph_and_set_dests_nodejs/start_graph_and_set_dests_nodejs_app}/ten_packages/extension/test_extension_4/src/index.ts (100%) rename tests/ten_runtime/integration/nodejs/{start_graph_and_set_dests_from_new_graph_nodejs/start_graph_and_set_dests_from_new_graph_nodejs_app => start_graph_and_set_dests_nodejs/start_graph_and_set_dests_nodejs_app}/ten_packages/extension/test_extension_4/tsconfig.json (100%) rename tests/ten_runtime/integration/nodejs/{start_graph_and_set_dests_from_new_graph_nodejs => start_graph_and_set_dests_nodejs}/test_case.py (79%) diff --git a/tests/ten_runtime/integration/nodejs/BUILD.gn b/tests/ten_runtime/integration/nodejs/BUILD.gn index 42e9686d91..aef6dcc081 100644 --- a/tests/ten_runtime/integration/nodejs/BUILD.gn +++ b/tests/ten_runtime/integration/nodejs/BUILD.gn @@ -24,7 +24,7 @@ group("nodejs") { "standalone_test_nodejs_2", "standalone_test_nodejs_3", "start_graph_and_communicate_nodejs", - "start_graph_and_set_dests_from_new_graph_nodejs", + "start_graph_and_set_dests_nodejs", "trigger_life_cycle_nodejs", "websocket_server_nodejs", ] diff --git a/tests/ten_runtime/integration/nodejs/start_graph_and_set_dests_from_new_graph_nodejs/BUILD.gn b/tests/ten_runtime/integration/nodejs/start_graph_and_set_dests_from_new_graph_nodejs/BUILD.gn deleted file mode 100644 index fe5384162b..0000000000 --- a/tests/ten_runtime/integration/nodejs/start_graph_and_set_dests_from_new_graph_nodejs/BUILD.gn +++ /dev/null @@ -1,100 +0,0 @@ -# -# Copyright © 2025 Agora -# This file is part of TEN Framework, an open source project. -# Licensed under the Apache License, Version 2.0, with certain conditions. -# Refer to the "LICENSE" file in the root directory for more information. -# -import("//build/ten_runtime/feature/test.gni") -import("//build/ten_runtime/ten.gni") - -ten_package_test_prepare_app( - "start_graph_and_set_dests_from_new_graph_nodejs_app") { - src_app = "default_app_nodejs" - src_app_language = "nodejs" - generated_app_src_root_dir_name = - "start_graph_and_set_dests_from_new_graph_nodejs_app" - - replace_paths_after_install_app = [ - "start_graph_and_set_dests_from_new_graph_nodejs_app/manifest.json", - "start_graph_and_set_dests_from_new_graph_nodejs_app/property.json", - "start_graph_and_set_dests_from_new_graph_nodejs_app/ten_packages/extension/test_extension_1/manifest.json", - "start_graph_and_set_dests_from_new_graph_nodejs_app/ten_packages/extension/test_extension_1/property.json", - "start_graph_and_set_dests_from_new_graph_nodejs_app/ten_packages/extension/test_extension_1/src/index.ts", - "start_graph_and_set_dests_from_new_graph_nodejs_app/ten_packages/extension/test_extension_1/package.json", - "start_graph_and_set_dests_from_new_graph_nodejs_app/ten_packages/extension/test_extension_1/tsconfig.json", - "start_graph_and_set_dests_from_new_graph_nodejs_app/ten_packages/extension/test_extension_2/manifest.json", - "start_graph_and_set_dests_from_new_graph_nodejs_app/ten_packages/extension/test_extension_2/property.json", - "start_graph_and_set_dests_from_new_graph_nodejs_app/ten_packages/extension/test_extension_2/src/index.ts", - "start_graph_and_set_dests_from_new_graph_nodejs_app/ten_packages/extension/test_extension_2/package.json", - "start_graph_and_set_dests_from_new_graph_nodejs_app/ten_packages/extension/test_extension_2/tsconfig.json", - "start_graph_and_set_dests_from_new_graph_nodejs_app/ten_packages/extension/test_extension_3/manifest.json", - "start_graph_and_set_dests_from_new_graph_nodejs_app/ten_packages/extension/test_extension_3/property.json", - "start_graph_and_set_dests_from_new_graph_nodejs_app/ten_packages/extension/test_extension_3/src/index.ts", - "start_graph_and_set_dests_from_new_graph_nodejs_app/ten_packages/extension/test_extension_3/package.json", - "start_graph_and_set_dests_from_new_graph_nodejs_app/ten_packages/extension/test_extension_3/tsconfig.json", - "start_graph_and_set_dests_from_new_graph_nodejs_app/ten_packages/extension/test_extension_4/manifest.json", - "start_graph_and_set_dests_from_new_graph_nodejs_app/ten_packages/extension/test_extension_4/property.json", - "start_graph_and_set_dests_from_new_graph_nodejs_app/ten_packages/extension/test_extension_4/src/index.ts", - "start_graph_and_set_dests_from_new_graph_nodejs_app/ten_packages/extension/test_extension_4/package.json", - "start_graph_and_set_dests_from_new_graph_nodejs_app/ten_packages/extension/test_extension_4/tsconfig.json", - ] - - if (ten_enable_ten_manager) { - deps = [ - "//core/src/ten_manager", - "//packages/core_apps/default_app_nodejs:upload_default_app_nodejs_to_server", - "//packages/core_protocols/msgpack:upload_protocol_msgpack_to_server", - ] - } -} - -ten_package_test_prepare_client( - "start_graph_and_set_dests_from_new_graph_nodejs_app_client") { - sources = [ "client/client.cc" ] - include_dirs = [ - "//core/src", - "//core", - "//packages", - "//tests", - ] - deps = [ - "//core/src/ten_runtime", - "//packages/core_protocols/msgpack:msgpack_files", - "//tests/common/client:msgpack_client", - "//third_party/msgpack:msgpackc", - "//third_party/nlohmann_json", - ] -} - -ten_package_test_prepare_auxiliary_resources( - "start_graph_and_set_dests_from_new_graph_nodejs_test_files") { - resources = [ - "__init__.py", - "test_case.py", - ] - - utils_files = exec_script("//.gnfiles/build/scripts/glob_file.py", - [ - "--dir", - rebase_path("//tests/utils/**/*"), - "--dir-base", - rebase_path("//tests/utils"), - "--recursive", - "--only-output-file", - ], - "json") - - foreach(utils_file, utils_files) { - utils_file_rel_path = utils_file.relative_path - resources += - [ "//tests/utils/${utils_file_rel_path}=>utils/${utils_file_rel_path}" ] - } -} - -group("start_graph_and_set_dests_from_new_graph_nodejs") { - deps = [ - ":start_graph_and_set_dests_from_new_graph_nodejs_app", - ":start_graph_and_set_dests_from_new_graph_nodejs_app_client", - ":start_graph_and_set_dests_from_new_graph_nodejs_test_files", - ] -} diff --git a/tests/ten_runtime/integration/nodejs/start_graph_and_set_dests_nodejs/BUILD.gn b/tests/ten_runtime/integration/nodejs/start_graph_and_set_dests_nodejs/BUILD.gn new file mode 100644 index 0000000000..e86d5a0ee2 --- /dev/null +++ b/tests/ten_runtime/integration/nodejs/start_graph_and_set_dests_nodejs/BUILD.gn @@ -0,0 +1,100 @@ +# +# Copyright © 2025 Agora +# This file is part of TEN Framework, an open source project. +# Licensed under the Apache License, Version 2.0, with certain conditions. +# Refer to the "LICENSE" file in the root directory for more information. +# +import("//build/ten_runtime/feature/test.gni") +import("//build/ten_runtime/ten.gni") + +ten_package_test_prepare_app( + "start_graph_and_set_dests_nodejs_app") { + src_app = "default_app_nodejs" + src_app_language = "nodejs" + generated_app_src_root_dir_name = + "start_graph_and_set_dests_nodejs_app" + + replace_paths_after_install_app = [ + "start_graph_and_set_dests_nodejs_app/manifest.json", + "start_graph_and_set_dests_nodejs_app/property.json", + "start_graph_and_set_dests_nodejs_app/ten_packages/extension/test_extension_1/manifest.json", + "start_graph_and_set_dests_nodejs_app/ten_packages/extension/test_extension_1/property.json", + "start_graph_and_set_dests_nodejs_app/ten_packages/extension/test_extension_1/src/index.ts", + "start_graph_and_set_dests_nodejs_app/ten_packages/extension/test_extension_1/package.json", + "start_graph_and_set_dests_nodejs_app/ten_packages/extension/test_extension_1/tsconfig.json", + "start_graph_and_set_dests_nodejs_app/ten_packages/extension/test_extension_2/manifest.json", + "start_graph_and_set_dests_nodejs_app/ten_packages/extension/test_extension_2/property.json", + "start_graph_and_set_dests_nodejs_app/ten_packages/extension/test_extension_2/src/index.ts", + "start_graph_and_set_dests_nodejs_app/ten_packages/extension/test_extension_2/package.json", + "start_graph_and_set_dests_nodejs_app/ten_packages/extension/test_extension_2/tsconfig.json", + "start_graph_and_set_dests_nodejs_app/ten_packages/extension/test_extension_3/manifest.json", + "start_graph_and_set_dests_nodejs_app/ten_packages/extension/test_extension_3/property.json", + "start_graph_and_set_dests_nodejs_app/ten_packages/extension/test_extension_3/src/index.ts", + "start_graph_and_set_dests_nodejs_app/ten_packages/extension/test_extension_3/package.json", + "start_graph_and_set_dests_nodejs_app/ten_packages/extension/test_extension_3/tsconfig.json", + "start_graph_and_set_dests_nodejs_app/ten_packages/extension/test_extension_4/manifest.json", + "start_graph_and_set_dests_nodejs_app/ten_packages/extension/test_extension_4/property.json", + "start_graph_and_set_dests_nodejs_app/ten_packages/extension/test_extension_4/src/index.ts", + "start_graph_and_set_dests_nodejs_app/ten_packages/extension/test_extension_4/package.json", + "start_graph_and_set_dests_nodejs_app/ten_packages/extension/test_extension_4/tsconfig.json", + ] + + if (ten_enable_ten_manager) { + deps = [ + "//core/src/ten_manager", + "//packages/core_apps/default_app_nodejs:upload_default_app_nodejs_to_server", + "//packages/core_protocols/msgpack:upload_protocol_msgpack_to_server", + ] + } +} + +ten_package_test_prepare_client( + "start_graph_and_set_dests_nodejs_app_client") { + sources = [ "client/client.cc" ] + include_dirs = [ + "//core/src", + "//core", + "//packages", + "//tests", + ] + deps = [ + "//core/src/ten_runtime", + "//packages/core_protocols/msgpack:msgpack_files", + "//tests/common/client:msgpack_client", + "//third_party/msgpack:msgpackc", + "//third_party/nlohmann_json", + ] +} + +ten_package_test_prepare_auxiliary_resources( + "start_graph_and_set_dests_nodejs_test_files") { + resources = [ + "__init__.py", + "test_case.py", + ] + + utils_files = exec_script("//.gnfiles/build/scripts/glob_file.py", + [ + "--dir", + rebase_path("//tests/utils/**/*"), + "--dir-base", + rebase_path("//tests/utils"), + "--recursive", + "--only-output-file", + ], + "json") + + foreach(utils_file, utils_files) { + utils_file_rel_path = utils_file.relative_path + resources += + [ "//tests/utils/${utils_file_rel_path}=>utils/${utils_file_rel_path}" ] + } +} + +group("start_graph_and_set_dests_nodejs") { + deps = [ + ":start_graph_and_set_dests_nodejs_app", + ":start_graph_and_set_dests_nodejs_app_client", + ":start_graph_and_set_dests_nodejs_test_files", + ] +} diff --git a/tests/ten_runtime/integration/nodejs/start_graph_and_set_dests_from_new_graph_nodejs/__init__.py b/tests/ten_runtime/integration/nodejs/start_graph_and_set_dests_nodejs/__init__.py similarity index 100% rename from tests/ten_runtime/integration/nodejs/start_graph_and_set_dests_from_new_graph_nodejs/__init__.py rename to tests/ten_runtime/integration/nodejs/start_graph_and_set_dests_nodejs/__init__.py diff --git a/tests/ten_runtime/integration/nodejs/start_graph_and_set_dests_from_new_graph_nodejs/client/client.cc b/tests/ten_runtime/integration/nodejs/start_graph_and_set_dests_nodejs/client/client.cc similarity index 100% rename from tests/ten_runtime/integration/nodejs/start_graph_and_set_dests_from_new_graph_nodejs/client/client.cc rename to tests/ten_runtime/integration/nodejs/start_graph_and_set_dests_nodejs/client/client.cc diff --git a/tests/ten_runtime/integration/nodejs/start_graph_and_set_dests_from_new_graph_nodejs/start_graph_and_set_dests_from_new_graph_nodejs_app/manifest.json b/tests/ten_runtime/integration/nodejs/start_graph_and_set_dests_nodejs/start_graph_and_set_dests_nodejs_app/manifest.json similarity index 100% rename from tests/ten_runtime/integration/nodejs/start_graph_and_set_dests_from_new_graph_nodejs/start_graph_and_set_dests_from_new_graph_nodejs_app/manifest.json rename to tests/ten_runtime/integration/nodejs/start_graph_and_set_dests_nodejs/start_graph_and_set_dests_nodejs_app/manifest.json diff --git a/tests/ten_runtime/integration/nodejs/start_graph_and_set_dests_from_new_graph_nodejs/start_graph_and_set_dests_from_new_graph_nodejs_app/property.json b/tests/ten_runtime/integration/nodejs/start_graph_and_set_dests_nodejs/start_graph_and_set_dests_nodejs_app/property.json similarity index 100% rename from tests/ten_runtime/integration/nodejs/start_graph_and_set_dests_from_new_graph_nodejs/start_graph_and_set_dests_from_new_graph_nodejs_app/property.json rename to tests/ten_runtime/integration/nodejs/start_graph_and_set_dests_nodejs/start_graph_and_set_dests_nodejs_app/property.json diff --git a/tests/ten_runtime/integration/nodejs/start_graph_and_set_dests_from_new_graph_nodejs/start_graph_and_set_dests_from_new_graph_nodejs_app/ten_packages/extension/test_extension_1/manifest.json b/tests/ten_runtime/integration/nodejs/start_graph_and_set_dests_nodejs/start_graph_and_set_dests_nodejs_app/ten_packages/extension/test_extension_1/manifest.json similarity index 100% rename from tests/ten_runtime/integration/nodejs/start_graph_and_set_dests_from_new_graph_nodejs/start_graph_and_set_dests_from_new_graph_nodejs_app/ten_packages/extension/test_extension_1/manifest.json rename to tests/ten_runtime/integration/nodejs/start_graph_and_set_dests_nodejs/start_graph_and_set_dests_nodejs_app/ten_packages/extension/test_extension_1/manifest.json diff --git a/tests/ten_runtime/integration/nodejs/start_graph_and_set_dests_from_new_graph_nodejs/start_graph_and_set_dests_from_new_graph_nodejs_app/ten_packages/extension/test_extension_1/package.json b/tests/ten_runtime/integration/nodejs/start_graph_and_set_dests_nodejs/start_graph_and_set_dests_nodejs_app/ten_packages/extension/test_extension_1/package.json similarity index 100% rename from tests/ten_runtime/integration/nodejs/start_graph_and_set_dests_from_new_graph_nodejs/start_graph_and_set_dests_from_new_graph_nodejs_app/ten_packages/extension/test_extension_1/package.json rename to tests/ten_runtime/integration/nodejs/start_graph_and_set_dests_nodejs/start_graph_and_set_dests_nodejs_app/ten_packages/extension/test_extension_1/package.json diff --git a/tests/ten_runtime/integration/nodejs/start_graph_and_set_dests_from_new_graph_nodejs/start_graph_and_set_dests_from_new_graph_nodejs_app/ten_packages/extension/test_extension_1/property.json b/tests/ten_runtime/integration/nodejs/start_graph_and_set_dests_nodejs/start_graph_and_set_dests_nodejs_app/ten_packages/extension/test_extension_1/property.json similarity index 100% rename from tests/ten_runtime/integration/nodejs/start_graph_and_set_dests_from_new_graph_nodejs/start_graph_and_set_dests_from_new_graph_nodejs_app/ten_packages/extension/test_extension_1/property.json rename to tests/ten_runtime/integration/nodejs/start_graph_and_set_dests_nodejs/start_graph_and_set_dests_nodejs_app/ten_packages/extension/test_extension_1/property.json diff --git a/tests/ten_runtime/integration/nodejs/start_graph_and_set_dests_from_new_graph_nodejs/start_graph_and_set_dests_from_new_graph_nodejs_app/ten_packages/extension/test_extension_1/src/index.ts b/tests/ten_runtime/integration/nodejs/start_graph_and_set_dests_nodejs/start_graph_and_set_dests_nodejs_app/ten_packages/extension/test_extension_1/src/index.ts similarity index 100% rename from tests/ten_runtime/integration/nodejs/start_graph_and_set_dests_from_new_graph_nodejs/start_graph_and_set_dests_from_new_graph_nodejs_app/ten_packages/extension/test_extension_1/src/index.ts rename to tests/ten_runtime/integration/nodejs/start_graph_and_set_dests_nodejs/start_graph_and_set_dests_nodejs_app/ten_packages/extension/test_extension_1/src/index.ts diff --git a/tests/ten_runtime/integration/nodejs/start_graph_and_set_dests_from_new_graph_nodejs/start_graph_and_set_dests_from_new_graph_nodejs_app/ten_packages/extension/test_extension_1/tsconfig.json b/tests/ten_runtime/integration/nodejs/start_graph_and_set_dests_nodejs/start_graph_and_set_dests_nodejs_app/ten_packages/extension/test_extension_1/tsconfig.json similarity index 100% rename from tests/ten_runtime/integration/nodejs/start_graph_and_set_dests_from_new_graph_nodejs/start_graph_and_set_dests_from_new_graph_nodejs_app/ten_packages/extension/test_extension_1/tsconfig.json rename to tests/ten_runtime/integration/nodejs/start_graph_and_set_dests_nodejs/start_graph_and_set_dests_nodejs_app/ten_packages/extension/test_extension_1/tsconfig.json diff --git a/tests/ten_runtime/integration/nodejs/start_graph_and_set_dests_from_new_graph_nodejs/start_graph_and_set_dests_from_new_graph_nodejs_app/ten_packages/extension/test_extension_2/manifest.json b/tests/ten_runtime/integration/nodejs/start_graph_and_set_dests_nodejs/start_graph_and_set_dests_nodejs_app/ten_packages/extension/test_extension_2/manifest.json similarity index 100% rename from tests/ten_runtime/integration/nodejs/start_graph_and_set_dests_from_new_graph_nodejs/start_graph_and_set_dests_from_new_graph_nodejs_app/ten_packages/extension/test_extension_2/manifest.json rename to tests/ten_runtime/integration/nodejs/start_graph_and_set_dests_nodejs/start_graph_and_set_dests_nodejs_app/ten_packages/extension/test_extension_2/manifest.json diff --git a/tests/ten_runtime/integration/nodejs/start_graph_and_set_dests_from_new_graph_nodejs/start_graph_and_set_dests_from_new_graph_nodejs_app/ten_packages/extension/test_extension_2/package.json b/tests/ten_runtime/integration/nodejs/start_graph_and_set_dests_nodejs/start_graph_and_set_dests_nodejs_app/ten_packages/extension/test_extension_2/package.json similarity index 100% rename from tests/ten_runtime/integration/nodejs/start_graph_and_set_dests_from_new_graph_nodejs/start_graph_and_set_dests_from_new_graph_nodejs_app/ten_packages/extension/test_extension_2/package.json rename to tests/ten_runtime/integration/nodejs/start_graph_and_set_dests_nodejs/start_graph_and_set_dests_nodejs_app/ten_packages/extension/test_extension_2/package.json diff --git a/tests/ten_runtime/integration/nodejs/start_graph_and_set_dests_from_new_graph_nodejs/start_graph_and_set_dests_from_new_graph_nodejs_app/ten_packages/extension/test_extension_2/property.json b/tests/ten_runtime/integration/nodejs/start_graph_and_set_dests_nodejs/start_graph_and_set_dests_nodejs_app/ten_packages/extension/test_extension_2/property.json similarity index 100% rename from tests/ten_runtime/integration/nodejs/start_graph_and_set_dests_from_new_graph_nodejs/start_graph_and_set_dests_from_new_graph_nodejs_app/ten_packages/extension/test_extension_2/property.json rename to tests/ten_runtime/integration/nodejs/start_graph_and_set_dests_nodejs/start_graph_and_set_dests_nodejs_app/ten_packages/extension/test_extension_2/property.json diff --git a/tests/ten_runtime/integration/nodejs/start_graph_and_set_dests_from_new_graph_nodejs/start_graph_and_set_dests_from_new_graph_nodejs_app/ten_packages/extension/test_extension_2/src/index.ts b/tests/ten_runtime/integration/nodejs/start_graph_and_set_dests_nodejs/start_graph_and_set_dests_nodejs_app/ten_packages/extension/test_extension_2/src/index.ts similarity index 100% rename from tests/ten_runtime/integration/nodejs/start_graph_and_set_dests_from_new_graph_nodejs/start_graph_and_set_dests_from_new_graph_nodejs_app/ten_packages/extension/test_extension_2/src/index.ts rename to tests/ten_runtime/integration/nodejs/start_graph_and_set_dests_nodejs/start_graph_and_set_dests_nodejs_app/ten_packages/extension/test_extension_2/src/index.ts diff --git a/tests/ten_runtime/integration/nodejs/start_graph_and_set_dests_from_new_graph_nodejs/start_graph_and_set_dests_from_new_graph_nodejs_app/ten_packages/extension/test_extension_2/tsconfig.json b/tests/ten_runtime/integration/nodejs/start_graph_and_set_dests_nodejs/start_graph_and_set_dests_nodejs_app/ten_packages/extension/test_extension_2/tsconfig.json similarity index 100% rename from tests/ten_runtime/integration/nodejs/start_graph_and_set_dests_from_new_graph_nodejs/start_graph_and_set_dests_from_new_graph_nodejs_app/ten_packages/extension/test_extension_2/tsconfig.json rename to tests/ten_runtime/integration/nodejs/start_graph_and_set_dests_nodejs/start_graph_and_set_dests_nodejs_app/ten_packages/extension/test_extension_2/tsconfig.json diff --git a/tests/ten_runtime/integration/nodejs/start_graph_and_set_dests_from_new_graph_nodejs/start_graph_and_set_dests_from_new_graph_nodejs_app/ten_packages/extension/test_extension_3/manifest.json b/tests/ten_runtime/integration/nodejs/start_graph_and_set_dests_nodejs/start_graph_and_set_dests_nodejs_app/ten_packages/extension/test_extension_3/manifest.json similarity index 100% rename from tests/ten_runtime/integration/nodejs/start_graph_and_set_dests_from_new_graph_nodejs/start_graph_and_set_dests_from_new_graph_nodejs_app/ten_packages/extension/test_extension_3/manifest.json rename to tests/ten_runtime/integration/nodejs/start_graph_and_set_dests_nodejs/start_graph_and_set_dests_nodejs_app/ten_packages/extension/test_extension_3/manifest.json diff --git a/tests/ten_runtime/integration/nodejs/start_graph_and_set_dests_from_new_graph_nodejs/start_graph_and_set_dests_from_new_graph_nodejs_app/ten_packages/extension/test_extension_3/package.json b/tests/ten_runtime/integration/nodejs/start_graph_and_set_dests_nodejs/start_graph_and_set_dests_nodejs_app/ten_packages/extension/test_extension_3/package.json similarity index 100% rename from tests/ten_runtime/integration/nodejs/start_graph_and_set_dests_from_new_graph_nodejs/start_graph_and_set_dests_from_new_graph_nodejs_app/ten_packages/extension/test_extension_3/package.json rename to tests/ten_runtime/integration/nodejs/start_graph_and_set_dests_nodejs/start_graph_and_set_dests_nodejs_app/ten_packages/extension/test_extension_3/package.json diff --git a/tests/ten_runtime/integration/nodejs/start_graph_and_set_dests_from_new_graph_nodejs/start_graph_and_set_dests_from_new_graph_nodejs_app/ten_packages/extension/test_extension_3/property.json b/tests/ten_runtime/integration/nodejs/start_graph_and_set_dests_nodejs/start_graph_and_set_dests_nodejs_app/ten_packages/extension/test_extension_3/property.json similarity index 100% rename from tests/ten_runtime/integration/nodejs/start_graph_and_set_dests_from_new_graph_nodejs/start_graph_and_set_dests_from_new_graph_nodejs_app/ten_packages/extension/test_extension_3/property.json rename to tests/ten_runtime/integration/nodejs/start_graph_and_set_dests_nodejs/start_graph_and_set_dests_nodejs_app/ten_packages/extension/test_extension_3/property.json diff --git a/tests/ten_runtime/integration/nodejs/start_graph_and_set_dests_from_new_graph_nodejs/start_graph_and_set_dests_from_new_graph_nodejs_app/ten_packages/extension/test_extension_3/src/index.ts b/tests/ten_runtime/integration/nodejs/start_graph_and_set_dests_nodejs/start_graph_and_set_dests_nodejs_app/ten_packages/extension/test_extension_3/src/index.ts similarity index 100% rename from tests/ten_runtime/integration/nodejs/start_graph_and_set_dests_from_new_graph_nodejs/start_graph_and_set_dests_from_new_graph_nodejs_app/ten_packages/extension/test_extension_3/src/index.ts rename to tests/ten_runtime/integration/nodejs/start_graph_and_set_dests_nodejs/start_graph_and_set_dests_nodejs_app/ten_packages/extension/test_extension_3/src/index.ts diff --git a/tests/ten_runtime/integration/nodejs/start_graph_and_set_dests_from_new_graph_nodejs/start_graph_and_set_dests_from_new_graph_nodejs_app/ten_packages/extension/test_extension_3/tsconfig.json b/tests/ten_runtime/integration/nodejs/start_graph_and_set_dests_nodejs/start_graph_and_set_dests_nodejs_app/ten_packages/extension/test_extension_3/tsconfig.json similarity index 100% rename from tests/ten_runtime/integration/nodejs/start_graph_and_set_dests_from_new_graph_nodejs/start_graph_and_set_dests_from_new_graph_nodejs_app/ten_packages/extension/test_extension_3/tsconfig.json rename to tests/ten_runtime/integration/nodejs/start_graph_and_set_dests_nodejs/start_graph_and_set_dests_nodejs_app/ten_packages/extension/test_extension_3/tsconfig.json diff --git a/tests/ten_runtime/integration/nodejs/start_graph_and_set_dests_from_new_graph_nodejs/start_graph_and_set_dests_from_new_graph_nodejs_app/ten_packages/extension/test_extension_4/manifest.json b/tests/ten_runtime/integration/nodejs/start_graph_and_set_dests_nodejs/start_graph_and_set_dests_nodejs_app/ten_packages/extension/test_extension_4/manifest.json similarity index 100% rename from tests/ten_runtime/integration/nodejs/start_graph_and_set_dests_from_new_graph_nodejs/start_graph_and_set_dests_from_new_graph_nodejs_app/ten_packages/extension/test_extension_4/manifest.json rename to tests/ten_runtime/integration/nodejs/start_graph_and_set_dests_nodejs/start_graph_and_set_dests_nodejs_app/ten_packages/extension/test_extension_4/manifest.json diff --git a/tests/ten_runtime/integration/nodejs/start_graph_and_set_dests_from_new_graph_nodejs/start_graph_and_set_dests_from_new_graph_nodejs_app/ten_packages/extension/test_extension_4/package.json b/tests/ten_runtime/integration/nodejs/start_graph_and_set_dests_nodejs/start_graph_and_set_dests_nodejs_app/ten_packages/extension/test_extension_4/package.json similarity index 100% rename from tests/ten_runtime/integration/nodejs/start_graph_and_set_dests_from_new_graph_nodejs/start_graph_and_set_dests_from_new_graph_nodejs_app/ten_packages/extension/test_extension_4/package.json rename to tests/ten_runtime/integration/nodejs/start_graph_and_set_dests_nodejs/start_graph_and_set_dests_nodejs_app/ten_packages/extension/test_extension_4/package.json diff --git a/tests/ten_runtime/integration/nodejs/start_graph_and_set_dests_from_new_graph_nodejs/start_graph_and_set_dests_from_new_graph_nodejs_app/ten_packages/extension/test_extension_4/property.json b/tests/ten_runtime/integration/nodejs/start_graph_and_set_dests_nodejs/start_graph_and_set_dests_nodejs_app/ten_packages/extension/test_extension_4/property.json similarity index 100% rename from tests/ten_runtime/integration/nodejs/start_graph_and_set_dests_from_new_graph_nodejs/start_graph_and_set_dests_from_new_graph_nodejs_app/ten_packages/extension/test_extension_4/property.json rename to tests/ten_runtime/integration/nodejs/start_graph_and_set_dests_nodejs/start_graph_and_set_dests_nodejs_app/ten_packages/extension/test_extension_4/property.json diff --git a/tests/ten_runtime/integration/nodejs/start_graph_and_set_dests_from_new_graph_nodejs/start_graph_and_set_dests_from_new_graph_nodejs_app/ten_packages/extension/test_extension_4/src/index.ts b/tests/ten_runtime/integration/nodejs/start_graph_and_set_dests_nodejs/start_graph_and_set_dests_nodejs_app/ten_packages/extension/test_extension_4/src/index.ts similarity index 100% rename from tests/ten_runtime/integration/nodejs/start_graph_and_set_dests_from_new_graph_nodejs/start_graph_and_set_dests_from_new_graph_nodejs_app/ten_packages/extension/test_extension_4/src/index.ts rename to tests/ten_runtime/integration/nodejs/start_graph_and_set_dests_nodejs/start_graph_and_set_dests_nodejs_app/ten_packages/extension/test_extension_4/src/index.ts diff --git a/tests/ten_runtime/integration/nodejs/start_graph_and_set_dests_from_new_graph_nodejs/start_graph_and_set_dests_from_new_graph_nodejs_app/ten_packages/extension/test_extension_4/tsconfig.json b/tests/ten_runtime/integration/nodejs/start_graph_and_set_dests_nodejs/start_graph_and_set_dests_nodejs_app/ten_packages/extension/test_extension_4/tsconfig.json similarity index 100% rename from tests/ten_runtime/integration/nodejs/start_graph_and_set_dests_from_new_graph_nodejs/start_graph_and_set_dests_from_new_graph_nodejs_app/ten_packages/extension/test_extension_4/tsconfig.json rename to tests/ten_runtime/integration/nodejs/start_graph_and_set_dests_nodejs/start_graph_and_set_dests_nodejs_app/ten_packages/extension/test_extension_4/tsconfig.json diff --git a/tests/ten_runtime/integration/nodejs/start_graph_and_set_dests_from_new_graph_nodejs/test_case.py b/tests/ten_runtime/integration/nodejs/start_graph_and_set_dests_nodejs/test_case.py similarity index 79% rename from tests/ten_runtime/integration/nodejs/start_graph_and_set_dests_from_new_graph_nodejs/test_case.py rename to tests/ten_runtime/integration/nodejs/start_graph_and_set_dests_nodejs/test_case.py index f39c34e023..018defe8e3 100644 --- a/tests/ten_runtime/integration/nodejs/start_graph_and_set_dests_from_new_graph_nodejs/test_case.py +++ b/tests/ten_runtime/integration/nodejs/start_graph_and_set_dests_nodejs/test_case.py @@ -1,5 +1,5 @@ """ -Test start_graph_and_set_dests_from_new_graph_nodejs. +Test start_graph_and_set_dests_nodejs. """ import subprocess @@ -9,14 +9,14 @@ from .utils import msgpack, build_config, build_pkg, fs_utils -def test_start_graph_and_set_dests_from_new_graph_nodejs(): +def test_start_graph_and_set_dests_nodejs(): """Test client and app server.""" base_path = os.path.dirname(os.path.abspath(__file__)) root_dir = os.path.join(base_path, "../../../../../") my_env = os.environ.copy() - app_dir_name = "start_graph_and_set_dests_from_new_graph_nodejs_app" + app_dir_name = "start_graph_and_set_dests_nodejs_app" app_root_path = os.path.join(base_path, app_dir_name) app_language = "nodejs" @@ -49,7 +49,7 @@ def test_start_graph_and_set_dests_from_new_graph_nodejs(): libasan_path = os.path.join( base_path, ( - "start_graph_and_set_dests_from_new_graph_nodejs_app/ten_packages/system/" + "start_graph_and_set_dests_nodejs_app/ten_packages/system/" "ten_runtime/lib/libasan.so" ), ) @@ -60,11 +60,11 @@ def test_start_graph_and_set_dests_from_new_graph_nodejs(): server_cmd = os.path.join( base_path, - "start_graph_and_set_dests_from_new_graph_nodejs_app/bin/start", + "start_graph_and_set_dests_nodejs_app/bin/start", ) client_cmd = os.path.join( - base_path, "start_graph_and_set_dests_from_new_graph_nodejs_app_client" + base_path, "start_graph_and_set_dests_nodejs_app_client" ) if not os.path.isfile(server_cmd): @@ -82,13 +82,13 @@ def test_start_graph_and_set_dests_from_new_graph_nodejs(): is_started, sock = msgpack.is_app_started("127.0.0.1", 8001, 30) if not is_started: print( - "The start_graph_and_set_dests_from_new_graph_nodejs is not started after 30 seconds." + "The start_graph_and_set_dests_nodejs is not started after 30 seconds." ) server.kill() exit_code = server.wait() print( - "The exit code of start_graph_and_set_dests_from_new_graph_nodejs: ", + "The exit code of start_graph_and_set_dests_nodejs: ", exit_code, ) @@ -99,13 +99,13 @@ def test_start_graph_and_set_dests_from_new_graph_nodejs(): # client depends on some libraries in the TEN app. my_env["DYLD_LIBRARY_PATH"] = os.path.join( base_path, - "start_graph_and_set_dests_from_new_graph_nodejs_app/ten_packages/system/ten_runtime/lib", + "start_graph_and_set_dests_nodejs_app/ten_packages/system/ten_runtime/lib", ) else: # client depends on some libraries in the TEN app. my_env["LD_LIBRARY_PATH"] = os.path.join( base_path, - "start_graph_and_set_dests_from_new_graph_nodejs_app/ten_packages/system/ten_runtime/lib", + "start_graph_and_set_dests_nodejs_app/ten_packages/system/ten_runtime/lib", ) my_env["LD_PRELOAD"] = "" From 3c8cef55e8e206ccb4310cd336b06369be95bdd5 Mon Sep 17 00:00:00 2001 From: Nie Zhihe Date: Mon, 9 Feb 2026 00:39:05 +0800 Subject: [PATCH 02/16] chore: enable nodejs_binding in workflow --- .github/workflows/win.yml | 103 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 100 insertions(+), 3 deletions(-) diff --git a/.github/workflows/win.yml b/.github/workflows/win.yml index fa6db4de58..c6b2f18105 100644 --- a/.github/workflows/win.yml +++ b/.github/workflows/win.yml @@ -265,10 +265,12 @@ jobs: if ("${{ matrix.compiler }}" -eq "mingw") { $buildArgs += "is_mingw=true" $buildArgs += "is_clang=false" + $buildArgs += "ten_enable_nodejs_binding=false" } else { $buildArgs += "is_mingw=false" # msvc $buildArgs += "is_clang=true" # choose to use clang-cl.exe instead of cl.exe, the latter does not work yet $buildArgs += "vs_version=2022" + $buildArgs += "ten_enable_nodejs_binding=true" } $buildArgs += "log_level=1" $buildArgs += "enable_serialized_actions=true" @@ -276,7 +278,6 @@ jobs: $buildArgs += "ten_enable_cargo_clean=true" $buildArgs += "ten_enable_python_binding=true" $buildArgs += "ten_enable_go_binding=true" - $buildArgs += "ten_enable_nodejs_binding=false" $buildArgs += "ten_enable_rust_incremental_build=false" $buildArgs += "ten_manager_enable_frontend=false" $argsString = $buildArgs -join " " @@ -1220,6 +1221,104 @@ jobs: # - name: Setup tmate session # uses: mxschmitt/action-tmate@v3 + test-integration-nodejs: + needs: build + runs-on: windows-latest + env: + PYTHONIOENCODING: utf-8 + strategy: + matrix: + build_type: [debug, release] + compiler: [msvc] + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + submodules: false + + - name: Enable Windows long path support + shell: pwsh + run: | + Write-Output "Enabling Windows long path support..." + New-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Control\FileSystem" -Name "LongPathsEnabled" -Value 1 -PropertyType DWORD -Force + Write-Output "Long path support enabled" + + - name: Trust working directory + run: git config --global --add safe.directory "${GITHUB_WORKSPACE}" + + - name: Initialize and update submodules except portal/ + shell: bash + run: | + # Retrieve all submodule paths, excluding `portal/`. + submodules=$(git config --file .gitmodules --get-regexp path | awk '$2 != "portal" { print $2 }') + git submodule init + for submodule in $submodules; do + echo "Initializing submodule: $submodule" + git submodule update --init --recursive --depth 1 "$submodule" + done + + - uses: actions/setup-node@v4 + with: + node-version: 20 + + - name: Setup MSVC + if: matrix.compiler == 'msvc' + uses: ilammy/msvc-dev-cmd@v1 + + - uses: actions/setup-python@v5 + with: + python-version: "3.10" + + - uses: actions/setup-go@v5 + with: + go-version: "stable" + cache: false + + - name: Install tools and dependencies + run: | + pip3 install --use-pep517 python-dotenv jinja2 + go install golang.org/dl/go1.24.3@latest + go1.24.3 download + go1.24.3 version + go env -w GOFLAGS="-buildvcs=false" + + - name: Get Python executable path + run: | + $pythonPath = python -c "import sys; print(sys.executable)" + Write-Output "Python executable path: $pythonPath" + $pythonDir = Split-Path $pythonPath + Write-Output "Python directory path: $pythonDir" + echo "PYTHON3_PATH=$pythonDir" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append + shell: pwsh + + - name: Use Python path + run: | + Write-Output "The Python directory is located at: $env:PYTHON3_PATH" + shell: pwsh + + - name: Download build artifacts + uses: actions/download-artifact@v4 + with: + name: tests-artifacts-win-${{ matrix.build_type }}-${{ matrix.compiler }} + path: out/win/x64 + + - name: Extract tests artifacts preserving permissions + shell: pwsh + run: | + Expand-Archive -Path "out/win/x64/tests-artifacts.zip" -DestinationPath "out/win/x64" -Force + + - name: Install Python dependencies via script + run: | + python .github/tools/setup_pytest_dependencies.py + + - name: Run Tests (ten_runtime Nodejs integration tests) + env: + TEN_ENABLE_BACKTRACE_DUMP: "true" + run: | + $ENV:PATH += ";$PWD/core/ten_gn" + cd out/win/x64/ + pytest -s tests/ten_runtime/integration/nodejs/ + test-integration-go: needs: build runs-on: windows-latest @@ -1229,8 +1328,6 @@ jobs: matrix: build_type: [debug, release] compiler: [msvc, mingw] - exclude: - - compiler: msvc # not implemented yet steps: - uses: actions/checkout@v4 with: From 26dda209a9d0be58efd06fb4abaca78189752e79 Mon Sep 17 00:00:00 2001 From: Nie Zhihe Date: Mon, 9 Feb 2026 16:12:26 +0800 Subject: [PATCH 03/16] chore: modify nodejs tests --- .../nodejs_addon_loader/BUILD.gn | 143 ++++++++++++------ .../nodejs_addon_loader/src/main.cc | 10 ++ .../core_apps/default_app_nodejs/BUILD.gn | 5 + .../core_apps/default_app_nodejs/bin/start.py | 42 +++++ .../tests/bin/start.py | 55 +++++++ .../test_case.py | 24 ++- .../cpp_app_nodejs_app/BUILD.gn | 4 + .../cpp_app_nodejs_app/bin/start.py | 41 +++++ .../nodejs/cpp_app_nodejs/test_case.py | 18 ++- .../get_set_property_nodejs/test_case.py | 24 ++- .../go_app_nodejs_app/bin/start.py | 41 +++++ .../nodejs/go_app_nodejs/test_case.py | 18 ++- .../bin/start.py | 45 ++++++ .../test_case.py | 24 ++- .../bin/start.py | 41 +++++ .../test_case.py | 24 ++- .../nodejs/http_server_nodejs/test_case.py | 18 ++- .../nodejs/leak_check_nodejs/test_case.py | 18 ++- .../nodejs/mix_python_ext_nodejs/BUILD.gn | 5 + .../bin/bootstrap.py | 44 ++++++ .../nodejs/mix_python_ext_nodejs/test_case.py | 34 ++++- .../send_audio_frame_nodejs/test_case.py | 24 ++- .../nodejs/send_cmd_ex_nodejs/test_case.py | 18 ++- .../nodejs/send_cmd_nodejs/test_case.py | 18 ++- .../nodejs/send_data_nodejs/test_case.py | 18 ++- .../send_video_frame_nodejs/test_case.py | 24 ++- .../nodejs/set_dest_nodejs/test_case.py | 18 ++- .../standalone_test_nodejs/test_case.py | 18 ++- .../standalone_test_nodejs_2/test_case.py | 18 ++- .../standalone_test_nodejs_3/test_case.py | 18 ++- .../test_case.py | 37 +++-- .../test_case.py | 37 +++-- .../trigger_life_cycle_nodejs/test_case.py | 24 ++- .../websocket_server_nodejs/test_case.py | 24 ++- 34 files changed, 778 insertions(+), 196 deletions(-) create mode 100644 packages/core_apps/default_app_nodejs/bin/start.py create mode 100644 packages/core_extensions/default_extension_nodejs/tests/bin/start.py create mode 100644 tests/ten_runtime/integration/nodejs/cpp_app_nodejs/cpp_app_nodejs_app/bin/start.py create mode 100644 tests/ten_runtime/integration/nodejs/go_app_nodejs/go_app_nodejs_app/bin/start.py create mode 100644 tests/ten_runtime/integration/nodejs/go_app_preload_all_addons_nodejs/go_app_preload_all_addons_nodejs_app/bin/start.py create mode 100644 tests/ten_runtime/integration/nodejs/go_app_websocket_server_nodejs/go_app_websocket_server_nodejs_app/bin/start.py create mode 100644 tests/ten_runtime/integration/nodejs/mix_python_ext_nodejs/mix_python_ext_nodejs_app/bin/bootstrap.py diff --git a/packages/core_addon_loaders/nodejs_addon_loader/BUILD.gn b/packages/core_addon_loaders/nodejs_addon_loader/BUILD.gn index 983692147a..cace601c1b 100644 --- a/packages/core_addon_loaders/nodejs_addon_loader/BUILD.gn +++ b/packages/core_addon_loaders/nodejs_addon_loader/BUILD.gn @@ -9,64 +9,107 @@ import("//build/ten_runtime/feature/publish.gni") import("//build/ten_runtime/glob.gni") import("//build/ten_runtime/options.gni") -action("copy_node_shared_lib") { - script = "//.gnfiles/build/scripts/copy_fs_entry.py" - - # Determine shared library naming based on platform - # Linux: libnode.so.127 - # Mac: libnode.127.dylib - if (is_mac) { - _source_lib = "${root_out_dir}/prebuilt/node_shared/lib/libnode.127.dylib" - _dest_lib = "${root_out_dir}/ten_packages/addon_loader/nodejs_addon_loader/lib/libnode.127.dylib" - } else { - _source_lib = "${root_out_dir}/prebuilt/node_shared/lib/libnode.so.127" - _dest_lib = "${root_out_dir}/ten_packages/addon_loader/nodejs_addon_loader/lib/libnode.so.127" +# Copy node shared library to addon_loader package +# Platform-specific handling: +# - Linux: libnode.so.127 + symlink libnode.so +# - Mac: libnode.127.dylib + symlink libnode.dylib +# - Win: libnode.dll (no symlink needed) + +if (is_win) { + action("copy_node_shared_lib") { + script = "//.gnfiles/build/scripts/copy_fs_entry.py" + + _source_lib = "//third_party/node/lib/win/x64/libnode.dll" + _dest_lib = "${root_out_dir}/ten_packages/addon_loader/nodejs_addon_loader/lib/libnode.dll" + + args = [ + "--source", + rebase_path(_source_lib), + "--destination", + rebase_path(_dest_lib), + ] + + sources = [ _source_lib ] + outputs = [ _dest_lib ] + + deps = [ "//third_party/node" ] } - args = [ - "--source", - rebase_path(_source_lib), - "--destination", - rebase_path(_dest_lib), - ] - - sources = [ _source_lib ] - outputs = [ _dest_lib ] - - deps = [ "//third_party/node" ] -} - -action("create_symlink") { - script = "//build/ten_common/scripts/create_symlink.py" - - # Determine shared library naming based on platform - # Linux: libnode.so -> libnode.so.127 - # Mac: libnode.dylib -> libnode.127.dylib - if (is_mac) { - _versioned_lib = "${root_out_dir}/ten_packages/addon_loader/nodejs_addon_loader/lib/libnode.127.dylib" - _symlink_lib = "${root_out_dir}/ten_packages/addon_loader/nodejs_addon_loader/lib/libnode.dylib" - } else { - _versioned_lib = "${root_out_dir}/ten_packages/addon_loader/nodejs_addon_loader/lib/libnode.so.127" - _symlink_lib = "${root_out_dir}/ten_packages/addon_loader/nodejs_addon_loader/lib/libnode.so" + # Windows doesn't need symlinks, create a no-op group + group("create_symlink") { + deps = [ ":copy_node_shared_lib" ] + } +} else { + action("copy_node_shared_lib") { + script = "//.gnfiles/build/scripts/copy_fs_entry.py" + + # Determine shared library naming based on platform + # Linux: libnode.so.127 + # Mac: libnode.127.dylib + if (is_mac) { + _source_lib = "${root_out_dir}/prebuilt/node_shared/lib/libnode.127.dylib" + _dest_lib = "${root_out_dir}/ten_packages/addon_loader/nodejs_addon_loader/lib/libnode.127.dylib" + } else { + _source_lib = "${root_out_dir}/prebuilt/node_shared/lib/libnode.so.127" + _dest_lib = "${root_out_dir}/ten_packages/addon_loader/nodejs_addon_loader/lib/libnode.so.127" + } + + args = [ + "--source", + rebase_path(_source_lib), + "--destination", + rebase_path(_dest_lib), + ] + + sources = [ _source_lib ] + outputs = [ _dest_lib ] + + deps = [ "//third_party/node" ] } - args = [ - rebase_path(_versioned_lib), - rebase_path(_symlink_lib), - ] - - sources = [ _versioned_lib ] - outputs = [ _symlink_lib ] - - deps = [ ":copy_node_shared_lib" ] + action("create_symlink") { + script = "//build/ten_common/scripts/create_symlink.py" + + # Determine shared library naming based on platform + # Linux: libnode.so -> libnode.so.127 + # Mac: libnode.dylib -> libnode.127.dylib + if (is_mac) { + _versioned_lib = "${root_out_dir}/ten_packages/addon_loader/nodejs_addon_loader/lib/libnode.127.dylib" + _symlink_lib = "${root_out_dir}/ten_packages/addon_loader/nodejs_addon_loader/lib/libnode.dylib" + } else { + _versioned_lib = "${root_out_dir}/ten_packages/addon_loader/nodejs_addon_loader/lib/libnode.so.127" + _symlink_lib = "${root_out_dir}/ten_packages/addon_loader/nodejs_addon_loader/lib/libnode.so" + } + + args = [ + rebase_path(_versioned_lib), + rebase_path(_symlink_lib), + ] + + sources = [ _versioned_lib ] + outputs = [ _symlink_lib ] + + deps = [ ":copy_node_shared_lib" ] + } } config("nodejs_addon_loader_config") { - cflags_cc = [ "-std=c++17" ] + # Prevent Windows SDK min/max macros from conflicting with std::min/max + if (is_win) { + defines = [ "NOMINMAX" ] + } + + if (is_win && !is_mingw) { + # MSVC or clang-cl on Windows uses /std:c++17 + cflags_cc = [ "/std:c++17" ] + } else { + # GCC, Clang, MinGW use -std=c++17 + cflags_cc = [ "-std=c++17" ] - if (is_clang && !is_mac) { - cflags_cc += [ "-stdlib=libstdc++" ] - libs = [ "stdc++" ] + if (is_clang && !is_mac && !is_win) { + cflags_cc += [ "-stdlib=libstdc++" ] + libs = [ "stdc++" ] + } } } diff --git a/packages/core_addon_loaders/nodejs_addon_loader/src/main.cc b/packages/core_addon_loaders/nodejs_addon_loader/src/main.cc index cc6ad99186..47272cf195 100644 --- a/packages/core_addon_loaders/nodejs_addon_loader/src/main.cc +++ b/packages/core_addon_loaders/nodejs_addon_loader/src/main.cc @@ -4,11 +4,21 @@ // Licensed under the Apache License, Version 2.0, with certain conditions. // Refer to the "LICENSE" file in the root directory for more information. // + +// On Windows, prevent min/max macros from Windows SDK conflicting with +// std::min/max and std::numeric_limits<>::min()/max() +#if defined(_WIN32) +#ifndef NOMINMAX +#define NOMINMAX +#endif +#endif + #include #include #include #include +#include #include #include "node.h" diff --git a/packages/core_apps/default_app_nodejs/BUILD.gn b/packages/core_apps/default_app_nodejs/BUILD.gn index cf0c3d53f0..c734dccb63 100644 --- a/packages/core_apps/default_app_nodejs/BUILD.gn +++ b/packages/core_apps/default_app_nodejs/BUILD.gn @@ -5,6 +5,7 @@ # import("//build/feature/ten_package.gni") import("//build/ten_runtime/feature/publish.gni") +import("//build/ten_runtime/options.gni") ten_package("default_app_nodejs") { package_kind = "app" @@ -19,6 +20,10 @@ ten_package("default_app_nodejs") { "tsconfig.json", ] + if (is_win) { + resources += [ "bin/start.py" ] + } + docs_files = exec_script("//.gnfiles/build/scripts/glob_file.py", [ "--dir", diff --git a/packages/core_apps/default_app_nodejs/bin/start.py b/packages/core_apps/default_app_nodejs/bin/start.py new file mode 100644 index 0000000000..c1516f4294 --- /dev/null +++ b/packages/core_apps/default_app_nodejs/bin/start.py @@ -0,0 +1,42 @@ +#!/usr/bin/env python3 +""" +Cross-platform start script for Node.js apps on Windows. +On Unix-like systems, prefer using bash start script for faster startup. +""" + +import os +import sys +import subprocess + +# Change to the project root directory +app_root = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) +os.chdir(app_root) + +env = os.environ.copy() + +# Set NODE_PATH to include ten_runtime_nodejs lib +ten_runtime_nodejs_lib = os.path.join( + app_root, "ten_packages", "system", "ten_runtime_nodejs", "lib" +) +if "NODE_PATH" in env: + env["NODE_PATH"] = ten_runtime_nodejs_lib + os.pathsep + env["NODE_PATH"] +else: + env["NODE_PATH"] = ten_runtime_nodejs_lib + +# Add DLL search paths to PATH for Windows +# ten_runtime_nodejs.node depends on libnode.dll and ten_runtime.dll +ten_runtime_lib = os.path.join( + app_root, "ten_packages", "system", "ten_runtime", "lib" +) +dll_paths = [ten_runtime_nodejs_lib, ten_runtime_lib] +env["PATH"] = os.pathsep.join(dll_paths) + os.pathsep + env.get("PATH", "") + +# Run node with the start script +start_js = os.path.join(app_root, "build", "start.js") + +if not os.path.exists(start_js): + print(f"Error: {start_js} not found") + sys.exit(1) + +cmd = ["node", "--expose-gc", start_js] +sys.exit(subprocess.run(cmd, env=env).returncode) diff --git a/packages/core_extensions/default_extension_nodejs/tests/bin/start.py b/packages/core_extensions/default_extension_nodejs/tests/bin/start.py new file mode 100644 index 0000000000..c4ae1adfc0 --- /dev/null +++ b/packages/core_extensions/default_extension_nodejs/tests/bin/start.py @@ -0,0 +1,55 @@ +#!/usr/bin/env python3 +""" +Cross-platform test start script for Node.js extensions on Windows. +On Unix-like systems, prefer using bash start script for faster startup. +""" + +import os +import sys +import subprocess + +# Change to the tests directory (parent of bin) +tests_root = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) +os.chdir(tests_root) + +env = os.environ.copy() + +# npm install +print("Running npm install...") +result = subprocess.run(["npm", "install"], env=env, shell=True) +if result.returncode != 0: + print("npm install failed") + sys.exit(result.returncode) + +# npm run build +print("Running npm run build...") +result = subprocess.run(["npm", "run", "build"], env=env, shell=True) +if result.returncode != 0: + print("npm run build failed") + sys.exit(result.returncode) + +# Set NODE_PATH to include ten_runtime_nodejs lib +ten_runtime_nodejs_lib = os.path.join( + tests_root, "..", ".ten", "app", "ten_packages", "system", + "ten_runtime_nodejs", "lib" +) +ten_runtime_nodejs_lib = os.path.normpath(ten_runtime_nodejs_lib) +if "NODE_PATH" in env: + env["NODE_PATH"] = ten_runtime_nodejs_lib + os.pathsep + env["NODE_PATH"] +else: + env["NODE_PATH"] = ten_runtime_nodejs_lib + +# Add DLL search paths to PATH for Windows +# ten_runtime_nodejs.node depends on libnode.dll and ten_runtime.dll +ten_runtime_lib = os.path.join( + tests_root, "..", ".ten", "app", "ten_packages", "system", + "ten_runtime", "lib" +) +ten_runtime_lib = os.path.normpath(ten_runtime_lib) +dll_paths = [ten_runtime_nodejs_lib, ten_runtime_lib] +env["PATH"] = os.pathsep.join(dll_paths) + os.pathsep + env.get("PATH", "") + +# npm test +print("Running npm test...") +result = subprocess.run(["npm", "test"], env=env, shell=True) +sys.exit(result.returncode) diff --git a/tests/ten_runtime/integration/nodejs/call_api_after_closing_nodejs/test_case.py b/tests/ten_runtime/integration/nodejs/call_api_after_closing_nodejs/test_case.py index 62d6bee581..b7220e33b7 100644 --- a/tests/ten_runtime/integration/nodejs/call_api_after_closing_nodejs/test_case.py +++ b/tests/ten_runtime/integration/nodejs/call_api_after_closing_nodejs/test_case.py @@ -69,13 +69,23 @@ def test_call_api_after_closing_nodejs(): print("Using AddressSanitizer library.") my_env["LD_PRELOAD"] = libasan_path - server_cmd = os.path.join( - base_path, "call_api_after_closing_nodejs_app/bin/start" - ) - - if not os.path.isfile(server_cmd): - print(f"Server command '{server_cmd}' does not exist.") - assert False + if sys.platform == "win32": + server_cmd = [ + sys.executable, + os.path.join( + base_path, "call_api_after_closing_nodejs_app/bin/start.py" + ), + ] + if not os.path.isfile(server_cmd[1]): + print(f"Server command '{server_cmd[1]}' does not exist.") + assert False + else: + server_cmd = os.path.join( + base_path, "call_api_after_closing_nodejs_app/bin/start" + ) + if not os.path.isfile(server_cmd): + print(f"Server command '{server_cmd}' does not exist.") + assert False server = subprocess.Popen( server_cmd, diff --git a/tests/ten_runtime/integration/nodejs/cpp_app_nodejs/cpp_app_nodejs_app/BUILD.gn b/tests/ten_runtime/integration/nodejs/cpp_app_nodejs/cpp_app_nodejs_app/BUILD.gn index f4bb9ee154..c77f042a17 100644 --- a/tests/ten_runtime/integration/nodejs/cpp_app_nodejs/cpp_app_nodejs_app/BUILD.gn +++ b/tests/ten_runtime/integration/nodejs/cpp_app_nodejs/cpp_app_nodejs_app/BUILD.gn @@ -16,5 +16,9 @@ ten_package("cpp_app_nodejs_app") { "property.json", ] + if (is_win) { + resources += [ "bin/start.py" ] + } + sources = [ "src/main.cc" ] } diff --git a/tests/ten_runtime/integration/nodejs/cpp_app_nodejs/cpp_app_nodejs_app/bin/start.py b/tests/ten_runtime/integration/nodejs/cpp_app_nodejs/cpp_app_nodejs_app/bin/start.py new file mode 100644 index 0000000000..8c7591f623 --- /dev/null +++ b/tests/ten_runtime/integration/nodejs/cpp_app_nodejs/cpp_app_nodejs_app/bin/start.py @@ -0,0 +1,41 @@ +#!/usr/bin/env python3 +""" +Cross-platform start script for C++ apps with Node.js extensions on Windows. +On Unix-like systems, prefer using bash start script for faster startup. +""" + +import os +import sys +import subprocess + +# Change to the project root directory +app_root = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) +os.chdir(app_root) + +env = os.environ.copy() + +# Set NODE_PATH to include ten_runtime_nodejs lib +ten_runtime_nodejs_lib = os.path.join( + app_root, "ten_packages", "system", "ten_runtime_nodejs", "lib" +) +if "NODE_PATH" in env: + env["NODE_PATH"] = ten_runtime_nodejs_lib + os.pathsep + env["NODE_PATH"] +else: + env["NODE_PATH"] = ten_runtime_nodejs_lib + +# Add DLL search paths to PATH for Windows +# ten_runtime_nodejs.node depends on libnode.dll and ten_runtime.dll +ten_runtime_lib = os.path.join( + app_root, "ten_packages", "system", "ten_runtime", "lib" +) +dll_paths = [ten_runtime_nodejs_lib, ten_runtime_lib] +env["PATH"] = os.pathsep.join(dll_paths) + os.pathsep + env.get("PATH", "") + +# Run the C++ app executable +main_exe = os.path.join(app_root, "bin", "cpp_app_nodejs_app.exe") + +if not os.path.exists(main_exe): + print(f"Error: {main_exe} not found") + sys.exit(1) + +sys.exit(subprocess.run([main_exe], env=env).returncode) diff --git a/tests/ten_runtime/integration/nodejs/cpp_app_nodejs/test_case.py b/tests/ten_runtime/integration/nodejs/cpp_app_nodejs/test_case.py index 8f9e5c0e2d..d375b108ab 100644 --- a/tests/ten_runtime/integration/nodejs/cpp_app_nodejs/test_case.py +++ b/tests/ten_runtime/integration/nodejs/cpp_app_nodejs/test_case.py @@ -71,11 +71,19 @@ def test_cpp_app_nodejs(): print("Using AddressSanitizer library.") my_env["LD_PRELOAD"] = libasan_path - server_cmd = os.path.join(base_path, "cpp_app_nodejs_app/bin/start") - - if not os.path.isfile(server_cmd): - print(f"Server command '{server_cmd}' does not exist.") - assert False + if sys.platform == "win32": + server_cmd = [ + sys.executable, + os.path.join(base_path, "cpp_app_nodejs_app/bin/start.py"), + ] + if not os.path.isfile(server_cmd[1]): + print(f"Server command '{server_cmd[1]}' does not exist.") + assert False + else: + server_cmd = os.path.join(base_path, "cpp_app_nodejs_app/bin/start") + if not os.path.isfile(server_cmd): + print(f"Server command '{server_cmd}' does not exist.") + assert False server = subprocess.Popen( server_cmd, diff --git a/tests/ten_runtime/integration/nodejs/get_set_property_nodejs/test_case.py b/tests/ten_runtime/integration/nodejs/get_set_property_nodejs/test_case.py index 5ad58febcf..4a3926154d 100644 --- a/tests/ten_runtime/integration/nodejs/get_set_property_nodejs/test_case.py +++ b/tests/ten_runtime/integration/nodejs/get_set_property_nodejs/test_case.py @@ -59,13 +59,23 @@ def test_get_set_property_nodejs(): print("Using AddressSanitizer library.") my_env["LD_PRELOAD"] = libasan_path - server_cmd = os.path.join( - base_path, "get_set_property_nodejs_app/bin/start" - ) - - if not os.path.isfile(server_cmd): - print(f"Server command '{server_cmd}' does not exist.") - assert False + if sys.platform == "win32": + server_cmd = [ + sys.executable, + os.path.join( + base_path, "get_set_property_nodejs_app/bin/start.py" + ), + ] + if not os.path.isfile(server_cmd[1]): + print(f"Server command '{server_cmd[1]}' does not exist.") + assert False + else: + server_cmd = os.path.join( + base_path, "get_set_property_nodejs_app/bin/start" + ) + if not os.path.isfile(server_cmd): + print(f"Server command '{server_cmd}' does not exist.") + assert False server = subprocess.Popen( server_cmd, diff --git a/tests/ten_runtime/integration/nodejs/go_app_nodejs/go_app_nodejs_app/bin/start.py b/tests/ten_runtime/integration/nodejs/go_app_nodejs/go_app_nodejs_app/bin/start.py new file mode 100644 index 0000000000..d65d27b129 --- /dev/null +++ b/tests/ten_runtime/integration/nodejs/go_app_nodejs/go_app_nodejs_app/bin/start.py @@ -0,0 +1,41 @@ +#!/usr/bin/env python3 +""" +Cross-platform start script for Go apps with Node.js extensions on Windows. +On Unix-like systems, prefer using bash start script for faster startup. +""" + +import os +import sys +import subprocess + +# Change to the project root directory +app_root = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) +os.chdir(app_root) + +env = os.environ.copy() + +# Set NODE_PATH to include ten_runtime_nodejs lib +ten_runtime_nodejs_lib = os.path.join( + app_root, "ten_packages", "system", "ten_runtime_nodejs", "lib" +) +if "NODE_PATH" in env: + env["NODE_PATH"] = ten_runtime_nodejs_lib + os.pathsep + env["NODE_PATH"] +else: + env["NODE_PATH"] = ten_runtime_nodejs_lib + +# Add DLL search paths to PATH for Windows +# ten_runtime_nodejs.node depends on libnode.dll and ten_runtime.dll +ten_runtime_lib = os.path.join( + app_root, "ten_packages", "system", "ten_runtime", "lib" +) +dll_paths = [ten_runtime_nodejs_lib, ten_runtime_lib] +env["PATH"] = os.pathsep.join(dll_paths) + os.pathsep + env.get("PATH", "") + +# Run the Go app executable +main_exe = os.path.join(app_root, "bin", "main.exe") + +if not os.path.exists(main_exe): + print(f"Error: {main_exe} not found") + sys.exit(1) + +sys.exit(subprocess.run([main_exe], env=env).returncode) diff --git a/tests/ten_runtime/integration/nodejs/go_app_nodejs/test_case.py b/tests/ten_runtime/integration/nodejs/go_app_nodejs/test_case.py index eb71dbc50a..f761d7e66b 100644 --- a/tests/ten_runtime/integration/nodejs/go_app_nodejs/test_case.py +++ b/tests/ten_runtime/integration/nodejs/go_app_nodejs/test_case.py @@ -72,11 +72,19 @@ def test_go_app_nodejs(): print("Using AddressSanitizer library.") my_env["LD_PRELOAD"] = libasan_path - server_cmd = os.path.join(base_path, "go_app_nodejs_app/bin/start") - - if not os.path.isfile(server_cmd): - print(f"Server command '{server_cmd}' does not exist.") - assert False + if sys.platform == "win32": + server_cmd = [ + sys.executable, + os.path.join(base_path, "go_app_nodejs_app/bin/start.py"), + ] + if not os.path.isfile(server_cmd[1]): + print(f"Server command '{server_cmd[1]}' does not exist.") + assert False + else: + server_cmd = os.path.join(base_path, "go_app_nodejs_app/bin/start") + if not os.path.isfile(server_cmd): + print(f"Server command '{server_cmd}' does not exist.") + assert False server = subprocess.Popen( server_cmd, diff --git a/tests/ten_runtime/integration/nodejs/go_app_preload_all_addons_nodejs/go_app_preload_all_addons_nodejs_app/bin/start.py b/tests/ten_runtime/integration/nodejs/go_app_preload_all_addons_nodejs/go_app_preload_all_addons_nodejs_app/bin/start.py new file mode 100644 index 0000000000..f0610115ee --- /dev/null +++ b/tests/ten_runtime/integration/nodejs/go_app_preload_all_addons_nodejs/go_app_preload_all_addons_nodejs_app/bin/start.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 +""" +Cross-platform start script for Go apps with Node.js extensions on Windows. +On Unix-like systems, prefer using bash start script for faster startup. +""" + +import os +import sys +import subprocess + +# Change to the project root directory +app_root = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) +os.chdir(app_root) + +env = os.environ.copy() + +# Set NODE_PATH to include ten_runtime_nodejs lib +ten_runtime_nodejs_lib = os.path.join( + app_root, "ten_packages", "system", "ten_runtime_nodejs", "lib" +) +if "NODE_PATH" in env: + env["NODE_PATH"] = ten_runtime_nodejs_lib + os.pathsep + env["NODE_PATH"] +else: + env["NODE_PATH"] = ten_runtime_nodejs_lib + +# Add DLL search paths to PATH for Windows +# ten_runtime_nodejs.node depends on libnode.dll and ten_runtime.dll +ten_runtime_lib = os.path.join( + app_root, "ten_packages", "system", "ten_runtime", "lib" +) +dll_paths = [ten_runtime_nodejs_lib, ten_runtime_lib] +env["PATH"] = os.pathsep.join(dll_paths) + os.pathsep + env.get("PATH", "") + +# Set Python environment variables +env["PYTHONMALLOC"] = "malloc" +env["PYTHONDEVMODE"] = "1" + +# Run the Go app executable +main_exe = os.path.join(app_root, "bin", "main.exe") + +if not os.path.exists(main_exe): + print(f"Error: {main_exe} not found") + sys.exit(1) + +sys.exit(subprocess.run([main_exe], env=env).returncode) diff --git a/tests/ten_runtime/integration/nodejs/go_app_preload_all_addons_nodejs/test_case.py b/tests/ten_runtime/integration/nodejs/go_app_preload_all_addons_nodejs/test_case.py index b59a280e1b..8c487b8845 100644 --- a/tests/ten_runtime/integration/nodejs/go_app_preload_all_addons_nodejs/test_case.py +++ b/tests/ten_runtime/integration/nodejs/go_app_preload_all_addons_nodejs/test_case.py @@ -79,13 +79,23 @@ def test_go_app_preload_all_addons_nodejs(): print("Using AddressSanitizer library.") my_env["LD_PRELOAD"] = libasan_path - server_cmd = os.path.join( - base_path, "go_app_preload_all_addons_nodejs_app/bin/start" - ) - - if not os.path.isfile(server_cmd): - print(f"Server command '{server_cmd}' does not exist.") - assert False + if sys.platform == "win32": + server_cmd = [ + sys.executable, + os.path.join( + base_path, "go_app_preload_all_addons_nodejs_app/bin/start.py" + ), + ] + if not os.path.isfile(server_cmd[1]): + print(f"Server command '{server_cmd[1]}' does not exist.") + assert False + else: + server_cmd = os.path.join( + base_path, "go_app_preload_all_addons_nodejs_app/bin/start" + ) + if not os.path.isfile(server_cmd): + print(f"Server command '{server_cmd}' does not exist.") + assert False server = subprocess.Popen( server_cmd, diff --git a/tests/ten_runtime/integration/nodejs/go_app_websocket_server_nodejs/go_app_websocket_server_nodejs_app/bin/start.py b/tests/ten_runtime/integration/nodejs/go_app_websocket_server_nodejs/go_app_websocket_server_nodejs_app/bin/start.py new file mode 100644 index 0000000000..d65d27b129 --- /dev/null +++ b/tests/ten_runtime/integration/nodejs/go_app_websocket_server_nodejs/go_app_websocket_server_nodejs_app/bin/start.py @@ -0,0 +1,41 @@ +#!/usr/bin/env python3 +""" +Cross-platform start script for Go apps with Node.js extensions on Windows. +On Unix-like systems, prefer using bash start script for faster startup. +""" + +import os +import sys +import subprocess + +# Change to the project root directory +app_root = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) +os.chdir(app_root) + +env = os.environ.copy() + +# Set NODE_PATH to include ten_runtime_nodejs lib +ten_runtime_nodejs_lib = os.path.join( + app_root, "ten_packages", "system", "ten_runtime_nodejs", "lib" +) +if "NODE_PATH" in env: + env["NODE_PATH"] = ten_runtime_nodejs_lib + os.pathsep + env["NODE_PATH"] +else: + env["NODE_PATH"] = ten_runtime_nodejs_lib + +# Add DLL search paths to PATH for Windows +# ten_runtime_nodejs.node depends on libnode.dll and ten_runtime.dll +ten_runtime_lib = os.path.join( + app_root, "ten_packages", "system", "ten_runtime", "lib" +) +dll_paths = [ten_runtime_nodejs_lib, ten_runtime_lib] +env["PATH"] = os.pathsep.join(dll_paths) + os.pathsep + env.get("PATH", "") + +# Run the Go app executable +main_exe = os.path.join(app_root, "bin", "main.exe") + +if not os.path.exists(main_exe): + print(f"Error: {main_exe} not found") + sys.exit(1) + +sys.exit(subprocess.run([main_exe], env=env).returncode) diff --git a/tests/ten_runtime/integration/nodejs/go_app_websocket_server_nodejs/test_case.py b/tests/ten_runtime/integration/nodejs/go_app_websocket_server_nodejs/test_case.py index 377f3bbba6..4e1ce69696 100644 --- a/tests/ten_runtime/integration/nodejs/go_app_websocket_server_nodejs/test_case.py +++ b/tests/ten_runtime/integration/nodejs/go_app_websocket_server_nodejs/test_case.py @@ -95,13 +95,23 @@ def test_go_app_websocket_server_nodejs(): print("Using AddressSanitizer library.") my_env["LD_PRELOAD"] = libasan_path - server_cmd = os.path.join( - base_path, "go_app_websocket_server_nodejs_app/bin/start" - ) - - if not os.path.isfile(server_cmd): - print(f"Server command '{server_cmd}' does not exist.") - assert False + if sys.platform == "win32": + server_cmd = [ + sys.executable, + os.path.join( + base_path, "go_app_websocket_server_nodejs_app/bin/start.py" + ), + ] + if not os.path.isfile(server_cmd[1]): + print(f"Server command '{server_cmd[1]}' does not exist.") + assert False + else: + server_cmd = os.path.join( + base_path, "go_app_websocket_server_nodejs_app/bin/start" + ) + if not os.path.isfile(server_cmd): + print(f"Server command '{server_cmd}' does not exist.") + assert False server = subprocess.Popen( server_cmd, diff --git a/tests/ten_runtime/integration/nodejs/http_server_nodejs/test_case.py b/tests/ten_runtime/integration/nodejs/http_server_nodejs/test_case.py index bb2e50702c..39a09c17c3 100644 --- a/tests/ten_runtime/integration/nodejs/http_server_nodejs/test_case.py +++ b/tests/ten_runtime/integration/nodejs/http_server_nodejs/test_case.py @@ -69,11 +69,19 @@ def test_http_server_nodejs(): print("Using AddressSanitizer library.") my_env["LD_PRELOAD"] = libasan_path - server_cmd = os.path.join(base_path, "http_server_nodejs_app/bin/start") - - if not os.path.isfile(server_cmd): - print(f"Server command '{server_cmd}' does not exist.") - assert False + if sys.platform == "win32": + server_cmd = [ + sys.executable, + os.path.join(base_path, "http_server_nodejs_app/bin/start.py"), + ] + if not os.path.isfile(server_cmd[1]): + print(f"Server command '{server_cmd[1]}' does not exist.") + assert False + else: + server_cmd = os.path.join(base_path, "http_server_nodejs_app/bin/start") + if not os.path.isfile(server_cmd): + print(f"Server command '{server_cmd}' does not exist.") + assert False server = subprocess.Popen( server_cmd, diff --git a/tests/ten_runtime/integration/nodejs/leak_check_nodejs/test_case.py b/tests/ten_runtime/integration/nodejs/leak_check_nodejs/test_case.py index cf350e9128..19bc4e261c 100644 --- a/tests/ten_runtime/integration/nodejs/leak_check_nodejs/test_case.py +++ b/tests/ten_runtime/integration/nodejs/leak_check_nodejs/test_case.py @@ -76,11 +76,19 @@ def test_leak_check_nodejs(): my_env["TEN_ENABLE_MEMORY_TRACKING"] = "true" my_env["TEN_ENABLE_INTENTIONAL_MEMORY_LEAK"] = "true" - server_cmd = os.path.join(base_path, "leak_check_nodejs_app/bin/start") - - if not os.path.isfile(server_cmd): - print(f"Server command '{server_cmd}' does not exist.") - assert False + if sys.platform == "win32": + server_cmd = [ + sys.executable, + os.path.join(base_path, "leak_check_nodejs_app/bin/start.py"), + ] + if not os.path.isfile(server_cmd[1]): + print(f"Server command '{server_cmd[1]}' does not exist.") + assert False + else: + server_cmd = os.path.join(base_path, "leak_check_nodejs_app/bin/start") + if not os.path.isfile(server_cmd): + print(f"Server command '{server_cmd}' does not exist.") + assert False server = subprocess.Popen( server_cmd, diff --git a/tests/ten_runtime/integration/nodejs/mix_python_ext_nodejs/BUILD.gn b/tests/ten_runtime/integration/nodejs/mix_python_ext_nodejs/BUILD.gn index cd2c557703..34c92882c8 100644 --- a/tests/ten_runtime/integration/nodejs/mix_python_ext_nodejs/BUILD.gn +++ b/tests/ten_runtime/integration/nodejs/mix_python_ext_nodejs/BUILD.gn @@ -22,6 +22,11 @@ ten_package_test_prepare_app("mix_python_ext_nodejs_app") { "mix_python_ext_nodejs_app/bin/bootstrap", ] + if (is_win) { + replace_paths_after_install_all += + [ "mix_python_ext_nodejs_app/bin/bootstrap.py" ] + } + if (ten_enable_ten_manager) { deps = [ "//core/src/ten_manager", diff --git a/tests/ten_runtime/integration/nodejs/mix_python_ext_nodejs/mix_python_ext_nodejs_app/bin/bootstrap.py b/tests/ten_runtime/integration/nodejs/mix_python_ext_nodejs/mix_python_ext_nodejs_app/bin/bootstrap.py new file mode 100644 index 0000000000..a9bdbea0b0 --- /dev/null +++ b/tests/ten_runtime/integration/nodejs/mix_python_ext_nodejs/mix_python_ext_nodejs_app/bin/bootstrap.py @@ -0,0 +1,44 @@ +#!/usr/bin/env python3 +""" +Cross-platform bootstrap script for Python apps on Windows. +On Unix-like systems, prefer using bash bootstrap script for faster startup. +""" + +import os +import sys +import subprocess + +# Change to the project root directory +app_root = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) +os.chdir(app_root) + +# Resolve the dependencies of the Python app and generate the +# 'merged_requirements.txt' file. +deps_resolver = os.path.join( + app_root, + "ten_packages", + "system", + "ten_runtime_python", + "tools", + "deps_resolver.py", +) + +result = subprocess.run([sys.executable, deps_resolver]) +if result.returncode != 0: + print("Error: Failed to resolve the dependencies of the Python app.") + sys.exit(result.returncode) + +merged_requirements = os.path.join(app_root, "merged_requirements.txt") +if os.path.exists(merged_requirements): + print("The 'merged_requirements.txt' file is generated successfully.") + # Pip install the dependencies in the 'merged_requirements.txt' file. + result = subprocess.run( + [sys.executable, "-m", "pip", "install", "-r", merged_requirements] + ) + sys.exit(result.returncode) +else: + print( + "No 'merged_requirements.txt' file is generated, " + "because there are no dependencies." + ) + sys.exit(0) diff --git a/tests/ten_runtime/integration/nodejs/mix_python_ext_nodejs/test_case.py b/tests/ten_runtime/integration/nodejs/mix_python_ext_nodejs/test_case.py index 8b0223caaf..5df31394d5 100644 --- a/tests/ten_runtime/integration/nodejs/mix_python_ext_nodejs/test_case.py +++ b/tests/ten_runtime/integration/nodejs/mix_python_ext_nodejs/test_case.py @@ -66,9 +66,17 @@ def test_mix_python_ext_nodejs(): if rc != 0: assert False, "Failed to build TypeScript extensions." - bootstrap_cmd = os.path.join( - base_path, "mix_python_ext_nodejs_app/bin/bootstrap" - ) + if sys.platform == "win32": + bootstrap_cmd = [ + sys.executable, + os.path.join( + base_path, "mix_python_ext_nodejs_app/bin/bootstrap.py" + ), + ] + else: + bootstrap_cmd = os.path.join( + base_path, "mix_python_ext_nodejs_app/bin/bootstrap" + ) bootstrap_process = subprocess.Popen( bootstrap_cmd, stdout=stdout, stderr=subprocess.STDOUT, env=my_env @@ -89,11 +97,21 @@ def test_mix_python_ext_nodejs(): print("Using AddressSanitizer library.") my_env["LD_PRELOAD"] = libasan_path - server_cmd = os.path.join(base_path, "mix_python_ext_nodejs_app/bin/start") - - if not os.path.isfile(server_cmd): - print(f"Server command '{server_cmd}' does not exist.") - assert False + if sys.platform == "win32": + server_cmd = [ + sys.executable, + os.path.join(base_path, "mix_python_ext_nodejs_app/bin/start.py"), + ] + if not os.path.isfile(server_cmd[1]): + print(f"Server command '{server_cmd[1]}' does not exist.") + assert False + else: + server_cmd = os.path.join( + base_path, "mix_python_ext_nodejs_app/bin/start" + ) + if not os.path.isfile(server_cmd): + print(f"Server command '{server_cmd}' does not exist.") + assert False server = subprocess.Popen( server_cmd, diff --git a/tests/ten_runtime/integration/nodejs/send_audio_frame_nodejs/test_case.py b/tests/ten_runtime/integration/nodejs/send_audio_frame_nodejs/test_case.py index 04fd7633d2..66ec28199b 100644 --- a/tests/ten_runtime/integration/nodejs/send_audio_frame_nodejs/test_case.py +++ b/tests/ten_runtime/integration/nodejs/send_audio_frame_nodejs/test_case.py @@ -69,13 +69,23 @@ def test_send_audio_frame_nodejs(): print("Using AddressSanitizer library.") my_env["LD_PRELOAD"] = libasan_path - server_cmd = os.path.join( - base_path, "send_audio_frame_nodejs_app/bin/start" - ) - - if not os.path.isfile(server_cmd): - print(f"Server command '{server_cmd}' does not exist.") - assert False + if sys.platform == "win32": + server_cmd = [ + sys.executable, + os.path.join( + base_path, "send_audio_frame_nodejs_app/bin/start.py" + ), + ] + if not os.path.isfile(server_cmd[1]): + print(f"Server command '{server_cmd[1]}' does not exist.") + assert False + else: + server_cmd = os.path.join( + base_path, "send_audio_frame_nodejs_app/bin/start" + ) + if not os.path.isfile(server_cmd): + print(f"Server command '{server_cmd}' does not exist.") + assert False server = subprocess.Popen( server_cmd, diff --git a/tests/ten_runtime/integration/nodejs/send_cmd_ex_nodejs/test_case.py b/tests/ten_runtime/integration/nodejs/send_cmd_ex_nodejs/test_case.py index 419879a9c6..bb1096e11d 100644 --- a/tests/ten_runtime/integration/nodejs/send_cmd_ex_nodejs/test_case.py +++ b/tests/ten_runtime/integration/nodejs/send_cmd_ex_nodejs/test_case.py @@ -69,11 +69,19 @@ def test_send_cmd_ex_nodejs(): print("Using AddressSanitizer library.") my_env["LD_PRELOAD"] = libasan_path - server_cmd = os.path.join(base_path, "send_cmd_ex_nodejs_app/bin/start") - - if not os.path.isfile(server_cmd): - print(f"Server command '{server_cmd}' does not exist.") - assert False + if sys.platform == "win32": + server_cmd = [ + sys.executable, + os.path.join(base_path, "send_cmd_ex_nodejs_app/bin/start.py"), + ] + if not os.path.isfile(server_cmd[1]): + print(f"Server command '{server_cmd[1]}' does not exist.") + assert False + else: + server_cmd = os.path.join(base_path, "send_cmd_ex_nodejs_app/bin/start") + if not os.path.isfile(server_cmd): + print(f"Server command '{server_cmd}' does not exist.") + assert False server = subprocess.Popen( server_cmd, diff --git a/tests/ten_runtime/integration/nodejs/send_cmd_nodejs/test_case.py b/tests/ten_runtime/integration/nodejs/send_cmd_nodejs/test_case.py index 234c92c3b8..729f7b9a1b 100644 --- a/tests/ten_runtime/integration/nodejs/send_cmd_nodejs/test_case.py +++ b/tests/ten_runtime/integration/nodejs/send_cmd_nodejs/test_case.py @@ -69,11 +69,19 @@ def test_send_cmd_nodejs(): print("Using AddressSanitizer library.") my_env["LD_PRELOAD"] = libasan_path - server_cmd = os.path.join(base_path, "send_cmd_nodejs_app/bin/start") - - if not os.path.isfile(server_cmd): - print(f"Server command '{server_cmd}' does not exist.") - assert False + if sys.platform == "win32": + server_cmd = [ + sys.executable, + os.path.join(base_path, "send_cmd_nodejs_app/bin/start.py"), + ] + if not os.path.isfile(server_cmd[1]): + print(f"Server command '{server_cmd[1]}' does not exist.") + assert False + else: + server_cmd = os.path.join(base_path, "send_cmd_nodejs_app/bin/start") + if not os.path.isfile(server_cmd): + print(f"Server command '{server_cmd}' does not exist.") + assert False server = subprocess.Popen( server_cmd, diff --git a/tests/ten_runtime/integration/nodejs/send_data_nodejs/test_case.py b/tests/ten_runtime/integration/nodejs/send_data_nodejs/test_case.py index 12eab3b352..72f9841536 100644 --- a/tests/ten_runtime/integration/nodejs/send_data_nodejs/test_case.py +++ b/tests/ten_runtime/integration/nodejs/send_data_nodejs/test_case.py @@ -69,11 +69,19 @@ def test_send_data_nodejs(): print("Using AddressSanitizer library.") my_env["LD_PRELOAD"] = libasan_path - server_cmd = os.path.join(base_path, "send_data_nodejs_app/bin/start") - - if not os.path.isfile(server_cmd): - print(f"Server command '{server_cmd}' does not exist.") - assert False + if sys.platform == "win32": + server_cmd = [ + sys.executable, + os.path.join(base_path, "send_data_nodejs_app/bin/start.py"), + ] + if not os.path.isfile(server_cmd[1]): + print(f"Server command '{server_cmd[1]}' does not exist.") + assert False + else: + server_cmd = os.path.join(base_path, "send_data_nodejs_app/bin/start") + if not os.path.isfile(server_cmd): + print(f"Server command '{server_cmd}' does not exist.") + assert False server = subprocess.Popen( server_cmd, diff --git a/tests/ten_runtime/integration/nodejs/send_video_frame_nodejs/test_case.py b/tests/ten_runtime/integration/nodejs/send_video_frame_nodejs/test_case.py index 33fcd06050..3eda36a65d 100644 --- a/tests/ten_runtime/integration/nodejs/send_video_frame_nodejs/test_case.py +++ b/tests/ten_runtime/integration/nodejs/send_video_frame_nodejs/test_case.py @@ -69,13 +69,23 @@ def test_send_video_frame_nodejs(): print("Using AddressSanitizer library.") my_env["LD_PRELOAD"] = libasan_path - server_cmd = os.path.join( - base_path, "send_video_frame_nodejs_app/bin/start" - ) - - if not os.path.isfile(server_cmd): - print(f"Server command '{server_cmd}' does not exist.") - assert False + if sys.platform == "win32": + server_cmd = [ + sys.executable, + os.path.join( + base_path, "send_video_frame_nodejs_app/bin/start.py" + ), + ] + if not os.path.isfile(server_cmd[1]): + print(f"Server command '{server_cmd[1]}' does not exist.") + assert False + else: + server_cmd = os.path.join( + base_path, "send_video_frame_nodejs_app/bin/start" + ) + if not os.path.isfile(server_cmd): + print(f"Server command '{server_cmd}' does not exist.") + assert False server = subprocess.Popen( server_cmd, diff --git a/tests/ten_runtime/integration/nodejs/set_dest_nodejs/test_case.py b/tests/ten_runtime/integration/nodejs/set_dest_nodejs/test_case.py index a6d0ee34f1..1b734a0d93 100644 --- a/tests/ten_runtime/integration/nodejs/set_dest_nodejs/test_case.py +++ b/tests/ten_runtime/integration/nodejs/set_dest_nodejs/test_case.py @@ -69,11 +69,19 @@ def test_set_dest_nodejs(): print("Using AddressSanitizer library.") my_env["LD_PRELOAD"] = libasan_path - server_cmd = os.path.join(base_path, "set_dest_nodejs_app/bin/start") - - if not os.path.isfile(server_cmd): - print(f"Server command '{server_cmd}' does not exist.") - assert False + if sys.platform == "win32": + server_cmd = [ + sys.executable, + os.path.join(base_path, "set_dest_nodejs_app/bin/start.py"), + ] + if not os.path.isfile(server_cmd[1]): + print(f"Server command '{server_cmd[1]}' does not exist.") + assert False + else: + server_cmd = os.path.join(base_path, "set_dest_nodejs_app/bin/start") + if not os.path.isfile(server_cmd): + print(f"Server command '{server_cmd}' does not exist.") + assert False server = subprocess.Popen( server_cmd, diff --git a/tests/ten_runtime/integration/nodejs/standalone_test_nodejs/test_case.py b/tests/ten_runtime/integration/nodejs/standalone_test_nodejs/test_case.py index 2a0d382ce1..62b56f7ef8 100644 --- a/tests/ten_runtime/integration/nodejs/standalone_test_nodejs/test_case.py +++ b/tests/ten_runtime/integration/nodejs/standalone_test_nodejs/test_case.py @@ -127,12 +127,18 @@ def test_standalone_test_nodejs(): # Step 4: # # Run the test. - test_cmd = [ - "tests/bin/start", - ] - - if sys.platform == "linux" and build_config_args.enable_sanitizer: - test_cmd.append("-asan") + if sys.platform == "win32": + test_cmd = [ + sys.executable, + "tests/bin/start.py", + ] + else: + test_cmd = [ + "tests/bin/start", + ] + + if build_config_args.enable_sanitizer: + test_cmd.append("-asan") tester_process = subprocess.Popen( test_cmd, diff --git a/tests/ten_runtime/integration/nodejs/standalone_test_nodejs_2/test_case.py b/tests/ten_runtime/integration/nodejs/standalone_test_nodejs_2/test_case.py index 4115dbb479..e05d74d85e 100644 --- a/tests/ten_runtime/integration/nodejs/standalone_test_nodejs_2/test_case.py +++ b/tests/ten_runtime/integration/nodejs/standalone_test_nodejs_2/test_case.py @@ -96,12 +96,18 @@ def test_standalone_test_nodejs_2(): # Step 3: # # Run the test. - test_cmd = [ - "tests/bin/start", - ] - - if sys.platform == "linux" and build_config_args.enable_sanitizer: - test_cmd.append("-asan") + if sys.platform == "win32": + test_cmd = [ + sys.executable, + "tests/bin/start.py", + ] + else: + test_cmd = [ + "tests/bin/start", + ] + + if build_config_args.enable_sanitizer: + test_cmd.append("-asan") tester_process = subprocess.Popen( test_cmd, diff --git a/tests/ten_runtime/integration/nodejs/standalone_test_nodejs_3/test_case.py b/tests/ten_runtime/integration/nodejs/standalone_test_nodejs_3/test_case.py index cf316598ea..3d56956424 100644 --- a/tests/ten_runtime/integration/nodejs/standalone_test_nodejs_3/test_case.py +++ b/tests/ten_runtime/integration/nodejs/standalone_test_nodejs_3/test_case.py @@ -96,12 +96,18 @@ def test_standalone_test_nodejs_3(): # Step 3: # # Run the test. - test_cmd = [ - "tests/bin/start", - ] - - if sys.platform == "linux" and build_config_args.enable_sanitizer: - test_cmd.append("-asan") + if sys.platform == "win32": + test_cmd = [ + sys.executable, + "tests/bin/start.py", + ] + else: + test_cmd = [ + "tests/bin/start", + ] + + if build_config_args.enable_sanitizer: + test_cmd.append("-asan") tester_process = subprocess.Popen( test_cmd, diff --git a/tests/ten_runtime/integration/nodejs/start_graph_and_communicate_nodejs/test_case.py b/tests/ten_runtime/integration/nodejs/start_graph_and_communicate_nodejs/test_case.py index 10a3598946..40481ca781 100644 --- a/tests/ten_runtime/integration/nodejs/start_graph_and_communicate_nodejs/test_case.py +++ b/tests/ten_runtime/integration/nodejs/start_graph_and_communicate_nodejs/test_case.py @@ -58,18 +58,31 @@ def test_start_graph_and_communicate_nodejs(): print("Using AddressSanitizer library.") my_env["LD_PRELOAD"] = libasan_path - server_cmd = os.path.join( - base_path, - "start_graph_and_communicate_nodejs_app/bin/start", - ) - - client_cmd = os.path.join( - base_path, "start_graph_and_communicate_nodejs_app_client" - ) - - if not os.path.isfile(server_cmd): - print(f"Server command '{server_cmd}' does not exist.") - assert False + if sys.platform == "win32": + server_cmd = [ + sys.executable, + os.path.join( + base_path, + "start_graph_and_communicate_nodejs_app/bin/start.py", + ), + ] + client_cmd = os.path.join( + base_path, "start_graph_and_communicate_nodejs_app_client.exe" + ) + if not os.path.isfile(server_cmd[1]): + print(f"Server command '{server_cmd[1]}' does not exist.") + assert False + else: + server_cmd = os.path.join( + base_path, + "start_graph_and_communicate_nodejs_app/bin/start", + ) + client_cmd = os.path.join( + base_path, "start_graph_and_communicate_nodejs_app_client" + ) + if not os.path.isfile(server_cmd): + print(f"Server command '{server_cmd}' does not exist.") + assert False server = subprocess.Popen( server_cmd, diff --git a/tests/ten_runtime/integration/nodejs/start_graph_and_set_dests_nodejs/test_case.py b/tests/ten_runtime/integration/nodejs/start_graph_and_set_dests_nodejs/test_case.py index 018defe8e3..e477d6c698 100644 --- a/tests/ten_runtime/integration/nodejs/start_graph_and_set_dests_nodejs/test_case.py +++ b/tests/ten_runtime/integration/nodejs/start_graph_and_set_dests_nodejs/test_case.py @@ -58,18 +58,31 @@ def test_start_graph_and_set_dests_nodejs(): print("Using AddressSanitizer library.") my_env["LD_PRELOAD"] = libasan_path - server_cmd = os.path.join( - base_path, - "start_graph_and_set_dests_nodejs_app/bin/start", - ) - - client_cmd = os.path.join( - base_path, "start_graph_and_set_dests_nodejs_app_client" - ) - - if not os.path.isfile(server_cmd): - print(f"Server command '{server_cmd}' does not exist.") - assert False + if sys.platform == "win32": + server_cmd = [ + sys.executable, + os.path.join( + base_path, + "start_graph_and_set_dests_nodejs_app/bin/start.py", + ), + ] + client_cmd = os.path.join( + base_path, "start_graph_and_set_dests_nodejs_app_client.exe" + ) + if not os.path.isfile(server_cmd[1]): + print(f"Server command '{server_cmd[1]}' does not exist.") + assert False + else: + server_cmd = os.path.join( + base_path, + "start_graph_and_set_dests_nodejs_app/bin/start", + ) + client_cmd = os.path.join( + base_path, "start_graph_and_set_dests_nodejs_app_client" + ) + if not os.path.isfile(server_cmd): + print(f"Server command '{server_cmd}' does not exist.") + assert False server = subprocess.Popen( server_cmd, diff --git a/tests/ten_runtime/integration/nodejs/trigger_life_cycle_nodejs/test_case.py b/tests/ten_runtime/integration/nodejs/trigger_life_cycle_nodejs/test_case.py index b53c585caa..dc4f1a0c7c 100644 --- a/tests/ten_runtime/integration/nodejs/trigger_life_cycle_nodejs/test_case.py +++ b/tests/ten_runtime/integration/nodejs/trigger_life_cycle_nodejs/test_case.py @@ -69,13 +69,23 @@ def test_trigger_life_cycle_nodejs(): print("Using AddressSanitizer library.") my_env["LD_PRELOAD"] = libasan_path - server_cmd = os.path.join( - base_path, "trigger_life_cycle_nodejs_app/bin/start" - ) - - if not os.path.isfile(server_cmd): - print(f"Server command '{server_cmd}' does not exist.") - assert False + if sys.platform == "win32": + server_cmd = [ + sys.executable, + os.path.join( + base_path, "trigger_life_cycle_nodejs_app/bin/start.py" + ), + ] + if not os.path.isfile(server_cmd[1]): + print(f"Server command '{server_cmd[1]}' does not exist.") + assert False + else: + server_cmd = os.path.join( + base_path, "trigger_life_cycle_nodejs_app/bin/start" + ) + if not os.path.isfile(server_cmd): + print(f"Server command '{server_cmd}' does not exist.") + assert False server = subprocess.Popen( server_cmd, diff --git a/tests/ten_runtime/integration/nodejs/websocket_server_nodejs/test_case.py b/tests/ten_runtime/integration/nodejs/websocket_server_nodejs/test_case.py index 3b02d40a48..7c47d86fc2 100644 --- a/tests/ten_runtime/integration/nodejs/websocket_server_nodejs/test_case.py +++ b/tests/ten_runtime/integration/nodejs/websocket_server_nodejs/test_case.py @@ -92,13 +92,23 @@ def test_websocket_server_nodejs(): print("Using AddressSanitizer library.") my_env["LD_PRELOAD"] = libasan_path - server_cmd = os.path.join( - base_path, "websocket_server_nodejs_app/bin/start" - ) - - if not os.path.isfile(server_cmd): - print(f"Server command '{server_cmd}' does not exist.") - assert False + if sys.platform == "win32": + server_cmd = [ + sys.executable, + os.path.join( + base_path, "websocket_server_nodejs_app/bin/start.py" + ), + ] + if not os.path.isfile(server_cmd[1]): + print(f"Server command '{server_cmd[1]}' does not exist.") + assert False + else: + server_cmd = os.path.join( + base_path, "websocket_server_nodejs_app/bin/start" + ) + if not os.path.isfile(server_cmd): + print(f"Server command '{server_cmd}' does not exist.") + assert False server = subprocess.Popen( server_cmd, From 7c4c4f2b580b5adbdf1155e9d462146db8d70028 Mon Sep 17 00:00:00 2001 From: Nie Zhihe Date: Mon, 9 Feb 2026 16:14:49 +0800 Subject: [PATCH 04/16] feat: windows nodejs build --- .gitignore | 3 + core/src/ten_runtime/binding/nodejs/BUILD.gn | 6 + .../binding/nodejs/interface/ten_addon.ts | 14 ++- .../binding/nodejs/native/BUILD.gn | 4 + core/src/ten_runtime/output_libs.gni | 18 ++- third_party/node/BUILD.gn | 107 ++++++++++++------ 6 files changed, 115 insertions(+), 37 deletions(-) diff --git a/.gitignore b/.gitignore index d43086e002..d52a75675c 100644 --- a/.gitignore +++ b/.gitignore @@ -19,6 +19,9 @@ __pycache__/ *.a *.o +# Do not ignore prebuilt node libraries for Windows +!third_party/node/lib/win/x64/*.dll + # Log *.log diff --git a/core/src/ten_runtime/binding/nodejs/BUILD.gn b/core/src/ten_runtime/binding/nodejs/BUILD.gn index 1b0ad4a602..54f010af50 100644 --- a/core/src/ten_runtime/binding/nodejs/BUILD.gn +++ b/core/src/ten_runtime/binding/nodejs/BUILD.gn @@ -53,6 +53,12 @@ ten_package("ten_nodejs_binding_system_package") { resources += [ "${lib}=>lib/${libname}" ] } + # On Windows, include libnode.dll in the package since ten_runtime_nodejs.node + # depends on it. + if (is_win) { + resources += [ "//third_party/node/lib/win/x64/libnode.dll=>lib/libnode.dll" ] + } + deps = [ ":ten_runtime_nodejs_js", "native:ten_runtime_nodejs", diff --git a/core/src/ten_runtime/binding/nodejs/interface/ten_addon.ts b/core/src/ten_runtime/binding/nodejs/interface/ten_addon.ts index 13451246a7..11506e0ab5 100644 --- a/core/src/ten_runtime/binding/nodejs/interface/ten_addon.ts +++ b/core/src/ten_runtime/binding/nodejs/interface/ten_addon.ts @@ -17,9 +17,19 @@ try { // They can instead be loaded with `module.createRequire()` or // `process.dlopen`. const require = createRequire(import.meta.url); - addon = require("libten_runtime_nodejs"); + + // Try different module names based on platform: + // - Linux/macOS: libten_runtime_nodejs (with lib prefix) + // - Windows MSVC: ten_runtime_nodejs (without lib prefix) + // - Windows MinGW: libten_runtime_nodejs (with lib prefix) + try { + addon = require("libten_runtime_nodejs"); + } catch { + // Fallback for Windows MSVC builds + addon = require("ten_runtime_nodejs"); + } } catch (e) { - console.error(`Failed to load libten_runtime_nodejs module: ${e}`); + console.error(`Failed to load ten_runtime_nodejs module: ${e}`); } export default addon as unknown as typeof import("libten_runtime_nodejs"); diff --git a/core/src/ten_runtime/binding/nodejs/native/BUILD.gn b/core/src/ten_runtime/binding/nodejs/native/BUILD.gn index 768a59e68a..c5901b696c 100644 --- a/core/src/ten_runtime/binding/nodejs/native/BUILD.gn +++ b/core/src/ten_runtime/binding/nodejs/native/BUILD.gn @@ -34,6 +34,9 @@ ten_shared_library("ten_runtime_nodejs") { } else if (is_linux || is_mingw) { # MinGW uses GNU ld and supports rpath ldflags = [ "-Wl,-rpath=\$ORIGIN/../../ten_runtime/lib" ] + } else if (is_win && !is_mingw) { + # MSVC: link against node import library + ldflags = [] } sources = [ "init.c" ] @@ -48,5 +51,6 @@ ten_shared_library("ten_runtime_nodejs") { "ten_env", "test", "//core/src/ten_runtime:ten_runtime_library", + "//third_party/node", ] } diff --git a/core/src/ten_runtime/output_libs.gni b/core/src/ten_runtime/output_libs.gni index 2bc3148d9f..1ebd15d6e9 100644 --- a/core/src/ten_runtime/output_libs.gni +++ b/core/src/ten_runtime/output_libs.gni @@ -58,5 +58,19 @@ if (is_linux) { # Note: ten_runtime_go_output_libs is no longer used since it's # a static library now. -ten_runtime_nodejs_output_libs = - [ "${root_out_dir}/libten_runtime_nodejs.node" ] +if (is_win) { + if (is_mingw) { + # MinGW: .node extension with lib prefix + ten_runtime_nodejs_output_libs = [ + "${root_out_dir}/libten_runtime_nodejs.node", + ] + } else { + # MSVC: .node extension without lib prefix + ten_runtime_nodejs_output_libs = [ + "${root_out_dir}/ten_runtime_nodejs.node", + ] + } +} else { + ten_runtime_nodejs_output_libs = + [ "${root_out_dir}/libten_runtime_nodejs.node" ] +} diff --git a/third_party/node/BUILD.gn b/third_party/node/BUILD.gn index ea7fdd0d96..2e33ae3f84 100644 --- a/third_party/node/BUILD.gn +++ b/third_party/node/BUILD.gn @@ -16,48 +16,89 @@ config("node_header") { ] } -ten_prebuilt_library("prebuilt_node_shared") { - name = "node_shared" +if (is_win) { + # Windows uses local prebuilt libraries + config("node_lib_config") { + lib_dirs = [ "//third_party/node/lib/win/x64" ] - # Version can be easily updated here - _node_shared_version = "v1.0.15" - _base_url = "https://github.com/TEN-framework/node_shared/releases/download/${_node_shared_version}" + if (is_mingw) { + # MinGW uses libnode.lib directly (same as MSVC import library) + libs = [ "libnode" ] + } else { + # MSVC: Link against libnode.lib (import library for libnode.dll) + libs = [ "libnode.lib" ] + } + } + + # Copy libnode.dll to output directory + action("copy_node_dll") { + script = "//.gnfiles/build/scripts/copy_fs_entry.py" + + _source_dll = "//third_party/node/lib/win/x64/libnode.dll" + _dest_dll = "${root_out_dir}/libnode.dll" - if (is_linux) { - if (target_cpu == "x64") { - if (is_clang) { - url = "${_base_url}/node-shared-linux-x64-clang.zip" + args = [ + "--source", + rebase_path(_source_dll), + "--destination", + rebase_path(_dest_dll), + ] + + sources = [ _source_dll ] + outputs = [ _dest_dll ] + } + + group("node") { + public_configs = [ + ":node_header", + ":node_lib_config", + ] + public_deps = [ ":copy_node_dll" ] + } +} else { + ten_prebuilt_library("prebuilt_node_shared") { + name = "node_shared" + + # Version can be easily updated here + _node_shared_version = "v1.0.15" + _base_url = "https://github.com/TEN-framework/node_shared/releases/download/${_node_shared_version}" + + if (is_linux) { + if (target_cpu == "x64") { + if (is_clang) { + url = "${_base_url}/node-shared-linux-x64-clang.zip" + } else { + url = "${_base_url}/node-shared-linux-x64-gcc.zip" + } + } else if (target_cpu == "arm64") { + if (is_clang) { + url = "${_base_url}/node-shared-linux-arm64-clang.zip" + } else { + url = "${_base_url}/node-shared-linux-arm64-gcc.zip" + } } else { - url = "${_base_url}/node-shared-linux-x64-gcc.zip" + assert(false, "Unsupported Linux CPU architecture: ${target_cpu}") } - } else if (target_cpu == "arm64") { - if (is_clang) { - url = "${_base_url}/node-shared-linux-arm64-clang.zip" + } else if (is_mac) { + if (target_cpu == "x64") { + url = "${_base_url}/node-shared-mac-x64-clang.zip" + } else if (target_cpu == "arm64") { + url = "${_base_url}/node-shared-mac-arm64-clang.zip" } else { - url = "${_base_url}/node-shared-linux-arm64-gcc.zip" + assert(false, "Unsupported Mac CPU architecture: ${target_cpu}") } } else { - assert(false, "Unsupported Linux CPU architecture: ${target_cpu}") - } - } else if (is_mac) { - if (target_cpu == "x64") { - url = "${_base_url}/node-shared-mac-x64-clang.zip" - } else if (target_cpu == "arm64") { - url = "${_base_url}/node-shared-mac-arm64-clang.zip" - } else { - assert(false, "Unsupported Mac CPU architecture: ${target_cpu}") + assert(false, "Unsupported platform") } - } else { - assert(false, "Unsupported platform") - } - is_shared_library = true + is_shared_library = true - libs = [ "node" ] - lib_version = 127 -} + libs = [ "node" ] + lib_version = 127 + } -group("node") { - public_configs = [ ":node_header" ] - public_deps = [ ":prebuilt_node_shared" ] + group("node") { + public_configs = [ ":node_header" ] + public_deps = [ ":prebuilt_node_shared" ] + } } From 1125eab3b594b20c3edd8d5c0275a154b231ca7e Mon Sep 17 00:00:00 2001 From: Nie Zhihe Date: Tue, 10 Feb 2026 15:32:20 +0800 Subject: [PATCH 05/16] fix: delay load+hook --- core/src/ten_runtime/binding/nodejs/BUILD.gn | 6 -- .../binding/nodejs/native/BUILD.gn | 30 +++++++- .../nodejs/native/win_delay_load_hook.cc | 68 +++++++++++++++++++ 3 files changed, 96 insertions(+), 8 deletions(-) create mode 100644 core/src/ten_runtime/binding/nodejs/native/win_delay_load_hook.cc diff --git a/core/src/ten_runtime/binding/nodejs/BUILD.gn b/core/src/ten_runtime/binding/nodejs/BUILD.gn index 54f010af50..1b0ad4a602 100644 --- a/core/src/ten_runtime/binding/nodejs/BUILD.gn +++ b/core/src/ten_runtime/binding/nodejs/BUILD.gn @@ -53,12 +53,6 @@ ten_package("ten_nodejs_binding_system_package") { resources += [ "${lib}=>lib/${libname}" ] } - # On Windows, include libnode.dll in the package since ten_runtime_nodejs.node - # depends on it. - if (is_win) { - resources += [ "//third_party/node/lib/win/x64/libnode.dll=>lib/libnode.dll" ] - } - deps = [ ":ten_runtime_nodejs_js", "native:ten_runtime_nodejs", diff --git a/core/src/ten_runtime/binding/nodejs/native/BUILD.gn b/core/src/ten_runtime/binding/nodejs/native/BUILD.gn index c5901b696c..1923f51d80 100644 --- a/core/src/ten_runtime/binding/nodejs/native/BUILD.gn +++ b/core/src/ten_runtime/binding/nodejs/native/BUILD.gn @@ -35,12 +35,38 @@ ten_shared_library("ten_runtime_nodejs") { # MinGW uses GNU ld and supports rpath ldflags = [ "-Wl,-rpath=\$ORIGIN/../../ten_runtime/lib" ] } else if (is_win && !is_mingw) { - # MSVC: link against node import library - ldflags = [] + # MSVC/clang-cl: Use /DELAYLOAD to avoid loading libnode.dll at startup. + # N-API symbols are provided by the host node.exe at runtime, so we don't + # actually need libnode.dll. But MSVC requires linking to resolve symbols. + # With delay load, the DLL won't be loaded unless we call functions from it, + # and since node.exe provides these symbols, libnode.dll is never loaded. + # + # The win_delay_load_hook.cc file implements a delay-load hook that: + # 1. For pure Node.js apps: returns handle to node.exe (which exports N-API) + # 2. For embedded scenarios: returns handle to libnode.dll if loaded + ldflags = [ + "/DELAYLOAD:libnode.dll", + + # Suppress linker warning LNK4199: /DELAYLOAD:libnode.dll ignored; + # no imports found from libnode.dll. This warning occurs because the + # delay-load hook redirects all imports before they reach libnode.dll. + "/ignore:4199", + ] + + # The delay import library provided by Microsoft. Required for delay-load + # support. + libs = [ "delayimp.lib" ] } sources = [ "init.c" ] + if (is_win && !is_mingw) { + # Include the delay-load hook for Windows MSVC/clang-cl builds. + # This hook intercepts the delay-load of libnode.dll and redirects it + # to either the already-loaded libnode.dll or the host node.exe. + sources += [ "win_delay_load_hook.cc" ] + } + deps = [ "addon", "app", diff --git a/core/src/ten_runtime/binding/nodejs/native/win_delay_load_hook.cc b/core/src/ten_runtime/binding/nodejs/native/win_delay_load_hook.cc new file mode 100644 index 0000000000..e57402b989 --- /dev/null +++ b/core/src/ten_runtime/binding/nodejs/native/win_delay_load_hook.cc @@ -0,0 +1,68 @@ +/* + * Copyright © 2025 Agora + * This file is part of TEN Framework, an open source project. + * Licensed under the Apache License, Version 2.0, with certain conditions. + * Refer to the "LICENSE" file in the root directory for more information. + * + * + * Ref: https://github.com/nodejs/node-gyp/blob/main/src/win_delay_load_hook.cc + * + * When this file is linked to a DLL, it sets up a delay-load hook that + * intervenes when the DLL is trying to load libnode.dll dynamically. + * + * For pure Node.js apps (started via node.exe), the N-API symbols are + * provided by node.exe itself, so we return a handle to the current process. + * + * For embedded Node.js scenarios (C++/Go apps with nodejs_addon_loader), + * libnode.dll is loaded separately, so we try to get its handle first. + * + * This allows the addon to work in both scenarios without requiring + * libnode.dll to be present for pure Node.js apps. + */ + +#ifdef _MSC_VER + +#pragma managed(push, off) + +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif + +#include + +#include +#include + +static FARPROC WINAPI load_exe_hook(unsigned int event, DelayLoadInfo* info) { + HMODULE m; + + if (event != dliNotePreLoadLibrary) { + return NULL; + } + + // Check if the DLL being loaded is libnode.dll + if (_stricmp(info->szDll, "libnode.dll") != 0) { + return NULL; + } + + // First, try to get libnode.dll if it's already loaded + // (for embedded Node.js scenarios like C++/Go apps with nodejs_addon_loader) + m = GetModuleHandle(TEXT("libnode.dll")); + + if (m == NULL) { + // libnode.dll is not loaded, we're running in a pure Node.js app + // where node.exe provides the N-API symbols directly. + // Return the handle to the current process (node.exe). + m = GetModuleHandle(NULL); + } + + return (FARPROC)m; +} + +// Register the delay-load hook. +// This symbol overrides the default delay-load helper in delayimp.lib. +decltype(__pfnDliNotifyHook2) __pfnDliNotifyHook2 = load_exe_hook; + +#pragma managed(pop) + +#endif From dc46398f0b41721c63e401fa613c11955d313b06 Mon Sep 17 00:00:00 2001 From: Nie Zhihe Date: Tue, 10 Feb 2026 15:41:52 +0800 Subject: [PATCH 06/16] fix: use pathToFileURL to standardize URL format for ESM --- .../binding/nodejs/interface/addon_manager.ts | 24 ++++++++++++++----- 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/core/src/ten_runtime/binding/nodejs/interface/addon_manager.ts b/core/src/ten_runtime/binding/nodejs/interface/addon_manager.ts index a849e7c4ab..80c8347863 100644 --- a/core/src/ten_runtime/binding/nodejs/interface/addon_manager.ts +++ b/core/src/ten_runtime/binding/nodejs/interface/addon_manager.ts @@ -7,13 +7,13 @@ import * as fs from "fs"; import * as path from "path"; import { dirname } from "path"; -import { fileURLToPath } from "url"; +import { fileURLToPath, pathToFileURL } from "url"; import type { Addon } from "./addon.js"; import ten_addon from "./ten_addon.js"; type Ctor = { - new (): T; + new(): T; prototype: T; }; @@ -98,7 +98,7 @@ export class AddonManager { const dirs = fs.opendirSync(extension_folder); const loadPromises = []; - for (;;) { + for (; ;) { const entry = dirs.readSync(); if (!entry) { break; @@ -113,9 +113,17 @@ export class AddonManager { if (fs.existsSync(packageJsonFile)) { // Log the extension name. console.log(`_load_all_addons Loading extension ${entry.name}`); - loadPromises.push( - import(`${extension_folder}/${entry.name}/build/index.js`), + + // On Windows, ESM dynamic import() requires file:// URLs, not raw paths. + // pathToFileURL() converts a path like "C:\foo\bar" to "file:///C:/foo/bar". + // Ref: https://nodejs.org/api/esm.html#urls + const modulePath = path.join( + extension_folder, + entry.name, + "build", + "index.js", ); + loadPromises.push(import(pathToFileURL(modulePath).href)); } } @@ -148,7 +156,11 @@ export class AddonManager { } try { - await import(`${extension_folder}/build/index.js`); + // On Windows, ESM dynamic import() requires file:// URLs, not raw paths. + // pathToFileURL() converts a path like "C:\foo\bar" to "file:///C:/foo/bar". + // Ref: https://nodejs.org/api/esm.html#urls + const modulePath = path.join(extension_folder, "build", "index.js"); + await import(pathToFileURL(modulePath).href); console.log(`Addon ${name} loaded`); return true; } catch (error) { From 3f710ccf4c7743da2795289aeeea799003be5d3c Mon Sep 17 00:00:00 2001 From: Nie Zhihe Date: Tue, 10 Feb 2026 19:56:17 +0800 Subject: [PATCH 07/16] fix: cpp app+node extension tests --- build/ten_runtime/feature/cmake.py | 12 +- core/src/ten_runtime/binding/nodejs/BUILD.gn | 7 + .../binding/nodejs/native/BUILD.gn | 138 ++++++++++++------ core/src/ten_runtime/output_libs.gni | 9 ++ core/ten_gn | 2 +- .../nodejs_addon_loader/BUILD.gn | 40 +++++ .../nodejs_addon_loader/src/main.cc | 7 +- 7 files changed, 161 insertions(+), 54 deletions(-) diff --git a/build/ten_runtime/feature/cmake.py b/build/ten_runtime/feature/cmake.py index 2c94ce3aa3..f56f9bac42 100644 --- a/build/ten_runtime/feature/cmake.py +++ b/build/ten_runtime/feature/cmake.py @@ -359,14 +359,22 @@ def clean(self): # # cmd = ' '.join(self.env) + f' cmake --build {self.args.build_path} # --target clean' - cmd = f"rm -rf {self.args.build_path}" + import platform + if platform.system() == "Windows": + cmd = f'rmdir /s /q "{self.args.build_path}"' + else: + cmd = f"rm -rf {self.args.build_path}" if self.args.log_level > 1: print(f"> {cmd}") returncode, output_text = cmd_exec.run_cmd_realtime( cmd, log_level=self.args.log_level ) if returncode != 0: - print(output_text) + if output_text: + try: + print(output_text) + except UnicodeEncodeError: + print(output_text.encode("ascii", errors="replace").decode("ascii")) raise RuntimeError("Failed to cmake clean.") def build(self): diff --git a/core/src/ten_runtime/binding/nodejs/BUILD.gn b/core/src/ten_runtime/binding/nodejs/BUILD.gn index 1b0ad4a602..7a912ceb8c 100644 --- a/core/src/ten_runtime/binding/nodejs/BUILD.gn +++ b/core/src/ten_runtime/binding/nodejs/BUILD.gn @@ -57,6 +57,13 @@ ten_package("ten_nodejs_binding_system_package") { ":ten_runtime_nodejs_js", "native:ten_runtime_nodejs", ] + + # When forcing MSVC for nodejs binding, add explicit dependency on the + # msvc_force toolchain target and use its output path. + if (is_win && !is_mingw && is_clang) { + _msvc_toolchain = "//.gnfiles/build/toolchain/msvc_force:msvc_force" + deps += [ "native:ten_runtime_nodejs_msvc(${_msvc_toolchain})" ] + } } if (ten_enable_ten_manager) { diff --git a/core/src/ten_runtime/binding/nodejs/native/BUILD.gn b/core/src/ten_runtime/binding/nodejs/native/BUILD.gn index 1923f51d80..b2cc440e44 100644 --- a/core/src/ten_runtime/binding/nodejs/native/BUILD.gn +++ b/core/src/ten_runtime/binding/nodejs/native/BUILD.gn @@ -18,65 +18,107 @@ config("ten_runtime_nodejs_config") { } } -ten_shared_library("ten_runtime_nodejs") { - configs = [ ":ten_runtime_nodejs_config" ] +# When under the msvc_force toolchain, build with a different target name. +# All deps are pinned back to the default clang toolchain so that only +# this target's own sources (init.c, win_delay_load_hook.cc) are compiled +# with cl.exe. The .obj files from clang-cl and cl.exe are ABI-compatible +# (both use MSVC ABI) so mixed linking is safe. +if (current_toolchain == "//.gnfiles/build/toolchain/msvc_force:msvc_force" && + current_toolchain != default_toolchain) { + _default_tc = "//.gnfiles/build/platform/win:clang" - output_extension = "node" + shared_library("ten_runtime_nodejs_msvc") { + output_name = "ten_runtime_nodejs" + output_extension = "node" - if (is_mac || is_linux) { - add_configs = [ "//.gnfiles/build/toolchain/common:allow_undefined" ] - remove_configs = [ "//.gnfiles/build/toolchain/common:disallow_undefined" ] - } + configs += [ ":ten_runtime_nodejs_config(${_default_tc})" ] + + include_dirs = ten_runtime_common_includes + + defines = ten_runtime_common_defines + + cflags = common_cflags + cflags_c = common_cflags_c + cflags_cc = common_cflags_cc - # Add rpath to find ten_runtime library. - if (is_mac) { - ldflags = [ "-Wl,-rpath,@loader_path/../../ten_runtime/lib" ] - } else if (is_linux || is_mingw) { - # MinGW uses GNU ld and supports rpath - ldflags = [ "-Wl,-rpath=\$ORIGIN/../../ten_runtime/lib" ] - } else if (is_win && !is_mingw) { - # MSVC/clang-cl: Use /DELAYLOAD to avoid loading libnode.dll at startup. - # N-API symbols are provided by the host node.exe at runtime, so we don't - # actually need libnode.dll. But MSVC requires linking to resolve symbols. - # With delay load, the DLL won't be loaded unless we call functions from it, - # and since node.exe provides these symbols, libnode.dll is never loaded. - # - # The win_delay_load_hook.cc file implements a delay-load hook that: - # 1. For pure Node.js apps: returns handle to node.exe (which exports N-API) - # 2. For embedded scenarios: returns handle to libnode.dll if loaded ldflags = [ "/DELAYLOAD:libnode.dll", - - # Suppress linker warning LNK4199: /DELAYLOAD:libnode.dll ignored; - # no imports found from libnode.dll. This warning occurs because the - # delay-load hook redirects all imports before they reach libnode.dll. "/ignore:4199", ] + ldflags += common_ldflags - # The delay import library provided by Microsoft. Required for delay-load - # support. libs = [ "delayimp.lib" ] - } + libs += common_libs + + lib_dirs = common_lib_dirs - sources = [ "init.c" ] + sources = [ + "init.c", + "win_delay_load_hook.cc", + ] - if (is_win && !is_mingw) { - # Include the delay-load hook for Windows MSVC/clang-cl builds. - # This hook intercepts the delay-load of libnode.dll and redirects it - # to either the already-loaded libnode.dll or the host node.exe. - sources += [ "win_delay_load_hook.cc" ] + deps = [ + "addon(${_default_tc})", + "app(${_default_tc})", + "common(${_default_tc})", + "error(${_default_tc})", + "extension(${_default_tc})", + "msg(${_default_tc})", + "ten_env(${_default_tc})", + "test(${_default_tc})", + "//core/src/ten_runtime:ten_runtime_library(${_default_tc})", + "//third_party/node(${_default_tc})", + ] } +} else if (is_win && !is_mingw && is_clang) { + # Default (clang) toolchain on Windows: delegate to msvc_force toolchain. + group("ten_runtime_nodejs") { + _msvc_toolchain = "//.gnfiles/build/toolchain/msvc_force:msvc_force" + public_deps = [ ":ten_runtime_nodejs_msvc(${_msvc_toolchain})" ] + } +} else { + # Non-Windows or non-clang: build normally. + ten_shared_library("ten_runtime_nodejs") { + configs = [ ":ten_runtime_nodejs_config" ] + + output_extension = "node" + + if (is_mac || is_linux) { + add_configs = [ "//.gnfiles/build/toolchain/common:allow_undefined" ] + remove_configs = + [ "//.gnfiles/build/toolchain/common:disallow_undefined" ] + } + + # Add rpath to find ten_runtime library. + if (is_mac) { + ldflags = [ "-Wl,-rpath,@loader_path/../../ten_runtime/lib" ] + } else if (is_linux || is_mingw) { + ldflags = [ "-Wl,-rpath=\$ORIGIN/../../ten_runtime/lib" ] + } else if (is_win && !is_mingw) { + ldflags = [ + "/DELAYLOAD:libnode.dll", + "/ignore:4199", + ] + libs = [ "delayimp.lib" ] + } - deps = [ - "addon", - "app", - "common", - "error", - "extension", - "msg", - "ten_env", - "test", - "//core/src/ten_runtime:ten_runtime_library", - "//third_party/node", - ] + sources = [ "init.c" ] + + if (is_win && !is_mingw) { + sources += [ "win_delay_load_hook.cc" ] + } + + deps = [ + "addon", + "app", + "common", + "error", + "extension", + "msg", + "ten_env", + "test", + "//core/src/ten_runtime:ten_runtime_library", + "//third_party/node", + ] + } } diff --git a/core/src/ten_runtime/output_libs.gni b/core/src/ten_runtime/output_libs.gni index 1ebd15d6e9..84c475e676 100644 --- a/core/src/ten_runtime/output_libs.gni +++ b/core/src/ten_runtime/output_libs.gni @@ -64,6 +64,15 @@ if (is_win) { ten_runtime_nodejs_output_libs = [ "${root_out_dir}/libten_runtime_nodejs.node", ] + } else if (is_clang) { + # clang-cl build: nodejs is compiled under msvc_force toolchain + _msvc_toolchain = "//.gnfiles/build/toolchain/msvc_force:msvc_force" + _msvc_target_out_dir = get_label_info( + "//core/src/ten_runtime/binding/nodejs/native:ten_runtime_nodejs_msvc(${_msvc_toolchain})", + "root_out_dir") + ten_runtime_nodejs_output_libs = [ + "${_msvc_target_out_dir}/ten_runtime_nodejs.node", + ] } else { # MSVC: .node extension without lib prefix ten_runtime_nodejs_output_libs = [ diff --git a/core/ten_gn b/core/ten_gn index 83a1ce48ef..55c2f16919 160000 --- a/core/ten_gn +++ b/core/ten_gn @@ -1 +1 @@ -Subproject commit 83a1ce48ef9782e284e92fccfa8776f8d0578148 +Subproject commit 55c2f16919b4063e846aefd13740dec73db0fc53 diff --git a/packages/core_addon_loaders/nodejs_addon_loader/BUILD.gn b/packages/core_addon_loaders/nodejs_addon_loader/BUILD.gn index cace601c1b..4d2b00af97 100644 --- a/packages/core_addon_loaders/nodejs_addon_loader/BUILD.gn +++ b/packages/core_addon_loaders/nodejs_addon_loader/BUILD.gn @@ -102,6 +102,25 @@ config("nodejs_addon_loader_config") { if (is_win && !is_mingw) { # MSVC or clang-cl on Windows uses /std:c++17 cflags_cc = [ "/std:c++17" ] + + # Use dynamic RELEASE CRT (/MD) to match the prebuilt libnode.dll. + # libnode.dll imports VCRUNTIME140.dll / MSVCP140.dll (release, no "D"), + # so we must use /MD even in debug builds. Using /MDd would link against + # VCRUNTIME140D.dll (debug), causing STL layout mismatches when passing + # std::vector across the DLL boundary to + # node::InitializeOncePerProcess(). + # + # This overrides the global /MTd set by common_cflags and msvc:debug + # configs. When both /MT and /MD appear, clang-cl uses the last one. + cflags = [ "/MD" ] + + # Disable MSVC debug iterator checks to match libnode.dll's release STL + # layout. Without this, _DEBUG causes std::vector/std::string to have + # extra debug fields that don't exist in the release-built libnode.dll. + defines += [ + "_ITERATOR_DEBUG_LEVEL=0", + "_HAS_ITERATOR_DEBUGGING=0", + ] } else { # GCC, Clang, MinGW use -std=c++17 cflags_cc = [ "-std=c++17" ] @@ -123,6 +142,27 @@ ten_package("nodejs_addon_loader") { "property.json", ] + # On Windows (non-MinGW), force linking against the RELEASE dynamic CRT + # to match the prebuilt libnode.dll (which uses VCRUNTIME140.dll, not + # VCRUNTIME140D.dll). The msvc:debug config adds debug CRT import libs + # (msvcrtd.lib, ucrtd.lib, vcruntimed.lib), so we must exclude them + # and substitute the release versions. + if (is_win && !is_mingw && is_debug) { + ldflags = [ + "/NODEFAULTLIB:msvcrtd.lib", + "/NODEFAULTLIB:ucrtd.lib", + "/NODEFAULTLIB:vcruntimed.lib", + "/NODEFAULTLIB:libcmtd.lib", + "/NODEFAULTLIB:libucrtd.lib", + "/NODEFAULTLIB:libvcruntimed.lib", + ] + libs = [ + "msvcrt.lib", + "ucrt.lib", + "vcruntime.lib", + ] + } + docs_files = exec_script("//.gnfiles/build/scripts/glob_file.py", [ "--dir", diff --git a/packages/core_addon_loaders/nodejs_addon_loader/src/main.cc b/packages/core_addon_loaders/nodejs_addon_loader/src/main.cc index 47272cf195..fe2d32e205 100644 --- a/packages/core_addon_loaders/nodejs_addon_loader/src/main.cc +++ b/packages/core_addon_loaders/nodejs_addon_loader/src/main.cc @@ -412,9 +412,10 @@ class nodejs_addon_loader_t : public ten::addon_loader_t { " console.log('wait 3 seconds to mock import slowly...');" " await new Promise(resolve => setTimeout(resolve, 3000));" #endif - " const module = await " - "import(process.cwd() + " - "'/ten_packages/system/ten_runtime_nodejs/build/index.js');" + " const { pathToFileURL } = require('node:url');" + " const modulePath = process.cwd() + " + "'/ten_packages/system/ten_runtime_nodejs/build/index.js';" + " const module = await import(pathToFileURL(modulePath).href);" " global.ten_runtime_nodejs = module;" " console.log('ten_runtime_nodejs module loaded successfully');" " global.__ten_runtime_nodejs_module_imported();" From 3c739539c4a57c203075639c0639e1fe904c4604 Mon Sep 17 00:00:00 2001 From: Nie Zhihe Date: Wed, 11 Feb 2026 14:38:34 +0800 Subject: [PATCH 08/16] chore: fix several tests --- tests/ten_runtime/integration/nodejs/BUILD.gn | 2 +- .../standalone_test_nodejs/test_case.py | 2 + .../tests/bin/start.py | 54 +++++++++++++++++++ .../standalone_test_nodejs_2/test_case.py | 2 + .../tests/bin/start.py | 54 +++++++++++++++++++ .../standalone_test_nodejs_3/test_case.py | 2 + 6 files changed, 115 insertions(+), 1 deletion(-) create mode 100644 tests/ten_runtime/integration/nodejs/standalone_test_nodejs_2/default_extension_nodejs/tests/bin/start.py create mode 100644 tests/ten_runtime/integration/nodejs/standalone_test_nodejs_3/default_extension_nodejs/tests/bin/start.py diff --git a/tests/ten_runtime/integration/nodejs/BUILD.gn b/tests/ten_runtime/integration/nodejs/BUILD.gn index aef6dcc081..676b57a199 100644 --- a/tests/ten_runtime/integration/nodejs/BUILD.gn +++ b/tests/ten_runtime/integration/nodejs/BUILD.gn @@ -40,7 +40,7 @@ group("nodejs") { deps += [ "go_app_preload_all_addons_nodejs" ] } - if (is_debug) { + if (is_debug && !is_win) { deps += [ "leak_check_nodejs" ] } } diff --git a/tests/ten_runtime/integration/nodejs/standalone_test_nodejs/test_case.py b/tests/ten_runtime/integration/nodejs/standalone_test_nodejs/test_case.py index 62b56f7ef8..3282b225f4 100644 --- a/tests/ten_runtime/integration/nodejs/standalone_test_nodejs/test_case.py +++ b/tests/ten_runtime/integration/nodejs/standalone_test_nodejs/test_case.py @@ -99,6 +99,7 @@ def test_standalone_test_nodejs(): stderr=subprocess.STDOUT, env=my_env, cwd=extension_in_app_folder, + shell=sys.platform == "win32", ) standalone_install_process.wait() @@ -117,6 +118,7 @@ def test_standalone_test_nodejs(): stderr=subprocess.STDOUT, env=my_env, cwd=extension_in_app_folder, + shell=sys.platform == "win32", ) build_extension_process.wait() diff --git a/tests/ten_runtime/integration/nodejs/standalone_test_nodejs_2/default_extension_nodejs/tests/bin/start.py b/tests/ten_runtime/integration/nodejs/standalone_test_nodejs_2/default_extension_nodejs/tests/bin/start.py new file mode 100644 index 0000000000..1e4049a48d --- /dev/null +++ b/tests/ten_runtime/integration/nodejs/standalone_test_nodejs_2/default_extension_nodejs/tests/bin/start.py @@ -0,0 +1,54 @@ +#!/usr/bin/env python3 +""" +Cross-platform test start script for Node.js extensions on Windows. +On Unix-like systems, prefer using bash start script for faster startup. +""" + +import os +import sys +import subprocess + +# Change to the tests directory (parent of bin) +tests_root = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) +os.chdir(tests_root) + +env = os.environ.copy() + +# npm install +print("Running npm install...") +result = subprocess.run(["npm", "install"], env=env, shell=True) +if result.returncode != 0: + print("npm install failed") + sys.exit(result.returncode) + +# npm run build +print("Running npm run build...") +result = subprocess.run(["npm", "run", "build"], env=env, shell=True) +if result.returncode != 0: + print("npm run build failed") + sys.exit(result.returncode) + +# Set NODE_PATH to include ten_runtime_nodejs lib +ten_runtime_nodejs_lib = os.path.join( + tests_root, "..", ".ten", "app", "ten_packages", "system", + "ten_runtime_nodejs", "lib" +) +ten_runtime_nodejs_lib = os.path.normpath(ten_runtime_nodejs_lib) +if "NODE_PATH" in env: + env["NODE_PATH"] = ten_runtime_nodejs_lib + os.pathsep + env["NODE_PATH"] +else: + env["NODE_PATH"] = ten_runtime_nodejs_lib + +# Add DLL search paths to PATH for Windows +ten_runtime_lib = os.path.join( + tests_root, "..", ".ten", "app", "ten_packages", "system", + "ten_runtime", "lib" +) +ten_runtime_lib = os.path.normpath(ten_runtime_lib) +dll_paths = [ten_runtime_nodejs_lib, ten_runtime_lib] +env["PATH"] = os.pathsep.join(dll_paths) + os.pathsep + env.get("PATH", "") + +# npm test +print("Running npm test...") +result = subprocess.run(["npm", "test"], env=env, shell=True) +sys.exit(result.returncode) diff --git a/tests/ten_runtime/integration/nodejs/standalone_test_nodejs_2/test_case.py b/tests/ten_runtime/integration/nodejs/standalone_test_nodejs_2/test_case.py index e05d74d85e..9b5f8a475f 100644 --- a/tests/ten_runtime/integration/nodejs/standalone_test_nodejs_2/test_case.py +++ b/tests/ten_runtime/integration/nodejs/standalone_test_nodejs_2/test_case.py @@ -68,6 +68,7 @@ def test_standalone_test_nodejs_2(): stderr=subprocess.STDOUT, env=my_env, cwd=extension_in_app_folder, + shell=sys.platform == "win32", ) standalone_install_process.wait() @@ -86,6 +87,7 @@ def test_standalone_test_nodejs_2(): stderr=subprocess.STDOUT, env=my_env, cwd=extension_in_app_folder, + shell=sys.platform == "win32", ) build_extension_process.wait() diff --git a/tests/ten_runtime/integration/nodejs/standalone_test_nodejs_3/default_extension_nodejs/tests/bin/start.py b/tests/ten_runtime/integration/nodejs/standalone_test_nodejs_3/default_extension_nodejs/tests/bin/start.py new file mode 100644 index 0000000000..22e4fb9b85 --- /dev/null +++ b/tests/ten_runtime/integration/nodejs/standalone_test_nodejs_3/default_extension_nodejs/tests/bin/start.py @@ -0,0 +1,54 @@ +#!/usr/bin/env python3 +""" +Cross-platform test start script for Node.js extensions on Windows. +On Unix-like systems, prefer using bash start script for faster startup. +""" + +import os +import sys +import subprocess + +# Change to the tests directory (parent of bin) +tests_root = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) +os.chdir(tests_root) + +env = os.environ.copy() + +# npm install +print("Running npm install...") +result = subprocess.run(["npm", "install"], env=env, shell=True) +if result.returncode != 0: + print("npm install failed") + sys.exit(result.returncode) + +# npm run build +print("Running npm run build...") +result = subprocess.run(["npm", "run", "build"], env=env, shell=True) +if result.returncode != 0: + print("npm run build failed") + sys.exit(result.returncode) + +# Set NODE_PATH to include ten_runtime_nodejs lib +ten_runtime_nodejs_lib = os.path.join( + tests_root, "..", ".ten", "app", "ten_packages", "system", + "ten_runtime_nodejs", "lib" +) +ten_runtime_nodejs_lib = os.path.normpath(ten_runtime_nodejs_lib) +if "NODE_PATH" in env: + env["NODE_PATH"] = ten_runtime_nodejs_lib + os.pathsep + env["NODE_PATH"] +else: + env["NODE_PATH"] = ten_runtime_nodejs_lib + +# Add DLL search paths to PATH for Windows +ten_runtime_lib = os.path.join( + tests_root, "..", ".ten", "app", "ten_packages", "system", + "ten_runtime", "lib" +) +ten_runtime_lib = os.path.normpath(ten_runtime_lib) +dll_paths = [ten_runtime_nodejs_lib, ten_runtime_lib] +env["PATH"] = os.pathsep.join(dll_paths) + os.pathsep + env.get("PATH", "") + +# Run test directly with node (matching the bash script behavior) +print("Running node --expose-gc build/index.js...") +result = subprocess.run(["node", "--expose-gc", "build/index.js"], env=env) +sys.exit(result.returncode) diff --git a/tests/ten_runtime/integration/nodejs/standalone_test_nodejs_3/test_case.py b/tests/ten_runtime/integration/nodejs/standalone_test_nodejs_3/test_case.py index 3d56956424..2c3e93cdcd 100644 --- a/tests/ten_runtime/integration/nodejs/standalone_test_nodejs_3/test_case.py +++ b/tests/ten_runtime/integration/nodejs/standalone_test_nodejs_3/test_case.py @@ -68,6 +68,7 @@ def test_standalone_test_nodejs_3(): stderr=subprocess.STDOUT, env=my_env, cwd=extension_in_app_folder, + shell=sys.platform == "win32", ) standalone_install_process.wait() @@ -86,6 +87,7 @@ def test_standalone_test_nodejs_3(): stderr=subprocess.STDOUT, env=my_env, cwd=extension_in_app_folder, + shell=sys.platform == "win32", ) build_extension_process.wait() From ac43d119f5aaf517777edcb6f2a0da01d90f79f1 Mon Sep 17 00:00:00 2001 From: Nie Zhihe Date: Wed, 11 Feb 2026 15:57:05 +0800 Subject: [PATCH 09/16] fix: download prebuilt node shared libraries 1.0.16 --- .gitignore | 3 - .../nodejs_addon_loader/BUILD.gn | 2 +- third_party/node/BUILD.gn | 106 ++++++++++-------- 3 files changed, 60 insertions(+), 51 deletions(-) diff --git a/.gitignore b/.gitignore index d52a75675c..d43086e002 100644 --- a/.gitignore +++ b/.gitignore @@ -19,9 +19,6 @@ __pycache__/ *.a *.o -# Do not ignore prebuilt node libraries for Windows -!third_party/node/lib/win/x64/*.dll - # Log *.log diff --git a/packages/core_addon_loaders/nodejs_addon_loader/BUILD.gn b/packages/core_addon_loaders/nodejs_addon_loader/BUILD.gn index 4d2b00af97..70c7c43bd6 100644 --- a/packages/core_addon_loaders/nodejs_addon_loader/BUILD.gn +++ b/packages/core_addon_loaders/nodejs_addon_loader/BUILD.gn @@ -19,7 +19,7 @@ if (is_win) { action("copy_node_shared_lib") { script = "//.gnfiles/build/scripts/copy_fs_entry.py" - _source_lib = "//third_party/node/lib/win/x64/libnode.dll" + _source_lib = "${root_out_dir}/prebuilt/node_shared/lib/libnode.dll" _dest_lib = "${root_out_dir}/ten_packages/addon_loader/nodejs_addon_loader/lib/libnode.dll" args = [ diff --git a/third_party/node/BUILD.gn b/third_party/node/BUILD.gn index 2e33ae3f84..36976c121e 100644 --- a/third_party/node/BUILD.gn +++ b/third_party/node/BUILD.gn @@ -16,16 +16,64 @@ config("node_header") { ] } +ten_prebuilt_library("prebuilt_node_shared") { + name = "node_shared" + + # Version can be easily updated here + _node_shared_version = "v1.0.16" + _base_url = "https://github.com/TEN-framework/node_shared/releases/download/${_node_shared_version}" + + if (is_win) { + if (target_cpu == "x64") { + url = "${_base_url}/node-shared-win-x64-msvc.zip" + } else { + assert(false, "Unsupported Windows CPU architecture: ${target_cpu}") + } + } else if (is_linux) { + if (target_cpu == "x64") { + if (is_clang) { + url = "${_base_url}/node-shared-linux-x64-clang.zip" + } else { + url = "${_base_url}/node-shared-linux-x64-gcc.zip" + } + } else if (target_cpu == "arm64") { + if (is_clang) { + url = "${_base_url}/node-shared-linux-arm64-clang.zip" + } else { + url = "${_base_url}/node-shared-linux-arm64-gcc.zip" + } + } else { + assert(false, "Unsupported Linux CPU architecture: ${target_cpu}") + } + } else if (is_mac) { + if (target_cpu == "x64") { + url = "${_base_url}/node-shared-mac-x64-clang.zip" + } else if (target_cpu == "arm64") { + url = "${_base_url}/node-shared-mac-arm64-clang.zip" + } else { + assert(false, "Unsupported Mac CPU architecture: ${target_cpu}") + } + } else { + assert(false, "Unsupported platform") + } + + is_shared_library = true + + libs = [ "node" ] + if (!is_win) { + lib_version = 127 + } +} + if (is_win) { - # Windows uses local prebuilt libraries + # Windows-specific config: link against libnode.lib (import library for + # libnode.dll) from the downloaded prebuilt directory. config("node_lib_config") { - lib_dirs = [ "//third_party/node/lib/win/x64" ] + lib_dirs = [ rebase_path("${root_out_dir}/prebuilt/node_shared/lib") ] if (is_mingw) { - # MinGW uses libnode.lib directly (same as MSVC import library) libs = [ "libnode" ] } else { - # MSVC: Link against libnode.lib (import library for libnode.dll) libs = [ "libnode.lib" ] } } @@ -34,7 +82,7 @@ if (is_win) { action("copy_node_dll") { script = "//.gnfiles/build/scripts/copy_fs_entry.py" - _source_dll = "//third_party/node/lib/win/x64/libnode.dll" + _source_dll = "${root_out_dir}/prebuilt/node_shared/lib/libnode.dll" _dest_dll = "${root_out_dir}/libnode.dll" args = [ @@ -46,6 +94,8 @@ if (is_win) { sources = [ _source_dll ] outputs = [ _dest_dll ] + + deps = [ ":prebuilt_node_shared" ] } group("node") { @@ -53,50 +103,12 @@ if (is_win) { ":node_header", ":node_lib_config", ] - public_deps = [ ":copy_node_dll" ] + public_deps = [ + ":copy_node_dll", + ":prebuilt_node_shared", + ] } } else { - ten_prebuilt_library("prebuilt_node_shared") { - name = "node_shared" - - # Version can be easily updated here - _node_shared_version = "v1.0.15" - _base_url = "https://github.com/TEN-framework/node_shared/releases/download/${_node_shared_version}" - - if (is_linux) { - if (target_cpu == "x64") { - if (is_clang) { - url = "${_base_url}/node-shared-linux-x64-clang.zip" - } else { - url = "${_base_url}/node-shared-linux-x64-gcc.zip" - } - } else if (target_cpu == "arm64") { - if (is_clang) { - url = "${_base_url}/node-shared-linux-arm64-clang.zip" - } else { - url = "${_base_url}/node-shared-linux-arm64-gcc.zip" - } - } else { - assert(false, "Unsupported Linux CPU architecture: ${target_cpu}") - } - } else if (is_mac) { - if (target_cpu == "x64") { - url = "${_base_url}/node-shared-mac-x64-clang.zip" - } else if (target_cpu == "arm64") { - url = "${_base_url}/node-shared-mac-arm64-clang.zip" - } else { - assert(false, "Unsupported Mac CPU architecture: ${target_cpu}") - } - } else { - assert(false, "Unsupported platform") - } - - is_shared_library = true - - libs = [ "node" ] - lib_version = 127 - } - group("node") { public_configs = [ ":node_header" ] public_deps = [ ":prebuilt_node_shared" ] From 5db010fde87ccd35b9b65ea495d50acbaab7d537 Mon Sep 17 00:00:00 2001 From: Nie Zhihe Date: Wed, 11 Feb 2026 16:45:36 +0800 Subject: [PATCH 10/16] chore: refine codes --- .../binding/nodejs/interface/ten_addon.ts | 1 - .../binding/nodejs/native/BUILD.gn | 23 +++++++++++++++++-- 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/core/src/ten_runtime/binding/nodejs/interface/ten_addon.ts b/core/src/ten_runtime/binding/nodejs/interface/ten_addon.ts index 11506e0ab5..0eb1cb92f2 100644 --- a/core/src/ten_runtime/binding/nodejs/interface/ten_addon.ts +++ b/core/src/ten_runtime/binding/nodejs/interface/ten_addon.ts @@ -21,7 +21,6 @@ try { // Try different module names based on platform: // - Linux/macOS: libten_runtime_nodejs (with lib prefix) // - Windows MSVC: ten_runtime_nodejs (without lib prefix) - // - Windows MinGW: libten_runtime_nodejs (with lib prefix) try { addon = require("libten_runtime_nodejs"); } catch { diff --git a/core/src/ten_runtime/binding/nodejs/native/BUILD.gn b/core/src/ten_runtime/binding/nodejs/native/BUILD.gn index b2cc440e44..d724c31889 100644 --- a/core/src/ten_runtime/binding/nodejs/native/BUILD.gn +++ b/core/src/ten_runtime/binding/nodejs/native/BUILD.gn @@ -85,26 +85,45 @@ if (current_toolchain == "//.gnfiles/build/toolchain/msvc_force:msvc_force" && if (is_mac || is_linux) { add_configs = [ "//.gnfiles/build/toolchain/common:allow_undefined" ] - remove_configs = - [ "//.gnfiles/build/toolchain/common:disallow_undefined" ] + remove_configs = [ "//.gnfiles/build/toolchain/common:disallow_undefined" ] } # Add rpath to find ten_runtime library. if (is_mac) { ldflags = [ "-Wl,-rpath,@loader_path/../../ten_runtime/lib" ] } else if (is_linux || is_mingw) { + # MinGW uses GNU ld and supports rpath ldflags = [ "-Wl,-rpath=\$ORIGIN/../../ten_runtime/lib" ] } else if (is_win && !is_mingw) { + # MSVC/clang-cl: Use /DELAYLOAD to avoid loading libnode.dll at startup. + # N-API symbols are provided by the host node.exe at runtime, so we don't + # actually need libnode.dll. But MSVC requires linking to resolve symbols. + # With delay load, the DLL won't be loaded unless we call functions from it, + # and since node.exe provides these symbols, libnode.dll is never loaded. + # + # The win_delay_load_hook.cc file implements a delay-load hook that: + # 1. For pure Node.js apps: returns handle to node.exe (which exports N-API) + # 2. For embedded scenarios: returns handle to libnode.dll if loaded ldflags = [ "/DELAYLOAD:libnode.dll", + + # Suppress linker warning LNK4199: /DELAYLOAD:libnode.dll ignored; + # no imports found from libnode.dll. This warning occurs because the + # delay-load hook redirects all imports before they reach libnode.dll. "/ignore:4199", ] + + # The delay import library provided by Microsoft. Required for delay-load + # support. libs = [ "delayimp.lib" ] } sources = [ "init.c" ] if (is_win && !is_mingw) { + # Include the delay-load hook for Windows MSVC/clang-cl builds. + # This hook intercepts the delay-load of libnode.dll and redirects it + # to either the already-loaded libnode.dll or the host node.exe. sources += [ "win_delay_load_hook.cc" ] } From d588ae6d7dcf653032897147faae4721874d469d Mon Sep 17 00:00:00 2001 From: Nie Zhihe Date: Wed, 11 Feb 2026 17:16:08 +0800 Subject: [PATCH 11/16] chore: refine codes --- build/ten_runtime/feature/cmake.py | 12 ++---------- core/src/ten_runtime/binding/nodejs/native/BUILD.gn | 1 - .../nodejs_addon_loader/src/main.cc | 1 - packages/core_apps/default_app_nodejs/BUILD.gn | 1 - 4 files changed, 2 insertions(+), 13 deletions(-) diff --git a/build/ten_runtime/feature/cmake.py b/build/ten_runtime/feature/cmake.py index f56f9bac42..2c94ce3aa3 100644 --- a/build/ten_runtime/feature/cmake.py +++ b/build/ten_runtime/feature/cmake.py @@ -359,22 +359,14 @@ def clean(self): # # cmd = ' '.join(self.env) + f' cmake --build {self.args.build_path} # --target clean' - import platform - if platform.system() == "Windows": - cmd = f'rmdir /s /q "{self.args.build_path}"' - else: - cmd = f"rm -rf {self.args.build_path}" + cmd = f"rm -rf {self.args.build_path}" if self.args.log_level > 1: print(f"> {cmd}") returncode, output_text = cmd_exec.run_cmd_realtime( cmd, log_level=self.args.log_level ) if returncode != 0: - if output_text: - try: - print(output_text) - except UnicodeEncodeError: - print(output_text.encode("ascii", errors="replace").decode("ascii")) + print(output_text) raise RuntimeError("Failed to cmake clean.") def build(self): diff --git a/core/src/ten_runtime/binding/nodejs/native/BUILD.gn b/core/src/ten_runtime/binding/nodejs/native/BUILD.gn index d724c31889..1ff2b09d44 100644 --- a/core/src/ten_runtime/binding/nodejs/native/BUILD.gn +++ b/core/src/ten_runtime/binding/nodejs/native/BUILD.gn @@ -137,7 +137,6 @@ if (current_toolchain == "//.gnfiles/build/toolchain/msvc_force:msvc_force" && "ten_env", "test", "//core/src/ten_runtime:ten_runtime_library", - "//third_party/node", ] } } diff --git a/packages/core_addon_loaders/nodejs_addon_loader/src/main.cc b/packages/core_addon_loaders/nodejs_addon_loader/src/main.cc index fe2d32e205..c3ba168f92 100644 --- a/packages/core_addon_loaders/nodejs_addon_loader/src/main.cc +++ b/packages/core_addon_loaders/nodejs_addon_loader/src/main.cc @@ -18,7 +18,6 @@ #include #include #include -#include #include #include "node.h" diff --git a/packages/core_apps/default_app_nodejs/BUILD.gn b/packages/core_apps/default_app_nodejs/BUILD.gn index c734dccb63..2cededdad0 100644 --- a/packages/core_apps/default_app_nodejs/BUILD.gn +++ b/packages/core_apps/default_app_nodejs/BUILD.gn @@ -5,7 +5,6 @@ # import("//build/feature/ten_package.gni") import("//build/ten_runtime/feature/publish.gni") -import("//build/ten_runtime/options.gni") ten_package("default_app_nodejs") { package_kind = "app" From ea2f29f2dd53bbdc938b04cd18c673158263e5a2 Mon Sep 17 00:00:00 2001 From: Nie Zhihe Date: Wed, 11 Feb 2026 17:16:16 +0800 Subject: [PATCH 12/16] chore: ten_gn --- core/ten_gn | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/ten_gn b/core/ten_gn index 55c2f16919..634b3b4730 160000 --- a/core/ten_gn +++ b/core/ten_gn @@ -1 +1 @@ -Subproject commit 55c2f16919b4063e846aefd13740dec73db0fc53 +Subproject commit 634b3b4730f13f2f89947939a639bee93e878cd0 From e5b710408df24bf954e1190971f9dd698e78383c Mon Sep 17 00:00:00 2001 From: Nie Zhihe Date: Wed, 11 Feb 2026 19:31:51 +0800 Subject: [PATCH 13/16] chore: install requests in workflow --- .github/workflows/win.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/win.yml b/.github/workflows/win.yml index c6b2f18105..99b2bec3ff 100644 --- a/.github/workflows/win.yml +++ b/.github/workflows/win.yml @@ -210,7 +210,7 @@ jobs: - name: Install tools and dependencies shell: pwsh run: | - pip3 install --use-pep517 python-dotenv jinja2 + pip3 install --use-pep517 python-dotenv jinja2 requests go install golang.org/dl/go1.24.3@latest go1.24.3 download go1.24.3 version From 49ad84ceaacebb14a6bbfc4c6491d93e108579d8 Mon Sep 17 00:00:00 2001 From: Nie Zhihe Date: Thu, 12 Feb 2026 15:27:20 +0800 Subject: [PATCH 14/16] chore: update node_shared version --- third_party/node/BUILD.gn | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/third_party/node/BUILD.gn b/third_party/node/BUILD.gn index 36976c121e..bf5b849571 100644 --- a/third_party/node/BUILD.gn +++ b/third_party/node/BUILD.gn @@ -20,7 +20,7 @@ ten_prebuilt_library("prebuilt_node_shared") { name = "node_shared" # Version can be easily updated here - _node_shared_version = "v1.0.16" + _node_shared_version = "v1.0.18" _base_url = "https://github.com/TEN-framework/node_shared/releases/download/${_node_shared_version}" if (is_win) { From 257bc2de833a7703d931a005f658591e419660a9 Mon Sep 17 00:00:00 2001 From: Nie Zhihe Date: Sun, 15 Feb 2026 20:30:47 +0800 Subject: [PATCH 15/16] chore: update node_shared version --- third_party/node/BUILD.gn | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/third_party/node/BUILD.gn b/third_party/node/BUILD.gn index bf5b849571..8291e2a78d 100644 --- a/third_party/node/BUILD.gn +++ b/third_party/node/BUILD.gn @@ -20,7 +20,7 @@ ten_prebuilt_library("prebuilt_node_shared") { name = "node_shared" # Version can be easily updated here - _node_shared_version = "v1.0.18" + _node_shared_version = "v1.0.19" _base_url = "https://github.com/TEN-framework/node_shared/releases/download/${_node_shared_version}" if (is_win) { From 9c18f4b5cb38dabc46c7f7c3d95d0c4b3cfc4089 Mon Sep 17 00:00:00 2001 From: Nie Zhihe Date: Sun, 15 Feb 2026 22:38:23 +0800 Subject: [PATCH 16/16] fix: use deps for prebuilt_node_shared --- third_party/node/BUILD.gn | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/third_party/node/BUILD.gn b/third_party/node/BUILD.gn index 8291e2a78d..11f5ffdbdc 100644 --- a/third_party/node/BUILD.gn +++ b/third_party/node/BUILD.gn @@ -103,7 +103,13 @@ if (is_win) { ":node_header", ":node_lib_config", ] - public_deps = [ + + # Use deps (not public_deps) for prebuilt_node_shared to prevent its + # public config from propagating libs = ["node"] to dependents. On + # Windows, MSVC link.exe interprets bare "node" as "node.obj" instead + # of "node.lib", causing LNK1181. The node_lib_config above already + # provides the correct libs = ["libnode.lib"]. + deps = [ ":copy_node_dll", ":prebuilt_node_shared", ]