Release.Mod 106 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368336933703371337233733374337533763377337833793380338133823383338433853386338733883389339033913392339333943395
  1. MODULE Release; (** AUTHOR "staubesv"; PURPOSE "Release build tool"; *)
  2. (**
  3. *
  4. * Usage:
  5. *
  6. * Release.Analyze [Options] ~ analyzes the package description file
  7. *
  8. * Option f, file : Package description file to be analyzed [default: Release.Tool]
  9. *
  10. * Release.CheckFiles [Options] filemask ~ lists all files that match to filemask but are not listed in the package description file
  11. *
  12. * Option f, file : Package description file to be analyzed [default: Release.Tool]
  13. *
  14. * Release.Check [Options] ~ builds all builds described in the package description file to a RAM disk
  15. *
  16. * Option f, file : Specify the package description file [default: Release.Tool]
  17. *
  18. * Release.Build [Options] BuildName ~
  19. *
  20. * Option b, build : Compile the specified build (otherwise, open Notepad with compile command)
  21. * Option c,compiler : Override compiler specified in the package description file
  22. * Option exclude : Exclude packages (specified as space separated list of package names)
  23. * Option e, extension : Override the extension option specified in the package description file
  24. * Option f, file : Specify the package description file [default: Release.Tool]
  25. * Option n, nocheck : Skip file existence and import order checks
  26. * Option o, options : Override the compiler options specified in the package description file
  27. * Option p, path : Override the path option specified in the package description file
  28. * Option s, symbolFileExtension : Ovveride the symbolFileExtension option specified in the package description file
  29. * Option t, target : Override the target option specified in the package description file
  30. * Option v, verbose : Show compiler output (only useful in combination with option --build)
  31. * Option w, workers : Number of worker threads for parallel compilation
  32. * Option x, xml : Generate XML package description file needed by WMInstaller.Mod
  33. * Option z, zip : Generate ZIP file for each package (for now, only together with --build option)
  34. *
  35. * Argument BuildName: Name of build to be built
  36. *
  37. * Release.FindPosition [Options] BuildName FileName ~ finds the first position in the package description file
  38. * where the specified file could be inserted (compile order)
  39. *
  40. * Option f, file : Specify the package description file [default: Release.Tool]
  41. *
  42. *
  43. * SystemTools.Free Release ~
  44. *
  45. * Based on Release.Mod by pjm
  46. *
  47. *
  48. * BuildDescription = Header Import BuildSection Packages
  49. * BuildSection = BUILDS {Build} END
  50. * Build = buildName OPENSECTION {BuildParameter} CLOSESECTION
  51. * Build = INCLUDE '"' [buildprefix {" " buildprefix} ] '"' |
  52. * COMPILER '"' compileCommand '"' | COMPILEROPTIONS '"' compileOptions '"' |
  53. * LINKER '"' linkCommand '"' | LINKEROPTIONS '"' linkOptions '"' |
  54. * TARGET '"' target '"' |
  55. * EXTENSION '"' objectFileExtension '"' | PATH '"' objectFilePath '"' |
  56. * EXCLUDEPACKAGES '"' [ exludedPackage {" " excludedPackage} ] '"'
  57. * DISABLED "TRUE" | "FALSE"
  58. *
  59. * Packages = { PackageSpec FileList }
  60. * PackageSpec = PACKAGE packageName ARCHIVE ArchiveName SOURCE SourceName ENDSECTION
  61. * FileList = { Filename | ReleaseSpec OPENSECTION [ {filename } ] CLOSESECTION }
  62. * ReleaseSpec = ReleaseName [ { SEPARATOR ReleaseName } ]
  63. *)
  64. IMPORT
  65. Modules, Streams, Commands, Options, Files, Dates, Strings, Texts, TextUtilities, ReleaseThreadPool, Diagnostics, WMGraphics, Zip,
  66. CompilerInterface, Compiler, SyntaxTree := FoxSyntaxTree ;
  67. CONST
  68. VersionMajor = 1;
  69. VersionMinor = 0;
  70. DefaultPackagesFile = "Release.Tool";
  71. (* Default build settings *)
  72. DefaultCompiler = "Compiler.Compile";
  73. DefaultCompileOptions = "";
  74. DefaultTarget = "" (* default chosen by compiler *);
  75. DefaultExtension = ""; (* default chosen by compiler *)
  76. DefaultSymbolFileExtension = ""; (* default chosen by compiler *)
  77. DefaultPath = "";
  78. DefaultDisabled = FALSE;
  79. (** If the prefix of a filename Prefix.Mid.Suffix matches <ReleasePrefix>, both Prefix.Mid.Suffix and Mid.Suffix will be
  80. included in the archive package. This is only valid for files that do not have the extension (Suffix) Mod *)
  81. ReleasePrefix = "Release";
  82. ToolFilename = "CompileCommand.Tool";
  83. InstallerPackageFile = "InstallerPackages.XML";
  84. DateTimeFormat = "wwww, mmmm d, yyyy hh:nn:ss";
  85. NoFile = -1;
  86. NoPackages = -2;
  87. (* Load Oberon text files ignoring text format *)
  88. OptimizedLoads = TRUE;
  89. (* If set to TRUE, the file references are re-used for different steps of the build process *)
  90. KeepFilesOpen = FALSE;
  91. (* File.flags *)
  92. ImportsSystem = 0;
  93. SourceCode = 1;
  94. HasReleasePrefix = 2; (* filename has prefix <ReleasePrefix> *)
  95. (* Package.installMode *)
  96. Undefined = 0;
  97. Required = 1; (* package must be installed *)
  98. Yes = 2; (* per default, install package *)
  99. No = 3; (* per default, don't install package *)
  100. (* File.release*)
  101. MaxBuilds = 128;
  102. MaxPrefixes = 128;
  103. MaxNofImports = 128; (* Maximum number of entries in a module import section *)
  104. Tab = 9X;
  105. Mode_ShowImported = 0;
  106. Mode_ShowImporting = 1;
  107. SetSize = MAX(SET)+1;
  108. TYPE
  109. Name = ARRAY 72 OF CHAR;
  110. Set = ARRAY (MaxPrefixes-1) DIV SetSize + 1 OF SET;
  111. TYPE
  112. Statistic = RECORD
  113. nofFiles : LONGINT;
  114. nofSources : LONGINT;
  115. END;
  116. Statistics = OBJECT
  117. VAR
  118. stats : ARRAY MaxPrefixes OF Statistic;
  119. (* Total number of files and sources *)
  120. nofFiles : LONGINT;
  121. nofSources : LONGINT;
  122. (* Number of files and sources that are contained in all releases *)
  123. nofFilesAll : LONGINT;
  124. nofSourcesAll : LONGINT;
  125. PROCEDURE Get(VAR nofFiles, nofSources : LONGINT; CONST release : Set);
  126. VAR i : LONGINT;
  127. BEGIN
  128. nofFiles := 0; nofSources := 0;
  129. FOR i := 0 TO MaxPrefixes-1 DO
  130. IF Has(release, i) THEN
  131. nofFiles := nofFiles + stats[i].nofFiles;
  132. nofSources := nofSources + stats[i].nofSources;
  133. END;
  134. END;
  135. END Get;
  136. PROCEDURE AddFile(file : File);
  137. VAR i : LONGINT;
  138. BEGIN
  139. INC(nofFiles);
  140. IF file.IsSourceCode() THEN INC(nofSources); END;
  141. IF ~IsAll(file.release) THEN
  142. FOR i := 0 TO MaxPrefixes-1 DO
  143. IF Has(file.release, i) THEN
  144. INC(stats[i].nofFiles);
  145. IF file.IsSourceCode() THEN INC(stats[i].nofSources); END;
  146. END;
  147. END;
  148. ELSE
  149. INC(nofFilesAll);
  150. IF file.IsSourceCode() THEN INC(nofSourcesAll); END;
  151. END;
  152. END AddFile;
  153. PROCEDURE &Reset;
  154. VAR i : LONGINT;
  155. BEGIN
  156. nofFiles := 0;
  157. nofSources := 0;
  158. FOR i := 0 TO LEN(stats)-1 DO
  159. stats[i].nofFiles := 0;
  160. stats[i].nofSources := 0;
  161. END;
  162. END Reset;
  163. END Statistics;
  164. TYPE
  165. (* The bitmap is used to analyze dependencies between modules *)
  166. Bitmap = OBJECT
  167. VAR
  168. map : POINTER TO ARRAY OF SET;
  169. size : LONGINT;
  170. PROCEDURE IsSet(bit : LONGINT) : BOOLEAN;
  171. BEGIN
  172. ASSERT(( 0 <= bit) & (bit < size));
  173. RETURN (bit MOD SIZEOF(SET)) IN map[bit DIV SIZEOF(SET)];
  174. END IsSet;
  175. PROCEDURE Set(bit : LONGINT);
  176. BEGIN
  177. ASSERT((0 <= bit) & (bit < size));
  178. INCL(map[bit DIV SIZEOF(SET)], bit MOD SIZEOF(SET));
  179. END Set;
  180. PROCEDURE NofBitsSet() : LONGINT;
  181. VAR nofBitsSet, index, subindex : LONGINT;
  182. BEGIN
  183. nofBitsSet := 0;
  184. FOR index := 0 TO LEN(map)-1 DO
  185. FOR subindex := 0 TO SIZEOF(SET)-1 DO
  186. IF subindex IN map[index] THEN INC(nofBitsSet); END;
  187. END;
  188. END;
  189. RETURN nofBitsSet;
  190. END NofBitsSet;
  191. PROCEDURE Union(bitmap : Bitmap);
  192. VAR i : LONGINT;
  193. BEGIN
  194. ASSERT((bitmap # NIL) & (bitmap.size = size));
  195. FOR i := 0 TO LEN(map)-1 DO
  196. map[i] := map[i] + bitmap.map[i];
  197. END;
  198. END Union;
  199. PROCEDURE &Init(size : LONGINT);
  200. VAR i : LONGINT;
  201. BEGIN
  202. ASSERT(size > 0);
  203. SELF.size := size;
  204. NEW(map, (size + SIZEOF(SET)-1) DIV SIZEOF(SET));
  205. FOR i := 0 TO LEN(map)-1 DO map[i] := {}; END;
  206. END Init;
  207. END Bitmap;
  208. TYPE
  209. Package* = OBJECT
  210. VAR
  211. name-, archive-, source- : ARRAY 32 OF CHAR;
  212. description- : ARRAY 256 OF CHAR;
  213. installMode : LONGINT; (* Required, Yes, No *)
  214. nofFiles- : LONGINT;
  215. nofSources- : LONGINT;
  216. position- : LONGINT;
  217. next : Package;
  218. PROCEDURE &Init(CONST name, archive, source, description : ARRAY OF CHAR; position : LONGINT);
  219. BEGIN
  220. COPY(name, SELF.name);
  221. COPY(archive, SELF.archive);
  222. COPY(source, SELF.source);
  223. COPY(description, SELF.description);
  224. SELF.position := position;
  225. installMode := Undefined;
  226. nofFiles := 0;
  227. nofSources := 0;
  228. position := -1;
  229. next := NIL;
  230. END Init;
  231. END Package;
  232. TYPE
  233. PackageArray* = POINTER TO ARRAY OF Package;
  234. PackageList* = OBJECT
  235. VAR
  236. head, tail : Package;
  237. nofPackages : LONGINT;
  238. (* Find the package with the specified name. Returns NIL if package not found *)
  239. PROCEDURE FindPackage(CONST name : ARRAY OF CHAR) : Package;
  240. VAR package : Package;
  241. BEGIN
  242. package := head.next;
  243. WHILE (package # NIL) & (package.name # name) DO package := package.next; END;
  244. RETURN package;
  245. END FindPackage;
  246. (* Add package to list. Returns FALSE if a package in the list has the same name *)
  247. PROCEDURE Add(package : Package) : BOOLEAN;
  248. BEGIN
  249. ASSERT((package # NIL) & (package.next = NIL));
  250. IF FindPackage(package.name) = NIL THEN
  251. tail.next := package;
  252. tail := package;
  253. INC(nofPackages);
  254. RETURN TRUE;
  255. ELSE
  256. RETURN FALSE;
  257. END;
  258. END Add;
  259. PROCEDURE GetAll*() : PackageArray;
  260. VAR packageArray : PackageArray; package : Package; i : LONGINT;
  261. BEGIN
  262. IF (nofPackages > 0) THEN
  263. NEW(packageArray, nofPackages);
  264. package := head.next;
  265. i := 0;
  266. WHILE (i < nofPackages) DO
  267. packageArray[i] := package;
  268. package := package.next;
  269. INC(i);
  270. END;
  271. ELSE
  272. packageArray := NIL;
  273. END;
  274. RETURN packageArray;
  275. END GetAll;
  276. PROCEDURE ToStream(out : Streams.Writer);
  277. VAR package : Package; nofPackages : LONGINT;
  278. BEGIN
  279. ASSERT(out # NIL);
  280. out.String("Packages: "); out.Ln;
  281. nofPackages := 0;
  282. package := head.next;
  283. WHILE (package # NIL) DO
  284. out.String(" "); out.String(package.name); out.Int(package.nofFiles, 4);
  285. out.String(" Files ("); out.Int(package.nofSources, 4); out.String(" source code files)"); out.Ln;
  286. INC(nofPackages);
  287. package := package.next;
  288. END;
  289. out.String(" "); out.Int(nofPackages, 0); out.String(" packages"); out.Ln;
  290. END ToStream;
  291. PROCEDURE &Init;
  292. BEGIN
  293. NEW(head, "Head", "", "", "", -1);
  294. tail := head;
  295. nofPackages := 0;
  296. END Init;
  297. END PackageList;
  298. TYPE
  299. ModuleInfo = RECORD
  300. name, context : Name;
  301. imports : ARRAY MaxNofImports OF Name; (* directly imported modules *)
  302. nofImports : LONGINT;
  303. flags : SET;
  304. isParsed : BOOLEAN;
  305. END;
  306. TYPE
  307. File* = OBJECT
  308. VAR
  309. module : ModuleInfo; (* if source code *)
  310. name-, uppercaseName : Files.FileName;
  311. doCompile : BOOLEAN;
  312. (* When using array indexes instead of names *)
  313. index : LONGINT; (* position of this File object in array *)
  314. importIndices : ARRAY MaxNofImports OF LONGINT;
  315. (* number of modules that directly or indirectly import this module *)
  316. nofDependentModules : LONGINT;
  317. (* number of modules directly or indirectly imported by this module *)
  318. nofRequiredModules : LONGINT;
  319. (* job ID in case of using the thread pool for parallel compilation of modules *)
  320. jobID : LONGINT;
  321. package- : Package;
  322. options: ARRAY 8 OF CHAR;
  323. release- : Set;
  324. flags-: SET;
  325. file : Files.File;
  326. pos : LONGINT; (* Position in package description file *)
  327. builds : Builds;
  328. prev-, next- : File;
  329. PROCEDURE &Init(builds : Builds);
  330. VAR i : LONGINT;
  331. BEGIN
  332. ASSERT(builds # NIL);
  333. SELF.builds := builds;
  334. module.name := ""; module.context := "";
  335. FOR i := 0 TO LEN(module.imports)-1 DO module.imports[i] := ""; END;
  336. module.nofImports := 0;
  337. module.flags := {};
  338. module.isParsed := FALSE;
  339. COPY("", name);
  340. doCompile := TRUE;
  341. package := NIL;
  342. COPY("", options);
  343. SetEmpty(release);
  344. flags := {};
  345. file := NIL;
  346. pos := 0;
  347. prev := NIL; next := NIL;
  348. END Init;
  349. PROCEDURE IsInRelease*(release : Set) : BOOLEAN;
  350. VAR i: LONGINT;
  351. BEGIN
  352. IF IsAll(SELF.release) THEN RETURN TRUE END;
  353. FOR i := 0 TO LEN(release)-1 DO
  354. IF SELF.release[i] * release[i] # {} THEN RETURN TRUE END;
  355. END;
  356. RETURN FALSE;
  357. END IsInRelease;
  358. PROCEDURE IsSourceCode*() : BOOLEAN;
  359. BEGIN
  360. RETURN SourceCode IN flags;
  361. END IsSourceCode;
  362. PROCEDURE CheckImports*(diagnostics : Diagnostics.Diagnostics; build : BuildObj; VAR error : BOOLEAN);
  363. VAR file : File; i : LONGINT; temp, message : ARRAY 256 OF CHAR;
  364. BEGIN
  365. ASSERT((diagnostics # NIL) & (build # NIL));
  366. error := FALSE;
  367. FOR i := 0 TO module.nofImports-1 DO
  368. file := prev;
  369. LOOP
  370. IF (file = NIL) THEN EXIT; END;
  371. IF (file.module.name = module.imports[i]) & file.IsInRelease(build.include) & ~build.PackageIsExcluded(file.package) THEN (* found *) EXIT; END;
  372. file := file.prev;
  373. END;
  374. IF (file = NIL) THEN
  375. error := TRUE;
  376. COPY(build.name, temp);
  377. Strings.Append(temp, ": Import # not found in file #");
  378. MakeMessage(message, temp, module.imports[i], name);
  379. diagnostics.Error(builds.source, pos, Diagnostics.Invalid, message);
  380. END;
  381. END;
  382. END CheckImports;
  383. PROCEDURE ParseModule*(diagnostics : Diagnostics.Diagnostics);
  384. VAR
  385. reader : Streams.Reader;
  386. pre, mid, suf: Files.FileName;
  387. message : ARRAY 256 OF CHAR;
  388. error : BOOLEAN;
  389. BEGIN
  390. IF module.isParsed OR ~IsSourceCode() THEN RETURN; END;
  391. reader := GetReader(SELF, diagnostics);
  392. IF (reader # NIL) THEN
  393. GetModuleInfo(reader, module, builds.source, name, pos, diagnostics, error);
  394. IF ~error THEN
  395. module.isParsed := TRUE;
  396. flags := flags + module.flags;
  397. SplitName(name, pre, mid, suf);
  398. IF (module.name # mid) THEN
  399. MakeMessage(message, "Module name not equal to filename in #", name, "");
  400. diagnostics.Warning(builds.source, pos, Diagnostics.Invalid, message);
  401. END;
  402. CreateContext(module.name, module.context);
  403. END;
  404. END;
  405. END ParseModule;
  406. PROCEDURE Show*(w : Streams.Writer);
  407. BEGIN
  408. w.String(name);
  409. END Show;
  410. END File;
  411. TYPE
  412. WorkerParameters = OBJECT
  413. VAR
  414. file : File;
  415. diagnostics : Diagnostics.Diagnostics;
  416. importCache : SyntaxTree.ModuleScope;
  417. PROCEDURE &Init(file : File; diagnostics : Diagnostics.Diagnostics; importCache : SyntaxTree.ModuleScope);
  418. BEGIN
  419. ASSERT((file # NIL) & (diagnostics # NIL) & (importCache # NIL));
  420. SELF.file := file;
  421. SELF.diagnostics := diagnostics;
  422. SELF.importCache := importCache;
  423. END Init;
  424. END WorkerParameters;
  425. TYPE
  426. BuildObj* = OBJECT
  427. VAR
  428. name- : Name;
  429. prefixes : ARRAY MaxPrefixes OF Name;
  430. excludedPackages : Strings.StringArray;
  431. onlyPackages: Strings.StringArray;
  432. compileOptions, linkOptions: Options.Parameter;
  433. compiler, linker: ARRAY 128 OF CHAR;
  434. target : ARRAY 32 OF CHAR;
  435. extension : ARRAY 8 OF CHAR;
  436. symbolFileExtension : ARRAY 8 OF CHAR;
  437. path : Files.FileName;
  438. disabled : BOOLEAN;
  439. link: BOOLEAN; (* link packages instead of compiling *)
  440. (* Used for dependencies analysis *)
  441. modules : POINTER TO ARRAY OF File;
  442. bitmap : POINTER TO ARRAY OF Bitmap;
  443. marked : BOOLEAN; (* Used by Check command *)
  444. (* List of all files and packages declared in the package description file *)
  445. files : File;
  446. packages : PackageList;
  447. (* Name of the package description file *)
  448. builds : Builds;
  449. include : Set;
  450. position- : LONGINT;
  451. PROCEDURE &Init;
  452. VAR i : LONGINT;
  453. BEGIN
  454. COPY("", name);
  455. FOR i := 0 TO LEN(prefixes)-1 DO prefixes[i] := ""; END;
  456. excludedPackages := NIL;
  457. onlyPackages := NIL;
  458. COPY(DefaultCompiler, compiler);
  459. COPY(DefaultCompileOptions, compileOptions);
  460. COPY(DefaultTarget, target);
  461. COPY(DefaultExtension, extension);
  462. COPY(DefaultSymbolFileExtension, symbolFileExtension);
  463. COPY(DefaultPath, path);
  464. disabled := DefaultDisabled;
  465. modules := NIL; bitmap := NIL;
  466. marked := FALSE;
  467. files := NIL;
  468. packages := NIL;
  469. builds := NIL;
  470. SetEmpty(include);
  471. position := -1;
  472. END Init;
  473. PROCEDURE CompileThisPackage(package: Package): BOOLEAN;
  474. VAR i : LONGINT;
  475. BEGIN
  476. IF (onlyPackages # NIL) THEN
  477. FOR i := 0 TO LEN(onlyPackages)-1 DO
  478. IF (package.name = onlyPackages[i]^) THEN
  479. RETURN TRUE
  480. END;
  481. END;
  482. RETURN FALSE
  483. END;
  484. RETURN TRUE
  485. END CompileThisPackage;
  486. PROCEDURE PackageIsExcluded(package : Package) : BOOLEAN;
  487. VAR i : LONGINT;
  488. BEGIN
  489. IF (package = NIL) THEN RETURN FALSE; END;
  490. IF (excludedPackages # NIL) THEN
  491. FOR i := 0 TO LEN(excludedPackages)-1 DO
  492. IF (package.name = excludedPackages[i]^) THEN
  493. RETURN TRUE;
  494. END;
  495. END;
  496. END;
  497. RETURN FALSE;
  498. END PackageIsExcluded;
  499. PROCEDURE SetOptions(options : Options.Options);
  500. VAR string : ARRAY 512 OF CHAR;
  501. BEGIN
  502. ASSERT(options # NIL);
  503. IF (options # NIL) THEN
  504. link := options.GetFlag("link");
  505. IF options.GetString("compiler", compiler) THEN END;
  506. IF link THEN
  507. IF options.GetString("options", linkOptions) THEN END;
  508. ELSE
  509. IF options.GetString("options", compileOptions) THEN END;
  510. END;
  511. IF options.GetString("target", target) THEN END;
  512. IF options.GetString("extension", extension) THEN END;
  513. IF options.GetString("symbolFileExtension", symbolFileExtension) THEN END;
  514. IF options.GetString("path", path) THEN END;
  515. IF options.GetString("exclude", string) THEN
  516. Strings.TrimWS(string);
  517. excludedPackages := Strings.Split(string, " ");
  518. END;
  519. IF options.GetString("only",string) THEN
  520. Strings.TrimWS(string);
  521. onlyPackages := Strings.Split(string, " ");
  522. END;
  523. END;
  524. END SetOptions;
  525. (** List all source code files included in this build to a stream *)
  526. PROCEDURE ToStream*(w : Streams.Writer; charactersPerLine : LONGINT);
  527. VAR file : File; color : LONGINT; characterCount : LONGINT; name: Files.FileName;
  528. BEGIN
  529. characterCount := 0;
  530. file := files;
  531. WHILE (file # NIL) DO
  532. IF file.IsSourceCode() & file.IsInRelease(include) & ~PackageIsExcluded(file.package) & file.doCompile & CompileThisPackage(file.package) THEN
  533. IF (w IS TextUtilities.TextWriter) THEN
  534. IF (ImportsSystem IN file.flags) THEN
  535. color := WMGraphics.Red;
  536. ELSE
  537. color := WMGraphics.Black;
  538. END;
  539. w(TextUtilities.TextWriter).SetFontColor(color);
  540. END;
  541. IF link THEN
  542. COPY(file.module.name, name)
  543. ELSE
  544. COPY(file.name, name)
  545. END;
  546. characterCount := characterCount + Strings.Length(name);
  547. IF (characterCount > charactersPerLine) THEN
  548. characterCount := 0;
  549. w.Ln;
  550. END;
  551. w.String(name); w.String(" ");
  552. END;
  553. file := file.next;
  554. END;
  555. IF (w IS TextUtilities.TextWriter) THEN
  556. w(TextUtilities.TextWriter).SetFontColor(WMGraphics.Black);
  557. END;
  558. w.Update;
  559. END ToStream;
  560. (** Generate a file that contains the list of all source code files of this build in compile order *)
  561. PROCEDURE GenerateToolFile*(CONST filename : Files.FileName; VAR res : LONGINT);
  562. VAR text : Texts.Text; tw : TextUtilities.TextWriter; dateTime : Dates.DateTime; temp : ARRAY 256 OF CHAR;
  563. BEGIN
  564. NEW(text); NEW(tw, text);
  565. tw.SetFontColor(LONGINT(808080FFH)); (* comment grey *)
  566. tw.String("# "); tw.String(name); tw.Ln;
  567. dateTime := Dates.Now();
  568. Strings.FormatDateTime(DateTimeFormat, dateTime, temp);
  569. tw.String("# "); tw.String(temp); tw.Ln;
  570. tw.String("# This file has been automatically generated using Release.Mod."); tw.Ln;
  571. tw.String("# Red colors indicate that a module imports SYSTEM."); tw.Ln;
  572. tw.SetFontColor(WMGraphics.Black);
  573. (* Compile command *)
  574. tw.SetFontStyle({WMGraphics.FontBold});
  575. tw.String("SystemTools.DoCommands"); tw.Ln;
  576. tw.SetFontStyle({});
  577. tw.String("SystemTools.Timer start ~"); tw.Ln;
  578. IF link THEN
  579. tw.String(linker);
  580. GetLinkerOptions(temp);
  581. ELSE
  582. tw.String(compiler);
  583. GetCompilerOptions(temp);
  584. END;
  585. tw.String(" "); tw.String(temp); tw.Ln;
  586. ToStream(tw, 80); (* file list *) tw.Ln; tw.String("~"); tw.Ln;
  587. tw.String("SystemTools.Show Time elapsed: ~ SystemTools.Ln ~"); tw.Ln;
  588. tw.String("SystemTools.Timer elapsed ~ SystemTools.Ln ~"); tw.Ln;
  589. tw.String("~");
  590. tw.Update;
  591. TextUtilities.StoreOberonText(text, filename, res);
  592. END GenerateToolFile;
  593. (* Generate a XML file describing the packages use by WMInstaller.Mod *)
  594. PROCEDURE GeneratePackageFile(CONST filename : ARRAY OF CHAR; VAR res : LONGINT);
  595. VAR
  596. packageArray : PackageArray; package : Package;
  597. fullname : Files.FileName; file : Files.File; w : Files.Writer;
  598. packageNbr, i : LONGINT;
  599. PROCEDURE WritePackage(package : Package; sources : BOOLEAN; packageNbr : LONGINT; w : Streams.Writer);
  600. VAR filename : Files.FileName; description : ARRAY 300 OF CHAR; name, installMode : ARRAY 128 OF CHAR;
  601. BEGIN
  602. ASSERT((package # NIL) & (w # NIL));
  603. COPY(package.description, description);
  604. IF sources THEN
  605. COPY(package.name, name); Strings.Append(name, " Sources");
  606. COPY(package.source, filename); Strings.Append(description, " sources");
  607. ELSE
  608. COPY(package.name, name);
  609. COPY(package.archive, filename);
  610. END;
  611. w.Char(Tab);
  612. w.String('<Package nr="'); w.Int(packageNbr, 0);
  613. w.String('" name="'); w.String(name);
  614. w.String('" file="'); w.String(filename);
  615. w.String('" description="'); w.String(description);
  616. IF (package.installMode = Required) THEN installMode := "required";
  617. ELSIF (package.installMode = Yes) THEN installMode := "yes";
  618. ELSIF (package.installMode = No) THEN installMode := "no";
  619. ELSE installMode := "undefined";
  620. END;
  621. w.String('" install="'); w.String(installMode); w.String('"/>'); w.Ln;
  622. END WritePackage;
  623. BEGIN
  624. res := Files.Ok;
  625. packageArray := packages.GetAll();
  626. IF (packageArray # NIL) THEN
  627. COPY(path, fullname); Strings.Append(fullname, filename);
  628. file := Files.New(fullname);
  629. IF (file # NIL) THEN
  630. packageNbr := 1;
  631. Files.OpenWriter(w, file, 0);
  632. w.String('<?xml version="1.0" encoding="UTF-8" standalone="yes"?>'); w.Ln; w.Ln;
  633. w.String("<!-- This has been automatically generated by Release.Mod -->"); w.Ln; w.Ln;
  634. w.String("<Packages>"); w.Ln;
  635. FOR i := 0 TO LEN(packageArray)-1 DO
  636. package := packageArray[i];
  637. IF ~PackageIsExcluded(package) THEN
  638. WritePackage(package, FALSE, packageNbr, w);
  639. INC(packageNbr);
  640. IF (package.source # "") THEN
  641. WritePackage(package, TRUE, packageNbr, w);
  642. INC(packageNbr);
  643. END;
  644. END;
  645. END;
  646. w.String("</Packages>"); w.Ln; w.Update;
  647. Files.Register(file);
  648. ELSE
  649. res := NoFile;
  650. END;
  651. ELSE
  652. res := NoPackages;
  653. END;
  654. END GeneratePackageFile;
  655. PROCEDURE GenerateZipFiles(out, error : Streams.Writer; diagnostics : Diagnostics.Diagnostics; VAR err : BOOLEAN);
  656. VAR packageArray : PackageArray; i, res : LONGINT;
  657. PROCEDURE AddFile(archive: Zip.Archive; CONST srcname, dstname: ARRAY OF CHAR; VAR res: LONGINT);
  658. VAR f: Files.File; r: Files.Rider; pathName, fileName: Files.FileName;
  659. BEGIN
  660. f := Files.Old(srcname);
  661. IF f = NIL THEN
  662. res := Zip.BadName
  663. ELSE
  664. f.Set(r, 0);
  665. Files.SplitPath(dstname, pathName, fileName);
  666. Zip.AddEntry(archive, fileName, r, f.Length(), 9, 2, res);
  667. END;
  668. END AddFile;
  669. PROCEDURE DeleteFile(filename : ARRAY OF CHAR; VAR nofFilesDeleted : LONGINT);
  670. VAR file : Files.File;
  671. BEGIN
  672. file := Files.Old(filename);
  673. IF (file # NIL) THEN
  674. Files.Delete(filename, res);
  675. IF (res = Files.Ok) THEN
  676. INC(nofFilesDeleted);
  677. ELSE
  678. out.String(" could not delete existing file, res: "); out.Int(res, 0); out.Ln;
  679. END;
  680. END;
  681. END DeleteFile;
  682. PROCEDURE GetObjectFileName(file : File; CONST prefix : ARRAY OF CHAR; VAR fileName: ARRAY OF CHAR);
  683. BEGIN
  684. COPY(prefix, fileName);
  685. Strings.Append(fileName, file.module.name);
  686. Strings.Append(fileName, ".");
  687. Strings.Append(fileName, extension);
  688. END GetObjectFileName;
  689. PROCEDURE GetSymbolFileName(file : File; CONST prefix : ARRAY OF CHAR; VAR fileName : ARRAY OF CHAR);
  690. BEGIN
  691. COPY(prefix, fileName);
  692. Strings.Append(fileName, file.module.name);
  693. Strings.Append(fileName, ".");
  694. Strings.Append(fileName, symbolFileExtension);
  695. END GetSymbolFileName;
  696. PROCEDURE GetPackageFileName(package : Package; sourceCode : BOOLEAN; VAR filename : ARRAY OF CHAR) : BOOLEAN;
  697. BEGIN
  698. ASSERT(package # NIL);
  699. COPY(path, filename);
  700. IF ~sourceCode THEN
  701. Strings.Append(filename, package.archive);
  702. ELSE
  703. Strings.Append(filename, package.source);
  704. END;
  705. RETURN (filename # path);
  706. END GetPackageFileName;
  707. PROCEDURE DeleteOldZipFiles(packageArray : PackageArray);
  708. VAR filename : Files.FileName; nofFilesDeleted, i : LONGINT;
  709. BEGIN
  710. ASSERT(packageArray # NIL);
  711. out.String("Deleting old archive files ... "); out.Update;
  712. nofFilesDeleted := 0;
  713. FOR i := 0 TO LEN(packageArray)-1 DO
  714. IF GetPackageFileName(packageArray[i], TRUE, filename) THEN DeleteFile(filename, nofFilesDeleted); END;
  715. IF GetPackageFileName(packageArray[i], FALSE, filename) THEN DeleteFile(filename, nofFilesDeleted); END;
  716. END;
  717. out.Int(nofFilesDeleted, 0); out.String(" files deleted."); out.Ln;
  718. END DeleteOldZipFiles;
  719. PROCEDURE StripReleasePrefix(file : File; VAR filename: ARRAY OF CHAR);
  720. VAR pre, mid, suf : Files.FileName;
  721. BEGIN
  722. SplitName(file.name, pre, mid, suf);
  723. ASSERT(pre = ReleasePrefix);
  724. COPY (mid, filename); Strings.Append(filename, "."); Strings.Append(filename, suf);
  725. END StripReleasePrefix;
  726. PROCEDURE GenerateZipFile(package : Package; sourceCode : BOOLEAN; VAR res : LONGINT);
  727. VAR
  728. file : File; archiveName, source, dest : Files.FileName;
  729. archive : Zip.Archive;
  730. nofFilesAdded : LONGINT;
  731. BEGIN
  732. ASSERT(package # NIL);
  733. res := Zip.Ok;
  734. IF GetPackageFileName(package, sourceCode, archiveName) THEN
  735. out.String("Creating archive "); out.String(archiveName); out.String(" ... "); out.Update;
  736. archive := Zip.CreateArchive(archiveName, res);
  737. IF (res # Zip.Ok) THEN
  738. error.String("error: "); Zip.ShowError(res, error); error.Ln;
  739. RETURN;
  740. END;
  741. nofFilesAdded := 0;
  742. file := files;
  743. WHILE (file # NIL) DO
  744. IF (file.package = package) & file.IsInRelease(include) THEN
  745. IF sourceCode THEN
  746. IF file.IsSourceCode() THEN
  747. AddFile(archive, file.name, file.name, res);
  748. INC(nofFilesAdded);
  749. END;
  750. ELSE
  751. IF file.IsSourceCode() THEN
  752. GetObjectFileName(file, path, source); GetObjectFileName(file, "", dest);
  753. AddFile(archive, source, dest, res);
  754. INC(nofFilesAdded);
  755. IF (res = Zip.Ok) & (package.source = "") THEN (* Also add source code since no source code package is defined *)
  756. AddFile(archive, file.name, file.name, res);
  757. END;
  758. IF symbolFileExtension # extension THEN
  759. GetSymbolFileName(file, path, source); GetSymbolFileName(file, "", dest);
  760. AddFile(archive, source, dest, res);
  761. INC(nofFilesAdded);
  762. END
  763. ELSE
  764. AddFile(archive, file.name, file.name, res);
  765. INC(nofFilesAdded);
  766. IF (res = Zip.Ok) & (HasReleasePrefix IN file.flags) THEN
  767. StripReleasePrefix(file, dest); AddFile(archive, file.name, dest, res);
  768. INC(nofFilesAdded);
  769. END;
  770. END;
  771. END;
  772. IF (res # Zip.Ok) THEN
  773. error.Ln; error.String("Could not add file "); error.String(file.name);
  774. error.String(": "); Zip.ShowError(res, error); error.Ln;
  775. error.Update;
  776. RETURN;
  777. END;
  778. END;
  779. file := file.next;
  780. END;
  781. out.Int(nofFilesAdded, 0); out.String(" files added."); out.Ln; out.Update;
  782. END;
  783. END GenerateZipFile;
  784. BEGIN
  785. packageArray := packages.GetAll();
  786. IF (packageArray # NIL) THEN
  787. DeleteOldZipFiles(packageArray);
  788. FOR i := 0 TO LEN(packageArray)-1 DO
  789. IF ~PackageIsExcluded(packageArray[i]) THEN
  790. GenerateZipFile(packageArray[i], TRUE, res);
  791. IF (res # Zip.Ok) THEN err := TRUE; RETURN END;
  792. GenerateZipFile(packageArray[i], FALSE, res);
  793. IF (res # Zip.Ok) THEN err := TRUE; RETURN END;
  794. END;
  795. END;
  796. ELSE
  797. diagnostics.Error("", Diagnostics.Invalid, Diagnostics.Invalid, "No packages");
  798. END;
  799. END GenerateZipFiles;
  800. PROCEDURE FindPosition*(CONST filename : ARRAY OF CHAR; diagnostics : Diagnostics.DiagnosticsList) : LONGINT;
  801. VAR
  802. file, ref : File; position : LONGINT; pre, suf : ARRAY 32 OF CHAR;
  803. importOk : ARRAY MaxNofImports OF BOOLEAN; i : LONGINT;
  804. PROCEDURE Done() : BOOLEAN;
  805. VAR i : LONGINT;
  806. BEGIN
  807. i := 0; WHILE (i < LEN(importOk)) & (importOk[i] = TRUE) DO INC(i); END;
  808. RETURN (i >= LEN(importOk));
  809. END Done;
  810. PROCEDURE Process(f : File);
  811. VAR i : LONGINT;
  812. BEGIN
  813. IF f.IsSourceCode() & f.IsInRelease(include) & ~PackageIsExcluded(f.package)THEN
  814. FOR i := 0 TO LEN(importOk)-1 DO
  815. IF (file.module.imports[i] = f.module.name) & ~PackageIsExcluded(file.package) THEN importOk[i] := TRUE; END;
  816. END;
  817. END;
  818. END Process;
  819. BEGIN
  820. NEW(file, builds);
  821. COPY(filename, file.name);
  822. SplitName(file.name, pre, file.module.name, suf);
  823. IF (suf = "Mod") OR (suf="Mdf") OR (suf="Mos") THEN
  824. INCL(file.flags, SourceCode);
  825. file.ParseModule(diagnostics);
  826. IF (diagnostics.nofErrors = 0) THEN
  827. FOR i := 0 TO LEN(importOk)-1 DO
  828. IF (file.module.imports[i] # "") THEN importOk[i] := FALSE; ELSE importOk[i] := TRUE; END;
  829. END;
  830. position := 0;
  831. ref := files;
  832. LOOP
  833. IF Done() THEN EXIT; END;
  834. IF (ref = NIL) THEN EXIT; END;
  835. Process(ref);
  836. IF (ref.next # NIL) THEN position := ref.next.pos; ELSE position := ref.pos + 100; END;
  837. ref := ref.next;
  838. END;
  839. ELSE
  840. position := -1;
  841. END;
  842. ELSE
  843. position := 0;
  844. END;
  845. RETURN position;
  846. END FindPosition;
  847. PROCEDURE FindModule(CONST moduleName : Modules.Name) : File;
  848. VAR file : File;
  849. BEGIN
  850. file := files;
  851. WHILE (file # NIL) DO
  852. IF file.IsInRelease(include) & file.IsSourceCode() & ~PackageIsExcluded(file.package) THEN
  853. IF (file.module.name = moduleName) THEN
  854. RETURN file;
  855. END;
  856. END;
  857. file := file.next;
  858. END;
  859. RETURN NIL;
  860. END FindModule;
  861. PROCEDURE FindFile(CONST filename : Files.FileName) : File;
  862. VAR file : File;
  863. BEGIN
  864. file := files;
  865. WHILE (file # NIL) DO
  866. IF file.IsInRelease(include) & file.IsSourceCode() & ~PackageIsExcluded(file.package) THEN
  867. IF (file.name = filename) THEN
  868. RETURN file;
  869. END;
  870. END;
  871. file := file.next;
  872. END;
  873. RETURN NIL;
  874. END FindFile;
  875. (** For all files contained in this build check whether they exist. Also try to parse the import section of the source code files *)
  876. PROCEDURE CheckFiles*(diagnostics : Diagnostics.Diagnostics);
  877. VAR file : File; message : ARRAY 256 OF CHAR;
  878. BEGIN
  879. file := files;
  880. WHILE (file # NIL) DO
  881. IF file.IsInRelease(include) & ~PackageIsExcluded(file.package) THEN
  882. IF (file.file = NIL) THEN
  883. file.file := Files.Old(file.name);
  884. END;
  885. IF (file.file = NIL) THEN
  886. MakeMessage(message, "File # does not exists (Package #)", file.name, file.package.name);
  887. diagnostics.Warning(builds.source, file.pos, Diagnostics.Invalid, message);
  888. ELSIF file.IsSourceCode() THEN
  889. file.ParseModule(diagnostics);
  890. END;
  891. IF ~KeepFilesOpen THEN
  892. file.file := NIL;
  893. END;
  894. END;
  895. file := file.next;
  896. END;
  897. END CheckFiles;
  898. (** Check the source code files of this build for the import order *)
  899. PROCEDURE CheckModules*(diagnostics : Diagnostics.Diagnostics; VAR error : BOOLEAN);
  900. VAR file : File; tempError : BOOLEAN;
  901. BEGIN
  902. error := FALSE;
  903. file := files;
  904. WHILE (file # NIL) DO
  905. IF file.IsSourceCode() & file.IsInRelease(include) & ~PackageIsExcluded(file.package) THEN
  906. tempError := FALSE;
  907. file.CheckImports(diagnostics, SELF, tempError);
  908. error := error OR tempError;
  909. END;
  910. file := file.next;
  911. END;
  912. END CheckModules;
  913. (** Call both CheckFiles and CheckModules *)
  914. PROCEDURE DoChecks*(out : Streams.Writer; diagnostics : Diagnostics.Diagnostics; VAR error : BOOLEAN);
  915. VAR start, start0 : Dates.DateTime;
  916. BEGIN
  917. ASSERT((diagnostics # NIL));
  918. IF (out # NIL) THEN out.String(name); out.String(": Check if all files are present... "); out.Update; END;
  919. start0 := Dates.Now();
  920. CheckFiles(diagnostics);
  921. Strings.ShowTimeDifference(start0, Dates.Now(), out);
  922. IF (out # NIL) THEN out.String(" done."); out.Ln; out.Update; END;
  923. IF (out # NIL) THEN out.String(name); out.String(": Check modules and imports... "); out.Update; END;
  924. start := Dates.Now();
  925. error := FALSE;
  926. CheckModules(diagnostics, error);
  927. Strings.ShowTimeDifference(start, Dates.Now(), out);
  928. IF (out # NIL) THEN out.String(" done."); out.Ln; out.Update; END;
  929. END DoChecks;
  930. (* Analyzes the import dependencies between all modules *)
  931. PROCEDURE AnalyzeDependencies(out : Streams.Writer);
  932. VAR
  933. nofModules : LONGINT;
  934. file : File;
  935. ignore, index, i, j : LONGINT;
  936. PROCEDURE GetIndexOf(file : File; CONST moduleName : Name) : LONGINT;
  937. VAR f : File;
  938. BEGIN
  939. f := file.prev;
  940. LOOP
  941. ASSERT(f # NIL);
  942. IF f.IsSourceCode() & f.IsInRelease(include) & ~PackageIsExcluded(f.package) & (f.module.name = moduleName) THEN
  943. EXIT;
  944. END;
  945. f := f.prev;
  946. END;
  947. RETURN f.index;
  948. END GetIndexOf;
  949. BEGIN
  950. modules:= NIL;
  951. nofModules := GetNofSources(ignore);
  952. IF (out # NIL) THEN out.String("Analyzing dependencies of "); out.Int(nofModules, 0); out.String(" modules... "); END;
  953. IF (nofModules > 0) THEN
  954. (* Initialize the array of File 'modules' and the fields File.index and File.importIndices. After initialization,
  955. all File.importIndices contain the indices of directly imported modules *)
  956. NEW(modules, nofModules);
  957. index := 0;
  958. file := files;
  959. WHILE (file # NIL) DO
  960. IF file.IsSourceCode() & file.IsInRelease(include) & ~PackageIsExcluded(file.package) THEN
  961. file.index := index;
  962. modules[index] := file;
  963. FOR i := 0 TO file.module.nofImports-1 DO
  964. modules[index].importIndices[i] := GetIndexOf(file, file.module.imports[i]);
  965. END;
  966. INC(index);
  967. END;
  968. file := file.next;
  969. END;
  970. (* Create a two-dimensional bitmap where bitmap[i] is the import bitmap of the file with file.index = i.
  971. The import bitmap[i] has all bits j set where j is directly or indirectly imported by the file i . *)
  972. NEW(bitmap, nofModules);
  973. FOR i := 0 TO nofModules-1 DO
  974. NEW(bitmap[i], nofModules);
  975. FOR j := 0 TO modules[i].module.nofImports-1 DO
  976. bitmap[i].Set(modules[i].importIndices[j]);
  977. bitmap[i].Union(bitmap[modules[i].importIndices[j]]);
  978. END;
  979. END;
  980. (* Initialize fields File.nofDependetModules and File.nofRequiredModules *)
  981. FOR i := 0 TO nofModules-1 DO
  982. modules[i].nofDependentModules := 0;
  983. FOR j := 0 TO nofModules-1 DO
  984. IF bitmap[j].IsSet(i) THEN INC(modules[i].nofDependentModules); END;
  985. END;
  986. modules[i].nofRequiredModules := bitmap[i].NofBitsSet();
  987. END;
  988. END;
  989. IF (out # NIL) THEN out.String("done."); out.Ln; END;
  990. END AnalyzeDependencies;
  991. PROCEDURE ShowDependencies(out : Streams.Writer);
  992. VAR i, j : LONGINT; temp : File;
  993. BEGIN
  994. ASSERT(out # NIL);
  995. ASSERT((modules # NIL) & (bitmap # NIL));
  996. (* bubble sort *)
  997. FOR i := 0 TO LEN(modules)-1 DO
  998. FOR j := 0 TO LEN(modules)-2 DO
  999. IF (modules[j].nofDependentModules < modules[j+1].nofDependentModules) THEN
  1000. temp := modules[j];
  1001. modules[j] := modules[j+1];
  1002. modules[j+1] := temp;
  1003. END;
  1004. END;
  1005. END;
  1006. FOR i := 0 TO LEN(modules)-1 DO
  1007. out.String(modules[i].name); out.String(" -- ");
  1008. out.Int(modules[i].nofDependentModules, 0);
  1009. out.String(" ("); out.Int(modules[i].module.nofImports, 0);
  1010. out.String("/"); out.Int(modules[i].nofRequiredModules, 0);
  1011. out.String(")");
  1012. out.Ln;
  1013. END;
  1014. END ShowDependencies;
  1015. (** Clear the doCompile flag of all source code files contained in this build and not excluded *)
  1016. PROCEDURE ClearMarks;
  1017. VAR file : File;
  1018. BEGIN
  1019. file := files;
  1020. WHILE (file # NIL) DO
  1021. IF file.IsSourceCode() & file.IsInRelease(include) & ~PackageIsExcluded(file.package) THEN
  1022. file.doCompile := FALSE;
  1023. END;
  1024. file := file.next;
  1025. END;
  1026. END ClearMarks;
  1027. (** Set the doCompile flag of all non-excluded source code files in this build that depend on the specified file *)
  1028. PROCEDURE MarkFiles(CONST filename : Files.FileName; VAR inBuild : BOOLEAN; VAR nofNewMarks : LONGINT);
  1029. VAR file : File; nofModules, i, ignore : LONGINT;
  1030. BEGIN
  1031. nofNewMarks := 0;
  1032. file := FindFile(filename);
  1033. IF (file # NIL) THEN
  1034. inBuild := TRUE;
  1035. IF ~file.doCompile THEN
  1036. file.doCompile := TRUE;
  1037. INC(nofNewMarks);
  1038. END;
  1039. nofModules := GetNofSources(ignore);
  1040. FOR i := 0 TO nofModules-1 DO
  1041. IF bitmap[i].IsSet(file.index) THEN
  1042. IF ~modules[i].doCompile THEN
  1043. INC(nofNewMarks);
  1044. modules[i].doCompile := TRUE;
  1045. END;
  1046. END;
  1047. END;
  1048. ELSE
  1049. inBuild := FALSE;
  1050. END;
  1051. END MarkFiles;
  1052. PROCEDURE ShowDependentModules(CONST modulename : Modules.Name; mode : LONGINT; out : Streams.Writer);
  1053. CONST CharactersPerLine = 60;
  1054. VAR module : File; ignore, nofModules, characterCount, i : LONGINT;
  1055. BEGIN
  1056. ASSERT(out # NIL);
  1057. module := FindModule(modulename);
  1058. IF (module # NIL) THEN
  1059. characterCount := 0;
  1060. nofModules := GetNofSources(ignore);
  1061. IF (mode = Mode_ShowImporting) THEN
  1062. FOR i := 0 TO nofModules-1 DO
  1063. IF bitmap[i].IsSet(module.index) THEN
  1064. out.String(modules[i].name); out.String(" ");
  1065. characterCount := characterCount + Strings.Length(modules[i].name);
  1066. IF (characterCount > CharactersPerLine) THEN characterCount := 0; out.Ln; out.Update; END;
  1067. END;
  1068. END;
  1069. ELSE
  1070. FOR i := 0 TO nofModules-1 DO
  1071. IF bitmap[module.index].IsSet(i) THEN
  1072. out.String(modules[i].name); out.String(" ");
  1073. characterCount := characterCount + Strings.Length(modules[i].name);
  1074. IF (characterCount > CharactersPerLine) THEN characterCount := 0; out.Ln; out.Update; END;
  1075. END;
  1076. END;
  1077. END;
  1078. ELSE
  1079. out.String("Module "); out.String(modulename); out.String(" not found"); out.Ln;
  1080. END;
  1081. out.Update;
  1082. END ShowDependentModules;
  1083. PROCEDURE GetCompilerOptions(VAR options: ARRAY OF CHAR);
  1084. BEGIN
  1085. COPY(compileOptions, options);
  1086. IF target # "" THEN
  1087. Strings.Append(options, " -b="); Strings.Append(options, target);
  1088. END;
  1089. IF extension # "" THEN
  1090. Strings.Append(options, " --objectFileExtension="); Strings.Append(options, "."); Strings.Append(options, extension);
  1091. Strings.Append(options, " --symbolFileExtension="); Strings.Append(options, "."); Strings.Append(options, symbolFileExtension);
  1092. END;
  1093. IF path # "" THEN
  1094. Strings.Append(options, " --destPath="); Strings.Append(options, path);
  1095. END;
  1096. END GetCompilerOptions;
  1097. PROCEDURE GetLinkerOptions(VAR options: ARRAY OF CHAR);
  1098. BEGIN
  1099. COPY(linkOptions, options);
  1100. END GetLinkerOptions;
  1101. (* Count the number of source codes files in a specific build *)
  1102. PROCEDURE GetNofSources(VAR nofMarked : LONGINT) : LONGINT;
  1103. VAR file : File; nofSources : LONGINT;
  1104. BEGIN
  1105. nofSources := 0;
  1106. file := files;
  1107. WHILE (file # NIL) DO
  1108. IF file.IsSourceCode() & file.IsInRelease(include) & ~PackageIsExcluded(file.package) & CompileThisPackage(file.package) THEN
  1109. INC(nofSources);
  1110. IF file.doCompile THEN INC(nofMarked); END;
  1111. END;
  1112. file := file.next;
  1113. END;
  1114. RETURN nofSources;
  1115. END GetNofSources;
  1116. PROCEDURE GetInfo*(VAR nofSources, nofFiles : LONGINT);
  1117. VAR file : File;
  1118. BEGIN
  1119. nofSources := 0; nofFiles := 0;
  1120. file := files;
  1121. WHILE (file # NIL) DO
  1122. IF file.IsInRelease(include) & ~PackageIsExcluded(file.package) THEN
  1123. INC(nofFiles);
  1124. IF file.IsSourceCode() THEN
  1125. INC(nofSources);
  1126. END;
  1127. END;
  1128. file := file.next;
  1129. END;
  1130. END GetInfo;
  1131. PROCEDURE CompileFile(file : File; diagnostics : Diagnostics.Diagnostics; log : Streams.Writer; VAR error : BOOLEAN; importCache : SyntaxTree.ModuleScope);
  1132. VAR
  1133. reader : Streams.Reader;
  1134. options : ARRAY 1024 OF CHAR;
  1135. optionsReader : Streams.StringReader;
  1136. message : ARRAY 512 OF CHAR;
  1137. compilerOptions: Compiler.CompilerOptions;
  1138. BEGIN
  1139. ASSERT((file # NIL) & (diagnostics # NIL) & (importCache # NIL));
  1140. reader := GetReader(file, diagnostics);
  1141. IF ~KeepFilesOpen THEN
  1142. file.file := NIL;
  1143. END;
  1144. IF (reader # NIL) THEN
  1145. GetCompilerOptions(options);
  1146. NEW(optionsReader, LEN(options));
  1147. optionsReader.Set(options);
  1148. error := ~Compiler.GetOptions(optionsReader, log, diagnostics, compilerOptions);
  1149. IF ~error THEN
  1150. error := ~Compiler.Modules(file.name, reader, 0, diagnostics, log, compilerOptions, importCache);
  1151. END;
  1152. ELSE
  1153. MakeMessage(message, "File # not found", file.name, "");
  1154. diagnostics.Error("", file.pos, Diagnostics.Invalid, message);
  1155. error := TRUE;
  1156. END;
  1157. END CompileFile;
  1158. (* Job procedure called by worker thread *)
  1159. PROCEDURE CompileJob(parameters : ANY; VAR error : BOOLEAN);
  1160. BEGIN
  1161. ASSERT(
  1162. (parameters # NIL) & (parameters IS WorkerParameters) &
  1163. (parameters(WorkerParameters).file # NIL) & (parameters(WorkerParameters).diagnostics # NIL) &
  1164. (parameters(WorkerParameters).importCache # NIL)
  1165. );
  1166. CompileFile(
  1167. parameters(WorkerParameters).file,
  1168. parameters(WorkerParameters).diagnostics,
  1169. NIL,
  1170. error,
  1171. parameters(WorkerParameters).importCache
  1172. );
  1173. END CompileJob;
  1174. PROCEDURE CreateJob(threadpool : ReleaseThreadPool.ThreadPool; file : File; diagnostics : Diagnostics.Diagnostics; importCache : SyntaxTree.ModuleScope);
  1175. VAR parameters : WorkerParameters; dependencies : ReleaseThreadPool.Dependencies; i, priority : LONGINT;
  1176. BEGIN
  1177. ASSERT((threadpool # NIL) & (file # NIL) & (diagnostics # NIL) & (importCache # NIL));
  1178. NEW(parameters, file, diagnostics, importCache);
  1179. priority := file.nofDependentModules;
  1180. i := 0;
  1181. WHILE (i < file.module.nofImports) DO
  1182. dependencies[i] := modules[file.importIndices[i]].jobID;
  1183. INC(i);
  1184. END;
  1185. dependencies[i] := ReleaseThreadPool.NoMoreDependencies;
  1186. file.jobID := threadpool.CreateJob(CompileJob, parameters, priority, dependencies);
  1187. END CreateJob;
  1188. (** Compile all sources included in this build *)
  1189. PROCEDURE Compile*(nofWorkers : LONGINT; out, error : Streams.Writer; verbose : BOOLEAN; diagnostics : Diagnostics.DiagnosticsList; VAR err : BOOLEAN);
  1190. VAR
  1191. file : File; nofFiles, nofSources, nofMarked, step, steps : LONGINT;
  1192. importCache : SyntaxTree.ModuleScope;
  1193. startTime : Dates.DateTime;
  1194. log : Streams.Writer;
  1195. dummyWriter : Streams.StringWriter;
  1196. threadpool : ReleaseThreadPool.ThreadPool;
  1197. BEGIN
  1198. ASSERT(nofWorkers >= 0);
  1199. nofSources := GetNofSources(nofMarked);
  1200. IF (nofWorkers > 0) THEN
  1201. AnalyzeDependencies(out);
  1202. NEW(threadpool, nofWorkers);
  1203. out.String("Generating jobs to compile build ");
  1204. ELSE
  1205. out.String("Compiling build ");
  1206. END;
  1207. out.String(name); out.String(" (");
  1208. IF (nofMarked # nofSources) THEN out.Int(nofMarked, 0); out.Char("/"); END;
  1209. out.Int(nofSources, 0); out.String(" files)");
  1210. IF (nofWorkers > 0) THEN
  1211. out.String(" using "); out.Int(nofWorkers, 0); out.String(" worker threads");
  1212. END;
  1213. out.String(" ... ");
  1214. IF verbose THEN
  1215. log := out;
  1216. ELSE
  1217. NEW(dummyWriter, 2);
  1218. log := dummyWriter;
  1219. step := ((nofMarked-1) DIV 10) +1; steps := 1;
  1220. out.String(" 00% "); out.Update;
  1221. END;
  1222. startTime := Dates.Now();
  1223. importCache := SyntaxTree.NewModuleScope();
  1224. nofFiles := 0; err := FALSE;
  1225. file := files;
  1226. WHILE (file # NIL) DO
  1227. IF file.IsSourceCode() & file.IsInRelease(include) & ~PackageIsExcluded(file.package) & file.doCompile & CompileThisPackage(file.package) THEN
  1228. IF (nofWorkers = 0) THEN
  1229. CompileFile(file, diagnostics, log, err, importCache);
  1230. IF err THEN
  1231. error.Ln; error.Ln;
  1232. error.String("Error(s) in file "); error.String(file.name); error.Ln;
  1233. diagnostics.ToStream(error, {Diagnostics.TypeError}); error.Ln;
  1234. diagnostics.Reset;
  1235. RETURN;
  1236. END;
  1237. ELSE
  1238. CreateJob(threadpool, file, diagnostics, importCache);
  1239. END;
  1240. INC(nofFiles);
  1241. IF ~verbose & (step # 0) & (nofFiles MOD step = 0) THEN
  1242. out.Int(steps * 10, 0); out.String("% "); out.Update;
  1243. INC(steps);
  1244. END;
  1245. END;
  1246. file := file.next;
  1247. END;
  1248. IF verbose THEN
  1249. out.Int(nofFiles, 0); out.String(" files done in ");
  1250. ELSIF (steps < 11) THEN
  1251. out.String("100% ");
  1252. END;
  1253. out.String(" done in "); Strings.ShowTimeDifference(startTime, Dates.Now(), out);
  1254. out.Ln; out.Update;
  1255. IF (nofWorkers > 0) THEN
  1256. threadpool.AwaitAllDone;
  1257. threadpool.Close;
  1258. err := diagnostics.nofErrors > 0;
  1259. IF (diagnostics.nofMessages > 0) THEN
  1260. diagnostics.ToStream(error, Diagnostics.All); error.Ln;
  1261. END;
  1262. out.String("Compilation time: "); Strings.ShowTimeDifference(startTime, Dates.Now(), out);
  1263. out.Ln; out.Update;
  1264. END;
  1265. END Compile;
  1266. END BuildObj;
  1267. TYPE
  1268. Version = RECORD
  1269. major, minor : LONGINT;
  1270. END;
  1271. Builds* = OBJECT
  1272. VAR
  1273. version : Version;
  1274. (* All builds described in the build description file *)
  1275. builds- : ARRAY MaxBuilds OF BuildObj;
  1276. nofBuilds : LONGINT;
  1277. packages- : PackageList;
  1278. (* In the build description file, the user may group files using prefixes, e.g.
  1279. WIN,NATIVE { filename }. In this example, WIN and NATIVE are prefixes.
  1280. The prefixes array contains each prefix exactly once. The index of specific prefix in this
  1281. are will be used as bit position for the BuildObj.include set. *)
  1282. prefixes : ARRAY MaxPrefixes OF Name;
  1283. nofPrefixes : LONGINT;
  1284. (* Name of the package description file *)
  1285. source : Files.FileName;
  1286. (* All files of all builds in the build description file *)
  1287. files : File;
  1288. (* number of files / sources in <files> list *)
  1289. nofFiles- : LONGINT;
  1290. nofSources- : LONGINT;
  1291. PROCEDURE &Init;
  1292. VAR i : LONGINT;
  1293. BEGIN
  1294. FOR i := 0 TO LEN(builds)-1 DO builds[i] := NIL; END;
  1295. nofBuilds := 0;
  1296. NEW(packages);
  1297. FOR i := 0 TO LEN(prefixes)-1 DO prefixes[i] := ""; END;
  1298. nofPrefixes := 0;
  1299. source := "";
  1300. files := NIL;
  1301. nofFiles := 0;
  1302. nofSources := 0;
  1303. END Init;
  1304. (* Add the specified prefix to the prefixes array if its not already contained *)
  1305. PROCEDURE AddPrefix(CONST prefix : Name; diagnostics : Diagnostics.Diagnostics) : BOOLEAN;
  1306. VAR error : BOOLEAN; i : LONGINT; j: LONGINT;
  1307. BEGIN
  1308. ASSERT((prefix # "") & (diagnostics # NIL));
  1309. error := FALSE;
  1310. (* check whether prefix is already registered *)
  1311. i := 0; WHILE (i < LEN(prefixes)) & (prefixes[i] # prefix) DO INC(i); END;
  1312. IF (i >= LEN(prefixes)) THEN (* new prefix, add it *)
  1313. IF (nofPrefixes < LEN(prefixes)) THEN
  1314. prefixes[nofPrefixes] := prefix;
  1315. INC(nofPrefixes);
  1316. ELSE
  1317. error := TRUE;
  1318. diagnostics.Warning("", Diagnostics.Invalid, Diagnostics.Invalid, "Maximum number of prefixes exceeded");
  1319. FOR j := 0 TO LEN(prefixes)-1 DO
  1320. TRACE(prefixes[j]);
  1321. END;
  1322. END;
  1323. END;
  1324. RETURN ~error;
  1325. END AddPrefix;
  1326. (* Get the index of the specified prefix in the prefixes array. Returns -1 if prefix not found *)
  1327. PROCEDURE GetPrefixIndex(CONST prefix : ARRAY OF CHAR) : LONGINT;
  1328. VAR index : LONGINT;
  1329. BEGIN
  1330. index := 0;
  1331. WHILE (index < nofPrefixes) & (prefixes[index] # prefix) DO INC(index); END;
  1332. IF (index >= nofPrefixes) THEN index := -1; END;
  1333. RETURN index;
  1334. END GetPrefixIndex;
  1335. (* Do checks for all builds *)
  1336. PROCEDURE CheckAll*(out : Streams.Writer; diagnostics : Diagnostics.Diagnostics);
  1337. VAR file : File; build : LONGINT; message : ARRAY 256 OF CHAR; error : BOOLEAN;
  1338. BEGIN
  1339. ASSERT(diagnostics # NIL);
  1340. (* Check whether all files exist and parse modules*)
  1341. out.String("Checking files for all builds... "); out.Update;
  1342. file := files;
  1343. WHILE (file # NIL) DO
  1344. IF (file.file = NIL) THEN
  1345. file.file := Files.Old(file.name);
  1346. END;
  1347. IF (file.file = NIL) THEN
  1348. MakeMessage(message, "File # does not exists (Package #)", file.name, file.package.name);
  1349. IF file.IsSourceCode() THEN
  1350. diagnostics.Error(source, file.pos, Diagnostics.Invalid, message);
  1351. ELSE
  1352. diagnostics.Warning(source, file.pos, Diagnostics.Invalid, message);
  1353. END;
  1354. ELSIF file.IsSourceCode() THEN
  1355. file.ParseModule(diagnostics);
  1356. END;
  1357. IF ~KeepFilesOpen THEN
  1358. file.file := NIL;
  1359. END;
  1360. file := file.next;
  1361. END;
  1362. out.String("done."); out.Ln;
  1363. build := 0;
  1364. WHILE (build < LEN(builds)) & (builds[build] # NIL) DO
  1365. out.String("Checking imports of builds "); out.String(builds[build].name); out.String("... "); out.Update;
  1366. error := FALSE;
  1367. builds[build].CheckModules(diagnostics, error);
  1368. out.String("done."); out.Ln;
  1369. INC(build);
  1370. END;
  1371. out.Update;
  1372. END CheckAll;
  1373. (* Show build statistics *)
  1374. PROCEDURE Show*(w : Streams.Writer; details : BOOLEAN);
  1375. VAR
  1376. statistics : Statistics; build, prefix, nofFiles, nofSources : LONGINT; file : File; options : ARRAY 256 OF CHAR;
  1377. diagnostics : Diagnostics.DiagnosticsList;
  1378. error : BOOLEAN;
  1379. BEGIN
  1380. NEW(statistics);
  1381. w.String("Release statistics:"); w.Ln;
  1382. nofFiles := 0; nofSources := 0;
  1383. file := files;
  1384. WHILE (file # NIL) DO
  1385. statistics.AddFile(file);
  1386. file := file.next;
  1387. END;
  1388. w.Int(statistics.nofFiles, 0); w.String(" files ("); w.Int(statistics.nofSources, 0); w.String(" sources)"); w.Ln;
  1389. w.String("Builds: ");
  1390. IF (builds[0].name # "") THEN
  1391. w.Ln;
  1392. FOR build := 0 TO LEN(builds)-1 DO
  1393. IF (builds[build] # NIL) THEN
  1394. w.String(builds[build].name); w.Ln;
  1395. w.Char(Tab); w.String("Includes: ");
  1396. FOR prefix := 0 TO LEN(builds[build].prefixes)-1 DO
  1397. IF (builds[build].prefixes[prefix] # "") THEN
  1398. w.String(" ["); w.String(builds[build].prefixes[prefix]); w.String("]");
  1399. END;
  1400. END;
  1401. w.Ln;
  1402. w.Char(Tab); w.String("Compile: ");
  1403. w.String(builds[build].compiler); w.String(" "); builds[build].GetCompilerOptions(options); w.String(options); w.Ln;
  1404. statistics.Get(nofFiles, nofSources, builds[build].include);
  1405. nofFiles := nofFiles + statistics.nofFilesAll;
  1406. nofSources := nofSources + statistics.nofSourcesAll;
  1407. w.Char(Tab); w.Int(nofFiles, 0); w.String(" files ("); w.Int(nofSources, 0); w.String(" sources)"); w.Ln;
  1408. END;
  1409. END;
  1410. ELSE
  1411. w.String("none"); w.Ln;
  1412. END;
  1413. packages.ToStream(w);
  1414. IF details THEN
  1415. prefix := 0;
  1416. WHILE (prefix < LEN(prefixes)) & (prefixes[prefix] # "") DO
  1417. w.String(prefixes[prefix]); w.Ln;
  1418. nofFiles := 0;
  1419. file := files;
  1420. WHILE (file # NIL) DO
  1421. IF ~IsAll(file.release) & Has(file.release, prefix) THEN
  1422. w.Char(Tab); w.String(file.name); w.Ln;
  1423. INC(nofFiles);
  1424. END;
  1425. file := file.next;
  1426. END;
  1427. w.Char(Tab); w.Int(nofFiles, 0); w.String(" files"); w.Ln;
  1428. INC(prefix);
  1429. END;
  1430. NEW(diagnostics);
  1431. FOR build := 0 TO LEN(builds)-1 DO
  1432. IF (builds[build] # NIL) THEN
  1433. w.String("*** Import statistics for build "); w.String(builds[build].name); w.String(" ***"); w.Ln;
  1434. error := FALSE;
  1435. builds[build].DoChecks(w, diagnostics, error); w.Ln;
  1436. IF ~error THEN
  1437. diagnostics.Reset;
  1438. builds[build].AnalyzeDependencies(w); w.Ln;
  1439. builds[build].ShowDependencies(w);
  1440. w.Ln;
  1441. ELSE
  1442. diagnostics.ToStream(w, Diagnostics.All); w.Ln;
  1443. END;
  1444. END;
  1445. END;
  1446. END;
  1447. w.Update;
  1448. END Show;
  1449. PROCEDURE GetReleaseSet(build : BuildObj; VAR release : Set): BOOLEAN;
  1450. VAR prefix, index, i : LONGINT;
  1451. BEGIN
  1452. SetEmpty(release);
  1453. FOR prefix := 0 TO LEN(build.prefixes)-1 DO
  1454. IF (build.prefixes[prefix] # "") THEN
  1455. index := GetPrefixIndex(build.prefixes[prefix]);
  1456. IF (index >= 0) THEN
  1457. Incl(release, index);
  1458. ELSE
  1459. TRACE("GETRELEASE SET FAILED", build.prefixes[prefix], index);
  1460. RETURN FALSE;
  1461. END;
  1462. END;
  1463. END;
  1464. RETURN TRUE;
  1465. END GetReleaseSet;
  1466. (** Get a reference to a BuildObj by name *)
  1467. PROCEDURE GetBuild*(CONST buildname : ARRAY OF CHAR) : BuildObj;
  1468. VAR build : BuildObj; i : LONGINT;
  1469. BEGIN
  1470. build := NIL;
  1471. i := 0; WHILE (i < LEN(builds)) & (builds[i] # NIL) & (builds[i].name # buildname) DO INC(i); END;
  1472. IF (i < LEN(builds)) THEN
  1473. build := builds[i];
  1474. END;
  1475. RETURN build;
  1476. END GetBuild;
  1477. PROCEDURE AddBuild(build : BuildObj; diagnostics : Diagnostics.Diagnostics) : BOOLEAN;
  1478. VAR error : BOOLEAN; i : LONGINT;
  1479. BEGIN
  1480. ASSERT((build # NIL) & (diagnostics # NIL));
  1481. error := FALSE;
  1482. IF (nofBuilds < LEN(builds)) THEN
  1483. build.builds := SELF;
  1484. builds[nofBuilds] := build;
  1485. FOR i := 0 TO LEN(build.prefixes)-1 DO
  1486. IF (build.prefixes[i] # "") THEN error := error OR ~AddPrefix(build.prefixes[i], diagnostics); END;
  1487. END;
  1488. INC(nofBuilds);
  1489. ELSE
  1490. error := TRUE;
  1491. diagnostics.Error(source, Diagnostics.Invalid, Diagnostics.Invalid, "Maximum number of builds exceeded");
  1492. END;
  1493. RETURN ~error;
  1494. END AddBuild;
  1495. PROCEDURE AddFile(CONST filename : ARRAY OF CHAR; release : Set; package : Package; pos : LONGINT);
  1496. VAR file, f : File; pre, suf : Files.FileName;
  1497. BEGIN
  1498. ASSERT(package # NIL);
  1499. NEW(file, SELF);
  1500. COPY(filename, file.name);
  1501. COPY(filename, file.uppercaseName);
  1502. Strings.UpperCase(file.uppercaseName);
  1503. SplitName(file.name, pre, file.module.name, suf);
  1504. IF (pre = ReleasePrefix) THEN
  1505. INCL(file.flags, HasReleasePrefix);
  1506. END;
  1507. IF (suf = "Mod") OR (suf="Mdf") OR (suf = "Mos") THEN
  1508. INCL(file.flags, SourceCode);
  1509. INC(nofSources);
  1510. INC(package.nofSources);
  1511. END;
  1512. INC(package.nofFiles);
  1513. file.package := package;
  1514. file.release := release;
  1515. file.pos := pos;
  1516. IF (files = NIL) THEN
  1517. files := file;
  1518. ELSE (* append to list *)
  1519. f := files;
  1520. WHILE (f.next # NIL) DO f := f.next; END;
  1521. f.next := file;
  1522. file.prev := f;
  1523. END;
  1524. INC(nofFiles);
  1525. END AddFile;
  1526. PROCEDURE FindFile(CONST filename : ARRAY OF CHAR) : File;
  1527. VAR file : File;
  1528. BEGIN
  1529. file := files;
  1530. WHILE (file # NIL) & (file.name # filename) DO file := file.next; END;
  1531. RETURN file;
  1532. END FindFile;
  1533. PROCEDURE FindFileCheckCase(CONST filename : ARRAY OF CHAR; VAR caseEqual : BOOLEAN) : File;
  1534. VAR result, file : File; fn : Name;
  1535. BEGIN
  1536. result := NIL;
  1537. COPY(filename, fn); Strings.UpperCase(fn);
  1538. file := files;
  1539. WHILE (file # NIL) & (result = NIL) DO
  1540. IF (filename = file.name) THEN
  1541. caseEqual := TRUE;
  1542. result := file;
  1543. ELSIF (fn = file.uppercaseName) THEN
  1544. caseEqual := FALSE;
  1545. result := file;
  1546. END;
  1547. file := file.next;
  1548. END;
  1549. RETURN result;
  1550. END FindFileCheckCase;
  1551. (* Called by the Parser after parsing is finished *)
  1552. PROCEDURE Initialize(diagnostics : Diagnostics.Diagnostics; VAR error : BOOLEAN);
  1553. VAR build, package : LONGINT; message : ARRAY 256 OF CHAR;
  1554. BEGIN
  1555. ASSERT(diagnostics # NIL);
  1556. FOR build := 0 TO LEN(builds)-1 DO
  1557. IF (builds[build] # NIL) THEN
  1558. IF GetReleaseSet(builds[build], builds[build].include) THEN
  1559. builds[build].files := files;
  1560. builds[build].packages := packages;
  1561. IF (builds[build].excludedPackages # NIL) THEN
  1562. FOR package := 0 TO LEN(builds[build].excludedPackages)-1 DO
  1563. IF (packages.FindPackage(builds[build].excludedPackages[package]^) = NIL) THEN
  1564. error := TRUE;
  1565. MakeMessage(message, "Excluded package '#' in build '#' does not exist",
  1566. builds[build].excludedPackages[package]^,
  1567. builds[build].name);
  1568. diagnostics.Error(source, Diagnostics.Invalid, Diagnostics.Invalid, message);
  1569. END;
  1570. END;
  1571. END;
  1572. END;
  1573. END;
  1574. END;
  1575. END Initialize;
  1576. END Builds;
  1577. CONST
  1578. (* Tokens*)
  1579. PACKAGE = "PACKAGE";
  1580. ARCHIVE = "ARCHIVE";
  1581. SOURCE = "SOURCE";
  1582. DESCRIPTION = "DESCRIPTION";
  1583. OPENSECTION = "{";
  1584. CLOSESECTION = "}";
  1585. SEPARATOR = ",";
  1586. ENDSECTION = "END";
  1587. HEADER = "HEADER";
  1588. VERSION = "VERSION";
  1589. BUILDS = "BUILDS";
  1590. INCLUDE="INCLUDE";
  1591. tIMPORT = "IMPORT";
  1592. COMPILER = "COMPILER";
  1593. COMPILEOPTIONS = "COMPILEOPTIONS";
  1594. LINKER = "LINKER";
  1595. LINKEROPTIONS = "LINKEROPTIONS";
  1596. TARGET = "TARGET";
  1597. EXTENSION="EXTENSION";
  1598. SYMBOLEXTENSION="SYMBOLEXTENSION";
  1599. PATH="PATH";
  1600. EXCLUDEPACKAGES = "EXCLUDEPACKAGES";
  1601. DISABLED = "DISABLED";
  1602. TYPE
  1603. Token = ARRAY 256 OF CHAR;
  1604. Scanner = OBJECT
  1605. VAR
  1606. source: Name;
  1607. reader : Streams.Reader;
  1608. diagnostics : Diagnostics.Diagnostics;
  1609. error : BOOLEAN;
  1610. peekMode, peekBufferValid : BOOLEAN;
  1611. peekToken : ARRAY 256 OF CHAR;
  1612. peekError : BOOLEAN;
  1613. pos : LONGINT;
  1614. name : ARRAY 256 OF CHAR;
  1615. PROCEDURE Error(pos : LONGINT; CONST msg, par1, par2 : ARRAY OF CHAR);
  1616. VAR message : ARRAY 128 OF CHAR;
  1617. BEGIN
  1618. error := TRUE;
  1619. MakeMessage(message, msg, par1, par2);
  1620. diagnostics.Error(source, pos, Diagnostics.Invalid, message);
  1621. END Error;
  1622. PROCEDURE Check(CONST token : Token) : BOOLEAN;
  1623. VAR temp : Token;
  1624. BEGIN
  1625. IF Get(temp) & (temp = token) THEN
  1626. RETURN TRUE
  1627. ELSE
  1628. Error(reader.Pos(), "Expected '#' token", token, "");
  1629. RETURN FALSE;
  1630. END;
  1631. END Check;
  1632. PROCEDURE IsIdentifier(CONST token : ARRAY OF CHAR) : BOOLEAN;
  1633. BEGIN
  1634. RETURN
  1635. (token # PACKAGE) & (token # ARCHIVE) & (token # SOURCE) & (token # DESCRIPTION) &
  1636. (token # OPENSECTION) & (token # CLOSESECTION) & (token # ENDSECTION) &
  1637. (token # SEPARATOR) & (token # BUILDS) & (token # HEADER) & (token # VERSION) &
  1638. (token # INCLUDE) & (token # COMPILER) & (token # COMPILEOPTIONS) & (token # TARGET) & (token # EXTENSION) & (token # SYMBOLEXTENSION) &
  1639. (token # PATH) & (token # EXCLUDEPACKAGES);
  1640. END IsIdentifier;
  1641. PROCEDURE GetIdentifier(VAR identifier : ARRAY OF CHAR) : BOOLEAN;
  1642. BEGIN
  1643. IF Get(identifier) THEN
  1644. IF IsIdentifier(identifier) THEN
  1645. RETURN TRUE;
  1646. ELSE
  1647. Error(pos, "Identifier expected but found token #", identifier, "");
  1648. END;
  1649. END;
  1650. RETURN FALSE;
  1651. END GetIdentifier;
  1652. PROCEDURE Peek(VAR token : ARRAY OF CHAR);
  1653. BEGIN
  1654. IF (peekMode = FALSE) THEN
  1655. IF Get(token) THEN
  1656. peekMode := TRUE;
  1657. peekError := FALSE;
  1658. COPY(token, peekToken);
  1659. ELSE
  1660. peekError := TRUE;
  1661. COPY("", peekToken);
  1662. END;
  1663. END;
  1664. COPY(peekToken, token);
  1665. END Peek;
  1666. PROCEDURE Get(VAR token : ARRAY OF CHAR) : BOOLEAN;
  1667. VAR delimiter, ch : CHAR; i : LONGINT; useDelimiter : BOOLEAN;
  1668. BEGIN
  1669. IF (peekMode) THEN
  1670. COPY(peekToken, token);
  1671. peekBufferValid := TRUE;
  1672. peekMode := FALSE;
  1673. IF (peekError) THEN
  1674. Error(reader.Pos(), "ERROR", "", "");
  1675. END;
  1676. RETURN ~peekError;
  1677. END;
  1678. name := "";
  1679. SkipComments;
  1680. delimiter := reader.Peek(); useDelimiter := (delimiter = "'") OR (delimiter = '"');
  1681. IF useDelimiter THEN reader.Char(ch); END;
  1682. pos := reader.Pos();
  1683. i := 0;
  1684. REPEAT
  1685. reader.Char(ch); (* Since we skipped the comments and whitespace, ch cannot be "#" or whitespace *)
  1686. IF useDelimiter & (ch = delimiter) OR (ch=0X) THEN
  1687. ELSE
  1688. token[i] := ch; INC(i);
  1689. END;
  1690. IF (~useDelimiter & (ch # "{") & (ch # "}") & (ch # ",")) THEN
  1691. ch := reader.Peek();
  1692. END;
  1693. UNTIL
  1694. (i >= LEN(token)-1) OR ((reader.res = Streams.EOF) & (ch = 0X)) OR
  1695. (~useDelimiter & (IsWhitespace(ch) OR (ch = "#") OR (ch ="{") OR (ch="}") OR (ch = ","))) OR
  1696. (useDelimiter & (ch = delimiter));
  1697. IF (i = 0) & (reader.res = Streams.EOF) THEN (* end of text *)
  1698. RETURN FALSE
  1699. ELSIF (i < LEN(token)) THEN
  1700. token[i] := 0X;
  1701. COPY(token, name);
  1702. RETURN TRUE;
  1703. ELSE
  1704. Error(reader.Pos(), "Token too long", "", "");
  1705. RETURN FALSE;
  1706. END;
  1707. END Get;
  1708. PROCEDURE IsWhitespace(ch : CHAR) : BOOLEAN;
  1709. BEGIN
  1710. RETURN (ch <= " ");
  1711. END IsWhitespace;
  1712. PROCEDURE SkipComments;
  1713. VAR ch : CHAR;
  1714. BEGIN
  1715. reader.SkipWhitespace;
  1716. ch := reader.Peek();
  1717. WHILE (ch = "#") DO reader.SkipLn; reader.SkipWhitespace; ch := reader.Peek(); END;
  1718. END SkipComments;
  1719. PROCEDURE &Init(CONST source: ARRAY OF CHAR; reader : Streams.Reader; diagnostics : Diagnostics.Diagnostics);
  1720. BEGIN
  1721. COPY (source, SELF.source);
  1722. ASSERT((reader # NIL) & (diagnostics # NIL));
  1723. SELF.reader := reader;
  1724. SELF.diagnostics := diagnostics;
  1725. peekMode := FALSE; peekToken := ""; peekError := FALSE;
  1726. error := FALSE;
  1727. END Init;
  1728. END Scanner;
  1729. TYPE
  1730. Parser = OBJECT
  1731. VAR
  1732. scanner : Scanner;
  1733. diagnostics : Diagnostics.Diagnostics;
  1734. log: Streams.Writer;
  1735. error : BOOLEAN;
  1736. currentPackage : Package;
  1737. PROCEDURE Error(pos : LONGINT; CONST msg, par1, par2 : ARRAY OF CHAR);
  1738. VAR message : ARRAY 128 OF CHAR;
  1739. BEGIN
  1740. error := TRUE;
  1741. MakeMessage(message, msg, par1, par2);
  1742. diagnostics.Error(scanner.source, pos, Diagnostics.Invalid, message);
  1743. END Error;
  1744. PROCEDURE Warning(pos : LONGINT; CONST msg, par1, par2 : ARRAY OF CHAR);
  1745. VAR message : ARRAY 128 OF CHAR;
  1746. BEGIN
  1747. MakeMessage(message, msg, par1, par2);
  1748. diagnostics.Warning(scanner.source, pos, Diagnostics.Invalid, message);
  1749. END Warning;
  1750. PROCEDURE IsFilename(CONST token : Token) : BOOLEAN;
  1751. VAR i : LONGINT;
  1752. BEGIN
  1753. i := 1; (* don't allow filenames to start with "." *)
  1754. WHILE (i < LEN(token)) & (token[i] # ".") & (token[i] # 0X) DO INC(i); END;
  1755. RETURN (i < LEN(token)) & (token[i] = ".");
  1756. END IsFilename;
  1757. PROCEDURE Parse(VAR builds : Builds) : BOOLEAN;
  1758. VAR token : Token; v1, v2 : ARRAY 16 OF CHAR;
  1759. BEGIN
  1760. IF builds = NIL THEN NEW(builds) END;
  1761. COPY(scanner.source, builds.source);
  1762. IF ParseHeader(builds) THEN
  1763. IF (builds.version.major = VersionMajor) & (builds.version.minor <= VersionMinor) THEN
  1764. IF ParseImport(builds) THEN (* load included sections *)
  1765. END;
  1766. IF ParseBuilds(builds) THEN END; (* optional *)
  1767. LOOP
  1768. currentPackage := ParsePackageHeader();
  1769. IF (currentPackage # NIL) THEN
  1770. IF builds.packages.Add(currentPackage) THEN
  1771. IF ~ParsePackage(builds, token) THEN
  1772. EXIT;
  1773. END;
  1774. ELSE
  1775. Error(scanner.pos, "Package # is already defined", currentPackage.name, "");
  1776. EXIT;
  1777. END;
  1778. ELSE
  1779. EXIT;
  1780. END;
  1781. scanner.SkipComments;
  1782. IF scanner.reader.Available() < 5 THEN EXIT; END;
  1783. END;
  1784. IF ~error THEN builds.Initialize(diagnostics, error); END;
  1785. ELSE
  1786. VersionToString(VersionMajor, VersionMinor, v1);
  1787. VersionToString(builds.version.major, builds.version.minor, v2);
  1788. Error(Diagnostics.Invalid, "Version mismatch, Release.Mod is version #, tool file is version #", v1, v2);
  1789. END;
  1790. END;
  1791. RETURN ~(error OR scanner.error OR (builds = NIL));
  1792. END Parse;
  1793. (* Import = IMPORT Entries END whereas Entries = FileName *)
  1794. PROCEDURE ParseImport(VAR builds : Builds) : BOOLEAN;
  1795. VAR token : Token; ignore : BOOLEAN; filename: Files.FileName;
  1796. BEGIN
  1797. ASSERT(builds # NIL);
  1798. scanner.Peek(token);
  1799. IF (token = tIMPORT) THEN
  1800. ignore := scanner.Get(token);
  1801. LOOP
  1802. scanner.Peek(token);
  1803. IF (token = ENDSECTION) THEN
  1804. ignore := scanner.Get(token);
  1805. RETURN TRUE;
  1806. ELSIF scanner.IsIdentifier(token) THEN (* filename *)
  1807. IF IsFilename(token) THEN
  1808. COPY(token, filename);
  1809. IF ~ParseBuildFile(filename, builds, log, diagnostics) THEN
  1810. Error(scanner.pos, "Could not include #", filename, "")
  1811. END;
  1812. ELSE
  1813. Error(scanner.pos, "No filename #",token,"");
  1814. RETURN FALSE
  1815. END;
  1816. ignore := scanner.Get(token);
  1817. END;
  1818. END;
  1819. END;
  1820. RETURN FALSE;
  1821. END ParseImport;
  1822. (* Header = HEADER Entries END whereas Entries = VERSION VersionString *)
  1823. PROCEDURE ParseHeader(builds : Builds) : BOOLEAN;
  1824. VAR token : Token; ignore : BOOLEAN;
  1825. PROCEDURE ParseVersionString(CONST string : ARRAY OF CHAR) : BOOLEAN;
  1826. VAR strings : Strings.StringArray;
  1827. BEGIN
  1828. strings := Strings.Split(string, ".");
  1829. IF (LEN(strings) = 2) THEN
  1830. Strings.StrToInt(strings[0]^, builds.version.major);
  1831. Strings.StrToInt(strings[1]^, builds.version.minor);
  1832. RETURN TRUE;
  1833. ELSE
  1834. Error(scanner.pos, "Expected version string major.minor, found #", string, "");
  1835. RETURN FALSE;
  1836. END;
  1837. END ParseVersionString;
  1838. BEGIN
  1839. ASSERT(builds # NIL);
  1840. scanner.Peek(token);
  1841. IF (token = HEADER) THEN
  1842. ignore := scanner.Get(token);
  1843. LOOP
  1844. scanner.Peek(token);
  1845. IF (token = ENDSECTION) THEN
  1846. ignore := scanner.Get(token);
  1847. RETURN TRUE;
  1848. ELSIF (token = VERSION) THEN
  1849. ignore := scanner.Get(token);
  1850. IF scanner.Get(token) THEN
  1851. IF ~ParseVersionString(token) THEN
  1852. Error(scanner.pos, "Invalid version string: #", token, "");
  1853. EXIT;
  1854. END;
  1855. ELSE
  1856. Error(scanner.pos, "Version number expected", "", "");
  1857. EXIT;
  1858. END;
  1859. ELSE
  1860. Error(scanner.pos, "Expected # or # token", HEADER, ENDSECTION);
  1861. EXIT;
  1862. END;
  1863. END;
  1864. ELSE
  1865. Error(scanner.pos, "# section expected", HEADER, "");
  1866. END;
  1867. RETURN FALSE;
  1868. END ParseHeader;
  1869. (* Builds = [ BUILDS {Build} END ] whereas Build = BuildName "{" prefix {" " perfix} "}" *)
  1870. PROCEDURE ParseBuilds(builds : Builds) : BOOLEAN;
  1871. VAR token : Token; build : BuildObj;
  1872. BEGIN
  1873. IF builds = NIL THEN RETURN FALSE END;
  1874. ASSERT(builds # NIL);
  1875. scanner.Peek(token);
  1876. IF (token = BUILDS) THEN
  1877. IF scanner.Get(token) THEN END; (* consume BUILDS *)
  1878. LOOP
  1879. scanner.Peek(token);
  1880. IF (token = ENDSECTION) THEN
  1881. IF scanner.Get(token) THEN END; (* consume token *)
  1882. RETURN TRUE;
  1883. ELSE
  1884. build := ParseBuild();
  1885. IF (build # NIL) THEN
  1886. IF ~builds.AddBuild(build, diagnostics) THEN
  1887. EXIT;
  1888. END;
  1889. ELSE
  1890. EXIT;
  1891. END;
  1892. END;
  1893. END;
  1894. END;
  1895. RETURN FALSE;
  1896. END ParseBuilds;
  1897. (* Build = BuildName "{" prefix {"," perfix} "}" *)
  1898. PROCEDURE ParseBuild() : BuildObj;
  1899. VAR
  1900. token : Token; prefix : LONGINT;
  1901. string : ARRAY 256 OF CHAR; stringArray : Strings.StringArray;
  1902. compilerSet, compileOptionsSet, linkerSet, linkOptionsSet, targetSet, includeSet, excludePackagesSet, extensionSet, symbolsSet, pathSet, disabledSet : BOOLEAN;
  1903. build : BuildObj;
  1904. PROCEDURE CheckOptions;
  1905. BEGIN
  1906. IF ~includeSet THEN Warning(scanner.pos, "# not set in build #", INCLUDE, build.name); END;
  1907. IF ~compilerSet THEN Warning(scanner.pos, "# not set in build #", COMPILER, build.name); END;
  1908. IF ~compileOptionsSet THEN Warning(scanner.pos, "# not set in build #", COMPILEOPTIONS, build.name); END;
  1909. IF ~targetSet THEN Warning(scanner.pos, "# not set in build #", TARGET, build.name); END;
  1910. IF ~extensionSet THEN Warning(scanner.pos, "# not set in build #", EXTENSION, build.name); END;
  1911. IF ~symbolsSet THEN (* no warning since optional *) END;
  1912. IF ~pathSet THEN Warning(scanner.pos, "# not set in build #", PATH, build.name); END;
  1913. IF ~disabledSet THEN Warning(scanner.pos, "# not set in build #", DISABLED, build.name); END;
  1914. END CheckOptions;
  1915. BEGIN
  1916. NEW(build);
  1917. compilerSet := FALSE; compileOptionsSet := FALSE; linkerSet := FALSE; linkOptionsSet := FALSE; targetSet := FALSE; includeSet := FALSE;
  1918. extensionSet := FALSE; symbolsSet := FALSE; pathSet := FALSE; excludePackagesSet := FALSE; disabledSet := FALSE;
  1919. IF scanner.GetIdentifier(build.name) THEN
  1920. build.position := scanner.pos;
  1921. IF scanner.Check(OPENSECTION) THEN
  1922. LOOP
  1923. IF scanner.Get(token) THEN
  1924. IF (token = CLOSESECTION) THEN
  1925. CheckOptions;
  1926. RETURN build;
  1927. ELSIF (token = COMPILER) THEN
  1928. IF compilerSet THEN Error(scanner.pos, "# already set in build #", COMPILER, build.name); ELSE compilerSet := TRUE; END;
  1929. IF ~scanner.GetIdentifier(build.compiler) THEN
  1930. (* continue *)
  1931. END;
  1932. ELSIF (token = COMPILEOPTIONS) THEN
  1933. IF compileOptionsSet THEN Error(scanner.pos, "# already set in build #", COMPILEOPTIONS, build.name); ELSE compileOptionsSet := TRUE; END;
  1934. IF ~scanner.GetIdentifier(build.compileOptions) THEN
  1935. (* continue *)
  1936. END;
  1937. ELSIF (token = LINKER) THEN
  1938. IF linkerSet THEN Error(scanner.pos, '# already set in build #', LINKER, build.name); ELSE linkerSet := TRUE; END;
  1939. IF ~scanner.GetIdentifier(build.linker) THEN
  1940. (* continue *)
  1941. END;
  1942. ELSIF (token = LINKEROPTIONS) THEN
  1943. IF linkOptionsSet THEN Error(scanner.pos, '# already set in build #', LINKEROPTIONS, build.name); ELSE linkOptionsSet := TRUE; END;
  1944. IF ~scanner.GetIdentifier(build.linkOptions) THEN
  1945. (* continue *)
  1946. END;
  1947. ELSIF (token = TARGET) THEN
  1948. IF targetSet THEN Error(scanner.pos, "# already set in build #", TARGET, build.name); ELSE targetSet := TRUE; END;
  1949. IF ~scanner.GetIdentifier(build.target) THEN
  1950. (* continue *)
  1951. END;
  1952. ELSIF (token = EXTENSION) THEN
  1953. IF extensionSet THEN Error(scanner.pos, "# already set in build #", EXTENSION, build.name); ELSE extensionSet := TRUE; END;
  1954. IF ~scanner.GetIdentifier(build.extension) THEN
  1955. (* continue *)
  1956. END;
  1957. ELSIF (token = SYMBOLEXTENSION) THEN
  1958. IF symbolsSet THEN Error(scanner.pos, "# already set in build #", SYMBOLEXTENSION, build.name); ELSE symbolsSet := TRUE; END;
  1959. IF ~scanner.GetIdentifier(build.symbolFileExtension) THEN
  1960. (* continue *)
  1961. END;
  1962. ELSIF (token = PATH) THEN
  1963. IF pathSet THEN Error(scanner.pos, "# already set in build #", PATH, build.name); ELSE pathSet := TRUE; END;
  1964. IF ~scanner.GetIdentifier(build.path) THEN
  1965. (* continue *)
  1966. END;
  1967. ELSIF (token = INCLUDE) THEN
  1968. IF includeSet THEN Error(scanner.pos, "# already set in build #", INCLUDE, build.name); ELSE includeSet := TRUE; END;
  1969. IF scanner.GetIdentifier(string) THEN
  1970. stringArray := Strings.Split(string, " ");
  1971. prefix := 0;
  1972. IF LEN(stringArray) <= LEN(build.prefixes) THEN
  1973. FOR prefix := 0 TO LEN(stringArray)-1 DO
  1974. COPY(stringArray[prefix]^, build.prefixes[prefix]);
  1975. END;
  1976. ELSE
  1977. Error(scanner.pos, "Maximum number of prefixes exceeded.", "", "");
  1978. END;
  1979. END;
  1980. ELSIF (token = EXCLUDEPACKAGES) THEN
  1981. IF excludePackagesSet THEN Error(scanner.pos, "# already set in build #", EXCLUDEPACKAGES, build.name);
  1982. ELSE excludePackagesSet := TRUE;
  1983. END;
  1984. IF scanner.GetIdentifier(string) THEN
  1985. Strings.TrimWS(string);
  1986. IF (string # "") THEN
  1987. build.excludedPackages := Strings.Split(string, " ");
  1988. END;
  1989. END;
  1990. ELSIF (token = DISABLED) THEN
  1991. IF disabledSet THEN Error(scanner.pos, "# already set in build #", DISABLED, build.name);
  1992. ELSE disabledSet := TRUE;
  1993. END;
  1994. IF scanner.GetIdentifier(string) THEN
  1995. Strings.TrimWS(string);
  1996. Strings.UpperCase(string);
  1997. IF (string = "TRUE") THEN
  1998. build.disabled := TRUE;
  1999. ELSIF (string = "FALSE") THEN
  2000. build.disabled := FALSE;
  2001. ELSE
  2002. Warning(scanner.pos, "Wrong value for # in build #", DISABLED, build.name);
  2003. build.disabled := TRUE;
  2004. END;
  2005. ELSE
  2006. (* error reported by scanner.GetIdentifier *)
  2007. END;
  2008. ELSE
  2009. IF includeSet & compilerSet & targetSet & extensionSet & pathSet THEN
  2010. Error(scanner.pos, "Expected # or # token", CLOSESECTION, EXCLUDEPACKAGES);
  2011. ELSE
  2012. Error(scanner.pos, "Expected INCLUDE, COMPILER or COMPILEOPTIONS token", "", "");
  2013. END;
  2014. EXIT;
  2015. END;
  2016. ELSE
  2017. EXIT;
  2018. END;
  2019. END;
  2020. END;
  2021. END;
  2022. RETURN NIL;
  2023. END ParseBuild;
  2024. (* PackageHeader = PACKAGE PackageName ARCHIVE ArchiveName SOURCE SourceArchiveName DESCRIPTION description *)
  2025. PROCEDURE ParsePackageHeader() : Package;
  2026. VAR package : Package; name, archive, source : ARRAY 32 OF CHAR; description : ARRAY 256 OF CHAR; position : LONGINT;
  2027. BEGIN
  2028. package := NIL;
  2029. IF scanner.Check(PACKAGE) THEN
  2030. position := scanner.pos;
  2031. IF scanner.GetIdentifier(name) &
  2032. scanner.Check(ARCHIVE) & scanner.GetIdentifier(archive) &
  2033. scanner.Check(SOURCE) & scanner.GetIdentifier(source) &
  2034. scanner.Check(DESCRIPTION) & scanner.GetIdentifier(description)
  2035. THEN
  2036. NEW(package, name, archive, source, description, position);
  2037. END;
  2038. END;
  2039. RETURN package;
  2040. END ParsePackageHeader;
  2041. (* Package = { FileList | Prefix "{" FileList "}" }
  2042. Prefix = prefix {"," prefix}
  2043. FileList = filename {" " filename} *)
  2044. PROCEDURE ParsePackage(builds : Builds; VAR token : Token) : BOOLEAN;
  2045. VAR currentRelease : Set; index : LONGINT; pos : LONGINT; nbr : ARRAY 8 OF CHAR; caseEqual : BOOLEAN; file : File;
  2046. BEGIN
  2047. SetAll(currentRelease);
  2048. LOOP
  2049. IF scanner.Get(token) THEN
  2050. index := builds.GetPrefixIndex(token);
  2051. IF (index >= 0) THEN
  2052. IF IsAll(currentRelease) THEN
  2053. SetEmpty(currentRelease);
  2054. IF ~ParseBuildPrefixes(builds, token, currentRelease, pos) THEN
  2055. RETURN FALSE;
  2056. END;
  2057. ELSE
  2058. Strings.IntToStr(pos, nbr);
  2059. Error(scanner.pos, "Expected closing brace for tag at position #", nbr, "");
  2060. RETURN FALSE;
  2061. END;
  2062. ELSIF (token = CLOSESECTION) THEN
  2063. IF ~IsAll(currentRelease) THEN
  2064. SetAll(currentRelease);
  2065. ELSE
  2066. Error(scanner.pos, "No matching opening bracket", "", "");
  2067. RETURN FALSE;
  2068. END;
  2069. ELSIF (token = ENDSECTION) THEN
  2070. RETURN TRUE;
  2071. ELSIF scanner.IsIdentifier(token) THEN (* filename *)
  2072. IF IsFilename(token) THEN
  2073. file := builds.FindFileCheckCase(token, caseEqual);
  2074. IF (file # NIL) THEN
  2075. IF caseEqual THEN
  2076. Warning(scanner.pos, "Duplicate file found: #", token, "");
  2077. ELSE
  2078. Warning(scanner.pos, "Same file name with different case found: # vs #", token, file.name);
  2079. END;
  2080. END;
  2081. builds.AddFile(token, currentRelease, currentPackage, scanner.pos);
  2082. ELSE
  2083. diagnostics.Warning(scanner.source, scanner.pos, Diagnostics.Invalid, "Expected filename (not file extension?)");
  2084. END;
  2085. ELSE
  2086. Error(scanner.pos, "Expected identifier, found #", token, "");
  2087. END;
  2088. ELSE
  2089. EXIT;
  2090. END;
  2091. END;
  2092. RETURN IsAll(currentRelease);
  2093. END ParsePackage;
  2094. PROCEDURE ParseBuildPrefixes(builds : Builds; VAR token : Token; VAR release : Set; VAR pos : LONGINT) : BOOLEAN;
  2095. VAR index : LONGINT; message : ARRAY 128 OF CHAR;
  2096. BEGIN
  2097. index := builds.GetPrefixIndex(token);
  2098. IF (index >= 0) THEN
  2099. Incl(release,index);
  2100. ELSE
  2101. MakeMessage(message, "Unknown build prefix #", token, "");
  2102. diagnostics.Warning(scanner.source, scanner.pos, Diagnostics.Invalid, message);
  2103. END;
  2104. IF scanner.Get(token) THEN
  2105. IF (token = OPENSECTION) THEN
  2106. RETURN TRUE;
  2107. ELSIF (token = SEPARATOR) THEN
  2108. RETURN scanner.Get(token) & ParseBuildPrefixes(builds, token, release, pos);
  2109. ELSE
  2110. Error(scanner.pos, "Expected '{' or ',' token", "", "");
  2111. RETURN FALSE;
  2112. END;
  2113. ELSE
  2114. RETURN FALSE;
  2115. END;
  2116. END ParseBuildPrefixes;
  2117. PROCEDURE &Init(scanner : Scanner; log: Streams.Writer; diagnostics : Diagnostics.Diagnostics);
  2118. BEGIN
  2119. ASSERT((scanner # NIL) & (diagnostics # NIL));
  2120. SELF.scanner := scanner;
  2121. SELF.diagnostics := diagnostics;
  2122. SELF.log := log
  2123. END Init;
  2124. END Parser;
  2125. PROCEDURE SetAll(VAR s: Set);
  2126. VAR i: LONGINT;
  2127. BEGIN
  2128. FOR i := 0 TO LEN(s)-1 DO
  2129. s[i] := {MIN(SET)..MAX(SET)};
  2130. END;
  2131. END SetAll;
  2132. PROCEDURE SetEmpty(VAR s: Set);
  2133. VAR i: LONGINT;
  2134. BEGIN
  2135. FOR i := 0 TO LEN(s)-1 DO
  2136. s[i] := {};
  2137. END;
  2138. END SetEmpty;
  2139. PROCEDURE IsAll(CONST s: Set): BOOLEAN;
  2140. VAR i: LONGINT;
  2141. BEGIN
  2142. FOR i := 0 TO LEN(s)-1 DO
  2143. IF s[i] # {MIN(SET)..MAX(SET)} THEN RETURN FALSE END;
  2144. END;
  2145. RETURN TRUE;
  2146. END IsAll;
  2147. PROCEDURE Incl(VAR s: Set; i: LONGINT);
  2148. BEGIN
  2149. INCL(s[i DIV SetSize], i MOD SetSize);
  2150. END Incl;
  2151. PROCEDURE Excl(VAR s: Set; i: LONGINT);
  2152. BEGIN
  2153. EXCL(s[i DIV SetSize], i MOD SetSize);
  2154. END Excl;
  2155. PROCEDURE Has(CONST s: Set; i: LONGINT): BOOLEAN;
  2156. BEGIN
  2157. RETURN i MOD SetSize IN s[i DIV SetSize];
  2158. END Has;
  2159. PROCEDURE GetModuleInfo(
  2160. in : Streams.Reader;
  2161. VAR mi : ModuleInfo;
  2162. CONST source, filename : ARRAY OF CHAR;
  2163. errorPosition : LONGINT;
  2164. diagnostics : Diagnostics.Diagnostics;
  2165. VAR error : BOOLEAN);
  2166. PROCEDURE SkipComments(in : Streams.Reader);
  2167. VAR level, oldLevel, oldPosition : LONGINT; ch, nextCh : CHAR;
  2168. BEGIN
  2169. ASSERT((in # NIL) & (in.CanSetPos()));
  2170. level := 0;
  2171. REPEAT
  2172. ASSERT(level >= 0);
  2173. in.SkipWhitespace;
  2174. oldLevel := level;
  2175. oldPosition := in.Pos();
  2176. in.Char(ch); nextCh := in.Peek();
  2177. IF (ch = "(") & (nextCh = "*") THEN
  2178. INC(level); in.Char(ch);
  2179. ELSIF (level > 0) & (ch = "*") & (nextCh = ")") THEN
  2180. DEC(level); in.Char(ch);
  2181. ELSIF (level = 0) THEN
  2182. in.SetPos(oldPosition);
  2183. END;
  2184. UNTIL ((level = 0) & (oldLevel = 0)) OR (in.res # Streams.Ok);
  2185. END SkipComments;
  2186. PROCEDURE SkipProperties (in : Streams.Reader);
  2187. VAR ch : CHAR;
  2188. BEGIN
  2189. in.SkipWhitespace;
  2190. ch := in.Peek();
  2191. IF ch = "{" THEN
  2192. in.Char(ch);
  2193. REPEAT
  2194. in.Char(ch);
  2195. UNTIL (ch = "}") OR (in.res # Streams.Ok)
  2196. END;
  2197. END SkipProperties;
  2198. PROCEDURE GetIdentifier(in : Streams.Reader; VAR identifier : ARRAY OF CHAR);
  2199. VAR ch : CHAR; i : LONGINT;
  2200. BEGIN
  2201. ASSERT(in # NIL);
  2202. i := 0;
  2203. ch := in.Peek();
  2204. WHILE (('a' <= ch) & (ch <= 'z')) OR (('A' <= ch) & (ch <= 'Z')) OR (('0' <= ch) & (ch <= '9')) OR (ch = "_") & (i < LEN(identifier) - 1) DO
  2205. in.Char(identifier[i]); INC(i);
  2206. ch := in.Peek();
  2207. END;
  2208. identifier[i] := 0X;
  2209. END GetIdentifier;
  2210. PROCEDURE GetContext(in : Streams.Reader; ch1, ch2 : CHAR; VAR context : ARRAY OF CHAR; diagnostics : Diagnostics.Diagnostics; VAR error : BOOLEAN);
  2211. VAR message : ARRAY 512 OF CHAR; ch : CHAR;
  2212. BEGIN
  2213. ASSERT((in # NIL) & (diagnostics # NIL));
  2214. SkipComments(in);
  2215. ch := in.Peek();
  2216. IF (Strings.UP(ch) = ch1) THEN
  2217. in.Char(ch); in.Char(ch);
  2218. IF Strings.UP(ch) = ch2 THEN
  2219. SkipComments(in);
  2220. GetIdentifier(in, context); (* ignore context identifier *)
  2221. IF (context = "") THEN
  2222. error := TRUE;
  2223. MakeMessage(message, "Context identifier missing in file #", filename, "");
  2224. diagnostics.Error(source, errorPosition, Diagnostics.Invalid, message);
  2225. END;
  2226. SkipComments(in);
  2227. ELSE
  2228. error := TRUE;
  2229. MakeMessage(message, "Expected 'IN' keyword in file #", filename, "");
  2230. diagnostics.Error(source, errorPosition, Diagnostics.Invalid, message);
  2231. END;
  2232. END;
  2233. END GetContext;
  2234. PROCEDURE GetModuleNameAndContext(in : Streams.Reader; VAR name, context : Name; diagnostics : Diagnostics.Diagnostics; VAR error : BOOLEAN);
  2235. VAR token, message : ARRAY 512 OF CHAR; ch : CHAR;
  2236. BEGIN
  2237. ASSERT((in # NIL) & (diagnostics # NIL) & (~error));
  2238. name := ""; COPY(Modules.DefaultContext, context);
  2239. SkipComments(in);
  2240. in.SkipWhitespace; in.String(token); Strings.UpperCase(token);
  2241. IF (token = "MODULE") OR (token = "CELLNET") THEN
  2242. SkipComments(in);
  2243. SkipProperties(in);
  2244. SkipComments(in);
  2245. GetIdentifier(in, name);
  2246. IF (name # "") THEN
  2247. SkipComments(in);
  2248. GetContext(in, "I", "N", context, diagnostics, error);
  2249. in.Char(ch);
  2250. IF ~error & (ch # ";") THEN
  2251. error := TRUE;
  2252. MakeMessage(message, "Expected semicolon after module identifier in file #", filename, "");
  2253. diagnostics.Error(source, errorPosition, Diagnostics.Invalid, message);
  2254. END;
  2255. ELSE
  2256. error := TRUE;
  2257. MakeMessage(message, "Module identifier missing in file #", filename, "");
  2258. diagnostics.Error(source, errorPosition, Diagnostics.Invalid, message);
  2259. END;
  2260. ELSE
  2261. error := TRUE;
  2262. MakeMessage(message, "MODULE keyword missing in file #, first token is #", filename, token);
  2263. diagnostics.Error(source, errorPosition, Diagnostics.Invalid, message);
  2264. END;
  2265. END GetModuleNameAndContext;
  2266. PROCEDURE GetImport(in : Streams.Reader; VAR import, context : ARRAY OF CHAR; diagnostics : Diagnostics.Diagnostics; VAR error : BOOLEAN);
  2267. VAR message : ARRAY 512 OF CHAR;
  2268. BEGIN
  2269. ASSERT((in # NIL) & (diagnostics # NIL));
  2270. SkipComments(in);
  2271. GetIdentifier(in, import);
  2272. IF (import # "") THEN
  2273. GetContext(in, ':', '=', import, diagnostics, error);
  2274. IF ~error THEN GetContext(in, "I", "N", context, diagnostics, error); END;
  2275. ELSE
  2276. error := TRUE;
  2277. MakeMessage(message, "Identifier expected in import section of file #", filename, "");
  2278. diagnostics.Error(source, errorPosition, Diagnostics.Invalid, message);
  2279. END;
  2280. END GetImport;
  2281. PROCEDURE GetImports(in : Streams.Reader; VAR mi : ModuleInfo; diagnostics : Diagnostics.Diagnostics; VAR error : BOOLEAN);
  2282. VAR string, import, context, message : ARRAY 256 OF CHAR; ch : CHAR;
  2283. BEGIN
  2284. ASSERT((in # NIL) & (in.CanSetPos()) & (diagnostics #NIL) & (~error));
  2285. SkipComments(in);
  2286. GetIdentifier(in, string); Strings.UpperCase(string);
  2287. IF (string = "IMPORT") THEN
  2288. LOOP
  2289. COPY(mi.context, context);
  2290. GetImport(in, import, context, diagnostics, error);
  2291. IF ~error THEN
  2292. IF (import = "SYSTEM") OR (import = "system") THEN
  2293. INCL(mi.flags, ImportsSystem);
  2294. ELSIF (mi.nofImports < LEN(mi.imports)) THEN
  2295. CreateContext(import, context);
  2296. COPY(import, mi.imports[mi.nofImports]);
  2297. INC(mi.nofImports);
  2298. ELSE
  2299. error := TRUE;
  2300. MakeMessage(message, "Maximum number of supported imports exceeded in module #", filename, "");
  2301. diagnostics.Error(source, Diagnostics.Invalid, Diagnostics.Invalid, message);
  2302. EXIT;
  2303. END;
  2304. SkipComments(in);
  2305. in.Char(ch);
  2306. IF (ch = ",") THEN
  2307. (* continue *)
  2308. ELSIF (ch = ";") THEN
  2309. EXIT;
  2310. ELSE
  2311. error := TRUE;
  2312. MakeMessage(message, "Parsing import section of module # failed", filename, "");
  2313. diagnostics.Error(source, errorPosition, Diagnostics.Invalid, message);
  2314. EXIT;
  2315. END;
  2316. ELSE
  2317. EXIT;
  2318. END;
  2319. END;
  2320. ELSE
  2321. mi.nofImports := 0;
  2322. END;
  2323. END GetImports;
  2324. BEGIN
  2325. ASSERT((in # NIL) & (diagnostics # NIL));
  2326. error := FALSE;
  2327. GetModuleNameAndContext(in, mi.name, mi.context, diagnostics, error);
  2328. IF ~error THEN
  2329. ASSERT(mi.nofImports = 0);
  2330. GetImports(in, mi, diagnostics, error);
  2331. END;
  2332. END GetModuleInfo;
  2333. PROCEDURE VersionToString(major, minor : LONGINT; VAR string : ARRAY OF CHAR);
  2334. VAR temp : ARRAY 16 OF CHAR;
  2335. BEGIN
  2336. Strings.IntToStr(major, string); Strings.Append(string, ".");
  2337. Strings.IntToStr(minor, temp); Strings.Append(string, temp);
  2338. END VersionToString;
  2339. (** SplitName - Split a filename into prefix, middle and suffix, seperated by ".". The prefix may contain dots ("."). *)
  2340. PROCEDURE SplitName(CONST name: ARRAY OF CHAR; VAR pre, mid, suf: ARRAY OF CHAR);
  2341. VAR i, j, d0, d1: LONGINT;
  2342. BEGIN
  2343. i := 0; d0 := -1; d1 := -1;
  2344. WHILE name[i] # 0X DO
  2345. IF name[i] = "." THEN
  2346. d0 := d1;
  2347. d1 := i
  2348. END;
  2349. INC(i)
  2350. END;
  2351. i := 0;
  2352. IF (d0 # -1) & (d1 # d0) THEN (* have prefix *)
  2353. WHILE i # d0 DO pre[i] := name[i]; INC(i) END
  2354. ELSE
  2355. d0 := -1
  2356. END;
  2357. pre[i] := 0X;
  2358. i := d0+1; j := 0;
  2359. WHILE (name[i] # 0X) & (i # d1) DO mid[j] := name[i]; INC(i); INC(j) END;
  2360. mid[j] := 0X; j := 0;
  2361. IF d1 # -1 THEN
  2362. i := d1+1;
  2363. WHILE name[i] # 0X DO suf[j] := name[i]; INC(i); INC(j) END
  2364. END;
  2365. suf[j] := 0X
  2366. END SplitName;
  2367. PROCEDURE CreateContext (VAR name: ARRAY OF CHAR; CONST context: ARRAY OF CHAR);
  2368. VAR temp: Name;
  2369. BEGIN
  2370. IF context # Modules.DefaultContext THEN
  2371. COPY (name, temp);
  2372. COPY (context, name);
  2373. Strings.Append (name, ".");
  2374. Strings.Append (name, temp);
  2375. END;
  2376. END CreateContext;
  2377. PROCEDURE MakeMessage(VAR msg : ARRAY OF CHAR; CONST string, par0, par1 : ARRAY OF CHAR);
  2378. VAR count, m, i, j : LONGINT; par : ARRAY 128 OF CHAR;
  2379. BEGIN
  2380. i := 0; m := 0; j := 0;
  2381. FOR count := 0 TO 3 DO
  2382. WHILE (m < LEN(msg)-1) & (i < LEN(string)) & (string[i] # "#") & (string[i] # 0X) DO
  2383. msg[m] := string[i]; INC(m); INC(i);
  2384. END;
  2385. IF (string[i] = "#") THEN
  2386. INC(i); j := 0;
  2387. IF (count = 0) THEN COPY(par0, par);
  2388. ELSIF (count = 1) THEN COPY(par1, par)
  2389. ELSE par[0] := 0X;
  2390. END;
  2391. WHILE (m < LEN(msg)-1) & (j < LEN(par)) & (par[j] # 0X) DO
  2392. msg[m] := par[j]; INC(m); INC(j);
  2393. END;
  2394. END;
  2395. END;
  2396. msg[m] := 0X;
  2397. END MakeMessage;
  2398. PROCEDURE GetReader(file : File; diagnostics : Diagnostics.Diagnostics) : Streams.Reader;
  2399. VAR
  2400. reader : Streams.Reader;
  2401. fileReader : Files.Reader; ch1, ch2 : CHAR; offset : LONGINT;
  2402. text : Texts.Text; textReader : TextUtilities.TextReader; format, res : LONGINT;
  2403. message : ARRAY 256 OF CHAR;
  2404. BEGIN
  2405. ASSERT((file # NIL) & (diagnostics # NIL));
  2406. reader := NIL;
  2407. IF OptimizedLoads THEN
  2408. IF (file.file = NIL) THEN file.file := Files.Old(file.name); END;
  2409. IF (file.file # NIL) THEN
  2410. Files.OpenReader(fileReader, file.file, 0);
  2411. fileReader.Char(ch1);
  2412. fileReader.Char(ch2);
  2413. IF (ch1= 0F0X) & (ch2 = 01X) THEN (* formatted Oberon text, skip formatting information *)
  2414. fileReader.RawLInt(offset);
  2415. fileReader.SetPos(offset);
  2416. ELSE
  2417. fileReader.SetPos(0);
  2418. END;
  2419. reader := fileReader;
  2420. ELSE
  2421. MakeMessage(message, "Could not open file #", file.name, "");
  2422. diagnostics.Error(file.name, file.pos, Diagnostics.Invalid, message);
  2423. END;
  2424. END;
  2425. IF (reader = NIL) THEN
  2426. NEW(text);
  2427. TextUtilities.LoadAuto(text, file.name, format, res);
  2428. IF (res = 0) THEN
  2429. NEW(textReader, text);
  2430. reader := textReader;
  2431. ELSE
  2432. MakeMessage(message, "Could not open file # (Package = )", file.name, "");
  2433. diagnostics.Error(file.name, file.pos, Diagnostics.Invalid, message);
  2434. END;
  2435. END;
  2436. RETURN reader;
  2437. END GetReader;
  2438. PROCEDURE CallCommand(CONST command, arguments : ARRAY OF CHAR; context : Commands.Context);
  2439. VAR
  2440. newContext : Commands.Context; arg : Streams.StringReader;
  2441. msg : ARRAY 128 OF CHAR; res : LONGINT;
  2442. BEGIN
  2443. NEW(arg, 256); arg.Set(arguments);
  2444. NEW(newContext, NIL, arg, context.out, context.error, context.caller);
  2445. Commands.Activate(command, newContext, {Commands.Wait}, res, msg);
  2446. IF (res #Commands.Ok) THEN
  2447. context.error.String(msg); context.error.Ln;
  2448. END;
  2449. END CallCommand;
  2450. PROCEDURE ParseBuildDescription*(text : Texts.Text; CONST source: ARRAY OF CHAR; VAR builds : Builds; log: Streams.Writer; diagnostics : Diagnostics.Diagnostics) : BOOLEAN;
  2451. VAR
  2452. parser : Parser; scanner : Scanner;
  2453. reader : Streams.StringReader;
  2454. buffer : POINTER TO ARRAY OF CHAR;
  2455. length : LONGINT;
  2456. BEGIN
  2457. ASSERT((text # NIL) & (diagnostics # NIL));
  2458. text.AcquireRead;
  2459. length := text.GetLength();
  2460. text.ReleaseRead;
  2461. IF length = 0 THEN length := 1 END;
  2462. NEW(buffer, length);
  2463. TextUtilities.TextToStr(text, buffer^);
  2464. NEW(reader, LEN(buffer)); reader.SetRaw(buffer^, 0, LEN(buffer));
  2465. NEW(scanner, source, reader, diagnostics);
  2466. NEW(parser, scanner, log, diagnostics);
  2467. RETURN parser.Parse(builds);
  2468. END ParseBuildDescription;
  2469. PROCEDURE ParseBuildFile*(
  2470. CONST filename : Files.FileName;
  2471. VAR builds : Builds;
  2472. log: Streams.Writer;
  2473. diagnostics : Diagnostics.Diagnostics
  2474. ) : BOOLEAN;
  2475. VAR text : Texts.Text; format, res : LONGINT; message : ARRAY 256 OF CHAR;
  2476. BEGIN
  2477. log.String("Loading package description file "); log.String(filename); log.String(" ... ");
  2478. log.Update;
  2479. NEW(text);
  2480. TextUtilities.LoadAuto(text, filename, format, res);
  2481. IF (res = 0) THEN
  2482. IF ParseBuildDescription(text, filename, builds, log, diagnostics) THEN
  2483. log.String("done."); log.Ln;
  2484. RETURN TRUE;
  2485. ELSE
  2486. log.Ln;
  2487. RETURN FALSE;
  2488. END;
  2489. ELSE
  2490. builds := NIL;
  2491. MakeMessage(message, "Could not open file #", filename, "");
  2492. diagnostics.Error("", Diagnostics.Invalid, Diagnostics.Invalid, message);
  2493. RETURN FALSE;
  2494. END;
  2495. END ParseBuildFile;
  2496. PROCEDURE ParseText(
  2497. text : Texts.Text;
  2498. CONST source: ARRAY OF CHAR;
  2499. pos: LONGINT; (* ignore *)
  2500. CONST pc, opt: ARRAY OF CHAR;
  2501. log: Streams.Writer; diagnostics : Diagnostics.Diagnostics; VAR error: BOOLEAN);
  2502. VAR
  2503. options : POINTER TO ARRAY OF CHAR;
  2504. builds : Builds;
  2505. BEGIN
  2506. NEW(options,LEN(opt));
  2507. ASSERT((text # NIL) & (diagnostics # NIL));
  2508. error := ~ParseBuildDescription(text, source, builds, log, diagnostics);
  2509. IF ~error THEN
  2510. COPY(opt, options^);
  2511. Strings.TrimWS(options^);
  2512. IF (builds # NIL) & (options^ = "\check") THEN
  2513. builds.CheckAll(log, diagnostics);
  2514. END;
  2515. END;
  2516. IF error THEN
  2517. log.String(" not done");
  2518. ELSE
  2519. log.String(" done");
  2520. END;
  2521. log.Update;
  2522. END ParseText;
  2523. PROCEDURE CheckBuilds(builds : Builds; nofWorkers : LONGINT; context : Commands.Context; diagnostics : Diagnostics.DiagnosticsList);
  2524. VAR
  2525. build : LONGINT; ignore : Streams.StringWriter; error, ignoreError, checkAll : BOOLEAN;
  2526. PROCEDURE CreateRamDisk(context : Commands.Context) : BOOLEAN;
  2527. VAR res : LONGINT; msg : ARRAY 128 OF CHAR; fs : Files.FileSystem;
  2528. BEGIN
  2529. Commands.Call("FSTools.Mount CHECKBUILDS RamFS 500000 4096", {Commands.Wait}, res, msg);
  2530. IF (res # Commands.Ok) THEN
  2531. context.error.String(msg); context.error.Ln;
  2532. END;
  2533. fs := Files.This("CHECKBUILDS");
  2534. RETURN fs # NIL;
  2535. END CreateRamDisk;
  2536. PROCEDURE UnmountRamDisk(context : Commands.Context);
  2537. VAR res : LONGINT; msg : ARRAY 128 OF CHAR;
  2538. BEGIN
  2539. Commands.Call("FSTools.Unmount CHECKBUILDS", {Commands.Wait}, res, msg);
  2540. IF (res # Commands.Ok) THEN
  2541. context.error.String(msg); context.error.Ln;
  2542. END;
  2543. END UnmountRamDisk;
  2544. PROCEDURE DeleteFiles(CONST pattern : ARRAY OF CHAR);
  2545. VAR
  2546. enum : Files.Enumerator; flags : SET; time, date, size : LONGINT; name : Files.FileName;
  2547. res : LONGINT;
  2548. BEGIN
  2549. NEW(enum); enum.Open(pattern, {});
  2550. WHILE enum.GetEntry(name, flags, time, date, size) DO
  2551. Files.Delete(name, res);
  2552. END;
  2553. enum.Close;
  2554. END DeleteFiles;
  2555. PROCEDURE DoCheckAll(builds : Builds) : BOOLEAN;
  2556. VAR i : LONGINT; all : BOOLEAN;
  2557. BEGIN
  2558. ASSERT(builds # NIL);
  2559. all := TRUE;
  2560. WHILE all & (i < LEN(builds.builds)) & (builds.builds[i] # NIL) DO
  2561. all := builds.builds[i].marked;
  2562. INC(i);
  2563. END;
  2564. RETURN all;
  2565. END DoCheckAll;
  2566. BEGIN
  2567. ASSERT((builds # NIL) & (context # NIL) &(diagnostics # NIL));
  2568. checkAll := DoCheckAll(builds);
  2569. IF checkAll THEN
  2570. builds.CheckAll(context.out, diagnostics);
  2571. END;
  2572. IF (diagnostics.nofErrors = 0) THEN
  2573. IF CreateRamDisk(context) THEN
  2574. IF (diagnostics.nofErrors = 0) THEN
  2575. NEW(ignore, 1);
  2576. WHILE (build < LEN(builds.builds)) & (builds.builds[build] # NIL) DO
  2577. diagnostics.Reset;
  2578. IF ~checkAll OR ~builds.builds[build].disabled THEN
  2579. error := FALSE;
  2580. IF ~checkAll & builds.builds[build].marked THEN
  2581. builds.builds[build].CheckFiles(diagnostics);
  2582. builds.builds[build].CheckModules(diagnostics, error);
  2583. END;
  2584. IF ~error & builds.builds[build].marked THEN
  2585. COPY("CHECKBUILDS:", builds.builds[build].path);
  2586. (*
  2587. COPY("Obt", builds.builds[build].extension);
  2588. COPY("386", builds.builds[build].target);
  2589. *)
  2590. builds.builds[build].Compile(nofWorkers, context.out, context.error, FALSE, diagnostics, ignoreError);
  2591. DeleteFiles("CHECKBUILDS:*.Obw");
  2592. DeleteFiles("CHECKBUILDS:*.Obt");
  2593. DeleteFiles("CHECKBUILDS:*.Obx");
  2594. DeleteFiles("CHECKBUILDS:*.Sym");
  2595. DeleteFiles("CHECKBUILDS:*.Gof");
  2596. ELSE
  2597. diagnostics.ToStream(context.out, Diagnostics.All);
  2598. END;
  2599. ELSE
  2600. context.out.String("Build "); context.out.String(builds.builds[build].name);
  2601. context.out.String(" is disabled."); context.out.Ln;
  2602. END;
  2603. context.out.Update; context.error.Update;
  2604. INC(build);
  2605. END;
  2606. diagnostics.Reset; (* all error messages already shown *)
  2607. ELSE
  2608. diagnostics.ToStream(context.error, Diagnostics.All); context.error.Ln;
  2609. END;
  2610. UnmountRamDisk(context);
  2611. ELSE
  2612. context.error.String("Could not create RAM disk"); context.error.Ln;
  2613. END;
  2614. ELSE
  2615. diagnostics.ToStream(context.out, Diagnostics.All);
  2616. END;
  2617. END CheckBuilds;
  2618. (** Find files that match the filemask but are not listed in the build description file *)
  2619. PROCEDURE CheckFiles*(context : Commands.Context); (** [Options] filemask ~*)
  2620. VAR
  2621. options : Options.Options; diagnostics : Diagnostics.Diagnostics;
  2622. builds : Builds;
  2623. buildFile, mask, fullname, path, filename : Files.FileName;
  2624. enumerator : Files.Enumerator;
  2625. flags : SET;
  2626. ignore : LONGINT;
  2627. BEGIN
  2628. NEW(options);
  2629. options.Add("f", "file", Options.String);
  2630. IF options.Parse(context.arg, context.error) THEN
  2631. COPY(DefaultPackagesFile, buildFile);
  2632. IF options.GetString("file", buildFile) THEN END;
  2633. IF context.arg.GetString(mask) THEN
  2634. NEW(diagnostics);
  2635. IF ParseBuildFile(buildFile, builds, context.out, diagnostics) THEN
  2636. NEW(enumerator); enumerator.Open(mask, {});
  2637. WHILE enumerator.GetEntry(fullname, flags, ignore, ignore, ignore) DO
  2638. IF ~(Files.Directory IN flags) THEN
  2639. Files.SplitPath(fullname, path, filename);
  2640. IF (builds.FindFile(filename) = NIL) THEN
  2641. context.out.String(filename); context.out.Ln;
  2642. END;
  2643. END;
  2644. END;
  2645. enumerator.Close;
  2646. END;
  2647. ELSE
  2648. context.error.String("Expected mask argument."); context.error.Ln;
  2649. END;
  2650. END;
  2651. END CheckFiles;
  2652. (** Find the text position where the specified file could be inserted into the build description file *)
  2653. PROCEDURE FindPosition*(context : Commands.Context); (** [Options] buildname modulename ~ *)
  2654. VAR
  2655. builds : Builds; build : BuildObj;
  2656. buildFile, filename : Files.FileName; buildName : Name;
  2657. diagnostics : Diagnostics.DiagnosticsList;
  2658. position : LONGINT;
  2659. options : Options.Options;
  2660. BEGIN
  2661. NEW(options);
  2662. options.Add("f", "file", Options.String);
  2663. IF options.Parse(context.arg, context.error) THEN
  2664. COPY(DefaultPackagesFile, buildFile);
  2665. IF options.GetString("file", buildFile) THEN END;
  2666. context.arg.SkipWhitespace; context.arg.String(buildName);
  2667. context.arg.SkipWhitespace; context.arg.String(filename);
  2668. IF (buildName # "") & (filename # "") THEN
  2669. NEW(diagnostics);
  2670. IF ParseBuildFile(buildFile, builds, context.out, diagnostics) THEN
  2671. build := builds.GetBuild(buildName);
  2672. IF (build # NIL) THEN
  2673. position := build.FindPosition(filename, diagnostics);
  2674. IF (position # -1) THEN
  2675. context.out.String("First text position where the file "); context.out.String(filename);
  2676. context.out.String(" could be inserted: "); context.out.Int(position, 0); context.out.Ln;
  2677. ELSE
  2678. diagnostics.ToStream(context.error, {0..31}); context.error.Ln;
  2679. END;
  2680. ELSE
  2681. context.error.String("Build "); context.error.String(buildName); context.error.String(" not found");
  2682. context.error.Update;
  2683. END;
  2684. ELSE
  2685. diagnostics.ToStream(context.error, {0..31}); context.error.Ln;
  2686. END;
  2687. ELSE
  2688. context.error.String("Usage: Release.FindPosition Release.Tool [options] buildname filename ~ ");
  2689. END;
  2690. END;
  2691. END FindPosition;
  2692. (** Analyze the builds *)
  2693. PROCEDURE Analyze*(context : Commands.Context); (** [Options] ~ *)
  2694. VAR filename : Files.FileName; builds : Builds; diagnostics : Diagnostics.DiagnosticsList; options : Options.Options;
  2695. BEGIN
  2696. NEW(options);
  2697. options.Add("d", "details", Options.Flag);
  2698. options.Add("f", "file", Options.String);
  2699. IF options.Parse(context.arg, context.error) THEN
  2700. COPY(DefaultPackagesFile, filename);
  2701. IF options.GetString("file", filename) THEN END;
  2702. IF (filename # "") THEN
  2703. NEW(diagnostics);
  2704. IF ParseBuildFile(filename, builds, context.out, diagnostics) THEN
  2705. builds.Show(context.out, options.GetFlag("details"));
  2706. ELSE
  2707. diagnostics.ToStream(context.error, Diagnostics.All); context.error.Ln;
  2708. END;
  2709. ELSE
  2710. context.error.String("Usage: Release.Analyze [options] ~"); context.error.Ln;
  2711. END;
  2712. END;
  2713. END Analyze;
  2714. (** Build specified build (or all of none specified) to a RAM disk *)
  2715. PROCEDURE Check*(context : Commands.Context); (** [Options] {buildname} ~ *)
  2716. VAR
  2717. filename : Files.FileName; builds : Builds; diagnostics : Diagnostics.DiagnosticsList;
  2718. options : Options.Options; nofWorkers : LONGINT;
  2719. PROCEDURE MarkBuilds(context : Commands.Context; builds : Builds) : BOOLEAN;
  2720. VAR build : BuildObj; name : Name; nofMarked, i : LONGINT; error : BOOLEAN;
  2721. BEGIN
  2722. ASSERT((context # NIL) & (builds # NIL));
  2723. nofMarked := 0;
  2724. REPEAT
  2725. name := "";
  2726. context.arg.SkipWhitespace; context.arg.String(name);
  2727. Strings.TrimWS(name);
  2728. IF (name # "") THEN
  2729. build := builds.GetBuild(name);
  2730. IF (build # NIL) THEN
  2731. INC(nofMarked);
  2732. build.marked := TRUE;
  2733. ELSE
  2734. error := TRUE;
  2735. context.error.String("Build "); context.error.String(name);
  2736. context.error.String(" not found."); context.error.Ln;
  2737. END;
  2738. END;
  2739. UNTIL (name = "");
  2740. IF ~error & (nofMarked = 0) THEN
  2741. FOR i := 0 TO LEN(builds.builds) - 1 DO
  2742. IF (builds.builds[i] # NIL) THEN builds.builds[i].marked := TRUE; END;
  2743. END;
  2744. END;
  2745. RETURN ~error;
  2746. END MarkBuilds;
  2747. BEGIN
  2748. NEW(options);
  2749. options.Add("f", "file", Options.String);
  2750. IF options.Parse(context.arg, context.error) THEN
  2751. COPY(DefaultPackagesFile, filename);
  2752. IF options.GetString("file", filename) THEN END;
  2753. IF (filename # "") THEN
  2754. NEW(diagnostics);
  2755. IF ParseBuildFile(filename, builds, context.out, diagnostics) THEN
  2756. IF MarkBuilds(context, builds) THEN
  2757. IF ~options.GetInteger("workers", nofWorkers) THEN nofWorkers := 0; END;
  2758. CheckBuilds(builds, nofWorkers, context, diagnostics);
  2759. END;
  2760. ELSE
  2761. diagnostics.ToStream(context.error, Diagnostics.All); context.error.Ln;
  2762. END;
  2763. ELSE
  2764. context.error.String('Usage: Release.Check [options] ~'); context.error.Ln;
  2765. END;
  2766. END;
  2767. END Check;
  2768. PROCEDURE CheckDiagnostics(diagnostics : Diagnostics.DiagnosticsList; warnings: BOOLEAN; out : Streams.Writer) : BOOLEAN;
  2769. BEGIN
  2770. ASSERT((diagnostics # NIL) & (out # NIL));
  2771. IF (diagnostics.nofErrors = 0) & (diagnostics.nofMessages > 0) THEN
  2772. IF warnings THEN diagnostics.ToStream(out, Diagnostics.All)
  2773. ELSE diagnostics.ToStream(out, {Diagnostics.TypeInformation, Diagnostics.TypeError})
  2774. END;
  2775. out.Update;
  2776. diagnostics.Reset;
  2777. END;
  2778. RETURN diagnostics.nofErrors = 0;
  2779. END CheckDiagnostics;
  2780. PROCEDURE ImportInformation(mode : LONGINT; context : Commands.Context);
  2781. VAR
  2782. options : Options.Options; diagnostics : Diagnostics.DiagnosticsList;
  2783. builds : Builds; build : BuildObj;
  2784. filename : Files.FileName;
  2785. modulename : Modules.Name;
  2786. buildname : Name;
  2787. error : BOOLEAN;
  2788. BEGIN
  2789. NEW(options);
  2790. options.Add("f", "file", Options.String);
  2791. options.Add(0X, "exclude", Options.String);
  2792. IF options.Parse(context.arg, context.out) THEN
  2793. IF ~options.GetString("file", filename) THEN COPY(DefaultPackagesFile, filename); END;
  2794. modulename := "";
  2795. context.arg.SkipWhitespace; context.arg.String(buildname);
  2796. context.arg.SkipWhitespace; context.arg.String(modulename);
  2797. IF (modulename # "") THEN
  2798. NEW(diagnostics);
  2799. IF ParseBuildFile(filename, builds, context.out, diagnostics) THEN
  2800. IF CheckDiagnostics(diagnostics, FALSE, context.out) THEN
  2801. build := builds.GetBuild(buildname);
  2802. IF (build # NIL) THEN
  2803. build.SetOptions(options);
  2804. build.DoChecks(context.out, diagnostics, error);
  2805. IF ~error THEN
  2806. build.AnalyzeDependencies(context.out);
  2807. build.ShowDependentModules(modulename, mode, context.out);
  2808. context.out.Ln; context.out.Update;
  2809. END;
  2810. ELSE
  2811. context.out.String("Build "); context.out.String(buildname); context.out.String(" not found");
  2812. context.out.Ln;
  2813. END;
  2814. END;
  2815. END;
  2816. IF (diagnostics.nofMessages > 0) THEN
  2817. diagnostics.ToStream(context.out, Diagnostics.All);
  2818. END;
  2819. ELSE
  2820. context.out.String("Usage: Release.WhoImports [options] buildname modulename ~"); context.out.Ln;
  2821. END;
  2822. END;
  2823. END ImportInformation;
  2824. PROCEDURE WhoImports*(context : Commands.Context);
  2825. BEGIN
  2826. ImportInformation(Mode_ShowImporting, context);
  2827. END WhoImports;
  2828. PROCEDURE RequiredModules*(context : Commands.Context);
  2829. BEGIN
  2830. ImportInformation(Mode_ShowImported, context);
  2831. END RequiredModules;
  2832. PROCEDURE Rebuild*(context : Commands.Context); (** [Options] buildname {filenames} ~ *)
  2833. VAR
  2834. options : Options.Options;
  2835. diagnostics : Diagnostics.DiagnosticsList;
  2836. builds : Builds; build : BuildObj;
  2837. packagename, filename, fullname : Files.FileName;
  2838. nofNewMarks, nofMarks : LONGINT;
  2839. inBuild : BOOLEAN;
  2840. buildname : ARRAY 32 OF CHAR;
  2841. start0 : Dates.DateTime;
  2842. error : BOOLEAN;
  2843. nofWorkers, res : LONGINT;
  2844. BEGIN
  2845. NEW(options);
  2846. options.Add("b", "build", Options.Flag);
  2847. options.Add("c", "compiler", Options.String);
  2848. options.Add("e", "extension", Options.String);
  2849. options.Add("f", "file", Options.String);
  2850. options.Add("o", "options", Options.String);
  2851. options.Add("p", "path", Options.String);
  2852. options.Add("t", "target", Options.String);
  2853. options.Add("v", "verbose", Options.Flag);
  2854. options.Add("w", "workers", Options.Integer);
  2855. options.Add("y", "", Options.Flag);
  2856. IF options.Parse(context.arg, context.error) THEN
  2857. COPY(DefaultPackagesFile, packagename);
  2858. IF options.GetString("file", packagename) THEN END;
  2859. context.arg.SkipWhitespace; context.arg.String(buildname);
  2860. IF (packagename # "") & (buildname # "") THEN
  2861. start0 := Dates.Now();
  2862. NEW(diagnostics);
  2863. IF ParseBuildFile(packagename, builds, context.out, diagnostics) THEN
  2864. build := builds.GetBuild(buildname);
  2865. IF (build # NIL) THEN
  2866. IF build.disabled THEN
  2867. context.out.String("Warning: Build "); context.out.String(build.name);
  2868. context.out.String(" is disabled."); context.out.Ln;
  2869. context.out.Update;
  2870. END;
  2871. build.SetOptions(options);
  2872. build.DoChecks(context.out, diagnostics, error);
  2873. IF CheckDiagnostics(diagnostics, FALSE, context.out) & ~error THEN
  2874. build.AnalyzeDependencies(context.out);
  2875. build.ClearMarks;
  2876. filename := ""; error := FALSE; nofMarks := 0;
  2877. LOOP
  2878. context.arg.SkipWhitespace; context.arg.String(filename);
  2879. IF (filename = "") THEN EXIT; END;
  2880. IF (Files.Old(filename) # NIL) THEN
  2881. build.MarkFiles(filename, inBuild, nofNewMarks);
  2882. IF inBuild THEN
  2883. nofMarks := nofMarks + nofNewMarks;
  2884. ELSE
  2885. context.out.String("Warning: No depenencies on file "); context.out.String(filename);
  2886. context.out.String("."); context.out.Ln; context.out.Update;
  2887. END;
  2888. ELSE
  2889. context.out.String("Error: File "); context.out.String(filename);
  2890. context.out.String(" does not exist."); context.out.Ln; context.out.Update;
  2891. error := TRUE;
  2892. END;
  2893. END;
  2894. IF ~error THEN
  2895. context.out.Int(nofMarks, 0); context.out.String(" files selected for compilation."); context.out.Ln;
  2896. context.out.Update;
  2897. IF ~options.GetString("path", fullname) THEN fullname := ""; END;
  2898. Strings.Append(fullname, ToolFilename);
  2899. context.out.String("Writing release file to "); context.out.String(fullname);
  2900. context.out.String(" ... "); context.out.Update;
  2901. build.GenerateToolFile(fullname, res);
  2902. IF (res = 0) THEN
  2903. context.out.String("done."); context.out.Ln; context.out.Update;
  2904. IF options.GetFlag("build") THEN
  2905. IF ~options.GetInteger("workers", nofWorkers) THEN nofWorkers := 0; END;
  2906. build.Compile(nofWorkers, context.out, context.error, options.GetFlag("verbose"), diagnostics, error);
  2907. ELSE
  2908. CallCommand("Notepad.Open", fullname, context);
  2909. END;
  2910. ELSE
  2911. IF ~options.GetFlag("build") THEN
  2912. CallCommand("Notepad.Open", fullname, context);
  2913. END;
  2914. context.out.String("error, res: "); context.out.Int(res, 0); context.out.Ln;
  2915. END;
  2916. END;
  2917. END;
  2918. ELSE
  2919. context.error.String("Build "); context.error.String(buildname); context.error.String(" not found."); context.error.Ln;
  2920. END;
  2921. END;
  2922. IF (diagnostics.nofMessages > 0) THEN
  2923. diagnostics.ToStream(context.out, Diagnostics.All);
  2924. context.out.Ln;
  2925. END;
  2926. ELSE
  2927. context.error.String('Usage: Release.ReBuild [options] BuildName {filenames}');
  2928. context.error.Ln;
  2929. END;
  2930. END;
  2931. END Rebuild;
  2932. (** Build the specified build *)
  2933. PROCEDURE Build*(context : Commands.Context); (** [Options] buildname ~ *)
  2934. VAR
  2935. filename, fullname : Files.FileName;
  2936. builds : Builds; build : BuildObj;
  2937. diagnostics : Diagnostics.DiagnosticsList;
  2938. options : Options.Options;
  2939. buildname : ARRAY 32 OF CHAR;
  2940. start0 : Dates.DateTime;
  2941. error : BOOLEAN;
  2942. nofWorkers, res : LONGINT;
  2943. BEGIN
  2944. NEW(options);
  2945. options.Add("b", "build", Options.Flag);
  2946. options.Add("c", "compiler", Options.String);
  2947. options.Add(0X, "exclude", Options.String);
  2948. options.Add(0X, "only", Options.String);
  2949. options.Add("e", "extension", Options.String);
  2950. options.Add("f", "file", Options.String);
  2951. options.Add("l", "link", Options.Flag);
  2952. options.Add("n", "nocheck", Options.Flag);
  2953. options.Add("o", "options", Options.String);
  2954. options.Add("p", "path", Options.String);
  2955. options.Add("s", "symbolFileExtension", Options.String);
  2956. options.Add("t", "target", Options.String);
  2957. options.Add("v", "verbose", Options.Flag);
  2958. options.Add(0X, "workers", Options.Integer);
  2959. options.Add("x", "xml", Options.Flag);
  2960. options.Add("y", "", Options.Flag);
  2961. options.Add("z", "zip", Options.Flag);
  2962. options.Add("w","warnings",Options.Flag);
  2963. IF options.Parse(context.arg, context.error) THEN
  2964. COPY(DefaultPackagesFile, filename);
  2965. IF options.GetString("file", filename) THEN END;
  2966. context.arg.SkipWhitespace; context.arg.String(buildname);
  2967. IF (filename # "") & (buildname # "") THEN
  2968. start0 := Dates.Now();
  2969. NEW(diagnostics);
  2970. IF ParseBuildFile(filename, builds, context.out, diagnostics) THEN
  2971. build := builds.GetBuild(buildname);
  2972. IF (build # NIL) THEN
  2973. IF build.disabled THEN
  2974. context.out.String("Warning: Build "); context.out.String(build.name);
  2975. context.out.String(" is disabled."); context.out.Ln;
  2976. context.out.Update;
  2977. END;
  2978. build.SetOptions(options);
  2979. IF ~options.GetInteger("workers", nofWorkers) THEN nofWorkers := 0; END;
  2980. IF ~options.GetFlag("nocheck") THEN
  2981. build.DoChecks(context.out, diagnostics, error);
  2982. ELSIF (nofWorkers > 0) THEN
  2983. context.error.String("Incompatible options: nocheck cannot be combined with workers");
  2984. context.error.Ln; context.error.Update;
  2985. RETURN;
  2986. END;
  2987. IF CheckDiagnostics(diagnostics, options.GetFlag("warnings"), context.out) & ~error THEN
  2988. IF ~options.GetString("path", fullname) THEN fullname := ""; END;
  2989. Strings.Append(fullname, ToolFilename);
  2990. context.out.String("Writing release file to "); context.out.String(fullname);
  2991. context.out.String(" ... "); context.out.Update;
  2992. build.GenerateToolFile(fullname, res);
  2993. IF (res = 0) THEN
  2994. context.out.String("done."); context.out.Ln; context.out.Update;
  2995. IF options.GetFlag("build") THEN
  2996. IF options.GetFlag("link") THEN
  2997. context.error.String("Incompatible options: link cannot automatically build");
  2998. context.error.Ln; context.error.Update;
  2999. RETURN;
  3000. END;
  3001. build.Compile(nofWorkers, context.out, context.error, options.GetFlag("verbose"), diagnostics, error);
  3002. ELSIF ~options.GetFlag("zip") THEN
  3003. CallCommand("Notepad.Open", fullname, context);
  3004. END;
  3005. IF ~error & options.GetFlag("zip") & CheckDiagnostics(diagnostics, options.GetFlag("warnings"), context.out) THEN
  3006. build.GenerateZipFiles(context.out, context.error, diagnostics, error);
  3007. END;
  3008. IF ~error & options.GetFlag("xml") THEN
  3009. IF ~options.GetString("path", fullname) THEN fullname := ""; END;
  3010. Strings.Append(fullname, InstallerPackageFile);
  3011. context.out.String("Writing XML package description to "); context.out.String(fullname);
  3012. context.out.String(" ... "); context.out.Update;
  3013. build.GeneratePackageFile(InstallerPackageFile, res);
  3014. IF (res = Files.Ok) THEN
  3015. context.out.String("done.");
  3016. ELSE
  3017. context.out.String("error, res: "); context.out.Int(res, 0);
  3018. END;
  3019. context.out.Ln;
  3020. END;
  3021. ELSE
  3022. IF ~options.GetFlag("build") THEN
  3023. CallCommand("Notepad.Open", fullname, context);
  3024. END;
  3025. context.out.String("error, res: "); context.out.Int(res, 0); context.out.Ln;
  3026. END;
  3027. END;
  3028. ELSE
  3029. context.error.String("Build "); context.error.String(buildname); context.error.String(" not found."); context.error.Ln;
  3030. END;
  3031. END;
  3032. IF (diagnostics.nofMessages > 0) THEN
  3033. IF options.GetFlag("warnings") THEN
  3034. diagnostics.ToStream(context.out, Diagnostics.All);
  3035. ELSE
  3036. diagnostics.ToStream(context.out, {Diagnostics.TypeError, Diagnostics.TypeInformation});
  3037. END;
  3038. context.out.Ln;
  3039. END;
  3040. ELSE
  3041. context.error.String('Usage: Release.Build [options] BuildName');
  3042. context.error.Ln;
  3043. END;
  3044. END;
  3045. END Build;
  3046. PROCEDURE Cleanup;
  3047. BEGIN
  3048. CompilerInterface.Unregister("ReleaseTool");
  3049. END Cleanup;
  3050. BEGIN
  3051. CompilerInterface.Register("ReleaseTool", "Parse release description file", "Tool", ParseText);
  3052. Modules.InstallTermHandler(Cleanup);
  3053. END Release.
  3054. SystemTools.Free Release ~
  3055. Release.FindPosition WinAos Usbdi.Mod ~
  3056. Release.Analyze --details ~
  3057. Release.Check ~
  3058. Release.Build A2 ~
  3059. Release.Build --path="../Test/" -b A2 ~
  3060. Release.Build --path="../ObjT1/" -bn WinAos ~
  3061. Release.Build --path="../TestE/" eWinAos ~
  3062. Release.Build --path="../Test/" --xml A2 ~
  3063. Release.CheckFiles -f=C2Release.Tool ../../CF/trunk/source/*.* ~
  3064. Release.Build --path="../Test/" -z A2 ~
  3065. Release.RequiredModules A2 WMTextView ~
  3066. Release.WhoImports WinAos WMComponents ~
  3067. Release.Tool ~
  3068. Release.Build --path="../Test" -b -x="Build Oberon Contributions " A2 ~
  3069. Release.Rebuild -b WinAos WMTextView.Mod ~
  3070. Release.Build -b WinAos ~