Clean MSI generator Try 2 [skip ci]

pull/6357/head
Michael Brockus 5 years ago committed by Jussi Pakkanen
parent eb67fb1d97
commit 585ccfc789
  1. 113
      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'

Loading…
Cancel
Save