Building the app
The short version: .\tools\build_installer.ps1 builds the app and then the installer.
| Output | Script | Result |
|---|---|---|
| The app (PyInstaller, one folder) | .\tools\build_app.ps1 |
dist\JBrowser\JBrowser.exe and its _internal\ folder |
| The installer (Inno Setup) | .\tools\build_installer.ps1 |
dist\installer\JBrowser-Setup-<version>.exe and .exe.sha256 |
The PowerShell scripts share tools/common.ps1: $Root, $Py (the .venv Python), the
Step helper, and where output goes.
Where the output goes#
- normally
dist\(andbuild\for PyInstaller's temporary files) inside the repository, ignored by git; %LOCALAPPDATA%\JBrowser-build\when the repository is inside OneDrive, because OneDrive would upload about 450 MB per build and lock files while PyInstaller is still writing them;- anywhere you like: set
JBROWSER_BUILD_DIR.
tools\build_app.ps1#
.\tools\build_app.ps1 # one-folder build (the one the installer uses)
.\tools\build_app.ps1 -SkipDeps # don't update packages first
.\tools\build_app.ps1 -OneFile # a single JBrowser.exe (slow to start; not used for releases)
tools\update_deps.pyupdates and verifies the packages (skipped with-SkipDeps).- It refuses to continue if JBrowser is running from the build folder, because open files would break the build.
tools\make_icon.pyrendersassets\jbrowser.icofrom the vector logo, andtools\version.py --syncwrites the exe's version resource (tools\version_info.txt).- PyInstaller builds with JBrowser.spec.
The spec#
| Choice | Why |
|---|---|
one folder (COLLECT) |
starts much faster than one file: Qt WebEngine is about 500 MB unpacked |
console=False |
a GUI app: no console window |
icon, version |
the icon and the details in the file's Properties |
collect_submodules("jbrowser") |
dialogs are imported lazily, so PyInstaller must be told about them |
PyQt6.QtWebChannel |
provides qwebchannel.js for the isolated autofill bridge |
PyQt6.QtPrintSupport, PyQt6.QtMultimedia + assets\sounds |
printing, and the welcome sounds (the FFmpeg plugin is filtered out: sounds use the native backend) |
upx=False |
UPX corrupts Qt WebEngine binaries |
PyInstaller's PyQt6 hooks bundle QtWebEngineProcess.exe, the Chromium .pak resources, locales and ICU data. Keep
the whole dist\JBrowser folder together.
Checking a build#
Run the built JBrowser.exe with a throw-away profile and look at: a page loads, DevTools open, a download works,
the welcome sound plays, and it exits cleanly (exit code 0, no QtWebEngineProcess.exe left behind). The log is in
<profile-dir>\Logs\jbrowser.log.
& "$env:LOCALAPPDATA\JBrowser-build\dist\JBrowser\JBrowser.exe" --profile-dir "$env:TEMP\jb-build-test"
Code signing (optional)#
Unsigned installers trigger a SmartScreen warning until they build up a reputation. With a code-signing certificate,
sign JBrowser.exe before building the installer, then sign the installer and recompute its .sha256:
signtool sign /fd SHA256 /tr http://timestamp.digicert.com /td SHA256 /a dist\JBrowser\JBrowser.exe