PMake -- A Tutorial
Prev | Chapter 3 Short-cuts and Other Nice Things | Next

# 3.4. Target Attributes

PMake allows you to give attributes to targets by means of special sources. Like everything else PMake uses, these sources begin with a period and are made up of all upper-case letters. There are various reasons for using them, and I will try to give examples for most of them. Others you will have to find uses for yourself. Think of it as an exercise for the reader. By placing one (or more) of these as a source on a dependency line, you are marking the target(s) with that attribute. That is just the way I phrase it, so you know.

Any attributes given as sources for a transformation rule are applied to the target of the transformation rule when the rule is applied.

`.DONTCARE` | If a target is marked with this attribute and PMake can not figure out how to create it, it will ignore this fact and assume the file is not really needed or actually exists and PMake just can not find it. This may prove wrong, but the error will be noted later on, not when PMake tries to create the target so marked. This attribute also prevents PMake from attempting to touch the target if it is given the `-t` flag.
`.EXEC` | This attribute causes its shell script to be executed while having no effect on targets that depend on it. This makes the target into a sort of subroutine. An example. Say you have some LISP files that need to be compiled and loaded into a LISP process. To do this, you echo LISP commands into a file and execute a LISP with this file as its input when everything is done. Say also that you have to load other files from another system before you can compile your files and further, that you do not want to go through the loading and dumping unless one of your files has changed. Your makefile might look a little bit like this (remember, this is an educational example, and do not worry about the `COMPILE` rule, all will soon become clear, grasshopper):
    system          : init a.fasl b.fasl c.fasl
    	for i in $(.ALLSRC);
    	do
    		echo -n '(load "' >> input
    		echo -n ${i} >> input
    		echo '")' >> input
    	done
    	echo '(dump "$(.TARGET)")' >> input
    	lisp < input

    a.fasl          : a.l init COMPILE
    b.fasl          : b.l init COMPILE
    c.fasl          : c.l init COMPILE
    COMPILE         : .USE
    	echo '(compile "$(.ALLSRC)")' >> input
    init            : .EXEC
    	echo '(load-system)' > input
`.EXEC` sources, do not appear in the local variables of targets that depend on them (nor are they touched if PMake is given the `-t` flag). Note that all the rules, not just that for system, include init as a source. This is because none of the other targets can be made until init has been made, thus they depend on it.
`.EXPORT` | This is used to mark those targets whose creation should be sent to another machine if at all possible. This may be used by some exportation schemes if the exportation is expensive. You should ask your system administrator if it is necessary.
`.EXPORTSAME` | Tells the export system that the job should be exported to a machine of the same architecture as the current one. Certain operations (e.g. running text through nroff) can be performed the same on any architecture (CPU and operating system type), while others (e.g. compiling a program with cc) must be performed on a machine with the same architecture. Not all export systems will support this attribute.
`.IGNORE` | Giving a target the `.IGNORE` attribute causes PMake to ignore errors from any of the target's commands, as if they all had `-` before them.
`.INVISIBLE` | This allows you to specify one target as a source for another without the one affecting the other's local variables. Useful if, say, you have a makefile that creates two programs, one of which is used to create the other, so it must exist before the other is created. You could say
    prog1           : $(PROG1OBJS) prog2 MAKEINSTALL
    prog2           : $(PROG2OBJS) .INVISIBLE MAKEINSTALL
where `MAKEINSTALL` is some complex `.USE` rule (see below) that depends on the `.ALLSRC` variable containing the right things. Without the `.INVISIBLE` attribute for `prog2`, the `MAKEINSTALL` rule could not be applied. This is not as useful as it should be, and the semantics may change (or the whole thing go away) in the not-too-distant future.
`.JOIN` | This is another way to avoid performing some operations in parallel while permitting everything else to be done so. Specifically it forces the target's shell script to be executed only if one or more of the sources was out-of-date. In addition, the target's name, in both its `.TARGET` variable and all the local variables of any target that depends on it, is replaced by the value of its `.ALLSRC` variable. As an example, suppose you have a program that has four libraries that compile in the same directory along with, and at the same time as, the program. You again have the problem with ranlib that I mentioned earlier, only this time it is more severe: you can not just put the ranlib off to the end since the program will need those libraries before it can be re-created. You can do something like this:
    program         : $(OBJS) libraries
    	cc -o $(.TARGET) $(.ALLSRC)

    libraries       : lib1.a lib2.a lib3.a lib4.a .JOIN
    	ranlib $(.OODATE)
