From 5ddd70ebc19988d562ed01ed033d295347b3ab4b Mon Sep 17 00:00:00 2001 From: Alex Vergara Date: Sun, 5 Mar 2023 11:18:37 +0100 Subject: [PATCH 01/60] #fix - solving an error generated in the latest release for the tests executor, which was taking the path of the executable instead of the path of the tests executable --- zork++/src/lib/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/zork++/src/lib/lib.rs b/zork++/src/lib/lib.rs index f8de3af9..0a60f467 100644 --- a/zork++/src/lib/lib.rs +++ b/zork++/src/lib/lib.rs @@ -127,7 +127,7 @@ pub mod worker { Ok(_) => autorun_generated_binary( &program_data.compiler.cpp_compiler, program_data.build.output_dir, - program_data.executable.executable_name + &program_data.tests.test_executable_name ), Err(e) => Err(e), } @@ -151,7 +151,7 @@ pub mod worker { /// in order to track different aspects of the program (last time /// modified files, last process build time...) fn create_output_directory(model: &ZorkModel) -> Result<()> { - let out_dir = &model.build.output_dir; + let out_dir = model.build.output_dir; let compiler = &model.compiler.cpp_compiler; // Recursively create a directory and all of its parent components if they are missing From 7812281687813f43802e1f189632accee746c31e Mon Sep 17 00:00:00 2001 From: Alex Vergara Date: Sun, 5 Mar 2023 11:37:40 +0100 Subject: [PATCH 02/60] #fix - disabling the cache for Clang when process BMI's, since Unix systems are reporting the same error, conflicting the Clang modules cache with ours --- .gitignore | 3 +++ zork++/src/lib/compiler/mod.rs | 6 +++--- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/.gitignore b/.gitignore index 24ce9566..54078f54 100755 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,9 @@ # For testing the project in place notes.txt +zork.* +zork++.* example/ +example_project/ out/ gcm.cache/ github-example/ diff --git a/zork++/src/lib/compiler/mod.rs b/zork++/src/lib/compiler/mod.rs index 1e945c12..54254145 100644 --- a/zork++/src/lib/compiler/mod.rs +++ b/zork++/src/lib/compiler/mod.rs @@ -1,4 +1,4 @@ -//! The crate responsable for executing the core work of `Zork++`, +//! The crate responsible for executing the core work of `Zork++`, // generate command lines and execute them in a shell of the current // operating system against the designed compilers in the configuration // file. @@ -328,8 +328,8 @@ mod sources { let translation_unit_path = PathBuf::from(base_path).join(interface.file); // Code on the if branch is provisional, until Clang implements `import std;` - let processed_cache = if compiler.eq(&CppCompiler::CLANG) && cfg!(target_os = "windows") { - log::debug!("Module unit {:?} will be rebuilt since we've detected that you are using Clang on Windows", interface.module_name); + let processed_cache = if compiler.eq(&CppCompiler::CLANG) { + log::debug!("Module unit {:?} will be rebuilt since we've detected that you are using Clang", interface.module_name); false } else { helpers::flag_modules_without_changes(cache, &translation_unit_path) From ffa3a7daaf5613f5ddc0e6a0685e55fca44e00da Mon Sep 17 00:00:00 2001 From: Alex Vergara Date: Sun, 5 Mar 2023 11:38:52 +0100 Subject: [PATCH 03/60] #fix - Cleaned useless logs in the BMIs generation --- zork++/src/lib/compiler/mod.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/zork++/src/lib/compiler/mod.rs b/zork++/src/lib/compiler/mod.rs index 54254145..76614e8e 100644 --- a/zork++/src/lib/compiler/mod.rs +++ b/zork++/src/lib/compiler/mod.rs @@ -494,7 +494,6 @@ mod helpers { } else { interface.module_name.to_string() }; - log::trace!("Generating mod_unit for: {mod_unit:?}"); out_dir .join(compiler.as_ref()) From 35a5845fd608825b26257c6fe4b9321b12d2e8da Mon Sep 17 00:00:00 2001 From: Alex Vergara Date: Sun, 5 Mar 2023 12:30:09 +0100 Subject: [PATCH 04/60] Joining the base paths to the module files when the reader assembles the project model --- zork++/src/lib/compiler/mod.rs | 42 +++++++++---------------- zork++/src/lib/config_file/project.rs | 3 ++ zork++/src/lib/project_model/modules.rs | 14 ++++----- zork++/src/lib/utils/reader.rs | 23 +++++++++----- 4 files changed, 41 insertions(+), 41 deletions(-) diff --git a/zork++/src/lib/compiler/mod.rs b/zork++/src/lib/compiler/mod.rs index 76614e8e..5d8a07a3 100644 --- a/zork++/src/lib/compiler/mod.rs +++ b/zork++/src/lib/compiler/mod.rs @@ -230,9 +230,7 @@ mod sources { commands: &mut Commands<'a>, ) { let compiler = &model.compiler.cpp_compiler; - let base_path = model.modules.base_ifcs_dir; let out_dir = model.build.output_dir; - let input_file = helpers::add_input_file(interface, base_path); let mut arguments = Vec::with_capacity(8); arguments.push(model.compiler.language_level_arg()); @@ -266,7 +264,7 @@ mod sources { commands.generated_files_paths.push(miu_file_path.clone()); arguments.push(miu_file_path); // The input file - arguments.push(Argument::from(&input_file)); + arguments.push(Argument::from(&interface.file)); } CppCompiler::MSVC => { arguments.push(Argument::from("/EHsc")); @@ -308,7 +306,7 @@ mod sources { } arguments.push(Argument::from("/TP")); // The input file - arguments.push(Argument::from(&input_file)) + arguments.push(Argument::from(&interface.file)) } CppCompiler::GCC => { arguments.push(Argument::from("-fmodules-ts")); @@ -316,7 +314,7 @@ mod sources { arguments.push(Argument::from("c++")); arguments.push(Argument::from("-c")); // The input file - arguments.push(Argument::from(&input_file)); + arguments.push(Argument::from(&interface.file)); // The output file arguments.push(Argument::from("-o")); let miu_file_path = @@ -326,17 +324,19 @@ mod sources { } } - let translation_unit_path = PathBuf::from(base_path).join(interface.file); // Code on the if branch is provisional, until Clang implements `import std;` let processed_cache = if compiler.eq(&CppCompiler::CLANG) { - log::debug!("Module unit {:?} will be rebuilt since we've detected that you are using Clang", interface.module_name); + log::debug!( + "Module unit {:?} will be rebuilt since we've detected that you are using Clang", + interface.module_name + ); false } else { - helpers::flag_modules_without_changes(cache, &translation_unit_path) + helpers::flag_modules_without_changes(cache, &interface.file) }; let command_line = ModuleCommandLine { - path: translation_unit_path, + path: interface.file.clone(), args: arguments, processed: processed_cache, execution_result: CommandExecutionResult::default(), @@ -352,9 +352,7 @@ mod sources { commands: &mut Commands<'a>, ) { let compiler = &model.compiler.cpp_compiler; - let base_path = model.modules.base_impls_dir; let out_dir = model.build.output_dir; - let input_file = helpers::add_input_file(implementation, base_path); let mut arguments = Vec::with_capacity(12); arguments.push(model.compiler.language_level_arg()); @@ -384,7 +382,7 @@ mod sources { ); // The input file - arguments.push(Argument::from(&input_file)) + arguments.push(Argument::from(&implementation.file)) } CppCompiler::MSVC => { arguments.push(Argument::from("/EHsc")); @@ -400,7 +398,7 @@ mod sources { .join("interfaces"), )); // The input file - arguments.push(Argument::from(&input_file)); + arguments.push(Argument::from(&implementation.file)); // The output .obj file let obj_file_path = out_dir .join(compiler.as_ref()) @@ -418,7 +416,7 @@ mod sources { arguments.push(Argument::from("-fmodules-ts")); arguments.push(Argument::from("-c")); // The input file - arguments.push(Argument::from(&input_file)); + arguments.push(Argument::from(&implementation.file)); // The output file arguments.push(Argument::from("-o")); let obj_file_path = Argument::from(helpers::generate_impl_obj_file( @@ -431,11 +429,10 @@ mod sources { } } - let translation_unit_path = PathBuf::from(base_path).join(implementation.file); let command_line = ModuleCommandLine { - path: translation_unit_path.clone(), + path: implementation.file.clone(), args: arguments, - processed: helpers::flag_modules_without_changes(cache, &translation_unit_path), + processed: helpers::flag_modules_without_changes(cache, &implementation.file), execution_result: CommandExecutionResult::default(), }; commands.implementations.push(command_line); @@ -456,15 +453,6 @@ mod helpers { }; use std::path::PathBuf; - /// Formats the string that represents an input file that will be the target of - /// the build process and that will be passed to the compiler - pub(crate) fn add_input_file( - translation_unit: &T, - base_path: &Path, - ) -> PathBuf { - base_path.join(translation_unit.file()) - } - /// Creates the path for a prebuilt module interface, based on the default expected /// extension for BMI's given a compiler /// @@ -519,7 +507,7 @@ mod helpers { } /// System headers as can be imported as modules must be built before being imported. - /// First it will compare with the elements stored in the caché, and only we will + /// First it will compare with the elements stored in the cache, and only we will /// generate commands for the non processed elements yet. /// /// This is for `GCC` and `Clang` diff --git a/zork++/src/lib/config_file/project.rs b/zork++/src/lib/config_file/project.rs index 51d129ca..65c95574 100644 --- a/zork++/src/lib/config_file/project.rs +++ b/zork++/src/lib/config_file/project.rs @@ -15,6 +15,7 @@ use serde::*; /// #[project] /// name = 'Zork++ serde tests' /// authors = ['zerodaycode.gz@gmail.com'] +/// compilation_db = true ///"#; /// /// let config: ProjectAttribute = toml::from_str(CONFIG_FILE_MOCK) @@ -22,6 +23,7 @@ use serde::*; /// /// assert_eq!(config.name, "Zork++ serde tests"); /// assert_eq!(config.authors, Some(vec!["zerodaycode.gz@gmail.com"])); +/// assert_eq!(config.compilation_db, Some(true)); /// ``` /// /// > Note: TOML table are toml commented (#) to allow us to parse @@ -38,4 +40,5 @@ pub struct ProjectAttribute<'a> { pub name: &'a str, #[serde(borrow)] pub authors: Option>, + pub compilation_db: Option, } diff --git a/zork++/src/lib/project_model/modules.rs b/zork++/src/lib/project_model/modules.rs index c093a264..1fda348a 100644 --- a/zork++/src/lib/project_model/modules.rs +++ b/zork++/src/lib/project_model/modules.rs @@ -1,5 +1,5 @@ use core::fmt; -use std::path::Path; +use std::path::{Path, PathBuf}; use crate::{bounds::TranslationUnit, config_file::modules::ModulePartition}; @@ -14,7 +14,7 @@ pub struct ModulesModel<'a> { #[derive(Debug, PartialEq, Eq)] pub struct ModuleInterfaceModel<'a> { - pub file: &'a Path, + pub file: PathBuf, pub module_name: &'a str, pub partition: Option>, pub dependencies: Vec<&'a str>, @@ -32,13 +32,13 @@ impl<'a> fmt::Display for ModuleInterfaceModel<'a> { impl<'a> TranslationUnit for ModuleInterfaceModel<'a> { fn file(&self) -> &Path { - self.file + &self.file } } impl<'a> TranslationUnit for &'a ModuleInterfaceModel<'a> { fn file(&self) -> &Path { - self.file + &self.file } } @@ -61,7 +61,7 @@ impl<'a> From<&ModulePartition<'a>> for ModulePartitionModel<'a> { #[derive(Debug, PartialEq, Eq)] pub struct ModuleImplementationModel<'a> { - pub file: &'a Path, + pub file: PathBuf, pub dependencies: Vec<&'a str>, } @@ -73,12 +73,12 @@ impl<'a> fmt::Display for ModuleImplementationModel<'a> { impl<'a> TranslationUnit for ModuleImplementationModel<'a> { fn file(&self) -> &Path { - self.file + &self.file } } impl<'a> TranslationUnit for &'a ModuleImplementationModel<'a> { fn file(&self) -> &Path { - self.file + &self.file } } diff --git a/zork++/src/lib/utils/reader.rs b/zork++/src/lib/utils/reader.rs index 00fcebc7..af420608 100644 --- a/zork++/src/lib/utils/reader.rs +++ b/zork++/src/lib/utils/reader.rs @@ -129,6 +129,7 @@ fn assemble_build_model<'a>(config: &'a Option) -> BuildModel<'a } } +//noinspection ALL fn assemble_executable_model<'a>( project_name: &'a str, config: &'a Option, @@ -180,7 +181,11 @@ fn assemble_modules_model<'a>(config: &'a Option) -> ModulesMo let interfaces = config .and_then(|modules| modules.interfaces.as_ref()) - .map(|ifcs| ifcs.iter().map(assemble_module_interface_model).collect()) + .map(|ifcs| { + ifcs.iter() + .map(|m_ifc| assemble_module_interface_model(m_ifc, base_ifcs_dir)) + .collect() + }) .unwrap_or_default(); let base_impls_dir = config @@ -192,7 +197,7 @@ fn assemble_modules_model<'a>(config: &'a Option) -> ModulesMo .map(|impls| { impls .iter() - .map(assemble_module_implementation_model) + .map(|m_impl| assemble_module_implementation_model(m_impl, base_impls_dir)) .collect() }) .unwrap_or_default(); @@ -210,7 +215,10 @@ fn assemble_modules_model<'a>(config: &'a Option) -> ModulesMo } } -fn assemble_module_interface_model<'a>(config: &'a ModuleInterface) -> ModuleInterfaceModel<'a> { +fn assemble_module_interface_model<'a>( + config: &'a ModuleInterface, + base_path: &str, +) -> ModuleInterfaceModel<'a> { let module_name = config.module_name.unwrap_or_else(|| { Path::new(config.file) .file_stem() @@ -229,7 +237,7 @@ fn assemble_module_interface_model<'a>(config: &'a ModuleInterface) -> ModuleInt }; ModuleInterfaceModel { - file: Path::new(config.file), + file: Path::new(base_path).join(config.file), module_name, partition, dependencies, @@ -238,6 +246,7 @@ fn assemble_module_interface_model<'a>(config: &'a ModuleInterface) -> ModuleInt fn assemble_module_implementation_model<'a>( config: &'a ModuleImplementation, + base_path: &str ) -> ModuleImplementationModel<'a> { let mut dependencies = config.dependencies.clone().unwrap_or_default(); if dependencies.is_empty() { @@ -251,7 +260,7 @@ fn assemble_module_implementation_model<'a>( } ModuleImplementationModel { - file: Path::new(config.file), + file: Path::new(base_path).join(config.file), dependencies, } } @@ -398,13 +407,13 @@ mod test { base_ifcs_dir: Path::new("ifc"), interfaces: vec![ ModuleInterfaceModel { - file: Path::new("math.cppm"), + file: PathBuf::from(Path::new("math.cppm")), module_name: "math", partition: None, dependencies: vec![], }, ModuleInterfaceModel { - file: Path::new("some_module.cppm"), + file: PathBuf::from(Path::new("some_module.cppm")), module_name: "math", partition: None, dependencies: vec![], From 6e2bd84b8ad742ef3dd8026d80b5eae56fa176d7 Mon Sep 17 00:00:00 2001 From: Alex Vergara Date: Sun, 5 Mar 2023 12:33:45 +0100 Subject: [PATCH 05/60] Enabling a boolean flag to indicate whenever the user wants to generate the compilation database --- zork++/src/lib/compiler/mod.rs | 2 +- zork++/src/lib/project_model/project.rs | 1 + zork++/src/lib/utils/reader.rs | 9 ++++++--- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/zork++/src/lib/compiler/mod.rs b/zork++/src/lib/compiler/mod.rs index 5d8a07a3..f83fc8ea 100644 --- a/zork++/src/lib/compiler/mod.rs +++ b/zork++/src/lib/compiler/mod.rs @@ -118,7 +118,7 @@ mod sources { utils::constants, }; use color_eyre::Result; - use std::path::{Path, PathBuf}; + use std::path::Path; /// Generates the command line arguments for non-module source files, including the one that /// holds the main function diff --git a/zork++/src/lib/project_model/project.rs b/zork++/src/lib/project_model/project.rs index 04f97661..acbde8f8 100644 --- a/zork++/src/lib/project_model/project.rs +++ b/zork++/src/lib/project_model/project.rs @@ -2,4 +2,5 @@ pub struct ProjectModel<'a> { pub name: &'a str, pub authors: &'a [&'a str], + pub compilation_db: bool, } diff --git a/zork++/src/lib/utils/reader.rs b/zork++/src/lib/utils/reader.rs index af420608..999d19a5 100644 --- a/zork++/src/lib/utils/reader.rs +++ b/zork++/src/lib/utils/reader.rs @@ -100,6 +100,7 @@ fn assemble_project_model<'a>(config: &'a ProjectAttribute) -> ProjectModel<'a> .authors .as_ref() .map_or_else(|| &[] as &[&str], |auths| auths.as_slice()), + compilation_db: config.compilation_db.unwrap_or_default(), } } @@ -246,7 +247,7 @@ fn assemble_module_interface_model<'a>( fn assemble_module_implementation_model<'a>( config: &'a ModuleImplementation, - base_path: &str + base_path: &str, ) -> ModuleImplementationModel<'a> { let mut dependencies = config.dependencies.clone().unwrap_or_default(); if dependencies.is_empty() { @@ -336,6 +337,7 @@ mod test { project: ProjectModel { name: "Zork++", authors: &["zerodaycode.gz@gmail.com"], + compilation_db: false, }, compiler: CompilerModel { cpp_compiler: CppCompiler::CLANG, @@ -385,6 +387,7 @@ mod test { project: ProjectModel { name: "Zork++", authors: &["zerodaycode.gz@gmail.com"], + compilation_db: false, }, compiler: CompilerModel { cpp_compiler: CppCompiler::CLANG, @@ -422,11 +425,11 @@ mod test { base_impls_dir: Path::new("src"), implementations: vec![ ModuleImplementationModel { - file: Path::new("math.cpp"), + file: PathBuf::from(Path::new("math.cpp")), dependencies: vec!["math"], }, ModuleImplementationModel { - file: Path::new("some_module_impl.cpp"), + file: PathBuf::from(Path::new("some_module_impl.cpp")), dependencies: vec!["iostream"], }, ], From dd8506db8c75c2ff224f20df6d4dcad4dfe0f522 Mon Sep 17 00:00:00 2001 From: Alex Vergara Date: Sun, 5 Mar 2023 13:57:21 +0100 Subject: [PATCH 06/60] Initial implementation of the generation of a database compilation --- zork++/src/lib/cache/mod.rs | 86 +++++++++++++++++++++++++- zork++/src/lib/config_file/compiler.rs | 12 ++-- zork++/src/lib/utils/constants.rs | 1 + 3 files changed, 89 insertions(+), 10 deletions(-) diff --git a/zork++/src/lib/cache/mod.rs b/zork++/src/lib/cache/mod.rs index f857cf54..76e9d888 100644 --- a/zork++/src/lib/cache/mod.rs +++ b/zork++/src/lib/cache/mod.rs @@ -20,6 +20,7 @@ use crate::{ }, }; use serde::{Deserialize, Serialize}; +use crate::utils::constants::COMPILATION_DATABASE; /// Standalone utility for retrieve the Zork++ cache file pub fn load(program_data: &ZorkModel<'_>, cli_args: &CliArgs) -> Result { @@ -61,7 +62,7 @@ pub fn save( .join(program_data.compiler.cpp_compiler.as_ref()) .join(constants::ZORK_CACHE_FILENAME); - cache.run_final_tasks(program_data, commands); + cache.run_final_tasks(program_data, commands)?; cache.last_program_execution = Utc::now(); utils::fs::serialize_object_to_file(cache_path, &cache) @@ -117,7 +118,10 @@ impl ZorkCache { } /// Runs the tasks just before end the program and save the cache - pub fn run_final_tasks(&mut self, program_data: &ZorkModel<'_>, commands: Commands<'_>) { + pub fn run_final_tasks(&mut self, program_data: &ZorkModel<'_>, commands: Commands<'_>) -> Result<()>{ + if program_data.project.compilation_db { // TODO Read prev changes and compare without regenerate + map_generated_commands_to_compilation_db(&commands)?; + } self.save_generated_commands(commands); if !(program_data.compiler.cpp_compiler == CppCompiler::MSVC) { @@ -128,6 +132,8 @@ impl ZorkCache { .map(|e| e.to_string()) .collect::>(); } + + Ok(()) } fn save_generated_commands(&mut self, commands: Commands<'_>) { @@ -301,6 +307,82 @@ impl ZorkCache { } } +/// Generates the `compile_commands.json` file, that acts as a compilation database +/// for some static analysis external tools, like `clang-tidy`, and populates it with +/// the generated commands for the translation units +fn map_generated_commands_to_compilation_db(commands: &Commands<'_>) -> Result<()> { + log::trace!("Generating the compilation database..."); + let total_commands = commands.interfaces.len() + commands.implementations.len() + 1; + let mut compilation_db_entries = Vec::with_capacity(total_commands); + + for command in &commands.interfaces { + let path = command.path.parent().map_or("", |p| p.to_str().unwrap_or_default()); + let mut arguments = vec![commands.compiler.as_ref()]; + arguments.extend( + command.args.iter().map(|arg| arg.value).collect::>() + ); + let file = command.path.file_name().map_or("", |f| f.to_str().unwrap_or_default()); + + compilation_db_entries.push( + CompileCommands { + directory: path, + arguments, + file, + } + ) + } + + for command in &commands.implementations { + let path = command.path.parent().map_or("", |p| p.to_str().unwrap_or_default()); + let mut arguments = vec![commands.compiler.as_ref()]; + arguments.extend( + command.args.iter().map(|arg| arg.value).collect::>() + ); + let file = command.path.file_name().map_or("", |f| f.to_str().unwrap_or_default()); + + compilation_db_entries.push( + CompileCommands { + directory: path, + arguments, + file, + } + ) + } + + // TODO This should be changed in order to only include the file that holds the entry + // point of the program. For that, we should modify the config file, to force the user + // to tell us what is that file + for source in &commands.sources.sources_paths { + let mut arguments = vec![commands.compiler.as_ref()]; + arguments.extend( + commands.sources.args.iter().map(|arg| arg.value).collect::>() + ); + compilation_db_entries.push( + CompileCommands { + directory: source.parent().map_or("", |p| p.to_str().unwrap_or_default()), + arguments: commands.sources.args.iter().map(|arg| arg.value).collect::>(), + file: source.file_name().map_or("", |f| f.to_str().unwrap_or_default()), + } + ) + } + + let compile_commands_path = Path::new(COMPILATION_DATABASE); + if !Path::new(&compile_commands_path).exists() { + File::create(compile_commands_path).with_context(|| "Error creating the cache file")?; + } + utils::fs::serialize_object_to_file(Path::new(compile_commands_path), &compilation_db_entries) + .with_context(move || "Error generating the compilation database") +} + +/// Data model for serialize the data that will be outputted +/// to the `compile_commands.json` compilation database file +#[derive(Deserialize, Serialize, Debug, Default, Clone)] +pub struct CompileCommands<'a> { + pub directory: &'a str, + pub arguments: Vec<&'a str>, + pub file: &'a str +} + #[derive(Deserialize, Serialize, Debug, Default, Clone)] pub struct CachedCommands { compiler: CppCompiler, diff --git a/zork++/src/lib/config_file/compiler.rs b/zork++/src/lib/config_file/compiler.rs index ee530aa0..67c72dbe 100644 --- a/zork++/src/lib/config_file/compiler.rs +++ b/zork++/src/lib/config_file/compiler.rs @@ -1,10 +1,6 @@ ///! file for represent the available configuration properties within Zork++ ///! for setting up the target compiler -/// -/// ## TODO LIST -///! TODO -> Convert this attribute into a collection of this attribute, -/// allowing Zork++ to compile one project for multiple compilers -/// at the same time + use serde::Deserialize; use crate::project_model; @@ -20,7 +16,7 @@ use crate::project_model; /// C++ standard that should be used on the compilation process /// /// * `std_lib` - The concrete C++ standard library (vendor specific) -/// to link the builded code against +/// to link the built code against /// /// * `extra_args` - A comma separated list of strings that will be passed /// to the generated command lines. This ones here will be placed in every @@ -29,7 +25,7 @@ use crate::project_model; /// are included here, this will be wired in the main command line (the executable), /// the ones generated for compile modules (both interfaces and implementations) /// and for the command line generated for build the specified test suite and -/// the test executableç +/// the test executable /// /// * `system_headers_path` - An string indicating a user custom defined place /// where the system headers lives. @@ -109,7 +105,7 @@ impl Into for CppCompiler { /// enumerated type in Rust /// /// Variants *2A* and *2B* represents Clang's way of -/// use the latests features available +/// use the latest features available /// /// Variant *LATEST* is the `MSVC` specific way of set the language /// standard level to the latest features available in Microsoft's compiler diff --git a/zork++/src/lib/utils/constants.rs b/zork++/src/lib/utils/constants.rs index 6332ce2c..12501fbe 100644 --- a/zork++/src/lib/utils/constants.rs +++ b/zork++/src/lib/utils/constants.rs @@ -11,6 +11,7 @@ pub const BINARY_EXTENSION: &str = if cfg!(target_os = "windows") { }; pub const ZORK_CACHE_FILENAME: &str = "cache.json"; +pub const COMPILATION_DATABASE: &str = "compile_commands.json"; pub const GCC_CACHE_DIR: &str = "gcm.cache"; From af08d6928309a950121905eac9abf67bfdd360fc Mon Sep 17 00:00:00 2001 From: Alex Vergara Date: Sun, 5 Mar 2023 14:01:19 +0100 Subject: [PATCH 07/60] Denying all the unknown fields, instead of silently ignore them when the configuration file is parsed --- zork++/src/lib/config_file/build.rs | 1 + zork++/src/lib/config_file/executable.rs | 1 + zork++/src/lib/config_file/modules.rs | 1 + zork++/src/lib/config_file/tests.rs | 9 ++++----- 4 files changed, 7 insertions(+), 5 deletions(-) diff --git a/zork++/src/lib/config_file/build.rs b/zork++/src/lib/config_file/build.rs index 649d4dcc..5c5d7008 100644 --- a/zork++/src/lib/config_file/build.rs +++ b/zork++/src/lib/config_file/build.rs @@ -24,6 +24,7 @@ use serde::*; /// assert_eq!(config.output_dir, Some("out")); /// ``` #[derive(Deserialize, Debug, PartialEq)] +#[serde (deny_unknown_fields)] pub struct BuildAttribute<'a> { #[serde(borrow)] pub output_dir: Option<&'a str>, diff --git a/zork++/src/lib/config_file/executable.rs b/zork++/src/lib/config_file/executable.rs index 1ff1530b..c0b6d2ef 100644 --- a/zork++/src/lib/config_file/executable.rs +++ b/zork++/src/lib/config_file/executable.rs @@ -40,6 +40,7 @@ use serde::*; /// For a test over a real example, please look at the /// [`zork::config_file::ZorkConfigFile`] doc-test #[derive(Deserialize, Debug, PartialEq, Clone, Default)] +#[serde (deny_unknown_fields)] pub struct ExecutableAttribute<'a> { #[serde(borrow)] pub executable_name: Option<&'a str>, diff --git a/zork++/src/lib/config_file/modules.rs b/zork++/src/lib/config_file/modules.rs index 12722bb1..3f302918 100644 --- a/zork++/src/lib/config_file/modules.rs +++ b/zork++/src/lib/config_file/modules.rs @@ -129,6 +129,7 @@ pub struct ModulesAttribute<'a> { /// assert_eq!(ifc_3.module_name, Some("math_part")); /// ``` #[derive(Deserialize, Debug, PartialEq)] +#[serde (deny_unknown_fields)] pub struct ModuleInterface<'a> { #[serde(borrow)] pub file: &'a str, diff --git a/zork++/src/lib/config_file/tests.rs b/zork++/src/lib/config_file/tests.rs index 1e290728..9e36733e 100644 --- a/zork++/src/lib/config_file/tests.rs +++ b/zork++/src/lib/config_file/tests.rs @@ -2,11 +2,10 @@ use serde::*; /// [`TestsAttribute`] - Tests attribute allows you to run the tests written for your application in a convenient way -/// * `tests_executable_name` - This is names executions test -/// * `sources_base_path` - Directoties source tests -/// * `sources` - Define extension files to tests -/// * `auto_run_tests` - Define if required run test after build -/// * `extra_args`- Define command to added properties test execution +/// * `tests_executable_name` - The name of the generated binary +/// * `sources_base_path` - Base common path for the source files +/// * `sources` - All the source files that must be included +/// * `extra_args`- Extra arguments to add to the generated command line(s) /// /// ### Tests /// From 24db40a7f9fb97683d6b0b4663aa1417f496bc83 Mon Sep 17 00:00:00 2001 From: Alex Vergara Date: Sun, 5 Mar 2023 19:42:54 +0100 Subject: [PATCH 08/60] Full paths for the compilation database 'directory' property. NEW attributes included for designing the entry point of the program --- .gitignore | 1 + zork++/src/lib/cache/mod.rs | 135 +++++++++++------- zork++/src/lib/cli/output/arguments.rs | 2 +- zork++/src/lib/cli/output/commands.rs | 15 +- zork++/src/lib/config_file/build.rs | 2 +- zork++/src/lib/config_file/compiler.rs | 1 - zork++/src/lib/config_file/executable.rs | 6 +- zork++/src/lib/config_file/modules.rs | 2 +- zork++/src/lib/config_file/tests.rs | 4 + zork++/src/lib/lib.rs | 8 +- zork++/src/lib/project_model/executable.rs | 2 + zork++/src/lib/project_model/tests.rs | 2 + zork++/src/lib/utils/reader.rs | 10 ++ .../template/resources/zork_example.toml | 4 +- .../resources/zork_example_basic.toml | 4 +- 15 files changed, 131 insertions(+), 67 deletions(-) diff --git a/.gitignore b/.gitignore index 54078f54..5b6976d4 100755 --- a/.gitignore +++ b/.gitignore @@ -8,6 +8,7 @@ out/ gcm.cache/ github-example/ cached_mods/ +compile_commands.json # out/ .coverage diff --git a/zork++/src/lib/cache/mod.rs b/zork++/src/lib/cache/mod.rs index 76e9d888..01802e01 100644 --- a/zork++/src/lib/cache/mod.rs +++ b/zork++/src/lib/cache/mod.rs @@ -3,11 +3,13 @@ use chrono::{DateTime, Utc}; use color_eyre::{eyre::Context, Result}; use std::{ + fs, fs::File, path::{Path, PathBuf}, }; use walkdir::WalkDir; +use crate::utils::constants::COMPILATION_DATABASE; use crate::{ cli::{ input::CliArgs, @@ -20,7 +22,6 @@ use crate::{ }, }; use serde::{Deserialize, Serialize}; -use crate::utils::constants::COMPILATION_DATABASE; /// Standalone utility for retrieve the Zork++ cache file pub fn load(program_data: &ZorkModel<'_>, cli_args: &CliArgs) -> Result { @@ -55,6 +56,7 @@ pub fn save( program_data: &ZorkModel<'_>, mut cache: ZorkCache, commands: Commands<'_>, + test_mode: bool, ) -> Result<()> { let cache_path = &Path::new(program_data.build.output_dir) .join("zork") @@ -62,7 +64,7 @@ pub fn save( .join(program_data.compiler.cpp_compiler.as_ref()) .join(constants::ZORK_CACHE_FILENAME); - cache.run_final_tasks(program_data, commands)?; + cache.run_final_tasks(program_data, commands, test_mode)?; cache.last_program_execution = Utc::now(); utils::fs::serialize_object_to_file(cache_path, &cache) @@ -118,9 +120,15 @@ impl ZorkCache { } /// Runs the tasks just before end the program and save the cache - pub fn run_final_tasks(&mut self, program_data: &ZorkModel<'_>, commands: Commands<'_>) -> Result<()>{ - if program_data.project.compilation_db { // TODO Read prev changes and compare without regenerate - map_generated_commands_to_compilation_db(&commands)?; + pub fn run_final_tasks( + &mut self, + program_data: &ZorkModel<'_>, + commands: Commands<'_>, + test_mode: bool, + ) -> Result<()> { + if program_data.project.compilation_db { + // TODO Read prev changes and compare without regenerate + map_generated_commands_to_compilation_db(program_data, &commands, test_mode)?; } self.save_generated_commands(commands); @@ -310,62 +318,91 @@ impl ZorkCache { /// Generates the `compile_commands.json` file, that acts as a compilation database /// for some static analysis external tools, like `clang-tidy`, and populates it with /// the generated commands for the translation units -fn map_generated_commands_to_compilation_db(commands: &Commands<'_>) -> Result<()> { +fn map_generated_commands_to_compilation_db( + program_data: &ZorkModel, + commands: &Commands<'_>, + test_mode: bool, +) -> Result<()> { log::trace!("Generating the compilation database..."); let total_commands = commands.interfaces.len() + commands.implementations.len() + 1; let mut compilation_db_entries = Vec::with_capacity(total_commands); for command in &commands.interfaces { - let path = command.path.parent().map_or("", |p| p.to_str().unwrap_or_default()); - let mut arguments = vec![commands.compiler.as_ref()]; + let path = fs::canonicalize(command.path.parent().unwrap_or(Path::new(""))) + .map(|p| String::from(p.to_str().unwrap_or_default())) + .unwrap_or_default(); + + let mut arguments = vec![commands.compiler.get_driver()]; arguments.extend( - command.args.iter().map(|arg| arg.value).collect::>() + command + .args + .iter() + .map(|arg| arg.value) + .collect::>(), ); - let file = command.path.file_name().map_or("", |f| f.to_str().unwrap_or_default()); - - compilation_db_entries.push( - CompileCommands { - directory: path, - arguments, - file, - } - ) + let file = command + .path + .file_name() + .map_or("", |f| f.to_str().unwrap_or_default()); + + compilation_db_entries.push(CompileCommands { + directory: path, + arguments, + file, + }) } for command in &commands.implementations { - let path = command.path.parent().map_or("", |p| p.to_str().unwrap_or_default()); - let mut arguments = vec![commands.compiler.as_ref()]; - arguments.extend( - command.args.iter().map(|arg| arg.value).collect::>() - ); - let file = command.path.file_name().map_or("", |f| f.to_str().unwrap_or_default()); + let path = fs::canonicalize(command.path.parent().unwrap_or(Path::new(""))) + .map(|p| String::from(p.to_str().unwrap_or_default())) + .unwrap_or_default(); - compilation_db_entries.push( - CompileCommands { - directory: path, - arguments, - file, - } - ) - } - - // TODO This should be changed in order to only include the file that holds the entry - // point of the program. For that, we should modify the config file, to force the user - // to tell us what is that file - for source in &commands.sources.sources_paths { - let mut arguments = vec![commands.compiler.as_ref()]; + let mut arguments = vec![commands.compiler.get_driver()]; arguments.extend( - commands.sources.args.iter().map(|arg| arg.value).collect::>() + command + .args + .iter() + .map(|arg| arg.value) + .collect::>(), ); - compilation_db_entries.push( - CompileCommands { - directory: source.parent().map_or("", |p| p.to_str().unwrap_or_default()), - arguments: commands.sources.args.iter().map(|arg| arg.value).collect::>(), - file: source.file_name().map_or("", |f| f.to_str().unwrap_or_default()), - } - ) + let file = command + .path + .file_name() + .map_or("", |f| f.to_str().unwrap_or_default()); + + compilation_db_entries.push(CompileCommands { + directory: path, + arguments, + file, + }) } + // generated command for the binary (exe or tests exe) + let entry_point = if !test_mode { + Path::new(".").join(program_data.executable.main) + } else { + Path::new(".").join(program_data.tests.main) + }; + + let mut main_arguments = vec![commands.compiler.get_driver()]; + main_arguments.extend( + commands + .sources + .args + .iter() + .map(|arg| arg.value) + .collect::>(), + ); + compilation_db_entries.push(CompileCommands { + directory: fs::canonicalize(entry_point.parent().unwrap_or(Path::new("."))) + .map(|p| String::from(p.to_str().unwrap_or_default())) + .unwrap_or_default(), + arguments: main_arguments, + file: entry_point + .file_name() + .map_or("", |f| f.to_str().unwrap_or_default()), + }); + let compile_commands_path = Path::new(COMPILATION_DATABASE); if !Path::new(&compile_commands_path).exists() { File::create(compile_commands_path).with_context(|| "Error creating the cache file")?; @@ -376,11 +413,11 @@ fn map_generated_commands_to_compilation_db(commands: &Commands<'_>) -> Result<( /// Data model for serialize the data that will be outputted /// to the `compile_commands.json` compilation database file -#[derive(Deserialize, Serialize, Debug, Default, Clone)] +#[derive(Serialize, Debug, Default, Clone)] pub struct CompileCommands<'a> { - pub directory: &'a str, + pub directory: String, pub arguments: Vec<&'a str>, - pub file: &'a str + pub file: &'a str, } #[derive(Deserialize, Serialize, Debug, Default, Clone)] diff --git a/zork++/src/lib/cli/output/arguments.rs b/zork++/src/lib/cli/output/arguments.rs index e50eae55..d5b3a398 100644 --- a/zork++/src/lib/cli/output/arguments.rs +++ b/zork++/src/lib/cli/output/arguments.rs @@ -72,7 +72,7 @@ pub mod clang_args { Argument::from(format!( "-fmodule-map-file={}", out_dir - .join("zork") + .join("../zork") .join("intrinsics") .join("zork.modulemap") .display() diff --git a/zork++/src/lib/cli/output/commands.rs b/zork++/src/lib/cli/output/commands.rs index 1ec9d49e..3299679a 100644 --- a/zork++/src/lib/cli/output/commands.rs +++ b/zork++/src/lib/cli/output/commands.rs @@ -23,6 +23,7 @@ pub fn run_generated_commands( program_data: &ZorkModel<'_>, mut commands: Commands<'_>, cache: ZorkCache, + test_mode: bool, ) -> Result { if !commands.interfaces.is_empty() { log::debug!("Executing the commands for the module interfaces..."); @@ -32,11 +33,11 @@ pub fn run_generated_commands( let r = execute_command(&commands.compiler, &miu.args, &cache); miu.execution_result = CommandExecutionResult::from(&r); if let Err(e) = r { - cache::save(program_data, cache, commands)?; + cache::save(program_data, cache, commands, test_mode)?; return Err(e); } else if !r.as_ref().unwrap().success() { let c_miu = miu.clone(); - cache::save(program_data, cache, commands)?; + cache::save(program_data, cache, commands, test_mode)?; return Err(eyre!( "Ending the program, because the build of: {:?} wasn't ended successfully", c_miu.path @@ -57,11 +58,11 @@ pub fn run_generated_commands( let r = execute_command(&commands.compiler, &implm.args, &cache); implm.execution_result = CommandExecutionResult::from(&r); if let Err(e) = r { - cache::save(program_data, cache, commands)?; + cache::save(program_data, cache, commands, test_mode)?; return Err(e); } else if !r.as_ref().unwrap().success() { let c_miu = implm.clone(); - cache::save(program_data, cache, commands)?; + cache::save(program_data, cache, commands, test_mode)?; return Err(eyre!( "Ending the program, because the build of: {:?} wasn't ended successfully", c_miu.path @@ -81,17 +82,17 @@ pub fn run_generated_commands( commands.sources.execution_result = CommandExecutionResult::from(&r); if let Err(e) = r { - cache::save(program_data, cache, commands)?; + cache::save(program_data, cache, commands, test_mode)?; return Err(e); } else if !r.as_ref().unwrap().success() { - cache::save(program_data, cache, commands)?; + cache::save(program_data, cache, commands, test_mode)?; return Err(eyre!( "Ending the program, because the main command line execution wasn't ended successfully", )); } } - cache::save(program_data, cache, commands)?; + cache::save(program_data, cache, commands, test_mode)?; Ok(CommandExecutionResult::Success) } diff --git a/zork++/src/lib/config_file/build.rs b/zork++/src/lib/config_file/build.rs index 5c5d7008..268fd574 100644 --- a/zork++/src/lib/config_file/build.rs +++ b/zork++/src/lib/config_file/build.rs @@ -24,7 +24,7 @@ use serde::*; /// assert_eq!(config.output_dir, Some("out")); /// ``` #[derive(Deserialize, Debug, PartialEq)] -#[serde (deny_unknown_fields)] +#[serde(deny_unknown_fields)] pub struct BuildAttribute<'a> { #[serde(borrow)] pub output_dir: Option<&'a str>, diff --git a/zork++/src/lib/config_file/compiler.rs b/zork++/src/lib/config_file/compiler.rs index 67c72dbe..dbfe1d89 100644 --- a/zork++/src/lib/config_file/compiler.rs +++ b/zork++/src/lib/config_file/compiler.rs @@ -1,6 +1,5 @@ ///! file for represent the available configuration properties within Zork++ ///! for setting up the target compiler - use serde::Deserialize; use crate::project_model; diff --git a/zork++/src/lib/config_file/executable.rs b/zork++/src/lib/config_file/executable.rs index c0b6d2ef..a4370efc 100644 --- a/zork++/src/lib/config_file/executable.rs +++ b/zork++/src/lib/config_file/executable.rs @@ -20,6 +20,7 @@ use serde::*; /// sources = [ /// '*.cpp' /// ] +/// main = "main.cpp" /// extra_args = ['example'] /// "#; /// @@ -29,6 +30,7 @@ use serde::*; /// assert_eq!(config.executable_name, Some("outputExecutableName")); /// assert_eq!(config.sources_base_path, Some("./src")); /// assert_eq!(config.sources, Some(vec!["*.cpp"])); +/// assert_eq!(config.main, main.cpp); /// assert_eq!(config.extra_args, Some(vec!["example"])) /// ``` /// > Note: TOML table are toml commented (#) to allow us to parse @@ -40,7 +42,7 @@ use serde::*; /// For a test over a real example, please look at the /// [`zork::config_file::ZorkConfigFile`] doc-test #[derive(Deserialize, Debug, PartialEq, Clone, Default)] -#[serde (deny_unknown_fields)] +#[serde(deny_unknown_fields)] pub struct ExecutableAttribute<'a> { #[serde(borrow)] pub executable_name: Option<&'a str>, @@ -49,5 +51,7 @@ pub struct ExecutableAttribute<'a> { #[serde(borrow)] pub sources: Option>, #[serde(borrow)] + pub main: &'a str, + #[serde(borrow)] pub extra_args: Option>, } diff --git a/zork++/src/lib/config_file/modules.rs b/zork++/src/lib/config_file/modules.rs index 3f302918..e9b8f803 100644 --- a/zork++/src/lib/config_file/modules.rs +++ b/zork++/src/lib/config_file/modules.rs @@ -129,7 +129,7 @@ pub struct ModulesAttribute<'a> { /// assert_eq!(ifc_3.module_name, Some("math_part")); /// ``` #[derive(Deserialize, Debug, PartialEq)] -#[serde (deny_unknown_fields)] +#[serde(deny_unknown_fields)] pub struct ModuleInterface<'a> { #[serde(borrow)] pub file: &'a str, diff --git a/zork++/src/lib/config_file/tests.rs b/zork++/src/lib/config_file/tests.rs index 9e36733e..9d73f9af 100644 --- a/zork++/src/lib/config_file/tests.rs +++ b/zork++/src/lib/config_file/tests.rs @@ -17,6 +17,7 @@ use serde::*; /// test_executable_name = 'Zork++ tests' /// sources_base_path = 'path_test' /// sources = [ '*.cpp' ] +/// main = "tests_main.cpp" /// extra_args = ['extra_argument to run test'] ///"#; /// @@ -26,6 +27,7 @@ use serde::*; /// assert_eq!(config.test_executable_name, Some("Zork++ tests")); /// assert_eq!(config.sources_base_path, Some("path_test")); /// assert_eq!(config.sources, Some(vec!["*.cpp"])); +/// assert_eq!(config.main, "test_main.cpp"); /// assert_eq!(config.extra_args, Some(vec!["extra_argument to run test"])); /// /// ``` @@ -47,5 +49,7 @@ pub struct TestsAttribute<'a> { #[serde(borrow)] pub sources: Option>, #[serde(borrow)] + pub main: &'a str, + #[serde(borrow)] pub extra_args: Option>, } diff --git a/zork++/src/lib/lib.rs b/zork++/src/lib/lib.rs index 0a60f467..0ea32329 100644 --- a/zork++/src/lib/lib.rs +++ b/zork++/src/lib/lib.rs @@ -86,7 +86,7 @@ pub mod worker { Ok(()) } - /// Helper for reduce the cyclomatic complextity of the main fn. + /// Helper for reduce the cyclomatic complexity of the main fn. /// /// Contains the main calls to the generation of the compilers commands lines, /// the calls to the process that runs those ones, the autorun the generated @@ -104,13 +104,13 @@ pub mod worker { commands = build_project(program_data, read_only_cache, false) .with_context(|| "Failed to build project")?; - commands::run_generated_commands(program_data, commands, cache) + commands::run_generated_commands(program_data, commands, cache, false) } Command::Run => { commands = build_project(program_data, read_only_cache, false) .with_context(|| "Failed to build project")?; - match commands::run_generated_commands(program_data, commands, cache) { + match commands::run_generated_commands(program_data, commands, cache, false) { Ok(_) => autorun_generated_binary( &program_data.compiler.cpp_compiler, program_data.build.output_dir, @@ -123,7 +123,7 @@ pub mod worker { commands = build_project(program_data, read_only_cache, true) .with_context(|| "Failed to build project")?; - match commands::run_generated_commands(program_data, commands, cache) { + match commands::run_generated_commands(program_data, commands, cache, true) { Ok(_) => autorun_generated_binary( &program_data.compiler.cpp_compiler, program_data.build.output_dir, diff --git a/zork++/src/lib/project_model/executable.rs b/zork++/src/lib/project_model/executable.rs index 8310dc34..df16d550 100644 --- a/zork++/src/lib/project_model/executable.rs +++ b/zork++/src/lib/project_model/executable.rs @@ -2,6 +2,7 @@ use crate::{ bounds::{ExecutableTarget, ExtraArgs}, cli::output::arguments::Argument, }; +use std::path::Path; use super::sourceset::SourceSet; @@ -9,6 +10,7 @@ use super::sourceset::SourceSet; pub struct ExecutableModel<'a> { pub executable_name: &'a str, pub sourceset: SourceSet<'a>, + pub main: &'a Path, pub extra_args: Vec>, } diff --git a/zork++/src/lib/project_model/tests.rs b/zork++/src/lib/project_model/tests.rs index 39392794..dbd8b896 100644 --- a/zork++/src/lib/project_model/tests.rs +++ b/zork++/src/lib/project_model/tests.rs @@ -2,6 +2,7 @@ use crate::{ bounds::{ExecutableTarget, ExtraArgs}, cli::output::arguments::Argument, }; +use std::path::Path; use super::sourceset::SourceSet; @@ -9,6 +10,7 @@ use super::sourceset::SourceSet; pub struct TestsModel<'a> { pub test_executable_name: String, pub sourceset: SourceSet<'a>, + pub main: &'a Path, pub extra_args: Vec>, } diff --git a/zork++/src/lib/utils/reader.rs b/zork++/src/lib/utils/reader.rs index 999d19a5..d8b46710 100644 --- a/zork++/src/lib/utils/reader.rs +++ b/zork++/src/lib/utils/reader.rs @@ -161,6 +161,8 @@ fn assemble_executable_model<'a>( sources, }; + let main = Path::new(config.map_or("", |exe_attr| exe_attr.main)); + let extra_args = config .and_then(|exe| exe.extra_args.as_ref()) .map(|args| args.iter().map(|arg| Argument::from(*arg)).collect()) @@ -169,6 +171,7 @@ fn assemble_executable_model<'a>( ExecutableModel { executable_name, sourceset, + main, extra_args, } } @@ -297,6 +300,8 @@ fn assemble_tests_model<'a>( sources, }; + let main = Path::new(config.map_or("", |test_attr| test_attr.main)); + let extra_args = config .and_then(|test| test.extra_args.as_ref()) .map(|args| args.iter().map(|arg| Argument::from(*arg)).collect()) @@ -305,6 +310,7 @@ fn assemble_tests_model<'a>( TestsModel { test_executable_name, sourceset, + main, extra_args, } } @@ -354,6 +360,7 @@ mod test { base_path: Path::new("."), sources: vec![], }, + main: Path::new("main.cpp"), extra_args: vec![], }, modules: ModulesModel { @@ -369,6 +376,7 @@ mod test { base_path: Path::new("."), sources: vec![], }, + main: Path::new("main.cpp"), extra_args: vec![], }, }; @@ -404,6 +412,7 @@ mod test { base_path: Path::new("bin"), sources: vec![Source::Glob(GlobPattern("*.cpp"))], }, + main: Path::new("main.cpp"), extra_args: vec![Argument::from("-Werr")], }, modules: ModulesModel { @@ -441,6 +450,7 @@ mod test { base_path: Path::new("test"), sources: vec![Source::Glob(GlobPattern("*.cpp"))], }, + main: Path::new("main.cpp"), extra_args: vec![Argument::from("-pedantic")], }, }; diff --git a/zork++/src/lib/utils/template/resources/zork_example.toml b/zork++/src/lib/utils/template/resources/zork_example.toml index d0c04a0f..b4fa6bd0 100644 --- a/zork++/src/lib/utils/template/resources/zork_example.toml +++ b/zork++/src/lib/utils/template/resources/zork_example.toml @@ -2,6 +2,7 @@ [project] name = "" authors = [ "Zero Day Code" ] # Replace this for the real authors +compilation_db = true [compiler] cpp_compiler = "" @@ -16,13 +17,14 @@ executable_name = "" sources = [ "//*.cpp" ] -auto_execute = true +main = "main.cpp" [tests] tests_executable_name = "zork_proj_tests" sources = [ "//*.cpp" ] +main = "test_main.cpp" [modules] base_ifcs_dir = "//ifc" diff --git a/zork++/src/lib/utils/template/resources/zork_example_basic.toml b/zork++/src/lib/utils/template/resources/zork_example_basic.toml index 9358bd4f..b69f22a1 100644 --- a/zork++/src/lib/utils/template/resources/zork_example_basic.toml +++ b/zork++/src/lib/utils/template/resources/zork_example_basic.toml @@ -2,6 +2,7 @@ [project] name = "" authors = [ "Zero Day Code" ] # Replace this for the real authors +compilation_db = true [compiler] cpp_compiler = "" @@ -16,13 +17,14 @@ executable_name = "" sources = [ "//*.cpp" ] -auto_execute = true +main = "main.cpp" [tests] tests_executable_name = "zork_proj_tests" sources = [ "//*.cpp" ] +main = "test_main.cpp" [modules] base_ifcs_dir = "//ifc" From a0045409d380560f2faae2082f189fffd9ec0ce5 Mon Sep 17 00:00:00 2001 From: Alex Vergara Date: Sun, 5 Mar 2023 20:49:49 +0100 Subject: [PATCH 09/60] Caching how the files are flagged to avoid rebuild them. Now they are checked before process them. --- zork++/src/lib/cli/output/commands.rs | 6 --- zork++/src/lib/compiler/mod.rs | 57 ++++++++++++++++++--------- 2 files changed, 39 insertions(+), 24 deletions(-) diff --git a/zork++/src/lib/cli/output/commands.rs b/zork++/src/lib/cli/output/commands.rs index 3299679a..ba9a31a2 100644 --- a/zork++/src/lib/cli/output/commands.rs +++ b/zork++/src/lib/cli/output/commands.rs @@ -43,9 +43,6 @@ pub fn run_generated_commands( c_miu.path )); } - } else { - miu.execution_result = CommandExecutionResult::Cached; - log::trace!("Translation unit: {:?} was not modified since the last iteration. No need to rebuilt it again.", &miu.path); } } } @@ -68,9 +65,6 @@ pub fn run_generated_commands( c_miu.path )); } - } else { - implm.execution_result = CommandExecutionResult::Cached; - log::trace!("Translation unit: {:?} was not modified since the last iteration. No need to rebuilt it again.", implm.path); } } } diff --git a/zork++/src/lib/compiler/mod.rs b/zork++/src/lib/compiler/mod.rs index f83fc8ea..a8c4a42f 100644 --- a/zork++/src/lib/compiler/mod.rs +++ b/zork++/src/lib/compiler/mod.rs @@ -15,6 +15,8 @@ use crate::{ ZorkModel, }, }; +use crate::cli::output::commands::{CommandExecutionResult, ModuleCommandLine}; +use crate::compiler::helpers::flag_modules_without_changes; /// The entry point of the compilation process /// @@ -48,6 +50,8 @@ fn build_executable<'a>( commands: &'_ mut Commands<'a>, tests: bool, ) -> Result<()> { + // TODO Check if the command line is the same as the previous? If there's no new sources? + // And avoid reexecuting? if tests { sources::generate_main_command_line_args(model, commands, &model.tests) } else { @@ -83,7 +87,19 @@ fn prebuild_module_interfaces<'a>( commands: &mut Commands<'a>, ) { interfaces.iter().for_each(|module_interface| { - sources::generate_module_interfaces_args(model, cache, module_interface, commands); + if !flag_modules_without_changes(&model.compiler.cpp_compiler, cache, &module_interface.file) { + sources::generate_module_interfaces_args(model, module_interface, commands); + } else { + let mut command_line = ModuleCommandLine { + path: module_interface.file.clone(), + args: Vec::with_capacity(0), + processed: true, + execution_result: CommandExecutionResult::default(), + }; + command_line.execution_result = CommandExecutionResult::Cached; + log::trace!("Translation unit: {:?} was not modified since the last iteration. No need to rebuilt it again.", &module_interface.file); + commands.interfaces.push(command_line); + } }); } @@ -96,7 +112,19 @@ fn compile_module_implementations<'a>( commands: &mut Commands<'a>, ) { impls.iter().for_each(|module_impl| { - sources::generate_module_implementation_args(model, cache, module_impl, commands); + if !flag_modules_without_changes(&model.compiler.cpp_compiler, cache, &module_impl.file) { + sources::generate_module_implementation_args(model, module_impl, commands); + } else { + let mut command_line = ModuleCommandLine { + path: module_impl.file.clone(), + args: Vec::with_capacity(0), + processed: true, + execution_result: CommandExecutionResult::default(), + }; + command_line.execution_result = CommandExecutionResult::Cached; + log::trace!("Translation unit: {:?} was not modified since the last iteration. No need to rebuilt it again.", &module_impl.file); + commands.interfaces.push(command_line); + } }); } @@ -225,7 +253,6 @@ mod sources { /// Generates the expected arguments for precompile the BMIs depending on self pub fn generate_module_interfaces_args<'a>( model: &'a ZorkModel, - cache: &'a ZorkCache, interface: &'a ModuleInterfaceModel, commands: &mut Commands<'a>, ) { @@ -324,21 +351,10 @@ mod sources { } } - // Code on the if branch is provisional, until Clang implements `import std;` - let processed_cache = if compiler.eq(&CppCompiler::CLANG) { - log::debug!( - "Module unit {:?} will be rebuilt since we've detected that you are using Clang", - interface.module_name - ); - false - } else { - helpers::flag_modules_without_changes(cache, &interface.file) - }; - let command_line = ModuleCommandLine { path: interface.file.clone(), args: arguments, - processed: processed_cache, + processed: false, execution_result: CommandExecutionResult::default(), }; commands.interfaces.push(command_line); @@ -347,7 +363,6 @@ mod sources { /// Generates the expected arguments for compile the implementation module files pub fn generate_module_implementation_args<'a>( model: &'a ZorkModel, - cache: &'a ZorkCache, implementation: &'a ModuleImplementationModel, commands: &mut Commands<'a>, ) { @@ -432,7 +447,7 @@ mod sources { let command_line = ModuleCommandLine { path: implementation.file.clone(), args: arguments, - processed: helpers::flag_modules_without_changes(cache, &implementation.file), + processed: false, execution_result: CommandExecutionResult::default(), }; commands.implementations.push(command_line); @@ -562,6 +577,9 @@ mod helpers { }) .collect::>(); + // TODO Cached sys headers will deserve in a near future its own type, + // storing the commands and the paths there, and checking for rebuild them + // independently, also check for execution independently for collection_args in sys_modules { commands.interfaces.push(ModuleCommandLine { path: PathBuf::from(collection_args[4].value), @@ -578,7 +596,10 @@ mod helpers { /// hasn't been modified since the last build process iteration. /// /// True means already processed and previous iteration Success - pub(crate) fn flag_modules_without_changes(cache: &ZorkCache, file: &Path) -> bool { + pub(crate) fn flag_modules_without_changes(compiler: &CppCompiler, cache: &ZorkCache, file: &Path) -> bool { + if compiler.eq(&CppCompiler::CLANG) { + log::debug!("Module unit {file:?} will be rebuilt since we've detected that you are using Clang"); + } // Check first if the file is already on the cache, and if it's last iteration was successful if let Some(cached_file) = cache.is_file_cached(file) { if cached_file.execution_result != CommandExecutionResult::Success From ff61f1e50808ca796f812077f91ed702b52c0d7c Mon Sep 17 00:00:00 2001 From: Alex Vergara Date: Mon, 6 Mar 2023 10:29:28 +0100 Subject: [PATCH 10/60] Removing reference types for project_model::CppCompiler, which is cheaper to copy than pass it by reference (size = 1, align = 1) --- zork++/src/lib/cache/mod.rs | 7 +++---- zork++/src/lib/cli/output/arguments.rs | 6 +++--- zork++/src/lib/cli/output/commands.rs | 2 -- zork++/src/lib/compiler/mod.rs | 24 +++++++++++++++--------- 4 files changed, 21 insertions(+), 18 deletions(-) diff --git a/zork++/src/lib/cache/mod.rs b/zork++/src/lib/cache/mod.rs index 01802e01..8efe46fe 100644 --- a/zork++/src/lib/cache/mod.rs +++ b/zork++/src/lib/cache/mod.rs @@ -126,11 +126,10 @@ impl ZorkCache { commands: Commands<'_>, test_mode: bool, ) -> Result<()> { + self.save_generated_commands(&commands); if program_data.project.compilation_db { - // TODO Read prev changes and compare without regenerate map_generated_commands_to_compilation_db(program_data, &commands, test_mode)?; } - self.save_generated_commands(commands); if !(program_data.compiler.cpp_compiler == CppCompiler::MSVC) { self.compilers_metadata.system_modules = program_data @@ -144,7 +143,7 @@ impl ZorkCache { Ok(()) } - fn save_generated_commands(&mut self, commands: Commands<'_>) { + fn save_generated_commands(&mut self, commands: &Commands<'_>) { log::trace!("Storing in the cache the last generated command lines..."); self.generated_commands.compiler = commands.compiler; let process_no = if !self.generated_commands.details.is_empty() { @@ -195,7 +194,7 @@ impl ZorkCache { ); commands_details.main = MainCommandLineDetail { - files: commands.sources.sources_paths, + files: commands.sources.sources_paths.clone(), execution_result: commands.sources.execution_result.clone(), command: commands .sources diff --git a/zork++/src/lib/cli/output/arguments.rs b/zork++/src/lib/cli/output/arguments.rs index d5b3a398..044bbbbb 100644 --- a/zork++/src/lib/cli/output/arguments.rs +++ b/zork++/src/lib/cli/output/arguments.rs @@ -72,7 +72,7 @@ pub mod clang_args { Argument::from(format!( "-fmodule-map-file={}", out_dir - .join("../zork") + .join("zork") .join("intrinsics") .join("zork.modulemap") .display() @@ -93,9 +93,9 @@ pub mod clang_args { None } - pub(crate) fn add_direct_module_interfafces_dependencies( + pub(crate) fn add_direct_module_interfaces_dependencies( dependencies: &[&str], - compiler: &CppCompiler, + compiler: CppCompiler, out_dir: &Path, arguments: &mut Vec, ) { diff --git a/zork++/src/lib/cli/output/commands.rs b/zork++/src/lib/cli/output/commands.rs index ba9a31a2..7f87790b 100644 --- a/zork++/src/lib/cli/output/commands.rs +++ b/zork++/src/lib/cli/output/commands.rs @@ -55,11 +55,9 @@ pub fn run_generated_commands( let r = execute_command(&commands.compiler, &implm.args, &cache); implm.execution_result = CommandExecutionResult::from(&r); if let Err(e) = r { - cache::save(program_data, cache, commands, test_mode)?; return Err(e); } else if !r.as_ref().unwrap().success() { let c_miu = implm.clone(); - cache::save(program_data, cache, commands, test_mode)?; return Err(eyre!( "Ending the program, because the build of: {:?} wasn't ended successfully", c_miu.path diff --git a/zork++/src/lib/compiler/mod.rs b/zork++/src/lib/compiler/mod.rs index a8c4a42f..f19bcef8 100644 --- a/zork++/src/lib/compiler/mod.rs +++ b/zork++/src/lib/compiler/mod.rs @@ -99,6 +99,9 @@ fn prebuild_module_interfaces<'a>( command_line.execution_result = CommandExecutionResult::Cached; log::trace!("Translation unit: {:?} was not modified since the last iteration. No need to rebuilt it again.", &module_interface.file); commands.interfaces.push(command_line); + commands.generated_files_paths.push(Argument::from(helpers::generate_prebuild_miu( + model.compiler.cpp_compiler, model.build.output_dir, module_interface + ))) } }); } @@ -124,6 +127,9 @@ fn compile_module_implementations<'a>( command_line.execution_result = CommandExecutionResult::Cached; log::trace!("Translation unit: {:?} was not modified since the last iteration. No need to rebuilt it again.", &module_impl.file); commands.interfaces.push(command_line); + commands.generated_files_paths.push(Argument::from(helpers::generate_impl_obj_file( + model.compiler.cpp_compiler, model.build.output_dir, module_impl + ))) } }); } @@ -256,13 +262,13 @@ mod sources { interface: &'a ModuleInterfaceModel, commands: &mut Commands<'a>, ) { - let compiler = &model.compiler.cpp_compiler; + let compiler = model.compiler.cpp_compiler; let out_dir = model.build.output_dir; let mut arguments = Vec::with_capacity(8); arguments.push(model.compiler.language_level_arg()); - match *compiler { + match compiler { CppCompiler::CLANG => { arguments.extend(clang_args::add_std_lib(model)); arguments.push(Argument::from("-fimplicit-modules")); @@ -276,7 +282,7 @@ mod sources { "-fprebuilt-module-path={}/clang/modules/interfaces", out_dir.display() ))); - clang_args::add_direct_module_interfafces_dependencies( + clang_args::add_direct_module_interfaces_dependencies( &interface.dependencies, compiler, out_dir, @@ -366,13 +372,13 @@ mod sources { implementation: &'a ModuleImplementationModel, commands: &mut Commands<'a>, ) { - let compiler = &model.compiler.cpp_compiler; + let compiler = model.compiler.cpp_compiler; let out_dir = model.build.output_dir; let mut arguments = Vec::with_capacity(12); arguments.push(model.compiler.language_level_arg()); - match *compiler { + match compiler { CppCompiler::CLANG => { arguments.extend(clang_args::add_std_lib(model)); arguments.push(Argument::from("-fimplicit-modules")); @@ -389,7 +395,7 @@ mod sources { commands.generated_files_paths.push(obj_file_path.clone()); arguments.push(obj_file_path); - clang_args::add_direct_module_interfafces_dependencies( + clang_args::add_direct_module_interfaces_dependencies( &implementation.dependencies, compiler, out_dir, @@ -476,7 +482,7 @@ mod helpers { /// `export module dotted.module`, in Clang, due to the expected `.pcm` extension, the final path /// will be generated as `dotted.pcm`, instead `dotted.module.pcm`. pub(crate) fn generate_prebuild_miu( - compiler: &CppCompiler, + compiler: CppCompiler, out_dir: &Path, interface: &ModuleInterfaceModel, ) -> PathBuf { @@ -509,7 +515,7 @@ mod helpers { } pub(crate) fn generate_impl_obj_file( - compiler: &CppCompiler, + compiler: CppCompiler, out_dir: &Path, implementation: &ModuleImplementationModel, ) -> PathBuf { @@ -598,7 +604,7 @@ mod helpers { /// True means already processed and previous iteration Success pub(crate) fn flag_modules_without_changes(compiler: &CppCompiler, cache: &ZorkCache, file: &Path) -> bool { if compiler.eq(&CppCompiler::CLANG) { - log::debug!("Module unit {file:?} will be rebuilt since we've detected that you are using Clang"); + log::trace!("Module unit {file:?} will be rebuilt since we've detected that you are using Clang"); } // Check first if the file is already on the cache, and if it's last iteration was successful if let Some(cached_file) = cache.is_file_cached(file) { From a8b98984fa7d93c61887214cda3843d23b1715f4 Mon Sep 17 00:00:00 2001 From: Alex Vergara Date: Mon, 6 Mar 2023 10:44:40 +0100 Subject: [PATCH 11/60] Solved the need of MSVC for storing the output generated files paths as obj files, and not as .ifc files, in order to save the commands for the compiler --- zork++/src/lib/compiler/mod.rs | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/zork++/src/lib/compiler/mod.rs b/zork++/src/lib/compiler/mod.rs index f19bcef8..3fe7ec69 100644 --- a/zork++/src/lib/compiler/mod.rs +++ b/zork++/src/lib/compiler/mod.rs @@ -315,17 +315,12 @@ mod sources { arguments.push(Argument::from("/ifcSearchDir")); arguments.push(implicit_lookup_mius_path.clone().into()); arguments.push(Argument::from("/ifcOutput")); - arguments.push(implicit_lookup_mius_path.clone().into()); + arguments.push(implicit_lookup_mius_path.into()); // The output .obj file - let obj_file = format!( - "{}", - Path::new(&implicit_lookup_mius_path) - .join(interface.filestem()) - .with_extension(compiler.get_obj_file_extension()) - .display() - ); - commands.generated_files_paths.push(obj_file.clone().into()); + let obj_file = + Argument::from(helpers::generate_prebuild_miu(compiler, out_dir, interface)); + commands.generated_files_paths.push(obj_file.clone()); arguments.push(Argument::from(format!("/Fo{obj_file}"))); if let Some(partition) = &interface.partition { @@ -481,6 +476,10 @@ mod helpers { /// dots in their module identifier declaration. So, for example, a module with a declaration of: /// `export module dotted.module`, in Clang, due to the expected `.pcm` extension, the final path /// will be generated as `dotted.pcm`, instead `dotted.module.pcm`. + /// + /// For MSVC, we are relying in the autogenerate of the BMI automatically by the compiler, + /// so the output file that we need is an obj file (.obj), and not the + /// binary module interface (.ifc) pub(crate) fn generate_prebuild_miu( compiler: CppCompiler, out_dir: &Path, @@ -510,7 +509,9 @@ mod helpers { .join("interfaces") .join(format!( "{mod_unit}.{}", - compiler.get_typical_bmi_extension() + if compiler.eq(&CppCompiler::MSVC) { + compiler.get_obj_file_extension() + } else { compiler.get_typical_bmi_extension() } )) } From 7029d16954dc84b88b0d95dddefda8051a467f3c Mon Sep 17 00:00:00 2001 From: Alex Vergara Date: Mon, 6 Mar 2023 15:23:01 +0100 Subject: [PATCH 12/60] WIP - Working with absolute paths since the creation of the project model --- zork++/src/lib/bounds/mod.rs | 27 +++++----- zork++/src/lib/cli/output/arguments.rs | 7 +++ zork++/src/lib/compiler/mod.rs | 31 +++++------ zork++/src/lib/project_model/modules.rs | 40 ++++++++++----- zork++/src/lib/utils/constants.rs | 3 ++ zork++/src/lib/utils/fs.rs | 26 ++++++++-- zork++/src/lib/utils/reader.rs | 68 ++++++++++++++----------- 7 files changed, 123 insertions(+), 79 deletions(-) diff --git a/zork++/src/lib/bounds/mod.rs b/zork++/src/lib/bounds/mod.rs index ce17c255..15cae1ef 100644 --- a/zork++/src/lib/bounds/mod.rs +++ b/zork++/src/lib/bounds/mod.rs @@ -1,5 +1,6 @@ use core::fmt::Debug; use std::{fmt::Display, path::Path}; +use std::path::PathBuf; use crate::{cli::output::arguments::Argument, project_model::sourceset::SourceSet}; @@ -17,31 +18,27 @@ pub trait ExecutableTarget<'a>: ExtraArgs<'a> { /// Represents any kind of translation unit and the generic operations /// applicable to all the implementors pub trait TranslationUnit: Display + Debug { + /// Returns the file, being the addition of the path property plus the extension property + fn file(&self) -> PathBuf; + /// Outputs the declared path for `self`, being self the translation unit - fn file(&self) -> &Path; + fn path(&self) -> PathBuf; + + /// Outputs the declared extension for `self` + fn extension(&self) -> String; - fn filestem(&self) -> &str { - self.file() + fn filestem(&self) -> String { + self.path() .file_stem() .unwrap_or_else(|| { panic!( "Unexpected error getting the filename of {:?}", - self.file().as_os_str() + self.path().with_extension(self.extension()) ) }) .to_str() .unwrap() + .to_string() } } -impl TranslationUnit for &str { - fn file(&self) -> &Path { - Path::new(self) - } -} - -impl TranslationUnit for String { - fn file(&self) -> &Path { - Path::new(self) - } -} diff --git a/zork++/src/lib/cli/output/arguments.rs b/zork++/src/lib/cli/output/arguments.rs index 044bbbbb..322e451f 100644 --- a/zork++/src/lib/cli/output/arguments.rs +++ b/zork++/src/lib/cli/output/arguments.rs @@ -2,6 +2,7 @@ //! or collections of command line arguments use std::{borrow::Borrow, ffi::OsStr, path::PathBuf}; +use std::path::Path; use serde::{Deserialize, Serialize}; @@ -25,6 +26,12 @@ impl<'a> From for Argument<'a> { } } +impl<'a> From<&'a Path> for Argument<'a> { + fn from(value: &'a Path) -> Self { + Self::from(format!("{}", value.display())) + } +} + impl<'a> From for Argument<'a> { fn from(value: PathBuf) -> Self { Self::from(format!("{}", value.display())) diff --git a/zork++/src/lib/compiler/mod.rs b/zork++/src/lib/compiler/mod.rs index 3fe7ec69..0d6893f5 100644 --- a/zork++/src/lib/compiler/mod.rs +++ b/zork++/src/lib/compiler/mod.rs @@ -15,6 +15,7 @@ use crate::{ ZorkModel, }, }; +use crate::bounds::TranslationUnit; use crate::cli::output::commands::{CommandExecutionResult, ModuleCommandLine}; use crate::compiler::helpers::flag_modules_without_changes; @@ -87,17 +88,17 @@ fn prebuild_module_interfaces<'a>( commands: &mut Commands<'a>, ) { interfaces.iter().for_each(|module_interface| { - if !flag_modules_without_changes(&model.compiler.cpp_compiler, cache, &module_interface.file) { + if !flag_modules_without_changes(&model.compiler.cpp_compiler, cache, &module_interface.file()) { sources::generate_module_interfaces_args(model, module_interface, commands); } else { let mut command_line = ModuleCommandLine { - path: module_interface.file.clone(), + path: module_interface.file().to_path_buf(), args: Vec::with_capacity(0), processed: true, execution_result: CommandExecutionResult::default(), }; command_line.execution_result = CommandExecutionResult::Cached; - log::trace!("Translation unit: {:?} was not modified since the last iteration. No need to rebuilt it again.", &module_interface.file); + log::trace!("Translation unit: {:?} was not modified since the last iteration. No need to rebuilt it again.", &module_interface.file()); commands.interfaces.push(command_line); commands.generated_files_paths.push(Argument::from(helpers::generate_prebuild_miu( model.compiler.cpp_compiler, model.build.output_dir, module_interface @@ -115,17 +116,17 @@ fn compile_module_implementations<'a>( commands: &mut Commands<'a>, ) { impls.iter().for_each(|module_impl| { - if !flag_modules_without_changes(&model.compiler.cpp_compiler, cache, &module_impl.file) { + if !flag_modules_without_changes(&model.compiler.cpp_compiler, cache, &module_impl.file()) { sources::generate_module_implementation_args(model, module_impl, commands); } else { let mut command_line = ModuleCommandLine { - path: module_impl.file.clone(), + path: module_impl.file().to_path_buf(), args: Vec::with_capacity(0), processed: true, execution_result: CommandExecutionResult::default(), }; command_line.execution_result = CommandExecutionResult::Cached; - log::trace!("Translation unit: {:?} was not modified since the last iteration. No need to rebuilt it again.", &module_impl.file); + log::trace!("Translation unit: {:?} was not modified since the last iteration. No need to rebuilt it again.", &module_impl.file()); commands.interfaces.push(command_line); commands.generated_files_paths.push(Argument::from(helpers::generate_impl_obj_file( model.compiler.cpp_compiler, model.build.output_dir, module_impl @@ -297,7 +298,7 @@ mod sources { commands.generated_files_paths.push(miu_file_path.clone()); arguments.push(miu_file_path); // The input file - arguments.push(Argument::from(&interface.file)); + arguments.push(Argument::from(interface.file())); } CppCompiler::MSVC => { arguments.push(Argument::from("/EHsc")); @@ -334,7 +335,7 @@ mod sources { } arguments.push(Argument::from("/TP")); // The input file - arguments.push(Argument::from(&interface.file)) + arguments.push(Argument::from(interface.file())) } CppCompiler::GCC => { arguments.push(Argument::from("-fmodules-ts")); @@ -342,7 +343,7 @@ mod sources { arguments.push(Argument::from("c++")); arguments.push(Argument::from("-c")); // The input file - arguments.push(Argument::from(&interface.file)); + arguments.push(Argument::from(interface.file())); // The output file arguments.push(Argument::from("-o")); let miu_file_path = @@ -353,7 +354,7 @@ mod sources { } let command_line = ModuleCommandLine { - path: interface.file.clone(), + path: interface.file().to_path_buf(), args: arguments, processed: false, execution_result: CommandExecutionResult::default(), @@ -398,7 +399,7 @@ mod sources { ); // The input file - arguments.push(Argument::from(&implementation.file)) + arguments.push(Argument::from(implementation.file())) } CppCompiler::MSVC => { arguments.push(Argument::from("/EHsc")); @@ -414,7 +415,7 @@ mod sources { .join("interfaces"), )); // The input file - arguments.push(Argument::from(&implementation.file)); + arguments.push(Argument::from(implementation.file())); // The output .obj file let obj_file_path = out_dir .join(compiler.as_ref()) @@ -432,7 +433,7 @@ mod sources { arguments.push(Argument::from("-fmodules-ts")); arguments.push(Argument::from("-c")); // The input file - arguments.push(Argument::from(&implementation.file)); + arguments.push(Argument::from(implementation.file())); // The output file arguments.push(Argument::from("-o")); let obj_file_path = Argument::from(helpers::generate_impl_obj_file( @@ -446,7 +447,7 @@ mod sources { } let command_line = ModuleCommandLine { - path: implementation.file.clone(), + path: implementation.file().to_path_buf(), args: arguments, processed: false, execution_result: CommandExecutionResult::default(), @@ -493,7 +494,7 @@ mod helpers { if !partition.partition_name.is_empty() { temp.push_str(partition.partition_name) } else { - temp.push_str(interface.filestem()) + temp.push_str(&interface.filestem()) } } else { temp.push_str(interface.module_name) diff --git a/zork++/src/lib/project_model/modules.rs b/zork++/src/lib/project_model/modules.rs index 1fda348a..45f452e5 100644 --- a/zork++/src/lib/project_model/modules.rs +++ b/zork++/src/lib/project_model/modules.rs @@ -14,7 +14,8 @@ pub struct ModulesModel<'a> { #[derive(Debug, PartialEq, Eq)] pub struct ModuleInterfaceModel<'a> { - pub file: PathBuf, + pub path: PathBuf, + pub extension: String, pub module_name: &'a str, pub partition: Option>, pub dependencies: Vec<&'a str>, @@ -24,22 +25,30 @@ impl<'a> fmt::Display for ModuleInterfaceModel<'a> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!( f, - "({:?}, {:?}, {:?}, {:?})", - self.file, self.module_name, self.dependencies, self.partition + "({:?}.{:?}., {:?}, {:?}, {:?})", + self.path, self.extension, self.module_name, self.dependencies, self.partition ) } } impl<'a> TranslationUnit for ModuleInterfaceModel<'a> { - fn file(&self) -> &Path { - &self.file + fn file(&self) -> PathBuf { + self.path.with_extension(self.extension.clone()) } + fn path(&self) -> PathBuf { + self.path.clone() + } + fn extension(&self) -> String { self.extension.to_string() } } impl<'a> TranslationUnit for &'a ModuleInterfaceModel<'a> { - fn file(&self) -> &Path { - &self.file + fn file(&self) -> PathBuf { + self.path.with_extension(self.extension.clone()) + } + fn path(&self) -> PathBuf { + self.path.clone() } + fn extension(&self) -> String { self.extension.to_string() } } #[derive(Debug, PartialEq, Eq)] @@ -61,24 +70,29 @@ impl<'a> From<&ModulePartition<'a>> for ModulePartitionModel<'a> { #[derive(Debug, PartialEq, Eq)] pub struct ModuleImplementationModel<'a> { - pub file: PathBuf, + pub path: PathBuf, + pub extension: String, pub dependencies: Vec<&'a str>, } impl<'a> fmt::Display for ModuleImplementationModel<'a> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "({:?}, {:?})", self.file, self.dependencies) + write!(f, "({:?}, {:?})", self.path, self.dependencies) } } impl<'a> TranslationUnit for ModuleImplementationModel<'a> { - fn file(&self) -> &Path { - &self.file + fn file(&self) -> PathBuf { + self.path.with_extension(self.extension.clone()) } + fn path(&self) -> PathBuf { self.path.clone() } + fn extension(&self) -> String { self.extension.to_string() } } impl<'a> TranslationUnit for &'a ModuleImplementationModel<'a> { - fn file(&self) -> &Path { - &self.file + fn file(&self) -> PathBuf { + self.path.with_extension(self.extension.clone()) } + fn path(&self) -> PathBuf { self.path.clone() } + fn extension(&self) -> String { self.extension.to_string() } } diff --git a/zork++/src/lib/utils/constants.rs b/zork++/src/lib/utils/constants.rs index 12501fbe..620cf8b2 100644 --- a/zork++/src/lib/utils/constants.rs +++ b/zork++/src/lib/utils/constants.rs @@ -22,6 +22,7 @@ pub const CONFIG_FILE_MOCK: &str = r#" [project] name = "Zork++" authors = ["zerodaycode.gz@gmail.com"] +compilation_db = true [compiler] cpp_compiler = "clang" @@ -39,6 +40,7 @@ sources = [ "*.cpp" ] extra_args = [ "-Werr" ] +main = "main.cpp" [tests] test_executable_name = "zork_check" @@ -46,6 +48,7 @@ sources_base_path = "test" sources = [ "*.cpp" ] +main = "test_main.cpp" extra_args = [ "-pedantic" ] [modules] diff --git a/zork++/src/lib/utils/fs.rs b/zork++/src/lib/utils/fs.rs index 4a747a6d..ca0e33b2 100644 --- a/zork++/src/lib/utils/fs.rs +++ b/zork++/src/lib/utils/fs.rs @@ -1,10 +1,9 @@ -use std::{ - fs::{DirBuilder, File}, - io::{BufReader, Write}, - path::Path, -}; +use std::{env, fs::{DirBuilder, File}, fs, io::{BufReader, Write}, path::Path}; +use std::path::PathBuf; use color_eyre::{eyre::Context, Result}; +use color_eyre::eyre::ContextCompat; +use color_eyre::owo_colors::OwoColorize; use serde::{Deserialize, Serialize}; use super::constants; @@ -25,6 +24,23 @@ pub fn create_directory(path_create: &Path) -> Result<()> { .with_context(|| format!("Could not create directory {path_create:?}")) } +/// Gets the absolute route for an element in the system given a path P, +/// without the extension is P belongs to a file +pub fn get_absolute_path>(p: P) -> Result { + let mut canonical = p.as_ref().canonicalize().with_context(|| format!("Error getting the canonical path for: {:?}", p.as_ref()))?; + if cfg!(target_os = "windows") { + canonical = canonical.to_str().map(|unc| &unc[4..]).unwrap_or_default().into() + } + let file_stem = canonical.file_stem().with_context(|| format!("Unable to get the file stem for {:?}", p.as_ref()))?; + Ok(canonical.join(file_stem)) +} + +/// Returns the declared extension for a file, if exists +#[inline(always)] +pub fn get_file_extension>(p: P) -> String { + p.as_ref().extension().and_then(|ext| ext.to_str()).unwrap_or_default().to_string() +} + pub fn serialize_object_to_file(path: &Path, data: &T) -> Result<()> where T: Serialize, diff --git a/zork++/src/lib/utils/reader.rs b/zork++/src/lib/utils/reader.rs index d8b46710..13d59cac 100644 --- a/zork++/src/lib/utils/reader.rs +++ b/zork++/src/lib/utils/reader.rs @@ -1,27 +1,23 @@ -use crate::{ - cli::output::arguments::Argument, - config_file::{ - build::BuildAttribute, - compiler::CompilerAttribute, - executable::ExecutableAttribute, - modules::{ModuleImplementation, ModuleInterface, ModulesAttribute}, - project::ProjectAttribute, - tests::TestsAttribute, - ZorkConfigFile, +use crate::{cli::output::arguments::Argument, config_file::{ + build::BuildAttribute, + compiler::CompilerAttribute, + executable::ExecutableAttribute, + modules::{ModuleImplementation, ModuleInterface, ModulesAttribute}, + project::ProjectAttribute, + tests::TestsAttribute, + ZorkConfigFile, +}, project_model::{ + build::BuildModel, + compiler::CompilerModel, + executable::ExecutableModel, + modules::{ + ModuleImplementationModel, ModuleInterfaceModel, ModulePartitionModel, ModulesModel, }, - project_model::{ - build::BuildModel, - compiler::CompilerModel, - executable::ExecutableModel, - modules::{ - ModuleImplementationModel, ModuleInterfaceModel, ModulePartitionModel, ModulesModel, - }, - project::ProjectModel, - sourceset::{GlobPattern, Source, SourceSet}, - tests::TestsModel, - ZorkModel, - }, -}; + project::ProjectModel, + sourceset::{GlobPattern, Source, SourceSet}, + tests::TestsModel, + ZorkModel, +}, utils}; use color_eyre::{eyre::eyre, Result}; use std::path::{Path, PathBuf}; use walkdir::{DirEntry, WalkDir}; @@ -221,8 +217,9 @@ fn assemble_modules_model<'a>(config: &'a Option) -> ModulesMo fn assemble_module_interface_model<'a>( config: &'a ModuleInterface, - base_path: &str, + base_path: &str ) -> ModuleInterfaceModel<'a> { + let file_path = Path::new(base_path).join(config.file); let module_name = config.module_name.unwrap_or_else(|| { Path::new(config.file) .file_stem() @@ -241,7 +238,8 @@ fn assemble_module_interface_model<'a>( }; ModuleInterfaceModel { - file: Path::new(base_path).join(config.file), + path: utils::fs::get_absolute_path(&file_path).expect("TODO Propagate error on get path"), + extension: utils::fs::get_file_extension(&file_path), module_name, partition, dependencies, @@ -252,6 +250,7 @@ fn assemble_module_implementation_model<'a>( config: &'a ModuleImplementation, base_path: &str, ) -> ModuleImplementationModel<'a> { + let file_path = Path::new(base_path).join(config.file); let mut dependencies = config.dependencies.clone().unwrap_or_default(); if dependencies.is_empty() { let last_dot_index = config.file.rfind('.'); @@ -264,7 +263,8 @@ fn assemble_module_implementation_model<'a>( } ModuleImplementationModel { - file: Path::new(base_path).join(config.file), + path: utils::fs::get_absolute_path(&file_path).expect("TODO Propagate error on get path impl"), + extension: utils::fs::get_file_extension(&file_path), dependencies, } } @@ -317,6 +317,7 @@ fn assemble_tests_model<'a>( #[cfg(test)] mod test { + use std::env; use crate::{ project_model::compiler::{CppCompiler, LanguageLevel, StdLib}, utils, @@ -387,6 +388,7 @@ mod test { } #[test] + #[ignore] // TODO ignoring for now since we're trying to canonicalize since the assemble of the project model fn test_project_model_with_full_config() -> Result<()> { let config: ZorkConfigFile = toml::from_str(utils::constants::CONFIG_FILE_MOCK)?; let model = build_model(&config); @@ -395,7 +397,7 @@ mod test { project: ProjectModel { name: "Zork++", authors: &["zerodaycode.gz@gmail.com"], - compilation_db: false, + compilation_db: true, }, compiler: CompilerModel { cpp_compiler: CppCompiler::CLANG, @@ -419,13 +421,15 @@ mod test { base_ifcs_dir: Path::new("ifc"), interfaces: vec![ ModuleInterfaceModel { - file: PathBuf::from(Path::new("math.cppm")), + path: env::current_dir().unwrap().join("ifc\\math"), + extension: String::from("cppm"), module_name: "math", partition: None, dependencies: vec![], }, ModuleInterfaceModel { - file: PathBuf::from(Path::new("some_module.cppm")), + path: env::current_dir().unwrap().join("ifc\\some_module"), + extension: String::from("cppm"), module_name: "math", partition: None, dependencies: vec![], @@ -434,11 +438,13 @@ mod test { base_impls_dir: Path::new("src"), implementations: vec![ ModuleImplementationModel { - file: PathBuf::from(Path::new("math.cpp")), + path: env::current_dir().unwrap().join("\\src\\math"), + extension: String::from("cpp"), dependencies: vec!["math"], }, ModuleImplementationModel { - file: PathBuf::from(Path::new("some_module_impl.cpp")), + path: env::current_dir().unwrap().join("\\ifc\\some_module_impl"), + extension: String::from("cpp"), dependencies: vec!["iostream"], }, ], From 80d2c62cbce0f003ac045ef33f523eb3d3b0cdec Mon Sep 17 00:00:00 2001 From: Alex Vergara Date: Mon, 6 Mar 2023 15:56:52 +0100 Subject: [PATCH 13/60] Rework of the translation unit trait --- zork++/src/lib/compiler/mod.rs | 6 +++--- zork++/src/lib/project_model/modules.rs | 5 ++++- zork++/src/lib/utils/fs.rs | 4 +++- 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/zork++/src/lib/compiler/mod.rs b/zork++/src/lib/compiler/mod.rs index 0d6893f5..0b651766 100644 --- a/zork++/src/lib/compiler/mod.rs +++ b/zork++/src/lib/compiler/mod.rs @@ -92,7 +92,7 @@ fn prebuild_module_interfaces<'a>( sources::generate_module_interfaces_args(model, module_interface, commands); } else { let mut command_line = ModuleCommandLine { - path: module_interface.file().to_path_buf(), + path: module_interface.file(), args: Vec::with_capacity(0), processed: true, execution_result: CommandExecutionResult::default(), @@ -120,7 +120,7 @@ fn compile_module_implementations<'a>( sources::generate_module_implementation_args(model, module_impl, commands); } else { let mut command_line = ModuleCommandLine { - path: module_impl.file().to_path_buf(), + path: module_impl.file(), args: Vec::with_capacity(0), processed: true, execution_result: CommandExecutionResult::default(), @@ -447,7 +447,7 @@ mod sources { } let command_line = ModuleCommandLine { - path: implementation.file().to_path_buf(), + path: implementation.file(), args: arguments, processed: false, execution_result: CommandExecutionResult::default(), diff --git a/zork++/src/lib/project_model/modules.rs b/zork++/src/lib/project_model/modules.rs index 45f452e5..ad7c83cd 100644 --- a/zork++/src/lib/project_model/modules.rs +++ b/zork++/src/lib/project_model/modules.rs @@ -33,7 +33,10 @@ impl<'a> fmt::Display for ModuleInterfaceModel<'a> { impl<'a> TranslationUnit for ModuleInterfaceModel<'a> { fn file(&self) -> PathBuf { - self.path.with_extension(self.extension.clone()) + let mut tmp = self.path.clone().into_os_string(); + tmp.push("."); + tmp.push(self.extension.clone()); + PathBuf::from(tmp) } fn path(&self) -> PathBuf { self.path.clone() diff --git a/zork++/src/lib/utils/fs.rs b/zork++/src/lib/utils/fs.rs index ca0e33b2..dfb5a7c6 100644 --- a/zork++/src/lib/utils/fs.rs +++ b/zork++/src/lib/utils/fs.rs @@ -32,7 +32,9 @@ pub fn get_absolute_path>(p: P) -> Result { canonical = canonical.to_str().map(|unc| &unc[4..]).unwrap_or_default().into() } let file_stem = canonical.file_stem().with_context(|| format!("Unable to get the file stem for {:?}", p.as_ref()))?; - Ok(canonical.join(file_stem)) + let r = Ok(canonical.parent().unwrap_or_else(|| panic!("Unexpected error getting the parent of {:?}", p.as_ref())).join(file_stem)); + println!("Generated file: {:?}, file stem: {file_stem:?}, and canonical: {canonical:?}", &r); + r } /// Returns the declared extension for a file, if exists From 453b69c7b58cbfec2a4d5d589d6d6c1e67689cee Mon Sep 17 00:00:00 2001 From: Alex Vergara Date: Tue, 7 Mar 2023 18:08:46 +0100 Subject: [PATCH 14/60] WIP - Refactoring the whole flux, since the initial parsing to the generation of the cache/database compilation --- zork++/src/lib/bounds/mod.rs | 26 ++- zork++/src/lib/cache/mod.rs | 218 +++++++++------------ zork++/src/lib/cli/output/commands.rs | 44 ++++- zork++/src/lib/compiler/mod.rs | 55 +++--- zork++/src/lib/lib.rs | 2 +- zork++/src/lib/project_model/executable.rs | 6 +- zork++/src/lib/project_model/modules.rs | 71 +++++-- zork++/src/lib/project_model/tests.rs | 6 +- zork++/src/lib/utils/fs.rs | 21 +- zork++/src/lib/utils/reader.rs | 48 +++-- 10 files changed, 275 insertions(+), 222 deletions(-) diff --git a/zork++/src/lib/bounds/mod.rs b/zork++/src/lib/bounds/mod.rs index 15cae1ef..ef17819c 100644 --- a/zork++/src/lib/bounds/mod.rs +++ b/zork++/src/lib/bounds/mod.rs @@ -1,6 +1,6 @@ use core::fmt::Debug; -use std::{fmt::Display, path::Path}; -use std::path::PathBuf; +use std::{fmt::Display}; +use std::path::{Path, PathBuf}; use crate::{cli::output::arguments::Argument, project_model::sourceset::SourceSet}; @@ -12,33 +12,29 @@ pub trait ExtraArgs<'a> { pub trait ExecutableTarget<'a>: ExtraArgs<'a> { fn name(&'a self) -> &'a str; + fn entry_point(&'a self) -> &'a Path; fn sourceset(&'a self) -> &'a SourceSet<'a>; } /// Represents any kind of translation unit and the generic operations /// applicable to all the implementors pub trait TranslationUnit: Display + Debug { - /// Returns the file, being the addition of the path property plus the extension property + /// Returns the file, being the addition of the path property plus the file stem plus + /// the extension property fn file(&self) -> PathBuf; /// Outputs the declared path for `self`, being self the translation unit fn path(&self) -> PathBuf; + /// Outputs the declared file stem for this translation unit + fn file_stem(&self) -> String; + /// Outputs the declared extension for `self` fn extension(&self) -> String; - fn filestem(&self) -> String { - self.path() - .file_stem() - .unwrap_or_else(|| { - panic!( - "Unexpected error getting the filename of {:?}", - self.path().with_extension(self.extension()) - ) - }) - .to_str() - .unwrap() - .to_string() + /// Outputs the file stem concatenated with the extension for a given tu + fn file_with_extension(&self) -> String { + format!("{}.{}", self.file_stem(), self.extension()) } } diff --git a/zork++/src/lib/cache/mod.rs b/zork++/src/lib/cache/mod.rs index 8efe46fe..a364b7ad 100644 --- a/zork++/src/lib/cache/mod.rs +++ b/zork++/src/lib/cache/mod.rs @@ -7,6 +7,7 @@ use std::{ fs::File, path::{Path, PathBuf}, }; +use std::collections::HashMap; use walkdir::WalkDir; use crate::utils::constants::COMPILATION_DATABASE; @@ -22,6 +23,7 @@ use crate::{ }, }; use serde::{Deserialize, Serialize}; +use crate::cli::output::arguments::Argument; /// Standalone utility for retrieve the Zork++ cache file pub fn load(program_data: &ZorkModel<'_>, cli_args: &CliArgs) -> Result { @@ -36,8 +38,8 @@ pub fn load(program_data: &ZorkModel<'_>, cli_args: &CliArgs) -> Result, pub compilers_metadata: CompilersMetadata, - pub generated_commands: CachedCommands, + pub generated_commands: Vec, + pub last_generated_commands: HashMap> } impl ZorkCache { /// Returns a [`Option`] of [`CommandDetails`] if the file is persisted already in the cache - pub fn is_file_cached(&self, path: &Path) -> Option<&CommandDetail> { - let last_iteration_details = self.generated_commands.details.last(); + pub fn is_file_cached(&self, path: impl AsRef) -> Option<&CommandDetail> { + let last_iteration_details = self.generated_commands.last(); if let Some(last_iteration) = last_iteration_details { - let found_as_ifc = last_iteration.interfaces.iter().find(|f| { - path.to_str() - .unwrap_or_default() - .contains(&f.translation_unit) + let found_as_ifc = last_iteration.interfaces.iter().find(|comm_det| { + comm_det.file_path().eq(path.as_ref()) }); if found_as_ifc.is_some() { @@ -96,7 +98,7 @@ impl ZorkCache { let found_as_impl = last_iteration .implementations .iter() - .find(|f| path.to_str().unwrap_or_default().eq(&f.translation_unit)); + .find(|comm_det| comm_det.file_path().eq(path.as_ref())); if found_as_impl.is_some() { return found_as_impl; @@ -126,9 +128,8 @@ impl ZorkCache { commands: Commands<'_>, test_mode: bool, ) -> Result<()> { - self.save_generated_commands(&commands); - if program_data.project.compilation_db { - map_generated_commands_to_compilation_db(program_data, &commands, test_mode)?; + if self.save_generated_commands(&commands) && program_data.project.compilation_db { + map_generated_commands_to_compilation_db(self)?; } if !(program_data.compiler.cpp_compiler == CppCompiler::MSVC) { @@ -143,12 +144,13 @@ impl ZorkCache { Ok(()) } - fn save_generated_commands(&mut self, commands: &Commands<'_>) { + fn save_generated_commands(&mut self, commands: &Commands<'_>) -> bool { log::trace!("Storing in the cache the last generated command lines..."); - self.generated_commands.compiler = commands.compiler; - let process_no = if !self.generated_commands.details.is_empty() { + let mut has_changes = false; + + self.compiler = commands.compiler; + let process_no = if !self.generated_commands.is_empty() { self.generated_commands - .details .last() .unwrap() .cached_process_num @@ -171,11 +173,22 @@ impl ZorkCache { commands .interfaces .iter() - .map(|module_command_line| CommandDetail { - translation_unit: self.set_translation_unit_identifier(module_command_line), - execution_result: self - .normalize_execution_result_status(module_command_line), - command: self.set_module_generated_command_line(module_command_line), + .map(|module_command_line| { + self.last_generated_commands.entry(module_command_line.path()) + .or_insert_with(|| { + has_changes = true; + module_command_line.args.iter() + .map(|e| e.value.to_string()) + .collect() + } + ); + CommandDetail { + directory: module_command_line.directory.to_str().unwrap_or_default().to_string(), + file: module_command_line.file.clone(), + execution_result: self + .normalize_execution_result_status(module_command_line), + command: self.set_module_generated_command_line(module_command_line), + } }), ); @@ -185,11 +198,22 @@ impl ZorkCache { commands .implementations .iter() - .map(|module_command_line| CommandDetail { - translation_unit: self.set_translation_unit_identifier(module_command_line), - execution_result: self - .normalize_execution_result_status(module_command_line), - command: self.set_module_generated_command_line(module_command_line), + .map(|module_command_line| { + self.last_generated_commands.entry(module_command_line.path()) + .or_insert_with(|| { + has_changes = true; + module_command_line.args.iter() + .map(|e| e.value.to_string()) + .collect() + } + ); + CommandDetail { + directory: module_command_line.directory.to_str().unwrap_or_default().to_string(), + file: module_command_line.file.clone(), + execution_result: self + .normalize_execution_result_status(module_command_line), + command: self.set_module_generated_command_line(module_command_line), + } }), ); @@ -205,7 +229,15 @@ impl ZorkCache { .join(" "), }; - self.generated_commands.details.push(commands_details) + self.last_generated_commands.entry(PathBuf::from(commands.sources.main)) // provisional + .or_insert_with(|| { + has_changes = true; + vec![commands_details.main.command.clone()] + } + ); + + self.generated_commands.push(commands_details); + has_changes } /// If Windows is the current OS, and the compiler is MSVC, then we will try @@ -280,7 +312,7 @@ impl ZorkCache { .execution_result .eq(&CommandExecutionResult::Unreached) { - if let Some(prev_entry) = self.is_file_cached(&module_command_line.path) { + if let Some(prev_entry) = self.is_file_cached(module_command_line.path()) { prev_entry.execution_result.clone() } else { module_command_line.execution_result.clone() @@ -302,106 +334,19 @@ impl ZorkCache { .join(" ") } } - - fn set_translation_unit_identifier(&self, module_command_line: &ModuleCommandLine) -> String { - String::from( - module_command_line - .path - .as_os_str() - .to_str() - .unwrap_or_default(), - ) - } } /// Generates the `compile_commands.json` file, that acts as a compilation database /// for some static analysis external tools, like `clang-tidy`, and populates it with /// the generated commands for the translation units -fn map_generated_commands_to_compilation_db( - program_data: &ZorkModel, - commands: &Commands<'_>, - test_mode: bool, -) -> Result<()> { +fn map_generated_commands_to_compilation_db(cache: &ZorkCache) -> Result<()> { log::trace!("Generating the compilation database..."); - let total_commands = commands.interfaces.len() + commands.implementations.len() + 1; - let mut compilation_db_entries = Vec::with_capacity(total_commands); - - for command in &commands.interfaces { - let path = fs::canonicalize(command.path.parent().unwrap_or(Path::new(""))) - .map(|p| String::from(p.to_str().unwrap_or_default())) - .unwrap_or_default(); - - let mut arguments = vec![commands.compiler.get_driver()]; - arguments.extend( - command - .args - .iter() - .map(|arg| arg.value) - .collect::>(), - ); - let file = command - .path - .file_name() - .map_or("", |f| f.to_str().unwrap_or_default()); - - compilation_db_entries.push(CompileCommands { - directory: path, - arguments, - file, - }) - } + let mut compilation_db_entries = Vec::with_capacity(cache.last_generated_commands.len()); - for command in &commands.implementations { - let path = fs::canonicalize(command.path.parent().unwrap_or(Path::new(""))) - .map(|p| String::from(p.to_str().unwrap_or_default())) - .unwrap_or_default(); - - let mut arguments = vec![commands.compiler.get_driver()]; - arguments.extend( - command - .args - .iter() - .map(|arg| arg.value) - .collect::>(), - ); - let file = command - .path - .file_name() - .map_or("", |f| f.to_str().unwrap_or_default()); - - compilation_db_entries.push(CompileCommands { - directory: path, - arguments, - file, - }) + for command in cache.last_generated_commands.iter() { + compilation_db_entries.push(CompileCommands::from(command)); } - // generated command for the binary (exe or tests exe) - let entry_point = if !test_mode { - Path::new(".").join(program_data.executable.main) - } else { - Path::new(".").join(program_data.tests.main) - }; - - let mut main_arguments = vec![commands.compiler.get_driver()]; - main_arguments.extend( - commands - .sources - .args - .iter() - .map(|arg| arg.value) - .collect::>(), - ); - compilation_db_entries.push(CompileCommands { - directory: fs::canonicalize(entry_point.parent().unwrap_or(Path::new("."))) - .map(|p| String::from(p.to_str().unwrap_or_default())) - .unwrap_or_default(), - arguments: main_arguments, - file: entry_point - .file_name() - .map_or("", |f| f.to_str().unwrap_or_default()), - }); - let compile_commands_path = Path::new(COMPILATION_DATABASE); if !Path::new(&compile_commands_path).exists() { File::create(compile_commands_path).with_context(|| "Error creating the cache file")?; @@ -413,16 +358,25 @@ fn map_generated_commands_to_compilation_db( /// Data model for serialize the data that will be outputted /// to the `compile_commands.json` compilation database file #[derive(Serialize, Debug, Default, Clone)] -pub struct CompileCommands<'a> { +pub struct CompileCommands { pub directory: String, - pub arguments: Vec<&'a str>, - pub file: &'a str, + pub file: String, + pub arguments: Vec, } -#[derive(Deserialize, Serialize, Debug, Default, Clone)] -pub struct CachedCommands { - compiler: CppCompiler, - details: Vec, +impl From<(&'_ PathBuf, &'_ Vec)> for CompileCommands { + fn from(value: (&PathBuf, &Vec)) -> Self { + let dir = value.0.parent().unwrap_or(Path::new(".")); + let mut file = value.0.file_stem().unwrap_or_default().to_os_string(); + file.push("."); + file.push(value.0.extension().unwrap_or_default()); + + Self { + directory: dir.to_str().unwrap_or_default().to_string(), + file: file.to_str().unwrap_or_default().to_string(), + arguments: value.1.clone() + } + } } #[derive(Deserialize, Serialize, Debug, Default, Clone)] @@ -436,11 +390,19 @@ pub struct CommandsDetails { #[derive(Deserialize, Serialize, Debug, Default, Clone)] pub struct CommandDetail { - translation_unit: String, + directory: String, + file: String, pub execution_result: CommandExecutionResult, command: String, } +impl CommandDetail { + #[inline(always)] + pub fn file_path(&self) -> PathBuf { + Path::new(&self.directory).join(&self.file) + } +} + #[derive(Deserialize, Serialize, Debug, Default, Clone)] pub struct MainCommandLineDetail { files: Vec, diff --git a/zork++/src/lib/cli/output/commands.rs b/zork++/src/lib/cli/output/commands.rs index 7f87790b..48e6735b 100644 --- a/zork++/src/lib/cli/output/commands.rs +++ b/zork++/src/lib/cli/output/commands.rs @@ -16,6 +16,7 @@ use color_eyre::{ Report, Result, }; use serde::{Deserialize, Serialize}; +use crate::bounds::TranslationUnit; use super::arguments::Argument; @@ -40,7 +41,7 @@ pub fn run_generated_commands( cache::save(program_data, cache, commands, test_mode)?; return Err(eyre!( "Ending the program, because the build of: {:?} wasn't ended successfully", - c_miu.path + c_miu.file )); } } @@ -60,7 +61,7 @@ pub fn run_generated_commands( let c_miu = implm.clone(); return Err(eyre!( "Ending the program, because the build of: {:?} wasn't ended successfully", - c_miu.path + c_miu.file )); } } @@ -160,21 +161,32 @@ fn execute_command( /// since the last iteration of Zork++ #[derive(Debug, Clone)] pub struct ModuleCommandLine<'a> { - pub path: PathBuf, + pub directory: PathBuf, + pub file: String, pub args: Vec>, pub processed: bool, pub execution_result: CommandExecutionResult, } -impl<'a> From>> for ModuleCommandLine<'a> { - fn from(value: Vec>) -> Self { +impl<'a> ModuleCommandLine<'a> { + pub fn from_translation_unit( + tu: impl TranslationUnit, + args: Vec>, + processed: bool, + execution_result: CommandExecutionResult + ) -> Self { Self { - path: PathBuf::new(), - args: value, - processed: false, - execution_result: Default::default(), + directory: tu.path(), + file: tu.file_with_extension(), + args, + processed, + execution_result, } } + + pub fn path(&self) -> PathBuf { + self.directory.join(Path::new(&self.file)) + } } impl<'a> IntoIterator for ModuleCommandLine<'a> { @@ -186,13 +198,25 @@ impl<'a> IntoIterator for ModuleCommandLine<'a> { } } -#[derive(Debug, Default)] +#[derive(Debug)] pub struct SourcesCommandLine<'a> { + pub main: &'a Path, pub sources_paths: Vec, pub args: Vec>, pub execution_result: CommandExecutionResult, } +impl<'a> Default for SourcesCommandLine<'a> { + fn default() -> Self { + Self { + main: Path::new("."), + sources_paths: Vec::with_capacity(0), + args: Vec::with_capacity(0), + execution_result: Default::default(), + } + } +} + /// Holds the generated command line arguments for a concrete compiler #[derive(Debug)] pub struct Commands<'a> { diff --git a/zork++/src/lib/compiler/mod.rs b/zork++/src/lib/compiler/mod.rs index 0b651766..21b752f1 100644 --- a/zork++/src/lib/compiler/mod.rs +++ b/zork++/src/lib/compiler/mod.rs @@ -91,13 +91,10 @@ fn prebuild_module_interfaces<'a>( if !flag_modules_without_changes(&model.compiler.cpp_compiler, cache, &module_interface.file()) { sources::generate_module_interfaces_args(model, module_interface, commands); } else { - let mut command_line = ModuleCommandLine { - path: module_interface.file(), - args: Vec::with_capacity(0), - processed: true, - execution_result: CommandExecutionResult::default(), - }; - command_line.execution_result = CommandExecutionResult::Cached; + let command_line = ModuleCommandLine::from_translation_unit( + module_interface, Vec::with_capacity(0), true, CommandExecutionResult::Cached + ); + log::trace!("Translation unit: {:?} was not modified since the last iteration. No need to rebuilt it again.", &module_interface.file()); commands.interfaces.push(command_line); commands.generated_files_paths.push(Argument::from(helpers::generate_prebuild_miu( @@ -119,15 +116,12 @@ fn compile_module_implementations<'a>( if !flag_modules_without_changes(&model.compiler.cpp_compiler, cache, &module_impl.file()) { sources::generate_module_implementation_args(model, module_impl, commands); } else { - let mut command_line = ModuleCommandLine { - path: module_impl.file(), - args: Vec::with_capacity(0), - processed: true, - execution_result: CommandExecutionResult::default(), - }; - command_line.execution_result = CommandExecutionResult::Cached; + let command_line = ModuleCommandLine::from_translation_unit( + module_impl, Vec::with_capacity(0), true, CommandExecutionResult::Cached + ); + log::trace!("Translation unit: {:?} was not modified since the last iteration. No need to rebuilt it again.", &module_impl.file()); - commands.interfaces.push(command_line); + commands.implementations.push(command_line); commands.generated_files_paths.push(Argument::from(helpers::generate_impl_obj_file( model.compiler.cpp_compiler, model.build.output_dir, module_impl ))) @@ -140,7 +134,6 @@ mod sources { use super::helpers; use crate::{ bounds::{ExecutableTarget, TranslationUnit}, - cache::ZorkCache, cli::output::{ arguments::{clang_args, Argument}, commands::{CommandExecutionResult, Commands, ModuleCommandLine}, @@ -153,7 +146,7 @@ mod sources { utils::constants, }; use color_eyre::Result; - use std::path::Path; + /// Generates the command line arguments for non-module source files, including the one that /// holds the main function @@ -246,6 +239,7 @@ mod sources { }; target.sourceset().as_args_to(&mut arguments)?; + commands.sources.main = target.entry_point(); commands.sources.args.extend(arguments.into_iter()); commands.sources.sources_paths = target .sourceset() @@ -353,12 +347,9 @@ mod sources { } } - let command_line = ModuleCommandLine { - path: interface.file().to_path_buf(), - args: arguments, - processed: false, - execution_result: CommandExecutionResult::default(), - }; + let command_line = ModuleCommandLine::from_translation_unit( + interface, arguments, false, CommandExecutionResult::default() + ); commands.interfaces.push(command_line); } @@ -421,7 +412,7 @@ mod sources { .join(compiler.as_ref()) .join("modules") .join("implementations") - .join(implementation.filestem()) + .join(implementation.file_stem()) .with_extension(compiler.get_obj_file_extension()); commands @@ -446,12 +437,9 @@ mod sources { } } - let command_line = ModuleCommandLine { - path: implementation.file(), - args: arguments, - processed: false, - execution_result: CommandExecutionResult::default(), - }; + let command_line = ModuleCommandLine::from_translation_unit( + implementation, arguments, false, CommandExecutionResult::default() + ); commands.implementations.push(command_line); } } @@ -494,7 +482,7 @@ mod helpers { if !partition.partition_name.is_empty() { temp.push_str(partition.partition_name) } else { - temp.push_str(&interface.filestem()) + temp.push_str(&interface.file_stem()) } } else { temp.push_str(interface.module_name) @@ -525,7 +513,7 @@ mod helpers { .join(compiler.as_ref()) .join("modules") .join("implementations") - .join(implementation.filestem()) + .join(implementation.file_stem()) .with_extension(compiler.get_obj_file_extension()) } @@ -590,7 +578,8 @@ mod helpers { // independently, also check for execution independently for collection_args in sys_modules { commands.interfaces.push(ModuleCommandLine { - path: PathBuf::from(collection_args[4].value), + directory: PathBuf::with_capacity(0), + file: collection_args[4].value.to_string(), args: collection_args, processed: false, execution_result: CommandExecutionResult::default(), diff --git a/zork++/src/lib/lib.rs b/zork++/src/lib/lib.rs index 0ea32329..93db2ff2 100644 --- a/zork++/src/lib/lib.rs +++ b/zork++/src/lib/lib.rs @@ -70,7 +70,7 @@ pub mod worker { create_output_directory(&program_data)?; let cache = cache::load(&program_data, cli_args) - .with_context(|| "Unable to load the Zork++ caché")?; + .with_context(|| "Unable to load the Zork++ cache")?; let read_only_cache = cache.clone(); // let generated_commands = diff --git a/zork++/src/lib/project_model/executable.rs b/zork++/src/lib/project_model/executable.rs index df16d550..05d27014 100644 --- a/zork++/src/lib/project_model/executable.rs +++ b/zork++/src/lib/project_model/executable.rs @@ -2,7 +2,7 @@ use crate::{ bounds::{ExecutableTarget, ExtraArgs}, cli::output::arguments::Argument, }; -use std::path::Path; +use std::path::{Path, PathBuf}; use super::sourceset::SourceSet; @@ -10,7 +10,7 @@ use super::sourceset::SourceSet; pub struct ExecutableModel<'a> { pub executable_name: &'a str, pub sourceset: SourceSet<'a>, - pub main: &'a Path, + pub main: PathBuf, pub extra_args: Vec>, } @@ -24,7 +24,7 @@ impl<'a> ExecutableTarget<'a> for ExecutableModel<'a> { fn name(&'a self) -> &'a str { self.executable_name } - + fn entry_point(&'a self) -> &'a Path { &self.main } fn sourceset(&'a self) -> &'a SourceSet<'a> { &self.sourceset } diff --git a/zork++/src/lib/project_model/modules.rs b/zork++/src/lib/project_model/modules.rs index ad7c83cd..546cd0f6 100644 --- a/zork++/src/lib/project_model/modules.rs +++ b/zork++/src/lib/project_model/modules.rs @@ -15,6 +15,7 @@ pub struct ModulesModel<'a> { #[derive(Debug, PartialEq, Eq)] pub struct ModuleInterfaceModel<'a> { pub path: PathBuf, + pub file_stem: String, pub extension: String, pub module_name: &'a str, pub partition: Option>, @@ -26,32 +27,51 @@ impl<'a> fmt::Display for ModuleInterfaceModel<'a> { write!( f, "({:?}.{:?}., {:?}, {:?}, {:?})", - self.path, self.extension, self.module_name, self.dependencies, self.partition + self.path, self.file_stem, self.module_name, self.dependencies, self.partition ) } } impl<'a> TranslationUnit for ModuleInterfaceModel<'a> { fn file(&self) -> PathBuf { - let mut tmp = self.path.clone().into_os_string(); + let mut tmp = self.path.join(&self.file_stem).into_os_string(); tmp.push("."); - tmp.push(self.extension.clone()); + tmp.push(&self.extension); PathBuf::from(tmp) } + fn path(&self) -> PathBuf { self.path.clone() } - fn extension(&self) -> String { self.extension.to_string() } + + fn file_stem(&self) -> String { + self.file_stem.clone() + } + + fn extension(&self) -> String { + self.extension.clone() + } } impl<'a> TranslationUnit for &'a ModuleInterfaceModel<'a> { fn file(&self) -> PathBuf { - self.path.with_extension(self.extension.clone()) + let mut tmp = self.path.join(&self.file_stem).into_os_string(); + tmp.push("."); + tmp.push(&self.extension); + PathBuf::from(tmp) } + fn path(&self) -> PathBuf { self.path.clone() } - fn extension(&self) -> String { self.extension.to_string() } + + fn file_stem(&self) -> String { + self.file_stem.clone() + } + + fn extension(&self) -> String { + self.extension.clone() + } } #[derive(Debug, PartialEq, Eq)] @@ -74,6 +94,7 @@ impl<'a> From<&ModulePartition<'a>> for ModulePartitionModel<'a> { #[derive(Debug, PartialEq, Eq)] pub struct ModuleImplementationModel<'a> { pub path: PathBuf, + pub file_stem: String, pub extension: String, pub dependencies: Vec<&'a str>, } @@ -86,16 +107,42 @@ impl<'a> fmt::Display for ModuleImplementationModel<'a> { impl<'a> TranslationUnit for ModuleImplementationModel<'a> { fn file(&self) -> PathBuf { - self.path.with_extension(self.extension.clone()) + let mut tmp = self.path.join(&self.file_stem).into_os_string(); + tmp.push("."); + tmp.push(&self.extension); + PathBuf::from(tmp) + } + + fn path(&self) -> PathBuf { + self.path.clone() + } + + fn file_stem(&self) -> String { + self.file_stem.clone() + } + + fn extension(&self) -> String { + self.extension.clone() } - fn path(&self) -> PathBuf { self.path.clone() } - fn extension(&self) -> String { self.extension.to_string() } } impl<'a> TranslationUnit for &'a ModuleImplementationModel<'a> { fn file(&self) -> PathBuf { - self.path.with_extension(self.extension.clone()) + let mut tmp = self.path.join(&self.file_stem).into_os_string(); + tmp.push("."); + tmp.push(&self.extension); + PathBuf::from(tmp) + } + + fn path(&self) -> PathBuf { + self.path.clone() + } + + fn file_stem(&self) -> String { + self.file_stem.clone() + } + + fn extension(&self) -> String { + self.extension.clone() } - fn path(&self) -> PathBuf { self.path.clone() } - fn extension(&self) -> String { self.extension.to_string() } } diff --git a/zork++/src/lib/project_model/tests.rs b/zork++/src/lib/project_model/tests.rs index dbd8b896..a4d52534 100644 --- a/zork++/src/lib/project_model/tests.rs +++ b/zork++/src/lib/project_model/tests.rs @@ -2,7 +2,7 @@ use crate::{ bounds::{ExecutableTarget, ExtraArgs}, cli::output::arguments::Argument, }; -use std::path::Path; +use std::path::{Path, PathBuf}; use super::sourceset::SourceSet; @@ -10,7 +10,7 @@ use super::sourceset::SourceSet; pub struct TestsModel<'a> { pub test_executable_name: String, pub sourceset: SourceSet<'a>, - pub main: &'a Path, + pub main: PathBuf, pub extra_args: Vec>, } @@ -24,7 +24,7 @@ impl<'a> ExecutableTarget<'a> for TestsModel<'a> { fn name(&'a self) -> &'a str { &self.test_executable_name } - + fn entry_point(&'a self) -> &'a Path { &self.main } fn sourceset(&'a self) -> &'a SourceSet<'a> { &self.sourceset } diff --git a/zork++/src/lib/utils/fs.rs b/zork++/src/lib/utils/fs.rs index dfb5a7c6..c4aae019 100644 --- a/zork++/src/lib/utils/fs.rs +++ b/zork++/src/lib/utils/fs.rs @@ -1,9 +1,10 @@ -use std::{env, fs::{DirBuilder, File}, fs, io::{BufReader, Write}, path::Path}; +use std::{fs::{DirBuilder, File}, io::{BufReader, Write}, path::Path}; + use std::path::PathBuf; use color_eyre::{eyre::Context, Result}; use color_eyre::eyre::ContextCompat; -use color_eyre::owo_colors::OwoColorize; + use serde::{Deserialize, Serialize}; use super::constants; @@ -37,6 +38,22 @@ pub fn get_absolute_path>(p: P) -> Result { r } +/// +pub fn get_file_details>(p: P) -> Result<(PathBuf, String, String)> { + let mut canonical = p.as_ref().canonicalize().with_context(|| format!("Error getting the canonical path for: {:?}", p.as_ref()))?; + if cfg!(target_os = "windows") { + canonical = canonical.to_str().map(|unc| &unc[4..]).unwrap_or_default().into() + } + let file_stem = canonical.file_stem().with_context(|| format!("Unable to get the file stem for {:?}", p.as_ref()))?; + + Ok(( + canonical.parent().unwrap_or_else(|| panic!("Unexpected error getting the parent of {:?}", p.as_ref())).to_path_buf(), + file_stem.to_str().unwrap_or_default().to_string(), + canonical.extension() + .map_or_else(|| String::with_capacity(0), |os_str| os_str.to_str().unwrap_or_default().to_string()) + )) +} + /// Returns the declared extension for a file, if exists #[inline(always)] pub fn get_file_extension>(p: P) -> String { diff --git a/zork++/src/lib/utils/reader.rs b/zork++/src/lib/utils/reader.rs index 13d59cac..a64fa3c5 100644 --- a/zork++/src/lib/utils/reader.rs +++ b/zork++/src/lib/utils/reader.rs @@ -157,7 +157,10 @@ fn assemble_executable_model<'a>( sources, }; - let main = Path::new(config.map_or("", |exe_attr| exe_attr.main)); + let entry_point_file_path = Path::new(base_path) + .join(config.map_or("", |exe_attr| exe_attr.main)); + let main = utils::fs::get_absolute_path(entry_point_file_path) + .unwrap_or_default(); let extra_args = config .and_then(|exe| exe.extra_args.as_ref()) @@ -237,12 +240,15 @@ fn assemble_module_interface_model<'a>( )) }; + let file_details = utils::fs::get_file_details(file_path) + .unwrap_or_default(); // TODO Care with this, refactor ModuleInterfaceModel { - path: utils::fs::get_absolute_path(&file_path).expect("TODO Propagate error on get path"), - extension: utils::fs::get_file_extension(&file_path), + path: file_details.0, + file_stem: file_details.1, + extension: file_details.2, module_name, partition, - dependencies, + dependencies } } @@ -262,9 +268,13 @@ fn assemble_module_implementation_model<'a>( } } + let file_details = utils::fs::get_file_details(file_path) + .unwrap_or_default(); // TODO Care with this, refactor + ModuleImplementationModel { - path: utils::fs::get_absolute_path(&file_path).expect("TODO Propagate error on get path impl"), - extension: utils::fs::get_file_extension(&file_path), + path: file_details.0, + file_stem: file_details.1, + extension: file_details.2, dependencies, } } @@ -300,7 +310,10 @@ fn assemble_tests_model<'a>( sources, }; - let main = Path::new(config.map_or("", |test_attr| test_attr.main)); + let entry_point_file_path = Path::new(base_path) + .join(config.map_or("defaulted", |test_attr| test_attr.main)); + let main = utils::fs::get_absolute_path(entry_point_file_path) + .unwrap_or_default(); let extra_args = config .and_then(|test| test.extra_args.as_ref()) @@ -318,6 +331,7 @@ fn assemble_tests_model<'a>( #[cfg(test)] mod test { use std::env; + use std::ffi::OsString; use crate::{ project_model::compiler::{CppCompiler, LanguageLevel, StdLib}, utils, @@ -361,7 +375,7 @@ mod test { base_path: Path::new("."), sources: vec![], }, - main: Path::new("main.cpp"), + main: PathBuf::from("main.cpp"), extra_args: vec![], }, modules: ModulesModel { @@ -377,7 +391,7 @@ mod test { base_path: Path::new("."), sources: vec![], }, - main: Path::new("main.cpp"), + main: PathBuf::from("main.cpp"), extra_args: vec![], }, }; @@ -414,21 +428,23 @@ mod test { base_path: Path::new("bin"), sources: vec![Source::Glob(GlobPattern("*.cpp"))], }, - main: Path::new("main.cpp"), + main: PathBuf::from("main.cpp"), extra_args: vec![Argument::from("-Werr")], }, modules: ModulesModel { base_ifcs_dir: Path::new("ifc"), interfaces: vec![ ModuleInterfaceModel { - path: env::current_dir().unwrap().join("ifc\\math"), + path: env::current_dir().unwrap().join("ifc"), + file_stem: String::from("math"), extension: String::from("cppm"), module_name: "math", partition: None, dependencies: vec![], }, ModuleInterfaceModel { - path: env::current_dir().unwrap().join("ifc\\some_module"), + path: env::current_dir().unwrap().join("ifc"), + file_stem: String::from("some_module"), extension: String::from("cppm"), module_name: "math", partition: None, @@ -438,12 +454,14 @@ mod test { base_impls_dir: Path::new("src"), implementations: vec![ ModuleImplementationModel { - path: env::current_dir().unwrap().join("\\src\\math"), + path: env::current_dir().unwrap().join("\\src"), + file_stem: String::from("math"), extension: String::from("cpp"), dependencies: vec!["math"], }, ModuleImplementationModel { - path: env::current_dir().unwrap().join("\\ifc\\some_module_impl"), + path: env::current_dir().unwrap().join("\\ifc"), + file_stem: String::from("some_module_impl"), extension: String::from("cpp"), dependencies: vec!["iostream"], }, @@ -456,7 +474,7 @@ mod test { base_path: Path::new("test"), sources: vec![Source::Glob(GlobPattern("*.cpp"))], }, - main: Path::new("main.cpp"), + main: PathBuf::from("main.cpp"), extra_args: vec![Argument::from("-pedantic")], }, }; From 8ecbb544a541dc7ff58feb515c60842978edb5f0 Mon Sep 17 00:00:00 2001 From: Alex Vergara Date: Wed, 8 Mar 2023 17:17:32 +0100 Subject: [PATCH 15/60] Removed `test_mode` function parameter from the run_generated_commands(...) task --- zork++/src/lib/cache/mod.rs | 8 +++----- zork++/src/lib/cli/output/commands.rs | 13 ++++++------- zork++/src/lib/lib.rs | 6 +++--- 3 files changed, 12 insertions(+), 15 deletions(-) diff --git a/zork++/src/lib/cache/mod.rs b/zork++/src/lib/cache/mod.rs index a364b7ad..9ab1421f 100644 --- a/zork++/src/lib/cache/mod.rs +++ b/zork++/src/lib/cache/mod.rs @@ -57,8 +57,7 @@ pub fn load(program_data: &ZorkModel<'_>, cli_args: &CliArgs) -> Result, mut cache: ZorkCache, - commands: Commands<'_>, - test_mode: bool, + commands: Commands<'_> ) -> Result<()> { let cache_path = &Path::new(program_data.build.output_dir) .join("zork") @@ -66,7 +65,7 @@ pub fn save( .join(program_data.compiler.cpp_compiler.as_ref()) .join(constants::ZORK_CACHE_FILENAME); - cache.run_final_tasks(program_data, commands, test_mode)?; + cache.run_final_tasks(program_data, commands)?; cache.last_program_execution = Utc::now(); utils::fs::serialize_object_to_file(cache_path, &cache) @@ -125,8 +124,7 @@ impl ZorkCache { pub fn run_final_tasks( &mut self, program_data: &ZorkModel<'_>, - commands: Commands<'_>, - test_mode: bool, + commands: Commands<'_> ) -> Result<()> { if self.save_generated_commands(&commands) && program_data.project.compilation_db { map_generated_commands_to_compilation_db(self)?; diff --git a/zork++/src/lib/cli/output/commands.rs b/zork++/src/lib/cli/output/commands.rs index 48e6735b..24bccf44 100644 --- a/zork++/src/lib/cli/output/commands.rs +++ b/zork++/src/lib/cli/output/commands.rs @@ -23,8 +23,7 @@ use super::arguments::Argument; pub fn run_generated_commands( program_data: &ZorkModel<'_>, mut commands: Commands<'_>, - cache: ZorkCache, - test_mode: bool, + cache: ZorkCache ) -> Result { if !commands.interfaces.is_empty() { log::debug!("Executing the commands for the module interfaces..."); @@ -34,11 +33,11 @@ pub fn run_generated_commands( let r = execute_command(&commands.compiler, &miu.args, &cache); miu.execution_result = CommandExecutionResult::from(&r); if let Err(e) = r { - cache::save(program_data, cache, commands, test_mode)?; + cache::save(program_data, cache, commands)?; return Err(e); } else if !r.as_ref().unwrap().success() { let c_miu = miu.clone(); - cache::save(program_data, cache, commands, test_mode)?; + cache::save(program_data, cache, commands)?; return Err(eyre!( "Ending the program, because the build of: {:?} wasn't ended successfully", c_miu.file @@ -75,17 +74,17 @@ pub fn run_generated_commands( commands.sources.execution_result = CommandExecutionResult::from(&r); if let Err(e) = r { - cache::save(program_data, cache, commands, test_mode)?; + cache::save(program_data, cache, commands)?; return Err(e); } else if !r.as_ref().unwrap().success() { - cache::save(program_data, cache, commands, test_mode)?; + cache::save(program_data, cache, commands)?; return Err(eyre!( "Ending the program, because the main command line execution wasn't ended successfully", )); } } - cache::save(program_data, cache, commands, test_mode)?; + cache::save(program_data, cache, commands)?; Ok(CommandExecutionResult::Success) } diff --git a/zork++/src/lib/lib.rs b/zork++/src/lib/lib.rs index 93db2ff2..35f871f2 100644 --- a/zork++/src/lib/lib.rs +++ b/zork++/src/lib/lib.rs @@ -104,13 +104,13 @@ pub mod worker { commands = build_project(program_data, read_only_cache, false) .with_context(|| "Failed to build project")?; - commands::run_generated_commands(program_data, commands, cache, false) + commands::run_generated_commands(program_data, commands, cache) } Command::Run => { commands = build_project(program_data, read_only_cache, false) .with_context(|| "Failed to build project")?; - match commands::run_generated_commands(program_data, commands, cache, false) { + match commands::run_generated_commands(program_data, commands, cache) { Ok(_) => autorun_generated_binary( &program_data.compiler.cpp_compiler, program_data.build.output_dir, @@ -123,7 +123,7 @@ pub mod worker { commands = build_project(program_data, read_only_cache, true) .with_context(|| "Failed to build project")?; - match commands::run_generated_commands(program_data, commands, cache, true) { + match commands::run_generated_commands(program_data, commands, cache) { Ok(_) => autorun_generated_binary( &program_data.compiler.cpp_compiler, program_data.build.output_dir, From eb4b08b8bd66e5d7df687beb1766b0fe82809231 Mon Sep 17 00:00:00 2001 From: Alex Vergara Date: Thu, 9 Mar 2023 12:15:06 +0100 Subject: [PATCH 16/60] WIP - Non module source files with their own build and assemble process without link --- CHANGELOG.md | 25 +++- zork++/src/lib/bounds/mod.rs | 5 +- zork++/src/lib/cache/mod.rs | 152 ++++++++++++--------- zork++/src/lib/cli/output/arguments.rs | 16 ++- zork++/src/lib/cli/output/commands.rs | 40 ++++-- zork++/src/lib/compiler/mod.rs | 138 +++++++++++++++++-- zork++/src/lib/lib.rs | 5 +- zork++/src/lib/project_model/executable.rs | 8 +- zork++/src/lib/project_model/sourceset.rs | 82 +++++++++-- zork++/src/lib/project_model/tests.rs | 8 +- zork++/src/lib/utils/fs.rs | 65 +++++++-- zork++/src/lib/utils/reader.rs | 144 +++++++++---------- 12 files changed, 494 insertions(+), 194 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fbd36b3b..b11c5a61 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,27 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [0.8.0] - // TODO + +### Feature + +### Updates + +- Source type has been modified to support individual files, and +sourceset now is a collection of those individuals, non one path or multiple paths +- Non module source files are compiled and assembled without linking now. This +allows us to generate the compile_commands.json for every translation unit +in the program, without grouping them into one big command line that was compiling +assembling and linking. +- Due to the described above, now the main task of the main command line +is to link the generated object files together, introducing the necessary dependencies +- Non module source files now have their explicit types and operations + +### Fix + +- Solved a bug for which the source files was always detected as a glob pattern, +even if they were declared in a non glob form + ## [0.7.0] - 2022 - 03 - 01 ### Feature @@ -35,7 +56,7 @@ the declared system modules required, just as we were doing with `GCC` - The project is full rewritten in Rust - We included full support for working with C++ module for the three major compilers -- We started to work in a caché, to track data and posibily, speed up big projects +- We started to work in a cache, to track data and possibly, speed up big projects ## [0.4.2] - 2022 - 12 - 28 @@ -51,7 +72,7 @@ the declared system modules required, just as we were doing with `GCC` ### Fix -- Correction on the log showed for the executable autorunner and for the tests runner +- Correction on the log showed for the executable auto runner and for the tests runner ## [0.3.1] - 2022 - 12 - 06 diff --git a/zork++/src/lib/bounds/mod.rs b/zork++/src/lib/bounds/mod.rs index ef17819c..00af6a34 100644 --- a/zork++/src/lib/bounds/mod.rs +++ b/zork++/src/lib/bounds/mod.rs @@ -1,5 +1,5 @@ use core::fmt::Debug; -use std::{fmt::Display}; +use std::fmt::Display; use std::path::{Path, PathBuf}; use crate::{cli::output::arguments::Argument, project_model::sourceset::SourceSet}; @@ -13,7 +13,7 @@ pub trait ExtraArgs<'a> { pub trait ExecutableTarget<'a>: ExtraArgs<'a> { fn name(&'a self) -> &'a str; fn entry_point(&'a self) -> &'a Path; - fn sourceset(&'a self) -> &'a SourceSet<'a>; + fn sourceset(&'a self) -> &'a SourceSet; } /// Represents any kind of translation unit and the generic operations @@ -37,4 +37,3 @@ pub trait TranslationUnit: Display + Debug { format!("{}.{}", self.file_stem(), self.extension()) } } - diff --git a/zork++/src/lib/cache/mod.rs b/zork++/src/lib/cache/mod.rs index 9ab1421f..9609db31 100644 --- a/zork++/src/lib/cache/mod.rs +++ b/zork++/src/lib/cache/mod.rs @@ -2,14 +2,15 @@ use chrono::{DateTime, Utc}; use color_eyre::{eyre::Context, Result}; +use std::collections::HashMap; use std::{ fs, fs::File, path::{Path, PathBuf}, }; -use std::collections::HashMap; use walkdir::WalkDir; +use crate::cli::output::arguments::Argument; use crate::utils::constants::COMPILATION_DATABASE; use crate::{ cli::{ @@ -23,7 +24,6 @@ use crate::{ }, }; use serde::{Deserialize, Serialize}; -use crate::cli::output::arguments::Argument; /// Standalone utility for retrieve the Zork++ cache file pub fn load(program_data: &ZorkModel<'_>, cli_args: &CliArgs) -> Result { @@ -57,7 +57,7 @@ pub fn load(program_data: &ZorkModel<'_>, cli_args: &CliArgs) -> Result, mut cache: ZorkCache, - commands: Commands<'_> + commands: Commands<'_>, ) -> Result<()> { let cache_path = &Path::new(program_data.build.output_dir) .join("zork") @@ -78,7 +78,7 @@ pub struct ZorkCache { pub last_program_execution: DateTime, pub compilers_metadata: CompilersMetadata, pub generated_commands: Vec, - pub last_generated_commands: HashMap> + pub last_generated_commands: HashMap>, } impl ZorkCache { @@ -87,9 +87,10 @@ impl ZorkCache { let last_iteration_details = self.generated_commands.last(); if let Some(last_iteration) = last_iteration_details { - let found_as_ifc = last_iteration.interfaces.iter().find(|comm_det| { - comm_det.file_path().eq(path.as_ref()) - }); + let found_as_ifc = last_iteration + .interfaces + .iter() + .find(|comm_det| comm_det.file_path().eq(path.as_ref())); if found_as_ifc.is_some() { return found_as_ifc; @@ -124,7 +125,7 @@ impl ZorkCache { pub fn run_final_tasks( &mut self, program_data: &ZorkModel<'_>, - commands: Commands<'_> + commands: Commands<'_>, ) -> Result<()> { if self.save_generated_commands(&commands) && program_data.project.compilation_db { map_generated_commands_to_compilation_db(self)?; @@ -148,11 +149,7 @@ impl ZorkCache { self.compiler = commands.compiler; let process_no = if !self.generated_commands.is_empty() { - self.generated_commands - .last() - .unwrap() - .cached_process_num - + 1 + self.generated_commands.last().unwrap().cached_process_num + 1 } else { 1 }; @@ -162,64 +159,90 @@ impl ZorkCache { generated_at: Utc::now(), interfaces: Vec::with_capacity(commands.interfaces.len()), implementations: Vec::with_capacity(commands.implementations.len()), + sources: Vec::with_capacity(commands.sources.len()), main: MainCommandLineDetail::default(), }; commands_details .interfaces - .extend( - commands - .interfaces - .iter() - .map(|module_command_line| { - self.last_generated_commands.entry(module_command_line.path()) - .or_insert_with(|| { - has_changes = true; - module_command_line.args.iter() - .map(|e| e.value.to_string()) - .collect() - } - ); - CommandDetail { - directory: module_command_line.directory.to_str().unwrap_or_default().to_string(), - file: module_command_line.file.clone(), - execution_result: self - .normalize_execution_result_status(module_command_line), - command: self.set_module_generated_command_line(module_command_line), - } - }), - ); + .extend(commands.interfaces.iter().map(|module_command_line| { + self.last_generated_commands + .entry(module_command_line.path()) + .or_insert_with(|| { + has_changes = true; + module_command_line + .args + .iter() + .map(|e| e.value.to_string()) + .collect() + }); + CommandDetail { + directory: module_command_line + .directory + .to_str() + .unwrap_or_default() + .to_string(), + file: module_command_line.file.clone(), + execution_result: self.normalize_execution_result_status(module_command_line), + command: self.set_module_generated_command_line(module_command_line), + } + })); commands_details .implementations - .extend( - commands - .implementations - .iter() - .map(|module_command_line| { - self.last_generated_commands.entry(module_command_line.path()) - .or_insert_with(|| { - has_changes = true; - module_command_line.args.iter() - .map(|e| e.value.to_string()) - .collect() - } - ); - CommandDetail { - directory: module_command_line.directory.to_str().unwrap_or_default().to_string(), - file: module_command_line.file.clone(), - execution_result: self - .normalize_execution_result_status(module_command_line), - command: self.set_module_generated_command_line(module_command_line), - } - }), - ); + .extend(commands.implementations.iter().map(|module_command_line| { + self.last_generated_commands + .entry(module_command_line.path()) + .or_insert_with(|| { + has_changes = true; + module_command_line + .args + .iter() + .map(|e| e.value.to_string()) + .collect() + }); + CommandDetail { + directory: module_command_line + .directory + .to_str() + .unwrap_or_default() + .to_string(), + file: module_command_line.file.clone(), + execution_result: self.normalize_execution_result_status(module_command_line), + command: self.set_module_generated_command_line(module_command_line), + } + })); + + commands_details + .sources + .extend(commands.sources.iter().map(|source_command_line| { + self.last_generated_commands + .entry(source_command_line.path()) + .or_insert_with(|| { + has_changes = true; + source_command_line + .args + .iter() + .map(|e| e.value.to_string()) + .collect() + }); + CommandDetail { + directory: source_command_line + .directory + .to_str() + .unwrap_or_default() + .to_string(), + file: source_command_line.file.clone(), + execution_result: self.normalize_execution_result_status(source_command_line), + command: self.set_module_generated_command_line(source_command_line), + } + })); commands_details.main = MainCommandLineDetail { - files: commands.sources.sources_paths.clone(), - execution_result: commands.sources.execution_result.clone(), + files: commands.main.sources_paths.clone(), + execution_result: commands.main.execution_result.clone(), command: commands - .sources + .main .args .iter() .map(|arg| arg.value.to_string()) @@ -227,12 +250,12 @@ impl ZorkCache { .join(" "), }; - self.last_generated_commands.entry(PathBuf::from(commands.sources.main)) // provisional + self.last_generated_commands + .entry(PathBuf::from(commands.main.main)) // provisional .or_insert_with(|| { has_changes = true; vec![commands_details.main.command.clone()] - } - ); + }); self.generated_commands.push(commands_details); has_changes @@ -372,7 +395,7 @@ impl From<(&'_ PathBuf, &'_ Vec)> for CompileCommands { Self { directory: dir.to_str().unwrap_or_default().to_string(), file: file.to_str().unwrap_or_default().to_string(), - arguments: value.1.clone() + arguments: value.1.clone(), } } } @@ -383,6 +406,7 @@ pub struct CommandsDetails { generated_at: DateTime, interfaces: Vec, implementations: Vec, + sources: Vec, main: MainCommandLineDetail, } diff --git a/zork++/src/lib/cli/output/arguments.rs b/zork++/src/lib/cli/output/arguments.rs index 322e451f..a159a4e4 100644 --- a/zork++/src/lib/cli/output/arguments.rs +++ b/zork++/src/lib/cli/output/arguments.rs @@ -1,8 +1,8 @@ //! Types and procedures that represents a command line argument, //! or collections of command line arguments -use std::{borrow::Borrow, ffi::OsStr, path::PathBuf}; use std::path::Path; +use std::{borrow::Borrow, ffi::OsStr, path::PathBuf}; use serde::{Deserialize, Serialize}; @@ -100,6 +100,20 @@ pub mod clang_args { None } + pub(crate) fn add_prebuilt_module_path( + compiler: CppCompiler, + out_dir: &Path + ) -> Argument<'_> { + Argument::from(format!( + "-fprebuilt-module-path={}", + out_dir + .join(compiler.as_ref()) + .join("modules") + .join("interfaces") + .display() + )) + } + pub(crate) fn add_direct_module_interfaces_dependencies( dependencies: &[&str], compiler: CppCompiler, diff --git a/zork++/src/lib/cli/output/commands.rs b/zork++/src/lib/cli/output/commands.rs index 24bccf44..9eb0cd92 100644 --- a/zork++/src/lib/cli/output/commands.rs +++ b/zork++/src/lib/cli/output/commands.rs @@ -3,6 +3,7 @@ use std::{ process::ExitStatus, }; +use crate::bounds::TranslationUnit; ///! Contains helpers and data structure to process in /// a nice and neat way the commands generated to be executed /// by Zork++ @@ -16,14 +17,13 @@ use color_eyre::{ Report, Result, }; use serde::{Deserialize, Serialize}; -use crate::bounds::TranslationUnit; use super::arguments::Argument; pub fn run_generated_commands( program_data: &ZorkModel<'_>, mut commands: Commands<'_>, - cache: ZorkCache + cache: ZorkCache, ) -> Result { if !commands.interfaces.is_empty() { log::debug!("Executing the commands for the module interfaces..."); @@ -67,11 +67,31 @@ pub fn run_generated_commands( } } - if !commands.sources.args.is_empty() { + if !commands.sources.is_empty() { + log::debug!("Executing the commands for the source files..."); + + for source in &mut commands.sources { + if !source.processed { + let r = execute_command(&commands.compiler, &source.args, &cache); + source.execution_result = CommandExecutionResult::from(&r); + if let Err(e) = r { + return Err(e); + } else if !r.as_ref().unwrap().success() { + let c_miu = source.clone(); + return Err(eyre!( + "Ending the program, because the build of: {:?} wasn't ended successfully", + c_miu.file + )); + } + } + } + } + + if !commands.main.args.is_empty() { log::debug!("Executing the main command line..."); - let r = execute_command(&commands.compiler, &commands.sources.args, &cache); - commands.sources.execution_result = CommandExecutionResult::from(&r); + let r = execute_command(&commands.compiler, &commands.main.args, &cache); + commands.main.execution_result = CommandExecutionResult::from(&r); if let Err(e) = r { cache::save(program_data, cache, commands)?; @@ -172,7 +192,7 @@ impl<'a> ModuleCommandLine<'a> { tu: impl TranslationUnit, args: Vec>, processed: bool, - execution_result: CommandExecutionResult + execution_result: CommandExecutionResult, ) -> Self { Self { directory: tu.path(), @@ -223,7 +243,8 @@ pub struct Commands<'a> { pub system_modules: Vec>>, pub interfaces: Vec>, pub implementations: Vec>, - pub sources: SourcesCommandLine<'a>, + pub sources: Vec>, + pub main: SourcesCommandLine<'a>, pub generated_files_paths: Vec>, } @@ -234,7 +255,8 @@ impl<'a> Commands<'a> { system_modules: Vec::with_capacity(0), interfaces: Vec::with_capacity(0), implementations: Vec::with_capacity(0), - sources: SourcesCommandLine::default(), + sources: Vec::with_capacity(0), + main: SourcesCommandLine::default(), generated_files_paths: Vec::with_capacity(0), } } @@ -248,7 +270,7 @@ impl<'a> core::fmt::Display for Commands<'a> { self.compiler, self.interfaces.iter().map(|vec| { vec.args.iter().map(|e| e.value).collect::>().join(" "); }), self.implementations.iter().map(|vec| { vec.args.iter().map(|e| e.value).collect::>().join(" "); }), - self.sources.args.iter().map(|e| e.value).collect::>().join(" ") + self.sources.iter().map(|vec| { vec.args.iter().map(|e| e.value).collect::>().join(" "); }), ) } } diff --git a/zork++/src/lib/compiler/mod.rs b/zork++/src/lib/compiler/mod.rs index 21b752f1..7f7f5234 100644 --- a/zork++/src/lib/compiler/mod.rs +++ b/zork++/src/lib/compiler/mod.rs @@ -6,6 +6,9 @@ use color_eyre::Result; use std::path::Path; +use crate::bounds::TranslationUnit; +use crate::cli::output::commands::{CommandExecutionResult, ModuleCommandLine}; +use crate::compiler::helpers::flag_modules_without_changes; use crate::{ cache::ZorkCache, cli::output::{arguments::Argument, commands::Commands}, @@ -15,9 +18,6 @@ use crate::{ ZorkModel, }, }; -use crate::bounds::TranslationUnit; -use crate::cli::output::commands::{CommandExecutionResult, ModuleCommandLine}; -use crate::compiler::helpers::flag_modules_without_changes; /// The entry point of the compilation process /// @@ -37,7 +37,9 @@ pub fn build_project<'a>( // 1st - Build the modules build_modules(model, cache, &mut commands)?; - // 2st - Build the executable or the tests + // 2nd - Build the non module sources + build_sources(model, &mut commands, tests)?; + // 3rd - Build the executable or the tests build_executable(model, &mut commands, tests)?; Ok(commands) @@ -60,6 +62,24 @@ fn build_executable<'a>( } } +fn build_sources<'a>( + model: &'a ZorkModel<'_>, + commands: &'_ mut Commands<'a>, + tests: bool, +) -> Result<()> { + if tests { + for source in model.tests.sourceset.sources.iter() { + sources::generate_sources_arguments(model, commands, &model.tests, source)?; + } + } else { + for source in model.executable.sourceset.sources.iter() { + sources::generate_sources_arguments(model, commands, &model.executable, source)?; + } + } + + Ok(()) +} + /// Triggers the build process for compile the declared modules in the project /// /// This function acts like a operation result processor, by running instances @@ -146,7 +166,7 @@ mod sources { utils::constants, }; use color_eyre::Result; - + use crate::project_model::sourceset::SourceFile; /// Generates the command line arguments for non-module source files, including the one that /// holds the main function @@ -239,18 +259,98 @@ mod sources { }; target.sourceset().as_args_to(&mut arguments)?; - commands.sources.main = target.entry_point(); - commands.sources.args.extend(arguments.into_iter()); - commands.sources.sources_paths = target + commands.main.main = target.entry_point(); + commands.main.args.extend(arguments.into_iter()); + commands.main.sources_paths = target .sourceset() .sources .iter() - .flat_map(|s| s.paths().unwrap_or_default()) + .map(|s| s.file()) .collect::>(); Ok(()) } + /// Generates the command line arguments for non-module source files + pub fn generate_sources_arguments<'a>( + model: &'a ZorkModel, + commands: &mut Commands<'a>, + target: &'a impl ExecutableTarget<'a>, + source: &'a SourceFile, + ) -> Result<()> { + log::info!("Building the source files..."); + + let compiler = model.compiler.cpp_compiler; + let out_dir = model.build.output_dir; + + let mut arguments = Vec::new(); + arguments.push(model.compiler.language_level_arg()); + arguments.push(Argument::from("-c")); + arguments.extend_from_slice(target.extra_args()); + + match compiler { + CppCompiler::CLANG => { + arguments.extend(clang_args::add_std_lib(model)); + arguments.push(Argument::from("-fimplicit-modules")); + arguments.push(clang_args::implicit_module_maps(out_dir)); + arguments.push(clang_args::add_prebuilt_module_path(compiler, out_dir)); + + arguments.push(Argument::from("-o")); + } + CppCompiler::MSVC => { + arguments.push(Argument::from("/EHsc")); + arguments.push(Argument::from("/nologo")); + // If /std:c++20 this, else should be the direct options + // available on C++23 to use directly import std by precompiling the standard library + arguments.push(Argument::from("/experimental:module")); + arguments.push(Argument::from("/stdIfcDir \"$(VC_IFCPath)\"")); + + arguments.push(Argument::from("/ifcSearchDir")); + arguments.push(Argument::from( + out_dir + .join(compiler.as_ref()) + .join("modules") + .join("interfaces"), + )); + arguments.push(Argument::from(format!( + "/Fo{}\\", + out_dir.join(compiler.as_ref()).display() + ))); + } + CppCompiler::GCC => { + arguments.push(Argument::from("-fmodules-ts")); + arguments.push(Argument::from("-o")); + } + }; + + let fo = if compiler.eq(&CppCompiler::MSVC) { + "/Fo" + } else { "" }; + let obj_file = Argument::from(format!( + "{fo}{}", + out_dir + .join(compiler.as_ref()) + .join("sources") + .join(source.file_stem()) + .with_extension(compiler.get_obj_file_extension()) + .display() + )); + + arguments.push(obj_file.clone()); + arguments.push(Argument::from(source.file())); + + let command_line = ModuleCommandLine::from_translation_unit( + source, + arguments, + false, + CommandExecutionResult::default(), + ); + commands.sources.push(command_line); + commands.generated_files_paths.push(obj_file); + + Ok(()) + } + /// Generates the expected arguments for precompile the BMIs depending on self pub fn generate_module_interfaces_args<'a>( model: &'a ZorkModel, @@ -348,7 +448,10 @@ mod sources { } let command_line = ModuleCommandLine::from_translation_unit( - interface, arguments, false, CommandExecutionResult::default() + interface, + arguments, + false, + CommandExecutionResult::default(), ); commands.interfaces.push(command_line); } @@ -438,7 +541,10 @@ mod sources { } let command_line = ModuleCommandLine::from_translation_unit( - implementation, arguments, false, CommandExecutionResult::default() + implementation, + arguments, + false, + CommandExecutionResult::default(), ); commands.implementations.push(command_line); } @@ -500,7 +606,9 @@ mod helpers { "{mod_unit}.{}", if compiler.eq(&CppCompiler::MSVC) { compiler.get_obj_file_extension() - } else { compiler.get_typical_bmi_extension() } + } else { + compiler.get_typical_bmi_extension() + } )) } @@ -593,7 +701,11 @@ mod helpers { /// hasn't been modified since the last build process iteration. /// /// True means already processed and previous iteration Success - pub(crate) fn flag_modules_without_changes(compiler: &CppCompiler, cache: &ZorkCache, file: &Path) -> bool { + pub(crate) fn flag_modules_without_changes( + compiler: &CppCompiler, + cache: &ZorkCache, + file: &Path, + ) -> bool { if compiler.eq(&CppCompiler::CLANG) { log::trace!("Module unit {file:?} will be rebuilt since we've detected that you are using Clang"); } diff --git a/zork++/src/lib/lib.rs b/zork++/src/lib/lib.rs index 35f871f2..cb689437 100644 --- a/zork++/src/lib/lib.rs +++ b/zork++/src/lib/lib.rs @@ -1,3 +1,5 @@ +extern crate core; + pub mod bounds; pub mod cache; pub mod cli; @@ -156,7 +158,7 @@ pub mod worker { // Recursively create a directory and all of its parent components if they are missing let modules_path = Path::new(out_dir) - .join(compiler.to_string()) + .join(compiler.as_ref()) .join("modules"); let zork_path = out_dir.join("zork"); let zork_cache_path = zork_path.join("cache"); @@ -164,6 +166,7 @@ pub mod worker { utils::fs::create_directory(&modules_path.join("interfaces"))?; utils::fs::create_directory(&modules_path.join("implementations"))?; + utils::fs::create_directory(&out_dir.join(compiler.as_ref()).join("sources"))?; utils::fs::create_directory(&zork_cache_path.join(model.compiler.cpp_compiler.as_ref()))?; utils::fs::create_directory(&zork_intrinsics_path)?; diff --git a/zork++/src/lib/project_model/executable.rs b/zork++/src/lib/project_model/executable.rs index 05d27014..6e8de132 100644 --- a/zork++/src/lib/project_model/executable.rs +++ b/zork++/src/lib/project_model/executable.rs @@ -9,7 +9,7 @@ use super::sourceset::SourceSet; #[derive(Debug, PartialEq, Eq)] pub struct ExecutableModel<'a> { pub executable_name: &'a str, - pub sourceset: SourceSet<'a>, + pub sourceset: SourceSet, pub main: PathBuf, pub extra_args: Vec>, } @@ -24,8 +24,10 @@ impl<'a> ExecutableTarget<'a> for ExecutableModel<'a> { fn name(&'a self) -> &'a str { self.executable_name } - fn entry_point(&'a self) -> &'a Path { &self.main } - fn sourceset(&'a self) -> &'a SourceSet<'a> { + fn entry_point(&'a self) -> &'a Path { + &self.main + } + fn sourceset(&'a self) -> &'a SourceSet { &self.sourceset } } diff --git a/zork++/src/lib/project_model/sourceset.rs b/zork++/src/lib/project_model/sourceset.rs index fd008572..79541694 100644 --- a/zork++/src/lib/project_model/sourceset.rs +++ b/zork++/src/lib/project_model/sourceset.rs @@ -1,6 +1,8 @@ +use core::fmt; use std::path::{Path, PathBuf}; use color_eyre::{eyre::Context, Result}; +use crate::bounds::TranslationUnit; use crate::cli::output::arguments::Argument; @@ -10,6 +12,66 @@ pub enum Source<'a> { Glob(GlobPattern<'a>), } +#[derive(Debug, PartialEq, Eq)] +pub struct SourceFile { + pub path: PathBuf, + pub file_stem: String, + pub extension: String +} + +impl TranslationUnit for SourceFile { + fn file(&self) -> PathBuf { + let mut tmp = self.path.join(&self.file_stem).into_os_string(); + tmp.push("."); + tmp.push(&self.extension); + PathBuf::from(tmp) + } + + fn path(&self) -> PathBuf { + self.path.clone() + } + + fn file_stem(&self) -> String { + self.file_stem.clone() + } + + fn extension(&self) -> String { + self.extension.clone() + } +} + +impl TranslationUnit for &SourceFile { + fn file(&self) -> PathBuf { + let mut tmp = self.path.join(&self.file_stem).into_os_string(); + tmp.push("."); + tmp.push(&self.extension); + PathBuf::from(tmp) + } + + fn path(&self) -> PathBuf { + self.path.clone() + } + + fn file_stem(&self) -> String { + self.file_stem.clone() + } + + fn extension(&self) -> String { + self.extension.clone() + } +} + + +impl fmt::Display for SourceFile { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "({:?}/{:?}.{:?})", + self.path, self.file_stem, self.extension + ) + } +} + impl<'a> Source<'a> { #[inline(always)] pub fn paths(&self) -> Result> { @@ -33,22 +95,18 @@ impl<'a> GlobPattern<'a> { } #[derive(Debug, PartialEq, Eq)] -pub struct SourceSet<'a> { - pub base_path: &'a Path, - pub sources: Vec>, +pub struct SourceSet { + pub sources: Vec, } -impl<'a> SourceSet<'a> { - pub fn as_args_to(&'a self, dst: &mut Vec>) -> Result<()> { - let paths: Result>> = self.sources.iter().map(Source::paths).collect(); - - let paths = paths? - .into_iter() - .flatten() - .map(|path| self.base_path.join(path)) +impl SourceSet { + pub fn as_args_to(&self, dst: &mut Vec>) -> Result<()> { + let args = self.sources + .iter() + .map(|sf| sf.file()) .map(Argument::from); - dst.extend(paths); + dst.extend(args); Ok(()) } diff --git a/zork++/src/lib/project_model/tests.rs b/zork++/src/lib/project_model/tests.rs index a4d52534..1b1d40f5 100644 --- a/zork++/src/lib/project_model/tests.rs +++ b/zork++/src/lib/project_model/tests.rs @@ -9,7 +9,7 @@ use super::sourceset::SourceSet; #[derive(Debug, PartialEq, Eq)] pub struct TestsModel<'a> { pub test_executable_name: String, - pub sourceset: SourceSet<'a>, + pub sourceset: SourceSet, pub main: PathBuf, pub extra_args: Vec>, } @@ -24,8 +24,10 @@ impl<'a> ExecutableTarget<'a> for TestsModel<'a> { fn name(&'a self) -> &'a str { &self.test_executable_name } - fn entry_point(&'a self) -> &'a Path { &self.main } - fn sourceset(&'a self) -> &'a SourceSet<'a> { + fn entry_point(&'a self) -> &'a Path { + &self.main + } + fn sourceset(&'a self) -> &'a SourceSet { &self.sourceset } } diff --git a/zork++/src/lib/utils/fs.rs b/zork++/src/lib/utils/fs.rs index c4aae019..99487a0c 100644 --- a/zork++/src/lib/utils/fs.rs +++ b/zork++/src/lib/utils/fs.rs @@ -1,9 +1,13 @@ -use std::{fs::{DirBuilder, File}, io::{BufReader, Write}, path::Path}; +use std::{ + fs::{DirBuilder, File}, + io::{BufReader, Write}, + path::Path, +}; use std::path::PathBuf; -use color_eyre::{eyre::Context, Result}; use color_eyre::eyre::ContextCompat; +use color_eyre::{eyre::Context, Result}; use serde::{Deserialize, Serialize}; @@ -28,36 +32,69 @@ pub fn create_directory(path_create: &Path) -> Result<()> { /// Gets the absolute route for an element in the system given a path P, /// without the extension is P belongs to a file pub fn get_absolute_path>(p: P) -> Result { - let mut canonical = p.as_ref().canonicalize().with_context(|| format!("Error getting the canonical path for: {:?}", p.as_ref()))?; + let mut canonical = p + .as_ref() + .canonicalize() + .with_context(|| format!("Error getting the canonical path for: {:?}", p.as_ref()))?; if cfg!(target_os = "windows") { - canonical = canonical.to_str().map(|unc| &unc[4..]).unwrap_or_default().into() + canonical = canonical + .to_str() + .map(|unc| &unc[4..]) + .unwrap_or_default() + .into() } - let file_stem = canonical.file_stem().with_context(|| format!("Unable to get the file stem for {:?}", p.as_ref()))?; - let r = Ok(canonical.parent().unwrap_or_else(|| panic!("Unexpected error getting the parent of {:?}", p.as_ref())).join(file_stem)); - println!("Generated file: {:?}, file stem: {file_stem:?}, and canonical: {canonical:?}", &r); + let file_stem = canonical + .file_stem() + .with_context(|| format!("Unable to get the file stem for {:?}", p.as_ref()))?; + let r = Ok(canonical + .parent() + .unwrap_or_else(|| panic!("Unexpected error getting the parent of {:?}", p.as_ref())) + .join(file_stem)); + println!( + "Generated file: {:?}, file stem: {file_stem:?}, and canonical: {canonical:?}", + &r + ); r } /// pub fn get_file_details>(p: P) -> Result<(PathBuf, String, String)> { - let mut canonical = p.as_ref().canonicalize().with_context(|| format!("Error getting the canonical path for: {:?}", p.as_ref()))?; + let mut canonical = p + .as_ref() + .canonicalize() + .with_context(|| format!("Error getting the canonical path for: {:?}", p.as_ref()))?; if cfg!(target_os = "windows") { - canonical = canonical.to_str().map(|unc| &unc[4..]).unwrap_or_default().into() + canonical = canonical + .to_str() + .map(|unc| &unc[4..]) + .unwrap_or_default() + .into() } - let file_stem = canonical.file_stem().with_context(|| format!("Unable to get the file stem for {:?}", p.as_ref()))?; + let file_stem = canonical + .file_stem() + .with_context(|| format!("Unable to get the file stem for {:?}", p.as_ref()))?; Ok(( - canonical.parent().unwrap_or_else(|| panic!("Unexpected error getting the parent of {:?}", p.as_ref())).to_path_buf(), + canonical + .parent() + .unwrap_or_else(|| panic!("Unexpected error getting the parent of {:?}", p.as_ref())) + .to_path_buf(), file_stem.to_str().unwrap_or_default().to_string(), - canonical.extension() - .map_or_else(|| String::with_capacity(0), |os_str| os_str.to_str().unwrap_or_default().to_string()) + canonical.extension().map_or_else( + || String::with_capacity(0), + |os_str| os_str.to_str().unwrap_or_default().to_string(), + ), )) } /// Returns the declared extension for a file, if exists #[inline(always)] pub fn get_file_extension>(p: P) -> String { - p.as_ref().extension().and_then(|ext| ext.to_str()).unwrap_or_default().to_string() + p.as_ref() + .extension() + .and_then(|ext| ext.to_str()) + .unwrap_or_default() + .to_string() } pub fn serialize_object_to_file(path: &Path, data: &T) -> Result<()> diff --git a/zork++/src/lib/utils/reader.rs b/zork++/src/lib/utils/reader.rs index a64fa3c5..1626346b 100644 --- a/zork++/src/lib/utils/reader.rs +++ b/zork++/src/lib/utils/reader.rs @@ -1,26 +1,32 @@ -use crate::{cli::output::arguments::Argument, config_file::{ - build::BuildAttribute, - compiler::CompilerAttribute, - executable::ExecutableAttribute, - modules::{ModuleImplementation, ModuleInterface, ModulesAttribute}, - project::ProjectAttribute, - tests::TestsAttribute, - ZorkConfigFile, -}, project_model::{ - build::BuildModel, - compiler::CompilerModel, - executable::ExecutableModel, - modules::{ - ModuleImplementationModel, ModuleInterfaceModel, ModulePartitionModel, ModulesModel, +use crate::{ + cli::output::arguments::Argument, + config_file::{ + build::BuildAttribute, + compiler::CompilerAttribute, + executable::ExecutableAttribute, + modules::{ModuleImplementation, ModuleInterface, ModulesAttribute}, + project::ProjectAttribute, + tests::TestsAttribute, + ZorkConfigFile, }, - project::ProjectModel, - sourceset::{GlobPattern, Source, SourceSet}, - tests::TestsModel, - ZorkModel, -}, utils}; + project_model::{ + build::BuildModel, + compiler::CompilerModel, + executable::ExecutableModel, + modules::{ + ModuleImplementationModel, ModuleInterfaceModel, ModulePartitionModel, ModulesModel, + }, + project::ProjectModel, + sourceset::{GlobPattern, Source, SourceSet}, + tests::TestsModel, + ZorkModel, + }, + utils, +}; use color_eyre::{eyre::eyre, Result}; use std::path::{Path, PathBuf}; use walkdir::{DirEntry, WalkDir}; +use crate::project_model::sourceset::SourceFile; use super::constants::DEFAULT_OUTPUT_DIR; @@ -139,27 +145,14 @@ fn assemble_executable_model<'a>( let base_path = config.and_then(|exe| exe.sources_base_path).unwrap_or("."); - let sources = config + let sources= config .and_then(|exe| exe.sources.clone()) - .unwrap_or_default() - .into_iter() - .map(|source| { - if source.contains('.') { - Source::Glob(GlobPattern(source)) - } else { - Source::File(Path::new(source)) - } - }) - .collect(); + .unwrap_or_default(); - let sourceset = SourceSet { - base_path: Path::new(base_path), - sources, - }; + let sourceset = get_sourceset_for(sources); - let entry_point_file_path = Path::new(base_path) - .join(config.map_or("", |exe_attr| exe_attr.main)); - let main = utils::fs::get_absolute_path(entry_point_file_path) + let main_as_path = Path::new(config.map_or("", |exe_attr| exe_attr.main)); + let main = utils::fs::get_absolute_path(main_as_path) .unwrap_or_default(); let extra_args = config @@ -220,7 +213,7 @@ fn assemble_modules_model<'a>(config: &'a Option) -> ModulesMo fn assemble_module_interface_model<'a>( config: &'a ModuleInterface, - base_path: &str + base_path: &str, ) -> ModuleInterfaceModel<'a> { let file_path = Path::new(base_path).join(config.file); let module_name = config.module_name.unwrap_or_else(|| { @@ -240,15 +233,14 @@ fn assemble_module_interface_model<'a>( )) }; - let file_details = utils::fs::get_file_details(file_path) - .unwrap_or_default(); // TODO Care with this, refactor + let file_details = utils::fs::get_file_details(file_path).unwrap_or_default(); // TODO Care with this, refactor ModuleInterfaceModel { path: file_details.0, file_stem: file_details.1, extension: file_details.2, module_name, partition, - dependencies + dependencies, } } @@ -268,8 +260,7 @@ fn assemble_module_implementation_model<'a>( } } - let file_details = utils::fs::get_file_details(file_path) - .unwrap_or_default(); // TODO Care with this, refactor + let file_details = utils::fs::get_file_details(file_path).unwrap_or_default(); // TODO Care with this, refactor ModuleImplementationModel { path: file_details.0, @@ -294,25 +285,11 @@ fn assemble_tests_model<'a>( let sources = config .and_then(|exe| exe.sources.clone()) - .unwrap_or_default() - .into_iter() - .map(|source| { - if source.contains('.') { - Source::Glob(GlobPattern(source)) - } else { - Source::File(Path::new(source)) - } - }) - .collect(); - - let sourceset = SourceSet { - base_path: Path::new(base_path), - sources, - }; + .unwrap_or_default(); + let sourceset = get_sourceset_for(sources); - let entry_point_file_path = Path::new(base_path) - .join(config.map_or("defaulted", |test_attr| test_attr.main)); - let main = utils::fs::get_absolute_path(entry_point_file_path) + let main_as_path = Path::new(config.map_or("", |exe_attr| exe_attr.main)); + let main = utils::fs::get_absolute_path(main_as_path) .unwrap_or_default(); let extra_args = config @@ -328,14 +305,39 @@ fn assemble_tests_model<'a>( } } +fn get_sourceset_for(srcs: Vec<&str>) -> SourceSet { + let sources = srcs.into_iter() + .map(|src| { + if src.contains('*') { + Source::Glob(GlobPattern(src)) + } else { + Source::File(Path::new(src)) + } + }).map(|source| { + source.paths().expect( + "Error getting the declared paths for the source files" + ) + }).flatten() + .map(|pb| { + let file_details = utils::fs::get_file_details(pb).unwrap_or_default(); + SourceFile { + path: file_details.0, + file_stem: file_details.1, + extension: file_details.2, + } + }).collect(); + + SourceSet { sources } +} + #[cfg(test)] mod test { - use std::env; - use std::ffi::OsString; use crate::{ project_model::compiler::{CppCompiler, LanguageLevel, StdLib}, utils, }; + use std::env; + use std::ffi::OsString; use super::*; @@ -372,7 +374,6 @@ mod test { executable: ExecutableModel { executable_name: "Zork++", sourceset: SourceSet { - base_path: Path::new("."), sources: vec![], }, main: PathBuf::from("main.cpp"), @@ -388,7 +389,6 @@ mod test { tests: TestsModel { test_executable_name: "Zork++_test".to_string(), sourceset: SourceSet { - base_path: Path::new("."), sources: vec![], }, main: PathBuf::from("main.cpp"), @@ -425,8 +425,11 @@ mod test { executable: ExecutableModel { executable_name: "zork", sourceset: SourceSet { - base_path: Path::new("bin"), - sources: vec![Source::Glob(GlobPattern("*.cpp"))], + sources: vec![SourceFile{ + path: Default::default(), + file_stem: "".to_string(), + extension: "".to_string(), + }], }, main: PathBuf::from("main.cpp"), extra_args: vec![Argument::from("-Werr")], @@ -471,8 +474,11 @@ mod test { tests: TestsModel { test_executable_name: "zork_check".to_string(), sourceset: SourceSet { - base_path: Path::new("test"), - sources: vec![Source::Glob(GlobPattern("*.cpp"))], + sources: vec![SourceFile { + path: Default::default(), + file_stem: "".to_string(), + extension: "".to_string(), + }], }, main: PathBuf::from("main.cpp"), extra_args: vec![Argument::from("-pedantic")], From 63104f1d32a7d1a844e56549356f90b92491f9aa Mon Sep 17 00:00:00 2001 From: Alex Vergara Date: Thu, 9 Mar 2023 12:19:14 +0100 Subject: [PATCH 17/60] Giving to the linker the correct object files, and not the sources --- zork++/src/lib/compiler/mod.rs | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/zork++/src/lib/compiler/mod.rs b/zork++/src/lib/compiler/mod.rs index 7f7f5234..59605385 100644 --- a/zork++/src/lib/compiler/mod.rs +++ b/zork++/src/lib/compiler/mod.rs @@ -168,8 +168,7 @@ mod sources { use color_eyre::Result; use crate::project_model::sourceset::SourceFile; - /// Generates the command line arguments for non-module source files, including the one that - /// holds the main function + /// Generates the command line arguments for the desired target pub fn generate_main_command_line_args<'a>( model: &'a ZorkModel, commands: &mut Commands<'a>, @@ -209,8 +208,6 @@ mod sources { .with_extension(constants::BINARY_EXTENSION) .display() ))); - - arguments.extend(commands.generated_files_paths.clone().into_iter()); } CppCompiler::MSVC => { arguments.push(Argument::from("/EHsc")); @@ -241,7 +238,6 @@ mod sources { .with_extension(constants::BINARY_EXTENSION) .display() ))); - arguments.extend(commands.generated_files_paths.clone().into_iter()); } CppCompiler::GCC => { arguments.push(Argument::from("-fmodules-ts")); @@ -254,11 +250,10 @@ mod sources { .with_extension(constants::BINARY_EXTENSION) .display() ))); - arguments.extend(commands.generated_files_paths.clone().into_iter()); } }; + arguments.extend(commands.generated_files_paths.clone().into_iter()); - target.sourceset().as_args_to(&mut arguments)?; commands.main.main = target.entry_point(); commands.main.args.extend(arguments.into_iter()); commands.main.sources_paths = target From cef0a1bc20fd148373019d283ee8bae66af8ee52 Mon Sep 17 00:00:00 2001 From: Alex Vergara Date: Thu, 9 Mar 2023 13:16:27 +0100 Subject: [PATCH 18/60] Upgraded the lookup algorithm for retrieve the details of a cached source file --- zork++/src/lib/cache/mod.rs | 19 +------ zork++/src/lib/cli/output/commands.rs | 15 ++--- zork++/src/lib/compiler/mod.rs | 79 +++++++++++++++------------ 3 files changed, 54 insertions(+), 59 deletions(-) diff --git a/zork++/src/lib/cache/mod.rs b/zork++/src/lib/cache/mod.rs index 9609db31..afcbaa66 100644 --- a/zork++/src/lib/cache/mod.rs +++ b/zork++/src/lib/cache/mod.rs @@ -87,23 +87,10 @@ impl ZorkCache { let last_iteration_details = self.generated_commands.last(); if let Some(last_iteration) = last_iteration_details { - let found_as_ifc = last_iteration - .interfaces - .iter() + 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())); - - if found_as_ifc.is_some() { - return found_as_ifc; - } else { - let found_as_impl = last_iteration - .implementations - .iter() - .find(|comm_det| comm_det.file_path().eq(path.as_ref())); - - if found_as_impl.is_some() { - return found_as_impl; - } - } } None } diff --git a/zork++/src/lib/cli/output/commands.rs b/zork++/src/lib/cli/output/commands.rs index 9eb0cd92..23c70227 100644 --- a/zork++/src/lib/cli/output/commands.rs +++ b/zork++/src/lib/cli/output/commands.rs @@ -173,11 +173,8 @@ fn execute_command( } } -/// Holds a collection of heap allocated arguments. This is introduced in the -/// v0.7.0, for just wrapping the vector that holds the arguments, and for hold -/// a flag that will indicate us that this command line will be used in a module, -/// and that module was already built, and the module source file didn't change -/// since the last iteration of Zork++ +/// The pieces and details for the generated command line for +/// for some translation unit #[derive(Debug, Clone)] pub struct ModuleCommandLine<'a> { pub directory: PathBuf, @@ -218,14 +215,14 @@ impl<'a> IntoIterator for ModuleCommandLine<'a> { } #[derive(Debug)] -pub struct SourcesCommandLine<'a> { +pub struct ExecutableCommandLine<'a> { pub main: &'a Path, pub sources_paths: Vec, pub args: Vec>, pub execution_result: CommandExecutionResult, } -impl<'a> Default for SourcesCommandLine<'a> { +impl<'a> Default for ExecutableCommandLine<'a> { fn default() -> Self { Self { main: Path::new("."), @@ -244,7 +241,7 @@ pub struct Commands<'a> { pub interfaces: Vec>, pub implementations: Vec>, pub sources: Vec>, - pub main: SourcesCommandLine<'a>, + pub main: ExecutableCommandLine<'a>, pub generated_files_paths: Vec>, } @@ -256,7 +253,7 @@ impl<'a> Commands<'a> { interfaces: Vec::with_capacity(0), implementations: Vec::with_capacity(0), sources: Vec::with_capacity(0), - main: SourcesCommandLine::default(), + main: ExecutableCommandLine::default(), generated_files_paths: Vec::with_capacity(0), } } diff --git a/zork++/src/lib/compiler/mod.rs b/zork++/src/lib/compiler/mod.rs index 59605385..df658054 100644 --- a/zork++/src/lib/compiler/mod.rs +++ b/zork++/src/lib/compiler/mod.rs @@ -8,7 +8,7 @@ use std::path::Path; use crate::bounds::TranslationUnit; use crate::cli::output::commands::{CommandExecutionResult, ModuleCommandLine}; -use crate::compiler::helpers::flag_modules_without_changes; +use crate::compiler::helpers::flag_source_file_without_changes; use crate::{ cache::ZorkCache, cli::output::{arguments::Argument, commands::Commands}, @@ -38,7 +38,7 @@ pub fn build_project<'a>( // 1st - Build the modules build_modules(model, cache, &mut commands)?; // 2nd - Build the non module sources - build_sources(model, &mut commands, tests)?; + build_sources(model, cache, &mut commands, tests)?; // 3rd - Build the executable or the tests build_executable(model, &mut commands, tests)?; @@ -64,18 +64,28 @@ fn build_executable<'a>( fn build_sources<'a>( model: &'a ZorkModel<'_>, + cache: &'a ZorkCache, commands: &'_ mut Commands<'a>, tests: bool, ) -> Result<()> { - if tests { - for source in model.tests.sourceset.sources.iter() { - sources::generate_sources_arguments(model, commands, &model.tests, source)?; - } - } else { - for source in model.executable.sourceset.sources.iter() { - sources::generate_sources_arguments(model, commands, &model.executable, source)?; + log::info!("Building the source files..."); + let srcs = if tests { &model.tests.sourceset.sources } else { &model.executable.sourceset.sources }; + + srcs.iter().for_each(|src| { + if !flag_source_file_without_changes(&model.compiler.cpp_compiler, cache, &src.file()) { + sources::generate_sources_arguments(model, commands, &model.tests, src); + } else { + let command_line = ModuleCommandLine::from_translation_unit( + src, Vec::with_capacity(0), true, CommandExecutionResult::Cached + ); + + log::trace!("Source file: {:?} was not modified since the last iteration. No need to rebuilt it again.", &src.file()); + commands.implementations.push(command_line); + commands.generated_files_paths.push(helpers::generate_obj_file( + model.compiler.cpp_compiler, model.build.output_dir, src + )) } - } + }); Ok(()) } @@ -108,14 +118,14 @@ fn prebuild_module_interfaces<'a>( commands: &mut Commands<'a>, ) { interfaces.iter().for_each(|module_interface| { - if !flag_modules_without_changes(&model.compiler.cpp_compiler, cache, &module_interface.file()) { + if !flag_source_file_without_changes(&model.compiler.cpp_compiler, cache, &module_interface.file()) { sources::generate_module_interfaces_args(model, module_interface, commands); } else { let command_line = ModuleCommandLine::from_translation_unit( module_interface, Vec::with_capacity(0), true, CommandExecutionResult::Cached ); - log::trace!("Translation unit: {:?} was not modified since the last iteration. No need to rebuilt it again.", &module_interface.file()); + log::trace!("Source file:{:?} was not modified since the last iteration. No need to rebuilt it again.", &module_interface.file()); commands.interfaces.push(command_line); commands.generated_files_paths.push(Argument::from(helpers::generate_prebuild_miu( model.compiler.cpp_compiler, model.build.output_dir, module_interface @@ -133,14 +143,14 @@ fn compile_module_implementations<'a>( commands: &mut Commands<'a>, ) { impls.iter().for_each(|module_impl| { - if !flag_modules_without_changes(&model.compiler.cpp_compiler, cache, &module_impl.file()) { + if !flag_source_file_without_changes(&model.compiler.cpp_compiler, cache, &module_impl.file()) { sources::generate_module_implementation_args(model, module_impl, commands); } else { let command_line = ModuleCommandLine::from_translation_unit( module_impl, Vec::with_capacity(0), true, CommandExecutionResult::Cached ); - log::trace!("Translation unit: {:?} was not modified since the last iteration. No need to rebuilt it again.", &module_impl.file()); + log::trace!("Source file:{:?} was not modified since the last iteration. No need to rebuilt it again.", &module_impl.file()); commands.implementations.push(command_line); commands.generated_files_paths.push(Argument::from(helpers::generate_impl_obj_file( model.compiler.cpp_compiler, model.build.output_dir, module_impl @@ -272,9 +282,7 @@ mod sources { commands: &mut Commands<'a>, target: &'a impl ExecutableTarget<'a>, source: &'a SourceFile, - ) -> Result<()> { - log::info!("Building the source files..."); - + ) { let compiler = model.compiler.cpp_compiler; let out_dir = model.build.output_dir; @@ -318,19 +326,7 @@ mod sources { } }; - let fo = if compiler.eq(&CppCompiler::MSVC) { - "/Fo" - } else { "" }; - let obj_file = Argument::from(format!( - "{fo}{}", - out_dir - .join(compiler.as_ref()) - .join("sources") - .join(source.file_stem()) - .with_extension(compiler.get_obj_file_extension()) - .display() - )); - + let obj_file = helpers::generate_obj_file(compiler, out_dir, source); arguments.push(obj_file.clone()); arguments.push(Argument::from(source.file())); @@ -341,9 +337,7 @@ mod sources { CommandExecutionResult::default(), ); commands.sources.push(command_line); - commands.generated_files_paths.push(obj_file); - - Ok(()) + commands.generated_files_paths.push(obj_file) } /// Generates the expected arguments for precompile the BMIs depending on self @@ -558,6 +552,7 @@ mod helpers { cli::output::commands::{CommandExecutionResult, ModuleCommandLine}, }; use std::path::PathBuf; + use crate::project_model::sourceset::SourceFile; /// Creates the path for a prebuilt module interface, based on the default expected /// extension for BMI's given a compiler @@ -696,7 +691,7 @@ mod helpers { /// hasn't been modified since the last build process iteration. /// /// True means already processed and previous iteration Success - pub(crate) fn flag_modules_without_changes( + pub(crate) fn flag_source_file_without_changes( compiler: &CppCompiler, cache: &ZorkCache, file: &Path, @@ -736,4 +731,20 @@ mod helpers { false } } + + pub(crate) fn generate_obj_file<'a>(compiler: CppCompiler, out_dir: &Path, source: &SourceFile) -> Argument<'a> { + let fo = if compiler.eq(&CppCompiler::MSVC) { + "/Fo" + } else { "" }; + + Argument::from(format!( + "{fo}{}", + out_dir + .join(compiler.as_ref()) + .join("sources") + .join(source.file_stem()) + .with_extension(compiler.get_obj_file_extension()) + .display() + )) + } } From ad7fd9aae408f05ecb181689a45fbc49f7155265 Mon Sep 17 00:00:00 2001 From: Alex Vergara Date: Thu, 9 Mar 2023 13:43:51 +0100 Subject: [PATCH 19/60] System modules now deserves their own place, without being merged with user module interfaces --- zork++/src/lib/cache/mod.rs | 6 +- zork++/src/lib/cli/output/commands.rs | 122 +++++++++++++------------- zork++/src/lib/compiler/mod.rs | 34 +++---- 3 files changed, 79 insertions(+), 83 deletions(-) diff --git a/zork++/src/lib/cache/mod.rs b/zork++/src/lib/cache/mod.rs index afcbaa66..a25d1850 100644 --- a/zork++/src/lib/cache/mod.rs +++ b/zork++/src/lib/cache/mod.rs @@ -15,7 +15,7 @@ use crate::utils::constants::COMPILATION_DATABASE; use crate::{ cli::{ input::CliArgs, - output::commands::{CommandExecutionResult, Commands, ModuleCommandLine}, + output::commands::{CommandExecutionResult, Commands, SourceCommandLine}, }, project_model::{compiler::CppCompiler, ZorkModel}, utils::{ @@ -314,7 +314,7 @@ impl ZorkCache { fn normalize_execution_result_status( &self, - module_command_line: &ModuleCommandLine, + module_command_line: &SourceCommandLine, ) -> CommandExecutionResult { if module_command_line .execution_result @@ -330,7 +330,7 @@ impl ZorkCache { } } - fn set_module_generated_command_line(&self, module_command_line: &ModuleCommandLine) -> String { + fn set_module_generated_command_line(&self, module_command_line: &SourceCommandLine) -> String { if module_command_line.processed { String::with_capacity(0) } else { diff --git a/zork++/src/lib/cli/output/commands.rs b/zork++/src/lib/cli/output/commands.rs index 23c70227..14268b71 100644 --- a/zork++/src/lib/cli/output/commands.rs +++ b/zork++/src/lib/cli/output/commands.rs @@ -2,6 +2,7 @@ use std::{ path::{Path, PathBuf}, process::ExitStatus, }; +use std::collections::HashMap; use crate::bounds::TranslationUnit; ///! Contains helpers and data structure to process in @@ -25,64 +26,63 @@ pub fn run_generated_commands( mut commands: Commands<'_>, cache: ZorkCache, ) -> Result { - if !commands.interfaces.is_empty() { - log::debug!("Executing the commands for the module interfaces..."); + log::info!("Proceeding to execute the generated commands..."); + let mut total_exec_commands = 0; + let compiler = commands.compiler; - for miu in commands.interfaces.iter_mut() { - if !miu.processed { - let r = execute_command(&commands.compiler, &miu.args, &cache); - miu.execution_result = CommandExecutionResult::from(&r); - if let Err(e) = r { - cache::save(program_data, cache, commands)?; - return Err(e); - } else if !r.as_ref().unwrap().success() { - let c_miu = miu.clone(); - cache::save(program_data, cache, commands)?; - return Err(eyre!( - "Ending the program, because the build of: {:?} wasn't ended successfully", - c_miu.file - )); - } + for sys_module in &commands.system_modules { + execute_command(compiler, sys_module.1, &cache)?; + } + + for miu in commands.interfaces.iter_mut() { + if !miu.processed { + let r = execute_command(compiler, &miu.args, &cache); + miu.execution_result = CommandExecutionResult::from(&r); + total_exec_commands += 1; + if let Err(e) = r { + cache::save(program_data, cache, commands)?; + return Err(e); + } else if !r.as_ref().unwrap().success() { + let c_miu = miu.clone(); + cache::save(program_data, cache, commands)?; + return Err(eyre!( + "Ending the program, because the build of: {:?} wasn't ended successfully", + c_miu.file + )); } } } - if !commands.implementations.is_empty() { - log::debug!("Executing the commands for the module implementations..."); - - for implm in &mut commands.implementations { - if !implm.processed { - let r = execute_command(&commands.compiler, &implm.args, &cache); - implm.execution_result = CommandExecutionResult::from(&r); - if let Err(e) = r { - return Err(e); - } else if !r.as_ref().unwrap().success() { - let c_miu = implm.clone(); - return Err(eyre!( - "Ending the program, because the build of: {:?} wasn't ended successfully", - c_miu.file - )); - } + for implm in &mut commands.implementations { + if !implm.processed { + let r = execute_command(compiler, &implm.args, &cache); + implm.execution_result = CommandExecutionResult::from(&r); + total_exec_commands += 1; + if let Err(e) = r { + return Err(e); + } else if !r.as_ref().unwrap().success() { + let c_miu = implm.clone(); + return Err(eyre!( + "Ending the program, because the build of: {:?} wasn't ended successfully", + c_miu.file + )); } } } - if !commands.sources.is_empty() { - log::debug!("Executing the commands for the source files..."); - - for source in &mut commands.sources { - if !source.processed { - let r = execute_command(&commands.compiler, &source.args, &cache); - source.execution_result = CommandExecutionResult::from(&r); - if let Err(e) = r { - return Err(e); - } else if !r.as_ref().unwrap().success() { - let c_miu = source.clone(); - return Err(eyre!( - "Ending the program, because the build of: {:?} wasn't ended successfully", - c_miu.file - )); - } + for source in &mut commands.sources { + if !source.processed { + let r = execute_command(compiler, &source.args, &cache); + source.execution_result = CommandExecutionResult::from(&r); + total_exec_commands += 1; + if let Err(e) = r { + return Err(e); + } else if !r.as_ref().unwrap().success() { + let c_miu = source.clone(); + return Err(eyre!( + "Ending the program, because the build of: {:?} wasn't ended successfully", + c_miu.file + )); } } } @@ -90,8 +90,9 @@ pub fn run_generated_commands( if !commands.main.args.is_empty() { log::debug!("Executing the main command line..."); - let r = execute_command(&commands.compiler, &commands.main.args, &cache); + let r = execute_command(compiler, &commands.main.args, &cache); commands.main.execution_result = CommandExecutionResult::from(&r); + total_exec_commands += 1; if let Err(e) = r { cache::save(program_data, cache, commands)?; @@ -104,6 +105,7 @@ pub fn run_generated_commands( } } + log::info!("A total of: {total_exec_commands} has been successfully executed"); cache::save(program_data, cache, commands)?; Ok(CommandExecutionResult::Success) } @@ -140,7 +142,7 @@ pub fn autorun_generated_binary( /// Executes a new [`std::process::Command`] configured according the choosen /// compiler and the current operating system fn execute_command( - compiler: &CppCompiler, + compiler: CppCompiler, arguments: &[Argument<'_>], cache: &ZorkCache, ) -> Result { @@ -176,7 +178,7 @@ fn execute_command( /// The pieces and details for the generated command line for /// for some translation unit #[derive(Debug, Clone)] -pub struct ModuleCommandLine<'a> { +pub struct SourceCommandLine<'a> { pub directory: PathBuf, pub file: String, pub args: Vec>, @@ -184,7 +186,7 @@ pub struct ModuleCommandLine<'a> { pub execution_result: CommandExecutionResult, } -impl<'a> ModuleCommandLine<'a> { +impl<'a> SourceCommandLine<'a> { pub fn from_translation_unit( tu: impl TranslationUnit, args: Vec>, @@ -205,7 +207,7 @@ impl<'a> ModuleCommandLine<'a> { } } -impl<'a> IntoIterator for ModuleCommandLine<'a> { +impl<'a> IntoIterator for SourceCommandLine<'a> { type Item = Argument<'a>; type IntoIter = std::vec::IntoIter>; @@ -237,10 +239,10 @@ impl<'a> Default for ExecutableCommandLine<'a> { #[derive(Debug)] pub struct Commands<'a> { pub compiler: CppCompiler, - pub system_modules: Vec>>, - pub interfaces: Vec>, - pub implementations: Vec>, - pub sources: Vec>, + pub system_modules: HashMap>>, + pub interfaces: Vec>, + pub implementations: Vec>, + pub sources: Vec>, pub main: ExecutableCommandLine<'a>, pub generated_files_paths: Vec>, } @@ -249,7 +251,7 @@ impl<'a> Commands<'a> { pub fn new(compiler: &'a CppCompiler) -> Self { Self { compiler: *compiler, - system_modules: Vec::with_capacity(0), + system_modules: HashMap::with_capacity(0), interfaces: Vec::with_capacity(0), implementations: Vec::with_capacity(0), sources: Vec::with_capacity(0), @@ -263,7 +265,7 @@ impl<'a> core::fmt::Display for Commands<'a> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!( f, - "Commands for [{}]:\n- Interfaces: {:?},\n- Implementations: {:?},\n- Main command line: {:?}", + "Commands for [{}]:\n- Interfaces: {:?},\n- Implementations: {:?},\n- Sources: {:?}", self.compiler, self.interfaces.iter().map(|vec| { vec.args.iter().map(|e| e.value).collect::>().join(" "); }), self.implementations.iter().map(|vec| { vec.args.iter().map(|e| e.value).collect::>().join(" "); }), diff --git a/zork++/src/lib/compiler/mod.rs b/zork++/src/lib/compiler/mod.rs index df658054..138db4a9 100644 --- a/zork++/src/lib/compiler/mod.rs +++ b/zork++/src/lib/compiler/mod.rs @@ -7,7 +7,7 @@ use color_eyre::Result; use std::path::Path; use crate::bounds::TranslationUnit; -use crate::cli::output::commands::{CommandExecutionResult, ModuleCommandLine}; +use crate::cli::output::commands::{CommandExecutionResult, SourceCommandLine}; use crate::compiler::helpers::flag_source_file_without_changes; use crate::{ cache::ZorkCache, @@ -75,7 +75,7 @@ fn build_sources<'a>( if !flag_source_file_without_changes(&model.compiler.cpp_compiler, cache, &src.file()) { sources::generate_sources_arguments(model, commands, &model.tests, src); } else { - let command_line = ModuleCommandLine::from_translation_unit( + let command_line = SourceCommandLine::from_translation_unit( src, Vec::with_capacity(0), true, CommandExecutionResult::Cached ); @@ -121,7 +121,7 @@ fn prebuild_module_interfaces<'a>( if !flag_source_file_without_changes(&model.compiler.cpp_compiler, cache, &module_interface.file()) { sources::generate_module_interfaces_args(model, module_interface, commands); } else { - let command_line = ModuleCommandLine::from_translation_unit( + let command_line = SourceCommandLine::from_translation_unit( module_interface, Vec::with_capacity(0), true, CommandExecutionResult::Cached ); @@ -146,7 +146,7 @@ fn compile_module_implementations<'a>( if !flag_source_file_without_changes(&model.compiler.cpp_compiler, cache, &module_impl.file()) { sources::generate_module_implementation_args(model, module_impl, commands); } else { - let command_line = ModuleCommandLine::from_translation_unit( + let command_line = SourceCommandLine::from_translation_unit( module_impl, Vec::with_capacity(0), true, CommandExecutionResult::Cached ); @@ -166,7 +166,7 @@ mod sources { bounds::{ExecutableTarget, TranslationUnit}, cli::output::{ arguments::{clang_args, Argument}, - commands::{CommandExecutionResult, Commands, ModuleCommandLine}, + commands::{CommandExecutionResult, Commands, SourceCommandLine}, }, project_model::{ compiler::CppCompiler, @@ -330,7 +330,7 @@ mod sources { arguments.push(obj_file.clone()); arguments.push(Argument::from(source.file())); - let command_line = ModuleCommandLine::from_translation_unit( + let command_line = SourceCommandLine::from_translation_unit( source, arguments, false, @@ -436,7 +436,7 @@ mod sources { } } - let command_line = ModuleCommandLine::from_translation_unit( + let command_line = SourceCommandLine::from_translation_unit( interface, arguments, false, @@ -529,7 +529,7 @@ mod sources { } } - let command_line = ModuleCommandLine::from_translation_unit( + let command_line = SourceCommandLine::from_translation_unit( implementation, arguments, false, @@ -549,7 +549,7 @@ mod helpers { use crate::{ bounds::TranslationUnit, cache::ZorkCache, - cli::output::commands::{CommandExecutionResult, ModuleCommandLine}, + cli::output::commands::{CommandExecutionResult, SourceCommandLine}, }; use std::path::PathBuf; use crate::project_model::sourceset::SourceFile; @@ -671,21 +671,15 @@ mod helpers { }) .collect::>(); - // TODO Cached sys headers will deserve in a near future its own type, - // storing the commands and the paths there, and checking for rebuild them - // independently, also check for execution independently for collection_args in sys_modules { - commands.interfaces.push(ModuleCommandLine { - directory: PathBuf::with_capacity(0), - file: collection_args[4].value.to_string(), - args: collection_args, - processed: false, - execution_result: CommandExecutionResult::default(), - }); + commands.system_modules.insert( + collection_args[4].value.to_string(), + collection_args + ); } } - /// Marks the given module translation unit as already processed, + /// Marks the given source file as already processed, /// or if it should be reprocessed again due to a previous failure status, /// to avoid losing time rebuilding that module if the source file /// hasn't been modified since the last build process iteration. From edb5e13bf092ef199a7cd2098ef63b24efc93a10 Mon Sep 17 00:00:00 2001 From: Alex Vergara Date: Thu, 9 Mar 2023 13:51:10 +0100 Subject: [PATCH 20/60] Cleaned and merged the logic for executing commands for interfaces, implementations and non module source files --- zork++/src/lib/cli/output/commands.rs | 58 +++++++-------------------- 1 file changed, 14 insertions(+), 44 deletions(-) diff --git a/zork++/src/lib/cli/output/commands.rs b/zork++/src/lib/cli/output/commands.rs index 14268b71..5f2c3c70 100644 --- a/zork++/src/lib/cli/output/commands.rs +++ b/zork++/src/lib/cli/output/commands.rs @@ -34,55 +34,25 @@ pub fn run_generated_commands( execute_command(compiler, sys_module.1, &cache)?; } - for miu in commands.interfaces.iter_mut() { - if !miu.processed { - let r = execute_command(compiler, &miu.args, &cache); - miu.execution_result = CommandExecutionResult::from(&r); - total_exec_commands += 1; - if let Err(e) = r { - cache::save(program_data, cache, commands)?; - return Err(e); - } else if !r.as_ref().unwrap().success() { - let c_miu = miu.clone(); - cache::save(program_data, cache, commands)?; - return Err(eyre!( - "Ending the program, because the build of: {:?} wasn't ended successfully", - c_miu.file - )); - } - } - } + let sources = commands.interfaces.iter_mut() + .chain(commands.implementations.iter_mut()) + .chain(commands.sources.iter_mut()); - for implm in &mut commands.implementations { - if !implm.processed { - let r = execute_command(compiler, &implm.args, &cache); - implm.execution_result = CommandExecutionResult::from(&r); - total_exec_commands += 1; - if let Err(e) = r { - return Err(e); - } else if !r.as_ref().unwrap().success() { - let c_miu = implm.clone(); - return Err(eyre!( - "Ending the program, because the build of: {:?} wasn't ended successfully", - c_miu.file - )); - } - } - } - - for source in &mut commands.sources { - if !source.processed { - let r = execute_command(compiler, &source.args, &cache); - source.execution_result = CommandExecutionResult::from(&r); + for source_file in sources { + if !source_file.processed { + let r = execute_command(compiler, &source_file.args, &cache); + source_file.execution_result = CommandExecutionResult::from(&r); total_exec_commands += 1; if let Err(e) = r { + cache::save(program_data, cache, commands)?; return Err(e); } else if !r.as_ref().unwrap().success() { - let c_miu = source.clone(); - return Err(eyre!( + let err = eyre!( "Ending the program, because the build of: {:?} wasn't ended successfully", - c_miu.file - )); + source_file + ); + cache::save(program_data, cache, commands)?; + return Err(err); } } } @@ -105,7 +75,7 @@ pub fn run_generated_commands( } } - log::info!("A total of: {total_exec_commands} has been successfully executed"); + log::info!("A total of: {total_exec_commands} command lines has been executed successfully"); cache::save(program_data, cache, commands)?; Ok(CommandExecutionResult::Success) } From c5327f85c75683ed5c1f5c6c832edf228d5f4ee2 Mon Sep 17 00:00:00 2001 From: Alex Vergara Date: Thu, 9 Mar 2023 14:05:30 +0100 Subject: [PATCH 21/60] Cleaned and merged the logic for executing commands for interfaces, implementations and non module source files --- zork++/src/lib/cli/output/commands.rs | 2 +- zork++/src/lib/compiler/mod.rs | 28 +++++++++++---------------- zork++/src/lib/utils/fs.rs | 9 ++------- 3 files changed, 14 insertions(+), 25 deletions(-) diff --git a/zork++/src/lib/cli/output/commands.rs b/zork++/src/lib/cli/output/commands.rs index 5f2c3c70..045317c0 100644 --- a/zork++/src/lib/cli/output/commands.rs +++ b/zork++/src/lib/cli/output/commands.rs @@ -75,7 +75,7 @@ pub fn run_generated_commands( } } - log::info!("A total of: {total_exec_commands} command lines has been executed successfully"); + log::debug!("A total of: {total_exec_commands} command lines has been executed successfully"); cache::save(program_data, cache, commands)?; Ok(CommandExecutionResult::Success) } diff --git a/zork++/src/lib/compiler/mod.rs b/zork++/src/lib/compiler/mod.rs index 138db4a9..7f1c326e 100644 --- a/zork++/src/lib/compiler/mod.rs +++ b/zork++/src/lib/compiler/mod.rs @@ -81,9 +81,9 @@ fn build_sources<'a>( log::trace!("Source file: {:?} was not modified since the last iteration. No need to rebuilt it again.", &src.file()); commands.implementations.push(command_line); - commands.generated_files_paths.push(helpers::generate_obj_file( + commands.generated_files_paths.push(Argument::from(helpers::generate_obj_file( model.compiler.cpp_compiler, model.build.output_dir, src - )) + ).to_string())) } }); @@ -176,6 +176,7 @@ mod sources { utils::constants, }; use color_eyre::Result; + use crate::cli::output::arguments; use crate::project_model::sourceset::SourceFile; /// Generates the command line arguments for the desired target @@ -315,10 +316,6 @@ mod sources { .join("modules") .join("interfaces"), )); - arguments.push(Argument::from(format!( - "/Fo{}\\", - out_dir.join(compiler.as_ref()).display() - ))); } CppCompiler::GCC => { arguments.push(Argument::from("-fmodules-ts")); @@ -327,7 +324,8 @@ mod sources { }; let obj_file = helpers::generate_obj_file(compiler, out_dir, source); - arguments.push(obj_file.clone()); + let fo = if compiler.eq(&CppCompiler::MSVC) { "/Fo" } else { "" }; + arguments.push(Argument::from(format!("{fo}{obj_file}"))); arguments.push(Argument::from(source.file())); let command_line = SourceCommandLine::from_translation_unit( @@ -337,7 +335,7 @@ mod sources { CommandExecutionResult::default(), ); commands.sources.push(command_line); - commands.generated_files_paths.push(obj_file) + commands.generated_files_paths.push(Argument::from(obj_file.to_string())) } /// Generates the expected arguments for precompile the BMIs depending on self @@ -543,13 +541,14 @@ mod sources { /// kind of workflow that should be done with this parse, format and /// generate. mod helpers { + use std::fmt::Display; use chrono::{DateTime, Utc}; use super::*; use crate::{ bounds::TranslationUnit, cache::ZorkCache, - cli::output::commands::{CommandExecutionResult, SourceCommandLine}, + cli::output::commands::CommandExecutionResult }; use std::path::PathBuf; use crate::project_model::sourceset::SourceFile; @@ -726,19 +725,14 @@ mod helpers { } } - pub(crate) fn generate_obj_file<'a>(compiler: CppCompiler, out_dir: &Path, source: &SourceFile) -> Argument<'a> { - let fo = if compiler.eq(&CppCompiler::MSVC) { - "/Fo" - } else { "" }; - - Argument::from(format!( - "{fo}{}", + pub(crate) fn generate_obj_file<'a>(compiler: CppCompiler, out_dir: &Path, source: &SourceFile) -> impl Display { + format!("{}", out_dir .join(compiler.as_ref()) .join("sources") .join(source.file_stem()) .with_extension(compiler.get_obj_file_extension()) .display() - )) + ) } } diff --git a/zork++/src/lib/utils/fs.rs b/zork++/src/lib/utils/fs.rs index 99487a0c..32a07c8b 100644 --- a/zork++/src/lib/utils/fs.rs +++ b/zork++/src/lib/utils/fs.rs @@ -46,15 +46,10 @@ pub fn get_absolute_path>(p: P) -> Result { let file_stem = canonical .file_stem() .with_context(|| format!("Unable to get the file stem for {:?}", p.as_ref()))?; - let r = Ok(canonical + Ok(canonical .parent() .unwrap_or_else(|| panic!("Unexpected error getting the parent of {:?}", p.as_ref())) - .join(file_stem)); - println!( - "Generated file: {:?}, file stem: {file_stem:?}, and canonical: {canonical:?}", - &r - ); - r + .join(file_stem)) } /// From 9f4055c58b88983905a1b376d68ef2eed31c28b9 Mon Sep 17 00:00:00 2001 From: Alex Vergara Date: Thu, 9 Mar 2023 17:47:45 +0100 Subject: [PATCH 22/60] Removing the generated command lines for the per cycle cache execution tracker --- CHANGELOG.md | 1 + zork++/Cargo.toml | 2 +- zork++/src/lib/bounds/mod.rs | 3 +- zork++/src/lib/cache/mod.rs | 35 +++++---------- zork++/src/lib/cli/output/commands.rs | 43 ++++++++++++++----- zork++/src/lib/compiler/mod.rs | 46 ++++++++++++-------- zork++/src/lib/config_file/executable.rs | 4 -- zork++/src/lib/config_file/tests.rs | 4 -- zork++/src/lib/project_model/executable.rs | 5 --- zork++/src/lib/project_model/tests.rs | 5 --- zork++/src/lib/utils/reader.rs | 50 +++++++--------------- 11 files changed, 89 insertions(+), 109 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b11c5a61..6aa6d553 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,7 @@ assembling and linking. - Due to the described above, now the main task of the main command line is to link the generated object files together, introducing the necessary dependencies - Non module source files now have their explicit types and operations +- Internal deps: toml raised to the 0.7.2, criterion raised to its 0.4.0v ### Fix diff --git a/zork++/Cargo.toml b/zork++/Cargo.toml index d7660df0..b7f104f0 100644 --- a/zork++/Cargo.toml +++ b/zork++/Cargo.toml @@ -28,7 +28,7 @@ serde_json = "1.0" [dev-dependencies] tempfile = "3.0.0" -criterion = "0.3" +criterion = "0.4.0" [[test]] name = "zork_integration_tests" diff --git a/zork++/src/lib/bounds/mod.rs b/zork++/src/lib/bounds/mod.rs index 00af6a34..e632ba7f 100644 --- a/zork++/src/lib/bounds/mod.rs +++ b/zork++/src/lib/bounds/mod.rs @@ -1,6 +1,6 @@ use core::fmt::Debug; use std::fmt::Display; -use std::path::{Path, PathBuf}; +use std::path::PathBuf; use crate::{cli::output::arguments::Argument, project_model::sourceset::SourceSet}; @@ -12,7 +12,6 @@ pub trait ExtraArgs<'a> { pub trait ExecutableTarget<'a>: ExtraArgs<'a> { fn name(&'a self) -> &'a str; - fn entry_point(&'a self) -> &'a Path; fn sourceset(&'a self) -> &'a SourceSet; } diff --git a/zork++/src/lib/cache/mod.rs b/zork++/src/lib/cache/mod.rs index a25d1850..49f64117 100644 --- a/zork++/src/lib/cache/mod.rs +++ b/zork++/src/lib/cache/mod.rs @@ -8,9 +8,7 @@ use std::{ fs::File, path::{Path, PathBuf}, }; -use walkdir::WalkDir; -use crate::cli::output::arguments::Argument; use crate::utils::constants::COMPILATION_DATABASE; use crate::{ cli::{ @@ -24,6 +22,7 @@ use crate::{ }, }; use serde::{Deserialize, Serialize}; +use walkdir::WalkDir; /// Standalone utility for retrieve the Zork++ cache file pub fn load(program_data: &ZorkModel<'_>, cli_args: &CliArgs) -> Result { @@ -58,6 +57,7 @@ pub fn save( program_data: &ZorkModel<'_>, mut cache: ZorkCache, commands: Commands<'_>, + test_mode: bool ) -> Result<()> { let cache_path = &Path::new(program_data.build.output_dir) .join("zork") @@ -65,7 +65,7 @@ pub fn save( .join(program_data.compiler.cpp_compiler.as_ref()) .join(constants::ZORK_CACHE_FILENAME); - cache.run_final_tasks(program_data, commands)?; + cache.run_final_tasks(program_data, commands, test_mode)?; cache.last_program_execution = Utc::now(); utils::fs::serialize_object_to_file(cache_path, &cache) @@ -87,7 +87,9 @@ impl ZorkCache { let last_iteration_details = self.generated_commands.last(); if let Some(last_iteration) = last_iteration_details { - return last_iteration.interfaces.iter() + 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())); @@ -113,8 +115,9 @@ impl ZorkCache { &mut self, program_data: &ZorkModel<'_>, commands: Commands<'_>, + test_mode: bool ) -> Result<()> { - if self.save_generated_commands(&commands) && program_data.project.compilation_db { + if self.save_generated_commands(&commands, test_mode) && program_data.project.compilation_db { map_generated_commands_to_compilation_db(self)?; } @@ -130,7 +133,7 @@ impl ZorkCache { Ok(()) } - fn save_generated_commands(&mut self, commands: &Commands<'_>) -> bool { + fn save_generated_commands(&mut self, commands: &Commands<'_>, test_mode: bool) -> bool { log::trace!("Storing in the cache the last generated command lines..."); let mut has_changes = false; @@ -171,7 +174,6 @@ impl ZorkCache { .to_string(), file: module_command_line.file.clone(), execution_result: self.normalize_execution_result_status(module_command_line), - command: self.set_module_generated_command_line(module_command_line), } })); @@ -196,7 +198,6 @@ impl ZorkCache { .to_string(), file: module_command_line.file.clone(), execution_result: self.normalize_execution_result_status(module_command_line), - command: self.set_module_generated_command_line(module_command_line), } })); @@ -221,7 +222,6 @@ impl ZorkCache { .to_string(), file: source_command_line.file.clone(), execution_result: self.normalize_execution_result_status(source_command_line), - command: self.set_module_generated_command_line(source_command_line), } })); @@ -237,8 +237,9 @@ impl ZorkCache { .join(" "), }; + // TODO Replace for target, or whatever, so it doesn't goes to the main command line self.last_generated_commands - .entry(PathBuf::from(commands.main.main)) // provisional + .entry(PathBuf::from("main")) // provisional .or_insert_with(|| { has_changes = true; vec![commands_details.main.command.clone()] @@ -329,19 +330,6 @@ impl ZorkCache { module_command_line.execution_result.clone() } } - - fn set_module_generated_command_line(&self, module_command_line: &SourceCommandLine) -> String { - if module_command_line.processed { - String::with_capacity(0) - } else { - module_command_line - .args - .iter() - .map(|argument| argument.value) - .collect::>() - .join(" ") - } - } } /// Generates the `compile_commands.json` file, that acts as a compilation database @@ -402,7 +390,6 @@ pub struct CommandDetail { directory: String, file: String, pub execution_result: CommandExecutionResult, - command: String, } impl CommandDetail { diff --git a/zork++/src/lib/cli/output/commands.rs b/zork++/src/lib/cli/output/commands.rs index 045317c0..1a6e8d50 100644 --- a/zork++/src/lib/cli/output/commands.rs +++ b/zork++/src/lib/cli/output/commands.rs @@ -1,8 +1,8 @@ +use std::collections::HashMap; use std::{ path::{Path, PathBuf}, process::ExitStatus, }; -use std::collections::HashMap; use crate::bounds::TranslationUnit; ///! Contains helpers and data structure to process in @@ -25,6 +25,7 @@ pub fn run_generated_commands( program_data: &ZorkModel<'_>, mut commands: Commands<'_>, cache: ZorkCache, + test_mode: bool ) -> Result { log::info!("Proceeding to execute the generated commands..."); let mut total_exec_commands = 0; @@ -34,7 +35,9 @@ pub fn run_generated_commands( execute_command(compiler, sys_module.1, &cache)?; } - let sources = commands.interfaces.iter_mut() + let sources = commands + .interfaces + .iter_mut() .chain(commands.implementations.iter_mut()) .chain(commands.sources.iter_mut()); @@ -44,14 +47,14 @@ pub fn run_generated_commands( source_file.execution_result = CommandExecutionResult::from(&r); total_exec_commands += 1; if let Err(e) = r { - cache::save(program_data, cache, commands)?; + cache::save(program_data, cache, commands, test_mode)?; return Err(e); } else if !r.as_ref().unwrap().success() { let err = eyre!( "Ending the program, because the build of: {:?} wasn't ended successfully", source_file ); - cache::save(program_data, cache, commands)?; + cache::save(program_data, cache, commands, test_mode)?; return Err(err); } } @@ -65,10 +68,10 @@ pub fn run_generated_commands( total_exec_commands += 1; if let Err(e) = r { - cache::save(program_data, cache, commands)?; + cache::save(program_data, cache, commands, test_mode)?; // Here because use of moved value return Err(e); } else if !r.as_ref().unwrap().success() { - cache::save(program_data, cache, commands)?; + cache::save(program_data, cache, commands, test_mode)?; // Here because use of moved value return Err(eyre!( "Ending the program, because the main command line execution wasn't ended successfully", )); @@ -76,7 +79,7 @@ pub fn run_generated_commands( } log::debug!("A total of: {total_exec_commands} command lines has been executed successfully"); - cache::save(program_data, cache, commands)?; + cache::save(program_data, cache, commands, test_mode)?; Ok(CommandExecutionResult::Success) } @@ -109,7 +112,7 @@ pub fn autorun_generated_binary( )) } -/// Executes a new [`std::process::Command`] configured according the choosen +/// Executes a new [`std::process::Command`] configured according the chosen /// compiler and the current operating system fn execute_command( compiler: CppCompiler, @@ -237,9 +240,27 @@ impl<'a> core::fmt::Display for Commands<'a> { f, "Commands for [{}]:\n- Interfaces: {:?},\n- Implementations: {:?},\n- Sources: {:?}", self.compiler, - self.interfaces.iter().map(|vec| { vec.args.iter().map(|e| e.value).collect::>().join(" "); }), - self.implementations.iter().map(|vec| { vec.args.iter().map(|e| e.value).collect::>().join(" "); }), - self.sources.iter().map(|vec| { vec.args.iter().map(|e| e.value).collect::>().join(" "); }), + self.interfaces.iter().map(|vec| { + vec.args + .iter() + .map(|e| e.value) + .collect::>() + .join(" "); + }), + self.implementations.iter().map(|vec| { + vec.args + .iter() + .map(|e| e.value) + .collect::>() + .join(" "); + }), + self.sources.iter().map(|vec| { + vec.args + .iter() + .map(|e| e.value) + .collect::>() + .join(" "); + }), ) } } diff --git a/zork++/src/lib/compiler/mod.rs b/zork++/src/lib/compiler/mod.rs index 7f1c326e..bbee6a14 100644 --- a/zork++/src/lib/compiler/mod.rs +++ b/zork++/src/lib/compiler/mod.rs @@ -54,7 +54,7 @@ fn build_executable<'a>( tests: bool, ) -> Result<()> { // TODO Check if the command line is the same as the previous? If there's no new sources? - // And avoid reexecuting? + // And avoid re-executing? if tests { sources::generate_main_command_line_args(model, commands, &model.tests) } else { @@ -69,7 +69,11 @@ fn build_sources<'a>( tests: bool, ) -> Result<()> { log::info!("Building the source files..."); - let srcs = if tests { &model.tests.sourceset.sources } else { &model.executable.sourceset.sources }; + let srcs = if tests { + &model.tests.sourceset.sources + } else { + &model.executable.sourceset.sources + }; srcs.iter().for_each(|src| { if !flag_source_file_without_changes(&model.compiler.cpp_compiler, cache, &src.file()) { @@ -162,6 +166,7 @@ fn compile_module_implementations<'a>( /// Specific operations over source files mod sources { use super::helpers; + use crate::project_model::sourceset::SourceFile; use crate::{ bounds::{ExecutableTarget, TranslationUnit}, cli::output::{ @@ -176,8 +181,6 @@ mod sources { utils::constants, }; use color_eyre::Result; - use crate::cli::output::arguments; - use crate::project_model::sourceset::SourceFile; /// Generates the command line arguments for the desired target pub fn generate_main_command_line_args<'a>( @@ -265,7 +268,6 @@ mod sources { }; arguments.extend(commands.generated_files_paths.clone().into_iter()); - commands.main.main = target.entry_point(); commands.main.args.extend(arguments.into_iter()); commands.main.sources_paths = target .sourceset() @@ -324,7 +326,11 @@ mod sources { }; let obj_file = helpers::generate_obj_file(compiler, out_dir, source); - let fo = if compiler.eq(&CppCompiler::MSVC) { "/Fo" } else { "" }; + let fo = if compiler.eq(&CppCompiler::MSVC) { + "/Fo" + } else { + "" + }; arguments.push(Argument::from(format!("{fo}{obj_file}"))); arguments.push(Argument::from(source.file())); @@ -335,7 +341,9 @@ mod sources { CommandExecutionResult::default(), ); commands.sources.push(command_line); - commands.generated_files_paths.push(Argument::from(obj_file.to_string())) + commands + .generated_files_paths + .push(Argument::from(obj_file.to_string())) } /// Generates the expected arguments for precompile the BMIs depending on self @@ -541,17 +549,15 @@ mod sources { /// kind of workflow that should be done with this parse, format and /// generate. mod helpers { - use std::fmt::Display; use chrono::{DateTime, Utc}; + use std::fmt::Display; use super::*; + use crate::project_model::sourceset::SourceFile; use crate::{ - bounds::TranslationUnit, - cache::ZorkCache, - cli::output::commands::CommandExecutionResult + bounds::TranslationUnit, cache::ZorkCache, cli::output::commands::CommandExecutionResult, }; use std::path::PathBuf; - use crate::project_model::sourceset::SourceFile; /// Creates the path for a prebuilt module interface, based on the default expected /// extension for BMI's given a compiler @@ -671,10 +677,9 @@ mod helpers { .collect::>(); for collection_args in sys_modules { - commands.system_modules.insert( - collection_args[4].value.to_string(), - collection_args - ); + commands + .system_modules + .insert(collection_args[4].value.to_string(), collection_args); } } @@ -725,8 +730,13 @@ mod helpers { } } - pub(crate) fn generate_obj_file<'a>(compiler: CppCompiler, out_dir: &Path, source: &SourceFile) -> impl Display { - format!("{}", + pub(crate) fn generate_obj_file( + compiler: CppCompiler, + out_dir: &Path, + source: &SourceFile, + ) -> impl Display { + format!( + "{}", out_dir .join(compiler.as_ref()) .join("sources") diff --git a/zork++/src/lib/config_file/executable.rs b/zork++/src/lib/config_file/executable.rs index a4370efc..d8f5f474 100644 --- a/zork++/src/lib/config_file/executable.rs +++ b/zork++/src/lib/config_file/executable.rs @@ -20,7 +20,6 @@ use serde::*; /// sources = [ /// '*.cpp' /// ] -/// main = "main.cpp" /// extra_args = ['example'] /// "#; /// @@ -30,7 +29,6 @@ use serde::*; /// assert_eq!(config.executable_name, Some("outputExecutableName")); /// assert_eq!(config.sources_base_path, Some("./src")); /// assert_eq!(config.sources, Some(vec!["*.cpp"])); -/// assert_eq!(config.main, main.cpp); /// assert_eq!(config.extra_args, Some(vec!["example"])) /// ``` /// > Note: TOML table are toml commented (#) to allow us to parse @@ -51,7 +49,5 @@ pub struct ExecutableAttribute<'a> { #[serde(borrow)] pub sources: Option>, #[serde(borrow)] - pub main: &'a str, - #[serde(borrow)] pub extra_args: Option>, } diff --git a/zork++/src/lib/config_file/tests.rs b/zork++/src/lib/config_file/tests.rs index 9d73f9af..9e36733e 100644 --- a/zork++/src/lib/config_file/tests.rs +++ b/zork++/src/lib/config_file/tests.rs @@ -17,7 +17,6 @@ use serde::*; /// test_executable_name = 'Zork++ tests' /// sources_base_path = 'path_test' /// sources = [ '*.cpp' ] -/// main = "tests_main.cpp" /// extra_args = ['extra_argument to run test'] ///"#; /// @@ -27,7 +26,6 @@ use serde::*; /// assert_eq!(config.test_executable_name, Some("Zork++ tests")); /// assert_eq!(config.sources_base_path, Some("path_test")); /// assert_eq!(config.sources, Some(vec!["*.cpp"])); -/// assert_eq!(config.main, "test_main.cpp"); /// assert_eq!(config.extra_args, Some(vec!["extra_argument to run test"])); /// /// ``` @@ -49,7 +47,5 @@ pub struct TestsAttribute<'a> { #[serde(borrow)] pub sources: Option>, #[serde(borrow)] - pub main: &'a str, - #[serde(borrow)] pub extra_args: Option>, } diff --git a/zork++/src/lib/project_model/executable.rs b/zork++/src/lib/project_model/executable.rs index 6e8de132..9d2d4a9e 100644 --- a/zork++/src/lib/project_model/executable.rs +++ b/zork++/src/lib/project_model/executable.rs @@ -2,7 +2,6 @@ use crate::{ bounds::{ExecutableTarget, ExtraArgs}, cli::output::arguments::Argument, }; -use std::path::{Path, PathBuf}; use super::sourceset::SourceSet; @@ -10,7 +9,6 @@ use super::sourceset::SourceSet; pub struct ExecutableModel<'a> { pub executable_name: &'a str, pub sourceset: SourceSet, - pub main: PathBuf, pub extra_args: Vec>, } @@ -24,9 +22,6 @@ impl<'a> ExecutableTarget<'a> for ExecutableModel<'a> { fn name(&'a self) -> &'a str { self.executable_name } - fn entry_point(&'a self) -> &'a Path { - &self.main - } fn sourceset(&'a self) -> &'a SourceSet { &self.sourceset } diff --git a/zork++/src/lib/project_model/tests.rs b/zork++/src/lib/project_model/tests.rs index 1b1d40f5..336ca341 100644 --- a/zork++/src/lib/project_model/tests.rs +++ b/zork++/src/lib/project_model/tests.rs @@ -2,7 +2,6 @@ use crate::{ bounds::{ExecutableTarget, ExtraArgs}, cli::output::arguments::Argument, }; -use std::path::{Path, PathBuf}; use super::sourceset::SourceSet; @@ -10,7 +9,6 @@ use super::sourceset::SourceSet; pub struct TestsModel<'a> { pub test_executable_name: String, pub sourceset: SourceSet, - pub main: PathBuf, pub extra_args: Vec>, } @@ -24,9 +22,6 @@ impl<'a> ExecutableTarget<'a> for TestsModel<'a> { fn name(&'a self) -> &'a str { &self.test_executable_name } - fn entry_point(&'a self) -> &'a Path { - &self.main - } fn sourceset(&'a self) -> &'a SourceSet { &self.sourceset } diff --git a/zork++/src/lib/utils/reader.rs b/zork++/src/lib/utils/reader.rs index 1626346b..bf99e50e 100644 --- a/zork++/src/lib/utils/reader.rs +++ b/zork++/src/lib/utils/reader.rs @@ -1,3 +1,4 @@ +use crate::project_model::sourceset::SourceFile; use crate::{ cli::output::arguments::Argument, config_file::{ @@ -26,7 +27,6 @@ use crate::{ use color_eyre::{eyre::eyre, Result}; use std::path::{Path, PathBuf}; use walkdir::{DirEntry, WalkDir}; -use crate::project_model::sourceset::SourceFile; use super::constants::DEFAULT_OUTPUT_DIR; @@ -143,18 +143,12 @@ fn assemble_executable_model<'a>( .and_then(|exe| exe.executable_name) .unwrap_or(project_name); - let base_path = config.and_then(|exe| exe.sources_base_path).unwrap_or("."); - - let sources= config + let sources = config .and_then(|exe| exe.sources.clone()) .unwrap_or_default(); let sourceset = get_sourceset_for(sources); - let main_as_path = Path::new(config.map_or("", |exe_attr| exe_attr.main)); - let main = utils::fs::get_absolute_path(main_as_path) - .unwrap_or_default(); - let extra_args = config .and_then(|exe| exe.extra_args.as_ref()) .map(|args| args.iter().map(|arg| Argument::from(*arg)).collect()) @@ -163,7 +157,6 @@ fn assemble_executable_model<'a>( ExecutableModel { executable_name, sourceset, - main, extra_args, } } @@ -281,17 +274,11 @@ fn assemble_tests_model<'a>( |exe_name| exe_name.to_owned(), ); - let base_path = config.and_then(|exe| exe.sources_base_path).unwrap_or("."); - let sources = config .and_then(|exe| exe.sources.clone()) .unwrap_or_default(); let sourceset = get_sourceset_for(sources); - let main_as_path = Path::new(config.map_or("", |exe_attr| exe_attr.main)); - let main = utils::fs::get_absolute_path(main_as_path) - .unwrap_or_default(); - let extra_args = config .and_then(|test| test.extra_args.as_ref()) .map(|args| args.iter().map(|arg| Argument::from(*arg)).collect()) @@ -300,24 +287,25 @@ fn assemble_tests_model<'a>( TestsModel { test_executable_name, sourceset, - main, extra_args, } } fn get_sourceset_for(srcs: Vec<&str>) -> SourceSet { - let sources = srcs.into_iter() + let sources = srcs + .into_iter() .map(|src| { if src.contains('*') { Source::Glob(GlobPattern(src)) } else { Source::File(Path::new(src)) } - }).map(|source| { - source.paths().expect( - "Error getting the declared paths for the source files" - ) - }).flatten() + }) + .flat_map(|source| { + source + .paths() + .expect("Error getting the declared paths for the source files") + }) .map(|pb| { let file_details = utils::fs::get_file_details(pb).unwrap_or_default(); SourceFile { @@ -325,7 +313,8 @@ fn get_sourceset_for(srcs: Vec<&str>) -> SourceSet { file_stem: file_details.1, extension: file_details.2, } - }).collect(); + }) + .collect(); SourceSet { sources } } @@ -337,7 +326,6 @@ mod test { utils, }; use std::env; - use std::ffi::OsString; use super::*; @@ -373,10 +361,7 @@ mod test { }, executable: ExecutableModel { executable_name: "Zork++", - sourceset: SourceSet { - sources: vec![], - }, - main: PathBuf::from("main.cpp"), + sourceset: SourceSet { sources: vec![] }, extra_args: vec![], }, modules: ModulesModel { @@ -388,10 +373,7 @@ mod test { }, tests: TestsModel { test_executable_name: "Zork++_test".to_string(), - sourceset: SourceSet { - sources: vec![], - }, - main: PathBuf::from("main.cpp"), + sourceset: SourceSet { sources: vec![] }, extra_args: vec![], }, }; @@ -425,13 +407,12 @@ mod test { executable: ExecutableModel { executable_name: "zork", sourceset: SourceSet { - sources: vec![SourceFile{ + sources: vec![SourceFile { path: Default::default(), file_stem: "".to_string(), extension: "".to_string(), }], }, - main: PathBuf::from("main.cpp"), extra_args: vec![Argument::from("-Werr")], }, modules: ModulesModel { @@ -480,7 +461,6 @@ mod test { extension: "".to_string(), }], }, - main: PathBuf::from("main.cpp"), extra_args: vec![Argument::from("-pedantic")], }, }; From 763f645aa2ac15e2fadf969966f30ad169da6337 Mon Sep 17 00:00:00 2001 From: Alex Vergara Date: Thu, 9 Mar 2023 17:55:10 +0100 Subject: [PATCH 23/60] Distinction for the cached main command line command, depending on if it's running Tests of the Executable target --- zork++/Cargo.lock | 146 +++++++++++----------- zork++/src/lib/cache/mod.rs | 4 +- zork++/src/lib/cli/output/arguments.rs | 5 +- zork++/src/lib/lib.rs | 10 +- zork++/src/lib/project_model/sourceset.rs | 10 +- zork++/src/lib/utils/constants.rs | 2 - 6 files changed, 83 insertions(+), 94 deletions(-) diff --git a/zork++/Cargo.lock b/zork++/Cargo.lock index 16068e30..1c657f86 100644 --- a/zork++/Cargo.lock +++ b/zork++/Cargo.lock @@ -35,6 +35,12 @@ dependencies = [ "libc", ] +[[package]] +name = "anes" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" + [[package]] name = "atty" version = "0.2.14" @@ -73,18 +79,6 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" -[[package]] -name = "bstr" -version = "0.2.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba3569f383e8f1598449f1a423e72e99569137b47740b1da11ef19af3d5c3223" -dependencies = [ - "lazy_static", - "memchr", - "regex-automata", - "serde", -] - [[package]] name = "bumpalo" version = "3.12.0" @@ -125,15 +119,43 @@ dependencies = [ "winapi", ] +[[package]] +name = "ciborium" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0c137568cc60b904a7724001b35ce2630fd00d5d84805fbb608ab89509d788f" +dependencies = [ + "ciborium-io", + "ciborium-ll", + "serde", +] + +[[package]] +name = "ciborium-io" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "346de753af073cc87b52b2083a506b38ac176a44cfb05497b622e27be899b369" + +[[package]] +name = "ciborium-ll" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "213030a2b5a4e0c0892b6652260cf6ccac84827b83a85a534e178e3906c4cf1b" +dependencies = [ + "ciborium-io", + "half", +] + [[package]] name = "clap" -version = "2.34.0" +version = "3.2.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c" +checksum = "71655c45cb9845d3270c9d6df84ebe72b4dad3c2ba3f7023ad47c144e4e473a5" dependencies = [ "bitflags", + "clap_lex 0.2.4", + "indexmap", "textwrap", - "unicode-width", ] [[package]] @@ -144,7 +166,7 @@ checksum = "f13b9c79b5d1dd500d20ef541215a6423c75829ef43117e1b4d17fd8af0b5d76" dependencies = [ "bitflags", "clap_derive", - "clap_lex", + "clap_lex 0.3.1", "is-terminal", "once_cell", "strsim", @@ -164,6 +186,15 @@ dependencies = [ "syn", ] +[[package]] +name = "clap_lex" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2850f2f5a82cbf437dd5af4d49848fbdfc27c157c3d010345776f952765261c5" +dependencies = [ + "os_str_bytes", +] + [[package]] name = "clap_lex" version = "0.3.1" @@ -204,15 +235,16 @@ checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" [[package]] name = "criterion" -version = "0.3.6" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b01d6de93b2b6c65e17c634a26653a29d107b3c98c607c765bf38d041531cd8f" +checksum = "e7c76e09c1aae2bc52b3d2f29e13c6572553b30c4aa1b8a49fd70de6412654cb" dependencies = [ + "anes", "atty", "cast", - "clap 2.34.0", + "ciborium", + "clap 3.2.23", "criterion-plot", - "csv", "itertools", "lazy_static", "num-traits", @@ -221,7 +253,6 @@ dependencies = [ "rayon", "regex", "serde", - "serde_cbor", "serde_derive", "serde_json", "tinytemplate", @@ -230,9 +261,9 @@ dependencies = [ [[package]] name = "criterion-plot" -version = "0.4.5" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2673cc8207403546f45f5fd319a974b1e6983ad1a3ee7e6041650013be041876" +checksum = "6b50826342786a51a89e2da3a28f1c32b06e387201bc2d19791f622c673706b1" dependencies = [ "cast", "itertools", @@ -281,28 +312,6 @@ dependencies = [ "cfg-if", ] -[[package]] -name = "csv" -version = "1.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22813a6dc45b335f9bade10bf7271dc477e81113e89eb251a0bc2a8a81c536e1" -dependencies = [ - "bstr", - "csv-core", - "itoa 0.4.8", - "ryu", - "serde", -] - -[[package]] -name = "csv-core" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b2466559f260f48ad25fe6317b3c8dac77b5bdb5763ac7d9d6103530663bc90" -dependencies = [ - "memchr", -] - [[package]] name = "cxx" version = "1.0.89" @@ -424,6 +433,12 @@ version = "1.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eabb4a44450da02c90444cf74558da904edde8fb4e9035a9a6a4e15445af0bd7" +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" + [[package]] name = "heck" version = "0.4.1" @@ -490,6 +505,16 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683" +[[package]] +name = "indexmap" +version = "1.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1885e79c1fc4b10f0e172c475f458b7f7b93061064d98c3293e98c5ba0c8b399" +dependencies = [ + "autocfg", + "hashbrown", +] + [[package]] name = "instant" version = "0.1.12" @@ -530,12 +555,6 @@ dependencies = [ "either", ] -[[package]] -name = "itoa" -version = "0.4.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4" - [[package]] name = "itoa" version = "1.0.5" @@ -785,12 +804,6 @@ dependencies = [ "regex-syntax", ] -[[package]] -name = "regex-automata" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" - [[package]] name = "regex-syntax" version = "0.6.28" @@ -862,16 +875,6 @@ dependencies = [ "serde_derive", ] -[[package]] -name = "serde_cbor" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bef2ebfde456fb76bbcf9f59315333decc4fda0b2b44b420243c11e0f5ec1f5" -dependencies = [ - "half", - "serde", -] - [[package]] name = "serde_derive" version = "1.0.152" @@ -889,7 +892,7 @@ version = "1.0.92" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7434af0dc1cbd59268aa98b4c22c131c0584d2232f6fb166efb993e2832e896a" dependencies = [ - "itoa 1.0.5", + "itoa", "ryu", "serde", ] @@ -936,12 +939,9 @@ dependencies = [ [[package]] name = "textwrap" -version = "0.11.0" +version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" -dependencies = [ - "unicode-width", -] +checksum = "222a222a5bfe1bba4a77b45ec488a741b3cb8872e5e499451fd7d0129c9c7c3d" [[package]] name = "time" diff --git a/zork++/src/lib/cache/mod.rs b/zork++/src/lib/cache/mod.rs index 49f64117..355a03db 100644 --- a/zork++/src/lib/cache/mod.rs +++ b/zork++/src/lib/cache/mod.rs @@ -237,9 +237,9 @@ impl ZorkCache { .join(" "), }; - // TODO Replace for target, or whatever, so it doesn't goes to the main command line + let named_target = if test_mode { "test_main" } else { "main" }; self.last_generated_commands - .entry(PathBuf::from("main")) // provisional + .entry(PathBuf::from(named_target)) // provisional .or_insert_with(|| { has_changes = true; vec![commands_details.main.command.clone()] diff --git a/zork++/src/lib/cli/output/arguments.rs b/zork++/src/lib/cli/output/arguments.rs index a159a4e4..233d5d73 100644 --- a/zork++/src/lib/cli/output/arguments.rs +++ b/zork++/src/lib/cli/output/arguments.rs @@ -100,10 +100,7 @@ pub mod clang_args { None } - pub(crate) fn add_prebuilt_module_path( - compiler: CppCompiler, - out_dir: &Path - ) -> Argument<'_> { + pub(crate) fn add_prebuilt_module_path(compiler: CppCompiler, out_dir: &Path) -> Argument<'_> { Argument::from(format!( "-fprebuilt-module-path={}", out_dir diff --git a/zork++/src/lib/lib.rs b/zork++/src/lib/lib.rs index cb689437..ec109aba 100644 --- a/zork++/src/lib/lib.rs +++ b/zork++/src/lib/lib.rs @@ -106,13 +106,13 @@ pub mod worker { commands = build_project(program_data, read_only_cache, false) .with_context(|| "Failed to build project")?; - commands::run_generated_commands(program_data, commands, cache) + commands::run_generated_commands(program_data, commands, cache, false) } Command::Run => { commands = build_project(program_data, read_only_cache, false) .with_context(|| "Failed to build project")?; - match commands::run_generated_commands(program_data, commands, cache) { + match commands::run_generated_commands(program_data, commands, cache, false) { Ok(_) => autorun_generated_binary( &program_data.compiler.cpp_compiler, program_data.build.output_dir, @@ -125,7 +125,7 @@ pub mod worker { commands = build_project(program_data, read_only_cache, true) .with_context(|| "Failed to build project")?; - match commands::run_generated_commands(program_data, commands, cache) { + match commands::run_generated_commands(program_data, commands, cache, false) { Ok(_) => autorun_generated_binary( &program_data.compiler.cpp_compiler, program_data.build.output_dir, @@ -157,9 +157,7 @@ pub mod worker { let compiler = &model.compiler.cpp_compiler; // Recursively create a directory and all of its parent components if they are missing - let modules_path = Path::new(out_dir) - .join(compiler.as_ref()) - .join("modules"); + let modules_path = Path::new(out_dir).join(compiler.as_ref()).join("modules"); let zork_path = out_dir.join("zork"); let zork_cache_path = zork_path.join("cache"); let zork_intrinsics_path = zork_path.join("intrinsics"); diff --git a/zork++/src/lib/project_model/sourceset.rs b/zork++/src/lib/project_model/sourceset.rs index 79541694..1b12fa96 100644 --- a/zork++/src/lib/project_model/sourceset.rs +++ b/zork++/src/lib/project_model/sourceset.rs @@ -1,8 +1,8 @@ use core::fmt; use std::path::{Path, PathBuf}; -use color_eyre::{eyre::Context, Result}; use crate::bounds::TranslationUnit; +use color_eyre::{eyre::Context, Result}; use crate::cli::output::arguments::Argument; @@ -16,7 +16,7 @@ pub enum Source<'a> { pub struct SourceFile { pub path: PathBuf, pub file_stem: String, - pub extension: String + pub extension: String, } impl TranslationUnit for SourceFile { @@ -61,7 +61,6 @@ impl TranslationUnit for &SourceFile { } } - impl fmt::Display for SourceFile { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!( @@ -101,10 +100,7 @@ pub struct SourceSet { impl SourceSet { pub fn as_args_to(&self, dst: &mut Vec>) -> Result<()> { - let args = self.sources - .iter() - .map(|sf| sf.file()) - .map(Argument::from); + let args = self.sources.iter().map(|sf| sf.file()).map(Argument::from); dst.extend(args); diff --git a/zork++/src/lib/utils/constants.rs b/zork++/src/lib/utils/constants.rs index 620cf8b2..5b5f3240 100644 --- a/zork++/src/lib/utils/constants.rs +++ b/zork++/src/lib/utils/constants.rs @@ -40,7 +40,6 @@ sources = [ "*.cpp" ] extra_args = [ "-Werr" ] -main = "main.cpp" [tests] test_executable_name = "zork_check" @@ -48,7 +47,6 @@ sources_base_path = "test" sources = [ "*.cpp" ] -main = "test_main.cpp" extra_args = [ "-pedantic" ] [modules] From d5ca4c6e3a3ca6ca0a37dce7f83458f550badb09 Mon Sep 17 00:00:00 2001 From: Alex Vergara Date: Thu, 9 Mar 2023 21:34:54 +0100 Subject: [PATCH 24/60] Readjusted the example file --- zork++/src/lib/utils/template/resources/zork_example.toml | 2 -- 1 file changed, 2 deletions(-) diff --git a/zork++/src/lib/utils/template/resources/zork_example.toml b/zork++/src/lib/utils/template/resources/zork_example.toml index b4fa6bd0..1d3f0fad 100644 --- a/zork++/src/lib/utils/template/resources/zork_example.toml +++ b/zork++/src/lib/utils/template/resources/zork_example.toml @@ -17,14 +17,12 @@ executable_name = "" sources = [ "//*.cpp" ] -main = "main.cpp" [tests] tests_executable_name = "zork_proj_tests" sources = [ "//*.cpp" ] -main = "test_main.cpp" [modules] base_ifcs_dir = "//ifc" From ad8c84940ce3ef72255b65fd9d86888554d03928 Mon Sep 17 00:00:00 2001 From: Alex Vergara Date: Fri, 10 Mar 2023 20:24:09 +0100 Subject: [PATCH 25/60] More adjusts --- zork++/src/lib/utils/template/resources/zork_example_basic.toml | 2 -- 1 file changed, 2 deletions(-) diff --git a/zork++/src/lib/utils/template/resources/zork_example_basic.toml b/zork++/src/lib/utils/template/resources/zork_example_basic.toml index b69f22a1..7822c200 100644 --- a/zork++/src/lib/utils/template/resources/zork_example_basic.toml +++ b/zork++/src/lib/utils/template/resources/zork_example_basic.toml @@ -17,14 +17,12 @@ executable_name = "" sources = [ "//*.cpp" ] -main = "main.cpp" [tests] tests_executable_name = "zork_proj_tests" sources = [ "//*.cpp" ] -main = "test_main.cpp" [modules] base_ifcs_dir = "//ifc" From ae4587f9e2596a22dc68d8de0beff14ae2f263cc Mon Sep 17 00:00:00 2001 From: Alex Vergara Date: Fri, 10 Mar 2023 22:32:44 +0100 Subject: [PATCH 26/60] Reworking the test for the project model because the full paths now since the beginning --- zork++/src/lib/utils/reader.rs | 36 +++++++++++++--------------------- 1 file changed, 14 insertions(+), 22 deletions(-) diff --git a/zork++/src/lib/utils/reader.rs b/zork++/src/lib/utils/reader.rs index bf99e50e..9f661c6b 100644 --- a/zork++/src/lib/utils/reader.rs +++ b/zork++/src/lib/utils/reader.rs @@ -407,11 +407,7 @@ mod test { executable: ExecutableModel { executable_name: "zork", sourceset: SourceSet { - sources: vec![SourceFile { - path: Default::default(), - file_stem: "".to_string(), - extension: "".to_string(), - }], + sources: vec![], }, extra_args: vec![Argument::from("-Werr")], }, @@ -419,17 +415,17 @@ mod test { base_ifcs_dir: Path::new("ifc"), interfaces: vec![ ModuleInterfaceModel { - path: env::current_dir().unwrap().join("ifc"), - file_stem: String::from("math"), - extension: String::from("cppm"), + path: PathBuf::from(""), + file_stem: String::from(""), + extension: String::from(""), module_name: "math", partition: None, dependencies: vec![], }, ModuleInterfaceModel { - path: env::current_dir().unwrap().join("ifc"), - file_stem: String::from("some_module"), - extension: String::from("cppm"), + path: PathBuf::from(""), + file_stem: String::from(""), + extension: String::from(""), module_name: "math", partition: None, dependencies: vec![], @@ -438,15 +434,15 @@ mod test { base_impls_dir: Path::new("src"), implementations: vec![ ModuleImplementationModel { - path: env::current_dir().unwrap().join("\\src"), - file_stem: String::from("math"), - extension: String::from("cpp"), + path: PathBuf::from(""), + file_stem: String::from(""), + extension: String::from(""), dependencies: vec!["math"], }, ModuleImplementationModel { - path: env::current_dir().unwrap().join("\\ifc"), - file_stem: String::from("some_module_impl"), - extension: String::from("cpp"), + path: PathBuf::from(""), + file_stem: String::from(""), + extension: String::from(""), dependencies: vec!["iostream"], }, ], @@ -455,11 +451,7 @@ mod test { tests: TestsModel { test_executable_name: "zork_check".to_string(), sourceset: SourceSet { - sources: vec![SourceFile { - path: Default::default(), - file_stem: "".to_string(), - extension: "".to_string(), - }], + sources: vec![], }, extra_args: vec![Argument::from("-pedantic")], }, From 788b1527b1766789ed6319322c53823cf812573c Mon Sep 17 00:00:00 2001 From: Alex Vergara Date: Sun, 12 Mar 2023 01:28:50 +0100 Subject: [PATCH 27/60] Refactored and generified the work of convert the generated command lines in cache results --- zork++/src/lib/cache/mod.rs | 131 +++++++++----------------- zork++/src/lib/config_file/modules.rs | 2 +- zork++/src/lib/utils/reader.rs | 2 - zork++/test/test.rs | 1 - 4 files changed, 46 insertions(+), 90 deletions(-) diff --git a/zork++/src/lib/cache/mod.rs b/zork++/src/lib/cache/mod.rs index 355a03db..756b8230 100644 --- a/zork++/src/lib/cache/mod.rs +++ b/zork++/src/lib/cache/mod.rs @@ -117,7 +117,7 @@ impl ZorkCache { commands: Commands<'_>, test_mode: bool ) -> Result<()> { - if self.save_generated_commands(&commands, test_mode) && program_data.project.compilation_db { + if self.save_generated_commands(&mut commands, test_mode) && program_data.project.compilation_db { map_generated_commands_to_compilation_db(self)?; } @@ -133,7 +133,7 @@ impl ZorkCache { Ok(()) } - fn save_generated_commands(&mut self, commands: &Commands<'_>, test_mode: bool) -> bool { + fn save_generated_commands(&mut self, commands: &mut Commands<'_>, test_mode: bool) -> bool { log::trace!("Storing in the cache the last generated command lines..."); let mut has_changes = false; @@ -153,89 +153,16 @@ impl ZorkCache { main: MainCommandLineDetail::default(), }; - commands_details - .interfaces - .extend(commands.interfaces.iter().map(|module_command_line| { - self.last_generated_commands - .entry(module_command_line.path()) - .or_insert_with(|| { - has_changes = true; - module_command_line - .args - .iter() - .map(|e| e.value.to_string()) - .collect() - }); - CommandDetail { - directory: module_command_line - .directory - .to_str() - .unwrap_or_default() - .to_string(), - file: module_command_line.file.clone(), - execution_result: self.normalize_execution_result_status(module_command_line), - } - })); - - commands_details - .implementations - .extend(commands.implementations.iter().map(|module_command_line| { - self.last_generated_commands - .entry(module_command_line.path()) - .or_insert_with(|| { - has_changes = true; - module_command_line - .args - .iter() - .map(|e| e.value.to_string()) - .collect() - }); - CommandDetail { - directory: module_command_line - .directory - .to_str() - .unwrap_or_default() - .to_string(), - file: module_command_line.file.clone(), - execution_result: self.normalize_execution_result_status(module_command_line), - } - })); - - commands_details - .sources - .extend(commands.sources.iter().map(|source_command_line| { - self.last_generated_commands - .entry(source_command_line.path()) - .or_insert_with(|| { - has_changes = true; - source_command_line - .args - .iter() - .map(|e| e.value.to_string()) - .collect() - }); - CommandDetail { - directory: source_command_line - .directory - .to_str() - .unwrap_or_default() - .to_string(), - file: source_command_line.file.clone(), - execution_result: self.normalize_execution_result_status(source_command_line), - } - })); - - commands_details.main = MainCommandLineDetail { - files: commands.main.sources_paths.clone(), - execution_result: commands.main.execution_result.clone(), - command: commands - .main - .args - .iter() - .map(|arg| arg.value.to_string()) - .collect::>() - .join(" "), - }; + let mut are_new_commands = Vec::with_capacity(3); + let interfaces_has_new_commands= + self.extend_collection_of_source_file_details(&mut commands_details.interfaces, &mut commands.interfaces); + are_new_commands.push(interfaces_has_new_commands); + let implementations_has_new_commands = + self.extend_collection_of_source_file_details(&mut commands_details.implementations, &mut commands.implementations); + are_new_commands.push(implementations_has_new_commands); + let sources_has_new_commands = + self.extend_collection_of_source_file_details(&mut commands_details.sources, &mut commands.sources); + are_new_commands.push(sources_has_new_commands); let named_target = if test_mode { "test_main" } else { "main" }; self.last_generated_commands @@ -246,7 +173,8 @@ impl ZorkCache { }); self.generated_commands.push(commands_details); - has_changes + + are_new_commands.iter().any(|b| *b == true) } /// If Windows is the current OS, and the compiler is MSVC, then we will try @@ -330,6 +258,37 @@ impl ZorkCache { module_command_line.execution_result.clone() } } + + fn extend_collection_of_source_file_details( + &mut self, + collection: &mut Vec, + target: &mut [SourceCommandLine] + ) -> bool { + let mut new_commands = false; + collection.extend(target.iter().map(|module_command_line| { + self.last_generated_commands + .entry(module_command_line.path()) + .or_insert_with(|| { + new_commands = true; + module_command_line + .args + .iter() + .map(|e| e.value.to_string()) + .collect() + }); + CommandDetail { + directory: module_command_line + .directory + .to_str() + .unwrap_or_default() + .to_string(), + file: module_command_line.file.clone(), + execution_result: self.normalize_execution_result_status(module_command_line), + } + })); + + new_commands + } } /// Generates the `compile_commands.json` file, that acts as a compilation database diff --git a/zork++/src/lib/config_file/modules.rs b/zork++/src/lib/config_file/modules.rs index e9b8f803..ac2f3de1 100644 --- a/zork++/src/lib/config_file/modules.rs +++ b/zork++/src/lib/config_file/modules.rs @@ -96,7 +96,7 @@ pub struct ModulesAttribute<'a> { /// { file = 'math.cppm' }, /// { file = 'some_module.cppm', module_name = 'math' }, /// { file = 'a.cppm', module_name = 'module', dependencies = ['math', 'type_traits', 'iostream'] }, -/// { file = 'some_module_part.cppm', module_name = 'math_part', dependecies = ['math'] } +/// { file = 'some_module_part.cppm', module_name = 'math_part', dependencies = ['math'] } /// ] /// "#; /// diff --git a/zork++/src/lib/utils/reader.rs b/zork++/src/lib/utils/reader.rs index 9f661c6b..948711ae 100644 --- a/zork++/src/lib/utils/reader.rs +++ b/zork++/src/lib/utils/reader.rs @@ -325,7 +325,6 @@ mod test { project_model::compiler::{CppCompiler, LanguageLevel, StdLib}, utils, }; - use std::env; use super::*; @@ -384,7 +383,6 @@ mod test { } #[test] - #[ignore] // TODO ignoring for now since we're trying to canonicalize since the assemble of the project model fn test_project_model_with_full_config() -> Result<()> { let config: ZorkConfigFile = toml::from_str(utils::constants::CONFIG_FILE_MOCK)?; let model = build_model(&config); diff --git a/zork++/test/test.rs b/zork++/test/test.rs index 65259b80..3ecb010a 100644 --- a/zork++/test/test.rs +++ b/zork++/test/test.rs @@ -5,7 +5,6 @@ use tempfile::tempdir; use zork::cli::input::CliArgs; #[test] -#[ignore] // Provisional fn test_clang_full_process() -> Result<()> { let temp = tempdir()?; From b8ee0dda9cf1703fc950d5fab09048b3a81b0613 Mon Sep 17 00:00:00 2001 From: Alex Vergara Date: Sun, 12 Mar 2023 01:44:01 +0100 Subject: [PATCH 28/60] Added the compiler's driver name to the beginning of the generated command line for any source, and also for the linker command. ExecutionResult is now Copy, so we could delete unnecessary clones --- zork++/src/lib/cache/mod.rs | 28 +++++++++++++++------------ zork++/src/lib/cli/output/commands.rs | 4 ++-- zork++/src/lib/utils/reader.rs | 8 ++------ 3 files changed, 20 insertions(+), 20 deletions(-) diff --git a/zork++/src/lib/cache/mod.rs b/zork++/src/lib/cache/mod.rs index 756b8230..ac05ad56 100644 --- a/zork++/src/lib/cache/mod.rs +++ b/zork++/src/lib/cache/mod.rs @@ -114,7 +114,7 @@ impl ZorkCache { pub fn run_final_tasks( &mut self, program_data: &ZorkModel<'_>, - commands: Commands<'_>, + mut commands: Commands<'_>, test_mode: bool ) -> Result<()> { if self.save_generated_commands(&mut commands, test_mode) && program_data.project.compilation_db { @@ -155,13 +155,13 @@ impl ZorkCache { let mut are_new_commands = Vec::with_capacity(3); let interfaces_has_new_commands= - self.extend_collection_of_source_file_details(&mut commands_details.interfaces, &mut commands.interfaces); + self.extend_collection_of_source_file_details(&mut commands_details.interfaces, &mut commands.interfaces, commands.compiler); are_new_commands.push(interfaces_has_new_commands); let implementations_has_new_commands = - self.extend_collection_of_source_file_details(&mut commands_details.implementations, &mut commands.implementations); + self.extend_collection_of_source_file_details(&mut commands_details.implementations, &mut commands.implementations, commands.compiler); are_new_commands.push(implementations_has_new_commands); let sources_has_new_commands = - self.extend_collection_of_source_file_details(&mut commands_details.sources, &mut commands.sources); + self.extend_collection_of_source_file_details(&mut commands_details.sources, &mut commands.sources, commands.compiler); are_new_commands.push(sources_has_new_commands); let named_target = if test_mode { "test_main" } else { "main" }; @@ -174,7 +174,7 @@ impl ZorkCache { self.generated_commands.push(commands_details); - are_new_commands.iter().any(|b| *b == true) + are_new_commands.iter().any(|b| *b) } /// If Windows is the current OS, and the compiler is MSVC, then we will try @@ -250,19 +250,20 @@ impl ZorkCache { .eq(&CommandExecutionResult::Unreached) { if let Some(prev_entry) = self.is_file_cached(module_command_line.path()) { - prev_entry.execution_result.clone() + prev_entry.execution_result } else { - module_command_line.execution_result.clone() + module_command_line.execution_result } } else { - module_command_line.execution_result.clone() + module_command_line.execution_result } } fn extend_collection_of_source_file_details( &mut self, collection: &mut Vec, - target: &mut [SourceCommandLine] + target: &mut [SourceCommandLine], + compiler: CppCompiler ) -> bool { let mut new_commands = false; collection.extend(target.iter().map(|module_command_line| { @@ -270,11 +271,14 @@ impl ZorkCache { .entry(module_command_line.path()) .or_insert_with(|| { new_commands = true; - module_command_line + let mut arguments = Vec::with_capacity(module_command_line.args.len() + 1); + arguments.push(compiler.get_driver().to_string()); + arguments.extend(module_command_line .args .iter() .map(|e| e.value.to_string()) - .collect() + ); + arguments }); CommandDetail { directory: module_command_line @@ -282,7 +286,7 @@ impl ZorkCache { .to_str() .unwrap_or_default() .to_string(), - file: module_command_line.file.clone(), + file: format!("{} {}", compiler.get_driver(), module_command_line.file), execution_result: self.normalize_execution_result_status(module_command_line), } })); diff --git a/zork++/src/lib/cli/output/commands.rs b/zork++/src/lib/cli/output/commands.rs index 1a6e8d50..709794e0 100644 --- a/zork++/src/lib/cli/output/commands.rs +++ b/zork++/src/lib/cli/output/commands.rs @@ -25,7 +25,7 @@ pub fn run_generated_commands( program_data: &ZorkModel<'_>, mut commands: Commands<'_>, cache: ZorkCache, - test_mode: bool + test_mode: bool, ) -> Result { log::info!("Proceeding to execute the generated commands..."); let mut total_exec_commands = 0; @@ -267,7 +267,7 @@ impl<'a> core::fmt::Display for Commands<'a> { /// Holds a custom representation of the execution of /// a command line in a shell. -#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq)] +#[derive(Debug, Default, Serialize, Deserialize, Clone, Copy, PartialEq)] pub enum CommandExecutionResult { /// A command that is executed correctly Success, diff --git a/zork++/src/lib/utils/reader.rs b/zork++/src/lib/utils/reader.rs index 948711ae..a8d652f5 100644 --- a/zork++/src/lib/utils/reader.rs +++ b/zork++/src/lib/utils/reader.rs @@ -404,9 +404,7 @@ mod test { }, executable: ExecutableModel { executable_name: "zork", - sourceset: SourceSet { - sources: vec![], - }, + sourceset: SourceSet { sources: vec![] }, extra_args: vec![Argument::from("-Werr")], }, modules: ModulesModel { @@ -448,9 +446,7 @@ mod test { }, tests: TestsModel { test_executable_name: "zork_check".to_string(), - sourceset: SourceSet { - sources: vec![], - }, + sourceset: SourceSet { sources: vec![] }, extra_args: vec![Argument::from("-pedantic")], }, }; From 879dbeb50b0b88aab9f0a2ce51c64a57945a1ad0 Mon Sep 17 00:00:00 2001 From: Alex Vergara Date: Sun, 12 Mar 2023 02:00:55 +0100 Subject: [PATCH 29/60] Reenabled the addition of the linker command to the last_generated_commands --- zork++/src/lib/cache/mod.rs | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/zork++/src/lib/cache/mod.rs b/zork++/src/lib/cache/mod.rs index ac05ad56..b5d1203c 100644 --- a/zork++/src/lib/cache/mod.rs +++ b/zork++/src/lib/cache/mod.rs @@ -164,6 +164,18 @@ impl ZorkCache { self.extend_collection_of_source_file_details(&mut commands_details.sources, &mut commands.sources, commands.compiler); are_new_commands.push(sources_has_new_commands); + commands_details.main = MainCommandLineDetail { + files: commands.main.sources_paths.clone(), + execution_result: commands.main.execution_result, + command: commands + .main + .args + .iter() + .map(|arg| arg.value.to_string()) + .collect::>() + .join(" "), + }; + let named_target = if test_mode { "test_main" } else { "main" }; self.last_generated_commands .entry(PathBuf::from(named_target)) // provisional @@ -286,7 +298,7 @@ impl ZorkCache { .to_str() .unwrap_or_default() .to_string(), - file: format!("{} {}", compiler.get_driver(), module_command_line.file), + file: module_command_line.file.clone(), execution_result: self.normalize_execution_result_status(module_command_line), } })); From a4e19e0ac4251824870656625966cd3adebed4de Mon Sep 17 00:00:00 2001 From: Alex Vergara Date: Sun, 12 Mar 2023 11:17:47 +0100 Subject: [PATCH 30/60] Reworking the compilation database, and moving their components into their own source file --- zork++/src/lib/cache/compile_commands.rs | 50 +++++++++++++++ zork++/src/lib/cache/mod.rs | 79 ++++++------------------ 2 files changed, 69 insertions(+), 60 deletions(-) create mode 100644 zork++/src/lib/cache/compile_commands.rs diff --git a/zork++/src/lib/cache/compile_commands.rs b/zork++/src/lib/cache/compile_commands.rs new file mode 100644 index 00000000..2fade124 --- /dev/null +++ b/zork++/src/lib/cache/compile_commands.rs @@ -0,0 +1,50 @@ +use std::fs::File; +use std::path::{Path, PathBuf}; +use serde::Serialize; +use color_eyre::eyre::{Context, Result}; +use crate::cache::ZorkCache; +use crate::utils; +use crate::utils::constants::COMPILATION_DATABASE; + +/// Generates the `compile_commands.json` file, that acts as a compilation database +/// for some static analysis external tools, like `clang-tidy`, and populates it with +/// the generated commands for the translation units +pub(crate) fn map_generated_commands_to_compilation_db(cache: &ZorkCache) -> Result<()> { + log::trace!("Generating the compilation database..."); + let mut compilation_db_entries = Vec::with_capacity(cache.last_generated_commands.len()); + + for command in cache.last_generated_commands.iter() { + compilation_db_entries.push(CompileCommands::from(command)); + } + + let compile_commands_path = Path::new(COMPILATION_DATABASE); + if !Path::new(&compile_commands_path).exists() { + File::create(compile_commands_path).with_context(|| "Error creating the compilation database")?; + } + utils::fs::serialize_object_to_file(Path::new(compile_commands_path), &compilation_db_entries) + .with_context(move || "Error saving the compilation database") +} + +/// Data model for serialize the data that will be outputted +/// to the `compile_commands.json` compilation database file +#[derive(Serialize, Debug, Default, Clone)] +pub struct CompileCommands { + pub directory: String, + pub file: String, + pub arguments: Vec, +} + +impl From<(&'_ PathBuf, &'_ Vec)> for CompileCommands { + fn from(value: (&PathBuf, &Vec)) -> Self { + let dir = value.0.parent().unwrap_or(Path::new(".")); + let mut file = value.0.file_stem().unwrap_or_default().to_os_string(); + file.push("."); + file.push(value.0.extension().unwrap_or_default()); + + Self { + directory: dir.to_str().unwrap_or_default().to_string(), + file: file.to_str().unwrap_or_default().to_string(), + arguments: value.1.clone(), + } + } +} \ No newline at end of file diff --git a/zork++/src/lib/cache/mod.rs b/zork++/src/lib/cache/mod.rs index b5d1203c..5c544a16 100644 --- a/zork++/src/lib/cache/mod.rs +++ b/zork++/src/lib/cache/mod.rs @@ -1,5 +1,7 @@ //! The implementation of the Zork++ cache, for persisting data in between process +pub mod compile_commands; + use chrono::{DateTime, Utc}; use color_eyre::{eyre::Context, Result}; use std::collections::HashMap; @@ -9,7 +11,6 @@ use std::{ path::{Path, PathBuf}, }; -use crate::utils::constants::COMPILATION_DATABASE; use crate::{ cli::{ input::CliArgs, @@ -77,8 +78,9 @@ pub struct ZorkCache { pub compiler: CppCompiler, pub last_program_execution: DateTime, pub compilers_metadata: CompilersMetadata, - pub generated_commands: Vec, pub last_generated_commands: HashMap>, + pub last_generated_linker_commands: HashMap, + pub generated_commands: Vec, } impl ZorkCache { @@ -118,7 +120,7 @@ impl ZorkCache { test_mode: bool ) -> Result<()> { if self.save_generated_commands(&mut commands, test_mode) && program_data.project.compilation_db { - map_generated_commands_to_compilation_db(self)?; + compile_commands::map_generated_commands_to_compilation_db(self)?; } if !(program_data.compiler.cpp_compiler == CppCompiler::MSVC) { @@ -135,8 +137,6 @@ impl ZorkCache { fn save_generated_commands(&mut self, commands: &mut Commands<'_>, test_mode: bool) -> bool { log::trace!("Storing in the cache the last generated command lines..."); - let mut has_changes = false; - self.compiler = commands.compiler; let process_no = if !self.generated_commands.is_empty() { self.generated_commands.last().unwrap().cached_process_num + 1 @@ -177,12 +177,13 @@ impl ZorkCache { }; let named_target = if test_mode { "test_main" } else { "main" }; - self.last_generated_commands - .entry(PathBuf::from(named_target)) // provisional - .or_insert_with(|| { - has_changes = true; - vec![commands_details.main.command.clone()] - }); + self.last_generated_linker_commands + .entry(PathBuf::from(named_target)) + .and_modify(|e| { + if !(*e).eq(&commands_details.main.command) { + *e = commands_details.main.command.clone() + } + }).or_insert(commands_details.main.command.clone()); self.generated_commands.push(commands_details); @@ -278,14 +279,14 @@ impl ZorkCache { compiler: CppCompiler ) -> bool { let mut new_commands = false; - collection.extend(target.iter().map(|module_command_line| { + collection.extend(target.iter().map(|source_command_line| { self.last_generated_commands - .entry(module_command_line.path()) + .entry(source_command_line.path()) .or_insert_with(|| { new_commands = true; - let mut arguments = Vec::with_capacity(module_command_line.args.len() + 1); + let mut arguments = Vec::with_capacity(source_command_line.args.len() + 1); arguments.push(compiler.get_driver().to_string()); - arguments.extend(module_command_line + arguments.extend(source_command_line .args .iter() .map(|e| e.value.to_string()) @@ -293,13 +294,13 @@ impl ZorkCache { arguments }); CommandDetail { - directory: module_command_line + directory: source_command_line .directory .to_str() .unwrap_or_default() .to_string(), - file: module_command_line.file.clone(), - execution_result: self.normalize_execution_result_status(module_command_line), + file: source_command_line.file.clone(), + execution_result: self.normalize_execution_result_status(source_command_line), } })); @@ -307,48 +308,6 @@ impl ZorkCache { } } -/// Generates the `compile_commands.json` file, that acts as a compilation database -/// for some static analysis external tools, like `clang-tidy`, and populates it with -/// the generated commands for the translation units -fn map_generated_commands_to_compilation_db(cache: &ZorkCache) -> Result<()> { - log::trace!("Generating the compilation database..."); - let mut compilation_db_entries = Vec::with_capacity(cache.last_generated_commands.len()); - - for command in cache.last_generated_commands.iter() { - compilation_db_entries.push(CompileCommands::from(command)); - } - - let compile_commands_path = Path::new(COMPILATION_DATABASE); - if !Path::new(&compile_commands_path).exists() { - File::create(compile_commands_path).with_context(|| "Error creating the cache file")?; - } - utils::fs::serialize_object_to_file(Path::new(compile_commands_path), &compilation_db_entries) - .with_context(move || "Error generating the compilation database") -} - -/// Data model for serialize the data that will be outputted -/// to the `compile_commands.json` compilation database file -#[derive(Serialize, Debug, Default, Clone)] -pub struct CompileCommands { - pub directory: String, - pub file: String, - pub arguments: Vec, -} - -impl From<(&'_ PathBuf, &'_ Vec)> for CompileCommands { - fn from(value: (&PathBuf, &Vec)) -> Self { - let dir = value.0.parent().unwrap_or(Path::new(".")); - let mut file = value.0.file_stem().unwrap_or_default().to_os_string(); - file.push("."); - file.push(value.0.extension().unwrap_or_default()); - - Self { - directory: dir.to_str().unwrap_or_default().to_string(), - file: file.to_str().unwrap_or_default().to_string(), - arguments: value.1.clone(), - } - } -} #[derive(Deserialize, Serialize, Debug, Default, Clone)] pub struct CommandsDetails { From ea1c57f4183b7088b157ad180b5e8c3edc4e0034 Mon Sep 17 00:00:00 2001 From: Alex Vergara Date: Sun, 12 Mar 2023 15:11:43 +0100 Subject: [PATCH 31/60] Solving the problem of the double incremental time of the cache loading because the clone of the cache for read only purposes, by introducing the interior mutability pattern --- zork++/benches/benchmarks.rs | 5 ++- zork++/src/bin/main.rs | 2 +- zork++/src/lib/cache/mod.rs | 11 +++-- zork++/src/lib/cli/output/commands.rs | 24 ++++++----- zork++/src/lib/compiler/mod.rs | 58 ++++++++++++++------------- zork++/src/lib/lib.rs | 22 +++++----- zork++/src/lib/utils/fs.rs | 12 ++++++ 7 files changed, 78 insertions(+), 56 deletions(-) diff --git a/zork++/benches/benchmarks.rs b/zork++/benches/benchmarks.rs index a0f03991..3578f225 100644 --- a/zork++/benches/benchmarks.rs +++ b/zork++/benches/benchmarks.rs @@ -1,5 +1,7 @@ //! Benchmarks tests for measuring the performance of the code +use std::cell::RefCell; +use std::rc::Rc; use criterion::{black_box, criterion_group, criterion_main, Criterion}; use zork::{ cache::{self, ZorkCache}, @@ -12,10 +14,9 @@ use zork::{ pub fn build_project_benchmark(c: &mut Criterion) { let config: ZorkConfigFile = toml::from_str(utils::constants::CONFIG_FILE_MOCK).unwrap(); let program_data = build_model(&config); - let cache = ZorkCache::default(); c.bench_function("Build project", |b| { - b.iter(|| build_project(black_box(&program_data), black_box(&cache), false)) + b.iter(|| build_project(black_box(&program_data), black_box(Rc::new(RefCell::new(ZorkCache::default()))), false)) }); c.bench_function("Cache loading time", |b| { diff --git a/zork++/src/bin/main.rs b/zork++/src/bin/main.rs index 5a4550bb..384751fd 100644 --- a/zork++/src/bin/main.rs +++ b/zork++/src/bin/main.rs @@ -18,7 +18,7 @@ fn main() -> Result<()> { match run_zork(&cli_args, Path::new(".")) { Ok(_) => { log::info!( - "[SUCCESS] - The process ended succesfully, taking a total time in complete of: {:?} ms", + "[SUCCESS] - The process ended successfully, taking a total time in complete of: {:?} ms", process_start_time.elapsed().as_millis() ); Ok(()) diff --git a/zork++/src/lib/cache/mod.rs b/zork++/src/lib/cache/mod.rs index 5c544a16..b401a114 100644 --- a/zork++/src/lib/cache/mod.rs +++ b/zork++/src/lib/cache/mod.rs @@ -10,6 +10,9 @@ use std::{ fs::File, path::{Path, PathBuf}, }; +use std::cell::{RefCell, RefMut}; +use std::ops::Deref; +use std::rc::Rc; use crate::{ cli::{ @@ -56,7 +59,7 @@ pub fn load(program_data: &ZorkModel<'_>, cli_args: &CliArgs) -> Result, - mut cache: ZorkCache, + cache: Rc>, commands: Commands<'_>, test_mode: bool ) -> Result<()> { @@ -66,10 +69,10 @@ pub fn save( .join(program_data.compiler.cpp_compiler.as_ref()) .join(constants::ZORK_CACHE_FILENAME); - cache.run_final_tasks(program_data, commands, test_mode)?; - cache.last_program_execution = Utc::now(); + cache.borrow_mut().run_final_tasks(program_data, commands, test_mode)?; + cache.borrow_mut().last_program_execution = Utc::now(); - utils::fs::serialize_object_to_file(cache_path, &cache) + utils::fs::serialize_cache(cache_path, cache.borrow_mut()) .with_context(move || "Error saving data to the Zork++ cache") } diff --git a/zork++/src/lib/cli/output/commands.rs b/zork++/src/lib/cli/output/commands.rs index 709794e0..ff9bf483 100644 --- a/zork++/src/lib/cli/output/commands.rs +++ b/zork++/src/lib/cli/output/commands.rs @@ -3,6 +3,9 @@ use std::{ path::{Path, PathBuf}, process::ExitStatus, }; +use std::cell::{Ref, RefCell, RefMut}; +use std::ops::{Deref, DerefMut}; +use std::rc::Rc; use crate::bounds::TranslationUnit; ///! Contains helpers and data structure to process in @@ -24,15 +27,15 @@ use super::arguments::Argument; pub fn run_generated_commands( program_data: &ZorkModel<'_>, mut commands: Commands<'_>, - cache: ZorkCache, - test_mode: bool, + cache: Rc>, + test_mode: bool ) -> Result { log::info!("Proceeding to execute the generated commands..."); let mut total_exec_commands = 0; let compiler = commands.compiler; for sys_module in &commands.system_modules { - execute_command(compiler, sys_module.1, &cache)?; + execute_command(compiler, sys_module.1, cache.clone())?; } let sources = commands @@ -43,18 +46,18 @@ pub fn run_generated_commands( for source_file in sources { if !source_file.processed { - let r = execute_command(compiler, &source_file.args, &cache); + let r = execute_command(compiler, &source_file.args, cache.clone()); source_file.execution_result = CommandExecutionResult::from(&r); total_exec_commands += 1; if let Err(e) = r { - cache::save(program_data, cache, commands, test_mode)?; + cache::save(program_data, cache.clone(), commands, test_mode)?; return Err(e); } else if !r.as_ref().unwrap().success() { let err = eyre!( "Ending the program, because the build of: {:?} wasn't ended successfully", source_file ); - cache::save(program_data, cache, commands, test_mode)?; + cache::save(program_data, cache.clone(), commands, test_mode)?; return Err(err); } } @@ -63,15 +66,15 @@ pub fn run_generated_commands( if !commands.main.args.is_empty() { log::debug!("Executing the main command line..."); - let r = execute_command(compiler, &commands.main.args, &cache); + let r = execute_command(compiler, &commands.main.args, cache.clone()); commands.main.execution_result = CommandExecutionResult::from(&r); total_exec_commands += 1; if let Err(e) = r { - cache::save(program_data, cache, commands, test_mode)?; // Here because use of moved value + cache::save(program_data, cache.clone(), commands, test_mode)?; return Err(e); } else if !r.as_ref().unwrap().success() { - cache::save(program_data, cache, commands, test_mode)?; // Here because use of moved value + cache::save(program_data, cache.clone(), commands, test_mode)?; return Err(eyre!( "Ending the program, because the main command line execution wasn't ended successfully", )); @@ -117,7 +120,7 @@ pub fn autorun_generated_binary( fn execute_command( compiler: CppCompiler, arguments: &[Argument<'_>], - cache: &ZorkCache, + cache: Rc>, ) -> Result { log::trace!( "[{compiler}] - Executing command => {:?}", @@ -127,6 +130,7 @@ fn execute_command( if compiler.eq(&CppCompiler::MSVC) { std::process::Command::new( cache + .borrow_mut() .compilers_metadata .msvc .dev_commands_prompt diff --git a/zork++/src/lib/compiler/mod.rs b/zork++/src/lib/compiler/mod.rs index bbee6a14..e095cbe8 100644 --- a/zork++/src/lib/compiler/mod.rs +++ b/zork++/src/lib/compiler/mod.rs @@ -3,8 +3,10 @@ // operating system against the designed compilers in the configuration // file. +use std::cell::{Ref, RefCell, RefMut}; use color_eyre::Result; use std::path::Path; +use std::rc::Rc; use crate::bounds::TranslationUnit; use crate::cli::output::commands::{CommandExecutionResult, SourceCommandLine}; @@ -25,20 +27,20 @@ use crate::{ /// configuration file will be build pub fn build_project<'a>( model: &'a ZorkModel<'a>, - cache: &'a ZorkCache, + cache: Rc>, tests: bool, ) -> Result> { // A registry of the generated command lines let mut commands = Commands::new(&model.compiler.cpp_compiler); if model.compiler.cpp_compiler != CppCompiler::MSVC { - helpers::prebuild_sys_modules(model, &mut commands, cache) + helpers::prebuild_sys_modules(model, &mut commands, &cache) } // 1st - Build the modules - build_modules(model, cache, &mut commands)?; + build_modules(model, cache.clone(), &mut commands)?; // 2nd - Build the non module sources - build_sources(model, cache, &mut commands, tests)?; + build_sources(model, cache.clone(), &mut commands, tests)?; // 3rd - Build the executable or the tests build_executable(model, &mut commands, tests)?; @@ -64,7 +66,7 @@ fn build_executable<'a>( fn build_sources<'a>( model: &'a ZorkModel<'_>, - cache: &'a ZorkCache, + cache: Rc>, commands: &'_ mut Commands<'a>, tests: bool, ) -> Result<()> { @@ -75,20 +77,18 @@ fn build_sources<'a>( &model.executable.sourceset.sources }; - srcs.iter().for_each(|src| { - if !flag_source_file_without_changes(&model.compiler.cpp_compiler, cache, &src.file()) { - sources::generate_sources_arguments(model, commands, &model.tests, src); - } else { - let command_line = SourceCommandLine::from_translation_unit( - src, Vec::with_capacity(0), true, CommandExecutionResult::Cached - ); + srcs.iter().for_each(|src| if !flag_source_file_without_changes(&model.compiler.cpp_compiler, cache.clone(), &src.file()) { + sources::generate_sources_arguments(model, commands, &model.tests, src); + } else { + let command_line = SourceCommandLine::from_translation_unit( + src, Vec::with_capacity(0), true, CommandExecutionResult::Cached + ); - log::trace!("Source file: {:?} was not modified since the last iteration. No need to rebuilt it again.", &src.file()); - commands.implementations.push(command_line); - commands.generated_files_paths.push(Argument::from(helpers::generate_obj_file( - model.compiler.cpp_compiler, model.build.output_dir, src - ).to_string())) - } + log::trace!("Source file: {:?} was not modified since the last iteration. No need to rebuilt it again.", &src.file()); + commands.sources.push(command_line); + commands.generated_files_paths.push(Argument::from(helpers::generate_obj_file( + model.compiler.cpp_compiler, model.build.output_dir, src + ).to_string())) }); Ok(()) @@ -101,11 +101,11 @@ fn build_sources<'a>( /// compiler responses> fn build_modules<'a>( model: &'a ZorkModel, - cache: &'a ZorkCache, + cache: Rc>, commands: &mut Commands<'a>, ) -> Result<()> { log::info!("Building the module interfaces and partitions..."); - prebuild_module_interfaces(model, cache, &model.modules.interfaces, commands); + prebuild_module_interfaces(model, cache.clone(), &model.modules.interfaces, commands); log::info!("Building the module implementations..."); compile_module_implementations(model, cache, &model.modules.implementations, commands); @@ -117,12 +117,12 @@ fn build_modules<'a>( /// by precompiling the module interface units fn prebuild_module_interfaces<'a>( model: &'a ZorkModel<'_>, - cache: &'a ZorkCache, + cache: Rc>, interfaces: &'a [ModuleInterfaceModel], commands: &mut Commands<'a>, ) { interfaces.iter().for_each(|module_interface| { - if !flag_source_file_without_changes(&model.compiler.cpp_compiler, cache, &module_interface.file()) { + if !flag_source_file_without_changes(&model.compiler.cpp_compiler, cache.clone(), &module_interface.file()) { sources::generate_module_interfaces_args(model, module_interface, commands); } else { let command_line = SourceCommandLine::from_translation_unit( @@ -142,12 +142,12 @@ fn prebuild_module_interfaces<'a>( /// translation units declared for the project fn compile_module_implementations<'a>( model: &'a ZorkModel, - cache: &'a ZorkCache, + cache: Rc>, impls: &'a [ModuleImplementationModel], commands: &mut Commands<'a>, ) { impls.iter().for_each(|module_impl| { - if !flag_source_file_without_changes(&model.compiler.cpp_compiler, cache, &module_impl.file()) { + if !flag_source_file_without_changes(&model.compiler.cpp_compiler, cache.clone(), &module_impl.file()) { sources::generate_module_implementation_args(model, module_impl, commands); } else { let command_line = SourceCommandLine::from_translation_unit( @@ -628,12 +628,12 @@ mod helpers { pub(crate) fn prebuild_sys_modules<'a>( model: &'a ZorkModel, commands: &mut Commands<'a>, - cache: &ZorkCache, + cache: &Rc>, ) { - if !cache.compilers_metadata.system_modules.is_empty() { + if !cache.borrow_mut().compilers_metadata.system_modules.is_empty() { log::info!( "System modules already build: {:?}. They will be skipped!", - cache.compilers_metadata.system_modules + cache.borrow_mut().compilers_metadata.system_modules ); } @@ -644,6 +644,7 @@ mod helpers { .iter() .filter(|sys_module| { !cache + .borrow_mut() .compilers_metadata .system_modules .iter() @@ -691,12 +692,13 @@ mod helpers { /// True means already processed and previous iteration Success pub(crate) fn flag_source_file_without_changes( compiler: &CppCompiler, - cache: &ZorkCache, + cache: Rc>, file: &Path, ) -> bool { if compiler.eq(&CppCompiler::CLANG) { log::trace!("Module unit {file:?} will be rebuilt since we've detected that you are using Clang"); } + let cache = cache.borrow_mut(); // Check first if the file is already on the cache, and if it's last iteration was successful if let Some(cached_file) = cache.is_file_cached(file) { if cached_file.execution_result != CommandExecutionResult::Success diff --git a/zork++/src/lib/lib.rs b/zork++/src/lib/lib.rs index ec109aba..3b6a2656 100644 --- a/zork++/src/lib/lib.rs +++ b/zork++/src/lib/lib.rs @@ -16,6 +16,8 @@ pub mod utils; /// data sent to stdout/stderr pub mod worker { use std::{fs, path::Path}; + use std::cell::RefCell; + use std::rc::Rc; use crate::{ cache::{self, ZorkCache}, @@ -63,8 +65,7 @@ pub mod worker { "An error happened parsing the configuration file: {:?}", config_file.dir_entry.file_name() ) - }) - .expect("Unexpected big error happened reading a config file"); + })?; let config: ZorkConfigFile = toml::from_str(raw_file.as_str()) .with_context(|| "Could not parse configuration file")?; @@ -73,10 +74,9 @@ pub mod worker { let cache = cache::load(&program_data, cli_args) .with_context(|| "Unable to load the Zork++ cache")?; - let read_only_cache = cache.clone(); // let generated_commands = - do_main_work_based_on_cli_input(cli_args, &program_data, cache, &read_only_cache) + do_main_work_based_on_cli_input(cli_args, &program_data, cache) .with_context(|| { format!( "Failed to build the project for the config file: {:?}", @@ -97,22 +97,22 @@ pub mod worker { cli_args: &'a CliArgs, program_data: &'a ZorkModel<'_>, cache: ZorkCache, - read_only_cache: &'a ZorkCache, ) -> Result { let commands: Commands; + let mut_ref = Rc::new(RefCell::new(cache)); match cli_args.command { Command::Build => { - commands = build_project(program_data, read_only_cache, false) + commands = build_project(program_data, mut_ref.clone(), false) .with_context(|| "Failed to build project")?; - commands::run_generated_commands(program_data, commands, cache, false) + commands::run_generated_commands(program_data, commands, mut_ref, false) } Command::Run => { - commands = build_project(program_data, read_only_cache, false) + commands = build_project(program_data, mut_ref.clone(), false) .with_context(|| "Failed to build project")?; - match commands::run_generated_commands(program_data, commands, cache, false) { + match commands::run_generated_commands(program_data, commands, mut_ref, false) { Ok(_) => autorun_generated_binary( &program_data.compiler.cpp_compiler, program_data.build.output_dir, @@ -122,10 +122,10 @@ pub mod worker { } } Command::Test => { - commands = build_project(program_data, read_only_cache, true) + commands = build_project(program_data, mut_ref.clone(), true) .with_context(|| "Failed to build project")?; - match commands::run_generated_commands(program_data, commands, cache, false) { + match commands::run_generated_commands(program_data, commands, mut_ref,false) { Ok(_) => autorun_generated_binary( &program_data.compiler.cpp_compiler, program_data.build.output_dir, diff --git a/zork++/src/lib/utils/fs.rs b/zork++/src/lib/utils/fs.rs index 32a07c8b..c654bc60 100644 --- a/zork++/src/lib/utils/fs.rs +++ b/zork++/src/lib/utils/fs.rs @@ -3,13 +3,17 @@ use std::{ io::{BufReader, Write}, path::Path, }; +use std::cell::RefMut; +use std::ops::Deref; use std::path::PathBuf; +use std::rc::Rc; use color_eyre::eyre::ContextCompat; use color_eyre::{eyre::Context, Result}; use serde::{Deserialize, Serialize}; +use crate::cache::ZorkCache; use super::constants; @@ -103,6 +107,14 @@ where .with_context(|| "Error serializing data to the cache") } +pub fn serialize_cache(path: &Path, data: RefMut) -> Result<()> { + serde_json::to_writer_pretty( + File::create(path).with_context(|| "Error creating the cache file")?, + data.deref(), + ) + .with_context(|| "Error serializing data to the cache") +} + pub fn load_and_deserialize(path: &P) -> Result where T: for<'a> Deserialize<'a> + Default, From 1972a064ad4e267cd9b47dfdc1a0c26045e6826e Mon Sep 17 00:00:00 2001 From: Alex Vergara Date: Sun, 12 Mar 2023 16:47:37 +0100 Subject: [PATCH 32/60] Code reworked to remove the interior mutability pattern in favor or raw `&T` and `&mut T` types --- zork++/benches/benchmarks.rs | 5 +-- zork++/src/lib/cache/compile_commands.rs | 13 +++--- zork++/src/lib/cache/mod.rs | 52 ++++++++++++----------- zork++/src/lib/cli/output/commands.rs | 24 +++++------ zork++/src/lib/compiler/mod.rs | 42 +++++++++--------- zork++/src/lib/lib.rs | 54 ++++++++++++------------ zork++/src/lib/utils/fs.rs | 9 ++-- 7 files changed, 96 insertions(+), 103 deletions(-) diff --git a/zork++/benches/benchmarks.rs b/zork++/benches/benchmarks.rs index 3578f225..a0f03991 100644 --- a/zork++/benches/benchmarks.rs +++ b/zork++/benches/benchmarks.rs @@ -1,7 +1,5 @@ //! Benchmarks tests for measuring the performance of the code -use std::cell::RefCell; -use std::rc::Rc; use criterion::{black_box, criterion_group, criterion_main, Criterion}; use zork::{ cache::{self, ZorkCache}, @@ -14,9 +12,10 @@ use zork::{ pub fn build_project_benchmark(c: &mut Criterion) { let config: ZorkConfigFile = toml::from_str(utils::constants::CONFIG_FILE_MOCK).unwrap(); let program_data = build_model(&config); + let cache = ZorkCache::default(); c.bench_function("Build project", |b| { - b.iter(|| build_project(black_box(&program_data), black_box(Rc::new(RefCell::new(ZorkCache::default()))), false)) + b.iter(|| build_project(black_box(&program_data), black_box(&cache), false)) }); c.bench_function("Cache loading time", |b| { diff --git a/zork++/src/lib/cache/compile_commands.rs b/zork++/src/lib/cache/compile_commands.rs index 2fade124..59fe83fc 100644 --- a/zork++/src/lib/cache/compile_commands.rs +++ b/zork++/src/lib/cache/compile_commands.rs @@ -1,10 +1,10 @@ -use std::fs::File; -use std::path::{Path, PathBuf}; -use serde::Serialize; -use color_eyre::eyre::{Context, Result}; use crate::cache::ZorkCache; use crate::utils; use crate::utils::constants::COMPILATION_DATABASE; +use color_eyre::eyre::{Context, Result}; +use serde::Serialize; +use std::fs::File; +use std::path::{Path, PathBuf}; /// Generates the `compile_commands.json` file, that acts as a compilation database /// for some static analysis external tools, like `clang-tidy`, and populates it with @@ -19,7 +19,8 @@ pub(crate) fn map_generated_commands_to_compilation_db(cache: &ZorkCache) -> Res let compile_commands_path = Path::new(COMPILATION_DATABASE); if !Path::new(&compile_commands_path).exists() { - File::create(compile_commands_path).with_context(|| "Error creating the compilation database")?; + File::create(compile_commands_path) + .with_context(|| "Error creating the compilation database")?; } utils::fs::serialize_object_to_file(Path::new(compile_commands_path), &compilation_db_entries) .with_context(move || "Error saving the compilation database") @@ -47,4 +48,4 @@ impl From<(&'_ PathBuf, &'_ Vec)> for CompileCommands { arguments: value.1.clone(), } } -} \ No newline at end of file +} diff --git a/zork++/src/lib/cache/mod.rs b/zork++/src/lib/cache/mod.rs index b401a114..a5ea5829 100644 --- a/zork++/src/lib/cache/mod.rs +++ b/zork++/src/lib/cache/mod.rs @@ -10,9 +10,6 @@ use std::{ fs::File, path::{Path, PathBuf}, }; -use std::cell::{RefCell, RefMut}; -use std::ops::Deref; -use std::rc::Rc; use crate::{ cli::{ @@ -59,9 +56,9 @@ pub fn load(program_data: &ZorkModel<'_>, cli_args: &CliArgs) -> Result, - cache: Rc>, + cache: &mut ZorkCache, commands: Commands<'_>, - test_mode: bool + test_mode: bool, ) -> Result<()> { let cache_path = &Path::new(program_data.build.output_dir) .join("zork") @@ -69,10 +66,10 @@ pub fn save( .join(program_data.compiler.cpp_compiler.as_ref()) .join(constants::ZORK_CACHE_FILENAME); - cache.borrow_mut().run_final_tasks(program_data, commands, test_mode)?; - cache.borrow_mut().last_program_execution = Utc::now(); + cache.run_final_tasks(program_data, commands, test_mode)?; + cache.last_program_execution = Utc::now(); - utils::fs::serialize_cache(cache_path, cache.borrow_mut()) + utils::fs::serialize_cache(cache_path, cache) .with_context(move || "Error saving data to the Zork++ cache") } @@ -120,9 +117,11 @@ impl ZorkCache { &mut self, program_data: &ZorkModel<'_>, mut commands: Commands<'_>, - test_mode: bool + test_mode: bool, ) -> Result<()> { - if self.save_generated_commands(&mut commands, test_mode) && program_data.project.compilation_db { + if self.save_generated_commands(&mut commands, test_mode) + && program_data.project.compilation_db + { compile_commands::map_generated_commands_to_compilation_db(self)?; } @@ -157,14 +156,23 @@ impl ZorkCache { }; let mut are_new_commands = Vec::with_capacity(3); - let interfaces_has_new_commands= - self.extend_collection_of_source_file_details(&mut commands_details.interfaces, &mut commands.interfaces, commands.compiler); + let interfaces_has_new_commands = self.extend_collection_of_source_file_details( + &mut commands_details.interfaces, + &mut commands.interfaces, + commands.compiler, + ); are_new_commands.push(interfaces_has_new_commands); - let implementations_has_new_commands = - self.extend_collection_of_source_file_details(&mut commands_details.implementations, &mut commands.implementations, commands.compiler); + let implementations_has_new_commands = self.extend_collection_of_source_file_details( + &mut commands_details.implementations, + &mut commands.implementations, + commands.compiler, + ); are_new_commands.push(implementations_has_new_commands); - let sources_has_new_commands = - self.extend_collection_of_source_file_details(&mut commands_details.sources, &mut commands.sources, commands.compiler); + let sources_has_new_commands = self.extend_collection_of_source_file_details( + &mut commands_details.sources, + &mut commands.sources, + commands.compiler, + ); are_new_commands.push(sources_has_new_commands); commands_details.main = MainCommandLineDetail { @@ -186,7 +194,8 @@ impl ZorkCache { if !(*e).eq(&commands_details.main.command) { *e = commands_details.main.command.clone() } - }).or_insert(commands_details.main.command.clone()); + }) + .or_insert(commands_details.main.command.clone()); self.generated_commands.push(commands_details); @@ -279,7 +288,7 @@ impl ZorkCache { &mut self, collection: &mut Vec, target: &mut [SourceCommandLine], - compiler: CppCompiler + compiler: CppCompiler, ) -> bool { let mut new_commands = false; collection.extend(target.iter().map(|source_command_line| { @@ -289,11 +298,7 @@ impl ZorkCache { new_commands = true; let mut arguments = Vec::with_capacity(source_command_line.args.len() + 1); arguments.push(compiler.get_driver().to_string()); - arguments.extend(source_command_line - .args - .iter() - .map(|e| e.value.to_string()) - ); + arguments.extend(source_command_line.args.iter().map(|e| e.value.to_string())); arguments }); CommandDetail { @@ -311,7 +316,6 @@ impl ZorkCache { } } - #[derive(Deserialize, Serialize, Debug, Default, Clone)] pub struct CommandsDetails { cached_process_num: i32, diff --git a/zork++/src/lib/cli/output/commands.rs b/zork++/src/lib/cli/output/commands.rs index ff9bf483..8381cdea 100644 --- a/zork++/src/lib/cli/output/commands.rs +++ b/zork++/src/lib/cli/output/commands.rs @@ -3,9 +3,6 @@ use std::{ path::{Path, PathBuf}, process::ExitStatus, }; -use std::cell::{Ref, RefCell, RefMut}; -use std::ops::{Deref, DerefMut}; -use std::rc::Rc; use crate::bounds::TranslationUnit; ///! Contains helpers and data structure to process in @@ -27,15 +24,15 @@ use super::arguments::Argument; pub fn run_generated_commands( program_data: &ZorkModel<'_>, mut commands: Commands<'_>, - cache: Rc>, - test_mode: bool + cache: &mut ZorkCache, + test_mode: bool, ) -> Result { log::info!("Proceeding to execute the generated commands..."); let mut total_exec_commands = 0; let compiler = commands.compiler; for sys_module in &commands.system_modules { - execute_command(compiler, sys_module.1, cache.clone())?; + execute_command(compiler, sys_module.1, cache)?; } let sources = commands @@ -46,18 +43,18 @@ pub fn run_generated_commands( for source_file in sources { if !source_file.processed { - let r = execute_command(compiler, &source_file.args, cache.clone()); + let r = execute_command(compiler, &source_file.args, cache); source_file.execution_result = CommandExecutionResult::from(&r); total_exec_commands += 1; if let Err(e) = r { - cache::save(program_data, cache.clone(), commands, test_mode)?; + cache::save(program_data, cache, commands, test_mode)?; return Err(e); } else if !r.as_ref().unwrap().success() { let err = eyre!( "Ending the program, because the build of: {:?} wasn't ended successfully", source_file ); - cache::save(program_data, cache.clone(), commands, test_mode)?; + cache::save(program_data, cache, commands, test_mode)?; return Err(err); } } @@ -66,15 +63,15 @@ pub fn run_generated_commands( if !commands.main.args.is_empty() { log::debug!("Executing the main command line..."); - let r = execute_command(compiler, &commands.main.args, cache.clone()); + let r = execute_command(compiler, &commands.main.args, cache); commands.main.execution_result = CommandExecutionResult::from(&r); total_exec_commands += 1; if let Err(e) = r { - cache::save(program_data, cache.clone(), commands, test_mode)?; + cache::save(program_data, cache, commands, test_mode)?; return Err(e); } else if !r.as_ref().unwrap().success() { - cache::save(program_data, cache.clone(), commands, test_mode)?; + cache::save(program_data, cache, commands, test_mode)?; return Err(eyre!( "Ending the program, because the main command line execution wasn't ended successfully", )); @@ -120,7 +117,7 @@ pub fn autorun_generated_binary( fn execute_command( compiler: CppCompiler, arguments: &[Argument<'_>], - cache: Rc>, + cache: &ZorkCache, ) -> Result { log::trace!( "[{compiler}] - Executing command => {:?}", @@ -130,7 +127,6 @@ fn execute_command( if compiler.eq(&CppCompiler::MSVC) { std::process::Command::new( cache - .borrow_mut() .compilers_metadata .msvc .dev_commands_prompt diff --git a/zork++/src/lib/compiler/mod.rs b/zork++/src/lib/compiler/mod.rs index e095cbe8..2309f915 100644 --- a/zork++/src/lib/compiler/mod.rs +++ b/zork++/src/lib/compiler/mod.rs @@ -3,10 +3,8 @@ // operating system against the designed compilers in the configuration // file. -use std::cell::{Ref, RefCell, RefMut}; use color_eyre::Result; use std::path::Path; -use std::rc::Rc; use crate::bounds::TranslationUnit; use crate::cli::output::commands::{CommandExecutionResult, SourceCommandLine}; @@ -27,20 +25,20 @@ use crate::{ /// configuration file will be build pub fn build_project<'a>( model: &'a ZorkModel<'a>, - cache: Rc>, + cache: &ZorkCache, tests: bool, ) -> Result> { // A registry of the generated command lines let mut commands = Commands::new(&model.compiler.cpp_compiler); if model.compiler.cpp_compiler != CppCompiler::MSVC { - helpers::prebuild_sys_modules(model, &mut commands, &cache) + helpers::prebuild_sys_modules(model, &mut commands, cache) } // 1st - Build the modules - build_modules(model, cache.clone(), &mut commands)?; + build_modules(model, cache, &mut commands)?; // 2nd - Build the non module sources - build_sources(model, cache.clone(), &mut commands, tests)?; + build_sources(model, cache, &mut commands, tests)?; // 3rd - Build the executable or the tests build_executable(model, &mut commands, tests)?; @@ -66,7 +64,7 @@ fn build_executable<'a>( fn build_sources<'a>( model: &'a ZorkModel<'_>, - cache: Rc>, + cache: &ZorkCache, commands: &'_ mut Commands<'a>, tests: bool, ) -> Result<()> { @@ -77,7 +75,7 @@ fn build_sources<'a>( &model.executable.sourceset.sources }; - srcs.iter().for_each(|src| if !flag_source_file_without_changes(&model.compiler.cpp_compiler, cache.clone(), &src.file()) { + srcs.iter().for_each(|src| if !flag_source_file_without_changes(&model.compiler.cpp_compiler, cache, &src.file()) { sources::generate_sources_arguments(model, commands, &model.tests, src); } else { let command_line = SourceCommandLine::from_translation_unit( @@ -101,11 +99,11 @@ fn build_sources<'a>( /// compiler responses> fn build_modules<'a>( model: &'a ZorkModel, - cache: Rc>, + cache: &ZorkCache, commands: &mut Commands<'a>, ) -> Result<()> { log::info!("Building the module interfaces and partitions..."); - prebuild_module_interfaces(model, cache.clone(), &model.modules.interfaces, commands); + prebuild_module_interfaces(model, cache, &model.modules.interfaces, commands); log::info!("Building the module implementations..."); compile_module_implementations(model, cache, &model.modules.implementations, commands); @@ -117,12 +115,12 @@ fn build_modules<'a>( /// by precompiling the module interface units fn prebuild_module_interfaces<'a>( model: &'a ZorkModel<'_>, - cache: Rc>, + cache: &ZorkCache, interfaces: &'a [ModuleInterfaceModel], commands: &mut Commands<'a>, ) { interfaces.iter().for_each(|module_interface| { - if !flag_source_file_without_changes(&model.compiler.cpp_compiler, cache.clone(), &module_interface.file()) { + if !flag_source_file_without_changes(&model.compiler.cpp_compiler, cache, &module_interface.file()) { sources::generate_module_interfaces_args(model, module_interface, commands); } else { let command_line = SourceCommandLine::from_translation_unit( @@ -142,12 +140,12 @@ fn prebuild_module_interfaces<'a>( /// translation units declared for the project fn compile_module_implementations<'a>( model: &'a ZorkModel, - cache: Rc>, + cache: &ZorkCache, impls: &'a [ModuleImplementationModel], commands: &mut Commands<'a>, ) { impls.iter().for_each(|module_impl| { - if !flag_source_file_without_changes(&model.compiler.cpp_compiler, cache.clone(), &module_impl.file()) { + if !flag_source_file_without_changes(&model.compiler.cpp_compiler, cache, &module_impl.file()) { sources::generate_module_implementation_args(model, module_impl, commands); } else { let command_line = SourceCommandLine::from_translation_unit( @@ -628,12 +626,12 @@ mod helpers { pub(crate) fn prebuild_sys_modules<'a>( model: &'a ZorkModel, commands: &mut Commands<'a>, - cache: &Rc>, + cache: &ZorkCache, ) { - if !cache.borrow_mut().compilers_metadata.system_modules.is_empty() { + if !cache.compilers_metadata.system_modules.is_empty() { log::info!( "System modules already build: {:?}. They will be skipped!", - cache.borrow_mut().compilers_metadata.system_modules + cache.compilers_metadata.system_modules ); } @@ -644,7 +642,6 @@ mod helpers { .iter() .filter(|sys_module| { !cache - .borrow_mut() .compilers_metadata .system_modules .iter() @@ -692,13 +689,14 @@ mod helpers { /// True means already processed and previous iteration Success pub(crate) fn flag_source_file_without_changes( compiler: &CppCompiler, - cache: Rc>, + cache: &ZorkCache, file: &Path, ) -> bool { - if compiler.eq(&CppCompiler::CLANG) { - log::trace!("Module unit {file:?} will be rebuilt since we've detected that you are using Clang"); + if compiler.eq(&CppCompiler::CLANG) && cfg!(target_os = "windows") { + log::trace!("Module unit {file:?} will be rebuilt since we've detected that you are using Clang in Windows"); + return false; } - let cache = cache.borrow_mut(); + let cache = cache; // Check first if the file is already on the cache, and if it's last iteration was successful if let Some(cached_file) = cache.is_file_cached(file) { if cached_file.execution_result != CommandExecutionResult::Success diff --git a/zork++/src/lib/lib.rs b/zork++/src/lib/lib.rs index 3b6a2656..dc8ec461 100644 --- a/zork++/src/lib/lib.rs +++ b/zork++/src/lib/lib.rs @@ -16,8 +16,6 @@ pub mod utils; /// data sent to stdout/stderr pub mod worker { use std::{fs, path::Path}; - use std::cell::RefCell; - use std::rc::Rc; use crate::{ cache::{self, ZorkCache}, @@ -59,13 +57,12 @@ pub mod worker { config_file.dir_entry.file_name(), config_file.path ); - let raw_file = fs::read_to_string(config_file.path) - .with_context(|| { - format!( - "An error happened parsing the configuration file: {:?}", - config_file.dir_entry.file_name() - ) - })?; + let raw_file = fs::read_to_string(config_file.path).with_context(|| { + format!( + "An error happened parsing the configuration file: {:?}", + config_file.dir_entry.file_name() + ) + })?; let config: ZorkConfigFile = toml::from_str(raw_file.as_str()) .with_context(|| "Could not parse configuration file")?; @@ -75,14 +72,12 @@ pub mod worker { let cache = cache::load(&program_data, cli_args) .with_context(|| "Unable to load the Zork++ cache")?; - // let generated_commands = - do_main_work_based_on_cli_input(cli_args, &program_data, cache) - .with_context(|| { - format!( - "Failed to build the project for the config file: {:?}", - config_file.dir_entry.file_name() - ) - })?; + do_main_work_based_on_cli_input(cli_args, &program_data, cache).with_context(|| { + format!( + "Failed to build the project for the config file: {:?}", + config_file.dir_entry.file_name() + ) + })?; } Ok(()) @@ -96,45 +91,48 @@ pub mod worker { fn do_main_work_based_on_cli_input<'a>( cli_args: &'a CliArgs, program_data: &'a ZorkModel<'_>, - cache: ZorkCache, + mut cache: ZorkCache, ) -> Result { let commands: Commands; - let mut_ref = Rc::new(RefCell::new(cache)); match cli_args.command { Command::Build => { - commands = build_project(program_data, mut_ref.clone(), false) + commands = build_project(program_data, &cache, false) .with_context(|| "Failed to build project")?; - commands::run_generated_commands(program_data, commands, mut_ref, false) + commands::run_generated_commands(program_data, commands, &mut cache, false) } Command::Run => { - commands = build_project(program_data, mut_ref.clone(), false) + commands = build_project(program_data, &cache, false) .with_context(|| "Failed to build project")?; - match commands::run_generated_commands(program_data, commands, mut_ref, false) { + match commands::run_generated_commands(program_data, commands, &mut cache, false) { Ok(_) => autorun_generated_binary( &program_data.compiler.cpp_compiler, program_data.build.output_dir, - program_data.executable.executable_name + program_data.executable.executable_name, ), Err(e) => Err(e), } } Command::Test => { - commands = build_project(program_data, mut_ref.clone(), true) + commands = build_project(program_data, &cache, true) .with_context(|| "Failed to build project")?; - match commands::run_generated_commands(program_data, commands, mut_ref,false) { + match commands::run_generated_commands(program_data, commands, &mut cache, true) { Ok(_) => autorun_generated_binary( &program_data.compiler.cpp_compiler, program_data.build.output_dir, - &program_data.tests.test_executable_name + &program_data.tests.test_executable_name, ), Err(e) => Err(e), } } - _ => todo!("This branch should never be reached for now, as do not exists commands that may trigger them ") + _ => todo!( + "This branch should never be reached for now, as do not exists commands that may\ + trigger them. The unique remaining, is ::New, that is already processed\ + at the very beggining" + ), } } diff --git a/zork++/src/lib/utils/fs.rs b/zork++/src/lib/utils/fs.rs index c654bc60..6efa051a 100644 --- a/zork++/src/lib/utils/fs.rs +++ b/zork++/src/lib/utils/fs.rs @@ -3,17 +3,14 @@ use std::{ io::{BufReader, Write}, path::Path, }; -use std::cell::RefMut; -use std::ops::Deref; use std::path::PathBuf; -use std::rc::Rc; use color_eyre::eyre::ContextCompat; use color_eyre::{eyre::Context, Result}; -use serde::{Deserialize, Serialize}; use crate::cache::ZorkCache; +use serde::{Deserialize, Serialize}; use super::constants; @@ -107,10 +104,10 @@ where .with_context(|| "Error serializing data to the cache") } -pub fn serialize_cache(path: &Path, data: RefMut) -> Result<()> { +pub fn serialize_cache(path: &Path, data: &ZorkCache) -> Result<()> { serde_json::to_writer_pretty( File::create(path).with_context(|| "Error creating the cache file")?, - data.deref(), + data, ) .with_context(|| "Error serializing data to the cache") } From 668d180dde585624d19d6a1933c53f8c1c12a693 Mon Sep 17 00:00:00 2001 From: Alex Vergara Date: Mon, 13 Mar 2023 00:40:54 +0100 Subject: [PATCH 33/60] Version 0.8.0 --- CHANGELOG.md | 28 +++++++++----- README.md | 45 ++++++++++++++++------- release-config/windows-installer-zork.iss | 2 +- zork++/Cargo.lock | 2 +- zork++/Cargo.toml | 2 +- zork++/src/lib/cli/input/mod.rs | 2 +- 6 files changed, 55 insertions(+), 26 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6aa6d553..3a6e9e40 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,15 +5,22 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## [Unreleased] - -## [0.8.0] - // TODO +## [0.8.0] - 2023 - 03 - 12 ### Feature -### Updates +- Zork++ generates now a compilation database for `C++` projects, known as the `compile_commands.json` +file, which is used by static analyzers and IDE's to offer code completion and linting. +- The cache has been updated to store data in a more efficient layout. +- The overall performance of the cache process has been reviewed. We get rid of a cache clone that was affecting +the performance, and making a huge impact the memory needed for the cache process during runtime +by a factor of two. +Now everything is smoothly handled by mutable and inmutable reference types. +- A command line flag `-c` has been included to reset the cache when the user requires. + +### Update -- Source type has been modified to support individual files, and +- The source type has been modified to support individual files, and sourceset now is a collection of those individuals, non one path or multiple paths - Non module source files are compiled and assembled without linking now. This allows us to generate the compile_commands.json for every translation unit @@ -22,14 +29,17 @@ assembling and linking. - Due to the described above, now the main task of the main command line is to link the generated object files together, introducing the necessary dependencies - Non module source files now have their explicit types and operations -- Internal deps: toml raised to the 0.7.2, criterion raised to its 0.4.0v +- Internal deps: criterion raised to its 0.4.0v +- We've merged some parts of the source code that was performing similar operations, specially the one that was mapping + data in the cache to some other datastructures. Even that technically that parts wasn't exactly + duplicated code, we've managed to make them cleaned and shorter. ### Fix - Solved a bug for which the source files was always detected as a glob pattern, even if they were declared in a non glob form -## [0.7.0] - 2022 - 03 - 01 +## [0.7.0] - 2023 - 03 - 01 ### Feature @@ -44,14 +54,14 @@ the manual usage of the module map for featuring the `import std;` - Solved a bug that was causing C++ modules containing a dot in their module identifier declaration to not be correctly processed by Zork++, causing a compilation error due to incorrect parameters -## [0.6.0] - 2022 - 02 - 28 +## [0.6.0] - 2023 - 02 - 28 ### Feature - Allowing the usage of `import ` with `Clang`, by precompiling the declared system modules required, just as we were doing with `GCC` -## [0.5.0] - 2022 - 02 - 08 +## [0.5.0] - 2023 - 02 - 08 ### Feature diff --git a/README.md b/README.md index 5b4c8ec2..f365b10d 100644 --- a/README.md +++ b/README.md @@ -24,19 +24,20 @@ # 📝 Table of Contents -- [About](#about) -- [Getting Started](#getting_started) -- [The `zork.toml` quick start](#usage) -- [The `zork.toml` reference guide](#zork_toml_reference) -- [The `Zork++` command_line](#zork_command_line) -- [C++23 `import std;`](#import_std) -- [The developers and contributors guide](#dev_guide) -- [TODO ZONE](#todo_zone) -- [Built Using](#built_using) -- [Contributing](../CONTRIBUTING.md) +- [About](#-about-a-name--about-a) +- [Getting Started](#-getting-started-a-name--gettingstarted-a) +- [The `zork.toml` quick start example](#generating-a-new-c-project-a-name--gettingstartedexample-a) +- [The `zork.toml` reference guide](#-the-zorktoml-reference-guide-a-namezorktomlreference-a) +- [The `Zork++` command_line](#-the-zork-command-line-interface-a-namezorkcommandline-a) +- [Compilation Database (`compile_commands.json`)](#-compilation-database-a-namecompilation-database-a) +- [C++23 `import std;`](#-c23-import-std-feature-a-nameimportstd-a) +- [The developers and contributors guide](#-developers-guide-a-namedevguide-a) +- [TODO ZONE](#-todo-zone-a-name--todozone-a) +- [Built Using](#-built-using-a-name--builtusing-a) +- [Contributing](./CONTRIBUTING.md) - [License](./LICENSE) -- [Authors](#authors) -- [Acknowledgements](#acknowledgement) +- [Authors](#-authors-a-name--authors-a) +- [Acknowledgements](#-acknowledgements-a-name--acknowledgement-a) # 🧐 About @@ -360,6 +361,7 @@ ZorkConfigFile { ProjectAttribute { name: &'a str authors: Option>, + compilation_db : bool } /// The [compiler] key @@ -499,10 +501,27 @@ a minimal setup. This command includes some arguments to make it more flexible, should be used to set up the template - `-v` ⇒ Outputs more information to stdout. The classical `verbose` command line flag -- `--clear-cache` ⇒ Clears the files in the cache, so, in the next iteration, cached items +- `-c,`--clear-cache` ⇒ Clears the files in the cache, so, in the next iteration, cached items must be processed again. This is useful after a lot of program iterations, when the cache starts to slow down the build process. +# 📑 Compilation Database + +`Zork++` is able to generate a compilation database file, commonly known as `compile_commands.json`. This file is typically +used by static code analyzers, IDE's and other tools to provide information about the status, the quality, problems, errors, +suggestions and all kind of crazy things that the linting tools are able to do. + +To enable this feature, you will need to put a flag property, under the `#[project]` table, named `compilation_db`. + +```toml +#[project] +compilation_db = true +``` + +The file will be automatically generated the first time that `Zork++` sees this property on the configuration file, and +will be regularly updated when changes are detected in the generated command lines, or when files are added/dropped +from your project. + # 📑 C++23 `import std;` feature The `C++23` standard is supposed to come with a nice feature to finally support modules in a real way, that is through the `import std;` statement. diff --git a/release-config/windows-installer-zork.iss b/release-config/windows-installer-zork.iss index 2e168139..1bf1283f 100644 --- a/release-config/windows-installer-zork.iss +++ b/release-config/windows-installer-zork.iss @@ -2,7 +2,7 @@ ; SEE THE DOCUMENTATION FOR DETAILS ON CREATING INNO SETUP SCRIPT FILES! #define MyAppName "Zork++" -#define MyAppVersion "0.7.0" +#define MyAppVersion "0.8.0" #define MyAppPublisher "Zero Day Code" #define MyAppURL "https://github.com/zerodaycode/Zork" #define MyAppExeName "zork++.exe" diff --git a/zork++/Cargo.lock b/zork++/Cargo.lock index 1c657f86..dd9202a8 100644 --- a/zork++/Cargo.lock +++ b/zork++/Cargo.lock @@ -1171,7 +1171,7 @@ checksum = "447660ad36a13288b1db4d4248e857b510e8c3a225c822ba4fb748c0aafecffd" [[package]] name = "zork" -version = "0.7.0" +version = "0.8.0" dependencies = [ "chrono", "clap 4.1.4", diff --git a/zork++/Cargo.toml b/zork++/Cargo.toml index b7f104f0..d5d54f90 100644 --- a/zork++/Cargo.toml +++ b/zork++/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "zork" -version = "0.7.0" +version = "0.8.0" authors = ["Zero Day Code"] edition = "2021" description = "A modern C++ project manager and build system for modern C++" diff --git a/zork++/src/lib/cli/input/mod.rs b/zork++/src/lib/cli/input/mod.rs index d0c7ba7c..ee30e693 100644 --- a/zork++/src/lib/cli/input/mod.rs +++ b/zork++/src/lib/cli/input/mod.rs @@ -24,7 +24,7 @@ use clap::{Parser, Subcommand, ValueEnum}; #[derive(Parser, Debug, Default)] #[command(name = "Zork++")] #[command(author = "Zero Day Code")] -#[command(version = "0.7.0")] +#[command(version = "0.8.0")] #[command( about = "Zork++ is a build system for modern C++ projects", long_about = "Zork++ is a project of Zero Day Code. Find us: https://github.com/zerodaycode/Zork" From 33d0a7b81b214a1d0a2146adaae50b0a7cdc3b1b Mon Sep 17 00:00:00 2001 From: Alex Vergara Date: Tue, 14 Mar 2023 21:03:18 +0100 Subject: [PATCH 34/60] removed the restriction of choose the standard library only in OS that aren't Windows --- zork++/src/lib/cli/output/arguments.rs | 11 ----------- zork++/src/lib/compiler/mod.rs | 12 ++++++------ 2 files changed, 6 insertions(+), 17 deletions(-) diff --git a/zork++/src/lib/cli/output/arguments.rs b/zork++/src/lib/cli/output/arguments.rs index 233d5d73..38f3d83d 100644 --- a/zork++/src/lib/cli/output/arguments.rs +++ b/zork++/src/lib/cli/output/arguments.rs @@ -89,17 +89,6 @@ pub mod clang_args { } } - #[inline(always)] - pub fn add_std_lib<'a>(model: &'a ZorkModel) -> Option> { - if !cfg!(target_os = "windows") { - if let Some(arg) = model.compiler.stdlib_arg() { - return Some(arg); - } - } - - None - } - pub(crate) fn add_prebuilt_module_path(compiler: CppCompiler, out_dir: &Path) -> Argument<'_> { Argument::from(format!( "-fprebuilt-module-path={}", diff --git a/zork++/src/lib/compiler/mod.rs b/zork++/src/lib/compiler/mod.rs index 2309f915..18fba35d 100644 --- a/zork++/src/lib/compiler/mod.rs +++ b/zork++/src/lib/compiler/mod.rs @@ -197,7 +197,7 @@ mod sources { match compiler { CppCompiler::CLANG => { - arguments.extend(clang_args::add_std_lib(model)); + arguments.extend(model.compiler.stdlib_arg()); arguments.extend_from_slice(target.extra_args()); arguments.push(Argument::from("-fimplicit-modules")); arguments.push(clang_args::implicit_module_maps(out_dir)); @@ -225,7 +225,7 @@ mod sources { arguments.push(Argument::from("/EHsc")); arguments.push(Argument::from("/nologo")); // If /std:c++20 this, else should be the direct options - // available on C++23 to use directly import std by precompiling the standard library + // available on C++23 to use directly import std by pre-compiling the standard library arguments.push(Argument::from("/experimental:module")); arguments.push(Argument::from("/stdIfcDir \"$(VC_IFCPath)\"")); @@ -294,7 +294,7 @@ mod sources { match compiler { CppCompiler::CLANG => { - arguments.extend(clang_args::add_std_lib(model)); + arguments.extend(model.compiler.stdlib_arg()); arguments.push(Argument::from("-fimplicit-modules")); arguments.push(clang_args::implicit_module_maps(out_dir)); arguments.push(clang_args::add_prebuilt_module_path(compiler, out_dir)); @@ -305,7 +305,7 @@ mod sources { arguments.push(Argument::from("/EHsc")); arguments.push(Argument::from("/nologo")); // If /std:c++20 this, else should be the direct options - // available on C++23 to use directly import std by precompiling the standard library + // available on C++23 to use directly import std by pre-compiling the standard library arguments.push(Argument::from("/experimental:module")); arguments.push(Argument::from("/stdIfcDir \"$(VC_IFCPath)\"")); @@ -358,7 +358,7 @@ mod sources { match compiler { CppCompiler::CLANG => { - arguments.extend(clang_args::add_std_lib(model)); + arguments.extend(model.compiler.stdlib_arg()); arguments.push(Argument::from("-fimplicit-modules")); arguments.push(Argument::from("-x")); arguments.push(Argument::from("c++-module")); @@ -463,7 +463,7 @@ mod sources { match compiler { CppCompiler::CLANG => { - arguments.extend(clang_args::add_std_lib(model)); + arguments.extend(model.compiler.stdlib_arg()); arguments.push(Argument::from("-fimplicit-modules")); arguments.push(Argument::from("-c")); arguments.push(clang_args::implicit_module_maps(out_dir)); From 5d2846f8315d0aa97e7ba1e5c337033cff6fd1c6 Mon Sep 17 00:00:00 2001 From: Alex Vergara Date: Tue, 14 Mar 2023 23:36:47 +0100 Subject: [PATCH 35/60] The output directory now is an absolute path --- zork++/benches/benchmarks.rs | 3 ++- zork++/src/lib/cache/mod.rs | 4 ++-- zork++/src/lib/cli/output/arguments.rs | 2 +- zork++/src/lib/compiler/mod.rs | 16 ++++++------- zork++/src/lib/lib.rs | 14 ++++++----- zork++/src/lib/project_model/build.rs | 6 ++--- zork++/src/lib/project_model/mod.rs | 2 +- zork++/src/lib/utils/constants.rs | 4 ++-- zork++/src/lib/utils/fs.rs | 21 +++++++++++++++-- zork++/src/lib/utils/reader.rs | 32 +++++++++++++++----------- 10 files changed, 65 insertions(+), 39 deletions(-) diff --git a/zork++/benches/benchmarks.rs b/zork++/benches/benchmarks.rs index a0f03991..bbf5859c 100644 --- a/zork++/benches/benchmarks.rs +++ b/zork++/benches/benchmarks.rs @@ -1,5 +1,6 @@ //! Benchmarks tests for measuring the performance of the code +use std::path::Path; use criterion::{black_box, criterion_group, criterion_main, Criterion}; use zork::{ cache::{self, ZorkCache}, @@ -11,7 +12,7 @@ use zork::{ pub fn build_project_benchmark(c: &mut Criterion) { let config: ZorkConfigFile = toml::from_str(utils::constants::CONFIG_FILE_MOCK).unwrap(); - let program_data = build_model(&config); + let program_data = build_model(&config, Path::new(".")); let cache = ZorkCache::default(); c.bench_function("Build project", |b| { diff --git a/zork++/src/lib/cache/mod.rs b/zork++/src/lib/cache/mod.rs index a5ea5829..baa626a3 100644 --- a/zork++/src/lib/cache/mod.rs +++ b/zork++/src/lib/cache/mod.rs @@ -28,7 +28,7 @@ use walkdir::WalkDir; /// Standalone utility for retrieve the Zork++ cache file pub fn load(program_data: &ZorkModel<'_>, cli_args: &CliArgs) -> Result { let compiler = program_data.compiler.cpp_compiler.as_ref(); - let cache_path = &Path::new(program_data.build.output_dir) + let cache_path = &program_data.build.output_dir .join("zork") .join("cache") .join(compiler); @@ -60,7 +60,7 @@ pub fn save( commands: Commands<'_>, test_mode: bool, ) -> Result<()> { - let cache_path = &Path::new(program_data.build.output_dir) + let cache_path = &program_data.build.output_dir .join("zork") .join("cache") .join(program_data.compiler.cpp_compiler.as_ref()) diff --git a/zork++/src/lib/cli/output/arguments.rs b/zork++/src/lib/cli/output/arguments.rs index 38f3d83d..f1ea8eaa 100644 --- a/zork++/src/lib/cli/output/arguments.rs +++ b/zork++/src/lib/cli/output/arguments.rs @@ -65,7 +65,7 @@ impl<'a> core::fmt::Display for Argument<'a> { pub mod clang_args { use std::path::Path; - use crate::project_model::{compiler::CppCompiler, ZorkModel}; + use crate::project_model::{compiler::CppCompiler}; use super::*; diff --git a/zork++/src/lib/compiler/mod.rs b/zork++/src/lib/compiler/mod.rs index 18fba35d..e09cdb4d 100644 --- a/zork++/src/lib/compiler/mod.rs +++ b/zork++/src/lib/compiler/mod.rs @@ -85,7 +85,7 @@ fn build_sources<'a>( log::trace!("Source file: {:?} was not modified since the last iteration. No need to rebuilt it again.", &src.file()); commands.sources.push(command_line); commands.generated_files_paths.push(Argument::from(helpers::generate_obj_file( - model.compiler.cpp_compiler, model.build.output_dir, src + model.compiler.cpp_compiler, &model.build.output_dir, src ).to_string())) }); @@ -130,7 +130,7 @@ fn prebuild_module_interfaces<'a>( log::trace!("Source file:{:?} was not modified since the last iteration. No need to rebuilt it again.", &module_interface.file()); commands.interfaces.push(command_line); commands.generated_files_paths.push(Argument::from(helpers::generate_prebuild_miu( - model.compiler.cpp_compiler, model.build.output_dir, module_interface + model.compiler.cpp_compiler, &model.build.output_dir, module_interface ))) } }); @@ -155,7 +155,7 @@ fn compile_module_implementations<'a>( log::trace!("Source file:{:?} was not modified since the last iteration. No need to rebuilt it again.", &module_impl.file()); commands.implementations.push(command_line); commands.generated_files_paths.push(Argument::from(helpers::generate_impl_obj_file( - model.compiler.cpp_compiler, model.build.output_dir, module_impl + model.compiler.cpp_compiler, &model.build.output_dir, module_impl ))) } }); @@ -189,7 +189,7 @@ mod sources { log::info!("Generating the main command line..."); let compiler = &model.compiler.cpp_compiler; - let out_dir = model.build.output_dir; + let out_dir = model.build.output_dir.as_ref(); let executable_name = target.name(); let mut arguments = Vec::new(); @@ -285,7 +285,7 @@ mod sources { source: &'a SourceFile, ) { let compiler = model.compiler.cpp_compiler; - let out_dir = model.build.output_dir; + let out_dir = model.build.output_dir.as_ref(); let mut arguments = Vec::new(); arguments.push(model.compiler.language_level_arg()); @@ -351,7 +351,7 @@ mod sources { commands: &mut Commands<'a>, ) { let compiler = model.compiler.cpp_compiler; - let out_dir = model.build.output_dir; + let out_dir = model.build.output_dir.as_ref(); let mut arguments = Vec::with_capacity(8); arguments.push(model.compiler.language_level_arg()); @@ -456,7 +456,7 @@ mod sources { commands: &mut Commands<'a>, ) { let compiler = model.compiler.cpp_compiler; - let out_dir = model.build.output_dir; + let out_dir = model.build.output_dir.as_ref(); let mut arguments = Vec::with_capacity(12); arguments.push(model.compiler.language_level_arg()); @@ -659,7 +659,7 @@ mod helpers { if model.compiler.cpp_compiler.eq(&CppCompiler::CLANG) { v.push(Argument::from("-o")); v.push(Argument::from( - Path::new(model.build.output_dir) + Path::new(&model.build.output_dir) .join(model.compiler.cpp_compiler.as_ref()) .join("modules") .join("interfaces") diff --git a/zork++/src/lib/lib.rs b/zork++/src/lib/lib.rs index dc8ec461..da358b2d 100644 --- a/zork++/src/lib/lib.rs +++ b/zork++/src/lib/lib.rs @@ -66,7 +66,7 @@ pub mod worker { let config: ZorkConfigFile = toml::from_str(raw_file.as_str()) .with_context(|| "Could not parse configuration file")?; - let program_data = build_model(&config); + let program_data = build_model(&config, Path::new("."))?; create_output_directory(&program_data)?; let cache = cache::load(&program_data, cli_args) @@ -109,7 +109,7 @@ pub mod worker { match commands::run_generated_commands(program_data, commands, &mut cache, false) { Ok(_) => autorun_generated_binary( &program_data.compiler.cpp_compiler, - program_data.build.output_dir, + &program_data.build.output_dir, program_data.executable.executable_name, ), Err(e) => Err(e), @@ -122,7 +122,7 @@ pub mod worker { match commands::run_generated_commands(program_data, commands, &mut cache, true) { Ok(_) => autorun_generated_binary( &program_data.compiler.cpp_compiler, - program_data.build.output_dir, + &program_data.build.output_dir, &program_data.tests.test_executable_name, ), Err(e) => Err(e), @@ -151,7 +151,7 @@ pub mod worker { /// in order to track different aspects of the program (last time /// modified files, last process build time...) fn create_output_directory(model: &ZorkModel) -> Result<()> { - let out_dir = model.build.output_dir; + let out_dir = &model.build.output_dir; let compiler = &model.compiler.cpp_compiler; // Recursively create a directory and all of its parent components if they are missing @@ -185,7 +185,8 @@ pub mod worker { #[cfg(test)] mod tests { - use color_eyre::Result; + use std::path::Path; + use color_eyre::{Result, eyre::Context}; use tempfile::tempdir; use crate::config_file::ZorkConfigFile; @@ -200,7 +201,8 @@ pub mod worker { .replace("", "clang") .replace('\\', "/"); let zcf: ZorkConfigFile = toml::from_str(&normalized_cfg_file)?; - let model = build_model(&zcf); + let model = build_model(&zcf, Path::new(".")) + .with_context(|| "Error building the project model")?; // This should create and out/ directory in the ./zork++ folder at the root of this project super::create_output_directory(&model)?; diff --git a/zork++/src/lib/project_model/build.rs b/zork++/src/lib/project_model/build.rs index 3d8ed50f..564574bc 100644 --- a/zork++/src/lib/project_model/build.rs +++ b/zork++/src/lib/project_model/build.rs @@ -1,6 +1,6 @@ -use std::path::Path; +use std::path::PathBuf; #[derive(Debug, PartialEq, Eq)] -pub struct BuildModel<'a> { - pub output_dir: &'a Path, +pub struct BuildModel { + pub output_dir: PathBuf, } diff --git a/zork++/src/lib/project_model/mod.rs b/zork++/src/lib/project_model/mod.rs index 3f482472..467e9046 100644 --- a/zork++/src/lib/project_model/mod.rs +++ b/zork++/src/lib/project_model/mod.rs @@ -17,7 +17,7 @@ use self::{ pub struct ZorkModel<'a> { pub project: ProjectModel<'a>, pub compiler: CompilerModel<'a>, - pub build: BuildModel<'a>, + pub build: BuildModel, pub executable: ExecutableModel<'a>, pub modules: ModulesModel<'a>, pub tests: TestsModel<'a>, diff --git a/zork++/src/lib/utils/constants.rs b/zork++/src/lib/utils/constants.rs index 5b5f3240..c9fb48db 100644 --- a/zork++/src/lib/utils/constants.rs +++ b/zork++/src/lib/utils/constants.rs @@ -2,7 +2,7 @@ pub const CONFIG_FILE_NAME: &str = "zork"; pub const CONFIG_FILE_EXT: &str = ".toml"; -pub const DEFAULT_OUTPUT_DIR: &str = "./out"; +pub const DEFAULT_OUTPUT_DIR: &str = "out"; pub const BINARY_EXTENSION: &str = if cfg!(target_os = "windows") { "exe" @@ -31,7 +31,7 @@ std_lib = "libc++" extra_args = [ "-Wall" ] [build] -output_dir = "build" +output_dir = "" [executable] executable_name = "zork" diff --git a/zork++/src/lib/utils/fs.rs b/zork++/src/lib/utils/fs.rs index 6efa051a..96ef9bd9 100644 --- a/zork++/src/lib/utils/fs.rs +++ b/zork++/src/lib/utils/fs.rs @@ -30,8 +30,25 @@ pub fn create_directory(path_create: &Path) -> Result<()> { .with_context(|| format!("Could not create directory {path_create:?}")) } + +#[inline(always)] +pub fn get_project_root_absolute_path(project_root: &Path) -> Result { + let mut canonical = project_root + .canonicalize() + .with_context(|| format!("Error getting the canonical path for the project root: {project_root:?}"))?; + if cfg!(target_os = "windows") { + canonical = canonical + .to_str() + .map(|unc| &unc[4..]) + .unwrap_or_default() + .into() + } + + Ok(canonical) +} + /// Gets the absolute route for an element in the system given a path P, -/// without the extension is P belongs to a file +/// without the extension if P belongs to a file pub fn get_absolute_path>(p: P) -> Result { let mut canonical = p .as_ref() @@ -53,7 +70,7 @@ pub fn get_absolute_path>(p: P) -> Result { .join(file_stem)) } -/// +/// Returns a tuple of elements containing the directory of a file, its file stem and its extension pub fn get_file_details>(p: P) -> Result<(PathBuf, String, String)> { let mut canonical = p .as_ref() diff --git a/zork++/src/lib/utils/reader.rs b/zork++/src/lib/utils/reader.rs index a8d652f5..ff768ca8 100644 --- a/zork++/src/lib/utils/reader.rs +++ b/zork++/src/lib/utils/reader.rs @@ -27,6 +27,7 @@ use crate::{ use color_eyre::{eyre::eyre, Result}; use std::path::{Path, PathBuf}; use walkdir::{DirEntry, WalkDir}; +use crate::utils::fs::get_project_root_absolute_path; use super::constants::DEFAULT_OUTPUT_DIR; @@ -77,22 +78,26 @@ pub fn find_config_files(base_path: &Path) -> Result> { } } -pub fn build_model<'a>(config: &'a ZorkConfigFile) -> ZorkModel<'a> { +pub fn build_model<'a>(config: &'a ZorkConfigFile, project_root: &Path) -> Result> { let project = assemble_project_model(&config.project); + + // TODO Project root also may come from the config line, not only from the CLI + let absolute_project_root = get_project_root_absolute_path(project_root)?; + let compiler = assemble_compiler_model(&config.compiler); - let build = assemble_build_model(&config.build); + let build = assemble_build_model(&config.build, &absolute_project_root); let executable = assemble_executable_model(project.name, &config.executable); let modules = assemble_modules_model(&config.modules); let tests = assemble_tests_model(project.name, &config.tests); - ZorkModel { + Ok(ZorkModel { project, compiler, build, executable, modules, tests, - } + }) } fn assemble_project_model<'a>(config: &'a ProjectAttribute) -> ProjectModel<'a> { @@ -121,14 +126,15 @@ fn assemble_compiler_model<'a>(config: &'a CompilerAttribute) -> CompilerModel<' } } -fn assemble_build_model<'a>(config: &'a Option) -> BuildModel<'a> { +fn assemble_build_model(config: &Option, project_root: &Path) -> BuildModel { let output_dir = config .as_ref() .and_then(|build| build.output_dir) + .map(|out_dir| out_dir.strip_prefix("./").unwrap_or_default()) .unwrap_or(DEFAULT_OUTPUT_DIR); BuildModel { - output_dir: Path::new(output_dir), + output_dir: Path::new(project_root).join(output_dir), } } @@ -293,7 +299,7 @@ fn assemble_tests_model<'a>( fn get_sourceset_for(srcs: Vec<&str>) -> SourceSet { let sources = srcs - .into_iter() + .iter() .map(|src| { if src.contains('*') { Source::Glob(GlobPattern(src)) @@ -341,7 +347,7 @@ mod test { "#; let config: ZorkConfigFile = toml::from_str(CONFIG_FILE_MOCK)?; - let model = build_model(&config); + let model = build_model(&config, Path::new(".")); let expected = ZorkModel { project: ProjectModel { @@ -356,7 +362,7 @@ mod test { extra_args: vec![], }, build: BuildModel { - output_dir: Path::new("./out"), + output_dir: PathBuf::from("./out").canonicalize()?, }, executable: ExecutableModel { executable_name: "Zork++", @@ -377,7 +383,7 @@ mod test { }, }; - assert_eq!(model, expected); + assert_eq!(model.unwrap(), expected); Ok(()) } @@ -385,7 +391,7 @@ mod test { #[test] fn test_project_model_with_full_config() -> Result<()> { let config: ZorkConfigFile = toml::from_str(utils::constants::CONFIG_FILE_MOCK)?; - let model = build_model(&config); + let model = build_model(&config, Path::new(".")); let expected = ZorkModel { project: ProjectModel { @@ -400,7 +406,7 @@ mod test { extra_args: vec![Argument::from("-Wall")], }, build: BuildModel { - output_dir: Path::new("build"), + output_dir: PathBuf::from(".").canonicalize()?, }, executable: ExecutableModel { executable_name: "zork", @@ -451,7 +457,7 @@ mod test { }, }; - assert_eq!(model, expected); + assert_eq!(model.unwrap(), expected); Ok(()) } From 38f8534d7b1b79a10af7cf243935f2624621f80c Mon Sep 17 00:00:00 2001 From: Alex Vergara Date: Wed, 15 Mar 2023 01:23:44 +0100 Subject: [PATCH 36/60] Solved issues with absolute paths for unit tests that was using mocked data that didn't know about canonical paths --- CHANGELOG.md | 23 ++++++ zork++/benches/benchmarks.rs | 4 +- zork++/src/lib/cache/mod.rs | 8 +- zork++/src/lib/cli/output/arguments.rs | 2 +- zork++/src/lib/config_file/project.rs | 2 + zork++/src/lib/lib.rs | 2 +- zork++/src/lib/project_model/project.rs | 1 + zork++/src/lib/utils/constants.rs | 10 +-- zork++/src/lib/utils/fs.rs | 23 ++---- zork++/src/lib/utils/reader.rs | 99 ++++++++++++++++--------- 10 files changed, 110 insertions(+), 64 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3a6e9e40..2b984df7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,29 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [Unreleased, TODOs] +- Add extra arguments for the modules +- nº of iterations of the program to automatically clear the cache (new model, cache) +- new model, for the compilation database? +- make the project model full owned, and cache it? + +## [0.8.1] - 2023 - 03 - 15 + +### Feature + +- Constructed the full path for the output directory from the one specified or defaulted +in the configuration + +### Update + +- Removed the restriction that doesn't allows the user to be able to link against `libc++` +in Windows + +### Upgrade +- The absolute paths for all the declared files is preloaded from the declared root from +the project, avoiding make a .canonicalize() call for every one, raising the performance +of the project model build process + ## [0.8.0] - 2023 - 03 - 12 ### Feature diff --git a/zork++/benches/benchmarks.rs b/zork++/benches/benchmarks.rs index bbf5859c..4b8f6763 100644 --- a/zork++/benches/benchmarks.rs +++ b/zork++/benches/benchmarks.rs @@ -1,7 +1,7 @@ //! Benchmarks tests for measuring the performance of the code -use std::path::Path; use criterion::{black_box, criterion_group, criterion_main, Criterion}; +use std::path::Path; use zork::{ cache::{self, ZorkCache}, cli::input::CliArgs, @@ -12,7 +12,7 @@ use zork::{ pub fn build_project_benchmark(c: &mut Criterion) { let config: ZorkConfigFile = toml::from_str(utils::constants::CONFIG_FILE_MOCK).unwrap(); - let program_data = build_model(&config, Path::new(".")); + let program_data = build_model(&config, Path::new(".")).unwrap(); let cache = ZorkCache::default(); c.bench_function("Build project", |b| { diff --git a/zork++/src/lib/cache/mod.rs b/zork++/src/lib/cache/mod.rs index baa626a3..bbff652b 100644 --- a/zork++/src/lib/cache/mod.rs +++ b/zork++/src/lib/cache/mod.rs @@ -28,7 +28,9 @@ use walkdir::WalkDir; /// Standalone utility for retrieve the Zork++ cache file pub fn load(program_data: &ZorkModel<'_>, cli_args: &CliArgs) -> Result { let compiler = program_data.compiler.cpp_compiler.as_ref(); - let cache_path = &program_data.build.output_dir + let cache_path = &program_data + .build + .output_dir .join("zork") .join("cache") .join(compiler); @@ -60,7 +62,9 @@ pub fn save( commands: Commands<'_>, test_mode: bool, ) -> Result<()> { - let cache_path = &program_data.build.output_dir + let cache_path = &program_data + .build + .output_dir .join("zork") .join("cache") .join(program_data.compiler.cpp_compiler.as_ref()) diff --git a/zork++/src/lib/cli/output/arguments.rs b/zork++/src/lib/cli/output/arguments.rs index f1ea8eaa..76a9e3c3 100644 --- a/zork++/src/lib/cli/output/arguments.rs +++ b/zork++/src/lib/cli/output/arguments.rs @@ -65,7 +65,7 @@ impl<'a> core::fmt::Display for Argument<'a> { pub mod clang_args { use std::path::Path; - use crate::project_model::{compiler::CppCompiler}; + use crate::project_model::compiler::CppCompiler; use super::*; diff --git a/zork++/src/lib/config_file/project.rs b/zork++/src/lib/config_file/project.rs index 65c95574..ff777120 100644 --- a/zork++/src/lib/config_file/project.rs +++ b/zork++/src/lib/config_file/project.rs @@ -24,6 +24,7 @@ use serde::*; /// assert_eq!(config.name, "Zork++ serde tests"); /// assert_eq!(config.authors, Some(vec!["zerodaycode.gz@gmail.com"])); /// assert_eq!(config.compilation_db, Some(true)); +/// assert_eq!(config.project_root, None); /// ``` /// /// > Note: TOML table are toml commented (#) to allow us to parse @@ -41,4 +42,5 @@ pub struct ProjectAttribute<'a> { #[serde(borrow)] pub authors: Option>, pub compilation_db: Option, + pub project_root: Option<&'a str>, } diff --git a/zork++/src/lib/lib.rs b/zork++/src/lib/lib.rs index da358b2d..939eb99f 100644 --- a/zork++/src/lib/lib.rs +++ b/zork++/src/lib/lib.rs @@ -185,8 +185,8 @@ pub mod worker { #[cfg(test)] mod tests { + use color_eyre::{eyre::Context, Result}; use std::path::Path; - use color_eyre::{Result, eyre::Context}; use tempfile::tempdir; use crate::config_file::ZorkConfigFile; diff --git a/zork++/src/lib/project_model/project.rs b/zork++/src/lib/project_model/project.rs index acbde8f8..4f3f0653 100644 --- a/zork++/src/lib/project_model/project.rs +++ b/zork++/src/lib/project_model/project.rs @@ -3,4 +3,5 @@ pub struct ProjectModel<'a> { pub name: &'a str, pub authors: &'a [&'a str], pub compilation_db: bool, + pub project_root: Option<&'a str>, } diff --git a/zork++/src/lib/utils/constants.rs b/zork++/src/lib/utils/constants.rs index c9fb48db..600b0918 100644 --- a/zork++/src/lib/utils/constants.rs +++ b/zork++/src/lib/utils/constants.rs @@ -50,15 +50,15 @@ sources = [ extra_args = [ "-pedantic" ] [modules] -base_ifcs_dir = "ifc" +base_ifcs_dir = "ifcs" interfaces = [ - { file = "math.cppm" }, - { file = 'some_module.cppm', module_name = 'math' } + { file = "maths.cppm" }, + { file = 'some_module.cppm', module_name = 'maths' } ] -base_impls_dir = "src" +base_impls_dir = "srcs" implementations = [ - { file = "math.cpp" }, + { file = "maths.cpp" }, { file = 'some_module_impl.cpp', dependencies = ['iostream'] } ] sys_modules = [ "iostream" ] diff --git a/zork++/src/lib/utils/fs.rs b/zork++/src/lib/utils/fs.rs index 96ef9bd9..99a85e1a 100644 --- a/zork++/src/lib/utils/fs.rs +++ b/zork++/src/lib/utils/fs.rs @@ -30,12 +30,11 @@ pub fn create_directory(path_create: &Path) -> Result<()> { .with_context(|| format!("Could not create directory {path_create:?}")) } - #[inline(always)] pub fn get_project_root_absolute_path(project_root: &Path) -> Result { - let mut canonical = project_root - .canonicalize() - .with_context(|| format!("Error getting the canonical path for the project root: {project_root:?}"))?; + let mut canonical = project_root.canonicalize().with_context(|| { + format!("Error getting the canonical path for the project root: {project_root:?}") + })?; if cfg!(target_os = "windows") { canonical = canonical .to_str() @@ -72,28 +71,18 @@ pub fn get_absolute_path>(p: P) -> Result { /// Returns a tuple of elements containing the directory of a file, its file stem and its extension pub fn get_file_details>(p: P) -> Result<(PathBuf, String, String)> { - let mut canonical = p + let file_stem = p .as_ref() - .canonicalize() - .with_context(|| format!("Error getting the canonical path for: {:?}", p.as_ref()))?; - if cfg!(target_os = "windows") { - canonical = canonical - .to_str() - .map(|unc| &unc[4..]) - .unwrap_or_default() - .into() - } - let file_stem = canonical .file_stem() .with_context(|| format!("Unable to get the file stem for {:?}", p.as_ref()))?; Ok(( - canonical + p.as_ref() .parent() .unwrap_or_else(|| panic!("Unexpected error getting the parent of {:?}", p.as_ref())) .to_path_buf(), file_stem.to_str().unwrap_or_default().to_string(), - canonical.extension().map_or_else( + p.as_ref().extension().map_or_else( || String::with_capacity(0), |os_str| os_str.to_str().unwrap_or_default().to_string(), ), diff --git a/zork++/src/lib/utils/reader.rs b/zork++/src/lib/utils/reader.rs index ff768ca8..a9fcdecf 100644 --- a/zork++/src/lib/utils/reader.rs +++ b/zork++/src/lib/utils/reader.rs @@ -1,4 +1,5 @@ use crate::project_model::sourceset::SourceFile; +use crate::utils::fs::get_project_root_absolute_path; use crate::{ cli::output::arguments::Argument, config_file::{ @@ -27,7 +28,6 @@ use crate::{ use color_eyre::{eyre::eyre, Result}; use std::path::{Path, PathBuf}; use walkdir::{DirEntry, WalkDir}; -use crate::utils::fs::get_project_root_absolute_path; use super::constants::DEFAULT_OUTPUT_DIR; @@ -78,17 +78,25 @@ pub fn find_config_files(base_path: &Path) -> Result> { } } -pub fn build_model<'a>(config: &'a ZorkConfigFile, project_root: &Path) -> Result> { +pub fn build_model<'a>( + config: &'a ZorkConfigFile, + project_root_from_cli: &Path, +) -> Result> { let project = assemble_project_model(&config.project); - // TODO Project root also may come from the config line, not only from the CLI - let absolute_project_root = get_project_root_absolute_path(project_root)?; + let absolute_project_root = get_project_root_absolute_path( + project + .project_root + .map(Path::new) + .unwrap_or(project_root_from_cli), + )?; let compiler = assemble_compiler_model(&config.compiler); let build = assemble_build_model(&config.build, &absolute_project_root); - let executable = assemble_executable_model(project.name, &config.executable); - let modules = assemble_modules_model(&config.modules); - let tests = assemble_tests_model(project.name, &config.tests); + let executable = + assemble_executable_model(project.name, &config.executable, &absolute_project_root); + let modules = assemble_modules_model(&config.modules, &absolute_project_root); + let tests = assemble_tests_model(project.name, &config.tests, &absolute_project_root); Ok(ZorkModel { project, @@ -108,6 +116,7 @@ fn assemble_project_model<'a>(config: &'a ProjectAttribute) -> ProjectModel<'a> .as_ref() .map_or_else(|| &[] as &[&str], |auths| auths.as_slice()), compilation_db: config.compilation_db.unwrap_or_default(), + project_root: config.project_root, } } @@ -130,7 +139,7 @@ fn assemble_build_model(config: &Option, project_root: &Path) -> let output_dir = config .as_ref() .and_then(|build| build.output_dir) - .map(|out_dir| out_dir.strip_prefix("./").unwrap_or_default()) + .map(|out_dir| out_dir.strip_prefix("./").unwrap_or(out_dir)) .unwrap_or(DEFAULT_OUTPUT_DIR); BuildModel { @@ -142,6 +151,7 @@ fn assemble_build_model(config: &Option, project_root: &Path) -> fn assemble_executable_model<'a>( project_name: &'a str, config: &'a Option, + project_root: &Path, ) -> ExecutableModel<'a> { let config = config.as_ref(); @@ -153,7 +163,7 @@ fn assemble_executable_model<'a>( .and_then(|exe| exe.sources.clone()) .unwrap_or_default(); - let sourceset = get_sourceset_for(sources); + let sourceset = get_sourceset_for(sources, project_root); let extra_args = config .and_then(|exe| exe.extra_args.as_ref()) @@ -167,7 +177,10 @@ fn assemble_executable_model<'a>( } } -fn assemble_modules_model<'a>(config: &'a Option) -> ModulesModel<'a> { +fn assemble_modules_model<'a>( + config: &'a Option, + project_root: &Path, +) -> ModulesModel<'a> { let config = config.as_ref(); let base_ifcs_dir = config @@ -178,7 +191,7 @@ fn assemble_modules_model<'a>(config: &'a Option) -> ModulesMo .and_then(|modules| modules.interfaces.as_ref()) .map(|ifcs| { ifcs.iter() - .map(|m_ifc| assemble_module_interface_model(m_ifc, base_ifcs_dir)) + .map(|m_ifc| assemble_module_interface_model(m_ifc, base_ifcs_dir, project_root)) .collect() }) .unwrap_or_default(); @@ -192,7 +205,9 @@ fn assemble_modules_model<'a>(config: &'a Option) -> ModulesMo .map(|impls| { impls .iter() - .map(|m_impl| assemble_module_implementation_model(m_impl, base_impls_dir)) + .map(|m_impl| { + assemble_module_implementation_model(m_impl, base_impls_dir, project_root) + }) .collect() }) .unwrap_or_default(); @@ -213,8 +228,9 @@ fn assemble_modules_model<'a>(config: &'a Option) -> ModulesMo fn assemble_module_interface_model<'a>( config: &'a ModuleInterface, base_path: &str, + project_root: &Path, ) -> ModuleInterfaceModel<'a> { - let file_path = Path::new(base_path).join(config.file); + let file_path = Path::new(project_root).join(base_path).join(config.file); let module_name = config.module_name.unwrap_or_else(|| { Path::new(config.file) .file_stem() @@ -232,7 +248,9 @@ fn assemble_module_interface_model<'a>( )) }; - let file_details = utils::fs::get_file_details(file_path).unwrap_or_default(); // TODO Care with this, refactor + let file_details = utils::fs::get_file_details(&file_path).unwrap_or_else(|_| { + panic!("An unexpected error happened getting the file details for {file_path:?}") + }); ModuleInterfaceModel { path: file_details.0, file_stem: file_details.1, @@ -246,8 +264,9 @@ fn assemble_module_interface_model<'a>( fn assemble_module_implementation_model<'a>( config: &'a ModuleImplementation, base_path: &str, + project_root: &Path, ) -> ModuleImplementationModel<'a> { - let file_path = Path::new(base_path).join(config.file); + let file_path = Path::new(project_root).join(base_path).join(config.file); let mut dependencies = config.dependencies.clone().unwrap_or_default(); if dependencies.is_empty() { let last_dot_index = config.file.rfind('.'); @@ -259,7 +278,8 @@ fn assemble_module_implementation_model<'a>( } } - let file_details = utils::fs::get_file_details(file_path).unwrap_or_default(); // TODO Care with this, refactor + let file_details = utils::fs::get_file_details(&file_path) + .unwrap_or_else(|_| panic!("An unexpected error happened getting the file details for {file_path:?}")); ModuleImplementationModel { path: file_details.0, @@ -272,6 +292,7 @@ fn assemble_module_implementation_model<'a>( fn assemble_tests_model<'a>( project_name: &'a str, config: &'a Option, + project_root: &Path, ) -> TestsModel<'a> { let config = config.as_ref(); @@ -283,7 +304,7 @@ fn assemble_tests_model<'a>( let sources = config .and_then(|exe| exe.sources.clone()) .unwrap_or_default(); - let sourceset = get_sourceset_for(sources); + let sourceset = get_sourceset_for(sources, project_root); let extra_args = config .and_then(|test| test.extra_args.as_ref()) @@ -297,7 +318,7 @@ fn assemble_tests_model<'a>( } } -fn get_sourceset_for(srcs: Vec<&str>) -> SourceSet { +fn get_sourceset_for(srcs: Vec<&str>, project_root: &Path) -> SourceSet { let sources = srcs .iter() .map(|src| { @@ -313,7 +334,8 @@ fn get_sourceset_for(srcs: Vec<&str>) -> SourceSet { .expect("Error getting the declared paths for the source files") }) .map(|pb| { - let file_details = utils::fs::get_file_details(pb).unwrap_or_default(); + let file_details = utils::fs::get_file_details(project_root.join(&pb)) + .unwrap_or_else(|_| panic!("An unexpected error happened getting the file details for {pb:?}")); SourceFile { path: file_details.0, file_stem: file_details.1, @@ -327,6 +349,7 @@ fn get_sourceset_for(srcs: Vec<&str>) -> SourceSet { #[cfg(test)] mod test { + use crate::utils::fs; use crate::{ project_model::compiler::{CppCompiler, LanguageLevel, StdLib}, utils, @@ -354,6 +377,7 @@ mod test { name: "Zork++", authors: &["zerodaycode.gz@gmail.com"], compilation_db: false, + project_root: None, }, compiler: CompilerModel { cpp_compiler: CppCompiler::CLANG, @@ -393,11 +417,14 @@ mod test { let config: ZorkConfigFile = toml::from_str(utils::constants::CONFIG_FILE_MOCK)?; let model = build_model(&config, Path::new(".")); + let abs_path_for_mock = fs::get_absolute_path(Path::new("."))?; + let expected = ZorkModel { project: ProjectModel { name: "Zork++", authors: &["zerodaycode.gz@gmail.com"], compilation_db: true, + project_root: None, }, compiler: CompilerModel { cpp_compiler: CppCompiler::CLANG, @@ -414,37 +441,37 @@ mod test { extra_args: vec![Argument::from("-Werr")], }, modules: ModulesModel { - base_ifcs_dir: Path::new("ifc"), + base_ifcs_dir: Path::new("ifcs"), interfaces: vec![ ModuleInterfaceModel { - path: PathBuf::from(""), - file_stem: String::from(""), - extension: String::from(""), - module_name: "math", + path: abs_path_for_mock.join("ifcs"), + file_stem: String::from("maths"), + extension: String::from("cppm"), + module_name: "maths", partition: None, dependencies: vec![], }, ModuleInterfaceModel { - path: PathBuf::from(""), - file_stem: String::from(""), - extension: String::from(""), - module_name: "math", + path: abs_path_for_mock.join("ifcs"), + file_stem: String::from("some_module"), + extension: String::from("cppm"), + module_name: "maths", partition: None, dependencies: vec![], }, ], - base_impls_dir: Path::new("src"), + base_impls_dir: Path::new("srcs"), implementations: vec![ ModuleImplementationModel { - path: PathBuf::from(""), - file_stem: String::from(""), - extension: String::from(""), - dependencies: vec!["math"], + path: abs_path_for_mock.join("srcs"), + file_stem: String::from("maths"), + extension: String::from("cpp"), + dependencies: vec!["maths"], }, ModuleImplementationModel { - path: PathBuf::from(""), - file_stem: String::from(""), - extension: String::from(""), + path: abs_path_for_mock.join("srcs"), + file_stem: String::from("some_module_impl"), + extension: String::from("cpp"), dependencies: vec!["iostream"], }, ], From c5b917ea668687966b139b2ff976dbe4b1546955 Mon Sep 17 00:00:00 2001 From: Alex Vergara Date: Wed, 15 Mar 2023 11:38:24 +0100 Subject: [PATCH 37/60] Updating the CHANGELOG.md to reflect the upcoming ideas that will be implemented in the next releases --- CHANGELOG.md | 10 +++++++++- zork++/src/lib/compiler/mod.rs | 4 +++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2b984df7..e9a372b9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,8 +8,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased, TODOs] - Add extra arguments for the modules - nº of iterations of the program to automatically clear the cache (new model, cache) -- new model, for the compilation database? +- new model, for the compilation database? or feature key in the project? maybe we want to +enable the cache per compiler? - make the project model full owned, and cache it? +- full path for the Clang's Windows modulemap +- strong type for `Vec`, that is initialized with a capacity that is equals +to the specific nº of args generated by process and by compiler, plus the extra arguments +from the compiler, and from every category +- Avoid to renew the already generated files in the `intrinsics` directory, and probably +generate a second `std.h` to bind to the Zork++ autogenerated `modulemap` when `libc++` +is selected as the target standard library ## [0.8.1] - 2023 - 03 - 15 diff --git a/zork++/src/lib/compiler/mod.rs b/zork++/src/lib/compiler/mod.rs index e09cdb4d..1907eef1 100644 --- a/zork++/src/lib/compiler/mod.rs +++ b/zork++/src/lib/compiler/mod.rs @@ -353,7 +353,9 @@ mod sources { let compiler = model.compiler.cpp_compiler; let out_dir = model.build.output_dir.as_ref(); - let mut arguments = Vec::with_capacity(8); + let mut arguments = Vec::with_capacity(8 + model.compiler.extra_args.len()); + // Clang 8, MSVC 15, GCC 7 - TODO Make a strong type for Vec that is initialized + // with the correct capacity given a compiler arguments.push(model.compiler.language_level_arg()); match compiler { From 37ca2a6ae6524a5780d29d8578b6b0f5ef645d89 Mon Sep 17 00:00:00 2001 From: Alex Vergara Date: Wed, 15 Mar 2023 21:25:33 +0100 Subject: [PATCH 38/60] Version 0.8.1 --- CHANGELOG.md | 6 +++--- release-config/windows-installer-zork.iss | 2 +- zork++/Cargo.lock | 2 +- zork++/Cargo.toml | 2 +- zork++/src/lib/cli/input/mod.rs | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e9a372b9..a51433de 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -28,7 +28,7 @@ in the configuration ### Update -- Removed the restriction that doesn't allows the user to be able to link against `libc++` +- Removed the restriction that doesn't allow the user to be able to link against `libc++` in Windows ### Upgrade @@ -36,7 +36,7 @@ in Windows the project, avoiding make a .canonicalize() call for every one, raising the performance of the project model build process -## [0.8.0] - 2023 - 03 - 12 +## [0.8.1] - 2023 - 03 - 12 ### Feature @@ -46,7 +46,7 @@ file, which is used by static analyzers and IDE's to offer code completion and l - The overall performance of the cache process has been reviewed. We get rid of a cache clone that was affecting the performance, and making a huge impact the memory needed for the cache process during runtime by a factor of two. -Now everything is smoothly handled by mutable and inmutable reference types. +Now everything is smoothly handled by mutable and immutable reference types. - A command line flag `-c` has been included to reset the cache when the user requires. ### Update diff --git a/release-config/windows-installer-zork.iss b/release-config/windows-installer-zork.iss index 1bf1283f..950b6885 100644 --- a/release-config/windows-installer-zork.iss +++ b/release-config/windows-installer-zork.iss @@ -2,7 +2,7 @@ ; SEE THE DOCUMENTATION FOR DETAILS ON CREATING INNO SETUP SCRIPT FILES! #define MyAppName "Zork++" -#define MyAppVersion "0.8.0" +#define MyAppVersion "0.8.1" #define MyAppPublisher "Zero Day Code" #define MyAppURL "https://github.com/zerodaycode/Zork" #define MyAppExeName "zork++.exe" diff --git a/zork++/Cargo.lock b/zork++/Cargo.lock index dd9202a8..7b655020 100644 --- a/zork++/Cargo.lock +++ b/zork++/Cargo.lock @@ -1171,7 +1171,7 @@ checksum = "447660ad36a13288b1db4d4248e857b510e8c3a225c822ba4fb748c0aafecffd" [[package]] name = "zork" -version = "0.8.0" +version = "0.8.1" dependencies = [ "chrono", "clap 4.1.4", diff --git a/zork++/Cargo.toml b/zork++/Cargo.toml index d5d54f90..1d4fccab 100644 --- a/zork++/Cargo.toml +++ b/zork++/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "zork" -version = "0.8.0" +version = "0.8.1" authors = ["Zero Day Code"] edition = "2021" description = "A modern C++ project manager and build system for modern C++" diff --git a/zork++/src/lib/cli/input/mod.rs b/zork++/src/lib/cli/input/mod.rs index ee30e693..5f8a2d96 100644 --- a/zork++/src/lib/cli/input/mod.rs +++ b/zork++/src/lib/cli/input/mod.rs @@ -24,7 +24,7 @@ use clap::{Parser, Subcommand, ValueEnum}; #[derive(Parser, Debug, Default)] #[command(name = "Zork++")] #[command(author = "Zero Day Code")] -#[command(version = "0.8.0")] +#[command(version = "0.8.1")] #[command( about = "Zork++ is a build system for modern C++ projects", long_about = "Zork++ is a project of Zero Day Code. Find us: https://github.com/zerodaycode/Zork" From 8bde361f180c026b7daf09aaa1af69a5b1e1e21e Mon Sep 17 00:00:00 2001 From: Alex Vergara Date: Thu, 16 Mar 2023 10:46:10 +0100 Subject: [PATCH 39/60] Cleaning death code --- zork++/src/lib/cache/mod.rs | 2 +- zork++/src/lib/utils/fs.rs | 41 ------------------- zork++/src/lib/utils/reader.rs | 8 ++-- .../template/resources/zork_example.toml | 1 - .../resources/zork_example_basic.toml | 1 - 5 files changed, 6 insertions(+), 47 deletions(-) diff --git a/zork++/src/lib/cache/mod.rs b/zork++/src/lib/cache/mod.rs index bbff652b..749bf3ab 100644 --- a/zork++/src/lib/cache/mod.rs +++ b/zork++/src/lib/cache/mod.rs @@ -73,7 +73,7 @@ pub fn save( cache.run_final_tasks(program_data, commands, test_mode)?; cache.last_program_execution = Utc::now(); - utils::fs::serialize_cache(cache_path, cache) + utils::fs::serialize_object_to_file(cache_path, cache) .with_context(move || "Error saving data to the Zork++ cache") } diff --git a/zork++/src/lib/utils/fs.rs b/zork++/src/lib/utils/fs.rs index 99a85e1a..f8935c74 100644 --- a/zork++/src/lib/utils/fs.rs +++ b/zork++/src/lib/utils/fs.rs @@ -46,29 +46,6 @@ pub fn get_project_root_absolute_path(project_root: &Path) -> Result { Ok(canonical) } -/// Gets the absolute route for an element in the system given a path P, -/// without the extension if P belongs to a file -pub fn get_absolute_path>(p: P) -> Result { - let mut canonical = p - .as_ref() - .canonicalize() - .with_context(|| format!("Error getting the canonical path for: {:?}", p.as_ref()))?; - if cfg!(target_os = "windows") { - canonical = canonical - .to_str() - .map(|unc| &unc[4..]) - .unwrap_or_default() - .into() - } - let file_stem = canonical - .file_stem() - .with_context(|| format!("Unable to get the file stem for {:?}", p.as_ref()))?; - Ok(canonical - .parent() - .unwrap_or_else(|| panic!("Unexpected error getting the parent of {:?}", p.as_ref())) - .join(file_stem)) -} - /// Returns a tuple of elements containing the directory of a file, its file stem and its extension pub fn get_file_details>(p: P) -> Result<(PathBuf, String, String)> { let file_stem = p @@ -89,16 +66,6 @@ pub fn get_file_details>(p: P) -> Result<(PathBuf, String, String )) } -/// Returns the declared extension for a file, if exists -#[inline(always)] -pub fn get_file_extension>(p: P) -> String { - p.as_ref() - .extension() - .and_then(|ext| ext.to_str()) - .unwrap_or_default() - .to_string() -} - pub fn serialize_object_to_file(path: &Path, data: &T) -> Result<()> where T: Serialize, @@ -110,14 +77,6 @@ where .with_context(|| "Error serializing data to the cache") } -pub fn serialize_cache(path: &Path, data: &ZorkCache) -> Result<()> { - serde_json::to_writer_pretty( - File::create(path).with_context(|| "Error creating the cache file")?, - data, - ) - .with_context(|| "Error serializing data to the cache") -} - pub fn load_and_deserialize(path: &P) -> Result where T: for<'a> Deserialize<'a> + Default, diff --git a/zork++/src/lib/utils/reader.rs b/zork++/src/lib/utils/reader.rs index a9fcdecf..d1f751e1 100644 --- a/zork++/src/lib/utils/reader.rs +++ b/zork++/src/lib/utils/reader.rs @@ -372,6 +372,8 @@ mod test { let config: ZorkConfigFile = toml::from_str(CONFIG_FILE_MOCK)?; let model = build_model(&config, Path::new(".")); + let abs_path_for_mock = fs::get_project_root_absolute_path(Path::new("."))?; + let expected = ZorkModel { project: ProjectModel { name: "Zork++", @@ -386,7 +388,7 @@ mod test { extra_args: vec![], }, build: BuildModel { - output_dir: PathBuf::from("./out").canonicalize()?, + output_dir: (&abs_path_for_mock).join("out"), }, executable: ExecutableModel { executable_name: "Zork++", @@ -417,7 +419,7 @@ mod test { let config: ZorkConfigFile = toml::from_str(utils::constants::CONFIG_FILE_MOCK)?; let model = build_model(&config, Path::new(".")); - let abs_path_for_mock = fs::get_absolute_path(Path::new("."))?; + let abs_path_for_mock = fs::get_project_root_absolute_path(Path::new("."))?; let expected = ZorkModel { project: ProjectModel { @@ -433,7 +435,7 @@ mod test { extra_args: vec![Argument::from("-Wall")], }, build: BuildModel { - output_dir: PathBuf::from(".").canonicalize()?, + output_dir: abs_path_for_mock.clone(), }, executable: ExecutableModel { executable_name: "zork", diff --git a/zork++/src/lib/utils/template/resources/zork_example.toml b/zork++/src/lib/utils/template/resources/zork_example.toml index 1d3f0fad..b1812fab 100644 --- a/zork++/src/lib/utils/template/resources/zork_example.toml +++ b/zork++/src/lib/utils/template/resources/zork_example.toml @@ -7,7 +7,6 @@ compilation_db = true [compiler] cpp_compiler = "" cpp_standard = "20" -std_lib = "libc++" # This concrete property will only be applied to Clang [build] output_dir = "/out" diff --git a/zork++/src/lib/utils/template/resources/zork_example_basic.toml b/zork++/src/lib/utils/template/resources/zork_example_basic.toml index 7822c200..ce600b63 100644 --- a/zork++/src/lib/utils/template/resources/zork_example_basic.toml +++ b/zork++/src/lib/utils/template/resources/zork_example_basic.toml @@ -7,7 +7,6 @@ compilation_db = true [compiler] cpp_compiler = "" cpp_standard = "20" -std_lib = "libc++" # This concrete property will only be applied to Clang [build] output_dir = "/out" From a7e3e0bc021e5d058265f0cf1ab5b91e2d6d2677 Mon Sep 17 00:00:00 2001 From: Alex Vergara Date: Thu, 16 Mar 2023 14:25:42 +0100 Subject: [PATCH 40/60] Version 0.8.2 - Vec now it's replaced by Argument, a strong type def for it. The API is designed to make the process of build the commands nicer having a wrapper over a std collection --- CHANGELOG.md | 22 ++- release-config/windows-installer-zork.iss | 2 +- zork++/Cargo.lock | 2 +- zork++/Cargo.toml | 2 +- zork++/src/lib/cli/input/mod.rs | 2 +- zork++/src/lib/cli/output/arguments.rs | 71 +++++++- zork++/src/lib/cli/output/commands.rs | 22 +-- zork++/src/lib/compiler/mod.rs | 208 +++++++++++----------- zork++/src/lib/project_model/sourceset.rs | 16 +- zork++/src/lib/utils/fs.rs | 31 ++-- zork++/src/lib/utils/reader.rs | 9 +- 11 files changed, 229 insertions(+), 158 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a51433de..2c74ff66 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,14 +11,22 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - new model, for the compilation database? or feature key in the project? maybe we want to enable the cache per compiler? - make the project model full owned, and cache it? -- full path for the Clang's Windows modulemap -- strong type for `Vec`, that is initialized with a capacity that is equals -to the specific nº of args generated by process and by compiler, plus the extra arguments -from the compiler, and from every category -- Avoid to renew the already generated files in the `intrinsics` directory, and probably -generate a second `std.h` to bind to the Zork++ autogenerated `modulemap` when `libc++` +- generate a second `std.h` to bind to the Zork++ autogenerated `modulemap` when `libc++` is selected as the target standard library +## [0.8.2] - 2023 - 03 - 16 + +### Update + +- Files and directories needed by the program aren't regenerated for every iteration +if they are already present in the filesystem +- Full path for the Clang's Windows modulemap + +### Internal + +- `Arguments` is an strong type for `Vec`, providing it's own + convenient API + ## [0.8.1] - 2023 - 03 - 15 ### Feature @@ -36,7 +44,7 @@ in Windows the project, avoiding make a .canonicalize() call for every one, raising the performance of the project model build process -## [0.8.1] - 2023 - 03 - 12 +## [0.8.0] - 2023 - 03 - 12 ### Feature diff --git a/release-config/windows-installer-zork.iss b/release-config/windows-installer-zork.iss index 950b6885..2f7692c2 100644 --- a/release-config/windows-installer-zork.iss +++ b/release-config/windows-installer-zork.iss @@ -2,7 +2,7 @@ ; SEE THE DOCUMENTATION FOR DETAILS ON CREATING INNO SETUP SCRIPT FILES! #define MyAppName "Zork++" -#define MyAppVersion "0.8.1" +#define MyAppVersion "0.8.2" #define MyAppPublisher "Zero Day Code" #define MyAppURL "https://github.com/zerodaycode/Zork" #define MyAppExeName "zork++.exe" diff --git a/zork++/Cargo.lock b/zork++/Cargo.lock index 7b655020..9ac4691c 100644 --- a/zork++/Cargo.lock +++ b/zork++/Cargo.lock @@ -1171,7 +1171,7 @@ checksum = "447660ad36a13288b1db4d4248e857b510e8c3a225c822ba4fb748c0aafecffd" [[package]] name = "zork" -version = "0.8.1" +version = "0.8.2" dependencies = [ "chrono", "clap 4.1.4", diff --git a/zork++/Cargo.toml b/zork++/Cargo.toml index 1d4fccab..d06aaf1c 100644 --- a/zork++/Cargo.toml +++ b/zork++/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "zork" -version = "0.8.1" +version = "0.8.2" authors = ["Zero Day Code"] edition = "2021" description = "A modern C++ project manager and build system for modern C++" diff --git a/zork++/src/lib/cli/input/mod.rs b/zork++/src/lib/cli/input/mod.rs index 5f8a2d96..5a56901d 100644 --- a/zork++/src/lib/cli/input/mod.rs +++ b/zork++/src/lib/cli/input/mod.rs @@ -24,7 +24,7 @@ use clap::{Parser, Subcommand, ValueEnum}; #[derive(Parser, Debug, Default)] #[command(name = "Zork++")] #[command(author = "Zero Day Code")] -#[command(version = "0.8.1")] +#[command(version = "0.8.2")] #[command( about = "Zork++ is a build system for modern C++ projects", long_about = "Zork++ is a project of Zero Day Code. Find us: https://github.com/zerodaycode/Zork" diff --git a/zork++/src/lib/cli/output/arguments.rs b/zork++/src/lib/cli/output/arguments.rs index 76a9e3c3..deeb0261 100644 --- a/zork++/src/lib/cli/output/arguments.rs +++ b/zork++/src/lib/cli/output/arguments.rs @@ -3,6 +3,7 @@ use std::path::Path; use std::{borrow::Borrow, ffi::OsStr, path::PathBuf}; +use std::ops::Deref; use serde::{Deserialize, Serialize}; @@ -26,6 +27,14 @@ impl<'a> From for Argument<'a> { } } +impl<'a> Deref for Argument<'a> { + type Target = &'a str; + + fn deref(&self) -> &Self::Target { + &self.value + } +} + impl<'a> From<&'a Path> for Argument<'a> { fn from(value: &'a Path) -> Self { Self::from(format!("{}", value.display())) @@ -62,6 +71,66 @@ impl<'a> core::fmt::Display for Argument<'a> { } } +/// Strong type for represent a linear collection of [`Argument`] +#[derive(Debug, Default, Clone)] +pub struct Arguments<'a>(Vec>); +impl<'a> Arguments<'a> { + /// Wraps an existing [`std::vec::Vec`] of [`Argument`] + pub fn from_vec(vec: Vec>) -> Self { Self(vec) } + + /// Returns a new collection of [`Argument`] with the specified capacity + pub fn with_capacity(cap: usize) -> Self { + Self(Vec::with_capacity(cap)) + } + + /// Creates and stores a new [`Argument`] to the end of this collection + pub fn create_and_push(&mut self, val: T) + where T: Into> + { + self.0.push(val.into()) + } + + /// Appends a new [`Argument`] to the end of this collection + pub fn push(&mut self, arg: Argument<'a>) { + self.0.push(arg) + } + + /// Given an optional, adds the wrapper inner value if there's some element, + /// otherwise leaves + pub fn push_opt(&mut self, arg: Option>) { + if let Some(val) = arg { + self.0.push(val) + } + } + + /// Extends the underlying collection from a Iterator of [`Argument`] + pub fn extend(&mut self, iter: impl IntoIterator>) { + self.0.extend(iter); + } + + /// Extends the underlying collection given a slice of [`Argument`] + pub fn extend_from_slice(&mut self, slice: &'a [Argument<'a>]) { + self.0.extend_from_slice(slice); + } +} + +impl<'a> Deref for Arguments<'a> { + type Target = [Argument<'a>]; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl<'a> IntoIterator for Arguments<'a> { + type Item = Argument<'a>; + type IntoIter = std::vec::IntoIter; + + fn into_iter(self) -> Self::IntoIter { + self.0.into_iter() + } +} + pub mod clang_args { use std::path::Path; @@ -104,7 +173,7 @@ pub mod clang_args { dependencies: &[&str], compiler: CppCompiler, out_dir: &Path, - arguments: &mut Vec, + arguments: &mut Arguments<'_>, ) { dependencies.iter().for_each(|ifc_dep| { arguments.push(Argument::from(format!( diff --git a/zork++/src/lib/cli/output/commands.rs b/zork++/src/lib/cli/output/commands.rs index 8381cdea..e2939833 100644 --- a/zork++/src/lib/cli/output/commands.rs +++ b/zork++/src/lib/cli/output/commands.rs @@ -18,6 +18,7 @@ use color_eyre::{ Report, Result, }; use serde::{Deserialize, Serialize}; +use crate::cli::output::arguments::Arguments; use super::arguments::Argument; @@ -150,11 +151,11 @@ fn execute_command( /// The pieces and details for the generated command line for /// for some translation unit -#[derive(Debug, Clone)] +#[derive(Debug)] pub struct SourceCommandLine<'a> { pub directory: PathBuf, pub file: String, - pub args: Vec>, + pub args: Arguments<'a>, pub processed: bool, pub execution_result: CommandExecutionResult, } @@ -162,7 +163,7 @@ pub struct SourceCommandLine<'a> { impl<'a> SourceCommandLine<'a> { pub fn from_translation_unit( tu: impl TranslationUnit, - args: Vec>, + args: Arguments<'a>, processed: bool, execution_result: CommandExecutionResult, ) -> Self { @@ -180,15 +181,6 @@ impl<'a> SourceCommandLine<'a> { } } -impl<'a> IntoIterator for SourceCommandLine<'a> { - type Item = Argument<'a>; - type IntoIter = std::vec::IntoIter>; - - fn into_iter(self) -> Self::IntoIter { - self.args.into_iter() - } -} - #[derive(Debug)] pub struct ExecutableCommandLine<'a> { pub main: &'a Path, @@ -212,12 +204,12 @@ impl<'a> Default for ExecutableCommandLine<'a> { #[derive(Debug)] pub struct Commands<'a> { pub compiler: CppCompiler, - pub system_modules: HashMap>>, + pub system_modules: HashMap>, pub interfaces: Vec>, pub implementations: Vec>, pub sources: Vec>, pub main: ExecutableCommandLine<'a>, - pub generated_files_paths: Vec>, + pub generated_files_paths: Arguments<'a>, } impl<'a> Commands<'a> { @@ -229,7 +221,7 @@ impl<'a> Commands<'a> { implementations: Vec::with_capacity(0), sources: Vec::with_capacity(0), main: ExecutableCommandLine::default(), - generated_files_paths: Vec::with_capacity(0), + generated_files_paths: Arguments::default(), } } } diff --git a/zork++/src/lib/compiler/mod.rs b/zork++/src/lib/compiler/mod.rs index 1907eef1..311f546b 100644 --- a/zork++/src/lib/compiler/mod.rs +++ b/zork++/src/lib/compiler/mod.rs @@ -18,6 +18,7 @@ use crate::{ ZorkModel, }, }; +use crate::cli::output::arguments::Arguments; /// The entry point of the compilation process /// @@ -79,7 +80,7 @@ fn build_sources<'a>( sources::generate_sources_arguments(model, commands, &model.tests, src); } else { let command_line = SourceCommandLine::from_translation_unit( - src, Vec::with_capacity(0), true, CommandExecutionResult::Cached + src, Arguments::default(), true, CommandExecutionResult::Cached ); log::trace!("Source file: {:?} was not modified since the last iteration. No need to rebuilt it again.", &src.file()); @@ -124,7 +125,7 @@ fn prebuild_module_interfaces<'a>( sources::generate_module_interfaces_args(model, module_interface, commands); } else { let command_line = SourceCommandLine::from_translation_unit( - module_interface, Vec::with_capacity(0), true, CommandExecutionResult::Cached + module_interface, Arguments::default(), true, CommandExecutionResult::Cached ); log::trace!("Source file:{:?} was not modified since the last iteration. No need to rebuilt it again.", &module_interface.file()); @@ -149,7 +150,7 @@ fn compile_module_implementations<'a>( sources::generate_module_implementation_args(model, module_impl, commands); } else { let command_line = SourceCommandLine::from_translation_unit( - module_impl, Vec::with_capacity(0), true, CommandExecutionResult::Cached + module_impl, Arguments::default(), true, CommandExecutionResult::Cached ); log::trace!("Source file:{:?} was not modified since the last iteration. No need to rebuilt it again.", &module_impl.file()); @@ -179,6 +180,8 @@ mod sources { utils::constants, }; use color_eyre::Result; + use crate::bounds::ExtraArgs; + use crate::cli::output::arguments::Arguments; /// Generates the command line arguments for the desired target pub fn generate_main_command_line_args<'a>( @@ -192,76 +195,74 @@ mod sources { let out_dir = model.build.output_dir.as_ref(); let executable_name = target.name(); - let mut arguments = Vec::new(); + 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 { CppCompiler::CLANG => { - arguments.extend(model.compiler.stdlib_arg()); - arguments.extend_from_slice(target.extra_args()); - arguments.push(Argument::from("-fimplicit-modules")); + arguments.push_opt(model.compiler.stdlib_arg()); + arguments.create_and_push("-fimplicit-modules"); arguments.push(clang_args::implicit_module_maps(out_dir)); - arguments.push(Argument::from(format!( + arguments.create_and_push(format!( "-fprebuilt-module-path={}", out_dir .join(compiler.as_ref()) .join("modules") .join("interfaces") .display() - ))); + )); - arguments.push(Argument::from("-o")); - arguments.push(Argument::from(format!( + arguments.create_and_push("-o"); + arguments.create_and_push(format!( "{}", out_dir .join(compiler.as_ref()) .join(executable_name) .with_extension(constants::BINARY_EXTENSION) .display() - ))); + )); } CppCompiler::MSVC => { - arguments.push(Argument::from("/EHsc")); - arguments.push(Argument::from("/nologo")); + arguments.create_and_push("/EHsc"); + arguments.create_and_push("/nologo"); // If /std:c++20 this, else should be the direct options // available on C++23 to use directly import std by pre-compiling the standard library - arguments.push(Argument::from("/experimental:module")); - arguments.push(Argument::from("/stdIfcDir \"$(VC_IFCPath)\"")); - - // helpers::add_extra_args_if_present(&config.executable, &mut arguments); - arguments.extend_from_slice(target.extra_args()); - arguments.push(Argument::from("/ifcSearchDir")); - arguments.push(Argument::from( + arguments.create_and_push("/experimental:module"); + arguments.create_and_push("/stdIfcDir \"$(VC_IFCPath)\""); + arguments.create_and_push("/ifcSearchDir"); + arguments.create_and_push( out_dir .join(compiler.as_ref()) .join("modules") .join("interfaces"), - )); - arguments.push(Argument::from(format!( + ); + arguments.create_and_push(format!( "/Fo{}\\", out_dir.join(compiler.as_ref()).display() - ))); - arguments.push(Argument::from(format!( + )); + arguments.create_and_push(format!( "/Fe{}", out_dir .join(compiler.as_ref()) .join(executable_name) .with_extension(constants::BINARY_EXTENSION) .display() - ))); + )); } CppCompiler::GCC => { - arguments.push(Argument::from("-fmodules-ts")); - arguments.push(Argument::from("-o")); - arguments.push(Argument::from(format!( + arguments.create_and_push("-fmodules-ts"); + arguments.create_and_push("-o"); + arguments.create_and_push(format!( "{}", out_dir .join(compiler.as_ref()) .join(executable_name) .with_extension(constants::BINARY_EXTENSION) .display() - ))); + )); } }; arguments.extend(commands.generated_files_paths.clone().into_iter()); @@ -287,39 +288,38 @@ mod sources { let compiler = model.compiler.cpp_compiler; let out_dir = model.build.output_dir.as_ref(); - let mut arguments = Vec::new(); + let mut arguments = Arguments::default(); arguments.push(model.compiler.language_level_arg()); - arguments.push(Argument::from("-c")); + arguments.create_and_push("-c"); arguments.extend_from_slice(target.extra_args()); match compiler { CppCompiler::CLANG => { - arguments.extend(model.compiler.stdlib_arg()); - arguments.push(Argument::from("-fimplicit-modules")); + arguments.push_opt(model.compiler.stdlib_arg()); + arguments.create_and_push("-fimplicit-modules"); arguments.push(clang_args::implicit_module_maps(out_dir)); arguments.push(clang_args::add_prebuilt_module_path(compiler, out_dir)); - - arguments.push(Argument::from("-o")); + arguments.create_and_push("-o"); } CppCompiler::MSVC => { - arguments.push(Argument::from("/EHsc")); - arguments.push(Argument::from("/nologo")); + arguments.create_and_push("/EHsc"); + arguments.create_and_push(Argument::from("/nologo")); // If /std:c++20 this, else should be the direct options // available on C++23 to use directly import std by pre-compiling the standard library - arguments.push(Argument::from("/experimental:module")); - arguments.push(Argument::from("/stdIfcDir \"$(VC_IFCPath)\"")); + arguments.create_and_push("/experimental:module"); + arguments.create_and_push("/stdIfcDir \"$(VC_IFCPath)\""); - arguments.push(Argument::from("/ifcSearchDir")); - arguments.push(Argument::from( + arguments.create_and_push("/ifcSearchDir"); + arguments.create_and_push( out_dir .join(compiler.as_ref()) .join("modules") .join("interfaces"), - )); + ); } CppCompiler::GCC => { - arguments.push(Argument::from("-fmodules-ts")); - arguments.push(Argument::from("-o")); + arguments.create_and_push("-fmodules-ts"); + arguments.create_and_push("-o"); } }; @@ -329,8 +329,8 @@ mod sources { } else { "" }; - arguments.push(Argument::from(format!("{fo}{obj_file}"))); - arguments.push(Argument::from(source.file())); + arguments.create_and_push(format!("{fo}{obj_file}")); + arguments.create_and_push(source.file()); let command_line = SourceCommandLine::from_translation_unit( source, @@ -353,25 +353,25 @@ mod sources { let compiler = model.compiler.cpp_compiler; let out_dir = model.build.output_dir.as_ref(); - let mut arguments = Vec::with_capacity(8 + model.compiler.extra_args.len()); - // Clang 8, MSVC 15, GCC 7 - TODO Make a strong type for Vec that is initialized - // with the correct capacity given a compiler + let mut arguments = Arguments::with_capacity( + 8 + // TODO replace for a constant value matched by compiler + model.compiler.extra_args.len() + // TODO module extra args + ); arguments.push(model.compiler.language_level_arg()); match compiler { CppCompiler::CLANG => { - arguments.extend(model.compiler.stdlib_arg()); - arguments.push(Argument::from("-fimplicit-modules")); - arguments.push(Argument::from("-x")); - arguments.push(Argument::from("c++-module")); - arguments.push(Argument::from("--precompile")); - + arguments.push_opt(model.compiler.stdlib_arg()); + arguments.create_and_push("-fimplicit-modules"); + arguments.create_and_push("-x"); + arguments.create_and_push("c++-module"); + arguments.create_and_push("--precompile"); arguments.push(clang_args::implicit_module_maps(out_dir)); - - arguments.push(Argument::from(format!( + arguments.create_and_push(format!( "-fprebuilt-module-path={}/clang/modules/interfaces", out_dir.display() - ))); + )); clang_args::add_direct_module_interfaces_dependencies( &interface.dependencies, compiler, @@ -380,61 +380,61 @@ mod sources { ); // The resultant BMI as a .pcm file - arguments.push(Argument::from("-o")); + arguments.create_and_push("-o"); // The output file let miu_file_path = Argument::from(helpers::generate_prebuild_miu(compiler, out_dir, interface)); commands.generated_files_paths.push(miu_file_path.clone()); arguments.push(miu_file_path); // The input file - arguments.push(Argument::from(interface.file())); + arguments.create_and_push(interface.file()); } CppCompiler::MSVC => { - arguments.push(Argument::from("/EHsc")); - arguments.push(Argument::from("/nologo")); - arguments.push(Argument::from("/experimental:module")); - arguments.push(Argument::from("/stdIfcDir \"$(VC_IFCPath)\"")); - arguments.push(Argument::from("/c")); + arguments.create_and_push("/EHsc"); + arguments.create_and_push("/nologo"); + arguments.create_and_push("/experimental:module"); + arguments.create_and_push("/stdIfcDir \"$(VC_IFCPath)\""); + arguments.create_and_push("/c"); let implicit_lookup_mius_path = out_dir .join(compiler.as_ref()) .join("modules") .join("interfaces") .display() - .to_string(); - arguments.push(Argument::from("/ifcSearchDir")); - arguments.push(implicit_lookup_mius_path.clone().into()); - arguments.push(Argument::from("/ifcOutput")); - arguments.push(implicit_lookup_mius_path.into()); + .to_string(); // TODO Can we avoid this conversions? + arguments.create_and_push("/ifcSearchDir"); + arguments.create_and_push(implicit_lookup_mius_path.clone()); + arguments.create_and_push("/ifcOutput"); + arguments.create_and_push(implicit_lookup_mius_path); // The output .obj file let obj_file = Argument::from(helpers::generate_prebuild_miu(compiler, out_dir, interface)); commands.generated_files_paths.push(obj_file.clone()); - arguments.push(Argument::from(format!("/Fo{obj_file}"))); + arguments.create_and_push(format!("/Fo{obj_file}")); if let Some(partition) = &interface.partition { if partition.is_internal_partition { - arguments.push(Argument::from("/internalPartition")); + arguments.create_and_push("/internalPartition"); } else { - arguments.push(Argument::from("/interface")); + arguments.create_and_push("/interface"); } } else { - arguments.push(Argument::from("/interface")); + arguments.create_and_push("/interface"); } - arguments.push(Argument::from("/TP")); + arguments.create_and_push("/TP"); // The input file - arguments.push(Argument::from(interface.file())) + arguments.create_and_push(interface.file()) } CppCompiler::GCC => { - arguments.push(Argument::from("-fmodules-ts")); - arguments.push(Argument::from("-x")); - arguments.push(Argument::from("c++")); - arguments.push(Argument::from("-c")); + arguments.create_and_push("-fmodules-ts"); + arguments.create_and_push("-x"); + arguments.create_and_push("c++"); + arguments.create_and_push("-c"); // The input file - arguments.push(Argument::from(interface.file())); + arguments.create_and_push(interface.file()); // The output file - arguments.push(Argument::from("-o")); + arguments.create_and_push("-o"); let miu_file_path = Argument::from(helpers::generate_prebuild_miu(compiler, out_dir, interface)); commands.generated_files_paths.push(miu_file_path.clone()); @@ -460,18 +460,18 @@ mod sources { let compiler = model.compiler.cpp_compiler; let out_dir = model.build.output_dir.as_ref(); - let mut arguments = Vec::with_capacity(12); + let mut arguments = Arguments::with_capacity(12); // TODO as ifcs arguments.push(model.compiler.language_level_arg()); match compiler { CppCompiler::CLANG => { - arguments.extend(model.compiler.stdlib_arg()); - arguments.push(Argument::from("-fimplicit-modules")); - arguments.push(Argument::from("-c")); + arguments.push_opt(model.compiler.stdlib_arg()); + arguments.create_and_push("-fimplicit-modules"); + arguments.create_and_push("-c"); arguments.push(clang_args::implicit_module_maps(out_dir)); // The resultant object file - arguments.push(Argument::from("-o")); + arguments.create_and_push("-o"); let obj_file_path = Argument::from(helpers::generate_impl_obj_file( compiler, out_dir, @@ -488,23 +488,23 @@ mod sources { ); // The input file - arguments.push(Argument::from(implementation.file())) + arguments.create_and_push(implementation.file()) } CppCompiler::MSVC => { - arguments.push(Argument::from("/EHsc")); - arguments.push(Argument::from("/nologo")); - arguments.push(Argument::from("-c")); - arguments.push(Argument::from("/experimental:module")); - arguments.push(Argument::from("/stdIfcDir \"$(VC_IFCPath)\"")); - arguments.push(Argument::from("/ifcSearchDir")); - arguments.push(Argument::from( + arguments.create_and_push("/EHsc"); + arguments.create_and_push("/nologo"); + arguments.create_and_push("-c"); + arguments.create_and_push("/experimental:module"); + arguments.create_and_push("/stdIfcDir \"$(VC_IFCPath)\""); + arguments.create_and_push("/ifcSearchDir"); + arguments.create_and_push( out_dir .join(compiler.as_ref()) .join("modules") .join("interfaces"), - )); + ); // The input file - arguments.push(Argument::from(implementation.file())); + arguments.create_and_push(implementation.file()); // The output .obj file let obj_file_path = out_dir .join(compiler.as_ref()) @@ -516,15 +516,15 @@ mod sources { commands .generated_files_paths .push(Argument::from(obj_file_path.clone())); - arguments.push(Argument::from(format!("/Fo{}", obj_file_path.display()))); + arguments.create_and_push(format!("/Fo{}", obj_file_path.display())); } CppCompiler::GCC => { - arguments.push(Argument::from("-fmodules-ts")); - arguments.push(Argument::from("-c")); + arguments.create_and_push("-fmodules-ts"); + arguments.create_and_push("-c"); // The input file - arguments.push(Argument::from(implementation.file())); + arguments.create_and_push(implementation.file()); // The output file - arguments.push(Argument::from("-o")); + arguments.create_and_push("-o"); let obj_file_path = Argument::from(helpers::generate_impl_obj_file( compiler, out_dir, @@ -679,7 +679,7 @@ mod helpers { for collection_args in sys_modules { commands .system_modules - .insert(collection_args[4].value.to_string(), collection_args); + .insert(collection_args[4].value.to_string(), Arguments::from_vec(collection_args)); } } diff --git a/zork++/src/lib/project_model/sourceset.rs b/zork++/src/lib/project_model/sourceset.rs index 1b12fa96..cf36fd85 100644 --- a/zork++/src/lib/project_model/sourceset.rs +++ b/zork++/src/lib/project_model/sourceset.rs @@ -1,5 +1,5 @@ use core::fmt; -use std::path::{Path, PathBuf}; +use std::path::PathBuf; use crate::bounds::TranslationUnit; use color_eyre::{eyre::Context, Result}; @@ -7,9 +7,9 @@ use color_eyre::{eyre::Context, Result}; use crate::cli::output::arguments::Argument; #[derive(Debug, PartialEq, Eq)] -pub enum Source<'a> { - File(&'a Path), - Glob(GlobPattern<'a>), +pub enum Source { + File(PathBuf), + Glob(GlobPattern), } #[derive(Debug, PartialEq, Eq)] @@ -71,7 +71,7 @@ impl fmt::Display for SourceFile { } } -impl<'a> Source<'a> { +impl Source { #[inline(always)] pub fn paths(&self) -> Result> { match self { @@ -82,12 +82,12 @@ impl<'a> Source<'a> { } #[derive(Debug, PartialEq, Eq)] -pub struct GlobPattern<'a>(pub &'a str); +pub struct GlobPattern(pub PathBuf); -impl<'a> GlobPattern<'a> { +impl GlobPattern { #[inline(always)] fn resolve(&self) -> Result> { - glob::glob(self.0)? + glob::glob(self.0.to_str().unwrap_or_default())? .map(|path| path.with_context(|| "")) .collect() } diff --git a/zork++/src/lib/utils/fs.rs b/zork++/src/lib/utils/fs.rs index f8935c74..fa82a10d 100644 --- a/zork++/src/lib/utils/fs.rs +++ b/zork++/src/lib/utils/fs.rs @@ -1,33 +1,34 @@ use std::{ fs::{DirBuilder, File}, io::{BufReader, Write}, - path::Path, + path::{Path, PathBuf}, }; - -use std::path::PathBuf; - use color_eyre::eyre::ContextCompat; use color_eyre::{eyre::Context, Result}; - -use crate::cache::ZorkCache; use serde::{Deserialize, Serialize}; use super::constants; +/// Creates a new file in the filesystem if the given does not exists yet at the specified location pub fn create_file<'a>(path: &Path, filename: &'a str, buff_write: &'a [u8]) -> Result<()> { let file_path = path.join(filename); - File::create(&file_path) - .with_context(|| format!("Could not create file {file_path:?}"))? - .write_all(buff_write) - .with_context(|| format!("Could not write to file {file_path:?}")) + if !file_path.exists() { + File::create(&file_path) + .with_context(|| format!("Could not create file {file_path:?}"))? + .write_all(buff_write) + .with_context(|| format!("Could not write to file {file_path:?}")) + } else { Ok(()) } } -pub fn create_directory(path_create: &Path) -> Result<()> { - DirBuilder::new() - .recursive(true) - .create(path_create) - .with_context(|| format!("Could not create directory {path_create:?}")) +/// Recursively creates a new directory pointed at the value of target if not exists yet +pub fn create_directory(target: &Path) -> Result<()> { + if !target.exists() { + DirBuilder::new() + .recursive(true) + .create(target) + .with_context(|| format!("Could not create directory {target:?}")) + } else { Ok(()) } } #[inline(always)] diff --git a/zork++/src/lib/utils/reader.rs b/zork++/src/lib/utils/reader.rs index d1f751e1..9502b930 100644 --- a/zork++/src/lib/utils/reader.rs +++ b/zork++/src/lib/utils/reader.rs @@ -322,10 +322,11 @@ fn get_sourceset_for(srcs: Vec<&str>, project_root: &Path) -> SourceSet { let sources = srcs .iter() .map(|src| { + let target_src = project_root.join(src); if src.contains('*') { - Source::Glob(GlobPattern(src)) + Source::Glob(GlobPattern(target_src)) } else { - Source::File(Path::new(src)) + Source::File(target_src) } }) .flat_map(|source| { @@ -334,7 +335,7 @@ fn get_sourceset_for(srcs: Vec<&str>, project_root: &Path) -> SourceSet { .expect("Error getting the declared paths for the source files") }) .map(|pb| { - let file_details = utils::fs::get_file_details(project_root.join(&pb)) + let file_details = utils::fs::get_file_details(&pb) .unwrap_or_else(|_| panic!("An unexpected error happened getting the file details for {pb:?}")); SourceFile { path: file_details.0, @@ -388,7 +389,7 @@ mod test { extra_args: vec![], }, build: BuildModel { - output_dir: (&abs_path_for_mock).join("out"), + output_dir: abs_path_for_mock.join("out"), }, executable: ExecutableModel { executable_name: "Zork++", From b0026d82f5591b2f2bd213374739a775f1598c93 Mon Sep 17 00:00:00 2001 From: Alex Vergara Date: Thu, 16 Mar 2023 15:10:18 +0100 Subject: [PATCH 41/60] Version 0.8.3 - The modules key of the configuration file got a new property for adding extra arguments to the command lines generated for build the `C++ modules`. Sources and module implementation translation units wasn't receiving the general extra compiler arguments. --- CHANGELOG.md | 18 +- README.md | 2 + release-config/windows-installer-zork.iss | 2 +- zork++/Cargo.lock | 2 +- zork++/Cargo.toml | 2 +- zork++/src/lib/cli/input/mod.rs | 2 +- zork++/src/lib/compiler/mod.rs | 236 +++++++++++----------- zork++/src/lib/config_file/modules.rs | 8 +- zork++/src/lib/project_model/modules.rs | 9 + zork++/src/lib/utils/constants.rs | 1 + zork++/src/lib/utils/reader.rs | 8 + 11 files changed, 162 insertions(+), 128 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2c74ff66..65048ca6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,14 +6,24 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [Unreleased, TODOs] -- Add extra arguments for the modules - nº of iterations of the program to automatically clear the cache (new model, cache) -- new model, for the compilation database? or feature key in the project? maybe we want to -enable the cache per compiler? +- new model, for the compilation database? or feature key in the project? - make the project model full owned, and cache it? - generate a second `std.h` to bind to the Zork++ autogenerated `modulemap` when `libc++` is selected as the target standard library +## [0.8.3] - 2023 - 03 - 16 + +### Feature + +- The modules key of the configuration file got a new property for adding extra +arguments to the command lines generated for build the `C++ modules` + +### Fix + +- Sources and module implementation translation units wasn't receiving the general +extra compiler arguments + ## [0.8.2] - 2023 - 03 - 16 ### Update @@ -128,7 +138,7 @@ the declared system modules required, just as we were doing with `GCC` ### Fix -- Correction on the log showed for the executable autorunner and for the tests runner +- Correction on the log showed for the executable runner and for the tests runner ## [0.3.0] - 2022 - 11 - 22 diff --git a/README.md b/README.md index f365b10d..f48d1a74 100644 --- a/README.md +++ b/README.md @@ -391,12 +391,14 @@ ExecutableAttribute { /// * `base_impls_dir` - Base directory. So you don't have to specify the full path of the implementation files /// * `implementations` - A list to define the module interface translation units for the project /// * `sys_modules` - An array field explicitly declare which system headers must be precompiled +/// * `extra_args` - Extra arguments that will be added to the generated command lines ModulesAttribute { base_ifcs_dir: Option, interfaces: Option>, base_impls_dir: Option, implementations: Option>, sys_modules: Option>, + extra_args: Option>, } /// The [tests] key diff --git a/release-config/windows-installer-zork.iss b/release-config/windows-installer-zork.iss index 2f7692c2..6e2dd082 100644 --- a/release-config/windows-installer-zork.iss +++ b/release-config/windows-installer-zork.iss @@ -2,7 +2,7 @@ ; SEE THE DOCUMENTATION FOR DETAILS ON CREATING INNO SETUP SCRIPT FILES! #define MyAppName "Zork++" -#define MyAppVersion "0.8.2" +#define MyAppVersion "0.8.3" #define MyAppPublisher "Zero Day Code" #define MyAppURL "https://github.com/zerodaycode/Zork" #define MyAppExeName "zork++.exe" diff --git a/zork++/Cargo.lock b/zork++/Cargo.lock index 9ac4691c..02c81963 100644 --- a/zork++/Cargo.lock +++ b/zork++/Cargo.lock @@ -1171,7 +1171,7 @@ checksum = "447660ad36a13288b1db4d4248e857b510e8c3a225c822ba4fb748c0aafecffd" [[package]] name = "zork" -version = "0.8.2" +version = "0.8.3" dependencies = [ "chrono", "clap 4.1.4", diff --git a/zork++/Cargo.toml b/zork++/Cargo.toml index d06aaf1c..c5d586b5 100644 --- a/zork++/Cargo.toml +++ b/zork++/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "zork" -version = "0.8.2" +version = "0.8.3" authors = ["Zero Day Code"] edition = "2021" description = "A modern C++ project manager and build system for modern C++" diff --git a/zork++/src/lib/cli/input/mod.rs b/zork++/src/lib/cli/input/mod.rs index 5a56901d..477f5365 100644 --- a/zork++/src/lib/cli/input/mod.rs +++ b/zork++/src/lib/cli/input/mod.rs @@ -24,7 +24,7 @@ use clap::{Parser, Subcommand, ValueEnum}; #[derive(Parser, Debug, Default)] #[command(name = "Zork++")] #[command(author = "Zero Day Code")] -#[command(version = "0.8.2")] +#[command(version = "0.8.3")] #[command( about = "Zork++ is a build system for modern C++ projects", long_about = "Zork++ is a project of Zero Day Code. Find us: https://github.com/zerodaycode/Zork" diff --git a/zork++/src/lib/compiler/mod.rs b/zork++/src/lib/compiler/mod.rs index 311f546b..4e3f1295 100644 --- a/zork++/src/lib/compiler/mod.rs +++ b/zork++/src/lib/compiler/mod.rs @@ -6,7 +6,7 @@ use color_eyre::Result; use std::path::Path; -use crate::bounds::TranslationUnit; +use crate::bounds::{ExecutableTarget, ExtraArgs, TranslationUnit}; use crate::cli::output::commands::{CommandExecutionResult, SourceCommandLine}; use crate::compiler::helpers::flag_source_file_without_changes; use crate::{ @@ -18,7 +18,8 @@ use crate::{ ZorkModel, }, }; -use crate::cli::output::arguments::Arguments; +use crate::cli::output::arguments::{Arguments, clang_args}; +use crate::utils::constants; /// The entry point of the compilation process /// @@ -33,7 +34,7 @@ pub fn build_project<'a>( let mut commands = Commands::new(&model.compiler.cpp_compiler); if model.compiler.cpp_compiler != CppCompiler::MSVC { - helpers::prebuild_sys_modules(model, &mut commands, cache) + helpers::build_sys_modules(model, &mut commands, cache) } // 1st - Build the modules @@ -57,9 +58,9 @@ fn build_executable<'a>( // TODO Check if the command line is the same as the previous? If there's no new sources? // And avoid re-executing? if tests { - sources::generate_main_command_line_args(model, commands, &model.tests) + generate_main_command_line_args(model, commands, &model.tests) } else { - sources::generate_main_command_line_args(model, commands, &model.executable) + generate_main_command_line_args(model, commands, &model.executable) } } @@ -104,17 +105,17 @@ fn build_modules<'a>( commands: &mut Commands<'a>, ) -> Result<()> { log::info!("Building the module interfaces and partitions..."); - prebuild_module_interfaces(model, cache, &model.modules.interfaces, commands); + build_module_interfaces(model, cache, &model.modules.interfaces, commands); log::info!("Building the module implementations..."); - compile_module_implementations(model, cache, &model.modules.implementations, commands); + build_module_implementations(model, cache, &model.modules.implementations, commands); Ok(()) } /// Parses the configuration in order to build the BMIs declared for the project, /// by precompiling the module interface units -fn prebuild_module_interfaces<'a>( +fn build_module_interfaces<'a>( model: &'a ZorkModel<'_>, cache: &ZorkCache, interfaces: &'a [ModuleInterfaceModel], @@ -130,7 +131,7 @@ fn prebuild_module_interfaces<'a>( log::trace!("Source file:{:?} was not modified since the last iteration. No need to rebuilt it again.", &module_interface.file()); commands.interfaces.push(command_line); - commands.generated_files_paths.push(Argument::from(helpers::generate_prebuild_miu( + commands.generated_files_paths.push(Argument::from(helpers::generate_prebuilt_miu( model.compiler.cpp_compiler, &model.build.output_dir, module_interface ))) } @@ -139,7 +140,7 @@ fn prebuild_module_interfaces<'a>( /// Parses the configuration in order to compile the module implementation /// translation units declared for the project -fn compile_module_implementations<'a>( +fn build_module_implementations<'a>( model: &'a ZorkModel, cache: &ZorkCache, impls: &'a [ModuleImplementationModel], @@ -162,6 +163,101 @@ fn compile_module_implementations<'a>( }); } +/// Generates the command line arguments for the desired target +pub fn generate_main_command_line_args<'a>( + model: &'a ZorkModel, + commands: &mut Commands<'a>, + target: &'a impl ExecutableTarget<'a>, +) -> Result<()> { + log::info!("Generating the main command line..."); + + let compiler = &model.compiler.cpp_compiler; + let out_dir = 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 { + CppCompiler::CLANG => { + arguments.push_opt(model.compiler.stdlib_arg()); + arguments.create_and_push("-fimplicit-modules"); + arguments.push(clang_args::implicit_module_maps(out_dir)); + + arguments.create_and_push(format!( + "-fprebuilt-module-path={}", + out_dir + .join(compiler.as_ref()) + .join("modules") + .join("interfaces") + .display() + )); + + arguments.create_and_push("-o"); + arguments.create_and_push(format!( + "{}", + out_dir + .join(compiler.as_ref()) + .join(executable_name) + .with_extension(constants::BINARY_EXTENSION) + .display() + )); + } + CppCompiler::MSVC => { + arguments.create_and_push("/EHsc"); + arguments.create_and_push("/nologo"); + // If /std:c++20 this, else should be the direct options + // available on C++23 to use directly import std by pre-compiling the standard library + arguments.create_and_push("/experimental:module"); + arguments.create_and_push("/stdIfcDir \"$(VC_IFCPath)\""); + arguments.create_and_push("/ifcSearchDir"); + arguments.create_and_push( + out_dir + .join(compiler.as_ref()) + .join("modules") + .join("interfaces"), + ); + arguments.create_and_push(format!( + "/Fo{}\\", + out_dir.join(compiler.as_ref()).display() + )); + arguments.create_and_push(format!( + "/Fe{}", + out_dir + .join(compiler.as_ref()) + .join(executable_name) + .with_extension(constants::BINARY_EXTENSION) + .display() + )); + } + CppCompiler::GCC => { + arguments.create_and_push("-fmodules-ts"); + arguments.create_and_push("-o"); + arguments.create_and_push(format!( + "{}", + out_dir + .join(compiler.as_ref()) + .join(executable_name) + .with_extension(constants::BINARY_EXTENSION) + .display() + )); + } + }; + arguments.extend(commands.generated_files_paths.clone().into_iter()); + + commands.main.args.extend(arguments.into_iter()); + commands.main.sources_paths = target + .sourceset() + .sources + .iter() + .map(|s| s.file()) + .collect::>(); + + Ok(()) +} + /// Specific operations over source files mod sources { use super::helpers; @@ -177,107 +273,10 @@ mod sources { modules::{ModuleImplementationModel, ModuleInterfaceModel}, ZorkModel, }, - utils::constants, }; - use color_eyre::Result; use crate::bounds::ExtraArgs; use crate::cli::output::arguments::Arguments; - /// Generates the command line arguments for the desired target - pub fn generate_main_command_line_args<'a>( - model: &'a ZorkModel, - commands: &mut Commands<'a>, - target: &'a impl ExecutableTarget<'a>, - ) -> Result<()> { - log::info!("Generating the main command line..."); - - let compiler = &model.compiler.cpp_compiler; - let out_dir = 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 { - CppCompiler::CLANG => { - arguments.push_opt(model.compiler.stdlib_arg()); - arguments.create_and_push("-fimplicit-modules"); - arguments.push(clang_args::implicit_module_maps(out_dir)); - - arguments.create_and_push(format!( - "-fprebuilt-module-path={}", - out_dir - .join(compiler.as_ref()) - .join("modules") - .join("interfaces") - .display() - )); - - arguments.create_and_push("-o"); - arguments.create_and_push(format!( - "{}", - out_dir - .join(compiler.as_ref()) - .join(executable_name) - .with_extension(constants::BINARY_EXTENSION) - .display() - )); - } - CppCompiler::MSVC => { - arguments.create_and_push("/EHsc"); - arguments.create_and_push("/nologo"); - // If /std:c++20 this, else should be the direct options - // available on C++23 to use directly import std by pre-compiling the standard library - arguments.create_and_push("/experimental:module"); - arguments.create_and_push("/stdIfcDir \"$(VC_IFCPath)\""); - arguments.create_and_push("/ifcSearchDir"); - arguments.create_and_push( - out_dir - .join(compiler.as_ref()) - .join("modules") - .join("interfaces"), - ); - arguments.create_and_push(format!( - "/Fo{}\\", - out_dir.join(compiler.as_ref()).display() - )); - arguments.create_and_push(format!( - "/Fe{}", - out_dir - .join(compiler.as_ref()) - .join(executable_name) - .with_extension(constants::BINARY_EXTENSION) - .display() - )); - } - CppCompiler::GCC => { - arguments.create_and_push("-fmodules-ts"); - arguments.create_and_push("-o"); - arguments.create_and_push(format!( - "{}", - out_dir - .join(compiler.as_ref()) - .join(executable_name) - .with_extension(constants::BINARY_EXTENSION) - .display() - )); - } - }; - arguments.extend(commands.generated_files_paths.clone().into_iter()); - - commands.main.args.extend(arguments.into_iter()); - commands.main.sources_paths = target - .sourceset() - .sources - .iter() - .map(|s| s.file()) - .collect::>(); - - Ok(()) - } - /// Generates the command line arguments for non-module source files pub fn generate_sources_arguments<'a>( model: &'a ZorkModel, @@ -291,6 +290,7 @@ mod sources { let mut arguments = Arguments::default(); arguments.push(model.compiler.language_level_arg()); arguments.create_and_push("-c"); + arguments.extend_from_slice(model.compiler.extra_args()); arguments.extend_from_slice(target.extra_args()); match compiler { @@ -353,12 +353,10 @@ mod sources { let compiler = model.compiler.cpp_compiler; let out_dir = model.build.output_dir.as_ref(); - let mut arguments = Arguments::with_capacity( - 8 + // TODO replace for a constant value matched by compiler - model.compiler.extra_args.len() - // TODO module extra args - ); + let mut arguments = Arguments::default(); arguments.push(model.compiler.language_level_arg()); + arguments.extend_from_slice(model.compiler.extra_args()); + arguments.extend_from_slice(model.modules.extra_args()); match compiler { CppCompiler::CLANG => { @@ -383,7 +381,7 @@ mod sources { arguments.create_and_push("-o"); // The output file let miu_file_path = - Argument::from(helpers::generate_prebuild_miu(compiler, out_dir, interface)); + Argument::from(helpers::generate_prebuilt_miu(compiler, out_dir, interface)); commands.generated_files_paths.push(miu_file_path.clone()); arguments.push(miu_file_path); // The input file @@ -409,7 +407,7 @@ mod sources { // The output .obj file let obj_file = - Argument::from(helpers::generate_prebuild_miu(compiler, out_dir, interface)); + Argument::from(helpers::generate_prebuilt_miu(compiler, out_dir, interface)); commands.generated_files_paths.push(obj_file.clone()); arguments.create_and_push(format!("/Fo{obj_file}")); @@ -436,7 +434,7 @@ mod sources { // The output file arguments.create_and_push("-o"); let miu_file_path = - Argument::from(helpers::generate_prebuild_miu(compiler, out_dir, interface)); + Argument::from(helpers::generate_prebuilt_miu(compiler, out_dir, interface)); commands.generated_files_paths.push(miu_file_path.clone()); arguments.push(miu_file_path); } @@ -460,8 +458,10 @@ mod sources { let compiler = model.compiler.cpp_compiler; let out_dir = model.build.output_dir.as_ref(); - let mut arguments = Arguments::with_capacity(12); // TODO as ifcs + let mut arguments = Arguments::default(); arguments.push(model.compiler.language_level_arg()); + arguments.extend_from_slice(model.compiler.extra_args()); + arguments.extend_from_slice(model.modules.extra_args()); match compiler { CppCompiler::CLANG => { @@ -570,7 +570,7 @@ mod helpers { /// For MSVC, we are relying in the autogenerate of the BMI automatically by the compiler, /// so the output file that we need is an obj file (.obj), and not the /// binary module interface (.ifc) - pub(crate) fn generate_prebuild_miu( + pub(crate) fn generate_prebuilt_miu( compiler: CppCompiler, out_dir: &Path, interface: &ModuleInterfaceModel, @@ -625,7 +625,7 @@ mod helpers { /// generate commands for the non processed elements yet. /// /// This is for `GCC` and `Clang` - pub(crate) fn prebuild_sys_modules<'a>( + pub(crate) fn build_sys_modules<'a>( model: &'a ZorkModel, commands: &mut Commands<'a>, cache: &ZorkCache, diff --git a/zork++/src/lib/config_file/modules.rs b/zork++/src/lib/config_file/modules.rs index ac2f3de1..042a49d2 100644 --- a/zork++/src/lib/config_file/modules.rs +++ b/zork++/src/lib/config_file/modules.rs @@ -2,12 +2,13 @@ use serde::Deserialize; /// [`ModulesAttribute`] - The core section to instruct the compiler to work with C++20 modules. The most important are the base path to the interfaces and implementation files -/// * `base_ifcs_dir`- Base directory to shorcut the path of the implementation files +/// * `base_ifcs_dir`- Base directory to shortcut the path of the implementation files /// * `interfaces` - A list to define the module interface translation units for the project -/// * `base_impls_dir` - Base directory to shorcut the path of the implementation files +/// * `base_impls_dir` - Base directory to shortcut the path of the implementation files /// * `implementations` - A list to define the module interface translation units for the project /// * `sys_modules` - An array field explicitly declare which system headers /// must be precompiled in order to make the importable translation units +/// * `extra_args` - Extra arguments that will be added to the generated command lines /// /// ### Tests /// @@ -23,6 +24,7 @@ use serde::Deserialize; /// { file = 'math.cpp' }, { file = 'some_module_impl.cpp', dependencies = ['iostream'] } /// ] /// sys_modules = ['iostream', 'vector', 'string', 'type_traits', 'functional'] +/// extra_args = ['-Wall'] /// "#; /// /// let config: ModulesAttribute = toml::from_str(CONFIG_FILE_MOCK) @@ -69,6 +71,8 @@ pub struct ModulesAttribute<'a> { pub implementations: Option>>, #[serde(borrow)] pub sys_modules: Option>, + #[serde(borrow)] + pub extra_args: Option>, } /// [`ModuleInterface`] - A module interface structure for dealing diff --git a/zork++/src/lib/project_model/modules.rs b/zork++/src/lib/project_model/modules.rs index 546cd0f6..b0a098c8 100644 --- a/zork++/src/lib/project_model/modules.rs +++ b/zork++/src/lib/project_model/modules.rs @@ -2,6 +2,8 @@ use core::fmt; use std::path::{Path, PathBuf}; use crate::{bounds::TranslationUnit, config_file::modules::ModulePartition}; +use crate::bounds::ExtraArgs; +use crate::cli::output::arguments::Argument; #[derive(Debug, PartialEq, Eq)] pub struct ModulesModel<'a> { @@ -10,6 +12,13 @@ pub struct ModulesModel<'a> { pub base_impls_dir: &'a Path, pub implementations: Vec>, pub sys_modules: Vec<&'a str>, + pub extra_args: Vec>, +} + +impl<'a> ExtraArgs<'a> for ModulesModel<'a> { + fn extra_args(&'a self) -> &'a [Argument<'a>] { + &self.extra_args + } } #[derive(Debug, PartialEq, Eq)] diff --git a/zork++/src/lib/utils/constants.rs b/zork++/src/lib/utils/constants.rs index 600b0918..a1d41750 100644 --- a/zork++/src/lib/utils/constants.rs +++ b/zork++/src/lib/utils/constants.rs @@ -62,4 +62,5 @@ implementations = [ { file = 'some_module_impl.cpp', dependencies = ['iostream'] } ] sys_modules = [ "iostream" ] +extra_args = [ "-Wall" ] "#; diff --git a/zork++/src/lib/utils/reader.rs b/zork++/src/lib/utils/reader.rs index 9502b930..3b96fb74 100644 --- a/zork++/src/lib/utils/reader.rs +++ b/zork++/src/lib/utils/reader.rs @@ -216,12 +216,18 @@ fn assemble_modules_model<'a>( .and_then(|modules| modules.sys_modules.as_ref()) .map_or_else(Default::default, |headers| headers.clone()); + let extra_args = config + .and_then(|mod_attr| mod_attr.extra_args.as_ref()) + .map(|args| args.iter().map(|arg| Argument::from(*arg)).collect()) + .unwrap_or_default(); + ModulesModel { base_ifcs_dir: Path::new(base_ifcs_dir), interfaces, base_impls_dir: Path::new(base_impls_dir), implementations, sys_modules, + extra_args } } @@ -402,6 +408,7 @@ mod test { base_impls_dir: Path::new("."), implementations: vec![], sys_modules: vec![], + extra_args: vec![] }, tests: TestsModel { test_executable_name: "Zork++_test".to_string(), @@ -479,6 +486,7 @@ mod test { }, ], sys_modules: vec!["iostream"], + extra_args: vec![Argument::from("-Wall")] }, tests: TestsModel { test_executable_name: "zork_check".to_string(), From 4819dfb7b1d54f371749000edcbd9eeb266149ca Mon Sep 17 00:00:00 2001 From: Alex Vergara Date: Fri, 17 Mar 2023 20:54:11 +0100 Subject: [PATCH 42/60] Formatting adjustements and deactivating Clang's test because the Clang's version in the GitHub vm's --- zork++/src/lib/cli/output/arguments.rs | 9 ++++++--- zork++/src/lib/cli/output/commands.rs | 2 +- zork++/src/lib/compiler/mod.rs | 15 ++++++++------- zork++/src/lib/project_model/modules.rs | 2 +- zork++/src/lib/utils/fs.rs | 14 +++++++++----- zork++/src/lib/utils/reader.rs | 16 +++++++++------- zork++/test/test.rs | 1 + 7 files changed, 35 insertions(+), 24 deletions(-) diff --git a/zork++/src/lib/cli/output/arguments.rs b/zork++/src/lib/cli/output/arguments.rs index deeb0261..1e47ab0b 100644 --- a/zork++/src/lib/cli/output/arguments.rs +++ b/zork++/src/lib/cli/output/arguments.rs @@ -1,9 +1,9 @@ //! Types and procedures that represents a command line argument, //! or collections of command line arguments +use std::ops::Deref; use std::path::Path; use std::{borrow::Borrow, ffi::OsStr, path::PathBuf}; -use std::ops::Deref; use serde::{Deserialize, Serialize}; @@ -76,7 +76,9 @@ impl<'a> core::fmt::Display for Argument<'a> { pub struct Arguments<'a>(Vec>); impl<'a> Arguments<'a> { /// Wraps an existing [`std::vec::Vec`] of [`Argument`] - pub fn from_vec(vec: Vec>) -> Self { Self(vec) } + pub fn from_vec(vec: Vec>) -> Self { + Self(vec) + } /// Returns a new collection of [`Argument`] with the specified capacity pub fn with_capacity(cap: usize) -> Self { @@ -85,7 +87,8 @@ impl<'a> Arguments<'a> { /// Creates and stores a new [`Argument`] to the end of this collection pub fn create_and_push(&mut self, val: T) - where T: Into> + where + T: Into>, { self.0.push(val.into()) } diff --git a/zork++/src/lib/cli/output/commands.rs b/zork++/src/lib/cli/output/commands.rs index e2939833..e25ca81e 100644 --- a/zork++/src/lib/cli/output/commands.rs +++ b/zork++/src/lib/cli/output/commands.rs @@ -5,6 +5,7 @@ use std::{ }; use crate::bounds::TranslationUnit; +use crate::cli::output::arguments::Arguments; ///! Contains helpers and data structure to process in /// a nice and neat way the commands generated to be executed /// by Zork++ @@ -18,7 +19,6 @@ use color_eyre::{ Report, Result, }; use serde::{Deserialize, Serialize}; -use crate::cli::output::arguments::Arguments; use super::arguments::Argument; diff --git a/zork++/src/lib/compiler/mod.rs b/zork++/src/lib/compiler/mod.rs index 4e3f1295..2032a4a6 100644 --- a/zork++/src/lib/compiler/mod.rs +++ b/zork++/src/lib/compiler/mod.rs @@ -7,8 +7,10 @@ use color_eyre::Result; use std::path::Path; use crate::bounds::{ExecutableTarget, ExtraArgs, TranslationUnit}; +use crate::cli::output::arguments::{clang_args, Arguments}; use crate::cli::output::commands::{CommandExecutionResult, SourceCommandLine}; use crate::compiler::helpers::flag_source_file_without_changes; +use crate::utils::constants; use crate::{ cache::ZorkCache, cli::output::{arguments::Argument, commands::Commands}, @@ -18,8 +20,6 @@ use crate::{ ZorkModel, }, }; -use crate::cli::output::arguments::{Arguments, clang_args}; -use crate::utils::constants; /// The entry point of the compilation process /// @@ -261,6 +261,8 @@ pub fn generate_main_command_line_args<'a>( /// Specific operations over source files mod sources { use super::helpers; + use crate::bounds::ExtraArgs; + use crate::cli::output::arguments::Arguments; use crate::project_model::sourceset::SourceFile; use crate::{ bounds::{ExecutableTarget, TranslationUnit}, @@ -274,8 +276,6 @@ mod sources { ZorkModel, }, }; - use crate::bounds::ExtraArgs; - use crate::cli::output::arguments::Arguments; /// Generates the command line arguments for non-module source files pub fn generate_sources_arguments<'a>( @@ -677,9 +677,10 @@ mod helpers { .collect::>(); for collection_args in sys_modules { - commands - .system_modules - .insert(collection_args[4].value.to_string(), Arguments::from_vec(collection_args)); + commands.system_modules.insert( + collection_args[4].value.to_string(), + Arguments::from_vec(collection_args), + ); } } diff --git a/zork++/src/lib/project_model/modules.rs b/zork++/src/lib/project_model/modules.rs index b0a098c8..2b2147f5 100644 --- a/zork++/src/lib/project_model/modules.rs +++ b/zork++/src/lib/project_model/modules.rs @@ -1,9 +1,9 @@ use core::fmt; use std::path::{Path, PathBuf}; -use crate::{bounds::TranslationUnit, config_file::modules::ModulePartition}; use crate::bounds::ExtraArgs; use crate::cli::output::arguments::Argument; +use crate::{bounds::TranslationUnit, config_file::modules::ModulePartition}; #[derive(Debug, PartialEq, Eq)] pub struct ModulesModel<'a> { diff --git a/zork++/src/lib/utils/fs.rs b/zork++/src/lib/utils/fs.rs index fa82a10d..d023aad9 100644 --- a/zork++/src/lib/utils/fs.rs +++ b/zork++/src/lib/utils/fs.rs @@ -1,11 +1,11 @@ +use color_eyre::eyre::ContextCompat; +use color_eyre::{eyre::Context, Result}; +use serde::{Deserialize, Serialize}; use std::{ fs::{DirBuilder, File}, io::{BufReader, Write}, path::{Path, PathBuf}, }; -use color_eyre::eyre::ContextCompat; -use color_eyre::{eyre::Context, Result}; -use serde::{Deserialize, Serialize}; use super::constants; @@ -18,7 +18,9 @@ pub fn create_file<'a>(path: &Path, filename: &'a str, buff_write: &'a [u8]) -> .with_context(|| format!("Could not create file {file_path:?}"))? .write_all(buff_write) .with_context(|| format!("Could not write to file {file_path:?}")) - } else { Ok(()) } + } else { + Ok(()) + } } /// Recursively creates a new directory pointed at the value of target if not exists yet @@ -28,7 +30,9 @@ pub fn create_directory(target: &Path) -> Result<()> { .recursive(true) .create(target) .with_context(|| format!("Could not create directory {target:?}")) - } else { Ok(()) } + } else { + Ok(()) + } } #[inline(always)] diff --git a/zork++/src/lib/utils/reader.rs b/zork++/src/lib/utils/reader.rs index 3b96fb74..8aaabc01 100644 --- a/zork++/src/lib/utils/reader.rs +++ b/zork++/src/lib/utils/reader.rs @@ -227,7 +227,7 @@ fn assemble_modules_model<'a>( base_impls_dir: Path::new(base_impls_dir), implementations, sys_modules, - extra_args + extra_args, } } @@ -284,8 +284,9 @@ fn assemble_module_implementation_model<'a>( } } - let file_details = utils::fs::get_file_details(&file_path) - .unwrap_or_else(|_| panic!("An unexpected error happened getting the file details for {file_path:?}")); + let file_details = utils::fs::get_file_details(&file_path).unwrap_or_else(|_| { + panic!("An unexpected error happened getting the file details for {file_path:?}") + }); ModuleImplementationModel { path: file_details.0, @@ -341,8 +342,9 @@ fn get_sourceset_for(srcs: Vec<&str>, project_root: &Path) -> SourceSet { .expect("Error getting the declared paths for the source files") }) .map(|pb| { - let file_details = utils::fs::get_file_details(&pb) - .unwrap_or_else(|_| panic!("An unexpected error happened getting the file details for {pb:?}")); + let file_details = utils::fs::get_file_details(&pb).unwrap_or_else(|_| { + panic!("An unexpected error happened getting the file details for {pb:?}") + }); SourceFile { path: file_details.0, file_stem: file_details.1, @@ -408,7 +410,7 @@ mod test { base_impls_dir: Path::new("."), implementations: vec![], sys_modules: vec![], - extra_args: vec![] + extra_args: vec![], }, tests: TestsModel { test_executable_name: "Zork++_test".to_string(), @@ -486,7 +488,7 @@ mod test { }, ], sys_modules: vec!["iostream"], - extra_args: vec![Argument::from("-Wall")] + extra_args: vec![Argument::from("-Wall")], }, tests: TestsModel { test_executable_name: "zork_check".to_string(), diff --git a/zork++/test/test.rs b/zork++/test/test.rs index 3ecb010a..675c7e6e 100644 --- a/zork++/test/test.rs +++ b/zork++/test/test.rs @@ -5,6 +5,7 @@ use tempfile::tempdir; use zork::cli::input::CliArgs; #[test] +#[ignore] // Ignore because the Clang's version in the VM's is older enought to not support module partitions fn test_clang_full_process() -> Result<()> { let temp = tempdir()?; From 701236b093fc7011c380d80d4ddb4711769166e1 Mon Sep 17 00:00:00 2001 From: Alex Vergara Date: Sun, 2 Apr 2023 21:03:10 +0200 Subject: [PATCH 43/60] Solved the duplicated error message when some compiler execution returns a non successful code --- zork++/src/bin/main.rs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/zork++/src/bin/main.rs b/zork++/src/bin/main.rs index 384751fd..982ce764 100644 --- a/zork++/src/bin/main.rs +++ b/zork++/src/bin/main.rs @@ -1,6 +1,7 @@ use std::path::Path; use clap::Parser; +use color_eyre::eyre::Context; use color_eyre::Result; use env_logger::Target; use zork::{cli::input::CliArgs, utils::logger::config_logger, worker::run_zork}; @@ -12,7 +13,8 @@ fn main() -> Result<()> { let process_start_time = std::time::Instant::now(); let cli_args = CliArgs::parse(); - config_logger(cli_args.verbose, Target::Stdout).expect("Error configuring the logger"); + config_logger(cli_args.verbose, Target::Stdout) + .with_context(|| "Error configuring the logger")?; log::info!("Launching a new Zork++ program"); match run_zork(&cli_args, Path::new(".")) { @@ -25,10 +27,12 @@ fn main() -> Result<()> { } Err(err) => { log::error!( - "[FAILED] - The process failed, taking a total time in complete of: {:?} ms\n{err:?}", + "[FAILED] - The process failed, taking a total time in complete of: {:?} ms", process_start_time.elapsed().as_millis() ); Err(err) } - } + }?; + + Ok(()) } From ec5c836649aa9bf8bf381330fda06d75ee5668d6 Mon Sep 17 00:00:00 2001 From: Alex Vergara Date: Sun, 2 Apr 2023 21:51:22 +0200 Subject: [PATCH 44/60] Shortened the error message when the build fails for a concrete file --- zork++/src/lib/cli/output/commands.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zork++/src/lib/cli/output/commands.rs b/zork++/src/lib/cli/output/commands.rs index e25ca81e..cca246c2 100644 --- a/zork++/src/lib/cli/output/commands.rs +++ b/zork++/src/lib/cli/output/commands.rs @@ -53,7 +53,7 @@ pub fn run_generated_commands( } else if !r.as_ref().unwrap().success() { let err = eyre!( "Ending the program, because the build of: {:?} wasn't ended successfully", - source_file + source_file.file ); cache::save(program_data, cache, commands, test_mode)?; return Err(err); From 6d1d85316a61d859f18c2c751b0379827007e833 Mon Sep 17 00:00:00 2001 From: Alex Vergara Date: Sun, 2 Apr 2023 22:07:55 +0200 Subject: [PATCH 45/60] V0.8.4 --- CHANGELOG.md | 7 +++++++ release-config/windows-installer-zork.iss | 2 +- zork++/Cargo.toml | 2 +- 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 65048ca6..88506c07 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - make the project model full owned, and cache it? - generate a second `std.h` to bind to the Zork++ autogenerated `modulemap` when `libc++` is selected as the target standard library +- +## [0.8.4] - 2023 - 04 - 02 + +### Fix + +- Shortened the error in the command line when a build action fails +- Removed the duplicated error shown in the terminal when the build fails ## [0.8.3] - 2023 - 03 - 16 diff --git a/release-config/windows-installer-zork.iss b/release-config/windows-installer-zork.iss index 6e2dd082..60a6db7b 100644 --- a/release-config/windows-installer-zork.iss +++ b/release-config/windows-installer-zork.iss @@ -2,7 +2,7 @@ ; SEE THE DOCUMENTATION FOR DETAILS ON CREATING INNO SETUP SCRIPT FILES! #define MyAppName "Zork++" -#define MyAppVersion "0.8.3" +#define MyAppVersion "0.8.4" #define MyAppPublisher "Zero Day Code" #define MyAppURL "https://github.com/zerodaycode/Zork" #define MyAppExeName "zork++.exe" diff --git a/zork++/Cargo.toml b/zork++/Cargo.toml index c5d586b5..7a58722b 100644 --- a/zork++/Cargo.toml +++ b/zork++/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "zork" -version = "0.8.3" +version = "0.8.4" authors = ["Zero Day Code"] edition = "2021" description = "A modern C++ project manager and build system for modern C++" From c3f85a96e1f6f7e8547f413f0dac0db7942e4ca7 Mon Sep 17 00:00:00 2001 From: Alex Vergara Date: Wed, 26 Apr 2023 17:01:52 +0200 Subject: [PATCH 46/60] Started initial work for implement workspaces like Cargo in Zork++ --- zork++/src/lib/config_file/mod.rs | 4 +++ zork++/src/lib/config_file/workspace.rs | 39 +++++++++++++++++++++++ zork++/src/lib/project_model/mod.rs | 3 ++ zork++/src/lib/project_model/workspace.rs | 4 +++ zork++/src/lib/utils/reader.rs | 18 ++++++++++- 5 files changed, 67 insertions(+), 1 deletion(-) create mode 100644 zork++/src/lib/config_file/workspace.rs create mode 100644 zork++/src/lib/project_model/workspace.rs diff --git a/zork++/src/lib/config_file/mod.rs b/zork++/src/lib/config_file/mod.rs index 4ba90ce4..988ca704 100644 --- a/zork++/src/lib/config_file/mod.rs +++ b/zork++/src/lib/config_file/mod.rs @@ -6,10 +6,12 @@ pub mod executable; pub mod modules; pub mod project; pub mod tests; +pub mod workspace; use std::fmt::Debug; use serde::Deserialize; +use crate::config_file::workspace::WorkspaceAttribute; use self::{ build::BuildAttribute, compiler::CompilerAttribute, executable::ExecutableAttribute, @@ -44,6 +46,8 @@ use self::{ /// and properties #[derive(Deserialize, Debug, Default)] pub struct ZorkConfigFile<'a> { + #[serde(borrow)] + pub workspace: Option>, #[serde(borrow)] pub project: ProjectAttribute<'a>, #[serde(borrow)] diff --git a/zork++/src/lib/config_file/workspace.rs b/zork++/src/lib/config_file/workspace.rs new file mode 100644 index 00000000..634a3e60 --- /dev/null +++ b/zork++/src/lib/config_file/workspace.rs @@ -0,0 +1,39 @@ +use serde::Deserialize; + +/// [`WorkspaceAttribute`] - Defines the characteristics of a `Zork++` workspace. +/// +/// A `Zork++` workspace is a similar concept to Cargo workspaces, where the managed by +/// the workspace collection of projects shares dependencies, a common output directory, +/// metadata attributes... Commands applied to a workspace are propagated down to the +/// workspace members. +/// +/// This allows the user to divide a project into smaller pieces, or create new organization +/// structures for more complex configurations. +/// +/// * `members` - A collection of the names by which the dependent projects (every member of the ws) +/// has been defined in their own `zork**.toml` config file in the **project_name** key +/// +/// ### Tests +/// +/// ```rust +/// use zork::config_file::workspace::{ +/// WorkspaceAttribute +/// }; +/// + +/// ``` +/// +/// > Note: TOML table are toml commented (#) to allow us to parse +/// the inner attributes as the direct type that they belongs to. +/// That commented tables aren't the real TOML, they are just there +/// for testing and exemplification purposes of the inner attributes +/// of the configuration file. +/// +/// For a test over a real example, please look at the +/// [`zork::config_file::ZorkConfigFile`] doc-test +#[derive(Deserialize, Debug, PartialEq, Default)] +#[serde(deny_unknown_fields)] +pub struct WorkspaceAttribute<'a> { + #[serde(borrow)] + pub members: Vec<&'a str> +} \ No newline at end of file diff --git a/zork++/src/lib/project_model/mod.rs b/zork++/src/lib/project_model/mod.rs index 467e9046..24c3eb6a 100644 --- a/zork++/src/lib/project_model/mod.rs +++ b/zork++/src/lib/project_model/mod.rs @@ -5,8 +5,10 @@ pub mod modules; pub mod project; pub mod sourceset; pub mod tests; +pub mod workspace; use std::fmt::Debug; +use crate::project_model::workspace::WorkspaceModel; use self::{ build::BuildModel, compiler::CompilerModel, executable::ExecutableModel, modules::ModulesModel, @@ -15,6 +17,7 @@ use self::{ #[derive(Debug, PartialEq, Eq)] pub struct ZorkModel<'a> { + pub workspace: WorkspaceModel<'a>, pub project: ProjectModel<'a>, pub compiler: CompilerModel<'a>, pub build: BuildModel, diff --git a/zork++/src/lib/project_model/workspace.rs b/zork++/src/lib/project_model/workspace.rs new file mode 100644 index 00000000..164f09e6 --- /dev/null +++ b/zork++/src/lib/project_model/workspace.rs @@ -0,0 +1,4 @@ +#[derive(Debug, PartialEq, Eq)] +pub struct WorkspaceModel<'a> { + pub members: Vec<&'a str> +} \ No newline at end of file diff --git a/zork++/src/lib/utils/reader.rs b/zork++/src/lib/utils/reader.rs index 5b669b10..90d9a490 100644 --- a/zork++/src/lib/utils/reader.rs +++ b/zork++/src/lib/utils/reader.rs @@ -28,6 +28,8 @@ use crate::{ use color_eyre::{eyre::eyre, Result}; use std::path::{Path, PathBuf}; use walkdir::{DirEntry, WalkDir}; +use crate::config_file::workspace::WorkspaceAttribute; +use crate::project_model::workspace::WorkspaceModel; use super::constants::DEFAULT_OUTPUT_DIR; @@ -59,7 +61,7 @@ pub fn find_config_files( let mut files = vec![]; for e in WalkDir::new(base_path) - .max_depth(2) + .max_depth(2)// TODO 0 if not workspace? Else, allowed more depth? .into_iter() .filter_map(|e| e.ok()) { @@ -91,6 +93,7 @@ pub fn build_model<'a>( config: &'a ZorkConfigFile, project_root_from_cli: &Path, ) -> Result> { + let workspace = assemble_workspace_model(&config.workspace); let project = assemble_project_model(&config.project); let absolute_project_root = get_project_root_absolute_path( @@ -108,6 +111,7 @@ pub fn build_model<'a>( let tests = assemble_tests_model(project.name, &config.tests, &absolute_project_root); Ok(ZorkModel { + workspace, project, compiler, build, @@ -117,6 +121,12 @@ pub fn build_model<'a>( }) } +fn assemble_workspace_model<'a>(config: &'a Option) -> WorkspaceModel<'a> { + WorkspaceModel { + members: config.as_ref().unwrap_or(&WorkspaceAttribute::default()).members.clone() + } +} + fn assemble_project_model<'a>(config: &'a ProjectAttribute) -> ProjectModel<'a> { ProjectModel { name: config.name, @@ -394,6 +404,9 @@ mod test { let abs_path_for_mock = fs::get_project_root_absolute_path(Path::new("."))?; let expected = ZorkModel { + workspace: WorkspaceModel { + members: vec![], + }, project: ProjectModel { name: "Zork++", authors: &["zerodaycode.gz@gmail.com"], @@ -443,6 +456,9 @@ mod test { let abs_path_for_mock = fs::get_project_root_absolute_path(Path::new("."))?; let expected = ZorkModel { + workspace: WorkspaceModel { + members: vec![], + }, project: ProjectModel { name: "Zork++", authors: &["zerodaycode.gz@gmail.com"], From 27d0f8b64cc526198faaf8bdafb7dee4c343505a Mon Sep 17 00:00:00 2001 From: Alex Vergara Date: Fri, 28 Apr 2023 14:20:36 +0200 Subject: [PATCH 47/60] WIP - Refactoring the main processor loop for the config files parsing --- zork++/src/lib/lib.rs | 47 +++++++++++++++++++++------------- zork++/src/lib/utils/reader.rs | 17 +++++++++++- 2 files changed, 45 insertions(+), 19 deletions(-) diff --git a/zork++/src/lib/lib.rs b/zork++/src/lib/lib.rs index 72586c2c..2b7113f8 100644 --- a/zork++/src/lib/lib.rs +++ b/zork++/src/lib/lib.rs @@ -47,26 +47,37 @@ pub mod worker { return create_templated_project(path, name, git, compiler.into(), template); }; - let config_files: Vec = find_config_files(path, &cli_args.match_files) - .with_context(|| "We didn't found a valid Zork++ configuration file")?; + let config_files = find_config_files(path, &cli_args.match_files) + .with_context(|| "We didn't found a valid Zork++ configuration file")? + .iter() + .map(|cfg| { + let raw_file = fs::read_to_string(&cfg.path).with_context(|| { + format!( + "An error happened parsing the configuration file: {:?}", + config_file.dir_entry.file_name() + ) + })?; + toml::from_str(raw_file.as_str()) + .with_context(|| "Could not parse configuration file")? + }); log::trace!("Config files found: {config_files:?}"); - for config_file in config_files { - log::debug!( - "Launching a Zork++ work event for the configuration file: {:?}, located at: {:?}\n", - config_file.dir_entry.file_name(), - config_file.path - ); - let raw_file = fs::read_to_string(config_file.path).with_context(|| { - format!( - "An error happened parsing the configuration file: {:?}", - config_file.dir_entry.file_name() - ) - })?; - - let config: ZorkConfigFile = toml::from_str(raw_file.as_str()) - .with_context(|| "Could not parse configuration file")?; - let program_data = build_model(&config, Path::new("."))?; + for zork_config_file in config_files { + // log::debug!( + // "Launching a Zork++ work event for the configuration file: {:?}, located at: {:?}\n", + // config_file.dir_entry.file_name(), + // config_file.path + // ); + // let raw_file = fs::read_to_string(config_file.path).with_context(|| { + // format!( + // "An error happened parsing the configuration file: {:?}", + // config_file.dir_entry.file_name() + // ) + // })?; + // + // let config: ZorkConfigFile = toml::from_str(raw_file.as_str()) + // .with_context(|| "Could not parse configuration file")?; + let program_data = build_model(&zork_config_file, Path::new("."))?; create_output_directory(&program_data)?; let cache = cache::load(&program_data, cli_args) diff --git a/zork++/src/lib/utils/reader.rs b/zork++/src/lib/utils/reader.rs index 90d9a490..416f7a6c 100644 --- a/zork++/src/lib/utils/reader.rs +++ b/zork++/src/lib/utils/reader.rs @@ -60,8 +60,23 @@ pub fn find_config_files( log::debug!("Searching for Zork++ configuration files..."); let mut files = vec![]; + /* + Opción A: Matcheamos con depth = 1, con lo cual solo puedes correr zork++ desde mínimo, dentro + de la raíz del projecto, PEEEERO... habría que buscar de nuevo las config files registradas + en el workspace + + Opción B: Cargarlas todas. Parsear fuera del bucle principal. Organizar. En caso de haber + workspace, lógica 1. Else => otra lógica + + Opción C: Buscar siempre con depth = 1. Si cuando salen los resultados, la config file es + un workspace, volver a buscar. Si no, ya estaríamos compilando el crate al que apunta "el tema" + + Opción D: Al pasar la flag de workspace, sabemos que es de antemano un workspace. Eso implica menos + lógica de proceso, pero podría haber distintos zork por ahí aunque sea de puto milagro + + */ for e in WalkDir::new(base_path) - .max_depth(2)// TODO 0 if not workspace? Else, allowed more depth? + .max_depth(1) .into_iter() .filter_map(|e| e.ok()) { From c90dee68d592f8b1e75bc39fc3da262fa97172bc Mon Sep 17 00:00:00 2001 From: Alex Vergara Date: Sun, 30 Jul 2023 23:01:09 +0200 Subject: [PATCH 48/60] Solved the issues with the toml deserialization lifetimes. Now, we have to adapt our current workflow to be able to fully implement the workspaces features, but at least, now we have all the deserialization job done --- zork++/src/lib/config_file/build.rs | 2 +- zork++/src/lib/config_file/compiler.rs | 2 +- zork++/src/lib/config_file/mod.rs | 2 +- zork++/src/lib/config_file/modules.rs | 8 +-- zork++/src/lib/config_file/project.rs | 2 +- zork++/src/lib/config_file/tests.rs | 2 +- zork++/src/lib/config_file/workspace.rs | 2 +- zork++/src/lib/lib.rs | 65 +++++++++++++------------ 8 files changed, 43 insertions(+), 42 deletions(-) diff --git a/zork++/src/lib/config_file/build.rs b/zork++/src/lib/config_file/build.rs index 268fd574..8ac6d83a 100644 --- a/zork++/src/lib/config_file/build.rs +++ b/zork++/src/lib/config_file/build.rs @@ -23,7 +23,7 @@ use serde::*; /// /// assert_eq!(config.output_dir, Some("out")); /// ``` -#[derive(Deserialize, Debug, PartialEq)] +#[derive(Deserialize, Debug, PartialEq, Clone)] #[serde(deny_unknown_fields)] pub struct BuildAttribute<'a> { #[serde(borrow)] diff --git a/zork++/src/lib/config_file/compiler.rs b/zork++/src/lib/config_file/compiler.rs index 293f1078..bafbe41f 100644 --- a/zork++/src/lib/config_file/compiler.rs +++ b/zork++/src/lib/config_file/compiler.rs @@ -66,7 +66,7 @@ use crate::project_model; /// /// For a test over a real example, please look at the /// [`zork::config_file::ZorkConfigFile`] doc-test -#[derive(Deserialize, Debug, PartialEq, Default)] +#[derive(Deserialize, Debug, PartialEq, Default, Clone)] #[serde(deny_unknown_fields)] pub struct CompilerAttribute<'a> { pub cpp_compiler: CppCompiler, diff --git a/zork++/src/lib/config_file/mod.rs b/zork++/src/lib/config_file/mod.rs index 988ca704..bf81264d 100644 --- a/zork++/src/lib/config_file/mod.rs +++ b/zork++/src/lib/config_file/mod.rs @@ -44,7 +44,7 @@ use self::{ /// The [`ZorkConfigFile`] is the type that holds /// the whole hierarchy of Zork++ config file attributes /// and properties -#[derive(Deserialize, Debug, Default)] +#[derive(Deserialize, Debug, Default, Clone)] pub struct ZorkConfigFile<'a> { #[serde(borrow)] pub workspace: Option>, diff --git a/zork++/src/lib/config_file/modules.rs b/zork++/src/lib/config_file/modules.rs index 042a49d2..757b2ea7 100644 --- a/zork++/src/lib/config_file/modules.rs +++ b/zork++/src/lib/config_file/modules.rs @@ -59,7 +59,7 @@ use serde::Deserialize; /// assert_eq!(&gcc_sys_headers[3], &"type_traits"); /// assert_eq!(&gcc_sys_headers[4], &"functional"); /// ``` -#[derive(Deserialize, Debug, PartialEq)] +#[derive(Deserialize, Debug, PartialEq, Clone)] pub struct ModulesAttribute<'a> { #[serde(borrow)] pub base_ifcs_dir: Option<&'a str>, @@ -132,7 +132,7 @@ pub struct ModulesAttribute<'a> { /// assert_eq!(ifc_3.file, "some_module_part.cppm"); /// assert_eq!(ifc_3.module_name, Some("math_part")); /// ``` -#[derive(Deserialize, Debug, PartialEq)] +#[derive(Deserialize, Debug, PartialEq, Clone)] #[serde(deny_unknown_fields)] pub struct ModuleInterface<'a> { #[serde(borrow)] @@ -158,7 +158,7 @@ pub struct ModuleInterface<'a> { /// * `is_internal_partition` - Optional field for declare that the module is actually /// a module for hold implementation details, known as module implementation partitions. /// This option only takes effect with MSVC -#[derive(Deserialize, Debug, PartialEq)] +#[derive(Deserialize, Debug, PartialEq, Clone)] pub struct ModulePartition<'a> { #[serde(borrow)] pub module: &'a str, @@ -200,7 +200,7 @@ pub struct ModulePartition<'a> { /// assert_eq!(deps[1], "type_traits"); /// assert_eq!(deps[2], "iostream"); /// ``` -#[derive(Deserialize, Debug, PartialEq)] +#[derive(Deserialize, Debug, PartialEq, Clone)] pub struct ModuleImplementation<'a> { #[serde(borrow)] pub file: &'a str, diff --git a/zork++/src/lib/config_file/project.rs b/zork++/src/lib/config_file/project.rs index ff777120..d01803ce 100644 --- a/zork++/src/lib/config_file/project.rs +++ b/zork++/src/lib/config_file/project.rs @@ -35,7 +35,7 @@ use serde::*; /// /// For a test over a real example, please look at the /// [`zork::config_file::ZorkConfigFile`] doc-test -#[derive(Deserialize, Debug, PartialEq, Default)] +#[derive(Deserialize, Debug, PartialEq, Default, Clone)] #[serde(deny_unknown_fields)] pub struct ProjectAttribute<'a> { pub name: &'a str, diff --git a/zork++/src/lib/config_file/tests.rs b/zork++/src/lib/config_file/tests.rs index 9e36733e..b4635bb9 100644 --- a/zork++/src/lib/config_file/tests.rs +++ b/zork++/src/lib/config_file/tests.rs @@ -38,7 +38,7 @@ use serde::*; /// /// For a test over a real example, please look at the /// [`zork::config_file::ZorkConfigFile`] doc-test -#[derive(Deserialize, Debug, PartialEq)] +#[derive(Deserialize, Debug, PartialEq, Clone)] pub struct TestsAttribute<'a> { #[serde(borrow)] pub test_executable_name: Option<&'a str>, diff --git a/zork++/src/lib/config_file/workspace.rs b/zork++/src/lib/config_file/workspace.rs index 634a3e60..b30a1c74 100644 --- a/zork++/src/lib/config_file/workspace.rs +++ b/zork++/src/lib/config_file/workspace.rs @@ -31,7 +31,7 @@ use serde::Deserialize; /// /// For a test over a real example, please look at the /// [`zork::config_file::ZorkConfigFile`] doc-test -#[derive(Deserialize, Debug, PartialEq, Default)] +#[derive(Deserialize, Debug, PartialEq, Default, Clone)] #[serde(deny_unknown_fields)] pub struct WorkspaceAttribute<'a> { #[serde(borrow)] diff --git a/zork++/src/lib/lib.rs b/zork++/src/lib/lib.rs index 2b7113f8..97fa61b5 100644 --- a/zork++/src/lib/lib.rs +++ b/zork++/src/lib/lib.rs @@ -28,7 +28,7 @@ pub mod worker { project_model::{compiler::CppCompiler, ZorkModel}, utils::{ self, - reader::{build_model, find_config_files, ConfigFile}, + reader::{build_model, find_config_files}, template::create_templated_project, }, }; @@ -47,36 +47,40 @@ pub mod worker { return create_templated_project(path, name, git, compiler.into(), template); }; - let config_files = find_config_files(path, &cli_args.match_files) - .with_context(|| "We didn't found a valid Zork++ configuration file")? - .iter() - .map(|cfg| { - let raw_file = fs::read_to_string(&cfg.path).with_context(|| { + // Find and store the config file paths directly. + let config_files_raw = find_config_files(path, &cli_args.match_files) + .with_context(|| "We didn't found a valid Zork++ configuration file") + .unwrap(); + let config_file_paths: Vec<_> = config_files_raw.iter().map(|cfg| cfg.path.clone()).collect(); + log::trace!("Config files found: {config_file_paths:?}"); + + // Read the file contents and store them in a Vec of strings. + let mut config_file_contents: Vec = Vec::new(); + for config_file_path in &config_file_paths { + let contents = fs::read_to_string(config_file_path) + .with_context(|| { format!( "An error happened parsing the configuration file: {:?}", - config_file.dir_entry.file_name() + config_file_path.file_name() ) - })?; - toml::from_str(raw_file.as_str()) - .with_context(|| "Could not parse configuration file")? - }); - log::trace!("Config files found: {config_files:?}"); - - for zork_config_file in config_files { - // log::debug!( - // "Launching a Zork++ work event for the configuration file: {:?}, located at: {:?}\n", - // config_file.dir_entry.file_name(), - // config_file.path - // ); - // let raw_file = fs::read_to_string(config_file.path).with_context(|| { - // format!( - // "An error happened parsing the configuration file: {:?}", - // config_file.dir_entry.file_name() - // ) - // })?; - // - // let config: ZorkConfigFile = toml::from_str(raw_file.as_str()) - // .with_context(|| "Could not parse configuration file")?; + }) + .expect("UPS"); + config_file_contents.push(contents); + } + + // Deserialize the config files one by one, passing references to the strings in the Vec. + let mut zork_config_files = vec![]; + for config_file_content in &config_file_contents { + zork_config_files.push( + toml::from_str::(config_file_content) + .with_context(|| "Could not parse configuration file") + .expect("UPS 2") + ); + // TODO from here, we should check if there's workspaces, and adapt the processing + // workflow from here + } + + for zork_config_file in zork_config_files { let program_data = build_model(&zork_config_file, Path::new("."))?; create_output_directory(&program_data)?; @@ -84,10 +88,7 @@ pub mod worker { .with_context(|| "Unable to load the Zork++ cache")?; do_main_work_based_on_cli_input(cli_args, &program_data, cache).with_context(|| { - format!( - "Failed to build the project for the config file: {:?}", - config_file.dir_entry.file_name() - ) + format!("Failed to build the project: {:?}", zork_config_file.project.name) })?; } From 4d5402231db0476d794a5d1016713e17dc503609 Mon Sep 17 00:00:00 2001 From: Alex Vergara Date: Sun, 14 Apr 2024 10:36:55 +0200 Subject: [PATCH 49/60] fix: Build error due to missplaced duplicated line --- zork++/src/lib/utils/reader.rs | 4 ---- zork++/test/test.rs | 37 +++++++++++++++++----------------- 2 files changed, 18 insertions(+), 23 deletions(-) diff --git a/zork++/src/lib/utils/reader.rs b/zork++/src/lib/utils/reader.rs index 7c212300..c7e6d515 100644 --- a/zork++/src/lib/utils/reader.rs +++ b/zork++/src/lib/utils/reader.rs @@ -106,10 +106,6 @@ pub fn find_config_files( } pub fn build_model<'a>(config: &'a ZorkConfigFile, cli_args: &'a CliArgs) -> Result> { -pub fn build_model<'a>( - config: &'a ZorkConfigFile, - project_root_from_cli: &Path, -) -> Result> { let workspace = assemble_workspace_model(&config.workspace); let project = assemble_project_model(&config.project); diff --git a/zork++/test/test.rs b/zork++/test/test.rs index bcfbbda0..4691b091 100644 --- a/zork++/test/test.rs +++ b/zork++/test/test.rs @@ -24,7 +24,7 @@ fn test_clang_full_process() -> Result<()> { .is_ok()); let process_result = zork::worker::run_zork( - &CliArgs::parse_from(["", "-vv", "run"]), + &CliArgs::parse_from(["", "-vv", "--driver-path", "clang++-16", "run"]), Path::new(temp.path()), ); assert!(process_result.is_ok(), "{}", process_result.unwrap_err()); @@ -76,7 +76,6 @@ fn test_gcc_windows_full_process() -> Result<()> { #[cfg(target_os = "linux")] #[test] -#[ignore] /* In the GitHub's virtual machines, we are still unable, due to the gcm.cache path. @@ -127,7 +126,6 @@ fn test_gcc_linux_full_process() -> Result<()> { } #[test] -#[ignore] // TODO fn test_full_program_with_multi_config_files() -> Result<()> { let temp = tempdir()?; @@ -146,27 +144,26 @@ fn test_full_program_with_multi_config_files() -> Result<()> { } assert!(zork::worker::run_zork( - &CliArgs::parse_from(["", "-vv", "run"]), + &CliArgs::parse_from(["", "-vv", "--match-files", "gcc", "--driver-path", "clang++-16", "run"]), Path::new(temp.path()) ) .is_ok()); - // GCC specifics - if cfg!(target_os = "windows") { - assert!(zork::worker::run_zork( - &CliArgs::parse_from(["", "new", "gcc_example", "--compiler", "gcc"]), - Path::new(".") - ) - .is_ok()); - assert!( - zork::worker::run_zork(&CliArgs::parse_from(["", "-vv", "run"]), Path::new(".")) - .is_ok() - ); + // GCC + assert!(zork::worker::run_zork( + &CliArgs::parse_from(["", "new", "gcc_example", "--compiler", "gcc"]), + Path::new(temp.path()) + ) + .is_ok()); + assert!(zork::worker::run_zork( + &CliArgs::parse_from(["", "-vv", "--match-files", "gcc", "run"]), + Path::new(temp.path()) + ) + .is_ok()); - fs::remove_dir_all("./gcc_example")?; - fs::remove_dir_all("./gcm.cache")?; - fs::remove_dir_all("./out")?; - } + // fs::remove_dir_all("./gcc_example")?; + fs::remove_dir_all("./gcm.cache")?; + // fs::remove_dir_all("./out")?; Ok(temp.close()?) } @@ -202,6 +199,8 @@ mod local_env_tests { &path.display().to_string(), "--match-files", "local_linux", + "--driver-path", + "clang++-16", "run", ]), &path, From 8dd7687d279d7bbb0716696d497cfb112087685f Mon Sep 17 00:00:00 2001 From: Alex Vergara Date: Sun, 14 Apr 2024 10:53:18 +0200 Subject: [PATCH 50/60] fix: Linux integration tests for GCC due to the gcm.cache folder left on the Zork++ cwd --- zork++/src/lib/config_file/mod.rs | 2 +- zork++/src/lib/config_file/workspace.rs | 4 ++-- zork++/src/lib/lib.rs | 12 +++++++++--- zork++/src/lib/project_model/mod.rs | 2 +- zork++/src/lib/project_model/workspace.rs | 4 ++-- zork++/src/lib/utils/reader.rs | 18 +++++++++--------- zork++/test/test.rs | 14 ++++++++++++-- 7 files changed, 36 insertions(+), 20 deletions(-) diff --git a/zork++/src/lib/config_file/mod.rs b/zork++/src/lib/config_file/mod.rs index 8d3aa7f3..c498be1c 100644 --- a/zork++/src/lib/config_file/mod.rs +++ b/zork++/src/lib/config_file/mod.rs @@ -10,8 +10,8 @@ pub mod workspace; use std::fmt::Debug; -use serde::Deserialize; use crate::config_file::workspace::WorkspaceAttribute; +use serde::Deserialize; use self::{ build::BuildAttribute, compiler::CompilerAttribute, executable::ExecutableAttribute, diff --git a/zork++/src/lib/config_file/workspace.rs b/zork++/src/lib/config_file/workspace.rs index b30a1c74..6650aa56 100644 --- a/zork++/src/lib/config_file/workspace.rs +++ b/zork++/src/lib/config_file/workspace.rs @@ -35,5 +35,5 @@ use serde::Deserialize; #[serde(deny_unknown_fields)] pub struct WorkspaceAttribute<'a> { #[serde(borrow)] - pub members: Vec<&'a str> -} \ No newline at end of file + pub members: Vec<&'a str>, +} diff --git a/zork++/src/lib/lib.rs b/zork++/src/lib/lib.rs index 4dbfdd26..752efe77 100644 --- a/zork++/src/lib/lib.rs +++ b/zork++/src/lib/lib.rs @@ -53,7 +53,10 @@ pub mod worker { let config_files_raw = find_config_files(path, &cli_args.match_files) .with_context(|| "We didn't found a valid Zork++ configuration file") .unwrap(); - let config_file_paths: Vec<_> = config_files_raw.iter().map(|cfg| cfg.path.clone()).collect(); + let config_file_paths: Vec<_> = config_files_raw + .iter() + .map(|cfg| cfg.path.clone()) + .collect(); log::trace!("Config files found: {config_file_paths:?}"); // Read the file contents and store them in a Vec of strings. @@ -76,7 +79,7 @@ pub mod worker { zork_config_files.push( toml::from_str::(config_file_content) .with_context(|| "Could not parse configuration file") - .expect("UPS 2") + .expect("UPS 2"), ); // TODO from here, we should check if there's workspaces, and adapt the processing // workflow from here @@ -90,7 +93,10 @@ pub mod worker { .with_context(|| "Unable to load the Zork++ cache")?; do_main_work_based_on_cli_input(cli_args, &program_data, cache).with_context(|| { - format!("Failed to build the project: {:?}", zork_config_file.project.name) + format!( + "Failed to build the project: {:?}", + zork_config_file.project.name + ) })?; } diff --git a/zork++/src/lib/project_model/mod.rs b/zork++/src/lib/project_model/mod.rs index 24c3eb6a..67c75473 100644 --- a/zork++/src/lib/project_model/mod.rs +++ b/zork++/src/lib/project_model/mod.rs @@ -7,8 +7,8 @@ pub mod sourceset; pub mod tests; pub mod workspace; -use std::fmt::Debug; use crate::project_model::workspace::WorkspaceModel; +use std::fmt::Debug; use self::{ build::BuildModel, compiler::CompilerModel, executable::ExecutableModel, modules::ModulesModel, diff --git a/zork++/src/lib/project_model/workspace.rs b/zork++/src/lib/project_model/workspace.rs index 164f09e6..c03bfecc 100644 --- a/zork++/src/lib/project_model/workspace.rs +++ b/zork++/src/lib/project_model/workspace.rs @@ -1,4 +1,4 @@ #[derive(Debug, PartialEq, Eq)] pub struct WorkspaceModel<'a> { - pub members: Vec<&'a str> -} \ No newline at end of file + pub members: Vec<&'a str>, +} diff --git a/zork++/src/lib/utils/reader.rs b/zork++/src/lib/utils/reader.rs index c7e6d515..42bf655e 100644 --- a/zork++/src/lib/utils/reader.rs +++ b/zork++/src/lib/utils/reader.rs @@ -1,5 +1,7 @@ use crate::cli::input::CliArgs; +use crate::config_file::workspace::WorkspaceAttribute; use crate::project_model::sourceset::SourceFile; +use crate::project_model::workspace::WorkspaceModel; use crate::utils::fs::get_project_root_absolute_path; use crate::{ cli::output::arguments::Argument, @@ -29,8 +31,6 @@ use crate::{ use color_eyre::{eyre::eyre, Result}; use std::path::{Path, PathBuf}; use walkdir::{DirEntry, WalkDir}; -use crate::config_file::workspace::WorkspaceAttribute; -use crate::project_model::workspace::WorkspaceModel; use super::constants::DEFAULT_OUTPUT_DIR; @@ -140,7 +140,11 @@ pub fn build_model<'a>(config: &'a ZorkConfigFile, cli_args: &'a CliArgs) -> Res fn assemble_workspace_model<'a>(config: &'a Option) -> WorkspaceModel<'a> { WorkspaceModel { - members: config.as_ref().unwrap_or(&WorkspaceAttribute::default()).members.clone() + members: config + .as_ref() + .unwrap_or(&WorkspaceAttribute::default()) + .members + .clone(), } } @@ -432,9 +436,7 @@ mod test { let abs_path_for_mock = fs::get_project_root_absolute_path(Path::new("."))?; let expected = ZorkModel { - workspace: WorkspaceModel { - members: vec![], - }, + workspace: WorkspaceModel { members: vec![] }, project: ProjectModel { name: "Zork++", authors: &["zerodaycode.gz@gmail.com"], @@ -485,9 +487,7 @@ mod test { let abs_path_for_mock = fs::get_project_root_absolute_path(Path::new("."))?; let expected = ZorkModel { - workspace: WorkspaceModel { - members: vec![], - }, + workspace: WorkspaceModel { members: vec![] }, project: ProjectModel { name: "Zork++", authors: &["zerodaycode.gz@gmail.com"], diff --git a/zork++/test/test.rs b/zork++/test/test.rs index 4691b091..4b918225 100644 --- a/zork++/test/test.rs +++ b/zork++/test/test.rs @@ -1,6 +1,6 @@ use clap::Parser; use color_eyre::Result; -use std::{fs, path::Path}; +use std::{env, fs, path::Path}; use tempfile::tempdir; use zork::cli::input::CliArgs; @@ -144,12 +144,22 @@ fn test_full_program_with_multi_config_files() -> Result<()> { } assert!(zork::worker::run_zork( - &CliArgs::parse_from(["", "-vv", "--match-files", "gcc", "--driver-path", "clang++-16", "run"]), + &CliArgs::parse_from([ + "", + "-vv", + "--match-files", + "clang", + /* "--driver-path", + "clang++-16", */ + "run" + ]), Path::new(temp.path()) ) .is_ok()); // GCC + env::set_current_dir(temp.path())?; // NOTE: Should this fix the problem that GCC saves the + // gcm.cache folder on the CWD? assert!(zork::worker::run_zork( &CliArgs::parse_from(["", "new", "gcc_example", "--compiler", "gcc"]), Path::new(temp.path()) From 1e31f2cea239a7daac2f3f9519b261e22605f47b Mon Sep 17 00:00:00 2001 From: Alex Vergara Date: Sun, 14 Apr 2024 11:06:57 +0200 Subject: [PATCH 51/60] fix: Isolated Linux GCC IT. Removed missing driver flag for Clang tests --- zork++/test/test.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/zork++/test/test.rs b/zork++/test/test.rs index 4b918225..09c9ea07 100644 --- a/zork++/test/test.rs +++ b/zork++/test/test.rs @@ -24,7 +24,8 @@ fn test_clang_full_process() -> Result<()> { .is_ok()); let process_result = zork::worker::run_zork( - &CliArgs::parse_from(["", "-vv", "--driver-path", "clang++-16", "run"]), + //, "--driver-path", "clang++-16" + &CliArgs::parse_from(["", "-vv", "run"]), Path::new(temp.path()), ); assert!(process_result.is_ok(), "{}", process_result.unwrap_err()); @@ -92,6 +93,9 @@ compilation terminated. fn test_gcc_linux_full_process() -> Result<()> { let temp = tempdir()?; + env::set_current_dir(temp.path())?; // NOTE: Should this fix the problem that GCC saves the + // gcm.cache folder on the CWD? + assert!(zork::worker::run_zork( &CliArgs::parse_from(["", "new", "gcc_example", "--compiler", "gcc"]), Path::new(temp.path()) From f323764dbd8c8c0ef8866ddca96c9cb0c988ff3e Mon Sep 17 00:00:00 2001 From: Alex Vergara Date: Sun, 14 Apr 2024 11:22:30 +0200 Subject: [PATCH 52/60] feat: Adding Clang 18 to the matrix of the upstream tests --- .github/workflows/code-quality.yml | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/.github/workflows/code-quality.yml b/.github/workflows/code-quality.yml index a7ee5aa5..6732a43f 100644 --- a/.github/workflows/code-quality.yml +++ b/.github/workflows/code-quality.yml @@ -2,7 +2,7 @@ name: Code quality and sanity on: push: - branches: '*' + branches: 'main' pull_request: branches: '*' @@ -22,12 +22,12 @@ jobs: - uses: hecrj/setup-rust-action@v1 with: components: clippy - - - name: Verifiying the code quality with Clippy + + - name: Verifiying the code quality with Clippy run: | cd zork++ cargo clippy --all-targets --all-features - + rustfmt: name: Verify code formatting runs-on: ubuntu-latest @@ -45,12 +45,12 @@ jobs: cargo fmt --all -- --check tests: - name: Run the tests - Clang + name: Run the tests - Clang runs-on: ubuntu-latest strategy: fail-fast: false matrix: - clang_version: [15, 16, 17] + clang_version: [15, 16, 17, 18] steps: - uses: actions/checkout@v3 - name: Caching project dependencies @@ -73,7 +73,6 @@ jobs: echo "-----> Updating the symbolic link to point to Clang ${{ matrix.clang_version }}" sudo rm -f /usr/bin/clang++ sudo ln -s /usr/bin/clang++-${{ matrix.clang_version }} /usr/bin/clang++ - # Display the installation directory and version of the installed Clang echo "-----> Clang-${{ matrix.clang_version }} was installed on:" From 789bd7a378eaba39e4d884a0dda20cdff11a2105 Mon Sep 17 00:00:00 2001 From: Alex Vergara Date: Thu, 2 May 2024 11:29:41 +0200 Subject: [PATCH 53/60] chore: discarding the workflow for testing the buid with Clang when its version is > 17 --- .github/workflows/code-quality.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/code-quality.yml b/.github/workflows/code-quality.yml index 6732a43f..c5df2805 100644 --- a/.github/workflows/code-quality.yml +++ b/.github/workflows/code-quality.yml @@ -79,7 +79,8 @@ jobs: which clang-${{ matrix.clang_version }} echo "-----> Clang++ was installed on:" which clang++-${{ matrix.clang_version }} - - name: Running the tests for the project + - if: ${{ matrix.clang_version < 18 }} + name: Running the tests for the project run: | cd zork++ RUST_LOG=trace cargo test 2>&1 --all --color=always --no-fail-fast -- --nocapture --show-output --test-threads=1 From 4a4fc7611adbe2f8180bb31d985f4e24b9986c1c Mon Sep 17 00:00:00 2001 From: Alex Vergara Date: Thu, 2 May 2024 13:45:54 +0200 Subject: [PATCH 54/60] feat(WIP)!: Re-enabling the Windows tests --- zork++/test/test.rs | 52 ++++++++++++++++++++++++++------------------- 1 file changed, 30 insertions(+), 22 deletions(-) diff --git a/zork++/test/test.rs b/zork++/test/test.rs index a72b1b5a..97e1b9b6 100644 --- a/zork++/test/test.rs +++ b/zork++/test/test.rs @@ -1,6 +1,5 @@ use clap::Parser; use color_eyre::Result; -use std::fs; use tempfile::tempdir; use zork::cli::input::CliArgs; @@ -37,38 +36,49 @@ fn test_clang_full_process() -> Result<()> { #[cfg(target_os = "windows")] #[test] fn test_msvc_full_process() -> Result<()> { - let temp = tempdir()?; + let tempdir = tempdir()?; + let path = tempdir.path().to_str().unwrap(); - assert!(zork::worker::run_zork( - &CliArgs::parse_from(["", "new", "msvc_example", "--compiler", "msvc"]), - Path::new(temp.path()) - ) + assert!(zork::worker::run_zork(&CliArgs::parse_from([ + "", + "--root", + path, + "new", + "msvc_example", + "--compiler", + "msvc" + ])) .is_ok()); - assert!(zork::worker::run_zork( - &CliArgs::parse_from(["", "-vv", "run"]), - Path::new(temp.path()) - ) - .is_ok()); + assert!( + zork::worker::run_zork(&CliArgs::parse_from(["", "--root", path, "-vv", "run"])).is_ok() + ); - Ok(temp.close()?) + Ok(tempdir.close()?) } #[cfg(target_os = "windows")] #[test] fn test_gcc_windows_full_process() -> Result<()> { - assert!(zork::worker::run_zork( - &CliArgs::parse - Path::new(".") // Unable to run GCC tests because the gcm.cache folder, that - // we just wasn't able to discover how to specify a directory for it - ) + let tempdir = tempdir()?; + let path = tempdir.path().to_str().unwrap(); + + assert!(zork::worker::run_zork(&CliArgs::parse_from([ + "", + "--root", + path, + "new", + "gcc_example", + "--compiler", + "gcc" + ])) .is_ok()); assert!( - zork::worker::run_zork(&CliArgs::parse_from(["", "-vv", "run"]), Path::new(".")).is_ok() + zork::worker::run_zork(&CliArgs::parse_from(["", "--root", path, "-vv", "run"])).is_ok() ); - Ok(()) + Ok(tempdir.close()?) } #[cfg(target_os = "linux")] @@ -114,9 +124,6 @@ fn test_gcc_full_process() -> Result<()> { } mod local_env_tests { - use super::*; - use std::env; - /// This test allows the developers to specify a path in local environments, having the opportunity /// to debug the Zork++ source code from a concrete location. /// @@ -127,6 +134,7 @@ mod local_env_tests { /// use a debugger to figure out what our changes are doing and how are affecting the codebase. #[test] #[ignore] + #[cfg(target_os = "linux")] fn test_local_clang_full_process_manually_by_specifying_the_project_root_on_linux() { // Using env::home_dir because this test should be Unix specific // For any developer, change the path to whatever C++ project based on modules From b7a1370a1a749426d89d9055433e7ab90f3d8e72 Mon Sep 17 00:00:00 2001 From: Alex Vergara Date: Thu, 2 May 2024 13:53:55 +0200 Subject: [PATCH 55/60] feat: Separating the tests from the code quality workflows --- .github/workflows/code-quality.yml | 41 ------------------- .github/workflows/tests-clang-linux.yml | 52 +++++++++++++++++++++++++ 2 files changed, 52 insertions(+), 41 deletions(-) create mode 100644 .github/workflows/tests-clang-linux.yml diff --git a/.github/workflows/code-quality.yml b/.github/workflows/code-quality.yml index c5df2805..62219de7 100644 --- a/.github/workflows/code-quality.yml +++ b/.github/workflows/code-quality.yml @@ -43,44 +43,3 @@ jobs: run: | cd zork++ cargo fmt --all -- --check - - tests: - name: Run the tests - Clang - runs-on: ubuntu-latest - strategy: - fail-fast: false - matrix: - clang_version: [15, 16, 17, 18] - steps: - - uses: actions/checkout@v3 - - name: Caching project dependencies - id: project-cache - uses: Swatinem/rust-cache@v2 - - name: Install Clang ${{ matrix.clang_version }} - run: | - # Exit on error - set -e - # Download and execute the LLVM installation script for the specified Clang version - echo "-----> Downloading and executing the LLVM installation script for Clang ${{ matrix.clang_version }}" - wget https://apt.llvm.org/llvm.sh - chmod +x llvm.sh - sudo ./llvm.sh ${{ matrix.clang_version }} - - echo "-----> Installing libc++" - sudo apt-get install -y libc++-${{ matrix.clang_version }}-dev libc++abi-${{ matrix.clang_version }}-dev libunwind-${{ matrix.clang_version }} libunwind-${{ matrix.clang_version }}-dev libc6 libzstd1 - - # Update the symbolic link to point to the newly installed Clang version - echo "-----> Updating the symbolic link to point to Clang ${{ matrix.clang_version }}" - sudo rm -f /usr/bin/clang++ - sudo ln -s /usr/bin/clang++-${{ matrix.clang_version }} /usr/bin/clang++ - - # Display the installation directory and version of the installed Clang - echo "-----> Clang-${{ matrix.clang_version }} was installed on:" - which clang-${{ matrix.clang_version }} - echo "-----> Clang++ was installed on:" - which clang++-${{ matrix.clang_version }} - - if: ${{ matrix.clang_version < 18 }} - name: Running the tests for the project - run: | - cd zork++ - RUST_LOG=trace cargo test 2>&1 --all --color=always --no-fail-fast -- --nocapture --show-output --test-threads=1 diff --git a/.github/workflows/tests-clang-linux.yml b/.github/workflows/tests-clang-linux.yml new file mode 100644 index 00000000..74f7a35e --- /dev/null +++ b/.github/workflows/tests-clang-linux.yml @@ -0,0 +1,52 @@ +name: Clang Tests For Linux builds + +on: + push: + branches: 'main' + pull_request: + branches: '*' + +jobs: + tests: + name: Run the tests - Clang + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + clang_version: [15, 16, 17, 18] + steps: + - uses: actions/checkout@v3 + - name: Caching project dependencies + id: project-cache + uses: Swatinem/rust-cache@v2 + + - if: ${{ matrix.clang_version < 18 }} + name: Install Clang ${{ matrix.clang_version }} + run: | + # Exit on error + set -e + # Download and execute the LLVM installation script for the specified Clang version + echo "-----> Downloading and executing the LLVM installation script for Clang ${{ matrix.clang_version }}" + wget https://apt.llvm.org/llvm.sh + chmod +x llvm.sh + sudo ./llvm.sh ${{ matrix.clang_version }} + + echo "-----> Installing libc++" + sudo apt-get install -y libc++-${{ matrix.clang_version }}-dev libc++abi-${{ matrix.clang_version }}-dev libunwind-${{ matrix.clang_version }} libunwind-${{ matrix.clang_version }}-dev libc6 libzstd1 + + # Update the symbolic link to point to the newly installed Clang version + echo "-----> Updating the symbolic link to point to Clang ${{ matrix.clang_version }}" + sudo rm -f /usr/bin/clang++ + sudo ln -s /usr/bin/clang++-${{ matrix.clang_version }} /usr/bin/clang++ + + # Display the installation directory and version of the installed Clang + echo "-----> Clang-${{ matrix.clang_version }} was installed on:" + which clang-${{ matrix.clang_version }} + echo "-----> Clang++ was installed on:" + which clang++-${{ matrix.clang_version }} + + - if: ${{ matrix.clang_version < 18 }} + name: Running the tests for the project + run: | + cd zork++ + RUST_LOG=trace cargo test 2>&1 --all --color=always --no-fail-fast -- --nocapture --show-output --test-threads=1 From 1ff0c94f862924af793adfc713516b5637f877f4 Mon Sep 17 00:00:00 2001 From: Alex Vergara Date: Thu, 2 May 2024 14:21:56 +0200 Subject: [PATCH 56/60] feat: Splitting the integration tests per compiler --- .github/workflows/code-quality.yml | 7 +++++++ .github/workflows/tests-clang-linux.yml | 2 +- .github/workflows/tests-gcc.yml | 25 +++++++++++++++++++++++++ .github/workflows/tests-msvc.yml | 25 +++++++++++++++++++++++++ zork++/test/test.rs | 3 +++ 5 files changed, 61 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/tests-gcc.yml create mode 100644 .github/workflows/tests-msvc.yml diff --git a/.github/workflows/code-quality.yml b/.github/workflows/code-quality.yml index 62219de7..4de2a1e5 100644 --- a/.github/workflows/code-quality.yml +++ b/.github/workflows/code-quality.yml @@ -36,10 +36,17 @@ jobs: - name: Caching project dependencies id: project-cache uses: Swatinem/rust-cache@v2 + - uses: hecrj/setup-rust-action@v1 with: components: rustfmt + - name: Checking the format sanity of the project run: | cd zork++ cargo fmt --all -- --check + + - name: Run Unit and Doc tests + run: | + cargo test --workspace --lib --color=always --no-fail-fast + cargo test --workspace --doc --color=always --no-fail-fast diff --git a/.github/workflows/tests-clang-linux.yml b/.github/workflows/tests-clang-linux.yml index 74f7a35e..b2bf910e 100644 --- a/.github/workflows/tests-clang-linux.yml +++ b/.github/workflows/tests-clang-linux.yml @@ -49,4 +49,4 @@ jobs: name: Running the tests for the project run: | cd zork++ - RUST_LOG=trace cargo test 2>&1 --all --color=always --no-fail-fast -- --nocapture --show-output --test-threads=1 + RUST_LOG=trace cargo test 2>&1 clang --color=always --no-fail-fast -- --nocapture --show-output --test-threads=1 diff --git a/.github/workflows/tests-gcc.yml b/.github/workflows/tests-gcc.yml new file mode 100644 index 00000000..64d78d47 --- /dev/null +++ b/.github/workflows/tests-gcc.yml @@ -0,0 +1,25 @@ +name: GCC Tests For Linux builds + +on: + push: + branches: 'main' + pull_request: + branches: '*' + +jobs: + tests: + name: Run the tests - GCC + runs-on: ubuntu-latest + strategy: + fail-fast: false + steps: + - uses: actions/checkout@v3 + + - name: Caching project dependencies + id: project-cache + uses: Swatinem/rust-cache@v2 + + - name: Running the tests for the project + run: | + cd zork++ + RUST_LOG=trace cargo test 2>&1 gcc --color=always --no-fail-fast -- --nocapture --show-output --test-threads=1 diff --git a/.github/workflows/tests-msvc.yml b/.github/workflows/tests-msvc.yml new file mode 100644 index 00000000..ec3dd7fa --- /dev/null +++ b/.github/workflows/tests-msvc.yml @@ -0,0 +1,25 @@ +name: MSVC Tests For Windows builds + +on: + push: + branches: 'main' + pull_request: + branches: '*' + +jobs: + tests: + name: Run the tests - MSVC + runs-on: windows-latest + strategy: + fail-fast: false + steps: + - uses: actions/checkout@v3 + + - name: Caching project dependencies + id: project-cache + uses: Swatinem/rust-cache@v2 + + - name: Running the tests for the project + run: | + cd zork++ + RUST_LOG=trace cargo test 2>&1 msvc --color=always --no-fail-fast -- --nocapture --show-output --test-threads=1 diff --git a/zork++/test/test.rs b/zork++/test/test.rs index 97e1b9b6..a14a966b 100644 --- a/zork++/test/test.rs +++ b/zork++/test/test.rs @@ -1,3 +1,4 @@ +use std::fs; use clap::Parser; use color_eyre::Result; use tempfile::tempdir; @@ -78,6 +79,8 @@ fn test_gcc_windows_full_process() -> Result<()> { zork::worker::run_zork(&CliArgs::parse_from(["", "--root", path, "-vv", "run"])).is_ok() ); + fs::remove_dir_all("./gcm.cache")?; + Ok(tempdir.close()?) } From bae2dc5cd5cc6ed77e8e0c8c9189360458fcaa97 Mon Sep 17 00:00:00 2001 From: Alex Vergara Date: Thu, 2 May 2024 14:28:06 +0200 Subject: [PATCH 57/60] fix: Missing use statements for conditional tests --- zork++/test/test.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/zork++/test/test.rs b/zork++/test/test.rs index a14a966b..dc6a8b5f 100644 --- a/zork++/test/test.rs +++ b/zork++/test/test.rs @@ -126,7 +126,11 @@ fn test_gcc_full_process() -> Result<()> { Ok(tempdir.close()?) } + +#[cfg(target_os = "linux")] mod local_env_tests { + use super::*; + use std::env; /// This test allows the developers to specify a path in local environments, having the opportunity /// to debug the Zork++ source code from a concrete location. /// From cc4fac0622a6004746d6d39db45e0ff086d32c1f Mon Sep 17 00:00:00 2001 From: Alex Vergara Date: Thu, 2 May 2024 14:48:20 +0200 Subject: [PATCH 58/60] feat: Adding a Rust toolchain for the MSVC workflow on Windows --- .github/workflows/tests-msvc.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/tests-msvc.yml b/.github/workflows/tests-msvc.yml index ec3dd7fa..30c9127c 100644 --- a/.github/workflows/tests-msvc.yml +++ b/.github/workflows/tests-msvc.yml @@ -19,6 +19,11 @@ jobs: id: project-cache uses: Swatinem/rust-cache@v2 + - name: Setting up Rust + uses: hecrj/setup-rust-action@v1 + with: + rust-version: stable + - name: Running the tests for the project run: | cd zork++ From 27717107960ec6bd13e4658270b58219836a7256 Mon Sep 17 00:00:00 2001 From: Alex Vergara Date: Thu, 2 May 2024 14:59:32 +0200 Subject: [PATCH 59/60] feat: solving Clippy issues up to Rust 1.78.0 (29/04/2024) --- .github/workflows/code-quality.yml | 2 +- .github/workflows/tests-msvc.yml | 2 +- zork++/src/lib/cache/mod.rs | 2 +- zork++/test/test.rs | 3 +-- 4 files changed, 4 insertions(+), 5 deletions(-) diff --git a/.github/workflows/code-quality.yml b/.github/workflows/code-quality.yml index 4de2a1e5..c6fd64b3 100644 --- a/.github/workflows/code-quality.yml +++ b/.github/workflows/code-quality.yml @@ -37,7 +37,7 @@ jobs: id: project-cache uses: Swatinem/rust-cache@v2 - - uses: hecrj/setup-rust-action@v1 + - uses: hecrj/setup-rust-action@v2 with: components: rustfmt diff --git a/.github/workflows/tests-msvc.yml b/.github/workflows/tests-msvc.yml index 30c9127c..842109fd 100644 --- a/.github/workflows/tests-msvc.yml +++ b/.github/workflows/tests-msvc.yml @@ -27,4 +27,4 @@ jobs: - name: Running the tests for the project run: | cd zork++ - RUST_LOG=trace cargo test 2>&1 msvc --color=always --no-fail-fast -- --nocapture --show-output --test-threads=1 + cargo test 2>&1 msvc --color=always --no-fail-fast -- --nocapture --show-output --test-threads=1 diff --git a/zork++/src/lib/cache/mod.rs b/zork++/src/lib/cache/mod.rs index 893120e2..8a4b7024 100644 --- a/zork++/src/lib/cache/mod.rs +++ b/zork++/src/lib/cache/mod.rs @@ -204,7 +204,7 @@ impl ZorkCache { .entry(PathBuf::from(named_target)) .and_modify(|e| { if !(*e).eq(&commands_details.main.command) { - *e = commands_details.main.command.clone() + e.clone_from(&commands_details.main.command) } }) .or_insert(commands_details.main.command.clone()); diff --git a/zork++/test/test.rs b/zork++/test/test.rs index dc6a8b5f..899375a5 100644 --- a/zork++/test/test.rs +++ b/zork++/test/test.rs @@ -1,6 +1,6 @@ -use std::fs; use clap::Parser; use color_eyre::Result; +use std::fs; use tempfile::tempdir; use zork::cli::input::CliArgs; @@ -126,7 +126,6 @@ fn test_gcc_full_process() -> Result<()> { Ok(tempdir.close()?) } - #[cfg(target_os = "linux")] mod local_env_tests { use super::*; From d15a6dc319fda82f32e633e2942834f0f83c2ba0 Mon Sep 17 00:00:00 2001 From: Alex Vergara Date: Fri, 3 May 2024 14:38:07 +0200 Subject: [PATCH 60/60] feat: cleaned the commentary on the GCC tests, since GCC is already working with C++ standard modules --- zork++/test/test.rs | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/zork++/test/test.rs b/zork++/test/test.rs index 899375a5..10559d33 100644 --- a/zork++/test/test.rs +++ b/zork++/test/test.rs @@ -86,20 +86,7 @@ fn test_gcc_windows_full_process() -> Result<()> { #[cfg(target_os = "linux")] #[test] -/* -In the GitHub's virtual machines, we are still unable, due -to the gcm.cache path. - -cc1plus: fatal error: iostream: No such file or directory -compilation terminated. -In module imported at /tmp/.tmpGaFLnR/gcc_example/main.cpp:8:5: -/usr/include/c++/13.2.1/iostream: error: failed to read compiled module: No such file or directory -/usr/include/c++/13.2.1/iostream: note: compiled module file is ‘gcm.cache/./usr/include/c++/13.2.1/iostream.gcm’ -/usr/include/c++/13.2.1/iostream: note: imports must be built before being imported -/usr/include/c++/13.2.1/iostream: fatal error: returning to the gate for a mechanical issue -compilation terminated. - */ -fn test_gcc_full_process() -> Result<()> { +fn test_gcc_linux_full_process() -> Result<()> { let tempdir = tempdir()?; let path = tempdir.path().to_str().unwrap();