Skip to content

Commit 0b256d1

Browse files
authored
Merge pull request #28 from ifreecarve/2018-03-27_compilation
Fix OSX compilation and other C++-related issues, supersede #24
2 parents ea1ab74 + fd3da09 commit 0b256d1

20 files changed

+167
-82
lines changed

.github/issue_template.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
- `ruby -v`:
55
- `bundle -v`:
66
- `bundle info arduino_ci`:
7-
- `gcc -v`:
7+
- `g++ -v`:
88
- Arduino IDE version:
99
- URL of failing Travis CI job:
1010
- URL of your Arduino project:

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
66

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

1013
### Changed
1114

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

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

1824
### Security
1925

README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -349,6 +349,10 @@ For your unit tests, in addition to setting specific libraries and platforms, yo
349349
350350
```yaml
351351
unittest:
352+
compilers:
353+
- g++ # default
354+
- g++-4.9
355+
- g++-7
352356
testfiles:
353357
select:
354358
- "*-*.*"

SampleProjects/DoSomething/Gemfile.lock

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
PATH
22
remote: /Users/ikatz/Development/non-wayfair/arduino_ci
33
specs:
4-
arduino_ci (0.0.1)
4+
arduino_ci (0.1.7)
55
os (~> 1.0)
66

77
GEM

SampleProjects/TestSomething/.arduino-ci.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,7 @@ unittest:
22
platforms:
33
- uno
44
- due
5+
6+
unittest:
7+
compilers:
8+
- g++

SampleProjects/TestSomething/test/serial.cpp

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,22 @@ unittest(serial_ports)
4343
assertEqual("bcdefg", state->serialPort[0].dataOut);
4444
}
4545

46+
unittest(all_serial_writes)
47+
{
48+
GodmodeState* state = GODMODE();
49+
state->serialPort[0].dataIn = "";
50+
state->serialPort[0].dataOut = "";
51+
52+
char str[4] = "xyz";
53+
Serial.write(reinterpret_cast<const uint8_t *>(str ), 3);
54+
Serial.print((int)1);
55+
Serial.print((long)2);
56+
Serial.print((double)3.4);
57+
Serial.print((char)'a');
58+
Serial.print("b");
59+
assertEqual("xyz123.4000000000ab", state->serialPort[0].dataOut);
60+
}
61+
4662
#endif
4763

4864
unittest_main()

cpp/arduino/ArduinoDefines.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,3 +89,6 @@
8989
#define TIMER5B 17
9090
#define TIMER5C 18
9191

92+
#if defined(__AVR_ATmega328P__) || defined(__AVR_ATmega32U4__) || defined(__AVR_ATmega328__) || defined(__AVR_ATmega168__) || defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
93+
#define LED_BUILTIN 13
94+
#endif

cpp/arduino/Godmode.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,11 @@ void analogWrite(uint8_t, int);
9191
#define analogReadResolution(...) _NOP()
9292
#define analogWriteResolution(...) _NOP()
9393

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

95100
GodmodeState* GODMODE();
96101

cpp/arduino/HardwareSerial.h

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -57,11 +57,8 @@ class HardwareSerial : public Stream, public ObservableDataStream
5757
return 1;
5858
}
5959

60-
inline size_t write(unsigned long n) { return write((uint8_t)n); }
61-
inline size_t write(long n) { return write((uint8_t)n); }
62-
inline size_t write(unsigned int n) { return write((uint8_t)n); }
63-
inline size_t write(int n) { return write((uint8_t)n); }
64-
// using Print::write; // pull in write(str) and write(buf, size) from Print
60+
// https://stackoverflow.com/a/4271276
61+
using Print::write; // pull in write(str) and write(buf, size) from Print
6562
operator bool() { return true; }
6663

6764
};

cpp/arduino/Print.h

Lines changed: 25 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#pragma once
22

33
#include <stdio.h>
4+
#include <avr/pgmspace.h>
45
#include "WString.h"
56

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

35-
3636
virtual size_t write(const uint8_t *buffer, size_t size) {
3737
size_t n;
3838
for (n = 0; size && write(*buffer++) && ++n; --size);
3939
return n;
4040
}
41+
4142
size_t write(const char *buffer, size_t size) { return write((const uint8_t *)buffer, size); }
4243

43-
size_t print(const String &s) { return write(s.c_str(), s.length()); }
44-
size_t print(const __FlashStringHelper *str) { return print(reinterpret_cast<PGM_P>(str)); }
45-
size_t print(const char* str) { return print(String(str)); }
46-
size_t print(char c) { return print(String(c)); }
47-
size_t print(unsigned char b, int base) { return print(String(b, base)); }
48-
size_t print(int n, int base) { return print(String(n, base)); }
49-
size_t print(unsigned int n, int base) { return print(String(n, base)); }
50-
size_t print(long n, int base) { return print(String(n, base)); }
51-
size_t print(unsigned long n, int base) { return print(String(n, base)); }
52-
size_t print(double n, int digits) { return print(String(n, digits)); }
53-
size_t print(const Printable& x) { return x.printTo(*this); }
44+
size_t print(const String &s) { return write(s.c_str(), s.length()); }
45+
size_t print(const __FlashStringHelper *str) { return print(reinterpret_cast<PGM_P>(str)); }
46+
size_t print(const char* str) { return print(String(str)); }
47+
size_t print(char c) { return print(String(c)); }
48+
size_t print(unsigned char b, int base = DEC) { return print(String(b, base)); }
49+
size_t print(int n, int base = DEC) { return print(String(n, base)); }
50+
size_t print(unsigned int n, int base = DEC) { return print(String(n, base)); }
51+
size_t print(long n, int base = DEC) { return print(String(n, base)); }
52+
size_t print(unsigned long n, int base = DEC) { return print(String(n, base)); }
53+
size_t print(double n, int base = DEC) { return print(String(n, base)); }
54+
size_t print(const Printable& x) { return x.printTo(*this); }
5455

55-
size_t println(void) { return print("\r\n"); }
56-
size_t println(const String &s) { return print(s) + println(); }
57-
size_t println(const __FlashStringHelper *str) { return println(reinterpret_cast<PGM_P>(str)); }
58-
size_t println(const char* c) { return println(String(c)); }
59-
size_t println(char c) { return println(String(c)); }
60-
size_t println(unsigned char b, int base) { return println(String(b, base)); }
61-
size_t println(int num, int base) { return println(String(num, base)); }
62-
size_t println(unsigned int num, int base) { return println(String(num, base)); }
63-
size_t println(long num, int base) { return println(String(num, base)); }
64-
size_t println(unsigned long num, int base) { return println(String(num, base)); }
65-
size_t println(double num, int digits) { return println(String(num, digits)); }
66-
size_t println(const Printable& x) { return print(x) + println(); }
56+
size_t println(void) { return print("\r\n"); }
57+
size_t println(const String &s) { return print(s) + println(); }
58+
size_t println(const __FlashStringHelper *str) { return println(reinterpret_cast<PGM_P>(str)); }
59+
size_t println(const char* c) { return println(String(c)); }
60+
size_t println(char c) { return println(String(c)); }
61+
size_t println(unsigned char b, int base = DEC) { return println(String(b, base)); }
62+
size_t println(int num, int base = DEC) { return println(String(num, base)); }
63+
size_t println(unsigned int num, int base = DEC) { return println(String(num, base)); }
64+
size_t println(long num, int base = DEC) { return println(String(num, base)); }
65+
size_t println(unsigned long num, int base = DEC) { return println(String(num, base)); }
66+
size_t println(double num, int base = DEC) { return println(String(num, base)); }
67+
size_t println(const Printable& x) { return print(x) + println(); }
6768

6869
virtual void flush() { }
6970

cpp/arduino/Stream.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,11 +56,11 @@ class Stream : public Print
5656
return ret;
5757
}
5858

59+
// https://stackoverflow.com/a/4271276
5960
using Print::write;
6061

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

63-
6464
Stream() {
6565
mTimeoutMillis = 1000;
6666
mGodmodeMicrosDelay = NULL;

cpp/arduino/WString.h

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,22 @@
99

1010
typedef std::string string;
1111

12+
// work around some portability issues
13+
#if defined(__clang__)
14+
#define ARDUINOCI_ISNAN ::isnan
15+
#define ARDUINOCI_ISINF ::isinf
16+
#elif defined(__GNUC__) || defined(__GNUG__)
17+
#define ARDUINOCI_ISNAN std::isnan
18+
#define ARDUINOCI_ISINF std::isinf
19+
#elif defined(_MSC_VER)
20+
// TODO: no idea
21+
#define ARDUINOCI_ISNAN ::isnan
22+
#define ARDUINOCI_ISINF ::isinf
23+
#else
24+
#define ARDUINOCI_ISNAN ::isnan
25+
#define ARDUINOCI_ISINF ::isinf
26+
#endif
27+
1228
class __FlashStringHelper;
1329
#define F(string_literal) (reinterpret_cast<const __FlashStringHelper *>(PSTR(string_literal)))
1430

@@ -36,8 +52,8 @@ class String: public string
3652

3753
static string dtoas(double val, int decimalPlaces) {
3854
double r = 0.5 * pow(0.1, decimalPlaces); // make sure that integer truncation will properly round
39-
if (std::isnan(val)) return "nan";
40-
if (std::isinf(val)) return "inf";
55+
if (ARDUINOCI_ISNAN(val)) return "nan";
56+
if (ARDUINOCI_ISINF(val)) return "inf";
4157
val += val > 0 ? r : -r;
4258
if (val > 4294967040.0) return "ovf";
4359
if (val <-4294967040.0) return "ovf";

exe/arduino_ci_remote.rb

Lines changed: 24 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -69,11 +69,15 @@ def assure(message, &block)
6969
attempt("Library installed at #{installed_library_path}") { true }
7070

7171
# check GCC
72-
attempt_multiline("Checking GCC version") do
73-
version = cpp_library.gcc_version
74-
next nil unless version
75-
puts version.split("\n").map { |l| " #{l}" }.join("\n")
76-
version
72+
compilers = config.compilers_to_use
73+
assure("The set of compilers (#{compilers.length}) isn't empty") { !compilers.empty? }
74+
compilers.each do |gcc_binary|
75+
attempt_multiline("Checking #{gcc_binary} version") do
76+
version = cpp_library.gcc_version(gcc_binary)
77+
next nil unless version
78+
puts version.split("\n").map { |l| " #{l}" }.join("\n")
79+
version
80+
end
7781
end
7882

7983
# gather up all required boards so we can install them up front.
@@ -121,20 +125,22 @@ def assure(message, &block)
121125
last_board = board
122126
cpp_library.test_files.each do |unittest_path|
123127
unittest_name = File.basename(unittest_path)
124-
attempt_multiline("Unit testing #{unittest_name}") do
125-
exe = cpp_library.build_for_test_with_configuration(
126-
unittest_path,
127-
config.aux_libraries_for_unittest,
128-
config.gcc_config(p)
129-
)
130-
puts
131-
unless exe
132-
puts "Last command: #{cpp_library.last_cmd}"
133-
puts cpp_library.last_out
134-
puts cpp_library.last_err
135-
next false
128+
compilers.each do |gcc_binary|
129+
attempt_multiline("Unit testing #{unittest_name} with #{gcc_binary}") do
130+
exe = cpp_library.build_for_test_with_configuration(
131+
unittest_path,
132+
config.aux_libraries_for_unittest,
133+
config.gcc_config(p)
134+
)
135+
puts
136+
unless exe
137+
puts "Last command: #{cpp_library.last_cmd}"
138+
puts cpp_library.last_out
139+
puts cpp_library.last_err
140+
next false
141+
end
142+
cpp_library.run_test_file(exe)
136143
end
137-
cpp_library.run_test_file(exe)
138144
end
139145
end
140146
end

lib/arduino_ci/ci_config.rb

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
}.freeze
2626

2727
UNITTEST_SCHEMA = {
28+
compilers: Array,
2829
platforms: Array,
2930
libraries: Array,
3031
testfiles: {
@@ -197,6 +198,13 @@ def package_url(package)
197198
@package_info[package][:url]
198199
end
199200

201+
# compilers to build (unit tests) with
202+
# @return [Array<String>] The compiler binary names (e.g. g++) to build with
203+
def compilers_to_use
204+
return [] if @unittest_info[:compilers].nil?
205+
@unittest_info[:compilers]
206+
end
207+
200208
# platforms to build [the examples on]
201209
# @return [Array<String>] The platforms to build
202210
def platforms_to_build

lib/arduino_ci/cpp_library.rb

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -107,10 +107,9 @@ def header_dirs
107107
end
108108

109109
# wrapper for the GCC command
110-
def run_gcc(*args, **kwargs)
111-
full_args = ["g++"] + args
110+
def run_gcc(gcc_binary, *args, **kwargs)
111+
full_args = [gcc_binary] + args
112112
@last_cmd = " $ #{full_args.join(' ')}"
113-
114113
ret = Host.run_and_capture(*full_args, **kwargs)
115114
@last_err = ret[:err]
116115
@last_out = ret[:out]
@@ -119,8 +118,8 @@ def run_gcc(*args, **kwargs)
119118

120119
# Return the GCC version
121120
# @return [String] the version reported by `gcc -v`
122-
def gcc_version
123-
return nil unless run_gcc("-v")
121+
def gcc_version(gcc_binary)
122+
return nil unless run_gcc(gcc_binary, "-v")
124123
@last_err
125124
end
126125

@@ -183,7 +182,7 @@ def test_args(aux_libraries, ci_gcc_config)
183182
# @param aux_libraries [String] The external Arduino libraries required by this project
184183
# @param ci_gcc_config [Hash] The GCC config object
185184
# @return [String] path to the compiled test executable
186-
def build_for_test_with_configuration(test_file, aux_libraries, ci_gcc_config)
185+
def build_for_test_with_configuration(test_file, aux_libraries, gcc_binary, ci_gcc_config)
187186
base = File.basename(test_file)
188187
executable = File.expand_path("unittest_#{base}.bin")
189188
File.delete(executable) if File.exist?(executable)
@@ -192,7 +191,7 @@ def build_for_test_with_configuration(test_file, aux_libraries, ci_gcc_config)
192191
test_args(aux_libraries, ci_gcc_config),
193192
[test_file],
194193
].flatten(1)
195-
return nil unless run_gcc(*args)
194+
return nil unless run_gcc(gcc_binary, *args)
196195
artifacts << executable
197196
executable
198197
end

misc/default.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,8 @@ compile:
8080
- leonardo
8181

8282
unittest:
83+
compilers:
84+
- g++
8385
libraries: ~
8486
platforms:
8587
- uno

spec/ci_config_spec.rb

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@
2828
expect(default_config.platforms_to_unittest).to match(["uno", "due", "zero", "leonardo"])
2929
expect(default_config.aux_libraries_for_build).to match([])
3030
expect(default_config.aux_libraries_for_unittest).to match([])
31+
32+
expect(default_config.compilers_to_use).to match(["g++"])
3133
end
3234
end
3335

@@ -63,6 +65,9 @@
6365
expect(combined_config.platforms_to_unittest).to match(["bogo"])
6466
expect(combined_config.aux_libraries_for_build).to match(["Adafruit FONA Library"])
6567
expect(combined_config.aux_libraries_for_unittest).to match(["abc123", "def456"])
68+
69+
expect(combined_config.compilers_to_use).to match(["g++", "g++-7"])
70+
6671
end
6772
end
6873

spec/cpp_library_spec.rb

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -62,10 +62,12 @@
6262
test_files = cpp_library.test_files
6363
test_files.each do |path|
6464
expected = path.include?("good")
65-
it "tests #{File.basename(path)} expecting #{expected}" do
66-
exe = cpp_library.build_for_test_with_configuration(path, [], config.gcc_config("uno"))
67-
expect(exe).not_to be nil
68-
expect(cpp_library.run_test_file(exe)).to eq(expected)
65+
config.compilers_to_use.each do |compiler|
66+
it "tests #{File.basename(path)} with #{compiler} expecting #{expected}" do
67+
exe = cpp_library.build_for_test_with_configuration(path, [], compiler, config.gcc_config("uno"))
68+
expect(exe).not_to be nil
69+
expect(cpp_library.run_test_file(exe)).to eq(expected)
70+
end
6971
end
7072
end
7173
end

0 commit comments

Comments
 (0)