#!/usr/bin/env  python
__license__   = 'GPL v3'
__copyright__ = '2008, Kovid Goyal kovid@kovidgoyal.net'
__docformat__ = 'restructuredtext en'

'''
Freeze app into executable using py2exe.
'''
QT_DIR           = 'C:\\Qt\\4.5.2'
LIBUSB_DIR       = 'C:\\libusb'
LIBUNRAR         = 'C:\\Program Files\\UnrarDLL\\unrar.dll'
PDFTOHTML        = 'C:\\cygwin\\home\\kovid\\poppler-0.10.6\\rel\\pdftohtml.exe'
POPPLER          = 'C:\\cygwin\\home\\kovid\\poppler'
IMAGEMAGICK_DIR  = 'C:\\ImageMagick'
PDFTK            = 'C:\\pdftk.exe'
PODOFO           = 'C:\\podofo'
FONTCONFIG_DIR   = 'C:\\fontconfig'
VC90             = r'C:\VC90.CRT'

import sys

def fix_module_finder():
    # ModuleFinder can't handle runtime changes to __path__, but win32com uses them
    import py2exe.mf as modulefinder
    import win32com
    for p in win32com.__path__[1:]:
        modulefinder.AddPackagePath("win32com", p)
    for extra in ["win32com.shell"]: #,"win32com.mapi"
        __import__(extra)
        m = sys.modules[extra]
        for p in m.__path__[1:]:
            modulefinder.AddPackagePath(extra, p)


import os, shutil, zipfile, glob, re
from distutils.core import setup
from setup import __version__ as VERSION, __appname__ as APPNAME, scripts, \
    basenames, SRC, Command

BASE_DIR = os.path.dirname(SRC)
ICONS = [os.path.abspath(os.path.join(BASE_DIR, 'icons', i)) for i in ('library.ico', 'viewer.ico')]
for icon in ICONS:
    if not os.access(icon, os.R_OK):
        raise Exception('No icon at '+icon)

VERSION = re.sub('[a-z]\d+', '', VERSION)
WINVER = VERSION+'.0'

PY2EXE_DIR = os.path.join(BASE_DIR, 'build','py2exe')

info = warn = None

class Win32Freeze(Command):

    def run(self, opts):
        global info, warn
        info, warn = self.info, self.warn
        main()

BOOT_COMMON = '''\
import sys, os
if sys.frozen == "windows_exe":
    class Stderr(object):
        softspace = 0
        _file = None
        _error = None
        def write(self, text, alert=sys._MessageBox, fname=os.path.expanduser('~\calibre.log')):
            if self._file is None and self._error is None:
                try:
                    self._file = open(fname, 'wb')
                except Exception, details:
                    self._error = details
                    import atexit
                    atexit.register(alert, 0,
                                    ("The logfile %s could not be opened: "
                                    "\\n%s\\n\\nTry setting the HOME environment "
                                    "variable to a directory for which you "
                                    "have write permission.") % (fname, details),
                                    "Errors occurred")
                else:
                    import atexit
                    #atexit.register(alert, 0,
                    #                "See the logfile '%s' for details" % fname,
                    #                "Errors occurred")
            if self._file is not None:
                self._file.write(text)
                self._file.flush()
        def flush(self):
            if self._file is not None:
                self._file.flush()

    #del sys._MessageBox
    #del Stderr

    class Blackhole(object):
        softspace = 0
        def write(self, text):
            pass
        def flush(self):
            pass
    sys.stdout = Stderr()
    sys.stderr = Stderr()
    del Blackhole

# Disable linecache.getline() which is called by
# traceback.extract_stack() when an exception occurs to try and read
# the filenames embedded in the packaged python code.  This is really
# annoying on windows when the d: or e: on our build box refers to
# someone elses removable or network drive so the getline() call
# causes it to ask them to insert a disk in that drive.
import linecache
def fake_getline(filename, lineno, module_globals=None):
    return ''
linecache.orig_getline = linecache.getline
linecache.getline = fake_getline

del linecache, fake_getline

fenc = sys.getfilesystemencoding( )
base = os.path.dirname(sys.executable.decode(fenc))
sys.resources_location = os.path.join(base, 'resources')
sys.extensions_location = os.path.join(base, 'plugins')


del sys
'''

