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