This is an old revision of the document!


Setting up somewhat bigger projects

Once you are out the "Hello? Anybody home?" stage of making yourself accustomed with cc65 and its amazing possibilities, you will probably start to think about developing something bigger (or at least less of a cliché ;-) ), right? That means that - as your project starts growing - your willingness to compile manually tens (if not hundreds) of files constituting your project, will be exponentially diminishing.

Out of the multitude of various possibilities for making your life easier, there is one, which is widely adopted among the programming folks all around the world: make. We shall use the highly popular GNU version of make, called (nomen omen) GNU Make for our further discussions.

Again - there is the excellent cc65 documentation to help you out on this topic too. Please read it. It is very concise and to the point. It shows you how to create a simple Makefile, which you can adapt to most of your future projects. It explains the basics, sets the fundaments for extension, so that once you understand it, you should be able to write (or copy'n adapt) a new Makefile for every new project relatively fast.

Wait… did I write “write new Makefile for every new project”? Isn't there an easier way to set the project up, including some sort of “universal” Makefile, which wouldn't require rewriting it all the time? Something like the good, old SAS/C had on Amiga for example? I mean I have no clue now what all my source files are going to be named like, I am not sure what options I am going to use in the end, etc. Sure I can maintain and adapt my Makefile every time I add a new source file. Adapt it again when I find the previously added file useless and delete it, adapt it whenever I want to test some new options, etc., etc. Sure - I can do it. Still, I somehow feel like I would prefer to be able to concentrate on quick development of the actual software, rather than my Makefile… So?

Well, if you are one of the lucky gals who live in the future, where cc65 v2.14 has already been released (or at least you use the latest “snapshot” aka “bleeding edge” version of cc65) - then we have something for you. Please bear in mind: THIS IS FOR SNAPSHOT or 2.14.x versions ONLY! WILL NOT WORK WITH EARLIER VERSIONS!

If you move further down the page, you will find THE Makefile for many simple to medium sized cc65 projects, which does not require any rewriting. There are some (we hope reasonable) conventions1), which you can easily follow when setting up the project:

  1. Create new directory naming it as you would like the program to be called (“superfrog”2) for a good example)
  2. Download and save the following Makefile inside this directory
  3. Inside the same directory create a subdirectory named “src”
  4. Put all your source files (both C and Assembler) inside “src” directory3)

Once you are done with the above, simply enter the project's directory and type

$ make

to get your project built… as long as it is free from show-stopping errors, that is.

Trying to be consistent with the rest of cc65, C64 target is assumed as default.

Build options

$ make OPTIONS=optsize,mapfile

will set the build options to turn on the size optimiser and generate the MAP file.4)

Once set, build OPTIONS will be saved in a so-called “state” file5) and remain effective until changed with another $ make OPTIONS=xxx,yyy,zzz statement. Issuing:

$ make OPTIONS=

i.e. with empty OPTIONS string, will reset the options and remove the state file.

Supported options:

  • optspeed
  • optsize
  • listing
  • mapfile
  • labelfile
  • debugfile

But I run Windows!

Well… what can I say? Your choice ;-) But don't worry that much. We still support your cc65 programming. During development of THE Makefile, efforts have been made to keep it compatible with typical Microsoft® Windows™ setups. First and foremost there is the Cygwin environment for Windows that will run THE Makefile out-of-the-box. Just make sure your CC65_HOME environment variable is set.

However a viable alternative is to run THE Makefile directly from the Windows cmd.exe shell using a 'Native Win32' build of GNU Make. There are several sources around but we recommend the GnuWin Make for Windows. Just make sure it's available through your PATH and you should be ready to go - and again don't forget to make sure your CC65_HOME is set.

What next?

First get the Makefile below. Play and experiment with it. Once you master the basics described here, and you get eager to try more advanced stuff, then you might be interested to know that the Makefile you got acquainted with, supports also building with target (and variant!) specific code, various libraries and linker configurations, and more. Advanced topics are described in another article.

