Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,19 @@ Additional flags for build configuration can be looked up if needed by:
cargo near build reproducible-wasm -h # replace `-h` with `--help` for more details
```

#### Caching for faster builds

To speed up subsequent builds, especially when building multiple contracts in the same workspace or rebuilding the same contract, you can mount local cache directories into the Docker container:

```bash
cargo near build reproducible-wasm --mount-target-cache --mount-cargo-cache
```

- `--mount-target-cache`: Mounts your local `./target` directory into the Docker container, reusing previously compiled dependencies and build artifacts
- `--mount-cargo-cache`: Mounts your local `~/.cargo` directory into the Docker container, reusing downloaded crate registry and git dependencies

**Note**: While these flags can significantly speed up builds (especially in CI/CD or when building workspace members sequentially), they may affect reproducibility guarantees in some edge cases. Use these flags primarily for development or when rebuilding the same codebase multiple times, and disable them for final production builds if strict reproducibility is required.

#### Custom `reproducible-wasm` build using `--variant <name>` flag

Beyond your `[package.metadata.near.reproducible_build]` configuration, you can
Expand Down
70 changes: 67 additions & 3 deletions cargo-near-build/src/near/docker_build/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -120,13 +120,77 @@ pub fn run(opts: DockerBuildOpts, quiet: bool) -> eyre::Result<CompilationArtifa
let docker_build_out_wasm = near_verify_rs::logic::nep330_build::run(
contract_source_metadata,
cloned_repo.contract_source_workdir()?,
additional_docker_args(),
additional_docker_args(&opts, &crate_in_repo.repo_root)?,
quiet,
)?;

cloned_repo.copy_artifact(docker_build_out_wasm, out_dir_arg)
}

fn additional_docker_args() -> Vec<String> {
vec!["--env".to_string(), RUST_LOG_EXPORT.to_string()]
fn additional_docker_args(
opts: &DockerBuildOpts,
repo_root: &camino::Utf8Path,
) -> eyre::Result<Vec<String>> {
let mut args = vec!["--env".to_string(), RUST_LOG_EXPORT.to_string()];

// Mount ./target directory if requested
if opts.mount_target_cache {
let target_dir = repo_root.join("target");

// Create target directory if it doesn't exist
std::fs::create_dir_all(&target_dir).map_err(|e| {
eyre::eyre!("Failed to create target directory {:?}: {}", target_dir, e)
})?;

let target_dir_str = dunce::canonicalize(&target_dir)
.map_err(|e| {
eyre::eyre!(
"Failed to canonicalize target directory {:?}: {}",
target_dir,
e
)
})?
.to_string_lossy()
.to_string();

// Mount to a fixed location and set CARGO_TARGET_DIR to use it
args.push("--volume".to_string());
args.push(format!("{}:/target_cache", target_dir_str));
args.push("--env".to_string());
args.push("CARGO_TARGET_DIR=/target_cache".to_string());

println!(
"{}{}",
"Mounting local target cache: ".cyan(),
target_dir.as_str().yellow()
);
}

// Mount ~/.cargo directory if requested
if opts.mount_cargo_cache {
let cargo_home = std::env::var("CARGO_HOME")
.or_else(|_| {
std::env::var("HOME")
.or_else(|_| std::env::var("USERPROFILE"))
.map(|home| format!("{}/.cargo", home))
})
.map_err(|_| eyre::eyre!("Failed to determine CARGO_HOME or ~/.cargo directory"))?;

let cargo_home_str = dunce::canonicalize(&cargo_home)
.map_err(|e| eyre::eyre!("Failed to canonicalize cargo home {:?}: {}", cargo_home, e))?
.to_string_lossy()
.to_string();
Comment on lines +171 to +182
Copy link

Copilot AI Jan 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The cargo home directory is not created before calling dunce::canonicalize, unlike the target directory (lines 141-143). If the directory doesn't exist (e.g., on a fresh system), canonicalize will fail. Add std::fs::create_dir_all(&cargo_home) before the canonicalize call, similar to the target directory handling.

Copilot uses AI. Check for mistakes.

// Mount to /usr/local/cargo which is the standard CARGO_HOME in Rust Docker images
args.push("--volume".to_string());
args.push(format!("{}:/usr/local/cargo", cargo_home_str));

println!(
"{}{}",
"Mounting local cargo cache: ".cyan(),
cargo_home.yellow()
);
}

Ok(args)
}
6 changes: 6 additions & 0 deletions cargo-near-build/src/types/near/docker_build/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,12 @@ pub struct Opts {
pub variant: Option<String>,
#[builder(default)]
pub context: BuildContext,
/// Mount local `./target` directory as a Docker volume to cache build artifacts
#[builder(default)]
pub mount_target_cache: bool,
/// Mount local `~/.cargo` directory as a Docker volume to cache Cargo registry
#[builder(default)]
pub mount_cargo_cache: bool,
}

pub const WARN_BECOMES_ERR: &str =
Expand Down
22 changes: 22 additions & 0 deletions cargo-near/src/commands/build/actions/reproducible_wasm/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,22 @@ pub struct BuildOpts {
#[interactive_clap(skip_interactive_input)]
#[interactive_clap(verbatim_doc_comment)]
pub variant: Option<String>,
/// Mount local `./target` directory as a Docker volume to cache build artifacts across builds
///
/// This can significantly speed up subsequent builds by reusing previously compiled dependencies.
/// Note: Using cache may affect reproducibility guarantees in some edge cases.
#[interactive_clap(long)]
#[interactive_clap(skip_interactive_input)]
#[interactive_clap(verbatim_doc_comment)]
pub mount_target_cache: bool,
/// Mount local `~/.cargo` directory as a Docker volume to cache Cargo registry and git dependencies
///
/// This can significantly speed up subsequent builds by reusing previously downloaded dependencies.
/// Note: Using cache may affect reproducibility guarantees in some edge cases.
#[interactive_clap(long)]
#[interactive_clap(skip_interactive_input)]
#[interactive_clap(verbatim_doc_comment)]
pub mount_cargo_cache: bool,
}

impl From<CliBuildOpts> for BuildOpts {
Expand All @@ -102,6 +118,8 @@ impl From<CliBuildOpts> for BuildOpts {
color: value.color,
variant: value.variant,
profile: value.profile,
mount_target_cache: value.mount_target_cache,
mount_cargo_cache: value.mount_cargo_cache,
}
}
}
Expand All @@ -122,6 +140,8 @@ mod context {
color: scope.color.clone(),
variant: scope.variant.clone(),
profile: scope.profile.clone(),
mount_target_cache: scope.mount_target_cache,
mount_cargo_cache: scope.mount_cargo_cache,
};
super::run(opts, previous_context)?;
Ok(Self)
Expand All @@ -140,6 +160,8 @@ fn docker_opts_from(value: (BuildOpts, BuildContext)) -> docker::DockerBuildOpts
color: value.0.color.map(Into::into),
variant: value.0.variant,
context: value.1,
mount_target_cache: value.0.mount_target_cache,
mount_cargo_cache: value.0.mount_cargo_cache,
}
}

Expand Down
Loading