diff --git a/Cargo.lock b/Cargo.lock index 2bec9e7..44d04f3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -211,6 +211,16 @@ dependencies = [ "clap_derive", ] +[[package]] +name = "clap-verbosity-flag" +version = "2.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e099138e1807662ff75e2cebe4ae2287add879245574489f9b1588eb5e5564ed" +dependencies = [ + "clap 4.5.4", + "log", +] + [[package]] name = "clap_builder" version = "4.5.2" @@ -926,6 +936,7 @@ dependencies = [ "ahash", "blake3", "clap 4.5.4", + "clap-verbosity-flag", "console", "convert_case", "criterion", diff --git a/Cargo.toml b/Cargo.toml index a17f55d..6639e55 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,10 +11,7 @@ serde_json = { version = "1.0.93" } ahash = "0.8.3" convert_case = "0.6.0" rayon = "1.6.1" -log = { version = "0.4.17", features = [ - "max_level_debug", - "release_max_level_warn", -] } +log = { version = "0.4.17" } env_logger = "0.10.0" indicatif = "0.17.3" console = "0.15.5" @@ -26,6 +23,7 @@ futures-timer = "3.0.2" clap = { version = "4.3.17", features = ["derive"] } sysinfo = "0.29.10" ctrlc = "3.4.4" +clap-verbosity-flag = "2.2.2" [profile.release] diff --git a/README.md b/README.md index 67ce418..f13787f 100644 --- a/README.md +++ b/README.md @@ -65,6 +65,12 @@ Options: -n, --no-timing [possible values: true, false] + -v, --verbose... + Increase logging verbosity + + -q, --quiet... + Decrease logging verbosity + -c, --create-sourcedirs This creates a source_dirs.json file at the root of the monorepo, which is needed when you want to use Reanalyze @@ -84,6 +90,7 @@ Options: -V, --version Print version + ``` # Contributing diff --git a/src/build.rs b/src/build.rs index aece691..e61cfec 100644 --- a/src/build.rs +++ b/src/build.rs @@ -16,6 +16,7 @@ use ahash::AHashSet; use build_types::*; use console::style; use indicatif::{ProgressBar, ProgressStyle}; +use log::log_enabled; use serde::Serialize; use std::fmt; use std::fs::File; @@ -137,6 +138,7 @@ impl fmt::Display for InitializeBuildError { pub fn initialize_build( default_timing: Option, filter: &Option, + show_progress: bool, path: &str, bsc_path: Option, ) -> Result { @@ -149,21 +151,26 @@ pub fn initialize_build( let root_config_name = packages::get_package_name(&project_root); let rescript_version = helpers::get_rescript_version(&bsc_path); - print!("{}{}Building package tree...", style("[1/7]").bold().dim(), TREE); - let _ = stdout().flush(); + if show_progress { + println!("{} {}Building package tree...", style("[1/7]").bold().dim(), TREE); + let _ = stdout().flush(); + } + let timing_package_tree = Instant::now(); let packages = packages::make(filter, &project_root, &workspace_root); let timing_package_tree_elapsed = timing_package_tree.elapsed(); - println!( - "{}{} {}Built package tree in {:.2}s", - LINE_CLEAR, - style("[1/7]").bold().dim(), - TREE, - default_timing - .unwrap_or(timing_package_tree_elapsed) - .as_secs_f64() - ); + if show_progress { + println!( + "{}{} {}Built package tree in {:.2}s", + LINE_CLEAR, + style("[1/7]").bold().dim(), + TREE, + default_timing + .unwrap_or(timing_package_tree_elapsed) + .as_secs_f64() + ); + } if !packages::validate_packages_dependencies(&packages) { return Err(InitializeBuildError::PackageDependencyValidation); @@ -171,12 +178,15 @@ pub fn initialize_build( let timing_source_files = Instant::now(); - print!( - "{} {}Finding source files...", - style("[2/7]").bold().dim(), - LOOKING_GLASS - ); - let _ = stdout().flush(); + if show_progress { + println!( + "{} {}Finding source files...", + style("[2/7]").bold().dim(), + LOOKING_GLASS + ); + let _ = stdout().flush(); + } + let mut build_state = BuildState::new( project_root, root_config_name, @@ -187,52 +197,62 @@ pub fn initialize_build( ); packages::parse_packages(&mut build_state); let timing_source_files_elapsed = timing_source_files.elapsed(); - println!( - "{}{} {}Found source files in {:.2}s", - LINE_CLEAR, - style("[2/7]").bold().dim(), - LOOKING_GLASS, - default_timing - .unwrap_or(timing_source_files_elapsed) - .as_secs_f64() - ); - print!( - "{} {}Reading compile state...", - style("[3/7]").bold().dim(), - COMPILE_STATE - ); - let _ = stdout().flush(); + if show_progress { + println!( + "{}{} {}Found source files in {:.2}s", + LINE_CLEAR, + style("[2/7]").bold().dim(), + LOOKING_GLASS, + default_timing + .unwrap_or(timing_source_files_elapsed) + .as_secs_f64() + ); + + println!( + "{} {}Reading compile state...", + style("[3/7]").bold().dim(), + COMPILE_STATE + ); + let _ = stdout().flush(); + } let timing_compile_state = Instant::now(); let compile_assets_state = read_compile_state::read(&mut build_state); let timing_compile_state_elapsed = timing_compile_state.elapsed(); - println!( - "{}{} {}Read compile state {:.2}s", - LINE_CLEAR, - style("[3/7]").bold().dim(), - COMPILE_STATE, - default_timing - .unwrap_or(timing_compile_state_elapsed) - .as_secs_f64() - ); - print!( - "{} {}Cleaning up previous build...", - style("[4/7]").bold().dim(), - SWEEP - ); + if show_progress { + println!( + "{}{} {}Read compile state {:.2}s", + LINE_CLEAR, + style("[3/7]").bold().dim(), + COMPILE_STATE, + default_timing + .unwrap_or(timing_compile_state_elapsed) + .as_secs_f64() + ); + + println!( + "{} {}Cleaning up previous build...", + style("[4/7]").bold().dim(), + SWEEP + ); + } let timing_cleanup = Instant::now(); let (diff_cleanup, total_cleanup) = clean::cleanup_previous_build(&mut build_state, compile_assets_state); let timing_cleanup_elapsed = timing_cleanup.elapsed(); - println!( - "{}{} {}Cleaned {}/{} {:.2}s", - LINE_CLEAR, - style("[4/7]").bold().dim(), - SWEEP, - diff_cleanup, - total_cleanup, - default_timing.unwrap_or(timing_cleanup_elapsed).as_secs_f64() - ); + + if show_progress { + println!( + "{}{} {}Cleaned {}/{} {:.2}s", + LINE_CLEAR, + style("[4/7]").bold().dim(), + SWEEP, + diff_cleanup, + total_cleanup, + default_timing.unwrap_or(timing_cleanup_elapsed).as_secs_f64() + ); + } + Ok(build_state) } @@ -259,12 +279,17 @@ pub fn incremental_build( build_state: &mut BuildState, default_timing: Option, initial_build: bool, + show_progress: bool, only_incremental: bool, create_sourcedirs: bool, ) -> Result<(), IncrementalBuildError> { logs::initialize(&build_state.packages); let num_dirty_modules = build_state.modules.values().filter(|m| is_dirty(m)).count() as u64; - let pb = ProgressBar::new(num_dirty_modules); + let pb = if show_progress { + ProgressBar::new(num_dirty_modules) + } else { + ProgressBar::hidden() + }; let mut current_step = if only_incremental { 1 } else { 5 }; let total_steps = if only_incremental { 3 } else { 7 }; pb.set_style( @@ -281,27 +306,31 @@ pub fn incremental_build( let timing_ast_elapsed = timing_ast.elapsed(); match result_asts { - Ok(err) => { - println!( - "{}{} {}Parsed {} source files in {:.2}s", - LINE_CLEAR, - format_step(current_step, total_steps), - CODE, - num_dirty_modules, - default_timing.unwrap_or(timing_ast_elapsed).as_secs_f64() - ); - print!("{}", &err); + Ok(_ast) => { + if show_progress { + println!( + "{}{} {}Parsed {} source files in {:.2}s", + LINE_CLEAR, + format_step(current_step, total_steps), + CODE, + num_dirty_modules, + default_timing.unwrap_or(timing_ast_elapsed).as_secs_f64() + ); + } } Err(err) => { logs::finalize(&build_state.packages); - println!( - "{}{} {}Error parsing source files in {:.2}s", - LINE_CLEAR, - format_step(current_step, total_steps), - CROSS, - default_timing.unwrap_or(timing_ast_elapsed).as_secs_f64() - ); - print!("{}", &err); + if show_progress { + println!( + "{}{} {}Error parsing source files in {:.2}s", + LINE_CLEAR, + format_step(current_step, total_steps), + CROSS, + default_timing.unwrap_or(timing_ast_elapsed).as_secs_f64() + ); + } + + log::error!("Could not parse source files: {}", &err); return Err(IncrementalBuildError::SourceFileParseError); } } @@ -310,13 +339,15 @@ pub fn incremental_build( let timing_deps_elapsed = timing_deps.elapsed(); current_step += 1; - println!( - "{}{} {}Collected deps in {:.2}s", - LINE_CLEAR, - format_step(current_step, total_steps), - DEPS, - default_timing.unwrap_or(timing_deps_elapsed).as_secs_f64() - ); + if show_progress { + println!( + "{}{} {}Collected deps in {:.2}s", + LINE_CLEAR, + format_step(current_step, total_steps), + DEPS, + default_timing.unwrap_or(timing_deps_elapsed).as_secs_f64() + ); + } // track the compile dirty state, we reset it when the compile fails let mut tracked_dirty_modules = AHashSet::new(); @@ -332,45 +363,46 @@ pub fn incremental_build( mark_modules_with_deleted_deps_dirty(build_state); current_step += 1; - // print all the compile_dirty modules - // for (module_name, module) in build_state.modules.iter() { - // if module.compile_dirty { - // println!("compile dirty: {}", module_name); - // } - // } + //print all the compile_dirty modules + if log_enabled!(log::Level::Trace) { + for (module_name, module) in build_state.modules.iter() { + if module.compile_dirty { + println!("compile dirty: {}", module_name); + } + } + }; let start_compiling = Instant::now(); - let pb = ProgressBar::new(build_state.modules.len().try_into().unwrap()); - pb.set_style( - ProgressStyle::with_template(&format!( - "{} {}Compiling... {{spinner}} {{pos}}/{{len}} {{msg}}", - format_step(current_step, total_steps), - SWORDS - )) - .unwrap(), - ); + let pb = if show_progress { + ProgressBar::new(build_state.modules.len().try_into().unwrap()) + } else { + ProgressBar::hidden() + }; + let (compile_errors, compile_warnings, num_compiled_modules) = compile::compile(build_state, || pb.inc(1), |size| pb.set_length(size)); let compile_duration = start_compiling.elapsed(); logs::finalize(&build_state.packages); if create_sourcedirs { - sourcedirs::print(&build_state); + sourcedirs::print(build_state); } pb.finish(); if !compile_errors.is_empty() { if helpers::contains_ascii_characters(&compile_warnings) { - println!("{}", &compile_warnings); + log::error!("{}", &compile_warnings); } - println!( - "{}{} {}Compiled {} modules in {:.2}s", - LINE_CLEAR, - format_step(current_step, total_steps), - CROSS, - num_compiled_modules, - default_timing.unwrap_or(compile_duration).as_secs_f64() - ); - print!("{}", &compile_errors); + if show_progress { + println!( + "{}{} {}Compiled {} modules in {:.2}s", + LINE_CLEAR, + format_step(current_step, total_steps), + CROSS, + num_compiled_modules, + default_timing.unwrap_or(compile_duration).as_secs_f64() + ); + } + log::error!("{}", &compile_errors); // mark the original files as dirty again, because we didn't complete a full build for (module_name, module) in build_state.modules.iter_mut() { if tracked_dirty_modules.contains(module_name) { @@ -379,16 +411,18 @@ pub fn incremental_build( } Err(IncrementalBuildError::CompileError) } else { - println!( - "{}{} {}Compiled {} modules in {:.2}s", - LINE_CLEAR, - format_step(current_step, total_steps), - SWORDS, - num_compiled_modules, - default_timing.unwrap_or(compile_duration).as_secs_f64() - ); + if show_progress { + println!( + "{}{} {}Compiled {} modules in {:.2}s", + LINE_CLEAR, + format_step(current_step, total_steps), + SWORDS, + num_compiled_modules, + default_timing.unwrap_or(compile_duration).as_secs_f64() + ); + } if helpers::contains_ascii_characters(&compile_warnings) { - print!("{}", &compile_warnings); + log::warn!("{}", &compile_warnings); } Ok(()) } @@ -415,12 +449,12 @@ impl fmt::Display for BuildError { } } +// write build.ninja files in the packages after a non-incremental build +// this is necessary to bust the editor tooling cache. The editor tooling +// is watching this file. +// we don't need to do this in an incremental build because there are no file +// changes (deletes / additions) pub fn write_build_ninja(build_state: &BuildState) { - // write build.ninja files in the packages after a non-incremental build - // this is necessary to bust the editor tooling cache. The editor tooling - // is watching this file. - // we don't need to do this in an incremental build because there are no file - // changes (deletes / additions) for package in build_state.packages.values() { // write empty file: let mut f = File::create(std::path::Path::new(&package.get_bs_build_path()).join("build.ninja")) @@ -432,6 +466,7 @@ pub fn write_build_ninja(build_state: &BuildState) { pub fn build( filter: &Option, path: &str, + show_progress: bool, no_timing: bool, create_sourcedirs: bool, bsc_path: Option, @@ -442,18 +477,27 @@ pub fn build( None }; let timing_total = Instant::now(); - let mut build_state = - initialize_build(default_timing, filter, path, bsc_path).map_err(BuildError::InitializeBuild)?; - - match incremental_build(&mut build_state, default_timing, true, false, create_sourcedirs) { + let mut build_state = initialize_build(default_timing, filter, show_progress, path, bsc_path) + .map_err(BuildError::InitializeBuild)?; + + match incremental_build( + &mut build_state, + default_timing, + true, + show_progress, + false, + create_sourcedirs, + ) { Ok(_) => { - let timing_total_elapsed = timing_total.elapsed(); - println!( - "\n{}{}Finished Compilation in {:.2}s", - LINE_CLEAR, - SPARKLES, - default_timing.unwrap_or(timing_total_elapsed).as_secs_f64() - ); + if show_progress { + let timing_total_elapsed = timing_total.elapsed(); + println!( + "\n{}{}Finished Compilation in {:.2}s", + LINE_CLEAR, + SPARKLES, + default_timing.unwrap_or(timing_total_elapsed).as_secs_f64() + ); + } clean::cleanup_after_build(&build_state); write_build_ninja(&build_state); Ok(build_state) diff --git a/src/build/clean.rs b/src/build/clean.rs index b55f2ee..91c90fe 100644 --- a/src/build/clean.rs +++ b/src/build/clean.rs @@ -319,7 +319,7 @@ pub fn cleanup_after_build(build_state: &BuildState) { }); } -pub fn clean(path: &str, bsc_path: Option) { +pub fn clean(path: &str, show_progress: bool, bsc_path: Option) { let project_root = helpers::get_abs_path(path); let workspace_root = helpers::get_workspace_root(&project_root); let packages = packages::make(&None, &project_root, &workspace_root); @@ -332,21 +332,25 @@ pub fn clean(path: &str, bsc_path: Option) { let rescript_version = helpers::get_rescript_version(&bsc_path); let timing_clean_compiler_assets = Instant::now(); - print!( - "{} {} Cleaning compiler assets...", - style("[1/2]").bold().dim(), - SWEEP - ); - std::io::stdout().flush().unwrap(); - packages.iter().for_each(|(_, package)| { - print!( - "{}{} {} Cleaning {}...", - LINE_CLEAR, + if show_progress { + println!( + "{} {}Cleaning compiler assets...", style("[1/2]").bold().dim(), - SWEEP, - package.name + SWEEP ); - std::io::stdout().flush().unwrap(); + let _ = std::io::stdout().flush(); + }; + packages.iter().for_each(|(_, package)| { + if show_progress { + println!( + "{}{} {}Cleaning {}...", + LINE_CLEAR, + style("[1/2]").bold().dim(), + SWEEP, + package.name + ); + let _ = std::io::stdout().flush(); + } let path_str = package.get_build_path(); let path = std::path::Path::new(&path_str); @@ -358,18 +362,22 @@ pub fn clean(path: &str, bsc_path: Option) { }); let timing_clean_compiler_assets_elapsed = timing_clean_compiler_assets.elapsed(); - println!( - "{}{} {}Cleaned compiler assets in {:.2}s", - LINE_CLEAR, - style("[1/2]").bold().dim(), - SWEEP, - timing_clean_compiler_assets_elapsed.as_secs_f64() - ); - std::io::stdout().flush().unwrap(); + if show_progress { + println!( + "{}{} {}Cleaned compiler assets in {:.2}s", + LINE_CLEAR, + style("[1/2]").bold().dim(), + SWEEP, + timing_clean_compiler_assets_elapsed.as_secs_f64() + ); + let _ = std::io::stdout().flush(); + } let timing_clean_mjs = Instant::now(); - print!("{} {} Cleaning mjs files...", style("[2/2]").bold().dim(), SWEEP); - std::io::stdout().flush().unwrap(); + if show_progress { + println!("{} {}Cleaning mjs files...", style("[2/2]").bold().dim(), SWEEP); + let _ = std::io::stdout().flush(); + } let mut build_state = BuildState::new( project_root.to_owned(), root_config_name, @@ -381,12 +389,15 @@ pub fn clean(path: &str, bsc_path: Option) { packages::parse_packages(&mut build_state); clean_mjs_files(&build_state); let timing_clean_mjs_elapsed = timing_clean_mjs.elapsed(); - println!( - "{}{} {}Cleaned mjs files in {:.2}s", - LINE_CLEAR, - style("[2/2]").bold().dim(), - SWEEP, - timing_clean_mjs_elapsed.as_secs_f64() - ); - std::io::stdout().flush().unwrap(); + + if show_progress { + println!( + "{}{} {}Cleaned mjs files in {:.2}s", + LINE_CLEAR, + style("[2/2]").bold().dim(), + SWEEP, + timing_clean_mjs_elapsed.as_secs_f64() + ); + let _ = std::io::stdout().flush(); + } } diff --git a/src/build/compile.rs b/src/build/compile.rs index 4b06927..863a47c 100644 --- a/src/build/compile.rs +++ b/src/build/compile.rs @@ -9,8 +9,7 @@ use crate::bsconfig; use crate::helpers; use ahash::{AHashMap, AHashSet}; use console::style; -use log::debug; -use log::{info, log_enabled, Level::Info}; +use log::{debug, log_enabled, trace, Level::Info}; use rayon::prelude::*; use std::path::Path; use std::process::Command; @@ -98,7 +97,7 @@ pub fn compile( files_current_loop_count = 0; loop_count += 1; - info!( + trace!( "Compiled: {} out of {}. Compile loop: {}", files_total_count, compile_universe.len(), diff --git a/src/build/packages.rs b/src/build/packages.rs index ee1ea17..7ecee3e 100644 --- a/src/build/packages.rs +++ b/src/build/packages.rs @@ -150,7 +150,7 @@ pub fn read_folders( if metadata.file_type().is_dir() && recurse { match read_folders(filter, package_dir, &new_path, recurse) { Ok(s) => map.extend(s), - Err(e) => println!("Error reading directory: {}", e), + Err(e) => log::error!("Could not read directory: {}", e), } } @@ -167,8 +167,8 @@ pub fn read_folders( ); } - Ok(_) => println!("Filtered: {:?}", name), - Err(ref e) => println!("Error reading directory: {}", e), + Ok(_) => log::info!("Filtered: {:?}", name), + Err(ref e) => log::error!("Could not read directory: {}", e), }, _ => (), } @@ -309,7 +309,7 @@ fn read_dependencies( let (bsconfig, canonical_path) = match read_dependency(package_name, parent_path, project_root, &workspace_root) { Err(error) => { - print!( + log::error!( "{} {} Error building package tree. {}", style("[1/2]").bold().dim(), CROSS, @@ -432,6 +432,7 @@ fn read_packages(project_root: &str, workspace_root: Option) -> AHashMap /// NPM package. The file reader allows for this, just warns when this happens. /// TODO -> Check whether we actually need the `fs::Metadata` pub fn get_source_files( + package_name: &String, package_dir: &Path, filter: &Option, source: &bsconfig::PackageSource, @@ -453,12 +454,17 @@ pub fn get_source_files( match read_folders(filter, package_dir, path_dir, recurse) { Ok(files) => map.extend(files), Err(_e) if type_ == &Some("dev".to_string()) => { - println!( + log::warn!( "Could not read folder: {}... Probably ok as type is dev", path_dir.to_string_lossy() ) } - Err(_e) => println!("Could not read folder: {}...", path_dir.to_string_lossy()), + Err(_e) => log::error!( + "Could not read folder: {:?}. Specified in dependency: {}, located {:?}...", + path_dir.to_path_buf().into_os_string(), + package_name, + package_dir + ), } } @@ -471,21 +477,21 @@ fn extend_with_children( filter: &Option, mut build: AHashMap, ) -> AHashMap { - for (_key, value) in build.iter_mut() { + for (_key, package) in build.iter_mut() { let mut map: AHashMap = AHashMap::new(); - value + package .source_folders .par_iter() - .map(|source| get_source_files(Path::new(&value.path), filter, source)) + .map(|source| get_source_files(&package.name, Path::new(&package.path), filter, source)) .collect::>>() .into_iter() .for_each(|source| map.extend(source)); let mut modules = AHashSet::from_iter( map.keys() - .map(|key| helpers::file_path_to_module_name(key, &value.namespace)), + .map(|key| helpers::file_path_to_module_name(key, &package.namespace)), ); - match value.namespace.to_owned() { + match package.namespace.to_owned() { Namespace::Namespace(namespace) => { let _ = modules.insert(namespace); } @@ -494,14 +500,14 @@ fn extend_with_children( } Namespace::NoNamespace => (), } - value.modules = Some(modules); + package.modules = Some(modules); let mut dirs = AHashSet::new(); map.keys().for_each(|path| { let dir = std::path::Path::new(&path).parent().unwrap(); dirs.insert(dir.to_owned()); }); - value.dirs = Some(dirs); - value.source_files = Some(map); + package.dirs = Some(dirs); + package.source_files = Some(map); } build } @@ -666,10 +672,11 @@ pub fn parse_packages(build_state: &mut BuildState) { implementation_filename.pop(); match source_files.get(&implementation_filename) { None => { - println!( - "{}Warning: No implementation file found for interface file (skipping): {}", - LINE_CLEAR, file - ) + log::warn!( + "{} No implementation file found for interface file (skipping): {}", + LINE_CLEAR, + file + ) } Some(_) => { build_state @@ -799,7 +806,7 @@ pub fn validate_packages_dependencies(packages: &AHashMap) -> b }); } for (package_name, unallowed_deps) in detected_unallowed_dependencies.iter() { - println!( + log::error!( "\n{}: {} has the following unallowed dependencies:", console::style("Error").red(), console::style(package_name).bold() @@ -813,7 +820,7 @@ pub fn validate_packages_dependencies(packages: &AHashMap) -> b .iter() .for_each(|(deps_type, map)| { if !map.is_empty() { - println!( + log::info!( "{} dependencies: {}", console::style(deps_type).bold().dim(), console::style(map.join(" \n -")).bold().dim() @@ -824,7 +831,7 @@ pub fn validate_packages_dependencies(packages: &AHashMap) -> b let has_any_unallowed_dependent = detected_unallowed_dependencies.len() > 0; if has_any_unallowed_dependent { - println!( + log::error!( "\nUpdate the {} value in the {} of the unallowed dependencies to solve the issue!", console::style("unallowed_dependents").bold().dim(), console::style("bsconfig.json").bold().dim() diff --git a/src/build/parse.rs b/src/build/parse.rs index 678a4a3..4ca3c18 100644 --- a/src/build/parse.rs +++ b/src/build/parse.rs @@ -340,7 +340,8 @@ fn generate_ast( Ok((ast_path, None)) } } else { - println!("Parsing file {}...", filename); + log::info!("Parsing file {}...", filename); + Err(format!( "Could not find canonicalize_string_path for file {} in package {}", filename, package.name diff --git a/src/cmd.rs b/src/cmd.rs index acc3c10..be14933 100644 --- a/src/cmd.rs +++ b/src/cmd.rs @@ -7,7 +7,7 @@ use std::time::Instant; pub fn run(command_string: String) { let start_subcommand = Instant::now(); - print!( + log::info!( "{} {}Running subcommand... \n{}\n", style("[...]").bold().dim(), COMMAND, @@ -39,11 +39,11 @@ pub fn run(command_string: String) { } for line in std_err { - println!("{}", line.unwrap()); + eprintln!("{}", line.unwrap()); } let subcommand_duration = start_subcommand.elapsed(); - println!( + log::info!( "{}{} {}Ran subcommand in {:.2}s", LINE_CLEAR, style("[...]").bold().dim(), diff --git a/src/helpers.rs b/src/helpers.rs index 0202f11..52befb0 100644 --- a/src/helpers.rs +++ b/src/helpers.rs @@ -15,9 +15,9 @@ pub mod emojis { pub static COMMAND: Emoji<'_, '_> = Emoji("πŸƒ ", ""); pub static TREE: Emoji<'_, '_> = Emoji("πŸ“¦ ", ""); pub static SWEEP: Emoji<'_, '_> = Emoji("🧹 ", ""); - pub static LOOKING_GLASS: Emoji<'_, '_> = Emoji("πŸ•΅οΈ ", ""); + pub static LOOKING_GLASS: Emoji<'_, '_> = Emoji("πŸ•΅οΈ ", ""); pub static CODE: Emoji<'_, '_> = Emoji("🧱 ", ""); - pub static SWORDS: Emoji<'_, '_> = Emoji("🀺 ️", ""); + pub static SWORDS: Emoji<'_, '_> = Emoji("βš”οΈ ", ""); pub static DEPS: Emoji<'_, '_> = Emoji("️🌴 ", ""); pub static CHECKMARK: Emoji<'_, '_> = Emoji("οΈβœ… ", ""); pub static CROSS: Emoji<'_, '_> = Emoji("οΈπŸ›‘ ", ""); diff --git a/src/lock.rs b/src/lock.rs index 98ab217..dfadcfb 100644 --- a/src/lock.rs +++ b/src/lock.rs @@ -21,9 +21,9 @@ pub enum Error { impl std::fmt::Display for Error { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { let msg = match self { - Error::Locked(pid) => format!("Rewatch is already running with PID {}", pid), - Error::ParsingLockfile(e) => format!("Could not parse lockfile: \n {}", e), - Error::ReadingLockfile(e) => format!("Could not read lockfile: \n {}", e), + Error::Locked(pid) => format!("Rewatch is already running. The process ID (PID) is {}", pid), + Error::ParsingLockfile(e) => format!("Could not parse lockfile: \n {} \n (try removing it and running the command again)", e), + Error::ReadingLockfile(e) => format!("Could not read lockfile: \n {} \n (try removing it and running the command again)", e), Error::WritingLockfile(e) => format!("Could not write lockfile: \n {}", e), }; write!(f, "{}", msg) diff --git a/src/main.rs b/src/main.rs index 3624035..1689434 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,5 +1,8 @@ use clap::{Parser, ValueEnum}; +use clap_verbosity_flag::InfoLevel; +use log::LevelFilter; use regex::Regex; +use std::io::Write; use rewatch::{build, cmd, lock, watcher}; @@ -40,6 +43,16 @@ struct Args { #[arg(short, long)] no_timing: Option, + /// Verbosity: + /// -v -> Debug + /// -vv -> Trace + /// -q -> Warn + /// -qq -> Error + /// -qqq -> Off. + /// Default (/ no argument given): 'info' + #[command(flatten)] + verbose: clap_verbosity_flag::Verbosity, + /// This creates a source_dirs.json file at the root of the monorepo, which is needed when you /// want to use Reanalyze #[arg(short, long)] @@ -60,8 +73,14 @@ struct Args { } fn main() { - env_logger::init(); let args = Args::parse(); + let log_level_filter = args.verbose.log_level_filter(); + + env_logger::Builder::new() + .format(|buf, record| writeln!(buf, "{}:\n{}", record.level(), record.args())) + .filter_level(log_level_filter) + .target(env_logger::fmt::Target::Stdout) + .init(); let command = args.command.unwrap_or(Command::Build); let folder = args.folder.unwrap_or(".".to_string()); @@ -80,23 +99,28 @@ fn main() { } } + // The 'normal run' mode will show the 'pretty' formatted progress. But if we turn off the log + // level, we should never show that. + let show_progress = log_level_filter != LevelFilter::Off; + match lock::get(&folder) { lock::Lock::Error(ref e) => { - eprintln!("Error while trying to get lock: {e}"); + log::error!("Could not start Rewatch: {e}"); std::process::exit(1) } lock::Lock::Aquired(_) => match command { - Command::Clean => build::clean::clean(&folder, args.bsc_path), + Command::Clean => build::clean::clean(&folder, show_progress, args.bsc_path), Command::Build => { match build::build( &filter, &folder, + show_progress, args.no_timing.unwrap_or(false), args.create_sourcedirs.unwrap_or(false), args.bsc_path, ) { Err(e) => { - eprintln!("Error Building: {e}"); + log::error!("{e}"); std::process::exit(1) } Ok(_) => { @@ -110,6 +134,7 @@ fn main() { Command::Watch => { watcher::start( &filter, + show_progress, &folder, args.after_build, args.create_sourcedirs.unwrap_or(false), diff --git a/src/watcher.rs b/src/watcher.rs index 74251e5..9fc140a 100644 --- a/src/watcher.rs +++ b/src/watcher.rs @@ -49,11 +49,13 @@ fn matches_filter(path_buf: &Path, filter: &Option) -> bool { async fn async_watch( q: Arc>>, path: &str, + show_progress: bool, filter: &Option, after_build: Option, create_sourcedirs: bool, ) -> notify::Result<()> { - let mut build_state = build::initialize_build(None, filter, path, None).expect("Can't initialize build"); + let mut build_state = + build::initialize_build(None, filter, show_progress, path, None).expect("Can't initialize build"); let mut needs_compile_type = CompileType::Incremental; // create a mutex to capture if ctrl-c was pressed let ctrlc_pressed = Arc::new(Mutex::new(false)); @@ -70,7 +72,9 @@ async fn async_watch( loop { if *ctrlc_pressed_clone.lock().unwrap() { - println!("\nExiting..."); + if show_progress { + println!("\nExiting..."); + } clean::cleanup_after_build(&build_state); break Ok(()); } @@ -183,6 +187,7 @@ async fn async_watch( &mut build_state, None, initial_build, + show_progress, !initial_build, create_sourcedirs, ) @@ -192,23 +197,31 @@ async fn async_watch( cmd::run(a) } let timing_total_elapsed = timing_total.elapsed(); - println!( - "\n{}{}Finished {} compilation in {:.2}s\n", - LINE_CLEAR, - SPARKLES, - if initial_build { "initial" } else { "incremental" }, - timing_total_elapsed.as_secs_f64() - ); + if show_progress { + println!( + "\n{}{}Finished {} compilation in {:.2}s\n", + LINE_CLEAR, + SPARKLES, + if initial_build { "initial" } else { "incremental" }, + timing_total_elapsed.as_secs_f64() + ); + } } needs_compile_type = CompileType::None; initial_build = false; } CompileType::Full => { let timing_total = Instant::now(); - build_state = - build::initialize_build(None, filter, path, None).expect("Can't initialize build"); - let _ = - build::incremental_build(&mut build_state, None, initial_build, false, create_sourcedirs); + build_state = build::initialize_build(None, filter, show_progress, path, None) + .expect("Can't initialize build"); + let _ = build::incremental_build( + &mut build_state, + None, + initial_build, + show_progress, + false, + create_sourcedirs, + ); if let Some(a) = after_build.clone() { cmd::run(a) } @@ -216,12 +229,14 @@ async fn async_watch( build::write_build_ninja(&build_state); let timing_total_elapsed = timing_total.elapsed(); - println!( - "\n{}{}Finished compilation in {:.2}s\n", - LINE_CLEAR, - SPARKLES, - timing_total_elapsed.as_secs_f64() - ); + if show_progress { + println!( + "\n{}{}Finished compilation in {:.2}s\n", + LINE_CLEAR, + SPARKLES, + timing_total_elapsed.as_secs_f64() + ); + } needs_compile_type = CompileType::None; initial_build = false; } @@ -236,6 +251,7 @@ async fn async_watch( pub fn start( filter: &Option, + show_progress: bool, folder: &str, after_build: Option, create_sourcedirs: bool, @@ -251,8 +267,17 @@ pub fn start( .watch(folder.as_ref(), RecursiveMode::Recursive) .expect("Could not start watcher"); - if let Err(e) = async_watch(consumer, folder, filter, after_build, create_sourcedirs).await { - println!("error: {:?}", e) + if let Err(e) = async_watch( + consumer, + folder, + show_progress, + filter, + after_build, + create_sourcedirs, + ) + .await + { + log::error!("{:?}", e) } }) } diff --git a/tests/lib/rewatch.lock b/tests/lib/rewatch.lock index da27e6c..22da5d2 100644 --- a/tests/lib/rewatch.lock +++ b/tests/lib/rewatch.lock @@ -1 +1 @@ -57150 \ No newline at end of file +36062 \ No newline at end of file diff --git a/tests/lock.sh b/tests/lock.sh index 65d48cb..019e616 100755 --- a/tests/lock.sh +++ b/tests/lock.sh @@ -23,7 +23,7 @@ success "Watcher Started" sleep 1 -if rewatch watch 2>&1 | grep 'Error while trying to get lock:' &> /dev/null; +if rewatch watch 2>&1 | grep 'Could not start Rewatch:' &> /dev/null; then success "Lock is correctly set" exit_watcher @@ -41,7 +41,7 @@ success "Watcher Started" sleep 1 -if cat tmp.txt | grep 'Error while trying to get lock:' &> /dev/null; +if cat tmp.txt | grep 'Could not start Rewatch:' &> /dev/null; then error "Lock not removed correctly" exit_watcher diff --git a/tests/snapshots/dependency-cycle.txt b/tests/snapshots/dependency-cycle.txt index ff0f93b..c0d889a 100644 --- a/tests/snapshots/dependency-cycle.txt +++ b/tests/snapshots/dependency-cycle.txt @@ -1,11 +1,18 @@ -[1/7]πŸ“¦ Building package tree... [1/7] πŸ“¦ Built package tree in 0.00s -[2/7] πŸ•΅οΈ Finding source files... [2/7] πŸ•΅οΈ Found source files in 0.00s -[3/7] πŸ“ Reading compile state... [3/7] πŸ“ Read compile state 0.00s -[4/7] 🧹 Cleaning up previous build... [4/7] 🧹 Cleaned 0/11 0.00s +[1/7] πŸ“¦ Building package tree... + [1/7] πŸ“¦ Built package tree in 0.00s +[2/7] πŸ•΅οΈ Finding source files... + [2/7] πŸ•΅οΈ Found source files in 0.00s +[3/7] πŸ“ Reading compile state... + [3/7] πŸ“ Read compile state 0.00s +[4/7] 🧹 Cleaning up previous build... + [4/7] 🧹 Cleaned 0/11 0.00s  [5/7] 🧱 Parsed 1 source files in 0.00s  [6/7] ️🌴 Collected deps in 0.00s  [7/7] οΈπŸ›‘ Compiled 0 modules in 0.00s +ERROR: Can't continue... Found a circular dependency in your code: NewNamespace.NS_alias -> Dep01 -> Dep02 -> NS -> NewNamespace.NS_alias -Error Building:  οΈπŸ›‘ Error Running Incremental Build:  οΈπŸ›‘ Failed to Compile. See Errors Above + +ERROR: + οΈπŸ›‘ Error Running Incremental Build:  οΈπŸ›‘ Failed to Compile. See Errors Above diff --git a/tests/snapshots/remove-file.txt b/tests/snapshots/remove-file.txt index 0844763..be083fa 100644 --- a/tests/snapshots/remove-file.txt +++ b/tests/snapshots/remove-file.txt @@ -1,10 +1,15 @@ -[1/7]πŸ“¦ Building package tree... [1/7] πŸ“¦ Built package tree in 0.00s -[2/7] πŸ•΅οΈ Finding source files... [2/7] πŸ•΅οΈ Found source files in 0.00s -[3/7] πŸ“ Reading compile state... [3/7] πŸ“ Read compile state 0.00s -[4/7] 🧹 Cleaning up previous build... [4/7] 🧹 Cleaned 1/11 0.00s +[1/7] πŸ“¦ Building package tree... + [1/7] πŸ“¦ Built package tree in 0.00s +[2/7] πŸ•΅οΈ Finding source files... + [2/7] πŸ•΅οΈ Found source files in 0.00s +[3/7] πŸ“ Reading compile state... + [3/7] πŸ“ Read compile state 0.00s +[4/7] 🧹 Cleaning up previous build... + [4/7] 🧹 Cleaned 1/11 0.00s  [5/7] 🧱 Parsed 0 source files in 0.00s  [6/7] ️🌴 Collected deps in 0.00s  [7/7] οΈπŸ›‘ Compiled 1 modules in 0.00s +ERROR: We've found a bug for you! /packages/dep01/src/Dep01.res:3:9-17 @@ -21,4 +26,6 @@ - Did you include the file's directory to the "sources" in bsconfig.json? -Error Building:  οΈπŸ›‘ Error Running Incremental Build:  οΈπŸ›‘ Failed to Compile. See Errors Above + +ERROR: + οΈπŸ›‘ Error Running Incremental Build:  οΈπŸ›‘ Failed to Compile. See Errors Above diff --git a/tests/snapshots/rename-file-internal-dep-namespace.txt b/tests/snapshots/rename-file-internal-dep-namespace.txt index 5230dcf..fc6c41f 100644 --- a/tests/snapshots/rename-file-internal-dep-namespace.txt +++ b/tests/snapshots/rename-file-internal-dep-namespace.txt @@ -1,10 +1,15 @@ -[1/7]πŸ“¦ Building package tree... [1/7] πŸ“¦ Built package tree in 0.00s -[2/7] πŸ•΅οΈ Finding source files... [2/7] πŸ•΅οΈ Found source files in 0.00s -[3/7] πŸ“ Reading compile state... [3/7] πŸ“ Read compile state 0.00s -[4/7] 🧹 Cleaning up previous build... [4/7] 🧹 Cleaned 2/11 0.00s +[1/7] πŸ“¦ Building package tree... + [1/7] πŸ“¦ Built package tree in 0.00s +[2/7] πŸ•΅οΈ Finding source files... + [2/7] πŸ•΅οΈ Found source files in 0.00s +[3/7] πŸ“ Reading compile state... + [3/7] πŸ“ Read compile state 0.00s +[4/7] 🧹 Cleaning up previous build... + [4/7] 🧹 Cleaned 2/11 0.00s  [5/7] 🧱 Parsed 2 source files in 0.00s  [6/7] ️🌴 Collected deps in 0.00s  [7/7] οΈπŸ›‘ Compiled 3 modules in 0.00s +ERROR: We've found a bug for you! /packages/new-namespace/src/NS_alias.res:2:1-16 @@ -21,4 +26,6 @@ Hint: Did you mean Other_module2? -Error Building:  οΈπŸ›‘ Error Running Incremental Build:  οΈπŸ›‘ Failed to Compile. See Errors Above + +ERROR: + οΈπŸ›‘ Error Running Incremental Build:  οΈπŸ›‘ Failed to Compile. See Errors Above diff --git a/tests/snapshots/rename-file-internal-dep.txt b/tests/snapshots/rename-file-internal-dep.txt index c22caa4..40c6bab 100644 --- a/tests/snapshots/rename-file-internal-dep.txt +++ b/tests/snapshots/rename-file-internal-dep.txt @@ -1,10 +1,15 @@ -[1/7]πŸ“¦ Building package tree... [1/7] πŸ“¦ Built package tree in 0.00s -[2/7] πŸ•΅οΈ Finding source files... [2/7] πŸ•΅οΈ Found source files in 0.00s -[3/7] πŸ“ Reading compile state... [3/7] πŸ“ Read compile state 0.00s -[4/7] 🧹 Cleaning up previous build... [4/7] 🧹 Cleaned 2/11 0.00s +[1/7] πŸ“¦ Building package tree... + [1/7] πŸ“¦ Built package tree in 0.00s +[2/7] πŸ•΅οΈ Finding source files... + [2/7] πŸ•΅οΈ Found source files in 0.00s +[3/7] πŸ“ Reading compile state... + [3/7] πŸ“ Read compile state 0.00s +[4/7] 🧹 Cleaning up previous build... + [4/7] 🧹 Cleaned 2/11 0.00s  [5/7] 🧱 Parsed 2 source files in 0.00s  [6/7] ️🌴 Collected deps in 0.00s  [7/7] οΈπŸ›‘ Compiled 2 modules in 0.00s +ERROR: We've found a bug for you! /packages/main/src/Main.res:4:8-24 @@ -21,4 +26,6 @@ - Did you include the file's directory to the "sources" in bsconfig.json? -Error Building:  οΈπŸ›‘ Error Running Incremental Build:  οΈπŸ›‘ Failed to Compile. See Errors Above + +ERROR: + οΈπŸ›‘ Error Running Incremental Build:  οΈπŸ›‘ Failed to Compile. See Errors Above diff --git a/tests/snapshots/rename-file-with-interface.txt b/tests/snapshots/rename-file-with-interface.txt index 3053463..9927321 100644 --- a/tests/snapshots/rename-file-with-interface.txt +++ b/tests/snapshots/rename-file-with-interface.txt @@ -1,10 +1,15 @@ -[1/7]πŸ“¦ Building package tree... [1/7] πŸ“¦ Built package tree in 0.00s -[2/7] πŸ•΅οΈ Finding source files... Warning: No implementation file found for interface file (skipping): src/ModuleWithInterface.resi - [2/7] πŸ•΅οΈ Found source files in 0.00s -[3/7] πŸ“ Reading compile state... [3/7] πŸ“ Read compile state 0.00s -[4/7] 🧹 Cleaning up previous build... [4/7] 🧹 Cleaned 2/11 0.00s +[1/7] πŸ“¦ Building package tree... + [1/7] πŸ“¦ Built package tree in 0.00s +[2/7] πŸ•΅οΈ Finding source files... +WARN: + No implementation file found for interface file (skipping): src/ModuleWithInterface.resi + [2/7] πŸ•΅οΈ Found source files in 0.00s +[3/7] πŸ“ Reading compile state... + [3/7] πŸ“ Read compile state 0.00s +[4/7] 🧹 Cleaning up previous build... + [4/7] 🧹 Cleaned 2/11 0.00s  [5/7] 🧱 Parsed 1 source files in 0.00s  [6/7] ️🌴 Collected deps in 0.00s - [7/7] 🀺 ️Compiled 1 modules in 0.00s + [7/7] βš”οΈ Compiled 1 modules in 0.00s  ✨ Finished Compilation in 0.00s diff --git a/tests/snapshots/rename-file.txt b/tests/snapshots/rename-file.txt index 43550cc..d8206e1 100644 --- a/tests/snapshots/rename-file.txt +++ b/tests/snapshots/rename-file.txt @@ -1,9 +1,13 @@ -[1/7]πŸ“¦ Building package tree... [1/7] πŸ“¦ Built package tree in 0.00s -[2/7] πŸ•΅οΈ Finding source files... [2/7] πŸ•΅οΈ Found source files in 0.00s -[3/7] πŸ“ Reading compile state... [3/7] πŸ“ Read compile state 0.00s -[4/7] 🧹 Cleaning up previous build... [4/7] 🧹 Cleaned 1/11 0.00s +[1/7] πŸ“¦ Building package tree... + [1/7] πŸ“¦ Built package tree in 0.00s +[2/7] πŸ•΅οΈ Finding source files... + [2/7] πŸ•΅οΈ Found source files in 0.00s +[3/7] πŸ“ Reading compile state... + [3/7] πŸ“ Read compile state 0.00s +[4/7] 🧹 Cleaning up previous build... + [4/7] 🧹 Cleaned 1/11 0.00s  [5/7] 🧱 Parsed 1 source files in 0.00s  [6/7] ️🌴 Collected deps in 0.00s - [7/7] 🀺 ️Compiled 1 modules in 0.00s + [7/7] βš”οΈ Compiled 1 modules in 0.00s  ✨ Finished Compilation in 0.00s diff --git a/tests/snapshots/rename-interface-file.txt b/tests/snapshots/rename-interface-file.txt index a46d80f..50dcd91 100644 --- a/tests/snapshots/rename-interface-file.txt +++ b/tests/snapshots/rename-interface-file.txt @@ -1,10 +1,15 @@ -[1/7]πŸ“¦ Building package tree... [1/7] πŸ“¦ Built package tree in 0.00s -[2/7] πŸ•΅οΈ Finding source files... Warning: No implementation file found for interface file (skipping): src/ModuleWithInterface2.resi - [2/7] πŸ•΅οΈ Found source files in 0.00s -[3/7] πŸ“ Reading compile state... [3/7] πŸ“ Read compile state 0.00s -[4/7] 🧹 Cleaning up previous build... [4/7] 🧹 Cleaned 1/11 0.00s +[1/7] πŸ“¦ Building package tree... + [1/7] πŸ“¦ Built package tree in 0.00s +[2/7] πŸ•΅οΈ Finding source files... +WARN: + No implementation file found for interface file (skipping): src/ModuleWithInterface2.resi + [2/7] πŸ•΅οΈ Found source files in 0.00s +[3/7] πŸ“ Reading compile state... + [3/7] πŸ“ Read compile state 0.00s +[4/7] 🧹 Cleaning up previous build... + [4/7] 🧹 Cleaned 1/11 0.00s  [5/7] 🧱 Parsed 1 source files in 0.00s  [6/7] ️🌴 Collected deps in 0.00s - [7/7] 🀺 ️Compiled 1 modules in 0.00s + [7/7] βš”οΈ Compiled 1 modules in 0.00s  ✨ Finished Compilation in 0.00s