如何在 Rust 中静态 link node.js?
How to statically link node.js in rust?
我想在 Rust 中嵌入 node.js。我 不 有兴趣使用 NAPI 编写 node.js 插件或从 Rust 中广泛控制 node.js。我需要的只是 node.js main()
启动方法 - 相当于 node myscript.js
.
为什么?我正在用 Rust 构建一个自包含的单文件二进制桌面应用程序,并且想要 运行 node.js 具有嵌入式 node.js 运行 时间的脚本。 Node.js 不能保证在最终用户的计算机上,启动时间很敏感,因此从二进制文件中提取 node.js 的独立 zip 到文件系统是不可取的。
我认为我在 rust(二进制)项目中静态链接 node.js 时遇到问题。
我把nodejs源码拉下来了
git clone https://github.com/nodejs/node
并重命名 node_main.cc
中的主要方法
sed -i .bak "s/int main(/int node_main(/g" ./src/node_main.cc
然后我将 node.js 构建为静态库
./configure --enable-static
make -j4
我有一个 c++ 包装器文件 wrapper.cpp
通过 extern c
公开 node_main()
方法
wrapper.cpp:
#include <string>
#include <iostream>
using namespace std;
int node_main(int argc, char **argv);
extern "C" {
void run_node() {
cout << "hello there! general kenobi..." << endl;
char *args[] = { (char*)"tester.js", NULL };
node_main(1, args);
}
}
此时我能够成功地将 c++ 包装器构建为二进制文件,同时成功地从 c++ 静态链接 node.js 库和 运行 node.js。然而,生锈...
main.rs:
extern {
fn run_node();
}
fn main() {
println!("hey there this is RUST");
unsafe { run_node(); }
}
build.rs:
extern crate cc;
fn main() {
println!("cargo:rustc-link-search=native=../node/out/Release");
println!("cargo:rustc-link-lib=static=node");
println!("cargo:rustc-link-lib=static=uv");
println!("cargo:rustc-link-lib=static=v8_base");
println!("cargo:rustc-link-lib=static=v8_libbase");
println!("cargo:rustc-link-lib=static=v8_snapshot");
println!("cargo:rustc-link-lib=static=v8_libplatform");
println!("cargo:rustc-link-lib=static=icuucx");
println!("cargo:rustc-link-lib=static=icui18n");
println!("cargo:rustc-link-lib=static=icudata");
println!("cargo:rustc-link-lib=static=icustubdata");
println!("cargo:rustc-link-lib=static=brotli");
println!("cargo:rustc-link-lib=static=nghttp2");
cc::Build::new()
.cpp(true)
.file("wrapper.cpp")
.compile("libwrapper.a");
}
注意上面的 rustc-link-search
路径是相对的。
当我 运行 cargo build
我得到以下错误:
= note: Undefined symbols for architecture x86_64:
"node_main(int, char**)", referenced from:
_run_node in libwrapper.a(wrapper.o)
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
我不确定链接器顺序是否重要,但我尝试了一些不同的顺序组合但没有成功。我还尝试从 node/out/Release
链接所有 lib .a
文件,但没有区别。我还尝试将 node/out/Release
中的所有 .a
文件合并到一个 lib .a
文件中,但收到重复符号错误。我尝试了 node.js 的不同标签(比如 v11.15.0
),没有任何区别。
我在 MacOS 10.14.5 上执行此操作
$ rustc --version
rustc 1.36.0 (a53f9df32 2019-07-03)
$ cargo --version
cargo 1.36.0 (c4fcfb725 2019-05-15)
$ g++ --version
Configured with: --prefix=/Library/Developer/CommandLineTools/usr --with-gxx-include-dir=/Library/Devel
oper/CommandLineTools/SDKs/MacOSX10.14.sdk/usr/include/c++/4.2.1
Apple LLVM version 10.0.1 (clang-1001.0.46.4)
Target: x86_64-apple-darwin18.6.0
Thread model: posix
InstalledDir: /Library/Developer/CommandLineTools/usr/bin
Cargo.toml:
[build-dependencies]
cc = "1.0"
如果有任何好的想法,我愿意接受更好的方法将 node.js 嵌入到 Rust 中。
我发现了一些问题,一旦解决,就可以在 Rust 中成功静态 linking node.js。
node_main.cc
未包含在 libnode.a
输出中
我通过使用 nm
查找符号找到了这个。我无法在符号列表中找到 node_main()
和 node_main.o
。因此我意识到 node_main.cc
中的任何内容都不会被导出。
修复:在另一个文件中公开库入口点 C 函数,例如 node.cc
。请注意,我们在这里添加了一个全新的函数,该函数调用 node::Start()
node.cc
extern "C" int node_main(int argc, char** argv) {
return node::Start(argc, argv);
}
需要 extern "C"
因为 c++ 名称符号重整
同样,使用 nm
工具搜索 libnode.a
文件中的所有符号,我发现 node_main()
函数符号被破坏了。为了从 Rust 中找到这个符号,它不能被破坏,这是通过 extern "C"
完成的
修复:确保使用 extern "C"
将要暴露给 Rust 的函数放在前面
node.cc
extern "C" int node_main()
根据节点版本,缺少一些额外的符号
根据 node.js Github 问题 #27431 一些符号存根没有在静态库中得到输出。我正在使用 node.js v13.x.x 标签,这是一个问题,所以我不得不为这些额外的存根创建库,并在 Rust 构建配置中 link 它们。
修复:从存根创建静态库并在 build.rs
中 link 它们
ar rcs obj/Release/lib_stub_code_cache.a obj/Release/obj.target/cctest/src/node_code_cache_stub.o
ar rcs obj/Release/lib_stub_snapshot.a obj/Release/obj.target/cctest/src/node_snapshot_stub.o
最终结果
建筑物node.js
git clone https://github.com/nodejs/node
cd node
printf 'extern "C" int node_main(int argc, char** argv) { return node::Start(argc, argv); }' >> src/node.cc
./configure --enable-static
make -j4
# temporary fix: https://github.com/nodejs/node/issues/27431#issuecomment-487288275
REL=obj/Release
STUBS=$REL/obj.target/cctest/src
ar rcs "$REL/lib_stub_code_cache.a $STUBS/node_code_cache_stub.o"
ar rcs "$REL/lib_stub_snapshot.a $STUBS/node_snapshot_stub.o"
wrapper.cpp
#include <string>
#include <iostream>
using namespace std;
extern "C" {
int node_main(int argc, char** argv);
void run_node() {
cout << "hello there! general kenobi...\n";
char *args[] = { (char*)"tester.js", NULL };
node_main(1, args);
}
}
build.rs
extern crate cc;
fn main() {
cc::Build::new()
.cpp(true)
.file("wrapper.cpp")
.compile("libwrapper.a");
println!("cargo:rustc-link-search=native=../node/out/Release");
println!("cargo:rustc-link-lib=static=node");
println!("cargo:rustc-link-lib=static=uv");
// temporary fix - https://github.com/nodejs/node/issues/27431#issuecomment-487288275
println!("cargo:rustc-link-lib=static=_stub_code_cache");
println!("cargo:rustc-link-lib=static=_stub_snapshot");
// end temporary fix
println!("cargo:rustc-link-lib=static=v8_base_without_compiler");
println!("cargo:rustc-link-lib=static=v8_compiler");
println!("cargo:rustc-link-lib=static=v8_initializers");
println!("cargo:rustc-link-lib=static=v8_libbase");
println!("cargo:rustc-link-lib=static=v8_libplatform");
println!("cargo:rustc-link-lib=static=v8_libsampler");
println!("cargo:rustc-link-lib=static=v8_snapshot");
println!("cargo:rustc-link-lib=static=icuucx");
println!("cargo:rustc-link-lib=static=icui18n");
println!("cargo:rustc-link-lib=static=icudata");
println!("cargo:rustc-link-lib=static=icustubdata");
println!("cargo:rustc-link-lib=static=zlib");
println!("cargo:rustc-link-lib=static=brotli");
println!("cargo:rustc-link-lib=static=cares");
println!("cargo:rustc-link-lib=static=histogram");
println!("cargo:rustc-link-lib=static=http_parser");
println!("cargo:rustc-link-lib=static=llhttp");
println!("cargo:rustc-link-lib=static=nghttp2");
println!("cargo:rustc-link-lib=static=openssl");
}
main.rs
extern {
fn run_node();
}
fn main() {
println!("a surprise to be sure, but a welcome one");
unsafe { run_node(); }
}
我想在 Rust 中嵌入 node.js。我 不 有兴趣使用 NAPI 编写 node.js 插件或从 Rust 中广泛控制 node.js。我需要的只是 node.js main()
启动方法 - 相当于 node myscript.js
.
为什么?我正在用 Rust 构建一个自包含的单文件二进制桌面应用程序,并且想要 运行 node.js 具有嵌入式 node.js 运行 时间的脚本。 Node.js 不能保证在最终用户的计算机上,启动时间很敏感,因此从二进制文件中提取 node.js 的独立 zip 到文件系统是不可取的。
我认为我在 rust(二进制)项目中静态链接 node.js 时遇到问题。
我把nodejs源码拉下来了
git clone https://github.com/nodejs/node
并重命名 node_main.cc
sed -i .bak "s/int main(/int node_main(/g" ./src/node_main.cc
然后我将 node.js 构建为静态库
./configure --enable-static
make -j4
我有一个 c++ 包装器文件 wrapper.cpp
通过 extern c
node_main()
方法
wrapper.cpp:
#include <string>
#include <iostream>
using namespace std;
int node_main(int argc, char **argv);
extern "C" {
void run_node() {
cout << "hello there! general kenobi..." << endl;
char *args[] = { (char*)"tester.js", NULL };
node_main(1, args);
}
}
此时我能够成功地将 c++ 包装器构建为二进制文件,同时成功地从 c++ 静态链接 node.js 库和 运行 node.js。然而,生锈...
main.rs:
extern {
fn run_node();
}
fn main() {
println!("hey there this is RUST");
unsafe { run_node(); }
}
build.rs:
extern crate cc;
fn main() {
println!("cargo:rustc-link-search=native=../node/out/Release");
println!("cargo:rustc-link-lib=static=node");
println!("cargo:rustc-link-lib=static=uv");
println!("cargo:rustc-link-lib=static=v8_base");
println!("cargo:rustc-link-lib=static=v8_libbase");
println!("cargo:rustc-link-lib=static=v8_snapshot");
println!("cargo:rustc-link-lib=static=v8_libplatform");
println!("cargo:rustc-link-lib=static=icuucx");
println!("cargo:rustc-link-lib=static=icui18n");
println!("cargo:rustc-link-lib=static=icudata");
println!("cargo:rustc-link-lib=static=icustubdata");
println!("cargo:rustc-link-lib=static=brotli");
println!("cargo:rustc-link-lib=static=nghttp2");
cc::Build::new()
.cpp(true)
.file("wrapper.cpp")
.compile("libwrapper.a");
}
注意上面的 rustc-link-search
路径是相对的。
当我 运行 cargo build
我得到以下错误:
= note: Undefined symbols for architecture x86_64:
"node_main(int, char**)", referenced from:
_run_node in libwrapper.a(wrapper.o)
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
我不确定链接器顺序是否重要,但我尝试了一些不同的顺序组合但没有成功。我还尝试从 node/out/Release
链接所有 lib .a
文件,但没有区别。我还尝试将 node/out/Release
中的所有 .a
文件合并到一个 lib .a
文件中,但收到重复符号错误。我尝试了 node.js 的不同标签(比如 v11.15.0
),没有任何区别。
我在 MacOS 10.14.5 上执行此操作
$ rustc --version
rustc 1.36.0 (a53f9df32 2019-07-03)
$ cargo --version
cargo 1.36.0 (c4fcfb725 2019-05-15)
$ g++ --version
Configured with: --prefix=/Library/Developer/CommandLineTools/usr --with-gxx-include-dir=/Library/Devel
oper/CommandLineTools/SDKs/MacOSX10.14.sdk/usr/include/c++/4.2.1
Apple LLVM version 10.0.1 (clang-1001.0.46.4)
Target: x86_64-apple-darwin18.6.0
Thread model: posix
InstalledDir: /Library/Developer/CommandLineTools/usr/bin
Cargo.toml:
[build-dependencies]
cc = "1.0"
如果有任何好的想法,我愿意接受更好的方法将 node.js 嵌入到 Rust 中。
我发现了一些问题,一旦解决,就可以在 Rust 中成功静态 linking node.js。
node_main.cc
未包含在 libnode.a
输出中
我通过使用 nm
查找符号找到了这个。我无法在符号列表中找到 node_main()
和 node_main.o
。因此我意识到 node_main.cc
中的任何内容都不会被导出。
修复:在另一个文件中公开库入口点 C 函数,例如 node.cc
。请注意,我们在这里添加了一个全新的函数,该函数调用 node::Start()
node.cc
extern "C" int node_main(int argc, char** argv) {
return node::Start(argc, argv);
}
需要 extern "C"
因为 c++ 名称符号重整
同样,使用 nm
工具搜索 libnode.a
文件中的所有符号,我发现 node_main()
函数符号被破坏了。为了从 Rust 中找到这个符号,它不能被破坏,这是通过 extern "C"
修复:确保使用 extern "C"
node.cc
extern "C" int node_main()
根据节点版本,缺少一些额外的符号
根据 node.js Github 问题 #27431 一些符号存根没有在静态库中得到输出。我正在使用 node.js v13.x.x 标签,这是一个问题,所以我不得不为这些额外的存根创建库,并在 Rust 构建配置中 link 它们。
修复:从存根创建静态库并在 build.rs
ar rcs obj/Release/lib_stub_code_cache.a obj/Release/obj.target/cctest/src/node_code_cache_stub.o
ar rcs obj/Release/lib_stub_snapshot.a obj/Release/obj.target/cctest/src/node_snapshot_stub.o
最终结果
建筑物node.js
git clone https://github.com/nodejs/node
cd node
printf 'extern "C" int node_main(int argc, char** argv) { return node::Start(argc, argv); }' >> src/node.cc
./configure --enable-static
make -j4
# temporary fix: https://github.com/nodejs/node/issues/27431#issuecomment-487288275
REL=obj/Release
STUBS=$REL/obj.target/cctest/src
ar rcs "$REL/lib_stub_code_cache.a $STUBS/node_code_cache_stub.o"
ar rcs "$REL/lib_stub_snapshot.a $STUBS/node_snapshot_stub.o"
wrapper.cpp
#include <string>
#include <iostream>
using namespace std;
extern "C" {
int node_main(int argc, char** argv);
void run_node() {
cout << "hello there! general kenobi...\n";
char *args[] = { (char*)"tester.js", NULL };
node_main(1, args);
}
}
build.rs
extern crate cc;
fn main() {
cc::Build::new()
.cpp(true)
.file("wrapper.cpp")
.compile("libwrapper.a");
println!("cargo:rustc-link-search=native=../node/out/Release");
println!("cargo:rustc-link-lib=static=node");
println!("cargo:rustc-link-lib=static=uv");
// temporary fix - https://github.com/nodejs/node/issues/27431#issuecomment-487288275
println!("cargo:rustc-link-lib=static=_stub_code_cache");
println!("cargo:rustc-link-lib=static=_stub_snapshot");
// end temporary fix
println!("cargo:rustc-link-lib=static=v8_base_without_compiler");
println!("cargo:rustc-link-lib=static=v8_compiler");
println!("cargo:rustc-link-lib=static=v8_initializers");
println!("cargo:rustc-link-lib=static=v8_libbase");
println!("cargo:rustc-link-lib=static=v8_libplatform");
println!("cargo:rustc-link-lib=static=v8_libsampler");
println!("cargo:rustc-link-lib=static=v8_snapshot");
println!("cargo:rustc-link-lib=static=icuucx");
println!("cargo:rustc-link-lib=static=icui18n");
println!("cargo:rustc-link-lib=static=icudata");
println!("cargo:rustc-link-lib=static=icustubdata");
println!("cargo:rustc-link-lib=static=zlib");
println!("cargo:rustc-link-lib=static=brotli");
println!("cargo:rustc-link-lib=static=cares");
println!("cargo:rustc-link-lib=static=histogram");
println!("cargo:rustc-link-lib=static=http_parser");
println!("cargo:rustc-link-lib=static=llhttp");
println!("cargo:rustc-link-lib=static=nghttp2");
println!("cargo:rustc-link-lib=static=openssl");
}
main.rs
extern {
fn run_node();
}
fn main() {
println!("a surprise to be sure, but a welcome one");
unsafe { run_node(); }
}