1
0
mirror of https://github.com/EDCD/EDMarketConnector.git synced 2025-06-01 16:11:18 +03:00

Merge pull request #1368 from EDCD/enhancement/1367/installer-all-files

Automate always including **all** files that py2exe places in `dist.win32`
This commit is contained in:
Athanasius 2021-12-16 13:05:40 +00:00 committed by GitHub
commit aedf0bf800
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 990 additions and 749 deletions

2
.gitignore vendored
View File

@ -23,3 +23,5 @@ venv
htmlcov/ htmlcov/
.ignored .ignored
.coverage .coverage
EDMarketConnector.wxs
wix/components.wxs

View File

@ -33,7 +33,7 @@ appcmdname = 'EDMC'
# <https://semver.org/#semantic-versioning-specification-semver> # <https://semver.org/#semantic-versioning-specification-semver>
# Major.Minor.Patch(-prerelease)(+buildmetadata) # Major.Minor.Patch(-prerelease)(+buildmetadata)
# NB: Do *not* import this, use the functions appversion() and appversion_nobuild() # NB: Do *not* import this, use the functions appversion() and appversion_nobuild()
_static_appversion = '5.3.0-beta1' _static_appversion = '5.3.0-beta2'
_cached_version: Optional[semantic_version.Version] = None _cached_version: Optional[semantic_version.Version] = None
copyright = '© 2015-2019 Jonathan Harris, 2020-2021 EDCD' copyright = '© 2015-2019 Jonathan Harris, 2020-2021 EDCD'

50
l10n.py
View File

@ -309,7 +309,7 @@ class _Locale:
return None return None
def preferred_languages(self) -> Iterable[str]: def preferred_languages(self) -> Iterable[str]: # noqa: CCR001
""" """
Return a list of preferred language codes. Return a list of preferred language codes.
@ -320,34 +320,46 @@ class _Locale:
:return: The preferred language list :return: The preferred language list
""" """
languages: Iterable[str]
if platform == 'darwin': if platform == 'darwin':
return NSLocale.preferredLanguages() languages = NSLocale.preferredLanguages()
elif platform != 'win32': elif platform != 'win32':
# POSIX # POSIX
lang = locale.getlocale()[0] lang = locale.getlocale()[0]
return lang and [lang.replace('_', '-')] or [] languages = lang and [lang.replace('_', '-')] or []
def wszarray_to_list(array): else:
offset = 0 def wszarray_to_list(array):
while offset < len(array): offset = 0
sz = ctypes.wstring_at(ctypes.addressof(array) + offset*2) while offset < len(array):
if sz: sz = ctypes.wstring_at(ctypes.addressof(array) + offset*2)
yield sz if sz:
offset += len(sz)+1 yield sz
offset += len(sz)+1
else: else:
break break
num = ctypes.c_ulong() num = ctypes.c_ulong()
size = ctypes.c_ulong(0) size = ctypes.c_ulong(0)
if GetUserPreferredUILanguages(MUI_LANGUAGE_NAME, ctypes.byref(num), None, ctypes.byref(size)) and size.value: languages = []
buf = ctypes.create_unicode_buffer(size.value) if GetUserPreferredUILanguages(
MUI_LANGUAGE_NAME, ctypes.byref(num), None, ctypes.byref(size)
) and size.value:
buf = ctypes.create_unicode_buffer(size.value)
if GetUserPreferredUILanguages(MUI_LANGUAGE_NAME, ctypes.byref(num), ctypes.byref(buf), ctypes.byref(size)): if GetUserPreferredUILanguages(
return wszarray_to_list(buf) MUI_LANGUAGE_NAME, ctypes.byref(num), ctypes.byref(buf), ctypes.byref(size)
):
languages = wszarray_to_list(buf)
return [] # HACK: <n/a> | 2021-12-11: OneSky calls "Chinese Simplified" "zh-Hans"
# in the name of the file, but that will be zh-CN in terms of
# locale. So map zh-CN -> zh-Hans
languages = ['zh-Hans' if lang == 'zh-CN' else lang for lang in languages]
return languages
# singletons # singletons

View File

@ -27,6 +27,8 @@ autopep8==1.6.0
grip==4.5.2 grip==4.5.2
# Packaging # Packaging
# Used to put together a WiX configuration from template/auto-gen
lxml==4.6.4
# We only need py2exe on windows. # We only need py2exe on windows.
py2exe==0.11.0.1; sys_platform == 'win32' py2exe==0.11.0.1; sys_platform == 'win32'

