#!/bin/bash
# $Id: dynacounter.bash,v 1.6 2012/06/30 01:32:25 bobgian Exp $

# config/dynacounter.bash in toplevel LAMARC directory
# Shell FUNCTION definition, not shell SCRIPT.

#-----------------------------------------------------------------------------------------------------------------------
#
# Builds count for DYNACOUNTER_START.
# Run this in the top-level LAMARC directory (ie, "~/lamarc").
# Takes no arguments.
#
# This BASH function scans all LAMARC source files ("./src/*/*.cpp") in the current toplevel directory,
# counts calls to "StartDynameter()", and builds a list of values for DYNACOUNTER_START.  It then writes
# a file called "dynacount.h" (in the "config" subdirectory of the current directory) which defines
# DYNACOUNTER_START as a function of DYNAMETER_FILE_INDEX.  Each file using the Dynameter machinery
# must include the following lines (indented portion, not the initial comment character):
#
#   // Must include this file BEFORE testing DYNAMETER_LEVEL (either explicitly below or implicitly
#   // inside file "dynatracer.h"), because this file establishes the definition (or not) of that symbol.
#   #include "local_build.h"
#   //
#   #ifdef DYNAMETER_LEVEL                          // If the Dynameter is enabled,
#   #define DYNAMETER_FILE_INDEX 0                  // this symbol is defined uniquely in each ".cpp" file
#   #include "dynacount.h"                          // and used in this file (hence order is important).
#   #endif // DYNAMETER_LEVEL
#   //
#   // If Dynameter calls are included (whether or not they are enabled by defining DYNAMETER_LEVEL),
#   // this file must be included.  It defines calls to StartDynameter() as macros that expand either into
#   // code (if enabled) or into whitespace (if disabled).  Also, if the Dynameter is enabled, this file
#   // depends on some definitions in "dynacount.h" (and thus must included AFTER it).
#   #include "dynatracer.h"
#
# The compile-time constant DYNAMETER_FILE_INDEX identifies the file (the value should be set sequentially
# starting from zero for the first Dynameter-using file and incrementing by one for each of the rest).
# That is, the value here will be different for each source file.  The other lines can appear verbatim.
#
# The file "dynacount.h" (written by this function) maps that index into the appropriate value of the
# compile-time constant DYNACOUNTER_START, which defines the value of the index used by the first invocation
# in that file of the macro StartDynameter().  For each invocation after the first, the index value passed
# as an argument to that macro in incremented automatically by the pre-defined macro __COUNTER__ (which starts
# at zero and increments with each usage in a given file).  This guarantees that each call to StartDynameter()
# system-wide gets its own unique index value, despite separate compilation of multiple source files.
#
# This function also keeps track of the maximum index value ever used (ie, the max count of all calls to
# StartDynameter() over all LAMARC source files processed by a run of this function) and writes the accumulated
# value as the definition of the constant DYNAMETER_ARRAYSIZE, which is used to dimension the array holding all
# the tracing and metering information.  This constant is used by "dynatracer.h", which is why THIS file must
# be included before THAT file.
#
# If source file editing ever leads to a change in the number of calls to StartDynameter(), systemwide,
# then make sure that the lines above are present in each file where needed, including a new value for the
# symbol DYNAMETER_FILE_INDEX for any files with freshly-added calls to StartDynameter().  Then run this
# function BEFORE recompiling the sources to make sure that the appropriate constants are defined with their
# newly-updated values during that compilation.
#
#-----------------------------------------------------------------------------------------------------------------------

function dynacounter()
{
    local dir file line startcounter flag
    local index_file=${PWD}/config/dynacount.h
    let startcounter=0

    if [[ ! -f ${PWD}/config/dynacounter.bash ]] || [[ ! -f ${PWD}/config/local_build.h ]] ; then
        echo
        echo "Run this in the toplevel LAMARC directory (the one containing \"config\" and \"src\" as subdirectories)."
        return 1
    fi

    if [[ -f ${PWD}/config/dynacount.h ]] ; then
        echo
        echo "Deleting old file:  \"${PWD}/config/dynacount.h\""
        /bin/rm -v ${PWD}/config/dynacount.h
    fi

    echo
    echo "Writing index file: \"${index_file}\""

    echo "// ${index_file}" > ${index_file}
    echo "// Automatically-generated index map for Dynameter." >> ${index_file}
    echo >> ${index_file}
    echo "#ifndef DYNACOUNT_H" >> ${index_file}
    echo "#define DYNACOUNT_H" >> ${index_file}
    echo >> ${index_file}

    # If DYNAMETER_FILE_INDEX is not defined when "dynacount.h" is included, then DYNACOUNTER_START
    # will not get defined either.  However, "dynacount.h" may still be needed even if DYNACOUNTER_START
    # is not, because "dynacount.h" also defines DYNAMETER_ARRAYSIZE.  For example, "dynameter.h"
    # requires DYNAMETER_ARRAYSIZE be defined but not DYNACOUNTER_START.  And "dynameter.h" requires
    # the correct value for DYNAMETER_ARRAYSIZE, so it needs the result of this function's count, even
    # though it does NOT need the intermediate results conveyed by DYNACOUNTER_START.
    #
    # The file "dynacount.h" provides a multi-way SWITCH statement, a compile-time sequence of
    # "#elif ( DYNAMETER_FILE_INDEX == XXX )" branches (where XXX is an incrementing integer sequence).
    # This next line provides the first line of that sequence, so that all the rest can start with "#elif"
    # rather than having to use a special case for the first value processed.
    #
    echo "#ifndef DYNAMETER_FILE_INDEX" >> ${index_file}

    echo
    for dir in ${PWD}/src/* ; do
        if [[ -d ${dir} ]] ; then
            for file in ${dir}/*.cpp ; do
                # Note that some of these will "count" even if commented out (ie, the ones NOT preceeded by "^[<>] ").  In this case, a disabled
                # call to "StartDynameter()" will result in incrementage of the count whether the call is used or not.  Wasteful, but not harmful.
                flag=NIL
                echo -n "File: \"${file}\",  Starting index: ${startcounter}"
                for line in $( /bin/grep -e "StartDynameter();" -e "DYNAMETER_FILE_INDEX" ${file} ) ; do
                    if [[ ${line} = DYNAMETER_FILE_INDEX ]] ; then
                        flag=T
                    elif [[ ${flag} = T ]] ; then
                        echo "#elif ( DYNAMETER_FILE_INDEX == ${line} )" >> ${index_file}
                        echo "  #define DYNACOUNTER_START ${startcounter}" >> ${index_file}
                        flag=NIL
                    elif [[ ${line} = "StartDynameter();" ]] ; then
                        let startcounter=$(( startcounter + 1 ))
                    fi
                done
                echo ",  Ending index: ${startcounter}"
            done
        fi
    done

    echo "#endif" >> ${index_file}

    echo >> ${index_file}
    echo "#define DYNAMETER_ARRAYSIZE ${startcounter}" >> ${index_file}

    echo >> ${index_file}
    echo "#endif // DYNACOUNT_H" >> ${index_file}
    echo >> ${index_file}
    echo "//____________________________________________________________________________________" >> ${index_file}

    return 0
}

#_______________________________________________________________________________________________________________________
