1
0
mirror of https://gitlab.com/openlp/packaging.git synced 2024-12-22 21:12:50 +00:00

Migrate to python 3

bzr-revno: 10
This commit is contained in:
Jonathan Springer 2014-05-17 20:27:11 +01:00 committed by Tim Bentley
commit bbe296c19c
3 changed files with 421 additions and 499 deletions

View File

@ -118,7 +118,7 @@ import subprocess
import sys import sys
from shutil import copy, rmtree from shutil import copy, rmtree
from subprocess import Popen, PIPE from subprocess import Popen, PIPE
from ConfigParser import SafeConfigParser as ConfigParser from configparser import ConfigParser
from argparse import ArgumentParser from argparse import ArgumentParser
@ -128,7 +128,7 @@ def _which(command):
""" """
for path in os.environ["PATH"].split(os.pathsep): for path in os.environ["PATH"].split(os.pathsep):
if os.access(os.path.join(path, command), os.X_OK): if os.access(os.path.join(path, command), os.X_OK):
print "%s/%s" % (path, command) print("%s/%s" % (path, command))
return "%s/%s" % (path, command) return "%s/%s" % (path, command)
@ -137,6 +137,7 @@ class MacosxBuilder(object):
The :class:`MacosxBuilder` class encapsulates everything that is needed The :class:`MacosxBuilder` class encapsulates everything that is needed
to build a Mac OS X .dmg file. to build a Mac OS X .dmg file.
""" """
def __init__(self): def __init__(self):
self.setup_args() self.setup_args()
self.setup_system_paths() self.setup_system_paths()
@ -150,7 +151,7 @@ class MacosxBuilder(object):
""" """
if len(args) > 0: if len(args) > 0:
text = text % tuple(args) text = text % tuple(args)
print text print(text)
def _print_verbose(self, text, *args): def _print_verbose(self, text, *args):
""" """
@ -191,9 +192,8 @@ class MacosxBuilder(object):
""" """
# Get the output in plist format. # Get the output in plist format.
paths = [] paths = []
output = self._run_command([self.hdiutil, 'info', '-plist'], output = self._run_command([self.hdiutil, 'info', '-plist'], 'Detecting mount points failed.')
u'Detecting mount points failed.') pl = plistlib.readPlistFromBytes(output)
pl = plistlib.readPlistFromString(output)
for image in pl['images']: for image in pl['images']:
for se in image['system-entities']: for se in image['system-entities']:
if se.get('mount-point'): if se.get('mount-point'):
@ -208,31 +208,23 @@ class MacosxBuilder(object):
parser = ArgumentParser() parser = ArgumentParser()
parser.add_argument('-b', '--branch', metavar='BRANCH', dest='branch', parser.add_argument('-b', '--branch', metavar='BRANCH', dest='branch',
help='Specify the path to the branch you wish to build.') help='Specify the path to the branch you wish to build.')
parser.add_argument('--devel', dest='devel', parser.add_argument('--devel', dest='devel', action='store_true', default=False,
action='store_true', default=False,
help='Development build does not have set icons for .dmg file ' help='Development build does not have set icons for .dmg file '
'and .dmg filename contains bzr revision number.') 'and .dmg filename contains bzr revision number.')
parser.add_argument('-d', '--documentation', metavar='DOCS', parser.add_argument('-d', '--documentation', metavar='DOCS', dest='docs',
dest='docs',
help='Specify the path to the documentation branch.') help='Specify the path to the documentation branch.')
parser.add_argument('-c', '--config', metavar='CONFIG', dest='config', parser.add_argument('-c', '--config', metavar='CONFIG', dest='config',
help='Specify the path to the configuration file.', help='Specify the path to the configuration file.',
default=os.path.abspath(os.path.join('.', 'config.ini.default'))) default=os.path.abspath(os.path.join('.', 'config.ini.default')))
parser.add_argument('-u', '--skip-update', dest='skip_update', parser.add_argument('-u', '--skip-update', dest='skip_update', action='store_true', default=False,
action='store_true', default=False,
help='Do NOT update the branch before building.') help='Do NOT update the branch before building.')
parser.add_argument('-t', '--skip-translations', parser.add_argument('-t', '--skip-translations', dest='skip_translations', action='store_true', default=False,
dest='skip_translations', action='store_true', default=False,
help='Do NOT update the language translation files.') help='Do NOT update the language translation files.')
parser.add_argument('--transifex', parser.add_argument('--transifex', dest='update_translations', action='store_true', default=False,
dest='update_translations', action='store_true', default=False,
help='Update the language translation from Transifex.') help='Update the language translation from Transifex.')
parser.add_argument('--transifex-user', parser.add_argument('--transifex-user', dest='transifex_user', help='Transifex username.')
dest='transifex_user', help='Transifex username.') parser.add_argument('--transifex-pass', dest='transifex_pass', help='Transifex password.')
parser.add_argument('--transifex-pass', parser.add_argument('-v', '--verbose', dest='verbose', action='store_true', default=False,
dest='transifex_pass', help='Transifex password.')
parser.add_argument('-v', '--verbose', dest='verbose',
action='store_true', default=False,
help='Print out additional information.') help='Print out additional information.')
self.args = parser.parse_args() self.args = parser.parse_args()
@ -241,10 +233,8 @@ class MacosxBuilder(object):
Read the configuration from the configuration file. Read the configuration from the configuration file.
""" """
self.config = ConfigParser(defaults={ self.config = ConfigParser(defaults={
u'here': self.script_path, 'here': self.script_path,
u'projects': os.path.abspath(os.path.join(self.script_path, 'projects': os.path.abspath(os.path.join(self.script_path, '..', '..')), })
'..', '..')),
})
self.config.read(os.path.abspath(self.args.config)) self.config.read(os.path.abspath(self.args.config))
def setup_system_paths(self): def setup_system_paths(self):
@ -258,13 +248,12 @@ class MacosxBuilder(object):
""" """
Set up the paths to the executables we use. Set up the paths to the executables we use.
""" """
self.sphinx = _which(self.config.get(u'executables', u'sphinx')) self.sphinx = _which(self.config.get('executables', 'sphinx'))
self.pyinstaller = os.path.abspath( self.pyinstaller = os.path.abspath(self.config.get('executables', 'pyinstaller'))
self.config.get(u'executables', u'pyinstaller')) self.lrelease = _which(self.config.get('executables', 'lrelease'))
self.lrelease = _which(self.config.get(u'executables', u'lrelease')) self.diskutil = _which(self.config.get('executables', 'diskutil'))
self.diskutil = _which(self.config.get(u'executables', u'diskutil')) self.hdiutil = _which(self.config.get('executables', 'hdiutil'))
self.hdiutil = _which(self.config.get(u'executables', u'hdiutil')) self.osascript = _which(self.config.get('executables', 'osascript'))
self.osascript = _which(self.config.get(u'executables', u'osascript'))
def setup_paths(self): def setup_paths(self):
""" """
@ -273,164 +262,168 @@ class MacosxBuilder(object):
if self.args.branch: if self.args.branch:
self.branch_path = os.path.abspath(self.args.branch) self.branch_path = os.path.abspath(self.args.branch)
else: else:
self.branch_path = self.config.get(u'paths', u'branch') self.branch_path = self.config.get('paths', 'branch')
if self.args.docs: if self.args.docs:
self.docs_path = os.path.abspath(self.args.docs) self.docs_path = os.path.abspath(self.args.docs)
else: else:
self.docs_path = self.config.get(u'paths', u'documentation') self.docs_path = self.config.get('paths', 'documentation')
self.openlp_script = os.path.abspath( self.openlp_script = os.path.abspath(os.path.join(self.branch_path, 'openlp.py'))
os.path.join(self.branch_path, u'openlp.py')) self.hooks_path = os.path.abspath(os.path.join(self.branch_path, self.config.get('paths', 'hooks')))
self.hooks_path = os.path.abspath(os.path.join( self.mac_icon = os.path.abspath(self.config.get('paths', 'macicon'))
self.branch_path, self.config.get(u'paths', u'hooks'))) self.bundle_info = os.path.abspath(self.config.get('paths', 'bundleinfo'))
self.mac_icon = os.path.abspath( self.dmg_background_img = os.path.abspath(self.config.get('paths', 'dmg_background'))
self.config.get(u'paths', u'macicon')) self.i18n_utils = os.path.join(self.branch_path, 'scripts', 'translation_utils.py')
self.bundle_info = os.path.abspath( self.source_path = os.path.join(self.branch_path, 'openlp')
self.config.get(u'paths', u'bundleinfo')) self.manual_path = os.path.join(self.docs_path, 'manual')
self.dmg_background_img = os.path.abspath( self.manual_build_path = os.path.join(self.manual_path, 'build')
self.config.get(u'paths', u'dmg_background')) self.i18n_path = os.path.join(self.branch_path, 'resources', 'i18n')
self.i18n_utils = os.path.join(self.branch_path, u'scripts', self.build_path = os.path.join(self.branch_path, 'build')
u'translation_utils.py') self.dist_app_path = os.path.join(self.branch_path, 'dist', 'OpenLP.app')
self.source_path = os.path.join(self.branch_path, u'openlp') self.dist_path = os.path.join(self.branch_path, 'dist', 'OpenLP.app', 'Contents', 'MacOS')
self.manual_path = os.path.join(self.docs_path, u'manual')
self.manual_build_path = os.path.join(self.manual_path, u'build')
self.i18n_path = os.path.join(self.branch_path, u'resources', u'i18n')
self.build_path = os.path.join(self.branch_path, u'build')
self.dist_app_path = os.path.join(self.branch_path, u'dist', u'OpenLP.app')
self.dist_path = os.path.join(self.branch_path, u'dist', u'OpenLP.app',
'Contents', 'MacOS')
# Path to Qt translation files. # Path to Qt translation files.
from PyQt4.QtCore import QCoreApplication from PyQt4.QtCore import QCoreApplication
qt_plug_dir = str(list(QCoreApplication.libraryPaths())[0]) qt_plug_dir = str(list(QCoreApplication.libraryPaths())[0])
self.qt_translat_path = os.path.join(os.path.dirname(qt_plug_dir), self.qt_translat_path = os.path.join(os.path.dirname(qt_plug_dir), 'translations')
'translations')
def update_code(self): def update_code(self):
""" """
Update the code in the branch. Update the code in the branch.
""" """
os.chdir(self.branch_path) os.chdir(self.branch_path)
self._print(u'Reverting any changes to the code...') self._print('Reverting any changes to the code...')
bzr = Popen((u'bzr', u'revert'), stdout=PIPE) bzr = Popen(('bzr', 'revert'), stdout=PIPE)
output = bzr.communicate()[0] output = bzr.communicate()[0]
code = bzr.wait() code = bzr.wait()
if code != 0: if code != 0:
self._print(output) self._print(output)
raise Exception(u'Error reverting the code') raise Exception('Error reverting the code')
self._print(u'Updating the code...') self._print('Updating the code...')
bzr = Popen((u'bzr', u'update'), stdout=PIPE) bzr = Popen(('bzr', 'update'), stdout=PIPE)
output = bzr.communicate()[0] output = bzr.communicate()[0]
code = bzr.wait() code = bzr.wait()
if code != 0: if code != 0:
self._print(output) self._print(output)
raise Exception(u'Error updating the code') raise Exception('Error updating the code')
def run_pyinstaller(self): def run_pyinstaller(self):
""" """
Run PyInstaller on the branch to build an executable. Run PyInstaller on the branch to build an executable.
""" """
self._print(u'Running PyInstaller...') self._print('Running PyInstaller...')
os.chdir(self.branch_path) os.chdir(self.branch_path)
pyinstaller = Popen((self.python, pyinstaller = Popen((self.python,
self.pyinstaller, self.pyinstaller,
u'--noconfirm', '--noconfirm',
u'--windowed', '--windowed',
u'--noupx', '--noupx',
u'--additional-hooks-dir', self.hooks_path, '--additional-hooks-dir', self.hooks_path,
u'--runtime-hook', os.path.join(self.hooks_path, 'rthook_openlp_pyqt4.py'), '--runtime-hook', os.path.join(self.hooks_path, 'rthook_openlp_pyqt4.py'),
u'--log-level=ERROR', '--log-level=ERROR',
u'-o', self.branch_path, # '--distpath', self.branch_path,
#u'-i', self.mac_icon, # '-i', self.mac_icon,
u'-p', self.branch_path, '-p', self.branch_path,
u'-n', u'OpenLP', '-n', 'OpenLP',
self.openlp_script), self.openlp_script),
stdout=PIPE) stdout=PIPE)
output = pyinstaller.communicate()[0] output = pyinstaller.communicate()[0]
code = pyinstaller.wait() code = pyinstaller.wait()
if code != 0: if code != 0:
self._print(output) self._print(output)
raise Exception(u'Error running PyInstaller') raise Exception('Error running PyInstaller')
def write_version_file(self): def write_version_file(self):
""" """
Write the version number to a file for reading once installed. Write the version number to a file for reading once installed.
""" """
self._print(u'Writing version file...') self._print('Writing version file...')
os.chdir(self.branch_path) os.chdir(self.branch_path)
bzr = Popen((u'bzr', u'tags', u'--sort', u'time'), stdout=PIPE) bzr = Popen(('bzr', 'tags'), stdout=PIPE)
output = bzr.communicate()[0] output = bzr.communicate()[0]
code = bzr.wait() code = bzr.wait()
if code != 0: if code != 0:
raise Exception(u'Error running bzr tags') raise Exception('Error running bzr tags')
lines = output.splitlines() lines = output.splitlines()
if len(lines) == 0: if len(lines) == 0:
tag = u'0.0.0' tag = '0.0.0'
revision = u'0' revision = '0'
else: else:
tag, revision = lines[-1].split() tag, revision = lines[-1].decode('utf-8').split()
bzr = Popen((u'bzr', u'log', u'--line', u'-r', u'-1'), stdout=PIPE) bzr = Popen(('bzr', 'log', '--line', '-r', '-1'), stdout=PIPE)
output, error = bzr.communicate() output, error = bzr.communicate()
code = bzr.wait() code = bzr.wait()
if code != 0: if code != 0:
raise Exception(u'Error running bzr log') raise Exception('Error running bzr log')
output_ascii = unicode(output, errors=u'ignore') latest = output.decode('utf-8').split(':')[0]
latest = output_ascii.split(u':')[0] self.version_string = '%s-bzr%s' % (tag, latest)
self.version_string = u'%s-bzr%s' % (tag, latest)
self.version_tag = tag self.version_tag = tag
version_file = open(os.path.join(self.dist_path, u'.version'), u'w') version_file = open(os.path.join(self.dist_path, '.version'), 'w')
# Release version does not contain revision in .dmg name. # Release version does not contain revision in .dmg name.
if self.args.devel: if self.args.devel:
version_file.write(self.version_string) version_file.write(str(self.version_string))
else: else:
version_file.write(self.version_tag) version_file.write(str(self.version_tag))
version_file.close() version_file.close()
def copy_default_theme(self):
"""
Copy the default theme to the correct directory for OpenLP.
"""
self._print('Copying default theme...')
source = os.path.join(self.source_path, 'core', 'lib', 'json')
dest = os.path.join(self.dist_path, 'core', 'lib', 'json')
for root, dirs, files in os.walk(source):
for filename in files:
if filename.endswith('.json'):
dest_path = os.path.join(dest, root[len(source) + 1:])
if not os.path.exists(dest_path):
os.makedirs(dest_path)
self._print_verbose('... %s', filename)
copy(os.path.join(root, filename), os.path.join(dest_path, filename))
def copy_plugins(self): def copy_plugins(self):
""" """
Copy all the plugins to the correct directory so that OpenLP sees that Copy all the plugins to the correct directory so that OpenLP sees that
it has plugins. it has plugins.
""" """
self._print(u'Copying plugins...') self._print('Copying plugins...')
source = os.path.join(self.source_path, u'plugins') source = os.path.join(self.source_path, 'plugins')
dest = os.path.join(self.dist_path, u'plugins') dest = os.path.join(self.dist_path, 'plugins')
for root, dirs, files in os.walk(source): for root, dirs, files in os.walk(source):
for filename in files: for filename in files:
if not filename.endswith(u'.pyc'): if not filename.endswith('.pyc'):
dest_path = os.path.join(dest, root[len(source) + 1:]) dest_path = os.path.join(dest, root[len(source) + 1:])
if not os.path.exists(dest_path): if not os.path.exists(dest_path):
os.makedirs(dest_path) os.makedirs(dest_path)
self._print_verbose(u'... %s', filename) self._print_verbose('... %s', filename)
copy(os.path.join(root, filename), copy(os.path.join(root, filename), os.path.join(dest_path, filename))
os.path.join(dest_path, filename))
def copy_media_player(self): def copy_media_player(self):
""" """
Copy the media players to the correct directory for OpenLP. Copy the media players to the correct directory for OpenLP.
""" """
self._print(u'Copying media player...') self._print('Copying media player...')
source = os.path.join(self.source_path, u'core', u'ui', u'media') source = os.path.join(self.source_path, 'core', 'ui', 'media')
dest = os.path.join(self.dist_path, u'core', u'ui', u'media') dest = os.path.join(self.dist_path, 'core', 'ui', 'media')
for root, dirs, files in os.walk(source): for root, dirs, files in os.walk(source):
for filename in files: for filename in files:
if not filename.endswith(u'.pyc'): if not filename.endswith('.pyc'):
dest_path = os.path.join(dest, root[len(source) + 1:]) dest_path = os.path.join(dest, root[len(source) + 1:])
if not os.path.exists(dest_path): if not os.path.exists(dest_path):
os.makedirs(dest_path) os.makedirs(dest_path)
self._print_verbose(u'... %s', filename) self._print_verbose('... %s', filename)
copy(os.path.join(root, filename), copy(os.path.join(root, filename), os.path.join(dest_path, filename))
os.path.join(dest_path, filename))
def copy_mac_bundle_files(self): def copy_mac_bundle_files(self):
""" """
Copy Info.plist and OpenLP.icns to app bundle. Copy Info.plist and OpenLP.icns to app bundle.
""" """
copy(self.mac_icon, os.path.join(self.dist_app_path, copy(self.mac_icon, os.path.join(self.dist_app_path, 'Contents', 'Resources', os.path.basename(self.mac_icon)))
'Contents', 'Resources', os.path.basename(self.mac_icon)))
# Add OpenLP version to Info.plist and put it to app bundle. # Add OpenLP version to Info.plist and put it to app bundle.
fr = open(self.bundle_info, u'r') fr = open(self.bundle_info, 'r')
fw = open(os.path.join(self.dist_app_path, fw = open(os.path.join(self.dist_app_path, 'Contents', os.path.basename(self.bundle_info)), 'w')
'Contents', os.path.basename(self.bundle_info)), 'w')
text = fr.read() text = fr.read()
text = text % {'openlp_version': self.version_string} text = text % {'openlp_version': self.version_string}
fw.write(text) fw.write(text)
@ -442,96 +435,87 @@ class MacosxBuilder(object):
""" """
Copy all the OSX-specific files. Copy all the OSX-specific files.
""" """
self._print(u'Copying extra files for Mac OS X...') self._print('Copying extra files for Mac OS X...')
self._print_verbose(u'... LICENSE.txt') self._print_verbose('... LICENSE.txt')
copy(os.path.join(self.script_path, u'LICENSE.txt'), copy(os.path.join(self.script_path, 'LICENSE.txt'), os.path.join(self.dist_path, 'LICENSE.txt'))
os.path.join(self.dist_path, u'LICENSE.txt'))
def update_translations(self): def update_translations(self):
""" """
Update the translations. Update the translations.
""" """
self._print(u'Updating translations...') self._print('Updating translations...')
if not self.config.has_section('transifex'): if not self.config.has_section('transifex'):
raise Exception(u'No section named "transifex" found.') raise Exception('No section named "transifex" found.')
if not self.config.has_option('transifex', 'username'): if not self.config.has_option('transifex', 'username'):
raise Exception(u'No option named "username" found.') raise Exception('No option named "username" found.')
if not self.config.has_option('transifex', 'password'): if not self.config.has_option('transifex', 'password'):
raise Exception(u'No option named "password" found.') raise Exception('No option named "password" found.')
if self.args.transifex_user: if self.args.transifex_user:
username = self.args.transifex_user username = self.args.transifex_user
else: else:
username = self.config.get(u'transifex', u'username') username = self.config.get('transifex', 'username')
if self.args.transifex_pass: if self.args.transifex_pass:
password = self.args.transifex_pass password = self.args.transifex_pass
else: else:
password = self.config.get(u'transifex', u'password') password = self.config.get('transifex', 'password')
os.chdir(os.path.split(self.i18n_utils)[0]) os.chdir(os.path.split(self.i18n_utils)[0])
translation_utils = Popen([self.python, self.i18n_utils, u'-qdpu', translation_utils = Popen([self.python, self.i18n_utils, '-qdpu', '-U', username, '-P', password])
u'-U', username, u'-P', password])
code = translation_utils.wait() code = translation_utils.wait()
if code != 0: if code != 0:
raise Exception(u'Error running translation_utils.py') raise Exception('Error running translation_utils.py')
def compile_translations(self): def compile_translations(self):
""" """
Compile the translations for Qt. Compile the translations for Qt.
""" """
self._print(u'Compiling translations...') self._print('Compiling translations...')
files = os.listdir(self.i18n_path) files = os.listdir(self.i18n_path)
if not os.path.exists(os.path.join(self.dist_path, u'i18n')): if not os.path.exists(os.path.join(self.dist_path, 'i18n')):
os.makedirs(os.path.join(self.dist_path, u'i18n')) os.makedirs(os.path.join(self.dist_path, 'i18n'))
for file in files: for file in files:
if file.endswith(u'.ts'): if file.endswith('.ts'):
self._print_verbose(u'... %s', file) self._print_verbose('... %s', file)
source_path = os.path.join(self.i18n_path, file) source_path = os.path.join(self.i18n_path, file)
dest_path = os.path.join(self.dist_path, u'i18n', dest_path = os.path.join(self.dist_path, 'i18n', file.replace('.ts', '.qm'))
file.replace(u'.ts', u'.qm')) lconvert = Popen((self.lrelease, '-compress', '-silent', source_path, '-qm', dest_path))
lconvert = Popen((self.lrelease, u'-compress', u'-silent',
source_path, u'-qm', dest_path))
code = lconvert.wait() code = lconvert.wait()
if code != 0: if code != 0:
raise Exception(u'Error running lconvert on %s' % \ raise Exception('Error running lconvert on %s' % source_path)
source_path) self._print('Copying qm files...')
self._print(u'Copying qm files...')
source = self.qt_translat_path source = self.qt_translat_path
files = os.listdir(source) files = os.listdir(source)
for filename in files: for filename in files:
if filename.startswith(u'qt_') and filename.endswith(u'.qm') and \ if filename.startswith('qt_') and filename.endswith('.qm') and len(filename) == 8:
len(filename) == 8: self._print_verbose('... %s', filename)
self._print_verbose(u'... %s', filename) copy(os.path.join(source, filename), os.path.join(self.dist_path, 'i18n', filename))
copy(os.path.join(source, filename),
os.path.join(self.dist_path, u'i18n', filename))
def run_sphinx(self): def run_sphinx(self):
""" """
Run Sphinx to build an HTML Help project. Run Sphinx to build an HTML Help project.
""" """
self._print(u'Deleting previous manual build... %s', self._print('Deleting previous manual build... %s', self.manual_build_path)
self.manual_build_path)
if os.path.exists(self.manual_build_path): if os.path.exists(self.manual_build_path):
rmtree(self.manual_build_path) rmtree(self.manual_build_path)
self._print(u'Running Sphinx...') self._print('Running Sphinx...')
os.chdir(self.manual_path) os.chdir(self.manual_path)
sphinx = Popen((self.sphinx, u'-b', u'htmlhelp', u'-d', sphinx = Popen((self.sphinx, '-b', 'htmlhelp', '-d', 'build/doctrees', 'source', 'build/htmlhelp'), stdout=PIPE)
u'build/doctrees', u'source', u'build/htmlhelp'), stdout=PIPE)
output, error = sphinx.communicate() output, error = sphinx.communicate()
code = sphinx.wait() code = sphinx.wait()
if code != 0: if code != 0:
self._print(output) self._print(output)
raise Exception(u'Error running Sphinx') raise Exception('Error running Sphinx')
def create_dmg_file(self): def create_dmg_file(self):
""" """
Create .dmg file. Create .dmg file.
""" """
self._print(u'Creating dmg file...') self._print('Creating dmg file...')
# Release version does not contain revision in .dmg name. # Release version does not contain revision in .dmg name.
if self.args.devel: if self.args.devel:
dmg_name = 'OpenLP-' + self.version_string + '.dmg' dmg_name = 'OpenLP-' + str(self.version_string) + '.dmg'
else: else:
dmg_name = 'OpenLP-' + self.version_tag + '.dmg' dmg_name = 'OpenLP-' + str(self.version_tag) + '.dmg'
dmg_file = os.path.join(self.branch_path, 'build', dmg_name) dmg_file = os.path.join(self.branch_path, 'build', dmg_name)
# Remove dmg if it exists. # Remove dmg if it exists.
@ -541,19 +525,14 @@ class MacosxBuilder(object):
size = self._get_directory_size(self.dist_app_path) # in bytes. size = self._get_directory_size(self.dist_app_path) # in bytes.
size = size / (1024 * 1024) # Convert to megabytes. size = size / (1024 * 1024) # Convert to megabytes.
size += 10 # Additional space in .dmg for other files. size += 10 # Additional space in .dmg for other files.
self._print(u'... dmg disk size: %s' % size) self._print('... dmg disk size: %s' % size)
self._run_command([self.hdiutil, 'create', dmg_file, self._run_command([self.hdiutil, 'create', dmg_file, '-ov', '-megabytes', str(size), '-fs', 'HFS+', '-volname',
'-ov', '-megabytes', str(size), 'OpenLP'], 'Could not create dmg file.')
'-fs', 'HFS+', '-volname', 'OpenLP'],
u'Could not create dmg file.'
)
# Mount empty dmg file. # Mount empty dmg file.
old_mounts = self._get_mountpoints() old_mounts = self._get_mountpoints()
self._print(u'... mounting the dmg file: %s' % dmg_file) self._print('... mounting the dmg file: %s' % dmg_file)
self._run_command([self.hdiutil, 'attach', dmg_file], self._run_command([self.hdiutil, 'attach', dmg_file], 'Could not mount dmg file, cannot continue.')
u'Could not mount dmg file, cannot continue.'
)
new_mounts = self._get_mountpoints() new_mounts = self._get_mountpoints()
# Get the mount point from difference between paths # Get the mount point from difference between paths
# after mounting and before mounting the dmg file. # after mounting and before mounting the dmg file.
@ -561,25 +540,19 @@ class MacosxBuilder(object):
# Copy OpenLP.app and other files to .dmg # Copy OpenLP.app and other files to .dmg
# TODO more reliable way to determine dmg_volume_path # TODO more reliable way to determine dmg_volume_path
self._print(u'... Copying the app to the dmg: ' + dmg_volume_path) self._print('... Copying the app to the dmg: ' + dmg_volume_path)
self._run_command(['cp', '-r', self.dist_app_path, self._run_command(['cp', '-r', self.dist_app_path, dmg_volume_path],
dmg_volume_path], 'Could not copy app bundle, dmg creation failed.')
u'Could not copy app bundle, dmg creation failed.'
)
# Set icon for dmg file. # Set icon for dmg file.
# http://endrift.com/blog/2010/06/14/dmg-files-volume-icons-cli/ # http://endrift.com/blog/2010/06/14/dmg-files-volume-icons-cli/
self._print('... Setting the dmg icon.') self._print('... Setting the dmg icon.')
dmg_icon = os.path.join(dmg_volume_path, '.VolumeIcon.icns') dmg_icon = os.path.join(dmg_volume_path, '.VolumeIcon.icns')
self._run_command(['cp', self.mac_icon, dmg_icon], self._run_command(['cp', self.mac_icon, dmg_icon], 'Could not copy the dmg icon file, dmg creation failed.')
u'Could not copy the dmg icon file, dmg creation failed.'
)
# Set proper dmg icon attributes. # Set proper dmg icon attributes.
self._run_command(['SetFile', '-c', 'icnC', dmg_icon], self._run_command(['SetFile', '-c', 'icnC', dmg_icon], 'Could not set dmg icon attributes.')
'Could not set dmg icon attributes.')
# Ensures dmg icon will be used while mounted. # Ensures dmg icon will be used while mounted.
self._run_command(['SetFile', '-a', 'C', dmg_volume_path], self._run_command(['SetFile', '-a', 'C', dmg_volume_path], 'Could not set dmg icon attributes.')
'Could not set dmg icon attributes.')
# Create symlink in dmg pointing to the /Applications directory on OS X. # Create symlink in dmg pointing to the /Applications directory on OS X.
self._print('... Creating symlink to /Applications.') self._print('... Creating symlink to /Applications.')
@ -591,15 +564,13 @@ class MacosxBuilder(object):
self._print('... Setting the background image.') self._print('... Setting the background image.')
os.mkdir(os.path.join(dmg_volume_path, '.background')) os.mkdir(os.path.join(dmg_volume_path, '.background'))
self._run_command(['cp', self.dmg_background_img, self._run_command(['cp', self.dmg_background_img, os.path.join(dmg_volume_path,
os.path.join(dmg_volume_path,
'.background/installer-background.png')], '.background/installer-background.png')],
u'Could not copy the background image, dmg creation failed.' 'Could not copy the background image, dmg creation failed.')
)
self.adjust_dmg_view(os.path.basename(dmg_volume_path)) self.adjust_dmg_view(os.path.basename(dmg_volume_path))
## Unmount dmg file. # Unmount dmg file.
self._print('... unmounting the dmg.') self._print('... unmounting the dmg.')
# Sometimes it could happen that OSX Finder is blocking umount. # Sometimes it could happen that OSX Finder is blocking umount.
# We need to find this process and kill it. # We need to find this process and kill it.
@ -609,50 +580,42 @@ class MacosxBuilder(object):
blocking_proc_pid = int(output.split()[0]) blocking_proc_pid = int(output.split()[0])
os.kill(int(blocking_proc_pid), signal.SIGKILL) os.kill(int(blocking_proc_pid), signal.SIGKILL)
except Exception as e: except Exception as e:
print str(e) print(str(e))
self._print('... failed to kill process using %s' % dmg_volume_path) self._print('... failed to kill process using %s' % dmg_volume_path)
# Unmount dmg file. # Unmount dmg file.
self._run_command([self.hdiutil, 'detach', dmg_volume_path], self._run_command([self.hdiutil, 'detach', dmg_volume_path],
'Could not unmount the dmg file, dmg creation failed.' 'Could not unmount the dmg file, dmg creation failed.')
)
# Compress dmg file. # Compress dmg file.
self._print('... compressing the dmg file') self._print('... compressing the dmg file')
compressed_dmg = os.path.join(self.branch_path, 'dist', compressed_dmg = os.path.join(self.branch_path, 'dist', os.path.basename(dmg_file)) # Put dmg to 'dist' dir.
os.path.basename(dmg_file)) # Put dmg to 'dist' dir.
# Remove dmg if it exists. # Remove dmg if it exists.
if os.path.exists(compressed_dmg): if os.path.exists(compressed_dmg):
os.remove(compressed_dmg) os.remove(compressed_dmg)
self._run_command([self.hdiutil, self._run_command([self.hdiutil, 'convert', dmg_file, '-format', 'UDZO', '-imagekey', 'zlib-level=9', '-o',
'convert', dmg_file, '-format', 'UDZO', compressed_dmg], 'Could not compress the dmg file, dmg creation failed.')
'-imagekey', 'zlib-level=9', '-o', compressed_dmg],
u'Could not compress the dmg file, dmg creation failed.'
)
# Jenkins integration. # Jenkins integration.
# Continuous integration server needs to know the filename of dmg. # Continuous integration server needs to know the filename of dmg.
# Write java property file. For uploading dmg to openlp. # Write java property file. For uploading dmg to openlp.
if self.args.devel: if self.args.devel:
fpath = os.path.join(self.branch_path, 'openlp.properties') fpath = os.path.join(self.branch_path, 'openlp.properties')
self._print('... writing property file for jenkins: %s' % self._print('... writing property file for jenkins: %s' % fpath)
fpath)
f = open(fpath, 'w') f = open(fpath, 'w')
f.write('OPENLP_DMGNAME=' + os.path.basename(dmg_file) + '\n') f.write('OPENLP_DMGNAME=' + os.path.basename(dmg_file) + '\n')
f.close() f.close()
# Dmg done. # Dmg done.
self._print('Finished creating dmg file, resulting file: %s' % self._print('Finished creating dmg file, resulting file: %s' % compressed_dmg)
compressed_dmg)
self.dmg_file = compressed_dmg self.dmg_file = compressed_dmg
def adjust_dmg_view(self, dmg_volume_name): def adjust_dmg_view(self, dmg_volume_name):
try: try:
# TODO: Use only one applescript file. Remove one for osx 10.5. # TODO: Use only one applescript file. Remove one for osx 10.5.
f = open(os.path.join(self.script_path, f = open(os.path.join(self.script_path, 'applescript-adjust-dmg-view.master'))
'applescript-adjust-dmg-view.master'))
p = Popen([self.osascript], stdin=PIPE) p = Popen([self.osascript], stdin=PIPE)
p.communicate(f.read() % (dmg_volume_name, 'OpenLP')) p.communicate(bytes(f.read() % (dmg_volume_name, 'OpenLP'), 'utf-8'))
f.close() f.close()
result = p.returncode result = p.returncode
if (result != 0): if (result != 0):
@ -664,33 +627,32 @@ class MacosxBuilder(object):
""" """
The main function to run the Mac OS X builder. The main function to run the Mac OS X builder.
""" """
self._print_verbose(u'OpenLP main script: ......%s', self._print_verbose('OpenLP main script: ......%s', self.openlp_script)
self.openlp_script) self._print_verbose('Script path: .............%s', os.path.split(os.path.abspath(__file__))[0])
self._print_verbose(u'Script path: .............%s', self._print_verbose('Branch path: .............%s', self.branch_path)
os.path.split(os.path.abspath(__file__))[0]) self._print_verbose('Source path: .............%s', self.source_path)
self._print_verbose(u'Branch path: .............%s', self.branch_path) self._print_verbose('"dist.app" path: .........%s', self.dist_app_path)
self._print_verbose(u'Source path: .............%s', self.source_path) self._print_verbose('"dist" path: .............%s', self.dist_path)
self._print_verbose(u'"dist.app" path: .........%s', self.dist_app_path) self._print_verbose('"hooks" path: ............%s', self.hooks_path)
self._print_verbose(u'"dist" path: .............%s', self.dist_path) self._print_verbose('PyInstaller: .............%s', self.pyinstaller)
self._print_verbose(u'"hooks" path: ............%s', self.hooks_path) self._print_verbose('Documentation branch path:%s', self.docs_path)
self._print_verbose(u'PyInstaller: .............%s', self.pyinstaller) self._print_verbose('')
self._print_verbose(u'Documentation branch path:%s', self.docs_path)
self._print_verbose(u'')
if not self.args.skip_update: if not self.args.skip_update:
self.update_code() self.update_code()
self.run_pyinstaller() self.run_pyinstaller()
self.write_version_file() self.write_version_file()
self.copy_mac_bundle_files() self.copy_mac_bundle_files()
self.copy_default_theme()
self.copy_plugins() self.copy_plugins()
self.copy_media_player() self.copy_media_player()
# TODO creating help on Mac # TODO creating help on Mac
if os.path.exists(self.manual_path): if os.path.exists(self.manual_path):
self.run_sphinx() self.run_sphinx()
else: else:
self._print(u'') self._print('')
self._print(u'WARNING: Documentation trunk not found. Mac OS X') self._print('WARNING: Documentation trunk not found. Mac OS X')
self._print(u' Help file will not be included in build') self._print(' Help file will not be included in build')
self._print(u'') self._print('')
self.copy_macosx_files() self.copy_macosx_files()
if not self.args.skip_translations: if not self.args.skip_translations:
if self.args.update_translations: if self.args.update_translations:
@ -698,9 +660,9 @@ class MacosxBuilder(object):
self.compile_translations() self.compile_translations()
self.create_dmg_file() self.create_dmg_file()
self._print(u'Done.') self._print('Done.')
raise SystemExit() raise SystemExit()
if __name__ == u'__main__': if __name__ == '__main__':
MacosxBuilder().main() MacosxBuilder().main()

