#!/usr/bin/env bash #$$$ subprogram documentation block # . . . . # subprogram: make_depend.bash # prgmmr: jing guo # org: NASA/GSFC, Global Modeling and Assimilation Office, 610.3 # date: 2015-06-05 # # abstract: This script generates a list of dependences supporting make(). # # program history log: # 2015-06-05 j guo - original design and coding, # and this document block # # attributes: # language: bash # machine: Linux # # synopsis: # bash make_depend.bash *.[Ff] *.[Ff]90 # # descriptions: # This script generates dependence lists in a compact form of # # x.o : x.f90 y.inc z.o # # without explicit referencing to *.mod, except where a *.mod # is considered external. # # Simple Fortran code style is assumed, where USE, INCLUDE # (as well as "#include"), and MODULE statements are searched # to find one and only one module name or file name. Also, no # MODULE definition is expected from include-files. # # *.mod entries in USE are resolved to corresponding "*.o" files # based on looking-up a table gathering MODULE declarations. # This table has the form of # # a.mod : z.o # b.mod : z.o # # This script can be configured to dynamically generate dependency # files (*.d) at the make() time. However, this version has been # configured to generate dependencies statically, to avoid some # complicities involved and to support traditional non-GNU make(). # #$$$ end subprogram documentation bsh make_delock # These two set commands are used to ensure this script can fail fast on exceptions. set -e set -o pipefail myname=$(basename $0) MYname="# $(basename $myname .bash)" mfile=allmod.list mfile=.--allmod.list-- warning="" warning="yes" #ignore_not_found_mod="yes" #ignore_not_found_inc="yes" ignore_dotobj_lookup_external="yes" multi_lines="yes" multi_lines="" if [ $# -eq 0 ]; then echo "Usage: $myname <*.[Ff]> <*.[Ff]90>" 1>&2 exit 1 fi warn_(){ local m if [[ -n $warning ]] then m=$1; shift 1 echo "$MYname:$m(): >>> WARNING <<<" "$@" 1>&2 fi } perr_(){ local m m=$1; shift 1 echo "$MYname:$m(): >>> ERROR <<<" "$@" 1>&2 } basepath_(){ echo "$1" | sed -e's:[.][cFf][^.]*$::' } inc_grep_(){ sed -f $incscr $1 | env LC_ALL=C sort -u } inc_screen_(){ # screen out include file names being black-listed local n r local -a v while read r do v=($r); n=${#v[*]} if [[ $n -ne 1 ]] then warn_ inc_screen_ "suspicious text from \"$1\" ignored, (INCLUDE?) \"$r\"" else case $r in $1) ;; mpif.h|netcdf.inc) ;; *) echo $r ;; esac fi done } dotinc_recurs_grep_(){ local i s s=$1 if [[ ! -r $s ]] then perr_ "dotinc_recurs_grep_" "can not access, \"$s\"" exit 2 fi for i in $(inc_grep_ $s | inc_screen_ $s ) do if [[ -r $i || -z $ignore_not_found_inc ]] then echo $i fi if [[ -r $i ]]; then dotinc_recurs_grep_ $i fi done } use_grep_(){ #sed -f $usescr $1 1>&2 sed -f $usescr $1 | tr '[:upper:]' '[:lower:]' | env LC_ALL=C sort -u } dotmod_screen_(){ # screen out dotmod names being black-listed local n r local -a v while read r do v=($r); n=${#v[*]} if [[ $n -ne 1 ]] then r=$(echo $r | sed -e's/[.]mod$//') warn_ "dotmod_screen_" "suspicious text from \"$1\" ignored, (USE?) \"$r\"" else case $r in mpi.mod|netcdf.mod) ;; omp_lib.mod) ;; *) echo $r ;; esac fi done } dotmod_use_grep_(){ # grep .mod names from uses in a given list of files local s for s in "$@" do if [[ ! -r $s ]] then warn_ "dotmod_use_grep_" "can not read thus ignored, \"$s\"" continue fi use_grep_ $s | dotmod_screen_ $1 done } src_grep_(){ local n o local -a v n=0 o=$1 v=($1) if [[ -r $mfile ]] then o=$(grep -i "^$1 : " $mfile | awk '{print $3}') v=($o) n=${#v[*]} if [[ $n -eq 0 ]] then o=$1 fi v=($o) fi if [[ $n -gt 1 ]] then perr_ "src_grep_" "multiple \".o\" prerequisites are present for the same \".mod\" target, \"$1 : ${v[@]}\"" exit 2 elif [[ $n -eq 0 ]] then if [[ -z $ignore_dotobj_lookup_external ]]; then warn_ "src_grep_" "not locally accessible, \"$1\"" fi fi if [[ $n -gt 0 || -z $ignore_not_found_mod ]] then echo "$o" fi } dotobj_mod_grep_(){ local m for m in "$@" do src_grep_ $m done } filter_out_(){ local e while read e do case $e in $1) ;; *) echo "$e" ;; esac done } modhash_make_(){ # create a single entry list local f o m local m for f in "$@"; do if [ -f "$f" ]; then # Instead of using basename, sed is used to preserve the full path if in use #o=$(echo "$f" | sed -e "s:\.[fF]$::" -e "s:\.[fF]90$::").o o=$(basepath_ "$f").o for m in $(sed -f $modscr $f | env LC_ALL=C sort -u | tr '[:upper:]' '[:lower:]') do echo $m.mod : $o done fi done | env LC_ALL=C sort -u # "sort -u" at the end is to prevent duplications in the given source file list. } check_single_(){ local n l local -a v while read l do v=($l); n=${#v[*]} if [[ $n -ne 1 ]] then warn_ "check_single_" "invalid name from \"$1\", token = \"$l\"" else echo "$l" fi done } dotobj_only_(){ local o for o in "$@" do case $o in *.o) echo "$o" ;; esac done } dotobj_unknown_(){ local m for m in "$@" do case $m in *.o) ;; *) echo "$m" ;; esac done } dotinc_known_(){ local i for i in "$@" do if [[ -r $i ]] then echo "$i" fi done } dotinc_unknown_(){ local i for i in "$@" do if [[ ! -r $i ]] then echo "$i" fi done } fdep_make_(){ local F O local ilist olist mlist local imiss omiss F="$1" if [[ ! -r $F ]] then perr_ "fdep_make_" "can not read, \"$F\"" exit 2 fi O=$(basepath_ "$F").o #echo "O=$O" #exit 0 ilist=$(dotinc_recurs_grep_ "$F" | env LC_ALL=C sort -u) imiss=$(dotinc_unknown_ $ilist) ilist=$(dotinc_known_ $ilist) mlist=$(dotmod_use_grep_ "$F" $ilist | env LC_ALL=C sort -u) olist=$(dotobj_mod_grep_ $mlist | env LC_ALL=C sort -u | filter_out_ "$O") # || \ omiss=$(dotobj_unknown_ $olist) olist=$(dotobj_only_ $olist) if [[ -z $multi_lines ]] then echo $O : $F $ilist $olist if [[ -n $imiss || -n $omiss ]] then echo "#--" $O : $imiss $omiss fi else echo $O : $F if [[ -n $ilist ]]; then echo $O : $ilist; fi if [[ -n $olist ]]; then echo $O : $olist; fi if [[ -n $imiss || -n $omiss ]] then if [[ -n $imiss ]]; then warn_ "fdep_make_" "locally unknown pre-requisites, \"$O : $imiss\"" echo $O : $imiss fi if [[ -n $omiss ]]; then warn_ "fdep_make_" "locally unknown pre-requisites, \"$O : $omiss\"" echo $O : $omiss fi fi fi # # Alternatively, for dynamically generated %.d file creations, # # D=$(basename $O .o).d # ilist=$(dotinc_recurs_grep_ "$F" | env LC_ALL=C sort -u) # ulist=$(dotmod_use_grep_ "$F" $ilist | env LC_ALL=C sort -u) # mlist=$(modhash_make_ $F) # # echo "$D : $F $ilist" # echo "$mlist : $O" # echo "$O : $F" # echo "$O : $ulist" # echo "$O : $ilist" } incscr="${TMPDIR:-/tmp}/$$inc" usescr="${TMPDIR:-/tmp}/$$use" modscr="${TMPDIR:-/tmp}/$$mod" m_hash="${TMPDIR:-/tmp}/$$m_hash" clean_up_on_exit_(){ rm -f $incscr rm -f $usescr rm -f $modscr rm -f $m_hash } trap -- "clean_up_on_exit_" EXIT # Make sed script #================= cat <<'EOT' >$incscr #n /^\#include[ ][ ]*/{ s/^[^"<]*["<]// s/[">].*$// s/^\#include[ ][ ]*// p b } /^[ ]*[Ii][Nn][Cc][Ll][Uu][Dd][Ee][ ][ ]*/{ s/^[ ]*[Ii][Nn][Cc][Ll][Uu][Dd][Ee][ ][ ]*// s/\!.*$// s/['"]//g s/[ ]*$// p b } EOT cat <<'EOT' >$usescr #n /^[ ]*[Uu][Ss][Ee][ ][ ]*/{ s/^[ ]*[Uu][Ss][Ee][ ][ ]*// s/[ ]*\!.*$// s/[ ]*,[ ]*[Oo][Nn][Ll][Yy].*$// s/$/.mod/ p b } EOT cat <<'EOT' >$modscr #n /^[ ]*[Mm][Oo][Dd][Uu][Ll][Ee][ ][ ]*[Pp][Rr][Oo][Cc][Ee][Dd][Uu][Rr][Ee][ ][ ]*/{ b } /^[ ]*[Mm][Oo][Dd][Uu][Ll][Ee][ ][ ]*/{ s/^[ ]*[Mm][Oo][Dd][Uu][Ll][Ee][ ][ ]*// s/\!.*$// s/[ ]*$// p } EOT #-- For testing ... #modhash_make_ "$@" #exit 0 if [[ ! -r $mfile ]] then mfile=$m_hash echo "$MYname: >>> ACTION <<< creating $mfile" 1>&2 modhash_make_ "$@" >$mfile echo "$MYname: >>> ACTION <<< created, $mfile" 1>&2 fi for F in "$@" do fdep_make_ "$F" done