Skip to content

Commit a668d2e

Browse files
committed
[IMP] packaging: add wine based windows packaging
Replace the initial KVM VM workflow with a Docker container using Wine for cross-platform Windows builds. - Add Dockerfile_wine: Wine + Python 3.14 + Inno Setup 6 + mingw-w64 + Rust - Add buildwine.sh: orchestrates the full build (Xvfb, Wine Python, cross-compilation via MinGW, PyInstaller, Inno Setup) - Makefile: add $(PYTHON) for Wine Python, exclude watchers from submodule loop - getversion.sh: add AW_VERSION environment variable - package-all.sh: add AW_PLATFORM and INNOSETUPDIR for Inno Setup via Wine - includes aw-systray-odoo.exe in NSIS installer The main idea is to cross compile rust binaries for windows within a Docker container. For that, the Cargo build target "x86_64-pc-windows-gnu" is used. On the other hand, the wine is needed for pyinstaller and Inno Setup.
1 parent 372c1e3 commit a668d2e

5 files changed

Lines changed: 226 additions & 9 deletions

File tree

Makefile

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,9 @@ package:
196196
rm -rf dist
197197
mkdir -p dist/activitywatch
198198
for dir in $(PACKAGEABLES); do \
199+
if [[ "$(ODOO_WINDOWS_BUILD)" == "true" ]] && [[ "$$dir" == "aw-watcher-afk" || "$$dir" == "aw-watcher-window" ]]; then \
200+
continue; \
201+
fi; \
199202
make --directory=$$dir package; \
200203
cp -r $$dir/dist/$$dir dist/activitywatch; \
201204
done
@@ -205,11 +208,8 @@ ifeq ($(TAURI_BUILD),true)
205208
cp aw-server-rust/target/$(targetdir)/aw-sync dist/activitywatch/aw-server-rust/aw-sync
206209
else
207210
ifeq ($(ODOO_WINDOWS_BUILD),true)
208-
# ODOO_WINDOWS_BUILD: Install pystray and build aw-systray-odoo.exe via PyInstaller
209-
@echo "ODOO_WINDOWS_BUILD: Installing pystray..."
210-
python -m pip install pystray pillow pywin32
211211
@echo "ODOO_WINDOWS_BUILD: Building aw-systray-odoo.exe via PyInstaller..."
212-
pyinstaller --clean --noconfirm odoo-setup/aw-systray-odoo.spec
212+
$(PYTHON) -m PyInstaller --clean --noconfirm odoo-setup/aw-systray-odoo.spec
213213
cp dist/aw-systray-odoo.exe dist/activitywatch/aw-systray-odoo.exe
214214
endif
215215
endif

