1# Copyright 2020 The Pigweed Authors 2# 3# Licensed under the Apache License, Version 2.0 (the "License"); you may not 4# use this file except in compliance with the License. You may obtain a copy of 5# the License at 6# 7# https://www.apache.org/licenses/LICENSE-2.0 8# 9# Unless required by applicable law or agreed to in writing, software 10# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12# License for the specific language governing permissions and limitations under 13# the License. 14"""Install and check status of teensy-core.""" 15 16import json 17import logging 18import re 19import subprocess 20import tempfile 21from pathlib import Path 22from typing import Sequence 23 24from pw_arduino_build import core_installer 25 26import pw_package.package_manager 27 28_LOG: logging.Logger = logging.getLogger(__name__) 29 30 31class ArduinoCore(pw_package.package_manager.Package): 32 """Install and check status of arduino cores.""" 33 def __init__(self, core_name, *args, **kwargs): 34 super().__init__(*args, name=core_name, **kwargs) 35 36 def status(self, path: Path) -> bool: 37 return (path / 'hardware').is_dir() 38 39 def populate_download_cache_from_cipd(self, path: Path) -> None: 40 """Check for arduino core availability in pigweed_internal cipd.""" 41 package_path = path.parent.resolve() 42 core_name = self.name 43 core_cache_path = package_path / ".cache" / core_name 44 core_cache_path.mkdir(parents=True, exist_ok=True) 45 46 cipd_package_subpath = "pigweed_internal/third_party/" 47 cipd_package_subpath += core_name 48 cipd_package_subpath += "/${platform}" 49 50 # Check if teensy cipd package is readable 51 52 with tempfile.NamedTemporaryFile(prefix='cipd', 53 delete=True) as temp_json: 54 cipd_acl_check_command = [ 55 "cipd", 56 "acl-check", 57 cipd_package_subpath, 58 "-reader", 59 "-json-output", 60 temp_json.name, 61 ] 62 subprocess.run(cipd_acl_check_command, capture_output=True) 63 # Return if no packages are readable. 64 if not json.load(temp_json)['result']: 65 return 66 67 def _run_command(command): 68 _LOG.debug("Running: `%s`", " ".join(command)) 69 result = subprocess.run(command, capture_output=True) 70 _LOG.debug("Output:\n%s", 71 result.stdout.decode() + result.stderr.decode()) 72 73 _run_command(["cipd", "init", "-force", core_cache_path.as_posix()]) 74 _run_command([ 75 "cipd", "install", cipd_package_subpath, "-root", 76 core_cache_path.as_posix(), "-force" 77 ]) 78 79 _LOG.debug( 80 "Available Cache Files:\n%s", 81 "\n".join([p.as_posix() for p in core_cache_path.glob("*")])) 82 83 def install(self, path: Path) -> None: 84 self.populate_download_cache_from_cipd(path) 85 86 if self.status(path): 87 return 88 # Otherwise delete current version and reinstall 89 core_installer.install_core(path.parent.resolve().as_posix(), 90 self.name) 91 92 def info(self, path: Path) -> Sequence[str]: 93 packages_root = path.parent.resolve() 94 arduino_package_path = path 95 arduino_package_name = None 96 97 message = [ 98 f'{self.name} currently installed in: {path}', 99 ] 100 # Make gn args sample copy/paste-able by omitting the starting timestamp 101 # and INF log on each line. 102 message_gn_args = [ 103 'Enable by running "gn args out" and adding these lines:', 104 f' pw_arduino_build_CORE_PATH = "{packages_root}"', 105 f' pw_arduino_build_CORE_NAME = "{self.name}"' 106 ] 107 108 # Search for first valid 'package/version' directory 109 for hardware_dir in [ 110 path for path in (path / 'hardware').iterdir() 111 if path.is_dir() 112 ]: 113 if path.name in ["arduino", "tools"]: 114 continue 115 for subdir in [ 116 path for path in hardware_dir.iterdir() if path.is_dir() 117 ]: 118 if subdir.name == 'avr' or re.match(r'[0-9.]+', subdir.name): 119 arduino_package_name = f'{hardware_dir.name}/{subdir.name}' 120 break 121 122 if arduino_package_name: 123 message_gn_args += [ 124 f' pw_arduino_build_PACKAGE_NAME = "{arduino_package_name}"', 125 ' pw_arduino_build_BOARD = "BOARD_NAME"' 126 ] 127 message += ["\n".join(message_gn_args)] 128 message += [ 129 'Where BOARD_NAME is any supported board.', 130 # Have arduino_builder command appear on it's own line. 131 'List available boards by running:\n' 132 ' arduino_builder ' 133 f'--arduino-package-path {arduino_package_path} ' 134 f'--arduino-package-name {arduino_package_name} list-boards' 135 ] 136 return message 137 138 139for arduino_core_name in core_installer.supported_cores(): 140 pw_package.package_manager.register(ArduinoCore, name=arduino_core_name) 141