blob: 3fdc97e33e2de1941b682f162cbf3b7037989354 [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};
Brian Silvermancc09f182022-03-09 15:40:20 -080012use crate::metadata::load_metadata;
13use crate::metadata::Annotations;
14use crate::rendering::{write_outputs, Renderer};
15use crate::splicing::SplicingManifest;
16
17/// Command line options for the `generate` subcommand
18#[derive(Parser, Debug)]
Brian Silverman5f6f2762022-08-13 19:30:05 -070019#[clap(about = "Command line options for the `generate` subcommand", version)]
Brian Silvermancc09f182022-03-09 15:40:20 -080020pub struct GenerateOptions {
21 /// The path to a Cargo binary to use for gathering metadata
22 #[clap(long, env = "CARGO")]
23 pub cargo: Option<PathBuf>,
24
25 /// The path to a rustc binary for use with Cargo
26 #[clap(long, env = "RUSTC")]
27 pub rustc: Option<PathBuf>,
28
29 /// The config file with information about the Bazel and Cargo workspace
30 #[clap(long)]
31 pub config: PathBuf,
32
33 /// A generated manifest of splicing inputs
34 #[clap(long)]
35 pub splicing_manifest: PathBuf,
36
37 /// The path to either a Cargo or Bazel lockfile
38 #[clap(long)]
Brian Silverman5f6f2762022-08-13 19:30:05 -070039 pub lockfile: Option<PathBuf>,
Brian Silvermancc09f182022-03-09 15:40:20 -080040
Brian Silverman5f6f2762022-08-13 19:30:05 -070041 /// 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 -080042 #[clap(long)]
Brian Silverman5f6f2762022-08-13 19:30:05 -070043 pub cargo_lockfile: PathBuf,
Brian Silvermancc09f182022-03-09 15:40:20 -080044
45 /// The directory of the current repository rule
46 #[clap(long)]
47 pub repository_dir: PathBuf,
48
49 /// A [Cargo config](https://doc.rust-lang.org/cargo/reference/config.html#configuration)
50 /// file to use when gathering metadata
51 #[clap(long)]
52 pub cargo_config: Option<PathBuf>,
53
54 /// Whether or not to ignore the provided lockfile and re-generate one
55 #[clap(long)]
56 pub repin: bool,
57
Brian Silverman5f6f2762022-08-13 19:30:05 -070058 /// 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 -080059 #[clap(long)]
60 pub metadata: Option<PathBuf>,
61
62 /// If true, outputs will be printed instead of written to disk.
63 #[clap(long)]
64 pub dry_run: bool,
65}
66
67pub fn generate(opt: GenerateOptions) -> Result<()> {
68 // Load the config
69 let config = Config::try_from_path(&opt.config)?;
70
Brian Silvermancc09f182022-03-09 15:40:20 -080071 // Go straight to rendering if there is no need to repin
Brian Silverman5f6f2762022-08-13 19:30:05 -070072 if !opt.repin {
73 if let Some(lockfile) = &opt.lockfile {
74 let context = Context::try_from_path(lockfile)?;
Brian Silvermancc09f182022-03-09 15:40:20 -080075
Brian Silverman5f6f2762022-08-13 19:30:05 -070076 // Render build files
77 let outputs = Renderer::new(config.rendering).render(&context)?;
Brian Silvermancc09f182022-03-09 15:40:20 -080078
Brian Silverman5f6f2762022-08-13 19:30:05 -070079 // Write the outputs to disk
80 write_outputs(outputs, &opt.repository_dir, opt.dry_run)?;
Brian Silvermancc09f182022-03-09 15:40:20 -080081
Brian Silverman5f6f2762022-08-13 19:30:05 -070082 return Ok(());
83 }
Brian Silvermancc09f182022-03-09 15:40:20 -080084 }
85
86 // Ensure Cargo and Rustc are available for use during generation.
87 let cargo_bin = match &opt.cargo {
88 Some(bin) => bin,
89 None => bail!("The `--cargo` argument is required when generating unpinned content"),
90 };
91 let rustc_bin = match &opt.rustc {
92 Some(bin) => bin,
93 None => bail!("The `--rustc` argument is required when generating unpinned content"),
94 };
95
96 // Ensure a path to a metadata file was provided
97 let metadata_path = match &opt.metadata {
98 Some(path) => path,
99 None => bail!("The `--metadata` argument is required when generating unpinned content"),
100 };
101
102 // Load Metadata and Lockfile
Brian Silverman5f6f2762022-08-13 19:30:05 -0700103 let (cargo_metadata, cargo_lockfile) = load_metadata(metadata_path)?;
Brian Silvermancc09f182022-03-09 15:40:20 -0800104
105 // Copy the rendering config for later use
106 let render_config = config.rendering.clone();
107
108 // Annotate metadata
Brian Silverman5f6f2762022-08-13 19:30:05 -0700109 let annotations = Annotations::new(cargo_metadata, cargo_lockfile.clone(), config.clone())?;
Brian Silvermancc09f182022-03-09 15:40:20 -0800110
111 // Generate renderable contexts for earch package
112 let context = Context::new(annotations)?;
113
114 // Render build files
115 let outputs = Renderer::new(render_config).render(&context)?;
116
117 // Write outputs
118 write_outputs(outputs, &opt.repository_dir, opt.dry_run)?;
119
120 // Ensure Bazel lockfiles are written to disk so future generations can be short-circuted.
Brian Silverman5f6f2762022-08-13 19:30:05 -0700121 if let Some(lockfile) = opt.lockfile {
Brian Silvermancc09f182022-03-09 15:40:20 -0800122 let splicing_manifest = SplicingManifest::try_from_path(&opt.splicing_manifest)?;
123
Brian Silverman5f6f2762022-08-13 19:30:05 -0700124 let lock_content =
125 lock_context(context, &config, &splicing_manifest, cargo_bin, rustc_bin)?;
Brian Silvermancc09f182022-03-09 15:40:20 -0800126
Brian Silverman5f6f2762022-08-13 19:30:05 -0700127 write_lockfile(lock_content, &lockfile, opt.dry_run)?;
Brian Silvermancc09f182022-03-09 15:40:20 -0800128 }
129
Brian Silverman5f6f2762022-08-13 19:30:05 -0700130 // Write the updated Cargo.lock file
131 fs::write(&opt.cargo_lockfile, cargo_lockfile.to_string())
132 .context("Failed to write Cargo.lock file back to the workspace.")?;
133
Brian Silvermancc09f182022-03-09 15:40:20 -0800134 Ok(())
135}