Skip to content

Commit fbe595a

Browse files
committed
Bugfixing build stuff for windows, add app.icns/app.ico
1 parent e4f6ae3 commit fbe595a

File tree

5 files changed

+134
-115
lines changed

5 files changed

+134
-115
lines changed

build_exe.bat

Lines changed: 44 additions & 113 deletions
Original file line numberDiff line numberDiff line change
@@ -1,129 +1,60 @@
11
@echo off
2-
REM build_exe.bat — Clean build a Windows EXE via cx_Freeze (and optional MSI)
2+
setlocal ENABLEEXTENSIONS
3+
rem Force predictable working dir = this script's folder
4+
cd /d "%~dp0" || (echo Failed to cd into script dir & exit /b 1)
35

4-
setlocal EnableExtensions EnableDelayedExpansion
5-
chcp 65001 >NUL
6+
rem --- CONFIG ---
7+
set "PYTHON=python"
8+
set "FREEZE_SETUP=freeze_setup2.py"
69

7-
REM --- Configuration ----------------------------------------------------------
8-
REM Allow overrides: set PYTHON_BIN=C:\Path\To\python.exe
9-
set "PYTHON_BIN=%PYTHON_BIN%"
10-
if "%PYTHON_BIN%"=="" set "PYTHON_BIN=python"
11-
12-
set "APP_NAME=AnycubicNFCTaggerQT5"
13-
set "ENTRY_EXE=%APP_NAME%.exe"
14-
set "BUILD_DIR=build"
15-
set "DIST_DIR=dist"
16-
set "DO_MSI=%DO_MSI%"
17-
if "%DO_MSI%"=="" set "DO_MSI=1"
18-
19-
REM --- Helpers ----------------------------------------------------------------
20-
set "ESC="
21-
for /F "delims=" %%A in ('echo prompt $E^| cmd') do set "ESC=%%A"
22-
set "INFO=%ESC%[1;34m[INFO]%ESC%[0m"
23-
set "WARN=%ESC%[1;33m[WARN]%ESC%[0m"
24-
set "ERR =%ESC%[1;31m[ERR ]%ESC%[0m"
25-
26-
echo %INFO% Using Python: %PYTHON_BIN%
27-
28-
REM --- Checks: python present -------------------------------------------------
29-
where "%PYTHON_BIN%" >NUL 2>&1
30-
if errorlevel 1 (
31-
echo %ERR % Python not found. Set PYTHON_BIN to a valid Python 3.11/3.12 interpreter.
10+
rem --- CHECK PYTHON ---
11+
where "%PYTHON%" >nul 2>nul || (
12+
echo Python not found in PATH.
3213
exit /b 1
3314
)
3415

35-
REM --- Checks: ensure cx_Freeze and PyQt5 installed --------------------------
36-
"%PYTHON_BIN%" -c "import cx_Freeze" >NUL 2>&1
37-
if errorlevel 1 (
38-
echo %WARN% cx_Freeze not found. Installing...
39-
"%PYTHON_BIN%" -m pip install -U cx_Freeze || (echo %ERR % Failed to install cx_Freeze & exit /b 1)
40-
)
41-
42-
"%PYTHON_BIN%" -c "import PyQt5" >NUL 2>&1
43-
if errorlevel 1 (
44-
echo %WARN% PyQt5 not found. Installing...
45-
"%PYTHON_BIN%" -m pip install -U PyQt5 || (echo %ERR % Failed to install PyQt5 & exit /b 1)
46-
)
47-
48-
REM --- Read version from pyproject.toml (fallback 0.3.0) ----------------------
49-
set "VERSION=0.3.0"
50-
for /f "usebackq delims=" %%V in (`
51-
"%PYTHON_BIN%" -c "import pathlib,sys;
52-
try:
53-
import tomllib as tl
54-
except Exception:
55-
import tomli as tl
56-
p=pathlib.Path('pyproject.toml')
57-
print(tl.loads(p.read_text(encoding='utf-8')).get('project',{}).get('version','0.3.0'))"
58-
`) do set "VERSION=%%V"
59-
echo %INFO% Project version: %VERSION%
60-
61-
REM --- Try to close any running instance -------------------------------------
62-
echo %INFO% Closing running app if present...
63-
taskkill /IM "%ENTRY_EXE%" /F >NUL 2>&1
16+
rem --- PRINT ENV ---
17+
echo [INFO] Using Python: %PYTHON%
18+
for /f "delims=" %%V in ('%PYTHON% -c "import sys;print(sys.version)"') do set "PYVER=%%V"
19+
echo [INFO] Python version: %PYVER%
6420

65-
REM --- Clean previous build/dist ---------------------------------------------
66-
if exist "%BUILD_DIR%" (
67-
echo %INFO% Removing "%BUILD_DIR%"...
68-
rmdir /S /Q "%BUILD_DIR%" || (
69-
echo %WARN% First removal attempt failed; retrying...
70-
timeout /t 1 >NUL
71-
rmdir /S /Q "%BUILD_DIR%" || (echo %ERR % Could not remove build directory & exit /b 1)
72-
)
73-
)
74-
75-
if exist "%DIST_DIR%" (
76-
echo %INFO% Removing "%DIST_DIR%"...
77-
rmdir /S /Q "%DIST_DIR%" || (
78-
echo %WARN% First removal attempt failed; retrying...
79-
timeout /t 1 >NUL
80-
rmdir /S /Q "%DIST_DIR%" || (echo %ERR % Could not remove dist directory & exit /b 1)
81-
)
82-
)
83-
84-
REM --- Build portable EXE -----------------------------------------------------
85-
echo %INFO% Building EXE via cx_Freeze (build_exe)...
86-
"%PYTHON_BIN%" freeze_setup.py build_exe
87-
if errorlevel 1 (
88-
echo %ERR % Build failed (build_exe).
21+
rem --- ENSURE PIP ---
22+
%PYTHON% -m ensurepip --upgrade >nul 2>nul
23+
%PYTHON% -m pip --version || (
24+
echo [ERR ] pip not available
8925
exit /b 1
9026
)
9127

92-
REM --- Locate built EXE -------------------------------------------------------
93-
set "FOUND_EXE="
94-
for /r "%BUILD_DIR%" %%F in ("%ENTRY_EXE%") do (
95-
set "FOUND_EXE=%%F"
96-
goto :found_exe
97-
)
98-
:found_exe
28+
rem --- INSTALL/LOCK BUILD DEPS ---
29+
echo [INFO] Installing build deps...
30+
%PYTHON% -m pip install --upgrade "pip<25" wheel ^
31+
"setuptools<=80.9.0,>=65.6.3" ^
32+
"cx_Freeze==8.4.1" ^
33+
"PyQt5>=5.15,<5.16" ^
34+
"pillow" ^
35+
"pyscard>=2.0,<3.0" || goto :pip_fail
9936

100-
if "%FOUND_EXE%"=="" (
101-
echo %WARN% Could not find "%ENTRY_EXE%" under "%BUILD_DIR%". Listing build tree:
102-
dir /S /B "%BUILD_DIR%"
103-
) else (
104-
echo %INFO% Built EXE: "%FOUND_EXE%"
105-
)
37+
rem --- CLEAN ---
38+
echo [INFO] Cleaning build/ dist/ ...
39+
if exist build rmdir /s /q build
40+
if exist dist rmdir /s /q dist
10641

107-
REM --- Optional: Build MSI installer -----------------------------------------
108-
if "%DO_MSI%"=="1" (
109-
echo %INFO% Building MSI installer (bdist_msi)...
110-
"%PYTHON_BIN%" freeze_setup.py bdist_msi
111-
if errorlevel 1 (
112-
echo %WARN% MSI build failed. You can disable MSI by setting DO_MSI=0
113-
) else (
114-
echo %INFO% MSI created under "%DIST_DIR%"
115-
)
116-
) else (
117-
echo %INFO% Skipping MSI build (DO_MSI=0)
118-
)
42+
rem --- RUN BUILD ---
43+
echo [INFO] Building with cx_Freeze...
44+
%PYTHON% "%FREEZE_SETUP%" build || goto :build_fail
11945

120-
REM --- Done -------------------------------------------------------------------
121-
echo %INFO% Done.
12246
echo.
123-
echo Portable EXE under: "%BUILD_DIR%"
124-
if "%DO_MSI%"=="1" echo MSI package (if successful) under: "%DIST_DIR%"
47+
echo [OK] Build finished.
48+
echo [INFO] Contents of dist:
49+
dir /b /s dist
12550
echo.
126-
echo To run:
127-
if not "%FOUND_EXE%"=="" echo "%FOUND_EXE%"
51+
pause
52+
exit /b 0
53+
54+
:pip_fail
55+
echo [ERR ] pip install failed.
56+
exit /b 1
12857

129-
exit /b 0
58+
:build_fail
59+
echo [ERR ] cx_Freeze build failed.
60+
exit /b 1

freeze_setup.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,12 @@
99
]
1010

1111
build_exe_options = dict(
12-
excludes=["tkinter", "tests"],
13-
includes=["PyQt5", "smartcard"],
12+
excludes=["tkinter", "tests", "unittest"],
13+
includes=["PyQt5", "smartcard", "logging", "json", "pathlib"],
1414
packages=["os", "sys", "anycubic_nfc_qt5"],
1515
include_files=include_files,
1616
optimize=1,
17+
silent_level=1,
1718
)
1819

1920
# inside setup(..., options={"bdist_mac": {...}})

freeze_setup2.py

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
# freeze_setup.py
2+
# Cross-platform cx_Freeze setup for Windows (.exe) and macOS (.app)
3+
# Code and comments intentionally in English.
4+
5+
from cx_Freeze import setup, Executable
6+
from pathlib import Path
7+
import sys
8+
9+
APP_NAME = "AnycubicNFCTaggerQT5"
10+
VERSION = "0.3.0"
11+
BASE_DIR = Path(__file__).parent
12+
13+
# Common build options
14+
build_exe_options = {
15+
"includes": [
16+
"PyQt5.QtCore",
17+
"PyQt5.QtGui",
18+
"PyQt5.QtWidgets",
19+
"json",
20+
"logging",
21+
"pathlib",
22+
# --- smartcard (pyscard) ---
23+
"smartcard",
24+
"smartcard.Exceptions",
25+
"smartcard.System",
26+
"smartcard.scard",
27+
"smartcard.CardMonitoring",
28+
"smartcard.CardType",
29+
"smartcard.util",
30+
],
31+
"excludes": [
32+
# removed "smartcard" from excludes, because the app imports it
33+
"tkinter",
34+
"unittest",
35+
"tests",
36+
],
37+
"zip_include_packages": ["encodings", "importlib", "PyQt5"],
38+
"zip_exclude_packages": [],
39+
"optimize": 1,
40+
"silent_level": 1,
41+
}
42+
43+
# Include resources (adjust folder names if needed)
44+
resources = []
45+
for pat in ["resources", "assets", "icons"]:
46+
p = BASE_DIR / pat
47+
if p.exists():
48+
# keep folder structure
49+
resources.append((str(p), str(p)))
50+
build_exe_options["include_files"] = resources
51+
52+
# Platform-specific executable base and icon
53+
if sys.platform == "win32":
54+
base = "Win32GUI"
55+
icon = BASE_DIR / "packaging" / "windows" / "app.ico"
56+
target_name = f"{APP_NAME}.exe"
57+
else:
58+
base = None # GUI apps on macOS don't need a special base
59+
icon = BASE_DIR / "packaging" / "macos" / "app.icns"
60+
target_name = APP_NAME # target inside the .app bundle
61+
62+
executables = [
63+
Executable(
64+
script="app.py", # <-- Adjust if your entry point has a different name
65+
base=base,
66+
target_name=target_name,
67+
icon=str(icon) if icon.exists() else None,
68+
)
69+
]
70+
71+
# macOS bundle options for 'bdist_mac'
72+
bdist_mac_options = {
73+
"bundle_name": APP_NAME,
74+
# dmgbuild will handle the DMG; this is just the .app packaging
75+
"iconfile": str(icon) if icon.exists() else None,
76+
}
77+
78+
setup(
79+
name=APP_NAME,
80+
version=VERSION,
81+
description="PyQt5 tool for Anycubic NFC filament tags",
82+
options={
83+
"build_exe": build_exe_options,
84+
"bdist_mac": bdist_mac_options,
85+
},
86+
executables=executables,
87+
)

packaging/macos/app.icns

227 KB
Binary file not shown.

packaging/windows/app.ico

364 KB
Binary file not shown.

0 commit comments

Comments
 (0)