require 'rbconfig'
require 'tempfile'
require 'tmpdir'
require 'open-uri'

ETCHVER = IO.read('../VERSION').chomp
TARBALLFILE = "etch-#{ETCHVER}.tar.gz"
TARBALL = File.expand_path(TARBALLFILE)

BUILDROOT = '/var/tmp/etch-client-buildroot'

# Copies the etch client files to destdir.  If any of the dir options
# are not specified the files that would go in that directory will not
# be copied.
# options:
# :bindir
# :libdir
# :etcdir
# :mandir
# :crondir
# :ruby (#! lines in scripts will be changed to specified ruby)
def copy_etch_files(destdir, options={})
  if options[:bindir]
    bindir = File.join(destdir, options[:bindir])
    mkdir_p(bindir)
    binapps = ['etch', 'etch_to_trunk', 'etch_cron_wrapper']
    binapps.each do |binapp|
      if options[:ruby]
        # Change #! line
        File.open(File.join(bindir, binapp), 'w') do |newfile|
          File.open(File.join('bin', binapp)) do |oldfile|
            # Modify the first line
            firstline = oldfile.gets
            # Preserve any options.  I.e. #!/usr/bin/ruby -w
            shebang, shebangopts = firstline.split(' ', 2)
            newfile.puts "#!#{options[:ruby]} #{shebangopts}"
            # Then dump in the rest of the file
            newfile.write(oldfile.read)
          end
        end
      else
        cp(File.join('bin', binapp), bindir, :preserve => true)
      end
      chmod(0555, File.join(bindir, binapp))
    end
  end
  
  if options[:libdir]
    libdir = File.join(destdir, options[:libdir])
    mkdir_p(libdir)
    mkdir_p(File.join(libdir, 'etch'))
    
    # Substitute ETCHVER into etch/client.rb
    # Substitute proper path into CONFIGDIR in etch/client.rb if appropriate
    File.open(File.join(libdir, 'etch', 'client.rb'), 'w') do |newfile|
      IO.foreach(File.join('lib', 'etch', 'client.rb')) do |line|
        if line =~ /^\s*VERSION/
          line.sub!(/=.*/, "= '#{ETCHVER}'")
        end
        if options[:etcdir] && line =~ /^\s*CONFIGDIR/
          line.sub!(/=.*/, "= '#{options[:etcdir]}'")
        end
        newfile.write(line)
      end
    end
    chmod(0444, File.join(libdir, 'etch', 'client.rb'))
    
    serverlibs = ['etch.rb', 'silently.rb', 'versiontype.rb']
    serverlibs.each do |serverlib|
      cp(File.join('..', 'server', 'lib', serverlib), libdir, :preserve => true)
      chmod(0444, File.join(libdir, serverlib))
    end
  end
  
  if options[:mandir]
    mandir = File.join(destdir, options[:mandir])
    man8dir = File.join(mandir, 'man8')
    mkdir_p(man8dir)
    cp(File.join('man', 'man8', 'etch.8'), man8dir, :preserve => true)
    chmod(0444, File.join(man8dir, 'etch.8'))
  end
  
  if options[:etcdir]
    etcdir = File.join(destdir, options[:etcdir])
    mkdir_p(etcdir)
    cp(File.join('etc', 'etch.conf'), etcdir, :preserve => true)
    chmod(0644, File.join(etcdir, 'etch.conf'))
    # All of the supporting config files go into a subdirectory
    etcetchdir = File.join(etcdir, 'etch')
    mkdir_p(etcetchdir)
    etcetchfiles = ['ca.pem', 'dhparams']
    etcetchfiles.each do |etcetchfile|
      cp(File.join('etc', 'etch', etcetchfile), etcetchdir, :preserve => true)
      chmod(0644, File.join(etcetchdir, etcetchfile))
    end
  end
  
  if options[:crondir]
    crondir = File.join(destdir, options[:crondir])
    mkdir_p(crondir)
    cp(File.join('etc', 'cron.d', 'etch'), crondir, :preserve => true)
    chmod(0444, File.join(crondir, 'etch'))
  end
end

