• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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