From aea147ad6a5ac94c2f8c3adb06168fc605dc670a Mon Sep 17 00:00:00 2001
From: Angus Gratton <gus@projectgus.com>
Date: Mon, 10 Aug 2015 15:51:57 +1000
Subject: [PATCH] Add C++ support to Makefile, and proof-of-concept simple.cpp
 example

This is a work in progress based on @mikejac's work.

Missing:
* No 'new' operator.
* I don't think STL is currently supported.
---
 FreeRTOS/Source/portable/esp8266/port.c | 11 ++++++
 common.mk                               | 14 +++++--
 core/sdk_compat.c                       | 15 +++++++
 examples/simple_cplusplus/Makefile      |  3 ++
 examples/simple_cplusplus/simple.cpp    | 52 +++++++++++++++++++++++++
 5 files changed, 92 insertions(+), 3 deletions(-)
 create mode 100644 examples/simple_cplusplus/Makefile
 create mode 100644 examples/simple_cplusplus/simple.cpp

diff --git a/FreeRTOS/Source/portable/esp8266/port.c b/FreeRTOS/Source/portable/esp8266/port.c
index 1632fe1..71c6658 100644
--- a/FreeRTOS/Source/portable/esp8266/port.c
+++ b/FreeRTOS/Source/portable/esp8266/port.c
@@ -178,6 +178,9 @@ void xPortSysTickHandle (void)
 	//OpenNMI();
 }
 
+static bool sdk_compat_initialised;
+void sdk_compat_initialise(void);
+
 /*
  * See header file for description.
  */
@@ -186,6 +189,14 @@ portBASE_TYPE xPortStartScheduler( void )
     _xt_isr_attach(INUM_SOFT, SV_ISR);
     _xt_isr_unmask(BIT(INUM_SOFT));
 
+    /* ENORMOUS HACK: Call the sdk_compat_initialise() function.
+       This can be removed happily once we have open source startup code.
+    */
+    if(!sdk_compat_initialised) {
+        sdk_compat_initialised = true;
+        sdk_compat_initialise();
+    }
+
     /* Initialize system tick timer interrupt and schedule the first tick. */
     sdk__xt_tick_timer_init();
 
diff --git a/common.mk b/common.mk
index 00a77dd..7361f70 100644
--- a/common.mk
+++ b/common.mk
@@ -220,13 +220,15 @@ $(1)_DEFAULT_ROOT := $(dir $(lastword $(MAKEFILE_LIST)))
 $(1)_ROOT ?= $$($(1)_DEFAULT_ROOT)
 $(1)_OBJ_DIR   = $(call lc,$(BUILD_DIR)$(1)/)
 ### determine source files and object files ###
