thg-winbuild / setup.py

"""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 stable 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 all build remnants
     --thg-release  Build a TortoiseHg release package
     --thg-rc       Build a TortoiseHg release candidate
     --thg-stable   Build a TortoiseHg nightly stable package
     --thg-unstable Build a TortoiseHg nightly unstable package
     --hg-release   Build a Mercurial release package
     --hg-stable    Build a Mercurial nightly stable package
     --hg-unstable  Build a Mercurial nightly unstable package
     --all          Build all four nightly targets
"""

#### Package Respins ####
# TortoiseHg 0.9.2-1 used mercurial.iss from changeset 275690997c5f

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

# default to TortoiseHg nightly stable build config
hgbranch = 'stable'
hgtag = 'tip'
thgbranch = 'stable'
thgtag = 'tip'
forcehg = False
forcethg = False
buildhg = False
buildthg = False
dopull = None
showconfig = False

URL = {
  'hg-main'   : 'http://selenic.com/repo/hg/',
  'tortoisehg': 'http://bitbucket.org/tortoisehg/stable/',
  'hgfold'    : 'http://bitbucket.org/bradobro/hgfold/',
  'hgcr-gui'  : 'http://bitbucket.org/glimchb/hgcr-gui/',
  }

thg_source_repos = ('tortoisehg', 'hg-main', 'hgfold', 'hgcr-gui')

def checkrepo(name, ver=''):
    if os.path.isdir(name):
        return
    run(r'hg clone -U %s %s' % (URL[name], name))
    run(r'hg update -R %s %s' % (name, ver))


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 repoversion(reponame):
    p = subprocess.Popen( ('python', 'setup.py', '--version'),
                          stdin=subprocess.PIPE, 
                          stdout=subprocess.PIPE,
                          shell=False,
                          cwd=reponame)
    stdout_text, stderr_text = p.communicate('') 
    return stdout_text.splitlines()[0]

