blob: 8df7d2d369345b31b7f3d546ae7357dedd09a5d9 [file] [log] [blame]
Bob Badour20fc1b32020-07-29 13:40:16 -07001#!/bin/bash
2
3set -u
4
5ME=$(basename $0)
6
7USAGE="Usage: ${ME} {options}
8
9Builds a license metadata specification and outputs it to stdout or {outfile}.
10
11The available options are:
12
13-k kind... license kinds
14-c condition... license conditions
15-p package... license package name
16-n notice... license notice file
17-d dependency... license metadata file dependency
18-t target... targets
19-m target:installed... map dependent targets to their installed names
20-is_container preserved dependent target name when given
21-o outfile output file
22"
23
24# Global flag variables
25license_kinds=
26license_conditions=
27license_package_name=
28license_notice=
29license_deps=
30targets=
31installmap=
32is_container=false
33ofile=
34
35
36# Global variables
37declare -A depfiles
38effective_conditions=
39declare -A depModules
40
41# work around bug where "${#array[@]}" traps if never set
42depfiles["x"]=808 # set an arbitrary index
43unset depfiles["x"] # delete it to make depfiles empty again
44depModules["x"]=808 # set an arbitrary index
45unset depModules["x"] # delete it to make depModules empty again
46
47
48# Exits with a message.
49#
50# When the exit status is 2, assumes a usage error and outputs the usage message
51# to stderr before outputting the specific error message to stderr.
52#
53# Parameters:
54# Optional numeric exit status (defaults to 2, i.e. a usage error.)
55# Remaining args treated as an error message sent to stderr.
56function die() {
57 local status=2
58 case "${1:-}" in *[^0-9]*) ;; *) status="$1"; shift ;; esac
59 case "${status}" in 2) echo "${USAGE}" >&2; echo >&2 ;; esac
60 if [ -n "$*" ]; then
61 echo -e "$*\n" >&2
62 fi
63 exit $status
64}
65
66
67# Sets the flag variables based on the command-line.
68#
69# invoke with: process_args "$@"
70function process_args() {
71 local curr_flag=
72 local name
73 local val
74 while [ "$#" -gt '0' ]; do
75 case "${1}" in
76 -h)
77 echo "${USAGE}"
78 exit 0
79 ;;
80 -k)
81 curr_flag=kind
82 ;;
83 -c)
84 curr_flag=condition
85 ;;
86 -p)
87 curr_flag=package
88 ;;
89 -n)
90 curr_flag=notice
91 ;;
92 -d)
93 curr_flag=dependency
94 ;;
95 -t)
96 curr_flag=target
97 ;;
98 -m)
99 curr_flag=installmap
100 ;;
101 -o)
102 curr_flag=ofile
103 ;;
104 -is_container)
105 curr_flag=
106 is_container=true
107 ;;
108 -*)
109 die "Unknown flag: \"${1}\""
110 ;;
111 *)
112 case "${curr_flag}" in
113 kind)
114 license_kinds="${license_kinds}${license_kinds:+ }${1}"
115 ;;
116 condition)
117 license_conditions="${license_conditions}${license_conditions:+ }${1}"
118 ;;
119 package)
120 license_package_name="${license_package_name}${license_package_name:+ }${1}"
121 ;;
122 notice)
123 license_notice="${license_notice}${license_notice:+ }${1}"
124 ;;
125 dependency)
126 license_deps="${license_deps}${license_deps:+ }${1}"
127 ;;
128 target)
129 targets="${targets}${targets:+ }${1}"
130 ;;
131 installmap)
132 installmap="${installmap}${installmap:+ }${1}"
133 ;;
134 ofile)
135 if [ -n "${ofile}" ]; then
136 die "Output file -o appears twice as \"${ofile}\" and \"${1}\""
137 fi
138 ofile="${1}"
139 ;;
140 *)
141 die "Must precede argument \"${1}\" with type flag."
142 ;;
143 esac
144 ;;
145 esac
146 shift
147 done
148}
149
150# Reads a license metadata file from stdin, and outputs the named dependencies.
151#
152# No parameters.
153function extract_deps() {
154 awk '$1 == "dep_name:" { sub(/^"/, "", $2); sub(/"$/, "", $2); print $2; }'
155}
156
157# Populates the `depfiles` associative array mapping dependencies to license
158# metadata content.
159#
160# Starting with the dependencies enumerated in `license_deps`, calculates the
161# transitive closure of all dependencies mapping the name of each license
162# metadata file to its content.
163#
164# Dependency names ending in `.meta_module` indirectly reference license
165# metadata with 1 license metadata filename per line.
166#
167# No parameters; no output.
168function read_deps() {
169 local newdeps=$(
170 for d in ${license_deps}; do
171 case "${d}" in
172 *.meta_module) cat "${d}" ;;
173 *) echo "${d}" ;;
174 esac
175 done | sort -u
176 )
177 local alldeps=
178 local deps=
179 local content=
180 local mod=
181 local dep=
182 while [ "${#newdeps}" -gt '0' ]; do
183 deps="${newdeps}"
184 newdeps=
185 for dep in ${deps}; do
186 content=$(cat ${dep})
187 depfiles["${dep}"]="${content}"
188 alldeps="${alldeps}${alldeps:+ }"$(echo "${content}" | extract_deps)
189 done
190 alldeps=$(for d in ${alldeps}; do echo "${d}"; done | sort -u)
191 for d in ${alldeps}; do
192 deps=$(
193 case "${d}" in
194 *.meta_module) cat"${d}" ;;
195 *) echo "${d}" ;;
196 esac
197 )
198 for mod in ${deps}; do
199 if [ -z "${depfiles[${mod}]+isset}" ]; then
200 newdeps="${newdeps}${newdeps:+ }${mod}"
201 fi
202 done
203 done
204 alldeps=
205 done
206}
207
208# Returns the effective license conditions for the current license metadata.
209#
210# If a module is restricted or links in a restricted module, the effective
211# license has a restricted condition.
212function calculate_effective_conditions() {
213 local conditions="${license_conditions}"
214 local condition
215 case "${license_conditions}" in
216 *restricted*) : do nothing ;;
217 *)
218 for d in "${!depfiles[@]}"; do
219 condition=$(
220 echo "${depfiles[${d}]}" | \
221 awk '$1 == "effective_condition:" {
222 $1 = ""
223 print
224 }' \
225 )
226 case "${condition}" in
227 *restricted*)
228 conditions="${conditions}${conditions:+ }restricted"
229 break
230 ;;
231 esac
232 done
233 ;;
234 esac
235 echo "${conditions}"
236}
237
238
239process_args "$@"
240
241if [ -n "${ofile}" ]; then
242 # truncate the output file before appending results
243 : >"${ofile}"
244else
245 ofile=/dev/stdout
246fi
247
248# spit out the license metadata file content
249(
250 echo 'license_package_name: "'${license_package_name}'"'
251 for kind in ${license_kinds}; do
252 echo 'license_kind: "'${kind}'"'
253 done
254 for condition in ${license_conditions}; do
255 echo 'license_condition: "'${condition}'"'
256 done
257 for f in ${license_notice}; do
258 echo 'license_text: "'${f}'"'
259 done
260 echo "is_container: ${is_container}"
261 for t in ${targets}; do
262 echo 'target: "'${t}'"'
263 done
264 for m in ${installmap}; do
265 echo 'install_map: "'${m}'"'
266 done
267) >>"${ofile}"
268read_deps
269for dep in "${!depfiles[@]}"; do
270 m=$(expr "${dep}" : '^.*[/]\([^/]*\)[.]meta_lic$')
271 depModules["${m}"]=true
272done
273effective_conditions=$(calculate_effective_conditions)
274for condition in ${effective_conditions}; do
275 echo 'effective_condition: "'${condition}'"'
276done >>"${ofile}"
277for dep in "${!depfiles[@]}"; do
278 echo 'dep {'
279 echo "${depfiles[${dep}]}" | \
280 awk -v name="${dep}" '
281 function strip_type() {
282 $1 = ""
283 sub(/^\s*/, "")
284 }
285 BEGIN {
286 print " dep_name: " name
287 }
288 $1 == "license_package_name:" {
289 strip_type()
290 print " dep_package_name: "$0
291 }
292 $1 == "dep_name:" {
293 print " dep_sub_dep: "$2
294 }
295 $1 == "license_kind:" {
296 print " dep_license_kind: "$2
297 }
298 $1 == "license_condition:" {
299 print " dep_license_condition: "$2
300 }
301 $1 == "is_container:" {
302 print " dep_is_container: "$2
303 }
304 $1 == "license_text:" {
305 strip_type()
306 print " dep_license_text: "$0
307 }
308 $1 == "target:" {
309 print " dep_target: "$2
310 }
311 $1 == "install_map:" {
312 print " dep_install_map: "$2
313 }
314 '
315 # The restricted license kind is contagious to all linked dependencies.
316 dep_conditions=$(echo $(
317 echo "${depfiles[${dep}]}" | awk '
318 $1 == "effective_condition:" {
319 $1 = ""
320 sub(/^\s*/, "")
321 gsub(/"/, "")
322 print
323 }
324 '
325 ))
326 for condition in ${dep_conditions}; do
327 echo ' dep_effective_condition: "'${condition}'"'
328 done
329 if ! ${is_container}; then
330 case "${dep_conditions}" in
331 *restricted*) : already restricted -- nothing to inherit ;;
332 *)
333 case "${effective_conditions}" in
334 *restricted*)
335 # "contagious" restricted infects everything linked to restricted
336 echo ' dep_effective_condition: "restricted"'
337 ;;
338 esac
339 ;;
340 esac
341 fi
342 echo '}'
343done >>"${ofile}"