arduino_ci 0.1.10 → 0.1.11

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.
@@ -0,0 +1,216 @@
1
+ #!/usr/bin/env ruby
2
+ require 'arduino_ci'
3
+ require 'set'
4
+ require 'pathname'
5
+
6
+ WIDTH = 80
7
+
8
+ @failure_count = 0
9
+ @passfail = proc { |result| result ? "✓" : "✗" }
10
+
11
+ # terminate after printing any debug info. TODO: capture debug info
12
+ def terminate(final = nil)
13
+ puts "Failures: #{@failure_count}"
14
+ unless @failure_count.zero? || final
15
+ puts "Last message: #{@arduino_cmd.last_msg}"
16
+ puts "========== Stdout:"
17
+ puts @arduino_cmd.last_out
18
+ puts "========== Stderr:"
19
+ puts @arduino_cmd.last_err
20
+ end
21
+ retcode = @failure_count.zero? ? 0 : 1
22
+ exit(retcode)
23
+ end
24
+
25
+ # make a nice status line for an action and react to the action
26
+ def perform_action(message, multiline, mark_fn, on_fail_msg, abort_on_fail)
27
+ line = "#{message}... "
28
+ endline = "...#{message} "
29
+ if multiline
30
+ puts line
31
+ else
32
+ print line
33
+ end
34
+ result = yield
35
+ mark = mark_fn.nil? ? "" : mark_fn.call(result)
36
+ # if multline, put checkmark at full width
37
+ print endline if multiline
38
+ puts mark.rjust(WIDTH - line.length, " ")
39
+ unless result
40
+ puts on_fail_msg unless on_fail_msg.nil?
41
+ @failure_count += 1
42
+ # print out error messaging here if we've captured it
43
+ terminate if abort_on_fail
44
+ end
45
+ result
46
+ end
47
+
48
+ # Make a nice status for something that defers any failure code until script exit
49
+ def attempt(message, &block)
50
+ perform_action(message, false, @passfail, nil, false, &block)
51
+ end
52
+
53
+ # Make a nice status for something that defers any failure code until script exit
54
+ def attempt_multiline(message, &block)
55
+ perform_action(message, true, @passfail, nil, false, &block)
56
+ end
57
+
58
+ # Make a nice status for something that kills the script immediately on failure
59
+ def assure(message, &block)
60
+ perform_action(message, false, @passfail, "This may indicate a problem with ArduinoCI, or your configuration", true, &block)
61
+ end
62
+
63
+ def inform(message, &block)
64
+ perform_action(message, false, proc { |x| x }, nil, false, &block)
65
+ end
66
+
67
+ def inform_multiline(message, &block)
68
+ <<<<<<< HEAD
69
+ perform_action(message, true, nil, nil, false, &block)
70
+ =======
71
+ perform_action(message, true, proc { }, nil, false, &block)
72
+ >>>>>>> 30d4a9f... Show file tree when unittests or examples aren't found
73
+ end
74
+
75
+ # Assure that a platform exists and return its definition
76
+ def assured_platform(purpose, name, config)
77
+ platform_definition = config.platform_definition(name)
78
+ assure("Requested #{purpose} platform '#{name}' is defined in 'platforms' YML") do
79
+ !platform_definition.nil?
80
+ end
81
+ platform_definition
82
+ end
83
+
84
+ # initialize command and config
85
+ config = ArduinoCI::CIConfig.default.from_project_library
86
+ @arduino_cmd = ArduinoCI::ArduinoInstallation.autolocate!
87
+
88
+ # initialize library under test
89
+ installed_library_path = assure("Installing library under test") { @arduino_cmd.install_local_library(".") }
90
+ library_examples = @arduino_cmd.library_examples(installed_library_path)
91
+ cpp_library = ArduinoCI::CppLibrary.new(installed_library_path, @arduino_cmd.lib_dir)
92
+ inform("Library installed at") { installed_library_path.to_s }
93
+
94
+ # check GCC
95
+ compilers = config.compilers_to_use
96
+ assure("The set of compilers (#{compilers.length}) isn't empty") { !compilers.empty? }
97
+ compilers.each do |gcc_binary|
98
+ attempt_multiline("Checking #{gcc_binary} version") do
99
+ version = cpp_library.gcc_version(gcc_binary)
100
+ next nil unless version
101
+
102
+ puts version.split("\n").map { |l| " #{l}" }.join("\n")
103
+ version
104
+ end
105
+ end
106
+
107
+ # gather up all required boards so we can install them up front.
108
+ # start with the "platforms to unittest" and add the examples
109
+ # while we're doing that, get the aux libraries as well
110
+ all_platforms = {}
111
+ aux_libraries = Set.new(config.aux_libraries_for_unittest + config.aux_libraries_for_build)
112
+ # while collecting the platforms, ensure they're defined
113
+ config.platforms_to_unittest.each { |p| all_platforms[p] = assured_platform("unittest", p, config) }
114
+ library_examples.each do |path|
115
+ ovr_config = config.from_example(path)
116
+ ovr_config.platforms_to_build.each { |p| all_platforms[p] = assured_platform("library example", p, config) }
117
+ aux_libraries.merge(ovr_config.aux_libraries_for_build)
118
+ end
119
+
120
+ # with all platform info, we can extract unique packages and their urls
121
+ # do that, set the URLs, and download the packages
122
+ all_packages = all_platforms.values.map { |v| v[:package] }.uniq.reject(&:nil?)
123
+ all_urls = all_packages.map { |p| config.package_url(p) }.uniq.reject(&:nil?)
124
+ unless all_urls.empty?
125
+ assure("Setting board manager URLs") do
126
+ @arduino_cmd.set_pref("boardsmanager.additional.urls", all_urls.join(","))
127
+ end
128
+ end
129
+
130
+ all_packages.each do |p|
131
+ assure("Installing board package #{p}") do
132
+ @arduino_cmd.install_boards(p)
133
+ end
134
+ end
135
+
136
+ aux_libraries.each do |l|
137
+ if @arduino_cmd.library_present?(l)
138
+ inform("Using pre-existing library") { l.to_s }
139
+ else
140
+ assure("Installing aux library '#{l}'") { @arduino_cmd.install_library(l) }
141
+ end
142
+ end
143
+
144
+ # iterate boards / tests
145
+ last_board = nil
146
+ if !cpp_library.tests_dir.exist?
147
+ inform_multiline("Skipping unit tests; no tests dir at #{cpp_library.tests_dir}") do
148
+ puts cpp_library.tests_dir.find.to_a.to_s
149
+ true
150
+ end
151
+ elsif cpp_library.test_files.empty?
152
+ inform_multiline("Skipping unit tests; no test files were found in #{cpp_library.tests_dir}") do
153
+ puts cpp_library.tests_dir.find.to_a.to_s
154
+ true
155
+ end
156
+ elsif config.platforms_to_unittest.empty?
157
+ inform("Skipping unit tests") { "no platforms were requested" }
158
+ else
159
+ config.platforms_to_unittest.each do |p|
160
+ board = all_platforms[p][:board]
161
+ last_board = "arduino:avr:pro:cpu=16MHzatmega328"
162
+ assure("Switching to board for #{p} (#{board})") { @arduino_cmd.use_board(board) } unless last_board == board
163
+ last_board = board
164
+ cpp_library.test_files.each do |unittest_path|
165
+ unittest_name = unittest_path.basename.to_s
166
+ compilers.each do |gcc_binary|
167
+ attempt_multiline("Unit testing #{unittest_name} with #{gcc_binary}") do
168
+ exe = cpp_library.build_for_test_with_configuration(
169
+ unittest_path,
170
+ config.aux_libraries_for_unittest,
171
+ gcc_binary,
172
+ config.gcc_config(p)
173
+ )
174
+ puts
175
+ unless exe
176
+ puts "Last command: #{cpp_library.last_cmd}"
177
+ puts cpp_library.last_out
178
+ puts cpp_library.last_err
179
+ next false
180
+ end
181
+ cpp_library.run_test_file(exe)
182
+ end
183
+ end
184
+ end
185
+ end
186
+ end
187
+
188
+ if library_examples.empty?
189
+ inform_multiline("Skipping libraries; no examples found in #{installed_library_path}") do
190
+ puts installed_library_path.find.to_a.to_s
191
+ end
192
+ else
193
+ attempt("Setting compiler warning level") { @arduino_cmd.set_pref("compiler.warning_level", "all") }
194
+
195
+ # unlike previous, iterate examples / boards
196
+ library_examples.each do |example_path|
197
+ ovr_config = config.from_example(example_path)
198
+ ovr_config.platforms_to_build.each do |p|
199
+ board = all_platforms[p][:board]
200
+ assure("Switching to board for #{p} (#{board})") { @arduino_cmd.use_board(board) } unless last_board == board
201
+ last_board = board
202
+ example_name = File.basename(example_path)
203
+ attempt("Verifying #{example_name}") do
204
+ ret = @arduino_cmd.verify_sketch(example_path)
205
+ unless ret
206
+ puts
207
+ puts "Last command: #{@arduino_cmd.last_msg}"
208
+ puts @arduino_cmd.last_err
209
+ end
210
+ ret
211
+ end
212
+ end
213
+ end
214
+ end
215
+
216
+ terminate(true)
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env ruby
2
+ require 'arduino_ci'
3
+
4
+ # this will exit after Arduino is located and/or forcibly installed
5
+ ArduinoCI::ArduinoInstallation.autolocate!
@@ -1,4 +1,5 @@
1
1
  require 'fileutils'
