Understanding the Dependencies of a Makefile (C++) - c++

While working on a C++ project, I noticed that I was making changes to one of the header files linked in my main code, but the make utility was not registering it. I had to force it to compile differently given the changes using "make - B".
I want to know why this is the case; is it because of how my makefile is written, how my files depend on each other, both, or neither?
Here is my makefile:
CXXFLAGS = -Wall -Werror -std=c++11 -pedantic -Wvla -g
//CXXFLAGS = -std=c++11
all: main.o game.o zombie.o
g++ $(FLAGS) game.o main.o zombie.o -o main $(CXXFLAGS)
game: game.cpp
g++ $(FLAGS) -c game.cpp $(CXXFLAGS)
zombie: zombie.cpp
g++ $(FLAGS) -c zombie.cpp $(CXXFLAGS)
main: main.cpp
g++ $(FLAGS) -c main.cpp pairing_heap.h $(CXXFLAGS)
I made a change to pairing_heap.h which is #included in my main file.
Why did make not notice that it should compile again? Because I feel like this is a conceptual misunderstanding, I felt that it was not necessary to include the changes I made or the output difference when I did "make - B". They were simple things like cout's and cerr's included in the new pairing_heap.h that were not being picked up until forced.
Let me know if I need to provide any more information.
Thank you.

You are listing pairing_heap.h in the recipe for main, which does not make it a dependency of main (besides, you should never pass headers to the compiler like this), for that you need to write the rule as follows:
main: main.cpp pairing_heap.h
g++ $(FLAGS) -c main.cpp $(CXXFLAGS)
There are a number of other things that are incorrect in your file, such as the fact that your targets are not actual files (main: should be main.o: etc.), and you aren't making use of automatic variables or pattern rules, but it's probably easier to just replace everything with the following
CPPFLAGS := -MMD -MP
CXXFLAGS := -Wall -Werror -std=c++11 -pedantic -Wvla -g
SOURCES := $(wildcard *.cpp)
main: $(SOURCES:.cpp=.o)
-include $(SOURCES:.cpp=.d)
which leverages make's implicit rules and gcc's/clang's auto dependency generation to make an executable called main.

Make doesn't really know anything about any particular programming language or tool, so it doesn't understand that C/C++ files depend on headers as well as source files. It just has rules of the form
target: dependencies
actions
All it knows from this is that the file target depends on the files listed in dependencies, and if any of those files are newer, the commands in actions should be run to update target. That's really it -- everything that make does comes from this simple idea of target files, dependencies, and actions.
Now there is more to it -- make has a bunch of built-in rules for common things you often want to do, as well as ways to specify 'pattern' rules -- rules where the target contains a wildcard, so can be used for many different targets that depend on other files with related names.
Now in your case, you have the rule:
all: main.o game.o zombie.o
g++ $(FLAGS) game.o main.o zombie.o -o main $(CXXFLAGS)
which says to remake the file all, if its older than the files main.o, game.o or zombie.o it should run the command. That's the first rule in the file, so it is what gets built by default when you type make.
Now, you probably don't have a file called all (if you did make probably wouldn't do anything), but that's generally fine, as if it doesn't exist, its obviously not up to date, so the command needs to run.
When a command needs to run, it also checks any of the dependencies to see if they in turn have dependencies that are older (so need to be rebuilt). Since these files all end in .o, they match a built-in rule that knows how to build them from a file with the same name, except ending with .cpp, so its runs those actions if (and only if) the .cpp file is newer.
Now, you say, "what about my other rules -- what do they do?" Well it turns out they don't do anything. They are rules to build files named game, zombie, and main, and since you never ask to build those files and nothing else depends on them, they do nothing. You might as well delete them.
You also ask "How do I make it rebuild if the header file changes?" Well, if you add a rule with no actions (just target and dependencies) it will just add those dependencies to another rule (built-in in this case) that does have an action. So if you add a line like:
main.o: pairing_heap.h
(with no action), make will add this dependency to the built-in rule that know how to build main.o from main.cpp and will run that rule (recompiling main.cpp) if either main.cpp or pairing_hep.h is newer than main.o -- which is exactly what you want.