THE Makefile itself

Here comes the long awaited pearl of GNU Make code in its full glory…

Makefile
###############################################################################
### Generic Makefile for cc65 projects - full version with abstract options ###
### V1.3.0(w) 2010 - 2013 Oliver Schmidt & Patryk "Silver Dream !" Łogiewa  ###
###############################################################################
 
###############################################################################
### In order to override defaults - values can be assigned to the variables ###
###############################################################################
 
# Space or comma separated list of cc65 supported target platforms to build for.
# Default: c64 (lowercase!)
TARGETS :=
 
# Name of the final, single-file executable.
# Default: name of the current dir with target name appended
PROGRAM :=
 
# Path(s) to additional libraries required for linking the program
# Use only if you don't want to place copies of the libraries in SRCDIR
# Default: none
LIBS    :=
 
# Custom linker configuration file
# Use only if you don't want to place it in SRCDIR
# Default: none
CONFIG  :=
 
# Additional C compiler flags and options.
# Default: none
CFLAGS  =
 
# Additional assembler flags and options.
# Default: none
ASFLAGS =
 
# Additional linker flags and options.
# Default: none
LDFLAGS =
 
# Path to the directory containing C and ASM sources.
# Default: src
SRCDIR :=
 
# Path to the directory where object files are to be stored (inside respective target subdirectories).
# Default: obj
OBJDIR :=
 
# Command used to run the emulator.
# Default: depending on target platform. For default (c64) target: x64 -kernal kernal -VICIIdsize -autoload
EMUCMD :=
 
# Optional commands used before starting the emulation process, and after finishing it.
# Default: none
#PREEMUCMD := osascript -e "tell application \"System Events\" to set isRunning to (name of processes) contains \"X11.bin\"" -e "if isRunning is true then tell application \"X11\" to activate"
#PREEMUCMD := osascript -e "tell application \"X11\" to activate"
#POSTEMUCMD := osascript -e "tell application \"System Events\" to tell process \"X11\" to set visible to false"
#POSTEMUCMD := osascript -e "tell application \"Terminal\" to activate"
PREEMUCMD :=
POSTEMUCMD :=
 
# On Windows machines VICE emulators may not be available in the PATH by default.
# In such case, please set the variable below to point to directory containing
# VICE emulators. 
#VICE_HOME := "C:\Program Files\WinVICE-2.2-x86\"
VICE_HOME :=
 
# Options state file name. You should not need to change this, but for those
# rare cases when you feel you really need to name it differently - here you are
STATEFILE := Makefile.options
 
###################################################################################
####  DO NOT EDIT BELOW THIS LINE, UNLESS YOU REALLY KNOW WHAT YOU ARE DOING!  ####
###################################################################################
 
###################################################################################
### Mapping abstract options to the actual compiler, assembler and linker flags ###
### Predefined compiler, assembler and linker flags, used with abstract options ###
### valid for 2.14.x. Consult the documentation of your cc65 version before use ###
###################################################################################
 
# Compiler flags used to tell the compiler to optimise for SPEED
define _optspeed_
  CFLAGS += -Oris
endef
 
# Compiler flags used to tell the compiler to optimise for SIZE
define _optsize_
  CFLAGS += -Or
endef
 
# Compiler and assembler flags for generating listings
define _listing_
  CFLAGS += --listing $$(@:.o=.lst)
  ASFLAGS += --listing $$(@:.o=.lst)
  REMOVES += $(addsuffix .lst,$(basename $(OBJECTS)))
endef
 
# Linker flags for generating map file
define _mapfile_
  LDFLAGS += --mapfile $$@.map
  REMOVES += $(PROGRAM).map
endef
 
# Linker flags for generating VICE label file
define _labelfile_
  LDFLAGS += -Ln $$@.lbl
  REMOVES += $(PROGRAM).lbl
endef
 
