blob: 925aecdb5ba57fc0b9f606417239d105e65601ef [file] [log] [blame]
Brian Silvermancc09f182022-03-09 15:40:20 -08001//! The cli entrypoint for the `generate` subcommand
2
Brian Silverman5f6f2762022-08-13 19:30:05 -07003use std::fs;
Brian Silvermancc09f182022-03-09 15:40:20 -08004use std::path::PathBuf;
5
Brian Silverman5f6f2762022-08-13 19:30:05 -07006use anyhow::{bail, Context as AnyhowContext, Result};
Brian Silvermancc09f182022-03-09 15:40:20 -08007use clap::Parser;
8
9use crate::config::Config;
10use crate::context::Context;
Brian Silverman5f6f2762022-08-13 19:30:05 -070011use crate::lockfile::{lock_context, write_lockfile};
Adam Snaider1c095c92023-07-08 02:09:58 -040012use crate::metadata::{load_metadata, Annotations, Cargo};
Brian Silvermancc09f182022-03-09 15:40:20 -080013use crate::rendering::{write_outputs, Renderer};
14use crate::splicing::SplicingManifest;
15
16/// Command line options for the `generate` subcommand
17#[derive(Parser, Debug)]
Brian Silverman5f6f2762022-08-13 19:30:05 -070018#[clap(about = "Command line options for the `generate` subcommand", version)]
Brian Silvermancc09f182022-03-09 15:40:20 -080019pub struct GenerateOptions {
20 /// The path to a Cargo binary to use for gathering metadata
21 #[clap(long, env = "CARGO")]
22 pub cargo: Option<PathBuf>,
23
24 /// The path to a rustc binary for use with Cargo
25 #[clap(long, env = "RUSTC")]
26 pub rustc: Option<PathBuf>,
27
28 /// The config file with information about the Bazel and Cargo workspace
29 #[clap(long)]
30 pub config: PathBuf,
31
32 /// A generated manifest of splicing inputs
33 #[clap(long)]
34 pub splicing_manifest: PathBuf,
35
36 /// The path to either a Cargo or Bazel lockfile
37 #[clap(long)]
Brian Silverman5f6f2762022-08-13 19:30:05 -070038 pub lockfile: Option<PathBuf>,
Brian Silvermancc09f182022-03-09 15:40:20 -080039
Brian Silverman5f6f2762022-08-13 19:30:05 -070040 /// The path to a [Cargo.lock](https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html) file.
Brian Silvermancc09f182022-03-09 15:40:20 -080041 #[clap(long)]
Brian Silverman5f6f2762022-08-13 19:30:05 -070042 pub cargo_lockfile: PathBuf,
Brian Silvermancc09f182022-03-09 15:40:20 -080043
44 /// The directory of the current repository rule
45 #[clap(long)]
46 pub repository_dir: PathBuf,
47
48 /// A [Cargo config](https://doc.rust-lang.org/cargo/reference/config.html#configuration)
49 /// file to use when gathering metadata
50 #[clap(long)]
51 pub cargo_config: Option<PathBuf>,
52
53 /// Whether or not to ignore the provided lockfile and re-generate one
54 #[clap(long)]
55 pub repin: bool,
56
Brian Silverman5f6f2762022-08-13 19:30:05 -070057 /// The path to a Cargo metadata `json` file. This file must be next to a `Cargo.toml` and `Cargo.lock` file.
Brian Silvermancc09f182022-03-09 15:40:20 -080058 #[clap(long)]
59 pub metadata: Option<PathBuf>,
60
61 /// If true, outputs will be printed instead of written to disk.
62 #[clap(long)]
63 pub dry_run: bool,
64}
65
66pub fn generate(opt: GenerateOptions) -> Result<()> {
67 // Load the config
68 let config = Config::try_from_path(&opt.config)?;
69
Brian Silvermancc09f182022-03-09 15:40:20 -080070 // Go straight to rendering if there is no need to repin
Brian Silverman5f6f2762022-08-13 19:30:05 -070071 if !opt.repin {
72 if let Some(lockfile) = &opt.lockfile {
73 let context = Context::try_from_path(lockfile)?;
Brian Silvermancc09f182022-03-09 15:40:20 -080074
Brian Silverman5f6f2762022-08-13 19:30:05 -070075 // Render build files
Adam Snaider1c095c92023-07-08 02:09:58 -040076 let outputs = Renderer::new(
77 config.rendering,
78 config.supported_platform_triples,
79 config.generate_target_compatible_with,
80 )
81 .render(&context)?;
Brian Silvermancc09f182022-03-09 15:40:20 -080082
Brian Silverman5f6f2762022-08-13 19:30:05 -070083 // Write the outputs to disk
84 write_outputs(outputs, &opt.repository_dir, opt.dry_run)?;
Brian Silvermancc09f182022-03-09 15:40:20 -080085
Brian Silverman5f6f2762022-08-13 19:30:05 -070086 return Ok(());
87 }
Brian Silvermancc09f182022-03-09 15:40:20 -080088 }
89
90 // Ensure Cargo and Rustc are available for use during generation.
Adam Snaider1c095c92023-07-08 02:09:58 -040091 let cargo_bin = Cargo::new(match opt.cargo {
Brian Silvermancc09f182022-03-09 15:40:20 -080092 Some(bin) => bin,
93 None => bail!("The `--cargo` argument is required when generating unpinned content"),
Adam Snaider1c095c92023-07-08 02:09:58 -040094 });
Brian Silvermancc09f182022-03-09 15:40:20 -080095 let rustc_bin = match &opt.rustc {
96 Some(bin) => bin,
97 None => bail!("The `--rustc` argument is required when generating unpinned content"),
98 };
99
100 // Ensure a path to a metadata file was provided
101 let metadata_path = match &opt.metadata {
102 Some(path) => path,
103 None => bail!("The `--metadata` argument is required when generating unpinned content"),
104 };
105
106 // Load Metadata and Lockfile
Brian Silverman5f6f2762022-08-13 19:30:05 -0700107 let (cargo_metadata, cargo_lockfile) = load_metadata(metadata_path)?;
Brian Silvermancc09f182022-03-09 15:40:20 -0800108
Brian Silvermancc09f182022-03-09 15:40:20 -0800109 // Annotate metadata
Brian Silverman5f6f2762022-08-13 19:30:05 -0700110 let annotations = Annotations::new(cargo_metadata, cargo_lockfile.clone(), config.clone())?;
Brian Silvermancc09f182022-03-09 15:40:20 -0800111
Adam Snaider1c095c92023-07-08 02:09:58 -0400112 // Generate renderable contexts for each package
Brian Silvermancc09f182022-03-09 15:40:20 -0800113 let context = Context::new(annotations)?;
114
115 // Render build files
Adam Snaider1c095c92023-07-08 02:09:58 -0400116 let outputs = Renderer::new(
117 config.rendering.clone(),
118 config.supported_platform_triples.clone(),
119 config.generate_target_compatible_with,
120 )
121 .render(&context)?;
Brian Silvermancc09f182022-03-09 15:40:20 -0800122
123 // Write outputs
124 write_outputs(outputs, &opt.repository_dir, opt.dry_run)?;
125
Adam Snaider1c095c92023-07-08 02:09:58 -0400126 // Ensure Bazel lockfiles are written to disk so future generations can be short-circuited.
Brian Silverman5f6f2762022-08-13 19:30:05 -0700127 if let Some(lockfile) = opt.lockfile {
Brian Silvermancc09f182022-03-09 15:40:20 -0800128 let splicing_manifest = SplicingManifest::try_from_path(&opt.splicing_manifest)?;
129
Brian Silverman5f6f2762022-08-13 19:30:05 -0700130 let lock_content =
Adam Snaider1c095c92023-07-08 02:09:58 -0400131 lock_context(context, &config, &splicing_manifest, &cargo_bin, rustc_bin)?;
Brian Silvermancc09f182022-03-09 15:40:20 -0800132
Brian Silverman5f6f2762022-08-13 19:30:05 -0700133 write_lockfile(lock_content, &lockfile, opt.dry_run)?;
Brian Silvermancc09f182022-03-09 15:40:20 -0800134 }
135
Brian Silverman5f6f2762022-08-13 19:30:05 -0700136 // Write the updated Cargo.lock file
137 fs::write(&opt.cargo_lockfile, cargo_lockfile.to_string())
138 .context("Failed to write Cargo.lock file back to the workspace.")?;
139
Brian Silvermancc09f182022-03-09 15:40:20 -0800140 Ok(())
141}