Skip to content

Fix OSX compilation and other C++-related issues #28

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
Apr 3, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/issue_template.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
- `ruby -v`:
- `bundle -v`:
- `bundle info arduino_ci`:
- `gcc -v`:
- `g++ -v`:
- Arduino IDE version:
- URL of failing Travis CI job:
- URL of your Arduino project:
Expand Down
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ and this project adheres to [Semantic Versioning](http://semver.org/).

## [Unreleased]
### Added
- Definition of `LED_BUILTIN`, first reported by `dfrencham` on GitHub
- Stubs for `tone` and `noTone`, first suggested by `dfrencham` on GitHub
- Ability to specify multiple compilers for unit testing

### Changed

Expand All @@ -14,6 +17,9 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
### Removed

### Fixed
- Compile errors / portability issues in `WString.h` and `Print.h`, first reported by `dfrencham` on GitHub
- Compile errors / inheritance issues in `Print.h` and `Stream.h`, first reported by `dfrencham` on GitHub
- Print functions for int, double, long, etc

### Security

Expand Down
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -349,6 +349,10 @@ For your unit tests, in addition to setting specific libraries and platforms, yo

```yaml
unittest:
compilers:
- g++ # default
- g++-4.9
- g++-7
testfiles:
select:
- "*-*.*"
Expand Down
2 changes: 1 addition & 1 deletion SampleProjects/DoSomething/Gemfile.lock
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
PATH
remote: /Users/ikatz/Development/non-wayfair/arduino_ci
specs:
arduino_ci (0.0.1)
arduino_ci (0.1.7)
os (~> 1.0)

GEM
Expand Down
4 changes: 4 additions & 0 deletions SampleProjects/TestSomething/.arduino-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,7 @@ unittest:
platforms:
- uno
- due

unittest:
compilers:
- g++
16 changes: 16 additions & 0 deletions SampleProjects/TestSomething/test/serial.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,22 @@ unittest(serial_ports)
assertEqual("bcdefg", state->serialPort[0].dataOut);
}

unittest(all_serial_writes)
{
GodmodeState* state = GODMODE();
state->serialPort[0].dataIn = "";
state->serialPort[0].dataOut = "";

char str[4] = "xyz";
Serial.write(reinterpret_cast<const uint8_t *>(str ), 3);
Serial.print((int)1);
Serial.print((long)2);
Serial.print((double)3.4);
Serial.print((char)'a');
Serial.print("b");
assertEqual("xyz123.4000000000ab", state->serialPort[0].dataOut);
}

#endif

unittest_main()
3 changes: 3 additions & 0 deletions cpp/arduino/ArduinoDefines.h
Original file line number Diff line number Diff line change
Expand Up @@ -89,3 +89,6 @@
#define TIMER5B 17
#define TIMER5C 18

#if defined(__AVR_ATmega328P__) || defined(__AVR_ATmega32U4__) || defined(__AVR_ATmega328__) || defined(__AVR_ATmega168__) || defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
#define LED_BUILTIN 13
#endif
5 changes: 5 additions & 0 deletions cpp/arduino/Godmode.h
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,11 @@ void analogWrite(uint8_t, int);
#define analogReadResolution(...) _NOP()
#define analogWriteResolution(...) _NOP()

// TODO: issue #26 to track the commanded state here
inline void tone(uint8_t _pin, unsigned int frequency, unsigned long duration = 0) {}
inline void tone(uint8_t _pin, unsigned int frequency) {}
inline void noTone(uint8_t _pin) {}


GodmodeState* GODMODE();

7 changes: 2 additions & 5 deletions cpp/arduino/HardwareSerial.h
Original file line number Diff line number Diff line change
Expand Up @@ -57,11 +57,8 @@ class HardwareSerial : public Stream, public ObservableDataStream
return 1;
}

inline size_t write(unsigned long n) { return write((uint8_t)n); }
inline size_t write(long n) { return write((uint8_t)n); }
inline size_t write(unsigned int n) { return write((uint8_t)n); }
inline size_t write(int n) { return write((uint8_t)n); }
// using Print::write; // pull in write(str) and write(buf, size) from Print
// https://stackoverflow.com/a/4271276
using Print::write; // pull in write(str) and write(buf, size) from Print
operator bool() { return true; }

};
Expand Down
49 changes: 25 additions & 24 deletions cpp/arduino/Print.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#pragma once

#include <stdio.h>
#include <avr/pgmspace.h>
#include "WString.h"

#define DEC 10
Expand Down Expand Up @@ -32,38 +33,38 @@ class Print
virtual size_t write(uint8_t) = 0;
size_t write(const char *str) { return str == NULL ? 0 : write((const uint8_t *)str, String(str).length()); }


virtual size_t write(const uint8_t *buffer, size_t size) {
size_t n;
for (n = 0; size && write(*buffer++) && ++n; --size);
return n;
}

size_t write(const char *buffer, size_t size) { return write((const uint8_t *)buffer, size); }

size_t print(const String &s) { return write(s.c_str(), s.length()); }
size_t print(const __FlashStringHelper *str) { return print(reinterpret_cast<PGM_P>(str)); }
size_t print(const char* str) { return print(String(str)); }
size_t print(char c) { return print(String(c)); }
size_t print(unsigned char b, int base) { return print(String(b, base)); }
size_t print(int n, int base) { return print(String(n, base)); }
size_t print(unsigned int n, int base) { return print(String(n, base)); }
size_t print(long n, int base) { return print(String(n, base)); }
size_t print(unsigned long n, int base) { return print(String(n, base)); }
size_t print(double n, int digits) { return print(String(n, digits)); }
size_t print(const Printable& x) { return x.printTo(*this); }
size_t print(const String &s) { return write(s.c_str(), s.length()); }
size_t print(const __FlashStringHelper *str) { return print(reinterpret_cast<PGM_P>(str)); }
size_t print(const char* str) { return print(String(str)); }
size_t print(char c) { return print(String(c)); }
size_t print(unsigned char b, int base = DEC) { return print(String(b, base)); }
size_t print(int n, int base = DEC) { return print(String(n, base)); }
size_t print(unsigned int n, int base = DEC) { return print(String(n, base)); }
size_t print(long n, int base = DEC) { return print(String(n, base)); }
size_t print(unsigned long n, int base = DEC) { return print(String(n, base)); }
size_t print(double n, int base = DEC) { return print(String(n, base)); }
size_t print(const Printable& x) { return x.printTo(*this); }

size_t println(void) { return print("\r\n"); }
size_t println(const String &s) { return print(s) + println(); }
size_t println(const __FlashStringHelper *str) { return println(reinterpret_cast<PGM_P>(str)); }
size_t println(const char* c) { return println(String(c)); }
size_t println(char c) { return println(String(c)); }
size_t println(unsigned char b, int base) { return println(String(b, base)); }
size_t println(int num, int base) { return println(String(num, base)); }
size_t println(unsigned int num, int base) { return println(String(num, base)); }
size_t println(long num, int base) { return println(String(num, base)); }
size_t println(unsigned long num, int base) { return println(String(num, base)); }
size_t println(double num, int digits) { return println(String(num, digits)); }
size_t println(const Printable& x) { return print(x) + println(); }
size_t println(void) { return print("\r\n"); }
size_t println(const String &s) { return print(s) + println(); }
size_t println(const __FlashStringHelper *str) { return println(reinterpret_cast<PGM_P>(str)); }
size_t println(const char* c) { return println(String(c)); }
size_t println(char c) { return println(String(c)); }
size_t println(unsigned char b, int base = DEC) { return println(String(b, base)); }
size_t println(int num, int base = DEC) { return println(String(num, base)); }
size_t println(unsigned int num, int base = DEC) { return println(String(num, base)); }
size_t println(long num, int base = DEC) { return println(String(num, base)); }
size_t println(unsigned long num, int base = DEC) { return println(String(num, base)); }
size_t println(double num, int base = DEC) { return println(String(num, base)); }
size_t println(const Printable& x) { return print(x) + println(); }

virtual void flush() { }

Expand Down
2 changes: 1 addition & 1 deletion cpp/arduino/Stream.h
Original file line number Diff line number Diff line change
Expand Up @@ -56,11 +56,11 @@ class Stream : public Print
return ret;
}

// https://stackoverflow.com/a/4271276
using Print::write;

virtual size_t write(uint8_t aChar) { mGodmodeDataIn->append(String((char)aChar)); return 1; }


Stream() {
mTimeoutMillis = 1000;
mGodmodeMicrosDelay = NULL;
Expand Down
20 changes: 18 additions & 2 deletions cpp/arduino/WString.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,22 @@

typedef std::string string;

// work around some portability issues
#if defined(__clang__)
#define ARDUINOCI_ISNAN ::isnan
#define ARDUINOCI_ISINF ::isinf
#elif defined(__GNUC__) || defined(__GNUG__)
#define ARDUINOCI_ISNAN std::isnan
#define ARDUINOCI_ISINF std::isinf
#elif defined(_MSC_VER)
// TODO: no idea
#define ARDUINOCI_ISNAN ::isnan
#define ARDUINOCI_ISINF ::isinf
#else
#define ARDUINOCI_ISNAN ::isnan
#define ARDUINOCI_ISINF ::isinf
#endif

class __FlashStringHelper;
#define F(string_literal) (reinterpret_cast<const __FlashStringHelper *>(PSTR(string_literal)))

Expand Down Expand Up @@ -36,8 +52,8 @@ class String: public string

static string dtoas(double val, int decimalPlaces) {
double r = 0.5 * pow(0.1, decimalPlaces); // make sure that integer truncation will properly round
if (std::isnan(val)) return "nan";
if (std::isinf(val)) return "inf";
if (ARDUINOCI_ISNAN(val)) return "nan";
if (ARDUINOCI_ISINF(val)) return "inf";
val += val > 0 ? r : -r;
if (val > 4294967040.0) return "ovf";
if (val <-4294967040.0) return "ovf";
Expand Down
42 changes: 24 additions & 18 deletions exe/arduino_ci_remote.rb
Original file line number Diff line number Diff line change
Expand Up @@ -69,11 +69,15 @@ def assure(message, &block)
attempt("Library installed at #{installed_library_path}") { true }

# check GCC
attempt_multiline("Checking GCC version") do
version = cpp_library.gcc_version
next nil unless version
puts version.split("\n").map { |l| " #{l}" }.join("\n")
version
compilers = config.compilers_to_use
assure("The set of compilers (#{compilers.length}) isn't empty") { !compilers.empty? }
compilers.each do |gcc_binary|
attempt_multiline("Checking #{gcc_binary} version") do
version = cpp_library.gcc_version(gcc_binary)
next nil unless version
puts version.split("\n").map { |l| " #{l}" }.join("\n")
version
end
end

# gather up all required boards so we can install them up front.
Expand Down Expand Up @@ -121,20 +125,22 @@ def assure(message, &block)
last_board = board
cpp_library.test_files.each do |unittest_path|
unittest_name = File.basename(unittest_path)
attempt_multiline("Unit testing #{unittest_name}") do
exe = cpp_library.build_for_test_with_configuration(
unittest_path,
config.aux_libraries_for_unittest,
config.gcc_config(p)
)
puts
unless exe
puts "Last command: #{cpp_library.last_cmd}"
puts cpp_library.last_out
puts cpp_library.last_err
next false
compilers.each do |gcc_binary|
attempt_multiline("Unit testing #{unittest_name} with #{gcc_binary}") do
exe = cpp_library.build_for_test_with_configuration(
unittest_path,
config.aux_libraries_for_unittest,
config.gcc_config(p)
)
puts
unless exe
puts "Last command: #{cpp_library.last_cmd}"
puts cpp_library.last_out
puts cpp_library.last_err
next false
end
cpp_library.run_test_file(exe)
end
cpp_library.run_test_file(exe)
end
end
end
Expand Down
8 changes: 8 additions & 0 deletions lib/arduino_ci/ci_config.rb
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
}.freeze

UNITTEST_SCHEMA = {
compilers: Array,
platforms: Array,
libraries: Array,
testfiles: {
Expand Down Expand Up @@ -197,6 +198,13 @@ def package_url(package)
@package_info[package][:url]
end

# compilers to build (unit tests) with
# @return [Array<String>] The compiler binary names (e.g. g++) to build with
def compilers_to_use
return [] if @unittest_info[:compilers].nil?
@unittest_info[:compilers]
end

# platforms to build [the examples on]
# @return [Array<String>] The platforms to build
def platforms_to_build
Expand Down
13 changes: 6 additions & 7 deletions lib/arduino_ci/cpp_library.rb
Original file line number Diff line number Diff line change
Expand Up @@ -107,10 +107,9 @@ def header_dirs
end

# wrapper for the GCC command
def run_gcc(*args, **kwargs)
full_args = ["g++"] + args
def run_gcc(gcc_binary, *args, **kwargs)
full_args = [gcc_binary] + args
@last_cmd = " $ #{full_args.join(' ')}"

ret = Host.run_and_capture(*full_args, **kwargs)
@last_err = ret[:err]
@last_out = ret[:out]
Expand All @@ -119,8 +118,8 @@ def run_gcc(*args, **kwargs)

# Return the GCC version
# @return [String] the version reported by `gcc -v`
def gcc_version
return nil unless run_gcc("-v")
def gcc_version(gcc_binary)
return nil unless run_gcc(gcc_binary, "-v")
@last_err
end

Expand Down Expand Up @@ -183,7 +182,7 @@ def test_args(aux_libraries, ci_gcc_config)
# @param aux_libraries [String] The external Arduino libraries required by this project
# @param ci_gcc_config [Hash] The GCC config object
# @return [String] path to the compiled test executable
def build_for_test_with_configuration(test_file, aux_libraries, ci_gcc_config)
def build_for_test_with_configuration(test_file, aux_libraries, gcc_binary, ci_gcc_config)
base = File.basename(test_file)
executable = File.expand_path("unittest_#{base}.bin")
File.delete(executable) if File.exist?(executable)
Expand All @@ -192,7 +191,7 @@ def build_for_test_with_configuration(test_file, aux_libraries, ci_gcc_config)
test_args(aux_libraries, ci_gcc_config),
[test_file],
].flatten(1)
return nil unless run_gcc(*args)
return nil unless run_gcc(gcc_binary, *args)
artifacts << executable
executable
end
Expand Down
2 changes: 2 additions & 0 deletions misc/default.yml
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,8 @@ compile:
- leonardo

unittest:
compilers:
- g++
libraries: ~
platforms:
- uno
Expand Down
5 changes: 5 additions & 0 deletions spec/ci_config_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@
expect(default_config.platforms_to_unittest).to match(["uno", "due", "zero", "leonardo"])
expect(default_config.aux_libraries_for_build).to match([])
expect(default_config.aux_libraries_for_unittest).to match([])

expect(default_config.compilers_to_use).to match(["g++"])
end
end

Expand Down Expand Up @@ -63,6 +65,9 @@
expect(combined_config.platforms_to_unittest).to match(["bogo"])
expect(combined_config.aux_libraries_for_build).to match(["Adafruit FONA Library"])
expect(combined_config.aux_libraries_for_unittest).to match(["abc123", "def456"])

expect(combined_config.compilers_to_use).to match(["g++", "g++-7"])

end
end

Expand Down
10 changes: 6 additions & 4 deletions spec/cpp_library_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -62,10 +62,12 @@
test_files = cpp_library.test_files
test_files.each do |path|
expected = path.include?("good")
it "tests #{File.basename(path)} expecting #{expected}" do
exe = cpp_library.build_for_test_with_configuration(path, [], config.gcc_config("uno"))
expect(exe).not_to be nil
expect(cpp_library.run_test_file(exe)).to eq(expected)
config.compilers_to_use.each do |compiler|
it "tests #{File.basename(path)} with #{compiler} expecting #{expected}" do
exe = cpp_library.build_for_test_with_configuration(path, [], compiler, config.gcc_config("uno"))
expect(exe).not_to be nil
expect(cpp_library.run_test_file(exe)).to eq(expected)
end
end
end
end
Expand Down
Loading