2
+ require 'pathname'
2
3
 
3
4
  module ArduinoCI
4
5
 
@@ -14,7 +15,7 @@ module ArduinoCI
14
15
  # @return [String] the text of the command line flag (`$2` in this case)
15
16
  def self.flag(name, text = nil)
16
17
  text = "(flag #{name} not defined)" if text.nil?
17
- self.class_eval("def flag_#{name};\"#{text}\";end")
18
+ self.class_eval("def flag_#{name};\"#{text}\";end", __FILE__, __LINE__)
18
19
  end
19
20
 
20
21
  # the path to the Arduino executable
@@ -66,8 +67,8 @@ module ArduinoCI
66
67
  end
67
68
 
68
69
  # @return [String] the path to the Arduino libraries directory
69
- def _lib_dir
70
- "<lib dir not defined>"
70
+ def lib_dir
71
+ Pathname.new(get_pref("sketchbook.path")) + "libraries"
71
72
  end
72
73
 
73
74
  # fetch preferences in their raw form
@@ -75,6 +76,8 @@ module ArduinoCI
75
76
  def _prefs_raw
76
77
  resp = run_and_capture(flag_get_pref)
77
78
  return nil unless resp[:success]
79
+
80
+ @prefs_fetched = true
78
81
  resp[:out]