-$(1)_SRC_FILES ?= $$(foreach sdir,$$($(1)_SRC_DIR), \
-			$$(wildcard $$(sdir)/*.c) $$(wildcard $$(sdir)/*.S)) \
+$(1)_SRC_FILES ?= $$(foreach sdir,$$($(1)_SRC_DIR), 				\
+			$$(wildcard $$(sdir)/*.c) $$(wildcard $$(sdir)/*.S) 	\
+			$$(wildcard $$(sdir)/*.cpp)) 				\
 			$$($(1)_EXTRA_SRC_FILES)
 $(1)_REAL_SRC_FILES = $$(foreach sfile,$$($(1)_SRC_FILES),$$(realpath $$(sfile)))
 $(1)_REAL_ROOT = $$(realpath $$($(1)_ROOT))
 # patsubst here substitutes real component root path for the relative OBJ_DIR path, making things short again
-$(1)_OBJ_FILES_C = $$(patsubst $$($(1)_REAL_ROOT)%.c,$$($(1)_OBJ_DIR)%.o,$$($(1)_REAL_SRC_FILES))
+$(1)_OBJ_FILES_CXX = $$(patsubst $$($(1)_REAL_ROOT)%.cpp,$$($(1)_OBJ_DIR)%.o,$$($(1)_REAL_SRC_FILES))
+$(1)_OBJ_FILES_C = $$(patsubst $$($(1)_REAL_ROOT)%.c,$$($(1)_OBJ_DIR)%.o,$$($(1)_OBJ_FILES_CXX))
 $(1)_OBJ_FILES = $$(patsubst $$($(1)_REAL_ROOT)%.S,$$($(1)_OBJ_DIR)%.o,$$($(1)_OBJ_FILES_C))
 # the last included makefile is our component's component.mk makefile (rebuild the component if it changes)
 $(1)_MAKEFILE ?= $(lastword $(MAKEFILE_LIST))
@@ -244,6 +246,12 @@ $$($(1)_OBJ_DIR)%.o: $$($(1)_REAL_ROOT)%.c $$($(1)_MAKEFILE) $(wildcard $(ROOT)*
 	$$($(1)_CC_ARGS) -c $$< -o $$@
 	$$($(1)_CC_ARGS) -MM -MT $$@ -MF $$(@:.o=.d) $$<
 
+$$($(1)_OBJ_DIR)%.o: $$($(1)_REAL_ROOT)%.cpp $$($(1)_MAKEFILE) $(wildcard $(ROOT)*.mk) | $$($(1)_SRC_DIR)
+	$(vecho) "C++ $$<"
+	$(Q) mkdir -p $$(dir $$@)
+	$$($(1)_CXX_ARGS) -c $$< -o $$@
+	$$($(1)_CXX_ARGS) -MM -MT $$@ -MF $$(@:.o=.d) $$<
+
 $$($(1)_OBJ_DIR)%.o: $$($(1)_REAL_ROOT)%.S $$($(1)_MAKEFILE) $(wildcard $(ROOT)*.mk) | $$($(1)_SRC_DIR)
 	$(vecho) "AS $$<"
 	$(Q) mkdir -p $$(dir $$@)
diff --git a/core/sdk_compat.c b/core/sdk_compat.c
index d141708..b6bb6b3 100644
--- a/core/sdk_compat.c
+++ b/core/sdk_compat.c
@@ -14,3 +14,18 @@ void IRAM *zalloc(size_t nbytes)
 {
     return calloc(1, nbytes);
 }
+
+extern void (*__init_array_start)(void);
+extern void (*__init_array_end)(void);
+
+/* Do things which should be done as part of the startup code, but aren't.
+
+   Can be replaced with _start() once we have open source startup code.
+*/
+void sdk_compat_initialise()
+{
+    /* Call C++ constructors or C functions marked with __attribute__((constructor)) */
+    void (**p)(void);
+    for ( p = &__init_array_start; p != &__init_array_end; ++p)
+        (*p)();
+}
diff --git a/examples/simple_cplusplus/Makefile b/examples/simple_cplusplus/Makefile
new file mode 100644
index 0000000..717b163
--- /dev/null
+++ b/examples/simple_cplusplus/Makefile
@@ -0,0 +1,3 @@
+# Simple makefile for simple example
+PROGRAM=simple
+include ../../common.mk
diff --git a/examples/simple_cplusplus/simple.cpp b/examples/simple_cplusplus/simple.cpp
new file mode 100644
index 0000000..dada441
--- /dev/null
+++ b/examples/simple_cplusplus/simple.cpp
@@ -0,0 +1,52 @@
+/* A very basic C++ example, really just proof of concept for C++
+
+   This sample code is in the public domain.
+ */
+#include "espressif/esp_common.h"
+#include "espressif/sdk_private.h"
+#include "FreeRTOS.h"
+#include "task.h"
+#include "queue.h"
+
+class Counter
+{
+private:
+  uint32_t _count;
+public:
+  Counter(uint32_t initial_count)
+  {
+    this->_count = initial_count;
+    printf("Counter initialised with count %ld\r\n", initial_count);
+  }
+
+  void Increment()
+  {
+    _count++;
+  }
+
+  uint32_t getCount()
+  {
+    return _count;
+  }
+};
+
+static Counter static_counter(99);
+
+void task1(void *pvParameters)
+{
+  Counter local_counter = Counter(12);
+  while(1) {
+    Counter &counter = rand() % 2 ? static_counter : local_counter;
+    counter.Increment();
+    printf("local counter %ld static counter %ld\r\n", local_counter.getCount(),
+	   static_counter.getCount());
+    vTaskDelay(100);
+  }
+}
+
+extern "C" void user_init(void)
+{
+    sdk_uart_div_modify(0, UART_CLK_FREQ / 115200);
+    printf("SDK version:%s\n", sdk_system_get_sdk_version());
+    xTaskCreate(task1, (signed char *)"tsk1", 256, NULL, 2, NULL);
+}