Skip to content

Commit 427490c

Browse files
committed
Switch to arduino-cli 0.13.0 backend
1 parent a54e72b commit 427490c

17 files changed

+189
-524
lines changed

Diff for: CHANGELOG.md

+2
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
2525
- Mocks of built-in macros made more accurate
2626
- NUM_SERIAL_PORTS can now be set explicitly
2727
- Improve SPI header strategy
28+
- Arduino backend is now `arduino-cli` version `0.13.0`
2829

2930
### Fixed
3031
- Don't define `ostream& operator<<(nullptr_t)` if already defined by Apple
@@ -37,6 +38,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
3738
- Deprecated `arduino_ci_remote.rb` in favor of `arduino_ci.rb`
3839

3940
### Removed
41+
- `ARDUINO_CI_SKIP_SPLASH_SCREEN_RSPEC_TESTS` no longer affects any tests because there are no longer splash screens since switching to `arduino-cli`
4042

4143
### Security
4244

Diff for: CONTRIBUTING.md

-1
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,6 @@ See `SampleProjects/TestSomething/test/*.cpp` for the existing tests (run by rsp
3131

3232
To speed up testing by targeting only the files you're working on, you can set several environment variables that `bundle exec rspec` will respond to:
3333

34-
* `ARDUINO_CI_SKIP_SPLASH_SCREEN_RSPEC_TESTS`: if set, this will avoid any rspec test that calls the arduino executable (and as such, causes the splash screen to pop up).
3534
* `ARDUINO_CI_SKIP_RUBY_RSPEC_TESTS`: if set, this will skip all tests against ruby code (useful if you are not changing Ruby code).
3635
* `ARDUINO_CI_SKIP_CPP_RSPEC_TESTS`: if set, this will skip all tests against the `TestSomething` sample project (useful if you are not changing C++ code).
3736
* `ARDUINO_CI_SELECT_CPP_TESTS=<glob>`: if set, this will skip all C++ unit tests whose filenames don't match the provided glob (executed in the tests directory)

Diff for: exe/arduino_ci.rb

100644100755
+3-7
Original file line numberDiff line numberDiff line change
@@ -345,7 +345,6 @@ def perform_compilation_tests(config)
345345

346346
install_arduino_library_dependencies(aux_libraries)
347347

348-
last_board = nil
349348
if config.platforms_to_build.empty?
350349
inform("Skipping builds") { "no platforms were requested" }
351350
return
@@ -370,13 +369,10 @@ def perform_compilation_tests(config)
370369

371370
examples_by_platform.each do |platform, example_paths|
372371
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-
376372
example_paths.each do |example_path|
377373
example_name = File.basename(example_path)
378-
attempt("Verifying #{example_name}") do
379-
ret = @arduino_cmd.verify_sketch(example_path)
374+
attempt("Compiling #{example_name} for #{board}") do
375+
ret = @arduino_cmd.compile_sketch(example_path, board)
380376
unless ret
381377
puts
382378
puts "Last command: #{@arduino_cmd.last_msg}"
@@ -393,7 +389,7 @@ def perform_compilation_tests(config)
393389
config = ArduinoCI::CIConfig.default.from_project_library
394390

395391
@arduino_cmd = ArduinoCI::ArduinoInstallation.autolocate!
396-
inform("Located Arduino binary") { @arduino_cmd.binary_path.to_s }
392+
inform("Located arduino-cli binary") { @arduino_cmd.binary_path.to_s }
397393

398394
perform_unit_tests(config)
399395
perform_compilation_tests(config)

Diff for: lib/arduino_ci/arduino_cmd.rb

+37-112
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
require 'fileutils'
22
require 'pathname'
3+
require 'json'
34

45
# workaround for https://github.com/arduino/Arduino/issues/3535
56
WORKAROUND_LIB = "USBHost".freeze
@@ -24,10 +25,6 @@ def self.flag(name, text = nil)
2425
self.class_eval("def flag_#{name};\"#{text}\";end", __FILE__, __LINE__)
2526
end
2627

27-
# the array of command components to launch the Arduino executable
28-
# @return [Array<String>]
29-
attr_accessor :base_cmd
30-
3128
# the actual path to the executable on this platform
3229
# @return [Pathname]
3330
attr_accessor :binary_path
@@ -44,97 +41,30 @@ def self.flag(name, text = nil)
4441
# @return [String] the most recently-run command
4542
attr_reader :last_msg
4643

44+
# @return [Array<String>] Additional URLs for the boards manager
45+
attr_reader :additional_urls
46+
4747
# set the command line flags (undefined for now).
4848
# 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
5349
flag :install_boards # flag_install_boards
5450
flag :install_library # flag_install_library
5551
flag :verify # flag_verify
5652

57-
def initialize
58-
@prefs_cache = {}
59-
@prefs_fetched = false
53+
def initialize(binary_path)
54+
@binary_path = binary_path
6055
@libraries_indexed = false
56+
@additional_urls = []
6157
@last_out = ""
6258
@last_err = ""
6359
@last_msg = ""
6460
end
6561

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-
13262
def _wrap_run(work_fn, *args, **kwargs)
13363
# do some work to extract & merge environment variables if they exist
13464
has_env = !args.empty? && args[0].class == Hash
13565
env_vars = has_env ? args[0] : {}
13666
actual_args = has_env ? args[1..-1] : args # need to shift over if we extracted args
137-
full_args = @base_cmd + actual_args
67+
full_args = [binary_path.to_s, "--format", "json"] + actual_args
13868
full_cmd = env_vars.empty? ? full_args : [env_vars] + full_args
13969

14070
shell_vars = env_vars.map { |k, v| "#{k}=#{v}" }.join(" ")
@@ -156,19 +86,34 @@ def run_and_capture(*args, **kwargs)
15686
ret
15787
end
15888

89+
def capture_json(*args, **kwargs)
90+
ret = run_and_capture(*args, **kwargs)
91+
ret[:json] = JSON.parse(ret[:out])
92+
end
93+
94+
# Get a dump of the entire config
95+
# @return [Hash] The configuration
96+
def config_dump
97+
capture_json("config", "dump")
98+
end
99+
100+
# @return [String] the path to the Arduino libraries directory
101+
def lib_dir
102+
Pathname.new(config_dump["directories"]["user"]) + "libraries"
103+
end
104+
159105
# Board manager URLs
160106
# @return [Array<String>] The additional URLs used by the board manager
161107
def board_manager_urls
162-
url_list = get_pref("boardsmanager.additional.urls")
163-
return [] if url_list.nil?
164-
165-
url_list.split(",")
108+
config_dump["board_manager"]["additional_urls"] + @additional_urls
166109
end
167110

168111
# Set board manager URLs
169112
# @return [Array<String>] The additional URLs used by the board manager
170113
def board_manager_urls=(all_urls)
171-
set_pref("boardsmanager.additional.urls", all_urls.join(","))
114+
raise ArgumentError("all_urls should be an array, got #{all_urls.class}") unless all_urls.is_a? Array
115+
116+
@additional_urls = all_urls
172117
end
173118

174119
# check whether a board is installed
@@ -177,17 +122,16 @@ def board_manager_urls=(all_urls)
177122
# @param boardname [String] The board to test
178123
# @return [bool] Whether the board is installed
179124
def board_installed?(boardname)
180-
run_and_capture(flag_use_board, boardname)[:success]
125+
# capture_json("core", "list")[:json].find { |b| b["ID"] == boardname } # nope, this is for the family
126+
run_and_capture("board", "details", "--fqbn", boardname)[:success]
181127
end
182128

183129
# install a board by name
184130
# @param name [String] the board name
185131
# @return [bool] whether the command succeeded
186132
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
133+
result = run_and_capture("core", "install", boardfamily)
134+
result[:success]
191135
end
192136

193137
# install a library by name
@@ -247,39 +191,20 @@ def update_library_index
247191
install_library(WORKAROUND_LIB)
248192
end
249193

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
194+
# @param path [String] The sketch to compile
258195
# @param boardname [String] The board to use
259196
# @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)
197+
def compile_sketch(path, boardname)
273198
ext = File.extname path
274199
unless ext.casecmp(".ino").zero?
275-
@last_msg = "Refusing to verify sketch with '#{ext}' extension -- rename it to '.ino'!"
200+
@last_msg = "Refusing to compile sketch with '#{ext}' extension -- rename it to '.ino'!"
276201
return false
277202
end
278203
unless File.exist? path
279-
@last_msg = "Can't verify Sketch at nonexistent path '#{path}'!"
204+
@last_msg = "Can't compile Sketch at nonexistent path '#{path}'!"
280205
return false
281206
end
282-
ret = run_and_capture(flag_verify, path)
207+
ret = run_and_capture("compile", "--fqbn", boardname, "--dry-run", path)
283208
ret[:success]
284209
end
285210

Diff for: lib/arduino_ci/arduino_cmd_linux.rb

-17
This file was deleted.

Diff for: lib/arduino_ci/arduino_cmd_linux_builder.rb

-19
This file was deleted.

Diff for: lib/arduino_ci/arduino_cmd_osx.rb

-17
This file was deleted.

Diff for: lib/arduino_ci/arduino_cmd_windows.rb

-17
This file was deleted.

0 commit comments

Comments
 (0)