How to extend makepp using perl
Makepp internally is flexible enough so that by writing a little bit of perl code, you can add functions or do a number of other operations.
Each makefile lives in its own package. Thus definitions in one makefile do not affect definitions in another makefile. A common set of functions including all the standard textual manipulation functions is imported into the package when it is created.
Makefile variables are stored as perl scalars in that package. (There is an exception to this: automatic variables and the default value of variables like CC are actually implemented as functions with no arguments.) Thus any perl code you write has access to all makefile variables, and any global variables you set can be accessed from the makefile.
You can add a new function to makepp's repertoire by simply defining a
perl subroutine of the same name but with a prefix of f_
. For
example:
sub f_myfunc {
my $argument = $_[0]; # Name the arguments.
... do something here
return $return_value;
}
XYZ := $(myfunc my func arguments)
The first argument to the function is the remaining text in the function invocation after the function name. You'll have to split it into words yourself if that's what you want. There are other arguments, but they are pretty specific to makepp's internals and for most applications you shouldn't need to use them.
The function should return a scalar string (not an array) which is then inserted into the text at that point.
If your function encounters an error, it should die using the usual perl die statement. This will be trapped by makepp and an error message displaying the file name and the line number of the expression causing the error will be printed out.
There are essentially no limits on what the function can do; you can access the file, run shell commands, etc.
At present, expressions appearing in dependencies and in the rule actions are expanded once while expressions appearing in targets are expanded twice, so be careful if your function has side effects and is present in an expression for a target.
Note that the environment (in particular, the cwd) in which the function evaluates will not necessarily match the environment in which the rules from the Makefile in which the function was evaluated are executed. If this is a problem for you, then your function probably ought to look something like this:
sub f_foo { my ($arg, $makefile, $makefile_line) = @_; chdir $makefile->{CWD}; ... etc. }
Sometimes you want makepp to compute a signature method using a different technique. For example, suppose you have a binary that depends on a shared library. Ordinarily, if you change the shared library, you don't have to relink executables that depend on it because the linking is done at run time. (However, it is possible that relinking the executable might be necessary, which is why I did not make this the default.) What you want makepp to do is to have the same signature for the shared library even if it changes.
This can be accomplished in several ways. The easiest way is to create your own new signature method (let's call it "shared_object"). You would use this signature method only on rules that link binaries, like this:
myprogram : *.o lib1/lib1.so lib2/lib2.so : signature shared_object $(CC) $(inputs) -o $(output)
Now we have to create the signature method.
All signature methods must be their own class, and the class must
contain a few special items (see Signature.pm in the distribution for
details). The class's name must be prefixed with Signature::
, so in
this case our class should be called Signature::shared_object
. We
have to create a file called shared_object.pm and put it into a
Signature directory somewhere in the perl include path; the easiest
place might be in the Signature directory in the makepp installation
(e.g., /usr/local/share/makepp/Signature or wherever you installed
it).
For precise details about what has to go in this class, you should look
carefully through the file Signature.pm and probably also
Signature/exact_match.pm in the makepp distribution. But in our
case, all we want to do is to make a very small change to an existing
signature mechanism; if the file is a shared library, we want to have a
constant signature, whereas if the file is anything else, we want to
rely on makepp's normal signature mechanism. The best way to do this is
to inherit from Signature::c_compilation_md5
, which is the signature
method that is usually chosen when makepp recognizes a link command.
So the file Signature/shared_object.pm might contain the following:
use strict; package Signature::shared_object; use Signature::c_compilation_md5; use vars qw/@ISA $shared_object/; # Global variables that we allow. @ISA = qw(Signature::c_compilation_md5); # Indicate inheritance. $shared_object = bless {}; # A piece of magic that helps makepp find # the subroutines for this method. All # signature methods must have one of these. # Now here's the method that gets called when we need the signature of # any target or dependency for which this signature method is active: sub signature { my ($self, # This will be the same as $shared_object. $finfo) = @_; # A special structure that contains everything # makepp knows about this file. See # FileInfo.pm for details.
if ($finfo->{NAME} =~ /\.s[oa]$/) { # Does the file name end in .so or .sa? return $finfo->file_exists ? "exists" : ""; # Always return the same signature if the file # exists. In this case, the signature is the # string "exists". }
Signature::c_compilation_md5::signature; # If the file didn't end in .so or .sa, # delegate to makepp's usual signature method. }
This file is provided as an example in the makepp distribution, with some additional comments.
Incidently, why don't we make this the default? Well, there are times when changing a shared library will require a relinking of your program. If you ever change either the symbols that a shared library defines, or the symbols that it depends on other libraries for, a relink may sometimes be necessary.
Suppose, for example, that the shared library invokes some subroutines
that your program provides. E.g., suppose you change the shared library
so it now calls an external subroutine xyz()
. Unless you use the
-E
or --export-dynamic
option to the linker (for GNU binutils;
other linkers have different option names), the symbol xyz()
may not
be accessible to the run-time linker even if it exists in your program.
Even worse, suppose you defined xyz()
in another library (call it
libxyz), like this:
my_program: main.o lib1/lib1.so xyz/libxyz.a
Since libxyz
is a .a file and not a .so file, then xyz()
may
not be pulled in correctly from libxyz.a unless you relink your
binary.
Signature methods also control not only the string that is used to
determine if a file has changed, but the algorithm that is used to
compare the strings. For example, the signature method target_newer
in the makepp distribution merely requires that the targets be newer
than the dependencies, whereas the signature method exact_match
(and
everything that depends on it, such as md5
and c_compilation_md5
)
requires that the file have the same signature as on the last build.
Here are some other kinds of signature methods that might be useful, to help you realize the possibilities. If general purpose enough, some of these may eventually be incorporated into makepp:
A signature method for shared libraries that returns a checksum of all the exported symbols, and also all the symbols that it needs from other libraries. This solves the problem with the example above, and guarantees a correct link under all circumstances. An experimental attempt has been made to do this in the makepp distribution (see Signature/shared_object.pm), but it will only work with GNU binutils and ELF libraries at the moment.
A signature method that ignores a date stamp written into a file. E.g., if you generate a .c file automatically using some program that insists on putting a string in like this:
static char * date_stamp = "Generated automatically on 01 Apr 2004 by nobody";
you could write a signature method that specifically ignores changes in date stamps. Thus if the date stamp is the only thing that has changed, makepp will not rebuild.
A signature method that computes the signatures the normal way, but ignores the architecture dependence when deciding whether to rebuild. This could be useful for truly architecture-independent files; currently if you build on one architecture, makepp will insist on rebuilding even architecture-independent files when you switch to a different architecture.
A signature method that knows how to ignore comments in latex files, as
the c_compilation_md5
method knows how to ignore comments in C files.
A signature method for automatic documentation extraction that checksums only to the comments that a documentation extractor needs and ignores other changes to the source file.
This document is not finished yet. It will soon cover how to write your own scanners for include files and things like that.