Skip to content

Commit f704a61

Browse files
authored
force explicit install penv
1 parent 1d14f77 commit f704a61

File tree

1 file changed

+70
-14
lines changed

1 file changed

+70
-14
lines changed

builder/main.py

Lines changed: 70 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
from platformio.project.helpers import get_project_dir
3535
from platformio.package.version import pepver_to_semver
3636
from platformio.util import get_serial_ports
37+
from platformio.compat import IS_WINDOWS
3738

3839
# Python dependencies required for the build process
3940
python_deps = {
@@ -56,6 +57,68 @@
5657
# Framework directory path
5758
FRAMEWORK_DIR = platform.get_package_dir("framework-arduinoespressif32")
5859

60+
platformio_dir = projectconfig.get("platformio", "core_dir")
61+
penv_dir = os.path.join(platformio_dir, "penv")
62+
63+
pip_path = os.path.join(
64+
penv_dir,
65+
"Scripts" if IS_WINDOWS else "bin",
66+
"pip" + (".exe" if IS_WINDOWS else ""),
67+
)
68+
69+
def setup_pipenv_in_package():
70+
"""
71+
Checks if 'penv' folder exists in platformio dir and creates virtual environment if not.
72+
"""
73+
if not os.path.exists(penv_dir):
74+
env.Execute(
75+
env.VerboseAction(
76+
'"$PYTHONEXE" -m venv --clear "%s"' % penv_dir,
77+
"Creating a new virtual environment for Python dependencies",
78+
)
79+
)
80+
81+
assert os.path.isfile(
82+
pip_path
83+
), "Error: Failed to create a proper virtual environment. Missing the `pip` binary!"
84+
85+
penv_python = os.path.join(penv_dir, "Scripts", "python.exe") if IS_WINDOWS else os.path.join(penv_dir, "bin", "python")
86+
env.Replace(PYTHONEXE=penv_python)
87+
print(f"PYTHONEXE updated to penv environment: {penv_python}")
88+
89+
setup_pipenv_in_package()
90+
# Update global PYTHON_EXE variable after potential pipenv setup
91+
PYTHON_EXE = env.subst("$PYTHONEXE")
92+
python_exe = PYTHON_EXE
93+
94+
# Ensure penv Python directory is in PATH for subprocess calls
95+
python_dir = os.path.dirname(PYTHON_EXE)
96+
current_path = os.environ.get("PATH", "")
97+
if python_dir not in current_path:
98+
os.environ["PATH"] = python_dir + os.pathsep + current_path
99+
100+
# Verify the Python executable exists
101+
assert os.path.isfile(PYTHON_EXE), f"Python executable not found: {PYTHON_EXE}"
102+
103+
if os.path.isfile(python_exe):
104+
# Update sys.path to include penv site-packages
105+
if IS_WINDOWS:
106+
penv_site_packages = os.path.join(penv_dir, "Lib", "site-packages")
107+
else:
108+
# Find the actual site-packages directory in the venv
109+
penv_lib_dir = os.path.join(penv_dir, "lib")
110+
if os.path.isdir(penv_lib_dir):
111+
for python_dir in os.listdir(penv_lib_dir):
112+
if python_dir.startswith("python"):
113+
penv_site_packages = os.path.join(penv_lib_dir, python_dir, "site-packages")
114+
break
115+
else:
116+
penv_site_packages = None
117+
else:
118+
penv_site_packages = None
119+
120+
if penv_site_packages and os.path.isdir(penv_site_packages) and penv_site_packages not in sys.path:
121+
sys.path.insert(0, penv_site_packages)
59122

60123
def add_to_pythonpath(path):
61124
"""
@@ -80,14 +143,10 @@ def add_to_pythonpath(path):
80143
if normalized_path not in sys.path:
81144
sys.path.insert(0, normalized_path)
82145

83-
84146
def setup_python_paths():
85147
"""
86148
Setup Python paths based on the actual Python executable being used.
87-
"""
88-
if not PYTHON_EXE or not os.path.isfile(PYTHON_EXE):
89-
return
90-
149+
"""
91150
# Get the directory containing the Python executable
92151
python_dir = os.path.dirname(PYTHON_EXE)
93152
add_to_pythonpath(python_dir)
@@ -107,7 +166,6 @@ def setup_python_paths():
107166
# Setup Python paths based on the actual Python executable
108167
setup_python_paths()
109168

110-
111169
def _get_executable_path(python_exe, executable_name):
112170
"""
113171
Get the path to an executable binary (esptool, uv, etc.) based on the Python executable path.
@@ -119,14 +177,11 @@ def _get_executable_path(python_exe, executable_name):
119177
Returns:
120178
str: Path to executable or fallback to executable name
121179
"""
122-
if not python_exe or not os.path.isfile(python_exe):
123-
return executable_name # Fallback to command name
124180

125181
python_dir = os.path.dirname(python_exe)
126182

127-
if sys.platform == "win32":
128-
scripts_dir = os.path.join(python_dir, "Scripts")
129-
executable_path = os.path.join(scripts_dir, f"{executable_name}.exe")
183+
if IS_WINDOWS:
184+
executable_path = os.path.join(python_dir, f"{executable_name}.exe")
130185
else:
131186
# For Unix-like systems, executables are typically in the same directory as python
132187
# or in a bin subdirectory
@@ -228,7 +283,7 @@ def install_python_deps():
228283
uv_executable = _get_uv_executable_path(PYTHON_EXE)
229284

230285
# Add Scripts directory to PATH for Windows
231-
if sys.platform == "win32":
286+
if IS_WINDOWS:
232287
python_dir = os.path.dirname(PYTHON_EXE)
233288
scripts_dir = os.path.join(python_dir, "Scripts")
234289
if os.path.isdir(scripts_dir):
@@ -366,8 +421,10 @@ def install_esptool():
366421
return 'esptool' # Fallback
367422

368423

369-
# Install Python dependencies and esptool
424+
# Install Python dependencies
370425
install_python_deps()
426+
427+
# Install esptool after dependencies
371428
esptool_binary_path = install_esptool()
372429

373430

@@ -756,7 +813,6 @@ def switch_off_ldf():
756813
if ' ' in esptool_binary_path
757814
else esptool_binary_path
758815
)
759-
760816
# Configure build tools and environment variables
761817
env.Replace(
762818
__get_board_boot_mode=_get_board_boot_mode,

0 commit comments

Comments
 (0)