-- MakeConfigImpl.Mesa, last edit February 4, 1983 1:14 pm -- Pilot 6.0/ Mesa 7.0 -- -- options -- /a make all -- /v construct dependency graph -- /z turn on debugging flag DIRECTORY BcdDefs: TYPE USING [BCD], CWF: TYPE USING [FWF0, FWF1, SetWriteProcedure, SWF1, WF0, WF1, WF2, WFCR], Directory: TYPE USING [UpdateDates], File: TYPE USING [Capability, read], FileStream: TYPE USING [Create], IO: TYPE USING[Handle, PutChar, UserAbort], LongString: TYPE USING [AppendString, EquivalentString, StringBoundsFault], MDSubr: TYPE USING [AddToDepends, AddToLook, AnalyzeLocalFiles, CheckNames, ConvertToLook, entNil, Entry, EntryRecord, EntrySeq, EntrySeqRecord, FreeEntrySeq, FreeLookSeq, GetAnEntry, GetBinderRule, GetCompilerRule, InsertOtherSym, Look, LookLook, lookNil, LookSeq, LookSeqRecord, ModInfo, ModInfoRecord, ParseObject, PrintEntries, ReadInLocalDirectoryAll, StopScanner, StringSeq, StringSeqRecord, StripFirstWord, ThrowAwayLeaves, versionNil, versionUnknown, WalkTheGraph], Rope: TYPE USING[ROPE, Text], RopeInline: TYPE USING[InlineFlatten], Space: TYPE USING [Create, Delete, Handle, LongPointer, Map, Unmap, virtualMemory], Stream: TYPE USING [Delete, Handle, PutChar], String: TYPE USING [CompareStrings], Subr: TYPE USING [AbortMyself, AllocateString, debugflg, EndsIn, errorflg, FreeString, HugeZone, LongZone, MakeTTYProcs, NewStream, PackedTime, Prefix, PrintGreeting, strcpy, SubrInit, SubrStop, TTYProcs, Write], Time: TYPE USING [Current], UECP: TYPE USING[Argv, Parse], UserExec: TYPE USING[AcquireResource, AskUser, CommandProc, GetStreams, RegisterCommand, ReleaseResource]; MakeConfigImpl: PROGRAM IMPORTS CWF, Directory, FileStream, IO, LongString, MDSubr, RopeInline, Space, Stream, String, Subr, Time, UECP, UserExec = { -- MDS USAGE !!! longzone: UNCOUNTED ZONE; compstr, bindstr: LONG STRING; recur: CARDINAL; presenttime: Subr.PackedTime; bcdspace: Space.Handle; -- this space is 1 page big and used to map in bcd headers stdout: IO.Handle; -- endof MDS USAGE -- local variables maxlooksize: CARDINAL = 1600; maxentrysize: CARDINAL = 800; maxotherstrings: CARDINAL = 50; Main: UserExec.CommandProc = TRUSTED { ENABLE UNWIND => [] ← UserExec.ReleaseResource[$MakeConfig]; h: Subr.TTYProcs; in, out: IO.Handle; [in, out] ← UserExec.GetStreams[exec]; [] ← UserExec.AcquireResource[$MakeConfig, "MakeConfig", exec]; h ← Subr.MakeTTYProcs[in, out, exec, MyConfirm]; MakeConfigUsingProcs[h, event.commandLine]; [] ← UserExec.ReleaseResource[$MakeConfig]; }; -- this is the procedure called by the Simple Executive MakeConfigUsingProcs: PROC[h: Subr.TTYProcs, commandLine: Rope.ROPE] = { entryseq: MDSubr.EntrySeq ← NIL; lookseq: MDSubr.LookSeq ← NIL; token: STRING ← [100]; forcemake, verbose: BOOL ← FALSE; starttime: Subr.PackedTime; hugezone: UNCOUNTED ZONE; argv: UECP.Argv ← UECP.Parse[commandLine]; flat: Rope.Text; ThrowAwayThisFile: PROC[name: STRING] RETURNS[BOOL] = { RETURN[NOT (Subr.EndsIn[name, ".bcd"L] OR Subr.EndsIn[name, ".mesa"L] OR Subr.EndsIn[name, ".config"L])]; }; Cleanup: PROC = { MDSubr.FreeLookSeq[@lookseq]; MDSubr.FreeEntrySeq[@entryseq]; MDSubr.StopScanner[]; -- frees memory in scanner CloseLineCm[]; -- frees storage for compstr and bindstr Subr.SubrStop[]; }; { ENABLE { UNWIND => Cleanup[]; Subr.AbortMyself => { CWF.WF0["MakeConfig Aborted.\n"L]; GOTO out; }; }; compstr ← bindstr ← NIL; recur ← 0; stdout ← h.out; [] ← CWF.SetWriteProcedure[MyPutChar]; presenttime ← starttime ← Time.Current[]; hugezone ← Subr.HugeZone[]; Subr.SubrInit[256]; -- the number of pages to be allocated in the long zone longzone ← Subr.LongZone[]; Subr.errorflg ← Subr.debugflg ← FALSE; Subr.PrintGreeting["MakeConfig"L]; lookseq ← hugezone.NEW[MDSubr.LookSeqRecord[maxlooksize]]; lookseq.lookzone ← hugezone; MDSubr.ReadInLocalDirectoryAll[lookseq,ThrowAwayThisFile]; IF h.in.UserAbort[] THEN Subr.AbortMyself; entryseq ← hugezone.NEW[MDSubr.EntrySeqRecord[maxentrysize]]; entryseq.entryzone ← hugezone; bcdspace ← Space.Create[size: 1, parent: Space.virtualMemory]; FOR p: CARDINAL IN [1 .. argv.argc) DO flat ← RopeInline.InlineFlatten[argv[p]]; Subr.strcpy[token, LOOPHOLE[flat]]; IF token[0] = '- OR token[0] = '/ THEN SELECT token[1] FROM 'a, 'A => forcemake ← TRUE; 'v, 'V => verbose ← TRUE; 'z, 'Z => Subr.debugflg ← TRUE; ENDCASE => { CWF.WF1["Unknown flag '%s'\n"L,token]; Subr.errorflg ← TRUE; } ELSE MainLoop[token, verbose, forcemake, entryseq, lookseq, h]; ENDLOOP; IF compstr ~= NIL OR bindstr ~= NIL THEN TellAboutCommands[h]; starttime ← Time.Current[] - starttime; CWF.WF1["\nTotal elapsed time for MakeConfig %lr."L,@starttime]; IF Subr.errorflg THEN CWF.WF0["\tErrors logged."L]; CWF.WFCR[]; EXITS out => NULL; }; Space.Delete[bcdspace]; Cleanup[]; }; TellAboutCommands: PROC[h: Subr.TTYProcs] = { linecm: Stream.Handle; WFProc: PROC[ch: CHAR] = { Stream.PutChar[linecm, ch]; h.out.PutChar[ch]; }; linecm ← Subr.NewStream["Line.cm"L, Subr.Write]; CWF.WF0["Execute: "L]; IF compstr ~= NIL THEN CWF.FWF1[WFProc, "Compiler.~ %s;"L, compstr]; IF bindstr ~= NIL THEN CWF.FWF1[WFProc, "Binder.~ %s;"L, bindstr]; CWF.FWF0[WFProc, "\n"L]; Stream.Delete[linecm]; CWF.WF0["(Also written on Line.cm)\n"L]; }; MainLoop: PROC[modulename: STRING, verbose, forcemake: BOOL, entryseq: MDSubr.EntrySeq, lookseq: MDSubr.LookSeq, h: Subr.TTYProcs] = { firstinx: CARDINAL ← 0; look: MDSubr.Look; stemp: STRING ← [100]; trymesa: BOOL ← TRUE; stringseq: MDSubr.StringSeq; stringseq ← longzone.NEW[MDSubr.StringSeqRecord[maxotherstrings]]; CWF.WF1["\nMaking '%s'\n"L, modulename]; IF Subr.EndsIn[modulename,".mesa"L] THEN modulename.length ← modulename.length - 5 ELSE IF Subr.EndsIn[modulename,".config"L] THEN { modulename.length ← modulename.length - 7; trymesa ← FALSE; } ELSE IF Subr.EndsIn[modulename,".bcd"L] THEN modulename.length ← modulename.length - 4; MDSubr.InsertOtherSym["MESACOMP"L, "Compiler.~"L, stringseq]; MDSubr.InsertOtherSym["MESABIND"L, "Binder.~"L, stringseq]; FOR i: CARDINAL IN [0.. lookseq.size) DO lookseq[i].need ← TRUE; ENDLOOP; MDSubr.CheckNames[entryseq, lookseq]; firstinx ← DependTree[modulename, entryseq, lookseq, stringseq, trymesa]; -- PrintEntries[entryseq, StreamDefs.GetDefaultDisplayStream[], -- @stringvar, nstringvars]; -- CWF.WF1["once for %s\n"L, modulename]; IF h.in.UserAbort[] THEN Subr.AbortMyself; MDSubr.CheckNames[entryseq, lookseq]; MDSubr.ConvertToLook[entryseq,lookseq]; MDSubr.CheckNames[entryseq, lookseq]; FOR i: CARDINAL IN [0.. lookseq.size) DO lookseq[i].need ← FALSE; lookseq[i].besttime ← MDSubr.versionNil; -- forces recompute ENDLOOP; [] ← Decide[forreal: FALSE, parinx: firstinx, entryseq: entryseq, lookseq: lookseq, justmark: TRUE, forcemake: FALSE]; -- PrintEntries[entryseq, StreamDefs.GetDefaultDisplayStream[], -- @stringvar, nstringvars]; -- PrintLook[lookseq.size]; -- CWF.WF2["look %d rule %s\n", @entryseq[firstinx].lookinx, entryseq[firstinx].rule]; look ← IF entryseq[firstinx].lookinx~= MDSubr.lookNil THEN @lookseq[entryseq[firstinx].lookinx] ELSE NIL; IF (look = NIL OR NOT look.presentonlocaldisk) AND entryseq[firstinx].rule = NIL THEN { CWF.WF1["Error: %s not found.\n"L, modulename]; Subr.errorflg ← TRUE; } ELSE { MDSubr.ThrowAwayLeaves[firstinx, entryseq, lookseq, FALSE]; MDSubr.AnalyzeLocalFiles[oktosort: FALSE, lookseq: lookseq, getcreatedate: TRUE]; IF h.in.UserAbort[] THEN Subr.AbortMyself; [] ← Decide[TRUE,firstinx,entryseq,lookseq,FALSE, forcemake]; IF h.in.UserAbort[] THEN Subr.AbortMyself; PrintMade[firstinx, entryseq, lookseq, forcemake]; IF verbose THEN { sh: Stream.Handle; CWF.SWF1[stemp, "%s.make$"L, modulename]; SortEntries[entryseq]; CWF.WF1["\nDependency Tree is on file '%s'\n"L, stemp]; sh ← Subr.NewStream[stemp, Subr.Write]; MDSubr.PrintEntries[entryseq, sh, stringseq, h]; Stream.Delete[sh]; }; }; FOR i: CARDINAL IN [0 .. stringseq.size) DO Subr.FreeString[stringseq[i].str]; Subr.FreeString[stringseq[i].val]; ENDLOOP; longzone.FREE[@stringseq]; }; DependTree: PROC[modulename: STRING, entryseq: MDSubr.EntrySeq, lookseq: MDSubr.LookSeq, stringseq: MDSubr.StringSeq, trymesa: BOOL] RETURNS[parent: CARDINAL]={ top, bottom, i, source: CARDINAL; sfn: STRING ← [100]; stemp: STRING ← [50]; savenstringvars: CARDINAL; look, lookchild: MDSubr.Look; lookchildinx: CARDINAL; -- tlook: Look; CWF.SWF1[sfn,"%s.bcd"L,modulename]; [parent,] ← MDSubr.GetAnEntry[sfn,entryseq]; IF entryseq[parent].rule ~= NIL THEN RETURN[parent]; -- not new [look, entryseq[parent].lookinx] ← MDSubr.LookLook[sfn, lookseq]; -- entryseq[parent].lookinx is lookNil if not found -- tlook ← @lookseq[entryseq[parent].lookinx]; -- CWF.WF2["entry %s look %s\n"L, entryseq[parent].name, tlook.name]; -- CWF.WF2["Entry %s look %s\n"L, entryseq[parent].name, -- IF look ~= NIL THEN look.name ELSE "nil"L]; CWF.SWF1[sfn,IF trymesa THEN "%s.mesa"L ELSE "%s.config"L,modulename]; -- CWF.WF1["Try %s.\n"L,sfn]; [lookchild, lookchildinx] ← MDSubr.LookLook[sfn, lookseq]; -- this special test is needed if there is a .mesa in the look array -- but it is not on the disk and there is a .config also IF lookchild = NIL OR ((NOT lookchild.presentonlocaldisk)) THEN { CWF.SWF1[sfn,IF NOT trymesa THEN "%s.mesa"L ELSE "%s.config"L, modulename]; -- CWF.WF1["Try %s.\n"L,sfn]; -- CWF.WF0["."L]; [lookchild, lookchildinx] ← MDSubr.LookLook[sfn,lookseq]; }; IF lookchild = NIL THEN { [source,] ← MDSubr.GetAnEntry[sfn,entryseq]; MDSubr.AddToDepends[parent,source,entryseq]; entryseq[source].lookinx ← lookseq.size; lookchild ← MDSubr.AddToLook[sfn, lookseq]; CWF.SWF1[sfn,IF trymesa THEN "%s.mesa"L ELSE "%s.config"L,modulename]; [source,] ← MDSubr.GetAnEntry[sfn,entryseq]; MDSubr.AddToDepends[parent,source,entryseq]; entryseq[source].lookinx ← lookseq.size; lookchild ← MDSubr.AddToLook[sfn, lookseq]; RETURN[parent]; }; -- CWF.WF1["Found %s.\n"L, lookchild.name]; -- PrintLook[lookseq, patharray]; savenstringvars ← stringseq.size; bottom ← entryseq.size; IF NOT ConstructDepends[lookchild, parent, entryseq, stringseq] THEN RETURN[parent]; top ← entryseq.size; -- CWF.WF0["Added:\n"L]; -- FOR i IN [bottom .. top) DO -- CWF.WF1[" %s\n"L,entryseq[i].name]; -- ENDLOOP; [source,] ← MDSubr.GetAnEntry[sfn,entryseq]; entryseq[source].lookinx ← lookchildinx; MDSubr.AddToDepends[parent,source,entryseq]; IF Subr.EndsIn[sfn,".config"L] THEN entryseq[parent].rule ← MDSubr.GetBinderRule[stringseq, sfn, entryseq.entryzone] ELSE entryseq[parent].rule ← MDSubr.GetCompilerRule[stringseq, sfn, entryseq.entryzone]; FOR i IN [bottom .. top) DO Subr.strcpy[sto: stemp,sfrom: entryseq[i].name]; stemp.length ← stemp.length - 4; [] ← DependTree[stemp, entryseq, lookseq, stringseq, trymesa]; ENDLOOP; -- throw away extensions -- ResetStringVar[stringseq, savenstringvars]; RETURN[parent]; }; ConstructDepends: PROC[lookchild: MDSubr.Look, parent: CARDINAL, entryseq: MDSubr.EntrySeq, stringseq: MDSubr.StringSeq] RETURNS[proceed: BOOL]= { sfn: STRING ← [50]; sh: Stream.Handle; pmod: MDSubr.ModInfo; cap: File.Capability; Subr.strcpy[sfn, lookchild.name]; IF NOT lookchild.presentonlocaldisk THEN RETURN[FALSE]; cap ← Directory.UpdateDates[lookchild.cap, File.read]; sh ← FileStream.Create[cap]; -- allocate dynamically to avoid large frame, which must be resident pmod ← longzone.NEW[MDSubr.ModInfoRecord]; MDSubr.ParseObject[sh, parent, entryseq, pmod, NIL, stringseq, sfn]; longzone.FREE[@pmod]; Stream.Delete[sh]; RETURN[TRUE]; }; ResetStringVar: PROC[stringseq: MDSubr.StringSeq, savenstringvars: CARDINAL]={ FOR i: CARDINAL IN [savenstringvars .. stringseq.size) DO Subr.FreeString[stringseq[i].str]; Subr.FreeString[stringseq[i].val]; ENDLOOP; stringseq.size ← savenstringvars; }; SortEntries: PROC[entryseq: MDSubr.EntrySeq] = { IndexSeqRecord: TYPE = RECORD[ body: SEQUENCE maxsize: CARDINAL OF CARDINAL ]; index: LONG POINTER TO IndexSeqRecord; i,j: CARDINAL; EntryLessThan: PROC[left, right: CARDINAL] RETURNS [BOOL] = { s1: STRING ← [100]; s2: STRING ← [100]; IF entryseq[left].name = NIL OR entryseq[right].name = NIL THEN RETURN[FALSE]; Subr.strcpy[s1, entryseq[left].name]; Subr.strcpy[s2, entryseq[right].name]; RETURN[String.CompareStrings[s1, s2, TRUE] < 0]; }; EntryExch: PROC[left, right: CARDINAL] = { t: MDSubr.EntryRecord; i: CARDINAL; stuffl, stuffr: CARDINAL ← MDSubr.entNil; t ← entryseq[left]; entryseq[left] ← entryseq[right]; entryseq[right] ← t; FOR i IN [0..entryseq.size) DO IF index[i] = left THEN stuffr ← i; IF index[i] = right THEN stuffl ← i; ENDLOOP; IF stuffr = MDSubr.entNil OR stuffl = MDSubr.entNil THEN ERROR; index[stuffr] ← right; index[stuffl] ← left; }; index ← longzone.NEW[IndexSeqRecord[entryseq.size]]; FOR i IN [0..entryseq.size) DO index[i] ← i; ENDLOOP; TreeSort[entryseq, EntryLessThan, EntryExch]; FOR i IN [0.. entryseq.size ) DO j ← 0; WHILE entryseq[i].depends[j] ~= MDSubr.entNil DO entryseq[i].depends[j] ← index[entryseq[i].depends[j]]; j ← j + 1; ENDLOOP; ENDLOOP; longzone.FREE[@index]; }; TreeSort: PROC[entryseq: MDSubr.EntrySeq, LessThan: PROC[CARDINAL, CARDINAL] RETURNS[BOOL], Exch: PROC[CARDINAL, CARDINAL]] = { left, right: CARDINAL; siftUp: PROC[low, high: INTEGER] ={ k, son: INTEGER; left, right: CARDINAL; k ← low; DO IF 2*k>high THEN EXIT; left ← 2*k+1-1; right ← 2*k-1; IF 2*k+1>high OR LessThan[left, right] THEN son ← 2*k ELSE son ← 2*k+1; left ← son-1; right ← k-1; IF LessThan[left, right] THEN EXIT; Exch[left, right]; k ← son; ENDLOOP; }; FOR i: CARDINAL DECREASING IN [1..entryseq.size/2] DO siftUp[i,entryseq.size] ENDLOOP; FOR i: CARDINAL DECREASING IN [1..entryseq.size) DO left ← 0; right ← i; Exch[left, right]; siftUp[1,i] ENDLOOP; }; PrintMade: PROC[firstinx: CARDINAL, entryseq:MDSubr.EntrySeq, lookseq: MDSubr.LookSeq, all: BOOL] = { p: BOOL ← FALSE; HandleNode: PROC[parent: MDSubr.Entry] = { i: CARDINAL; child: MDSubr.Entry; stemp: STRING ← [100]; IF NOT parent.made OR parent.rule = NIL THEN RETURN; i ← 0; WHILE parent.depends[i] ~= MDSubr.entNil DO child ← @entryseq[parent.depends[i]]; IF Subr.EndsIn[child.name, "mesa"L] OR Subr.EndsIn[child.name, "config"L] THEN { IF NOT p THEN { CWF.WF0[ "\nThese files were compiled or bound:\n"L]; p ← TRUE; }; CWF.WF1[" %s"L, child.name]; Subr.strcpy[stemp, child.name]; CWF.WFCR[]; }; i ← i + 1; ENDLOOP; }; MDSubr.WalkTheGraph[firstinx, entryseq, HandleNode]; CWF.WFCR[]; }; -- if noextrastuff is TRUE, then the str is just written on line.cm -- if FALSE, str must be a complete command! -- CR not added to str WriteLineCm: PROC[str: LONG STRING, noextrastuff: BOOL] = { command: STRING ← [700]; printcmd: STRING ← [50]; sf: STRING ← [30]; Subr.strcpy[command, str]; MDSubr.StripFirstWord[command, sf]; -- puts first word into sf IF Subr.Prefix[sf, "compile"L] THEN { IF compstr = NIL THEN compstr ← Subr.AllocateString[3000]; LongString.AppendString[compstr, command ! LongString.StringBoundsFault => { CWF.WF0["Error - Command Line Too Long.\n"L]; CONTINUE; }]; Subr.strcpy[printcmd, "Compile.~"L]; } ELSE IF Subr.Prefix[sf, "bind"L] THEN { IF bindstr = NIL THEN bindstr ← Subr.AllocateString[3000]; LongString.AppendString[bindstr, command ! LongString.StringBoundsFault => { CWF.WF0["Error - Command Line Too Long.\n"L]; CONTINUE; }]; Subr.strcpy[printcmd, "Bind.~"L]; } ELSE CWF.WF1["Error - can only compile or bind= '%s'\n"L, sf]; CWF.WF2["%s %s\n"L, printcmd, command]; }; CloseLineCm: PROC = { IF compstr ~= NIL THEN Subr.FreeString[compstr]; IF bindstr ~= NIL THEN Subr.FreeString[bindstr]; compstr ← bindstr ← NIL; }; MAXRECUR: CARDINAL = 30; -- this crucial proc is called in at least three modes -- 1. to simply set need true for all children of some parent -- (forreal: FALSE, justmark: TRUE) -- 2. to set the need flag if we'll actually need to compile this -- (forreal: FALSE, justmark: FALSE, localonly: FALSE) -- 3. to actually make the dependencies if necessary -- (forreal: TRUE) -- -- uses look array -- before this is called if forreal and presentonlocaldisk -- all the localversions must be filled in (perhaps by BringInRemoteFiles) Decide: PROC[forreal: BOOL, parinx: CARDINAL, entryseq: MDSubr.EntrySeq, lookseq: MDSubr.LookSeq, justmark, forcemake: BOOL] RETURNS[LONG CARDINAL] = { FOR i: CARDINAL IN [0 .. entryseq.size) DO entryseq[i].visited ← FALSE; ENDLOOP; FOR i: CARDINAL IN [0 .. lookseq.size) DO lookseq[i].besttime ← MDSubr.versionNil; ENDLOOP; RETURN[InternalDecide[forreal, parinx, entryseq, lookseq, justmark, forcemake]]; }; InternalDecide: PROC[forreal: BOOL, parinx: CARDINAL, entryseq: MDSubr.EntrySeq, lookseq: MDSubr.LookSeq, justmark, forcemake: BOOL] RETURNS[LONG CARDINAL] = { parenttime, besttime: LONG CARDINAL; childinx: CARDINAL; parent, child: MDSubr.Entry; i: CARDINAL; isbcd, needmake: BOOL; lookinx: CARDINAL; nowhere: BOOL; look, lookchild: MDSubr.Look; localerr: BOOL; IF parinx = MDSubr.entNil THEN { CWF.WF0["Error - Decide - parinx is nil\n"L]; RETURN[0]; }; parent ← @entryseq[parinx]; lookinx ← parent.lookinx; IF lookinx = MDSubr.lookNil THEN { CWF.WF0["bad lookinx\n"L]; RETURN[0]; }; look ← @lookseq[lookinx]; IF NOT LongString.EquivalentString[look.name, parent.name] THEN { CWF.WF2["Oops names don't agree - %s vs. %s.\n"L,look.name, parent.name]; RETURN[0]; }; -- if nowhere is TRUE then we must make it nowhere ← NOT look.presentonlocaldisk; besttime ← look.localversion; -- leaves are given 0 time -- not on local or remote disk => present time parenttime←IF NOT nowhere THEN besttime ELSE (IF parent.depends[0] = MDSubr.entNil AND parent.rule = NIL THEN MDSubr.versionUnknown ELSE presenttime); look.need ← TRUE; -- CWF.WF1["need %s\n"L, look.name]; -- a parent with no children without a rule just gives us info IF parent.depends[0] = MDSubr.entNil AND parent.rule = NIL THEN RETURN[parenttime]; -- a parent with no children with a rule will always need to make itself recur ← recur + 1; IF recur >= MAXRECUR THEN { recur ← 32767; CWF.WF1["Error - '%s' seems to be in a recursive chain\n"L, parent.name]; Subr.errorflg ← TRUE; RETURN[MDSubr.versionUnknown]; }; isbcd ← Subr.EndsIn[parent.name, ".bcd"L]; i← 0; needmake ← (parent.depends[0] = MDSubr.entNil) OR nowhere; IF forcemake THEN needmake ← TRUE; localerr ← FALSE; WHILE parent.depends[i] ~= MDSubr.entNil DO childinx ← parent.depends[i]; child ← @entryseq[childinx]; lookchild ← @lookseq[child.lookinx]; IF lookchild.besttime = MDSubr.versionNil THEN lookchild.besttime ← InternalDecide[forreal,childinx,entryseq, lookseq, justmark, forcemake]; IF isbcd AND forreal AND (Subr.EndsIn[child.name,".mesa"L] OR Subr.EndsIn[child.name, ".config"L]) AND look.presentonlocaldisk AND lookchild.presentonlocaldisk THEN { IF MustComp[parinx, childinx, entryseq, lookseq] THEN needmake ← TRUE; } ELSE IF lookchild.besttime >= parenttime THEN needmake ← TRUE; IF forreal AND lookchild.besttime = MDSubr.versionUnknown THEN BEGIN IF NOT child.visited THEN { CWF.WF1["\nError - Can't find '%s'\n"L,child.name]; Subr.errorflg ← TRUE; }; localerr ← TRUE; child.visited ← TRUE; END; i ← i + 1; ENDLOOP; -- if we haven't made any children then we really don't need to look at them IF NOT needmake AND NOT justmark THEN { i← 0; WHILE parent.depends[i] ~= MDSubr.entNil DO childinx ← parent.depends[i]; lookchild ← @lookseq[entryseq[childinx].lookinx]; lookchild.need ← FALSE; i ← i + 1; ENDLOOP; }; IF needmake THEN { parenttime ← presenttime; IF forreal THEN { -- WF.WF1["About to make '%s'\n"L,parent.name]; IF NOT parent.made AND parent.rule ~= NIL THEN { WriteLineCm[parent.rule, FALSE]; }; parent.made ← TRUE; }; }; recur ← recur - 1; RETURN[parenttime]; }; -- returns TRUE if the source must be recompiled -- will return TRUE if the create time in the BCD is -- different from the .mesa time, even if the create time is "better" MustComp: PROC[parinx, childinx: CARDINAL, entryseq: MDSubr.EntrySeq, lookseq: MDSubr.LookSeq] RETURNS[mustcomp: BOOL] = { sourcetimecompiled: LONG CARDINAL; wantconfig, isconfig: BOOL; look, lookchild: MDSubr.Look; -- time: LONG CARDINAL; -- CWF.WF1["file %s: "L, entryseq[parinx].name]; look ← @lookseq[entryseq[parinx].lookinx]; lookchild ← @lookseq[entryseq[childinx].lookinx]; [sourcetimecompiled, wantconfig] ← BcdCap[look.cap, look.name, look.localversion]; -- IF wantconfig THEN CWF.WF0["wantconfig "L]; isconfig ← Subr.EndsIn[entryseq[childinx].name, ".config"L]; -- IF isconfig THEN CWF.WF0["isconfig "L]; IF (isconfig AND NOT wantconfig) OR (NOT isconfig AND wantconfig) THEN RETURN[TRUE]; mustcomp ← lookchild.localversion ~= sourcetimecompiled; -- time ← lookchild.localversion; -- IF mustcomp THEN -- CWF.WF2["mustcomp time in bcd is %lt, time of .mesa is %lt\n"L, -- @bcdtime, @time]; }; -- space is passed in to avoid extra Space.Create's BcdCap: PROC[cap: File.Capability, name: LONG STRING, time: LONG CARDINAL] RETURNS[sourcecreatetime: LONG CARDINAL, isconfig: BOOL] = { bcd: LONG POINTER TO BcdDefs.BCD; Space.Map[space: bcdspace, window: [file: cap, base: 1]]; bcd ← Space.LongPointer[bcdspace]; sourcecreatetime ← bcd.sourceVersion.time; isconfig ← (bcd.nConfigs > 0); Space.Unmap[bcdspace]; }; MyConfirm: PROC[in, out: IO.Handle, data: REF ANY, msg: Rope.ROPE, dch: CHAR] RETURNS[CHAR] = { value: ATOM; value ← UserExec.AskUser[msg: msg, exec: NARROW[data], keyList: LIST[$Yes, $No]]; -- order is important SELECT value FROM $No => RETURN['n]; $Yes => RETURN['y]; ENDCASE => ERROR; }; MyPutChar: PROC[ch: CHAR] = { stdout.PutChar[ch]; }; UserExec.RegisterCommand["MakeConfig.~", Main]; }.