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
DIRECTORY Commander, CommanderBackdoor, CommanderOps, IO, PFS, Process, Rope, RopeList, SymTab, InstallationComforts, InstallationSupportPrivate, MesaLoadState;
CommanderInstallCommandsImpl: CEDAR MONITOR
IMPORTS Commander, CommanderBackdoor, CommanderOps, IO, PFS, Process, Rope, RopeList, SymTab, InstallationComforts, MesaLoadState
EXPORTS MesaLoadState -- Opaque type
~ BEGIN
PATH: TYPE ~ PFS.PATH;
ROPE: TYPE ~ Rope.ROPE;
Load state inquiry
ConfigRep: PUBLIC TYPE ~ InstallationSupportPrivate.ConfigRep;
IsExported: PROC [qualifiedName: ROPE] RETURNS [BOOL] ~ {
global: REF ConfigRep = MesaLoadState.GlobalConfig[];
dot: INT ~ Rope.Find[qualifiedName, "."];
IF dot >= 0
THEN {
interface: ROPE ~ Rope.Substr[qualifiedName, 0, dot];
item: ROPE ~ Rope.Substr[qualifiedName, dot+1];
a: PROCEDURE ANY RETURNS ANY ~ InstallationComforts.ProcFromNamedInterface[interface, item, global];
RETURN [a # NIL]
}
ELSE {
RETURN [FALSE] -- might want to test for existence of the interface record
};
};
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: BOOLFALSE]
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"];
END.