• 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    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