Skip to content

Commit

Permalink
buildsys: use clap for env vars
Browse files Browse the repository at this point in the history
Required input in the form of environment variables was sprinkled
throughout the code. Here we aggregate the inputs into a CLI interface
using Clap, while still allowing all to specified via environment
variables.
  • Loading branch information
webern committed Jan 21, 2024
1 parent 40e21e0 commit c71f56e
Show file tree
Hide file tree
Showing 8 changed files with 309 additions and 138 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions tools/buildsys/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ exclude = ["README.md"]

[dependencies]
bottlerocket-variant = { version = "0.1", path = "../bottlerocket-variant" }
clap = { version = "4", features = ["derive", "env"] }
duct = "0.13"
hex = "0.4"
lazy_static = "1"
Expand Down
188 changes: 188 additions & 0 deletions tools/buildsys/src/args.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,188 @@
/*!
These structs provide the CLI interface for buildsys which is called from Cargo.toml and accepts all
of its input arguments from environment variables.
!*/

use clap::{Parser, Subcommand};
use std::path::PathBuf;

/// A list of environment variables and the type of build that should be rerun if that environment
/// variable changes. The build type is represented with bit flags so that we can easily list
/// multiple build types for a single variable. See `[BuildType]` and `[rerun_for_envs]` below to
/// see how this list is used.
const REBUILD_VARS: [(&str, u8); 13] = [
("BUILDSYS_ARCH", PACKAGE | VARIANT),
("BUILDSYS_NAME", VARIANT),
("BUILDSYS_OUTPUT_DIR", VARIANT),
("BUILDSYS_PACKAGES_DIR", PACKAGE),
("BUILDSYS_PRETTY_NAME", VARIANT),
("BUILDSYS_ROOT_DIR", PACKAGE | VARIANT),
("BUILDSYS_STATE_DIR", PACKAGE | VARIANT),
("BUILDSYS_TIMESTAMP", VARIANT),
("BUILDSYS_VARIANT", VARIANT),
("BUILDSYS_VERSION_BUILD", VARIANT),
("BUILDSYS_VERSION_IMAGE", VARIANT),
("TLPRIVATE_SDK_IMAGE", PACKAGE | VARIANT),
("TLPRIVATE_TOOLCHAIN", PACKAGE | VARIANT),
];

/// A tool for building Bottlerocket images and artifacts.
#[derive(Debug, Parser)]
pub(crate) struct Buildsys {
#[command(subcommand)]
pub(crate) command: Command,
}

#[derive(Subcommand, Debug)]
pub(crate) enum Command {
BuildPackage(BuildPackageArgs),
BuildVariant(BuildVariantArgs),
}

impl Command {
pub(crate) fn build_type(&self) -> BuildType {
match self {
Command::BuildPackage(_) => BuildType::Package,
Command::BuildVariant(_) => BuildType::Variant,
}
}
}

/// Arguments common to all subcommands.
#[derive(Debug, Parser)]
pub(crate) struct Common {
#[arg(long, env = "BUILDSYS_ARCH")]
pub(crate) arch: String,

#[arg(long, env = "BUILDSYS_OUTPUT_DIR")]
pub(crate) output_dir: PathBuf,

#[arg(long, env = "BUILDSYS_ROOT_DIR")]
pub(crate) root_dir: PathBuf,

#[arg(long, env = "BUILDSYS_STATE_DIR")]
pub(crate) state_dir: PathBuf,

#[arg(long, env = "BUILDSYS_TIMESTAMP")]
pub(crate) timestamp: String,

#[arg(long, env = "BUILDSYS_VERSION_FULL")]
pub(crate) version_full: String,

#[arg(long, env = "CARGO_MANIFEST_DIR")]
pub(crate) cargo_manifest_dir: PathBuf,

#[arg(long, env = "TLPRIVATE_SDK_IMAGE")]
pub(crate) sdk_image: String,

#[arg(long, env = "TLPRIVATE_TOOLCHAIN")]
pub(crate) toolchain: String,
}

/// Build RPMs from a spec file and sources.
#[derive(Debug, Parser)]
pub(crate) struct BuildPackageArgs {
#[arg(long, env = "BUILDSYS_PACKAGES_DIR")]
pub(crate) packages_dir: PathBuf,

#[arg(long, env = "BUILDSYS_VARIANT")]
pub(crate) variant: String,

#[arg(long, env = "BUILDSYS_SOURCES_DIR")]
pub(crate) sources_dir: PathBuf,

#[arg(long, env = "CARGO_PKG_NAME")]
pub(crate) cargo_package_name: String,

#[command(flatten)]
pub(crate) common: Common,
}

/// Build filesystem and disk images from RPMs.
#[derive(Debug, Parser)]
pub(crate) struct BuildVariantArgs {
#[arg(long, env = "BUILDSYS_NAME")]
pub(crate) name: String,

#[arg(long, env = "BUILDSYS_PRETTY_NAME")]
pub(crate) pretty_name: String,

#[arg(long, env = "BUILDSYS_VARIANT")]
pub(crate) variant: String,

#[arg(long, env = "BUILDSYS_VERSION_BUILD")]
pub(crate) version_build: String,

#[arg(long, env = "BUILDSYS_VERSION_IMAGE")]
pub(crate) version_image: String,

#[command(flatten)]
pub(crate) common: Common,
}

/// Returns the environment variables that need to be watched for a given `[BuildType]`.
fn sensitive_env_vars(build_type: BuildType) -> impl Iterator<Item = &'static str> {
REBUILD_VARS
.into_iter()
.filter(move |(_, flags)| build_type.includes(*flags))
.map(|(var, _)| var)
}

/// Emits the cargo directives for a the list of sensitive environment variables for a given
/// `[BuildType]`.
pub(crate) fn rerun_for_envs(build_type: BuildType) {
for var in sensitive_env_vars(build_type) {
println!("cargo:rerun-if-env-changed={}", var)
}
}

/// The thing that buildsys is building.
#[repr(u8)]
#[derive(Debug, Clone, Copy)]
pub(crate) enum BuildType {
Package = 0b00000001,
Variant = 0b00000010,
}

impl BuildType {
fn includes(&self, flags: u8) -> bool {
let this = *self as u8;
let and = flags & this;
and == this
}
}

const PACKAGE: u8 = BuildType::Package as u8;
const VARIANT: u8 = BuildType::Variant as u8;

#[test]
fn build_type_includes_test() {
// true
assert!(BuildType::Package.includes(PACKAGE | VARIANT));
assert!(BuildType::Variant.includes(VARIANT));
assert!(BuildType::Variant.includes(VARIANT | PACKAGE));

// false
assert!(!BuildType::Package.includes(VARIANT));
assert!(!BuildType::Variant.includes(PACKAGE));
assert!(!BuildType::Variant.includes(32));
assert!(!BuildType::Variant.includes(0));
}

#[test]
fn test_sensitive_env_vars_variant() {
let list: Vec<&str> = sensitive_env_vars(BuildType::Variant).collect();
assert!(list.contains(&"BUILDSYS_ARCH"));
assert!(list.contains(&"BUILDSYS_VARIANT"));
assert!(!list.contains(&"BUILDSYS_PACKAGES_DIR"));
}

#[test]
fn test_sensitive_env_vars_package() {
let list: Vec<&str> = sensitive_env_vars(BuildType::Package).collect();
assert!(list.contains(&"BUILDSYS_ARCH"));
assert!(list.contains(&"BUILDSYS_PACKAGES_DIR"));
assert!(!list.contains(&"BUILDSYS_VARIANT"));
}
Loading

0 comments on commit c71f56e

Please sign in to comment.