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