1
1
require 'fileutils'
2
2
require 'pathname'
3
+ require 'json'
3
4
4
5
# workaround for https://github.com/arduino/Arduino/issues/3535
5
6
WORKAROUND_LIB = "USBHost" . freeze
@@ -24,10 +25,6 @@ def self.flag(name, text = nil)
24
25
self . class_eval ( "def flag_#{ name } ;\" #{ text } \" ;end" , __FILE__ , __LINE__ )
25
26
end
26
27
27
- # the array of command components to launch the Arduino executable
28
- # @return [Array<String>]
29
- attr_accessor :base_cmd
30
-
31
28
# the actual path to the executable on this platform
32
29
# @return [Pathname]
33
30
attr_accessor :binary_path
@@ -44,97 +41,30 @@ def self.flag(name, text = nil)
44
41
# @return [String] the most recently-run command
45
42
attr_reader :last_msg
46
43
44
+ # @return [Array<String>] Additional URLs for the boards manager
45
+ attr_reader :additional_urls
46
+
47
47
# set the command line flags (undefined for now).
48
48
# 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
53
49
flag :install_boards # flag_install_boards
54
50
flag :install_library # flag_install_library
55
51
flag :verify # flag_verify
56
52
57
- def initialize
58
- @prefs_cache = { }
59
- @prefs_fetched = false
53
+ def initialize ( binary_path )
54
+ @binary_path = binary_path
60
55
@libraries_indexed = false
56
+ @additional_urls = [ ]
61
57
@last_out = ""
62
58
@last_err = ""
63
59
@last_msg = ""
64
60
end
65
61
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
-
132
62
def _wrap_run ( work_fn , *args , **kwargs )
133
63
# do some work to extract & merge environment variables if they exist
134
64
has_env = !args . empty? && args [ 0 ] . class == Hash
135
65
env_vars = has_env ? args [ 0 ] : { }
136
66
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
138
68
full_cmd = env_vars . empty? ? full_args : [ env_vars ] + full_args
139
69
140
70
shell_vars = env_vars . map { |k , v | "#{ k } =#{ v } " } . join ( " " )
@@ -156,19 +86,34 @@ def run_and_capture(*args, **kwargs)
156
86
ret
157
87
end
158
88
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
+
159
105
# Board manager URLs
160
106
# @return [Array<String>] The additional URLs used by the board manager
161
107
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
166
109
end
167
110
168
111
# Set board manager URLs
169
112
# @return [Array<String>] The additional URLs used by the board manager
170
113
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
172
117
end
173
118
174
119
# check whether a board is installed
@@ -177,17 +122,16 @@ def board_manager_urls=(all_urls)
177
122
# @param boardname [String] The board to test
178
123
# @return [bool] Whether the board is installed
179
124
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 ]
181
127
end
182
128
183
129
# install a board by name
184
130
# @param name [String] the board name
185
131
# @return [bool] whether the command succeeded
186
132
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 ]
191
135
end
192
136
193
137
# install a library by name
@@ -247,39 +191,20 @@ def update_library_index
247
191
install_library ( WORKAROUND_LIB )
248
192
end
249
193
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
258
195
# @param boardname [String] The board to use
259
196
# @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 )
273
198
ext = File . extname path
274
199
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'!"
276
201
return false
277
202
end
278
203
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 } '!"
280
205
return false
281
206
end
282
- ret = run_and_capture ( flag_verify , path )
207
+ ret = run_and_capture ( "compile" , "--fqbn" , boardname , "--dry-run" , path )
283
208
ret [ :success ]
284
209
end
285
210
0 commit comments