123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402 |
- MODULE Release; (** AUTHOR "staubesv"; PURPOSE "Release build tool"; *)
- (**
- *
- * Usage:
- *
- * Release.Analyze [Options] ~ analyzes the package description file
- *
- * Option f, file : Package description file to be analyzed [default: Release.Tool]
- *
- * Release.CheckFiles [Options] filemask ~ lists all files that match to filemask but are not listed in the package description file
- *
- * Option f, file : Package description file to be analyzed [default: Release.Tool]
- *
- * Release.Check [Options] ~ builds all builds described in the package description file to a RAM disk
- *
- * Option f, file : Specify the package description file [default: Release.Tool]
- *
- * Release.Build [Options] BuildName ~
- *
- * Option b, build : Compile the specified build (otherwise, open Notepad with compile command)
- * Option c,compiler : Override compiler specified in the package description file
- * Option exclude : Exclude packages (specified as space separated list of package names)
- * Option e, extension : Override the extension option specified in the package description file
- * Option f, file : Specify the package description file [default: Release.Tool]
- * Option n, nocheck : Skip file existence and import order checks
- * Option o, options : Override the compiler options specified in the package description file
- * Option only : Only compile the packages provided in a list. No other effect applies
- i.e. packages are fully taken into account otherwise.
- if packages need to be excluded, use exclude instead
- * Option p, path : Override the path option specified in the package description file
- * Option s, symbolFileExtension : Ovveride the symbolFileExtension option specified in the package description file
- * Option t, target : Override the target option specified in the package description file
- * Option v, verbose : Show compiler output (only useful in combination with option --build)
- * Option w, workers : Number of worker threads for parallel compilation
- * Option x, xml : Generate XML package description file needed by WMInstaller.Mod
- * Option z, zip : Generate ZIP file for each package (for now, only together with --build option)
- *
- * Argument BuildName: Name of build to be built
- *
- * Release.FindPosition [Options] BuildName FileName ~ finds the first position in the package description file
- * where the specified file could be inserted (compile order)
- *
- * Option f, file : Specify the package description file [default: Release.Tool]
- *
- *
- * SystemTools.Free Release ~
- *
- * Based on Release.Mod by pjm
- *
- *
- * BuildDescription = Header Import BuildSection Packages
- * BuildSection = BUILDS {Build} END
- * Build = buildName OPENSECTION {BuildParameter} CLOSESECTION
- * Build = INCLUDE '"' [buildprefix {" " buildprefix} ] '"' |
- * COMPILER '"' compileCommand '"' | COMPILEROPTIONS '"' compileOptions '"' |
- * LINKER '"' linkCommand '"' | LINKEROPTIONS '"' linkOptions '"' |
- * TARGET '"' target '"' |
- * EXTENSION '"' objectFileExtension '"' | PATH '"' objectFilePath '"' |
- * EXCLUDEPACKAGES '"' [ exludedPackage {" " excludedPackage} ] '"'
- * DISABLED "TRUE" | "FALSE"
- *
- * Packages = { PackageSpec FileList }
- * PackageSpec = PACKAGE packageName ARCHIVE ArchiveName SOURCE SourceName ENDSECTION
- * FileList = { Filename | ReleaseSpec OPENSECTION [ {filename } ] CLOSESECTION }
- * ReleaseSpec = ReleaseName [ { SEPARATOR ReleaseName } ]
- *)
- IMPORT
- Modules, Streams, Commands, Options, Files, Dates, Strings, Texts, TextUtilities, ReleaseThreadPool, Diagnostics, WMGraphics, Zip,
- CompilerInterface, Compiler, SyntaxTree := FoxSyntaxTree ;
- CONST
- VersionMajor = 1;
- VersionMinor = 0;
- DefaultPackagesFile = "Release.Tool";
- (* Default build settings *)
- DefaultCompiler = "Compiler.Compile";
- DefaultCompileOptions = "";
- DefaultTarget = "" (* default chosen by compiler *);
- DefaultExtension = ""; (* default chosen by compiler *)
- DefaultSymbolFileExtension = ""; (* default chosen by compiler *)
- DefaultPath = "";
- DefaultDisabled = FALSE;
- (** If the prefix of a filename Prefix.Mid.Suffix matches <ReleasePrefix>, both Prefix.Mid.Suffix and Mid.Suffix will be
- included in the archive package. This is only valid for files that do not have the extension (Suffix) Mod *)
- ReleasePrefix = "Release";
- ToolFilename = "CompileCommand.Tool";
- InstallerPackageFile = "InstallerPackages.XML";
- DateTimeFormat = "wwww, mmmm d, yyyy hh:nn:ss";
- NoFile = -1;
- NoPackages = -2;
- (* Load Oberon text files ignoring text format *)
- OptimizedLoads = TRUE;
- (* If set to TRUE, the file references are re-used for different steps of the build process *)
- KeepFilesOpen = FALSE;
- (* File.flags *)
- ImportsSystem = 0;
- SourceCode = 1;
- HasReleasePrefix = 2; (* filename has prefix <ReleasePrefix> *)
- (* Package.installMode *)
- Undefined = 0;
- Required = 1; (* package must be installed *)
- Yes = 2; (* per default, install package *)
- No = 3; (* per default, don't install package *)
- (* File.release*)
- MaxBuilds = 128;
- MaxPrefixes = 128;
- MaxNofImports = 128; (* Maximum number of entries in a module import section *)
- Tab = 9X;
- Mode_ShowImported = 0;
- Mode_ShowImporting = 1;
- SetSize = MAX(SET)+1;
- TYPE
- Name = ARRAY 72 OF CHAR;
- Set = ARRAY (MaxPrefixes-1) DIV SetSize + 1 OF SET;
- TYPE
- Statistic = RECORD
- nofFiles : LONGINT;
- nofSources : LONGINT;
- END;
- Statistics = OBJECT
- VAR
- stats : ARRAY MaxPrefixes OF Statistic;
- (* Total number of files and sources *)
- nofFiles : LONGINT;
- nofSources : LONGINT;
- (* Number of files and sources that are contained in all releases *)
- nofFilesAll : LONGINT;
- nofSourcesAll : LONGINT;
- PROCEDURE Get(VAR nofFiles, nofSources : LONGINT; CONST release : Set);
- VAR i : LONGINT;
- BEGIN
- nofFiles := 0; nofSources := 0;
- FOR i := 0 TO MaxPrefixes-1 DO
- IF Has(release, i) THEN
- nofFiles := nofFiles + stats[i].nofFiles;
- nofSources := nofSources + stats[i].nofSources;
- END;
- END;
- END Get;
- PROCEDURE AddFile(file : File);
- VAR i : LONGINT;
- BEGIN
- INC(nofFiles);
- IF file.IsSourceCode() THEN INC(nofSources); END;
- IF ~IsAll(file.release) THEN
- FOR i := 0 TO MaxPrefixes-1 DO
- IF Has(file.release, i) THEN
- INC(stats[i].nofFiles);
- IF file.IsSourceCode() THEN INC(stats[i].nofSources); END;
- END;
- END;
- ELSE
- INC(nofFilesAll);
- IF file.IsSourceCode() THEN INC(nofSourcesAll); END;
- END;
- END AddFile;
- PROCEDURE &Reset;
- VAR i : LONGINT;
- BEGIN
- nofFiles := 0;
- nofSources := 0;
- FOR i := 0 TO LEN(stats)-1 DO
- stats[i].nofFiles := 0;
- stats[i].nofSources := 0;
- END;
- END Reset;
- END Statistics;
- TYPE
- (* The bitmap is used to analyze dependencies between modules *)
- Bitmap = OBJECT
- VAR
- map : POINTER TO ARRAY OF SET;
- size : LONGINT;
- PROCEDURE IsSet(bit : LONGINT) : BOOLEAN;
- BEGIN
- ASSERT(( 0 <= bit) & (bit < size));
- RETURN (bit MOD SIZEOF(SET)) IN map[bit DIV SIZEOF(SET)];
- END IsSet;
- PROCEDURE Set(bit : LONGINT);
- BEGIN
- ASSERT((0 <= bit) & (bit < size));
- INCL(map[bit DIV SIZEOF(SET)], bit MOD SIZEOF(SET));
- END Set;
- PROCEDURE NofBitsSet() : LONGINT;
- VAR nofBitsSet, index, subindex : LONGINT;
- BEGIN
- nofBitsSet := 0;
- FOR index := 0 TO LEN(map)-1 DO
- FOR subindex := 0 TO SIZEOF(SET)-1 DO
- IF subindex IN map[index] THEN INC(nofBitsSet); END;
- END;
- END;
- RETURN nofBitsSet;
- END NofBitsSet;
- PROCEDURE Union(bitmap : Bitmap);
- VAR i : LONGINT;
- BEGIN
- ASSERT((bitmap # NIL) & (bitmap.size = size));
- FOR i := 0 TO LEN(map)-1 DO
- map[i] := map[i] + bitmap.map[i];
- END;
- END Union;
- PROCEDURE &Init(size : LONGINT);
- VAR i : LONGINT;
- BEGIN
- ASSERT(size > 0);
- SELF.size := size;
- NEW(map, (size + SIZEOF(SET)-1) DIV SIZEOF(SET));
- FOR i := 0 TO LEN(map)-1 DO map[i] := {}; END;
- END Init;
- END Bitmap;
- TYPE
- Package* = OBJECT
- VAR
- name-, archive-, source- : ARRAY 32 OF CHAR;
- description- : ARRAY 256 OF CHAR;
- installMode : LONGINT; (* Required, Yes, No *)
- nofFiles- : LONGINT;
- nofSources- : LONGINT;
- position- : LONGINT;
- next : Package;
- PROCEDURE &Init(CONST name, archive, source, description : ARRAY OF CHAR; position : LONGINT);
- BEGIN
- COPY(name, SELF.name);
- COPY(archive, SELF.archive);
- COPY(source, SELF.source);
- COPY(description, SELF.description);
- SELF.position := position;
- installMode := Undefined;
- nofFiles := 0;
- nofSources := 0;
- position := -1;
- next := NIL;
- END Init;
- END Package;
- TYPE
- PackageArray* = POINTER TO ARRAY OF Package;
- PackageList* = OBJECT
- VAR
- head, tail : Package;
- nofPackages : LONGINT;
- (* Find the package with the specified name. Returns NIL if package not found *)
- PROCEDURE FindPackage(CONST name : ARRAY OF CHAR) : Package;
- VAR package : Package;
- BEGIN
- package := head.next;
- WHILE (package # NIL) & (package.name # name) DO package := package.next; END;
- RETURN package;
- END FindPackage;
- (* Add package to list. Returns FALSE if a package in the list has the same name *)
- PROCEDURE Add(package : Package) : BOOLEAN;
- BEGIN
- ASSERT((package # NIL) & (package.next = NIL));
- IF FindPackage(package.name) = NIL THEN
- tail.next := package;
- tail := package;
- INC(nofPackages);
- RETURN TRUE;
- ELSE
- RETURN FALSE;
- END;
- END Add;
- PROCEDURE GetAll*() : PackageArray;
- VAR packageArray : PackageArray; package : Package; i : LONGINT;
- BEGIN
- IF (nofPackages > 0) THEN
- NEW(packageArray, nofPackages);
- package := head.next;
- i := 0;
- WHILE (i < nofPackages) DO
- packageArray[i] := package;
- package := package.next;
- INC(i);
- END;
- ELSE
- packageArray := NIL;
- END;
- RETURN packageArray;
- END GetAll;
- PROCEDURE ToStream(out : Streams.Writer);
- VAR package : Package; nofPackages : LONGINT;
- BEGIN
- ASSERT(out # NIL);
- out.String("Packages: "); out.Ln;
- nofPackages := 0;
- package := head.next;
- WHILE (package # NIL) DO
- out.String(" "); out.String(package.name); out.Int(package.nofFiles, 4);
- out.String(" Files ("); out.Int(package.nofSources, 4); out.String(" source code files)"); out.Ln;
- INC(nofPackages);
- package := package.next;
- END;
- out.String(" "); out.Int(nofPackages, 0); out.String(" packages"); out.Ln;
- END ToStream;
- PROCEDURE &Init;
- BEGIN
- NEW(head, "Head", "", "", "", -1);
- tail := head;
- nofPackages := 0;
- END Init;
- END PackageList;
- TYPE
- ModuleInfo = RECORD
- name, context : Name;
- imports : ARRAY MaxNofImports OF Name; (* directly imported modules *)
- nofImports : LONGINT;
- flags : SET;
- isParsed : BOOLEAN;
- END;
- TYPE
- File* = OBJECT
- VAR
- module : ModuleInfo; (* if source code *)
- name-, uppercaseName : Files.FileName;
- doCompile : BOOLEAN;
- (* When using array indexes instead of names *)
- index : LONGINT; (* position of this File object in array *)
- importIndices : ARRAY MaxNofImports OF LONGINT;
- (* number of modules that directly or indirectly import this module *)
- nofDependentModules : LONGINT;
- (* number of modules directly or indirectly imported by this module *)
- nofRequiredModules : LONGINT;
- (* job ID in case of using the thread pool for parallel compilation of modules *)
- jobID : LONGINT;
- package- : Package;
- options: ARRAY 8 OF CHAR;
- release- : Set;
- flags-: SET;
- file : Files.File;
- pos : LONGINT; (* Position in package description file *)
- builds : Builds;
- prev-, next- : File;
- PROCEDURE &Init(builds : Builds);
- VAR i : LONGINT;
- BEGIN
- ASSERT(builds # NIL);
- SELF.builds := builds;
- module.name := ""; module.context := "";
- FOR i := 0 TO LEN(module.imports)-1 DO module.imports[i] := ""; END;
- module.nofImports := 0;
- module.flags := {};
- module.isParsed := FALSE;
- COPY("", name);
- doCompile := TRUE;
- package := NIL;
- COPY("", options);
- SetEmpty(release);
- flags := {};
- file := NIL;
- pos := 0;
- prev := NIL; next := NIL;
- END Init;
- PROCEDURE IsInRelease*(release : Set) : BOOLEAN;
- VAR i: LONGINT;
- BEGIN
- IF IsAll(SELF.release) THEN RETURN TRUE END;
- FOR i := 0 TO LEN(release)-1 DO
- IF SELF.release[i] * release[i] # {} THEN RETURN TRUE END;
- END;
- RETURN FALSE;
- END IsInRelease;
- PROCEDURE IsSourceCode*() : BOOLEAN;
- BEGIN
- RETURN SourceCode IN flags;
- END IsSourceCode;
- PROCEDURE CheckImports*(diagnostics : Diagnostics.Diagnostics; build : BuildObj; VAR error : BOOLEAN);
- VAR file : File; i : LONGINT; temp, message : ARRAY 256 OF CHAR;
- BEGIN
- ASSERT((diagnostics # NIL) & (build # NIL));
- error := FALSE;
- FOR i := 0 TO module.nofImports-1 DO
- file := prev;
- LOOP
- IF (file = NIL) THEN EXIT; END;
- IF (file.module.name = module.imports[i]) & file.IsInRelease(build.include) & ~build.PackageIsExcluded(file.package) THEN (* found *) EXIT; END;
- file := file.prev;
- END;
- IF (file = NIL) THEN
- error := TRUE;
- COPY(build.name, temp);
- Strings.Append(temp, ": Import # not found in file #");
- MakeMessage(message, temp, module.imports[i], name);
- diagnostics.Error(builds.source, pos, Diagnostics.Invalid, message);
- END;
- END;
- END CheckImports;
- PROCEDURE ParseModule*(diagnostics : Diagnostics.Diagnostics);
- VAR
- reader : Streams.Reader;
- pre, mid, suf: Files.FileName;
- message : ARRAY 256 OF CHAR;
- error : BOOLEAN;
- BEGIN
- IF module.isParsed OR ~IsSourceCode() THEN RETURN; END;
- reader := GetReader(SELF, diagnostics);
- IF (reader # NIL) THEN
- GetModuleInfo(reader, module, builds.source, name, pos, diagnostics, error);
- IF ~error THEN
- module.isParsed := TRUE;
- flags := flags + module.flags;
- SplitName(name, pre, mid, suf);
- IF (module.name # mid) THEN
- MakeMessage(message, "Module name not equal to filename in #", name, "");
- diagnostics.Warning(builds.source, pos, Diagnostics.Invalid, message);
- END;
- CreateContext(module.name, module.context);
- END;
- END;
- END ParseModule;
- PROCEDURE Show*(w : Streams.Writer);
- BEGIN
- w.String(name);
- END Show;
- END File;
- TYPE
- WorkerParameters = OBJECT
- VAR
- file : File;
- diagnostics : Diagnostics.Diagnostics;
- importCache : SyntaxTree.ModuleScope;
- PROCEDURE &Init(file : File; diagnostics : Diagnostics.Diagnostics; importCache : SyntaxTree.ModuleScope);
- BEGIN
- ASSERT((file # NIL) & (diagnostics # NIL) & (importCache # NIL));
- SELF.file := file;
- SELF.diagnostics := diagnostics;
- SELF.importCache := importCache;
- END Init;
- END WorkerParameters;
- TYPE
- BuildObj* = OBJECT
- VAR
- name- : Name;
- prefixes : ARRAY MaxPrefixes OF Name;
- excludedPackages : Strings.StringArray;
- onlyPackages: Strings.StringArray;
- compileOptions, linkOptions: Options.Parameter;
- compiler, linker: ARRAY 128 OF CHAR;
- target : ARRAY 32 OF CHAR;
- extension : ARRAY 8 OF CHAR;
- symbolFileExtension : ARRAY 8 OF CHAR;
- path : Files.FileName;
- disabled : BOOLEAN;
- link: BOOLEAN; (* link packages instead of compiling *)
- (* Used for dependencies analysis *)
- modules : POINTER TO ARRAY OF File;
- bitmap : POINTER TO ARRAY OF Bitmap;
- marked : BOOLEAN; (* Used by Check command *)
- (* List of all files and packages declared in the package description file *)
- files : File;
- packages : PackageList;
- (* Name of the package description file *)
- builds : Builds;
- include : Set;
- position- : LONGINT;
- PROCEDURE &Init;
- VAR i : LONGINT;
- BEGIN
- COPY("", name);
- FOR i := 0 TO LEN(prefixes)-1 DO prefixes[i] := ""; END;
- excludedPackages := NIL;
- onlyPackages := NIL;
- COPY(DefaultCompiler, compiler);
- COPY(DefaultCompileOptions, compileOptions);
- COPY(DefaultTarget, target);
- COPY(DefaultExtension, extension);
- COPY(DefaultSymbolFileExtension, symbolFileExtension);
- COPY(DefaultPath, path);
- disabled := DefaultDisabled;
- modules := NIL; bitmap := NIL;
- marked := FALSE;
- files := NIL;
- packages := NIL;
- builds := NIL;
- SetEmpty(include);
- position := -1;
- END Init;
- PROCEDURE CompileThisPackage(package: Package): BOOLEAN;
- VAR i : LONGINT;
- BEGIN
- IF (onlyPackages # NIL) THEN
- FOR i := 0 TO LEN(onlyPackages)-1 DO
- IF (package.name = onlyPackages[i]^) THEN
- RETURN TRUE
- END;
- END;
- RETURN FALSE
- END;
- RETURN TRUE
- END CompileThisPackage;
- PROCEDURE PackageIsExcluded(package : Package) : BOOLEAN;
- VAR i : LONGINT;
- BEGIN
- IF (package = NIL) THEN RETURN FALSE; END;
- IF (excludedPackages # NIL) THEN
- FOR i := 0 TO LEN(excludedPackages)-1 DO
- IF (package.name = excludedPackages[i]^) THEN
- RETURN TRUE;
- END;
- END;
- END;
- RETURN FALSE;
- END PackageIsExcluded;
- PROCEDURE SetOptions(options : Options.Options);
- VAR string : ARRAY 512 OF CHAR;
- BEGIN
- ASSERT(options # NIL);
- IF (options # NIL) THEN
- link := options.GetFlag("link");
- IF options.GetString("compiler", compiler) THEN END;
- IF link THEN
- IF options.GetString("options", linkOptions) THEN END;
- ELSE
- IF options.GetString("options", compileOptions) THEN END;
- END;
- IF options.GetString("target", target) THEN END;
- IF options.GetString("extension", extension) THEN END;
- IF options.GetString("symbolFileExtension", symbolFileExtension) THEN END;
- IF options.GetString("path", path) THEN END;
- IF options.GetString("exclude", string) THEN
- Strings.TrimWS(string);
- excludedPackages := Strings.Split(string, " ");
- END;
- IF options.GetString("only",string) THEN
- Strings.TrimWS(string);
- onlyPackages := Strings.Split(string, " ");
- END;
- END;
- END SetOptions;
- (** List all source code files included in this build to a stream *)
- PROCEDURE ToStream*(w : Streams.Writer; charactersPerLine : LONGINT);
- VAR file : File; color : LONGINT; characterCount : LONGINT; name: Files.FileName;
- BEGIN
- characterCount := 0;
- file := files;
- WHILE (file # NIL) DO
- IF file.IsSourceCode() & file.IsInRelease(include) & ~PackageIsExcluded(file.package) & file.doCompile & CompileThisPackage(file.package) THEN
- IF (w IS TextUtilities.TextWriter) THEN
- IF (ImportsSystem IN file.flags) THEN
- color := WMGraphics.Red;
- ELSE
- color := WMGraphics.Black;
- END;
- w(TextUtilities.TextWriter).SetFontColor(color);
- END;
- IF link THEN
- COPY(file.module.name, name)
- ELSE
- COPY(file.name, name)
- END;
- characterCount := characterCount + Strings.Length(name);
- IF (characterCount > charactersPerLine) THEN
- characterCount := 0;
- w.Ln;
- END;
- w.String(name); w.String(" ");
- END;
- file := file.next;
- END;
- IF (w IS TextUtilities.TextWriter) THEN
- w(TextUtilities.TextWriter).SetFontColor(WMGraphics.Black);
- END;
- w.Update;
- END ToStream;
- (** Generate a file that contains the list of all source code files of this build in compile order *)
- PROCEDURE GenerateToolFile*(CONST filename : Files.FileName; VAR res : LONGINT);
- VAR text : Texts.Text; tw : TextUtilities.TextWriter; dateTime : Dates.DateTime; temp : ARRAY 256 OF CHAR;
- BEGIN
- NEW(text); NEW(tw, text);
- tw.SetFontColor(LONGINT(808080FFH)); (* comment grey *)
- tw.String("# "); tw.String(name); tw.Ln;
- dateTime := Dates.Now();
- Strings.FormatDateTime(DateTimeFormat, dateTime, temp);
- tw.String("# "); tw.String(temp); tw.Ln;
- tw.String("# This file has been automatically generated using Release.Mod."); tw.Ln;
- tw.String("# Red colors indicate that a module imports SYSTEM."); tw.Ln;
- tw.SetFontColor(WMGraphics.Black);
- (* Compile command *)
- tw.SetFontStyle({WMGraphics.FontBold});
- tw.String("SystemTools.DoCommands"); tw.Ln;
- tw.SetFontStyle({});
- tw.String("SystemTools.Timer start ~"); tw.Ln;
- IF link THEN
- tw.String(linker);
- GetLinkerOptions(temp);
- ELSE
- tw.String(compiler);
- GetCompilerOptions(temp);
- END;
- tw.String(" "); tw.String(temp); tw.Ln;
- ToStream(tw, 80); (* file list *) tw.Ln; tw.String("~"); tw.Ln;
- tw.String("SystemTools.Show Time elapsed: ~ SystemTools.Ln ~"); tw.Ln;
- tw.String("SystemTools.Timer elapsed ~ SystemTools.Ln ~"); tw.Ln;
- tw.String("~");
- tw.Update;
- TextUtilities.StoreOberonText(text, filename, res);
- END GenerateToolFile;
- (* Generate a XML file describing the packages use by WMInstaller.Mod *)
- PROCEDURE GeneratePackageFile(CONST filename : ARRAY OF CHAR; VAR res : LONGINT);
- VAR
- packageArray : PackageArray; package : Package;
- fullname : Files.FileName; file : Files.File; w : Files.Writer;
- packageNbr, i : LONGINT;
- PROCEDURE WritePackage(package : Package; sources : BOOLEAN; packageNbr : LONGINT; w : Streams.Writer);
- VAR filename : Files.FileName; description : ARRAY 300 OF CHAR; name, installMode : ARRAY 128 OF CHAR;
- BEGIN
- ASSERT((package # NIL) & (w # NIL));
- COPY(package.description, description);
- IF sources THEN
- COPY(package.name, name); Strings.Append(name, " Sources");
- COPY(package.source, filename); Strings.Append(description, " sources");
- ELSE
- COPY(package.name, name);
- COPY(package.archive, filename);
- END;
- w.Char(Tab);
- w.String('<Package nr="'); w.Int(packageNbr, 0);
- w.String('" name="'); w.String(name);
- w.String('" file="'); w.String(filename);
- w.String('" description="'); w.String(description);
- IF (package.installMode = Required) THEN installMode := "required";
- ELSIF (package.installMode = Yes) THEN installMode := "yes";
- ELSIF (package.installMode = No) THEN installMode := "no";
- ELSE installMode := "undefined";
- END;
- w.String('" install="'); w.String(installMode); w.String('"/>'); w.Ln;
- END WritePackage;
- BEGIN
- res := Files.Ok;
- packageArray := packages.GetAll();
- IF (packageArray # NIL) THEN
- COPY(path, fullname); Strings.Append(fullname, filename);
- file := Files.New(fullname);
- IF (file # NIL) THEN
- packageNbr := 1;
- Files.OpenWriter(w, file, 0);
- w.String('<?xml version="1.0" encoding="UTF-8" standalone="yes"?>'); w.Ln; w.Ln;
- w.String("<!-- This has been automatically generated by Release.Mod -->"); w.Ln; w.Ln;
- w.String("<Packages>"); w.Ln;
- FOR i := 0 TO LEN(packageArray)-1 DO
- package := packageArray[i];
- IF ~PackageIsExcluded(package) THEN
- WritePackage(package, FALSE, packageNbr, w);
- INC(packageNbr);
- IF (package.source # "") THEN
- WritePackage(package, TRUE, packageNbr, w);
- INC(packageNbr);
- END;
- END;
- END;
- w.String("</Packages>"); w.Ln; w.Update;
- Files.Register(file);
- ELSE
- res := NoFile;
- END;
- ELSE
- res := NoPackages;
- END;
- END GeneratePackageFile;
- PROCEDURE GenerateZipFiles(out, error : Streams.Writer; diagnostics : Diagnostics.Diagnostics; VAR err : BOOLEAN);
- VAR packageArray : PackageArray; i, res : LONGINT;
- PROCEDURE AddFile(archive: Zip.Archive; CONST srcname, dstname: ARRAY OF CHAR; VAR res: LONGINT);
- VAR f: Files.File; r: Files.Rider; pathName, fileName: Files.FileName;
- BEGIN
- f := Files.Old(srcname);
- IF f = NIL THEN
- res := Zip.BadName
- ELSE
- f.Set(r, 0);
- Files.SplitPath(dstname, pathName, fileName);
- Zip.AddEntry(archive, fileName, r, f.Length(), 9, 2, res);
- END;
- END AddFile;
- PROCEDURE DeleteFile(filename : ARRAY OF CHAR; VAR nofFilesDeleted : LONGINT);
- VAR file : Files.File;
- BEGIN
- file := Files.Old(filename);
- IF (file # NIL) THEN
- Files.Delete(filename, res);
- IF (res = Files.Ok) THEN
- INC(nofFilesDeleted);
- ELSE
- out.String(" could not delete existing file, res: "); out.Int(res, 0); out.Ln;
- END;
- END;
- END DeleteFile;
- PROCEDURE GetObjectFileName(file : File; CONST prefix : ARRAY OF CHAR; VAR fileName: ARRAY OF CHAR);
- BEGIN
- COPY(prefix, fileName);
- Strings.Append(fileName, file.module.name);
- Strings.Append(fileName, ".");
- Strings.Append(fileName, extension);
- END GetObjectFileName;
- PROCEDURE GetSymbolFileName(file : File; CONST prefix : ARRAY OF CHAR; VAR fileName : ARRAY OF CHAR);
- BEGIN
- COPY(prefix, fileName);
- Strings.Append(fileName, file.module.name);
- Strings.Append(fileName, ".");
- Strings.Append(fileName, symbolFileExtension);
- END GetSymbolFileName;
- PROCEDURE GetPackageFileName(package : Package; sourceCode : BOOLEAN; VAR filename : ARRAY OF CHAR) : BOOLEAN;
- BEGIN
- ASSERT(package # NIL);
- COPY(path, filename);
- IF ~sourceCode THEN
- Strings.Append(filename, package.archive);
- ELSE
- Strings.Append(filename, package.source);
- END;
- RETURN (filename # path);
- END GetPackageFileName;
- PROCEDURE DeleteOldZipFiles(packageArray : PackageArray);
- VAR filename : Files.FileName; nofFilesDeleted, i : LONGINT;
- BEGIN
- ASSERT(packageArray # NIL);
- out.String("Deleting old archive files ... "); out.Update;
- nofFilesDeleted := 0;
- FOR i := 0 TO LEN(packageArray)-1 DO
- IF GetPackageFileName(packageArray[i], TRUE, filename) THEN DeleteFile(filename, nofFilesDeleted); END;
- IF GetPackageFileName(packageArray[i], FALSE, filename) THEN DeleteFile(filename, nofFilesDeleted); END;
- END;
- out.Int(nofFilesDeleted, 0); out.String(" files deleted."); out.Ln;
- END DeleteOldZipFiles;
- PROCEDURE StripReleasePrefix(file : File; VAR filename: ARRAY OF CHAR);
- VAR pre, mid, suf : Files.FileName;
- BEGIN
- SplitName(file.name, pre, mid, suf);
- ASSERT(pre = ReleasePrefix);
- COPY (mid, filename); Strings.Append(filename, "."); Strings.Append(filename, suf);
- END StripReleasePrefix;
- PROCEDURE GenerateZipFile(package : Package; sourceCode : BOOLEAN; VAR res : LONGINT);
- VAR
- file : File; archiveName, source, dest : Files.FileName;
- archive : Zip.Archive;
- nofFilesAdded : LONGINT;
- BEGIN
- ASSERT(package # NIL);
- res := Zip.Ok;
- IF GetPackageFileName(package, sourceCode, archiveName) THEN
- out.String("Creating archive "); out.String(archiveName); out.String(" ... "); out.Update;
- archive := Zip.CreateArchive(archiveName, res);
- IF (res # Zip.Ok) THEN
- error.String("error: "); Zip.ShowError(res, error); error.Ln;
- RETURN;
- END;
- nofFilesAdded := 0;
- file := files;
- WHILE (file # NIL) DO
- IF (file.package = package) & file.IsInRelease(include) THEN
- IF sourceCode THEN
- IF file.IsSourceCode() THEN
- AddFile(archive, file.name, file.name, res);
- INC(nofFilesAdded);
- END;
- ELSE
- IF file.IsSourceCode() THEN
- GetObjectFileName(file, path, source); GetObjectFileName(file, "", dest);
- AddFile(archive, source, dest, res);
- INC(nofFilesAdded);
- IF (res = Zip.Ok) & (package.source = "") THEN (* Also add source code since no source code package is defined *)
- AddFile(archive, file.name, file.name, res);
- END;
- IF symbolFileExtension # extension THEN
- GetSymbolFileName(file, path, source); GetSymbolFileName(file, "", dest);
- AddFile(archive, source, dest, res);
- INC(nofFilesAdded);
- END
- ELSE
- AddFile(archive, file.name, file.name, res);
- INC(nofFilesAdded);
- IF (res = Zip.Ok) & (HasReleasePrefix IN file.flags) THEN
- StripReleasePrefix(file, dest); AddFile(archive, file.name, dest, res);
- INC(nofFilesAdded);
- END;
- END;
- END;
- IF (res # Zip.Ok) THEN
- error.Ln; error.String("Could not add file "); error.String(file.name);
- error.String(": "); Zip.ShowError(res, error); error.Ln;
- error.Update;
- RETURN;
- END;
- END;
- file := file.next;
- END;
- out.Int(nofFilesAdded, 0); out.String(" files added."); out.Ln; out.Update;
- END;
- END GenerateZipFile;
- BEGIN
- packageArray := packages.GetAll();
- IF (packageArray # NIL) THEN
- DeleteOldZipFiles(packageArray);
- FOR i := 0 TO LEN(packageArray)-1 DO
- IF ~PackageIsExcluded(packageArray[i]) THEN
- GenerateZipFile(packageArray[i], TRUE, res);
- IF (res # Zip.Ok) THEN err := TRUE; RETURN END;
- GenerateZipFile(packageArray[i], FALSE, res);
- IF (res # Zip.Ok) THEN err := TRUE; RETURN END;
- END;
- END;
- ELSE
- diagnostics.Error("", Diagnostics.Invalid, Diagnostics.Invalid, "No packages");
- END;
- END GenerateZipFiles;
- PROCEDURE FindPosition*(CONST filename : ARRAY OF CHAR; diagnostics : Diagnostics.DiagnosticsList) : LONGINT;
- VAR
- file, ref : File; position : LONGINT; pre, suf : ARRAY 32 OF CHAR;
- importOk : ARRAY MaxNofImports OF BOOLEAN; i : LONGINT;
- PROCEDURE Done() : BOOLEAN;
- VAR i : LONGINT;
- BEGIN
- i := 0; WHILE (i < LEN(importOk)) & (importOk[i] = TRUE) DO INC(i); END;
- RETURN (i >= LEN(importOk));
- END Done;
- PROCEDURE Process(f : File);
- VAR i : LONGINT;
- BEGIN
- IF f.IsSourceCode() & f.IsInRelease(include) & ~PackageIsExcluded(f.package)THEN
- FOR i := 0 TO LEN(importOk)-1 DO
- IF (file.module.imports[i] = f.module.name) & ~PackageIsExcluded(file.package) THEN importOk[i] := TRUE; END;
- END;
- END;
- END Process;
- BEGIN
- NEW(file, builds);
- COPY(filename, file.name);
- SplitName(file.name, pre, file.module.name, suf);
- IF (suf = "Mod") OR (suf="Mdf") OR (suf="Mos") THEN
- INCL(file.flags, SourceCode);
- file.ParseModule(diagnostics);
- IF (diagnostics.nofErrors = 0) THEN
- FOR i := 0 TO LEN(importOk)-1 DO
- IF (file.module.imports[i] # "") THEN importOk[i] := FALSE; ELSE importOk[i] := TRUE; END;
- END;
- position := 0;
- ref := files;
- LOOP
- IF Done() THEN EXIT; END;
- IF (ref = NIL) THEN EXIT; END;
- Process(ref);
- IF (ref.next # NIL) THEN position := ref.next.pos; ELSE position := ref.pos + 100; END;
- ref := ref.next;
- END;
- ELSE
- position := -1;
- END;
- ELSE
- position := 0;
- END;
- RETURN position;
- END FindPosition;
- PROCEDURE FindModule(CONST moduleName : Modules.Name) : File;
- VAR file : File;
- BEGIN
- file := files;
- WHILE (file # NIL) DO
- IF file.IsInRelease(include) & file.IsSourceCode() & ~PackageIsExcluded(file.package) THEN
- IF (file.module.name = moduleName) THEN
- RETURN file;
- END;
- END;
- file := file.next;
- END;
- RETURN NIL;
- END FindModule;
- PROCEDURE FindFile(CONST filename : Files.FileName) : File;
- VAR file : File;
- BEGIN
- file := files;
- WHILE (file # NIL) DO
- IF file.IsInRelease(include) & file.IsSourceCode() & ~PackageIsExcluded(file.package) THEN
- IF (file.name = filename) THEN
- RETURN file;
- END;
- END;
- file := file.next;
- END;
- RETURN NIL;
- END FindFile;
- (** For all files contained in this build check whether they exist. Also try to parse the import section of the source code files *)
- PROCEDURE CheckFiles*(diagnostics : Diagnostics.Diagnostics);
- VAR file : File; message : ARRAY 256 OF CHAR;
- BEGIN
- file := files;
- WHILE (file # NIL) DO
- IF file.IsInRelease(include) & ~PackageIsExcluded(file.package) THEN
- IF (file.file = NIL) THEN
- file.file := Files.Old(file.name);
- END;
- IF (file.file = NIL) THEN
- MakeMessage(message, "File # does not exists (Package #)", file.name, file.package.name);
- diagnostics.Warning(builds.source, file.pos, Diagnostics.Invalid, message);
- ELSIF file.IsSourceCode() THEN
- file.ParseModule(diagnostics);
- END;
- IF ~KeepFilesOpen THEN
- file.file := NIL;
- END;
- END;
- file := file.next;
- END;
- END CheckFiles;
- (** Check the source code files of this build for the import order *)
- PROCEDURE CheckModules*(diagnostics : Diagnostics.Diagnostics; VAR error : BOOLEAN);
- VAR file : File; tempError : BOOLEAN;
- BEGIN
- error := FALSE;
- file := files;
- WHILE (file # NIL) DO
- IF file.IsSourceCode() & file.IsInRelease(include) & ~PackageIsExcluded(file.package) THEN
- tempError := FALSE;
- file.CheckImports(diagnostics, SELF, tempError);
- error := error OR tempError;
- END;
- file := file.next;
- END;
- END CheckModules;
- (** Call both CheckFiles and CheckModules *)
- PROCEDURE DoChecks*(out : Streams.Writer; diagnostics : Diagnostics.Diagnostics; VAR error : BOOLEAN);
- VAR start, start0 : Dates.DateTime;
- BEGIN
- ASSERT((diagnostics # NIL));
- IF (out # NIL) THEN out.String(name); out.String(": Check if all files are present... "); out.Update; END;
- start0 := Dates.Now();
- CheckFiles(diagnostics);
- Strings.ShowTimeDifference(start0, Dates.Now(), out);
- IF (out # NIL) THEN out.String(" done."); out.Ln; out.Update; END;
- IF (out # NIL) THEN out.String(name); out.String(": Check modules and imports... "); out.Update; END;
- start := Dates.Now();
- error := FALSE;
- CheckModules(diagnostics, error);
- Strings.ShowTimeDifference(start, Dates.Now(), out);
- IF (out # NIL) THEN out.String(" done."); out.Ln; out.Update; END;
- END DoChecks;
- (* Analyzes the import dependencies between all modules *)
- PROCEDURE AnalyzeDependencies(out : Streams.Writer);
- VAR
- nofModules : LONGINT;
- file : File;
- ignore, index, i, j : LONGINT;
- PROCEDURE GetIndexOf(file : File; CONST moduleName : Name) : LONGINT;
- VAR f : File;
- BEGIN
- f := file.prev;
- LOOP
- ASSERT(f # NIL);
- IF f.IsSourceCode() & f.IsInRelease(include) & ~PackageIsExcluded(f.package) & (f.module.name = moduleName) THEN
- EXIT;
- END;
- f := f.prev;
- END;
- RETURN f.index;
- END GetIndexOf;
- BEGIN
- modules:= NIL;
- nofModules := GetNofSources(ignore);
- IF (out # NIL) THEN out.String("Analyzing dependencies of "); out.Int(nofModules, 0); out.String(" modules... "); END;
- IF (nofModules > 0) THEN
- (* Initialize the array of File 'modules' and the fields File.index and File.importIndices. After initialization,
- all File.importIndices contain the indices of directly imported modules *)
- NEW(modules, nofModules);
- index := 0;
- file := files;
- WHILE (file # NIL) DO
- IF file.IsSourceCode() & file.IsInRelease(include) & ~PackageIsExcluded(file.package) THEN
- file.index := index;
- modules[index] := file;
- FOR i := 0 TO file.module.nofImports-1 DO
- modules[index].importIndices[i] := GetIndexOf(file, file.module.imports[i]);
- END;
- INC(index);
- END;
- file := file.next;
- END;
- (* Create a two-dimensional bitmap where bitmap[i] is the import bitmap of the file with file.index = i.
- The import bitmap[i] has all bits j set where j is directly or indirectly imported by the file i . *)
- NEW(bitmap, nofModules);
- FOR i := 0 TO nofModules-1 DO
- NEW(bitmap[i], nofModules);
- FOR j := 0 TO modules[i].module.nofImports-1 DO
- bitmap[i].Set(modules[i].importIndices[j]);
- bitmap[i].Union(bitmap[modules[i].importIndices[j]]);
- END;
- END;
- (* Initialize fields File.nofDependetModules and File.nofRequiredModules *)
- FOR i := 0 TO nofModules-1 DO
- modules[i].nofDependentModules := 0;
- FOR j := 0 TO nofModules-1 DO
- IF bitmap[j].IsSet(i) THEN INC(modules[i].nofDependentModules); END;
- END;
- modules[i].nofRequiredModules := bitmap[i].NofBitsSet();
- END;
- END;
- IF (out # NIL) THEN out.String("done."); out.Ln; END;
- END AnalyzeDependencies;
- PROCEDURE ShowDependencies(out : Streams.Writer);
- VAR i, j : LONGINT; temp : File;
- BEGIN
- ASSERT(out # NIL);
- ASSERT((modules # NIL) & (bitmap # NIL));
- (* bubble sort *)
- FOR i := 0 TO LEN(modules)-1 DO
- FOR j := 0 TO LEN(modules)-2 DO
- IF (modules[j].nofDependentModules < modules[j+1].nofDependentModules) THEN
- temp := modules[j];
- modules[j] := modules[j+1];
- modules[j+1] := temp;
- END;
- END;
- END;
- FOR i := 0 TO LEN(modules)-1 DO
- out.String(modules[i].name); out.String(" -- ");
- out.Int(modules[i].nofDependentModules, 0);
- out.String(" ("); out.Int(modules[i].module.nofImports, 0);
- out.String("/"); out.Int(modules[i].nofRequiredModules, 0);
- out.String(")");
- out.Ln;
- END;
- END ShowDependencies;
- (** Clear the doCompile flag of all source code files contained in this build and not excluded *)
- PROCEDURE ClearMarks;
- VAR file : File;
- BEGIN
- file := files;
- WHILE (file # NIL) DO
- IF file.IsSourceCode() & file.IsInRelease(include) & ~PackageIsExcluded(file.package) THEN
- file.doCompile := FALSE;
- END;
- file := file.next;
- END;
- END ClearMarks;
- (** Set the doCompile flag of all non-excluded source code files in this build that depend on the specified file *)
- PROCEDURE MarkFiles(CONST filename : Files.FileName; VAR inBuild : BOOLEAN; VAR nofNewMarks : LONGINT);
- VAR file : File; nofModules, i, ignore : LONGINT;
- BEGIN
- nofNewMarks := 0;
- file := FindFile(filename);
- IF (file # NIL) THEN
- inBuild := TRUE;
- IF ~file.doCompile THEN
- file.doCompile := TRUE;
- INC(nofNewMarks);
- END;
- nofModules := GetNofSources(ignore);
- FOR i := 0 TO nofModules-1 DO
- IF bitmap[i].IsSet(file.index) THEN
- IF ~modules[i].doCompile THEN
- INC(nofNewMarks);
- modules[i].doCompile := TRUE;
- END;
- END;
- END;
- ELSE
- inBuild := FALSE;
- END;
- END MarkFiles;
- PROCEDURE ShowDependentModules(CONST modulename : Modules.Name; mode : LONGINT; out : Streams.Writer);
- CONST CharactersPerLine = 60;
- VAR module : File; ignore, nofModules, characterCount, i : LONGINT;
- BEGIN
- ASSERT(out # NIL);
- module := FindModule(modulename);
- IF (module # NIL) THEN
- characterCount := 0;
- nofModules := GetNofSources(ignore);
- IF (mode = Mode_ShowImporting) THEN
- FOR i := 0 TO nofModules-1 DO
- IF bitmap[i].IsSet(module.index) THEN
- out.String(modules[i].name); out.String(" ");
- characterCount := characterCount + Strings.Length(modules[i].name);
- IF (characterCount > CharactersPerLine) THEN characterCount := 0; out.Ln; out.Update; END;
- END;
- END;
- ELSE
- FOR i := 0 TO nofModules-1 DO
- IF bitmap[module.index].IsSet(i) THEN
- out.String(modules[i].name); out.String(" ");
- characterCount := characterCount + Strings.Length(modules[i].name);
- IF (characterCount > CharactersPerLine) THEN characterCount := 0; out.Ln; out.Update; END;
- END;
- END;
- END;
- ELSE
- out.String("Module "); out.String(modulename); out.String(" not found"); out.Ln;
- END;
- out.Update;
- END ShowDependentModules;
- PROCEDURE GetCompilerOptions(VAR options: ARRAY OF CHAR);
- BEGIN
- COPY(compileOptions, options);
- IF target # "" THEN
- Strings.Append(options, " -b="); Strings.Append(options, target);
- END;
- IF extension # "" THEN
- Strings.Append(options, " --objectFileExtension="); Strings.Append(options, "."); Strings.Append(options, extension);
- Strings.Append(options, " --symbolFileExtension="); Strings.Append(options, "."); Strings.Append(options, symbolFileExtension);
- END;
- IF path # "" THEN
- Strings.Append(options, " --destPath="); Strings.Append(options, path);
- END;
- END GetCompilerOptions;
- PROCEDURE GetLinkerOptions(VAR options: ARRAY OF CHAR);
- BEGIN
- COPY(linkOptions, options);
- END GetLinkerOptions;
- (* Count the number of source codes files in a specific build *)
- PROCEDURE GetNofSources(VAR nofMarked : LONGINT) : LONGINT;
- VAR file : File; nofSources : LONGINT;
- BEGIN
- nofSources := 0;
- file := files;
- WHILE (file # NIL) DO
- IF file.IsSourceCode() & file.IsInRelease(include) & ~PackageIsExcluded(file.package) & CompileThisPackage(file.package) THEN
- INC(nofSources);
- IF file.doCompile THEN INC(nofMarked); END;
- END;
- file := file.next;
- END;
- RETURN nofSources;
- END GetNofSources;
- PROCEDURE GetInfo*(VAR nofSources, nofFiles : LONGINT);
- VAR file : File;
- BEGIN
- nofSources := 0; nofFiles := 0;
- file := files;
- WHILE (file # NIL) DO
- IF file.IsInRelease(include) & ~PackageIsExcluded(file.package) THEN
- INC(nofFiles);
- IF file.IsSourceCode() THEN
- INC(nofSources);
- END;
- END;
- file := file.next;
- END;
- END GetInfo;
- PROCEDURE CompileFile(file : File; diagnostics : Diagnostics.Diagnostics; log : Streams.Writer; VAR error : BOOLEAN; importCache : SyntaxTree.ModuleScope);
- VAR
- reader : Streams.Reader;
- options : ARRAY 1024 OF CHAR;
- optionsReader : Streams.StringReader;
- message : ARRAY 512 OF CHAR;
- compilerOptions: Compiler.CompilerOptions;
- BEGIN
- ASSERT((file # NIL) & (diagnostics # NIL) & (importCache # NIL));
- reader := GetReader(file, diagnostics);
- IF ~KeepFilesOpen THEN
- file.file := NIL;
- END;
- IF (reader # NIL) THEN
- GetCompilerOptions(options);
- NEW(optionsReader, LEN(options));
- optionsReader.Set(options);
- error := ~Compiler.GetOptions(optionsReader, log, diagnostics, compilerOptions);
- IF ~error THEN
- error := ~Compiler.Modules(file.name, reader, 0, diagnostics, log, compilerOptions, importCache);
- END;
- ELSE
- MakeMessage(message, "File # not found", file.name, "");
- diagnostics.Error("", file.pos, Diagnostics.Invalid, message);
- error := TRUE;
- END;
- END CompileFile;
- (* Job procedure called by worker thread *)
- PROCEDURE CompileJob(parameters : ANY; VAR error : BOOLEAN);
- BEGIN
- ASSERT(
- (parameters # NIL) & (parameters IS WorkerParameters) &
- (parameters(WorkerParameters).file # NIL) & (parameters(WorkerParameters).diagnostics # NIL) &
- (parameters(WorkerParameters).importCache # NIL)
- );
- CompileFile(
- parameters(WorkerParameters).file,
- parameters(WorkerParameters).diagnostics,
- NIL,
- error,
- parameters(WorkerParameters).importCache
- );
- END CompileJob;
- PROCEDURE CreateJob(threadpool : ReleaseThreadPool.ThreadPool; file : File; diagnostics : Diagnostics.Diagnostics; importCache : SyntaxTree.ModuleScope);
- VAR parameters : WorkerParameters; dependencies : ReleaseThreadPool.Dependencies; i, priority : LONGINT;
- BEGIN
- ASSERT((threadpool # NIL) & (file # NIL) & (diagnostics # NIL) & (importCache # NIL));
- NEW(parameters, file, diagnostics, importCache);
- priority := file.nofDependentModules;
- i := 0;
- WHILE (i < file.module.nofImports) DO
- dependencies[i] := modules[file.importIndices[i]].jobID;
- INC(i);
- END;
- dependencies[i] := ReleaseThreadPool.NoMoreDependencies;
- file.jobID := threadpool.CreateJob(CompileJob, parameters, priority, dependencies);
- END CreateJob;
- (** Compile all sources included in this build *)
- PROCEDURE Compile*(nofWorkers : LONGINT; out, error : Streams.Writer; verbose : BOOLEAN; diagnostics : Diagnostics.DiagnosticsList; VAR err : BOOLEAN);
- VAR
- file : File; nofFiles, nofSources, nofMarked, step, steps : LONGINT;
- importCache : SyntaxTree.ModuleScope;
- startTime : Dates.DateTime;
- log : Streams.Writer;
- dummyWriter : Streams.StringWriter;
- threadpool : ReleaseThreadPool.ThreadPool;
- BEGIN
- ASSERT(nofWorkers >= 0);
- nofSources := GetNofSources(nofMarked);
- IF (nofWorkers > 0) THEN
- AnalyzeDependencies(out);
- NEW(threadpool, nofWorkers);
- out.String("Generating jobs to compile build ");
- ELSE
- out.String("Compiling build ");
- END;
- out.String(name); out.String(" (");
- IF (nofMarked # nofSources) THEN out.Int(nofMarked, 0); out.Char("/"); END;
- out.Int(nofSources, 0); out.String(" files)");
- IF (nofWorkers > 0) THEN
- out.String(" using "); out.Int(nofWorkers, 0); out.String(" worker threads");
- END;
- out.String(" ... ");
- IF verbose THEN
- log := out;
- ELSE
- NEW(dummyWriter, 2);
- log := dummyWriter;
- step := ((nofMarked-1) DIV 10) +1; steps := 1;
- out.String(" 00% "); out.Update;
- END;
- startTime := Dates.Now();
- importCache := SyntaxTree.NewModuleScope();
- nofFiles := 0; err := FALSE;
- file := files;
- WHILE (file # NIL) DO
- IF file.IsSourceCode() & file.IsInRelease(include) & ~PackageIsExcluded(file.package) & file.doCompile & CompileThisPackage(file.package) THEN
- IF (nofWorkers = 0) THEN
- CompileFile(file, diagnostics, log, err, importCache);
- IF err THEN
- error.Ln; error.Ln;
- error.String("Error(s) in file "); error.String(file.name); error.Ln;
- diagnostics.ToStream(error, {Diagnostics.TypeError}); error.Ln;
- diagnostics.Reset;
- RETURN;
- END;
- ELSE
- CreateJob(threadpool, file, diagnostics, importCache);
- END;
- INC(nofFiles);
- IF ~verbose & (step # 0) & (nofFiles MOD step = 0) THEN
- out.Int(steps * 10, 0); out.String("% "); out.Update;
- INC(steps);
- END;
- END;
- file := file.next;
- END;
- IF verbose THEN
- out.Int(nofFiles, 0); out.String(" files done in ");
- ELSIF (steps < 11) THEN
- out.String("100% ");
- END;
- out.String(" done in "); Strings.ShowTimeDifference(startTime, Dates.Now(), out);
- out.Ln; out.Update;
- IF (nofWorkers > 0) THEN
- threadpool.AwaitAllDone;
- threadpool.Close;
- err := diagnostics.nofErrors > 0;
- IF (diagnostics.nofMessages > 0) THEN
- diagnostics.ToStream(error, Diagnostics.All); error.Ln;
- END;
- out.String("Compilation time: "); Strings.ShowTimeDifference(startTime, Dates.Now(), out);
- out.Ln; out.Update;
- END;
- END Compile;
- END BuildObj;
- TYPE
- Version = RECORD
- major, minor : LONGINT;
- END;
- Builds* = OBJECT
- VAR
- version : Version;
- (* All builds described in the build description file *)
- builds- : ARRAY MaxBuilds OF BuildObj;
- nofBuilds : LONGINT;
- packages- : PackageList;
- (* In the build description file, the user may group files using prefixes, e.g.
- WIN,NATIVE { filename }. In this example, WIN and NATIVE are prefixes.
- The prefixes array contains each prefix exactly once. The index of specific prefix in this
- are will be used as bit position for the BuildObj.include set. *)
- prefixes : ARRAY MaxPrefixes OF Name;
- nofPrefixes : LONGINT;
- (* Name of the package description file *)
- source : Files.FileName;
- (* All files of all builds in the build description file *)
- files : File;
- (* number of files / sources in <files> list *)
- nofFiles- : LONGINT;
- nofSources- : LONGINT;
- PROCEDURE &Init;
- VAR i : LONGINT;
- BEGIN
- FOR i := 0 TO LEN(builds)-1 DO builds[i] := NIL; END;
- nofBuilds := 0;
- NEW(packages);
- FOR i := 0 TO LEN(prefixes)-1 DO prefixes[i] := ""; END;
- nofPrefixes := 0;
- source := "";
- files := NIL;
- nofFiles := 0;
- nofSources := 0;
- END Init;
- (* Add the specified prefix to the prefixes array if its not already contained *)
- PROCEDURE AddPrefix(CONST prefix : Name; diagnostics : Diagnostics.Diagnostics) : BOOLEAN;
- VAR error : BOOLEAN; i : LONGINT; j: LONGINT;
- BEGIN
- ASSERT((prefix # "") & (diagnostics # NIL));
- error := FALSE;
- (* check whether prefix is already registered *)
- i := 0; WHILE (i < LEN(prefixes)) & (prefixes[i] # prefix) DO INC(i); END;
- IF (i >= LEN(prefixes)) THEN (* new prefix, add it *)
- IF (nofPrefixes < LEN(prefixes)) THEN
- prefixes[nofPrefixes] := prefix;
- INC(nofPrefixes);
- ELSE
- error := TRUE;
- diagnostics.Warning("", Diagnostics.Invalid, Diagnostics.Invalid, "Maximum number of prefixes exceeded");
- FOR j := 0 TO LEN(prefixes)-1 DO
- TRACE(prefixes[j]);
- END;
- END;
- END;
- RETURN ~error;
- END AddPrefix;
- (* Get the index of the specified prefix in the prefixes array. Returns -1 if prefix not found *)
- PROCEDURE GetPrefixIndex(CONST prefix : ARRAY OF CHAR) : LONGINT;
- VAR index : LONGINT;
- BEGIN
- index := 0;
- WHILE (index < nofPrefixes) & (prefixes[index] # prefix) DO INC(index); END;
- IF (index >= nofPrefixes) THEN index := -1; END;
- RETURN index;
- END GetPrefixIndex;
- (* Do checks for all builds *)
- PROCEDURE CheckAll*(out : Streams.Writer; diagnostics : Diagnostics.Diagnostics);
- VAR file : File; build : LONGINT; message : ARRAY 256 OF CHAR; error : BOOLEAN;
- BEGIN
- ASSERT(diagnostics # NIL);
- (* Check whether all files exist and parse modules*)
- out.String("Checking files for all builds... "); out.Update;
- file := files;
- WHILE (file # NIL) DO
- IF (file.file = NIL) THEN
- file.file := Files.Old(file.name);
- END;
- IF (file.file = NIL) THEN
- MakeMessage(message, "File # does not exists (Package #)", file.name, file.package.name);
- IF file.IsSourceCode() THEN
- diagnostics.Error(source, file.pos, Diagnostics.Invalid, message);
- ELSE
- diagnostics.Warning(source, file.pos, Diagnostics.Invalid, message);
- END;
- ELSIF file.IsSourceCode() THEN
- file.ParseModule(diagnostics);
- END;
- IF ~KeepFilesOpen THEN
- file.file := NIL;
- END;
- file := file.next;
- END;
- out.String("done."); out.Ln;
- build := 0;
- WHILE (build < LEN(builds)) & (builds[build] # NIL) DO
- out.String("Checking imports of builds "); out.String(builds[build].name); out.String("... "); out.Update;
- error := FALSE;
- builds[build].CheckModules(diagnostics, error);
- out.String("done."); out.Ln;
- INC(build);
- END;
- out.Update;
- END CheckAll;
- (* Show build statistics *)
- PROCEDURE Show*(w : Streams.Writer; details : BOOLEAN);
- VAR
- statistics : Statistics; build, prefix, nofFiles, nofSources : LONGINT; file : File; options : ARRAY 256 OF CHAR;
- diagnostics : Diagnostics.DiagnosticsList;
- error : BOOLEAN;
- BEGIN
- NEW(statistics);
- w.String("Release statistics:"); w.Ln;
- nofFiles := 0; nofSources := 0;
- file := files;
- WHILE (file # NIL) DO
- statistics.AddFile(file);
- file := file.next;
- END;
- w.Int(statistics.nofFiles, 0); w.String(" files ("); w.Int(statistics.nofSources, 0); w.String(" sources)"); w.Ln;
- w.String("Builds: ");
- IF (builds[0].name # "") THEN
- w.Ln;
- FOR build := 0 TO LEN(builds)-1 DO
- IF (builds[build] # NIL) THEN
- w.String(builds[build].name); w.Ln;
- w.Char(Tab); w.String("Includes: ");
- FOR prefix := 0 TO LEN(builds[build].prefixes)-1 DO
- IF (builds[build].prefixes[prefix] # "") THEN
- w.String(" ["); w.String(builds[build].prefixes[prefix]); w.String("]");
- END;
- END;
- w.Ln;
- w.Char(Tab); w.String("Compile: ");
- w.String(builds[build].compiler); w.String(" "); builds[build].GetCompilerOptions(options); w.String(options); w.Ln;
- statistics.Get(nofFiles, nofSources, builds[build].include);
- nofFiles := nofFiles + statistics.nofFilesAll;
- nofSources := nofSources + statistics.nofSourcesAll;
- w.Char(Tab); w.Int(nofFiles, 0); w.String(" files ("); w.Int(nofSources, 0); w.String(" sources)"); w.Ln;
- END;
- END;
- ELSE
- w.String("none"); w.Ln;
- END;
- packages.ToStream(w);
- IF details THEN
- prefix := 0;
- WHILE (prefix < LEN(prefixes)) & (prefixes[prefix] # "") DO
- w.String(prefixes[prefix]); w.Ln;
- nofFiles := 0;
- file := files;
- WHILE (file # NIL) DO
- IF ~IsAll(file.release) & Has(file.release, prefix) THEN
- w.Char(Tab); w.String(file.name); w.Ln;
- INC(nofFiles);
- END;
- file := file.next;
- END;
- w.Char(Tab); w.Int(nofFiles, 0); w.String(" files"); w.Ln;
- INC(prefix);
- END;
- NEW(diagnostics);
- FOR build := 0 TO LEN(builds)-1 DO
- IF (builds[build] # NIL) THEN
- w.String("*** Import statistics for build "); w.String(builds[build].name); w.String(" ***"); w.Ln;
- error := FALSE;
- builds[build].DoChecks(w, diagnostics, error); w.Ln;
- IF ~error THEN
- diagnostics.Reset;
- builds[build].AnalyzeDependencies(w); w.Ln;
- builds[build].ShowDependencies(w);
- w.Ln;
- ELSE
- diagnostics.ToStream(w, Diagnostics.All); w.Ln;
- END;
- END;
- END;
- END;
- w.Update;
- END Show;
- PROCEDURE GetReleaseSet(build : BuildObj; VAR release : Set): BOOLEAN;
- VAR prefix, index, i : LONGINT;
- BEGIN
- SetEmpty(release);
- FOR prefix := 0 TO LEN(build.prefixes)-1 DO
- IF (build.prefixes[prefix] # "") THEN
- index := GetPrefixIndex(build.prefixes[prefix]);
- IF (index >= 0) THEN
- Incl(release, index);
- ELSE
- TRACE("GETRELEASE SET FAILED", build.prefixes[prefix], index);
- RETURN FALSE;
- END;
- END;
- END;
- RETURN TRUE;
- END GetReleaseSet;
- (** Get a reference to a BuildObj by name *)
- PROCEDURE GetBuild*(CONST buildname : ARRAY OF CHAR) : BuildObj;
- VAR build : BuildObj; i : LONGINT;
- BEGIN
- build := NIL;
- i := 0; WHILE (i < LEN(builds)) & (builds[i] # NIL) & (builds[i].name # buildname) DO INC(i); END;
- IF (i < LEN(builds)) THEN
- build := builds[i];
- END;
- RETURN build;
- END GetBuild;
- PROCEDURE AddBuild(build : BuildObj; diagnostics : Diagnostics.Diagnostics) : BOOLEAN;
- VAR error : BOOLEAN; i : LONGINT;
- BEGIN
- ASSERT((build # NIL) & (diagnostics # NIL));
- error := FALSE;
- IF (nofBuilds < LEN(builds)) THEN
- build.builds := SELF;
- builds[nofBuilds] := build;
- FOR i := 0 TO LEN(build.prefixes)-1 DO
- IF (build.prefixes[i] # "") THEN error := error OR ~AddPrefix(build.prefixes[i], diagnostics); END;
- END;
- INC(nofBuilds);
- ELSE
- error := TRUE;
- diagnostics.Error(source, Diagnostics.Invalid, Diagnostics.Invalid, "Maximum number of builds exceeded");
- END;
- RETURN ~error;
- END AddBuild;
- PROCEDURE AddFile(CONST filename : ARRAY OF CHAR; release : Set; package : Package; pos : LONGINT);
- VAR file, f : File; pre, suf : Files.FileName;
- BEGIN
- ASSERT(package # NIL);
- NEW(file, SELF);
- COPY(filename, file.name);
- COPY(filename, file.uppercaseName);
- Strings.UpperCase(file.uppercaseName);
- SplitName(file.name, pre, file.module.name, suf);
- IF (pre = ReleasePrefix) THEN
- INCL(file.flags, HasReleasePrefix);
- END;
- IF (suf = "Mod") OR (suf="Mdf") OR (suf = "Mos") THEN
- INCL(file.flags, SourceCode);
- INC(nofSources);
- INC(package.nofSources);
- END;
- INC(package.nofFiles);
- file.package := package;
- file.release := release;
- file.pos := pos;
- IF (files = NIL) THEN
- files := file;
- ELSE (* append to list *)
- f := files;
- WHILE (f.next # NIL) DO f := f.next; END;
- f.next := file;
- file.prev := f;
- END;
- INC(nofFiles);
- END AddFile;
- PROCEDURE FindFile(CONST filename : ARRAY OF CHAR) : File;
- VAR file : File;
- BEGIN
- file := files;
- WHILE (file # NIL) & (file.name # filename) DO file := file.next; END;
- RETURN file;
- END FindFile;
- PROCEDURE FindFileCheckCase(CONST filename : ARRAY OF CHAR; VAR caseEqual : BOOLEAN) : File;
- VAR result, file : File; fn : Name;
- BEGIN
- result := NIL;
- COPY(filename, fn); Strings.UpperCase(fn);
- file := files;
- WHILE (file # NIL) & (result = NIL) DO
- IF (filename = file.name) THEN
- caseEqual := TRUE;
- result := file;
- ELSIF (fn = file.uppercaseName) THEN
- caseEqual := FALSE;
- result := file;
- END;
- file := file.next;
- END;
- RETURN result;
- END FindFileCheckCase;
- (* Called by the Parser after parsing is finished *)
- PROCEDURE Initialize(diagnostics : Diagnostics.Diagnostics; VAR error : BOOLEAN);
- VAR build, package : LONGINT; message : ARRAY 256 OF CHAR;
- BEGIN
- ASSERT(diagnostics # NIL);
- FOR build := 0 TO LEN(builds)-1 DO
- IF (builds[build] # NIL) THEN
- IF GetReleaseSet(builds[build], builds[build].include) THEN
- builds[build].files := files;
- builds[build].packages := packages;
- IF (builds[build].excludedPackages # NIL) THEN
- FOR package := 0 TO LEN(builds[build].excludedPackages)-1 DO
- IF (packages.FindPackage(builds[build].excludedPackages[package]^) = NIL) THEN
- error := TRUE;
- MakeMessage(message, "Excluded package '#' in build '#' does not exist",
- builds[build].excludedPackages[package]^,
- builds[build].name);
- diagnostics.Error(source, Diagnostics.Invalid, Diagnostics.Invalid, message);
- END;
- END;
- END;
- END;
- END;
- END;
- END Initialize;
- END Builds;
- CONST
- (* Tokens*)
- PACKAGE = "PACKAGE";
- ARCHIVE = "ARCHIVE";
- SOURCE = "SOURCE";
- DESCRIPTION = "DESCRIPTION";
- OPENSECTION = "{";
- CLOSESECTION = "}";
- SEPARATOR = ",";
- ENDSECTION = "END";
- HEADER = "HEADER";
- VERSION = "VERSION";
- BUILDS = "BUILDS";
- INCLUDE="INCLUDE";
- tIMPORT = "IMPORT";
- COMPILER = "COMPILER";
- COMPILEOPTIONS = "COMPILEOPTIONS";
- LINKER = "LINKER";
- LINKEROPTIONS = "LINKEROPTIONS";
- TARGET = "TARGET";
- EXTENSION="EXTENSION";
- SYMBOLEXTENSION="SYMBOLEXTENSION";
- PATH="PATH";
- EXCLUDEPACKAGES = "EXCLUDEPACKAGES";
- DISABLED = "DISABLED";
- TYPE
- Token = ARRAY 256 OF CHAR;
- Scanner = OBJECT
- VAR
- source: Name;
- reader : Streams.Reader;
- diagnostics : Diagnostics.Diagnostics;
- error : BOOLEAN;
- peekMode, peekBufferValid : BOOLEAN;
- peekToken : ARRAY 256 OF CHAR;
- peekError : BOOLEAN;
- pos : LONGINT;
- name : ARRAY 256 OF CHAR;
- PROCEDURE Error(pos : LONGINT; CONST msg, par1, par2 : ARRAY OF CHAR);
- VAR message : ARRAY 128 OF CHAR;
- BEGIN
- error := TRUE;
- MakeMessage(message, msg, par1, par2);
- diagnostics.Error(source, pos, Diagnostics.Invalid, message);
- END Error;
- PROCEDURE Check(CONST token : Token) : BOOLEAN;
- VAR temp : Token;
- BEGIN
- IF Get(temp) & (temp = token) THEN
- RETURN TRUE
- ELSE
- Error(reader.Pos(), "Expected '#' token", token, "");
- RETURN FALSE;
- END;
- END Check;
- PROCEDURE IsIdentifier(CONST token : ARRAY OF CHAR) : BOOLEAN;
- BEGIN
- RETURN
- (token # PACKAGE) & (token # ARCHIVE) & (token # SOURCE) & (token # DESCRIPTION) &
- (token # OPENSECTION) & (token # CLOSESECTION) & (token # ENDSECTION) &
- (token # SEPARATOR) & (token # BUILDS) & (token # HEADER) & (token # VERSION) &
- (token # INCLUDE) & (token # COMPILER) & (token # COMPILEOPTIONS) & (token # TARGET) & (token # EXTENSION) & (token # SYMBOLEXTENSION) &
- (token # PATH) & (token # EXCLUDEPACKAGES);
- END IsIdentifier;
- PROCEDURE GetIdentifier(VAR identifier : ARRAY OF CHAR) : BOOLEAN;
- BEGIN
- IF Get(identifier) THEN
- IF IsIdentifier(identifier) THEN
- RETURN TRUE;
- ELSE
- Error(pos, "Identifier expected but found token #", identifier, "");
- END;
- END;
- RETURN FALSE;
- END GetIdentifier;
- PROCEDURE Peek(VAR token : ARRAY OF CHAR);
- BEGIN
- IF (peekMode = FALSE) THEN
- IF Get(token) THEN
- peekMode := TRUE;
- peekError := FALSE;
- COPY(token, peekToken);
- ELSE
- peekError := TRUE;
- COPY("", peekToken);
- END;
- END;
- COPY(peekToken, token);
- END Peek;
- PROCEDURE Get(VAR token : ARRAY OF CHAR) : BOOLEAN;
- VAR delimiter, ch : CHAR; i : LONGINT; useDelimiter : BOOLEAN;
- BEGIN
- IF (peekMode) THEN
- COPY(peekToken, token);
- peekBufferValid := TRUE;
- peekMode := FALSE;
- IF (peekError) THEN
- Error(reader.Pos(), "ERROR", "", "");
- END;
- RETURN ~peekError;
- END;
- name := "";
- SkipComments;
- delimiter := reader.Peek(); useDelimiter := (delimiter = "'") OR (delimiter = '"');
- IF useDelimiter THEN reader.Char(ch); END;
- pos := reader.Pos();
- i := 0;
- REPEAT
- reader.Char(ch); (* Since we skipped the comments and whitespace, ch cannot be "#" or whitespace *)
- IF useDelimiter & (ch = delimiter) OR (ch=0X) THEN
- ELSE
- token[i] := ch; INC(i);
- END;
- IF (~useDelimiter & (ch # "{") & (ch # "}") & (ch # ",")) THEN
- ch := reader.Peek();
- END;
- UNTIL
- (i >= LEN(token)-1) OR ((reader.res = Streams.EOF) & (ch = 0X)) OR
- (~useDelimiter & (IsWhitespace(ch) OR (ch = "#") OR (ch ="{") OR (ch="}") OR (ch = ","))) OR
- (useDelimiter & (ch = delimiter));
- IF (i = 0) & (reader.res = Streams.EOF) THEN (* end of text *)
- RETURN FALSE
- ELSIF (i < LEN(token)) THEN
- token[i] := 0X;
- COPY(token, name);
- RETURN TRUE;
- ELSE
- Error(reader.Pos(), "Token too long", "", "");
- RETURN FALSE;
- END;
- END Get;
- PROCEDURE IsWhitespace(ch : CHAR) : BOOLEAN;
- BEGIN
- RETURN (ch <= " ");
- END IsWhitespace;
- PROCEDURE SkipComments;
- VAR ch : CHAR;
- BEGIN
- reader.SkipWhitespace;
- ch := reader.Peek();
- WHILE (ch = "#") DO reader.SkipLn; reader.SkipWhitespace; ch := reader.Peek(); END;
- END SkipComments;
- PROCEDURE &Init(CONST source: ARRAY OF CHAR; reader : Streams.Reader; diagnostics : Diagnostics.Diagnostics);
- BEGIN
- COPY (source, SELF.source);
- ASSERT((reader # NIL) & (diagnostics # NIL));
- SELF.reader := reader;
- SELF.diagnostics := diagnostics;
- peekMode := FALSE; peekToken := ""; peekError := FALSE;
- error := FALSE;
- END Init;
- END Scanner;
- TYPE
- Parser = OBJECT
- VAR
- scanner : Scanner;
- diagnostics : Diagnostics.Diagnostics;
- log: Streams.Writer;
- error : BOOLEAN;
- currentPackage : Package;
- PROCEDURE Error(pos : LONGINT; CONST msg, par1, par2 : ARRAY OF CHAR);
- VAR message : ARRAY 128 OF CHAR;
- BEGIN
- error := TRUE;
- MakeMessage(message, msg, par1, par2);
- diagnostics.Error(scanner.source, pos, Diagnostics.Invalid, message);
- END Error;
- PROCEDURE Warning(pos : LONGINT; CONST msg, par1, par2 : ARRAY OF CHAR);
- VAR message : ARRAY 128 OF CHAR;
- BEGIN
- MakeMessage(message, msg, par1, par2);
- diagnostics.Warning(scanner.source, pos, Diagnostics.Invalid, message);
- END Warning;
- PROCEDURE IsFilename(CONST token : Token) : BOOLEAN;
- VAR i : LONGINT;
- BEGIN
- i := 1; (* don't allow filenames to start with "." *)
- WHILE (i < LEN(token)) & (token[i] # ".") & (token[i] # 0X) DO INC(i); END;
- RETURN (i < LEN(token)) & (token[i] = ".");
- END IsFilename;
- PROCEDURE Parse(VAR builds : Builds) : BOOLEAN;
- VAR token : Token; v1, v2 : ARRAY 16 OF CHAR;
- BEGIN
- IF builds = NIL THEN NEW(builds) END;
- COPY(scanner.source, builds.source);
- IF ParseHeader(builds) THEN
- IF (builds.version.major = VersionMajor) & (builds.version.minor <= VersionMinor) THEN
- IF ParseImport(builds) THEN (* load included sections *)
- END;
- IF ParseBuilds(builds) THEN END; (* optional *)
- LOOP
- currentPackage := ParsePackageHeader();
- IF (currentPackage # NIL) THEN
- IF builds.packages.Add(currentPackage) THEN
- IF ~ParsePackage(builds, token) THEN
- EXIT;
- END;
- ELSE
- Error(scanner.pos, "Package # is already defined", currentPackage.name, "");
- EXIT;
- END;
- ELSE
- EXIT;
- END;
- scanner.SkipComments;
- IF scanner.reader.Available() < 5 THEN EXIT; END;
- END;
- IF ~error THEN builds.Initialize(diagnostics, error); END;
- ELSE
- VersionToString(VersionMajor, VersionMinor, v1);
- VersionToString(builds.version.major, builds.version.minor, v2);
- Error(Diagnostics.Invalid, "Version mismatch, Release.Mod is version #, tool file is version #", v1, v2);
- END;
- END;
- RETURN ~(error OR scanner.error OR (builds = NIL));
- END Parse;
- (* Import = IMPORT Entries END whereas Entries = FileName *)
- PROCEDURE ParseImport(VAR builds : Builds) : BOOLEAN;
- VAR token : Token; ignore : BOOLEAN; filename: Files.FileName;
- BEGIN
- ASSERT(builds # NIL);
- scanner.Peek(token);
- IF (token = tIMPORT) THEN
- ignore := scanner.Get(token);
- LOOP
- scanner.Peek(token);
- IF (token = ENDSECTION) THEN
- ignore := scanner.Get(token);
- RETURN TRUE;
- ELSIF scanner.IsIdentifier(token) THEN (* filename *)
- IF IsFilename(token) THEN
- COPY(token, filename);
- IF ~ParseBuildFile(filename, builds, log, diagnostics) THEN
- Error(scanner.pos, "Could not include #", filename, "")
- END;
- ELSE
- Error(scanner.pos, "No filename #",token,"");
- RETURN FALSE
- END;
- ignore := scanner.Get(token);
- END;
- END;
- END;
- RETURN FALSE;
- END ParseImport;
- (* Header = HEADER Entries END whereas Entries = VERSION VersionString *)
- PROCEDURE ParseHeader(builds : Builds) : BOOLEAN;
- VAR token : Token; ignore : BOOLEAN;
- PROCEDURE ParseVersionString(CONST string : ARRAY OF CHAR) : BOOLEAN;
- VAR strings : Strings.StringArray;
- BEGIN
- strings := Strings.Split(string, ".");
- IF (LEN(strings) = 2) THEN
- Strings.StrToInt(strings[0]^, builds.version.major);
- Strings.StrToInt(strings[1]^, builds.version.minor);
- RETURN TRUE;
- ELSE
- Error(scanner.pos, "Expected version string major.minor, found #", string, "");
- RETURN FALSE;
- END;
- END ParseVersionString;
- BEGIN
- ASSERT(builds # NIL);
- scanner.Peek(token);
- IF (token = HEADER) THEN
- ignore := scanner.Get(token);
- LOOP
- scanner.Peek(token);
- IF (token = ENDSECTION) THEN
- ignore := scanner.Get(token);
- RETURN TRUE;
- ELSIF (token = VERSION) THEN
- ignore := scanner.Get(token);
- IF scanner.Get(token) THEN
- IF ~ParseVersionString(token) THEN
- Error(scanner.pos, "Invalid version string: #", token, "");
- EXIT;
- END;
- ELSE
- Error(scanner.pos, "Version number expected", "", "");
- EXIT;
- END;
- ELSE
- Error(scanner.pos, "Expected # or # token", HEADER, ENDSECTION);
- EXIT;
- END;
- END;
- ELSE
- Error(scanner.pos, "# section expected", HEADER, "");
- END;
- RETURN FALSE;
- END ParseHeader;
- (* Builds = [ BUILDS {Build} END ] whereas Build = BuildName "{" prefix {" " perfix} "}" *)
- PROCEDURE ParseBuilds(builds : Builds) : BOOLEAN;
- VAR token : Token; build : BuildObj;
- BEGIN
- IF builds = NIL THEN RETURN FALSE END;
- ASSERT(builds # NIL);
- scanner.Peek(token);
- IF (token = BUILDS) THEN
- IF scanner.Get(token) THEN END; (* consume BUILDS *)
- LOOP
- scanner.Peek(token);
- IF (token = ENDSECTION) THEN
- IF scanner.Get(token) THEN END; (* consume token *)
- RETURN TRUE;
- ELSE
- build := ParseBuild();
- IF (build # NIL) THEN
- IF ~builds.AddBuild(build, diagnostics) THEN
- EXIT;
- END;
- ELSE
- EXIT;
- END;
- END;
- END;
- END;
- RETURN FALSE;
- END ParseBuilds;
- (* Build = BuildName "{" prefix {"," perfix} "}" *)
- PROCEDURE ParseBuild() : BuildObj;
- VAR
- token : Token; prefix : LONGINT;
- string : ARRAY 256 OF CHAR; stringArray : Strings.StringArray;
- compilerSet, compileOptionsSet, linkerSet, linkOptionsSet, targetSet, includeSet, excludePackagesSet, extensionSet, symbolsSet, pathSet, disabledSet : BOOLEAN;
- build : BuildObj;
- PROCEDURE CheckOptions;
- BEGIN
- IF ~includeSet THEN Warning(scanner.pos, "# not set in build #", INCLUDE, build.name); END;
- IF ~compilerSet THEN Warning(scanner.pos, "# not set in build #", COMPILER, build.name); END;
- IF ~compileOptionsSet THEN Warning(scanner.pos, "# not set in build #", COMPILEOPTIONS, build.name); END;
- IF ~targetSet THEN Warning(scanner.pos, "# not set in build #", TARGET, build.name); END;
- IF ~extensionSet THEN Warning(scanner.pos, "# not set in build #", EXTENSION, build.name); END;
- IF ~symbolsSet THEN (* no warning since optional *) END;
- IF ~pathSet THEN Warning(scanner.pos, "# not set in build #", PATH, build.name); END;
- IF ~disabledSet THEN Warning(scanner.pos, "# not set in build #", DISABLED, build.name); END;
- END CheckOptions;
- BEGIN
- NEW(build);
- compilerSet := FALSE; compileOptionsSet := FALSE; linkerSet := FALSE; linkOptionsSet := FALSE; targetSet := FALSE; includeSet := FALSE;
- extensionSet := FALSE; symbolsSet := FALSE; pathSet := FALSE; excludePackagesSet := FALSE; disabledSet := FALSE;
- IF scanner.GetIdentifier(build.name) THEN
- build.position := scanner.pos;
- IF scanner.Check(OPENSECTION) THEN
- LOOP
- IF scanner.Get(token) THEN
- IF (token = CLOSESECTION) THEN
- CheckOptions;
- RETURN build;
- ELSIF (token = COMPILER) THEN
- IF compilerSet THEN Error(scanner.pos, "# already set in build #", COMPILER, build.name); ELSE compilerSet := TRUE; END;
- IF ~scanner.GetIdentifier(build.compiler) THEN
- (* continue *)
- END;
- ELSIF (token = COMPILEOPTIONS) THEN
- IF compileOptionsSet THEN Error(scanner.pos, "# already set in build #", COMPILEOPTIONS, build.name); ELSE compileOptionsSet := TRUE; END;
- IF ~scanner.GetIdentifier(build.compileOptions) THEN
- (* continue *)
- END;
- ELSIF (token = LINKER) THEN
- IF linkerSet THEN Error(scanner.pos, '# already set in build #', LINKER, build.name); ELSE linkerSet := TRUE; END;
- IF ~scanner.GetIdentifier(build.linker) THEN
- (* continue *)
- END;
- ELSIF (token = LINKEROPTIONS) THEN
- IF linkOptionsSet THEN Error(scanner.pos, '# already set in build #', LINKEROPTIONS, build.name); ELSE linkOptionsSet := TRUE; END;
- IF ~scanner.GetIdentifier(build.linkOptions) THEN
- (* continue *)
- END;
- ELSIF (token = TARGET) THEN
- IF targetSet THEN Error(scanner.pos, "# already set in build #", TARGET, build.name); ELSE targetSet := TRUE; END;
- IF ~scanner.GetIdentifier(build.target) THEN
- (* continue *)
- END;
- ELSIF (token = EXTENSION) THEN
- IF extensionSet THEN Error(scanner.pos, "# already set in build #", EXTENSION, build.name); ELSE extensionSet := TRUE; END;
- IF ~scanner.GetIdentifier(build.extension) THEN
- (* continue *)
- END;
- ELSIF (token = SYMBOLEXTENSION) THEN
- IF symbolsSet THEN Error(scanner.pos, "# already set in build #", SYMBOLEXTENSION, build.name); ELSE symbolsSet := TRUE; END;
- IF ~scanner.GetIdentifier(build.symbolFileExtension) THEN
- (* continue *)
- END;
- ELSIF (token = PATH) THEN
- IF pathSet THEN Error(scanner.pos, "# already set in build #", PATH, build.name); ELSE pathSet := TRUE; END;
- IF ~scanner.GetIdentifier(build.path) THEN
- (* continue *)
- END;
- ELSIF (token = INCLUDE) THEN
- IF includeSet THEN Error(scanner.pos, "# already set in build #", INCLUDE, build.name); ELSE includeSet := TRUE; END;
- IF scanner.GetIdentifier(string) THEN
- stringArray := Strings.Split(string, " ");
- prefix := 0;
- IF LEN(stringArray) <= LEN(build.prefixes) THEN
- FOR prefix := 0 TO LEN(stringArray)-1 DO
- COPY(stringArray[prefix]^, build.prefixes[prefix]);
- END;
- ELSE
- Error(scanner.pos, "Maximum number of prefixes exceeded.", "", "");
- END;
- END;
- ELSIF (token = EXCLUDEPACKAGES) THEN
- IF excludePackagesSet THEN Error(scanner.pos, "# already set in build #", EXCLUDEPACKAGES, build.name);
- ELSE excludePackagesSet := TRUE;
- END;
- IF scanner.GetIdentifier(string) THEN
- Strings.TrimWS(string);
- IF (string # "") THEN
- build.excludedPackages := Strings.Split(string, " ");
- END;
- END;
- ELSIF (token = DISABLED) THEN
- IF disabledSet THEN Error(scanner.pos, "# already set in build #", DISABLED, build.name);
- ELSE disabledSet := TRUE;
- END;
- IF scanner.GetIdentifier(string) THEN
- Strings.TrimWS(string);
- Strings.UpperCase(string);
- IF (string = "TRUE") THEN
- build.disabled := TRUE;
- ELSIF (string = "FALSE") THEN
- build.disabled := FALSE;
- ELSE
- Warning(scanner.pos, "Wrong value for # in build #", DISABLED, build.name);
- build.disabled := TRUE;
- END;
- ELSE
- (* error reported by scanner.GetIdentifier *)
- END;
- ELSE
- IF includeSet & compilerSet & targetSet & extensionSet & pathSet THEN
- Error(scanner.pos, "Expected # or # token", CLOSESECTION, EXCLUDEPACKAGES);
- ELSE
- Error(scanner.pos, "Expected INCLUDE, COMPILER or COMPILEOPTIONS token", "", "");
- END;
- EXIT;
- END;
- ELSE
- EXIT;
- END;
- END;
- END;
- END;
- RETURN NIL;
- END ParseBuild;
- (* PackageHeader = PACKAGE PackageName ARCHIVE ArchiveName SOURCE SourceArchiveName DESCRIPTION description *)
- PROCEDURE ParsePackageHeader() : Package;
- VAR package : Package; name, archive, source : ARRAY 32 OF CHAR; description : ARRAY 256 OF CHAR; position : LONGINT;
- BEGIN
- package := NIL;
- IF scanner.Check(PACKAGE) THEN
- position := scanner.pos;
- IF scanner.GetIdentifier(name) &
- scanner.Check(ARCHIVE) & scanner.GetIdentifier(archive) &
- scanner.Check(SOURCE) & scanner.GetIdentifier(source) &
- scanner.Check(DESCRIPTION) & scanner.GetIdentifier(description)
- THEN
- NEW(package, name, archive, source, description, position);
- END;
- END;
- RETURN package;
- END ParsePackageHeader;
- (* Package = { FileList | Prefix "{" FileList "}" }
- Prefix = prefix {"," prefix}
- FileList = filename {" " filename} *)
- PROCEDURE ParsePackage(builds : Builds; VAR token : Token) : BOOLEAN;
- VAR currentRelease : Set; index : LONGINT; pos : LONGINT; nbr : ARRAY 8 OF CHAR; caseEqual : BOOLEAN; file : File;
- BEGIN
- SetAll(currentRelease);
- LOOP
- IF scanner.Get(token) THEN
- index := builds.GetPrefixIndex(token);
- IF (index >= 0) THEN
- IF IsAll(currentRelease) THEN
- SetEmpty(currentRelease);
- IF ~ParseBuildPrefixes(builds, token, currentRelease, pos) THEN
- RETURN FALSE;
- END;
- ELSE
- Strings.IntToStr(pos, nbr);
- Error(scanner.pos, "Expected closing brace for tag at position #", nbr, "");
- RETURN FALSE;
- END;
- ELSIF (token = CLOSESECTION) THEN
- IF ~IsAll(currentRelease) THEN
- SetAll(currentRelease);
- ELSE
- Error(scanner.pos, "No matching opening bracket", "", "");
- RETURN FALSE;
- END;
- ELSIF (token = ENDSECTION) THEN
- RETURN TRUE;
- ELSIF scanner.IsIdentifier(token) THEN (* filename *)
- IF IsFilename(token) THEN
- file := builds.FindFileCheckCase(token, caseEqual);
- IF (file # NIL) THEN
- IF caseEqual THEN
- Warning(scanner.pos, "Duplicate file found: #", token, "");
- ELSE
- Warning(scanner.pos, "Same file name with different case found: # vs #", token, file.name);
- END;
- END;
- builds.AddFile(token, currentRelease, currentPackage, scanner.pos);
- ELSE
- diagnostics.Warning(scanner.source, scanner.pos, Diagnostics.Invalid, "Expected filename (not file extension?)");
- END;
- ELSE
- Error(scanner.pos, "Expected identifier, found #", token, "");
- END;
- ELSE
- EXIT;
- END;
- END;
- RETURN IsAll(currentRelease);
- END ParsePackage;
- PROCEDURE ParseBuildPrefixes(builds : Builds; VAR token : Token; VAR release : Set; VAR pos : LONGINT) : BOOLEAN;
- VAR index : LONGINT; message : ARRAY 128 OF CHAR;
- BEGIN
- index := builds.GetPrefixIndex(token);
- IF (index >= 0) THEN
- Incl(release,index);
- ELSE
- MakeMessage(message, "Unknown build prefix #", token, "");
- diagnostics.Warning(scanner.source, scanner.pos, Diagnostics.Invalid, message);
- END;
- IF scanner.Get(token) THEN
- IF (token = OPENSECTION) THEN
- RETURN TRUE;
- ELSIF (token = SEPARATOR) THEN
- RETURN scanner.Get(token) & ParseBuildPrefixes(builds, token, release, pos);
- ELSE
- Error(scanner.pos, "Expected '{' or ',' token", "", "");
- RETURN FALSE;
- END;
- ELSE
- RETURN FALSE;
- END;
- END ParseBuildPrefixes;
- PROCEDURE &Init(scanner : Scanner; log: Streams.Writer; diagnostics : Diagnostics.Diagnostics);
- BEGIN
- ASSERT((scanner # NIL) & (diagnostics # NIL));
- SELF.scanner := scanner;
- SELF.diagnostics := diagnostics;
- SELF.log := log
- END Init;
- END Parser;
- PROCEDURE SetAll(VAR s: Set);
- VAR i: LONGINT;
- BEGIN
- FOR i := 0 TO LEN(s)-1 DO
- s[i] := {MIN(SET)..MAX(SET)};
- END;
- END SetAll;
-
- PROCEDURE SetEmpty(VAR s: Set);
- VAR i: LONGINT;
- BEGIN
- FOR i := 0 TO LEN(s)-1 DO
- s[i] := {};
- END;
- END SetEmpty;
-
- PROCEDURE IsAll(CONST s: Set): BOOLEAN;
- VAR i: LONGINT;
- BEGIN
- FOR i := 0 TO LEN(s)-1 DO
- IF s[i] # {MIN(SET)..MAX(SET)} THEN RETURN FALSE END;
- END;
- RETURN TRUE;
- END IsAll;
-
- PROCEDURE Incl(VAR s: Set; i: LONGINT);
- BEGIN
- INCL(s[i DIV SetSize], i MOD SetSize);
- END Incl;
- PROCEDURE Excl(VAR s: Set; i: LONGINT);
- BEGIN
- EXCL(s[i DIV SetSize], i MOD SetSize);
- END Excl;
-
- PROCEDURE Has(CONST s: Set; i: LONGINT): BOOLEAN;
- BEGIN
- RETURN i MOD SetSize IN s[i DIV SetSize];
- END Has;
-
- PROCEDURE GetModuleInfo(
- in : Streams.Reader;
- VAR mi : ModuleInfo;
- CONST source, filename : ARRAY OF CHAR;
- errorPosition : LONGINT;
- diagnostics : Diagnostics.Diagnostics;
- VAR error : BOOLEAN);
- PROCEDURE SkipComments(in : Streams.Reader);
- VAR level, oldLevel, oldPosition : LONGINT; ch, nextCh : CHAR;
- BEGIN
- ASSERT((in # NIL) & (in.CanSetPos()));
- level := 0;
- REPEAT
- ASSERT(level >= 0);
- in.SkipWhitespace;
- oldLevel := level;
- oldPosition := in.Pos();
- in.Char(ch); nextCh := in.Peek();
- IF (ch = "(") & (nextCh = "*") THEN
- INC(level); in.Char(ch);
- ELSIF (level > 0) & (ch = "*") & (nextCh = ")") THEN
- DEC(level); in.Char(ch);
- ELSIF (level = 0) THEN
- in.SetPos(oldPosition);
- END;
- UNTIL ((level = 0) & (oldLevel = 0)) OR (in.res # Streams.Ok);
- END SkipComments;
- PROCEDURE SkipProperties (in : Streams.Reader);
- VAR ch : CHAR;
- BEGIN
- in.SkipWhitespace;
- ch := in.Peek();
- IF ch = "{" THEN
- in.Char(ch);
- REPEAT
- in.Char(ch);
- UNTIL (ch = "}") OR (in.res # Streams.Ok)
- END;
- END SkipProperties;
- PROCEDURE GetIdentifier(in : Streams.Reader; VAR identifier : ARRAY OF CHAR);
- VAR ch : CHAR; i : LONGINT;
- BEGIN
- ASSERT(in # NIL);
- i := 0;
- ch := in.Peek();
- WHILE (('a' <= ch) & (ch <= 'z')) OR (('A' <= ch) & (ch <= 'Z')) OR (('0' <= ch) & (ch <= '9')) OR (ch = "_") & (i < LEN(identifier) - 1) DO
- in.Char(identifier[i]); INC(i);
- ch := in.Peek();
- END;
- identifier[i] := 0X;
- END GetIdentifier;
- PROCEDURE GetContext(in : Streams.Reader; ch1, ch2 : CHAR; VAR context : ARRAY OF CHAR; diagnostics : Diagnostics.Diagnostics; VAR error : BOOLEAN);
- VAR message : ARRAY 512 OF CHAR; ch : CHAR;
- BEGIN
- ASSERT((in # NIL) & (diagnostics # NIL));
- SkipComments(in);
- ch := in.Peek();
- IF (Strings.UP(ch) = ch1) THEN
- in.Char(ch); in.Char(ch);
- IF Strings.UP(ch) = ch2 THEN
- SkipComments(in);
- GetIdentifier(in, context); (* ignore context identifier *)
- IF (context = "") THEN
- error := TRUE;
- MakeMessage(message, "Context identifier missing in file #", filename, "");
- diagnostics.Error(source, errorPosition, Diagnostics.Invalid, message);
- END;
- SkipComments(in);
- ELSE
- error := TRUE;
- MakeMessage(message, "Expected 'IN' keyword in file #", filename, "");
- diagnostics.Error(source, errorPosition, Diagnostics.Invalid, message);
- END;
- END;
- END GetContext;
- PROCEDURE GetModuleNameAndContext(in : Streams.Reader; VAR name, context : Name; diagnostics : Diagnostics.Diagnostics; VAR error : BOOLEAN);
- VAR token, message : ARRAY 512 OF CHAR; ch : CHAR;
- BEGIN
- ASSERT((in # NIL) & (diagnostics # NIL) & (~error));
- name := ""; COPY(Modules.DefaultContext, context);
- SkipComments(in);
- in.SkipWhitespace; in.String(token); Strings.UpperCase(token);
- IF (token = "MODULE") OR (token = "CELLNET") THEN
- SkipComments(in);
- SkipProperties(in);
- SkipComments(in);
- GetIdentifier(in, name);
- IF (name # "") THEN
- SkipComments(in);
- GetContext(in, "I", "N", context, diagnostics, error);
- in.Char(ch);
- IF ~error & (ch # ";") THEN
- error := TRUE;
- MakeMessage(message, "Expected semicolon after module identifier in file #", filename, "");
- diagnostics.Error(source, errorPosition, Diagnostics.Invalid, message);
- END;
- ELSE
- error := TRUE;
- MakeMessage(message, "Module identifier missing in file #", filename, "");
- diagnostics.Error(source, errorPosition, Diagnostics.Invalid, message);
- END;
- ELSE
- error := TRUE;
- MakeMessage(message, "MODULE keyword missing in file #, first token is #", filename, token);
- diagnostics.Error(source, errorPosition, Diagnostics.Invalid, message);
- END;
- END GetModuleNameAndContext;
- PROCEDURE GetImport(in : Streams.Reader; VAR import, context : ARRAY OF CHAR; diagnostics : Diagnostics.Diagnostics; VAR error : BOOLEAN);
- VAR message : ARRAY 512 OF CHAR;
- BEGIN
- ASSERT((in # NIL) & (diagnostics # NIL));
- SkipComments(in);
- GetIdentifier(in, import);
- IF (import # "") THEN
- GetContext(in, ':', '=', import, diagnostics, error);
- IF ~error THEN GetContext(in, "I", "N", context, diagnostics, error); END;
- ELSE
- error := TRUE;
- MakeMessage(message, "Identifier expected in import section of file #", filename, "");
- diagnostics.Error(source, errorPosition, Diagnostics.Invalid, message);
- END;
- END GetImport;
- PROCEDURE GetImports(in : Streams.Reader; VAR mi : ModuleInfo; diagnostics : Diagnostics.Diagnostics; VAR error : BOOLEAN);
- VAR string, import, context, message : ARRAY 256 OF CHAR; ch : CHAR;
- BEGIN
- ASSERT((in # NIL) & (in.CanSetPos()) & (diagnostics #NIL) & (~error));
- SkipComments(in);
- GetIdentifier(in, string); Strings.UpperCase(string);
- IF (string = "IMPORT") THEN
- LOOP
- COPY(mi.context, context);
- GetImport(in, import, context, diagnostics, error);
- IF ~error THEN
- IF (import = "SYSTEM") OR (import = "system") THEN
- INCL(mi.flags, ImportsSystem);
- ELSIF (mi.nofImports < LEN(mi.imports)) THEN
- CreateContext(import, context);
- COPY(import, mi.imports[mi.nofImports]);
- INC(mi.nofImports);
- ELSE
- error := TRUE;
- MakeMessage(message, "Maximum number of supported imports exceeded in module #", filename, "");
- diagnostics.Error(source, Diagnostics.Invalid, Diagnostics.Invalid, message);
- EXIT;
- END;
- SkipComments(in);
- in.Char(ch);
- IF (ch = ",") THEN
- (* continue *)
- ELSIF (ch = ";") THEN
- EXIT;
- ELSE
- error := TRUE;
- MakeMessage(message, "Parsing import section of module # failed", filename, "");
- diagnostics.Error(source, errorPosition, Diagnostics.Invalid, message);
- EXIT;
- END;
- ELSE
- EXIT;
- END;
- END;
- ELSE
- mi.nofImports := 0;
- END;
- END GetImports;
- BEGIN
- ASSERT((in # NIL) & (diagnostics # NIL));
- error := FALSE;
- GetModuleNameAndContext(in, mi.name, mi.context, diagnostics, error);
- IF ~error THEN
- ASSERT(mi.nofImports = 0);
- GetImports(in, mi, diagnostics, error);
- END;
- END GetModuleInfo;
- PROCEDURE VersionToString(major, minor : LONGINT; VAR string : ARRAY OF CHAR);
- VAR temp : ARRAY 16 OF CHAR;
- BEGIN
- Strings.IntToStr(major, string); Strings.Append(string, ".");
- Strings.IntToStr(minor, temp); Strings.Append(string, temp);
- END VersionToString;
- (** SplitName - Split a filename into prefix, middle and suffix, seperated by ".". The prefix may contain dots ("."). *)
- PROCEDURE SplitName(CONST name: ARRAY OF CHAR; VAR pre, mid, suf: ARRAY OF CHAR);
- VAR i, j, d0, d1: LONGINT;
- BEGIN
- i := 0; d0 := -1; d1 := -1;
- WHILE name[i] # 0X DO
- IF name[i] = "." THEN
- d0 := d1;
- d1 := i
- END;
- INC(i)
- END;
- i := 0;
- IF (d0 # -1) & (d1 # d0) THEN (* have prefix *)
- WHILE i # d0 DO pre[i] := name[i]; INC(i) END
- ELSE
- d0 := -1
- END;
- pre[i] := 0X;
- i := d0+1; j := 0;
- WHILE (name[i] # 0X) & (i # d1) DO mid[j] := name[i]; INC(i); INC(j) END;
- mid[j] := 0X; j := 0;
- IF d1 # -1 THEN
- i := d1+1;
- WHILE name[i] # 0X DO suf[j] := name[i]; INC(i); INC(j) END
- END;
- suf[j] := 0X
- END SplitName;
- PROCEDURE CreateContext (VAR name: ARRAY OF CHAR; CONST context: ARRAY OF CHAR);
- VAR temp: Name;
- BEGIN
- IF context # Modules.DefaultContext THEN
- COPY (name, temp);
- COPY (context, name);
- Strings.Append (name, ".");
- Strings.Append (name, temp);
- END;
- END CreateContext;
- PROCEDURE MakeMessage(VAR msg : ARRAY OF CHAR; CONST string, par0, par1 : ARRAY OF CHAR);
- VAR count, m, i, j : LONGINT; par : ARRAY 128 OF CHAR;
- BEGIN
- i := 0; m := 0; j := 0;
- FOR count := 0 TO 3 DO
- WHILE (m < LEN(msg)-1) & (i < LEN(string)) & (string[i] # "#") & (string[i] # 0X) DO
- msg[m] := string[i]; INC(m); INC(i);
- END;
- IF (string[i] = "#") THEN
- INC(i); j := 0;
- IF (count = 0) THEN COPY(par0, par);
- ELSIF (count = 1) THEN COPY(par1, par)
- ELSE par[0] := 0X;
- END;
- WHILE (m < LEN(msg)-1) & (j < LEN(par)) & (par[j] # 0X) DO
- msg[m] := par[j]; INC(m); INC(j);
- END;
- END;
- END;
- msg[m] := 0X;
- END MakeMessage;
- PROCEDURE GetReader(file : File; diagnostics : Diagnostics.Diagnostics) : Streams.Reader;
- VAR
- reader : Streams.Reader;
- fileReader : Files.Reader; ch1, ch2 : CHAR; offset : LONGINT;
- text : Texts.Text; textReader : TextUtilities.TextReader; format, res : LONGINT;
- message : ARRAY 256 OF CHAR;
- BEGIN
- ASSERT((file # NIL) & (diagnostics # NIL));
- reader := NIL;
- IF OptimizedLoads THEN
- IF (file.file = NIL) THEN file.file := Files.Old(file.name); END;
- IF (file.file # NIL) THEN
- Files.OpenReader(fileReader, file.file, 0);
- fileReader.Char(ch1);
- fileReader.Char(ch2);
- IF (ch1= 0F0X) & (ch2 = 01X) THEN (* formatted Oberon text, skip formatting information *)
- fileReader.RawLInt(offset);
- fileReader.SetPos(offset);
- ELSE
- fileReader.SetPos(0);
- END;
- reader := fileReader;
- ELSE
- MakeMessage(message, "Could not open file #", file.name, "");
- diagnostics.Error(file.name, file.pos, Diagnostics.Invalid, message);
- END;
- END;
- IF (reader = NIL) THEN
- NEW(text);
- TextUtilities.LoadAuto(text, file.name, format, res);
- IF (res = 0) THEN
- NEW(textReader, text);
- reader := textReader;
- ELSE
- MakeMessage(message, "Could not open file # (Package = )", file.name, "");
- diagnostics.Error(file.name, file.pos, Diagnostics.Invalid, message);
- END;
- END;
- RETURN reader;
- END GetReader;
- PROCEDURE CallCommand(CONST command, arguments : ARRAY OF CHAR; context : Commands.Context);
- VAR
- newContext : Commands.Context; arg : Streams.StringReader;
- msg : ARRAY 128 OF CHAR; res : LONGINT;
- BEGIN
- NEW(arg, 256); arg.Set(arguments);
- NEW(newContext, NIL, arg, context.out, context.error, context.caller);
- Commands.Activate(command, newContext, {Commands.Wait}, res, msg);
- IF (res #Commands.Ok) THEN
- context.error.String(msg); context.error.Ln;
- END;
- END CallCommand;
- PROCEDURE ParseBuildDescription*(text : Texts.Text; CONST source: ARRAY OF CHAR; VAR builds : Builds; log: Streams.Writer; diagnostics : Diagnostics.Diagnostics) : BOOLEAN;
- VAR
- parser : Parser; scanner : Scanner;
- reader : Streams.StringReader;
- buffer : POINTER TO ARRAY OF CHAR;
- length : LONGINT;
- BEGIN
- ASSERT((text # NIL) & (diagnostics # NIL));
- text.AcquireRead;
- length := text.GetLength();
- text.ReleaseRead;
- IF length = 0 THEN length := 1 END;
- NEW(buffer, length);
- TextUtilities.TextToStr(text, buffer^);
- NEW(reader, LEN(buffer)); reader.SetRaw(buffer^, 0, LEN(buffer));
- NEW(scanner, source, reader, diagnostics);
- NEW(parser, scanner, log, diagnostics);
- RETURN parser.Parse(builds);
- END ParseBuildDescription;
- PROCEDURE ParseBuildFile*(
- CONST filename : Files.FileName;
- VAR builds : Builds;
- log: Streams.Writer;
- diagnostics : Diagnostics.Diagnostics
- ) : BOOLEAN;
- VAR text : Texts.Text; format, res : LONGINT; message : ARRAY 256 OF CHAR;
- BEGIN
- log.String("Loading package description file "); log.String(filename); log.String(" ... ");
- log.Update;
- NEW(text);
- TextUtilities.LoadAuto(text, filename, format, res);
- IF (res = 0) THEN
- IF ParseBuildDescription(text, filename, builds, log, diagnostics) THEN
- log.String("done."); log.Ln;
- RETURN TRUE;
- ELSE
- log.Ln;
- RETURN FALSE;
- END;
- ELSE
- builds := NIL;
- MakeMessage(message, "Could not open file #", filename, "");
- diagnostics.Error("", Diagnostics.Invalid, Diagnostics.Invalid, message);
- RETURN FALSE;
- END;
- END ParseBuildFile;
- PROCEDURE ParseText(
- text : Texts.Text;
- CONST source: ARRAY OF CHAR;
- pos: LONGINT; (* ignore *)
- CONST pc, opt: ARRAY OF CHAR;
- log: Streams.Writer; diagnostics : Diagnostics.Diagnostics; VAR error: BOOLEAN);
- VAR
- options : POINTER TO ARRAY OF CHAR;
- builds : Builds;
- BEGIN
- NEW(options,LEN(opt));
- ASSERT((text # NIL) & (diagnostics # NIL));
- error := ~ParseBuildDescription(text, source, builds, log, diagnostics);
- IF ~error THEN
- COPY(opt, options^);
- Strings.TrimWS(options^);
- IF (builds # NIL) & (options^ = "\check") THEN
- builds.CheckAll(log, diagnostics);
- END;
- END;
- IF error THEN
- log.String(" not done");
- ELSE
- log.String(" done");
- END;
- log.Update;
- END ParseText;
- PROCEDURE CheckBuilds(builds : Builds; nofWorkers : LONGINT; context : Commands.Context; diagnostics : Diagnostics.DiagnosticsList);
- VAR
- build : LONGINT; ignore : Streams.StringWriter; error, ignoreError, checkAll : BOOLEAN;
- PROCEDURE CreateRamDisk(context : Commands.Context) : BOOLEAN;
- VAR res : LONGINT; msg : ARRAY 128 OF CHAR; fs : Files.FileSystem;
- BEGIN
- Commands.Call("FSTools.Mount CHECKBUILDS RamFS 500000 4096", {Commands.Wait}, res, msg);
- IF (res # Commands.Ok) THEN
- context.error.String(msg); context.error.Ln;
- END;
- fs := Files.This("CHECKBUILDS");
- RETURN fs # NIL;
- END CreateRamDisk;
- PROCEDURE UnmountRamDisk(context : Commands.Context);
- VAR res : LONGINT; msg : ARRAY 128 OF CHAR;
- BEGIN
- Commands.Call("FSTools.Unmount CHECKBUILDS", {Commands.Wait}, res, msg);
- IF (res # Commands.Ok) THEN
- context.error.String(msg); context.error.Ln;
- END;
- END UnmountRamDisk;
- PROCEDURE DeleteFiles(CONST pattern : ARRAY OF CHAR);
- VAR
- enum : Files.Enumerator; flags : SET; time, date, size : LONGINT; name : Files.FileName;
- res : LONGINT;
- BEGIN
- NEW(enum); enum.Open(pattern, {});
- WHILE enum.GetEntry(name, flags, time, date, size) DO
- Files.Delete(name, res);
- END;
- enum.Close;
- END DeleteFiles;
- PROCEDURE DoCheckAll(builds : Builds) : BOOLEAN;
- VAR i : LONGINT; all : BOOLEAN;
- BEGIN
- ASSERT(builds # NIL);
- all := TRUE;
- WHILE all & (i < LEN(builds.builds)) & (builds.builds[i] # NIL) DO
- all := builds.builds[i].marked;
- INC(i);
- END;
- RETURN all;
- END DoCheckAll;
- BEGIN
- ASSERT((builds # NIL) & (context # NIL) &(diagnostics # NIL));
- checkAll := DoCheckAll(builds);
- IF checkAll THEN
- builds.CheckAll(context.out, diagnostics);
- END;
- IF (diagnostics.nofErrors = 0) THEN
- IF CreateRamDisk(context) THEN
- IF (diagnostics.nofErrors = 0) THEN
- NEW(ignore, 1);
- WHILE (build < LEN(builds.builds)) & (builds.builds[build] # NIL) DO
- diagnostics.Reset;
- IF ~checkAll OR ~builds.builds[build].disabled THEN
- error := FALSE;
- IF ~checkAll & builds.builds[build].marked THEN
- builds.builds[build].CheckFiles(diagnostics);
- builds.builds[build].CheckModules(diagnostics, error);
- END;
- IF ~error & builds.builds[build].marked THEN
- COPY("CHECKBUILDS:", builds.builds[build].path);
- (*
- COPY("Obt", builds.builds[build].extension);
- COPY("386", builds.builds[build].target);
- *)
- builds.builds[build].Compile(nofWorkers, context.out, context.error, FALSE, diagnostics, ignoreError);
- DeleteFiles("CHECKBUILDS:*.Obw");
- DeleteFiles("CHECKBUILDS:*.Obt");
- DeleteFiles("CHECKBUILDS:*.Obx");
- DeleteFiles("CHECKBUILDS:*.Sym");
- DeleteFiles("CHECKBUILDS:*.Gof");
- ELSE
- diagnostics.ToStream(context.out, Diagnostics.All);
- END;
- ELSE
- context.out.String("Build "); context.out.String(builds.builds[build].name);
- context.out.String(" is disabled."); context.out.Ln;
- END;
- context.out.Update; context.error.Update;
- INC(build);
- END;
- diagnostics.Reset; (* all error messages already shown *)
- ELSE
- diagnostics.ToStream(context.error, Diagnostics.All); context.error.Ln;
- END;
- UnmountRamDisk(context);
- ELSE
- context.error.String("Could not create RAM disk"); context.error.Ln;
- END;
- ELSE
- diagnostics.ToStream(context.out, Diagnostics.All);
- END;
- END CheckBuilds;
- (** Find files that match the filemask but are not listed in the build description file *)
- PROCEDURE CheckFiles*(context : Commands.Context); (** [Options] filemask ~*)
- VAR
- options : Options.Options; diagnostics : Diagnostics.Diagnostics;
- builds : Builds;
- buildFile, mask, fullname, path, filename : Files.FileName;
- enumerator : Files.Enumerator;
- flags : SET;
- ignore : LONGINT;
- BEGIN
- NEW(options);
- options.Add("f", "file", Options.String);
- IF options.Parse(context.arg, context.error) THEN
- COPY(DefaultPackagesFile, buildFile);
- IF options.GetString("file", buildFile) THEN END;
- IF context.arg.GetString(mask) THEN
- NEW(diagnostics);
- IF ParseBuildFile(buildFile, builds, context.out, diagnostics) THEN
- NEW(enumerator); enumerator.Open(mask, {});
- WHILE enumerator.GetEntry(fullname, flags, ignore, ignore, ignore) DO
- IF ~(Files.Directory IN flags) THEN
- Files.SplitPath(fullname, path, filename);
- IF (builds.FindFile(filename) = NIL) THEN
- context.out.String(filename); context.out.Ln;
- END;
- END;
- END;
- enumerator.Close;
- END;
- ELSE
- context.error.String("Expected mask argument."); context.error.Ln;
- END;
- END;
- END CheckFiles;
- (** Find the text position where the specified file could be inserted into the build description file *)
- PROCEDURE FindPosition*(context : Commands.Context); (** [Options] buildname modulename ~ *)
- VAR
- builds : Builds; build : BuildObj;
- buildFile, filename : Files.FileName; buildName : Name;
- diagnostics : Diagnostics.DiagnosticsList;
- position : LONGINT;
- options : Options.Options;
- BEGIN
- NEW(options);
- options.Add("f", "file", Options.String);
- IF options.Parse(context.arg, context.error) THEN
- COPY(DefaultPackagesFile, buildFile);
- IF options.GetString("file", buildFile) THEN END;
- context.arg.SkipWhitespace; context.arg.String(buildName);
- context.arg.SkipWhitespace; context.arg.String(filename);
- IF (buildName # "") & (filename # "") THEN
- NEW(diagnostics);
- IF ParseBuildFile(buildFile, builds, context.out, diagnostics) THEN
- build := builds.GetBuild(buildName);
- IF (build # NIL) THEN
- position := build.FindPosition(filename, diagnostics);
- IF (position # -1) THEN
- context.out.String("First text position where the file "); context.out.String(filename);
- context.out.String(" could be inserted: "); context.out.Int(position, 0); context.out.Ln;
- ELSE
- diagnostics.ToStream(context.error, {0..31}); context.error.Ln;
- END;
- ELSE
- context.error.String("Build "); context.error.String(buildName); context.error.String(" not found");
- context.error.Update;
- END;
- ELSE
- diagnostics.ToStream(context.error, {0..31}); context.error.Ln;
- END;
- ELSE
- context.error.String("Usage: Release.FindPosition Release.Tool [options] buildname filename ~ ");
- END;
- END;
- END FindPosition;
- (** Analyze the builds *)
- PROCEDURE Analyze*(context : Commands.Context); (** [Options] ~ *)
- VAR filename : Files.FileName; builds : Builds; diagnostics : Diagnostics.DiagnosticsList; options : Options.Options;
- BEGIN
- NEW(options);
- options.Add("d", "details", Options.Flag);
- options.Add("f", "file", Options.String);
- IF options.Parse(context.arg, context.error) THEN
- COPY(DefaultPackagesFile, filename);
- IF options.GetString("file", filename) THEN END;
- IF (filename # "") THEN
- NEW(diagnostics);
- IF ParseBuildFile(filename, builds, context.out, diagnostics) THEN
- builds.Show(context.out, options.GetFlag("details"));
- ELSE
- diagnostics.ToStream(context.error, Diagnostics.All); context.error.Ln;
- END;
- ELSE
- context.error.String("Usage: Release.Analyze [options] ~"); context.error.Ln;
- END;
- END;
- END Analyze;
- (** Build specified build (or all of none specified) to a RAM disk *)
- PROCEDURE Check*(context : Commands.Context); (** [Options] {buildname} ~ *)
- VAR
- filename : Files.FileName; builds : Builds; diagnostics : Diagnostics.DiagnosticsList;
- options : Options.Options; nofWorkers : LONGINT;
- PROCEDURE MarkBuilds(context : Commands.Context; builds : Builds) : BOOLEAN;
- VAR build : BuildObj; name : Name; nofMarked, i : LONGINT; error : BOOLEAN;
- BEGIN
- ASSERT((context # NIL) & (builds # NIL));
- nofMarked := 0;
- REPEAT
- name := "";
- context.arg.SkipWhitespace; context.arg.String(name);
- Strings.TrimWS(name);
- IF (name # "") THEN
- build := builds.GetBuild(name);
- IF (build # NIL) THEN
- INC(nofMarked);
- build.marked := TRUE;
- ELSE
- error := TRUE;
- context.error.String("Build "); context.error.String(name);
- context.error.String(" not found."); context.error.Ln;
- END;
- END;
- UNTIL (name = "");
- IF ~error & (nofMarked = 0) THEN
- FOR i := 0 TO LEN(builds.builds) - 1 DO
- IF (builds.builds[i] # NIL) THEN builds.builds[i].marked := TRUE; END;
- END;
- END;
- RETURN ~error;
- END MarkBuilds;
- BEGIN
- NEW(options);
- options.Add("f", "file", Options.String);
- IF options.Parse(context.arg, context.error) THEN
- COPY(DefaultPackagesFile, filename);
- IF options.GetString("file", filename) THEN END;
- IF (filename # "") THEN
- NEW(diagnostics);
- IF ParseBuildFile(filename, builds, context.out, diagnostics) THEN
- IF MarkBuilds(context, builds) THEN
- IF ~options.GetInteger("workers", nofWorkers) THEN nofWorkers := 0; END;
- CheckBuilds(builds, nofWorkers, context, diagnostics);
- END;
- ELSE
- diagnostics.ToStream(context.error, Diagnostics.All); context.error.Ln;
- END;
- ELSE
- context.error.String('Usage: Release.Check [options] ~'); context.error.Ln;
- END;
- END;
- END Check;
- PROCEDURE CheckDiagnostics(diagnostics : Diagnostics.DiagnosticsList; warnings: BOOLEAN; out : Streams.Writer) : BOOLEAN;
- BEGIN
- ASSERT((diagnostics # NIL) & (out # NIL));
- IF (diagnostics.nofErrors = 0) & (diagnostics.nofMessages > 0) THEN
- IF warnings THEN diagnostics.ToStream(out, Diagnostics.All)
- ELSE diagnostics.ToStream(out, {Diagnostics.TypeInformation, Diagnostics.TypeError})
- END;
- out.Update;
- diagnostics.Reset;
- END;
- RETURN diagnostics.nofErrors = 0;
- END CheckDiagnostics;
- PROCEDURE ImportInformation(mode : LONGINT; context : Commands.Context);
- VAR
- options : Options.Options; diagnostics : Diagnostics.DiagnosticsList;
- builds : Builds; build : BuildObj;
- filename : Files.FileName;
- modulename : Modules.Name;
- buildname : Name;
- error : BOOLEAN;
- BEGIN
- NEW(options);
- options.Add("f", "file", Options.String);
- options.Add(0X, "exclude", Options.String);
- IF options.Parse(context.arg, context.out) THEN
- IF ~options.GetString("file", filename) THEN COPY(DefaultPackagesFile, filename); END;
- modulename := "";
- context.arg.SkipWhitespace; context.arg.String(buildname);
- context.arg.SkipWhitespace; context.arg.String(modulename);
- IF (modulename # "") THEN
- NEW(diagnostics);
- IF ParseBuildFile(filename, builds, context.out, diagnostics) THEN
- IF CheckDiagnostics(diagnostics, FALSE, context.out) THEN
- build := builds.GetBuild(buildname);
- IF (build # NIL) THEN
- build.SetOptions(options);
- build.DoChecks(context.out, diagnostics, error);
- IF ~error THEN
- build.AnalyzeDependencies(context.out);
- build.ShowDependentModules(modulename, mode, context.out);
- context.out.Ln; context.out.Update;
- END;
- ELSE
- context.out.String("Build "); context.out.String(buildname); context.out.String(" not found");
- context.out.Ln;
- END;
- END;
- END;
- IF (diagnostics.nofMessages > 0) THEN
- diagnostics.ToStream(context.out, Diagnostics.All);
- END;
- ELSE
- context.out.String("Usage: Release.WhoImports [options] buildname modulename ~"); context.out.Ln;
- END;
- END;
- END ImportInformation;
- PROCEDURE WhoImports*(context : Commands.Context);
- BEGIN
- ImportInformation(Mode_ShowImporting, context);
- END WhoImports;
- PROCEDURE RequiredModules*(context : Commands.Context);
- BEGIN
- ImportInformation(Mode_ShowImported, context);
- END RequiredModules;
- PROCEDURE Rebuild*(context : Commands.Context); (** [Options] buildname {filenames} ~ *)
- VAR
- options : Options.Options;
- diagnostics : Diagnostics.DiagnosticsList;
- builds : Builds; build : BuildObj;
- packagename, filename, fullname : Files.FileName;
- nofNewMarks, nofMarks : LONGINT;
- inBuild : BOOLEAN;
- buildname : ARRAY 32 OF CHAR;
- start0 : Dates.DateTime;
- error : BOOLEAN;
- nofWorkers, res : LONGINT;
- BEGIN
- NEW(options);
- options.Add("b", "build", Options.Flag);
- options.Add("c", "compiler", Options.String);
- options.Add("e", "extension", Options.String);
- options.Add("f", "file", Options.String);
- options.Add("o", "options", Options.String);
- options.Add("p", "path", Options.String);
- options.Add("t", "target", Options.String);
- options.Add("v", "verbose", Options.Flag);
- options.Add("w", "workers", Options.Integer);
- options.Add("y", "", Options.Flag);
- IF options.Parse(context.arg, context.error) THEN
- COPY(DefaultPackagesFile, packagename);
- IF options.GetString("file", packagename) THEN END;
- context.arg.SkipWhitespace; context.arg.String(buildname);
- IF (packagename # "") & (buildname # "") THEN
- start0 := Dates.Now();
- NEW(diagnostics);
- IF ParseBuildFile(packagename, builds, context.out, diagnostics) THEN
- build := builds.GetBuild(buildname);
- IF (build # NIL) THEN
- IF build.disabled THEN
- context.out.String("Warning: Build "); context.out.String(build.name);
- context.out.String(" is disabled."); context.out.Ln;
- context.out.Update;
- END;
- build.SetOptions(options);
- build.DoChecks(context.out, diagnostics, error);
- IF CheckDiagnostics(diagnostics, FALSE, context.out) & ~error THEN
- build.AnalyzeDependencies(context.out);
- build.ClearMarks;
- filename := ""; error := FALSE; nofMarks := 0;
- LOOP
- context.arg.SkipWhitespace; context.arg.String(filename);
- IF (filename = "") THEN EXIT; END;
- IF (Files.Old(filename) # NIL) THEN
- build.MarkFiles(filename, inBuild, nofNewMarks);
- IF inBuild THEN
- nofMarks := nofMarks + nofNewMarks;
- ELSE
- context.out.String("Warning: No depenencies on file "); context.out.String(filename);
- context.out.String("."); context.out.Ln; context.out.Update;
- END;
- ELSE
- context.out.String("Error: File "); context.out.String(filename);
- context.out.String(" does not exist."); context.out.Ln; context.out.Update;
- error := TRUE;
- END;
- END;
- IF ~error THEN
- context.out.Int(nofMarks, 0); context.out.String(" files selected for compilation."); context.out.Ln;
- context.out.Update;
- IF ~options.GetString("path", fullname) THEN fullname := ""; END;
- Strings.Append(fullname, ToolFilename);
- context.out.String("Writing release file to "); context.out.String(fullname);
- context.out.String(" ... "); context.out.Update;
- build.GenerateToolFile(fullname, res);
- IF (res = 0) THEN
- context.out.String("done."); context.out.Ln; context.out.Update;
- IF options.GetFlag("build") THEN
- IF ~options.GetInteger("workers", nofWorkers) THEN nofWorkers := 0; END;
- build.Compile(nofWorkers, context.out, context.error, options.GetFlag("verbose"), diagnostics, error);
- ELSE
- CallCommand("Notepad.Open", fullname, context);
- END;
- ELSE
- IF ~options.GetFlag("build") THEN
- CallCommand("Notepad.Open", fullname, context);
- END;
- context.out.String("error, res: "); context.out.Int(res, 0); context.out.Ln;
- END;
- END;
- END;
- ELSE
- context.error.String("Build "); context.error.String(buildname); context.error.String(" not found."); context.error.Ln;
- END;
- END;
- IF (diagnostics.nofMessages > 0) THEN
- diagnostics.ToStream(context.out, Diagnostics.All);
- context.out.Ln;
- END;
- ELSE
- context.error.String('Usage: Release.ReBuild [options] BuildName {filenames}');
- context.error.Ln;
- END;
- END;
- END Rebuild;
- (** Build the specified build *)
- PROCEDURE Build*(context : Commands.Context); (** [Options] buildname ~ *)
- VAR
- filename, fullname : Files.FileName;
- builds : Builds; build : BuildObj;
- diagnostics : Diagnostics.DiagnosticsList;
- options : Options.Options;
- buildname : ARRAY 32 OF CHAR;
- start0 : Dates.DateTime;
- error : BOOLEAN;
- nofWorkers, res : LONGINT;
- BEGIN
- NEW(options);
- options.Add("b", "build", Options.Flag);
- options.Add("c", "compiler", Options.String);
- options.Add(0X, "exclude", Options.String);
- options.Add(0X, "only", Options.String);
- options.Add("e", "extension", Options.String);
- options.Add("f", "file", Options.String);
- options.Add("l", "link", Options.Flag);
- options.Add(0X, "list", Options.Flag);
- options.Add("n", "nocheck", Options.Flag);
- options.Add("o", "options", Options.String);
- options.Add("p", "path", Options.String);
- options.Add("s", "symbolFileExtension", Options.String);
- options.Add("t", "target", Options.String);
- options.Add("v", "verbose", Options.Flag);
- options.Add(0X, "workers", Options.Integer);
- options.Add("x", "xml", Options.Flag);
- options.Add("y", "", Options.Flag);
- options.Add("z", "zip", Options.Flag);
- options.Add("w","warnings",Options.Flag);
- IF options.Parse(context.arg, context.error) THEN
- COPY(DefaultPackagesFile, filename);
- IF options.GetString("file", filename) THEN END;
- context.arg.SkipWhitespace; context.arg.String(buildname);
- IF (filename # "") & (buildname # "") THEN
- start0 := Dates.Now();
- NEW(diagnostics);
- IF ParseBuildFile(filename, builds, context.out, diagnostics) THEN
- build := builds.GetBuild(buildname);
- IF (build # NIL) THEN
- IF build.disabled THEN
- context.out.String("Warning: Build "); context.out.String(build.name);
- context.out.String(" is disabled."); context.out.Ln;
- context.out.Update;
- END;
- build.SetOptions(options);
- IF ~options.GetInteger("workers", nofWorkers) THEN nofWorkers := 0; END;
- IF ~options.GetFlag("nocheck") THEN
- build.DoChecks(context.out, diagnostics, error);
- ELSIF (nofWorkers > 0) THEN
- context.error.String("Incompatible options: nocheck cannot be combined with workers");
- context.error.Ln; context.error.Update;
- RETURN;
- END;
- IF CheckDiagnostics(diagnostics, options.GetFlag("warnings"), context.out) & ~error THEN
- IF options.GetFlag("list") THEN
- build.ToStream(context.out, 0); context.out.Ln;
- ELSE
- IF ~options.GetString("path", fullname) THEN fullname := ""; END;
- Strings.Append(fullname, ToolFilename);
- context.out.String("Writing release file to "); context.out.String(fullname);
- context.out.String(" ... "); context.out.Update;
- build.GenerateToolFile(fullname, res);
- IF (res = 0) THEN
- context.out.String("done."); context.out.Ln; context.out.Update;
- IF options.GetFlag("build") THEN
- IF options.GetFlag("link") THEN
- context.error.String("Incompatible options: link cannot automatically build");
- context.error.Ln; context.error.Update;
- RETURN;
- END;
- build.Compile(nofWorkers, context.out, context.error, options.GetFlag("verbose"), diagnostics, error);
- ELSIF ~options.GetFlag("zip") THEN
- CallCommand("Notepad.Open", fullname, context);
- END;
- IF ~error & options.GetFlag("zip") & CheckDiagnostics(diagnostics, options.GetFlag("warnings"), context.out) THEN
- build.GenerateZipFiles(context.out, context.error, diagnostics, error);
- END;
- IF ~error & options.GetFlag("xml") THEN
- IF ~options.GetString("path", fullname) THEN fullname := ""; END;
- Strings.Append(fullname, InstallerPackageFile);
- context.out.String("Writing XML package description to "); context.out.String(fullname);
- context.out.String(" ... "); context.out.Update;
- build.GeneratePackageFile(InstallerPackageFile, res);
- IF (res = Files.Ok) THEN
- context.out.String("done.");
- ELSE
- context.out.String("error, res: "); context.out.Int(res, 0);
- END;
- context.out.Ln;
- END;
- ELSE
- IF ~options.GetFlag("build") THEN
- CallCommand("Notepad.Open", fullname, context);
- END;
- context.out.String("error, res: "); context.out.Int(res, 0); context.out.Ln;
- END;
- END;
- END;
- ELSE
- context.error.String("Build "); context.error.String(buildname); context.error.String(" not found."); context.error.Ln;
- END;
- END;
- IF (diagnostics.nofMessages > 0) THEN
- IF options.GetFlag("warnings") THEN
- diagnostics.ToStream(context.out, Diagnostics.All);
- ELSE
- diagnostics.ToStream(context.out, {Diagnostics.TypeError, Diagnostics.TypeInformation});
- END;
- context.out.Ln;
- END;
- ELSE
- context.error.String('Usage: Release.Build [options] BuildName');
- context.error.Ln;
- END;
- END;
- END Build;
- PROCEDURE Cleanup;
- BEGIN
- CompilerInterface.Unregister("ReleaseTool");
- END Cleanup;
- BEGIN
- CompilerInterface.Register("ReleaseTool", "Parse release description file", "Tool", ParseText);
- Modules.InstallTermHandler(Cleanup);
- END Release.
- SystemTools.Free Release ~
- Release.FindPosition WinAos Usbdi.Mod ~
- Release.Analyze --details ~
- Release.Check ~
- Release.Build A2 ~
- Release.Build --path="../Test/" -b A2 ~
- Release.Build --path="../ObjT1/" -bn WinAos ~
- Release.Build --path="../TestE/" eWinAos ~
- Release.Build --path="../Test/" --xml A2 ~
- Release.CheckFiles -f=C2Release.Tool ../../CF/trunk/source/*.* ~
- Release.Build --path="../Test/" -z A2 ~
- Release.RequiredModules A2 WMTextView ~
- Release.WhoImports WinAos WMComponents ~
- Release.Tool ~
- Release.Build --path="../Test" -b -x="Build Oberon Contributions " A2 ~
- Release.Rebuild -b WinAos WMTextView.Mod ~
- Release.Build -b WinAos ~
|