1
0
Fork 0

get build plan

This commit is contained in:
Alex Janka 2024-10-23 13:07:03 +11:00
parent 5fb5bcd018
commit f872aa2a9c
3 changed files with 129 additions and 2 deletions

38
Cargo.lock generated
View file

@ -1513,6 +1513,18 @@ dependencies = [
"dtoa", "dtoa",
] ]
[[package]]
name = "duct"
version = "0.13.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e4ab5718d1224b63252cd0c6f74f6480f9ffeb117438a2e0f5cf6d9a4798929c"
dependencies = [
"libc",
"once_cell",
"os_pipe",
"shared_child",
]
[[package]] [[package]]
name = "dwrote" name = "dwrote"
version = "0.11.0" version = "0.11.0"
@ -3132,9 +3144,11 @@ version = "0.1.0"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"cargo_metadata", "cargo_metadata",
"duct",
"goblin", "goblin",
"reflink", "reflink",
"serde", "serde",
"serde_json",
"toml", "toml",
] ]
@ -3448,6 +3462,16 @@ dependencies = [
"pin-project-lite", "pin-project-lite",
] ]
[[package]]
name = "os_pipe"
version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5ffd2b0a5634335b135d5728d84c5e0fd726954b87111f7506a61c502280d982"
dependencies = [
"libc",
"windows-sys 0.59.0",
]
[[package]] [[package]]
name = "owned_ttf_parser" name = "owned_ttf_parser"
version = "0.24.0" version = "0.24.0"
@ -4302,9 +4326,9 @@ dependencies = [
[[package]] [[package]]
name = "serde_json" name = "serde_json"
version = "1.0.125" version = "1.0.132"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "83c8e735a073ccf5be70aa8066aa984eaf2fa000db6c8d0100ae605b366d31ed" checksum = "d726bfaff4b320266d395898905d0eba0345aae23b54aee3a737e260fd46db03"
dependencies = [ dependencies = [
"itoa", "itoa",
"memchr", "memchr",
@ -4364,6 +4388,16 @@ dependencies = [
"digest", "digest",
] ]
[[package]]
name = "shared_child"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09fa9338aed9a1df411814a5b2252f7cd206c55ae9bf2fa763f8de84603aa60c"
dependencies = [
"libc",
"windows-sys 0.59.0",
]
[[package]] [[package]]
name = "shlex" name = "shlex"
version = "1.3.0" version = "1.3.0"

View file

@ -14,3 +14,5 @@ goblin = "0.6.1"
reflink = { git = "https://github.com/nicokoch/reflink.git", rev = "e8d93b465f5d9ad340cd052b64bbc77b8ee107e2" } reflink = { git = "https://github.com/nicokoch/reflink.git", rev = "e8d93b465f5d9ad340cd052b64bbc77b8ee107e2" }
serde = { version = "1.0", features = ["derive"] } serde = { version = "1.0", features = ["derive"] }
toml = "0.7.2" toml = "0.7.2"
duct = "0.13.7"
serde_json = "1.0.132"

View file

@ -71,6 +71,97 @@ pub fn main() -> Result<()> {
main_with_args("cargo xtask", args) main_with_args("cargo xtask", args)
} }
pub fn get_build_plan(
command_name: &str,
args: impl IntoIterator<Item = String>,
) -> Result<Vec<(Option<String>, usize)>> {
chdir_workspace_root()?;
let mut args = args.into_iter().chain(
[
"-Zunstable-options",
"--build-plan",
"--message-format=json-render-diagnostics",
]
.into_iter()
.map(String::from),
);
let usage_string = build_usage_string(command_name);
let command = args
.next()
.with_context(|| format!("Missing command name\n\n{usage_string}",))?;
match command.as_str() {
"bundle" => {
// For convenience's sake we'll allow building multiple packages with `-p` just like
// cargo build, but you can also build a single package without specifying `-p`. Since
// multiple packages can be built in parallel if we pass all of these flags to a single
// `cargo build` we'll first build all of these packages and only then bundle them.
let (packages, other_args) = split_bundle_args(args, &usage_string)?;
// As explained above, for efficiency's sake this is a two step process
let output = build_plan(&packages, &other_args)?;
let plan: serde_json::Value = serde_json::from_str(output.lines().nth(1).unwrap())?;
let num_invocations = plan["invocations"].as_array().unwrap().len();
Ok(vec![(None, num_invocations)])
}
"bundle-universal" => {
// The same as `--bundle`, but builds universal binaries for macOS Cargo will also error
// out on duplicate `--target` options, but it seems like a good idea to preemptively
// abort the bundling process if that happens
let (packages, other_args) = split_bundle_args(args, &usage_string)?;
for arg in &other_args {
if arg == "--target" || arg.starts_with("--target=") {
anyhow::bail!(
"'{command_name} xtask bundle-universal' is incompatible with the '{arg}' \
option."
)
}
}
// We can just use the regular build function here. There's sadly no way to build both
// targets in parallel, so this will likely take twice as logn as a regular build.
// TODO: Explicitly specifying the target even on the native target causes a rebuild in
// the target `target/<target_triple>` directory. This makes bundling much simpler
// because there's no conditional logic required based on the current platform,
// but it does waste some resources and requires a rebuild if the native target
// was already built.
let mut x86_64_args = other_args.clone();
x86_64_args.push(String::from("--target=x86_64-apple-darwin"));
let mut aarch64_args = other_args.clone();
aarch64_args.push(String::from("--target=aarch64-apple-darwin"));
let x86_output = build_plan(&packages, &x86_64_args)?;
let x86_plan: serde_json::Value =
serde_json::from_str(x86_output.lines().nth(1).unwrap())?;
let x86_num_invocations = x86_plan["invocations"].as_array().unwrap().len();
let aarch64_output = build_plan(&packages, &aarch64_args)?;
let aarch64_plan: serde_json::Value =
serde_json::from_str(aarch64_output.lines().nth(1).unwrap())?;
let aarch64_num_invocations = aarch64_plan["invocations"].as_array().unwrap().len();
Ok(vec![
(Some(String::from("x86")), x86_num_invocations),
(Some(String::from("aarch64")), aarch64_num_invocations),
])
}
_ => anyhow::bail!("Unknown command '{command}'\n\n{usage_string}"),
}
}
fn build_plan(packages: &[String], args: &[String]) -> Result<String> {
let package_args = packages.iter().flat_map(|package| ["-p", package]);
let output = duct::cmd(
"cargo",
["build"]
.into_iter()
.chain(package_args)
.chain(args.iter().map(String::as_str)),
)
.read()?;
Ok(output)
}
/// The main xtask entry point function, but with custom command line arguments. `args` should not /// The main xtask entry point function, but with custom command line arguments. `args` should not
/// contain the command name, so you should always skip at least one argument from /// contain the command name, so you should always skip at least one argument from
/// `std::env::args()` before passing it to this function. /// `std::env::args()` before passing it to this function.