Skip to content

Commit 750d76e

Browse files
authored
add dynamic toolchain configuration
1 parent dc1c7ed commit 750d76e

File tree

1 file changed

+157
-0
lines changed

1 file changed

+157
-0
lines changed

platform.py

Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,46 @@ def configure_default_packages(self, variables, targets):
6363
else:
6464
del self.packages["tool-dfuutil-arduino"]
6565

66+
build_core = variables.get(
67+
"board_build.core", board_config.get("build.core", "arduino")
68+
).lower()
69+
70+
if frameworks == ["arduino"] and build_core == "esp32":
71+
# In case the upstream Arduino framework is specified in the configuration
72+
# file then we need to dynamically extract toolchain versions from the
73+
# Arduino index file. This feature can be disabled via a special option:
74+
if (
75+
variables.get(
76+
"board_build.arduino.upstream_packages",
77+
board_config.get("build.arduino.upstream_packages", "no"),
78+
).lower()
79+
== "yes"
80+
):
81+
package_version = self.packages["framework-arduinoespressif32"][
82+
"version"
83+
]
84+
85+
url_items = urllib.parse.urlparse(package_version)
86+
# Only GitHub repositories support dynamic packages
87+
if (
88+
url_items.scheme in ("http", "https")
89+
and url_items.netloc.startswith("github")
90+
and url_items.path.endswith(".git")
91+
):
92+
try:
93+
self.configure_upstream_arduino_packages(url_items)
94+
except Exception as e:
95+
sys.stderr.write(
96+
"Error! Failed to extract upstream toolchain"
97+
"configurations:\n%s\n" % str(e)
98+
)
99+
sys.stderr.write(
100+
"You can disable this feature via the "
101+
"`board_build.arduino.upstream_packages = no` setting in "
102+
"your `platformio.ini` file.\n"
103+
)
104+
sys.exit(1)
105+
66106
# Starting from v12, Espressif's toolchains are shipped without
67107
# bundled GDB. Instead, it's distributed as separate packages for Xtensa
68108
# and RISC-V targets.
@@ -131,6 +171,9 @@ def _add_dynamic_options(self, board):
131171
"tumpa",
132172
]
133173

174+
# A special case for the Kaluga board that has a separate interface config
175+
if board.id == "esp32-s2-kaluga-1":
176+
supported_debug_tools.append("ftdi")
134177
if board.get("build.mcu", "") in ("esp32c3", "esp32c6", "esp32s3", "esp32h2"):
135178
supported_debug_tools.append("esp-builtin")
136179

@@ -201,6 +244,9 @@ def _add_dynamic_options(self, board):
201244
"default": link == debug.get("default_tool"),
202245
}
203246

247+
# Avoid erasing Arduino Nano bootloader by preloading app binary
248+
if board.id == "arduino_nano_esp32":
249+
debug["tools"][link]["load_cmds"] = "preload"
204250
board.manifest["debug"] = debug
205251
return board
206252

@@ -236,3 +282,114 @@ def configure_debug_session(self, debug_config):
236282
)
237283
)
238284
debug_config.load_cmds = load_cmds
285+
286+
287+
@staticmethod
288+
def extract_toolchain_versions(tool_deps):
289+
def _parse_version(original_version):
290+
assert original_version
291+
version_patterns = (
292+
r"^gcc(?P<MAJOR>\d+)_(?P<MINOR>\d+)_(?P<PATCH>\d+)-esp-(?P<EXTRA>.+)$",
293+
r"^esp-(?P<EXTRA>.+)-(?P<MAJOR>\d+)\.(?P<MINOR>\d+)\.?(?P<PATCH>\d+)$",
294+
r"^esp-(?P<MAJOR>\d+)\.(?P<MINOR>\d+)\.(?P<PATCH>\d+)(_(?P<EXTRA>.+))?$",
295+
)
296+
for pattern in version_patterns:
297+
match = re.search(pattern, original_version)
298+
if match:
299+
result = "%s.%s.%s" % (
300+
match.group("MAJOR"),
301+
match.group("MINOR"),
302+
match.group("PATCH"),
303+
)
304+
if match.group("EXTRA"):
305+
result = result + "+%s" % match.group("EXTRA")
306+
return result
307+
308+
raise ValueError("Bad package version `%s`" % original_version)
309+
310+
if not tool_deps:
311+
raise ValueError(
312+
("Failed to extract tool dependencies from the remote package file")
313+
)
314+
315+
toolchain_remap = {
316+
"xtensa-esp32-elf-gcc": "toolchain-xtensa-esp32",
317+
"xtensa-esp32s2-elf-gcc": "toolchain-xtensa-esp32s2",
318+
"xtensa-esp32s3-elf-gcc": "toolchain-xtensa-esp32s3",
319+
"riscv32-esp-elf-gcc": "toolchain-riscv32-esp",
320+
}
321+
322+
result = dict()
323+
for tool in tool_deps:
324+
if tool["name"] in toolchain_remap:
325+
result[toolchain_remap[tool["name"]]] = _parse_version(tool["version"])
326+
327+
return result
328+
329+
@staticmethod
330+
def parse_tool_dependencies(index_data):
331+
for package in index_data.get("packages", []):
332+
if package["name"] == "esp32":
333+
for platform in package["platforms"]:
334+
if platform["name"] == "esp32":
335+
return platform["toolsDependencies"]
336+
337+
return []
338+
339+
@staticmethod
340+
def download_remote_package_index(url_items):
341+
def _prepare_url_for_index_file(url_items):
342+
tag = "master"
343+
if url_items.fragment:
344+
tag = url_items.fragment
345+
return (
346+
"https://raw.githubusercontent.com/%s/"
347+
"%s/package/package_esp32_index.template.json"
348+
% (url_items.path.replace(".git", ""), tag)
349+
)
350+
351+
index_file_url = _prepare_url_for_index_file(url_items)
352+
353+
try:
354+
from platformio.public import fetch_http_content
355+
content = fetch_http_content(index_file_url)
356+
except ImportError:
357+
import requests
358+
content = requests.get(index_file_url, timeout=5).text
359+
360+
return json.loads(content)
361+
362+
def configure_arduino_toolchains(self, package_index):
363+
if not package_index:
364+
return
365+
366+
toolchain_packages = self.extract_toolchain_versions(
367+
self.parse_tool_dependencies(package_index)
368+
)
369+
for toolchain_package, version in toolchain_packages.items():
370+
if toolchain_package not in self.packages:
371+
self.packages[toolchain_package] = dict()
372+
self.packages[toolchain_package]["version"] = version
373+
self.packages[toolchain_package]["owner"] = "espressif"
374+
self.packages[toolchain_package]["type"] = "toolchain"
375+
376+
def configure_upstream_arduino_packages(self, url_items):
377+
framework_index_file = os.path.join(
378+
self.get_package_dir("framework-arduinoespressif32") or "",
379+
"package",
380+
"package_esp32_index.template.json",
381+
)
382+
383+
# Detect whether the remote is already cloned
384+
if os.path.isfile(framework_index_file) and os.path.isdir(
385+
os.path.join(
386+
self.get_package_dir("framework-arduinoespressif32") or "", ".git"
387+
)
388+
):
389+
with open(framework_index_file) as fp:
390+
self.configure_arduino_toolchains(json.load(fp))
391+
else:
392+
print("Configuring toolchain packages from a remote source...")
393+
self.configure_arduino_toolchains(
394+
self.download_remote_package_index(url_items)
395+
)

0 commit comments

Comments
 (0)