Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 19 additions & 7 deletions .github/workflows/CI.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,30 +26,41 @@ jobs:
rustup component add rustfmt ### required for the build script to work ###
[ "${{ matrix.arch }}" = 'aarch64' ] && sudo apt update && sudo apt install -y g++-aarch64-linux-gnu || :

- name: build fails without MUJOCO_DIR
- name: build fails without mujoco
env:
CARGO_BUILD_TARGET: ${{ matrix.arch }}-unknown-linux-gnu
run: |
if cargo build; then
echo 'cargo build succeeded without MUJOCO_DIR, which is unexpected.'
echo 'cargo build **unexpectedly** succeeded without mujoco.'
exit 1
else
echo 'cargo build failed as expected without mujoco.'
fi

- name: install mujoco and set MUJOCO_DIR
- name: install mujoco
run: |
mkdir -p $HOME/.mujoco
cd $HOME/.mujoco
wget https://github.com/google-deepmind/mujoco/releases/download/3.3.2/mujoco-3.3.2-linux-${{ matrix.arch }}.tar.gz
tar -xzf mujoco-3.3.2-linux-${{ matrix.arch }}.tar.gz
echo "MUJOCO_DIR=$HOME/.mujoco/mujoco-3.3.2" >> $GITHUB_ENV
echo "LD_LIBRARY_PATH=$HOME/.mujoco/mujoco-3.3.2/lib:$LD_LIBRARY_PATH" >> $GITHUB_ENV

- name: build succeeds with MUJOCO_DIR
- name: build succeeds with MUJOCO_LIB
env:
CARGO_BUILD_TARGET: ${{ matrix.arch }}-unknown-linux-gnu
run: |
export MUJOCO_LIB="$HOME/.mujoco/mujoco-3.3.2/lib"
cargo clean ### clean up the build cache to assure the build script is re-run ###
cargo build
cargo build --features bindgen
git diff --exit-code ./src/bindgen.rs || (echo "bindgen.rs changed after build with bindgen feature."; exit 1)

- name: build succeeds using system mujoco without MUJOCO_LIB
env:
CARGO_BUILD_TARGET: ${{ matrix.arch }}-unknown-linux-gnu
run: |
sudo cp $HOME/.mujoco/mujoco-3.3.2/lib/libmujoco.so /usr/local/lib/
sudo ldconfig
cargo clean ### clean up the build cache to assure the build script is re-run ###
cargo build
cargo build --features bindgen
git diff --exit-code ./src/bindgen.rs || (echo "bindgen.rs changed after build with bindgen feature."; exit 1)
Expand Down Expand Up @@ -81,7 +92,8 @@ jobs:
mkdir -p $HOME/.mujoco && cd $HOME/.mujoco
wget https://github.com/google-deepmind/mujoco/releases/download/3.3.2/$MUJOCO_FILENAME
tar -xzf $MUJOCO_FILENAME
echo "MUJOCO_DIR=$HOME/.mujoco/mujoco-3.3.2" >> $GITHUB_ENV
### Set `MUJOCO_LIB` and `LD_LIBRARY_PATH` for the test jobs ###
echo "MUJOCO_LIB=$HOME/.mujoco/mujoco-3.3.2/lib" >> $GITHUB_ENV
echo "LD_LIBRARY_PATH=$HOME/.mujoco/mujoco-3.3.2/lib:$LD_LIBRARY_PATH" >> $GITHUB_ENV

- name: setup additional dependencies for examples
Expand Down
4 changes: 4 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,13 @@ categories = ["api-bindings", "science::robotics", "simulation"]

[build-dependencies]
bindgen = { optional = true, version = "=0.72.1" }
cc = { version = "=1.2.45" }

[dev-dependencies]
glfw = "0.60"

[features]
bindgen = ["dep:bindgen"] # run bindgen at build time, instead of using pre-generated bindgen.rs

