2121PyInstaller build script (Python version)
2222"""
2323
24+ import hashlib
2425import os
2526import shutil
2627import subprocess
2728import sys
2829from pathlib import Path
2930
3031
32+ def get_venv_base_dir ():
33+ """
34+ Get the base directory for virtual environments outside the project.
35+
36+ Returns:
37+ Path: Base directory path
38+ - Linux/macOS: ~/.cache/iotdb-ainode-build/
39+ - Windows: %LOCALAPPDATA%\\ iotdb-ainode-build\\
40+ """
41+ if sys .platform == "win32" :
42+ localappdata = os .environ .get ("LOCALAPPDATA" ) or os .environ .get ("APPDATA" , os .path .expanduser ("~" ))
43+ base_dir = Path (localappdata ) / "iotdb-ainode-build"
44+ else :
45+ base_dir = Path .home () / ".cache" / "iotdb-ainode-build"
46+
47+ return base_dir
48+
49+
50+ def get_project_venv_name (project_dir ):
51+ """
52+ Generate a unique virtual environment name based on the project directory path.
53+
54+ Uses MD5 hash of the absolute path to ensure uniqueness across different
55+ project locations while keeping the name readable.
56+
57+ Returns:
58+ str: Virtual environment name in format <project-name>-<hash>
59+ """
60+ project_path = str (project_dir .absolute ())
61+ path_hash = hashlib .md5 (project_path .encode ()).hexdigest ()[:8 ]
62+ project_name = project_dir .name
63+ return f"{ project_name } -{ path_hash } "
64+
65+
3166def setup_venv ():
32- """Create virtual environment if it doesn't exist"""
67+ """
68+ Create virtual environment outside the project directory.
69+
70+ The virtual environment is created in a platform-specific location:
71+ - Linux/macOS: ~/.cache/iotdb-ainode-build/<project-name>-<hash>/
72+ - Windows: %LOCALAPPDATA%\\ iotdb-ainode-build\\ <project-name>-<hash>\\
73+
74+ Returns:
75+ Path: Path to the virtual environment directory
76+ """
3377 script_dir = Path (__file__ ).parent
34- venv_dir = script_dir / ".venv"
35-
78+ venv_base_dir = get_venv_base_dir ()
79+ venv_name = get_project_venv_name (script_dir )
80+ venv_dir = venv_base_dir / venv_name
81+
3682 if venv_dir .exists ():
3783 print (f"Virtual environment already exists at: { venv_dir } " )
3884 return venv_dir
39-
85+
86+ venv_base_dir .mkdir (parents = True , exist_ok = True )
4087 print (f"Creating virtual environment at: { venv_dir } " )
4188 subprocess .run ([sys .executable , "-m" , "venv" , str (venv_dir )], check = True )
4289 print ("Virtual environment created successfully" )
@@ -52,12 +99,7 @@ def get_venv_python(venv_dir):
5299
53100
54101def update_pip (venv_python ):
55- """
56- Update pip in virtual environment.
57-
58- Note: subprocess.run() is synchronous and blocks until the subprocess completes.
59- This ensures pip upgrade finishes before the script continues.
60- """
102+ """Update pip in the virtual environment to the latest version."""
61103 print ("Updating pip..." )
62104 subprocess .run (
63105 [str (venv_python ), "-m" , "pip" , "install" , "--upgrade" , "pip" ], check = True
@@ -66,12 +108,7 @@ def update_pip(venv_python):
66108
67109
68110def install_poetry (venv_python ):
69- """
70- Install poetry 2.1.2 in virtual environment.
71-
72- Note: subprocess.run() is synchronous and blocks until the subprocess completes.
73- This ensures poetry installation finishes before the script continues.
74- """
111+ """Install poetry 2.1.2 in the virtual environment."""
75112 print ("Installing poetry 2.1.2..." )
76113 subprocess .run (
77114 [
@@ -87,46 +124,39 @@ def install_poetry(venv_python):
87124
88125
89126def get_venv_env (venv_dir ):
90- """Get environment variables for virtual environment activation"""
127+ """
128+ Get environment variables configured for the virtual environment.
129+
130+ Sets VIRTUAL_ENV and prepends the venv's bin/Scripts directory to PATH
131+ so that tools installed in the venv take precedence.
132+
133+ Returns:
134+ dict: Environment variables dictionary
135+ """
91136 env = os .environ .copy ()
92- venv_path = str (venv_dir .absolute ())
93- env ["VIRTUAL_ENV" ] = venv_path
94-
95- # Add venv bin directory to PATH
96- if sys .platform == "win32" :
97- venv_bin = str (venv_dir / "Scripts" )
98- else :
99- venv_bin = str (venv_dir / "bin" )
100-
101- # Prepend venv bin to PATH to ensure venv tools take precedence
137+ env ["VIRTUAL_ENV" ] = str (venv_dir .absolute ())
138+
139+ venv_bin = str (venv_dir / ("Scripts" if sys .platform == "win32" else "bin" ))
102140 env ["PATH" ] = f"{ venv_bin } { os .pathsep } { env .get ('PATH' , '' )} "
103-
141+
104142 return env
105143
106144
107145def install_dependencies (venv_python , venv_dir , script_dir ):
108146 """
109- Install dependencies using poetry.
110-
111- Note: subprocess.run() is synchronous and blocks until each command completes.
112- This ensures each step (poetry lock, install) finishes before proceeding.
113-
114- We configure poetry to use our .venv directory by:
115- 1. Configuring poetry to use in-project virtualenvs
116- 2. Setting poetry to use our .venv via poetry env use
117- 3. Running poetry lock and install which will use our .venv
147+ Install project dependencies using poetry.
148+
149+ Configures poetry to use the external virtual environment and installs
150+ all dependencies from pyproject.toml.
118151 """
119152 print ("Installing dependencies with poetry..." )
120-
121- # Get environment with VIRTUAL_ENV set
122153 venv_env = get_venv_env (venv_dir )
123154
124- # Configure poetry to use in-project virtualenvs
125- # This makes poetry create/use .venv in the project directory
126- print ("Configuring poetry to use in-project virtualenvs..." )
155+ # Configure poetry to use external virtual environments
156+ print ("Configuring poetry to use external virtual environments..." )
127157 try :
128158 subprocess .run (
129- ["poetry" , "config" , "virtualenvs.in-project" , "true " ],
159+ ["poetry" , "config" , "virtualenvs.in-project" , "false " ],
130160 cwd = str (script_dir ),
131161 env = venv_env ,
132162 check = True ,
@@ -136,32 +166,26 @@ def install_dependencies(venv_python, venv_dir, script_dir):
136166 except Exception :
137167 pass # Configuration may already be set
138168
139- # Configure poetry to use our existing virtual environment
140- # This links poetry's management to our .venv directory
169+ # Link poetry to the existing virtual environment
141170 print (f"Configuring poetry to use virtual environment at: { venv_dir } " )
142171 result = subprocess .run (
143172 ["poetry" , "env" , "use" , str (venv_python )],
144173 cwd = str (script_dir ),
145174 env = venv_env ,
146- check = False , # Don't fail if venv is already configured
175+ check = False ,
147176 capture_output = True ,
148177 text = True ,
149178 )
150179
151- # Check output - if poetry tries to recreate venv, that's okay as it will use our path
152180 if result .stdout :
153- output = result .stdout .strip ()
154- print (output )
181+ print (result .stdout .strip ())
155182 if result .stderr :
156183 stderr = result .stderr .strip ()
157- # Ignore warnings about venv already existing or being created
158- if (
159- "already been activated" not in stderr .lower ()
160- and "already in use" not in stderr .lower ()
161- ):
184+ # Only print non-benign errors
185+ if "already been activated" not in stderr .lower () and "already in use" not in stderr .lower ():
162186 print (stderr )
163187
164- # Run poetry lock
188+ # Update lock file and install dependencies
165189 print ("Running poetry lock..." )
166190 result = subprocess .run (
167191 ["poetry" , "lock" ],
@@ -176,8 +200,6 @@ def install_dependencies(venv_python, venv_dir, script_dir):
176200 if result .stderr :
177201 print (result .stderr )
178202
179- # Run poetry install
180- # With VIRTUAL_ENV set and poetry env use configured, this should install into our .venv
181203 print ("Running poetry install..." )
182204 result = subprocess .run (
183205 ["poetry" , "install" ],
@@ -218,23 +240,24 @@ def check_pyinstaller(venv_python):
218240
219241
220242def build ():
221- """Execute build process"""
243+ """
244+ Execute the complete build process.
245+
246+ Steps:
247+ 1. Setup virtual environment (outside project directory)
248+ 2. Update pip and install poetry 2.1.2
249+ 3. Install project dependencies
250+ 4. Build executable using PyInstaller
251+ """
222252 script_dir = Path (__file__ ).parent
223253
224- # Setup virtual environment
225254 venv_dir = setup_venv ()
226255 venv_python = get_venv_python (venv_dir )
227256
228- # Update pip
229257 update_pip (venv_python )
230-
231- # Install poetry 2.1.2
232258 install_poetry (venv_python )
233-
234- # Install dependencies
235259 install_dependencies (venv_python , venv_dir , script_dir )
236260
237- # Check PyInstaller
238261 if not check_pyinstaller (venv_python ):
239262 sys .exit (1 )
240263
@@ -243,7 +266,6 @@ def build():
243266 print ("=" * 50 )
244267 print ()
245268
246- # Execute build (incremental build - no cleanup for faster rebuilds)
247269 print ("Starting build..." )
248270 print ()
249271
0 commit comments