arduino_ci 0.3.0 → 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.
- checksums.yaml +4 -4
- data/README.md +21 -19
- data/REFERENCE.md +625 -0
- data/cpp/arduino/Arduino.h +1 -2
- data/cpp/arduino/AvrMath.h +117 -17
- data/cpp/arduino/Client.h +26 -0
- data/cpp/arduino/EEPROM.h +64 -0
- data/cpp/arduino/Godmode.cpp +7 -0
- data/cpp/arduino/Godmode.h +58 -9
- data/cpp/arduino/HardwareSerial.h +4 -4
- 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 +173 -77
- data/cpp/arduino/avr/io.h +10 -1
- data/cpp/arduino/avr/pgmspace.h +76 -46
- data/cpp/unittest/OstreamHelpers.h +4 -0
- data/exe/arduino_ci.rb +401 -0
- data/exe/arduino_ci_remote.rb +2 -393
- data/lib/arduino_ci.rb +1 -0
- data/lib/arduino_ci/arduino_downloader.rb +5 -4
- data/lib/arduino_ci/arduino_installation.rb +5 -5
- data/lib/arduino_ci/cpp_library.rb +124 -24
- data/lib/arduino_ci/installed_cpp_library.rb +0 -0
- data/lib/arduino_ci/library_properties.rb +86 -0
- data/lib/arduino_ci/version.rb +1 -1
- data/misc/default.yml +50 -3
- metadata +15 -7
- data/cpp/arduino/Arduino.h.orig +0 -143
- data/exe/libasan.rb +0 -29
data/cpp/arduino/avr/pgmspace.h
CHANGED
@@ -14,8 +14,10 @@ out = externs.map {|l| l.split("(")[0].split(" ")[-1].gsub("*", "") }.uniq
|
|
14
14
|
out.each { |l| puts d(l) }
|
15
15
|
*/
|
16
16
|
|
17
|
-
#include <avr/io.h>
|
18
17
|
#include <string.h>
|
18
|
+
#include <stdio.h>
|
19
|
+
#include <stdint.h>
|
20
|
+
#include <stdarg.h>
|
19
21
|
|
20
22
|
#define PROGMEM
|
21
23
|
|
@@ -27,6 +29,11 @@ out.each { |l| puts d(l) }
|
|
27
29
|
#define PGM_VOID_P const void *
|
28
30
|
#endif
|
29
31
|
|
32
|
+
// These are normally 32-bit, but here use (u)intptr_t to ensure a pointer can
|
33
|
+
// always be safely cast to these types.
|
34
|
+
typedef intptr_t int_farptr_t;
|
35
|
+
typedef uintptr_t uint_farptr_t;
|
36
|
+
|
30
37
|
// everything's a no-op
|
31
38
|
#define PSTR(s) ((const char *)(s))
|
32
39
|
|
@@ -34,13 +41,13 @@ out.each { |l| puts d(l) }
|
|
34
41
|
#define pgm_read_word_near(address_short) (* (const uint16_t *) (address_short) )
|
35
42
|
#define pgm_read_dword_near(address_short) (* (const uint32_t *) (address_short) )
|
36
43
|
#define pgm_read_float_near(address_short) (* (const float *) (address_short) )
|
37
|
-
#define pgm_read_ptr_near(address_short) (* (const void
|
44
|
+
#define pgm_read_ptr_near(address_short) (* (const void **) (address_short) )
|
38
45
|
|
39
46
|
#define pgm_read_byte_far(address_long) (* (const uint8_t *) (address_long) )
|
40
47
|
#define pgm_read_word_far(address_long) (* (const uint16_t *) (address_long) )
|
41
48
|
#define pgm_read_dword_far(address_long) (* (const uint32_t *) (address_long) )
|
42
49
|
#define pgm_read_float_far(address_long) (* (const float *) (address_long) )
|
43
|
-
#define pgm_read_ptr_far(address_long) (* (const void
|
50
|
+
#define pgm_read_ptr_far(address_long) (* (const void **) (address_long) )
|
44
51
|
|
45
52
|
#define pgm_read_byte(address_short) pgm_read_byte_near(address_short)
|
46
53
|
#define pgm_read_word(address_short) pgm_read_word_near(address_short)
|
@@ -50,46 +57,69 @@ out.each { |l| puts d(l) }
|
|
50
57
|
|
51
58
|
#define pgm_get_far_address(var) ( (uint_farptr_t) (&(var)) )
|
52
59
|
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
60
|
+
inline const void * memchr_P(const void *s, int val, size_t len) { return memchr(s, val, len); }
|
61
|
+
inline int memcmp_P(const void *s1, const void *s2, size_t len) { return memcmp(s1, s2, len); }
|
62
|
+
inline void *memcpy_P(void *dest, const void *src, size_t n) { return memcpy(dest, src, n); }
|
63
|
+
inline char *strcat_P(char *dest, const char *src) { return strcat(dest, src); }
|
64
|
+
inline const char *strchr_P(const char *s, int val) { return strchr(s, val); }
|
65
|
+
inline int strcmp_P(const char *s1, const char *s2) { return strcmp(s1, s2); }
|
66
|
+
inline char *strcpy_P(char *dest, const char *src) { return strcpy(dest, src); }
|
67
|
+
inline size_t strcspn_P(const char *s, const char *reject) { return strcspn(s, reject); }
|
68
|
+
// strlcat and strlcpy are AVR-specific and not entirely trivial to reimplement using strncat it seems
|
69
|
+
//inline size_t strlcat_P(char *dst, const char *src, size_t siz) { return strlcat(dst, src, siz); }
|
70
|
+
//inline size_t strlcpy_P(char *dst, const char *src, size_t siz) { return strlcpy(dst, src, siz); }
|
71
|
+
//inline size_t strlcat_PF(char *dst, uint_farptr_t src, size_t n) { return strlcat(dst, (const char*)src, n); }
|
72
|
+
//inline size_t strlcpy_PF(char *dst, uint_farptr_t src, size_t siz) { return strlcpy(dst, (const char*)src, siz); }
|
73
|
+
inline int strncmp_P(const char *s1, const char *s2, size_t n) { return strncmp(s1, s2, n); }
|
74
|
+
inline char *strncat_P(char *dest, const char *src, size_t len) { return strncat(dest, src, len); }
|
75
|
+
inline char *strncpy_P(char *dest, const char *src, size_t n) { return strncpy(dest, src, n); }
|
76
|
+
inline char *strpbrk_P(const char *s, const char *accept) { return (char*)strpbrk(s, accept); }
|
77
|
+
inline const char *strrchr_P(const char *s, int val) { return strrchr(s, val); }
|
78
|
+
inline size_t strspn_P(const char *s, const char *accept) { return strspn(s, accept); }
|
79
|
+
inline char *strstr_P(const char *s1, const char *s2) { return (char*)strstr(s1, s2); }
|
80
|
+
inline char *strtok_P(char *s, const char * delim) { return strtok(s, delim); }
|
81
|
+
inline size_t strlen_PF(uint_farptr_t s) { return strlen((char*)s); }
|
82
|
+
inline void *memcpy_PF(void *dest, uint_farptr_t src, size_t n) { return memcpy(dest, (const char*)src, n); }
|
83
|
+
inline char *strcpy_PF(char *dst, uint_farptr_t src) { return strcpy(dst, (const char*)src); }
|
84
|
+
inline char *strncpy_PF(char *dst, uint_farptr_t src, size_t n) { return strncpy(dst, (const char*)src, n); }
|
85
|
+
inline char *strcat_PF(char *dst, uint_farptr_t src) { return strcat(dst, (const char*)src); }
|
86
|
+
inline char *strncat_PF(char *dst, uint_farptr_t src, size_t n) { return strncat(dst, (const char*)src, n); }
|
87
|
+
inline int strcmp_PF(const char *s1, uint_farptr_t s2) { return strcmp(s1, (const char*)s2); }
|
88
|
+
inline int strncmp_PF(const char *s1, uint_farptr_t s2, size_t n) { return strncmp(s1, (const char*)s2, n); }
|
89
|
+
inline char *strstr_PF(const char *s1, uint_farptr_t s2) { return (char*)strstr(s1, (const char*)s2); }
|
90
|
+
inline int memcmp_PF(const void *s1, uint_farptr_t s2, size_t len) { return memcmp(s1, (const char*)s2, len); }
|
91
|
+
inline size_t strlen_P(const char *src) { return strlen(src); }
|
92
|
+
|
93
|
+
// TODO: These functions cannot be found on the CYGWIN test build for
|
94
|
+
// some reason, so disable them for now. Most of these are less common
|
95
|
+
// and/or GNU-specific addons anyway
|
96
|
+
//inline void *memccpy_P(void *dest, const void *src, int val, size_t len) { return memccpy(dest, src, val, len); }
|
97
|
+
//inline void *memmem_P(const void *s1, size_t len1, const void *s2, size_t len2) { return memmem(s1, len1, s2, len2); }
|
98
|
+
//inline const void *memrchr_P(const void *src, int val, size_t len) { return memrchr(src, val, len); }
|
99
|
+
//inline const char *strchrnul_P(const char *s, int c) { return strchrnul(s, c); }
|
100
|
+
//inline int strcasecmp_P(const char *s1, const char *s2) { return strcasecmp(s1, s2); }
|
101
|
+
//inline char *strcasestr_P(const char *s1, const char *s2) { return (char*)strcasestr(s1, s2); }
|
102
|
+
//inline int strncasecmp_P(const char *s1, const char *s2, size_t n) { return strncasecmp(s1, s2, n); }
|
103
|
+
//inline char *strsep_P(char **sp, const char *delim) { return strsep(sp, delim); }
|
104
|
+
//inline char *strtok_r_P(char *string, const char *delim, char **last) { return strtok_r(string, delim, last); }
|
105
|
+
//inline int strcasecmp_PF(const char *s1, uint_farptr_t s2) { return strcasecmp(s1, (const char*)s2); }
|
106
|
+
//inline int strncasecmp_PF(const char *s1, uint_farptr_t s2, size_t n) { return strncasecmp(s1, (const char*)s2, n); }
|
107
|
+
//inline size_t strnlen_P(uint_farptr_t s, size_t len) { return strnlen((char*)s, len); }
|
108
|
+
|
109
|
+
// These are normally defined by stdio.h on AVR, but we cannot override that
|
110
|
+
// include file (at least not without no longer being able to include the
|
111
|
+
// original as well), so just define these here. It seems likely that any
|
112
|
+
// sketch that uses these progmem-stdio functions will also include pgmspace.h
|
113
|
+
inline int vfprintf_P(FILE *stream, const char *__fmt, va_list __ap) { return vfprintf(stream, __fmt, __ap); }
|
114
|
+
inline int printf_P(const char *__fmt, ...) { va_list args; va_start(args, __fmt); return vprintf(__fmt, args); va_end(args); }
|
115
|
+
inline int sprintf_P(char *s, const char *__fmt, ...) { va_list args; va_start(args, __fmt); return sprintf(s, __fmt, args); va_end(args); }
|
116
|
+
inline int snprintf_P(char *s, size_t __n, const char *__fmt, ...) { va_list args; va_start(args, __fmt); return vsnprintf(s, __n, __fmt, args); va_end(args); }
|
117
|
+
inline int vsprintf_P(char *s, const char *__fmt, va_list ap) { return vsprintf(s, __fmt, ap); }
|
118
|
+
inline int vsnprintf_P(char *s, size_t __n, const char *__fmt, va_list ap) { return vsnprintf(s, __n, __fmt, ap); }
|
119
|
+
inline int fprintf_P(FILE *stream, const char *__fmt, ...) { va_list args; va_start(args, __fmt); return vfprintf(stream, __fmt, args); va_end(args); }
|
120
|
+
inline int fputs_P(const char *str, FILE *__stream) { return fputs(str, __stream); }
|
121
|
+
inline int puts_P(const char *str) { return puts(str); }
|
122
|
+
inline int vfscanf_P(FILE *stream, const char *__fmt, va_list __ap) { return vfscanf(stream, __fmt, __ap); }
|
123
|
+
inline int fscanf_P(FILE *stream, const char *__fmt, ...) { va_list args; va_start(args, __fmt); return vfscanf(stream, __fmt, args); va_end(args); }
|
124
|
+
inline int scanf_P(const char *__fmt, ...) { va_list args; va_start(args, __fmt); return vscanf(__fmt, args); va_end(args); }
|
125
|
+
inline int sscanf_P(const char *buf, const char *__fmt, ...) { va_list args; va_start(args, __fmt); return vsscanf(buf, __fmt, args); va_end(args); }
|
@@ -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,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)
|