Skip to content

Commit

Permalink
Add python script with templates for creating msi installer with WIX
Browse files Browse the repository at this point in the history
  • Loading branch information
Christian Knieling authored and chr11115 committed Nov 16, 2022
1 parent 76f0bfe commit f6d83f3
Show file tree
Hide file tree
Showing 11 changed files with 316 additions and 0 deletions.
247 changes: 247 additions & 0 deletions deploy/build.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,247 @@
import argparse
import glob
import subprocess
import json
import shutil
import os
from pathlib import Path
from string import Template

# Dirs and Paths
SCRIPT_DIR = Path(__file__).resolve().parent
CFG_PATH = SCRIPT_DIR / f'installer_config.json'
RESOURCE_DIR = SCRIPT_DIR / f'resources'

VERSION_PATH = SCRIPT_DIR / f'../version.txt'

DEFAULT_BUILD_DIR = SCRIPT_DIR / f'../build/build_msi'

TEMPLATE_DIR = SCRIPT_DIR / f'templates'
DEPLOYMENT_PROPERTIES_TEMPLATE_PATH = TEMPLATE_DIR / 'deployment.properties.in'

WIX_TEMPLATE_PATH = TEMPLATE_DIR / f'palladio.wxs.in'
WIX_REG_SEARCH_TEMPLATE_PATH = TEMPLATE_DIR / f'palladio_registry_search.in'
WIX_DIRECTORY_TEMPLATE_PATH = TEMPLATE_DIR / f'palladio_directory.in'
WIX_FEATURE_TEMPLATE_PATH = TEMPLATE_DIR / f'palladio_feature.in'

PROJECT_NAME = 'Palladio'
PLATFORM = 'win64'
PACKAGE_VENDOR_ESCAPED = 'Esri R&D Center Zurich'


def get_palladio_version_string(includePre=False, build_version=''):
version_map = {}
delimiter = '.'
with open(VERSION_PATH, 'r') as version_file:
version_text = version_file.read()
lines = version_text.splitlines()
for line in lines:
key_value = line.split(' ')
if len(key_value) == 2:
version_map[key_value[0]] = key_value[1]
version_string = version_map['PLD_VERSION_MAJOR'] + delimiter + \
version_map['PLD_VERSION_MINOR'] + \
delimiter + version_map['PLD_VERSION_PATCH']
if includePre:
version_string += version_map['PLD_VERSION_PRE']
if len(build_version) > 0:
version_string += '+b' + build_version
return version_string


def gen_installer_filename(build_version):
return PROJECT_NAME + '-installer-' + get_palladio_version_string(True, build_version) + '.msi'


def rel_to_os_dir(path):
return Path(path).absolute()


def clear_folder(path):
if path.exists():
shutil.rmtree(path)
os.makedirs(path)


def run_wix_command(cmd, build_dir):
process = subprocess.run(args=[str(i) for i in cmd], cwd=build_dir, stdout=subprocess.PIPE, stderr=subprocess.PIPE,
universal_newlines=True)
print(process.args)
print(process.stdout)
print(process.stderr)


def wix_harvest(houdini_version, binaries_install_dir, build_dir):
output_path = build_dir / f'houdini{houdini_version}.wxs'
wxs_file = output_path
wxs_var = f'-dPalladioInstallComponentDir{houdini_version}={binaries_install_dir}'
heat_cmd = ['heat', 'dir', binaries_install_dir, '-dr', f'PalladioInstallComponentDir{houdini_version}', '-ag',
'-cg', f'PalladioInstallComponent{houdini_version}', '-sfrag', '-template fragment', '-var', f'wix.PalladioInstallComponentDir{houdini_version}', '-srd', '-sreg', '-o', output_path]

run_wix_command(heat_cmd, build_dir)

return wxs_file, wxs_var


def wix_compile(sources, vars, build_dir):
candle_cmd = ['candle', '-arch', 'x64',
build_dir / 'palladio.wxs'] + sources + vars
run_wix_command(candle_cmd, build_dir)

objects = glob.glob(str(build_dir / '*.wixobj'))
return objects


def wix_link(objects, vars, build_dir, installer_filename):
light_cmd = ['light', f'-dWixUILicenseRtf={SCRIPT_DIR}\\resources\\license.rtf', f'-dInstallDir=', '-ext', 'WixUIExtension',
'-cultures:en-us', '-ext', 'WixUtilExtension', '-o', f'{build_dir}\\' + installer_filename]
light_cmd.extend(objects)
light_cmd.extend(vars)
run_wix_command(light_cmd, build_dir)


def create_installer(houdini_versions, binary_folders, build_dir, installer_filename):
wxs_files = []
wxs_vars = []
for houdini_version_key in binary_folders:
rel_path_string = binary_folders[houdini_version_key]
major = houdini_versions[houdini_version_key]['major']
minor = houdini_versions[houdini_version_key]['minor']

