1) Insure .df file has a global attachment
2) Process the .df file, and extract the .remoteCommands and .summonerload files, and build a list of all the exported file.
3) open remoteCommands file. Check that it parses OK and is reasonable.
4) open summonerload file. Check packages in summonerload for export in the .df file, and check to see that the files with these create dates exist.
5) Take package bcd and generate IMPORTs. Check against list of good imports. Complain if needed. (this should be done on the controller, and be protected, but I trust people) This is a noop for now, however.
6) Tell the controller about the new package.
mainDFFileCreated: BasicTime.GMT;
{
-- nest to get the EXIT to see variables
DoOneItemFromDFFile: DFUtilities.ProcessItemProc = {
PROC [item: REF ANY] RETURNS [stop: BOOL ← FALSE]
errors, warnings, filesActedUpon: INT ← 0;
WITH item
SELECT
FROM
dir:
REF DFUtilities.DirectoryItem => {
lastDir ← dir;
};
imports:
REF DFUtilities.ImportsItem => {
importsCreated: BasicTime.GMT;
[created: importsCreated] ←
FS.FileInfo[name: imports.path1, remoteCheck:
TRUE !
FS.Error => {
ERROR DFUtilities.SyntaxError[Rope.Cat["Imported df file ", imports.path1, " does not exist on the file server"]];
};
];
IF BasicTime.Period[from: mainDFFileCreated, to: importsCreated] > 0
THEN {
cmd.out.PutRope[Rope.Cat["Warning: Imported df file ", imports.path1, " newer than package df file.\n"]];
IF noIERRyet
THEN {
cmd.out.PutRope["Bringover the package df file and see if new attachments are made for interfaces. If so, rebuild, smodel and submit the package. If no interfaces have changed but there are newer implementations (packages in the summonerLoad file), then a package you depend on has changed. Edit the package df file (replace a space with a space), smodel it, and submit the package."];
};
noIERRyet ← FALSE;
};
IF imports.exported
THEN {
IF imports.form # list
THEN {
RETURN[FALSE];
};
FOR i:
NAT
IN [0..imports.list.nEntries)
DO
using: DFUtilities.UsingEntry = imports.list[i];
exportedFilesList ← CONS[[path1: imports.path1, name: using.name, included: TRUE], exportedFilesList];
ENDLOOP;
};
};
comment:
REF DFUtilities.CommentItem => {
};
white:
REF DFUtilities.WhiteSpaceItem => {
};
file:
REF DFUtilities.FileItem => {
shortName: ROPE = Rope.Substr[file.name, 0, Rope.Index[s1: file.name, s2: "!"]];
IF lastDir = NIL THEN ERROR DFUtilities.SyntaxError["no directory item before files"]
ELSE {
IF lastDir.exported THEN exportedFilesList ← CONS[[path1: lastDir.path1, file: file, included: FALSE], exportedFilesList];
};
IF Rope.Equal[remoteCommands, shortName, FALSE] THEN fullNameOfRemoteCommands ← Rope.Concat[exportedFilesList.first.path1, exportedFilesList.first.file.name];
IF Rope.Equal[summonerLoad, shortName, FALSE] THEN fullNameOfSummonerLoad ← Rope.Concat[exportedFilesList.first.path1, exportedFilesList.first.file.name];
};
ENDCASE;
};
checkSummonerRemoteCommands:
PROC
RETURNS [good:
BOOL ←
TRUE] = {
fileName: ROPE = fullNameOfRemoteCommands ;
packageListStream: IO.STREAM;
maintainer: LIST OF ROPE ← NIL;
commands: LIST OF ROPE ← NIL;
version: LIST OF ROPE ← NIL;
count: INT ← 0 ;
IF fileName = NIL THEN {msg ← "no remoteCommands file in DF file"; RETURN[FALSE]};
packageListStream ← FS.StreamOpen[fileName
! FS.Error => GOTO cantOpen];
DO
token: ROPE ← NIL;
tokens, tail: LIST OF ROPE ← NIL;
key: ROPE ← NIL;
token ← ComputeUtils.LocalToken[packageListStream, TRUE];
IF (key ← token) = NIL THEN EXIT;
SELECT ComputeUtils.SkipWhite[packageListStream]
FROM
': => [] ← packageListStream.GetChar[]; -- flush the ':
ENDCASE => {
key was NOT followed by ':, so flush to the end of line and report the error
DO
IF packageListStream.GetChar[ ! IO.EndOfStream => EXIT] = '\n THEN EXIT;
ENDLOOP;
ReportInternal[msg: IO.PutFR["missing : at [%d]", IO.int[position]]];
LOOP;
};
DO
list: LIST OF ROPE ← NIL;
token ← ComputeUtils.LocalToken[packageListStream];
IF token = NIL THEN EXIT;
list ← LIST[token];
IF tail =
NIL
THEN {tail ← tokens ← list}
ELSE {tail.rest ← list; tail ← list};
ENDLOOP;
now the key is key, and the list of tokens is in tokens
SELECT
TRUE
FROM
Rope.Equal[key, "version",
FALSE] => {
IF tail =
NIL
THEN {version ← tokens}
ELSE {tail.rest ← version; version ← tokens};
};
Rope.Equal[key, "maintainer",
FALSE] => {
IF tail =
NIL
THEN {maintainer ← tokens}
ELSE {tail.rest ← maintainer; maintainer ← tokens};
};
Rope.Equal[key, "commands",
FALSE] => {
IF tail =
NIL
THEN {commands ← tokens}
ELSE {tail.rest ← commands; commands ← tokens};
};
Rope.Equal[key, "exclusive",
FALSE] => {
IF tail =
NIL
OR tail # tokens
THEN {
msg ← "exactly one parameter needed for exclusive in .remoteCommands file";
RETURN [FALSE];
}
ELSE {
SELECT ComputeUtils.trueOrFalse[tail.first]
FROM
true => {};
false => {};
ENDCASE => {
msg ← "invalid parameter for exclusive in .remoteCommands file";
RETURN [FALSE];
};
};
};
Rope.Equal[key, "countActive",
FALSE] => {
IF tail =
NIL
OR tail # tokens
THEN {
msg ← "exactly one parameter needed for countActive in .remoteCommands file";
RETURN [FALSE];
}
ELSE {
count : INT;
bad: BOOL ← FALSE;
count ← Convert.IntFromRope[tail.first ! Convert.Error => {bad ← TRUE; CONTINUE}];
IF bad
THEN {
msg ← "bad parameter for countActive in .remoteCommands file";
RETURN [FALSE];
};
IF count <= 0
THEN {
msg ← "count <= 0 for countActive in .remoteCommands file";
RETURN [FALSE];
};
};
};
ENDCASE => {
allWhitespace: BOOL ← FALSE;
tokenKind: IO.TokenKind ← tokenERROR;
[tokenKind: tokenKind] ←
IO.GetCedarTokenRope[
IO.
RIS[key] !
IO.EndOfStream => {
allWhitespace ← TRUE;
CONTINUE;
};
IO.Error => {
CONTINUE;
};
];
IF tokenKind = tokenEOF OR tokenKind = tokenCOMMENT THEN allWhitespace ← TRUE;
IF ~allWhitespace
THEN {
packageListStream.Close[];
msg ← Rope.Concat[key, " is an unknown keyword in the remoteCommands file"];
RETURN [FALSE];
};
};
ENDLOOP; -- for that DO way back up there
IF commands =
NIL
THEN {
packageListStream.Close[];
msg ← "no commands specified in .remoteCommands file";
RETURN [FALSE];
};
packageListStream.Close[];
EXITS
cantOpen => { msg ← "cannot open .remoteCommands file"; RETURN [FALSE];};
};
checkSummonerLoad:
PROC
RETURNS [good:
BOOL ←
TRUE] = {
parseStateType: TYPE = {sawName, sawLeft, sawGFI, sawRight};
packageLoad: ROPE = fullNameOfSummonerLoad ;
packageLoadStream: IO.STREAM;
containedPackage: ROPE ← NIL;
shortBcdName: ROPE ← NIL;
parseState: parseStateType ← sawRight;
count: INT ← 0 ;
IF packageLoad = NIL THEN {msg ← "no summonerLoad file in DF file"; RETURN[FALSE]};
{
packageLoadStream ← FS.StreamOpen[packageLoad ! FS.Error => GOTO cantOpen];
DO
token: ROPE ← NIL;
dfCreated: BasicTime.GMT;
runOK: BOOL ← FALSE;
differentInStd: BOOL ← FALSE;
[token: token] ← packageLoadStream.GetTokenRope[IO.TokenProc
! IO.EndOfStream => EXIT];
IF token.Equal["-"]
AND packageLoadStream.PeekChar[] = '-
THEN {
[] ← packageLoadStream.GetLineRope[ ! IO.EndOfStream => EXIT];
LOOP;
};
SELECT parseState
FROM
sawName, sawRight => {
IF token.Equal["("] THEN { parseState ← sawLeft; LOOP;};
parseState ← sawName;
};
sawLeft => {
[] ← Convert.IntFromRope[token ! Convert.Error => {
msg ← Rope.Cat["Expected ", token, " to be the number of GFI's for", shortBcdName];
GOTO return;
};
];
parseState ← sawGFI;
LOOP;
};
sawGFI => {
IF token.Equal[")"] THEN { parseState ← sawLeft; LOOP;};
msg ← Rope.Cat["Expected ')' after gfi number for", shortBcdName];
GOTO return;
};
ENDCASE;
containedPackage ← token.Substr[0, token.Index[0, ".", FALSE]];
shortBcdName ← containedPackage.Concat[".bcd"];
FOR exports:
LIST
OF exportedFiles ← exportedFilesList, exports.rest
WHILE exports #
NIL
DO
IF (exports.first.included
AND Rope.Equal[exports.first.name, shortBcdName,
FALSE])
OR (~exports.first.included
AND Rope.Equal[exports.first.file.name.Substr[0, Rope.Index[s1: exports.first.file.name, s2: "!"]], shortBcdName,
FALSE])
THEN {
IF exports.first.included THEN EXIT; -- too hard to check imports
[created: dfCreated] ←
FS.FileInfo[name: shortBcdName, remoteCheck:
FALSE, wDir: exports.first.path1
!
FS.Error => {
msg ← Rope.Cat[exports.first.path1, shortBcdName, " missing from server"];
GOTO return;
}
];
EXIT;
};
REPEAT
FINISHED => {
msg ← Rope.Cat["cannot find ", shortBcdName, " in df file"];
RETURN [FALSE];
};
ENDLOOP;
ENDLOOP;
packageLoadStream.Close[ ! FS.Error => CONTINUE;];
EXITS
cantOpen => { msg ← "cannot open .remoteCommands file"; RETURN [FALSE];};
return => {};
};
};
checkImports:
PROC
RETURNS [good:
BOOL ←
TRUE] = {
};
Start of body of SubmitPackage
IF ComputeClientInternal.Enabled # true THEN RETURN[msg: "Client is not enabled"];
argv ← CommandTool.Parse[cmd ! CommandTool.Failed => { msg ← errorMsg; CONTINUE; }];
IF argv = NIL THEN RETURN[$Failure, msg];
IF argv.argc < 2 THEN RETURN[$Failure, "No package specified"];
cleanFName ← FileNames.ResolveRelativePath[argv[1]];
cleanFName ← cleanFName.Substr[0, Rope.Index[s1: cleanFName, s2: "."]];
[fullFName: fullDFFName, cp: fullDFcp] ← FS.ExpandName[cleanFName];
package ← fullDFFName.Substr[fullDFcp.base.start, fullDFcp.base.length];
remoteCommands ← Rope.Concat[package, ".remoteCommands"];
summonerLoad ← Rope.Concat[package, ".summonerLoad"];
fileName ← Rope.Concat[cleanFName, ".df"];
1) Insure .df file has a global attachment
[attachedTo: attachedFile] ←
FS.FileInfo[name: fileName, remoteCheck:
FALSE
!
FS.Error => {
msg ← Rope.Concat["Submitted df file is not attached to a global file since ", error.explanation];
GOTO returnError;
};
];
IF attachedFile =
NIL
THEN {
fullFName: ROPE;
cp: FS.ComponentPositions;
dirOmitted: BOOLEAN;
[fullFName: fullFName, cp: cp, dirOmitted: dirOmitted] ← FS.ExpandName[name: fileName, wDir: NIL];
IF cp.server.length > 0 THEN attachedFile ← fileName
ELSE RETURN[$Failure, "local file submitted, not attached to a global file"];
};
2) Process the .df file, and extract the .remoteCommands and .summonerload files, and build a list of all the exported file.
[created: mainDFFileCreated] ←
FS.FileInfo[name: fileName !
FS.Error => {
msg ← Rope.Concat["Could not get info (FS.FileInfo) about submitted df file since ", error.explanation];
GOTO returnError;
}
];
packageDFStream ←
FS.StreamOpen[fileName: fileName !
FS.Error => {
msg ← Rope.Concat["Submitted df file could not be opened since ", error.explanation];
GOTO returnError;
}
];
DFUtilities.ParseFromStream[packageDFStream, DoOneItemFromDFFile ! DFUtilities.SyntaxError => {
msg ← reason;
packageDFStream.Close[! FS.Error => CONTINUE];
GOTO returnError;
}
];
packageDFStream.Close[! FS.Error => CONTINUE];
IF msg # NIL THEN RETURN[$Failure, msg];
3) open remoteCommands file. Check that it parses OK and is reasonable.
IF ~checkSummonerRemoteCommands[]
THEN
GOTO returnError;
4) open summonerload file. Check packages in summonerload for export in the .df file, and check to see that the files with these create dates exist.
IF ~checkSummonerLoad[]
THEN
GOTO returnError;
5) Take package bcd and generate IMPORTs. Check against list of good imports. Complain if needed. (this should be done on the controller, and be protected, but I trust people) This is a noop for now, however.
IF ~checkImports[] THEN GOTO returnError;
6) Tell the controller about the new package.
tempControllerInterface ← ComputeClientInternal.ControllerInterface;
IF tempControllerInterface =
NIL
THEN {
ComputeClientInternal.TryToImportController[];
RETURN[$Failure, "Compute Server Controller Down - try again later"] ;
};
[error: controllerError, tryDifferentController: tryDifferentController, msg: controllerMsg] ← tempControllerInterface.NoticeNewPackage[attachedFile
!
RPC.CallFailed => {
ComputeClientInternal.ControllerInterface ← NIL;
ComputeClientInternal.TryToImportController[];
msg ← "RPC Call Failed - controller might be down or recovering";
GOTO returnError;
};
];
IF tryDifferentController THEN RETURN[$Failure, "Compute Server Controller Recovering. Package may or may not have been added. - Resubmit package"] ;
IF controllerMsg # NIL THEN RETURN[$Failure, Rope.Concat["Controller rejected package because ", controllerMsg]];
msg ← "New package accepted; please wait about 20 seconds before trying to use it.";
EXITS
returnError => {
RETURN[$Failure, msg]
};
};