desc 'Build an etch client RPM on a Red Hat box'
task :redhat => [:redhatprep, :rpm]
desc 'Prep a Red Hat box for building an RPM'
task :redhatprep do
  # Install the package which contains the rpmbuild command
  system('rpm --quiet -q rpm-build || sudo yum install rpm-build')
end
desc 'Build an etch client RPM'
task :rpm do
  #
  # Create package file structure in build root
  #
  
  rm_rf(BUILDROOT)
  
  sbindir = File.join('usr', 'sbin')
  libdir = File.join('usr', 'lib', 'ruby', 'site_ruby', '1.8')
  mandir = File.join('usr', 'share', 'man')
  etcdir = '/etc'
  crondir = File.join('etc', 'cron.d')
  copy_etch_files(BUILDROOT, :bindir => sbindir, :libdir => libdir,
                  :mandir => mandir, :etcdir => etcdir, :crondir => crondir)
  
  #
  # Prep spec file
  #
  
  spec = Tempfile.new('etchrpm')
  IO.foreach(File.join('packages', 'rpm', 'etch-client.spec')) do |line|
    line.sub!('%VER%', ETCHVER)
    spec.puts(line)
  end
  spec.close
  
  #
  # Build the package
  #
  
  system("rpmbuild -bb --buildroot #{BUILDROOT} #{spec.path}")
  
  #
  # Cleanup
  #
  
  rm_rf(BUILDROOT)
end

desc 'Build an etch client deb'
task :deb do
  #
  # Create package file structure in build root
  #
  
  rm_rf(BUILDROOT)
  
  mkdir_p(File.join(BUILDROOT, 'DEBIAN'))
  File.open(File.join(BUILDROOT, 'DEBIAN', 'control'), 'w') do |control|
    IO.foreach(File.join('packages', 'deb', 'control')) do |line|
      next if line =~ /^\s*#/  # Remove comments
      line.sub!('%VER%', ETCHVER)
      control.puts(line)
    end
  end
  
  sbindir = File.join('usr', 'sbin')
  libdir = File.join('usr', 'local', 'lib', 'site_ruby', '1.8')
  mandir = File.join('usr', 'share', 'man')
  etcdir = '/etc'
  crondir = File.join('etc', 'cron.d')
  copy_etch_files(BUILDROOT, :bindir => sbindir, :libdir => libdir,
                  :mandir => mandir, :etcdir => etcdir, :crondir => crondir)
  
  #
  # Set permissions
  #
  
  system("sudo chown -R 0:0 #{BUILDROOT}")
  
  #
  # Build the package
  #
  
  system("dpkg --build #{BUILDROOT} etch-client-#{ETCHVER}.deb")
  
  #
  # Cleanup
  #
  
  rm_rf(BUILDROOT)
end

desc 'Build etch client SysV packages for Solaris'
task :solaris => [:sysvpkg, :sysvpkgsparc]
desc 'Build an etch client SysV package'
task :sysvpkg do
  #
  # Create package file structure in build root
  #
  
  rm_rf(BUILDROOT)
  
  sbindir = File.join('usr', 'sbin')
  libdir = File.join('opt', 'csw', 'lib', 'ruby', 'site_ruby', '1.8')
  mandir = File.join('usr', 'share', 'man')
  etcdir = '/etc'
  copy_etch_files(BUILDROOT, :bindir => sbindir, :libdir => libdir,
                  :mandir => mandir, :etcdir => etcdir,
                  :ruby => '/opt/csw/bin/ruby')
  
  #
  # Prep packaging files
  #
  
  rm_rf('solbuild')
  mkdir('solbuild')
  File.open(File.join('solbuild', 'pkginfo'), 'w') do |pkginfo|
    IO.foreach(File.join('packages', 'sysv', 'pkginfo')) do |line|
      line.sub!('%VER%', ETCHVER)
      pkginfo.puts(line)
    end
  end
  File.open(File.join('solbuild', 'prototype'), 'w') do |prototype|
    prototype.puts("i pkginfo=./pkginfo")
    cp(File.join('packages', 'sysv', 'depend'), 'solbuild/depend')
    prototype.puts("i depend=./depend")
    cp(File.join('packages', 'sysv', 'postinstall.solaris'), 'solbuild/postinstall')
    chmod(0555, 'solbuild/postinstall')
    prototype.puts("i postinstall=./postinstall")
    cp(File.join('packages', 'sysv', 'postremove.solaris'), 'solbuild/postremove')
    chmod(0555, 'solbuild/postremove')
    prototype.puts("i postremove=./postremove")
    # The tail +2 removes the first line, which is the base directory
    # and doesn't need to be included in the package.
    IO.popen("find #{BUILDROOT} | tail +2 | pkgproto") do |pipe|
      pipe.each do |line|
        # Clean up the directory names
        line.sub!(BUILDROOT, '')
        # Don't force our permissions on directories
        if line =~ /^d/
          line.sub!(/\S+ \S+ \S+$/, '? ? ?')
        end
        prototype.write(line)
      end
    end
  end
  
  #
  # Build the package
  #
  
  system("cd solbuild && pkgmk -r #{BUILDROOT} -d $PWD/solbuild")
  system("pkgtrans solbuild ../OSSetch-#{ETCHVER}.pkg OSSetch")
  
  #
  # Cleanup
  #
  
  rm_rf('solbuild')
  rm_rf(BUILDROOT)
