#! /usr/bin/env tclsh
# -*- tcl -*- 
# <20190419.1739.19>


# This file first checks that the change log is up to date
# if so, it builds the release change log
# and builds a version file for the other build tools to 
# use in naming the components.
#
proc Help {} {
  foreach ln [split {
    # This is the filerunner 'makerelease' control
    # file.  Here is a summery of what it does.
    #
    # 0.) Scans all the files to find the latest update
    #     to build the frVersion file. While doing this
    #     build a list of files to exclude from non-windows
    #     versions.
    #
    # 1.) Verifies that the 'HISTORY' file rev code
    #     matches the mains (fr) rev code.
    #     This first updates the version file by
    #     scanning all product files for the latest
    #     updates.
    # 2.) calls './tip_index.tcl to index the
    #     'Tips.txt' file.
    # 3.) calls './buildtclfilelist' start building 
    #     the 'warpfilelist.txt' which controls the
    #     the building of the *.exe files.
    # 4.) calls './makepackagelinks.tcl' to finish
    #     building 'wrapfilelist.txt' by fixing the
    #     package references for the wrap code.
    # 5.) calls './wrapwindows' to build the two
    #     '*.exe' wrap files (32 & 64 bit versions).
    # 6.) calls './pack' to build the tar release file.
    # 7.) calls './buildRPM.tcl' to build the RPM and DEB
    #     files.
    # 8.) Moves the RPM and DEB files to the release directory.
    #
    # If command line contains ver* stop after step 1.
    # If command line contains win* stop after step 5.
    # If command line contains *help* print this massage and stop.
  } \n] {
    puts "[string trim $ln]"
  }
}

if {[string match -nocase *help* $argv]} {Help;exit}

