@@ -63,6 +63,46 @@ def configure_default_packages(self, variables, targets):
63
63
else :
64
64
del self .packages ["tool-dfuutil-arduino" ]
65
65
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
+
66
106
# Starting from v12, Espressif's toolchains are shipped without
67
107
# bundled GDB. Instead, it's distributed as separate packages for Xtensa
68
108
# and RISC-V targets.
@@ -131,6 +171,9 @@ def _add_dynamic_options(self, board):
131
171
"tumpa" ,
132
172
]
133
173
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" )
134
177
if board .get ("build.mcu" , "" ) in ("esp32c3" , "esp32c6" , "esp32s3" , "esp32h2" ):
135
178
supported_debug_tools .append ("esp-builtin" )
136
179
@@ -201,6 +244,9 @@ def _add_dynamic_options(self, board):
201
244
"default" : link == debug .get ("default_tool" ),
202
245
}
203
246
247
+ # Avoid erasing Arduino Nano bootloader by preloading app binary
248
+ if board .id == "arduino_nano_esp32" :
249
+ debug ["tools" ][link ]["load_cmds" ] = "preload"
204
250
board .manifest ["debug" ] = debug
205
251
return board
206
252
@@ -236,3 +282,114 @@ def configure_debug_session(self, debug_config):
236
282
)
237
283
)
238
284
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