22import glob
33import sys
44import subprocess
5+ from subprocess import Popen , PIPE
56import time
6- import argparse
7-
8- FQBN_PREFIX = 'adafruit:samd:adafruit_'
97
10-
11- parser = argparse .ArgumentParser (
12- description = 'python wrapper for adafruit arduino CI workflows' ,
13- allow_abbrev = False
14- )
15- parser .add_argument (
16- '--all_warnings' , '--Wall' ,
17- action = 'store_true' ,
18- help = 'build with all warnings enabled (`--warnings all`)' ,
19- )
20- parser .add_argument (
21- '--warnings_do_not_cause_job_failure' ,
22- action = 'store_true' ,
23- help = 'failed builds will be listed as failed, but not cause job to exit with an error status' ,
24- )
25- parser .add_argument (
26- 'build_boards' ,
27- metavar = 'board' ,
28- nargs = '*' ,
29- help = 'list of boards to be built -- Note that the fqbn is created by prepending "{}"' .format (FQBN_PREFIX ),
30- default = [ 'metro_m0' , 'metro_m4' , 'circuitplayground_m0' , 'feather_m4_can' ]
31- )
32- args = parser .parse_args ()
8+ SUCCEEDED = "\033 [32msucceeded\033 [0m"
9+ FAILED = "\033 [31mfailed\033 [0m"
10+ SKIPPED = "\033 [35mskipped\033 [0m"
11+ WARNING = "\033 [33mwarnings\033 [0m "
3312
3413exit_status = 0
3514success_count = 0
3615fail_count = 0
3716skip_count = 0
38- build_format = '| {:22} | {:30} | {:9} '
39- build_separator = '-' * 80
4017
41- def errorOutputFilter (line : str ):
42- if len (line ) == 0 :
43- return False
44- if line .isspace (): # Note: empty string does not match here!
45- return False
46- # TODO: additional items to remove?
47- return True
18+ build_format = '| {:20} | {:35} | {:18} | {:6} |'
19+ build_separator = '-' * 83
20+
21+ FQBN_PREFIX = 'adafruit:samd:adafruit_'
22+
23+ default_boards = [ 'metro_m0' , 'metro_m4' , 'circuitplayground_m0' , 'feather_m4_can' ]
24+ build_boards = []
25+
26+ # build all variants if input not existed
27+ if len (sys .argv ) > 1 :
28+ build_boards .append (sys .argv [1 ])
29+ else :
30+ build_boards = default_boards
31+
32+ all_examples = list (glob .iglob ('libraries/**/*.ino' , recursive = True ))
33+ all_examples .sort ()
4834
49- def build_examples (variant : str ):
50- global args , exit_status , success_count , fail_count , skip_count , build_format , build_separator
35+ def build_examples (variant ):
36+ global exit_status , success_count , fail_count , skip_count , build_format , build_separator
5137
5238 print ('\n ' )
5339 print (build_separator )
54- print ('| {:^76 } |' .format ('Board ' + variant ))
40+ print ('| {:^79 } |' .format ('Board ' + variant ))
5541 print (build_separator )
56- print (( build_format + '| {:6} |' ) .format ('Library' , 'Example' , 'Result ' , 'Time' ))
42+ print (build_format .format ('Library' , 'Example' , '\033 [39mResult \033 [0m ' , 'Time' ))
5743 print (build_separator )
5844
5945 fqbn = "{}{}" .format (FQBN_PREFIX , variant )
6046
61- for sketch in glob . iglob ( 'libraries/**/*.ino' , recursive = True ) :
47+ for sketch in all_examples :
6248 # TODO skip TinyUSB library examples for now
6349 if "libraries/Adafruit_TinyUSB_Arduino" in sketch :
6450 continue
@@ -69,60 +55,47 @@ def build_examples(variant: str):
6955 # Skip if not contains: ".board.test.only" for a specific board
7056 sketchdir = os .path .dirname (sketch )
7157 if os .path .exists (sketchdir + '/.all.test.skip' ) or os .path .exists (sketchdir + '/.' + variant + '.test.skip' ):
72- success = "\033 [33mskipped\033 [0m "
73- elif glob .glob (sketchdir + "/.*.test.only" ) and not os .path .exists (sketchdir + '/.build.' + variant ):
74- success = "\033 [33mskipped\033 [0m "
58+ success = SKIPPED
59+ skip_count += 1
60+ elif glob .glob (sketchdir + "/.*.test.only" ) and not os .path .exists (sketchdir + '/.' + variant + '.test.only' ):
61+ success = SKIPPED
62+ skip_count += 1
7563 else :
76- # TODO - preferably, would have STDERR show up in **both** STDOUT and STDERR.
77- # preferably, would use Python logging handler to get both distinct outputs and one merged output
78- # for now, split STDERR when building with all warnings enabled, so can detect warning/error output.
79- if args .all_warnings :
80- build_result = subprocess .run ("arduino-cli compile --warnings all --fqbn {} {}" .format (fqbn , sketch ), shell = True , stdout = subprocess .PIPE , stderr = subprocess .PIPE )
81- else :
82- build_result = subprocess .run ("arduino-cli compile --warnings default --fqbn {} {}" .format (fqbn , sketch ), shell = True , stdout = subprocess .PIPE , stderr = subprocess .STDOUT )
83-
84- # get stderr into a form where len(warningLines) indicates a true warning was output to stderr
85- warningLines = [];
86- if args .all_warnings and build_result .stderr :
87- tmpWarningLines = build_result .stderr .decode ("utf-8" ).splitlines ()
88- warningLines = list (filter (errorOutputFilter , (tmpWarningLines )))
64+ build_result = subprocess .run ("arduino-cli compile --warnings all --fqbn {} {}" .format (fqbn , sketch ), shell = True , stdout = PIPE , stderr = PIPE )
8965
66+ # get stderr into a form where warning/error was output to stderr
9067 if build_result .returncode != 0 :
9168 exit_status = build_result .returncode
92- success = "\033 [31mfailed\033 [0m "
93- fail_count += 1
94- elif len (warningLines ) != 0 :
95- if not args .warnings_do_not_cause_job_failure :
96- exit_status = - 1
97- success = "\033 [31mwarnings\033 [0m "
69+ success = FAILED
9870 fail_count += 1
9971 else :
100- success = "\033 [32msucceeded\033 [0m"
10172 success_count += 1
73+ if build_result .stderr :
74+ success = WARNING
75+ else :
76+ success = SUCCEEDED
10277
10378 build_duration = time .monotonic () - start_time
10479
105- print (( build_format + '| {:5.2f}s |' ). format (sketch .split (os .path .sep )[1 ], os .path .basename (sketch ), success , build_duration ))
80+ print (build_format . format (sketch .split (os .path .sep )[1 ], os .path .basename (sketch ), success , '{:5.2f}s' . format ( build_duration ) ))
10681
107- if success != "\033 [33mskipped\033 [0m " :
82+ if success != SKIPPED :
83+ # Build failed
10884 if build_result .returncode != 0 :
10985 print (build_result .stdout .decode ("utf-8" ))
110- if (build_result .stderr ):
111- print (build_result .stderr .decode ("utf-8" ))
112- if len (warningLines ) != 0 :
113- for line in warningLines :
114- print (line )
115- else :
116- skip_count += 1
86+
87+ # Build with warnings
88+ if build_result .stderr :
89+ print (build_result .stderr .decode ("utf-8" ))
11790
11891build_time = time .monotonic ()
11992
120- for board in args . build_boards :
93+ for board in build_boards :
12194 build_examples (board )
12295
12396print (build_separator )
12497build_time = time .monotonic () - build_time
125- print ("Build Summary: {} \033 [32msucceeded \033 [0m , {} \033 [31mfailed \033 [0m , {} \033 [33mskipped \033 [0m and took {:.2f}s" .format (success_count , fail_count , skip_count , build_time ))
98+ print ("Build Summary: {} {} , {} {} , {} {} and took {:.2f}s" .format (success_count , SUCCEEDED , fail_count , FAILED , skip_count , SKIPPED , build_time ))
12699print (build_separator )
127100
128101sys .exit (exit_status )
0 commit comments