binary_path = rel_to_os_dir(rel_path_string)

print(binary_path, " ", houdini_version_key)
wxs_file, wxs_var = wix_harvest(
str(major) + str(minor), binary_path, build_dir)
wxs_files.append(wxs_file)
wxs_vars.append(wxs_var)

wix_objects = wix_compile(wxs_files, wxs_vars, build_dir)
wix_link(wix_objects, wxs_vars, build_dir, installer_filename)


def copy_binaries(binary_folders, build_dir):
clear_folder(build_dir)

valid_binaries = {}
for binary_folder in binary_folders:
src_path = binary_folders[binary_folder]
if len(src_path) == 0:
continue
os_src_path = rel_to_os_dir(src_path)
if os_src_path.exists():
binary_path = build_dir / f'install' / Path(binary_folder)
print("copy ", os_src_path, " to ", binary_path)
shutil.copytree(os_src_path, binary_path)
valid_binaries[binary_folder] = binary_path
return valid_binaries


def fill_template(src_path, token_value_dict):
result = ''
with open(src_path, 'r') as file:
src = Template(file.read())
result = src.substitute(token_value_dict)
return result


def fill_template_to_file(src_path, dest_path, token_value_dict):
result = fill_template(src_path, token_value_dict)
with open(dest_path, 'w') as outfile:
outfile.write(result)


def fill_wix_template(houdini_versions, copied_binaries, build_dir):
wix_reg_search = ''
wix_directories = ''
wix_features = ''

for version in copied_binaries:
version_dict = {
'HOU_MAJOR': houdini_versions[version]['major'],
'HOU_MINOR': houdini_versions[version]['minor'],
'HOU_PATCH': houdini_versions[version]['patch']
}
newline = ''
if len(wix_reg_search) > 0:
newline = '\n'
wix_reg_search += newline + \
fill_template(WIX_REG_SEARCH_TEMPLATE_PATH, version_dict)
wix_directories += newline + \
fill_template(WIX_DIRECTORY_TEMPLATE_PATH, version_dict)
wix_features += newline + \
fill_template(WIX_FEATURE_TEMPLATE_PATH, version_dict)

wix_properties = {
'PROJECT_NAME': PROJECT_NAME,
'PLD_VERSION_MMP': get_palladio_version_string(),
'PACKAGE_VENDOR_ESCAPED': PACKAGE_VENDOR_ESCAPED,
'RESOURCE_FOLDER': RESOURCE_DIR,
'HOUDINI_REGISTRY_SEARCH': wix_reg_search,
'HOUDINI_DIRECTORY': wix_directories,
'PALLADIO_FOR_HOUDINI_FEATURE': wix_features
}
fill_template_to_file(WIX_TEMPLATE_PATH, build_dir /
f'palladio.wxs', wix_properties)


def fill_deployment_properties_templates(installer_filename, build_dir, build_version):
deployment_properties = {
'PACKAGE_NAME': PROJECT_NAME,
'PLD_VERSION_BASE': get_palladio_version_string(),
'PLD_VERSION': get_palladio_version_string(True, build_version),
'PACKAGE_FILE_NAME': installer_filename,
'PLD_PKG_OS': PLATFORM
}
fill_template_to_file(DEPLOYMENT_PROPERTIES_TEMPLATE_PATH, build_dir /
f'deployment.properties', deployment_properties)


def parse_arguments(houdini_versions):
parser = argparse.ArgumentParser(
description='Build unified MSI installer for the Palladio Plugin')
for houdini_version in houdini_versions:
h_major = houdini_versions[houdini_version]['major']
h_minor = houdini_versions[houdini_version]['minor']
parser.add_argument('-h' + h_major + h_minor, '--' + houdini_version, default='',
help='path to binary location of Palladio build for ' + houdini_version)

parser.add_argument('-bv', '--build_version', default='',
help='build version for current Palladio build')

parser.add_argument('-bd', '--build_dir', default=str(DEFAULT_BUILD_DIR),
help='build directory where installer files are generated')

parsed_args = vars(parser.parse_args())
binary_folders = {}
for item in parsed_args:
if item.__contains__('houdini'):
binary_folders[item] = parsed_args[item]
return binary_folders, parsed_args['build_version'], Path(parsed_args['build_dir']).resolve()


def main():
with open(CFG_PATH, 'r') as config_file:
installer_config = json.load(config_file)

houdini_versions = installer_config['houdini_versions']

binary_folders, build_version, build_dir = parse_arguments(houdini_versions)

copied_binaries = copy_binaries(binary_folders, build_dir)
installer_filename = gen_installer_filename(build_version)

if len(copied_binaries) == 0:
print('Please provide at least one palladio binary folder, see "build.py --help" for help')
return

fill_deployment_properties_templates(
installer_filename, build_dir, build_version)
fill_wix_template(houdini_versions, copied_binaries, build_dir)

