diff --git a/.gitignore b/.gitignore index 75bf4c75..4f89af25 100644 --- a/.gitignore +++ b/.gitignore @@ -11,3 +11,6 @@ vendor # rspec failure tracking .rspec_status + +# C++ stuff +arduino_ci_built.bin diff --git a/SampleProjects/DoSomething/do-something.cpp b/SampleProjects/DoSomething/do-something.cpp index 60f08106..2ceefa55 100644 --- a/SampleProjects/DoSomething/do-something.cpp +++ b/SampleProjects/DoSomething/do-something.cpp @@ -1,3 +1,4 @@ +#include #include int doSomething(void) { millis(); // this line is only here to test that we're able to refer to the builtins diff --git a/arduino_ci.gemspec b/arduino_ci.gemspec index b25f8ba6..afbe7550 100644 --- a/arduino_ci.gemspec +++ b/arduino_ci.gemspec @@ -23,6 +23,8 @@ Gem::Specification.new do |spec| spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) } spec.require_paths = ["lib"] + spec.add_dependency "os", "~> 1.0" + spec.add_development_dependency "bundler", "~> 1.15" spec.add_development_dependency "rspec", "~> 3.0" spec.add_development_dependency 'rubocop', '~>0.49.0' diff --git a/cpp/Arduino.h b/cpp/Arduino.h new file mode 100644 index 00000000..12cdc863 --- /dev/null +++ b/cpp/Arduino.h @@ -0,0 +1,30 @@ +/* +Mock Arduino.h library. + +Where possible, variable names from the Arduino library are used to avoid conflicts + +*/ + + +#ifndef ARDUINO_CI_ARDUINO + +#include "math.h" +#define ARDUINO_CI_ARDUINO + +struct unit_test_state { + unsigned long micros; +}; + +struct unit_test_state godmode { + 0, // micros +}; + +unsigned long millis() { + return godmode.micros / 1000; +} + +unsigned long micros() { + return godmode.micros; +} + +#endif diff --git a/cpp/math.h b/cpp/math.h new file mode 100644 index 00000000..f7f6e372 --- /dev/null +++ b/cpp/math.h @@ -0,0 +1,41 @@ +//abs +long abs(long x) { return x > 0 ? x : -x; } +double fabs(double x) { return x > 0 ? x : -x; } + +//max +long max(long a, long b) { return a > b ? a : b; } +double fmax(double a, double b) { return a > b ? a : b; } + +//min +long min(long a, long b) { return a < b ? a : b; } +double fmin(double a, double b) { return a < b ? a : b; } + +//constrain +long constrain(long x, long a, long b) { return max(a, min(b, x)); } +double constrain(double x, double a, double b) { return max(a, min(b, x)); } + +//map +long map(long x, long in_min, long in_max, long out_min, long out_max) +{ + return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min; +} + +double map(double x, double in_min, double in_max, double out_min, double out_max) +{ + return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min; +} + +//sq +long sq(long x) { return x * x; } +double sq(double x) { return x * x; } + +// ??? too lazy to sort these now +//pow +//sqrt + +// http://www.ganssle.com/approx.htm +// http://www.ganssle.com/approx/sincos.cpp +//cos +//sin +//tan + diff --git a/exe/ci_system_check.rb b/exe/ci_system_check.rb index b738f3c4..fea71cb5 100755 --- a/exe/ci_system_check.rb +++ b/exe/ci_system_check.rb @@ -1,10 +1,7 @@ require 'arduino_ci' -puts "Enabling display with display manager" -ArduinoCI::DisplayManager::instance.enable - puts "Autlocating Arduino command" -arduino_cmd = ArduinoCI::ArduinoCmd.autolocate! +arduino_cmd = ArduinoCI::ArduinoInstallation.autolocate! board_tests = { "arduino:avr:uno" => true, @@ -50,6 +47,11 @@ got_problem = true unless arduino_cmd.verify_sketch(simple_sketch) library_path = File.join(File.dirname(File.dirname(__FILE__)), "SampleProjects", "DoSomething") + +puts "verify a library with arduino mocks" +cpp_library = ArduinoCI::CppLibrary.new(library_path) +got_problem = true unless cpp_library.build(arduino_cmd) + puts "verify the examples of a library (#{library_path})..." puts " - Install the library" installed_library_path = arduino_cmd.install_local_library(library_path) diff --git a/lib/arduino_ci.rb b/lib/arduino_ci.rb index 2f871c67..bfd11647 100644 --- a/lib/arduino_ci.rb +++ b/lib/arduino_ci.rb @@ -1,5 +1,6 @@ require "arduino_ci/version" -require "arduino_ci/arduino_cmd" +require "arduino_ci/arduino_installation" +require "arduino_ci/cpp_library" # ArduinoCI contains classes for automated testing of Arduino code on the command line # @author Ian Katz diff --git a/lib/arduino_ci/arduino_cmd.rb b/lib/arduino_ci/arduino_cmd.rb index cf88347f..f6fcbd76 100644 --- a/lib/arduino_ci/arduino_cmd.rb +++ b/lib/arduino_ci/arduino_cmd.rb @@ -1,41 +1,43 @@ require 'fileutils' -require 'arduino_ci/display_manager' -require 'arduino_ci/arduino_installation' module ArduinoCI # Wrap the Arduino executable. This requires, in some cases, a faked display. class ArduinoCmd - class << self - protected :new - - # @return [ArduinoCmd] A command object with a best guess (or nil) for the installation - def autolocate - new(ArduinoInstallation.autolocate) - end - - # @return [ArduinoCmd] A command object, installing Arduino if necessary - def autolocate! - new(ArduinoInstallation.autolocate!) - end - + # Enable a shortcut syntax for command line flags + # @param name [String] What the flag will be called (prefixed with 'flag_') + # @return [void] + # @macro [attach] flag + # @!attribute [r] flag_$1 + # @return String $2 the text of the command line flag + def self.flag(name, text = nil) + text = "(flag #{name} not defined)" if text.nil? + self.class_eval("def flag_#{name};\"#{text}\";end") end attr_accessor :installation - attr_reader :prefs_response_time + attr_accessor :base_cmd + attr_accessor :gcc_cmd + attr_reader :library_is_indexed - # @param installation [ArduinoInstallation] the location of the Arduino program installation - def initialize(installation) - @display_mgr = DisplayManager::instance - @installation = installation - @prefs_response_time = nil + # set the command line flags (undefined for now). + # These vary between gui/cli + flag :get_pref + flag :set_pref + flag :save_prefs + flag :use_board + flag :install_boards + flag :install_library + flag :verify + + def initialize @prefs_cache = nil @library_is_indexed = false end - def _parse_pref_string(arduino_output) + def parse_pref_string(arduino_output) lines = arduino_output.split("\n").select { |l| l.include? "=" } ret = lines.each_with_object({}) do |e, acc| parts = e.split("=", 2) @@ -45,26 +47,21 @@ def _parse_pref_string(arduino_output) ret end - # fetch preferences to a hash - def _prefs - resp = nil - if @installation.requires_x - @display_mgr.with_display do - start = Time.now - resp = run_and_capture("--get-pref") - @prefs_response_time = Time.now - start - end - else - start = Time.now - resp = run_and_capture("--get-pref") - @prefs_response_time = Time.now - start - end + def _lib_dir + "" + end + + # fetch preferences to a string + def _prefs_raw + resp = run_and_capture(flag_get_pref) return nil unless resp[:success] - _parse_pref_string(resp[:out]) + resp[:out] end def prefs - @prefs_cache = _prefs if @prefs_cache.nil? + prefs_raw = _prefs_raw if @prefs_cache.nil? + return nil if prefs_raw.nil? + @prefs_cache = parse_pref_string(prefs_raw) @prefs_cache.clone end @@ -74,42 +71,38 @@ def get_pref(key) data[key] end - # set a preference key/value pair + # underlying preference-setter. + # @return [bool] whether the command succeeded + def _set_pref(key, value) + run_and_capture(flag_set_pref, "#{key}=#{value}", flag_save_prefs)[:success] + end + + # set a preference key/value pair, and update the cache. # @param key [String] the preference key # @param value [String] the preference value # @return [bool] whether the command succeeded def set_pref(key, value) - success = run_with_gui_guess(" about preferences", "--pref", "#{key}=#{value}", "--save-prefs") + success = _set_pref(key, value) @prefs_cache[key] = value if success success end # run the arduino command - def run(*args, **kwargs) - full_args = @installation.base_cmd + args - if @installation.requires_x - @display_mgr.run(*full_args, **kwargs) - else - Host.run(*full_args, **kwargs) - end + def _run(*args, **kwargs) + raise "Ian needs to implement this in a subclass #{args} #{kwargs}" end - def run_with_gui_guess(message, *args, **kwargs) - # On Travis CI, we get an error message in the GUI instead of on STDERR - # so, assume that if we don't get a rapid reply that things are not installed - - # if we don't need X, we can skip this whole thing - return run_and_capture(*args, **kwargs)[:success] unless @installation.requires_x + # build and run the arduino command + def run(*args, **kwargs) + # TODO: detect env!! + full_args = @base_cmd + args + _run(*full_args, **kwargs) + end - prefs if @prefs_response_time.nil? - x3 = @prefs_response_time * 3 - Timeout.timeout(x3) do - result = run_and_capture(*args, **kwargs) - result[:success] - end - rescue Timeout::Error - puts "No response in #{x3} seconds. Assuming graphical modal error message#{message}." - false + def run_gcc(*args, **kwargs) + # TODO: detect env!! + full_args = @gcc_cmd + args + _run(*full_args, **kwargs) end # run a command and capture its output @@ -140,7 +133,7 @@ def run_wrap(*args, **kwargs) # 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 def board_installed?(boardname) - run_with_gui_guess(" about board not installed", "--board", boardname) + run_and_capture(flag_use_board, boardname)[:success] end # install a board by name @@ -148,22 +141,21 @@ def board_installed?(boardname) # @return [bool] whether the command succeeded def install_board(boardname) # TODO: find out why IO.pipe fails but File::NULL succeeds :( - run_and_capture("--install-boards", boardname, out: File::NULL)[:success] + run_and_capture(flag_install_boards, boardname, out: File::NULL)[:success] end # install a library by name # @param name [String] the library name # @return [bool] whether the command succeeded def install_library(library_name) - result = run_and_capture("--install-library", library_name) + result = run_and_capture(flag_install_library, library_name) @library_is_indexed = true if result[:success] result[:success] end # generate the (very likely) path of a library given its name def library_path(library_name) - sketchbook = get_pref("sketchbook.path") - File.join(sketchbook, library_name) + File.join(_lib_dir, library_name) end # update the library index @@ -175,7 +167,7 @@ def update_library_index # use a particular board for compilation def use_board(boardname) - run_with_gui_guess(" about board not installed", "--board", boardname, "--save-prefs") + run_and_capture(flag_use_board, boardname, flag_save_prefs)[:success] end # use a particular board for compilation, installing it if necessary @@ -197,25 +189,25 @@ def verify_sketch(path) puts "Can't verify nonexistent Sketch at '#{path}'!" return false end - run("--verify", path, err: :out) + run(flag_verify, path, err: :out) end # ensure that the given library is installed, or symlinked as appropriate # return the path of the prepared library, or nil - def install_local_library(library_path) - library_name = File.basename(library_path) - destination_path = File.join(@installation.lib_dir, library_name) + def install_local_library(path) + library_name = File.basename(path) + destination_path = library_path(library_name) # things get weird if the sketchbook contains the library. # check that first if File.exist? destination_path uhoh = "There is already a library '#{library_name}' in the library directory" - return destination_path if destination_path == library_path + return destination_path if destination_path == path # maybe it's a symlink? that would be OK if File.symlink?(destination_path) - return destination_path if File.readlink(destination_path) == library_path - puts "#{uhoh} and it's not symlinked to #{library_path}" + return destination_path if File.readlink(destination_path) == path + puts "#{uhoh} and it's not symlinked to #{path}" return nil end @@ -224,7 +216,7 @@ def install_local_library(library_path) end # install the library - FileUtils.ln_s(library_path, destination_path) + FileUtils.ln_s(path, destination_path) destination_path end diff --git a/lib/arduino_ci/arduino_cmd_linux.rb b/lib/arduino_ci/arduino_cmd_linux.rb new file mode 100644 index 00000000..a57d8dcb --- /dev/null +++ b/lib/arduino_ci/arduino_cmd_linux.rb @@ -0,0 +1,80 @@ +require 'arduino_ci/arduino_cmd' +require 'arduino_ci/display_manager' + +module ArduinoCI + + # Implementation of Arduino linux IDE commands + class ArduinoCmdLinux < ArduinoCmd + + attr_reader :prefs_response_time + + flag :get_pref, "--get-pref" + flag :set_pref, "--pref" + flag :save_prefs, "--save-prefs" + flag :use_board, "--board" + flag :install_boards, "--install-boards" + flag :install_library, "--install-library" + flag :verify, "--verify" + + def initialize + super + @prefs_response_time = nil + @display_mgr = DisplayManager::instance + end + + # fetch preferences to a hash + def _prefs_raw + start = Time.now + resp = run_and_capture(flag_get_pref) + @prefs_response_time = Time.now - start + return nil unless resp[:success] + resp[:out] + end + + def _lib_dir + File.join(get_pref("sketchbook.path"), "libraries") + end + + # run the arduino command + def _run(*args, **kwargs) + @display_mgr.run(*args, **kwargs) + end + + def run_with_gui_guess(message, *args, **kwargs) + # On Travis CI, we get an error message in the GUI instead of on STDERR + # so, assume that if we don't get a rapid reply that things are not installed + + prefs if @prefs_response_time.nil? + x3 = @prefs_response_time * 3 + Timeout.timeout(x3) do + result = run_and_capture(*args, **kwargs) + result[:success] + end + rescue Timeout::Error + puts "No response in #{x3} seconds. Assuming graphical modal error message#{message}." + false + end + + # underlying preference-setter. + # @param key [String] the preference key + # @param value [String] the preference value + # @return [bool] whether the command succeeded + def _set_pref(key, value) + run_with_gui_guess(" about preferences", flag_set_pref, "#{key}=#{value}", flag_save_prefs) + end + + # 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 + def board_installed?(boardname) + run_with_gui_guess(" about board not installed", flag_use_board, boardname) + end + + # use a particular board for compilation + def use_board(boardname) + run_with_gui_guess(" about board not installed", flag_use_board, boardname, flag_save_prefs) + end + + end + +end diff --git a/lib/arduino_ci/arduino_cmd_linux_builder.rb b/lib/arduino_ci/arduino_cmd_linux_builder.rb new file mode 100644 index 00000000..c31f63f7 --- /dev/null +++ b/lib/arduino_ci/arduino_cmd_linux_builder.rb @@ -0,0 +1,28 @@ +require "arduino_ci/host" +require 'arduino_ci/arduino_cmd' + +module ArduinoCI + + # Implementation of Arduino linux CLI commands + class ArduinoCmdLinuxBuilder < ArduinoCmd + + flag :get_pref, "--get-pref" # apparently doesn't exist + flag :set_pref, "--pref" # apparently doesn't exist + flag :save_prefs, "--save-prefs" # apparently doesn't exist + flag :use_board, "-fqbn" + flag :install_boards, "--install-boards" # apparently doesn't exist + flag :install_library, "--install-library" # apparently doesn't exist + flag :verify, "-compile" + + def _lib_dir + File.join(get_pref("sketchbook.path"), "libraries") + end + + # run the arduino command + def _run(*args, **kwargs) + Host.run(*args, **kwargs) + end + + end + +end diff --git a/lib/arduino_ci/arduino_cmd_osx.rb b/lib/arduino_ci/arduino_cmd_osx.rb new file mode 100644 index 00000000..68b865bf --- /dev/null +++ b/lib/arduino_ci/arduino_cmd_osx.rb @@ -0,0 +1,27 @@ +require "arduino_ci/host" +require 'arduino_ci/arduino_cmd' + +module ArduinoCI + + # Implementation of OSX commands + class ArduinoCmdOSX < ArduinoCmd + flag :get_pref, "--get-pref" + flag :set_pref, "--pref" + flag :save_prefs, "--save-prefs" + flag :use_board, "--board" + flag :install_boards, "--install-boards" + flag :install_library, "--install-library" + flag :verify, "--verify" + + # run the arduino command + def _run(*args, **kwargs) + Host.run(*args, **kwargs) + end + + def _lib_dir + File.join(ENV['HOME'], "Documents", "Arduino", "libraries") + end + + end + +end diff --git a/lib/arduino_ci/arduino_installation.rb b/lib/arduino_ci/arduino_installation.rb index a27b86f0..c7baee0d 100644 --- a/lib/arduino_ci/arduino_installation.rb +++ b/lib/arduino_ci/arduino_installation.rb @@ -1,4 +1,7 @@ require "arduino_ci/host" +require "arduino_ci/arduino_cmd_osx" +require "arduino_ci/arduino_cmd_linux" +require "arduino_ci/arduino_cmd_linux_builder" DESIRED_ARDUINO_IDE_VERSION = "1.8.5".freeze USE_BUILDER = false @@ -7,104 +10,125 @@ module ArduinoCI # Manage the OS-specific install location of Arduino class ArduinoInstallation - attr_accessor :base_cmd - attr_accessor :lib_dir - attr_accessor :requires_x class << self def force_install_location File.join(ENV['HOME'], 'arduino_ci_ide') end - def from_forced_install - ret = new - builder = File.join(force_install_location, "arduino-builder") - if USE_BUILDER && File.exist?(builder) - ret.base_cmd = [builder] - ret.requires_x = false + # attempt to find a workable Arduino executable across platforms + def autolocate + case Host.os + when :osx then autolocate_osx + when :linux then autolocate_linux + end + end + + def autolocate_osx + osx_root = "/Applications/Arduino.app/Contents" + old_way = false + return nil unless File.exist? osx_root + + ret = ArduinoCmdOSX.new + osx_place = "#{osx_root}/MacOS" + + if old_way + ret.base_cmd = [File.join(osx_place, "Arduino")] else - ret.base_cmd = [File.join(force_install_location, "arduino")] - ret.requires_x = true + jvm_runtime = `/usr/libexec/java_home` + ret.base_cmd = [ + "java", + "-cp", "#{osx_root}/Java/*", + "-DAPP_DIR=#{osx_root}/Java", + "-Djava.ext.dirs=$JVM_RUNTIME/Contents/Home/lib/ext/:#{jvm_runtime}/Contents/Home/jre/lib/ext/", + "-Dfile.encoding=UTF-8", + "-Dapple.awt.UIElement=true", + "-Xms128M", + "-Xmx512M", + "processing.app.Base", + ] end - ret.lib_dir = File.join(force_install_location, "libraries") - # TODO: "libraries" is what's in the adafruit install.sh script + + hardware_dir = File.join(osx_root, "Java", "hardware") + ret.gcc_cmd = [File.join(hardware_dir, "tools", "avr", "bin", "avr-gcc")] ret end - # attempt to find a workable Arduino executable across platforms - def autolocate - osx_root = "/Applications/Arduino.app" - old_way = false - if File.exist? osx_root - ret = new - osx_place = "#{osx_root}/Contents/MacOS" + def autolocate_linux + forced_avr = File.join(force_install_location, "hardware", "tools", "avr") + if USE_BUILDER + builder_name = "arduino-builder" + cli_place = Host.which(builder_name) + unless cli_place.nil? + ret = ArduinoCmdLinuxBuilder.new + ret.base_cmd = [cli_place] + ret.gcc_cmd = [Host.which("avr-gcc")] + return ret + end - if old_way - ret.base_cmd = [File.join(osx_place, "Arduino")] - else - jvm_runtime = `/usr/libexec/java_home` - ret.base_cmd = [ - "java", - "-cp", "#{osx_root}/Contents/Java/*", - "-DAPP_DIR=#{osx_root}/Contents/Java", - "-Djava.ext.dirs=$JVM_RUNTIME/Contents/Home/lib/ext/:#{jvm_runtime}/Contents/Home/jre/lib/ext/", - "-Dfile.encoding=UTF-8", - "-Dapple.awt.UIElement=true", - "-Xms128M", - "-Xmx512M", - "processing.app.Base", - ] + forced_builder = File.join(force_install_location, builder_name) + if File.exist?(forced_builder) + ret = ArduinoCmdLinuxBuilder.new + ret.base_cmd = [forced_builder] + ret.gcc_cmd = [File.join(forced_avr, "bin", "avr-gcc")] + return ret end - ret.lib_dir = File.join(osx_place, "Libraries") # TODO: probably wrong - ret.requires_x = false - return ret end - # AAARRRRGGGGHHH - # Even though arduino-builder is an awesome CLI for Arduino, - # ALL THE OPTIONS ARE DIFFERENT (single vs double dash for flags) - # USELESS FOR THE TIME BEING - posix_place = Host.which("arduino-builder") - if USE_BUILDER && !posix_place.nil? - ret = new - ret.base_cmd = [posix_place] - ret.lib_dir = File.join(ENV['HOME'], "Sketchbook") # assume linux - ret.requires_x = false - # https://learn.adafruit.com/adafruit-all-about-arduino-libraries-install-use/how-to-install-a-library + gui_name = "arduino" + gui_place = Host.which(gui_name) + unless gui_place.nil? + ret = ArduinoCmdLinux.new + ret.base_cmd = [gui_place] + ret.gcc_cmd = [Host.which("avr-gcc")] return ret end - posix_place = Host.which("arduino") - unless posix_place.nil? - ret = new - ret.base_cmd = [posix_place] - ret.lib_dir = File.join(ENV['HOME'], "Sketchbook") # assume linux - ret.requires_x = true - # https://learn.adafruit.com/adafruit-all-about-arduino-libraries-install-use/how-to-install-a-library + forced_arduino = File.join(force_install_location, gui_name) + if File.exist?(forced_arduino) + ret = ArduinoCmdLinux.new + ret.base_cmd = [forced_arduino] + ret.gcc_cmd = [File.join(forced_avr, "bin", "avr-gcc")] return ret end - - return from_forced_install if File.exist? force_install_location - - new + nil end # Attempt to find a workable Arduino executable across platforms, and install it if we don't def autolocate! candidate = autolocate - return candidate unless candidate.base_cmd.nil? + return candidate unless candidate.nil? # force the install - candidate = from_forced_install if force_install - candidate + force_install + autolocate end def force_install - pkgname = "arduino-#{DESIRED_ARDUINO_IDE_VERSION}" - tarfile = "#{pkgname}-linux64.tar.xz" - system("wget", "https://downloads.arduino.cc/#{tarfile}") - system("tar", "xf", tarfile) - system("mv", pkgname, force_install_location) + case Host.os + when :linux + pkgname = "arduino-#{DESIRED_ARDUINO_IDE_VERSION}" + tarfile = "#{pkgname}-linux64.tar.xz" + if File.exist? tarfile + puts "Arduino tarfile seems to have been downloaded already" + else + puts "Downloading Arduino binary with wget" + system("wget", "https://downloads.arduino.cc/#{tarfile}") + end + + if File.exist? pkgname + puts "Tarfile seems to have been extracted already" + else + puts "Extracting archive with tar" + system("tar", "xf", tarfile) + end + + if File.exist? force_install_location + puts "Arduino binary seems to have already been force-installed" + else + system("mv", pkgname, force_install_location) + end + end end end diff --git a/lib/arduino_ci/cpp_library.rb b/lib/arduino_ci/cpp_library.rb new file mode 100644 index 00000000..ec516f78 --- /dev/null +++ b/lib/arduino_ci/cpp_library.rb @@ -0,0 +1,39 @@ +require 'find' +require "arduino_ci/host" + +HPP_EXTENSIONS = [".hpp", ".hh", ".h", ".hxx", ".h++"].freeze +CPP_EXTENSIONS = [".cpp", ".cc", ".c", ".cxx", ".c++"].freeze +ARDUINO_HEADER_DIR = File.expand_path("../../../cpp", __FILE__) + +module ArduinoCI + + # Information about an Arduino CPP library, specifically for compilation purposes + class CppLibrary + + attr_reader :base_dir + + def initialize(base_dir) + @base_dir = base_dir + end + + def cpp_files + Find.find(@base_dir).select { |path| CPP_EXTENSIONS.include?(File.extname(path)) } + end + + def header_dirs + files = Find.find(@base_dir).select { |path| HPP_EXTENSIONS.include?(File.extname(path)) } + files.map { |path| File.dirname(path) }.uniq + end + + def build_args + ["-I#{ARDUINO_HEADER_DIR}"] + header_dirs.map { |d| "-I#{d}" } + cpp_files + end + + def build(arduino_cmd) + args = ["-c", "-o", "arduino_ci_built.bin"] + build_args + arduino_cmd.run_gcc(*args) + end + + end + +end diff --git a/lib/arduino_ci/host.rb b/lib/arduino_ci/host.rb index 8623bfc1..1eb83948 100644 --- a/lib/arduino_ci/host.rb +++ b/lib/arduino_ci/host.rb @@ -1,3 +1,5 @@ +require 'os' + module ArduinoCI # Tools for interacting with the host machine @@ -27,9 +29,15 @@ def self.run(*args, **kwargs) puts " $ #{shell_vars} #{actual_args.join(' ')}" ret = system(*full_cmd, **kwargs) status = ret ? "succeeded" : "failed" - puts "#{actual_args[0]} has #{status}" + puts "Command '#{File.basename(actual_args[0])}' has #{status}" ret end + def self.os + return :osx if OS.osx? + return :linux if OS.linux? + return :windows if OS.windows? + end + end end diff --git a/spec/arduino_cmd_spec.rb b/spec/arduino_cmd_spec.rb index f35e964b..139a43be 100644 --- a/spec/arduino_cmd_spec.rb +++ b/spec/arduino_cmd_spec.rb @@ -6,24 +6,15 @@ def get_sketch(dir, file) RSpec.describe ArduinoCI::ArduinoCmd do - context "autolocate" do - it "Finds the Arduino executable" do - arduino_cmd = ArduinoCI::ArduinoCmd.autolocate - end - end - - context "autolocate!" do - it "Finds the Arduino executable" do - arduino_cmd = ArduinoCI::ArduinoCmd.autolocate! - expect(arduino_cmd.installation.base_cmd).not_to be nil + arduino_cmd = ArduinoCI::ArduinoInstallation.autolocate! + context "initialize" do + it "sets base vars" do + expect(arduino_cmd.base_cmd).not_to be nil expect(arduino_cmd.prefs.class).to be Hash - expect(arduino_cmd.prefs_response_time).not_to be nil end end context "board_installed?" do - arduino_cmd = ArduinoCI::ArduinoCmd.autolocate! - ArduinoCI::DisplayManager::instance.enable it "Finds installed boards" do uno_installed = arduino_cmd.board_installed? "arduino:avr:uno" expect(uno_installed).to be true @@ -38,8 +29,6 @@ def get_sketch(dir, file) end context "set_pref" do - arduino_cmd = ArduinoCI::ArduinoCmd.autolocate! - ArduinoCI::DisplayManager::instance.enable it "Sets key to what it was before" do upload_verify = arduino_cmd.get_pref("upload.verify") @@ -49,8 +38,6 @@ def get_sketch(dir, file) end context "verify_sketch" do - arduino_cmd = ArduinoCI::ArduinoCmd.autolocate! - ArduinoCI::DisplayManager::instance.enable sketch_path_ino = get_sketch("FakeSketch", "FakeSketch.ino") sketch_path_pde = get_sketch("FakeSketch", "FakeSketch.pde") diff --git a/spec/arduino_installation_spec.rb b/spec/arduino_installation_spec.rb index 6915a027..861053fa 100644 --- a/spec/arduino_installation_spec.rb +++ b/spec/arduino_installation_spec.rb @@ -14,10 +14,16 @@ end context "autolocate!" do + arduino_cmd = ArduinoCI::ArduinoInstallation.autolocate! it "doesn't fail" do - installation = ArduinoCI::ArduinoInstallation.autolocate! - expect(installation.base_cmd).not_to be nil - expect(installation.lib_dir).not_to be nil + expect(arduino_cmd.base_cmd).not_to be nil + expect(arduino_cmd.gcc_cmd).not_to be nil + expect(arduino_cmd._lib_dir).not_to be nil + end + + it "produces a working AVR-GCC" do + expect(arduino_cmd.gcc_cmd).not_to be nil + expect(arduino_cmd.run_gcc("--version")).to be true end end diff --git a/spec/cpp_library_spec.rb b/spec/cpp_library_spec.rb new file mode 100644 index 00000000..238a90fe --- /dev/null +++ b/spec/cpp_library_spec.rb @@ -0,0 +1,31 @@ +require "spec_helper" + +sampleproj_path = File.join(File.dirname(File.dirname(__FILE__)), "SampleProjects") + +RSpec.describe ArduinoCI::CppLibrary do + cpp_lib_path = File.join(sampleproj_path, "DoSomething") + cpp_library = ArduinoCI::CppLibrary.new(cpp_lib_path) + context "cpp_files" do + it "finds cpp files in directory" do + dosomething_cpp_files = ["DoSomething/do-something.cpp"] + relative_paths = cpp_library.cpp_files.map { |f| f.split("SampleProjects/", 2)[1] } + expect(relative_paths).to match_array(dosomething_cpp_files) + end + end + + context "header_dirs" do + it "finds directories containing h files" do + dosomething_header_dirs = ["DoSomething"] + relative_paths = cpp_library.header_dirs.map { |f| f.split("SampleProjects/", 2)[1] } + expect(relative_paths).to match_array(dosomething_header_dirs) + end + end + + context "build" do + arduino_cmd = ArduinoCI::ArduinoInstallation.autolocate! + it "builds libraries" do + expect(cpp_library.build(arduino_cmd)).to be true + end + end + +end