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:
commit
aedf0bf800
2
.gitignore
vendored
2
.gitignore
vendored
@ -23,3 +23,5 @@ venv
|
|||||||
htmlcov/
|
htmlcov/
|
||||||
.ignored
|
.ignored
|
||||||
.coverage
|
.coverage
|
||||||
|
EDMarketConnector.wxs
|
||||||
|
wix/components.wxs
|
||||||
|
@ -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
50
l10n.py
@ -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
|
||||||
|
@ -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
110
setup.py
@ -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
125
wix/template.wxs
Normal 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: -->
|
Loading…
x
Reference in New Issue
Block a user