# $Id: gartest.py 11860 2010-12-09 03:47:03Z skayser $
#
# Style guide:
# http://code.google.com/p/soc/wiki/PythonStyleGuide

import logging
import os
import os.path
import shutil
import subprocess
import tempfile
import opencsw
import Cheetah.Template

"""A module used to do end-to-end testing of GAR."""

MAKEFILE_TMPL = """# GAR Makefile generated by $test_id
#for varname in $sorted($garvars)
$varname = $garvars[$varname]
#end for
#if $blurb
define BLURB
  $blurb
endef
#end if
include gar/category.mk
#if $install_files
post-install-modulated:
#for filedir_name, directory, file_name, content in $install_files
\tginstall -m 755 -d \$(DESTDIR)$directory
\tginstall -m 644 \$(FILEDIR)/$filedir_name \$(DESTDIR)$directory/$file_name
#end for
#end if
# GAR recipe ends here.
"""
TMPDIR_PREFIX = "gartest-"
# FIXME: This information should be pulled out from GAR
DIR_PKG_OUT_DIR = os.path.join(os.environ["HOME"], "spool.5.9-sparc")


class Error(Exception):
  pass


class GarBuild(object):

  def __init__(self, build_dir):
    self.built = False
    self.packages = None
    self.cleanup = True
    self.build_dir = build_dir

  def GetDebugHelp(self):
    """To be overriden in subclasses."""
    return ("When subclassing GarBuild, add here "
            "information about how to reproduce\n"
            "the build problem.")

  def Build(self):
    if self.built:
      return 0
    # There's a need to turn off the use of checkpkg, as it needs a pkg.gz
    # file, and the dirpackage target doesn't create it.  But it's not
    # a problem, since the built package needs to be examined in a very
    # specific way, we don't want to run all the tests against it.
    #
    # Hence, "ENABLE_CHECK=".
    args = ["gmake", "ENABLE_CHECK=", "dirpackage"]
    gar_proc = subprocess.Popen(args, cwd=self.build_dir,
                                stdout=subprocess.PIPE,
                                stderr=subprocess.PIPE)
    stdout, stderr = gar_proc.communicate()
    ret = gar_proc.wait()
    if ret:
      print "ERROR: GAR run has failed."
      print stdout
      print stderr
      self.built = False
      # To allow debugging, don't clean up, leaving the files for inspection.
      self.cleanup = False
      print self.GetDebugHelp()
    else:
      self.built = True
    return ret

  def GetBuiltPackages(self):
    if not self.built:
      raise Error("The packages have not been built yet.")
    args = ["gmake", "pkglist"]
    gar_proc = subprocess.Popen(args, cwd=self.build_dir, stdout=subprocess.PIPE)
    stdout, stderr = gar_proc.communicate()
    unused_ret = gar_proc.wait()
    pkglist = []
    for line in stdout.splitlines():
      # directory, catalogname, pkgname
      pkglist.append(tuple(line.split("\t")))
    self.packages = [
        opencsw.DirectoryFormatPackage(
          os.path.join(DIR_PKG_OUT_DIR, z))
        for x, y, z in pkglist]
    return self.packages

  def GetFirstBuiltPackage(self):
    """Returns only the first package. Easy to use."""
    packages = self.GetBuiltPackages()
    if packages:
      return packages[0]


class DynamicGarBuild(GarBuild):
  """Represents a GAR build.

  Can create a GAR build and execute it.
  """
  def __init__(self):
    build_dir = tempfile.mkdtemp(prefix=TMPDIR_PREFIX)
    super(DynamicGarBuild, self).__init__(build_dir)
    self.filedir = os.path.join(self.build_dir, "files")
    self.makefile_filename = os.path.join(self.build_dir, "Makefile")
    os.mkdir(self.filedir)
    self.install_files = []
    self.garvars = {
        "NAME": "testbuild",
        "DESCRIPTION": u"A test package from %s" % self,
        "CATEGORIES": "lib",
        "CONFIGURE_ARGS": "$(DIRPATHS)",
        "SPKG_SOURCEURL": "http://www.opencsw.org/",
        "MASTER_SITES": "",
        "VERSION": "0.0.1",
        "CONFIGURE_SCRIPTS": "",
        "BUILD_SCRIPTS": "",
        "TEST_SCRIPTS": "",
        "INSTALL_SCRIPTS": "",
    }
    garsrc = os.path.join(os.getcwd(), os.path.dirname(__file__), "..", "..")
    self.tmpldata = {
        "garvars": self.garvars,
        "garsrc": garsrc,
        "blurb": None,
        "install_files": self.install_files,
        "build_dir": self.build_dir,
        "test_id": "$Id: gartest.py 11860 2010-12-09 03:47:03Z skayser $",
    }
    os.symlink(self.tmpldata["garsrc"], os.path.join(self.build_dir, "gar"))

  def SetGarVariable(self, varname, value):
    self.garvars[varname] = value

  def WriteGarFiles(self):
    for filedir_name, directory, filename, content in self.install_files:
      file_path = os.path.join(self.filedir, filedir_name)
      # print "Writing to %s" % file_path
      fp = open(file_path, "w")
      fp.write(content)
      fp.close()
    searchlist = [self.tmpldata]
    t = Cheetah.Template.Template(MAKEFILE_TMPL, searchlist)
    # For easy debugging, but only until we find a better solution, because it
    # clutters the screen.
    # print t
    fp = open(self.makefile_filename, "w")
    fp.write(str(t))
    fp.close()

  def AddInstallFile(self, file_path, content):
    """Creates a file in the package, with the given path and content."""
    filedir_name = file_path.replace("/", "-")
    directory, file_name = os.path.split(file_path)
    self.install_files.append((filedir_name, directory,
                               file_name, content))

  def GetDebugHelp(self):
    return ("Please look into %s.\n"
            "Remember to remove it when you're done debugging."
            % repr(self.build_dir))

  def __del__(self):
    if self.cleanup:
      if os.path.isdir(self.build_dir):
        shutil.rmtree(self.build_dir)
    else:
      logging.warn("Not removing files from %s", repr(self.build_dir))


class StaticGarBuild(GarBuild):

  def __init__(self, build_dir):
    super(StaticGarBuild, self).__init__(build_dir)

  def GetDebugHelp(self):
    return ("Please look into %s.\n"
            % repr(self.build_dir))

  def __del__(self):
    if self.cleanup:
      if os.path.isdir(self.build_dir):
        shutil.rmtree(os.path.join(self.build_dir, "work"))

# vim:set ts=2 sts=2 sw=2 expandtab:
