├── .gitignore ├── .github ├── FUNDING.yml └── workflows │ ├── install.yml │ └── ci.yml ├── src ├── cmd.rs ├── error.rs ├── count.rs ├── table.rs ├── opts.rs └── main.rs ├── Cargo.toml ├── LICENSE-MIT ├── README.md ├── LICENSE-APACHE └── Cargo.lock /.gitignore: -------------------------------------------------------------------------------- 1 | /target/ 2 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: dtolnay 2 | -------------------------------------------------------------------------------- /.github/workflows/install.yml: -------------------------------------------------------------------------------- 1 | name: Install 2 | 3 | on: 4 | workflow_dispatch: 5 | schedule: [cron: "40 1 * * *"] 6 | push: {tags: ['*']} 7 | 8 | permissions: {} 9 | 10 | env: 11 | RUSTFLAGS: -Dwarnings 12 | 13 | jobs: 14 | install: 15 | name: Install 16 | uses: dtolnay/.github/.github/workflows/check_install.yml@master 17 | with: 18 | crate: cargo-llvm-lines 19 | -------------------------------------------------------------------------------- /src/cmd.rs: -------------------------------------------------------------------------------- 1 | use std::ffi::OsStr; 2 | use std::process::Command; 3 | 4 | pub trait CommandExt { 5 | fn flag_value(&mut self, k: K, v: V) 6 | where 7 | K: AsRef, 8 | V: AsRef; 9 | } 10 | 11 | impl CommandExt for Command { 12 | fn flag_value(&mut self, k: K, v: V) 13 | where 14 | K: AsRef, 15 | V: AsRef, 16 | { 17 | let k = k.as_ref(); 18 | let v = v.as_ref(); 19 | if let Some(k) = k.to_str() { 20 | if let Some(v) = v.to_str() { 21 | self.arg(format!("{}={}", k, v)); 22 | return; 23 | } 24 | } 25 | self.arg(k); 26 | self.arg(v); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "cargo-llvm-lines" 3 | version = "0.4.45" 4 | authors = ["David Tolnay ", "Nicholas Nethercote "] 5 | categories = ["development-tools::cargo-plugins", "development-tools::debugging"] 6 | description = "Count the number of lines of LLVM IR across all instantiations of a generic function." 7 | documentation = "https://github.com/dtolnay/cargo-llvm-lines" 8 | edition = "2021" 9 | keywords = ["cargo", "subcommand"] 10 | license = "MIT OR Apache-2.0" 11 | repository = "https://github.com/dtolnay/cargo-llvm-lines" 12 | rust-version = "1.86" 13 | 14 | [dependencies] 15 | cargo-subcommand-metadata = "0.1" 16 | clap = { version = "4", features = ["deprecated", "derive", "wrap_help"] } 17 | clap-cargo = "0.18" 18 | regex = { version = "0.1", package = "regex-lite" } 19 | rustc-demangle = "0.1" 20 | shlex = "1.3" 21 | tempfile = "3.4" 22 | termcolor = "1.4" 23 | 24 | [package.metadata.docs.rs] 25 | targets = ["x86_64-unknown-linux-gnu"] 26 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Permission is hereby granted, free of charge, to any 2 | person obtaining a copy of this software and associated 3 | documentation files (the "Software"), to deal in the 4 | Software without restriction, including without 5 | limitation the rights to use, copy, modify, merge, 6 | publish, distribute, sublicense, and/or sell copies of 7 | the Software, and to permit persons to whom the Software 8 | is furnished to do so, subject to the following 9 | conditions: 10 | 11 | The above copyright notice and this permission notice 12 | shall be included in all copies or substantial portions 13 | of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 16 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 17 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 18 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 19 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 20 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 22 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 23 | DEALINGS IN THE SOFTWARE. 24 | -------------------------------------------------------------------------------- /src/error.rs: -------------------------------------------------------------------------------- 1 | use std::fmt::{self, Display}; 2 | use std::io; 3 | use std::path::PathBuf; 4 | 5 | pub type Result = std::result::Result; 6 | 7 | #[derive(Debug)] 8 | pub enum Error { 9 | Msg(&'static str), 10 | Io(io::Error), 11 | PathIo(PathBuf, io::Error), 12 | Quote(shlex::QuoteError), 13 | } 14 | 15 | impl From for Error { 16 | fn from(error: io::Error) -> Self { 17 | Error::Io(error) 18 | } 19 | } 20 | 21 | impl From for Error { 22 | fn from(error: shlex::QuoteError) -> Self { 23 | Error::Quote(error) 24 | } 25 | } 26 | 27 | impl Display for Error { 28 | fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { 29 | match self { 30 | Error::Msg(msg) => formatter.write_str(msg), 31 | Error::Io(e) => Display::fmt(e, formatter), 32 | Error::PathIo(path, e) => write!(formatter, "{}: {}", path.display(), e), 33 | Error::Quote(e) => Display::fmt(e, formatter), 34 | } 35 | } 36 | } 37 | 38 | impl std::error::Error for Error { 39 | fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { 40 | match self { 41 | Error::Msg(_) => None, 42 | Error::Io(e) => e.source(), 43 | Error::PathIo(_path, e) => e.source(), 44 | Error::Quote(e) => e.source(), 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/count.rs: -------------------------------------------------------------------------------- 1 | use rustc_demangle::demangle; 2 | use std::collections::HashMap as Map; 3 | 4 | #[derive(Default)] 5 | pub struct Instantiations { 6 | pub copies: usize, 7 | pub total_lines: usize, 8 | } 9 | 10 | impl Instantiations { 11 | fn record_lines(&mut self, lines: usize) { 12 | self.copies += 1; 13 | self.total_lines += lines; 14 | } 15 | } 16 | 17 | pub fn count_lines(instantiations: &mut Map, ir: &[u8]) { 18 | let mut current_function = None; 19 | let mut count = 0; 20 | 21 | for line in String::from_utf8_lossy(ir).lines() { 22 | if line.starts_with("define ") { 23 | current_function = parse_function_name(line); 24 | } else if line == "}" { 25 | if let Some(name) = current_function.take() { 26 | instantiations 27 | .entry(name) 28 | .or_insert_with(Default::default) 29 | .record_lines(count); 30 | } 31 | count = 0; 32 | } else if line.starts_with(" ") && !line.starts_with(" ") { 33 | count += 1; 34 | } 35 | } 36 | } 37 | 38 | fn parse_function_name(line: &str) -> Option { 39 | let start = line.find('@')? + 1; 40 | let end = line[start..].find('(')?; 41 | let mangled = line[start..start + end].trim_matches('"'); 42 | let mut name = demangle(mangled).to_string(); 43 | if has_hash(&name) { 44 | let len = name.len() - 19; 45 | name.truncate(len); 46 | } 47 | Some(name) 48 | } 49 | 50 | fn has_hash(name: &str) -> bool { 51 | let mut bytes = name.bytes().rev(); 52 | for _ in 0..16 { 53 | if !bytes.next().is_some_and(is_ascii_hexdigit) { 54 | return false; 55 | } 56 | } 57 | bytes.next() == Some(b'h') && bytes.next() == Some(b':') && bytes.next() == Some(b':') 58 | } 59 | 60 | fn is_ascii_hexdigit(byte: u8) -> bool { 61 | byte.is_ascii_digit() || (b'a'..=b'f').contains(&byte) 62 | } 63 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | pull_request: 6 | workflow_dispatch: 7 | schedule: [cron: "40 1 * * *"] 8 | 9 | permissions: 10 | contents: read 11 | 12 | env: 13 | RUSTFLAGS: -Dwarnings 14 | 15 | jobs: 16 | pre_ci: 17 | uses: dtolnay/.github/.github/workflows/pre_ci.yml@master 18 | 19 | test: 20 | name: Rust ${{matrix.rust}} on ${{matrix.target || 'Linux'}} 21 | needs: pre_ci 22 | if: needs.pre_ci.outputs.continue 23 | runs-on: ${{matrix.os}}-latest 24 | strategy: 25 | fail-fast: false 26 | matrix: 27 | rust: [nightly, beta, stable, 1.86.0] 28 | os: [ubuntu] 29 | include: 30 | - rust: nightly 31 | target: Windows 32 | os: windows 33 | timeout-minutes: 45 34 | steps: 35 | - uses: actions/checkout@v6 36 | - uses: dtolnay/rust-toolchain@master 37 | with: 38 | toolchain: ${{matrix.rust}} 39 | - name: Enable type layout randomization 40 | run: echo RUSTFLAGS=${RUSTFLAGS}\ -Zrandomize-layout >> $GITHUB_ENV 41 | if: matrix.rust == 'nightly' 42 | shell: bash 43 | - run: cargo check --locked 44 | - run: cargo update 45 | - run: cargo run -- llvm-lines 46 | - run: cargo test 47 | - uses: actions/upload-artifact@v6 48 | if: matrix.os == 'ubuntu' && matrix.rust == 'nightly' && always() 49 | with: 50 | name: Cargo.lock 51 | path: Cargo.lock 52 | continue-on-error: true 53 | 54 | clippy: 55 | name: Clippy 56 | runs-on: ubuntu-latest 57 | if: github.event_name != 'pull_request' 58 | timeout-minutes: 45 59 | steps: 60 | - uses: actions/checkout@v6 61 | - uses: dtolnay/rust-toolchain@clippy 62 | - run: cargo update 63 | - run: cargo clippy -- -Dclippy::all -Dclippy::pedantic 64 | 65 | outdated: 66 | name: Outdated 67 | runs-on: ubuntu-latest 68 | if: github.event_name != 'pull_request' 69 | timeout-minutes: 45 70 | steps: 71 | - uses: actions/checkout@v6 72 | - uses: dtolnay/rust-toolchain@stable 73 | - uses: dtolnay/install@cargo-outdated 74 | - run: cargo update 75 | - run: cargo outdated --workspace --exit-code 1 76 | -------------------------------------------------------------------------------- /src/table.rs: -------------------------------------------------------------------------------- 1 | use crate::opts::SortOrder; 2 | use crate::Instantiations; 3 | use regex::Regex; 4 | use std::collections::HashMap as Map; 5 | use std::io::{self, Write}; 6 | 7 | pub(crate) fn print( 8 | instantiations: Map, 9 | sort_order: SortOrder, 10 | function_filter: Option<&Regex>, 11 | ) { 12 | let mut data = instantiations.into_iter().collect::>(); 13 | 14 | let mut total = Instantiations { 15 | copies: 0, 16 | total_lines: 0, 17 | }; 18 | for row in &data { 19 | total.copies += row.1.copies; 20 | total.total_lines += row.1.total_lines; 21 | } 22 | 23 | match sort_order { 24 | SortOrder::Lines => { 25 | data.sort_by(|a, b| { 26 | let key_lo = (b.1.total_lines, b.1.copies, &a.0); 27 | let key_hi = (a.1.total_lines, a.1.copies, &b.0); 28 | key_lo.cmp(&key_hi) 29 | }); 30 | } 31 | SortOrder::Copies => { 32 | data.sort_by(|a, b| { 33 | let key_lo = (b.1.copies, b.1.total_lines, &a.0); 34 | let key_hi = (a.1.copies, a.1.total_lines, &b.0); 35 | key_lo.cmp(&key_hi) 36 | }); 37 | } 38 | SortOrder::Name => data.sort_by(|a, b| { 39 | let key_lo = (&a.0, b.1.copies, b.1.total_lines); 40 | let key_hi = (&b.0, a.1.copies, b.1.total_lines); 41 | key_lo.cmp(&key_hi) 42 | }), 43 | } 44 | 45 | let lines_width = total.total_lines.to_string().len(); 46 | let copies_width = total.copies.to_string().len(); 47 | 48 | let stdout = io::stdout(); 49 | let mut handle = stdout.lock(); 50 | let _ = writeln!( 51 | handle, 52 | " Lines{0:1$} Copies{0:2$} Function name", 53 | "", lines_width, copies_width, 54 | ); 55 | let _ = writeln!( 56 | handle, 57 | " -----{0:1$} ------{0:2$} -------------", 58 | "", lines_width, copies_width, 59 | ); 60 | let _ = writeln!( 61 | handle, 62 | " {0:1$} {2:3$} (TOTAL)", 63 | total.total_lines, lines_width, total.copies, copies_width, 64 | ); 65 | let mut cumul_lines = 0; 66 | let mut cumul_copies = 0; 67 | let perc = |m, cumul_m: &mut _, n| { 68 | *cumul_m += m; 69 | format!( 70 | "({:3.1}%,{:5.1}%)", 71 | m as f64 / n as f64 * 100f64, 72 | *cumul_m as f64 / n as f64 * 100f64, 73 | ) 74 | }; 75 | for row in data { 76 | if function_filter.map_or(true, |ff| ff.is_match(&row.0)) { 77 | let _ = writeln!( 78 | handle, 79 | " {0:1$} {2:<14} {3:4$} {5:<14} {6}", 80 | row.1.total_lines, 81 | lines_width, 82 | perc(row.1.total_lines, &mut cumul_lines, total.total_lines), 83 | row.1.copies, 84 | copies_width, 85 | perc(row.1.copies, &mut cumul_copies, total.copies), 86 | row.0, 87 | ); 88 | } 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /src/opts.rs: -------------------------------------------------------------------------------- 1 | use clap::{Parser, ValueEnum}; 2 | use regex::Regex; 3 | use std::ffi::OsString; 4 | use std::path::PathBuf; 5 | 6 | const ABOUT: &str = " 7 | Print the number of lines of LLVM IR that is generated for the current project. 8 | 9 | Options shown below without an explanation mean the same thing as the 10 | corresponding option of `cargo build`."; 11 | 12 | const TEMPLATE: &str = "\ 13 | cargo-llvm-lines {version} 14 | {author} 15 | {about} 16 | 17 | {usage-heading} 18 | {usage} 19 | 20 | {all-args}"; 21 | 22 | // Help headings 23 | const PACKAGE_SELECTION: &str = "Package Selection"; 24 | const TARGET_SELECTION: &str = "Target Selection"; 25 | const FEATURE_SELECTION: &str = "Feature Selection"; 26 | const COMPILATION_OPTIONS: &str = "Compilation Options"; 27 | const MANIFEST_OPTIONS: &str = "Manifest Options"; 28 | 29 | #[derive(Parser, Debug)] 30 | #[command( 31 | name = "cargo-llvm-lines", 32 | bin_name = "cargo", 33 | author, 34 | version, 35 | disable_help_subcommand = true, 36 | styles = clap_cargo::style::CLAP_STYLING, 37 | )] 38 | #[allow(dead_code)] 39 | pub enum Subcommand { 40 | #[command( 41 | name = "llvm-lines", 42 | author, 43 | version, 44 | about = ABOUT, 45 | help_template = TEMPLATE, 46 | override_usage = "cargo llvm-lines [OPTIONS] -- [RUSTC OPTIONS]", 47 | disable_help_flag = true, 48 | disable_version_flag = true, 49 | )] 50 | LlvmLines(LlvmLines), 51 | } 52 | 53 | #[derive(Parser, Debug)] 54 | #[command(styles = clap_cargo::style::CLAP_STYLING)] 55 | pub struct LlvmLines { 56 | /// Set column by which to sort output table. 57 | #[arg( 58 | short, 59 | long, 60 | value_enum, 61 | value_name = "ORDER", 62 | default_value_t = SortOrder::Lines, 63 | )] 64 | pub sort: SortOrder, 65 | 66 | /// Display only functions matching the given regex. 67 | #[arg(long, value_name = "REGEX")] 68 | pub filter: Option, 69 | 70 | /// Analyze existing .ll files that were produced by e.g. 71 | /// `RUSTFLAGS="--emit=llvm-ir" ./x.py build --stage 0 compiler/rustc`. 72 | #[arg(short, long, value_name = "FILES")] 73 | pub files: Vec, 74 | 75 | // The following options are passed through to the cargo rustc invocation. 76 | #[arg(long)] 77 | pub verbose: bool, 78 | #[arg(short, long)] 79 | pub quiet: bool, 80 | #[arg(long, value_name = "WHEN", hide_possible_values = true)] 81 | pub color: Option, 82 | #[arg(long, value_name = "KEY=VALUE")] 83 | pub config: Vec, 84 | #[arg(short = 'Z', value_name = "FLAG")] 85 | pub nightly_only_flags: Vec, 86 | #[arg(short, long)] 87 | pub help: bool, 88 | #[arg(short = 'V', long)] 89 | pub version: bool, 90 | 91 | // Package selection 92 | #[arg(short, long, value_name = "SPEC", help_heading = PACKAGE_SELECTION)] 93 | pub package: Option, 94 | 95 | // Target selection 96 | #[arg(long, help_heading = TARGET_SELECTION)] 97 | pub lib: bool, 98 | #[arg(long, value_name = "NAME", help_heading = TARGET_SELECTION)] 99 | pub bin: Option, 100 | #[arg(long, value_name = "NAME", help_heading = TARGET_SELECTION)] 101 | pub example: Option, 102 | #[arg(long, value_name = "NAME", help_heading = TARGET_SELECTION)] 103 | pub test: Option, 104 | #[arg(long, value_name = "NAME", help_heading = TARGET_SELECTION)] 105 | pub bench: Option, 106 | 107 | // Feature selection 108 | #[arg(short = 'F', long, value_name = "FEATURES", help_heading = FEATURE_SELECTION)] 109 | pub features: Option, 110 | #[arg(long, help_heading = FEATURE_SELECTION)] 111 | pub all_features: bool, 112 | #[arg(long, help_heading = FEATURE_SELECTION)] 113 | pub no_default_features: bool, 114 | 115 | // Compilation options 116 | #[arg(short, long, value_name = "N", help_heading = COMPILATION_OPTIONS)] 117 | pub jobs: Option, 118 | #[arg(long, help_heading = COMPILATION_OPTIONS)] 119 | pub release: bool, 120 | #[arg(long, value_name = "PROFILE-NAME", help_heading = COMPILATION_OPTIONS)] 121 | pub profile: Option, 122 | #[arg(long, value_name = "TRIPLE", help_heading = COMPILATION_OPTIONS)] 123 | pub target: Option, 124 | #[arg(long, value_name = "DIRECTORY", help_heading = COMPILATION_OPTIONS)] 125 | pub target_dir: Option, 126 | 127 | // Manifest options 128 | #[arg(long, value_name = "PATH", help_heading = MANIFEST_OPTIONS)] 129 | pub manifest_path: Option, 130 | #[arg(long, help_heading = MANIFEST_OPTIONS)] 131 | pub frozen: bool, 132 | #[arg(long, help_heading = MANIFEST_OPTIONS)] 133 | pub locked: bool, 134 | #[arg(long, help_heading = MANIFEST_OPTIONS)] 135 | pub offline: bool, 136 | 137 | // Any additional flags for rustc taken after `--`. 138 | #[arg(last = true, hide = true)] 139 | pub rest: Vec, 140 | } 141 | 142 | #[derive(ValueEnum, Copy, Clone, Debug)] 143 | pub enum SortOrder { 144 | Lines, 145 | Copies, 146 | Name, 147 | } 148 | 149 | #[derive(ValueEnum, Debug, Clone, Copy)] 150 | pub enum Coloring { 151 | Auto, 152 | Always, 153 | Never, 154 | } 155 | 156 | #[test] 157 | fn test_cli() { 158 | ::command().debug_assert(); 159 | } 160 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # cargo-llvm-lines 2 | 3 | [github](https://github.com/dtolnay/cargo-llvm-lines) 4 | [crates.io](https://crates.io/crates/cargo-llvm-lines) 5 | [build status](https://github.com/dtolnay/cargo-llvm-lines/actions?query=branch%3Amaster) 6 | 7 | This tool measures the number and size of instantiations of each generic 8 | function in a program, indicating which parts of your code offer the highest 9 | leverage in improving compilation metrics. 10 | 11 | Generic functions in Rust can be instantiated multiple times, so they can 12 | disproportionately affect compile time, compiler memory usage, and the size of 13 | compiled executables. 14 | 15 | ## Installation 16 | 17 | Install with `cargo install cargo-llvm-lines`. 18 | 19 | ## Output 20 | 21 | Example output from running `cargo llvm-lines` on its own codebase: 22 | 23 | ```console 24 | $ cargo llvm-lines | head -20 25 | 26 | Lines Copies Function name 27 | ----- ------ ------------- 28 | 51637 1222 (TOTAL) 29 | 2240 (4.3%, 4.3%) 1 (0.1%, 0.1%) ::augment_args 30 | 1190 (2.3%, 6.6%) 1 (0.1%, 0.2%) ::from_arg_matches_mut 31 | 1005 (1.9%, 8.6%) 3 (0.2%, 0.4%) alloc::raw_vec::RawVec::grow_amortized 32 | 973 (1.9%, 10.5%) 7 (0.6%, 1.0%) clap_builder::parser::matches::arg_matches::ArgMatches::try_remove_arg_t 33 | 939 (1.8%, 12.3%) 7 (0.6%, 1.6%) alloc::sync::Arc::try_unwrap 34 | 935 (1.8%, 14.1%) 6 (0.5%, 2.0%) as alloc::vec::spec_from_iter_nested::SpecFromIterNested>::from_iter 35 | 861 (1.7%, 15.8%) 7 (0.6%, 2.6%) alloc::sync::Arc::downcast 36 | 761 (1.5%, 17.2%) 5 (0.4%, 3.0%) alloc::vec::Vec::extend_desugared 37 | 638 (1.2%, 18.5%) 1 (0.1%, 3.1%) cargo_llvm_lines::table::print 38 | 599 (1.2%, 19.6%) 16 (1.3%, 4.4%) core::option::Option::ok_or_else 39 | 592 (1.1%, 20.8%) 2 (0.2%, 4.6%) core::slice::sort::merge 40 | 574 (1.1%, 21.9%) 2 (0.2%, 4.7%) core::slice::sort::merge_sort 41 | 561 (1.1%, 23.0%) 7 (0.6%, 5.3%) clap_builder::parser::matches::any_value::AnyValue::downcast_into 42 | 556 (1.1%, 24.1%) 4 (0.3%, 5.6%) as core::iter::traits::iterator::Iterator>::next 43 | 541 (1.0%, 25.1%) 16 (1.3%, 7.0%) core::option::Option::map 44 | 536 (1.0%, 26.1%) 8 (0.7%, 7.6%) as core::ops::drop::Drop>::drop 45 | 533 (1.0%, 27.2%) 1 (0.1%, 7.7%) core::str::pattern::simd_contains 46 | ``` 47 | 48 | There is one line per function with three columns of output: 49 | 50 | 1. Total number of lines of LLVM IR generated across all instantiations of the 51 | function (plus the percentage of the total and the cumulative percentage 52 | of all functions so far). 53 | 2. Number of instantiations of the function (plus the percentage of the total 54 | and the cumulative percentage of all functions so far). For a generic 55 | function, the number of instantiations is roughly the number of distinct 56 | combinations of generic type parameters it is called with. 57 | 3. Name of the function. 58 | 59 | ## Multicrate Projects 60 | 61 | Interpreting the output in the presence of multiple crates and generics can be 62 | tricky. `cargo llvm-lines` only shows the contribution of the root crate; 63 | dependencies are not included. To assess the contribution of an intermediate 64 | crate, use the `-p` flag: 65 | 66 | ```console 67 | $ cargo llvm-lines -p some-dependency 68 | ``` 69 | 70 | Note however, that Rust generics are monomorphised — a generic function 71 | will be accounted for in the crates that use it, rather than in the defining 72 | crate. 73 | 74 | There is a trick to get a holistic view: enabling link time optimization causes 75 | all code generation to happen in the root crate. So you can use the following 76 | invocation to get a full picture: 77 | 78 | ```console 79 | $ CARGO_PROFILE_RELEASE_LTO=fat cargo llvm-lines --release 80 | ``` 81 | 82 | ## Acknowledgements 83 | 84 | Based on a suggestion from **@eddyb** on how to count monomorphized functions 85 | in order to debug compiler memory usage, executable size and compile time. 86 | 87 | > **\** unoptimized LLVM IR
88 | > **\** first used grep '^define' to get only the lines defining function bodies
89 | > **\** then regex replace in my editor to remove everything before @ and everything after (
90 | > **\** then sort | uniq -c
91 | 92 |
93 | 94 | #### License 95 | 96 | 97 | Licensed under either of Apache License, Version 98 | 2.0 or MIT license at your option. 99 | 100 | 101 |
102 | 103 | 104 | Unless you explicitly state otherwise, any contribution intentionally submitted 105 | for inclusion in this crate by you, as defined in the Apache-2.0 license, shall 106 | be dual licensed as above, without any additional terms or conditions. 107 | 108 | -------------------------------------------------------------------------------- /LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | //! [![github]](https://github.com/dtolnay/cargo-llvm-lines) [![crates-io]](https://crates.io/crates/cargo-llvm-lines) [![docs-rs]](https://docs.rs/cargo-llvm-lines) 2 | //! 3 | //! [github]: https://img.shields.io/badge/github-8da0cb?style=for-the-badge&labelColor=555555&logo=github 4 | //! [crates-io]: https://img.shields.io/badge/crates.io-fc8d62?style=for-the-badge&labelColor=555555&logo=rust 5 | //! [docs-rs]: https://img.shields.io/badge/docs.rs-66c2a5?style=for-the-badge&labelColor=555555&logo=docs.rs 6 | 7 | #![allow( 8 | clippy::cast_precision_loss, 9 | clippy::let_underscore_untyped, 10 | clippy::module_name_repetitions, 11 | clippy::struct_excessive_bools, 12 | clippy::too_many_lines, 13 | clippy::uninlined_format_args, 14 | clippy::unnecessary_map_or, 15 | clippy::unseparated_literal_suffix, 16 | clippy::unwrap_or_default 17 | )] 18 | 19 | mod cmd; 20 | mod count; 21 | mod error; 22 | mod opts; 23 | mod table; 24 | 25 | use crate::cmd::CommandExt as _; 26 | use crate::count::{count_lines, Instantiations}; 27 | use crate::error::{Error, Result}; 28 | use crate::opts::{Coloring, LlvmLines, SortOrder, Subcommand}; 29 | use clap::{CommandFactory, Parser}; 30 | use regex::Regex; 31 | use std::collections::HashMap as Map; 32 | use std::env; 33 | use std::ffi::OsString; 34 | use std::fs; 35 | use std::io::{self, BufRead, IsTerminal, Write}; 36 | use std::path::{Path, PathBuf}; 37 | use std::process::{self, Command, Stdio}; 38 | use tempfile::TempDir; 39 | use termcolor::{Color::Green, ColorChoice, ColorSpec, StandardStream, WriteColor as _}; 40 | 41 | cargo_subcommand_metadata::description!( 42 | "Count the number of lines of LLVM IR across all instantiations of a generic function" 43 | ); 44 | 45 | fn main() { 46 | let Subcommand::LlvmLines(opts) = Subcommand::parse(); 47 | 48 | if opts.help { 49 | let _ = Subcommand::command() 50 | .get_subcommands_mut() 51 | .next() 52 | .unwrap() 53 | .print_help(); 54 | return; 55 | } 56 | 57 | if opts.version { 58 | let mut stdout = io::stdout(); 59 | let _ = stdout.write_all(Subcommand::command().render_version().as_bytes()); 60 | return; 61 | } 62 | 63 | let result = if opts.files.is_empty() { 64 | cargo_llvm_lines(&opts) 65 | } else { 66 | read_llvm_ir_from_paths(&opts.files, opts.sort, opts.filter.as_ref()) 67 | }; 68 | 69 | process::exit(match result { 70 | Ok(code) => code, 71 | Err(err) => { 72 | let _ = writeln!(io::stderr(), "{}", err); 73 | 1 74 | } 75 | }); 76 | } 77 | 78 | fn cargo_llvm_lines(opts: &LlvmLines) -> Result { 79 | let outdir = tempfile::Builder::new() 80 | .prefix("cargo-llvm-lines") 81 | .tempdir() 82 | .expect("failed to create tmp file"); 83 | let outfile = outdir.path().join("crate"); 84 | 85 | // If cargo-llvm-lines was invoked from cargo, use the cargo that invoked it. 86 | let cargo = env::var_os("CARGO").unwrap_or_else(|| OsString::from("cargo")); 87 | let mut cmd = Command::new(cargo); 88 | propagate_opts(&mut cmd, opts, &outfile); 89 | cmd.env("CARGO_INCREMENTAL", ""); 90 | cmd.stdout(Stdio::inherit()); 91 | 92 | if opts.verbose { 93 | let color = opts.color.unwrap_or(Coloring::Auto); 94 | print_command(&cmd, color)?; 95 | } 96 | 97 | let exit = filter_err(&mut cmd)?; 98 | if exit != 0 { 99 | return Ok(exit); 100 | } 101 | 102 | let ir = read_llvm_ir_from_dir(&outdir)?; 103 | let mut instantiations = Map::::new(); 104 | count_lines(&mut instantiations, &ir); 105 | table::print(instantiations, opts.sort, opts.filter.as_ref()); 106 | 107 | Ok(0) 108 | } 109 | 110 | fn read_llvm_ir_from_dir(outdir: &TempDir) -> Result> { 111 | for file in fs::read_dir(outdir)? { 112 | let path = file?.path(); 113 | if let Some(ext) = path.extension() { 114 | if ext == "ll" { 115 | let content = fs::read(path)?; 116 | return Ok(content); 117 | } 118 | } 119 | } 120 | 121 | Err(Error::Msg("Ran --emit=llvm-ir but did not find output IR")) 122 | } 123 | 124 | fn read_llvm_ir_from_paths( 125 | paths: &[PathBuf], 126 | sort_order: SortOrder, 127 | function_filter: Option<&Regex>, 128 | ) -> Result { 129 | let mut instantiations = Map::::new(); 130 | 131 | for path in paths { 132 | match fs::read(path) { 133 | Ok(ir) => count_lines(&mut instantiations, &ir), 134 | Err(err) => return Err(Error::PathIo(path.clone(), err)), 135 | } 136 | } 137 | 138 | table::print(instantiations, sort_order, function_filter); 139 | Ok(0) 140 | } 141 | 142 | fn propagate_opts(cmd: &mut Command, opts: &LlvmLines, outfile: &Path) { 143 | let LlvmLines { 144 | // Strip out options that are for cargo-llvm-lines itself. 145 | sort: _, 146 | filter: _, 147 | files: _, 148 | help: _, 149 | version: _, 150 | 151 | // Options to pass through to the cargo rustc invocation. 152 | verbose, 153 | quiet, 154 | color, 155 | ref config, 156 | ref nightly_only_flags, 157 | ref package, 158 | lib, 159 | ref bin, 160 | ref example, 161 | ref test, 162 | ref bench, 163 | ref features, 164 | all_features, 165 | no_default_features, 166 | jobs, 167 | release, 168 | ref profile, 169 | ref target, 170 | ref target_dir, 171 | ref manifest_path, 172 | frozen, 173 | locked, 174 | offline, 175 | ref rest, 176 | } = *opts; 177 | 178 | cmd.arg("rustc"); 179 | 180 | if verbose { 181 | cmd.arg("--verbose"); 182 | } 183 | 184 | if quiet { 185 | cmd.arg("--quiet"); 186 | } 187 | 188 | match color { 189 | Some(Coloring::Always) => cmd.flag_value("--color", "always"), 190 | Some(Coloring::Never) => cmd.flag_value("--color", "never"), 191 | None | Some(Coloring::Auto) => { 192 | if env::var_os("NO_COLOR").is_none() && io::stderr().is_terminal() { 193 | cmd.flag_value("--color", "always"); 194 | } else { 195 | cmd.flag_value("--color", "never"); 196 | } 197 | } 198 | } 199 | 200 | for kv in config { 201 | cmd.flag_value("--config", kv); 202 | } 203 | 204 | for flag in nightly_only_flags { 205 | cmd.arg(format!("-Z{}", flag)); 206 | } 207 | 208 | if let Some(package) = package { 209 | cmd.flag_value("--package", package); 210 | } 211 | 212 | if lib { 213 | cmd.arg("--lib"); 214 | } 215 | 216 | if let Some(bin) = bin { 217 | cmd.flag_value("--bin", bin); 218 | } 219 | 220 | if let Some(example) = example { 221 | cmd.flag_value("--example", example); 222 | } 223 | 224 | if let Some(test) = test { 225 | cmd.flag_value("--test", test); 226 | } 227 | 228 | if let Some(bench) = bench { 229 | cmd.flag_value("--bench", bench); 230 | } 231 | 232 | if let Some(features) = features { 233 | cmd.flag_value("--features", features); 234 | } 235 | 236 | if all_features { 237 | cmd.arg("--all-features"); 238 | } 239 | 240 | if no_default_features { 241 | cmd.arg("--no-default-features"); 242 | } 243 | 244 | if let Some(jobs) = jobs { 245 | cmd.flag_value("--jobs", jobs.to_string()); 246 | } 247 | 248 | if release { 249 | cmd.arg("--release"); 250 | } 251 | 252 | if let Some(profile) = profile { 253 | cmd.flag_value("--profile", profile); 254 | } 255 | 256 | if let Some(target) = target { 257 | cmd.flag_value("--target", target); 258 | } 259 | 260 | if let Some(target_dir) = target_dir { 261 | cmd.flag_value("--target-dir", target_dir); 262 | } 263 | 264 | if let Some(manifest_path) = manifest_path { 265 | cmd.flag_value("--manifest-path", manifest_path); 266 | } 267 | 268 | if frozen { 269 | cmd.arg("--frozen"); 270 | } 271 | 272 | if locked { 273 | cmd.arg("--locked"); 274 | } 275 | 276 | if offline { 277 | cmd.arg("--offline"); 278 | } 279 | 280 | // The `-Cno-prepopulate-passes` means we skip LLVM optimizations, which is 281 | // good because (a) we count the LLVM IR lines are sent to LLVM, not how 282 | // many there are after optimizations run, and (b) it's faster. 283 | // 284 | // The `-Cpasses=name-anon-globals` follows on: it's required to avoid the 285 | // following error on some programs: "The current compilation is going to 286 | // use thin LTO buffers without running LLVM's NameAnonGlobals pass. This 287 | // will likely cause errors in LLVM. Consider adding -C 288 | // passes=name-anon-globals to the compiler command line." 289 | cmd.arg("--"); 290 | cmd.flag_value("--emit", "llvm-ir"); 291 | cmd.arg("-Cno-prepopulate-passes"); 292 | cmd.arg("-Cpasses=name-anon-globals"); 293 | cmd.arg("-o"); 294 | cmd.arg(outfile); 295 | cmd.args(rest); 296 | } 297 | 298 | fn filter_err(cmd: &mut Command) -> Result { 299 | let mut child = cmd.stderr(Stdio::piped()).spawn()?; 300 | let mut stderr = io::BufReader::new(child.stderr.take().unwrap()); 301 | let mut line = String::new(); 302 | while let Ok(n) = stderr.read_line(&mut line) { 303 | if n == 0 { 304 | break; 305 | } 306 | if !ignore_cargo_err(&line) { 307 | let _ = write!(io::stderr(), "{}", line); 308 | } 309 | line.clear(); 310 | } 311 | let code = child.wait()?.code().unwrap_or(1); 312 | Ok(code) 313 | } 314 | 315 | fn ignore_cargo_err(line: &str) -> bool { 316 | if line.trim().is_empty() { 317 | return true; 318 | } 319 | 320 | let discarded_lines = [ 321 | "warnings emitted", 322 | "ignoring specified output filename because multiple outputs were \ 323 | requested", 324 | "ignoring specified output filename for 'link' output because multiple \ 325 | outputs were requested", 326 | "ignoring --out-dir flag due to -o flag", 327 | "due to multiple output types requested, the explicitly specified \ 328 | output file name will be adapted for each output type", 329 | "ignoring -C extra-filename flag due to -o flag", 330 | ]; 331 | for s in &discarded_lines { 332 | if line.contains(s) { 333 | return true; 334 | } 335 | } 336 | 337 | // warning: `cratename` (lib) generated 2 warnings 338 | if let Some(i) = line.find(") generated ") { 339 | let rest = &line[i + ") generated ".len()..]; 340 | let n = rest.bytes().take_while(u8::is_ascii_digit).count(); 341 | if n > 0 && rest[n..].starts_with(" warning") { 342 | return true; 343 | } 344 | } 345 | 346 | false 347 | } 348 | 349 | fn print_command(cmd: &Command, color: Coloring) -> Result<()> { 350 | let mut shell_words = String::new(); 351 | let quoter = shlex::Quoter::new().allow_nul(true); 352 | for arg in cmd.get_args() { 353 | let arg_lossy = arg.to_string_lossy(); 354 | shell_words.push(' '); 355 | match arg_lossy.split_once('=') { 356 | Some((flag, value)) if flag.starts_with('-') && flag == quoter.quote(flag)? => { 357 | shell_words.push_str(flag); 358 | shell_words.push('='); 359 | if !value.is_empty() { 360 | shell_words.push_str("er.quote(value)?); 361 | } 362 | } 363 | _ => shell_words.push_str("er.quote(&arg_lossy)?), 364 | } 365 | } 366 | 367 | let color_choice = match color { 368 | Coloring::Auto => ColorChoice::Auto, 369 | Coloring::Always => ColorChoice::Always, 370 | Coloring::Never => ColorChoice::Never, 371 | }; 372 | 373 | let mut stream = StandardStream::stderr(color_choice); 374 | let _ = stream.set_color(ColorSpec::new().set_bold(true).set_fg(Some(Green))); 375 | let _ = write!(stream, "{:>12}", "Running"); 376 | let _ = stream.reset(); 377 | let _ = writeln!(stream, " `cargo{}`", shell_words); 378 | Ok(()) 379 | } 380 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 4 4 | 5 | [[package]] 6 | name = "anstream" 7 | version = "0.6.20" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "3ae563653d1938f79b1ab1b5e668c87c76a9930414574a6583a7b7e11a8e6192" 10 | dependencies = [ 11 | "anstyle", 12 | "anstyle-parse", 13 | "anstyle-query", 14 | "anstyle-wincon", 15 | "colorchoice", 16 | "is_terminal_polyfill", 17 | "utf8parse", 18 | ] 19 | 20 | [[package]] 21 | name = "anstyle" 22 | version = "1.0.13" 23 | source = "registry+https://github.com/rust-lang/crates.io-index" 24 | checksum = "5192cca8006f1fd4f7237516f40fa183bb07f8fbdfedaa0036de5ea9b0b45e78" 25 | 26 | [[package]] 27 | name = "anstyle-parse" 28 | version = "0.2.7" 29 | source = "registry+https://github.com/rust-lang/crates.io-index" 30 | checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2" 31 | dependencies = [ 32 | "utf8parse", 33 | ] 34 | 35 | [[package]] 36 | name = "anstyle-query" 37 | version = "1.1.4" 38 | source = "registry+https://github.com/rust-lang/crates.io-index" 39 | checksum = "9e231f6134f61b71076a3eab506c379d4f36122f2af15a9ff04415ea4c3339e2" 40 | dependencies = [ 41 | "windows-sys 0.60.2", 42 | ] 43 | 44 | [[package]] 45 | name = "anstyle-wincon" 46 | version = "3.0.10" 47 | source = "registry+https://github.com/rust-lang/crates.io-index" 48 | checksum = "3e0633414522a32ffaac8ac6cc8f748e090c5717661fddeea04219e2344f5f2a" 49 | dependencies = [ 50 | "anstyle", 51 | "once_cell_polyfill", 52 | "windows-sys 0.60.2", 53 | ] 54 | 55 | [[package]] 56 | name = "bitflags" 57 | version = "2.9.4" 58 | source = "registry+https://github.com/rust-lang/crates.io-index" 59 | checksum = "2261d10cca569e4643e526d8dc2e62e433cc8aba21ab764233731f8d369bf394" 60 | 61 | [[package]] 62 | name = "cargo-llvm-lines" 63 | version = "0.4.45" 64 | dependencies = [ 65 | "cargo-subcommand-metadata", 66 | "clap", 67 | "clap-cargo", 68 | "regex-lite", 69 | "rustc-demangle", 70 | "shlex", 71 | "tempfile", 72 | "termcolor", 73 | ] 74 | 75 | [[package]] 76 | name = "cargo-subcommand-metadata" 77 | version = "0.1.0" 78 | source = "registry+https://github.com/rust-lang/crates.io-index" 79 | checksum = "a33d3b80a8db16c4ad7676653766a8e59b5f95443c8823cb7cff587b90cb91ba" 80 | 81 | [[package]] 82 | name = "cfg-if" 83 | version = "1.0.3" 84 | source = "registry+https://github.com/rust-lang/crates.io-index" 85 | checksum = "2fd1289c04a9ea8cb22300a459a72a385d7c73d3259e2ed7dcb2af674838cfa9" 86 | 87 | [[package]] 88 | name = "clap" 89 | version = "4.5.48" 90 | source = "registry+https://github.com/rust-lang/crates.io-index" 91 | checksum = "e2134bb3ea021b78629caa971416385309e0131b351b25e01dc16fb54e1b5fae" 92 | dependencies = [ 93 | "clap_builder", 94 | "clap_derive", 95 | ] 96 | 97 | [[package]] 98 | name = "clap-cargo" 99 | version = "0.18.0" 100 | source = "registry+https://github.com/rust-lang/crates.io-index" 101 | checksum = "85f1710adac227b75c1b1867b06b7a8f3e6b1b719d24605f7a0659fd65700e65" 102 | dependencies = [ 103 | "anstyle", 104 | "clap", 105 | ] 106 | 107 | [[package]] 108 | name = "clap_builder" 109 | version = "4.5.48" 110 | source = "registry+https://github.com/rust-lang/crates.io-index" 111 | checksum = "c2ba64afa3c0a6df7fa517765e31314e983f51dda798ffba27b988194fb65dc9" 112 | dependencies = [ 113 | "anstream", 114 | "anstyle", 115 | "clap_lex", 116 | "strsim", 117 | "terminal_size", 118 | ] 119 | 120 | [[package]] 121 | name = "clap_derive" 122 | version = "4.5.47" 123 | source = "registry+https://github.com/rust-lang/crates.io-index" 124 | checksum = "bbfd7eae0b0f1a6e63d4b13c9c478de77c2eb546fba158ad50b4203dc24b9f9c" 125 | dependencies = [ 126 | "heck", 127 | "proc-macro2", 128 | "quote", 129 | "syn", 130 | ] 131 | 132 | [[package]] 133 | name = "clap_lex" 134 | version = "0.7.5" 135 | source = "registry+https://github.com/rust-lang/crates.io-index" 136 | checksum = "b94f61472cee1439c0b966b47e3aca9ae07e45d070759512cd390ea2bebc6675" 137 | 138 | [[package]] 139 | name = "colorchoice" 140 | version = "1.0.4" 141 | source = "registry+https://github.com/rust-lang/crates.io-index" 142 | checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" 143 | 144 | [[package]] 145 | name = "errno" 146 | version = "0.3.14" 147 | source = "registry+https://github.com/rust-lang/crates.io-index" 148 | checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" 149 | dependencies = [ 150 | "libc", 151 | "windows-sys 0.61.1", 152 | ] 153 | 154 | [[package]] 155 | name = "fastrand" 156 | version = "2.3.0" 157 | source = "registry+https://github.com/rust-lang/crates.io-index" 158 | checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" 159 | 160 | [[package]] 161 | name = "getrandom" 162 | version = "0.3.3" 163 | source = "registry+https://github.com/rust-lang/crates.io-index" 164 | checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4" 165 | dependencies = [ 166 | "cfg-if", 167 | "libc", 168 | "r-efi", 169 | "wasi", 170 | ] 171 | 172 | [[package]] 173 | name = "heck" 174 | version = "0.5.0" 175 | source = "registry+https://github.com/rust-lang/crates.io-index" 176 | checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" 177 | 178 | [[package]] 179 | name = "is_terminal_polyfill" 180 | version = "1.70.1" 181 | source = "registry+https://github.com/rust-lang/crates.io-index" 182 | checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" 183 | 184 | [[package]] 185 | name = "libc" 186 | version = "0.2.176" 187 | source = "registry+https://github.com/rust-lang/crates.io-index" 188 | checksum = "58f929b4d672ea937a23a1ab494143d968337a5f47e56d0815df1e0890ddf174" 189 | 190 | [[package]] 191 | name = "linux-raw-sys" 192 | version = "0.11.0" 193 | source = "registry+https://github.com/rust-lang/crates.io-index" 194 | checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039" 195 | 196 | [[package]] 197 | name = "once_cell" 198 | version = "1.21.3" 199 | source = "registry+https://github.com/rust-lang/crates.io-index" 200 | checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" 201 | 202 | [[package]] 203 | name = "once_cell_polyfill" 204 | version = "1.70.1" 205 | source = "registry+https://github.com/rust-lang/crates.io-index" 206 | checksum = "a4895175b425cb1f87721b59f0f286c2092bd4af812243672510e1ac53e2e0ad" 207 | 208 | [[package]] 209 | name = "proc-macro2" 210 | version = "1.0.101" 211 | source = "registry+https://github.com/rust-lang/crates.io-index" 212 | checksum = "89ae43fd86e4158d6db51ad8e2b80f313af9cc74f5c0e03ccb87de09998732de" 213 | dependencies = [ 214 | "unicode-ident", 215 | ] 216 | 217 | [[package]] 218 | name = "quote" 219 | version = "1.0.41" 220 | source = "registry+https://github.com/rust-lang/crates.io-index" 221 | checksum = "ce25767e7b499d1b604768e7cde645d14cc8584231ea6b295e9c9eb22c02e1d1" 222 | dependencies = [ 223 | "proc-macro2", 224 | ] 225 | 226 | [[package]] 227 | name = "r-efi" 228 | version = "5.3.0" 229 | source = "registry+https://github.com/rust-lang/crates.io-index" 230 | checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" 231 | 232 | [[package]] 233 | name = "regex-lite" 234 | version = "0.1.7" 235 | source = "registry+https://github.com/rust-lang/crates.io-index" 236 | checksum = "943f41321c63ef1c92fd763bfe054d2668f7f225a5c29f0105903dc2fc04ba30" 237 | 238 | [[package]] 239 | name = "rustc-demangle" 240 | version = "0.1.26" 241 | source = "registry+https://github.com/rust-lang/crates.io-index" 242 | checksum = "56f7d92ca342cea22a06f2121d944b4fd82af56988c270852495420f961d4ace" 243 | 244 | [[package]] 245 | name = "rustix" 246 | version = "1.1.2" 247 | source = "registry+https://github.com/rust-lang/crates.io-index" 248 | checksum = "cd15f8a2c5551a84d56efdc1cd049089e409ac19a3072d5037a17fd70719ff3e" 249 | dependencies = [ 250 | "bitflags", 251 | "errno", 252 | "libc", 253 | "linux-raw-sys", 254 | "windows-sys 0.61.1", 255 | ] 256 | 257 | [[package]] 258 | name = "shlex" 259 | version = "1.3.0" 260 | source = "registry+https://github.com/rust-lang/crates.io-index" 261 | checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" 262 | 263 | [[package]] 264 | name = "strsim" 265 | version = "0.11.1" 266 | source = "registry+https://github.com/rust-lang/crates.io-index" 267 | checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" 268 | 269 | [[package]] 270 | name = "syn" 271 | version = "2.0.106" 272 | source = "registry+https://github.com/rust-lang/crates.io-index" 273 | checksum = "ede7c438028d4436d71104916910f5bb611972c5cfd7f89b8300a8186e6fada6" 274 | dependencies = [ 275 | "proc-macro2", 276 | "quote", 277 | "unicode-ident", 278 | ] 279 | 280 | [[package]] 281 | name = "tempfile" 282 | version = "3.23.0" 283 | source = "registry+https://github.com/rust-lang/crates.io-index" 284 | checksum = "2d31c77bdf42a745371d260a26ca7163f1e0924b64afa0b688e61b5a9fa02f16" 285 | dependencies = [ 286 | "fastrand", 287 | "getrandom", 288 | "once_cell", 289 | "rustix", 290 | "windows-sys 0.61.1", 291 | ] 292 | 293 | [[package]] 294 | name = "termcolor" 295 | version = "1.4.1" 296 | source = "registry+https://github.com/rust-lang/crates.io-index" 297 | checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" 298 | dependencies = [ 299 | "winapi-util", 300 | ] 301 | 302 | [[package]] 303 | name = "terminal_size" 304 | version = "0.4.3" 305 | source = "registry+https://github.com/rust-lang/crates.io-index" 306 | checksum = "60b8cb979cb11c32ce1603f8137b22262a9d131aaa5c37b5678025f22b8becd0" 307 | dependencies = [ 308 | "rustix", 309 | "windows-sys 0.60.2", 310 | ] 311 | 312 | [[package]] 313 | name = "unicode-ident" 314 | version = "1.0.19" 315 | source = "registry+https://github.com/rust-lang/crates.io-index" 316 | checksum = "f63a545481291138910575129486daeaf8ac54aee4387fe7906919f7830c7d9d" 317 | 318 | [[package]] 319 | name = "utf8parse" 320 | version = "0.2.2" 321 | source = "registry+https://github.com/rust-lang/crates.io-index" 322 | checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" 323 | 324 | [[package]] 325 | name = "wasi" 326 | version = "0.14.7+wasi-0.2.4" 327 | source = "registry+https://github.com/rust-lang/crates.io-index" 328 | checksum = "883478de20367e224c0090af9cf5f9fa85bed63a95c1abf3afc5c083ebc06e8c" 329 | dependencies = [ 330 | "wasip2", 331 | ] 332 | 333 | [[package]] 334 | name = "wasip2" 335 | version = "1.0.1+wasi-0.2.4" 336 | source = "registry+https://github.com/rust-lang/crates.io-index" 337 | checksum = "0562428422c63773dad2c345a1882263bbf4d65cf3f42e90921f787ef5ad58e7" 338 | dependencies = [ 339 | "wit-bindgen", 340 | ] 341 | 342 | [[package]] 343 | name = "winapi-util" 344 | version = "0.1.11" 345 | source = "registry+https://github.com/rust-lang/crates.io-index" 346 | checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" 347 | dependencies = [ 348 | "windows-sys 0.61.1", 349 | ] 350 | 351 | [[package]] 352 | name = "windows-link" 353 | version = "0.2.0" 354 | source = "registry+https://github.com/rust-lang/crates.io-index" 355 | checksum = "45e46c0661abb7180e7b9c281db115305d49ca1709ab8242adf09666d2173c65" 356 | 357 | [[package]] 358 | name = "windows-sys" 359 | version = "0.60.2" 360 | source = "registry+https://github.com/rust-lang/crates.io-index" 361 | checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" 362 | dependencies = [ 363 | "windows-targets", 364 | ] 365 | 366 | [[package]] 367 | name = "windows-sys" 368 | version = "0.61.1" 369 | source = "registry+https://github.com/rust-lang/crates.io-index" 370 | checksum = "6f109e41dd4a3c848907eb83d5a42ea98b3769495597450cf6d153507b166f0f" 371 | dependencies = [ 372 | "windows-link", 373 | ] 374 | 375 | [[package]] 376 | name = "windows-targets" 377 | version = "0.53.4" 378 | source = "registry+https://github.com/rust-lang/crates.io-index" 379 | checksum = "2d42b7b7f66d2a06854650af09cfdf8713e427a439c97ad65a6375318033ac4b" 380 | dependencies = [ 381 | "windows-link", 382 | "windows_aarch64_gnullvm", 383 | "windows_aarch64_msvc", 384 | "windows_i686_gnu", 385 | "windows_i686_gnullvm", 386 | "windows_i686_msvc", 387 | "windows_x86_64_gnu", 388 | "windows_x86_64_gnullvm", 389 | "windows_x86_64_msvc", 390 | ] 391 | 392 | [[package]] 393 | name = "windows_aarch64_gnullvm" 394 | version = "0.53.0" 395 | source = "registry+https://github.com/rust-lang/crates.io-index" 396 | checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764" 397 | 398 | [[package]] 399 | name = "windows_aarch64_msvc" 400 | version = "0.53.0" 401 | source = "registry+https://github.com/rust-lang/crates.io-index" 402 | checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c" 403 | 404 | [[package]] 405 | name = "windows_i686_gnu" 406 | version = "0.53.0" 407 | source = "registry+https://github.com/rust-lang/crates.io-index" 408 | checksum = "c1dc67659d35f387f5f6c479dc4e28f1d4bb90ddd1a5d3da2e5d97b42d6272c3" 409 | 410 | [[package]] 411 | name = "windows_i686_gnullvm" 412 | version = "0.53.0" 413 | source = "registry+https://github.com/rust-lang/crates.io-index" 414 | checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11" 415 | 416 | [[package]] 417 | name = "windows_i686_msvc" 418 | version = "0.53.0" 419 | source = "registry+https://github.com/rust-lang/crates.io-index" 420 | checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d" 421 | 422 | [[package]] 423 | name = "windows_x86_64_gnu" 424 | version = "0.53.0" 425 | source = "registry+https://github.com/rust-lang/crates.io-index" 426 | checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba" 427 | 428 | [[package]] 429 | name = "windows_x86_64_gnullvm" 430 | version = "0.53.0" 431 | source = "registry+https://github.com/rust-lang/crates.io-index" 432 | checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57" 433 | 434 | [[package]] 435 | name = "windows_x86_64_msvc" 436 | version = "0.53.0" 437 | source = "registry+https://github.com/rust-lang/crates.io-index" 438 | checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" 439 | 440 | [[package]] 441 | name = "wit-bindgen" 442 | version = "0.46.0" 443 | source = "registry+https://github.com/rust-lang/crates.io-index" 444 | checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59" 445 | --------------------------------------------------------------------------------