Make ignores implicit rule dependency - c

In this partial Makefile when I execute make with no argument and .PHONY disabled it returns:
make: Nothing to be done for 'debug'.
With .PHONY enabled (or make -r) it go to 'build' without make any object file, so GCC don't can open any object file because there is no object files in target directories yet.
arm-none-eabi-gcc: error:
obj/debug/ThirdParty/STM32F1xx_HAL_Driver/Src/stm32f1xx_hal_adc.o: No
such file or directory
This makefile separate objs into folders obj/debug or obj/release.
File structure:
bin
inc
Src
ThirdParty // thirdparty source files
obj // mkdir -p should create this directories tree
debug
Src
ThirdParty
release
...
Makefile
.PHONY: build debug release clean $(COBJ) $(SOBJ)
# Main target
debug: CC_FLAGS += $(DEBUG)
debug: ELF = debug.elf
debug: OBJPATH = obj/debug
debug: COBJ = $(patsubst ./%,$(OBJPATH)/%,$(C:.c=.o)) # C contains c code
debug: SOBJ = $(patsubst ./%,$(OBJPATH)/%,$(S:.s=.o)) # S contains asm code
debug: build
release: CC_FLAGS += $(RELEASE)
release: OBJPATH = obj/release
release: COBJ = $(patsubst ./%,$(OBJPATH)/%,$(C:.c=.o))
release: SOBJ = $(patsubst ./%,$(OBJPATH)/%,$(S:.s=.o))
release: ELF = release.elf
release: build
build: $(COBJ) $(SOBJ)
$(CC) $(COBJ) $(SOBJ) $(LIBS) $(LD_FLAGS) -o bin/$(ELF)
%.o: %.c
echo $#
#mkdir -p $(OBJPATH)/$(dir $#)
$(CC) $(CC_FLAGS) -c $< -o $(OBJPATH)/$#
%.o: %.s
#mkdi -p $(OBJPATH)/$(dir $#)
$(CC) $(CC_FLAGS) -c $< -o $(OBJPATH)/$#
One sample of $(COBJ):
obj/debug/ThirdParty/FreeRTOS/queue.o
Linux x86-64
GNU Make 4.2.1
Arm-none-eabi-gcc - I think this don't matter

