| #!/usr/bin/env python3 |
| # Copyright 2020 The Pigweed Authors |
| # |
| # Licensed under the Apache License, Version 2.0 (the "License"); you may not |
| # use this file except in compliance with the License. You may obtain a copy of |
| # the License at |
| # |
| # https://www.apache.org/licenses/LICENSE-2.0 |
| # |
| # Unless required by applicable law or agreed to in writing, software |
| # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
| # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
| # License for the specific language governing permissions and limitations under |
| # the License. |
| """Arduino Core Installer.""" |
| |
| import argparse |
| import importlib.resources |
| import logging |
| import os |
| from pathlib import Path |
| import platform |
| import shutil |
| |
| try: |
| from pw_arduino_build import file_operations |
| |
| except ImportError: |
| # Load this directory if pw_arduino_build is not available. |
| import file_operations # type: ignore |
| |
| _LOG = logging.getLogger(__name__) |
| |
| |
| class ArduinoCoreNotSupported(Exception): |
| """Exception raised when a given core can not be installed.""" |
| |
| |
| class ArduinoCoreInstallationFailed(Exception): |
| """Exception raised when a given core failed to be installed.""" |
| |
| |
| _ARDUINO_CORE_ARTIFACTS: dict[str, dict] = { |
| # pylint: disable=line-too-long |
| "teensy": { |
| "all": { |
| "core": { |
| "version": "1.58.1", |
| "url": "https://www.pjrc.com/teensy/td_158-1/teensy-package.tar.bz2", |
| "file_name": "teensy-package.tar.bz2", |
| "sha256": "3a3f3728045621d25068c5b5dfc24bf171550127e9fae4d0e8be2574c6636cff", |
| } |
| }, |
| "Linux": { |
| "teensy-tools": { |
| "url": "https://www.pjrc.com/teensy/td_158/teensy-tools-linux64.tar.bz2", |
| "file_name": "teensy-tools-linux64.tar.bz2", |
| "sha256": "0a272575ca42b4532ab89516df160e1d68e449fe1538c0bd71dbb768f1b3c0b6", |
| "version": "1.58.0", |
| }, |
| }, |
| # TODO(tonymd): Handle 32-bit Linux Install? |
| "Linux32": { |
| "teensy-tools": { |
| "url": "https://www.pjrc.com/teensy/td_158/teensy-tools-linux32.tar.bz2", |
| "file_name": "teensy-tools-linux32.tar.bz2", |
| "sha256": "995d974935c8118ad6d4c191206453dd8f57c1e299264bb4cffcc62c96c6d077", |
| "version": "1.58.0", |
| }, |
| }, |
| # TODO(tonymd): Handle ARM32 (Raspberry Pi) Install? |
| "LinuxARM32": { |
| "teensy-tools": { |
| "url": "https://www.pjrc.com/teensy/td_158/teensy-tools-linuxarm.tar.bz2", |
| "file_name": "teensy-tools-linuxarm.tar.bz2", |
| "sha256": "88cf8e55549f5d5937fa7dbc763cad49bd3680d4e5185b318c667f541035e633", |
| "version": "1.58.0", |
| }, |
| }, |
| # TODO(tonymd): Handle ARM64 Install? |
| "LinuxARM64": { |
| "teensy-tools": { |
| "url": "https://www.pjrc.com/teensy/td_158/teensy-tools-linuxaarch64.tar.bz2", |
| "file_name": "teensy-tools-linuxaarch64.tar.bz2", |
| "sha256": "a20b1c5e91fe51c3b6591e4cfcf711d4a4c0a0bb5120c59d1c8dd8d32ae44e31", |
| "version": "1.58.0", |
| }, |
| }, |
| "Darwin": { |
| "teensy-tools": { |
| "url": "https://www.pjrc.com/teensy/td_158/teensy-tools-macos.tar.bz2", |
| "file_name": "teensy-tools-macos.tar.bz2", |
| "sha256": "d386412e38fe6dd6c5d849c2b1f8eea00cbf7bc3659fb6ba9f83cebfb736924b", |
| "version": "1.58.0", |
| }, |
| }, |
| "Windows": { |
| "teensy-tools": { |
| "url": "https://www.pjrc.com/teensy/td_158/teensy-tools-windows.tar.bz2", |
| "file_name": "teensy-tools-windows.tar.bz2", |
| "sha256": "206315ddc82381d2da92da9f633a1719e00c0e8f5432acfed434573409a48de1", |
| "version": "1.58.0", |
| }, |
| }, |
| }, |
| "adafruit-samd": { |
| "all": { |
| "core": { |
| "version": "1.6.2", |
| "url": "https://github.com/adafruit/ArduinoCore-samd/archive/1.6.2.tar.gz", |
| "file_name": "adafruit-samd-1.6.2.tar.gz", |
| "sha256": "5875f5bc05904c10e6313f02653f28f2f716db639d3d43f5a1d8a83d15339d64", |
| } |
| }, |
| "Linux": {}, |
| "Darwin": {}, |
| "Windows": {}, |
| }, |
| "arduino-samd": { |
| "all": { |
| "core": { |
| "version": "1.8.8", |
| "url": "http://downloads.arduino.cc/cores/samd-1.8.8.tar.bz2", |
| "file_name": "arduino-samd-1.8.8.tar.bz2", |
| "sha256": "7b93eb705cba9125d9ee52eba09b51fb5fe34520ada351508f4253abbc9f27fa", |
| } |
| }, |
| "Linux": {}, |
| "Darwin": {}, |
| "Windows": {}, |
| }, |
| "stm32duino": { |
| "all": { |
| "core": { |
| "version": "2.6.0", |
| "url": "https://github.com/stm32duino/Arduino_Core_STM32/archive/2.6.0.tar.gz", |
| "file_name": "stm32duino-2.6.0.tar.gz", |
| "sha256": "53f37df1202b1bccfb353e4775200f63b36d487fe734fdb8ca9bfa00c2f3429f", |
| } |
| }, |
| "Linux": {}, |
| "Darwin": {}, |
| "Windows": {}, |
| }, |
| } |
| |
| |
| def install_core_command(args: argparse.Namespace): |
| install_core(args.prefix, args.core_name) |
| |
| |
| def install_core(prefix, core_name): |
| install_prefix = os.path.realpath( |
| os.path.expanduser(os.path.expandvars(prefix)) |
| ) |
| install_dir = os.path.join(install_prefix, core_name) |
| cache_dir = os.path.join(install_prefix, ".cache", core_name) |
| |
| if core_name in supported_cores(): |
| shutil.rmtree(install_dir, ignore_errors=True) |
| os.makedirs(install_dir, exist_ok=True) |
| os.makedirs(cache_dir, exist_ok=True) |
| |
| if core_name == "teensy": |
| install_teensy_core(install_prefix, install_dir, cache_dir) |
| apply_teensy_patches(install_dir) |
| elif core_name == "adafruit-samd": |
| install_adafruit_samd_core(install_prefix, install_dir, cache_dir) |
| elif core_name == "stm32duino": |
| install_stm32duino_core(install_prefix, install_dir, cache_dir) |
| elif core_name == "arduino-samd": |
| install_arduino_samd_core(install_prefix, install_dir, cache_dir) |
| else: |
| raise ArduinoCoreNotSupported( |
| "Invalid core '{}'. Supported cores: {}".format( |
| core_name, ", ".join(supported_cores()) |
| ) |
| ) |
| |
| |
| def supported_cores(): |
| return _ARDUINO_CORE_ARTIFACTS.keys() |
| |
| |
| def install_teensy_core(_install_prefix: str, install_dir: str, cache_dir: str): |
| """Install teensy core files and tools.""" |
| # Install the Teensy core source files |
| artifacts = _ARDUINO_CORE_ARTIFACTS["teensy"]["all"]["core"] |
| core_tarfile = file_operations.download_to_cache( |
| url=artifacts["url"], |
| expected_sha256sum=artifacts["sha256"], |
| cache_directory=cache_dir, |
| downloaded_file_name=artifacts["file_name"], |
| ) |
| |
| package_path = os.path.join( |
| install_dir, "hardware", "avr", artifacts["version"] |
| ) |
| os.makedirs(package_path, exist_ok=True) |
| file_operations.extract_archive(core_tarfile, package_path, cache_dir) |
| |
| expected_files = [ |
| Path(package_path) / 'boards.txt', |
| Path(package_path) / 'platform.txt', |
| ] |
| |
| if any(not expected_file.is_file() for expected_file in expected_files): |
| expected_files_str = "".join( |
| list(f" {expected_file}\n" for expected_file in expected_files) |
| ) |
| |
| raise ArduinoCoreInstallationFailed( |
| "\n\nError: Installation Failed.\n" |
| "Please remove the package:\n\n" |
| " pw package remove teensy\n\n" |
| "Try again and ensure the following files exist:\n\n" |
| + expected_files_str |
| ) |
| |
| teensy_tools = _ARDUINO_CORE_ARTIFACTS["teensy"][platform.system()] |
| |
| for tool_name, artifacts in teensy_tools.items(): |
| tool_tarfile = file_operations.download_to_cache( |
| url=artifacts["url"], |
| expected_sha256sum=artifacts["sha256"], |
| cache_directory=cache_dir, |
| downloaded_file_name=artifacts["file_name"], |
| ) |
| |
| tool_path = os.path.join( |
| install_dir, "tools", tool_name, artifacts["version"] |
| ) |
| |
| os.makedirs(tool_path, exist_ok=True) |
| file_operations.extract_archive(tool_tarfile, tool_path, cache_dir) |
| |
| return True |
| |
| |
| def apply_teensy_patches(install_dir): |
| # On Mac the "hardware" directory is a symlink: |
| # ls -l third_party/arduino/cores/teensy/ |
| # hardware -> Teensyduino.app/Contents/Java/hardware |
| # Resolve paths since `git apply` doesn't work if a path is beyond a |
| # symbolic link. |
| patch_root_path = ( |
| Path(install_dir) / "hardware/avr/1.58.1/cores" |
| ).resolve() |
| |
| # Get all *.diff files for the teensy core. |
| patches_python_package = 'pw_arduino_build.core_patches.teensy' |
| |
| patch_file_names = sorted( |
| patch |
| for patch in importlib.resources.contents(patches_python_package) |
| if Path(patch).suffix in ['.diff'] |
| ) |
| |
| # Apply each patch file. |
| for diff_name in patch_file_names: |
| with importlib.resources.path( |
| patches_python_package, diff_name |
| ) as diff_path: |
| file_operations.git_apply_patch( |
| patch_root_path.as_posix(), |
| diff_path.as_posix(), |
| unsafe_paths=True, |
| ) |
| |
| |
| def install_arduino_samd_core( |
| install_prefix: str, install_dir: str, cache_dir: str |
| ): |
| artifacts = _ARDUINO_CORE_ARTIFACTS["arduino-samd"]["all"]["core"] |
| core_tarfile = file_operations.download_to_cache( |
| url=artifacts["url"], |
| expected_sha256sum=artifacts["sha256"], |
| cache_directory=cache_dir, |
| downloaded_file_name=artifacts["file_name"], |
| ) |
| |
| package_path = os.path.join( |
| install_dir, "hardware", "samd", artifacts["version"] |
| ) |
| os.makedirs(package_path, exist_ok=True) |
| file_operations.extract_archive(core_tarfile, package_path, cache_dir) |
| original_working_dir = os.getcwd() |
| os.chdir(install_prefix) |
| # TODO(tonymd): Fetch core/tools as specified by: |
| # http://downloads.arduino.cc/packages/package_index.json |
| os.chdir(original_working_dir) |
| return True |
| |
| |
| def install_adafruit_samd_core( |
| install_prefix: str, install_dir: str, cache_dir: str |
| ): |
| artifacts = _ARDUINO_CORE_ARTIFACTS["adafruit-samd"]["all"]["core"] |
| core_tarfile = file_operations.download_to_cache( |
| url=artifacts["url"], |
| expected_sha256sum=artifacts["sha256"], |
| cache_directory=cache_dir, |
| downloaded_file_name=artifacts["file_name"], |
| ) |
| |
| package_path = os.path.join( |
| install_dir, "hardware", "samd", artifacts["version"] |
| ) |
| os.makedirs(package_path, exist_ok=True) |
| file_operations.extract_archive(core_tarfile, package_path, cache_dir) |
| |
| original_working_dir = os.getcwd() |
| os.chdir(install_prefix) |
| # TODO(tonymd): Fetch platform specific tools as specified by: |
| # https://adafruit.github.io/arduino-board-index/package_adafruit_index.json |
| # Specifically: |
| # https://github.com/ARM-software/CMSIS_5/archive/5.4.0.tar.gz |
| os.chdir(original_working_dir) |
| return True |
| |
| |
| def install_stm32duino_core(install_prefix, install_dir, cache_dir): |
| artifacts = _ARDUINO_CORE_ARTIFACTS["stm32duino"]["all"]["core"] |
| core_tarfile = file_operations.download_to_cache( |
| url=artifacts["url"], |
| expected_sha256sum=artifacts["sha256"], |
| cache_directory=cache_dir, |
| downloaded_file_name=artifacts["file_name"], |
| ) |
| |
| package_path = os.path.join( |
| install_dir, "hardware", "stm32", artifacts["version"] |
| ) |
| os.makedirs(package_path, exist_ok=True) |
| file_operations.extract_archive(core_tarfile, package_path, cache_dir) |
| original_working_dir = os.getcwd() |
| os.chdir(install_prefix) |
| # TODO(tonymd): Fetch platform specific tools as specified by: |
| # https://github.com/stm32duino/BoardManagerFiles/raw/HEAD/STM32/package_stm_index.json |
| os.chdir(original_working_dir) |
| return True |