123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551 |
- unit namespacetool;
- {$mode ObjFPC}{$H+}
- interface
- uses
- Classes, SysUtils, types, prefixer;
- Const
- DefaultSubdir = 'namespaced';
- DefaultDoneList = 'done.lst';
- type
- { TNamespaceCreation }
- TSubDirMode = (
- sdmAppend, // append dirmap result to subdir
- sdmReplace // replace directory part with result of dirmap
- );
- TNamespaceToolLogEvent = procedure(Sender : TObject; EventType : TEventType; Const Msg : String) of object;
- TChangeFPMakeResult = (cmrFailed,cmrAlreadyDone,cmrOK);
- { TNamespaceTool }
- TNamespaceTool = class(TComponent)
- Private
- FDoneFileName : string;
- FDirMapFileName: string;
- FOnLog: TNamespaceToolLogEvent;
- FPrefixesFileName: string;
- FDefaultPrefix: string;
- FFPMakeNameSpaceFile : String;
- FSubDir : String;
- FCasedFiles,
- FUpdate,
- FDryRun,
- FWritePrefixes,
- FBackup: Boolean;
- FSubdirMode: TSubDirMode;
- FFPMakeMap : TStrings;
- FDirmap : TStrings;
- FKnownPrefixes : TStrings;
- FRestart : Boolean;
- FLastOpts: TStringDynArray;
- FLastRule,
- FLastDir : String;
- FForcedExt : String;
- procedure DoPrefixLog(Sender: TObject; aType: TEventType; const aMsg: String
- );
- procedure SetForcedExt(AValue: String);
- procedure SetSubdir(AValue: String);
- Protected
- procedure DoMsg(const aFmt: String; const aArgs: array of const;
- EventType: TEventType=etInfo); overload;
- procedure DoMsg(const aMessage: String; EventType: TEventType=etInfo); overload;
- // Add code to initialize namespace to fpmake in filename.
- function AddNamespaceNameToFpMake(const aFileName: string): TChangeFPMakeResult;
- // add file to FPMake namespaces file
- procedure AddToFPMakeMap(const aSrcFileName, aDestFileName: string);
- // Create directory if not dryrun
- procedure CreateDestDir(const aDestDir: string);
- // Actual HandleFileList
- procedure DoHandleFileList(const aFileName: String);
- // Return name of package dir from filename (first level of dir tree).
- function GetPackageDir(const aFileName: string): string;
- // Return unit name from file name.
- function GetUnitNameFromFile(aFile: String): string;
- // Split line into
- procedure SplitLine(aLine: String; out aFileName, aRule: String;
- var aOpts: TStringDynArray);
- // Write FPMake Namespaces file.
- procedure WritePackageNameSpaceFile(aDir: String; aList: TStrings; DoClear: Boolean=True);
- Public
- class procedure SplitRuleLine(aLine: String; out aFileName, aRule: String;
- var AlastDir, aLastRule: String; var aOpts, aLastOpts: TStringDynArray);
- Public
- Constructor Create(aOwner : TComponent); override;
- Destructor Destroy; override;
- // Initialize (load config files)
- Procedure Init;
- // Actual actions
- // Apply rule to a single unit file
- procedure HandleFile(const aFileName: String; aRule: string; aOptions: array of String);
- // Load file list and call handlefile for each
- procedure HandleFileList(const aFileName: String);
- // Create a 'known prefixes' file with the names of the files
- procedure CreateKnown(const aFileName: String);
- Property OnLog : TNamespaceToolLogEvent Read FOnLog Write FOnLog;
- Property ForcedExt : String Read FForcedExt Write SetForcedExt;
- Property DirMapFileName : String Read FDirMapFileName Write FDirMapFileName;
- Property PrefixesFileName : String Read FPrefixesFileName Write FPrefixesFileName;
- Property DefaultPrefix : String Read FDefaultPrefix Write FDefaultPrefix;
- Property Subdir : String Read FSubdir Write SetSubdir;
- Property SubdirMode : TSubDirMode Read FSubdirMode Write FSubdirMode;
- Property Backup : Boolean Read FBackup Write FBackup;
- Property Update : Boolean Read FUpdate Write FUpdate;
- Property DryRun : Boolean Read FDryRun Write FDryRun;
- Property Restart : Boolean Read FRestart Write FRestart;
- Property CasedFiles : Boolean Read FCasedFiles Write FCasedFiles;
- Property FPMakeNameSpaceFile : String Read FFPMakeNameSpaceFile Write FFPMakeNameSpaceFile;
- Property KnownPrefixes : TStrings Read FKnownPrefixes;
- Property DirMap : Tstrings Read FDirmap;
- end;
- implementation
- procedure TNamespaceTool.CreateDestDir(const aDestDir : string);
- begin
- if not DirectoryExists(aDestDir) then
- begin
- DoMsg('Creating destination directory: %s',[aDestDir]);
- if not FDryRun then
- if not ForceDirectories(aDestDir) then
- Raise Exception.Create('Could not create destination directory '+aDestDir);
- end;
- end;
- procedure TNamespaceTool.DoMsg(const aFmt: String; const aArgs: array of const; EventType : TEventType = etInfo);
- begin
- DoMsg(Format(aFmt,aArgs),EventType);
- end;
- procedure TNamespaceTool.DoMsg(const aMessage: String; EventType : TEventType = etInfo);
- begin
- if assigned(OnLog) then
- OnLog(Self,EventType, aMessage);
- end;
- procedure TNamespaceTool.AddToFPMakeMap(const aSrcFileName,aDestFileName : string);
- Var
- Src,Dest,aDir,aRule : String;
- begin
- Src:=aSrcFileName;
- Dest:=aDestFileName;
- // Strip package dir
- aDir:=GetPackageDir(aSrcFileName);
- if Pos(aDir,Src)=1 then
- Delete(Src,1,Length(aDir));
- if Pos(aDir,Dest)=1 then
- Delete(Dest,1,Length(aDir));
- // Map file itself.
- FFPMakeMap.Values[Src]:=Dest;
- aDir:=ExtractFilePath(Src);
- // Map source directory to namespaced
- aRule:='{s*:'+aDir+'}';
- FFPMakeMap.Values[aRule]:=ExtractFilePath(Dest);
- // Add original to include directory
- aRule:='{i+:'+aDir+'}';
- if FFPMakeMap.IndexOf(aRule)=-1 then
- FFPMakeMap.Add(aRule);
- end;
- function TNamespaceTool.GetUnitNameFromFile(aFile : String) : string;
- begin
- Result:=ExtractFileName(ChangeFileExt(aFile,''))
- end;
- procedure TNamespaceTool.SetForcedExt(AValue: String);
- begin
- if FForcedExt=AValue then Exit;
- if (aValue<>'') and (aValue[1]<>'.') then
- aValue:='.'+aValue;
- FForcedExt:=AValue;
- end;
- procedure TNamespaceTool.DoPrefixLog(Sender: TObject; aType: TEventType;
- const aMsg: String);
- begin
- DoMsg(aMsg,aType);
- end;
- procedure TNamespaceTool.SetSubdir(AValue: String);
- begin
- if FSubdir=AValue then Exit;
- FSubdir:=AValue;
- if FSubDir<>'' then
- FSubDir:=IncludeTrailingPathDelimiter(FSubDir);
- end;
- procedure TNamespaceTool.HandleFile(const aFileName: String; aRule : string; aOptions: array of String);
- Var
- aNewUnitName,aNewUnitFile,Ext,SrcDir,aUnitName,DestDir,aDummy,DestFN : String;
- P : TPrefixer;
- NeedUpdate : Boolean;
- Idx : Integer;
- begin
- NeedUpdate:=False;
- Ext:=FForcedExt;
- if Ext='' then
- Ext:=ExtractFileExt(aFileName);
- // Construct File name
- aUnitName:=GetUnitNameFromFile(aFilename);
- // Construct destination dir.
- SrcDir:=ExtractFilePath(aFileName);
- DestDir:=FDirMap.Values[aUnitName];
- if DestDir='' then
- DestDir:=FDirMap.Values[ExcludeTrailingBackslash(SrcDir)];
- if DestDir='' then
- DestDir:=SrcDir;
- case SubDirMode of
- sdmAppend : DestDir:=FSubDir+DestDir;
- sdmReplace : ; // do nothing
- end;
- DestDir:=IncludeTrailingPathDelimiter(DestDir);
- // No rule, see if there is a filename rule in known prefixes
- if aRule='' then
- begin
- Idx:=FKnownPrefixes.IndexOfName(aUnitName);
- if Idx<>-1 then
- FKnownPrefixes.GetNameValue(Idx,aDummy,aRule);
- end;
- aNewUnitFile:=TPrefixer.ApplyRule(aFileName,aDummy,aRule,FCasedFiles and (aRule<>''));
- aNewUnitName:=GetUnitNameFromFile(aNewUnitFile);
- if SameText(aNewUnitName,aUnitName) then
- begin
- DoMsg('Rule for %s does not result in different unit name, skipping.',[aFileName],etWarning);
- exit;
- end;
- DestFN:=DestDir+aNewUnitName+Ext;
- // Add new file to FPMake map.
- AddToFPMakeMap(aFileName,DestFN);
- if FileExists(DestFN) then
- DoMsg('File %s already exists, skipping generation',[DestFN]);
- // Create directory.
- CreateDestDir(DestDir);
- DoMsg('Converting %s to %s',[aFileName,DestFN]);
- if not FDryRun then
- begin
- P:=TPrefixer.Create(Self);
- try
- P.OnLog:=@DoPrefixLog;
- P.UnitFileMode:=fmInclude;
- P.IncludeUnitNameMode:=inmIfndef;
- P.FileName:=aFileName;
- P.NameSpace:=TPrefixer.ExtractPrefix(aRule);
- P.KnownNameSpaces.AddStrings(FKnownPrefixes);
- P.SkipDestFileName:=FileExists(DestFN);
- P.DestFileName:=DestFN;
- P.CreateBackups:=FBackup;
- P.CasedFileNames:=FCasedFiles;
- P.Params.AddStrings(aOptions);
- P.Params.Add('-Fi'+ExtractFilePath(aFileName));
- P.Execute;
- finally
- P.Free;
- end;
- end;
- If NeedUpdate then
- begin
- FKnownPrefixes.Values[aUnitName]:='*'+aNewUnitName;
- FWritePrefixes:=True;
- end;
- end;
- Function TNamespaceTool.AddNamespaceNameToFpMake(const aFileName : string) : TChangeFPMakeResult;
- const
- namespacelist = 'namespaces.lst';
- Var
- aFile : TStringList;
- I : Integer;
- aLine : string;
- begin
- Result:=cmrFailed;
- aFile:=TStringList.Create;
- try
- aFile.LoadFromFile(aFileName);
- i:=aFile.Count-1;
- while (I>=0) and (Result=cmrFailed) do
- begin
- if Pos('p.namespacemap',LowerCase(aFile[i]))>0 then
- result:=cmrAlreadyDone;
- Dec(I);
- end;
- i:=aFile.Count-1;
- while (I>=0) and (Result=cmrFailed) do
- begin
- aLine:=aFile[i];
- if pos('{$ifndef ALLPACKAGES}',aLine)>0 then
- if Pos('run',Lowercase(aFile[i+1]))>0 then
- begin
- aFile.Insert(I,'');
- aFile.Insert(I,Format(' P.NamespaceMap:=''%s'';',[namespacelist]));
- aFile.Insert(I,'');
- Result:=cmrOK;
- end;
- Dec(I);
- end;
- if Result=cmrOK then
- aFile.SaveToFile(aFileName);
- finally
- aFile.Free;
- end;
- end;
- procedure TNamespaceTool.WritePackageNameSpaceFile(aDir : String; aList : TStrings; DoClear : Boolean = True);
- Var
- FN : String;
- begin
- if aDir<>'' then
- aDir:=IncludeTrailingPathDelimiter(aDir);
- if (FFPMakeNameSpaceFile='') or (FFPMakeMap.Count=0) then
- exit;
- FN:=aDir+FFPMakeNameSpaceFile;
- DoMsg('Writing fpmake map file to %s, writing %d rules',[FN,FFPMakeMap.Count]);
- FFPMakeMap.SaveToFile(FN);
- if DoClear then
- FFPMakeMap.Clear;
- if FileExists(aDir+'fpmake.pp') then
- Case AddNamespaceNameToFpMake(aDir+'fpmake.pp') of
- cmrFailed : DoMsg('Failed to set NamespaceMap to file "%s"',[FN],etError);
- cmrAlreadyDone : DoMsg('NamespaceMap already set in "%s"',[FN],etWarning);
- cmrOK : DoMsg('Added NamespaceMap to file "%s"',[FN],etInfo);
- end
- end;
- constructor TNamespaceTool.Create(aOwner: TComponent);
- begin
- inherited Create(aOwner);
- FDirmap:=TStringList.Create;
- FKnownPrefixes:=TStringList.Create;
- FFPMakeMap:=TStringList.Create;
- FDoneFileName:=DefaultDoneList;
- end;
- destructor TNamespaceTool.Destroy;
- begin
- FreeAndNil(FDirmap);
- FreeAndNil(FKnownPrefixes);
- FreeAndNil(FFPMakeMap);
- inherited Destroy;
- end;
- procedure TNamespaceTool.Init;
- begin
- if (PrefixesFileName<>'') then
- begin
- KnownPrefixes.LoadFromFile(PrefixesFileName);
- DoMsg('Load of %s results in %d known prefixes',[PrefixesFileName,KnownPrefixes.Count]);
- end;
- if (DirMapFileName<>'') then
- begin
- Dirmap.LoadFromFile(DirMapFileName);
- DoMsg('Load of %s results in %d directory mappings',[DirMapFileName,DirMap.Count]);
- end;
- end;
- procedure TNamespaceTool.SplitLine(aLine: String; out aFileName, aRule: String;
- var aOpts: TStringDynArray);
- begin
- SplitRuleLine(aLine,aFileName,aRule,FLastDir,FLastRule,aOpts,FLastOpts);
- end;
- Class procedure TNamespaceTool.SplitRuleLine(aLine: String; out aFileName, aRule: String; var AlastDir, aLastRule : String; var aOpts, aLastOpts: TStringDynArray);
- var
- I,P : Integer;
- aDir,FN,Opt : String;
- begin
- aRule:='';
- aFileName:='';
- aOpts:=[];
- P:=Pos(';',aLine);
- if P=0 then
- begin
- FN:=aLine;
- SetLength(aOpts,0);
- end
- else
- begin
- FN:=Copy(aLine,1,P-1);
- Opt:=Trim(Copy(aLine,P+1));
- SetLength(aOpts,Length(Opt));
- I:=0;
- Repeat
- P:=Pos(' ',Opt);
- if P=0 then
- P:=Length(Opt)+1;
- if p>1 then
- begin
- aOpts[I]:=Copy(Opt,1,P-1);
- Opt:=Trim(Copy(Opt,P+1));
- inc(I);
- end;
- until (Opt='');
- SetLength(aOpts,I);
- end;
- P:=Pos('=',FN);
- if P<>0 then
- begin
- aRule:=Copy(FN,P+1);
- FN:=Copy(FN,1,P-1);
- end;
- aFileName:=FN;
- // Use previous rule ?
- aDir:=ExtractFilePath(FN);
- if aDir=aLastDir then
- begin
- if (aRule='') then
- aRule:=aLastRule;
- if Length(aOpts)=0 then
- aOpts:=aLastOpts;
- end;
- aLastDir:=aDir;
- aLastRule:=aRule;
- aLastOpts:=aOpts;
- end;
- function TNamespaceTool.GetPackageDir(const aFileName : string) : string;
- Var
- P : Integer;
- begin
- Result:='';
- if aFileName='' then
- exit;
- P:=Pos('/',aFileName,2);
- if P=0 then
- exit;
- Result:=Copy(aFileName,1,P);
- If Result[1]='/' then
- Delete(Result,1,1);
- end;
- procedure TNamespaceTool.HandleFileList(const aFileName : String);
- begin
- DoHandleFileList(aFileName);
- if FWritePrefixes and Update then
- begin
- DoMsg('Updating known prefixes file: %s ',[PrefixesFileName]);
- if not FDryRun then
- FKnownPrefixes.SaveToFile(FPrefixesFileName);
- end;
- end;
- procedure TNamespaceTool.DoHandleFileList(const aFileName : String);
- Var
- List,Done : TStringList;
- aLine,FN,FNDir, LastPackageDir,aRule : String;
- aOpts : TStringDynArray;
- begin
- aOpts:=[];
- Done:=Nil;
- LastPackageDir:='';
- List:=TStringList.Create;
- try
- Done:=TStringList.Create;
- if (not FRestart) and fileExists(FDoneFileName) then
- Done.LoadFromFile(FDoneFileName);
- List.LoadFromFile(aFileName);
- For aLine in List do
- begin
- // Lines have 3 parts
- // FileName=Rule;Compile Options
- SplitLine(aLine,FN,aRule,aOpts);
- FNDir:=GetPackageDir(FN);
- if (LastPackageDir<>FNDir) then
- begin
- if (LastPackageDir<>'') and (FFPMakeNameSpaceFile<>'') then
- WritePackageNameSpaceFile(LastPackageDir,List);
- LastPackageDir:=FNDir;
- end;
- if Done.indexOf(FN)=-1 then
- begin
- try
- HandleFile(FN,aRule,aOpts);
- Done.Add(FN);
- except
- On E : Exception do
- DoMsg('Error %s while handling file %s : %s',[E.ClassName,FN,E.Message],etError);
- end;
- end;
- end;
- if (LastPackageDir<>'') and (FFPMakeNameSpaceFile<>'') then
- WritePackageNameSpaceFile(LastPackageDir,List);
- finally
- Done.SaveToFile(FDoneFileName);
- List.Free;
- end;
- end;
- procedure TNamespaceTool.CreateKnown(const aFileName: String);
- Var
- List,Done : TStringList;
- aRule,aLine,FN,aUnit,aNewUnit : String;
- aOpts : TStringDynArray;
- begin
- Done:=Nil;
- FLastDir:='';
- FLastRule:='';
- aOpts:=[];
- if FPrefixesFileName='' then
- FPrefixesFileName:=ChangeFileExt(aFileName,'.map');
- List:=TStringList.Create;
- try
- Done:=TStringList.Create;
- if FileExists(FPrefixesFileName) then
- Done.LoadFromFile(FPrefixesFileName);
- List.LoadFromFile(aFileName);
- // Lines have 3 parts
- // FileName=Rule;Compile Options
- For aLine in List do
- begin
- SplitLine(aLine,FN,aRule,aOpts);
- aUnit:=ChangeFileExt(ExtractFileName(FN),'');
- aNewUnit:=ChangeFileExt(ExtractFileName(TPrefixer.ApplyRule(FN,aUnit,aRule,FCasedFiles)),'');
- Done.Values[aUnit]:='*'+aNewUnit;
- end;
- if FDryRun then
- begin
- for aLine in Done do
- DoMsg(aLine)
- end
- else
- Done.SaveToFile(FPrefixesFileName);
- finally
- Done.SaveToFile('done.tmp');
- Done.Free;
- List.Free;
- end;
- end;
- end.
|