M2make.mod 34 KB

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