########################################################
#     This is the boiler plate code                    #
########################################################
# This bit of code figures out where the rest of the   #  
# routines are on the assumption that they are in the  #
# same directory as the initial code file.             #
# If 'setIt' is 1 or not coded auto_path is set in any #
# case the resulting dir is returned to the caller     #
# A second dir is also returned, which is the same     #
# with a leading C:/ removed as required by Wrap code  # 
# for windows. If not windows the two will be the same.#
#                                                      #
# To function correctly this code MUST be called prior #
# to completion of the 'source' command that brings it #
# in. Also, since it is used to set up auto_path it    #
# can not be auto loaded.  It may be sourced, but      #
# again the from where issue is there.                 #
#                                                      #
# It is best if this is just merger with the using     #
# code in a location prior to its call.                #
#                                                      #
proc setAutoPath {{setIt 1}} {
  set it [info script]
  set it [expr {$it == "" ? "[pwd]/*" : $it}]
  set it [file dir [file dir [file norm $it/*]]]
  # Wrap code requires we not have the drive letter...
  if {$setIt} {
    lappend ::auto_path $it [regsub {^[a-zA-Z]:/} $it {/}]
  }
  return [list $it [regsub {^[a-zA-Z]:/} $it {/}]]
}
##########################################################
#            End of boiler plate code                    #
##########################################################



# we assume fr (the main) is passed and therefor open.
# This code is to build the filerunner release.  We assume
# we are running in the .../filerunner/Makefiles dir.
# We use the following files explicitly to verify that the 
# version of the History matches the filerunner version
# and to build the change log:
#

set history "doc/HISTORY"
set mkDir [pwd]

proc fileError {file err} {
  puts "Error accessing file \"$file\": $err"
  exit 1
}

# this is basically an "in" operater that takes patterns in
# the list.

proc In {this that} {
  foreach pat $that {
    if {[string match $pat $this]} {return 1}
  }
  return 0
}

proc upDateVersion {files exclued} {
  global notInRpm
  # This function takes a list of files and figures the latest
  # version. This is found by looking at the version of each file.
  # This will be either an emacs time string within
  # the first 900 characters of the file or the mtime of the file.
  #
  # We exclude files in the exclude list (mainly those we build here)
  # and also dirs found in the list.
  set complained 0
  set latest 0
  set lastFile {}
  set exc {}
  foreach file $files {
    if {![file exists $file]} {
      if {!$complained} {
	puts "The following file(s) do not exist"
	incr complained
      }
      puts "$file"
    }
    
      
    if {[file isdir $file] || [In $file  $exclued]} {
      lappend exc [list [expr {$file in $exclued ? "Exc" : "Dir"}] $file]
      continue
    }
    set ffid [open $file r]
    set lines [split [read $ffid] \n]
    close $ffid
    set tm {}
    # puts "$file"
    set lc 0
    foreach fdata $lines {
      if {[regexp {.*<([0-9]{8}\.[0-9]{4}\.[0-9]{2})>.*} $fdata dum tm] || [incr lc] > 25} {break}
    }
    if {$tm == {}} {
      # no time string...
      set tmm [file mtime $file]
      # puts "clock  $tmm $file"
    } else {
      # puts "convert $tm $file"
      set tmm [clock scan $tm -format "%Y%m%d.%H%M.%S"]
      # puts "tstamp $tmm $file $tm $dum $lc"
    }
    # puts "$tmm $file"
    if {$tmm > $latest} {
      # puts "$tmm $file replaces $latest $lastFile $tm" 
      set latest $tmm
      set lastFile $file
    }
  }
  if {0} {
    puts "Excluded from time check"
    foreach en $exc {
      puts "$en"
    }
  }

  return [list [clock format $latest -format "%Y%m%d.%H%M.%S"] $lastFile $complained] 
}

# setPermissions makes sure each component has the right permissions for
# For most files the permession will be rw-rw-rw
# For executables we add 'x'. Since we know where we are, *.tcl & *.bit are NOT
# exectuable (except for fr.tcl) unless in the 'Makerelease' directory.
# So here is the rule: Only *.tcl and files with no extension might be
# executable and *.tcl only in Makerelese. Other file will be set
# executable if they start with '#!/'
#

proc setPermissions {fileList {reportErrors 0}} {
  package require Tclx
  # the currend pwd is .../filerunner
  # names in wrapfilelist.txt are relative to this path
  # We have a chicken/egg problem here in that we need to
  # have correct permissions to do some work before we
  # actually make the files we want to set. So this is
  # called twice. We return a fail count if a file is missing.
  # On the final call a non-zero return is BAD!
  set failCount 0
  set notInRpm {}
  puts "failCount $failCount"
  
  foreach target $fileList {
    if {![file exists $target]} {
      incr failCount
      if {$reportErrors} {
	puts "File $target is missing. Pwd is [pwd]"
      }
      continue
    }
    if {[file isdir $target]} {continue}
    if {$target == "fr.tcl" ||\
	    [file dir $target] == "Makefiles" ||\
	    [file extension $target] == {}} {
      # Possible executable
      set test [string trim [read [set fidt [open $target r]] 10]]
      # puts "possible exc $target $test"
      close $fidt
      if {[string index $test 0] == "#" &&\
	      [string index \
		   [set p1 [string trim [string range $test 1 end]]] 0] == "!" &&\
	      [string index [string trim [string range $p1 1 end]] 0] == "/"} {
	# puts "found executable $target"
	set mode 0775
      }      
    } else {
      set mode 0664
    }
    chmod $mode $target
  }
  return $failCount
}

proc try_this {what {OK no}} {
  set r [catch {exec -ignorestderr -keepnewline {*}$what} out]
  puts "$out"
  if {$r == 0} {return}

  puts "Error trying to execute: $what "

  if {$OK == "no"} {
    puts  "Stop the music."
    exit 1
  } else {
    puts "continuing with out doing [lindex $what 0]."
  }
}

#
cd ..

setPermissions {wrapfilelist wraplistControl extras}
cd $mkDir 
puts -nonewline "Indexing the Tip file... "
#return
try_this "./tip_index.tcl ../doc/Tips.txt"
puts "Building 'wrapfilelist.txt' ... "
try_this "./buildtclfilelist -c wraplistControl -o wrapfilelist.txt"
# now the Tarlist
puts "Building 'tarfilelist.txt' ... "
try_this "./buildtclfilelist -c extras -o tarfilelist.txt"

# At this point we have assembled a complete list of all the files
# in the release.

# We can now build the version file.
# First the exclude list:

lappend exclude \
    Makefiles/exclude \
    Makefiles/wrapfilelist.txt\
    Makefiles/tarfilelist.txt\
    frVersion.tcl \
    doc/HISTORY \
    doc/Tips.txt\
    *tclIndex \
    */pkgIndex.tcl\
    Makefiles/makerelease\
    packageLinks.tcl \
    */config?.tcl\
    Makefiles/tip_index.tcl \
    Makefiles/tcl-inotify-1.4.1.tar.gz

# Now the all file list.  This will be all files listed in
# "tarfilelist.txt"
set allFiles [split [exec cat $mkDir/tarfilelist.txt] \n]
cd ..

# set cmd [list cat $mkDir/wrapfilelist.txt | $mkDir/expand.tcl]
# # set r [exec {*}$cmd]
# set cmd2 [list cat $mkDir/extras | $mkDir/expand.tcl]

# # eliminate dirs...
# set f1 [split [exec {*}$cmd] \n]
# set f2 [split [exec {*}$cmd2] \n]
# # puts "$f1"
# # puts "$f2"
# #exit
# foreach file [concat $f1 $f2] {
#   if {[file isdir $file]} {continue}
#   lappend nFileList $file
# }

lassign [upDateVersion $allFiles $exclude] revCode file err

if {$err} {
  puts "Stop the music."
  exit 1
}

set fid [open frVersion.tcl w]
puts $fid "# This file is generated by \".../Makefiles/makerelease\" and reflects\
          \n# the lastest file changed in the filerunner package.\
          \n#\n# The last file changed was: $file.\
          \n#\n# which generate the following date code/revision:\n"

puts $fid "set glob(version) $revCode"
close $fid


puts "Found last file modified to be: $file. Updateing \"frVersion\" to $revCode\n"

if {[string match ver* $argv] } {
  exit 0
}
# handy stings...
set strRevMatch {[0-9][0-9].[0-9][0-9].[0-9][0-9].[0-9][0-9]*}

# convert rev code to the shortened form

regsub {20([0-9][0-9])([0-9][0-9])([0-9][0-9])\.([0-9][0-9]).+} \
         $revCode {\1.\2.\3.\4} revCode

# now we examine the History file...
#
if {[catch {split [read [set fid [open $history r]]] \n} lines] !=0} {
  fileError $history $lines
}
close $fid

set found 0
foreach ln $lines {
  if {[string match $strRevMatch $ln]} {
    incr found
    break
  }
  if {[incr hlines] >10} {break}
}
if {! $found} {
  puts "Failed to find version in first 10 lines of history ($history) file.
  Fix this."
  exit 1
}
#
# we want to allow other characters around the date code...
#
regexp {(.*)([0-9][0-9]\.[0-9][0-9]\.[0-9][0-9]\.[0-9][0-9]).*} $ln m one hver
set glob(version) 0
if {$hver != $revCode && ! [string match *win* $argv]} {
  puts "Version mismatch, \n$revCode from file $file\n$hver from $history "
  puts "fix this!"
  exit 1
}
puts "Found version $hver in $history, building change log"

cd $mkDir

catch {file mkdir "../release-$hver"}
if {[catch {open "../release-$hver/changelog-$hver.txt" w} fid] != 0} {
  fileError "../release-$hver/changelog-$hver.txt" $fid
}

set revFound 0
while {$revFound < 3 && [set lines [lassign $lines ln]] != {} } {
  puts $fid $ln
  if {[string match $strRevMatch $ln] || \
	  [string match "[string repeat - 30]*" $ln]} {
    incr revFound
  }
}
close $fid

if {[string index $ln 0] != "-"} {
  puts "\nChange log terminated on revcode ($ln) rather than '-----------...'"
  puts "Fix this."
  exit 1
}

puts "Building packageLinks.tcl... "
try_this "./makepackagelinks.tcl wrapfilelist.txt"
# First set permissions...

set pw [pwd]
puts "pwd $pw"
cd ..
puts "pwd [pwd]"
if {[setPermissions $allFiles 1] != 0} {
  puts "Failed setting up permissions!"
  puts "Stop the music."
  exit 1
}
cd $pw
puts "Wraping filerunner... "
try_this "./wrapwindows $hver 64 32" yes
# stop here if asked for windows ...

if {[string match *win* $argv]} {exit 0}
puts "Building the tar file"
# this was in "pack" but is now local
cd ..
file delete -force ./filerunner
try_this "cat Makefiles/tarfilelist.txt | cpio -pdL filerunner"
try_this "tar -czf release-$hver/fr-$hver.tar.gz --numeric-owner filerunner/"
#try_this "./pack $hver"
# end of tar build...
if {[string match *tar* $argv]} {exit 0}
cd $mkDir

puts "building the RPM and DEB files"
try_this "./buildRPM.tcl -v EMAIL=tom@wildturkeyranch.net \"NAME=Tom\
 Turkey\" TAR=../release-$hver/fr-$hver.tar.gz" 

puts "Moving the RPM and DEB files to ../release-$hver"

set r [catch {set file [concat [glob ~/rpmbuild/RPMS/noarch/*$hver*] \
			    [glob  ~/rpmbuild/DEB/*$hver*.deb] \
			    [glob  ~/rpmbuild/SRPMS/*$hver*]]} err]
if {$r != 0} {
  puts "Error: $err"
  puts "Stop the music."
  exit 1
}

foreach fl $file {
  set r [catch {file rename -force $fl ../release-$hver} err]
  if {$r == 0} {continue}
  puts "Error: $err"
  puts "Stop the music."
  exit 1
}

# Time to build the windows msi file...

puts "Building the 'msi' file"

try_this "./msi-tool.tcl msiControl revision=$hver"

if {![file exists Filerunner.msi]} {
  puts "Error: msi build failed"
  puts "Stop the music."
  exit 1
}

puts "Moving Filerunner.msi to ../release-$hver"

set r [catch {file rename -force Filerunner.msi ../release-$hver/Filerunner_$hver.msi} err]

puts "Build finished.  All built files put in ../release-$hver"
puts "Time to test!"