View File

@ -41,10 +41,10 @@
import sip import sip
sip.setapi(u'QDate', 2) sip.setapi('QDate', 2)
sip.setapi(u'QDateTime', 2) sip.setapi('QDateTime', 2)
sip.setapi(u'QString', 2) sip.setapi('QString', 2)
sip.setapi(u'QTextStream', 2) sip.setapi('QTextStream', 2)
sip.setapi(u'QTime', 2) sip.setapi('QTime', 2)
sip.setapi(u'QUrl', 2) sip.setapi('QUrl', 2)
sip.setapi(u'QVariant', 2) sip.setapi('QVariant', 2)

View File

@ -129,7 +129,7 @@ import sys
from shutil import copy, rmtree, move from shutil import copy, rmtree, move
from distutils import dir_util from distutils import dir_util
from subprocess import Popen, PIPE from subprocess import Popen, PIPE
from ConfigParser import SafeConfigParser as ConfigParser from configparser import ConfigParser
from argparse import ArgumentParser from argparse import ArgumentParser
@ -138,13 +138,14 @@ class WindowsBuilder(object):
The :class:`WindowsBuilder` class encapsulates everything that is needed The :class:`WindowsBuilder` class encapsulates everything that is needed
to build a Windows installer. to build a Windows installer.
""" """
def __init__(self): def __init__(self):
self.setup_args() self.setup_args()
self.setup_system_paths() self.setup_system_paths()
self.read_config() self.read_config()
self.setup_executables() self.setup_executables()
self.setup_paths() self.setup_paths()
self.version = u'' self.version = ''
def _print(self, text, *args): def _print(self, text, *args):
""" """
@ -152,7 +153,7 @@ class WindowsBuilder(object):
""" """
if len(args) > 0: if len(args) > 0:
text = text % tuple(args) text = text % tuple(args)
print text print(text)
def _print_verbose(self, text, *args): def _print_verbose(self, text, *args):
""" """
@ -167,25 +168,19 @@ class WindowsBuilder(object):
""" """
parser = ArgumentParser() parser = ArgumentParser()
parser.add_argument('-b', '--branch', metavar='BRANCH', dest='branch', parser.add_argument('-b', '--branch', metavar='BRANCH', dest='branch',
help='Specify the path to the branch you wish to build.', help='Specify the path to the branch you wish to build.', default=None)
default=None) parser.add_argument('-d', '--documentation', metavar='DOCS', dest='docs', default=None,
parser.add_argument('-d', '--documentation', metavar='DOCS',
dest='docs', default=None,
help='Specify the path to the documentation branch.') help='Specify the path to the documentation branch.')
parser.add_argument('-c', '--config', metavar='CONFIG', dest='config', parser.add_argument('-c', '--config', metavar='CONFIG', dest='config',
help='Specify the path to the configuration file.', help='Specify the path to the configuration file.',
default=os.path.abspath(os.path.join('.', 'config.ini'))) default=os.path.abspath(os.path.join('.', 'config.ini')))
parser.add_argument('-u', '--skip-update', dest='skip_update', parser.add_argument('-u', '--skip-update', dest='skip_update', action='store_true', default=False,
action='store_true', default=False,
help='Do NOT update the branch before building.') help='Do NOT update the branch before building.')
parser.add_argument('-p', '--portable', metavar='PORTABLE', parser.add_argument('-p', '--portable', metavar='PORTABLE', dest='portable', default=None,
dest='portable', default=None,
help='Specify the path to build the portable installation.') help='Specify the path to build the portable installation.')
parser.add_argument('-t', '--skip-translations', parser.add_argument('-t', '--skip-translations', dest='skip_translations', action='store_true', default=False,
dest='skip_translations', action='store_true', default=False,
help='Do NOT update the language translation files.') help='Do NOT update the language translation files.')
parser.add_argument('-v', '--verbose', dest='verbose', parser.add_argument('-v', '--verbose', dest='verbose', action='store_true', default=False,
action='store_true', default=False,
help='Print out additional information.') help='Print out additional information.')
self.args = parser.parse_args() self.args = parser.parse_args()
@ -194,10 +189,10 @@ class WindowsBuilder(object):
Read the configuration from the configuration file. Read the configuration from the configuration file.
""" """
self.config = ConfigParser(defaults={ self.config = ConfigParser(defaults={
u'pyroot': self.python_root, 'pyroot': self.python_root,
u'progfiles': self.program_files, 'progfiles': self.program_files,
u'sitepackages': self.site_packages, 'sitepackages': self.site_packages,
u'here': self.script_path 'here': self.script_path
}) })
self.config.read(os.path.abspath(self.args.config)) self.config.read(os.path.abspath(self.args.config))
@ -208,40 +203,29 @@ class WindowsBuilder(object):
self.script_path = os.path.split(os.path.abspath(__file__))[0] self.script_path = os.path.split(os.path.abspath(__file__))[0]
self.python = sys.executable self.python = sys.executable
self.python_root = os.path.split(self.python)[0] self.python_root = os.path.split(self.python)[0]
self.site_packages = os.path.join(self.python_root, self.site_packages = os.path.join(self.python_root, 'Lib', 'site-packages')
u'Lib', u'site-packages') self.program_files = os.getenv('PROGRAMFILES')
self.program_files = os.getenv(u'PROGRAMFILES')
def setup_executables(self): def setup_executables(self):
""" """
Set up the paths to the executables we use. Set up the paths to the executables we use.
""" """
self.innosetup = os.path.abspath( self.innosetup = os.path.abspath(self.config.get('executables', 'innosetup'))
self.config.get(u'executables', u'innosetup')) self.sphinx = os.path.abspath(self.config.get('executables', 'sphinx'))
self.sphinx = os.path.abspath( self.pyinstaller = os.path.abspath(self.config.get('executables', 'pyinstaller'))
self.config.get(u'executables', u'sphinx')) self.vcbuild = os.path.abspath(self.config.get('executables', 'vcbuild'))
self.pyinstaller = os.path.abspath( self.hhc = os.path.abspath(self.config.get('executables', 'htmlhelp'))
self.config.get(u'executables', u'pyinstaller')) self.psvince = os.path.abspath(self.config.get('executables', 'psvince'))
self.vcbuild = os.path.abspath( self.portableinstaller = os.path.abspath(self.config.get('executables', 'portableinstaller'))
self.config.get(u'executables', u'vcbuild')) self.portablelauncher = os.path.abspath(self.config.get('executables', 'portablelauncher'))
self.hhc = os.path.abspath( if os.path.exists(os.path.join(self.site_packages, 'PyQt4', 'bin')):
self.config.get(u'executables', u'htmlhelp'))
self.psvince = os.path.abspath(
self.config.get(u'executables', u'psvince'))
self.portableinstaller = os.path.abspath(
self.config.get(u'executables', u'portableinstaller'))
self.portablelauncher = os.path.abspath(
self.config.get(u'executables', u'portablelauncher'))
if os.path.exists(os.path.join(self.site_packages, u'PyQt4', u'bin')):
# Older versions of the PyQt4 Windows installer put their binaries # Older versions of the PyQt4 Windows installer put their binaries
# in the "bin" directory # in the "bin" directory
self.lrelease = os.path.join(self.site_packages, u'PyQt4', self.lrelease = os.path.join(self.site_packages, 'PyQt4', 'bin', 'lrelease.exe')
u'bin', u'lrelease.exe')
else: else:
# Newer versions of the PyQt4 Windows installer put their binaries # Newer versions of the PyQt4 Windows installer put their binaries
# in the base directory of the installation # in the base directory of the installation
self.lrelease = os.path.join(self.site_packages, u'PyQt4', self.lrelease = os.path.join(self.site_packages, 'PyQt4', 'lrelease.exe')
u'lrelease.exe')
def setup_paths(self): def setup_paths(self):
""" """
@ -250,270 +234,263 @@ class WindowsBuilder(object):
if self.args.branch: if self.args.branch:
branch_path = self.args.branch branch_path = self.args.branch
else: else:
branch_path = self.config.get(u'paths', u'branch') branch_path = self.config.get('paths', 'branch')
self.branch_path = os.path.abspath(branch_path) self.branch_path = os.path.abspath(branch_path)
if self.args.docs: if self.args.docs:
docs_path = self.args.docs docs_path = self.args.docs
else: else:
docs_path = self.config.get(u'paths', u'documentation') docs_path = self.config.get('paths', 'documentation')
self.docs_path = os.path.abspath(docs_path) self.docs_path = os.path.abspath(docs_path)
if self.args.portable: if self.args.portable:
portable_path = self.args.portable portable_path = self.args.portable
else: else:
try: try:
portable_path = self.config.get(u'paths', u'portable') portable_path = self.config.get('paths', 'portable')
except: except:
portable_path = u'' portable_path = ''
if portable_path: if portable_path:
self.portable_path = os.path.abspath(portable_path) self.portable_path = os.path.abspath(portable_path)
self.args.portable = self.portable_path self.args.portable = self.portable_path
else: else:
self.portable_path = u'' self.portable_path = ''
self.openlp_script = os.path.abspath( self.openlp_script = os.path.abspath(os.path.join(branch_path, 'openlp.py'))
os.path.join(branch_path, u'openlp.py')) self.hooks_path = os.path.abspath(self.config.get('paths', 'hooks'))
self.hooks_path = os.path.abspath(self.config.get(u'paths', u'hooks')) self.win32_icon = os.path.abspath(self.config.get('paths', 'win32icon'))
self.win32_icon = os.path.abspath( self.i18n_utils = os.path.join(self.branch_path, 'scripts', 'translation_utils.py')
self.config.get(u'paths', u'win32icon')) self.source_path = os.path.join(self.branch_path, 'openlp')
self.i18n_utils = os.path.join(self.branch_path, u'scripts', self.manual_path = os.path.join(self.docs_path, 'manual')
u'translation_utils.py') self.manual_build_path = os.path.join(self.manual_path, 'build')
self.source_path = os.path.join(self.branch_path, u'openlp') self.helpfile_path = os.path.join(self.manual_build_path, 'htmlhelp')
self.manual_path = os.path.join(self.docs_path, u'manual') self.i18n_path = os.path.join(self.branch_path, 'resources', 'i18n')
self.manual_build_path = os.path.join(self.manual_path, u'build') self.winres_path = os.path.join(self.branch_path, 'resources', 'windows')
self.helpfile_path = os.path.join(self.manual_build_path, u'htmlhelp') self.build_path = os.path.join(self.branch_path, 'build')
self.i18n_path = os.path.join(self.branch_path, u'resources', u'i18n') self.dist_path = os.path.join(self.branch_path, 'dist', 'OpenLP')
self.winres_path = os.path.join(self.branch_path, u'resources', self.pptviewlib_path = os.path.join(self.source_path, 'plugins', 'presentations', 'lib', 'pptviewlib')
u'windows')
self.build_path = os.path.join(self.branch_path, u'build')
self.dist_path = os.path.join(self.branch_path, u'dist', u'OpenLP')
self.pptviewlib_path = os.path.join(self.source_path, u'plugins',
u'presentations', u'lib', u'pptviewlib')
def update_code(self): def update_code(self):
""" """
Update the code in the branch. Update the code in the branch.
""" """
os.chdir(self.branch_path) os.chdir(self.branch_path)
self._print(u'Reverting any changes to the code...') self._print('Reverting any changes to the code...')
bzr = Popen((u'bzr', u'revert'), stdout=PIPE) bzr = Popen(('bzr', 'revert'), stdout=PIPE)
output = bzr.communicate()[0] output = bzr.communicate()[0]
code = bzr.wait() code = bzr.wait()
if code != 0: if code != 0:
self._print(output) self._print(output)
raise Exception(u'Error reverting the code') raise Exception('Error reverting the code')
self._print(u'Updating the code...') self._print('Updating the code...')
bzr = Popen((u'bzr', u'update'), stdout=PIPE) bzr = Popen(('bzr', 'update'), stdout=PIPE)
output = bzr.communicate()[0] output = bzr.communicate()[0]
code = bzr.wait() code = bzr.wait()
if code != 0: if code != 0:
self._print(output) self._print(output)
raise Exception(u'Error updating the code') raise Exception('Error updating the code')
def run_pyinstaller(self): def run_pyinstaller(self):
""" """
Run PyInstaller on the branch to build an executable. Run PyInstaller on the branch to build an executable.
""" """
self._print(u'Running PyInstaller...') self._print('Running PyInstaller...')
os.chdir(self.branch_path) os.chdir(self.branch_path)
pyinstaller = Popen((self.python, self.pyinstaller, pyinstaller = Popen((self.python, self.pyinstaller,
u'--noconfirm', '--noconfirm',
u'--windowed', '--windowed',
u'--noupx', '--noupx',
u'--additional-hooks-dir', self.hooks_path, '--additional-hooks-dir', self.hooks_path,
u'--runtime-hook', os.path.join(self.hooks_path, 'rthook_openlp_pyqt4.py'), '--runtime-hook', os.path.join(self.hooks_path, 'rthook_openlp_pyqt4.py'),
u'--log-level=ERROR', '--log-level=ERROR',
u'-o', self.branch_path, '--distpath', self.branch_path,
u'-i', self.win32_icon, '-i', self.win32_icon,
u'-p', self.branch_path, '-p', self.branch_path,
u'-n', u'OpenLP', '-n', 'OpenLP',
self.openlp_script), self.openlp_script),
stdout=PIPE) stdout=PIPE)
output = pyinstaller.communicate()[0] output = pyinstaller.communicate()[0]
code = pyinstaller.wait() code = pyinstaller.wait()
if code != 0: if code != 0:
self._print(output) self._print(output)
raise Exception(u'Error running PyInstaller') raise Exception('Error running PyInstaller')
def write_version_file(self): def write_version_file(self):
""" """
Write the version number to a file for reading once installed. Write the version number to a file for reading once installed.
""" """
self._print(u'Writing version file...') self._print('Writing version file...')
os.chdir(self.branch_path) os.chdir(self.branch_path)
bzr = Popen((u'bzr', u'tags', u'--sort', u'time'), stdout=PIPE) bzr = Popen(('bzr', 'tags'), stdout=PIPE)
output = bzr.communicate()[0] output = bzr.communicate()[0]
code = bzr.wait() code = bzr.wait()
if code != 0: if code != 0:
raise Exception(u'Error running bzr tags') raise Exception('Error running bzr tags')
lines = output.splitlines() lines = output.splitlines()
if len(lines) == 0: if len(lines) == 0:
tag = u'0.0.0' tag = '0.0.0'
revision = u'0' revision = '0'
else: else:
tag, revision = lines[-1].split() tag, revision = lines[-1].decode('utf-8').split()
bzr = Popen((u'bzr', u'log', u'--line', u'-r', u'-1'), stdout=PIPE) bzr = Popen(('bzr', 'log', '--line', '-r', '-1'), stdout=PIPE)
output, error = bzr.communicate() output, error = bzr.communicate()
code = bzr.wait() code = bzr.wait()
if code != 0: if code != 0:
raise Exception(u'Error running bzr log') raise Exception('Error running bzr log')
output_ascii = unicode(output, errors=u'ignore') latest = output.decode('utf-8').split(':')[0]
latest = output_ascii.split(u':')[0] version_string = latest == revision and tag or '%s-bzr%s' % (tag, latest)
version_string = latest == revision and tag or \
u'%s-bzr%s' % (tag, latest)
# Save decimal version in case we need to do a portable build. # Save decimal version in case we need to do a portable build.
self.version = latest == revision and tag or\ self.version = latest == revision and tag or '%s.%s' % (tag, latest)
u'%s.%s' % (tag, latest) version_file = open(os.path.join(self.dist_path, '.version'), 'w')
version_file = open(os.path.join(self.dist_path, u'.version'), u'w') version_file.write(str(version_string))
version_file.write(version_string)
version_file.close() version_file.close()
def copy_default_theme(self):
"""
Copy the default theme to the correct directory for OpenLP.
"""
self._print('Copying default theme...')
source = os.path.join(self.source_path, 'core', 'lib', 'json')
dest = os.path.join(self.dist_path, 'core', 'lib', 'json')
for root, dirs, files in os.walk(source):
for filename in files:
if filename.endswith('.json'):
dest_path = os.path.join(dest, root[len(source) + 1:])
if not os.path.exists(dest_path):
os.makedirs(dest_path)
self._print_verbose('... %s', filename)
copy(os.path.join(root, filename), os.path.join(dest_path, filename))
def copy_plugins(self): def copy_plugins(self):
""" """
Copy all the plugins to the correct directory so that OpenLP sees that Copy all the plugins to the correct directory so that OpenLP sees that
it has plugins. it has plugins.
""" """
self._print(u'Copying plugins...') self._print('Copying plugins...')
source = os.path.join(self.source_path, u'plugins') source = os.path.join(self.source_path, 'plugins')
dest = os.path.join(self.dist_path, u'plugins') dest = os.path.join(self.dist_path, 'plugins')
for root, dirs, files in os.walk(source): for root, dirs, files in os.walk(source):
for filename in files: for filename in files:
if not filename.endswith(u'.pyc'): if not filename.endswith('.pyc'):
dest_path = os.path.join(dest, root[len(source) + 1:]) dest_path = os.path.join(dest, root[len(source) + 1:])
if not os.path.exists(dest_path): if not os.path.exists(dest_path):
os.makedirs(dest_path) os.makedirs(dest_path)
self._print_verbose(u'... %s', filename) self._print_verbose('... %s', filename)
copy(os.path.join(root, filename), copy(os.path.join(root, filename), os.path.join(dest_path, filename))
os.path.join(dest_path, filename))
def copy_media_player(self): def copy_media_player(self):
""" """
Copy the media players to the correct directory for OpenLP. Copy the media players to the correct directory for OpenLP.
""" """
self._print(u'Copying media player...') self._print('Copying media player...')
source = os.path.join(self.source_path, u'core', u'ui', u'media') source = os.path.join(self.source_path, 'core', 'ui', 'media')
dest = os.path.join(self.dist_path, u'core', u'ui', u'media') dest = os.path.join(self.dist_path, 'core', 'ui', 'media')
for root, dirs, files in os.walk(source): for root, dirs, files in os.walk(source):
for filename in files: for filename in files:
if not filename.endswith(u'.pyc'): if not filename.endswith('.pyc'):
dest_path = os.path.join(dest, root[len(source) + 1:]) dest_path = os.path.join(dest, root[len(source) + 1:])
if not os.path.exists(dest_path): if not os.path.exists(dest_path):
os.makedirs(dest_path) os.makedirs(dest_path)
self._print_verbose(u'... %s', filename) self._print_verbose('... %s', filename)
copy(os.path.join(root, filename), copy(os.path.join(root, filename), os.path.join(dest_path, filename))
os.path.join(dest_path, filename))
def copy_windows_files(self): def copy_windows_files(self):
""" """
Copy all the Windows-specific files. Copy all the Windows-specific files.
""" """
self._print(u'Copying extra files for Windows...') self._print('Copying extra files for Windows...')
self._print_verbose(u'... OpenLP.ico') self._print_verbose('... OpenLP.ico')
copy(os.path.join(self.script_path, u'OpenLP.ico'), copy(os.path.join(self.script_path, 'OpenLP.ico'), os.path.join(self.dist_path, 'OpenLP.ico'))
os.path.join(self.dist_path, u'OpenLP.ico')) self._print_verbose('... LICENSE.txt')
self._print_verbose(u'... LICENSE.txt') copy(os.path.join(self.script_path, 'LICENSE.txt'), os.path.join(self.dist_path, 'LICENSE.txt'))
copy(os.path.join(self.script_path, u'LICENSE.txt'), self._print_verbose('... psvince.dll')
os.path.join(self.dist_path, u'LICENSE.txt')) copy(self.psvince, os.path.join(self.dist_path, 'psvince.dll'))
self._print_verbose(u'... psvince.dll') if os.path.isfile(os.path.join(self.helpfile_path, 'OpenLP.chm')):
copy(self.psvince, os.path.join(self.dist_path, u'psvince.dll')) self._print_verbose('... OpenLP.chm')
if os.path.isfile(os.path.join(self.helpfile_path, u'OpenLP.chm')): copy(os.path.join(self.helpfile_path, 'OpenLP.chm'), os.path.join(self.dist_path, 'OpenLP.chm'))
self._print_verbose(u'... OpenLP.chm')
copy(os.path.join(self.helpfile_path, u'OpenLP.chm'),
os.path.join(self.dist_path, u'OpenLP.chm'))
else: else:
self._print(u'... WARNING: Windows help file not found') self._print('... WARNING: Windows help file not found')
def update_translations(self): def update_translations(self):
""" """
Update the translations. Update the translations.
""" """
self._print(u'Updating translations...') self._print('Updating translations...')
if not self.config.has_section('transifex'): if not self.config.has_section('transifex'):
raise Exception(u'No section named "transifex" found.') raise Exception('No section named "transifex" found.')
if not self.config.has_option('transifex', 'username'): if not self.config.has_option('transifex', 'username'):
raise Exception(u'No option named "username" found.') raise Exception('No option named "username" found.')
if not self.config.has_option('transifex', 'password'): if not self.config.has_option('transifex', 'password'):
raise Exception(u'No option named "password" found.') raise Exception('No option named "password" found.')
username = self.config.get(u'transifex', u'username') username = self.config.get('transifex', 'username')
password = self.config.get(u'transifex', u'password') password = self.config.get('transifex', 'password')
os.chdir(os.path.split(self.i18n_utils)[0]) os.chdir(os.path.split(self.i18n_utils)[0])
translation_utils = Popen([self.python, self.i18n_utils, u'-qdpu', translation_utils = Popen([self.python, self.i18n_utils, '-qdpu', '-U', username, '-P', password])
u'-U', username, u'-P', password])
code = translation_utils.wait() code = translation_utils.wait()
if code != 0: if code != 0:
raise Exception(u'Error running translation_utils.py') raise Exception('Error running translation_utils.py')
def compile_translations(self): def compile_translations(self):
""" """
Compile the translations for Qt. Compile the translations for Qt.
""" """
self._print(u'Compiling translations...') self._print('Compiling translations...')
files = os.listdir(self.i18n_path) files = os.listdir(self.i18n_path)
if not os.path.exists(os.path.join(self.dist_path, u'i18n')): if not os.path.exists(os.path.join(self.dist_path, 'i18n')):
os.makedirs(os.path.join(self.dist_path, u'i18n')) os.makedirs(os.path.join(self.dist_path, 'i18n'))
for file in files: for file in files:
if file.endswith(u'.ts'): if file.endswith('.ts'):
self._print_verbose(u'... %s', file) self._print_verbose('... %s', file)
source_path = os.path.join(self.i18n_path, file) source_path = os.path.join(self.i18n_path, file)
dest_path = os.path.join(self.dist_path, u'i18n', dest_path = os.path.join(self.dist_path, 'i18n', file.replace('.ts', '.qm'))
file.replace(u'.ts', u'.qm')) lconvert = Popen((self.lrelease, '-compress', '-silent', source_path, '-qm', dest_path))
lconvert = Popen((self.lrelease, u'-compress', u'-silent',
source_path, u'-qm', dest_path))
code = lconvert.wait() code = lconvert.wait()
if code != 0: if code != 0:
raise Exception(u'Error running lconvert on %s' % \ raise Exception('Error running lconvert on %s' % source_path)
source_path) self._print('Copying qm files...')
self._print(u'Copying qm files...') source = os.path.join(self.site_packages, 'PyQt4', 'translations')
source = os.path.join(self.site_packages, u'PyQt4', u'translations')
files = os.listdir(source) files = os.listdir(source)
for filename in files: for filename in files:
if filename.startswith(u'qt_') and filename.endswith(u'.qm') and \ if filename.startswith('qt_') and filename.endswith('.qm') and len(filename) == 8:
len(filename) == 8: self._print_verbose('... %s', filename)
self._print_verbose(u'... %s', filename) copy(os.path.join(source, filename), os.path.join(self.dist_path, 'i18n', filename))
copy(os.path.join(source, filename),
os.path.join(self.dist_path, u'i18n', filename))
def run_sphinx(self): def run_sphinx(self):
""" """
Run Sphinx to build an HTML Help project. Run Sphinx to build an HTML Help project.
""" """
self._print(u'Deleting previous help manual build... %s', self._print('Deleting previous help manual build... %s', self.manual_build_path)
self.manual_build_path)
if os.path.exists(self.manual_build_path): if os.path.exists(self.manual_build_path):
rmtree(self.manual_build_path) rmtree(self.manual_build_path)
self._print(u'Running Sphinx...') self._print('Running Sphinx...')
os.chdir(self.manual_path) os.chdir(self.manual_path)
sphinx = Popen((self.sphinx, u'-b', u'htmlhelp', u'-d', sphinx = Popen((self.sphinx, '-b', 'htmlhelp', '-d', 'build/doctrees', 'source', 'build/htmlhelp'), stdout=PIPE)
u'build/doctrees', u'source', u'build/htmlhelp'), stdout=PIPE)
output, error = sphinx.communicate() output, error = sphinx.communicate()
code = sphinx.wait() code = sphinx.wait()
if code != 0: if code != 0:
self._print(output) self._print(output)
raise Exception(u'Error running Sphinx') raise Exception('Error running Sphinx')
def run_htmlhelp(self): def run_htmlhelp(self):
""" """
Run HTML Help Workshop to convert the Sphinx output into a manual. Run HTML Help Workshop to convert the Sphinx output into a manual.
""" """
self._print(u'Running HTML Help Workshop...') self._print('Running HTML Help Workshop...')
os.chdir(os.path.join(self.manual_build_path, u'htmlhelp')) os.chdir(os.path.join(self.manual_build_path, 'htmlhelp'))
hhc = Popen((self.hhc, u'OpenLP.chm'), stdout=PIPE) hhc = Popen((self.hhc, 'OpenLP.chm'), stdout=PIPE)
output, error = hhc.communicate() output, error = hhc.communicate()
code = hhc.wait() code = hhc.wait()
if code != 1: if code != 1:
self._print(u'Exit code:', code) self._print('Exit code:', code)
self._print(output) self._print(output)
raise Exception(u'Error running HTML Help Workshop') raise Exception('Error running HTML Help Workshop')
def create_innosetup_file(self): def create_innosetup_file(self):
""" """
Create an InnoSetup file pointing to the branch being built. Create an InnoSetup file pointing to the branch being built.
""" """
self._print(u'Creating Inno Setup file...') self._print('Creating Inno Setup file...')
input = open(os.path.join(self.script_path, input = open(os.path.join(self.script_path, 'OpenLP-2.0.iss.default'), 'r').read()
u'OpenLP-2.0.iss.default'), u'r').read() output = input.replace('%(branch)s', self.branch_path)
output = input.replace(u'%(branch)s', self.branch_path) output = output.replace('%(display_version)s', self.version)
output = output.replace(u'%(display_version)s', self.version) outfile = open(os.path.join(self.script_path, 'OpenLP-2.0.iss'), 'w')
outfile = open(os.path.join(self.script_path,
u'OpenLP-2.0.iss'), u'w')
outfile.write(output) outfile.write(output)
outfile.close() outfile.close()
@ -522,13 +499,11 @@ class WindowsBuilder(object):
Checks the PortableApp directory structure amd creates Checks the PortableApp directory structure amd creates
missing subdirs missing subdirs
""" """
self._print(u' Checking PortableApps directory structure...') self._print(' Checking PortableApps directory structure...')
launcher_path = os.path.join(self.portable_path, u'App', launcher_path = os.path.join(self.portable_path, 'App', 'Appinfo', 'Launcher')
u'Appinfo', u'Launcher')
if not os.path.exists(launcher_path): if not os.path.exists(launcher_path):
os.makedirs(launcher_path) os.makedirs(launcher_path)
settings_path = os.path.join(self.portable_path, u'Data', settings_path = os.path.join(self.portable_path, 'Data', 'Settings')
u'Settings')
if not os.path.exists(settings_path): if not os.path.exists(settings_path):
os.makedirs(settings_path) os.makedirs(settings_path)
@ -536,14 +511,12 @@ class WindowsBuilder(object):
""" """
Create a Portabbleapps appinfo.ini file. Create a Portabbleapps appinfo.ini file.
""" """
self._print(u' Creating PortableApps appinfo file ...') self._print(' Creating PortableApps appinfo file ...')
portable_version = self.version + '.0' * (3 - self.version.count('.')) portable_version = self.version + '.0' * (3 - self.version.count('.'))
input = open(os.path.join(self.script_path, input = open(os.path.join(self.script_path, 'appinfo.ini.default'), 'r').read()
u'appinfo.ini.default'), u'r').read() output = input.replace('%(display_version)s', self.version)
output = input.replace(u'%(display_version)s', self.version) output = output.replace('%(package_version)s', portable_version)
output = output.replace(u'%(package_version)s', portable_version) outfile = open(os.path.join(self.portable_path, 'App', 'Appinfo', 'appinfo.ini'), 'w')
outfile = open(os.path.join(self.portable_path, u'App',
u'Appinfo', u'appinfo.ini'), u'w')
outfile.write(output) outfile.write(output)
outfile.close() outfile.close()
@ -551,13 +524,12 @@ class WindowsBuilder(object):
""" """
Run InnoSetup to create an installer. Run InnoSetup to create an installer.
""" """
self._print(u'Running Inno Setup...') self._print('Running Inno Setup...')
os.chdir(self.script_path) os.chdir(self.script_path)
innosetup = Popen((self.innosetup, innosetup = Popen((self.innosetup, os.path.join(self.script_path, 'OpenLP-2.0.iss'), '/q'))
os.path.join(self.script_path, u'OpenLP-2.0.iss'), u'/q'))
code = innosetup.wait() code = innosetup.wait()
if code != 0: if code != 0:
raise Exception(u'Error running Inno Setup') raise Exception('Error running Inno Setup')
def run_portableapp_builder(self): def run_portableapp_builder(self):
""" """
@ -566,105 +538,92 @@ class WindowsBuilder(object):
2 Builds the PortableApps Launcher 2 Builds the PortableApps Launcher
3 Builds the PortableApps Install 3 Builds the PortableApps Install
""" """
self._print(u'Running PortableApps Builder...') self._print('Running PortableApps Builder...')
self._print(u' Clearing old files') self._print(' Clearing old files')
# Remove previous contents of portableapp build directory. # Remove previous contents of portableapp build directory.
if os.path.exists(self.portable_path): if os.path.exists(self.portable_path):
rmtree(self.portable_path) rmtree(self.portable_path)
self._print(u' Creating PortableApps build directory') self._print(' Creating PortableApps build directory')
# Copy the contents of the OpenLPPortable directory to the portable # Copy the contents of the OpenLPPortable directory to the portable
# build directory. # build directory.
dir_util.copy_tree(os.path.join(self.script_path, u'OpenLPPortable'), dir_util.copy_tree(os.path.join(self.script_path, 'OpenLPPortable'), self.portable_path)
self.portable_path)
self.check_portableapp_directory() self.check_portableapp_directory()
self.create_portableapps_appinfo_file() self.create_portableapps_appinfo_file()
# Copy distribution files to portableapp build directory. # Copy distribution files to portableapp build directory.
self._print(u' Copying distribution files') self._print(' Copying distribution files')
portable_app_path = os.path.join(self.portable_path, u'App', u'OpenLP') portable_app_path = os.path.join(self.portable_path, 'App', 'OpenLP')
dir_util.copy_tree(self.dist_path, portable_app_path) dir_util.copy_tree(self.dist_path, portable_app_path)
# Copy help files to portableapp build directory. # Copy help files to portableapp build directory.
self._print(u' Copying help files') self._print(' Copying help files')
dir_util.copy_tree(self.helpfile_path, dir_util.copy_tree(self.helpfile_path, os.path.join(portable_app_path, 'help'))
os.path.join(portable_app_path, u'help'))
# Build the launcher. # Build the launcher.
self._print(u' Building PortableApps Launcher') self._print(' Building PortableApps Launcher')
portableapps = Popen((self.portablelauncher, self.portable_path), portableapps = Popen((self.portablelauncher, self.portable_path), stdout=PIPE)
stdout=PIPE)
code = portableapps.wait() code = portableapps.wait()
if code != 0: if code != 0:
raise Exception(u'Error creating PortableAppa Launcher') raise Exception('Error creating PortableAppa Launcher')
# Build the portable installer. # Build the portable installer.
self._print(u' Building PortableApps Installer') self._print(' Building PortableApps Installer')
portableapps = Popen((self.portableinstaller, self.portable_path), portableapps = Popen((self.portableinstaller, self.portable_path), stdout=PIPE)
stdout=PIPE)
code = portableapps.wait() code = portableapps.wait()
if code != 0: if code != 0:
raise Exception(u'Error running PortableApps Installer') raise Exception('Error running PortableApps Installer')
portable_app = os.path.abspath(os.path.join(self.portable_path, u'..', portable_app = os.path.abspath(os.path.join(self.portable_path, '..',
u'OpenLPPortable_%s.paf.exe' % self.version)) 'OpenLPPortable_%s.paf.exe' % self.version))
if os.path.exists(portable_app): if os.path.exists(portable_app):
move(portable_app, os.path.abspath( move(portable_app, os.path.abspath(os.path.join(self.dist_path, '..')))
os.path.join(self.dist_path, u'..'))) self._print(' PortableApp build complete')
self._print(u' PortableApp build complete')
else: else:
raise Exception(u'PortableApp failed to build') raise Exception('PortableApp failed to build')
def build_pptviewlib(self): def build_pptviewlib(self):
""" """
Build the PowerPoint Viewer DLL using Visual Studio. Build the PowerPoint Viewer DLL using Visual Studio.
""" """
self._print(u'Building PPTVIEWLIB.DLL...') self._print('Building PPTVIEWLIB.DLL...')
vcbuild = Popen((self.vcbuild, u'/rebuild', vcbuild = Popen((self.vcbuild, '/rebuild', os.path.join(self.pptviewlib_path, 'pptviewlib.vcproj'),
os.path.join(self.pptviewlib_path, u'pptviewlib.vcproj'), 'Release|Win32'))
u'Release|Win32'))
code = vcbuild.wait() code = vcbuild.wait()
if code != 0: if code != 0:
raise Exception(u'Error building pptviewlib.dll') raise Exception('Error building pptviewlib.dll')
copy(os.path.join(self.pptviewlib_path, u'Release', copy(os.path.join(self.pptviewlib_path, 'Release', 'pptviewlib.dll'), self.pptviewlib_path)
u'pptviewlib.dll'), self.pptviewlib_path)
def main(self): def main(self):
""" """
The main function to run the Windows builder. The main function to run the Windows builder.
""" """
self._print_verbose(u'OpenLP main script: ......%s', self._print_verbose('OpenLP main script: ......%s', self.openlp_script)
self.openlp_script) self._print_verbose('Script path: .............%s', os.path.split(os.path.abspath(__file__))[0])
self._print_verbose(u'Script path: .............%s', self._print_verbose('Branch path: .............%s', self.branch_path)
os.path.split(os.path.abspath(__file__))[0]) self._print_verbose('Source path: .............%s', self.source_path)
self._print_verbose(u'Branch path: .............%s', self.branch_path) self._print_verbose('Dist path: ...............%s', self.dist_path)
self._print_verbose(u'Source path: .............%s', self.source_path) self._print_verbose('Portable path: ...........%s', self.portable_path)
self._print_verbose(u'Dist path: ...............%s', self.dist_path) self._print_verbose('PyInstaller: .............%s', self.pyinstaller)
self._print_verbose(u'Portable path: ...........%s', self._print_verbose('Documentation branch path:%s', self.docs_path)
self.portable_path) self._print_verbose('Help file build path: ....%s', self.helpfile_path)
self._print_verbose(u'PyInstaller: .............%s', self.pyinstaller) self._print_verbose('Inno Setup path: .........%s', self.innosetup)
self._print_verbose(u'Documentation branch path:%s', self.docs_path) self._print_verbose('PortableApp Launcher......%s', self.portablelauncher)
self._print_verbose(u'Help file build path: ....%s', self._print_verbose('PortableApp Installer.....%s', self.portableinstaller)
self.helpfile_path) self._print_verbose('Windows resources: .......%s', self.winres_path)
self._print_verbose(u'Inno Setup path: .........%s', self.innosetup) self._print_verbose('VCBuild path: ............%s', self.vcbuild)
self._print_verbose(u'PortableApp Launcher......%s', self._print_verbose('PPTVIEWLIB path: .........%s', self.pptviewlib_path)
self.portablelauncher) self._print_verbose('')
self._print_verbose(u'PortableApp Installer.....%s',
self.portableinstaller)
self._print_verbose(u'Windows resources: .......%s', self.winres_path)
self._print_verbose(u'VCBuild path: ............%s', self.vcbuild)
self._print_verbose(u'PPTVIEWLIB path: .........%s',
self.pptviewlib_path)
self._print_verbose(u'')
if not self.args.skip_update: if not self.args.skip_update:
self.update_code() self.update_code()
self.build_pptviewlib() self.build_pptviewlib()
self.run_pyinstaller() self.run_pyinstaller()
self.write_version_file() self.write_version_file()
self.copy_default_theme()
self.copy_plugins() self.copy_plugins()
self.copy_media_player() self.copy_media_player()
if os.path.exists(self.manual_path): if os.path.exists(self.manual_path):
self.run_sphinx() self.run_sphinx()
self.run_htmlhelp() self.run_htmlhelp()
else: else:
self._print(u'') self._print('')
self._print(u'WARNING: Documentation trunk not found. Windows') self._print('WARNING: Documentation trunk not found. Windows')
self._print(u' Help file will not be included in build') self._print(' Help file will not be included in build')
self._print(u'') self._print('')
self.copy_windows_files() self.copy_windows_files()
if not self.args.skip_translations: if not self.args.skip_translations:
self.update_translations() self.update_translations()
@ -673,7 +632,8 @@ class WindowsBuilder(object):
self.run_innosetup() self.run_innosetup()
if self.args.portable: if self.args.portable:
self.run_portableapp_builder() self.run_portableapp_builder()
self._print(u'Done.') self._print('Done.')
if __name__ == u'__main__':
if __name__ == '__main__':
WindowsBuilder().main() WindowsBuilder().main()