xref: /aosp_15_r20/build/soong/scripts/reverse-deps.sh (revision 333d2b3687b3a337dbcca9d65000bca186795e39)
1*333d2b36SAndroid Build Coastguard Worker#!/bin/bash
2*333d2b36SAndroid Build Coastguard Worker
3*333d2b36SAndroid Build Coastguard Workerset -eu
4*333d2b36SAndroid Build Coastguard Worker
5*333d2b36SAndroid Build Coastguard Worker# Copyright 2020 Google Inc. All rights reserved.
6*333d2b36SAndroid Build Coastguard Worker#
7*333d2b36SAndroid Build Coastguard Worker# Licensed under the Apache License, Version 2.0 (the "License");
8*333d2b36SAndroid Build Coastguard Worker# you may not use this file except in compliance with the License.
9*333d2b36SAndroid Build Coastguard Worker# You may obtain a copy of the License at
10*333d2b36SAndroid Build Coastguard Worker#
11*333d2b36SAndroid Build Coastguard Worker#     http://www.apache.org/licenses/LICENSE-2.0
12*333d2b36SAndroid Build Coastguard Worker#
13*333d2b36SAndroid Build Coastguard Worker# Unless required by applicable law or agreed to in writing, software
14*333d2b36SAndroid Build Coastguard Worker# distributed under the License is distributed on an "AS IS" BASIS,
15*333d2b36SAndroid Build Coastguard Worker# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16*333d2b36SAndroid Build Coastguard Worker# See the License for the specific language governing permissions and
17*333d2b36SAndroid Build Coastguard Worker# limitations under the License.
18*333d2b36SAndroid Build Coastguard Worker
19*333d2b36SAndroid Build Coastguard Worker# Tool to evaluate the transitive closure of the ninja dependency graph of the
20*333d2b36SAndroid Build Coastguard Worker# files and targets depending on a given target.
21*333d2b36SAndroid Build Coastguard Worker#
22*333d2b36SAndroid Build Coastguard Worker# i.e. the list of things that could change after changing a target.
23*333d2b36SAndroid Build Coastguard Worker
24*333d2b36SAndroid Build Coastguard Workerreadonly me=$(basename "${0}")
25*333d2b36SAndroid Build Coastguard Worker
26*333d2b36SAndroid Build Coastguard Workerreadonly usage="usage: ${me} {options} target [target...]
27*333d2b36SAndroid Build Coastguard Worker
28*333d2b36SAndroid Build Coastguard WorkerEvaluate the reverse transitive closure of ninja targets depending on one or
29*333d2b36SAndroid Build Coastguard Workermore targets.
30*333d2b36SAndroid Build Coastguard Worker
31*333d2b36SAndroid Build Coastguard WorkerOptions:
32*333d2b36SAndroid Build Coastguard Worker
33*333d2b36SAndroid Build Coastguard Worker  -(no)quiet        Suppresses progress output to stderr and interactive
34*333d2b36SAndroid Build Coastguard Worker    alias -(no)q    prompts. By default, when stderr is a tty, progress gets
35*333d2b36SAndroid Build Coastguard Worker                    reported to stderr; when both stderr and stdin are tty,
36*333d2b36SAndroid Build Coastguard Worker                    the script asks user whether to delete intermediate files.
37*333d2b36SAndroid Build Coastguard Worker                    When suppressed or not prompted, script always deletes the
38*333d2b36SAndroid Build Coastguard Worker                    temporary / intermediate files.
39*333d2b36SAndroid Build Coastguard Worker  -sep=<delim>      Use 'delim' as output field separator between notice
40*333d2b36SAndroid Build Coastguard Worker                    checksum and notice filename in notice output.
41*333d2b36SAndroid Build Coastguard Worker                    e.g. sep='\t'
42*333d2b36SAndroid Build Coastguard Worker                    (Default space)
43*333d2b36SAndroid Build Coastguard Worker  -csv              Shorthand for -sep=','
44*333d2b36SAndroid Build Coastguard Worker
45*333d2b36SAndroid Build Coastguard WorkerAt minimum, before running this script, you must first run:
46*333d2b36SAndroid Build Coastguard Worker$ source build/envsetup.sh
47*333d2b36SAndroid Build Coastguard Worker$ lunch
48*333d2b36SAndroid Build Coastguard Worker$ m nothing
49*333d2b36SAndroid Build Coastguard Workerto setup the build environment, choose a target platform, and build the ninja
50*333d2b36SAndroid Build Coastguard Workerdependency graph.
51*333d2b36SAndroid Build Coastguard Worker"
52*333d2b36SAndroid Build Coastguard Worker
53*333d2b36SAndroid Build Coastguard Workerfunction die() { echo -e "${*}" >&2; exit 2; }
54*333d2b36SAndroid Build Coastguard Worker
55*333d2b36SAndroid Build Coastguard Worker# Reads one input target per line from stdin; outputs (isnotice target) tuples.
56*333d2b36SAndroid Build Coastguard Worker#
57*333d2b36SAndroid Build Coastguard Worker# output target is a ninja target that the input target depends on
58*333d2b36SAndroid Build Coastguard Worker# isnotice in {0,1} with 1 for output targets believed to be license or notice
59*333d2b36SAndroid Build Coastguard Worker#
60*333d2b36SAndroid Build Coastguard Worker# only argument is the dependency depth indicator
61*333d2b36SAndroid Build Coastguard Workerfunction getDeps() {
62*333d2b36SAndroid Build Coastguard Worker    (tr '\n' '\0' | xargs -0 "${ninja_bin}" -f "${ninja_file}" -t query) \
63*333d2b36SAndroid Build Coastguard Worker    | awk -v depth="${1}" '
64*333d2b36SAndroid Build Coastguard Worker      BEGIN {
65*333d2b36SAndroid Build Coastguard Worker        inoutput = 0
66*333d2b36SAndroid Build Coastguard Worker      }
67*333d2b36SAndroid Build Coastguard Worker      $0 ~ /^\S\S*:$/ {
68*333d2b36SAndroid Build Coastguard Worker        inoutput = 0
69*333d2b36SAndroid Build Coastguard Worker      }
70*333d2b36SAndroid Build Coastguard Worker      $1 == "validations:" {
71*333d2b36SAndroid Build Coastguard Worker        inoutput = 0
72*333d2b36SAndroid Build Coastguard Worker      }
73*333d2b36SAndroid Build Coastguard Worker      inoutput != 0 {
74*333d2b36SAndroid Build Coastguard Worker        print gensub(/^\s*/, "", "g")" "depth
75*333d2b36SAndroid Build Coastguard Worker      }
76*333d2b36SAndroid Build Coastguard Worker      $1 == "outputs:" {
77*333d2b36SAndroid Build Coastguard Worker        inoutput = 1
78*333d2b36SAndroid Build Coastguard Worker      }
79*333d2b36SAndroid Build Coastguard Worker    '
80*333d2b36SAndroid Build Coastguard Worker}
81*333d2b36SAndroid Build Coastguard Worker
82*333d2b36SAndroid Build Coastguard Worker
83*333d2b36SAndroid Build Coastguard Workerif [ -z "${ANDROID_BUILD_TOP}" ]; then
84*333d2b36SAndroid Build Coastguard Worker    die "${me}: Run 'lunch' to configure the build environment"
85*333d2b36SAndroid Build Coastguard Workerfi
86*333d2b36SAndroid Build Coastguard Worker
87*333d2b36SAndroid Build Coastguard Workerif [ -z "${TARGET_PRODUCT}" ]; then
88*333d2b36SAndroid Build Coastguard Worker    die "${me}: Run 'lunch' to configure the build environment"
89*333d2b36SAndroid Build Coastguard Workerfi
90*333d2b36SAndroid Build Coastguard Worker
91*333d2b36SAndroid Build Coastguard Workerninja_file="${ANDROID_BUILD_TOP}/out/combined-${TARGET_PRODUCT}.ninja"
92*333d2b36SAndroid Build Coastguard Workerif [ ! -f "${ninja_file}" ]; then
93*333d2b36SAndroid Build Coastguard Worker    die "${me}: Run 'm nothing' to build the dependency graph"
94*333d2b36SAndroid Build Coastguard Workerfi
95*333d2b36SAndroid Build Coastguard Worker
96*333d2b36SAndroid Build Coastguard Workerninja_bin="${ANDROID_BUILD_TOP}/prebuilts/build-tools/linux-x86/bin/ninja"
97*333d2b36SAndroid Build Coastguard Workerif [ ! -x "${ninja_bin}" ]; then
98*333d2b36SAndroid Build Coastguard Worker    die "${me}: Cannot find ninja executable expected at ${ninja_bin}"
99*333d2b36SAndroid Build Coastguard Workerfi
100*333d2b36SAndroid Build Coastguard Worker
101*333d2b36SAndroid Build Coastguard Worker
102*333d2b36SAndroid Build Coastguard Worker# parse the command-line
103*333d2b36SAndroid Build Coastguard Worker
104*333d2b36SAndroid Build Coastguard Workerdeclare -a targets # one or more targets to evaluate
105*333d2b36SAndroid Build Coastguard Worker
106*333d2b36SAndroid Build Coastguard Workerquiet=false      # whether to suppress progress
107*333d2b36SAndroid Build Coastguard Worker
108*333d2b36SAndroid Build Coastguard Workersep=" "          # output separator between depth and target
109*333d2b36SAndroid Build Coastguard Worker
110*333d2b36SAndroid Build Coastguard Workeruse_stdin=false  # whether to read targets from stdin i.e. target -
111*333d2b36SAndroid Build Coastguard Worker
112*333d2b36SAndroid Build Coastguard Workerwhile [ $# -gt 0 ]; do
113*333d2b36SAndroid Build Coastguard Worker    case "${1:-}" in
114*333d2b36SAndroid Build Coastguard Worker      -)
115*333d2b36SAndroid Build Coastguard Worker        use_stdin=true
116*333d2b36SAndroid Build Coastguard Worker      ;;
117*333d2b36SAndroid Build Coastguard Worker      -*)
118*333d2b36SAndroid Build Coastguard Worker        flag=$(expr "${1}" : '^-*\(.*\)$')
119*333d2b36SAndroid Build Coastguard Worker        case "${flag:-}" in
120*333d2b36SAndroid Build Coastguard Worker          q) ;&
121*333d2b36SAndroid Build Coastguard Worker          quiet)
122*333d2b36SAndroid Build Coastguard Worker            quiet=true;;
123*333d2b36SAndroid Build Coastguard Worker          noq) ;&
124*333d2b36SAndroid Build Coastguard Worker          noquiet)
125*333d2b36SAndroid Build Coastguard Worker            quiet=false;;
126*333d2b36SAndroid Build Coastguard Worker          csv)
127*333d2b36SAndroid Build Coastguard Worker            sep=",";;
128*333d2b36SAndroid Build Coastguard Worker          sep)
129*333d2b36SAndroid Build Coastguard Worker            sep="${2?"${usage}"}"; shift;;
130*333d2b36SAndroid Build Coastguard Worker          sep=*)
131*333d2b36SAndroid Build Coastguard Worker            sep=$(expr "${flag}" : '^sep=\(.*\)$';;
132*333d2b36SAndroid Build Coastguard Worker          *)
133*333d2b36SAndroid Build Coastguard Worker            die "Unknown flag ${1}"
134*333d2b36SAndroid Build Coastguard Worker          ;;
135*333d2b36SAndroid Build Coastguard Worker        esac
136*333d2b36SAndroid Build Coastguard Worker      ;;
137*333d2b36SAndroid Build Coastguard Worker      *)
138*333d2b36SAndroid Build Coastguard Worker        targets+=("${1:-}")
139*333d2b36SAndroid Build Coastguard Worker      ;;
140*333d2b36SAndroid Build Coastguard Worker    esac
141*333d2b36SAndroid Build Coastguard Worker    shift
142*333d2b36SAndroid Build Coastguard Workerdone
143*333d2b36SAndroid Build Coastguard Worker
144*333d2b36SAndroid Build Coastguard Workerif [ ! -v targets[0] ] && ! ${use_stdin}; then
145*333d2b36SAndroid Build Coastguard Worker    die "${usage}\n\nNo target specified."
146*333d2b36SAndroid Build Coastguard Workerfi
147*333d2b36SAndroid Build Coastguard Worker
148*333d2b36SAndroid Build Coastguard Worker# showProgress when stderr is a tty
149*333d2b36SAndroid Build Coastguard Workerif [ -t 2 ] && ! ${quiet}; then
150*333d2b36SAndroid Build Coastguard Worker    showProgress=true
151*333d2b36SAndroid Build Coastguard Workerelse
152*333d2b36SAndroid Build Coastguard Worker    showProgress=false
153*333d2b36SAndroid Build Coastguard Workerfi
154*333d2b36SAndroid Build Coastguard Worker
155*333d2b36SAndroid Build Coastguard Worker# interactive when both stderr and stdin are tty
156*333d2b36SAndroid Build Coastguard Workerif ${showProgress} && [ -t 0 ]; then
157*333d2b36SAndroid Build Coastguard Worker    interactive=true
158*333d2b36SAndroid Build Coastguard Workerelse
159*333d2b36SAndroid Build Coastguard Worker    interactive=false
160*333d2b36SAndroid Build Coastguard Workerfi
161*333d2b36SAndroid Build Coastguard Worker
162*333d2b36SAndroid Build Coastguard Worker
163*333d2b36SAndroid Build Coastguard Workerreadonly tmpFiles=$(mktemp -d "${TMPDIR}.tdeps.XXXXXXXXX")
164*333d2b36SAndroid Build Coastguard Workerif [ -z "${tmpFiles}" ]; then
165*333d2b36SAndroid Build Coastguard Worker    die "${me}: unable to create temporary directory"
166*333d2b36SAndroid Build Coastguard Workerfi
167*333d2b36SAndroid Build Coastguard Worker
168*333d2b36SAndroid Build Coastguard Worker# The deps files contain unique (isnotice target) tuples where
169*333d2b36SAndroid Build Coastguard Worker# isnotice in {0,1} with 1 when ninja target `target` is a license or notice.
170*333d2b36SAndroid Build Coastguard Workerreadonly oldDeps="${tmpFiles}/old"
171*333d2b36SAndroid Build Coastguard Workerreadonly newDeps="${tmpFiles}/new"
172*333d2b36SAndroid Build Coastguard Workerreadonly allDeps="${tmpFiles}/all"
173*333d2b36SAndroid Build Coastguard Worker
174*333d2b36SAndroid Build Coastguard Workerif ${use_stdin}; then # start deps by reading 1 target per line from stdin
175*333d2b36SAndroid Build Coastguard Worker  awk '
176*333d2b36SAndroid Build Coastguard Worker    NF > 0 {
177*333d2b36SAndroid Build Coastguard Worker      print gensub(/\s*$/, "", "g", gensub(/^\s*/, "", "g"))" "0
178*333d2b36SAndroid Build Coastguard Worker    }
179*333d2b36SAndroid Build Coastguard Worker  ' >"${newDeps}"
180*333d2b36SAndroid Build Coastguard Workerelse # start with no deps by clearing file
181*333d2b36SAndroid Build Coastguard Worker  : >"${newDeps}"
182*333d2b36SAndroid Build Coastguard Workerfi
183*333d2b36SAndroid Build Coastguard Worker
184*333d2b36SAndroid Build Coastguard Worker# extend deps by appending targets from command-line
185*333d2b36SAndroid Build Coastguard Workerfor idx in "${!targets[*]}"; do
186*333d2b36SAndroid Build Coastguard Worker    echo "${targets[${idx}]} 0" >>"${newDeps}"
187*333d2b36SAndroid Build Coastguard Workerdone
188*333d2b36SAndroid Build Coastguard Worker
189*333d2b36SAndroid Build Coastguard Worker# remove duplicates and start with new, old and all the same
190*333d2b36SAndroid Build Coastguard Workersort -u <"${newDeps}" >"${allDeps}"
191*333d2b36SAndroid Build Coastguard Workercp "${allDeps}" "${newDeps}"
192*333d2b36SAndroid Build Coastguard Workercp "${allDeps}" "${oldDeps}"
193*333d2b36SAndroid Build Coastguard Worker
194*333d2b36SAndroid Build Coastguard Worker# report depth of dependenciens when showProgress
195*333d2b36SAndroid Build Coastguard Workerdepth=0
196*333d2b36SAndroid Build Coastguard Worker
197*333d2b36SAndroid Build Coastguard Workerwhile [ $(wc -l < "${newDeps}") -gt 0 ]; do
198*333d2b36SAndroid Build Coastguard Worker    if ${showProgress}; then
199*333d2b36SAndroid Build Coastguard Worker        echo "depth ${depth} has "$(wc -l < "${newDeps}")" targets" >&2
200*333d2b36SAndroid Build Coastguard Worker    fi
201*333d2b36SAndroid Build Coastguard Worker    depth=$(expr ${depth} + 1)
202*333d2b36SAndroid Build Coastguard Worker    ( # recalculate dependencies by combining unique inputs of new deps w. old
203*333d2b36SAndroid Build Coastguard Worker        cut -d\  -f1 "${newDeps}" | getDeps "${depth}"
204*333d2b36SAndroid Build Coastguard Worker        cat "${oldDeps}"
205*333d2b36SAndroid Build Coastguard Worker    ) | sort -n | awk '
206*333d2b36SAndroid Build Coastguard Worker      BEGIN {
207*333d2b36SAndroid Build Coastguard Worker        prev = ""
208*333d2b36SAndroid Build Coastguard Worker      }
209*333d2b36SAndroid Build Coastguard Worker      {
210*333d2b36SAndroid Build Coastguard Worker        depth = $NF
211*333d2b36SAndroid Build Coastguard Worker        $NF = ""
212*333d2b36SAndroid Build Coastguard Worker        gsub(/\s*$/, "")
213*333d2b36SAndroid Build Coastguard Worker        if ($0 != prev) {
214*333d2b36SAndroid Build Coastguard Worker          print gensub(/\s*$/, "", "g")" "depth
215*333d2b36SAndroid Build Coastguard Worker        }
216*333d2b36SAndroid Build Coastguard Worker        prev = $0
217*333d2b36SAndroid Build Coastguard Worker      }
218*333d2b36SAndroid Build Coastguard Worker    ' >"${allDeps}"
219*333d2b36SAndroid Build Coastguard Worker    # recalculate new dependencies as net additions to old dependencies
220*333d2b36SAndroid Build Coastguard Worker    set +e
221*333d2b36SAndroid Build Coastguard Worker    diff "${oldDeps}" "${allDeps}" --old-line-format='' \
222*333d2b36SAndroid Build Coastguard Worker      --new-line-format='%L' --unchanged-line-format='' > "${newDeps}"
223*333d2b36SAndroid Build Coastguard Worker    set -e
224*333d2b36SAndroid Build Coastguard Worker    # recalculate old dependencies for next iteration
225*333d2b36SAndroid Build Coastguard Worker    cp "${allDeps}" "${oldDeps}"
226*333d2b36SAndroid Build Coastguard Workerdone
227*333d2b36SAndroid Build Coastguard Worker
228*333d2b36SAndroid Build Coastguard Worker# found all deps -- clean up last iteration of old and new
229*333d2b36SAndroid Build Coastguard Workerrm -f "${oldDeps}"
230*333d2b36SAndroid Build Coastguard Workerrm -f "${newDeps}"
231*333d2b36SAndroid Build Coastguard Worker
232*333d2b36SAndroid Build Coastguard Workerif ${showProgress}; then
233*333d2b36SAndroid Build Coastguard Worker    echo $(wc -l < "${allDeps}")" targets" >&2
234*333d2b36SAndroid Build Coastguard Workerfi
235*333d2b36SAndroid Build Coastguard Worker
236*333d2b36SAndroid Build Coastguard Workerawk -v sep="${sep}" '{
237*333d2b36SAndroid Build Coastguard Worker  depth = $NF
238*333d2b36SAndroid Build Coastguard Worker  $NF = ""
239*333d2b36SAndroid Build Coastguard Worker  gsub(/\s*$/, "")
240*333d2b36SAndroid Build Coastguard Worker  print depth sep $0
241*333d2b36SAndroid Build Coastguard Worker}' "${allDeps}" | sort -n
242*333d2b36SAndroid Build Coastguard Worker
243*333d2b36SAndroid Build Coastguard Workerif ${interactive}; then
244*333d2b36SAndroid Build Coastguard Worker    echo -n "$(date '+%F %-k:%M:%S') Delete ${tmpFiles} ? [n] " >&2
245*333d2b36SAndroid Build Coastguard Worker    read answer
246*333d2b36SAndroid Build Coastguard Worker    case "${answer}" in [yY]*) rm -fr "${tmpFiles}";; esac
247*333d2b36SAndroid Build Coastguard Workerelse
248*333d2b36SAndroid Build Coastguard Worker    rm -fr "${tmpFiles}"
249*333d2b36SAndroid Build Coastguard Workerfi
250