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