79
82
  end
80
83
 
@@ -83,6 +86,7 @@ module ArduinoCI
83
86
  def prefs
84
87
  prefs_raw = _prefs_raw unless @prefs_fetched
85
88
  return nil if prefs_raw.nil?
89
+
86
90
  @prefs_cache = parse_pref_string(prefs_raw)
87
91
  @prefs_cache.clone
88
92
  end
@@ -108,23 +112,12 @@ module ArduinoCI
108
112
  # @param value [String] the preference value
109
113
  # @return [bool] whether the command succeeded
110
114
  def set_pref(key, value)
115
+ prefs unless @prefs_fetched # update cache first
111
116
  success = _set_pref(key, value)
112
117
  @prefs_cache[key] = value if success
113
118
  success
114
119
  end
115
120
 
116
- # run the arduino command
117
- # @return [bool] whether the command succeeded
118
- def _run_and_output(*args, **kwargs)
119
- raise "Ian needs to implement this in a subclass #{args} #{kwargs}"
120
- end
121
-
122
- # run the arduino command
123
- # @return [Hash] keys for :success, :out, and :err
124
- def _run_and_capture(*args, **kwargs)
125
- raise "Ian needs to implement this in a subclass #{args} #{kwargs}"
126
- end
127
-
128
121
  def _wrap_run(work_fn, *args, **kwargs)
