Skip to content

Commit 87540b1

Browse files
committed
add: added new header-file with type-definitions (def.h) + additional callback
The new callback is needed because bindgen cannot infer type-casted macros. For example the macro '#define SCIP_INVALID (double)1e99' is too complicated. This is because of the type-cast to double. If we simply remove the (double), then bindgen CAN generate a binding for it. It also defaults to an `f64` in Rust, which is what the C-representation would look like too. The callback should only be used for types of which we know that Rust can infer the datatype. Therefore, I made sure that it only goes off on; 1. macros that the user deliberately targets (added via `target`-method) 2. only looks at casts to clang-keywords. So no weird custom mumbo-jumbo casts
1 parent 932454f commit 87540b1

File tree

2 files changed

+79
-5
lines changed

2 files changed

+79
-5
lines changed

build.rs

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
mod bundled;
2+
mod callback;
23
mod from_source;
34

45
#[cfg(any(feature = "bundled", feature = "from-source"))]
@@ -13,6 +14,7 @@ use std::path::PathBuf;
1314

1415
use crate::from_source::{compile_scip, download_scip_source, is_from_source_feature_enabled};
1516
use bundled::*;
17+
use callback::DeriveCastedConstant;
1618

1719
#[cfg(not(feature = "bundled"))]
1820
pub fn is_bundled_feature_enabled() -> bool {
@@ -60,10 +62,18 @@ fn _build_from_scip_dir(path: &str) -> bindgen::Builder {
6062
.to_str()
6163
.unwrap()
6264
.to_owned();
65+
let scipdef_file = PathBuf::from(&path)
66+
.join("include")
67+
.join("scip")
68+
.join("def.h")
69+
.to_str()
70+
.unwrap()
71+
.to_owned();
6372

6473
bindgen::Builder::default()
6574
.header(scip_header_file)
6675
.header(scipdefplugins_header_file)
76+
.header(scipdef_file)
6777
.parse_callbacks(Box::new(bindgen::CargoCallbacks))
6878
.clang_arg(format!("-I{}", include_dir_path))
6979
}
@@ -100,9 +110,9 @@ fn try_system_include_paths() -> Option<bindgen::Builder> {
100110
let search_paths = vec![
101111
"/usr/include",
102112
"/usr/local/include",
103-
"/opt/local/include", // MacPorts
104-
"/opt/homebrew/include", // Homebrew ARM Mac
105-
"/usr/local/opt/scip/include", // Homebrew Intel Mac
113+
"/opt/local/include", // MacPorts
114+
"/opt/homebrew/include", // Homebrew ARM Mac
115+
"/usr/local/opt/scip/include", // Homebrew Intel Mac
106116
];
107117

108118
for base_path in search_paths {
@@ -118,7 +128,7 @@ fn try_system_include_paths() -> Option<bindgen::Builder> {
118128
.header(scip_h.to_str().unwrap())
119129
.header(scipdefplugins_h.to_str().unwrap())
120130
.parse_callbacks(Box::new(bindgen::CargoCallbacks))
121-
.clang_arg(format!("-I{}", base_path))
131+
.clang_arg(format!("-I{}", base_path)),
122132
);
123133
}
124134
}
@@ -188,14 +198,17 @@ fn main() -> Result<(), Box<dyn Error>> {
188198
println!("cargo:rustc-link-lib=soplex");
189199
}
190200
}
201+
// Setup the DeriveCastedConstant callback to target SCIP_INVALID
202+
let derive_casted_constant = DeriveCastedConstant::new().target("SCIP_INVALID");
191203

192204
let builder = builder
193205
.blocklist_item("FP_NAN")
194206
.blocklist_item("FP_INFINITE")
195207
.blocklist_item("FP_ZERO")
196208
.blocklist_item("FP_SUBNORMAL")
197209
.blocklist_item("FP_NORMAL")
198-
.parse_callbacks(Box::new(bindgen::CargoCallbacks));
210+
.parse_callbacks(Box::new(bindgen::CargoCallbacks))
211+
.parse_callbacks(Box::new(derive_casted_constant));
199212

200213
let bindings = builder.generate()?;
201214
let out_path = PathBuf::from(env::var("OUT_DIR").unwrap());

callback.rs

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
//! This file contains some custom callbacks for bindgen.
2+
3+
use bindgen::callbacks::{IntKind, Token, TokenKind};
4+
use std::collections::HashSet;
5+
6+
/// This callback will be used to remove the type casts.
7+
/// bindgen has a hard time parsing constants with type casts like
8+
/// ```rust
9+
/// #define SCIP_INVALID (double)1e99
10+
/// ```
11+
///
12+
/// ### Note;
13+
/// Maybe we should be careful on which macros we use this? I can see a situation where Rust and C
14+
/// would have different opinions on what the macro should look like.
15+
#[derive(Debug)]
16+
pub struct DeriveCastedConstant {
17+
/// Set of macros to target for removing type casts
18+
targets: HashSet<String>,
19+
}
20+
21+
impl DeriveCastedConstant {
22+
pub fn new() -> Self {
23+
DeriveCastedConstant {
24+
targets: HashSet::new(),
25+
}
26+
}
27+
28+
pub fn target(mut self, name: &str) -> Self {
29+
self.targets.insert(name.to_string());
30+
self
31+
}
32+
}
33+
34+
/// Implement the ParseCallbacks trait for DeriveCastedConstant
35+
impl bindgen::callbacks::ParseCallbacks for DeriveCastedConstant {
36+
fn modify_macro(&self, _name: &str, _tokens: &mut Vec<Token>) {
37+
// modify SCIP_INVALID
38+
if self.targets.contains(_name) {
39+
// So here we are looking for a pattern like ['(', type, ')']
40+
let position_cast = _tokens.windows(3).position(|window| match window {
41+
[Token {
42+
kind: TokenKind::Punctuation,
43+
raw: left_parenthesis,
44+
}, Token {
45+
// this will not go off on a cast like (SCIP_Real) as SCIP_Real is not a
46+
// Clang-keyword
47+
kind: TokenKind::Keyword,
48+
..
49+
}, Token {
50+
kind: TokenKind::Punctuation,
51+
raw: right_parenthesis,
52+
}] => **left_parenthesis == *b"(" && **right_parenthesis == *b")",
53+
_ => false,
54+
});
55+
if let Some(pos) = position_cast {
56+
// position found! So a macro with a type cast exists. We remove the typecast.
57+
*_tokens = [&_tokens[..pos], &_tokens[pos + 3..]].concat();
58+
}
59+
}
60+
}
61+
}

0 commit comments

Comments
 (0)