From eb6a7b51ffc5894341b5748c8d29fc0bf98581f1 Mon Sep 17 00:00:00 2001 From: Richard Feldman Date: Wed, 18 Nov 2020 00:28:47 -0500 Subject: [PATCH] Use app path in output --- cli/src/build.rs | 6 +-- compiler/load/src/file.rs | 89 ++++++++++++++++++++++++++++-------- compiler/module/src/ident.rs | 1 + 3 files changed, 74 insertions(+), 22 deletions(-) diff --git a/cli/src/build.rs b/cli/src/build.rs index 2cbb70d078..c4e2dc02ed 100644 --- a/cli/src/build.rs +++ b/cli/src/build.rs @@ -85,6 +85,9 @@ pub fn build_file( buf ); + let cwd = app_o_file.parent().unwrap(); + let binary_path = cwd.join(&*loaded.output_path); // TODO should join ".exe" on Windows + program::gen_from_mono_module( &arena, loaded, @@ -106,11 +109,8 @@ pub fn build_file( size, ); - let cwd = app_o_file.parent().unwrap(); - // Step 2: link the precompiled host and compiled app let host_input_path = cwd.join("platform").join("host.o"); - let binary_path = cwd.join("app"); // TODO should be app.exe on Windows // TODO we should no longer need to do this once we have platforms on // a package repository, as we can then get precompiled hosts from there. diff --git a/compiler/load/src/file.rs b/compiler/load/src/file.rs index 1f505ad0ab..f3cba328a6 100644 --- a/compiler/load/src/file.rs +++ b/compiler/load/src/file.rs @@ -19,7 +19,7 @@ use roc_mono::ir::{ CapturedSymbols, ExternalSpecializations, PartialProc, PendingSpecialization, Proc, Procs, }; use roc_mono::layout::{Layout, LayoutCache}; -use roc_parse::ast::{self, Attempting, TypeAnnotation}; +use roc_parse::ast::{self, Attempting, StrLiteral, TypeAnnotation}; use roc_parse::header::{ExposesEntry, ImportsEntry, PlatformHeader, TypedIdent}; use roc_parse::module::module_defs; use roc_parse::parser::{self, Fail, Parser}; @@ -39,6 +39,9 @@ use std::str::from_utf8_unchecked; use std::sync::Arc; use std::time::{Duration, SystemTime}; +/// Default name for the binary generated for an app, if an invalid one was specified. +const DEFAULT_APP_OUTPUT_PATH: &str = "app"; + /// Filename extension for normal Roc modules const ROC_FILE_EXTENSION: &str = "roc"; @@ -533,7 +536,7 @@ pub enum BuildProblem<'a> { #[derive(Debug)] struct ModuleHeader<'a> { module_id: ModuleId, - module_name: ModuleName, + module_name: AppOrInterfaceName<'a>, module_path: PathBuf, exposed_ident_ids: IdentIds, deps_by_name: MutMap, @@ -580,6 +583,7 @@ pub struct MonomorphizedModule<'a> { pub module_id: ModuleId, pub interns: Interns, pub subs: Subs, + pub output_path: Box, pub can_problems: MutMap>, pub type_problems: MutMap>, pub mono_problems: MutMap>, @@ -598,7 +602,7 @@ pub struct VariablySizedLayouts<'a> { #[derive(Debug)] struct ParsedModule<'a> { module_id: ModuleId, - module_name: ModuleName, + module_name: AppOrInterfaceName<'a>, module_path: PathBuf, src: &'a str, module_timing: ModuleTiming, @@ -617,7 +621,7 @@ enum Msg<'a> { CanonicalizedAndConstrained { constrained_module: ConstrainedModule, canonicalization_problems: Vec, - module_docs: ModuleDocumentation, + module_docs: Option, }, MadeEffectModule { constrained_module: ConstrainedModule, @@ -671,6 +675,7 @@ struct State<'a> { pub goal_phase: Phase, pub stdlib: StdLib, pub exposed_types: SubsByModule, + pub output_path: Option<&'a str>, pub headers_parsed: MutSet, @@ -1242,6 +1247,7 @@ where root_id, goal_phase, stdlib, + output_path: None, module_cache: ModuleCache::default(), dependencies: Dependencies::default(), procedures: MutMap::default(), @@ -1426,6 +1432,22 @@ fn update<'a>( .sources .insert(parsed.module_id, (parsed.module_path.clone(), parsed.src)); + // If this was an app module, set the output path to be + // the module's declared "name". + // + // e.g. for `app "blah"` we should generate an output file named "blah" + match &parsed.module_name { + AppOrInterfaceName::App(output_str) => match output_str { + StrLiteral::PlainLine(path) => { + state.output_path = Some(path); + } + _ => { + todo!("TODO gracefully handle a malformed string literal after `app` keyword."); + } + }, + AppOrInterfaceName::Interface(_) => {} + } + let module_id = parsed.module_id; state.module_cache.parsed.insert(parsed.module_id, parsed); @@ -1449,10 +1471,9 @@ fn update<'a>( .can_problems .insert(module_id, canonicalization_problems); - state - .module_cache - .documentation - .insert(module_id, module_docs); + if let Some(docs) = module_docs { + state.module_cache.documentation.insert(module_id, docs); + } state .module_cache @@ -1750,6 +1771,7 @@ fn finish_specialization<'a>( let State { procedures, module_cache, + output_path, .. } = state; @@ -1770,6 +1792,7 @@ fn finish_specialization<'a>( can_problems, mono_problems, type_problems, + output_path: output_path.unwrap_or(DEFAULT_APP_OUTPUT_PATH).into(), exposed_to_host, module_id: state.root_id, subs, @@ -1966,7 +1989,10 @@ fn parse_header<'a>( match parsed { Ok((ast::Module::Interface { header }, parse_state)) => Ok(send_header( - header.name, + Located { + region: header.name.region, + value: AppOrInterfaceName::Interface(header.name.value), + }, filename, header.exposes.into_bump_slice(), header.imports.into_bump_slice(), @@ -1980,7 +2006,10 @@ fn parse_header<'a>( pkg_config_dir.pop(); let (module_id, app_module_header_msg) = send_header( - header.name, + Located { + region: header.name.region, + value: AppOrInterfaceName::App(header.name.value), + }, filename, header.provides.into_bump_slice(), header.imports.into_bump_slice(), @@ -2082,9 +2111,16 @@ fn load_from_str<'a>( ) } +#[derive(Debug)] +enum AppOrInterfaceName<'a> { + /// A filename + App(StrLiteral<'a>), + Interface(roc_parse::header::ModuleName<'a>), +} + #[allow(clippy::too_many_arguments)] fn send_header<'a>( - name: Located>, + loc_name: Located>, filename: PathBuf, exposes: &'a [Located>], imports: &'a [Located>], @@ -2093,10 +2129,17 @@ fn send_header<'a>( ident_ids_by_module: Arc>>, module_timing: ModuleTiming, ) -> (ModuleId, Msg<'a>) { - let declared_name: ModuleName = name.value.as_str().into(); + use AppOrInterfaceName::*; - // TODO check to see if declared_name is consistent with filename. - // If it isn't, report a problem! + let declared_name: ModuleName = match &loc_name.value { + App(_) => ModuleName::APP.into(), + Interface(module_name) => { + // TODO check to see if module_name is consistent with filename. + // If it isn't, report a problem! + + module_name.as_str().into() + } + }; let mut imported: Vec<(ModuleName, Vec, Region)> = Vec::with_capacity(imports.len()); let mut imported_modules: MutSet = MutSet::default(); @@ -2199,15 +2242,13 @@ fn send_header<'a>( // We always need to send these, even if deps is empty, // because the coordinator thread needs to receive this message // to decrement its "pending" count. - - // Send the header the main thread for processing, ( home, Msg::Header(ModuleHeader { module_id: home, module_path: filename, exposed_ident_ids: ident_ids, - module_name: declared_name, + module_name: loc_name.value, imported_modules, deps_by_name, exposes: exposed, @@ -2618,8 +2659,14 @@ fn canonicalize_and_constrain<'a>( // Generate documentation information // TODO: store timing information? - let module_docs = - crate::docs::generate_module_docs(module_name, &exposed_ident_ids, &parsed_defs); + let module_docs = match module_name { + AppOrInterfaceName::App(_) => None, + AppOrInterfaceName::Interface(name) => Some(crate::docs::generate_module_docs( + name.as_str().into(), + &exposed_ident_ids, + &parsed_defs, + )), + }; let mut var_store = VarStore::default(); let canonicalized = canonicalize_module_defs( @@ -2749,6 +2796,10 @@ fn exposed_from_import(entry: &ImportsEntry<'_>) -> (ModuleName, Vec) { (module_name.as_str().into(), exposed) } + Package(_package_name, _exposes) => { + todo!("TODO support exposing package-qualified module names."); + } + SpaceBefore(sub_entry, _) | SpaceAfter(sub_entry, _) => { // Ignore spaces. exposed_from_import(*sub_entry) diff --git a/compiler/module/src/ident.rs b/compiler/module/src/ident.rs index 4920a53826..f3bcb53561 100644 --- a/compiler/module/src/ident.rs +++ b/compiler/module/src/ident.rs @@ -59,6 +59,7 @@ impl TagName { impl ModuleName { // NOTE: After adding one of these, go to `impl ModuleId` and // add a corresponding ModuleId to there! + pub const APP: &'static str = ""; // app modules have no module name pub const BOOL: &'static str = "Bool"; pub const STR: &'static str = "Str"; pub const NUM: &'static str = "Num";