1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317
// Copyright Materialize, Inc. and contributors. All rights reserved.
//
// Use of this software is governed by the Business Source License
// included in the LICENSE file.
//
// As of the Change Date specified in that file, in accordance with
// the Business Source License, use of this software will be governed
// by the Apache License, Version 2.0.
use std::borrow::Cow;
use std::collections::BTreeMap;
use guppy::graph::PackageMetadata;
use std::sync::LazyLock;
use crate::targets::{AdditiveContent, RustTestSize};
const KEY_NAME: &str = "cargo-gazelle";
/// Name that should be used to specify extra config for library tests.
///
/// e.g. `package.metadata.cargo-gazelle.test.lib`
const LIB_TEST_NAME: &str = "lib";
/// Name that should be used to specify extra config for doc tests.
///
/// e.g. `package.metadata.cargo-gazelle.test.doc`
const DOC_TEST_NAME: &str = "doc";
/// Global configuration for generating `BUILD` files.
#[derive(Debug, Clone)]
pub struct GlobalConfig {
pub ignored_crates: Vec<Cow<'static, str>>,
pub proto_build_crates: Vec<Cow<'static, str>>,
}
impl Default for GlobalConfig {
fn default() -> Self {
GlobalConfig {
ignored_crates: vec!["workspace-hack".into()],
proto_build_crates: vec!["prost_build".into(), "tonic_build".into()],
}
}
}
impl GlobalConfig {
/// Returns `true` if the named dependency should be included, `false` if it should be ignored.
pub fn include_dep(&self, name: &str) -> bool {
!self.ignored_crates.contains(&Cow::Borrowed(name))
}
}
/// Extra configuration for the Bazel targets generated for a crate.
///
/// We should try to make generating `BUILD.bazel` files is as seamless as possible, but there are
/// instances where this isn't possible. For example, some tests rely on text-based snapshot files
/// that Bazel needs to know about so it can include them in the sandbox. But Rust/Cargo has no way
/// of formally declaring a dependency on these files so we must manually specify them.
///
#[derive(Default, Debug, serde::Deserialize)]
pub struct CrateConfig {
/// Should we skip generating a `BUILD.bazel` file entirely for this crate.
#[serde(default)]
skip_generating: bool,
/// Additive content we paste at the bottom of the generated `BUILD.bazel` file.
additive_content: Option<String>,
/// Extra config for the `rust_library` target.
#[serde(default)]
lib: LibraryConfig,
/// Extra config for the `cargo_build_script` target.
#[serde(default)]
build: BuildConfig,
/// Extra config for any test targets.
#[serde(alias = "test")]
#[serde(default)]
tests: BTreeMap<String, TestConfig>,
/// Extra config for any binary targets.
#[serde(alias = "binary")]
#[serde(default)]
binaries: BTreeMap<String, BinaryConfig>,
}
impl CrateConfig {
pub fn new(package: &PackageMetadata) -> Self {
package
.metadata_table()
.get(KEY_NAME)
.and_then(|v| serde_json::from_value(v.clone()).ok())
.unwrap_or_default()
}
pub fn skip_generating(&self) -> bool {
self.skip_generating
}
pub fn additive_content(&self) -> Option<AdditiveContent> {
self.additive_content
.as_ref()
.map(|s| AdditiveContent::new(s.as_str()))
}
pub fn lib(&self) -> &LibraryConfig {
&self.lib
}
pub fn lib_test(&self) -> &TestConfig {
self.test(LIB_TEST_NAME)
}
pub fn doc_test(&self) -> &TestConfig {
self.test(DOC_TEST_NAME)
}
pub fn build(&self) -> &BuildConfig {
&self.build
}
pub fn test(&self, name: &str) -> &TestConfig {
static EMPTY_TEST: LazyLock<TestConfig> = LazyLock::new(TestConfig::default);
self.tests.get(name).unwrap_or(&*EMPTY_TEST)
}
pub fn binary(&self, name: &str) -> &BinaryConfig {
static EMPTY_BINARY: LazyLock<BinaryConfig> = LazyLock::new(BinaryConfig::default);
self.binaries.get(name).unwrap_or(&*EMPTY_BINARY)
}
}
/// Extra configuration for a [`RustLibrary`] target.
///
/// [`RustLibrary`]: crate::targets::RustLibrary
#[derive(Default, Debug, serde::Deserialize)]
pub struct LibraryConfig {
#[serde(flatten)]
common: CommonConfig,
/// By default Bazel enables all features of a crate. If this field is set we'll override that
/// behavior and only set the specified features.
features_override: Option<Vec<String>>,
/// Extra dependencies to include.
#[serde(default)]
extra_deps: Vec<String>,
/// Extra proc macro dependencies to include.
#[serde(default)]
extra_proc_macro_deps: Vec<String>,
/// Should we disable pipelined compilation for this library.
#[serde(default)]
disable_pipelining: Option<bool>,
}
impl LibraryConfig {
pub fn common(&self) -> &CommonConfig {
&self.common
}
pub fn features_override(&self) -> Option<&Vec<String>> {
self.features_override.as_ref()
}
pub fn extra_deps(&self) -> &[String] {
&self.extra_deps
}
pub fn extra_proc_macro_deps(&self) -> &[String] {
&self.extra_proc_macro_deps
}
pub fn disable_pipelining(&self) -> Option<bool> {
self.disable_pipelining
}
}
/// Extra configuration for a [`CargoBuildScript`] target.
///
/// [`CargoBuildScript`]: crate::targets::CargoBuildScript
#[derive(Default, Debug, serde::Deserialize)]
pub struct BuildConfig {
#[serde(flatten)]
common: CommonConfig,
/// Environment variables to set for the build script.
#[serde(default)]
build_script_env: BTreeMap<String, String>,
/// Skip the automatic search for protobuf files.
#[serde(default)]
skip_proto_search: bool,
}
impl BuildConfig {
pub fn common(&self) -> &CommonConfig {
&self.common
}
pub fn build_script_env(&self) -> &BTreeMap<String, String> {
&self.build_script_env
}
pub fn skip_proto_search(&self) -> bool {
self.skip_proto_search
}
}
/// Extra configuration for a [`RustTest`] target.
///
/// [`RustTest`]: crate::targets::RustTest
#[derive(Default, Debug, serde::Deserialize)]
pub struct TestConfig {
#[serde(flatten)]
common: CommonConfig,
/// ["size"](https://bazel.build/reference/be/common-definitions#common-attributes-tests)
/// of the test target, this defines how many resources Bazel provides to the test.
size: Option<RustTestSize>,
/// Set of environment variables to set when the test is executed.
#[serde(default)]
env: BTreeMap<String, String>,
}
impl TestConfig {
pub fn common(&self) -> &CommonConfig {
&self.common
}
pub fn size(&self) -> Option<&RustTestSize> {
self.size.as_ref()
}
pub fn env(&self) -> &BTreeMap<String, String> {
&self.env
}
}
/// Extra configuration for a [`RustBinary`] target.
///
/// [`RustBinary`]: crate::targets::RustBinary
#[derive(Default, Debug, serde::Deserialize)]
pub struct BinaryConfig {
#[serde(flatten)]
common: CommonConfig,
/// Additional environment variables that get set when invoked by `bazel run`.
#[serde(default)]
env: BTreeMap<String, String>,
}
impl BinaryConfig {
pub fn common(&self) -> &CommonConfig {
&self.common
}
pub fn env(&self) -> &BTreeMap<String, String> {
&self.env
}
}
/// Extra config that is common among all target types.
#[derive(Default, Debug, serde::Deserialize)]
pub struct CommonConfig {
/// Skip generating this target.
#[serde(default)]
skip: bool,
/// Paths that will be added to the `compile_data` field of the generated Bazel target.
#[serde(default)]
compile_data: Vec<String>,
/// Paths that will be added to the `data` field of the generated Bazel target.
#[serde(default)]
data: Vec<String>,
/// Extra flags that should be passed to the Rust compiler.
#[serde(default)]
rustc_flags: Vec<String>,
/// Set of environment variables to set for the Rust compiler.
#[serde(default)]
rustc_env: BTreeMap<String, String>,
}
impl CommonConfig {
pub fn skip(&self) -> bool {
self.skip
}
/// Returns a tuple of `(<non-glob paths>, <glob paths, if any>)`.
pub fn compile_data(&self) -> (Vec<&String>, Option<Vec<&String>>) {
let paths: Vec<_> = self
.compile_data
.iter()
.filter(|s| !s.contains('*'))
.collect();
let globs: Vec<_> = self
.compile_data
.iter()
.filter(|s| s.contains('*'))
.collect();
let globs = if globs.is_empty() { None } else { Some(globs) };
(paths, globs)
}
/// Returns a tuple of `(<non-glob paths>, <glob paths, if any>)`.
pub fn data(&self) -> (Vec<&String>, Option<Vec<&String>>) {
let paths: Vec<_> = self.data.iter().filter(|s| !s.contains('*')).collect();
let globs: Vec<_> = self.data.iter().filter(|s| s.contains('*')).collect();
let globs = if globs.is_empty() { None } else { Some(globs) };
(paths, globs)
}
pub fn rustc_flags(&self) -> &[String] {
&self.rustc_flags
}
pub fn rustc_env(&self) -> &BTreeMap<String, String> {
&self.rustc_env
}
}