Use Makefile and sdcc to build a perfect automation for MCS-51 development

548 Views Asked by At

Since sdcc have some limitations, like compiling one file at a time, I've tried hard to write a Makefile to perfect the automation of MCS-51 development, which have some requirements:

  1. Source file (.c) expect main.c are stored in ProjectFolder/Sources/, while main.c are stored at the root of project folder.
  2. Headers are stored in ProjectFolder/Includes/.
  3. Outputs through compiling, linking and locating should be stored at ProjectFolder/Builds/
  4. Makefile should be smart enough to find all source files, instead of type their file name by hand.
  5. Makefile should be smart enough to if there are some files in Sources/, or there's only main.c in the project.

The file structure can be depicted like:

Project Folder
|
|- Sources
|  |
|  |(some source files, but OPTIONAL)
|
|- Includes
|  |
|  |(some headers, but OPTIONAL)
|
|- Builds
|  |
|  |(some .rel .o .hex files. OUTPUT here)
|
|- main.c
|
|- Makefile

Here's my solution but still have a problem. It cannot be used for project only have one file main.c which means no source file in Sources/.

INCLUDES = Includes/
SOURCES = Sources/
BUILDS = Builds/
CC = sdcc
CFLAGS = -o $(BUILDS)
LOADER = stcgal
LOADER_FLAGS = -P stc89

$(BUILDS)main.ihx: main.c $(BUILDS)main.rel
#   Link
    @$(CC) main.c $(shell find $(BUILDS) -name "*.rel" -not -name "main.rel" -maxdepth 1) $(CFLAGS)
    @echo Link & Locate Succeeded

$(BUILDS)main.rel: $(SOURCES) $(BUILDS)
#   Compile
    @for f in $(shell ls $(SOURCES)*.c) ; do \
        $(CC) -c $${f} $(CFLAGS) ; \
    done
    @echo Compile Succeeded

$(SOURCES):
    @mkdir $(SOURCES)

$(BUILDS):
    @mkdir $(BUILDS)

clean:
#   Remove all files in build folder
    @rm $(BUILDS)*
    @echo Build Folder Cleaned

load: $(BUILDS)main.ihx
#   Load data to MCU via USB port
    @$(LOADER) $(LOADER_FLAGS) -p $(shell ls /dev/tty.usbserial*) $(BUILDS)main.ihx
1

There are 1 best solutions below

0
AProgrammer On

Let's try something. First note that I've not looked at the load target.

Let's start with the same definition as you:

INCLUDES = Includes/
SOURCES = Sources/
BUILDS = Builds/
CC = sdcc

We need a variable with the source files from Sources. GNU Make has a wildcard functions which does the same thing as your find. See that I'm using := to have an immediate expansion of the value, so the wildcard will not be executed several times.

SRCFILES := $(wildcard $(SOURCES)*.c) 

Now a variable with the .rel files. It is build from main.rel and the SRCFILES value:

RELFILES := $(BUILDS)main.rel $(SRCFILES:$(SOURCES)%.c=$(BUILDS)%.rel)

Let's define another variable with the flag to pass so the Includes directory is searched:

CPPFLAGS = -I$(INCLUDES)

Now we can define pattern rules to describe how to build .rel files from .c files. I'm using an order-only prerequisite for the build directory:

$(BUILDS)%.rel: $(SOURCES)%.c | $(BUILDS)
        $(CC) $(CPPFLAGS) $(CFLAGS) -c -o $@ -c $<

$(BUILDS)%.rel: %.c | $(BUILDS)
        $(CC) $(CPPFLAGS) $(CFLAGS) -o $@ -c $<

Let's define some usability targets:

.PHONY: all clean

all: $(BUILDS)main.ihx

clean:
        rm $(BUILDS)*

And finally define how to build the targets which aren't handled by the pattern rules:

$(BUILDS)main.ihx: $(RELFILES) | $(BUILDS)
        $(CC) $(LDFLAGS) -o $@ $^ $(LOADLIBES) $(LDLIBS)

$(BUILDS):
        mkdir $(BUILDS)

I've used a few variables (CC, CPPFLAGS, CFLAGS, LDFLAGS, LOADLIBES, LDLIBS) in the same way as they are used by the built-in rules of GNU Make.

I've kept your makefile behavior. There are good reasons to have Makefiles targeting to build in the current directory. Explaining them and modifying the Makefile for that is out of scope for this answer, you may look at MadScientist's GNU Make White Papers and the GNU Make Manual.