123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249 |
- #!/bin/bash
- set -eu
- # Copyright 2020 Google Inc. All rights reserved.
- #
- # Licensed under the Apache License, Version 2.0 (the "License");
- # you may not use this file except in compliance with the License.
- # You may obtain a copy of the License at
- #
- # http://www.apache.org/licenses/LICENSE-2.0
- #
- # Unless required by applicable law or agreed to in writing, software
- # distributed under the License is distributed on an "AS IS" BASIS,
- # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- # See the License for the specific language governing permissions and
- # limitations under the License.
- # Tool to evaluate the transitive closure of the ninja dependency graph of the
- # files and targets depending on a given target.
- #
- # i.e. the list of things that could change after changing a target.
- readonly me=$(basename "${0}")
- readonly usage="usage: ${me} {options} target [target...]
- Evaluate the reverse transitive closure of ninja targets depending on one or
- more targets.
- Options:
- -(no)quiet Suppresses progress output to stderr and interactive
- alias -(no)q prompts. By default, when stderr is a tty, progress gets
- reported to stderr; when both stderr and stdin are tty,
- the script asks user whether to delete intermediate files.
- When suppressed or not prompted, script always deletes the
- temporary / intermediate files.
- -sep=<delim> Use 'delim' as output field separator between notice
- checksum and notice filename in notice output.
- e.g. sep='\t'
- (Default space)
- -csv Shorthand for -sep=','
- At minimum, before running this script, you must first run:
- $ source build/envsetup.sh
- $ lunch
- $ m nothing
- to setup the build environment, choose a target platform, and build the ninja
- dependency graph.
- "
- function die() { echo -e "${*}" >&2; exit 2; }
- # Reads one input target per line from stdin; outputs (isnotice target) tuples.
- #
- # output target is a ninja target that the input target depends on
- # isnotice in {0,1} with 1 for output targets believed to be license or notice
- #
- # only argument is the dependency depth indicator
- function getDeps() {
- (tr '\n' '\0' | xargs -0 "${ninja_bin}" -f "${ninja_file}" -t query) \
- | awk -v depth="${1}" '
- BEGIN {
- inoutput = 0
- }
- $0 ~ /^\S\S*:$/ {
- inoutput = 0
- }
- $1 == "validations:" {
- inoutput = 0
- }
- inoutput != 0 {
- print gensub(/^\s*/, "", "g")" "depth
- }
- $1 == "outputs:" {
- inoutput = 1
- }
- '
- }
- if [ -z "${ANDROID_BUILD_TOP}" ]; then
- die "${me}: Run 'lunch' to configure the build environment"
- fi
- if [ -z "${TARGET_PRODUCT}" ]; then
- die "${me}: Run 'lunch' to configure the build environment"
- fi
- ninja_file="${ANDROID_BUILD_TOP}/out/combined-${TARGET_PRODUCT}.ninja"
- if [ ! -f "${ninja_file}" ]; then
- die "${me}: Run 'm nothing' to build the dependency graph"
- fi
- ninja_bin="${ANDROID_BUILD_TOP}/prebuilts/build-tools/linux-x86/bin/ninja"
- if [ ! -x "${ninja_bin}" ]; then
- die "${me}: Cannot find ninja executable expected at ${ninja_bin}"
- fi
- # parse the command-line
- declare -a targets # one or more targets to evaluate
- quiet=false # whether to suppress progress
- sep=" " # output separator between depth and target
- use_stdin=false # whether to read targets from stdin i.e. target -
- while [ $# -gt 0 ]; do
- case "${1:-}" in
- -)
- use_stdin=true
- ;;
- -*)
- flag=$(expr "${1}" : '^-*\(.*\)$')
- case "${flag:-}" in
- q) ;&
- quiet)
- quiet=true;;
- noq) ;&
- noquiet)
- quiet=false;;
- csv)
- sep=",";;
- sep)
- sep="${2?"${usage}"}"; shift;;
- sep=*)
- sep=$(expr "${flag}" : '^sep=\(.*\)$';;
- *)
- die "Unknown flag ${1}"
- ;;
- esac
- ;;
- *)
- targets+=("${1:-}")
- ;;
- esac
- shift
- done
- if [ ! -v targets[0] ] && ! ${use_stdin}; then
- die "${usage}\n\nNo target specified."
- fi
- # showProgress when stderr is a tty
- if [ -t 2 ] && ! ${quiet}; then
- showProgress=true
- else
- showProgress=false
- fi
- # interactive when both stderr and stdin are tty
- if ${showProgress} && [ -t 0 ]; then
- interactive=true
- else
- interactive=false
- fi
- readonly tmpFiles=$(mktemp -d "${TMPDIR}.tdeps.XXXXXXXXX")
- if [ -z "${tmpFiles}" ]; then
- die "${me}: unable to create temporary directory"
- fi
- # The deps files contain unique (isnotice target) tuples where
- # isnotice in {0,1} with 1 when ninja target `target` is a license or notice.
- readonly oldDeps="${tmpFiles}/old"
- readonly newDeps="${tmpFiles}/new"
- readonly allDeps="${tmpFiles}/all"
- if ${use_stdin}; then # start deps by reading 1 target per line from stdin
- awk '
- NF > 0 {
- print gensub(/\s*$/, "", "g", gensub(/^\s*/, "", "g"))" "0
- }
- ' >"${newDeps}"
- else # start with no deps by clearing file
- : >"${newDeps}"
- fi
- # extend deps by appending targets from command-line
- for idx in "${!targets[*]}"; do
- echo "${targets[${idx}]} 0" >>"${newDeps}"
- done
- # remove duplicates and start with new, old and all the same
- sort -u <"${newDeps}" >"${allDeps}"
- cp "${allDeps}" "${newDeps}"
- cp "${allDeps}" "${oldDeps}"
- # report depth of dependenciens when showProgress
- depth=0
- while [ $(wc -l < "${newDeps}") -gt 0 ]; do
- if ${showProgress}; then
- echo "depth ${depth} has "$(wc -l < "${newDeps}")" targets" >&2
- fi
- depth=$(expr ${depth} + 1)
- ( # recalculate dependencies by combining unique inputs of new deps w. old
- cut -d\ -f1 "${newDeps}" | getDeps "${depth}"
- cat "${oldDeps}"
- ) | sort -n | awk '
- BEGIN {
- prev = ""
- }
- {
- depth = $NF
- $NF = ""
- gsub(/\s*$/, "")
- if ($0 != prev) {
- print gensub(/\s*$/, "", "g")" "depth
- }
- prev = $0
- }
- ' >"${allDeps}"
- # recalculate new dependencies as net additions to old dependencies
- set +e
- diff "${oldDeps}" "${allDeps}" --old-line-format='' \
- --new-line-format='%L' --unchanged-line-format='' > "${newDeps}"
- set -e
- # recalculate old dependencies for next iteration
- cp "${allDeps}" "${oldDeps}"
- done
- # found all deps -- clean up last iteration of old and new
- rm -f "${oldDeps}"
- rm -f "${newDeps}"
- if ${showProgress}; then
- echo $(wc -l < "${allDeps}")" targets" >&2
- fi
- awk -v sep="${sep}" '{
- depth = $NF
- $NF = ""
- gsub(/\s*$/, "")
- print depth sep $0
- }' "${allDeps}" | sort -n
- if ${interactive}; then
- echo -n "$(date '+%F %-k:%M:%S') Delete ${tmpFiles} ? [n] " >&2
- read answer
- case "${answer}" in [yY]*) rm -fr "${tmpFiles}";; esac
- else
- rm -fr "${tmpFiles}"
- fi
|