1#!/usr/bin/env bash 2 3# 4# gen-make-frag.sh 5# 6# Field G. Van Zee 7# 8 9 10print_usage() 11{ 12 #local script_name 13 14 # Get the script name 15 #script_name=${0##*/} 16 17 # Echo usage info 18 echo " " 19 echo " "$script_name 20 echo " " 21 echo " Field G. Van Zee" 22 echo " " 23 echo " Automatically generates makefile fragments for a given directory. " 24 echo " " 25 echo " Usage:" 26 echo " ${script_name} [options] root_dir frag_dir templ.mk" 27 echo " " 28 echo " Arguments (mandatory):" 29 echo " " 30 echo " root_dir The root directory to scan when generating makefile" 31 echo " fragments." 32 echo " " 33 echo " frag_dir The root directory in which makefile fragments will be" 34 echo " generated." 35 echo " " 36 echo " templ.mk The template makefile fragment used to generate the actual" 37 echo " fragments." 38 echo " " 39 echo " " 40 echo " The following options are accepted:" 41 echo " " 42 echo " -d dry-run" 43 echo " Go through all the motions, but don't actually generate any" 44 echo " makefile fragments." 45 echo " -r recursive" 46 echo " Also generate makefile fragments for subdirectories." 47 echo " -h hide" 48 echo " Hide the makefile fragments by prepending filenames with '.'." 49 echo " -v [0|1|2] verboseness level" 50 echo " level 0: silent (no output)" 51 echo " level 1: default (one line per directory)" 52 echo " level 2: verbose (several lines per directory)." 53 echo " " 54 55 # Exit with non-zero exit status 56 exit 1 57} 58 59 60 61 62 63 64 65# 66# gen_mkfile() 67# 68# Creates a single makefile fragment in a user-specified directory and adds 69# any local source files found to a top-level Makefile variable. 70# 71gen_mkfile() 72{ 73 # Local variable declarations 74 local mkfile_frag_tmpl_path 75 local mkfile_frag_var_name 76 local src_file_suffixes 77 local this_dir 78 local this_frag_dir 79 local mkfile_frag_tmpl_name 80 local mkfile_name 81 local mkfile_frag_path 82 local curr_frag_dir 83 local curr_frag_path 84 local local_src_files 85 local sub_items 86 local item_path 87 local item_suffix 88 local curr_frag_sub_dirs 89 90 91 # Extract our arguments to local variables 92 mkfile_frag_tmpl_path=$1 93 mkfile_frag_var_name=$2 94 src_file_suffixes="$3" 95 this_dir=$4 96 this_frag_dir=$5 97 98 99 # Strip the leading path from the template makefile path to get its 100 # simple filename. Hide the output makefile fragment filename, if 101 # requested. 102 mkfile_frag_tmpl_name=${mkfile_frag_tmpl_path##*/} 103 if [ -n "$hide_flag" ]; then 104 mkfile_frag_path=$this_frag_dir/.$mkfile_frag_tmpl_name 105 else 106 mkfile_frag_path=$this_frag_dir/$mkfile_frag_tmpl_name 107 fi 108 109 110 # Determine the directory in which the fragment will reside. 111 curr_frag_path=$this_dir 112 curr_frag_dir=${this_dir##*/} 113 114 115 # Initialize the local source list to empty 116 local_src_files="" 117 118 # Get a listing of the items in $this_dir 119 sub_items=$(ls $this_dir) 120 121 # Generate a list of the source files we've chosen 122 for item in $sub_items; do 123 124 # Prepend the directory to the item to get a relative path 125 item_path=$this_dir/$item 126 127 # Acquire the item's suffix, if it has one 128 item_suffix=${item_path##*.} 129 130 # If the suffix matches, then add it to our list 131 if is_in_list $item_suffix "$src_file_suffixes" 132 then 133 local_src_files="$local_src_files $item" 134 fi 135 done 136 137 # Delete the leading " " space character in the local source files list. 138 local_src_files=${local_src_files##" "} 139 140 141 # Initialize the fragment subdirectory list to empty 142 curr_frag_sub_dirs="" 143 144 # Capture the relative path listing of items in $this_dir. 145 sub_items=$(ls $this_dir) 146 147 # Determine the fragment's subdirectory names, if any exist 148 for item in $sub_items; do 149 150 # Prepend the directory to the item to get a relative path 151 item_path=$this_dir/$item 152 153 # If item is a directory, and it's not in the ignore list, descend into it. 154 if [ -d $item_path ] && ! should_ignore $item; then 155 curr_frag_sub_dirs=$curr_frag_sub_dirs" "$item 156 fi 157 done 158 159 # Delete the leading " " space character in fragment's subdirectory list. 160 curr_frag_sub_dirs=${curr_frag_sub_dirs##" "} 161 162 163 # Be verbose, if level 2 was requested. 164 if [ "$verbose_flag" = "2" ]; then 165 echo "mkf frag tmpl path: $mkfile_frag_tmpl_path" 166 echo "mkf frag path: $mkfile_frag_path" 167 echo "curr frag path: $curr_frag_path" 168 echo "curr frag dir: $curr_frag_dir" 169 echo "curr frag sub dirs: $curr_frag_sub_dirs" 170 echo "local src files: $local_src_files" 171 echo "src file suffixes: $src_file_suffixes" 172 echo "mkf frag var name: $mkfile_frag_var_name" 173 echo "--------------------------------------------------" 174 fi 175 176 177 # Copy the template makefile to the directory given, using the new 178 # makefile name we just created above. 179 if [ -z "$dry_run_flag" ]; then 180 cat $mkfile_frag_tmpl_path | sed -e s/"$mkfile_fragment_curr_dir_name_anchor"/"$curr_frag_dir"/g \ 181 | sed -e s/"$mkfile_fragment_sub_dir_names_anchor"/"$curr_frag_sub_dirs"/g \ 182 | sed -e s/"$mkfile_fragment_local_src_files_anchor"/"$local_src_files"/g \ 183 | sed -e s/"$mkfile_fragment_src_var_name_anchor"/"$mkfile_frag_var_name"/g \ 184 > $mkfile_frag_path 185 fi 186 187 188 # Return peacefully. 189 return 0 190} 191 192 193# 194# gen_mkfiles 195# 196# Recursively generates makefile fragments for a directory and all 197# subdirectories. All of the actual work happens in gen_mkfile(). 198# 199gen_mkfiles() 200{ 201 # Local variable declarations 202 local item sub_items curr_dir this_frag_dir this_dir 203 204 205 # Extract our argument 206 curr_dir=$1 207 this_frag_dir=$2 208 209 210 # Append a relevant suffix to the makefile variable name, if necesary 211 all_add_src_var_name "$curr_dir" 212 213 214 # Be verbose if level 2 was requested 215 if [ "$verbose_flag" = "2" ]; then 216 echo ">>>" $script_name $mkfile_frag_tmpl_path ${src_var_name}_$SRC "\"$src_file_suffixes\"" $curr_dir $this_frag_dir 217 elif [ "$verbose_flag" = "1" ]; then 218 echo "$script_name: creating makefile fragment in $this_frag_dir from $curr_dir" 219 fi 220 221 222 # Call our function to generate a makefile in the directory given. 223 gen_mkfile $mkfile_frag_tmpl_path "${src_var_name}_$SRC" "$src_file_suffixes" $curr_dir $this_frag_dir 224 225 226 # Get a listing of the directories in $directory 227 sub_items=$(ls $curr_dir) 228 229 # Descend into the contents of root_dir to generate the subdirectories' 230 # makefile fragments. 231 for item in $sub_items; do 232 233 # If item is a directory, and it's not in the ignore list, descend into it. 234 if [ -d "$curr_dir/$item" ] && ! should_ignore $item; then 235 gen_mkfiles $curr_dir/$item $this_frag_dir/$item 236 fi 237 done 238 239 240 # Remove a relevant suffix from the makefile variable name, if necesary 241 all_del_src_var_name "$curr_dir" 242 243 244 # Return peacefully 245 return 0 246} 247 248 249 250update_src_var_name_lib() 251{ 252 local dir act i name var_suffix 253 254 255 # Extract arguments 256 act="$1" 257 dir="$2" 258 259 260 # Strip / from end of directory path, if there is one, and then strip 261 # path from directory name. 262 dir=${dir%/} 263 dir=${dir##*/} 264 265 266 # Run through our list 267 for i in ${lib_i[@]}; do 268 269 # Get the ith name 270 name=${lib_name[$i]} 271 272 # If the current item matches $dir, then we'll probably have to make 273 # a modification of some form to src_var_name. 274 if [ "$dir" = "$name" ]; then 275 276 # Get the suffix in uppercase. 277 var_suffix=$(echo "$name" | tr '[:lower:]' '[:upper:]') 278 279 # Either add or remove the suffix. 280 if [ "$act" == "+" ]; then 281 282 # This conditional is added so that only the first directory 283 # matching an item in the lib_list is appended to the 284 # src_var_name variable. Otherwise we might have source in 285 # base/flamec/wrappers/blas/3/gemm being appended to a 286 # variable named MK_BASE_FLAMEC_BLAS_SRC, which is not what 287 # we want. 288 if [ "$src_var_name" = "MK" ]; then 289 src_var_name=${src_var_name}_$var_suffix 290 else 291 continue 292 fi 293 else 294 src_var_name=${src_var_name%_$var_suffix} 295 fi 296 297 # No need to continue iterating. 298 break; 299 fi 300 done 301} 302update_src_var_name_leaf() 303{ 304 local dir act i name var_suffix 305 306 307 # Extract arguments 308 act="$1" 309 dir="$2" 310 311 312 # Strip / from end of directory path, if there is one, and then strip 313 # path from directory name. 314 dir=${dir%/} 315 dir=${dir##*/} 316 317 318 # Run through our list 319 for i in ${leaf_i[@]}; do 320 321 # Get the ith name 322 name=${leaf_name[$i]} 323 324 # If the current item matches $dir, then we'll have 325 # to make a modification of some form. 326 if [ "$dir" = "$name" ]; then 327 328 # Convert the variable suffix to uppercase. 329 var_suffix=$(echo "$name" | tr '[:lower:]' '[:upper:]') 330 331 # Get the valid source file suffixes from the leaf array. 332 file_suffix_list=${leaf_suffix[$i]} 333 334 # Either add or remove the suffix, and also update the 335 # source file suffix variable. 336 if [ "$act" == "+" ]; then 337 src_var_name=${src_var_name}_$var_suffix 338 src_file_suffixes="$file_suffix_list" 339 else 340 src_var_name=${src_var_name%_$var_suffix} 341 src_file_suffixes=$no_file_suffix 342 fi 343 344 # No need to continue iterating. 345 break; 346 fi 347 done 348} 349 350init_src_var_name() 351{ 352 local dir="$1" 353 354 # Strip off the leading / if there is one 355 dir=${dir%%/} 356 357 # Convert the / directory separators into spaces to make a list of 358 # directories. 359 list=${dir//\// } 360 361 # Inspect each item in $list 362 for item in $list; do 363 364 # Try to initialize the source variable name 365 all_add_src_var_name $item 366 done 367} 368 369all_add_src_var_name() 370{ 371 local dir="$1" 372 373 update_src_var_name_lib "+" "$dir" 374 update_src_var_name_leaf "+" "$dir" 375 376} 377 378all_del_src_var_name() 379{ 380 local dir="$1" 381 382 update_src_var_name_leaf "-" "$dir" 383 update_src_var_name_lib "-" "$dir" 384} 385 386read_mkfile_var_config() 387{ 388 local index lname lsuff 389 declare -i count 390 391 # Read each line of the file describing the library types that might be 392 # built. 393 count=0 394 for i in $(cat "${script_path}/config/lib_list"); do 395 396 # Get the index and library name for each line 397 #index=${i%%:*} 398 #lname=${i##*:} 399 lname=${i} 400 401 # Save this info into their respective arrays 402 lib_i[$count]=$count 403 lib_name[$count]=$lname 404 405 # Increment the counter 406 let count=$count+1 407 done 408 409 410 # Read each line of the file describing leaf node types 411 count=0 412 for i in $(cat "${script_path}/config/leaf_list"); do 413 414 # Get the index, suffix, and directory name for each line 415 #index=${i%%:*} 416 lname=${i%%:*} 417 #lname=${lname#*:} 418 lsuff=${i##*:} 419 lsuff=${lsuff//,/ } 420 421 # Save this info into their respective arrays 422 leaf_i[$count]=$count 423 leaf_name[$count]=$lname 424 leaf_suffix[$count]=$lsuff 425 426 # Increment the counter 427 let count=$count+1 428 done 429 430 431 # Read each line of the file describing directories to ignore 432 count=0 433 for i in $(cat "${script_path}/config/ignore_list"); do 434 435 # Get the index and name for each line 436 #index=${i%%:*} 437 lname=${i} 438 439 # Save this info into their respective arrays 440 ignore_i[$count]=$count 441 ignore_name[$count]=$lname 442 443 # Increment the counter 444 let count=$count+1 445 done 446} 447 448main() 449{ 450 # Global array delcarations 451 declare -a lib_i 452 declare -a lib_name 453 declare -a leaf_i 454 declare -a leaf_name 455 declare -a leaf_suffix 456 declare -a ignore_i 457 declare -a ignore_name 458 459 460 # Define these makefile template "anchors" used in gen_mkfile() 461 mkfile_fragment_curr_dir_name_anchor="_mkfile_fragment_curr_dir_name_" 462 mkfile_fragment_sub_dir_names_anchor="_mkfile_fragment_sub_dir_names_" 463 mkfile_fragment_local_src_files_anchor="_mkfile_fragment_local_src_files_" 464 mkfile_fragment_src_var_name_anchor="_mkfile_fragment_src_var_name_" 465 466 # The name of the script, stripped of any preceeding path. 467 script_name=${0##*/} 468 469 # The path to the script. 470 script_path=${0%/${script_name}} 471 472 # The variable that always holds the string that will be passed to 473 # gen_mkfile() as the source variable to insert into the fragment.mk. 474 src_var_name='MK' 475 476 # The suffix appended to all makefile fragment source variables 477 SRC='SRC' 478 479 # The placeholder we use to signify that we're not looking for any 480 # source files. (Does any file format use .z?) 481 no_file_suffix='z' 482 src_file_suffixes='z' 483 484 # The arguments to this function. They'll get assigned meaningful 485 # values after getopts. 486 root_dir="" 487 frag_dir="" 488 mkfile_frag_tmpl_path="" 489 490 # Flags set by getopts. 491 dry_run_flag="" 492 hide_flag="" 493 recursive_flag="" 494 verbose_flag="" 495 496 497 # Local variable declarations 498 local item sub_items this_dir 499 500 501 502 # Read the makefile source variable config files to be used in the 503 # makefile fragment generation. 504 read_mkfile_var_config 505 506 507 # Process our command line options. 508 while getopts ":dhrv:" opt; do 509 case $opt in 510 d ) dry_run_flag="1" ;; 511 h ) hide_flag="1" ;; 512 r ) recursive_flag="1" ;; 513 v ) verbose_flag=$OPTARG ;; 514 \? ) print_usage 515 esac 516 done 517 shift $(($OPTIND - 1)) 518 519 520 # Make sure that verboseness level is valid 521 if [ "$verbose_flag" != "0" ] && 522 [ "$verbose_flag" != "1" ] && 523 [ "$verbose_flag" != "2" ]; then 524 verbose_flag="1" 525 fi 526 527 # Check the number of arguments after command line option processing. 528 if [ $# != "3" ]; then 529 print_usage 530 fi 531 532 533 # Extract our arguments 534 root_dir=$1 535 frag_dir=$2 536 mkfile_frag_tmpl_path=$3 537 538 539 # Strip / from end of directory path, if there is one. 540 root_dir=${root_dir%/} 541 frag_dir=${frag_dir%/} 542 543 544 # Append relevant suffixes to the makefile variable name based on the 545 # current root, if any of the directory names match. 546 init_src_var_name "$root_dir" 547 548 549 # Be verbose if level 2 was requested 550 if [ "$verbose_flag" = "2" ]; then 551 echo ">>>" $script_name $mkfile_frag_tmpl_path ${src_var_name}_$SRC "\"$src_file_suffixes\"" $root_dir $frag_dir 552 elif [ "$verbose_flag" = "1" ]; then 553 echo "$script_name: creating makefile fragment in $frag_dir from $root_dir" 554 fi 555 556 557 # Call our function to generate a makefile in the root directory given. 558 gen_mkfile $mkfile_frag_tmpl_path "${src_var_name}_$SRC" "$src_file_suffixes" $root_dir $frag_dir 559 560 561 # If we were asked to act recursively, then continue processing 562 # root_dir's contents. 563 if [ -n "$recursive_flag" ]; then 564 565 # Get a listing of the directories in $directory 566 sub_items=$(ls $root_dir) 567 568 # Descend into the contents of root_dir to generate the makefile 569 # fragments. 570 for item in $sub_items; do 571 572 # If item is a directory, and it's not in the ignore list, descend into it. 573 if [ -d "$root_dir/$item" ] && ! should_ignore $item ; then 574 575 gen_mkfiles $root_dir/$item $frag_dir/$item 576 fi 577 done 578 fi 579 580 581 # Exit peacefully 582 return 0 583} 584 585should_ignore() 586{ 587 local item name 588 589 590 # Extract argument, the item that we may have to ignore. 591 item="$1" 592 593 594 # Process each index in ignore array 595 for i in "${ignore_i[@]}"; do 596 597 # Get the ith name 598 name=${ignore_name[$i]} 599 600 # If the current value of $name matches $item, then we need to 601 # signal to calling function that we SHOULD ignore $item. 602 # Notice that returning zero value means "success". 603 if [ "$item" = "$name" ]; then 604 return 0 605 fi 606 done 607 608 609 # If we got this far, then item is not in the ignore list, so we 610 # signal that we should NOT ignore item. Notice that returning 611 # a non-zero value means "failure". 612 return 1 613} 614 615is_in_list() 616{ 617 local cur_item the_item item_list 618 619 # Extract argument. 620 the_item="$1" 621 item_list="$2" 622 623 # Check each item in the list against the item of interest. 624 for cur_item in ${item_list}; do 625 626 # If the current item in the list matches the one of interest 627 if [ "${cur_item}" = "${the_item}" ]; then 628 629 # Return success (ie: item was found). 630 return 0 631 fi 632 done 633 634 # If we made it this far, return failure (ie: item not found). 635 return 1 636} 637 638# The script's main entry point, passing all parameters given. 639main "$@" 640