Skip to content

Commit 98123dd

Browse files
authored
feat: Conditional bindgen generation (#43)
* feat: conditional bindgen generation * move rustfmt assertion into `fn bindgen` * fix
1 parent 90e9b1b commit 98123dd

3 files changed

Lines changed: 65 additions & 58 deletions

File tree

.github/workflows/CI.yml

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -26,15 +26,15 @@ jobs:
2626
rustup component add rustfmt ### required for the build script to work ###
2727
[ "${{ matrix.arch }}" = 'aarch64' ] && sudo apt update && sudo apt install -y g++-aarch64-linux-gnu || :
2828
29-
- name: check fails without MUJOCO_DIR
29+
- name: build fails without MUJOCO_DIR
3030
env:
3131
CARGO_BUILD_TARGET: ${{ matrix.arch }}-unknown-linux-gnu
3232
run: |
3333
if cargo build; then
34-
echo 'cargo check succeeded without mujoco, which is unexpected.'
34+
echo 'cargo build succeeded without MUJOCO_DIR, which is unexpected.'
3535
exit 1
3636
else
37-
echo 'cargo check failed as expected without mujoco.'
37+
echo 'cargo build failed as expected without mujoco.'
3838
fi
3939
4040
- name: install mujoco and set MUJOCO_DIR
@@ -46,17 +46,12 @@ jobs:
4646
echo "MUJOCO_DIR=$HOME/.mujoco/mujoco-3.3.2" >> $GITHUB_ENV
4747
echo "LD_LIBRARY_PATH=$HOME/.mujoco/mujoco-3.3.2/lib:$LD_LIBRARY_PATH" >> $GITHUB_ENV
4848
49-
- name: check succeeds with MUJOCO_DIR
49+
- name: build succeeds with MUJOCO_DIR
5050
env:
5151
CARGO_BUILD_TARGET: ${{ matrix.arch }}-unknown-linux-gnu
5252
run: |
53-
if cargo build; then
54-
echo 'cargo check succeeded with mujoco, as expected.'
55-
else
56-
echo 'cargo check failed with mujoco, which is unexpected.'
57-
echo "[DEBUG] bindgen.rs content:" && cat ./src/bindgen.rs
58-
exit 1
59-
fi
53+
cargo build
54+
cargo build --features bindgen
6055
6156
test:
6257
strategy:

Cargo.toml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,10 @@ keywords = ["mujoco", "rl", "ml", "physics", "robotics"]
1313
categories = ["api-bindings", "science::robotics", "simulation"]
1414

1515
[build-dependencies]
16-
bindgen = "0.72"
16+
bindgen = { optional = true, version = "0.72" }
1717

1818
[dev-dependencies]
1919
glfw = "0.60"
20+
21+
[features]
22+
bindgen = ["dep:bindgen"] # run bindgen at build time, instead of using pre-generated bindgen.rs

build.rs

Lines changed: 55 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -1,66 +1,76 @@
1-
use std::{env, io::BufRead, path::Path, process::{Command, Stdio}};
1+
fn main() {
2+
if option_env!("DOCS_RS").is_some() { return }
23

3-
#[derive(Debug)]
4-
struct TrimUnderscoreCallbacks;
5-
impl bindgen::callbacks::ParseCallbacks for TrimUnderscoreCallbacks {
6-
fn item_name(&self, item_info: bindgen::callbacks::ItemInfo) -> Option<String> {
7-
/*
8-
This finally oversets non-suffixed name (like `mjData`)
9-
to/over its original name (like `mjData_`).
10-
*/
11-
item_info.name.strip_suffix('_').map(str::to_owned)
12-
}
4+
let mujoco_dir = std::env::var("MUJOCO_DIR").expect("MUJOCO_DIR environment variable is not set");
5+
let mujoco_dir = std::path::Path::new(&mujoco_dir).canonicalize().expect("MUJOCO_DIR is not a valid path");
6+
let mujoco_lib = mujoco_dir.join("lib").to_str().unwrap().to_owned();
7+
8+
println!("cargo:rustc-link-search={mujoco_lib}");
9+
println!("cargo:rustc-link-lib=dylib=mujoco");
10+
11+
#[cfg(feature = "bindgen")]
12+
bindgen(mujoco_dir);
1313
}
1414

15-
#[derive(Debug)]
16-
struct MakeMjnConstantsCallbacks;
17-
impl bindgen::callbacks::ParseCallbacks for MakeMjnConstantsCallbacks {
18-
fn enum_variant_behavior(
19-
&self,
20-
_enum_name: Option<&str>,
21-
original_variant_name: &str,
22-
_variant_value: bindgen::callbacks::EnumVariantValue,
23-
) -> Option<bindgen::callbacks::EnumVariantCustomBehavior> {
24-
/*
25-
This generates const like:
26-
```
27-
pub const mjNTEXROLE: mjtTextureRole = mjtTextureRole::mjNTEXROLE;
28-
```
29-
at module top for `mjN*` variants.
30-
*/
31-
original_variant_name.starts_with("mjN").then_some(bindgen::callbacks::EnumVariantCustomBehavior::Constify)
15+
#[cfg(feature = "bindgen")]
16+
fn bindgen(mujoco_dir: impl AsRef<std::path::Path>) {
17+
#[derive(Debug)]
18+
struct TrimUnderscoreCallbacks;
19+
impl bindgen::callbacks::ParseCallbacks for TrimUnderscoreCallbacks {
20+
fn item_name(&self, item_info: bindgen::callbacks::ItemInfo) -> Option<String> {
21+
/*
22+
This finally oversets non-suffixed name (like `mjData`)
23+
to/over its original name (like `mjData_`).
24+
*/
25+
item_info.name.strip_suffix('_').map(str::to_owned)
26+
}
27+
}
28+
29+
#[derive(Debug)]
30+
struct MakeMjnConstantsCallbacks;
31+
impl bindgen::callbacks::ParseCallbacks for MakeMjnConstantsCallbacks {
32+
fn enum_variant_behavior(
33+
&self,
34+
_enum_name: Option<&str>,
35+
original_variant_name: &str,
36+
_variant_value: bindgen::callbacks::EnumVariantValue,
37+
) -> Option<bindgen::callbacks::EnumVariantCustomBehavior> {
38+
/*
39+
This generates const like:
40+
```
41+
pub const mjNTEXROLE: mjtTextureRole = mjtTextureRole::mjNTEXROLE;
42+
```
43+
at module top for `mjN*` variants.
44+
*/
45+
original_variant_name.starts_with("mjN").then_some(bindgen::callbacks::EnumVariantCustomBehavior::Constify)
46+
}
3247
}
33-
}
34-
35-
fn main() {
36-
if option_env!("DOCS_RS").is_some() { return }
3748

3849
/*
39-
* The hand-process step after `bindgen` generation assumes that
50+
* The hand-processing step after `bindgen` generation requires
4051
* `cargo fmt` (and then it's automatically applied to the
4152
* bindgen's raw output, and the hand-processing correctly works).
4253
* This is a **requirement** for the build script to continue.
4354
*/
4455
assert!(
45-
Command::new("cargo").args(["help", "fmt"]).stdout(Stdio::null()).status().is_ok_and(|s| s.success()),
56+
std::process::Command::new("cargo")
57+
.args(["help", "fmt"])
58+
.stdout(std::process::Stdio::null())
59+
.status()
60+
.is_ok_and(|s| s.success()),
4661
"`cargo fmt` is not available; This build script can't continue without it."
4762
);
4863

49-
let src_dir = Path::new(env!("CARGO_MANIFEST_DIR")).join("src");
64+
let mujoco_dir = mujoco_dir.as_ref();
65+
let mujoco_include = mujoco_dir.join("include").to_str().unwrap().to_owned();
66+
let mujoco_include_mujoco = mujoco_dir.join("include").join("mujoco").to_str().unwrap().to_owned();
67+
68+
let src_dir = std::path::Path::new(env!("CARGO_MANIFEST_DIR")).join("src");
5069
let bindgen_h = src_dir.join("bindgen.h").to_str().unwrap().to_owned();
5170
let bindgen_rs = src_dir.join("bindgen.rs").to_str().unwrap().to_owned();
5271

5372
println!("cargo:rerun-if-changed={bindgen_h}");
5473

55-
let mujoco_dir = std::env::var("MUJOCO_DIR").expect("MUJOCO_DIR environment variable is not set");
56-
let mujoco_dir = Path::new(&mujoco_dir).canonicalize().expect("MUJOCO_DIR is not a valid path");
57-
let mujoco_lib = mujoco_dir.join("lib").to_str().unwrap().to_owned();
58-
let mujoco_include = mujoco_dir.join("include").to_str().unwrap().to_owned();
59-
let mujoco_include_mujoco = mujoco_dir.join("include").join("mujoco").to_str().unwrap().to_owned();
60-
61-
println!("cargo:rustc-link-search={mujoco_lib}");
62-
println!("cargo:rustc-link-lib=dylib=mujoco");
63-
6474
let mut bindings = Vec::new();
6575
bindgen::builder()
6676
.header(bindgen_h)
@@ -98,8 +108,7 @@ fn main() {
98108
99109
using bindgen, so we do them manually...
100110
*/
101-
let bindings = bindings
102-
.lines()
111+
let bindings = std::io::BufRead::lines(&*bindings)
103112
.map(Result::unwrap)
104113
.fold(Vec::with_capacity(bindings.len()), |mut new, line| {
105114
if line.starts_with("pub struct mjt") {

0 commit comments

Comments
 (0)