Source

thg-winbuild / setup.py

Full commit
"""Generate binary installer for Mercurial or TortoiseHg

Usage: setup.py [OPTIONS]

Options:
  -h --help         Print this message and exit
     --pull         Pull from upstream, implied by nightly and unstable targets
     --nopull       Disable pull for all targets
     --config       Show build configuration and exit
  -f --force-hg     Force the build-hg directory to be recloned
  -F --force-thg    Force the build-thg directory to be recloned
  -c --clean        Clean build remnants
     --thg-release  Build a TortoiseHg release package
     --thg-nightly  Build a TortoiseHg nightly package
     --thg-unstable Build a TortoiseHg unstable nightly package
     --hg-release   Build a Mercurial release package
     --hg-nightly   Build a Mercurial nightly package
     --hg-unstable  Build a Mercurial unstable nightly package
"""

import os
import sys
import datetime
import subprocess
import fileinput
import getopt
import re

# default to TortoiseHg nightly build config
hgbranch = 'hg-stable'
hgtag = 'tip'
thgbranch = 'stable'
thgtag = 'tip'
forcehg = False
forcethg = False
buildhg = False
buildthg = False
isccopts = []
dopull = None
hgurl = 'http://selenic.com/repo/hg-stable/'
thgurl = 'http://bitbucket.org/tortoisehg/stable/'
showconfig = False

def run(cmd, cwd='.'):
    print cwd + "> " + cmd
    subprocess.check_call(cmd, shell=True, cwd=cwd)


def getdate():
    return datetime.date.today().strftime('%y%m%d')


def lasttag(reponame):
    p = subprocess.Popen( ('hg', '-R', reponame, 'tags', '-q'), 
                          stdin = subprocess.PIPE, 
                          stdout = subprocess.PIPE,
                          shell = False)
    stdout_text, stderr_text = p.communicate('') 
    return stdout_text.splitlines()[1]


def build_hg(force, branch, tag):
    if force or not os.path.exists('build-hg'):
        run(r'if exist build-hg rmdir /s /q build-hg')
        run(r'hg clone %s build-hg' % branch)
    else:
        run(r'hg pull', 'build-hg')

    # Apply local tag for THG release build
    if '+' in tag:
        r, hash = tag.split('+')
        run(r'hg tag -r %s -l %s' % (hash, tag), 'build-hg')

    run(r'hg update ' + tag, 'build-hg')
    # Comment the next line to use MSVC to build Mercurial
    run(r'copy misc\setup.cfg build-hg')
    run(r'copy hgfold\fold.py build-hg\hgext')
    run(r'python setup.py --version', 'build-hg')
    run(r'python setup.py build_py -c -d . build_ext -i build_mo', 'build-hg')
    # Build man pages
    run(r'gendoc.py > hg.1.gendoc.txt', 'build-hg/doc')
    for man in ('hg.1.txt', 'hgignore.5.txt', 'hgrc.5.txt'):
        outfile = os.path.join('build-hg', 'doc', man[:-3]) + 'html'
        if os.path.exists(outfile):
            continue
        if branch == 'hg-stable':
            run(r'python ../../asciidoc/asciidoc.py -b html4 ' + man,
                    'build-hg/doc')
        else:
            run(r'rst2html.py %s > %s' % (man, os.path.basename(outfile)),
                    'build-hg/doc')


def build_thg(force, branch, tag):
    if force or not os.path.exists('build-thg'):
        run(r'if exist build-thg rmdir /s /q build-thg')
        run(r'hg clone -r %s tortoisehg build-thg' % branch)
    else:
        run(r'hg pull -r' + branch, 'build-thg')
    run(r'hg update ' + tag, 'build-thg')
    run(r'python setup.py --version', 'build-thg')
    run(r'python setup.py build_mo', 'build-thg')
    run(r'if exist dist rmdir /s /q dist', 'build-thg')
    run(r'mkdir dist', 'build-thg')
    run(r'xcopy /s /q ..\gtk-dist dist', 'build-thg')
    run(r'copy ..\misc\gtkrc dist\gtk\etc\gtk-2.0', 'build-thg')
    run(r'copy contrib\win32\setup.cfg .', 'build-thg')
    # Build docs
    os.environ['hhc_compiler'] = '"' + findhhc() + '"'
    run(r'build chm', 'build-thg/doc')
    run(r'build pdf', 'build-thg/doc')
    # build extensions
    for arch, tgt in [('x86', 'x86'), ('x86_amd64', 'x64')]:
        if not os.path.exists(os.path.join('misc', 'ThgShell%s.dll' % tgt)):
            build_extension(arch, tgt)


def findiscc():
    for env in ('ProgramFiles', 'ProgramFiles(x86)'):
        if env in os.environ:
            p = os.environ[env]
            iscc = os.path.join(p, 'Inno Setup 5', 'iscc.exe')
            if os.path.exists(iscc):
                return iscc
    else:
        print >> sys.stderr, "Unable to find Inno Setup"
        sys.exit(1)


def findhhc():
    for env in ('ProgramFiles', 'ProgramFiles(x86)'):
        if env in os.environ:
            p = os.environ[env]
            hhc = os.path.join(p, 'HTML Help Workshop', 'hhc.exe')
            if os.path.exists(hhc):
                return hhc
    else:
        print >> sys.stderr, "Unable to find HTML Help Compiler"
        sys.exit(1)


def build_hg_installer(opts=[]):
    run(r'python setup.py py2exe -b2', 'build-hg')
    run(r'copy contrib\add_path.exe build-hg\dist')
    run(r'copy contrib\msvcr*.dll build-hg\dist')
    cmd = [findiscc()] + opts + [r'contrib\win32\mercurial.iss']
    cmdline = '"' + ' '.join(['"' + c + '"' for c in cmd]) + '"'
    run(cmdline, 'build-hg')


