diff --git a/Cargo.lock b/Cargo.lock index 495f4036..c79d7fa4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -752,6 +752,7 @@ dependencies = [ "tracing", "tracing-chrome", "tracing-subscriber", + "which", ] [[package]] @@ -1250,6 +1251,15 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" +[[package]] +name = "home" +version = "0.5.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5" +dependencies = [ + "windows-sys 0.52.0", +] + [[package]] name = "icu_collections" version = "1.5.0" @@ -2872,6 +2882,18 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "which" +version = "7.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9cad3279ade7346b96e38731a641d7343dd6a53d55083dd54eadfa5a1b38c6b" +dependencies = [ + "either", + "home", + "rustix", + "winsafe", +] + [[package]] name = "winapi" version = "0.3.9" @@ -3060,6 +3082,12 @@ dependencies = [ "memchr", ] +[[package]] +name = "winsafe" +version = "0.0.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d135d17ab770252ad95e9a872d365cf3090e3be864a34ab46f48555993efc904" + [[package]] name = "write16" version = "1.0.0" diff --git a/Cargo.toml b/Cargo.toml index 0884b626..1d4b2b01 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -49,6 +49,7 @@ tempfile = "3" tracing = "0.1" tracing-chrome = "0.7.1" tracing-subscriber = { version = "0.3", features = ["env-filter"] } +which = "7.0.0" [target.'cfg(target_vendor = "apple")'.dependencies] libc = "0.2.155" diff --git a/src/project/unmanaged_core_crate.rs b/src/project/unmanaged_core_crate.rs index 0bce9420..d9f9b552 100644 --- a/src/project/unmanaged_core_crate.rs +++ b/src/project/unmanaged_core_crate.rs @@ -33,13 +33,6 @@ pub fn try_to_init_unmanaged_core( pub fn find_unmanaged_core(config: &Config, scarb: &ScarbToolchain) -> Option { find_core_at_config_path(config) .or_else(|| find_scarb_managed_core(scarb)) - .or_else(|| { - if cfg!(feature = "testing") { - cairo_lang_filesystem::detect::detect_corelib() - } else { - None - } - }) .and_then(ensure_absolute) } diff --git a/src/toolchain/scarb.rs b/src/toolchain/scarb.rs index d52fead5..ef315ee2 100644 --- a/src/toolchain/scarb.rs +++ b/src/toolchain/scarb.rs @@ -6,6 +6,7 @@ use anyhow::{Context, Result, bail}; use lsp_types::notification::Notification; use scarb_metadata::{Metadata, MetadataCommand}; use tracing::{error, warn}; +use which::which; use crate::env_config; use crate::lsp::ext::ScarbMetadataFailed; @@ -47,6 +48,15 @@ impl ScarbToolchain { fn discover(&self) -> Option<&Path> { self.scarb_path_cell .get_or_init(|| { + // While running tests, we do not have SCARB env set, + // but we expect `scarb` binary to be in the PATH. + if cfg!(feature = "testing") { + return Some( + which("scarb") + .expect("running tests requires a `scarb` binary available in `PATH`"), + ); + } + let path = env_config::scarb_path(); // TODO(mkaput): Perhaps we should display this notification again after reloading? if path.is_none() { diff --git a/tests/e2e/analysis.rs b/tests/e2e/analysis.rs index dee8235f..4839124d 100644 --- a/tests/e2e/analysis.rs +++ b/tests/e2e/analysis.rs @@ -39,7 +39,7 @@ fn cairo_projects() { assert_eq!(normalize(&ls, output), indoc! {r#" # Analyzed Crates - - `core`: `["[CAIRO_SOURCE]/corelib/src/lib.cairo"]` + - `core`: `["[SCARB_REGISTRY_STD]/core/src/lib.cairo"]` ```rust CrateSettings { name: None, diff --git a/tests/e2e/support/mod.rs b/tests/e2e/support/mod.rs index 6feeeacc..d2139ddf 100644 --- a/tests/e2e/support/mod.rs +++ b/tests/e2e/support/mod.rs @@ -4,6 +4,7 @@ pub mod fixture; pub mod jsonrpc; mod mock_client; pub mod normalize; +pub mod scarb; pub use self::cursor::cursors; pub use self::mock_client::MockClient; diff --git a/tests/e2e/support/normalize.rs b/tests/e2e/support/normalize.rs index 16c45120..822df62e 100644 --- a/tests/e2e/support/normalize.rs +++ b/tests/e2e/support/normalize.rs @@ -1,6 +1,7 @@ use std::path::Path; use crate::support::fixture::Fixture; +use crate::support::scarb::scarb_registry_std_path; /// Performs various normalization steps of the input data, to remove any runtime-specific artifacts /// and make comparisons in test assertions deterministic. @@ -19,8 +20,10 @@ fn normalize_well_known_paths(fixture: &Fixture, data: String) -> String { data = data.replace(&normalize_path(&pwd), "[PWD]"); } - let cairo_source = Path::new(env!("CARGO_MANIFEST_DIR")).parent().unwrap().parent().unwrap(); - data = data.replace(&normalize_path(cairo_source), "[CAIRO_SOURCE]"); + let cairols_source = Path::new(env!("CARGO_MANIFEST_DIR")); + data = data.replace(&normalize_path(cairols_source), "[CAIROLS_SOURCE]"); + + data = data.replace(&normalize_path(scarb_registry_std_path()), "[SCARB_REGISTRY_STD]"); data } diff --git a/tests/e2e/support/scarb.rs b/tests/e2e/support/scarb.rs new file mode 100644 index 00000000..fbfb685b --- /dev/null +++ b/tests/e2e/support/scarb.rs @@ -0,0 +1,61 @@ +use std::fs; +use std::path::{Path, PathBuf}; +use std::sync::LazyLock; + +use scarb_metadata::MetadataCommand; +use tempfile::tempdir; + +/// Finds Scarb-managed `core` package. +/// +/// This is a stripped-down version of similar logic in `unmanaged_core_crate` with these changes: +/// - instant panicking instead of trying to recover, +/// - using `scarb_metadata` directly instead of `ScarbToolchain`, +/// - no tracing. +pub fn scarb_core_path() -> &'static Path { + static CACHE: LazyLock = LazyLock::new(|| { + let workspace = tempdir().expect("failed to create temporary directory"); + + let scarb_toml = workspace.path().join("Scarb.toml"); + fs::write( + &scarb_toml, + r#" + [package] + name = "cairols_unmanaged_core_lookup" + version = "1.0.0" + "#, + ) + .expect("failed to write Scarb.toml"); + + let metadata = MetadataCommand::new() + .manifest_path(scarb_toml) + .inherit_stderr() + .exec() + .expect("failed to execute: scarb metadata"); + + // Ensure the workspace directory is deleted after running Scarb. + workspace.close().expect("failed to wipe temporary directory"); + + // Scarb is expected to generate only one compilation unit (for our stub package) + // that will consist of this package and the `core` crate. + // Therefore, we allow ourselves to liberally just look for any first usage of a package + // named `core` in all compilation units components we got. + metadata + .compilation_units + .into_iter() + .find_map(|compilation_unit| { + compilation_unit + .components + .iter() + .find(|component| component.name == "core") + .map(|component| component.source_root().to_path_buf().into_std_path_buf()) + }) + .expect("failed to find `core` crate path") + }); + + &*CACHE +} + +/// Finds a path where Scarb unpacks its `std` source. +pub fn scarb_registry_std_path() -> &'static Path { + scarb_core_path().parent().unwrap().parent().unwrap() +} diff --git a/tests/e2e/workspace_configuration.rs b/tests/e2e/workspace_configuration.rs index c4908d47..117234d2 100644 --- a/tests/e2e/workspace_configuration.rs +++ b/tests/e2e/workspace_configuration.rs @@ -5,6 +5,7 @@ use lsp_types::request::Request as _; use serde_json::json; use crate::support::sandbox; +use crate::support::scarb::scarb_core_path; /// The LS used to panic when some files in Salsa database were interned with a relative path. /// The panic happened while trying to create a `file://` URL to affected file. @@ -18,7 +19,7 @@ use crate::support::sandbox; #[test] fn relative_path_to_core() { let core_path = { - let detected = cairo_lang_filesystem::detect::detect_corelib().unwrap(); + let detected = scarb_core_path(); let pwd = std::env::current_dir().unwrap(); let path = pathdiff::diff_paths(detected, pwd).unwrap(); assert!(path.is_relative());