55require 'optparse'
66
77WIDTH = 80
8- FIND_FILES_INDENT = 4
98VAR_EXPECT_EXAMPLES = "EXPECT_EXAMPLES" . freeze
109VAR_EXPECT_UNITTESTS = "EXPECT_UNITTESTS" . freeze
1110
@@ -99,7 +98,7 @@ def perform_action(message, multiline, mark_fn, on_fail_msg, tally_on_fail, abor
9998 else
10099 print line
101100 end
102- STDOUT . flush
101+ $stdout . flush
103102 result = yield
104103 mark = mark_fn . nil? ? "" : mark_fn . call ( result )
105104 # if multline, put checkmark at full width
@@ -125,7 +124,7 @@ def attempt_multiline(message, &block)
125124end
126125
127126# Make a nice status for something that kills the script immediately on failure
128- FAILED_ASSURANCE_MESSAGE = "This may indicate a problem with ArduinoCI, or your configuration" . freeze
127+ FAILED_ASSURANCE_MESSAGE = "This may indicate a problem with your configuration; halting here " . freeze
129128def assure ( message , &block )
130129 perform_action ( message , false , @passfail , FAILED_ASSURANCE_MESSAGE , true , true , &block )
131130end
@@ -145,9 +144,7 @@ def inform_multiline(message, &block)
145144# Assure that a platform exists and return its definition
146145def assured_platform ( purpose , name , config )
147146 platform_definition = config . platform_definition ( name )
148- assure ( "Requested #{ purpose } platform '#{ name } ' is defined in 'platforms' YML" ) do
149- !platform_definition . nil?
150- end
147+ assure ( "Requested #{ purpose } platform '#{ name } ' is defined in 'platforms' YML" ) { !platform_definition . nil? }
151148 platform_definition
152149end
153150
@@ -170,17 +167,15 @@ def display_files(pathname)
170167 non_hidden = all_files . reject { |path | file_is_hidden_somewhere? ( path ) }
171168
172169 # print files with an indent
173- margin = " " * FIND_FILES_INDENT
174- non_hidden . each { |p | puts "#{ margin } #{ p } " }
170+ puts " Files (excluding hidden files): #{ non_hidden . size } "
171+ non_hidden . each { |p | puts " #{ p } " }
175172end
176173
177174# @return [Array<String>] The list of installed libraries
178175def install_arduino_library_dependencies ( library_names , on_behalf_of , already_installed = [ ] )
179176 installed = already_installed . clone
180- library_names . map { |n | @backend . library_of_name ( n ) } . each do |l |
181- if installed . include? ( l )
182- # do nothing
183- elsif l . installed?
177+ ( library_names . map { |n | @backend . library_of_name ( n ) } - installed ) . each do |l |
178+ if l . installed?
184179 inform ( "Using pre-existing dependency of #{ on_behalf_of } " ) { l . name }
185180 else
186181 assure ( "Installing dependency of #{ on_behalf_of } : '#{ l . name } '" ) do
@@ -195,30 +190,77 @@ def install_arduino_library_dependencies(library_names, on_behalf_of, already_in
195190 installed
196191end
197192
198- def handle_expectation_of_files ( expectation_envvar , operation , filegroup_name , dir_description , dir )
193+ # @param example_platform_info [Hash] mapping of platform name to package information
194+ # @param board_package_url [Hash] mapping of package name to URL
195+ def install_all_packages ( example_platform_info , board_package_url )
196+ # with all platform info, we can extract unique packages and their urls
197+ # do that, set the URLs, and download the packages
198+ all_packages = example_platform_info . values . map { |v | v [ :package ] } . uniq . reject ( &:nil? )
199+
200+ # make sure any non-builtin package has a URL defined
201+ all_packages . each { |p | assure ( "Board package #{ p } has a defined URL" ) { board_package_url [ p ] } }
202+
203+ # set up all the board manager URLs.
204+ # we can safely reject nils now, they would be for the builtins
205+ all_urls = all_packages . map { |p | board_package_url [ p ] } . uniq . reject ( &:nil? )
206+ unless all_urls . empty?
207+ assure_multiline ( "Setting board manager URLs" ) do
208+ @backend . board_manager_urls = all_urls
209+ result = @backend . board_manager_urls
210+ result . each { |u | puts " #{ u } " }
211+ ( all_urls - result ) . empty? # check that all_urls is completely contained in the result
212+ end
213+ end
214+ all_packages . each { |p | assure ( "Installing board package #{ p } " ) { @backend . install_boards ( p ) } }
215+ end
216+
217+ # @param expectation_envvar [String] the name of the env var to check
218+ # @param operation [String] a description of what operation we might be skipping
219+ # @param filegroup_name [String] a description of the set of files without which we effectively skip the operation
220+ # @param dir_description [String] a description of the directory where we looked for the files
221+ # @param dir [Pathname] the directory where we looked for the files
222+ def handle_expectation_of_files ( expectation_envvar , operation , filegroup_name , dir_description , dir_path )
223+ # alert future me about running the script from the wrong directory, instead of doing the huge file dump
224+ # otherwise, assume that the user might be running the script on a library with no actual unit tests
225+ if Pathname . new ( __dir__ ) . parent == Pathname . new ( Dir . pwd )
226+ inform_multiline ( "arduino_ci seems to be trying to test itself" ) do
227+ [
228+ "arduino_ci (the ruby gem) isn't an arduino project itself, so running the CI test script against" ,
229+ "the core library isn't really a valid thing to do... but it's easy for a developer (including the" ,
230+ "owner) to mistakenly do just that. Hello future me, you probably meant to run this against one of" ,
231+ "the sample projects in SampleProjects/ ... if not, please submit a bug report; what a wild case!"
232+ ] . each { |l | puts " #{ l } " }
233+ false
234+ end
235+ exit ( 1 )
236+ end
237+
238+ # either the directory is empty, or it doesn't exist at all. message accordingly.
239+ ( problem , dir_desc , dir ) = if dir_path . exist?
240+ [ "No #{ filegroup_name } were found in" , dir_description , dir_path ]
241+ else
242+ [ "No #{ dir_description } at" , "base directory" , dir_path . parent ]
243+ end
244+
245+ inform ( problem ) { dir_path }
246+ inform ( "Environment variable #{ expectation_envvar } is" ) { "(#{ ENV [ expectation_envvar ] . class } ) #{ ENV [ expectation_envvar ] } " }
199247 if ENV [ expectation_envvar ] . nil?
200- inform_multiline ( "Skipping #{ operation } ; no #{ filegroup_name } were found in #{ dir } " ) do
201- puts " In case that's an error, this is what was found in the #{ dir_description } :"
248+ inform_multiline ( "Skipping #{ operation } " ) do
249+ puts " In case that's an error, this is what was found in the #{ dir_desc } :"
202250 display_files ( dir )
203- puts "To force an error in this case, set the environment variable #{ expectation_envvar } "
251+ puts " To force an error in this case, set the environment variable #{ expectation_envvar } "
204252 true
205253 end
206254 else
207- assure_multiline ( "No #{ filegroup_name } were found in #{ dir } and #{ expectation_envvar } was set" ) do
208- puts " This is what was found in the #{ dir_description } :"
255+ assure_multiline ( "Dumping project's #{ dir_desc } before exit" ) do
209256 display_files ( dir )
210257 false
211258 end
212259 end
213260end
214261
215- def perform_unit_tests ( cpp_library , file_config )
216- if @cli_options [ :skip_unittests ]
217- inform ( "Skipping unit tests" ) { "as requested via command line" }
218- return
219- end
220- config = file_config . with_override_config ( @cli_options [ :ci_config ] )
221-
262+ # report and return the set of compilers
263+ def get_annotated_compilers ( config , cpp_library )
222264 # check GCC
223265 compilers = config . compilers_to_use
224266 assure ( "The set of compilers (#{ compilers . length } ) isn't empty" ) { !compilers . empty? }
@@ -232,62 +274,54 @@ def perform_unit_tests(cpp_library, file_config)
232274 end
233275 inform ( "libasan availability for #{ gcc_binary } " ) { cpp_library . libasan? ( gcc_binary ) }
234276 end
277+ compilers
278+ end
235279
236- # Ensure platforms exist for unit test, and save their info in all_platform_info keyed by name
237- all_platform_info = { }
238- config . platforms_to_unittest . each { |p | all_platform_info [ p ] = assured_platform ( "unittest" , p , config ) }
280+ def perform_unit_tests ( cpp_library , file_config )
281+ if @cli_options [ :skip_unittests ]
282+ inform ( "Skipping unit tests" ) { "as requested via command line" }
283+ return
284+ end
285+
286+ config = file_config . with_override_config ( @cli_options [ :ci_config ] )
287+ compilers = get_annotated_compilers ( config , cpp_library )
288+ config . platforms_to_unittest . each_with_object ( { } ) { |p , acc | acc [ p ] = assured_platform ( "unittest" , p , config ) }
239289
240290 inform ( "Library conforms to Arduino library specification" ) { cpp_library . one_point_five? ? "1.5" : "1.0" }
241291
242- # iterate boards / tests
243- if !cpp_library . tests_dir . exist?
244- # alert future me about running the script from the wrong directory, instead of doing the huge file dump
245- # otherwise, assume that the user might be running the script on a library with no actual unit tests
246- if Pathname . new ( __dir__ ) . parent == Pathname . new ( Dir . pwd )
247- inform_multiline ( "arduino_ci seems to be trying to test itself" ) do
248- [
249- "arduino_ci (the ruby gem) isn't an arduino project itself, so running the CI test script against" ,
250- "the core library isn't really a valid thing to do... but it's easy for a developer (including the" ,
251- "owner) to mistakenly do just that. Hello future me, you probably meant to run this against one of" ,
252- "the sample projects in SampleProjects/ ... if not, please submit a bug report; what a wild case!"
253- ] . each { |l | puts " #{ l } " }
254- false
255- end
256- exit ( 1 )
257- else
258- inform_multiline ( "Skipping unit tests; no tests dir at #{ cpp_library . tests_dir } " ) do
259- puts " In case that's an error, this is what was found in the library:"
260- display_files ( cpp_library . tests_dir . parent )
261- true
262- end
263- end
264- elsif cpp_library . test_files . empty?
292+ # Handle lack of test files
293+ if cpp_library . test_files . empty?
265294 handle_expectation_of_files ( VAR_EXPECT_UNITTESTS , "unit tests" , "test files" , "tests directory" , cpp_library . tests_dir )
266- elsif config . platforms_to_unittest . empty?
295+ return
296+ end
297+
298+ # Handle lack of platforms
299+ if config . platforms_to_unittest . empty?
267300 inform ( "Skipping unit tests" ) { "no platforms were requested" }
268- else
269- install_arduino_library_dependencies ( config . aux_libraries_for_unittest , "<unittest/libraries>" )
270-
271- config . platforms_to_unittest . each do | p |
272- config . allowable_unittest_files ( cpp_library . test_files ) . each do | unittest_path |
273- unittest_name = unittest_path . basename . to_s
274- compilers . each do |gcc_binary |
275- attempt_multiline ( "Unit testing #{ unittest_name } with #{ gcc_binary } for #{ p } " ) do
276- exe = cpp_library . build_for_test_with_configuration (
277- unittest_path ,
278- config . aux_libraries_for_unittest ,
279- gcc_binary ,
280- config . gcc_config ( p )
281- )
282- puts
283- unless exe
284- puts "Last command: #{ cpp_library . last_cmd } "
285- puts cpp_library . last_out
286- puts cpp_library . last_err
287- next false
288- end
289- cpp_library . run_test_file ( exe )
301+ return
302+ end
303+
304+ install_arduino_library_dependencies ( config . aux_libraries_for_unittest , "<unittest/libraries>" )
305+
306+ config . platforms_to_unittest . each do | p |
307+ config . allowable_unittest_files ( cpp_library . test_files ) . each do |unittest_path |
308+ unittest_name = unittest_path . basename . to_s
309+ compilers . each do | gcc_binary |
310+ attempt_multiline ( "Unit testing #{ unittest_name } with #{ gcc_binary } for #{ p } " ) do
311+ exe = cpp_library . build_for_test_with_configuration (
312+ unittest_path ,
313+ config . aux_libraries_for_unittest ,
314+ gcc_binary ,
315+ config . gcc_config ( p )
316+ )
317+ puts
318+ unless exe
319+ puts "Last command: #{ cpp_library . last_cmd } "
320+ puts cpp_library . last_out
321+ puts cpp_library . last_err
322+ next false
290323 end
324+ cpp_library . run_test_file ( exe )
291325 end
292326 end
293327 end
@@ -325,38 +359,14 @@ def perform_example_compilation_tests(cpp_library, config)
325359 aux_libraries . merge ( ovr_config . aux_libraries_for_build )
326360 end
327361
328- # with all platform info, we can extract unique packages and their urls
329- # do that, set the URLs, and download the packages
330- all_packages = example_platform_info . values . map { |v | v [ :package ] } . uniq . reject ( &:nil? )
331-
332- # make sure any non-builtin package has a URL defined
333- all_packages . each do |p |
334- assure ( "Board package #{ p } has a defined URL" ) { board_package_url [ p ] }
335- end
336-
337- # set up all the board manager URLs.
338- # we can safely reject nils now, they would be for the builtins
339- all_urls = all_packages . map { |p | board_package_url [ p ] } . uniq . reject ( &:nil? )
340-
341- unless all_urls . empty?
342- assure ( "Setting board manager URLs" ) do
343- @backend . board_manager_urls = all_urls
344- end
345- end
346-
347- all_packages . each do |p |
348- assure ( "Installing board package #{ p } " ) do
349- @backend . install_boards ( p )
350- end
351- end
352-
362+ install_all_packages ( example_platform_info , board_package_url )
353363 install_arduino_library_dependencies ( aux_libraries , "<compile/libraries>" )
354364
355365 if config . platforms_to_build . empty?
356366 inform ( "Skipping builds" ) { "no platforms were requested" }
357367 return
358368 elsif library_examples . empty?
359- handle_expectation_of_files ( VAR_EXPECT_EXAMPLES , "builds" , "examples" , "the library directory" , installed_library_path )
369+ handle_expectation_of_files ( VAR_EXPECT_EXAMPLES , "builds" , "examples" , "the examples directory" , cpp_library . examples_dir )
360370 return
361371 end
362372
0 commit comments