Extending a simple makefile to support different implicit compiler flags - c++

I have been using a makefile that was fairly straightforward. I defined OBJS with a list of .cc files. I set up dependencies and include flags and appended all of those to $CXXFLAGS. It looks something like this:
SRCS = file1.cc file2.cc file3.cc
OBJS = $(SRCS:.cc=.o)
CXXFLAGS=some flags
CXXFLAGS+=some include dirs
$(mylib.so): $OBJS
$CXX -shared -o $# $^
mylib.so uses the CXXFLAGS (implicitly) and everything builds just fine.
I have recently had the need to have mylib_1.so and mylib_2.so, in addition to mylib.so. Each .so depend on all the same .cc files, but the compiler flags are all different (including include directories).
How do I get it so I can set the compiler flags based on the target .so? The problem that I have is that if I set CXXFLAGS more than once it gets overwritten. It's almost like I need an if/else situation.
I tried doing something like setting three different flags, $CXXFLAGS1, $CXXFLAGS2, $CXXFLAGS3, and using those in the line
$(mylib1.so): $OBJS
$CXX $(CXXFLAGS1) -shared -o $# $^
but that does not work.
How do I accomplish what I am trying to do? Is it better to have 3 separate makefiles? I did find a way to get it to work. I can stop using $OBJS and spell out the flags explicitly for each source file but this seems like a horrible idea in terms of scaling to size.

Your CXXFLAGS1 in your example is only used at the stage of creating the .so file, not for compilation of the actual C++ sources (which is what you are trying to do, I assume).
To achieve the above, consider making the Makefile invoke itself 3 times for 3 different targets and pass CXXFLAGS (with different values) as part of MAKEFLAGS or in the command line.
Update: here's an example
all: build-lib1 build-lib2 build-lib3
build-lib1:
$(MAKE) $(MAKEFLAGS) CXXFLAGS="$(CXXFLAGS1)" lib1.so
build-lib2:
$(MAKE) $(MAKEFLAGS) CXXFLAGS="$(CXXFLAGS2)" lib2.so
build-lib3:
$(MAKE) $(MAKEFLAGS) CXXFLAGS="$(CXXFLAGS3)" lib3.so
$(lib1.so): $OBJS
$(CXX) -shared -o $# $^
etc...

Makefiles can have target-specific variable values. Something like:
$(mylib1.so): CXXFLAGS += -lib1flags
$(mylib2.so): CXXFLAGS += -lib2flags
$(mylib3.so): CXXFLAGS += -lib3flags
According to the documentation, the flags will propagate to prerequisite targets.
There is one more special feature of target-specific variables: when
you define a target-specific variable that variable value is also in
effect for all prerequisites of this target, and all their
prerequisites, etc. (unless those prerequisites override that variable
with their own target-specific variable value). So, for example, a
statement like this:
prog : CFLAGS = -g
prog : prog.o foo.o bar.o
will set CFLAGS to ā€˜-gā€™ in the recipe for prog, but it will also set
CFLAGS to ā€˜-gā€™ in the recipes that create prog.o, foo.o, and bar.o,
and any recipes which create their prerequisites.

