rust-lang / rust-bindgen

Automatically generates Rust FFI bindings to C (and some C++) libraries.
https://rust-lang.github.io/rust-bindgen/
BSD 3-Clause "New" or "Revised" License
4.44k stars 695 forks source link

build.rs, Builder and no tests? #2913

Closed ghenry closed 2 months ago

ghenry commented 2 months ago

Just following the tut and notice no tests at the cargo test stage? There was when I ran bindgen by hand like so:

$ bindgen --allowlist-function "sentrypeer_config_new|sentrypeer_config_destroy" \
      conf.h -o ../sentrypeer_rust/src/sentrypeer_c/config.rs

$ bindgen sip_message_event.h
      -o ../sentrypeer_rust/src/sentrypeer_c/sip_message_event.rs

$ bindgen --allowlist-function "sip_log_event" sip_daemon.h \
      -o ../sentrypeer_rust/src/sentrypeer_c/sip_daemon.rs

Here are the ones I generated by hand:

Input C/C++ Header

wrapper.h:

#include "../src/conf.h"
#include "../src/sip_message_event.h"
#include "../src/sip_daemon.h"

https://github.com/SentryPeer/SentryPeer/blob/main/src/conf.h https://github.com/SentryPeer/SentryPeer/blob/main/src/sip_message_event.h https://github.com/SentryPeer/SentryPeer/blob/main/src/sip_daemon.h

Bindgen Invocation

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

fn main() {
    // Tell cargo to tell rustc to link the sentrypeer
    // shared library and how to find it
    println!("cargo:rustc-link-search=../.libs");
    println!("cargo:rustc-link-lib=sentrypeer");

    // Our other dependencies
    println!("cargo:rustc-link-lib=opendht-c");
    println!("cargo:rustc-link-lib=jansson");
    println!("cargo:rustc-link-lib=uuid");

    // 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.
        .header("wrapper.h")
        // Pick the functions we want to generate bindings for
        .allowlist_function("sentrypeer_config_new|sentrypeer_config_destroy")
        .allowlist_function("sip_message_event_new|sip_message_event_destroy")
        .allowlist_function("sip_log_event")
        // 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!");
}

My lib.rs;

// Use the include! macro to dump our generated bindings right into our crate's main entry point, 
// src/lib.rs:
include!(concat!(env!("OUT_DIR"), "/bindings.rs"));

My bindings.rs:

/* automatically generated by rust-bindgen 0.70.1 */

