From aac336bc61428fed5224163c75c5b2cfeb7cbe0f Mon Sep 17 00:00:00 2001 From: Alex Vergara Date: Fri, 21 Jun 2024 18:21:28 +0200 Subject: [PATCH] feat(wip)!: Creational Flyweights to reduce the memory usage footprint and common Argument(s) that only need to be created once --- zork++/Cargo.lock | 47 ++++++++ zork++/Cargo.toml | 1 + zork++/src/lib/cache/compile_commands.rs | 9 +- zork++/src/lib/cache/mod.rs | 64 +++++++---- zork++/src/lib/cli/output/arguments.rs | 4 +- zork++/src/lib/cli/output/commands.rs | 29 +++-- zork++/src/lib/compiler/clang.rs | 0 zork++/src/lib/compiler/data_factory.rs | 110 +++++++++++++++++++ zork++/src/lib/compiler/mod.rs | 132 ++++++++++++----------- zork++/src/lib/lib.rs | 3 +- zork++/src/lib/project_model/compiler.rs | 6 +- 11 files changed, 300 insertions(+), 105 deletions(-) create mode 100644 zork++/src/lib/compiler/clang.rs create mode 100644 zork++/src/lib/compiler/data_factory.rs diff --git a/zork++/Cargo.lock b/zork++/Cargo.lock index ce34500a..37ffc494 100644 --- a/zork++/Cargo.lock +++ b/zork++/Cargo.lock @@ -388,6 +388,16 @@ dependencies = [ "log", ] +[[package]] +name = "erased-serde" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24e2389d65ab4fab27dc2a5de7b191e1f6617d1f1c8855c0dc569c94a4cbb18d" +dependencies = [ + "serde", + "typeid", +] + [[package]] name = "errno" version = "0.3.9" @@ -502,6 +512,12 @@ dependencies = [ "hashbrown", ] +[[package]] +name = "inventory" +version = "0.3.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f958d3d68f4167080a18141e10381e7634563984a537f2a49a30fd8e53ac5767" + [[package]] name = "is_terminal_polyfill" version = "1.70.0" @@ -827,6 +843,36 @@ dependencies = [ "serde", ] +[[package]] +name = "typeid" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "059d83cc991e7a42fc37bd50941885db0888e34209f8cfd9aab07ddec03bc9cf" + +[[package]] +name = "typetag" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "661d18414ec032a49ece2d56eee03636e43c4e8d577047ab334c0ba892e29aaf" +dependencies = [ + "erased-serde", + "inventory", + "once_cell", + "serde", + "typetag-impl", +] + +[[package]] +name = "typetag-impl" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac73887f47b9312552aa90ef477927ff014d63d1920ca8037c6c1951eab64bb1" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "unicode-ident" version = "1.0.12" @@ -1042,5 +1088,6 @@ dependencies = [ "serde_json", "tempfile", "toml", + "typetag", "walkdir", ] diff --git a/zork++/Cargo.toml b/zork++/Cargo.toml index 2b599124..bb01a3b9 100644 --- a/zork++/Cargo.toml +++ b/zork++/Cargo.toml @@ -18,6 +18,7 @@ path = "src/bin/main.rs" toml = "0.5.11" glob = "0.3.1" serde = { version = "1.0.202", features = ["derive"] } +typetag = "0.2" clap = { version = "4.0.32", features = ["derive"] } log = "0.4.17" env_logger = "0.11.3" diff --git a/zork++/src/lib/cache/compile_commands.rs b/zork++/src/lib/cache/compile_commands.rs index e2fd881e..0e2a189e 100644 --- a/zork++/src/lib/cache/compile_commands.rs +++ b/zork++/src/lib/cache/compile_commands.rs @@ -13,9 +13,11 @@ pub type CompileCommands = Vec; /// 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 { +pub(crate) fn map_generated_commands_to_compilation_db( + cache: &ZorkCache, +) -> Result { log::trace!("Generating the compilation database..."); - + let generated_commands = cache.get_all_commands_iter(); let mut compilation_db_entries: Vec = Vec::with_capacity(cache.count_total_generated_commands()); @@ -30,9 +32,10 @@ pub(crate) fn map_generated_commands_to_compilation_db(cache: &ZorkCache) -> Res 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")?; - + Ok(compilation_db_entries) } diff --git a/zork++/src/lib/cache/mod.rs b/zork++/src/lib/cache/mod.rs index b1b01307..319c9e76 100644 --- a/zork++/src/lib/cache/mod.rs +++ b/zork++/src/lib/cache/mod.rs @@ -13,6 +13,9 @@ use std::{ path::{Path, PathBuf}, }; +use crate::bounds::TranslationUnit; +use crate::cache::compile_commands::CompileCommands; +use crate::project_model::modules::{ModuleImplementationModel, ModuleInterfaceModel}; use crate::project_model::sourceset::SourceFile; use crate::{ cli::{ @@ -27,9 +30,6 @@ use crate::{ }; use serde::{Deserialize, Serialize}; use walkdir::WalkDir; -use crate::bounds::TranslationUnit; -use crate::cache::compile_commands::CompileCommands; -use crate::project_model::modules::{ModuleImplementationModel, ModuleInterfaceModel}; /// Standalone utility for load from the file system the Zork++ cache file /// for the target [`CppCompiler`] @@ -87,7 +87,7 @@ pub fn save( .with_context(move || "Error saving data to the Zork++ cache") } -#[derive(Serialize, Deserialize, Debug, Default, Clone)] +#[derive(Serialize, Deserialize, Default)] pub struct ZorkCache { pub compiler: CppCompiler, pub last_program_execution: DateTime, @@ -96,19 +96,35 @@ pub struct ZorkCache { } impl ZorkCache { + pub fn new() -> Self { + Self { + compiler: todo!(), + last_program_execution: todo!(), + compilers_metadata: todo!(), + generated_commands: todo!(), + } + } pub fn last_program_execution(&self) -> &DateTime { &self.last_program_execution } - pub fn get_module_ifc_cmd(&self, module_interface_model: &ModuleInterfaceModel) -> Option<&SourceCommandLine>{ - self.generated_commands.interfaces.iter().find(|mi| - module_interface_model.file() == (*mi).path() - ) + pub fn get_module_ifc_cmd( + &self, + module_interface_model: &ModuleInterfaceModel, + ) -> Option<&SourceCommandLine> { + self.generated_commands + .interfaces + .iter() + .find(|mi| module_interface_model.file() == (*mi).path()) } - pub fn get_module_impl_cmd(&self, module_impl_model: &ModuleImplementationModel) -> Option<&SourceCommandLine>{ - self.generated_commands.implementations.iter().find(|mi| - module_impl_model.file() == (*mi).path() - ) + pub fn get_module_impl_cmd( + &self, + module_impl_model: &ModuleImplementationModel, + ) -> Option<&SourceCommandLine> { + self.generated_commands + .implementations + .iter() + .find(|mi| module_impl_model.file() == (*mi).path()) } // pub fn get_source_cmd(&self, module_impl_model: &Source) -> Option<&SourceCommandLine>{ @@ -116,7 +132,7 @@ impl ZorkCache { // module_impl_model.file() == (*mi).path() // ) // } - + /// Returns a [`Option`] of [`CommandDetails`] if the file is persisted already in the cache pub fn is_file_cached(&self, _path: impl AsRef) -> Option<&CommandDetail> { // let last_iteration_details = self.generated_commands.last(); @@ -161,9 +177,11 @@ impl ZorkCache { // { // compile_commands::map_generated_commands_to_compilation_db(self)?; // } - // - if let Some(_new_commands) = self.save_generated_commands(commands, program_data, test_mode) { - if program_data.project.compilation_db { // TODO:: pass the new commands + // + if let Some(_new_commands) = self.save_generated_commands(commands, program_data, test_mode) + { + if program_data.project.compilation_db { + // TODO:: pass the new commands compile_commands::map_generated_commands_to_compilation_db(self)?; } } @@ -199,10 +217,10 @@ impl ZorkCache { // } else { // 1 // }; - + // Generating the compilation database if enabled, and some file has been added, modified // or comes from a previous failure status - + // TODO: oh, fk, I get it finally. We should only regenerate the compilation database if // the generated command line has changed! (which is highly unlikely) // TODO: Create a wrapper enumerated over the Vec, so that we can store in the @@ -213,9 +231,11 @@ impl ZorkCache { // let at_least_one_changed = commands. self.generated_commands = commands; - self.get_all_commands_iter()// TODO: Review the conditions and ensure that are the ones that we're looking for - .any(|cmd| cmd.need_to_build || cmd.execution_result.eq(&CommandExecutionResult::Success)); - + self.get_all_commands_iter() // TODO: Review the conditions and ensure that are the ones that we're looking for + .any(|cmd| { + cmd.need_to_build || cmd.execution_result.eq(&CommandExecutionResult::Success) + }); + // INSTEAD OF THIS, we just can return an Optional with the compilation database, so we can serialize the args in the compile_commands.json // format and then join them in a one-liner string, so they're easy to read and/or copy None @@ -260,7 +280,7 @@ impl ZorkCache { // TODO: read_only_iterator (better name) and docs pls pub fn get_all_commands_iter(&self) -> impl Iterator + Debug + '_ { let generated_commands = &self.generated_commands; - + generated_commands .pre_tasks .iter() diff --git a/zork++/src/lib/cli/output/arguments.rs b/zork++/src/lib/cli/output/arguments.rs index a8b6981c..cc542817 100644 --- a/zork++/src/lib/cli/output/arguments.rs +++ b/zork++/src/lib/cli/output/arguments.rs @@ -8,7 +8,7 @@ use std::{borrow::Borrow, ffi::OsStr, path::PathBuf}; use serde::{Deserialize, Serialize}; /// Wrapper type for represent and storing a command line argument -#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +#[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize)] pub struct Argument(String); impl Argument { @@ -144,7 +144,7 @@ pub mod clang_args { // The Windows variant is a Zork++ feature to allow the users to write `import std;` // under -std=c++20 with clang linking against GCC with // some MinGW installation or similar - pub(crate) fn implicit_module_maps(out_dir: &Path) -> Argument { + pub(crate) fn implicit_module_map(out_dir: &Path) -> Argument { if std::env::consts::OS.eq("windows") { Argument::from(format!( "-fmodule-map-file={}", diff --git a/zork++/src/lib/cli/output/commands.rs b/zork++/src/lib/cli/output/commands.rs index f3aa3f84..2b1357cd 100644 --- a/zork++/src/lib/cli/output/commands.rs +++ b/zork++/src/lib/cli/output/commands.rs @@ -1,3 +1,6 @@ +//! Contains helpers and data structures to be processed in a nice and neat way the commands generated to be executed +//! by Zork++ + use std::collections::HashMap; use std::fmt::Debug; use std::slice::Iter; @@ -8,9 +11,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++ +use crate::compiler::data_factory::{CommonArgs, CompilerCommonArguments}; use crate::{ cache::{self, ZorkCache}, project_model::{compiler::CppCompiler, ZorkModel}, @@ -175,9 +176,9 @@ impl SourceCommandLine { } pub fn for_translation_unit( - // TODO init it as a args holder, but doesn't have the status yet - tu: impl TranslationUnit, - args: Arguments + // TODO init it as a args holder, but doesn't have the status yet + tu: impl TranslationUnit, + args: Arguments, ) -> Self { Self { directory: tu.path(), @@ -216,11 +217,15 @@ impl LinkerCommandLine { } /// Holds the generated command line arguments for a concrete compiler -#[derive(Debug, Clone, Serialize, Deserialize, Default)] +#[derive(Serialize, Deserialize, Default)] pub struct Commands { pub compiler: CppCompiler, pub pre_tasks: Vec, // TODO: since there's no really pre-tasks (only build the std_lib), create named entries for std and std.compat pub system_modules: HashMap, + + pub general_args: CommonArgs, + pub compiler_common_args: Box, + pub interfaces: Vec, pub implementations: Vec, pub sources: Vec, @@ -228,12 +233,16 @@ pub struct Commands { } impl Commands { - pub fn new(compiler: CppCompiler) -> Self { + pub fn new(model: &ZorkModel<'_>, general_args: CommonArgs, compiler_specific_common_args: Box) -> Self { Self { // TODO: try to see if its possible to move around the code and have a From, avoiding default initialization, - // since this will always cause reallocations, and from may be able to allocate at the exact required capacity + // since this will always cause reallocations, and 'from' may be able to allocate at the exact required capacity // of every collection - compiler, + compiler: model.compiler.cpp_compiler, + + general_args, + compiler_common_args: compiler_specific_common_args, + pre_tasks: Vec::with_capacity(0), system_modules: HashMap::with_capacity(0), interfaces: Vec::with_capacity(0), diff --git a/zork++/src/lib/compiler/clang.rs b/zork++/src/lib/compiler/clang.rs new file mode 100644 index 00000000..e69de29b diff --git a/zork++/src/lib/compiler/data_factory.rs b/zork++/src/lib/compiler/data_factory.rs new file mode 100644 index 00000000..a4c076d5 --- /dev/null +++ b/zork++/src/lib/compiler/data_factory.rs @@ -0,0 +1,110 @@ +//! Stores Flyweight data structures that allow to reduce n-plications of arguments for every +//! translation unit, having shared data without replicating it until the final command line must +//! be generated in order to be stored (in cache) and executed (in the underlying shell) + +use std::path::{Path, PathBuf}; + +use serde::{Deserialize, Serialize}; + +use crate::{ + cli::output::arguments::{clang_args, Argument, Arguments}, + project_model::compiler::{CppCompiler, LanguageLevel, StdLib}, + project_model::ZorkModel, +}; + +/// Holds the common arguments across all the different command lines regarding the target compiler +#[derive(Serialize, Deserialize, Debug, Clone, Default)] +pub struct CommonArgs { + compiler: CppCompiler, + out_dir: PathBuf, + // out_dir: &'a PathBuf, + language_level: LanguageLevel, + extra_args: Arguments, + // extra_args: &'a [Argument], +} + +impl<'a> From<&'a ZorkModel<'_>> for CommonArgs { + fn from(model: &'a ZorkModel<'_>) -> Self { + let compiler = model.compiler.cpp_compiler; + let out_dir = model.build.output_dir.clone(); + let language_level = model.compiler.cpp_standard; + let extra_args = model.compiler.extra_args.clone(); + + Self { + compiler, + out_dir, + language_level, + extra_args: Arguments::from_vec(extra_args), + } + } +} + +// TODO: the specific ones, like the object file... can we just create a prototype +// function + +/// Allows to have a common interface for any type that represents a data structure which its +/// purpose is to hold common [`Argument`] across the diferent kind of [`TranslationUnit`] +#[typetag::serde(tag = "type")] +pub trait CompilerCommonArguments {} +impl Default for Box { + fn default() -> Self { + Box::new(ClangCommonArgs::default()) // TODO: isn't this a code smell? + } +} +#[typetag::serde] +impl CompilerCommonArguments for ClangCommonArgs {} +#[typetag::serde] +impl CompilerCommonArguments for MsvcCommonArgs {} +#[typetag::serde] +impl CompilerCommonArguments for GccCommonArgs {} + +#[derive(Serialize, Deserialize, Default, Debug, Clone)] +pub struct ClangCommonArgs { + // TODO: a HashMap per kind of translation unit which stores the common ones + // for every different kind of translation unit + std_lib: Option, // TODO: technically, should this already be an arg? or should we decouple the + // project model for the Argument(s) type(s)? + implicit_modules: &'static str, + implicit_module_map: Argument, + prebuilt_module_path: Argument, +} +impl ClangCommonArgs { + pub fn new(model: &ZorkModel<'_>) -> Self { + let out_dir: &Path = model.build.output_dir.as_ref(); + + Self { + std_lib: model.compiler.std_lib, // TODO: Argument::from(std_lib) + implicit_modules: "-fimplicit-modules", + implicit_module_map: clang_args::implicit_module_map(out_dir), + prebuilt_module_path: + Argument::from(format!( + "-fprebuilt-module-path={}/clang/modules/interfaces", + out_dir.display() + )) + } + } +} + +#[derive(Serialize, Deserialize, Default, Debug, Clone)] +pub struct MsvcCommonArgs { + exception_handling_model: &'static str, + no_logo: &'static str, + no_compile: &'static str, // TODO: should be in the general and pass in the model? + // ref_stdlib: &'static str, // TODO: this are tecnically two args, /reference and the value + // ref_stdlib_compat: &'static str, // TODO: this are tecnically two args, /reference and the value + // TODO: split the dual cases per switches + // TODO: can we have switches like tuples? like switch-value pairs? +} +impl MsvcCommonArgs { + pub fn new() -> Self { + Self { exception_handling_model: "/EHsc", no_logo: "nologo", no_compile: "/c", } + } +} + +#[derive(Serialize, Deserialize, Default, Debug, Clone)] +pub struct GccCommonArgs {} +impl GccCommonArgs { + pub fn new() -> Self { + Self {} + } +} diff --git a/zork++/src/lib/compiler/mod.rs b/zork++/src/lib/compiler/mod.rs index 4817f149..52905b6c 100644 --- a/zork++/src/lib/compiler/mod.rs +++ b/zork++/src/lib/compiler/mod.rs @@ -1,11 +1,14 @@ //! 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. +//! generate command lines and execute them in a shell of the current +//! operating system against the designed compilers in the configuration +//! file. + +pub mod data_factory; use color_eyre::Result; use std::path::Path; + use crate::bounds::{ExecutableTarget, ExtraArgs, TranslationUnit}; use crate::cli::output::arguments::{clang_args, msvc_args, Arguments}; use crate::cli::output::commands::{CommandExecutionResult, SourceCommandLine}; @@ -22,6 +25,10 @@ use crate::{ }, }; +use self::data_factory::{ + ClangCommonArgs, CommonArgs, CompilerCommonArguments, GccCommonArgs, MsvcCommonArgs, +}; + /// The entry point of the compilation process /// /// Whenever this process gets triggered, the files declared within the @@ -31,19 +38,26 @@ pub fn build_project<'a>( cache: &mut ZorkCache, tests: bool, ) -> Result { + // Generate the Flyweight struct, with the repetitive data and details of the command lines + let general_args = CommonArgs::from(model); + let compiler_specific_common_args: Box = + compiler_common_arguments_factory(model); + // A registry of the generated command lines - let mut commands = Commands::new(model.compiler.cpp_compiler); + let mut commands = Commands::new(model, general_args, compiler_specific_common_args); // TODO from cache, and find them here instead from the cache // Pre-tasks if model.compiler.cpp_compiler == CppCompiler::GCC && !model.modules.sys_modules.is_empty() { helpers::build_sys_modules(model, &mut commands, cache) } + + // TODO: add them to the commands DS, so they are together until they're generated // Build the std library as a module build_modular_stdlib(model, cache, &mut commands); // TODO: ward it with an if for only call this fn for the // 1st - Build the modules - build_modules(model, cache, &mut commands)?; + process_modules(model, cache, &mut commands)?; // 2nd - Build the non module sources build_sources(model, cache, &mut commands, tests)?; // 3rd - Build the executable or the tests @@ -52,6 +66,17 @@ pub fn build_project<'a>( Ok(commands) } +/// Factory function for bring the data structure that holds the common arguments of a source +/// command line for every translation unit, regardeless the underlying choosen compiler +fn compiler_common_arguments_factory(model: &ZorkModel<'_>) -> Box { + // TODO: consider having a union (enum) instead of a fat ptr + match model.compiler.cpp_compiler { + CppCompiler::CLANG => Box::new(ClangCommonArgs::new(model)), + CppCompiler::MSVC => Box::new(MsvcCommonArgs::new()), + CppCompiler::GCC => Box::new(GccCommonArgs::new()), + } +} + /// Builds the C++ standard library as a pre-step acording to the specification /// of each compiler vendor fn build_modular_stdlib(model: &ZorkModel<'_>, cache: &mut ZorkCache, commands: &mut Commands) { @@ -77,30 +102,27 @@ fn build_modular_stdlib(model: &ZorkModel<'_>, cache: &mut ZorkCache, commands: .to_string_lossy() .to_string(), args: Arguments::default(), - need_to_build: true, + need_to_build: false, execution_result: CommandExecutionResult::Cached, }; source_command_line }; commands.pre_tasks.push(cpp_stdlib); - let built_stdlib_path = &cache.compilers_metadata.msvc.stdlib_bmi_path; + let built_stdlib_compat_path = &cache.compilers_metadata.msvc.c_stdlib_bmi_path; let c_cpp_stdlib = if !built_stdlib_path.exists() { - log::trace!( - "Building the {:?} C ISO standard library implementation", - compiler - ); + log::trace!("Building the {:?} C compat CPP std lib", compiler); msvc_args::generate_std_cmd(model, cache, StdLibMode::CCompat) } else { let source_command_line = SourceCommandLine { - directory: built_stdlib_path.file_stem().unwrap().into(), - filename: built_stdlib_path + directory: built_stdlib_compat_path.file_stem().unwrap().into(), + filename: built_stdlib_compat_path .file_name() .unwrap() .to_string_lossy() .to_string(), args: Arguments::default(), - need_to_build: true, + need_to_build: false, execution_result: CommandExecutionResult::Cached, }; source_command_line @@ -162,17 +184,8 @@ fn build_sources( /// /// This function acts like a operation result processor, by running instances /// and parsing the obtained result, handling the flux according to the -/// compiler responses> -/// -/// TODO: all this fns should be conceptually named and used for something like -> generate_commands_for -fn build_modules(model: &ZorkModel, cache: &mut ZorkCache, commands: &mut Commands) -> Result<()> { - - // TODO: siguiente actualización. En realidad, creo que la idea legendaria no es nada de lo de arriba exactamente. - // Por ejemplo, y si buildeamos en una data-structure un prototype para cada uno de ellos? Así, además de usar el patrón de diseño - // prototype, lo único que hacemos es inyectarle partes concretas, como -> añade los extra args (si son nuevos) y podríamos hacer - // el check todas las veces por cada mínima parte, pero el prototipo podía estar cacheado y solo inyectarle cambios? - // EJ: module_interface_prototype(cpp_compiler...) y puede ser una puta clase en si mismo, con cada cosa detallada, en vez de un - // vector con todo a palo seco (INCLUSO UNA CLASE BUILDER :D) +/// compiler responses +fn process_modules(model: &ZorkModel, cache: &mut ZorkCache, commands: &mut Commands) -> Result<()> { log::info!("Generating the commands for the module interfaces and partitions..."); process_module_interfaces(model, cache, &model.modules.interfaces, commands); @@ -209,6 +222,7 @@ fn process_module_interfaces<'a>( sources::generate_module_interface_cmd(model, cache, module_interface, commands) }; + // TODO: should we get rid of commands and just store everything on the Cache? commands.interfaces.push(command_line); // commands.linker.add_owned_buildable_at(helpers::generate_prebuilt_miu( // model.compiler.cpp_compiler, &model.build.output_dir, module_interface @@ -216,10 +230,7 @@ fn process_module_interfaces<'a>( }); } - - -/// Parses the configuration in order to compile the module implementation -/// translation units declared for the project +/// Generates the commands for every [`ModuleImplementationModel`] fn process_module_implementations<'a>( model: &'a ZorkModel, cache: &ZorkCache, @@ -245,7 +256,7 @@ fn process_module_implementations<'a>( } else { sources::generate_module_implementation_cmd(model, cache, module_impl, commands) }; - + commands.interfaces.push(command_line); }); } @@ -260,7 +271,7 @@ pub fn generate_main_command_line_args<'a>( log::info!("Generating the main command line..."); let compiler = &model.compiler.cpp_compiler; - let out_dir = model.build.output_dir.as_ref(); + let out_dir: &Path = model.build.output_dir.as_ref(); let executable_name = target.name(); let mut arguments = Arguments::default(); @@ -272,7 +283,7 @@ pub fn generate_main_command_line_args<'a>( 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.push(clang_args::implicit_module_map(out_dir)); arguments.create_and_push(format!( "-fprebuilt-module-path={}", @@ -337,6 +348,8 @@ pub fn generate_main_command_line_args<'a>( commands.linker.args.extend(arguments); commands.linker.built_files = target // TODO: built_files means raw cpp sources + // TODO: add a custom collector on the mod sources + // TODO: just name the field 'sources' .sourceset() .sources .iter() @@ -348,6 +361,8 @@ pub fn generate_main_command_line_args<'a>( /// Specific operations over source files mod sources { + use std::path::Path; + use super::helpers; use crate::bounds::ExtraArgs; use crate::cache::ZorkCache; @@ -391,7 +406,7 @@ mod sources { 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.push(clang_args::implicit_module_map(out_dir)); arguments.push(clang_args::add_prebuilt_module_path(compiler, out_dir)); arguments.create_and_push("-o"); } @@ -447,25 +462,22 @@ mod sources { commands: &mut Commands, ) -> SourceCommandLine { let compiler = model.compiler.cpp_compiler; - let out_dir = model.build.output_dir.as_ref(); + let out_dir: &Path = model.build.output_dir.as_ref(); 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 => { - arguments.push_opt(model.compiler.stdlib_arg()); - arguments.create_and_push("-fimplicit-modules"); + // 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.create_and_push(format!( + // arguments.push(clang_args::implicit_module_map(out_dir)); + /* arguments.create_and_push(format!( "-fprebuilt-module-path={}/clang/modules/interfaces", out_dir.display() - )); + )); */ clang_args::add_direct_module_interfaces_dependencies( &interface.dependencies, compiler, @@ -539,15 +551,7 @@ mod sources { } } - let command_line = SourceCommandLine::from_translation_unit( - interface, - arguments, - false, - CommandExecutionResult::default(), - ); - // commands.interfaces.push(command_line); // TODO move commands out of here and later just add the linker - - command_line + SourceCommandLine::for_translation_unit(interface, arguments) } /// Generates the expected arguments for compile the implementation module files @@ -570,7 +574,7 @@ mod sources { 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)); + arguments.push(clang_args::implicit_module_map(out_dir)); // The resultant object file arguments.create_and_push("-o"); @@ -639,10 +643,7 @@ mod sources { } } - let command_line = SourceCommandLine::for_translation_unit( - implementation, - arguments - ); + let command_line = SourceCommandLine::for_translation_unit(implementation, arguments); // commands.implementations.push(command_line); command_line @@ -805,11 +806,13 @@ mod helpers { /// hasn't been modified since the last build process iteration. /// /// True means 'already processed with previous iteration: Success' and stored on the cache - pub(crate) fn flag_source_file_without_changes( // TODO: kind of `look_for_tu_on_cache_or_generate_command + pub(crate) fn flag_source_file_without_changes( + // TODO: kind of `look_for_tu_on_cache_or_generate_command compiler: &CppCompiler, cache: &ZorkCache, file: &Path, - ) -> bool { // TODO: it should return an Optional with the SourceCommandLine from the cache if its already there + ) -> bool { + // TODO: it should return an Optional with the SourceCommandLine from the cache if its already there if compiler.eq(&CppCompiler::CLANG) && cfg!(target_os = "windows") { // TODO: Review this // with the new Clang @@ -852,16 +855,17 @@ mod helpers { } /// TODO - pub(crate) fn translation_unit_must_be_rebuilt(compiler: CppCompiler, - last_process_execution: &DateTime, - cached_source_cmd: &SourceCommandLine, - file: &Path, -) -> bool { + pub(crate) fn translation_unit_must_be_rebuilt( + compiler: CppCompiler, + last_process_execution: &DateTime, + cached_source_cmd: &SourceCommandLine, + file: &Path, + ) -> bool { if compiler.eq(&CppCompiler::CLANG) && cfg!(target_os = "windows") { log::trace!("Module unit {:?} will be rebuilt since we've detected that you are using Clang in Windows", cached_source_cmd.path()); return true; } - + let execution_result = cached_source_cmd.execution_result; if execution_result != CommandExecutionResult::Success && execution_result != CommandExecutionResult::Cached diff --git a/zork++/src/lib/lib.rs b/zork++/src/lib/lib.rs index a8e2954c..bc27e26c 100644 --- a/zork++/src/lib/lib.rs +++ b/zork++/src/lib/lib.rs @@ -108,7 +108,8 @@ pub mod worker { cli_args: &'a CliArgs, program_data: &'a ZorkModel<'_>, mut cache: ZorkCache, - ) -> Result { // TODO: the return type isn't as clever as it could be + ) -> Result { + // TODO: the return type isn't as clever as it could be let commands: Commands; match cli_args.command { diff --git a/zork++/src/lib/project_model/compiler.rs b/zork++/src/lib/project_model/compiler.rs index 23826a20..94e59d77 100644 --- a/zork++/src/lib/project_model/compiler.rs +++ b/zork++/src/lib/project_model/compiler.rs @@ -100,10 +100,10 @@ impl CppCompiler { } } -#[derive(Debug, PartialEq, Eq)] +#[derive(Serialize, Deserialize, Default, Debug, PartialEq, Eq, Clone, Copy)] pub enum LanguageLevel { CPP20, - CPP23, + #[default] CPP23, CPP2A, CPP2B, LATEST, @@ -127,7 +127,7 @@ impl AsRef for LanguageLevel { } } -#[derive(Debug, PartialEq, Eq)] +#[derive(Debug, PartialEq, Eq, Clone, Copy, Serialize, Deserialize)] pub enum StdLib { STDLIBCPP, LIBCPP,