xref: /aosp_15_r20/development/tools/external_crates/test_mapping/src/rdeps.rs (revision 90c8c64db3049935a07c6143d7fd006e26f8ecca)
1 // Copyright (C) 2024 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 //! Find reverse dependencies of Rust crates by searching blueprint files
16 //! for rustlibs.
17 
18 use std::{
19     collections::{BTreeSet, HashMap},
20     path::{Path, PathBuf},
21     process::Command,
22     str::from_utf8,
23     sync::{LazyLock, Mutex},
24 };
25 
26 use android_bp::BluePrint;
27 use owning_ref::MutexGuardRef;
28 
29 use crate::{blueprint::RustDeps, TestMappingError};
30 
31 pub(crate) struct ReverseDeps {
32     // Mapping from Rust build rule target name => list of paths that depend on it.
33     rdeps: HashMap<String, BTreeSet<String>>,
34 }
35 
36 impl ReverseDeps {
37     /// Returns a reverse dependency lookup for the Android source repo
38     /// at the specified absolute path. Each lookup is created once
39     /// and cached.
for_repo( repo_root: &Path, ) -> MutexGuardRef<'static, HashMap<PathBuf, ReverseDeps>, ReverseDeps>40     pub fn for_repo(
41         repo_root: &Path,
42     ) -> MutexGuardRef<'static, HashMap<PathBuf, ReverseDeps>, ReverseDeps> {
43         static RDEPS: LazyLock<Mutex<HashMap<PathBuf, ReverseDeps>>> =
44             LazyLock::new(|| Mutex::new(HashMap::new()));
45 
46         RDEPS
47             .lock()
48             .unwrap()
49             .entry(repo_root.to_path_buf())
50             .or_insert_with(|| ReverseDeps::grep_and_parse(repo_root).unwrap());
51         MutexGuardRef::new(RDEPS.lock().unwrap()).map(|rdeps| rdeps.get(repo_root).unwrap())
52     }
53     /// Get the paths that depend on a rust library.
get(&self, name: &str) -> Option<&BTreeSet<String>>54     pub fn get(&self, name: &str) -> Option<&BTreeSet<String>> {
55         self.rdeps.get(name)
56     }
grep_and_parse<P: Into<PathBuf>>(repo_root: P) -> Result<ReverseDeps, TestMappingError>57     fn grep_and_parse<P: Into<PathBuf>>(repo_root: P) -> Result<ReverseDeps, TestMappingError> {
58         let repo_root = repo_root.into();
59         // Empirically, TEST_MAPPING files for 3rd party crates only
60         // have imports from external, packages, system, and tools.
61         let output = Command::new("grep")
62             .args([
63                 "-r",
64                 "-l",
65                 "--include=*.bp",
66                 "rustlibs",
67                 "external",
68                 "packages",
69                 "system",
70                 "tools",
71             ])
72             .current_dir(&repo_root)
73             .output()?;
74         let stdout = from_utf8(&output.stdout)?;
75         let mut rdeps = HashMap::new();
76         for line in stdout.lines() {
77             if EXCLUDED_PATHS.iter().any(|excluded| line.starts_with(excluded)) {
78                 continue;
79             }
80             let (dir, _) =
81                 line.rsplit_once('/').ok_or(TestMappingError::GrepParseError(line.to_string()))?;
82             if let Ok(bp) = BluePrint::from_file(repo_root.join(line)) {
83                 for rustlib in bp.rust_deps() {
84                     rdeps.entry(rustlib).or_insert(BTreeSet::new()).insert(dir.to_string());
85                 }
86             }
87         }
88         Ok(ReverseDeps { rdeps })
89     }
90 }
91 
92 // Originally taken from update_crate_tests.py, but of the values in there, only external/crosvm
93 // seems to exist.
94 static EXCLUDED_PATHS: LazyLock<Vec<&'static str>> =
95     LazyLock::new(|| vec!["external/crosvm/", "development/tools/cargo_embargo/testdata/"]);
96