feat(solc): add Reporter type (#883)
* feat(solc): add Reporter type * chore(clippy): make clippy happy
This commit is contained in:
parent
b657e47e6b
commit
36c3adeec9
|
@ -1,7 +1,7 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
artifacts::Source,
|
artifacts::Source,
|
||||||
error::{Result, SolcError},
|
error::{Result, SolcError},
|
||||||
utils, CompilerInput, CompilerOutput,
|
report, utils, CompilerInput, CompilerOutput,
|
||||||
};
|
};
|
||||||
use semver::{Version, VersionReq};
|
use semver::{Version, VersionReq};
|
||||||
use serde::{de::DeserializeOwned, Deserialize, Serialize};
|
use serde::{de::DeserializeOwned, Deserialize, Serialize};
|
||||||
|
@ -423,9 +423,9 @@ impl Solc {
|
||||||
#[cfg(feature = "svm")]
|
#[cfg(feature = "svm")]
|
||||||
pub async fn install(version: &Version) -> std::result::Result<(), svm::SolcVmError> {
|
pub async fn install(version: &Version) -> std::result::Result<(), svm::SolcVmError> {
|
||||||
tracing::trace!("installing solc version \"{}\"", version);
|
tracing::trace!("installing solc version \"{}\"", version);
|
||||||
println!("installing solc version \"{}\"", version);
|
report::solc_installation_start(version);
|
||||||
let result = svm::install(version).await;
|
let result = svm::install(version).await;
|
||||||
println!("installation complete");
|
report::solc_installation_success(version);
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -433,9 +433,9 @@ impl Solc {
|
||||||
#[cfg(all(feature = "svm", feature = "async"))]
|
#[cfg(all(feature = "svm", feature = "async"))]
|
||||||
pub fn blocking_install(version: &Version) -> std::result::Result<(), svm::SolcVmError> {
|
pub fn blocking_install(version: &Version) -> std::result::Result<(), svm::SolcVmError> {
|
||||||
tracing::trace!("blocking installing solc version \"{}\"", version);
|
tracing::trace!("blocking installing solc version \"{}\"", version);
|
||||||
println!("installing solc version \"{}\"", version);
|
report::solc_installation_start(version);
|
||||||
tokio::runtime::Runtime::new().unwrap().block_on(svm::install(version))?;
|
tokio::runtime::Runtime::new().unwrap().block_on(svm::install(version))?;
|
||||||
println!("installation completed");
|
report::solc_installation_success(version);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -80,6 +80,7 @@ use crate::{
|
||||||
cache::ArtifactsCache,
|
cache::ArtifactsCache,
|
||||||
error::Result,
|
error::Result,
|
||||||
output::AggregatedCompilerOutput,
|
output::AggregatedCompilerOutput,
|
||||||
|
report,
|
||||||
resolver::GraphEdges,
|
resolver::GraphEdges,
|
||||||
ArtifactOutput, CompilerInput, Graph, Project, ProjectCompileOutput, ProjectPathsConfig, Solc,
|
ArtifactOutput, CompilerInput, Graph, Project, ProjectCompileOutput, ProjectPathsConfig, Solc,
|
||||||
Sources,
|
Sources,
|
||||||
|
@ -343,7 +344,10 @@ fn compile_sequential(
|
||||||
input.sources.len(),
|
input.sources.len(),
|
||||||
input.sources.keys()
|
input.sources.keys()
|
||||||
);
|
);
|
||||||
let output = solc.compile(&input)?;
|
|
||||||
|
report::solc_spawn(&solc, &version, &input);
|
||||||
|
let output = solc.compile_exact(&input)?;
|
||||||
|
report::solc_success(&solc, &version, &output);
|
||||||
tracing::trace!("compiled input, output has error: {}", output.has_error());
|
tracing::trace!("compiled input, output has error: {}", output.has_error());
|
||||||
|
|
||||||
aggregated.extend(version, output);
|
aggregated.extend(version, output);
|
||||||
|
@ -392,7 +396,11 @@ fn compile_parallel(
|
||||||
input.sources.len(),
|
input.sources.len(),
|
||||||
input.sources.keys()
|
input.sources.keys()
|
||||||
);
|
);
|
||||||
solc.compile(&input).map(|output| (version, output))
|
report::solc_spawn(&solc, &version, &input);
|
||||||
|
solc.compile(&input).map(move |output| {
|
||||||
|
report::solc_success(&solc, &version, &output);
|
||||||
|
(version, output)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
.collect::<Result<Vec<_>>>()
|
.collect::<Result<Vec<_>>>()
|
||||||
})?;
|
})?;
|
||||||
|
|
|
@ -25,6 +25,7 @@ pub mod remappings;
|
||||||
use crate::artifacts::Source;
|
use crate::artifacts::Source;
|
||||||
|
|
||||||
pub mod error;
|
pub mod error;
|
||||||
|
pub mod report;
|
||||||
pub mod utils;
|
pub mod utils;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
|
|
@ -0,0 +1,208 @@
|
||||||
|
//! Subscribe to events in the compiler pipeline
|
||||||
|
|
||||||
|
use crate::{CompilerInput, CompilerOutput, Solc};
|
||||||
|
use semver::Version;
|
||||||
|
use std::{
|
||||||
|
error::Error,
|
||||||
|
fmt,
|
||||||
|
sync::{
|
||||||
|
atomic::{AtomicUsize, Ordering},
|
||||||
|
Arc,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Install this `Reporter` as the global default if one is
|
||||||
|
/// not already set.
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
/// Returns an Error if the initialization was unsuccessful, likely
|
||||||
|
/// because a global reporter was already installed by another
|
||||||
|
/// call to `try_init`.
|
||||||
|
pub fn try_init<T>(reporter: T) -> Result<(), Box<dyn Error + Send + Sync + 'static>>
|
||||||
|
where
|
||||||
|
T: Reporter + Send + Sync + 'static,
|
||||||
|
{
|
||||||
|
set_global_reporter(Report::new(reporter))?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Install this `Reporter` as the global default.
|
||||||
|
///
|
||||||
|
/// # Panics
|
||||||
|
///
|
||||||
|
/// Panics if the initialization was unsuccessful, likely because a
|
||||||
|
/// global reporter was already installed by another call to `try_init`.
|
||||||
|
/// ```rust
|
||||||
|
/// use ethers_solc::report::BasicStdoutReporter;
|
||||||
|
/// let subscriber = ethers_solc::report::init(BasicStdoutReporter::default());
|
||||||
|
/// ```
|
||||||
|
pub fn init<T>(reporter: T)
|
||||||
|
where
|
||||||
|
T: Reporter + Send + Sync + 'static,
|
||||||
|
{
|
||||||
|
try_init(reporter).expect("Failed to install global reporter")
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Trait representing the functions required to emit information about various steps in the
|
||||||
|
/// compiler pipeline.
|
||||||
|
///
|
||||||
|
/// This trait provides a series of callbacks that are invoked at certain parts of the
|
||||||
|
/// [`crate::Project::compile()`] process.
|
||||||
|
///
|
||||||
|
/// Implementers of this trait can use these callbacks to emit additional information, for example
|
||||||
|
/// print custom messages to `stdout`.
|
||||||
|
///
|
||||||
|
/// A `Reporter` is entirely passive and only listens to incoming "events".
|
||||||
|
pub trait Reporter: 'static {
|
||||||
|
/// Callback invoked right before [`Solc::compile()`] is called
|
||||||
|
fn on_solc_spawn(&self, _solc: &Solc, _version: &Version, _input: &CompilerInput) {}
|
||||||
|
|
||||||
|
/// Invoked with the `CompilerOutput` if [`Solc::compiled()`] was successful
|
||||||
|
fn on_solc_success(&self, _solc: &Solc, _version: &Version, _output: &CompilerOutput) {}
|
||||||
|
|
||||||
|
/// Invoked before a new [`Solc`] bin is installed
|
||||||
|
fn on_solc_installation_start(&self, _version: &Version) {}
|
||||||
|
|
||||||
|
/// Invoked before a new [`Solc`] bin was successfully installed
|
||||||
|
fn on_solc_installation_success(&self, _version: &Version) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn solc_spawn(solc: &Solc, version: &Version, input: &CompilerInput) {
|
||||||
|
with_global(|r| r.reporter.on_solc_spawn(solc, version, input));
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn solc_success(solc: &Solc, version: &Version, output: &CompilerOutput) {
|
||||||
|
with_global(|r| r.reporter.on_solc_success(solc, version, output));
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn solc_installation_start(version: &Version) {
|
||||||
|
with_global(|r| r.reporter.on_solc_installation_start(version));
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn solc_installation_success(version: &Version) {
|
||||||
|
with_global(|r| r.reporter.on_solc_installation_success(version));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_global() -> Option<&'static Report> {
|
||||||
|
if GLOBAL_REPORTER_STATE.load(Ordering::SeqCst) != SET {
|
||||||
|
return None
|
||||||
|
}
|
||||||
|
unsafe {
|
||||||
|
// This is safe given the invariant that setting the global reporter
|
||||||
|
// also sets `GLOBAL_REPORTER_STATE` to `SET`.
|
||||||
|
Some(GLOBAL_REPORTER.as_ref().expect(
|
||||||
|
"Reporter invariant violated: GLOBAL_REPORTER must be initialized before GLOBAL_REPORTER_STATE is set",
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Executes a closure with a reference to the `Reporter`.
|
||||||
|
pub fn with_global<T>(f: impl FnOnce(&Report) -> T) -> Option<T> {
|
||||||
|
let dispatch = get_global()?;
|
||||||
|
Some(f(dispatch))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A no-op [`Reporter`] that does nothing.
|
||||||
|
#[derive(Copy, Clone, Debug, Default)]
|
||||||
|
pub struct NoReporter(());
|
||||||
|
|
||||||
|
impl Reporter for NoReporter {}
|
||||||
|
|
||||||
|
/// A [`Reporter`] that emits some general information to `stdout`
|
||||||
|
#[derive(Copy, Clone, Debug, Default)]
|
||||||
|
pub struct BasicStdoutReporter(());
|
||||||
|
|
||||||
|
impl Reporter for BasicStdoutReporter {
|
||||||
|
/// Callback invoked right before [`Solc::compile()`] is called
|
||||||
|
fn on_solc_spawn(&self, _solc: &Solc, version: &Version, input: &CompilerInput) {
|
||||||
|
println!(
|
||||||
|
"Compiling {} files with {}.{}.{}",
|
||||||
|
input.sources.len(),
|
||||||
|
version.major,
|
||||||
|
version.minor,
|
||||||
|
version.patch
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Invoked with the `CompilerOutput` if [`Solc::compiled()`] was successful
|
||||||
|
fn on_solc_success(&self, _: &Solc, _: &Version, _: &CompilerOutput) {
|
||||||
|
println!("Compilation finished successfully");
|
||||||
|
}
|
||||||
|
/// Invoked before a new [`Solc`] bin is installed
|
||||||
|
fn on_solc_installation_start(&self, version: &Version) {
|
||||||
|
println!("installing solc version \"{}\"", version);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Invoked before a new [`Solc`] bin was successfully installed
|
||||||
|
fn on_solc_installation_success(&self, version: &Version) {
|
||||||
|
println!("Successfully installed solc {}", version);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returned if setting the global reporter fails.
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct SetGlobalReporterError {
|
||||||
|
// private marker so this type can't be initiated
|
||||||
|
_priv: (),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for SetGlobalReporterError {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
f.pad("a global reporter has already been set")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Error for SetGlobalReporterError {}
|
||||||
|
|
||||||
|
/// `Report` trace data to a [`Reporter`].
|
||||||
|
pub struct Report {
|
||||||
|
reporter: Arc<dyn Reporter + Send + Sync>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Report {
|
||||||
|
/// Returns a new `Report` that does nothing
|
||||||
|
pub fn none() -> Self {
|
||||||
|
Report { reporter: Arc::new(NoReporter::default()) }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a `Report` that forwards to the given [`Reporter`].
|
||||||
|
///
|
||||||
|
/// [`Reporter`]: ../reporter/trait.Reporter.html
|
||||||
|
pub fn new<S>(reporter: S) -> Self
|
||||||
|
where
|
||||||
|
S: Reporter + Send + Sync + 'static,
|
||||||
|
{
|
||||||
|
Self { reporter: Arc::new(reporter) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// tracks the state of `GLOBAL_REPORTER`
|
||||||
|
static GLOBAL_REPORTER_STATE: AtomicUsize = AtomicUsize::new(UN_SET);
|
||||||
|
|
||||||
|
const UN_SET: usize = 0;
|
||||||
|
const SETTING: usize = 1;
|
||||||
|
const SET: usize = 2;
|
||||||
|
|
||||||
|
static mut GLOBAL_REPORTER: Option<Report> = None;
|
||||||
|
|
||||||
|
/// Sets this report as the global default for the duration of the entire program.
|
||||||
|
///
|
||||||
|
/// The global reporter can only be set once; additional attempts to set the global reporter will
|
||||||
|
/// fail. Returns `Err` if the global reporter has already been set.
|
||||||
|
fn set_global_reporter(report: Report) -> Result<(), SetGlobalReporterError> {
|
||||||
|
// `compare_exchange` tries to store `SETTING` if the current value is `UN_SET`
|
||||||
|
// this returns `Ok(_)` if the current value of `GLOBAL_REPORTER_STATE` was `UN_SET` and
|
||||||
|
// `SETTING` was written, this guarantees the value is `SETTING`.
|
||||||
|
if GLOBAL_REPORTER_STATE
|
||||||
|
.compare_exchange(UN_SET, SETTING, Ordering::SeqCst, Ordering::SeqCst)
|
||||||
|
.is_ok()
|
||||||
|
{
|
||||||
|
unsafe {
|
||||||
|
GLOBAL_REPORTER = Some(report);
|
||||||
|
}
|
||||||
|
GLOBAL_REPORTER_STATE.store(SET, Ordering::SeqCst);
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(SetGlobalReporterError { _priv: () })
|
||||||
|
}
|
||||||
|
}
|
|
@ -655,6 +655,7 @@ struct SolData {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SolData {
|
impl SolData {
|
||||||
|
#[allow(unused)]
|
||||||
fn fmt_version<W: std::fmt::Write>(
|
fn fmt_version<W: std::fmt::Write>(
|
||||||
&self,
|
&self,
|
||||||
f: &mut W,
|
f: &mut W,
|
||||||
|
|
Loading…
Reference in New Issue