diff --git a/Cargo.lock b/Cargo.lock index 69a0d5e45..e73c6fddb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1876,6 +1876,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5f54427cfd1c7829e2a139fcefea601bf088ebca651d2bf53ebc600eac295dae" dependencies = [ "crc32fast", + "libz-sys", "miniz_oxide", ] @@ -3474,6 +3475,17 @@ dependencies = [ "libc", ] +[[package]] +name = "libz-sys" +version = "1.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2d16453e800a8cf6dd2fc3eb4bc99b786a9b90c663b8559a5b1a041bf89e472" +dependencies = [ + "cc", + "pkg-config", + "vcpkg", +] + [[package]] name = "linkme" version = "0.3.28" @@ -4614,6 +4626,7 @@ dependencies = [ "directories", "dunce", "expect-test", + "flate2", "fs4", "fs_extra", "futures", diff --git a/Cargo.toml b/Cargo.toml index e6d4a566d..a8ba49276 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -72,6 +72,7 @@ dialoguer = "0.11.0" directories = "5" dunce = "1" expect-test = "1.5" +flate2 = { version = "1.0.30", default-features = false, features = ["zlib"] } fs4 = { version = "0.7", features = ["tokio"] } fs_extra = "1" futures = { version = "0.3", default-features = false, features = ["std", "async-await"] } diff --git a/scarb/Cargo.toml b/scarb/Cargo.toml index cb0cc9392..5f887e927 100644 --- a/scarb/Cargo.toml +++ b/scarb/Cargo.toml @@ -86,6 +86,7 @@ which.workspace = true windows-sys.workspace = true zstd.workspace = true cargo_metadata.workspace = true +flate2.workspace = true [target.'cfg(not(target_os = "linux"))'.dependencies] reqwest = { workspace = true, default-features = true } diff --git a/scarb/src/compiler/plugin/proc_macro/compilation.rs b/scarb/src/compiler/plugin/proc_macro/compilation.rs index 451bdf3b8..487e56afe 100644 --- a/scarb/src/compiler/plugin/proc_macro/compilation.rs +++ b/scarb/src/compiler/plugin/proc_macro/compilation.rs @@ -1,12 +1,14 @@ use crate::compiler::ProcMacroCompilationUnit; use crate::core::{Config, Package, Workspace}; use crate::flock::Filesystem; +use crate::internal::fsx; use crate::ops::PackageOpts; use crate::process::exec_piping; use crate::CARGO_MANIFEST_FILE_NAME; use anyhow::{anyhow, Context, Result}; use camino::Utf8PathBuf; use cargo_metadata::MetadataCommand; +use flate2::read::GzDecoder; use indoc::formatdoc; use libloading::library_filename; use ra_ap_toolchain::Tool; @@ -15,7 +17,10 @@ use serde::{Serialize, Serializer}; use serde_json::value::RawValue; use std::fmt::Display; use std::fs; +use std::io::{Seek, SeekFrom}; +use std::ops::Deref; use std::process::Command; +use tar::Archive; use tracing::trace_span; pub const PROC_MACRO_BUILD_PROFILE: &str = "release"; @@ -150,6 +155,32 @@ pub fn get_crate_archive_basename(package: &Package) -> Result { Ok(format!("{}-{}", package_name, package_version)) } +pub fn unpack_crate(package: &Package, config: &Config) -> Result<()> { + let archive_basename = get_crate_archive_basename(package)?; + let archive_name = format!("{}.crate", archive_basename); + + let tar = package + .target_path(config) + .into_child("package") + .open_ro_exclusive(&archive_name, &archive_name, config)?; + + // The following implementation has been copied from the `Cargo` codebase with slight modifications only. + // The original implementation can be found here: + // https://github.com/rust-lang/cargo/blob/a4600184b8d6619ed0b5a0a19946dbbe97e1d739/src/cargo/ops/cargo_package.rs#L1110 + + tar.deref().seek(SeekFrom::Start(0))?; + let f = GzDecoder::new(tar.deref()); + let dst = tar.parent().unwrap().join(&archive_basename); + if dst.exists() { + fsx::remove_dir_all(&dst)?; + } + let mut archive = Archive::new(f); + archive.set_preserve_mtime(false); // Don't set modified time to avoid filesystem errors + archive.unpack(dst.parent().unwrap())?; + + Ok(()) +} + pub fn fetch_crate(package: &Package, ws: &Workspace<'_>) -> Result<()> { run_cargo(CargoAction::Fetch, package, ws) } @@ -233,6 +264,7 @@ impl<'c> From> for Command { CargoAction::Package(ref opts) => { cmd.arg("--target-dir"); cmd.arg(args.target_dir); + cmd.arg("--no-verify"); if !opts.check_metadata { cmd.arg("--no-metadata"); } diff --git a/scarb/src/flock.rs b/scarb/src/flock.rs index 7cfa89482..5f071f487 100644 --- a/scarb/src/flock.rs +++ b/scarb/src/flock.rs @@ -37,6 +37,10 @@ impl FileLockGuard { self.path.as_path() } + pub fn parent(&self) -> Option<&Utf8Path> { + self.path.parent() + } + pub fn lock_kind(&self) -> FileLockKind { self.lock_kind } @@ -293,6 +297,31 @@ impl Filesystem { ) } + /// Opens exclusive access to a [`File`], returning the locked version of it. + /// + /// This function will fail if `path` doesn't already exist, but if it does then it will + /// acquire an exclusive lock on `path`. + /// If the process must block waiting for the lock, the `description` annotated with _blocking_ + /// status message is printed to [`Config::ui`]. + /// + /// The returned file can be accessed to look at the path and also has read + /// access to the underlying file. + /// Any writes to the file will return an error. + pub fn open_ro_exclusive( + &self, + path: impl AsRef, + description: &str, + config: &Config, + ) -> Result { + self.open( + path.as_ref(), + OpenOptions::new().read(true), + FileLockKind::Exclusive, + description, + config, + ) + } + fn open( &self, path: &Utf8Path, diff --git a/scarb/src/ops/package.rs b/scarb/src/ops/package.rs index 0d017327d..7164ad134 100644 --- a/scarb/src/ops/package.rs +++ b/scarb/src/ops/package.rs @@ -14,7 +14,7 @@ use scarb_ui::{HumanBytes, HumanCount}; use serde::Serialize; use crate::compiler::plugin::proc_macro::compilation::{ - get_crate_archive_basename, package_crate, SharedLibraryProvider, + get_crate_archive_basename, package_crate, unpack_crate, SharedLibraryProvider, }; use crate::core::publishing::manifest_normalization::prepare_manifest_for_publish; use crate::core::publishing::source::list_source_files; @@ -279,6 +279,9 @@ fn prepare_archive_recipe( )); } + // Unpack .crate to make normalized Cargo.toml available. + unpack_crate(pkg, ws.config())?; + // Add normalized Cargo.toml file. recipe.push(ArchiveFile { path: CARGO_MANIFEST_FILE_NAME.into(), diff --git a/scarb/tests/package.rs b/scarb/tests/package.rs index 9e4c0d131..00d3cb279 100644 --- a/scarb/tests/package.rs +++ b/scarb/tests/package.rs @@ -1453,6 +1453,29 @@ fn package_without_verification() { "#}); } +#[test] +fn package_cairo_plugin_without_verification() { + let t = TempDir::new().unwrap(); + CairoPluginProjectBuilder::default().build(&t); + + Scarb::quick_snapbox() + .arg("package") + .arg("--no-verify") + .arg("--no-metadata") + .env("CARGO_TERM_QUIET", "true") + .current_dir(&t) + .assert() + .success() + .stdout_matches(indoc! {r#" + [..] Packaging some v1.0.0 [..] + [..]warn: package name or version differs between Cargo manifest and Scarb manifest + [..]Scarb manifest: `some-1.0.0`, Cargo manifest: `some-0.1.0` + [..]this might become an error in future Scarb releases + + [..] Packaged [..] + "#}); +} + #[test] fn package_without_publish_metadata() { let t = TempDir::new().unwrap();