xref: /openbsd/gnu/llvm/llvm/utils/TableGen/tdtags (revision 09467b48)
1*09467b48Spatrick#!/bin/sh
2*09467b48Spatrick#===-- tdtags - TableGen tags wrapper ---------------------------*- sh -*-===#
3*09467b48Spatrick# vim:set sts=2 sw=2 et:
4*09467b48Spatrick#===----------------------------------------------------------------------===#
5*09467b48Spatrick#
6*09467b48Spatrick# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
7*09467b48Spatrick# See https://llvm.org/LICENSE.txt for license information.
8*09467b48Spatrick# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
9*09467b48Spatrick#
10*09467b48Spatrick#===----------------------------------------------------------------------===#
11*09467b48Spatrick#
12*09467b48Spatrick# This is a wrapper script to simplify generating ctags(1)-compatible index
13*09467b48Spatrick# files for target .td files. Run tdtags -H for more documentation.
14*09467b48Spatrick#
15*09467b48Spatrick# For portability, this script is intended to conform to IEEE Std 1003.1-2008.
16*09467b48Spatrick#
17*09467b48Spatrick#===----------------------------------------------------------------------===#
18*09467b48Spatrick
19*09467b48SpatrickSELF=${0##*/}
20*09467b48Spatrick
21*09467b48Spatrickusage() {
22*09467b48Spatrickcat <<END
23*09467b48SpatrickUsage: $SELF [ <options> ] tdfile
24*09467b48Spatrick   or: $SELF [ <options> ] -x recipe [arg ...]
25*09467b48SpatrickOPTIONS
26*09467b48Spatrick  -H          Display further help.
27*09467b48Spatrick  -a          Append the tags to an existing tags file.
28*09467b48Spatrick  -f <file>   Write tags to the specified file (defaults to 'tags').
29*09467b48Spatrick  -I <dir>    Add the directory to the search path for tblgen include files.
30*09467b48Spatrick  -x <recipe> Generate tags file(s) for a common use case:
31*09467b48Spatrick  -q          Suppress $TBLGEN error messages.
32*09467b48Spatrick  -v          Be verbose; report progress.
33*09467b48SpatrickEND
34*09467b48Spatrick  usage_recipes
35*09467b48Spatrick}
36*09467b48Spatrick
37*09467b48Spatrickusage_recipes() {
38*09467b48Spatrickcat <<END
39*09467b48Spatrick     all      - Generate an index in each directory that contains .td files
40*09467b48Spatrick                in the LLVM source tree.
41*09467b48Spatrick     here     - Generate an index for all .td files in the current directory.
42*09467b48Spatrick     recurse  - Generate an index in each directory that contains .td files
43*09467b48Spatrick                in and under the current directory.
44*09467b48Spatrick     target [<target> ...]
45*09467b48Spatrick              - Generate a tags file for each specified LLVM code generator
46*09467b48Spatrick                target, or if none are specified, all targets.
47*09467b48SpatrickEND
48*09467b48Spatrick}
49*09467b48Spatrick
50*09467b48Spatrickhelp() {
51*09467b48Spatrickcat <<END
52*09467b48SpatrickNAME
53*09467b48Spatrick  $SELF - generate ctags(1)-compatible index files for tblgen .td source
54*09467b48Spatrick
55*09467b48SpatrickSYNOPSIS
56*09467b48Spatrick  $SELF [ options ] -x recipe [arg ...]
57*09467b48Spatrick  $SELF [ options ] [file ...]
58*09467b48Spatrick
59*09467b48SpatrickDESCRIPTION
60*09467b48Spatrick  With the '-x' option, $SELF produces one or more tags files for a
61*09467b48Spatrick  particular common use case. See the RECIPES section below for details.
62*09467b48Spatrick
63*09467b48Spatrick  Without the '-x' option, $SELF provides a ctags(1)-like interface to
64*09467b48Spatrick  $TBLGEN.
65*09467b48Spatrick
66*09467b48SpatrickOPTIONS
67*09467b48Spatrick  -a          Append newly generated tags to those already in an existing
68*09467b48Spatrick              tags file. Without ths option, any and all existing tags are
69*09467b48Spatrick              replaced. NOTE: When building a mixed tags file, using ${SELF}
70*09467b48Spatrick              for tblgen tags and ctags(1) for other languages, it is best
71*09467b48Spatrick              to run ${SELF} first without '-a', and ctags(1) second with '-a',
72*09467b48Spatrick              because ctags(1) handling is more capable.
73*09467b48Spatrick  -f <file>   Use the name <file> for the tags file, rather than the default
74*09467b48Spatrick              "tags". If the <file> is "-", then the tag index is written to
75*09467b48Spatrick              standard output.
76*09467b48Spatrick  -H          Display this document.
77*09467b48Spatrick  -I <dir>    Add the directory <dir> to the search path for 'include'
78*09467b48Spatrick              statements in tblgen source.
79*09467b48Spatrick  -x          Run a canned recipe, rather than operate on specified files.
80*09467b48Spatrick              When '-x' is present, the first non-option argument is the
81*09467b48Spatrick              name of a recipe, and any further arguments are arguments to
82*09467b48Spatrick              that recipe. With no arguments, lists the available recipes.
83*09467b48Spatrick  -q          Suppress $TBLGEN error messages. Not all .td files are well-
84*09467b48Spatrick              formed outside a specific context, so recipes will sometimes
85*09467b48Spatrick              produce error messages for certain .td files. These errors
86*09467b48Spatrick              do not affect the indices produced for valid files.
87*09467b48Spatrick  -v          Be verbose; report progress.
88*09467b48Spatrick
89*09467b48SpatrickRECIPES
90*09467b48Spatrick  $SELF -x all
91*09467b48Spatrick              Produce a tags file in every directory in the LLVM source tree
92*09467b48Spatrick              that contains any .td files.
93*09467b48Spatrick  $SELF -x here
94*09467b48Spatrick              Produce a tags file from .td files in the current directory.
95*09467b48Spatrick  $SELF -x recurse
96*09467b48Spatrick              Produce a tags file in every directory that contains any .td
97*09467b48Spatrick              files, in and under the current directory.
98*09467b48Spatrick  $SELF -x target [<target> ...]
99*09467b48Spatrick              Produce a tags file for each named code generator target, or
100*09467b48Spatrick              if none are named, for all code generator targets.
101*09467b48SpatrickEND
102*09467b48Spatrick}
103*09467b48Spatrick
104*09467b48Spatrick# Temporary file management.
105*09467b48Spatrick#
106*09467b48Spatrick# Since SUS sh(1) has no arrays, this script makes extensive use of
107*09467b48Spatrick# temporary files. The follow are 'global' and used to carry information
108*09467b48Spatrick# across functions:
109*09467b48Spatrick#   $TMP:D    Include directories.
110*09467b48Spatrick#   $TMP:I    Included files.
111*09467b48Spatrick#   $TMP:T    Top-level files, that are not included by another.
112*09467b48Spatrick#   $TMP:W    Directories in which to generate tags (Worklist).
113*09467b48Spatrick# For portability to OS X, names must not differ only in case.
114*09467b48Spatrick#
115*09467b48SpatrickTMP=${TMPDIR:-/tmp}/$SELF:$$
116*09467b48Spatricktrap "rm -f $TMP*" 0
117*09467b48Spatricktrap exit 1 2 13 15
118*09467b48Spatrick>$TMP:D
119*09467b48Spatrick
120*09467b48Spatricktd_dump()
121*09467b48Spatrick{
122*09467b48Spatrick  if [ $OPT_VERBOSE -gt 1 ]
123*09467b48Spatrick  then
124*09467b48Spatrick    printf '===== %s =====\n' "$1"
125*09467b48Spatrick    cat <"$1"
126*09467b48Spatrick  fi
127*09467b48Spatrick}
128*09467b48Spatrick
129*09467b48Spatrick# Escape the arguments, taken as a whole.
130*09467b48Spatricke() {
131*09467b48Spatrick  printf '%s' "$*" |
132*09467b48Spatrick    sed -e "s/'/'\\\\''/g" -e "1s/^/'/" -e "\$s/\$/'/"
133*09467b48Spatrick}
134*09467b48Spatrick
135*09467b48Spatrick# Determine whether the given directory contains at least one .td file.
136*09467b48Spatrickdir_has_td() {
137*09467b48Spatrick  for i in $1/*.td
138*09467b48Spatrick  do
139*09467b48Spatrick    [ -f "$i" ] && return 0
140*09467b48Spatrick  done
141*09467b48Spatrick  return 1
142*09467b48Spatrick}
143*09467b48Spatrick
144*09467b48Spatrick# Partition the supplied list of files, plus any files included from them,
145*09467b48Spatrick# into two groups:
146*09467b48Spatrick#   $TMP:T    Top-level files, that are not included by another.
147*09467b48Spatrick#   $TMP:I    Included files.
148*09467b48Spatrick# Add standard directories to the include paths in $TMP:D if this would
149*09467b48Spatrick# benefit the any of the included files.
150*09467b48Spatricktd_prep() {
151*09467b48Spatrick  >$TMP:E
152*09467b48Spatrick  >$TMP:J
153*09467b48Spatrick  for i in *.td
154*09467b48Spatrick  do
155*09467b48Spatrick    [ "x$i" = 'x*.td' ] && return 1
156*09467b48Spatrick    if [ -f "$i" ]
157*09467b48Spatrick    then
158*09467b48Spatrick      printf '%s\n' "$i" >>$TMP:E
159*09467b48Spatrick      sed -n -e 's/include[[:space:]]"\(.*\)".*/\1/p' <"$i" >>$TMP:J
160*09467b48Spatrick    else
161*09467b48Spatrick      printf >&2 '%s: "%s" not found.\n' "$SELF" "$i"
162*09467b48Spatrick      exit 7
163*09467b48Spatrick    fi
164*09467b48Spatrick  done
165*09467b48Spatrick  sort -u <$TMP:E >$TMP:X
166*09467b48Spatrick  sort -u <$TMP:J >$TMP:I
167*09467b48Spatrick  # A file that exists but is not included is toplevel.
168*09467b48Spatrick  comm -23 $TMP:X $TMP:I >$TMP:T
169*09467b48Spatrick  td_dump $TMP:T
170*09467b48Spatrick  td_dump $TMP:I
171*09467b48Spatrick  # Check include files.
172*09467b48Spatrick  while read i
173*09467b48Spatrick  do
174*09467b48Spatrick    [ -f "$i" ] && continue
175*09467b48Spatrick    while read d
176*09467b48Spatrick    do
177*09467b48Spatrick      [ -f "$d/$i" ] && break
178*09467b48Spatrick    done <$TMP:D
179*09467b48Spatrick    if [ -z "$d" ]
180*09467b48Spatrick    then
181*09467b48Spatrick      # See whether this include file can be found in a common location.
182*09467b48Spatrick      for d in $LLVM_SRC_ROOT/include \
183*09467b48Spatrick               $LLVM_SRC_ROOT/tools/clang/include
184*09467b48Spatrick      do
185*09467b48Spatrick        if [ -f "$d/$i" ]
186*09467b48Spatrick        then
187*09467b48Spatrick          printf '%s\n' "$d" >>$TMP:D
188*09467b48Spatrick          break
189*09467b48Spatrick        fi
190*09467b48Spatrick      done
191*09467b48Spatrick    fi
192*09467b48Spatrick  done <$TMP:I
193*09467b48Spatrick  td_dump $TMP:D
194*09467b48Spatrick}
195*09467b48Spatrick
196*09467b48Spatrick# Generate tags for the list of files in $TMP:T.
197*09467b48Spatricktd_tag() {
198*09467b48Spatrick  # Collect include directories.
199*09467b48Spatrick  inc=
200*09467b48Spatrick  while read d
201*09467b48Spatrick  do
202*09467b48Spatrick    inc="${inc}${inc:+ }$(e "-I=$d")"
203*09467b48Spatrick  done <$TMP:D
204*09467b48Spatrick
205*09467b48Spatrick  if [ $OPT_VERBOSE -ne 0 ]
206*09467b48Spatrick  then
207*09467b48Spatrick    printf >&2 'In "%s",\n' "$PWD"
208*09467b48Spatrick  fi
209*09467b48Spatrick
210*09467b48Spatrick  # Generate tags for each file.
211*09467b48Spatrick  n=0
212*09467b48Spatrick  while read i
213*09467b48Spatrick  do
214*09467b48Spatrick    if [ $OPT_VERBOSE -ne 0 ]
215*09467b48Spatrick    then
216*09467b48Spatrick      printf >&2 '  generating tags from "%s"\n' "$i"
217*09467b48Spatrick    fi
218*09467b48Spatrick    n=$((n + 1))
219*09467b48Spatrick    t=$(printf '%s:A:%05u' "$TMP" $n)
220*09467b48Spatrick    eval $TBLGEN --gen-ctags $inc "$i" >$t 2>$TMP:F
221*09467b48Spatrick    [ $OPT_NOTBLGENERR -eq 1 ] || cat $TMP:F
222*09467b48Spatrick  done <$TMP:T
223*09467b48Spatrick
224*09467b48Spatrick  # Add existing tags if requested.
225*09467b48Spatrick  if [ $OPT_APPEND -eq 1 -a -f "$OPT_TAGSFILE" ]
226*09467b48Spatrick  then
227*09467b48Spatrick    if [ $OPT_VERBOSE -ne 0 ]
228*09467b48Spatrick    then
229*09467b48Spatrick      printf >&2 '  and existing tags from "%s"\n' "$OPT_TAGSFILE"
230*09467b48Spatrick    fi
231*09467b48Spatrick    n=$((n + 1))
232*09467b48Spatrick    t=$(printf '%s:A:%05u' "$TMP" $n)
233*09467b48Spatrick    sed -e '/^!_TAG_/d' <"$OPT_TAGSFILE" | sort -u >$t
234*09467b48Spatrick  fi
235*09467b48Spatrick
236*09467b48Spatrick  # Merge tags.
237*09467b48Spatrick  if [ $n = 1 ]
238*09467b48Spatrick  then
239*09467b48Spatrick    mv -f "$t" $TMP:M
240*09467b48Spatrick  else
241*09467b48Spatrick    sort -m -u $TMP:A:* >$TMP:M
242*09467b48Spatrick  fi
243*09467b48Spatrick
244*09467b48Spatrick  # Emit tags.
245*09467b48Spatrick  if [ x${OPT_TAGSFILE}x = x-x ]
246*09467b48Spatrick  then
247*09467b48Spatrick    cat $TMP:M
248*09467b48Spatrick  else
249*09467b48Spatrick    if [ $OPT_VERBOSE -ne 0 ]
250*09467b48Spatrick    then
251*09467b48Spatrick      printf >&2 '  into "%s".\n' "$OPT_TAGSFILE"
252*09467b48Spatrick    fi
253*09467b48Spatrick    mv -f $TMP:M "$OPT_TAGSFILE"
254*09467b48Spatrick  fi
255*09467b48Spatrick}
256*09467b48Spatrick
257*09467b48Spatrick# Generate tags for the current directory.
258*09467b48Spatricktd_here() {
259*09467b48Spatrick  td_prep
260*09467b48Spatrick  [ -s $TMP:T ] || return 1
261*09467b48Spatrick  td_tag
262*09467b48Spatrick}
263*09467b48Spatrick
264*09467b48Spatrick# Generate tags for the current directory, and report an error if there are
265*09467b48Spatrick# no .td files present.
266*09467b48Spatrickdo_here()
267*09467b48Spatrick{
268*09467b48Spatrick  if ! td_here
269*09467b48Spatrick  then
270*09467b48Spatrick    printf >&2 '%s: Nothing to do here.\n' "$SELF"
271*09467b48Spatrick    exit 1
272*09467b48Spatrick  fi
273*09467b48Spatrick}
274*09467b48Spatrick
275*09467b48Spatrick# Generate tags for all .td files under the current directory.
276*09467b48Spatrickdo_recurse()
277*09467b48Spatrick{
278*09467b48Spatrick  td_find "$PWD"
279*09467b48Spatrick  td_dirs
280*09467b48Spatrick}
281*09467b48Spatrick
282*09467b48Spatrick# Generate tags for all .td files in LLVM.
283*09467b48Spatrickdo_all()
284*09467b48Spatrick{
285*09467b48Spatrick  td_find "$LLVM_SRC_ROOT"
286*09467b48Spatrick  td_dirs
287*09467b48Spatrick}
288*09467b48Spatrick
289*09467b48Spatrick# Generate tags for each directory in the worklist $TMP:W.
290*09467b48Spatricktd_dirs()
291*09467b48Spatrick{
292*09467b48Spatrick  while read d
293*09467b48Spatrick  do
294*09467b48Spatrick    (cd "$d" && td_here)
295*09467b48Spatrick  done <$TMP:W
296*09467b48Spatrick}
297*09467b48Spatrick
298*09467b48Spatrick# Find directories containing .td files within the specified directory,
299*09467b48Spatrick# and record them in the worklist $TMP:W.
300*09467b48Spatricktd_find()
301*09467b48Spatrick{
302*09467b48Spatrick  find -L "$1" -type f -name '*.td' |
303*09467b48Spatrick    sed -e 's:/[^/]*$::' |
304*09467b48Spatrick    sort -u >$TMP:W
305*09467b48Spatrick  td_dump $TMP:W
306*09467b48Spatrick}
307*09467b48Spatrick
308*09467b48Spatrick# Generate tags for the specified code generator targets, or
309*09467b48Spatrick# if there are no arguments, all targets.
310*09467b48Spatrickdo_targets() {
311*09467b48Spatrick  cd $LLVM_SRC_ROOT/lib/Target
312*09467b48Spatrick  if [ -z "$*" ]
313*09467b48Spatrick  then
314*09467b48Spatrick    td_find "$PWD"
315*09467b48Spatrick  else
316*09467b48Spatrick    # Check that every specified argument is a target directory;
317*09467b48Spatrick    # if not, list all target directories.
318*09467b48Spatrick    for d
319*09467b48Spatrick    do
320*09467b48Spatrick      if [ -d "$d" ] && dir_has_td "$d"
321*09467b48Spatrick      then
322*09467b48Spatrick        printf '%s/%s\n' "$PWD" "$d"
323*09467b48Spatrick      else
324*09467b48Spatrick        printf >&2 '%s: "%s" is not a target. Targets are:\n' "$SELF" "$d"
325*09467b48Spatrick        for d in *
326*09467b48Spatrick        do
327*09467b48Spatrick          [ -d "$d" ] || continue
328*09467b48Spatrick          dir_has_td "$d" && printf >&2 '  %s\n' "$d"
329*09467b48Spatrick        done
330*09467b48Spatrick        exit 2
331*09467b48Spatrick      fi
332*09467b48Spatrick    done >$TMP:W
333*09467b48Spatrick  fi
334*09467b48Spatrick  td_dirs
335*09467b48Spatrick}
336*09467b48Spatrick
337*09467b48Spatrick# Change to the directory at the top of the enclosing LLVM source tree,
338*09467b48Spatrick# if possible.
339*09467b48Spatrickllvm_src_root() {
340*09467b48Spatrick  while [ "$PWD" != / ]
341*09467b48Spatrick  do
342*09467b48Spatrick    # Use this directory if multiple notable subdirectories are present.
343*09467b48Spatrick    [ -d include/llvm -a -d lib/Target ] && return 0
344*09467b48Spatrick    cd ..
345*09467b48Spatrick  done
346*09467b48Spatrick  return 1
347*09467b48Spatrick}
348*09467b48Spatrick
349*09467b48Spatrick# Ensure sort(1) behaves consistently.
350*09467b48SpatrickLC_ALL=C
351*09467b48Spatrickexport LC_ALL
352*09467b48Spatrick
353*09467b48Spatrick# Globals.
354*09467b48SpatrickTBLGEN=llvm-tblgen
355*09467b48SpatrickLLVM_SRC_ROOT=
356*09467b48Spatrick
357*09467b48Spatrick# Command options.
358*09467b48SpatrickOPT_TAGSFILE=tags
359*09467b48SpatrickOPT_RECIPES=0
360*09467b48SpatrickOPT_APPEND=0
361*09467b48SpatrickOPT_VERBOSE=0
362*09467b48SpatrickOPT_NOTBLGENERR=0
363*09467b48Spatrick
364*09467b48Spatrickwhile getopts 'af:hxqvHI:' opt
365*09467b48Spatrickdo
366*09467b48Spatrick  case $opt in
367*09467b48Spatrick  a)
368*09467b48Spatrick    OPT_APPEND=1
369*09467b48Spatrick    ;;
370*09467b48Spatrick  f)
371*09467b48Spatrick    OPT_TAGSFILE="$OPTARG"
372*09467b48Spatrick    ;;
373*09467b48Spatrick  x)
374*09467b48Spatrick    OPT_RECIPES=1
375*09467b48Spatrick    ;;
376*09467b48Spatrick  q)
377*09467b48Spatrick    OPT_NOTBLGENERR=1
378*09467b48Spatrick    ;;
379*09467b48Spatrick  v)
380*09467b48Spatrick    OPT_VERBOSE=$((OPT_VERBOSE + 1))
381*09467b48Spatrick    ;;
382*09467b48Spatrick  I)
383*09467b48Spatrick    printf '%s\n' "$OPTARG" >>$TMP:D
384*09467b48Spatrick    ;;
385*09467b48Spatrick  [hH])
386*09467b48Spatrick    help
387*09467b48Spatrick    exit 0
388*09467b48Spatrick    ;;
389*09467b48Spatrick  *)
390*09467b48Spatrick    usage >&2
391*09467b48Spatrick    exit 4
392*09467b48Spatrick    ;;
393*09467b48Spatrick  esac
394*09467b48Spatrickdone
395*09467b48Spatrickshift $((OPTIND - 1))
396*09467b48Spatrick
397*09467b48Spatrick# Handle the case where tdtags is a simple ctags(1)-like wrapper for tblgen.
398*09467b48Spatrickif [ $OPT_RECIPES -eq 0 ]
399*09467b48Spatrickthen
400*09467b48Spatrick  if [ -z "$*" ]
401*09467b48Spatrick  then
402*09467b48Spatrick    help >&2
403*09467b48Spatrick    exit 5
404*09467b48Spatrick  fi
405*09467b48Spatrick  for i
406*09467b48Spatrick  do
407*09467b48Spatrick    printf '%s\n' "$i"
408*09467b48Spatrick  done >$TMP:T
409*09467b48Spatrick  td_tag
410*09467b48Spatrick  exit $?
411*09467b48Spatrickfi
412*09467b48Spatrick
413*09467b48Spatrick# Find the directory at the top of the enclosing LLVM source tree.
414*09467b48Spatrickif ! LLVM_SRC_ROOT=$(llvm_src_root && pwd)
415*09467b48Spatrickthen
416*09467b48Spatrick  printf >&2 '%s: Run from within the LLVM source tree.\n' "$SELF"
417*09467b48Spatrick  exit 3
418*09467b48Spatrickfi
419*09467b48Spatrick
420*09467b48Spatrick# Select canned actions.
421*09467b48SpatrickRECIPE="$1"
422*09467b48Spatrickcase "$RECIPE" in
423*09467b48Spatrickall)
424*09467b48Spatrick  shift
425*09467b48Spatrick  do_all
426*09467b48Spatrick  ;;
427*09467b48Spatrick.|cwd|here)
428*09467b48Spatrick  shift
429*09467b48Spatrick  do_here
430*09467b48Spatrick  ;;
431*09467b48Spatrickrecurse)
432*09467b48Spatrick  shift
433*09467b48Spatrick  do_recurse
434*09467b48Spatrick  ;;
435*09467b48Spatricktarget)
436*09467b48Spatrick  shift
437*09467b48Spatrick  do_targets "$@"
438*09467b48Spatrick  ;;
439*09467b48Spatrick*)
440*09467b48Spatrick  if [ -n "$RECIPE" ]
441*09467b48Spatrick  then
442*09467b48Spatrick    shift
443*09467b48Spatrick    printf >&2 '%s: Unknown recipe "-x %s". ' "$SELF" "$RECIPE"
444*09467b48Spatrick  fi
445*09467b48Spatrick  printf >&2 'Recipes:\n'
446*09467b48Spatrick  usage_recipes >&2
447*09467b48Spatrick  printf >&2 'Run "%s -H" for help.\n' "$SELF"
448*09467b48Spatrick  exit 6
449*09467b48Spatrick  ;;
450*09467b48Spatrickesac
451*09467b48Spatrick
452*09467b48Spatrickexit $?
453