From 10dfc1441bcd066a7cc9803b4c368a12327f8590 Mon Sep 17 00:00:00 2001 From: Micah Lee Date: Wed, 28 Oct 2020 12:13:09 -0700 Subject: [PATCH] Build the Wix wxs file on the fly --- .gitignore | 3 +- BUILD.md | 10 +- RELEASE.md | 1 - install/windows/Dangerzone.wxs | 228 ---------------------------- install/windows/build-wxs.py | 225 +++++++++++++++++++++++++++ install/windows/step1-build-exe.bat | 3 + 6 files changed, 235 insertions(+), 235 deletions(-) delete mode 100644 install/windows/Dangerzone.wxs create mode 100644 install/windows/build-wxs.py diff --git a/.gitignore b/.gitignore index 0302e4b..dea4911 100644 --- a/.gitignore +++ b/.gitignore @@ -131,4 +131,5 @@ dmypy.json .vscode *.tar.gz deb_dist -.DS_Store \ No newline at end of file +.DS_Store +install/windows/Dangerzone.wxs \ No newline at end of file diff --git a/BUILD.md b/BUILD.md index b938443..ad4a05e 100644 --- a/BUILD.md +++ b/BUILD.md @@ -113,8 +113,8 @@ Download and install the [Windows 10 SDK](https://developer.microsoft.com/en-US/ Add the following directories to the path: -* `C:\Program Files (x86)\Windows Kits\10\bin\10.0.18362.0\x86` -* `C:\Program Files (x86)\Windows Kits\10\Redist\10.0.18362.0\ucrt\DLLs\x86` +* `C:\Program Files (x86)\Windows Kits\10\bin\10.0.19041.0\x86` +* `C:\Program Files (x86)\Windows Kits\10\Redist\ucrt\DLLs\x86` ### If you want the .exe to not get falsely flagged as malicious by anti-virus software @@ -122,7 +122,7 @@ Dangerzone uses PyInstaller to turn the python source code into Windows executab Here's how to compile the PyInstaller bootloader: -Download and install [Microsoft Build Tools for Visual Studio 2017](https://www.visualstudio.com/downloads/#build-tools-for-visual-studio-2019). I downloaded `vs_buildtools__1378184674.1581551596.exe`. In the installer, check the box next to "C++ build tools". Click "Individual components", and under "Compilers, build tools and runtimes", check "Windows Universal CRT SDK". Then click install. When installation is done, you may have to reboot your computer. +Download and install [Microsoft Build Tools for Visual Studio 2019](https://www.visualstudio.com/downloads/#build-tools-for-visual-studio-2019). I downloaded `vs_buildtools__719988613.1603831511.exe`. In the installer, check the box next to "C++ build tools". Click "Individual components", and under "Compilers, build tools and runtimes", check "Windows Universal CRT SDK". Then click install. When installation is done, you may have to reboot your computer. Then, enable the 32-bit Visual C++ Toolset on the Command Line like this: @@ -136,7 +136,7 @@ Change to a folder where you keep source code, and clone the PyInstaller git rep ``` git clone https://github.com/pyinstaller/pyinstaller.git cd pyinstaller -git tag -v v3.6 +git tag -v v4.0 ``` (Note that ideally you would verify the git tag, but the PGP key that has signed the v3.5 git tag for is not published anywhere, so this isn't possible. See [this issue](https://github.com/pyinstaller/pyinstaller/issues/4430).) @@ -170,7 +170,7 @@ Now the next time you use PyInstaller to build dangerzone, the `.exe` file shoul * Go to https://dotnet.microsoft.com/download/dotnet-framework and download and install .NET Framework 3.5 SP1 Runtime. I downloaded `dotnetfx35.exe`. * Go to https://wixtoolset.org/releases/ and download and install WiX toolset. I downloaded `wix311.exe`. -* Add `C:\Program Files (x86)\WiX Toolset v3.1.1\bin` to the path. +* Add `C:\Program Files (x86)\WiX Toolset v3.11\bin` to the path. ### If you want to sign binaries with Authenticode diff --git a/RELEASE.md b/RELEASE.md index dad4956..b7ba10e 100644 --- a/RELEASE.md +++ b/RELEASE.md @@ -8,7 +8,6 @@ Before making a release, all of these should be complete: * Update `version` in `pyproject.toml` * Update `dangerzone_version` in `dangerzone/__init__.py` -* Update `ProductVersion` in `install/windows/Dangerzone.wxs` * Update version and download links in `README.md` * CHANGELOG.md should be updated to include a list of all major changes since the last release * There must be a PGP-signed git tag for the version, e.g. for dangerzone 0.1.0, the tag must be `v0.1.0` diff --git a/install/windows/Dangerzone.wxs b/install/windows/Dangerzone.wxs deleted file mode 100644 index 527a9c6..0000000 --- a/install/windows/Dangerzone.wxs +++ /dev/null @@ -1,228 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - NOT NEWERVERSIONDETECTED - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/install/windows/build-wxs.py b/install/windows/build-wxs.py new file mode 100644 index 0000000..24d74a9 --- /dev/null +++ b/install/windows/build-wxs.py @@ -0,0 +1,225 @@ +#!/usr/bin/env python3 +import os +import uuid +import xml.etree.ElementTree as ET + + +def build_data(dirname, dir_prefix, id_, name): + data = { + "id": id_, + "name": name, + "files": [], + "dirs": [], + } + + for basename in os.listdir(dirname): + filename = os.path.join(dirname, basename) + if os.path.isfile(filename): + data["files"].append(os.path.join(dir_prefix, basename)) + elif os.path.isdir(filename): + if id_ == "INSTALLDIR": + id_prefix = "Folder" + else: + id_prefix = id_ + + data["dirs"].append( + build_data( + os.path.join(dirname, basename), + os.path.join(dir_prefix, basename), + f"{id_prefix}{basename.capitalize()}", + basename, + ) + ) + + if len(data["files"]) > 0: + if id_ == "INSTALLDIR": + data["component_id"] = "ApplicationFiles" + else: + data["component_id"] = "FolderComponent" + id_[len("Folder") :] + data["component_guid"] = str(uuid.uuid4()) + + return data + + +def build_dir_xml(root, data): + attrs = {} + if "id" in data: + attrs["Id"] = data["id"] + if "name" in data: + attrs["Name"] = data["name"] + el = ET.SubElement(root, "Directory", attrs) + for subdata in data["dirs"]: + build_dir_xml(el, subdata) + + +def build_components_xml(root, data): + component_ids = [] + if "component_id" in data: + component_ids.append(data["component_id"]) + + for subdata in data["dirs"]: + if "component_guid" in subdata: + dir_ref_el = ET.SubElement(root, "DirectoryRef", Id=subdata["id"]) + component_el = ET.SubElement( + dir_ref_el, + "Component", + Id=subdata["component_id"], + Guid=subdata["component_guid"], + ) + for filename in subdata["files"]: + file_el = ET.SubElement( + component_el, "File", Source=filename, Id="file_" + uuid.uuid4().hex + ) + + component_ids += build_components_xml(root, subdata) + + return component_ids + + +def main(): + version_filename = os.path.join( + os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))), + "dangerzone", + "__init__.py", + ) + with open(version_filename) as f: + for line in f.readlines(): + if line.startswith("dangerzone_version ="): + version = line.split('"')[1] + break + + dist_dir = os.path.join( + os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))), + "dist", + "dangerzone", + ) + if not os.path.exists(dist_dir): + print( + "You must run step1-build-exe.bat to build dangerzone binary before running this" + ) + return + + data = { + "id": "TARGETDIR", + "name": "SourceDir", + "dirs": [ + { + "id": "ProgramFilesFolder", + "dirs": [], + } + ], + } + + data["dirs"][0]["dirs"].append( + build_data( + dist_dir, + os.path.join("..", "..", "dist", "dangerzone"), + "INSTALLDIR", + "Dangerzone", + ) + ) + + root_el = ET.Element("Wix", xmlns="http://schemas.microsoft.com/wix/2006/wi") + product_el = ET.SubElement( + root_el, + "Product", + Name="Dangerzone", + Manufacturer="First Look Media", + Id="f40ff0a9-ebf8-4e1e-9bce-6ab5c74fe119", + UpgradeCode="$(var.ProductUpgradeCode)", + Language="1033", + Codepage="1252", + Version="$(var.ProductVersion)", + ) + ET.SubElement( + product_el, + "Package", + Id="*", + Keywords="Installer", + Description="Dangerzone $(var.ProductVersion) Installer", + Manufacturer="First Look Media", + InstallerVersion="100", + Languages="1033", + Compressed="yes", + SummaryCodepage="1252", + ) + ET.SubElement(product_el, "Media", Id="1", Cabinet="product.cab", EmbedCab="yes") + ET.SubElement( + product_el, "Icon", Id="ProductIcon", SourceFile="..\\..\\share\\dangerzone.ico" + ) + ET.SubElement(product_el, "Property", Id="ARPPRODUCTICON", Value="ProductIcon") + ET.SubElement( + product_el, + "Property", + Id="ARPHELPLINK", + Value="https://github.com/firstlookmedia/dangerzone", + ) + ET.SubElement( + product_el, + "Property", + Id="ARPURLINFOABOUT", + Value="https://tech.firstlook.media", + ) + ET.SubElement(product_el, "UIRef", Id="WixUI_Minimal") + ET.SubElement(product_el, "UIRef", Id="WixUI_ErrorProgressText") + ET.SubElement( + product_el, + "WixVariable", + Id="WixUILicenseRtf", + Value="..\\..\\install\\windows\\license.rtf", + ) + ET.SubElement( + product_el, + "WixVariable", + Id="WixUIDialogBmp", + Value="..\\..\\install\\windows\\dialog.bmp", + ) + upgrade_el = ET.SubElement(product_el, "Upgrade", Id="$(var.ProductUpgradeCode)") + ET.SubElement( + upgrade_el, + "UpgradeVersion", + Minimum="$(var.ProductVersion)", + OnlyDetect="yes", + Property="NEWERVERSIONDETECTED", + ) + ET.SubElement( + upgrade_el, + "UpgradeVersion", + Minimum="0.0.0", + Maximum="$(var.ProductVersion)", + IncludeMinimum="yes", + IncludeMaximum="no", + Property="OLDERVERSIONBEINGUPGRADED", + ) + condition_el = ET.SubElement( + product_el, + "Condition", + Message="A newer version of this software is already installed.", + ) + condition_el.text = "NOT NEWERVERSIONDETECTED" + + build_dir_xml(product_el, data) + component_ids = build_components_xml(product_el, data) + + install_exec_seq_el = ET.SubElement( + product_el, + "InstallExecuteSequence", + ) + ET.SubElement( + install_exec_seq_el, "RemoveExistingProducts", After="InstallValidate" + ) + + feature_el = ET.SubElement(product_el, "Feature", Id="DefaultFeature", Level="1") + for component_id in component_ids: + ET.SubElement(feature_el, "ComponentRef", Id=component_id) + + print('') + print(f'') + print('') + + ET.indent(root_el) + ET.dump(root_el) + + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/install/windows/step1-build-exe.bat b/install/windows/step1-build-exe.bat index dacc95a..b92ac8b 100644 --- a/install/windows/step1-build-exe.bat +++ b/install/windows/step1-build-exe.bat @@ -7,3 +7,6 @@ pyinstaller install\pyinstaller\pyinstaller.spec REM code sign dangerzone.exe signtool.exe sign /v /d "Dangerzone" /a /tr http://time.certum.pl/ dist\dangerzone\dangerzone.exe + +REM build the wix file +python install\windows\build-wxs.py > install\windows\Dangerzone.wxs