• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/usr/bin/env python3
2# -*- coding: utf-8 -*-
3
4#
5# Copyright (c) 2023 Huawei Device Co., Ltd.
6# Licensed under the Apache License, Version 2.0 (the "License");
7# you may not use this file except in compliance with the License.
8# You may obtain a copy of the License at
9#
10#     http://www.apache.org/licenses/LICENSE-2.0
11#
12# Unless required by applicable law or agreed to in writing, software
13# distributed under the License is distributed on an "AS IS" BASIS,
14# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15# See the License for the specific language governing permissions and
16# limitations under the License.
17#
18
19import sys
20import re
21import os
22
23from containers.colors import Colors
24from hb.helper.no_instance import NoInstance
25from resources.global_var import STATUS_FILE
26from util.io_util import IoUtil
27from exceptions.ohos_exception import OHOSException
28
29
30class LogLevel():
31    INFO = 0
32    WARNING = 1
33    ERROR = 2
34    DEBUG = 3
35
36
37class LogUtil(metaclass=NoInstance):
38    # static member for store current stage
39    stage = ""
40
41    @staticmethod
42    def set_stage(stage):
43        LogUtil.stage = stage
44
45    @staticmethod
46    def clear_stage():
47        LogUtil.stage = ""
48
49    @staticmethod
50    def hb_info(msg, stage='', mode='normal'):
51        level = 'info'
52        if not stage:
53            stage = LogUtil.stage
54        if mode == 'silent':
55            for line in str(msg).splitlines():
56                sys.stdout.write('\033[K')
57                sys.stdout.write(
58                    '\r' + (LogUtil.message(level, line, stage)).strip('\n'))
59                sys.stdout.flush()
60        elif mode == 'normal':
61            level = 'info'
62            for line in str(msg).splitlines():
63                sys.stdout.write(LogUtil.message(level, line, stage))
64                sys.stdout.flush()
65
66    @staticmethod
67    def hb_warning(msg, stage=''):
68        level = 'warning'
69        if not stage:
70            stage = LogUtil.stage
71        for line in str(msg).splitlines():
72            sys.stderr.write(LogUtil.message(level, line, stage))
73            sys.stderr.flush()
74
75    @staticmethod
76    def hb_error(msg, stage=''):
77        level = 'error'
78        if not stage:
79            stage = LogUtil.stage
80        sys.stderr.write('\n')
81        for line in str(msg).splitlines():
82            sys.stderr.write(LogUtil.message(level, line, stage))
83            sys.stderr.flush()
84
85    @staticmethod
86    def message(level, msg, stage=''):
87        if isinstance(msg, str) and not msg.endswith('\n'):
88            msg += '\n'
89        if level == 'error':
90            msg = msg.replace('error:', f'{Colors.ERROR}error{Colors.END}:')
91            return f'{Colors.ERROR}[OHOS {level.upper()}]{Colors.END} {stage} {msg}'
92        elif level == 'info':
93            return f'[OHOS {level.upper()}] {stage} {msg}'
94        else:
95            return f'{Colors.WARNING}[OHOS {level.upper()}]{Colors.END} {stage} {msg}'
96
97    @staticmethod
98    def write_log(log_path, msg, level):
99        os.makedirs(os.path.dirname(log_path), exist_ok=True)
100        sys.stderr.write('\n')
101        with open(log_path, 'at', encoding='utf-8') as log_file:
102            for line in str(msg).splitlines():
103                sys.stderr.write(LogUtil.message(level, line, LogUtil.stage))
104                sys.stderr.flush()
105                log_file.write(LogUtil.message(level, line, LogUtil.stage))
106
107    @staticmethod
108    def analyze_build_error(error_log, status_code_prefix):
109        with open(error_log, 'rt', encoding='utf-8') as log_file:
110            data = log_file.read()
111            status_file = IoUtil.read_json_file(STATUS_FILE)
112            choices = []
113            status_map = {}
114            for status_code, status in status_file.items():
115                if not status_code.startswith(status_code_prefix):
116                    continue
117                if isinstance(status, dict) and status.get('pattern'):
118                    choices.append(status['pattern'])
119                    status_map[status['pattern']] = status.get('code')
120            best_match = None
121            best_ratio = 0
122            for choice in choices:
123                pattern = re.compile(choice, re.DOTALL)
124                match = pattern.search(data)
125                if not match:
126                    continue
127                ratio = len(match.group()) / len(data)
128                if ratio > best_ratio:
129                    best_ratio = ratio
130                    best_match = choice
131            return_status_code = status_map.get(
132                best_match) if best_match else f'{status_code_prefix}000'
133        return return_status_code
134
135    @staticmethod
136    def get_gn_failed_log(log_path):
137        error_log = os.path.join(os.path.dirname(log_path), 'error.log')
138        is_gn_failed = False
139        with open(log_path, 'rt', encoding='utf-8') as log_file:
140            lines = log_file.readlines()
141        error_lines = []
142        for i, line in enumerate(lines):
143            if line.startswith('ERROR at'):
144                error_lines.extend(lines[i: i + 50])
145                is_gn_failed = True
146                break
147        for log in error_lines[:50]:
148            LogUtil.hb_error(log)
149            with open(error_log, 'at', encoding='utf-8') as log_file:
150                log_file.write(log + '\n')
151        if is_gn_failed:
152            return_status_code = LogUtil.analyze_build_error(error_log, '3')
153            raise OHOSException(
154                'GN Failed! Please check error in {}, and for more build information in {}'.format(
155                    error_log, log_path), return_status_code)
156
157    @staticmethod
158    def get_ninja_failed_log(log_path):
159        error_log = os.path.join(os.path.dirname(log_path), 'error.log')
160        is_ninja_failed = False
161        with open(log_path, 'rt', encoding='utf-8') as log_file:
162            data = log_file.read()
163        failed_pattern = re.compile(r'(ninja: error:.*?)\n', re.DOTALL)
164        failed_log = failed_pattern.findall(data)
165        if failed_log:
166            is_ninja_failed = True
167        for log in failed_log:
168            LogUtil.hb_error(log)
169            with open(error_log, 'at', encoding='utf-8') as log_file:
170                log_file.write(log)
171        if is_ninja_failed:
172            return_status_code = LogUtil.analyze_build_error(error_log, '4')
173            raise OHOSException(
174                'NINJA Failed! Please check error in {}, and for more build information in {}'.format(
175                    error_log, log_path), return_status_code)
176
177    @staticmethod
178    def get_compiler_failed_log(log_path):
179        error_log = os.path.join(os.path.dirname(log_path), 'error.log')
180        is_compiler_failed = False
181        with open(log_path, 'rt', encoding='utf-8') as log_file:
182            data = log_file.read()
183        failed_pattern = re.compile(
184            r'(\[\d+/\d+\].*?)(?=\[\d+/\d+\]|'
185            'ninja: build stopped)', re.DOTALL)
186        failed_log = failed_pattern.findall(data)
187        if failed_log:
188            is_compiler_failed = True
189        for log in failed_log:
190            if 'FAILED:' in log:
191                LogUtil.hb_error(log)
192                with open(error_log, 'at', encoding='utf-8') as log_file:
193                    log_file.write(log)
194        if is_compiler_failed:
195            return_status_code = LogUtil.analyze_build_error(error_log, '4')
196            raise OHOSException(
197                'COMPILE Failed! Please check error in {}, and for more build information in {}'.format(
198                    error_log, log_path), return_status_code)
199
200    @staticmethod
201    def get_failed_log(log_path):
202        last_error_log = os.path.join(os.path.dirname(log_path), 'error.log')
203        if os.path.exists(last_error_log):
204            mtime = os.stat(last_error_log).st_mtime
205            os.rename(
206                last_error_log, '{}/error.{}.log'.format(os.path.dirname(last_error_log), mtime))
207        LogUtil.get_gn_failed_log(log_path)
208        LogUtil.get_ninja_failed_log(log_path)
209        LogUtil.get_compiler_failed_log(log_path)
210        raise OHOSException(
211            'BUILD Failed! Please check build log for more information: {}'.format(log_path))
212