arduino_ci 0.2.0 → 1.1.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.
- checksums.yaml +5 -5
- data/README.md +49 -20
- data/REFERENCE.md +636 -0
- data/cpp/arduino/Arduino.h +1 -1
- data/cpp/arduino/AvrMath.h +117 -17
- data/cpp/arduino/Client.h +27 -0
- data/cpp/arduino/EEPROM.h +64 -0
- data/cpp/arduino/Godmode.cpp +11 -0
- data/cpp/arduino/Godmode.h +58 -9
- data/cpp/arduino/HardwareSerial.h +9 -28
- data/cpp/arduino/IPAddress.h +59 -0
- data/cpp/arduino/Print.h +9 -12
- data/cpp/arduino/Printable.h +8 -0
- data/cpp/arduino/SPI.h +11 -3
- data/cpp/arduino/Server.h +5 -0
- data/cpp/arduino/Udp.h +27 -0
- data/cpp/arduino/Wire.h +234 -0
- data/cpp/arduino/avr/io.h +10 -1
- data/cpp/arduino/avr/pgmspace.h +76 -46
- data/cpp/arduino/ci/StreamTape.h +36 -0
- data/cpp/unittest/OstreamHelpers.h +4 -0
- data/exe/arduino_ci.rb +427 -0
- data/exe/arduino_ci_remote.rb +2 -385
- data/exe/arduino_library_location.rb +2 -2
- data/exe/ensure_arduino_installation.rb +7 -1
- data/lib/arduino_ci.rb +1 -0
- data/lib/arduino_ci/arduino_backend.rb +222 -0
- data/lib/arduino_ci/arduino_downloader.rb +43 -73
- data/lib/arduino_ci/arduino_downloader_linux.rb +17 -55
- data/lib/arduino_ci/arduino_downloader_osx.rb +21 -33
- data/lib/arduino_ci/arduino_downloader_windows.rb +11 -53
- data/lib/arduino_ci/arduino_installation.rb +18 -80
- data/lib/arduino_ci/ci_config.rb +15 -9
- data/lib/arduino_ci/cpp_library.rb +266 -48
- data/lib/arduino_ci/host.rb +59 -4
- data/lib/arduino_ci/library_properties.rb +96 -0
- data/lib/arduino_ci/version.rb +1 -1
- data/misc/default.yml +55 -4
- metadata +21 -87
- data/cpp/arduino/Arduino.h.orig +0 -143
- data/exe/libasan.rb +0 -29
- data/lib/arduino_ci/arduino_cmd.rb +0 -328
- data/lib/arduino_ci/arduino_cmd_linux.rb +0 -17
- data/lib/arduino_ci/arduino_cmd_linux_builder.rb +0 -19
- data/lib/arduino_ci/arduino_cmd_osx.rb +0 -17
- data/lib/arduino_ci/arduino_cmd_windows.rb +0 -17
@@ -0,0 +1,36 @@
|
|
1
|
+
#pragma once
|
2
|
+
|
3
|
+
#include "../Stream.h"
|
4
|
+
|
5
|
+
/**
|
6
|
+
* Stream with godmode-controlled input and godmode-persisted output
|
7
|
+
*/
|
8
|
+
class StreamTape : public Stream, public ObservableDataStream
|
9
|
+
{
|
10
|
+
protected:
|
11
|
+
String* mGodmodeDataOut;
|
12
|
+
// mGodmodeDataIn is provided by Stream
|
13
|
+
|
14
|
+
public:
|
15
|
+
StreamTape(String* dataIn, String* dataOut, unsigned long* delay): Stream(), ObservableDataStream() {
|
16
|
+
mGodmodeDataIn = dataIn;
|
17
|
+
mGodmodeDataOut = dataOut;
|
18
|
+
mGodmodeMicrosDelay = delay;
|
19
|
+
}
|
20
|
+
|
21
|
+
// virtual int available(void);
|
22
|
+
// virtual int peek(void);
|
23
|
+
// virtual int read(void);
|
24
|
+
// virtual int availableForWrite(void);
|
25
|
+
// virtual void flush(void);
|
26
|
+
virtual size_t write(uint8_t aChar) {
|
27
|
+
mGodmodeDataOut->append(String((char)aChar));
|
28
|
+
advertiseByte((unsigned char)aChar);
|
29
|
+
return 1;
|
30
|
+
}
|
31
|
+
|
32
|
+
// https://stackoverflow.com/a/4271276
|
33
|
+
using Print::write; // pull in write(str) and write(buf, size) from Print
|
34
|
+
|
35
|
+
};
|
36
|
+
|
@@ -2,4 +2,8 @@
|
|
2
2
|
|
3
3
|
#include <ostream>
|
4
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
|
5
8
|
inline std::ostream& operator << (std::ostream& out, const std::nullptr_t &np) { return out << "nullptr"; }
|
9
|
+
#endif
|
data/exe/arduino_ci.rb
ADDED
@@ -0,0 +1,427 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
require 'arduino_ci'
|
3
|
+
require 'set'
|
4
|
+
require 'pathname'
|
5
|
+
require 'optparse'
|
6
|
+
|
7
|
+
WIDTH = 80
|
8
|
+
VAR_EXPECT_EXAMPLES = "EXPECT_EXAMPLES".freeze
|
9
|
+
VAR_EXPECT_UNITTESTS = "EXPECT_UNITTESTS".freeze
|
10
|
+
|
11
|
+
@failure_count = 0
|
12
|
+
@passfail = proc { |result| result ? "✓" : "✗" }
|
13
|
+
@backend = nil
|
14
|
+
|
15
|
+
# Use some basic parsing to allow command-line overrides of config
|
16
|
+
class Parser
|
17
|
+
def self.parse(options)
|
18
|
+
unit_config = {}
|
19
|
+
output_options = {
|
20
|
+
skip_unittests: false,
|
21
|
+
skip_compilation: false,
|
22
|
+
ci_config: {
|
23
|
+
"unittest" => unit_config
|
24
|
+
},
|
25
|
+
}
|
26
|
+
|
27
|
+
opt_parser = OptionParser.new do |opts|
|
28
|
+
opts.banner = "Usage: #{File.basename(__FILE__)} [options]"
|
29
|
+
|
30
|
+
opts.on("--skip-unittests", "Don't run unit tests") do |p|
|
31
|
+
output_options[:skip_unittests] = p
|
32
|
+
end
|
33
|
+
|
34
|
+
opts.on("--skip-examples-compilation", "Don't compile example sketches") do |p|
|
35
|
+
output_options[:skip_compilation] = p
|
36
|
+
end
|
37
|
+
|
38
|
+
opts.on("--testfile-select=GLOB", "Unit test file (or glob) to select") do |p|
|
39
|
+
unit_config["testfiles"] ||= {}
|
40
|
+
unit_config["testfiles"]["select"] ||= []
|
41
|
+
unit_config["testfiles"]["select"] << p
|
42
|
+
end
|
43
|
+
|
44
|
+
opts.on("--testfile-reject=GLOB", "Unit test file (or glob) to reject") do |p|
|
45
|
+
unit_config["testfiles"] ||= {}
|
46
|
+
unit_config["testfiles"]["reject"] ||= []
|
47
|
+
unit_config["testfiles"]["reject"] << p
|
48
|
+
end
|
49
|
+
|
50
|
+
opts.on("-h", "--help", "Prints this help") do
|
51
|
+
puts opts
|
52
|
+
puts
|
53
|
+
puts "Additionally, the following environment variables control the script:"
|
54
|
+
puts " - #{VAR_EXPECT_EXAMPLES} - if set, testing will fail if no example sketches are present"
|
55
|
+
puts " - #{VAR_EXPECT_UNITTESTS} - if set, testing will fail if no unit tests are present"
|
56
|
+
exit
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
opt_parser.parse!(options)
|
61
|
+
output_options
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
# Read in command line options and make them read-only
|
66
|
+
@cli_options = (Parser.parse ARGV).freeze
|
67
|
+
|
68
|
+
# terminate after printing any debug info. TODO: capture debug info
|
69
|
+
def terminate(final = nil)
|
70
|
+
puts "Failures: #{@failure_count}"
|
71
|
+
unless @failure_count.zero? || final
|
72
|
+
puts "Last message: #{@backend.last_msg}"
|
73
|
+
puts "========== Stdout:"
|
74
|
+
puts @backend.last_out
|
75
|
+
puts "========== Stderr:"
|
76
|
+
puts @backend.last_err
|
77
|
+
end
|
78
|
+
retcode = @failure_count.zero? ? 0 : 1
|
79
|
+
exit(retcode)
|
80
|
+
end
|
81
|
+
|
82
|
+
# make a nice status line for an action and react to the action
|
83
|
+
# TODO / note to self: inform_multline is tougher to write
|
84
|
+
# without altering the signature because it only leaves space
|
85
|
+
# for the checkmark _after_ the multiline, it doesn't know how
|
86
|
+
# to make that conditionally the body
|
87
|
+
# @param message String the text of the progress indicator
|
88
|
+
# @param multiline boolean whether multiline output is expected
|
89
|
+
# @param mark_fn block (string) -> string that says how to describe the result
|
90
|
+
# @param on_fail_msg String custom message for failure
|
91
|
+
# @param tally_on_fail boolean whether to increment @failure_count
|
92
|
+
# @param abort_on_fail boolean whether to abort immediately on failure (i.e. if this is a fatal error)
|
93
|
+
def perform_action(message, multiline, mark_fn, on_fail_msg, tally_on_fail, abort_on_fail)
|
94
|
+
line = "#{message}... "
|
95
|
+
endline = "...#{message} "
|
96
|
+
if multiline
|
97
|
+
puts line
|
98
|
+
else
|
99
|
+
print line
|
100
|
+
end
|
101
|
+
$stdout.flush
|
102
|
+
result = yield
|
103
|
+
mark = mark_fn.nil? ? "" : mark_fn.call(result)
|
104
|
+
# if multline, put checkmark at full width
|
105
|
+
print endline if multiline
|
106
|
+
puts mark.to_s.rjust(WIDTH - line.length, " ")
|
107
|
+
unless result
|
108
|
+
puts on_fail_msg unless on_fail_msg.nil?
|
109
|
+
@failure_count += 1 if tally_on_fail
|
110
|
+
# print out error messaging here if we've captured it
|
111
|
+
terminate if abort_on_fail
|
112
|
+
end
|
113
|
+
result
|
114
|
+
end
|
115
|
+
|
116
|
+
# Make a nice status for something that defers any failure code until script exit
|
117
|
+
def attempt(message, &block)
|
118
|
+
perform_action(message, false, @passfail, nil, true, false, &block)
|
119
|
+
end
|
120
|
+
|
121
|
+
# Make a nice status for something that defers any failure code until script exit
|
122
|
+
def attempt_multiline(message, &block)
|
123
|
+
perform_action(message, true, @passfail, nil, true, false, &block)
|
124
|
+
end
|
125
|
+
|
126
|
+
# Make a nice status for something that kills the script immediately on failure
|
127
|
+
FAILED_ASSURANCE_MESSAGE = "This may indicate a problem with your configuration; halting here".freeze
|
128
|
+
def assure(message, &block)
|
129
|
+
perform_action(message, false, @passfail, FAILED_ASSURANCE_MESSAGE, true, true, &block)
|
130
|
+
end
|
131
|
+
|
132
|
+
def assure_multiline(message, &block)
|
133
|
+
perform_action(message, true, @passfail, FAILED_ASSURANCE_MESSAGE, true, true, &block)
|
134
|
+
end
|
135
|
+
|
136
|
+
def inform(message, &block)
|
137
|
+
perform_action(message, false, proc { |x| x }, nil, false, false, &block)
|
138
|
+
end
|
139
|
+
|
140
|
+
def inform_multiline(message, &block)
|
141
|
+
perform_action(message, true, nil, nil, false, false, &block)
|
142
|
+
end
|
143
|
+
|
144
|
+
# Assure that a platform exists and return its definition
|
145
|
+
def assured_platform(purpose, name, config)
|
146
|
+
platform_definition = config.platform_definition(name)
|
147
|
+
assure("Requested #{purpose} platform '#{name}' is defined in 'platforms' YML") { !platform_definition.nil? }
|
148
|
+
platform_definition
|
149
|
+
end
|
150
|
+
|
151
|
+
# Return true if the file (or one of the dirs containing it) is hidden
|
152
|
+
def file_is_hidden_somewhere?(path)
|
153
|
+
# this is clunkly but pre-2.2-ish ruby doesn't return ascend as an enumerator
|
154
|
+
path.ascend do |part|
|
155
|
+
return true if part.basename.to_s.start_with? "."
|
156
|
+
end
|
157
|
+
false
|
158
|
+
end
|
159
|
+
|
160
|
+
# print out some files
|
161
|
+
def display_files(pathname)
|
162
|
+
# `find` doesn't follow symlinks, so we should instead
|
163
|
+
realpath = ArduinoCI::Host.symlink?(pathname) ? ArduinoCI::Host.readlink(pathname) : pathname
|
164
|
+
|
165
|
+
# suppress directories and dotfile-based things
|
166
|
+
all_files = realpath.find.select(&:file?)
|
167
|
+
non_hidden = all_files.reject { |path| file_is_hidden_somewhere?(path) }
|
168
|
+
|
169
|
+
# print files with an indent
|
170
|
+
puts " Files (excluding hidden files): #{non_hidden.size}"
|
171
|
+
non_hidden.each { |p| puts " #{p}" }
|
172
|
+
end
|
173
|
+
|
174
|
+
# @return [Array<String>] The list of installed libraries
|
175
|
+
def install_arduino_library_dependencies(library_names, on_behalf_of, already_installed = [])
|
176
|
+
installed = already_installed.clone
|
177
|
+
(library_names.map { |n| @backend.library_of_name(n) } - installed).each do |l|
|
178
|
+
if l.installed?
|
179
|
+
inform("Using pre-existing dependency of #{on_behalf_of}") { l.name }
|
180
|
+
else
|
181
|
+
assure("Installing dependency of #{on_behalf_of}: '#{l.name}'") do
|
182
|
+
next nil unless l.install
|
183
|
+
|
184
|
+
l.name
|
185
|
+
end
|
186
|
+
end
|
187
|
+
installed << l.name
|
188
|
+
installed += install_arduino_library_dependencies(l.arduino_library_dependencies, l.name, installed)
|
189
|
+
end
|
190
|
+
installed
|
191
|
+
end
|
192
|
+
|
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]}" }
|
247
|
+
if ENV[expectation_envvar].nil?
|
248
|
+
inform_multiline("Skipping #{operation}") do
|
249
|
+
puts " In case that's an error, this is what was found in the #{dir_desc}:"
|
250
|
+
display_files(dir)
|
251
|
+
puts " To force an error in this case, set the environment variable #{expectation_envvar}"
|
252
|
+
true
|
253
|
+
end
|
254
|
+
else
|
255
|
+
assure_multiline("Dumping project's #{dir_desc} before exit") do
|
256
|
+
display_files(dir)
|
257
|
+
false
|
258
|
+
end
|
259
|
+
end
|
260
|
+
end
|
261
|
+
|
262
|
+
# report and return the set of compilers
|
263
|
+
def get_annotated_compilers(config, cpp_library)
|
264
|
+
# check GCC
|
265
|
+
compilers = config.compilers_to_use
|
266
|
+
assure("The set of compilers (#{compilers.length}) isn't empty") { !compilers.empty? }
|
267
|
+
compilers.each do |gcc_binary|
|
268
|
+
attempt_multiline("Checking #{gcc_binary} version") do
|
269
|
+
version = cpp_library.gcc_version(gcc_binary)
|
270
|
+
next nil unless version
|
271
|
+
|
272
|
+
puts version.split("\n").map { |l| " #{l}" }.join("\n")
|
273
|
+
version
|
274
|
+
end
|
275
|
+
inform("libasan availability for #{gcc_binary}") { cpp_library.libasan?(gcc_binary) }
|
276
|
+
end
|
277
|
+
compilers
|
278
|
+
end
|
279
|
+
|
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) }
|
289
|
+
|
290
|
+
inform("Library conforms to Arduino library specification") { cpp_library.one_point_five? ? "1.5" : "1.0" }
|
291
|
+
|
292
|
+
# Handle lack of test files
|
293
|
+
if cpp_library.test_files.empty?
|
294
|
+
handle_expectation_of_files(VAR_EXPECT_UNITTESTS, "unit tests", "test files", "tests directory", cpp_library.tests_dir)
|
295
|
+
return
|
296
|
+
end
|
297
|
+
|
298
|
+
# Handle lack of platforms
|
299
|
+
if config.platforms_to_unittest.empty?
|
300
|
+
inform("Skipping unit tests") { "no platforms were requested" }
|
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
|
323
|
+
end
|
324
|
+
cpp_library.run_test_file(exe)
|
325
|
+
end
|
326
|
+
end
|
327
|
+
end
|
328
|
+
end
|
329
|
+
end
|
330
|
+
|
331
|
+
def perform_example_compilation_tests(cpp_library, config)
|
332
|
+
if @cli_options[:skip_compilation]
|
333
|
+
inform("Skipping compilation of examples") { "as requested via command line" }
|
334
|
+
return
|
335
|
+
end
|
336
|
+
|
337
|
+
# gather up all required boards for compilation so we can install them up front.
|
338
|
+
# start with the "platforms to unittest" and add the examples
|
339
|
+
# while we're doing that, get the aux libraries as well
|
340
|
+
example_platform_info = {}
|
341
|
+
board_package_url = {}
|
342
|
+
aux_libraries = Set.new(config.aux_libraries_for_build)
|
343
|
+
# while collecting the platforms, ensure they're defined
|
344
|
+
|
345
|
+
library_examples = cpp_library.example_sketches
|
346
|
+
library_examples.each do |path|
|
347
|
+
ovr_config = config.from_example(path)
|
348
|
+
ovr_config.platforms_to_build.each do |platform|
|
349
|
+
# assure the platform if we haven't already
|
350
|
+
next if example_platform_info.key?(platform)
|
351
|
+
|
352
|
+
platform_info = assured_platform("library example", platform, config)
|
353
|
+
next if platform_info.nil?
|
354
|
+
|
355
|
+
example_platform_info[platform] = platform_info
|
356
|
+
package = platform_info[:package]
|
357
|
+
board_package_url[package] = ovr_config.package_url(package)
|
358
|
+
end
|
359
|
+
aux_libraries.merge(ovr_config.aux_libraries_for_build)
|
360
|
+
end
|
361
|
+
|
362
|
+
install_all_packages(example_platform_info, board_package_url)
|
363
|
+
install_arduino_library_dependencies(aux_libraries, "<compile/libraries>")
|
364
|
+
|
365
|
+
if config.platforms_to_build.empty?
|
366
|
+
inform("Skipping builds") { "no platforms were requested" }
|
367
|
+
return
|
368
|
+
elsif library_examples.empty?
|
369
|
+
handle_expectation_of_files(VAR_EXPECT_EXAMPLES, "builds", "examples", "the examples directory", cpp_library.examples_dir)
|
370
|
+
return
|
371
|
+
end
|
372
|
+
|
373
|
+
library_examples.each do |example_path|
|
374
|
+
ovr_config = config.from_example(example_path)
|
375
|
+
ovr_config.platforms_to_build.each do |p|
|
376
|
+
board = example_platform_info[p][:board]
|
377
|
+
example_name = File.basename(example_path)
|
378
|
+
attempt("Compiling #{example_name} for #{board}") do
|
379
|
+
ret = @backend.compile_sketch(example_path, board)
|
380
|
+
unless ret
|
381
|
+
puts
|
382
|
+
puts "Last command: #{@backend.last_msg}"
|
383
|
+
puts @backend.last_err
|
384
|
+
end
|
385
|
+
ret
|
386
|
+
end
|
387
|
+
end
|
388
|
+
end
|
389
|
+
end
|
390
|
+
|
391
|
+
# initialize command and config
|
392
|
+
config = ArduinoCI::CIConfig.default.from_project_library
|
393
|
+
|
394
|
+
@backend = ArduinoCI::ArduinoInstallation.autolocate!
|
395
|
+
inform("Located arduino-cli binary") { @backend.binary_path.to_s }
|
396
|
+
|
397
|
+
# initialize library under test
|
398
|
+
cpp_library_path = Pathname.new(".")
|
399
|
+
cpp_library = assure("Installing library under test") do
|
400
|
+
@backend.install_local_library(cpp_library_path)
|
401
|
+
end
|
402
|
+
|
403
|
+
assumed_name = @backend.name_of_library(cpp_library_path)
|
404
|
+
ondisk_name = cpp_library_path.realpath.basename
|
405
|
+
if assumed_name != ondisk_name
|
406
|
+
inform("WARNING") { "Installed library named '#{assumed_name}' has directory name '#{ondisk_name}'" }
|
407
|
+
end
|
408
|
+
|
409
|
+
if !cpp_library.nil?
|
410
|
+
inform("Library installed at") { cpp_library.path.to_s }
|
411
|
+
else
|
412
|
+
# this is a longwinded way of failing, we aren't really "assuring" anything at this point
|
413
|
+
assure_multiline("Library installed successfully") do
|
414
|
+
puts @backend.last_msg
|
415
|
+
false
|
416
|
+
end
|
417
|
+
end
|
418
|
+
|
419
|
+
install_arduino_library_dependencies(
|
420
|
+
cpp_library.arduino_library_dependencies,
|
421
|
+
"<#{ArduinoCI::CppLibrary::LIBRARY_PROPERTIES_FILE}>"
|
422
|
+
)
|
423
|
+
|
424
|
+
perform_unit_tests(cpp_library, config)
|
425
|
+
perform_example_compilation_tests(cpp_library, config)
|
426
|
+
|
427
|
+
terminate(true)
|