MODULE---------------------------------------------------------------------------; IMPORT Text, TextSeq, TextTextTbl, FileRd, Rd, FileWr, Wr, Pathname, OSError, Thread, Fmt, Time, RCS_Date, TextWr, MxConfig; IMPORT TextTextTextTbl, PkgBase, PoolSet, DependencyGraph, FSUtils, StdDepGraphNodeSeq, Tag, TextUtils, PathRepr, RegEx, Checkpoint, ProcessEnv, MsgX, MsgIF, Confirmation, PkgVC; IMPORT SMsg AS Msg; FROM TextReadingUtils IMPORT GetTokenOrString; FROM TextUtils IMPORT MemberOfTextSeq, SubstEnvVars, SubstituteVariables; PrjDesc
REVEAL T = Public BRANDED "PrjDesc Type 0.0" OBJECT collectionRootOrig : TEXT; collectionRoot : TEXT; collectionList : TextSeq.T; collectionTbl : TextTextTbl.T; (* collection names -> path *) packageList : TextSeq.T; ignoredList : TextSeq.T; missingList : TextSeq.T; locationTbl : TextTextTbl.T; (* package names -> path *) packageTbl : TextTextTbl.T; (* package names -> collection names *) snapshotTbl : TextTextTextTbl.T; releaseTbl : TextTextTextTbl.T; poolset : PoolSet.T; pkgKind : PkgBase.Kind; depGraph : DependencyGraph.T; depsMandatory : BOOLEAN; externalShell : TEXT; varTbl : TextTextTbl.T; stateLabelTbl : TextTextTextTbl.T; oldState : Checkpoint.T; changedPkgDirs : TextSeq.T; useCache : BOOLEAN; env : ProcessEnv.T; msgif : MsgIF.T; pkgvcAcc : PoolSet.PkgVCAccessor; locs : TextSeq.T := NIL; locsText : TEXT := NIL; (* attributes *) name : TEXT; desc : TEXT; user : TEXT; cdate : Time.T; mdate : Time.T; METHODS addRoot(p : Pathname.T) : Pathname.T := AddRoot; varTableCopy() : TextTextTbl.T := VarTableCopy; OVERRIDES init := Init; reinit := ReInit; parse := Parse; loadFile := LoadFile; pkgsOkay := PkgsOkay; getPoolSet := GetPoolSet; setExternalShell := SetExternalShell; setPreferredPkgKind := SetPreferredPkgKind; defineGlobalVar := DefineGlobalVar; deleteGlobalVar := DeleteGlobalVar; defineGlobalVars := DefineGlobalVars; collectionPath := CollectionPath; packages := Packages; locations := Locations; packagesCollection := PackagesCollection; collections := Collections; collectionsLocation := CollectionsLocation; snapshots := Snapshots; releases := Releases; snapshot := Snapshot; release := Release; defineSnapshot := DefineSnapshot; defineRelease := DefineRelease; getTags := GetTags; newSnapshot := NewSnapshot; newRelease := NewRelease; checkoutSnapshot := CheckoutSnapshot; checkoutRelease := CheckoutRelease; checkoutHead := CheckoutHead; checkoutTrunkOrBranchHead := CheckoutTrunkOrBranchHead; checkoutPackages := CheckoutPackages; readDepGraph := ReadDepGraph; buildDepGraph := BuildDepGraph; writeDepGraph := WriteDepGraph; readDepGraphAsText := ReadDepGraphAsText; writeDepGraphAsText := WriteDepGraphAsText; write := Write; writeSnapshot := WriteSnapshot; writeRelease := WriteRelease; packageUpdateSequence := PackageUpdateSequence; ignoredPackages := IgnoredPackages; missingPackages := MissingPackages; applyToPackages := ApplyToPackages; selectPackages := SelectPackages; applyCmdListDirectly := ApplyCmdListDirectly; selectByCmdList := SelectByCmdList; dependendPackages := DependendPackages; packageDependencies := PackageDependencies; addDependingPackages := AddDependingPackages; modifiedPackages := ModifiedPackages; modifiedAndDependingPackages := ModifiedAndDependingPackages; upToDatePackages := UpToDatePackages; outOfDatePackages := OutOfDatePackages; outOfDateAndDependingPackages := OutOfDateAndDependingPackages; packagesWithConflicts := PackagesWithConflicts; testAllPackagesReleased := TestAllPackagesReleased; testNoPackageModified := TestNoPackageModified; checkCurrentLabels := CheckCurrentLabels; checkCurrentLabelsGen := CheckCurrentLabelsGen; cacheAllStateLabels := CacheAllStateLabels; checkLabelsOfSnapshot := CheckLabelsOfSnapshot; checkLabelsOfRelease := CheckLabelsOfRelease; newCheckpoint := NewCheckpoint; loadNewCheckpoint := LoadNewCheckpoint; writeCheckpoint := WriteCheckpoint; invalidateCachedUnsureVersionInfo := InvalidateCachedUnsureVersionInfo; invalidateCachedBuildInfo := InvalidateCachedBuildInfo; getName := GetName; getCreationDate := GetCreationDate; getModificationDate := GetModificationDate; getUser := GetUser; getDescription := GetDescription; setName := SetName; setCreationDate := SetCreationDate; setModificationDate := SetModificationDate; setUser := SetUser; setDescription := SetDescription; logText := LogText; snapshotText := SnapshotText; releaseText := ReleaseText; toText := ToText; END;---------------------------------------------------------------------------
PROCEDURE---------------------------------------------------------------------------GetName (self : T) : TEXT = BEGIN RETURN self.name; END GetName;
PROCEDURE---------------------------------------------------------------------------GetCreationDate (self : T) : Time.T = BEGIN RETURN self.cdate; END GetCreationDate;
PROCEDURE---------------------------------------------------------------------------GetModificationDate (self : T) : Time.T = BEGIN RETURN self.mdate; END GetModificationDate;
PROCEDURE---------------------------------------------------------------------------GetUser (self : T) : TEXT = BEGIN RETURN self.user; END GetUser;
PROCEDURE---------------------------------------------------------------------------GetDescription (self : T) : TEXT = BEGIN RETURN self.desc; END GetDescription;
PROCEDURE---------------------------------------------------------------------------SetName (self : T; name : TEXT) = BEGIN self.name := name; END SetName;
PROCEDURE---------------------------------------------------------------------------SetCreationDate (self : T; date : Time.T) = BEGIN self.cdate := date; END SetCreationDate;
PROCEDURE---------------------------------------------------------------------------SetModificationDate (self : T; date : Time.T) = BEGIN self.mdate := date; END SetModificationDate;
PROCEDURE---------------------------------------------------------------------------SetUser (self : T; user : TEXT) = BEGIN self.user := user; END SetUser;
PROCEDURE---------------------------------------------------------------------------SetDescription (self : T; desc : TEXT) = BEGIN self.desc := desc; END SetDescription;
PROCEDURE---------------------------------------------------------------------------LogText (self : T) : TEXT = VAR res : TEXT; pkg : TEXT; ver : TEXT; snap : TextTextTbl.T; BEGIN res := "snapshot/release description " & self.name & NL & "created by " & self.user; IF self.cdate = 0.0d0 THEN res := res & NL & "no creation date available" & NL; ELSE res := res & " at " & RCS_Date.FromTime(self.cdate) & NL; END; IF self.mdate = 0.0d0 THEN res := res & "no modification date available" & NL; ELSE res := res & "last modified at " & RCS_Date.FromTime(self.cdate) & NL; END; res := res & NL & "description:" & NL & self.desc & NL & "package versions:" & NL; IF NOT self.releaseTbl.get(self.name, snap) THEN IF NOT self.snapshotTbl.get(self.name, snap) THEN RETURN res & " !!! not found !!!" & NL; END; END; WITH iter = snap.iterate() DO WHILE iter.next(pkg, ver) DO res := res & " " & pkg & "\t" & ver & NL; END; END; RETURN res; END LogText;
PROCEDURE---------------------------------------------------------------------------AddRoot (self : T; p : Pathname.T) : Pathname.T = VAR pn := PathRepr.Native(p); BEGIN IF Pathname.Absolute(pn) THEN RETURN pn; ELSE RETURN Pathname.Join(self.collectionRoot, pn, NIL); END; END AddRoot;
PROCEDURE---------------------------------------------------------------------------Locations (self : T) : TextSeq.T = VAR loc : TEXT; BEGIN IF self.locs = NIL THEN self.locs := NEW(TextSeq.T).init(self.packageList.size()); FOR i := 0 TO self.packageList.size() - 1 DO WITH pkg = self.packageList.get(i) DO EVAL self.locationTbl.get(pkg, loc); self.locs.addhi(Pathname.Join(loc, pkg, NIL)); END; END; END; RETURN self.locs; END Locations;
PROCEDURE---------------------------------------------------------------------------ParseProjectDescription (self : T; rd : Rd.T) RAISES {Error} = VAR token, name, location : TEXT; tokenValid := FALSE; colNr := 1; alreadyKnown : BOOLEAN; (*-----------------------------------------------------------------------*) PROCEDURE NextToken() : TEXT RAISES {Error} = BEGIN TRY token := GetTokenOrString(rd); EXCEPT Rd.EndOfFile => token := ""; | Rd.Failure => RAISE Error("error reading project description file"); | Thread.Alerted => RAISE Error("interrupted while reading " & "project description file"); END; RETURN token; END NextToken; (*-----------------------------------------------------------------------*) PROCEDURE CheckName(name, where : TEXT) RAISES {Error} = BEGIN IF Text.Length(name) = 0 THEN RAISE Error("empty argument name after " & where); END; END CheckName; (*-----------------------------------------------------------------------*) PROCEDURE CheckName2(name, what : TEXT) RAISES {Error} = BEGIN IF Text.Length(name) = 0 THEN RAISE Error("empty " & what); END; END CheckName2; (*-----------------------------------------------------------------------*) PROCEDURE ParsePkgVersionBlock(what : TEXT) : TextTextTbl.T RAISES {Error} = VAR tbl := NEW(TextTextTbl.Default).init(); name, version, res : TEXT; BEGIN TRY WHILE NOT Rd.EOF(rd) DO name := NextToken(); IF Text.Length(token) > 0 AND Text.GetChar(token, 0) = '#' THEN (* comment till end of line *) EVAL Rd.GetLine(rd); ELSIF Text.Equal(token, "end") THEN RETURN tbl; (* the one correct exit *) ELSE CheckName2(name, "package name in " & what & " block"); version := NextToken(); CheckName2(version, "version in " & what & " block"); IF tbl.get(name, res) THEN RAISE Error("multiple entry for package " & name & " in " & what); ELSE EVAL tbl.put(name, version); END; END; END; EXCEPT Rd.Failure => RAISE Error("error reading project description file"); | Rd.EndOfFile => RAISE Error("premature eof in project description" & " file"); | Thread.Alerted => RAISE Error("interrupted while reading " & "project description file"); END; RAISE Error("syntax error: missing `end' after " & what & " block"); END ParsePkgVersionBlock; PROCEDURE ReadDesc() RAISES {Rd.Failure, Thread.Alerted, Rd.EndOfFile} = VAR lines := NEW(TextSeq.T).init(); cont := TRUE; line : TEXT; BEGIN EVAL Rd.GetLine(rd); WHILE NOT Rd.EOF(rd) AND cont DO line := Rd.GetLine(rd); IF Text.Equal(line, "end") THEN cont := FALSE; ELSE lines.addhi(line); END; END; self.desc := TextUtils.TextSeqToText(lines, sep := NL); END ReadDesc; BEGIN (* ParseProjectDescription *) TRY WHILE NOT Rd.EOF(rd) DO IF tokenValid THEN tokenValid := FALSE; ELSE EVAL NextToken(); END; IF Text.Length(token) > 0 THEN IF Text.GetChar(token, 0) = '#' THEN (* comment till end of line *) EVAL Rd.GetLine(rd); ELSIF Text.Equal(token, "collection") THEN name := PathRepr.Native(NextToken()); CheckName(name, "collection"); EVAL NextToken(); IF Text.Equal(token, "at") OR Text.Equal(token, "=") THEN EVAL NextToken(); location := PathRepr.Native(token); ELSE location := PathRepr.Native(name); name := "collection_" & Fmt.Int(colNr); INC(colNr); tokenValid := TRUE; END; alreadyKnown := MemberOfTextSeq(self.collectionList, name); IF NOT alreadyKnown THEN self.collectionList.addhi(name); EVAL self.collectionTbl.put(name, location); END; ELSIF Text.Equal(token, "package") THEN name := NextToken(); CheckName(name, "package"); alreadyKnown := MemberOfTextSeq(self.packageList, name); IF NOT alreadyKnown THEN self.packageList.addhi(name); END; EVAL NextToken(); IF Text.Equal(token, "in") OR Text.Equal(token, "from") THEN EVAL NextToken(); IF NOT alreadyKnown THEN EVAL self.packageTbl.put(name, token); IF self.collectionTbl.get(token, location) THEN EVAL self.locationTbl.put(name, self.addRoot(location)); ELSE RAISE Error("collection " & token & " not found"); END; END; ELSE tokenValid := TRUE; END; ELSIF Text.Equal(token, "collectionroot") OR Text.Equal(token, "collectionpath") OR Text.Equal(token, "root") OR Text.Equal(token, "prefix") THEN name := token; location := PathRepr.Native(NextToken()); CheckName(location, name); self.collectionRootOrig := location; IF NOT ProcessEnv.Defined(self.env, "HOME") THEN ProcessEnv.Set(self.env, "HOME", PathRepr.Native("/")); END; IF NOT ProcessEnv.Defined(self.env, "USER") THEN ProcessEnv.Set(self.env, "USER", "nobody"); END; IF ProcessEnv.Defined(self.env, "PRJ_ROOT") THEN ProcessEnv.Set( self.env, "PRJ_ROOT", PathRepr.Native( ProcessEnv.Value(self.env, "PRJ_ROOT"))); ELSE IF ProcessEnv.Defined(self.env, "collectionroot") THEN ProcessEnv.Set( self.env, "PRJ_ROOT", PathRepr.Native( ProcessEnv.Value(self.env, "collectionroot"))); ELSIF ProcessEnv.Defined(self.env, "COLLECTIONROOT") THEN ProcessEnv.Set( self.env, "PRJ_ROOT", PathRepr.Native( ProcessEnv.Value(self.env, "COLLECTIONROOT"))); ELSE ProcessEnv.Set(self.env, "PRJ_ROOT", "undefined_PRJ_ROOT"); END; END; WITH prjRoot = ProcessEnv.Value(self.env, "PRJ_ROOT") DO IF Text.Equal(prjRoot, Pathname.Current) OR Text.Equal(prjRoot, FSUtils.CanonicalPathname(Pathname.Current)) THEN MsgX.Warning(self.msgif, "project root dirctory (PRJ_ROOT) is set to " & Pathname.Current); ELSIF NOT FSUtils.IsDir(prjRoot) THEN MsgX.Fatal(self.msgif, "project root dirctory (PRJ_ROOT) " & prjRoot & " does not xist"); END; END; self.collectionRoot := SubstEnvVars(location, self.env); self.collectionRoot := SubstituteVariables(self.collectionRoot, self.env); self.collectionRoot := FSUtils.CanonicalPathname(self.collectionRoot); ELSIF Text.Equal(token, "snapshot") THEN name := NextToken(); CheckName(name, "snapshot"); IF Text.Empty(self.name) THEN self.name := name END; EVAL self.snapshotTbl.put(name, ParsePkgVersionBlock("snapshot")); ELSIF Text.Equal(token, "release") THEN name := NextToken(); CheckName(name, "release"); IF Text.Empty(self.name) THEN self.name := name END; EVAL self.releaseTbl.put(name, ParsePkgVersionBlock("release")); ELSIF Text.Equal(token, "name") THEN self.name := NextToken(); ELSIF Text.Equal(token, "user") OR Text.Equal(token, "creator") THEN self.user := NextToken(); ELSIF Text.Equal(token, "mtime") THEN token := NextToken(); self.mdate := RCS_Date.ToTimeApprox(token); ELSIF Text.Equal(token, "ctime") THEN token := NextToken(); self.cdate := RCS_Date.ToTimeApprox(token); ELSIF Text.Equal(token, "description") THEN ReadDesc(); ELSE RAISE Error("syntax error at token " & token); END; END; END; EXCEPT Rd.Failure => RAISE Error("error reading project description file"); | Rd.EndOfFile => RAISE Error("premature eof in project description" & " file"); | Thread.Alerted => RAISE Error("interrupted while reading " & "project description file"); | TextUtils.Error(e) => RAISE Error("undefined variable in collectionroot " & e); | FSUtils.E(e) => RAISE Error(e); END; END ParseProjectDescription;
PROCEDURE---------------------------------------------------------------------------Init (self : T; fn : TEXT; cfg : PkgBase.T; check := FALSE; defaultCollectionRoot : TEXT := NIL; cpfn : TEXT; useCache := TRUE; env : ProcessEnv.T := NIL; msgif : MsgIF.T := NIL; pkgvcAcc : PoolSet.PkgVCAccessor := NIL; verboseCacheMsgs := TRUE; preferredPkgKind : TEXT := NIL; depsMandatory := TRUE; cacheEarly := FALSE) : T RAISES {Error} = VAR rd : FileRd.T; res : TEXT; BEGIN self.name := ""; self.desc := ""; self.user := "unknown"; self.cdate := 0.0d0; self.mdate := 0.0d0; self.msgif := msgif; self.pkgvcAcc := pkgvcAcc; self.depsMandatory := depsMandatory; self.locs := NIL; self.locsText := NIL; IF env = NIL THEN self.env := ProcessEnv.Current(); ELSE self.env := env; END; self.depGraph := NIL; self.useCache := useCache; TRY IF defaultCollectionRoot = NIL THEN WITH cur = FSUtils.CanonicalPathname(Pathname.Current) DO self.collectionRoot := cur; self.collectionRootOrig := cur; END; ELSE WITH defroot = FSUtils.CanonicalPathname(defaultCollectionRoot) DO self.collectionRoot := defroot; self.collectionRootOrig := defroot; END; END; EXCEPT FSUtils.E(e) => RAISE Error(e); END; self.collectionList := NEW(TextSeq.T).init(5); self.collectionTbl := NEW(TextTextTbl.Default).init(5); self.packageList := NEW(TextSeq.T).init(40); self.locationTbl := NEW(TextTextTbl.Default).init(40); self.packageTbl := NEW(TextTextTbl.Default).init(40); self.snapshotTbl := NEW(TextTextTextTbl.Default).init(40); self.releaseTbl := NEW(TextTextTextTbl.Default).init(40); TRY self.poolset := NEW(PoolSet.T).init(cfg, cpfn, useCache, msgif := self.msgif, pkgvcAcc := self.pkgvcAcc, verboseCache := verboseCacheMsgs, prefkind := preferredPkgKind, cacheEarly := cacheEarly); EXCEPT PoolSet.Error(e) => RAISE Error(e); END; self.ignoredList := NIL; self.missingList := NIL; self.externalShell := NIL; self.varTbl := NEW(TextTextTbl.Default).init(10); self.stateLabelTbl := NEW(TextTextTextTbl.Default).init(40); IF cpfn = NIL OR NOT useCache OR NOT FSUtils.IsFile(cpfn) THEN self.oldState := NIL; ELSE self.oldState := Checkpoint.New(self.poolset.getFileCache(), self.msgif); TRY self.oldState.fromFile(cpfn); IF debugStateCache AND Msg.dFlag THEN self.poolset.dumpStateCache("PrjDesc.Init(): " & "current state:"); MsgX.D(self.msgif, "old state:", level := 2); MsgX.D(self.msgif, self.oldState.toText(), level := 2); MsgX.D(self.msgif, "(current state and old state should be exactly the same)", level := 2); END; EXCEPT Checkpoint.Error(e) => RAISE Error("error reading checkpoint: " & e); END; END; (* if there is no file to parse, we quit the initialization here *) IF fn = NIL THEN RETURN self END; TRY rd := FileRd.Open(fn); EXCEPT OSError.E => RAISE Error("cannot open file " & fn); END; ParseProjectDescription(self, rd); TRY Rd.Close(rd); EXCEPT ELSE END; (* fn parsed without errors *) FOR i := 0 TO self.collectionList.size() - 1 DO VAR loc : TEXT; BEGIN WITH col = self.collectionList.get(i) DO IF self.collectionTbl.get(col, loc) THEN TRY self.poolset.appendPool(self.addRoot(loc)); EXCEPT PoolSet.Error(e) => RAISE Error(e); END; ELSE RAISE Error("internal error: collection " & col & " not in table") END; END; END; END; (* all collections of packages added as pools to be searched *) IF check OR self.useCache THEN IF NOT self.poolset.checkAll(self.packageList, res, self.missingList, self.locationTbl, checkHomogeneity := FALSE, ignoreMissingPackages := TRUE) THEN IF check THEN RAISE Error(res); END; END; END; (* all packages exist and are of the same type *) IF self.packageList.size() > 0 THEN self.pkgKind := self.poolset.pkgType(self.packageList.get(0)); ELSE self.pkgKind := NIL; END; (* some state cache tests *) (* VAR sc1 := Checkpoint.New(self.poolset.getFileCache(), self.msgif); sc2 : Checkpoint.T; BEGIN sc1.update("/usr/tmp"); MsgX.T(self.msgif, "sc1 1:"); MsgX.T(self.msgif, sc1.toText()); sc1.update("/usr/tmp"); MsgX.T(self.msgif, "sc1 w:"); MsgX.T(self.msgif, sc1.toText()); sc2 := Checkpoint.New(self.poolset.getFileCache(), self.msgif); sc2.update("/usr/tmp"); MsgX.T(self.msgif, "sc2 1:"); MsgX.T(self.msgif, sc2.toText()); sc2.update("/usr/tmp"); MsgX.T(self.msgif, "sc2 w:"); MsgX.T(self.msgif, sc2.toText()); END; *) (* end state cache tests *) IF self.oldState = NIL THEN self.oldState := self.poolset.cachedState(); END; EvaluateStateCacheChanges(self, self.oldState); (* -- this cannot be done properly until the dependency graph is built *) RETURN self; END Init;
PROCEDURE---------------------------------------------------------------------------ReInit (self : T; fn : TEXT; cfg : PkgBase.T; check := FALSE; defaultCollectionRoot : TEXT := NIL; cpfn : TEXT; useCache := TRUE; env : ProcessEnv.T := NIL; msgif : MsgIF.T := NIL; pkgvcAcc : PoolSet.PkgVCAccessor := NIL) RAISES {Error} = VAR rd : FileRd.T; res : TEXT; BEGIN self.msgif := msgif; self.pkgvcAcc := pkgvcAcc; IF env = NIL THEN self.env := ProcessEnv.Current(); ELSE self.env := env; END; self.depGraph := NIL; self.useCache := useCache; self.locs := NIL; self.locsText := NIL; TRY IF defaultCollectionRoot = NIL THEN WITH cur = FSUtils.CanonicalPathname(Pathname.Current) DO self.collectionRoot := cur; self.collectionRootOrig := cur; END; ELSE WITH defroot = FSUtils.CanonicalPathname(defaultCollectionRoot) DO self.collectionRoot := defroot; self.collectionRootOrig := defroot; END; END; EXCEPT FSUtils.E(e) => RAISE Error(e); END; EVAL self.collectionList.init(5); EVAL NARROW(self.collectionTbl, TextTextTbl.Default).init(5); EVAL self.packageList.init(40); EVAL NARROW(self.locationTbl, TextTextTbl.Default).init(40); EVAL NARROW(self.packageTbl, TextTextTbl.Default).init(40); EVAL NARROW(self.snapshotTbl, TextTextTextTbl.Default).init(40); EVAL NARROW(self.releaseTbl, TextTextTextTbl.Default).init(40); TRY EVAL self.poolset.init(cfg, cpfn, useCache, msgif := self.msgif, pkgvcAcc := self.pkgvcAcc); EXCEPT PoolSet.Error(e) => RAISE Error(e); END; self.ignoredList := NIL; self.missingList := NIL; self.externalShell := NIL; EVAL NARROW(self.varTbl, TextTextTbl.Default).init(10); EVAL NARROW(self.stateLabelTbl, TextTextTextTbl.Default).init(40); IF cpfn = NIL OR NOT FSUtils.IsFile(cpfn) THEN self.oldState := NIL; ELSE self.oldState := Checkpoint.New(self.poolset.getFileCache(), self.msgif); TRY self.oldState.fromFile(cpfn); IF debugStateCache AND Msg.dFlag THEN self.poolset.dumpStateCache("PrjDesc.Init(): " & "current state:"); MsgX.D(self.msgif, "old state:", level := 2); MsgX.D(self.msgif, self.oldState.toText(), level := 2); MsgX.D(self.msgif, "(current state and old state should be exactly the same)", level := 2); END; EXCEPT Checkpoint.Error(e) => RAISE Error("error reading checkpoint: " & e); END; END; TRY rd := FileRd.Open(fn); EXCEPT OSError.E => RAISE Error("cannot open file " & fn); END; ParseProjectDescription(self, rd); TRY Rd.Close(rd); EXCEPT ELSE END; (* fn parsed without errors *) FOR i := 0 TO self.collectionList.size() - 1 DO VAR loc : TEXT; BEGIN WITH col = self.collectionList.get(i) DO IF self.collectionTbl.get(col, loc) THEN TRY self.poolset.appendPool(self.addRoot(loc)); EXCEPT PoolSet.Error(e) => RAISE Error(e); END; ELSE RAISE Error("internal error: collection " & col & " not in table") END; END; END; END; (* all collections of packages added as pools to be searched *) IF check OR self.useCache THEN IF NOT self.poolset.checkAll(self.packageList, res, self.missingList, self.locationTbl, checkHomogeneity := FALSE, ignoreMissingPackages := TRUE) THEN IF check THEN RAISE Error(res); END; END; END; (* all packages exist and are of the same type *) IF self.packageList.size() > 0 THEN self.pkgKind := self.poolset.pkgType(self.packageList.get(0)); ELSE self.pkgKind := NIL; END; IF self.oldState = NIL THEN self.oldState := self.poolset.cachedState(); END; EvaluateStateCacheChanges(self, self.oldState); (* -- this cannot be done properly until the dependency graph is built *) END ReInit;
PROCEDURE---------------------------------------------------------------------------EvaluateStateCacheChanges (self : T; oldState : Checkpoint.T) RAISES {Error} = VAR pkgdirs : TextSeq.T; BEGIN IF NOT self.useCache THEN RETURN END; MsgX.T(self.msgif, "evaluating state cache changes..."); IF debugStateCache THEN self.poolset.dumpStateCache("EvaluateStateCacheChanges(): " & "current state:"); MsgX.T(self.msgif, "old state:"); TRY MsgX.T(self.msgif, self.oldState.toText()); EXCEPT ELSE END; END; self.changedPkgDirs := self.poolset.cachedState().diff(oldState); (* `changedPkgDirs' contains a list of directories whose fingerprint is different from that recorded in the last checkpoint (oldState). Since we cannot know from the changed fingerprint if anything relevant has changed, we need to reset certain cache atributes. For all packages with changed fingerprint the dependencies need to be recomputed, they need to be rebuilt and re-checked for their modification status. *) pkgdirs := self.changedPkgDirs; FOR i := 0 TO pkgdirs.size() - 1 DO WITH dir = pkgdirs.get(i) DO TRY self.poolset.updateStateCache(dir, "need-mkdep-build-ship", 0); self.poolset.updateStateCache(dir, "clear-mod-unmod", 0); EXCEPT PoolSet.Error(e) => RAISE Error(e); END; END; END; IF debugStateCache THEN self.poolset.dumpStateCache("EvaluateStateCacheChanges(end): " & "current state:"); END; (* self.changedPkgDirs is now defined *) END EvaluateStateCacheChanges;
PROCEDURE---------------------------------------------------------------------------EvaluateStateCacheChangesForDependendPackages ( self : T; pkgs : TextSeq.T := NIL) RAISES {Error} = VAR pkgdirs : TextSeq.T; dir : TEXT; BEGIN IF NOT self.useCache THEN RETURN END; MsgX.T(self.msgif, "readjusting state cache..."); IF debugStateCache THEN self.poolset.dumpStateCache( "EvaluateStateCacheChangesForDependendPackages(): " & "current state:"); END; <* ASSERT self.depGraph # NIL *> <* ASSERT self.changedPkgDirs # NIL *> (* If packages have changed, they need to be rebuilt and reshipped. Consequently, all packages depending on all changed packages need to be rebuilt and reshipped, too. *) IF pkgs = NIL THEN pkgs := NEW(TextSeq.T).init(); FOR i := 0 TO self.changedPkgDirs.size() - 1 DO WITH pkg = Pathname.Last(self.changedPkgDirs.get(i)) DO pkgs.addhi(pkg); END; END; END; pkgs := AddDependingPackages(self, pkgs); pkgdirs := NEW(TextSeq.T).init(pkgs.size()); FOR i := 0 TO pkgs.size() - 1 DO WITH pkg = pkgs.get(i) DO IF self.locationTbl.get(pkg, dir) THEN pkgdirs.addhi(Pathname.Join(dir, pkg, NIL)); ELSE RAISE Error("no location known for package " & pkg); END; END; END; (* depGraph # NIL AND pkgdirs = dirs(changed + dependend pkgs) *) FOR i := 0 TO pkgdirs.size() - 1 DO WITH dir = pkgdirs.get(i) DO TRY self.poolset.updateStateCache(dir, "need-build-ship", 0); EXCEPT PoolSet.Error(e) => RAISE Error(e); END; END; END; IF debugStateCache THEN self.poolset.dumpStateCache( "EvaluateStateCacheChangesForDependendPackages(end): " & "current state"); END; END EvaluateStateCacheChangesForDependendPackages;
PROCEDURE---------------------------------------------------------------------------Parse (self : T; rd : Rd.T) RAISES {Error} = BEGIN self.locs := NIL; self.locsText := NIL; ParseProjectDescription(self, rd); IF self.useCache THEN self.changedPkgDirs := self.poolset.cachedState().diff(self.oldState); END; END Parse;
PROCEDURE---------------------------------------------------------------------------LoadFile (self : T; fn : TEXT) RAISES {Error} = VAR rd : FileRd.T; BEGIN TRY rd := FileRd.Open(fn); EXCEPT OSError.E => RAISE Error("cannot open file " & fn); END; Parse(self, rd); TRY Rd.Close(rd); EXCEPT ELSE END; END LoadFile;
PROCEDURE---------------------------------------------------------------------------PkgsOkay (self : T; VAR res : TEXT) : BOOLEAN = BEGIN RETURN self.poolset.checkAll(self.packageList, res, self.missingList, self.locationTbl, FALSE); END PkgsOkay;
PROCEDURE---------------------------------------------------------------------------GetPoolSet (self : T) : PoolSet.T = BEGIN RETURN self.poolset; END GetPoolSet;
PROCEDURE---------------------------------------------------------------------------SetExternalShell (self : T; shell : TEXT) = BEGIN self.externalShell := shell; END SetExternalShell;
PROCEDURE---------------------------------------------------------------------------SetPreferredPkgKind (self : T; k : PkgBase.Kind) = BEGIN self.poolset.setPreferredPkgKind(k); END SetPreferredPkgKind;
PROCEDURE---------------------------------------------------------------------------DefineGlobalVar (self : T; name, val : TEXT) = BEGIN EVAL self.varTbl.put(name, val); END DefineGlobalVar;
PROCEDURE---------------------------------------------------------------------------DeleteGlobalVar (self : T; name : TEXT) = VAR value : TEXT; BEGIN EVAL self.varTbl.delete(name, value); END DeleteGlobalVar;
PROCEDURE---------------------------------------------------------------------------DefineGlobalVars (self : T; vars : TextTextTbl.T) = VAR name, val : TEXT; iter := vars.iterate(); BEGIN WHILE iter.next(name, val) DO EVAL self.varTbl.put(name, val); END; END DefineGlobalVars;
PROCEDURE---------------------------------------------------------------------------VarTableCopy (self : T) : TextTextTbl.T = VAR n, v : TEXT; res := NEW(TextTextTbl.Default).init(self.varTbl.size() + 10); iter := self.varTbl.iterate(); BEGIN WHILE iter.next(n, v) DO EVAL res.put(n, v); END; RETURN res; END VarTableCopy;
PROCEDURE---------------------------------------------------------------------------CollectionPath (self : T) : TEXT = BEGIN RETURN self.collectionRoot; END CollectionPath;
PROCEDURE---------------------------------------------------------------------------Collections (self : T) : TextSeq.T = BEGIN RETURN self.collectionList; END Collections;
PROCEDURE---------------------------------------------------------------------------CollectionsLocation (self : T) : TextTextTbl.T = BEGIN RETURN self.collectionTbl; END CollectionsLocation;
PROCEDURE---------------------------------------------------------------------------Packages (self : T) : TextSeq.T = BEGIN RETURN self.packageList; END Packages;
PROCEDURE---------------------------------------------------------------------------PackagesCollection (self : T) : TextTextTbl.T = BEGIN RETURN self.packageTbl; END PackagesCollection;
PROCEDURE---------------------------------------------------------------------------Snapshots (self : T) : TextSeq.T = VAR iter := self.snapshotTbl.iterate(); res := NEW(TextSeq.T).init(self.snapshotTbl.size()); name : TEXT; tbl : TextTextTbl.T; BEGIN WHILE iter.next(name, tbl) DO res.addhi(name); END; RETURN res; END Snapshots;
PROCEDURE---------------------------------------------------------------------------Releases (self : T) : TextSeq.T = VAR iter := self.releaseTbl.iterate(); res := NEW(TextSeq.T).init(self.releaseTbl.size()); name : TEXT; tbl : TextTextTbl.T; BEGIN WHILE iter.next(name, tbl) DO res.addhi(name); END; RETURN res; END Releases;
PROCEDURE---------------------------------------------------------------------------MakeHead (self : T) : TextTextTbl.T = VAR head := NEW(TextTextTbl.Default).init(self.packageList.size()); BEGIN FOR i := 0 TO self.packageList.size() - 1 DO WITH pkg = self.packageList.get(i) DO EVAL head.put(pkg, "head"); END; END; RETURN head; END MakeHead;
PROCEDURE---------------------------------------------------------------------------Snapshot (self : T; name : TEXT) : TextTextTbl.T = VAR tbl : TextTextTbl.T; BEGIN IF Text.Equal(name, "head") THEN RETURN MakeHead(self); ELSIF self.snapshotTbl.get(name, tbl) THEN RETURN tbl; ELSE RETURN NIL; END; END Snapshot;
PROCEDURE---------------------------------------------------------------------------Release (self : T; name : TEXT) : TextTextTbl.T = VAR tbl : TextTextTbl.T; BEGIN IF self.releaseTbl.get(name, tbl) THEN RETURN tbl; ELSE RETURN NIL; END; END Release;
PROCEDURE---------------------------------------------------------------------------CurrentTagList (self : T) : TextTextTbl.T RAISES {Error} = VAR pkg, fn : TEXT; tagText : TEXT; tag := NEW(Tag.T); snap : TextTextTbl.T; BEGIN snap := NEW(TextTextTbl.Default).init(self.packageList.size()); TRY FOR i := 0 TO self.packageList.size() - 1 DO pkg := self.packageList.get(i); IF Msg.vFlag THEN MsgX.V(self.msgif, "--- looking for current version of package " & pkg & " ---"); END; IF self.poolset.execAction(pkg, "currenttag", tagText, self.externalShell, self.varTbl) = 0 THEN fn := "PkgCT"; ELSE RAISE Error("cannot get current tag for package " & pkg); END; IF tagText = NIL THEN tagText := TextUtils.Compress(self.poolset.fileContents(pkg, fn)); self.poolset.setVal(pkg, "current-tag", tagText); END; EVAL tag.initFromText(tagText); IF NOT tag.okay() THEN RAISE Error("tag `" & tagText & "' for package " & pkg & " seems to be incorrect"); END; EVAL snap.put(pkg, tag.denotation()); END; EXCEPT Error(e) => RAISE Error(e); | PoolSet.Error(e) => RAISE Error(e); END; RETURN snap; END CurrentTagList;
PROCEDURE---------------------------------------------------------------------------DefineSnapshot (self : T; name : TEXT; snap : TextTextTbl.T) RAISES {Error} = VAR dummy : TextTextTbl.T; BEGIN IF self.snapshotTbl.get(name, dummy) THEN RAISE Error("snapshot " & name & " already exists"); END; EVAL self.snapshotTbl.put(name, snap); (* cannot exist already *) END DefineSnapshot;
PROCEDURE---------------------------------------------------------------------------DefineRelease (self : T; name : TEXT; snap : TextTextTbl.T) RAISES {Error} = VAR dummy : TextTextTbl.T; BEGIN IF self.releaseTbl.get(name, dummy) THEN RAISE Error("release " & name & " already exists"); END; EVAL self.releaseTbl.put(name, snap); (* cannot exist already *) END DefineRelease;
PROCEDURE---------------------------------------------------------------------------GetTags (self : T; packageList : TextSeq.T) : TextTextTbl.T RAISES {Error} = VAR pkg, fn : TEXT; tagText : TEXT; tag := NEW(Tag.T); snap : TextTextTbl.T; BEGIN snap := NEW(TextTextTbl.Default).init(packageList.size()); TRY FOR i := 0 TO packageList.size() - 1 DO pkg := packageList.get(i); IF Msg.vFlag THEN MsgX.V(self.msgif, "--- package " & pkg & " ---"); END; IF self.poolset.execAction(pkg, "isrelease", tagText, self.externalShell, self.varTbl) = 0 THEN EVAL self.poolset.execAction(pkg, "currentreleasetag", tagText, self.externalShell, self.varTbl); fn := "PkgCRT"; ELSIF self.poolset.execAction(pkg, "currentdeveltag", tagText, self.externalShell, self.varTbl) = 0 THEN fn := "PkgCDT"; ELSE RAISE Error("cannot get tag for package " & pkg); END; IF tagText = NIL THEN tagText := TextUtils.Compress(self.poolset.fileContents(pkg, fn)); IF Text.Equal(fn, "PkgCRT") THEN self.poolset.setVal(pkg, "current-release-tag", tagText); ELSE self.poolset.setVal(pkg, "current-devel-tag", tagText); END; END; EVAL tag.initFromText(tagText); IF NOT tag.okay() THEN RAISE Error("tag `" & tagText & "' for package " & pkg & " seems to be incorrect"); END; EVAL snap.put(pkg, tag.denotation()); END; EXCEPT Error(e) => RAISE Error(e); | PoolSet.Error(e) => RAISE Error(e); END; RETURN snap; END GetTags;
PROCEDURE---------------------------------------------------------------------------NewSnapshot (self : T; name : TEXT) RAISES {Error} = VAR pkg, fn : TEXT; tagText : TEXT; dummy : TEXT; tag := NEW(Tag.T); snap : TextTextTbl.T; BEGIN IF self.snapshotTbl.get(name, snap) THEN RAISE Error("snapshot " & name & " already exists"); END; snap := NEW(TextTextTbl.Default).init(self.packageList.size()); TRY FOR i := 0 TO self.packageList.size() - 1 DO pkg := self.packageList.get(i); IF Msg.vFlag THEN MsgX.V(self.msgif, "--- package " & pkg & " ---"); END; IF self.poolset.execAction(pkg, "modified", dummy, self.externalShell, self.varTbl) = 0 THEN RAISE Error("package " & pkg & " is locally modified. " & "Please commit your changes first."); ELSIF self.poolset.execAction(pkg, "isrelease", tagText, self.externalShell, self.varTbl) = 0 THEN EVAL self.poolset.execAction(pkg, "currentreleasetag", tagText, self.externalShell, self.varTbl); fn := "PkgCRT"; ELSIF self.poolset.execAction(pkg, "currentdeveltag", tagText, self.externalShell, self.varTbl) = 0 THEN fn := "PkgCDT"; ELSE RAISE Error("cannot get tag for package " & pkg); END; IF tagText = NIL THEN tagText := TextUtils.Compress(self.poolset.fileContents(pkg, fn)); IF Text.Equal(fn, "PkgCRT") THEN self.poolset.setVal(pkg, "current-release-tag", tagText); ELSE self.poolset.setVal(pkg, "current-devel-tag", tagText); END; END; EVAL tag.initFromText(tagText); IF NOT tag.okay() THEN RAISE Error("tag `" & tagText & "' for package " & pkg & " seems to be incorrect"); END; EVAL snap.put(pkg, tag.denotation()); END; EXCEPT Error(e) => RAISE Error(e); | PoolSet.Error(e) => RAISE Error(e); END; EVAL self.snapshotTbl.put(name, snap); (* cannot exist already *) END NewSnapshot;
PROCEDURE---------------------------------------------------------------------------NewRelease (self : T; name : TEXT) RAISES {Error} = VAR pkg, fn : TEXT; tagText : TEXT; dummy : TEXT; tag := NEW(Tag.T); rel : TextTextTbl.T; BEGIN IF self.releaseTbl.get(name, rel) THEN RAISE Error("release " & name & " already exists"); END; rel := NEW(TextTextTbl.Default).init(self.packageList.size()); TRY FOR i := 0 TO self.packageList.size() - 1 DO pkg := self.packageList.get(i); IF Msg.vFlag THEN MsgX.V(self.msgif, "--- package " & pkg & " ---"); END; IF self.poolset.execAction(pkg, "modified", dummy, self.externalShell, self.varTbl) = 0 THEN RAISE Error("package " & pkg & " is locally modified. " & "Please commit your changes first."); ELSIF self.poolset.execAction(pkg, "isrelease", tagText, self.externalShell, self.varTbl) = 0 THEN EVAL self.poolset.execAction(pkg, "currentreleasetag", tagText, self.externalShell, self.varTbl); fn := "PkgCRT"; ELSE RAISE Error("cannot get release tag for package " & pkg); END; IF tagText = NIL THEN tagText := TextUtils.Compress(self.poolset.fileContents(pkg, fn)); IF Text.Equal(fn, "PkgCRT") THEN self.poolset.setVal(pkg, "current-release-tag", tagText); ELSE self.poolset.setVal(pkg, "current-devel-tag", tagText); END; END; EVAL tag.initFromText(tagText); IF NOT tag.okay() THEN RAISE Error("tag `" & tagText & "' for package " & pkg & " seems to be incorrect"); END; EVAL rel.put(pkg, tag.denotation()); END; EXCEPT Error(e) => RAISE Error(e); | PoolSet.Error(e) => RAISE Error(e); END; EVAL self.releaseTbl.put(name, rel); (* cannot exist already *) END NewRelease;
PROCEDURE---------------------------------------------------------------------------Checkout (self : T; name : TEXT; tab : TextTextTbl.T; heedModifiedPackages := TRUE) RAISES {Error} = PROCEDURE GetPackage(pkg, tag : TEXT) RAISES {Error} = BEGIN IF Msg.tFlag THEN MsgX.T(self.msgif, "--- get package " & pkg & " with tag " & tag & " ---"); END; IF NOT self.locationTbl.get(pkg, loc) THEN RAISE Error("package " & pkg & " not found"); END; TRY exists := self.poolset.exists(pkg, loc); EXCEPT PoolSet.Error(e) => RAISE Error(e); END; IF exists THEN (* package is already checked out, just update it *) TRY IF self.poolset.execAction(pkg, "modified", dummy, self.externalShell, self.varTbl) = 0 THEN IF heedModifiedPackages THEN WITH msg = "package " & pkg & " is locally modified. " & "Continue" DO IF NOT PkgVC.confirmation.okay(msg) THEN RAISE Error("package " & pkg & " is locally modified. " & "Please commit your changes first."); END; END; ELSE MsgX.Warning(self.msgif, "package " & pkg & " is locally modified."); END END EXCEPT PoolSet.Error(e) => RAISE Error(e); | Error(e) => RAISE Error(e); END; par := self.varTableCopy(); EVAL par.put("TAG", tag); TRY IF self.poolset.execAction(pkg, "update", dummy, self.externalShell, par) # 0 THEN IF dummy = NIL THEN dummy := "(no further explanation)"; END; RAISE Error("update of package " & pkg & " failed:" & NL & dummy); END; EXCEPT PoolSet.Error(e) => RAISE Error(e); | Error(e) => RAISE Error(e); END; ELSE (* package must be checked out *) IF NOT self.packageTbl.get(pkg, col) THEN RAISE Error("collection for package " & pkg & " not found"); END; IF NOT self.collectionTbl.get(col, loc) THEN RAISE Error("location of collection " & col & " not found"); END; par := self.varTableCopy(); EVAL par.put("TAG", tag); EVAL par.put("LOCATION", loc); EVAL par.put("PKG", pkg); TRY IF self.poolset.checkout(pkg, "checkout", self.externalShell, self.collectionRoot, par) # 0 THEN RAISE Error("checkout of package " & pkg & " failed"); END; (* remove the package from the list of missing packages, assuming it is at most listed once... *) IF self.missingList # NIL AND self.missingList.size() > 0 THEN IF Text.Equal(self.missingList.getlo(), pkg) THEN EVAL self.missingList.remlo(); ELSIF Text.Equal(self.missingList.gethi(), pkg) THEN EVAL self.missingList.remhi(); ELSE FOR i := 1 TO self.missingList.size() - 2 DO WITH act = self.missingList.get(i) DO IF Text.Equal(act, pkg) THEN self.missingList.put(i, self.missingList.remhi()); EXIT; END; END; END; END; END; EXCEPT PoolSet.Error(e) => RAISE Error(e); | Error(e) => RAISE Error(e); END; END; END GetPackage; VAR iter : TextTextTbl.Iterator; pkg, tag, col, loc, dummy : TEXT; exists : BOOLEAN; par : TextTextTbl.T; BEGIN IF tab = NIL THEN (* If we haven't got a configuration (mapping from packages to versions), we try to get the packages using the given name. *) IF self = NIL OR self.packageList = NIL THEN RAISE Error("undefined project"); END; FOR i := 0 TO self.packageList.size() - 1 DO pkg := self.packageList.get(i); GetPackage(pkg, name); END; ELSE iter := tab.iterate(); WHILE iter.next(pkg, tag) DO GetPackage(pkg, tag); END; END; END Checkout;
PROCEDURE---------------------------------------------------------------------------CheckoutSnapshot (self : T; name : TEXT) RAISES {Error} = VAR snap := self.snapshot(name); BEGIN Checkout(self, name, snap); END CheckoutSnapshot;
PROCEDURE---------------------------------------------------------------------------CheckoutRelease (self : T; name : TEXT) RAISES {Error} = VAR release := self.release(name); BEGIN Checkout(self, name, release); END CheckoutRelease;
PROCEDURE---------------------------------------------------------------------------CheckoutHead (self : T) RAISES {Error} = BEGIN Checkout(self, "", MakeHead(self), heedModifiedPackages := FALSE); END CheckoutHead;
PROCEDURE---------------------------------------------------------------------------CheckoutTrunkOrBranchHead (self : T; pkgs : TextSeq.T) RAISES {Error} = VAR head := NEW(TextTextTbl.Default).init(self.packageList.size()); tagText : TEXT; fn : TEXT; tag := NEW(Tag.T); isrel : BOOLEAN; BEGIN FOR i := 0 TO pkgs.size() - 1 DO WITH pkg = pkgs.get(i) DO IF Msg.vFlag THEN MsgX.V(self.msgif, "--- inspecting package " & pkg & " ---"); END; TRY IF self.poolset.execAction(pkg, "isrelease", tagText, self.externalShell, self.varTbl) = 0 THEN EVAL self.poolset.execAction(pkg, "currentreleasetag", tagText, self.externalShell, self.varTbl); fn := "PkgCRT"; isrel := TRUE; ELSIF self.poolset.execAction(pkg, "currentdeveltag", tagText, self.externalShell, self.varTbl) = 0 THEN fn := "PkgCDT"; isrel := FALSE; ELSE RAISE Error("cannot get tag for package " & pkg); END; IF tagText = NIL THEN tagText := TextUtils.Compress(self.poolset.fileContents(pkg, fn)); IF Text.Equal(fn, "PkgCRT") THEN self.poolset.setVal(pkg, "current-release-tag", tagText); ELSE self.poolset.setVal(pkg, "current-devel-tag", tagText); END; END; EXCEPT PoolSet.Error(e) => RAISE Error(e); END; EVAL tag.initFromText(tagText); IF NOT tag.okay() THEN RAISE Error("tag `" & tagText & "' for package " & pkg & " seems to be incorrect"); END; IF Msg.vFlag THEN MsgX.V(self.msgif, " current tag is " & tagText); END; IF isrel THEN tag := Tag.NewStableBranch(tag); tagText := tag.denotation(); ELSE tagText := "head"; END; IF Msg.vFlag THEN MsgX.V(self.msgif, " tip of branch tag is " & tagText); END; EVAL head.put(pkg, tagText); END; END; Checkout(self, "", head, FALSE); END CheckoutTrunkOrBranchHead;
PROCEDURE---------------------------------------------------------------------------CheckoutPackages (self : T; pkgs : TextSeq.T; tag := "head") RAISES {Error} = VAR head := NEW(TextTextTbl.Default).init(self.packageList.size()); snap : TextTextTbl.T := NIL; BEGIN IF NOT Text.Equal(tag, "head") THEN snap := self.release(tag); IF snap = NIL THEN snap := self.snapshot(tag); END; END; FOR i := 0 TO pkgs.size() - 1 DO WITH pkg = pkgs.get(i) DO IF snap = NIL THEN EVAL head.put(pkg, tag); ELSE VAR t : TEXT; BEGIN EVAL snap.get(pkg, t); IF t = NIL THEN t := tag; END; EVAL head.put(pkg, t); END; END; END; END; Checkout(self, "", head, FALSE); END CheckoutPackages;
PROCEDURE---------------------------------------------------------------------------BuildDepGraph ( self : T; confirmationCl : Confirmation.Closure := NIL) RAISES {Error} = VAR pkg : TEXT; loc : TEXT; res : TEXT; path : Pathname.T; errs := ""; okay := TRUE; par : TextTextTbl.T; locs := self.locations(); dummy: TEXT; depFileExists : BOOLEAN; thisPkgDepsOkay : BOOLEAN; BEGIN MsgX.T(self.msgif, "building dependency graph..."); IF self.missingList # NIL AND self.missingList.size() > 0 THEN MsgX.T(self.msgif, "checking out missing packages first..."); WITH msg = "The following packages are missing in your workspace:" & NL & TextUtils.TextSeqToText(self.missingList, NL) & NL & "Shall I check them out now" DO IF confirmationCl # NIL THEN IF confirmationCl.okay(msg) THEN self.checkoutPackages(self.missingList); ELSE RAISE Error("cannot build dependency graph without all packages"); END; END; END; END; self.depGraph := DependencyGraph.New(NIL); FOR i := 0 TO self.packageList.size() - 1 DO pkg := self.packageList.get(i); self.depGraph.addElem(pkg, action := NIL, phony := TRUE); END; IF self.locationTbl.size() < self.packageList.size() THEN IF NOT self.poolset.checkAll(self.packageList, res, self.missingList, self.locationTbl, checkHomogeneity := FALSE, ignoreMissingPackages := FALSE) THEN RAISE Error(res); END; FOR i := 0 TO self.packageList.size() - 1 DO pkg := self.packageList.get(i); EVAL self.locationTbl.put( pkg, Pathname.Prefix(self.poolset.pkgPath(pkg))); END; END; TRY par := self.varTableCopy(); IF self.locsText = NIL THEN self.locsText := TextUtils.TextSeqToText(locs); END; EVAL par.put("LOCATIONS", self.locsText); FOR i := 0 TO self.packageList.size() - 1 DO pkg := self.packageList.get(i); IF Msg.vFlag THEN MsgX.V(self.msgif, "--- package " & pkg & ": mkdep ---"); END; EVAL par.put("PKG", pkg); EVAL self.locationTbl.get(pkg, loc); EVAL par.put("LOCATION", loc); IF NOT self.poolset.exists(pkg, loc) THEN RAISE Error("package " & pkg & " has disappeared from location " & loc); END; thisPkgDepsOkay := TRUE; path := Pathname.Join(self.poolset.pkgPath(pkg), "PkgDep", NIL); IF self.poolset.execAction(pkg, "mkdep", dummy, self.externalShell, par) # 0 THEN errs := errs & "building dependencies for package " & pkg & " at " & path & " failed" & NL; thisPkgDepsOkay := FALSE; IF self.depsMandatory THEN okay := FALSE; END; END; (* dependencies should now be listed in collection/pkg/PkgDep *) depFileExists := FSUtils.Exists(path); IF depFileExists THEN IF NOT self.depGraph.addFromDependFile(path) THEN errs := errs & "adding dependencies for package " & pkg & " at " & path & " failed" & NL; thisPkgDepsOkay := FALSE; IF self.depsMandatory THEN okay := FALSE; END; END; ELSE thisPkgDepsOkay := FALSE; IF self.depsMandatory THEN okay := FALSE; END; END; IF NOT self.depsMandatory AND NOT thisPkgDepsOkay THEN MsgX.Warning(self.msgif, "no dependecies for package " & pkg); END; END; EXCEPT PoolSet.Error(e) => RAISE Error(e); END; IF self.depsMandatory AND NOT okay THEN RAISE Error(errs); END; IF self.useCache THEN EvaluateStateCacheChangesForDependendPackages(self); END; END BuildDepGraph;
PROCEDURE---------------------------------------------------------------------------ReadDepGraph (self : T; fn : TEXT) RAISES {Error} = BEGIN IF self.depGraph = NIL THEN self.depGraph := DependencyGraph.New(NIL); END; IF NOT self.depGraph.load(fn) THEN RAISE Error("reading dependency graph from file " & fn & "failed"); END; IF self.useCache THEN EvaluateStateCacheChangesForDependendPackages(self); END; END ReadDepGraph;
PROCEDURE---------------------------------------------------------------------------WriteDepGraph (self : T; fn : TEXT) RAISES {Error} = BEGIN IF NOT self.depGraph.save(fn) THEN RAISE Error("writing dependency graph to file " & fn & "failed"); END; END WriteDepGraph;
PROCEDURE---------------------------------------------------------------------------ReadDepGraphAsText (self : T; fn : TEXT) RAISES {Error} = BEGIN IF self.depGraph = NIL THEN self.depGraph := DependencyGraph.New(NIL); END; IF NOT self.depGraph.loadAsText(fn) THEN RAISE Error("reading dependency graph from file " & fn & "failed"); END; IF self.useCache THEN EvaluateStateCacheChangesForDependendPackages(self); END; END ReadDepGraphAsText;
PROCEDURE---------------------------------------------------------------------------WriteDepGraphAsText (self : T; fn : TEXT) RAISES {Error} = BEGIN IF NOT self.depGraph.saveAsText(fn) THEN RAISE Error("writing dependency graph to file " & fn & "failed"); END; END WriteDepGraphAsText;
PROCEDURE---------------------------------------------------------------------------PutPkgVersionBlock (wr : Wr.T; tbl : TextTextTbl.T) RAISES {Wr.Failure, Thread.Alerted} = VAR iter := tbl.iterate(); name, version : TEXT; BEGIN WHILE iter.next(name, version) DO Wr.PutText(wr, " " & name & " " & version & NL); END; Wr.PutText(wr, "end" & NL); END PutPkgVersionBlock;
PROCEDURE---------------------------------------------------------------------------ConvertToText (self : T; snapshotName : TEXT := NIL; releaseName : TEXT := NIL) : TEXT RAISES {Error} = VAR wr := TextWr.New(); res : TEXT; BEGIN TRY WriteInternalWr(self, wr, snapshotName, releaseName); FINALLY TRY res := TextWr.ToText(wr); Wr.Close(wr); EXCEPT ELSE RAISE Error("closing text writer failed"); END; END; RETURN res; END ConvertToText;
PROCEDURE---------------------------------------------------------------------------SnapshotText (self : T; snapshotName : TEXT) : TEXT RAISES {Error} = BEGIN RETURN ConvertToText(self, snapshotName, NIL); END SnapshotText;
PROCEDURE---------------------------------------------------------------------------ReleaseText (self : T; snapshotName : TEXT) : TEXT RAISES {Error} = BEGIN RETURN ConvertToText(self, NIL, snapshotName); END ReleaseText;
PROCEDURE---------------------------------------------------------------------------ToText (self : T) : TEXT RAISES {Error} = BEGIN RETURN ConvertToText(self, "", ""); END ToText;
PROCEDURE---------------------------------------------------------------------------WriteInternal (self : T; fn : TEXT; snapshotName : TEXT := NIL; (* "" <=> all *) releaseName : TEXT := NIL) (* "" <=> all *) RAISES {Error} = VAR wr : Wr.T; BEGIN TRY wr := FileWr.Open(fn); EXCEPT ELSE RAISE Error("opening file " & fn & " failed"); END; TRY WriteInternalWr(self, wr, snapshotName, releaseName); FINALLY TRY Wr.Close(wr); EXCEPT ELSE RAISE Error("closing file " & fn & " failed"); END; END; END WriteInternal;
PROCEDURE---------------------------------------------------------------------------WriteInternalWr (self : T; wr : Wr.T; snapshotName : TEXT := NIL; (* "" <=> all *) releaseName : TEXT := NIL) (* "" <=> all *) RAISES {Error} = VAR name : TEXT; loc : TEXT; tbl : TextTextTbl.T; nameDone := FALSE; BEGIN TRY Wr.PutText(wr, "collectionroot " & self.collectionRootOrig & NL); FOR i := 0 TO self.collectionList.size() - 1 DO name := self.collectionList.get(i); EVAL self.collectionTbl.get(name, loc); Wr.PutText(wr, "collection " & name & " at " & loc & NL); END; Wr.PutText(wr, NL); (* collections written *) FOR i := 0 TO self.packageList.size() - 1 DO name := self.packageList.get(i); EVAL self.packageTbl.get(name, loc); Wr.PutText(wr, "package " & name & " in " & loc & NL); END; Wr.PutText(wr, NL); (* packages written *) IF snapshotName # NIL AND Text.Empty(snapshotName) THEN VAR iter := self.snapshotTbl.iterate(); BEGIN WHILE iter.next(name, tbl) DO Wr.PutText(wr, "snapshot " & name & NL); PutPkgVersionBlock(wr, tbl); END; END; Wr.PutText(wr, "name \"" & self.name & "\"" & NL); nameDone := TRUE; ELSIF snapshotName # NIL THEN IF self.snapshotTbl.get(snapshotName, tbl) THEN Wr.PutText(wr, "snapshot " & snapshotName & NL); PutPkgVersionBlock(wr, tbl); ELSE RAISE Error("snapshot " & snapshotName & " not found"); END; END; Wr.PutText(wr, NL); (* snapshots written *) IF releaseName # NIL AND Text.Empty(releaseName)THEN VAR iter := self.releaseTbl.iterate(); BEGIN WHILE iter.next(name, tbl) DO Wr.PutText(wr, "release " & name & NL); PutPkgVersionBlock(wr, tbl); END; END; IF NOT nameDone THEN Wr.PutText(wr, "name \"" & self.name & "\"" & NL); END; ELSIF releaseName # NIL THEN IF self.releaseTbl.get(releaseName, tbl) THEN Wr.PutText(wr, "release " & releaseName & NL); PutPkgVersionBlock(wr, tbl); ELSE RAISE Error("release " & releaseName & " not found"); END; END; Wr.PutText(wr, NL); (* releases written *) Wr.PutText(wr, "creator \"" & self.user & "\"" & NL); IF self.cdate # 0.0d0 THEN Wr.PutText(wr, "ctime \"" & RCS_Date.FromTime(self.cdate) & "\"" & NL); END; IF self.mdate # 0.0d0 THEN Wr.PutText(wr, "mtime \"" & RCS_Date.FromTime(self.mdate) & "\"" & NL); END; Wr.PutText(wr, "description" & NL); Wr.PutText(wr, self.desc); Wr.PutText(wr, NL & "end" & NL); EXCEPT Wr.Failure => RAISE Error("writing project description failed"); | Thread.Alerted => RAISE Error("interrupted writing project description"); END; END WriteInternalWr;
PROCEDURE---------------------------------------------------------------------------Write (self : T; fn : TEXT) RAISES {Error} = BEGIN WriteInternal(self, fn, "", ""); END Write;
PROCEDURE---------------------------------------------------------------------------WriteSnapshot (self : T; fn : TEXT; name : TEXT) RAISES {Error} = BEGIN WriteInternal(self, fn, name, NIL); END WriteSnapshot;
PROCEDURE---------------------------------------------------------------------------WriteRelease (self : T; fn : TEXT; name : TEXT) RAISES {Error} = BEGIN WriteInternal(self, fn, NIL, name); END WriteRelease;
PROCEDURE---------------------------------------------------------------------------PackageUpdateSequence (self : T) : TextSeq.T = BEGIN IF self.depGraph = NIL THEN TRY BuildDepGraph(self); EXCEPT Error(e) => MsgX.Fatal(self.msgif, "cannot build package dependency graph: " & e); END; END; VAR nodes := self.depGraph.topologicalSort(); res := NEW(TextSeq.T).init(self.packageList.size()); BEGIN FOR i := 0 TO nodes.size() - 1 DO WITH act = nodes.get(i) DO IF MemberOfTextSeq(self.packageList, act.name()) THEN res.addhi(act.name()); END; END; END; RETURN res; END; END PackageUpdateSequence;
PROCEDURE---------------------------------------------------------------------------IgnoredPackages (self : T) : TextSeq.T = BEGIN IF self.ignoredList # NIL THEN RETURN self.ignoredList; END; VAR graphNodes := self.depGraph.nodes(); BEGIN self.ignoredList := NEW(TextSeq.T).init(graphNodes.size()); FOR i := 0 TO graphNodes.size() - 1 DO WITH act = graphNodes.get(i) DO IF NOT MemberOfTextSeq(self.packageList, act.name()) THEN self.ignoredList.addhi(act.name()); END; END; END; END; RETURN self.ignoredList; END IgnoredPackages;
PROCEDURE---------------------------------------------------------------------------MissingPackages (self : T) : TextSeq.T = BEGIN RETURN self.missingList; END MissingPackages;
PROCEDURE---------------------------------------------------------------------------ApplyToPackages (self : T; action : PkgBase.Action; action2 : PkgBase.Action := NIL; action3 : PkgBase.Action := NIL; cpkgs : TextSeq.T := NIL; ordered := TRUE; breakOnZeroReturn := FALSE; breakOnError := TRUE; breakOnFailure := TRUE; tag1Values : TextTextTbl.T := NIL; tag2Values : TextTextTbl.T := NIL) : INTEGER RAISES {Error} = PROCEDURE CheckStateCacheIfBuild(pkg, action : TEXT) RAISES {Error} = VAR loc, dir : TEXT; ret : INTEGER; res : TEXT; BEGIN IF self.useCache AND TextUtils.Contains(action, "build") THEN (* If we execute a build action, we need to invalidate the attributes that indicate `built or shipped' for all packages that depend on the newly built package if the build action is really executed. *) IF NOT self.locationTbl.get(pkg, loc) THEN RAISE Error("no location for package " & pkg); END; dir := Pathname.Join(loc, pkg, NIL); TRY IF NOT self.poolset.actionProbablyNeeded(pkg, dir, action, ret, res) THEN RETURN; END; EXCEPT PoolSet.Error(e) => RAISE Error(e); END; WITH deps = NEW(TextSeq.T).init() DO deps.addhi(pkg); FOR i := 0 TO deps.size() - 1 DO WITH p = deps.get(i) DO IF NOT self.locationTbl.get(p, loc) THEN RAISE Error("no location for package " & p); END; dir := Pathname.Join(loc, p, NIL); IF NOT Text.Equal(p, pkg) THEN TRY self.poolset.updateStateCache(dir, "need-build-ship", 0); EXCEPT PoolSet.Error(e) => RAISE Error(e); END; END; END; END; END; END; END CheckStateCacheIfBuild; PROCEDURE CheckAndDisplayResult(res : INTEGER; rtext : TEXT) = BEGIN IF rtext # NIL THEN IF NOT Text.Empty(rtext) THEN MsgX.T(self.msgif, rtext); END; IF Msg.vFlag THEN MsgX.V(self.msgif, "exit code was " & Fmt.Int(res)); END; END; END CheckAndDisplayResult; VAR loc : TEXT; upds : TextSeq.T; pkgs : TextSeq.T; res : INTEGER; locs := self.locations(); par := self.varTableCopy(); rtext: TEXT; BEGIN IF ordered THEN IF cpkgs = NIL THEN pkgs := self.packageUpdateSequence(); ELSE upds := self.packageUpdateSequence(); pkgs := NEW(TextSeq.T).init(cpkgs.size()); FOR i := 0 TO upds.size() - 1 DO WITH pkg = upds.get(i) DO IF MemberOfTextSeq(cpkgs, pkg) THEN pkgs.addhi(pkg); END; END; END; END; ELSE IF cpkgs = NIL THEN pkgs := self.packageList; ELSE pkgs := cpkgs; END; END; IF self.locsText = NIL THEN self.locsText := TextUtils.TextSeqToText(locs); END; EVAL par.put("LOCATIONS", self.locsText ); res := 0; FOR i := 0 TO pkgs.size() - 1 DO WITH pkg = pkgs.get(i) DO IF Msg.tFlag THEN MsgX.T(self.msgif, "--- package " & pkg & " ---"); END; EVAL par.put("PKG", pkg); EVAL self.locationTbl.get(pkg, loc); EVAL par.put("LOCATION", loc); VAR val : TEXT; BEGIN IF tag1Values # NIL AND tag1Values.get(pkg, val) AND val # NIL THEN EVAL par.put("TAG1", val); END; IF tag2Values # NIL AND tag2Values.get(pkg, val) AND val # NIL THEN EVAL par.put("TAG2", val); END; END; TRY CheckStateCacheIfBuild(pkg, action); IF Msg.vFlag THEN MsgX.V(self.msgif, "--- package " & pkg & ": applying action 1: " & action & " ---", level := 2); END; res := ExecutionFailure; rtext := NIL; res := self.poolset.execAction(pkg, action, rtext, self.externalShell, par); CheckAndDisplayResult(res, rtext); IF action2 # NIL THEN IF NOT breakOnError OR (res = 0 AND NOT breakOnZeroReturn) OR (res # 0 AND breakOnZeroReturn) THEN CheckStateCacheIfBuild(pkg, action2); IF Msg.vFlag THEN MsgX.V(self.msgif, "--- package " & pkg & ": applying action 2: " & action2 & " ---", level := 2); END; res := ExecutionFailure; rtext := NIL; res := self.poolset.execAction(pkg, action2, rtext, self.externalShell, par); CheckAndDisplayResult(res, rtext); END; END; IF action3 # NIL THEN IF NOT breakOnError OR (res = 0 AND NOT breakOnZeroReturn) OR (res # 0 AND breakOnZeroReturn) THEN CheckStateCacheIfBuild(pkg, action3); IF Msg.vFlag THEN MsgX.V(self.msgif, "--- package " & pkg & ": applying action 3: " & action3 & " ---", level := 2); END; rtext := NIL; res := ExecutionFailure; res := self.poolset.execAction(pkg, action3, rtext, self.externalShell, par); CheckAndDisplayResult(res, rtext); END; END; IF breakOnError THEN IF breakOnZeroReturn THEN IF res = 0 THEN EXIT; END; ELSE IF res # 0 THEN EXIT; END; END; END; EXCEPT PoolSet.Error(e) => IF breakOnFailure THEN RAISE Error(e); ELSE (* silently skip exception *) MsgX.V(self.msgif, "caught and ignored exception"); END; END; END; END; RETURN res; END ApplyToPackages;
PROCEDURE---------------------------------------------------------------------------ApplyCmdListDirectly (self : T; cmd : TEXT; cpkgs : TextSeq.T := NIL; ordered := TRUE; breakOnZeroReturn := FALSE; breakOnError := TRUE; breakOnFailure := TRUE) : INTEGER RAISES {Error} = VAR upds : TextSeq.T; pkgs : TextSeq.T; res : INTEGER; BEGIN IF ordered THEN IF cpkgs = NIL THEN pkgs := self.packageUpdateSequence(); ELSE upds := self.packageUpdateSequence(); pkgs := NEW(TextSeq.T).init(cpkgs.size()); FOR i := 0 TO upds.size() - 1 DO WITH pkg = upds.get(i) DO IF MemberOfTextSeq(cpkgs, pkg) THEN pkgs.addhi(pkg); END; END; END; END; ELSE IF cpkgs = NIL THEN pkgs := self.packageList; ELSE pkgs := cpkgs; END; END; res := 0; FOR i := 0 TO pkgs.size() - 1 DO WITH pkg = pkgs.get(i) DO IF Msg.tFlag THEN MsgX.T(self.msgif, "--- package " & pkg & " ---"); END; TRY IF Msg.vFlag THEN MsgX.V(self.msgif, "--- package " & pkg & ": applying command: " & cmd & " ---", level := 2); END; res := ExecutionFailure; res := self.poolset.execCmdList(pkg, cmd, self.externalShell); IF breakOnError THEN IF breakOnZeroReturn THEN IF res = 0 THEN EXIT; END; ELSE IF res # 0 THEN EXIT; END; END; END; EXCEPT PoolSet.Error(e) => IF breakOnFailure THEN RAISE Error(e); ELSE (* silently skip exception *) MsgX.V(self.msgif, "caught and ignored exception"); END; END; END; END; RETURN res; END ApplyCmdListDirectly;
PROCEDURE---------------------------------------------------------------------------SelectPackages (self : T; pred : PkgBase.Action; ordered := TRUE; selectOnZeroReturn := TRUE; breakOnFailure := TRUE; tag1Values : TextTextTbl.T := NIL; tag2Values : TextTextTbl.T := NIL) : TextSeq.T RAISES {Error} = VAR loc : TEXT; pkgs : TextSeq.T; res := NEW(TextSeq.T).init(); ret := 0; locs := self.locations(); par := self.varTableCopy(); done : BOOLEAN; dummy: TEXT; BEGIN IF ordered THEN pkgs := self.packageUpdateSequence(); ELSE pkgs := self.packageList; END; IF self.locsText = NIL THEN self.locsText := TextUtils.TextSeqToText(locs); END; EVAL par.put("LOCATIONS", self.locsText); FOR i := 0 TO pkgs.size() - 1 DO WITH pkg = pkgs.get(i) DO done := FALSE; IF NOT done THEN IF Msg.vFlag THEN MsgX.V(self.msgif, "--- package " & pkg & ": selecting by " & pred & " ---"); END; EVAL par.put("PKG", pkg); EVAL self.locationTbl.get(pkg, loc); EVAL par.put("LOCATION", loc); VAR val : TEXT; BEGIN IF tag1Values # NIL AND tag1Values.get(pkg, val) AND val # NIL THEN EVAL par.put("TAG1", val); END; IF tag2Values # NIL AND tag2Values.get(pkg, val) AND val # NIL THEN EVAL par.put("TAG2", val); END; END; TRY ret := ExecutionFailure; ret := self.poolset.execAction(pkg, pred, dummy, self.externalShell, par); done := TRUE; EXCEPT PoolSet.Error(e) => IF breakOnFailure THEN RAISE Error(e); ELSE (* silently skip exception *) MsgX.V(self.msgif, "caught and ignored exception testing package " & pkg); END; END; END; IF done THEN IF selectOnZeroReturn THEN IF ret = 0 THEN res.addhi(pkg); END; ELSE IF ret # 0 THEN res.addhi(pkg); END; END; END; END; END; RETURN res; END SelectPackages;
PROCEDURE---------------------------------------------------------------------------SelectByCmdList (self : T; cmd : TEXT; ordered := TRUE; selectOnZeroReturn := TRUE; breakOnFailure := TRUE) : TextSeq.T RAISES {Error} = VAR pkgs : TextSeq.T; res := NEW(TextSeq.T).init(); ret := 0; BEGIN IF ordered THEN pkgs := self.packageUpdateSequence(); ELSE pkgs := self.packageList; END; FOR i := 0 TO pkgs.size() - 1 DO WITH pkg = pkgs.get(i) DO IF Msg.vFlag THEN MsgX.V(self.msgif, "--- package " & pkg & ": selecting by `" & cmd & "' ---"); END; TRY ret := ExecutionFailure; ret := self.poolset.execCmdList(pkg, cmd, self.externalShell); IF selectOnZeroReturn THEN IF ret = 0 THEN res.addhi(pkg); END; ELSE IF ret # 0 THEN res.addhi(pkg); END; END; EXCEPT PoolSet.Error(e) => IF breakOnFailure THEN RAISE Error(e); ELSE (* silently skip exception *) MsgX.V(self.msgif, "caught and ignored exception"); END; END; END; END; RETURN res; END SelectByCmdList;
PROCEDURE---------------------------------------------------------------------------DependendPackages (self : T; pkg : TEXT) : TextSeq.T RAISES {} = VAR dep : StdDepGraphNodeSeq.T; res : TextSeq.T := NEW(TextSeq.T).init(); BEGIN dep := self.depGraph.dependingNodes(pkg); FOR j := 0 TO dep.size() - 1 DO WITH dpkg = dep.get(j).name() DO res.addhi(dpkg); END; END; RETURN res; END DependendPackages;
PROCEDURE---------------------------------------------------------------------------PackageDependencies (self : T; pkg : TEXT) : TextSeq.T RAISES {} = VAR dep : StdDepGraphNodeSeq.T; res : TextSeq.T := NEW(TextSeq.T).init(); BEGIN dep := self.depGraph.nodeDependencies(pkg); FOR j := 0 TO dep.size() - 1 DO WITH dpkg = dep.get(j).name() DO res.addhi(dpkg); END; END; RETURN res; END PackageDependencies;
PROCEDURE---------------------------------------------------------------------------ModifiedPackages (self : T) : TextSeq.T RAISES {Error} = VAR res : TextSeq.T; BEGIN res := self.selectPackages("modified", ordered := FALSE, selectOnZeroReturn := TRUE, breakOnFailure := TRUE); RETURN res; END ModifiedPackages;
PROCEDURE---------------------------------------------------------------------------OutOfDatePackages (self : T) : TextSeq.T RAISES {Error} = VAR res : TextSeq.T; BEGIN res := self.selectPackages("uptodate", ordered := FALSE, selectOnZeroReturn := FALSE, breakOnFailure := TRUE); RETURN res; END OutOfDatePackages;
PROCEDURE---------------------------------------------------------------------------UpToDatePackages (self : T) : TextSeq.T RAISES {Error} = VAR res : TextSeq.T; BEGIN res := self.selectPackages("uptodate", ordered := FALSE, selectOnZeroReturn := TRUE, breakOnFailure := TRUE); RETURN res; END UpToDatePackages;
PROCEDURE---------------------------------------------------------------------------AddDependingPackages (self : T; pkgs : TextSeq.T) : TextSeq.T = VAR dep : StdDepGraphNodeSeq.T; depp : TextSeq.T := NEW(TextSeq.T).init(); res : TextSeq.T := NEW(TextSeq.T).init(); uds : TextSeq.T; BEGIN IF self.depGraph = NIL THEN TRY BuildDepGraph(self); EXCEPT Error(e) => MsgX.Fatal(self.msgif, "cannot build package dependency graph: " & e); END; END; FOR i := 0 TO pkgs.size() - 1 DO WITH mpkg = pkgs.get(i) DO depp.addhi(mpkg); dep := self.depGraph.dependingNodes(mpkg); FOR j := 0 TO dep.size() - 1 DO WITH dpkg = dep.get(j).name() DO depp.addhi(dpkg); END; END; END; END; (* depp contains all elements of pkgs and dependend packages, but probably a number of times and in wrong order *) uds := self.packageUpdateSequence(); (* uds contains the correct order of updates for packages *) FOR i := 0 TO uds.size() - 1 DO WITH pkg = uds.get(i) DO IF MemberOfTextSeq(depp, pkg) THEN res.addhi(pkg); END; END; END; (* all the packages in depp have been added to res in the order of their occurence in uds *) RETURN res; END AddDependingPackages;
PROCEDURE---------------------------------------------------------------------------ModifiedAndDependingPackages (self : T) : TextSeq.T RAISES {Error} = VAR mod : TextSeq.T; res : TextSeq.T; BEGIN mod := self.selectPackages("modified", ordered := FALSE, selectOnZeroReturn := TRUE, breakOnFailure := TRUE); (* mod contains all locally modified packages *) res := self.addDependingPackages(mod); RETURN res; END ModifiedAndDependingPackages;
PROCEDURE---------------------------------------------------------------------------OutOfDateAndDependingPackages (self : T) : TextSeq.T RAISES {Error} = VAR ood : TextSeq.T; res : TextSeq.T; BEGIN ood := self.selectPackages("uptodate", ordered := FALSE, selectOnZeroReturn := FALSE, breakOnFailure := TRUE); (* mod contains all locally modified packages *) res := self.addDependingPackages(ood); RETURN res; END OutOfDateAndDependingPackages;
PROCEDURE---------------------------------------------------------------------------PackagesWithConflicts (self : T) : TextSeq.T RAISES {Error} = VAR res : TextSeq.T; BEGIN res := self.selectPackages("conflicts", ordered := FALSE, selectOnZeroReturn := TRUE, breakOnFailure := TRUE); RETURN res; END PackagesWithConflicts;
PROCEDURE---------------------------------------------------------------------------TestAllPackagesReleased (self : T) : BOOLEAN = BEGIN TRY RETURN self.applyToPackages("isrelease", ordered := FALSE, breakOnError := TRUE, breakOnFailure := TRUE) = 0; EXCEPT Error => RETURN FALSE; END; END TestAllPackagesReleased;
PROCEDURE---------------------------------------------------------------------------TestNoPackageModified (self : T) : BOOLEAN = VAR sel : TextSeq.T; BEGIN TRY sel := self.selectPackages("modified", ordered := FALSE, selectOnZeroReturn := TRUE, breakOnFailure := TRUE); (* sel contains all modified packages *) RETURN sel.size() = 0; EXCEPT Error => RETURN FALSE; END; END TestNoPackageModified;
PROCEDURE---------------------------------------------------------------------------BuildStateLabelTable (self : T; lazy := FALSE) : TextTextTextTbl.T RAISES {Error} = VAR pkg, fn : TEXT; tag : TEXT; label : TEXT; tabText : TEXT; tabLines : TextSeq.T; tbl : TextTextTbl.T; (* tag -> label *) res : TextTextTextTbl.T; (* pkgname -> ( tag -> label ) *) found : BOOLEAN; dummy : TEXT; BEGIN res := NEW(TextTextTextTbl.Default).init(self.packageList.size()); TRY fn := ".labels"; FOR i := 0 TO self.packageList.size() - 1 DO pkg := self.packageList.get(i); IF lazy THEN TRY tabText := self.poolset.fileContents(pkg, fn); IF Msg.vFlag THEN MsgX.V(self.msgif, "--- using old state labels of package " & pkg & " ---"); END; found := TRUE; EXCEPT ELSE found := FALSE; END; END; IF NOT lazy OR NOT found THEN IF Msg.vFlag THEN MsgX.V(self.msgif, "--- reading state labels from package " & pkg & " ---"); END; IF self.poolset.execAction(pkg, "listlabels", dummy, self.externalShell, self.varTbl) = 0 THEN ELSE RAISE Error("cannot get state labels for package " & pkg); END; tabText := self.poolset.fileContents(pkg, fn); END; tbl := NEW(TextTextTbl.Default).init(); tabLines := TextUtils.Split(tabText, "\n"); FOR j := 0 TO tabLines.size() - 1 DO WITH line = tabLines.get(j) DO WITH elems = TextUtils.Split(line, "-->") DO IF elems.size() = 2 THEN tag := TextUtils.Compress(elems.get(0)); label := TextUtils.Compress(elems.get(1)); EVAL tbl.put(tag, label); ELSE IF NOT Text.Empty(TextUtils.Compress(line)) THEN MsgX.Warning(self.msgif, "invalid line in state list table ignored: " & line); END; END; END; END; END; EVAL res.put(pkg, tbl); END; EXCEPT Error(e) => RAISE Error(e); | PoolSet.Error(e) => RAISE Error(e); END; RETURN res; END BuildStateLabelTable;
PROCEDURE---------------------------------------------------------------------------CacheAllStateLabels (self : T; lazy := FALSE) RAISES {Error} = BEGIN self.stateLabelTbl := BuildStateLabelTable(self, lazy); END CacheAllStateLabels;
PROCEDURE---------------------------------------------------------------------------CheckLabelsInternal (self : T; snap : TextTextTbl.T; pattern : TEXT) : TextSeq.T RAISES {Error} = VAR res := NEW(TextSeq.T).init(); iter := snap.iterate(); tbl : TextTextTbl.T; re : RegEx.Pattern; pkg, tag, label : TEXT; BEGIN TRY re := RegEx.Compile(pattern); EXCEPT RegEx.Error(t) => RAISE Error("cannot compile regex " & pattern & ": " & t); END; WHILE iter.next(pkg, tag) DO IF self.stateLabelTbl.get(pkg, tbl) THEN IF tbl.get(tag, label) THEN IF RegEx.Execute(re, label) # -1 THEN IF Msg.vFlag THEN MsgX.V(self.msgif, " found match in package " & pkg & " version " & tag & " label " & label); END; res.addhi(pkg); END; ELSE MsgX.Error(self.msgif, "cannot get state label for pkg " & pkg & " tag " & tag); END; ELSE MsgX.Error(self.msgif, "cannot get state labels for pkg " & pkg); END; END; RETURN res; END CheckLabelsInternal;
PROCEDURE---------------------------------------------------------------------------CheckCurrentLabelsGen (self : T; pattern : TEXT; useCachedLabels := FALSE) : TextSeq.T RAISES {Error} = VAR snap := CurrentTagList(self); BEGIN IF NOT useCachedLabels THEN CacheAllStateLabels(self); END; RETURN CheckLabelsInternal(self, snap, pattern); END CheckCurrentLabelsGen;
PROCEDURE---------------------------------------------------------------------------CheckCurrentLabels (self : T; pattern : TEXT; lazy := FALSE) : TextSeq.T RAISES {Error} = VAR pkg, fn : TEXT; label : TEXT; res := NEW(TextSeq.T).init(); found : BOOLEAN; re : RegEx.Pattern; BEGIN TRY re := RegEx.Compile(pattern); EXCEPT RegEx.Error(t) => RAISE Error("cannot compile regex " & pattern & ": " & t); END; TRY fn := ".label"; FOR i := 0 TO self.packageList.size() - 1 DO pkg := self.packageList.get(i); IF lazy THEN TRY label := TextUtils.Compress(self.poolset.fileContents(pkg, fn)); IF Msg.vFlag THEN MsgX.V(self.msgif, "--- using old state label of package " & pkg & " ---"); END; found := TRUE; EXCEPT ELSE found := FALSE; END; END; IF NOT lazy OR NOT found THEN IF Msg.vFlag THEN MsgX.V(self.msgif, "--- reading state label from package " & pkg & " ---"); END; IF self.poolset.execAction(pkg, "currentlabel", label, self.externalShell, self.varTbl) = 0 THEN ELSE RAISE Error("cannot get current state label for package " & pkg); END; IF label = NIL THEN label := TextUtils.Compress(self.poolset.fileContents(pkg, fn)); self.poolset.setVal(pkg, "current-label", label); END; END; IF RegEx.Execute(re, label) # -1 THEN IF Msg.vFlag THEN MsgX.V(self.msgif, " found match in package " & pkg & " label " & label); END; res.addhi(pkg); END; END; EXCEPT Error(e) => RAISE Error(e); | PoolSet.Error(e) => RAISE Error(e); END; RETURN res; END CheckCurrentLabels;
PROCEDURE---------------------------------------------------------------------------CheckLabelsOfSnapshot (self : T; name : TEXT; pattern : TEXT; useCachedLabels := FALSE) : TextSeq.T RAISES {Error} = VAR snap : TextTextTbl.T; BEGIN IF NOT useCachedLabels THEN CacheAllStateLabels(self); END; snap := Snapshot(self, name); IF snap = NIL THEN RAISE Error("there is no snapshot " & name); END; RETURN CheckLabelsInternal(self, snap, pattern); END CheckLabelsOfSnapshot;
PROCEDURE---------------------------------------------------------------------------CheckLabelsOfRelease (self : T; name : TEXT; pattern : TEXT; useCachedLabels := FALSE) : TextSeq.T RAISES {Error} = VAR snap : TextTextTbl.T; BEGIN IF NOT useCachedLabels THEN CacheAllStateLabels(self); END; snap := Release(self, name); IF snap = NIL THEN RAISE Error("there is no release " & name); END; RETURN CheckLabelsInternal(self, snap, pattern); END CheckLabelsOfRelease;
PROCEDURE---------------------------------------------------------------------------NewCheckpoint (self : T; fn : TEXT; update := FALSE) RAISES {Error} = VAR cp : Checkpoint.T; BEGIN IF NOT self.useCache THEN RETURN END; TRY cp := self.poolset.newCheckpoint(update); cp.toFile(fn); EXCEPT PoolSet.Error(e) => RAISE Error(e); | Checkpoint.Error(e) => RAISE Error("NewCheckpoint: " & e); END END NewCheckpoint;
PROCEDURE---------------------------------------------------------------------------LoadNewCheckpoint (self : T; fn : TEXT; update := FALSE) RAISES {Error} = VAR cp : Checkpoint.T; BEGIN IF NOT self.useCache THEN RETURN END; MsgX.T(self.msgif, "loading new checkpoint from file " & fn); self.oldState := self.poolset.cachedState(); TRY cp := Checkpoint.New(self.poolset.getFileCache(), self.msgif); cp.fromFile(fn); IF update THEN IF NOT Msg.vFlag THEN MsgX.T(self.msgif, "scanning all packages (loading new checkpoint)..."); END; cp.update(); END; EXCEPT Checkpoint.Error(e) => RAISE Error("error reading checkpoint: " & e); END; self.poolset.replaceStateCache(cp); END LoadNewCheckpoint;
PROCEDURE---------------------------------------------------------------------------WriteCheckpoint (self : T; fn : TEXT) RAISES {Error} = VAR cp : Checkpoint.T; BEGIN IF NOT self.useCache THEN RETURN END; MsgX.T(self.msgif, "writing checkpoint to file " & fn); TRY cp := self.poolset.cachedState(); cp.toFile(fn); EXCEPT Checkpoint.Error(e) => RAISE Error("WriteCheckpoint: " & e); END END WriteCheckpoint;
PROCEDURE---------------------------------------------------------------------------InvalidateCachedUnsureVersionInfo (self : T) RAISES {Error} = VAR pkg, loc : TEXT; BEGIN IF NOT self.useCache THEN RETURN END; MsgX.T(self.msgif, "purging unsure version info..."); FOR i := 0 TO self.packageList.size() - 1 DO pkg := self.packageList.get(i); IF NOT self.locationTbl.get(pkg, loc) THEN RAISE Error("no location for package " & pkg); END; WITH dir = Pathname.Join(loc, pkg, NIL) DO TRY self.poolset.updateStateCache(dir, "clear-utd-nocfl", 0); self.poolset.updateStateCache(dir, "clear-mod-unmod", 0); self.poolset.updateStateCache(dir, "clear-tags", 0); EXCEPT PoolSet.Error(e) => RAISE Error(e); END; END; END; END InvalidateCachedUnsureVersionInfo;
PROCEDURE---------------------------------------------------------------------------InvalidateCachedBuildInfo (self : T) RAISES {Error} = VAR pkg, loc : TEXT; BEGIN IF NOT self.useCache THEN RETURN END; MsgX.T(self.msgif, "purging build info..."); FOR i := 0 TO self.packageList.size() - 1 DO pkg := self.packageList.get(i); IF NOT self.locationTbl.get(pkg, loc) THEN RAISE Error("no location for package " & pkg); END; WITH dir = Pathname.Join(loc, pkg, NIL) DO TRY self.poolset.updateStateCache(dir, "need-build-ship", 0); self.poolset.updateStateCache(dir, "need-mkdep-build-ship", 0); EXCEPT PoolSet.Error(e) => RAISE Error(e); END; END; END; END InvalidateCachedBuildInfo;
VAR NL := "\n"; BEGIN IF Text.Equal(MxConfig.HOST_OS_TYPE, "WIN32") THEN NL := "\r\n"; END; END PrjDesc.