Compiling all C files and creating executable with Makefile - c

I have a C project, which has the following file structure:
Makefile
src
|-utils
| |--vic.c
| |--vic.h
|-mod
| |--type.c
| |--type.h
|-bb.c
|-bb.h
|-main.c
So, at the root directory I have the actual Makefile and the src directory, which includes the source files. Inside the src directory there are multiple .c and .h files along with the main.c file, and there are other directories which also contain other .c and .h files. Please note that the above shown file structure is kept short for brevity. I want to create a Makefile that will automatically compile everything and generate an executable main program, and also delete the object files generated during the compilation. Currently, I have something like this:
CC=gcc
CFLAGS=
RM=rm -rf
OUT=main
SRC=src
OBJ=obj
SOURCES=$(wildcard $(SRC)/*.c)
OBJECTS=$(patsubst $(SRC)/%.c, $(OBJ)/%.o, $(SOURCES))
all: build
build: $(OBJECTS)
$(CC) $(CFLAGS) $^ -o $#
$(RM) $(OBJ)
$(OBJ)/%.o: $(SRC)/%.c
$(CC) $(CFLAGS) -I$(SRC) -c $< -o $#
debug: CFLAGS+=-DDEBUG
debug: build
.PHONY: clean
clean:
$(RM) $(OBJ) $(OUT)
But that doesn't seem to work as expected, as when I issue make command, it returns the following error message:
Assembler messages: Fatal error: can't create obj/main.o: No such file
or directory
Any ideas how to achieve what I want?

It seems you rm -rf obj/ after each step, but there's no mkdir -p obj/ to replace it.
Since GNU make will, itself, remove intermediate targets, I don't see why you would do $(RM) $(OBJ), but your %.o target could have a line like
mkdir -p "$(shell dirname "$#")"
… to ensure that the destination directory exists.
PS: your output is named build rather than main because that's the name you gave it …

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.

How to run and execute a makefile C

I'm trying to make a Makefile for my program. It is so difficult because i've read a lot of guide but none is clear. I have 3 files : main.c , library.c , library.h . Main.c and library.c depend on library.h . The structure of my directory project is formed by :
MyProject directory -> Build directory and Exercise1 directory -> all of my files . In compiler I wrote make and it compiled ; then when I write make execute command, it gives me this error:
cd ../build; ./test
Error: No such file or directory
makefile:23: recipe for target 'execute' failed
make: *** [execute] Error 1
MAKEFILE
CC=gcc
CFLAGS=-Wall
ODIR=../build
DIR = build
.PHONY: all
all: main.o library.o test
$(ODIR)/%.o: %.c $(DEPS)
$(CC) -std=c99 -c -o $# $< $(CFLAGS)
library.o: library.c library.h
$(CC) -std=c99 -c -o $(ODIR)/$# $< $(CFLAGS)
main.o: main.c library.h
$(CC) -std=c99 -c -o $(ODIR)/$# $< $(CFLAGS)
test: $(ODIR)/library.o $(ODIR)/main.o
$(CC) -std=c99 -o $(ODIR)/$# $^ $(CFLAGS)
execute:
cd $(ODIR); ./test
clean:
rm -f $(ODIR)/*.o
These lines:
execute:
cd $(ODIR); ./test
tell make that, when you give the command make execute, it should change the working directory to $(ODIR) and then execute ./test, which means to execute the file test in the current working directory. However, there is no file test in the $(ODIR) directory because you have not built it yet.
You can make that file by executing make test, but that is a bad way to do it. It is better to tell make that the execute target depends on $(ODIR)/test:
execute: $(ODIR)/test
cd $(ODIR); ./test
Then we should change the rule for test to $(ODIR)/test:
$(ODIR)/test: $(ODIR)/library.o $(ODIR)/main.o
$(CC) -std=c99 -o $(ODIR)/$# $^ $(CFLAGS)
Next, delete the rule for all and the .PHONY rule. A rule for all should be used when a makefile can make several different final targets, like ProgramA, ProgramB, and ProgramC, and you want one target that makes all of them. It should not be used to make all of the intermediate object files for a target. The intermediate files should arise out of the rules for building a final target.
Then delete the rules for library.o and main.o. Those are names for files in the current directory, but you are building in $(ODIR). We will let the pattern rule for $(ODIR)/%.o build those.
But we need to fix the pattern rule. It uses DEPS, but that is not defined. Add a line above that says what all the object files depend on:
DEPS=library.h
Nothing in the makefile uses DIR, so delete the line DIR = build.
Finally, you might want to put the execute target first, so that it is the default. Then your makefile is:
CC=gcc
CFLAGS=-Wall
ODIR=../build
DEPS=library.h
execute: $(ODIR)/test
cd $(ODIR); ./test
$(ODIR)/%.o: %.c $(DEPS)
$(CC) -std=c99 -c -o $# $< $(CFLAGS)
$(ODIR)/test: $(ODIR)/library.o $(ODIR)/main.o
$(CC) -std=c99 -o $(ODIR)/$# $^ $(CFLAGS)
clean:
rm -f $(ODIR)/*.o
You might also change the command for clean to remove test:
rm -f $(ODIR)/*.o $(ODIR)/test

C Makefile, Strange error with "No target" when a target is present

So I have the makefile
HEADERS = preferences.h distances.h
OBJECTS = movie_recommender.o preferences.o distances.o
default: movie_recommender
%.o: %.c $(HEADERS)
; gcc -c $< -o $#
movie_recommender: $(OBJECTS)
; gcc $(OBJECTS) -o $#
clean:
; -rm -f $(OBJECTS)
; -rm -f movie_recommender
And I receive the error:
No rule to make target 'movie_recommender.o', needed by 'movie_recommender'. Stop.
File paths
I've tried adding the target,
movie_recommender.o: movie_recommender.c
gcc -c movie_recommender.c
And I receive another error. Could anyone please tell me what I'm doing wrong?
Many thanks!
The make program only looks in the current directory for your files. There is no movie_recommender.c file in the current directory, so make doesn't see it and doesn't find any way to make movie_recommender.o.
The solution is to add make your makefile aware of the source and header directories, which you can do with the vpath directive. If you add
vpath %.c src
then it will automatically look for %.c files in the src directory.
Similarly, make won't find your headers.
GCC also won't find the headers, and it can't know about make's vpath, so instead you need to pass -Iinclude to GCC.

Makefile cannot create a seperate Object Folder for simple project

I am using this Makefile Tutorial for understanding how to use Makefiles.
This Question might be a duplicate for this thread but I think I need more clarity here.
My Project structure:
--exercise_14/
--> ex14.c
--> ex14.h
--> main.c
--> Makefile
There is nothing complex about ex14.*, just simple header file with 3 function declarations (ex14.h) and their implementation (ex14.c) and main.c calls them.
My Makefile is as follows:
CC = gcc
CFLAGS = -Wall -g
DEPS = ex14.h
ODIR=obj
_OBJ=ex14.o main.o
OBJ=$(patsubst %,$(ODIR)/%,$(_OBJ))
all: ex14
$(ODIR)/%.o: %.c $(DEPS)
$(CC) $(CFLAGS) -c -o $# $<
ex14: $(OBJ)
$(CC) $(CFLAGS) -o $# $^
clean:
rm -f ex14 $(ODIR)*.o
rm -rf $(ODIR)
I am currently understanding how the patsubst in the file should work and everytime I run
make clean all
I get:
gcc -Wall -g -c -o obj/ex14.o ex14.c
Assembler messages:
Fatal error: can't create obj/ex14.o: No such file or directory
Makefile:16: recipe for target 'obj/ex14.o' failed
make: *** [obj/ex14.o] Error 1
Which makes sense that there is no obj/ folder created and no ex14.o is found for further compilation. A way around is to use mkdir obj and then perform make but I want to avoid that.
Question
What lines should be added to let my Makefile make a folder as ODIR=obj and put all the object files within it?
The correct solution is to make your object files depend on their directory via order-only dependency:
Consider an example where your targets are to be placed in a separate directory, and that directory might not exist before make is run. In this situation, you want the directory to be created before any targets are placed into it but, because the timestamps on directories change whenever a file is added, removed, or renamed, we certainly don’t want to rebuild all the targets whenever the directory’s timestamp changes. One way to manage this is with order-only prerequisites: make the directory an order-only prerequisite on all the targets:
$(ODIR)/%.o: %.c $(DEPS) | $(ODIR)
<same-original-recipe>
$(ODIR):
mkdir -p $#
As commented by others, but you can explicitly put a dependency to the $(ODIR) directory (it must be created before any dependent files ---compilation):
$(OBJS): $(ODIR)
$(ODIR):
mkdir -p $#
This will ensure you have created the $(ODIR) directory before any dependent files (any *.o file) and that it will be created only once.
The final contents of your Makefile should be as this:
CC = gcc
CFLAGS = -Wall -g
DEPS = ex14.h
ODIR=obj
_OBJ=ex14.o main.o
OBJ=$(patsubst %,$(ODIR)/%,$(_OBJ))
all: ex14
$(ODIR)/%.o: %.c $(DEPS)
$(CC) $(CFLAGS) -c -o $# $<
ex14: $(OBJ)
$(CC) $(CFLAGS) -o $# $^
clean:
rm -f ex14 $(ODIR)*.o
rm -rf $(ODIR)
$(OBJ): $(ODIR)
$(ODIR):
mkdir -p $#
EDIT 2
After posting the correct rule I found some errors in the $(patsubst) variable expansion, that made make to fail when not everything was erased.
Following is a correct, optimized Makefile:
$ cat Makefile
CC = gcc
CFLAGS = -Wall -g
DEPS = ex14.h
ODIR=obj
OBJS=ex14.o main.o
POBJS=$(foreach i,$(OBJS),$(ODIR)/$(i))
LIBS= # for example: -lm for math library.
.PHONY: all ex14 clean
$(ODIR)/%.o: %.c
$(CC) $(CFLAGS) -c $< -o $#
all: ex14
ex14: $(POBJS)
$(CC) $(CFLAGS) $(LDFLAGS) -o $# $(POBJS) $(LIBS)
clean:
rm -rf $(ODIR)
$(POBJS): $(ODIR) $(DEPS)
$(ODIR):
mkdir -p $#
putting the dependency on $(DEPS) in the
$(OBJS): $(ODIR) $(DEPS)
makes the automatic default dependency rule for .c -> .o files valid, and not needed anymore in the makefile. Also, I have used P prefix to mean pathed file in POBJS against OBJS (which is the plain list of object files) Also removing recursively the objs subdirectory makes unnecessary to remove the object files first, and then the subdirectory (only the last command is needed) Also, when linking, it is common use, to pass the compiler the $(LDFLAGS) for it to pass them to the linker. And finally, if you have some libraries to include at the end, just use a $(LIBS) library (not needed for your sample).
As stated in one of the comments, the compilation of just one source file makes the $(ODIR) directory to be touched, and all the other files to be out of date, and this will make all the files but the one just compiled to be compiled next time. There's no solution to this problem as there's a circular dependency on this (the objects depend on the directory to exist, and the directory is touched on each compilation which makes the dependency to be triggered again for all the files compiled before the last one) The only possible solution to this problem is to eliminate the dependency on the directory and construct it by hand before calling make.
Berkeley make (pmake or bmake, it depends) has a workaround to this problem, by allowing you to define a .BEGIN dependency, that is to be solved before any other work. You can create the directory there. I think GNU make doesn't have this feature:
.BEGIN:
mkdir -p $(ODIR)
But this is out of scope for this question that is directed to GNU make.

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

Resources