M2make.mod 37 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425
  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. AppendTok(tmpCmd, src)
  933. ELSE
  934. (* M2 resolves the target .def and all imported .defs from -I. *)
  935. IFlags(flags);
  936. IF Len(flags) > 0 THEN
  937. AppendSeg(tmpCmd, " ");
  938. AppendSeg(tmpCmd, flags)
  939. END;
  940. IF modIsInstance[m] THEN
  941. gi := FindMod(modInstGen[m]);
  942. IF gi >= 0 THEN
  943. GetDef(VAL(CARDINAL, gi), tmpP);
  944. IF Len(tmpP) > 0 THEN AppendTok(tmpCmd, tmpP) END;
  945. GetSrc(VAL(CARDINAL, gi), tmpP);
  946. IF Len(tmpP) > 0 THEN AppendTok(tmpCmd, tmpP) END
  947. END;
  948. GetDef(m, tmpP);
  949. IF Len(tmpP) > 0 THEN AppendTok(tmpCmd, tmpP) END
  950. END;
  951. AppendTok(tmpCmd, src)
  952. END;
  953. AppendSeg(tmpCmd, " 2>&1 | tee m2make.log | grep -q 'Parsed correctly'");
  954. AppendSeg(tmpCmd, " && qbe -o ");
  955. AppendTok(tmpCmd, sfile);
  956. AppendSeg(tmpCmd, " ");
  957. AppendTok(tmpCmd, ssafile);
  958. AppendSeg(tmpCmd, " && cc -c ");
  959. AppendTok(tmpCmd, sfile);
  960. AppendSeg(tmpCmd, " -o ");
  961. AppendTok(tmpCmd, obj);
  962. CheckLen(tmpCmd);
  963. Out(tmpCmd);
  964. OutLn;
  965. IF NOT dryRun THEN
  966. rc := M2makeOS.ExecCmd(tmpCmd);
  967. IF rc # 0 THEN
  968. Out("M2make: separate compile failed");
  969. OutLn;
  970. M2makeOS.ExitNow(1)
  971. END
  972. END;
  973. compiledAny := TRUE
  974. END
  975. END;
  976. INC(k)
  977. END;
  978. linkNeeded := compiledAny;
  979. IF NOT linkNeeded THEN
  980. IF NOT M2makeOS.FileExists(out) THEN linkNeeded := TRUE
  981. ELSIF NOT M2makeOS.FileMTime(out, outT) THEN linkNeeded := TRUE
  982. ELSE
  983. k := 0;
  984. WHILE k < nOrder DO
  985. m := order[k];
  986. GetSrc(m, src);
  987. IF Len(src) > 0 THEN
  988. IF modIsMain[m] THEN Copy(progName, tmpN) ELSE GetName(m, tmpN) END;
  989. Zero(obj); Cat("gen_ssa/", tmpN, tmpA); Cat(tmpA, ".o", obj);
  990. IF ObjNewerThan(obj, out, outT) THEN linkNeeded := TRUE END
  991. END;
  992. INC(k)
  993. END;
  994. IF SrcNewer(shimPath, outT) THEN linkNeeded := TRUE END;
  995. IF Len(extraC) > 0 THEN
  996. IF SrcNewer(extraC, outT) THEN linkNeeded := TRUE END
  997. END
  998. END
  999. END;
  1000. IF NOT linkNeeded THEN
  1001. Out("M2make: "); Out(out); Out(" up to date"); OutLn;
  1002. RETURN
  1003. END;
  1004. (* link: one object per module, target object (main) last *)
  1005. Zero(tmpCmd);
  1006. AppendSeg(tmpCmd, "cc ");
  1007. k := 0;
  1008. WHILE k < nOrder DO
  1009. m := order[k];
  1010. GetSrc(m, src);
  1011. GetDef(m, tmpQ);
  1012. IF (NOT modIsGeneric[m]) AND
  1013. ((Len(src) > 0) OR ((NOT modIsMain[m]) AND (Len(tmpQ) > 0))) THEN
  1014. IF modIsMain[m] THEN Copy(progName, tmpN) ELSE GetName(m, tmpN) END;
  1015. Zero(obj); Cat("gen_ssa/", tmpN, tmpA); Cat(tmpA, ".o", obj);
  1016. AppendTok(tmpCmd, obj)
  1017. END;
  1018. INC(k)
  1019. END;
  1020. AppendTok(tmpCmd, shimPath);
  1021. IF Len(extraC) > 0 THEN AppendTok(tmpCmd, extraC) END;
  1022. AppendSeg(tmpCmd, " -o ");
  1023. AppendTok(tmpCmd, out);
  1024. AppendSeg(tmpCmd, " -lm");
  1025. CheckLen(tmpCmd);
  1026. Out(tmpCmd);
  1027. OutLn;
  1028. IF dryRun THEN RETURN END;
  1029. rc := M2makeOS.ExecCmd(tmpCmd);
  1030. IF rc # 0 THEN
  1031. Out("M2make: separate build failed");
  1032. OutLn;
  1033. M2makeOS.ExitNow(1)
  1034. END;
  1035. WriteAbiStamp
  1036. END BuildM2Separate;
  1037. PROCEDURE CpyPath(VAR dst: Path; src: ARRAY OF CHAR);
  1038. BEGIN
  1039. Copy(src, dst)
  1040. END CpyPath;
  1041. PROCEDURE BuildGm2(out: ARRAY OF CHAR);
  1042. VAR cmd, seg: Cmd; flags: Path; obj: Path;
  1043. k, m: CARDINAL; rc: INTEGER; outT: LONGINT;
  1044. compiledAny, linkNeeded: BOOLEAN;
  1045. BEGIN
  1046. Zero(cmd);
  1047. IFlags(flags);
  1048. compiledAny := FALSE;
  1049. k := 0;
  1050. WHILE k < nOrder DO
  1051. m := order[k];
  1052. GetSrc(m, tmpP);
  1053. IF (NOT modIsMain[m]) AND (Len(tmpP) > 0) THEN
  1054. BaseObj(tmpP, obj);
  1055. IF ObjStale(m, obj) THEN
  1056. Zero(seg);
  1057. Cat("gm2 -fiso ", flags, seg);
  1058. IF Len(flags) > 0 THEN
  1059. Cat(seg, " ", segA);
  1060. Copy(segA, seg)
  1061. END;
  1062. Cat(seg, "-c ", segA);
  1063. Copy(segA, seg);
  1064. Cat(seg, tmpP, segA);
  1065. Copy(segA, seg);
  1066. AppendCmd(cmd, seg);
  1067. compiledAny := TRUE
  1068. END
  1069. END;
  1070. INC(k)
  1071. END;
  1072. linkNeeded := compiledAny;
  1073. IF NOT linkNeeded THEN
  1074. IF NOT M2makeOS.FileExists(out) THEN linkNeeded := TRUE
  1075. ELSIF NOT M2makeOS.FileMTime(out, outT) THEN linkNeeded := TRUE
  1076. ELSE
  1077. k := 0;
  1078. WHILE k < nOrder DO
  1079. m := order[k];
  1080. GetSrc(m, tmpP);
  1081. IF (NOT modIsMain[m]) AND (Len(tmpP) > 0) THEN
  1082. BaseObj(tmpP, obj);
  1083. IF ObjNewerThan(obj, out, outT) THEN linkNeeded := TRUE END
  1084. END;
  1085. INC(k)
  1086. END
  1087. END
  1088. END;
  1089. IF linkNeeded THEN
  1090. Zero(seg);
  1091. Cat("gm2 -fiso ", flags, seg);
  1092. IF Len(flags) > 0 THEN
  1093. Cat(seg, " ", segA);
  1094. Copy(segA, seg)
  1095. END;
  1096. Cat(seg, "-o ", segA);
  1097. Copy(segA, seg);
  1098. Cat(seg, out, segA);
  1099. Copy(segA, seg);
  1100. Cat(seg, " ", segA);
  1101. Copy(segA, seg);
  1102. GetSrc(VAL(CARDINAL, mainIdx), tmpP);
  1103. Cat(seg, tmpP, segA);
  1104. Copy(segA, seg);
  1105. k := 0;
  1106. WHILE k < nOrder DO
  1107. m := order[k];
  1108. GetSrc(m, tmpP);
  1109. IF (NOT modIsMain[m]) AND (Len(tmpP) > 0) THEN
  1110. BaseObj(tmpP, obj);
  1111. Cat(seg, " ", segA);
  1112. Copy(segA, seg);
  1113. Cat(seg, obj, segA);
  1114. Copy(segA, seg)
  1115. END;
  1116. INC(k)
  1117. END;
  1118. AppendCmd(cmd, seg)
  1119. END;
  1120. IF Len(cmd) = 0 THEN
  1121. Out("M2make: ");
  1122. Out(out);
  1123. Out(" up to date");
  1124. OutLn;
  1125. RETURN
  1126. END;
  1127. CheckLen(cmd);
  1128. Out(cmd);
  1129. OutLn;
  1130. IF dryRun THEN RETURN END;
  1131. rc := M2makeOS.ExecCmd(cmd);
  1132. IF rc # 0 THEN
  1133. Out("M2make: build failed");
  1134. OutLn;
  1135. M2makeOS.ExitNow(1)
  1136. END
  1137. END BuildGm2;
  1138. PROCEDURE PrintOrder;
  1139. VAR k: CARDINAL; m: CARDINAL;
  1140. BEGIN
  1141. Out("M2make: build order");
  1142. OutLn;
  1143. k := 0;
  1144. WHILE k < nOrder DO
  1145. m := order[k];
  1146. Out(" ");
  1147. GetName(m, tmpN);
  1148. Out(tmpN);
  1149. GetDef(m, tmpP);
  1150. IF Len(tmpP) > 0 THEN
  1151. Out(" : ");
  1152. Out(tmpP)
  1153. END;
  1154. GetSrc(m, tmpP);
  1155. IF Len(tmpP) > 0 THEN
  1156. GetDef(m, tmpQ);
  1157. IF Len(tmpQ) > 0 THEN Out(" ") ELSE Out(" : ") END;
  1158. Out(tmpP)
  1159. END;
  1160. OutLn;
  1161. INC(k)
  1162. END
  1163. END PrintOrder;
  1164. (* ---------------- argument handling ---------------- *)
  1165. PROCEDURE ParseArgs;
  1166. VAR na: CARDINAL; s: Path; ok: BOOLEAN;
  1167. BEGIN
  1168. dryRun := FALSE;
  1169. verbose := FALSE;
  1170. useGm2 := FALSE;
  1171. separate := FALSE;
  1172. outGiven := FALSE;
  1173. Zero(mainPath);
  1174. Zero(outName);
  1175. Copy("M2", m2bin);
  1176. Copy("shim.c", shimPath);
  1177. Zero(m2Flags);
  1178. dialect[0] := CHR(0);
  1179. nDirs := 0;
  1180. na := 0;
  1181. LOOP
  1182. IF na >= MaxArgs THEN Die("M2make: too many arguments", 2) END;
  1183. ok := M2makeOS.GetArg(na, s, HIGH(s) + 1);
  1184. IF NOT ok THEN EXIT END;
  1185. INC(na);
  1186. IF Cmp(s, "-n") = 0 THEN dryRun := TRUE
  1187. ELSIF Cmp(s, "-v") = 0 THEN verbose := TRUE
  1188. ELSIF Cmp(s, "--separate") = 0 THEN separate := TRUE
  1189. ELSIF Cmp(s, "-c") = 0 THEN
  1190. ok := M2makeOS.GetArg(na, s, HIGH(s) + 1);
  1191. IF NOT ok THEN
  1192. Out("M2make: -c expects m2 or gm2"); OutLn;
  1193. M2makeOS.ExitNow(2)
  1194. END;
  1195. INC(na);
  1196. IF Cmp(s, "gm2") = 0 THEN useGm2 := TRUE
  1197. ELSIF Cmp(s, "m2") = 0 THEN useGm2 := FALSE
  1198. ELSE
  1199. Out("M2make: -c expects m2 or gm2"); OutLn;
  1200. M2makeOS.ExitNow(2)
  1201. END
  1202. ELSIF Cmp(s, "-I") = 0 THEN
  1203. ok := M2makeOS.GetArg(na, s, HIGH(s) + 1);
  1204. IF NOT ok THEN
  1205. Out("M2make: -I expects a directory"); OutLn;
  1206. M2makeOS.ExitNow(2)
  1207. END;
  1208. INC(na);
  1209. IF nDirs >= MaxDirs THEN Die("M2make: too many -I dirs", 2) END;
  1210. Copy(s, tmpP);
  1211. PutDir(nDirs, tmpP);
  1212. INC(nDirs)
  1213. ELSIF Cmp(s, "-o") = 0 THEN
  1214. ok := M2makeOS.GetArg(na, s, HIGH(s) + 1);
  1215. IF NOT ok THEN
  1216. Out("M2make: -o expects a name"); OutLn;
  1217. M2makeOS.ExitNow(2)
  1218. END;
  1219. INC(na);
  1220. Copy(s, outName);
  1221. outGiven := TRUE
  1222. ELSIF Cmp(s, "--m2bin") = 0 THEN
  1223. ok := M2makeOS.GetArg(na, s, HIGH(s) + 1);
  1224. IF NOT ok THEN
  1225. Out("M2make: --m2bin expects a path"); OutLn;
  1226. M2makeOS.ExitNow(2)
  1227. END;
  1228. INC(na);
  1229. Copy(s, m2bin)
  1230. ELSIF Cmp(s, "--shim") = 0 THEN
  1231. ok := M2makeOS.GetArg(na, s, HIGH(s) + 1);
  1232. IF NOT ok THEN
  1233. Out("M2make: --shim expects a path"); OutLn;
  1234. M2makeOS.ExitNow(2)
  1235. END;
  1236. INC(na);
  1237. Copy(s, shimPath)
  1238. ELSIF Cmp(s, "--dialect") = 0 THEN
  1239. ok := M2makeOS.GetArg(na, s, HIGH(s) + 1);
  1240. IF NOT ok THEN
  1241. Out("M2make: --dialect expects pim, iso or gm2"); OutLn;
  1242. M2makeOS.ExitNow(2)
  1243. END;
  1244. INC(na);
  1245. IF (Cmp(s, "pim") # 0) AND (Cmp(s, "iso") # 0)
  1246. AND (Cmp(s, "gm2") # 0) THEN
  1247. Out("M2make: --dialect expects pim, iso or gm2"); OutLn;
  1248. M2makeOS.ExitNow(2)
  1249. END;
  1250. Copy(s, dialect)
  1251. ELSIF Cmp(s, "--m2flag") = 0 THEN
  1252. ok := M2makeOS.GetArg(na, s, HIGH(s) + 1);
  1253. IF NOT ok THEN
  1254. Out("M2make: --m2flag expects an argument"); OutLn;
  1255. M2makeOS.ExitNow(2)
  1256. END;
  1257. INC(na);
  1258. Copy(s, m2Flags)
  1259. ELSIF Cmp(s, "--extra") = 0 THEN
  1260. ok := M2makeOS.GetArg(na, s, HIGH(s) + 1);
  1261. IF NOT ok THEN
  1262. Out("M2make: --extra expects a path"); OutLn;
  1263. M2makeOS.ExitNow(2)
  1264. END;
  1265. INC(na);
  1266. Copy(s, extraC)
  1267. ELSIF (Cmp(s, "-h") = 0) OR (Cmp(s, "--help") = 0) THEN
  1268. Usage;
  1269. M2makeOS.ExitNow(0)
  1270. ELSIF s[0] = "-" THEN
  1271. Out("M2make: unknown option ");
  1272. Out(s);
  1273. OutLn;
  1274. Usage;
  1275. M2makeOS.ExitNow(2)
  1276. ELSE
  1277. IF Len(mainPath) > 0 THEN
  1278. Out("M2make: only one main file"); OutLn;
  1279. Usage;
  1280. M2makeOS.ExitNow(2)
  1281. END;
  1282. Copy(s, mainPath)
  1283. END
  1284. END;
  1285. IF Len(mainPath) = 0 THEN
  1286. Usage;
  1287. M2makeOS.ExitNow(2)
  1288. END
  1289. END ParseArgs;
  1290. (* ---------------- main ---------------- *)
  1291. VAR
  1292. missing: BOOLEAN;
  1293. rebuild: BOOLEAN;
  1294. BEGIN
  1295. nMods := 0;
  1296. nOrder := 0;
  1297. wantProg := TRUE;
  1298. Zero(progName);
  1299. ParseArgs;
  1300. IF Len(dialect) > 0 THEN
  1301. IF nDirs + 4 > MaxDirs THEN Die("M2make: too many -I dirs", 2) END;
  1302. Copy("lib/", tmpP); AppendSeg(tmpP, dialect); Copy(tmpP, tmpQ);
  1303. PutDir(nDirs, tmpQ); INC(nDirs);
  1304. Copy("lib/common", tmpQ); PutDir(nDirs, tmpQ); INC(nDirs);
  1305. Copy("lib/gm2", tmpQ); PutDir(nDirs, tmpQ); INC(nDirs);
  1306. Copy("runtime/syslib", tmpQ); PutDir(nDirs, tmpQ); INC(nDirs)
  1307. END;
  1308. IF NOT M2makeOS.FileExists(mainPath) THEN
  1309. Cat("M2make: main file not found ", mainPath, tmpCmd);
  1310. Die(tmpCmd, 2)
  1311. END;
  1312. DirOf(mainPath, tmpA);
  1313. IF nDirs = 0 THEN
  1314. Copy(tmpA, tmpP);
  1315. PutDir(0, tmpP);
  1316. nDirs := 1
  1317. ELSE
  1318. (* main dir becomes an extra search dir when -I was given *)
  1319. IF nDirs >= MaxDirs THEN Die("M2make: too many -I dirs", 2) END;
  1320. Copy(tmpA, tmpP);
  1321. PutDir(nDirs, tmpP);
  1322. INC(nDirs)
  1323. END;
  1324. mainIdx := AddMod("M2makeMain");
  1325. modIsMain[VAL(CARDINAL, mainIdx)] := TRUE;
  1326. Copy(mainPath, tmpP);
  1327. PutSrc(VAL(CARDINAL, mainIdx), tmpP);
  1328. ResolveAll;
  1329. IF Len(progName) = 0 THEN
  1330. Die("M2make: no MODULE declaration in main file", 1)
  1331. END;
  1332. IF NOT outGiven THEN Copy(progName, outName) END;
  1333. TopoSort;
  1334. IF verbose OR dryRun THEN PrintOrder END;
  1335. IF separate THEN
  1336. (* BuildM2Separate owns per-object freshness and the ABI stamp. *)
  1337. rebuild := TRUE
  1338. ELSE
  1339. rebuild := NeedBuild(outName, missing)
  1340. END;
  1341. IF NOT rebuild THEN
  1342. Out("M2make: ");
  1343. Out(outName);
  1344. Out(" up to date");
  1345. OutLn
  1346. ELSE
  1347. IF useGm2 THEN BuildGm2(outName)
  1348. ELSIF separate THEN BuildM2Separate(outName)
  1349. ELSE BuildM2(outName)
  1350. END
  1351. END
  1352. END M2make.