1 //! The cli entrypoint for the `splice` subcommand
2
3 use std::path::PathBuf;
4
5 use anyhow::Context;
6 use clap::Parser;
7
8 use crate::cli::Result;
9 use crate::config::Config;
10 use crate::metadata::{
11 write_metadata, Cargo, CargoUpdateRequest, FeatureGenerator, Generator, MetadataGenerator,
12 };
13 use crate::splicing::{generate_lockfile, Splicer, SplicingManifest, WorkspaceMetadata};
14
15 /// Command line options for the `splice` subcommand
16 #[derive(Parser, Debug)]
17 #[clap(about = "Command line options for the `splice` subcommand", version)]
18 pub struct SpliceOptions {
19 /// A generated manifest of splicing inputs
20 #[clap(long)]
21 pub splicing_manifest: PathBuf,
22
23 /// The path to a [Cargo.lock](https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html) file.
24 #[clap(long)]
25 pub cargo_lockfile: Option<PathBuf>,
26
27 /// The desired update/repin behavior
28 #[clap(long, env = "CARGO_BAZEL_REPIN", num_args=0..=1, default_missing_value = "true")]
29 pub repin: Option<CargoUpdateRequest>,
30
31 /// The directory in which to build the workspace. If this argument is not
32 /// passed, a temporary directory will be generated.
33 #[clap(long)]
34 pub workspace_dir: Option<PathBuf>,
35
36 /// The location where the results of splicing are written.
37 #[clap(long)]
38 pub output_dir: PathBuf,
39
40 /// If true, outputs will be printed instead of written to disk.
41 #[clap(long)]
42 pub dry_run: bool,
43
44 /// The path to a Cargo configuration file.
45 #[clap(long)]
46 pub cargo_config: Option<PathBuf>,
47
48 /// The path to the config file (containing `cargo_bazel::config::Config`.)
49 #[clap(long)]
50 pub config: PathBuf,
51
52 /// The path to a Cargo binary to use for gathering metadata
53 #[clap(long, env = "CARGO")]
54 pub cargo: PathBuf,
55
56 /// The path to a rustc binary for use with Cargo
57 #[clap(long, env = "RUSTC")]
58 pub rustc: PathBuf,
59 }
60
61 /// Combine a set of disjoint manifests into a single workspace.
splice(opt: SpliceOptions) -> Result<()>62 pub fn splice(opt: SpliceOptions) -> Result<()> {
63 // Load the all config files required for splicing a workspace
64 let splicing_manifest = SplicingManifest::try_from_path(&opt.splicing_manifest)?;
65
66 // Determine the splicing workspace
67 let temp_dir;
68 let splicing_dir = match &opt.workspace_dir {
69 Some(dir) => dir.clone(),
70 None => {
71 temp_dir = tempfile::tempdir().context("Failed to generate temporary directory")?;
72 temp_dir.as_ref().to_path_buf()
73 }
74 };
75
76 // Generate a splicer for creating a Cargo workspace manifest
77 let splicer = Splicer::new(splicing_dir, splicing_manifest)?;
78
79 // Splice together the manifest
80 let manifest_path = splicer.splice_workspace(&opt.cargo)?;
81
82 let cargo = Cargo::new(opt.cargo);
83
84 // Generate a lockfile
85 let cargo_lockfile = generate_lockfile(
86 &manifest_path,
87 &opt.cargo_lockfile,
88 cargo.clone(),
89 &opt.rustc,
90 &opt.repin,
91 )?;
92
93 let config = Config::try_from_path(&opt.config)?;
94
95 let feature_map = FeatureGenerator::new(cargo.clone(), opt.rustc.clone()).generate(
96 manifest_path.as_path_buf(),
97 &config.supported_platform_triples,
98 )?;
99 // Write the registry url info to the manifest now that a lockfile has been generated
100 WorkspaceMetadata::write_registry_urls_and_feature_map(
101 &cargo,
102 &cargo_lockfile,
103 feature_map,
104 manifest_path.as_path_buf(),
105 manifest_path.as_path_buf(),
106 )?;
107
108 let output_dir = opt.output_dir.clone();
109
110 // Write metadata to the workspace for future reuse
111 let (cargo_metadata, _) = Generator::new()
112 .with_cargo(cargo)
113 .with_rustc(opt.rustc)
114 .generate(manifest_path.as_path_buf())?;
115
116 let cargo_lockfile_path = manifest_path
117 .as_path_buf()
118 .parent()
119 .with_context(|| {
120 format!(
121 "The path {} is expected to have a parent directory",
122 manifest_path.as_path_buf().display()
123 )
124 })?
125 .join("Cargo.lock");
126
127 // Generate the consumable outputs of the splicing process
128 std::fs::create_dir_all(&output_dir)
129 .with_context(|| format!("Failed to create directories for {}", &output_dir.display()))?;
130
131 write_metadata(&opt.output_dir.join("metadata.json"), &cargo_metadata)?;
132
133 std::fs::copy(cargo_lockfile_path, output_dir.join("Cargo.lock"))
134 .context("Failed to copy lockfile")?;
135
136 Ok(())
137 }
138