#!/usr/bin/env python
#
# GNU Enterprise Common Library - Installation Procedure
#
# This file is part of GNU Enterprise.
#
# GNU Enterprise is free software; you can redistribute it
# and/or modify it under the terms of the GNU General Public
# License as published by the Free Software Foundation; either
# version 2, or (at your option) any later version.
#
# GNU Enterprise is distributed in the hope that it will be
# useful, but WITHOUT ANY WARRANTY; without even the implied
# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
# PURPOSE. See the GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public
# License along with program; see the file COPYING. If not,
# write to the Free Software Foundation, Inc., 59 Temple Place
# - Suite 330, Boston, MA 02111-1307, USA.
#
# Copyright 2001-2006 Free Software Foundation

import sys
import string
import os
from src.setup import ChangeLog
from src.utils import version

from src import TITLE, PACKAGE, VERSION


# =============================================================================
# Please keep these routines up to date
# =============================================================================

# -----------------------------------------------------------------------------
# Definition of basic parameters for distribution and installation.
# Please add new files/directories that should be installed here.
# Note that you also have to add them to MANIFEST.in.
# -----------------------------------------------------------------------------

def set_params (params):
  
  # The Work
  params ["name"]             = PACKAGE.lower ()
  params ["version"]          = VERSION
  params ["description"]      = "GNU Enterprise Common Base"
  params ["long_description"] = ""
  params ["license"]          = "GPL"

  # The Author
  params ["author"]       = "GNU Enterprise Team"
  params ["author_email"] = "gnue-dev@gnu.org"
  params ["url"]          = "http://www.gnue.org"

  # The Programs
  params ["package_dir"] = {"gnue":        "module/base",
                            "gnue.common": "src"}
  params ["scripts"] = ["scripts/gnue-schema"]

  # The Data
  # TODO: get rid of setupext!
  params ["data_files"] = \
    [Data_Files (base_dir = 'install_data',
                 copy_to = 'share/gnue',
                 template = ['recursive-include images *.png',
                             'recursive-include images *.bmp'],
                 preserve_path = 1),
     Data_Files (base_dir = 'install_config',
                 copy_to = '',
                 template = ['recursive-include etc *',
                             'prune etc/.svn'],
                 preserve_path = 0),
     Data_Files (base_dir = 'install_data',
                 copy_to = 'share/man/man1',
                 template = ['recursive-include doc/man *.1'],
                 preserve_path = 0),
     Data_Files (base_dir = 'install_data',
                 copy_to = 'share/doc/gnue-common',
                 template = ['recursive-include doc *',
                             'prune doc/.svn',
                             'prune doc/man',
                             'prune doc/technotes'],
                 strip_dirs = 1, # This is a number, not true/false
                 preserve_path = 1)]

# -----------------------------------------------------------------------------
# Build files to be distributed and installed:
# Should generate the files that go in a distribution but aren't in SVN.
# Gets called on sdist (always) and on build/install (only when run from SVN).
# FIXME: Better to build files in target directory directly.
# -----------------------------------------------------------------------------

def build_files (action):

  if os.name == 'posix':

    # First check if we have everything installed we need to build the
    # distribution

    if os.path.isdir ('po'):
      # xgettext
      if os.system ("pygettext --version > /dev/null") != 0:
        log.fatal("Could not find 'pygettext'. Strange.")
        log.fatal("It should be included in the Python distribution.")
        sys.exit (1)

      # msgmerge
      if os.system ("msgmerge --version > /dev/null") != 0:
        log.fatal("Could not find 'msgmerge'. Please install Gettext.")
        sys.exit (1)

      # msgfmt
      if os.system ("msgfmt --version > /dev/null") != 0:
        log.fatal("Could not find 'msgfmt'. Please install Gettext.")
        sys.exit (1)

    # -------------------------------------------------------------------------

    if action == 'sdist':
      # build ChangeLog file
      log.info('building ChangeLog')
      ChangeLog.build ()

    # build translations
    if os.path.isdir ('po'):
      log.info("building translations")
      if os.system ("cd po && make gmo") != 0:
        sys.exit (1)

  else:
    # on non posix systems just run msgfmt on existing .po files
    if os.path.isdir ('po'):
      # msgfmt.py
      argv0_path = os.path.dirname(os.path.abspath(sys.executable))
      sys.path.append(argv0_path + "\\tools\\i18n")
      
      msgfmtOK = 0
      try:
        import msgfmt
        msgfmtOK = 1
      except:
        pass
      
      if msgfmtOK == 1:
        # pygettext.py exist in Python, but no msgmerge, so
        # just create a placeholder...
        potfile = open('po/'+ PACKAGE.lower() +'.pot', 'w')
        potfile.write("#placeholder")
        potfile.close()

        # build translations
        log.info("building translations")
        for f in os.listdir('po'):
          if f[-3:] == '.po':
            msgfmt.make ('po/'+f, 'po/'+f[:-3]+'.gmo')
            msgfmt.MESSAGES = {}

