diff --git a/.github/workflows/aunit_tests.yml b/.github/workflows/aunit_tests.yml index efbb4d4..8d2a225 100644 --- a/.github/workflows/aunit_tests.yml +++ b/.github/workflows/aunit_tests.yml @@ -18,10 +18,10 @@ on: [push] jobs: build: - runs-on: ubuntu-20.04 + runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Setup run: | diff --git a/CHANGELOG.md b/CHANGELOG.md index 00b22f7..695f9a0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,21 @@ # Changelog * Unreleased +* 1.5.0 (2022-12-08) + * Support compiling plain `*.c` files in libraries and in the application. + * The C files are assumed to be written in C11. + * Define the `-D ARDUINO=100` macro. + * Some 3rd party libraries test for `ARDUINO >= 100` to check if it's + being compiled under Arduino. + * Many will compile under EpoxyDuino with this macro set. + * Add `make run` target which runs the binary generated by `make all`. + * Useful in the `vim` editor where `:make run` will invoke the quickfix + feature so that the editor will jump directly to the file and line + where the AUnit assertion failure occurred. + * Update the example `for` loops to invoke `make -C {dir} run` instead + of running the binary directly. The `vim` editor can parse the output + caused by the `-C` flag, and automatically generate the correct path + to the files in the subdirectories. * 1.4.0 (2022-11-06) * Add `memcmp_P()` by @dawidchyrzynski in [PR#71](https://github.com/bxparks/EpoxyDuino/pull/71). diff --git a/EpoxyDuino.mk b/EpoxyDuino.mk index 4537753..c8b4d4a 100644 --- a/EpoxyDuino.mk +++ b/EpoxyDuino.mk @@ -68,8 +68,8 @@ EPOXY_DUINO_PARENT_DIR := $(abspath $(EPOXY_DUINO_DIR)/..) # directory. ARDUINO_LIB_DIRS ?= -# C macro to select a specific core. Valid options are: -# EPOXY_CORE_AVR (default), EPOXY_CORE_ESP8266. +# CPP macro to select a specific core. Valid options are: +# EPOXY_CORE_AVR (default), and EPOXY_CORE_ESP8266. EPOXY_CORE ?= EPOXY_CORE_AVR # Define the directory where the and other core API files are @@ -77,9 +77,10 @@ EPOXY_CORE ?= EPOXY_CORE_AVR EPOXY_CORE_PATH ?= $(EPOXY_DUINO_DIR)/cores/epoxy # Find the directory paths of the libraries listed in ARDUINO_LIBS by looking -# under directory given by EPOXY_DUINO_LIB_DIR, the directory given by -# EPOXY_DUINO_PARENT_DIR to look for siblings, and each directory listed in -# ARDUINO_LIB_DIRS. +# at the following: +# 1) under the directory given by EPOXY_DUINO_LIB_DIR, +# 2) the sibling directories under EPOXY_DUINO_PARENT_DIR, and +# 3) each directory listed in ARDUINO_LIB_DIRS. EPOXY_MODULES := $(foreach lib,$(ARDUINO_LIBS),${EPOXY_DUINO_LIB_DIR}/${lib}) EPOXY_MODULES += $(foreach lib,$(ARDUINO_LIBS),${EPOXY_DUINO_PARENT_DIR}/${lib}) EPOXY_MODULES += \ @@ -118,19 +119,29 @@ EPOXY_MODULES += \ # like c++ on FreeBSD is actualy clang++. It's possible that FreeBSD has the # latest GNU version of libstdc++, but I'm not sure. # -# I added EXTRA_CXXFLAGS to allow end-user Makefiles to specify additional -# CXXFLAGs. +# The CFLAGS assume that any C files are C11 files (not C99) because my +# AceTimeC library requires C11. It may be possible to override that by passing +# the `-std=c99` flag in the EXTRA_CFLAGS variable (which overrides the +# `-std=c11` flag), but this has not been tested. +# +# I added EXTRA_CXXFLAGS and EXTRA_CFLAGS to allow end-user Makefiles to +# specify additional flags to CXXFLAGS and CFLAGS. ifeq ($(UNAME), Linux) CXX ?= g++ -CXXFLAGS ?= -Wextra -Wall -std=gnu++11 -fno-exceptions -fno-threadsafe-statics -flto +CXXFLAGS ?= -Wextra -Wall -std=gnu++11 -flto \ + -fno-exceptions -fno-threadsafe-statics +CFLAGS ?= -Wextra -Wall -std=c11 -flto else ifeq ($(UNAME), Darwin) CXX ?= clang++ CXXFLAGS ?= -Wextra -Wall -std=c++11 -stdlib=libc++ +CFLAGS ?= -Wextra -Wall -std=c11 else ifeq ($(UNAME), FreeBSD) CXX ?= clang++ CXXFLAGS ?= -Wextra -Wall -std=c++11 -stdlib=libc++ +CFLAGS ?= -Wextra -Wall -std=c11 endif CXXFLAGS += $(EXTRA_CXXFLAGS) +CFLAGS += $(EXTRA_CFLAGS) # Pre-processor flags (-I, -D, etc), mostly for header files. CPPFLAGS += $(EXTRA_CPPFLAGS) @@ -138,7 +149,7 @@ CPPFLAGS += $(EXTRA_CPPFLAGS) # instead of Arduino.h so that files like 'compat.h' can determine the # compile-time environment without having to include . # Also define UNIX_HOST_DUINO for backwards compatibility. -CPPFLAGS += -D UNIX_HOST_DUINO -D EPOXY_DUINO -D $(EPOXY_CORE) +CPPFLAGS += -D ARDUINO=100 -D UNIX_HOST_DUINO -D EPOXY_DUINO -D $(EPOXY_CORE) # Add the header files for the Core files. CPPFLAGS += -I$(EPOXY_CORE_PATH) # Add the header files for libraries. Old Arduino libraries place the header @@ -150,9 +161,9 @@ CPPFLAGS += $(foreach module,$(EPOXY_MODULES),$(CPPFLAGS_EXPANSION)) # Linker settings (e.g. -lm). LDFLAGS ?= -# Generate list of C++ srcs to compile. +# Collect list of C and C++ srcs to compile. # -# 1) Add the source files in the Core directory. Support subdirectory +# 1) Collect the source files in the Epoxy Core directory. Support subdirectory # expansions up to 3 levels below the given target. (There might be a better # way to do this using GNU Make but I can't find a mechanism that doesn't barf # when the 'src/' directory doesn't exist.) @@ -160,23 +171,41 @@ EPOXY_SRCS := $(wildcard $(EPOXY_CORE_PATH)/*.cpp) \ $(wildcard $(EPOXY_CORE_PATH)/*/*.cpp) \ $(wildcard $(EPOXY_CORE_PATH)/*/*/*.cpp) \ $(wildcard $(EPOXY_CORE_PATH)/*/*/*/*.cpp) -# 2) Add the source files of the libraries. Old Arduino libraries place the -# source files at the top level. Later Arduino libraries put the source files -# under the src/ directory. Also support 3 levels of subdirectories. -MODULE_EXPANSION = $(wildcard $(module)/*.cpp) \ +# 2) Collect the source files of the libraries referenced by the EPOXY_MODULES +# parameter. Old Arduino libraries place the source files at the top level. +# Later Arduino libraries put the source files under the src/ directory. Also +# support 3 levels of subdirectories. Support both C and C++ libraries files. +MODULE_EXPANSION_CPP = $(wildcard $(module)/*.cpp) \ $(wildcard $(module)/src/*.cpp) \ $(wildcard $(module)/src/*/*.cpp) \ $(wildcard $(module)/src/*/*/*.cpp) \ $(wildcard $(module)/src/*/*/*/*.cpp) -EPOXY_SRCS += $(foreach module,$(EPOXY_MODULES),$(MODULE_EXPANSION)) -# 3) Add the source files in the application directory, also 3 levels down. -EPOXY_SRCS += $(wildcard *.cpp) $(wildcard */*.cpp) $(wildcard */*/*.cpp) \ +MODULE_EXPANSION_C = $(wildcard $(module)/*.c) \ + $(wildcard $(module)/src/*.c) \ + $(wildcard $(module)/src/*/*.c) \ + $(wildcard $(module)/src/*/*/*.c) \ + $(wildcard $(module)/src/*/*/*/*.c) +MODULE_SRCS_CPP += $(foreach module,$(EPOXY_MODULES),$(MODULE_EXPANSION_CPP)) +MODULE_SRCS_C += $(foreach module,$(EPOXY_MODULES),$(MODULE_EXPANSION_C)) +# 3) Collect the source files in the application directory, also 3 levels down. +APP_SRCS_CPP += $(wildcard *.cpp) \ + $(wildcard */*.cpp) \ + $(wildcard */*/*.cpp) \ $(wildcard */*/*/*.cpp) - -# Objects including *.o from *.ino -OBJS += $(EPOXY_SRCS:%.cpp=%.o) $(APP_NAME).o - -# Finally, the rule to generate the binary for the application. +APP_SRCS_C += $(wildcard *.c) \ + $(wildcard */*.c) \ + $(wildcard */*/*.c) \ + $(wildcard */*/*/*.c) + +# Generate the list of object files from the *.cpp, *.c, and *.ino files. +OBJS += $(EPOXY_SRCS:%.cpp=%.o) +OBJS += $(MODULE_SRCS_CPP:%.cpp=%.o) +OBJS += $(MODULE_SRCS_C:%.c=%.o) +OBJS += $(APP_SRCS_CPP:%.cpp=%.o) +OBJS += $(APP_SRCS_C:%.c=%.o) +OBJS +=$(APP_NAME).o + +# Finally the rule to generate the *.out binary file for the application. $(APP_NAME).out: $(OBJS) $(CXX) $(CPPFLAGS) $(CXXFLAGS) -o $@ $^ $(LDFLAGS) @@ -184,18 +213,36 @@ $(APP_NAME).out: $(OBJS) $(APP_NAME).o: $(APP_NAME).ino $(DEPS) $(CXX) $(CPPFLAGS) $(CXXFLAGS) -x c++ -c $< -%.o: %.cpp - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $< -o $@ - -# This simple rule does not capture all header dependencies of a given cpp -# file. Maybe it's better to make each cpp to depend on all headers of a given -# module, and force a recompilation of all cpp files. As far as I understand, -# this is what the Arduino IDE does upon each compile iteration. +# We don't need this rule because the implicit GNU Make rules for converting +# *.c and *.cpp into *.o files are sufficient. +# +# %.o: %.cpp +# $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $< -o $@ + +# The following simple rules do not capture all header dependencies of a given +# *.cpp or *.c file. It's probably better to make each *.cpp and *.c to depend +# on all headers of a given module, and force a recompilation of all the files. +# As far as I understand, this is what the Arduino IDE does upon each compile +# iteration. But this is good enough for now, but has the disadvantage that we +# have to run `make clean` more often than necessary to reset all the +# dependencies. %.cpp: %.h +%.c: %.h -.PHONY: all clean $(MORE_CLEAN) +.PHONY: all clean run $(MORE_CLEAN) +# Use 'make all' or just 'make' to compile the binary. all: $(APP_NAME).out +# Use 'make run' to run the binary created by 'make all'. This is useful when +# the (APP_NAME).out is an AUnit unit test. You can execute ': make run' inside +# the vim editor. The error message of AUnit (as of v1.7) is compatible with +# the quickfix errorformat of vim, so vim will automatically detect assertion +# errors and jump directly to the line where the assertion failed. +run: + ./$(APP_NAME).out + +# Use 'make clean' to remove intermediate '*.o' files, the target '*.out' file, +# and any generated files defined by $(GENERATED). clean: $(MORE_CLEAN) rm -f $(OBJS) $(APP_NAME).out $(GENERATED) diff --git a/README.md b/README.md index 57b6997..12c5ab8 100644 --- a/README.md +++ b/README.md @@ -71,7 +71,7 @@ The disadvantages are: environments (e.g. 16-bit `int` versus 32-bit `int`, or 32-bit `long` versus 64-bit `long`). -**Version**: 1.4.0 (2022-11-06) +**Version**: 1.5.0 (2022-12-08) **Changelog**: See [CHANGELOG.md](CHANGELOG.md) @@ -195,11 +195,20 @@ $ make clean $ make ``` -The executable will be created with a `.out` extension. To run it, just type: +The executable will be created with a `.out` extension. To run it, type +one of the following: ``` $ ./BlinkSOS.out +$ make run ``` +If you run `:make run` command inside the `vim` editor, it knows how to parse +the file and line number contained in the assertion messages generated by +[AUnit](https://github.com/bxparks/AUnit). The `vim` editor will jump directly +to the file and line where the assertion failure occurred. See +https://vimhelp.org/quickfix.txt.html for information on the `quickfix` feature +and the `errorformat` format that parses the GNU Make output. + The output that would normally be printed on the `Serial` on an Arduino board will be sent to the `STDOUT` of the Linux, MacOS, or FreeBSD terminal. The output should be identical to what would be shown on the serial port of the @@ -233,6 +242,13 @@ for these additional libraries at the following locations: * under each of the additional directories listed in `ARDUINO_LIB_DIRS` (see below) +Version v1.5 supports compiling and linking plain C files in the third-party +libraries (defined by `ARDUINO_LIBS`) or in the application directory itself. +The C files are assumed to be written in C11 and are compiled using the `cc` +compiler. It may be possible to override the compiler flags by adding `-std=c99` +into the `EXTRA_CFLAGS` in the Makefile (which clobbers the default `-std=c11`) +but this has not been tested. + ### Additional Arduino Library Locations @@ -404,7 +420,7 @@ runtests: set -e; \ for i in *Test/Makefile; do \ echo '==== Running:' $$(dirname $$i); \ - $$(dirname $$i)/$$(dirname $$i).out; \ + $(MAKE) -C $$(dirname $$i) run; \ done clean: @@ -426,7 +442,16 @@ $ make -C tests tests $ make -C tests runtests | grep failed ``` -These parent Makefiles can be used in Continuous Integration, as shown below. +If you run `:make runtests` inside the `vim` editor, it knows how to parse the +diagnostic outputs generated by GNU Make (as it changes directories through the +`-C` flag), and it knows how to parse the file and line number contained in the +assertion messages generated by [AUnit](https://github.com/bxparks/AUnit). The +`vim` editor will jump directly to the file and line where the assertion failure +occurred. See https://vimhelp.org/quickfix.txt.html for information on the +`quickfix` feature and the `errorformat` format that parses the GNU Make output. + +These parent Makefiles can also be used in Continuous Integration, as shown +below. ### Continuous Integration @@ -461,8 +486,16 @@ you can use that instead by specifying the `CXX` makefile variable: ``` $ make CXX=clang++ ``` -(This tells `make` to set the `CXX` variable to `clang++` within the context of -`EpoxyDuino.mk` which causes `clang++` to be used over the default `g++`.) + +This tells `make` to set the `CXX` variable to `clang++` within the context of +`EpoxyDuino.mk` which causes `clang++` to be used over the default `g++`. + +If you have C files in your library or application, you can use to following to +compile the C files using `clang` instead of `cc` (which is the same as `gcc` on +Linux): +``` +$ make CXX=clang++ CC=clang +``` One reason to use `clang++` instead of `g++` is that the `clang++` compiler will sometimes catch a different set of programming errors. @@ -487,6 +520,12 @@ variable, like this: $ make EXTRA_CXXFLAGS='-g' ``` +or + +``` +$ make EXTRA_CXXFLAGS='-g' EXTRA_CFLAGS='-g' +``` + ### Additional Clean Up @@ -822,6 +861,35 @@ The `pin` parameter should satisfy `0 <= pin < 32`. If `pin >= 32`, then ## Supported Arduino Features + +### Arduino Macros + +The following C-preprocessor macros are defined: + +* `ARDUINO=100` + * Indicates compilation under Arduino. +* `EPOXY_DUINO` + * Indicates compilation under EpoxyDuino +* `UNIX_HOST_DUINO` + * For backwards compatibility with previous version of `EpoxyDuino` + which was named `UnixHostDuino`. +* `EPOXY_CORE_AVR` + * Defined when the Makefile variable `EPOXY_CORE` is set to + `ARDUINO_ARCH_AVR`. + * This is the default. +* `EPOXY_CORE_ESP8266` + * Defined when the Makefile variable `EPOXY_CORE` is set to + `ARDUINO_ARCH_ESP8266`. + +The following are **not** defined. Many third party libraries tend to break with +those defined because EpoxyDuino does not emulate those environments perfectly: + +* `ARDUINO_ARCH_AVR` +* `ARDUINO_ARCH_ESP8266` +* `ARDUINO_ARCH_ESP32` +* `ESP8266` +* `ESP32` + ### Arduino Functions diff --git a/cores/epoxy/Arduino.h b/cores/epoxy/Arduino.h index 41e1d85..6e7ae2d 100644 --- a/cores/epoxy/Arduino.h +++ b/cores/epoxy/Arduino.h @@ -14,8 +14,8 @@ #define EPOXY_DUINO_EPOXY_ARDUINO_H // xx.yy.zz => xxyyzz (without leading 0) -#define EPOXY_DUINO_VERSION 10400 -#define EPOXY_DUINO_VERSION_STRING "1.4.0" +#define EPOXY_DUINO_VERSION 10500 +#define EPOXY_DUINO_VERSION_STRING "1.5.0" #include // min(), max() #include // abs() diff --git a/examples/Makefile b/examples/Makefile index 4c34194..a63c8d8 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -2,7 +2,7 @@ all: set -e; \ for i in */Makefile; do \ echo '==== Making:' $$(dirname $$i); \ - $(MAKE) -C $$(dirname $$i) -j; \ + $(MAKE) -C $$(dirname $$i); \ done clean: diff --git a/libraries/EpoxyEepromAvr/examples/Makefile b/libraries/EpoxyEepromAvr/examples/Makefile index 4c34194..a63c8d8 100644 --- a/libraries/EpoxyEepromAvr/examples/Makefile +++ b/libraries/EpoxyEepromAvr/examples/Makefile @@ -2,7 +2,7 @@ all: set -e; \ for i in */Makefile; do \ echo '==== Making:' $$(dirname $$i); \ - $(MAKE) -C $$(dirname $$i) -j; \ + $(MAKE) -C $$(dirname $$i); \ done clean: diff --git a/libraries/EpoxyEepromAvr/tests/Makefile b/libraries/EpoxyEepromAvr/tests/Makefile index 5529273..c018e96 100644 --- a/libraries/EpoxyEepromAvr/tests/Makefile +++ b/libraries/EpoxyEepromAvr/tests/Makefile @@ -2,14 +2,14 @@ tests: set -e; \ for i in *Test/Makefile; do \ echo '==== Making:' $$(dirname $$i); \ - $(MAKE) -C $$(dirname $$i) -j; \ + $(MAKE) -C $$(dirname $$i); \ done runtests: set -e; \ for i in *Test/Makefile; do \ echo '==== Running:' $$(dirname $$i); \ - $$(dirname $$i)/$$(dirname $$i).out; \ + $(MAKE) -C $$(dirname $$i) run; \ done clean: diff --git a/libraries/EpoxyEepromEsp/examples/Makefile b/libraries/EpoxyEepromEsp/examples/Makefile index 4c34194..a63c8d8 100644 --- a/libraries/EpoxyEepromEsp/examples/Makefile +++ b/libraries/EpoxyEepromEsp/examples/Makefile @@ -2,7 +2,7 @@ all: set -e; \ for i in */Makefile; do \ echo '==== Making:' $$(dirname $$i); \ - $(MAKE) -C $$(dirname $$i) -j; \ + $(MAKE) -C $$(dirname $$i); \ done clean: diff --git a/libraries/EpoxyEepromEsp/tests/Makefile b/libraries/EpoxyEepromEsp/tests/Makefile index 5529273..c018e96 100644 --- a/libraries/EpoxyEepromEsp/tests/Makefile +++ b/libraries/EpoxyEepromEsp/tests/Makefile @@ -2,14 +2,14 @@ tests: set -e; \ for i in *Test/Makefile; do \ echo '==== Making:' $$(dirname $$i); \ - $(MAKE) -C $$(dirname $$i) -j; \ + $(MAKE) -C $$(dirname $$i); \ done runtests: set -e; \ for i in *Test/Makefile; do \ echo '==== Running:' $$(dirname $$i); \ - $$(dirname $$i)/$$(dirname $$i).out; \ + $(MAKE) -C $$(dirname $$i) run; \ done clean: diff --git a/libraries/EpoxyFS/examples/Makefile b/libraries/EpoxyFS/examples/Makefile index 4c34194..a63c8d8 100644 --- a/libraries/EpoxyFS/examples/Makefile +++ b/libraries/EpoxyFS/examples/Makefile @@ -2,7 +2,7 @@ all: set -e; \ for i in */Makefile; do \ echo '==== Making:' $$(dirname $$i); \ - $(MAKE) -C $$(dirname $$i) -j; \ + $(MAKE) -C $$(dirname $$i); \ done clean: diff --git a/libraries/EpoxyFS/tests/Makefile b/libraries/EpoxyFS/tests/Makefile index 5529273..c018e96 100644 --- a/libraries/EpoxyFS/tests/Makefile +++ b/libraries/EpoxyFS/tests/Makefile @@ -2,14 +2,14 @@ tests: set -e; \ for i in *Test/Makefile; do \ echo '==== Making:' $$(dirname $$i); \ - $(MAKE) -C $$(dirname $$i) -j; \ + $(MAKE) -C $$(dirname $$i); \ done runtests: set -e; \ for i in *Test/Makefile; do \ echo '==== Running:' $$(dirname $$i); \ - $$(dirname $$i)/$$(dirname $$i).out; \ + $(MAKE) -C $$(dirname $$i) run; \ done clean: diff --git a/libraries/EpoxyMockDigitalWriteFast/examples/Makefile b/libraries/EpoxyMockDigitalWriteFast/examples/Makefile index 4c34194..a63c8d8 100644 --- a/libraries/EpoxyMockDigitalWriteFast/examples/Makefile +++ b/libraries/EpoxyMockDigitalWriteFast/examples/Makefile @@ -2,7 +2,7 @@ all: set -e; \ for i in */Makefile; do \ echo '==== Making:' $$(dirname $$i); \ - $(MAKE) -C $$(dirname $$i) -j; \ + $(MAKE) -C $$(dirname $$i); \ done clean: diff --git a/library.json b/library.json index d5d16dd..020c729 100644 --- a/library.json +++ b/library.json @@ -1,6 +1,6 @@ { "name": "EpoxyDuino", - "version": "1.4.0", + "version": "1.5.0", "description": "Compile and run Arduino programs natively on Linux, MacOS and FreeBSD.", "keywords": [ "unit-test", diff --git a/tests/Makefile b/tests/Makefile index 3cd6316..1677e18 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -11,7 +11,7 @@ tests: for i in *Test/Makefile; do \ echo '==== Making:' $$(dirname $$i); \ $(MAKE) -C $$(dirname $$i) clean; \ - $(MAKE) -C $$(dirname $$i) -j; \ + $(MAKE) -C $$(dirname $$i); \ done runtests: @@ -19,8 +19,8 @@ runtests: for i in *Test/Makefile; do \ echo '==== Running:' $$(dirname $$i); \ $(MAKE) -C $$(dirname $$i) clean; \ - $(MAKE) -C $$(dirname $$i) -j all; \ - $$(dirname $$i)/$$(dirname $$i).out; \ + $(MAKE) -C $$(dirname $$i) all; \ + $(MAKE) -C $$(dirname $$i) run; \ done clean: