Skip to content

Instantly share code, notes, and snippets.

@ben0x539
Created May 6, 2018 21:15
Show Gist options
  • Save ben0x539/9796e5d67b4e83c525f5919a764812a2 to your computer and use it in GitHub Desktop.
Save ben0x539/9796e5d67b4e83c525f5919a764812a2 to your computer and use it in GitHub Desktop.
#![feature(proc_macro, drain_filter)]
#[crate = "0.1"]
extern crate failure;
#[crate = "0.2"]
extern crate structopt;
#[crate = "0.7"]
extern crate sha2;
#[crate = "0.5"]
extern crate quote;
#[crate = { version = "0.13", features = ["full", "extra-traits"] }]
extern crate syn;
use std::{
ffi,
fs,
io::{
self,
Write,
},
path,
process,
};
use failure::{
Error,
};
use structopt:: {
StructOpt,
};
#[derive(structopt::StructOpt, Debug)]
struct Opt {
#[structopt()]
file: path::PathBuf,
#[structopt(parse(from_os_str))]
args: Vec<ffi::OsString>,
#[structopt(long = "cache-dir")]
cache_dir: Option<path::PathBuf>,
}
fn get_deps(ast: &mut syn::File) -> Result<Vec<(String, String)>, Error> {
let crate_path = syn::Path::from("crate");
let mut deps = Vec::new();
for item in &mut ast.items {
let krate = if let syn::Item::ExternCrate(ref mut k) = *item {
k
} else {
continue
};
let f = |attr: &mut syn::Attribute| attr.path == crate_path;
for attr in krate.attrs.drain_filter(f) {
deps.push((krate.ident.to_string(), attr.tts.to_string()));
break;
}
}
return Ok(deps);
}
fn read_file(p: &path::Path) -> Result<String, Error> {
let mut f = fs::File::open(p)?;
let mut s = String::new();
io::Read::read_to_string(&mut f, &mut s)?;
Ok(s)
}
fn make_hash(s: &str) -> String {
let hash = <sha2::Sha256 as sha2::Digest>::digest_str(s);
format!("{:x}", hash)
}
fn main() -> Result<(), Error> {
let opt = Opt::from_args();
let source = read_file(&opt.file)?;
let hash = make_hash(&source);
let mut ast = syn::parse_file(&source)?;
let deps = get_deps(&mut ast)?;
let cache_dir = opt.cache_dir.as_ref().map(|p| p.as_path())
.unwrap_or(path::Path::new("/tmp/rust-build"));
let name = opt.file.file_stem().and_then(|s| s.to_str()).unwrap_or("app");
let suffixed = format!("{}-{}", name, hash);
let dir = cache_dir.join(&suffixed);
fs::create_dir_all(dir.join("src"))?;
let manifest_path = dir.join("Cargo.toml");
let mut f = fs::File::create(&manifest_path)?;
writeln!(f, "\
[package]\n\
name = {:?}\n\
version = \"0.1.0\"\n\
\n\
[[bin]]\n\
name = {:?}\n\
path = \"src/main.rs\"
\n\
[dependencies]", suffixed, name)?;
for &(ref krate, ref spec) in &deps {
writeln!(f, "{} {}", krate, spec)?;
}
drop(f);
let mut f = fs::File::create(dir.join("src/main.rs"))?;
writeln!(f, "{}", quote::ToTokens::into_tokens(ast))?;
drop(f);
let status = process::Command::new("cargo")
.arg("run").arg("--manifest-path").arg(&manifest_path)
.args(&opt.args).spawn()?.wait()?;
if !status.success() {
process::exit(status.code().unwrap_or(1));
}
Ok(())
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment