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.
@@ -12,23 +12,6 @@ module ArduinoCI
12
12
  flag :install_boards, "--install-boards"
13
13
  flag :install_library, "--install-library"
14
14
  flag :verify, "--verify"
15
-
16
- # run the arduino command
17
- # @return [bool] whether the command succeeded
18
- def _run_and_output(*args, **kwargs)
19
- Host.run_and_output(*args, **kwargs)
20
- end
21
-
22
- # run the arduino command
23
- # @return [Hash] keys for :success, :out, and :err
24
- def _run_and_capture(*args, **kwargs)
25
- Host.run_and_capture(*args, **kwargs)
26
- end
27
-
28
- def _lib_dir
29
- File.join(ENV['HOME'], "Documents", "Arduino", "libraries")
30
- end
31
-
32
15
  end
33
16
 
34
17
  end
@@ -12,23 +12,6 @@ module ArduinoCI
12
12
  flag :install_boards, "--install-boards"
13
13
  flag :install_library, "--install-library"
14
14
  flag :verify, "--verify"
15
-
16
- # run the arduino command
17
- # @return [bool] whether the command succeeded
18
- def _run_and_output(*args, **kwargs)
19
- Host.run_and_output(*args, **kwargs)
20
- end
21
-
22
- # run the arduino command
23
- # @return [Hash] keys for :success, :out, and :err
24
- def _run_and_capture(*args, **kwargs)
25
- Host.run_and_capture(*args, **kwargs)
26
- end
27
-
28
- def _lib_dir
29
- File.join(ENV['userprofile'], "Documents", "Arduino", "libraries")
30
- end
31
-
32
15
  end
33
16
 
34
17
  end
@@ -35,6 +35,7 @@ module ArduinoCI
35
35
  locations.each do |loc|
36
36
  next if loc.nil?
37
37
  next unless File.exist? loc
38
+
38
39
  return loc
39
40
  end
40
41
  nil
@@ -51,6 +52,7 @@ module ArduinoCI
51
52
  locations.each do |loc|
52
53
  next if loc.nil?
53
54
  next unless File.exist? loc
55
+
54
56
  return loc
55
57
  end
56
58
  nil
@@ -132,6 +134,8 @@ module ArduinoCI
132
134
  open(package_url, ssl_verify_mode: 0, progress_proc: dot_printer) do |url|
133
135
  File.open(package_file, 'wb') { |file| file.write(url.read) }
134
136
  end
137
+ rescue Net::OpenTimeout, Net::ReadTimeout => e
138
+ puts "\nArduino force-install failed downloading #{package_url}: #{e}"
135
139
  end
136
140
 
137
141
  # Extract the package_file to extracted_file
@@ -47,6 +47,7 @@ module ArduinoCI
47
47
  def self.existing_installation
48
48
  exe = self.existing_executable
49
49
  return nil if exe.nil?
50
+
50
51
  File.dirname(exe) # it's not really this
51
52
  # but for this platform it doesn't really matter
52
53
  end
@@ -79,6 +80,7 @@ module ArduinoCI
79
80
  end
80
81
  forced_arduino = File.join(self.force_install_location, "arduino")
81
82
  return forced_arduino if File.exist? forced_arduino
83
+
82
84
  nil
83
85
  end
84
86
 
@@ -73,6 +73,7 @@ module ArduinoCI
73
73
  def self.existing_installation
74
74
  exe = self.existing_executable
75
75
  return nil if exe.nil?
76
+
76
77
  File.dirname(exe)
77
78
  end
78
79
 
@@ -94,6 +95,7 @@ module ArduinoCI
94
95
  def self.force_installed_executable
95
96
  exe = File.join(self.force_install_location, "arduino_debug.exe")
96
97
  return nil if exe.nil?
98
+
97
99
  exe
98
100
  end
99
101
 
@@ -8,7 +8,7 @@ require "arduino_ci/arduino_downloader_linux"
8
8
 
9
9
  require "arduino_ci/arduino_downloader_windows" if ArduinoCI::Host.os == :windows
10
10
 
11
- DESIRED_ARDUINO_IDE_VERSION = "1.8.5".freeze
11
+ DESIRED_ARDUINO_IDE_VERSION = "1.8.6".freeze
12
12
 
13
13
  module ArduinoCI
14
14
 
