Makefile: Rule dependent on directory of target - c

I am trying to create a Makefile to create object files in a directory based on the source file. For example I have DirA/file.c and DirB/file.c and I want to create a recipe for creating DirA/file.o and DirB/file.o.
rootdir/
|-- Makefile
|-- DirA
| |-- source.c
| +-- source.o
+-- DirB
|-- file.c
+-- file.o
I would assume the recipe would be similar to the following:
MAKEFLAGS += --no-builtin-rules
DirA_Files=$(wildcard DirA/*.c)
DirB_Files=$(wildcard DirB/*.c)
.PHONY: all a_files b_files
all: a_files b_files
#echo Done with all!
#echo built $(patsubst %.c,%.o,$(DirA_Files)) $(patsubst %.c,%.o,$(DirB_Files))
a_files: $(patsubst %.c,%.o,$(DirA_Files))
b_files: $(patsubst %.c,%.o,$(DirB_Files))
DirA/%.o DirB/%.o : DirA/%.c DirB/%.c
# Complex recipe that I'd rather not state twice in the Makefile
#echo "Building $# because $? changed"
#touch $#
But I do not want the object files in DirA to be dependent on the source files of DirB, which is what the above recipe implies.
I have also tried
DirA/%.o : DirA/%.c
DirB/%.o : DirB/%.c
DirA/%.o DirB/%.o :
# Complex recipe that I'd rather not state twice in the Makefile
#echo "Building $# because $? changed"
#touch $#
But then the $? variable is always blank.
How could I create a single recipe that would also allow me to build a_files and b_files independently

You can't combine pattern rules quite that easily. But this situation is what defined recipes were made for:
define complex-rule
# Complex recipe that I'd rather not state twice in the Makefile
#echo "Building $# because $? changed"
#touch $#
endef
DirA/%.o : DirA/%.c
$(complex-rule)
DirB/%.o : DirB/%.c
$(complex-rule)

related info:
DirA_Files := $(wildcard DirA/*.c)
DirB_Files := $(wildcard DirB/*.c)
$(info DirA_Files: $(DirA_Files))
$(info DirB_Files: $(DirB_Files))
aobj := $(patsubst %.c,%.o,$(DirA_Files))
bobj := $(patsubst %.c,%.o,$(DirB_Files))
$(info aobj: $(aobj))
$(info bobj: $(bobj))
all: $(aobj) $(bobj)
#echo Done with all!

Related

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.

Build Makefile for project have multiple directories

I'm building a project have tree as following:
.
├── build
├── inc
│   └── log.h
├── Makefile
└── src
└── ota.c
"src" directory contain source files .c;
"inc" contain header files .h;
"build" will contain object files .o and execute file.
Makefile details:
vpath %.h inc
vpath %.c src
RED = \033[1;31m
GREEN = \033[1;32m
YELLOW = \033[1;33m
BLUE = \033[1;34m
RESET = \033[1;0m
INC_DIR = inc
SRC_DIR = src
BUILD_DIR = build
CC = gcc
CFLAGS = -Wall -I$(INC_DIR)
RM = rm -rf
SRCS = $(wildcard */*.c)
INCS = $(wildcard */*.h)
OBJS = $(patsubst %,$(BUILD_DIR)/%,$(patsubst
%.c,%.o,$(notdir $(SRCS))))
TARGET = OTA
$(BUILD_DIR)/$(TARGET): $(OBJS)
#echo "$(YELLOW)Linking ...$(RESET)"
$(CC) $(CFLAGS) -o $# $^
#echo Finished!
$(BUILD_DIR)/%.o: %.c %.h
#echo "$(GREEN)Compiling objects ...$(RESET)"
$(CC) $(CFLAGS) -c $< -o $#
.PHONY: clean
clean:
#echo "$(RED)$(OBJS) $(SRCS) $(INCS) $(RESET)"
$(RM) $(BUILD_DIR)/*
When I run make by terminal on Ubuntu 20.04 then encouter an error as below:
minh#Minh:~/Workspaces/OTA-tool$ make
make: *** No rule to make target 'build/ota.o', needed by 'build/OTA'. Stop.
Please, tell me why error and help me fix it. Thanks
The basic problem is this:
OBJS = $(patsubst %,$(BUILD_DIR)/%,$(patsubst %.c,%.o,$(notdir $(SRCS))))
By setting OBJS to a value without the pathname, now make can no longer match up the object filename to the source filename.
So for example if you have src/foo.c, then OBJ will be build/foo.o. Then this pattern rule:
$(BUILD_DIR)/%.o: %.c %.h
does not match, because the % in the target matches foo, which means make will be looking for foo.c (which doesn't exist, it's src/foo.c) and foo.h (which also doesn't exist).
Since the prerequisites don't match, this pattern rule doesn't match. And since no other pattern rules match, make says it doesn't have any rule which knows how to create the target build/foo.o.
One way to solve this is to add the directories on the prerequisite as well:
$(BUILD_DIR)/%.o: src/%.c inc/%.h
Now make will look for src/foo.c and inc/foo.h.

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

"No rule to make target" althoug the rule exists

I have an issue with my makefile which says No rule to make target /obj/%.o, needed by /bin/exec. Stop. But from what I understand I have it:
# define the C compiler to use
CC = gcc
# define any compile-time flags
# add -DDEBUG for debug mode
CFLAGS = -Wall
# define any directories containing header files
INCLUDES = -I/includes
# define src folder
SRC_FOLDER = /src
# define src files
SRC = $(wildcard $(SRC_FOLDER)/%.cpp)
# define object folder
OBJ_FOLDER = /obj
# define obj files
OBJ = $(patsubst %.cpp, %.o, $(SRC))
# define binary path
BIN_FOLFER = /bin
# define the executable file
MAIN = $(BIN_FOLFER)/exec
# compile object files
$(OBJ_FOLDER)/%.o: $(SRC_FOLDER)/%.cpp
$(CC) $(CFLAGS) $< -o $#
# build
$(MAIN): $(OBJ_FOLDER)/%.o
$(CC) $(CFLAGS) $^ -o $#
# cleaning
.PHONY: clean
clean:
rm -f $(OBJ_FOLDER)/%.o
I am sorry for any possible major errors in the makefile, this is my first makefile. What am I doing wrong?
$(MAIN): $(OBJ_FOLDER)/%.o
requests %.o exactly. The % does not act as pattern here, because it does not appear on both sides of the rule. You need to use the $(OBJ) variable there. But it first need to be fixed, because you are only replacing the extension, but you need to replace the directory too.
Finish the $(OBJ) variable (as Lutin already said) (ok, but I modified it a bit; I presume you only want the direct descendants of the directory):
SRC = $(wildcard $(SRC_FOLDER)/*.cpp)
OBJ = $(patsubst $(SRC_FOLDER)/%.cpp, $(OBJ_FOLDER)/%.o, $(SRC))
Fix the rule to actually use the $(OBJ) variable:
$(MAIN): $(OBJ)
Oh, and you most probably don't want SRC_FOLDER, OBJ_FOLDER and BIN_FOLDER to start with / as that puts them in the filesystem root it's not where your project lives. And with the patterns above they should not end with slash either.
It works with that:
SRC = $(wildcard $(SRC_FOLDER)*/*.cpp $(SRC_FOLDER)*.cpp)
OBJ = $(patsubst $(SRC_FOLDER)%.cpp, $(OBJ_FOLDER)%.o, $(SRC))

Resources