From 585ccfc78922393a375160ee3d994ddfcd716f6a Mon Sep 17 00:00:00 2001 From: Michael Brockus <55331536+michaelbadcrumble@users.noreply.github.com> Date: Mon, 16 Dec 2019 08:33:54 -0800 Subject: [PATCH] Clean MSI generator Try 2 [skip ci] --- msi/createmsi.py | 113 +++++++++++++++++++++++++++++++++++------------ 1 file changed, 85 insertions(+), 28 deletions(-) diff --git a/msi/createmsi.py b/msi/createmsi.py index 9b4e105c3..f80d1dccb 100644 --- a/msi/createmsi.py +++ b/msi/createmsi.py @@ -13,8 +13,15 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. - -import sys, os, subprocess, shutil, uuid +''' + This script is for generating MSI packages + for Windows users. +''' +import subprocess +import shutil +import uuid +import sys +import os from glob import glob import platform import xml.etree.ElementTree as ET @@ -23,27 +30,53 @@ sys.path.append(os.getcwd()) from mesonbuild import coredata def gen_guid(): + ''' + Generate guid + ''' return str(uuid.uuid4()).upper() class Node: + ''' + Node to hold path and directory values + ''' + def __init__(self, dirs, files): - assert(isinstance(dirs, list)) - assert(isinstance(files, list)) + self.check_dirs(dirs) + self.check_files(files) self.dirs = dirs self.files = files + @staticmethod + def check_dirs(dirs): + ''' + Check to see if directory is instance of list + ''' + assert isinstance(dirs, list) + + @staticmethod + def check_files(files): + ''' + Check to see if files is instance of list + ''' + assert isinstance(files, list) + + class PackageGenerator: + ''' + Package generator for MSI pacakges + ''' def __init__(self): self.product_name = 'Meson Build System' self.manufacturer = 'The Meson Development Team' self.version = coredata.version.replace('dev', '') + self.root = None self.guid = '*' self.update_guid = '141527EE-E28A-4D14-97A4-92E6075D28B2' self.main_xml = 'meson.wxs' self.main_o = 'meson.wixobj' self.bytesize = 32 if '32' in platform.architecture()[0] else 64 - self.final_output = 'meson-%s-%d.msi' % (self.version, self.bytesize) + self.final_output = 'meson-{}-{}.msi'.format(self.version, self.bytesize) self.staging_dirs = ['dist', 'dist2'] if self.bytesize == 64: self.progfile_dir = 'ProgramFiles64Folder' @@ -72,18 +105,27 @@ class PackageGenerator: } } self.feature_components = {} - for sd in self.staging_dirs: - self.feature_components[sd] = [] + for s_d in self.staging_dirs: + self.feature_components[s_d] = [] - def get_all_modules_from_dir(self, dirname): + @staticmethod + def get_all_modules_from_dir(dirname): + ''' + Get all modules required for Meson build MSI package + from directories. + ''' modname = os.path.basename(dirname) modules = [os.path.splitext(os.path.split(x)[1])[0] for x in glob(os.path.join(dirname, '*'))] modules = ['mesonbuild.' + modname + '.' + x for x in modules if not x.startswith('_')] return modules - def get_more_modules(self): - # Python packagers want to minimal and only copy the things that - # they can see that are used. They are blind to many things. + @staticmethod + def get_more_modules(): + ''' + Getter for missing Modules. + Python packagers want to be minimal and only copy the things + that they can see that being used. They are blind to many things. + ''' return ['distutils.archive_util', 'distutils.cmd', 'distutils.config', @@ -104,6 +146,9 @@ class PackageGenerator: ] def build_dist(self): + ''' + Build dist file from PyInstaller info + ''' for sdir in self.staging_dirs: if os.path.exists(sdir): shutil.rmtree(sdir) @@ -137,6 +182,9 @@ class PackageGenerator: sys.exit('Ninja exe missing from staging dir.') def generate_files(self): + ''' + Generate package files for MSI installer package + ''' self.root = ET.Element('Wix', {'xmlns': 'http://schemas.microsoft.com/wix/2006/wi'}) product = ET.SubElement(self.root, 'Product', { 'Name': self.product_name, @@ -148,10 +196,10 @@ class PackageGenerator: 'Version': self.version, }) - package = ET.SubElement(product, 'Package', { + package = ET.SubElement(product, 'Package', { 'Id': '*', 'Keywords': 'Installer', - 'Description': 'Meson %s installer' % self.version, + 'Description': 'Meson {} installer'.format(self.version), 'Comments': 'Meson is a high performance build system', 'Manufacturer': 'The Meson Development Team', 'InstallerVersion': '500', @@ -195,8 +243,8 @@ class PackageGenerator: ET.SubElement(product, 'UIRef', { 'Id': 'WixUI_FeatureTree', }) - for sd in self.staging_dirs: - assert(os.path.isdir(sd)) + for s_d in self.staging_dirs: + assert os.path.isdir(s_d) top_feature = ET.SubElement(product, 'Feature', { 'Id': 'Complete', 'Title': 'Meson ' + self.version, @@ -205,13 +253,13 @@ class PackageGenerator: 'Level': '1', 'ConfigurableDirectory': 'INSTALLDIR', }) - for sd in self.staging_dirs: + for s_d in self.staging_dirs: nodes = {} - for root, dirs, files in os.walk(sd): + for root, dirs, files in os.walk(s_d): cur_node = Node(dirs, files) nodes[root] = cur_node - self.create_xml(nodes, sd, installdir, sd) - self.build_features(nodes, top_feature, sd) + self.create_xml(nodes, s_d, installdir, s_d) + self.build_features(top_feature, s_d) vcredist_feature = ET.SubElement(top_feature, 'Feature', { 'Id': 'VCRedist', 'Title': 'Visual C++ runtime', @@ -224,20 +272,26 @@ class PackageGenerator: # ElementTree can not do prettyprinting so do it manually import xml.dom.minidom doc = xml.dom.minidom.parse(self.main_xml) - with open(self.main_xml, 'w') as of: - of.write(doc.toprettyxml()) + with open(self.main_xml, 'w') as open_file: + open_file.write(doc.toprettyxml()) - def build_features(self, nodes, top_feature, staging_dir): - feature = ET.SubElement(top_feature, 'Feature', self.feature_properties[staging_dir]) + def build_features(self, top_feature, staging_dir): + ''' + Generate build features + ''' + feature = ET.SubElement(top_feature, 'Feature', self.feature_properties[staging_dir]) for component_id in self.feature_components[staging_dir]: ET.SubElement(feature, 'ComponentRef', { 'Id': component_id, }) def create_xml(self, nodes, current_dir, parent_xml_node, staging_dir): + ''' + Create XML file + ''' cur_node = nodes[current_dir] if cur_node.files: - component_id = 'ApplicationFiles%d' % self.component_num + component_id = 'ApplicationFiles{}'.format(self.component_num) comp_xml_node = ET.SubElement(parent_xml_node, 'Component', { 'Id': component_id, 'Guid': gen_guid(), @@ -255,12 +309,12 @@ class PackageGenerator: 'Value': '[INSTALLDIR]', }) self.component_num += 1 - for f in cur_node.files: - file_id = os.path.join(current_dir, f).replace('\\', '_').replace('#', '_').replace('-', '_') + for f_node in cur_node.files: + file_id = os.path.join(current_dir, f_node).replace('\\', '_').replace('#', '_').replace('-', '_') ET.SubElement(comp_xml_node, 'File', { 'Id': file_id, - 'Name': f, - 'Source': os.path.join(current_dir, f), + 'Name': f_node, + 'Source': os.path.join(current_dir, f_node), }) for dirname in cur_node.dirs: @@ -272,6 +326,9 @@ class PackageGenerator: self.create_xml(nodes, os.path.join(current_dir, dirname), dir_node, staging_dir) def build_package(self): + ''' + Generate the Meson build MSI package. + ''' wixdir = 'c:\\Program Files\\Wix Toolset v3.11\\bin' if not os.path.isdir(wixdir): wixdir = 'c:\\Program Files (x86)\\Wix Toolset v3.11\\bin'