-
Notifications
You must be signed in to change notification settings - Fork 28
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
buildsys: use clap for env vars #134
Conversation
5086222
to
b535980
Compare
tools/buildsys/src/builder.rs
Outdated
&build_variant_args.common.output_dir, | ||
&build_variant_args.common.root_dir, | ||
&build_variant_args.common.state_dir, | ||
&build_variant_args.common.sdk_image, | ||
&build_variant_args.common.toolchain, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see there's a TODO for this, but it would be nice to have a BuildContext
type to hold these values so that the build
function signature isn't so complicated.
tools/buildsys/src/builder.rs
Outdated
let arch = &build_variant_args.common.arch; | ||
let goarch = serde_plain::from_str::<SupportedArch>(arch) | ||
.context(error::UnsupportedArchSnafu { arch })? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we do this conversion from &str
to SupportedArch
just once, perhaps when parsing arguments? Otherwise we end up doing it in a few different places - here and every time supported_arch
is called.
tools/buildsys/src/cache.rs
Outdated
@@ -79,11 +78,9 @@ impl LookasideCache { | |||
|
|||
/// Retrieves a file from the specified URL and write it to the given path, | |||
/// then verifies the contents against the SHA-512 hash provided. | |||
fn fetch_file<P: AsRef<Path>>(url: &str, path: P, hash: &str) -> Result<()> { | |||
fn fetch_file<P: AsRef<Path>>(url: &str, path: P, hash: &str, version: &str) -> Result<()> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't love this function signature change - it feels like one thing to add additional context to a request header based on an environment variable, and another to take an otherwise useless parameter just to do the same.
I'd prefer to either not emit the version at all - do we really care? - or else to add it as a field to LookasideCache
, pass it into LookasideCache::new
, and retrieve it from self
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't know if we care about emitting the version, so I've kept it but with your suggestion to make it self.version.
tools/buildsys/src/args.rs
Outdated
"BUILDSYS_PACKAGES_DIR", | ||
"BUILDSYS_SOURCES_DIR", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm OK with unconditionally emitting these for variables we expect to be constant for the life of a given workspace, if we want to simplify slightly and confine the package- and variant-specific rerun_for_envs()
to truly special cases.
#[arg(long, env = "CARGO_MANIFEST_DIR")] | ||
pub(crate) cargo_manifest_dir: PathBuf, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not as excited about converting cargo's own environment variables into arguments since the environment is the canonical way to pass this information.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My preference would be to include it here anyway for two reasons:
- Readability: self-documenting code
- If I want to write buildsys tests, I may call it directly instead of invoking it from a build.rs script and using args might be helpful for that
Fixup the cargo:rerun-if-env-changed list to make it the same as it used to be. |
Make the version a parameter of LookasideCache |
I've gotten myself into a much bigger refactor of builder.rs so converted this to a draft. |
@bcressey this is ready for you to re-review. The refactor is rather substantial, but I think it is an improvement. My goal was to separate the concerns of a) gathering data from the environment, b) manufacturing and constructing args for docker, and c) the program flow of calling docker. Previously all of those things were happening in the same code-paths. I think having them in separate code paths will make it easier to hack on this for kits. |
c822ce8
to
7f95550
Compare
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.
.image_features() | ||
.map(|set| set.iter().map(|&x| x.to_owned()).collect()), | ||
package, | ||
publish_repo: args.publish_repo, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I opened #151 to correct this since it felt out of place here.
tools/buildsys/src/builder.rs
Outdated
"build {context} \ | ||
--target {target} \ | ||
--tag {tag} \ | ||
--file {dockerfile}", | ||
dockerfile = dockerfile.display(), | ||
target = target, | ||
tag = tag, | ||
) | ||
.split_string(); | ||
|
||
build.extend(build_args); | ||
build.build_arg("SDK", sdk); | ||
build.build_arg("TOOLCHAIN", toolchain); | ||
build.build_arg("NOCACHE", nocache.to_string()); | ||
// Avoid using a cached layer from a concurrent build in another checkout. | ||
build.build_arg("TOKEN", token); | ||
|
||
let create = format!("create --name {} {} true", tag, tag).split_string(); | ||
let cp = format!("cp {}:/output/. {}", tag, build_dir.display()).split_string(); | ||
let rm = format!("rm --force {}", tag).split_string(); | ||
let rmi = format!("rmi --force {}", tag).split_string(); | ||
|
||
// Clean up the stopped container if it exists. | ||
let _ = docker(&rm, Retry::No); | ||
|
||
// Clean up the previous image if it exists. | ||
let _ = docker(&rmi, Retry::No); | ||
|
||
// Build the image, which builds the artifacts we want. | ||
// Work around transient, known failure cases with Docker. | ||
docker( | ||
&build, | ||
Retry::Yes { | ||
attempts: DOCKER_BUILD_MAX_ATTEMPTS, | ||
messages: &[ | ||
&*DOCKER_BUILD_FRONTEND_ERROR, | ||
&*DOCKER_BUILD_DEAD_RECORD_ERROR, | ||
&*UNEXPECTED_EOF_ERROR, | ||
&*CREATEREPO_C_READ_HEADER_ERROR, | ||
], | ||
}, | ||
)?; | ||
|
||
// Create a stopped container so we can copy artifacts out. | ||
docker(&create, Retry::No)?; | ||
|
||
// Copy artifacts into our output directory. | ||
docker(&cp, Retry::No)?; | ||
|
||
// Clean up our stopped container after copying artifacts out. | ||
docker(&rm, Retry::No)?; | ||
|
||
// Clean up our image now that we're done. | ||
docker(&rmi, Retry::No)?; | ||
|
||
// Copy artifacts to the expected directory and write markers to track them. | ||
copy_build_files(&build_dir, output_dir)?; | ||
context = self.context.display(), | ||
dockerfile = self.dockerfile.display(), | ||
target = self.target, | ||
tag = self.tag, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: the spacing on these lines seems off?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: the spacing on these lines seems off?
Yeah fmt is no help here. I've now aligned the multiline string line starts with the variable list below it.
/// 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), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
❤️
This is a nice way to organize something that was very ad-hoc and organic before.
("BUILDSYS_OUTPUT_DIR", VARIANT), | ||
("BUILDSYS_PACKAGES_DIR", PACKAGE), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: it feels like a (pre-existing) mistake to not track BUILDSYS_SOURCES_DIR
if we are tracking BUILDSYS_PACKAGES_DIR
- if we override the build to point to a completely different sources tree then that should invalidate any builds that used those sources.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: it feels like a (pre-existing) mistake to not track
BUILDSYS_SOURCES_DIR
if we are trackingBUILDSYS_PACKAGES_DIR
- if we override the build to point to a completely different sources tree then that should invalidate any builds that used those sources.
I'm going to defer this because I'm concerned that the symlink change when changing variant could trigger something. I'll track this as a GitHub issue and play around with adding it. #152
Edit: no that's dumb. I can safely add this.
Edit, edit: Actually I'm still squeamish... deferring.
#[arg(long, env = "BUILDSYS_VARIANT")] | ||
pub(crate) variant: String, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To check my understanding - we don't want this in Common
because kits won't need this variable?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To check my understanding - we don't want this in
Common
because kits won't need this variable?
That's the idea, yeah. And I think our goal should be that package builds also don't need to know the variant name.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
#[arg(long, env = "BUILDSYS_PACKAGES_DIR")] | ||
pub(crate) packages_dir: PathBuf, | ||
|
||
#[arg(long, env = "BUILDSYS_VARIANT")] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This goes away once we no longer have conditional compilation right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This goes away once we no longer have conditional compilation right?
That should be the goal for packages, yes.
let mut build = format!( | ||
"build . \ | ||
let mut build = format!( | ||
"build {context} \ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: this intending looks strange to me?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: this intending looks strange to me?
Yeah fmt is no help here. I've now aligned the multiline string line starts with the variable list below it.
tools/buildsys/src/builder.rs
Outdated
@@ -358,6 +443,8 @@ fn docker(args: &[String], retry: Retry) -> Result<Output> { | |||
retry_messages = messages; | |||
} | |||
|
|||
println!("Running command:\ndocker {}", args.join(" /\n")); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is only one place out of two we println!()
in this file. Is that intentional? I think so but wanted to ensure this isn't a hold over from debugging and this output is doing something useful for logs/output for the user?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is only one place out of two we println!() in this file. Is that intentional? I think so but wanted to ensure this isn't a hold over from debugging and this output is doing something useful for logs/output for the user?
Good catch. Fixed.
tools/buildsys/src/builder.rs
Outdated
target: String, | ||
tag: String, | ||
root_dir: PathBuf, | ||
// build_dir: PathBuf, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I fixed this.
// build_dir: PathBuf, |
tools/buildsys/src/builder.rs
Outdated
@@ -358,6 +443,8 @@ fn docker(args: &[String], retry: Retry) -> Result<Output> { | |||
retry_messages = messages; | |||
} | |||
|
|||
println!("Running command:\ndocker {}", args.join(" /\n")); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is only one place out of two we println!() in this file. Is that intentional? I think so but wanted to ensure this isn't a hold over from debugging and this output is doing something useful for logs/output for the user?
Good catch. Fixed.
tools/buildsys/src/builder.rs
Outdated
"build {context} \ | ||
--target {target} \ | ||
--tag {tag} \ | ||
--file {dockerfile}", | ||
dockerfile = dockerfile.display(), | ||
target = target, | ||
tag = tag, | ||
) | ||
.split_string(); | ||
|
||
build.extend(build_args); | ||
build.build_arg("SDK", sdk); | ||
build.build_arg("TOOLCHAIN", toolchain); | ||
build.build_arg("NOCACHE", nocache.to_string()); | ||
// Avoid using a cached layer from a concurrent build in another checkout. | ||
build.build_arg("TOKEN", token); | ||
|
||
let create = format!("create --name {} {} true", tag, tag).split_string(); | ||
let cp = format!("cp {}:/output/. {}", tag, build_dir.display()).split_string(); | ||
let rm = format!("rm --force {}", tag).split_string(); | ||
let rmi = format!("rmi --force {}", tag).split_string(); | ||
|
||
// Clean up the stopped container if it exists. | ||
let _ = docker(&rm, Retry::No); | ||
|
||
// Clean up the previous image if it exists. | ||
let _ = docker(&rmi, Retry::No); | ||
|
||
// Build the image, which builds the artifacts we want. | ||
// Work around transient, known failure cases with Docker. | ||
docker( | ||
&build, | ||
Retry::Yes { | ||
attempts: DOCKER_BUILD_MAX_ATTEMPTS, | ||
messages: &[ | ||
&*DOCKER_BUILD_FRONTEND_ERROR, | ||
&*DOCKER_BUILD_DEAD_RECORD_ERROR, | ||
&*UNEXPECTED_EOF_ERROR, | ||
&*CREATEREPO_C_READ_HEADER_ERROR, | ||
], | ||
}, | ||
)?; | ||
|
||
// Create a stopped container so we can copy artifacts out. | ||
docker(&create, Retry::No)?; | ||
|
||
// Copy artifacts into our output directory. | ||
docker(&cp, Retry::No)?; | ||
|
||
// Clean up our stopped container after copying artifacts out. | ||
docker(&rm, Retry::No)?; | ||
|
||
// Clean up our image now that we're done. | ||
docker(&rmi, Retry::No)?; | ||
|
||
// Copy artifacts to the expected directory and write markers to track them. | ||
copy_build_files(&build_dir, output_dir)?; | ||
context = self.context.display(), | ||
dockerfile = self.dockerfile.display(), | ||
target = self.target, | ||
tag = self.tag, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: the spacing on these lines seems off?
Yeah fmt is no help here. I've now aligned the multiline string line starts with the variable list below it.
let mut build = format!( | ||
"build . \ | ||
let mut build = format!( | ||
"build {context} \ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: this intending looks strange to me?
Yeah fmt is no help here. I've now aligned the multiline string line starts with the variable list below it.
#[arg(long, env = "BUILDSYS_PACKAGES_DIR")] | ||
pub(crate) packages_dir: PathBuf, | ||
|
||
#[arg(long, env = "BUILDSYS_VARIANT")] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This goes away once we no longer have conditional compilation right?
That should be the goal for packages, yes.
#[arg(long, env = "BUILDSYS_VARIANT")] | ||
pub(crate) variant: String, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To check my understanding - we don't want this in
Common
because kits won't need this variable?
That's the idea, yeah. And I think our goal should be that package builds also don't need to know the variant name.
("BUILDSYS_OUTPUT_DIR", VARIANT), | ||
("BUILDSYS_PACKAGES_DIR", PACKAGE), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: it feels like a (pre-existing) mistake to not track
BUILDSYS_SOURCES_DIR
if we are trackingBUILDSYS_PACKAGES_DIR
- if we override the build to point to a completely different sources tree then that should invalidate any builds that used those sources.
I'm going to defer this because I'm concerned that the symlink change when changing variant could trigger something. I'll track this as a GitHub issue and play around with adding it. #152
Edit: no that's dumb. I can safely add this.
Edit, edit: Actually I'm still squeamish... deferring.
Refactor the code, primarily in builder.rs. The goal of the refactor is to segregate data collection and docker argument creation from program flow logic.
8b857d9
to
92bc3bb
Compare
Issue number:
Related to, refactor before #70
Description of changes:
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.
Testing done:
Also, during the refactoring process, I captured the output of the docker build command before and after this PR's changes. I found that I was getting the same build args and secrets args before and after (with the exception that SDK, TOOLCHAIN, TOKEN and NOCACHE have been added during build-variant, which seems fine).
Terms of contribution:
By submitting this pull request, I agree that this contribution is dual-licensed under the terms of both the Apache License, version 2.0, and the MIT license.