create_installer(houdini_versions, copied_binaries,
build_dir, installer_filename)


if __name__ == '__main__':
main()
19 changes: 19 additions & 0 deletions deploy/installer_config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"houdini_versions":{
"houdini18.5": {
"major": "18",
"minor": "5",
"patch": "759"
},
"houdini19.0": {
"major": "19",
"minor": "0",
"patch": "720"
},
"houdini19.5": {
"major": "19",
"minor": "5",
"patch": "303"
}
}
}
Binary file added deploy/resources/banner.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added deploy/resources/dialog.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added deploy/resources/license.rtf
Binary file not shown.
Binary file added deploy/resources/palladio.ico
Binary file not shown.
5 changes: 5 additions & 0 deletions deploy/templates/deployment.properties.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package_name = ${PACKAGE_NAME}
package_version_base = ${PLD_VERSION_BASE}
package_version = ${PLD_VERSION}
package_file = ${PACKAGE_FILE_NAME}
package_classifier = ${PLD_PKG_OS}
37 changes: 37 additions & 0 deletions deploy/templates/palladio.wxs.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<?xml version="1.0"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
<Product Name="${PROJECT_NAME}" Manufacturer="${PACKAGE_VENDOR_ESCAPED}" Version="${PLD_VERSION_MMP}"
Id="*" UpgradeCode="D5336B62-F851-4D34-B6CA-0A9A3B685910" Language="1033" Codepage='1252'>

<Package Platform='x64' Id='*' Keywords='Installer' Description="${PROJECT_NAME}"
Manufacturer='${PACKAGE_VENDOR_ESCAPED}' InstallerVersion='200'
Languages='1033' Compressed='yes' SummaryCodepage='1252' />

<Media Id='1' Cabinet='Sample.cab' EmbedCab='yes' DiskPrompt="CD-ROM #1" />
<Property Id='DiskPrompt' Value="${PROJECT_NAME} Installation [1]" />

${HOUDINI_REGISTRY_SEARCH}

<MajorUpgrade Schedule="afterInstallInitialize" AllowSameVersionUpgrades="yes"
DowngradeErrorMessage="A later version of [ProductName] is already installed.&#xA;
Please uninstall [ProductName] first if you want to downgrade to version [ProductVersion].&#xA;
Setup will now exit."/>

<Property Id="ARPPRODUCTICON">ProductIcon.ico</Property>
<Icon Id="ProductIcon.ico" SourceFile="${RESOURCE_FOLDER}\palladio.ico"/>
<WixVariable Id="WixUIBannerBmp" Value="${RESOURCE_FOLDER}\banner.png"/>
<WixVariable Id="WixUIDialogBmp" Value="${RESOURCE_FOLDER}\dialog.png"/>
<PropertyRef Id="WIX_DIR_PERSONAL"/>

<Directory Id='TARGETDIR' Name='SourceDir' ComponentGuidGenerationSeed="D676EF3E-B710-444A-BCCC-F11F888CD928">
<Directory Id='WIX_DIR_PERSONAL'>
${HOUDINI_DIRECTORY}
</Directory>
</Directory>

${PALLADIO_FOR_HOUDINI_FEATURE}

<Property Id="WIXUI_INSTALLDIR" Value="WIX_DIR_PERSONAL" />
<UIRef Id="WixUI_FeatureTree" />
</Product>
</Wix>
1 change: 1 addition & 0 deletions deploy/templates/palladio_directory.in
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<Directory Id='PalladioInstallComponentDir${HOU_MAJOR}${HOU_MINOR}' Name='houdini${HOU_MAJOR}.${HOU_MINOR}' />
4 changes: 4 additions & 0 deletions deploy/templates/palladio_feature.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<Feature Id='PalladioForHoudini${HOU_MAJOR}${HOU_MINOR}' Level='0' Title="Palladio for Houdini ${HOU_MAJOR}.${HOU_MINOR}.${HOU_PATCH}" Description="Installs the Palladio Plugin for Houdini ${HOU_MAJOR}.${HOU_MINOR}.${HOU_PATCH}">
<ComponentGroupRef Id='PalladioInstallComponent${HOU_MAJOR}${HOU_MINOR}' />
<Condition Level="1">HOUDINI${HOU_MAJOR}${HOU_MINOR}INSTALLDIR</Condition>
</Feature>
3 changes: 3 additions & 0 deletions deploy/templates/palladio_registry_search.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<Property Id="HOUDINI${HOU_MAJOR}${HOU_MINOR}INSTALLDIR">
<RegistrySearch Id="Houdini${HOU_MAJOR}${HOU_MINOR}InstallSearch" Root="HKLM" Key="SOFTWARE\Side Effects Software\Houdini" Name="${HOU_MAJOR}.${HOU_MINOR}.0.${HOU_PATCH}" Type="raw" />
</Property>

0 comments on commit f6d83f3

Please sign in to comment.