Related

Understanding Makefile. make cannot link armadillo library

I am new to C++ and I am having trouble understanding how Makefiles do their thing with the g++ compiler.
I have successfully installed armadillo library (via apt) and have a very simple c++ program test.cpp, like the one below:
#include <iostream>
#include <armadillo>
using namespace std;
int main()
{
arma::mat A;
A << -1 << 2 << arma::endr
<< 3 << 5;
cout << A << endl;
arma::fmat B;
B.randu(4,5);
cout << B;
return 0;
}
This works just fine if I compile manually like this:
g++ src/test.cpp -std=c++11 -Wall -o test -DARMA_DONT_USE_WRAPPER -lopenblas -llapack
I can manually run the program and it delivers the matrices as expected.
On the other hand, I have the Makefile template from the VSCode C/C++ Extension, which I have modifed slightly for including the LAPACK an BLAS Fortran libraries:
########################################################################
####################### Makefile Template ##############################
########################################################################
# Compiler settings - Can be customized.
CC = g++
CXXFLAGS = -std=c++11 -Wall
LDFLAGS = -DARMA_DONT_USE_WRAPPER -lopenblas -llapack
# Makefile settings - Can be customized.
APPNAME = test
EXT = .cpp
SRCDIR = src
OBJDIR = obj
############## Do not change anything from here downwards! #############
SRC = $(wildcard $(SRCDIR)/*$(EXT))
OBJ = $(SRC:$(SRCDIR)/%$(EXT)=$(OBJDIR)/%.o)
DEP = $(OBJ:$(OBJDIR)/%.o=%.d)
# UNIX-based OS variables & settings
RM = rm
DELOBJ = $(OBJ)
# Windows OS variables & settings
DEL = del
EXE = .exe
WDELOBJ = $(SRC:$(SRCDIR)/%$(EXT)=$(OBJDIR)\\%.o)
########################################################################
####################### Targets beginning here #########################
########################################################################
all: $(APPNAME)
# Builds the app
$(APPNAME): $(OBJ)
$(CC) $(CXXFLAGS) -o $# $^ $(LDFLAGS)
# Creates the dependecy rules
%.d: $(SRCDIR)/%$(EXT)
#$(CPP) $(CFLAGS) $< -MM -MT $(#:%.d=$(OBJDIR)/%.o) >$#
# Includes all .h files
-include $(DEP)
# Building rule for .o files and its .c/.cpp in combination with all .h
$(OBJDIR)/%.o: $(SRCDIR)/%$(EXT)
$(CC) $(CXXFLAGS) -o $# -c $<
################### Cleaning rules for Unix-based OS ###################
# Cleans complete project
.PHONY: clean
clean:
$(RM) $(DELOBJ) $(DEP) $(APPNAME)
# Cleans only all files with the extension .d
.PHONY: cleandep
cleandep:
$(RM) $(DEP)
#################### Cleaning rules for Windows OS #####################
# Cleans complete project
.PHONY: cleanw
cleanw:
$(DEL) $(WDELOBJ) $(DEP) $(APPNAME)$(EXE)
# Cleans only all files with the extension .d
.PHONY: cleandepw
cleandepw:
$(DEL) $(DEP)
I have passed the needed libraries under LDFLAGS = -DARMA_DONT_USE_WRAPPER -lopenblas -llapack. Nevertheless, this solution does not work. It looks to me like the compiler is unable to find the armadillo library, so I must have linked it somehow wrongly. It delivers:
g++ -std=c++11 -Wall -o test obj/test.o -DARMA_DONT_USE_WRAPPER -lopenblas -llapack
/usr/bin/ld: obj/test.o: in function `TLS wrapper function for arma::arma_rng_cxx11_instance':
test.cpp:(.text._ZTWN4arma23arma_rng_cxx11_instanceE[_ZTWN4arma23arma_rng_cxx11_instanceE]+0x25): undefined reference to `arma::arma_rng_cxx11_instance'
collect2: error: ld returned 1 exit status
make: *** [Makefile:36: test] Error 1
So, aside from the obvious question (Why does this not work?), I would as well appreciate if someone could help me clarify as well the following aspects:
On the one hand, rom the message error it seems that the command run g++ -std=c++11 -Wall -o test obj/test.o -DARMA_DONT_USE_WRAPPER -lopenblas -llapack does not include the name of the cpp file I wrote (as opposed to in my manual compilation, in which it works). Nevertheless, if I do not use armadillo, the Makefile recipe above works just fine. I see the Makefile somehow looking for all cpp files in the source code folder SRC = $(wildcard $(SRCDIR)/*$(EXT)), but I cannot see where is this forwarded to the compiler. Can someone help me with that?
The other thing is that, in my manual compilation, it seems to make no difference to pass the LAPACK and BLAS libraries as CXXFLAGS or LDFLAGS, meaning both of the following commands:
g++ src/test.cpp -std=c++11 -Wall -DARMA_DONT_USE_WRAPPER -lopenblas -llapack -o test
and
g++ src/test.cpp -std=c++11 -Wall -o test -DARMA_DONT_USE_WRAPPER -lopenblas -llapack
work just fine. As far as I have been able to read, I understood the flags before -o are meant for the compiler, and those after are meant for the "linker" (whatever that is). Can someone explain me what are the main differences between the CXXFLAGS and LDFLAGS? Why both combinations work? And what is the linker?
Thank you very much for your help.
Best,
D.
The other answer is a good general introduction to compilation but if you want to know what is happening in your situation you need to first understand that answer and the difference between source files, object files, and executable files and the way that they work, then go deeper to figure out what's wrong.
As far as I have been able to read, I understood the flags before -o are meant for the compiler, and those after are meant for the "linker" (whatever that is)
No, that is not right.
Turning source files into an executable involves several steps each managed by a different tool. The compiler front-end (e.g., g++) manages the order of these. Each of these may use different options, and whenever the compiler front-end invokes one of these tools it will pass the appropriate flags from the command line for that tool. It's not the case that "only" flags before or after -o are passed to different tools; it doesn't matter where on the command line they live.
The tools involved with compilation, in the order in which they're invoked, are:
Preprocessor: this handles #include and #ifdef and #define, etc. (the lines that start with # in your source). The preprocessor takes the options -D, -I, and some others.
Compiler: this turns your source code (after preprocessing to handle all the included files etc.) into assembly code which is very low-level: basically machine code but in ASCII form. This does the bulk of the work including optimization etc. Flags like -O2, -g, and many others are used by this tool.
Assembler: this turns the assembly code into a binary format for your CPU and generates an object file (foo.o).
Linker: this takes one or more object files plus libraries and turns them into an executable. This tool uses options like -L and -l to find libraries.
There's a separate tool, the archiver (ar) which is not invoked by the compiler front-end, which is used to turn object files (foo.o) into static libraries (libfoo.a).
Note, the above is a "classical" view of building: newer compilers munge the above steps together sometimes to get either better error messages or better optimization or both.
Most of the time the first three steps are all done by a single invocation of the compiler front-end: it turns a source file into an object file. You do this once for each source file. Then at the end, another invocation of the compiler front-end takes those object files and builds an executable.
If you look at the output make prints you'll see these two steps. First you'll see the compilation step, which is controlled by this make rule:
$(OBJDIR)/%.o: $(SRCDIR)/%$(EXT)
$(CC) $(CXXFLAGS) -o $# -c $<
and runs this command:
g++ -std=c++11 -Wall -o obj/test.o -c src/test.cpp
The -c option here tells the compiler, "do all the steps up to and including the compile step, then stop and don't do the link step".
Then you will see your link command, which is controlled by this make rule:
$(APPNAME): $(OBJ)
$(CC) $(CXXFLAGS) -o $# $^ $(LDFLAGS)
and runs this command:
g++ -std=c++11 -Wall -o test obj/test.o -DARMA_DONT_USE_WRAPPER -lopenblas -llapack
What do you notice about this? The -DARMA_DONT_USE_WRAPPER is a preprocessor option, but you're passing it to the link step and not passing it to the compile step. That means when the source is compiled, that option is not present and so whatever operation it was intended to suppress (using a wrapper apparently) is not being suppressed.
You need to put preprocessor options in a make variable that is sent to the compiler / preprocessor, so it should be this:
CXXFLAGS = -std=c++11 -Wall -DARMA_DONT_USE_WRAPPER
LDFLAGS = -lopenblas -llapack
Be sure to run clean before trying to build again.
One minor thing, but generally you should use CXX for your C++ compiler and CC for your C compiler (these are the usual conventions). If you do end up trying to compile C++ source with a C compiler you are likely to have problems. Less so the other way round.
So what it happening? Roughly speaking, you have two steps:
Compilation
Linking
When you compile a small exe, you can combine these into a single steps. Makefiles generally don't as two steps is more general.
For compilation the input has a .cpp suffix and you are passing the -c flag to tell the compiler to just compile. This will result in an object file (.o suffix).
For linking, there is no -c. The inputs are object files and the output is your application.
Other suffixes are possible (.cxx, .CC etc.).
There are 4 commonly used make variables
CPPFLAGS for preprocessor flags, can be used for C and C++ compilation
CFLAGS for flags specific to C compilation
CXXFLAGS for flags specific to C++ compilation
LDFLAGS for flags specific to linking
Historically, ld was the linker (and hence LDFLAGS), but it isn't smart enough to handle C++ linking well on its own. So now it is usually the C++ compiler that performs the task of "linker driver", that is g++ controls the linking that ld does.
Finally, your specific problem. You should add the armadillo library to LDFLAGS. The best way to do that is to just add -larmadillo. If armadillo is not installed in a 'standard' location like /usr/lib then you may need to additional arguments such as
-L/path//to/armadillo_lib -Wl,-rpath,/path//to/armadillo_lib
(the first one tells the linker where the library is, the second one puts that path into the executable so that is also knows where the library is).

How to avoid forgetting dependencies in make/CMake?

I am new to C++ and am trying to get the hang of build systems like make/CMake. Coming from Go, it seems that there is a constant risk that if you forget to do a little thing, your binaries will become stale. In particular, I can't find a best practice for remembering to keep dependencies/prerequisites updated in make/CMake. I'm hoping I am missing something obvious.
For example, suppose I have a basic makefile that just compiles main.cpp:
CFLAGS = -stdlib=libc++ -std=c++17
main: main.o
clang++ $(CFLAGS) main.o -o main
main.o: main.cpp
clang++ $(CFLAGS) -c main.cpp -o main.o
main.cpp:
#include <iostream>
int main() {
std::cout << "Hello, world\n";
}
So far so good; make works as expected. But suppose I have some other header-only library called cow.cpp:
#include <iostream>
namespace cow {
void moo() {
std::cout << "Moo!\n";
}
}
And I decide to call moo() from within main.cpp via `include "cow.cpp":
#include <iostream>
#include "cow.cpp"
int main() {
std::cout << "Hello, world\n";
cow::moo();
}
However, I forget to update the dependencies for main.o in makefile. This mistake is not revealed during the obvious testing period of running make and rerunning the binary ./main, because the whole cow.cpp library is directly included in main.cpp. So everything seems fine, and Moo! is printed out as expected.
But when I change cow.cpp to print Bark! instead of Moo!, then running make doesn't do anything and now my ./main binary is out of date, and Moo! is still printed from ./main.
I'm very curious to hear how experienced C++ devs avoid this problem with much more complicated codebases. Perhaps if you force yourself to split every file into a header and an implementation file, you'll at least be able to quickly correct all such errors? This doesn't seem bulletproof either; since header files sometimes contain some inline implementations.
My example uses make instead of CMake, but it looks like CMake has the same dependency listing problem in target_link_libraries (though transitivity helps a bit).
As a related question: it seems like the obvious solution is for the build system to just look at the source files and infer dependencies (it can just go one level in and rely on CMake to handle transitivity). Is there a reason this doesn't work? Is there a build system that actually does this, or should I write my own?
Thanks!
First of all you will need to reference the dependencies file in your Makefile.
This can be done with the function
SOURCES := $(wildcard *.cpp)
DEPENDS := $(patsubst %.cpp,%.d,$(SOURCES))
wich will take the name of all *.cpp files and substitute and append the extension *.d to name your dependency.
Then in your code
-include $(DEPENDS)
- tells the Makefile to not complain if the files do not exist. If they exist they will be included and recompile your sources properly according to the dependencies.
Finally the dependencies can be created automatically with the options: -MMD -MP for the rules to create the objects file. Here you can find a complete explanation. What generates the dependencies is MMD; MP is to avoid some errors. If you want to recompile when system libraries are updated use MD instead of MMD.
In your case you can try:
main.o: main.cpp
clang++ $(CFLAGS) -MMD -MP -c main.cpp -o main.o
If you have more files it is better to have a single rule to create object files. Something like:
%.o: %.cpp Makefile
clang++ $(CFLAGS) -MMD -MP -c $< -o $#
You can take a look also at this 2 great answers:
one
two
In your case a more suitable Makefile should look like the following (there might be some errors but let me know):
CXX = clang++
CXXFLAGS = -stdlib=libc++ -std=c++17
WARNING := -Wall -Wextra
PROJDIR := .
SOURCEDIR := $(PROJDIR)/
SOURCES := $(wildcard $(SOURCEDIR)/*.cpp)
OBJDIR := $(PROJDIR)/
OBJECTS := $(patsubst $(SOURCEDIR)/%.cpp,$(OBJDIR)/%.o,$(SOURCES))
DEPENDS := $(patsubst $(SOURCEDIR)/%.cpp,$(OBJDIR)/%.d,$(SOURCES))
# .PHONY means these rules get executed even if
# files of those names exist.
.PHONY: all clean
all: main
clean:
$(RM) $(OBJECTS) $(DEPENDS) main
# Linking the executable from the object files
main: $(OBJECTS)
$(CXX) $(WARNING) $(CXXFLAGS) $^ -o $#
#include your dependencies
-include $(DEPENDS)
#create OBJDIR if not existin (you should not need this)
$(OBJDIR):
mkdir -p $(OBJDIR)
$(OBJDIR)/%.o: $(SOURCEDIR)/%.cpp Makefile | $(OBJDIR)
$(CXX) $(WARNING) $(CXXFLAGS) -MMD -MP -c $< -o $#
EDIT to answer comments
As another question, is there any problem with rewriting the DEPENDS definition as just DEPENDS := $(wildcard $(OBJDIR)/*.d)?
Nice question, it took me a while to see your point
From here
$(wildcard pattern…) This string, used anywhere in a makefile, is
replaced by a space-separated list of names of existing files that
match one of the given file name patterns. If no existing file name
matches a pattern, then that pattern is omitted from the output of the
wildcard function.
So wildcard return a list of the file names matching the pattern. patsubst acts on strings, it does not care about what are those strings: it is used as a way to create the file names of the dependencies, not the files themselves. In the Makefile example that I posted DEPENDS is actually use in two cases: when cleaning with make clean and with include so in this case they both work because you are not using DEPENDS in any rule. There are some differences (I tried to run and you should too to confirm). With DEPENDS := $(patsubst $(SOURCEDIR)/%.cpp,$(OBJDIR)/%.d,$(SOURCES)) if you run make clean dependencies *.d that do not have a correspondent *.cpp file will not be removed while they will with your change. On the contrary you might include dependencies not relevant to your *.cpp file.
I asked this questions: let's see the answers.
If the .d files get deleted from carelessness but the .o files remain, then we are in trouble. In the original example, if main.d is deleted and then cow.cpp is subsequently changed, make won't realize it needs to recompile main.o and thus it will never recreate the dependency file. Is there a way to cheaply create the .d files without recompiling the object files? If so then we could probably recreate all the /.d files on every make command?
Nice question again.
Yes, you are right. Actually it was an error of mine. This happens because of the rule
main: main.o
$(CXX) $(WARNING) $(CFLAGS) main.o -o main
actually should have been:
main: $(OBJECTS)
$(CXX) $(WARNING) $(CXXFLAGS) $^ -o $#
so that it got relinked (the executable is updated) whenever one of the objects change and they will change whenever one their cpp file change.
One problem remains: if you delete your dependencies but not the objects, and change only one or more header files (but not the sources) then your program is not updated.
I corrected also the previous part of the answer.
EDIT 2
To create the dependencies you can also add a new rule to your Makefile:
here is an example.

Changed .h file in C++ does not need to be compiled again?

I have the following question. After a successful compilation, if I compile it again after I only change some content in one of the .h files, the computer says:
make: Nothing to be done for `all'.
Can I force the compiler to compile again even if I have only modified the .h files?
If you want your output to be updated when header files change, then you have to add it to your dependency statement:
myprogram: myprogram.cpp myprogam.h
c++ -o myprogram myprogram.cpp
Typically we don't do it this way because the code that does things stays in the cpp file. If you are on unix and want to force a rebuild, just touch a cpp file to update its timestamp (forcing a rebuild) with "touch myprogram.cpp", for example, or delete your existing executable.
If you are using make without a Makefile, letting it infer dependencies, it will probably not infer the header dependencies. In this case, either blow away your executable or touch your source file.
Sounds like your Makefile does not have dependencies configured correctly. That is what you should look into fixing.
If you really want to just force a rebuild rather than fix the underlying problem. Then you can do a make clean before your make all or, if the Makefile does not have a "clean" target, delete all the generated object files and libs/executables and then run make all again.
You can force make to rebuild everything using the --always-make command line option.
However, it sounds like you don't have your dependencies setup properly in your Makefile. If your code (.cpp files) actually include headers, then generally your target for compiling them should have a prerequisite on the header files that it includes.
There is a simpler way than the accepted answer. Simply add -MD to your compiler flags in your Makefile, and -include myfile.d at the end of the Makefile (listing all source files with a *.d extension instead). This will, respectively, generate and reference additional *.d dependency files in your build folder (wherever your *.o files go) when you make, so you do not need to explicitly add every single header file to your makefile dependencies.
This is useful for projects with a long list of header files. Furthermore, this way, you know that you can't forget to include a header file in your Makefile dependencies, preventing troubleshooting time lost later when you think your binary updated when you changed a header file, but it actually didn't because you forgot to put it in the Makefile.
For example, use gcc -MD -I. -c myfile.cpp -o obj/myfile.o, and you can keep your Makefile dependencies as just foo: myfile.cpp without myfile.h.
A shortcut way to do this so you only need to list all files once is something like the following:
# Beginning of Makefile etc. etc.
# Only need to list all files once, right here.
SRCS = myfile.cpp myfile2.cpp
OBJS = $(SRCS:%.cpp=%.o)
# put .o and .d files in ./obj/
# (Assumes 'obj' directory exists)
FULLOBJS = $(addprefix obj/,$(OBJS))
# rule to make object (*.o) files
$(FULLOBJS): obj/%.o:%.cpp
gcc -MD -I. -c %< -o $#
# rule to make binary
foo: $(FULLOBJS)
g++ -o $# $(FULLOBJS)
# rule to clean (Note that it also deletes *.d files)
.PHONY: clean
clean:
rm -rf obj/*.o obj/*.d foo
# include dependency files (*.d) if available
-include $(FULLOBJS:%.o=%.d)
Can I force the compiler to compile again even if I have only modified
the .h files?
Yes ... but you probably want to improve your make (tool).
What I do is to force the most recent compile of the file in question, where the command generated by make shows.
Example:
# ... noise
g++ -O3 -ggdb -std=c++14 -Wall -Wextra -Wshadow -Wnon-virtual-dtor -pedantic -Wcast-align -Wcast-qual -Wconversion -Wpointer-arith -Wunused -Woverloaded-virtual -O0 lmbm101_11.cc -o lmbm101_11 -L../../bag -lbag_i686 -lnet_i686 -lposix_i686 -lzlib_i686 -lrt -pthread
# ... more noise.
To force a build, I highlight the "command" make created (starts with "g++", and resides between noise and more noise), and invoke it instead of make.
This is trivial using emacs on Linux. Might not be so easy on other systems.
You might consider copying this command into the file, for future use.
( i.e. I bypass make until I choose to fix my make file. )

how to compile multiple file under make

I have several .cpp and .hpp files. I want compile and lnk them over using make. How can I do that ?
sample.cpp sample.hpp
sample_2.cpp sample_2.hpp
sample_3.cpp sample_3.hpp
...
and
main.cpp
I have done :
default:
g++ -c sample sample.cpp
g++ -c sample_2 sample_2.cpp
g++ -c sample_3 sample_3.cpp
g++ -o main main.cpp sample sample_2 sample_3
When I type make on terminal, it gives error :
/usr/bin/ld: /usr/lib/debug/usr/lib/x86_64-linux-gnu/crt1.o(.debug_info): relocation 0 has invalid symbol index 10
Instead of having a single default target that does everything each separate output should be a make target. That allows make to work properly, building one thing at a time, in the correct order (or in parallel if the dependency graph allows it) and reporting errors properly at each step. Also, very importantly, by telling make about each separate target and what they depend on you ensure that when a dependency changes make will know exactly what targets need to be rebuilt because of the changed dependency. That is Make's best feature.
In your case the final target is main so you want the default target to build that, which you do by saying main is a prerequisite of main:
default: main
Now you need to say how to build main, it depends on the various object files, so we tell make that:
main: main.o sample.o sample_2.o sample_3.o
This says it depends on those object files (they are its prerequsites) and so they'll be built first. Each of those objects is another make target that will be built separately.
When all the prerequisites have been built a recipe will be used to link them into main, so we need to add a recipe to the above target:
main: main.o sample.o sample_2.o sample_3.o
g++ main.o sample.o sample_2.o sample_3.o -o main
Make has lots of abbreviations to simplify things, e.g. $(CXX) is the C++ compiler, $# means the current target and $^ means the current target's prerequisites, so you can simplify that rule to:
main: main.o sample.o sample_2.o sample_3.o
$(CXX) $^ -o $#
That's actually all you need, Make already knows how to build the .o prerequisites using its builtin rules, so it will see a file called main.cpp and know it can compile that to create main.o, and see sample.cpp and compile that to sample.o etc. It will create a dependency graph from the makefile to decide what targets are needed in order to build default (which means it decides it needs main and that needs main.o, sample.o etc. and they need main.cpp and sample.cpp etc. which already exist, so it can start building prerequisites until it has everything needed to link main then it can do that and finish.)
Now if you alter sample_2.cpp and run make again it will see that sample_2.o is out of date and needs to be recompiled, but the other .o files are still OK (they are newer then the .cpp files they depend on), so it will recompile sample_2.o and relink main and not rebuild everything else.
In fact you could simplify it even further, by using the default recipe for linking objects into an executable:
LINK.o = $(CXX)
default: main
main: main.o sample.o sample_2.o sample_3.o
That's all you need! But it's often clearer, especially for a beginner, to go with the more verbose version, as it's easier to customise when you're not familiar with all of Make's automatic rules and variables.
It's also helpful to tell make about dependencies on header files, so that things get rebuild when headers change. That can be done automatically using the compiler to generate prerequisites, but for simple cases you can just add it to the makefile directly e.g.
sample.o: sample.hpp sample.cpp

Writing a makefile to build dynamic libraries

I have a makefile in my src directory.
The makefile should build the data structures, which are in DataStructures/, and then iterate over all cpp files in calculations/ and create a corresponding .so file in ../bin/calculations
I tried the following syntax:
DAST = DataStructures/
COMPS = computations/
BIN = ../bin/
OBJECTS = ${DAST}Atom.o ${DAST}Molecule.o
COMPILE = g++ -Wall -g -c -std=c++0x -I/usr/local/include/openbabel-2.0 LINK = g++ -Wall -g -std=c++0x ${OBJECTS} -lopenbabel -I/usr/local/include/openbabel-2.0
all: ${BIN}main ${DAST}Molecule.o ${DAST}Atom.o ${BIN}${COMPS}%.so
${BIN}main: ${OBJECTS} main.cpp
${LINK} main.cpp -o ${BIN}main
${DAST}Molecule.o: ${DAST}Molecule.h ${DAST}Molecule.cpp
${COMPILE} ${DAST}Molecule.cpp -o ${DAST}Molecule.o
${DAST}Atom.o: ${DAST}Atom.h ${DAST}Atom.cpp
${COMPILE} ${DAST}Atom.cpp -o ${DAST}Atom.o
${BIN}${COMPS}%.o: ${COMPS}%.cpp
gcc -Wall -fPIC -c -lopenbabel $< -I/usr/local/include/openbabel-2.0 -std=c++0x
${BIN}${COMPS}%.so: ${COMPS}%.o
gcc -shared -Wl,-soname,libcsmtest.so.1 -o libcsmtest.so $#
clean:
rm -rf ${OBJECTS}
.PHONY: all clean
But it obviously doesn't work, as I get the following output:
shai#ubuntu:~/csm/csm2/src$ make all
make: *** No rule to make target `../bin/computations/%.so', needed by 'all'. Stop.
thanks
You need to specify in the all: target, the prerequisites explicitly.
In Makefile parlance, % is a wildcard that can be used in automatic rules. However, the all: target is a simple target with no such wildcard, thus ${BIN}${COMPS}%.so is wrong in that context.
Please note that when I say 'wildcard' in this context, this wildcard matches the target against the prerequisites, not against the filesystem like * do in glob expressions.
Also, while your hart is in the right place, as a matter of style, your Makefile can be better:
Intermediary objects, should not be prerequisites of the all target, but only the final targets you wish to ship.
There is a mix of automatic and simple rules to specify the creation of objects.
Typically one doesn't write an automatic rule for %.so, because a library is often constructed from more than one object.
The dependencies between an object and header files is a complex issue. In short you need to specify that the resulting object depends on the *.cpp (or .c) as well as all the headers included (directly and indirectly) by the *.cpp file.
By convention, that is well supported by GNU make, instead of using ${COMPILE} as you do, one should use $(CXX) for your C++ compiler, and $(CXXFLAGS) for the standard flags you wish to pass to that compiler.
You need something like
SOBJECTS = ...
all: ${BIN}main ${SOBJECTS}
...
You need a way to gather all the *.so names in the variable SOBJECTS. You can do this manually, or use some of make's internal functions to scan the source directory.
Also notice that I removed the two *.o files as dependencies from the all target. They are not final goals of the build (I assume), so you don't need to mention them there.
Besides this there are other stylistic points which I would do differently, but at the moment they are not causing immediate problems, so I won't digress, but I advise you to have a look at some tutorials to see how things are done generally.
For starters, look at Paul's Rules of Makefiles, and How Not to Use VPATH.