# Linker flags for generating a debug file
define _debugfile_
  LDFLAGS += -Wl --dbgfile,$$@.dbg
  REMOVES += $(PROGRAM).dbg
endef
 
###############################################################################
###  Defaults to be used if nothing defined in the editable sections above  ###
###############################################################################
 
# Presume the C64 target like the cl65 compile & link utility does.
# Set TARGETS to override.
ifeq ($(TARGETS),)
  TARGETS := c64
endif
 
# Presume we're in a project directory so name the program like the current
# directory. Set PROGRAM to override.
ifeq ($(PROGRAM),)
  PROGRAM := $(notdir $(CURDIR))
endif
 
# Presume the C and asm source files to be located in the subdirectory 'src'.
# Set SRCDIR to override.
ifeq ($(SRCDIR),)
  SRCDIR := src
endif
 
# Presume the object and dependency files to be located in the subdirectory
# 'obj' (which will be created). Set OBJDIR to override.
ifeq ($(OBJDIR),)
  OBJDIR := obj
endif
TARGETOBJDIR := $(OBJDIR)/$(TARGETS)
 
# On Windows it is mandatory to have CC65_HOME set. So do not unnecessarily
# rely on cl65 being added to the PATH in this scenario.
ifdef CC65_HOME
  CC := $(CC65_HOME)/bin/cl65
else
  CC := cl65
endif
 
# Default emulator commands and options for particular targets.
# Set EMUCMD to override.
c64_EMUCMD := $(VICE_HOME)x64 -kernal kernal -VICIIdsize -autoload
c128_EMUCMD := $(VICE_HOME)x128 -kernal kernal -VICIIdsize -autoload
vic20_EMUCMD := $(VICE_HOME)xvic -kernal kernal -VICdsize -autoload
pet_EMUCMD := $(VICE_HOME)xpet -Crtcdsize -autoload
plus4_EMUCMD := $(VICE_HOME)xplus4 -TEDdsize -autoload
# So far there is no x16 emulator in VICE (why??) so we have to use xplus4 with -memsize option
c16_EMUCMD := $(VICE_HOME)xplus4 -ramsize 16 -TEDdsize -autoload
cbm510_EMUCMD := $(VICE_HOME)xcbm2 -model 510 -VICIIdsize -autoload
cbm610_EMUCMD := $(VICE_HOME)xcbm2 -model 610 -Crtcdsize -autoload
atari_EMUCMD := atari800 -windowed -xl -pal -nopatchall -run
 
ifeq ($(EMUCMD),)
  EMUCMD = $($(CC65TARGET)_EMUCMD)
endif
 
###############################################################################
### The magic begins                                                        ###
###############################################################################
 
# The "Native Win32" GNU Make contains quite some workarounds to get along with
# cmd.exe as shell. However it does not provide means to determine that it does
# actually activate those workarounds. Especially does $(SHELL) NOT contain the
# value 'cmd.exe'. So the usual way to determine if cmd.exe is being used is to
# execute the command 'echo' without any parameters. Only cmd.exe will return a
# non-empy string - saying 'ECHO is on/off'.
#
# Many "Native Win32" prorams accept '/' as directory delimiter just fine. How-
# ever the internal commands of cmd.exe generally require '\' to be used.
#
# cmd.exe has an internal command 'mkdir' that doesn't understand nor require a
# '-p' to create parent directories as needed.
#
# cmd.exe has an internal command 'del' that reports a syntax error if executed
# without any file so make sure to call it only if there's an actual argument.
ifeq ($(shell echo),)
  MKDIR = mkdir -p $1
  RMDIR = rmdir $1
  RMFILES = $(RM) $1
else
  MKDIR = mkdir $(subst /,\,$1)
  RMDIR = rmdir $(subst /,\,$1)
  RMFILES = $(if $1,del /f $(subst /,\,$1))
endif
COMMA := ,
SPACE := $(N/A) $(N/A)
define NEWLINE
 
 
endef
# Note: Do not remove any of the two empty lines above !
 