pub type pthread_t = ::std::os::raw::c_ulong;
pub type sa_family_t = ::std::os::raw::c_ushort;
#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct sockaddr {
    pub sa_family: sa_family_t,
    pub sa_data: [::std::os::raw::c_char; 14usize],
}
#[allow(clippy::unnecessary_operation, clippy::identity_op)]
const _: () = {
    ["Size of sockaddr"][::std::mem::size_of::<sockaddr>() - 16usize];
    ["Alignment of sockaddr"][::std::mem::align_of::<sockaddr>() - 2usize];
    ["Offset of field: sockaddr::sa_family"][::std::mem::offset_of!(sockaddr, sa_family) - 0usize];
    ["Offset of field: sockaddr::sa_data"][::std::mem::offset_of!(sockaddr, sa_data) - 2usize];
};
#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct dht_infohash {
    pub d: [u8; 20usize],
}
#[allow(clippy::unnecessary_operation, clippy::identity_op)]
const _: () = {
    ["Size of dht_infohash"][::std::mem::size_of::<dht_infohash>() - 20usize];
    ["Alignment of dht_infohash"][::std::mem::align_of::<dht_infohash>() - 1usize];
    ["Offset of field: dht_infohash::d"][::std::mem::offset_of!(dht_infohash, d) - 0usize];
};
#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct dht_op_token {
    _unused: [u8; 0],
}
#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct dht_runner {
    _unused: [u8; 0],
}
#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct sentrypeer_config {
    pub api_mode: bool,
    pub bgp_agent_mode: bool,
    pub debug_mode: bool,
    pub json_log_mode: bool,
    pub p2p_dht_mode: bool,
    pub sip_agent_mode: bool,
    pub sip_mode: bool,
    pub sip_responsive_mode: bool,
    pub syslog_mode: bool,
    pub verbose_mode: bool,
    pub webhook_mode: bool,
    pub webhook_url: *mut ::std::os::raw::c_char,
    pub oauth2_mode: bool,
    pub oauth2_client_id: *mut ::std::os::raw::c_char,
    pub oauth2_client_secret: *mut ::std::os::raw::c_char,
    pub oauth2_access_token: *mut ::std::os::raw::c_char,
    pub db_file: *mut ::std::os::raw::c_char,
    pub json_log_file: *mut ::std::os::raw::c_char,
    pub node_id: *mut ::std::os::raw::c_char,
    pub p2p_bootstrap_node: *mut ::std::os::raw::c_char,
    pub sip_daemon_thread: pthread_t,
    pub http_daemon: *mut MHD_Daemon,
    pub dht_node: *mut dht_runner,
    pub dht_info_hash: *mut dht_infohash,
    pub dht_op_token: *mut dht_op_token,
}
#[allow(clippy::unnecessary_operation, clippy::identity_op)]
const _: () = {
    ["Size of sentrypeer_config"][::std::mem::size_of::<sentrypeer_config>() - 128usize];
    ["Alignment of sentrypeer_config"][::std::mem::align_of::<sentrypeer_config>() - 8usize];
    ["Offset of field: sentrypeer_config::api_mode"]
        [::std::mem::offset_of!(sentrypeer_config, api_mode) - 0usize];
    ["Offset of field: sentrypeer_config::bgp_agent_mode"]
        [::std::mem::offset_of!(sentrypeer_config, bgp_agent_mode) - 1usize];
    ["Offset of field: sentrypeer_config::debug_mode"]
        [::std::mem::offset_of!(sentrypeer_config, debug_mode) - 2usize];
    ["Offset of field: sentrypeer_config::json_log_mode"]
        [::std::mem::offset_of!(sentrypeer_config, json_log_mode) - 3usize];
    ["Offset of field: sentrypeer_config::p2p_dht_mode"]
        [::std::mem::offset_of!(sentrypeer_config, p2p_dht_mode) - 4usize];
    ["Offset of field: sentrypeer_config::sip_agent_mode"]
        [::std::mem::offset_of!(sentrypeer_config, sip_agent_mode) - 5usize];
    ["Offset of field: sentrypeer_config::sip_mode"]
        [::std::mem::offset_of!(sentrypeer_config, sip_mode) - 6usize];
    ["Offset of field: sentrypeer_config::sip_responsive_mode"]
        [::std::mem::offset_of!(sentrypeer_config, sip_responsive_mode) - 7usize];
    ["Offset of field: sentrypeer_config::syslog_mode"]
        [::std::mem::offset_of!(sentrypeer_config, syslog_mode) - 8usize];
    ["Offset of field: sentrypeer_config::verbose_mode"]
        [::std::mem::offset_of!(sentrypeer_config, verbose_mode) - 9usize];
    ["Offset of field: sentrypeer_config::webhook_mode"]
        [::std::mem::offset_of!(sentrypeer_config, webhook_mode) - 10usize];
    ["Offset of field: sentrypeer_config::webhook_url"]
        [::std::mem::offset_of!(sentrypeer_config, webhook_url) - 16usize];
    ["Offset of field: sentrypeer_config::oauth2_mode"]
        [::std::mem::offset_of!(sentrypeer_config, oauth2_mode) - 24usize];
    ["Offset of field: sentrypeer_config::oauth2_client_id"]
        [::std::mem::offset_of!(sentrypeer_config, oauth2_client_id) - 32usize];
    ["Offset of field: sentrypeer_config::oauth2_client_secret"]
        [::std::mem::offset_of!(sentrypeer_config, oauth2_client_secret) - 40usize];
    ["Offset of field: sentrypeer_config::oauth2_access_token"]
        [::std::mem::offset_of!(sentrypeer_config, oauth2_access_token) - 48usize];
    ["Offset of field: sentrypeer_config::db_file"]
        [::std::mem::offset_of!(sentrypeer_config, db_file) - 56usize];
    ["Offset of field: sentrypeer_config::json_log_file"]
        [::std::mem::offset_of!(sentrypeer_config, json_log_file) - 64usize];
    ["Offset of field: sentrypeer_config::node_id"]
        [::std::mem::offset_of!(sentrypeer_config, node_id) - 72usize];
    ["Offset of field: sentrypeer_config::p2p_bootstrap_node"]
        [::std::mem::offset_of!(sentrypeer_config, p2p_bootstrap_node) - 80usize];
    ["Offset of field: sentrypeer_config::sip_daemon_thread"]
        [::std::mem::offset_of!(sentrypeer_config, sip_daemon_thread) - 88usize];
    ["Offset of field: sentrypeer_config::http_daemon"]
        [::std::mem::offset_of!(sentrypeer_config, http_daemon) - 96usize];
    ["Offset of field: sentrypeer_config::dht_node"]
        [::std::mem::offset_of!(sentrypeer_config, dht_node) - 104usize];
    ["Offset of field: sentrypeer_config::dht_info_hash"]
        [::std::mem::offset_of!(sentrypeer_config, dht_info_hash) - 112usize];
    ["Offset of field: sentrypeer_config::dht_op_token"]
        [::std::mem::offset_of!(sentrypeer_config, dht_op_token) - 120usize];
};
extern "C" {
    pub fn sentrypeer_config_new() -> *mut sentrypeer_config;
}
extern "C" {
    pub fn sentrypeer_config_destroy(self_ptr: *mut *mut sentrypeer_config);
}
#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct sip_message_event {
    pub packet: *mut ::std::os::raw::c_char,
    pub packet_len: usize,
    pub socket: ::std::os::raw::c_int,
    pub transport_type: *mut ::std::os::raw::c_char,
    pub client_ip_addr: *mut sockaddr,
    pub client_ip_addr_str: *mut ::std::os::raw::c_char,
    pub client_addr_len: usize,
    pub dest_ip_addr_str: *mut ::std::os::raw::c_char,
}
#[allow(clippy::unnecessary_operation, clippy::identity_op)]
const _: () = {
    ["Size of sip_message_event"][::std::mem::size_of::<sip_message_event>() - 64usize];
    ["Alignment of sip_message_event"][::std::mem::align_of::<sip_message_event>() - 8usize];
    ["Offset of field: sip_message_event::packet"]
        [::std::mem::offset_of!(sip_message_event, packet) - 0usize];
    ["Offset of field: sip_message_event::packet_len"]
        [::std::mem::offset_of!(sip_message_event, packet_len) - 8usize];
    ["Offset of field: sip_message_event::socket"]
        [::std::mem::offset_of!(sip_message_event, socket) - 16usize];
    ["Offset of field: sip_message_event::transport_type"]
        [::std::mem::offset_of!(sip_message_event, transport_type) - 24usize];
    ["Offset of field: sip_message_event::client_ip_addr"]
        [::std::mem::offset_of!(sip_message_event, client_ip_addr) - 32usize];
    ["Offset of field: sip_message_event::client_ip_addr_str"]
        [::std::mem::offset_of!(sip_message_event, client_ip_addr_str) - 40usize];
    ["Offset of field: sip_message_event::client_addr_len"]
        [::std::mem::offset_of!(sip_message_event, client_addr_len) - 48usize];
    ["Offset of field: sip_message_event::dest_ip_addr_str"]
        [::std::mem::offset_of!(sip_message_event, dest_ip_addr_str) - 56usize];
};
extern "C" {
    pub fn sip_message_event_new(
        packet: *mut ::std::os::raw::c_char,
        packet_len: usize,
        socket: ::std::os::raw::c_int,
        transport_type: *mut ::std::os::raw::c_char,
        client_ip_addr: *mut sockaddr,
        client_ip_addr_str: *mut ::std::os::raw::c_char,
        client_ip_addr_len: usize,
        dest_ip_addr_str: *mut ::std::os::raw::c_char,
    ) -> *mut sip_message_event;
}
extern "C" {
    pub fn sip_message_event_destroy(self_ptr: *mut *mut sip_message_event);
}
extern "C" {
    pub fn sip_log_event(
        config: *mut sentrypeer_config,
        sip_event: *const sip_message_event,
    ) -> ::std::os::raw::c_int;
}
#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct MHD_Daemon {
    pub _address: u8,
}

