Created
May 6, 2018 21:15
-
-
Save ben0x539/9796e5d67b4e83c525f5919a764812a2 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#![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