Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Working with cargo #47

Merged
merged 20 commits into from
Feb 17, 2025
Merged
Show file tree
Hide file tree
Changes from 15 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
119 changes: 65 additions & 54 deletions Cargo.lock

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

10 changes: 8 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,13 +1,19 @@
[package]
name = "stable_mir_pretty"
name = "stable_mir_json"
version = "0.1.0"
edition = "2021"
default-run = "stable_mir_json"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
dot-writer = "0.1.4"
tracing = "0.1"
anyhow = "1"
home = "0.5"

[[bin]]
name = "cargo_stable_mir_json"
path = "src/bin/cargo_stable_mir_json.rs"

[package.metadata.rust-analyzer]
# This package uses rustc crates.
Expand Down
14 changes: 14 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,3 +64,17 @@ To run the tests, do the following:
```shell
make integration-test
```

## Integration with `cargo`
Currently the system to integrate with cargo is to create a `.stable_mir_json` package that contains the libraries, binaries, and run script for `stable_mir_json`. This run script ensures that the the same library that built `stable_mir_json` is used in the `cargo` project. Here are the steps required:

1. Navigate to the project root of `stable_mir_json` and run
```bash
cargo run --bin cargo_stable_mir_json -- $PWD [OPTIONAL-PATH-FOR-DIR]
```

2. Navigate to `cargo` project and run
```bash
RUSTC=<PATH-TO-.stable_mir_json>/run.sh cargo build
```
NOTE: by default `<PATH-TO-.stable_mir_json>` is `~/.stable_mir_json`
178 changes: 178 additions & 0 deletions src/bin/cargo_stable_mir_json.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
use std::env;

use std::io::Write;
use std::os::unix::fs::PermissionsExt;
use std::path::{Path, PathBuf};

use anyhow::{bail, Result};

fn main() -> Result<()> {
let args: Vec<_> = env::args().collect();

let (repo_dir, maybe_user_provided_dir): (PathBuf, Option<PathBuf>) = match &args[..] {
[_, repo_dir] => (repo_dir.into(), None),
[_, repo_dir, user_provided_dir] => (repo_dir.into(), Some(user_provided_dir.into())),
_ => bail!("Usage: cargo run --bin cargo_stable_mir_json -- <PATH-TO-STABLE-MIR-JSON-REPO> [OPTIONAL-PATH-TO-CREATE-BUILD-DIR]"),
};

if !repo_dir.is_dir() {
bail!(
"Provided should be path to stable_mir_json repo, but {} is not a dir",
repo_dir.display()
);
}

if let Some(ref user_provided_dir) = maybe_user_provided_dir {
if !user_provided_dir.is_dir() {
bail!(
"Provided should be path to create the .stable_mir_json dir, but {} is not a dir",
user_provided_dir.display()
);
}
}

setup(repo_dir, maybe_user_provided_dir)
}

fn setup(repo_dir: PathBuf, maybe_user_provided_dir: Option<PathBuf>) -> Result<()> {
let smir_json_dir = smir_json_dir(maybe_user_provided_dir)?;
println!("Creating {} directory", smir_json_dir.display());
std::fs::create_dir(&smir_json_dir)?; // This errors is the directory already exists
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This errors is the directory already exists ... not sure that's desirable. We can leave it that way for now, though...
Also, when I suggested the user-defined location I thought this would be the final destination, not that a directory would be created there.


copy_artefacts(&repo_dir, &smir_json_dir)?;

let ld_library_path = record_ld_library_path(&smir_json_dir)?;
add_run_script(&smir_json_dir, &ld_library_path)
}

fn smir_json_dir(maybe_user_provided_dir: Option<PathBuf>) -> Result<PathBuf> {
let user_provided_dir = match maybe_user_provided_dir {
Some(user_provided_dir) => user_provided_dir,
None => home::home_dir().expect("couldn't find home directory"),
};
if !user_provided_dir.is_dir() {
bail!(
// We know this is home because main checked user_provided_dir already
"got home directory `{}` which isn't a directory",
user_provided_dir.display()
);
}
let smir_json_dir = user_provided_dir.join(".stable_mir_json");
Ok(smir_json_dir)
}

fn copy_artefacts(repo_dir: &Path, smir_json_dir: &Path) -> Result<()> {
let dev_dir = repo_dir.join("target/debug/");
let dev_rlib = dev_dir.join("libstable_mir_json.rlib");

let release_dir = repo_dir.join("target/release/");
let release_rlib = release_dir.join("libstable_mir_json.rlib");

if !dev_rlib.exists() && !release_rlib.exists() {
bail!(
"Neither dev rlib `{}`, nor release rlib `{}` exists",
dev_dir.display(),
release_dir.display()
);
}

// Debug
if dev_rlib.exists() {
cp_artefacts_from_profile(smir_json_dir, Profile::Dev(repo_dir))?;
}

// Release
if release_rlib.exists() {
cp_artefacts_from_profile(smir_json_dir, Profile::Release(repo_dir))?;
}

Ok(())
}

enum Profile<'a> {
Dev(&'a Path),
Release(&'a Path),
}

impl Profile<'_> {
fn folder_as_string(&self) -> String {
match self {
Profile::Dev(_) => "debug/".into(),
Profile::Release(_) => "release/".into(),
}
}

fn profile_dir(&self) -> PathBuf {
match self {
Profile::Dev(repo_dir) => repo_dir.join("target/debug/"),
Profile::Release(repo_dir) => repo_dir.join("target/release/"),
}
}
}

fn cp_artefacts_from_profile(smir_json_dir: &Path, profile: Profile) -> Result<()> {
let rlib = profile.profile_dir().join("libstable_mir_json.rlib");
let bin = profile.profile_dir().join("stable_mir_json");

// Stable MIR JSON bin and rlib
let smir_json_profile_dir = smir_json_dir.join(profile.folder_as_string());
std::fs::create_dir(&smir_json_profile_dir)?;

let smir_json_profile_rlib = smir_json_profile_dir.join("libstable_mir_json.rlib");
println!(
"Copying {} to {}",
rlib.display(),
smir_json_profile_rlib.display()
);
std::fs::copy(rlib, smir_json_profile_rlib)?;

let smir_json_profile_bin = smir_json_profile_dir.join("stable_mir_json");
println!(
"Copying {} to {}",
bin.display(),
smir_json_profile_bin.display()
);
std::fs::copy(bin, smir_json_profile_bin)?;

Ok(())
}

fn add_run_script(smir_json_dir: &Path, ld_library_path: &Path) -> Result<()> {
let run_script_path = smir_json_dir.join("run.sh");
let mut run_script = std::fs::File::create(&run_script_path)?;
writeln!(run_script, "#!/bin/bash")?;
writeln!(run_script, "set -eu")?;
writeln!(run_script)?;
writeln!(
run_script,
"export LD_LIBRARY_PATH={}",
ld_library_path.display(),
)?;
writeln!(
run_script,
"exec \"{}/debug/stable_mir_json\" \"$@\"",
smir_json_dir.display()
)?;

// Set the script permissions to -rwxr-xr-x
std::fs::set_permissions(run_script_path, std::fs::Permissions::from_mode(0o755))?;
Ok(())
}

fn record_ld_library_path(smir_json_dir: &Path) -> Result<PathBuf> {
const LOADER_PATH: &str = "LD_LIBRARY_PATH";
if let Some(paths) = env::var_os(LOADER_PATH) {
// Note: kani filters the LD_LIBRARY_PATH, not sure why as it is working locally as is
let mut ld_library_file = std::fs::File::create(smir_json_dir.join("ld_library_path"))?;

match paths.to_str() {
Some(ld_library_path) => {
writeln!(ld_library_file, "{}", ld_library_path)?;
Ok(ld_library_path.into())
}
None => bail!("Couldn't cast LD_LIBRARY_PATH to str"),
}
} else {
bail!("Couldn't read LD_LIBRARY_PATH from env"); // This should be unreachable
}
}
2 changes: 1 addition & 1 deletion src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ pub mod driver;
pub mod printer;
use driver::stable_mir_driver;
use printer::emit_smir;
use stable_mir_pretty::mk_graph::emit_dotfile;
use stable_mir_json::mk_graph::emit_dotfile;

fn main() {
let mut args: Vec<String> = env::args().collect();
Expand Down