-
-
Notifications
You must be signed in to change notification settings - Fork 82
feat: incremental compilation on hot reloading #49
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
Merged
baszalmstra
merged 5 commits into
mun-lang:master
from
baszalmstra:incremental_compilation
Nov 22, 2019
Merged
Changes from all commits
Commits
Show all changes
5 commits
Select commit
Hold shift + click to select a range
9074f03
feat: adds Driver to enable incremental compilation
baszalmstra e7ff644
feat: tests for runtime hotreloading and incremental compilation
baszalmstra 01e16e6
fix: linux file watching did not detect recreate
baszalmstra 77f6f64
Apply suggestions from code review
baszalmstra 8b6c3bf
refactor(test): added assert_invoke_eq! for runtime tests
baszalmstra File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
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
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
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
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,26 @@ | ||
| use mun_hir::salsa; | ||
|
|
||
| #[salsa::database( | ||
| mun_hir::SourceDatabaseStorage, | ||
| mun_hir::DefDatabaseStorage, | ||
| mun_hir::HirDatabaseStorage, | ||
| mun_codegen::IrDatabaseStorage | ||
| )] | ||
| #[derive(Debug)] | ||
| pub(crate) struct CompilerDatabase { | ||
| runtime: salsa::Runtime<CompilerDatabase>, | ||
| } | ||
|
|
||
| impl CompilerDatabase { | ||
| pub fn new() -> Self { | ||
| CompilerDatabase { | ||
| runtime: salsa::Runtime::default(), | ||
| } | ||
| } | ||
| } | ||
|
|
||
| impl salsa::Database for CompilerDatabase { | ||
| fn salsa_runtime(&self) -> &salsa::Runtime<CompilerDatabase> { | ||
| &self.runtime | ||
| } | ||
| } |
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,89 @@ | ||
| use mun_hir::diagnostics::{Diagnostic as HirDiagnostic, DiagnosticSink}; | ||
| use mun_hir::{FileId, HirDatabase, HirDisplay, Module}; | ||
| use mun_syntax::{ast, AstNode, SyntaxKind}; | ||
| use std::cell::RefCell; | ||
|
|
||
| mod emit; | ||
|
|
||
| pub use emit::Emit; | ||
| use mun_errors::{Diagnostic, Level}; | ||
|
|
||
| /// Constructs diagnostic messages for the given file. | ||
| pub fn diagnostics(db: &impl HirDatabase, file_id: FileId) -> Vec<Diagnostic> { | ||
| let parse = db.parse(file_id); | ||
| let mut result = Vec::new(); | ||
|
|
||
| result.extend(parse.errors().iter().map(|err| Diagnostic { | ||
| level: Level::Error, | ||
| loc: err.location(), | ||
| message: format!("Syntax Error: {}", err), | ||
| })); | ||
|
|
||
| let result = RefCell::new(result); | ||
| let mut sink = DiagnosticSink::new(|d| { | ||
| result.borrow_mut().push(Diagnostic { | ||
| level: Level::Error, | ||
| loc: d.highlight_range().into(), | ||
| message: d.message(), | ||
| }); | ||
| }) | ||
| .on::<mun_hir::diagnostics::UnresolvedValue, _>(|d| { | ||
| let text = d.expr.to_node(&parse.tree().syntax()).text().to_string(); | ||
| result.borrow_mut().push(Diagnostic { | ||
| level: Level::Error, | ||
| loc: d.highlight_range().into(), | ||
| message: format!("could not find value `{}` in this scope", text), | ||
| }); | ||
| }) | ||
| .on::<mun_hir::diagnostics::UnresolvedType, _>(|d| { | ||
| let text = d | ||
| .type_ref | ||
| .to_node(&parse.tree().syntax()) | ||
| .syntax() | ||
| .text() | ||
| .to_string(); | ||
| result.borrow_mut().push(Diagnostic { | ||
| level: Level::Error, | ||
| loc: d.highlight_range().into(), | ||
| message: format!("could not find type `{}` in this scope", text), | ||
| }); | ||
| }) | ||
| .on::<mun_hir::diagnostics::ExpectedFunction, _>(|d| { | ||
| result.borrow_mut().push(Diagnostic { | ||
| level: Level::Error, | ||
| loc: d.highlight_range().into(), | ||
| message: format!("expected function, found `{}`", d.found.display(db)), | ||
| }); | ||
| }) | ||
| .on::<mun_hir::diagnostics::MismatchedType, _>(|d| { | ||
| result.borrow_mut().push(Diagnostic { | ||
| level: Level::Error, | ||
| loc: d.highlight_range().into(), | ||
| message: format!( | ||
| "expected `{}`, found `{}`", | ||
| d.expected.display(db), | ||
| d.found.display(db) | ||
| ), | ||
| }); | ||
| }) | ||
| .on::<mun_hir::diagnostics::DuplicateDefinition, _>(|d| { | ||
| result.borrow_mut().push(Diagnostic { | ||
| level: Level::Error, | ||
| loc: match d.definition.kind() { | ||
| SyntaxKind::FUNCTION_DEF => { | ||
| ast::FunctionDef::cast(d.definition.to_node(&parse.tree().syntax())) | ||
| .map(|f| f.signature_range()) | ||
| .unwrap_or_else(|| d.highlight_range()) | ||
| .into() | ||
| } | ||
| _ => d.highlight_range().into(), | ||
| }, | ||
| message: d.message(), | ||
| }); | ||
| }); | ||
|
|
||
| Module::from(file_id).diagnostics(db, &mut sink); | ||
|
|
||
| drop(sink); | ||
| result.into_inner() | ||
| } |
File renamed without changes.
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,157 @@ | ||
| //! `Driver` is a stateful compiler frontend that enables incremental compilation by retaining state | ||
| //! from previous compilation. | ||
|
|
||
| use crate::{ | ||
| db::CompilerDatabase, | ||
| diagnostics::{diagnostics, Emit}, | ||
| PathOrInline, | ||
| }; | ||
| use mun_codegen::IrDatabase; | ||
| use mun_hir::{FileId, RelativePathBuf, SourceDatabase, SourceRoot, SourceRootId}; | ||
| use mun_target::spec::Target; | ||
| use std::{ | ||
| path::{Path, PathBuf}, | ||
| sync::Arc, | ||
| }; | ||
|
|
||
| mod config; | ||
|
|
||
| pub use self::config::Config; | ||
| use mun_errors::{Diagnostic, Level}; | ||
| use termcolor::WriteColor; | ||
|
|
||
| pub const WORKSPACE: SourceRootId = SourceRootId(0); | ||
|
|
||
| #[derive(Debug)] | ||
| pub struct Driver { | ||
| db: CompilerDatabase, | ||
| out_dir: Option<PathBuf>, | ||
| } | ||
|
|
||
| impl Driver { | ||
| /// Constructs a driver with a specific configuration. | ||
| pub fn with_config(config: Config) -> Self { | ||
| let mut driver = Driver { | ||
| db: CompilerDatabase::new(), | ||
| out_dir: None, | ||
| }; | ||
|
|
||
| // Move relevant configuration into the database | ||
| driver.db.set_target(config.target); | ||
| driver | ||
| .db | ||
| .set_context(Arc::new(mun_codegen::Context::create())); | ||
| driver.db.set_optimization_lvl(config.optimization_lvl); | ||
|
|
||
| driver.out_dir = config.out_dir; | ||
|
|
||
| driver | ||
| } | ||
|
|
||
| /// Constructs a driver with a configuration and a single file. | ||
| pub fn with_file( | ||
| config: Config, | ||
| path: PathOrInline, | ||
| ) -> Result<(Driver, FileId), failure::Error> { | ||
| let mut driver = Driver::with_config(config); | ||
|
|
||
| // Construct a SourceRoot | ||
| let mut source_root = SourceRoot::default(); | ||
|
|
||
| // Get the path and contents of the path | ||
| let (rel_path, text) = match path { | ||
| PathOrInline::Path(p) => { | ||
| let filename = p.file_name().ok_or_else(|| { | ||
| std::io::Error::new( | ||
| std::io::ErrorKind::InvalidInput, | ||
| "Input path is missing a filename.", | ||
| ) | ||
| })?; | ||
| ( | ||
| RelativePathBuf::from_path(filename).unwrap(), | ||
| std::fs::read_to_string(p)?, | ||
| ) | ||
| } | ||
| PathOrInline::Inline { rel_path, contents } => (rel_path, contents), | ||
| }; | ||
|
|
||
| // Store the file information in the database together with the source root | ||
| let file_id = FileId(0); | ||
| driver.db.set_file_relative_path(file_id, rel_path.clone()); | ||
| driver.db.set_file_text(file_id, Arc::new(text)); | ||
| driver.db.set_file_source_root(file_id, WORKSPACE); | ||
| source_root.insert_file(rel_path, file_id); | ||
| driver.db.set_source_root(WORKSPACE, Arc::new(source_root)); | ||
|
|
||
| Ok((driver, file_id)) | ||
| } | ||
| } | ||
|
|
||
| impl Driver { | ||
| /// Sets the contents of a specific file. | ||
| pub fn set_file_text<T: AsRef<str>>(&mut self, file_id: FileId, text: T) { | ||
| self.db | ||
| .set_file_text(file_id, Arc::new(text.as_ref().to_owned())); | ||
| } | ||
| } | ||
|
|
||
| impl Driver { | ||
| /// Returns a vector containing all the diagnostic messages for the project. | ||
| pub fn diagnostics(&self) -> Vec<Diagnostic> { | ||
| self.db | ||
| .source_root(WORKSPACE) | ||
| .files() | ||
| .map(|f| diagnostics(&self.db, f)) | ||
| .flatten() | ||
| .collect() | ||
| } | ||
|
|
||
| /// Emits all diagnostic messages currently in the database; returns true if errors were | ||
| /// emitted. | ||
| pub fn emit_diagnostics(&self, writer: &mut impl WriteColor) -> Result<bool, failure::Error> { | ||
| let mut has_errors = false; | ||
| for file_id in self.db.source_root(WORKSPACE).files() { | ||
| let diags = diagnostics(&self.db, file_id); | ||
| for diagnostic in diags.iter() { | ||
| diagnostic.emit(writer, &self.db, file_id)?; | ||
| if diagnostic.level == Level::Error { | ||
| has_errors = true; | ||
| } | ||
| } | ||
| } | ||
| Ok(has_errors) | ||
| } | ||
| } | ||
|
|
||
| impl Driver { | ||
| /// Computes the output path for the assembly of the specified file. | ||
| fn assembly_output_path(&self, file_id: FileId) -> PathBuf { | ||
| let target: Target = self.db.target(); | ||
| let relative_path: RelativePathBuf = self.db.file_relative_path(file_id); | ||
| let original_filename = Path::new(relative_path.file_name().unwrap()); | ||
|
|
||
| // Get the dll suffix without the starting dot | ||
| let dll_extension = if target.options.dll_suffix.starts_with('.') { | ||
| &target.options.dll_suffix[1..] | ||
| } else { | ||
| &target.options.dll_suffix | ||
| }; | ||
|
|
||
| // Add the dll suffix to the original filename | ||
| let output_file_name = original_filename.with_extension(dll_extension); | ||
|
|
||
| // If there is an out dir specified, prepend the output directory | ||
| if let Some(ref out_dir) = self.out_dir { | ||
| out_dir.join(output_file_name) | ||
| } else { | ||
| output_file_name | ||
| } | ||
| } | ||
|
|
||
| /// Generate an assembly for the given file | ||
| pub fn write_assembly(&self, file_id: FileId) -> Result<Option<PathBuf>, failure::Error> { | ||
| let output_path = self.assembly_output_path(file_id); | ||
| mun_codegen::write_module_shared_object(&self.db, file_id, &output_path)?; | ||
| Ok(Some(output_path)) | ||
| } | ||
| } | ||
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,31 @@ | ||
| use crate::host_triple; | ||
| use mun_codegen::OptimizationLevel; | ||
| use mun_target::spec::Target; | ||
| use std::path::PathBuf; | ||
|
|
||
| /// Describes all the permanent settings that are used during compilations. | ||
| #[derive(Debug, Clone)] | ||
| pub struct Config { | ||
| /// The target triple to compile the code for. | ||
| pub target: Target, | ||
|
|
||
| /// The optimization level to use for the IR generation. | ||
| pub optimization_lvl: OptimizationLevel, | ||
|
|
||
| /// The optional output directory to store all outputs. If no directory is specified all output | ||
| /// is stored in a temporary directory. | ||
| pub out_dir: Option<PathBuf>, | ||
| } | ||
|
|
||
| impl Default for Config { | ||
| fn default() -> Self { | ||
| let target = Target::search(&host_triple()); | ||
| Config { | ||
| // This unwrap is safe because we only compile for targets that have an implemented host | ||
| // triple. | ||
| target: target.unwrap(), | ||
| optimization_lvl: OptimizationLevel::Default, | ||
| out_dir: None, | ||
| } | ||
| } | ||
| } |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.