Skip to content

Commit f7ee341

Browse files
committed
[action/ci] add qemu-auto-test yml
1 parent 0c08453 commit f7ee341

File tree

2 files changed

+246
-1
lines changed

2 files changed

+246
-1
lines changed

bsp/qemu-vexpress-a9/.ci/attachconfig/ci.attachconfig.yml

+34
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,36 @@
1+
bsp_board_info:
2+
arch: arm
3+
toolchain: arm-none-eabi-gcc
4+
pre_build: |
5+
scons -c
6+
qemu-system-arm --version
7+
build_cmd: |
8+
scons -j8
9+
post_build: |
10+
scons --version
11+
qemu-system-arm --version
12+
run_cmd: ./qemu-nographic.sh
13+
qemu_flag: true
14+
pkg.tools.coremark:
15+
kconfig:
16+
- CONFIG_PKG_USING_COREMARK=y
17+
- CONFIG_COREMARK_ITERATIONS=36000
18+
pre_build: |
19+
scons -c
20+
qemu-system-arm --version
21+
build_cmd: |
22+
scons -j8
23+
post_build: |
24+
scons --version
25+
qemu-system-arm --version
26+
ci_build_run_flag : true
27+
buildcheckresult: "core_main" #检查编译的log中是否有匹配字
28+
msh_cmd: |
29+
ps
30+
version
31+
core_mark
32+
msh_cmd_timeout: 60
33+
checkresult: 'CoreMark 1.0' #检查执行过程中的log是否有匹配字
134
# ------ PERIPHERAL CI ------
235
peripheral.EMAC:
336
kconfig:
@@ -21,3 +54,4 @@ online-packages.ai.llmchat:
2154
- CONFIG_BSP_DRV_EMAC=y
2255
- CONFIG_PKG_USING_LLMCHAT=y
2356
- CONFIG_PKG_LLM_API_KEY="sk-xxxxxx"
57+

tools/ci/bsp_buildings.py

+212-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
import subprocess
2+
import threading
3+
import time
4+
import logging
5+
import sys
16
import os
27
import shutil
38
import re
@@ -158,7 +163,137 @@ def build_bsp_attachconfig(bsp, attach_file):
158163

159164
return res
160165

