diff --git a/CHANGELOG.md b/CHANGELOG.md index f53758e3f..dd3ede7c6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -414,6 +414,11 @@ ### Bug fixes +- Fixed a bug where the generated Erlang `.app` file's `modules` list would only + contain the modules recompiled by the latest build, becoming empty on a warm + rebuild where nothing changed. + ([Charlie Tonneslan](https://github.com/c-tonneslan)) + - When using the language server to extract a function from within an anonymous function, the return value of the extracted function is respected. diff --git a/compiler-core/src/build/package_compiler.rs b/compiler-core/src/build/package_compiler.rs index 68c11a34e..9a1d43e14 100644 --- a/compiler-core/src/build/package_compiler.rs +++ b/compiler-core/src/build/package_compiler.rs @@ -239,7 +239,7 @@ where // modules // }; - if let Err(error) = self.perform_codegen(&modules) { + if let Err(error) = self.perform_codegen(&modules, &cached_module_names) { return error.into(); } @@ -352,7 +352,11 @@ where Ok(()) } - fn perform_codegen(&mut self, modules: &[Module]) -> Result<()> { + fn perform_codegen( + &mut self, + modules: &[Module], + cached_module_names: &[EcoString], + ) -> Result<()> { if !self.perform_codegen { tracing::debug!("skipping_codegen"); return Ok(()); @@ -370,7 +374,7 @@ where prelude_location, ), TargetCodegenConfiguration::Erlang { app_file } => { - self.perform_erlang_codegen(modules, app_file.as_ref()) + self.perform_erlang_codegen(modules, cached_module_names, app_file.as_ref()) } } } @@ -378,6 +382,7 @@ where fn perform_erlang_codegen( &mut self, modules: &[Module], + cached_module_names: &[EcoString], app_file_config: Option<&ErlangAppCodegenConfiguration>, ) -> Result<(), Error> { let mut written = HashSet::new(); @@ -418,6 +423,7 @@ where io, &self.config, modules, + cached_module_names, native_modules, )?; } diff --git a/compiler-core/src/codegen.rs b/compiler-core/src/codegen.rs index 2601e11b0..26ec89fd3 100644 --- a/compiler-core/src/codegen.rs +++ b/compiler-core/src/codegen.rs @@ -99,6 +99,7 @@ impl<'a> ErlangApp<'a> { writer: Writer, config: &PackageConfig, modules: &[Module], + cached_module_names: &[EcoString], native_modules: Vec, ) -> Result<()> { fn tuple(key: &str, value: &str) -> String { @@ -119,9 +120,12 @@ impl<'a> ErlangApp<'a> { } }; + // Include the cached modules that were not recompiled this build, so a + // warm rebuild doesn't shrink the `modules` list (see #5834). let modules = modules .iter() .map(|m| m.erlang_name()) + .chain(cached_module_names.iter().map(module_erlang_name)) .chain(native_modules) .unique() .sorted() @@ -298,3 +302,40 @@ impl<'a> JavaScript<'a> { Ok(()) } } + +#[cfg(test)] +mod tests { + use super::*; + use crate::io::{FileSystemReader, memory::InMemoryFileSystem}; + use std::collections::HashMap; + + #[test] + fn app_file_includes_cached_modules() { + // A warm rebuild recompiles nothing, so every module arrives as a cached + // name rather than in `modules`; they must still be listed in the .app + // (https://github.com/gleam-lang/gleam/issues/5834). + let fs = InMemoryFileSystem::new(); + let mut config = PackageConfig::default(); + config.name = "my_app".into(); + let codegen_config = ErlangAppCodegenConfiguration { + include_dev_deps: false, + package_name_overrides: HashMap::new(), + }; + + ErlangApp::new(Utf8Path::new("/ebin"), &codegen_config) + .render( + fs.clone(), + &config, + &[], + &["my_app/one".into(), "my_app/two".into()], + vec![], + ) + .expect("render .app"); + + let app = fs + .read(Utf8Path::new("/ebin/my_app.app")) + .expect("read .app"); + assert!(app.contains("my_app@one"), "{app}"); + assert!(app.contains("my_app@two"), "{app}"); + } +}