• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/usr/bin/env python3
2# -*- coding: utf-8 -*-
3
4# Copyright (c) 2024 Huawei Device Co., Ltd.
5# Licensed under the Apache License, Version 2.0 (the "License");
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9# http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16#
17
18import os
19import logging
20import shutil
21import json
22from pathlib import Path
23from typing import Optional, Iterable, Tuple
24from abc import ABC, abstractmethod
25from enum import Flag, auto
26from subprocess import TimeoutExpired
27from vmb.unit import BenchUnit
28from vmb.helpers import StringEnum
29from vmb.shell import ShellDevice, ShellUnix, ShellResult
30from vmb.target import Target
31from vmb.x_shell import CrossShell
32
33log = logging.getLogger('vmb')
34
35
36class VmbToolExecError(Exception):
37    """VMB Error. Tool execution failed."""
38
39    def __init__(self, message: str,
40                 res: Optional[ShellResult] = None) -> None:
41        super().__init__(message)
42        self.out = f'{message}\n\nout:\n{res.out}\n\nerr:\n{res.err}\n' \
43                   if res is not None else message
44
45
46class ToolMode(StringEnum):
47    AOT = 'aot'
48    INT = 'int'
49    JIT = 'jit'
50    DEFAULT = 'default'
51
52
53class OptFlags(Flag):
54    NONE = auto()
55    GC_STATS = auto()
56    JIT_STATS = auto()
57    AOT_STATS = auto()
58    AOT_SKIP_LIBS = auto()
59    DRY_RUN = auto()
60    DISABLE_INLINING = auto()
61    # these 3 flags are mutually exclusive (this is guarantied by ToolMode)
62    AOT = auto()
63    INT = auto()
64    JIT = auto()
65
66
67class ToolBase(CrossShell, ABC):
68
69    sh_: ShellUnix
70    andb_: ShellDevice
71    hdc_: ShellDevice
72    dev_dir: Path
73    libs: Path
74
75    def __init__(self,
76                 target: Target = Target.HOST,
77                 flags: OptFlags = OptFlags.NONE,
78                 custom: str = ''):
79        self._target = target
80        self.flags = flags
81        self.custom = custom
82
83    def __call__(self, bu: BenchUnit) -> None:
84        self.exec(bu)
85
86    @property
87    @abstractmethod
88    def name(self) -> str:
89        return ''
90
91    @property
92    def version(self) -> str:
93        return 'version n/a'
94
95    @property
96    def target(self) -> Target:
97        return self._target
98
99    @property
100    def sh(self) -> ShellUnix:
101        return ToolBase.sh_
102
103    @property
104    def andb(self) -> ShellDevice:
105        return ToolBase.andb_
106
107    @property
108    def hdc(self) -> ShellDevice:
109        return ToolBase.hdc_
110
111    @staticmethod
112    def rename_suffix(old: Path, new_suffix: str) -> Path:
113        new = old.with_suffix(new_suffix)
114        old.rename(new)
115        return new
116
117    @staticmethod
118    def get_cmd_path(cmd: str, env_var: str = '') -> Optional[str]:
119        # use specifically requested
120        p: Optional[str] = os.environ.get(env_var, '')
121        # . or use default
122        if not p:
123            p = shutil.which(cmd)
124        if not p or (not os.path.isfile(p)):
125            extra_msg = f' or set via {env_var} env var' if env_var else ''
126            raise RuntimeError(
127                f'{cmd} not found. Add it to PATH{extra_msg}')
128        log.info('Using %s as %s', p, cmd)
129        return p
130
131    @staticmethod
132    def ensure_file(*args, err: str = '') -> str:
133        f = os.path.join(*args)
134        if not os.path.isfile(f):
135            raise RuntimeError(f'File "{f}" not found! {err}')
136        return str(f)
137
138    @staticmethod
139    def ensure_dir(*args, err: str = '') -> str:
140        d = os.path.join(*args)
141        if not os.path.isdir(d):
142            raise RuntimeError(f'Dir "{d}" not found! {err}')
143        return str(d)
144
145    @staticmethod
146    def ensure_dir_env(var_name: str) -> str:
147        return ToolBase.ensure_dir(os.environ.get(var_name, ''),
148                                   err=f'Please set {var_name} env var.')
149
150    @abstractmethod
151    def exec(self, bu: BenchUnit) -> None:
152        pass
153
154    def kill(self) -> None:
155        """Kill tool process(es).
156
157        For host target there is os.killpg() in Shell,
158        but on device tool process needs remote pkill <tool>
159        """
160
161    def x_run(self, cmd: str, measure_time: bool = True,
162              timeout: Optional[float] = None, cwd: str = '') -> ShellResult:
163        try:
164            res = self.x_sh.run(
165                cmd, measure_time=measure_time, timeout=timeout, cwd=cwd)
166        except TimeoutExpired as e:
167            self.kill()
168            raise e
169        if not res or res.ret != 0:
170            raise VmbToolExecError(f'{self.name} failed', res)
171        return res
172
173    def x_src(self, bu: BenchUnit, *ext) -> Path:
174        if self.target == Target.HOST:
175            return bu.src(*ext)
176        return bu.src_device(*ext)
177
178    def x_libs(self, bu: BenchUnit, *ext) -> Iterable[Path]:
179        if self.target == Target.HOST:
180            return bu.libs(*ext)
181        return bu.libs_device(*ext)
182
183    def get_bu_opts(self, bu: BenchUnit) -> Tuple[OptFlags, str]:
184        conf = bu.path.joinpath('config.json')
185        flags: OptFlags = self.flags
186        aot_opts: str = ''
187        if conf.exists():
188            with open(conf, 'r', encoding='utf-8') as f:
189                conf_data = json.load(f)
190                if conf_data.get('disable_inlining', False):
191                    flags |= OptFlags.DISABLE_INLINING
192                aot_opts = conf_data.get('aot_opts', '')
193        return flags, aot_opts
194