diff options
Diffstat (limited to 'Make.lpr')
-rw-r--r-- | Make.lpr | 636 |
1 files changed, 636 insertions, 0 deletions
diff --git a/Make.lpr b/Make.lpr new file mode 100644 index 0000000..942f568 --- /dev/null +++ b/Make.lpr @@ -0,0 +1,636 @@ +program Make; + +{$mode objfpc}{$H+} + +uses + {$IFDEF UNIX}{$IFDEF UseCThreads} + cthreads, + {$ENDIF}{$ENDIF} + Classes, SysUtils, CustApp + { you can add units after this }, + RegExpr, Math, Process; + +type + + { TMake } + + TMake = class(TCustomApplication) + protected + procedure DoRun; override; + public + constructor Create(TheOwner: TComponent); override; + procedure WriteHelp; virtual; + end; + + TAbhArt = (aaFinal,aaEinsZuEins,aaNZuN,aaIgnore); + TDateienMitDaten = array of ^TDateiMitDatum; + + { TAbhaengigkeit } + + TAbhaengigkeit = class(TObject) + AbhArt: TAbhArt; + QuellenRE: TRegExpr; + ZieleFkt, + Befehle: TStringlist; + Quellen, + Ziele: TDateienMitDaten; + Erben: array of ^TAbhaengigkeit; + Prioritaet: Integer; + constructor create; overload; + constructor create(Original: TAbhaengigkeit); overload; + destructor destroy; override; + end; + TAbhaengigkeiten = array of ^TAbhaengigkeit; + TDateiMitDatum = record + Name: AnsiString; + Datum: TDateTime; + end; + +var + jetzt: TDateTime; + inputfile: string; + + { TAbhaengigkeit } + +constructor TAbhaengigkeit.create; +begin + inherited create; + QuellenRE:=TRegExpr.create; + QuellenRE.Expression:=''; + ZieleFkt:=TStringlist.create; + SetLength(Quellen,0); + SetLength(Ziele,0); + Befehle:=TStringlist.create; + SetLength(Erben,0); + AbhArt:=aaNZuN; + Prioritaet:=42; +end; + +constructor TAbhaengigkeit.create(Original: TAbhaengigkeit); +var i: integer; +begin + inherited create; + QuellenRE:=TRegExpr.create; + QuellenRE.Expression:=Original.QuellenRE.Expression; + ZieleFkt:=TStringlist.create; + ZieleFkt.Text:=Original.ZieleFkt.Text; + SetLength(Quellen,length(Original.Quellen)); + for i:=0 to length(Original.Quellen)-1 do + Quellen[i]:=Original.Quellen[i]; + SetLength(Ziele,length(Original.Ziele)); + for i:=0 to length(Original.Ziele)-1 do + Ziele[i]:=Original.Ziele[i]; + Befehle:=TStringlist.create; + Befehle.Text:=Original.Befehle.Text; + SetLength(Erben,length(Original.Erben)); + for i:=0 to length(Original.Erben)-1 do + Erben[i]:=Original.Erben[i]; + Prioritaet:=Original.Prioritaet; + AbhArt:=Original.AbhArt; +end; + +destructor TAbhaengigkeit.destroy; +begin + QuellenRE.free; + SetLength(Quellen,0); + SetLength(Ziele,0); + ZieleFkt.free; + Befehle.free; + SetLength(Erben,0); + inherited destroy; +end; + +function liesZeile(var dat: textfile; out s: string): boolean; +begin + repeat + result:=not eof(dat); + if not result then exit; + readln(dat,s); + if pos('#',s)>0 then delete(s,pos('#',s),length(s)); + while (length(s)>0) and (s[length(s)] in [#9,' ']) do + delete(s,length(s),1); + until s<>''; +end; + +procedure sammleDateien(wo: string; rekursiv: boolean; var dats: TDateienMitDaten); +var sr: TSearchRec; + err: integer; +begin + err:=FindFirst(wo,faReadOnly or faHidden or faSysFile or (byte(rekursiv)*faDirectory),sr); + while err=0 do begin + if sr.Attr and faDirectory <> 0 then begin + if rekursiv and + (sr.Name<>'.') and + (sr.Name<>'..') then + sammleDateien(extractfilepath(wo)+sr.Name+'/'+extractfilename(wo),rekursiv,dats); + end + else begin + setlength(dats,length(dats)+1); + getmem(dats[length(dats)-1],sizeof(TDateiMitDatum)); + fillchar(dats[length(dats)-1]^,sizeof(TDateiMitDatum),0); + dats[length(dats)-1]^.Name:=extractfilepath(wo)+sr.name; + dats[length(dats)-1]^.Datum:=FileDateTodateTime(FileAge(dats[length(dats)-1]^.Name)); + end; + err:=FindNext(sr); + end; + FindClose(sr); +end; + +function anzCs(c: char; s: string): longint; +var i: longint; +begin + result:=0; + for i:=1 to length(s) do + if s[i]=c then + inc(result); +end; + +function notQuotedPos(was,worin: string): longint; +begin + result:=0; + repeat + if pos(was,copy(worin,result+1,length(worin)-result))=0 then begin + result:=0; + exit; + end; + result:=result+pos(was,copy(worin,result+1,length(worin)-result)); + if not odd(anzCs('"',copy(worin,1,result-1))) then + exit; + until false; +end; + +function unescapedpos(was,worin: string): integer; +var Ebene: longint; +begin + result:=0; + Ebene:=0; + while pos(was,copy(worin,result+1,length(worin)))>0 do begin + repeat + result:=result+1; + case worin[result] of + '{': inc(Ebene); + '}': dec(Ebene); + end; + until (result>=length(worin)) or ((copy(worin,result,length(was))=was) and (Ebene=0)); + if ((result=1) or (worin[result-1]<>'\')) and (copy(worin,result,length(was))=was) then exit; + end; + result:=0; +end; + +function liesMakeFile(datNam: string; out Abh: TAbhaengigkeiten; out dats: TDateienMitDaten): boolean; +var f: textfile; + s,t: string; + NA: TAbhaengigkeit; + beg,fin,rek: boolean; + wo,num: integer; +begin + result:=false; + NA:=TAbhaengigkeit.create; + setlength(dats,0); + setlength(Abh,0); + wo:=0; + num:=0; + assignfile(f,datNam); + reset(f); + while liesZeile(f,s) do begin + if wo=0 then begin + if notQuotedPos(':',s)=0 then begin + while (length(s)>0) and (s[1] in [' ',#9]) do delete(s,1,1); + if length(s)=0 then continue; + s:=s+' '; + rek:=pos('-r',s)=1; + if rek then delete(s,1,pos(' ',s)); + while pos(' ',s)>0 do begin + sammleDateien(copy(s,1,pos(' ',s)-1),rek,dats); + delete(s,1,pos(' ',s)); + end; + end + else wo:=1; + end; + if wo=1 then begin + fin:=s[length(s)]=';'; + if fin then delete(s,length(s),1); + beg:=not (s[1] in [' ',#9]); + if (notQuotedPos(':',s)>0) xor beg then begin + closefile(f); + exit; + end; + if beg then begin + SetLength(NA.Quellen,0); + NA.QuellenRE.Expression:=''; + SetLength(NA.Ziele,0); + NA.ZieleFkt.Clear; + NA.AbhArt:=aaFinal; + if pos('1>1 ',s)=1 then NA.AbhArt:=aaEinsZuEins; + if pos('n>n ',s)=1 then NA.AbhArt:=aaNZuN; + if NA.AbhArt=aaFinal then NA.AbhArt:=aaNZuN // der Standard + else delete(s,1,pos(' ',s)); + t:=copy(s,1,notQuotedPos(':',s)-1)+' '; + while pos(' ',t)>0 do begin + while pos(' ',t)=1 do delete(t,1,1); + NA.ZieleFkt.Add(copy(t,1,pos(' ',t)-1)); + delete(t,1,pos(' ',t)); + end; + delete(s,1,notQuotedPos(':',s)); + while pos(' ',s)=1 do delete(s,1,1); + NA.QuellenRE.Expression:=s; + end + else begin + while s[1] in [' ',#9] do + delete(s,1,1); + while unescapedpos(';',s)>0 do + s:=copy(s,1,unescapedpos(';',s)-1)+' &&'+copy(s,unescapedpos(';',s)+1,length(s)-unescapedpos(';',s)); + NA.Befehle.Add(s); + end; + if fin then begin + setlength(Abh,length(Abh)+1); + getmem(Abh[length(Abh)-1],sizeof(TAbhaengigkeit)); + fillchar(Abh[length(Abh)-1]^,sizeof(TAbhaengigkeit),0); + Abh[length(Abh)-1]^:=TAbhaengigkeit.create(NA); + Abh[length(Abh)-1]^.Prioritaet:=num; + inc(num); + SetLength(NA.Quellen,0); + NA.QuellenRE.Expression:=''; + SetLength(NA.Ziele,0); + NA.ZieleFkt.Clear; + setlength(NA.Erben,0); + NA.Befehle.Clear; + end; + end; + end; + closefile(f); + NA.free; + result:=true; +end; + +function unescape(s: string): string; +begin + result:=s; + while pos('\,',result)>0 do + delete(result,pos('\,',result),1); +end; + +function escape(s,toe: string; ec: char): string; +var i,j: longint; + b: boolean; +begin + result:=''; + for i:=1 to length(s) do begin + b:=false; + for j:=1 to length(toe) do + b:=b or (toe[j]=s[i]); + if b then result:=result+ec; + result:=result+s[i]; + end; +end; + +function Quellersetzung(var worin: string; Quelle: string): boolean; +var s: string; + i: integer; +begin + result:=false; + while pos('%nurmit''',worin)>0 do begin + i:=pos('%nurmit''',worin); + while worin[i]<>'''' do inc(i); + repeat + inc(i); + until worin[i]=''''; + if pos(copy(worin,pos('%nurmit''',worin)+8,i-pos('%nurmit''',worin)-8),Quelle)=0 then + exit; + delete(worin,pos('%nurmit''',worin),i-pos('%nurmit''',worin)+1); + end; + while pos('%nurohne''',worin)>0 do begin + i:=pos('%nurohne''',worin); + while worin[i]<>'''' do inc(i); + repeat + inc(i); + until worin[i]=''''; + if pos(copy(worin,pos('%nurohne''',worin)+9,i-pos('%nurohne''',worin)-9),Quelle)>0 then + exit; + delete(worin,pos('%nurohne''',worin),i-pos('%nurohne''',worin)+1); + end; + result:=true; + while pos('%in',worin)>0 do + worin:=copy(worin,1,pos('%in',worin)-1)+Quelle+copy(worin,pos('%in',worin)+3,length(worin)); + s:=extractfilename(Quelle); + while pos('%ifile',worin)>0 do + worin:=copy(worin,1,pos('%ifile',worin)-1)+s+copy(worin,pos('%ifile',worin)+6,length(worin)); + if pos('.',s)>0 then begin + while s[length(s)]<>'.' do delete(s,length(s),1); + delete(s,length(s),1); + end; + while pos('%basename',worin)>0 do + worin:=copy(worin,1,pos('%basename',worin)-1)+s+copy(worin,pos('%basename',worin)+9,length(worin)); + s:=extractfilepath(Quelle); + while pos('%basedir/',worin)>0 do + worin:=copy(worin,1,pos('%basedir/',worin)-1)+s+copy(worin,pos('%basedir/',worin)+9,length(worin)); + while pos('%basedir',worin)>0 do + worin:=copy(worin,1,pos('%basedir',worin)-1)+s+copy(worin,pos('%basedir',worin)+8,length(worin)); + while pos('%BASEDIR/',worin)>0 do + worin:=copy(worin,1,pos('%BASEDIR/',worin)-1)+extractfilepath(inputfile)+copy(worin,pos('%BASEDIR/',worin)+9,length(worin)); + while pos('%BASEDIR',worin)>0 do + worin:=copy(worin,1,pos('%BASEDIR',worin)-1)+extractfilepath(inputfile)+copy(worin,pos('%BASEDIR',worin)+8,length(worin)); +end; + +function bashMatch(was,worauf: string): boolean; +var RE: TRegExpr; +begin + if pos('*',was)=0 then begin + result:=was=worauf; + exit; + end; + RE:=TRegExpr.create; + RE.Expression:='^'+escape(escape(was,'.|()^$','\'),'*','.')+'$'; + result:=RE.Exec(worauf); + RE.free; +end; + +procedure ZieleHinzufuegen(var Ziele: TDateienMitDaten; Ziel,Quelle: string; var dats: TDateienMitDaten); +var i,Anz,Ende: integer; + s,t,u: string; + wasda: boolean; +begin + if pos('{',Ziel)=0 then begin + if not Quellersetzung(Ziel,Quelle) then exit; + Ziel:=unescape(Ziel); + i:=0; + wasda:=false; + while i<length(dats) do begin + if bashMatch(Ziel,dats[i]^.Name) then begin + wasda:=true; + SetLength(Ziele,length(Ziele)+1); + Ziele[length(Ziele)-1]:=dats[i]; + end; + inc(i); + end; + if not wasda then begin + SetLength(dats,length(dats)+1); + getmem(dats[i],sizeof(TDateiMitDatum)); + fillchar(dats[i]^,sizeof(TDateiMitDatum),0); + if pos('*',Ziel)=0 then dats[i]^.Name:=Ziel + else dats[i]^.Name:='.uralt.'; // unpassende *-Muster erzeugen formal keine echte Datei + dats[i]^.Datum:=0; // nicht existente Dateien sind formal uralt + SetLength(Ziele,length(Ziele)+1); + Ziele[length(Ziele)-1]:=dats[i]; + end; + end + else begin + Ende:=pos('{',Ziel); + Anz:=1; + while (Anz>0) or (Ziel[Ende]<>'}') do begin + inc(Ende); + case Ziel[Ende] of + '{': inc(Anz); + '}': dec(Anz); + end{of case}; + end; + s:=copy(Ziel,1,pos('{',Ziel)-1); + t:=copy(Ziel,pos('{',Ziel)+1,Ende-pos('{',Ziel)-1); + u:=copy(Ziel,Ende+1,length(Ziel)); + if unescapedpos('..',t)>0 then begin + for i:=strtoint(copy(t,1,unescapedpos('..',t)-1)) to strtoint(copy(t,unescapedpos('..',t)+2,length(t))) do + ZieleHinzufuegen(Ziele,s+inttostr(i)+u,Quelle,dats); + end + else begin + t:=t+','; + while unescapedpos(',',t)>0 do begin + ZieleHinzufuegen(Ziele,s+copy(t,1,unescapedpos(',',t)-1)+u,Quelle,dats); + delete(t,1,unescapedpos(',',t)); + end; + end; + end; +end; + +procedure findeMehrZiele(var Ziele: TDateienMitDaten; ZieleFkt: TStringList; Quelle: string; var dats: TDateienMitDaten); +var i: integer; +begin + for i:=0 to ZieleFkt.Count-1 do + ZieleHinzufuegen(Ziele,ZieleFkt[i],Quelle,dats); +end; + +procedure findeZiele(out Ziele: TDateienMitDaten; ZieleFkt: TStringList; Quelle: string; var dats: TDateienMitDaten); +begin + setlength(Ziele,0); + findeMehrZiele(Ziele,ZieleFkt,Quelle,dats); +end; + +procedure findeWasZuTunIst(var mgl: TAbhaengigkeiten; out zuTun: TAbhaengigkeiten; var dats: TDateienMitDaten); +var i,j,k,l: integer; + neues,schonda: boolean; + quNeu, + ziAlt: TDateTime; + s: string; +begin + setlength(zuTun,0); + repeat + neues:=false; + i:=0; + while i<length(mgl) do begin + case mgl[i]^.AbhArt of + aaNZuN: begin // alle Quellen werden mit einem Mal behandelt + if length(mgl[i]^.Erben)=0 then begin // noch keine davon abgeleitete Abhängigkeit vorhanden + SetLength(mgl,length(mgl)+1); + getmem(mgl[length(mgl)-1],sizeof(TAbhaengigkeiten)); + fillchar(mgl[length(mgl)-1]^,sizeof(TAbhaengigkeiten),0); + mgl[length(mgl)-1]^:=TAbhaengigkeit.create; + SetLength(mgl[i]^.Erben,1); + mgl[i]^.Erben[0]:=mgl[length(mgl)-1]; + mgl[length(mgl)-1]^.AbhArt:=aaFinal; + mgl[length(mgl)-1]^.Prioritaet:=mgl[i]^.Prioritaet; + mgl[length(mgl)-1]^.ZieleFkt.Text:=mgl[i]^.ZieleFkt.Text; + mgl[length(mgl)-1]^.Befehle.Text:=mgl[i]^.Befehle.Text; + end; + for j:=0 to length(dats)-1 do if mgl[i]^.QuellenRE.Exec(dats[j]^.Name) then begin + schonda:=false; + for k:=0 to length(mgl[i]^.Erben[0]^.Quellen)-1 do + schonda:=schonda or (mgl[i]^.Erben[0]^.Quellen[k]=dats[j]); + if schonda then continue; + + SetLength(mgl[i]^.Erben[0]^.Quellen,length(mgl[i]^.Erben[0]^.Quellen)+1); + mgl[i]^.Erben[0]^.Quellen[length(mgl[i]^.Erben[0]^.Quellen)-1]:=dats[j]; + + for k:=0 to mgl[i]^.Befehle.Count-1 do begin + s:=mgl[i]^.Befehle[k]; + if Quellersetzung(s,dats[j]^.Name) then begin + if k=0 then mgl[i]^.Erben[0]^.Befehle.Clear; + mgl[i]^.Erben[0]^.Befehle.Add(s); + end; + end; + end; + end; + aaEinsZuEins: begin // jede Quelle muss einzeln behandelt werden + for j:=0 to length(dats)-1 do + if mgl[i]^.QuellenRE.Exec(dats[j]^.Name) then begin + schonda:=false; + for k:=0 to length(mgl[i]^.Erben)-1 do + for l:=0 to length(mgl[i]^.Erben[k]^.Quellen)-1 do + schonda:=schonda or (mgl[i]^.Erben[k]^.Quellen[l]=dats[j]); + if schonda then continue; + SetLength(mgl,length(mgl)+1); + getmem(mgl[length(mgl)-1],sizeof(TAbhaengigkeit)); + fillchar(mgl[length(mgl)-1]^,sizeof(TAbhaengigkeiten),0); + mgl[length(mgl)-1]^:=TAbhaengigkeit.create; + SetLength(mgl[i]^.Erben,length(mgl[i]^.Erben)+1); + mgl[i]^.Erben[length(mgl[i]^.Erben)-1]:=mgl[length(mgl)-1]; + mgl[length(mgl)-1]^.AbhArt:=aaFinal; + SetLength(mgl[length(mgl)-1]^.Quellen,1); + mgl[length(mgl)-1]^.Quellen[0]:=dats[j]; + findeZiele(mgl[length(mgl)-1]^.Ziele,mgl[i]^.ZieleFkt,dats[j]^.name,dats); + mgl[length(mgl)-1]^.Befehle.Clear; + for k:=0 to mgl[i]^.Befehle.Count-1 do begin + s:=mgl[i]^.Befehle[k]; + if Quellersetzung(s,dats[j]^.Name) then + mgl[length(mgl)-1]^.Befehle.Add(s); + end; + if mgl[length(mgl)-1]^.Befehle.Count=0 then begin + writeln('Keine Befehle für diese Abhängigkeit auszuführen!'); + halt; + end; + mgl[length(mgl)-1]^.Prioritaet:=mgl[i]^.Prioritaet; + end; + end; + aaFinal: if length(mgl[i]^.Erben)=0 then begin // Quellen stehen schon fest, noch nicht auszuführen + if length(mgl[i]^.Quellen)=0 then begin + inc(i); + continue; + end; + quNeu:=mgl[i]^.Quellen[0]^.Datum; + for j:=1 to length(mgl[i]^.Quellen)-1 do + quNeu:=max(quNeu,mgl[i]^.Quellen[j]^.Datum); + if length(mgl[i]^.Ziele)=0 then begin + for j:=0 to length(mgl[i]^.Quellen)-1 do + findeMehrZiele(mgl[i]^.Ziele,mgl[i]^.ZieleFkt,mgl[i]^.Quellen[j]^.Name,dats); + if length(mgl[i]^.Ziele)=0 then begin + mgl[i]^.AbhArt:=aaIgnore; + continue; + end; + end; + ziAlt:=mgl[i]^.Ziele[0]^.Datum; + for j:=1 to length(mgl[i]^.Ziele)-1 do + ziAlt:=min(ziAlt,mgl[i]^.Ziele[j]^.Datum); + if ziAlt<quNeu then begin + SetLength(zuTun,length(zuTun)+1); + getmem(zuTun[length(zuTun)-1],sizeof(TAbhaengigkeit)); + fillchar(zuTun[length(zuTun)-1]^,sizeof(TAbhaengigkeit),0); + zuTun[length(zuTun)-1]^:=TAbhaengigkeit.create(mgl[i]^); + SetLength(mgl[i]^.Erben,1); + mgl[i]^.Erben[0]:=zuTun[length(zuTun)-1]; + for j:=0 to length(mgl[i]^.Ziele)-1 do + mgl[i]^.Ziele[j]^.Datum:=jetzt; + neues:=true; + end; + end; + aaIgnore: ; // keine Ziele + end{of case}; + inc(i); + end; + until not neues; +end; + +procedure tueWasZuTunIst(zuTun: TAbhaengigkeiten; nurAnzeigen: boolean; Ausgabedatei: string); +var Prior,i,j: integer; + Ausg: Textfile; + Befehle: TStringlist; +begin + Befehle:=TStringlist.create; + Prior:=0; + for i:=0 to length(zuTun)-1 do + Prior:=max(Prior,zuTun[i]^.Prioritaet); + while Prior>=0 do begin + for i:=0 to length(zuTun)-1 do + if zuTun[i]^.Prioritaet=Prior then + for j:=0 to zuTun[i]^.Befehle.Count-1 do + Befehle.Add(zuTun[i]^.Befehle[j]); + dec(Prior); + end; + if length(zuTun)=0 then + Befehle.Add('echo "Es gibt hier nichts zu tun!"'); + Befehle.Add('sleep 2'); + if nurAnzeigen then begin + for i:=0 to Befehle.Count-1 do + writeln(Befehle[i]); + end + else begin + Assignfile(Ausg,Ausgabedatei); + Rewrite(Ausg); + for i:=0 to Befehle.Count-1 do + writeln(Ausg,'('+Befehle[i]+') || (read -p "Ein Fehler ist aufgetreten! ... "; exit 1)'); + Closefile(Ausg); + end; + Befehle.Free; +end; + +{ TMake } + +procedure TMake.DoRun; +var + ErrorMsg: String; + mglAbhaengigkeiten, + zutunAbhaengigkeiten: TAbhaengigkeiten; + Dateien: TDateienMitDaten; +begin + ErrorMsg:=CheckOptions('A:HWD:','Ausgabe: Hilfe Watte Datei:'); + if ErrorMsg<>'' then begin + ShowException(Exception.Create(ErrorMsg)); + Terminate; + Exit; + end; + + if HasOption('H','Hilfe') then begin + WriteHelp; + Terminate; + Exit; + end; + + if (GetOptionValue('A','Ausgabe')='') and not HasOption('W','Watte') then begin + ShowException(Exception.Create('Ausgabedatei wird benötigt!')); + Terminate; + Exit; + end; + + if HasOption('D','Datei') then inputfile:=GetOptionValue('D','Datei') + else inputfile:='Machdatei'; + + jetzt:=now; + + if fileexists(inputfile) then begin + if not liesMakeFile(inputfile,mglAbhaengigkeiten,Dateien) then begin + ShowException(Exception.Create('Datei '''+inputfile+''' ist fehlerhaft!')); + Terminate; + Exit; + end; + end + else begin + ShowException(Exception.Create('Datei '''+inputfile+''' existiert nicht!')); + Terminate; + Exit; + end; + + writeln('Regeln: '+inttostr(length(mglAbhaengigkeiten))+', Dateien: '+inttostr(length(Dateien))); + findeWasZuTunIst(mglAbhaengigkeiten,zuTunAbhaengigkeiten,Dateien); + writeln('anzuwendende Regeln: '+inttostr(length(zuTunAbhaengigkeiten))); + tueWasZuTunIst(zuTunAbhaengigkeiten,HasOption('W','Watte'),GetOptionValue('A','Ausgabe')); + + Terminate; +end; + +constructor TMake.Create(TheOwner: TComponent); +begin + inherited Create(TheOwner); + StopOnException:=True; +end; + +procedure TMake.WriteHelp; +begin + writeln('Verwendung:'); + writeln(' ',ExeName,' -A/--Ausgabe -H/--Hilfe -W/--Watte -D/--Datei'); +end; + +var + Application: TMake; +begin + Application:=TMake.Create(nil); + Application.Run; + Application.Free; +end. + |