M2make.mod 37 KB

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