1#!/usr/bin/env python 2# -*- coding: utf-8 -*- 3""" 4Copyright (C) 2022 Huawei Device Co., Ltd. 5SPDX-License-Identifier: GPL-2.0 6 7Unless required by applicable law or agreed to in writing, software 8distributed under the License is distributed on an "AS IS" BASIS, 9WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10See the License for the specific language governing permissions and 11limitations under the License. 12""" 13 14import logging 15import os 16import re 17import select 18import sys 19import subprocess 20import shlex 21import time 22 23 24ignores = [ 25 "include/trace/events/eas_sched.h: warning: format '%d' expects argument of type 'int', but argument 9 has type 'long unsigned int' [-Wformat=]", 26 "drivers/block/zram/zram_drv.c: warning: left shift count >= width of type", 27 "include/trace/events/eas_sched.h: note: in expansion of macro", 28 "include/trace/events/eas_sched.h: note: format string is defined here", 29 "include/trace/trace_events.h: note: in expansion of macro", 30 "mm/vmscan.c: warning: suggest parentheses around assignment used as truth value [-Wparentheses]", 31 "lib/bitfield_kunit.c: warning: the frame size of \d+ bytes is larger than \d+ bytes", 32 "drivers/mmc/host/sdhci-esdhc-imx.c: warning: 'sdhci_esdhc_imx_probe_nondt' defined but not used", 33] 34 35 36class Reporter: 37 def __init__(self, arch, path): 38 self.arch = arch 39 self.path = path 40 41 42 def normpath(self, filename): 43 if re.search("^[\.]+[^/]", filename): 44 filename = re.sub("^[\.]+", "", filename) 45 return os.path.normpath(filename) 46 47 48 def format_title(self, title): 49 title = re.sub("\u2018", "'", title) 50 title = re.sub("\u2019", "'", title) 51 52 return title.strip() 53 54 55 def report_build_warning(self, filename, regex, details): 56 if len(details) == 0 or filename is None: 57 return None 58 59 if not os.path.exists(os.path.join(self.path, filename)): 60 return None 61 62 line = details[0] 63 try: 64 warning = re.search(regex, line).group(0) 65 except: 66 print('Except>>>', details) 67 return 68 69 report = { 70 'title': self.format_title("%s: %s" % (filename, warning)), 71 'filename': filename, 72 'report': '\n'.join(details), 73 'nr': line.split(':')[1], 74 } 75 76 return report 77 78 79 def parse_build_warning(self, blocks, regex, title_regex): 80 issues = {} 81 reports = [] 82 details = [] 83 filename = None 84 unused = False 85 86 for line in blocks: 87 attrs = line.split(':') 88 if len(attrs) < 5 and filename is None: 89 continue 90 if line.startswith(' ') or len(attrs) < 2: 91 if unused is True: 92 details.append(line) 93 continue 94 if not regex in line: 95 unused = False 96 continue 97 unused = True 98 newfile = self.normpath(attrs[0]) 99 if newfile != filename: 100 if len(details) and filename: 101 if filename in issues: 102 issues[filename].extend(details) 103 else: 104 issues[filename] = details 105 filename = newfile 106 details = [] 107 108 details.append(line) 109 110 if len(details) and filename: 111 if filename in issues: 112 issues[filename].extend(details) 113 else: 114 issues[filename] = details 115 116 for filename, details in issues.items(): 117 report = self.report_build_warning(filename, title_regex, details) 118 if not report is None: 119 reports.append(report) 120 121 return reports 122 123 124 def parse(self, content): 125 blocks = content.split('\n') 126 reports = [] 127 128 patterns = ( 129 ('[-Wunused-but-set-variable]', 'warning: .* set but not used'), 130 ('[-Wunused-but-set-parameter]', 'warning: .* set but not used'), 131 ('[-Wunused-const-variable=]', 'warning: .* defined but not used'), 132 ('[-Wold-style-definition]', 'warning: .* definition'), 133 ('[-Wold-style-declaration]', 'warning: .* declaration'), 134 ('[-Wmaybe-uninitialized]', 'warning: .* uninitialized'), 135 ('[-Wtype-limits]', 'warning: .* always (false|true)'), 136 ('[-Wunused-function]', 'warning: .* defined but not used'), 137 ('[-Wsequence-point]', 'warning: .* may be undefined'), 138 ('[-Wformat=]', 'warning: format.*'), 139 ('[-Wunused-variable]', 'warning: [^\[]*'), 140 ('[-Wframe-larger-than=]', 'warning: the frame size [^\[]*'), 141 ('[-Wshift-count-overflow]', 'warning: left shift count >= width of type'), 142 ('definition or declaration', 'warning: .* declared inside parameter list will not be visible outside of this definition or declaration'), 143 ('character', 'warning: missing terminating .* character'), 144 ('in expansion of macro', 'note: in expansion of macro'), 145 ('note: format string is defined here', 'note: format string is defined here'), 146 ('[-Wparentheses]', 'suggest parentheses around assignment used as truth value'), 147 ) 148 149 for regex, title_regex in patterns: 150 items = self.parse_build_warning(blocks, regex, title_regex) 151 if items is None: 152 continue 153 154 reports.extend(items) 155 156 return reports 157 158 159def exec_cmd(command_list, shell=False, show_output=False, cwd=None): 160 if isinstance(command_list, str): 161 command_list = shlex.split(command_list) 162 elif not isinstance(command_list, list): 163 raise f"command_list to exec_cmd need to be a list or string" 164 command_list = ['nice'] + [str(s) for s in command_list] 165 166 print(f"cwd: '{cwd}'") 167 print(f"cmd: '{command_list}'") 168 start = time.time() 169 proc = subprocess.Popen( 170 command_list if not shell else ' '.join(command_list), 171 cwd=cwd, 172 shell=shell, 173 stdout=subprocess.PIPE, 174 stderr=subprocess.PIPE, 175 bufsize=1, 176 universal_newlines=True, 177 ) 178 179 outmsg = "" 180 errmsg = "" 181 poller = select.epoll() 182 poller.register(proc.stdout, select.EPOLLIN) 183 poller.register(proc.stderr, select.EPOLLIN) 184 while proc.poll() is None: 185 for fd, event in poller.poll(): 186 if event is not select.EPOLLIN: 187 continue 188 if fd == proc.stdout.fileno(): 189 line = proc.stdout.readline() 190 if show_output is True: 191 print(">> [stdout] %s", line.strip('\n')) 192 outmsg += line 193 elif fd == proc.stderr.fileno(): 194 line = proc.stderr.readline() 195 if show_output is True: 196 print(">> [stderr] %s", line.strip('\n')) 197 errmsg += line 198 199 for line in proc.stdout.readlines(): 200 if show_output is True: 201 print(">> [stdout] %s", line.strip('\n')) 202 outmsg += line 203 for line in proc.stderr.readlines(): 204 if show_output is True: 205 print(">> [stderr] %s", line.strip('\n')) 206 errmsg += line 207 208 ret = proc.wait() 209 print(f"Returned {ret} in {int(time.time() - start)} seconds") 210 211 return outmsg, errmsg, ret 212 213 214def make_cmd(cmd, arch, cross_compile, knl_path): 215 make = f"{cmd} ARCH={arch} CROSS_COMPILE={cross_compile}" 216 outmsg, errmsg, ret = exec_cmd(make, cwd=knl_path) 217 if ret: 218 print(f'"{make}" errors --> \n {errmsg}') 219 return ret, f'"{make}" errors --> \n {errmsg}' 220 221 return ret, f'"{make}" success!' 222 223 224def make_config(arch, config, corss_compile, knl_path): 225 make = f"make {config} ARCH={arch} CROSS_COMPILE={corss_compile}" 226 outmsg, errmsg, ret = exec_cmd(make, cwd=knl_path) 227 if ret: 228 print(f'"{make}" errors --> \n {errmsg}') 229 return ret, f'"{make}" errors --> \n {errmsg}' 230 231 return ret, f'"{make}" success!' 232 233 234def make_j(arch, cross_compile, knl_path): 235 make = f'make -j{os.cpu_count()} ARCH={arch} CROSS_COMPILE={cross_compile}' 236 outmsg, errmsg, ret = exec_cmd(make, cwd=knl_path) 237 if ret: 238 print(f'"{make}" errors --> \n {errmsg}') 239 return ret, f'"{make}" errors --> \n {errmsg}' 240 elif len(errmsg) > 0: 241 print(f'"{make}" warnings --> \n {errmsg}') 242 result = "success" 243 reporter = Reporter(arch, knl_path) 244 known_issue = "\nKnown issue:\n" 245 for report in reporter.parse(errmsg): 246 if ignores and [i for i in ignores if re.match(i, report['title'])]: 247 known_issue = known_issue + report['title'] + "\n" 248 known_issue = known_issue + report['report'] + "\n" 249 continue 250 result = 'failed' 251 252 print(known_issue) 253 new_issue = "\nNew Issue:\n" 254 if result == "failed": 255 for report in reporter.parse(errmsg): 256 if ignores and [i for i in ignores if re.match(i, report['title'])]: 257 continue 258 new_issue = new_issue + report['title'] + "\n" 259 new_issue = new_issue + report['report'] + "\n" 260 print(new_issue) 261 return 2, f'"{make}" warning --> \n {new_issue}' 262 263 return ret, f'"{make}" warnings in ignores --> \n {known_issue}' 264 265 return ret, f'"{make}" success!' 266 267 268def cp_config(arch, config, config_path, knl_path): 269 if os.path.exists(config_path.format(arch, config)): 270 cp = f'cp ' + config_path.format(arch, config) + ' ' + os.path.join(knl_path, 'arch', arch, 'configs', config) 271 outmsg, errmsg, ret = exec_cmd(cp) 272 if ret: 273 print(f'"{cp}" errors --> \n {errmsg}') 274 return ret, f'"{cp}" errors --> \n {errmsg}' 275 else: 276 print(f'"{config_path.format(arch, config)}" not exists!') 277 return ret, f'"{config_path.format(arch, config)}" not exists!' 278 279 return ret, f'"{cp}" success!' 280 281 282def get_logger(filename): 283 log_format = '%(asctime)s %(name)s %(levelname)s %(message)s' 284 log_date_format = '%Y-%m-%d %H:%M:%S' 285 logging.basicConfig( 286 level=logging.INFO, 287 filename=filename, 288 format=log_format, 289 datefmt=log_date_format 290 ) 291 logger = logging.getLogger(__name__) 292 return logger 293 294 295def build(arch, config, config_path, cross_compile, knl_path, logger): 296 ret, msg = make_cmd('make defconfig', arch, cross_compile, knl_path) 297 if ret: 298 logger.error(msg) 299 return ret, msg 300 301 ret, msg = make_cmd('make oldconfig', arch, cross_compile, knl_path) 302 if ret: 303 logger.error(msg) 304 return ret, msg 305 306 ret, msg = make_cmd('make clean', arch, cross_compile, knl_path) 307 if ret: 308 logger.error(msg) 309 return ret, msg 310 311 ret, msg = make_j(arch, cross_compile, knl_path) 312 if ret: 313 logger.error(msg) 314 return ret, msg 315 316 ret, msg = cp_config(arch, config, config_path, knl_path) 317 if ret: 318 logger.error(msg) 319 return ret, msg 320 else: 321 ret, msg = make_config(arch, config, cross_compile, knl_path) 322 if ret: 323 logger.error(msg) 324 return ret, msg 325 326 ret, msg = make_cmd('make clean', arch, cross_compile, knl_path) 327 if ret: 328 logger.error(msg) 329 return ret, msg 330 331 ret, msg = make_j(arch, cross_compile, knl_path) 332 if ret: 333 logger.error(msg) 334 return ret, msg 335 336 ret, msg = make_cmd('make allmodconfig', arch, cross_compile, knl_path) 337 if ret: 338 logger.error(msg) 339 return ret, msg 340 341 sed = f'sed -i s/^.*CONFIG_FRAME_WARN.*$/CONFIG_FRAME_WARN=2048/ .config' 342 outmsg, errmsg, ret = exec_cmd(sed, cwd=knl_path) 343 344 ret, msg = make_cmd('make clean', arch, cross_compile, knl_path) 345 if ret: 346 logger.error(msg) 347 return ret, msg 348 349 ret, msg = make_j(arch, cross_compile, knl_path) 350 if ret: 351 logger.error(msg) 352 return ret, msg 353 354 return 0, f'build success!' 355 356 357def main(): 358 config_path = './kernel/linux/config/linux-5.10/arch/{0}/configs/{1}' 359 knl_path = './kernel/linux/linux-5.10' 360 log_path = os.getcwd() 361 now_date = time.strftime("%Y%m%d%H%M%S", time.localtime()) 362 log_file = os.path.join(log_path, 'kernel_build_test.log') 363 logger = get_logger(log_file) 364 365 arch = 'arm' 366 config = 'hispark_taurus_standard_defconfig' 367 cross_compile = '../../../prebuilts/gcc/linux-x86/arm/gcc-linaro-7.5.0-arm-linux-gnueabi/bin/arm-linux-gnueabi-' 368 arm_ret, arm_msg = build(arch, config, config_path, cross_compile, knl_path, logger) 369 370 arch = 'arm64' 371 config = 'rk3568_standard_defconfig' 372 cross_compile = '../../../prebuilts/gcc/linux-x86/aarch64/gcc-linaro-7.5.0-2019.12-x86_64_aarch64-linux-gnu/bin/aarch64-linux-gnu-' 373 arm64_ret, arm64_msg = build(arch, config, config_path, cross_compile, knl_path, logger) 374 375 print(f'arm_ret: {arm_ret}, arm64_ret: {arm64_ret}') 376 if any([arm_ret, arm64_ret]): 377 print('kernel build test failed!') 378 exit(arm_ret or arm64_ret) 379 380 print('kernel build test success.') 381 exit(0) 382 383 384if __name__ == "__main__": 385 main() 386