In this case, PMake will re-create the `$(OBJS)` as necessary, along with `lib1.a`, `lib2.a`, `lib3.a` and `lib4.a`. It will then execute ranlib on any library that was changed and set program's `.ALLSRC` variable to contain what's in `$(OBJS)` followed by `lib1.a` `lib2.a` `lib3.a` `lib4.a`. In case you are wondering, it is called `.JOIN` because it joins together different threads of the input graph at the target marked with the attribute. Another aspect of the `.JOIN` attribute is it keeps the target from being created if the `-t` flag was given.
`.MAKE` | The `.MAKE` attribute marks its target as being a recursive invocation of PMake. This forces PMake to execute the script associated with the target (if it is out-of-date) even if you gave the `-n` or `-t` flag. By doing this, you can start at the top of a system and type `pmake -n` and have it descend the directory tree (if your makefiles are set up correctly), printing what it would have executed if you had not included the `-n` flag.
`.NOEXPORT` | If possible, PMake will attempt to export the creation of all targets to another machine (this depends on how PMake was configured). Sometimes, the creation is so simple, it is pointless to send it to another machine. If you give the target the `.NOEXPORT` attribute, it will be run loally, even if you have given PMake the `-L 0` flag.
`.NOTMAIN` | Normally, if you do not specify a target to make in any other way, PMake will take the first target on the first dependency line of a makefile as the target to create. That target is known as the Main Target and is labeled as such if you print the dependencies out using the `-p` flag. Giving a target this attribute tells PMake that the target is definitely not the Main Target. This allows you to place targets in an included makefile and have PMake create something else by default.
`.PRECIOUS` | When PMake is interrupted (you type control-C at the keyboard), it will attempt to clean up after itself by removing any half-made targets. If a target has the `.PRECIOUS` attribute, however, PMake will leave it alone. An additional side effect of the `::` operator is to mark the targets as `.PRECIOUS`.
`.SILENT` | Marking a target with this attribute keeps its commands from being printed when they are executed, just as if they had an `@` in front of them.
`.USE` | By giving a target this attribute, you turn it into PMake's equivalent of a macro. When the target is used as a source for another target, the other target acquires the commands, sources and attributes (except `.USE`) of the source. If the target already has commands, the `.USE` target's commands are added to the end. If more than one `.USE`-marked source is given to a target, the rules are applied sequentially. The typical `.USE` rule (as I call them) will use the sources of the target to which it is applied (as stored in the `.ALLSRC` variable for the target) as its arguments, if you will. For example, you probably noticed that the commands for creating `lib1.a` and `lib2.a` in the example in section Section 3.3 were exactly the same. You can use the `.USE` attribute to eliminate the repetition, like so:
    lib1.a          : $(LIB1OBJS) MAKELIB
    lib2.a          : $(LIB2OBJS) MAKELIB

    MAKELIB         : .USE
    	rm -f $(.TARGET)
    	ar cr $(.TARGET) $(.ALLSRC)
    	...
    	ranlib $(.TARGET)
Several system makefiles (not to be confused with The System Makefile) make use of these `.USE` rules to make your life easier (they are in the default, system makefile directory...take a look). Note that the `.USE` rule source itself (`MAKELIB`) does not appear in any of the targets's local variables. There is no limit to the number of times I could use the `MAKELIB` rule. If there were more libraries, I could continue with `lib3.a : $(LIB3OBJS) MAKELIB` and so on and so forth.

Prev | Home | Next
Saving Commands | Up | Special Targets