Skip to content

Commit b535980

Browse files
committed
buildsys: use clap for env vars
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.
1 parent 40e21e0 commit b535980

File tree

8 files changed

+253
-129
lines changed

8 files changed

+253
-129
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

tools/buildsys/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ exclude = ["README.md"]
1010

1111
[dependencies]
1212
bottlerocket-variant = { version = "0.1", path = "../bottlerocket-variant" }
13+
clap = { version = "4", features = ["derive", "env"] }
1314
duct = "0.13"
1415
hex = "0.4"
1516
lazy_static = "1"

tools/buildsys/src/args.rs

Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
/*!
2+
3+
These structs provide the CLI interface for buildsys which is called from Cargo.toml and accepts all
4+
of its input arguments from environment variables.
5+
6+
!*/
7+
8+
use clap::{Parser, Subcommand};
9+
use std::path::PathBuf;
10+
11+
/// A tool for building Bottlerocket images and artifacts.
12+
#[derive(Debug, Parser)]
13+
pub(crate) struct Buildsys {
14+
#[command(subcommand)]
15+
pub(crate) command: Command,
16+
}
17+
18+
#[derive(Subcommand, Debug)]
19+
pub(crate) enum Command {
20+
BuildPackage(BuildPackageArgs),
21+
BuildVariant(BuildVariantArgs),
22+
}
23+
24+
/// Arguments common to all subcommands.
25+
#[derive(Debug, Parser)]
26+
pub(crate) struct Common {
27+
#[arg(long, env = "BUILDSYS_ARCH")]
28+
pub(crate) arch: String,
29+
30+
#[arg(long, env = "BUILDSYS_OUTPUT_DIR")]
31+
pub(crate) output_dir: PathBuf,
32+
33+
#[arg(long, env = "BUILDSYS_ROOT_DIR")]
34+
pub(crate) root_dir: PathBuf,
35+
36+
#[arg(long, env = "BUILDSYS_STATE_DIR")]
37+
pub(crate) state_dir: PathBuf,
38+
39+
#[arg(long, env = "BUILDSYS_TIMESTAMP")]
40+
pub(crate) timestamp: String,
41+
42+
#[arg(long, env = "BUILDSYS_VERSION_FULL")]
43+
pub(crate) version_full: String,
44+
45+
#[arg(long, env = "CARGO_MANIFEST_DIR")]
46+
pub(crate) cargo_manifest_dir: PathBuf,
47+
48+
#[arg(long, env = "TLPRIVATE_SDK_IMAGE")]
49+
pub(crate) sdk_image: String,
50+
51+
#[arg(long, env = "TLPRIVATE_TOOLCHAIN")]
52+
pub(crate) toolchain: String,
53+
}
54+
55+
impl Common {
56+
pub(crate) fn rerun_for_envs() {
57+
[
58+
"BUILDSYS_ARCH",
59+
"BUILDSYS_OUTPUT_DIR",
60+
"BUILDSYS_ROOT_DIR",
61+
"BUILDSYS_VERSION_FULL",
62+
"CARGO_MANIFEST_DIR",
63+
"TLPRIVATE_SDK_IMAGE",
64+
"TLPRIVATE_TOOLCHAIN",
65+
]
66+
.iter()
67+
.for_each(|&var| rerun_for_env(var));
68+
}
69+
}
70+
71+
/// Build RPMs from a spec file and sources.
72+
#[derive(Debug, Parser)]
73+
pub(crate) struct BuildPackageArgs {
74+
#[arg(long, env = "BUILDSYS_PACKAGES_DIR")]
75+
pub(crate) packages_dir: PathBuf,
76+
77+
#[arg(long, env = "BUILDSYS_VARIANT")]
78+
pub(crate) variant: String,
79+
80+
#[arg(long, env = "BUILDSYS_SOURCES_DIR")]
81+
pub(crate) sources_dir: PathBuf,
82+
83+
#[arg(long, env = "CARGO_PKG_NAME")]
84+
pub(crate) cargo_package_name: String,
85+
86+
#[command(flatten)]
87+
pub(crate) common: Common,
88+
}
89+
90+
impl BuildPackageArgs {
91+
pub(crate) fn rerun_for_envs() {
92+
Common::rerun_for_envs();
93+
[
94+
"BUILDSYS_PACKAGES_DIR",
95+
"BUILDSYS_SOURCES_DIR",
96+
"BUILDSYS_VARIANT",
97+
"CARGO_PKG_NAME",
98+
]
99+
.iter()
100+
.for_each(|&var| rerun_for_env(var));
101+
}
102+
}
103+
104+
/// Build filesystem and disk images from RPMs.
105+
#[derive(Debug, Parser)]
106+
pub(crate) struct BuildVariantArgs {
107+
#[arg(long, env = "BUILDSYS_NAME")]
108+
pub(crate) name: String,
109+
110+
#[arg(long, env = "BUILDSYS_PRETTY_NAME")]
111+
pub(crate) pretty_name: String,
112+
113+
#[arg(long, env = "BUILDSYS_VARIANT")]
114+
pub(crate) variant: String,
115+
116+
#[arg(long, env = "BUILDSYS_VERSION_BUILD")]
117+
pub(crate) version_build: String,
118+
119+
#[arg(long, env = "BUILDSYS_VERSION_IMAGE")]
120+
pub(crate) version_image: String,
121+
122+
#[command(flatten)]
123+
pub(crate) common: Common,
124+
}
125+
126+
impl BuildVariantArgs {
127+
pub(crate) fn rerun_for_envs() {
128+
Common::rerun_for_envs();
129+
[
130+
"BUILDSYS_NAME",
131+
"BUILDSYS_PRETTY_NAME",
132+
"BUILDSYS_VARIANT",
133+
"BUILDSYS_VERSION_BUILD",
134+
"BUILDSYS_VERSION_IMAGE",
135+
]
136+
.iter()
137+
.for_each(|&var| rerun_for_env(var));
138+
}
139+
}
140+
141+
fn rerun_for_env(var: &str) {
142+
println!("cargo:rerun-if-env-changed={}", var);
143+
}

