M2make.mod 32 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303
  1. MODULE M2make;
  2. (* M2make: build-order front-end for the M2 and GNU Modula-2 compilers.
  3. Usage: M2make [-n] [-v] [-c m2|gm2] [-I dir] [-o out]
  4. [--m2bin path] [--shim path] main.mod
  5. Follows IMPORT/FROM imports from main.mod transitively, sorts the
  6. modules topologically, skips the build when the output is newer
  7. than every source, otherwise invokes the selected compiler.
  8. Exits 0 on success (or nothing to do), 1 on build failure,
  9. 2 on usage errors.
  10. Portable source: compiles with gm2 -fiso and with the V3 M2
  11. driver. Only FileIO (console) and M2makeOS (files/time/exec)
  12. are imported; all string handling is NUL-based and local, since
  13. the two compilers' FileIO string helpers differ. *)
  14. IMPORT FileIO, M2makeOS;
  15. CONST
  16. MaxMods = 256;
  17. MaxDeps = 64;
  18. MaxDirs = 16;
  19. MaxArgs = 64;
  20. StackCap = 264;
  21. NameStride = 64;
  22. PathStride = 256;
  23. TYPE
  24. Name = ARRAY [0..63] OF CHAR;
  25. Path = ARRAY [0..255] OF CHAR;
  26. Line = ARRAY [0..2047] OF CHAR;
  27. Cmd = ARRAY [0..2047] OF CHAR;
  28. VAR
  29. nMods: CARDINAL;
  30. (* Flat 1D pools: every table is a single indexed array (a style
  31. choice -- both compilers handle nested arrays now). *)
  32. namePool: ARRAY [0..16383] OF CHAR;
  33. srcPool: ARRAY [0..65535] OF CHAR;
  34. defPool: ARRAY [0..65535] OF CHAR;
  35. dirPool: ARRAY [0..4095] OF CHAR;
  36. modScanned: ARRAY [0..255] OF BOOLEAN;
  37. modExternal: ARRAY [0..255] OF BOOLEAN;
  38. modIsMain: ARRAY [0..255] OF BOOLEAN;
  39. nDep: ARRAY [0..255] OF CARDINAL;
  40. depTab: ARRAY [0..16383] OF INTEGER;
  41. state: ARRAY [0..255] OF CARDINAL;
  42. childPos: ARRAY [0..255] OF CARDINAL;
  43. order: ARRAY [0..255] OF CARDINAL;
  44. nOrder: CARDINAL;
  45. dryRun, verbose, useGm2, separate: BOOLEAN;
  46. outGiven: BOOLEAN;
  47. mainPath, outName, m2bin, shimPath: Path;
  48. m2Flags: Cmd;
  49. dialect: Name;
  50. extraC: Path; (* extra C file linked in m2 mode (may be empty) *)
  51. nDirs: CARDINAL;
  52. progName: Name;
  53. wantProg: BOOLEAN;
  54. mainIdx: INTEGER;
  55. inComment: CARDINAL;
  56. gPendingFrom, gInImport, gSawHead, gWantName, gFromSeen: BOOLEAN;
  57. tmpA, tmpB: Path;
  58. tmpN: Name;
  59. tmpP, tmpQ: Path;
  60. tmpCmd: Cmd;
  61. segA: Cmd;
  62. (* ---------------- zeroed (NUL) string utilities ---------------- *)
  63. PROCEDURE Len(s: ARRAY OF CHAR): CARDINAL;
  64. (* NUL scan. Both compilers short-circuit AND/OR now, so the index
  65. guard protects the access; the bound is the string's HIGH + 1
  66. (V3 sting literals carry no NUL padding, fixed buffers do). *)
  67. VAR i: CARDINAL;
  68. BEGIN
  69. i := 0;
  70. WHILE (i <= HIGH(s)) AND (s[i] # CHR(0)) DO INC(i) END;
  71. RETURN i
  72. END Len;
  73. PROCEDURE Zero(VAR s: ARRAY OF CHAR);
  74. VAR i: CARDINAL;
  75. BEGIN
  76. i := 0;
  77. WHILE i <= HIGH(s) DO s[i] := CHR(0); INC(i) END
  78. END Zero;
  79. PROCEDURE Copy(src: ARRAY OF CHAR; VAR dst: ARRAY OF CHAR);
  80. VAR i, n: CARDINAL;
  81. BEGIN
  82. Zero(dst);
  83. n := Len(src);
  84. IF n > HIGH(dst) THEN n := HIGH(dst) END;
  85. i := 0;
  86. WHILE i < n DO dst[i] := src[i]; INC(i) END
  87. END Copy;
  88. PROCEDURE Cmp(a, b: ARRAY OF CHAR): INTEGER;
  89. (* Length-aware comparison: also correct when one side is a string
  90. literal (no NUL padding under V3). *)
  91. VAR i, na, nb: CARDINAL;
  92. BEGIN
  93. na := Len(a);
  94. nb := Len(b);
  95. i := 0;
  96. WHILE (i < na) AND (i < nb) DO
  97. IF a[i] # b[i] THEN
  98. IF a[i] < b[i] THEN RETURN -1 ELSE RETURN 1 END
  99. END;
  100. INC(i)
  101. END;
  102. IF na < nb THEN RETURN -1 END;
  103. IF na > nb THEN RETURN 1 END;
  104. RETURN 0
  105. END Cmp;
  106. PROCEDURE Cat(a, b: ARRAY OF CHAR; VAR dst: ARRAY OF CHAR);
  107. (* dst := a + b (NUL-terminated). Short-circuit AND keeps the index
  108. guard ahead of every access. *)
  109. VAR i, k: CARDINAL;
  110. BEGIN
  111. Zero(dst);
  112. k := 0; i := 0;
  113. WHILE (i <= HIGH(a)) AND (k <= HIGH(dst)) AND (a[i] # CHR(0)) DO
  114. dst[k] := a[i]; INC(k); INC(i)
  115. END;
  116. i := 0;
  117. WHILE (i <= HIGH(b)) AND (k <= HIGH(dst)) AND (b[i] # CHR(0)) DO
  118. dst[k] := b[i]; INC(k); INC(i)
  119. END
  120. END Cat;
  121. PROCEDURE Sub(src: ARRAY OF CHAR; start, count: CARDINAL;
  122. VAR dst: ARRAY OF CHAR);
  123. VAR j: CARDINAL;
  124. BEGIN
  125. Zero(dst);
  126. j := 0;
  127. WHILE (j < count) AND (start + j <= HIGH(src)) AND (j <= HIGH(dst))
  128. AND (src[start + j] # CHR(0)) DO
  129. dst[j] := src[start + j]; INC(j)
  130. END
  131. END Sub;
  132. PROCEDURE Lower(src: ARRAY OF CHAR; VAR dst: ARRAY OF CHAR);
  133. VAR i: CARDINAL; ch: CHAR;
  134. BEGIN
  135. Zero(dst);
  136. i := 0;
  137. WHILE (i <= HIGH(src)) AND (i <= HIGH(dst)) AND (src[i] # CHR(0)) DO
  138. ch := src[i];
  139. IF (ch >= "A") AND (ch <= "Z") THEN
  140. ch := CHR(ORD(ch) - ORD("A") + ORD("a"))
  141. END;
  142. dst[i] := ch; INC(i)
  143. END
  144. END Lower;
  145. PROCEDURE IsLetter(ch: CHAR): BOOLEAN;
  146. BEGIN
  147. RETURN ((ch >= "A") AND (ch <= "Z"))
  148. OR ((ch >= "a") AND (ch <= "z"))
  149. END IsLetter;
  150. PROCEDURE IsDig(ch: CHAR): BOOLEAN;
  151. BEGIN
  152. RETURN (ch >= "0") AND (ch <= "9")
  153. END IsDig;
  154. (* ---------------- flat-pool accessors ---------------- *)
  155. (* Single-indexed pool traffic only (see VAR comment). *)
  156. PROCEDURE GetName(m: CARDINAL; VAR dst: Name);
  157. VAR k: CARDINAL;
  158. BEGIN
  159. k := 0;
  160. WHILE k <= HIGH(dst) DO
  161. dst[k] := namePool[m * NameStride + k];
  162. INC(k)
  163. END
  164. END GetName;
  165. PROCEDURE PutName(m: CARDINAL; src: Name);
  166. VAR k: CARDINAL;
  167. BEGIN
  168. k := 0;
  169. WHILE k <= HIGH(src) DO
  170. namePool[m * NameStride + k] := src[k];
  171. INC(k)
  172. END
  173. END PutName;
  174. PROCEDURE GetSrc(m: CARDINAL; VAR dst: Path);
  175. VAR k: CARDINAL;
  176. BEGIN
  177. k := 0;
  178. WHILE k <= HIGH(dst) DO
  179. dst[k] := srcPool[m * PathStride + k];
  180. INC(k)
  181. END
  182. END GetSrc;
  183. PROCEDURE PutSrc(m: CARDINAL; src: Path);
  184. VAR k: CARDINAL;
  185. BEGIN
  186. k := 0;
  187. WHILE k <= HIGH(src) DO
  188. srcPool[m * PathStride + k] := src[k];
  189. INC(k)
  190. END
  191. END PutSrc;
  192. PROCEDURE GetDef(m: CARDINAL; VAR dst: Path);
  193. VAR k: CARDINAL;
  194. BEGIN
  195. k := 0;
  196. WHILE k <= HIGH(dst) DO
  197. dst[k] := defPool[m * PathStride + k];
  198. INC(k)
  199. END
  200. END GetDef;
  201. PROCEDURE PutDef(m: CARDINAL; src: Path);
  202. VAR k: CARDINAL;
  203. BEGIN
  204. k := 0;
  205. WHILE k <= HIGH(src) DO
  206. defPool[m * PathStride + k] := src[k];
  207. INC(k)
  208. END
  209. END PutDef;
  210. PROCEDURE GetDir(m: CARDINAL; VAR dst: Path);
  211. VAR k: CARDINAL;
  212. BEGIN
  213. k := 0;
  214. WHILE k <= HIGH(dst) DO
  215. dst[k] := dirPool[m * PathStride + k];
  216. INC(k)
  217. END
  218. END GetDir;
  219. PROCEDURE PutDir(m: CARDINAL; src: Path);
  220. VAR k: CARDINAL;
  221. BEGIN
  222. k := 0;
  223. WHILE k <= HIGH(src) DO
  224. dirPool[m * PathStride + k] := src[k];
  225. INC(k)
  226. END
  227. END PutDir;
  228. (* ---------------- console output ---------------- *)
  229. PROCEDURE Out(s: ARRAY OF CHAR);
  230. (* Character by character: V3 FileIO.WriteString emits the whole
  231. descriptor capacity (including NUL padding), so loop instead. *)
  232. VAR i, n: CARDINAL;
  233. BEGIN
  234. n := Len(s);
  235. i := 0;
  236. WHILE i < n DO
  237. FileIO.Write(FileIO.StdOut, s[i]);
  238. INC(i)
  239. END
  240. END Out;
  241. PROCEDURE OutLn;
  242. BEGIN
  243. FileIO.WriteLn(FileIO.StdOut)
  244. END OutLn;
  245. PROCEDURE OutInt(i: INTEGER);
  246. BEGIN
  247. FileIO.WriteInt(FileIO.StdOut, i, 0)
  248. END OutInt;
  249. PROCEDURE Die(s: ARRAY OF CHAR; code: INTEGER);
  250. BEGIN
  251. Out(s); OutLn;
  252. M2makeOS.ExitNow(code)
  253. END Die;
  254. PROCEDURE Usage;
  255. BEGIN
  256. Out("Usage: M2make [-n] [-v] [-c m2|gm2] [-I dir] [-o out]"); OutLn;
  257. Out(" [--m2bin path] [--shim path] [--extra path]
  258. [--m2flag flag] [--dialect pim|iso|gm2] [--separate] main.mod"); OutLn
  259. END Usage;
  260. (* ---------------- module table ---------------- *)
  261. PROCEDURE FindMod(n: ARRAY OF CHAR): INTEGER;
  262. VAR i: CARDINAL;
  263. BEGIN
  264. i := 0;
  265. WHILE i < nMods DO
  266. GetName(i, tmpN);
  267. IF Cmp(tmpN, n) = 0 THEN RETURN VAL(INTEGER, i) END;
  268. INC(i)
  269. END;
  270. RETURN -1
  271. END FindMod;
  272. PROCEDURE AddMod(n: ARRAY OF CHAR): INTEGER;
  273. BEGIN
  274. IF FindMod(n) >= 0 THEN RETURN FindMod(n) END;
  275. IF nMods >= MaxMods THEN Die("M2make: too many modules", 1) END;
  276. Copy(n, tmpN);
  277. PutName(nMods, tmpN);
  278. Zero(tmpP);
  279. PutSrc(nMods, tmpP);
  280. PutDef(nMods, tmpP);
  281. modScanned[nMods] := FALSE;
  282. modExternal[nMods] := FALSE;
  283. modIsMain[nMods] := FALSE;
  284. nDep[nMods] := 0;
  285. INC(nMods);
  286. RETURN VAL(INTEGER, nMods - 1)
  287. END AddMod;
  288. PROCEDURE AddDep(m, d: INTEGER);
  289. VAR mc: CARDINAL; k: CARDINAL;
  290. BEGIN
  291. IF (m < 0) OR (d < 0) OR (m = d) THEN RETURN END;
  292. mc := VAL(CARDINAL, m);
  293. k := 0;
  294. WHILE k < nDep[mc] DO
  295. IF depTab[mc * MaxDeps + k] = d THEN RETURN END;
  296. INC(k)
  297. END;
  298. IF nDep[mc] >= MaxDeps THEN Die("M2make: too many imports", 1) END;
  299. depTab[mc * MaxDeps + nDep[mc]] := d;
  300. INC(nDep[mc])
  301. END AddDep;
  302. PROCEDURE AddDepName(midx: INTEGER; n: ARRAY OF CHAR);
  303. VAR d: INTEGER;
  304. BEGIN
  305. d := AddMod(n);
  306. AddDep(midx, d)
  307. END AddDepName;
  308. (* ---------------- path handling ---------------- *)
  309. PROCEDURE DirOf(p: ARRAY OF CHAR; VAR dir: ARRAY OF CHAR);
  310. VAR i, n, k: CARDINAL;
  311. BEGIN
  312. Zero(dir);
  313. n := Len(p);
  314. k := 0;
  315. i := 0;
  316. WHILE i < n DO
  317. IF p[i] = "/" THEN k := i + 1 END;
  318. INC(i)
  319. END;
  320. i := 0;
  321. WHILE (i < k) AND (i <= HIGH(dir)) DO
  322. dir[i] := p[i]; INC(i)
  323. END
  324. END DirOf;
  325. PROCEDURE TryVariant(dir, base, ext: ARRAY OF CHAR;
  326. VAR out: ARRAY OF CHAR);
  327. VAR full: Path;
  328. BEGIN
  329. Zero(out);
  330. IF Len(dir) = 0 THEN
  331. Cat(base, ext, full)
  332. ELSE
  333. IF Len(dir) + 1 + Len(base) + Len(ext) > HIGH(full) THEN RETURN END;
  334. IF dir[Len(dir) - 1] = "/" THEN
  335. Cat(dir, base, tmpA);
  336. Cat(tmpA, ext, full)
  337. ELSE
  338. Cat(dir, "/", tmpA);
  339. Cat(tmpA, base, tmpB);
  340. Cat(tmpB, ext, full)
  341. END
  342. END;
  343. IF M2makeOS.FileExists(full) THEN Copy(full, out) END
  344. END TryVariant;
  345. PROCEDURE ResolveModule(n: ARRAY OF CHAR; VAR dp, mp: Path): BOOLEAN;
  346. VAR di: CARDINAL; low: Name; cand: Path; found: BOOLEAN;
  347. BEGIN
  348. Zero(dp);
  349. Zero(mp);
  350. Lower(n, low);
  351. found := FALSE;
  352. di := 0;
  353. WHILE di < nDirs DO
  354. GetDir(di, tmpQ);
  355. TryVariant(tmpQ, n, ".def", cand);
  356. IF (Len(dp) = 0) AND (Len(cand) > 0) THEN
  357. Copy(cand, dp); found := TRUE
  358. END;
  359. TryVariant(tmpQ, n, ".mod", cand);
  360. IF (Len(mp) = 0) AND (Len(cand) > 0) THEN
  361. Copy(cand, mp); found := TRUE
  362. END;
  363. TryVariant(tmpQ, low, ".def", cand);
  364. IF (Len(dp) = 0) AND (Len(cand) > 0) THEN
  365. Copy(cand, dp); found := TRUE
  366. END;
  367. TryVariant(tmpQ, low, ".mod", cand);
  368. IF (Len(mp) = 0) AND (Len(cand) > 0) THEN
  369. Copy(cand, mp); found := TRUE
  370. END;
  371. INC(di)
  372. END;
  373. RETURN found
  374. END ResolveModule;
  375. (* ---------------- import scanner ---------------- *)
  376. PROCEDURE HandleTok(tok: ARRAY OF CHAR; midx: INTEGER);
  377. BEGIN
  378. IF Cmp(tok, "FROM") = 0 THEN
  379. gPendingFrom := TRUE;
  380. gInImport := FALSE;
  381. gSawHead := FALSE;
  382. gWantName := FALSE;
  383. gFromSeen := FALSE;
  384. RETURN
  385. END;
  386. IF Cmp(tok, "IMPORT") = 0 THEN
  387. IF gPendingFrom OR gFromSeen THEN
  388. (* FROM M IMPORT ... : following names are items, not modules *)
  389. gPendingFrom := FALSE;
  390. gFromSeen := FALSE
  391. ELSE gInImport := TRUE
  392. END;
  393. gSawHead := FALSE;
  394. gWantName := FALSE;
  395. RETURN
  396. END;
  397. IF Cmp(tok, "DEFINITION") = 0 THEN
  398. gSawHead := TRUE;
  399. gPendingFrom := FALSE;
  400. gInImport := FALSE;
  401. gWantName := FALSE;
  402. gFromSeen := FALSE;
  403. RETURN
  404. END;
  405. IF Cmp(tok, "IMPLEMENTATION") = 0 THEN
  406. gSawHead := TRUE;
  407. gPendingFrom := FALSE;
  408. gInImport := FALSE;
  409. gWantName := FALSE;
  410. gFromSeen := FALSE;
  411. RETURN
  412. END;
  413. IF Cmp(tok, "MODULE") = 0 THEN
  414. IF gSawHead THEN gWantName := FALSE
  415. ELSE gWantName := TRUE
  416. END;
  417. gSawHead := FALSE;
  418. gPendingFrom := FALSE;
  419. gInImport := FALSE;
  420. gFromSeen := FALSE;
  421. RETURN
  422. END;
  423. IF gPendingFrom THEN
  424. AddDepName(midx, tok);
  425. gPendingFrom := FALSE;
  426. gFromSeen := TRUE;
  427. gSawHead := FALSE;
  428. RETURN
  429. END;
  430. IF gInImport THEN
  431. AddDepName(midx, tok);
  432. gSawHead := FALSE;
  433. gFromSeen := FALSE;
  434. RETURN
  435. END;
  436. IF gWantName THEN
  437. gWantName := FALSE;
  438. IF wantProg AND (midx = mainIdx) THEN
  439. Copy(tok, progName);
  440. wantProg := FALSE
  441. END;
  442. gSawHead := FALSE;
  443. gFromSeen := FALSE;
  444. RETURN
  445. END;
  446. gSawHead := FALSE;
  447. gFromSeen := FALSE
  448. END HandleTok;
  449. PROCEDURE ScanLine(line: ARRAY OF CHAR; midx: INTEGER);
  450. VAR i, n, k: CARDINAL; ch, qc: CHAR;
  451. inStr: BOOLEAN; tok: Name;
  452. BEGIN
  453. n := Len(line);
  454. i := 0;
  455. inStr := FALSE;
  456. qc := CHR(0);
  457. WHILE i < n DO
  458. ch := line[i];
  459. IF inStr THEN
  460. IF ch = qc THEN
  461. IF (i + 1 < n) AND (line[i + 1] = qc) THEN i := i + 2
  462. ELSE inStr := FALSE; INC(i)
  463. END
  464. ELSE INC(i)
  465. END
  466. ELSIF inComment > 0 THEN
  467. IF (ch = "(") AND (i + 1 < n) AND (line[i + 1] = "*") THEN
  468. INC(inComment); i := i + 2
  469. ELSIF (ch = "*") AND (i + 1 < n) AND (line[i + 1] = ")") THEN
  470. DEC(inComment); i := i + 2
  471. ELSE INC(i)
  472. END
  473. ELSE
  474. IF (ch = "(") AND (i + 1 < n) AND (line[i + 1] = "*") THEN
  475. INC(inComment); i := i + 2
  476. ELSIF (ch = "/") AND (i + 1 < n) AND (line[i + 1] = "/") THEN
  477. i := n
  478. ELSIF (ch = "'") OR (ch = '"') THEN
  479. inStr := TRUE; qc := ch; INC(i)
  480. ELSIF ch = ";" THEN
  481. gPendingFrom := FALSE;
  482. gInImport := FALSE;
  483. gSawHead := FALSE;
  484. gWantName := FALSE;
  485. gFromSeen := FALSE;
  486. INC(i)
  487. ELSIF IsLetter(ch) THEN
  488. k := i;
  489. WHILE (k < n) AND (IsLetter(line[k]) OR IsDig(line[k])) DO
  490. INC(k)
  491. END;
  492. Sub(line, i, k - i, tok);
  493. i := k;
  494. HandleTok(tok, midx)
  495. ELSE INC(i)
  496. END
  497. END
  498. END
  499. END ScanLine;
  500. PROCEDURE ScanFile(path: ARRAY OF CHAR; midx: INTEGER);
  501. VAR h: M2makeOS.RdFile; line: Line; ok: BOOLEAN;
  502. BEGIN
  503. h := M2makeOS.OpenRead(path);
  504. IF h = NIL THEN
  505. Cat("M2make: cannot open ", path, tmpCmd);
  506. Die(tmpCmd, 1)
  507. END;
  508. LOOP
  509. Zero(line);
  510. ok := M2makeOS.ReadLine(h, line, HIGH(line) + 1);
  511. IF NOT ok THEN EXIT END;
  512. ScanLine(line, midx)
  513. END;
  514. M2makeOS.CloseRead(h)
  515. END ScanFile;
  516. PROCEDURE ScanModule(m: CARDINAL);
  517. VAR dp, mp: Path;
  518. BEGIN
  519. IF NOT modIsMain[m] THEN
  520. Zero(dp);
  521. Zero(mp);
  522. GetName(m, tmpN);
  523. IF NOT ResolveModule(tmpN, dp, mp) THEN
  524. modExternal[m] := TRUE;
  525. modScanned[m] := TRUE;
  526. IF verbose THEN
  527. Out("M2make: external module ");
  528. Out(tmpN);
  529. OutLn
  530. END;
  531. RETURN
  532. END;
  533. Copy(dp, tmpP);
  534. PutDef(m, tmpP);
  535. Copy(mp, tmpP);
  536. PutSrc(m, tmpP)
  537. END;
  538. inComment := 0;
  539. gPendingFrom := FALSE;
  540. gInImport := FALSE;
  541. gSawHead := FALSE;
  542. gWantName := FALSE;
  543. gFromSeen := FALSE;
  544. GetDef(m, tmpP);
  545. IF Len(tmpP) > 0 THEN ScanFile(tmpP, VAL(INTEGER, m)) END;
  546. GetSrc(m, tmpP);
  547. IF Len(tmpP) > 0 THEN ScanFile(tmpP, VAL(INTEGER, m)) END;
  548. modScanned[m] := TRUE
  549. END ScanModule;
  550. PROCEDURE ResolveAll;
  551. VAR i: CARDINAL;
  552. BEGIN
  553. i := 0;
  554. WHILE i < nMods DO
  555. IF NOT modScanned[i] THEN ScanModule(i) END;
  556. INC(i)
  557. END
  558. END ResolveAll;
  559. (* ---------------- topological sort (iterative DFS) ---------------- *)
  560. PROCEDURE TopoSort;
  561. VAR stack: ARRAY [0..263] OF CARDINAL;
  562. top, cur: CARDINAL; d: INTEGER; dd: CARDINAL; i: CARDINAL;
  563. BEGIN
  564. i := 0;
  565. WHILE i < nMods DO
  566. state[i] := 0; childPos[i] := 0; INC(i)
  567. END;
  568. nOrder := 0;
  569. top := 0;
  570. stack[0] := VAL(CARDINAL, mainIdx);
  571. state[VAL(CARDINAL, mainIdx)] := 1;
  572. LOOP
  573. cur := stack[top];
  574. IF childPos[cur] < nDep[cur] THEN
  575. d := depTab[cur * MaxDeps + childPos[cur]];
  576. INC(childPos[cur]);
  577. IF d < 0 THEN Die("M2make: bad dependency", 1) END;
  578. dd := VAL(CARDINAL, d);
  579. IF NOT modExternal[dd] THEN
  580. IF state[dd] = 0 THEN
  581. state[dd] := 1;
  582. INC(top);
  583. IF top > HIGH(stack) THEN Die("M2make: stack overflow", 1) END;
  584. stack[top] := dd
  585. ELSIF state[dd] = 1 THEN
  586. GetName(dd, tmpN);
  587. Out("M2make: cyclic import involving ");
  588. Out(tmpN);
  589. OutLn;
  590. M2makeOS.ExitNow(1)
  591. END
  592. END
  593. ELSE
  594. state[cur] := 2;
  595. order[nOrder] := cur;
  596. INC(nOrder);
  597. IF top = 0 THEN EXIT END;
  598. DEC(top)
  599. END
  600. END
  601. END TopoSort;
  602. (* ---------------- staleness ---------------- *)
  603. PROCEDURE SrcNewer(src: ARRAY OF CHAR; outT: LONGINT): BOOLEAN;
  604. VAR t: LONGINT;
  605. BEGIN
  606. IF Len(src) = 0 THEN RETURN FALSE END;
  607. IF NOT M2makeOS.FileMTime(src, t) THEN RETURN TRUE END;
  608. RETURN t > outT
  609. END SrcNewer;
  610. PROCEDURE NeedBuild(out: ARRAY OF CHAR; VAR missing: BOOLEAN): BOOLEAN;
  611. VAR outT: LONGINT; k: CARDINAL; m: CARDINAL;
  612. BEGIN
  613. missing := FALSE;
  614. IF NOT M2makeOS.FileExists(out) THEN
  615. missing := TRUE;
  616. RETURN TRUE
  617. END;
  618. IF NOT M2makeOS.FileMTime(out, outT) THEN RETURN TRUE END;
  619. k := 0;
  620. WHILE k < nOrder DO
  621. m := order[k];
  622. GetDef(m, tmpP);
  623. IF SrcNewer(tmpP, outT) THEN RETURN TRUE END;
  624. GetSrc(m, tmpP);
  625. IF SrcNewer(tmpP, outT) THEN RETURN TRUE END;
  626. INC(k)
  627. END;
  628. RETURN FALSE
  629. END NeedBuild;
  630. (* ---------------- command construction ---------------- *)
  631. PROCEDURE AppendTok(VAR cmd: ARRAY OF CHAR; tok: ARRAY OF CHAR);
  632. BEGIN
  633. IF Len(cmd) > 0 THEN
  634. Cat(cmd, " ", segA);
  635. Copy(segA, cmd)
  636. END;
  637. Cat(cmd, tok, segA);
  638. Copy(segA, cmd)
  639. END AppendTok;
  640. PROCEDURE IFlags(VAR flags: ARRAY OF CHAR);
  641. VAR di: CARDINAL;
  642. BEGIN
  643. Zero(flags);
  644. di := 0;
  645. WHILE di < nDirs DO
  646. GetDir(di, tmpP);
  647. IF Len(tmpP) > 0 THEN
  648. AppendTok(flags, "-I");
  649. AppendTok(flags, tmpP)
  650. END;
  651. INC(di)
  652. END
  653. END IFlags;
  654. PROCEDURE AppendCh(VAR s: ARRAY OF CHAR; ch: CHAR);
  655. VAR n: CARDINAL;
  656. BEGIN
  657. n := Len(s);
  658. IF n <= HIGH(s) THEN
  659. s[n] := ch;
  660. IF n + 1 <= HIGH(s) THEN s[n + 1] := CHR(0) END
  661. END
  662. END AppendCh;
  663. PROCEDURE BaseObj(src: ARRAY OF CHAR; VAR obj: ARRAY OF CHAR);
  664. (* "some/dir/Calc.mod" -> "Calc.o" (gm2 -c drops objects in the cwd). *)
  665. VAR i, n, slash, dot, k: CARDINAL;
  666. BEGIN
  667. Zero(obj);
  668. n := Len(src);
  669. slash := 0;
  670. i := 0;
  671. WHILE i < n DO
  672. IF src[i] = "/" THEN slash := i + 1 END;
  673. INC(i)
  674. END;
  675. dot := n;
  676. i := slash;
  677. WHILE i < n DO
  678. IF src[i] = "." THEN dot := i END;
  679. INC(i)
  680. END;
  681. IF dot <= slash THEN dot := n END;
  682. k := 0;
  683. i := slash;
  684. WHILE (i < dot) AND (k <= HIGH(obj)) DO
  685. obj[k] := src[i]; INC(k); INC(i)
  686. END;
  687. Cat(obj, ".o", tmpA);
  688. Copy(tmpA, obj)
  689. END BaseObj;
  690. PROCEDURE ObjStale(m: CARDINAL; obj: ARRAY OF CHAR): BOOLEAN;
  691. VAR ot: LONGINT;
  692. BEGIN
  693. IF NOT M2makeOS.FileExists(obj) THEN RETURN TRUE END;
  694. IF NOT M2makeOS.FileMTime(obj, ot) THEN RETURN TRUE END;
  695. GetDef(m, tmpP);
  696. IF SrcNewer(tmpP, ot) THEN RETURN TRUE END;
  697. GetSrc(m, tmpP);
  698. IF SrcNewer(tmpP, ot) THEN RETURN TRUE END;
  699. RETURN FALSE
  700. END ObjStale;
  701. PROCEDURE ObjNewerThan(obj, out: ARRAY OF CHAR;
  702. outT: LONGINT): BOOLEAN;
  703. VAR t: LONGINT;
  704. BEGIN
  705. IF Len(obj) = 0 THEN RETURN FALSE END;
  706. IF NOT M2makeOS.FileMTime(obj, t) THEN RETURN TRUE END;
  707. RETURN t > outT
  708. END ObjNewerThan;
  709. PROCEDURE CheckLen(cmd: ARRAY OF CHAR);
  710. BEGIN
  711. IF Len(cmd) + 32 > HIGH(cmd) THEN
  712. Die("M2make: command too long", 1)
  713. END
  714. END CheckLen;
  715. PROCEDURE AppendCmd(VAR cmd: ARRAY OF CHAR; seg: ARRAY OF CHAR);
  716. BEGIN
  717. IF Len(cmd) > 0 THEN
  718. Cat(cmd, " && ", segA);
  719. Copy(segA, cmd)
  720. END;
  721. Cat(cmd, seg, segA);
  722. Copy(segA, cmd)
  723. END AppendCmd;
  724. PROCEDURE AppendSeg(VAR cmd: ARRAY OF CHAR; seg: ARRAY OF CHAR);
  725. (* Direct concatenation (segments carry their own spacing).
  726. segA is the dedicated Cmd-sized scratch: always distinct from
  727. cmd and seg, and big enough for whole command lines. *)
  728. BEGIN
  729. Cat(cmd, seg, segA);
  730. Copy(segA, cmd)
  731. END AppendSeg;
  732. PROCEDURE BuildM2(out: ARRAY OF CHAR);
  733. VAR cmd: Cmd; k: CARDINAL; m: CARDINAL; rc: INTEGER;
  734. BEGIN
  735. Zero(cmd);
  736. AppendSeg(cmd, "mkdir -p gen_ssa && ");
  737. AppendSeg(cmd, m2bin);
  738. IF Len(m2Flags) > 0 THEN
  739. AppendSeg(cmd, " ");
  740. AppendSeg(cmd, m2Flags)
  741. END;
  742. k := 0;
  743. WHILE k < nOrder DO
  744. m := order[k];
  745. GetDef(m, tmpP);
  746. IF Len(tmpP) > 0 THEN AppendTok(cmd, tmpP) END;
  747. GetSrc(m, tmpP);
  748. IF Len(tmpP) > 0 THEN AppendTok(cmd, tmpP) END;
  749. INC(k)
  750. END;
  751. AppendSeg(cmd, " 2>&1 | tee m2make.log | grep -q 'Parsed correctly'");
  752. AppendSeg(cmd, " && qbe -o gen_ssa/");
  753. AppendSeg(cmd, progName);
  754. AppendSeg(cmd, ".s gen_ssa/");
  755. AppendSeg(cmd, progName);
  756. AppendSeg(cmd, ".ssa");
  757. AppendSeg(cmd, " && cc gen_ssa/");
  758. AppendSeg(cmd, progName);
  759. AppendSeg(cmd, ".s ");
  760. AppendTok(cmd, shimPath);
  761. IF Len(extraC) > 0 THEN AppendTok(cmd, extraC) END;
  762. AppendSeg(cmd, " -o ");
  763. AppendTok(cmd, out);
  764. AppendSeg(cmd, " -lm");
  765. CheckLen(cmd);
  766. Out(cmd);
  767. OutLn;
  768. IF dryRun THEN RETURN END;
  769. rc := M2makeOS.ExecCmd(cmd);
  770. IF rc # 0 THEN
  771. Out("M2make: build failed");
  772. OutLn;
  773. M2makeOS.ExitNow(1)
  774. END
  775. END BuildM2;
  776. PROCEDURE SepObjName(m: CARDINAL; VAR obj: Path);
  777. (* One object per module in gen_ssa/, keyed by module name. *)
  778. BEGIN
  779. Zero(obj);
  780. GetName(m, tmpN);
  781. Cat("gen_ssa/", tmpN, tmpA);
  782. Cat(tmpA, ".o", obj)
  783. END SepObjName;
  784. PROCEDURE SepObjStale (m: CARDINAL; obj: ARRAY OF CHAR): BOOLEAN;
  785. (* Separate mode is conservative: any interface change rebuilds the
  786. object. This also covers transitive .def type-layout changes, at the
  787. cost of rebuilding more than strictly necessary. *)
  788. VAR ot, ct: LONGINT; k: CARDINAL;
  789. BEGIN
  790. IF NOT M2makeOS.FileExists(obj) THEN RETURN TRUE END;
  791. IF NOT M2makeOS.FileMTime(obj, ot) THEN RETURN TRUE END;
  792. (* A compiler rebuild is an ABI event; conservative but cheap. *)
  793. IF M2makeOS.FileMTime(m2bin, ct) AND (ct > ot) THEN RETURN TRUE END;
  794. GetDef(m, tmpP);
  795. IF SrcNewer(tmpP, ot) THEN RETURN TRUE END;
  796. GetSrc(m, tmpP);
  797. IF SrcNewer(tmpP, ot) THEN RETURN TRUE END;
  798. k := 0;
  799. WHILE k < nOrder DO
  800. GetDef(order[k], tmpP);
  801. IF SrcNewer(tmpP, ot) THEN RETURN TRUE END;
  802. INC(k)
  803. END;
  804. RETURN FALSE
  805. END SepObjStale;
  806. PROCEDURE BuildM2Separate(out: ARRAY OF CHAR);
  807. (* Compile stale modules to their own QBE objects, then link. The
  808. program module is last in the topological order and is the only
  809. object with main; every module object exports <mod>_init. *)
  810. VAR cmd: Cmd; k, m: CARDINAL; rc: INTEGER;
  811. obj, src, ssafile, sfile, flags: Path;
  812. outT: LONGINT; compiledAny, linkNeeded: BOOLEAN;
  813. BEGIN
  814. Zero(cmd);
  815. compiledAny := FALSE;
  816. k := 0;
  817. WHILE k < nOrder DO
  818. m := order[k];
  819. GetSrc(m, src);
  820. IF Len(src) > 0 THEN
  821. IF modIsMain[m] THEN Copy(progName, tmpN) ELSE GetName(m, tmpN) END;
  822. Zero(obj); Cat("gen_ssa/", tmpN, tmpA); Cat(tmpA, ".o", obj);
  823. IF SepObjStale(m, obj) THEN
  824. CpyPath(ssafile, "gen_ssa/"); Cat(ssafile, tmpN, tmpA); Cat(tmpA, ".ssa", ssafile);
  825. CpyPath(sfile, "gen_ssa/"); Cat(sfile, tmpN, tmpA); Cat(tmpA, ".s", sfile);
  826. Zero(tmpCmd);
  827. AppendSeg(tmpCmd, "mkdir -p gen_ssa && ");
  828. AppendSeg(tmpCmd, m2bin);
  829. AppendSeg(tmpCmd, " -c");
  830. IF Len(m2Flags) > 0 THEN
  831. AppendSeg(tmpCmd, " ");
  832. AppendSeg(tmpCmd, m2Flags)
  833. END;
  834. (* M2 resolves the target .def and all imported .defs from -I. *)
  835. IFlags(flags);
  836. IF Len(flags) > 0 THEN
  837. AppendSeg(tmpCmd, " ");
  838. AppendSeg(tmpCmd, flags)
  839. END;
  840. AppendTok(tmpCmd, src);
  841. AppendSeg(tmpCmd, " 2>&1 | tee m2make.log | grep -q 'Parsed correctly'");
  842. AppendSeg(tmpCmd, " && qbe -o ");
  843. AppendTok(tmpCmd, sfile);
  844. AppendSeg(tmpCmd, " ");
  845. AppendTok(tmpCmd, ssafile);
  846. AppendSeg(tmpCmd, " && cc -c ");
  847. AppendTok(tmpCmd, sfile);
  848. AppendSeg(tmpCmd, " -o ");
  849. AppendTok(tmpCmd, obj);
  850. CheckLen(tmpCmd);
  851. Out(tmpCmd);
  852. OutLn;
  853. IF NOT dryRun THEN
  854. rc := M2makeOS.ExecCmd(tmpCmd);
  855. IF rc # 0 THEN
  856. Out("M2make: separate compile failed");
  857. OutLn;
  858. M2makeOS.ExitNow(1)
  859. END
  860. END;
  861. compiledAny := TRUE
  862. END
  863. END;
  864. INC(k)
  865. END;
  866. linkNeeded := compiledAny;
  867. IF NOT linkNeeded THEN
  868. IF NOT M2makeOS.FileExists(out) THEN linkNeeded := TRUE
  869. ELSIF NOT M2makeOS.FileMTime(out, outT) THEN linkNeeded := TRUE
  870. ELSE
  871. k := 0;
  872. WHILE k < nOrder DO
  873. m := order[k];
  874. GetSrc(m, src);
  875. IF Len(src) > 0 THEN
  876. IF modIsMain[m] THEN Copy(progName, tmpN) ELSE GetName(m, tmpN) END;
  877. Zero(obj); Cat("gen_ssa/", tmpN, tmpA); Cat(tmpA, ".o", obj);
  878. IF ObjNewerThan(obj, out, outT) THEN linkNeeded := TRUE END
  879. END;
  880. INC(k)
  881. END;
  882. IF SrcNewer(shimPath, outT) THEN linkNeeded := TRUE END;
  883. IF Len(extraC) > 0 THEN
  884. IF SrcNewer(extraC, outT) THEN linkNeeded := TRUE END
  885. END
  886. END
  887. END;
  888. IF NOT linkNeeded THEN
  889. Out("M2make: "); Out(out); Out(" up to date"); OutLn;
  890. RETURN
  891. END;
  892. (* link: one object per module, target object (main) last *)
  893. Zero(tmpCmd);
  894. AppendSeg(tmpCmd, "cc ");
  895. k := 0;
  896. WHILE k < nOrder DO
  897. m := order[k];
  898. GetSrc(m, src);
  899. IF Len(src) > 0 THEN
  900. IF modIsMain[m] THEN Copy(progName, tmpN) ELSE GetName(m, tmpN) END;
  901. Zero(obj); Cat("gen_ssa/", tmpN, tmpA); Cat(tmpA, ".o", obj);
  902. AppendTok(tmpCmd, obj)
  903. END;
  904. INC(k)
  905. END;
  906. AppendTok(tmpCmd, shimPath);
  907. IF Len(extraC) > 0 THEN AppendTok(tmpCmd, extraC) END;
  908. AppendSeg(tmpCmd, " -o ");
  909. AppendTok(tmpCmd, out);
  910. AppendSeg(tmpCmd, " -lm");
  911. CheckLen(tmpCmd);
  912. Out(tmpCmd);
  913. OutLn;
  914. IF dryRun THEN RETURN END;
  915. rc := M2makeOS.ExecCmd(tmpCmd);
  916. IF rc # 0 THEN
  917. Out("M2make: separate build failed");
  918. OutLn;
  919. M2makeOS.ExitNow(1)
  920. END
  921. END BuildM2Separate;
  922. PROCEDURE CpyPath(VAR dst: Path; src: ARRAY OF CHAR);
  923. BEGIN
  924. Copy(src, dst)
  925. END CpyPath;
  926. PROCEDURE BuildGm2(out: ARRAY OF CHAR);
  927. VAR cmd, seg: Cmd; flags: Path; obj: Path;
  928. k, m: CARDINAL; rc: INTEGER; outT: LONGINT;
  929. compiledAny, linkNeeded: BOOLEAN;
  930. BEGIN
  931. Zero(cmd);
  932. IFlags(flags);
  933. compiledAny := FALSE;
  934. k := 0;
  935. WHILE k < nOrder DO
  936. m := order[k];
  937. GetSrc(m, tmpP);
  938. IF (NOT modIsMain[m]) AND (Len(tmpP) > 0) THEN
  939. BaseObj(tmpP, obj);
  940. IF ObjStale(m, obj) THEN
  941. Zero(seg);
  942. Cat("gm2 -fiso ", flags, seg);
  943. IF Len(flags) > 0 THEN
  944. Cat(seg, " ", segA);
  945. Copy(segA, seg)
  946. END;
  947. Cat(seg, "-c ", segA);
  948. Copy(segA, seg);
  949. Cat(seg, tmpP, segA);
  950. Copy(segA, seg);
  951. AppendCmd(cmd, seg);
  952. compiledAny := TRUE
  953. END
  954. END;
  955. INC(k)
  956. END;
  957. linkNeeded := compiledAny;
  958. IF NOT linkNeeded THEN
  959. IF NOT M2makeOS.FileExists(out) THEN linkNeeded := TRUE
  960. ELSIF NOT M2makeOS.FileMTime(out, outT) THEN linkNeeded := TRUE
  961. ELSE
  962. k := 0;
  963. WHILE k < nOrder DO
  964. m := order[k];
  965. GetSrc(m, tmpP);
  966. IF (NOT modIsMain[m]) AND (Len(tmpP) > 0) THEN
  967. BaseObj(tmpP, obj);
  968. IF ObjNewerThan(obj, out, outT) THEN linkNeeded := TRUE END
  969. END;
  970. INC(k)
  971. END
  972. END
  973. END;
  974. IF linkNeeded THEN
  975. Zero(seg);
  976. Cat("gm2 -fiso ", flags, seg);
  977. IF Len(flags) > 0 THEN
  978. Cat(seg, " ", segA);
  979. Copy(segA, seg)
  980. END;
  981. Cat(seg, "-o ", segA);
  982. Copy(segA, seg);
  983. Cat(seg, out, segA);
  984. Copy(segA, seg);
  985. Cat(seg, " ", segA);
  986. Copy(segA, seg);
  987. GetSrc(VAL(CARDINAL, mainIdx), tmpP);
  988. Cat(seg, tmpP, segA);
  989. Copy(segA, seg);
  990. k := 0;
  991. WHILE k < nOrder DO
  992. m := order[k];
  993. GetSrc(m, tmpP);
  994. IF (NOT modIsMain[m]) AND (Len(tmpP) > 0) THEN
  995. BaseObj(tmpP, obj);
  996. Cat(seg, " ", segA);
  997. Copy(segA, seg);
  998. Cat(seg, obj, segA);
  999. Copy(segA, seg)
  1000. END;
  1001. INC(k)
  1002. END;
  1003. AppendCmd(cmd, seg)
  1004. END;
  1005. IF Len(cmd) = 0 THEN
  1006. Out("M2make: ");
  1007. Out(out);
  1008. Out(" up to date");
  1009. OutLn;
  1010. RETURN
  1011. END;
  1012. CheckLen(cmd);
  1013. Out(cmd);
  1014. OutLn;
  1015. IF dryRun THEN RETURN END;
  1016. rc := M2makeOS.ExecCmd(cmd);
  1017. IF rc # 0 THEN
  1018. Out("M2make: build failed");
  1019. OutLn;
  1020. M2makeOS.ExitNow(1)
  1021. END
  1022. END BuildGm2;
  1023. PROCEDURE PrintOrder;
  1024. VAR k: CARDINAL; m: CARDINAL;
  1025. BEGIN
  1026. Out("M2make: build order");
  1027. OutLn;
  1028. k := 0;
  1029. WHILE k < nOrder DO
  1030. m := order[k];
  1031. Out(" ");
  1032. GetName(m, tmpN);
  1033. Out(tmpN);
  1034. GetDef(m, tmpP);
  1035. IF Len(tmpP) > 0 THEN
  1036. Out(" : ");
  1037. Out(tmpP)
  1038. END;
  1039. GetSrc(m, tmpP);
  1040. IF Len(tmpP) > 0 THEN
  1041. GetDef(m, tmpQ);
  1042. IF Len(tmpQ) > 0 THEN Out(" ") ELSE Out(" : ") END;
  1043. Out(tmpP)
  1044. END;
  1045. OutLn;
  1046. INC(k)
  1047. END
  1048. END PrintOrder;
  1049. (* ---------------- argument handling ---------------- *)
  1050. PROCEDURE ParseArgs;
  1051. VAR na: CARDINAL; s: Path; ok: BOOLEAN;
  1052. BEGIN
  1053. dryRun := FALSE;
  1054. verbose := FALSE;
  1055. useGm2 := FALSE;
  1056. separate := FALSE;
  1057. outGiven := FALSE;
  1058. Zero(mainPath);
  1059. Zero(outName);
  1060. Copy("M2", m2bin);
  1061. Copy("shim.c", shimPath);
  1062. Zero(m2Flags);
  1063. dialect[0] := CHR(0);
  1064. nDirs := 0;
  1065. na := 0;
  1066. LOOP
  1067. IF na >= MaxArgs THEN Die("M2make: too many arguments", 2) END;
  1068. ok := M2makeOS.GetArg(na, s, HIGH(s) + 1);
  1069. IF NOT ok THEN EXIT END;
  1070. INC(na);
  1071. IF Cmp(s, "-n") = 0 THEN dryRun := TRUE
  1072. ELSIF Cmp(s, "-v") = 0 THEN verbose := TRUE
  1073. ELSIF Cmp(s, "--separate") = 0 THEN separate := TRUE
  1074. ELSIF Cmp(s, "-c") = 0 THEN
  1075. ok := M2makeOS.GetArg(na, s, HIGH(s) + 1);
  1076. IF NOT ok THEN
  1077. Out("M2make: -c expects m2 or gm2"); OutLn;
  1078. M2makeOS.ExitNow(2)
  1079. END;
  1080. INC(na);
  1081. IF Cmp(s, "gm2") = 0 THEN useGm2 := TRUE
  1082. ELSIF Cmp(s, "m2") = 0 THEN useGm2 := FALSE
  1083. ELSE
  1084. Out("M2make: -c expects m2 or gm2"); OutLn;
  1085. M2makeOS.ExitNow(2)
  1086. END
  1087. ELSIF Cmp(s, "-I") = 0 THEN
  1088. ok := M2makeOS.GetArg(na, s, HIGH(s) + 1);
  1089. IF NOT ok THEN
  1090. Out("M2make: -I expects a directory"); OutLn;
  1091. M2makeOS.ExitNow(2)
  1092. END;
  1093. INC(na);
  1094. IF nDirs >= MaxDirs THEN Die("M2make: too many -I dirs", 2) END;
  1095. Copy(s, tmpP);
  1096. PutDir(nDirs, tmpP);
  1097. INC(nDirs)
  1098. ELSIF Cmp(s, "-o") = 0 THEN
  1099. ok := M2makeOS.GetArg(na, s, HIGH(s) + 1);
  1100. IF NOT ok THEN
  1101. Out("M2make: -o expects a name"); OutLn;
  1102. M2makeOS.ExitNow(2)
  1103. END;
  1104. INC(na);
  1105. Copy(s, outName);
  1106. outGiven := TRUE
  1107. ELSIF Cmp(s, "--m2bin") = 0 THEN
  1108. ok := M2makeOS.GetArg(na, s, HIGH(s) + 1);
  1109. IF NOT ok THEN
  1110. Out("M2make: --m2bin expects a path"); OutLn;
  1111. M2makeOS.ExitNow(2)
  1112. END;
  1113. INC(na);
  1114. Copy(s, m2bin)
  1115. ELSIF Cmp(s, "--shim") = 0 THEN
  1116. ok := M2makeOS.GetArg(na, s, HIGH(s) + 1);
  1117. IF NOT ok THEN
  1118. Out("M2make: --shim expects a path"); OutLn;
  1119. M2makeOS.ExitNow(2)
  1120. END;
  1121. INC(na);
  1122. Copy(s, shimPath)
  1123. ELSIF Cmp(s, "--dialect") = 0 THEN
  1124. ok := M2makeOS.GetArg(na, s, HIGH(s) + 1);
  1125. IF NOT ok THEN
  1126. Out("M2make: --dialect expects pim, iso or gm2"); OutLn;
  1127. M2makeOS.ExitNow(2)
  1128. END;
  1129. INC(na);
  1130. IF (Cmp(s, "pim") # 0) AND (Cmp(s, "iso") # 0)
  1131. AND (Cmp(s, "gm2") # 0) THEN
  1132. Out("M2make: --dialect expects pim, iso or gm2"); OutLn;
  1133. M2makeOS.ExitNow(2)
  1134. END;
  1135. Copy(s, dialect)
  1136. ELSIF Cmp(s, "--m2flag") = 0 THEN
  1137. ok := M2makeOS.GetArg(na, s, HIGH(s) + 1);
  1138. IF NOT ok THEN
  1139. Out("M2make: --m2flag expects an argument"); OutLn;
  1140. M2makeOS.ExitNow(2)
  1141. END;
  1142. INC(na);
  1143. Copy(s, m2Flags)
  1144. ELSIF Cmp(s, "--extra") = 0 THEN
  1145. ok := M2makeOS.GetArg(na, s, HIGH(s) + 1);
  1146. IF NOT ok THEN
  1147. Out("M2make: --extra expects a path"); OutLn;
  1148. M2makeOS.ExitNow(2)
  1149. END;
  1150. INC(na);
  1151. Copy(s, extraC)
  1152. ELSIF (Cmp(s, "-h") = 0) OR (Cmp(s, "--help") = 0) THEN
  1153. Usage;
  1154. M2makeOS.ExitNow(0)
  1155. ELSIF s[0] = "-" THEN
  1156. Out("M2make: unknown option ");
  1157. Out(s);
  1158. OutLn;
  1159. Usage;
  1160. M2makeOS.ExitNow(2)
  1161. ELSE
  1162. IF Len(mainPath) > 0 THEN
  1163. Out("M2make: only one main file"); OutLn;
  1164. Usage;
  1165. M2makeOS.ExitNow(2)
  1166. END;
  1167. Copy(s, mainPath)
  1168. END
  1169. END;
  1170. IF Len(mainPath) = 0 THEN
  1171. Usage;
  1172. M2makeOS.ExitNow(2)
  1173. END
  1174. END ParseArgs;
  1175. (* ---------------- main ---------------- *)
  1176. VAR
  1177. missing: BOOLEAN;
  1178. rebuild: BOOLEAN;
  1179. BEGIN
  1180. nMods := 0;
  1181. nOrder := 0;
  1182. wantProg := TRUE;
  1183. Zero(progName);
  1184. ParseArgs;
  1185. IF Len(dialect) > 0 THEN
  1186. IF nDirs + 4 > MaxDirs THEN Die("M2make: too many -I dirs", 2) END;
  1187. Copy("lib/", tmpP); AppendSeg(tmpP, dialect); Copy(tmpP, tmpQ);
  1188. PutDir(nDirs, tmpQ); INC(nDirs);
  1189. Copy("lib/common", tmpQ); PutDir(nDirs, tmpQ); INC(nDirs);
  1190. Copy("lib/gm2", tmpQ); PutDir(nDirs, tmpQ); INC(nDirs);
  1191. Copy("runtime/syslib", tmpQ); PutDir(nDirs, tmpQ); INC(nDirs)
  1192. END;
  1193. IF NOT M2makeOS.FileExists(mainPath) THEN
  1194. Cat("M2make: main file not found ", mainPath, tmpCmd);
  1195. Die(tmpCmd, 2)
  1196. END;
  1197. DirOf(mainPath, tmpA);
  1198. IF nDirs = 0 THEN
  1199. Copy(tmpA, tmpP);
  1200. PutDir(0, tmpP);
  1201. nDirs := 1
  1202. ELSE
  1203. (* main dir becomes an extra search dir when -I was given *)
  1204. IF nDirs >= MaxDirs THEN Die("M2make: too many -I dirs", 2) END;
  1205. Copy(tmpA, tmpP);
  1206. PutDir(nDirs, tmpP);
  1207. INC(nDirs)
  1208. END;
  1209. mainIdx := AddMod("M2makeMain");
  1210. modIsMain[VAL(CARDINAL, mainIdx)] := TRUE;
  1211. Copy(mainPath, tmpP);
  1212. PutSrc(VAL(CARDINAL, mainIdx), tmpP);
  1213. ResolveAll;
  1214. IF Len(progName) = 0 THEN
  1215. Die("M2make: no MODULE declaration in main file", 1)
  1216. END;
  1217. IF NOT outGiven THEN Copy(progName, outName) END;
  1218. TopoSort;
  1219. IF verbose OR dryRun THEN PrintOrder END;
  1220. rebuild := NeedBuild(outName, missing);
  1221. IF NOT rebuild THEN
  1222. Out("M2make: ");
  1223. Out(outName);
  1224. Out(" up to date");
  1225. OutLn
  1226. ELSE
  1227. IF useGm2 THEN BuildGm2(outName)
  1228. ELSIF separate THEN BuildM2Separate(outName)
  1229. ELSE BuildM2(outName)
  1230. END
  1231. END
  1232. END M2make.