1 // Copyright (C) 2023 The Android Open Source Project
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 use std::{
16 collections::BTreeMap,
17 env,
18 path::Path,
19 process::{Command, Output},
20 str::from_utf8,
21 sync::mpsc::channel,
22 };
23
24 use anyhow::{anyhow, Context, Result};
25 use threadpool::ThreadPool;
26
27 use crate::{Crate, NameAndVersion, NameAndVersionMap, NamedAndVersioned, RepoPath};
28
generate_android_bps<'a, T: Iterator<Item = &'a Crate>>( crates: T, ) -> Result<BTreeMap<NameAndVersion, Output>>29 pub fn generate_android_bps<'a, T: Iterator<Item = &'a Crate>>(
30 crates: T,
31 ) -> Result<BTreeMap<NameAndVersion, Output>> {
32 let pool = ThreadPool::new(std::cmp::max(num_cpus::get(), 32));
33 let (tx, rx) = channel();
34
35 let mut num_crates = 0;
36 for krate in crates {
37 num_crates += 1;
38 let tx = tx.clone();
39 let crate_name = krate.name().to_string();
40 let crate_version = krate.version().clone();
41 let staging_path = krate.staging_path();
42 pool.execute(move || {
43 tx.send((crate_name, crate_version, generate_android_bp(&staging_path)))
44 .expect("Failed to send");
45 });
46 }
47 let mut results = BTreeMap::new();
48 for (crate_name, crate_version, result) in rx.iter().take(num_crates) {
49 results.insert_or_error(NameAndVersion::new(crate_name, crate_version), result?)?;
50 }
51 Ok(results)
52 }
53
generate_android_bp(staging_path: &RepoPath) -> Result<Output>54 pub(crate) fn generate_android_bp(staging_path: &RepoPath) -> Result<Output> {
55 let generate_android_bp_output = run_cargo_embargo(staging_path)?;
56 if !generate_android_bp_output.status.success() {
57 println!(
58 "cargo_embargo failed for {}\nstdout:\n{}\nstderr:\n{}",
59 staging_path,
60 from_utf8(&generate_android_bp_output.stdout)?,
61 from_utf8(&generate_android_bp_output.stderr)?
62 );
63 }
64 Ok(generate_android_bp_output)
65 }
66
run_cargo_embargo(staging_path: &RepoPath) -> Result<Output>67 fn run_cargo_embargo(staging_path: &RepoPath) -> Result<Output> {
68 // Make sure we can find bpfmt.
69 let host_bin = staging_path.with_same_root(&"out/host/linux-x86/bin").abs();
70 let new_path = match env::var_os("PATH") {
71 Some(p) => {
72 let mut paths = vec![host_bin];
73 paths.extend(env::split_paths(&p));
74 env::join_paths(paths)?
75 }
76 None => host_bin.as_os_str().into(),
77 };
78
79 let mut cmd =
80 Command::new(staging_path.with_same_root(&"out/host/linux-x86/bin/cargo_embargo").abs());
81 cmd.args(["generate", "cargo_embargo.json"])
82 .env("PATH", new_path)
83 .env("ANDROID_BUILD_TOP", staging_path.root())
84 .current_dir(staging_path.abs())
85 .output()
86 .context(format!("Failed to execute {:?}", cmd.get_program()))
87 }
88
maybe_build_cargo_embargo(repo_root: &impl AsRef<Path>, force_rebuild: bool) -> Result<()>89 pub fn maybe_build_cargo_embargo(repo_root: &impl AsRef<Path>, force_rebuild: bool) -> Result<()> {
90 if !force_rebuild
91 && repo_root.as_ref().join("out/host/linux-x86/bin/cargo_embargo").exists()
92 && repo_root.as_ref().join("out/host/linux-x86/bin/bpfmt").exists()
93 {
94 Ok(())
95 } else {
96 println!("Rebuilding cargo_embargo");
97 build_cargo_embargo(repo_root)
98 }
99 }
100
build_cargo_embargo(repo_root: &impl AsRef<Path>) -> Result<()>101 pub fn build_cargo_embargo(repo_root: &impl AsRef<Path>) -> Result<()> {
102 let status = Command::new("/usr/bin/bash")
103 .args(["-c", "source build/envsetup.sh && lunch aosp_cf_x86_64_phone-trunk_staging-eng && m cargo_embargo bpfmt"])
104 .current_dir(repo_root).spawn().context("Failed to spawn build of cargo embargo and bpfmt")?.wait().context("Failed to wait on child process building cargo embargo and bpfmt")?;
105 match status.success() {
106 true => Ok(()),
107 false => Err(anyhow!(
108 "Building cargo embargo and bpfmt failed with exit code {}",
109 status.code().map(|code| { format!("{}", code) }).unwrap_or("(unknown)".to_string())
110 )),
111 }
112 }
113