Skip to content

Commit 8e2a364

Browse files
authored
Full uv path (#227)
* Use full path for uv and set windows path var * Global Python executable path
1 parent 2c603d4 commit 8e2a364

File tree

1 file changed

+96
-55
lines changed

1 file changed

+96
-55
lines changed

builder/main.py

Lines changed: 96 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@
5151
platform = env.PioPlatform()
5252
projectconfig = env.GetProjectConfig()
5353
terminal_cp = locale.getpreferredencoding().lower()
54+
PYTHON_EXE = env.subst("$PYTHONEXE") # Global Python executable path
5455

5556
# Framework directory path
5657
FRAMEWORK_DIR = platform.get_package_dir("framework-arduinoespressif32")
@@ -80,24 +81,20 @@ def add_to_pythonpath(path):
8081
sys.path.insert(0, normalized_path)
8182

8283

83-
def setup_python_paths(env):
84+
def setup_python_paths():
8485
"""
8586
Setup Python paths based on the actual Python executable being used.
86-
87-
Args:
88-
env: SCons environment object
8987
"""
90-
python_exe = env.subst('$PYTHONEXE')
91-
if not python_exe or not os.path.isfile(python_exe):
88+
if not PYTHON_EXE or not os.path.isfile(PYTHON_EXE):
9289
return
9390

9491
# Get the directory containing the Python executable
95-
python_dir = os.path.dirname(python_exe)
92+
python_dir = os.path.dirname(PYTHON_EXE)
9693
add_to_pythonpath(python_dir)
9794

9895
# Try to find site-packages directory using the actual Python executable
9996
result = subprocess.run(
100-
[python_exe, "-c", "import site; print(site.getsitepackages()[0])"],
97+
[PYTHON_EXE, "-c", "import site; print(site.getsitepackages()[0])"],
10198
capture_output=True,
10299
text=True,
103100
timeout=5
@@ -108,7 +105,68 @@ def setup_python_paths(env):
108105
add_to_pythonpath(site_packages)
109106

110107
# Setup Python paths based on the actual Python executable
111-
setup_python_paths(env)
108+
setup_python_paths()
109+
110+
111+
def _get_executable_path(python_exe, executable_name):
112+
"""
113+
Get the path to an executable binary (esptool, uv, etc.) based on the Python executable path.
114+
115+
Args:
116+
python_exe (str): Path to Python executable
117+
executable_name (str): Name of the executable to find (e.g., 'esptool', 'uv')
118+
119+
Returns:
120+
str: Path to executable or fallback to executable name
121+
"""
122+
if not python_exe or not os.path.isfile(python_exe):
123+
return executable_name # Fallback to command name
124+
125+
python_dir = os.path.dirname(python_exe)
126+
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")
130+
else:
131+
# For Unix-like systems, executables are typically in the same directory as python
132+
# or in a bin subdirectory
133+
executable_path = os.path.join(python_dir, executable_name)
134+
135+
# If not found in python directory, try bin subdirectory
136+
if not os.path.isfile(executable_path):
137+
bin_dir = os.path.join(python_dir, "bin")
138+
executable_path = os.path.join(bin_dir, executable_name)
139+
140+
if os.path.isfile(executable_path):
141+
return executable_path
142+
143+
return executable_name # Fallback to command name
144+
145+
146+
def _get_esptool_executable_path(python_exe):
147+
"""
148+
Get the path to the esptool executable binary.
149+
150+
Args:
151+
python_exe (str): Path to Python executable
152+
153+
Returns:
154+
str: Path to esptool executable
155+
"""
156+
return _get_executable_path(python_exe, "esptool")
157+
158+
159+
def _get_uv_executable_path(python_exe):
160+
"""
161+
Get the path to the uv executable binary.
162+
163+
Args:
164+
python_exe (str): Path to Python executable
165+
166+
Returns:
167+
str: Path to uv executable
168+
"""
169+
return _get_executable_path(python_exe, "uv")
112170

113171

114172
def get_packages_to_install(deps, installed_packages):
@@ -138,9 +196,12 @@ def install_python_deps():
138196
Returns:
139197
bool: True if successful, False otherwise
140198
"""
199+
# Get uv executable path
200+
uv_executable = _get_uv_executable_path(PYTHON_EXE)
201+
141202
try:
142203
result = subprocess.run(
143-
["uv", "--version"],
204+
[uv_executable, "--version"],
144205
capture_output=True,
145206
text=True,
146207
timeout=3
@@ -152,7 +213,7 @@ def install_python_deps():
152213
if not uv_available:
153214
try:
154215
result = subprocess.run(
155-
[env.subst("$PYTHONEXE"), "-m", "pip", "install", "uv>=0.1.0", "-q", "-q", "-q"],
216+
[PYTHON_EXE, "-m", "pip", "install", "uv>=0.1.0", "-q", "-q", "-q"],
156217
capture_output=True,
157218
text=True,
158219
timeout=30, # 30 second timeout
@@ -162,6 +223,17 @@ def install_python_deps():
162223
if result.stderr:
163224
print(f"Error output: {result.stderr.strip()}")
164225
return False
226+
227+
# Update uv executable path after installation
228+
uv_executable = _get_uv_executable_path(PYTHON_EXE)
229+
230+
# Add Scripts directory to PATH for Windows
231+
if sys.platform == "win32":
232+
python_dir = os.path.dirname(PYTHON_EXE)
233+
scripts_dir = os.path.join(python_dir, "Scripts")
234+
if os.path.isdir(scripts_dir):
235+
os.environ["PATH"] = scripts_dir + os.pathsep + os.environ.get("PATH", "")
236+
165237
except subprocess.TimeoutExpired:
166238
print("Error: uv installation timed out")
167239
return False
@@ -182,7 +254,7 @@ def _get_installed_uv_packages():
182254
"""
183255
result = {}
184256
try:
185-
cmd = ["uv", "pip", "list", "--format=json"]
257+
cmd = [uv_executable, "pip", "list", "--format=json"]
186258
result_obj = subprocess.run(
187259
cmd,
188260
capture_output=True,
@@ -221,8 +293,8 @@ def _get_installed_uv_packages():
221293
packages_list = [f"{p}{python_deps[p]}" for p in packages_to_install]
222294

223295
cmd = [
224-
"uv", "pip", "install",
225-
f"--python={env.subst('$PYTHONEXE')}",
296+
uv_executable, "pip", "install",
297+
f"--python={PYTHON_EXE}",
226298
"--quiet", "--upgrade"
227299
] + packages_list
228300

@@ -254,68 +326,37 @@ def _get_installed_uv_packages():
254326
return True
255327

256328

257-
def install_esptool(env):
329+
def install_esptool():
258330
"""
259331
Install esptool from package folder "tool-esptoolpy" using uv package manager.
260332
Also determines the path to the esptool executable binary.
261333
262-
Args:
263-
env: SCons environment object
264-
265334
Returns:
266335
str: Path to esptool executable, or 'esptool' as fallback
267336
"""
268-
def _get_esptool_executable_path(python_exe):
269-
"""
270-
Get the path to the esptool executable binary.
271-
272-
Args:
273-
python_exe (str): Path to Python executable
274-
275-
Returns:
276-
str: Path to esptool executable
277-
"""
278-
if not python_exe or not os.path.isfile(python_exe):
279-
return 'esptool' # Fallback
280-
281-
python_dir = os.path.dirname(python_exe)
282-
283-
if sys.platform == "win32":
284-
scripts_dir = os.path.join(python_dir, "Scripts")
285-
esptool_exe = os.path.join(scripts_dir, "esptool.exe")
286-
else:
287-
scripts_dir = os.path.join(python_dir)
288-
esptool_exe = os.path.join(scripts_dir, "esptool")
289-
290-
if os.path.isfile(esptool_exe):
291-
return esptool_exe
292-
293-
return 'esptool'
294-
295337
try:
296338
subprocess.check_call(
297-
[env.subst("$PYTHONEXE"), "-c", "import esptool"],
339+
[PYTHON_EXE, "-c", "import esptool"],
298340
stdout=subprocess.DEVNULL,
299341
stderr=subprocess.DEVNULL,
300342
env=os.environ
301343
)
302-
python_exe = env.subst("$PYTHONEXE")
303-
esptool_binary_path = _get_esptool_executable_path(python_exe)
344+
esptool_binary_path = _get_esptool_executable_path(PYTHON_EXE)
304345
return esptool_binary_path
305346
except (subprocess.CalledProcessError, FileNotFoundError):
306347
pass
307348

308349
esptool_repo_path = env.subst(platform.get_package_dir("tool-esptoolpy") or "")
309350
if esptool_repo_path and os.path.isdir(esptool_repo_path):
351+
uv_executable = _get_uv_executable_path(PYTHON_EXE)
310352
try:
311353
subprocess.check_call([
312-
"uv", "pip", "install", "--quiet",
313-
f"--python={env.subst('$PYTHONEXE')}",
354+
uv_executable, "pip", "install", "--quiet",
355+
f"--python={PYTHON_EXE}",
314356
"-e", esptool_repo_path
315357
], env=os.environ)
316358

317-
python_exe = env.subst("$PYTHONEXE")
318-
esptool_binary_path = _get_esptool_executable_path(python_exe)
359+
esptool_binary_path = _get_esptool_executable_path(PYTHON_EXE)
319360
return esptool_binary_path
320361

321362
except subprocess.CalledProcessError as e:
@@ -327,7 +368,7 @@ def _get_esptool_executable_path(python_exe):
327368

328369
# Install Python dependencies and esptool
329370
install_python_deps()
330-
esptool_binary_path = install_esptool(env)
371+
esptool_binary_path = install_esptool()
331372

332373

333374
def BeforeUpload(target, source, env):
@@ -874,7 +915,7 @@ def firmware_metrics(target, source, env):
874915
return
875916

876917
try:
877-
cmd = [env.subst("$PYTHONEXE"), "-m", "esp_idf_size", "--ng"]
918+
cmd = [PYTHON_EXE, "-m", "esp_idf_size", "--ng"]
878919

879920
# Parameters from platformio.ini
880921
extra_args = env.GetProjectOption("custom_esp_idf_size_args", "")
@@ -1002,7 +1043,7 @@ def firmware_metrics(target, source, env):
10021043
env.Replace(
10031044
UPLOADER=join(FRAMEWORK_DIR, "tools", "espota.py"),
10041045
UPLOADERFLAGS=["--debug", "--progress", "-i", "$UPLOAD_PORT"],
1005-
UPLOADCMD='"$PYTHONEXE" "$UPLOADER" $UPLOADERFLAGS -f $SOURCE',
1046+
UPLOADCMD=f'"{PYTHON_EXE}" "$UPLOADER" $UPLOADERFLAGS -f $SOURCE',
10061047
)
10071048
if set(["uploadfs", "uploadfsota"]) & set(COMMAND_LINE_TARGETS):
10081049
env.Append(UPLOADERFLAGS=["--spiffs"])

0 commit comments

Comments
 (0)