Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions .config/nextest.toml
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,10 @@ default-filter = "(kind(test) + test(use_extra_args)) - package(clang-tools-mana
# show log output from each failing test
failure-output = "final"

# set slow-timeout to 30 seconds and
# terminate tests after hitting the period 8 times (30s x 8 = 4 minutes).
slow-timeout = { period = "30s", terminate-after = 8 }

[profile.all]
# A profile to run all tests (including tests that run longer than 10 seconds)
default-filter = "all() - test(#*eval_version)"

# Revert slow-timeout to nextest default value.
# Otherwise, default profile value (10s) is inherited.
slow-timeout = "60s"
1 change: 0 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 0 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ strip = "symbols"
[workspace.dependencies]
anyhow = "1.0.102"
clap = { version = "4.6.1", features = ["derive"] }
colored = "3.1.1"
log = { version = "0.4.31", features = ["std"] }
mockito = "1.7.2"
pyo3 = {version = "0.29.0", features = ["extension-module"] }
Expand Down
5 changes: 2 additions & 3 deletions clang-tools-manager/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@ required-features = ["bin"]
[dependencies]
anyhow = { workspace = true, optional = true }
blake2 = "0.10.6"
clap = { workspace = true, features = ["derive"], optional = true }
colored = { workspace = true, optional = true }
clap = { workspace = true, optional = true }
colored = { version = "3.1.1", optional = true }
directories = "6.0.0"
log = { workspace = true }
regex = { workspace = true }
Expand All @@ -41,7 +41,6 @@ which = "8.0.2"
zip = { version = "8.6.0", default-features = false, features = ["deflate"] }

[dev-dependencies]
colored = { workspace = true }
mockito = { workspace = true }
reqwest = { workspace = true, features = ["default-tls"] }
tempfile = { workspace = true }
Expand Down
35 changes: 27 additions & 8 deletions clang-tools-manager/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,15 @@ The `clang-tools` binary's Command Line Interface (CLI) is rather simple.
<details><summary><code>clang-tools --help</code></summary>
<p>

```sh
Usage: clang-tools [OPTIONS]
```txt
Usage: clang-tools.exe [OPTIONS] [TOOL]...

Arguments:
[TOOL]...
The clang tool to install

[default: clang-format clang-tidy]
[possible values: clang-tidy, clang-format]

Options:
-v, --version [<VERSION>]
Expand All @@ -56,12 +63,6 @@ Options:
This will include more DEBUG level log messages.
Without it, log level is set to INFO by default.

-t, --tool <TOOL>
The clang tool to install

[default: "clang-format clang-tidy"]
[possible values: clang-tidy, clang-format]

-d, --directory <DIRECTORY>
The directory where the clang tools should be installed

Expand All @@ -70,6 +71,24 @@ Options:

This will only overwrite an existing symlink.

--mod-sys
Whether to use the system's available package managers.

By default, this matches the value of a CI environment variable.
For non-CI contexts, this allows users to opt-in to using
system package managers as a fallback in case PyPI offerings are
unsatisfactory.

If system package managers are not allowed or fail, then
static binaries built by cpp-linter are sought
(for compatible platforms).

--no-mod-sys
Strictly disallow using system package managers.

This can be used to override the default behavior of `--mod-sys`,
useful in sensitive CI environments, like self-hosted runners.

-h, --help
Print help (see a summary with '-h')
```
Expand Down
2 changes: 2 additions & 0 deletions clang-tools-manager/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,5 @@ pub use version::{ClangVersion, GetToolError, RequestedVersion, RequestedVersion

mod progress_bar;
pub use progress_bar::ProgressBar;

pub mod logger;
26 changes: 21 additions & 5 deletions cpp-linter/src/logger.rs → clang-tools-manager/src/logger.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,26 @@ use std::{
io::{Write, stdout},
};

use clap::builder::{
Styles,
styling::{AnsiColor, Color, Style},
};
use colored::{Colorize, control::set_override};
use log::{Level, LevelFilter, Log, Metadata, Record};

/// A color scheme to use for CLI `--help` output.
pub const CLI_HELP_STYLE: Styles = Styles::styled()
.usage(Style::new().fg_color(Some(Color::Ansi(AnsiColor::BrightBlue))))
.header(
Style::new()
.fg_color(Some(Color::Ansi(AnsiColor::BrightBlue)))
.underline(),
)
.placeholder(Style::new().fg_color(Some(Color::Ansi(AnsiColor::Yellow))))
.literal(Style::new().fg_color(Some(Color::Ansi(AnsiColor::BrightGreen))))
.context_value(Style::new().fg_color(Some(Color::Ansi(AnsiColor::Cyan))))
.invalid(Style::new().fg_color(Some(Color::Ansi(AnsiColor::BrightRed))));

#[derive(Default)]
struct SimpleLogger;

Expand Down Expand Up @@ -41,7 +58,7 @@ impl Log for SimpleLogger {
.expect("Failed to flush log command in stdout");
} else if self.enabled(record.metadata()) {
let module = record.module_path();
if module.is_none_or(|v| v.starts_with("cpp_linter")) {
if module.is_none_or(|v| v.starts_with("cpp_linter") || v.starts_with("clang_tools")) {
writeln!(
stdout,
"[{}]: {}",
Expand Down Expand Up @@ -74,7 +91,7 @@ impl Log for SimpleLogger {
/// The logging level defaults to [`LevelFilter::Info`].
/// This logs a debug message about [`SetLoggerError`](struct@log::SetLoggerError)
/// if the `LOGGER` is already initialized.
pub fn try_init() {
pub fn try_init_logger() {
let logger: SimpleLogger = SimpleLogger;
if matches!(
env::var("CPP_LINTER_COLOR").as_deref(),
Expand All @@ -95,15 +112,14 @@ pub fn try_init() {
mod test {
use std::env;

use super::{SimpleLogger, try_init};
use super::try_init_logger;

#[test]
fn trace_log() {
unsafe {
env::set_var("CPP_LINTER_COLOR", "true");
}
try_init();
assert!(SimpleLogger::level_color(&log::Level::Trace).contains("TRACE"));
try_init_logger();
log::set_max_level(log::LevelFilter::Trace);
log::trace!("A dummy log statement for code coverage");
}
Expand Down
112 changes: 10 additions & 102 deletions clang-tools-manager/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,101 +1,14 @@
use anyhow::Result;
use clang_tools_manager::{ClangTool, RequestedVersion};
use clang_tools_manager::{
ClangTool, RequestedVersion,
logger::{CLI_HELP_STYLE, try_init_logger},
};
use clap::Parser;

use std::{collections::HashMap, path::PathBuf, str::FromStr};
mod logging {
use colored::{Colorize, control::set_override};
use log::{Level, LevelFilter, Log, Metadata, Record};
use std::{
env,
io::{Write, stdout},
};

struct SimpleLogger;

impl SimpleLogger {
fn level_color(level: &Level) -> String {
let name = format!("{:>5}", level.as_str().to_uppercase());
match level {
Level::Error => name.red().bold().to_string(),
Level::Warn => name.yellow().bold().to_string(),
Level::Info => name.green().bold().to_string(),
Level::Debug => name.blue().bold().to_string(),
Level::Trace => name.magenta().bold().to_string(),
}
}
}

impl Log for SimpleLogger {
fn enabled(&self, metadata: &Metadata) -> bool {
metadata.level() <= log::max_level()
}

fn log(&self, record: &Record) {
let mut stdout = stdout().lock();
if record.target() == "CI_LOG_GROUPING" {
// this log is meant to manipulate a CI workflow's log grouping
writeln!(stdout, "{}", record.args())
.expect("Failed to write log command to stdout");
stdout
.flush()
.expect("Failed to flush log command in stdout");
} else if self.enabled(record.metadata()) {
let module = record.module_path();
if module.is_none_or(|v| {
v.starts_with("clang_tools_manager") || v.starts_with("clang_tools")
}) {
writeln!(
stdout,
"[{}]: {}",
Self::level_color(&record.level()),
record.args()
)
.expect("Failed to write log message to stdout");
} else if let Some(module) = module {
writeln!(
stdout,
"[{}]{{{}:{}}}: {}",
Self::level_color(&record.level()),
module,
record.line().unwrap_or_default(),
record.args()
)
.expect("Failed to write detailed log message to stdout");
}
stdout
.flush()
.expect("Failed to flush log message in stdout");
}
}

fn flush(&self) {}
}

/// A function to initialize the private `LOGGER`.
///
/// The logging level defaults to [`LevelFilter::Info`].
/// This logs a debug message about [`SetLoggerError`](struct@log::SetLoggerError)
/// if the `LOGGER` is already initialized.
pub fn initialize_logger() {
let logger: SimpleLogger = SimpleLogger;
if env::var("CPP_LINTER_COLOR")
.as_deref()
.is_ok_and(|v| matches!(v, "on" | "1" | "true"))
{
set_override(true);
}
if let Err(e) =
log::set_boxed_logger(Box::new(logger)).map(|()| log::set_max_level(LevelFilter::Info))
{
// logger singleton already instantiated.
// we'll just use whatever the current config is.
log::debug!("{e:?}");
}
}
}

#[derive(clap::Parser, Debug)]
#[command(styles(CLI_HELP_STYLE))]
pub struct CliOptions {
/// The desired version of clang to install.
#[arg(
Expand All @@ -122,12 +35,10 @@ pub struct CliOptions {

/// The clang tool to install.
#[arg(
short,
long,
value_delimiter = ' ',
default_value = "clang-format clang-tidy"
num_args = 0..,
default_values_t = vec![ClangTool::ClangFormat, ClangTool::ClangTidy],
)]
pub tool: Option<Vec<ClangTool>>,
pub tool: Vec<ClangTool>,

/// The directory where the clang tools should be installed.
#[arg(short, long)]
Expand Down Expand Up @@ -162,14 +73,11 @@ pub struct CliOptions {

#[tokio::main]
async fn main() -> Result<()> {
logging::initialize_logger();
try_init_logger();
let options = CliOptions::parse();
if options.verbose {
log::set_max_level(log::LevelFilter::Debug);
}
let tool = options
.tool
.expect("--tool should have a default value: [clang-format, clang-tidy]");
match options.version.unwrap_or(RequestedVersion::default()) {
RequestedVersion::NoValue => {
log::info!(
Expand All @@ -179,7 +87,7 @@ async fn main() -> Result<()> {
}
req_ver => {
let mut map_tools = HashMap::new();
for t in tool {
for t in options.tool {
if let Some(version) = req_ver
.eval_tool(
&t,
Expand Down
Loading
Loading