Tweak of stand-alone command-line environment

I like the idea of keeping sketches compatible with the rest of the deve environment, so I decided to set up a command-line development environment. This required a few tweaks to get rid of some compiler warnings and to get the Makefile into shape. Now I can build apps without using any other AVR tools except what comes with the Arduino software.

I thought I’d pass on my changes to others who also want to do this, not to mention that some changes to core probably need more eyes on them; this was about getting things working, not (necessarily) figuring out why things were issuing warnings. I’m interested if something here is totally wrong, but all I can report is that it “works for me.”

The compiler optimization options were cribbed from the avr-lib FAQ. The Makefile changes were about me hacking it into something more my style – take it with a grain of salt.

There was an implicit cast in pins_arduino.c that I just made explicit to shut up the compiler:

--- /Users/clvrmnky/Downloads/arduino-0015/hardware/cores/arduino/pins_arduino.c      2009-03-25 11:51:30.000000000 -0400
+++ pins_arduino.c      2009-03-30 19:38:06.000000000 -0400
@@ -362,25 +362,25 @@
 const uint16_t PROGMEM port_to_mode_PGM[] = {
       NOT_A_PORT,
       NOT_A_PORT,
-      &DDRB,
-      &DDRC,
-      &DDRD,
+      (int)&DDRB,
+      (int)&DDRC,
+      (int)&DDRD,
 };
 
 const uint16_t PROGMEM port_to_output_PGM[] = {
       NOT_A_PORT,
       NOT_A_PORT,
-      &PORTB,
-      &PORTC,
-      &PORTD,
+      (int)&PORTB,
+      (int)&PORTC,
+      (int)&PORTD,
 };
 
 const uint16_t PROGMEM port_to_input_PGM[] = {
       NOT_A_PORT,
       NOT_A_PORT,
-      &PINB,
-      &PINC,
-      &PIND,
+      (int)&PINB,
+      (int)&PINC,
+      (int)&PIND,
 };
 
 const uint8_t PROGMEM digital_pin_to_port_PGM[] = {

There was also this header file reference that was obviously (given the noisy message in the original file) wrong:

--- /Users/clvrmnky/Downloads/arduino-0015/hardware/cores/arduino/wiring_private.h      2009-03-25 11:51:30.000000000 -0400
+++ wiring_private.h      2009-03-30 19:34:54.000000000 -0400
@@ -27,7 +27,7 @@
 
 #include <avr/io.h>
 #include <avr/interrupt.h>
-#include <avr/delay.h>
+#include <util/delay.h>
 #include <stdio.h>
 #include <stdarg.h>

I suspect I’ll have to provide the Makefile diff in a follow-up or provide it as a URL.

Makefile, heavily tweaked the way I like things. Lots of unilateral decisions, but that’s just how I roll:

--- /Users/clvrmnky/Downloads/arduino-0015/hardware/cores/arduino/Makefile      2009-03-25 11:51:30.000000000 -0400
+++ /Users/clvrmnky/src/Blinky/Makefile      2009-03-31 02:22:29.000000000 -0400
@@ -1,2 +1,2 @@
-# Arduino 0011 Makefile
+# Arduino 0015 Makefile
 # Arduino adaptation by mellis, eighthave, oli.keller
@@ -23,12 +23,14 @@
 #     Older one's are atmega8 based, newer ones like Arduino Mini, Bluetooth
-#     or Diecimila have the atmega168.  If you're using a LilyPad Arduino,
-#     change F_CPU to 8000000.
+#     or Diecimila have the atmega168.
 #
-#  5. At the command line, change to the directory containing your
+#  5. If necessary, change MHZ to the speed of your MCU, in MHz. If you're
+#     using a LilyPad Arduino change it to 8. Otherwise, leave this at 16.
+#
+#  6. At the command line, change to the directory containing your
 #     program's file and the makefile.
 #
-#  6. Type "make" and press enter to compile/verify your program.
+#  7. Type "make" and press enter to compile/verify your program.
 #
-#  7. Type "make upload", reset your Arduino board, and press enter to
-#     upload your program to the Arduino board.
+#  8. Type "make upload", reset your Arduino board (if necessary) and press
+#     enter to upload your program to the Arduino board.
 #
@@ -37,8 +39,8 @@
 TARGET = $(notdir $(CURDIR))
-INSTALL_DIR = /Users/dmellis/Source/arduino/trunk/build/macosx/build/work
+INSTALL_DIR = /Users/clvrmnky/Developer/arduino-0015
 PORT = /dev/tty.usb*
-UPLOAD_RATE = 19200
+UPLOAD_RATE = 57600
 AVRDUDE_PROGRAMMER = stk500v1
-MCU = atmega168
-F_CPU = 16000000
+MCU = atmega328p
+MHZ = 16
 
@@ -49,11 +51,10 @@
 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
+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 = $(ARDUINO)/HardwareSerial.cpp $(ARDUINO)/WMath.cpp \
-$(ARDUINO)/Print.cpp
+      $(ARDUINO)/Print.cpp
 FORMAT = ihex
 
-
 # Name of this Makefile (used for "make depend").
@@ -66,7 +67,9 @@
 
-OPT = s
+# Optimization options.  This can be left at "-Os" in most cases, perhaps "-O0"
+# for extreme debugging purposes if the resulting .hex is still small enough.
+OPT = -Os -mcall-prologues
 
 # Place -D or -U options here
-CDEFS = -DF_CPU=$(F_CPU)
-CXXDEFS = -DF_CPU=$(F_CPU)
+CDEFS = -DF_CPU=$(MHZ)000000UL
+CXXDEFS = -DF_CPU=$(MHZ)000000UL
 
@@ -87,4 +90,5 @@
 
-CFLAGS = $(CDEBUG) $(CDEFS) $(CINCS) -O$(OPT) $(CWARN) $(CSTANDARD) $(CEXTRA)
-CXXFLAGS = $(CDEFS) $(CINCS) -O$(OPT)
+CFLAGS = $(CDEBUG) $(CDEFS) $(CINCS) $(OPT) $(CWARN) $(CSTANDARD) \
+      $(CEXTRA)
+CXXFLAGS = $(CDEFS) $(CINCS) $(OPT)
 #ASFLAGS = -Wa,-adhlns=$(<:.S=.lst),-gstabs 
@@ -92,3 +96,2 @@
 
-
 # Programming support using avrdude. Settings and variables.
@@ -96,5 +99,6 @@
 AVRDUDE_WRITE_FLASH = -U flash:w:applet/$(TARGET).hex
-AVRDUDE_FLAGS = -V -F -C $(INSTALL_DIR)/hardware/tools/avr/etc/avrdude.conf \
--p $(MCU) -P $(AVRDUDE_PORT) -c $(AVRDUDE_PROGRAMMER) \
--b $(UPLOAD_RATE)
+AVRDUDE_FLAGS = -V -F \
+      -C $(INSTALL_DIR)/hardware/tools/avr/etc/avrdude.conf \
+      -p $(MCU) -P $(AVRDUDE_PORT) -c $(AVRDUDE_PROGRAMMER) \
+      -b $(UPLOAD_RATE)
 
@@ -102,6 +106,6 @@
 CC = $(AVR_TOOLS_PATH)/avr-gcc
-CXX = $(AVR_TOOLS_PATH)/avr-g++
+CXX      = $(AVR_TOOLS_PATH)/avr-g++
 OBJCOPY = $(AVR_TOOLS_PATH)/avr-objcopy
 OBJDUMP = $(AVR_TOOLS_PATH)/avr-objdump
-AR  = $(AVR_TOOLS_PATH)/avr-ar
+AR = $(AVR_TOOLS_PATH)/avr-ar
 SIZE = $(AVR_TOOLS_PATH)/avr-size
@@ -124,5 +128,6 @@
 
-
 # Default target.
-all: applet_files build sizeafter
+all: generate_applet_files build sizes
+
+sizes: sizebefore sizeafter
 
@@ -130,14 +135,16 @@
 
-applet_files: $(TARGET).pde
-      # Here is the "preprocessing".
-      # It creates a .cpp file based with the same name as the .pde file.
-      # On top of the new .cpp file comes the WProgram.h header.
-      # At the end there is a generic main() function attached.
-      # Then the .cpp file will be compiled. Errors during compile will
-      # refer to this new, automatically generated, file. 
-      # Not the original .pde file you actually edit...
-      test -d applet || mkdir applet
-      echo '#include "WProgram.h"' > applet/$(TARGET).cpp
-      cat $(TARGET).pde >> applet/$(TARGET).cpp
-      cat $(ARDUINO)/main.cxx >> applet/$(TARGET).cpp
+# Here is the "preprocessing".
+# It creates a .cpp file based with the same name as the .pde file.
+# On top of the new .cpp file comes the WProgram.h header.
+# At the end there is a generic main() function attached.
+# Then the .cpp file will be compiled. Errors during compile will
+# refer to this new, automatically generated, file. 
+# Not the original .pde file you actually edit...
+generate_applet_files: $(TARGET).pde
+      @echo "Generating avr-libc compatible applet/$(TARGET).cpp from $<"
+      @test -d applet || mkdir applet
+      @echo '#include "WProgram.h"' > applet/$(TARGET).cpp
+      @cat $(TARGET).pde >> applet/$(TARGET).cpp
+      @echo "\n/* End of $<; start of main() */" >> applet/$(TARGET).cpp
+      @cat $(ARDUINO)/main.cxx >> applet/$(TARGET).cpp
 
@@ -153,20 +160,29 @@
 
-
-      # Display size of file.
+# Display size of file.
 HEXSIZE = $(SIZE) --target=$(FORMAT) applet/$(TARGET).hex
-ELFSIZE = $(SIZE)  applet/$(TARGET).elf
+ELFSIZE = $(SIZE) applet/$(TARGET).elf
+MSG_SIZE_BEFORE = ELF section sizes:
+MSG_SIZE_AFTER = .hex section size:
+
 sizebefore:
-      @if [ -f applet/$(TARGET).elf ]; then echo; echo $(MSG_SIZE_BEFORE); $(HEXSIZE); echo; fi
+      @if [ -f applet/$(TARGET).elf ]; then \
+            echo; \
+            echo $(MSG_SIZE_BEFORE); \
+            $(ELFSIZE); \
+      fi
 
 sizeafter:
-      @if [ -f applet/$(TARGET).elf ]; then echo; echo $(MSG_SIZE_AFTER); $(HEXSIZE); echo; fi
-
+      @if [ -f applet/$(TARGET).hex ]; then \
+            echo; \
+            echo $(MSG_SIZE_AFTER); \
+            $(HEXSIZE);      \
+      fi
 
-# Convert ELF to COFF for use in debugging / simulating in AVR Studio or VMLAB.
+# 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 
-
+      --change-section-address .data-0x800000 \
+      --change-section-address .bss-0x800000 \
+      --change-section-address .noinit-0x800000 \
+      --change-section-address .eeprom-0x810000 
 
@@ -175,7 +191,5 @@
 
-
-extcoff: $(TARGET).elf
+extcoff: applet/$(TARGET).elf
       $(COFFCONVERT) -O coff-ext-avr applet/$(TARGET).elf $(TARGET).cof
 
-
 .SUFFIXES: .elf .hex .eep .lss .sym
@@ -197,10 +211,12 @@
 
-      # Link: create ELF output file from library.
+# Link: create ELF output file from library.
 applet/$(TARGET).elf: $(TARGET).pde applet/core.a 
-      $(CC) $(ALL_CFLAGS) -o $@ applet/$(TARGET).cpp -L. applet/core.a $(LDFLAGS)
+      $(CXX) $(ALL_CXXFLAGS) -o $@ applet/$(TARGET).cpp $(LDFLAGS) -L. \
+            applet/core.a
 
 applet/core.a: $(OBJ)
-      @for i in $(OBJ); do echo $(AR) rcs applet/core.a $i; $(AR) rcs applet/core.a $i; done
-
-
+      @for i in $(OBJ); do \
+            echo $(AR) rcs applet/core.a $i; \
+            $(AR) rcs applet/core.a $i; \
+      done
 
@@ -214,3 +230,2 @@
 
-
 # Compile: create assembler files from C source files.
@@ -219,3 +234,2 @@
 
-
 # Assemble: create object files from assembler source files.
@@ -224,9 +238,13 @@
 
-
-
 # Target: clean project.
 clean:
-      $(REMOVE) applet/$(TARGET).hex applet/$(TARGET).eep applet/$(TARGET).cof applet/$(TARGET).elf \
-      applet/$(TARGET).map applet/$(TARGET).sym applet/$(TARGET).lss applet/core.a \
-      $(OBJ) $(LST) $(SRC:.c=.s) $(SRC:.c=.d) $(CXXSRC:.cpp=.s) $(CXXSRC:.cpp=.d)
+      $(REMOVE) applet/$(TARGET).hex applet/$(TARGET).eep applet/$(TARGET).cof \
+            applet/$(TARGET).elf applet/$(TARGET).map applet/$(TARGET).sym \
+            applet/$(TARGET).lss
+
+# Clean everything, including stuff that doesn't change much.
+# This will clobber things in the Arduino cores directory.
+really-clean: clean
+      $(REMOVE) applet/core.a $(OBJ) $(LST) $(SRC:.c=.s) $(SRC:.c=.d) \
+            $(CXXSRC:.cpp=.s) $(CXXSRC:.cpp=.d)

You will notice that I changed how the .elf target was made. Since GCC was compiling with g++ anyway, I just went ahead and explicitly told it to use g++ to make GCC shut up about ObjC/C only options. Other stuff is probably pretty clear from the context.

Cuss and discuss.

Well, of course my trivial program was not exercising all the necessary bits and pieces!

If you need a third-party library you will have to tweak the -I and -L opts passed to the CXX compiler. And make sure the third-parties are already built (by running the Arduino IDE on 'em) or add a target to build those, if necessary. It will not be easy to automatically build the include and link object list from the sketch source like Processing/Arduino does. At least, not with ordinary GNU Make.

It might be easier to simply keep sketches sketches and build .cpp files based on a template file with the avr tools.

Still, I learnt a fair amount about the AVR build env, and how Arduino sketches resolve the cross-compilation issues.