Rust使用libuv库

本文版权归作者所有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

转载自夜明的孤行灯

本文链接地址: 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/

发表评论