use crate::cache;
use crate::crate_utils;
use crate::state::State;
use anyhow::{bail, Context, Result};
use crate::strutil::{split, word_after};
use std::collections::HashSet;
use std::env::consts::{DLL_PREFIX, DLL_SUFFIX};
use std::io::IsTerminal;
use std::path::{Path, PathBuf};
use std::process;
fn simplify_qualified_names(text: &str) -> String {
let std = "std::";
let mut res = String::new();
let mut s = text;
while let Some(pos) = s.find(std) {
res.push_str(&s[0..pos]);
s = &s[pos + std.len()..];
if let Some(pos) = s.find("::") {
s = &s[pos + 2..];
}
}
res.push_str(s);
res
}
pub fn compile_crate(
state: &State,
crate_name: &str,
crate_path: &Path,
output_program: Option<&Path>,
mut extern_crates: Vec<String>,
features: Vec<String>,
) -> Result<bool> {
let verbose = state.verbose;
let simplify = state.simplify;
let debug = !state.optimize;
extern_crates.extend(state.externs.clone());
extern_crates.sort();
extern_crates.dedup();
if state.libc {
extern_crates.push("libc".into());
}
let mut cfg = state.cfg.clone();
let explicit_features = state.features.clone();
for f in if explicit_features.len() > 0 {
explicit_features
} else {
features
} {
cfg.push(format!("feature=\"{}\"", f));
}
let cache = cache::get_cache(&state)?;
let mut builder = process::Command::new("rustc");
if state.edition != "2015" {
builder.args(&["--edition", &state.edition]);
}
if !state.build_static {
builder
.args(&["-C", "prefer-dynamic"])
.args(&["-C", "debuginfo=0"]);
if let Some(link) = &state.link {
if verbose {
println!("linking against {}", link);
}
builder.arg("-L").arg(link);
}
} else {
builder.arg(if state.optimize { "-O" } else { "-g" });
if state.optimize {
builder.args(&["-C", "debuginfo=0"]);
}
}
builder.arg("-L").arg(&cache);
if !state.exe {
builder
.args(&["--crate-type", "dylib"])
.arg("--out-dir")
.arg(&cache)
.arg("--crate-name")
.arg(&crate_utils::proper_crate_name(crate_name));
} else {
builder.arg("-o").arg(output_program.unwrap());
}
for c in cfg {
builder.arg("--cfg").arg(&c);
}
let extern_crates: Vec<(String, String)> = if state.build_static && extern_crates.len() > 0 {
let m = cache::get_metadata()?;
let result = extern_crates
.into_iter()
.map(|c| {
Ok::<(String, String), String>((
m.get_full_crate_name(&c, debug).ok_or_else(|| c.clone())?,
c,
))
})
.collect::<Vec<_>>();
let (mut crates, mut errors) = (vec![], vec![]);
for r in result {
match r {
Ok(c) => crates.push(c),
Err(e) => errors.push(e),
}
}
if errors.len() > 0 {
cache::save_missing_crates(&errors)?;
bail!(
"no such crates '{}' in static cache: use --add . to add them",
errors.join(" ")
);
}
if verbose {
println!("{:?} crates", crates);
}
crates
} else {
extern_crates
.into_iter()
.map(|c| (format!("{}{}{}", DLL_PREFIX, c, DLL_SUFFIX), c))
.collect()
};
for (name, c) in extern_crates {
let full_path = PathBuf::from(&cache).join(&name);
let ext = format!("{}={}", c, full_path.display());
if verbose {
println!("extern {}", ext);
}
builder.arg("--extern").arg(&ext);
}
builder.arg(crate_path);
if simplify {
if std::io::stderr().is_terminal() {
builder.args(&["--color", "always"]);
}
let output = builder.output().context("can't run rustc")?;
let status = output.status.success();
if !status {
let err = String::from_utf8_lossy(&output.stderr);
eprintln!("{}", simplify_qualified_names(&err));
}
Ok(status)
} else {
Ok(builder.status().context("can't run rustc")?.success())
}
}
pub fn massage_snippet(
code: String,
prelude: String,
extern_crates: Vec<String>,
wild_crates: Vec<String>,
macro_crates: HashSet<String>,
body_prelude: String,
) -> Result<(String, Vec<String>)> {
use crate::strutil::{after, word_after};
fn indent_line(line: &str) -> String {
format!(" {}\n", line)
}
let mut prefix = prelude;
let mut crate_begin = String::new();
let mut body = String::new();
let mut deduced_externs = Vec::new();
body += &body_prelude;
if extern_crates.len() > 0 {
let aliases = cache::get_aliases()?;
for c in &extern_crates {
prefix += &if let Some(aliased) = aliases.get(c) {
format!("extern crate {} as {};\n", aliased, c)
} else {
let mac = if macro_crates.contains(c) {
"#[macro_use] "
} else {
""
};
format!("{}extern crate {};\n", mac, c)
};
}
for c in wild_crates {
prefix += &format!("use {}::*;\n", c);
}
}
let mut lines = code.lines();
let mut first = true;
for line in lines.by_ref() {
let line = line.trim_start();
if first {
if line.starts_with("#!/") || line.starts_with("//") {
continue;
}
first = false;
}
if let Some(rest) = after(line, "#[macro_use") {
if let Some(crate_name) = word_after(rest, "extern crate ") {
deduced_externs.push(crate_name);
}
prefix += line;
prefix.push('\n');
} else if line.starts_with("extern ") || line.starts_with("use ") {
extract_externs(line, &mut deduced_externs);
prefix += line;
prefix.push('\n');
} else if line.starts_with("#![") {
crate_begin += line;
crate_begin.push('\n');
} else if line.len() > 0 {
body += &indent_line(line);
break;
}
}
body.extend(lines.map(indent_line));
deduced_externs.extend(extern_crates);
deduced_externs.sort();
deduced_externs.dedup();
let massaged_code = format!(
"{}
{}
fn run(args: Vec<String>) -> std::result::Result<(),Box<dyn std::error::Error+Sync+Send>> {{
{} Ok(())
}}
fn main() {{
if let Err(e) = run(std::env::args().collect()) {{
println!(\"error: {{:?}}\",e);
}}
}}
",
crate_begin, prefix, body
);
Ok((massaged_code, deduced_externs))
}
pub fn extract_externs(line: &str, deduced_externs: &mut Vec<String>) -> bool {
if let Some(crate_name) = word_after(line, "extern crate ") {
deduced_externs.push(crate_name);
return true;
}
if let Some(path) = word_after(line, "use ") {
let (name, rest) = split(&path, ':');
if !["std", "core", "alloc", "crate"].contains(&name) || rest == "" {
deduced_externs.push(name.into());
return true;
}
}
false
}