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
129 changes: 32 additions & 97 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 4 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "mlir-sys"
version = "220.0.0"
version = "220.0.1"
authors = ["McCoy R. Becker", "Yota Toyama", "Edgar Luque"]
keywords = ["llvm", "mlir"]
categories = ["external-ffi-bindings"]
Expand All @@ -11,5 +11,8 @@ readme = "README.md"
links = "MLIR"
license-file = "LICENSE"

[features]
no-version-check = []

[build-dependencies]
bindgen = "0.72.1"
10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,16 @@ cargo add mlir-sys

This crate searches an `llvm-config` command on build and uses it to determine build configurations related to LLVM and MLIR. You can also use a `MLIR_SYS_220_PREFIX` environment variable to specify a custom directory of LLVM installation.

The C API bindings are automatically generated at build time by discovering all MLIR C headers from the LLVM installation. This means the bindings will match whatever headers your LLVM build provides, even if it includes custom or out-of-tree dialects.

### Using MLIR from main

To build against MLIR built from the main branch (or any non-release version), enable the `no-version-check` feature to skip the LLVM version validation:

```sh
cargo add mlir-sys --features no-version-check
```

## License

[MIT](LICENSE)
70 changes: 56 additions & 14 deletions build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ use std::{
env,
error::Error,
ffi::OsStr,
fs::read_dir,
path::Path,
fs,
path::{Path, PathBuf},
process::{Command, Stdio, exit},
str,
};
Expand All @@ -19,25 +19,25 @@ fn main() {

fn run() -> Result<(), Box<dyn Error>> {
println!("cargo:rerun-if-changed=build.rs");
println!("cargo:rerun-if-changed=wrapper.h");

let link_mode = detect_link_mode();

let version = llvm_config("--version", &link_mode)?;
if !cfg!(feature = "no-version-check") {
let version = llvm_config("--version", &link_mode)?;

if !version.starts_with(&format!("{LLVM_MAJOR_VERSION}.")) {
return Err(format!(
"failed to find correct version ({LLVM_MAJOR_VERSION}.x.x) of llvm-config (found {version})"
)
.into());
if !version.starts_with(&format!("{LLVM_MAJOR_VERSION}.")) {
return Err(format!(
"failed to find correct version ({LLVM_MAJOR_VERSION}.x.x) of llvm-config (found {version})"
)
.into());
}
}

let directory = llvm_config("--libdir", &link_mode)?;
println!("cargo:rustc-link-search={directory}");

match link_mode {
LinkMode::Static => {
for entry in read_dir(&directory)? {
for entry in fs::read_dir(&directory)? {
if let Some(name) = entry?.path().file_name().and_then(OsStr::to_str)
&& name.starts_with("libMLIR")
&& let Some(name) = parse_static_lib_name(name)
Expand Down Expand Up @@ -106,9 +106,12 @@ fn run() -> Result<(), Box<dyn Error>> {
println!("cargo:rustc-link-lib={name}");
}

let include_dir = llvm_config("--includedir", &link_mode)?;
let wrapper_path = generate_wrapper(&include_dir)?;

bindgen::builder()
.header("wrapper.h")
.clang_arg(format!("-I{}", llvm_config("--includedir", &link_mode)?))
.header(wrapper_path.to_str().unwrap())
.clang_arg(format!("-I{include_dir}"))
.parse_callbacks(Box::new(bindgen::CargoCallbacks::new()))
.generate()
.unwrap()
Expand Down Expand Up @@ -220,7 +223,7 @@ fn parse_static_lib_name(name: &str) -> Option<&str> {
fn parse_shared_lib_name(name: &str) -> Option<&str> {
let name = name.strip_prefix("lib").unwrap_or(name);

// Handle libFoo.so, libFoo.so.21, libFoo.dylib
// Handle libFoo.so, libFoo.so.22, libFoo.dylib
if let Some(pos) = name.find(".so") {
Some(&name[..pos])
} else if let Some(name) = name.strip_suffix(".dylib") {
Expand All @@ -229,3 +232,42 @@ fn parse_shared_lib_name(name: &str) -> Option<&str> {
None
}
}

fn generate_wrapper(include_dir: &str) -> Result<PathBuf, Box<dyn Error>> {
let mlir_c_dir = Path::new(include_dir).join("mlir-c");
let mut headers = Vec::new();
collect_headers(&mlir_c_dir, &mlir_c_dir, &mut headers)?;
headers.sort();

let mut content = String::new();
for header in &headers {
content.push_str(&format!("#include <mlir-c/{header}>\n"));
}

let out_path = PathBuf::from(env::var("OUT_DIR")?).join("wrapper.h");
fs::write(&out_path, content)?;
Ok(out_path)
}

fn collect_headers(
base: &Path,
dir: &Path,
headers: &mut Vec<String>,
) -> Result<(), Box<dyn Error>> {
for entry in fs::read_dir(dir)? {
let entry = entry?;
let path = entry.path();

if path.is_dir() {
// Skip Bindings/ (Python bindings, not relevant for Rust FFI)
if path.file_name().and_then(OsStr::to_str) == Some("Bindings") {
continue;
}
collect_headers(base, &path, headers)?;
} else if path.extension().and_then(OsStr::to_str) == Some("h") {
let relative = path.strip_prefix(base)?;
headers.push(relative.to_string_lossy().into_owned());
}
}
Ok(())
}
Loading
Loading