# Scanning Commands and Include Files

Makepp can guess additional dependencies or targets for certain commands that it knows something about. This is especially importat for C/C++ compilation, where it is too error-prone to list manually all of the include files that a given source file depends on. By looking at the compilation command and the source files themselves, makepp is able to determine accurately which object files need to be rebuilt when some include file changes.

Makepp looks at the first word of your command line. If it recognizes it, it uses the scanner corresponding to that first word. Specifically, it isolates the first word and looks it up in its table; if nothing is found, it strips off the directory information and looks it up again. (This means that you can specify the path to your compiler and makepp will still recognize it.) Currently, makepp recognizes most C/C++ compiler names. It also recognizes commands invoking `sh` and `libtool`; for these commands, it skips to the first word in the command that's not an option and looks it up in the table again.

If makepp thinks it's compiling a C/C++ program but can't find a scanner, it will give a warning message to let you know. This usually means that you buried your compiler command too deeply in the action for makepp to find it. For example, I have seen rules like this:

    %.o: %.c
    	@echo Compiling $< now
    	@gcc -c $< $(CFLAGS) -o $@

The first word of the action here is `echo`, for which therer is no scanner, so makepp will not scan for include files in this case.

## C/C++ compilation

The C/C++ scanner is activated by a command beginning with a compile program that makepp knows about. (I've included every compiler I've ever heard of, but if I missed your compiler, you can tell makepp about it by adding an entry to the `%scanners` array. Also send me email so I can include it in subsequent releases. See the section on custom scanners for details.)

It looks at the command for `-Idir` options specifying the include path or `-Ldir` options specifying the link path. It then scans any source files for `#include` directives, and also looks at the command line to see if there are any source files or libraries mentioned which are not listed as dependencies. It recognizes these by their extension.

This scanner gives a warning message if files included with `#include "file.h"` are not found in the include path, or in the directory containing the file which is `#includ`ing, or in `/usr/include`. No warning is given if a file included with `#include <file.h>` is not found. makepp assumes it is in some system include directory that the compiler knows about, and that files in system include directories won't change.

In addition, files in `/usr/include`, `/usr/local/include`, `/usr/X11R6/include`, and any other directory which is not writable are not scanned to see what they include. Makepp assumes that these files won't change. (If you're running as root, the writability test is performed with the UID and GID of the directory you ran makepp from. This is so compiling a program as an ordinary user and then doing `make install` as root won't cause extra directories to be scanned.)

This is a fairly simple-minded scanner. It will get confused if you do things like this:

    #ifdef INCLUDE_THIS
    #include "this.h"
    #endif

because it doesn't know about preprocessor conditionals. This is usually harmless; it might cause additional extra files to be labelled as dependencies (occasionally causing unnecessary rebuilds), or else it might cause makepp to warn that the include file was not found. You can either ignore the warning messages, or put an empty file `this.h` out there to shut makepp up.

## Libtool

Libtool is a very clever compilation system that greatly simplifies making shared libraries by hiding all the system-dependent details away in a shell script. The only difficulty is that the library binary files are not actually stored in the same directory as the output file--libtool actuall creates a subdirectory, `.libs`, which contains the real files. This is ordinarily not a problem, but makepp has to know where the real binaries are if it is to link them in from a repository. At the moment, libtool libraries (`.la` files) are not linked in from repositories; they are always rebuilt if needed. Also, makepp at the moment is not able to use the dependency information that is stored inside the `.la` file itself. This will hopefully change soon.

## Other special command words

Makepp recognizes the following command words and skips over them in its search for the correct scanner:

- `purify`
- `sh`
- `ignore_error`
- `noecho`
- `condor_compile`

## Custom scanners

You can tell makepp to use a specific scanner on a rule-by-rule basis; see the `:scanner` modifier for the rule. If you're using an unusual compiler, or you want to write your own scanner for a different language, you can also modify makepp's table of what scanners it knows about. The table is stored in the `%scanners` hash, which you can access from perl code in your makefile.

For example, the outputs from a java compilation are not directly predictable from the command line; it depends on what nested classes are defined in the source file. You could write a scanner for java code (contributions are welcome!) that tells makepp what the command modifies, and what the inputs to the command are, by putting something like this in your makefile:

    perl_begin   # Begins a block of perl code in the makefile.

    sub scanner_javac {
      my ($action, $rule) = @_;	# $action is the text of the command.
    				# $rule is a reference to the Rule object
    				# (see Rule.pm) which you need to add
    				# dependencies and targets.
      my @cmd_words = split(' ', $action); # Get the words of the command.
      my $build_cwd = $rule->build_cwd;
    #
    # ... Now scan the command, pulling out the source files and figuring out
    # what the targets are.
    #
      ...

      foreach (@dependencies_discovered) {
        $rule->add_dependency(file_info($_, $build_cwd));
    				# file_info returns makepp's handle for the
    				# file.  Rule::add_dependency adds it to the
    				# dependency list.
      }

      foreach (@targets_this_command_produces) {
        $rule->add_target(file_info($_, $build_cwd))
      }
    }

    $scanners{"javac"} = \&scanner_javac;
    				# Puts the scanner in makepp's table of
    				# scanners, to be activated whenever a command
    				# begins with the word "javac".

    end_perl			# Ends the block of perl code.

That's pretty much all there is to it. The name of your scanner function should begin with `scanner_`, because the `:scanner` option adds `scanner_` to the name before looking up the function.

Table of contents | Next (inferring objects) | Previous (repositories)
Last modified: Sat Oct 14 22:37:27 PDT 2000