arduino_ci 0.1.21 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (48) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +49 -20
  3. data/REFERENCE.md +625 -0
  4. data/cpp/arduino/Arduino.h +1 -1
  5. data/cpp/arduino/AvrMath.h +117 -17
  6. data/cpp/arduino/Client.h +27 -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/OstreamHelpers.h +4 -0
  24. data/exe/arduino_ci.rb +400 -0
  25. data/exe/arduino_ci_remote.rb +2 -385
  26. data/exe/arduino_library_location.rb +2 -2
  27. data/lib/arduino_ci.rb +1 -0
  28. data/lib/arduino_ci/arduino_backend.rb +218 -0
  29. data/lib/arduino_ci/arduino_downloader.rb +42 -72
  30. data/lib/arduino_ci/arduino_downloader_linux.rb +17 -55
  31. data/lib/arduino_ci/arduino_downloader_osx.rb +21 -33
  32. data/lib/arduino_ci/arduino_downloader_windows.rb +11 -53
  33. data/lib/arduino_ci/arduino_installation.rb +18 -80
  34. data/lib/arduino_ci/ci_config.rb +12 -7
  35. data/lib/arduino_ci/cpp_library.rb +262 -48
  36. data/lib/arduino_ci/host.rb +59 -4
  37. data/lib/arduino_ci/library_properties.rb +96 -0
  38. data/lib/arduino_ci/version.rb +1 -1
  39. data/misc/default.yml +55 -4
  40. metadata +18 -83
  41. data/cpp/arduino/Arduino.h.orig +0 -143
  42. data/cpp/arduino/ci/Queue.h +0 -73
  43. data/exe/libasan.rb +0 -29
  44. data/lib/arduino_ci/arduino_cmd.rb +0 -328
  45. data/lib/arduino_ci/arduino_cmd_linux.rb +0 -17
  46. data/lib/arduino_ci/arduino_cmd_linux_builder.rb +0 -19
  47. data/lib/arduino_ci/arduino_cmd_osx.rb +0 -17
  48. 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,73 +0,0 @@
