Skip to content

Commit 217c943

Browse files
authored
chore: add more tracing around spawning Solc (#57)
1 parent 2524fb7 commit 217c943

File tree

3 files changed

+64
-51
lines changed

3 files changed

+64
-51
lines changed

.github/workflows/ci.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ jobs:
3737
shell: bash
3838
run: |
3939
cargo nextest run \
40-
${{ matrix.flags.flags }} \
40+
${{ matrix.flags }} \
4141
-E "!(kind(test))" \
4242
--retries 2
4343

src/compile/mod.rs

Lines changed: 61 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -546,66 +546,84 @@ impl Solc {
546546
Ok(out)
547547
}
548548

549-
/// Run `solc --stand-json` and return the `solc`'s output as
550-
/// `CompilerOutput`
549+
/// Compiles with `--standard-json` and deserializes the output as [`CompilerOutput`].
551550
///
552551
/// # Example
553552
///
554553
/// ```no_run
555-
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
556554
/// use foundry_compilers::{CompilerInput, Solc};
555+
///
557556
/// let solc = Solc::default();
558557
/// let input = CompilerInput::new("./contracts")?;
559558
/// let output = solc.compile(&input)?;
560-
/// # Ok(())
561-
/// # }
559+
/// # Ok::<_, foundry_compilers::error::SolcError>(())
562560
/// ```
563-
pub fn compile<T: Serialize>(&self, input: &T) -> Result<CompilerOutput> {
561+
pub fn compile<T: Serialize + std::fmt::Debug>(&self, input: &T) -> Result<CompilerOutput> {
564562
self.compile_as(input)
565563
}
566564

567-
/// Run `solc --stand-json` and return the `solc`'s output as the given json
568-
/// output
569-
pub fn compile_as<T: Serialize, D: DeserializeOwned>(&self, input: &T) -> Result<D> {
565+
/// Compiles with `--standard-json` and deserializes the output as the given `D`.
566+
pub fn compile_as<T: Serialize + std::fmt::Debug, D: DeserializeOwned>(
567+
&self,
568+
input: &T,
569+
) -> Result<D> {
570570
let output = self.compile_output(input)?;
571-
Ok(serde_json::from_slice(&output)?)
571+
572+
// Only run UTF-8 validation once.
573+
let output = std::str::from_utf8(&output).map_err(|_| SolcError::InvalidUtf8)?;
574+
575+
Ok(serde_json::from_str(output)?)
572576
}
573577

574-
pub fn compile_output<T: Serialize>(&self, input: &T) -> Result<Vec<u8>> {
578+
/// Compiles with `--standard-json` and returns the raw `stdout` output.
579+
#[instrument(name = "compile", level = "debug", skip_all)]
580+
pub fn compile_output<T: Serialize + std::fmt::Debug>(&self, input: &T) -> Result<Vec<u8>> {
575581
let mut cmd = Command::new(&self.solc);
576-
if let Some(ref base_path) = self.base_path {
582+
if let Some(base_path) = &self.base_path {
577583
cmd.current_dir(base_path);
578584
cmd.arg("--base-path").arg(base_path);
579585
}
580-
let mut child = cmd
581-
.args(&self.args)
582-
.arg("--standard-json")
583-
.stdin(Stdio::piped())
584-
.stderr(Stdio::piped())
585-
.stdout(Stdio::piped())
586-
.spawn()
587-
.map_err(|err| SolcError::io(err, &self.solc))?;
588-
let stdin = child.stdin.take().expect("Stdin exists.");
586+
cmd.args(&self.args).arg("--standard-json");
587+
cmd.stdin(Stdio::piped()).stderr(Stdio::piped()).stdout(Stdio::piped());
588+
589+
trace!(?input);
590+
debug!(?cmd, "compiling");
591+
592+
let mut child = cmd.spawn().map_err(self.map_io_err())?;
593+
debug!("spawned");
594+
595+
let stdin = child.stdin.as_mut().unwrap();
589596
serde_json::to_writer(stdin, input)?;
590-
compile_output(child.wait_with_output().map_err(|err| SolcError::io(err, &self.solc))?)
597+
debug!("wrote JSON input to stdin");
598+
599+
let output = child.wait_with_output().map_err(self.map_io_err())?;
600+
debug!(%output.status, output.stderr = ?String::from_utf8_lossy(&output.stderr), "finished");
601+
602+
compile_output(output)
591603
}
592604

605+
/// Invokes `solc --version` and parses the output as a SemVer [`Version`], stripping the
606+
/// pre-release and build metadata.
593607
pub fn version_short(&self) -> Result<Version> {
594608
let version = self.version()?;
595609
Ok(Version::new(version.major, version.minor, version.patch))
596610
}
597611

598-
/// Returns the version from the configured `solc`
612+
/// Invokes `solc --version` and parses the output as a SemVer [`Version`].
613+
#[instrument(level = "debug", skip_all)]
599614
pub fn version(&self) -> Result<Version> {
600-
version_from_output(
601-
Command::new(&self.solc)
602-
.arg("--version")
603-
.stdin(Stdio::piped())
604-
.stderr(Stdio::piped())
605-
.stdout(Stdio::piped())
606-
.output()
607-
.map_err(|err| SolcError::io(err, &self.solc))?,
608-
)
615+
let mut cmd = Command::new(&self.solc);
616+
cmd.arg("--version").stdin(Stdio::piped()).stderr(Stdio::piped()).stdout(Stdio::piped());
617+
debug!(?cmd, "getting Solc version");
618+
let output = cmd.output().map_err(self.map_io_err())?;
619+
trace!(?output);
620+
let version = version_from_output(output)?;
621+
debug!(%version);
622+
Ok(version)
623+
}
624+
625+
fn map_io_err(&self) -> impl FnOnce(std::io::Error) -> SolcError + '_ {
626+
move |err| SolcError::io(err, &self.solc)
609627
}
610628
}
611629

@@ -647,28 +665,21 @@ impl Solc {
647665
.stderr(Stdio::piped())
648666
.stdout(Stdio::piped())
649667
.spawn()
650-
.map_err(|err| SolcError::io(err, &self.solc))?;
668+
.map_err(self.map_io_err())?;
651669
let stdin = child.stdin.as_mut().unwrap();
652-
stdin.write_all(&content).await.map_err(|err| SolcError::io(err, &self.solc))?;
653-
stdin.flush().await.map_err(|err| SolcError::io(err, &self.solc))?;
654-
compile_output(
655-
child.wait_with_output().await.map_err(|err| SolcError::io(err, &self.solc))?,
656-
)
670+
stdin.write_all(&content).await.map_err(self.map_io_err())?;
671+
stdin.flush().await.map_err(self.map_io_err())?;
672+
compile_output(child.wait_with_output().await.map_err(self.map_io_err())?)
657673
}
658674

659675
pub async fn async_version(&self) -> Result<Version> {
660-
version_from_output(
661-
tokio::process::Command::new(&self.solc)
662-
.arg("--version")
663-
.stdin(Stdio::piped())
664-
.stderr(Stdio::piped())
665-
.stdout(Stdio::piped())
666-
.spawn()
667-
.map_err(|err| SolcError::io(err, &self.solc))?
668-
.wait_with_output()
669-
.await
670-
.map_err(|err| SolcError::io(err, &self.solc))?,
671-
)
676+
let mut cmd = tokio::process::Command::new(&self.solc);
677+
cmd.arg("--version").stdin(Stdio::piped()).stderr(Stdio::piped()).stdout(Stdio::piped());
678+
debug!(?cmd, "getting version");
679+
let output = cmd.output().await.map_err(self.map_io_err())?;
680+
let version = version_from_output(output)?;
681+
debug!(%version);
682+
Ok(version)
672683
}
673684

674685
/// Compiles all `CompilerInput`s with their associated `Solc`.

src/error.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ pub enum SolcError {
2525
/// Errors related to the Solc executable itself.
2626
#[error("solc exited with {0}\n{1}")]
2727
SolcError(std::process::ExitStatus, String),
28+
#[error("invalid UTF-8 in Solc output")]
29+
InvalidUtf8,
2830
#[error("missing pragma from Solidity file")]
2931
PragmaNotFound,
3032
#[error("could not find Solc version locally or upstream")]

0 commit comments

Comments
 (0)