From 0c2b486e23ab07209fa2e759e460f0cd108dad3c Mon Sep 17 00:00:00 2001 From: Ian Katz Date: Mon, 15 Jan 2018 07:41:46 -0500 Subject: [PATCH 01/11] add ability to verify sketches --- CHANGELOG.md | 1 + exe/ci_system_check.rb | 7 ++++++- lib/arduino_ci/arduino_cmd.rb | 9 +++++++++ spec/BadSketch/BadSketch.ino | 1 + spec/FakeSketch/FakeSketch.ino | 2 ++ spec/arduino_cmd_spec.rb | 33 +++++++++++++++++++++++++++++++++ 6 files changed, 52 insertions(+), 1 deletion(-) create mode 100644 spec/BadSketch/BadSketch.ino create mode 100644 spec/FakeSketch/FakeSketch.ino diff --git a/CHANGELOG.md b/CHANGELOG.md index 4f521c27..f58dd6fa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - `ArduinoCmd` installs boards - `ArduinoCmd` installs libraries - `ArduinoCmd` selects boards (compiler preference) +- `ArduinoCmd` verifies sketches ### Changed - `DisplayManger.with_display` doesn't `disable` if the display was enabled prior to starting the block diff --git a/exe/ci_system_check.rb b/exe/ci_system_check.rb index 84b565c9..39ababac 100755 --- a/exe/ci_system_check.rb +++ b/exe/ci_system_check.rb @@ -37,8 +37,13 @@ got_problem = true unless arduino_cmd.set_pref("compiler.warning_level", "all") puts "use board! (install board)" got_problem = true unless arduino_cmd.use_board!("arduino:samd:zero") -puts "verify that board has been installed" +puts "assert that board has been installed" got_problem = true unless arduino_cmd.board_installed?("arduino:samd:zero") +simple_sketch = File.join(File.dirname(File.dirname(__FILE__)), "spec", "FakeSketch", "FakeSketch.ino") + +puts "verify a simple sketch" +got_problem = true unless arduino_cmd.verify_sketch(simple_sketch) + abort if got_problem exit(0) diff --git a/lib/arduino_ci/arduino_cmd.rb b/lib/arduino_ci/arduino_cmd.rb index aee8b6fe..aefb095a 100644 --- a/lib/arduino_ci/arduino_cmd.rb +++ b/lib/arduino_ci/arduino_cmd.rb @@ -151,5 +151,14 @@ def use_board!(boardname) use_board(boardname) end + def verify_sketch(path) + ext = File.extname path + unless ext.casecmp(".ino").zero? + puts "Refusing to verify sketch with '#{ext}' extension -- rename it to '.ino'!" + return false + end + run("--verify", path, err: :out) + end + end end diff --git a/spec/BadSketch/BadSketch.ino b/spec/BadSketch/BadSketch.ino new file mode 100644 index 00000000..2f590816 --- /dev/null +++ b/spec/BadSketch/BadSketch.ino @@ -0,0 +1 @@ +Oh god how did this get in here I am not good with computer diff --git a/spec/FakeSketch/FakeSketch.ino b/spec/FakeSketch/FakeSketch.ino new file mode 100644 index 00000000..869b9ab3 --- /dev/null +++ b/spec/FakeSketch/FakeSketch.ino @@ -0,0 +1,2 @@ +void setup(void) { 0; } +void loop(void) { 0; } diff --git a/spec/arduino_cmd_spec.rb b/spec/arduino_cmd_spec.rb index 07670fc0..99a2d379 100644 --- a/spec/arduino_cmd_spec.rb +++ b/spec/arduino_cmd_spec.rb @@ -1,5 +1,10 @@ require "spec_helper" +def get_sketch(dir, file) + File.join(File.dirname(__FILE__), dir, file) +end + + RSpec.describe ArduinoCI::ArduinoCmd do context "autolocate" do it "Finds the Arduino executable" do @@ -42,4 +47,32 @@ expect(result).to be true end 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") + sketch_path_mia = get_sketch("NO_FILE_HERE", "foo.ino") + sketch_path_bad = get_sketch("BadSketch", "BadSketch.ino") + + it "Passes a simple INO sketch at #{sketch_path_ino}" do + expect(arduino_cmd.verify_sketch(sketch_path_ino)).to be true + # try twice in a row + expect(arduino_cmd.verify_sketch(sketch_path_ino)).to be true + end + + it "Rejects a PDE sketch at #{sketch_path_pde}" do + expect(arduino_cmd.verify_sketch(sketch_path_pde)).to be false + end + + it "Fails a missing sketch at #{sketch_path_mia}" do + expect(arduino_cmd.verify_sketch(sketch_path_mia)).to be false + end + + it "Fails a bad sketch at #{sketch_path_bad}" do + expect(arduino_cmd.verify_sketch(sketch_path_bad)).to be false + end + end end From d29cebefe90c0c78296c2e150870028146903128 Mon Sep 17 00:00:00 2001 From: Ian Katz Date: Mon, 15 Jan 2018 07:42:34 -0500 Subject: [PATCH 02/11] report success or failure of command run by DisplayManager --- lib/arduino_ci/display_manager.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/arduino_ci/display_manager.rb b/lib/arduino_ci/display_manager.rb index 34e15e92..f011c888 100644 --- a/lib/arduino_ci/display_manager.rb +++ b/lib/arduino_ci/display_manager.rb @@ -151,7 +151,8 @@ def run(*args, **kwargs) shell_vars = env_vars.map { |k, v| "#{k}=#{v}" }.join(" ") puts " $ #{shell_vars} #{actual_args.join(' ')}" ret = system(*full_cmd, **kwargs) - puts "#{actual_args[0]} has completed" + status = ret ? "succeeded" : "failed" + puts "#{actual_args[0]} has #{status}" end ret end From 02e3588f75c37972c17aa70423dd18ed49206625 Mon Sep 17 00:00:00 2001 From: Ian Katz Date: Mon, 15 Jan 2018 10:10:00 -0500 Subject: [PATCH 03/11] update to arduino 1.8.5 --- lib/arduino_ci/arduino_installation.rb | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/lib/arduino_ci/arduino_installation.rb b/lib/arduino_ci/arduino_installation.rb index 10ef76fd..235237bb 100644 --- a/lib/arduino_ci/arduino_installation.rb +++ b/lib/arduino_ci/arduino_installation.rb @@ -1,5 +1,7 @@ require "arduino_ci/host" +DESIRED_ARDUINO_IDE_VERSION = "1.8.5".freeze + module ArduinoCI # Manage the OS-specific install location of Arduino @@ -55,9 +57,11 @@ def autolocate! end def force_install - system("wget", "https://downloads.arduino.cc/arduino-1.6.5-linux64.tar.xz") - system("tar", "xf", "arduino-1.6.5-linux64.tar.xz") - system("mv", "arduino-1.6.5", force_install_location) + 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) end end From 2f67cadbe59e9c7cead9bae16944e7790c73276f Mon Sep 17 00:00:00 2001 From: Ian Katz Date: Mon, 15 Jan 2018 10:13:40 -0500 Subject: [PATCH 04/11] explicit error message for nonexistent file --- lib/arduino_ci/arduino_cmd.rb | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/arduino_ci/arduino_cmd.rb b/lib/arduino_ci/arduino_cmd.rb index aefb095a..e2cc2f86 100644 --- a/lib/arduino_ci/arduino_cmd.rb +++ b/lib/arduino_ci/arduino_cmd.rb @@ -157,6 +157,10 @@ def verify_sketch(path) puts "Refusing to verify sketch with '#{ext}' extension -- rename it to '.ino'!" return false end + unless File.exist? path + puts "Can't verify nonexistent Sketch at '#{path}'!" + return false + end run("--verify", path, err: :out) end From d798161d3f15f05679639febf06c66f7cfb21aa6 Mon Sep 17 00:00:00 2001 From: Ian Katz Date: Mon, 15 Jan 2018 10:36:32 -0500 Subject: [PATCH 05/11] try a different board to pass CI --- exe/ci_system_check.rb | 12 ++++++++---- spec/arduino_cmd_spec.rb | 11 +++++------ 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/exe/ci_system_check.rb b/exe/ci_system_check.rb index 39ababac..576a8f62 100755 --- a/exe/ci_system_check.rb +++ b/exe/ci_system_check.rb @@ -33,12 +33,16 @@ got_problem = true unless arduino_cmd.install_library("USBHost") puts "checking that library is indexed" got_problem = true unless arduino_cmd.library_is_indexed -puts "setting compiler warning level" -got_problem = true unless arduino_cmd.set_pref("compiler.warning_level", "all") + +my_board = "arduino:sam:arduino_due_x" + puts "use board! (install board)" -got_problem = true unless arduino_cmd.use_board!("arduino:samd:zero") +got_problem = true unless arduino_cmd.use_board!(my_board) puts "assert that board has been installed" -got_problem = true unless arduino_cmd.board_installed?("arduino:samd:zero") +got_problem = true unless arduino_cmd.board_installed?(my_board) + +puts "setting compiler warning level" +got_problem = true unless arduino_cmd.set_pref("compiler.warning_level", "all") simple_sketch = File.join(File.dirname(File.dirname(__FILE__)), "spec", "FakeSketch", "FakeSketch.ino") diff --git a/spec/arduino_cmd_spec.rb b/spec/arduino_cmd_spec.rb index 99a2d379..232fdc97 100644 --- a/spec/arduino_cmd_spec.rb +++ b/spec/arduino_cmd_spec.rb @@ -57,12 +57,6 @@ def get_sketch(dir, file) sketch_path_mia = get_sketch("NO_FILE_HERE", "foo.ino") sketch_path_bad = get_sketch("BadSketch", "BadSketch.ino") - it "Passes a simple INO sketch at #{sketch_path_ino}" do - expect(arduino_cmd.verify_sketch(sketch_path_ino)).to be true - # try twice in a row - expect(arduino_cmd.verify_sketch(sketch_path_ino)).to be true - end - it "Rejects a PDE sketch at #{sketch_path_pde}" do expect(arduino_cmd.verify_sketch(sketch_path_pde)).to be false end @@ -74,5 +68,10 @@ def get_sketch(dir, file) it "Fails a bad sketch at #{sketch_path_bad}" do expect(arduino_cmd.verify_sketch(sketch_path_bad)).to be false end + + it "Passes a simple INO sketch at #{sketch_path_ino}" do + expect(arduino_cmd.verify_sketch(sketch_path_ino)).to be true + end + end end From b730e425667e55b1dd831f9eceb21f8b4b653ac0 Mon Sep 17 00:00:00 2001 From: Ian Katz Date: Mon, 15 Jan 2018 11:21:34 -0500 Subject: [PATCH 06/11] big refactor to enable no-x-required arduino-builder --- lib/arduino_ci/arduino_cmd.rb | 55 ++++++++++++----- lib/arduino_ci/arduino_installation.rb | 81 ++++++++++++++++++++------ lib/arduino_ci/display_manager.rb | 6 +- lib/arduino_ci/host.rb | 16 +++++ spec/arduino_cmd_spec.rb | 6 +- spec/arduino_installation_spec.rb | 2 +- 6 files changed, 125 insertions(+), 41 deletions(-) diff --git a/lib/arduino_ci/arduino_cmd.rb b/lib/arduino_ci/arduino_cmd.rb index e2cc2f86..97080793 100644 --- a/lib/arduino_ci/arduino_cmd.rb +++ b/lib/arduino_ci/arduino_cmd.rb @@ -22,7 +22,6 @@ def autolocate! end attr_accessor :installation - attr_reader :prefs_cache attr_reader :prefs_response_time attr_reader :library_is_indexed @@ -31,26 +30,47 @@ def initialize(installation) @display_mgr = DisplayManager::instance @installation = installation @prefs_response_time = nil - @prefs_cache = prefs + @prefs_cache = nil @library_is_indexed = false end + 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) + acc[parts[0]] = parts[1] + acc + end + ret + end + # fetch preferences to a hash - def prefs + def _prefs resp = nil - @display_mgr.with_display do + 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 return nil unless resp[:success] - lines = resp[:out].split("\n").select { |l| l.include? "=" } - ret = lines.each_with_object({}) do |e, acc| - parts = e.split("=", 2) - acc[parts[0]] = parts[1] - acc - end - ret + _parse_pref_string(resp[:out]) + end + + def prefs + @prefs_cache = _prefs if @prefs_cache.nil? + @prefs_cache.clone + end + + # get a preference key + def get_pref(key) + data = @prefs_cache.nil? ? prefs : @prefs_cache + data[key] end # set a preference key/value pair @@ -65,13 +85,22 @@ def set_pref(key, value) # run the arduino command def run(*args, **kwargs) - full_args = [@installation.cmd_path] + args - @display_mgr.run(*full_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 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 + + prefs if @prefs_response_time.nil? x3 = @prefs_response_time * 3 Timeout.timeout(x3) do result = run_and_capture(*args, **kwargs) diff --git a/lib/arduino_ci/arduino_installation.rb b/lib/arduino_ci/arduino_installation.rb index 235237bb..80477383 100644 --- a/lib/arduino_ci/arduino_installation.rb +++ b/lib/arduino_ci/arduino_installation.rb @@ -6,53 +6,96 @@ module ArduinoCI # Manage the OS-specific install location of Arduino class ArduinoInstallation - attr_accessor :cmd_path + 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 File.exist? builder + ret.base_cmd = [builder] + ret.requires_x = false + else + ret.base_cmd = [File.join(force_install_location, "arduino")] + ret.requires_x = true + end + ret.lib_dir = File.join(force_install_location, "libraries") + # TODO: "libraries" is what's in the adafruit install.sh script + ret + end + # attempt to find a workable Arduino executable across platforms def autolocate - ret = new + osx_root = "/Applications/Arduino.app" + old_way = false + if File.exist? osx_root + ret = new + osx_place = "#{osx_root}/Contents/MacOS" - osx_place = "/Applications/Arduino.app/Contents/MacOS" - if File.exist? osx_place - ret.cmd_path = File.join(osx_place, "Arduino") + 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", + ] + end ret.lib_dir = File.join(osx_place, "Libraries") + 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") + # unless 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 + # return ret + # end + posix_place = Host.which("arduino") unless posix_place.nil? - ret.cmd_path = posix_place + 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 return ret end - if File.exist? force_install_location - ret.cmd_path = File.join(force_install_location, "arduino") - ret.lib_dir = File.join(force_install_location, "libraries") - # TODO: "libraries" is what's in the adafruit install.sh script - return ret - end + return from_forced_install if File.exist? force_install_location - ret + new 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.cmd_path.nil? - # force the install + return candidate unless candidate.base_cmd.nil? - if force_install - candidate.cmd_path = File.join(force_install_location, "arduino") - candidate.lib_dir = File.join(force_install_location, "libraries") - end + # force the install + candidate = from_forced_install if force_install candidate end diff --git a/lib/arduino_ci/display_manager.rb b/lib/arduino_ci/display_manager.rb index f011c888..372af9ae 100644 --- a/lib/arduino_ci/display_manager.rb +++ b/lib/arduino_ci/display_manager.rb @@ -148,11 +148,7 @@ def run(*args, **kwargs) env_vars.merge!(args[0]) if has_env actual_args = has_env ? args[1..-1] : args # need to shift over if we extracted args full_cmd = env_vars.empty? ? actual_args : [env_vars] + actual_args - shell_vars = env_vars.map { |k, v| "#{k}=#{v}" }.join(" ") - puts " $ #{shell_vars} #{actual_args.join(' ')}" - ret = system(*full_cmd, **kwargs) - status = ret ? "succeeded" : "failed" - puts "#{actual_args[0]} has #{status}" + ret = Host.run(*full_cmd, **kwargs) end ret end diff --git a/lib/arduino_ci/host.rb b/lib/arduino_ci/host.rb index aeed2c2f..8623bfc1 100644 --- a/lib/arduino_ci/host.rb +++ b/lib/arduino_ci/host.rb @@ -15,5 +15,21 @@ def self.which(cmd) end nil end + + # run a command in a display + def self.run(*args, **kwargs) + # do some work to extract & merge environment variables if they exist + has_env = !args.empty? && args[0].class == Hash + env_vars = has_env ? args[0] : {} + actual_args = has_env ? args[1..-1] : args # need to shift over if we extracted args + full_cmd = env_vars.empty? ? actual_args : [env_vars] + actual_args + shell_vars = env_vars.map { |k, v| "#{k}=#{v}" }.join(" ") + puts " $ #{shell_vars} #{actual_args.join(' ')}" + ret = system(*full_cmd, **kwargs) + status = ret ? "succeeded" : "failed" + puts "#{actual_args[0]} has #{status}" + ret + end + end end diff --git a/spec/arduino_cmd_spec.rb b/spec/arduino_cmd_spec.rb index 232fdc97..f35e964b 100644 --- a/spec/arduino_cmd_spec.rb +++ b/spec/arduino_cmd_spec.rb @@ -15,8 +15,8 @@ def get_sketch(dir, file) context "autolocate!" do it "Finds the Arduino executable" do arduino_cmd = ArduinoCI::ArduinoCmd.autolocate! - expect(arduino_cmd.installation.cmd_path).not_to be nil - expect(arduino_cmd.prefs_cache.class).to be Hash + expect(arduino_cmd.installation.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 @@ -42,7 +42,7 @@ def get_sketch(dir, file) ArduinoCI::DisplayManager::instance.enable it "Sets key to what it was before" do - upload_verify = arduino_cmd.prefs_cache["upload.verify"] + upload_verify = arduino_cmd.get_pref("upload.verify") result = arduino_cmd.set_pref("upload.verify", upload_verify) expect(result).to be true end diff --git a/spec/arduino_installation_spec.rb b/spec/arduino_installation_spec.rb index c0a81278..6915a027 100644 --- a/spec/arduino_installation_spec.rb +++ b/spec/arduino_installation_spec.rb @@ -16,7 +16,7 @@ context "autolocate!" do it "doesn't fail" do installation = ArduinoCI::ArduinoInstallation.autolocate! - expect(installation.cmd_path).not_to be nil + expect(installation.base_cmd).not_to be nil expect(installation.lib_dir).not_to be nil end end From fbabb27612f83eccd15496f0d3736dea4e2ac7e7 Mon Sep 17 00:00:00 2001 From: Ian Katz Date: Mon, 15 Jan 2018 11:47:28 -0500 Subject: [PATCH 07/11] add the ability to disable the arduino builder which we just added :( --- lib/arduino_ci/arduino_installation.rb | 30 +++++++++++++------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/lib/arduino_ci/arduino_installation.rb b/lib/arduino_ci/arduino_installation.rb index 80477383..2d96ceee 100644 --- a/lib/arduino_ci/arduino_installation.rb +++ b/lib/arduino_ci/arduino_installation.rb @@ -1,6 +1,7 @@ require "arduino_ci/host" DESIRED_ARDUINO_IDE_VERSION = "1.8.5".freeze +USE_BUILDER = false module ArduinoCI @@ -18,7 +19,7 @@ def force_install_location def from_forced_install ret = new builder = File.join(force_install_location, "arduino-builder") - if File.exist? builder + if USE_BUILDER && File.exist?(builder) ret.base_cmd = [builder] ret.requires_x = false else @@ -59,20 +60,19 @@ def autolocate 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") - # unless 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 - # 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 + return ret + end posix_place = Host.which("arduino") unless posix_place.nil? From 6fcab7aa5c05884990dcd989e51e1c1f30d0cb0f Mon Sep 17 00:00:00 2001 From: Ian Katz Date: Mon, 15 Jan 2018 14:34:36 -0500 Subject: [PATCH 08/11] Add SampleProjects directory --- SampleProjects/README.md | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 SampleProjects/README.md diff --git a/SampleProjects/README.md b/SampleProjects/README.md new file mode 100644 index 00000000..892f9af4 --- /dev/null +++ b/SampleProjects/README.md @@ -0,0 +1,4 @@ +Arduino Sample Projects +======================= + +This directory contains example projects that are meant to be built with this gem. From 9e0934e7a58479c4b4c6f1dacb4fe78836f43cfe Mon Sep 17 00:00:00 2001 From: Ian Katz Date: Mon, 15 Jan 2018 14:35:31 -0500 Subject: [PATCH 09/11] swallow https://github.com/ifreecarve/arduino-ci-unit-tests --- SampleProjects/minimal/.travis.yml | 12 ++ .../minimal/DoSomething/DoSomething.ino | 9 + SampleProjects/minimal/Gemfile | 2 + SampleProjects/minimal/Gemfile.lock | 17 ++ SampleProjects/minimal/LICENSE | 201 ++++++++++++++++++ SampleProjects/minimal/README.md | 23 ++ SampleProjects/minimal/do-something.cpp | 5 + SampleProjects/minimal/do-something.h | 2 + SampleProjects/minimal/library.properties | 10 + SampleProjects/minimal/platformio.ini | 16 ++ 10 files changed, 297 insertions(+) create mode 100644 SampleProjects/minimal/.travis.yml create mode 100644 SampleProjects/minimal/DoSomething/DoSomething.ino create mode 100644 SampleProjects/minimal/Gemfile create mode 100644 SampleProjects/minimal/Gemfile.lock create mode 100644 SampleProjects/minimal/LICENSE create mode 100644 SampleProjects/minimal/README.md create mode 100644 SampleProjects/minimal/do-something.cpp create mode 100644 SampleProjects/minimal/do-something.h create mode 100644 SampleProjects/minimal/library.properties create mode 100644 SampleProjects/minimal/platformio.ini diff --git a/SampleProjects/minimal/.travis.yml b/SampleProjects/minimal/.travis.yml new file mode 100644 index 00000000..31b0e470 --- /dev/null +++ b/SampleProjects/minimal/.travis.yml @@ -0,0 +1,12 @@ +language: ruby + +env: + - PLATFORMIO_CI_SRC=examples/DoSomething/DoSomething.ino + +install: + - bundle install + +script: + # board IDS come from here I think + # http://docs.platformio.org/en/latest/platforms/atmelavr.html#arduino + - platformio ci --lib="." --board=uno diff --git a/SampleProjects/minimal/DoSomething/DoSomething.ino b/SampleProjects/minimal/DoSomething/DoSomething.ino new file mode 100644 index 00000000..b00feb16 --- /dev/null +++ b/SampleProjects/minimal/DoSomething/DoSomething.ino @@ -0,0 +1,9 @@ +#include +// if it seems bare, that's because it's only meant to +// demonstrate compilation -- that references work +void setup() { +} + +void loop() { + doSomething(); +} diff --git a/SampleProjects/minimal/Gemfile b/SampleProjects/minimal/Gemfile new file mode 100644 index 00000000..b2b3b1fd --- /dev/null +++ b/SampleProjects/minimal/Gemfile @@ -0,0 +1,2 @@ +source 'https://rubygems.org' +gem 'arduino_ci', path: '../../' diff --git a/SampleProjects/minimal/Gemfile.lock b/SampleProjects/minimal/Gemfile.lock new file mode 100644 index 00000000..39057fe5 --- /dev/null +++ b/SampleProjects/minimal/Gemfile.lock @@ -0,0 +1,17 @@ +PATH + remote: /Users/ikatz/Development/non-wayfair/arduino_ci + specs: + arduino_ci (0.0.1) + +GEM + remote: https://rubygems.org/ + specs: + +PLATFORMS + ruby + +DEPENDENCIES + arduino_ci! + +BUNDLED WITH + 1.16.0 diff --git a/SampleProjects/minimal/LICENSE b/SampleProjects/minimal/LICENSE new file mode 100644 index 00000000..261eeb9e --- /dev/null +++ b/SampleProjects/minimal/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/SampleProjects/minimal/README.md b/SampleProjects/minimal/README.md new file mode 100644 index 00000000..3012fec2 --- /dev/null +++ b/SampleProjects/minimal/README.md @@ -0,0 +1,23 @@ +# Arduino CI and Unit Tests HOWTO [![Build Status](https://travis-ci.org/ifreecarve/arduino-ci-unit-tests.svg?branch=master)](https://travis-ci.org/ifreecarve/arduino-ci-unit-tests) + +This project is a template for a CI-enabled (and unit testable) Arduino project of your own. + + +### Features + +* Travis CI +* Unit tests +* Development workflow matches CI workflow - the `platformio ci` line at the bottom of `.travis.yml` can be run on your local terminal (just append the name of the file you want to compile). + +# Where The Magic Happens + +Here is the minimal set of files that you will need to adapt to your own project: + +* `.travis.yml` - You'll need to fill in the `env` section with files relevant to your project, and list out all the `--board`s under the `script` section. +* `platformio.ini` - You'll need to add information for any architectures you plan to support. +* `library.properties` - You'll need to update the `architectures` and `includes` lines as appropriate + + +# Credits + +This Arduino example was created in January 2018 by Ian Katz . diff --git a/SampleProjects/minimal/do-something.cpp b/SampleProjects/minimal/do-something.cpp new file mode 100644 index 00000000..60f08106 --- /dev/null +++ b/SampleProjects/minimal/do-something.cpp @@ -0,0 +1,5 @@ +#include +int doSomething(void) { + millis(); // this line is only here to test that we're able to refer to the builtins + return 4; +}; diff --git a/SampleProjects/minimal/do-something.h b/SampleProjects/minimal/do-something.h new file mode 100644 index 00000000..f39ecdff --- /dev/null +++ b/SampleProjects/minimal/do-something.h @@ -0,0 +1,2 @@ +#include +int doSomething(void); diff --git a/SampleProjects/minimal/library.properties b/SampleProjects/minimal/library.properties new file mode 100644 index 00000000..cfa484dc --- /dev/null +++ b/SampleProjects/minimal/library.properties @@ -0,0 +1,10 @@ +name=arduino-ci-unit-tests +version=0.1.0 +author=Ian Katz +maintainer=Ian Katz +sentence=Arduino CI unit test example +paragraph=A skeleton library demonstrating CI and unit tests +category=Other +url=https://github.com/ifreecarve/arduino-ci-unit-tests +architectures=avr +includes=do-something.h diff --git a/SampleProjects/minimal/platformio.ini b/SampleProjects/minimal/platformio.ini new file mode 100644 index 00000000..13244492 --- /dev/null +++ b/SampleProjects/minimal/platformio.ini @@ -0,0 +1,16 @@ +; PlatformIO Project Configuration File +; +; Build options: build flags, source filter, extra scripting +; Upload options: custom port, speed and extra flags +; Library options: dependencies, extra library storages +; +; Please visit documentation for the other options and examples +; http://docs.platformio.org/page/projectconf.html + +[platformio] +env_default = uno + +[env:uno] +platform = atmelavr +framework = arduino +board = uno From b48a96d2beecb87aa3bb9e90f41a7fd91218e37f Mon Sep 17 00:00:00 2001 From: Ian Katz Date: Mon, 15 Jan 2018 15:19:29 -0500 Subject: [PATCH 10/11] initial convert of sample project to ruby CI --- SampleProjects/{minimal => DoSomething}/Gemfile | 0 .../{minimal => DoSomething}/Gemfile.lock | 0 SampleProjects/{minimal => DoSomething}/LICENSE | 0 .../{minimal => DoSomething}/README.md | 0 .../{minimal => DoSomething}/do-something.cpp | 0 .../{minimal => DoSomething}/do-something.h | 0 .../DoSomethingExample/DoSomethingExample.ino} | 0 .../{minimal => DoSomething}/library.properties | 0 SampleProjects/minimal/.travis.yml | 12 ------------ SampleProjects/minimal/platformio.ini | 16 ---------------- 10 files changed, 28 deletions(-) rename SampleProjects/{minimal => DoSomething}/Gemfile (100%) rename SampleProjects/{minimal => DoSomething}/Gemfile.lock (100%) rename SampleProjects/{minimal => DoSomething}/LICENSE (100%) rename SampleProjects/{minimal => DoSomething}/README.md (100%) rename SampleProjects/{minimal => DoSomething}/do-something.cpp (100%) rename SampleProjects/{minimal => DoSomething}/do-something.h (100%) rename SampleProjects/{minimal/DoSomething/DoSomething.ino => DoSomething/examples/DoSomethingExample/DoSomethingExample.ino} (100%) rename SampleProjects/{minimal => DoSomething}/library.properties (100%) delete mode 100644 SampleProjects/minimal/.travis.yml delete mode 100644 SampleProjects/minimal/platformio.ini diff --git a/SampleProjects/minimal/Gemfile b/SampleProjects/DoSomething/Gemfile similarity index 100% rename from SampleProjects/minimal/Gemfile rename to SampleProjects/DoSomething/Gemfile diff --git a/SampleProjects/minimal/Gemfile.lock b/SampleProjects/DoSomething/Gemfile.lock similarity index 100% rename from SampleProjects/minimal/Gemfile.lock rename to SampleProjects/DoSomething/Gemfile.lock diff --git a/SampleProjects/minimal/LICENSE b/SampleProjects/DoSomething/LICENSE similarity index 100% rename from SampleProjects/minimal/LICENSE rename to SampleProjects/DoSomething/LICENSE diff --git a/SampleProjects/minimal/README.md b/SampleProjects/DoSomething/README.md similarity index 100% rename from SampleProjects/minimal/README.md rename to SampleProjects/DoSomething/README.md diff --git a/SampleProjects/minimal/do-something.cpp b/SampleProjects/DoSomething/do-something.cpp similarity index 100% rename from SampleProjects/minimal/do-something.cpp rename to SampleProjects/DoSomething/do-something.cpp diff --git a/SampleProjects/minimal/do-something.h b/SampleProjects/DoSomething/do-something.h similarity index 100% rename from SampleProjects/minimal/do-something.h rename to SampleProjects/DoSomething/do-something.h diff --git a/SampleProjects/minimal/DoSomething/DoSomething.ino b/SampleProjects/DoSomething/examples/DoSomethingExample/DoSomethingExample.ino similarity index 100% rename from SampleProjects/minimal/DoSomething/DoSomething.ino rename to SampleProjects/DoSomething/examples/DoSomethingExample/DoSomethingExample.ino diff --git a/SampleProjects/minimal/library.properties b/SampleProjects/DoSomething/library.properties similarity index 100% rename from SampleProjects/minimal/library.properties rename to SampleProjects/DoSomething/library.properties diff --git a/SampleProjects/minimal/.travis.yml b/SampleProjects/minimal/.travis.yml deleted file mode 100644 index 31b0e470..00000000 --- a/SampleProjects/minimal/.travis.yml +++ /dev/null @@ -1,12 +0,0 @@ -language: ruby - -env: - - PLATFORMIO_CI_SRC=examples/DoSomething/DoSomething.ino - -install: - - bundle install - -script: - # board IDS come from here I think - # http://docs.platformio.org/en/latest/platforms/atmelavr.html#arduino - - platformio ci --lib="." --board=uno diff --git a/SampleProjects/minimal/platformio.ini b/SampleProjects/minimal/platformio.ini deleted file mode 100644 index 13244492..00000000 --- a/SampleProjects/minimal/platformio.ini +++ /dev/null @@ -1,16 +0,0 @@ -; PlatformIO Project Configuration File -; -; Build options: build flags, source filter, extra scripting -; Upload options: custom port, speed and extra flags -; Library options: dependencies, extra library storages -; -; Please visit documentation for the other options and examples -; http://docs.platformio.org/page/projectconf.html - -[platformio] -env_default = uno - -[env:uno] -platform = atmelavr -framework = arduino -board = uno From fdd2c658479c65643304112201140484efcde82b Mon Sep 17 00:00:00 2001 From: Ian Katz Date: Mon, 15 Jan 2018 15:41:04 -0500 Subject: [PATCH 11/11] try to test an actual library --- exe/ci_system_check.rb | 11 +++++++ lib/arduino_ci/arduino_cmd.rb | 44 ++++++++++++++++++++++++++ lib/arduino_ci/arduino_installation.rb | 2 +- 3 files changed, 56 insertions(+), 1 deletion(-) diff --git a/exe/ci_system_check.rb b/exe/ci_system_check.rb index 576a8f62..b738f3c4 100755 --- a/exe/ci_system_check.rb +++ b/exe/ci_system_check.rb @@ -49,5 +49,16 @@ puts "verify a simple sketch" got_problem = true unless arduino_cmd.verify_sketch(simple_sketch) +library_path = File.join(File.dirname(File.dirname(__FILE__)), "SampleProjects", "DoSomething") +puts "verify the examples of a library (#{library_path})..." +puts " - Install the library" +installed_library_path = arduino_cmd.install_local_library(library_path) +got_problem = true if installed_library_path.nil? +puts " - Iterate over the examples" +arduino_cmd.each_library_example(installed_library_path) do |example_path| + puts "Iterating #{example_path}" + got_problem = true unless arduino_cmd.verify_sketch(example_path) +end + abort if got_problem exit(0) diff --git a/lib/arduino_ci/arduino_cmd.rb b/lib/arduino_ci/arduino_cmd.rb index 97080793..cf88347f 100644 --- a/lib/arduino_ci/arduino_cmd.rb +++ b/lib/arduino_ci/arduino_cmd.rb @@ -1,3 +1,4 @@ +require 'fileutils' require 'arduino_ci/display_manager' require 'arduino_ci/arduino_installation' @@ -159,6 +160,12 @@ def install_library(library_name) 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) + end + # update the library index def update_library_index # install random lib so the arduino IDE grabs a new library index @@ -193,5 +200,42 @@ def verify_sketch(path) run("--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) + + # 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 + + # 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 nil + end + + puts "#{uhoh}. It may need to be removed manually." + return nil + end + + # install the library + FileUtils.ln_s(library_path, destination_path) + destination_path + end + + def each_library_example(installed_library_path) + example_path = File.join(installed_library_path, "examples") + examples = Pathname.new(example_path).children.select(&:directory?).map(&:to_path).map(&File.method(:basename)) + examples.each do |e| + proj_file = File.join(example_path, e, "#{e}.ino") + puts "Considering #{proj_file}" + yield proj_file if File.exist?(proj_file) + end + end end end diff --git a/lib/arduino_ci/arduino_installation.rb b/lib/arduino_ci/arduino_installation.rb index 2d96ceee..a27b86f0 100644 --- a/lib/arduino_ci/arduino_installation.rb +++ b/lib/arduino_ci/arduino_installation.rb @@ -55,7 +55,7 @@ def autolocate "processing.app.Base", ] end - ret.lib_dir = File.join(osx_place, "Libraries") + ret.lib_dir = File.join(osx_place, "Libraries") # TODO: probably wrong ret.requires_x = false return ret end