Makefile with multiple programs with debug and release modes? - makefile

I've written simple calculator in C++, and decided to separate lexer+parser and actual "frontends" which can be GUI or command-line. Project structure looks like that:
src/
parser.hpp
parser.cpp
scanner.hpp
scanner.cpp
exceptions.hpp
term-calc.cpp
gui-calc.cpp
Makefile
Obviously parser and scanner should be compiled into object files separately, and term-calc and gui-calc separately. Furthermore I want to have debug builds and release builds,
so I imagine final project structure like that:
src/
...
obj/
debug/
parser.o
scanner.o
...
release/
parser.o
scanner.o
...
out/
debug/
term-calc
gui-calc
release/
term-calc
gui-calc
Makefile
I'm pretty new to Makefiles but this is what I came up with so far (I've ommited automatic dependency generation for now):
CXX := g++
CXXFLAGS := -std=c++17 -pedantic -Wall -Wextra -fno-rtti
SRCDIR := $(CURDIR)/src # sources
OBJDIR := $(CURDIR)/obj # objects
INCDIR := $(CURDIR)/inc # generated dependencies
OUTDIR := $(CURDIR)/out # executables
# target programs
TERM_CALC := term-calc
GUI_CALC := gui-calc
all: debug
# debug flags
debug: CXXFLAGS += -O0 -g -fsanitize=address
# debug objects and executables go into /debug subdirectory
debug: OBJDIR += /debug
debug: OUTDIR += /debug
# release flags
release: CXXFLAGS += -Os -ffunction-sections -fdata-sections -flto -fno-ident
release: LDFLAGS += -Wl,-gc-sections -s -flto
# release objects and executables go into /release subdirectory
release: OBJDIR += /release
release: OUTDIR += /release
# common objects
OBJECTS := $(OBJDIR)/scanner.o $(OBJDIR)/parser.o
# target-specific objects
$(TERM_CALC): OBJECTS += $(OBJDIR)/term-calc.o
$(GUI_CALC): OBJECTS += $(OBJDIR)/gui-calc.o
# TARGET_NAME is name of program to build
# ensure it is valid, if defined
ifdef TARGET_NAME
ifneq ($(TARGET_NAME),$(TERM_CALC))
ifneq ($(TARGET_NAME),$(GUI_CALC))
$(error Invalid target name '$(TARGET_NANE)')
endif
endif
endif
ifdef TARGET_NAME
# how to build target program
$(TARGET_NAME): $(OUTDIR)/$(TARGET_NAME)
$(OUTDIR)/$(TARGET_NAME): $(OBJECTS)
#mkdir -p $(OUTDIR)
$(CXX) $(CXXFLAGS) $(LDFLAGS) $(LDLIBS) $^ -o $#
endif
# how to build objects
$(OBJDIR)/%.o: $(SRCDIR)/%.cpp
#mkdir -p $(OBJDIR)
$(CXX) $(CXXFLAGS) $(CPPFLAGS) -c $< -o $#
# if TARGET_NAME is not specified, just compile objects
TARGET_NAME ?= $(OBJECTS)
debug : $(TARGET_NAME)
release : $(TARGET_NAME)
# clean everything
clean:
$(RM) -r $(OBJDIR) $(OUTDIR) $(INCDIR)
.PHONY = all debug release clean
Unfortunately it absolutely does not work, while writing this makefile, I was getting many errors but even after fixing all of them as I thought, I still get:
Makefile:52: *** mixed implicit and normal rules: deprecated syntax
make: *** No rule to make target '/%.cpp', needed by '/root/cpp/calc/obj'. Stop.

Related

How to use python "include" and "libs" path in a windows makefile to compile all python embedded C++ program in a folder?

