2929 sys .argv .pop (sys .argv .index ("--build_timeout" ) + 1 )
3030 sys .argv .remove ("--build_timeout" )
3131
32+ # optional wippersnapper argument to generate a dependencies header file
33+ PRINT_DEPENDENCIES_AS_HEADER = False
34+ INCLUDE_PRINT_DEPENDENCIES_HEADER = False
35+ PRINT_DEPENDENCIES_AS_HEADER_FILENAME = None
36+ if "--include_print_dependencies_header" in sys .argv :
37+ # check argument not null and folder path exists
38+ PRINT_DEPENDENCIES_AS_HEADER_FILENAME = sys .argv [sys .argv .index ("--include_print_dependencies_header" ) + 1 ] if len (sys .argv ) > sys .argv .index ("--include_print_dependencies_header" ) + 1 else None
39+ if PRINT_DEPENDENCIES_AS_HEADER_FILENAME is None or not os .path .exists (PRINT_DEPENDENCIES_AS_HEADER_FILENAME ):
40+ raise AttributeError ("Header file path not found or not provided to --include_print_dependencies_header argument" )
41+ INCLUDE_PRINT_DEPENDENCIES_HEADER = True
42+ sys .argv .pop (sys .argv .index ("--include_print_dependencies_header" ) + 1 )
43+ sys .argv .remove ("--include_print_dependencies_header" )
44+
3245# add user bin to path!
3346BUILD_DIR = ''
3447# add user bin to path!
5669 print ("Found MetroX Examples Repo" )
5770 IS_LEARNING_SYS = True
5871
59- #os.system('pwd')
60- #os.system('ls -lA')
61-
6272CROSS = u'\N{cross mark} '
6373CHECK = u'\N{check mark} '
6474
@@ -104,7 +114,7 @@ def manually_install_esp32_bsp(repo_info):
104114 # Assemble git url
105115 repo_url = "git clone -b {0} https://github.com/{1}/arduino-esp32.git esp32" .format (repo_info .split ("/" )[1 ], repo_info .split ("/" )[0 ])
106116 # Locally clone repo (https://docs.espressif.com/projects/arduino-esp32/en/latest/installing.html#linux)
107- os . system ("mkdir -p /home/runner/Arduino/hardware/espressif" )
117+ subprocess . run ("mkdir -p /home/runner/Arduino/hardware/espressif" , shell = True , check = True )
108118 print ("Cloning %s" % repo_url )
109119 cmd = "cd /home/runner/Arduino/hardware/espressif && " + repo_url
110120 proc = subprocess .Popen (cmd , stdout = subprocess .PIPE , stderr = subprocess .PIPE , shell = True )
@@ -135,10 +145,11 @@ def manually_install_esp32_bsp(repo_info):
135145
136146
137147def install_platform (fqbn , full_platform_name = None ):
148+ print ("Checking for drazzy.json (before moving)" )
138149 if os .path .exists ("/home/runner/.arduino15/package_drazzy.json" ):
139150 print ("Moving drazzy.json" )
140151 shutil .move ("/home/runner/.arduino15/package_drazzy.json" , "/home/runner/.arduino15/package_drazzy.com_index.json" )
141- print ("Installing" , fqbn , end = " " )
152+ print ("Installing" , fqbn , " (" , full_platform_name , ")" , end = " " )
142153 if fqbn == "adafruit:avr" : # we have a platform dep
143154 install_platform ("arduino:avr" , full_platform_name )
144155 if full_platform_name [2 ] is not None :
@@ -147,7 +158,7 @@ def install_platform(fqbn, full_platform_name=None):
147158 return # bail out
148159 for retry in range (0 , 3 ):
149160 print ("arduino-cli core install " + fqbn + " --additional-urls " + BSP_URLS )
150- if os . system ("arduino-cli core install " + fqbn + " --additional-urls " + BSP_URLS + " > /dev/null" ) == 0 :
161+ if subprocess . run ("arduino-cli core install " + fqbn + " --additional-urls " + BSP_URLS + " > /dev/null" , shell = True , check = False ). returncode == 0 :
151162 break
152163 print ("...retrying..." , end = " " )
153164 time .sleep (10 ) # wait 10 seconds then try again?
@@ -157,14 +168,16 @@ def install_platform(fqbn, full_platform_name=None):
157168 exit (- 1 )
158169 ColorPrint .print_pass (CHECK )
159170 # print installed core version
160- print (os .popen ('arduino-cli core list | grep {}' .format (fqbn )).read (), end = '' )
171+ result = subprocess .Popen ('arduino-cli core list | grep {}' .format (fqbn ), shell = True , stdout = subprocess .PIPE , stderr = subprocess .PIPE )
172+ print (result .stdout .read ().decode (), end = '' )
173+
161174
162175
163176def run_or_die (cmd , error ):
164177 print (cmd )
165178 attempt = 0
166179 while attempt < 3 :
167- if os . system (cmd ) == 0 :
180+ if subprocess . run (cmd , shell = True , check = False ). returncode == 0 :
168181 return
169182 attempt += 1
170183 print ('attempt {} failed, {} retry left' .format (attempt , 3 - attempt ))
@@ -175,7 +188,7 @@ def run_or_die(cmd, error):
175188
176189def is_library_installed (lib_name ):
177190 try :
178- installed_libs = subprocess .check_output ([ "arduino-cli" , " lib" , " list"] ).decode ("utf-8" )
191+ installed_libs = subprocess .check_output ("arduino-cli lib list" , shell = True ).decode ("utf-8" )
179192 return not all (not item for item in [re .match ('^' + lib_name + '\\ s*\\ d+\\ .' , line ) for line in installed_libs .split ('\n ' )])
180193 except subprocess .CalledProcessError as e :
181194 print ("Error checking installed libraries:" , e )
@@ -290,6 +303,7 @@ def generate_uf2(platform, fqbn, example_path):
290303 family_id = ALL_PLATFORMS [platform ][1 ]
291304 cmd = ['python3' , 'uf2conv.py' , hex_input_file , '-c' , '-f' , family_id , '-o' , output_file ]
292305 else :
306+ # ColorPrint.print_info(subprocess.check_output(["tree"]).decode("utf-8"))
293307 cli_build_path = "build/*.*." + fqbn .split (':' )[2 ] + "/*.ino.bin"
294308 input_file = glob1 (os .path .join (example_path , cli_build_path ))
295309 output_file = os .path .splitext (input_file )[0 ] + ".uf2"
@@ -329,27 +343,101 @@ def group_output(title):
329343 sys .stdout .flush ()
330344
331345
346+ def extract_dependencies (output ):
347+ print ("Extracting libraries from output:" , output )
348+ print (f"{ "############## Done #############" :-^80} " )
349+
350+ IS_LIBS_FOUND = False
351+ IS_BSP_FOUND = False
352+ libraries = []
353+ platforms = []
354+ COLS = []
355+ for i ,line in enumerate (output .split ('\n ' )):
356+ if line .strip () == '' :
357+ continue
358+ if not IS_LIBS_FOUND :
359+ if re .match (r'Used library' , line ):
360+ IS_LIBS_FOUND = True
361+ print ("Found libraries token using regex" )
362+ print ("find Version:" , line .find ('Version' ))
363+ print ("find Path:" , line .find ('Path' ))
364+ COLS = [0 ,line .find ('Version' ), line .find ('Path' )]
365+ continue
366+ else :
367+ if line .find ("Used library" ) != - 1 :
368+ print ("Found libraries token using find" )
369+ IS_LIBS_FOUND = True
370+ print ("non regex find Version:" , line .find ('Version' ))
371+ print ("find Path:" , line .find ('Path' ))
372+ COLS = [0 ,line .find ('Version' ), line .find ('Path' )]
373+ else :
374+ if not IS_BSP_FOUND :
375+ if re .match (r'Used platform' , line ):
376+ print ("Found platform token using regex" )
377+ IS_BSP_FOUND = True
378+ COLS = [0 ,line .find ('Version' ), line .find ('Path' )]
379+ continue
380+ elif line .find ("Used platform" ) != - 1 :
381+ print ("Found platform token using find" )
382+ IS_BSP_FOUND = True
383+ COLS = [0 ,line .find ('Version' ), line .find ('Path' )]
384+ continue
385+ else :
386+ libraries .append ([line [:COLS [1 ]].strip (),line [COLS [1 ]:COLS [2 ]].strip ()])
387+ else :
388+ platforms .append ([line [:COLS [1 ]].strip (),line [COLS [1 ]:COLS [2 ]].strip ()])
389+
390+ dependencies = {
391+ 'libraries' : libraries ,
392+ 'platforms' : platforms
393+ }
394+ print ("Extracted list of dependencies:" , dependencies )
395+ return dependencies
396+
397+ def write_dependencies_to_header (dependencies , output_file ):
398+ # header file
399+ with open (output_file , 'w' ) as f :
400+ f .write ('#ifndef PROJECT_DEPENDENCIES_H\n ' )
401+ f .write ('#define PROJECT_DEPENDENCIES_H\n \n ' )
402+ f .write ('#define PRINT_DEPENDENCIES 1\n ' )
403+ f .write ('extern const char* project_dependencies;\n \n ' )
404+ f .write ('#endif // PROJECT_DEPENDENCIES_H\n ' )
405+ # cpp file
406+ with open (re .sub (r'\.h$' ,'.cpp' , output_file ), 'w' ) as f :
407+ f .write ('#include "print_dependencies.h"\n \n ' )
408+ f .write ('const char* project_dependencies = R"(\n ' )
409+
410+ f .write ('Libraries and Versions:\n ' )
411+ for lib in dependencies ['libraries' ]:
412+ f .write (f'Library: { lib [0 ].strip ()} , Version: { lib [1 ].strip ()} \n ' )
413+
414+ f .write ('\n Platforms and Versions:\n ' )
415+ for plat in dependencies ['platforms' ]:
416+ f .write (f'Platform: { plat [0 ].strip ()} , Version: { plat [1 ].strip ()} \n ' )
417+
418+ f .write (')";\n \n ' )
419+
332420def test_examples_in_folder (platform , folderpath ):
333- global success
421+ global success , BUILD_TIMEOUT , popen_timeout , BUILD_WALL , BUILD_WARN , PRINT_DEPENDENCIES_AS_HEADER , INCLUDE_PRINT_DEPENDENCIES_HEADER , IS_LEARNING_SYS , BUILD_DIR
334422 fqbn = ALL_PLATFORMS [platform ][0 ]
335423 for example in sorted (os .listdir (folderpath )):
336- examplepath = folderpath + "/" + example
424+ examplepath = folderpath + "/" + example
337425 if os .path .isdir (examplepath ):
338426 test_examples_in_folder (platform , examplepath )
339427 continue
340428 if not examplepath .endswith (".ino" ):
341429 continue
342430
343- print ('\t ' + example , end = ' ' )
431+ print ('\t ' + example , end = ' ' )
344432
345433 # check if we should SKIP
346- skipfilename = folderpath + "/." + platform + ".test.skip"
347- onlyfilename = folderpath + "/." + platform + ".test.only"
434+ skipfilename = folderpath + "/." + platform + ".test.skip"
435+ onlyfilename = folderpath + "/." + platform + ".test.only"
348436 # check if we should GENERATE UF2
349- gen_file_name = folderpath + "/." + platform + ".generate"
437+ gen_file_name = folderpath + "/." + platform + ".generate"
350438
351439 # .skip txt include all skipped platforms, one per line
352- skip_txt = folderpath + "/.skip.txt"
440+ skip_txt = folderpath + "/.skip.txt"
353441
354442 is_skip = False
355443 if os .path .exists (skipfilename ):
@@ -365,10 +453,10 @@ def test_examples_in_folder(platform, folderpath):
365453 ColorPrint .print_warn ("skipping" )
366454 continue
367455
368- if glob .glob (folderpath + "/.*.test.only" ):
369- platformname = glob .glob (folderpath + "/.*.test.only" )[0 ].split ('.' )[1 ]
370- if platformname != "none" and not platformname in ALL_PLATFORMS :
371- # uh oh, this isnt a valid testonly!
456+ if glob .glob (folderpath + "/.*.test.only" ):
457+ platformname = glob .glob (folderpath + "/.*.test.only" )[0 ].split ('.' )[1 ]
458+ if platformname != "none" and platformname not in ALL_PLATFORMS :
459+ # uh oh, this isn't a valid testonly!
372460 ColorPrint .print_fail (CROSS )
373461 ColorPrint .print_fail ("This example does not have a valid .platform.test.only file" )
374462 success = 1
@@ -386,8 +474,16 @@ def test_examples_in_folder(platform, folderpath):
386474 cmd = ['arduino-cli' , 'compile' , '--warnings' , 'all' , '--fqbn' , fqbn , folderpath ]
387475 else :
388476 cmd = ['arduino-cli' , 'compile' , '--warnings' , 'none' , '--export-binaries' , '--fqbn' , fqbn , folderpath ]
389- proc = subprocess .Popen (cmd , stdout = subprocess .PIPE ,
390- stderr = subprocess .PIPE )
477+
478+ if PRINT_DEPENDENCIES_AS_HEADER :
479+ cmd .append ('--only-compilation-database' )
480+ cmd .append ('--no-color' )
481+ elif INCLUDE_PRINT_DEPENDENCIES_HEADER :
482+ cmd .append ('--build-property' )
483+ cmd .append ('"build.extra_flags=\' -DPRINT_DEPENDENCIES -I\" ' + os .path .join (BUILD_DIR , "print_dependencies.cpp" ) + '\" \' "' )
484+ cmd .append ('--verbose' )
485+
486+ proc = subprocess .Popen (cmd , stdout = subprocess .PIPE , stderr = subprocess .PIPE )
391487 try :
392488 if BUILD_TIMEOUT :
393489 out , err = proc .communicate (timeout = popen_timeout )
@@ -405,7 +501,11 @@ def test_examples_in_folder(platform, folderpath):
405501 # also print out warning message
406502 with group_output (f"{ example } { fqbn } build output" ):
407503 ColorPrint .print_fail (err .decode ("utf-8" ))
408- if os .path .exists (gen_file_name ):
504+ if PRINT_DEPENDENCIES_AS_HEADER :
505+ # Extract dependencies and write to header for the first successful example
506+ dependencies = extract_dependencies (out .decode ("utf-8" ) + err .decode ("utf-8" ))
507+ write_dependencies_to_header (dependencies , PRINT_DEPENDENCIES_AS_HEADER_FILENAME )
508+ elif os .path .exists (gen_file_name ):
409509 if ALL_PLATFORMS [platform ][1 ] is None :
410510 ColorPrint .print_info ("Platform does not support UF2 files, skipping..." )
411511 else :
@@ -415,10 +515,10 @@ def test_examples_in_folder(platform, folderpath):
415515 success = 1 # failure
416516 if IS_LEARNING_SYS :
417517 fqbnpath , uf2file = filename .split ("/" )[- 2 :]
418- os .makedirs (BUILD_DIR + "/build" , exist_ok = True )
419- os .makedirs (BUILD_DIR + "/build/" + fqbnpath , exist_ok = True )
420- shutil .copy (filename , BUILD_DIR + "/build/" + fqbnpath + "-" + uf2file )
421- os . system ("ls -lR " + BUILD_DIR + "/build" )
518+ os .makedirs (BUILD_DIR + "/build" , exist_ok = True )
519+ os .makedirs (BUILD_DIR + "/build/" + fqbnpath , exist_ok = True )
520+ shutil .copy (filename , BUILD_DIR + "/build/" + fqbnpath + "-" + uf2file )
521+ subprocess . run ("ls -lR " + BUILD_DIR + "/build" , shell = True , check = True )
422522 else :
423523 ColorPrint .print_fail (CROSS )
424524 with group_output (f"{ example } { fqbn } built output" ):
@@ -428,6 +528,7 @@ def test_examples_in_folder(platform, folderpath):
428528
429529
430530def main ():
531+ global INCLUDE_PRINT_DEPENDENCIES_HEADER , PRINT_DEPENDENCIES_AS_HEADER
431532 # Test platforms
432533 platforms = []
433534
@@ -449,15 +550,22 @@ def main():
449550 for platform in platforms :
450551 fqbn = ALL_PLATFORMS [platform ][0 ]
451552 print ('#' * 80 )
452- ColorPrint .print_info ("SWITCHING TO " + fqbn )
553+ ColorPrint .print_info ("SWITCHING TO " + fqbn )
453554 install_platform (":" .join (fqbn .split (':' , 2 )[0 :2 ]), ALL_PLATFORMS [platform ]) # take only first two elements
454555 print ('#' * 80 )
455556 if not IS_LEARNING_SYS :
456- test_examples_in_folder (platform , BUILD_DIR + "/examples" )
557+ if INCLUDE_PRINT_DEPENDENCIES_HEADER :
558+ PRINT_DEPENDENCIES_AS_HEADER = True
559+ INCLUDE_PRINT_DEPENDENCIES_HEADER = False
560+ test_examples_in_folder (platform , BUILD_DIR + "/examples" )
561+ PRINT_DEPENDENCIES_AS_HEADER = False
562+ INCLUDE_PRINT_DEPENDENCIES_HEADER = True
563+ test_examples_in_folder (platform , BUILD_DIR + "/examples" )
564+ else :
565+ test_examples_in_folder (platform , BUILD_DIR + "/examples" )
457566 else :
458567 test_examples_in_folder (platform , BUILD_DIR )
459568
460-
461569if __name__ == "__main__" :
462570 main ()
463571 exit (success )
0 commit comments