Size difference in toolchain binary

Hey Arduino Community!

I am using an Arduino to compete in the Trinity College Robotics Competition. I am more familiar in standard C/C++ than wiring, so I decided to try my luck at installing and configuring the toolchain with the makefile. After an hour or so of modifying the Makefile to work with version 13 of the arduino software, I am using what is attached below.
It all works, and I can successfully run C++ apps on the arduino compiled through it. There is one... oddity, however. It seems as if the hex files created using my makefile are about 6x larger than the hex files created by the official Arduino IDE. I tested this out by creating a simple "sketch" in arduino IDE, then compiling the .cpp version in my toolchain. The exact same program compiled to a hex 6x larger than in the IDE. I am sure I just gave the compiler some incorrect flags, but I do not have enough experience to know which flags these are.

Thanks in advance for your help!

Output form loading on Arduino IDE

Binary sketch size: 1416 bytes (of a 14336 byte maximum

# ls -l
# 4002 Feb 18 16:58 sketch_090218a.hex

Output from loading through toolchain

avrdude: 8320 bytes of flash written

# ls -l
# 23426 Feb 18 17:24 main.hex

The Sketch:

void setup(){
  Serial.begin(9600);
}

void loop(){
   Serial.println("test");
   delay(1000);
}

The "identical" CPP code:

#include "WProgram.h"

void setup(){
  Serial.begin(9600);
}

void loop(){
  Serial.println("test");
  delay(1000);
}

int main(void){
  init();
  setup();
  for(;;)
    loop();
  return 0;
}

The Makefile:

# Arduino Makefile modified by Jordan Perr
# Original from Arduino.cc
#
# To Use this Makefile:
#       -Copy this makefile into your project's directory
#            -set BUILD_TARGETS all of your project's files to BUILD_TARGETS
#            -set TARGET to your main target file's name. (eg. main for main.cpp)
#            -set INSTALL_DIR to the location of your arduino installation
#            -set PORT to the serial port you wish to download to. Use * as a wildcard
#            -set UPLAOD_RATE to the baudrate of your Arduino
#
#      make
#            -builds into current directory, creating a TARGET.hex binary
#
# make upload
#            -uses avrdude to download TARGET.hex to PORT
#
#      make clean
#            -removes all temp and binary files created during build
#
#
#

TARGET = main
BUILD_TARGETS = main.cpp
INSTALL_DIR = /Applications/arduino-0013
PORT = /dev/tty.usb*
UPLOAD_RATE = 19200
AVRDUDE_PROGRAMMER = stk500
MCU = atmega168
F_CPU = 16000000


###############################
##      don't go below this line ##
###############################


ARDUINO = $(INSTALL_DIR)/hardware/cores/arduino
AVR_TOOLS_PATH = $(INSTALL_DIR)/hardware/tools/avr/bin
SRC =  $(ARDUINO)/pins_arduino.c $(ARDUINO)/wiring.c \
$(ARDUINO)/wiring_analog.c $(ARDUINO)/wiring_digital.c \
$(ARDUINO)/wiring_pulse.c $(ARDUINO)/wiring_serial.c \
$(ARDUINO)/wiring_shift.c $(ARDUINO)/WInterrupts.c
CXXSRC = $(BUILD_TARGETS) $(ARDUINO)/HardwareSerial.cpp $(ARDUINO)/WMath.cpp $(ARDUINO)/Print.cpp
FORMAT = ihex

# Name of this Makefile (used for "make depend").
MAKEFILE = Makefile

# Debugging format.
# Native formats for AVR-GCC's -g are stabs [default], or dwarf-2.
# AVR (extended) COFF requires stabs, plus an avr-objcopy run.
DEBUG = stabs

OPT = s

# Place -D or -U options here
CDEFS = -DF_CPU=$(F_CPU)
CXXDEFS = -DF_CPU=$(F_CPU)

# Place -I options here
CINCS = -I$(ARDUINO)
CXXINCS = -I$(ARDUINO)

# Compiler flag to set the C Standard level.
# c89   - "ANSI" C
# gnu89 - c89 plus GCC extensions
# c99   - ISO C99 standard (not yet fully implemented)
# gnu99 - c99 plus GCC extensions
CSTANDARD = -std=gnu99
CDEBUG = -g$(DEBUG)
CWARN = -Wall -Wstrict-prototypes
CTUNING = -funsigned-char -funsigned-bitfields -fpack-struct -fshort-enums
#CEXTRA = -Wa,-adhlns=$(<:.c=.lst)

CFLAGS = $(CDEBUG) $(CDEFS) $(CINCS) -O$(OPT) $(CWARN) $(CSTANDARD) $(CEXTRA)
CXXFLAGS = $(CDEFS) $(CINCS) -O$(OPT)
#ASFLAGS = -Wa,-adhlns=$(<:.S=.lst),-gstabs 
LDFLAGS = 


# Programming support using avrdude. Settings and variables.
AVRDUDE_PORT = $(PORT)
AVRDUDE_WRITE_FLASH = -U flash:w:$(TARGET).hex
AVRDUDE_FLAGS = -V -F \
-p $(MCU) -P $(AVRDUDE_PORT) -c $(AVRDUDE_PROGRAMMER) \
-b $(UPLOAD_RATE)


# Program settings
CC = avr-gcc
CXX = avr-gcc
OBJCOPY = avr-objcopy
OBJDUMP = avr-objdump
SIZE = avr-size
NM = avr-nm
AVRDUDE = avrdude
REMOVE = rm -f
MV = mv -f

# Define all object files.
OBJ = $(SRC:.c=.o) $(CXXSRC:.cpp=.o) $(ASRC:.S=.o)

# Define all listing files.
LST = $(ASRC:.S=.lst) $(CXXSRC:.cpp=.lst) $(SRC:.c=.lst)

# Combine all necessary flags and optional flags.
# Add target processor to flags.
ALL_CFLAGS = -mmcu=$(MCU) -I. $(CFLAGS)
ALL_CXXFLAGS = -mmcu=$(MCU) -I. $(CXXFLAGS)
ALL_ASFLAGS = -mmcu=$(MCU) -I. -x assembler-with-cpp $(ASFLAGS)


# Default target.
all: build

build: elf hex

elf: $(TARGET).elf
hex: $(TARGET).hex
eep: $(TARGET).eep
lss: $(TARGET).lss 
sym: $(TARGET).sym

# Program the device.  
upload: $(TARGET).hex
      $(AVRDUDE) $(AVRDUDE_FLAGS) $(AVRDUDE_WRITE_FLASH)




# Convert ELF to COFF for use in debugging / simulating in AVR Studio or VMLAB.
COFFCONVERT=$(OBJCOPY) --debugging \
--change-section-address .data-0x800000 \
--change-section-address .bss-0x800000 \
--change-section-address .noinit-0x800000 \
--change-section-address .eeprom-0x810000 


coff: $(TARGET).elf
      $(COFFCONVERT) -O coff-avr $(TARGET).elf $(TARGET).cof


extcoff: $(TARGET).elf
      $(COFFCONVERT) -O coff-ext-avr $(TARGET).elf $(TARGET).cof


.SUFFIXES: .elf .hex .eep .lss .sym

.elf.hex:
      $(OBJCOPY) -O $(FORMAT) -R .eeprom $< $@

.elf.eep:
      -$(OBJCOPY) -j .eeprom --set-section-flags=.eeprom="alloc,load" \
      --change-section-lma .eeprom=0 -O $(FORMAT) $< $@

# Create extended listing file from ELF output file.
.elf.lss:
      $(OBJDUMP) -h -S $< > $@

# Create a symbol table from ELF output file.
.elf.sym:
      $(NM) -n $< > $@



# Link: create ELF output file from object files.
$(TARGET).elf: $(OBJ)
      $(CC) $(ALL_CFLAGS) $(OBJ) --output $@ $(LDFLAGS)


# Compile: create object files from C++ source files.
.cpp.o:
      $(CXX) -c $(ALL_CXXFLAGS) $< -o $@ 

# Compile: create object files from C source files.
.c.o:
      $(CC) -c $(ALL_CFLAGS) $< -o $@ 


# Compile: create assembler files from C source files.
.c.s:
      $(CC) -S $(ALL_CFLAGS) $< -o $@


# Assemble: create object files from assembler source files.
.S.o:
      $(CC) -c $(ALL_ASFLAGS) $< -o $@



# Target: clean project.
clean:
      $(REMOVE) $(TARGET).hex $(TARGET).eep $(TARGET).cof $(TARGET).elf \
      $(TARGET).map $(TARGET).sym $(TARGET).lss \
      $(OBJ) $(LST) $(SRC:.c=.s) $(SRC:.c=.d) $(CXXSRC:.cpp=.s) $(CXXSRC:.cpp=.d)

depend:
      if grep '^# DO NOT DELETE' $(MAKEFILE) >/dev/null; \
      then \
            sed -e '/^# DO NOT DELETE/,$d' $(MAKEFILE) > \
                  $(MAKEFILE).$$ && \
            $(MV) $(MAKEFILE).$$ $(MAKEFILE); \
      fi
      echo '# DO NOT DELETE THIS LINE -- make depend depends on it.' \
            >> $(MAKEFILE); \
      $(CC) -M -mmcu=$(MCU) $(CDEFS) $(CINCS) $(SRC) $(ASRC) >> $(MAKEFILE)

.PHONY:      all build elf hex eep lss sym program coff extcoff clean depend

Set the build.verbose=true in the Arduino IDE preferences.txt file. That should show you the build switches and all intermediate output.

AWESOME.. After an hour or so of playing around, I created a pretty nice makefile which uses the Arduino packaged binaries and the IDE options. This should produce IDENTICAL hex code to the IDE.

I also added in a make read command (just for fun) which cats the serial port!

# Arduino Makefile modified and cleaned up by Jordan Perr
# Original from Arduino.cc
#
# Tested with Arduino software 0013 for Mac
#
# To Use this Makefile:
#   -Copy this makefile into your project's directory
#        -set BUILD_TARGETS all of your project's files to BUILD_TARGETS
#        -set TARGET to your main target file's name. (eg. main for main.cpp)
#        -set INSTALL_DIR to the location of your arduino installation
#        -set LOAD_PORT to the serial port you wish to download to. Use * as a wildcard
#   -set READ_PORT to the serial port you wish to read from (cat port)
#
#
#
# make
#   -builds into current directory, creating a TARGET.hex binary
#
# make upload
#   -uses avrdude to download TARGET.hex to PORT
#
# make clean
#   -removes all temp and binary files created during build
#
#

# project specific settings

TARGET = main
BUILD_TARGETS = main.cpp
INSTALL_DIR = /Applications/arduino-0013
LOAD_PORT = /dev/tty.usb*
READ_PORT = /dev/cu.usb*

# arduino model specific settings

UPLOAD_RATE = 19200
AVRDUDE_PROGRAMMER = stk500
MCU = atmega168


###############################
##  don't go below this line ##
###############################

# Arduino Distrobution Settings (v.0013)
ARDUINO = $(INSTALL_DIR)/hardware/cores/arduino
AVR_TOOLS_PATH = $(INSTALL_DIR)/hardware/tools/avr/bin
SRC =  $(ARDUINO)/pins_arduino.c $(ARDUINO)/wiring.c \
$(ARDUINO)/wiring_analog.c $(ARDUINO)/wiring_digital.c \
$(ARDUINO)/wiring_pulse.c $(ARDUINO)/wiring_serial.c \
$(ARDUINO)/wiring_shift.c $(ARDUINO)/WInterrupts.c
CXXSRC = $(BUILD_TARGETS) $(ARDUINO)/HardwareSerial.cpp $(ARDUINO)/WMath.cpp $(ARDUINO)/Print.cpp
FORMAT = ihex

# Environment settings
CC = $(AVR_TOOLS_PATH)/avr-gcc
CXX = $(AVR_TOOLS_PATH)/avr-gcc
OBJCOPY = $(AVR_TOOLS_PATH)/avr-objcopy
OBJDUMP = $(AVR_TOOLS_PATH)/avr-objdump
SIZE = $(AVR_TOOLS_PATH)/avr-size
NM = $(AVR_TOOLS_PATH)/avr-nm
AVRDUDE = $(AVR_TOOLS_PATH)/avrdude
REMOVE = rm -f
MV = mv -f


###### HELPFUL DEFINITIONS =====

# Compiler Flags
ALL_CFLAGS = -c -g -Os -w -ffunction-sections -fdata-sections -mmcu=$(MCU) -DF_CPU=16000000L -I$(ARDUINO)
ALL_CXXFLAGS = -c -g -Os -w -fno-exceptions -ffunction-sections -fdata-sections -mmcu=$(MCU) -DF_CPU=16000000L -I$(ARDUINO)

# Assembler Flags
ALL_ASFLAGS = -mmcu=$(MCU) -I. -x assembler-with-cpp

# Loader flags
AVRDUDE_FLAGS = -C $(INSTALL_DIR)/hardware/tools/avr/etc/avrdude.conf -V -p $(MCU) -P $(LOAD_PORT) -c $(AVRDUDE_PROGRAMMER) -b $(UPLOAD_RATE) -D -U flash:w:$(TARGET).hex:i

# Define all object files.
OBJ = $(SRC:.c=.o) $(CXXSRC:.cpp=.o) $(ASRC:.S=.o)



###### ROUTINES #######


# DEFAULT
all: build

# READ
read:
      cat $(READ_PORT)

# UPLAOD  
load: upload
upload: $(TARGET).hex
      $(AVRDUDE) $(AVRDUDE_FLAGS)
      
# CLEAN
clean:
      $(REMOVE) $(TARGET).hex $(TARGET).eep $(TARGET).cof $(TARGET).elf \
      $(TARGET).map $(TARGET).sym $(TARGET).lss \
      $(OBJ) $(SRC:.c=.s) $(SRC:.c=.d) $(CXXSRC:.cpp=.s) $(CXXSRC:.cpp=.d)

# BUILD
build: elf hex



# Transcode: create HEX file from ELF file.
hex:
      $(OBJCOPY) -O $(FORMAT) -R .eeprom $(TARGET).elf $(TARGET).hex


# Link: create ELF output file from object files.
elf: $(OBJ)
      $(CC) -Os -Wl,--gc-sections -mmcu=atmega168 -o $(TARGET).elf $(OBJ) -lm


# Compile: create object files from C++ source files.
.cpp.o:
      $(CXX) -c $(ALL_CXXFLAGS) $< -o $@

# Compile: create object files from C source files.
.c.o:
      $(CC) -c $(ALL_CFLAGS) $< -o $@


# Compile: create assembler files from C source files.
.c.s:
      $(CC) -S $(ALL_CFLAGS) $< -o $@


# Assemble: create object files from assembler source files.
.S.o:
      $(CC) -c $(ALL_ASFLAGS) $< -o $@

What would be nice is if the IDE could, on demand, produce a Makefile that would duplicate the results of the usual Verify process.

Or even a separate project tied in with the IDE.

Also, does anybody have a reason for Arduino IDE's weird way of wrapping your "sketch" with an include header and the main function? I really dislike that, and think it causes the IDE to be fairly limited. If the IDE supported a real "C++ Project" mode as well as "Sketch" mode, it would be vastly more useful. The only reason I bothered creating that makefile was to be able to spread my project out accross multiple files.
The IDE, in my opinion, is only useful to beginners. People who want to create more advanced scripts, who want to work with a team and use version control, etc.. we all need something a bit less "managed." Unfortunately, we have no middle ground.. It's either Arduino IDE, or Toolchain.

Maybe a feature for the next version of the IDE?

The main tab is assumed to be C++, wrapped with a #include <WProgram.h>, and prototypes declared for any of your functions that it can find in any tab. Other tabs can be .c, .cpp, .h at your discretion.

I agree that it's a bit odd and I wish it were more transparent about the way it stitches together the parts of your sketch. At a minimum, it should save an asset in your sketch directory that looks like this:

main.cpp

#include <WProgram.h>
// prototypes found in sketch tabs
void setup();
void loop();
int waitForPushButton(int pin); // from mysketch.pde tab
void saveConfig(int address); // from myconfig.cpp tab
#include "mysketch.pde"

I don't agree that it has to grow all the hooks into version-control managers that the "big IDEs" do. I like the fact that the IDE is focused and relatively small. It shouldn't feel mandatory, or be all things for all people.

All things considered, we're talking about microcontrollers with about as much smarts as a common microwave oven, not massive control apparatus for huge projects with fifty engineers slaving over two hundred code classes.

Arduino lets people who would be scared to death by C to program in C without realising it...

I think it's an important result... any attempt at turning into a supposedly "real IDE" will destroy this...

Look at Eclipse... it's a pain in the ass for me imagine for a beginner..

I'm not a beginner and I use Arduino to code... it's simple and it gets the job done...

Arduino is not just for beginners... is for people who want to get the job done... if you are more interested in the tool then the result the makefile is your friend...

m

I'm not a beginner and I use Arduino to code... it's simple and it gets the job done...

Ditto.

You may want to look at eclipse on top of the AVR toolchain if you're feeling limited by the Arduino IDE. I have not used it personally, but I've heard good things about Eclipse in general.

-j