Makefile For Arduino Under Cygwin/Windows

From Wiki
Jump to: navigation, search

This could still use a little cleanup. It handles building a project with multiple source files, but it still requires too much manual editing when a new library is pulled in, new source files are added, etc. The MY_LIBS thing is a pretty sloppy fix, as it should figure out what directories are in the sketch library folder and automatically put them in the path.

However, it works, and it got me out of the horrendous IDE.

Please note the -jcw extension on arduino-00xx directory. I have a special build that has some header files fixed to reduce errors when compiling with -W -Wall. This Makefile is using -w to keep things quiet while I was getting paths and such right. I would strongly suggest enabling -W -Wall (and removing the -w) so that fun little problems like signed vs unsigned compares don't bite you in the butt.

Under Cygwin, if you're using 'make' version 3.81 or higher, you will need to down-grade to 3.80. This is because at some point, some idiot decided to remove DOS path name support, so a ':' in something like 'ARDUINO_DIR = c:/cygwin/home' will result in an error message: "Error : *** target pattern contains no `%'." What moron decided this was a smart decision is beyond me. Instructions to down-grade to make-3.80 are here. It's very trivial to do so.

Why not just use the '/cygwin/c' nomenclature to refer to 'c:/'? Because the avr-gcc and avr-g++ built for Windows don't understand that. To it, it looks internally like '/cygwin/c/somepath', which doesn't exist, since it's a meta-directory inside the Cygwin environment, and the provided toolchain is not Cygwin-awares. An avr-gcc toolchain built for Cygwin would likely work, but that's not what ships in the Arduino toolchain.

#
#  $Id$
#  $Revision$
#  $Date$
#  $Author$
#  $HeadURL$
#

#
# Swiped from http://www.robgray.com/temp/makefile
#

#
# Tune for project
#
PROJECT_NAME = Compactor
MY_FILES = config.cpp dac.cpp log.cpp modem.cpp monitor.cpp nvprom.cpp remote.cpp storage.cpp util.cpp
MY_LIBS = ../libraries/SdFat/Sd2Card.cpp ../libraries/SdFat/SdFile.cpp  ../libraries/SdFat/SdVolume.cpp ../libraries/Time/Time.cpp ../libraries/Timer1/TimerOne.cpp
MY_LIBS_INC = -I../libraries/SdFat -I../libraries/Time -I../libraries/Timer1
COM = 3

#
#  Tweak for Arduino-00xx package installation
#
VERSION=22
ARDUINO_DIR = c:/cygwin/home/jcw/arduino-00$(VERSION)-jcw
ARDUINO_CORE = $(ARDUINO_DIR)/hardware/arduino/cores/arduino
ARDUINO_LIB = $(ARDUINO_DIR)/libraries
AVRDUDE_PATH = $(ARDUINO_DIR)/hardware/tools/avr/bin
AVRDUDECONFIG_PATH = $(ARDUINO_DIR)/hardware/tools/avr/etc
AVR_TOOLS_PATH = $(ARDUINO_DIR)/hardware/tools/avr/bin
TARGET = $(notdir $(CURDIR))

#
# Tweak for AVR hardware (currently set for Arduino Mega 2560 on COM3)
#
PORT = //./COM$(COM)
UPLOAD_RATE = 115200
AVRDUDE_PROGRAMMER = stk500v2
MCU = atmega2560
F_CPU = 16000000

#
# Note that if your program has dependencies other than those
# already listed below, you will need to add them accordingly.
#
C_MODULES =  \
$(ARDUINO_CORE)/wiring_pulse.c \
$(ARDUINO_CORE)/wiring_analog.c \
$(ARDUINO_CORE)/pins_arduino.c \
$(ARDUINO_CORE)/wiring.c \
$(ARDUINO_CORE)/wiring_digital.c \
$(ARDUINO_CORE)/WInterrupts.c \
$(ARDUINO_CORE)/wiring_shift.c \

CXX_MODULES = $(MY_FILES) $(MY_LIBS) \
$(ARDUINO_CORE)/Tone.cpp \
$(ARDUINO_CORE)/main.cpp \
$(ARDUINO_CORE)/WMath.cpp \
$(ARDUINO_CORE)/Print.cpp \
$(ARDUINO_CORE)/HardwareSerial.cpp \
$(ARDUINO_LIB)/EEPROM/EEPROM.cpp 

CXX_APP = applet/$(TARGET).cpp
MODULES = $(C_MODULES) $(CXX_MODULES)
SRC = $(C_MODULES)
CXXSRC = $(CXX_MODULES) $(CXX_APP)
FORMAT = ihex

#
# 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
#
DEBUG =

#
#  Optimization level s, 0, 1, 2 or 3
#
OPT = s

#
# Place -D (#define) or -U (#undef) options here
#
CDEFS = -DF_CPU=$(F_CPU)L -DARDUINO=$(VERSION)
CXXDEFS = -DF_CPU=$(F_CPU)L -DARDUINO=$(VERSION)

#
# Place -I options here
#
CINCS = -I. -I$(ARDUINO_CORE) -I$(ARDUINO_LIB)/EEPROM $(MY_LIBS_INC)
CXXINCS =  -I. -I$(ARDUINO_CORE) -I$(ARDUINO_LIB)/EEPROM $(MY_LIBS_INC)