@@ -29,11 +29,13 @@ module ArduinoCI
29
29
  when :linux then
30
30
  loc = ArduinoDownloaderLinux.autolocated_executable
31
31
  return nil if loc.nil?
32
+
32
33
  ret = ArduinoCmdLinux.new
33
34
  ret.base_cmd = [loc]
34
35
  when :windows then
35
36
  loc = ArduinoDownloaderWindows.autolocated_executable
36
37
  return nil if loc.nil?
38
+
37
39
  ret = ArduinoCmdWindows.new
38
40
  ret.base_cmd = [loc]
39
41
  end
@@ -51,8 +53,9 @@ module ArduinoCI
51
53
  # from https://github.com/arduino/Arduino/issues/1970#issuecomment-321975809
52
54
  [
53
55
  "java",
54
- "-cp", "#{osx_root}/Java/*",
55
- "-DAPP_DIR=#{osx_root}/Java",
56
+ "-cp",
57
+ "#{osx_root}/Contents/Java/*",
58
+ "-DAPP_DIR=#{osx_root}/Contents/Java",
56
59
  "-Dfile.encoding=UTF-8",
57
60
  "-Dapple.awt.UIElement=true",
58
61
  "-Xms128M",
@@ -54,7 +54,7 @@ module ArduinoCI
54
54
  # @return [ArudinoCI::CIConfig] The configuration with defaults filled in
55
55
  def default
56
56
  ret = new
57
- ret.load_yaml(File.expand_path("../../../misc/default.yml", __FILE__))
57
+ ret.load_yaml(File.expand_path("../../misc/default.yml", __dir__))
58
58
  ret
59
59
  end
60
60
  end
@@ -85,6 +85,7 @@ module ArduinoCI
85
85
  # @return [Hash] a copy, containing only expected & valid data
86
86
  def validate_data(rootname, source, schema)
87
87
  return nil if source.nil?
88
+
88
89
  good_data = {}
89
90
  source.each do |key, value|
90
91
  ksym = key.to_sym
@@ -125,12 +126,12 @@ module ArduinoCI
125
126
 
126
127
  if yml.include?("compile")
127
128
  valid_data = validate_data("compile", yml["compile"], COMPILE_SCHEMA)
128
- @compile_info = valid_data
129
+ valid_data.each { |k, v| @compile_info[k] = v }
129
130
  end
130
131
 
131
132
  if yml.include?("unittest")
132
133
  valid_data = validate_data("unittest", yml["unittest"], UNITTEST_SCHEMA)
133
- @unittest_info = valid_data
134
+ valid_data.each { |k, v| @unittest_info[k] = v }
134
135
  end
135
136
 
136
137
  self
@@ -186,6 +187,7 @@ module ArduinoCI
186
187
  def platform_definition(platform_name)
187
188
  defn = @platform_info[platform_name]
188
189
  return nil if defn.nil?
190
+
189
191
  deep_clone(defn)
190
192
  end
191
193
 
@@ -195,6 +197,7 @@ module ArduinoCI
195
197
  # @return [String] the URL defined for this package
196
198
  def package_url(package)
197
199
  return nil if @package_info[package].nil?
200
+
198
201
  @package_info[package][:url]
199
202
  end
200
203
 
@@ -202,6 +205,7 @@ module ArduinoCI
202
205
  # @return [Array<String>] The compiler binary names (e.g. g++) to build with
203
206
  def compilers_to_use
204
207
  return [] if @unittest_info[:compilers].nil?
208
+
205
209
  @unittest_info[:compilers]
206
210
  end
207
211
 
@@ -209,6 +213,7 @@ module ArduinoCI
209
213
  # @return [Array<String>] The platforms to build
210
214
  def platforms_to_build
211
215
  return [] if @compile_info[:platforms].nil?
216
+
212
217
  @compile_info[:platforms]
213
218
  end
214
219
 
@@ -216,18 +221,21 @@ module ArduinoCI
216
221
  # @return [Array<String>] The platforms to unit test on
217
222
  def platforms_to_unittest
218
223
  return [] if @unittest_info[:platforms].nil?
224
+
219
225
  @unittest_info[:platforms]
220
226
  end
221
227
 
222
228
  # @return [Array<String>] The aux libraries required for building/verifying
223
229
  def aux_libraries_for_build
224
230
  return [] if @compile_info[:libraries].nil?
231
+
225
232
  @compile_info[:libraries]
226
233
  end
227
234
 
228
235
  # @return [Array<String>] The aux libraries required for unit testing
229
236
  def aux_libraries_for_unittest
230
237
  return [] if @unittest_info[:libraries].nil?
238
+
231
239
  @unittest_info[:libraries]
232
240
  end
233
241
 
@@ -236,6 +244,7 @@ module ArduinoCI
236
244
  # @return [Array<String>] files that match the select/reject criteria
237
245
  def allowable_unittest_files(paths)
238
246
  return paths if @unittest_info[:testfiles].nil?
247
+
239
248
  ret = paths
240
249
  unless @unittest_info[:testfiles][:select].nil? || @unittest_info[:testfiles][:select].empty?
241
250
  ret = ret.select { |p| unittest_info[:testfiles][:select].any? { |glob| File.fnmatch(glob, File.basename(p)) } }
@@ -253,6 +262,7 @@ module ArduinoCI
253
262
  plat = @platform_info[platform_name]
254
263
  return {} if plat.nil?
255
264
  return {} if plat[:gcc].nil?
265
+
256
266
  plat[:gcc]
257
267
  end
258
268
  end
@@ -1,20 +1,25 @@
1
1
  require 'find'
2
2
  require "arduino_ci/host"
3
+ require 'pathname'
3
4
 
4
5
  HPP_EXTENSIONS = [".hpp", ".hh", ".h", ".hxx", ".h++"].freeze
5
6
  CPP_EXTENSIONS = [".cpp", ".cc", ".c", ".cxx", ".c++"].freeze
6
- ARDUINO_HEADER_DIR = File.expand_path("../../../cpp/arduino", __FILE__)
7
- UNITTEST_HEADER_DIR = File.expand_path("../../../cpp/unittest", __FILE__)
7
+ CI_CPP_DIR = Pathname.new(__dir__).parent.parent + "cpp"
8
+ ARDUINO_HEADER_DIR = CI_CPP_DIR + "arduino"
9
+ UNITTEST_HEADER_DIR = CI_CPP_DIR + "unittest"
8
10
 
9
11
  module ArduinoCI
10
12
 
11
13
  # Information about an Arduino CPP library, specifically for compilation purposes
12
14
  class CppLibrary
13
15
 
14
- # @return [String] The path to the library being tested
16
+ # @return [Pathname] The path to the library being tested
15
17
  attr_reader :base_dir
16
18
 
17
- # @return [Array<String>] The set of artifacts created by this class (note: incomplete!)
19
+ # @return [Pathname] The path to the Arduino 3rd-party library directory
20
+ attr_reader :arduino_lib_dir
21
+
22
+ # @return [Array<Pathname>] The set of artifacts created by this class (note: incomplete!)
18
23
  attr_reader :artifacts
19
24
 
20
25
  # @return [String] STDERR from the last command
@@ -26,84 +31,124 @@ module ArduinoCI
26
31
  # @return [String] the last command
27
32
  attr_reader :last_cmd
28
33
 
29
- # @param base_dir [String] The path to the library being tested
30
- def initialize(base_dir)
31
- @base_dir = File.expand_path(base_dir)
34
+ # @param base_dir [Pathname] The path to the library being tested
35
+ # @param arduino_lib_dir [Pathname] The path to the libraries directory
36
+ def initialize(base_dir, arduino_lib_dir)
37
+ raise ArgumentError, 'base_dir is not a Pathname' unless base_dir.is_a? Pathname
38
+ raise ArgumentError, 'arduino_lib_dir is not a Pathname' unless arduino_lib_dir.is_a? Pathname
39
+
40
+ @base_dir = base_dir
41
+ @arduino_lib_dir = arduino_lib_dir.expand_path
32
42
  @artifacts = []
33
43
  @last_err = ""
34
44
  @last_out = ""
35
45
  @last_msg = ""
46
+ @has_libasan_cache = {}
36
47
  end
37
48
 
38
49
  # Guess whether a file is part of the vendor bundle (indicating we should ignore it).
39
50
  #
40
51
  # This assumes the vendor bundle will be at `vendor/bundle` and not some other location
41
- # @param path [String] The path to check
42
- # @return [Array<String>] The paths of the found files
52
+ # @param path [Pathname] The path to check
53
+ # @return [Array<Pathname>] The paths of the found files
43
54
  def vendor_bundle?(path)
44
55
  # TODO: look for Gemfile, look for .bundle/config and get BUNDLE_PATH from there?
45
- base = File.join(@base_dir, "vendor")
46
- real = File.join(File.realpath(@base_dir), "vendor")
47
- return true if path.start_with?(base)
48
- return true if path.start_with?(real)
56
+ base = @base_dir + "vendor"
57
+ return false unless base.exist?
58
+
59
+ real = base.realpath
60
+ path.ascend do |path_part|
61
+ return true if path_part == base
62
+ return true if path_part == real
63
+ end
49
64
  false
50
65
  end
51
66
 
67
+ # Check whether libasan (and by extension -fsanitizer=address) is supported
68
+ #
69
+ # This requires compilation of a sample program, and will be cached
70
+ # @param gcc_binary [String]
71
+ def libasan?(gcc_binary)
72
+ unless @has_libasan_cache.key?(gcc_binary)
73
+ file = Tempfile.new('arduino_ci_libasan_check')
74
+ begin
75
+ file.write "int main(){}"
76
+ file.close
77
+ @has_libasan_cache[gcc_binary] = run_gcc(gcc_binary, "-o", "/dev/null", "-fsanitize=address", file.path)
78
+ ensure
79
+ file.delete
80
+ end
81
+ end
82
+ @has_libasan_cache[gcc_binary]
83
+ end
84
+
52
85
  # Get a list of all CPP source files in a directory and its subdirectories
53
- # @param some_dir [String] The directory in which to begin the search
54
- # @return [Array<String>] The paths of the found files
86
+ # @param some_dir [Pathname] The directory in which to begin the search
87
+ # @return [Array<Pathname>] The paths of the found files
55
88
  def cpp_files_in(some_dir)
56
- return [] unless File.exist?(some_dir)
57
- real = File.realpath(some_dir)
58
- files = Find.find(real).reject { |path| File.directory?(path) }
59
- ret = files.select { |path| CPP_EXTENSIONS.include?(File.extname(path)) }
60
- ret
89
+ raise ArgumentError, 'some_dir is not a Pathname' unless some_dir.is_a? Pathname
90
+ return [] unless some_dir.exist? && some_dir.directory?
91
+
92
+ real = some_dir.realpath
93
+ files = Find.find(real).map { |p| Pathname.new(p) }.reject(&:directory?)
94
+ cpp = files.select { |path| CPP_EXTENSIONS.include?(path.extname.downcase) }
95
+ cpp.reject { |path| path.basename.to_s.start_with?(".") } # ignore hidden
61
96
  end
62
97
 
63
98
  # CPP files that are part of the project library under test
64
- # @return [Array<String>]
99
+ # @return [Array<Pathname>]
65
100
  def cpp_files
66
- real_tests_dir = File.realpath(tests_dir)
101
+ real_tests_dir = tests_dir.realpath
67
102
  cpp_files_in(@base_dir).reject do |p|
68
- next true if File.dirname(p).include?(tests_dir)
69
- next true if File.dirname(p).include?(real_tests_dir)
70
- next true if vendor_bundle?(p)
103
+ next true if p.ascend do |path_part|
104
+ break true if path_part == tests_dir
105
+ break true if path_part == real_tests_dir
106
+ break true if vendor_bundle?(p)
107
+ end
108
+
109
+ false
71
110
  end
72
111
  end
73
112
 
74
113
  # CPP files that are part of the arduino mock library we're providing
75
- # @return [Array<String>]
114
+ # @return [Array<Pathname>]
76
115
  def cpp_files_arduino
77
116
  cpp_files_in(ARDUINO_HEADER_DIR)
78
117
  end
79
118
 
80
119
  # CPP files that are part of the unit test library we're providing
81
- # @return [Array<String>]
120
+ # @return [Array<Pathname>]
82
121
  def cpp_files_unittest
83
122
  cpp_files_in(UNITTEST_HEADER_DIR)
84
123
  end
85
124
 
125
+ # CPP files that are part of the 3rd-party libraries we're including
126
+ # @param [Array<String>] aux_libraries
127
+ # @return [Array<Pathname>]
128
+ def cpp_files_libraries(aux_libraries)
129
+ arduino_library_src_dirs(aux_libraries).map { |d| cpp_files_in(d) }.flatten.uniq
130
+ end
131
+
86
132
  # The directory where we expect to find unit test defintions provided by the user
87
- # @return [String]
133
+ # @return [Pathname]
88
134
  def tests_dir
89
- File.join(@base_dir, "test")
135
+ Pathname.new(@base_dir) + "test"
90
136
  end
91
137
 
92
138
  # The files provided by the user that contain unit tests
93
- # @return [Array<String>]
139
+ # @return [Array<Pathname>]
94
140
  def test_files
95
141
  cpp_files_in(tests_dir)
96
142
  end
97
143
 
98
144
  # Find all directories in the project library that include C++ header files
99
- # @return [Array<String>]
145
+ # @return [Array<Pathname>]
100
146
  def header_dirs
101
- real = File.realpath(@base_dir)
102
- all_files = Find.find(real).reject { |path| File.directory?(path) }
147
+ real = @base_dir.realpath
148
+ all_files = Find.find(real).map { |f| Pathname.new(f) }.reject(&:directory?)
103
149
  unbundled = all_files.reject { |path| vendor_bundle?(path) }
104
- files = unbundled.select { |path| HPP_EXTENSIONS.include?(File.extname(path)) }
105
- ret = files.map { |path| File.dirname(path) }.uniq
106
- ret
150
+ files = unbundled.select { |path| HPP_EXTENSIONS.include?(path.extname.downcase) }
151
+ files.map(&:dirname).uniq
107
152
  end
108
153
 
109
154
  # wrapper for the GCC command
@@ -120,14 +165,29 @@ module ArduinoCI
120
165
  # @return [String] the version reported by `gcc -v`
121
166
  def gcc_version(gcc_binary)
122
167
  return nil unless run_gcc(gcc_binary, "-v")
168
+
123
169
  @last_err
124
170
  end
125
171
 
172
+ # Arduino library directories containing sources
173
+ # @return [Array<Pathname>]
174
+ def arduino_library_src_dirs(aux_libraries)
175
+ # Pull in all possible places that headers could live, according to the spec:
176
+ # https://github.com/arduino/Arduino/wiki/Arduino-IDE-1.5:-Library-specification
177
+ # TODO: be smart and implement library spec (library.properties, etc)?
178
+ subdirs = ["", "src", "utility"]
179
+ all_aux_include_dirs_nested = aux_libraries.map do |libdir|
180
+ subdirs.map { |subdir| Pathname.new(@arduino_lib_dir) + libdir + subdir }
181
+ end
182
+ all_aux_include_dirs_nested.flatten.select(&:exist?).select(&:directory?)
183
+ end
184
+
126
185
  # GCC command line arguments for including aux libraries
127
- # @param aux_libraries [String] The external Arduino libraries required by this project
186
+ # @param aux_libraries [Array<Pathname>] The external Arduino libraries required by this project
128
187
  # @return [Array<String>] The GCC command-line flags necessary to include those libraries
129
188
  def include_args(aux_libraries)
130
- places = [ARDUINO_HEADER_DIR, UNITTEST_HEADER_DIR] + header_dirs + aux_libraries
189
+ all_aux_include_dirs = arduino_library_src_dirs(aux_libraries)
190
+ places = [ARDUINO_HEADER_DIR, UNITTEST_HEADER_DIR] + header_dirs + all_aux_include_dirs
131
191
  places.map { |d| "-I#{d}" }
132
192
  end
133
193
 
@@ -136,6 +196,7 @@ module ArduinoCI
136
196
  # @return [Array<String>] GCC command-line flags
137
197
  def feature_args(ci_gcc_config)
138
198
  return [] if ci_gcc_config[:features].nil?
199
+
139
200
  ci_gcc_config[:features].map { |f| "-f#{f}" }
140
201
  end
141
202
 
@@ -144,6 +205,7 @@ module ArduinoCI
144
205
  # @return [Array<String>] GCC command-line flags
145
206
  def warning_args(ci_gcc_config)
146
207
  return [] if ci_gcc_config[:warnings].nil?
208
+
147
209
  ci_gcc_config[:features].map { |w| "-W#{w}" }
148
210
  end
149
211
 
@@ -152,6 +214,7 @@ module ArduinoCI
152
214
  # @return [Array<String>] GCC command-line flags
153
215
  def define_args(ci_gcc_config)
154
216
  return [] if ci_gcc_config[:defines].nil?
217
+
155
218
  ci_gcc_config[:defines].map { |d| "-D#{d}" }
156
219
  end
157
220
 
@@ -160,16 +223,20 @@ module ArduinoCI
160
223
  # @return [Array<String>] GCC command-line flags
161
224
  def flag_args(ci_gcc_config)
162
225
  return [] if ci_gcc_config[:flags].nil?
226
+
163
227
  ci_gcc_config[:flags]
164
228
  end
165
229
 
166
230
  # All GCC command line args for building any unit test
167
- # @param aux_libraries [String] The external Arduino libraries required by this project
231
+ # @param aux_libraries [Array<Pathname>] The external Arduino libraries required by this project
168
232
  # @param ci_gcc_config [Hash] The GCC config object
169
233
  # @return [Array<String>] GCC command-line flags
170
234
  def test_args(aux_libraries, ci_gcc_config)
171
235
  # TODO: something with libraries?
172
- ret = include_args(aux_libraries) + cpp_files_arduino + cpp_files_unittest + cpp_files
236
+ ret = include_args(aux_libraries)
237
+ ret += cpp_files_arduino.map(&:to_s)
238
+ ret += cpp_files_unittest.map(&:to_s)
239
+ ret += cpp_files.map(&:to_s)
173
240
  unless ci_gcc_config.nil?
174
241
  cgc = ci_gcc_config
175
242
  ret = feature_args(cgc) + warning_args(cgc) + define_args(cgc) + flag_args(cgc) + ret
@@ -178,32 +245,42 @@ module ArduinoCI
178
245
  end
179
246
 
180
247
  # build a file for running a test of the given unit test file
181
- # @param test_file [String] The path to the file containing the unit tests
182
- # @param aux_libraries [String] The external Arduino libraries required by this project
248
+ # @param test_file [Pathname] The path to the file containing the unit tests
249
+ # @param aux_libraries [Array<Pathname>] The external Arduino libraries required by this project
183
250
  # @param ci_gcc_config [Hash] The GCC config object
184
- # @return [String] path to the compiled test executable
251
+ # @return [Pathname] path to the compiled test executable
185
252
  def build_for_test_with_configuration(test_file, aux_libraries, gcc_binary, ci_gcc_config)
186
- base = File.basename(test_file)
187
- executable = File.expand_path("unittest_#{base}.bin")
253
+ base = test_file.basename
254
+ executable = Pathname.new("unittest_#{base}.bin").expand_path
188
255
  File.delete(executable) if File.exist?(executable)
189
- args = [
190
- ["-std=c++0x", "-o", executable, "-DARDUINO=100"],
191
- test_args(aux_libraries, ci_gcc_config),
192
- [test_file],
193
- ].flatten(1)
256
+ arg_sets = []
257
+ arg_sets << ["-std=c++0x", "-o", executable.to_s, "-DARDUINO=100"]
258
+ if libasan?(gcc_binary)
259
+ arg_sets << [ # Stuff to help with dynamic memory mishandling
260
+ "-g", "-O1",
261
+ "-fno-omit-frame-pointer",
262
+ "-fno-optimize-sibling-calls",
263
+ "-fsanitize=address"
264
+ ]
265
+ end
266
+ arg_sets << test_args(aux_libraries, ci_gcc_config)
267
+ arg_sets << cpp_files_libraries(aux_libraries).map(&:to_s)
268
+ arg_sets << [test_file.to_s]
269
+ args = arg_sets.flatten(1)
194
270
  return nil unless run_gcc(gcc_binary, *args)
271
+
195
272
  artifacts << executable
196
273
  executable
197
274
  end
198
275
 
199
276
  # run a test file
200
- # @param [String] the path to the test file
277
+ # @param [Pathname] the path to the test file
201
278
  # @return [bool] whether all tests were successful
202
279
  def run_test_file(executable)
203
280
  @last_cmd = executable
204
281
  @last_out = ""
205
282
  @last_err = ""
206
- Host.run_and_output(executable)
283
+ Host.run_and_output(executable.to_s)
207
284
  end
208
285
 
209
286
  end