110
setup.py
View File

@ -9,6 +9,7 @@ Script to build to .exe and .msi package.
import codecs import codecs
import os import os
import pathlib
import platform import platform
import re import re
import shutil import shutil
@ -18,6 +19,8 @@ from os.path import exists, isdir, join
from tempfile import gettempdir from tempfile import gettempdir
from typing import Any, Generator, Set from typing import Any, Generator, Set
from lxml import etree
from config import ( from config import (
appcmdname, applongname, appname, appversion, appversion_nobuild, copyright, git_shorthash_from_head, update_feed, appcmdname, applongname, appname, appversion, appversion_nobuild, copyright, git_shorthash_from_head, update_feed,
update_interval update_interval
@ -209,7 +212,7 @@ elif sys.platform == 'win32':
'WinSparkle.dll', 'WinSparkle.dll',
'WinSparkle.pdb', # For debugging - don't include in package 'WinSparkle.pdb', # For debugging - don't include in package
'EUROCAPS.TTF', 'EUROCAPS.TTF',
'Changelog.md', 'ChangeLog.md',
'commodity.csv', 'commodity.csv',
'rare_commodity.csv', 'rare_commodity.csv',
'snd_good.wav', 'snd_good.wav',
@ -283,13 +286,110 @@ if sys.platform == 'darwin':
os.system(f'cd {dist_dir}; ditto -ck --keepParent --sequesterRsrc {appname}.app ../{package_filename}; cd ..') os.system(f'cd {dist_dir}; ditto -ck --keepParent --sequesterRsrc {appname}.app ../{package_filename}; cd ..')
elif sys.platform == 'win32': elif sys.platform == 'win32':
os.system(rf'"{WIXPATH}\candle.exe" -out {dist_dir}\ {appname}.wxs') template_file = pathlib.Path('wix/template.wxs')
components_file = pathlib.Path('wix/components.wxs')
final_wxs_file = pathlib.Path('EDMarketConnector.wxs')
if not exists(f'{dist_dir}/{appname}.wixobj'): # Use heat.exe to generate the Component for all files inside dist.win32
raise AssertionError(f'No {dist_dir}/{appname}.wixobj: candle.exe failed?') os.system(rf'"{WIXPATH}\heat.exe" dir {dist_dir}\ -ag -sfrag -srid -suid -out {components_file}')
component_tree = etree.parse(str(components_file))
# 1. Change the element:
#
# <Directory Id="dist.win32" Name="dist.win32">
#
# to:
#
# <Directory Id="INSTALLDIR" Name="$(var.PRODUCTNAME)">
directory_win32 = component_tree.find('.//{*}Directory[@Id="dist.win32"][@Name="dist.win32"]')
if directory_win32 is None:
raise ValueError(f'{components_file}: Expected Directory with Id="dist.win32"')
directory_win32.set('Id', 'INSTALLDIR')
directory_win32.set('Name', '$(var.PRODUCTNAME)')
# 2. Change:
#
# <Component Id="EDMarketConnector.exe" Guid="*">
# <File Id="EDMarketConnector.exe" KeyPath="yes" Source="SourceDir\EDMarketConnector.exe" />
# </Component>
#
# to:
#
# <Component Id="MainExecutable" Guid="{D33BB66E-9664-4AB6-A044-3004B50A09B0}">
# <File Id="EDMarketConnector.exe" KeyPath="yes" Source="SourceDir\EDMarketConnector.exe" />
# <Shortcut Id="MainExeShortcut" Directory="ProgramMenuFolder" Name="$(var.PRODUCTLONGNAME)"
# Description="Downloads station data from Elite: Dangerous" WorkingDirectory="INSTALLDIR"
# Icon="EDMarketConnector.exe" IconIndex="0" Advertise="yes" />
# </Component>
main_executable = directory_win32.find('.//{*}Component[@Id="EDMarketConnector.exe"]')
if main_executable is None:
raise ValueError(f'{components_file}: Expected Component with Id="EDMarketConnector.exe"')
main_executable.set('Id', 'MainExecutable')
main_executable.set('Guid', '{D33BB66E-9664-4AB6-A044-3004B50A09B0}')
shortcut = etree.SubElement(
main_executable,
'Shortcut',
nsmap=main_executable.nsmap,
attrib={
'Id': 'MainExeShortcut',
'Directory': 'ProgramMenuFolder',
'Name': '$(var.PRODUCTLONGNAME)',
'Description': 'Downloads station data from Elite: Dangerous',
'WorkingDirectory': 'INSTALLDIR',
'Icon': 'EDMarketConnector.exe',
'IconIndex': '0',
'Advertise': 'yes'
}
)
# Now insert the appropriate parts as a child of the ProgramFilesFolder part
# of the template.
template_tree = etree.parse(str(template_file))
program_files_folder = template_tree.find('.//{*}Directory[@Id="ProgramFilesFolder"]')
if program_files_folder is None:
raise ValueError(f'{template_file}: Expected Directory with Id="ProgramFilesFolder"')
program_files_folder.insert(0, directory_win32)
# Append the Feature/ComponentRef listing to match
feature = template_tree.find('.//{*}Feature[@Id="Complete"][@Level="1"]')
if feature is None:
raise ValueError(f'{template_file}: Expected Feature element with Id="Complete" Level="1"')
# This isn't part of the components
feature.append(
etree.Element(
'ComponentRef',
attrib={
'Id': 'RegistryEntries'
},
nsmap=directory_win32.nsmap
)
)
for c in directory_win32.findall('.//{*}Component'):
feature.append(
etree.Element(
'ComponentRef',
attrib={
'Id': c.get('Id')
},
nsmap=directory_win32.nsmap
)
)
# Insert what we now have into the template and write it out
template_tree.write(
str(final_wxs_file), encoding='utf-8',
pretty_print=True,
xml_declaration=True
)
os.system(rf'"{WIXPATH}\candle.exe" {appname}.wxs')
if not exists(f'{appname}.wixobj'):
raise AssertionError(f'No {appname}.wixobj: candle.exe failed?')
package_filename = f'{appname}_win_{appversion_nobuild()}.msi' package_filename = f'{appname}_win_{appversion_nobuild()}.msi'
os.system(rf'"{WIXPATH}\light.exe" -sacl -spdb -sw1076 {dist_dir}\{appname}.wixobj -out {package_filename}') os.system(rf'"{WIXPATH}\light.exe" -b {dist_dir}\ -sacl -spdb -sw1076 {appname}.wixobj -out {package_filename}')
if not exists(package_filename): if not exists(package_filename):
raise AssertionError(f'light.exe failed, no {package_filename}') raise AssertionError(f'light.exe failed, no {package_filename}')

File diff suppressed because it is too large Load Diff

125
wix/template.wxs Normal file
View File

@ -0,0 +1,125 @@
<?xml version="1.0" encoding="utf-8"?>
<?define PRODUCTNAME = "EDMarketConnector"?>
<?define PRODUCTLONGNAME = "Elite Dangerous Market Connector"?>
<?define PRODUCTVERSION = "!(bind.fileVersion.EDMarketConnector.exe)" ?>
<?define UPGRADECODE = "9df571ae-d56d-46e6-af79-4e72ad54efe6" ?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
<Product Id="*"
Name="$(var.PRODUCTLONGNAME)"
Version="$(var.PRODUCTVERSION)"
UpgradeCode="$(var.UPGRADECODE)"
Language="!(bind.fileLanguage.EDMarketConnector.exe)"
Manufacturer="EDCD">
<Package Id="*" Keywords="Installer"
InstallScope="perMachine"
Description="$(var.PRODUCTLONGNAME) installer"
InstallerVersion="300" Compressed="yes"
Platform="x86"
Languages="1033,1029,1031,1034,1035,1036,1038,1040,1041,1043,1045,1046,1049,1058,1062,2052,2070,2074,6170,1060,1053,18,0" />
<!-- en cs, de es fi fr hu it ja nl pl pt-BR ru uk lv zh-CN pt-PT sr-Latn sr-Latn-BA sl sv-SE ko neutral -->
<!-- https://msdn.microsoft.com/en-gb/goglobal/bb964664.aspx -->
<!-- Always reinstall since patching is problematic -->
<!-- http://www.joyofsetup.com/2010/01/16/major-upgrades-now-easier-than-ever/ -->
<MajorUpgrade AllowSameVersionUpgrades="yes" DowngradeErrorMessage="A newer version of [ProductName] is already installed." />
<Media Id="1" Cabinet="product.cab" EmbedCab="yes" />
<Icon Id="EDMarketConnector.exe" SourceFile="EDMarketConnector.ico"/>
<!-- For Add/Remove programs -->
<Property Id="ARPPRODUCTICON" Value="EDMarketConnector.exe" />
<Property Id="ARPNOMODIFY" Value="yes" Secure="yes" /> <!-- Remove modify - also set by WixUI_Minimal -->
<Property Id="ARPHELPLINK" Value="https://github.com/EDCD/EDMarketConnector/wiki" />
<!-- Set INSTALLDIR from ARPINSTALLLOCATION if replacing/upgrading -->
<!-- https://wyrdfish.wordpress.com/2012/07/20/msi-writing-guidelines-this-may-be-out-of-date/ -->
<Property Id="ARPINSTALLLOCATION">
<RegistrySearch Id="GetARPINSTALLLOCATION"
Root="HKLM"
Key="Software\Microsoft\Windows\CurrentVersion\Uninstall\[WIX_UPGRADE_DETECTED]"
Name="InstallLocation"
Type="raw" />
</Property>
<CustomAction Id="SetINSTALLDIR" Property="INSTALLDIR" Value="[ARPINSTALLLOCATION]" />
<InstallUISequence>
<Custom Action="SetINSTALLDIR" After="AppSearch">
WIX_UPGRADE_DETECTED AND ARPINSTALLLOCATION
</Custom>
</InstallUISequence>
<InstallExecuteSequence>
<Custom Action="SetINSTALLDIR" After="AppSearch">
WIX_UPGRADE_DETECTED AND ARPINSTALLLOCATION
</Custom>
</InstallExecuteSequence>
<!-- Set ARPINSTALLLOCATION from INSTALLDIR if new install -->
<!-- http://blogs.technet.com/b/alexshev/archive/2008/02/09/from-msi-to-wix-part-2.aspx -->
<CustomAction Id="SetARPINSTALLLOCATION" Property="ARPINSTALLLOCATION" Value="[INSTALLDIR]" />
<InstallExecuteSequence>
<Custom Action="SetARPINSTALLLOCATION" After="InstallValidate">
NOT Installed
</Custom>
</InstallExecuteSequence>
<!-- Launch app after upgrade -->
<Property Id="LAUNCH" Value="yes" />
<CustomAction Id="DoLaunch"
Directory="INSTALLDIR"
ExeCommand='"[INSTALLDIR]EDMarketConnector.exe"'
Return="asyncNoWait"
Execute="deferred"
Impersonate="yes"
/>
<InstallExecuteSequence>
<!-- http://alekdavis.blogspot.co.uk/2013/05/wix-woes-what-is-your-installer-doing.html -->
<Custom Action="DoLaunch" Before="InstallFinalize">
NOT Installed AND LAUNCH ~= "yes"
</Custom>
</InstallExecuteSequence>
<Directory Id="TARGETDIR" Name="SourceDir">
<!-- http://wixtoolset.org/documentation/manual/v3/howtos/files_and_registry/write_a_registry_entry.html -->
<Component Id="RegistryEntries" Guid="*">
<RegistryKey Root="HKCR" Key="edmc">
<RegistryValue Type="string" Value="$(var.PRODUCTLONGNAME)"/>
<RegistryValue Type="string" Name="URL Protocol" Value=""/>
<RegistryKey Key="DefaultIcon">
<RegistryValue Type="string" Value="[INSTALLDIR]EDMarketConnector.exe,0"/>
</RegistryKey>
<RegistryKey Key="shell">
<RegistryKey Key="open">
<RegistryKey Key="command">
<RegistryValue Type="string" Value='"[INSTALLDIR]EDMarketConnector.exe" "%1"'/>
</RegistryKey>
<RegistryKey Key="ddeexec">
<RegistryValue Type="string" Value='Open("%1")'/>
</RegistryKey>
</RegistryKey>
</RegistryKey>
</RegistryKey>
</Component>
<!-- Contents auto-generated with heat.exe, see setup.py -->
<Directory Id="ProgramFilesFolder">
</Directory>
<Directory Id="ProgramMenuFolder" Name="Programs">
</Directory>
</Directory>
<!-- Contents auto-generated in setup.py -->
<Feature Id='Complete' Level='1'>
</Feature>
</Product>
</Wix>
<!-- Local Variables: -->
<!-- tab-width: 4 -->
<!-- End: -->