# -----------------------------------------------------------------------------
# Build the svnrev.py file.
# Gets called on sdist (always) and on build/install (only when run from SVN).
# FIXME: Could be done with svnrev.py.dist_template, but it wouldn't happen on
# build in that case.
# -----------------------------------------------------------------------------

def build_svnrev(filename):

    log.info("building svnrev.py")
    output = open(filename, 'w')
    output.write('svnrev = %r' % version.get_svn_revision('src'))
    output.close()


# -----------------------------------------------------------------------------
# Check dependencies for installation:
# Should sys.exit(1) in case any requirement isn't met.
# Gets called on install.
# -----------------------------------------------------------------------------

def check_dependencies ():

  # seems like we have really none :-)
  pass

# =============================================================================
# No changes should be required below this line
# =============================================================================

from distutils import log
from distutils.core import setup
from distutils.filelist import FileList
from distutils.util import change_root
from distutils.sysconfig import get_python_lib
import distutils.command.sdist
import distutils.command.build
import distutils.command.install
import time
from setupext import Data_Files, install_Data_Files

# -----------------------------------------------------------------------------
# Check Python version
# -----------------------------------------------------------------------------

try:
  if sys.hexversion < 0x02030000:
    raise AttributeError
except AttributeError:
  print "-" * 70
  print """
  You are running Python %s.

  GNU Enterprise requires at least Python 2.3.
  If you have a later version installed, you should run setup.py
  against that version. For example, if you have Python 2.3
  installed, you may need to run:

       python2.3 setup.py
""" % string.split(sys.version)[0]
  print "-" * 70
  sys.exit (1)

# -----------------------------------------------------------------------------
# Find out whether or not to use our setup.cfg
# -----------------------------------------------------------------------------

have_prefix = 0

for arg in sys.argv:
  if arg == "--prefix" or arg [:9] == "--prefix=" or \
     arg == "--home"   or arg [:7] == "--home=":
    have_prefix = 1

# Was setup.cfg specified on the command line?
setupcfg = "setup.cfg.in"
have_setupcfg = 0
try:
  index = sys.argv.index ('--setup-cfg')
  setupcfg = sys.argv [index + 1]
  sys.argv.pop (index)
  sys.argv.pop (index)
  have_setupcfg = 1

except ValueError:
  pass

if (os.name == "posix" and not have_prefix and "install" in sys.argv) \
   or have_setupcfg:
  os.system ("cp %s setup.cfg" % setupcfg)
else:
  try:
    os.remove ("setup.cfg")
  except:
    pass

# =============================================================================
# sdist: build files to be distributed first
# =============================================================================

class sdist (distutils.command.sdist.sdist):

  def run (self):
    build_files ('sdist')
    distutils.command.sdist.sdist.run (self)

  def prune_file_list(self):
    distutils.command.sdist.sdist.prune_file_list(self)
    self.filelist.exclude_pattern('*.dist_template', anchor=0)

  def make_release_tree (self, base_dir, files):
    distutils.command.sdist.sdist.make_release_tree (self, base_dir, files)
    self.process_templates(base_dir)
    build_svnrev(os.path.join(base_dir, 'src', 'svnrev.py'))

  def process_templates(self, target):

    # Build list of files to be processed.
    filelist = FileList()
    if filelist.include_pattern('*.dist_template', anchor=0) == 0:
        # Nothing to do.
        return

    # FIXME: For compatibility with old packages not yet using the version
    # module. Change to unconditional import in gnue-common 0.8.
    try:
        from src import version
    except:
        return

    # List of keywords to replace.
    keywords = {
            ':PACKAGE:': self.distribution.get_name(),
            ':TITLE:': self.distribution.get_description(),
            ':VERSION:': self.distribution.get_version(),
            ':MAJOR:': str(version.major),
            ':MINOR:': str(version.minor),
            ':PHASE:': str(version.phase),
            ':BUILD:': str(version.build),
            ':SVN:': str(version.svn),
            ':DATE_ISO:': time.strftime('%Y-%m-%d', time.gmtime()),
            ':DATE_RFC:': time.strftime('%a, %d %b %Y', time.gmtime()),
            ':TIME:': time.strftime('%H:%M:%S', time.gmtime())}
    # Hack for version numbering schemes that are limited to x.y.z.
    if version.phase == 'final':
        keywords[':FINAL:'] = str(version.build)
    else:
        keywords[':FINAL:'] = '0'

    for src in filelist.files:
        dst = os.path.join(target, src[:-14])
        args = (src, dst, keywords)
        self.execute(self.__process_template, args,
                "generating %s from %s" % (dst, src))

  def __process_template(self, src, dst, keywords):
    infile = open(src, 'r')
    content = infile.read()
    infile.close()
    for keyword, value in keywords.iteritems():
        content = content.replace(keyword, value)
    outfile = open(dst, 'w')
    outfile.write(content)
    outfile.close()
    # Let destination file have the same mode than the source file.
    os.chmod(dst, os.stat(src).st_mode)


