blob: d38b874be73cee8793cb0a15668fdc71ebdea264 [file] [log] [blame]
//! The cli entrypoint for the `splice` subcommand
use std::path::PathBuf;
use anyhow::Context;
use clap::Parser;
use crate::cli::Result;
use crate::config::Config;
use crate::metadata::{
write_metadata, Cargo, CargoUpdateRequest, FeatureGenerator, Generator, MetadataGenerator,
};
use crate::splicing::{generate_lockfile, Splicer, SplicingManifest, WorkspaceMetadata};
/// Command line options for the `splice` subcommand
#[derive(Parser, Debug)]
#[clap(about = "Command line options for the `splice` subcommand", version)]
pub struct SpliceOptions {
/// A generated manifest of splicing inputs
#[clap(long)]
pub splicing_manifest: PathBuf,
/// The path to a [Cargo.lock](https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html) file.
#[clap(long)]
pub cargo_lockfile: Option<PathBuf>,
/// The desired update/repin behavior
#[clap(long, env = "CARGO_BAZEL_REPIN", num_args=0..=1, default_missing_value = "true")]
pub repin: Option<CargoUpdateRequest>,
/// The directory in which to build the workspace. If this argument is not
/// passed, a temporary directory will be generated.
#[clap(long)]
pub workspace_dir: Option<PathBuf>,
/// The location where the results of splicing are written.
#[clap(long)]
pub output_dir: PathBuf,
/// If true, outputs will be printed instead of written to disk.
#[clap(long)]
pub dry_run: bool,
/// The path to a Cargo configuration file.
#[clap(long)]
pub cargo_config: Option<PathBuf>,
/// The path to the config file (containing `cargo_bazel::config::Config`.)
#[clap(long)]
pub config: PathBuf,
/// The path to a Cargo binary to use for gathering metadata
#[clap(long, env = "CARGO")]
pub cargo: PathBuf,
/// The path to a rustc binary for use with Cargo
#[clap(long, env = "RUSTC")]
pub rustc: PathBuf,
}
/// Combine a set of disjoint manifests into a single workspace.
pub fn splice(opt: SpliceOptions) -> Result<()> {
// Load the all config files required for splicing a workspace
let splicing_manifest = SplicingManifest::try_from_path(&opt.splicing_manifest)
.context("Failed to parse splicing manifest")?;
// Determine the splicing workspace
let temp_dir;
let splicing_dir = match &opt.workspace_dir {
Some(dir) => dir.clone(),
None => {
temp_dir = tempfile::tempdir().context("Failed to generate temporary directory")?;
temp_dir.as_ref().to_path_buf()
}
};
// Generate a splicer for creating a Cargo workspace manifest
let splicer = Splicer::new(splicing_dir, splicing_manifest)?;
// Splice together the manifest
let manifest_path = splicer
.splice_workspace(&opt.cargo)
.context("Failed to splice workspace")?;
let cargo = Cargo::new(opt.cargo);
// Generate a lockfile
let cargo_lockfile = generate_lockfile(
&manifest_path,
&opt.cargo_lockfile,
cargo.clone(),
&opt.rustc,
&opt.repin,
)
.context("Failed to generate lockfile")?;
let config = Config::try_from_path(&opt.config).context("Failed to parse config")?;
let feature_map = FeatureGenerator::new(cargo.clone(), opt.rustc.clone())
.generate(
manifest_path.as_path_buf(),
&config.supported_platform_triples,
)
.context("Failed to generate features")?;
// Write the registry url info to the manifest now that a lockfile has been generated
WorkspaceMetadata::write_registry_urls_and_feature_map(
&cargo,
&cargo_lockfile,
feature_map,
manifest_path.as_path_buf(),
manifest_path.as_path_buf(),
)
.context("Failed to write registry URLs and feature map")?;
let output_dir = opt.output_dir.clone();
// Write metadata to the workspace for future reuse
let (cargo_metadata, _) = Generator::new()
.with_cargo(cargo)
.with_rustc(opt.rustc)
.generate(manifest_path.as_path_buf())
.context("Failed to generate cargo metadata")?;
let cargo_lockfile_path = manifest_path
.as_path_buf()
.parent()
.with_context(|| {
format!(
"The path {} is expected to have a parent directory",
manifest_path.as_path_buf().display()
)
})?
.join("Cargo.lock");
// Generate the consumable outputs of the splicing process
std::fs::create_dir_all(&output_dir)
.with_context(|| format!("Failed to create directories for {}", &output_dir.display()))?;
write_metadata(&opt.output_dir.join("metadata.json"), &cargo_metadata)
.context("Failed to write metadata")?;
std::fs::copy(cargo_lockfile_path, output_dir.join("Cargo.lock"))
.context("Failed to copy lockfile")?;
Ok(())
}