129
122
  # do some work to extract & merge environment variables if they exist
130
123
  has_env = !args.empty? && args[0].class == Hash
@@ -140,13 +133,13 @@ module ArduinoCI
140
133
 
141
134
  # build and run the arduino command
142
135
  def run_and_output(*args, **kwargs)
143
- _wrap_run((proc { |*a, **k| _run_and_output(*a, **k) }), *args, **kwargs)
136
+ _wrap_run((proc { |*a, **k| Host.run_and_output(*a, **k) }), *args, **kwargs)
144
137
  end
145
138
 
146
139
  # run a command and capture its output
147
140
  # @return [Hash] {:out => String, :err => String, :success => bool}
148
141
  def run_and_capture(*args, **kwargs)
149
- ret = _wrap_run((proc { |*a, **k| _run_and_capture(*a, **k) }), *args, **kwargs)
142
+ ret = _wrap_run((proc { |*a, **k| Host.run_and_capture(*a, **k) }), *args, **kwargs)
150
143
  @last_err = ret[:err]
151
144
  @last_out = ret[:out]
152
145
  ret
@@ -200,9 +193,20 @@ module ArduinoCI
200
193
 
201
194
  # generate the (very likely) path of a library given its name
202
195
  # @param library_name [String] The name of the library
203
- # @return [String] The fully qualified library name
196
+ # @return [Pathname] The fully qualified library name
204
197
  def library_path(library_name)
205
- File.join(_lib_dir, library_name)
198
+ Pathname.new(lib_dir) + library_name
199
+ end
200
+
201
+ # Determine whether a library is present in the lib dir
202
+ #
203
+ # Note that `true` doesn't guarantee that the library is valid/installed
204
+ # and `false` doesn't guarantee that the library isn't built-in
205
+ #
206
+ # @param library_name [String] The name of the library
207
+ # @return [bool]
208
+ def library_present?(library_name)
209
+ library_path(library_name).exist?
206
210
  end
207
211
 
208
212
  # update the library index
@@ -225,9 +229,11 @@ module ArduinoCI
225
229
  # @return [bool] whether the command succeeded
226
230
  def use_board!(boardname)
227
231
  return true if use_board(boardname)
232
+
228
233
  boardfamily = boardname.split(":")[0..1].join(":")
229
234
  puts "Board '#{boardname}' not found; attempting to install '#{boardfamily}'"
230
235
  return false unless install_boards(boardfamily) # guess board family from first 2 :-separated fields
236
+
231
237
  use_board(boardname)
232
238
  end
233
239
 
@@ -249,23 +255,24 @@ module ArduinoCI
249
255
 
250
256
  # ensure that the given library is installed, or symlinked as appropriate
251
257
  # return the path of the prepared library, or nil
252
- # @param path [String] library to use
258
+ # @param path [Pathname] library to use
253
259
  # @return [String] the path of the installed library
254
260
  def install_local_library(path)
255
- realpath = File.expand_path(path)
256
- library_name = File.basename(realpath)
261
+ src_path = path.realpath
262
+ library_name = src_path.basename
257
263
  destination_path = library_path(library_name)
258
264
 
259
265
  # things get weird if the sketchbook contains the library.
260
266
  # check that first
261
- if File.exist? destination_path
267
+ if destination_path.exist?
262
268
  uhoh = "There is already a library '#{library_name}' in the library directory"
263
- return destination_path if destination_path == realpath
269
+ return destination_path if destination_path == src_path
264
270
 
265
271
  # maybe it's a symlink? that would be OK
