Class: ArduinoCI::ArduinoBackend
- Inherits:
-
Object
- Object
- ArduinoCI::ArduinoBackend
- Defined in:
- lib/arduino_ci/arduino_backend.rb
Overview
Wrap the Arduino executable. This requires, in some cases, a faked display.
Constant Summary collapse
- CONFIG_FILE_NAME =
We never even use this in code, it’s just here for reference because the backend is picky about it. Used for testing
"arduino-cli.yaml".freeze
- CONFIG_FILE_APOLOGY =
Unfortunately we need error messaging around this quirk
"Sorry this is weird, see https://github.com/arduino/arduino-cli/issues/753".freeze
Instance Attribute Summary collapse
-
#additional_urls ⇒ Array<String>
readonly
Additional URLs for the boards manager.
-
#binary_path ⇒ Pathname
the actual path to the executable on this platform.
-
#config_dir ⇒ Pathname
readonly
The directory that contains the config file.
-
#last_err ⇒ String
readonly
STDERR of the most recently-run command.
-
#last_msg ⇒ String
readonly
The most recently-run command.
-
#last_out ⇒ String
readonly
STDOUT of the most recently-run command.
Class Method Summary collapse
-
.config_file_path_from_dir(dir) ⇒ Pathname
Get an acceptable filename for use as a config file.
Instance Method Summary collapse
- #_wrap_run(work_fn, *args, **kwargs) ⇒ Object
-
#board_installed?(boardname) ⇒ bool
check whether a board is installed we do this by just selecting a board.
-
#board_manager_urls ⇒ Array<String>
Board manager URLs.
-
#board_manager_urls=(all_urls) ⇒ Array<String>
Set board manager URLs.
-
#boards_installed?(boardfamily_name) ⇒ bool
check whether a board family is installed (e.g. arduino:avr).
- #capture_json(*args, **kwargs) ⇒ Object
-
#compile_sketch(path, boardname) ⇒ bool
Whether the command succeeded.
-
#config_dump ⇒ Hash
Get a dump of the entire config.
-
#config_file_cli_param ⇒ Pathname
The config file to be used as a CLI param.
-
#config_file_path ⇒ Pathname
The config file name to be passed on the command line.
-
#config_file_path=(rhs) ⇒ Pathname
The config file name to be passed on the command line.
-
#initialize(binary_path) ⇒ ArduinoBackend
constructor
A new instance of ArduinoBackend.
-
#install_boards(boardfamily) ⇒ bool
install a board by name.
-
#install_local_library(path) ⇒ CppLibrary
install a library from a path on the local machine (not via library manager), by symlink or no-op as appropriate.
-
#installed_libraries ⇒ Hash
Information about installed libraries via the CLI.
-
#last_bytes_usage ⇒ Hash
extract the “Free space remaining” amount from the last run.
-
#lib_dir ⇒ String
The path to the Arduino libraries directory.
-
#library_available?(name) ⇒ bool
Find out if a library is available.
-
#library_of_name(name) ⇒ CppLibrary
Create a handle to an Arduino library by name.
-
#library_of_path(path) ⇒ CppLibrary
Create a handle to an Arduino library by path.
-
#name_of_library(path) ⇒ String
Guess the name of a library.
-
#run_and_capture(*args, **kwargs) ⇒ Hash
run a command and capture its output.
-
#run_and_output(*args, **kwargs) ⇒ Object
build and run the arduino command.
-
#should_use_config_dir? ⇒ Bool
Since the config dir behavior has changed from a directory to a file (At some point??).
-
#should_use_dry_run? ⇒ Bool
Since the dry-run behavior became default in arduino-cli 0.14, the command line flag was removed.
-
#version ⇒ Gem::Version
The arduino-cli version that the backend is using, as a semver object.
-
#version_str ⇒ String
The arduino-cli version that the backend is using, as String.
Constructor Details
#initialize(binary_path) ⇒ ArduinoBackend
Returns a new instance of ArduinoBackend.
44 45 46 47 48 49 50 51 52 |
# File 'lib/arduino_ci/arduino_backend.rb', line 44 def initialize(binary_path) @binary_path = binary_path @config_dir = nil @additional_urls = [] @last_out = "" @last_err = "" @last_msg = "" @config_dir_hack = false end |
Instance Attribute Details
#additional_urls ⇒ Array<String> (readonly)
Returns Additional URLs for the boards manager.
42 43 44 |
# File 'lib/arduino_ci/arduino_backend.rb', line 42 def additional_urls @additional_urls end |
#binary_path ⇒ Pathname
the actual path to the executable on this platform
26 27 28 |
# File 'lib/arduino_ci/arduino_backend.rb', line 26 def binary_path @binary_path end |
#config_dir ⇒ Pathname (readonly)
The directory that contains the config file
30 31 32 |
# File 'lib/arduino_ci/arduino_backend.rb', line 30 def config_dir @config_dir end |
#last_err ⇒ String (readonly)
Returns STDERR of the most recently-run command.
36 37 38 |
# File 'lib/arduino_ci/arduino_backend.rb', line 36 def last_err @last_err end |
#last_msg ⇒ String (readonly)
Returns the most recently-run command.
39 40 41 |
# File 'lib/arduino_ci/arduino_backend.rb', line 39 def last_msg @last_msg end |
#last_out ⇒ String (readonly)
Returns STDOUT of the most recently-run command.
33 34 35 |
# File 'lib/arduino_ci/arduino_backend.rb', line 33 def last_out @last_out end |
Class Method Details
.config_file_path_from_dir(dir) ⇒ Pathname
Get an acceptable filename for use as a config file
Note github.com/arduino/arduino-cli/issues/753 : the –config-file option is really the directory that contains the file
110 111 112 |
# File 'lib/arduino_ci/arduino_backend.rb', line 110 def self.config_file_path_from_dir(dir) Pathname(dir) + CONFIG_FILE_NAME end |
Instance Method Details
#_wrap_run(work_fn, *args, **kwargs) ⇒ Object
54 55 56 57 58 59 60 61 62 63 64 65 66 67 |
# File 'lib/arduino_ci/arduino_backend.rb', line 54 def _wrap_run(work_fn, *args, **kwargs) # do some work to extract & merge environment variables if they exist has_env = !args.empty? && args[0].instance_of?(Hash) env_vars = has_env ? args[0] : {} actual_args = has_env ? args[1..-1] : args # need to shift over if we extracted args custom_config = [] custom_config += ["--config-file", config_file_cli_param.to_s] unless @config_dir_hack || @config_dir.nil? full_args = [binary_path.to_s, "--format", "json"] + custom_config + actual_args full_cmd = env_vars.empty? ? full_args : [env_vars] + full_args shell_vars = env_vars.map { |k, v| "#{k}=#{v}" }.join(" ") @last_msg = " $ #{shell_vars} #{full_args.join(' ')}" work_fn.call(*full_cmd, **kwargs) end |
#board_installed?(boardname) ⇒ bool
check whether a board is installed we do this by just selecting a board.
the arduino binary will error if unrecognized and do a successful no-op if it's installed
166 167 168 |
# File 'lib/arduino_ci/arduino_backend.rb', line 166 def board_installed?(boardname) run_and_capture("board", "details", "--fqbn", boardname)[:success] end |
#board_manager_urls ⇒ Array<String>
Board manager URLs
149 150 151 |
# File 'lib/arduino_ci/arduino_backend.rb', line 149 def board_manager_urls config_dump["board_manager"]["additional_urls"] + @additional_urls end |
#board_manager_urls=(all_urls) ⇒ Array<String>
Set board manager URLs
155 156 157 158 159 |
# File 'lib/arduino_ci/arduino_backend.rb', line 155 def board_manager_urls=(all_urls) raise ArgumentError("all_urls should be an array, got #{all_urls.class}") unless all_urls.is_a? Array @additional_urls = all_urls end |
#boards_installed?(boardfamily_name) ⇒ bool
check whether a board family is installed (e.g. arduino:avr)
174 175 176 |
# File 'lib/arduino_ci/arduino_backend.rb', line 174 def boards_installed?(boardfamily_name) capture_json("core", "list")[:json].any? { |b| b["ID"] == boardfamily_name } end |
#capture_json(*args, **kwargs) ⇒ Object
128 129 130 131 132 |
# File 'lib/arduino_ci/arduino_backend.rb', line 128 def capture_json(*args, **kwargs) ret = run_and_capture(*args, **kwargs) ret[:json] = JSON.parse(ret[:out]) ret end |
#compile_sketch(path, boardname) ⇒ bool
Returns whether the command succeeded.
210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 |
# File 'lib/arduino_ci/arduino_backend.rb', line 210 def compile_sketch(path, boardname) ext = File.extname path unless ext.casecmp(".ino").zero? @last_msg = "Refusing to compile sketch with '#{ext}' extension -- rename it to '.ino'!" return false end unless File.exist? path @last_msg = "Can't compile Sketch at nonexistent path '#{path}'!" return false end ret = if should_use_dry_run? run_and_capture("compile", "--fqbn", boardname, "--warnings", "all", "--dry-run", path.to_s) else run_and_capture("compile", "--fqbn", boardname, "--warnings", "all", path.to_s) end @last_msg = ret[:out] ret[:success] end |
#config_dump ⇒ Hash
Get a dump of the entire config
136 137 138 |
# File 'lib/arduino_ci/arduino_backend.rb', line 136 def config_dump capture_json("config", "dump")[:json] end |
#config_file_cli_param ⇒ Pathname
The config file to be used as a CLI param
This format changes based on version, which is very annoying. See unit tests.
99 100 101 |
# File 'lib/arduino_ci/arduino_backend.rb', line 99 def config_file_cli_param should_use_config_dir? ? @config_dir : config_file_path end |
#config_file_path ⇒ Pathname
The config file name to be passed on the command line
Note github.com/arduino/arduino-cli/issues/753 : the –config-file option is really the directory that contains the file
75 76 77 |
# File 'lib/arduino_ci/arduino_backend.rb', line 75 def config_file_path @config_dir + CONFIG_FILE_NAME end |
#config_file_path=(rhs) ⇒ Pathname
The config file name to be passed on the command line
Note github.com/arduino/arduino-cli/issues/753 : the –config-file option is really the directory that contains the file
86 87 88 89 90 91 92 |
# File 'lib/arduino_ci/arduino_backend.rb', line 86 def config_file_path=(rhs) path_rhs = Pathname(rhs) err_text = "Config file basename must be '#{CONFIG_FILE_NAME}'. #{CONFIG_FILE_APOLOGY}" raise ArgumentError, err_text unless path_rhs.basename.to_s == CONFIG_FILE_NAME @config_dir = path_rhs.dirname end |
#install_boards(boardfamily) ⇒ bool
install a board by name
181 182 183 184 185 186 187 188 189 190 191 |
# File 'lib/arduino_ci/arduino_backend.rb', line 181 def install_boards(boardfamily) result = if @additional_urls.empty? run_and_capture("core", "install", boardfamily) else urls = @additional_urls.join(",") # update the index, then install. if the update step fails, return that result updater = run_and_capture("core", "update-index", "--additional-urls", urls) updater[:success] ? run_and_capture("core", "install", boardfamily, "--additional-urls", urls) : updater end result[:success] end |
#install_local_library(path) ⇒ CppLibrary
install a library from a path on the local machine (not via library manager), by symlink or no-op as appropriate
265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 |
# File 'lib/arduino_ci/arduino_backend.rb', line 265 def install_local_library(path) src_path = path.realpath library_name = name_of_library(path) cpp_library = library_of_name(library_name) destination_path = cpp_library.path # things get weird if the sketchbook contains the library. # check that first if cpp_library.installed? # maybe the project has always lived in the libraries directory, no need to symlink return cpp_library if destination_path == src_path uhoh = "There is already a library '#{library_name}' in the library directory (#{destination_path})" # maybe it's a symlink? that would be OK if Host.symlink?(destination_path) current_destination_target = Host.readlink(destination_path) return cpp_library if current_destination_target == src_path @last_msg = "#{uhoh} and it's symlinked to #{current_destination_target} (expected #{src_path})" return nil end @last_msg = "#{uhoh}. It may need to be removed manually." return nil end # install the library libraries_dir = destination_path.parent libraries_dir.mkpath unless libraries_dir.exist? Host.symlink(src_path, destination_path) cpp_library end |
#installed_libraries ⇒ Hash
Returns information about installed libraries via the CLI.
203 204 205 |
# File 'lib/arduino_ci/arduino_backend.rb', line 203 def installed_libraries capture_json("lib", "list")[:json] end |
#last_bytes_usage ⇒ Hash
extract the “Free space remaining” amount from the last run
300 301 302 303 304 305 306 307 308 309 310 311 312 |
# File 'lib/arduino_ci/arduino_backend.rb', line 300 def last_bytes_usage # Free-spacing syntax for regexes is not working today, not sure why. Make a string and convert to regex. re_str = [ 'Global variables use (?<globals>\d+) bytes', '\(\d+%\) of dynamic memory,', 'leaving (?<free>\d+) bytes for local variables.', 'Maximum is (?<max>\d+) bytes.' ].join(" ") mem_info = Regexp.new(re_str).match(@last_msg) return {} if mem_info.nil? Hash[mem_info.names.map(&:to_sym).zip(mem_info.captures.map(&:to_i))] end |
#lib_dir ⇒ String
Returns the path to the Arduino libraries directory.
141 142 143 144 145 |
# File 'lib/arduino_ci/arduino_backend.rb', line 141 def lib_dir user_dir_raw = config_dump["directories"]["user"] user_dir = OS.windows? ? Host.windows_to_pathname(user_dir_raw) : user_dir_raw Pathname.new(user_dir) + "libraries" end |
#library_available?(name) ⇒ bool
Find out if a library is available
197 198 199 200 |
# File 'lib/arduino_ci/arduino_backend.rb', line 197 def library_available?(name) # the --names flag limits the size of the response to just the name field capture_json("lib", "search", "--names", name)[:json]["libraries"].any? { |l| l["name"] == name } end |
#library_of_name(name) ⇒ CppLibrary
Create a handle to an Arduino library by name
245 246 247 248 249 |
# File 'lib/arduino_ci/arduino_backend.rb', line 245 def library_of_name(name) raise ArgumentError, "name is not a String (got #{name.class})" unless name.is_a? String CppLibrary.new(name, self) end |
#library_of_path(path) ⇒ CppLibrary
Create a handle to an Arduino library by path
254 255 256 257 258 259 260 |
# File 'lib/arduino_ci/arduino_backend.rb', line 254 def library_of_path(path) # the path must exist... and if it does, brute-force search the installed libs for it realpath = path.realpath # should produce error if the path doesn't exist to begin with entry = installed_libraries.find { |l| Pathname.new(l["library"]["install_dir"]).realpath == realpath } probable_name = entry["real_name"].nil? ? realpath.basename.to_s : entry["real_name"] CppLibrary.new(probable_name, self) end |
#name_of_library(path) ⇒ String
Guess the name of a library
233 234 235 236 237 238 239 240 |
# File 'lib/arduino_ci/arduino_backend.rb', line 233 def name_of_library(path) src_path = path.realpath properties_file = src_path + CppLibrary::LIBRARY_PROPERTIES_FILE return src_path.basename.to_s unless properties_file.exist? return src_path.basename.to_s if LibraryProperties.new(properties_file).name.nil? LibraryProperties.new(properties_file).name end |
#run_and_capture(*args, **kwargs) ⇒ Hash
run a command and capture its output
121 122 123 124 125 126 |
# File 'lib/arduino_ci/arduino_backend.rb', line 121 def run_and_capture(*args, **kwargs) ret = _wrap_run((proc { |*a, **k| Host.run_and_capture(*a, **k) }), *args, **kwargs) @last_err = ret[:err] @last_out = ret[:out] ret end |
#run_and_output(*args, **kwargs) ⇒ Object
build and run the arduino command
115 116 117 |
# File 'lib/arduino_ci/arduino_backend.rb', line 115 def run_and_output(*args, **kwargs) _wrap_run((proc { |*a, **k| Host.run_and_output(*a, **k) }), *args, **kwargs) end |
#should_use_config_dir? ⇒ Bool
Since the config dir behavior has changed from a directory to a file (At some point??)
332 333 334 335 336 337 |
# File 'lib/arduino_ci/arduino_backend.rb', line 332 def should_use_config_dir? @config_dir_hack = true # prevent an infinite loop when trying to run the command version < Gem::Version.new('0.14') ensure @config_dir_hack = false end |
#should_use_dry_run? ⇒ Bool
Since the dry-run behavior became default in arduino-cli 0.14, the command line flag was removed
326 327 328 |
# File 'lib/arduino_ci/arduino_backend.rb', line 326 def should_use_dry_run? version < Gem::Version.new('0.14') end |
#version ⇒ Gem::Version
Returns the arduino-cli version that the backend is using, as a semver object.
320 321 322 |
# File 'lib/arduino_ci/arduino_backend.rb', line 320 def version Gem::Version.new(version_str) end |
#version_str ⇒ String
Returns the arduino-cli version that the backend is using, as String.
315 316 317 |
# File 'lib/arduino_ci/arduino_backend.rb', line 315 def version_str capture_json("version")[:json]["VersionString"] end |