Line Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172
#!/bin/bash

# Deep-searches CMSSW config python files, and displays the tree structure of all imported files in it.
# K. Banicz

function findFileFor(){
    # Return 0 if a file given by absolute path exists, or it converts a python name 
    # to a file name and returns 0 if it finds it in CMSSW_SEARCH_PATH. 

    # argument 1: the sought file, or the python name to seek the corresponding file for

    local FILE_NAME
    if [ ${1:0:1} == "/" ]; then
	[ -f $1 ] && FOUND_FILE=$1 && return 0
    else
	FILE_NAME=$( echo $1 | sed -n -e 's:\([[:alnum:]_]\+\)[/.]\([[:alnum:]_]\+\)[/.]\([[:alnum:]_/.]\+\)$:\1/\2/python/\3:p' | sed -e 's:[\.]:/:g' ).py
	IFS=:
	for SEARCH_PATH in $CMSSW_SEARCH_PATH; do
	    [ -f $SEARCH_PATH/$FILE_NAME ] && FOUND_FILE=$SEARCH_PATH/$FILE_NAME && return 0
	done    
    fi


    # Not found.
    FOUND_FILE=""
    return 1
}

function findInFile(){
    # Finds all occurrences of the sought expression in the file.
    # Invokes itself on all included files too.
    # argument 1: indent
    # argument 2: the searched file
    # argument 3: the sought expression

    #
    # Load 'import'-s and 'load'-s into an array (discard FWCore.ParameterSet as it leads to an infinite loop)
    #
    # sed'd regex below will extract the word in capital from the following example lines ('...' means any string):
    #    import FINDS.THIS.WORD ...
    #    from FINDS.THIS.WORD ...
    #    execfile("FINDS/THIS/WORD")
    #    process.load("FINDS.THIS/WORD")
    # And the same things inside an 'exec' statement, too:
    #    exec 'import FINDS.THIS.WORD ...'
    #    exec "from FINDS.THIS.WORD ..."
    #    exec 'execfile("FINDS/THIS/WORD")'
    #    exec 'process.load("FINDS.THIS/WORD")'

    IFS="
"
    local -a IMPORTS_AND_LOADS_ARRAY
    IMPORTS_AND_LOADS_ARRAY=( $( sed -n -e "s:^[[:space:]]*\(exec[[:space:]]\+[\"']\)\?\(from[[:space:]]\+\|import[[:space:]]\+\|process\.load[[:space:]]*([[:space:]]*[\"']\|execfile[[:space:]]*([[:space:]]*[\"']\)[[:space:]]*\(\([^[:space:])\"'#]\+[/.]\)\+[^[:space:])\"'#]\+\).*:\3:p" $2 | grep -v "^FWCore.ParameterSet" ) )

    #
    # First list all occurrences in this file:
    #
    if [ ${#3} -ne 0 ]; then
	if [ $EDMCONFIGTREE_TREE == "false" ]; then
	    grep -H -n $3 $2
	else
	    grep $3 $2 | while read LINE_WITH_MATCH; do
		if [ $EDMCONFIGTREE_COLOR == "false" ]; then
		    [ ${#IMPORTS_AND_LOADS_ARRAY[@]} -gt 0 ] && echo -n "$1  |" || echo -n "$1  "
		    echo "     $LINE_WITH_MATCH" | grep $3
		else
		    [ ${#IMPORTS_AND_LOADS_ARRAY[@]} -gt 0 ] && echo -n "$1  |" || echo -n "$1  "
		    echo "     $LINE_WITH_MATCH" | grep --color $3
		fi
	    done
	fi
    fi

    #
    # Now find all 'import'-ed and 'process.load'-ed files and search them too
    #
    local I
    for (( I=0; I<${#IMPORTS_AND_LOADS_ARRAY[@]}; I++ )); do
	local PYTHON_NAME=${IMPORTS_AND_LOADS_ARRAY[$I]}
	local INDENT
	local II
	((II=I+1))
	if [ $EDMCONFIGTREE_TREE == "false" ]; then
	    if findFileFor "$PYTHON_NAME"; then
		[ $SHOW_ABSOLUTE_PATH == "true" ] && echo "$FOUND_FILE" || echo "$PYTHON_NAME"
		findInFile "" "$FOUND_FILE" "$3"
	    else
		echo "${PYTHON_NAME} *** not found in CMSSW_SEARCH_PATH ***"
	    fi
	else
	    if findFileFor "$PYTHON_NAME"; then
		[ $SHOW_ABSOLUTE_PATH == "true" ] && NAME_TO_SHOW=FOUND_FILE || NAME_TO_SHOW=PYTHON_NAME
		if [ $EDMCONFIGTREE_COLOR == "false" ]; then
		    echo "$1  |- ${!NAME_TO_SHOW}"
		    [ $II -ge ${#IMPORTS_AND_LOADS_ARRAY[@]} ] && INDENT="$1   " || INDENT="$1  |"
		else
		    [[ $SHOW_ABSOLUTE_PATH == "true" || ${PYTHON_NAME:0:1} == "/" ]] && echo "$1    ${FOUND_FILE%%*([^/])}${FOUND_FILE##*/}" || echo "$1    ${PYTHON_NAME%%*([^.])}${PYTHON_NAME##*.}"
		    [ $II -ge ${#IMPORTS_AND_LOADS_ARRAY[@]} ] && INDENT="$1   " || INDENT="$1  |"
		fi
		findInFile "$INDENT" "$FOUND_FILE" "$3"
	    else
		if [ $EDMCONFIGTREE_COLOR == "false" ]; then
		    echo "$1  |- ${PYTHON_NAME} *** not found in CMSSW_SEARCH_PATH ***"
		else
		    [ ${PYTHON_NAME:0:1} == "/" ] && echo "$1    ${PYTHON_NAME%%*([^/])}${PYTHON_NAME##*/}" || echo -n "$1    ${PYTHON_NAME%%*([^.])}${PYTHON_NAME##*.} "
		    echo " not found in CMSSW_SEARCH_PATH "
		fi
	    fi
	fi
    done

}

# Take care of the relevant shell variables.
${EDMCONFIGTREE_COLOR:=true}
${EDMCONFIGTREE_TREE:=true}
[ $EDMCONFIGTREE_TREE == "false" ] && EDMCONFIGTREE_COLOR=false

# Invoked with a wrong number of arguments. Give help.
if [ $# -eq 0 ] || [ $# -gt 2 ]; then
    cat <<EOF
edmConfigTree displays the complete tree of imported files in a given CMSSW configuration file,
and optionally searches them recursively for a regular expression.

Usage:
           edmConfigTree [regex] configuration_file
               regex             :    regular expression, as with grep
               configuration_file:    CMSSW configuration python file
                                      (Give absolute path to display imported files' resolved absolute path.)

           Note that the shell variable CMSSW_SEARCH_PATH must be set before edmConfigTree is invoked.

           To suppress colors, set the shell variable EDMCONFIGTREE_COLOR=false
           To suppress the tree structure (and the colors), set the shell variable EDMCONFIGTREE_TREE=false

Examples:
           Display the tree of all imported files in FullChainExample_cfg.py:
                 edmConfigTree FullChainExample_cfg.py
           Display with absolute paths the tree of all imported files in FullChainExample_cfg.py:
                 edmConfigTree /full/path/to/FullChainExample_cfg.py
           Recursively search all imported files for "Verbosity":
                 edmConfigTree Verbosity FullChainExample_cfg.py
           Recursively search all imported files for "Time" or "time":
                 edmConfigTree "[Tt]ime" FullChainExample_cfg.py
EOF
    exit 1
fi

# Exit if CMSSW_SEARCH_PATH is not set.
if [ ${#CMSSW_SEARCH_PATH} -eq 0 ]; then
    echo "CMSSW_SEARCH_PATH is not set. Exiting."
    exit 1
fi

# Set extglob for extended pattern matching features in parameter expansion.
shopt -s extglob

# If invoked with one argument, list files.
if [ $# -eq 1 ]; then
    FILE=$1
    [ ${FILE:0:1} == "/" ] && SHOW_ABSOLUTE_PATH=true || SHOW_ABSOLUTE_PATH=false
    [ $EDMCONFIGTREE_COLOR == "false" ] && echo "$FILE" || echo "  ${FILE%%*([^/])}${FILE##*/}"
    findInFile " " $1
fi

# If invoked with two arguments, list files and search them.
if [ $# -eq 2 ]; then
    FILE=$2
    [ ${FILE:0:1} == "/" ] && SHOW_ABSOLUTE_PATH=true || SHOW_ABSOLUTE_PATH=false
    [ $EDMCONFIGTREE_COLOR == "false" ] && echo "$FILE" || echo "  ${FILE%%*([^/])}${FILE##*/}"
    findInFile " " $2 "$1"
fi