266
- if File.symlink?(destination_path)
267
- return destination_path if File.readlink(destination_path) == realpath
268
- @last_msg = "#{uhoh} and it's not symlinked to #{realpath}"
272
+ if destination_path.symlink?
273
+ return destination_path if destination_path.readlink == src_path
274
+
275
+ @last_msg = "#{uhoh} and it's not symlinked to #{src_path}"
269
276
  return nil
270
277
  end
271
278
 
@@ -274,19 +281,20 @@ module ArduinoCI
274
281
  end
275
282
 
276
283
  # install the library
277
- Host.symlink(realpath, destination_path)
284
+ Host.symlink(src_path, destination_path)
278
285
  destination_path
279
286
  end
280
287
 
281
288
  # @param installed_library_path [String] The library to query
282
289
  # @return [Array<String>] Example sketch files
283
290
  def library_examples(installed_library_path)
284
- example_path = File.join(installed_library_path, "examples")
291
+ example_path = Pathname.new(installed_library_path) + "examples"
285
292
  return [] unless File.exist?(example_path)
286
- examples = Pathname.new(example_path).children.select(&:directory?).map(&:to_path).map(&File.method(:basename))
293
+
294
+ examples = example_path.children.select(&:directory?).map(&:to_path).map(&File.method(:basename))
287
295
  files = examples.map do |e|
288
- proj_file = File.join(example_path, e, "#{e}.ino")
289
- File.exist?(proj_file) ? proj_file : nil
296
+ proj_file = example_path + e + "#{e}.ino"
297
+ proj_file.exist? ? proj_file.to_s : nil
290
298
  end
291
299
  files.reject(&:nil?)
292
300
  end
@@ -1,5 +1,5 @@
1
1
  require 'arduino_ci/arduino_cmd'
2
- require 'arduino_ci/display_manager'
2
+ require 'timeout'
3
3
 
4
4
  module ArduinoCI
5
5
 
@@ -19,7 +19,6 @@ module ArduinoCI
19
19
  def initialize
20
20
  super
21
21
  @prefs_response_time = nil
22
- @display_mgr = DisplayManager::instance
23
22
  end
24
23
 
25
24
  # fetch preferences in their raw form
@@ -29,25 +28,8 @@ module ArduinoCI
29
28
  resp = run_and_capture(flag_get_pref)
30
29
  @prefs_response_time = Time.now - start
31
30
  return nil unless resp[:success]
32
- resp[:out]
33
- end
34
-
35
- # implementation for Arduino library dir location
36
- # @return [String] the path to the Arduino libraries directory
37
- def _lib_dir
38
- File.join(get_pref("sketchbook.path"), "libraries")
39
- end
40
31
 
41
- # run the arduino command
42
- # @return [bool] whether the command succeeded
43
- def _run_and_output(*args, **kwargs)
44
- @display_mgr.run_and_output(*args, **kwargs)
45
- end
46
-
47
- # run the arduino command
48
- # @return [Hash] keys for :success, :out, and :err
49
- def _run_and_capture(*args, **kwargs)
50
- @display_mgr.run_and_capture(*args, **kwargs)
32
+ resp[:out]
51
33
  end
52
34
 
53
35
  def run_with_gui_guess(message, *args, **kwargs)
@@ -14,24 +14,6 @@ module ArduinoCI
14
14
  flag :install_library, "--install-library" # apparently doesn't exist
15
15
  flag :verify, "-compile"
16
16
 
17
- # linux-specific implementation
18
- # @return [String] The path to the library dir
19
- def _lib_dir
20
- File.join(get_pref("sketchbook.path"), "libraries")
21
- end
22
-
23
- # run the arduino command
24
- # @return [bool] whether the command succeeded
25
- def _run_and_output(*args, **kwargs)
26
- Host.run_and_output(*args, **kwargs)
27
- end
28
-
29
- # run the arduino command
30
- # @return [Hash] keys for :success, :out, and :err
31
- def _run_and_capture(*args, **kwargs)
32
- Host.run_and_capture(*args, **kwargs)
33
- end
34
-
35
17
  end
36
18
 
37
19
  end