I would do this with a recursive call to make. I would use two makefiles:
In Makefile:
all: mylib1.so mylib2.so
SRCS := file1.cc file2.cc file3.cc
mylib1.so: $(SRCS)
test -d mylib1 || mkdir mylib1
$(MAKE) -f ../lib.mak -C mylib1 TARGET=mylib1.so CXXFLAGS=-DMYLIB=1
cp mylib1/mylib1.so mylib1.so
mylib2.so: $(SRCS)
test -d mylib2 || mkdir mylib2
$(MAKE) -f ../lib.mak -C mylib2 TARGET=mylib2.so CXXFLAGS=-DMYLIB=2
cp mylib2/mylib2.so mylib2.so
In lib.mak, in the same directory:
VPATH = ..
SRCS := file1.cc file2.cc file3.cc
OBJS := $(SRCS:.cc=.o)
$(TARGET): $(OBJS)
$(CXX) -shared -o $# $^
The second makefile actually builds the library, but only uses one set of CXXFLAGS. The primary makefile calls the first makefile for each version with separate CXXFLAGS and in a separate directory. The VPATH makes it easier to compile source files that aren't in the same directory.
I tested this setup with a dry run,
test -d mylib1 || mkdir mylib1
make -f ../lib.mak -C mylib1 TARGET=mylib1.so CXXFLAGS=-DMYLIB=1
make[1]: Entering directory `/home/depp/Maketest2/mylib1'
g++ -DMYLIB=1 -c -o file1.o ../file1.cc
g++ -DMYLIB=1 -c -o file2.o ../file2.cc
g++ -DMYLIB=1 -c -o file3.o ../file3.cc
g++ -shared -o mylib1.so file1.o file2.o file3.o
make[1]: Leaving directory `/home/depp/Maketest2/mylib1'
cp mylib1/mylib1.so mylib1.so
test -d mylib2 || mkdir mylib2
make -f ../lib.mak -C mylib2 TARGET=mylib2.so CXXFLAGS=-DMYLIB=2
make[1]: Entering directory `/home/depp/Maketest2/mylib2'
g++ -DMYLIB=2 -c -o file1.o ../file1.cc
g++ -DMYLIB=2 -c -o file2.o ../file2.cc
g++ -DMYLIB=2 -c -o file3.o ../file3.cc
g++ -shared -o mylib2.so file1.o file2.o file3.o
make[1]: Leaving directory `/home/depp/Maketest2/mylib2'
cp mylib2/mylib2.so mylib2.so

Related

Makefile missing include path Although the path exists and defined

