#!/usr/bin/ruby

help = <<EOF
--- qcreate ---
Creates a dedicated "execution" directory to store all the output of a command.
Optional: uses qsub to submit the job.

General usage:
  qcreate [-qsub] [-statePath /path/to/state] <command> [<arg> ... <arg>]

All occurrences of _OUTPATH_ in the arguments are replaced with the execution directory.
For example:
  qcreate touch _OUTPATH_/foo
will create the file state/execs/<id>.exec/foo, where <id> is unique to this job.

Usage for fig programs:
  qcreate java ... -execDir _OUTPATH_ -overwriteExecDir
More generally, _OUTPATH_ will be replaced with the execution directory.
EOF

statePath = 'state'
qsub = false

# Interpret the prefix of ARGV as options to be interpreted.
while true
  if ARGV[0] == '-qsub'
    ARGV.shift
    qsub = true
  elsif ARGV[0] == '-statePath'
    ARGV.shift
    statePath = ARGV.shift
    if not statePath
      puts "Error: missing statePath"
      puts help
      exit 1
    end
  else
    break
  end
end
if ARGV.size == 0
  puts "Error: no command specified"
  puts help
  exit 1
end

system "mkdir -p #{statePath}/execs" or exit 1

lastExecFile = statePath+"/lastExec"
if not File.exists?(lastExecFile)
  puts "Creating #{lastExecFile}"
  system "touch #{lastExecFile}"
end

f = File.open(lastExecFile, 'r+')
if f.flock(File::LOCK_EX) != 0
  puts "Error: unable to lock #{lastExecFile}"
  exit 1
end
id = f.read
begin
  id = id == '' ? -1 : Integer(id)
  id += 1
  f.rewind
  f.puts id
  f.flush
  f.truncate(f.pos)
rescue
  puts "Error: #{lastExecFile} has '#{id}' which is not an integer"
  id = nil
end
f.close
exit 1 if not id

execPath = statePath + "/execs/#{id}.exec"
puts "Execution directory: #{execPath}"
if not Dir.mkdir(execPath)
  puts "Already exists (this shouldn't happen): #{execPath}"
  exit 1
end

cmdFile = "#{execPath}/#{id}.sh"
out = open(cmdFile, 'w')
cmd = ARGV.map{|x| x =~ /[ "]/ ? x.inspect : x}.join(' ').gsub(/_OUTPATH_/, execPath)
cmd += " > #{execPath}/stdout 2> #{execPath}/stderr" if qsub
out.puts "cd #{Dir.pwd}"
out.puts cmd
out.close
puts cmd

system "git rev-parse HEAD > #{execPath}/git-hash" if File.exists?('.git')

if qsub
  exec("qsub #{cmdFile} -o /dev/null -e /dev/null")
else
  exec("bash #{cmdFile}")
end
