1
+ import subprocess
2
+ import threading
3
+ import time
4
+ import logging
5
+ import sys
1
6
import os
2
7
import shutil
3
8
import re
@@ -158,7 +163,137 @@ def build_bsp_attachconfig(bsp, attach_file):
158
163
159
164
return res
160
165
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 )
161
295
296
+ return flag
162
297
if __name__ == "__main__" :
163
298
"""
164
299
build all bsp and attach config.
@@ -169,10 +304,12 @@ def build_bsp_attachconfig(bsp, attach_file):
169
304
"""
170
305
failed = 0
171
306
count = 0
307
+ ci_build_run_flag = False
308
+ qemu_timeout_second = 50
172
309
173
310
rtt_root = os .getcwd ()
174
311
srtt_bsp = os .getenv ('SRTT_BSP' ).split (',' )
175
-
312
+ print ( srtt_bsp )
176
313
for bsp in srtt_bsp :
177
314
count += 1
178
315
print (f"::group::Compiling BSP: =={ count } === { bsp } ====" )
@@ -210,23 +347,55 @@ def build_bsp_attachconfig(bsp, attach_file):
210
347
211
348
for projects in yml_files_content :
212
349
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" )
213
375
count += 1
214
376
config_bacakup = config_file + '.origin'
215
377
shutil .copyfile (config_file , config_bacakup )
378
+ #加载yml中的配置放到.config文件
216
379
with open (config_file , 'a' ) as destination :
217
380
if details .get ("kconfig" ) is None :
381
+ #如果没有Kconfig,读取下一个配置
218
382
continue
219
383
if (projects .get (name ) is not None ):
384
+ # 获取Kconfig中所有的信息
220
385
detail_list = get_details_and_dependencies ([name ],projects )
221
386
for line in detail_list :
222
387
destination .write (line + '\n ' )
223
388
scons_arg = []
389
+ #如果配置中有scons_arg
224
390
if details .get ('scons_arg' ) is not None :
225
391
for line in details .get ('scons_arg' ):
226
392
scons_arg .append (line )
227
393
scons_arg_str = ' ' .join (scons_arg ) if scons_arg else ' '
228
394
print (f"::group::\t Compiling yml project: =={ count } ==={ name } =scons_arg={ scons_arg_str } ==" )
395
+
396
+ #开始编译bsp
229
397
res = build_bsp (bsp , scons_arg_str ,name = name )
398
+
230
399
if not res :
231
400
print (f"::error::build { bsp } { name } failed." )
232
401
add_summary (f'\t - ❌ build { bsp } { name } failed.' )
@@ -235,11 +404,53 @@ def build_bsp_attachconfig(bsp, attach_file):
235
404
add_summary (f'\t - ✅ build { bsp } { name } success.' )
236
405
print ("::endgroup::" )
237
406
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
+
238
446
shutil .copyfile (config_bacakup , config_file )
239
447
os .remove (config_bacakup )
240
448
449
+
450
+
241
451
attach_dir = os .path .join (rtt_root , 'bsp' , bsp , '.ci/attachconfig' )
242
452
attach_list = []
453
+ #这里是旧的文件方式
243
454
for root , dirs , files in os .walk (attach_dir ):
244
455
for file in files :
245
456
if file .endswith ('attach' ):
0 commit comments