i have make file which i try to make them generic
but it keeps to compline it missing include directory
this is the makefile :
CXX=g++
CPPFAGS= -Wall -O0 -g -std=c++14
INCLUDES = -I/home/vagrant/libuv/include -Isrc
LIBS_DIRS = -L/home/vagrant/libuv/build
LDFLAGS= -lssl -lcrypto
LIB_STATIC = -Wl,--no-as-needed -Bstatic -luv_a -ldl -lpthread
SOURCE = $(wildcard echo.cpp) \
$(wildcard src/*.cpp)
OBJ = $(SOURCE:.cpp=.o)
DEP = $(OBJ:.o=.d)
TARGET = myproj
$(TARGET) : $(OBJ)
$(CXX) $(CPPFLAGS) $(INCLUDES) -o $# $^ $(LIBS_DIRS) $(LDFLAGS) $(LIB_STATIC)
all: $(TARGET)
clean:
rm -f $(OBJ) $(TARGET)
cleandep:
rm -f $(DEP)
.PHONY:all clean cleandep
when i make : make -n :
make -n
g++ -c -o echo.o echo.cpp
g++ -c -o src/base64.o src/base64.cpp
g++ -c -o src/Server.o src/Server.cpp
g++ -c -o src/sha1.o src/sha1.cpp
g++ -c -o src/Client.o src/Client.cpp
g++ -I/home/vagrant/libuv/include -Isrc -o myproj echo.o src/base64.o src/Server.o src/sha1.o src/Client.o -L/home/vagrant/libuv/build -lssl -lcrypto -Wl,--no-as-needed -Bstatic -luv_a -ldl -lpthread
when i invoke make , im getting this error:
make
g++ -c -o echo.o echo.cpp
In file included from src/Server.h:9:0,
from echo.cpp:1:
src/Client.h:6:10: fatal error: uv.h: No such file or directory
#include <uv.h>
^~~~~~
compilation terminated.
make: *** [echo.o] Error 1
but the uv do exist in : /home/vagrant/libuv/include
You have no rule to build your object files: you've only defined a rule to link your object files into a final executable. As mentioned in the comments, adding $(INCLUDES) into that recipe is useless because header file directories are only used during compiling (creating object files) not linking (converting object files and libraries into executables).
Because you haven't defined your own rule to build object files, you're using make's built-in rule. But make's built-in rule doesn't know anything about a variable named INCLUDES, so that variable is not used during compilation. You can easily see this by looking at the compiler commands generated by make.
You need to either (a) create your own rule for compiling object files that uses your personal make variables, or (b) use the normal built-in variables that make expects to be used with its built-in rules.
For (b), as described in the manual, you should take your current CPPFAGS [sic] variable and rename it to CXXFLAGS, take your current INCLUDES variable and rename it CPPFLAGS, take your current LIBS_DIRS variable and rename it LDFLAGS, and take your current LDFLAGS variable and rename it to LDLIBS.
Also just to note, you have DEPS etc. but there is nothing in your makefile that does anything with them or to create them so they're useless.

Makefile for many c++ executables

I am working on a project where I constantly need to create new c++ executables. They all rely on some common headers and sources files, so I am wondering how to simplify the compilation and Makefile writing.
The best I have come up with so far is something like this:
file1: $(BUILDDIR)/$#.o $(COMMON_OBJECTS) $(COMMON_LIBS)
$(CCCOM) $(CCFLAGS) $(BUILDDIR)/$#.o $(COMMON_OBJECTS) -o $(BINDIR)/$# $(LIBFLAGS)
and then I have to copy this target for each executable I want to add. Ideally I want to define this rule once for arbitrary target and then simply call make any_file_name.
Is something like that possible?
How do people organize c++ project with lots of executables? (I am new to c++ and coming from python that is a very natural thing)
You could make it so that each executable corresponds to a single .cpp file in a directory (e.g. executables/foo.cpp, executables/bar.bpp), and then work from there -- this will save you from having to touch the Makefile every time you add another one.
You should, probably, also set up your project to create a shared library, which the (light-weight) executables link to. (The executables effectively just doing some command-line parsing, and offloading the actual work to library functions.) This way, you will not end up with the code from those $(COMMON_OBJECTS) being replicated in every executable.
To simplify everything I'm assuming you have sources file1.cpp, file2.cpp and so on, and all your files reside in the same directory. Then Makefile fragment below will do what you want:
all: $(basename $(wildcard file?.cpp))
file%: file%.cpp
To make everyhing:
make all
To make file1:
make file1
To make file1 and file2:
make file1 file2
Here is an example makefile with automatic header dependency generation for you:
BUILD := debug
BUILD_DIR := ${BUILD}
CXX := g++
cppflags.debug :=
cppflags.release := -DNDEBUG
cppflags := ${cppflags.${BUILD}} ${CPPFLAGS}
cxxflags.debug :=
cxxflags.release := -O3
cxxflags := ${cxxflags.${BUILD}} ${CXXFLAGS}
ldflags := ${LDFLAGS}
ldlibs := ${LDLIBS}
exes := # Executables to build.
### Define executables begin.
exes += exe1
exe1.obj := exe1.o
exes += exe2
exe2.obj := exe2.o
### Define executables end.
all : ${exes:%=${BUILD_DIR}/%}
.SECONDEXPANSION:
${BUILD_DIR}:
mkdir -p $#
# Rule to link all exes.
${exes:%=${BUILD_DIR}/%} : ${BUILD_DIR}/% : $$(addprefix ${BUILD_DIR}/,$${$$*.obj}) | $${#D}
${CXX} -o $# ${ldflags} $^ ${ldlibs}
# Rule to compile C sources. And generate header dependencies.
${BUILD_DIR}/%.o : %.cc | $${#D}
${CXX} -o $# -c ${cppflags} ${cxxflags} -MD -MP $<
# Include automatically generated header dependencies.
ifneq ($(MAKECMDGOALS),clean)
-include $(foreach exe,${exes},$(patsubst %.o,${BUILD_DIR}/%.d,${${exe}.obj}))
endif
clean:
rm -rf $(BUILD_DIR)
.PHONY: all clean
Usage example:
$ cat exe1.cc
#include <iostream>
int main() { std::cout << "Hello, world!\n"; }
$ cat exe2.cc
#include <iostream>
int main() { std::cout << "Hello, world!\n"; }
$ make
mkdir -p debug
g++ -o debug/exe1.o -c -MD -MP exe1.cc
g++ -o debug/exe1 debug/exe1.o
g++ -o debug/exe2.o -c -MD -MP exe2.cc
g++ -o debug/exe2 debug/exe2.o
$ make BUILD=release
mkdir -p release
g++ -o release/exe1.o -c -DNDEBUG -O3 -MD -MP exe1.cc
g++ -o release/exe1 release/exe1.o
g++ -o release/exe2.o -c -DNDEBUG -O3 -MD -MP exe2.cc
g++ -o release/exe2 release/exe2.o
$ make clean
rm -rf debug
$ make BUILD=release clean
rm -rf release

How to do makefile to compile multiple .cpp files in different directories using a single makefile?

I have the following files in my proj2 directories and need to compile them together to have one executable file.
proj2/main.cpp
proj2/model/Player.cpp
proj2/model/gameBoard.cpp
proj2/controller/TTTController.cpp
proj2/Makefile
I'm using the following command inside my makefile, but it is not working.
all:
g++ /project2_p1/main.cpp /project2_p1/controller/TTTController.cpp /model/gameBoard.cpp /model/Player.cpp -o ttt
clean:
-rm ttt
Can anybody help me please.Thank you
I strongly recommend you start learning make as it is one of the fundamental tools that programmers use. And, if you can learn C++, you can definitely learn make.
In your project you have source files buried in their own subdirectories so in order to find them all you can use the $(shell find...) command. Same with any header files in your project.
By making all: the direct target it gets executed unconditionally and you lose the benefits of using make - only compile when you change something.
Having said that the basic template I am providing here could be improved to recompile only those source files that have changed but that's an exercise for the reader.
I think this should work in your case:
# set non-optional compiler flags here
CXXFLAGS += -std=c++11 -Wall -Wextra -pedantic-errors
# set non-optional preprocessor flags here
# eg. project specific include directories
CPPFLAGS +=
# find cpp files in subdirectories
SOURCES := $(shell find . -name '*.cpp')
# find headers
HEADERS := $(shell find . -name '*.h')
OUTPUT := ttt
# Everything depends on the output
all: $(OUTPUT)
# The output depends on sources and headers
$(OUTPUT): $(SOURCES) $(HEADERS)
$(CXX) $(CXXFLAGS) $(CPPFLAGS) -o $(OUTPUT) $(SOURCES)
clean:
$(RM) $(OUTPUT)
thats my minGW project's makefile codes:
hepsi: derle calistir
Nesneler := ./lib/Hata.o ./lib/Hatalar.o ./lib/Dugum.o ./lib/ListeGezici.o ./lib/BagilListe.o
derle:
g++ -I ./include/ -o ./lib/Hata.o -c ./src/Hata.cpp
g++ -I ./include/ -o ./lib/Hatalar.o -c ./src/Hatalar.cpp
g++ -I ./include/ -o ./lib/Dugum.o -c ./src/Dugum.cpp
g++ -I ./include/ -o ./lib/ListeGezici.o -c ./src/ListeGezici.cpp
g++ -I ./include/ -o ./lib/BagilListe.o -c ./src/BagilListe.cpp
g++ -I ./include/ -o ./bin/test $(Nesneler) ./src/test.cpp
calistir:
./bin/test
In your project I think this will work;
all: compile run
Objects := ./lib/Player.o ./lib/gameBoard.o ./lib/TTTController.o
compile:
g++ -I ./include/ -o ./lib/Player.o -c ./model/Player.cpp
g++ -I ./include/ -o ./lib/gameBoard.o -c ./model/gameBoard.cpp
g++ -I ./include/ -o ./lib/TTTController.o -c .controller/TTTController.cpp
g++ -I ./include/ -o ./bin/main $(Objects) ./main.cpp
run:
./bin/main
lib folder contains .o files. You can chance it if you want.
include folder refers your header .h or .hpp files. You can change every one of them according to your headers location.
bin folder contains your .exe file called main.exe. You can change or remove it like that
run:
./main
I hope it'll work.
#Galik has right. if you want to learn C++, you should definitely learn make.

Can't run program after putting the compiled file to the folder

In my Makefile I'm trying to specify, that I want to save the compiled executable file to /dvoram64/ folder. Before, when I tried to save it diretly to / everything worked all right, but now I get this message after calling make run:
ubuntu#pa2:~/Project$ make run
Makefile:37: warning: overriding commands for target `dvoram64/main'
Makefile:34: warning: ignoring old commands for target `dvoram64/main'
make: Circular dvoram64/main <- dvoram64/main dependency dropped.
mkdir -p dvoram64
g++ -Wall -pedantic -Wno-long-long -O0 -ggdb -g -c src/main.cpp -o dvoram64/main
./dvoram64/main
make: execvp: ./dvoram64/main: Permission denied
make: *** [run] Error 127
My Makefile looks like this:
#macros
CC=g++
CCFLAGS=-Wall -pedantic -Wno-long-long -O0 -ggdb -g
LBFLAGS=-lncurses -pthread
Remove=rm -rf
Objects=dvoram64/main
Doxygen=Doxyfile
RUN=./dvoram64/main
CPATH=objects/
#generates final binary and documentation
all: $(Objects) $(Doxygen)
make compile
make doc
#build into final binary
compile: $(RUN)
#run program
run: $(RUN)
$(RUN)
clean:
$(Remove) doc/
$(Remove) dvoram64
#generate documentation in '<login>/doc' folder
doc: $(Doxygen) src/*
( cd src | doxygen $(Doxygen))
#rules to make objects ----------------------------------------------------------------------------------
$(RUN): $(Objects)
$(CC) $(CCFLAGS) $(Objects) -o $(RUN) $(LBFLAGS)
dvoram64/main: src/main.cpp
mkdir -p dvoram64
$(CC) $(CCFLAGS) -c src/main.cpp -o dvoram64/main
Could anybody tell me, what causes that and how to fix it?
You have several problems. First:
RUN=./dvoram64/main
$(RUN): $(Objects)
$(CC) $(CCFLAGS) $(Objects) -o $(RUN) $(LBFLAGS)
dvoram64/main: src/main.cpp
mkdir -p dvoram64
$(CC) $(CCFLAGS) -c src/main.cpp -o dvoram64/main
You have two rules for the same target(dvoram64/main). Which do you want to use? Why do you have two? Remove one of them.
Next:
Objects=dvoram64/main
RUN=./dvoram64/main
$(RUN): $(Objects)
...
You have two variables for (essentially) the same thing, which is untidy but not illegal. But You make one the prerequisite of the other. A thing cannot be it's own prerequisite, and Make must correct the error for you. You should rethink this.
Next:
RUN=./dvoram64/main
run: $(RUN)
$(RUN)
dvoram64/main: src/main.cpp
mkdir -p dvoram64
$(CC) $(CCFLAGS) -c src/main.cpp -o dvoram64/main
You have lost track of whether dvoram64/main should be an object file or an executable file. You have two rules to build it; one builds an executable, the other an object. You name it main, which sounds like an executable file, but you put that name in a variable called Objects. And in the end you build an object file and try to execute it.
You can save yourself some trouble by keeping the makefile as free of redundancy as possible. And when the big target fails, try the little targets one at a time to narrow nown the scope of the problem.

Why does this makefile execute a target on 'make clean'

This is my current makefile.
CXX = g++
CXXFLAGS = -Wall -O3
LDFLAGS =
TARGET = testcpp
SRCS = main.cpp object.cpp foo.cpp
OBJS = $(SRCS:.cpp=.o)
DEPS = $(SRCS:.cpp=.d)
.PHONY: clean all
all: $(TARGET)
$(TARGET): $(OBJS)
$(CXX) $(CXXFLAGS) $(LDFLAGS) $(OBJS) -o $(TARGET)
.cpp.o:
$(CXX) $(CXXFLAGS) -c $< -o $#
%.d: %.cpp
$(CXX) -M $(CXXFLAGS) $< > $#
clean:
rm -f $(OBJS) $(DEPS) $(TARGET)
-include $(DEPS)
It works perfectly with one exception. If the directory is already clean (no *.d, *.o) and I run 'make clean', it re-creates the dependencies, then immediately deletes them:
[user#server proj]$ make
g++ -M -Wall -O3 foo.cpp > foo.d
g++ -M -Wall -O3 object.cpp > object.d
g++ -M -Wall -O3 main.cpp > main.d
g++ -Wall -O3 -c main.cpp -o main.o
g++ -Wall -O3 -c object.cpp -o object.o
g++ -Wall -O3 -c foo.cpp -o foo.o
g++ -Wall -O3 main.o object.o foo.o -o testcpp
[user#server proj]$ make clean
rm -f main.o object.o foo.o main.d object.d foo.d testcpp
[user#server proj]$ make clean
g++ -M -Wall -O3 foo.cpp > foo.d
g++ -M -Wall -O3 object.cpp > object.d
g++ -M -Wall -O3 main.cpp > main.d
rm -f main.o object.o foo.o main.d object.d foo.d testcpp
[user#server proj]$
I don't understand why the second 'make clean' would re-generate the dependency files. How can I avoid this? This isn't a big deal for this contrived example, but for a large project, it can be quite time-consuming.
Thanks.
It's because the .d files are being -included unconditionally. As far as make knows, they could add dependencies or commands to the clean target. All included files are built first for this reason, otherwise you might get an incorrect or failed build. To disable this, you want to conditionally include the dependency files:
ifneq ($(MAKECMDGOALS),clean)
-include $(DEPS)
endif
An alternative solution is to generate the dependency files using touch and have them replaced by actual data as a side-effect of compilation. This is how automake does its dependency tracking, as it makes one-time builds faster. Look into the -MD and -MMD options to gcc if you want to go this route. Use a pattern rule like:
%.d:
#touch $#
To initially create the dependency files.
If you want to skip the include for multiple targets, you can use the filter function.
MAKEFILE_TARGETS_WITHOUT_INCLUDE := clean distclean doc
# Include only if the goal needs it
ifeq ($(filter $(MAKECMDGOALS),$(MAKEFILE_TARGETS_WITHOUT_INCLUDE)),)
-include $(DEPS)
endif
It wants to regenerate the dependency files because it always tries to regenerate all of the makefiles, including -include'd makefiles, before doing anything else. (Well, actually, for me it doesn't do that - I have GNU Make 3.81 - so maybe it's a bug in your version that was fixed, or an optimization that mine has and yours doesn't. But anyway.)
The easiest way around this is to write your rules so they generate the .d files as a side effect of regular compilation, rather than giving explicit rules to generate them. That way, when they're not there, Make doesn't know how to generate them so it doesn't try (in a clean tree, the .cpp.o rules are enough, you don't need the header file dependencies). Look at an Automake-generated makefile -- a simple one -- to see how it's done.
The leading - in -include means that make won't complain if the dependencies are missing and can't be remade, but it doesn't mean it won't try to make them first (and, in this case, succeed) -- after all, anything interesting or important could be in the included files, whence the let's-try-making them attempt. I don't think there's a way to stop that.
For docs on include and -include, see here.