M2make.mod 40 KB

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