odoo-setup/Dockerfile_wine

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
FROM debian:trixie
2+
3+
ENV DEBIAN_FRONTEND=noninteractive
4+
ENV LC_ALL=C.UTF-8
5+
6+
RUN dpkg --add-architecture i386
7+
8+
ARG USID=1000 GRID=1000
9+
RUN groupadd -g $GRID odoo \
10+
&& useradd --create-home -u $USID -g odoo -G audio,video odoo \
11+
&& mkdir -p /run/user/$USID \
12+
&& chmod 700 /run/user/$USID
13+
14+
RUN apt-get update \
15+
&& apt-get install -y curl unzip 7zip xvfb mingw-w64 rustup make build-essential git zip \
16+
python3 python3-pip python3-venv python3-poetry \
17+
&& rm -rf /var/lib/apt/lists/*
18+
19+
RUN mkdir -pm755 /etc/apt/keyrings \
20+
&& curl -sSL https://dl.winehq.org/wine-builds/winehq.key -o /etc/apt/keyrings/winehq-archive.key \
21+
&& curl -sSL https://dl.winehq.org/wine-builds/debian/dists/trixie/winehq-trixie.sources -o /etc/apt/sources.list.d/winehq.sources \
22+
&& apt-get update \
23+
&& apt-get install --install-recommends -y winehq-stable \
24+
&& rm -rf /var/lib/apt/lists/*
25+
26+
USER odoo
27+
28+
ENV NVM_DIR=/home/odoo/.nvm
29+
ENV NODE_VERSION=22.22.1
30+
RUN curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.3/install.sh | bash && \
31+
bash -c "source $NVM_DIR/nvm.sh && \
32+
nvm install $NODE_VERSION && \
33+
nvm alias default $NODE_VERSION && \
34+
nvm use default"
35+
36+
ENV PATH="$NVM_DIR/versions/node/v$NODE_VERSION/bin:$PATH"
37+
38+
RUN rustup toolchain install nightly \
39+
&& rustup default nightly \
40+
&& rustup target add x86_64-pc-windows-gnu
41+
42+
RUN python3 -m venv /home/odoo/awvenv
43+
ENV PATH="/home/odoo/awvenv/bin:$PATH"
44+
ENV VIRTUAL_ENV=/home/odoo/awvenv
45+
46+
ENV XDG_RUNTIME_DIR=/run/user/$USID
47+
48+
ENV WINEPREFIX=/home/odoo/.wine
49+
ENV WINEARCH=win64
50+
# Ignoring debug and fixme's message when running wine
51+
ENV WINEDEBUG=-all
52+
53+
ENV CARGO_BUILD_TARGET=x86_64-pc-windows-gnu
54+
55+
RUN wineboot --init && wineserver -w
56+
57+
RUN curl -fsSL https://www.python.org/ftp/python/3.14.4/python-3.14.4-amd64.exe -o /tmp/python-installer.exe \
58+
&& chmod u+x /tmp/python-installer.exe
59+
60+
RUN xvfb-run --auto-servernum --server-args="-screen 0 1024x768x24" \
61+
sh -c "wine /tmp/python-installer.exe /quiet Include_doc=0 InstallAllUsers=1 PrependPath=1 \
62+
&& (while pgrep -u $(id -u) -x wineserver > /dev/null 2>&1; do sleep 1; done) \
63+
&& wineserver -w"
64+
65+
RUN PYTHON_EXE="/home/odoo/.wine/drive_c/Program Files/Python314/python.exe" \
66+
&& PIP_EXE="/home/odoo/.wine/drive_c/Program Files/Python314/Scripts/pip.exe" \
67+
&& xvfb-run --auto-servernum --server-args="-screen 0 1024x768x24" \
68+
sh -c "wine '$PIP_EXE' install pystray pillow pywin32 pyinstaller"
69+
70+
RUN curl -fsSL https://github.com/jrsoftware/issrc/releases/download/is-6_7_1/innosetup-6.7.1.exe -o /tmp/innosetup.exe \
71+
&& chmod u+x /tmp/innosetup.exe \
72+
&& xvfb-run --auto-servernum wine /tmp/innosetup.exe /VERYSILENT /SUPPRESSMSGBOXES /NORESTART \
73+
&& (while pgrep -u $(id -u) -x wineserver > /dev/null 2>&1; do sleep 1; done) \
74+
&& wineserver -w

odoo-setup/buildwine.sh

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
#!/bin/bash
2+
set -euo pipefail
3+
4+
: "${CARGO_BUILD_TARGET:=x86_64-pc-windows-gnu}"
5+
: "${ODOO_WINDOWS_BUILD:=true}"
6+
: "${SKIP_SERVER_PYTHON:=true}"
7+
: "${WINEPREFIX:=/home/odoo/.wine}"
8+
: "${WINEARCH:=win64}"
9+
10+
PYTHON_DIR="$WINEPREFIX/drive_c/Program Files/Python314"
11+
PYTHON_WIN="C:\\Program Files\\Python314\\python.exe"
12+
PIP_WIN="C:\\Program Files\\Python314\\Scripts\\pip.exe"
13+
AW_SOURCE="${HOME}/activitywatch"
14+
15+
export CARGO_BUILD_TARGET ODOO_WINDOWS_BUILD SKIP_SERVER_PYTHON
16+
export WINEPREFIX WINEARCH WINEDEBUG=-all
17+
export XDG_RUNTIME_DIR=/run/user/1000
18+
export DISPLAY=${DISPLAY:-:99}
19+
20+
# Python venv wrappers needed for make subshells
21+
export VIRTUAL_ENV=/home/odoo/awvenv
22+
export PATH="/home/odoo/awvenv/bin:$PATH"
23+
mkdir -p /tmp/awvenv-wrappers
24+
cat > /tmp/awvenv-wrappers/pip << 'EOF'
25+
#!/bin/bash
26+
exec /home/odoo/awvenv/bin/pip "$@"
27+
EOF
28+
cat > /tmp/awvenv-wrappers/python << 'EOF'
29+
#!/bin/bash
30+
exec /home/odoo/awvenv/bin/python "$@"
31+
EOF
32+
chmod +x /tmp/awvenv-wrappers/{pip,python}
33+
export PATH="/tmp/awvenv-wrappers:$PATH"
34+
export PYTHON="wine '$PYTHON_WIN'"
35+
export WINE_PYTHON="wine '$PYTHON_WIN'"
36+
37+
# Xvfb start and stop functions
38+
XVFB_PID=""
39+
_start_xvfb() {
40+
if ! pgrep -x Xvfb > /dev/null 2>&1; then
41+
Xvfb $DISPLAY -screen 0 1024x768x24 &
42+
XVFB_PID=$!
43+
sleep 2
44+
echo "[Xvfb] started (PID $XVFB_PID)"
45+
fi
46+
}
47+
48+
_stop_xvfb() {
49+
if [[ -n "$XVFB_PID" ]] && kill -0 "$XVFB_PID" 2>/dev/null; then
50+
kill "$XVFB_PID"
51+
wait "$XVFB_PID" 2>/dev/null || true
52+
echo "[Xvfb] stopped"
53+
fi
54+
}
55+
56+
trap _stop_xvfb EXIT
57+
_start_xvfb
58+
59+
# Wine Python helpers
60+
wine_pip() {
61+
xvfb-run --auto-servernum wine "$PIP_WIN" "$@"
62+
}
63+
64+
wine_python() {
65+
xvfb-run --auto-servernum wine "$PYTHON_WIN" "$@"
66+
}
67+
68+
echo "[Wine] initializing prefix..."
69+
wineboot --init 2>/dev/null || true
70+
wineserver -w
71+
72+
echo "[Build] Starting ActivityWatch build..."
73+
export AW_VERSION="0.13.2"
74+
cd "$AW_SOURCE"
75+
make build
76+
77+
echo "[PyInstaller] Installing dependencies ..."
78+
wine_pip install --no-warn-script-location pystray pillow pynput wmi
79+
80+
echo "[PyInstaller] Installing aw packages in Wine..."
81+
wine_pip install --no-warn-script-location \
82+
"$AW_SOURCE/aw-core" \
83+
"$AW_SOURCE/aw-client"
84+
85+
echo "[PyInstaller] Building aw-systray-odoo.exe..."
86+
wine_python -m PyInstaller --clean --noconfirm odoo-setup/aw-systray-odoo.spec
87+
88+
echo "[PyInstaller] Building aw-watcher-afk.exe..."
89+
cd "$AW_SOURCE/aw-watcher-afk"
90+
wine_python -m PyInstaller --clean --noconfirm aw-watcher-afk.spec
91+
92+
echo "[PyInstaller] Building aw-watcher-window.exe..."
93+
cd "$AW_SOURCE/aw-watcher-window"
94+
wine_python -m PyInstaller --clean --noconfirm aw-watcher-window.spec
95+
96+
cd "$AW_SOURCE"
97+
STAGING_DIR="$AW_SOURCE/staging-watcher-build"
98+
mkdir -p "$STAGING_DIR"
99+
cp "$AW_SOURCE/dist/aw-systray-odoo.exe" "$STAGING_DIR/"
100+
cp -r "$AW_SOURCE/aw-watcher-afk/dist/aw-watcher-afk" "$STAGING_DIR/"
101+
cp -r "$AW_SOURCE/aw-watcher-window/dist/aw-watcher-window" "$STAGING_DIR/"
102+
103+
export AW_VERSION="odoo-$(date +%Y-%m-%d)"
104+
export AW_PLATFORM=windows
105+
export INNOSETUPDIR="C:\Program Files (x86)\Inno Setup 6"
106+
echo "[Package] Creating package..."
107+
rm -rf "$AW_SOURCE/dist"
108+
mkdir -p "$AW_SOURCE/dist/activitywatch"
109+
for dir in aw-server-rust; do
110+
make --directory="$AW_SOURCE/$dir" package
111+
cp -r "$AW_SOURCE/$dir/dist/$dir" "$AW_SOURCE/dist/activitywatch/"
112+
done
113+
cp -r "$STAGING_DIR/aw-watcher-afk" "$AW_SOURCE/dist/activitywatch/"
114+
cp -r "$STAGING_DIR/aw-watcher-window" "$AW_SOURCE/dist/activitywatch/"
115+
cp "$STAGING_DIR/aw-systray-odoo.exe" "$AW_SOURCE/dist/activitywatch/"
116+
rm -rf "$STAGING_DIR"
117+
118+
# Remove problem-causing binaries (see original Makefile)
119+
rm -f "$AW_SOURCE/dist/activitywatch/libdrm.so.2"
120+
rm -f "$AW_SOURCE/dist/activitywatch/libharfbuzz.so.0"
121+
rm -f "$AW_SOURCE/dist/activitywatch/libfontconfig.so.1"
122+
rm -f "$AW_SOURCE/dist/activitywatch/libfreetype.so.6"
123+
rm -rf "$AW_SOURCE/dist/activitywatch/pytz"
124+
125+
# Build zip and installer
126+
bash "$AW_SOURCE/scripts/package/package-all.sh"
127+
128+
echo ""
129+
echo "=== Build finished 🎉 ==="
130+
find "$AW_SOURCE/dist" -name "*.exe" -o -name "*.zip" 2>/dev/null | head -20

scripts/package/getversion.sh

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@
33
# TODO: Merge with scripts/package/getversion.sh
44
# set -e
55

6-
if [[ $TRAVIS_TAG ]]; then
6+
if [[ -n "${AW_VERSION:-}" ]]; then
7+
_version="$AW_VERSION";
8+
elif [[ $TRAVIS_TAG ]]; then
79
_version=$TRAVIS_TAG;
810
elif [[ $APPVEYOR_REPO_TAG_NAME ]]; then
911
_version=$APPVEYOR_REPO_TAG_NAME;

scripts/package/package-all.sh

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,11 @@ function get_platform() {
1111
# Will return "macos" for macOS/OS X
1212
# Will return "windows" for Windows/MinGW/msys
1313

14+
if [[ -n "${AW_PLATFORM:-}" ]]; then
15+
echo "$AW_PLATFORM"
16+
return
17+
fi
18+
1419
_platform=$(uname | tr '[:upper:]' '[:lower:]')
1520
if [[ $_platform == "darwin" ]]; then
1621
_platform="macos";
@@ -67,18 +72,24 @@ function build_setup() {
6772
filename="activitywatch-${version}-${platform}-${arch}-setup.exe"
6873
echo "Name of package will be: $filename"
6974

70-
innosetupdir="/c/Program Files (x86)/Inno Setup 6"
71-
if [ ! -d "$innosetupdir" ]; then
75+
: "${INNOSETUPDIR:=}"
76+
if [[ -n "$INNOSETUPDIR" ]] && [ -d "$INNOSETUPDIR" ]; then
77+
innosetupdir="$INNOSETUPDIR"
78+
elif [ -d "/c/Program Files (x86)/Inno Setup 6" ]; then
79+
innosetupdir="/c/Program Files (x86)/Inno Setup 6"
80+
elif command -v winepath > /dev/null && winepath -u "C:\\Program Files (x86)\\Inno Setup 6" &>/dev/null; then
81+
innosetupdir="$(winepath -u 'C:\Program Files (x86)\Inno Setup 6')"
82+
else
7283
echo "ERROR: Couldn't find innosetup which is needed to build the installer. We suggest you install it using chocolatey. Exiting."
7384
exit 1
7485
fi
7586

7687
# Windows installer version should not include 'v' prefix, see: https://github.com/microsoft/winget-pkgs/pull/17564
7788
version_no_prefix="$(echo $version | sed -e 's/^v//')"
7889
if [[ $TAURI_BUILD == "true" ]]; then
79-
env AW_VERSION=$version_no_prefix "$innosetupdir/iscc.exe" scripts/package/aw-tauri.iss
90+
env AW_VERSION=$version_no_prefix wine "$innosetupdir/iscc.exe" scripts/package/aw-tauri.iss
8091
else
81-
env AW_VERSION=$version_no_prefix "$innosetupdir/iscc.exe" scripts/package/activitywatch-setup.iss
92+
env AW_VERSION=$version_no_prefix wine "$innosetupdir/iscc.exe" scripts/package/activitywatch-setup.iss
8293
fi
8394
mv dist/activitywatch-setup.exe dist/$filename
8495
echo "Setup built!"

0 commit comments

Comments
 (0)