CommanderInstallCommandsImpl.mesa
Copyright Ó 1989, 1990, 1991 by Xerox Corporation. All rights reserved.
Michael Plass, January 14, 1992 10:25 am PST
Chauser, July 31, 1990 1:26 pm PDT
Willie-s, July 23, 1991 10:23 am PDT
Christian Jacobi, July 21, 1992 5:20 pm PDT
Install Commands
InstallData: TYPE = REF InstallDataPrivate;
InstallDataPrivate:
TYPE =
RECORD [
statuses: SymTab.Ref,
change: CONDITION
];
InstallStatus: TYPE = REF InstallStatusPrivate;
InstallStatusPrivate:
TYPE =
RECORD [
state: InstallState ¬ notDone,
foundFile: BOOL ¬ FALSE,
failed: BOOL ¬ TRUE
];
InstallState: TYPE = {notDone, inProgress, done};
installData: InstallData ¬ InitInstallData[];
InitInstallData:
PROC
RETURNS [installData: InstallData] ~ {
installData ¬
NEW [InstallDataPrivate ¬ [
statuses: SymTab.Create[case: FALSE]
]];
TRUSTED {
Process.InitializeCondition[@installData.change, Process.SecondsToTicks[60]];
Process.EnableAborts[@installData.change];
};
};
ForgetInstallationCommand: Commander.CommandProc = {
from: IO.STREAM = IO.RIS[cmd.commandLine];
FOR i:
INT ¬ from.SkipWhitespace[], from.SkipWhitespace[]
WHILE
NOT from.EndOf[]
DO
packageName: ROPE = from.GetTokenRope[IO.IDProc].token;
ForgetInstallation[packageName];
ENDLOOP;
};
ForgetInstallation:
PUBLIC
PROC [packageName:
ROPE] = {
SetInstallState[installData, AcquireInstallStatus[installData, packageName], notDone];
};
Installed:
PUBLIC
PROC [packageName:
ROPE] = {
status: InstallStatus ~ AcquireInstallStatus[installData, packageName];
status.failed ¬ FALSE;
SetInstallState[installData, status, done];
};
GetInstalled:
PUBLIC
ENTRY
PROC
RETURNS [
LIST
OF
ROPE] = {
list: LIST OF ROPE ¬ NIL;
EachPair:
PROC [key:
ROPE, val:
REF]
RETURNS [quit:
BOOL ¬
FALSE] ~ {
WITH val
SELECT
FROM
status: InstallStatus => {
IF status.state = done THEN list ¬ CONS[key, list];
};
ENDCASE => NULL;
};
[] ¬ SymTab.Pairs[installData.statuses, EachPair];
RETURN [RopeList.Sort[list, RopeList.IgnoreCase]]
};
IsInstalled:
PUBLIC
ENTRY
PROC [key:
ROPE]
RETURNS [
BOOL] = {
ENABLE UNWIND => NULL;
sought: InstallStatus ¬ NARROW[installData.statuses.Fetch[key].val];
RETURN [IF sought = NIL OR sought.state = notDone THEN FALSE ELSE TRUE];
};
IfInstalledCommand: Commander.CommandProc = {
ris: IO.STREAM ~ IO.RIS[cmd.commandLine];
resource: ROPE ~ CommanderOps.GetCmdToken[ris].value;
IF resource = NIL THEN CommanderOps.Failed[cmd.procData.doc];
IF (cmd.procData.clientData=$NOT) # IsInstalled[resource] THEN RETURN CommanderOps.ExecuteCommand[cmd, IO.GetRope[ris]];
};
IfProcExportedCommand: Commander.CommandProc = {
ris: IO.STREAM ~ IO.RIS[cmd.commandLine];
resource: ROPE ~ CommanderOps.GetCmdToken[ris].value;
IF resource = NIL THEN CommanderOps.Failed[cmd.procData.doc];
IF (cmd.procData.clientData=$NOT) # IsExported[resource] THEN RETURN CommanderOps.ExecuteCommand[cmd, IO.GetRope[ris]];
};
whenInstalled: SymTab.Ref ¬ SymTab.Create[case: FALSE];
WhenInstalled:
PUBLIC
ENTRY
PROC [key:
ROPE, commandLine:
ROPE]
RETURNS [doItNow:
BOOL ¬
FALSE] = {
Returns TRUE if it needs to be done right away.
ENABLE UNWIND => NULL;
sought: InstallStatus ¬ NARROW[installData.statuses.Fetch[key].val];
doItNow ¬ NOT (sought = NIL OR sought.state = notDone);
IF
NOT doItNow
THEN {
Update: SymTab.UpdateAction ~ {
PROC [found: BOOL, val: Val] RETURNS [op: UpdateOperation ← none, new: Val ← NIL]
actions: LIST OF ROPE ¬ NARROW[val];
IF actions =
NIL
THEN actions ¬ LIST[commandLine]
ELSE
FOR tail:
LIST
OF
ROPE ¬ actions, tail.rest
DO
IF tail.rest = NIL THEN {tail.rest ¬ LIST[commandLine]; EXIT};
ENDLOOP;
RETURN [op: store, new: actions];
};
SymTab.Update[whenInstalled, key, Update];
};
};
WhenInstalledCommand: Commander.CommandProc = {
ris: IO.STREAM ~ IO.RIS[cmd.commandLine];
resource: ROPE ~ CommanderOps.GetCmdToken[ris].value;
IF resource =
NIL
THEN {
PrintEachPair: SymTab.EachPairAction ~ {
PROC [key: Key, val: Val] RETURNS [quit: BOOL ← FALSE]
FOR tail:
LIST
OF
ROPE ¬
NARROW[val], tail.rest
UNTIL tail =
NIL
DO
IO.PutRope[cmd.out, "WhenInstalled "];
IO.PutRope[cmd.out, key];
IO.PutRope[cmd.out, " "];
IO.PutRope[cmd.out, tail.first];
ENDLOOP;
};
[] ¬ SymTab.Pairs[whenInstalled, PrintEachPair];
}
ELSE {
commandLine: ROPE ~ Rope.Cat["From ", PFS.RopeFromPath[PFS.GetWDir[]], IO.GetRope[ris]];
doItNow: BOOL ~ WhenInstalled[resource, commandLine];
IF doItNow THEN RETURN CommanderOps.ExecuteCommand[cmd, commandLine];
};
};
RequireCommand: Commander.CommandProc = {
ENABLE PFS.Error => { IF error.group=user THEN ERROR CommanderOps.Failed[error.explanation] };
world: ROPE ~ CommanderOps.NextArgument[cmd];
component: ROPE ~ CommanderOps.NextArgument[cmd];
resource: ROPE ~ CommanderOps.NextArgument[cmd];
found, failed: BOOL;
IF CommanderOps.NextArgument[cmd] # NIL OR resource = NIL THEN CommanderOps.Failed[cmd.procData.doc];
[found, failed] ¬ Require[world: world, component: component, resource: resource, parent: cmd];
SELECT
TRUE
FROM
NOT failed => { NULL };
NOT found => {
result ¬ $Failure;
msg ¬ msg.Cat["Failed to find ", resource, ".require file.\n"];
};
ENDCASE => {
result ¬ $Failure;
msg ¬ msg.Cat[resource, ".require file ", " failed.\n"];
};
};
InstallCommand: Commander.CommandProc = {
ENABLE PFS.Error => { IF error.group=user THEN ERROR CommanderOps.Failed[error.explanation] };
again: BOOL ¬ FALSE;
n: NAT ¬ 0;
FOR resource:
ROPE ¬ CommanderOps.NextArgument[cmd], CommanderOps.NextArgument[cmd]
UNTIL resource =
NIL
DO
n ¬ n + 1;
SELECT
TRUE
FROM
resource.Equal["-a", FALSE] => again ¬ TRUE;
resource.Equal["-~a", FALSE] => again ¬ FALSE;
ENDCASE => {
found, failed: BOOL;
IF again THEN ForgetInstallation[resource];
[found, failed] ¬ Install[resource, cmd];
SELECT
TRUE
FROM
NOT failed => { NULL };
NOT found => {
result ¬ $Failure;
msg ¬ msg.Cat["Failed to find ", resource, ".require file.\n"];
};
ENDCASE => {
result ¬ $Failure;
msg ¬ msg.Cat[resource, ".require file ", " failed.\n"];
};
};
ENDLOOP;
IF n = 0 THEN RETURN [$Failure, "Install (-a|-~a|packageName)* - Ensure the installation of one or more packages. -a => install again, even if already installed"];
};
InstalledCommand: Commander.CommandProc = {
n: NAT ¬ 0;
FOR arg:
ROPE ¬ CommanderOps.NextArgument[cmd], CommanderOps.NextArgument[cmd]
UNTIL arg =
NIL
DO
Installed[arg];
n ¬ n + 1;
ENDLOOP;
IF n = 0
THEN {
FOR tail:
LIST
OF
ROPE ¬ GetInstalled[], tail.rest
UNTIL tail =
NIL
DO
IO.PutRope[cmd.out, tail.first];
IO.PutRope[cmd.out, IF tail.rest = NIL THEN "\n" ELSE " "];
ENDLOOP;
};
};
SecondSource:
PUBLIC
PROC
[fileName:
ROPE, parent: Commander.Handle, stopOnFailure:
BOOL ¬
FALSE]
RETURNS [allOK:
BOOL ¬
FALSE] = {
fileStream: IO.STREAM ~ PFS.StreamOpen[PFS.PathFromRope[fileName]];
BEGIN
ENABLE
UNWIND => {
IO.Close[fileStream,
TRUE] };
child: Commander.Handle ~ CommanderOps.CreateFromStreams[in: fileStream, parentCommander: parent];
data: CommanderBackdoor.CommandToolData ~ CommanderBackdoor.GetCommandToolData[child];
data.stopOnFailure ¬ stopOnFailure;
allOK ¬ NOT CommanderOps.ReadEvalPrintLoop[child].hadFailure;
END;
IO.Close[fileStream, TRUE];
};
Install:
PUBLIC
PROC [packageName:
ROPE, parent: Commander.Handle]
RETURNS [foundFile, failed:
BOOL] = {
status: InstallStatus = AcquireInstallStatus[installData, packageName];
SELECT status.state
FROM
notDone => ERROR;
inProgress => {
finalState: InstallState ¬ done;
{
ENABLE
UNWIND => { SetInstallState[installData, status, notDone] };
installFileName: ROPE = packageName.Concat[".require"];
foundFile ¬ TRUE;
[] ¬ PFS.FileInfo[PFS.PathFromRope[installFileName] ! PFS.Error => IF error.group = user THEN { foundFile ¬ FALSE; CONTINUE } ];
SELECT status.foundFile ¬ foundFile
FROM
FALSE => { finalState ¬ notDone; failed ¬ TRUE };
TRUE => {
status.failed ¬ failed ¬ NOT SecondSource[installFileName, parent, stopOnFailureDuringInstall];
IF failed THEN finalState ¬ notDone;
};
ENDCASE => ERROR;
};
SetInstallState[installData, status, finalState];
};
done => { foundFile ¬ status.foundFile; failed ¬ status.failed };
ENDCASE => ERROR;
IF
NOT failed
THEN {
commandLines: LIST OF ROPE ¬ NIL;
Update: SymTab.UpdateAction ~ {
PROC [found: BOOL, val: Val] RETURNS [op: UpdateOperation ← none, new: Val ← NIL]
IF found THEN { commandLines ¬ NARROW[val]; RETURN [op: delete] };
};
SymTab.Update[whenInstalled, packageName, Update];
FOR tail:
LIST
OF
ROPE ¬ commandLines, tail.rest
UNTIL tail =
NIL
DO
[] ¬ CommanderOps.DoCommand[tail.first, parent];
ENDLOOP;
};
};
stopOnFailureDuringInstall: BOOL ¬ TRUE;
Require:
--PUBLIC--
PROC [world, component, resource:
ROPE, parent: Commander.Handle]
RETURNS [foundFile, failed:
BOOL ¬ FALSE] = {
RequireInner:
PROC ~ {
[foundFile, failed] ¬ Install[resource, parent];
};
path: PATH ~ PFS.PathFromRope[Rope.Cat["/", world, "/", component, "/"]];
PFS.DoInWDir[path, RequireInner];
};
AcquireInstallStatus:
ENTRY
PROC [d: InstallData, packageName:
ROPE]
RETURNS [sought: InstallStatus] = {
ENABLE UNWIND => {};
sought ¬ NARROW[d.statuses.Fetch[packageName].val];
IF sought =
NIL
THEN {
sought ¬ NEW [InstallStatusPrivate ¬ []];
IF NOT d.statuses.Insert[packageName, sought] THEN ERROR;
};
WHILE sought.state = inProgress DO WAIT d.change ENDLOOP;
IF sought.state = notDone THEN sought.state ¬ inProgress;
};
SetInstallState:
ENTRY
PROC [d: InstallData, status: InstallStatus, state: InstallState] = {
ENABLE UNWIND => {};
status.state ¬ state;
BROADCAST d.change;
};
Registration
Commander.Register[key: "ForgetInstallation", proc: ForgetInstallationCommand, doc: "ForgetInstallation packageName* - Forget that one or more packages are installed", clientData: NIL];
Commander.Register[key: "IfInstalled", proc: IfInstalledCommand, doc: "packageName commandLine\ndo the commandLine if the named package has been installed", clientData: NIL];
Commander.Register[key: "IfNotInstalled", proc: IfInstalledCommand, doc: "packageName commandLine\ndo the commandLine if the named package has not been installed", clientData: $NOT];
Commander.Register[key: "IfProcExported", proc: IfProcExportedCommand, doc: "interface.procedure commandLine\ndo the commandLine if the named procedure is exported", clientData: NIL];
Commander.Register[key: "IfProcNotExported", proc: IfProcExportedCommand, doc: "interface.procedure commandLine\ndo the commandLine if the named procedure is not exported", clientData: $NOT];
Commander.Register[key: "Install", proc: InstallCommand, doc: "Install (-a|-~a|packageName)* - Ensure the installation of one or more packages. -a => install again, even if already installed", clientData: NIL];
Commander.Register[key: "Installed", proc: InstalledCommand, doc: "packageName ... - Record the named packages as having been installed\n (no args tells you what has been installed)", clientData: NIL];
Commander.Register[key: "Require", proc: RequireCommand, doc: "Usage: Require <world> <component> <resource> - Ensure the installation of a resource", clientData: NIL];
Commander.Register[key: "WhenInstalled", proc: WhenInstalledCommand, doc: "packageName commandLine\ndo the commandLine as soon as the named package becomes installed\n(no arguments => print the pending commands)", clientData: $NOT];
What's already running...
Installed["BasicCedar"];
Installed["CedarCore"];
Installed["Commander"];
IF IsExported["BootTime.Get"]
THEN Installed["DebugNubCedarParts"];
IF IsExported["Feedback.Append"]
THEN Installed["Feedback"];
IF IsExported["LocalRegistryAgent.MaintainService"]
THEN Installed["LocalRegistryAgent"];
IF IsExported["LocalRegistryClient.Register"]
THEN Installed["LocalRegistryClient"];
IF IsExported["PFS.FileInfo"]
THEN Installed["PFS"];
IF IsExported["SunPMapClient.CallWithMultipleReplies"]
THEN Installed["SunPMapClient"];
IF IsExported["SunRPC.StartCall"]
THEN Installed["SunRPCRuntime"];
IF IsExported["SystemNames.UserName"]
THEN Installed["SystemNames"];