I don't understand libraries in C++ - Page 1
Forum section: electronics / general

author: ksoviero
author_rank: Contributor
timestamp: August 12, 2020, 10:45:50 pm
content: Complete beginner to C/C++ here...So, I'm trying to include the Arduino SoftwareSerial library in my otherwise vanilla AVR project (mostly out of laziness). However, when I build I get an "undefined reference" error.Here's the code in question:Code:#include <avr/io.h>#include <SoftwareSerial.h>#define DEBUG_TX PB0#define DEBUG_RX PB1SoftwareSerial debug = SoftwareSerial(DEBUG_RX, DEBUG_TX);int main() {return 0;}And here's the command I'm using to build and the output:Code:avr-g++ -Ilib -I/usr/share/arduino/libraries/SoftwareSerial -I/usr/share/arduino/hardware/arduino/cores/arduino -g -Os -mmcu=atmega328p -Wall *.cpp -o example.bin -DF_CPU=8000000L/tmp/ccsvCELp.o: In function `_GLOBAL__sub_I_debug':/home/ksoviero/VS Code/Example/main.cpp:8: undefined reference to `SoftwareSerial::SoftwareSerial(unsigned char, unsigned char, bool)'/tmp/ccsvCELp.o: In function `_GLOBAL__sub_D_debug':/home/ksoviero/VS Code/Example/main.cpp:(.text.exit+0x4): undefined reference to `SoftwareSerial::~SoftwareSerial()'collect2: error: ld returned 1 exit statusmake: *** [Makefile:13: build] Error 1So, without resorting to some premade tool like arduino-mk, how would I make this work manually? I want to learn how this works, not just make it work for the sake of working.Thanks!

author: langwadt
author_rank: Super Contributor
timestamp: August 12, 2020, 10:54:08 pm
content: https://www.arduino.cc/en/Reference/SoftwareSerial

author: ataradov
author_rank: Super Contributor
timestamp: August 12, 2020, 11:04:58 pm
content: You can't just 'g++' on a single file. You need to link a whole lot of libraries. So while you may not need a full Arduino Makefile, you still need something that will do this."#include" just includes a header file so that function prototypes and data structures are declared. This does not bring in the actual code.

author: AntiProtonBoy
author_rank: Frequent Contributor
timestamp: August 13, 2020, 02:04:52 am
content: Those look like linker errors. The linker is unable to locate the binary symbols for the SoftwareSerial constructor and destructor functions. Did you tell the linker where to locate the SoftwareSerial binary library?

