Skip to content

Commit

Permalink
Create Windows installer (#848)
Browse files Browse the repository at this point in the history
* Add Windows installer

* Fix MinGW compatibility

* Harvest files added by windeployqt dynamically

* Clean up BAFunctions

* Fix MUI strings

* Make installer layout more spacious

* Finish installer update buttons

* Fix installer UI strings

* Add installer localisation infrastructure

* Adapt installer UI strings for upgrades

* Add rc file localisation infrastructure

* Add installer development documentation

* Make WiX version explicit

* Make final installer adjustments
  • Loading branch information
J5lx authored Mar 11, 2024
1 parent 288ecf3 commit d513920
Show file tree
Hide file tree
Showing 34 changed files with 1,883 additions and 22 deletions.
81 changes: 70 additions & 11 deletions .github/actions/create-package/create-package.sh
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,28 @@
trap 'echo "::error::Command failed"' ERR
set -eE

harvest_files() {
echo "<?xml version='1.0' encoding='utf-8'?>"
echo "<Wix xmlns='http://wixtoolset.org/schemas/v4/wxs'>"
echo " <Fragment>"
echo " <ComponentGroup Id='$1' Directory='INSTALLDIR'>"

while IFS= read -r filepath; do
local subdirectory="$(dirname "${filepath}")"
if [ "${subdirectory}" = "." ]; then
echo " <Component>"
else
echo " <Component Subdirectory='${subdirectory}'>"
fi
echo " <File Source='${filepath}' />"
echo " </Component>"
done

echo " </ComponentGroup>"
echo " </Fragment>"
echo "</Wix>"
}

create_package_linux() {
echo "::group::Set up AppImage contents"
make install INSTALL_ROOT="${PWD}/Pencil2D"
Expand Down Expand Up @@ -40,7 +62,7 @@ wayland-decoration-client,wayland-graphics-integration-client,wayland-shell-inte
${update_info} \
-appimage
local qtsuffix="-qt${INPUT_QT}"
local output_name="pencil2d${qtsuffix/-qt5/}-linux-$1-$(date +%F)"
local output_name="pencil2d${qtsuffix/-qt5/}-linux-$3"
mv Pencil2D*.AppImage "$output_name.AppImage"
mv Pencil2D*.AppImage.zsync "$output_name.AppImage.zsync" \
&& sed -i '1,/^$/s/^\(Filename\|URL\): .*$/\1: '"$output_name.AppImage/" "$output_name.AppImage.zsync" \
Expand Down Expand Up @@ -84,8 +106,8 @@ create_package_macos() {
popd >/dev/null
echo "Create ZIP"
local qtsuffix="-qt${INPUT_QT}"
bsdtar caf "pencil2d${qtsuffix/-qt5/}-mac-$1-$(date +%F).zip" Pencil2D
echo "output-basename=pencil2d${qtsuffix/-qt5/}-mac-$1-$(date +%F)" > "${GITHUB_OUTPUT}"
bsdtar caf "pencil2d${qtsuffix/-qt5/}-mac-$3.zip" Pencil2D
echo "output-basename=pencil2d${qtsuffix/-qt5/}-mac-$3" > "${GITHUB_OUTPUT}"
}

create_package_windows() {
Expand All @@ -104,19 +126,56 @@ create_package_windows() {

echo "Remove files"
find \( -name '*.pdb' -o -name '*.ilk' \) -delete
echo "::group::Deploy Qt libraries"
windeployqt Pencil2D/pencil2d.exe
echo "::endgroup::"
echo "Deploy Qt libraries"
# windeployqt lists some translation files that it doesn't actually copy, and the MSVC redistributable is handled by the bundle, so skip those
windeployqt --list relative Pencil2D/pencil2d.exe | grep -v '^translations\\qtbase_' | grep -v '^translations\\qtmultimedia_' | grep -v '^vc_' | harvest_files windeployqt > windeployqt.wxs
echo "Copy OpenSSL DLLs"
curl -fsSLO https://download.firedaemon.com/FireDaemon-OpenSSL/openssl-1.1.1w.zip
"${WINDIR}\\System32\\tar" xf openssl-1.1.1w.zip
local xbits="x${platform#win}"
local _xbits="-${xbits}"
local wordsize="${platform#win}"
local xbits="x${wordsize}"
local _xbits="-x${wordsize}"
cp "openssl-1.1\\${xbits/32/86}\\bin\\lib"{ssl,crypto}"-1_1${_xbits/-x32/}.dll" Pencil2D/
echo "::group::Create Installer"
env -C ../util/installer qmake CONFIG-=debug_and_release CONFIG+=release
env -C ../util/installer "PATH=${PATH/\/usr\/bin:/}" nmake
env -C Pencil2D find resources/ -type f | harvest_files resources > resources.wxs
for i in ../util/installer/translations/pencil2d_*.wxl.xlf; do
local basename="$(basename -s .wxl.xlf "$i")"
local locale="${basename#*_}"
local culture="${locale/_/-}"
local lcid="$(pwsh -c "(Get-Culture -Name ${culture}).LCID")"
sed "s/Culture=\"en\"/Culture=\"${culture}\"/;s/Language=\"9\"/Language=\"${lcid}\"/" ../util/installer/pencil2d.wxl > "../util/installer/pencil2d_${locale}.wxl"
tikal.bat -m -fc ../util/installer/okf_xml_wxl -ie utf-8 -oe utf-8 -sd ../util/installer -od ../util/installer "${i}"
done
local versiondefines="-d Edition=Nightly -d NightlyBuildNumber=$1 -d NightlyBuildTimestamp=$(date +%F)"
if [ "$IS_RELEASE" = "true" ]; then
versiondefines="-d Edition=Release -d Version=$2"
fi
wix build -pdbtype none -arch "x${wordsize/32/86}" -dcl high -b ../util/installer -b Pencil2D \
-d "ProductCode=$(python -c "import uuid; print(str(uuid.uuid5(uuid.NAMESPACE_URL, '-Nhttps://github.com/${GITHUB_REPOSITORY}/commit/${GITHUB_SHA}#${platform}')).upper())")" \
$versiondefines \
-out "pencil2d-${platform}-$3.msi" \
../util/installer/pencil2d.wxs windeployqt.wxs resources.wxs
wix build -pdbtype none -arch "x${wordsize/32/86}" -dcl high -sw1133 -b ../util/installer -b Pencil2D \
-ext WixToolset.Util.wixext -ext WixToolset.Bal.wixext \
$versiondefines \
-out "pencil2d-${platform}-$3.exe" \
../util/installer/pencil2d.bundle.wxs
echo "::endgroup::"
echo "Create ZIP"
local qtsuffix="-qt${INPUT_QT}"
"${WINDIR}\\System32\\tar" caf "pencil2d${qtsuffix/-qt5/}-${platform}-$1-$(date +%F).zip" Pencil2D
echo "output-basename=pencil2d${qtsuffix/-qt5/}-${platform}-$1-$(date +%F)" > "${GITHUB_OUTPUT}"
"${WINDIR}\\System32\\tar" caf "pencil2d${qtsuffix/-qt5/}-${platform}-$3.zip" Pencil2D
# This basename pattern deliberately does not include the installer for the Qt 6 build.
# Should this ever be changed so that more than one installer is uploaded per workflow run,
# absolutely make sure not to break any Windows Installer rules.
echo "output-basename=pencil2d${qtsuffix/-qt5/}-${platform}-$3" > "${GITHUB_OUTPUT}"
}

"create_package_$(echo $RUNNER_OS | tr '[A-Z]' '[a-z]')" "${GITHUB_RUN_NUMBER}"
eval "$(grep '^VERSION =' ../util/common.pri | tr -d '[:blank:]')"
buildversion="${GITHUB_RUN_NUMBER}-$(date +%F)"
if [ "$IS_RELEASE" = "true" ]; then
buildversion="${VERSION}"
fi

"create_package_$(echo $RUNNER_OS | tr '[A-Z]' '[a-z]')" "${GITHUB_RUN_NUMBER}" "${VERSION}" "${buildversion}"
12 changes: 6 additions & 6 deletions .github/actions/install-dependencies/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,15 @@ inputs:
runs:
using: composite
steps:
- run: ${GITHUB_ACTION_PATH}/install-dependencies.sh
shell: bash
env:
RUNNER_OS: ${{runner.os}}
INPUT_ARCH: ${{inputs.arch}}
INPUT_QT: ${{inputs.qt}}
- if: runner.os == 'Windows'
uses: jurplel/install-qt-action@v3
with:
arch: ${{inputs.arch}}
version: ${{matrix.qt == 6 && '6.4.2' || '5.15.2'}}
modules: ${{matrix.qt == 6 && 'qtmultimedia' || ''}}
- run: ${GITHUB_ACTION_PATH}/install-dependencies.sh
shell: bash
env:
RUNNER_OS: ${{runner.os}}
INPUT_ARCH: ${{inputs.arch}}
INPUT_QT: ${{inputs.qt}}
10 changes: 9 additions & 1 deletion .github/actions/install-dependencies/install-dependencies.sh
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,15 @@ setup_macos() {
}

setup_windows() {
: # Nothing to do here
pip install translate-toolkit[rc]
curl -fsSLO https://okapiframework.org/binaries/main/1.45.0/okapi-apps_win32-x86_64_1.45.0.zip
mkdir okapi
"${WINDIR}\\System32\\tar" xfC okapi-apps_win32-x86_64_1.45.0.zip okapi
dotnet tool install -g wix --version 4.0.4
wix extension add -g WixToolset.Util.wixext/4.0.4 WixToolset.Bal.wixext/4.0.4
nuget install -x -OutputDirectory util/installer WixToolset.BootstrapperCore.Native -Version 4.0.4
nuget install -x -OutputDirectory util/installer WixToolset.DUtil -Version 4.0.4
nuget install -x -OutputDirectory util/installer WixToolset.BalUtil -Version 4.0.4
}

"setup_$(echo "${RUNNER_OS}" | tr '[A-Z]' '[a-z]')"
2 changes: 2 additions & 0 deletions .github/actions/setup-environment/setup-environment.sh
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ setup_windows() {
local platform="${INPUT_ARCH%%_*}"
local vcvars="C:\\Program^ Files^ ^(x86^)\\Microsoft^ Visual^ Studio\\2019\\Enterprise\\VC\\Auxiliary\\Build\\vcvars${platform#win}.bat"
($(which cmd) //c set; $(which cmd) //c "${vcvars} 2>&1>nul && set") | sort -st= -k1,1 | uniq -u >> "${GITHUB_ENV}"
echo "${JAVA_HOME_17_X64}\\bin" >> "${GITHUB_PATH}"
realpath okapi/ >> "${GITHUB_PATH}"
}

"setup_$(echo "${RUNNER_OS}" | tr '[A-Z]' '[a-z]')"
41 changes: 40 additions & 1 deletion app/app.pro
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,11 @@ TARGET = pencil2d

RESOURCES += data/app.qrc

MUI_TRANSLATIONS += \
translations/mui_de.po

RC_LANGS.de = --lang LANG_GERMAN --sublang SUBLANG_NEUTRAL

EXTRA_TRANSLATIONS += \
$$PWD/../translations/pencil_ar.ts \
$$PWD/../translations/pencil_ca.ts \
Expand Down Expand Up @@ -225,7 +230,7 @@ win32 {

PRI_CONFIG = data/resources.xml
PRI_INDEX_NAME = Pencil2D
RC_FILE = data/pencil2d.rc
RC_FILES = data/version.rc data/mui.rc
INSTALLS += target visualelements resources

makepri.name = makepri
Expand All @@ -235,6 +240,40 @@ win32 {
silent: makepri.commands = @echo makepri ${QMAKE_FILE_IN} && $$makepri.commands
makepri.CONFIG = no_link
QMAKE_EXTRA_COMPILERS += makepri

ensurePathEnv()
isEmpty(PO2RC): for(dir, QMAKE_PATH_ENV) {
exists("$$dir/po2rc.exe") {
PO2RC = "$$dir/po2rc.exe"
break()
}
}
!isEmpty(PO2RC) {
defineReplace(rcLang) {
name = $$basename(1)
base = $$section(name, ., 0, -2)
return($$member(RC_LANGS.$$section(base, _, 1), 0, -1))
}
po2rc.name = po2rc
po2rc.input = MUI_TRANSLATIONS
po2rc.output = ${QMAKE_FILE_IN_BASE}.rc
po2rc.commands = $$shell_path($$PO2RC) -t $$PWD/data/mui.rc ${QMAKE_FILE_IN} ${QMAKE_FUNC_FILE_IN_rcLang} ${QMAKE_FILE_OUT}
silent: makepri.commands = @echo po2rc ${QMAKE_FILE_IN} && $$makepri.commands
po2rc.CONFIG = no_link
QMAKE_EXTRA_COMPILERS += po2rc
# variable_out doesn't seem to work in this case
for(file, MUI_TRANSLATIONS): {
name = $$basename(file)
RC_FILES += $$replace(name, .po, .rc)
}
} else {
warning("po2rc was not found. MUI resources will not be translated. You can safely ignore this warning if you do not plan to distribute this build of Pencil2D through its installer.")
}

for(file, RC_FILES): RC_INCLUDES += "$${LITERAL_HASH}include \"$$file\""
write_file($$OUT_PWD/pencil2d.rc, RC_INCLUDES)|error()
RC_FILE = $$OUT_PWD/pencil2d.rc
RC_INCLUDEPATH += $$PWD $$PWD/data
}

unix:!macx {
Expand Down
15 changes: 15 additions & 0 deletions app/data/mui.rc
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
#if defined(__MINGW32__) || defined(__MINGW64__)
// This is needed for MinGW versions < 8
// (not to be confused with the GCC version by which Qt labels their MinGW packages)
#include <winresrc.h>
#else
#include <winres.h>
#endif

STRINGTABLE
LANGUAGE LANG_ENGLISH, SUBLANG_NEUTRAL
{
0 "Pencil2D"
1 "Pencil2D Animation"
2 "Pencil2D Animation (Old Format)"
}
1 change: 0 additions & 1 deletion app/data/pencil2d.rc

This file was deleted.

66 changes: 66 additions & 0 deletions app/data/version.rc
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
#if defined(__MINGW32__) || defined(__MINGW64__)
// This is needed for MinGW versions < 8
// (not to be confused with the GCC version by which Qt labels their MinGW packages)
#include <winresrc.h>
#else
#include <winres.h>
#endif

#define STRINGIFY_(x) #x
#define STRINGIFY(x) STRINGIFY_(x)

#if defined(PENCIL2D_RELEASE_BUILD)
#define BUILD_FILEFLAG 0
#elif defined(PENCIL2D_NIGHTLY_BUILD)
#define BUILD_FILEFLAG VS_FF_PRERELEASE
#else
#define BUILD_FILEFLAG VS_FF_PRIVATEBUILD
#endif

#if defined(QT_NO_DEBUG)
#define DEBUG_FILEFLAG 0
#else
#define DEBUG_FILEFLAG VS_FF_DEBUG
#endif

IDI_ICON1 ICON "pencil2d.ico"

VS_VERSION_INFO VERSIONINFO
FILEVERSION APP_VERSION_RC
PRODUCTVERSION APP_VERSION_RC
FILEFLAGSMASK VS_FFI_FILEFLAGSMASK
FILEFLAGS DEBUG_FILEFLAG|BUILD_FILEFLAG
FILEOS VOS_NT_WINDOWS32
FILETYPE VFT_APP
{
BLOCK "StringFileInfo"
{
BLOCK "000904B0"
{
VALUE "ProductName", "Pencil2D"
#ifdef __GNUC__
VALUE "ProductVersion", STRINGIFY(APP_VERSION)
#else
VALUE "ProductVersion", APP_VERSION
#endif
VALUE "CompanyName", "The Pencil2D Team"
VALUE "LegalCopyright", "\xA9 The Pencil2D Team"
VALUE "FileDescription", "Pencil2D"
#ifdef __GNUC__
VALUE "FileVersion", STRINGIFY(APP_VERSION)
#else
VALUE "FileVersion", APP_VERSION
#endif
VALUE "InternalName", "pencil2d"
VALUE "OriginalFilename", "pencil2d.exe"
#if !defined(PENCIL2D_RELEASE_BUILD) && !defined(PENCIL2D_NIGHTLY_BUILD)
VALUE "PrivateBuild", "Private Build"
#endif
}
}

BLOCK "VarFileInfo"
{
VALUE "Translation", 0x0009, 0x04B0
}
}
27 changes: 27 additions & 0 deletions app/translations/mui.pot
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
#. extracted from data\mui.rc
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2023-09-22 15:26+0200\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <[email protected]>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Accelerator-Marker: &\n"
"X-Generator: Translate Toolkit 3.9.0\n"
"X-Merge-On: location\n"

#: STRINGTABLE.0
msgid "Pencil2D"
msgstr ""

#: STRINGTABLE.1
msgid "Pencil2D Animation"
msgstr ""

#: STRINGTABLE.2
msgid "Pencil2D Animation (Old Format)"
msgstr ""
32 changes: 32 additions & 0 deletions app/translations/mui_de.po
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
#
# Translators:
# Jakob <[email protected]>, 2023
#
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2023-09-22 15:26+0200\n"
"PO-Revision-Date: 2023-09-22 14:10+0000\n"
"Last-Translator: Jakob <[email protected]>, 2023\n"
"Language-Team: German (https://app.transifex.com/pencil2d/teams/76612/de/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Language: de\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"X-Accelerator-Marker: &\n"
"X-Generator: Translate Toolkit 3.9.0\n"
"X-Merge-On: location\n"

#: STRINGTABLE.0
msgid "Pencil2D"
msgstr "Pencil2D"

#: STRINGTABLE.1
msgid "Pencil2D Animation"
msgstr "Pencil2D-Animation"

#: STRINGTABLE.2
msgid "Pencil2D Animation (Old Format)"
msgstr "Pencil2D-Animation (altes Format)"
12 changes: 12 additions & 0 deletions core_lib/src/external/win32/win32.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ GNU General Public License for more details.
#include <QCoreApplication>
#include <QSettings>

#include <ShObjIdl.h>

#include "pencildef.h"

namespace PlatformHandler
Expand All @@ -29,6 +31,16 @@ namespace PlatformHandler
bool isDarkMode() { return false; };
void initialise()
{
#if _WIN32_WINNT >= _WIN32_WINNT_WIN7
#if defined(PENCIL2D_RELEASE_BUILD)
SetCurrentProcessExplicitAppUserModelID(L"Pencil2D.Pencil2D.Release");
#elif defined(PENCIL2D_NIGHTLY_BUILD)
SetCurrentProcessExplicitAppUserModelID(L"Pencil2D.Pencil2D.Nightly");
#else
SetCurrentProcessExplicitAppUserModelID(L"Pencil2D.Pencil2D");
#endif
#endif // _WIN32_WINNT >= _WIN32_WINNT_WIN7

#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
// Temporary solution for high DPI displays
// EnableHighDpiScaling is a just in case mechanism in the event that we
Expand Down
Loading

0 comments on commit d513920

Please sign in to comment.