Trying to make / use a stand-alone library - test not working

Hi all,

I’m testing a “library” (simple for now just to see if it will work). I’m compiling using a Makefile and avr-gcc. I can’t seem to make it work, but I think I’m close and I need some ideas.

Here’s the code I’m working with:

The main program “test.cpp”

#include "LIB.h" // include the "library"

static LIB OBJ; // instantiate lib

int main (void)
{
    uint8_t n;

    DDRB |= _BV(7); // led pin as output

    // pass "5" through the "library" function
    n = OBJ.count (5);

    while (n--) {
        PORTB |= _BV(7); // led on
        __builtin_avr_delay_cycles(8000000UL); // 500 msec
        PORTB &= ~_BV(7); // led off
        __builtin_avr_delay_cycles(8000000UL); // 500 msec
    }

    while (1); // hang here
}

The “library” “LIB.cpp”:

// LIB.cpp

#include "LIB.h"

uint8_t LIB::count (uint8_t inp)
{
    return inp;
}

The header file for the library “LIB.h”:

// LIB.h

#ifndef LIB_H
#define LIB_H

#include <avr/interrupt.h> // for PORTX, inttypes, etc..

class LIB
{
    public:
        uint8_t count (uint8_t);
};

#endif // #ifndef LIB_H

When I try to compile it, I get this error:

~~~
test.o: In function main': /usr/share/arduino-1.0.6/sketches/test/test.cpp:13: undefined reference to LIB::count(unsigned char)’
collect2: error: ld returned 1 exit status
make: *** [test.elf] Error 1

~~~
Here’s the Makefile in case you want to see it (although I’m sure the problem is not here - since it works without “libraries”):

## avr part
PART = atmega2560

## fuse settings
EFUSE  = 0xFF
HFUSE  = 0xDF
LFUSE  = 0xF7

############## project specific
F_CPU = 16000000UL
TARGET = $(notdir $(CURDIR))
CODEADDR = 0x0000
OPT = O3

#uncomment if floating point is needed
#FLOAT_SUPPORT = -Wl,-u,vfprintf,-lprintf_flt
#FLOAT_SUPPORT = -Wl,-u,vfscanf,-lscanf_flt

################### no changes beyond this point ################

CPPFLAGS += -Wall
CPPFLAGS += -ffunction-sections
CPPFLAGS += -fdata-sections
CPPFLAGS += -g
CPPFLAGS += -$(OPT)
CPPFLAGS += -mmcu=$(PART)
CPPFLAGS += -DF_CPU=$(F_CPU)
CPPFLAGS += -MMD
CPPFLAGS += -MP

LINKFLAGS += -mmcu=$(PART)
LINKFLAGS += $(FLOAT_SUPPORT)
LINKFLAGS += -Wl,--gc-sections
LINKFLAGS += -Wl,--relax
LINKFLAGS += -Wl,--section-start=.text=$(CODEADDR)

AVR_ERASE += $(AVRDUDE)
AVR_ERASE += -p $(PART)
AVR_ERASE += -b 115200
AVR_ERASE += -c avrispmkii
AVR_ERASE += -P usb
AVR_ERASE += -B 8.0
AVR_ERASE += -e
AVR_ERASE += -qq

AVRDUDE_CMD += $(AVR_ERASE)
AVRDUDE_CMD += -s
AVRDUDE_CMD += -U lfuse:w:$(LFUSE):m
AVRDUDE_CMD += -U hfuse:w:$(HFUSE):m
AVRDUDE_CMD += -U efuse:w:$(EFUSE):m
AVRDUDE_CMD += -U flash:w:$(TARGET).hex:i
AVRDUDE_CMD += -U eeprom:w:$(TARGET).eep:i

CC = avr-gcc
CXX = avr-g++
LD = avr-ld
AR = avr-ar
OBJCOPY = avr-objcopy
OBJDUMP = avr-objdump
NM = avr-nm
AVRSTRIP = avr-strip
AVRSIZE = avr-size
AVRDUDE = avrdude

# default rule
.DEFAULT_GOAL = target

#_______________________________________________________________________________
# BUILD RULES

all: target erase upload

target: $(TARGET).o $(TARGET).elf $(TARGET).hex $(TARGET).eep $(TARGET).lst
	@echo "Build of $(TARGET) complete."

erase:
	@$(AVR_ERASE)
	@echo "$(PART) erased."

upload:
	@$(AVRDUDE_CMD)
	@echo "$(TARGET) uploaded to $(PART)."

clean:
	@rm -f $(TARGET).d
	@rm -f $(TARGET).eep
	@rm -f $(TARGET).elf
	@rm -f $(TARGET).hex
	@rm -f $(TARGET).lst
	@rm -f $(TARGET).o

#_______________________________________________________________________________
# BUILD THE TARGET

%.o: %.cpp
	@$(COMPILE.cpp) -o $@ $<
	@$(AVRSIZE) $@
	@echo

%.o: %.c
	@$(COMPILE.c) -o $@ $<
	@$(AVRSIZE) $@
	@echo

%.elf: %.o
	@$(CC) $(LINKFLAGS) -o $@ $<
	@$(AVRSIZE) $@
	@echo

