77import shutil
88import platform
99from pathlib import Path
10- from typing import Dict , List , Tuple
10+ import subprocess
1111
1212def clean_build ():
1313 """Clean previous build artifacts"""
@@ -24,92 +24,68 @@ def check_environment() -> bool:
2424 if sys .version_info >= (3 , 12 ):
2525 print ("Warning: Python 3.12+ might have compatibility issues. Using 3.9-3.11 is recommended." )
2626
27- required_modules = ['PyInstaller' , 'Flask' , 'click' , 'scapy' ]
28- missing_modules = []
29-
30- for module in required_modules :
31- try :
32- __import__ (module )
33- except ImportError :
34- missing_modules .append (module )
35-
36- if missing_modules :
37- print ("Missing required modules:" )
38- for module in missing_modules :
39- print (f" - { module } " )
27+ try :
28+ import PyInstaller
29+ return True
30+ except ImportError :
31+ print ("\n PyInstaller not found. Please install build requirements:" )
32+ print ("pip install -r requirements-build.txt" )
4033 return False
41-
42- return True
4334
44- def get_platform_settings () -> Tuple [ str , Dict ] :
35+ def get_platform_settings ():
4536 """Get platform-specific build settings"""
4637 system = platform .system ().lower ()
4738
4839 # Base settings common to all platforms
49- base_settings = {
40+ settings = {
5041 'name' : 'NetworkMonitor' ,
5142 'console' : True ,
5243 'debug' : False ,
5344 'noconfirm' : True ,
54- 'strip' : True ,
55- 'clean' : True
45+ 'clean' : True ,
46+ 'icon_path' : None ,
47+ 'hiddenimports' : []
5648 }
5749
5850 # Platform-specific settings
5951 if system == 'windows' :
60- icon_file = 'assets/icon.ico'
61- base_settings . update ({
52+ settings . update ({
53+ 'icon_path' : 'assets/icon.ico' ,
6254 'uac_admin' : True ,
63- 'win_private_assemblies' : True ,
64- 'win_no_prefer_redirects' : True ,
6555 'hiddenimports' : ['win32com' , 'win32com.shell' , 'win32api' , 'wmi' ]
6656 })
6757 elif system == 'darwin' :
68- icon_file = 'assets/icon.icns'
69- base_settings ['hiddenimports' ] = ['pkg_resources.py2_warn' ]
70- else : # Linux
71- icon_file = 'assets/icon.ico' # Use .ico for Linux as well
72- base_settings ['hiddenimports' ] = ['pkg_resources.py2_warn' ]
58+ settings ['icon_path' ] = 'assets/icon.icns'
59+ elif system == 'linux' :
60+ settings ['icon_path' ] = 'assets/icon.ico'
7361
74- return icon_file , base_settings
62+ return settings
7563
76- def create_spec_file () -> bool :
64+ def create_spec_file (settings ) -> bool :
7765 """Create PyInstaller spec file based on platform"""
7866 try :
79- # Get platform-specific settings
80- icon_file , settings = get_platform_settings ()
81-
82- if not os .path .exists (icon_file ):
83- print (f"Warning: Icon file not found at { icon_file } " )
84- icon_file = None
85-
8667 # Convert settings to spec file content
87- spec_content = """# -*- mode: python ; coding: utf-8 -*-
68+ spec_content = f """# -*- mode: python ; coding: utf-8 -*-
8869
89- from PyInstaller.building.api import PYZ, EXE, COLLECT
90- from PyInstaller.building.build_main import Analysis
91-
92- datas = [
93- ('assets/*', 'assets'),
94- ('networkmonitor/web/build/*', 'web/build')
95- ]
70+ block_cipher = None
9671
9772a = Analysis(
9873 ['networkmonitor/scripts/networkmonitor_cli.py'],
9974 pathex=[],
10075 binaries=[],
101- datas=datas,
102- hiddenimports={hiddenimports},
76+ datas=[
77+ ('assets/*', 'assets')
78+ ],
79+ hiddenimports={ settings ['hiddenimports' ]} ,
10380 hookspath=[],
10481 hooksconfig={{}},
10582 runtime_hooks=[],
10683 excludes=[],
107- win_no_prefer_redirects={win_no_prefer_redirects},
108- win_private_assemblies={win_private_assemblies},
109- noarchive=False,
84+ cipher=block_cipher,
85+ noarchive=False
11086)
11187
112- pyz = PYZ(a.pure, a.zipped_data)
88+ pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher )
11389
11490exe = EXE(
11591 pyz,
@@ -118,37 +94,29 @@ def create_spec_file() -> bool:
11894 a.zipfiles,
11995 a.datas,
12096 [],
121- name='{name} ',
122- debug={debug} ,
123- strip={strip} ,
124- upx=True ,
97+ name='NetworkMonitor ',
98+ debug=False ,
99+ bootloader_ignore_signals=False ,
100+ upx=False ,
125101 runtime_tmpdir=None,
126- console={console} ,
102+ console=True ,
127103 disable_windowed_traceback=False,
128104 argv_emulation=False,
129105 target_arch=None,
130106 codesign_identity=None,
131- entitlements_file=None,""" .format (
132- name = settings ['name' ],
133- hiddenimports = settings .get ('hiddenimports' , []),
134- win_no_prefer_redirects = settings .get ('win_no_prefer_redirects' , False ),
135- win_private_assemblies = settings .get ('win_private_assemblies' , False ),
136- debug = settings ['debug' ],
137- strip = settings ['strip' ],
138- console = settings ['console' ]
139- )
140-
107+ entitlements_file=None"""
108+
141109 # Add icon if available
142- if icon_file :
143- spec_content += f"\n icon=['{ icon_file } '], "
110+ if settings . get ( 'icon_path' ) :
111+ spec_content += f", \n icon=['{ settings [ 'icon_path' ] } ']"
144112
145113 spec_content += "\n )"
146114
147115 # Write spec file
148116 with open ('NetworkMonitor.spec' , 'w' , encoding = 'utf-8' ) as f :
149117 f .write (spec_content )
150118
151- print ("Generated NetworkMonitor.spec file\n " )
119+ print ("Generated NetworkMonitor.spec file with platform-specific settings \n " )
152120 return True
153121
154122 except Exception as e :
@@ -158,26 +126,34 @@ def create_spec_file() -> bool:
158126def build_executable () -> bool :
159127 """Build the executable using PyInstaller"""
160128 try :
161- import PyInstaller .__main__
162-
163129 # Clean previous build
164130 clean_build ()
165131
132+ # Get platform-specific settings
133+ settings = get_platform_settings ()
134+
166135 # Create spec file
167- if not create_spec_file ():
136+ if not create_spec_file (settings ):
168137 return False
169-
170- # Build with optimized settings
138+
171139 print ("Building executable with optimized settings..." )
172- PyInstaller .__main__ .run ([
140+ pyinstaller_args = [
141+ sys .executable ,
142+ '-m' ,
143+ 'PyInstaller' ,
173144 'NetworkMonitor.spec' ,
174- '--noconfirm' ,
175- '--clean' ,
176- '--strip'
177- ])
178-
179- return True
145+ '--noconfirm'
146+ ]
147+
148+ result = subprocess .run (pyinstaller_args , check = True )
149+ return result .returncode == 0
180150
151+ except subprocess .CalledProcessError as e :
152+ print (f"PyInstaller build failed with return code { e .returncode } " )
153+ if hasattr (e , 'stderr' ) and e .stderr :
154+ print ("Error output:" )
155+ print (e .stderr .decode () if isinstance (e .stderr , bytes ) else e .stderr )
156+ return False
181157 except Exception as e :
182158 print (f"Error during build: { e } " )
183159 print ("\n Troubleshooting tips:" )
@@ -189,6 +165,7 @@ def build_executable() -> bool:
189165
190166if __name__ == '__main__' :
191167 try :
168+ print ("Building NetworkMonitor executable..." )
192169 if not check_environment ():
193170 sys .exit (1 )
194171
0 commit comments