try:
    import py2exe
    bc = py2exe.build_exe.py2exe
except ImportError:
    py2exe = object
    bc = object

class BuildEXE(bc):

    def run(self):
        py2exe.build_exe.py2exe.run(self)
        info('\nAdding plugins...')
        tgt = os.path.join(self.dist_dir, 'plugins')
        if not os.path.exists(tgt):
            os.mkdir(tgt)
        for f in glob.glob(os.path.join(BASE_DIR, 'src', 'calibre', 'plugins', '*.dll')):
            shutil.copyfile(f, os.path.join(self.dist_dir, os.path.basename(f)))
        for f in glob.glob(os.path.join(BASE_DIR, 'src', 'calibre', 'plugins', '*.pyd')):
            shutil.copyfile(f, os.path.join(tgt, os.path.basename(f)))
        for f in glob.glob(os.path.join(BASE_DIR, 'src', 'calibre', 'plugins', '*.manifest')):
            shutil.copyfile(f, os.path.join(tgt, os.path.basename(f)))
        shutil.copyfile('LICENSE', os.path.join(self.dist_dir, 'LICENSE'))


        info('\nAdding resources...')
        tgt = os.path.join(self.dist_dir, 'resources')
        if os.path.exists(tgt):
            shutil.rmtree(tgt)
        shutil.copytree(os.path.join(BASE_DIR, 'resources'), tgt)

        info('\nAdding QtXml4.dll')
        shutil.copyfile(os.path.join(QT_DIR, 'bin', 'QtXml4.dll'),
                            os.path.join(self.dist_dir, 'QtXml4.dll'))
        info('\nAdding Qt plugins...')
        qt_prefix = QT_DIR
        plugdir = os.path.join(qt_prefix, 'plugins')
        for d in ('imageformats', 'codecs', 'iconengines'):
            info(d)
            imfd = os.path.join(plugdir, d)
            tg = os.path.join(self.dist_dir, d)
            if os.path.exists(tg):
                shutil.rmtree(tg)
            shutil.copytree(imfd, tg)

        info('Adding main scripts')
        f = zipfile.ZipFile(os.path.join(PY2EXE_DIR, 'library.zip'), 'a', zipfile.ZIP_DEFLATED)
        for i in scripts['console'] + scripts['gui']:
            f.write(i, i.partition('\\')[-1])
        f.close()

        info('Copying icons')
        for icon in ICONS:
            shutil.copyfile(icon, os.path.join(PY2EXE_DIR, os.path.basename(icon)))

        print
        print 'Adding third party dependencies'
        tdir = os.path.join(PY2EXE_DIR, 'driver')
        os.makedirs(tdir)
        for pat in ('*.dll', '*.sys', '*.cat', '*.inf'):
            for f in glob.glob(os.path.join(LIBUSB_DIR, pat)):
                shutil.copyfile(f, os.path.join(tdir, os.path.basename(f)))
        print '\tAdding unrar'
        shutil.copyfile(LIBUNRAR, os.path.join(PY2EXE_DIR, os.path.basename(LIBUNRAR)))
        print '\tAdding poppler'
        for x in ('bin\\pdftohtml.exe', 'bin\\poppler-qt4.dll',
            'bin\\freetype.dll', 'bin\\jpeg62.dll'):
            shutil.copyfile(os.path.join(POPPLER, x),
                    os.path.join(PY2EXE_DIR, os.path.basename(x)))
        print '\tAdding podofo'
        for f in glob.glob(os.path.join(PODOFO, '*.dll')):
            shutil.copyfile(f, os.path.join(PY2EXE_DIR, os.path.basename(f)))

        print '\tAdding ImageMagick'
        for f in os.listdir(IMAGEMAGICK_DIR):
            shutil.copyfile(os.path.join(IMAGEMAGICK_DIR, f), os.path.join(PY2EXE_DIR, f))
        print '\tCopying fontconfig'
        for f in glob.glob(os.path.join(FONTCONFIG_DIR, '*')):
            tgt = os.path.join(PY2EXE_DIR, os.path.basename(f))
            if os.path.isdir(f):
                shutil.copytree(f, tgt)
            else:
                shutil.copyfile(f, tgt)

        print
        print 'Doing DLL redirection' # See http://msdn.microsoft.com/en-us/library/ms682600(VS.85).aspx
        for f in glob.glob(os.path.join(PY2EXE_DIR, '*.exe')):
            open(f + '.local', 'w').write('\n')

        print
        print 'Adding Windows runtime dependencies...'
        for f in glob.glob(os.path.join(VC90, '*')):
            shutil.copyfile(f, os.path.join(PY2EXE_DIR, os.path.basename(f)))


