arduino_ci 0.1.10 → 0.1.11

Sign up to get free protection for your applications and to get access to all the features.
@@ -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