arduino_ci 0.1.20 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +21 -19
  3. data/REFERENCE.md +625 -0
  4. data/cpp/arduino/Arduino.h +1 -2
  5. data/cpp/arduino/AvrMath.h +117 -17
  6. data/cpp/arduino/Client.h +26 -0
  7. data/cpp/arduino/EEPROM.h +64 -0
  8. data/cpp/arduino/Godmode.cpp +38 -19
  9. data/cpp/arduino/Godmode.h +88 -22
  10. data/cpp/arduino/HardwareSerial.h +9 -28
  11. data/cpp/arduino/IPAddress.h +59 -0
  12. data/cpp/arduino/MockEventQueue.h +86 -0
  13. data/cpp/arduino/PinHistory.h +64 -24
  14. data/cpp/arduino/Print.h +9 -12
  15. data/cpp/arduino/Printable.h +8 -0
  16. data/cpp/arduino/SPI.h +11 -3
  17. data/cpp/arduino/Server.h +5 -0
  18. data/cpp/arduino/Udp.h +27 -0
  19. data/cpp/arduino/Wire.h +234 -0
  20. data/cpp/arduino/avr/io.h +10 -1
  21. data/cpp/arduino/avr/pgmspace.h +76 -46
  22. data/cpp/arduino/ci/StreamTape.h +36 -0
  23. data/cpp/unittest/ArduinoUnitTests.h +1 -0
  24. data/cpp/unittest/Compare.h +91 -897
  25. data/cpp/unittest/OstreamHelpers.h +9 -0
  26. data/exe/arduino_ci.rb +401 -0
  27. data/exe/arduino_ci_remote.rb +2 -385
  28. data/lib/arduino_ci.rb +1 -0
  29. data/lib/arduino_ci/arduino_cmd.rb +13 -9
  30. data/lib/arduino_ci/arduino_downloader.rb +5 -4
  31. data/lib/arduino_ci/arduino_installation.rb +5 -5
  32. data/lib/arduino_ci/ci_config.rb +12 -0
  33. data/lib/arduino_ci/cpp_library.rb +152 -25
  34. data/lib/arduino_ci/installed_cpp_library.rb +0 -0
  35. data/lib/arduino_ci/library_properties.rb +86 -0
  36. data/lib/arduino_ci/version.rb +1 -1
  37. data/misc/default.yml +50 -3
  38. metadata +23 -13
  39. data/cpp/arduino/Arduino.h.orig +0 -143
  40. data/cpp/arduino/Nullptr.h +0 -7
  41. data/cpp/arduino/ci/Queue.h +0 -73
  42. data/exe/libasan.rb +0 -29
