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 20SELF=${0##*/} 21 22usage() { 23cat <<END 24Usage: $SELF [ <options> ] tdfile 25 or: $SELF [ <options> ] -x recipe [arg ...] 26OPTIONS 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. 34END 35 usage_recipes 36} 37 38usage_recipes() { 39cat <<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. 48END 49} 50 51help() { 52cat <<END 53NAME 54 $SELF - generate ctags(1)-compatible index files for tblgen .td source 55 56SYNOPSIS 57 $SELF [ options ] -x recipe [arg ...] 58 $SELF [ options ] [file ...] 59 60DESCRIPTION 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 67OPTIONS 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 90RECIPES 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. 102END 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# 116TMP=${TMPDIR:-/tmp}/$SELF:$$ 117trap "rm -f $TMP*" 0 118trap exit 1 2 13 15 119>$TMP:D 120 121td_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. 131e() { 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. 137dir_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. 151td_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. 198td_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. 259td_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. 267do_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. 277do_recurse() 278{ 279 td_find "$PWD" 280 td_dirs 281} 282 283# Generate tags for all .td files in LLVM. 284do_all() 285{ 286 td_find "$LLVM_SRC_ROOT" 287 td_dirs 288} 289 290# Generate tags for each directory in the worklist $TMP:W. 291td_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. 301td_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. 311do_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. 340llvm_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. 351LC_ALL=C 352export LC_ALL 353 354# Globals. 355TBLGEN=llvm-tblgen 356LLVM_SRC_ROOT= 357 358# Command options. 359OPT_TAGSFILE=tags 360OPT_RECIPES=0 361OPT_APPEND=0 362OPT_VERBOSE=0 363OPT_NOTBLGENERR=0 364 365while getopts 'af:hxqvHI:' opt 366do 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 395done 396shift $((OPTIND - 1)) 397 398# Handle the case where tdtags is a simple ctags(1)-like wrapper for tblgen. 399if [ $OPT_RECIPES -eq 0 ] 400then 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 $? 412fi 413 414# Find the directory at the top of the enclosing LLVM source tree. 415if ! LLVM_SRC_ROOT=$(llvm_src_root && pwd) 416then 417 printf >&2 '%s: Run from within the LLVM source tree.\n' "$SELF" 418 exit 3 419fi 420 421# Select canned actions. 422RECIPE="$1" 423case "$RECIPE" in 424all) 425 shift 426 do_all 427 ;; 428.|cwd|here) 429 shift 430 do_here 431 ;; 432recurse) 433 shift 434 do_recurse 435 ;; 436target) 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 ;; 451esac 452 453exit $? 454