summaryrefslogtreecommitdiff
path: root/package/windows
diff options
context:
space:
mode:
Diffstat (limited to 'package/windows')
-rw-r--r--package/windows/README.rst39
-rw-r--r--package/windows/bootstrap-silx-view.py3
-rw-r--r--package/windows/bootstrap.py3
-rw-r--r--package/windows/create-installer.iss.template92
-rw-r--r--package/windows/pyinstaller-silx-view.spec54
-rw-r--r--package/windows/pyinstaller.spec177
6 files changed, 251 insertions, 117 deletions
diff --git a/package/windows/README.rst b/package/windows/README.rst
index a7f3702..c263dce 100644
--- a/package/windows/README.rst
+++ b/package/windows/README.rst
@@ -4,20 +4,13 @@ Generate silx fat binary for Windows
Pre-requisites
--------------
-- PyInstaller must be installed.
- It is best to use the development version to use package specific hooks up-to-date.
- Run either::
-
- pip install -r requirements-dev.txt
-
- or::
-
- pip install https://github.com/pyinstaller/pyinstaller/archive/develop.zip
-
+- `PyInstaller <https://pyinstaller.readthedocs.io/>`_ must be installed.
+ Run: ``pip install -r requirements-dev.txt``
+- `InnoSetup <https://jrsoftware.org/isinfo.php>`_ must be installed and in the ``PATH``.
- silx and all its dependencies must be INSTALLED::
pip install silx[full]
-
+
or from the source directory::
pip install .[full]
@@ -26,12 +19,20 @@ Pre-requisites
Procedure
---------
-- Go to the `package/windows` folder in the source directory
-- Run `pyinstaller pyinstaller.spec`.
- This generates a fat binary in `package/windows/dist/silx/` for the generic launcher `silx.exe`.
-- Run `pyinstaller pyinstaller-silx-view.spec`.
- This generates a fat binary in `package/windows/dist/silx-view/` for the silx view command `silx-view.exe`.
-- Copy `silx-view.exe` and `silx-view.exe.manifest` to `package/windows/dist/silx/`.
- This is a hack until PyInstaller supports multiple executables (see https://github.com/pyinstaller/pyinstaller/issues/1527).
-- Zip `package\windows\dist\silx` to make the application available as a single zip file.
+- Go to the ``package/windows`` folder in the source directory
+- Run ``pyinstaller pyinstaller.spec``.
+ This will generates the fat binary in ``package/windows/dist/``.
+ It will then run InnoSetup to create the installer in ``package/windows/artifacts/``.
+
+
+Troubleshooting
+---------------
+
+In case of issues with anti-virus during the build process, try to re-install PyInstaller
+from source and rebuild the bootloader:
+```
+SET PYINSTALLER_COMPILE_BOOTLOADER=1
+SET PYINSTALLER_BOOTLOADER_WAF_ARGS=--msvc_target=x64
+pip install pyinstaller --no-binary pyinstaller
+``` \ No newline at end of file
diff --git a/package/windows/bootstrap-silx-view.py b/package/windows/bootstrap-silx-view.py
index f8da02d..e2f2513 100644
--- a/package/windows/bootstrap-silx-view.py
+++ b/package/windows/bootstrap-silx-view.py
@@ -1,5 +1,3 @@
-# coding: utf-8
-
import logging
logging.basicConfig()
@@ -9,4 +7,3 @@ from silx.app.view.main import main
if __name__ == "__main__":
main(sys.argv)
-
diff --git a/package/windows/bootstrap.py b/package/windows/bootstrap.py
index 3c6e887..f7a0155 100644
--- a/package/windows/bootstrap.py
+++ b/package/windows/bootstrap.py
@@ -1,5 +1,3 @@
-# coding: utf-8
-
import logging
logging.basicConfig()
@@ -13,4 +11,3 @@ from silx.__main__ import main
if __name__ == "__main__":
main()
-
diff --git a/package/windows/create-installer.iss.template b/package/windows/create-installer.iss.template
new file mode 100644
index 0000000..88c1a52
--- /dev/null
+++ b/package/windows/create-installer.iss.template
@@ -0,0 +1,92 @@
+[Setup]
+AppId={{A694A78C-B4D1-4983-8BC6-A84D30341EB4}
+AppName=silx view
+AppVersion=#Version
+AppVerName=silx-view-#Version
+AppPublisher=ESRF
+AppPublisherURL=https://www.silx.org
+AppSupportURL=https://github.com/silx-kit/silx
+AppUpdatesURL=https://github.com/silx-kit/silx/releases
+DefaultDirName={autopf}\silx
+DefaultGroupName=silx
+LicenseFile=LICENSE
+OutputDir=artifacts
+OutputBaseFilename=silx-#Version-windows-installer-x86_64
+Compression=lzma
+SolidCompression=yes
+ArchitecturesAllowed=x64
+ArchitecturesInstallIn64BitMode=x64
+WizardStyle=modern
+
+[Languages]
+Name: "english"; MessagesFile: "compiler:Default.isl"
+
+[Files]
+Source: "dist\silx\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs
+Source: "silx.ico"; DestDir: "{app}"
+
+[Icons]
+Name: "{group}\silx"; Filename: "{app}\silx-view.exe"; IconFilename: "{app}\silx.ico"
+Name: "{group}\Uninstall"; Filename: "{uninstallexe}"
+
+// Code from https://stackoverflow.com/questions/2000296/inno-setup-how-to-automatically-uninstall-previous-installed-version/2099805#209980
+[Code]
+
+/////////////////////////////////////////////////////////////////////
+function GetUninstallString(): String;
+var
+ sUnInstPath: String;
+ sUnInstallString: String;
+begin
+ sUnInstPath := ExpandConstant('Software\Microsoft\Windows\CurrentVersion\Uninstall\{#emit SetupSetting("AppId")}_is1');
+ sUnInstallString := '';
+ if not RegQueryStringValue(HKLM, sUnInstPath, 'UninstallString', sUnInstallString) then
+ RegQueryStringValue(HKCU, sUnInstPath, 'UninstallString', sUnInstallString);
+ Result := sUnInstallString;
+end;
+
+
+/////////////////////////////////////////////////////////////////////
+function IsUpgrade(): Boolean;
+begin
+ Result := (GetUninstallString() <> '');
+end;
+
+
+/////////////////////////////////////////////////////////////////////
+function UnInstallOldVersion(): Integer;
+var
+ sUnInstallString: String;
+ iResultCode: Integer;
+begin
+ // Return Values:
+ // 1 - uninstall string is empty
+ // 2 - error executing the UnInstallString
+ // 3 - successfully executed the UnInstallString
+
+ // default return value
+ Result := 0;
+
+ // get the uninstall string of the old app
+ sUnInstallString := GetUninstallString();
+ if sUnInstallString <> '' then begin
+ sUnInstallString := RemoveQuotes(sUnInstallString);
+ if Exec(sUnInstallString, '/VERYSILENT /NORESTART /SUPPRESSMSGBOXES','', SW_HIDE, ewWaitUntilTerminated, iResultCode) then
+ Result := 3
+ else
+ Result := 2;
+ end else
+ Result := 1;
+end;
+
+/////////////////////////////////////////////////////////////////////
+procedure CurStepChanged(CurStep: TSetupStep);
+begin
+ if (CurStep=ssInstall) then
+ begin
+ if (IsUpgrade()) then
+ begin
+ UnInstallOldVersion();
+ end;
+ end;
+end;
diff --git a/package/windows/pyinstaller-silx-view.spec b/package/windows/pyinstaller-silx-view.spec
deleted file mode 100644
index 6f36128..0000000
--- a/package/windows/pyinstaller-silx-view.spec
+++ /dev/null
@@ -1,54 +0,0 @@
-# -*- mode: python -*-
-import os.path
-from PyInstaller.utils.hooks import collect_data_files
-
-datas = []
-
-PROJECT_PATH = os.path.abspath(os.path.join(SPECPATH, "..", ".."))
-datas.append((os.path.join(PROJECT_PATH, "README.rst"), "."))
-datas.append((os.path.join(PROJECT_PATH, "LICENSE"), "."))
-datas.append((os.path.join(PROJECT_PATH, "copyright"), "."))
-
-
-datas += collect_data_files("silx.resources")
-
-
-block_cipher = None
-
-
-a = Analysis(['bootstrap-silx-view.py'],
- pathex=[],
- binaries=[],
- datas=datas,
- hiddenimports=[],
- hookspath=[],
- runtime_hooks=[],
- excludes=[],
- win_no_prefer_redirects=False,
- win_private_assemblies=False,
- cipher=block_cipher,
- noarchive=False)
-
-pyz = PYZ(a.pure,
- a.zipped_data,
- cipher=block_cipher)
-
-exe = EXE(pyz,
- a.scripts,
- [],
- exclude_binaries=True,
- name='silx-view',
- debug=False,
- bootloader_ignore_signals=False,
- strip=False,
- upx=False,
- console=False,
- icon='silx.ico')
-
-coll = COLLECT(exe,
- a.binaries,
- a.zipfiles,
- a.datas,
- strip=False,
- upx=False,
- name='silx-view')
diff --git a/package/windows/pyinstaller.spec b/package/windows/pyinstaller.spec
index 74d6a0f..bff8a9f 100644
--- a/package/windows/pyinstaller.spec
+++ b/package/windows/pyinstaller.spec
@@ -1,54 +1,155 @@
# -*- mode: python -*-
+import importlib.metadata
import os.path
-from PyInstaller.utils.hooks import collect_data_files
+from pathlib import Path
+import shutil
+import subprocess
+import sys
+
+from PyInstaller.utils.hooks import collect_data_files, collect_submodules
+
datas = []
+
PROJECT_PATH = os.path.abspath(os.path.join(SPECPATH, "..", ".."))
datas.append((os.path.join(PROJECT_PATH, "README.rst"), "."))
datas.append((os.path.join(PROJECT_PATH, "LICENSE"), "."))
datas.append((os.path.join(PROJECT_PATH, "copyright"), "."))
+datas += collect_data_files("silx.resources")
-datas += collect_data_files("silx.resources")
+hiddenimports = ["hdf5plugin"]
+hiddenimports += collect_submodules("fabio")
block_cipher = None
-a = Analysis(['bootstrap.py'],
- pathex=[],
- binaries=[],
- datas=datas,
- hiddenimports=[],
- hookspath=[],
- runtime_hooks=[],
- excludes=[],
- win_no_prefer_redirects=False,
- win_private_assemblies=False,
- cipher=block_cipher,
- noarchive=False)
-
-pyz = PYZ(a.pure,
- a.zipped_data,
- cipher=block_cipher)
-
-exe = EXE(pyz,
- a.scripts,
- [],
- exclude_binaries=True,
- name='silx',
- debug=False,
- bootloader_ignore_signals=False,
- strip=False,
- upx=False,
- console=True,
- icon='silx.ico')
-
-coll = COLLECT(exe,
- a.binaries,
- a.zipfiles,
- a.datas,
- strip=False,
- upx=False,
- name='silx')
+silx_a = Analysis(
+ ["bootstrap.py"],
+ pathex=[],
+ binaries=[],
+ datas=datas,
+ hiddenimports=hiddenimports,
+ hookspath=[],
+ runtime_hooks=[],
+ excludes=[],
+ cipher=block_cipher,
+ noarchive=False,
+)
+
+
+silx_pyz = PYZ(silx_a.pure, silx_a.zipped_data, cipher=block_cipher)
+
+
+silx_exe = EXE(
+ silx_pyz,
+ silx_a.scripts,
+ silx_a.dependencies,
+ [],
+ exclude_binaries=True,
+ name="silx",
+ debug=False,
+ bootloader_ignore_signals=False,
+ strip=False,
+ upx=False,
+ console=True,
+ icon="silx.ico",
+)
+
+
+silx_view_a = Analysis(
+ ["bootstrap-silx-view.py"],
+ pathex=[],
+ binaries=[],
+ datas=datas,
+ hiddenimports=hiddenimports,
+ hookspath=[],
+ runtime_hooks=[],
+ excludes=[],
+ cipher=block_cipher,
+ noarchive=False,
+)
+
+
+silx_view_pyz = PYZ(silx_view_a.pure, silx_view_a.zipped_data, cipher=block_cipher)
+
+
+silx_view_exe = EXE(
+ silx_view_pyz,
+ silx_view_a.scripts,
+ silx_view_a.dependencies,
+ [],
+ exclude_binaries=True,
+ name="silx-view",
+ debug=False,
+ bootloader_ignore_signals=False,
+ strip=False,
+ upx=False,
+ console=False,
+ icon="silx.ico",
+)
+
+
+silx_coll = COLLECT(
+ silx_view_exe,
+ silx_view_a.binaries,
+ silx_view_a.zipfiles,
+ silx_view_a.datas,
+ silx_exe,
+ silx_a.binaries,
+ silx_a.zipfiles,
+ silx_a.datas,
+ strip=False,
+ upx=False,
+ name="silx",
+)
+
+
+# Generate license file from current Python env
+def create_license_file(filename: str):
+ import PyQt5.QtCore
+
+ with open(filename, "w") as f:
+ f.write(
+ f"""
+This is free software.
+
+This distribution of silx is provided under the
+GNU General Public License v3 (https://www.gnu.org/licenses/gpl-3.0.en.html) since it includes PyQt5.
+
+It includes mainy software packages with different licenses:
+
+- Python ({sys.version}): PSF license, https://www.python.org/
+- Qt ({PyQt5.QtCore.qVersion()}): GNU Lesser General Public License v3, https://www.qt.io/
+"""
+ )
+
+ for dist in sorted(
+ importlib.metadata.distributions(), key=lambda d: d.name.lower()
+ ):
+ license = dist.metadata.get("License")
+ homepage = dist.metadata.get("Home-page")
+ info = ", ".join(info for info in (license, homepage) if info)
+ f.write(f"- {dist.name} ({dist.version}): {info}\n")
+
+
+create_license_file("LICENSE")
+
+
+# Run innosetup
+def innosetup():
+ from silx import strictversion
+
+ config_name = "create-installer.iss"
+ with open(config_name + ".template") as f:
+ content = f.read().replace("#Version", strictversion)
+ with open(config_name, "w") as f:
+ f.write(content)
+
+ subprocess.call(["iscc", os.path.join(SPECPATH, config_name)])
+ os.remove(config_name)
+
+
+innosetup()