@@ -0,0 +1,9 @@
1
+ #pragma once
2
+
3
+ #include <ostream>
4
+
5
+ #if (defined __apple_build_version__) && (__apple_build_version__ >= 12000000)
6
+ // defined in /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../include/c++/v1/ostream:223:20
7
+ #else
8
+ inline std::ostream& operator << (std::ostream& out, const std::nullptr_t &np) { return out << "nullptr"; }
9
+ #endif
@@ -0,0 +1,401 @@
1
+ #!/usr/bin/env ruby
2
+ require 'arduino_ci'
3
+ require 'set'
4
+ require 'pathname'
5
+ require 'optparse'
6
+
7
+ WIDTH = 80
8
+ FIND_FILES_INDENT = 4
9
+
10
+ @failure_count = 0
11
+ @passfail = proc { |result| result ? "✓" : "✗" }
12
+
13
+ # Use some basic parsing to allow command-line overrides of config
14
+ class Parser
15
+ def self.parse(options)
16
+ unit_config = {}
17
+ output_options = {
18
+ skip_unittests: false,
19
+ skip_compilation: false,
20
+ ci_config: {
21
+ "unittest" => unit_config
22
+ },
23
+ }
24
+
25
+ opt_parser = OptionParser.new do |opts|
26
+ opts.banner = "Usage: #{File.basename(__FILE__)} [options]"
27
+
28
+ opts.on("--skip-unittests", "Don't run unit tests") do |p|
29
+ output_options[:skip_unittests] = p
30
+ end
31
+
32
+ opts.on("--skip-compilation", "Don't compile example sketches (deprecated)") do |p|
33
+ puts "The option --skip-compilation has been deprecated in favor of --skip-examples-compilation"
34
+ output_options[:skip_compilation] = p
35
+ end
36
+
37
+ opts.on("--skip-examples-compilation", "Don't compile example sketches") do |p|
38
+ output_options[:skip_compilation] = p
39
+ end
40
+
41
+ opts.on("--testfile-select=GLOB", "Unit test file (or glob) to select") do |p|
42
+ unit_config["testfiles"] ||= {}
43
+ unit_config["testfiles"]["select"] ||= []
44
+ unit_config["testfiles"]["select"] << p
45
+ end
46
+
47
+ opts.on("--testfile-reject=GLOB", "Unit test file (or glob) to reject") do |p|
48
+ unit_config["testfiles"] ||= {}
49
+ unit_config["testfiles"]["reject"] ||= []
50
+ unit_config["testfiles"]["reject"] << p
51
+ end
52
+
53
+ opts.on("-h", "--help", "Prints this help") do
54
+ puts opts
55
+ exit
56
+ end
57
+ end
58
+
59
+ opt_parser.parse!(options)
60
+ output_options
61
+ end
62
+ end
63
+
64
+ # Read in command line options and make them read-only
65
+ @cli_options = (Parser.parse ARGV).freeze
66
+
67
+ # terminate after printing any debug info. TODO: capture debug info
68
+ def terminate(final = nil)
69
+ puts "Failures: #{@failure_count}"
70
+ unless @failure_count.zero? || final
71
+ puts "Last message: #{@arduino_cmd.last_msg}"
72
+ puts "========== Stdout:"
73
+ puts @arduino_cmd.last_out
74
+ puts "========== Stderr:"
75
+ puts @arduino_cmd.last_err
76
+ end
77
+ retcode = @failure_count.zero? ? 0 : 1
78
+ exit(retcode)
79
+ end
80
+
81
+ # make a nice status line for an action and react to the action
82
+ # TODO / note to self: inform_multline is tougher to write
83
+ # without altering the signature because it only leaves space
84
+ # for the checkmark _after_ the multiline, it doesn't know how
85
+ # to make that conditionally the body
86
+ # @param message String the text of the progress indicator
87
+ # @param multiline boolean whether multiline output is expected
88
+ # @param mark_fn block (string) -> string that says how to describe the result
89
+ # @param on_fail_msg String custom message for failure
90
+ # @param tally_on_fail boolean whether to increment @failure_count
91
+ # @param abort_on_fail boolean whether to abort immediately on failure (i.e. if this is a fatal error)
92
+ def perform_action(message, multiline, mark_fn, on_fail_msg, tally_on_fail, abort_on_fail)
93
+ line = "#{message}... "
94
+ endline = "...#{message} "
95
+ if multiline
96
+ puts line
97
+ else
98
+ print line
99
+ end
100
+ STDOUT.flush
101
+ result = yield
102
+ mark = mark_fn.nil? ? "" : mark_fn.call(result)
103
+ # if multline, put checkmark at full width
104
+ print endline if multiline
105
+ puts mark.to_s.rjust(WIDTH - line.length, " ")
106
+ unless result
107
+ puts on_fail_msg unless on_fail_msg.nil?
108
+ @failure_count += 1 if tally_on_fail
109
+ # print out error messaging here if we've captured it
110
+ terminate if abort_on_fail
111
+ end
112
+ result
113
+ end
114
+
115
+ # Make a nice status for something that defers any failure code until script exit
116
+ def attempt(message, &block)
117
+ perform_action(message, false, @passfail, nil, true, false, &block)
118
+ end
119
+
120
+ # Make a nice status for something that defers any failure code until script exit
121
+ def attempt_multiline(message, &block)
122
+ perform_action(message, true, @passfail, nil, true, false, &block)
123
+ end
124
+
125
+ # Make a nice status for something that kills the script immediately on failure
126
+ FAILED_ASSURANCE_MESSAGE = "This may indicate a problem with ArduinoCI, or your configuration".freeze
127
+ def assure(message, &block)
128
+ perform_action(message, false, @passfail, FAILED_ASSURANCE_MESSAGE, true, true, &block)
129
+ end
130
+
131
+ def assure_multiline(message, &block)
132
+ perform_action(message, true, @passfail, FAILED_ASSURANCE_MESSAGE, true, true, &block)
133
+ end
134
+
135
+ def inform(message, &block)
136
+ perform_action(message, false, proc { |x| x }, nil, false, false, &block)
137
+ end
138
+
139
+ def inform_multiline(message, &block)
140
+ perform_action(message, true, nil, nil, false, false, &block)
141
+ end
142
+
143
+ # Assure that a platform exists and return its definition
144
+ def assured_platform(purpose, name, config)
145
+ platform_definition = config.platform_definition(name)
146
+ assure("Requested #{purpose} platform '#{name}' is defined in 'platforms' YML") do
147
+ !platform_definition.nil?
148
+ end
149
+ platform_definition
150
+ end
151
+
152
+ # Return true if the file (or one of the dirs containing it) is hidden
153
+ def file_is_hidden_somewhere?(path)
154
+ # this is clunkly but pre-2.2-ish ruby doesn't return ascend as an enumerator
155
+ path.ascend do |part|
156
+ return true if part.basename.to_s.start_with? "."
157
+ end
158
+ false
159
+ end
160
+
161
+ # print out some files
162
+ def display_files(pathname)
163
+ # `find` doesn't follow symlinks, so we should instead
164
+ realpath = pathname.symlink? ? pathname.readlink : pathname
165
+
166
+ # suppress directories and dotfile-based things
167
+ all_files = realpath.find.select(&:file?)
168
+ non_hidden = all_files.reject { |path| file_is_hidden_somewhere?(path) }
169
+
170
+ # print files with an indent
171
+ margin = " " * FIND_FILES_INDENT
172
+ non_hidden.each { |p| puts "#{margin}#{p}" }
173
+ end
174
+
175
+ def install_arduino_library_dependencies(aux_libraries)
176
+ aux_libraries.each do |l|
177
+ if @arduino_cmd.library_present?(l)
178
+ inform("Using pre-existing library") { l.to_s }
179
+ else
180
+ assure("Installing aux library '#{l}'") { @arduino_cmd.install_library(l) }
181
+ end
182
+ end
183
+ end
184
+
185
+ def perform_unit_tests(file_config)
186
+ if @cli_options[:skip_unittests]
187
+ inform("Skipping unit tests") { "as requested via command line" }
188
+ return
189
+ end
190
+ config = file_config.with_override_config(@cli_options[:ci_config])
191
+ cpp_library = ArduinoCI::CppLibrary.new(Pathname.new("."),
192
+ @arduino_cmd.lib_dir,
193
+ config.exclude_dirs.map(&Pathname.method(:new)))
194
+
195
+ # check GCC
196
+ compilers = config.compilers_to_use
197
+ assure("The set of compilers (#{compilers.length}) isn't empty") { !compilers.empty? }
198
+ compilers.each do |gcc_binary|
199
+ attempt_multiline("Checking #{gcc_binary} version") do
200
+ version = cpp_library.gcc_version(gcc_binary)
201
+ next nil unless version
202
+
203
+ puts version.split("\n").map { |l| " #{l}" }.join("\n")
204
+ version
205
+ end
206
+ inform("libasan availability for #{gcc_binary}") { cpp_library.libasan?(gcc_binary) }
207
+ end
208
+
209
+ # Ensure platforms exist for unit test, and save their info in all_platform_info keyed by name
210
+ all_platform_info = {}
211
+ config.platforms_to_unittest.each { |p| all_platform_info[p] = assured_platform("unittest", p, config) }
212
+
213
+ inform("Library conforms to Arduino library specification") { cpp_library.one_point_five? ? "1.5" : "1.0" }
214
+
215
+ # iterate boards / tests
216
+ if !cpp_library.tests_dir.exist?
217
+ inform_multiline("Skipping unit tests; no tests dir at #{cpp_library.tests_dir}") do
218
+ puts " In case that's an error, this is what was found in the library:"
219
+ display_files(cpp_library.tests_dir.parent)
220
+ true
221
+ end
222
+ elsif cpp_library.test_files.empty?
223
+ inform_multiline("Skipping unit tests; no test files were found in #{cpp_library.tests_dir}") do
224
+ puts " In case that's an error, this is what was found in the tests directory:"
225
+ display_files(cpp_library.tests_dir)
226
+ true
227
+ end
228
+ elsif config.platforms_to_unittest.empty?
229
+ inform("Skipping unit tests") { "no platforms were requested" }
230
+ else
231
+ install_arduino_library_dependencies(config.aux_libraries_for_unittest)
232
+
233
+ config.platforms_to_unittest.each do |p|
234
+ config.allowable_unittest_files(cpp_library.test_files).each do |unittest_path|
235
+ unittest_name = unittest_path.basename.to_s
236
+ compilers.each do |gcc_binary|
237
+ attempt_multiline("Unit testing #{unittest_name} with #{gcc_binary} for #{p}") do
238
+ exe = cpp_library.build_for_test_with_configuration(
239
+ unittest_path,
240
+ config.aux_libraries_for_unittest,
241
+ gcc_binary,
242
+ config.gcc_config(p)
243
+ )
244
+ puts
245
+ unless exe
246
+ puts "Last command: #{cpp_library.last_cmd}"
247
+ puts cpp_library.last_out
248
+ puts cpp_library.last_err
249
+ next false
250
+ end
251
+ cpp_library.run_test_file(exe)
252
+ end
253
+ end
254
+ end
255
+ end
256
+ end
257
+ end
258
+
259
+ def perform_compilation_tests(config)
260
+ if @cli_options[:skip_compilation]
261
+ inform("Skipping compilation of examples") { "as requested via command line" }
262
+ return
263
+ end
264
+
265
+ # index the existing libraries
266
+ attempt("Indexing libraries") { @arduino_cmd.index_libraries } unless @arduino_cmd.libraries_indexed
267
+
268
+ # initialize library under test
269
+ installed_library_path = attempt("Installing library under test") do
270
+ @arduino_cmd.install_local_library(Pathname.new("."))
271
+ end
272
+
273
+ if !installed_library_path.nil? && installed_library_path.exist?
274
+ inform("Library installed at") { installed_library_path.to_s }
275
+ else
276
+ assure_multiline("Library installed successfully") do
277
+ if installed_library_path.nil?
278
+ puts @arduino_cmd.last_msg
279
+ else
280
+ # print out the contents of the deepest directory we actually find
281
+ @arduino_cmd.lib_dir.ascend do |path_part|
282
+ next unless path_part.exist?
283
+
284
+ break display_files(path_part)
285
+ end
286
+ false
287
+ end
288
+ end
289
+ end
290
+ library_examples = @arduino_cmd.library_examples(installed_library_path)
291
+
292
+ # gather up all required boards for compilation so we can install them up front.
293
+ # start with the "platforms to unittest" and add the examples
294
+ # while we're doing that, get the aux libraries as well
295
+ example_platform_info = {}
296
+ board_package_url = {}
297
+ aux_libraries = Set.new(config.aux_libraries_for_build)
298
+ # while collecting the platforms, ensure they're defined
299
+
300
+ library_examples.each do |path|
301
+ ovr_config = config.from_example(path)
302
+ ovr_config.platforms_to_build.each do |platform|
303
+ # assure the platform if we haven't already
304
+ next if example_platform_info.key?(platform)
305
+
306
+ platform_info = assured_platform("library example", platform, config)
307
+ next if platform_info.nil?
308
+
309
+ example_platform_info[platform] = platform_info
310
+ package = platform_info[:package]
311
+ board_package_url[package] = ovr_config.package_url(package)
312
+ end
313
+ aux_libraries.merge(ovr_config.aux_libraries_for_build)
314
+ end
315
+
316
+ # with all platform info, we can extract unique packages and their urls
317
+ # do that, set the URLs, and download the packages
318
+ all_packages = example_platform_info.values.map { |v| v[:package] }.uniq.reject(&:nil?)
319
+
320
+ # inform about builtin packages
321
+ all_packages.select { |p| config.package_builtin?(p) }.each do |p|
322
+ inform("Using built-in board package") { p }
323
+ end
324
+
325
+ # make sure any non-builtin package has a URL defined
326
+ all_packages.reject { |p| config.package_builtin?(p) }.each do |p|
327
+ assure("Board package #{p} has a defined URL") { board_package_url[p] }
328
+ end
329
+
330
+ # set up all the board manager URLs.
331
+ # we can safely reject nils now, they would be for the builtins
332
+ all_urls = all_packages.map { |p| board_package_url[p] }.uniq.reject(&:nil?)
333
+
334
+ unless all_urls.empty?
335
+ assure("Setting board manager URLs") do
336
+ @arduino_cmd.board_manager_urls = all_urls
337
+ end
338
+ end
339
+
340
+ all_packages.each do |p|
341
+ assure("Installing board package #{p}") do
342
+ @arduino_cmd.install_boards(p)
343
+ end
344
+ end
345
+
346
+ install_arduino_library_dependencies(aux_libraries)
347
+
348
+ last_board = nil
349
+ if config.platforms_to_build.empty?
350
+ inform("Skipping builds") { "no platforms were requested" }
351
+ return
352
+ elsif library_examples.empty?
353
+ inform_multiline("Skipping builds; no examples found in #{installed_library_path}") do
354
+ display_files(installed_library_path)
355
+ end
356
+ return
357
+ end
358
+
359
+ attempt("Setting compiler warning level") { @arduino_cmd.set_pref("compiler.warning_level", "all") }
360
+
361
+ # switching boards takes time, so iterate board first
362
+ # _then_ whichever examples match it
363
+ examples_by_platform = library_examples.each_with_object({}) do |example_path, acc|
364
+ ovr_config = config.from_example(example_path)
365
+ ovr_config.platforms_to_build.each do |p|
366
+ acc[p] = [] unless acc.key?(p)
367
+ acc[p] << example_path
368
+ end
369
+ end
370
+
371
+ examples_by_platform.each do |platform, example_paths|
372
+ board = example_platform_info[platform][:board]
373
+ assure("Switching to board for #{platform} (#{board})") { @arduino_cmd.use_board(board) } unless last_board == board
374
+ last_board = board
375
+
376
+ example_paths.each do |example_path|
377
+ example_name = File.basename(example_path)
378
+ attempt("Verifying #{example_name}") do
379
+ ret = @arduino_cmd.verify_sketch(example_path)
380
+ unless ret
381
+ puts
382
+ puts "Last command: #{@arduino_cmd.last_msg}"
383
+ puts @arduino_cmd.last_err
384
+ end
385
+ ret
386
+ end
387
+ end
388
+ end
389
+
390
+ end
391
+
392
+ # initialize command and config
393
+ config = ArduinoCI::CIConfig.default.from_project_library
394
+
395
+ @arduino_cmd = ArduinoCI::ArduinoInstallation.autolocate!
396
+ inform("Located Arduino binary") { @arduino_cmd.binary_path.to_s }
397
+
398
+ perform_unit_tests(config)
399
+ perform_compilation_tests(config)
400
+
401
+ terminate(true)
@@ -1,386 +1,3 @@
1
1
  #!/usr/bin/env ruby
