2121"""
2222
2323import copy
24+ import importlib .util
2425import json
2526import subprocess
2627import sys
4445from platformio .compat import IS_WINDOWS
4546from platformio .proc import exec_command
4647from platformio .builder .tools .piolib import ProjectAsLibBuilder
47- from platformio .project .config import ProjectConfig
4848from platformio .package .version import get_original_version , pepver_to_semver
4949
5050
5151env = DefaultEnvironment ()
5252env .SConscript ("_embed_files.py" , exports = "env" )
53+ platform = env .PioPlatform ()
54+
55+ _penv_setup_file = os .path .join (platform .get_dir (), "builder" , "penv_setup.py" )
56+ _spec = importlib .util .spec_from_file_location ("penv_setup" , _penv_setup_file )
57+ _penv_setup = importlib .util .module_from_spec (_spec )
58+ _spec .loader .exec_module (_penv_setup ) # type: ignore[attr-defined]
59+ get_executable_path = _penv_setup .get_executable_path
5360
5461# remove maybe existing old map file in project root
5562map_file = os .path .join (env .subst ("$PROJECT_DIR" ), env .subst ("$PROGNAME" ) + ".map" )
5966# Allow changes in folders of managed components
6067os .environ ["IDF_COMPONENT_OVERWRITE_MANAGED_COMPONENTS" ] = "1"
6168
62- platform = env .PioPlatform ()
6369config = env .GetProjectConfig ()
6470board = env .BoardConfig ()
6571mcu = board .get ("build.mcu" , "esp32" )
8389 if mcu in ("esp32" , "esp32s2" , "esp32s3" )
8490 else "toolchain-riscv32-esp"
8591)
86-
92+ PLATFORMIO_DIR = env . subst ( "$PROJECT_CORE_DIR" )
8793
8894assert os .path .isdir (FRAMEWORK_DIR )
8995assert os .path .isdir (TOOLCHAIN_DIR )
9096
97+
9198def create_silent_action (action_func ):
9299 """Create a silent SCons action that suppresses output"""
93100 silent_action = env .Action (action_func )
@@ -351,7 +358,7 @@ def HandleCOMPONENTsettings(env):
351358
352359if flag_custom_sdkonfig == True and "arduino" in env .subst ("$PIOFRAMEWORK" ) and "espidf" not in env .subst ("$PIOFRAMEWORK" ):
353360 HandleArduinoIDFsettings (env )
354- LIB_SOURCE = os .path .join (ProjectConfig . get_instance (). get ( "platformio" , "platforms_dir" ), "espressif32" , "builder" , "build_lib" )
361+ LIB_SOURCE = os .path .join (platform . get_dir () , "builder" , "build_lib" )
355362 if not bool (os .path .exists (os .path .join (PROJECT_DIR , ".dummy" ))):
356363 shutil .copytree (LIB_SOURCE , os .path .join (PROJECT_DIR , ".dummy" ))
357364 PROJECT_SRC_DIR = os .path .join (PROJECT_DIR , ".dummy" )
@@ -1488,9 +1495,13 @@ def generate_mbedtls_bundle(sdk_config):
14881495 )
14891496
14901497
1498+ def _get_uv_exe ():
1499+ return get_executable_path (os .path .join (PLATFORMIO_DIR , "penv" ), "uv" )
1500+
1501+
14911502def install_python_deps ():
1492- PYTHON_EXE = env . subst ( "$PYTHONEXE" )
1493- UV_EXE = os . path . join ( os . path . dirname ( PYTHON_EXE ), "uv" + ( ".exe" if IS_WINDOWS else "" ))
1503+ UV_EXE = _get_uv_exe ( )
1504+
14941505 def _get_installed_uv_packages (python_exe_path ):
14951506 result = {}
14961507 try :
@@ -1512,7 +1523,6 @@ def _get_installed_uv_packages(python_exe_path):
15121523 return
15131524
15141525 deps = {
1515- "uv" : ">=0.1.0" ,
15161526 # https://github.com/platformio/platformio-core/issues/4614
15171527 "urllib3" : "<2" ,
15181528 # https://github.com/platformio/platform-espressif32/issues/635
@@ -1563,9 +1573,7 @@ def get_idf_venv_dir():
15631573 # as an IDF component requires a different version of the IDF package and
15641574 # hence a different set of Python deps or their versions
15651575 idf_version = get_framework_version ()
1566- return os .path .join (
1567- env .subst ("$PROJECT_CORE_DIR" ), "penv" , ".espidf-" + idf_version
1568- )
1576+ return os .path .join (PLATFORMIO_DIR , "penv" , f".espidf-{ idf_version } " )
15691577
15701578
15711579def ensure_python_venv_available ():
@@ -1607,11 +1615,7 @@ def _is_venv_outdated(venv_data_file):
16071615 return True
16081616
16091617 def _create_venv (venv_dir ):
1610- pip_path = os .path .join (
1611- venv_dir ,
1612- "Scripts" if IS_WINDOWS else "bin" ,
1613- "pip" + (".exe" if IS_WINDOWS else "" ),
1614- )
1618+ uv_path = _get_uv_exe ()
16151619
16161620 if os .path .isdir (venv_dir ):
16171621 try :
@@ -1624,17 +1628,20 @@ def _create_venv(venv_dir):
16241628 )
16251629 env .Exit (1 )
16261630
1627- # Use the built-in PlatformIO Python to create a standalone IDF virtual env
1631+ # Use uv to create a standalone IDF virtual env
16281632 env .Execute (
16291633 env .VerboseAction (
1630- '"$PYTHONEXE" -m venv --clear "%s"' % venv_dir ,
1631- "Creating a new virtual environment for IDF Python dependencies" ,
1634+ '"%s" venv --clear --quiet --python "%s" "%s" ' % ( uv_path , env . subst ( "$PYTHONEXE" ), venv_dir ) ,
1635+ "Creating a new virtual environment for IDF Python dependencies using uv " ,
16321636 )
16331637 )
16341638
1639+ # Verify that the venv was created successfully by checking for Python executable
1640+ python_path = get_executable_path (venv_dir , "python" )
1641+
16351642 assert os .path .isfile (
1636- pip_path
1637- ), "Error: Failed to create a proper virtual environment. Missing the `pip` binary !"
1643+ python_path
1644+ ), "Error: Failed to create a proper virtual environment. Missing the Python executable !"
16381645
16391646 venv_dir = get_idf_venv_dir ()
16401647 venv_data_file = os .path .join (venv_dir , "pio-idf-venv.json" )
0 commit comments