1+ import subprocess
2+ import threading
3+ import time
4+ import logging
5+ import sys
16import os
27import shutil
38import 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
162297if __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::\t Compiling 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