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 i0) 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=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.