Release.Mod 107 KB

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