#!/usr/bin/python

# This is an extension to the Nautilus file manager to allow better 
# integration with the Subversion source control system.
# 
# Copyright (C) 2009 by Jason Heeris <jason.heeris@gmail.com>
# 
# RabbitVCS 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 of the License, or
# (at your option) any later version.
# 
# RabbitVCS 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 RabbitVCS;  If not, see <http://www.gnu.org/licenses/>.

"""
This is a script to build RabbitVCS packages for various distros. It is very
shell-scripty, but it does the job.

It is meant to be used from either the RabbitVCS source root or packages dir
(although it does a decent job of finding out where those places are anyway). It
should be invoked like:

make [options] <distro> <command>

You can use "make --help" to show a usage message which lists the available
commands.
"""

from optparse import OptionParser
import os, os.path, sys, shutil, re
import tempfile, subprocess
import imp

from debian import Debian
from generic import Generic

import pysvn

MODULE_NAME = "rabbitvcs"

CHANGELOG_ENTRY = "Local build via packaging script."

COMMANDS = {
    "binary" : 
        {"desc" : "Build a binary package from the current state of the source tree.",
         "method" : "build_current_binary"},
    "source" :
        {"desc" : "Build a source package from the current state of the source tree.",
         "method" : "build_current_source"},
    
    "official" :
        {"desc" : "Build an official source and binary from a tarball and current packaging.",
         "method" : "build_official_package"},
    
    "ppa" :
        {"desc" : "Build a source package for uploading to the PPA.",
         "method": "build_ppa_source"}
    }
    # modify sys.path, do import
   
def package_id(root_dir):
    """
    Uses the python stdlib "imp" library to find and load the local rabbitvcs
    module. We need to do it this way because (a) the version we are packaging
    may be different from the version we are running and (b) there may not
    actually be a version available to import.
    
    @param root_dir: the root directory of the local copy of RabbitVCS
    @type root_dir: string
    
    @return: the tuple (package_name, package_version, module_path)
    """
    (module_file, module_path, desc) = imp.find_module(MODULE_NAME, [root_dir])
    rabbitvcs = imp.load_module(MODULE_NAME, module_file, module_path, desc)    
    package_name = rabbitvcs.package_name()
    package_version = rabbitvcs.package_version()
    return (package_name, package_version, module_path)


def get_distros(basepath = None):
    """
    List the directories we can see from here. These directories represent the
    different distros we can build packages for. They will then be used to look
    up the classes that can build the packages.
    
    @param basepath: the directory in which to look for the packaging dirs - if
                     nothing is given, look in the directory of this script (not
                     the current dir)
    @type basepath: string
    
    @return: a list of non-hidden directories
    """
    if not basepath:
        basepath = os.path.dirname(__file__)
    
    dirs = os.walk(basepath).next()[1]
    
    # Remove hidden dirs
    dirs = [dr for dr in dirs if not dr.startswith(".")]
    
    return dirs

def usage():
    """
    Convenience method to return a formatted program usage string.
    """
    ustr = "usage: %prog distro command [options]\n\n"
    
    ustr += "Detected distros are: " + ", ".join(get_distros())
    
    ustr += "\n\n"
    
    ustr += "Available commands are:\n"
    
    for cmd, info in COMMANDS.items():
        ustr += cmd + ": " + info["desc"] + "\n"
    
    return ustr

# These are the classes that build the distro-specific packages. See their
# documentation for more details.
CLASSES = [Debian, Generic]


if __name__ == "__main__":
        
    parser = OptionParser(usage = usage())
    
    parser.add_option("-o", "--output-dir", dest="output_dir",
                      help = "directory for package files")
    
    parser.add_option("-p", "--pbuilder", dest="pbuilder", action="store_true",
                      default = False,
                      help = "use pbuilder for Debian/Ubuntu packages")
    
    parser.add_option("-a", "--ppa", dest="ppa", action="store_true",
                      default = False,
                      help = "prepare packages for PPA")
    
    (options, args) = parser.parse_args()

    output_dir = options.output_dir

    if len(args) < 2:
        parser.error("You must specify a distro and a command!")

    distro = sys.argv[1]
    command = sys.argv[2]
    
    root_dir = os.path.split(os.path.abspath(os.path.dirname(__file__)))[0]
    
    # If no output dir is given, use ".."
    if not output_dir:
        output_dir = os.path.split(root_dir)[0]
    
    temp_dir = None
    
    try:
    
        bld_class = None

        for cls in CLASSES:
            for dst in cls.distros:
                regex = re.compile(dst)
                if regex.match(distro):
                    bld_class = cls
                    break
        
        if bld_class is None:
            print "Could not find a builder for that distro!"
            sys.exit(1)
    
        if os.path.lexists(output_dir) and not os.path.isdir(output_dir):
            print "Output dir already exists, but is not a directory!"
            sys.exit(1)

        # Create our destination dir
        if not os.path.lexists(output_dir):
            os.mkdir(output_dir)
        
        print "Output dir: %s" % output_dir
        
        print "Distro: %s" % distro
        
        if distro not in get_distros():
            print "Distro dir not found!"
        
        print "Command: %s" % command
        
        if command not in COMMANDS.keys():
            print "Command not valid!"
            sys.exit(2)
        
        print "Original working dir: %s" % root_dir
      
        package_name = None
        package_ver = None
      
        try:
            package_name, package_ver, package_path = package_id(root_dir)            
        except ImportError:
            print "Could not detect local RabbitVCS module!"
            sys.exit(2)
        
        print "Detected RabbitVCS v%s in %s" % (package_ver, package_path)

        temp_dir = tempfile.mkdtemp(prefix=(package_name+"-"))
        
        print "Using temp dir: %s" % temp_dir

        print "Detected package name: %s, version: %s" % (package_name, package_ver)
        
        print "Using build class: %s" % bld_class

        builder = bld_class(root_dir,
                            temp_dir,
                            output_dir,
                            distro,
                            package_name,
                            package_ver,
                            use_pbuilder = options.pbuilder,
                            ppa = options.ppa)
        
        try:
            getattr(builder, COMMANDS[command]["method"])()
        except (NotImplementedError, AttributeError):
            print "Distro \'%s\' does not support command \'%s\'!" % (distro, command)
            sys.exit(1)

        sys.exit(0)
    
    finally: 
        if temp_dir:
            shutil.rmtree(temp_dir)
