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.
@@ -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