0

I am trying to port my simple application from C to Rust. It was running only on my Mac, with a library on Mac only. Here is a simplified version of the failed part in C code

// myLog.h
#include <os/log.h> // macOS header

void debug(const char *str);

//************************************

// myLog.c
#include "myLog.h"

void debug(const char* str) {
    // call the macOS log function
    os_log_debug(OS_LOG_DEFAULT, "%{public}s", str);
}

This code can be compiled simply calling gcc debug.c, and it works fine.

Then I added the .h and .c to my rust project with bindgen specified like below

fn main() {
    println!("cargo:rerun-if-changed=myLog.h");

    let bindings = bindgen::Builder::default()
        .header("myLog.h")
        .parse_callbacks(Box::new(bindgen::CargoCallbacks))
        .generate()
        .expect("Unable to build bindgen");
    let out_path = PathBuf::from(env::var("OUT_DIR").unwrap());
    bindings
        .write_to_file(out_path.join("mylog_bindings.rs"))
        .expect("Couldn't write bindings!");
}

And the main function has no other functions, but testing the log for now:

#![allow(non_upper_case_globals)]
#![allow(non_camel_case_types)]
#![allow(non_snake_case)]

use std::ffi::CString;
include!(concat!(env!("OUT_DIR"), "/mylog_bindings.rs"));

fn main() {
    let log_infomation = CString::new("Log from Rust").expect("Failed to create c string");
    let c_pointer = log_infomation.as_ptr();
    unsafe {
        debug(c_pointer);
    }
}

The program failed with following error:

error: linking with `cc` failed: exit code: 1
  |
  = note: "cc" "-m64" "-arch" "x86_64" "-L" ......
  = note: Undefined symbols for architecture x86_64:
            "_debug", referenced from:
                bindgen_test::main::hc0e5702b90adf92c in bindgen_test.3ccmhz8adio5obzw.rcgu.o
          ld: symbol(s) not found for architecture x86_64
          clang: error: linker command failed with exit code 1 (use -v to see invocation)
          

error: aborting due to previous error; 2 warnings emitted

error: could not compile `bindgen_test`.

I am not sure why this failed, but I found if I remove the whole unsafe block (without calling the function), the compilation will work. But can someone explain to me what I did wrong? Is there something I need to add to make it compile?

Thank you very much!

WatashiJ
  • 722
  • 6
  • 19

1 Answers1

0

The problem is that you are not including the myLog.c file anywhere, only the myLog.h header. This is what bindgen does: it converts a C header file into Rust code, but it does not compile the C code itself.

For that you need the cc crate. You have to use both cc and bindgen together in your build.rs file:

use std::env;
use std::path::PathBuf;

fn main() {
    println!("cargo:rerun-if-changed=myLog.h");
    println!("cargo:rerun-if-changed=myLog.c");  // new line here!!

    let bindings = bindgen::Builder::default()
        .header("myLog.h")
        .parse_callbacks(Box::new(bindgen::CargoCallbacks))
        .generate()
        .expect("Unable to build bindgen");
    let out_path = PathBuf::from(env::var("OUT_DIR").unwrap());
    bindings
        .write_to_file(out_path.join("mylog_bindings.rs"))
        .expect("Couldn't write bindings!");

    //Compile and link a static library named `myLog`:
    cc::Build::new()
        .file("myLog.c")
        .compile("myLog");
}

And do not forget to add the cc crate to your build-dependencies.

rodrigo
  • 94,151
  • 12
  • 143
  • 190