Makefile based on sub-directories - c

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

Related

MS-Windows MakeFIles

I am trying to write makefile for ms-windows application. The idea is very simple. I have to generate the output .o, .exe files into the build directory. I have written makefile which will compile source directory, but is not able to generate the build directory and compilation process is perfectly working.
Please have a look for the makefile. It looks like i am not able to generate the build directory using mkdir command.
CC= gcc
CFLAG= -g -Wall
TARGET_EXEC ?= test.exe
RELEASE= Release
RELEASE_BIN= bin
RELEASE_CNFG= config
RELEASE_LOG= log
RELEASE_DATA=data
BUILD_DIR ?= build
SRC_DIRS ?= src
#OS dependent cleaning command
ifdef OS
RM = rd /s /q
else
ifeq ($(shell uname), Linux)
RM = rm -f
endif
endif
SRCS := $(wildcard $(SRC_DIRS)/*.c)
OBJS := $(SRCS:%=$(BUILD_DIR)/%.o)
DEPS := $(OBJS:.o=.d)
INC_DIRS := $(wildcard $(SRC_DIRS) -type d)
INC_FLAGS := $(addprefix -I,$(INC_DIRS))
#find the directory for the include file
#this is best otpion to search curl directory
USR_INC := /usr/include
FILES :=
INCLUDES = -Iinclude
LIBS= -lm
CPPFLAGS ?= $(INC_FLAGS) -MMD -MP
$(BUILD_DIR)/$(TARGET_EXEC): $(OBJS)
$(CC) $(OBJS) -o $# $(LIBS) $(LLFLAGS)
# c source
$(BUILD_DIR)/%.c.o: %.c
#echo Compiling $<
# here I am trying to generate the file into the build/src/xxxc.o
# $(MKDIR_P) $#
$(CC) $(CFLAG) -c $< -o $# $(INCLUDES)
.PHONY: install
install:
# echo "Installing..........."
# echo "Creating $(RELEASE) Directory..."
# $(MKDIR_P) $(RELEASE)
# echo "Creating $(RELEASE)/$(RELEASE_BIN) Directory.."
# $(MKDIR_P) $(RELEASE)/$(RELEASE_BIN)
# echo "Creating $(RELEASE)/$(RELEASE_CNFG) Directory..."
# $(MKDIR_P) $(RELEASE)/$(RELEASE_CNFG)
# echo "Creating $(RELEASE)/$(RELEASE_LOG) Directory..."
# $(MKDIR_P) $(RELEASE)/$(RELEASE_LOG)
# echo "Creating $(RELEASE)/$(RELEASE_DATA) Directory..."
# $(MKDIR_P) $(RELEASE)/$(RELEASE_DATA)
# echo "Makking Release folder ready.."
#cp $(BUILD_DIR)/$(TARGET_EXEC) $(RELEASE)/$(RELEASE_BIN)
# echo "copy certificate file to Release"
#cp -r cert $(RELEASE)
# echo "copy .ini file to Release"
#cp -r config/*.ini $(RELEASE)/$(RELEASE_CNFG)
.PHONY: clean
clean:
$(RM) $(BUILD_DIR) $(RELEASE)
-include $(DEPS)
MKDIR_P ?= mkdir
I do understand what your are trying. But please be aware that some differences in platforms and dependencies will force you to create something that is similar to a "configure" and "make" step.
So you will start to generate a custom Makefile in a tool to be invented by you sooner or later :).
It is mentioned in the comments: This has already been done and cmake is now "industrial standard"
It works on Windows, Linux, OSX and so on with great toolchain support. Have a look at it!
Example:
cmake_minimum_required(VERSION 3.4)
project(HelloWorldCMake)
set(MY_SOURCES
hello_world.c
)
add_executable(HelloWorldCMake
${MY_SOURCES}
)
And hello_world.c
#include <stdio.h>
int main()
{
printf("Hello world (cmake)");
}
If you want to link additional libraries, please research
the CMake command: target_link_libraries
https://cmake.org/cmake/help/latest/command/target_link_libraries.html

Makefile that handles subdirectories and puts object files in a object folder tree

I have a question about Makefiles with subdirectories:
I have a program with the structure
---src
|
|-> main.c
|-> morestuff.c
|-> [...]
|-> builtins -> builtin.c
-> builtin2.c
---obj
---inc
Now what I want to do is: I want to run make such that I create object files in my object directory (order structure not necessarily needed) and that I (obviously) create an executable.
I am able to do that without the subdirectories, but my pattern rules break, once I try to include the subdirectories...
My current approach (without subdirectories) looks something like this:
NAME = minishell
SRC_DIR = src/
OBJ_DIR = obj/
INC_DIR = inc/
LIBFT_DIR = libft/
LIBFT_EXEC = libft.a
CC = gcc
CFLAGS = -Wall -Werror -Wextra -g
# place all source files here
SRC = $(SRC_DIR)main.c \
$(SRC_DIR)builtin1.c \
$(SRC_DIR)builtin2.c \
[...]
# takes all named source files and converts them to .o files in the /obj directory
OBJ = $(SRC:$(SRC_DIR)%.c=$(OBJ_DIR)%.o)
# prevents rules from being considered as files
.PHONY: all clean fclean re
all: $(NAME)
# creates subdirectory /obj
$(OBJ_DIR):
#mkdir $#
#echo "Creating object directory..."
# makes sure to make a /obj dir before compiling .o files
$(OBJ): | $(OBJ_DIR)
$(OBJ): $(OBJ_DIR)%.o: $(SRC_DIR)%.c
$(CC) $(CFLAGS) -c $< -o $#
# compiles all object files and builds executable file 'minishell' -> ADJUST READLINE FOR LINUX!
$(NAME): $(OBJ)
#echo "Compiling libft..."
$(MAKE) -C libft
#echo "Compiling $(NAME)..."
$(CC) $(CFLAGS) $^ $(LIBFT_DIR)$(LIBFT_EXEC) -L $(HOME)/goinfre/.brew/opt/readline/lib/ -lreadline -o $#
#echo "SUCCESSFULLY CREATED MINISHELL!"
So how can I scale that up to handle subdirectories?
I know I could make Makefiles in subdirectories, but this is not worth the effort since there aren't a lot of files in there...
Instead of just creating the object directory before compiling, you should create the object file directory tree prior to compiling each file:
$(OBJ_DIR)%.o: $(SRC_DIR)%.c
#mkdir -p $(dir $#)
$(CC) $(CFLAGS) -c $< -o $#
$(dir $#) is a GNU make extension. You can use #mkdir -p `dirname $#` for portability to other make flavors.

filter-out is not filtering file? Makefile for C

so ive got a make file here and my project currently has a master.c and slave.c which both have main functions. therefore i just want to filter the slave.c file out of the building process. so I used fliter-out when defining the source files. but when run make the project keeps turning up with the "multiple definitions of main" error. why is this when filter-out should be hiding the slave.c file?
########################################################################
####################### Makefile Template ##############################
########################################################################
#Compiler settings - Can be customized.
CC = gcc
CXXFLAGS = -std=c11 -Wall
LDFLAGS =
# Makefile settings - Can be customized.
APPNAME = master
SUBAPPNAME = slave
EXT = .c
SRCDIR = .
OBJDIR = .
############## Do not change anything from here downwards! #############
SRC := $(filter-out slave.c, $(wildcard $(SRCDIR)/*$(EXT)))
OBJ := $(SRC:$(SRCDIR)/%$(EXT)=$(OBJDIR)/%.o)
DEP := $(OBJ:$(OBJDIR)/%.o=%.d)
#UNIX-based OS variables & settings
RM = rm
DELOBJ = $(OBJ)
# Windows OS variables & settings
DEL = del
EXE = .exe
WDELOBJ = $(SRC:$(SRCDIR)/%$(EXT)=$(OBJDIR)\\%.o)
########################################################################
####################### Targets beginning here #########################
########################################################################
all: $(APPNAME)
# Builds the app
$(APPNAME): $(OBJ)
$(CC) $(CXXFLAGS) -o $# $^ $(LDFLAGS)
# Creates the dependecy rules
%.d: $(SRCDIR)/%$(EXT)
#$(CPP) $(CFLAGS) $< -MM -MT $(#:%.d=$(OBJDIR)/%.o) >$#
# Includes all .h files
-include $(DEP)
# Building rule for .o files and its .c/.cpp in combination with all .h
$(OBJDIR)/%.o: $(SRCDIR)/%$(EXT)
$(CC) $(CXXFLAGS) -o $# -c $<
################### Cleaning rules for Unix-based OS ###################
# Cleans complete project
.PHONY: clean
clean:
$(RM) $(DELOBJ) $(DEP) $(APPNAME)
# Cleans only all files with the extension .d
.PHONY: cleandep
cleandep:
$(RM) $(DEP)
# Clean only all files with the extension .o
.PHONY: cleanobj
cleanobj:
$(RM) $(DELOBJ)
# Cleans both files with .d and .o extensions
.PHONY: cleanod
cleanod:
$(RM) $(DELOBJ) $(DEP)
The call
SRC := $(filter-out slave.c, $(wildcard $(SRCDIR)/*$(EXT)))
is just a string operation, that is, make is unaware of the underlying file tree and tries to throw out the string slave.c from the liste yoursrcdir/slave.c yoursrcdir/master.c which obviously fails. Although you may disagree at first, this is a good thing because the semantic of filter operations on filetrees is by no means universal or easy to document or transport. Therefore make just looks at the presented strings and decides on the character-for-character comparison which to take and which to drop.
That said, the rewrite to
SRC := $(filter-out $(SRCDIR)/slave.c, $(wildcard $(SRCDIR)/*$(EXT)))
will do the trick in your case.
For wider reaching functionality look up the two functions abspath and realpath to get file names in a canonical format, which prevent filter et.al. from stumbling on differences in OS nomenclature.

Unexpected prerequisite skip in GNU Make 4.2.1

I'm trying to create a generic Makefile to use with most of my projects. It should work as follows: only rebuild and link those .o files whose .c or .h dependency has changed. The .o and .d files are stored in a separate directory called 'build'.
With the help of the official GNU Make manual and some googling I've managed to achieve the desired behavior except for one thing: when I run make re I get the error:
Assembler messages: Fatal error: can't create build/ft_build_buffer.o: No such file or directory — the reason for this is that the 'build' directory only gets created whenever the .d files are generated, but for some reason the re rule simply skips this step and goes on to compile .o files straight away! Note: if I run make clean && make fclean && make all (which should be the exact same thing) everything works fine.
A few other things: I've tried using the -MMD option to generate dependencies on the fly but on my machine that causes the .d files to only contain .c dependencies. Of course I could just make all .c files depend on all .h files but that seems like a very sloppy solution.
Feel free to share any other advice/improvements that will make this file more clean and readable, thanks! :)
# Define the C compiler to use.
CC := gcc
# Define any compile-time flags.
CFLAGS := -I./include -Wall -Wextra -Werror -g
#CFLAGS := -I./include -march=native -O2 -pipe
# Define the executable file.
BIN := ft_hexdump
# Define build directory.
BUILD_DIR := build
# Define source files directory.
SRC_DIR := src
# Define the C source files.
SRCS := $(wildcard $(SRC_DIR)/*.c)
# Define the C object files.
OBJS := $(SRCS:$(SRC_DIR)/%.c=$(BUILD_DIR)/%.o)
# Define the prerequisite files.
DEPS := $(OBJS:%.o=%.d)
.PHONY: all clean fclean re
.DELETE_ON_ERROR:
all: $(BIN)
-include $(DEPS)
$(BIN): $(OBJS)
$(CC) $(CFLAGS) $^ -o $#
$(BUILD_DIR)/%.o: $(BUILD_DIR)/%.d
$(CC) $(CFLAGS) -c $(SRC_DIR)/$*.c -o $#
$(BUILD_DIR)/%.d: $(SRC_DIR)/%.c
#mkdir -p $(#D)
#set -e; rm -f $#; \
$(CC) $(CFLAGS) $(INCLUDE) -MM $< > $#.$$$$; \
sed 's,\($*\)\.o[ :]*,\1.o $# : ,g' < $#.$$$$ > $#; \
rm -f $#.$$$$
clean:
-rm -rf $(BUILD_DIR)
fclean: clean
-rm -f $(BIN)
re: fclean all
Here is the modified working version as suggested by #M.M
# Define the C compiler to use.
CC := gcc
# Define any compile-time flags.
CFLAGS := -I./include -Wall -Wextra -Werror -g
#CFLAGS := -I./include -march=native -O2 -pipe
# Define the executable file.
BIN := ft_hexdump
# Define build directory.
BUILD_DIR := build
# Define source files directory.
SRC_DIR := src
# Define the C source files.
SRCS := $(wildcard $(SRC_DIR)/*.c)
# Define the C object files.
OBJS := $(SRCS:$(SRC_DIR)/%.c=$(BUILD_DIR)/%.o)
# Define the prerequisite files.
DEPS := $(OBJS:%.o=%.d)
.PHONY: all clean fclean re
.DELETE_ON_ERROR:
all: $(BIN)
-include $(DEPS)
$(BIN): $(OBJS)
$(CC) $(CFLAGS) $^ -o $#
$(BUILD_DIR)/%.o: $(SRC_DIR)/%.c
#mkdir -p $(#D)
$(CC) $(CFLAGS) -MMD -c $(SRC_DIR)/$*.c -o $#
clean:
-rm -rf $(BUILD_DIR)
fclean: clean
-rm -f $(BIN)
re:
$(MAKE) fclean
$(MAKE) all

Make ignores implicit rule dependency

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

Resources