1use crate::{
2 artifacts::Source,
3 error::{Result, SolcError},
4 utils, CompilerInput, CompilerOutput,
5};
6use once_cell::sync::Lazy;
7use semver::{Version, VersionReq};
8use serde::{de::DeserializeOwned, Deserialize, Serialize};
9use std::{
10 fmt,
11 path::{Path, PathBuf},
12 process::{Command, Output, Stdio},
13 str::FromStr,
14};
15pub mod many;
16pub mod output;
17pub use output::{contracts, info, sources};
18pub mod project;
19
20pub const SOLC: &str = "solc";
22
23pub const BYZANTIUM_SOLC: Version = Version::new(0, 4, 21);
26
27pub const CONSTANTINOPLE_SOLC: Version = Version::new(0, 4, 22);
30
31pub const PETERSBURG_SOLC: Version = Version::new(0, 5, 5);
34
35pub const ISTANBUL_SOLC: Version = Version::new(0, 5, 14);
38
39pub const BERLIN_SOLC: Version = Version::new(0, 8, 5);
42
43pub const LONDON_SOLC: Version = Version::new(0, 8, 7);
46
47pub const PARIS_SOLC: Version = Version::new(0, 8, 18);
50
51pub const SHANGHAI_SOLC: Version = Version::new(0, 8, 20);
54
55pub static SUPPORTS_BASE_PATH: Lazy<VersionReq> =
57 Lazy::new(|| VersionReq::parse(">=0.6.9").unwrap());
58
59pub static SUPPORTS_INCLUDE_PATH: Lazy<VersionReq> =
61 Lazy::new(|| VersionReq::parse(">=0.8.8").unwrap());
62
63#[cfg(any(test, feature = "tests"))]
64use std::sync::Mutex;
65
66#[cfg(any(test, feature = "tests"))]
67#[allow(unused)]
68static LOCK: Lazy<Mutex<()>> = Lazy::new(|| Mutex::new(()));
69
70#[cfg(any(test, feature = "tests"))]
77#[allow(unused)]
78pub(crate) fn take_solc_installer_lock() -> std::sync::MutexGuard<'static, ()> {
79 LOCK.lock().unwrap()
80}
81
82#[cfg(all(feature = "svm-solc", not(target_arch = "wasm32")))]
86pub static RELEASES: Lazy<(svm::Releases, Vec<Version>, bool)> =
87 Lazy::new(|| match serde_json::from_str::<svm::Releases>(svm_builds::RELEASE_LIST_JSON) {
88 Ok(releases) => {
89 let sorted_versions = releases.clone().into_versions();
90 (releases, sorted_versions, true)
91 }
92 Err(err) => {
93 tracing::error!("{:?}", err);
94 Default::default()
95 }
96 });
97
98#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Serialize, Deserialize)]
101#[serde(untagged)]
102pub enum SolcVersion {
103 Installed(Version),
104 Remote(Version),
105}
106
107impl SolcVersion {
108 pub fn is_installed(&self) -> bool {
110 matches!(self, SolcVersion::Installed(_))
111 }
112}
113
114impl AsRef<Version> for SolcVersion {
115 fn as_ref(&self) -> &Version {
116 match self {
117 SolcVersion::Installed(v) | SolcVersion::Remote(v) => v,
118 }
119 }
120}
121
122impl From<SolcVersion> for Version {
123 fn from(s: SolcVersion) -> Version {
124 match s {
125 SolcVersion::Installed(v) | SolcVersion::Remote(v) => v,
126 }
127 }
128}
129
130impl fmt::Display for SolcVersion {
131 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
132 write!(f, "{}", self.as_ref())
133 }
134}
135
136#[derive(Debug, Clone, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
146pub struct Solc {
147 pub solc: PathBuf,
149 pub base_path: Option<PathBuf>,
151 pub args: Vec<String>,
153}
154
155impl Default for Solc {
156 fn default() -> Self {
157 if let Ok(solc) = std::env::var("SOLC_PATH") {
158 return Solc::new(solc)
159 }
160 #[cfg(not(target_arch = "wasm32"))]
161 {
162 if let Some(solc) = Solc::svm_global_version()
163 .and_then(|vers| Solc::find_svm_installed_version(vers.to_string()).ok())
164 .flatten()
165 {
166 return solc
167 }
168 }
169
170 Solc::new(SOLC)
171 }
172}
173
174impl fmt::Display for Solc {
175 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
176 write!(f, "{}", self.solc.display())?;
177 if !self.args.is_empty() {
178 write!(f, " {}", self.args.join(" "))?;
179 }
180 Ok(())
181 }
182}
183
184impl Solc {
185 pub fn new(path: impl Into<PathBuf>) -> Self {
187 Solc { solc: path.into(), base_path: None, args: Vec::new() }
188 }
189
190 pub fn with_base_path(mut self, base_path: impl Into<PathBuf>) -> Self {
194 self.base_path = Some(base_path.into());
195 self
196 }
197
198 #[must_use]
200 pub fn arg<T: Into<String>>(mut self, arg: T) -> Self {
201 self.args.push(arg.into());
202 self
203 }
204
205 #[must_use]
207 pub fn args<I, S>(mut self, args: I) -> Self
208 where
209 I: IntoIterator<Item = S>,
210 S: Into<String>,
211 {
212 for arg in args {
213 self = self.arg(arg);
214 }
215 self
216 }
217
218 #[cfg(not(target_arch = "wasm32"))]
224 pub fn svm_home() -> Option<PathBuf> {
225 match home::home_dir().map(|dir| dir.join(".svm")) {
226 Some(dir) => {
227 if !dir.exists() {
228 dirs::data_dir().map(|dir| dir.join("svm"))
229 } else {
230 Some(dir)
231 }
232 }
233 None => dirs::data_dir().map(|dir| dir.join("svm")),
234 }
235 }
236
237 #[cfg(not(target_arch = "wasm32"))]
243 pub fn svm_global_version() -> Option<Version> {
244 let home = Self::svm_home()?;
245 let version = std::fs::read_to_string(home.join(".global_version")).ok()?;
246 Version::parse(&version).ok()
247 }
248
249 #[cfg(not(target_arch = "wasm32"))]
251 pub fn installed_versions() -> Vec<SolcVersion> {
252 Self::svm_home()
253 .map(|home| {
254 utils::installed_versions(home)
255 .unwrap_or_default()
256 .into_iter()
257 .map(SolcVersion::Installed)
258 .collect()
259 })
260 .unwrap_or_default()
261 }
262
263 #[cfg(all(feature = "svm-solc", not(target_arch = "wasm32")))]
266 pub fn all_versions() -> Vec<SolcVersion> {
267 let mut all_versions = Self::installed_versions();
268 let mut uniques = all_versions
269 .iter()
270 .map(|v| {
271 let v = v.as_ref();
272 (v.major, v.minor, v.patch)
273 })
274 .collect::<std::collections::HashSet<_>>();
275 all_versions.extend(
276 RELEASES
277 .1
278 .clone()
279 .into_iter()
280 .filter(|v| uniques.insert((v.major, v.minor, v.patch)))
281 .map(SolcVersion::Remote),
282 );
283 all_versions.sort_unstable();
284 all_versions
285 }
286
287 #[cfg(not(target_arch = "wasm32"))]
299 pub fn find_svm_installed_version(version: impl AsRef<str>) -> Result<Option<Self>> {
300 let version = version.as_ref();
301 let solc = Self::svm_home()
302 .ok_or_else(|| SolcError::msg("svm home dir not found"))?
303 .join(version)
304 .join(format!("solc-{version}"));
305
306 if !solc.is_file() {
307 return Ok(None)
308 }
309 Ok(Some(Solc::new(solc)))
310 }
311
312 #[cfg(all(not(target_arch = "wasm32"), feature = "svm-solc"))]
326 pub fn find_or_install_svm_version(version: impl AsRef<str>) -> Result<Self> {
327 let version = version.as_ref();
328 if let Some(solc) = Solc::find_svm_installed_version(version)? {
329 Ok(solc)
330 } else {
331 Ok(Solc::blocking_install(&version.parse::<Version>()?)?)
332 }
333 }
334
335 pub fn find_matching_installation(
338 versions: &[Version],
339 required_version: &VersionReq,
340 ) -> Option<Version> {
341 versions.iter().rev().find(|version| required_version.matches(version)).cloned()
343 }
344
345 #[cfg(all(feature = "svm-solc", not(target_arch = "wasm32")))]
350 pub fn detect_version(source: &Source) -> Result<Version> {
351 let sol_version = Self::source_version_req(source)?;
353 Self::ensure_installed(&sol_version)
354 }
355
356 #[cfg(all(feature = "svm-solc", not(target_arch = "wasm32")))]
361 pub fn ensure_installed(sol_version: &VersionReq) -> Result<Version> {
362 #[cfg(any(test, feature = "tests"))]
363 let _lock = take_solc_installer_lock();
364
365 let versions = utils::installed_versions(svm::SVM_DATA_DIR.as_path()).unwrap_or_default();
367
368 let local_versions = Self::find_matching_installation(&versions, sol_version);
369 let remote_versions = Self::find_matching_installation(&RELEASES.1, sol_version);
370 Ok(match (local_versions, remote_versions) {
372 (Some(local), None) => local,
373 (Some(local), Some(remote)) => {
374 if remote > local {
375 Self::blocking_install(&remote)?;
376 remote
377 } else {
378 local
379 }
380 }
381 (None, Some(version)) => {
382 Self::blocking_install(&version)?;
383 version
384 }
385 _ => return Err(SolcError::VersionNotFound),
387 })
388 }
389
390 pub fn source_version_req(source: &Source) -> Result<VersionReq> {
393 let version =
394 utils::find_version_pragma(&source.content).ok_or(SolcError::PragmaNotFound)?;
395 Self::version_req(version.as_str())
396 }
397
398 pub fn version_req(version: &str) -> Result<VersionReq> {
403 let version = version.replace(' ', ",");
404
405 let exact = !matches!(&version[0..1], "*" | "^" | "=" | ">" | "<" | "~");
409 let mut version = VersionReq::parse(&version)?;
410 if exact {
411 version.comparators[0].op = semver::Op::Exact;
412 }
413
414 Ok(version)
415 }
416
417 #[cfg(all(feature = "svm-solc", not(target_arch = "wasm32")))]
429 pub async fn install(version: &Version) -> std::result::Result<Self, svm::SolcVmError> {
430 tracing::trace!("installing solc version \"{}\"", version);
431 crate::report::solc_installation_start(version);
432 let result = svm::install(version).await;
433 crate::report::solc_installation_success(version);
434 result.map(Solc::new)
435 }
436
437 #[cfg(all(feature = "svm-solc", not(target_arch = "wasm32")))]
439 pub fn blocking_install(version: &Version) -> std::result::Result<Self, svm::SolcVmError> {
440 use crate::utils::RuntimeOrHandle;
441
442 tracing::trace!("blocking installing solc version \"{}\"", version);
443 crate::report::solc_installation_start(version);
444 cfg_if::cfg_if! {
448 if #[cfg(target_arch = "wasm32")] {
449 let installation = svm::blocking_install(version);
450 } else {
451 let installation = RuntimeOrHandle::new().block_on(svm::install(version));
452 }
453 };
454 match installation {
455 Ok(path) => {
456 crate::report::solc_installation_success(version);
457 Ok(Solc::new(path))
458 }
459 Err(err) => {
460 crate::report::solc_installation_error(version, &err.to_string());
461 Err(err)
462 }
463 }
464 }
465
466 #[cfg(all(feature = "svm-solc", not(target_arch = "wasm32")))]
469 pub fn verify_checksum(&self) -> Result<()> {
470 let version = self.version_short()?;
471 let mut version_path = svm::version_path(version.to_string().as_str());
472 version_path.push(format!("solc-{}", version.to_string().as_str()));
473 tracing::trace!(target:"solc", "reading solc binary for checksum {:?}", version_path);
474 let content =
475 std::fs::read(&version_path).map_err(|err| SolcError::io(err, version_path.clone()))?;
476
477 if !RELEASES.2 {
478 return Ok(())
481 }
482
483 #[cfg(windows)]
484 {
485 const V0_7_2: Version = Version::new(0, 7, 2);
488 if version < V0_7_2 {
489 return Ok(())
490 }
491 }
492
493 use sha2::Digest;
494 let mut hasher = sha2::Sha256::new();
495 hasher.update(content);
496 let checksum_calc = &hasher.finalize()[..];
497
498 let checksum_found = &RELEASES
499 .0
500 .get_checksum(&version)
501 .ok_or_else(|| SolcError::ChecksumNotFound { version: version.clone() })?;
502
503 if checksum_calc == checksum_found {
504 Ok(())
505 } else {
506 let expected = hex::encode(checksum_found);
507 let detected = hex::encode(checksum_calc);
508 tracing:: warn!(target : "solc", "checksum mismatch for {:?}, expected {}, but found {} for file {:?}", version, expected, detected, version_path);
509 Err(SolcError::ChecksumMismatch { version, expected, detected, file: version_path })
510 }
511 }
512
513 pub fn compile_source(&self, path: impl AsRef<Path>) -> Result<CompilerOutput> {
515 let path = path.as_ref();
516 let mut res: CompilerOutput = Default::default();
517 for input in CompilerInput::new(path)? {
518 let output = self.compile(&input)?;
519 res.merge(output)
520 }
521 Ok(res)
522 }
523
524 pub fn compile_exact(&self, input: &CompilerInput) -> Result<CompilerOutput> {
542 let mut out = self.compile(input)?;
543 out.retain_files(input.sources.keys().filter_map(|p| p.to_str()));
544 Ok(out)
545 }
546
547 pub fn compile<T: Serialize>(&self, input: &T) -> Result<CompilerOutput> {
562 self.compile_as(input)
563 }
564
565 pub fn compile_as<T: Serialize, D: DeserializeOwned>(&self, input: &T) -> Result<D> {
568 let output = self.compile_output(input)?;
569 Ok(serde_json::from_slice(&output)?)
570 }
571
572 pub fn compile_output<T: Serialize>(&self, input: &T) -> Result<Vec<u8>> {
573 let mut cmd = Command::new(&self.solc);
574 if let Some(ref base_path) = self.base_path {
575 cmd.current_dir(base_path);
576 cmd.arg("--base-path").arg(base_path);
577 }
578 let mut child = cmd
579 .args(&self.args)
580 .arg("--standard-json")
581 .stdin(Stdio::piped())
582 .stderr(Stdio::piped())
583 .stdout(Stdio::piped())
584 .spawn()
585 .map_err(|err| SolcError::io(err, &self.solc))?;
586 let stdin = child.stdin.take().expect("Stdin exists.");
587 serde_json::to_writer(stdin, input)?;
588 compile_output(child.wait_with_output().map_err(|err| SolcError::io(err, &self.solc))?)
589 }
590
591 pub fn version_short(&self) -> Result<Version> {
592 let version = self.version()?;
593 Ok(Version::new(version.major, version.minor, version.patch))
594 }
595
596 pub fn version(&self) -> Result<Version> {
598 version_from_output(
599 Command::new(&self.solc)
600 .arg("--version")
601 .stdin(Stdio::piped())
602 .stderr(Stdio::piped())
603 .stdout(Stdio::piped())
604 .output()
605 .map_err(|err| SolcError::io(err, &self.solc))?,
606 )
607 }
608}
609
610#[cfg(feature = "async")]
611impl Solc {
612 pub async fn async_compile_source(&self, path: impl AsRef<Path>) -> Result<CompilerOutput> {
614 self.async_compile(&CompilerInput::with_sources(Source::async_read_all_from(path).await?))
615 .await
616 }
617
618 pub async fn async_compile<T: Serialize>(&self, input: &T) -> Result<CompilerOutput> {
621 self.async_compile_as(input).await
622 }
623
624 pub async fn async_compile_as<T: Serialize, D: DeserializeOwned>(
627 &self,
628 input: &T,
629 ) -> Result<D> {
630 let output = self.async_compile_output(input).await?;
631 Ok(serde_json::from_slice(&output)?)
632 }
633
634 pub async fn async_compile_output<T: Serialize>(&self, input: &T) -> Result<Vec<u8>> {
635 use tokio::io::AsyncWriteExt;
636 let content = serde_json::to_vec(input)?;
637 let mut cmd = tokio::process::Command::new(&self.solc);
638 if let Some(ref base_path) = self.base_path {
639 cmd.current_dir(base_path);
640 }
641 let mut child = cmd
642 .args(&self.args)
643 .arg("--standard-json")
644 .stdin(Stdio::piped())
645 .stderr(Stdio::piped())
646 .stdout(Stdio::piped())
647 .spawn()
648 .map_err(|err| SolcError::io(err, &self.solc))?;
649 let stdin = child.stdin.as_mut().unwrap();
650 stdin.write_all(&content).await.map_err(|err| SolcError::io(err, &self.solc))?;
651 stdin.flush().await.map_err(|err| SolcError::io(err, &self.solc))?;
652 compile_output(
653 child.wait_with_output().await.map_err(|err| SolcError::io(err, &self.solc))?,
654 )
655 }
656
657 pub async fn async_version(&self) -> Result<Version> {
658 version_from_output(
659 tokio::process::Command::new(&self.solc)
660 .arg("--version")
661 .stdin(Stdio::piped())
662 .stderr(Stdio::piped())
663 .stdout(Stdio::piped())
664 .spawn()
665 .map_err(|err| SolcError::io(err, &self.solc))?
666 .wait_with_output()
667 .await
668 .map_err(|err| SolcError::io(err, &self.solc))?,
669 )
670 }
671
672 pub async fn compile_many<I>(jobs: I, n: usize) -> crate::many::CompiledMany
694 where
695 I: IntoIterator<Item = (Solc, CompilerInput)>,
696 {
697 use futures_util::stream::StreamExt;
698
699 let outputs = futures_util::stream::iter(
700 jobs.into_iter()
701 .map(|(solc, input)| async { (solc.async_compile(&input).await, solc, input) }),
702 )
703 .buffer_unordered(n)
704 .collect::<Vec<_>>()
705 .await;
706
707 crate::many::CompiledMany::new(outputs)
708 }
709}
710
711fn compile_output(output: Output) -> Result<Vec<u8>> {
712 if output.status.success() {
713 Ok(output.stdout)
714 } else {
715 Err(SolcError::solc_output(&output))
716 }
717}
718
719fn version_from_output(output: Output) -> Result<Version> {
720 if output.status.success() {
721 let stdout = String::from_utf8_lossy(&output.stdout);
722 let version = stdout
723 .lines()
724 .filter(|l| !l.trim().is_empty())
725 .last()
726 .ok_or_else(|| SolcError::msg("Version not found in Solc output"))?;
727 Ok(Version::from_str(&version.trim_start_matches("Version: ").replace(".g++", ".gcc"))?)
729 } else {
730 Err(SolcError::solc_output(&output))
731 }
732}
733
734impl AsRef<Path> for Solc {
735 fn as_ref(&self) -> &Path {
736 &self.solc
737 }
738}
739
740impl<T: Into<PathBuf>> From<T> for Solc {
741 fn from(solc: T) -> Self {
742 Solc::new(solc.into())
743 }
744}
745
746#[cfg(test)]
747mod tests {
748 use super::*;
749 use crate::Artifact;
750
751 #[test]
752 fn test_version_parse() {
753 let req = Solc::version_req(">=0.6.2 <0.8.21").unwrap();
754 let semver_req: VersionReq = ">=0.6.2,<0.8.21".parse().unwrap();
755 assert_eq!(req, semver_req);
756 }
757
758 fn solc() -> Solc {
759 Solc::default()
760 }
761
762 #[test]
763 fn solc_version_works() {
764 solc().version().unwrap();
765 }
766
767 #[test]
768 fn can_parse_version_metadata() {
769 let _version = Version::from_str("0.6.6+commit.6c089d02.Linux.gcc").unwrap();
770 }
771
772 #[cfg(feature = "async")]
773 #[tokio::test]
774 async fn async_solc_version_works() {
775 let _version = solc().async_version().await.unwrap();
776 }
777
778 #[test]
779 fn solc_compile_works() {
780 let input = include_str!("../../test-data/in/compiler-in-1.json");
781 let input: CompilerInput = serde_json::from_str(input).unwrap();
782 let out = solc().compile(&input).unwrap();
783 let other = solc().compile(&serde_json::json!(input)).unwrap();
784 assert_eq!(out, other);
785 }
786
787 #[test]
788 fn solc_metadata_works() {
789 let input = include_str!("../../test-data/in/compiler-in-1.json");
790 let mut input: CompilerInput = serde_json::from_str(input).unwrap();
791 input.settings.push_output_selection("metadata");
792 let out = solc().compile(&input).unwrap();
793 for (_, c) in out.split().1.contracts_iter() {
794 assert!(c.metadata.is_some());
795 }
796 }
797
798 #[test]
799 fn can_compile_with_remapped_links() {
800 let input: CompilerInput =
801 serde_json::from_str(include_str!("../../test-data/library-remapping-in.json"))
802 .unwrap();
803 let out = solc().compile(&input).unwrap();
804 let (_, mut contracts) = out.split();
805 let contract = contracts.remove("LinkTest").unwrap();
806 let bytecode = &contract.get_bytecode().unwrap().object;
807 assert!(!bytecode.is_unlinked());
808 }
809
810 #[test]
811 fn can_compile_with_remapped_links_temp_dir() {
812 let input: CompilerInput =
813 serde_json::from_str(include_str!("../../test-data/library-remapping-in-2.json"))
814 .unwrap();
815 let out = solc().compile(&input).unwrap();
816 let (_, mut contracts) = out.split();
817 let contract = contracts.remove("LinkTest").unwrap();
818 let bytecode = &contract.get_bytecode().unwrap().object;
819 assert!(!bytecode.is_unlinked());
820 }
821
822 #[cfg(feature = "async")]
823 #[tokio::test]
824 async fn async_solc_compile_works() {
825 let input = include_str!("../../test-data/in/compiler-in-1.json");
826 let input: CompilerInput = serde_json::from_str(input).unwrap();
827 let out = solc().async_compile(&input).await.unwrap();
828 let other = solc().async_compile(&serde_json::json!(input)).await.unwrap();
829 assert_eq!(out, other);
830 }
831
832 #[cfg(feature = "async")]
833 #[tokio::test]
834 async fn async_solc_compile_works2() {
835 let input = include_str!("../../test-data/in/compiler-in-2.json");
836 let input: CompilerInput = serde_json::from_str(input).unwrap();
837 let out = solc().async_compile(&input).await.unwrap();
838 let other = solc().async_compile(&serde_json::json!(input)).await.unwrap();
839 assert_eq!(out, other);
840 let sync_out = solc().compile(&input).unwrap();
841 assert_eq!(out, sync_out);
842 }
843
844 #[test]
845 fn test_version_req() {
846 let versions = ["=0.1.2", "^0.5.6", ">=0.7.1", ">0.8.0"];
847 let sources = versions.iter().map(|version| source(version));
848
849 sources.zip(versions).for_each(|(source, version)| {
850 let version_req = Solc::source_version_req(&source).unwrap();
851 assert_eq!(version_req, VersionReq::from_str(version).unwrap());
852 });
853
854 let version_range = ">=0.8.0 <0.9.0";
857 let source = source(version_range);
858 let version_req = Solc::source_version_req(&source).unwrap();
859 assert_eq!(version_req, VersionReq::from_str(">=0.8.0,<0.9.0").unwrap());
860 }
861
862 #[test]
863 #[cfg(all(feature = "svm-solc", not(target_arch = "wasm32")))]
865 fn test_detect_version() {
866 for (pragma, expected) in [
867 ("=0.4.14", "0.4.14"),
869 ("0.4.14", "0.4.14"),
871 ("^0.4.14", "0.4.26"),
873 (">=0.4.0 <0.5.0", "0.4.26"),
875 (">=0.5.0", "0.8.24"),
878 ] {
879 let source = source(pragma);
880 let res = Solc::detect_version(&source).unwrap();
881 assert_eq!(res, Version::from_str(expected).unwrap());
882 }
883 }
884
885 #[test]
886 #[cfg(feature = "full")]
887 fn test_find_installed_version_path() {
888 let _lock = LOCK.lock();
891 let ver = "0.8.6";
892 let version = Version::from_str(ver).unwrap();
893 if utils::installed_versions(svm::SVM_DATA_DIR.as_path())
894 .map(|versions| !versions.contains(&version))
895 .unwrap_or_default()
896 {
897 Solc::blocking_install(&version).unwrap();
898 }
899 let res = Solc::find_svm_installed_version(version.to_string()).unwrap().unwrap();
900 let expected = svm::SVM_DATA_DIR.join(ver).join(format!("solc-{ver}"));
901 assert_eq!(res.solc, expected);
902 }
903
904 #[test]
905 #[cfg(all(feature = "svm-solc", not(target_arch = "wasm32")))]
906 fn can_install_solc_in_tokio_rt() {
907 let version = Version::from_str("0.8.6").unwrap();
908 let rt = tokio::runtime::Runtime::new().unwrap();
909 let result = rt.block_on(async { Solc::blocking_install(&version) });
910 assert!(result.is_ok());
911 }
912
913 #[test]
914 fn does_not_find_not_installed_version() {
915 let ver = "1.1.1";
916 let version = Version::from_str(ver).unwrap();
917 let res = Solc::find_svm_installed_version(version.to_string()).unwrap();
918 assert!(res.is_none());
919 }
920
921 #[test]
922 fn test_find_latest_matching_installation() {
923 let versions = ["0.4.24", "0.5.1", "0.5.2"]
924 .iter()
925 .map(|version| Version::from_str(version).unwrap())
926 .collect::<Vec<_>>();
927
928 let required = VersionReq::from_str(">=0.4.24").unwrap();
929
930 let got = Solc::find_matching_installation(&versions, &required).unwrap();
931 assert_eq!(got, versions[2]);
932 }
933
934 #[test]
935 fn test_no_matching_installation() {
936 let versions = ["0.4.24", "0.5.1", "0.5.2"]
937 .iter()
938 .map(|version| Version::from_str(version).unwrap())
939 .collect::<Vec<_>>();
940
941 let required = VersionReq::from_str(">=0.6.0").unwrap();
942 let got = Solc::find_matching_installation(&versions, &required);
943 assert!(got.is_none());
944 }
945
946 fn source(version: &str) -> Source {
949 Source::new(format!("pragma solidity {version};\n"))
950 }
951}