diff --git a/zork++/src/lib/cache/mod.rs b/zork++/src/lib/cache/mod.rs index 7037f734..bd10c6cc 100644 --- a/zork++/src/lib/cache/mod.rs +++ b/zork++/src/lib/cache/mod.rs @@ -89,12 +89,15 @@ pub struct ZorkCache<'a> { pub last_program_execution: DateTime, pub compilers_metadata: CompilersMetadata<'a>, pub generated_commands: Commands, + // pub new_commands: bool //< if the current iteration added some new command with respect the + // previous one } impl<'a> ZorkCache<'a> { pub fn last_program_execution(&self) -> &DateTime { &self.last_program_execution } + pub fn get_module_ifc_cmd( &mut self, module_interface_model: &ModuleInterfaceModel, @@ -125,22 +128,6 @@ impl<'a> ZorkCache<'a> { .find(|mi| module_impl_model.file() == (*mi).path()) } - /// Returns a [`Option`] of [`CommandDetails`] if the file is persisted already in the cache - pub fn is_file_cached(&self, _path: impl AsRef) -> Option<&CommandDetail> { - // let last_iteration_details = self.generated_commands.last(); - - // TODO: what a cost. We need to join them for every iteration and every file - // if let Some(last_iteration) = last_iteration_details { - // return last_iteration - // .interfaces - // .iter() - // .chain(last_iteration.implementations.iter()) - // .chain(last_iteration.sources.iter()) - // .find(|comm_det| comm_det.file_path().eq(path.as_ref())); - // } - None - } - /// The tasks associated with the cache after load it from the file system pub fn run_tasks(&mut self, program_data: &'a ZorkModel<'_>) -> Result<()> { let compiler = program_data.compiler.cpp_compiler; @@ -238,7 +225,7 @@ impl<'a> ZorkCache<'a> { None } - fn normalize_execution_result_status( + /* fn normalize_execution_result_status( // TODO: pending to re-implement it // ALe, don't read again this. We just need to fix the implementation when the commands // are generated, or even better, bring them from the cache @@ -262,7 +249,7 @@ impl<'a> ZorkCache<'a> { } else { module_command_line.execution_result } - } + } */ /// Method that returns the HashMap that holds the environmental variables that must be passed /// to the underlying shell diff --git a/zork++/src/lib/cli/output/arguments.rs b/zork++/src/lib/cli/output/arguments.rs index e6999bfd..eafa5c29 100644 --- a/zork++/src/lib/cli/output/arguments.rs +++ b/zork++/src/lib/cli/output/arguments.rs @@ -197,16 +197,6 @@ impl FromIterator for Arguments { } } -impl FromIterator for &Arguments { - fn from_iter>(iter: I) -> Self { - let mut vec = Vec::new(); - for item in iter { - vec.push(item); - } - &Arguments(vec) - } -} - impl<'a> FromIterator<&'a Argument> for Arguments { fn from_iter>(iter: I) -> Arguments { let mut vec = Vec::new(); @@ -283,7 +273,7 @@ pub mod msvc_args { use crate::{ bounds::TranslationUnit, cache::ZorkCache, - cli::output::commands::{CommandExecutionResult, SourceCommandLine}, + cli::output::commands::SourceCommandLine, project_model::{compiler::StdLibMode, ZorkModel}, }; @@ -311,9 +301,6 @@ pub mod msvc_args { ) }; - arguments.push(model.compiler.language_level_arg()); - arguments.create_and_push("/EHsc"); - arguments.create_and_push("/nologo"); arguments.create_and_push("/W4"); arguments.create_and_push("/reference"); @@ -331,11 +318,6 @@ pub mod msvc_args { "/Fo{}", stdlib_obj_path.display() }); - SourceCommandLine::from_translation_unit( - stdlib_sf, - arguments, - false, - CommandExecutionResult::default(), - ) + SourceCommandLine::new(stdlib_sf, arguments) } } diff --git a/zork++/src/lib/cli/output/commands.rs b/zork++/src/lib/cli/output/commands.rs index 69aa978a..693b8ba7 100644 --- a/zork++/src/lib/cli/output/commands.rs +++ b/zork++/src/lib/cli/output/commands.rs @@ -76,18 +76,13 @@ pub fn run_generated_commands( if !cache.generated_commands.linker.args.is_empty() { log::debug!("Processing the linker command line..."); - let linker_cmdline_args = general_args - .iter() - .chain(compiler_specific_shared_args.iter()) - .chain(cache.generated_commands.linker.args.iter()) - .collect::(); // TODO: can we avoid clone and own all the args? just use the - // .iter() as a view? let r = execute_command( program_data, - &linker_cmdline_args, + &cache.generated_commands.linker.args, &env_args, ); + cache.generated_commands.linker.execution_result = CommandExecutionResult::from(&r); if let Err(e) = r { @@ -173,28 +168,7 @@ pub struct SourceCommandLine { } impl SourceCommandLine { - pub fn from_translation_unit( - // TODO init it as a args holder, but doesn't have the status yet - tu: impl TranslationUnit, - args: Arguments, // TODO: maybe this should be an option? Cached arguments are passed - // here as default. So probably, even better than having an optional, - // we must replicate this to have a separate entity like - // CachedSourceCommandLine, and them just call them over any kind of - // constrained over some bound that wraps the operation of - // distinguish between them or not - processed: bool, - execution_result: CommandExecutionResult, - ) -> Self { - Self { - directory: tu.path(), - filename: tu.file_with_extension(), - args, - need_to_build: !processed, - execution_result, - } - } - - pub fn for_translation_unit( + pub fn new( // TODO init it as a args holder, but doesn't have the status yet tu: impl TranslationUnit, args: Arguments, @@ -215,10 +189,7 @@ impl SourceCommandLine { #[derive(Debug, Default, Clone, Serialize, Deserialize)] pub struct LinkerCommandLine { - // pub main: &'a Path, // TODO: can't this disappear? At the end of the day, is just another obj file - pub built_files: Vec, // TODO: obj files? - pub args: Arguments, // TODO: :does the linker command line needs any different that the - // generals? + pub args: Arguments, pub execution_result: CommandExecutionResult, } @@ -241,7 +212,8 @@ impl LinkerCommandLine { /// Holds the generated command line arguments for a concrete compiler #[derive(Serialize, Deserialize, Default)] pub struct Commands { - pub compiler: CppCompiler, + pub compiler: CppCompiler, // TODO: review if we can afford this field given the new + // architechture pub cpp_stdlib: Option, pub c_compat_stdlib: Option, pub system_modules: HashMap, @@ -318,6 +290,8 @@ pub enum CommandExecutionResult { Cached, /// A command which is return code indicates an unsuccessful execution Failed, + /// Whenever a translation unit must be rebuilt + PendingToBuild, /// The execution failed, returning a [`Result`] with the Err variant Error, /// A previous state before executing a command line diff --git a/zork++/src/lib/compiler/mod.rs b/zork++/src/lib/compiler/mod.rs index a60a3b0d..1b0b6cc9 100644 --- a/zork++/src/lib/compiler/mod.rs +++ b/zork++/src/lib/compiler/mod.rs @@ -8,9 +8,9 @@ pub mod data_factory; use color_eyre::Result; use std::path::Path; -use crate::bounds::{ExecutableTarget, ExtraArgs, TranslationUnit}; +use crate::bounds::{ExecutableTarget, TranslationUnit}; use crate::cli::output::arguments::{clang_args, msvc_args, Arguments}; -use crate::cli::output::commands::SourceCommandLine; +use crate::cli::output::commands::{CommandExecutionResult, SourceCommandLine}; use crate::project_model::compiler::StdLibMode; use crate::utils::constants; use crate::{ @@ -53,9 +53,9 @@ pub fn generate_commands<'a>( process_modules(model, cache)?; }; // 2nd - Generate the commands for the non-module sources - build_sources(model, cache, tests)?; - // 3rd - Build the executable or the tests // TODO: commentary and fn name - build_executable(model, cache, tests)?; + generate_sources_cmds_args(model, cache, tests)?; + // 3rd - Genates the commands for the 'targets' declared by the user + generate_linkage_targets_commands(model, cache, tests)?; Ok(()) } @@ -64,42 +64,92 @@ pub fn generate_commands<'a>( /// of each compiler vendor fn generate_modular_stdlibs_cmds(model: &ZorkModel<'_>, cache: &mut ZorkCache) { let compiler = model.compiler.cpp_compiler; + let lpe = cache.last_program_execution; // TODO: remaining ones: Clang, GCC - // TODO: try to abstract the procedures into just one entity if compiler.eq(&CppCompiler::MSVC) { - let built_stdlib_path = &cache.compilers_metadata.msvc.stdlib_bmi_path; + let vs_stdlib_path = &cache + .compilers_metadata + .msvc + .vs_stdlib_path + .as_ref() + .unwrap() + .path(); + if let Some(cpp_stdlib_cmd) = cache.generated_commands.cpp_stdlib.as_mut() { + let build_std = helpers::translation_unit_must_be_built( + compiler, + &lpe, + cpp_stdlib_cmd, + vs_stdlib_path, + ); - if !built_stdlib_path.exists() { - log::info!("Generating the command for build the {:?} C++ standard library implementation", compiler); + cpp_stdlib_cmd.need_to_build = build_std; + if !build_std && !cpp_stdlib_cmd.execution_result.eq(&CommandExecutionResult::Cached) { + cpp_stdlib_cmd.execution_result = CommandExecutionResult::Cached; + } else { + cpp_stdlib_cmd.execution_result = CommandExecutionResult::PendingToBuild; + } + } else { + log::info!( + "Generating the command for build the {:?} C++ standard library implementation", + compiler + ); let cpp_stdlib = msvc_args::generate_std_cmd(model, cache, StdLibMode::Cpp); cache.generated_commands.cpp_stdlib = Some(cpp_stdlib); } - let built_stdlib_compat_path = &cache.compilers_metadata.msvc.c_stdlib_bmi_path; - if !built_stdlib_compat_path.exists() { - log::info!("Generating the command for build the {:?} C compat C++ standard library", compiler); - let c_compat = msvc_args::generate_std_cmd(model, cache, StdLibMode::CCompat); - cache.generated_commands.c_compat_stdlib = Some(c_compat); + let vs_ccompat_stdlib_path = &cache + .compilers_metadata + .msvc + .vs_c_stdlib_path + .as_ref() + .unwrap() + .path(); + if let Some(ccompat_stdlib_cmd) = cache.generated_commands.c_compat_stdlib.as_mut() { + let build_ccompat = helpers::translation_unit_must_be_built( + compiler, + &lpe, + ccompat_stdlib_cmd, + vs_ccompat_stdlib_path, + ); + + ccompat_stdlib_cmd.need_to_build = build_ccompat; + if !build_ccompat && !ccompat_stdlib_cmd.execution_result.eq(&CommandExecutionResult::Cached) { + ccompat_stdlib_cmd.execution_result = CommandExecutionResult::Cached; + } else { + ccompat_stdlib_cmd.execution_result = CommandExecutionResult::PendingToBuild; + } + } else { + log::info!( + "Generating the command for build the {:?} C++ standard library implementation", + compiler + ); + let ccompat_stdlib = msvc_args::generate_std_cmd(model, cache, StdLibMode::CCompat); + cache.generated_commands.c_compat_stdlib = Some(ccompat_stdlib); } } } -/// Triggers the build process for compile the source files declared for the project +/// Generates the command line that will be passed to the linker to generate an [`ExecutableTarget`] +/// +/// Legacy: /// If this flow is enabled by the Cli arg `Tests`, then the executable will be generated /// for the files and properties declared for the tests section in the configuration file -fn build_executable(model: &ZorkModel<'_>, cache: &mut ZorkCache, tests: bool) -> Result<()> { +fn generate_linkage_targets_commands(model: &ZorkModel<'_>, cache: &mut ZorkCache, tests: bool) -> Result<()> { // TODO: Check if the command line is the same as the previous? If there's no new sources? // And avoid re-executing? - // TODO refactor this code, just having the if-else branch inside the fn + // TODO: refactor this code, just having the if-else branch inside the fn + // Also, shouldn't we start to think about named targets? So introduce the static and dynamic + // libraries wouldn't be such a pain? if tests { - generate_main_command_line_args(model, cache, &model.tests) + generate_linker_command_line_args(model, cache, &model.tests) // TODO: shouldn't tests be + // just a target? } else { - generate_main_command_line_args(model, cache, &model.executable) + generate_linker_command_line_args(model, cache, &model.executable) } } -fn build_sources(model: &ZorkModel<'_>, cache: &mut ZorkCache, tests: bool) -> Result<()> { +fn generate_sources_cmds_args(model: &ZorkModel<'_>, cache: &mut ZorkCache, tests: bool) -> Result<()> { log::info!("Generating the commands for the source files..."); let (srcs, target_kind) = if tests { ( @@ -119,7 +169,6 @@ fn build_sources(model: &ZorkModel<'_>, cache: &mut ZorkCache, tests: bool) -> R srcs.iter().for_each(|source| { if let Some(generated_cmd) = cache.get_source_cmd(source) { let translation_unit_must_be_rebuilt = helpers::translation_unit_must_be_built(compiler, &lpe, generated_cmd, &source.file()); - log::trace!("Source file: {:?} must be rebuilt: {translation_unit_must_be_rebuilt}", &source.file()); if !translation_unit_must_be_rebuilt { log::trace!("Source file:{:?} was not modified since the last iteration. No need to rebuilt it again.", &source.file()); @@ -172,7 +221,6 @@ fn process_module_interfaces<'a>( interfaces.iter().for_each(|module_interface| { if let Some(generated_cmd) = cache.get_module_ifc_cmd(module_interface) { let translation_unit_must_be_rebuilt = helpers::translation_unit_must_be_built(compiler, &lpe, generated_cmd, &module_interface.file()); - log::trace!("Source file: {:?} must be rebuilt: {translation_unit_must_be_rebuilt}", &module_interface.file()); if !translation_unit_must_be_rebuilt { log::trace!("Source file:{:?} was not modified since the last iteration. No need to rebuilt it again.", &module_interface.file()); @@ -198,7 +246,6 @@ fn process_module_implementations<'a>( impls.iter().for_each(|module_impl| { if let Some(generated_cmd) = cache.get_module_impl_cmd(module_impl) { let translation_unit_must_be_rebuilt = helpers::translation_unit_must_be_built(compiler, &lpe, generated_cmd, &module_impl.file()); - log::trace!("Source file: {:?} must be rebuilt: {translation_unit_must_be_rebuilt}", &module_impl.file()); if !translation_unit_must_be_rebuilt { log::trace!("Source file: {:?} was not modified since the last iteration. No need to rebuilt it again.", &module_impl.file()); @@ -211,20 +258,18 @@ fn process_module_implementations<'a>( } /// Generates the command line arguments for the desired target -pub fn generate_main_command_line_args<'a>( +pub fn generate_linker_command_line_args<'a>( model: &'a ZorkModel, cache: &mut ZorkCache, target: &'a impl ExecutableTarget<'a>, ) -> Result<()> { - log::info!("Generating the main command line..."); + log::info!("Generating the linker command line..."); let compiler = &model.compiler.cpp_compiler; let out_dir: &Path = model.build.output_dir.as_ref(); let executable_name = target.name(); let mut arguments = Arguments::default(); - /* arguments.push(model.compiler.language_level_arg()); - arguments.extend_from_slice(model.compiler.extra_args()); */ arguments.extend_from_slice(target.extra_args()); match compiler { @@ -274,9 +319,6 @@ pub fn generate_main_command_line_args<'a>( .with_extension(constants::BINARY_EXTENSION) .display() )); - // Add the .obj file of the modular stdlib to the linker command - arguments.create_and_push(&cache.compilers_metadata.msvc.stdlib_obj_path); - arguments.create_and_push(&cache.compilers_metadata.msvc.c_stdlib_obj_path); } CppCompiler::GCC => { arguments.create_and_push("-fmodules-ts"); @@ -308,10 +350,7 @@ mod sources { use crate::project_model::sourceset::SourceFile; use crate::{ bounds::{ExecutableTarget, TranslationUnit}, - cli::output::{ - arguments::clang_args, - commands::{CommandExecutionResult, SourceCommandLine}, - }, + cli::output::{arguments::clang_args, commands::SourceCommandLine}, project_model::{ compiler::CppCompiler, modules::{ModuleImplementationModel, ModuleInterfaceModel}, @@ -381,12 +420,7 @@ mod sources { arguments.create_and_push(format!("{fo}{}", obj_file.display())); arguments.create_and_push(source.file()); - let command_line = SourceCommandLine::from_translation_unit( - source, - arguments, - false, - CommandExecutionResult::default(), - ); + let command_line = SourceCommandLine::new(source, arguments); cache.generated_commands.sources.push(command_line); cache .generated_commands @@ -472,7 +506,7 @@ mod sources { } } - let cmd_line = SourceCommandLine::for_translation_unit(interface, arguments); + let cmd_line = SourceCommandLine::new(interface, arguments); cache.generated_commands.interfaces.push(cmd_line); } @@ -548,7 +582,7 @@ mod sources { } } - let cmd = SourceCommandLine::for_translation_unit(implementation, arguments); + let cmd = SourceCommandLine::new(implementation, arguments); cache.generated_commands.implementations.push(cmd); } }