# =============================================================================
# build: if done from SVN, build files to be installed first
# =============================================================================

class build (distutils.command.build.build):

  def run (self):
    # TODO: Some smarter way to generate files. Maybe build a sdist object?
    if not os.path.isfile ("PKG-INFO"):         # downloaded from SVN?
      build_files ('build')
    distutils.command.build.build.run (self)
    if not os.path.isfile ("PKG-INFO"):
      build_svnrev(os.path.join(self.build_lib, 'gnue', 'common', 'svnrev.py'))

# =============================================================================
# install: New option, generate path dependent files
# =============================================================================

class install (distutils.command.install.install):

  user_options = distutils.command.install.install.user_options
  i = 0
  for option in user_options:
    i = i + 1
    if option [0] == "install-data=":
      user_options.insert (i, ("install-config=", None,
                             "installation directory for configuration files"))
      break

  # ---------------------------------------------------------------------------
  # Initalize options
  # ---------------------------------------------------------------------------

  def initialize_options (self):
    distutils.command.install.install.initialize_options (self)
    self.install_config = None

  # ---------------------------------------------------------------------------
  # Finalize options (i.e. provide default if not set by user)
  # ---------------------------------------------------------------------------

  def finalize_options (self):
    distutils.command.install.install.finalize_options (self)

    # try to set a reasonable default for --install-config
    if self.install_config is None:
      if self.home == "/usr" or self.prefix == "/usr":
        self.install_config = "/etc/gnue"
        if self.root:
          self.install_config = change_root (self.root, self.install_config)
      elif self.home == "/usr/local" or self.prefix == "/usr/local":
        self.install_config = "/usr/local/etc/gnue"
        if self.root:
          self.install_config = change_root (self.root, self.install_config)
      elif self.home == "/opt/gnue" or self.prefix == "/opt/gnue":
        self.install_config = "/etc/opt/gnue"
        if self.root:
          self.install_config = change_root (self.root, self.install_config)
      else:
        self.install_config = os.path.join (self.install_data, "etc")

  # ---------------------------------------------------------------------------
  # Quote a path: remove --root and duplicate backslashes
  # ---------------------------------------------------------------------------

  def __quote (self, path):
        return self.__remove_root(path).replace("\\","\\\\")

  def __remove_root (self, path):
        p = path
        if self.root:
            r = self.root
            if r [-1] == os.sep:
                r = r [:-1]
            p = p[len(r):]
        if p.endswith(os.sep):
            p = p[:-1]
        return p

  # ---------------------------------------------------------------------------
  # Create gnue.pth
  # ---------------------------------------------------------------------------

  def __write_gnue_pth (self):

    # we need gnue.pth only if we don't install in python's search path
    if self.__remove_root(self.install_lib) not in sys.path:
      # where to install gnue.pth
      site_dir = get_python_lib ()
      if self.root:
        # add --root to target directory for gnue.py
        site_dir = change_root (self.root, site_dir)

      # especially if we have --root, the directory might not exist
      if not os.path.isdir (site_dir):
        os.makedirs (site_dir)

      # now create the file
      file = os.path.join (site_dir, "gnue.pth")
      log.info("trying to create %s" % file)
      try:
        output = open (file, "w")
        output.write (self.__quote (self.install_lib))
        output.close ()
        # It worked. Turn off error message about installing into directory
        # not in path.
        sys.path.append (self.install_lib)
        # Record file
        self.__outputs.append (file)
      except:
        pass

  # ---------------------------------------------------------------------------
  # Create paths.py
  # ---------------------------------------------------------------------------

  def __write_paths_py (self):
    dir = os.path.join (self.install_lib, "gnue")
    if not os.path.isdir (dir):
      os.makedirs (dir)
    file = os.path.join (dir, "paths.py")
    log.info("creating %s" % file)
    output = open (file, "w")
    output.write ("# This file was generated during installation.\n")
    output.write ("import os, sys\n")
    output.write ("\n")
    output.write ("lib     = \"%s\"\n" % self.__quote (self.install_lib))
    output.write ("scripts = \"%s\"\n" % self.__quote (self.install_scripts))
    output.write ("\n")
    output.write ("############################\n")
    output.write ("# This is a workaround for McMillan packaging!!!\n")
    output.write ("# McMillan sets sys.frozen=1 in packaged exe file.\n")
    output.write ("############################\n")
    output.write ("if hasattr(sys, 'frozen') and sys.platform == 'win32':\n")
    output.write ("  data    = os.path.dirname(os.path.dirname(sys.argv[0]))\n")
    output.write ("  config  = os.path.join(data, 'etc')\n")
    output.write ("else:\n")
    output.write ("  data    = \"%s\"\n" % self.__quote (self.install_data))
    output.write ("  config  = \"%s\"\n" % self.__quote (self.install_config))
    output.close ()
    # Record file
    self.__outputs.append (file)

  # ---------------------------------------------------------------------------
  # Install translations by copying the files to the correct location
  # ---------------------------------------------------------------------------

  def __install_translations (self):

    if os.path.isdir ('po'):
      # copy files
      for f in os.listdir ('po'):
        if f [-4:] == '.gmo':
          src = os.path.join ('po', f)
          dst = os.path.join (self.install_data, 'share', 'locale', f [:-4],
                              'LC_MESSAGES')
          self.mkpath (dst)
          dst = os.path.join (dst, PACKAGE.lower () + '.mo')
          self.copy_file (src, dst)
          # Record file
          self.__outputs.append (dst)

  # ---------------------------------------------------------------------------
  # install.run: generate and install path dependent files
  # ---------------------------------------------------------------------------

  def run (self):
    check_dependencies ()

    self.__outputs = []

    self.__write_gnue_pth ()

    self.__write_paths_py ()

    self.__install_translations ()

    distutils.command.install.install.run (self)

  # ---------------------------------------------------------------------------
  # install.get_outputs: list all installed files
  # ---------------------------------------------------------------------------

  def get_outputs (self):
    return distutils.command.install.install.get_outputs (self) \
           + self.__outputs

