@@ -210,7 +210,8 @@ def install_arduino_cli(self):
210210 url = arduino_cli_archive_download_url_prefix + arduino_cli_archive_file_name ,
211211 # The Arduino CLI has no root folder, so just install the arduino-cli executable from the archive root
212212 source_path = "arduino-cli" ,
213- destination_parent_path = self .arduino_cli_installation_path
213+ destination_parent_path = self .arduino_cli_installation_path ,
214+ force = False
214215 )
215216
216217 # Configure the location of the Arduino CLI user directory
@@ -424,20 +425,32 @@ def install_platforms_from_path(self, platform_list):
424425
425426 # Install the platform
426427 install_from_path (source_path = source_path ,
427- destination_parent_path = platform_installation_path .parent ,
428- destination_name = platform_installation_path .name )
428+ destination_parent_path = platform_installation_path .path .parent ,
429+ destination_name = platform_installation_path .path .name ,
430+ force = platform_installation_path .is_overwrite )
429431
430432 def get_platform_installation_path (self , platform ):
431- """Return the correct installation path for the given platform
433+ """Determines the correct installation path for the given platform and returns an object with the attributes:
434+ path -- correct installation path for the platform (pathlib.Path() object)
435+ is_overwrite -- whether there is an existing installation of the platform (True, False)
432436
433437 Keyword arguments:
434438 platform -- dictionary defining the platform dependency
435439 """
440+
441+ class PlatformInstallationPath :
442+ def __init__ (self ):
443+ self .path = pathlib .Path ()
444+ self .is_overwrite = False
445+
446+ platform_installation_path = PlatformInstallationPath ()
447+
448+ # Default to installing to the sketchbook
436449 platform_vendor = platform [self .dependency_name_key ].split (sep = ":" )[0 ]
437450 platform_architecture = platform [self .dependency_name_key ].rsplit (sep = ":" , maxsplit = 1 )[1 ]
438451
439452 # Default to installing to the sketchbook
440- platform_installation_path = self .user_platforms_path .joinpath (platform_vendor , platform_architecture )
453+ platform_installation_path . path = self .user_platforms_path .joinpath (platform_vendor , platform_architecture )
441454
442455 # I have no clue why this is needed, but arduino-cli core list fails if this isn't done first. The 3rd party
443456 # platforms are still shown in the list even if their index URLs are not specified to the command via the
@@ -449,15 +462,13 @@ def get_platform_installation_path(self, platform):
449462 for installed_platform in installed_platform_list :
450463 if installed_platform ["ID" ] == platform [self .dependency_name_key ]:
451464 # The platform has been installed via Board Manager, so do an overwrite
452- platform_installation_path = (
465+ platform_installation_path . path = (
453466 self .board_manager_platforms_path .joinpath (platform_vendor ,
454467 "hardware" ,
455468 platform_architecture ,
456469 installed_platform ["Installed" ])
457470 )
458-
459- # Remove the existing installation so it can be replaced by the installation function
460- shutil .rmtree (path = platform_installation_path )
471+ platform_installation_path .is_overwrite = True
461472
462473 break
463474
@@ -484,8 +495,9 @@ def install_platforms_from_repository(self, platform_list):
484495 self .install_from_repository (url = platform [self .dependency_source_url_key ],
485496 git_ref = git_ref ,
486497 source_path = source_path ,
487- destination_parent_path = destination_path .parent ,
488- destination_name = destination_path .name )
498+ destination_parent_path = destination_path .path .parent ,
499+ destination_name = destination_path .path .name ,
500+ force = destination_path .is_overwrite )
489501
490502 def get_repository_dependency_ref (self , dependency ):
491503 """Return the appropriate git ref value for a repository dependency
@@ -500,7 +512,13 @@ def get_repository_dependency_ref(self, dependency):
500512
501513 return git_ref
502514
503- def install_from_repository (self , url , git_ref , source_path , destination_parent_path , destination_name = None ):
515+ def install_from_repository (self ,
516+ url ,
517+ git_ref ,
518+ source_path ,
519+ destination_parent_path ,
520+ destination_name = None ,
521+ force = False ):
504522 """Install by cloning a repository
505523
506524 Keyword arguments:
@@ -510,6 +528,7 @@ def install_from_repository(self, url, git_ref, source_path, destination_parent_
510528 destination_parent_path -- path under which to install
511529 destination_name -- folder name to use for the installation. Set to None to use the repository name.
512530 (default None)
531+ force -- replace existing destination folder if present. (default False)
513532 """
514533 if destination_name is None and source_path .rstrip ("/" ) == "." :
515534 # Use the repository name
@@ -521,7 +540,8 @@ def install_from_repository(self, url, git_ref, source_path, destination_parent_
521540 # Install to the final location
522541 install_from_path (source_path = pathlib .Path (clone_folder , source_path ),
523542 destination_parent_path = destination_parent_path ,
524- destination_name = destination_name )
543+ destination_name = destination_name ,
544+ force = force )
525545
526546 def clone_repository (self , url , git_ref , destination_path ):
527547 """Clone a Git repository to a specified location and check out the specified ref
@@ -567,8 +587,9 @@ def install_platforms_from_download(self, platform_list):
567587
568588 install_from_download (url = platform [self .dependency_source_url_key ],
569589 source_path = source_path ,
570- destination_parent_path = destination_path .parent ,
571- destination_name = destination_path .name )
590+ destination_parent_path = destination_path .path .parent ,
591+ destination_name = destination_path .path .name ,
592+ force = destination_path .is_overwrite )
572593
573594 def install_libraries (self ):
574595 """Install Arduino libraries."""
@@ -587,6 +608,9 @@ def install_libraries(self):
587608 # that behavior is retained when using the old input syntax
588609 library_list .path = [{self .dependency_source_path_key : os .environ ["GITHUB_WORKSPACE" ]}]
589610
611+ # Dependencies of Library Manager sourced libraries (as defined by the library's metadata file) are
612+ # automatically installed. For this reason, LM-sources must be installed first so the library dependencies from
613+ # other sources which were explicitly defined won't be replaced.
590614 if len (library_list .manager ) > 0 :
591615 self .install_libraries_from_library_manager (library_list = library_list .manager )
592616
@@ -639,7 +663,8 @@ def install_libraries_from_path(self, library_list):
639663
640664 install_from_path (source_path = source_path ,
641665 destination_parent_path = self .libraries_path ,
642- destination_name = destination_name )
666+ destination_name = destination_name ,
667+ force = True )
643668
644669 def install_libraries_from_repository (self , library_list ):
645670 """Install libraries by cloning Git repositories
@@ -669,7 +694,8 @@ def install_libraries_from_repository(self, library_list):
669694 git_ref = git_ref ,
670695 source_path = source_path ,
671696 destination_parent_path = self .libraries_path ,
672- destination_name = destination_name )
697+ destination_name = destination_name ,
698+ force = True )
673699
674700 def install_libraries_from_download (self , library_list ):
675701 """Install libraries by downloading them
@@ -692,7 +718,8 @@ def install_libraries_from_download(self, library_list):
692718 install_from_download (url = library [self .dependency_source_url_key ],
693719 source_path = source_path ,
694720 destination_parent_path = self .libraries_path ,
695- destination_name = destination_name )
721+ destination_name = destination_name ,
722+ force = True )
696723
697724 def find_sketches (self ):
698725 """Return a list of all sketches under the paths specified in the sketch paths list recursively."""
@@ -1269,23 +1296,32 @@ def __init__(self):
12691296 return input_list
12701297
12711298
1272- def install_from_path (source_path , destination_parent_path , destination_name = None ):
1299+ def install_from_path (source_path , destination_parent_path , destination_name = None , force = False ):
12731300 """Copy the source path to the destination path.
12741301
12751302 Keyword arguments:
12761303 source_path -- path to install
12771304 destination_parent_path -- path under which to install
12781305 destination_name -- folder or filename name to use for the installation. Set to None to take the name from
12791306 source_path. (default None)
1307+ force -- replace existing destination if present. (default False)
12801308 """
12811309 if destination_name is None :
12821310 destination_name = source_path .name
12831311
1312+ destination_path = destination_parent_path .joinpath (destination_name )
1313+
1314+ if destination_path .exists ():
1315+ if force :
1316+ # Clear existing folder
1317+ shutil .rmtree (path = destination_path )
1318+ else :
1319+ print ("::error::Installation already exists:" , destination_path )
1320+ sys .exit (1 )
1321+
12841322 # Create the parent path if it doesn't already exist
12851323 destination_parent_path .mkdir (parents = True , exist_ok = True )
12861324
1287- destination_path = destination_parent_path .joinpath (destination_name )
1288-
12891325 if source_path .is_dir ():
12901326 shutil .copytree (src = source_path , dst = destination_path )
12911327 else :
@@ -1321,7 +1357,7 @@ def list_to_string(list_input):
13211357 return " " .join ([str (item ) for item in list_input ])
13221358
13231359
1324- def install_from_download (url , source_path , destination_parent_path , destination_name = None ):
1360+ def install_from_download (url , source_path , destination_parent_path , destination_name = None , force = False ):
13251361 """Download an archive, extract, and install.
13261362
13271363 Keyword arguments:
@@ -1330,6 +1366,7 @@ def install_from_download(url, source_path, destination_parent_path, destination
13301366 destination_parent_path -- path under which to install
13311367 destination_name -- folder name to use for the installation. Set to None to take the name from source_path.
13321368 (default None)
1369+ force -- replace existing destination folder if present. (default False)
13331370 """
13341371 destination_parent_path = pathlib .Path (destination_parent_path )
13351372
@@ -1362,7 +1399,8 @@ def install_from_download(url, source_path, destination_parent_path, destination
13621399
13631400 install_from_path (source_path = absolute_source_path ,
13641401 destination_parent_path = destination_parent_path ,
1365- destination_name = destination_name )
1402+ destination_name = destination_name ,
1403+ force = force )
13661404
13671405
13681406def get_archive_root_path (archive_extract_path ):
0 commit comments