def exe_factory(dest_base, script, icon_resources=None):
    exe = {
           'dest_base'       : dest_base,
           'script'          : script,
           'name'            : dest_base,
           'version'         : WINVER,
           'description'     : 'calibre - E-book library management',
           'author'          : 'Kovid Goyal',
           'copyright'       : '(c) Kovid Goyal, 2008',
           'company'         : 'kovidgoyal.net',
           }
    if icon_resources is not None:
        exe['icon_resources'] = icon_resources
    return exe

def main(args=sys.argv):
    sys.argv[1:2] = ['py2exe']
    if os.path.exists(PY2EXE_DIR):
        shutil.rmtree(PY2EXE_DIR)

    fix_module_finder()

    boot_common = os.path.join(sys.prefix, 'Lib', 'site-packages', 'py2exe',
    'boot_common.py')
    open(boot_common, 'wb').write(BOOT_COMMON)

    console = [exe_factory(basenames['console'][i], scripts['console'][i])
               for i in range(len(scripts['console']))]
    setup(
          cmdclass = {'py2exe': BuildEXE},
          windows = [
                     exe_factory(APPNAME, scripts['gui'][0], [(1, ICONS[0])]),
                     exe_factory('lrfviewer', scripts['gui'][1], [(1, ICONS[1])]),
                     exe_factory('ebook-viewer', scripts['gui'][2], [(1, ICONS[1])]),
                    ],
          console = console,
          options = { 'py2exe' : {'compressed': 1,
                                  'optimize'  : 2,
                                  'dist_dir'  : PY2EXE_DIR,
                                  'includes'  : [
                                             'sip', 'pkg_resources', 'PyQt4.QtSvg',
                                             'mechanize', 'ClientForm', 'wmi',
                                             'win32file', 'pythoncom',
                                             'email.iterators',
                                             'email.generator',
                                             'win32process', 'win32api', 'msvcrt',
                                             'win32event', 'calibre.ebooks.lrf.any.*',
                                             'sqlite3.dump',
                                             'BeautifulSoup', 'pyreadline',
                                             'pydoc', 'IPython.Extensions.*',
                                             'calibre.web.feeds.recipes.*',
                                             'calibre.gui2.convert.*',
                                             'calibre.ebooks.lrf.fonts.prs500.*',
                                             'PyQt4.QtWebKit', 'PyQt4.QtNetwork',
                                             ],
                                  'packages'  : ['PIL', 'lxml', 'cherrypy',
                                                 'dateutil', 'dns'],
                                  'excludes'  : ["Tkconstants", "Tkinter", "tcl",
                                                 "_imagingtk", "ImageTk", "FixTk"
                                                ],
                                  'dll_excludes' : ['mswsock.dll'],
                                 },
                    },

          )
    return 0



