Release.Mod 105 KB

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