• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
28 use crate::{blueprint::RustDeps, Error};
29 
30 #[derive(Clone)]
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) -> ReverseDeps40     pub fn for_repo(repo_root: &Path) -> ReverseDeps {
41         static RDEPS: LazyLock<Mutex<HashMap<PathBuf, ReverseDeps>>> =
42             LazyLock::new(|| Mutex::new(HashMap::new()));
43 
44         RDEPS
45             .lock()
46             .unwrap()
47             .entry(repo_root.to_path_buf())
48             .or_insert_with(|| ReverseDeps::grep_and_parse(repo_root).unwrap())
49             .clone()
50     }
51     /// Get the paths that depend on a rust library.
get(&self, name: &str) -> Option<&BTreeSet<String>>52     pub fn get(&self, name: &str) -> Option<&BTreeSet<String>> {
53         self.rdeps.get(name)
54     }
grep_and_parse<P: Into<PathBuf>>(repo_root: P) -> Result<ReverseDeps, Error>55     fn grep_and_parse<P: Into<PathBuf>>(repo_root: P) -> Result<ReverseDeps, Error> {
56         let repo_root = repo_root.into();
57         // Empirically, TEST_MAPPING files for 3rd party crates only
58         // have imports from external, packages, system, and tools.
59         let output = Command::new("grep")
60             .args([
61                 "-r",
62                 "-l",
63                 "--include=*.bp",
64                 "rustlibs",
65                 "external",
66                 "packages",
67                 "system",
68                 "tools",
69             ])
70             .current_dir(&repo_root)
71             .output()?;
72         let stdout = from_utf8(&output.stdout)?;
73         let mut rdeps = HashMap::new();
74         for line in stdout.lines() {
75             if EXCLUDED_PATHS.iter().any(|excluded| line.starts_with(excluded)) {
76                 continue;
77             }
78             let (dir, _) = line.rsplit_once('/').ok_or(Error::GrepParseError(line.to_string()))?;
79             if let Ok(bp) = BluePrint::from_file(repo_root.join(line)) {
80                 for rustlib in bp.rust_deps() {
81                     rdeps.entry(rustlib).or_insert(BTreeSet::new()).insert(dir.to_string());
82                 }
83             }
84         }
85         Ok(ReverseDeps { rdeps })
86     }
87 }
88 
89 // Originally taken from update_crate_tests.py, but of the values in there, only external/crosvm
90 // seems to exist.
91 static EXCLUDED_PATHS: LazyLock<Vec<&'static str>> =
92     LazyLock::new(|| vec!["external/crosvm/", "development/tools/cargo_embargo/testdata/"]);
93