author: westfw
author_rank: Super Contributor
timestamp: August 13, 2020, 02:19:32 am
content: Your compile command only compiles the C++ files in your/home/ksoviero/VS Code/Example/directory. You also need to compile the SoftwareSerial library (whatever source files it uses), and link the results in with your source objects.Also, you have bit numbers (PB0, PB1) where you need Arduino Board Pin Numbers.It might be easiest to just copy SoftwareSerial.cpp into your source directory.(This is something that the Arduino IDE does with it's "handholding" - given an "#include <libraryname>" and some databased of installed libraries, the IDE will find the source, compile it, and link in the binaries. Step into bare C, and you have to do that all yourself...)

author: fanOfeeDIY
author_rank: Unknown
timestamp: August 14, 2020, 01:11:36 pm
content: Quote from: ksoviero on August 12, 2020, 10:45:50 pmI am also new to Arduino, so this may not the right way but I was able to build your code with no error.I just tired copying your code tomain.cmain.cpp withCode:#vi main.cppand then create a Makefile withCode:BOARD_TAG ?= pro328TARGET = atmega328CFLAGS += -g -WallMONITOR_PORT ?= (for your machine port)ARDUINO_LIBS ?= SoftwareSerialinclude /usr/share/arduino/Arduino.mkand then typeCode:# makethen it will generates binaries under `build-pro328` directory from your main.cpp.

author: fanOfeeDIY
author_rank: Unknown
timestamp: August 14, 2020, 01:15:55 pm
content: I tried them after on Ubuntu with installingCode:# sudo apt install arduino arduino-mk

author: fanOfeeDIY
author_rank: Unknown
timestamp: August 14, 2020, 04:08:30 pm
content: Quote from: ksoviero on August 12, 2020, 10:45:50 pmI missed reading this sentence, let me try build your code without using arduino-mk.

author: fanOfeeDIY
author_rank: Unknown
timestamp: August 15, 2020, 03:45:25 am
content: I was able build using the libcore.a from the above procedure.I am writing while I am also learning how the Arduino build works.This is the Makefile I created:Code:AVR_FREQ ?= 8000000LMCU ?= atmega328pCC = avr-gccCXX = avr-g++OBJCOPY = avr-objcopyCFLAGS += -g -Wall -DF_CPU=$(AVR_FREQ) -mmcu=$(MCU)MONITOR_PORT ?= `echo your serial port on pc`ARDUINO_LIBS ?= SoftwareSerialINCLUDES := -I/usr/share/arduino/libraries/SoftwareSerial \-I/usr/share/arduino/hardware/arduino/cores/arduino \-I/usr/share/arduino/hardware/arduino/variants/standardSRC := main.cppTARGET := exampleBIN := $(TARGET).binHEX := $(TARGET).hexall: $(BIN) $(HEX)$(BIN): $(SRC)$(CXX) $(INCLUDES) $(CFLAGS) $^ build-pro328/libcore.a -o $@$(HEX): $(BIN)$(OBJCOPY) -O ihex $< $@upload: $(HEX)avrdude -v -c arduino -p $(MCU) -P $(MONITOR_PORT) -b 115200 -U flash:w:$<clean:rm -f $(BIN) $(HEX)Then type:Code:$ makewill generate example.bin and example.hex.Two things I felft burdon for this manual makefile method.First is that you have to add the path to include for libraries you use, such as/usr/share/arduino/libraries/SoftwareSerialto INCLUDE everytime for using other libraries. Those libraries are in /usr/share/arduino/libraries.Second, building libcore.a without arduino-mk do now look easy.But let me investigte later.Would this help?

author: fanOfeeDIY
author_rank: Unknown
timestamp: August 15, 2020, 04:32:58 am
content: I got them to build together with libcore.a<- not working, user my later comment. it was still using build-pro328/libcore.a which was built before with arduino-mkThe Makefile became bloated.Code:AVR_FREQ ?= 8000000LMCU ?= atmega328pCC = avr-gccCXX = avr-g++OBJCOPY = avr-objcopyAR = avr-arCFLAGS += -g -Wall -DF_CPU=$(AVR_FREQ) -mmcu=$(MCU)MONITOR_PORT ?= `echo your serial port on pc`ARDUINO_LIBS ?= SoftwareSerialINCLUDES := -I/usr/share/arduino/libraries/SoftwareSerial \-I/usr/share/arduino/hardware/arduino/cores/arduino \-I/usr/share/arduino/hardware/arduino/variants/standardSRC := main.cppTARGET := exampleBIN := $(TARGET).binHEX := $(TARGET).hexARDUINO_DIR = /usr/share/arduinoARDUINO_CORE_PATH = /usr/share/arduino/hardware/arduino/cores/arduinoARDUINO_VAR_PATH = /usr/share/arduino/hardware/arduino/variantsVARIANT = standardOBJDIR = .CORE_LIB = $(OBJDIR)/libcore.aall: $(BIN) $(HEX)$(BIN): $(SRC) $(CORE_LIB)$(CXX) $(INCLUDES) $(CFLAGS) $^ build-pro328/libcore.a -o $@$(HEX): $(BIN)$(OBJCOPY) -O ihex $< $@upload: $(HEX)avrdude -v -c arduino -p $(MCU) -P $(MONITOR_PORT) -b 115200 -U flash:w:$<clean:rm -f $(BIN) $(HEX)## Bellow here is to build libcore.aSYS_INCLUDES := $(foreach lib, $(SYS_LIBS), $(call get_library_includes,$(lib)))USER_INCLUDES := $(foreach lib, $(USER_LIBS), $(call get_library_includes,$(lib)))PLATFORM_INCLUDES := $(foreach lib, $(PLATFORM_LIBS), $(call get_library_includes,$(lib)))CPPFLAGS += -mmcu=$(MCU) -DF_CPU=$(AVR_FREQ) -D__PROG_TYPES_COMPAT__ \-I$(ARDUINO_CORE_PATH) -I$(ARDUINO_VAR_PATH)/$(VARIANT) \$(SYS_INCLUDES) $(PLATFORM_INCLUDES) $(USER_INCLUDES) -Wall -ffunction-sections \-fdata-sectionsCORE_C_SRCS = $(wildcard $(ARDUINO_CORE_PATH)/*.c)CORE_C_SRCS += $(wildcard $(ARDUINO_CORE_PATH)/avr-libc/*.c)CORE_CPP_SRCS = $(wildcard $(ARDUINO_CORE_PATH)/*.cpp)CORE_AS_SRCS = $(wildcard $(ARDUINO_CORE_PATH)/*.S)CORE_OBJ_FILES = $(CORE_C_SRCS:.c=.c.o) $(CORE_CPP_SRCS:.cpp=.cpp.o) $(CORE_AS_SRCS:.S=.S.o)MKDIR = mkdir -p$(OBJDIR)/core/%.c.o: $(ARDUINO_CORE_PATH)/%.c $(COMMON_DEPS) | $(OBJDIR)@$(MKDIR) $(dir $@)$(CC) -MMD -c $(CPPFLAGS) $(CFLAGS) $< -o $@$(OBJDIR)/core/%.cpp.o: $(ARDUINO_CORE_PATH)/%.cpp $(COMMON_DEPS) | $(OBJDIR)@$(MKDIR) $(dir $@)$(CXX) -MMD -c $(CPPFLAGS) $(CXXFLAGS) $< -o $@$(OBJDIR)/core/%.S.o: $(ARDUINO_CORE_PATH)/%.S $(COMMON_DEPS) | $(OBJDIR)@$(MKDIR) $(dir $@)$(CC) -MMD -c $(CPPFLAGS) $(ASFLAGS) $< -o $@CORE_OBJS = $(patsubst $(ARDUINO_CORE_PATH)/%, $(OBJDIR)/core/%,$(CORE_OBJ_FILES))LIB_OBJS = $(patsubst $(ARDUINO_LIB_PATH)/%.c,$(OBJDIR)/libs/%.c.o,$(LIB_C_SRCS)) \$(patsubst $(ARDUINO_LIB_PATH)/%.cpp,$(OBJDIR)/libs/%.cpp.o,$(LIB_CPP_SRCS)) \$(patsubst $(ARDUINO_LIB_PATH)/%.S,$(OBJDIR)/libs/%.S.o,$(LIB_AS_SRCS))USER_LIB_OBJS = $(patsubst $(USER_LIB_PATH)/%.cpp,$(OBJDIR)/userlibs/%.cpp.o,$(USER_LIB_CPP_SRCS)) \$(patsubst $(USER_LIB_PATH)/%.c,$(OBJDIR)/userlibs/%.c.o,$(USER_LIB_C_SRCS)) \$(patsubst $(USER_LIB_PATH)/%.S,$(OBJDIR)/userlibs/%.S.o,$(USER_LIB_AS_SRCS))PLATFORM_LIB_OBJS := $(patsubst $(ARDUINO_PLATFORM_LIB_PATH)/%.cpp,$(OBJDIR)/platformlibs/%.cpp.o,$(PLATFORM_LIB_CPP_SRCS)) \$(patsubst $(ARDUINO_PLATFORM_LIB_PATH)/%.c,$(OBJDIR)/platformlibs/%.c.o,$(PLATFORM_LIB_C_SRCS)) \$(patsubst $(ARDUINO_PLATFORM_LIB_PATH)/%.S,$(OBJDIR)/platformlibs/%.S.o,$(PLATFORM_LIB_AS_SRCS))$(CORE_LIB): $(CORE_OBJS) $(LIB_OBJS) $(PLATFORM_LIB_OBJS) $(USER_LIB_OBJS)$(AR) rcs $@ $(CORE_OBJS) $(LIB_OBJS) $(PLATFORM_LIB_OBJS) $(USER_LIB_OBJS)Then it will have:Code:$ lscore example.bin example.hex libcore.a main.cpp Makefile

author: fanOfeeDIY
author_rank: Unknown
timestamp: August 15, 2020, 04:39:35 am
content: I will stop here working this. It was my fun for my summer holiday.Probably good to optimize CFLAGS and CPPFLAGS in Makefile.Would like to know `make upload` would work or not.

author: fanOfeeDIY
author_rank: Unknown
timestamp: August 15, 2020, 06:47:37 am
content: This is the fixed Makefile for building without arduino-mk.However, how to build libcore.a manually is highly reverse engineered from /usr/share/arduino/Arduino.mk.Please convert the spaces to tab of indentation inside the Makefile when copying bellow.Code:SHELL = /bin/bash -xueAVR_FREQ ?= 8000000LMCU ?= atmega328pCC = avr-gccCXX = avr-g++OBJCOPY = avr-objcopyAR = avr-gcc-arCFLAGS += -g -Wall -DF_CPU=$(AVR_FREQ) -mmcu=$(MCU)MONITOR_PORT ?= `echo your serial port on pc`ARDUINO_LIBS ?= SoftwareSerialINCLUDES := -I/usr/share/arduino/libraries/SoftwareSerial \-I/usr/share/arduino/hardware/arduino/cores/arduino \-I/usr/share/arduino/hardware/arduino/variants/standardSRC := main.cppTARGET := exampleBIN := $(TARGET).binHEX := $(TARGET).hexARDUINO_DIR = /usr/share/arduinoARDUINO_CORE_PATH = /usr/share/arduino/hardware/arduino/cores/arduinoARDUINO_VAR_PATH = /usr/share/arduino/hardware/arduino/variantsVARIANT = standardOBJDIR = .CORE_LIB = $(OBJDIR)/libcore.aall: $(CORE_LIB) $(BIN) $(HEX)$(BIN): $(SRC) $(CORE_LIB)$(CXX) $(INCLUDES) $(CFLAGS) $^ -o $@$(HEX): $(BIN)$(OBJCOPY) -O ihex $< $@upload: $(HEX)avrdude -v -c arduino -p $(MCU) -P $(MONITOR_PORT) -b 115200 -U flash:w:$<clean:rm -fr $(BIN) $(HEX) $(CORE_LIB) $(OBJDIR)/core $(OBJDIR)/libs $(OBJDIR)/platformlibs## Bellow here is to build libcore.aCFLAGS_STD = -std=gnu11 -flto -fno-fat-lto-objectsCXXFLAGS_STD = -fpermissive -fno-exceptions -std=gnu++11 -fno-threadsafe-statics -fltoLOCAL_C_SRCS ?= $(wildcard *.c)LOCAL_CPP_SRCS ?= $(wildcard *.cpp)LOCAL_CC_SRCS ?= $(wildcard *.cc)LOCAL_PDE_SRCS ?= $(wildcard *.pde)LOCAL_INO_SRCS ?= $(wildcard *.ino)LOCAL_AS_SRCS ?= $(wildcard *.S)LOCAL_SRCS = $(LOCAL_C_SRCS) $(LOCAL_CPP_SRCS) \$(LOCAL_CC_SRCS) $(LOCAL_PDE_SRCS) \$(LOCAL_INO_SRCS) $(LOCAL_AS_SRCS)ARDUINO_LIB_PATH = $(ARDUINO_DIR)/librariesARDUINO_LIBS += $(filter $(notdir $(wildcard $(ARDUINO_DIR)/libraries/*)), \$(shell sed -ne 's/^ *\# *include *[<\"]\(.*\)\.h[>\"]/\1/p' $(LOCAL_SRCS)))USER_LIBS := $(sort $(wildcard $(patsubst %,$(USER_LIB_PATH)/%,$(ARDUINO_LIBS))))USER_LIB_NAMES := $(patsubst $(USER_LIB_PATH)/%,%,$(USER_LIBS))SYS_LIBS := $(sort $(wildcard $(patsubst %,$(ARDUINO_LIB_PATH)/%,$(filter-out $(USER_LIB_NAMES),$(ARDUINO_LIBS)))))SYS_LIB_NAMES := $(patsubst $(ARDUINO_LIB_PATH)/%,%,$(SYS_LIBS))get_library_includes = $(if $(and $(wildcard $(1)/src), $(wildcard $(1)/library.properties)), \-I$(1)/src, \$(addprefix -I,$(1) $(wildcard $(1)/utility)))SYS_INCLUDES := $(foreach lib, $(SYS_LIBS), $(call get_library_includes,$(lib)))USER_INCLUDES := $(foreach lib, $(USER_LIBS), $(call get_library_includes,$(lib)))CPPFLAGS += -mmcu=$(MCU) -DF_CPU=$(AVR_FREQ) -D__PROG_TYPES_COMPAT__ \-I$(ARDUINO_CORE_PATH) -I$(ARDUINO_VAR_PATH)/$(VARIANT) \$(SYS_INCLUDES) $(PLATFORM_INCLUDES) $(USER_INCLUDES) -Wall -ffunction-sections \-fdata-sections -Os -fpermissive -fno-exceptions -std=gnu++11 -fno-threadsafe-statics -fltoCORE_C_SRCS = $(wildcard $(ARDUINO_CORE_PATH)/*.c)CORE_C_SRCS += $(wildcard $(ARDUINO_CORE_PATH)/avr-libc/*.c)CORE_CPP_SRCS = $(wildcard $(ARDUINO_CORE_PATH)/*.cpp)CORE_AS_SRCS = $(wildcard $(ARDUINO_CORE_PATH)/*.S)CORE_OBJ_FILES = $(CORE_C_SRCS:.c=.c.o) $(CORE_CPP_SRCS:.cpp=.cpp.o) $(CORE_AS_SRCS:.S=.S.o)MKDIR = mkdir -p$(OBJDIR)/core/%.c.o: $(ARDUINO_CORE_PATH)/%.c $(COMMON_DEPS) | $(OBJDIR)@$(MKDIR) $(dir $@)$(CC) -MMD -c $(CPPFLAGS) $(CFLAGS_STD) $< -o $@$(OBJDIR)/core/%.cpp.o: $(ARDUINO_CORE_PATH)/%.cpp $(COMMON_DEPS) | $(OBJDIR)@$(MKDIR) $(dir $@)$(CXX) -MMD -c $(CPPFLAGS) $(CXXFLAGS_STD) $< -o $@$(OBJDIR)/core/%.S.o: $(ARDUINO_CORE_PATH)/%.S $(COMMON_DEPS) | $(OBJDIR)@$(MKDIR) $(dir $@)$(CC) -MMD -c $(CPPFLAGS) $(ASFLAGS) $< -o $@CORE_OBJS = $(patsubst $(ARDUINO_CORE_PATH)/%, $(OBJDIR)/core/%,$(CORE_OBJ_FILES))get_library_files = $(if $(and $(wildcard $(1)/src), $(wildcard $(1)/library.properties)), \$(call rwildcard,$(1)/src/,*.$(2)), \$(wildcard $(1)/*.$(2) $(1)/utility/*.$(2)))LIB_C_SRCS := $(foreach lib, $(SYS_LIBS), $(call get_library_files,$(lib),c))LIB_CPP_SRCS := $(foreach lib, $(SYS_LIBS), $(call get_library_files,$(lib),cpp))LIB_AS_SRCS := $(foreach lib, $(SYS_LIBS), $(call get_library_files,$(lib),S))USER_LIB_CPP_SRCS := $(foreach lib, $(USER_LIBS), $(call get_library_files,$(lib),cpp))USER_LIB_C_SRCS := $(foreach lib, $(USER_LIBS), $(call get_library_files,$(lib),c))USER_LIB_AS_SRCS := $(foreach lib, $(USER_LIBS), $(call get_library_files,$(lib),S))LIB_OBJS = $(patsubst $(ARDUINO_LIB_PATH)/%.c,$(OBJDIR)/libs/%.c.o,$(LIB_C_SRCS)) \$(patsubst $(ARDUINO_LIB_PATH)/%.cpp,$(OBJDIR)/libs/%.cpp.o,$(LIB_CPP_SRCS)) \$(patsubst $(ARDUINO_LIB_PATH)/%.S,$(OBJDIR)/libs/%.S.o,$(LIB_AS_SRCS))$(OBJDIR)/libs/%.c.o: $(ARDUINO_LIB_PATH)/%.c@$(MKDIR) $(dir $@)$(CC) -MMD -c $(CPPFLAGS) $(CFLAGS_STD) $< -o $@$(OBJDIR)/libs/%.cpp.o: $(ARDUINO_LIB_PATH)/%.cpp@$(MKDIR) $(dir $@)$(CXX) -MMD -c $(CPPFLAGS) $(CXXFLAGS_STD) $< -o $@$(OBJDIR)/libs/%.S.o: $(ARDUINO_LIB_PATH)/%.S@$(MKDIR) $(dir $@)$(CC) -MMD -c $(CPPFLAGS) $(ASFLAGS) $< -o $@$(CORE_LIB): $(CORE_OBJS) $(LIB_OBJS) $(PLATFORM_LIB_OBJS)$(AR) rcs $@ $(CORE_OBJS) $(LIB_OBJS) $(PLATFORM_LIB_OBJS) $(USER_LIB_OBJS)After the build, it should have these files.Code:$ lscore example.hex libs Makefile example.bin libcore.a main.cpp

author: fanOfeeDIY
author_rank: Unknown
timestamp: August 15, 2020, 07:02:45 am
content: Probably Arduino was not a good example of how the linking works on gcc/g++, since it uses customized libcore to build.I have not run the binary on Arduino.Happy to hear if anybody tries the Makehile.

author: fanOfeeDIY
author_rank: Unknown
timestamp: August 21, 2020, 12:44:45 pm
content: Hi,After three days of ongoing updates on the Makefile, I end up making a project of simple Makefiles for Arduino on AVR and STM32.https://github.com/mcd500/arduino-commandliners/blob/master/README.mdThanks, for the question in the forum!