1
- #pragma once
2
-
3
- template <typename T>
4
- class ArduinoCIQueue {
5
- private:
6
- struct Node {
7
- T data;
8
- Node* next;
9
- };
10
-
11
- Node* mFront;
12
- Node* mBack;
13
- unsigned long mSize;
14
- T mNil;
15
-
16
- void init() {
17
- mFront = mBack = NULL;
18
- mSize = 0;
19
- }
20
-
21
- public:
22
- ArduinoCIQueue(): mNil() { init(); }
23
-
24
- ArduinoCIQueue(const ArduinoCIQueue<T>& q) {
25
- init();
26
- for (Node* n = q.mFront; n; n = n->next) push(n->data);
27
- }
28
-
29
- inline unsigned long size() const { return mSize; }
30
-
31
- inline bool empty() const { return 0 == mSize; }
32
-
33
- T front() const { return empty() ? mNil : mFront->data; }
34
-
35
- T back() const { return empty() ? mNil : mBack->data; }
36
-
37
- bool push(const T& v)
38
- {
39
- Node *n = new Node;
40
- if (n == NULL) return false;
41
-
42
- n->data = v;
43
- n->next = NULL;
44
-
45
- if (mFront == NULL)
46
- {
47
- mFront = mBack = n;
48
- } else {
49
- mBack->next = n;
50
- mBack = n;
51
- }
52
-
53
- ++mSize;
54
- return true;
55
- }
56
-
57
- void pop() {
58
- if (empty()) return;
59
- if (mFront == mBack) {
60
- mFront = mBack = NULL;
61
- } else {
62
- Node* n = mFront;
63
- mFront = mFront->next;
64
- delete n;
65
- }
66
-
67
- --mSize;
68
- }
69
-
70
- void clear() { while (!empty()) pop(); }
71
-
72
- ~ArduinoCIQueue() { clear(); }
73
- };
@@ -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,328 +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
- # Wrap the Arduino executable. This requires, in some cases, a faked display.
10
- class ArduinoCmd
11
-
12
- # Enable a shortcut syntax for command line flags
13
- # @param name [String] What the flag will be called (prefixed with 'flag_')
14
- # @return [void]
15
- # @macro [attach] flag
16
- # The text of the command line flag for $1
17
- # @!attribute [r] flag_$1
18
- # @return [String] the text of the command line flag (`$2` in this case)
19
- def self.flag(name, text = nil)
20
- text = "(flag #{name} not defined)" if text.nil?
21
- self.class_eval("def flag_#{name};\"#{text}\";end", __FILE__, __LINE__)
22
- end
23
-
24
- # the array of command components to launch the Arduino executable
25
- # @return [Array<String>]
26
- attr_accessor :base_cmd
27
-
28
- # the actual path to the executable on this platform
29
- # @return [Pathname]
30
- attr_accessor :binary_path
31
-
32
- # part of a workaround for https://github.com/arduino/Arduino/issues/3535
33
- attr_reader :libraries_indexed
34
-
35
- # @return [String] STDOUT of the most recently-run command
36
- attr_reader :last_out
37
-
38
- # @return [String] STDERR of the most recently-run command
39
- attr_reader :last_err
40
-
41
- # @return [String] the most recently-run command
42
- attr_reader :last_msg
43
-
44
- # set the command line flags (undefined for now).
45
- # These vary between gui/cli
46
- flag :get_pref
47
- flag :set_pref
48
- flag :save_prefs
49
- flag :use_board
50
- flag :install_boards
51
- flag :install_library
52
- flag :verify
53
-
54
- def initialize
55
- @prefs_cache = {}
56
- @prefs_fetched = false
57
- @libraries_indexed = false
58
- @last_out = ""
59
- @last_err = ""
60
- @last_msg = ""
61
- end
62
-
63
- # Convert a preferences dump into a flat hash
64
- # @param arduino_output [String] The raw Arduino executable output
65
- # @return [Hash] preferences as a hash
66
- def parse_pref_string(arduino_output)
67
- lines = arduino_output.split("\n").select { |l| l.include? "=" }
68
- ret = lines.each_with_object({}) do |e, acc|
69
- parts = e.split("=", 2)
70
- acc[parts[0]] = parts[1]
71
- acc
72
- end
73
- ret
74
- end
75
-
76
- # @return [String] the path to the Arduino libraries directory
77
- def lib_dir
78
- Pathname.new(get_pref("sketchbook.path")) + "libraries"
79
- end
80
-
81
- # fetch preferences in their raw form
82
- # @return [String] Preferences as a set of lines
83
- def _prefs_raw
84
- resp = run_and_capture(flag_get_pref)
85
- return nil unless resp[:success]
86
-
87
- @prefs_fetched = true
88
- resp[:out]
89
- end
90
-
91
- # Get the Arduino preferences, from cache if possible
92
- # @return [Hash] The full set of preferences
93
- def prefs
94
- prefs_raw = _prefs_raw unless @prefs_fetched
95
- return nil if prefs_raw.nil?
96
-
97
- @prefs_cache = parse_pref_string(prefs_raw)
98
- @prefs_cache.clone
99
- end
100
-
101
- # get a preference key
102
- # @param key [String] The preferences key to look up
103
- # @return [String] The preference value
104
- def get_pref(key)
105
- data = @prefs_fetched ? @prefs_cache : prefs
106
- data[key]
107
- end
108
-
109
- # underlying preference-setter.
110
- # @param key [String] The preference name
111
- # @param value [String] The value to set to
112
- # @return [bool] whether the command succeeded
113
- def _set_pref(key, value)
114
- run_and_capture(flag_set_pref, "#{key}=#{value}", flag_save_prefs)[:success]
115
- end
116
-
117
- # set a preference key/value pair, and update the cache.
118
- # @param key [String] the preference key
119
- # @param value [String] the preference value
120
- # @return [bool] whether the command succeeded
121
- def set_pref(key, value)
122
- prefs unless @prefs_fetched # update cache first
123
- success = _set_pref(key, value)
124
- @prefs_cache[key] = value if success
125
- success
126
- end
127
-
128
- def _wrap_run(work_fn, *args, **kwargs)
129
- # do some work to extract & merge environment variables if they exist
130
- has_env = !args.empty? && args[0].class == Hash
131
- env_vars = has_env ? args[0] : {}
132
- actual_args = has_env ? args[1..-1] : args # need to shift over if we extracted args
133
- full_args = @base_cmd + actual_args
134
- full_cmd = env_vars.empty? ? full_args : [env_vars] + full_args
135
-
136
- shell_vars = env_vars.map { |k, v| "#{k}=#{v}" }.join(" ")
137
- @last_msg = " $ #{shell_vars} #{full_args.join(' ')}"
138
- work_fn.call(*full_cmd, **kwargs)
139
- end
140
-
141
- # build and run the arduino command
142
- def run_and_output(*args, **kwargs)
143
- _wrap_run((proc { |*a, **k| Host.run_and_output(*a, **k) }), *args, **kwargs)
144
- end
145
-
146
- # run a command and capture its output
147
- # @return [Hash] {:out => String, :err => String, :success => bool}
148
- def run_and_capture(*args, **kwargs)
149
- ret = _wrap_run((proc { |*a, **k| Host.run_and_capture(*a, **k) }), *args, **kwargs)
150
- @last_err = ret[:err]
151
- @last_out = ret[:out]
152
- ret
153
- end
154
-
155
- # Board manager URLs
156
- # @return [Array<String>] The additional URLs used by the board manager
157
- def board_manager_urls
158
- url_list = get_pref("boardsmanager.additional.urls")
159
- return [] if url_list.nil?
160
-
161
- url_list.split(",")
162
- end
163
-
164
- # Set board manager URLs
165
- # @return [Array<String>] The additional URLs used by the board manager
166
- def board_manager_urls=(all_urls)
167
- set_pref("boardsmanager.additional.urls", all_urls.join(","))
168
- end
169
-
170
- # check whether a board is installed
171
- # we do this by just selecting a board.
172
- # the arduino binary will error if unrecognized and do a successful no-op if it's installed
173
- # @param boardname [String] The board to test
174
- # @return [bool] Whether the board is installed
175
- def board_installed?(boardname)
176
- run_and_capture(flag_use_board, boardname)[:success]
177
- end
178
-
179
- # install a board by name
180
- # @param name [String] the board name
181
- # @return [bool] whether the command succeeded
182
- def install_boards(boardfamily)
183
- # TODO: find out why IO.pipe fails but File::NULL succeeds :(
184
- result = run_and_capture(flag_install_boards, boardfamily)
185
- already_installed = result[:err].include?("Platform is already installed!")
186
- result[:success] || already_installed
187
- end
188
-
189
- # install a library by name
190
- # @param name [String] the library name
191
- # @return [bool] whether the command succeeded
192
- def _install_library(library_name)
193
- result = run_and_capture(flag_install_library, library_name)
194
-
195
- already_installed = result[:err].include?("Library is already installed: #{library_name}")
196
- success = result[:success] || already_installed
197
-
198
- @libraries_indexed = (@libraries_indexed || success) if library_name == WORKAROUND_LIB
199
- success
200
- end
201
-
202
- # index the set of libraries by installing a dummy library
203
- # related to WORKAROUND_LIB and https://github.com/arduino/Arduino/issues/3535
204
- # TODO: unclear if this is still necessary
205
- def index_libraries
206
- return true if @libraries_indexed
207
-
208
- _install_library(WORKAROUND_LIB)
209
- @libraries_indexed
210
- end
211
-
212
- # install a library by name
213
- # @param name [String] the library name
214
- # @return [bool] whether the command succeeded
215
- def install_library(library_name)
216
- index_libraries
217
- _install_library(library_name)
218
- end
219
-
220
- # generate the (very likely) path of a library given its name
221
- # @param library_name [String] The name of the library
222
- # @return [Pathname] The fully qualified library name
223
- def library_path(library_name)
224
- Pathname.new(lib_dir) + library_name
225
- end
226
-
227
- # Determine whether a library is present in the lib dir
228
- #
229
- # Note that `true` doesn't guarantee that the library is valid/installed
230
- # and `false` doesn't guarantee that the library isn't built-in
231
- #
232
- # @param library_name [String] The name of the library
233
- # @return [bool]
234
- def library_present?(library_name)
235
- library_path(library_name).exist?
236
- end
237
-
238
- # update the library index
239
- # @return [bool] Whether the update succeeded
240
- def update_library_index
241
- # install random lib so the arduino IDE grabs a new library index
242
- # see: https://github.com/arduino/Arduino/issues/3535
243
- install_library(WORKAROUND_LIB)
244
- end
245
-
246
- # use a particular board for compilation
247
- # @param boardname [String] The board to use
248
- # @return [bool] whether the command succeeded
249
- def use_board(boardname)
250
- run_and_capture(flag_use_board, boardname, flag_save_prefs)[:success]
251
- end
252
-
253
- # use a particular board for compilation, installing it if necessary
254
- # @param boardname [String] The board to use
255
- # @return [bool] whether the command succeeded
256
- def use_board!(boardname)
257
- return true if use_board(boardname)
258
-
259
- boardfamily = boardname.split(":")[0..1].join(":")
260
- puts "Board '#{boardname}' not found; attempting to install '#{boardfamily}'"
261
- return false unless install_boards(boardfamily) # guess board family from first 2 :-separated fields
262
-
263
- use_board(boardname)
264
- end
265
-
266
- # @param path [String] The sketch to verify
267
- # @return [bool] whether the command succeeded
268
- def verify_sketch(path)
269
- ext = File.extname path
270
- unless ext.casecmp(".ino").zero?
271
- @last_msg = "Refusing to verify sketch with '#{ext}' extension -- rename it to '.ino'!"
272
- return false
273
- end
274
- unless File.exist? path
275
- @last_msg = "Can't verify Sketch at nonexistent path '#{path}'!"
276
- return false
277
- end
278
- ret = run_and_capture(flag_verify, path)
279
- ret[:success]
280
- end
281
-
282
- # ensure that the given library is installed, or symlinked as appropriate
283
- # return the path of the prepared library, or nil
284
- # @param path [Pathname] library to use
285
- # @return [String] the path of the installed library
286
- def install_local_library(path)
287
- src_path = path.realpath
288
- library_name = src_path.basename
289
- destination_path = library_path(library_name)
290
-
291
- # things get weird if the sketchbook contains the library.
292
- # check that first
293
- if destination_path.exist?
294
- uhoh = "There is already a library '#{library_name}' in the library directory"
295
- return destination_path if destination_path == src_path
296
-
297
- # maybe it's a symlink? that would be OK
298
- if destination_path.symlink?
299
- return destination_path if destination_path.readlink == src_path
300
-
301
- @last_msg = "#{uhoh} and it's not symlinked to #{src_path}"
302
- return nil
303
- end
304
-
305
- @last_msg = "#{uhoh}. It may need to be removed manually."
306
- return nil
307
- end
308
-
309
- # install the library
310
- Host.symlink(src_path, destination_path)
311
- destination_path
312
- end
313
-
314
- # @param installed_library_path [String] The library to query
315
- # @return [Array<String>] Example sketch files
316
- def library_examples(installed_library_path)
317
- example_path = Pathname.new(installed_library_path) + "examples"
318
- return [] unless File.exist?(example_path)
319
-
320
- examples = example_path.children.select(&:directory?).map(&:to_path).map(&File.method(:basename))
321
- files = examples.map do |e|
322
- proj_file = example_path + e + "#{e}.ino"
323
- proj_file.exist? ? proj_file.to_s : nil
324
- end
325
- files.reject(&:nil?).sort_by(&:to_s)
326
- end
327
- end
328
- end