TARGETLIST := $(subst $(COMMA),$(SPACE),$(TARGETS))
 
ifeq ($(words $(TARGETLIST)),1)
 
# Set PROGRAM to something like 'myprog.c64'.
override PROGRAM := $(PROGRAM).$(TARGETLIST)
 
# Set SOURCES to something like 'src/foo.c src/bar.s'.
# Use of assembler files with names ending differently than .s is deprecated!
SOURCES := $(wildcard $(SRCDIR)/*.c)
SOURCES += $(wildcard $(SRCDIR)/*.s)
SOURCES += $(wildcard $(SRCDIR)/*.asm)
SOURCES += $(wildcard $(SRCDIR)/*.a65)
 
# Add to SOURCES something like 'src/c64/me.c src/c64/too.s'.
# Use of assembler files with names ending differently than .s is deprecated!
SOURCES += $(wildcard $(SRCDIR)/$(TARGETLIST)/*.c)
SOURCES += $(wildcard $(SRCDIR)/$(TARGETLIST)/*.s)
SOURCES += $(wildcard $(SRCDIR)/$(TARGETLIST)/*.asm)
SOURCES += $(wildcard $(SRCDIR)/$(TARGETLIST)/*.a65)
 
# Set OBJECTS to something like 'obj/c64/foo.o obj/c64/bar.o'.
OBJECTS := $(addsuffix .o,$(basename $(addprefix $(TARGETOBJDIR)/,$(notdir $(SOURCES)))))
 
# Set DEPENDS to something like 'obj/c64/foo.d obj/c64/bar.d'.
DEPENDS := $(OBJECTS:.o=.d)
 
# Add to LIBS something like 'src/foo.lib src/c64/bar.lib'.
LIBS += $(wildcard $(SRCDIR)/*.lib)
LIBS += $(wildcard $(SRCDIR)/$(TARGETLIST)/*.lib)
 
# Add to CONFIG something like 'src/c64/bar.cfg src/foo.cfg'.
CONFIG += $(wildcard $(SRCDIR)/$(TARGETLIST)/*.cfg)
CONFIG += $(wildcard $(SRCDIR)/*.cfg)
 
# Select CONFIG file to use. Target specific configs have higher priority.
ifneq ($(word 2,$(CONFIG)),)
  CONFIG := $(firstword $(CONFIG))
  $(info Using config file $(CONFIG) for linking)
endif
 
.SUFFIXES:
.PHONY: all test clean zap love
 
all: $(PROGRAM)
 
-include $(DEPENDS)
-include $(STATEFILE)
 
# If OPTIONS are given on the command line then save them to STATEFILE
# if (and only if) they have actually changed. But if OPTIONS are not
# given on the command line then load them from STATEFILE. Have object
# files depend on STATEFILE only if it actually exists.
ifeq ($(origin OPTIONS),command line)
  ifneq ($(OPTIONS),$(_OPTIONS_))
    ifeq ($(OPTIONS),)
      $(info Removing OPTIONS)
      $(shell $(RM) $(STATEFILE))
      $(eval $(STATEFILE):)
    else
      $(info Saving OPTIONS=$(OPTIONS))
      $(shell echo _OPTIONS_=$(OPTIONS) > $(STATEFILE))
    endif
    $(eval $(OBJECTS): $(STATEFILE))
  endif
else
  ifeq ($(origin _OPTIONS_),file)
    $(info Using saved OPTIONS=$(_OPTIONS_))
    OPTIONS = $(_OPTIONS_)
    $(eval $(OBJECTS): $(STATEFILE))
  endif
endif
 
# Transform the abstract OPTIONS to the actual cc65 options.
$(foreach o,$(subst $(COMMA),$(SPACE),$(OPTIONS)),$(eval $(_$o_)))
 
# Strip potential variant suffix from the actual cc65 target.
CC65TARGET := $(firstword $(subst .,$(SPACE),$(TARGETLIST)))
 
# The remaining targets.
$(TARGETOBJDIR):
	$(call MKDIR,$@)
 
vpath %.c $(SRCDIR)/$(TARGETLIST) $(SRCDIR)
 
$(TARGETOBJDIR)/%.o: %.c | $(TARGETOBJDIR)
	$(CC) -t $(CC65TARGET) -c --create-dep $(@:.o=.d) $(CFLAGS) -o $@ $<
 
vpath %.s $(SRCDIR)/$(TARGETLIST) $(SRCDIR)
 
$(TARGETOBJDIR)/%.o: %.s | $(TARGETOBJDIR)
	$(CC) -t $(CC65TARGET) -c --create-dep $(@:.o=.d) $(ASFLAGS) -o $@ $<
 
vpath %.asm $(SRCDIR)/$(TARGETLIST) $(SRCDIR)
 
$(TARGETOBJDIR)/%.o: %.asm | $(TARGETOBJDIR)
	$(CC) -t $(CC65TARGET) -c --create-dep $(@:.o=.d) $(ASFLAGS) -o $@ $<
 
vpath %.a65 $(SRCDIR)/$(TARGETLIST) $(SRCDIR)
 
$(TARGETOBJDIR)/%.o: %.a65 | $(TARGETOBJDIR)
	$(CC) -t $(CC65TARGET) -c --create-dep $(@:.o=.d) $(ASFLAGS) -o $@ $<
 
$(PROGRAM): $(CONFIG) $(OBJECTS) $(LIBS)
	$(CC) -t $(CC65TARGET) $(LDFLAGS) -o $@ $(patsubst %.cfg,-C %.cfg,$^)
 
test: $(PROGRAM)
	$(PREEMUCMD)
	$(EMUCMD) $<
	$(POSTEMUCMD)
 
clean:
	$(call RMFILES,$(OBJECTS))
	$(call RMFILES,$(DEPENDS))
	$(call RMFILES,$(REMOVES))
	$(call RMFILES,$(PROGRAM))
 
else # $(words $(TARGETLIST)),1
 
all test clean:
	$(foreach t,$(TARGETLIST),$(MAKE) TARGETS=$t $@$(NEWLINE))
 
endif # $(words $(TARGETLIST)),1
 
OBJDIRLIST := $(wildcard $(OBJDIR)/*)
 
zap:
	$(foreach o,$(OBJDIRLIST),-$(call RMFILES,$o/*.o $o/*.d $o/*.lst)$(NEWLINE))
	$(foreach o,$(OBJDIRLIST),-$(call RMDIR,$o)$(NEWLINE))
	-$(call RMDIR,$(OBJDIR))
	-$(call RMFILES,$(basename $(PROGRAM)).* $(STATEFILE))
 
love:
	@echo "Not war, eh?"
 
###################################################################
###  Place your additional targets in the additional Makefiles  ###
### in the same directory - their names have to end with ".mk"! ###
###################################################################
-include *.mk
1) Yes, some of us are big fans of “Convention over Configuration” paradigm.
2) Or should it rather be called “superprog”? ;-)
3) Please note that C source files have to have names ending with ”.c” (obvious, isn't it?) and assembler source files have to have names ending with ”.s” (less obvious). While The Makefile will accept also assembler files, which names end with ”.asm” or ”.a65”, use of endings different than ”.s” is deprecated.
4) Please note “OPTIONS” keyword must be given in uppercase.
5) By default called “Makefile.options” and saved into the project's main directory, next to the Makefile itself.
cc65/project_setup.1358378533.txt.gz · Last modified: 2013-01-17 00:22 by silverdr
 
Except where otherwise noted, content on this wiki is licensed under the following license: CC Attribution 3.0 Unported
Recent changes RSS feed Donate Powered by PHP Valid XHTML 1.0 Valid CSS Driven by DokuWiki