context.rs
```rust
use cxx::UniquePtr;
use std::os::raw::c_long;
use cxx::rust::String;
use std::convert::TryInto;
// Import the BGV struct and its fields
use super::bgv::*;
#[cxx::bridge]
mod ffi {
unsafe extern "C++" {
include!("zno-helib-sys/ffi/ffi_wrapper.h");
type Context;
type ContextBuilderWrapper;
fn create_context_builder() -> UniquePtr;
fn build(builder: &mut UniquePtr) -> UniquePtr;
fn set_m(builder: &mut UniquePtr, m: i64);
fn set_p(builder: &mut UniquePtr, p: i64);
fn set_r(builder: &mut UniquePtr, r: i64);
// ... other methods
// For vectors, you might need to handle conversions yourself
// or write additional helper functions in C++
fn set_gens(builder: &mut UniquePtr, gens: &CxxVector);
fn set_ords(builder: &mut UniquePtr, ords: &CxxVector);
// ... other methods
fn create_bgv_context(
m: u64,
p: u64,
r: u64,
gens: Vec,
ords: Vec,
) -> UniquePtr;
// fn create_generic_context(
// m: i64,
// p: i64,
// r: i64,
// gens: Vec,
// ords: Vec,
// mparams: Option,
// bparams: Option,
// ) -> UniquePtr;
// Declare the new functions you've exposed in C++
fn get_m(context: &Context) -> u64;
fn is_bootstrappable(context: &Context) -> bool;
}
// External Rust functions, used to convert Rust types into types that can be used in C++:
extern "Rust" {
fn convert_to_vec(s: &str) -> Vec;
}
}
// Define the Rust struct to represent the C++ Context class
struct Context {
// Fields matching the C++ constructor arguments
m: c_long,
p: c_long,
r: c_long,
gens: Vec,
ords: Vec,
}
// Define methods for the Rust Context struct
impl Context {
pub fn convert_to_vec(s: &str) -> Vec {
s.split(',')
.filter_map(|s| s.parse::().ok())
.collect()
}
// Create Rust functions to create BGV contexts using FFI
pub fn create_bgv_context_default() -> UniquePtr {
ffi::create_context_default()
}
// Define a method to create a Context instance using one constructor
pub fn create_bgv_context_params(bgvs: BGV) -> UniquePtr {
let gens_vec: Vec = bgvs.gens.iter().cloned().collect();
let ords_vec: Vec = bgvs.ords.iter().cloned().collect();
ffi::create_context_params(bgvs.m, bgvs.p, bgvs.r, gens_vec, ords_vec)
}
pub fn create_bgv_context_params_extended(bgvs: BGV, mparams: Option, bparams: Option) -> UniquePtr {
let gens_vec: Vec = bgvs.gens.iter().cloned().collect();
let ords_vec: Vec = bgvs.ords.iter().cloned().collect();
ffi::create_context_params_extended(bgvs.m, bgvs.p, bgvs.r, gens_vec, ords_vec, mparams, bparams)
}
pub fn create_bgv_context_serializable(content: SerializableContent) -> UniquePtr {
ffi::create_context_serializable(content)
}
}
```
and this is the build script:
buil.rs
```rust
use std::collections::HashSet;
use std::env;
use std::ffi::OsString;
use std::fs;
use std::path::{Path, PathBuf};
use std::process::Command;
fn main() -> miette::Result<()> {
// cxx_build::CFG.exported_header_prefixes = vec!["helib"];
// Determine the project directory
let project_dir = env::var("CARGO_MANIFEST_DIR").unwrap();
let ffi_dir = Path::new(&project_dir).join("ffi");
let helib_dir = Path::new(&project_dir).join("src").join("helib_pack");
// let helib_dir = Path::new(&ffi_dir);
let helib_lib_dir = helib_dir.join("lib");
let helib_include_dir = helib_dir.join("include");
// Compile ffi_wrapper.cpp separately
let cpp_source = ffi_dir.join("ffi_wrapper.cpp");
let cpp_output = ffi_dir.join("ffi_wrapper"); // Output binary name
// Copy ffi_wrapper.h and ffi_wrapper.cpp alongside helib.h
let _ = fs::copy(ffi_dir.join("ffi_wrapper.h"), helib_include_dir.join("ffi_wrapper.h"));
let _ = fs::copy(ffi_dir.join("ffi_wrapper.cpp"), cpp_source.clone());
// Retrieve the CARGO_MANIFEST_DIR and OUT_DIR environment variables
let manifest_dir = env::var("CARGO_MANIFEST_DIR").unwrap();
let out_dir = env::var("OUT_DIR").unwrap();
let source_dir = Path::new(&manifest_dir).join("src/helib_pack/include");
let target_dir = Path::new(&manifest_dir).join("ffi");
// Recursively copy files from source to target directory
if let Err(e) = copy_dir_to(&source_dir, &target_dir) {
panic!("Failed to copy header files: {}", e);
}
// Allows Rust to call C++ functions defined in "ffi_wrapper.cpp" and use
// any C++ functionality encapsulated in "ffi_wrapper.cpp" separately from the original C++ code.
// let _ = Command::new("g++")
// .args(&["-std=c++17", "-shared", "-fPIC", "-o", &cpp_output.to_string_lossy(), &cpp_source.to_string_lossy()])
// .status()
// .expect("Failed to compile ffi_wrapper.cpp");
// // Generate Rust bindings using cxx_build
// cxx_build::bridge("src/cxx-bridges/context.rs")
// .file(cpp_source.to_string_lossy()) // Include the necessary C++ source files
// .flag_if_supported("-std=c++17")
// .compile("ffi_wrapper");
// Output the linker flags for the compiled wrapper C++ source
println!("cargo:rustc-link-search=native={}", ffi_dir.display());
println!("cargo:rustc-link-lib=dylib=ffi_wrapper");
println!("cargo:rerun-if-changed={}",ffi_dir.join("ffi_wrapper.h").display());
println!("cargo:rerun-if-changed={}",ffi_dir.join("ffi_wrapper.cpp").display());
// println!("cargo:rustc-cfg=exported_header_prefixes=\"helib\"");
// let helib_include_dir = std::path::PathBuf::from("src/helib_pack/include"); // include path
// let mut b = autocxx_build::Builder::new("src/lib.rs", &[&helib_include_dir])
// .extra_clang_args(&["-std=c++17"])
// .build()?;
// // This assumes all your C++ bindings are in main.rs
// b.flag_if_supported("-std=c++17")
// .compile("helib-autocxx"); // arbitrary library name, pick anything
// Compile cxx generated bindings. This is the name of the Rust FFI library
// that includes the generated Rust bindings for your C++ code.
// It is used to link the Rust code with the upstream C++ code.
let path: PathBuf = cpp_source.to_string_lossy().into_owned().into();
println!("cargo:warning=This is a test 1");
let mut cc_build = cxx_build::bridge("src/cxx-bridges/context.rs");
println!("cargo:warning=This is a test 2");
println!("cargo:include=/home/hedge/src/zno-fhe-src/zno-helib-sys/ffi");
// println!("cargo:include=/home/hedge/src/zno-fhe-src/zno-helib-sys/src/helib_pack/include");
// println!("cargo:include=/home/hedge/src/zno-fhe-src/zno-helib-sys/src/helib_pack/include/helib");
// println!("cargo:include=/home/hedge/src/zno-fhe-src/zno-helib-sys/src/helib_pack/include/helib/../");
cc_build.file(path)
.include("/home/hedge/src/zno-fhe-src/zno-helib-sys/ffi")
// .include("/home/hedge/src/zno-fhe-src/zno-helib-sys/src/helib_pack/include")
// .include("/home/hedge/src/zno-fhe-src/zno-helib-sys/src/helib_pack/include/helib")
.flag_if_supported("-std=c++17")
.compile("cxx-bridge-context"); // compile cxx generated bindings
println!("cargo:rerun-if-changed=src/lib.rs");
println!("cargo:rerun-if-changed=ffi/helib/helib.h");
// Add instructions to link to any C++ libraries you need.
let rtfcts = Build::new().artifacts();
rtfcts.print_cargo_metadata();
Ok(())
}
pub fn source_dir() -> PathBuf {
Path::new(env!("CARGO_MANIFEST_DIR")).join("helib")
}
pub fn version() -> &'static str {
env!("CARGO_PKG_VERSION")
}
/// Function to recursively copy directories
fn copy_dir_to(src: &Path, dst: &Path) -> std::io::Result<()> {
if !dst.is_dir() {
fs::create_dir_all(dst)?;
}
for entry_result in src.read_dir()? {
let entry = entry_result?;
let file_type = entry.file_type()?;
let src_path = src.join(entry.file_name());
let dst_path = dst.join(entry.file_name());
if file_type.is_dir() {
copy_dir_to(&src_path, &dst_path)?;
} else {
fs::copy(&src_path, &dst_path)?;
}
}
Ok(())
}
pub struct Build {
out_dir: Option,
target: Option,
host: Option,
}
pub struct Artifacts {
package_dir: PathBuf,
include_dir: PathBuf,
lib_dir: PathBuf,
bin_dir: PathBuf,
share_dir: PathBuf,
libs: Vec,
target: String,
}
impl Build {
pub fn new() -> Build {
Build {
out_dir: env::var_os("OUT_DIR").map(|s| PathBuf::from(s)),
target: env::var("TARGET").ok(),
host: env::var("HOST").ok(),
}
}
pub fn out_dir>(&mut self, path: P) -> &mut Build {
self.out_dir = Some(path.as_ref().to_path_buf());
self
}
pub fn target(&mut self, target: &str) -> &mut Build {
self.target = Some(target.to_string());
self
}
pub fn host(&mut self, host: &str) -> &mut Build {
self.host = Some(host.to_string());
self
}
fn cmd_make(&self) -> Command {
let host = &self.host.as_ref().expect("HOST dir not set")[..];
if host.contains("dragonfly")
|| host.contains("freebsd")
|| host.contains("openbsd")
|| host.contains("solaris")
|| host.contains("illumos")
{
Command::new("gmake")
} else {
Command::new("make")
}
}
#[cfg(windows)]
fn check_env_var(&self, var_name: &str) -> Option {
env::var_os(var_name).map(|s| {
if s == "1" {
// a message to stdout, let user know asm is force enabled
println!(
"{}: nasm.exe is force enabled by the \
'ZNO_RUST_USE_NASM' env var.",
env!("CARGO_PKG_NAME")
);
true
} else if s == "0" {
// a message to stdout, let user know asm is force disabled
println!(
"{}: nasm.exe is force disabled by the \
'ZNO_RUST_USE_NASM' env var.",
env!("CARGO_PKG_NAME")
);
false
} else {
panic!(
"The environment variable {} is set to an unacceptable value: {:?}",
var_name, s
);
}
})
}
// #[cfg(windows)]
// fn is_nasm_ready(&self) -> bool {
// self.check_env_var("ZNO_RUST_USE_NASM")
// .unwrap_or_else(|| {
// // On Windows, use cmd `where` command to check if nasm is installed
// let wherenasm = Command::new("cmd")
// .args(&["/C", "where nasm"])
// .output()
// .expect("Failed to execute `cmd`.");
// wherenasm.status.success()
// })
// }
// #[cfg(not(windows))]
// fn is_nasm_ready(&self) -> bool {
// // We assume that nobody would run nasm.exe on a non-windows system.
// false
// }
pub fn artifacts(&mut self) -> Artifacts {
let target = &self.target.as_ref().expect("TARGET dir not set")[..];
let out_dir = self.out_dir.as_ref().expect("OUT_DIR not set");
// Generate files under the -sys crate src folder
let install_dir = env::var_os("CARGO_MANIFEST_DIR")
.map(|s| PathBuf::from(s))
.unwrap()
.join("src");
let libs = if target.contains("msvc") {
vec!["helibw".to_string(), "gmp".to_string(), "ntl".to_string()]
} else {
vec!["helib".to_string(), "gmp".to_string(), "ntl".to_string()]
};
let pd = install_dir.join("helib_pack");
Artifacts {
package_dir: pd.clone(),
lib_dir: pd.clone().join("lib"),
bin_dir: pd.clone().join("bin"),
share_dir: pd.clone().join("share"),
include_dir:pd.clone().join("include"),
libs,
target: target.to_string(),
}
}
}
impl Artifacts {
pub fn include_dir(&self) -> &Path {
&self.include_dir
}
pub fn lib_dir(&self) -> &Path {
&self.lib_dir
}
pub fn bin_dir(&self) -> &Path {
&self.bin_dir
}
pub fn package_dir(&self) -> &Path {
&self.package_dir
}
pub fn share_dir(&self) -> &Path {
&self.share_dir
}
pub fn libs(&self) -> &[String] {
&self.libs
}
pub fn target(&self) -> String {
self.target.clone()
}
pub fn print_cargo_metadata(&self) {
println!("cargo:rustc-link-search=native={}", self.lib_dir.display());
let libdirs = vec![self.lib_dir().to_path_buf()];
let kind = Self::determine_mode(&libdirs, &self.libs);
for lib in self.libs.clone().into_iter() {
println!("cargo:rustc-link-lib={}={}", kind, lib);
}
println!("cargo:include={}", self.include_dir.display());
println!("cargo:lib={}", self.lib_dir.display());
if self.target.contains("msvc") {
println!("cargo:rustc-link-lib=user32");
} else if self.target == "wasm32-wasi" {
println!("cargo:rustc-link-lib=wasi-emulated-signal");
println!("cargo:rustc-link-lib=wasi-emulated-process-clocks");
println!("cargo:rustc-link-lib=wasi-emulated-mman");
println!("cargo:rustc-link-lib=wasi-emulated-getpid");
}
}
pub fn env_inner(name: &str) -> Option {
let var = env::var_os(name);
println!("cargo:rerun-if-env-changed={}", name);
match var {
Some(ref v) => println!("{} = {}", name, v.to_string_lossy()),
None => println!("{} unset", name),
}
var
}
pub fn get_env(name: &str) -> Option {
let prefix = env::var("TARGET").unwrap().to_uppercase().replace('-', "_");
let prefixed = format!("{}_{}", prefix, name);
Self::env_inner(&prefixed).or_else(|| Self::env_inner(name))
}
/// Given a libdir (where artifacts are located) as well as the name
/// of the libraries we're linking to, figure out whether we should link them
/// statically or dynamically.
fn determine_mode(libdirs: &Vec, libs: &[String]) -> &'static str {
// First see if a mode was explicitly requested
let kind = Self::get_env("HELIB_STATIC");
match kind.as_ref().and_then(|s| s.to_str()) {
Some("0") => return "dylib",
Some(_) => return "static",
None => {}
}
// Next, see what files we actually have to link against, and see what our
// possibilities even are.
let mut files = HashSet::new();
for dir in libdirs {
for path in dir
.read_dir()
.unwrap()
.map(|e| e.unwrap())
.map(|e| e.file_name())
.filter_map(|e| e.into_string().ok())
{
files.insert(path);
}
}
let can_static = libs
.iter()
.all(|l| files.contains(&format!("lib{}.a", l)) || files.contains(&format!("{}.lib", l)));
let can_dylib = libs.iter().all(|l| {
files.contains(&format!("lib{}.so", l))
|| files.contains(&format!("{}.dll", l))
|| files.contains(&format!("lib{}.dylib", l))
});
match (can_static, can_dylib) {
(true, false) => return "static",
(false, true) => return "dylib",
(false, false) => {
panic!(
"libdir does not contain the required files \
to either statically or dynamically link HElib"
);
}
(true, true) => {}
}
// Ok, we've got not explicit preference and can *either* link statically or
// link dynamically. In the interest of "security upgrades" and/or "best
// practices with security libs", let's link dynamically.
"dylib"
}
}
```
Thanks for all the work on cxx. This is my first FFI build/project - apologies if I've done something silly... appreciate any hints or tips.
I observe this error:
When I build or test this cxx-bridge:
context.rs
```rust use cxx::UniquePtr; use std::os::raw::c_long; use cxx::rust::String; use std::convert::TryInto; // Import the BGV struct and its fields use super::bgv::*; #[cxx::bridge] mod ffi { unsafe extern "C++" { include!("zno-helib-sys/ffi/ffi_wrapper.h"); type Context; type ContextBuilderWrapper; fn create_context_builder() -> UniquePtrand this is the build script:
buil.rs
```rust use std::collections::HashSet; use std::env; use std::ffi::OsString; use std::fs; use std::path::{Path, PathBuf}; use std::process::Command; fn main() -> miette::Result<()> { // cxx_build::CFG.exported_header_prefixes = vec!["helib"]; // Determine the project directory let project_dir = env::var("CARGO_MANIFEST_DIR").unwrap(); let ffi_dir = Path::new(&project_dir).join("ffi"); let helib_dir = Path::new(&project_dir).join("src").join("helib_pack"); // let helib_dir = Path::new(&ffi_dir); let helib_lib_dir = helib_dir.join("lib"); let helib_include_dir = helib_dir.join("include"); // Compile ffi_wrapper.cpp separately let cpp_source = ffi_dir.join("ffi_wrapper.cpp"); let cpp_output = ffi_dir.join("ffi_wrapper"); // Output binary name // Copy ffi_wrapper.h and ffi_wrapper.cpp alongside helib.h let _ = fs::copy(ffi_dir.join("ffi_wrapper.h"), helib_include_dir.join("ffi_wrapper.h")); let _ = fs::copy(ffi_dir.join("ffi_wrapper.cpp"), cpp_source.clone()); // Retrieve the CARGO_MANIFEST_DIR and OUT_DIR environment variables let manifest_dir = env::var("CARGO_MANIFEST_DIR").unwrap(); let out_dir = env::var("OUT_DIR").unwrap(); let source_dir = Path::new(&manifest_dir).join("src/helib_pack/include"); let target_dir = Path::new(&manifest_dir).join("ffi"); // Recursively copy files from source to target directory if let Err(e) = copy_dir_to(&source_dir, &target_dir) { panic!("Failed to copy header files: {}", e); } // Allows Rust to call C++ functions defined in "ffi_wrapper.cpp" and use // any C++ functionality encapsulated in "ffi_wrapper.cpp" separately from the original C++ code. // let _ = Command::new("g++") // .args(&["-std=c++17", "-shared", "-fPIC", "-o", &cpp_output.to_string_lossy(), &cpp_source.to_string_lossy()]) // .status() // .expect("Failed to compile ffi_wrapper.cpp"); // // Generate Rust bindings using cxx_build // cxx_build::bridge("src/cxx-bridges/context.rs") // .file(cpp_source.to_string_lossy()) // Include the necessary C++ source files // .flag_if_supported("-std=c++17") // .compile("ffi_wrapper"); // Output the linker flags for the compiled wrapper C++ source println!("cargo:rustc-link-search=native={}", ffi_dir.display()); println!("cargo:rustc-link-lib=dylib=ffi_wrapper"); println!("cargo:rerun-if-changed={}",ffi_dir.join("ffi_wrapper.h").display()); println!("cargo:rerun-if-changed={}",ffi_dir.join("ffi_wrapper.cpp").display()); // println!("cargo:rustc-cfg=exported_header_prefixes=\"helib\""); // let helib_include_dir = std::path::PathBuf::from("src/helib_pack/include"); // include path // let mut b = autocxx_build::Builder::new("src/lib.rs", &[&helib_include_dir]) // .extra_clang_args(&["-std=c++17"]) // .build()?; // // This assumes all your C++ bindings are in main.rs // b.flag_if_supported("-std=c++17") // .compile("helib-autocxx"); // arbitrary library name, pick anything // Compile cxx generated bindings. This is the name of the Rust FFI library // that includes the generated Rust bindings for your C++ code. // It is used to link the Rust code with the upstream C++ code. let path: PathBuf = cpp_source.to_string_lossy().into_owned().into(); println!("cargo:warning=This is a test 1"); let mut cc_build = cxx_build::bridge("src/cxx-bridges/context.rs"); println!("cargo:warning=This is a test 2"); println!("cargo:include=/home/hedge/src/zno-fhe-src/zno-helib-sys/ffi"); // println!("cargo:include=/home/hedge/src/zno-fhe-src/zno-helib-sys/src/helib_pack/include"); // println!("cargo:include=/home/hedge/src/zno-fhe-src/zno-helib-sys/src/helib_pack/include/helib"); // println!("cargo:include=/home/hedge/src/zno-fhe-src/zno-helib-sys/src/helib_pack/include/helib/../"); cc_build.file(path) .include("/home/hedge/src/zno-fhe-src/zno-helib-sys/ffi") // .include("/home/hedge/src/zno-fhe-src/zno-helib-sys/src/helib_pack/include") // .include("/home/hedge/src/zno-fhe-src/zno-helib-sys/src/helib_pack/include/helib") .flag_if_supported("-std=c++17") .compile("cxx-bridge-context"); // compile cxx generated bindings println!("cargo:rerun-if-changed=src/lib.rs"); println!("cargo:rerun-if-changed=ffi/helib/helib.h"); // Add instructions to link to any C++ libraries you need. let rtfcts = Build::new().artifacts(); rtfcts.print_cargo_metadata(); Ok(()) } pub fn source_dir() -> PathBuf { Path::new(env!("CARGO_MANIFEST_DIR")).join("helib") } pub fn version() -> &'static str { env!("CARGO_PKG_VERSION") } /// Function to recursively copy directories fn copy_dir_to(src: &Path, dst: &Path) -> std::io::Result<()> { if !dst.is_dir() { fs::create_dir_all(dst)?; } for entry_result in src.read_dir()? { let entry = entry_result?; let file_type = entry.file_type()?; let src_path = src.join(entry.file_name()); let dst_path = dst.join(entry.file_name()); if file_type.is_dir() { copy_dir_to(&src_path, &dst_path)?; } else { fs::copy(&src_path, &dst_path)?; } } Ok(()) } pub struct Build { out_dir: Optionwhich generates this code:
context.rs.cc
```cpp #include "zno-helib-sys/ffi/ffi_wrapper.h" #include