Makefile specified in this question, compiling all the cpp programs in a folder but not with python embedded cpp programs.
all: myUB
sourcesC := $(wildcard ../src/*.cpp)
objectsC := $(patsubst %.cpp,%.o,$(sourcesC))
INPATH=-I"C:/Python27/include"
LIBPATH=-L"C:/Python27/libs"-lpython27
myUB:
#echo 'Building target $#'
g++ -O0 -Wall -c -g3 -fmessage-length=0 \
$(sourcesC)
del *.o
clean:
Your final makefile could look somthing like:
all: myUB
sourcesC := $(wildcard ../src/*.cpp)
# Not used
#objectsC := $(patsubst %.cpp,%.o,$(sourcesC))
INC = -IC:\Python27\include
LIBS = -LC:\Python27\libs -lpython27
myUB:
#echo 'Building target $#'
g++ -O0 -Wall -g3 -fmessage-length=0 -o myprog.out $(sourcesC) $(INC) $(LIBS)
clean:
rm myprog.out
update
For the undefined ref to WinMain(), it means the linker can't find this function in your code. Either you need to include a library/object that contains it or you can define it yourself in a cpp file like:
#include <windows.h>
int WINAPI (*MyDummyReferenceToWinMain)(HINSTANCE hInstance, ..., int
nShowCmd ) = &WinMain;
I got the function template from here.
But this seems to mean that you are creating a windows application instead of a console app which uses int main(...) entry point.
Update2
I have made a new makefile to do what you have asked for in your latest comment which seems to be to create one executable per source file - I am assuming each source file has its own main.
# Build vars
CXX = g++
CXX_FLAGS = -O0 -Wall -g3
INC = -IC:\Python27\includ
LIBS = -LC:\Python27\libs -lpython27
# Sources
SRC_DIR=src
SOURCES = $(wildcard $(SRC_DIR)/*.cpp)
$(info SOURCES: $(SOURCES))
# Executables
EXE_DIR=bin
EXECUTABLES = $(subst $(SRC_DIR)/,$(EXE_DIR)/,$(subst cpp,out,$(SOURCES)))
$(info EXECUTABLES: $(EXECUTABLES))
$(info ----)
# Directories
DIRS = $(EXE_DIR)
# Rule to create folders and compile executables
all: $(DIRS) $(EXECUTABLES)
# Pattern rule to build each executable
$(EXE_DIR)/%.out : $(SRC_DIR)/%.cpp
#echo "compiling $< --> $#"
#$(CXX) $(CXX_FLAGS) -o $# $< $(INC) $(LIBS)
# Rule to create output dirs
$(DIRS):
#echo "Creating output folders"
#mkdir -p $(EXE_DIR)
# Rule to clean up
clean:
#echo "Cleaning"
#rm -rf $(EXE_DIR)
This should create one executable in the folder bin/ for each source file (.cpp) in folder src/.

CXXFLAGS not being appended in makefile

I have a makefile that I want to be able to produce both a release build and a debug build. When I just run:
make
I want the CXXFLAGS to be:
-std=c++11 -Isrc/includes -c -Os
and when I run
make debug
I want the CXXFLAGS to be
-std=c++11 -Isrc/includes -c -g
I am trying to do this through he use of phony targets and appending the extra flags to the CXXFLAGS variable, however these extra flags are never being appended. why is it that make debug still produces:
g++ -std=c++11 -Isrc/includes -c src/file.cpp -o build/obj/file.o
and not the expected
g++ -std=c++11 -Isrc/includes -c -g src/file.cpp -o build/obj/file.o
when it is run?
contents of makefile:
vpath %.cpp src/macro
vpath %.cpp src/data_types
vpath %.hpp src/includes
vpath %.cpp src
CXX := g++
CXXFLAGS := -std=c++11 -Isrc/includes -c
LXX = g++
LXXFLAGS :=
BUILDDIR := build
OBJDIR := $(BUILDDIR)/obj
SRCS := $(notdir $(shell find -name '*.cpp'))
OBJS := $(patsubst %.cpp, $(OBJDIR)/%.o, $(SRCS))
all: release aval
aval: builddir $(OBJS) $(SRCS)
$(LXX) $(LXXFLAGS) $(OBJS) -o $(BUILDDIR)/aval
$(OBJDIR)/%.o: %.cpp
$(CXX) $(CXXFLAGS) $^ -o $#
.PHONY: release
release: CXXFLAGS += -Os
release: LXXFLAGS += -s -Os
.PHONY: debug
debug: clean db aval
.PHONY: db
db: CXXFLAGS += -g
.PHONY: builddir
builddir:
#mkdir -p $(OBJDIR)
.PHONY: clean
clean:
#rm -f -r build/obj/*.o
#rm -f build/avalanche
the issue with what you are doing is that you are editing the CXXFLAGS in the dependency list of a rule which won't work because of the way the make file is parsed.
Another way - and really easy, and saves you recursively calling make - which I don't see as particularly evil - but some do. Its certainly less complicated this way I find.
CXXFLAGS = defaults
ifneq (,$(findstring release,$(MAKECMDGOALS)))
CXXFLAGS += release
else ifneq (,$(findstring debug,$(MAKECMDGOALS)))
CXXFLAGS += debug
endif
all:
#echo CXXFLAGS = $(CXXFLAGS)
# Rules for release / debug. The `; #:` part means the body of the rule is empty (do nothing). It just "calls" the dependency rule `all`.
release: all ; #:
debug: all ; #:
So here we are looking at the command goals and "parsing them" to look for your options and add to the flags.
We also need rules for debug and release to call the build rule (which I am calling all for the moment).
Here is the output:
admin#osboxes:~/sandbox/flags$ make
CXXFLAGS = defaults
admin#osboxes:~/sandbox/flags$ make release
CXXFLAGS = defaults release
admin#osboxes:~/sandbox/flags$ make debug
CXXFLAGS = defaults debug
The approach you have chosen does not work, because
db: CXXFLAGS += -g
means that the variable CXXFLAGS is updated to include -g for the goal db, but no other goal. I.e. this change is not global like you intended it to be.
The following would be one way of implementing what you intended:
.PHONY: all release
# NOTE: must be first goal in Makefile to be default goal
all release:
$(MAKE) -f $(lastword $(MAKEFILE_LIST)) BUILD_CXXFLAGS="-Os" BUILD_LXXFLAGS="-s -Os" build
.PHONY: debug
debug:
$(MAKE) -f $(lastword $(MAKEFILE_LIST)) BUILD_CXXFLAGS="-g" BUILD_LXXFLAGS="-g" build
.PHONY: build
build: clean aval
CXX := g++
CXXFLAGS := $(BUILD_CXXFLAGS) -std=c++11 -Isrc/includes -c
LXX = g++
LXXFLAGS := $(BUILD_LXXFLAGS)
# ... and the rest of your original Makefile ...
With build implemented as a dummy echo, I get the following output from the above:
$ make -s
CXX '-Os -std=c++11 -Isrc/includes -c' LXX '-s -Os'
$ make -s all
CXX '-Os -std=c++11 -Isrc/includes -c' LXX '-s -Os'
$ make -s release
CXX '-Os -std=c++11 -Isrc/includes -c' LXX '-s -Os'
$ make -s debug
CXX '-g -std=c++11 -Isrc/includes -c' LXX '-g'
BTW: you also need to add -g to LXXFLAGS, otherwise you won't get a debug binary.

Defer evaluation of automatic variables in recipes

I have the following makefile:
CC ?= gcc
LD := gcc
CFLAGS := -Wall -Wextra -Werror -Wfatal-errors
LDFLAGS :=
LIBRARIES := m c
INCLUDEDIRS := .
LIBS = $(addprefix -l,$(LIBRARIES))
INCLUDES = $(addprefix -I,$(INCLUDEDIRS))
SRC := $(wildcard *.c)
TARGET = $(TARGETDIR)/test
OBJDIR = $(TARGETDIR)/obj/
OBJ = $(addprefix $(OBJDIR),$(SRC:%.c=%.c.o))
.SUFFIXES:
.SUFFIXES: .c.o
.PHONY: all debug i7avx i7avx-debug
all: TARGETDIR := generic
all: CFLAGS += -O3
all: LDFLAGS += -s
all: $(TARGET)
debug: CFLAGS += -Og
debug: TARGETDIR := generic/dbg
debug: $(TARGET)
$(OBJDIR):
#mkdir -p $(OBJDIR)
$(OBJ): | $(OBJDIR)
$(OBJDIR)%.c.o : %.c
$(CC) $(CFLAGS) $(INCLUDES) -c $< -o $#
$(TARGET) : $(OBJ)
$(LD) $(LDFLAGS) -o $# $^ $(LIBS)
The special thing here is that the output directory depends on the target.
Currently, only all and debug is defined, but the idea is to support a whole slew of architectures, and to define an outputdir per target.
Problem: this does not work. If I run this, I get:
cc -Wall -Wextra -Werror -Wfatal-errors -O3 -I. -c main.c -o /obj/main.c.o
Assembler messages:
Fatal error: can't create /obj/main.c.o: No such file or directory
make: *** [Makefile:37: /obj/main.c.o] Error 1
Which implies that the TARGETDIR variable was expanded too late.
If I replace the automatic variables with real variables, it does work:
$(OBJ): | $(OBJDIR)
$(OBJDIR)%.c.o : %.c
$(CC) $(CFLAGS) $(INCLUDES) -c $(SRC) -o $(OBJ)
$(TARGET) : $(OBJ)
$(LD) $(LDFLAGS) -o $(TARGET) $(OBJ) $(LIBS)
running this:
cc -Wall -Wextra -Werror -Wfatal-errors -O3 -I. -c main.c -o generic/obj/main.c.o
gcc -s -o generic/test generic/obj/main.c.o -lm -lc
Sooo, how can I make the autmatic variables expand after the TARGETDIR was defined?
Make does handle wildcards very deftly, or this would be a much easier problem.
As it is, I think the best solution is to use recursive Make. Just change this:
all: $(TARGET)
debug: $(TARGET)
to this:
all debug:
$(MAKE) $(TARGET) TARGETDIR=$(TARGETDIR) CFLAGS+='$(CFLAGS)' LDFLAGS=$(LDFLAGS)
First, the reason why it does not work :
You're using target-specific variables, but those are only available in the context of a target recipe (I'm quoting the manual here), not during the rules evaluation :
Make will first read your Makefile, evaluate your $(OBJDIR) and $(TARGET) rules (at this point $(TARGETDIR)is not yet defined) then, it will try to update all, and at this point set $(TARGETDIR) to the target-specific value for all (which explains why you're second example work, but it should rebuild every time).
I may have some hints to achieving what you're trying to do (I'm actually planning on doing a similar thing soon) :
you could use the eval function to generate one rule for each build/archi, like this:
#define TARGET_RULE
$(TARGET) : $(OBJ)
$$(RECIPE)
#endif
$(foreach TARGETDIR, $(BUILD_LIST), $(eval $(TARGET_RULE))
($$ is needed for the recipe to avoid it being expanded during the rule evaluation)
You should also be able to define only the rule or rules for the target you are currently building (not sure if that would make a signifiant perf difference).

How can I run a pattern rule within another rule in a makefile?

I am looking to write a makefile to automate the compiling of a project that I am working on where the files may, or may not, change in number. I also need to be able to quickly tell make to compile the files as a debug build or a release build (differentiated by a command line define). After some research, I came upon pattern rules and made one. Here is the code I have so far:
# Our folders
# ODIR - The .o file directory
# CDIR - The .cpp file directory
# HDIR - The .hpp file directory
ODIR = obj
CDIR = src
HDIR = inc
# Get our compiler and compiler commands out of the way
# CC - Our compiler
# CFNO - Our compiler flags for when we don't have an output file
# CF - Our compiler flags. This should be appended to any compile and should
# have the name of the output file at the end of it.
# OF - Object flags. This should be appended to any line that is generating
# a .o file.
CC = g++
CFNO = -std=c++11 -wall -Wno-write-strings -Wno-sign-compare -lpaho-mqtt3c -pthread -O2 -I$(HDIR)
CF = $(CFNO) -o
OF = -c
# Our project/output name
NAME = tls_test
# Set out Debug and Release settings, as well as any defines we will use to identify which mode we are in
# NOTE: '-D[NAME OF DEFINE]' is how you create a define through the compile commands
DEBUG = -DDEBUG -g
RELEASE = -DRELEASE
# Our two compile modes
# eval allows us to create variables mid-rule
debug:
$(eval DR = $(DEBUG))
release:
$(eval DR = $(RELEASE))
# Our compiling commands
all:
$(CC) $(CF) $(NAME) $(ODIR)/*.o
# NOTE: $# is the end product being created and $< is the first of the prerequisite
$(ODIR)/%.o: $(CDIR)/%.c
echo "$(CC) $(DR) $(OF) $(CF) $# $<"
The issue that I am having is with the order that I need things to run in. The command line call should tell make to use either debug or release, which sets a make variable, then call all. all should then run the pattern rule at the bottom before running the line currently in the all rule. So, how do I make a pattern rule a dependency and how do I call a rule from another rule?
Use target-specific variables
While not strictly necessary, separating your flags goes a long way in managing build options, you can then use target-specific variable appends to toggle the flags. While you're at it you might as well use the built-in variable names.
I've also added dependency generation (-MMD -MP) because it's always useful.
ODIR := obj
CDIR := src
HDIR := inc
SRCS := $(wildcard $(CDIR)/*.cpp)
OBJS := $(SRCS:$(CDIR)/%.cpp=$(ODIR)/%.o)
DEPS := $(OBJS:%.o=%.d)
CPPFLAGS := -I$(HDIR) -MMD -MP
CXXFLAGS := -std=c++11 -Wall -Wno-write-strings -Wno-sign-compare -pthread -O2
LDFLAGS := -pthread
LDLIBS := -lpaho-mqtt3c
NAME := tls_test
.PHONY: debug release clean
debug: CPPFLAGS+=-DDEBUG
debug: CXXFLAGS+=-g
release: CPPFLAGS+=-DRELEASE
debug release: $(NAME)
$(NAME): $(OBJS)
$(CXX) $(LDFLAGS) $^ $(LDLIBS) -o $#
$(OBJS): $(ODIR)/%.o: $(CDIR)/%.cpp
$(CXX) $(CXXFLAGS) $(CPPFLAGS) -c $(OUTPUT_OPTION) $<
clean: ; $(RM) $(NAME) $(OBJS) $(DEPS)
-include $(DEPS)

Eclipse CDT MinGW - Make skips rules, no such file or directory error

I am trying to create debug and release configurations for a project, and I just can't figure out what is wrong.
For some reason, when I do 'make debug', make skips the dependencies for 'all', giving me a bunch of errors from g++ (no such file or directory). Even weirder is that if I do 'make all', everything works just fine.
Here is the makefile:
SHELL=/bin/sh
CXX=g++
LIBS=-LE:\Software\StrategyEngine\Release -llibdebug
CFLAGS=-Wall -O3 -IE:\Software\StrategyEngine\include
BDIR=Build\Release
debug: CFLAGS+=-g -DDEBUG
debug: LIBS=-LE:\Software\StrategyEngine\Debug -llibdebug
debug: BDIR=Build\Debug
OBJS= $(BDIR)\blocksort.o \
#... more object files
$(BDIR)\CompressionStream.o
debug: all
all: $(OBJS)
$(CXX) $(LIBS) -shared -o $(BDIR)\libbz2.dll $(OBJS)
$(CXX) $(LIBS) $(CFLAGS) -o $(BDIR)\bzip2-shared bzip2.cpp $(BDIR)\libbz2.dll
$(BDIR)\blocksort.o: blocksort.cpp
$(CXX) $(CFLAGS) -c blocksort.cpp -o $(BDIR)\blocksort.o
#.... more rules for each object file defined in OBJS
clean:
rm -f Build\debug\* Build\release\*
Why is this happening? I can't find any error in the makefile.
I am using the mingw compiler suite (make version 3.81), running on windows 7.
The object file location BDIR in your makefile doesn't change depending on the build mode.
I suggest refactoring the script as follows:
SHELL=/bin/sh
CXX=g++
BUILD := debug # default mode
CFLAGS.release := -Wall -O3 -IE:/Software/StrategyEngine/include -D_NDEBUG
CFLAGS.debug := -Wall -g -IE:/Software/StrategyEngine/include -DDEBUG
LIBS.release := -LE:/Software/StrategyEngine/Release -llibdebug
LIBS.debug := -LE:/Software/StrategyEngine/Debug -llibdebug
BDIR.release := Build/Release
BDIR.debug := Build/Debug
CFLAGS := ${CFLAGS.${BUILD}}
LIBS := ${LIBS.${BUILD}}
BDIR := ${BDIR.${BUILD}}
OBJS= $(BDIR)/blocksort.o /
#... more object files
$(BDIR)/CompressionStream.o
all: $(OBJS)
$(CXX) $(LIBS) -shared -o $(BDIR)/libbz2.dll $(OBJS)
$(CXX) $(LIBS) $(CFLAGS) -o $(BDIR)/bzip2-shared bzip2.cpp $(BDIR)/libbz2.dll
$(BDIR)/blocksort.o: blocksort.cpp
$(CXX) $(CFLAGS) -c blocksort.cpp -o $(BDIR)/blocksort.o
#.... more rules for each object file defined in OBJS
clean:
rm -f Build/debug/* Build/release/*
.PHONY: all clean
And use it like:
make BUILD=debug
make BUILD=release

Resources