@@ -10,6 +10,59 @@ from pathlib import Path
1010import aw_core
1111import flask_restx
1212
13+
14+ def build_analysis (name , location , binaries = [], datas = [], hiddenimports = []):
15+ name_py = name .replace ("-" , "_" )
16+ location_candidates = [
17+ location / f"{ name_py } /__main__.py" ,
18+ location / f"src/{ name_py } /__main__.py" ,
19+ ]
20+ try :
21+ location = next (p for p in location_candidates if p .exists ())
22+ except StopIteration :
23+ raise Exception (f"Could not find { name } location from { location_candidates } " )
24+
25+ return Analysis (
26+ [location ],
27+ pathex = [],
28+ binaries = binaries ,
29+ datas = datas ,
30+ hiddenimports = hiddenimports ,
31+ hookspath = [],
32+ runtime_hooks = [],
33+ excludes = [],
34+ win_no_prefer_redirects = False ,
35+ win_private_assemblies = False ,
36+ )
37+
38+
39+ def build_collect (analysis , name , console = True ):
40+ """Used to build the COLLECT statements for each module"""
41+ pyz = PYZ (analysis .pure , analysis .zipped_data )
42+ exe = EXE (
43+ pyz ,
44+ analysis .scripts ,
45+ exclude_binaries = True ,
46+ name = name ,
47+ debug = False ,
48+ strip = False ,
49+ upx = True ,
50+ console = console ,
51+ entitlements_file = entitlements_file ,
52+ codesign_identity = codesign_identity ,
53+ )
54+ return COLLECT (
55+ exe ,
56+ analysis .binaries ,
57+ analysis .zipfiles ,
58+ analysis .datas ,
59+ strip = False ,
60+ upx = True ,
61+ name = name ,
62+ )
63+
64+
65+ # Get the current release version
1366current_release = subprocess .run (
1467 shlex .split ("git describe --tags --abbrev=0" ),
1568 stdout = subprocess .PIPE ,
@@ -18,6 +71,7 @@ current_release = subprocess.run(
1871).stdout .strip ()
1972print ("bundling activitywatch version " + current_release )
2073
74+ # Get entitlements and codesign identity
2175entitlements_file = Path ("." ) / "scripts" / "package" / "entitlements.plist"
2276codesign_identity = os .environ .get ("APPLE_PERSONALID" , "" ).strip ()
2377if not codesign_identity :
@@ -32,69 +86,42 @@ aw_server_rust_bin = aw_server_rust_location / "target/package/aw-server-rust"
3286aw_qt_location = Path ("aw-qt" )
3387awa_location = Path ("aw-watcher-afk" )
3488aww_location = Path ("aw-watcher-window" )
89+ awi_location = Path ("aw-watcher-input" )
90+ aw_notify_location = Path ("aw-notify" )
3591
3692if platform .system () == "Darwin" :
3793 icon = aw_qt_location / "media/logo/logo.icns"
3894else :
3995 icon = aw_qt_location / "media/logo/logo.ico"
40- block_cipher = None
41-
42- extra_pathex = []
43- if platform .system () == "Windows" :
44- # The Windows version includes paths to Qt binaries which are
45- # not automatically found due to bug in PyInstaller 3.2.
46- # See: https://github.com/pyinstaller/pyinstaller/issues/2152
47- import PyQt5
48-
49- pyqt_path = os .path .dirname (PyQt5 .__file__ )
50- extra_pathex .append (pyqt_path + "\\ Qt\\ bin" )
5196
5297skip_rust = False
5398if not aw_server_rust_bin .exists ():
5499 skip_rust = True
55100 print ("Skipping Rust build because aw-server-rust binary not found." )
56101
57- aw_server_a = Analysis (
58- ["aw-server/__main__.py" ],
59- pathex = [],
60- binaries = None ,
61- datas = [
62- (aws_location / "aw_server/static" , "aw_server/static" ),
63- (restx_path / "templates" , "flask_restx/templates" ),
64- (restx_path / "static" , "flask_restx/static" ),
65- (aw_core_path / "schemas" , "aw_core/schemas" ),
66- ],
67- hiddenimports = [],
68- hookspath = [],
69- runtime_hooks = [],
70- excludes = [],
71- win_no_prefer_redirects = False ,
72- win_private_assemblies = False ,
73- cipher = block_cipher ,
74- )
75102
76- aw_qt_a = Analysis (
77- [ aw_qt_location / "aw_qt/__main__.py" ] ,
78- pathex = [] + extra_pathex ,
103+ aw_qt_a = build_analysis (
104+ "aw-qt" ,
105+ aw_qt_location ,
79106 binaries = [(aw_server_rust_bin , "." )] if not skip_rust else [],
80107 datas = [
81108 (aw_qt_location / "resources/aw-qt.desktop" , "aw_qt/resources" ),
82109 (aw_qt_location / "media" , "aw_qt/media" ),
83110 ],
84- hiddenimports = [],
85- hookspath = [],
86- runtime_hooks = [],
87- excludes = [],
88- win_no_prefer_redirects = False ,
89- win_private_assemblies = False ,
90- cipher = block_cipher ,
91111)
92-
93- aw_watcher_afk_a = Analysis (
94- [awa_location / "aw_watcher_afk/__main__.py" ],
95- pathex = [],
96- binaries = None ,
97- datas = None ,
112+ aw_server_a = build_analysis (
113+ "aw-server" ,
114+ aws_location ,
115+ datas = [
116+ (aws_location / "aw_server/static" , "aw_server/static" ),
117+ (restx_path / "templates" , "flask_restx/templates" ),
118+ (restx_path / "static" , "flask_restx/static" ),
119+ (aw_core_path / "schemas" , "aw_core/schemas" ),
120+ ],
121+ )
122+ aw_watcher_afk_a = build_analysis (
123+ "aw_watcher_afk" ,
124+ awa_location ,
98125 hiddenimports = [
99126 "Xlib.keysymdef.miscellany" ,
100127 "Xlib.keysymdef.latin1" ,
@@ -117,17 +144,11 @@ aw_watcher_afk_a = Analysis(
117144 "pynput.keyboard._darwin" ,
118145 "pynput.mouse._darwin" ,
119146 ],
120- hookspath = [],
121- runtime_hooks = [],
122- excludes = [],
123- win_no_prefer_redirects = False ,
124- win_private_assemblies = False ,
125- cipher = block_cipher ,
126147)
127-
128- aw_watcher_window_a = Analysis (
129- [ aww_location / "aw_watcher_window/__main__.py" ] ,
130- pathex = [] ,
148+ aw_watcher_input_a = build_analysis ( "aw_watcher_input" , awi_location )
149+ aw_watcher_window_a = build_analysis (
150+ "aw_watcher_window" ,
151+ aww_location ,
131152 binaries = [
132153 (
133154 aww_location / "aw_watcher_window/aw-watcher-window-macos" ,
@@ -139,13 +160,9 @@ aw_watcher_window_a = Analysis(
139160 datas = [
140161 (aww_location / "aw_watcher_window/printAppStatus.jxa" , "aw_watcher_window" )
141162 ],
142- hiddenimports = [],
143- hookspath = [],
144- runtime_hooks = [],
145- excludes = [],
146- win_no_prefer_redirects = False ,
147- win_private_assemblies = False ,
148- cipher = block_cipher ,
163+ )
164+ aw_notify_a = build_analysis (
165+ "aw_notify" , aw_notify_location , hiddenimports = ["desktop_notifier.resources" ]
149166)
150167
151168# https://pythonhosted.org/PyInstaller/spec-files.html#multipackage-bundles
@@ -156,110 +173,40 @@ MERGE(
156173 (aw_qt_a , "aw-qt" , "aw-qt" ),
157174 (aw_watcher_afk_a , "aw-watcher-afk" , "aw-watcher-afk" ),
158175 (aw_watcher_window_a , "aw-watcher-window" , "aw-watcher-window" ),
176+ (aw_watcher_input_a , "aw-watcher-input" , "aw-watcher-input" ),
177+ (aw_notify_a , "aw-notify" , "aw-notify" ),
159178)
160179
161- aww_pyz = PYZ (
162- aw_watcher_window_a .pure , aw_watcher_window_a .zipped_data , cipher = block_cipher
163- )
164- aww_exe = EXE (
165- aww_pyz ,
166- aw_watcher_window_a .scripts ,
167- exclude_binaries = True ,
168- name = "aw-watcher-window" ,
169- debug = False ,
170- strip = False ,
171- upx = True ,
172- console = True ,
173- entitlements_file = entitlements_file ,
174- codesign_identity = codesign_identity ,
175- )
176- aww_coll = COLLECT (
177- aww_exe ,
178- aw_watcher_window_a .binaries ,
179- aw_watcher_window_a .zipfiles ,
180- aw_watcher_window_a .datas ,
181- strip = False ,
182- upx = True ,
183- name = "aw-watcher-window" ,
184- )
185180
186- awa_pyz = PYZ (aw_watcher_afk_a .pure , aw_watcher_afk_a .zipped_data , cipher = block_cipher )
187- awa_exe = EXE (
188- awa_pyz ,
189- aw_watcher_afk_a .scripts ,
190- exclude_binaries = True ,
191- name = "aw-watcher-afk" ,
192- debug = False ,
193- strip = False ,
194- upx = True ,
195- console = True ,
196- entitlements_file = entitlements_file ,
197- codesign_identity = codesign_identity ,
198- )
199- awa_coll = COLLECT (
200- awa_exe ,
201- aw_watcher_afk_a .binaries ,
202- aw_watcher_afk_a .zipfiles ,
203- aw_watcher_afk_a .datas ,
204- strip = False ,
205- upx = True ,
206- name = "aw-watcher-afk" ,
207- )
181+ # aw-server
182+ aws_coll = build_collect (aw_server_a , "aw-server" )
208183
209- aws_pyz = PYZ (aw_server_a .pure , aw_server_a .zipped_data , cipher = block_cipher )
184+ # aw-watcher-window
185+ aww_coll = build_collect (aw_watcher_window_a , "aw-watcher-window" )
210186
211- aws_exe = EXE (
212- aws_pyz ,
213- aw_server_a .scripts ,
214- exclude_binaries = True ,
215- name = "aw-server" ,
216- debug = False ,
217- strip = False ,
218- upx = True ,
219- console = True ,
220- entitlements_file = entitlements_file ,
221- codesign_identity = codesign_identity ,
222- )
223- aws_coll = COLLECT (
224- aws_exe ,
225- aw_server_a .binaries ,
226- aw_server_a .zipfiles ,
227- aw_server_a .datas ,
228- strip = False ,
229- upx = True ,
230- name = "aw-server" ,
231- )
187+ # aw-watcher-afk
188+ awa_coll = build_collect (aw_watcher_afk_a , "aw-watcher-afk" )
232189
233- awq_pyz = PYZ (aw_qt_a .pure , aw_qt_a .zipped_data , cipher = block_cipher )
234- awq_exe = EXE (
235- awq_pyz ,
236- aw_qt_a .scripts ,
237- exclude_binaries = True ,
238- name = "aw-qt" ,
239- debug = True ,
240- strip = False ,
241- upx = True ,
242- icon = icon ,
190+ # aw-qt
191+ awq_coll = build_collect (
192+ aw_qt_a ,
193+ "aw-qt" ,
243194 console = False if platform .system () == "Windows" else True ,
244- entitlements_file = entitlements_file ,
245- codesign_identity = codesign_identity ,
246- )
247- awq_coll = COLLECT (
248- awq_exe ,
249- aw_qt_a .binaries ,
250- aw_qt_a .zipfiles ,
251- aw_qt_a .datas ,
252- strip = False ,
253- upx = True ,
254- name = "aw-qt" ,
255195)
256196
197+ # aw-watcher-input
198+ awi_coll = build_collect (aw_watcher_input_a , "aw-watcher-input" )
199+
200+ aw_notify_coll = build_collect (aw_notify_a , "aw-notify" )
201+
257202if platform .system () == "Darwin" :
258203 app = BUNDLE (
259204 awq_coll ,
205+ aws_coll ,
260206 aww_coll ,
261207 awa_coll ,
262- aws_coll ,
208+ awi_coll ,
209+ aw_notify_coll ,
263210 name = "ActivityWatch.app" ,
264211 icon = icon ,
265212 bundle_identifier = "net.activitywatch.ActivityWatch" ,
0 commit comments