#
# 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
#CWARN = -Wall # show all warnings
CWARN = -w # suppress all warnings
####CTUNING = -funsigned-char -funsigned-bitfields -fpack-struct -fshort-enums
CTUNING = -ffunction-sections -fdata-sections
CXXTUNING = -fno-exceptions -ffunction-sections -fdata-sections
#CEXTRA = -Wa,-adhlns=$(<:.c=.lst)
MMCU = -mmcu=$(MCU)

CFLAGS = $(CDEBUG) -O$(OPT) $(CWARN) $(CTUNING) $(MMCU) $(CDEFS) $(CINCS) $(CSTANDARD) $(CEXTRA)
CXXFLAGS = $(CDEBUG) -O$(OPT) $(CWARN) $(CXXTUNING) $(CDEFS) $(CINCS)
#ASFLAGS = -Wa,-adhlns=$(<:.S=.lst),-gstabs
LDFLAGS = -O$(OPT) -lm -Wl,--gc-sections -Wl,-Map,applet/$(TARGET).map

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

#
# Program names
#
CC = $(AVR_TOOLS_PATH)/avr-gcc
CXX = $(AVR_TOOLS_PATH)/avr-g++
LD = $(AVR_TOOLS_PATH)/avr-gcc
OBJCOPY = $(AVR_TOOLS_PATH)/avr-objcopy
OBJDUMP = $(AVR_TOOLS_PATH)/avr-objdump
AR  = $(AVR_TOOLS_PATH)/avr-ar
SIZE = $(AVR_TOOLS_PATH)/avr-size
NM = $(AVR_TOOLS_PATH)/avr-nm
AVRDUDE = $(AVRDUDE_PATH)/avrdude
REMOVE = rm -f
MV = mv -f

#
# Define all object files.
#
OBJ = $(SRC:.c=.o) $(CXXSRC:.cpp=.o) $(ASRC:.S=.o)
OBJ_MODULES = $(C_MODULES:.c=.o) $(CXX_MODULES:.cpp=.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 = $(CFLAGS) -mmcu=$(MCU)
ALL_CXXFLAGS = $(CXXFLAGS) -mmcu=$(MCU)
ALL_ASFLAGS = -x assembler-with-cpp $(ASFLAGS) -mmcu=$(MCU)
ALL_LDFLAGS = $(LDFLAGS) -mmcu=$(MCU)

#
# Default target.
#
all: applet_files build sizeafter

build: elf lss hex 

#
# applet_files: quubmon.pde
#
applet/$(TARGET).cpp: $(PROJECT_NAME).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.
  # and prototypes for setup() and Loop()
  # 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
	@echo 'void setup();' >> applet/$(TARGET).cpp
	@echo 'void loop();' >> applet/$(TARGET).cpp
	@cat $(PROJECT_NAME).pde >> applet/$(TARGET).cpp

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

#
# Program the device.
#
upload: applet/$(TARGET).hex
	@echo "Programming..."
	@$(AVRDUDE) $(AVRDUDE_FLAGS) $(AVRDUDE_WRITE_FLASH)

#
# Display size of file.
#
HEXSIZE = $(SIZE) --target=$(FORMAT) applet/$(TARGET).elf
ELFSIZE = $(SIZE) -C applet/$(TARGET).elf
sizebefore:
	@if [ -f applet/$(TARGET).elf ]; then echo; echo $(MSG_SIZE_BEFORE); $(HEXSIZE); echo; fi

sizeafter:
	@if [ -f applet/$(TARGET).elf ]; then echo; echo $(MSG_SIZE_AFTER); $(HEXSIZE); echo; fi

#
# 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: applet/$(TARGET).elf
	$(COFFCONVERT) -O coff-avr applet/$(TARGET).elf $(TARGET).cof

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

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

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

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

#
# 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 library.
#
applet/$(TARGET).elf: applet/$(TARGET).o applet/core.a .depend
	@$(LD) $(ALL_LDFLAGS) -o $@ applet/$(TARGET).o applet/core.a

applet/core.a: $(OBJ_MODULES)
#@for i in $(OBJ_MODULES); do echo $(AR) rcs applet/core.a $$i; $(AR) rcs applet/core.a $$i; done
	@for i in $(OBJ_MODULES); do $(AR) rcs applet/core.a $$i; done

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

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

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

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

#
# Automatic dependencies
#
%.d: %.c
	$(CC) -M $(ALL_CFLAGS) $< | sed "s;$(notdir $*).o:;$*.o $*.d:;" > $@

%.d: %.cpp
	$(CXX) -M $(ALL_CXXFLAGS) $< | sed "s;$(notdir $*).o:;$*.o $*.d:;" > $@

#
# 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)

.PHONY: all build elf hex eep lss sym program coff extcoff applet_files sizebefore sizeafter

#
#  The .depend files contains the list of header files that the
#  various source files depend on.  By doing this, we'll only
#  rebuild the .o's that are affected by header files changing.
#
.depend:
	$(CC) -M $(CINCS) $(CXX_MODULES) $(CXX_APP) > .depend

#
#  Utility targets
#
dep:
	$(CC) -M $(CINCS) $(CXX_MODULES) $(CXX_APP) > .depend

#
#  Include the .depend file so we have the list of dependencies
#
ifeq (.depend,$(wildcard .depend))
include .depend
endif