function define in makefile - makefile

I have test project folder structure view:
TOPDIR
├── a
│   └── a.c
├── b
│   └── b.c
├── c
│   └── c.c
└── makefile
I wrote a test makefile:
CC := gcc
LD := ld
MAKE_DIR = $(PWD)
MODULES := a b c
SRC_DIR := $(addprefix ${MAKE_DIR}/,$(MODULES))
SRC := $(foreach sdir,$(SRC_DIR),$(wildcard $(sdir)/*.c))
OBJ := $(patsubst %.c,%.o,$(SRC))
INCLUDES := $(addprefix -I,$(SRC_DIR))
vpath %.c $(SRC_DIR)
define make-goal
$1/%.o: %.c
$(CC) $(INCLUDES) -c $$< -o $$#
endef
all:
$(foreach sdir,$(SRC_DIR),$(eval $(call make-goal,$(sdir))))
during the make, it just STOPPED and shows:
makefile:37: *** prerequisites cannot be defined in command scripts. Stop.
the line 37 is:
$(foreach sdir,$(SRC_DIR),$(eval $(call make-goal,$(sdir))))
what is wrong with my makefile?

You are expanding make-goal inside a command. Just move it outside:
all: $(OBJ)
$(foreach sdir,$(SRC_DIR),$(eval $(call make-goal,$(sdir))))
Note that defining a recipe, then using foreach-eval-call is overkill in this case. A pattern rule will do:
all: $(OBJ)
%.o: %.c
$(CC) $(INCLUDES) -c $< -o $#
EDIT:
You can even get away without a pattern rule, using Make's implicit rule for compiling C files:
CFLAGS += $(INCLUDES)
all: $(OBJ)

Related

Write generic Makefile rule for subdirectories

I have a project with a directory tree that looks like this:
.
├── modules
│   ├── mod1
│   │   └── mod1.f90
│   ├── mod2
│   │   └── mod2.f90
│   └── mod.f90
└── src
└── main.f90
├── bin
└── Makefile
main.f90 uses all the modules found in modules and in the 'submodules' that are mod1/ and mod2/.
A simple Makefile I can write to compile the project is something like:
F90 = gfortran
FLAGS = -g -I$(BINDIR)
MODFLAGS = -J$(BINDIR)
BINDIR = bin
SRCDIR = src
MODDIR = modules
SMODDIR = $(dir $(wildcard $(MODDIR)/*/.))
MODFILES = $(wildcard $(MODDIR)/*f90)
MODOBJ = $(join $(addsuffix $(MODFROMBIN)/, $(dir $(MODFILES))), \
$(notdir $(MODFILES:.f90=.o)))
SMODFILES = $(foreach smoddir, $(SMODDIR), $(wildcard $(smoddir)*f90))
SMODOBJ = $(join $(addsuffix $(SMODFROMBIN)/, $(dir $(SMODFILES))), \
$(notdir $(SMODFILES:.f90=.o)))
SRCFILES = $(wildcard $(SRCDIR)/*f90)
SRCOBJ = $(join $(addsuffix $(SRCFROMBIN)/, $(dir $(SRCFILES))), \
$(notdir $(SRCFILES:.f90=.o)))
ALLOBJ = $(MODOBJ) $(SMODOBJ) $(SRCOBJ)
BINOBJ = $(addprefix $(BINDIR)/, $(sort $(notdir $(ALLOBJ))))
all: main
$(MODDIR)/../bin/%.o: $(MODDIR)/%.f90
$(F90) $(FLAGS) -c $^ -o $# $(MODFLAGS)
modules/mod1/../../bin/%.o: modules/mod1/%.f90
$(F90) $(FLAGS) -c $^ -o $# $(MODFLAGS)
modules/mod2/../../bin/%.o: modules/mod2/%.f90
$(F90) $(FLAGS) -c $^ -o $# $(MODFLAGS)
$(SRCDIR)/../bin/%.o: $(SRCDIR)/%.f90
$(F90) $(FLAGS) -c $^ -o $#
main: $(ALLOBJ)
$(F90) $(FLAGS) -o ./bin/main $(BINOBJ)
clean:
#rm bin/*.o bin/*.mod
But now I want to write a generic rule to be able to compile all modules located in directories that are inside modules directory (the code I am working on have more than two submodules and I am not willing to write as many rules as I have subdirectories).
My first try was to write something like this:
$(SMODDIR)/../../%.o: $(SMODDIR)/%.f90
$(F90) $(FLAGS) -c $^ -o $# $(MODFLAGS)
but it fails; from what I understand, it will put all the subdirectories paths and I would actually end up with a rule that would looks like this:
modules/mod1/../../bin/ modules/mod2/../../bin/%.o: modules/mod1/ modules/mod2/%.f90
that indeed looks funny.
As shown in the original Makefile, it is possible to retrieve specificaly with
SMODDIR = $(dir $(wildcard $(MODDIR)/*/.))
SMODFILES = $(foreach smoddir, $(SMODDIR), $(wildcard $(smoddir)*f90))
so I guess it may be possible to use something similar to have more generic rules. I couldn't see however how to use such a syntax to write a rule that makes sense.
Any help would be appreciated!
You have two choices to avoid writing lots of rules.
One is, you can use VPATH and put all your source directories in it, something like this:
VPATH := $(MODDIR) $(SMODDIR) $(SRCDIR)
$(BINDIR)/%.o: %.f90
$(F90) $(FLAGS) -c $< -o $# $(MODFLAGS)
Or you can put all your object files into equivalent subdirectories of $(BINDIR), something like this:
MODOBJ = $(MODFILES:%.f90=$(BINDIR)/%.o)
SMODOBJ = $(SMODFILES:%.f90=$(BINDIR)/%.o)
SRCOBJ = $(SRCFILES:%.f90=$(BINDIR)/%.o)
$(BINDIR)/%.o: %.f90
#mkdir -p $(#D)
$(F90) $(FLAGS) -c $< -o $# $(MODFLAGS)