Expected Results

$ cargo test

to show layout tests like before?

Thanks.

emilio commented 2 months ago

The layout tests were intentionally switched to built-time tests, that's the:

const _: () = {
    ["Size of sip_message_event"][::std::mem::size_of::<sip_message_event>() - 64usize];
    ["Alignment of sip_message_event"][::std::mem::align_of::<sip_message_event>() - 8usize];
    ["Offset of field: sip_message_event::packet"]
        [::std::mem::offset_of!(sip_message_event, packet) - 0usize];

So this is somewhat intentional?

We have the old code gated on offset_of being stable (so you can get it by downgrading the --rust-target), but is there a strong reason to prefer the #[test] variant to the compile-time variant?

ghenry commented 2 months ago

We have the old code gated on offset_of being stable (so you can get it by downgrading the --rust-target), but is there a strong reason to prefer the #[test] variant to the compile-time variant?

No strong reason. Just thought I'd done something wrong :-)

One other question. This has generated a:

pub type pthread_t = ::std::os::raw::c_ulong;
pub type sa_family_t = ::std::os::raw::c_ushort;
#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct sockaddr {
    pub sa_family: sa_family_t,
    pub sa_data: [::std::os::raw::c_char; 14usize],
}

which is just libc::socket

Is there a way to tell the Builder this? I've read:

and have ended up here because I needed to get from SocketAddr to socket, so am using:

https://docs.rs/os_socketaddr/latest/os_socketaddr/

Trying to resolve:

mismatched types [E0308] expected `sockaddr`, found `libc::sockaddr` Note: `libc::sockaddr` and `sockaddr` have similar names, but are actually distinct types Note: `libc::sockaddr` is defined in crate `libc` Note: `sockaddr` is defined in the current crate Note: function defined here

Probably my beginner level again.

Thanks.

ghenry commented 2 months ago

Sorted it out:

let peer_addr_c: OsSocketAddr = peer_addr.into();
peer_addr_c.as_mut_ptr() as *mut sockaddr,
pvdrz commented 2 months ago

I'm closing this issue as the root issue has been clarified.