1818import multiprocessing
1919import yaml
2020
21+ SCONS_FATAL_PATTERNS = (
22+ re .compile (r'No SConstruct file found' , re .IGNORECASE ),
23+ re .compile (r'SConscript.*No such file or directory' , re .IGNORECASE ),
24+ re .compile (r'No such file or directory.*SConscript' , re .IGNORECASE ),
25+ re .compile (r"(can'?t|cannot|unable to)\s+(read|open|find).*SConscript" , re .IGNORECASE ),
26+ re .compile (r'FileNotFoundError:.*SConscript' , re .IGNORECASE ),
27+ re .compile (r'scons:\s+\*\*\*' , re .IGNORECASE ),
28+ )
29+
2130def add_summary (text ):
2231 """
2332 add summary to github action.
2433 """
25- os .system (f'echo "{ text } " >> $GITHUB_STEP_SUMMARY ;' )
34+ summary_file = os .getenv ('GITHUB_STEP_SUMMARY' )
35+ if summary_file :
36+ with open (summary_file , 'a' , encoding = 'utf-8' ) as file :
37+ file .write (text + '\n ' )
38+
39+ def normalize_returncode (status ):
40+ """
41+ normalize os.system status to command exit code.
42+ """
43+ if status == 0 :
44+ return 0
45+
46+ if os .name == 'nt' :
47+ return status
48+
49+ if hasattr (os , 'waitstatus_to_exitcode' ):
50+ try :
51+ return os .waitstatus_to_exitcode (status )
52+ except ValueError :
53+ return status
54+
55+ return status >> 8
56+
57+ def contains_scons_fatal_error (output ):
58+ """
59+ check whether scons output contains a fatal build-script error.
60+ """
61+ output_str = '' .join (output ) if isinstance (output , list ) else str (output )
62+ return any (pattern .search (output_str ) for pattern in SCONS_FATAL_PATTERNS )
63+
64+ def check_bsp_build_scripts (bsp_dir ):
65+ """
66+ check whether the BSP has the basic scons build entry files.
67+ """
68+ missing = []
69+ for filename in ('SConstruct' , 'SConscript' ):
70+ if not os .path .isfile (os .path .join (bsp_dir , filename )):
71+ missing .append (filename )
72+
73+ if missing :
74+ print (f"::error::missing { ', ' .join (missing )} in { bsp_dir } " )
75+ return False
2676
77+ return True
2778
2879def run_cmd (cmd , output_info = True ):
2980 """
@@ -33,19 +84,27 @@ def run_cmd(cmd, output_info=True):
3384
3485 output_str_list = []
3586 res = 0
87+ output_file = f'output_{ os .getpid ()} _{ threading .get_ident ()} .txt'
88+ devnull = os .devnull
3689
3790 if output_info :
38- res = os .system (cmd + " > output.txt 2>&1" )
91+ res = os .system (cmd + f " > { output_file } 2>&1" )
3992 else :
40- res = os .system (cmd + " > /dev/null 2>output.txt " )
93+ res = os .system (cmd + f " > { devnull } 2>{ output_file } " )
4194
42- with open ("output.txt" , "r" ) as file :
95+ with open (output_file , "r" ) as file :
4396 output_str_list = file .readlines ()
4497
4598 for line in output_str_list :
4699 print (line , end = '' )
47100
48- os .remove ("output.txt" )
101+ os .remove (output_file )
102+
103+ res = normalize_returncode (res )
104+ if contains_scons_fatal_error (output_str_list ):
105+ print (f"::error::scons fatal error detected while running: { cmd } " )
106+ if res == 0 :
107+ res = 1
49108
50109 return output_str_list , res
51110
@@ -61,6 +120,10 @@ def run_dist_build_check(bsp, scons_args=''):
61120 build BSP distribution and verify that the generated project can compile.
62121 """
63122 os .chdir (rtt_root )
123+ bsp_dir = os .path .join (rtt_root , 'bsp' , bsp )
124+ if not check_bsp_build_scripts (bsp_dir ):
125+ return False
126+
64127 dist_root = os .path .join (rtt_root , 'bsp' , bsp , 'dist' )
65128 dist_project = os .path .join (dist_root , 'project' )
66129 if os .path .exists (dist_root ):
@@ -77,6 +140,9 @@ def run_dist_build_check(bsp, scons_args=''):
77140 print (f"::error::dist project not found: { dist_project } " )
78141 return False
79142
143+ if not check_bsp_build_scripts (dist_project ):
144+ return False
145+
80146 old_rtt_root = os .environ .pop ('RTT_ROOT' , None )
81147 _ , res = run_cmd (f'scons --pyconfig-silent -C { dist_project } ' , output_info = True )
82148 if res != 0 :
@@ -118,6 +184,15 @@ def build_bsp(bsp, scons_args='',name='default', pre_build_commands=None, post_b
118184
119185 """
120186 success = True
187+ bsp_dir = os .path .join (rtt_root , 'bsp' , bsp )
188+
189+ if not os .path .isdir (bsp_dir ):
190+ print (f"::error::BSP directory not found: { bsp_dir } " )
191+ return False
192+
193+ if not check_bsp_build_scripts (bsp_dir ):
194+ return False
195+
121196 # 设置环境变量
122197 if bsp_build_env is not None :
123198 print ("Setting environment variables:" )
@@ -128,50 +203,63 @@ def build_bsp(bsp, scons_args='',name='default', pre_build_commands=None, post_b
128203 os .makedirs (f'{ rtt_root } /output/bsp/{ bsp } ' , exist_ok = True )
129204 if os .path .exists (f"{ rtt_root } /bsp/{ bsp } /Kconfig" ):
130205 os .chdir (rtt_root )
131- run_cmd (f'scons -C bsp/{ bsp } --pyconfig-silent' , output_info = True )
206+ _ , res = run_cmd (f'scons -C bsp/{ bsp } --pyconfig-silent' , output_info = True )
207+ if res != 0 :
208+ print (f"::error::pyconfig failed for { bsp } " )
209+ success = False
132210
133211 os .chdir (f'{ rtt_root } /bsp/{ bsp } ' )
134- run_cmd ('pkgs --update-force' , output_info = True )
135- run_cmd ('pkgs --list' )
136-
137- nproc = multiprocessing .cpu_count ()
138- if pre_build_commands is not None :
139- print ("Pre-build commands:" )
140- print (pre_build_commands )
141- for command in pre_build_commands :
142- print (command )
143- output , returncode = run_cmd (command , output_info = True )
144- print (output )
145- if returncode != 0 :
146- print (f"Pre-build command failed: { command } " )
147- print (output )
148- os .chdir (rtt_root )
149- # scons 编译命令
150- cmd = f'scons -C bsp/{ bsp } -j{ nproc } { scons_args } ' # --debug=time for debug time
151- output , res = run_cmd (cmd , output_info = True )
152- if build_check_result is not None :
153- if res != 0 or not check_output (output , build_check_result ):
154- print ("Build failed or build check result not found" )
155- print (output )
212+ _ , res = run_cmd ('pkgs --update-force' , output_info = True )
156213 if res != 0 :
214+ print (f"::error::pkgs --update-force failed for { bsp } " )
157215 success = False
158- else :
159- #拷贝当前的文件夹下面的所有以elf结尾的文件拷贝到rt-thread/output文件夹下
160- import glob
161- # 拷贝编译生成的文件到output目录,文件拓展为 elf,bin,hex
162- for file_type in ['*.elf' , '*.bin' , '*.hex' ]:
163- files = glob .glob (f'{ rtt_root } /bsp/{ bsp } /{ file_type } ' )
164- for file in files :
165- shutil .copy (file , f'{ rtt_root } /output/bsp/{ bsp } /{ name .replace ("/" , "_" )} .{ file_type [2 :]} ' )
166- if is_env_enabled ('RTT_CI_BUILD_DIST' ):
167- print (f"::group::\t Checking dist project: { bsp } { name } " )
168- dist_res = run_dist_build_check (bsp , scons_args )
169- print ("::endgroup::" )
170- if not dist_res :
171- add_summary (f'\t - ❌ dist build { bsp } { name } failed.' )
172- success = False
173- else :
174- add_summary (f'\t - ✅ dist build { bsp } { name } success.' )
216+ _ , res = run_cmd ('pkgs --list' )
217+ if res != 0 :
218+ print (f"::error::pkgs --list failed for { bsp } " )
219+ success = False
220+ else :
221+ print (f"Kconfig not found, skip pyconfig and package update: { os .path .join (bsp_dir , 'Kconfig' )} " )
222+
223+ nproc = multiprocessing .cpu_count ()
224+ if pre_build_commands is not None :
225+ print ("Pre-build commands:" )
226+ print (pre_build_commands )
227+ for command in pre_build_commands :
228+ print (command )
229+ output , returncode = run_cmd (command , output_info = True )
230+ print (output )
231+ if returncode != 0 :
232+ print (f"Pre-build command failed: { command } " )
233+ print (output )
234+ success = False
235+ os .chdir (rtt_root )
236+ # scons 编译命令
237+ cmd = f'scons -C bsp/{ bsp } -j{ nproc } { scons_args } ' # --debug=time for debug time
238+ output , res = run_cmd (cmd , output_info = True )
239+ if build_check_result is not None :
240+ if res != 0 or not check_output (output , build_check_result ):
241+ print ("Build failed or build check result not found" )
242+ print (output )
243+ success = False
244+ if res != 0 :
245+ success = False
246+ if success :
247+ #拷贝当前的文件夹下面的所有以elf结尾的文件拷贝到rt-thread/output文件夹下
248+ import glob
249+ # 拷贝编译生成的文件到output目录,文件拓展为 elf,bin,hex
250+ for file_type in ['*.elf' , '*.bin' , '*.hex' ]:
251+ files = glob .glob (f'{ rtt_root } /bsp/{ bsp } /{ file_type } ' )
252+ for file in files :
253+ shutil .copy (file , f'{ rtt_root } /output/bsp/{ bsp } /{ name .replace ("/" , "_" )} .{ file_type [2 :]} ' )
254+ if is_env_enabled ('RTT_CI_BUILD_DIST' ):
255+ print (f"::group::\t Checking dist project: { bsp } { name } " )
256+ dist_res = run_dist_build_check (bsp , scons_args )
257+ print ("::endgroup::" )
258+ if not dist_res :
259+ add_summary (f'\t - ❌ dist build { bsp } { name } failed.' )
260+ success = False
261+ else :
262+ add_summary (f'\t - ✅ dist build { bsp } { name } success.' )
175263
176264 os .chdir (f'{ rtt_root } /bsp/{ bsp } ' )
177265 if post_build_command is not None :
@@ -181,7 +269,11 @@ def build_bsp(bsp, scons_args='',name='default', pre_build_commands=None, post_b
181269 if returncode != 0 :
182270 print (f"Post-build command failed: { command } " )
183271 print (output )
184- run_cmd ('scons -c' , output_info = False )
272+ success = False
273+ _ , clean_res = run_cmd ('scons -c' , output_info = False )
274+ if clean_res != 0 :
275+ print (f"::error::scons clean failed for { bsp } " )
276+ success = False
185277
186278 return success
187279
@@ -240,19 +332,27 @@ def build_bsp_attachconfig(bsp, attach_file):
240332 """
241333 config_file = os .path .join (rtt_root , 'bsp' , bsp , '.config' )
242334 config_bacakup = config_file + '.origin'
243- shutil .copyfile (config_file , config_bacakup )
335+ if not os .path .isfile (config_file ):
336+ print (f"::error::.config not found: { config_file } " )
337+ return False
244338
245339 attachconfig_dir = os .path .join (rtt_root , 'bsp' , bsp , '.ci/attachconfig' )
246340 attach_path = os .path .join (attachconfig_dir , attach_file )
341+ if not os .path .isfile (attach_path ):
342+ print (f"::error::attach config not found: { attach_path } " )
343+ return False
247344
248- append_file ( attach_path , config_file )
345+ shutil . copyfile ( config_file , config_bacakup )
249346
250- scons_args = check_scons_args (attach_path )
347+ try :
348+ append_file (attach_path , config_file )
251349
252- res = build_bsp ( bsp , scons_args , name = attach_file )
350+ scons_args = check_scons_args ( attach_path )
253351
254- shutil .copyfile (config_bacakup , config_file )
255- os .remove (config_bacakup )
352+ res = build_bsp (bsp , scons_args ,name = attach_file )
353+ finally :
354+ shutil .copyfile (config_bacakup , config_file )
355+ os .remove (config_bacakup )
256356
257357 return res
258358
0 commit comments