Visualize lines of source code by module

This visualization generates a stacked bar chart depicting lines of code
within each module broken down by language.

Change-Id: I7a14a6dcf38a6611c41e21182177bb23a4baf2a2
diff --git a/script/graphs/sloc-viz.bash b/script/graphs/sloc-viz.bash
new file mode 100644
index 0000000..135b4cf
--- /dev/null
+++ b/script/graphs/sloc-viz.bash
@@ -0,0 +1,85 @@
+#
+# Copyright (c) 2021 Arm Limited. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+#!/usr/bin/env bash
+set -euo pipefail
+
+# Overview
+# ========
+#
+# This script generates source lines of code as a tab separated values (TSV)
+# file and a stacked bar chart. It uses `tokei` for gathering the data, and
+# `gnuplot` for generating the plot. The data is available on stderr and the
+# plot will be put in stdout.
+#
+# This script generates information about the directory that it's run in,
+# aggregated by subdirectory.
+# It is recommended that you run it from within the TF-A root directory for
+# best results.
+
+# Variables
+# =========
+
+# convert newlines to tabs
+n2t="tr \n \t"
+
+# We will build the final data file incrementally throughout the script. We need
+# A place to store this data, temporarily, so mktemp fills the role.
+data=$(mktemp XXXXXX-sloc.tsv)
+
+# Top level TF-A directories that we consider by themselves.
+toplevel=$(find -mindepth 1 -maxdepth 1 -type d -and ! -name ".*" | sed "s|./||g")
+
+# Second level TF-A directories that we consider separately.
+secondlevel=$(find drivers plat -mindepth 1 -maxdepth 1 -type d || true)
+
+# We want to be sure that we always put the data in the same order, with the
+# same keys in the resulting TSV file. To ensure this, we keep a json-encoded
+# array of the categories we would like to show in the graph.
+# This was generated by taking the output of `tokei --output json | jq keys`
+# and trimming out things that we don't really need like "Svg"
+categories='["AssemblyGAS", "C", "CHeader", "DeviceTree", "Makefile", "Python", "ReStructuredText"]'
+
+# Data File Generation
+# ====================
+#
+# Below we generate the data file used for the graph. The table is a
+# tab separated value(TSV) matrix with columns of code language (Bash, C, etc),
+# and rows of subdirectories of TF-A that contain the code.
+
+# Column headers
+# --------------
+(echo module; echo $categories | jq ".[]" ) | $n2t  > $data
+# add a newline
+echo >> $data
+
+# Build Each Row
+# --------------
+for dir in $toplevel $secondlevel; do
+	# Gnuplot likes to treat underscores as a syntax for subscripts. This
+	# looks weird, as module names are not named with this syntax in mind.
+	# Further, it turns out that we go through 3 expansions, so we need 8 (2^3)
+	# backslashes.
+	echo $dir | sed -e "s/_/\\\\\\\\_/g" | $n2t >> $data
+	# This is the heart of the implementation, and probably the most
+	# complicated line in this script. First, we generate the subdirectory
+	# sloc with tokei, in json format. We then filter it with jq. The jq
+	# filter is a foreach loop where we iterate over $x = column name, as
+	# passed in as the first positional argument. Each interation through
+	# the loop, we print out the code value, when it exists, or null + 0.
+	# This takes advantage of the property of null:
+	#  > null can be added to any value, and returns the other value
+	#  > unchanged.
+	tokei --output json $dir \
+		| jq '$ARGS.positional[0][] as $x | .[$x].code + 0' \
+			--jsonargs "$categories" \
+		| $n2t >> $data
+	echo  >> $data
+done
+
+cat $data 1>&2
+gnuplot -c ${0%bash}plot $data
+
+rm $data