arduino_ci 0.3.0 → 1.3.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 (49) hide show
  1. checksums.yaml +5 -5
  2. data/README.md +125 -69
  3. data/REFERENCE.md +711 -0
  4. data/cpp/arduino/Arduino.h +1 -7
  5. data/cpp/arduino/ArduinoDefines.h +3 -0
  6. data/cpp/arduino/AvrMath.h +117 -17
  7. data/cpp/arduino/Client.h +27 -0
  8. data/cpp/arduino/EEPROM.h +64 -0
  9. data/cpp/arduino/Godmode.cpp +7 -0
  10. data/cpp/arduino/Godmode.h +121 -15
  11. data/cpp/arduino/HardwareSerial.h +4 -4
  12. data/cpp/arduino/IPAddress.h +59 -0
  13. data/cpp/arduino/Print.h +9 -12
  14. data/cpp/arduino/Printable.h +8 -0
  15. data/cpp/arduino/SPI.h +11 -3
  16. data/cpp/arduino/Server.h +5 -0
  17. data/cpp/arduino/Udp.h +27 -0
  18. data/cpp/arduino/Wire.h +197 -77
  19. data/cpp/arduino/avr/io.h +10 -1
  20. data/cpp/arduino/avr/pgmspace.h +76 -46
  21. data/cpp/unittest/ArduinoUnitTests.h +32 -0
  22. data/cpp/unittest/Assertion.h +54 -26
  23. data/cpp/unittest/Compare.h +58 -51
  24. data/cpp/unittest/OstreamHelpers.h +4 -0
  25. data/exe/arduino_ci.rb +538 -0
  26. data/exe/arduino_ci_remote.rb +2 -393
  27. data/exe/arduino_library_location.rb +2 -2
  28. data/exe/ensure_arduino_installation.rb +7 -1
  29. data/lib/arduino_ci.rb +1 -0
  30. data/lib/arduino_ci/arduino_backend.rb +238 -0
  31. data/lib/arduino_ci/arduino_downloader.rb +43 -73
  32. data/lib/arduino_ci/arduino_downloader_linux.rb +17 -55
  33. data/lib/arduino_ci/arduino_downloader_osx.rb +21 -33
  34. data/lib/arduino_ci/arduino_downloader_windows.rb +11 -53
  35. data/lib/arduino_ci/arduino_installation.rb +18 -80
  36. data/lib/arduino_ci/ci_config.rb +8 -11
  37. data/lib/arduino_ci/cpp_library.rb +250 -59
  38. data/lib/arduino_ci/host.rb +59 -4
  39. data/lib/arduino_ci/library_properties.rb +101 -0
  40. data/lib/arduino_ci/version.rb +1 -1
  41. data/misc/default.yml +57 -6
  42. metadata +19 -87
  43. data/cpp/arduino/Arduino.h.orig +0 -143
  44. data/exe/libasan.rb +0 -29
  45. data/lib/arduino_ci/arduino_cmd.rb +0 -332
  46. data/lib/arduino_ci/arduino_cmd_linux.rb +0 -17
  47. data/lib/arduino_ci/arduino_cmd_linux_builder.rb +0 -19
  48. data/lib/arduino_ci/arduino_cmd_osx.rb +0 -17
  49. data/lib/arduino_ci/arduino_cmd_windows.rb +0 -17
@@ -1,143 +0,0 @@
1
- #pragma once
2
- /*
3
- Mock Arduino.h library.
4
-
5
- Where possible, variable names from the Arduino library are used to avoid conflicts
6
-
7
- */
8
- // Chars and strings
9
-
10
- #include "ArduinoDefines.h"
11
-
12
- #include "WCharacter.h"
13
- #include "WString.h"
14
- #include "Print.h"
15
- #include "Stream.h"
16
- #include "HardwareSerial.h"
17
- #include "SPI.h"
18
- #include "Nullptr.h"
19
-
20
- typedef bool boolean;
21
- typedef uint8_t byte;
22
-
23
- #include "binary.h"
24
-
25
- // Math and Trig
26
- #include "AvrMath.h"
27
-
28
- #include "Godmode.h"
29
-
30
-
31
- // Bits and Bytes
32
- #define bit(b) (1UL << (b))
33
- #define bitClear(value, bit) ((value) &= ~(1UL << (bit)))
34
- #define bitRead(value, bit) (((value) >> (bit)) & 0x01)
35
- #define bitSet(value, bit) ((value) |= (1UL << (bit)))
36
- #define bitWrite(value, bit, bitvalue) (bitvalue ? bitSet(value, bit) : bitClear(value, bit))
37
- #define highByte(w) ((uint8_t) ((w) >> 8))
38
- #define lowByte(w) ((uint8_t) ((w) & 0xff))
39
-
40
- // Arduino defines this
41
- #define _NOP() do { 0; } while (0)
42
-
43
- // might as well use that NO-op macro for these, while unit testing
44
- // you need interrupts? interrupt yourself
45
- #define yield() _NOP()
46
- #define interrupts() _NOP()
47
- #define noInterrupts() _NOP()
48
-
49
- // TODO: correctly establish this per-board!
50
- #define F_CPU 1000000UL
51
- #define clockCyclesPerMicrosecond() ( F_CPU / 1000000L )
52
- #define clockCyclesToMicroseconds(a) ( (a) / clockCyclesPerMicrosecond() )
53
- #define microsecondsToClockCycles(a) ( (a) * clockCyclesPerMicrosecond() )
54
-
55
- typedef unsigned int word;
56
-
57
- #define bit(b) (1UL << (b))
58
-
59
-
60
-
61
- /*
62
- unsigned long pulseIn(uint8_t pin, uint8_t state, unsigned long timeout);
63
- unsigned long pulseInLong(uint8_t pin, uint8_t state, unsigned long timeout);
64
-
65
- void shiftOut(uint8_t dataPin, uint8_t clockPin, uint8_t bitOrder, uint8_t val);
66
- uint8_t shiftIn(uint8_t dataPin, uint8_t clockPin, uint8_t bitOrder);
67
-
68
- */
69
-
70
- // Get the bit location within the hardware port of the given virtual pin.
71
- // This comes from the pins_*.c file for the active board configuration.
72
-
73
- #define analogInPinToBit(P) (P)
74
- #define digitalPinToInterrupt(P) (P)
75
-
76
- /*
77
- // On the ATmega1280, the addresses of some of the port registers are
78
- // greater than 255, so we can't store them in uint8_t's.
79
- extern const uint16_t PROGMEM port_to_mode_PGM[];
80
- extern const uint16_t PROGMEM port_to_input_PGM[];
81
- extern const uint16_t PROGMEM port_to_output_PGM[];
82
-
83
- extern const uint8_t PROGMEM digital_pin_to_port_PGM[];
84
- // extern const uint8_t PROGMEM digital_pin_to_bit_PGM[];
85
- extern const uint8_t PROGMEM digital_pin_to_bit_mask_PGM[];
86
- extern const uint8_t PROGMEM digital_pin_to_timer_PGM[];
87
-
88
- // Get the bit location within the hardware port of the given virtual pin.
89
- // This comes from the pins_*.c file for the active board configuration.
90
- //
91
- // These perform slightly better as macros compared to inline functions
92
- //
93
- #define digitalPinToPort(P) ( pgm_read_byte( digital_pin_to_port_PGM + (P) ) )
94
- #define digitalPinToBitMask(P) ( pgm_read_byte( digital_pin_to_bit_mask_PGM + (P) ) )
95
- #define digitalPinToTimer(P) ( pgm_read_byte( digital_pin_to_timer_PGM + (P) ) )
96
- #define analogInPinToBit(P) (P)
97
- #define portOutputRegister(P) ( (volatile uint8_t *)( pgm_read_word( port_to_output_PGM + (P))) )
98
- #define portInputRegister(P) ( (volatile uint8_t *)( pgm_read_word( port_to_input_PGM + (P))) )
99
- #define portModeRegister(P) ( (volatile uint8_t *)( pgm_read_word( port_to_mode_PGM + (P))) )
100
-
101
- */
102
-
103
-
104
- /* TODO
105
-
106
- // USB
107
- #include "USBAPI.h"
108
- Keyboard
109
- Mouse
110
-
111
- */
112
-
113
- // uint16_t makeWord(uint16_t w);
114
- // uint16_t makeWord(byte h, byte l);
115
- inline unsigned int makeWord(unsigned int w) { return w; }
116
- inline unsigned int makeWord(unsigned char h, unsigned char l) { return (h << 8) | l; }
117
-
118
- #define word(...) makeWord(__VA_ARGS__)
119
-
120
- <<<<<<< Updated upstream
121
-
122
- =======
123
- unsigned long pulseIn(uint8_t pin, uint8_t state, unsigned long timeout = 1000000L);
124
- unsigned long pulseInLong(uint8_t pin, uint8_t state, unsigned long timeout = 1000000L);
125
-
126
- // audio is taken care of in GODMODE
127
-
128
-
129
- // BIG TODO ON THIS ONE
130
- // $ find . | grep pins_
131
- // ./arduino-1.8.5/hardware/arduino/avr/variants/circuitplay32u4/pins_arduino.h
132
- // ./arduino-1.8.5/hardware/arduino/avr/variants/eightanaloginputs/pins_arduino.h
133
- // ./arduino-1.8.5/hardware/arduino/avr/variants/ethernet/pins_arduino.h
134
- // ./arduino-1.8.5/hardware/arduino/avr/variants/gemma/pins_arduino.h
135
- // ./arduino-1.8.5/hardware/arduino/avr/variants/leonardo/pins_arduino.h
136
- // ./arduino-1.8.5/hardware/arduino/avr/variants/mega/pins_arduino.h
137
- // ./arduino-1.8.5/hardware/arduino/avr/variants/micro/pins_arduino.h
138
- // ./arduino-1.8.5/hardware/arduino/avr/variants/robot_control/pins_arduino.h
139
- // ./arduino-1.8.5/hardware/arduino/avr/variants/robot_motor/pins_arduino.h
140
- // ./arduino-1.8.5/hardware/arduino/avr/variants/standard/pins_arduino.h
141
- // ./arduino-1.8.5/hardware/arduino/avr/variants/yun/pins_arduino.h
142
- // #include "pins_arduino.h"
143
- >>>>>>> Stashed changes
@@ -1,29 +0,0 @@
1
- #!/usr/bin/env ruby
2
- require 'arduino_ci'
3
- require 'set'
4
- require 'pathname'
5
-
6
- WIDTH = 80
7
-
8
- # initialize command and config
9
- config = ArduinoCI::CIConfig.default.from_project_library
10
- @arduino_cmd = ArduinoCI::ArduinoInstallation.autolocate!
11
-
12
- # initialize library under test
13
- installed_library_path = @arduino_cmd.install_local_library(Pathname.new("."))
14
- cpp_library = ArduinoCI::CppLibrary.new(installed_library_path, @arduino_cmd.lib_dir)
15
-
16
- # check GCC
17
- compilers = config.compilers_to_use
18
- compilers.each do |gcc_binary|
19
- puts "Checking #{gcc_binary} version"
20
- puts cpp_library.gcc_version(gcc_binary).split("\n").map { |l| " #{l}" }.join("\n")
21
- exists = cpp_library.libasan?(gcc_binary)
22
- puts "libasan availability for #{gcc_binary}: #{exists}"
23
- next unless exists
24
-
25
- puts "========== Stdout:"
26
- puts @arduino_cmd.last_out
27
- puts "========== Stderr:"
28
- puts @arduino_cmd.last_err
29
- end
@@ -1,332 +0,0 @@
1
- require 'fileutils'
2
- require 'pathname'
3
-
4
- # workaround for https://github.com/arduino/Arduino/issues/3535
5
- WORKAROUND_LIB = "USBHost".freeze
6
-
7
- module ArduinoCI
8
-
9
- # To report errors that we can't resolve or possibly even explain
10
- class ArduinoExecutionError < StandardError; end
11
-
12
- # Wrap the Arduino executable. This requires, in some cases, a faked display.
13
- class ArduinoCmd
14
-
15
- # Enable a shortcut syntax for command line flags
16
- # @param name [String] What the flag will be called (prefixed with 'flag_')
17
- # @return [void]
18
- # @macro [attach] flag
19
- # The text of the command line flag for $1
20
- # @!attribute [r] flag_$1
21
- # @return [String] the text of the command line flag (`$2` in this case)
22
- def self.flag(name, text = nil)
23
- text = "(flag #{name} not defined)" if text.nil?
24
- self.class_eval("def flag_#{name};\"#{text}\";end", __FILE__, __LINE__)
25
- end
26
-
27
- # the array of command components to launch the Arduino executable
28
- # @return [Array<String>]
29
- attr_accessor :base_cmd
30
-
31
- # the actual path to the executable on this platform
32
- # @return [Pathname]
33
- attr_accessor :binary_path
34
-
35
- # part of a workaround for https://github.com/arduino/Arduino/issues/3535
36
- attr_reader :libraries_indexed
37
-
38
- # @return [String] STDOUT of the most recently-run command
39
- attr_reader :last_out
40
-
41
- # @return [String] STDERR of the most recently-run command
42
- attr_reader :last_err
43
-
44
- # @return [String] the most recently-run command
45
- attr_reader :last_msg
46
-
47
- # set the command line flags (undefined for now).
48
- # These vary between gui/cli. Inline comments added for greppability
49
- flag :get_pref # flag_get_pref
50
- flag :set_pref # flag_set_pref
51
- flag :save_prefs # flag_save_prefs
52
- flag :use_board # flag_use_board
53
- flag :install_boards # flag_install_boards
54
- flag :install_library # flag_install_library
55
- flag :verify # flag_verify
56
-
57
- def initialize
58
- @prefs_cache = {}
59
- @prefs_fetched = false
60
- @libraries_indexed = false
61
- @last_out = ""
62
- @last_err = ""
63
- @last_msg = ""
64
- end
65
-
66
- # Convert a preferences dump into a flat hash
67
- # @param arduino_output [String] The raw Arduino executable output
68
- # @return [Hash] preferences as a hash
69
- def parse_pref_string(arduino_output)
70
- lines = arduino_output.split("\n").select { |l| l.include? "=" }
71
- ret = lines.each_with_object({}) do |e, acc|
72
- parts = e.split("=", 2)
73
- acc[parts[0]] = parts[1]
74
- acc
75
- end
76
- ret
77
- end
78
-
79
- # @return [String] the path to the Arduino libraries directory
80
- def lib_dir
81
- Pathname.new(get_pref("sketchbook.path")) + "libraries"
82
- end
83
-
84
- # fetch preferences in their raw form
85
- # @return [String] Preferences as a set of lines
86
- def _prefs_raw
87
- resp = run_and_capture(flag_get_pref)
88
- fail_msg = "Arduino binary failed to operate as expected; you will have to troubleshoot it manually"
89
- raise ArduinoExecutionError, "#{fail_msg}. The command was #{@last_msg}" unless resp[:success]
90
-
91
- @prefs_fetched = true
92
- resp[:out]
93
- end
94
-
95
- # Get the Arduino preferences, from cache if possible
96
- # @return [Hash] The full set of preferences
97
- def prefs
98
- prefs_raw = _prefs_raw unless @prefs_fetched
99
- return nil if prefs_raw.nil?
100
-
101
- @prefs_cache = parse_pref_string(prefs_raw)
102
- @prefs_cache.clone
103
- end
104
-
105
- # get a preference key
106
- # @param key [String] The preferences key to look up
107
- # @return [String] The preference value
108
- def get_pref(key)
109
- data = @prefs_fetched ? @prefs_cache : prefs
110
- data[key]
111
- end
112
-
113
- # underlying preference-setter.
114
- # @param key [String] The preference name
115
- # @param value [String] The value to set to
116
- # @return [bool] whether the command succeeded
117
- def _set_pref(key, value)
118
- run_and_capture(flag_set_pref, "#{key}=#{value}", flag_save_prefs)[:success]
119
- end
120
-
121
- # set a preference key/value pair, and update the cache.
122
- # @param key [String] the preference key
123
- # @param value [String] the preference value
124
- # @return [bool] whether the command succeeded
125
- def set_pref(key, value)
126
- prefs unless @prefs_fetched # update cache first
127
- success = _set_pref(key, value)
128
- @prefs_cache[key] = value if success
129
- success
130
- end
131
-
132
- def _wrap_run(work_fn, *args, **kwargs)
133
- # do some work to extract & merge environment variables if they exist
134
- has_env = !args.empty? && args[0].class == Hash
135
- env_vars = has_env ? args[0] : {}
136
- actual_args = has_env ? args[1..-1] : args # need to shift over if we extracted args
137
- full_args = @base_cmd + actual_args
138
- full_cmd = env_vars.empty? ? full_args : [env_vars] + full_args
139
-
140
- shell_vars = env_vars.map { |k, v| "#{k}=#{v}" }.join(" ")
141
- @last_msg = " $ #{shell_vars} #{full_args.join(' ')}"
142
- work_fn.call(*full_cmd, **kwargs)
143
- end
144
-
145
- # build and run the arduino command
146
- def run_and_output(*args, **kwargs)
147
- _wrap_run((proc { |*a, **k| Host.run_and_output(*a, **k) }), *args, **kwargs)
148
- end
149
-
150
- # run a command and capture its output
151
- # @return [Hash] {:out => String, :err => String, :success => bool}
152
- def run_and_capture(*args, **kwargs)
153
- ret = _wrap_run((proc { |*a, **k| Host.run_and_capture(*a, **k) }), *args, **kwargs)
154
- @last_err = ret[:err]
155
- @last_out = ret[:out]
156
- ret
157
- end
158
-
159
- # Board manager URLs
160
- # @return [Array<String>] The additional URLs used by the board manager
161
- def board_manager_urls
162
- url_list = get_pref("boardsmanager.additional.urls")
163
- return [] if url_list.nil?
164
-
165
- url_list.split(",")
166
- end
167
-
168
- # Set board manager URLs
169
- # @return [Array<String>] The additional URLs used by the board manager
170
- def board_manager_urls=(all_urls)
171
- set_pref("boardsmanager.additional.urls", all_urls.join(","))
172
- end
173
-
174
- # check whether a board is installed
175
- # we do this by just selecting a board.
176
- # the arduino binary will error if unrecognized and do a successful no-op if it's installed
177
- # @param boardname [String] The board to test
178
- # @return [bool] Whether the board is installed
179
- def board_installed?(boardname)
180
- run_and_capture(flag_use_board, boardname)[:success]
181
- end
182
-
183
- # install a board by name
184
- # @param name [String] the board name
185
- # @return [bool] whether the command succeeded
186
- def install_boards(boardfamily)
187
- # TODO: find out why IO.pipe fails but File::NULL succeeds :(
188
- result = run_and_capture(flag_install_boards, boardfamily)
189
- already_installed = result[:err].include?("Platform is already installed!")
190
- result[:success] || already_installed
191
- end
192
-
193
- # install a library by name
194
- # @param name [String] the library name
195
- # @return [bool] whether the command succeeded
196
- def _install_library(library_name)
197
- result = run_and_capture(flag_install_library, library_name)
198
-
199
- already_installed = result[:err].include?("Library is already installed: #{library_name}")
200
- success = result[:success] || already_installed
201
-
202
- @libraries_indexed = (@libraries_indexed || success) if library_name == WORKAROUND_LIB
203
- success
204
- end
205
-
206
- # index the set of libraries by installing a dummy library
207
- # related to WORKAROUND_LIB and https://github.com/arduino/Arduino/issues/3535
208
- # TODO: unclear if this is still necessary
209
- def index_libraries
210
- return true if @libraries_indexed
211
-
212
- _install_library(WORKAROUND_LIB)
213
- @libraries_indexed
214
- end
215
-
216
- # install a library by name
217
- # @param name [String] the library name
218
- # @return [bool] whether the command succeeded
219
- def install_library(library_name)
220
- index_libraries
221
- _install_library(library_name)
222
- end
223
-
224
- # generate the (very likely) path of a library given its name
225
- # @param library_name [String] The name of the library
226
- # @return [Pathname] The fully qualified library name
227
- def library_path(library_name)
228
- Pathname.new(lib_dir) + library_name
229
- end
230
-
231
- # Determine whether a library is present in the lib dir
232
- #
233
- # Note that `true` doesn't guarantee that the library is valid/installed
234
- # and `false` doesn't guarantee that the library isn't built-in
235
- #
236
- # @param library_name [String] The name of the library
237
- # @return [bool]
238
- def library_present?(library_name)
239
- library_path(library_name).exist?
240
- end
241
-
242
- # update the library index
243
- # @return [bool] Whether the update succeeded
244
- def update_library_index
245
- # install random lib so the arduino IDE grabs a new library index
246
- # see: https://github.com/arduino/Arduino/issues/3535
247
- install_library(WORKAROUND_LIB)
248
- end
249
-
250
- # use a particular board for compilation
251
- # @param boardname [String] The board to use
252
- # @return [bool] whether the command succeeded
253
- def use_board(boardname)
254
- run_and_capture(flag_use_board, boardname, flag_save_prefs)[:success]
255
- end
256
-
257
- # use a particular board for compilation, installing it if necessary
258
- # @param boardname [String] The board to use
259
- # @return [bool] whether the command succeeded
260
- def use_board!(boardname)
261
- return true if use_board(boardname)
262
-
263
- boardfamily = boardname.split(":")[0..1].join(":")
264
- puts "Board '#{boardname}' not found; attempting to install '#{boardfamily}'"
265
- return false unless install_boards(boardfamily) # guess board family from first 2 :-separated fields
266
-
267
- use_board(boardname)
268
- end
269
-
270
- # @param path [String] The sketch to verify
271
- # @return [bool] whether the command succeeded
272
- def verify_sketch(path)
273
- ext = File.extname path
274
- unless ext.casecmp(".ino").zero?
275
- @last_msg = "Refusing to verify sketch with '#{ext}' extension -- rename it to '.ino'!"
276
- return false
277
- end
278
- unless File.exist? path
279
- @last_msg = "Can't verify Sketch at nonexistent path '#{path}'!"
280
- return false
281
- end
282
- ret = run_and_capture(flag_verify, path)
283
- ret[:success]
284
- end
285
-
286
- # ensure that the given library is installed, or symlinked as appropriate
287
- # return the path of the prepared library, or nil
288
- # @param path [Pathname] library to use
289
- # @return [String] the path of the installed library
290
- def install_local_library(path)
291
- src_path = path.realpath
292
- library_name = src_path.basename
293
- destination_path = library_path(library_name)
294
-
295
- # things get weird if the sketchbook contains the library.
296
- # check that first
297
- if destination_path.exist?
298
- uhoh = "There is already a library '#{library_name}' in the library directory"
299
- return destination_path if destination_path == src_path
300
-
301
- # maybe it's a symlink? that would be OK
302
- if destination_path.symlink?
303
- return destination_path if destination_path.readlink == src_path
304
-
305
- @last_msg = "#{uhoh} and it's not symlinked to #{src_path}"
306
- return nil
307
- end
308
-
309
- @last_msg = "#{uhoh}. It may need to be removed manually."
310
- return nil
311
- end
312
-
313
- # install the library
314
- Host.symlink(src_path, destination_path)
315
- destination_path
316
- end
317
-
318
- # @param installed_library_path [String] The library to query
319
- # @return [Array<String>] Example sketch files
320
- def library_examples(installed_library_path)
321
- example_path = Pathname.new(installed_library_path) + "examples"
322
- return [] unless File.exist?(example_path)
323
-
324
- examples = example_path.children.select(&:directory?).map(&:to_path).map(&File.method(:basename))
325
- files = examples.map do |e|
326
- proj_file = example_path + e + "#{e}.ino"
327
- proj_file.exist? ? proj_file.to_s : nil
328
- end
329
- files.reject(&:nil?).sort_by(&:to_s)
330
- end
331
- end
332
- end