end

# On Sparc systems we're having problems with the CSW/Blastwave ruby core
# dumping when running etch.  The Sunfreeware ruby seems to work. Sunfreeware
# doesn't play well with pkg-get, so we create a bit of a hybrid.  We still
# express all the dependencies against CSW, and put our library file
# (etchclient.rb) into /opt/csw.  We modify etch to use the Sunfreeware ruby
# in /usr/local/bin, but then tell it to also look in the /opt/csw directory
# for libraries.  Users will have to manually install the Sunfreeware ruby
# package.
desc 'Build an etch client SysV package with hybrid CSW/Sunfreeware dependencies'
task :sysvpkgsparc do
  #
  # Create package file structure in build root
  #
  
  rm_rf(BUILDROOT)
  
  sbindir = File.join('usr', 'sbin')
  libdir = File.join('opt', 'csw', 'lib', 'ruby', 'site_ruby', '1.8')
  mandir = File.join('usr', 'share', 'man')
  etcdir = '/etc'
  copy_etch_files(BUILDROOT, :bindir => sbindir, :libdir => libdir,
                  :mandir => mandir, :etcdir => etcdir,
                  :ruby => '/usr/local/bin/ruby')
  
  # Since we're using the Sunfreeware ruby but CSW libraries we need to add
  # the CSW ruby library directory to the search path
  newetch = File.join(BUILDROOT, 'usr', 'sbin', 'etch.new')
  etch = File.join(BUILDROOT, 'usr', 'sbin', 'etch')
  File.open(newetch, 'w') do |newfile|
    IO.foreach(etch) do |line|
      if line =~ /unshift.*__FILE__/
        line << "$:.unshift('/opt/csw/lib/ruby/site_ruby/1.8')\n"
      end
      newfile.write(line)
    end
  end
  mv(newetch, etch)
  chmod(0555, etch)
  
  #
  # Prep packaging files
  #
  
  rm_rf('solbuild')
  mkdir('solbuild')
  File.open(File.join('solbuild', 'pkginfo'), 'w') do |pkginfo|
    IO.foreach(File.join('packages', 'sysv', 'pkginfo')) do |line|
      line.sub!('%VER%', ETCHVER)
      pkginfo.puts(line)
    end
  end
  File.open(File.join('solbuild', 'prototype'), 'w') do |prototype|
    prototype.puts("i pkginfo=./pkginfo")
    cp(File.join('packages', 'sysv', 'depend'), 'solbuild/depend')
    prototype.puts("i depend=./depend")
    cp(File.join('packages', 'sysv', 'postinstall.solaris'), 'solbuild/postinstall')
    chmod(0555, 'solbuild/postinstall')
    prototype.puts("i postinstall=./postinstall")
    cp(File.join('packages', 'sysv', 'postremove.solaris'), 'solbuild/postremove')
    chmod(0555, 'solbuild/postremove')
    prototype.puts("i postremove=./postremove")
    # The tail +2 removes the first line, which is the base directory
    # and doesn't need to be included in the package.
    IO.popen("find #{BUILDROOT} | tail +2 | pkgproto") do |pipe|
      pipe.each do |line|
        # Clean up the directory names
        line.sub!(BUILDROOT, '')
        # Don't force our permissions on directories
        if line =~ /^d/
          line.sub!(/\S+ \S+ \S+$/, '? ? ?')
        end
        prototype.write(line)
      end
    end
  end
  
  #
  # Build the package
  #
  
  system("cd solbuild && pkgmk -r #{BUILDROOT} -d $PWD/solbuild")
  system("pkgtrans solbuild ../OSSetch-#{ETCHVER}-sparc.pkg OSSetch")
  
  #
  # Cleanup
  #
  
  rm_rf('solbuild')
  rm_rf(BUILDROOT)
