最近看了下Rust,做爲系統編程語言,真的是很複雜。我計劃作一個簡單的Rust模塊,用於調用Dynamsoft Barcode Reader SDK,而後打包發佈到https://crates.io/。git
建立Rust lib工程:github
cargo new dbr --lib
在lib.rs
中添加:編程
pub mod reader;
reader
模塊對應reader.rs
文件,因此須要建立reader.h
, reader.c
:編程語言
// reader.h typedef struct Barcode { char* barcode_type; char* barcode_value; } Barcode; typedef __int32 int32_t; typedef void (*RustCallback)(int32_t, const char *, const char *); int32_t register_callback(RustCallback callback); void c_decodeFile(const char *fileName, const char *pszLicense);
// reader.c #include "reader.h" #include "DynamsoftBarcodeReader.h" #include <stdio.h> RustCallback cb; int32_t register_callback(RustCallback callback) { cb = callback; return 1; } void c_decodeFile(const char *fileName, const char *pszLicense) { void *hBarcode = DBR_CreateInstance(); if (hBarcode) { int ret = DBR_InitLicense(hBarcode, pszLicense); STextResultArray *paryResult = NULL; ret = DBR_DecodeFile(hBarcode, fileName, ""); DBR_GetAllTextResults(hBarcode, &paryResult); int count = paryResult->nResultsCount; printf("Barcode found: %d\n", count); int i = 0; for (; i < count; i++) { // printf("Index: %d, Barcode Type: %s, Value: %s\n", i, paryResult->ppResults[i]->pszBarcodeFormatString, paryResult->ppResults[i]->pszBarcodeText); if (cb) { cb(i, paryResult->ppResults[i]->pszBarcodeFormatString, paryResult->ppResults[i]->pszBarcodeText); } } DBR_FreeTextResults(&paryResult); DBR_DestroyInstance(hBarcode); } }
接下來用bindgen
把reader.h
轉換成reader.rs
:ui
cargo install bindgen bindgen src/reader.h -o src/reader.rs
在根目錄建立build.rs
用於構建工程。在構建的時候把.lib和.dll文件都拷貝到輸出目錄,而後把輸出目錄設置成庫的查找路徑:命令行
extern crate cc; extern crate bindgen; use std::env; use std::fs; use std::path::Path; fn main() { // Get the output path let out_dir = env::var("OUT_DIR").unwrap(); let package_offset = out_dir.find("package").unwrap_or(0); if package_offset == 0 { // Generates Rust FFI bindings. let bindings = bindgen::Builder::default() // The input header we would like to generate // bindings for. .header("src/reader.h") // Finish the builder and generate the bindings. .generate() // Unwrap the Result and panic on failure. .expect("Unable to generate bindings"); bindings .write_to_file("src/reader.rs") .expect("Couldn't write bindings!"); } // Build C code. cc::Build::new() .include("include") .file("src/reader.c") .compile("reader"); // Copy *.dll & .lib to the output path let dll_src: String = String::from("./platforms/win/DynamsoftBarcodeReaderx64.dll"); let dll_dest_path = Path::new(&out_dir).join("DynamsoftBarcodeReaderx64.dll"); let _dll_result = fs::copy(dll_src, dll_dest_path); let lib_src: String = String::from("./platforms/win/DBRx64.lib"); let lib_dest_path = Path::new(&out_dir).join("DBRx64.lib"); let _lib_result = fs::copy(lib_src, lib_dest_path); // Link Dynamsoft Barcode Reader. // println!("cargo:rustc-link-search={}", env!("DBR_LIB")); println!("cargo:rustc-link-search={}", &out_dir); println!("cargo:rustc-link-lib=DBRx64"); }
上面的代碼除了庫的連接,還包括C代碼的編譯以及.h文件到.rs文件的轉換。code
在Cargo.toml
中添加依賴orm
[build-dependencies] cc = "1.0" bindgen = "0.42.1"
生成reader.rs
:blog
cargo build
在打包前,記得把文件類型添加到Cargo.toml
中:token
include = [ "**/*.lib", "**/*.dll", "**/*.rs", "**/*.c", "**/*.h", "Cargo.toml", ] [lib] name = "dbr" path = "src/lib.rs"
Crate打包:
cargo package
在https://crates.io/me生成token,經過命令行登陸:
cargo login <token>
發佈Crate包:
cargo publish
這裏是我製做的dbr
包:https://crates.io/crates/dbr
建立一個新的Rust工程:
cargo new test
在Cargo.toml
中添加依賴:
[dependencies] dbr = "0.1.3"
編輯main.rs
:
extern crate dbr; use std::ffi::CStr; use std::ffi::CString; use std::os::raw::c_char; use std::env; use dbr::reader::*; extern "C" fn callback(index: i32, barcode_type: *const c_char, barcode_value: *const c_char) { unsafe { println!( "Index {}, {}, {}", index, CStr::from_ptr(barcode_type).to_str().unwrap(), CStr::from_ptr(barcode_value).to_str().unwrap() ); } } fn main() { let args: Vec<String> = env::args().collect(); if args.len() == 1 { println!("Please input an image file."); return } println!("Hello Dynamsoft Barcode Reader!"); unsafe { register_callback(Some(callback)); let image_file = CString::new(env::args().nth(1).expect("Missing argument")).unwrap(); let license = CString::new("t0068NQAAAFKYHV9xSZDEThUtClXNzxXH9TLSj/vYcY8mSKa0RxaGw3qNynyAMJ9Ib8UPxzFsbAMIugqPO313BvfiOdmZFTY=").unwrap(); c_decodeFile(image_file.as_ptr(), license.as_ptr()); } println!("Bye!"); }
運行程序:
cargo run <image file>