Search code examples
makefilexc8

Create intermediate files in build directory from makefile


I have to make a XC8 project in Ubuntu terminal with makefile. The directory structure is like this

Project/lib/GPIO/gpio.c
Project/lib/GPIO/gpio.h
Project/main.c
Project/Makefile

Project/build/lib/GPIO

The makefile is generating intermediate files in the Project directory itself. Actually makefile will goto each location and generates intermediate files for each .c file.

Like this

Project/gpio.d
Project/gpio.p1
Project/gpio.pre
Project/<other files>

But I wanted to generate them in Project/build/lib/GPIO instead. i.e

Project/build/lib/GPIO/gpio.d
Project/build/lib/GPIO/gpio.p1
Project/build/lib/GPIO/gpio.pre
Project/build/<other files>

Makefile:

CHIP := 18F4580
CC := xc8
INCLUDE_PATH := /opt/microchip/xc8/v2.10/include/
PROG := /usr/share/tinybldlin/tinybldlin.py
PORT := /dev/ttyUSB0
AR := libr

SRC_DIR := $(wildcard *.c)
INC_DIR := $(wildcard *.h)

LIB_DIR := \
. \
lib \
lib/GPIO

BUILD_DIR := \
build/ \
build/lib/GPIO

TARGET := pic${CHIP}
FLASH_REGION := 0-3000

ifeq (${SRCS},)
SRCS := $(foreach libdir, $(LIB_DIR),  $(wildcard $(libdir)/*.c))
endif
ifeq (${INCS},)
INCS := $(foreach libdir, $(LIB_DIR),  $(wildcard $(libdir)/*.h))
endif
ifeq (${OBJS},)
OBJS := $(SRCS:.c=.obj)
endif
ifeq (${P1},)
OBJS := $(SRCS:.c=.p1)
endif

MIN_CFLAGS := --chip=${CHIP} -I${INCLUDE_PATH} --ROM=${FLASH_REGION}
# The following MSG_CFLAGS controls the compiler messages
MSG_CFLAGS := -Q
# The following OPT_CFLAGS reduces the code size
OPT_CFLAGS := --opt=all
# The following OUT_CFLAGS controls the generated outputs
OUT_CFLAGS := --asmlist --summary=psect,mem -M${TARGET}.map 
EXTRA_CFLAGS += ${MSG_CFLAGS} ${OPT_CFLAGS} ${OUT_CFLAGS}
CFLAGS := ${MIN_CFLAGS} ${EXTRA_CFLAGS} -DCOMPILER=${COMPILER}
ARFLAGS := r

${TARGET}.hex: ${OBJS}
    ${CC} ${CFLAGS} -intel $^ -o$@
    ${MAKE} xclean

${TARGET}.bin: ${OBJS}
    ${CC} ${CFLAGS} -bin $^ -o$@
    ${MAKE} xclean

%.p1: %.c ${INC_DIR}
    ${CC} ${CPPFLAGS} ${CFLAGS} --pass1 $<

%.obj: %.c ${INC_DIR}
    ${CC} ${CPPFLAGS} ${CFLAGS} -C $<

%.as: %.c ${INC_DIR}
    ${CC} ${CPPFLAGS} ${CFLAGS} -S $<

%.lib: %.obj
    ${AR} ${ARFLAGS} $@ $<

Solution

  • The first thing I'll say is that this answer relies on your using GNU make. You don't say for sure that you are but since you're using pattern rules I will assume so. Note that what you want to do is not possible with standard POSIX make (unless you want to write out explicit rules for every target to be built).

    You need two things:

    1. Tell your compiler what you want to do
    2. Tell make what you want to do

    For #1, by default the compiler will generate the output which is the input file name, minus the extension (e.g. .c) plus a specific extension (e.g. .as). Since your input file is lib/GPIO/gpio.c the compiler generates gpio.as. Most compilers support a -o option which allows you to rename the output: you'll have to check your compiler's documentation if this doesn't work. So, you'd want to write your rule:

    %.as: %.c ${INC_DIR}
            ${CC} ${CPPFLAGS} ${CFLAGS} -S -o $@ $<
    

    (etc.). Now when you compile lib/GPIO/gpio.c into lib/GPIO/gpio.as, make will set $@ to lib/GPIO/gpio.as and invoke your compiler such that it creates lib/GPIO/gpio.as not gpio.as.

    But you wanted to put the output files in a build subdirectory. Now that you've convinced your compiler to write files where make tells it to, you have to tell make where to put the output.

    So first, when you generate the output files to be created you have to create them with the correct path:

    OBJS := $(SRCS:.c=.p1)
    

    That converts lib/GPIO/gpio.c into lib/GPIO/gpio.p1 which isn't what you want. You need this:

    OBJS := $(SRCS:%.c=build/%.p1)
    

    Now, make knows you want to build build/lib/GPIO/gpio.p1 instead. However now you'll get an error because make doesn't know how to create that file. You've told it how to build a %.p1 from %.c, but the % must be an exact text match between the target and prerequisite, and here the target % matches build/lib/GPIO/gpio but there's no build/lib/GPIO/gpio.c so the rule doesn't match. You need to rewrite the rule like this:

    build/%.p1: %.c ${INC_DIR}
            ${CC} ${CPPFLAGS} ${CFLAGS} --pass1 -o $@ $<
    

    Now it should work. You might also want to add in a mkdir to ensure the directory exists:

    build/%.p1: %.c ${INC_DIR}
            @mkdir -p ${@D}
            ${CC} ${CPPFLAGS} ${CFLAGS} --pass1 -o $@ $<