mirror of
https://github.com/EDCD/EDMarketConnector.git
synced 2025-05-31 07:39:44 +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/
|
||||
.ignored
|
||||
.coverage
|
||||
EDMarketConnector.wxs
|
||||
wix/components.wxs
|
||||
|
@ -33,7 +33,7 @@ appcmdname = 'EDMC'
|
||||
# <https://semver.org/#semantic-versioning-specification-semver>
|
||||
# Major.Minor.Patch(-prerelease)(+buildmetadata)
|
||||
# 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
|
||||
copyright = '© 2015-2019 Jonathan Harris, 2020-2021 EDCD'
|
||||
|
||||
|
50
l10n.py
50
l10n.py
@ -309,7 +309,7 @@ class _Locale:
|
||||
|
||||
return None
|
||||
|
||||
def preferred_languages(self) -> Iterable[str]:
|
||||
def preferred_languages(self) -> Iterable[str]: # noqa: CCR001
|
||||
"""
|
||||
Return a list of preferred language codes.
|
||||
|
||||
@ -320,34 +320,46 @@ class _Locale:
|
||||
|
||||
:return: The preferred language list
|
||||
"""
|
||||
languages: Iterable[str]
|
||||
if platform == 'darwin':
|
||||
return NSLocale.preferredLanguages()
|
||||
languages = NSLocale.preferredLanguages()
|
||||
|
||||
elif platform != 'win32':
|
||||
# POSIX
|
||||
lang = locale.getlocale()[0]
|
||||
return lang and [lang.replace('_', '-')] or []
|
||||
languages = lang and [lang.replace('_', '-')] or []
|
||||
|
||||
def wszarray_to_list(array):
|
||||
offset = 0
|
||||
while offset < len(array):
|
||||
sz = ctypes.wstring_at(ctypes.addressof(array) + offset*2)
|
||||
if sz:
|
||||
yield sz
|
||||
offset += len(sz)+1
|
||||
else:
|
||||
def wszarray_to_list(array):
|
||||
offset = 0
|
||||
while offset < len(array):
|
||||
sz = ctypes.wstring_at(ctypes.addressof(array) + offset*2)
|
||||
if sz:
|
||||
yield sz
|
||||
offset += len(sz)+1
|
||||
|
||||
else:
|
||||
break
|
||||
else:
|
||||
break
|
||||
|
||||
num = ctypes.c_ulong()
|
||||
size = ctypes.c_ulong(0)
|
||||
if GetUserPreferredUILanguages(MUI_LANGUAGE_NAME, ctypes.byref(num), None, ctypes.byref(size)) and size.value:
|
||||
buf = ctypes.create_unicode_buffer(size.value)
|
||||
num = ctypes.c_ulong()
|
||||
size = ctypes.c_ulong(0)
|
||||
languages = []
|
||||
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)):
|
||||
return wszarray_to_list(buf)
|
||||
if GetUserPreferredUILanguages(
|
||||
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
|
||||
|
@ -27,6 +27,8 @@ autopep8==1.6.0
|
||||
grip==4.5.2
|
||||
|
||||
# Packaging
|
||||
# Used to put together a WiX configuration from template/auto-gen
|
||||
lxml==4.6.4
|
||||
# We only need py2exe on windows.
|
||||
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 os
|
||||
import pathlib
|
||||
import platform
|
||||
import re
|
||||
import shutil
|
||||
@ -18,6 +19,8 @@ from os.path import exists, isdir, join
|
||||
from tempfile import gettempdir
|
||||
from typing import Any, Generator, Set
|
||||
|
||||
from lxml import etree
|
||||
|
||||
from config import (
|
||||
appcmdname, applongname, appname, appversion, appversion_nobuild, copyright, git_shorthash_from_head, update_feed,
|
||||
update_interval
|
||||
@ -209,7 +212,7 @@ elif sys.platform == 'win32':
|
||||
'WinSparkle.dll',
|
||||
'WinSparkle.pdb', # For debugging - don't include in package
|
||||
'EUROCAPS.TTF',
|
||||
'Changelog.md',
|
||||
'ChangeLog.md',
|
||||
'commodity.csv',
|
||||
'rare_commodity.csv',
|
||||
'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 ..')
|
||||
|
||||
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'):
|
||||
raise AssertionError(f'No {dist_dir}/{appname}.wixobj: candle.exe failed?')
|
||||
# Use heat.exe to generate the Component for all files inside dist.win32
|
||||
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'
|
||||
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):
|
||||
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