def build_thg_installer(opts=[]):
    oldpath = os.environ.get('PYTHONPATH')
    if oldpath:
        os.environ['PYTHONPATH'] = '../build-hg;'+oldpath
    else:
        os.environ['PYTHONPATH'] = '../build-hg'
    run(r'copy contrib\win32\config.py thgutil', 'build-thg')
    run(r'python setup.py py2exe -b2', 'build-thg')
    cmd = [findiscc()] + opts + [r'contrib\win32\mercurial.iss']
    cmdline = '"' + ' '.join(['"' + c + '"' for c in cmd]) + '"'
    run(cmdline, 'build-thg')


def build_extension(arch, tgt):
    outfile = os.path.join('build-thg', 'win32', 'shellext', 'ThgShell.dll')
    if os.path.exists(outfile):
        os.unlink(outfile)
    subprocess.check_call(r'misc\build_extension.bat build-thg ' + arch, shell=True)
    if os.path.exists(outfile):
        tgtfile = os.path.join('misc', 'ThgShell%s.dll' % tgt)
        if os.path.exists(tgtfile):
            os.unlink(tgtfile)
        os.rename(outfile, tgtfile)


def usage(code, msg=''):
    print >> sys.stderr, __doc__
    if msg:
        print >> sys.stderr, msg
    sys.exit(code)

if len(sys.argv) < 2:
    usage(0)

try:
    opts, args = getopt.getopt(sys.argv[1:], 'hcfF',
          ['clean', 'forcehg', 'forcethg', 'thg-release', 'thg-nightly',
           'thg-unstable', 'hg-release', 'hg-nightly', 'hg-unstable',
           'pull', 'nopull', 'config'])
except getopt.error, msg:
    usage(1, msg)

# parse options
for opt, arg in opts:
    if opt in ('-h', '--help'):
        usage(0)
    elif opt in ('-f', '--force-hg'):
        forcehg = True
    elif opt in ('-F', '--force-thg'):
        forcethg = True
    elif opt in ('-c', '--clean'):
        run(r'if exist build-hg rmdir /s /q build-hg')
        run(r'if exist build-thg rmdir /s /q build-thg')
        run(r'del misc\ThgShellx??.dll')
        sys.exit(0)
    elif opt == '--thg-release':
        buildthg = True
        thgbranch = 'stable'
        thgtag = '0.8.2'
        hgbranch = 'hg-stable'
        hgtag = '1.3.1+1444a42f6052'
        isccopts.append('/F"TortoiseHg-%s-hg-%s"' % (thgtag, hgtag))
        isccopts.append('/DVERSION=' + thgtag)
    elif opt == '--thg-nightly':
        if dopull is None:
            dopull = True
        buildthg = True
        thgbranch = 'stable'
        thgtag = 'tip'
        hgbranch = 'hg-stable'
        hgtag = 'tip'
        isccopts.append('/F"TortoiseHg-Nightly-%s"' % getdate())
        isccopts.append('/DVERSION=' + getdate())
    elif opt == '--thg-unstable':
        if dopull is None:
            dopull = True
        buildthg = True
        thgbranch = 'default'
        thgtag = 'tip'
        hgurl = 'http://selenic.com/repo/hg/'
        hgbranch = 'hg-main'
        hgtag = 'tip'
        isccopts.append('/F"TortoiseHg-Unstable-%s"' % getdate())
        isccopts.append('/DVERSION=' + getdate())
    elif opt == '--hg-release':
        buildhg = True
        hgbranch = 'hg-stable'
        hgtag = lasttag(hgbranch)
        isccopts.append('/F"Mercurial-%s"' % (hgtag))
        isccopts.append('/DVERSION=' + hgtag)
    elif opt == '--hg-nightly':
        if dopull is None:
            dopull = True
        buildhg = True
        hgbranch = 'hg-stable'
        hgtag = 'tip'
        isccopts.append('/F"Mercurial-Nightly-%s"' % getdate())
        isccopts.append('/DVERSION=' + getdate())
    elif opt == '--hg-unstable':
        if dopull is None:
            dopull = True
        buildhg = True
        hgurl = 'http://selenic.com/repo/hg/'
        hgbranch = 'hg-main'
        hgtag = 'tip'
        isccopts.append('/F"Mercurial-Unstable-%s"' % getdate())
        isccopts.append('/DVERSION=' + getdate())
    elif opt == '--pull':
        dopull = True
    elif opt == '--nopull':
        dopull = False
    elif opt == '--config':
        showconfig = True

print 'Build config:'
print '[TortoiseHg] branch=%s, tag=%s' % (thgbranch, thgtag)
print '[Mercurial]  branch=%s, tag=%s' % (hgbranch, hgtag)

if showconfig:
    sys.exit()

if buildhg:
    if dopull:
        run(r'hg -R %s pull %s' % (hgbranch, hgurl))
    build_hg(forcehg, hgbranch, hgtag)
    build_hg_installer(isccopts)
elif buildthg:
    if dopull:
        run(r'hg -R %s pull %s' % (hgbranch, hgurl))
        run(r'hg -R tortoisehg pull %s' % thgurl)
    build_hg(forcehg, hgbranch, hgtag)
    build_thg(forcethg, thgbranch, thgtag)
    build_thg_installer(isccopts)
elif dopull:
    run(r'hg -R %s pull %s' % (hgbranch, hgurl))
    run(r'hg -R tortoisehg pull %s' % thgurl)