def pipetofile(cmdlist, outfile, cwd):
    print '> ', ' '.join(cmdlist), '>', outfile
    p = subprocess.Popen( cmdlist,
                          stdin=subprocess.PIPE,
                          stdout=subprocess.PIPE,
                          shell=False,
                          cwd=cwd)
    out, err = p.communicate('')
    fp = open(outfile, 'wb')
    fp.write(out)
    fp.close

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 hg-main -u %s build-hg' % branch)
    else:
        run(r'hg pull', 'build-hg')
        run(r'hg update ' + branch, 'build-hg')
    if tag == 'tip':
        run(r'hg update', 'build-hg')
    else:
        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'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
    pipetofile((sys.executable, 'gendoc.py'),
               'build-hg/doc/hg.1.gendoc.txt',
               'build-hg/doc')
    rst2html = os.path.dirname(sys.executable) + r'\Scripts\rst2html.py'
    for man in ('hg.1.txt', 'hgignore.5.txt', 'hgrc.5.txt'):
        outfile = man[:-3] + 'html'
        run(r'python %s --link-stylesheet --stylesheet-path style.css %s %s' % (
            rst2html, man, 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 tortoisehg -u %s build-thg' % branch)
    else:
        run(r'hg pull', 'build-thg')
        run(r'hg update ' + branch, 'build-thg')
    if tag == 'tip':
        run(r'hg update', 'build-thg')
    else:
        run(r'hg update ' + tag, 'build-thg')
    run(r'python setup.py --version', 'build-thg')
    run(r'python setup.py build_mo', 'build-thg')
    # on x64, some (*&$ batch file somewhere is pointing PROGRAMFILES at
    # PROGRAMFILES(x86), which is incredibly unhelpful.
    pf = os.environ['PROGRAMFILES']
    if pf.endswith('(x86)'):
        pf = pf[:-6]
        os.environ['PROGRAMFILES'] = pf
    win7sdk = pf + r'\Microsoft SDKs\Windows\v7.0\Bin'
    run(r'cmd.exe /e:on /v:on /c misc\sdk_build_extensions.cmd')
    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')
    for ext in ('hgfold/fold.py', 'hgcr-gui/hgcr-gui.py'):
        if os.path.exists(ext):
	    shutil.copy(ext, 'build-hg/hgext')
    # Build docs
    os.environ['hhc_compiler'] = '"' + findhhc() + '"'
    run(r'build chm', 'build-thg/doc')
    run(r'build pdf', 'build-thg/doc')
    run(r'copy contrib\win32\config.py tortoisehg\util', 'build-thg')
    run(r'python reggen.py', 'build-thg/win32')


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(name):
    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')
    version = repoversion('build-hg')
    opts = []
    opts.append('/F"%s-%s"' % (name, version))
    opts.append('/DVERSION=' + version)
    cmd = [findiscc()] + opts + [r'contrib\win32\mercurial.iss']
    cmdline = '"' + ' '.join(['"' + c + '"' for c in cmd]) + '"'
    run(cmdline, 'build-hg')
    run(r'move build-hg\Output\*.exe output\.')


def build_thg_installer(name):
    oldpath = os.environ.get('PYTHONPATH')
    if oldpath:
        os.environ['PYTHONPATH'] = '../build-hg;'+oldpath
    else:
        os.environ['PYTHONPATH'] = '../build-hg'
    run(r'python setup.py py2exe -b2', 'build-thg')
    hgversion = repoversion('build-hg')
    thgversion = repoversion('build-thg')
    opts = []
    opts.append('/F"%s-%s-hg-%s"' % (name, thgversion, hgversion))
    opts.append('/DVERSION=' + thgversion)
    if os.path.exists(r'build-thg\contrib\win32\tortoisehg.iss'):
        cmd = [findiscc()] + opts + [r'contrib\win32\tortoisehg.iss']
    else:
        cmd = [findiscc()] + opts + [r'contrib\win32\mercurial.iss']
    cmdline = '"' + ' '.join(['"' + c + '"' for c in cmd]) + '"'
    run(cmdline, 'build-thg')
    run(r'move build-thg\Output\*.exe output\.')

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-stable',
           'thg-unstable', 'thg-rc', 'hg-release', 'hg-stable', 'hg-unstable',
           'pull', 'nopull', 'config', 'all'])
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'if exist misc\ThgShellx86.dll del misc\ThgShellx86.dll')
        run(r'if exist misc\ThgShellx64.dll del misc\ThgShellx64.dll')
        sys.exit(0)
    elif opt == '--thg-release':
        for r in thg_source_repos:
            checkrepo(r)
        buildthg = True
        thgbranch = 'stable'
        thgtag = lasttag('tortoisehg')
        hgbranch = 'stable'
        hgtag = lasttag('hg-main')
        packagename = 'tortoisehg'
    elif opt == '--thg-stable':
        for r in thg_source_repos:
            checkrepo(r)
        if dopull is None:
            dopull = True
        buildthg = True
        thgbranch = 'stable'
        thgtag = 'tip'
        hgbranch = 'stable'
        hgtag = 'tip'
        packagename = 'tortoisehg-stable'
    elif opt == '--thg-rc':
        for r in thg_source_repos:
            checkrepo(r)
        if dopull is None:
            dopull = True
        buildthg = True
        thgbranch = 'stable'
        thgtag = 'tip'
        hgbranch = 'stable'
        hgtag = 'tip'
        packagename = 'tortoisehg-rc'
    elif opt == '--thg-unstable':
        for r in thg_source_repos:
            checkrepo(r)
        if dopull is None:
            dopull = True
        buildthg = True
        thgbranch = 'default'
        thgtag = 'tip'
        hgbranch = 'default'
        hgtag = 'tip'
        packagename = 'tortoisehg-unstable'
    elif opt == '--hg-release':
        checkrepo('hg-main')
        buildhg = True
        hgbranch = 'stable'
        hgtag = lasttag('hg-main')
        packagename = 'mercurial'
    elif opt == '--hg-stable':
        checkrepo('hg-main')
        if dopull is None:
            dopull = True
        buildhg = True
        hgbranch = 'stable'
        hgtag = 'tip'
        packagename = 'mercurial-stable'
    elif opt == '--hg-unstable':
        checkrepo('hg-main')
        if dopull is None:
            dopull = True
        buildhg = True
        hgbranch = 'default'
        hgtag = 'tip'
        packagename = 'mercurial-unstable'
    elif opt == '--pull':
        dopull = True
    elif opt == '--nopull':
        dopull = False
    elif opt == '--config':
        showconfig = True
    elif opt == '--all':
        run(r'if exist build-hg rmdir /s /q build-hg')
        run('python setup.py --hg-stable')
        run('python setup.py --hg-unstable')
        run('python setup.py -c')
        run('python setup.py --thg-stable')
        run(r'if exist misc\ThgShellx86.dll del misc\ThgShellx86.dll')
        run(r'if exist misc\ThgShellx64.dll del misc\ThgShellx64.dll')
        run('python setup.py --thg-unstable')
        sys.exit(0)

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

if showconfig:
    sys.exit(0)

if not os.path.isdir('output'):
    run(r'mkdir output')

if dopull:
    run(r'hg -R hg-main pull')
    run(r'hg -R tortoisehg pull')
    if buildthg:
        run(r'hg -R hgcr-gui pull -u')
        run(r'hg -R hgfold pull -u')

if buildhg:
    build_hg(forcehg, hgbranch, hgtag)
    build_hg_installer(packagename)
elif buildthg:
    build_hg(forcehg, hgbranch, hgtag)
    build_thg(forcethg, thgbranch, thgtag)
    build_thg_installer(packagename)
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.