blob: c371d589adb1d3b7b7ca09eeb94e141140817fe6 [file] [log] [blame]
#!/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