166+
# 配置日志
167+
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s: %(message)s')
168+
logger = logging.getLogger(__name__)
169+
170+
class QemuManager:
171+
def __init__(self, qemu_cmd, idle_timeout=5, checkresult=None):
172+
"""
173+
初始化QEMU管理器
174+
:param qemu_cmd: QEMU启动命令
175+
:param idle_timeout: 日志空闲超时时间(秒)
176+
"""
177+
self.qemu_cmd = qemu_cmd
178+
self.idle_timeout = idle_timeout
179+
self.qemu_process = None
180+
self.log_thread = None
181+
self.checkresult = checkresult
182+
self.last_log_time = time.time()
183+
self.logs = []
184+
self.running = False
185+
self.checkresult_found = False # 标记是否找到checkresult
186+
def start_qemu(self):
187+
"""启动QEMU进程"""
188+
logger.info("Starting QEMU...")
189+
self.qemu_process = subprocess.Popen(
190+
self.qemu_cmd,
191+
stdin=subprocess.PIPE,
192+
stdout=subprocess.PIPE,
193+
stderr=subprocess.PIPE,
194+
shell=True,
195+
bufsize=0,
196+
text=True
197+
)
198+
self.running = True
199+
logger.info("QEMU started successfully.")
200+
201+
def log_monitor(self):
202+
"""监控QEMU输出日志"""
203+
logger.info("Starting log monitor...")
204+
while self.running:
205+
line = self.qemu_process.stdout.readline()
206+
if line:
207+
line = line.strip()
208+
self.logs.append(line)
209+
self.last_log_time = time.time() # 更新最后日志时间
210+
logger.info(f"QEMU Output: {line}") # 实时打印日志
211+
# 检查是否包含checkresult
212+
if self.checkresult and self.checkresult in line:
213+
logger.info(f"Checkresult '{self.checkresult}' found in logs. Success...")
214+
self.checkresult_found = True
215+
#self.running = False # 停止监控
216+
#break
217+
else:
218+
time.sleep(0.1)
219+
220+
def send_command(self, command):
221+
"""向QEMU发送命令"""
222+
if not self.running or not self.qemu_process:
223+
logger.error("QEMU is not running.")
224+
return False
225+
226+
logger.info(f"Sending command: {command}")
227+
try:
228+
self.qemu_process.stdin.write(command + "\n")
229+
self.qemu_process.stdin.flush()
230+
return True
231+
except Exception as e:
232+
logger.error(f"Failed to send command: {e}")
233+
return False
234+
235+
def stop_qemu(self):
236+
"""停止QEMU进程"""
237+
if self.qemu_process:
238+
logger.info("Stopping QEMU...")
239+
self.running = False
240+
self.qemu_process.terminate()
241+
self.qemu_process.wait(timeout=5)
242+
logger.info("QEMU stopped.")
243+
244+
245+
246+
def run(self,commands):
247+
"""主运行逻辑"""
248+
try:
249+
# 启动QEMU
250+
self.start_qemu()
251+
252+
# 启动日志监控线程
253+
self.log_thread = threading.Thread(target=self.log_monitor, daemon=True)
254+
self.log_thread.start()
255+
256+
# 等待QEMU启动完成
257+
time.sleep(5)
258+
259+
for cmd in commands:
260+
if not self.send_command(cmd):
261+
break
262+
time.sleep(2) # 命令之间间隔2秒
263+
264+
# 监控日志输出,超时退出
265+
while self.running:
266+
idle_time = time.time() - self.last_log_time
267+
if idle_time > self.idle_timeout :
268+
if not self.checkresult_found:
269+
logger.info(f"No logs for {self.idle_timeout} seconds. ::error:: Exiting...")
270+
else:
271+
logger.info(f"No logs for {self.idle_timeout} seconds. ::check success:: Exiting...")
272+
break
273+
time.sleep(0.1)
274+
275+
except KeyboardInterrupt:
276+
logger.info("Script interrupted by user.")
277+
except Exception as e:
278+
logger.error(f"An error occurred: {e}")
279+
finally:
280+
self.stop_qemu()
281+
282+
def run_command(command, timeout=None):
283+
"""运行命令并返回输出和结果"""
284+
print(command)
285+
result = subprocess.run(command, shell=True, capture_output=True, text=True, timeout=timeout)
286+
return result.stdout, result.returncode
287+
288+
def check_output(output, check_string):
289+
"""检查输出中是否包含指定字符串"""
290+
flag = check_string in output
291+
if flag == True:
292+
print('find string ' + check_string)
293+
else:
294+
print('::error:: can not find string ' + check_string + output)
161295

296+
return flag
162297
if __name__ == "__main__":
163298
"""
164299
build all bsp and attach config.
@@ -169,10 +304,12 @@ def build_bsp_attachconfig(bsp, attach_file):
169304
"""
170305
failed = 0
171306
count = 0
307+
ci_build_run_flag = False
308+
qemu_timeout_second = 50
172309

173310
rtt_root = os.getcwd()
174311
srtt_bsp = os.getenv('SRTT_BSP').split(',')
175-
312+
print(srtt_bsp)
176313
for bsp in srtt_bsp:
177314
count += 1
178315
print(f"::group::Compiling BSP: =={count}=== {bsp} ====")
@@ -210,23 +347,55 @@ def build_bsp_attachconfig(bsp, attach_file):
210347

