| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104 |
- MODULE M2make;
- (* M2make: build-order front-end for the M2 and GNU Modula-2 compilers.
- Usage: M2make [-n] [-v] [-c m2|gm2] [-I dir] [-o out]
- [--m2bin path] [--shim path] main.mod
- Follows IMPORT/FROM imports from main.mod transitively, sorts the
- modules topologically, skips the build when the output is newer
- than every source, otherwise invokes the selected compiler.
- Exits 0 on success (or nothing to do), 1 on build failure,
- 2 on usage errors.
- Portable source: compiles with gm2 -fiso and with the V3 M2
- driver. Only FileIO (console) and M2makeOS (files/time/exec)
- are imported; all string handling is NUL-based and local, since
- the two compilers' FileIO string helpers differ. *)
- IMPORT FileIO, M2makeOS;
- CONST
- MaxMods = 256;
- MaxDeps = 64;
- MaxDirs = 16;
- MaxArgs = 64;
- StackCap = 264;
- NameStride = 64;
- PathStride = 256;
- TYPE
- Name = ARRAY [0..63] OF CHAR;
- Path = ARRAY [0..255] OF CHAR;
- Line = ARRAY [0..2047] OF CHAR;
- Cmd = ARRAY [0..2047] OF CHAR;
- VAR
- nMods: CARDINAL;
- (* Flat 1D pools: every table is a single indexed array (a style
- choice -- both compilers handle nested arrays now). *)
- namePool: ARRAY [0..16383] OF CHAR;
- srcPool: ARRAY [0..65535] OF CHAR;
- defPool: ARRAY [0..65535] OF CHAR;
- dirPool: ARRAY [0..4095] OF CHAR;
- modScanned: ARRAY [0..255] OF BOOLEAN;
- modExternal: ARRAY [0..255] OF BOOLEAN;
- modIsMain: ARRAY [0..255] OF BOOLEAN;
- nDep: ARRAY [0..255] OF CARDINAL;
- depTab: ARRAY [0..16383] OF INTEGER;
- state: ARRAY [0..255] OF CARDINAL;
- childPos: ARRAY [0..255] OF CARDINAL;
- order: ARRAY [0..255] OF CARDINAL;
- nOrder: CARDINAL;
- dryRun, verbose, useGm2: BOOLEAN;
- outGiven: BOOLEAN;
- mainPath, outName, m2bin, shimPath: Path;
- extraC: Path; (* extra C file linked in m2 mode (may be empty) *)
- nDirs: CARDINAL;
- progName: Name;
- wantProg: BOOLEAN;
- mainIdx: INTEGER;
- inComment: CARDINAL;
- gPendingFrom, gInImport, gSawHead, gWantName, gFromSeen: BOOLEAN;
- tmpA, tmpB: Path;
- tmpN: Name;
- tmpP, tmpQ: Path;
- tmpCmd: Cmd;
- segA: Cmd;
- (* ---------------- zeroed (NUL) string utilities ---------------- *)
- PROCEDURE Len(s: ARRAY OF CHAR): CARDINAL;
- (* NUL scan. Both compilers short-circuit AND/OR now, so the index
- guard protects the access; the bound is the string's HIGH + 1
- (V3 sting literals carry no NUL padding, fixed buffers do). *)
- VAR i: CARDINAL;
- BEGIN
- i := 0;
- WHILE (i <= HIGH(s)) AND (s[i] # CHR(0)) DO INC(i) END;
- RETURN i
- END Len;
- PROCEDURE Zero(VAR s: ARRAY OF CHAR);
- VAR i: CARDINAL;
- BEGIN
- i := 0;
- WHILE i <= HIGH(s) DO s[i] := CHR(0); INC(i) END
- END Zero;
- PROCEDURE Copy(src: ARRAY OF CHAR; VAR dst: ARRAY OF CHAR);
- VAR i, n: CARDINAL;
- BEGIN
- Zero(dst);
- n := Len(src);
- IF n > HIGH(dst) THEN n := HIGH(dst) END;
- i := 0;
- WHILE i < n DO dst[i] := src[i]; INC(i) END
- END Copy;
- PROCEDURE Cmp(a, b: ARRAY OF CHAR): INTEGER;
- (* Length-aware comparison: also correct when one side is a string
- literal (no NUL padding under V3). *)
- VAR i, na, nb: CARDINAL;
- BEGIN
- na := Len(a);
- nb := Len(b);
- i := 0;
- WHILE (i < na) AND (i < nb) DO
- IF a[i] # b[i] THEN
- IF a[i] < b[i] THEN RETURN -1 ELSE RETURN 1 END
- END;
- INC(i)
- END;
- IF na < nb THEN RETURN -1 END;
- IF na > nb THEN RETURN 1 END;
- RETURN 0
- END Cmp;
- PROCEDURE Cat(a, b: ARRAY OF CHAR; VAR dst: ARRAY OF CHAR);
- (* dst := a + b (NUL-terminated). Short-circuit AND keeps the index
- guard ahead of every access. *)
- VAR i, k: CARDINAL;
- BEGIN
- Zero(dst);
- k := 0; i := 0;
- WHILE (i <= HIGH(a)) AND (k <= HIGH(dst)) AND (a[i] # CHR(0)) DO
- dst[k] := a[i]; INC(k); INC(i)
- END;
- i := 0;
- WHILE (i <= HIGH(b)) AND (k <= HIGH(dst)) AND (b[i] # CHR(0)) DO
- dst[k] := b[i]; INC(k); INC(i)
- END
- END Cat;
- PROCEDURE Sub(src: ARRAY OF CHAR; start, count: CARDINAL;
- VAR dst: ARRAY OF CHAR);
- VAR j: CARDINAL;
- BEGIN
- Zero(dst);
- j := 0;
- WHILE (j < count) AND (start + j <= HIGH(src)) AND (j <= HIGH(dst))
- AND (src[start + j] # CHR(0)) DO
- dst[j] := src[start + j]; INC(j)
- END
- END Sub;
- PROCEDURE Lower(src: ARRAY OF CHAR; VAR dst: ARRAY OF CHAR);
- VAR i: CARDINAL; ch: CHAR;
- BEGIN
- Zero(dst);
- i := 0;
- WHILE (i <= HIGH(src)) AND (i <= HIGH(dst)) AND (src[i] # CHR(0)) DO
- ch := src[i];
- IF (ch >= "A") AND (ch <= "Z") THEN
- ch := CHR(ORD(ch) - ORD("A") + ORD("a"))
- END;
- dst[i] := ch; INC(i)
- END
- END Lower;
- PROCEDURE IsLetter(ch: CHAR): BOOLEAN;
- BEGIN
- RETURN ((ch >= "A") AND (ch <= "Z"))
- OR ((ch >= "a") AND (ch <= "z"))
- END IsLetter;
- PROCEDURE IsDig(ch: CHAR): BOOLEAN;
- BEGIN
- RETURN (ch >= "0") AND (ch <= "9")
- END IsDig;
- (* ---------------- flat-pool accessors ---------------- *)
- (* Single-indexed pool traffic only (see VAR comment). *)
- PROCEDURE GetName(m: CARDINAL; VAR dst: Name);
- VAR k: CARDINAL;
- BEGIN
- k := 0;
- WHILE k <= HIGH(dst) DO
- dst[k] := namePool[m * NameStride + k];
- INC(k)
- END
- END GetName;
- PROCEDURE PutName(m: CARDINAL; src: Name);
- VAR k: CARDINAL;
- BEGIN
- k := 0;
- WHILE k <= HIGH(src) DO
- namePool[m * NameStride + k] := src[k];
- INC(k)
- END
- END PutName;
- PROCEDURE GetSrc(m: CARDINAL; VAR dst: Path);
- VAR k: CARDINAL;
- BEGIN
- k := 0;
- WHILE k <= HIGH(dst) DO
- dst[k] := srcPool[m * PathStride + k];
- INC(k)
- END
- END GetSrc;
- PROCEDURE PutSrc(m: CARDINAL; src: Path);
- VAR k: CARDINAL;
- BEGIN
- k := 0;
- WHILE k <= HIGH(src) DO
- srcPool[m * PathStride + k] := src[k];
- INC(k)
- END
- END PutSrc;
- PROCEDURE GetDef(m: CARDINAL; VAR dst: Path);
- VAR k: CARDINAL;
- BEGIN
- k := 0;
- WHILE k <= HIGH(dst) DO
- dst[k] := defPool[m * PathStride + k];
- INC(k)
- END
- END GetDef;
- PROCEDURE PutDef(m: CARDINAL; src: Path);
- VAR k: CARDINAL;
- BEGIN
- k := 0;
- WHILE k <= HIGH(src) DO
- defPool[m * PathStride + k] := src[k];
- INC(k)
- END
- END PutDef;
- PROCEDURE GetDir(m: CARDINAL; VAR dst: Path);
- VAR k: CARDINAL;
- BEGIN
- k := 0;
- WHILE k <= HIGH(dst) DO
- dst[k] := dirPool[m * PathStride + k];
- INC(k)
- END
- END GetDir;
- PROCEDURE PutDir(m: CARDINAL; src: Path);
- VAR k: CARDINAL;
- BEGIN
- k := 0;
- WHILE k <= HIGH(src) DO
- dirPool[m * PathStride + k] := src[k];
- INC(k)
- END
- END PutDir;
- (* ---------------- console output ---------------- *)
- PROCEDURE Out(s: ARRAY OF CHAR);
- (* Character by character: V3 FileIO.WriteString emits the whole
- descriptor capacity (including NUL padding), so loop instead. *)
- VAR i, n: CARDINAL;
- BEGIN
- n := Len(s);
- i := 0;
- WHILE i < n DO
- FileIO.Write(FileIO.StdOut, s[i]);
- INC(i)
- END
- END Out;
- PROCEDURE OutLn;
- BEGIN
- FileIO.WriteLn(FileIO.StdOut)
- END OutLn;
- PROCEDURE OutInt(i: INTEGER);
- BEGIN
- FileIO.WriteInt(FileIO.StdOut, i, 0)
- END OutInt;
- PROCEDURE Die(s: ARRAY OF CHAR; code: INTEGER);
- BEGIN
- Out(s); OutLn;
- M2makeOS.ExitNow(code)
- END Die;
- PROCEDURE Usage;
- BEGIN
- Out("Usage: M2make [-n] [-v] [-c m2|gm2] [-I dir] [-o out]"); OutLn;
- Out(" [--m2bin path] [--shim path] [--extra path] main.mod"); OutLn
- END Usage;
- (* ---------------- module table ---------------- *)
- PROCEDURE FindMod(n: ARRAY OF CHAR): INTEGER;
- VAR i: CARDINAL;
- BEGIN
- i := 0;
- WHILE i < nMods DO
- GetName(i, tmpN);
- IF Cmp(tmpN, n) = 0 THEN RETURN VAL(INTEGER, i) END;
- INC(i)
- END;
- RETURN -1
- END FindMod;
- PROCEDURE AddMod(n: ARRAY OF CHAR): INTEGER;
- BEGIN
- IF FindMod(n) >= 0 THEN RETURN FindMod(n) END;
- IF nMods >= MaxMods THEN Die("M2make: too many modules", 1) END;
- Copy(n, tmpN);
- PutName(nMods, tmpN);
- Zero(tmpP);
- PutSrc(nMods, tmpP);
- PutDef(nMods, tmpP);
- modScanned[nMods] := FALSE;
- modExternal[nMods] := FALSE;
- modIsMain[nMods] := FALSE;
- nDep[nMods] := 0;
- INC(nMods);
- RETURN VAL(INTEGER, nMods - 1)
- END AddMod;
- PROCEDURE AddDep(m, d: INTEGER);
- VAR mc: CARDINAL; k: CARDINAL;
- BEGIN
- IF (m < 0) OR (d < 0) OR (m = d) THEN RETURN END;
- mc := VAL(CARDINAL, m);
- k := 0;
- WHILE k < nDep[mc] DO
- IF depTab[mc * MaxDeps + k] = d THEN RETURN END;
- INC(k)
- END;
- IF nDep[mc] >= MaxDeps THEN Die("M2make: too many imports", 1) END;
- depTab[mc * MaxDeps + nDep[mc]] := d;
- INC(nDep[mc])
- END AddDep;
- PROCEDURE AddDepName(midx: INTEGER; n: ARRAY OF CHAR);
- VAR d: INTEGER;
- BEGIN
- d := AddMod(n);
- AddDep(midx, d)
- END AddDepName;
- (* ---------------- path handling ---------------- *)
- PROCEDURE DirOf(p: ARRAY OF CHAR; VAR dir: ARRAY OF CHAR);
- VAR i, n, k: CARDINAL;
- BEGIN
- Zero(dir);
- n := Len(p);
- k := 0;
- i := 0;
- WHILE i < n DO
- IF p[i] = "/" THEN k := i + 1 END;
- INC(i)
- END;
- i := 0;
- WHILE (i < k) AND (i <= HIGH(dir)) DO
- dir[i] := p[i]; INC(i)
- END
- END DirOf;
- PROCEDURE TryVariant(dir, base, ext: ARRAY OF CHAR;
- VAR out: ARRAY OF CHAR);
- VAR full: Path;
- BEGIN
- Zero(out);
- IF Len(dir) = 0 THEN
- Cat(base, ext, full)
- ELSE
- IF Len(dir) + 1 + Len(base) + Len(ext) > HIGH(full) THEN RETURN END;
- IF dir[Len(dir) - 1] = "/" THEN
- Cat(dir, base, tmpA);
- Cat(tmpA, ext, full)
- ELSE
- Cat(dir, "/", tmpA);
- Cat(tmpA, base, tmpB);
- Cat(tmpB, ext, full)
- END
- END;
- IF M2makeOS.FileExists(full) THEN Copy(full, out) END
- END TryVariant;
- PROCEDURE ResolveModule(n: ARRAY OF CHAR; VAR dp, mp: Path): BOOLEAN;
- VAR di: CARDINAL; low: Name; cand: Path; found: BOOLEAN;
- BEGIN
- Zero(dp);
- Zero(mp);
- Lower(n, low);
- found := FALSE;
- di := 0;
- WHILE di < nDirs DO
- GetDir(di, tmpQ);
- TryVariant(tmpQ, n, ".def", cand);
- IF Len(cand) > 0 THEN Copy(cand, dp); found := TRUE END;
- TryVariant(tmpQ, n, ".mod", cand);
- IF Len(cand) > 0 THEN Copy(cand, mp); found := TRUE END;
- TryVariant(tmpQ, low, ".def", cand);
- IF (Len(dp) = 0) AND (Len(cand) > 0) THEN
- Copy(cand, dp); found := TRUE
- END;
- TryVariant(tmpQ, low, ".mod", cand);
- IF (Len(mp) = 0) AND (Len(cand) > 0) THEN
- Copy(cand, mp); found := TRUE
- END;
- INC(di)
- END;
- RETURN found
- END ResolveModule;
- (* ---------------- import scanner ---------------- *)
- PROCEDURE HandleTok(tok: ARRAY OF CHAR; midx: INTEGER);
- BEGIN
- IF Cmp(tok, "FROM") = 0 THEN
- gPendingFrom := TRUE;
- gInImport := FALSE;
- gSawHead := FALSE;
- gWantName := FALSE;
- gFromSeen := FALSE;
- RETURN
- END;
- IF Cmp(tok, "IMPORT") = 0 THEN
- IF gPendingFrom OR gFromSeen THEN
- (* FROM M IMPORT ... : following names are items, not modules *)
- gPendingFrom := FALSE;
- gFromSeen := FALSE
- ELSE gInImport := TRUE
- END;
- gSawHead := FALSE;
- gWantName := FALSE;
- RETURN
- END;
- IF Cmp(tok, "DEFINITION") = 0 THEN
- gSawHead := TRUE;
- gPendingFrom := FALSE;
- gInImport := FALSE;
- gWantName := FALSE;
- gFromSeen := FALSE;
- RETURN
- END;
- IF Cmp(tok, "IMPLEMENTATION") = 0 THEN
- gSawHead := TRUE;
- gPendingFrom := FALSE;
- gInImport := FALSE;
- gWantName := FALSE;
- gFromSeen := FALSE;
- RETURN
- END;
- IF Cmp(tok, "MODULE") = 0 THEN
- IF gSawHead THEN gWantName := FALSE
- ELSE gWantName := TRUE
- END;
- gSawHead := FALSE;
- gPendingFrom := FALSE;
- gInImport := FALSE;
- gFromSeen := FALSE;
- RETURN
- END;
- IF gPendingFrom THEN
- AddDepName(midx, tok);
- gPendingFrom := FALSE;
- gFromSeen := TRUE;
- gSawHead := FALSE;
- RETURN
- END;
- IF gInImport THEN
- AddDepName(midx, tok);
- gSawHead := FALSE;
- gFromSeen := FALSE;
- RETURN
- END;
- IF gWantName THEN
- gWantName := FALSE;
- IF wantProg AND (midx = mainIdx) THEN
- Copy(tok, progName);
- wantProg := FALSE
- END;
- gSawHead := FALSE;
- gFromSeen := FALSE;
- RETURN
- END;
- gSawHead := FALSE;
- gFromSeen := FALSE
- END HandleTok;
- PROCEDURE ScanLine(line: ARRAY OF CHAR; midx: INTEGER);
- VAR i, n, k: CARDINAL; ch, qc: CHAR;
- inStr: BOOLEAN; tok: Name;
- BEGIN
- n := Len(line);
- i := 0;
- inStr := FALSE;
- qc := CHR(0);
- WHILE i < n DO
- ch := line[i];
- IF inStr THEN
- IF ch = qc THEN
- IF (i + 1 < n) AND (line[i + 1] = qc) THEN i := i + 2
- ELSE inStr := FALSE; INC(i)
- END
- ELSE INC(i)
- END
- ELSIF inComment > 0 THEN
- IF (ch = "(") AND (i + 1 < n) AND (line[i + 1] = "*") THEN
- INC(inComment); i := i + 2
- ELSIF (ch = "*") AND (i + 1 < n) AND (line[i + 1] = ")") THEN
- DEC(inComment); i := i + 2
- ELSE INC(i)
- END
- ELSE
- IF (ch = "(") AND (i + 1 < n) AND (line[i + 1] = "*") THEN
- INC(inComment); i := i + 2
- ELSIF (ch = "/") AND (i + 1 < n) AND (line[i + 1] = "/") THEN
- i := n
- ELSIF (ch = "'") OR (ch = '"') THEN
- inStr := TRUE; qc := ch; INC(i)
- ELSIF ch = ";" THEN
- gPendingFrom := FALSE;
- gInImport := FALSE;
- gSawHead := FALSE;
- gWantName := FALSE;
- gFromSeen := FALSE;
- INC(i)
- ELSIF IsLetter(ch) THEN
- k := i;
- WHILE (k < n) AND (IsLetter(line[k]) OR IsDig(line[k])) DO
- INC(k)
- END;
- Sub(line, i, k - i, tok);
- i := k;
- HandleTok(tok, midx)
- ELSE INC(i)
- END
- END
- END
- END ScanLine;
- PROCEDURE ScanFile(path: ARRAY OF CHAR; midx: INTEGER);
- VAR h: M2makeOS.RdFile; line: Line; ok: BOOLEAN;
- BEGIN
- h := M2makeOS.OpenRead(path);
- IF h = NIL THEN
- Cat("M2make: cannot open ", path, tmpCmd);
- Die(tmpCmd, 1)
- END;
- LOOP
- Zero(line);
- ok := M2makeOS.ReadLine(h, line, HIGH(line) + 1);
- IF NOT ok THEN EXIT END;
- ScanLine(line, midx)
- END;
- M2makeOS.CloseRead(h)
- END ScanFile;
- PROCEDURE ScanModule(m: CARDINAL);
- VAR dp, mp: Path;
- BEGIN
- IF NOT modIsMain[m] THEN
- Zero(dp);
- Zero(mp);
- GetName(m, tmpN);
- IF NOT ResolveModule(tmpN, dp, mp) THEN
- modExternal[m] := TRUE;
- modScanned[m] := TRUE;
- IF verbose THEN
- Out("M2make: external module ");
- Out(tmpN);
- OutLn
- END;
- RETURN
- END;
- Copy(dp, tmpP);
- PutDef(m, tmpP);
- Copy(mp, tmpP);
- PutSrc(m, tmpP)
- END;
- inComment := 0;
- gPendingFrom := FALSE;
- gInImport := FALSE;
- gSawHead := FALSE;
- gWantName := FALSE;
- gFromSeen := FALSE;
- GetDef(m, tmpP);
- IF Len(tmpP) > 0 THEN ScanFile(tmpP, VAL(INTEGER, m)) END;
- GetSrc(m, tmpP);
- IF Len(tmpP) > 0 THEN ScanFile(tmpP, VAL(INTEGER, m)) END;
- modScanned[m] := TRUE
- END ScanModule;
- PROCEDURE ResolveAll;
- VAR i: CARDINAL;
- BEGIN
- i := 0;
- WHILE i < nMods DO
- IF NOT modScanned[i] THEN ScanModule(i) END;
- INC(i)
- END
- END ResolveAll;
- (* ---------------- topological sort (iterative DFS) ---------------- *)
- PROCEDURE TopoSort;
- VAR stack: ARRAY [0..263] OF CARDINAL;
- top, cur: CARDINAL; d: INTEGER; dd: CARDINAL; i: CARDINAL;
- BEGIN
- i := 0;
- WHILE i < nMods DO
- state[i] := 0; childPos[i] := 0; INC(i)
- END;
- nOrder := 0;
- top := 0;
- stack[0] := VAL(CARDINAL, mainIdx);
- state[VAL(CARDINAL, mainIdx)] := 1;
- LOOP
- cur := stack[top];
- IF childPos[cur] < nDep[cur] THEN
- d := depTab[cur * MaxDeps + childPos[cur]];
- INC(childPos[cur]);
- IF d < 0 THEN Die("M2make: bad dependency", 1) END;
- dd := VAL(CARDINAL, d);
- IF NOT modExternal[dd] THEN
- IF state[dd] = 0 THEN
- state[dd] := 1;
- INC(top);
- IF top > HIGH(stack) THEN Die("M2make: stack overflow", 1) END;
- stack[top] := dd
- ELSIF state[dd] = 1 THEN
- GetName(dd, tmpN);
- Out("M2make: cyclic import involving ");
- Out(tmpN);
- OutLn;
- M2makeOS.ExitNow(1)
- END
- END
- ELSE
- state[cur] := 2;
- order[nOrder] := cur;
- INC(nOrder);
- IF top = 0 THEN EXIT END;
- DEC(top)
- END
- END
- END TopoSort;
- (* ---------------- staleness ---------------- *)
- PROCEDURE SrcNewer(src: ARRAY OF CHAR; outT: LONGINT): BOOLEAN;
- VAR t: LONGINT;
- BEGIN
- IF Len(src) = 0 THEN RETURN FALSE END;
- IF NOT M2makeOS.FileMTime(src, t) THEN RETURN TRUE END;
- RETURN t > outT
- END SrcNewer;
- PROCEDURE NeedBuild(out: ARRAY OF CHAR; VAR missing: BOOLEAN): BOOLEAN;
- VAR outT: LONGINT; k: CARDINAL; m: CARDINAL;
- BEGIN
- missing := FALSE;
- IF NOT M2makeOS.FileExists(out) THEN
- missing := TRUE;
- RETURN TRUE
- END;
- IF NOT M2makeOS.FileMTime(out, outT) THEN RETURN TRUE END;
- k := 0;
- WHILE k < nOrder DO
- m := order[k];
- GetDef(m, tmpP);
- IF SrcNewer(tmpP, outT) THEN RETURN TRUE END;
- GetSrc(m, tmpP);
- IF SrcNewer(tmpP, outT) THEN RETURN TRUE END;
- INC(k)
- END;
- RETURN FALSE
- END NeedBuild;
- (* ---------------- command construction ---------------- *)
- PROCEDURE AppendTok(VAR cmd: ARRAY OF CHAR; tok: ARRAY OF CHAR);
- BEGIN
- IF Len(cmd) > 0 THEN
- Cat(cmd, " ", segA);
- Copy(segA, cmd)
- END;
- Cat(cmd, tok, segA);
- Copy(segA, cmd)
- END AppendTok;
- PROCEDURE IFlags(VAR flags: ARRAY OF CHAR);
- VAR di: CARDINAL;
- BEGIN
- Zero(flags);
- di := 0;
- WHILE di < nDirs DO
- GetDir(di, tmpP);
- IF Len(tmpP) > 0 THEN
- AppendTok(flags, "-I");
- AppendTok(flags, tmpP)
- END;
- INC(di)
- END
- END IFlags;
- PROCEDURE AppendCh(VAR s: ARRAY OF CHAR; ch: CHAR);
- VAR n: CARDINAL;
- BEGIN
- n := Len(s);
- IF n <= HIGH(s) THEN
- s[n] := ch;
- IF n + 1 <= HIGH(s) THEN s[n + 1] := CHR(0) END
- END
- END AppendCh;
- PROCEDURE BaseObj(src: ARRAY OF CHAR; VAR obj: ARRAY OF CHAR);
- (* "some/dir/Calc.mod" -> "Calc.o" (gm2 -c drops objects in the cwd). *)
- VAR i, n, slash, dot, k: CARDINAL;
- BEGIN
- Zero(obj);
- n := Len(src);
- slash := 0;
- i := 0;
- WHILE i < n DO
- IF src[i] = "/" THEN slash := i + 1 END;
- INC(i)
- END;
- dot := n;
- i := slash;
- WHILE i < n DO
- IF src[i] = "." THEN dot := i END;
- INC(i)
- END;
- IF dot <= slash THEN dot := n END;
- k := 0;
- i := slash;
- WHILE (i < dot) AND (k <= HIGH(obj)) DO
- obj[k] := src[i]; INC(k); INC(i)
- END;
- Cat(obj, ".o", tmpA);
- Copy(tmpA, obj)
- END BaseObj;
- PROCEDURE ObjStale(m: CARDINAL; obj: ARRAY OF CHAR): BOOLEAN;
- VAR ot: LONGINT;
- BEGIN
- IF NOT M2makeOS.FileExists(obj) THEN RETURN TRUE END;
- IF NOT M2makeOS.FileMTime(obj, ot) THEN RETURN TRUE END;
- GetDef(m, tmpP);
- IF SrcNewer(tmpP, ot) THEN RETURN TRUE END;
- GetSrc(m, tmpP);
- IF SrcNewer(tmpP, ot) THEN RETURN TRUE END;
- RETURN FALSE
- END ObjStale;
- PROCEDURE ObjNewerThan(obj, out: ARRAY OF CHAR;
- outT: LONGINT): BOOLEAN;
- VAR t: LONGINT;
- BEGIN
- IF Len(obj) = 0 THEN RETURN FALSE END;
- IF NOT M2makeOS.FileMTime(obj, t) THEN RETURN TRUE END;
- RETURN t > outT
- END ObjNewerThan;
- PROCEDURE CheckLen(cmd: ARRAY OF CHAR);
- BEGIN
- IF Len(cmd) + 32 > HIGH(cmd) THEN
- Die("M2make: command too long", 1)
- END
- END CheckLen;
- PROCEDURE AppendCmd(VAR cmd: ARRAY OF CHAR; seg: ARRAY OF CHAR);
- BEGIN
- IF Len(cmd) > 0 THEN
- Cat(cmd, " && ", segA);
- Copy(segA, cmd)
- END;
- Cat(cmd, seg, segA);
- Copy(segA, cmd)
- END AppendCmd;
- PROCEDURE AppendSeg(VAR cmd: ARRAY OF CHAR; seg: ARRAY OF CHAR);
- (* Direct concatenation (segments carry their own spacing).
- segA is the dedicated Cmd-sized scratch: always distinct from
- cmd and seg, and big enough for whole command lines. *)
- BEGIN
- Cat(cmd, seg, segA);
- Copy(segA, cmd)
- END AppendSeg;
- PROCEDURE BuildM2(out: ARRAY OF CHAR);
- VAR cmd: Cmd; k: CARDINAL; m: CARDINAL; rc: INTEGER;
- BEGIN
- Zero(cmd);
- AppendSeg(cmd, "mkdir -p gen_ssa && ");
- AppendSeg(cmd, m2bin);
- k := 0;
- WHILE k < nOrder DO
- m := order[k];
- GetDef(m, tmpP);
- IF Len(tmpP) > 0 THEN AppendTok(cmd, tmpP) END;
- GetSrc(m, tmpP);
- IF Len(tmpP) > 0 THEN AppendTok(cmd, tmpP) END;
- INC(k)
- END;
- AppendSeg(cmd, " 2>&1 | tee m2make.log | grep -q Parsed");
- AppendSeg(cmd, " && qbe -o gen_ssa/");
- AppendSeg(cmd, progName);
- AppendSeg(cmd, ".s gen_ssa/");
- AppendSeg(cmd, progName);
- AppendSeg(cmd, ".ssa");
- AppendSeg(cmd, " && cc gen_ssa/");
- AppendSeg(cmd, progName);
- AppendSeg(cmd, ".s ");
- AppendTok(cmd, shimPath);
- IF Len(extraC) > 0 THEN AppendTok(cmd, extraC) END;
- AppendSeg(cmd, " -o ");
- AppendTok(cmd, out);
- AppendSeg(cmd, " -lm");
- CheckLen(cmd);
- Out(cmd);
- OutLn;
- IF dryRun THEN RETURN END;
- rc := M2makeOS.ExecCmd(cmd);
- IF rc # 0 THEN
- Out("M2make: build failed");
- OutLn;
- M2makeOS.ExitNow(1)
- END
- END BuildM2;
- PROCEDURE BuildGm2(out: ARRAY OF CHAR);
- VAR cmd, seg: Cmd; flags: Path; obj: Path;
- k, m: CARDINAL; rc: INTEGER; outT: LONGINT;
- compiledAny, linkNeeded: BOOLEAN;
- BEGIN
- Zero(cmd);
- IFlags(flags);
- compiledAny := FALSE;
- k := 0;
- WHILE k < nOrder DO
- m := order[k];
- GetSrc(m, tmpP);
- IF (NOT modIsMain[m]) AND (Len(tmpP) > 0) THEN
- BaseObj(tmpP, obj);
- IF ObjStale(m, obj) THEN
- Zero(seg);
- Cat("gm2 -fiso ", flags, seg);
- IF Len(flags) > 0 THEN
- Cat(seg, " ", segA);
- Copy(segA, seg)
- END;
- Cat(seg, "-c ", segA);
- Copy(segA, seg);
- Cat(seg, tmpP, segA);
- Copy(segA, seg);
- AppendCmd(cmd, seg);
- compiledAny := TRUE
- END
- END;
- INC(k)
- END;
- linkNeeded := compiledAny;
- IF NOT linkNeeded THEN
- IF NOT M2makeOS.FileExists(out) THEN linkNeeded := TRUE
- ELSIF NOT M2makeOS.FileMTime(out, outT) THEN linkNeeded := TRUE
- ELSE
- k := 0;
- WHILE k < nOrder DO
- m := order[k];
- GetSrc(m, tmpP);
- IF (NOT modIsMain[m]) AND (Len(tmpP) > 0) THEN
- BaseObj(tmpP, obj);
- IF ObjNewerThan(obj, out, outT) THEN linkNeeded := TRUE END
- END;
- INC(k)
- END
- END
- END;
- IF linkNeeded THEN
- Zero(seg);
- Cat("gm2 -fiso ", flags, seg);
- IF Len(flags) > 0 THEN
- Cat(seg, " ", segA);
- Copy(segA, seg)
- END;
- Cat(seg, "-o ", segA);
- Copy(segA, seg);
- Cat(seg, out, segA);
- Copy(segA, seg);
- Cat(seg, " ", segA);
- Copy(segA, seg);
- GetSrc(VAL(CARDINAL, mainIdx), tmpP);
- Cat(seg, tmpP, segA);
- Copy(segA, seg);
- k := 0;
- WHILE k < nOrder DO
- m := order[k];
- GetSrc(m, tmpP);
- IF (NOT modIsMain[m]) AND (Len(tmpP) > 0) THEN
- BaseObj(tmpP, obj);
- Cat(seg, " ", segA);
- Copy(segA, seg);
- Cat(seg, obj, segA);
- Copy(segA, seg)
- END;
- INC(k)
- END;
- AppendCmd(cmd, seg)
- END;
- IF Len(cmd) = 0 THEN
- Out("M2make: ");
- Out(out);
- Out(" up to date");
- OutLn;
- RETURN
- END;
- CheckLen(cmd);
- Out(cmd);
- OutLn;
- IF dryRun THEN RETURN END;
- rc := M2makeOS.ExecCmd(cmd);
- IF rc # 0 THEN
- Out("M2make: build failed");
- OutLn;
- M2makeOS.ExitNow(1)
- END
- END BuildGm2;
- PROCEDURE PrintOrder;
- VAR k: CARDINAL; m: CARDINAL;
- BEGIN
- Out("M2make: build order");
- OutLn;
- k := 0;
- WHILE k < nOrder DO
- m := order[k];
- Out(" ");
- GetName(m, tmpN);
- Out(tmpN);
- GetDef(m, tmpP);
- IF Len(tmpP) > 0 THEN
- Out(" : ");
- Out(tmpP)
- END;
- GetSrc(m, tmpP);
- IF Len(tmpP) > 0 THEN
- GetDef(m, tmpQ);
- IF Len(tmpQ) > 0 THEN Out(" ") ELSE Out(" : ") END;
- Out(tmpP)
- END;
- OutLn;
- INC(k)
- END
- END PrintOrder;
- (* ---------------- argument handling ---------------- *)
- PROCEDURE ParseArgs;
- VAR na: CARDINAL; s: Path; ok: BOOLEAN;
- BEGIN
- dryRun := FALSE;
- verbose := FALSE;
- useGm2 := FALSE;
- outGiven := FALSE;
- Zero(mainPath);
- Zero(outName);
- Copy("M2", m2bin);
- Copy("shim.c", shimPath);
- nDirs := 0;
- na := 0;
- LOOP
- IF na >= MaxArgs THEN Die("M2make: too many arguments", 2) END;
- ok := M2makeOS.GetArg(na, s, HIGH(s) + 1);
- IF NOT ok THEN EXIT END;
- INC(na);
- IF Cmp(s, "-n") = 0 THEN dryRun := TRUE
- ELSIF Cmp(s, "-v") = 0 THEN verbose := TRUE
- ELSIF Cmp(s, "-c") = 0 THEN
- ok := M2makeOS.GetArg(na, s, HIGH(s) + 1);
- IF NOT ok THEN
- Out("M2make: -c expects m2 or gm2"); OutLn;
- M2makeOS.ExitNow(2)
- END;
- INC(na);
- IF Cmp(s, "gm2") = 0 THEN useGm2 := TRUE
- ELSIF Cmp(s, "m2") = 0 THEN useGm2 := FALSE
- ELSE
- Out("M2make: -c expects m2 or gm2"); OutLn;
- M2makeOS.ExitNow(2)
- END
- ELSIF Cmp(s, "-I") = 0 THEN
- ok := M2makeOS.GetArg(na, s, HIGH(s) + 1);
- IF NOT ok THEN
- Out("M2make: -I expects a directory"); OutLn;
- M2makeOS.ExitNow(2)
- END;
- INC(na);
- IF nDirs >= MaxDirs THEN Die("M2make: too many -I dirs", 2) END;
- Copy(s, tmpP);
- PutDir(nDirs, tmpP);
- INC(nDirs)
- ELSIF Cmp(s, "-o") = 0 THEN
- ok := M2makeOS.GetArg(na, s, HIGH(s) + 1);
- IF NOT ok THEN
- Out("M2make: -o expects a name"); OutLn;
- M2makeOS.ExitNow(2)
- END;
- INC(na);
- Copy(s, outName);
- outGiven := TRUE
- ELSIF Cmp(s, "--m2bin") = 0 THEN
- ok := M2makeOS.GetArg(na, s, HIGH(s) + 1);
- IF NOT ok THEN
- Out("M2make: --m2bin expects a path"); OutLn;
- M2makeOS.ExitNow(2)
- END;
- INC(na);
- Copy(s, m2bin)
- ELSIF Cmp(s, "--shim") = 0 THEN
- ok := M2makeOS.GetArg(na, s, HIGH(s) + 1);
- IF NOT ok THEN
- Out("M2make: --shim expects a path"); OutLn;
- M2makeOS.ExitNow(2)
- END;
- INC(na);
- Copy(s, shimPath)
- ELSIF Cmp(s, "--extra") = 0 THEN
- ok := M2makeOS.GetArg(na, s, HIGH(s) + 1);
- IF NOT ok THEN
- Out("M2make: --extra expects a path"); OutLn;
- M2makeOS.ExitNow(2)
- END;
- INC(na);
- Copy(s, extraC)
- ELSIF (Cmp(s, "-h") = 0) OR (Cmp(s, "--help") = 0) THEN
- Usage;
- M2makeOS.ExitNow(0)
- ELSIF s[0] = "-" THEN
- Out("M2make: unknown option ");
- Out(s);
- OutLn;
- Usage;
- M2makeOS.ExitNow(2)
- ELSE
- IF Len(mainPath) > 0 THEN
- Out("M2make: only one main file"); OutLn;
- Usage;
- M2makeOS.ExitNow(2)
- END;
- Copy(s, mainPath)
- END
- END;
- IF Len(mainPath) = 0 THEN
- Usage;
- M2makeOS.ExitNow(2)
- END
- END ParseArgs;
- (* ---------------- main ---------------- *)
- VAR
- missing: BOOLEAN;
- rebuild: BOOLEAN;
- BEGIN
- nMods := 0;
- nOrder := 0;
- wantProg := TRUE;
- Zero(progName);
- ParseArgs;
- IF NOT M2makeOS.FileExists(mainPath) THEN
- Cat("M2make: main file not found ", mainPath, tmpCmd);
- Die(tmpCmd, 2)
- END;
- DirOf(mainPath, tmpA);
- IF nDirs = 0 THEN
- Copy(tmpA, tmpP);
- PutDir(0, tmpP);
- nDirs := 1
- ELSE
- (* main dir becomes an extra search dir when -I was given *)
- IF nDirs >= MaxDirs THEN Die("M2make: too many -I dirs", 2) END;
- Copy(tmpA, tmpP);
- PutDir(nDirs, tmpP);
- INC(nDirs)
- END;
- mainIdx := AddMod("M2makeMain");
- modIsMain[VAL(CARDINAL, mainIdx)] := TRUE;
- Copy(mainPath, tmpP);
- PutSrc(VAL(CARDINAL, mainIdx), tmpP);
- ResolveAll;
- IF Len(progName) = 0 THEN
- Die("M2make: no MODULE declaration in main file", 1)
- END;
- IF NOT outGiven THEN Copy(progName, outName) END;
- TopoSort;
- IF verbose OR dryRun THEN PrintOrder END;
- rebuild := NeedBuild(outName, missing);
- IF NOT rebuild THEN
- Out("M2make: ");
- Out(outName);
- Out(" up to date");
- OutLn
- ELSE
- IF useGm2 THEN BuildGm2(outName)
- ELSE BuildM2(outName)
- END
- END
- END M2make.
|