Scripts to get package size information

Adds some scripts to get the size contributions for individual
packages in the final system image.

  ./package_dependency_list.sh
    Takes a package, and determines all of its dependencies.
    Then gets the size of those dependencies using qsize and
    outputs the sum.
  ./package_dependency_runtime_stats.sh
    Builds the given package and its runtime dependencies and
    installs them to a temporary directory.  Then returns the
    final size used by those packages.

BUG=chromium:327527
TEST=`./package_size_stats.sh --board=beaglebone \
     /build/beaglebone` works
TEST=`./package_dependency_list.sh --board=beaglebone \
     update_engine` works
TEST=`./package_dependency_runtime_stats.sh --board=beaglebone \
     gflags` works

Change-Id: I5a99980567e0cb1cfa2bbbc009a167b4f6392ccf
Reviewed-on: https://chromium-review.googlesource.com/182393
Reviewed-by: Mike Frysinger <vapier@chromium.org>
Reviewed-by: Steve Fung <stevefung@chromium.org>
Commit-Queue: Steve Fung <stevefung@chromium.org>
Tested-by: Steve Fung <stevefung@chromium.org>
diff --git a/.gitignore b/.gitignore
index eb4e302..f4c0612 100644
--- a/.gitignore
+++ b/.gitignore
@@ -9,3 +9,4 @@
 *~
 /cbuildbot_package.list
 dev_server.log
+*.out
diff --git a/package_dependency_list.sh b/package_dependency_list.sh
new file mode 100755
index 0000000..84a99cd
--- /dev/null
+++ b/package_dependency_list.sh
@@ -0,0 +1,119 @@
+#!/bin/bash
+# Copyright (c) 2014 The Chromium OS Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+# Script that finds all dependencies and sub-dependencies of a package and
+# then sums up the total sizes of those dependencies based on output from
+# the script `package_size_stats.sh`.
+
+. "$(dirname "$0")/common.sh" || exit 1
+
+# Script must run inside the chroot
+restart_in_chroot_if_needed "$@"
+
+assert_not_root_user
+
+# Developer-visible flags.
+DEFINE_string board "${DEFAULT_BOARD}" \
+  "The board to build packages for."
+DEFINE_string depth "20" \
+  "The depth to go looking for dependencies."
+
+# Clean up the package name returned from qdepends.  Removes version numbers
+# and USE flag conditional requirements from the given string.
+# Arguments:
+#   $1: An individual package string returned from qdepends.
+# Returns:
+#   The clean package name.
+clean_package_name() {
+  echo "$1" | sed -r -e 's/([~>=!<]*)(.*)/\2/g' -e 's/[:[].*//g'
+}
+
+get_package_size() {
+  local package=${1}
+  echo "$(qsize-${FLAGS_board} -b -C "${package}" \
+            | sed -n "\:${package}-[0-9]:p" \
+            | sed -r 's/.*, ([0-9]*) bytes/\1/')"
+}
+
+get_dependency_size() {
+  declare -A deps
+
+  local dependencies="$1"
+
+  local dep_depth=${FLAGS_depth}
+
+  info "Dependency Search Depth: ${dep_depth}"
+
+  local i
+  local cleanname
+  for i in ${dependencies}; do
+    cleanname=$(clean_package_name $i)
+    deps[${cleanname}]=$(get_package_size ${cleanname})
+    if [[ -z "${deps[${cleanname}]}" ]]; then
+      deps[${cleanname}]="0"
+    fi
+  done
+
+  local loop=0
+  local prevsize
+  while [[ ${loop} -lt ${dep_depth} ]]; do
+    : $(( loop += 1 ))
+    prevsize=${#deps[@]}
+
+    local depname
+    for depname in "${!deps[@]}"; do
+      dependencies="$(qdepends -r -N -C -q "${depname}" \
+        | sed 's/\s\+/\n/g' \
+        | sed '1d')"
+      for i in ${dependencies}; do
+        cleanname=$(clean_package_name "${i}")
+        if [[ ${deps[${cleanname}]+set} != "set" ]]; then
+          # New key found, add to array and get size if it's available.
+          deps[${cleanname}]=$(get_package_size ${cleanname})
+          if [[ -z "${deps[${cleanname}]}" ]]; then
+            deps[${cleanname}]="0"
+          fi
+        fi
+      done
+    done
+
+    if [[ ${prevsize} -eq ${#deps[@]} ]]; then
+      # No new dependencies found on this iteration, break out of loop
+      break
+    fi
+  done
+
+  # Print all dependencies.
+  info "Found ${#deps[@]} dependencies!"
+  local total_size=0
+  for depname in "${!deps[@]}"; do
+    info "${depname}: ${deps[${depname}]}"
+    : $(( total_size += ${deps[${depname}]} ))
+  done
+
+  info "\n\nTotal size for package ${1}: ${total_size}\n\n--\n\n"
+}
+
+main() {
+
+  # Parse command line
+  FLAGS "$@" || exit 1
+  eval set -- "${FLAGS_ARGV}"
+
+  if [[ $# -eq 0 ]]; then
+    die "Usage: $0 --board=<board> <package>"
+  fi
+
+  if [[ -z "${FLAGS_board}" ]]; then
+    die "Error: --board is required."
+  fi
+
+  local package
+  for package in "$@"; do
+    get_dependency_size "${package}"
+  done
+}
+
+main "$@"
diff --git a/package_dependency_runtime_stats.sh b/package_dependency_runtime_stats.sh
new file mode 100755
index 0000000..e394acc
--- /dev/null
+++ b/package_dependency_runtime_stats.sh
@@ -0,0 +1,62 @@
+#!/bin/bash
+# Copyright (c) 2014 The Chromium OS Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+# Script that builds the given package and all its runtime dependencies, and
+# installs them in a temporary directory and returns the size used
+
+. "$(dirname "$0")/common.sh" || exit 1
+
+export INSTALL_MASK="${DEFAULT_INSTALL_MASK}"
+
+# Script must run inside the chroot
+restart_in_chroot_if_needed "$@"
+
+assert_not_root_user
+
+# Developer-visible flags.
+DEFINE_string board "${DEFAULT_BOARD}" \
+  "The board to build packages for."
+
+cleanup() {
+  echo "Do you wish to remove the temporary install directory [${tmp_folder}]?"
+  PS3="Remove? "
+  local reply="Error"
+  while [[ "${reply}" == "Error" ]]; do
+    choose reply "Error" "Error" "Yes" "No"
+  done
+  if [[ "${reply}" == "Yes" ]]; then
+    sudo rm -rf "${tmp_folder}"
+  fi
+}
+
+main() {
+
+  # Parse command line
+  FLAGS "$@" || exit 1
+  eval set -- "${FLAGS_ARGV}"
+
+  if [[ $# -eq 0 ]]; then
+    die "Usage: $0 --board=<board> <package>"
+  fi
+
+  if [[ -z "${FLAGS_board}" ]]; then
+    die "Error: --board is required."
+  fi
+
+  local package="$1"
+  tmp_folder=$(mktemp -d "/tmp/pdeps-${1}-XXXXX") \
+    || die "Couldn't create temp folder."
+
+  trap cleanup EXIT
+
+  emerge-${FLAGS_board} --root="${tmp_folder}" --root-deps=rdeps \
+    --keep-going=y ${package}
+
+  local size_used=$(sudo du -sh "${tmp_folder}" | cut -f1)
+
+  info "Size used for package ${package}: ${size_used}\n\n"
+}
+
+main "$@"