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