I've been through probably 20 different webpages about makefile but I still can't seem to get it to work for my project. A project with all the files in one folder works, but I can't get it to work with my specific project which has everything in different folders.
Example structure
Here is an example project that has all the necessary features and distinctions to be structurally identical to my project yet obviously smaller:
.
|-- bin
| |-- foo_apple (executable 1) // needs apple's .o files (C )
| |-- foo_banana (executable 2) // needs banana's .o files .so file (C/C )
|-- include
| |-- format
| | |-- format.h
| |-- apple
| | |-- foo.h
| |-- banana
| |-- bar.h
|-- lib
| |-- apple
| | |-- apple_main.o
| | |-- apple_one.o
| |-- banana
| | |-- banana_main.o
| | |-- banana_one.o
| | |-- banana_two.o
| |-- archive_apple
| | |-- libapple_one.a
| |-- archive_banana
| | |-- libbanana_one.a
| | |-- libbanana_two.a
| |-- external
| | |-- foo.so
|-- makefile
|-- src
|-- drivers
| |-- apple_main.cpp // needs format.h
| |-- banana_main.cpp // needs format.h,
|-- apple
| |-- apple_one.cpp // needs format.h, foo.h
|-- banana
|-- banana_one.c // needs format.h, bar.h
|-- banana_two.cpp // needs format.h, bar.h
I've been reading about things online about automatic makefile generation, where you type in certain things into the makefile that goes through the folders and uses substitutions and other things to eliminate the need to list every single rule explicitly, but none of them worked. I must be doing something wrong, but I don't know what it is.
What I need
I want to put the correct things into my makefile that creates what you see above.
As you can see, some parts of the project are necessarily written in C. (I guess I could change everything to be C but I don't want to if I don't have to).
My compilers and flags will be:
CC := gcc
CXX := g
C_FLAGS := -Wall -std=c17
CXX_FLAGS := -Wall -std=c 17
My directories will use -I
to avoid things like
// #include "../../include/format/format.h"
// instead:
#include <format/format.h>
I'll also be using -L
, if that helps, because I want to create those .a
files.
As far as I can see, it's something like:
libsomething.a: something.o
ar rvs ./lib/archive/libsomething.a ./lib/foo/something.o
What do I need in my makefile in order to put this project together and generate my executables, object files, and libraries?
Note: I understand the basics of makefile, like how a rule works and how to put together a makefile manually for a small and simple project, but I'm only just starting to understand the built-in functions and wildcards and other complex things.
And of course, since my project is much larger than this, though structurally identical, I need the answer to be something either generic or easy to modify, like those examples of makefiles I saw online that for some reason don't work on my project. That way, I can adapt this to future projects without unnecessary manual work.
Preferably (unless it is bad practice to do so), I would like for the executables to be built from the libraries, something along the lines of:
executable: something_main.o libfoo.a libbar.a
$(CXX_BUILD) -o ./bin/executable \
./lib/something_main.o \
-L./lib/something \
-lfoo -lbar
CodePudding user response:
Ad 1 and 2: The filenames can safely include directories and %
matches /
as necessary. So you can easily have:
$(wildcard subdir/*.c) $(wildcard anotherdir/*.c)
or even
$(wildcard */*.c)
... or as suggested by keltar in comment
$(shell find . -name '*.c')
which is recursive.
Ad 3: You are doing it.
Ad 4: Create a target with $(OBJ)
as dependencies and use the automatic variable just as you do for compilation:
main : $(OBJ)
$(LD) $(LDFLAGS) -o $@ $< $(LIBS)
Original answer by @Jan Hudec: make for compiling — all *.c files in folders & subfolders in project
CodePudding user response:
May Help.
The Structure of the project:
xc
inc
src
test
inc
src
obj
xc
test
bin
Makefile
PNAME = xc
OBJS_DIR=obj
BIN_DIR=bin
INC_SUB_DIR=inc
SRC_SUB_DIR=src
CPP = gcc
#CPP = -m32
#LIBPATH = -Llib
LIBLIST = -lz
LDFLAGS = $(LIBPATH) $(LIBLIST)
UTILS_DIR = xc
UTILS_SRCS = $(wildcard $(UTILS_DIR)/$(SRC_SUB_DIR)/*.c)
UTILS_INCS = $(wildcard $(UTILS_DIR)/$(INC_SUB_DIR)/*.h)
UTILS_SLIST = $(UTILS_SRCS) $(UTILS_INCS)
UTILS_OLIST = $(wildcard $(OBJS_DIR)/$(UTILS_DIR)/*.o)
UTILS_OBJS = $(patsubst $(UTILS_DIR)/$(SRC_SUB_DIR)/%.c,$(OBJS_DIR)/$(UTILS_DIR)/%.o,$(UTILS_SRCS))
BASE_DIR = test
BASE_SRCS = $(wildcard $(BASE_DIR)/$(SRC_SUB_DIR)/*.c)
BASE_INCS = $(wildcard $(BASE_DIR)/$(INC_SUB_DIR)/*.h)
BASE_SLIST = $(BASE_SRCS) $(BASE_INCS)
BASE_OLIST = $(wildcard $(OBJS_DIR)/$(BASE_DIR)/*.o)
BASE_OBJS = $(patsubst $(BASE_DIR)/$(SRC_SUB_DIR)/%.c,$(OBJS_DIR)/$(BASE_DIR)/%.o,$(BASE_SRCS))
SLIST = $(UTILS_SLIST) $(BASE_SLIST)
OLIST = $(UTILS_OLIST) $(BASE_OLIST)
OBJS = $(UTILS_OBJS) $(BASE_OBJS)
INCPATH = -I$(UTILS_DIR)/$(INC_SUB_DIR) -I$(BASE_DIR)/$(INC_SUB_DIR)
CPPFLAGS = -c -Wall -Wextra
#CPPFLAGS = -O2
CPPFLAGS = $(INCPATH)
#CPPFLAGS = -g
# target
.PHONY = all
all: $(PNAME)
packsrc:
tar zcvf $(PNAME)-src-`date %Y%m%d%H%M%S`.tgz $(SLIST) doc Makefile
slib:
$(AR) rcs $(PNAME)-`date %Y%m%d%H%M%S`.a $(OBJS)
clean:
$(RM) $(OLIST) $(BIN_DIR)/$(PNAME)
$(PNAME): $(OBJS)
@ mkdir -p $(BIN_DIR)
@ mkdir -p $(OBJS_DIR)
@ mkdir -p $(OBJS_DIR)/$(UTILS_DIR)
@ mkdir -p $(OBJS_DIR)/$(BASE_DIR)
$(CPP) $^ -o $(BIN_DIR)/$@ $(LDFLAGS)
$(OBJS_DIR)/$(UTILS_DIR)/%.o:$(UTILS_DIR)/$(SRC_SUB_DIR)/%.c
$(CPP) $(CPPFLAGS) $< -o $@
$(OBJS_DIR)/$(BASE_DIR)/%.o:$(BASE_DIR)/$(SRC_SUB_DIR)/%.c
$(CPP) $(CPPFLAGS) $< -o $@
dos2unix:
dos2unix $(SLIST)