# -----------------------------------------------------------------------------
# Get all packages in a directory
# -----------------------------------------------------------------------------

def get_packages (directory, package):
  content = os.listdir (directory)
  result = []
  if "__init__.py" in content:
    result = [package]
    for name in content:
      fullname = os.path.join (directory, name)
      if os.path.isdir (fullname):
        result = result + get_packages (fullname, package + "." + name)
  return result

# -----------------------------------------------------------------------------
# Call the actual setup routine
# -----------------------------------------------------------------------------

setup_params = {}

set_params (setup_params)

if not setup_params.has_key ("packages"):
  packages = []
  for module, directory in setup_params["package_dir"].items ():
    packages = packages + get_packages (directory, module)
  setup_params ["packages"] = packages

setup (name             = setup_params ["name"],
       version          = setup_params ["version"],
       description      = setup_params ["description"],
       long_description = setup_params ["long_description"],
       author           = setup_params ["author"],
       author_email     = setup_params ["author_email"],
       url              = setup_params ["url"],
       license          = setup_params ["license"],
       packages         = setup_params ["packages"],
       package_dir      = setup_params ["package_dir"],
       scripts          = setup_params ["scripts"],
       data_files       = setup_params ["data_files"],

       # Override certain command classes with our own ones
       cmdclass = {"sdist": sdist,
                   "build": build,
                   "install": install,
                   "install_data": install_Data_Files})

# -----------------------------------------------------------------------------
# Clean up
# -----------------------------------------------------------------------------

if os.path.isfile ("setup.cfg"):
  os.remove ("setup.cfg")
