Fork of daniellemaywood.uk/gleam — Wasm codegen work
2

Configure Feed

Select the types of activity you want to include in your feed.

Do not write compile escript to build directory

Closes https://github.com/gleam-lang/gleam/issues/4474

+117 -78
+4
CHANGELOG.md
··· 188 188 segments when compiling a function with a JavaScript external. 189 189 ([Giacomo Cavalieri](https://github.com/giacomocavalieri)) 190 190 191 + - A `gleam@@compile.erl` is no longer left in the build output of 192 + `gleam compile-package`. 193 + ([Louis Pilfold](https://github.com/lpil)) 194 + 191 195 - When using the language server to extract a function from within the body of a 192 196 use statement, only the selected statement(s) are extracted. 193 197
+2 -2
compiler-cli/Cargo.toml
··· 36 36 same-file = "1" 37 37 # Creation of zip files 38 38 zip = { version = "8", features = ["deflate"], default-features = false } 39 + # Creation of temporary directories 40 + tempfile = "3" 39 41 40 42 async-trait.workspace = true 41 43 base16.workspace = true ··· 65 67 tracing.workspace = true 66 68 67 69 [dev-dependencies] 68 - # Creation of temporary directories 69 - tempfile = "3" 70 70 # Setting file modification times for tests 71 71 filetime = "0.2" 72 72 pretty_assertions.workspace = true
+36 -39
compiler-cli/src/beam_compiler.rs
··· 20 20 use itertools::Itertools; 21 21 22 22 #[derive(Debug)] 23 - struct BeamCompilerInner { 23 + pub struct BeamCompilerInstance { 24 24 process: Child, 25 - stdin: ChildStdin, 25 + stdin: Option<ChildStdin>, 26 26 stdout: BufReader<ChildStdout>, 27 + // A guard held for cleaning up the temporary file used to start the BEAM instance. 28 + _source: tempfile::NamedTempFile, 27 29 } 28 30 29 - #[derive(Debug, Default)] 30 - pub struct BeamCompiler { 31 - inner: Option<BeamCompilerInner>, 32 - } 33 - 34 - impl BeamCompiler { 35 - pub fn compile<IO: FileSystemWriter>( 31 + impl BeamCompilerInstance { 32 + pub fn compile( 36 33 &mut self, 37 - io: &IO, 38 34 out: &Utf8Path, 39 35 lib: &Utf8Path, 40 36 modules: &HashSet<Utf8PathBuf>, 41 37 stdio: Stdio, 42 38 ) -> Result<Vec<String>, Error> { 43 - let inner = match self.inner { 44 - Some(ref mut inner) => match inner.process.try_wait() { 45 - Ok(None) => inner, 46 - _ => self.inner.insert(self.spawn(io, out)?), 47 - }, 39 + // Check that the BEAM instance is still alive before attempting to use it. 40 + let exit_status = self 41 + .process 42 + .try_wait() 43 + .expect("access BEAM instance exit state"); 44 + if let Some(status) = exit_status { 45 + panic!("BEAM compiler instance exited: {status}"); 46 + } 48 47 49 - None => self.inner.insert(self.spawn(io, out)?), 50 - }; 51 - 48 + // Prepare work to send to the BEAM instance. 52 49 let args = format!( 53 50 "{{\"{}\", \"{}\", [\"{}\"]}}", 54 51 escape_path(lib), ··· 61 58 62 59 tracing::debug!(args=?args, "call_beam_compiler"); 63 60 64 - writeln!(inner.stdin, "{args}.").map_err(|e| Error::ShellCommand { 65 - program: "escript".into(), 66 - reason: ShellCommandFailureReason::IoError(e.kind()), 61 + writeln!(self.stdin.as_ref().expect("stdin present"), "{args}.").map_err(|e| { 62 + Error::ShellCommand { 63 + program: "escript".into(), 64 + reason: ShellCommandFailureReason::IoError(e.kind()), 65 + } 67 66 })?; 68 67 69 68 let mut buf = String::new(); 70 69 let mut accumulated_modules: Vec<String> = Vec::new(); 71 - while let (Ok(_), Ok(None)) = (inner.stdout.read_line(&mut buf), inner.process.try_wait()) { 70 + while let (Ok(_), Ok(None)) = (self.stdout.read_line(&mut buf), self.process.try_wait()) { 72 71 match buf.trim() { 73 72 "gleam-compile-result-ok" => { 74 73 // Return Ok with the accumulated modules ··· 101 100 }) 102 101 } 103 102 104 - fn spawn<IO: FileSystemWriter>( 105 - &self, 106 - io: &IO, 107 - out: &Utf8Path, 108 - ) -> Result<BeamCompilerInner, Error> { 109 - let escript_path = out 110 - .join(paths::ARTEFACT_DIRECTORY_NAME) 111 - .join("gleam@@compile.erl"); 103 + pub fn new<IO: FileSystemWriter>(io: &IO) -> Result<Self, Error> { 104 + let escript_source = std::include_str!("../templates/gleam@@compile.erl"); 105 + let escript_file = 106 + tempfile::NamedTempFile::new().map_err(|e| Error::CouldNotCreateTempFile { 107 + error: e.to_string(), 108 + })?; 109 + let escript_path = 110 + Utf8PathBuf::from_path_buf(escript_file.path().to_path_buf()).expect("UTF8 temp"); 112 111 113 - let escript_source = std::include_str!("../templates/gleam@@compile.erl"); 114 112 io.write(&escript_path, escript_source)?; 115 113 116 114 tracing::trace!(escript_path=?escript_path, "spawn_beam_compiler"); ··· 134 132 let stdin = process.stdin.take().expect("could not get child stdin"); 135 133 let stdout = process.stdout.take().expect("could not get child stdout"); 136 134 137 - Ok(BeamCompilerInner { 135 + Ok(Self { 138 136 process, 139 - stdin, 137 + stdin: Some(stdin), 140 138 stdout: BufReader::new(stdout), 139 + _source: escript_file, 141 140 }) 142 141 } 143 142 } 144 143 145 - impl Drop for BeamCompiler { 144 + impl Drop for BeamCompilerInstance { 146 145 fn drop(&mut self) { 147 - if let Some(mut inner) = self.inner.take() { 148 - // closing stdin will cause the erlang process to exit. 149 - drop(inner.stdin); 150 - let _ = inner.process.wait(); 151 - } 146 + // closing stdin will cause the BEAM instance to exit. 147 + drop(self.stdin.take()); 148 + let _ = self.process.wait(); 152 149 } 153 150 } 154 151
+7 -5
compiler-cli/src/build.rs
··· 47 47 } else { 48 48 &cli::Reporter 49 49 }; 50 + let target = options.target.unwrap_or(root_config.target); 50 51 let io = fs::ProjectIO::new(); 52 + // Initialise the BEAM compiler instance eagerly, so we don't have to wait 53 + // for it to boot when we come to use it for the first time. 54 + if target.is_erlang() { 55 + io.initialise_beam_compiler()?; 56 + } 51 57 let start = Instant::now(); 52 - let lock = BuildLock::new_target( 53 - paths, 54 - options.mode, 55 - options.target.unwrap_or(root_config.target), 56 - )?; 58 + let lock = BuildLock::new_target(paths, options.mode, target)?; 57 59 58 60 tracing::info!("Compiling packages"); 59 61 let result = {
+8 -1
compiler-cli/src/compile_package.rs
··· 29 29 let paths = ProjectPaths::new(options.package_directory.clone()); 30 30 let config = config::read(paths.root_config())?; 31 31 32 + let io = ProjectIO::new(); 33 + // Initialise the BEAM compiler instance eagerly, so we don't have to wait 34 + // for it to boot when we come to use it for the first time. 35 + if options.target.is_erlang() && !options.skip_beam_compilation { 36 + io.initialise_beam_compiler()?; 37 + } 38 + 32 39 let target = match options.target { 33 40 Target::Erlang => TargetCodegenConfiguration::Erlang { app_file: None }, 34 41 Target::JavaScript => TargetCodegenConfiguration::JavaScript { ··· 50 57 &options.libraries_directory, 51 58 &target, 52 59 ids, 53 - ProjectIO::new(), 60 + io, 54 61 ); 55 62 compiler.write_entrypoint = false; 56 63 compiler.write_metadata = true;
+26 -8
compiler-cli/src/fs.rs
··· 6 6 build::{NullTelemetry, Target}, 7 7 error::{Error, FileIoAction, FileKind, OS, ShellCommandFailureReason, parse_os}, 8 8 io::{ 9 - BeamCompiler, Command, CommandExecutor, Content, DirEntry, FileSystemReader, 9 + BeamCompilerIO, Command, CommandExecutor, Content, DirEntry, FileSystemReader, 10 10 FileSystemWriter, OutputFile, ReadDir, Stdio, WrappedReader, is_native_file_extension, 11 11 }, 12 12 manifest::Manifest, ··· 25 25 26 26 use camino::{ReadDirUtf8, Utf8Path, Utf8PathBuf}; 27 27 28 - use crate::{dependencies, lsp::LspLocker}; 28 + use crate::{beam_compiler::BeamCompilerInstance, dependencies, lsp::LspLocker}; 29 29 30 30 #[cfg(test)] 31 31 mod tests; ··· 89 89 /// A `FileWriter` implementation that writes to the file system. 90 90 #[derive(Debug, Clone, Default)] 91 91 pub struct ProjectIO { 92 - beam_compiler: Arc<Mutex<crate::beam_compiler::BeamCompiler>>, 92 + beam_compiler: Arc<Mutex<Option<BeamCompilerInstance>>>, 93 93 } 94 94 95 95 impl ProjectIO { ··· 101 101 102 102 pub fn boxed() -> Box<Self> { 103 103 Box::new(Self::new()) 104 + } 105 + 106 + pub(crate) fn initialise_beam_compiler(&self) -> Result<(), Error> { 107 + let mut guard = self 108 + .beam_compiler 109 + .lock() 110 + .expect("could not lock beam_compiler"); 111 + *guard = Some(BeamCompilerInstance::new(self)?); 112 + Ok(()) 104 113 } 105 114 } 106 115 ··· 244 253 } 245 254 } 246 255 247 - impl BeamCompiler for ProjectIO { 256 + impl BeamCompilerIO for ProjectIO { 248 257 fn compile_beam( 249 258 &self, 250 259 out: &Utf8Path, ··· 252 261 modules: &HashSet<Utf8PathBuf>, 253 262 stdio: Stdio, 254 263 ) -> Result<Vec<String>, Error> { 255 - self.beam_compiler 264 + let mut guard = self 265 + .beam_compiler 256 266 .lock() 257 - .as_mut() 258 - .expect("could not get beam_compiler") 259 - .compile(self, out, lib, modules, stdio) 267 + .expect("could not lock beam_compiler"); 268 + 269 + match &mut *guard { 270 + Some(compiler) => compiler.compile(out, lib, modules, stdio), 271 + None => { 272 + let mut compiler = BeamCompilerInstance::new(self)?; 273 + let result = compiler.compile(out, lib, modules, stdio); 274 + *guard = Some(compiler); 275 + result 276 + } 277 + } 260 278 } 261 279 } 262 280
+2 -2
compiler-core/src/build/package_compiler.rs
··· 23 23 codegen::{Erlang, ErlangApp, JavaScript, TypeScriptDeclarations}, 24 24 config::PackageConfig, 25 25 dep_tree, error, 26 - io::{BeamCompiler, CommandExecutor, FileSystemReader, FileSystemWriter, Stdio}, 26 + io::{BeamCompilerIO, CommandExecutor, FileSystemReader, FileSystemWriter, Stdio}, 27 27 parse::extra::ModuleExtra, 28 28 paths, type_, 29 29 uid::UniqueIdGenerator, ··· 79 79 80 80 impl<'a, IO> PackageCompiler<'a, IO> 81 81 where 82 - IO: FileSystemReader + FileSystemWriter + CommandExecutor + BeamCompiler + Clone, 82 + IO: FileSystemReader + FileSystemWriter + CommandExecutor + BeamCompilerIO + Clone, 83 83 { 84 84 pub fn new( 85 85 config: &'a PackageConfig,
+2 -2
compiler-core/src/build/project_compiler.rs
··· 15 15 config::PackageConfig, 16 16 dep_tree, 17 17 error::{DefinedModuleOrigin, FileIoAction, FileKind, ShellCommandFailureReason}, 18 - io::{BeamCompiler, Command, CommandExecutor, FileSystemReader, FileSystemWriter, Stdio}, 18 + io::{BeamCompilerIO, Command, CommandExecutor, FileSystemReader, FileSystemWriter, Stdio}, 19 19 manifest::{ManifestPackage, ManifestPackageSource}, 20 20 metadata, 21 21 paths::{self, ProjectPaths}, ··· 128 128 129 129 impl<IO> ProjectCompiler<IO> 130 130 where 131 - IO: CommandExecutor + FileSystemWriter + FileSystemReader + BeamCompiler + Clone, 131 + IO: CommandExecutor + FileSystemWriter + FileSystemReader + BeamCompilerIO + Clone, 132 132 { 133 133 pub fn new( 134 134 config: PackageConfig,
+11
compiler-core/src/error.rs
··· 425 425 426 426 #[error("{path} could not be added to the tarball as it is outside the project root")] 427 427 TarPathOutsideOfProjectRoot { path: Utf8PathBuf }, 428 + 429 + #[error("could not create temp file: {error}")] 430 + CouldNotCreateTempFile { error: String }, 428 431 } 429 432 430 433 #[derive(Debug, Eq, PartialEq, Clone, Copy)] ··· 1778 1781 location: None, 1779 1782 }] 1780 1783 } 1784 + 1785 + Error::CouldNotCreateTempFile { error } => vec![Diagnostic { 1786 + title: "File IO failure".into(), 1787 + text: wrap_format!("Could not create temporary file:\n\n\t{error}"), 1788 + level: Level::Error, 1789 + location: None, 1790 + hint: None, 1791 + }], 1781 1792 1782 1793 Error::FileIo { 1783 1794 kind,
+1 -1
compiler-core/src/io.rs
··· 322 322 } 323 323 324 324 /// A trait used to compile Erlang and Elixir modules to BEAM bytecode. 325 - pub trait BeamCompiler { 325 + pub trait BeamCompilerIO { 326 326 fn compile_beam( 327 327 &self, 328 328 out: &Utf8Path,
+1 -1
compiler-core/src/io/memory.rs
··· 452 452 } 453 453 } 454 454 455 - impl BeamCompiler for InMemoryFileSystem { 455 + impl BeamCompilerIO for InMemoryFileSystem { 456 456 fn compile_beam( 457 457 &self, 458 458 _out: &Utf8Path,
+3 -3
compiler-wasm/src/wasm_filesystem.rs
··· 5 5 use gleam_core::{ 6 6 Error, Result, 7 7 io::{ 8 - BeamCompiler, Command, CommandExecutor, FileSystemReader, FileSystemWriter, ReadDir, Stdio, 9 - WrappedReader, memory::InMemoryFileSystem, 8 + BeamCompilerIO, Command, CommandExecutor, FileSystemReader, FileSystemWriter, ReadDir, 9 + Stdio, WrappedReader, memory::InMemoryFileSystem, 10 10 }, 11 11 }; 12 12 use std::collections::HashSet; ··· 28 28 } 29 29 } 30 30 31 - impl BeamCompiler for WasmFileSystem { 31 + impl BeamCompilerIO for WasmFileSystem { 32 32 fn compile_beam( 33 33 &self, 34 34 _out: &Utf8Path,
+2 -2
language-server/src/compiler.rs
··· 10 10 analyse::TargetSupport, 11 11 build::{self, Mode, Module, NullTelemetry, Outcome, ProjectCompiler}, 12 12 config::PackageConfig, 13 - io::{BeamCompiler, CommandExecutor, FileSystemReader, FileSystemWriter, Stdio}, 13 + io::{BeamCompilerIO, CommandExecutor, FileSystemReader, FileSystemWriter, Stdio}, 14 14 line_numbers::LineNumbers, 15 15 manifest::Manifest, 16 16 paths::ProjectPaths, ··· 45 45 46 46 impl<IO> LspProjectCompiler<IO> 47 47 where 48 - IO: CommandExecutor + FileSystemWriter + FileSystemReader + BeamCompiler + Clone, 48 + IO: CommandExecutor + FileSystemWriter + FileSystemReader + BeamCompilerIO + Clone, 49 49 { 50 50 pub fn new( 51 51 manifest: Manifest,
+2 -2
language-server/src/engine.rs
··· 15 15 ExpressionPosition, Located, Module, UnqualifiedImport, type_constructor_from_modules, 16 16 }, 17 17 config::PackageConfig, 18 - io::{BeamCompiler, CommandExecutor, FileSystemReader, FileSystemWriter}, 18 + io::{BeamCompilerIO, CommandExecutor, FileSystemReader, FileSystemWriter}, 19 19 line_numbers::LineNumbers, 20 20 paths::ProjectPaths, 21 21 type_::{ ··· 115 115 // IO to be supplied from outside of gleam-core 116 116 IO: FileSystemReader 117 117 + FileSystemWriter 118 - + BeamCompiler 118 + + BeamCompilerIO 119 119 + CommandExecutor 120 120 + DownloadDependencies 121 121 + MakeLocker
+4 -4
language-server/src/files.rs
··· 10 10 Result, 11 11 error::Error, 12 12 io::{ 13 - BeamCompiler, Command, CommandExecutor, FileSystemReader, FileSystemWriter, ReadDir, Stdio, 14 - WrappedReader, memory::InMemoryFileSystem, 13 + BeamCompilerIO, Command, CommandExecutor, FileSystemReader, FileSystemWriter, ReadDir, 14 + Stdio, WrappedReader, memory::InMemoryFileSystem, 15 15 }, 16 16 }; 17 17 ··· 169 169 } 170 170 } 171 171 172 - impl<IO> BeamCompiler for FileSystemProxy<IO> 172 + impl<IO> BeamCompilerIO for FileSystemProxy<IO> 173 173 where 174 - IO: BeamCompiler, 174 + IO: BeamCompilerIO, 175 175 { 176 176 fn compile_beam( 177 177 &self,
+2 -2
language-server/src/router.rs
··· 5 5 Error, Result, 6 6 build::SourceFingerprint, 7 7 error::{FileIoAction, FileKind}, 8 - io::{BeamCompiler, CommandExecutor, FileSystemReader, FileSystemWriter}, 8 + io::{BeamCompilerIO, CommandExecutor, FileSystemReader, FileSystemWriter}, 9 9 paths::ProjectPaths, 10 10 }; 11 11 use std::{ ··· 39 39 // IO to be supplied from outside of gleam-core 40 40 IO: FileSystemReader 41 41 + FileSystemWriter 42 - + BeamCompiler 42 + + BeamCompilerIO 43 43 + CommandExecutor 44 44 + DownloadDependencies 45 45 + MakeLocker
+2 -2
language-server/src/server.rs
··· 16 16 use gleam_core::{ 17 17 Result, 18 18 diagnostic::{Diagnostic, ExtraLabel, Level}, 19 - io::{BeamCompiler, CommandExecutor, FileSystemReader, FileSystemWriter}, 19 + io::{BeamCompilerIO, CommandExecutor, FileSystemReader, FileSystemWriter}, 20 20 line_numbers::LineNumbers, 21 21 }; 22 22 use itertools::Itertools; ··· 54 54 where 55 55 IO: FileSystemReader 56 56 + FileSystemWriter 57 - + BeamCompiler 57 + + BeamCompilerIO 58 58 + CommandExecutor 59 59 + DownloadDependencies 60 60 + MakeLocker
+2 -2
language-server/src/tests.rs
··· 32 32 build::Origin, 33 33 config::PackageConfig, 34 34 io::{ 35 - BeamCompiler, Command, CommandExecutor, FileSystemReader, FileSystemWriter, ReadDir, 35 + BeamCompilerIO, Command, CommandExecutor, FileSystemReader, FileSystemWriter, ReadDir, 36 36 WrappedReader, memory::InMemoryFileSystem, 37 37 }, 38 38 line_numbers::LineNumbers, ··· 256 256 } 257 257 } 258 258 259 - impl BeamCompiler for LanguageServerTestIO { 259 + impl BeamCompilerIO for LanguageServerTestIO { 260 260 fn compile_beam( 261 261 &self, 262 262 out: &Utf8Path,