Jimmy Brisson | c7e28f5 | 2021-01-21 16:23:21 -0600 | [diff] [blame^] | 1 | # |
| 2 | # Copyright (c) 2021 Arm Limited. All rights reserved. |
| 3 | # |
| 4 | # SPDX-License-Identifier: BSD-3-Clause |
| 5 | # |
| 6 | #!/usr/bin/env bash |
| 7 | set -euo pipefail |
| 8 | |
| 9 | # Overview |
| 10 | # ======== |
| 11 | # |
| 12 | # This script generates source lines of code as a tab separated values (TSV) |
| 13 | # file and a stacked bar chart. It uses `tokei` for gathering the data, and |
| 14 | # `gnuplot` for generating the plot. The data is available on stderr and the |
| 15 | # plot will be put in stdout. |
| 16 | # |
| 17 | # This script generates information about the directory that it's run in, |
| 18 | # aggregated by subdirectory. |
| 19 | # It is recommended that you run it from within the TF-A root directory for |
| 20 | # best results. |
| 21 | |
| 22 | # Variables |
| 23 | # ========= |
| 24 | |
| 25 | # convert newlines to tabs |
| 26 | n2t="tr \n \t" |
| 27 | |
| 28 | # We will build the final data file incrementally throughout the script. We need |
| 29 | # A place to store this data, temporarily, so mktemp fills the role. |
| 30 | data=$(mktemp XXXXXX-sloc.tsv) |
| 31 | |
| 32 | # Top level TF-A directories that we consider by themselves. |
| 33 | toplevel=$(find -mindepth 1 -maxdepth 1 -type d -and ! -name ".*" | sed "s|./||g") |
| 34 | |
| 35 | # Second level TF-A directories that we consider separately. |
| 36 | secondlevel=$(find drivers plat -mindepth 1 -maxdepth 1 -type d || true) |
| 37 | |
| 38 | # We want to be sure that we always put the data in the same order, with the |
| 39 | # same keys in the resulting TSV file. To ensure this, we keep a json-encoded |
| 40 | # array of the categories we would like to show in the graph. |
| 41 | # This was generated by taking the output of `tokei --output json | jq keys` |
| 42 | # and trimming out things that we don't really need like "Svg" |
| 43 | categories='["AssemblyGAS", "C", "CHeader", "DeviceTree", "Makefile", "Python", "ReStructuredText"]' |
| 44 | |
| 45 | # Data File Generation |
| 46 | # ==================== |
| 47 | # |
| 48 | # Below we generate the data file used for the graph. The table is a |
| 49 | # tab separated value(TSV) matrix with columns of code language (Bash, C, etc), |
| 50 | # and rows of subdirectories of TF-A that contain the code. |
| 51 | |
| 52 | # Column headers |
| 53 | # -------------- |
| 54 | (echo module; echo $categories | jq ".[]" ) | $n2t > $data |
| 55 | # add a newline |
| 56 | echo >> $data |
| 57 | |
| 58 | # Build Each Row |
| 59 | # -------------- |
| 60 | for dir in $toplevel $secondlevel; do |
| 61 | # Gnuplot likes to treat underscores as a syntax for subscripts. This |
| 62 | # looks weird, as module names are not named with this syntax in mind. |
| 63 | # Further, it turns out that we go through 3 expansions, so we need 8 (2^3) |
| 64 | # backslashes. |
| 65 | echo $dir | sed -e "s/_/\\\\\\\\_/g" | $n2t >> $data |
| 66 | # This is the heart of the implementation, and probably the most |
| 67 | # complicated line in this script. First, we generate the subdirectory |
| 68 | # sloc with tokei, in json format. We then filter it with jq. The jq |
| 69 | # filter is a foreach loop where we iterate over $x = column name, as |
| 70 | # passed in as the first positional argument. Each interation through |
| 71 | # the loop, we print out the code value, when it exists, or null + 0. |
| 72 | # This takes advantage of the property of null: |
| 73 | # > null can be added to any value, and returns the other value |
| 74 | # > unchanged. |
| 75 | tokei --output json $dir \ |
| 76 | | jq '$ARGS.positional[0][] as $x | .[$x].code + 0' \ |
| 77 | --jsonargs "$categories" \ |
| 78 | | $n2t >> $data |
| 79 | echo >> $data |
| 80 | done |
| 81 | |
| 82 | cat $data 1>&2 |
| 83 | gnuplot -c ${0%bash}plot $data |
| 84 | |
| 85 | rm $data |