feat: prove elm object cache is complete
This commit is contained in:
parent
0a5e25e4f0
commit
8354d51ecb
7 changed files with 583 additions and 157 deletions
301
src/elm.rs
Normal file
301
src/elm.rs
Normal file
|
|
@ -0,0 +1,301 @@
|
|||
use crate::reporting::{CompilerError, Problem};
|
||||
use elmi::DataBinary;
|
||||
use std::collections::HashMap;
|
||||
use std::fs::{self, canonicalize, metadata};
|
||||
use std::io::{self, BufRead, Error, ErrorKind, Write};
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::process::{Command};
|
||||
use tracing::info_span;
|
||||
|
||||
pub fn make(file: &Path, debug: bool, verbosity: u64) -> Result<(), Problem> {
|
||||
let mut command = Command::new("elm");
|
||||
command.arg("make").arg("--output").arg("/dev/null");
|
||||
|
||||
if debug {
|
||||
command.arg("--debug");
|
||||
}
|
||||
command.arg(file);
|
||||
|
||||
let span = info_span!("elm make target file");
|
||||
let timing_guard = span.enter();
|
||||
match command.output() {
|
||||
Ok(output) => {
|
||||
if !output.status.success() {
|
||||
io::stderr().write_all(&output.stderr).unwrap();
|
||||
io::stderr().write_all(&output.stdout).unwrap();
|
||||
return Err(CompilerError::FailedBuildingFixture.into());
|
||||
}
|
||||
if verbosity > 0 {
|
||||
io::stderr().write_all(&output.stderr).unwrap();
|
||||
}
|
||||
}
|
||||
Err(_io_err) => {
|
||||
// TODO handle this one
|
||||
return Err(Problem::Wildcard("elm failed".into()));
|
||||
}
|
||||
}
|
||||
drop(timing_guard);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn load_interfaces(elm_cache_dir: &Path) -> Result<HashMap<String, elmi::Interface>, Problem> {
|
||||
let mut interfaces = HashMap::new();
|
||||
|
||||
let entries = fs::read_dir(&elm_cache_dir)
|
||||
.map_err(|io_err| CompilerError::ReadInputFailed(io_err, elm_cache_dir.to_path_buf()))?;
|
||||
|
||||
for entry in entries {
|
||||
let entry = entry.map_err(|io_err| {
|
||||
CompilerError::ReadInputFailed(io_err, elm_cache_dir.to_path_buf())
|
||||
})?;
|
||||
let path = entry.path();
|
||||
if path.is_file() {
|
||||
match path.extension() {
|
||||
Some(ext) if ext == "elmi" => {
|
||||
// step 4 load all the modules
|
||||
let parse_span = info_span!("parse elmi");
|
||||
let _parse_guard = parse_span.enter();
|
||||
|
||||
let load_span = info_span!("file reads");
|
||||
let load_guard = load_span.enter();
|
||||
let data = std::fs::read(&path)
|
||||
.map_err(|io_err| CompilerError::ReadInputFailed(io_err, path.clone()))?;
|
||||
drop(load_guard);
|
||||
|
||||
let (_remaining, i) = elmi::Interface::get(&data).map_err(|_err| {
|
||||
CompilerError::FailedElmiParse("todo elmi parsing".to_owned())
|
||||
})?;
|
||||
|
||||
if let Some(stem) = path.file_stem() {
|
||||
// in theory the module name of the interface can be determined by
|
||||
// the filename in elm 0.19.0 and 0.19.1
|
||||
let module_name: String = stem
|
||||
.to_string_lossy()
|
||||
.split('-')
|
||||
.collect::<Vec<&str>>()
|
||||
.join(".");
|
||||
interfaces.insert(module_name, i);
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(interfaces)
|
||||
}
|
||||
|
||||
pub fn load_objects(elm_cache_dir: &Path) -> Result<HashMap<elmi::Global, elmi::Node>, Problem> {
|
||||
let mut objects = HashMap::new();
|
||||
|
||||
let entries = fs::read_dir(&elm_cache_dir)
|
||||
.map_err(|io_err| CompilerError::ReadInputFailed(io_err, elm_cache_dir.to_path_buf()))?;
|
||||
|
||||
for entry in entries {
|
||||
let entry = entry.map_err(|io_err| {
|
||||
CompilerError::ReadInputFailed(io_err, elm_cache_dir.to_path_buf())
|
||||
})?;
|
||||
let path = entry.path();
|
||||
if path.is_file() {
|
||||
match path.file_name() {
|
||||
Some(file_name) if file_name == "o.dat" => {
|
||||
let data = std::fs::read(&path)
|
||||
.map_err(|io_err| CompilerError::ReadInputFailed(io_err, path.clone()))?;
|
||||
|
||||
let (_remaining, opt_global_graph) = elmi::OptGlobalGraph::get(&data).map_err(|_err| {
|
||||
CompilerError::FailedElmiParse("todo elmi parsing".to_owned())
|
||||
})?;
|
||||
|
||||
objects.extend(opt_global_graph.nodes.into_iter());
|
||||
}
|
||||
_ => {},
|
||||
}
|
||||
match path.extension() {
|
||||
Some(ext) if ext == "elmo" => {
|
||||
// step 4 load all the modules
|
||||
let parse_span = info_span!("parse elmo");
|
||||
let _parse_guard = parse_span.enter();
|
||||
|
||||
let load_span = info_span!("file reads");
|
||||
let load_guard = load_span.enter();
|
||||
let data = std::fs::read(&path)
|
||||
.map_err(|io_err| CompilerError::ReadInputFailed(io_err, path.clone()))?;
|
||||
drop(load_guard);
|
||||
|
||||
let (_remaining, opt_local_graph) = elmi::OptLocalGraph::get(&data).map_err(|_err| {
|
||||
CompilerError::FailedElmiParse("todo elmi parsing".to_owned())
|
||||
})?;
|
||||
|
||||
objects.extend(opt_local_graph.nodes.into_iter());
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(objects)
|
||||
}
|
||||
|
||||
pub struct SymbolLoader {
|
||||
project_cache_directory: PathBuf,
|
||||
global_cache_directory: PathBuf,
|
||||
symbol_cache: HashMap<elmi::Global, elmi::Node>,
|
||||
}
|
||||
|
||||
impl SymbolLoader {
|
||||
pub fn try_load(&mut self, symbol: elmi::Global) -> Result<bool, Problem> {
|
||||
if self.symbol_cache.contains_key(&symbol) {
|
||||
return Ok(true);
|
||||
}
|
||||
|
||||
let elmi::Global(
|
||||
elmi::ModuleNameCanonical {
|
||||
ref package,
|
||||
ref module,
|
||||
},
|
||||
ref _identifier,
|
||||
) = symbol;
|
||||
|
||||
if *package == elmi::PackageName::new("author", "project") {
|
||||
let filename = module.0.split('.').collect::<Vec<&str>>().join("-");
|
||||
let mut path = self.project_cache_directory.join(filename);
|
||||
path.set_extension(".elmo");
|
||||
} else {
|
||||
let mut path = self.global_cache_directory.clone();
|
||||
path.push(&package.author);
|
||||
path.push(&package.project);
|
||||
// TODO figure out the version required.
|
||||
//path.push(version);
|
||||
path.push("artifacts.dat");
|
||||
}
|
||||
|
||||
Ok(false)
|
||||
}
|
||||
}
|
||||
|
||||
//pub fn load_symbol_table(elm_cache_dir: &Path) -> Result<HashMap<String, elmi::Interface>, Problem> {
|
||||
// // given a global symbol, calculate the folder that contains the cache file.
|
||||
// let (_remaining, i) = OptLocalGraph::get(&data).unwrap();
|
||||
//
|
||||
// let mut interfaces = HashMap::new();
|
||||
//
|
||||
// let entries = fs::read_dir(&elm_cache_dir)
|
||||
// .map_err(|io_err| CompilerError::ReadInputFailed(io_err, elm_cache_dir.to_path_buf()))?;
|
||||
//
|
||||
// for entry in entries {
|
||||
// let entry = entry.map_err(|io_err| {
|
||||
// CompilerError::ReadInputFailed(io_err, elm_cache_dir.to_path_buf())
|
||||
// })?;
|
||||
// let path = entry.path();
|
||||
// if path.is_file() {
|
||||
// match path.extension() {
|
||||
// Some(ext) if ext == "elmi" => {
|
||||
// // step 4 load all the modules
|
||||
// let parse_span = info_span!("parse elmi");
|
||||
// let _parse_guard = parse_span.enter();
|
||||
//
|
||||
// let load_span = info_span!("file reads");
|
||||
// let load_guard = load_span.enter();
|
||||
// let data = std::fs::read(&path)
|
||||
// .map_err(|io_err| CompilerError::ReadInputFailed(io_err, path.clone()))?;
|
||||
// drop(load_guard);
|
||||
//
|
||||
// let (_remaining, i) = elmi::Interface::get(&data).map_err(|_err| {
|
||||
// CompilerError::FailedElmiParse("todo elmi parsing".to_owned())
|
||||
// })?;
|
||||
//
|
||||
// if let Some(stem) = path.file_stem() {
|
||||
// // in theory the module name of the interface can be determined by
|
||||
// // the filename in elm 0.19.0 and 0.19.1
|
||||
// let module_name: String = stem
|
||||
// .to_string_lossy()
|
||||
// .split('-')
|
||||
// .collect::<Vec<&str>>()
|
||||
// .join(".");
|
||||
// interfaces.insert(module_name, i);
|
||||
// }
|
||||
// }
|
||||
// _ => (),
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// Ok(interfaces)
|
||||
//}
|
||||
|
||||
/// Look for the cargo.toml file that we are building to determine the root directory of the project
|
||||
/// (so that we can locate the source files)
|
||||
pub fn find_project_root<S: AsRef<Path>>(filename: S, search: S) -> io::Result<PathBuf> {
|
||||
if !search.as_ref().is_dir() {
|
||||
return Err(Error::new(
|
||||
ErrorKind::InvalidInput,
|
||||
format!("`{}` is not a directory", search.as_ref().display()),
|
||||
));
|
||||
}
|
||||
let mut path_buf = canonicalize(&search)?;
|
||||
|
||||
let error: io::Error = Error::new(
|
||||
ErrorKind::NotFound,
|
||||
format!(
|
||||
"could not find `{}` in `{:?}` or any parent directory",
|
||||
filename.as_ref().display(),
|
||||
path_buf
|
||||
),
|
||||
);
|
||||
|
||||
loop {
|
||||
let test_file = path_buf.as_path().join(&filename);
|
||||
match metadata(test_file) {
|
||||
Ok(_) => return Ok(path_buf),
|
||||
Err(_) => (),
|
||||
}
|
||||
match path_buf.as_path().parent() {
|
||||
Some(_) => (),
|
||||
None => return Err(error),
|
||||
}
|
||||
path_buf.pop();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_elm_home() -> io::Result<PathBuf> {
|
||||
match std::env::var("ELM_HOME") {
|
||||
Ok(elm_home) => Ok(PathBuf::from(elm_home)),
|
||||
Err(_) => {
|
||||
if let Some(mut dir) = home::home_dir() {
|
||||
dir.push(".elm");
|
||||
Ok(dir)
|
||||
} else {
|
||||
Err(std::io::Error::new(
|
||||
ErrorKind::NotFound,
|
||||
"ELM_HOME not set and home directory could not be calculated by `home` crate",
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parse_module_name(data: &[u8]) -> Result<elmi::Name, CompilerError> {
|
||||
if let Some(Ok(first_line)) = data.lines().next() {
|
||||
let mut tokens = first_line.split_whitespace();
|
||||
match tokens.next() {
|
||||
Some(token) if token == "port" => {
|
||||
tokens.next();
|
||||
}
|
||||
Some(token) if token == "module" => (),
|
||||
Some(token) if token == "effect" => {
|
||||
tokens.next();
|
||||
}
|
||||
_ => {
|
||||
return Err(CompilerError::CantParseModule(first_line.clone()).into());
|
||||
}
|
||||
};
|
||||
if let Some(module_name) = tokens.next() {
|
||||
Ok(elmi::Name(module_name.to_string()))
|
||||
} else {
|
||||
Err(CompilerError::CantParseModule(first_line.clone()).into())
|
||||
}
|
||||
} else {
|
||||
Err(CompilerError::EmptyModule.into())
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue