<reponame>ccgreen13/winpython
# -*- coding: utf-8 -*-
#
# Copyright © 2012 <NAME>
# Licensed under the terms of the MIT License
# (see winpython/__init__.py for details)

"""
WinPython build script

Created on Sun Aug 12 11:17:50 2012
"""

from __future__ import print_function

import os
import os.path as osp
import re
import subprocess
import shutil
import sys

from guidata import disthelpers as dh

# Local imports
from winpython import wppm, utils
import diff


CHANGELOGS_DIR = osp.join(osp.dirname(__file__), 'changelogs')
assert osp.isdir(CHANGELOGS_DIR)


#==============================================================================
# How to prepare the MinGW package:
#==============================================================================
#
# * download and install MinGW using the latest mingw-get-inst-YYYYMMDD.exe
#   (the default target installation directory is C:\MinGW) and install the 
#   C/C++/Fortran compilers
# * create WinPython MinGW32 directory %WINPYTHONBASEDIR%\tools.win32\mingw32
#   (where the WINPYTHONBASEDIR environment variable points to your WinPython 
#    base directory -- see function `make_winpython` below)
# * explore the target MinGW installation directory (default: C:\MinGW)
# * copy the `bin`, `doc`, `include`, `lib` and `libexec` folders from 
#   C:\MinGW to %WINPYTHONBASEDIR%\tools.win32\mingw32 (with MinGW 4.6.2, 
#   the overall size should be around 151 MB for 1435 files and 63 folders):
#   * %WINPYTHONBASEDIR%\tools.win32\mingw32\bin
#   * %WINPYTHONBASEDIR%\tools.win32\mingw32\doc
#   * %WINPYTHONBASEDIR%\tools.win32\mingw32\include
#   * %WINPYTHONBASEDIR%\tools.win32\mingw32\lib
#   * %WINPYTHONBASEDIR%\tools.win32\mingw32\libexec


#==============================================================================
# How to prepare the gettext package:
#==============================================================================
#
# * download the latest gettext binaries for win32 (the latest should still be 
#   from 2005... anyway)
# * add the missing 'libiconv2.dll' by copying the 'libiconv-2.dll' from MinGW
#   and renaming to 'libiconv2.dll'


def get_drives():
    """Return all active drives"""
    import win32api
    return win32api.GetLogicalDriveStrings().split('\000')[:-1]

def get_nsis_exe():
    """Return NSIS executable"""
    localdir = osp.join(sys.prefix, os.pardir, os.pardir)
    for drive in get_drives():
        for dirname in (r'C:\Program Files', r'C:\Program Files (x86)',
                        drive+r'PortableApps\NSISPortableANSI',
                        drive+r'PortableApps\NSISPortable',
                        osp.join(localdir, 'NSISPortableANSI'),
                        osp.join(localdir, 'NSISPortable'),
                    ):
            for subdirname in ('.', 'App'):
                exe = osp.join(dirname, subdirname, 'NSIS', 'makensis.exe')
                include = osp.join(dirname, subdirname, 'NSIS', 'include')
                if osp.isfile(exe) and\
                   osp.isfile(osp.join(include, 'TextReplace.nsh')):
                    return exe
    else:
        raise RuntimeError("NSIS (with TextReplace plugin) is not installed "\
                           "on this computer.")

NSIS_EXE = get_nsis_exe()


def replace_in_nsis_file(fname, data):
    """Replace text in line starting with *start*, from this position:
    data is a list of (start, text) tuples"""
    fd = open(fname, 'U')
    lines = fd.readlines()
    fd.close()
    for idx, line in enumerate(lines):
        for start, text in data:
            if start not in ('Icon', 'OutFile') and not start.startswith('!'):
                start = '!define ' + start
            if line.startswith(start):
                lines[idx] = line[:len(start)+1] + ('"%s"' % text) + '\n'
    fd = open(fname, 'w')
    fd.writelines(lines)
    fd.close()

