1// Copyright Materialize, Inc. and contributors. All rights reserved.
2//
3// Use of this software is governed by the Business Source License
4// included in the LICENSE file.
5//
6// As of the Change Date specified in that file, in accordance with
7// the Business Source License, use of this software will be governed
8// by the Apache License, Version 2.0.
910use std::borrow::Cow;
11use std::collections::BTreeMap;
1213use guppy::graph::PackageMetadata;
14use std::sync::LazyLock;
1516use crate::targets::{AdditiveContent, RustTestSize};
1718const KEY_NAME: &str = "cargo-gazelle";
1920/// Name that should be used to specify extra config for library tests.
21///
22/// e.g. `package.metadata.cargo-gazelle.test.lib`
23const LIB_TEST_NAME: &str = "lib";
2425/// Name that should be used to specify extra config for doc tests.
26///
27/// e.g. `package.metadata.cargo-gazelle.test.doc`
28const DOC_TEST_NAME: &str = "doc";
2930/// Global configuration for generating `BUILD` files.
31#[derive(Debug, Clone)]
32pub struct GlobalConfig {
33pub ignored_crates: Vec<Cow<'static, str>>,
34pub proto_build_crates: Vec<Cow<'static, str>>,
35}
3637impl Default for GlobalConfig {
38fn default() -> Self {
39 GlobalConfig {
40 ignored_crates: vec!["workspace-hack".into()],
41 proto_build_crates: vec!["prost_build".into(), "tonic_build".into()],
42 }
43 }
44}
4546impl GlobalConfig {
47/// Returns `true` if the named dependency should be included, `false` if it should be ignored.
48pub fn include_dep(&self, name: &str) -> bool {
49 !self.ignored_crates.contains(&Cow::Borrowed(name))
50 }
51}
5253/// Extra configuration for the Bazel targets generated for a crate.
54///
55/// We should try to make generating `BUILD.bazel` files is as seamless as possible, but there are
56/// instances where this isn't possible. For example, some tests rely on text-based snapshot files
57/// that Bazel needs to know about so it can include them in the sandbox. But Rust/Cargo has no way
58/// of formally declaring a dependency on these files so we must manually specify them.
59///
60#[derive(Default, Debug, serde::Deserialize)]
61pub struct CrateConfig {
62/// Should we skip generating a `BUILD.bazel` file entirely for this crate.
63#[serde(default)]
64skip_generating: bool,
65/// Additive content we paste at the bottom of the generated `BUILD.bazel` file.
66additive_content: Option<String>,
6768/// Extra config for the `rust_library` target.
69#[serde(default)]
70lib: LibraryConfig,
71/// Extra config for the `cargo_build_script` target.
72#[serde(default)]
73build: BuildConfig,
74/// Extra config for any test targets.
75#[serde(alias = "test")]
76 #[serde(default)]
77tests: BTreeMap<String, TestConfig>,
78/// Extra config for any binary targets.
79#[serde(alias = "binary")]
80 #[serde(default)]
81binaries: BTreeMap<String, BinaryConfig>,
82}
8384impl CrateConfig {
85pub fn new(package: &PackageMetadata) -> Self {
86 package
87 .metadata_table()
88 .get(KEY_NAME)
89 .and_then(|v| serde_json::from_value(v.clone()).ok())
90 .unwrap_or_default()
91 }
9293pub fn skip_generating(&self) -> bool {
94self.skip_generating
95 }
9697pub fn additive_content(&self) -> Option<AdditiveContent> {
98self.additive_content
99 .as_ref()
100 .map(|s| AdditiveContent::new(s.as_str()))
101 }
102103pub fn lib(&self) -> &LibraryConfig {
104&self.lib
105 }
106107pub fn lib_test(&self) -> &TestConfig {
108self.test(LIB_TEST_NAME)
109 }
110111pub fn doc_test(&self) -> &TestConfig {
112self.test(DOC_TEST_NAME)
113 }
114115pub fn build(&self) -> &BuildConfig {
116&self.build
117 }
118119pub fn test(&self, name: &str) -> &TestConfig {
120static EMPTY_TEST: LazyLock<TestConfig> = LazyLock::new(TestConfig::default);
121self.tests.get(name).unwrap_or(&*EMPTY_TEST)
122 }
123124pub fn binary(&self, name: &str) -> &BinaryConfig {
125static EMPTY_BINARY: LazyLock<BinaryConfig> = LazyLock::new(BinaryConfig::default);
126self.binaries.get(name).unwrap_or(&*EMPTY_BINARY)
127 }
128}
129130/// Extra configuration for a [`RustLibrary`] target.
131///
132/// [`RustLibrary`]: crate::targets::RustLibrary
133#[derive(Default, Debug, serde::Deserialize)]
134pub struct LibraryConfig {
135#[serde(flatten)]
136common: CommonConfig,
137138/// By default Bazel enables all features of a crate. If this field is set we'll override that
139 /// behavior and only set the specified features.
140features_override: Option<Vec<String>>,
141/// Extra dependencies to include.
142#[serde(default)]
143extra_deps: Vec<String>,
144/// Extra proc macro dependencies to include.
145#[serde(default)]
146extra_proc_macro_deps: Vec<String>,
147/// Should we disable pipelined compilation for this library.
148#[serde(default)]
149disable_pipelining: Option<bool>,
150}
151152impl LibraryConfig {
153pub fn common(&self) -> &CommonConfig {
154&self.common
155 }
156157pub fn features_override(&self) -> Option<&Vec<String>> {
158self.features_override.as_ref()
159 }
160161pub fn extra_deps(&self) -> &[String] {
162&self.extra_deps
163 }
164165pub fn extra_proc_macro_deps(&self) -> &[String] {
166&self.extra_proc_macro_deps
167 }
168169pub fn disable_pipelining(&self) -> Option<bool> {
170self.disable_pipelining
171 }
172}
173174/// Extra configuration for a [`CargoBuildScript`] target.
175///
176/// [`CargoBuildScript`]: crate::targets::CargoBuildScript
177#[derive(Default, Debug, serde::Deserialize)]
178pub struct BuildConfig {
179#[serde(flatten)]
180common: CommonConfig,
181182/// Environment variables to set for the build script.
183#[serde(default)]
184build_script_env: BTreeMap<String, String>,
185/// Skip the automatic search for protobuf files.
186#[serde(default)]
187skip_proto_search: bool,
188}
189190impl BuildConfig {
191pub fn common(&self) -> &CommonConfig {
192&self.common
193 }
194195pub fn build_script_env(&self) -> &BTreeMap<String, String> {
196&self.build_script_env
197 }
198199pub fn skip_proto_search(&self) -> bool {
200self.skip_proto_search
201 }
202}
203204/// Extra configuration for a [`RustTest`] target.
205///
206/// [`RustTest`]: crate::targets::RustTest
207#[derive(Default, Debug, serde::Deserialize)]
208pub struct TestConfig {
209#[serde(flatten)]
210common: CommonConfig,
211212/// ["size"](https://bazel.build/reference/be/common-definitions#common-attributes-tests)
213 /// of the test target, this defines how many resources Bazel provides to the test.
214size: Option<RustTestSize>,
215/// Set of environment variables to set when the test is executed.
216#[serde(default)]
217env: BTreeMap<String, String>,
218}
219220impl TestConfig {
221pub fn common(&self) -> &CommonConfig {
222&self.common
223 }
224225pub fn size(&self) -> Option<&RustTestSize> {
226self.size.as_ref()
227 }
228229pub fn env(&self) -> &BTreeMap<String, String> {
230&self.env
231 }
232}
233234/// Extra configuration for a [`RustBinary`] target.
235///
236/// [`RustBinary`]: crate::targets::RustBinary
237#[derive(Default, Debug, serde::Deserialize)]
238pub struct BinaryConfig {
239#[serde(flatten)]
240common: CommonConfig,
241242/// Additional environment variables that get set when invoked by `bazel run`.
243#[serde(default)]
244env: BTreeMap<String, String>,
245}
246247impl BinaryConfig {
248pub fn common(&self) -> &CommonConfig {
249&self.common
250 }
251252pub fn env(&self) -> &BTreeMap<String, String> {
253&self.env
254 }
255}
256257/// Extra config that is common among all target types.
258#[derive(Default, Debug, serde::Deserialize)]
259pub struct CommonConfig {
260/// Skip generating this target.
261#[serde(default)]
262skip: bool,
263/// Paths that will be added to the `compile_data` field of the generated Bazel target.
264#[serde(default)]
265compile_data: Vec<String>,
266/// Paths that will be added to the `data` field of the generated Bazel target.
267#[serde(default)]
268data: Vec<String>,
269/// Extra flags that should be passed to the Rust compiler.
270#[serde(default)]
271rustc_flags: Vec<String>,
272/// Set of environment variables to set for the Rust compiler.
273#[serde(default)]
274rustc_env: BTreeMap<String, String>,
275}
276277impl CommonConfig {
278pub fn skip(&self) -> bool {
279self.skip
280 }
281282/// Returns a tuple of `(<non-glob paths>, <glob paths, if any>)`.
283pub fn compile_data(&self) -> (Vec<&String>, Option<Vec<&String>>) {
284let paths: Vec<_> = self
285.compile_data
286 .iter()
287 .filter(|s| !s.contains('*'))
288 .collect();
289let globs: Vec<_> = self
290.compile_data
291 .iter()
292 .filter(|s| s.contains('*'))
293 .collect();
294295let globs = if globs.is_empty() { None } else { Some(globs) };
296297 (paths, globs)
298 }
299300/// Returns a tuple of `(<non-glob paths>, <glob paths, if any>)`.
301pub fn data(&self) -> (Vec<&String>, Option<Vec<&String>>) {
302let paths: Vec<_> = self.data.iter().filter(|s| !s.contains('*')).collect();
303let globs: Vec<_> = self.data.iter().filter(|s| s.contains('*')).collect();
304305let globs = if globs.is_empty() { None } else { Some(globs) };
306307 (paths, globs)
308 }
309310pub fn rustc_flags(&self) -> &[String] {
311&self.rustc_flags
312 }
313314pub fn rustc_env(&self) -> &BTreeMap<String, String> {
315&self.rustc_env
316 }
317}