12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368336933703371337233733374337533763377337833793380338133823383338433853386338733883389339033913392339333943395 |
- 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 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("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.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;
- 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 ~
|