cargo_bazel/cli/
splice.rs1use std::path::PathBuf;
4
5use anyhow::Context;
6use camino::Utf8PathBuf;
7use clap::Parser;
8
9use crate::cli::Result;
10use crate::config::Config;
11use crate::metadata::{
12 write_metadata, Cargo, CargoUpdateRequest, Generator, MetadataGenerator, TreeResolver,
13};
14use crate::splicing::{generate_lockfile, Splicer, SplicingManifest, WorkspaceMetadata};
15
16#[derive(Parser, Debug)]
18#[clap(about = "Command line options for the `splice` subcommand", version)]
19pub struct SpliceOptions {
20 #[clap(long)]
22 pub splicing_manifest: PathBuf,
23
24 #[clap(long)]
26 pub cargo_lockfile: Option<PathBuf>,
27
28 #[clap(long, env = "CARGO_BAZEL_REPIN", num_args=0..=1, default_missing_value = "true")]
30 pub repin: Option<CargoUpdateRequest>,
31
32 #[clap(long)]
35 pub workspace_dir: Option<Utf8PathBuf>,
36
37 #[clap(long)]
39 pub output_dir: PathBuf,
40
41 #[clap(long)]
43 pub dry_run: bool,
44
45 #[clap(long)]
47 pub cargo_config: Option<PathBuf>,
48
49 #[clap(long)]
51 pub config: PathBuf,
52
53 #[clap(long, env = "CARGO")]
55 pub cargo: PathBuf,
56
57 #[clap(long, env = "RUSTC")]
59 pub rustc: PathBuf,
60
61 #[clap(long)]
62 pub nonhermetic_root_bazel_workspace_dir: PathBuf,
63}
64
65pub fn splice(opt: SpliceOptions) -> Result<()> {
67 let splicing_manifest = SplicingManifest::try_from_path(&opt.splicing_manifest)
69 .context("Failed to parse splicing manifest")?;
70
71 let temp_dir;
73 let splicing_dir = match &opt.workspace_dir {
74 Some(dir) => dir.clone(),
75 None => {
76 temp_dir = tempfile::tempdir().context("Failed to generate temporary directory")?;
77 Utf8PathBuf::from_path_buf(temp_dir.as_ref().to_path_buf())
78 .unwrap_or_else(|path| panic!("Temporary directory wasn't valid UTF-8: {:?}", path))
79 }
80 };
81
82 let splicer = Splicer::new(splicing_dir, splicing_manifest)?;
84
85 let cargo = Cargo::new(opt.cargo, opt.rustc.clone());
86
87 let manifest_path = splicer
89 .splice_workspace(&opt.nonhermetic_root_bazel_workspace_dir)
90 .context("Failed to splice workspace")?;
91
92 let cargo_lockfile = generate_lockfile(
94 &manifest_path,
95 &opt.cargo_lockfile,
96 cargo.clone(),
97 &opt.repin,
98 )
99 .context("Failed to generate lockfile")?;
100
101 let config = Config::try_from_path(&opt.config).context("Failed to parse config")?;
102
103 let resolver_data = TreeResolver::new(cargo.clone())
104 .generate(
105 manifest_path.as_path_buf(),
106 &config.supported_platform_triples,
107 )
108 .context("Failed to generate features")?;
109 WorkspaceMetadata::write_registry_urls_and_feature_map(
111 &cargo,
112 &cargo_lockfile,
113 resolver_data,
114 manifest_path.as_path_buf(),
115 manifest_path.as_path_buf(),
116 )
117 .context("Failed to write registry URLs and feature map")?;
118
119 let output_dir = opt.output_dir.clone();
120
121 let (cargo_metadata, _) = Generator::new()
123 .with_cargo(cargo)
124 .with_rustc(opt.rustc)
125 .generate(manifest_path.as_path_buf())
126 .context("Failed to generate cargo metadata")?;
127
128 let cargo_lockfile_path = manifest_path
129 .as_path_buf()
130 .parent()
131 .with_context(|| {
132 format!(
133 "The path {} is expected to have a parent directory",
134 manifest_path.as_path_buf()
135 )
136 })?
137 .join("Cargo.lock");
138
139 std::fs::create_dir_all(&output_dir)
141 .with_context(|| format!("Failed to create directories for {}", &output_dir.display()))?;
142
143 write_metadata(&opt.output_dir.join("metadata.json"), &cargo_metadata)
144 .context("Failed to write metadata")?;
145
146 std::fs::copy(cargo_lockfile_path, output_dir.join("Cargo.lock"))
147 .context("Failed to copy lockfile")?;
148
149 Ok(())
150}