makepp_build_algorithm -- How makepp executes a makefile
Makepp's internals differ from the standard Unix make in fundamental ways. This
page describes the different philosophy in detail.
Makepp works in the opposite direction from the standard Unix make. Traditional
Unix make is given a target to build, and then it finds a rule which matches
the characters in the target filename. If the target is older than any of its
dependencies of the rule, then it is rebuilt.
For example, consider this pattern rule:
$(CXX) $(CXXFLAGS) -c $(input) -o $(output)
When make realizes it needs to make a file called "xyz.o", it searches
through its list of pattern rules until it sees that "xyz.o" matches
the pattern "%.o", and then it applies this rule.
Makepp works in the opposite direction. It first computes all files that it can
possibly build by applying rules that match the characters in the dependency
filenames. Then when it needs to build a file, it simply looks to see if it's
one of the files that it knows how to build. The list of known files is stored
based on the absolute filename.
When makepp encounters the above pattern rule, it searches for all files in the
directory matching the pattern "%.cxx" (i.e., "*.cxx").
For each of these files, it then remembers that it can produce the
corresponding ".o" file. If subsequently makepp discovers that it
can make another ".cxx" file that doesn't currently exist, this rule
will also be applied and the corresponding ".o" file will be marked.
This might seem somewhat inefficient, but it turns out not to be that slow in
most cases, and it is often true that virtually all the files that can be
built are in fact built. And knowing the complete list of files that can be
built has several advantages:
- Wildcards can match files which don't exist yet but can be built.
- Header files which have been detected by the automatic dependency scanner
don't have to exist; makepp knows where they will be. (Most other
solutions to this problem assume that any headers which don't exist yet
are in the current directory.)
- Repositories are much simpler to implement since makepp knows beforehand
what files it can make. (See makepp_repositories for details.)
- It is possible to determine easily which files can be built (see the
"$(only_targets )" function.
- Makepp's "$(infer_objects)" function is greatly simplified by
knowing what objects are available.
Makepp associates build commands with a target file, not to a textual pattern
for a filename. It is therefore not confused by different names for the same
file. Thus, for example, makepp will know that "./xyz" and and
"xyz" are the same file, whereas other make utilities may not.
This is particularly important because (unlike the standard make) makepp loads
makefiles from different directories. In order for the makefiles to be
relatively independent, with no special position given to a top-level
makefile, each makefile refers to all files relative to its own directory.
Thus if you load a makefile from the subdirectory "other_stuff", and
that makefile refers to "../xyz", makepp will again realize that
it's the same file referred to above. (It also won't be confused by
soft-linked directory names.)
Makepp stores much more information about each file that it builds beyond just
the date stamp (which is all that the standard make cares about). This
- The signature of this file on the last build, so we know if the file
itself has changed.
- The names of each dependency file, including include files and other files
inferred automatically. If this list changes, then makepp assumes it needs
- The signature of each dependency. This way, makepp knows to rebuild not
only when the dependencies are newer than the target, but when they change
at all. This also makes it possible to use other kinds of signatures, such
as cryptographic checksums, rather than the file date.
- The entire build command (and its cwd). This way if you change the build
command (e.g., change the compiler options), makepp knows to rebuild even
if the files themselves haven't changed.
- The architecture. If you have compiled your program on Linux and then
switch to Solaris, makepp automatically knows to recompile
Makepp makes a subdirectory in every directory that it touches called
".makepp". The build information for a file filename
directory is stored in .makepp/filename
. If you delete this
subdirectory or alter the files, makepp will rebuild all affected files.
If makepp is trying to build a target in a directory and doesn't have a rule for
it yet, or if it is looking for files matching a wildcard in a directory, it
will look in that directory to see if a makefile is present. If so, the
makefile will be loaded automatically.
This means that you usually don't have to tell makepp explicitly where to find
makefiles--all you have to do is to reference a file in another directory, and
makepp will automatically figure out how to build it.
Implicit loading will occur only if the directory is writable to you. Thus if
you want to prevent makepp from trying to build a bunch of things that never
change, simply make the directory read-only.
Implicit loading will not occur if you are in a tree under a
and the other makefile is outside that tree. If you
do want this once, you can give a "--do-build=/" option to makepp,
to make everything outside the tree buildable. If you always want this, you
can put a "load_makefile" statement somewhere within the tree to
explicitly connect it to the tree.
If implicit loading gets in your way (i.e., makepp loads too many makefiles and
it wastes time, or else you really don't want it to try to rebuild all the
stuff described in the makefiles), you can turn it off for all directories
using the "--noimplicit_load" command line option, or you can turn
it off for selected directories using the "no_implicit_load"
statement in your makefile.