Make not setting correctly the location of the source code

Here is a little problem that I've encountered:
I have the following project layout:
.
├── Makefile
├── README.md
├── inc
│ └── include.hpp
├── out
│ ├── debug
│ └── release
└── src
└── main.cpp
And the following Makefile (which was copied from this post, and edited a little bit by me, to adequate to my project):
# Compiler flags
CXX := g++
CXXFLAGS := -Wall -Werror -Wextra
# Project files
SRC_DIR := src
SRCS := $(wildcard $(SRC_DIR)/*.cpp)
INC_DIR := inc
INCLUDES := -I $(INC_DIR)
OBJS := $(SRCS:.cpp:=.o)
EXE := <ProgramName>
BUILD_DIR := out
# Debug build settings
DBGDIR := $(BUILD_DIR)/debug
DBGEXE := $(DBGDIR)/$(EXE)
DBGOBJS := $(addprefix $(DBGDIR)/, $(OBJS))
DBGCXXFLAGS := -g -O0 -DDEBUG
# Release build settings
RELDIR := $(BUILD_DIR)/release
RELEXE := $(RELDIR)/$(EXE)
RELOBJS := $(addprefix $(RELDIR)/, $(OBJS))
RELCXXFLAGS := -O3 -DNDEBUG
.PHONY: all clean debug prep release remake
# Default build
all: prep release
# Debug rules
debug: $(DBGEXE)
$(DBGEXE): $(DBGOBJS)
$(CXX) $(CXXFLAGS) $(DBGCXXFLAGS) -o $(DBGEXE) $^
$(DBGDIR)/%.o: $(SRC_DIR)/%.cpp
#echo $#
$(CXX) -c $(CXXFLAGS) $(DBGCXXFLAGS) -o $# $<
# Release rules
release: $(RELEXE)
$(RELEXE): $(RELOBJS)
$(CXX) $(CXXFLAGS) $(RELCXXFLAGS) -o $(RELEXE) $^
$(RELDIR)/%.o: $(SRC_DIR)%.cpp
$(CXX) -c $(CXXFLAGS) $(RELCXXFLAGS) -o $# $<
# Other rules
prep:
#mkdir -p $(DBGDIR) $(RELDIR)
remake: clean all
clean:
#rm -rf $(RELEXE) $(RELOBJS) $(DBGEXE) $(DBGOBJS)
#echo "Cleaned!"
Produces the following error:
make: *** No rule to make target 'out/release/src/main.cpp', needed by 'out/release/<ProgramName>'. Stop.
Where is the issue? And when does make assume that the directory of the source files is in '/out/release'? I'm still a noob writing makefiles, I've always been a little bit lazy and used Visual Studio even when targeting linux.
Any help is vastly appreciated!!
You have a couple of small errors.
OBJS := $(SRCS:.cpp:=.o)
You have an extra colon. As written, you change all instances of .cc:, but there are none, so OBJS is src/main.cpp.
You probably meant this:
OBJS := $(SRCS:.cpp=.o)
But this gives src/main.o. You probably did not intend to keep the path. You can correct that with another line:
OBJS := $(notdir $(OBJS))
Then here:
$(RELDIR)/%.o: $(SRC_DIR)%.cpp
...
You are missing a slash. The correct line is
$(RELDIR)/%.o: $(SRC_DIR)/%.cpp
...
The rule as written searches for a file that doesn't exist.
You can detect such errors yourself -- and correct them as you develop the makefile -- with diagnostics like this:
$(info OBJS is $(OBJS))
$(info RELOBJS is $(RELOBJS))
Write a small makefile that works perfectly, then add complexity a little at a time, testing at every step, and never add to code that doesn't work.

C++ Makefile path include and src directory makefile

I'm trying to create a Makefile for my C++ project, which has the following structure:
root
├── include/
| └──external
| └── stb_image.h
│ └── all .h files here
├── src/
| └── main.cpp
│ └── all .cpp files here
└── Makefile
To compile this project, I'm trying to use the Makefile proposed in this answer, which is for gcc, but I have added CC:=g++ so it should work (I think):
SRC_DIR := src
OBJ_DIR := obj
BIN_DIR := bin
EXE := $(BIN_DIR)/color
SRC := $(wildcard $(SRC_DIR)/*.cpp)
OBJ := $(SRC:$(SRC_DIR)/%.cpp=$(OBJ_DIR)/%.o)
CPPFLAGS := -Iinclude -MMD -MP
CFLAGS := -g -std=c++2a -Wall
LDFLAGS := -Llib
LDLIBS := -lpthread
CC := g++
.PHONY: all clean
all: $(EXE)
$(EXE): $(OBJ) | $(BIN_DIR)
$(CC) $(LDFLAGS) $^ $(LDLIBS) -o $#
$(OBJ_DIR)/%.o: $(SRC_DIR)/%.cpp | $(OBJ_DIR)
$(CC) $(CPPFLAGS) $(CFLAGS) -c $< -o $#
$(BIN_DIR) $(OBJ_DIR):
mkdir -p $#
clean:
#$(RM) -rv $(BIN_DIR) $(OBJ_DIR)
-include $(OBJ:.o=.d)
But when running make I obtain a very long error which I do not understand. I do not know if this error comes from having main.cpp without a main.h.
I need this makefile because I want to separate the declarations and the implementations of my project. Before doing that, I had everything done in headers files and I could compile my project with the following command:
g++ -g -std=c++2a -Wall -Isrc/include -o bin/color.exe src/main.cc -lpthread
Any idea what am I doing wrong? I still do not know a lot about Makefiles, so maybe I'm doing something weird.

Generic Makefile build directory error

I have the following directory structure for a dummy C project.
.
├── inc
│   ├── getmsg.c
│   └── getmsg.h
├── makefile
└── src
└── main.c
My current generic Makefile is below,
TARGET = main
# finds all .c files, inc/getmsg.c src/main.c
SOURCES := $(shell find * -name *.c)
# converts all .c files to .o files, inc/getmsg.o src/main.o
OBJECTS := $(SOURCES:.c=.o)
# directories that contain .h files for gcc -I flag, inc/
HEADERS := $(dir $(shell find * -name *.h))
CC = gcc
CFLAGS = -Wall -std=c99 -iquote "$(HEADERS)"
all: $(TARGET)
%.o: %.c
$(CC) $(CFLAGS) -c $< -o $#
$(TARGET): $(OBJECTS)
$(CC) -o $# $^
clean:
rm -rf $(shell find * -name *.o) $(TARGET)
This all compiles fine however it just dumps the .o files into the same directory as its corresponding .c file.
What I would like to do is have all object files put into a build directory. To do this I change the OBJECTS to OBJECTS := $(patsubst %,build/%,$(notdir $(SOURCES:.c=.o))) which lists the files build/getmsg.o build/main.o. Then I set the %.o target to build/%.o: %.c.
This however returns No rule to make target 'build/getmsg.o'. So the make file is unable to build the .o files. What am I missing here?
Try changing
%.o: %.c
to
build/%.o: %.c

Using Makefile with subfolders

I have the following directory structure.
Project/
├── bin/
├── src/
│ ├── main.c
│ ├── util/
│ ├── util.c
│ ├── util.h
├── obj/
├── .depend/
All my source code are in the src folder. In the src root is my main.c file; which includes other files that are on the same level that he (or within a same level folder). I have a Makefile below that works well for all files in the same level of main.c but does not work on files in a subfolder within src
How change my Makefile to allow subfolder within the src folder?
CC := gcc
CFLAGS := -Wall -Wextra
BINDIR := bin
OBJDIR := obj
SRCDIR := src
DEPDIR := .depend
SOURCES := $(wildcard $(SRCDIR)/*.c)
OBJECTS := $(patsubst $(SRCDIR)/%.c, $(OBJDIR)/%.o, $(SOURCES))
DEPENDS := $(patsubst $(SRCDIR)/%.c, $(DEPDIR)/%.d, $(SOURCES))
$(BINDIR)/app: $(OBJECTS) | $(BINDIR)
#$(CC) -o $# $^
-include $(DEPENDS)
$(OBJDIR)/%.o: $(SRCDIR)/%.c | $(OBJDIR)
#$(CC) $(CFLAGS) -c -o $# $<
$(DEPDIR)/%.d: $(SRCDIR)/%.c | $(DEPDIR)
#$(CC) -MM -MG $< | sed 's!^\(.\+\).o:!$(DEPDIR)/\1.d $(OBJDIR)/\1.o:!' > $#
$(DEPDIR) $(BINDIR) $(OBJDIR):
#mkdir $#
clean:
#rm -rf $(BINDIR)/*
#rm -rf $(OBJDIR)/*
.PHONY: clean
EDIT: the .o and .d files do not need to respect the original design of the structure. And I'm using Windows (MinGW)
First, you'll have to change your SOURCES to recursively find the sources. This can be done in pure make:
subdirs = $(filter-out $1,$(sort $(dir $(wildcard $1*/))))
rfind = $(wildcard $1$2) $(foreach d,$(call subdirs,$1),$(call rfind,$d,$2))
SOURCES := $(call rfind,$(SRCDIR)/,*.c)
Everything else will work, except for directory creation. First, change your prerequisites to use $(#D) with secondary expansion:
.SECONDEXPANSION:
$(OBJDIR)/%.o: $(SRCDIR)/%.c | $$(#D)
...
$(DEPDIR)/%.d: $(SRCDIR)/%.c | $$(#D)
...
Then, change your directory creation rule to include all the directories:
$(BINDIR) $(patsubst %/,%,$(sort $(dir $(OBJECTS) $(DEPENDS)))):
#mkdir -p $#
Like the recursive find, it uses sort to deduplicate the directories (otherwise make will warn) and strips the trailing slash (because $(#D) won't have a trailing slash). Note that -p is needed to avoid issues with order and with directories only containing other directories and no sources.
try to set your sources like this:
SOURCES := $(shell find $(SRCDIR) -type f -name "*.c")
if it does not work right a way, try to debug it by just running in your command line
find src -type f -name "*.c"
and see if it outputs the correct list of files and with correct relative path, adjust accordingly.
Note, that this approach works only in unix-like environment, if you are using MinGW from MSYS or Cygwin environment it should work.

Resources