def build_nsis(srcname, dstname, data):
    """Build NSIS script"""
    portable_dir = osp.join(osp.dirname(__file__), 'portable')
    shutil.copy(osp.join(portable_dir, srcname), dstname)
    data = [('!addincludedir', osp.join(portable_dir, 'include'))
            ] + list(data)
    replace_in_nsis_file(dstname, data)
    try:
        retcode = subprocess.call('"%s" -V2 "%s"' % (NSIS_EXE, dstname),
                                  shell=True, stdout=sys.stderr)
        if retcode < 0:
            print("Child was terminated by signal", -retcode, file=sys.stderr)
    except OSError as e:
        print("Execution failed:", e, file=sys.stderr)
    os.remove(dstname)


class WinPythonDistribution(object):
    """WinPython distribution"""
    THG_PATH = r'\tools\TortoiseHg\thgw.exe'
    WINMERGE_PATH = r'\tools\WinMerge\WinMerge.exe'
    MINGW32_PATH = r'\tools\mingw32\bin'
    
    def __init__(self, build_number, release_level, target, instdir,
                 srcdir=None, toolsdirs=None, verbose=False, simulation=False):
        assert isinstance(build_number, int)
        assert isinstance(release_level, str)
        self.build_number = build_number
        self.release_level = release_level
        self.target = target
        self.instdir = instdir
        self.srcdir = srcdir
        if toolsdirs is None:
            toolsdirs = []
        self._toolsdirs = toolsdirs
        self.verbose = verbose
        self.winpydir = None
        self.python_fname = None
        self.python_name = None
        self.python_version = None
        self.python_fullversion = None
        self.distribution = None
        self.installed_packages = []
        self.simulation = simulation
    
    @property
    def package_index_wiki(self):
        """Return Package Index page in Wiki format"""
        installed_tools = [('gettext', '0.14.4'), ('SciTE', '3.3.7')]        
        def get_tool_path(relpath, checkfunc):
            if self.simulation:
                for dirname in self.toolsdirs:
                    path = dirname + relpath.replace(r'\tools', '')
                    if checkfunc(path):
                        return path
            else:
                path = self.winpydir + relpath
                if checkfunc(path):
                    return path
        thgpath = get_tool_path(self.THG_PATH, osp.isfile)
        if thgpath is not None:
            thgver = utils.get_thg_version(osp.dirname(thgpath))
            installed_tools += [('TortoiseHg', thgver)]
        if get_tool_path(self.WINMERGE_PATH, osp.isfile) is not None:
            installed_tools += [('WinMerge', '2.12.4')]
        gccpath = get_tool_path(self.MINGW32_PATH, osp.isdir)
        if gccpath is not None:
            gccver = utils.get_gcc_version(gccpath)
            installed_tools += [('MinGW32', gccver)]
        tools = []
        for name, ver in installed_tools:
            metadata = wppm.get_package_metadata('tools.ini', name)
            url, desc = metadata['url'], metadata['description']
            tools += ['|| [%s %s] || %s || %s ||' % (url, name, ver, desc)]
        packages = ['|| [%s %s] || %s || %s ||'
                    % (pack.url, pack.name, pack.version, pack.description)
                    for pack in sorted(self.installed_packages,
                                       key=lambda p: p.name.lower())]
        python_desc = 'Python programming language with standard library'
        return """== WinPython %s ==

The following packages are included in WinPython v%s.

=== Tools ===

%s

=== Python packages ===

|| [http://www.python.org/ Python] || %s || %s ||

%s""" % (self.winpyver, self.winpyver, '\n'.join(tools),
         self.python_fullversion, python_desc, '\n'.join(packages))
    
    @property
    def winpyver(self):
        """Return WinPython version (with release level!)"""
        return '%s.%d%s' % (self.python_fullversion, self.build_number,
                            self.release_level)

    @property
    def python_dir(self):
        """Return Python dirname (full path) of the target distribution"""
        return osp.join(self.winpydir, self.python_name)

    @property
    def winpy_arch(self):
        """Return WinPython architecture"""
        return '%dbit' % self.distribution.architecture

    @property
    def pyqt_arch(self):
        """Return distribution architecture, in PyQt format: x32/x64"""
        return 'x%d' % self.distribution.architecture
        
    @property
    def py_arch(self):
        """Return distribution architecture, in Python distutils format:
        win-amd64 or win32"""
        if self.distribution.architecture == 64:
            return 'win-amd64'
        else:
            return 'win32'
    
    @property
    def prepath(self):
        """Return PATH contents to be prepend to the environment variable"""
        path = [r"Lib\site-packages\PyQt4",
                "",  # Python root directory (python.exe)
                "DLLs", "Scripts", r"..\tools", r"..\tools\gnuwin32\bin"]
        if self.distribution.architecture == 32 \
           and osp.isdir(self.winpydir + self.MINGW32_PATH):
            path += [r".." + self.MINGW32_PATH]
        return path
    
    @property
    def postpath(self):
        """Return PATH contents to be append to the environment variable"""
        path = []
        if osp.isfile(self.winpydir + self.THG_PATH):
            path += [r"..\tools\TortoiseHg"]
        return path

    @property
    def toolsdirs(self):
        """Return tools directory list"""
        return [osp.join(osp.dirname(__file__), 'tools')] + self._toolsdirs

    def get_package_fname(self, pattern):
        """Get package matching pattern in instdir"""
        for path in (self.instdir, self.srcdir):
            for fname in os.listdir(path):
                match = re.match(pattern, fname)
                if match is not None:
                    return osp.abspath(osp.join(path, fname))
        else:
            raise RuntimeError(
                  'Could not found required package matching %s' % pattern)
    
    def install_package(self, pattern):
        """Install package matching pattern"""
        fname = self.get_package_fname(pattern)
        if fname not in [p.fname for p in self.installed_packages]:
            pack = wppm.Package(fname)
            if self.simulation:
                self.distribution._print(pack, "Installing")
                self.distribution._print_done()
            else:
                self.distribution.install(pack)
            self.installed_packages.append(pack)

    def create_batch_script(self, name, contents):
        """Create batch script %WINPYDIR%/name"""
        scriptdir = osp.join(self.winpydir, 'scripts')
        if not osp.isdir(scriptdir):
            os.mkdir(scriptdir)
        fd = open(osp.join(scriptdir, name), 'w')
        fd.write(contents)
        fd.close()
        
    def create_launcher(self, name, icon, command=None,
                        args=None, workdir=None, settingspath=None):
        """Create exe launcher with NSIS"""
        assert name.endswith('.exe')
        portable_dir = osp.join(osp.dirname(__file__), 'portable')
        icon_fname = osp.join(portable_dir, 'icons', icon)
        assert osp.isfile(icon_fname)
        
        # Customizing NSIS script
        conv = lambda path: ";".join(['${WINPYDIR}\\'+pth for pth in path])
        prepath = conv(self.prepath)
        postpath = conv(self.postpath)
        if command is None:
            if args is not None and '.pyw' in args:
                command = '${WINPYDIR}\pythonw.exe'
            else:
                command = '${WINPYDIR}\python.exe'
        if args is None:
            args = ''
        if workdir is None:
            workdir = ''

        fname = osp.join(self.winpydir, osp.splitext(name)[0]+'.nsi')
        
        data = [('WINPYDIR', '$EXEDIR\%s' % self.python_name),
                ('WINPYVER', self.winpyver),
                ('COMMAND', command),
                ('PARAMETERS', args),
                ('WORKDIR', workdir),
                ('PREPATH', prepath),
                ('POSTPATH', postpath),
                ('Icon', icon_fname),
                ('OutFile', name)]
        if settingspath is not None:
            data += [('SETTINGSDIR', osp.dirname(settingspath)),
                     ('SETTINGSNAME', osp.basename(settingspath))]

        build_nsis('launcher.nsi', fname, data)

    def create_python_batch(self, name, script_name,
                            workdir=None, options=None):
        """Create batch file to run a Python script"""
        if options is None:
            options = ''
        else:
            options = ' ' + options
        if script_name.endswith('.pyw'):
            cmd = 'start %WINPYDIR%\pythonw.exe'
        else:
            cmd = '%WINPYDIR%\python.exe'
        changedir = ''
        if workdir is not None:
            workdir = osp.join('%WINPYDIR%', workdir)
            changedir = r"""cd %s
""" % workdir
        if script_name:
            script_name = ' ' + script_name
        self.create_batch_script(name, r"""@echo off
call %~dp0env.bat
""" + changedir + cmd + script_name + options + " %*")

    def create_installer(self):
        """Create installer with NSIS"""
        self._print("Creating WinPython installer")
        portable_dir = osp.join(osp.dirname(__file__), 'portable')
        fname = osp.join(portable_dir, 'installer-tmp.nsi')
        data = (('DISTDIR', self.winpydir),
                ('ARCH', self.winpy_arch),
                ('VERSION', '%s.%d' % (self.python_fullversion,
                                       self.build_number)),
                ('RELEASELEVEL', self.release_level),)
        build_nsis('installer.nsi', fname, data)
        self._print_done()

    def _print(self, text):
        """Print action text indicating progress"""
        if self.verbose:
            utils.print_box(text)
        else:
            print(text + '...', end=" ")
    
    def _print_done(self):
        """Print OK at the end of a process"""
        if not self.verbose:
            print("OK")
    
    def _extract_python(self):
        """Extracting Python installer, creating distribution object"""
        self._print("Extracting Python installer")
        os.mkdir(self.python_dir)
        utils.extract_msi(self.python_fname, targetdir=self.python_dir)
        os.remove(osp.join(self.python_dir, osp.basename(self.python_fname)))
        os.mkdir(osp.join(self.python_dir, 'Scripts'))
        self._print_done()
    
    def _add_msvc_files(self):
        """Adding Microsoft Visual C++ DLLs"""
        print("Adding Microsoft Visual C++ DLLs""")
        msvc_version = dh.get_msvc_version(self.distribution.version)
        for fname in dh.get_msvc_dlls(msvc_version,
                               architecture=self.distribution.architecture):
            shutil.copy(fname, self.python_dir)

    def _check_packages(self):
        """Check packages for duplicates or unsupported packages"""
        print("Checking packages")
        packages = []
        for fname0 in os.listdir(self.srcdir) + os.listdir(self.instdir):
            fname = self.get_package_fname(fname0)
            if fname == self.python_fname:
                continue
            try:
                pack = wppm.Package(fname)
            except NotImplementedError:
                print("WARNING: package %s is not supported"\
                      % osp.basename(fname), file=sys.stderr)
                continue
            packages.append(pack)
        all_duplicates = []
        for pack in packages:
            if pack.name in all_duplicates:
                continue
            all_duplicates.append(pack.name)
            duplicates = [p for p in packages if p.name == pack.name]
            if len(duplicates) > 1:
                print("WARNING: duplicate packages %s (%s)" % \
                      (pack.name, ", ".join([p.version for p in duplicates])),
                      file=sys.stderr)

    def _install_required_packages(self):
        """Installing required packages"""
        print("Installing required packages")
        self.install_package('pywin32-([0-9\.]*[a-z]*).%s-py%s.exe'
                             % (self.py_arch, self.python_version))
        self.install_package('setuptools-([0-9\.]*[a-z]*[0-9]?).%s(-py%s)?.exe'
                             % (self.py_arch, self.python_version))
        self.install_package('winpython-([0-9\.]*[a-z]*[0-9]?).%s(-py%s)?.exe'
                             % (self.py_arch, self.python_version))
        self.install_package('spyder(lib)?-([0-9\.]*[a-z]*[0-9]?).%s(-py%s)?.exe'
                             % (self.py_arch, self.python_version))
        self.install_package(pattern='PyQt-Py%s-%s-gpl-([0-9\.\-]*).exe'
                                     % (self.python_version, self.pyqt_arch))
        self.install_package(
                    pattern='PyQwt-([0-9\.]*)-py%s-%s-([a-z0-9\.\-]*).exe'
                            % (self.python_version, self.pyqt_arch))
    
    def _install_all_other_packages(self):
        """Try to install all other packages in instdir"""
        print("Installing other packages")
        for fname in os.listdir(self.srcdir) + os.listdir(self.instdir):
            if osp.basename(fname) != osp.basename(self.python_fname):
                try:
                    self.install_package(fname)
                except NotImplementedError:
                    print("WARNING: unable to install package %s"\
                          % osp.basename(fname), file=sys.stderr)
    
    def _copy_dev_tools(self):
        """Copy dev tools"""
        self._print("Copying tools")
        toolsdir = osp.join(self.winpydir, 'tools')
        os.mkdir(toolsdir)
        for dirname in self.toolsdirs:
            for name in os.listdir(dirname):
                path = osp.join(dirname, name)
                copy = shutil.copytree if osp.isdir(path) else shutil.copyfile
                copy(path, osp.join(toolsdir, name))
                if self.verbose:
                    print(path + ' --> ' + osp.join(toolsdir, name))
        self._print_done()
    
    def _create_launchers(self):
        """Create launchers"""
        self._print("Creating launchers")
        self.create_launcher('WinPython Command Prompt.exe', 'cmd.ico',
                             command='$SYSDIR\cmd.exe',
                             args='/k', workdir='${WINPYDIR}')
        self.create_launcher('WinPython Interpreter.exe', 'python.ico')
        settingspath = osp.join('.spyder2', '.spyder.ini')
        self.create_launcher('Spyder.exe', 'spyder.ico',
                             args='spyder', workdir='${WINPYDIR}\Scripts',
                             settingspath=settingspath)
        self.create_launcher('Spyder (light).exe', 'spyder_light.ico',
                             args='spyder --light',
                             workdir='${WINPYDIR}\Scripts',
                             settingspath=settingspath)
        self.create_launcher('WinPython Control Panel.exe', 'winpython.ico',
                             command='${WINPYDIR}\pythonw.exe',
                             args='wpcp', workdir='${WINPYDIR}\Scripts')

        #XXX: Uncomment this part only when we are clear on how to handle 
        # the registration process during installation. "Register.exe" was 
        # only intended to be executed during installation by installer.nsi,
        # but, we can't let this executable at the root of WinPython directory
        # (too dangerous) and we can't move it easily as launchers are made 
        # to be executed when located at root directory... so we could remove 
        # it just after executing it, but is it even possible?
#        self.create_launcher('Register.exe', 'winpython.ico',
#                             args='register_python',
#                             workdir='${WINPYDIR}\Scripts')

        self.create_launcher('Qt Demo.exe', 'qt.ico', args='qtdemo.pyw',
          workdir=r'${WINPYDIR}\Lib\site-packages\PyQt4\examples\demos\qtdemo')
        self.create_launcher('Qt Assistant.exe', 'qtassistant.ico',
                  command=r'${WINPYDIR}\Lib\site-packages\PyQt4\assistant.exe',
                  workdir=r'${WINPYDIR}')
        self.create_launcher('Qt Designer.exe', 'qtdesigner.ico',
                   command=r'${WINPYDIR}\Lib\site-packages\PyQt4\designer.exe',
                   workdir=r'${WINPYDIR}')
        self.create_launcher('Qt Linguist.exe', 'qtlinguist.ico',
                   command=r'${WINPYDIR}\Lib\site-packages\PyQt4\linguist.exe',
                   workdir=r'${WINPYDIR}')
        if self.python_version[0] == '3':
            ipython_exe = 'ipython3.exe'
            ipython_scr = 'ipython3-script.py'
        else:
            ipython_exe = 'ipython.exe'
            ipython_scr = 'ipython-script.py'
        if osp.isfile(osp.join(self.python_dir, 'Scripts', ipython_exe)):
            self.create_launcher('IPython Qt Console.exe', 'ipython.ico',
                             command='${WINPYDIR}\pythonw.exe',
                             args='%s qtconsole --pylab=inline' % ipython_scr,
                             workdir='${WINPYDIR}\Scripts')
            self.create_launcher('IPython Notebook.exe', 'ipython.ico',
                             command='${WINPYDIR}\python.exe',
                             args='%s notebook --pylab=inline' % ipython_scr,
                             workdir='${WINPYDIR}\Scripts')
        if osp.isfile(self.winpydir + self.THG_PATH):
            self.create_launcher('TortoiseHg.exe', 'tortoisehg.ico',
                                 command=r'${WINPYDIR}\..'+self.THG_PATH,
                                 workdir=r'${WINPYDIR}')
        if osp.isfile(self.winpydir + self.WINMERGE_PATH):
            self.create_launcher('WinMerge.exe', 'winmerge.ico',
                                 command=r'${WINPYDIR}\..'+self.WINMERGE_PATH,
                                 workdir=r'${WINPYDIR}')
        self._print_done()
    
    def _create_batch_scripts(self):
        """Create batch scripts"""
        self._print("Creating batch scripts")
        self.create_batch_script('readme.txt', \
r"""These batch files are not required to run WinPython.

The purpose of these files is to help the user writing his/her own 
batch file to call Python scripts inside WinPython.
The examples here ('spyder.bat', 'spyder_light.bat', 'wppm.bat', 
'pyqt_demo.bat', 'python.bat' and 'cmd.bat') are quite similar to the 
launchers located in the parent directory.
The environment variables are set-up in 'env.bat'.""")
        conv = lambda path: ";".join(['%WINPYDIR%\\'+pth for pth in path])
        path = conv(self.prepath) + ";%PATH%;" + conv(self.postpath)
        self.create_batch_script('env.bat', """@echo off
set WINPYDIR=%~dp0..\\""" + self.python_name + r"""
set WINPYVER=""" + self.winpyver + """
set HOME=%WINPYDIR%\..\settings
set PATH=""" + path)
        self.create_batch_script('cmd.bat', r"""@echo off
call %~dp0env.bat
cmd.exe /k""")
        self.create_python_batch('python.bat', '')
        self.create_python_batch('spyder.bat', 'spyder', workdir='Scripts')
        self.create_python_batch('spyder_light.bat', 'spyder',
                                 workdir='Scripts', options='--light')
        self.create_python_batch('register_python.bat', 'register_python',
                                 workdir='Scripts')
        self.create_batch_script('register_python_for_all.bat',
                                 r"""@echo off
call %~dp0env.bat
call %~dp0register_python.bat --all""")
        self.create_python_batch('wpcp.bat', 'wpcp', workdir='Scripts')
        self.create_python_batch('pyqt_demo.bat', 'qtdemo.pyw',
                     workdir=r'Lib\site-packages\PyQt4\examples\demos\qtdemo')
        self._print_done()

    def make(self, remove_existing=True):
        """Make WinPython distribution in target directory from the installers 
        located in instdir
        
        remove_existing=True: (default) install all from scratch
        remove_existing=False: only for test purpose (launchers/scripts)"""
        if self.simulation:
            print("WARNING: this is just a simulation!", file=sys.stderr)

        self.python_fname = self.get_package_fname(
                                    r'python-([0-9\.]*)(\.amd64)?\.msi')
        self.python_name = osp.basename(self.python_fname)[:-4]
        distname = 'win%s' % self.python_name
        vlst = re.match(r'winpython-([0-9\.]*)', distname
                        ).groups()[0].split('.')
        self.python_version = '.'.join(vlst[:2])
        self.python_fullversion = '.'.join(vlst[:3])

        # Create the WinPython base directory
        self._print("Creating WinPython %s base directory"\
                    % self.python_version)
        self.winpydir = osp.join(self.target, distname)
        if osp.isdir(self.winpydir) and remove_existing \
           and not self.simulation:
            shutil.rmtree(self.winpydir, onerror=utils.onerror)
        if not osp.isdir(self.winpydir):
            os.mkdir(self.winpydir)
        if remove_existing and not self.simulation:
            # Create settings directory
            # (only necessary if user is starting an application with a batch 
            #  scripts before using an executable launcher, because the latter 
            #  is creating the directory automatically)
            os.mkdir(osp.join(self.winpydir, 'settings'))
        self._print_done()

        if remove_existing and not self.simulation:
            self._extract_python()
        self.distribution = wppm.Distribution(self.python_dir,
                                          verbose=self.verbose, indent=True)

        self._check_packages()

        if remove_existing:
            if not self.simulation:
                self._add_msvc_files()
            self._install_required_packages()
            self._install_all_other_packages()
            if not self.simulation:
                self._copy_dev_tools()
        if not self.simulation:
            self._create_launchers()
            self._create_batch_scripts()
        
        if remove_existing and not self.simulation:
            self._print("Cleaning up distribution")
            self.distribution.clean_up()
            self._print_done()
        
        # Writing package index
        self._print("Writing package index")
        fname = osp.join(self.winpydir, os.pardir,
                         'WinPython-%s.txt' % self.winpyver)
        open(fname, 'w').write(self.package_index_wiki)
        # Copy to winpython/changelogs
        shutil.copyfile(fname, osp.join(CHANGELOGS_DIR, osp.basename(fname)))
        self._print_done()
        
        # Writing changelog
        self._print("Writing changelog")
        diff.write_changelog(self.winpyver)
        self._print_done()


def rebuild_winpython(basedir=None, verbose=False):
    """Rebuild winpython package from source"""
    basedir = basedir if basedir is not None else utils.BASE_DIR
    for architecture in (32, 64):
        suffix = '.win32' if architecture == 32 else '.win-amd64'
        packdir = osp.join(basedir, 'packages' + suffix)
        for name in os.listdir(packdir):
            if name.startswith('winpython-') and name.endswith('.exe'):
                os.remove(osp.join(packdir, name))
        utils.build_wininst(osp.dirname(__file__), copy_to=packdir,
                            architecture=architecture, verbose=verbose)


def make_winpython(build_number, release_level, architecture,
                   basedir=None, verbose=False, remove_existing=True,
                   create_installer=True, simulation=False):
    """Make WinPython distribution, for a given base directory and 
    architecture:

    make_winpython(build_number, release_level, architecture,
                   basedir=None, verbose=False, remove_existing=True,
                   create_installer=True, simulation=False)
    
    `build_number`: build number [int]
    `release_level`: release level (e.g. 'beta1', '') [str]
    `architecture`: [int] (32 or 64)
    `basedir`: [str] if None, WINPYTHONBASEDIR env var must be set
    (rootdir: root directory containing 'basedir27', 'basedir33', etc.)
    """ + utils.ROOTDIR_DOC
    basedir = basedir if basedir is not None else utils.BASE_DIR
    assert basedir is not None, "The *basedir* directory must be specified"
    assert architecture in (32, 64)
    utils.print_box("Making WinPython %dbits" % architecture)
    suffix = '.win32' if architecture == 32 else '.win-amd64'
    packdir = osp.join(basedir, 'packages' + suffix)
    assert osp.isdir(packdir)
    srcdir = osp.join(basedir, 'packages.src')
    assert osp.isdir(srcdir)
    builddir = osp.join(basedir, 'build')
    if not osp.isdir(builddir):
        os.mkdir(builddir)
    toolsdir1 = osp.join(basedir, 'tools')
    assert osp.isdir(toolsdir1)
    toolsdirs = [toolsdir1]
    toolsdir2 = osp.join(basedir, 'tools' + suffix)
    if osp.isdir(toolsdir2):
        toolsdirs.append(toolsdir2)
    dist = WinPythonDistribution(build_number, release_level,
                                 builddir, packdir, srcdir, toolsdirs,
                                 verbose=verbose, simulation=simulation)
    dist.make(remove_existing=remove_existing)
    if create_installer and not simulation:
        dist.create_installer()
    return dist

def make_all(build_number, release_level, pyver,
             rootdir=None, simulation=False, create_installer=True,
             verbose=False, remove_existing=True):
    """Make WinPython for both 32 and 64bit architectures:
    
    make_all(build_number, release_level, pyver, rootdir, simulation=False,
             create_installer=True, verbose=False, remove_existing=True)

    `build_number`: build number [int]
    `release_level`: release level (e.g. 'beta1', '') [str]
    `pyver`: Python version (X.Y format) [str]
    `rootdir`: [str] if None, WINPYTHONROOTDIR env var must be set
    (rootdir: root directory containing 'basedir27', 'basedir33', etc.)
    """ + utils.ROOTDIR_DOC
    basedir = utils.get_basedir(pyver, rootdir=rootdir)
    rebuild_winpython(basedir=basedir)
    for architecture in (64, 32):
        make_winpython(build_number, release_level, architecture, basedir,
                       verbose, remove_existing, create_installer, simulation)


if __name__ == '__main__':
    make_all(0, '', pyver='3.3')#, create_installer=False)#, remove_existing=False, simulation=True)
    make_all(0, '', pyver='2.7')#, create_installer=False)#, remove_existing=False, simulation=True)

    import upload
    import time
    for version in ("3.3.3.0", "2.7.6.0"):
        for architecture in (64, 32):
            print(time.ctime())
            upload.upload_installer(version, architecture)
            print()
