├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── LICENSE ├── README.md └── src ├── lib.rs └── main.rs /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | [[package]] 4 | name = "aho-corasick" 5 | version = "0.7.18" 6 | source = "registry+https://github.com/rust-lang/crates.io-index" 7 | checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f" 8 | dependencies = [ 9 | "memchr", 10 | ] 11 | 12 | [[package]] 13 | name = "ansi_term" 14 | version = "0.11.0" 15 | source = "registry+https://github.com/rust-lang/crates.io-index" 16 | checksum = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" 17 | dependencies = [ 18 | "winapi", 19 | ] 20 | 21 | [[package]] 22 | name = "aosp-missing-blobs" 23 | version = "0.5.0" 24 | dependencies = [ 25 | "clap", 26 | "goblin", 27 | "ignore", 28 | ] 29 | 30 | [[package]] 31 | name = "atty" 32 | version = "0.2.14" 33 | source = "registry+https://github.com/rust-lang/crates.io-index" 34 | checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" 35 | dependencies = [ 36 | "hermit-abi", 37 | "libc", 38 | "winapi", 39 | ] 40 | 41 | [[package]] 42 | name = "bitflags" 43 | version = "1.2.1" 44 | source = "registry+https://github.com/rust-lang/crates.io-index" 45 | checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" 46 | 47 | [[package]] 48 | name = "bstr" 49 | version = "0.2.16" 50 | source = "registry+https://github.com/rust-lang/crates.io-index" 51 | checksum = "90682c8d613ad3373e66de8c6411e0ae2ab2571e879d2efbf73558cc66f21279" 52 | dependencies = [ 53 | "memchr", 54 | ] 55 | 56 | [[package]] 57 | name = "cfg-if" 58 | version = "1.0.0" 59 | source = "registry+https://github.com/rust-lang/crates.io-index" 60 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 61 | 62 | [[package]] 63 | name = "clap" 64 | version = "2.33.3" 65 | source = "registry+https://github.com/rust-lang/crates.io-index" 66 | checksum = "37e58ac78573c40708d45522f0d80fa2f01cc4f9b4e2bf749807255454312002" 67 | dependencies = [ 68 | "ansi_term", 69 | "atty", 70 | "bitflags", 71 | "strsim", 72 | "textwrap", 73 | "unicode-width", 74 | "vec_map", 75 | ] 76 | 77 | [[package]] 78 | name = "crossbeam-utils" 79 | version = "0.8.5" 80 | source = "registry+https://github.com/rust-lang/crates.io-index" 81 | checksum = "d82cfc11ce7f2c3faef78d8a684447b40d503d9681acebed6cb728d45940c4db" 82 | dependencies = [ 83 | "cfg-if", 84 | "lazy_static", 85 | ] 86 | 87 | [[package]] 88 | name = "fnv" 89 | version = "1.0.7" 90 | source = "registry+https://github.com/rust-lang/crates.io-index" 91 | checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" 92 | 93 | [[package]] 94 | name = "globset" 95 | version = "0.4.6" 96 | source = "registry+https://github.com/rust-lang/crates.io-index" 97 | checksum = "c152169ef1e421390738366d2f796655fec62621dabbd0fd476f905934061e4a" 98 | dependencies = [ 99 | "aho-corasick", 100 | "bstr", 101 | "fnv", 102 | "log", 103 | "regex", 104 | ] 105 | 106 | [[package]] 107 | name = "goblin" 108 | version = "0.4.0" 109 | source = "registry+https://github.com/rust-lang/crates.io-index" 110 | checksum = "532a09cd3df2c6bbfc795fb0434bff8f22255d1d07328180e918a2e6ce122d4d" 111 | dependencies = [ 112 | "log", 113 | "plain", 114 | "scroll", 115 | ] 116 | 117 | [[package]] 118 | name = "hermit-abi" 119 | version = "0.1.18" 120 | source = "registry+https://github.com/rust-lang/crates.io-index" 121 | checksum = "322f4de77956e22ed0e5032c359a0f1273f1f7f0d79bfa3b8ffbc730d7fbcc5c" 122 | dependencies = [ 123 | "libc", 124 | ] 125 | 126 | [[package]] 127 | name = "ignore" 128 | version = "0.4.17" 129 | source = "registry+https://github.com/rust-lang/crates.io-index" 130 | checksum = "b287fb45c60bb826a0dc68ff08742b9d88a2fea13d6e0c286b3172065aaf878c" 131 | dependencies = [ 132 | "crossbeam-utils", 133 | "globset", 134 | "lazy_static", 135 | "log", 136 | "memchr", 137 | "regex", 138 | "same-file", 139 | "thread_local", 140 | "walkdir", 141 | "winapi-util", 142 | ] 143 | 144 | [[package]] 145 | name = "lazy_static" 146 | version = "1.4.0" 147 | source = "registry+https://github.com/rust-lang/crates.io-index" 148 | checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" 149 | 150 | [[package]] 151 | name = "libc" 152 | version = "0.2.95" 153 | source = "registry+https://github.com/rust-lang/crates.io-index" 154 | checksum = "789da6d93f1b866ffe175afc5322a4d76c038605a1c3319bb57b06967ca98a36" 155 | 156 | [[package]] 157 | name = "log" 158 | version = "0.4.14" 159 | source = "registry+https://github.com/rust-lang/crates.io-index" 160 | checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710" 161 | dependencies = [ 162 | "cfg-if", 163 | ] 164 | 165 | [[package]] 166 | name = "memchr" 167 | version = "2.4.0" 168 | source = "registry+https://github.com/rust-lang/crates.io-index" 169 | checksum = "b16bd47d9e329435e309c58469fe0791c2d0d1ba96ec0954152a5ae2b04387dc" 170 | 171 | [[package]] 172 | name = "once_cell" 173 | version = "1.7.2" 174 | source = "registry+https://github.com/rust-lang/crates.io-index" 175 | checksum = "af8b08b04175473088b46763e51ee54da5f9a164bc162f615b91bc179dbf15a3" 176 | 177 | [[package]] 178 | name = "plain" 179 | version = "0.2.3" 180 | source = "registry+https://github.com/rust-lang/crates.io-index" 181 | checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6" 182 | 183 | [[package]] 184 | name = "proc-macro2" 185 | version = "1.0.26" 186 | source = "registry+https://github.com/rust-lang/crates.io-index" 187 | checksum = "a152013215dca273577e18d2bf00fa862b89b24169fb78c4c95aeb07992c9cec" 188 | dependencies = [ 189 | "unicode-xid", 190 | ] 191 | 192 | [[package]] 193 | name = "quote" 194 | version = "1.0.9" 195 | source = "registry+https://github.com/rust-lang/crates.io-index" 196 | checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7" 197 | dependencies = [ 198 | "proc-macro2", 199 | ] 200 | 201 | [[package]] 202 | name = "regex" 203 | version = "1.5.4" 204 | source = "registry+https://github.com/rust-lang/crates.io-index" 205 | checksum = "d07a8629359eb56f1e2fb1652bb04212c072a87ba68546a04065d525673ac461" 206 | dependencies = [ 207 | "aho-corasick", 208 | "memchr", 209 | "regex-syntax", 210 | ] 211 | 212 | [[package]] 213 | name = "regex-syntax" 214 | version = "0.6.25" 215 | source = "registry+https://github.com/rust-lang/crates.io-index" 216 | checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b" 217 | 218 | [[package]] 219 | name = "same-file" 220 | version = "1.0.6" 221 | source = "registry+https://github.com/rust-lang/crates.io-index" 222 | checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" 223 | dependencies = [ 224 | "winapi-util", 225 | ] 226 | 227 | [[package]] 228 | name = "scroll" 229 | version = "0.10.2" 230 | source = "registry+https://github.com/rust-lang/crates.io-index" 231 | checksum = "fda28d4b4830b807a8b43f7b0e6b5df875311b3e7621d84577188c175b6ec1ec" 232 | dependencies = [ 233 | "scroll_derive", 234 | ] 235 | 236 | [[package]] 237 | name = "scroll_derive" 238 | version = "0.10.5" 239 | source = "registry+https://github.com/rust-lang/crates.io-index" 240 | checksum = "aaaae8f38bb311444cfb7f1979af0bc9240d95795f75f9ceddf6a59b79ceffa0" 241 | dependencies = [ 242 | "proc-macro2", 243 | "quote", 244 | "syn", 245 | ] 246 | 247 | [[package]] 248 | name = "strsim" 249 | version = "0.8.0" 250 | source = "registry+https://github.com/rust-lang/crates.io-index" 251 | checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" 252 | 253 | [[package]] 254 | name = "syn" 255 | version = "1.0.71" 256 | source = "registry+https://github.com/rust-lang/crates.io-index" 257 | checksum = "ad184cc9470f9117b2ac6817bfe297307418819ba40552f9b3846f05c33d5373" 258 | dependencies = [ 259 | "proc-macro2", 260 | "quote", 261 | "unicode-xid", 262 | ] 263 | 264 | [[package]] 265 | name = "textwrap" 266 | version = "0.11.0" 267 | source = "registry+https://github.com/rust-lang/crates.io-index" 268 | checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" 269 | dependencies = [ 270 | "unicode-width", 271 | ] 272 | 273 | [[package]] 274 | name = "thread_local" 275 | version = "1.1.3" 276 | source = "registry+https://github.com/rust-lang/crates.io-index" 277 | checksum = "8018d24e04c95ac8790716a5987d0fec4f8b27249ffa0f7d33f1369bdfb88cbd" 278 | dependencies = [ 279 | "once_cell", 280 | ] 281 | 282 | [[package]] 283 | name = "unicode-width" 284 | version = "0.1.8" 285 | source = "registry+https://github.com/rust-lang/crates.io-index" 286 | checksum = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3" 287 | 288 | [[package]] 289 | name = "unicode-xid" 290 | version = "0.2.1" 291 | source = "registry+https://github.com/rust-lang/crates.io-index" 292 | checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" 293 | 294 | [[package]] 295 | name = "vec_map" 296 | version = "0.8.2" 297 | source = "registry+https://github.com/rust-lang/crates.io-index" 298 | checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" 299 | 300 | [[package]] 301 | name = "walkdir" 302 | version = "2.3.2" 303 | source = "registry+https://github.com/rust-lang/crates.io-index" 304 | checksum = "808cf2735cd4b6866113f648b791c6adc5714537bc222d9347bb203386ffda56" 305 | dependencies = [ 306 | "same-file", 307 | "winapi", 308 | "winapi-util", 309 | ] 310 | 311 | [[package]] 312 | name = "winapi" 313 | version = "0.3.9" 314 | source = "registry+https://github.com/rust-lang/crates.io-index" 315 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 316 | dependencies = [ 317 | "winapi-i686-pc-windows-gnu", 318 | "winapi-x86_64-pc-windows-gnu", 319 | ] 320 | 321 | [[package]] 322 | name = "winapi-i686-pc-windows-gnu" 323 | version = "0.4.0" 324 | source = "registry+https://github.com/rust-lang/crates.io-index" 325 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 326 | 327 | [[package]] 328 | name = "winapi-util" 329 | version = "0.1.5" 330 | source = "registry+https://github.com/rust-lang/crates.io-index" 331 | checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" 332 | dependencies = [ 333 | "winapi", 334 | ] 335 | 336 | [[package]] 337 | name = "winapi-x86_64-pc-windows-gnu" 338 | version = "0.4.0" 339 | source = "registry+https://github.com/rust-lang/crates.io-index" 340 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 341 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "aosp-missing-blobs" 3 | version = "0.5.0" 4 | authors = ["Josh Choo "] 5 | edition = "2018" 6 | description = """ 7 | A tool to identify required blobs (.so) that are missing from AOSP ROM builds, and to show which existing blobs depend on them. 8 | """ 9 | license = "MIT" 10 | documentation = "https://github.com/joshchoo/aosp-missing-blobs" 11 | homepage = "https://github.com/joshchoo/aosp-missing-blobs" 12 | repository = "https://github.com/joshchoo/aosp-missing-blobs" 13 | 14 | [dependencies] 15 | clap = "2.33" 16 | goblin = "0.4" 17 | ignore = "0.4" 18 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) Josh Choo 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## aosp-missing-blobs 2 | 3 | aosp-missing-blobs is a nifty tool to identify required blobs (.so) that are missing from AOSP ROM builds, 4 | and to show which existing blobs rely on them. This will be particularly useful for ROM developers who 5 | want to ensure that they have not missed out any required proprietary OEM blobs. 6 | 7 | ### Concept 8 | 9 | Blobs (.so) and compiled binaries in Android need other blobs to work. It is possible to identify the 10 | dependencies for each blob by inspecting their ELF information: 11 | 12 | ``` 13 | $ readelf -d | grep "\(NEEDED\)" | sed -r "s/.*\[(.*)\]/\1/" 14 | ``` 15 | 16 | Example (finding the dependencies of `libqti_performance.so`): 17 | 18 | ``` 19 | $ readelf -d libqti_performance.so | grep "\(NEEDED\)" | sed -r "s/.*\[(.*)\]/\1/" 20 | libnativehelper.so 21 | liblog.so 22 | libcutils.so 23 | libutils.so 24 | libbase.so 25 | vendor.qti.hardware.iop@2.0.so 26 | libhidlbase.so 27 | libhidltransport.so 28 | libqti-perfd-client_system.so 29 | libhwui.so 30 | libc++.so 31 | libc.so 32 | libm.so 33 | libdl.so 34 | ``` 35 | 36 | From the example, we can see that `libqti_performance.so` depends on `libqti-perfd-client_system.so` 37 | and `vendor.qti.hardware.iop@2.0.so`. 38 | 39 | aosp-missing-blobs uses this idea to identify all the dependencies of a given list of blobs, and to 40 | check whether any of these dependencies are not present. 41 | 42 | ### Installation 43 | 44 | #### Cargo 45 | 46 | You can install with `cargo` after [setting up Rust](https://www.rust-lang.org/tools/install): 47 | 48 | ``` 49 | cargo install aosp-missing-blobs 50 | ``` 51 | 52 | ### Building 53 | 54 | aosp-missing-blobs is written in Rust, hence you'll need [Rust to be installed](https://www.rust-lang.org) to build the project. 55 | 56 | In the root directory of the repo, execute: 57 | 58 | ``` 59 | $ git clone https://github.com/joshchoo/aosp-missing-blobs 60 | $ cd aosp-missing-blobs 61 | $ cargo build --release 62 | 63 | $ ./target/release/aosp-missing-blobs 64 | ``` 65 | 66 | A runnable `aosp-missing-blobs` binary will be produced. 67 | 68 | ### Usage 69 | 70 | This program takes as arguments a **list of directories** that contain compiled binaries and blobs (.so). 71 | 72 | ``` 73 | $ aosp-missing-blobs 74 | ``` 75 | 76 | Search blob directories recursively: 77 | 78 | ``` 79 | $ aosp-missing-blobs -r 80 | ``` 81 | 82 | #### Example 83 | 84 | Assuming you have extracted the ROM's system.img and vendor.img to `~/system` and `~/vendor`, respectively, run the following: 85 | 86 | ``` 87 | $ aosp-missing-blobs ~/system/lib ~/vendor/lib 88 | ``` 89 | 90 | The program does the following: 91 | 92 | 1. Search `~/system/lib` and `~/vendor/lib` directories for blobs. 93 | 2. Identify dependencies of each blob found. 94 | 3. Checks if required dependencies exist in `~/system/lib` and `~/vendor/lib`. 95 | 4. Output a list of missing dependencies and existing blobs that need them. 96 | 97 | Depending on how wide your search scope is, the program may flag out missing dependencies 'incorrectly'. 98 | Some dependencies may exist in other directories besides what the examples have shown. 99 | Other directories with blobs include the following: 100 | 101 | - /system/bin 102 | - /system/lib[64] 103 | - /system/lib[64]/vndk-28 104 | - /system/lib[64]/vndk-sp-28 105 | - /vendor/bin 106 | - /vendor/lib[64] 107 | - /vendor/lib[64]/vndk 108 | - Etc. 109 | 110 | Take note that the more directories you specify as arguments, the longer the program will run for! 111 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | use goblin::{self, Object}; 2 | use ignore::WalkBuilder; 3 | use std::collections::HashMap; 4 | use std::fs; 5 | use std::path::{Path, PathBuf}; 6 | 7 | /// The main program wrapper. 8 | pub struct MissingBlobs { 9 | recursive: bool, 10 | } 11 | 12 | impl MissingBlobs { 13 | /// Creates a new MissingBlobs builder. 14 | pub fn builder() -> MissingBlobsBuilder { 15 | MissingBlobsBuilder::default() 16 | } 17 | 18 | /// Searches for blobs in the given paths, and displays missing dependencies. 19 | pub fn run(&self, paths: &[&str]) { 20 | let file_paths: Vec = if self.recursive { 21 | find_files_recursively(&paths) 22 | } else { 23 | find_files(&paths) 24 | }; 25 | 26 | let blob_paths: Vec<&PathBuf> = file_paths 27 | .iter() 28 | .filter(|path| match path.extension() { 29 | // Assume that valid blobs have ".so" extension. 30 | Some(ext) => ext == "so", 31 | None => false, 32 | }) 33 | .collect(); 34 | 35 | let blobs_to_dependencies = get_dependencies(&blob_paths); 36 | let missing_blobs = identify_missing(&blobs_to_dependencies); 37 | display_missing_blobs(&missing_blobs); 38 | } 39 | } 40 | 41 | /// The MissingBlobs builder. 42 | pub struct MissingBlobsBuilder { 43 | recursive: bool, 44 | } 45 | 46 | impl Default for MissingBlobsBuilder { 47 | fn default() -> Self { 48 | Self { recursive: false } 49 | } 50 | } 51 | 52 | impl MissingBlobsBuilder { 53 | /// Builds a MissingBlobs. 54 | pub fn build(&self) -> MissingBlobs { 55 | MissingBlobs { 56 | recursive: self.recursive, 57 | } 58 | } 59 | 60 | /// Sets whether to search paths recursively. 61 | pub fn recursive(mut self, enable: bool) -> Self { 62 | self.recursive = enable; 63 | self 64 | } 65 | } 66 | 67 | fn find_files(paths: &[&str]) -> Vec { 68 | let dirs = paths 69 | .iter() 70 | .map(Path::new) 71 | .filter(|path| path.is_dir()) 72 | .collect::>(); 73 | 74 | let file_paths: Vec = dirs 75 | .iter() 76 | .map(|dir| fs::read_dir(dir).expect("Could not read directory.")) 77 | .flat_map(|read_dir| { 78 | read_dir.map(|dir_entry| dir_entry.expect("Could not read directory entry.").path()) 79 | }) 80 | .collect(); 81 | 82 | file_paths 83 | } 84 | 85 | fn find_files_recursively(paths: &[&str]) -> Vec { 86 | let mut walker = WalkBuilder::new(paths[0]); 87 | for path in &paths[1..] { 88 | walker.add(path); 89 | } 90 | 91 | // Don't read from ignore configs 92 | walker 93 | .ignore(false) 94 | .git_ignore(false) 95 | .git_exclude(false) 96 | .git_global(false); 97 | 98 | walker 99 | .build() 100 | .map(|dir_entry| { 101 | dir_entry 102 | .expect("Could not read directory entry.") 103 | .into_path() 104 | }) 105 | .collect() 106 | } 107 | 108 | fn get_dependencies(blob_paths: &[&PathBuf]) -> HashMap> { 109 | let mut dependencies: HashMap> = HashMap::new(); 110 | 111 | blob_paths.iter().for_each(|path| { 112 | let filename = path 113 | .file_name() 114 | .expect("Could not get file name.") 115 | .to_str() 116 | .expect("Could not convert to string.") 117 | .to_owned(); 118 | 119 | let buffer; 120 | match fs::read(&path) { 121 | Ok(b) => buffer = b, 122 | Err(_) => { 123 | eprintln!("Warning: Could not read file: {}", path.display()); 124 | return; 125 | }, 126 | } 127 | 128 | let obj = goblin::Object::parse(&buffer); 129 | 130 | if let Ok(Object::Elf(elf)) = obj { 131 | let deps: Vec = elf.libraries.iter().map(|dep| dep.to_string()).collect(); 132 | dependencies.insert(filename, deps); 133 | } 134 | }); 135 | 136 | dependencies 137 | } 138 | 139 | fn identify_missing( 140 | blobs_to_dependencies: &HashMap>, 141 | ) -> HashMap> { 142 | let mut dependencies_to_blobs: HashMap> = HashMap::new(); 143 | blobs_to_dependencies.iter().for_each(|(blob, deps)| { 144 | deps.iter().for_each( 145 | |dependency| match dependencies_to_blobs.get_mut(dependency) { 146 | Some(dependants) => { 147 | dependants.push(blob.to_owned()); 148 | } 149 | None => { 150 | dependencies_to_blobs.insert(dependency.to_owned(), vec![blob.to_owned()]); 151 | } 152 | }, 153 | ) 154 | }); 155 | 156 | let mut missing_blobs: HashMap> = HashMap::new(); 157 | 158 | for dep in dependencies_to_blobs.keys() { 159 | if !blobs_to_dependencies.contains_key(dep) { 160 | // Dependency is not present. 161 | let missing_dep = dep.to_owned(); 162 | let blobs_requiring_missing_dep = dependencies_to_blobs[dep].to_owned(); 163 | missing_blobs.insert(missing_dep, blobs_requiring_missing_dep); 164 | } 165 | } 166 | 167 | missing_blobs 168 | } 169 | 170 | fn display_missing_blobs(missing_blobs: &HashMap>) { 171 | for blob in missing_blobs.keys() { 172 | println!("{} required by: {}", blob, missing_blobs[blob].join("; ")); 173 | println!(); 174 | } 175 | } 176 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | use aosp_missing_blobs::MissingBlobs; 2 | use clap::{crate_description, crate_name, crate_version, App, Arg}; 3 | 4 | fn main() { 5 | let matches = App::new(crate_name!()) 6 | .version(crate_version!()) 7 | .about(crate_description!()) 8 | .arg( 9 | Arg::with_name("PATHS") 10 | .help("Paths to blobs") 11 | .required(true) 12 | .multiple(true), 13 | ) 14 | .arg( 15 | Arg::with_name("recursive") 16 | .short("r") 17 | .long("recursive") 18 | .help("Read blobs in each path recursively"), 19 | ) 20 | .get_matches(); 21 | 22 | let paths = matches.values_of("PATHS").unwrap().collect::>(); 23 | let recursive = matches.is_present("recursive"); 24 | 25 | MissingBlobs::builder() 26 | .recursive(recursive) 27 | .build() 28 | .run(&paths); 29 | } 30 | --------------------------------------------------------------------------------