tools/buildsys/src/builder.rs

Lines changed: 53 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,9 @@ the repository's top-level Dockerfile.
55
66
*/
77
pub(crate) mod error;
8-
use error::Result;
98

10-
use crate::constants::{SDK_VAR, TOOLCHAIN_VAR};
119
use duct::cmd;
10+
use error::Result;
1211
use lazy_static::lazy_static;
1312
use nonzero_ext::nonzero;
1413
use rand::Rng;
@@ -23,6 +22,7 @@ use std::path::{Path, PathBuf};
2322
use std::process::Output;
2423
use walkdir::{DirEntry, WalkDir};
2524

25+
use crate::args::{BuildPackageArgs, BuildVariantArgs};
2626
use buildsys::manifest::{ImageFeature, ImageFormat, ImageLayout, PartitionPlan, SupportedArch};
2727

2828
const TOOLS_DIR: &str = "TWOLITER_TOOLS_DIR";
@@ -91,20 +91,20 @@ pub(crate) struct PackageBuilder;
9191
impl PackageBuilder {
9292
/// Build RPMs for the specified package.
9393
pub(crate) fn build(
94+
build_packages_args: &BuildPackageArgs,
9495
package: &str,
9596
image_features: Option<HashSet<&ImageFeature>>,
9697
) -> Result<Self> {
97-
let output_dir: PathBuf = getenv("BUILDSYS_PACKAGES_DIR")?.into();
98-
let arch = getenv("BUILDSYS_ARCH")?;
99-
let goarch = serde_plain::from_str::<SupportedArch>(&arch)
100-
.context(error::UnsupportedArchSnafu { arch: &arch })?
98+
let arch = &build_packages_args.common.arch;
99+
let goarch = serde_plain::from_str::<SupportedArch>(arch)
100+
.context(error::UnsupportedArchSnafu { arch })?
101101
.goarch();
102102

103103
let mut args = Vec::new();
104104
args.push("--network".into());
105105
args.push("none".into());
106106
args.build_arg("PACKAGE", package);
107-
args.build_arg("ARCH", &arch);
107+
args.build_arg("ARCH", arch);
108108
args.build_arg("GOARCH", goarch);
109109

110110
// Pass certain environment variables into the build environment. These variables aren't
@@ -136,7 +136,18 @@ impl PackageBuilder {
136136
}
137137
}
138138

139-
build(BuildType::Package, package, &arch, args, &tag, &output_dir)?;
139+
build(
140+
BuildType::Package,
141+
package,
142+
arch,
143+
args,
144+
&tag,
145+
&build_packages_args.packages_dir,
146+
&build_packages_args.common.root_dir,
147+
&build_packages_args.common.state_dir,
148+
&build_packages_args.common.sdk_image,
149+
&build_packages_args.common.toolchain,
150+
)?;
140151

141152
Ok(Self)
142153
}
@@ -147,18 +158,16 @@ pub(crate) struct VariantBuilder;
147158
impl VariantBuilder {
148159
/// Build a variant with the specified packages installed.
149160
pub(crate) fn build(
161+
build_variant_args: &BuildVariantArgs,
150162
packages: &[String],
151163
image_format: Option<&ImageFormat>,
152164
image_layout: Option<&ImageLayout>,
153165
kernel_parameters: Option<&Vec<String>>,
154166
image_features: Option<HashSet<&ImageFeature>>,
155167
) -> Result<Self> {
156-
let output_dir: PathBuf = getenv("BUILDSYS_OUTPUT_DIR")?.into();
157-
158-
let variant = getenv("BUILDSYS_VARIANT")?;
159-
let arch = getenv("BUILDSYS_ARCH")?;
160-
let goarch = serde_plain::from_str::<SupportedArch>(&arch)
161-
.context(error::UnsupportedArchSnafu { arch: &arch })?
168+
let arch = &build_variant_args.common.arch;
169+
let goarch = serde_plain::from_str::<SupportedArch>(arch)
170+
.context(error::UnsupportedArchSnafu { arch })?
162171
.goarch();
163172

164173
let image_layout = image_layout.cloned().unwrap_or_default();
@@ -176,13 +185,13 @@ impl VariantBuilder {
176185
args.push("--network".into());
177186
args.push("host".into());
178187
args.build_arg("PACKAGES", packages.join(" "));
179-
args.build_arg("ARCH", &arch);
188+
args.build_arg("ARCH", arch);
180189
args.build_arg("GOARCH", goarch);
181-
args.build_arg("VARIANT", &variant);
182-
args.build_arg("VERSION_ID", getenv("BUILDSYS_VERSION_IMAGE")?);
183-
args.build_arg("BUILD_ID", getenv("BUILDSYS_VERSION_BUILD")?);
184-
args.build_arg("PRETTY_NAME", getenv("BUILDSYS_PRETTY_NAME")?);
185-
args.build_arg("IMAGE_NAME", getenv("BUILDSYS_NAME")?);
190+
args.build_arg("VARIANT", &build_variant_args.variant);
191+
args.build_arg("VERSION_ID", &build_variant_args.version_image);
192+
args.build_arg("BUILD_ID", &build_variant_args.version_build);
193+
args.build_arg("PRETTY_NAME", &build_variant_args.pretty_name);
194+
args.build_arg("IMAGE_NAME", &build_variant_args.name);
186195
args.build_arg(
187196
"IMAGE_FORMAT",
188197
match image_format {
@@ -222,17 +231,24 @@ impl VariantBuilder {
222231
// Add known secrets to the build argments.
223232
add_secrets(&mut args)?;
224233

225-
// Always rebuild variants since they are located in a different workspace,
226-
// and don't directly track changes in the underlying packages.
227-
getenv("BUILDSYS_TIMESTAMP")?;
228-
229234
let tag = format!(
230235
"buildsys-var-{variant}-{arch}",
231-
variant = variant,
236+
variant = build_variant_args.variant,
232237
arch = arch
233238
);
234239

235-
build(BuildType::Variant, &variant, &arch, args, &tag, &output_dir)?;
240+
build(
241+
BuildType::Variant,
242+
&build_variant_args.variant,
243+
arch,
244+
args,
245+
&tag,
246+
&build_variant_args.common.output_dir,
247+
&build_variant_args.common.root_dir,
248+
&build_variant_args.common.state_dir,
249+
&build_variant_args.common.sdk_image,
250+
&build_variant_args.common.toolchain,
251+
)?;
236252

237253
Ok(Self)
238254
}
@@ -246,33 +262,34 @@ enum BuildType {
246262
}
247263

248264
/// Invoke a series of `docker` commands to drive a package or variant build.
265+
// TODO - refactor this and the builders a bit to have fewer arguments
266+
#[allow(clippy::too_many_arguments)]
249267
fn build(
250268
kind: BuildType,
251269
what: &str,
252270
arch: &str,
253271
build_args: Vec<String>,
254272
tag: &str,
255273
output_dir: &PathBuf,
274+
root: &PathBuf,
275+
state_dir: &Path,
276+
sdk: &str,
277+
toolchain: &str,
256278
) -> Result<()> {
257-
let root = getenv("BUILDSYS_ROOT_DIR")?;
258-
env::set_current_dir(&root).context(error::DirectoryChangeSnafu { path: &root })?;
279+
env::set_current_dir(root).context(error::DirectoryChangeSnafu { path: root })?;
259280

260281
// Compute a per-checkout prefix for the tag to avoid collisions.
261282
let mut d = Sha512::new();
262-
d.update(&root);
283+
d.update(root.display().to_string());
263284
let digest = hex::encode(d.finalize());
264285
let token = &digest[..12];
265286
let tag = format!("{}-{}", tag, token);
266287

267-
// Our SDK and toolchain are picked by the external `cargo make` invocation.
268-
let sdk = getenv(SDK_VAR)?;
269-
let toolchain = getenv(TOOLCHAIN_VAR)?;
270-
271288
// Avoid using a cached layer from a previous build.
272289
let nocache = rand::thread_rng().gen::<u32>();
273290

274291
// Create a directory for tracking outputs before we move them into position.
275-
let build_dir = create_build_dir(&kind, what, arch)?;
292+
let build_dir = create_build_dir(&kind, what, arch, state_dir)?;
276293

277294
// Clean up any previous outputs we have tracked.
278295
clean_build_files(&build_dir, output_dir)?;
@@ -428,13 +445,13 @@ fn add_secrets(args: &mut Vec<String>) -> Result<()> {
428445
// =^..^= =^..^= =^..^= =^..^= =^..^= =^..^= =^..^= =^..^= =^..^= =^..^= =^..^=
429446

430447
/// Create a directory for build artifacts.
431-
fn create_build_dir(kind: &BuildType, name: &str, arch: &str) -> Result<PathBuf> {
448+
fn create_build_dir(kind: &BuildType, name: &str, arch: &str, state_dir: &Path) -> Result<PathBuf> {
432449
let prefix = match kind {
433450
BuildType::Package => "packages",
434451
BuildType::Variant => "variants",
435452
};
436453

437-
let path = [&getenv("BUILDSYS_STATE_DIR")?, arch, prefix, name]
454+
let path = [&state_dir.display().to_string(), arch, prefix, name]
438455
.iter()
439456
.collect();
440457

@@ -590,14 +607,6 @@ where
590607
.filter(|e| e.is_file() || e.is_symlink())
591608
}
592609

593-
/// Retrieve a BUILDSYS_* variable that we expect to be set in the environment,
594-
/// and ensure that we track it for changes, since it will directly affect the
595-
/// output.
596-
fn getenv(var: &str) -> Result<String> {
597-
println!("cargo:rerun-if-env-changed={}", var);
598-
env::var(var).context(error::EnvironmentSnafu { var })
599-
}
600-
601610
// =^..^= =^..^= =^..^= =^..^= =^..^= =^..^= =^..^= =^..^= =^..^= =^..^= =^..^=
602611

603612
/// Helper trait for constructing buildkit --build-arg arguments.

0 commit comments

Comments
 (0)