Hey everyone,
I'm trying to generate bindings for the libical project in Rust via CMake and Bindgen. My platform is MacOS 14.3.1 arm64, rustc version 1.76.0, and I'm trying to build for my local macOS.
I followed the Bindgen tutorial and it worked fine, but when I tried to apply it to my use case I started running into issues.
For context, the libical source lives at $PROJECT_ROOT/libical
. The project builds successfully, and I'm able to see the expected functions in the generated bindings.rs
file. However, when trying to build and run my lib.rs
tests, things break down and the project does not compile.
The two compilation errors I receive when running cargo test
:
linking with cc
error: linking with `cc` failed: exit status: 1
|
= note: env -u IPHONEOS_DEPLOYMENT_TARGET -u TVOS_DEPLOYMENT_TARGET LC_ALL="C" PATH="...
undefined symbols for arch arm64
= note: Undefined symbols for architecture arm64:
"_icalrecurrencetype_from_string", referenced from:
libical_sys::tests::test::heaee76a4405841fa in libical_sys-d5abc1bb0890726a.2sgdgyytue3hyrnx.rcgu.o
ld: symbol(s) not found for architecture arm64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
What follows are my files:
wrapper.h
#include "libical/ical.h"
build.rs
extern crate bindgen;
extern crate cmake;
use std::env;
use std::path::PathBuf;
use cmake::Config;
fn main() {
let dst = Config::new("libical")
.env("CMAKE_INSTALL_DATAROOTDIR", "./shared")
.define("CMAKE_BUILD_TYPE", "Release")
.define("ICAL_GLIB", "False")
.define("ENABLE_GTK_DOC", "OFF")
.build();
println!(
"cargo:rustc-link-search=native={}",
dst.join("lib").display()
);
// The bindgen::Builder is the main entry point
// to bindgen, and lets you build up options for
// the resulting bindings.
let bindings = bindgen::Builder::default()
// The input header we would like to generate
// bindings for.
.clang_arg(format!("-I{}/include", env::var("OUT_DIR").unwrap()))
.clang_arg(format!("-L{}/lib", env::var("OUT_DIR").unwrap()))
.header("wrapper.h")
// Tell cargo to invalidate the built crate whenever any of the
// included header files changed.
.parse_callbacks(Box::new(bindgen::CargoCallbacks::new()))
// Finish the builder and generate the bindings.
.generate()
// Unwrap the Result and panic on failure.
.expect("Unable to generate bindings");
// Write the bindings to the $OUT_DIR/bindings.rs file.
let out_path = PathBuf::from(env::var("OUT_DIR").unwrap());
bindings
.write_to_file(out_path.join("bindings.rs"))
.expect("Couldn't write bindings!");
}
lib.rs
#![allow(non_upper_case_globals)]
#![allow(non_camel_case_types)]
#![allow(non_snake_case)]
extern crate alloc;
include!(concat!(env!("OUT_DIR"), "/bindings.rs"));
#[cfg(test)]
mod tests {
use alloc::ffi::CString;
use std::os::raw::c_char;
use super::*;
#[test]
fn test() {
let rrule = CString::new("FREQ=YEARLY;BYDAY=SU,WE").unwrap();
unsafe {
let recur = icalrecurrencetype_from_string(rrule.as_ptr() as *const c_char);
println!("{:#?}", recur.freq);
}
}
}
What am I doing wrong? How can I link the appropriate files to ensure the project compiles for testing?