So, I decided to learn make as my first step into large projects, and I have to say that it is not that hard if you are just doing simple tasks and got addicted to it. However I usually work with a scheme for my directories:
.
├── build
├── include
│ └── func.h
├── lib
│ └── func.c
├── Makefile
└── src
└── main.c
I usually have all my object files spread in the build directory. However, I could only map the source files to the build folder (like ./build/src/main.o where I prefer ./build/main.o). I Tried Reading The Documentation to no avail! this is what I came up with so far:
# C Compiler
CC = gcc
#------------- Directories
SOURCE_DIR = src lib
OBJECTS_DIR = build
INCLUDE_DIR = . ./include
#----------------------------
VPATH = $(SOURCE_DIR)
#------------- Files
SOURCE = $(foreach dir, $(SOURCE_DIR), $(wildcard $(dir)/*.c))
# Fake Objects (Just so I can map them to c files)
FOBJECTS = $(addprefix $(OBJECTS_DIR)/, $(SOURCE:.c=.o))
OBJECTS = $(addprefix $(OBJECTS_DIR)/, $(notdir $(FOBJECTS)))
DEPS = $(foreach dir, $(INCLUDE_DIR), $(wildcard $(dir)/*.h))
#----------------------------
#------------- Flags
OPT = -O0
IFLAGS = $(foreach dir, $(INCLUDE_DIR), -I$(dir))
LFLAGS = -lm
CFLAGS = -Wall
FLAGS = $(OPT) $(IFLAGS) $(LFLAGS) $(CFLAGS)
#----------------------------
BINARY = bin
all : $(BINARY)
$(BINARY) : $(OBJECTS)
$(CC) -o $@ $(OBJECTS)
$(OBJECTS) : $(FOBJECTS)
mv -t $(OBJECTS_DIR) $(FOBJECTS)
rm -rf -- $(OBJECTS_DIR)/*/
$(OBJECTS_DIR)/%.o : %.c $(DEPS)
$(CC) $(FLAGS) -c -o $@ $<
exec : $(BINARY)
@./$(BINARY)
clean :
rm -rf $(OBJECTS) $(BINARY)
I keep getting this error:
gcc -O0 -I. -I./include -lm -Wall -c -o build/src/main.o src/main.c
Assembler messages:
Fatal error: can't create build/src/main.o: No such file or directory
make: *** [Makefile:39: build/src/main.o] Error 1
I know the reason is the Fake Objects I created but creating the perfect rule for this is hard
CodePudding user response:
As I said, you probably do not want to put all .o
in the same directory because comingling .o
files from unrelated projects isn't the best organization. If the .o
files were related, you'd probably put the .c
files in the same subdir.
But, if you did want all .o
in a single build
directory, one way is to create the build/*
subdirs:
# C Compiler
CC = gcc
#------------- Directories
SOURCE_DIR = src lib
OBJECTS_DIR = build
INCLUDE_DIR = . ./include
OBJ_MK = $(addprefix $(OBJECTS_DIR)/, $(SOURCE_DIR))
#----------------------------
VPATH = $(SOURCE_DIR)
#------------- Files
SOURCE = $(foreach dir, $(SOURCE_DIR), $(wildcard $(dir)/*.c))
# Fake Objects (Just so I can map them to c files)
FOBJECTS = $(addprefix $(OBJECTS_DIR)/, $(SOURCE:.c=.o))
OBJECTS = $(addprefix $(OBJECTS_DIR)/, $(notdir $(FOBJECTS)))
DEPS = $(foreach dir, $(INCLUDE_DIR), $(wildcard $(dir)/*.h))
#----------------------------
#------------- Flags
OPT = -O0
IFLAGS = $(foreach dir, $(INCLUDE_DIR), -I$(dir))
LFLAGS = -lm
CFLAGS = -Wall
FLAGS = $(OPT) $(IFLAGS) $(LFLAGS) $(CFLAGS)
#----------------------------
BINARY = bin
all : $(OBJ_MK) $(BINARY)
$(BINARY) : $(OBJECTS)
$(CC) -o $@ $(OBJECTS)
$(OBJECTS) : $(FOBJECTS)
mv -t $(OBJECTS_DIR) $(FOBJECTS)
rm -rf -- $(OBJECTS_DIR)/*/
$(OBJECTS_DIR)/%.o : %.c $(DEPS)
$(CC) $(FLAGS) -c -o $@ $<
exec : $(BINARY)
@./$(BINARY)
clean :
rm -rf $(OBJECTS) $(BINARY)
rm -rf $(OBJ_MK)
$(OBJ_MK):
mkdir $@
The make
output is:
mkdir build/src
mkdir build/lib
gcc -O0 -I. -I./include -lm -Wall -c -o build/src/main.o src/main.c
gcc -O0 -I. -I./include -lm -Wall -c -o build/lib/func.o lib/func.c
mv -t build build/src/main.o build/lib/func.o
rm -rf -- build/*/
gcc -o bin build/main.o build/func.o
However, the above actually makes the build more complex because its "natural" tendency was to create the subdirs. To override that required extra mv
and rm
commands.
To use the subdirectory method, the build is actually simpler, and we can do:
# C Compiler
CC = gcc
#------------- Directories
SOURCE_DIR = src lib
OBJECTS_DIR = build
INCLUDE_DIR = . ./include
OBJ_MK = $(addprefix $(OBJECTS_DIR)/, $(SOURCE_DIR))
#----------------------------
VPATH = $(SOURCE_DIR)
#------------- Files
SOURCE = $(foreach dir, $(SOURCE_DIR), $(wildcard $(dir)/*.c))
# Fake Objects (Just so I can map them to c files)
OBJECTS = $(addprefix $(OBJECTS_DIR)/, $(SOURCE:.c=.o))
DEPS = $(foreach dir, $(INCLUDE_DIR), $(wildcard $(dir)/*.h))
#----------------------------
#------------- Flags
OPT = -O0
IFLAGS = $(foreach dir, $(INCLUDE_DIR), -I$(dir))
LFLAGS = -lm
CFLAGS = -Wall
FLAGS = $(OPT) $(IFLAGS) $(LFLAGS) $(CFLAGS)
#----------------------------
BINARY = bin
all : $(OBJ_MK) $(BINARY)
$(BINARY) : $(OBJECTS)
$(CC) -o $@ $(OBJECTS)
$(OBJECTS_DIR)/%.o : %.c $(DEPS)
$(CC) $(FLAGS) -c -o $@ $<
exec : $(BINARY)
@./$(BINARY)
clean :
rm -rf $(OBJECTS) $(BINARY)
rm -rf $(OBJ_MK)
$(OBJ_MK):
mkdir $@
The make
output is:
mkdir build/src
mkdir build/lib
gcc -O0 -I. -I./include -lm -Wall -c -o build/src/main.o src/main.c
gcc -O0 -I. -I./include -lm -Wall -c -o build/lib/func.o lib/func.c
gcc -o bin build/src/main.o build/lib/func.o
CodePudding user response:
If you really want all the objects in a single directory, you almost have it right but you added some very strange rules that I don't understand; what is this for:
$(OBJECTS) : $(FOBJECTS)
mv -t $(OBJECTS_DIR) $(FOBJECTS)
rm -rf -- $(OBJECTS_DIR)/*/
? This is what's causing your problem. You're saying that every individual object file depends on all the "intermediate" object files, so then make tries to build these "intermediate" object files. The only way it knows to do that is with the pattern rule you provided, but that doesn't build those files.
Remove that rule altogether and it will probably work. You just want:
VPATH = $(SOURCE_DIR)
OBJECTS = $(addprefix $(OBJECTS_DIR)/, $(notdir $(SOURCE:.c=.o)))
all : $(BINARY)
$(BINARY) : $(OBJECTS)
$(CC) -o $@ $(OBJECTS)
$(OBJECTS_DIR)/%.o : %.c $(DEPS)
$(CC) $(FLAGS) -c -o $@ $<
etc. The compiler will build the object files directly into their final destination. You don't need the FOBJECTS
thing or the rule that uses it.