%.hex: %.elf
	@$(OBJCOPY) \
	-O ihex \
	-R .eeprom \
	$< $@
	@$(AVRSIZE) $@
	@echo

%.eep: %.elf
	@$(OBJCOPY) \
	-O ihex \
	-j .eeprom \
	--set-section-flags=.eeprom=alloc,load \
	--no-change-warnings \
	--change-section-lma .eeprom=0 \
	$< $@
	@$(AVRSIZE) $@
	@echo

%.lst: %.elf
	@$(OBJDUMP) \
	--demangle \
	--disassemble \
	--source \
	--wide \
	$< > $@

Any input will be greatly appreciated.

You haven't defined 'count()' in the LIB.cpp file, only 'init()', (which isn't declared in the header file). A simple typo?

OldSteve: You haven't defined 'count()' in the LIB.cpp file, only 'init()'.

You caught my editing mistake (which unfortunately I uploaded).

In desperation, I was thinking maybe "init" was a reserved work, so I changed it to "count" (still didn't work).

I'll correct my original post, and thanks for catching it.

Funny thing is, it compiles the .o object file, but then it won't link. Here's the whole command line with error:

~~~ **root@michael:/usr/share/arduino-1.0.6/sketches/test# make    text    data     bss     dec     hex filename      62       0       1      63      3f test.o

test.o: In function main': /usr/share/arduino-1.0.6/sketches/test/test.cpp:13: undefined reference toLIB::count(unsigned char)' collect2: error: ld returned 1 exit status make: *** [test.elf] Error 1** ~~~

I can only compile in the Arduino IDE, but once I changed everything to ‘init’, I still had to add this to the top of the “LIB.cpp” file:-

#include <avr/interrupt.h> // for PORTX, inttypes, etc..

and then it compiled fine for me. (‘uint8_t’ wasn’t found without that addition.)

I don’t know if this helps, though, since I’m using the Arduino IDE. (I also had to change “test.cpp” to “test.ino”, of course.)

Edit: Sorry, I can’t help with the Makefile-related stuff.

OldSteve:
I can only compile in the Arduino IDE, but once I changed everything to ‘init’, I still had to add this to the top of the “LIB.cpp” file:-

#include <avr/interrupt.h> // for PORTX, inttypes, etc..

and then it compiled fine for me. (‘uint8_t’ wasn’t found without that addition.)

I don’t know if this helps, though, since I’m using the Arduino IDE. (I also had to change “test.cpp” to “test.ino”, of course.)

Edit: Sorry, I can’t help with the Makefile-related stuff.

It’s really not a “Makefile” problem (I don’t think). For some reason, the function in the “library” isn’t being “seen” by the compiler (or linker?) Ugh… always a learning curve.

OldSteve:
I can only compile in the Arduino IDE, but once I changed everything to ‘init’, I still had to add this to the top of the “LIB.cpp” file:-

#include <avr/interrupt.h> // for PORTX, inttypes, etc..

and then it compiled fine for me. (‘uint8_t’ wasn’t found without that addition.)

I don’t know if this helps, though, since I’m using the Arduino IDE. (I also had to change “test.cpp” to “test.ino”, of course.)

Edit: Sorry, I can’t help with the Makefile-related stuff.

I found the problem and indeed it WAS my Makefile.

The Makefile used the directory name to generate the target string (i.e. if in the “test” directory, the source was test.cpp, the object file was test.o and the AVRDUDE files were test.hex and test.epp).

So, the compile never pulled in the “LIB.cpp” file, which is why it’s contents could not be found.

I changed the Makefile to recursively build all source files in the directory and now it works just fine.

I found out the problem by “unhiding” the compiler lines (i.e. removed the preceding “@” character) and looked at the compile string it was generating.

I saw it doing test.cpp → test.o, but nowhere was LIB.cpp seen.

So I copied and pasted the same compiler line directly in, but added the LIB.cpp name to it and taa-daa, it worked!

Just when I think I have things figured out, I discover yet more stuff that I don’t know.

I knew the learning curve was steep, but I never knew that it went on infinitely!

Anyway, thanks for the help. Your mention of the Makefile possibly being the problem made me look at it more closely (when before I thought it was just fine).

Thanks again, and karma++ for you.

(edit to add): Here’s what it’s doing now… and it works fine:

[b]root@michael:/usr/share/arduino-1.0.6/sketches/test# make
avr-gcc LIB.cpp test.cpp -Wall -ffunction-sections -fdata-sections -g -O3 -mmcu=atmega2560 -DF_CPU=16000000UL -MMD -MP -mmcu=atmega2560  -Wl,--gc-sections -Wl,--relax -Wl,--section-start=.text=0x0000 -o test.elf
   text    data     bss     dec     hex filename
    398       0       1     399     18f test.elf

   text    data     bss     dec     hex filename
      0     394       0     394     18a test.hex

   text    data     bss     dec     hex filename
      0       4       0       4       4 test.eep

Build of test complete.[/b]

Krupski: I found the problem and indeed it WAS my Makefile.

Excellent. I'm glad that you got it sorted out. This sort of problem is frustrating.

Thanks again, and karma++ for you.

Thank you for that. :)