211348
for projects in yml_files_content:
212349
for name, details in projects.items():
350+
# 如果是bsp_board_info,读取基本的信息
351+
if(name == 'bsp_board_info'):
352+
print(details)
353+
pre_build_commands = details.get("pre_build").splitlines()
354+
build_command = details.get("build_cmd").splitlines()
355+
post_build_command = details.get("post_build").splitlines()
356+
qemu_command = details.get("run_cmd")
357+
358+
if details.get("kconfig") is not None:
359+
if details.get("buildcheckresult") is not None:
360+
build_check_result = details.get("buildcheckresult")
361+
if details.get("msh_cmd") is not None:
362+
commands = details.get("msh_cmd").splitlines()
363+
if details.get("checkresult") is not None:
364+
check_result = details.get("checkresult")
365+
if details.get("ci_build_run_flag") is not None:
366+
ci_build_run_flag = details.get("ci_build_run_flag")
367+
if details.get("pre_build") is not None:
368+
pre_build_commands = details.get("pre_build").splitlines()
369+
if details.get("build_cmd") is not None:
370+
build_command = details.get("build_cmd").splitlines()
371+
if details.get("post_build") is not None:
372+
post_build_command = details.get("post_build").splitlines()
373+
if details.get("run_cmd") is not None:
374+
qemu_command = details.get("run_cmd")
213375
count += 1
214376
config_bacakup = config_file+'.origin'
215377
shutil.copyfile(config_file, config_bacakup)
378+
#加载yml中的配置放到.config文件
216379
with open(config_file, 'a') as destination:
217380
if details.get("kconfig") is None:
381+
#如果没有Kconfig,读取下一个配置
218382
continue
219383
if(projects.get(name) is not None):
384+
# 获取Kconfig中所有的信息
220385
detail_list=get_details_and_dependencies([name],projects)
221386
for line in detail_list:
222387
destination.write(line + '\n')
223388
scons_arg=[]
389+
#如果配置中有scons_arg
224390
if details.get('scons_arg') is not None:
225391
for line in details.get('scons_arg'):
226392
scons_arg.append(line)
227393
scons_arg_str=' '.join(scons_arg) if scons_arg else ' '
228394
print(f"::group::\tCompiling yml project: =={count}==={name}=scons_arg={scons_arg_str}==")
395+
396+
#开始编译bsp
229397
res = build_bsp(bsp, scons_arg_str,name=name)
398+
230399
if not res:
231400
print(f"::error::build {bsp} {name} failed.")
232401
add_summary(f'\t- ❌ build {bsp} {name} failed.')
@@ -235,11 +404,53 @@ def build_bsp_attachconfig(bsp, attach_file):
235404
add_summary(f'\t- ✅ build {bsp} {name} success.')
236405
print("::endgroup::")
237406

407+
print(commands)
408+
print(check_result)
409+
print(build_check_result)
410+
print(qemu_command)
411+
print(pre_build_commands)
412+
print(ci_build_run_flag)
413+
414+
#执行编译前的命令
415+
if pre_build_commands is not None:
416+
for command in pre_build_commands:
417+
output, returncode = run_command(command)
418+
print(output)
419+
if returncode != 0:
420+
print(f"Pre-build command failed: {command}")
421+
print(output)
422+
#执行编译命令
423+
if build_command is not None:
424+
for command in build_command:
425+
output, returncode = run_command(command)
426+
print(output)
427+
if returncode != 0 or not check_output(output, build_check_result):
428+
print("Build failed or build check result not found")
429+
print(output)
430+
#执行编译后的命令
431+
if post_build_command is not None:
432+
for command in post_build_command:
433+
output, returncode = run_command(command)
434+
print(output)
435+
if returncode != 0:
436+
print(f"Post-build command failed: {post_build_command}")
437+
print(output)
438+
#执行qemu中的执行命令
439+
if ci_build_run_flag is True:
440+
print(qemu_command)
441+
#exit(1)
442+
print(os.getcwd())
443+
qemu_manager = QemuManager(qemu_cmd=qemu_command, idle_timeout=qemu_timeout_second,checkresult=check_result)
444+
qemu_manager.run(commands)
445+
238446
shutil.copyfile(config_bacakup, config_file)
239447
os.remove(config_bacakup)
240448

449+
450+
241451
attach_dir = os.path.join(rtt_root, 'bsp', bsp, '.ci/attachconfig')
242452
attach_list = []
453+
#这里是旧的文件方式
243454
for root, dirs, files in os.walk(attach_dir):
244455
for file in files:
245456
if file.endswith('attach'):

0 commit comments

Comments
 (0)