If your program grows to a substantial size, or if it uses libraries that need to be built but should be kept separate, it is quite likely that you have split up your sources into several directories. One of the main motivations for writing makepp was to make dealing with several directories much easier than with the standard make utility. If you're familiar with the standard unix make, you'll notice that with makepp, you don't have to mess around with ugly complexities like recursive invocations of make.
With makepp, you simply put a separate makefile in each directory that builds the relevant files in that directory. When a makefile refers to files whose build commands are in different makefiles, makepp automatically finds the appropriate build rules in the other makefiles. All actions in each makefile are executed with the current directory set to be the directory containing the makefile, so each makefile can be written independently of all the others. No makefile has to know anything about the other makefiles; it does not even have to tell makepp to load the rules from those other makefiles.
When you've written your makefiles, cd to the directory that
contains your main program, and type makepp
just like
you usually would. Makepp will load in the makefile from
that directory. It will notice that this makefile refers to files
in other directories, and it will examine those other directories to
see if there is a makefile in them. In this way, all relevant
makefiles will be loaded.
As a simple example, suppose your top level directory contains the following makefile:
# Top level makefile: CXX := c++ CXXFLAGS := -O2 my_program: main.o goodies/libgoodies.so $(CXX) $(inputs) -o $(output) %.o: %.cxx $(CXX) $(CXXFLAGS) -c $(input) -o $(output)
You would need to write a makefile in the directory
goodies
which builds libgoodies.so
, like
this:
# goodies/Makefile CXX := c++ CXXFLAGS := -O2 MODULES = candy.o chips.o licorice.o cookies.o popcorn.o spinach.o libgoodies.so: $(MODULES) $(CXX) -shared $(inputs) -o $(output) # Note that the command is written assuming that # the current directory is the subdirectory # "goodies", not the top level subdirectory. # Makepp cds into this directory before executing # any commands from this makefile. %.o: %.cxx $(CXX) $(CXXFLAGS) -fpic -c $(input) -o $(output)
And that's all you need to do.
Any variables which you specify on the command line override the
definition of the variable in all makefiles. Thus, for
example, if you type makepp CXXFLAGS="-g"
, all
modules will be recompiled for debug because the definition of
CXXFLAGS
in both makefiles is overridden.
The directories containing other sources need not be subdirectories of the top-level directory (as they are in this example). They can be anywhere in the file system; makepp will automatically load a makefile from any directory that contains a file which is a dependency of some target it is trying to build. It will also load a makefile from any directory that is scanned by a wildcard.
Automatic loading works if files built by your makefile all
reside in the same directory as the makefile itself. If you write
your makefile so that its rules produce files in a different
directory than the makefile itself, then you might have to tell
makepp where to look for the makefiles, since it doesn't have
any way of guessing. You can do this using the load_makefile
statement in your makefile. For more information about this and
other issues related to multi-directory builds, see the section on directories in the reference
manual.
One caveat: if you reference the variable $(MAKE)
in
your makefile, makepp automatically goes into backward
compatibility mode and turns off automatic loading.
Makepp has several other features which make life slightly
easier for programmers who have to maintain a program spanning
several directories. In the above examples, you'll notice that the
definitions of the variables CXX
and
CXXFLAGS
have to be repeated in each makefile. It can
be a nuisance to reenter the same information into every makefile,
and it could be a problem if you ever decide to change it--you may
have to modify dozens of different makefiles.
What you can do instead is to put all of the information that's
common to each makefile into a separate file, located perhaps at the
top of the directory tree. Common information usually includes
variable definitions, and sometimes also pattern rules. (In the
above example, however, the pattern rules are not the same in both
makefiles.) Let's suppose you've called this file
standard_defs.mk
. Then each makefile simply needs to
contain a statement like this:
include standard_defs.mk
When makepp sees this statement, it inserts the contents
of the file into the makefile at that point. The
include
statement first looks for the file in the
current directory, then in the parent of the current directory, and
so on up to the top level of the file system, so you don't actually
need to specify ../standard_defs.mk
or
../../../../standard_defs.mk
.
So we could rewrite the above makefiles to look like this.
standard_defs.mk
would exist in the top level
directory, and it might contain the following definitions:
# standard_defs.mk CXX := c++ CXXFLAGS := -O2 # # We've also included a pattern rule that might be useful in one or more # subdirectories. This pattern rule is for C compilation for putting # things into a shared library (that's what the -fpic option is for). # %.o: %.cxx $(CXX) $(CXXFLAGS) -fpic -c $(input) -o $(output)
Note that since the included file is actually inserted into each makefile, rules in the included file are applied with the default directory set to the directory containing the makefile that included the file, not the directory containing the include file.
The top level Makefile
might look like this:
# Top level makefile include standard_defs.mk my_program: main.o goodies/libgoodies.so $(CXX) $(inputs) -o $(output) # # Note that this pattern rule overrides the one found in standard_defs.mk, # because makepp sees it later. This pattern rule is for compilation for # a module that doesn't belong in a shared library. # %.o: %.cxx $(CXX) $(CXXFLAGS) $(input) -o $(output)
And the subdirectory's makefile might look like this:
# goodies/Makefile include standard_defs.mk MODULES = candy.o chips.o licorice.o cookies.o popcorn.o spinach.o libgoodies.so: $(MODULES) $(CXX) -shared $(inputs) -o $(output) # We don't need the pattern rule for compilation of .cxx to .o files, because # it's contained in standard_defs.mk.
If you run makepp from within an editor such as emacs, and you are editing sources from several different directories, you may find that the default directory for makepp differs depending on which file you were most recently editing. As a result, makepp may not load the correct makefile.
What you can do to ensure that makepp always loads the
correct makefile(s), no matter what directory happens to be your
current directory, is to use the -F
command line
option, like this:
makepp -F ~/src/my_program
Makepp will first cd to the directory
~/src/my_program
before it attempts to load a
makefile.