M2make.mod 30 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237
  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(cand) > 0 THEN Copy(cand, dp); found := TRUE END;
  357. TryVariant(tmpQ, n, ".mod", cand);
  358. IF Len(cand) > 0 THEN Copy(cand, mp); found := TRUE END;
  359. TryVariant(tmpQ, low, ".def", cand);
  360. IF (Len(dp) = 0) AND (Len(cand) > 0) THEN
  361. Copy(cand, dp); found := TRUE
  362. END;
  363. TryVariant(tmpQ, low, ".mod", cand);
  364. IF (Len(mp) = 0) AND (Len(cand) > 0) THEN
  365. Copy(cand, mp); found := TRUE
  366. END;
  367. INC(di)
  368. END;
  369. RETURN found
  370. END ResolveModule;
  371. (* ---------------- import scanner ---------------- *)
  372. PROCEDURE HandleTok(tok: ARRAY OF CHAR; midx: INTEGER);
  373. BEGIN
  374. IF Cmp(tok, "FROM") = 0 THEN
  375. gPendingFrom := TRUE;
  376. gInImport := FALSE;
  377. gSawHead := FALSE;
  378. gWantName := FALSE;
  379. gFromSeen := FALSE;
  380. RETURN
  381. END;
  382. IF Cmp(tok, "IMPORT") = 0 THEN
  383. IF gPendingFrom OR gFromSeen THEN
  384. (* FROM M IMPORT ... : following names are items, not modules *)
  385. gPendingFrom := FALSE;
  386. gFromSeen := FALSE
  387. ELSE gInImport := TRUE
  388. END;
  389. gSawHead := FALSE;
  390. gWantName := FALSE;
  391. RETURN
  392. END;
  393. IF Cmp(tok, "DEFINITION") = 0 THEN
  394. gSawHead := TRUE;
  395. gPendingFrom := FALSE;
  396. gInImport := FALSE;
  397. gWantName := FALSE;
  398. gFromSeen := FALSE;
  399. RETURN
  400. END;
  401. IF Cmp(tok, "IMPLEMENTATION") = 0 THEN
  402. gSawHead := TRUE;
  403. gPendingFrom := FALSE;
  404. gInImport := FALSE;
  405. gWantName := FALSE;
  406. gFromSeen := FALSE;
  407. RETURN
  408. END;
  409. IF Cmp(tok, "MODULE") = 0 THEN
  410. IF gSawHead THEN gWantName := FALSE
  411. ELSE gWantName := TRUE
  412. END;
  413. gSawHead := FALSE;
  414. gPendingFrom := FALSE;
  415. gInImport := FALSE;
  416. gFromSeen := FALSE;
  417. RETURN
  418. END;
  419. IF gPendingFrom THEN
  420. AddDepName(midx, tok);
  421. gPendingFrom := FALSE;
  422. gFromSeen := TRUE;
  423. gSawHead := FALSE;
  424. RETURN
  425. END;
  426. IF gInImport THEN
  427. AddDepName(midx, tok);
  428. gSawHead := FALSE;
  429. gFromSeen := FALSE;
  430. RETURN
  431. END;
  432. IF gWantName THEN
  433. gWantName := FALSE;
  434. IF wantProg AND (midx = mainIdx) THEN
  435. Copy(tok, progName);
  436. wantProg := FALSE
  437. END;
  438. gSawHead := FALSE;
  439. gFromSeen := FALSE;
  440. RETURN
  441. END;
  442. gSawHead := FALSE;
  443. gFromSeen := FALSE
  444. END HandleTok;
  445. PROCEDURE ScanLine(line: ARRAY OF CHAR; midx: INTEGER);
  446. VAR i, n, k: CARDINAL; ch, qc: CHAR;
  447. inStr: BOOLEAN; tok: Name;
  448. BEGIN
  449. n := Len(line);
  450. i := 0;
  451. inStr := FALSE;
  452. qc := CHR(0);
  453. WHILE i < n DO
  454. ch := line[i];
  455. IF inStr THEN
  456. IF ch = qc THEN
  457. IF (i + 1 < n) AND (line[i + 1] = qc) THEN i := i + 2
  458. ELSE inStr := FALSE; INC(i)
  459. END
  460. ELSE INC(i)
  461. END
  462. ELSIF inComment > 0 THEN
  463. IF (ch = "(") AND (i + 1 < n) AND (line[i + 1] = "*") THEN
  464. INC(inComment); i := i + 2
  465. ELSIF (ch = "*") AND (i + 1 < n) AND (line[i + 1] = ")") THEN
  466. DEC(inComment); i := i + 2
  467. ELSE INC(i)
  468. END
  469. ELSE
  470. IF (ch = "(") AND (i + 1 < n) AND (line[i + 1] = "*") THEN
  471. INC(inComment); i := i + 2
  472. ELSIF (ch = "/") AND (i + 1 < n) AND (line[i + 1] = "/") THEN
  473. i := n
  474. ELSIF (ch = "'") OR (ch = '"') THEN
  475. inStr := TRUE; qc := ch; INC(i)
  476. ELSIF ch = ";" THEN
  477. gPendingFrom := FALSE;
  478. gInImport := FALSE;
  479. gSawHead := FALSE;
  480. gWantName := FALSE;
  481. gFromSeen := FALSE;
  482. INC(i)
  483. ELSIF IsLetter(ch) THEN
  484. k := i;
  485. WHILE (k < n) AND (IsLetter(line[k]) OR IsDig(line[k])) DO
  486. INC(k)
  487. END;
  488. Sub(line, i, k - i, tok);
  489. i := k;
  490. HandleTok(tok, midx)
  491. ELSE INC(i)
  492. END
  493. END
  494. END
  495. END ScanLine;
  496. PROCEDURE ScanFile(path: ARRAY OF CHAR; midx: INTEGER);
  497. VAR h: M2makeOS.RdFile; line: Line; ok: BOOLEAN;
  498. BEGIN
  499. h := M2makeOS.OpenRead(path);
  500. IF h = NIL THEN
  501. Cat("M2make: cannot open ", path, tmpCmd);
  502. Die(tmpCmd, 1)
  503. END;
  504. LOOP
  505. Zero(line);
  506. ok := M2makeOS.ReadLine(h, line, HIGH(line) + 1);
  507. IF NOT ok THEN EXIT END;
  508. ScanLine(line, midx)
  509. END;
  510. M2makeOS.CloseRead(h)
  511. END ScanFile;
  512. PROCEDURE ScanModule(m: CARDINAL);
  513. VAR dp, mp: Path;
  514. BEGIN
  515. IF NOT modIsMain[m] THEN
  516. Zero(dp);
  517. Zero(mp);
  518. GetName(m, tmpN);
  519. IF NOT ResolveModule(tmpN, dp, mp) THEN
  520. modExternal[m] := TRUE;
  521. modScanned[m] := TRUE;
  522. IF verbose THEN
  523. Out("M2make: external module ");
  524. Out(tmpN);
  525. OutLn
  526. END;
  527. RETURN
  528. END;
  529. Copy(dp, tmpP);
  530. PutDef(m, tmpP);
  531. Copy(mp, tmpP);
  532. PutSrc(m, tmpP)
  533. END;
  534. inComment := 0;
  535. gPendingFrom := FALSE;
  536. gInImport := FALSE;
  537. gSawHead := FALSE;
  538. gWantName := FALSE;
  539. gFromSeen := FALSE;
  540. GetDef(m, tmpP);
  541. IF Len(tmpP) > 0 THEN ScanFile(tmpP, VAL(INTEGER, m)) END;
  542. GetSrc(m, tmpP);
  543. IF Len(tmpP) > 0 THEN ScanFile(tmpP, VAL(INTEGER, m)) END;
  544. modScanned[m] := TRUE
  545. END ScanModule;
  546. PROCEDURE ResolveAll;
  547. VAR i: CARDINAL;
  548. BEGIN
  549. i := 0;
  550. WHILE i < nMods DO
  551. IF NOT modScanned[i] THEN ScanModule(i) END;
  552. INC(i)
  553. END
  554. END ResolveAll;
  555. (* ---------------- topological sort (iterative DFS) ---------------- *)
  556. PROCEDURE TopoSort;
  557. VAR stack: ARRAY [0..263] OF CARDINAL;
  558. top, cur: CARDINAL; d: INTEGER; dd: CARDINAL; i: CARDINAL;
  559. BEGIN
  560. i := 0;
  561. WHILE i < nMods DO
  562. state[i] := 0; childPos[i] := 0; INC(i)
  563. END;
  564. nOrder := 0;
  565. top := 0;
  566. stack[0] := VAL(CARDINAL, mainIdx);
  567. state[VAL(CARDINAL, mainIdx)] := 1;
  568. LOOP
  569. cur := stack[top];
  570. IF childPos[cur] < nDep[cur] THEN
  571. d := depTab[cur * MaxDeps + childPos[cur]];
  572. INC(childPos[cur]);
  573. IF d < 0 THEN Die("M2make: bad dependency", 1) END;
  574. dd := VAL(CARDINAL, d);
  575. IF NOT modExternal[dd] THEN
  576. IF state[dd] = 0 THEN
  577. state[dd] := 1;
  578. INC(top);
  579. IF top > HIGH(stack) THEN Die("M2make: stack overflow", 1) END;
  580. stack[top] := dd
  581. ELSIF state[dd] = 1 THEN
  582. GetName(dd, tmpN);
  583. Out("M2make: cyclic import involving ");
  584. Out(tmpN);
  585. OutLn;
  586. M2makeOS.ExitNow(1)
  587. END
  588. END
  589. ELSE
  590. state[cur] := 2;
  591. order[nOrder] := cur;
  592. INC(nOrder);
  593. IF top = 0 THEN EXIT END;
  594. DEC(top)
  595. END
  596. END
  597. END TopoSort;
  598. (* ---------------- staleness ---------------- *)
  599. PROCEDURE SrcNewer(src: ARRAY OF CHAR; outT: LONGINT): BOOLEAN;
  600. VAR t: LONGINT;
  601. BEGIN
  602. IF Len(src) = 0 THEN RETURN FALSE END;
  603. IF NOT M2makeOS.FileMTime(src, t) THEN RETURN TRUE END;
  604. RETURN t > outT
  605. END SrcNewer;
  606. PROCEDURE NeedBuild(out: ARRAY OF CHAR; VAR missing: BOOLEAN): BOOLEAN;
  607. VAR outT: LONGINT; k: CARDINAL; m: CARDINAL;
  608. BEGIN
  609. missing := FALSE;
  610. IF NOT M2makeOS.FileExists(out) THEN
  611. missing := TRUE;
  612. RETURN TRUE
  613. END;
  614. IF NOT M2makeOS.FileMTime(out, outT) THEN RETURN TRUE END;
  615. k := 0;
  616. WHILE k < nOrder DO
  617. m := order[k];
  618. GetDef(m, tmpP);
  619. IF SrcNewer(tmpP, outT) THEN RETURN TRUE END;
  620. GetSrc(m, tmpP);
  621. IF SrcNewer(tmpP, outT) THEN RETURN TRUE END;
  622. INC(k)
  623. END;
  624. RETURN FALSE
  625. END NeedBuild;
  626. (* ---------------- command construction ---------------- *)
  627. PROCEDURE AppendTok(VAR cmd: ARRAY OF CHAR; tok: ARRAY OF CHAR);
  628. BEGIN
  629. IF Len(cmd) > 0 THEN
  630. Cat(cmd, " ", segA);
  631. Copy(segA, cmd)
  632. END;
  633. Cat(cmd, tok, segA);
  634. Copy(segA, cmd)
  635. END AppendTok;
  636. PROCEDURE IFlags(VAR flags: ARRAY OF CHAR);
  637. VAR di: CARDINAL;
  638. BEGIN
  639. Zero(flags);
  640. di := 0;
  641. WHILE di < nDirs DO
  642. GetDir(di, tmpP);
  643. IF Len(tmpP) > 0 THEN
  644. AppendTok(flags, "-I");
  645. AppendTok(flags, tmpP)
  646. END;
  647. INC(di)
  648. END
  649. END IFlags;
  650. PROCEDURE AppendCh(VAR s: ARRAY OF CHAR; ch: CHAR);
  651. VAR n: CARDINAL;
  652. BEGIN
  653. n := Len(s);
  654. IF n <= HIGH(s) THEN
  655. s[n] := ch;
  656. IF n + 1 <= HIGH(s) THEN s[n + 1] := CHR(0) END
  657. END
  658. END AppendCh;
  659. PROCEDURE BaseObj(src: ARRAY OF CHAR; VAR obj: ARRAY OF CHAR);
  660. (* "some/dir/Calc.mod" -> "Calc.o" (gm2 -c drops objects in the cwd). *)
  661. VAR i, n, slash, dot, k: CARDINAL;
  662. BEGIN
  663. Zero(obj);
  664. n := Len(src);
  665. slash := 0;
  666. i := 0;
  667. WHILE i < n DO
  668. IF src[i] = "/" THEN slash := i + 1 END;
  669. INC(i)
  670. END;
  671. dot := n;
  672. i := slash;
  673. WHILE i < n DO
  674. IF src[i] = "." THEN dot := i END;
  675. INC(i)
  676. END;
  677. IF dot <= slash THEN dot := n END;
  678. k := 0;
  679. i := slash;
  680. WHILE (i < dot) AND (k <= HIGH(obj)) DO
  681. obj[k] := src[i]; INC(k); INC(i)
  682. END;
  683. Cat(obj, ".o", tmpA);
  684. Copy(tmpA, obj)
  685. END BaseObj;
  686. PROCEDURE ObjStale(m: CARDINAL; obj: ARRAY OF CHAR): BOOLEAN;
  687. VAR ot: LONGINT;
  688. BEGIN
  689. IF NOT M2makeOS.FileExists(obj) THEN RETURN TRUE END;
  690. IF NOT M2makeOS.FileMTime(obj, ot) THEN RETURN TRUE END;
  691. GetDef(m, tmpP);
  692. IF SrcNewer(tmpP, ot) THEN RETURN TRUE END;
  693. GetSrc(m, tmpP);
  694. IF SrcNewer(tmpP, ot) THEN RETURN TRUE END;
  695. RETURN FALSE
  696. END ObjStale;
  697. PROCEDURE ObjNewerThan(obj, out: ARRAY OF CHAR;
  698. outT: LONGINT): BOOLEAN;
  699. VAR t: LONGINT;
  700. BEGIN
  701. IF Len(obj) = 0 THEN RETURN FALSE END;
  702. IF NOT M2makeOS.FileMTime(obj, t) THEN RETURN TRUE END;
  703. RETURN t > outT
  704. END ObjNewerThan;
  705. PROCEDURE CheckLen(cmd: ARRAY OF CHAR);
  706. BEGIN
  707. IF Len(cmd) + 32 > HIGH(cmd) THEN
  708. Die("M2make: command too long", 1)
  709. END
  710. END CheckLen;
  711. PROCEDURE AppendCmd(VAR cmd: ARRAY OF CHAR; seg: ARRAY OF CHAR);
  712. BEGIN
  713. IF Len(cmd) > 0 THEN
  714. Cat(cmd, " && ", segA);
  715. Copy(segA, cmd)
  716. END;
  717. Cat(cmd, seg, segA);
  718. Copy(segA, cmd)
  719. END AppendCmd;
  720. PROCEDURE AppendSeg(VAR cmd: ARRAY OF CHAR; seg: ARRAY OF CHAR);
  721. (* Direct concatenation (segments carry their own spacing).
  722. segA is the dedicated Cmd-sized scratch: always distinct from
  723. cmd and seg, and big enough for whole command lines. *)
  724. BEGIN
  725. Cat(cmd, seg, segA);
  726. Copy(segA, cmd)
  727. END AppendSeg;
  728. PROCEDURE BuildM2(out: ARRAY OF CHAR);
  729. VAR cmd: Cmd; k: CARDINAL; m: CARDINAL; rc: INTEGER;
  730. BEGIN
  731. Zero(cmd);
  732. AppendSeg(cmd, "mkdir -p gen_ssa && ");
  733. AppendSeg(cmd, m2bin);
  734. IF Len(m2Flags) > 0 THEN
  735. AppendSeg(cmd, " ");
  736. AppendSeg(cmd, m2Flags)
  737. END;
  738. k := 0;
  739. WHILE k < nOrder DO
  740. m := order[k];
  741. GetDef(m, tmpP);
  742. IF Len(tmpP) > 0 THEN AppendTok(cmd, tmpP) END;
  743. GetSrc(m, tmpP);
  744. IF Len(tmpP) > 0 THEN AppendTok(cmd, tmpP) END;
  745. INC(k)
  746. END;
  747. AppendSeg(cmd, " 2>&1 | tee m2make.log | grep -q 'Parsed correctly'");
  748. AppendSeg(cmd, " && qbe -o gen_ssa/");
  749. AppendSeg(cmd, progName);
  750. AppendSeg(cmd, ".s gen_ssa/");
  751. AppendSeg(cmd, progName);
  752. AppendSeg(cmd, ".ssa");
  753. AppendSeg(cmd, " && cc gen_ssa/");
  754. AppendSeg(cmd, progName);
  755. AppendSeg(cmd, ".s ");
  756. AppendTok(cmd, shimPath);
  757. IF Len(extraC) > 0 THEN AppendTok(cmd, extraC) END;
  758. AppendSeg(cmd, " -o ");
  759. AppendTok(cmd, out);
  760. AppendSeg(cmd, " -lm");
  761. CheckLen(cmd);
  762. Out(cmd);
  763. OutLn;
  764. IF dryRun THEN RETURN END;
  765. rc := M2makeOS.ExecCmd(cmd);
  766. IF rc # 0 THEN
  767. Out("M2make: build failed");
  768. OutLn;
  769. M2makeOS.ExitNow(1)
  770. END
  771. END BuildM2;
  772. PROCEDURE SepObjName(m: CARDINAL; VAR obj: Path);
  773. (* One object per module in gen_ssa/, keyed by module name. *)
  774. BEGIN
  775. Zero(obj);
  776. GetName(m, tmpN);
  777. Cat("gen_ssa/", tmpN, tmpA);
  778. Cat(tmpA, ".o", obj)
  779. END SepObjName;
  780. PROCEDURE BuildM2Separate(out: ARRAY OF CHAR);
  781. (* Compile every module to its own QBE object, then link. The program
  782. module is last in the topological order and is the only object with
  783. main; every module object exports <mod>_init. *)
  784. VAR cmd: Cmd; k, m: CARDINAL; rc: INTEGER;
  785. obj, src, ssafile, sfile: Path;
  786. BEGIN
  787. Zero(cmd);
  788. k := 0;
  789. WHILE k < nOrder DO
  790. m := order[k];
  791. GetSrc(m, src);
  792. IF Len(src) > 0 THEN
  793. IF modIsMain[m] THEN Copy(progName, tmpN) ELSE GetName(m, tmpN) END;
  794. Zero(obj); Cat("gen_ssa/", tmpN, tmpA); Cat(tmpA, ".o", obj);
  795. CpyPath(ssafile, "gen_ssa/"); Cat(ssafile, tmpN, tmpA); Cat(tmpA, ".ssa", ssafile);
  796. CpyPath(sfile, "gen_ssa/"); Cat(sfile, tmpN, tmpA); Cat(tmpA, ".s", sfile);
  797. Zero(tmpCmd);
  798. AppendSeg(tmpCmd, "mkdir -p gen_ssa && ");
  799. AppendSeg(tmpCmd, m2bin);
  800. AppendSeg(tmpCmd, " -c");
  801. IF Len(m2Flags) > 0 THEN
  802. AppendSeg(tmpCmd, " ");
  803. AppendSeg(tmpCmd, m2Flags)
  804. END;
  805. (* all interfaces, in dependency order, then this object *)
  806. m := 0;
  807. WHILE m < nOrder DO
  808. GetDef(order[m], tmpP);
  809. IF Len(tmpP) > 0 THEN AppendTok(tmpCmd, tmpP) END;
  810. INC(m)
  811. END;
  812. AppendTok(tmpCmd, src);
  813. AppendSeg(tmpCmd, " 2>&1 | tee m2make.log | grep -q 'Parsed correctly'");
  814. AppendSeg(tmpCmd, " && qbe -o ");
  815. AppendTok(tmpCmd, sfile);
  816. AppendSeg(tmpCmd, " ");
  817. AppendTok(tmpCmd, ssafile);
  818. AppendSeg(tmpCmd, " && cc -c ");
  819. AppendTok(tmpCmd, sfile);
  820. AppendSeg(tmpCmd, " -o ");
  821. AppendTok(tmpCmd, obj);
  822. AppendCmd(cmd, tmpCmd)
  823. END;
  824. INC(k)
  825. END;
  826. (* link: one object per module, target object (main) last *)
  827. Zero(tmpCmd);
  828. AppendSeg(tmpCmd, "cc ");
  829. k := 0;
  830. WHILE k < nOrder DO
  831. m := order[k];
  832. GetSrc(m, src);
  833. IF Len(src) > 0 THEN
  834. IF modIsMain[m] THEN Copy(progName, tmpN) ELSE GetName(m, tmpN) END;
  835. Zero(obj); Cat("gen_ssa/", tmpN, tmpA); Cat(tmpA, ".o", obj);
  836. AppendTok(tmpCmd, obj)
  837. END;
  838. INC(k)
  839. END;
  840. AppendTok(tmpCmd, shimPath);
  841. IF Len(extraC) > 0 THEN AppendTok(tmpCmd, extraC) END;
  842. AppendSeg(tmpCmd, " -o ");
  843. AppendTok(tmpCmd, out);
  844. AppendSeg(tmpCmd, " -lm");
  845. AppendCmd(cmd, tmpCmd);
  846. CheckLen(cmd);
  847. Out(cmd);
  848. OutLn;
  849. IF dryRun THEN RETURN END;
  850. rc := M2makeOS.ExecCmd(cmd);
  851. IF rc # 0 THEN
  852. Out("M2make: separate build failed");
  853. OutLn;
  854. M2makeOS.ExitNow(1)
  855. END
  856. END BuildM2Separate;
  857. PROCEDURE CpyPath(VAR dst: Path; src: ARRAY OF CHAR);
  858. BEGIN
  859. Copy(src, dst)
  860. END CpyPath;
  861. PROCEDURE BuildGm2(out: ARRAY OF CHAR);
  862. VAR cmd, seg: Cmd; flags: Path; obj: Path;
  863. k, m: CARDINAL; rc: INTEGER; outT: LONGINT;
  864. compiledAny, linkNeeded: BOOLEAN;
  865. BEGIN
  866. Zero(cmd);
  867. IFlags(flags);
  868. compiledAny := FALSE;
  869. k := 0;
  870. WHILE k < nOrder DO
  871. m := order[k];
  872. GetSrc(m, tmpP);
  873. IF (NOT modIsMain[m]) AND (Len(tmpP) > 0) THEN
  874. BaseObj(tmpP, obj);
  875. IF ObjStale(m, obj) THEN
  876. Zero(seg);
  877. Cat("gm2 -fiso ", flags, seg);
  878. IF Len(flags) > 0 THEN
  879. Cat(seg, " ", segA);
  880. Copy(segA, seg)
  881. END;
  882. Cat(seg, "-c ", segA);
  883. Copy(segA, seg);
  884. Cat(seg, tmpP, segA);
  885. Copy(segA, seg);
  886. AppendCmd(cmd, seg);
  887. compiledAny := TRUE
  888. END
  889. END;
  890. INC(k)
  891. END;
  892. linkNeeded := compiledAny;
  893. IF NOT linkNeeded THEN
  894. IF NOT M2makeOS.FileExists(out) THEN linkNeeded := TRUE
  895. ELSIF NOT M2makeOS.FileMTime(out, outT) THEN linkNeeded := TRUE
  896. ELSE
  897. k := 0;
  898. WHILE k < nOrder DO
  899. m := order[k];
  900. GetSrc(m, tmpP);
  901. IF (NOT modIsMain[m]) AND (Len(tmpP) > 0) THEN
  902. BaseObj(tmpP, obj);
  903. IF ObjNewerThan(obj, out, outT) THEN linkNeeded := TRUE END
  904. END;
  905. INC(k)
  906. END
  907. END
  908. END;
  909. IF linkNeeded THEN
  910. Zero(seg);
  911. Cat("gm2 -fiso ", flags, seg);
  912. IF Len(flags) > 0 THEN
  913. Cat(seg, " ", segA);
  914. Copy(segA, seg)
  915. END;
  916. Cat(seg, "-o ", segA);
  917. Copy(segA, seg);
  918. Cat(seg, out, segA);
  919. Copy(segA, seg);
  920. Cat(seg, " ", segA);
  921. Copy(segA, seg);
  922. GetSrc(VAL(CARDINAL, mainIdx), tmpP);
  923. Cat(seg, tmpP, segA);
  924. Copy(segA, seg);
  925. k := 0;
  926. WHILE k < nOrder DO
  927. m := order[k];
  928. GetSrc(m, tmpP);
  929. IF (NOT modIsMain[m]) AND (Len(tmpP) > 0) THEN
  930. BaseObj(tmpP, obj);
  931. Cat(seg, " ", segA);
  932. Copy(segA, seg);
  933. Cat(seg, obj, segA);
  934. Copy(segA, seg)
  935. END;
  936. INC(k)
  937. END;
  938. AppendCmd(cmd, seg)
  939. END;
  940. IF Len(cmd) = 0 THEN
  941. Out("M2make: ");
  942. Out(out);
  943. Out(" up to date");
  944. OutLn;
  945. RETURN
  946. END;
  947. CheckLen(cmd);
  948. Out(cmd);
  949. OutLn;
  950. IF dryRun THEN RETURN END;
  951. rc := M2makeOS.ExecCmd(cmd);
  952. IF rc # 0 THEN
  953. Out("M2make: build failed");
  954. OutLn;
  955. M2makeOS.ExitNow(1)
  956. END
  957. END BuildGm2;
  958. PROCEDURE PrintOrder;
  959. VAR k: CARDINAL; m: CARDINAL;
  960. BEGIN
  961. Out("M2make: build order");
  962. OutLn;
  963. k := 0;
  964. WHILE k < nOrder DO
  965. m := order[k];
  966. Out(" ");
  967. GetName(m, tmpN);
  968. Out(tmpN);
  969. GetDef(m, tmpP);
  970. IF Len(tmpP) > 0 THEN
  971. Out(" : ");
  972. Out(tmpP)
  973. END;
  974. GetSrc(m, tmpP);
  975. IF Len(tmpP) > 0 THEN
  976. GetDef(m, tmpQ);
  977. IF Len(tmpQ) > 0 THEN Out(" ") ELSE Out(" : ") END;
  978. Out(tmpP)
  979. END;
  980. OutLn;
  981. INC(k)
  982. END
  983. END PrintOrder;
  984. (* ---------------- argument handling ---------------- *)
  985. PROCEDURE ParseArgs;
  986. VAR na: CARDINAL; s: Path; ok: BOOLEAN;
  987. BEGIN
  988. dryRun := FALSE;
  989. verbose := FALSE;
  990. useGm2 := FALSE;
  991. separate := FALSE;
  992. outGiven := FALSE;
  993. Zero(mainPath);
  994. Zero(outName);
  995. Copy("M2", m2bin);
  996. Copy("shim.c", shimPath);
  997. Zero(m2Flags);
  998. dialect[0] := CHR(0);
  999. nDirs := 0;
  1000. na := 0;
  1001. LOOP
  1002. IF na >= MaxArgs THEN Die("M2make: too many arguments", 2) END;
  1003. ok := M2makeOS.GetArg(na, s, HIGH(s) + 1);
  1004. IF NOT ok THEN EXIT END;
  1005. INC(na);
  1006. IF Cmp(s, "-n") = 0 THEN dryRun := TRUE
  1007. ELSIF Cmp(s, "-v") = 0 THEN verbose := TRUE
  1008. ELSIF Cmp(s, "--separate") = 0 THEN separate := TRUE
  1009. ELSIF Cmp(s, "-c") = 0 THEN
  1010. ok := M2makeOS.GetArg(na, s, HIGH(s) + 1);
  1011. IF NOT ok THEN
  1012. Out("M2make: -c expects m2 or gm2"); OutLn;
  1013. M2makeOS.ExitNow(2)
  1014. END;
  1015. INC(na);
  1016. IF Cmp(s, "gm2") = 0 THEN useGm2 := TRUE
  1017. ELSIF Cmp(s, "m2") = 0 THEN useGm2 := FALSE
  1018. ELSE
  1019. Out("M2make: -c expects m2 or gm2"); OutLn;
  1020. M2makeOS.ExitNow(2)
  1021. END
  1022. ELSIF Cmp(s, "-I") = 0 THEN
  1023. ok := M2makeOS.GetArg(na, s, HIGH(s) + 1);
  1024. IF NOT ok THEN
  1025. Out("M2make: -I expects a directory"); OutLn;
  1026. M2makeOS.ExitNow(2)
  1027. END;
  1028. INC(na);
  1029. IF nDirs >= MaxDirs THEN Die("M2make: too many -I dirs", 2) END;
  1030. Copy(s, tmpP);
  1031. PutDir(nDirs, tmpP);
  1032. INC(nDirs)
  1033. ELSIF Cmp(s, "-o") = 0 THEN
  1034. ok := M2makeOS.GetArg(na, s, HIGH(s) + 1);
  1035. IF NOT ok THEN
  1036. Out("M2make: -o expects a name"); OutLn;
  1037. M2makeOS.ExitNow(2)
  1038. END;
  1039. INC(na);
  1040. Copy(s, outName);
  1041. outGiven := TRUE
  1042. ELSIF Cmp(s, "--m2bin") = 0 THEN
  1043. ok := M2makeOS.GetArg(na, s, HIGH(s) + 1);
  1044. IF NOT ok THEN
  1045. Out("M2make: --m2bin expects a path"); OutLn;
  1046. M2makeOS.ExitNow(2)
  1047. END;
  1048. INC(na);
  1049. Copy(s, m2bin)
  1050. ELSIF Cmp(s, "--shim") = 0 THEN
  1051. ok := M2makeOS.GetArg(na, s, HIGH(s) + 1);
  1052. IF NOT ok THEN
  1053. Out("M2make: --shim expects a path"); OutLn;
  1054. M2makeOS.ExitNow(2)
  1055. END;
  1056. INC(na);
  1057. Copy(s, shimPath)
  1058. ELSIF Cmp(s, "--dialect") = 0 THEN
  1059. ok := M2makeOS.GetArg(na, s, HIGH(s) + 1);
  1060. IF NOT ok THEN
  1061. Out("M2make: --dialect expects pim, iso or gm2"); OutLn;
  1062. M2makeOS.ExitNow(2)
  1063. END;
  1064. INC(na);
  1065. IF (Cmp(s, "pim") # 0) AND (Cmp(s, "iso") # 0)
  1066. AND (Cmp(s, "gm2") # 0) THEN
  1067. Out("M2make: --dialect expects pim, iso or gm2"); OutLn;
  1068. M2makeOS.ExitNow(2)
  1069. END;
  1070. Copy(s, dialect)
  1071. ELSIF Cmp(s, "--m2flag") = 0 THEN
  1072. ok := M2makeOS.GetArg(na, s, HIGH(s) + 1);
  1073. IF NOT ok THEN
  1074. Out("M2make: --m2flag expects an argument"); OutLn;
  1075. M2makeOS.ExitNow(2)
  1076. END;
  1077. INC(na);
  1078. Copy(s, m2Flags)
  1079. ELSIF Cmp(s, "--extra") = 0 THEN
  1080. ok := M2makeOS.GetArg(na, s, HIGH(s) + 1);
  1081. IF NOT ok THEN
  1082. Out("M2make: --extra expects a path"); OutLn;
  1083. M2makeOS.ExitNow(2)
  1084. END;
  1085. INC(na);
  1086. Copy(s, extraC)
  1087. ELSIF (Cmp(s, "-h") = 0) OR (Cmp(s, "--help") = 0) THEN
  1088. Usage;
  1089. M2makeOS.ExitNow(0)
  1090. ELSIF s[0] = "-" THEN
  1091. Out("M2make: unknown option ");
  1092. Out(s);
  1093. OutLn;
  1094. Usage;
  1095. M2makeOS.ExitNow(2)
  1096. ELSE
  1097. IF Len(mainPath) > 0 THEN
  1098. Out("M2make: only one main file"); OutLn;
  1099. Usage;
  1100. M2makeOS.ExitNow(2)
  1101. END;
  1102. Copy(s, mainPath)
  1103. END
  1104. END;
  1105. IF Len(mainPath) = 0 THEN
  1106. Usage;
  1107. M2makeOS.ExitNow(2)
  1108. END
  1109. END ParseArgs;
  1110. (* ---------------- main ---------------- *)
  1111. VAR
  1112. missing: BOOLEAN;
  1113. rebuild: BOOLEAN;
  1114. BEGIN
  1115. nMods := 0;
  1116. nOrder := 0;
  1117. wantProg := TRUE;
  1118. Zero(progName);
  1119. ParseArgs;
  1120. IF Len(dialect) > 0 THEN
  1121. IF nDirs + 4 > MaxDirs THEN Die("M2make: too many -I dirs", 2) END;
  1122. Copy("lib/", tmpP); AppendSeg(tmpP, dialect); Copy(tmpP, tmpQ);
  1123. PutDir(nDirs, tmpQ); INC(nDirs);
  1124. Copy("lib/common", tmpQ); PutDir(nDirs, tmpQ); INC(nDirs);
  1125. Copy("lib/gm2", tmpQ); PutDir(nDirs, tmpQ); INC(nDirs);
  1126. Copy("runtime/syslib", tmpQ); PutDir(nDirs, tmpQ); INC(nDirs)
  1127. END;
  1128. IF NOT M2makeOS.FileExists(mainPath) THEN
  1129. Cat("M2make: main file not found ", mainPath, tmpCmd);
  1130. Die(tmpCmd, 2)
  1131. END;
  1132. DirOf(mainPath, tmpA);
  1133. IF nDirs = 0 THEN
  1134. Copy(tmpA, tmpP);
  1135. PutDir(0, tmpP);
  1136. nDirs := 1
  1137. ELSE
  1138. (* main dir becomes an extra search dir when -I was given *)
  1139. IF nDirs >= MaxDirs THEN Die("M2make: too many -I dirs", 2) END;
  1140. Copy(tmpA, tmpP);
  1141. PutDir(nDirs, tmpP);
  1142. INC(nDirs)
  1143. END;
  1144. mainIdx := AddMod("M2makeMain");
  1145. modIsMain[VAL(CARDINAL, mainIdx)] := TRUE;
  1146. Copy(mainPath, tmpP);
  1147. PutSrc(VAL(CARDINAL, mainIdx), tmpP);
  1148. ResolveAll;
  1149. IF Len(progName) = 0 THEN
  1150. Die("M2make: no MODULE declaration in main file", 1)
  1151. END;
  1152. IF NOT outGiven THEN Copy(progName, outName) END;
  1153. TopoSort;
  1154. IF verbose OR dryRun THEN PrintOrder END;
  1155. rebuild := NeedBuild(outName, missing);
  1156. IF NOT rebuild THEN
  1157. Out("M2make: ");
  1158. Out(outName);
  1159. Out(" up to date");
  1160. OutLn
  1161. ELSE
  1162. IF useGm2 THEN BuildGm2(outName)
  1163. ELSIF separate THEN BuildM2Separate(outName)
  1164. ELSE BuildM2(outName)
  1165. END
  1166. END
  1167. END M2make.