blob: 67ae868607ce31f859e2cb806d259fee077b15af [file] [log] [blame]
Brian Silvermancc09f182022-03-09 15:40:20 -08001//! The cli entrypoint for the `generate` subcommand
2
3use std::path::PathBuf;
4
5use anyhow::{bail, Result};
6use clap::Parser;
7
8use crate::config::Config;
9use crate::context::Context;
10use crate::lockfile::{is_cargo_lockfile, lock_context, write_lockfile, LockfileKind};
11use crate::metadata::load_metadata;
12use crate::metadata::Annotations;
13use crate::rendering::{write_outputs, Renderer};
14use crate::splicing::SplicingManifest;
15
16/// Command line options for the `generate` subcommand
17#[derive(Parser, Debug)]
18#[clap(about, version)]
19pub 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)]
38 pub lockfile: PathBuf,
39
40 /// The type of lockfile
41 #[clap(long)]
42 pub lockfile_kind: LockfileKind,
43
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
57 /// The path to a Cargo metadata `json` file.
58 #[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
70 // Determine if the dependencies need to be repinned.
71 let mut should_repin = opt.repin;
72
73 // Cargo lockfiles must always be repinned.
74 if is_cargo_lockfile(&opt.lockfile, &opt.lockfile_kind) {
75 should_repin = true;
76 }
77
78 // Go straight to rendering if there is no need to repin
79 if !should_repin {
80 let context = Context::try_from_path(opt.lockfile)?;
81
82 // Render build files
83 let outputs = Renderer::new(config.rendering).render(&context)?;
84
85 // Write the outputs to disk
86 write_outputs(outputs, &opt.repository_dir, opt.dry_run)?;
87
88 return Ok(());
89 }
90
91 // Ensure Cargo and Rustc are available for use during generation.
92 let cargo_bin = match &opt.cargo {
93 Some(bin) => bin,
94 None => bail!("The `--cargo` argument is required when generating unpinned content"),
95 };
96 let rustc_bin = match &opt.rustc {
97 Some(bin) => bin,
98 None => bail!("The `--rustc` argument is required when generating unpinned content"),
99 };
100
101 // Ensure a path to a metadata file was provided
102 let metadata_path = match &opt.metadata {
103 Some(path) => path,
104 None => bail!("The `--metadata` argument is required when generating unpinned content"),
105 };
106
107 // Load Metadata and Lockfile
108 let (cargo_metadata, cargo_lockfile) = load_metadata(
109 metadata_path,
110 if is_cargo_lockfile(&opt.lockfile, &opt.lockfile_kind) {
111 Some(&opt.lockfile)
112 } else {
113 None
114 },
115 )?;
116
117 // Copy the rendering config for later use
118 let render_config = config.rendering.clone();
119
120 // Annotate metadata
121 let annotations = Annotations::new(cargo_metadata, cargo_lockfile, config.clone())?;
122
123 // Generate renderable contexts for earch package
124 let context = Context::new(annotations)?;
125
126 // Render build files
127 let outputs = Renderer::new(render_config).render(&context)?;
128
129 // Write outputs
130 write_outputs(outputs, &opt.repository_dir, opt.dry_run)?;
131
132 // Ensure Bazel lockfiles are written to disk so future generations can be short-circuted.
133 if matches!(opt.lockfile_kind, LockfileKind::Bazel) {
134 let splicing_manifest = SplicingManifest::try_from_path(&opt.splicing_manifest)?;
135
136 let lockfile = lock_context(context, &config, &splicing_manifest, cargo_bin, rustc_bin)?;
137
138 write_lockfile(lockfile, &opt.lockfile, opt.dry_run)?;
139 }
140
141 Ok(())
142}