Skip to content

Commit 5d37b07

Browse files
authored
force explicit install penv (#241)
1 parent cb2cd7b commit 5d37b07

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
# Check Python version requirement
3940
if sys.version_info < (3, 10):
@@ -65,6 +66,68 @@
6566
# Framework directory path
6667
FRAMEWORK_DIR = platform.get_package_dir("framework-arduinoespressif32")
6768

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

69132
def add_to_pythonpath(path):
70133
"""
@@ -89,14 +152,10 @@ def add_to_pythonpath(path):
89152
if normalized_path not in sys.path:
90153
sys.path.insert(0, normalized_path)
91154

92-
93155
def setup_python_paths():
94156
"""
95157
Setup Python paths based on the actual Python executable being used.
96-
"""
97-
if not PYTHON_EXE or not os.path.isfile(PYTHON_EXE):
98-
return
99-
158+
"""
100159
# Get the directory containing the Python executable
101160
python_dir = os.path.dirname(PYTHON_EXE)
102161
add_to_pythonpath(python_dir)
@@ -116,7 +175,6 @@ def setup_python_paths():
116175
# Setup Python paths based on the actual Python executable
117176
setup_python_paths()
118177

119-
120178
def _get_executable_path(python_exe, executable_name):
121179
"""
122180
Get the path to an executable binary (esptool, uv, etc.) based on the Python executable path.
@@ -128,14 +186,11 @@ def _get_executable_path(python_exe, executable_name):
128186
Returns:
129187
str: Path to executable or fallback to executable name
130188
"""
131-
if not python_exe or not os.path.isfile(python_exe):
132-
return executable_name # Fallback to command name
133189

134190
python_dir = os.path.dirname(python_exe)
135191

136-
if sys.platform == "win32":
137-
scripts_dir = os.path.join(python_dir, "Scripts")
138-
executable_path = os.path.join(scripts_dir, f"{executable_name}.exe")
192+
if IS_WINDOWS:
193+
executable_path = os.path.join(python_dir, f"{executable_name}.exe")
139194
else:
140195
# For Unix-like systems, executables are typically in the same directory as python
141196
# or in a bin subdirectory
@@ -237,7 +292,7 @@ def install_python_deps():
237292
uv_executable = _get_uv_executable_path(PYTHON_EXE)
238293

239294
# Add Scripts directory to PATH for Windows
240-
if sys.platform == "win32":
295+
if IS_WINDOWS:
241296
python_dir = os.path.dirname(PYTHON_EXE)
242297
scripts_dir = os.path.join(python_dir, "Scripts")
243298
if os.path.isdir(scripts_dir):
@@ -375,8 +430,10 @@ def install_esptool():
375430
return 'esptool' # Fallback
376431

377432

378-
# Install Python dependencies and esptool
433+
# Install Python dependencies
379434
install_python_deps()
435+
436+
# Install esptool after dependencies
380437
esptool_binary_path = install_esptool()
381438

382439

@@ -765,7 +822,6 @@ def switch_off_ldf():
765822
if ' ' in esptool_binary_path
766823
else esptool_binary_path
767824
)
768-
769825
# Configure build tools and environment variables
770826
env.Replace(
771827
__get_board_boot_mode=_get_board_boot_mode,

0 commit comments

Comments
 (0)