# internal
DEBUG = []
25 changes: 14 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,10 @@

## Requirements

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

### Note / Tips

Expand All @@ -45,29 +46,31 @@
```
to download & expand MuJoCo 3.3.2.\
On other platforms, do the same with the appropriate archive file for your system.

- One way to setup is to install MuJoCo to _a default standard path_ like `/usr/local/lib/`
(or a folder in _PATH_ on Windows), then if needed create symlink to `mujoco-3.3.2/lib/libmujoco.so` there,
and insert to your shell config file:
```sh
# example on Linux with /usr/local/lib/
export MUJOCO_DIR="/usr/local/lib/mujoco-3.3.2"
export MUJOCO_LIB="/usr/local/lib/mujoco-3.3.2/lib"
```
Or if you'd like to avoid to install MuJoCo to such a system directory:
```sh
# example on Linux with $HOME/.mujoco/
export MUJOCO_DIR="$HOME/.mujoco/mujoco-3.3.2"
export LD_LIBRARY_PATH="$MUJOCO_DIR/lib:$LD_LIBRARY_PATH"
export MUJOCO_LIB="$HOME/.mujoco/mujoco-3.3.2/lib"
export LD_LIBRARY_PATH="$MUJOCO_LIB:$LD_LIBRARY_PATH"
```

- Depending on your setting, be sure to specify `$MUJOCO_DIR/lib` as shared library path
when executing your app (for example `LD_LIBRARY_PATH=$MUJOCO_DIR/lib cargo run` on Linux)

- Or, you can get MuJoCo library through Python toolchain like `uv` or `pip`.

- Depending on your setting, be sure to specify `$MUJOCO_LIB` as shared library path
when executing your app (for example `LD_LIBRARY_PATH=$MUJOCO_LIB cargo run` on Linux)

## Example

```toml
[dependencies]
rusty_mujoco = "0.1"
rusty_mujoco = "0.2"
glfw = "0.60"
```

Expand Down
102 changes: 96 additions & 6 deletions build.rs
Original file line number Diff line number Diff line change
@@ -1,17 +1,107 @@
fn main() {
if option_env!("DOCS_RS").is_some() { return }

let mujoco_dir = std::env::var("MUJOCO_DIR").expect("MUJOCO_DIR environment variable is not set");
let mujoco_dir = std::path::Path::new(&mujoco_dir).canonicalize().expect("MUJOCO_DIR is not a valid path");
let mujoco_lib = mujoco_dir.join("lib").to_str().unwrap().to_owned();

println!("cargo:rustc-link-search={mujoco_lib}");
println!("cargo:rustc-link-lib=dylib=mujoco");
if dbg!(!probe_mujoco_lib_with_libdir(None)) {
if let Some(mujoco_lib_dir) = mujoco_lib_directory_from_env("MUJOCO_LIB") {
assert!(
dbg!(probe_mujoco_lib_with_libdir(Some(&mujoco_lib_dir))),
"Failed to link with mujoco library even after setting `MUJOCO_LIB` environment variable!"
);
println!("cargo:rustc-link-search=native={}", mujoco_lib_dir.display());
} else {
panic!("\
MuJoCo library not found. Make sure that the mujoco library is installed and, \
if its location is non-standard, set the path via the `MUJOCO_LIB` environment variable.\
");
}
}
println!("cargo:rustc-link-lib=mujoco");

#[cfg(feature = "bindgen")]
bindgen();
}

fn probe_mujoco_lib_with_libdir(libdir: Option<&std::path::Path>) -> bool {
let crate_root = std::path::Path::new(env!("CARGO_MANIFEST_DIR"));
let vendor_dir = crate_root.join("vendor");

let probe_c = crate_root.join("probe.c");
let vendor_include = vendor_dir.join("include");
let vendor_include_mujoco = vendor_include.join("mujoco");

/*
* `cc` crate is designed as:
*
* > A library for Cargo build scripts to compile a set of C/C++/assembly/CUDA files
* into a static archive for Cargo to link into the crate being built.
*
* However, here we need to probe whether linking with `mujoco` library to an executable
* succeeds or not, so we manually invoke the compiler with appropriate arguments,
* using `cc` crate to get the compiler path and kind.
*/

let cc = cc::Build::new().cargo_metadata(false).get_compiler();
let cc_path = cc.path();
let cc_args = if cc.is_like_gnu() || cc.is_like_clang() {
let mut args = vec![
probe_c.to_str().unwrap().to_string(),
format!("-I{}", vendor_include.display()),
format!("-I{}", vendor_include_mujoco.display()),
"-o".to_string(), "/dev/null".to_string(),
];
if let Some(libdir) = libdir {
args.push(format!("-L{}", libdir.display()));
}
args.push("-lmujoco".to_string());
args
} else if cc.is_like_msvc() || cc.is_like_clang_cl() {
let mut args = vec![
probe_c.to_str().unwrap().to_string(),
format!("/I{}", vendor_include.display()),
format!("/I{}", vendor_include_mujoco.display()),
"/Fe:NUL".to_string(),
];
if let Some(libdir) = libdir {
args.push(format!("/LIBPATH:{}", libdir.display()));
}
args.push("mujoco.lib".to_string());
args
} else {
panic!("Unsupported compiler: {}", cc_path.display());
};

std::process::Command::new(dbg!(cc_path))
.args(dbg!(cc_args))
.stdout(std::process::Stdio::inherit())
.stderr(std::process::Stdio::inherit())
.status()
.unwrap_or_else(|err| panic!("Failed to invoke compiler to probe mujoco library: {err}"))
.success()
}

/// Adds the given path to the library search path for linking,
/// with resolving the directory path from an environment variable `env`
/// that may be either the directory path or path of the library file itself.
fn mujoco_lib_directory_from_env(env: &'static str) -> Option<std::path::PathBuf> {
let mujoco_lib = match std::env::var(env) {
Ok(value) => value,
Err(std::env::VarError::NotPresent) => return None,
Err(std::env::VarError::NotUnicode(os_str)) => panic!("{env} contains invalid unicode: `{}`", os_str.to_string_lossy()),
};
let mujoco_lib = std::path::Path::new(&mujoco_lib);

Some(if mujoco_lib.is_dir() {
mujoco_lib.to_owned()
} else if mujoco_lib.is_file() {
mujoco_lib
.parent()
.unwrap_or_else(|| panic!("{env} must be a valid path to mujoco library file or directory containing it"))
.to_owned()
} else {
panic!("{env} must be a valid path to mujoco library file or directory containing it")
})
}

#[cfg(feature = "bindgen")]
fn bindgen() {
#[derive(Debug)]
Expand Down
14 changes: 10 additions & 4 deletions examples/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,19 +15,25 @@ Pass the path to a MuJoCo model XML file as an argument. For example,
you can use the `humanoid.xml` model provided by MuJoCo:

```sh
cargo run --example visualize_left_object -- $MUJOCO_DIR/model/humanoid/humanoid.xml
cargo run --example visualize_left_object -- $MUJOCO_LIB/../model/humanoid/humanoid.xml
```

*(replace the path to humanoid.xml with yours)*

Options:

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

Depending on your system, you may need to give:
Depending on your setting, you may need to specify:

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

like `LD_LIBRARY_PATH="$MUJOCO_DIR/lib" cargo run --example visualize_left_object -- $MUJOCO_DIR/model/humanoid.xml`
like:

```sh
LD_LIBRARY_PATH="$MUJOCO_LIB" cargo run --example visualize_left_object -- $MUJOCO_LIB/../model/humanoid.xml
```
6 changes: 6 additions & 0 deletions probe.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
#include "mujoco/mujoco.h"

int main(void) {
mj_version();
return 0;
}