diff --git a/builders/builder.py b/builders/builder.py index 6aec400..308a39d 100644 --- a/builders/builder.py +++ b/builders/builder.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4 +# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4 ############################################################################### # OpenLP - Open Source Lyrics Projection # @@ -216,7 +216,7 @@ class Builder(object): else: self.version = None self.work_path = self.branch_path - self.openlp_script = os.path.abspath(os.path.join(self.work_path, 'openlp-run.py')) + self.openlp_script = os.path.abspath(os.path.join(self.work_path, 'run_openlp.py')) self.source_path = os.path.join(self.work_path, 'openlp') self.manual_path = os.path.join(self.documentation_path, 'manual') self.manual_build_path = os.path.join(self.manual_path, 'build') @@ -267,10 +267,12 @@ class Builder(object): Run PyInstaller on the branch to build an executable. """ self._print('Running PyInstaller...') - copy(os.path.join(self.work_path, 'openlp.py'), self.openlp_script) os.chdir(self.work_path) - cmd = [self.python, - self.pyinstaller_exe, + if self.pyinstaller_exe.endswith('.py'): + cmd = [self.python, self.pyinstaller_exe] + else: + cmd = [self.pyinstaller_exe] + cmd.extend([ '--clean', '--noconfirm', '--windowed', @@ -279,7 +281,8 @@ class Builder(object): '--runtime-hook', os.path.join(self.hooks_path, 'rthook_ssl.py'), '-i', self.icon_path, '-n', 'OpenLP', - self.openlp_script] + self.openlp_script + ]) if self.args.verbose: cmd.append('--log-level=DEBUG') else: @@ -423,7 +426,8 @@ class Builder(object): rmtree(self.manual_build_path) os.chdir(self.manual_path) sphinx_build = self.get_sphinx_build() - command = [self.sphinx_exe, '-b', sphinx_build, '-d', 'build/doctrees', 'source', 'build/{}'.format(sphinx_build)] + command = [self.sphinx_exe, '-b', sphinx_build, '-d', 'build/doctrees', 'source', + 'build/{}'.format(sphinx_build)] self._run_command(command, 'Error running Sphinx') self.after_run_sphinx() @@ -472,5 +476,3 @@ class Builder(object): self._print('Done.') raise SystemExit() - - diff --git a/builders/windows-builder.py b/builders/windows-builder.py index a0086f3..f2b8d47 100644 --- a/builders/windows-builder.py +++ b/builders/windows-builder.py @@ -118,8 +118,15 @@ import os from distutils import dir_util from shutil import copy, move, rmtree +from lxml.etree import fromstring, tostring +from lxml.builder import E + from builder import Builder +BLACKLIST = [ + 'pptviewlib.dll.intermediate.manifest' +] + class WindowsBuilder(Builder): """ @@ -138,28 +145,95 @@ class WindowsBuilder(Builder): 'Release|Win32'], 'Error building pptviewlib.dll') copy(os.path.join(self.pptviewlib_path, 'Release', 'pptviewlib.dll'), self.pptviewlib_path) - def _create_innosetup_file(self): + def _walk_dirs(self, dir_dict, path): """ - Create an InnoSetup file pointing to the branch being built. + Walk a dictionary according to path """ - self._print('Creating Inno Setup file...') - config_dir = os.path.dirname(self.config_path) - with open(os.path.join(config_dir, 'OpenLP.iss.default'), 'r') as input_file, \ - open(os.path.join(config_dir, 'OpenLP.iss'), 'w') as output_file: - content = input_file.read() - content = content.replace('%(branch)s', self.branch_path) - content = content.replace('%(display_version)s', self.version.replace('-bzr', '.')) - output_file.write(content) + parts = path.split(os.sep) + search_key = parts.pop(0) + if search_key in dir_dict.keys(): + if not parts: + return dir_dict[search_key] + else: + return walk_dirs(dir_dict[search_key], os.sep.join(parts)) + else: + return None - def _run_innosetup(self): + def _get_fragments_from_files(self, start_dir): """ - Run InnoSetup to create an installer. + Walk down a directory recursively and build up the XML for WiX """ - self._print('Running Inno Setup...') + start_base, start_path = os.path.split(start_dir) + element = E.DirectoryRef(Id='INSTALLDIR') + directories = {start_path: {'__dir__': element}} + components = [] + + for root, _, files in os.walk(start_dir): + parent = os.sep.join(root.replace(os.path.join(start_base, ''), '').split(os.sep)[:-1]) + if root == start_dir: + path = '' + else: + path = root.replace(os.path.join(start_dir, ''), '') + base = os.path.basename(root) + if root != start_dir: + dir_id = 'dir_{parent}_{base}'.format(parent=parent.replace(os.sep, '_'), base=base) + element = E.Directory(Id=dir_id, Name=base) + new_dir = {'__dir__': element} + parent_dir = walk_dirs(directories, parent) + parent_dir[base] = new_dir + parent_dir['__dir__'].append(element) + for fname in files: + if fname in BLACKLIST: + continue + source = os.path.join(path, fname) if path else fname + file_id = 'file_{source}'.format(source=source.replace('-', '_').replace(os.sep, '_')) + file_ = E.File(Id=file_id, KeyPath="yes", Source=source) + component = E.Component(file_, Id='cmp_' + fixed_id, Guid='*') + element.append(component) + components.append(component) + + files_fragment = E.Fragment(directories[start_path]['__dir__']) + comps_fragment = E.Fragment(E.ComponentGroup(*[E.ComponentRef(Id=c.attrib['Id']) for c in components], Id='Files')) + return files_fragment, comps_fragment + + def _create_wix_file(self): + """ + Create a WiX project file + """ + self._print('Creating WiX file...') config_dir = os.path.dirname(self.config_path) - os.chdir(config_dir) - self._run_command([self.innosetup_exe, os.path.join(config_dir, 'OpenLP.iss'), '/q'], - 'Error running InnoSetup') + self._print_verbose('Reading base WiX file') + with open(os.path.join(config_dir, 'OpenLP-base.wxs'), 'rb') as base_file: + xml = base_file.read() + xml = xml.format(dialog=os.path.join(config_dir, 'WizardMain.bmp'), + banner=os.path.join(config_dir, 'WizardBanner.bmp')) + tree = fromstring(xml) + self._print_verbose('Creating XML fragments from files and directories') + fragments = self._get_fragments_from_files(self.dist_path) + self._print_verbose('Inserting XML fragments into base WiX file') + wix = base_tree.getroot() + for fragment in fragments: + wix.append(fragment) + self._print_verbose('Writing new WiX file') + with open(os.path.join(self.config_path, 'OpenLP.wxs'), 'wb') as f: + f.write(tostring(tree, encoding='utf-8', xml_declaration=True, pretty_print=True)) + + def _run_wix_tools(self): + """ + Run the WiX toolset to create an installer + """ + self._print('Running WiX tools...') + msi_file = os.path.join(self.dist_path, 'OpenLP-{}.msi'.format(self.version)) + if msi_file: + self._print_verbose('Removing old MSI file') + os.unlink(msi_file) + config_dir = os.path.dirname(self.config_path) + os.chdir(self.dist_path) + self._run_command([self.candle_exe, '-ext', 'WiXUtilExtension', os.path.join(config_dir, 'OpenLP.wxs')], + 'Error running WiX tool: candle') + self._run_command([self.light_exe, '-ext', 'WiXUtilExtension', '-ext', 'WixUIExtension', 'OpenLP.wixobj', + '-o', msi_file], + 'Error running WiX tool: light') def _create_portableapp_structure(self): """ @@ -335,8 +409,8 @@ class WindowsBuilder(Builder): Build the installer """ self._build_pptviewlib() - self._create_innosetup_file() - self._run_innosetup() + self._create_wix_file() + self._run_wix_tools() if self.args.portable: self._run_portableapp_builder() diff --git a/windows/OpenLP-base.wxs b/windows/OpenLP-base.wxs new file mode 100644 index 0000000..d56b20d --- /dev/null +++ b/windows/OpenLP-base.wxs @@ -0,0 +1,79 @@ + + + + + Privileged + + + + + + + + + WIXUI_EXITDIALOGOPTIONALCHECKBOX = 1 and NOT Installed + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/windows/WizardBanner.bmp b/windows/WizardBanner.bmp new file mode 100644 index 0000000..7e4fbbc Binary files /dev/null and b/windows/WizardBanner.bmp differ diff --git a/windows/WizardMain.bmp b/windows/WizardMain.bmp new file mode 100644 index 0000000..d5ddc20 Binary files /dev/null and b/windows/WizardMain.bmp differ