end

# Install based on Config::CONFIG paths
task :install, :destdir do |t, args|
  destdir = nil
  if args.destdir
    destdir = args.destdir
  else
    destdir = '/'
  end
  copy_etch_files(destdir,
                  :bindir => Config::CONFIG['sbindir'],
                  :libdir => Config::CONFIG['sitelibdir'],
                  :mandir => Config::CONFIG['mandir'],
                  :etcdir => Config::CONFIG['sysconfdir'],
                  # Can't find a better way to get the path to the current ruby
                  :ruby => File.join(Config::CONFIG['bindir'], Config::CONFIG['ruby_install_name']))
end

desc 'Fetch tarball from sourceforge'
task :fetch do
  if !File.exist?(TARBALL)
    url = "http://downloads.sourceforge.net/project/etch/etch/#{ETCHVER}/#{TARBALLFILE}"
    puts "Fetching tarball from #{url}"
    open(url) do |df|
      open(TARBALL, 'w') do |lf|
        lf.write(df.read)
      end
    end
  end
end

desc 'Prepare portfile for submission to MacPorts'
task :macport => :fetch do
  md5 = `openssl md5 #{TARBALL}`.chomp.split.last
  sha1 = `openssl sha1 #{TARBALL}`.chomp.split.last
  rmd160 = `openssl rmd160 #{TARBALL}`.chomp.split.last
  sha256 = `openssl sha256 #{TARBALL}`.chomp.split.last
  
  portfile = File.join(Dir.tmpdir, 'Portfile')
  rm_f(portfile)
  File.open(portfile, 'w') do |newfile|
    IO.foreach(File.join('packages', 'macports', 'Portfile.template')) do |line|
      line.sub!('%VER%', ETCHVER)
      line.sub!('%MD5%', md5)
      line.sub!('%SHA1%', sha1)
      line.sub!('%RMD160%', rmd160)
      line.sub!('%SHA256%', sha256)
      newfile.puts(line)
    end
  end
  puts "Portfile is #{portfile}"
end

desc 'Build rubygem package'
task :gem do
  #
  # Create package file structure in build root
  #
  
  rm_rf(BUILDROOT)
  copy_etch_files(BUILDROOT,
                  :bindir => 'bin',
                  :libdir => 'lib')
  
  #
  # Prep gemspec (renaming to Rakefile in the process)
  #
  File.open(File.join(BUILDROOT, 'Rakefile'), 'w') do |gemspec|
    IO.foreach(File.join('packages', 'gem', 'gemspec')) do |line|
      line.sub!('%VER%', ETCHVER)
      gemspec.puts(line)
    end
  end
  
  #
  # Build the package
  #
  
  system("cd #{BUILDROOT} && rake gem")
  gemglob = File.join(BUILDROOT, 'pkg', '*.gem')
  gemfile = Dir.glob(gemglob).first
  if gemfile
    mv(gemfile, Dir.tmpdir)
    puts "Gem is #{File.join(Dir.tmpdir, File.basename(gemfile))}"
  else
    warn "Gem file #{gemglob} not found!"
  end
  
  #
  # Cleanup
  #
  
  rm_rf(BUILDROOT)
end

