Skip to content

Commit 7712761

Browse files
authored
feat: MUJOCO_LIB (#46)
* feat: MUJOCO_LIB * revise build.rs * lock `cc` version * update CI * update examples' README * debug CI * fix CI::build to run cargo clean as needed * fix CI::build: environment variable handling * fix CI::build: environment variable handling * fix CI::test: update MUJOCO_DIR -> MUJOCO_LIB * update docs
1 parent dfc6ec0 commit 7712761

6 files changed

Lines changed: 149 additions & 28 deletions

File tree

.github/workflows/CI.yml

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -26,30 +26,41 @@ 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: build fails without MUJOCO_DIR
29+
- name: build fails without mujoco
3030
env:
3131
CARGO_BUILD_TARGET: ${{ matrix.arch }}-unknown-linux-gnu
3232
run: |
3333
if cargo build; then
34-
echo 'cargo build succeeded without MUJOCO_DIR, which is unexpected.'
34+
echo 'cargo build **unexpectedly** succeeded without mujoco.'
3535
exit 1
3636
else
3737
echo 'cargo build failed as expected without mujoco.'
3838
fi
3939
40-
- name: install mujoco and set MUJOCO_DIR
40+
- name: install mujoco
4141
run: |
4242
mkdir -p $HOME/.mujoco
4343
cd $HOME/.mujoco
4444
wget https://github.com/google-deepmind/mujoco/releases/download/3.3.2/mujoco-3.3.2-linux-${{ matrix.arch }}.tar.gz
4545
tar -xzf mujoco-3.3.2-linux-${{ matrix.arch }}.tar.gz
46-
echo "MUJOCO_DIR=$HOME/.mujoco/mujoco-3.3.2" >> $GITHUB_ENV
47-
echo "LD_LIBRARY_PATH=$HOME/.mujoco/mujoco-3.3.2/lib:$LD_LIBRARY_PATH" >> $GITHUB_ENV
4846
49-
- name: build succeeds with MUJOCO_DIR
47+
- name: build succeeds with MUJOCO_LIB
48+
env:
49+
CARGO_BUILD_TARGET: ${{ matrix.arch }}-unknown-linux-gnu
50+
run: |
51+
export MUJOCO_LIB="$HOME/.mujoco/mujoco-3.3.2/lib"
52+
cargo clean ### clean up the build cache to assure the build script is re-run ###
53+
cargo build
54+
cargo build --features bindgen
55+
git diff --exit-code ./src/bindgen.rs || (echo "bindgen.rs changed after build with bindgen feature."; exit 1)
56+
57+
- name: build succeeds using system mujoco without MUJOCO_LIB
5058
env:
5159
CARGO_BUILD_TARGET: ${{ matrix.arch }}-unknown-linux-gnu
5260
run: |
61+
sudo cp $HOME/.mujoco/mujoco-3.3.2/lib/libmujoco.so /usr/local/lib/
62+
sudo ldconfig
63+
cargo clean ### clean up the build cache to assure the build script is re-run ###
5364
cargo build
5465
cargo build --features bindgen
5566
git diff --exit-code ./src/bindgen.rs || (echo "bindgen.rs changed after build with bindgen feature."; exit 1)
@@ -81,7 +92,8 @@ jobs:
8192
mkdir -p $HOME/.mujoco && cd $HOME/.mujoco
8293
wget https://github.com/google-deepmind/mujoco/releases/download/3.3.2/$MUJOCO_FILENAME
8394
tar -xzf $MUJOCO_FILENAME
84-
echo "MUJOCO_DIR=$HOME/.mujoco/mujoco-3.3.2" >> $GITHUB_ENV
95+
### Set `MUJOCO_LIB` and `LD_LIBRARY_PATH` for the test jobs ###
96+
echo "MUJOCO_LIB=$HOME/.mujoco/mujoco-3.3.2/lib" >> $GITHUB_ENV
8597
echo "LD_LIBRARY_PATH=$HOME/.mujoco/mujoco-3.3.2/lib:$LD_LIBRARY_PATH" >> $GITHUB_ENV
8698
8799
- name: setup additional dependencies for examples

Cargo.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,13 @@ categories = ["api-bindings", "science::robotics", "simulation"]
1414

1515
[build-dependencies]
1616
bindgen = { optional = true, version = "=0.72.1" }
17+
cc = { version = "=1.2.45" }
1718

1819
[dev-dependencies]
1920
glfw = "0.60"
2021

2122
[features]
2223
bindgen = ["dep:bindgen"] # run bindgen at build time, instead of using pre-generated bindgen.rs
24+
25+
# internal
26+
DEBUG = []

README.md

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,10 @@
3232

3333
## Requirements
3434

35-
- [MuJoCo 3.3.2](https://github.com/google-deepmind/mujoco/releases/tag/3.3.2) downloaded
36-
and expanded **as it is** (don't move or rename the files within it)
37-
- `MUJOCO_DIR` environment variable set to the path of the MuJoCo directory (e.g. `$HOME/.mujoco/mujoco-3.3.2`)
35+
- [MuJoCo 3.3.2](https://github.com/google-deepmind/mujoco/releases/tag/3.3.2) downloaded and installed
36+
- Additionally, if you place mujoco library in a non-standard directory of the platform,
37+
you need `MUJOCO_LIB` environment variable set to the path of the directory containing
38+
`libmujoco.so` or `mujoco.lib` (e.g. `$HOME/.mujoco/mujoco-3.3.2/lib` when you placed the official release above in `~/.mujoco`)
3839

3940
### Note / Tips
4041

@@ -45,29 +46,31 @@
4546
```
4647
to download & expand MuJoCo 3.3.2.\
4748
On other platforms, do the same with the appropriate archive file for your system.
48-
49+
4950
- One way to setup is to install MuJoCo to _a default standard path_ like `/usr/local/lib/`
5051
(or a folder in _PATH_ on Windows), then if needed create symlink to `mujoco-3.3.2/lib/libmujoco.so` there,
5152
and insert to your shell config file:
5253
```sh
5354
# example on Linux with /usr/local/lib/
54-
export MUJOCO_DIR="/usr/local/lib/mujoco-3.3.2"
55+
export MUJOCO_LIB="/usr/local/lib/mujoco-3.3.2/lib"
5556
```
5657
Or if you'd like to avoid to install MuJoCo to such a system directory:
5758
```sh
5859
# example on Linux with $HOME/.mujoco/
59-
export MUJOCO_DIR="$HOME/.mujoco/mujoco-3.3.2"
60-
export LD_LIBRARY_PATH="$MUJOCO_DIR/lib:$LD_LIBRARY_PATH"
60+
export MUJOCO_LIB="$HOME/.mujoco/mujoco-3.3.2/lib"
61+
export LD_LIBRARY_PATH="$MUJOCO_LIB:$LD_LIBRARY_PATH"
6162
```
62-
63-
- Depending on your setting, be sure to specify `$MUJOCO_DIR/lib` as shared library path
64-
when executing your app (for example `LD_LIBRARY_PATH=$MUJOCO_DIR/lib cargo run` on Linux)
63+
64+
- Or, you can get MuJoCo library through Python toolchain like `uv` or `pip`.
65+
66+
- Depending on your setting, be sure to specify `$MUJOCO_LIB` as shared library path
67+
when executing your app (for example `LD_LIBRARY_PATH=$MUJOCO_LIB cargo run` on Linux)
6568

6669
## Example
6770

6871
```toml
6972
[dependencies]
70-
rusty_mujoco = "0.1"
73+
rusty_mujoco = "0.2"
7174
glfw = "0.60"
7275
```
7376

build.rs

Lines changed: 96 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,107 @@
11
fn main() {
22
if option_env!("DOCS_RS").is_some() { return }
33

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");
4+
if dbg!(!probe_mujoco_lib_with_libdir(None)) {
5+
if let Some(mujoco_lib_dir) = mujoco_lib_directory_from_env("MUJOCO_LIB") {
6+
assert!(
7+
dbg!(probe_mujoco_lib_with_libdir(Some(&mujoco_lib_dir))),
8+
"Failed to link with mujoco library even after setting `MUJOCO_LIB` environment variable!"
9+
);
10+
println!("cargo:rustc-link-search=native={}", mujoco_lib_dir.display());
11+
} else {
12+
panic!("\
13+
MuJoCo library not found. Make sure that the mujoco library is installed and, \
14+
if its location is non-standard, set the path via the `MUJOCO_LIB` environment variable.\
15+
");
16+
}
17+
}
18+
println!("cargo:rustc-link-lib=mujoco");
1019

1120
#[cfg(feature = "bindgen")]
1221
bindgen();
1322
}
1423

24+
fn probe_mujoco_lib_with_libdir(libdir: Option<&std::path::Path>) -> bool {
25+
let crate_root = std::path::Path::new(env!("CARGO_MANIFEST_DIR"));
26+
let vendor_dir = crate_root.join("vendor");
27+
28+
let probe_c = crate_root.join("probe.c");
29+
let vendor_include = vendor_dir.join("include");
30+
let vendor_include_mujoco = vendor_include.join("mujoco");
31+
32+
/*
33+
* `cc` crate is designed as:
34+
*
35+
* > A library for Cargo build scripts to compile a set of C/C++/assembly/CUDA files
36+
* into a static archive for Cargo to link into the crate being built.
37+
*
38+
* However, here we need to probe whether linking with `mujoco` library to an executable
39+
* succeeds or not, so we manually invoke the compiler with appropriate arguments,
40+
* using `cc` crate to get the compiler path and kind.
41+
*/
42+
43+
let cc = cc::Build::new().cargo_metadata(false).get_compiler();
44+
let cc_path = cc.path();
45+
let cc_args = if cc.is_like_gnu() || cc.is_like_clang() {
46+
let mut args = vec![
47+
probe_c.to_str().unwrap().to_string(),
48+
format!("-I{}", vendor_include.display()),
49+
format!("-I{}", vendor_include_mujoco.display()),
50+
"-o".to_string(), "/dev/null".to_string(),
51+
];
52+
if let Some(libdir) = libdir {
53+
args.push(format!("-L{}", libdir.display()));
54+
}
55+
args.push("-lmujoco".to_string());
56+
args
57+
} else if cc.is_like_msvc() || cc.is_like_clang_cl() {
58+
let mut args = vec![
59+
probe_c.to_str().unwrap().to_string(),
60+
format!("/I{}", vendor_include.display()),
61+
format!("/I{}", vendor_include_mujoco.display()),
62+
"/Fe:NUL".to_string(),
63+
];
64+
if let Some(libdir) = libdir {
65+
args.push(format!("/LIBPATH:{}", libdir.display()));
66+
}
67+
args.push("mujoco.lib".to_string());
68+
args
69+
} else {
70+
panic!("Unsupported compiler: {}", cc_path.display());
71+
};
72+
73+
std::process::Command::new(dbg!(cc_path))
74+
.args(dbg!(cc_args))
75+
.stdout(std::process::Stdio::inherit())
76+
.stderr(std::process::Stdio::inherit())
77+
.status()
78+
.unwrap_or_else(|err| panic!("Failed to invoke compiler to probe mujoco library: {err}"))
79+
.success()
80+
}
81+
82+
/// Adds the given path to the library search path for linking,
83+
/// with resolving the directory path from an environment variable `env`
84+
/// that may be either the directory path or path of the library file itself.
85+
fn mujoco_lib_directory_from_env(env: &'static str) -> Option<std::path::PathBuf> {
86+
let mujoco_lib = match std::env::var(env) {
87+
Ok(value) => value,
88+
Err(std::env::VarError::NotPresent) => return None,
89+
Err(std::env::VarError::NotUnicode(os_str)) => panic!("{env} contains invalid unicode: `{}`", os_str.to_string_lossy()),
90+
};
91+
let mujoco_lib = std::path::Path::new(&mujoco_lib);
92+
93+
Some(if mujoco_lib.is_dir() {
94+
mujoco_lib.to_owned()
95+
} else if mujoco_lib.is_file() {
96+
mujoco_lib
97+
.parent()
98+
.unwrap_or_else(|| panic!("{env} must be a valid path to mujoco library file or directory containing it"))
99+
.to_owned()
100+
} else {
101+
panic!("{env} must be a valid path to mujoco library file or directory containing it")
102+
})
103+
}
104+
15105
#[cfg(feature = "bindgen")]
16106
fn bindgen() {
17107
#[derive(Debug)]

examples/README.md

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,19 +15,25 @@ Pass the path to a MuJoCo model XML file as an argument. For example,
1515
you can use the `humanoid.xml` model provided by MuJoCo:
1616

1717
```sh
18-
cargo run --example visualize_left_object -- $MUJOCO_DIR/model/humanoid/humanoid.xml
18+
cargo run --example visualize_left_object -- $MUJOCO_LIB/../model/humanoid/humanoid.xml
1919
```
2020

21+
*(replace the path to humanoid.xml with yours)*
22+
2123
Options:
2224

2325
- `--camera <camera name>`: Specify the name of camera to use for visualization (optional).
2426
If not provided, the default camera will be used.
2527
- example: `--camera side` for the humanoid model
2628

27-
Depending on your system, you may need to give:
29+
Depending on your setting, you may need to specify:
2830

29-
- `MUJOCO_DIR` environment variable, to the MuJoCo directory path (e.g. `$HOME/.mujoco/mujoco-3.3.2`)
31+
- `MUJOCO_LIB` environment variable, to the MuJoCo directory path (e.g. `$HOME/.mujoco/mujoco-3.3.2/lib`)
3032
- `LD_LIBRARY_PATH` (Linux), `DYLD_LIBRARY_PATH` (macOS), or `PATH` (Windows) configuration
3133
for searching the MuJoCo library path
3234

33-
like `LD_LIBRARY_PATH="$MUJOCO_DIR/lib" cargo run --example visualize_left_object -- $MUJOCO_DIR/model/humanoid.xml`
35+
like:
36+
37+
```sh
38+
LD_LIBRARY_PATH="$MUJOCO_LIB" cargo run --example visualize_left_object -- $MUJOCO_LIB/../model/humanoid.xml
39+
```

probe.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
#include "mujoco/mujoco.h"
2+
3+
int main(void) {
4+
mj_version();
5+
return 0;
6+
}

0 commit comments

Comments
 (0)