本文版权归作者所有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
转载自夜明的孤行灯
本文链接地址: https://www.huangyunkun.com/2024/04/03/rust-with-libuv-with-autocxx/
在使用Rust或多或少有需要调用外部库的需求,Rust FFI对于C和C++的处理有一些区别,为了简单快捷可以使用第三方工具来进行跨语言调用,比如autocxx。这里用libuv做一个演示。
libuv
是一个跨平台的异步 I/O 库,它被设计用来作为 Node.js 的新平台抽象层。libuv
提供了跨所有主要平台的统一的非阻塞 I/O 基础设施,包括 Windows、Linux、macOS 和其他类 Unix 系统。
我们先在github现在需要版本的libuv源代码,这里核心需要的是h文件,放到项目中,我这里用的third/libuv-1.48.0/include
在Cargo文件中配置依赖
[dependencies]
autocxx = "0.26.0"
cxx = "1.0"
[build-dependencies]
autocxx-build = "0.26.0"
miette = { version = "5", features = ["fancy"] }
再新增一个build.rs文件
fn main() -> miette::Result<()> {
let path = std::path::PathBuf::from("src");
let libuv_path = std::path::PathBuf::from("third/libuv-1.48.0/include");
let mut b = autocxx_build::Builder::new("src/main.rs", &[&path, &libuv_path]).build()?;
b.flag_if_supported("-std=c++14")
.compile("rc-sys");
println!("cargo:rerun-if-changed=src/main.rs");
Ok(())
}
这里先尝试一个简单的,我们打印下版本号
use autocxx::prelude::*;
include_cpp! {
#include "uv.h"
safety!(unsafe_ffi)
generate!("uv_default_loop")
}
fn main() {
unsafe {
println!("Hello, libuv! version:{}", ffi::UV_VERSION_MAJOR);
}
}
直接cargo run就能看到结果。
我们正常使用需要的是调用libuv对外暴露的方法,所以还是需要连接到libuv库。我们这里使用系统自带的,修改build.rs
fn main() -> miette::Result<()> {
let path = std::path::PathBuf::from("src");
let libuv_path = std::path::PathBuf::from("third/libuv-1.48.0/include");
let mut b = autocxx_build::Builder::new("src/main.rs", &[&path, &libuv_path]).build()?;
b.flag_if_supported("-std=c++14")
.compile("rc-sys");
println!("cargo:rerun-if-changed=src/main.rs");
println!("cargo:rustc-link-lib=uv");
Ok(())
}
在rust中我们起一个loop
use autocxx::prelude::*;
include_cpp! {
#include "uv.h"
safety!(unsafe_ffi)
generate!("uv_default_loop")
generate!("uv_loop_init")
generate!("uv_run")
generate!("uv_loop_close")
generate!("uv_run_mode")
}
fn main() {
unsafe {
println!("Hello, libuv!");
let uv_loop_new = ffi::uv_default_loop();
println!("Create a new loop: {:?}", uv_loop_new);
ffi::uv_loop_init(uv_loop_new);
println!("Initialize the loop: {:?}", uv_loop_new);
ffi::uv_run(uv_loop_new, ffi::uv_run_mode::UV_RUN_DEFAULT);
ffi::uv_loop_close(uv_loop_new);
println!("Close the loop: {:?}", uv_loop_new);
}
}
如果你更擅长C/C++,其实还可以在二次封装下libuv方法再在Rust中使用,可以避免很多问题。我们新增一个extras.h文件,并添加相关代码
#pragma once
#include <stdio.h>
#include <stdlib.h>
#include "uv.h"
inline int r_start_tcp_server() {
uv_loop_t *loop;
loop = uv_default_loop();
uv_tcp_t server;
uv_tcp_init(loop, &server);
struct sockaddr_in bind_addr;
uv_ip4_addr("0.0.0.0", 7000, &bind_addr);
uv_tcp_bind(&server, (const struct sockaddr *)&bind_addr, 0);
int r = uv_listen((uv_stream_t*) &server, 128, NULL);
if (r) {
fprintf(stderr, "Listen error %s\n", uv_err_name(r));
return 1;
}
return uv_run(loop, UV_RUN_DEFAULT);
}
在build.rs中添加extras.h的信息
fn main() -> miette::Result<()> {
let path = std::path::PathBuf::from("src");
let libuv_path = std::path::PathBuf::from("third/libuv-1.48.0/include");
let mut b = autocxx_build::Builder::new("src/main.rs", &[&path, &libuv_path]).build()?;
b.flag_if_supported("-std=c++14")
.compile("rc-sys");
println!("cargo:rerun-if-changed=src/main.rs");
println!("cargo:rerun-if-changed=src/extras.h");
println!("cargo:rustc-link-lib=uv");
Ok(())
}
在Rust中直接启动
use autocxx::prelude::*;
include_cpp! {
#include "uv.h"
#include "extras.h"
safety!(unsafe_ffi)
generate!("r_start_tcp_server")
}
fn main() {
println!("Hello, libuv!");
let code: autocxx::c_int = ffi::r_start_tcp_server();
println!("Start_tcp_server returned: {}", code.0);
}
如果我们还需要编译生成libuv的话,可以使用cmake-rs,在build.rs中添加
let dst = cmake::build("third/libuv-1.48.0");
println!("cargo:rustc-link-search=native={}", dst.display());
如果你对于cargo没有特别偏好,要使用bazel也行
说明:这里的代码只是一个演示,现实中应该不会存在在Rust中走libuv启动TCP的情况。
本文版权归作者所有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
转载自夜明的孤行灯
本文链接地址: https://www.huangyunkun.com/2024/04/03/rust-with-libuv-with-autocxx/