Release.Mod 111 KB

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