2
- require 'arduino_ci'
3
- require 'set'
4
- require 'pathname'
5
- require 'optparse'
6
-
7
- WIDTH = 80
8
- FIND_FILES_INDENT = 4
9
-
10
- @failure_count = 0
11
- @passfail = proc { |result| result ? "✓" : "✗" }
12
-
13
- # Use some basic parsing to allow command-line overrides of config
14
- class Parser
15
- def self.parse(options)
16
- unit_config = {}
17
- output_options = {
18
- skip_unittests: false,
19
- skip_compilation: false,
20
- ci_config: {
21
- "unittest" => unit_config
22
- },
23
- }
24
-
25
- opt_parser = OptionParser.new do |opts|
26
- opts.banner = "Usage: #{File.basename(__FILE__)} [options]"
27
-
28
- opts.on("--skip-unittests", "Don't run unit tests") do |p|
29
- output_options[:skip_unittests] = p
30
- end
31
-
32
- opts.on("--skip-compilation", "Don't compile example sketches") do |p|
33
- output_options[:skip_compilation] = p
34
- end
35
-
36
- opts.on("--testfile-select=GLOB", "Unit test file (or glob) to select") do |p|
37
- unit_config["testfiles"] ||= {}
38
- unit_config["testfiles"]["select"] ||= []
39
- unit_config["testfiles"]["select"] << p
40
- end
41
-
42
- opts.on("--testfile-reject=GLOB", "Unit test file (or glob) to reject") do |p|
43
- unit_config["testfiles"] ||= {}
44
- unit_config["testfiles"]["reject"] ||= []
45
- unit_config["testfiles"]["reject"] << p
46
- end
47
-
48
- opts.on("-h", "--help", "Prints this help") do
49
- puts opts
50
- exit
51
- end
52
- end
53
-
54
- opt_parser.parse!(options)
55
- output_options
56
- end
57
- end
58
-
59
- # Read in command line options and make them read-only
60
- @cli_options = (Parser.parse ARGV).freeze
61
-
62
- # terminate after printing any debug info. TODO: capture debug info
63
- def terminate(final = nil)
64
- puts "Failures: #{@failure_count}"
65
- unless @failure_count.zero? || final
66
- puts "Last message: #{@arduino_cmd.last_msg}"
67
- puts "========== Stdout:"
68
- puts @arduino_cmd.last_out
69
- puts "========== Stderr:"
70
- puts @arduino_cmd.last_err
71
- end
72
- retcode = @failure_count.zero? ? 0 : 1
73
- exit(retcode)
74
- end
75
-
76
- # make a nice status line for an action and react to the action
77
- # TODO / note to self: inform_multline is tougher to write
78
- # without altering the signature because it only leaves space
79
- # for the checkmark _after_ the multiline, it doesn't know how
80
- # to make that conditionally the body
81
- # @param message String the text of the progress indicator
82
- # @param multiline boolean whether multiline output is expected
83
- # @param mark_fn block (string) -> string that says how to describe the result
84
- # @param on_fail_msg String custom message for failure
85
- # @param tally_on_fail boolean whether to increment @failure_count
86
- # @param abort_on_fail boolean whether to abort immediately on failure (i.e. if this is a fatal error)
87
- def perform_action(message, multiline, mark_fn, on_fail_msg, tally_on_fail, abort_on_fail)
88
- line = "#{message}... "
89
- endline = "...#{message} "
90
- if multiline
91
- puts line
92
- else
93
- print line
94
- end
95
- STDOUT.flush
96
- result = yield
97
- mark = mark_fn.nil? ? "" : mark_fn.call(result)
98
- # if multline, put checkmark at full width
99
- print endline if multiline
100
- puts mark.to_s.rjust(WIDTH - line.length, " ")
101
- unless result
102
- puts on_fail_msg unless on_fail_msg.nil?
103
- @failure_count += 1 if tally_on_fail
104
- # print out error messaging here if we've captured it
105
- terminate if abort_on_fail
106
- end
107
- result
108
- end
109
-
110
- # Make a nice status for something that defers any failure code until script exit
111
- def attempt(message, &block)
112
- perform_action(message, false, @passfail, nil, true, false, &block)
113
- end
114
-
115
- # Make a nice status for something that defers any failure code until script exit
116
- def attempt_multiline(message, &block)
117
- perform_action(message, true, @passfail, nil, true, false, &block)
118
- end
119
-
120
- # Make a nice status for something that kills the script immediately on failure
121
- FAILED_ASSURANCE_MESSAGE = "This may indicate a problem with ArduinoCI, or your configuration".freeze
122
- def assure(message, &block)
123
- perform_action(message, false, @passfail, FAILED_ASSURANCE_MESSAGE, true, true, &block)
124
- end
125
-
126
- def assure_multiline(message, &block)
127
- perform_action(message, true, @passfail, FAILED_ASSURANCE_MESSAGE, true, true, &block)
128
- end
129
-
130
- def inform(message, &block)
131
- perform_action(message, false, proc { |x| x }, nil, false, false, &block)
132
- end
133
-
134
- def inform_multiline(message, &block)
135
- perform_action(message, true, nil, nil, false, false, &block)
136
- end
137
-
138
- # Assure that a platform exists and return its definition
139
- def assured_platform(purpose, name, config)
140
- platform_definition = config.platform_definition(name)
141
- assure("Requested #{purpose} platform '#{name}' is defined in 'platforms' YML") do
142
- !platform_definition.nil?
143
- end
144
- platform_definition
145
- end
146
-
147
- # Return true if the file (or one of the dirs containing it) is hidden
148
- def file_is_hidden_somewhere?(path)
149
- # this is clunkly but pre-2.2-ish ruby doesn't return ascend as an enumerator
150
- path.ascend do |part|
151
- return true if part.basename.to_s.start_with? "."
152
- end
153
- false
154
- end
155
-
156
- # print out some files
157
- def display_files(pathname)
158
- # `find` doesn't follow symlinks, so we should instead
159
- realpath = pathname.symlink? ? pathname.readlink : pathname
160
-
161
- # suppress directories and dotfile-based things
162
- all_files = realpath.find.select(&:file?)
163
- non_hidden = all_files.reject { |path| file_is_hidden_somewhere?(path) }
164
-
165
- # print files with an indent
166
- margin = " " * FIND_FILES_INDENT
167
- non_hidden.each { |p| puts "#{margin}#{p}" }
168
- end
169
-
170
- def perform_unit_tests(file_config)
171
- if @cli_options[:skip_unittests]
172
- inform("Skipping unit tests") { "as requested via command line" }
173
- return
174
- end
175
- config = file_config.with_override_config(@cli_options[:ci_config])
176
- cpp_library = ArduinoCI::CppLibrary.new(Pathname.new("."), @arduino_cmd.lib_dir)
177
-
178
- # check GCC
179
- compilers = config.compilers_to_use
180
- assure("The set of compilers (#{compilers.length}) isn't empty") { !compilers.empty? }
181
- compilers.each do |gcc_binary|
182
- attempt_multiline("Checking #{gcc_binary} version") do
183
- version = cpp_library.gcc_version(gcc_binary)
184
- next nil unless version
185
-
186
- puts version.split("\n").map { |l| " #{l}" }.join("\n")
187
- version
188
- end
189
- inform("libasan availability for #{gcc_binary}") { cpp_library.libasan?(gcc_binary) }
190
- end
191
-
192
- # Ensure platforms exist for unit test, and save their info in all_platform_info keyed by name
193
- all_platform_info = {}
194
- config.platforms_to_unittest.each { |p| all_platform_info[p] = assured_platform("unittest", p, config) }
195
-
196
- # iterate boards / tests
197
- if !cpp_library.tests_dir.exist?
198
- inform_multiline("Skipping unit tests; no tests dir at #{cpp_library.tests_dir}") do
199
- puts " In case that's an error, this is what was found in the library:"
200
- display_files(cpp_library.tests_dir.parent)
201
- true
202
- end
203
- elsif cpp_library.test_files.empty?
204
- inform_multiline("Skipping unit tests; no test files were found in #{cpp_library.tests_dir}") do
205
- puts " In case that's an error, this is what was found in the tests directory:"
206
- display_files(cpp_library.tests_dir)
207
- true
208
- end
209
- elsif config.platforms_to_unittest.empty?
210
- inform("Skipping unit tests") { "no platforms were requested" }
211
- else
212
- config.platforms_to_unittest.each do |p|
213
- config.allowable_unittest_files(cpp_library.test_files).each do |unittest_path|
214
- unittest_name = unittest_path.basename.to_s
215
- compilers.each do |gcc_binary|
216
- attempt_multiline("Unit testing #{unittest_name} with #{gcc_binary}") do
217
- exe = cpp_library.build_for_test_with_configuration(
218
- unittest_path,
219
- config.aux_libraries_for_unittest,
220
- gcc_binary,
221
- config.gcc_config(p)
222
- )
223
- puts
224
- unless exe
225
- puts "Last command: #{cpp_library.last_cmd}"
226
- puts cpp_library.last_out
227
- puts cpp_library.last_err
228
- next false
229
- end
230
- cpp_library.run_test_file(exe)
231
- end
232
- end
233
- end
234
- end
235
- end
236
- end
237
-
238
- def perform_compilation_tests(config)
239
- if @cli_options[:skip_compilation]
240
- inform("Skipping compilation of examples") { "as requested via command line" }
241
- return
242
- end
243
-
244
- # index the existing libraries
245
- attempt("Indexing libraries") { @arduino_cmd.index_libraries } unless @arduino_cmd.libraries_indexed
246
-
247
- # initialize library under test
248
- installed_library_path = attempt("Installing library under test") do
249
- @arduino_cmd.install_local_library(Pathname.new("."))
250
- end
251
-
252
- if !installed_library_path.nil? && installed_library_path.exist?
253
- inform("Library installed at") { installed_library_path.to_s }
254
- else
255
- assure_multiline("Library installed successfully") do
256
- if installed_library_path.nil?
257
- puts @arduino_cmd.last_msg
258
- else
259
- # print out the contents of the deepest directory we actually find
260
- @arduino_cmd.lib_dir.ascend do |path_part|
261
- next unless path_part.exist?
262
-
263
- break display_files(path_part)
264
- end
265
- false
266
- end
267
- end
268
- end
269
- library_examples = @arduino_cmd.library_examples(installed_library_path)
270
-
271
- # gather up all required boards for compilation so we can install them up front.
272
- # start with the "platforms to unittest" and add the examples
273
- # while we're doing that, get the aux libraries as well
274
- example_platform_info = {}
275
- board_package_url = {}
276
- aux_libraries = Set.new(config.aux_libraries_for_unittest + config.aux_libraries_for_build)
277
- # while collecting the platforms, ensure they're defined
278
-
279
- library_examples.each do |path|
280
- ovr_config = config.from_example(path)
281
- ovr_config.platforms_to_build.each do |platform|
282
- # assure the platform if we haven't already
283
- next if example_platform_info.key?(platform)
284
-
285
- platform_info = assured_platform("library example", platform, config)
286
- next if platform_info.nil?
287
-
288
- example_platform_info[platform] = platform_info
289
- package = platform_info[:package]
290
- board_package_url[package] = ovr_config.package_url(package)
291
- end
292
- aux_libraries.merge(ovr_config.aux_libraries_for_build)
293
- end
294
-
295
- # with all platform info, we can extract unique packages and their urls
296
- # do that, set the URLs, and download the packages
297
- all_packages = example_platform_info.values.map { |v| v[:package] }.uniq.reject(&:nil?)
298
-
299
- # inform about builtin packages
300
- all_packages.select { |p| config.package_builtin?(p) }.each do |p|
301
- inform("Using built-in board package") { p }
302
- end
303
-
304
- # make sure any non-builtin package has a URL defined
305
- all_packages.reject { |p| config.package_builtin?(p) }.each do |p|
306
- assure("Board package #{p} has a defined URL") { board_package_url[p] }
307
- end
308
-
309
- # set up all the board manager URLs.
310
- # we can safely reject nils now, they would be for the builtins
311
- all_urls = all_packages.map { |p| board_package_url[p] }.uniq.reject(&:nil?)
312
-
313
- unless all_urls.empty?
314
- assure("Setting board manager URLs") do
315
- @arduino_cmd.board_manager_urls = all_urls
316
- end
317
- end
318
-
319
- all_packages.each do |p|
320
- assure("Installing board package #{p}") do
321
- @arduino_cmd.install_boards(p)
322
- end
323
- end
324
-
325
- aux_libraries.each do |l|
326
- if @arduino_cmd.library_present?(l)
327
- inform("Using pre-existing library") { l.to_s }
328
- else
329
- assure("Installing aux library '#{l}'") { @arduino_cmd.install_library(l) }
330
- end
331
- end
332
-
333
- last_board = nil
334
- if config.platforms_to_build.empty?
335
- inform("Skipping builds") { "no platforms were requested" }
336
- return
337
- elsif library_examples.empty?
338
- inform_multiline("Skipping builds; no examples found in #{installed_library_path}") do
339
- display_files(installed_library_path)
340
- end
341
- return
342
- end
343
-
344
- attempt("Setting compiler warning level") { @arduino_cmd.set_pref("compiler.warning_level", "all") }
345
-
346
- # switching boards takes time, so iterate board first
347
- # _then_ whichever examples match it
348
- examples_by_platform = library_examples.each_with_object({}) do |example_path, acc|
349
- ovr_config = config.from_example(example_path)
350
- ovr_config.platforms_to_build.each do |p|
351
- acc[p] = [] unless acc.key?(p)
352
- acc[p] << example_path
353
- end
354
- end
355
-
356
- examples_by_platform.each do |platform, example_paths|
357
- board = example_platform_info[platform][:board]
358
- assure("Switching to board for #{platform} (#{board})") { @arduino_cmd.use_board(board) } unless last_board == board
359
- last_board = board
360
-
361
- example_paths.each do |example_path|
362
- example_name = File.basename(example_path)
363
- attempt("Verifying #{example_name}") do
364
- ret = @arduino_cmd.verify_sketch(example_path)
365
- unless ret
366
- puts
367
- puts "Last command: #{@arduino_cmd.last_msg}"
368
- puts @arduino_cmd.last_err
369
- end
370
- ret
371
- end
372
- end
373
- end
374
-
375
- end
376
-
377
- # initialize command and config
378
- config = ArduinoCI::CIConfig.default.from_project_library
379
-
380
- @arduino_cmd = ArduinoCI::ArduinoInstallation.autolocate!
381
- inform("Located Arduino binary") { @arduino_cmd.binary_path.to_s }
382
-
383
- perform_unit_tests(config)
384
- perform_compilation_tests(config)
385
-
386
- terminate(true)
2
+ puts "arduino_ci_remote.rb is deprecated in favor of arduino_ci.rb."
3
+ require_relative "arduino_ci.rb"