MakeDoDoc.tioga
Eric Nickell, September 30, 1986 6:42:11 pm PDT
Mike Spreitzer December 11, 1986 6:33:47 pm PST
MakeDo
CEDAR 6.0 — FOR INTERNAL XEROX USE ONLY
MakeDo
Mike Spreitzer
© Copyright 1985 Xerox Corporation. All rights reserved.
Abstract: MakeDo automatically determines dependencies between files, and can issue whatever commands are necessary to bring derived files up to date.
Created by: Mike Spreitzer
Maintained by: Mike Spreitzer <Spreitzer.pa>
Keywords: Make, Dependency, Consistency, Derive, File, DF File, Command, Compile, Bind, Order, Sequence
XEROX  Xerox Corporation
   Palo Alto Research Center
   3333 Coyote Hill Road
   Palo Alto, California 94304

For Internal Xerox Use Only
0. Introduction
MakeDo is a package for determining dependency relations between Cedar objects (usually files), and executing actions to bring these objects up-to-date. It is less ambitious than the System Modeling project, in that it has a lower-level understanding (e.g., source and binary files, instead of Cedar modules), and in that it derives the dependencies itself in a standard way from the environment, rather than reading a model.
1. Theory
1.1 The Dependency Graph
There are objects. An object is like a variable: it has some interesting contents, and they may vary over time. At any given time, an object has a create date, which is the most recent time at which the object's contents were set. A UNIX file is an example of this. A Cedar FS filename-without-version is another example (take the create date and contents of the current highest version to be the create date and contents of the object). An object may not exist, which means its contents can not be examined, and its create date is MakeDo.notExistTime. This is like a NIL pointer, or the name of a file that does not exist (have any versions).
There are actions. An action has something which can be executed, which so far must be a Command Tool command. An action also has some inputs (objects whose contents it reads), and some outputs (objects whose contents it sets). Some of the inputs may be optional, which means that if they don't exist, the action is happy to not read their contents. An action also has some determiners. These are objects, the contents of which are used in determining some variable aspects of the action. The variable aspects of an action are its executable part and its set of inputs. An example action is [command: "RCompile Rope", inputs: {[]<>P>Basics.BCD, []<>P>PrincOps.BCD, []<>P>Rope.Mesa}, outputs: {[]<>P>Rope.BCD}, determiners: {[]<>P>Rope.Mesa, []<>P>Rope.BCD.Switches}].
There are some simplifying assumptions. Each object is an output of at most one action. None of an action's inputs or determiners depends (transitively) on any of its outputs (among other things, this implies that no object can be both an input and an output of the same action).
There are dependencies. An action depends on its determiners. Effectively, each of an action's outputs depends on all of its inputs, but in fact we think of this in two parts: an action depends on its inputs, and an output depends on the action (if any) that produces it.
There is a concept of modifiability. MakeDo is willing to accept a set of objects which it may modify, with the assumption that it is not allowed to modify any others. MakeDo is also willing to operate without such guidance, and will modify any objects it detects a need to. Thus, each object is either certainly modifiable, certainly not modifiable, or unspecifiedly modifiable.
An object is a leaf (of the dependency DAG) if there is no way to derive it from other objects, or if it is certainly not modifiable.
There are two intertwined concepts of current and broken. An object is current if it's in as good shape as possible. A current object is broken if there's still something definitely wrong with it (determining this is somewhat problematical, because MakeDo is sometimes asked to operate without much guidance about what's to be expected).
Loosely speaking, an object is current if either it's A-OK, or there's nothing MakeDo can do about it. Here is a precise formulation:
A leaf is current;
a non-leaf N, with producing action A, is current iff
all of A's determiners are current and
either
some of A's determiners are broken,
or
A's variable parts are derived from the current contents of its determiners, and
all inputs to A are current, and
either
some of A's inputs are broken, or
some of A's mandatory inputs don't exist, or
A fails when executed on the current contents of its inputs, or
A claims N is consistent with the current contents of its inputs.
From the above, you can deduce that MakeDo will attempt to execute an action A iff:
all of A's determiners are current, and
none of A's determiners are broken, and
A's variable parts are derived from the current contents of its determiners, and
all of A's inputs are current, and
none of A's inputs are broken, and
all of A's mandatory inputs exist, and
A is not known to fail when executed on the current contents of its inputs, and
A claims some output N is inconsistent with the current contents of its inputs.
Loosely speaking, a current object is broken if it doesn't exist but is expected to, or if the action that derives it fails, or if it depends on broken objects. Precisely, a current object N is broken iff:
N is certainly modifiable and doesn't exist, or
N is produced by A and is not certainly not modifiable and either:
some of A's inputs or determiners are broken, or
N is certainly modifiable and some of A's mandatory inputs don't exist, or
all of A's mandatory inputs exist and A fails when executed on the current contents of its inputs.
I am not confident that this definition of broken accurately captures what's desired. Comments and suggestions are welcome.
1.2 The Action-Class Registry
All Compile or Bind actions belong to the CompileAndBind action class. All Lupine actions belong to the Lupine action class. An action class specifies the rules for discovering dependencies, the rules for determining when an output is consistent with with the inputs, and the rules for how the variable parts of an action are computed from its determiners. MakeDo maintains a registry of action classes. It initially contains four (CompileAndBind, Lupine, MakeIt and RederiveSummonerLoad); clients may add more.
The CompileAndBind action class uses version stamps to determine when outputs are consistent with inputs. This is consistent with the way the Compiler and Binder work. This class uses an optional extra determiner, file Foo.BCD.Switches (for the action "RCompile Foo"), to get the switches for the compile or bind command. The file should contain the switch string, including introductory character (currently a dash ('-)).
The Lupine action class uses create dates to determine when outputs are consistent with inputs. It also uses an optional extra determiner, file Foo.LupineSwitches (for the action "Lupine TranslateInterface[Foo]"), to get additional Lupine arguments.
The MakeIt action class produces file foo by executing file foo.makeIt . Like the Lupine action class, it uses create dates to determine when outputs are consistent with inputs. The file should contain a line of the form ``-- { dependency1 dependency2 ... dependencyn }'' to declare its dependencies. Careful construction of the makeIt files is necessary to ensure proper execution. Also note that foo.makeIt must exist before MakeDo will will build the dependency between foo.makeIt and foo. (Thus, the MakeIt action class, unfortunately, will not build a two-step dependency between foo.makeIt.makeIt and foo, unless some version of the file foo.makeIt is present.)
The SummonerLoad action class produces file foo.summonerLoad as an expansion of foo.summonerInstall. This allows a straightforward way to produce accurate summonerLoad files.
1.3 Bringing Things Up-To-Date
The basic operation of MakeDo is to simultaneously derive the dependency graph and make object current. Given a set of goals, and a modifiability spec, MakeDo makes the goals current, and returns counts of how many are broken and how many are not.
1.4 Incrementality
Although conceptually at all times an object has well-defined currentness, brokenness, and producer, MakeDo does not try to know these things at all times. However, once it computes these things, it does not forget them. When computing new information, or determining what to do, MakeDo stops exploring the dependency graph where all the relevant values are known and nothing needs to be done.
1.4.1 The Modifiability Bug
One of the contributors to the currency computation is the modifiability. The modifiability is unique in having the unfortunate property of coming from the user who is asking for something to be done, instead of from the dependency graph itself. It is expected that, for any given object, the user is not really interested in changing its modifiability (very often), although the user may direct MakeDo at alternating tasks, with a different modifiability for each, but (probably, mostly) non-conflicting modifiability for the intersecting objects, if any. Comforted by this assumption, MakeDo stores modifiability on the objects. Now, strictly speaking, this means that every time modifiability is specified, MakeDo should explore the entire dependency graph and update the modifiability stored there --- and you've probably already guessed that it doesn't. What it does do is update the modifiability of any objects it happens to touch for other reasons. It is hoped that this comes close enough for most purposes --- please complain if it's not.
1.5 The object-class Registry
Each object belongs to an object class. An object class is responsible for being able to report the current create date of any of its members. MakeDo remembers such reports, and only asks the object class again if there is reason to suspect the create date may have changed. There is a procedure in the MakeDo interface that an object class may call to make MakeDo suspect that an object's create date may have changed. An object class could monitor all of its members, and poke MakeDo whenever any of them changes. The file object class does this. Alternatively, one could suspect create date changes in all the relevant objects (supposing one could enumerate this set) just before calling MakeDo.Ensure.
1.6 Parallelism
When bringing things up-to-date, MakeDo is willing to employ additional processes, up to some client-specified limit. The reason for this is that while doing the executable part of an action, that process does nothing else. With multiple processes, you could be executing multiple actions at once (e.g., taking advantage of multiple compiler servers).
When MakeDo forks an auxiliary process, it is for the purpose of executing one action --- when that finishes, the process is de-allocated. Before forking, MakeDo prints a message in the command tool indicating that it is about to fork a process, and what action it will execute. Then, in a container viewer maintained for just this purpose, named "MakeDoAuxBox", a typescript viewer is created, and the action is executed in that typescript. When it is done, the typescript is deleted. The output from the auxiliary process is also gathered in a buffer, and if the auxiliary process failed, its output printed all at once in the original command tool when the process finishes. If it succeeded, only that fact is printed, unless the MakeDo.AlwaysGush user profile entry says otherwise.
1.7 Interrupting
Some MakeDo operations deal well with being interrupted, and some don't. For the real truth, see the descriptions of the individual commands.
When an action is forked, a button is created just to the left of its typescript. This button's name is an exclamation-mark (!), and its function is to abort that action.
2. Programmer's Interface
See the interface module MakeDo.
3. User Profile Entries
MakeDo.AuxilliaryProcessAllocation: INT ← 10
This is the initial setting for the limit on the number of additional processes MakeDo is willing to employ. It can be later modified by Cedar procedure calls or command switches.
MakeDo.AlwaysGush: BOOLFALSE
When TRUE, all the output of a forked command is copied back to the originating typescript, regardless of whether the forked command failed.
MakeDo.DeleteEmptyAuxBox: BOOLTRUE
When TRUE, the container for forked typescripts will be deleted whenever the number of forked activities goes to 0 at the end of a job.
MakeDo.IconFile: Token ← "MakeDo.Icons"
Names the file used for the icons. If the file name is relative, it is relative to the installation directory of MakeDo (i.e. ///Commands/, if you're kosher). The 0'th icon is used when there are 0 forked processes, the 1'th when there are 1, and so on; the last icon is used as a last resort. Note that since Viewers is only willing to conceive of a limited number (64) of icon-types, you don't want to try changing this a lot in one VM.
4. Command Tool Commands
name MakeDo:
syntax
MakeDo arg*
arg:
object |
-nm object |
-rg DF file name |
-og DF file name |
-rgom DF file name |
-dr DF file name |
-om DF file name |
-p number
object:
file name |
(className:ROPE objectName:ROPE)
description
MakeDo executes whatever commands are necessary to produce up-to-date versions of given objects. For instance, it will run the compiler and binder, on the right files, in the right order. You have the option of delimiting what MakeDo is allowed to cause to be changed. Exercising this option gives you added safety and error reporting.
Each argument to MakeDo is a bare object, or something with a switch. Bare objects are taken to be goals. Here is what the switches mean:
-rg DF file name: (roots are goals) Take the files in the DF file marked as verify-roots (i.e., preceded by a "+") to be goals.
-og DF file name: (owned are goals) Take all the owned files (i.e., ones that might be stored if you did an SModel) to be goals.
-rgom DF file name: (roots are goals, owned are modifiable) Take the files in it marked as verify-roots to be goals. Add to the delimited set of what MakeDo may change all of the modifiable files in the DF-file.
-dr DF file name: Same as -rgom; for backward compatibility.
-om DF file name: (owned are modifiable) Add to the delimited set of what MakeDo may change all of the files owned by the DF-file.
-nm object: (named object is modifiable) Add it to the delimited set of what MakeDo may change.
-p number: Reset the limit of auxiliary processes MakeDo may use to the given value --- if you don't use this switch, the limit won't change.
If one of the switches rgom, dr, om, or nm is used, the set of things MakeDo may cause to be changed is delimited to what was explicitly asked for, plus, of course, the goals. Otherwise, what MakeDo may change is undelimited --- it may change anything.
examples
Suppose you have a package. It has a public interface (Foo), a private interface (FooPrivate, which references Foo), two implementation modules (FooImplA and FooImplB, both of which reference FooPrivate), and a configuration (FooPackage, which references FooImplA and FooImplB). After any sequence of compiling, binding, and editing, the command

% MakeDo FooPackage.BCD (the ".BCD" is optional, so far)
will do whatever compiles and binds are necessary to produce an up-to-date version of FooPackage.BCD. For instance, if everything was up-to-date, then you edited FooImplA, then issued the MakeDo command, MakeDo would compile FooImplA and then (if the compile was successful) bind FooPackage. If you were working in subdirectory Foo, with a FooPackage.BCD.Switches that contained
/c~s
it might look like this:

% RCompile FooImplA
Compiling: FooImplA/-g . . . . . . no errors.
End of compilation
% Bind /c~s FooPackage
Binding: FooPackage
. . . . 0 errors
End of binding
If you have a Foo.DF that follows all the usual conventions, a good way to invoke MakeDo is

% MakeDo -rgom Foo.DF
warnings
Beware!
stop/undo
Click the STOP! button in the command tool.
name MakePrediction:
syntax
MakePrediction arg*
arg: <same as before>
description
Gives an estimate of what commands the MakeDo command would execute.
stop/undo
Click the STOP! button in the commander.
name MakeCommandList:
syntax
MakeCommandList arg*
arg: <same as before>
description
Lists all the commands to make the goals, in order.
stop/undo
Click the STOP! button in the commander.
name MakeExcuses:
syntax
MakeExcuses object*
description
Explains what MakeDo thinks about the given objects.
examples
Suppose your package has a module named MakeDoPrivate; you might do something like this:

% MakeExcuses MakeDoPrivate.BCD
[]<>Users>Spreitzer.pa>MD>MakeDoPrivate.BCD
 Created September 7, 1985 15:28:17.
 Is Current
 Not troubled.
 Needed by {
  RCompile MakeDoNodeProps
  RCompile MakeDoFinders
  RCompile MakeDoBasicImpl
  RCompile MakeDoAuxImpl};
 determines {}.
 Certainly modifiable.
 Currently consistent with inputs.
RCompile MakeDoPrivate
 According to {
 ? []<>Users>Spreitzer.pa>MD>MakeDoPrivate.BCD.Switches
  []<>Users>Spreitzer.pa>MD>MakeDoPrivate.Mesa}
 Makes {
  []<>Users>Spreitzer.pa>MD>MakeDoPrivate.BCD}
 From {
  []<>Users>Spreitzer.pa>MD>BasicTime.BCD
  []<>Users>Spreitzer.pa>MD>CedarProcess.BCD
  []<>Users>Spreitzer.pa>MD>Commander.BCD
*  []<>Users>Spreitzer.pa>MD>IO.BCD
  []<>Users>Spreitzer.pa>MD>List.BCD
!  []<>Users>Spreitzer.pa>MD>MakeDo.BCD
  []<>Users>Spreitzer.pa>MD>RedBlackTree.BCD
 ?? []<>Users>Spreitzer.pa>MD>Rope.BCD
  []<>Users>Spreitzer.pa>MD>TimeStamp.BCD
  []<>Users>Spreitzer.pa>MD>MakeDoPrivate.Mesa}.
 Derived from current determiners.
 Not tried with current inputs.
Those funny punctuation marks give additional information about the objects referenced. When an object is preceded by an asterisk (*), it means that object is not known to be current. In the example above, this is the case for IO.BCD. When an object is preceded by an exclamation mark (!), it is broken; this is the case for MakeDo.BCD in the example above. When an object is preceded by a question mark (?), it doesn't exist; see MakeDoPrivate.BCD.Switches above for an example. When an object is preceded by a double question mark (??), MakeDo does not know the create date of that object; this should never happen, but it is illustrated in the example above for Rope.BCD.
warnings
Beware the Ides of March!
stop/undo
Click the STOP! button in the commander.
name MakeSuspicion:
syntax
MakeSuspicion arg*
arg:
-change object --suspect the named file of change
-current object --forget whether named file is current
-producer object --suspect that the next execution of the file's producer will be different from the last
object --all of the above
-allChange --suspect every object of change
-allCurrent --suspect every object of not being current and every action of behaving differently than last time
-all --all of the above
description
This command is for flushing parts of MakeDo's cache of computed knowledge. It should rarely be necessary.
examples

% MakeSuspicion -all
stop/undo
Don't!
name MakeEmpty:
syntax
MakeEmpty
description
Deletes the entire dependency graph. For when MakeDo is thoroughly messed up.
examples

% MakeEmpty
stop/undo
Don't!
name MakeProducer:
syntax
MakeProducer object*
description
Retries figuring out how to make named leaves.
examples

% MakeProducer FooPackage.BCD
stop/undo
Don't!
name RederiveSummonerLoad:
syntax
RederiveSummonerLoad object*
description
Builds .summonerLoad files based on .summonerInstall files.
examples

% RederiveSummonerLoad TSetter
stop/undo
Don't!