You are missing a critical note from the GNU make manual regarding target-specific variables:
As with automatic variables, these values are only available within the context of a target’s recipe (and in other target-specific assignments).
So, in your makefile:
debug: COBJ = $(patsubst ./%,$(OBJPATH)/%,$(C:.c=.o)) # C contains c code
debug: SOBJ = $(patsubst ./%,$(OBJPATH)/%,$(S:.s=.o)) # S contains asm code
build: $(COBJ) $(SOBJ)
...
At this point $(COBJ) and $(SOBJ) refer to the globally-set values of the COBJ and SOBJ variables (because as above, target-specific values are available only within the recipe, NOT in the prerequisites list). These variables have no global values, so they expand to the empty string, and your makefile essentially has just:
build:
...
with no prerequisites, which is why you're seeing the behavior you are.
There are multiple ways you could manage this. One is to use recursive make: remove the release: build and debug: build lines and add this:
debug release:
#$(MAKE) COBJ='$(COBJ)' SOBJ='$(SOBJ)' build
Another way is to use secondary expansion (you can't do it the way I originally suggested but you can do it with constructed variable names:
OBJPREFIX := obj
COBJ = $(patsubst ./%,$(OBJPREFIX)/$#/%,$(C:.c=.o))
SOBJ = $(patsubst ./%,$(OBJPREFIX)/$#/%,$(S:.s=.o))
# Main target
debug: CC_FLAGS += $(DEBUG)
debug: ELF = debug.elf
release: CC_FLAGS += $(RELEASE)
release: ELF = release.elf
.SECONDEXPANSION:
release debug: $$(COBJ) $$(SOBJ)
$(CC) $(COBJ) $(SOBJ) $(LIBS) $(LD_FLAGS) -o bin/$(ELF)
This uses the name of the target in the output object name.
Another way is to use generated makefiles.
You might consider reading the series of posts here: http://make.mad-scientist.net/category/metaprogramming/ (starting with the oldest one first).

As MadScientist mentioned the limited scope of target-specific variables I put out of makefile a selector and run make with TARGET = 'target' argument like this:
make TARGET = debug
make TARGET = release
No elegant but functional!
Makefile:
O = $(C:%.c=%.o)
O += $(S:%.s=%.o)
ifeq ($(TARGET), release)
ELF = bin/release.elf
CC_FLAGS += -O3
OBJPATH = obj/release
else
ELF = bin/debug.elf
CC_FLAGS += -g3
OBJPATH = obj/debug
endif
OBJ = $(addprefix $(OBJPATH)/, $(O))
all: makepath build
build: $(OBJ)
#echo ---- LINKING ----
$(CC) $(OBJ) $(LIBS) $(LD_FLAGS) -o $(ELF)
makepath:
#mkdir -p $(dir $(OBJ))
$(OBJPATH)/%.o:%.c
#echo ---- C ----
$(CC) $(CC_FLAGS) -c $< -o $#
$(OBJPATH)/%.o:%.s
#echo ---- S ----
$(CC) $(CC_FLAGS) -c $< -o $#
clean:
find -name *.o -delete
find -name *.elf -delete

Related

Using Static Pattern Rule causes single source change to rebuild all

I am trying to fix a Makefile that currently does not detect source file changes due to a lack of dependency on the .c source files. I have implemented a static pattern rule but that is not able to rebuild a single object file when a single source file changes. It will rebuild all of the changes.
I have the following:
ARCHITECTURE_DIR = arm
BUILD_DIR = build
OUTPUT_DIR = $(BUILD_DIR)/$(ARCHITECTURE_DIR)
SDK_DIR = sdks/v4.0
SOURCE_FILES = \
$(SDK_DIR)/alarm.c \
...
CSOURCES = $(filter %.c, $(SOURCE_FILES))
COBJS = $(addprefix $(OUTPUT_DIR)/,$(CSOURCES:.c=.o))
all: PROLOGUE $(COBJS)
$(LD) $(COBJS) -o ...
Previously, the file had the following:
$(COBJS): %.o:
#echo 'Building $(notdir $(#:%.o=%.c))...'
#$(CC) $(CFLAGS) -o $# $(filter %$(#:$(OUTPUT_DIR)/%.o=%.c),$(CSOURCES)) > /dev/null \
But this does not have a dependency on the .c files and thus does not recognize changes made and rebuild. I do not want to clean any time I make a source change. I changed it to the following:
COBJS = $(CSOURCES:.c=.o)
$(COBJS): %.o: %.c
#echo 'Building $(notdir $(#:%.o=%.c))...'
#$(CC) $(CFLAGS) -o $(addprefix $(OUTPUT_DIR)/,$#) $< > /dev/null \
But this causes it to get rebuilt every time since the object files are getting put into a different directory. Is there a way to make it such that it can find these object files and not rebuilt if necessary?
Thanks for your help!
Not sure I fully understand where you want to store your different products but let's give it a try. From what you show of your Makefile I will assume that you want the object file corresponding to sdks/v4.0/alarm.c to be build/arm/sdks/v4.0/alarm.o. I will also assume that your target executable is build/arm/sdks/v4.0/my_executable.
You could rework your compilation pattern rule to use the real source and target files:
$(OUTPUT_DIR)/%.o: %.c
#echo 'Building $(#F)...'
#mkdir -p $(#D)
#$(CC) $(CFLAGS) -o $# $<
Note that the recipe creates the destination directory before compiling (mkdir -p ...) and that it makes use of automatic variables ($(#F), $(#D)) to get the basename and directory of the target.
You could use the real target file for your link rule to avoid useless rebuild; of course, you can also have a phony target used as an alias for the real target:
EXEC := $(OUTPUT_DIR)/$(SDK_DIR)/my_executable
...
.PHONY: all
all: $(EXEC)
...
$(EXEC): PROLOGUE $(COBJS)
$(LD) $(COBJS) -o $#
All in all, you could try something like:
ARCHITECTURE_DIR := arm
BUILD_DIR := build
OUTPUT_DIR := $(BUILD_DIR)/$(ARCHITECTURE_DIR)
SDK_DIR := sdks/v4.0
SOURCE_FILES := \
$(SDK_DIR)/alarm.c \
...
CSOURCES := $(filter %.c,$(SOURCE_FILES))
COBJS := $(patsubst %.c,$(OUTPUT_DIR)/%.o,$(CSOURCES))
EXEC := $(OUTPUT_DIR)/$(SDK_DIR)/my_executable
.PHONY: all
all: $(EXEC)
$(EXEC): PROLOGUE $(COBJS)
$(LD) $(COBJS) -o $#
$(OUTPUT_DIR)/%.o: %.c
#echo 'Building $(#F)...'
#mkdir -p $(#D)
#$(CC) $(CFLAGS) -c -o $# $<

Adjust Makefile to only recompile files if they have changed

I have a question regarding my Makefile. The Makefile intends to compile C files containing code for a STM8 µC using the Cosmic compiler. The problem is that everytime I invoke the build target, all available source file are getting recompiled without any change. I'm really new in the field of Makefiles and I have no idea how to fix it.
The second questions is related to the two targets "%.o: src/%.c" and %.o: src/stm8/%.c. They do exactly the same and I would prefer a generic one that is able to deal with all subdirectories within the src folder. With this solution it ist required to add an additional rule for each subfolder of the src folder
#***************PROJECT INFORMATIONS****************
PROJECT_NAME = stm8template
MODULES = stm8
#****************SET BUILD MODE*********************
ifeq ($(MODE), )
MODE=Debug
endif
#***************DIRECTORY INFORMATION***************
SRCDIR = src
INCLUDES = includes
OUTPUT_DIR = bin/$(MODE)
#**************HELPER FUNCTIONS*********************
rwildcard=$(wildcard $1$2) $(foreach d,$(wildcard $1*),$(call rwildcard,$d/,$2))
#***************FILE SPECIFICATIONS***************
SOURCE_FILES = $(foreach d, $(call rwildcard,$(SRCDIR),*.c), $(notdir $d))
OBJECT_FILES = $(patsubst %.c, %.o, $(SOURCE_FILES))
HEADER_FILES = $(wildcard $(INCLUDES)/*.h) $(wildcard $(INCLUDES)/**/*.h)
INCLUDE_DIRS_PARAM=$(foreach d, $(MODULES), -iincludes/$d) -iincludes -iC:\Hstm8
#***************COMPILER INFORMATIONS***************
CC = cxstm8
LIBS = -l +mods0
ifeq ("$(MODE)","Debug")
CFLAGS = $(INCLUDE_DIRS_PARAM) -cl$(OUTPUT_DIR) -co$(OUTPUT_DIR) -pxp -no -pp +debug
else
CFLAGS = $(INCLUDE_DIRS_PARAM) -cl$(OUTPUT_DIR) -co$(OUTPUT_DIR) -pxp -no -pp
endif
#***************LINKER INFORMATIONS***************
LINKFILE=$(OUTPUT_DIR)\$(PROJECT_NAME).lkf
OUTFILE=$(PROJECT_NAME)
LFLAGS = -lC:\Lib
#*************FLASHER CONFIGURATIONS***************
FLASHER_PATH="C:\Program Files (x86)\STMicroelectronics\st_toolset\stvp\STVP_CmdLine.exe"
DEVICE=STM8S105x6
PORT=USB
PROG_MODE=SWIM
BOARD_NAME=ST-LINK
FLASHER_PARAM = -no_loop
#***************BUILT TARGETS***************
all: build run
%.o: src/%.c
$(info ********** Compile $< ***********)
$(CC) $(CFLAGS) $(LIBS) $<
%.o: src/stm8/%.c
$(info ********** Compile $< ***********)
$(CC) $(CFLAGS) $(LIBS) $<
build: $(OBJECT_FILES)
$(info ********** Build the Application ***********)
clnk -m $(OUTPUT_DIR)\$(OUTFILE).map -o $(OUTPUT_DIR)\$(OUTFILE).sm8 $(LINKFILE)
cvdwarf $(OUTPUT_DIR)\$(OUTFILE).sm8
chex -o $(OUTPUT_DIR)\$(OUTFILE).s19 $(OUTPUT_DIR)\$(OUTFILE).sm8
run:
$(info ********** Flashing the Application ***********)
$(FLASHER_PATH) -BoardName=$(BOARD_NAME) -Device=$(DEVICE) -Port=$(PORT) -ProgMode=$(PROG_MODE) -FileProg="$(OUTPUT_DIR)\$(OUTFILE).s19" $(FLASHER_PARAM)
The build target never gets created, so the commands after it are executed every time you run make (or make all or make build), so the program is linked each time.
Change your build target so that it is phony:
.PHONY: build clean
and so that it depends on the program, not the object files:
build: $(OUTPUT_DIR)\$(OUTFILE).sm8
and then have a rule (recipe) that builds the program if the object files are more recent:
$(OUTPUT_DIR)\$(OUTFILE).sm8: $(OBJECT_FILES)
$(info ********** Build the Application ***********)
clnk -m $(OUTPUT_DIR)\$(OUTFILE).map -o $(OUTPUT_DIR)\$(OUTFILE).sm8 $(LINKFILE)
cvdwarf $(OUTPUT_DIR)\$(OUTFILE).sm8
chex -o $(OUTPUT_DIR)\$(OUTFILE).s19 $(OUTPUT_DIR)\$(OUTFILE).sm8
It isn't 100% clear to me that I chose the correct suffix for the program. I would also create series of macros to avoid the repetition I see:
OUTFILE.sm8 = $(OUTPUT_DIR)\$(OUTFILE).sm8
OUTFILE.s19 = $(OUTPUT_DIR)\$(OUTFILE).s19
OUTFILE.map = $(OUTPUT_DIR)\$(OUTFILE).map
build: $(OUTFILE.sm8)
$(OUTFILE.sm8): $(OBJECT_FILES)
$(info ********** Build the Application ***********)
clnk -m $(OUTFILE.map) -o $(OUTFILE.sm8) $(LINKFILE)
cvdwarf $(OUTFILE.sm8)
chex -o $(OUTFILE.s19) $(OUTFILE.sm8)
Also, since I work on Unix mostly, I'd use / instead of \, but that's a minor detail.
Update:
Thank you all for your help. I changed the Makefile in the way shown below. The second problem is now fixed but the first problem still remains.
Every time the build rule is invoked all .c files are recompiled. Compiling only the changed files is the main purpose/benefit of using make, I thought. So something is wrong but unfortunately I don't find the mistake.
#***************PROJECT INFORMATIONS****************
PROJECT_NAME = stm8template
MODULES = stm8
#****************SET BUILD MODE*********************
ifeq ($(MODE), )
MODE=Debug
endif
#***************DIRECTORY INFORMATION***************
SRCDIR = src
INCLUDES = includes
#**************HELPER FUNCTIONS*********************
rwildcard=$(wildcard $1$2) $(foreach d,$(wildcard $1*),$(call rwildcard,$d/,$2))
#***************FILE SPECIFICATIONS***************
SOURCE_FILES = $(foreach d, $(call rwildcard,$(SRCDIR),*.c), $(notdir $d))
OBJECT_FILES = $(patsubst %.c, %.o, $(call rwildcard,$(SRCDIR),*.c))
HEADER_FILES = $(wildcard $(INCLUDES)/*.h) $(wildcard $(INCLUDES)/**/*.h)
INCLUDE_DIRS_PARAM=$(foreach d, $(MODULES), -iincludes/$d) -iincludes -iC:\Hstm8
#***************COMPILER INFORMATIONS***************
CC = cxstm8
LIBS = -l +mods0
ifeq ("$(MODE)","Debug")
CFLAGS = $(INCLUDE_DIRS_PARAM) -cl$(OUTPUT_DIR) -co$(OUTPUT_DIR) -pxp -no -pp +debug
else
CFLAGS = $(INCLUDE_DIRS_PARAM) -cl$(OUTPUT_DIR) -co$(OUTPUT_DIR) -pxp -no -pp
endif
#***************LINKER INFORMATIONS***************
LINKFILE=$(OUTPUT_DIR)\$(PROJECT_NAME).lkf
LFLAGS = -LC:\Lib
#*******************OUTPUT FILES********************
OUTPUT_DIR = bin/$(MODE)
OUTFILE=$(PROJECT_NAME)
OUTFILE.sm8 = $(OUTPUT_DIR)\$(OUTFILE).sm8
OUTFILE.s19 = $(OUTPUT_DIR)\$(OUTFILE).s19
OUTFILE.map = $(OUTPUT_DIR)\$(OUTFILE).map
TARGET_FILE=$(OUTPUT_DIR)\$(PROJECT_NAME).elf
#*************FLASHER CONFIGURATIONS***************
FLASHER_PATH="C:\Program Files (x86)\STMicroelectronics\st_toolset\stvp\STVP_CmdLine.exe"
DEVICE := STM8S105x6
PORT=USB
PROG_MODE=SWIM
BOARD_NAME=ST-LINK
FLASHER_PARAM = -no_loop
#***************BUILT TARGETS***************
.PHONY: all run build clean
all: build run
%.o: %.c
$(info ********** Compile $< ***********)
$(CC) $(CFLAGS) $(LIBS) $<
build: $(OUTPUT_DIR)\$(PROJECT_NAME).elf
$(TARGET_FILE): $(OBJECT_FILES)
$(info ********** Build the Application ***********)
clnk -m $(OUTFILE.map) -o $(OUTFILE.sm8) $(LINKFILE)
cvdwarf $(OUTFILE.sm8)
chex -o $(OUTFILE.s19) $(OUTFILE.sm8)
run:
$(info ********** Flashing the Application ***********)
$(FLASHER_PATH) -BoardName=$(BOARD_NAME) -Device=$(DEVICE) -Port=$(PORT) -ProgMode=$(PROG_MODE) -FileProg="$(OUTPUT_DIR)\$(OUTFILE).s19" $(FLASHER_PARAM)

Makefile based on sub-directories

I have a C project program that I want to compile and manage with a makefile:
./include: where headers resides
a.h , b.h , c.h
./obj: where object will be stored
-
./src: where source file resides
main.c , a.c , b.c , c.c
How can I create a makefile to create an executable test , and put object files to obj folder ?
You can use this template I made for simple project. I don't know which compiler you are using but you can configure it with the variables in the first sections as well as other useful configs:
#=============================================================================
# Project related variables
EXENAME = test
FILEIDENTIFIER = .c
COMPFLAGS = -pedantic -Wall
COMPSTANDARD = -std=c11
EXELINKS = -lm
DBARGS = -g
BUILDDIR = build/
BINARY_OUTPUT_DIR = $(BUILDDIR)bin/
OBJDIR = obj/
SOURCEDIRS = src/
INCLUDEDIRS = include/
LIBSDIRS = /usr/lib/
#=============================================================================
# Commands variables
COMPILER = gcc
LINKER = ld -r
DISPLAY = printf
MKDIR = mkdir -p
RMDIR = rmdir
RM = rm -f
#=============================================================================
# Other
VOIDECHO = > /dev/null 2>&1
#=============================================================================
# Semi-automatic variables
EXEFINALOBJ = $(OBJDIR)$(EXENAME).o
EXEFINAL = $(BINARY_OUTPUT_DIR)$(EXENAME)
INCLUDEARGS = $(addprefix -I,$(INCLUDEDIRS))
#=============================================================================
# Automatic variables
SOURCES = $(foreach sourcedir,$(SOURCEDIRS),$(wildcard $(sourcedir)**/*$(FILEIDENTIFIER)) $(wildcard $(sourcedir)*$(FILEIDENTIFIER)))
OBJECTS = $(patsubst %$(FILEIDENTIFIER),%.o,$(foreach sourcedir,$(SOURCEDIRS),$(subst $(sourcedir),$(OBJDIR),$(wildcard $(sourcedir)**/*$( FILEIDENTIFIER)) $(wildcard $(sourcedir)*$(FILEIDENTIFIER)))))
GENERATED_FILES = $(OBJECTS) $(EXEFINALOBJ) $(EXEFINAL)
GENERATED_FOLDERS = $(OBJDIR) $(BINARY_OUTPUT_DIR) $(BUILDDIR)
#=============================================================================
# Special GNU make variables
VPATH = $(SOURCEDIRS)
#=============================================================================
# Rules: Phony Targets
.PHONY: silent
silent:
#make --silent $(EXEFINAL)
.PHONY: all
all: $(EXEFINAL)
.PHONY: debug
debug: COMPFLAGS += $(DBARGS)
debug: all
.PHONY: clean
clean:
#$(DISPLAY) "\n-> Cleaning files...\n"
#$(DISPLAY) " $(foreach file,$(GENERATED_FILES),$(if $(wildcard $(file)),- Removing file $(file)\n,\b))"
#$(RM) $(GENERATED_FILES)
#$(DISPLAY) "\n-> Cleaning folders...\n"
#$(DISPLAY) " $(foreach folder,$(GENERATED_FOLDERS),$(if $(wildcard $(folder)),- Removing folder $(folder)\n,\b))"
#$(RMDIR) $(GENERATED_FOLDERS) $(VOIDECHO) || true
#$(DISPLAY) "\n"
#=============================================================================
# Rules: File Targets
$(EXEFINAL): $(EXEFINALOBJ)
#$(DISPLAY) "\n - Building $# from $^... "
#$(MKDIR) $(BINARY_OUTPUT_DIR)
$(COMPILER) $(EXEFINALOBJ) -o $# $(LIBARGS) $(EXELINKS)
#$(DISPLAY) "Done"
#$(DISPLAY) "\n\n"
$(EXEFINALOBJ): $(OBJECTS)
#$(DISPLAY) "\n - Merging objects files into $#... "
$(LINKER) $(OBJECTS) -o $#
#$(DISPLAY) "Done"
$(OBJDIR)%.o: %$(FILEIDENTIFIER)
#$(DISPLAY) "\n - Building $# from $^... "
#$(MKDIR) $(OBJDIR)
$(COMPILER) $(COMPFLAGS) $(COMPSTANDARD) $(INCLUDEARGS) -c $^ -o $#
#$(DISPLAY) "Done"
The actual configuration is for Linux and uses gcc and ld. It support subfolders for sources and 4 targets are defined:
silent (default): silent build
all: verbose build
debug: debug build
clean: remove files and folders generated by the makefile
If you want to understand exactly how the Makefile work, as MadScientist wrote, look at the GNU make manual at https://www.gnu.org/software/make/manual/html_node/Introduction.html

Make error when trying to create *.o

I'm at my wits end here because of this extremely stupid error I'm getting from my makefile.
I finally gave up stripped the makefile down to just two lines:
%.o: %.c
gcc -c -o $# $< -I../inc
Command: make . The output:
make: *** No targets. Stop.
The spaces at the beginning are real tabs instead of spaces. The c files are in the same directory. If instead of %.o I give, say, file1.o and file1.c instead of %.c, all is well (file1.o gets created). I see plenty of examples on the 'net that use the % operator, though. If I include a clean: target, it is promptly found, like so:
%.o: %.c
gcc -c -o $# $< -I../inc
clean:
echo "this is clean!"
Command: make . The output:
echo "this is clean!"
this is clean!
Please help me out here as I'm totally clueless about what's wrong with my targets. In the second sample (the one with clean target), I guess the clean target is found and acted upon as the first one is 'invalid' somehow.
Looks like you forgot to write a target. You have just written rules of how to compile, but not what to do with those objects. I mean, I miss something like:
my_executable_file: *.o
gcc -o my_executable_file *.o
EDIT:
What is set before is true, you need a target. But as you want only to compile, your target should be something like:
OBJECTS = file.o #and whatever objects you need, as a list separated by commas
And then your target:
my_objects: $(OBJECTS)
So putting it all together:
OBJECTS = file.o #and whatever objects you need, as a list separated by commas
my_objects: $(OBJECTS)
%.o: %.c
gcc -c -o $# $< -I../inc
Below is the Makefile that will enable to any number of targets to compile
OBJ := file.o
all: $(OBJ)
%.o: %.c
gcc -c -o $# $< -I../inc
clean:
echo "this is clean!"
Here, OBJ will be the list of the files that you want to compile , like here it is file.c
Add the file name you want to compile to OBJ, when make is called it will build the target all first which depends on the OBJ.
To build OBJ the gcc command is used.
When an explicit target is not given to make, then the first (non-pattern?) target in the Makefile is used. In the case above, it is the clean target.
I see your intention to make only .o files (can be needed for creation of libraries).
You can modify your Makefile to build only .o files or build only executable by using the same Makefile
For the below directory structure (using tree command)
# tree .
.
|-- include
| `-- head.h
|-- Makefile
|-- obj
`-- src
`-- main.c
Makefile
# GNU Makefile #
# Some Variables #
CC := gcc
RM := rm
MV := mv
# Phony Targets #
.PHONY: clean
.PHONY: move
# Path for Source, Object and Include #
SRC_PATH := ./src/
OBJ_PATH := ./obj/
INCLUDE_PATH := ./include/
# Source and Object File Names #
SRC := $(SRC_PATH)main.c
OBJ := $(SRC:c=o) # Substitutes all SRC but with .c as .o (main.c becomes main.o) #
# Executable Name #
TARGET := exe
# Building Binary - use 'make' #
binary: $(TARGET) move
$(TARGET): $(OBJ)
$(CC) -o $(TARGET) $^
# Building only Object Files - use 'make object_only' #
object_only : $(OBJ) move
$(OBJ): $(SRC)
$(CC) -c -o $# $< -I $(INCLUDE_PATH)
# This rule is for moving .o files to ./obj directory (More Organised) #
move:
$(MV) $(SRC_PATH)*.o $(OBJ_PATH)
# For Cleaning - use 'make clean' #
clean:
echo "Cleaning Up!"
$(RM) -rfv $(TARGET) $(OBJ_PATH)*.o $(SRC_PATH)*.o # Delete .o and executable #
Execution:
To build only object files use
$ make object_only
To build object files and executable, use
$ make

How can I create a Makefile for C projects with SRC, OBJ, and BIN subdirectories?

A few months ago, I came up with the following generic Makefile for school assignments:
# ------------------------------------------------
# Generic Makefile
#
# Author: yanick.rochon#gmail.com
# Date : 2010-11-05
#
# Changelog :
# 0.01 - first version
# ------------------------------------------------
# project name (generate executable with this name)
TARGET = projectname
CC = gcc -std=c99 -c
# compiling flags here
CFLAGS = -Wall -I.
LINKER = gcc -o
# linking flags here
LFLAGS = -Wall
SOURCES := $(wildcard *.c)
INCLUDES := $(wildcard *.h)
OBJECTS := $(SOURCES:.c=*.o)
rm = rm -f
$(TARGET): obj
#$(LINKER) $(TARGET) $(LFLAGS) $(OBJECTS)
#echo "Linking complete!"
obj: $(SOURCES) $(INCLUDES)
#$(CC) $(CFLAGS) $(SOURCES)
#echo "Compilation complete!"
clean:
#$(rm) $(TARGET) $(OBJECTS)
#echo "Cleanup complete!"
This will basically compile every .c and .h file to generate .o files and the executable projectname all in the same folder.
Now, I'd like to push this a little. How can I write a Makefile to compile a C project with the following directory structure?
./
./Makefile
./src/*.c;*.h
./obj/*.o
./bin/<executable>
In other words, I'd like to have a Makefile that compiles C sources from ./src/ into ./obj/ and then link everything to create the executable in ./bin/.
I've tried to read different Makefiles, but I simply can't make them work for the project structure above; instead, the project fails to compile with all sorts of errors. Sure, I could use full blown IDE (Monodevelop, Anjuta, etc.), but I honestly prefer to stick with gEdit and the good ol' terminal.
Is there a guru who can give me a working solution, or clear information about how this can be done? Thank you!
** UPDATE (v4) **
The final solution :
# ------------------------------------------------
# Generic Makefile
#
# Author: yanick.rochon#gmail.com
# Date : 2011-08-10
#
# Changelog :
# 2010-11-05 - first version
# 2011-08-10 - added structure : sources, objects, binaries
# thanks to http://stackoverflow.com/users/128940/beta
# 2017-04-24 - changed order of linker params
# ------------------------------------------------
# project name (generate executable with this name)
TARGET = projectname
CC = gcc
# compiling flags here
CFLAGS = -std=c99 -Wall -I.
LINKER = gcc
# linking flags here
LFLAGS = -Wall -I. -lm
# change these to proper directories where each file should be
SRCDIR = src
OBJDIR = obj
BINDIR = bin
SOURCES := $(wildcard $(SRCDIR)/*.c)
INCLUDES := $(wildcard $(SRCDIR)/*.h)
OBJECTS := $(SOURCES:$(SRCDIR)/%.c=$(OBJDIR)/%.o)
rm = rm -f
$(BINDIR)/$(TARGET): $(OBJECTS)
#$(LINKER) $(OBJECTS) $(LFLAGS) -o $#
#echo "Linking complete!"
$(OBJECTS): $(OBJDIR)/%.o : $(SRCDIR)/%.c
#$(CC) $(CFLAGS) -c $< -o $#
#echo "Compiled "$<" successfully!"
.PHONY: clean
clean:
#$(rm) $(OBJECTS)
#echo "Cleanup complete!"
.PHONY: remove
remove: clean
#$(rm) $(BINDIR)/$(TARGET)
#echo "Executable removed!"
First, your $(OBJECTS) rule is problematic, because:
it's kind of indiscriminate, making all sources prerequisites of every object,
it often uses the wrong source (as you discovered with file1.o and file2.o)
it tries to build executables instead of stopping at objects, and
the name of the target (foo.o) is not what the rule will actually produce (obj/foo.o).
I suggest the following:
OBJECTS := $(SOURCES:$(SRCDIR)/%.c=$(OBJDIR)/%.o)
$(OBJECTS): $(OBJDIR)/%.o : $(SRCDIR)/%.c
$(CC) $(CFLAGS) -c $< -o $#
#echo "Compiled "$<" successfully!"
The $(TARGET) rule has the same problem that the target name does not actually describe what the rule builds. For that reason, if you type make several times, Make will rebuild the target each time, even though there is no reason to. A small change fixes that:
$(BINDIR)/$(TARGET): $(OBJECTS)
$(LINKER) $# $(LFLAGS) $(OBJECTS)
#echo "Linking complete!"
Once that's all in order, you might consider more sophisticated dependency handling; if you modify one of the header files, this makefile will not know which objects/executables must be rebuilt. But that can wait for another day.
EDIT:
Sorry, I omitted part of the $(OBJECTS) rule above; I've corrected it. (I wish I could use "strike" inside a code sample.)
You can add the -I flag to the compiler flags (CFLAGS) to indicate where the compiler should look for source files , and the -o flag to indicate where the binary should be left:
CFLAGS = -Wall -I./src
TARGETPATH = ./bin
$(TARGET): obj
#$(LINKER) $(TARGETPATH)/$(TARGET) $(LFLAGS) $(OBJECTS)
#echo "Linking complete!"
In order to drop the object files into the obj directory, use the -o option when compiling. Also, look at the $# and $< automatic variables.
For example, consider this simple Makefile
CFLAGS= -g -Wall -O3
OBJDIR= ./obj
SRCS=$(wildcard *.c)
OBJS=$(SRCS:.c=.o )
all:$(OBJS)
%.o: %.c
$(CC) $(CFLAGS) -c $< -o $(OBJDIR)/$#
Update>
By looking at your makefile, I realize you are using the -o flag. Good. Continue using it, but add a target directory variable to indicate where the output file should be written.
I have stopped writing makefiles these days, if your intention is to learn go ahead, else you have good makefile generator that comes with eclipse CDT. If you want some maintainability / multiple project support with in your build tree, have a look at the following -
https://github.com/dmoulding/boilermake I found this pretty good..!

Resources