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 34 def __init__(self, core_name, *args, **kwargs): 35 super().__init__(*args, name=core_name, **kwargs) 36 37 def status(self, path: Path) -> bool: 38 return (path / 'hardware').is_dir() 39 40 def populate_download_cache_from_cipd(self, path: Path) -> None: 41 """Check for arduino core availability in pigweed_internal cipd.""" 42 package_path = path.parent.resolve() 43 core_name = self.name 44 core_cache_path = package_path / '.cache' / core_name 45 core_cache_path.mkdir(parents=True, exist_ok=True) 46 47 cipd_package_subpath = 'pigweed_internal/third_party/' 48 cipd_package_subpath += core_name 49 cipd_package_subpath += '/${platform}' 50 51 def _run_command(command): 52 _LOG.debug('Running: `%s`', ' '.join(command)) 53 result = subprocess.run(command, capture_output=True) 54 _LOG.debug( 55 'Output:\n%s', result.stdout.decode() + result.stderr.decode() 56 ) 57 58 # Check if teensy cipd package is readable 59 with tempfile.NamedTemporaryFile( 60 prefix='cipd', delete=True, dir=core_cache_path 61 ) as temp_json: 62 temp_json_path = Path(temp_json.name) 63 cipd_acl_check_command = [ 64 'cipd', 65 'acl-check', 66 cipd_package_subpath, 67 '-reader', 68 '-json-output', 69 str(temp_json_path), 70 ] 71 72 _run_command(cipd_acl_check_command) 73 74 # Return if cipd_package_subpath does not exist or is not readable 75 # by the current user. 76 if not temp_json_path.is_file(): 77 # Return and proceed with normal installation. 78 return 79 result_text = temp_json_path.read_text() 80 result_dict = json.loads(result_text) 81 if 'result' not in result_dict: 82 # Return and proceed with normal installation. 83 return 84 85 _run_command(['cipd', 'init', '-force', core_cache_path.as_posix()]) 86 _run_command( 87 [ 88 'cipd', 89 'install', 90 cipd_package_subpath, 91 '-root', 92 core_cache_path.as_posix(), 93 '-force', 94 ] 95 ) 96 97 _LOG.debug( 98 'Available Cache Files:\n%s', 99 '\n'.join([p.as_posix() for p in core_cache_path.glob('*')]), 100 ) 101 102 def install(self, path: Path) -> None: 103 self.populate_download_cache_from_cipd(path) 104 105 if self.status(path): 106 return 107 # Otherwise delete current version and reinstall 108 core_installer.install_core(path.parent.resolve().as_posix(), self.name) 109 110 def info(self, path: Path) -> Sequence[str]: 111 packages_root = path.parent.resolve() 112 arduino_package_path = path 113 arduino_package_name = None 114 115 message = [ 116 f'{self.name} currently installed in: {path}', 117 ] 118 # Make gn args sample copy/paste-able by omitting the starting timestamp 119 # and INF log on each line. 120 message_gn_args = [ 121 'Enable by running "gn args out" and adding these lines:', 122 f' pw_arduino_build_CORE_PATH = "{packages_root}"', 123 f' pw_arduino_build_CORE_NAME = "{self.name}"', 124 ] 125 126 # Search for first valid 'package/version' directory 127 for hardware_dir in [ 128 path for path in (path / 'hardware').iterdir() if path.is_dir() 129 ]: 130 if path.name in ['arduino', 'tools']: 131 continue 132 for subdir in [ 133 path for path in hardware_dir.iterdir() if path.is_dir() 134 ]: 135 if subdir.name == 'avr' or re.match(r'[0-9.]+', subdir.name): 136 arduino_package_name = f'{hardware_dir.name}/{subdir.name}' 137 break 138 139 if arduino_package_name: 140 message_gn_args += [ 141 f' pw_arduino_build_PACKAGE_NAME = "{arduino_package_name}"', 142 ' pw_arduino_build_BOARD = "BOARD_NAME"', 143 ] 144 message += ['\n'.join(message_gn_args)] 145 message += [ 146 'Where BOARD_NAME is any supported board.', 147 # Have arduino_builder command appear on it's own line. 148 'List available boards by running:\n' 149 ' arduino_builder ' 150 f'--arduino-package-path {arduino_package_path} ' 151 f'--arduino-package-name {arduino_package_name} list-boards', 152 ] 153 return message 154 155 156for arduino_core_name in core_installer.supported_cores(): 157 pw_package.package_manager.register( 158 ArduinoCore, core_name=arduino_core_name 159 ) 160