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.