#!/bin/sh #===-- tdtags - TableGen tags wrapper ---------------------------*- sh -*-===# # vim:set sts=2 sw=2 et: #===----------------------------------------------------------------------===# # # The LLVM Compiler Infrastructure # # This file is distributed under the University of Illinois Open Source # License. See LICENSE.TXT for details. # #===----------------------------------------------------------------------===# # # This is a wrapper script to simplify generating ctags(1)-compatible index # files for target .td files. Run tdtags -H for more documentation. # # For portability, this script is intended to conform to IEEE Std 1003.1-2008. # #===----------------------------------------------------------------------===# SELF=${0##*/} usage() { cat < ] tdfile or: $SELF [ ] -x recipe [arg ...] OPTIONS -H Display further help. -a Append the tags to an existing tags file. -f Write tags to the specified file (defaults to 'tags'). -I Add the directory to the search path for tblgen include files. -x Generate tags file(s) for a common use case: -q Suppress $TBLGEN error messages. -v Be verbose; report progress. END usage_recipes } usage_recipes() { cat < ...] - Generate a tags file for each specified LLVM code generator target, or if none are specified, all targets. END } help() { cat < Use the name for the tags file, rather than the default "tags". If the is "-", then the tag index is written to standard output. -H Display this document. -I Add the directory to the search path for 'include' statements in tblgen source. -x Run a canned recipe, rather than operate on specified files. When '-x' is present, the first non-option argument is the name of a recipe, and any further arguments are arguments to that recipe. With no arguments, lists the available recipes. -q Suppress $TBLGEN error messages. Not all .td files are well- formed outside a specific context, so recipes will sometimes produce error messages for certain .td files. These errors do not affect the indices produced for valid files. -v Be verbose; report progress. RECIPES $SELF -x all Produce a tags file in every directory in the LLVM source tree that contains any .td files. $SELF -x here Produce a tags file from .td files in the current directory. $SELF -x recurse Produce a tags file in every directory that contains any .td files, in and under the current directory. $SELF -x target [ ...] Produce a tags file for each named code generator target, or if none are named, for all code generator targets. END } # Temporary file management. # # Since SUS sh(1) has no arrays, this script makes extensive use of # temporary files. The follow are 'global' and used to carry information # across functions: # $TMP:D Include directories. # $TMP:I Included files. # $TMP:T Top-level files, that are not included by another. # $TMP:W Directories in which to generate tags (Worklist). # For portability to OS X, names must not differ only in case. # TMP=${TMPDIR:-/tmp}/$SELF:$$ trap "rm -f $TMP*" 0 trap exit 1 2 13 15 >$TMP:D td_dump() { if [ $OPT_VERBOSE -gt 1 ] then printf '===== %s =====\n' "$1" cat <"$1" fi } # Escape the arguments, taken as a whole. e() { printf '%s' "$*" | sed -e "s/'/'\\\\''/g" -e "1s/^/'/" -e "\$s/\$/'/" } # Determine whether the given directory contains at least one .td file. dir_has_td() { for i in $1/*.td do [ -f "$i" ] && return 0 done return 1 } # Partition the supplied list of files, plus any files included from them, # into two groups: # $TMP:T Top-level files, that are not included by another. # $TMP:I Included files. # Add standard directories to the include paths in $TMP:D if this would # benefit the any of the included files. td_prep() { >$TMP:E >$TMP:J for i in *.td do [ "x$i" = 'x*.td' ] && return 1 if [ -f "$i" ] then printf '%s\n' "$i" >>$TMP:E sed -n -e 's/include[[:space:]]"\(.*\)".*/\1/p' <"$i" >>$TMP:J else printf >&2 '%s: "%s" not found.\n' "$SELF" "$i" exit 7 fi done sort -u <$TMP:E >$TMP:X sort -u <$TMP:J >$TMP:I # A file that exists but is not included is toplevel. comm -23 $TMP:X $TMP:I >$TMP:T td_dump $TMP:T td_dump $TMP:I # Check include files. while read i do [ -f "$i" ] && continue while read d do [ -f "$d/$i" ] && break done <$TMP:D if [ -z "$d" ] then # See whether this include file can be found in a common location. for d in $LLVM_SRC_ROOT/include \ $LLVM_SRC_ROOT/tools/clang/include do if [ -f "$d/$i" ] then printf '%s\n' "$d" >>$TMP:D break fi done fi done <$TMP:I td_dump $TMP:D } # Generate tags for the list of files in $TMP:T. td_tag() { # Collect include directories. inc= while read d do inc="${inc}${inc:+ }$(e "-I=$d")" done <$TMP:D if [ $OPT_VERBOSE -ne 0 ] then printf >&2 'In "%s",\n' "$PWD" fi # Generate tags for each file. n=0 while read i do if [ $OPT_VERBOSE -ne 0 ] then printf >&2 ' generating tags from "%s"\n' "$i" fi n=$((n + 1)) t=$(printf '%s:A:%05u' "$TMP" $n) eval $TBLGEN --gen-ctags $inc "$i" >$t 2>$TMP:F [ $OPT_NOTBLGENERR -eq 1 ] || cat $TMP:F done <$TMP:T # Add existing tags if requested. if [ $OPT_APPEND -eq 1 -a -f "$OPT_TAGSFILE" ] then if [ $OPT_VERBOSE -ne 0 ] then printf >&2 ' and existing tags from "%s"\n' "$OPT_TAGSFILE" fi n=$((n + 1)) t=$(printf '%s:A:%05u' "$TMP" $n) sed -e '/^!_TAG_/d' <"$OPT_TAGSFILE" | sort -u >$t fi # Merge tags. if [ $n = 1 ] then mv -f "$t" $TMP:M else sort -m -u $TMP:A:* >$TMP:M fi # Emit tags. if [ x${OPT_TAGSFILE}x = x-x ] then cat $TMP:M else if [ $OPT_VERBOSE -ne 0 ] then printf >&2 ' into "%s".\n' "$OPT_TAGSFILE" fi mv -f $TMP:M "$OPT_TAGSFILE" fi } # Generate tags for the current directory. td_here() { td_prep [ -s $TMP:T ] || return 1 td_tag } # Generate tags for the current directory, and report an error if there are # no .td files present. do_here() { if ! td_here then printf >&2 '%s: Nothing to do here.\n' "$SELF" exit 1 fi } # Generate tags for all .td files under the current directory. do_recurse() { td_find "$PWD" td_dirs } # Generate tags for all .td files in LLVM. do_all() { td_find "$LLVM_SRC_ROOT" td_dirs } # Generate tags for each directory in the worklist $TMP:W. td_dirs() { while read d do (cd "$d" && td_here) done <$TMP:W } # Find directories containing .td files within the specified directory, # and record them in the worklist $TMP:W. td_find() { find -L "$1" -type f -name '*.td' | sed -e 's:/[^/]*$::' | sort -u >$TMP:W td_dump $TMP:W } # Generate tags for the specified code generator targets, or # if there are no arguments, all targets. do_targets() { cd $LLVM_SRC_ROOT/lib/Target if [ -z "$*" ] then td_find "$PWD" else # Check that every specified argument is a target directory; # if not, list all target directories. for d do if [ -d "$d" ] && dir_has_td "$d" then printf '%s/%s\n' "$PWD" "$d" else printf >&2 '%s: "%s" is not a target. Targets are:\n' "$SELF" "$d" for d in * do [ -d "$d" ] || continue dir_has_td "$d" && printf >&2 ' %s\n' "$d" done exit 2 fi done >$TMP:W fi td_dirs } # Change to the directory at the top of the enclosing LLVM source tree, # if possible. llvm_src_root() { while [ "$PWD" != / ] do # Use this directory if multiple notable subdirectories are present. [ -d include/llvm -a -d lib/Target ] && return 0 cd .. done return 1 } # Ensure sort(1) behaves consistently. LC_ALL=C export LC_ALL # Globals. TBLGEN=llvm-tblgen LLVM_SRC_ROOT= # Command options. OPT_TAGSFILE=tags OPT_RECIPES=0 OPT_APPEND=0 OPT_VERBOSE=0 OPT_NOTBLGENERR=0 while getopts 'af:hxqvHI:' opt do case $opt in a) OPT_APPEND=1 ;; f) OPT_TAGSFILE="$OPTARG" ;; x) OPT_RECIPES=1 ;; q) OPT_NOTBLGENERR=1 ;; v) OPT_VERBOSE=$((OPT_VERBOSE + 1)) ;; I) printf '%s\n' "$OPTARG" >>$TMP:D ;; [hH]) help exit 0 ;; *) usage >&2 exit 4 ;; esac done shift $((OPTIND - 1)) # Handle the case where tdtags is a simple ctags(1)-like wrapper for tblgen. if [ $OPT_RECIPES -eq 0 ] then if [ -z "$*" ] then help >&2 exit 5 fi for i do printf '%s\n' "$i" done >$TMP:T td_tag exit $? fi # Find the directory at the top of the enclosing LLVM source tree. if ! LLVM_SRC_ROOT=$(llvm_src_root && pwd) then printf >&2 '%s: Run from within the LLVM source tree.\n' "$SELF" exit 3 fi # Select canned actions. RECIPE="$1" case "$RECIPE" in all) shift do_all ;; .|cwd|here) shift do_here ;; recurse) shift do_recurse ;; target) shift do_targets "$@" ;; *) if [ -n "$RECIPE" ] then shift printf >&2 '%s: Unknown recipe "-x %s". ' "$SELF" "$RECIPE" fi printf >&2 'Recipes:\n' usage_recipes >&2 printf >&2 'Run "%s -H" for help.\n' "$SELF" exit 6 ;; esac exit $?