IDE: started checking orphaned ppu files of packages

git-svn-id: trunk@47981 -
This commit is contained in:
mattias 2015-02-24 19:17:25 +00:00
parent 2620f33806
commit e89f096e51

View File

@ -833,7 +833,8 @@ begin
end; end;
type type
TPGInterPkgOwnerInfo = record TPGInterPkgOwnerInfo = class
public
Name: string; Name: string;
Owner: TObject; Owner: TObject;
HasOptionUr: boolean; HasOptionUr: boolean;
@ -842,14 +843,16 @@ type
IncDirs: string; // incpath without inherited and without SrcDirs IncDirs: string; // incpath without inherited and without SrcDirs
UnitOutDir: string; // can be empty -> if empty FPC creates ppu in SrcDirs UnitOutDir: string; // can be empty -> if empty FPC creates ppu in SrcDirs
end; end;
POwnerInfo = ^TPGInterPkgOwnerInfo;
{ TPGInterPkgFile }
TPGInterPkgFile = class TPGInterPkgFile = class
public public
FullFilename: string; FullFilename: string;
ShortFilename: string; ShortFilename: string;
AnUnitName: string; AnUnitName: string;
Owner: TPGInterPkgOwnerInfo; OwnerInfo: TPGInterPkgOwnerInfo;
constructor Create(TheFullFilename, TheUnitName: string; Owner: TPGInterPkgOwnerInfo);
end; end;
function ComparePGInterPkgFullFilenames(File1, File2: Pointer): integer; function ComparePGInterPkgFullFilenames(File1, File2: Pointer): integer;
@ -860,6 +863,33 @@ begin
Result:=CompareFilenames(F1.FullFilename,F2.FullFilename); Result:=CompareFilenames(F1.FullFilename,F2.FullFilename);
end; end;
function ComparePGInterPkgUnitnames(File1, File2: Pointer): integer;
var
F1: TPGInterPkgFile absolute File1;
F2: TPGInterPkgFile absolute File2;
begin
Result:=CompareDottedIdentifiers(PChar(Pointer(F1.AnUnitName)),PChar(Pointer(F2.AnUnitName)));
end;
function ComparePGInterPkgShortFilename(File1, File2: Pointer): integer;
var
F1: TPGInterPkgFile absolute File1;
F2: TPGInterPkgFile absolute File2;
begin
Result:=CompareFilenames(F1.ShortFilename,F2.ShortFilename);
end;
{ TPGInterPkgFile }
constructor TPGInterPkgFile.Create(TheFullFilename, TheUnitName: string;
Owner: TPGInterPkgOwnerInfo);
begin
FullFilename:=TheFullFilename;
ShortFilename:=ExtractFileName(FullFilename);
AnUnitName:=TheUnitName;
OwnerInfo:=Owner;
end;
function TLazPackageGraph.CheckAmbiguousInterPkgFiles(IDEObject: TObject; function TLazPackageGraph.CheckAmbiguousInterPkgFiles(IDEObject: TObject;
PkgList: TFPList): TModalResult; PkgList: TFPList): TModalResult;
{ Scan all source and output directories (Note: they are already cached, because { Scan all source and output directories (Note: they are already cached, because
@ -868,78 +898,82 @@ function TLazPackageGraph.CheckAmbiguousInterPkgFiles(IDEObject: TObject;
IDEObject can be a TProject, TLazPackage or TLazPackageGraph(building IDE) IDEObject can be a TProject, TLazPackage or TLazPackageGraph(building IDE)
PkgList is list of TLazPackage PkgList is list of TLazPackage
Check the following:
} }
var var
OwnerInfos: array of TPGInterPkgOwnerInfo; OwnerInfos: TObjectList; // list of TPGInterPkgOwnerInfo
TargetOS: String; TargetOS: String;
TargetCPU: String; TargetCPU: String;
LCLWidgetType: String; LCLWidgetType: String;
Files: TAVLTree; FullFiles: TAVLTree; // tree of TPGInterPkgFile sorted for FullFilename
Units: TAVLTree; // tree of TPGInterPkgFile sorted for AnUnitName
ShortFiles: TAVLTree; // tree of TPGInterPkgFile sorted for ShortFilename
procedure InitOwnerInfo(OwnerInfo: POwnerInfo; TheOwner: TObject); procedure AddOwnerInfo(TheOwner: TObject);
var var
LazDir: String; LazDir: String;
CustomOptions: String; CustomOptions: String;
p: Integer; p: Integer;
OwnerInfo: TPGInterPkgOwnerInfo;
begin begin
FillByte(OwnerInfo^,SizeOf(TPGInterPkgOwnerInfo),0); OwnerInfo:=TPGInterPkgOwnerInfo.Create;
OwnerInfo^.Owner:=TheOwner; OwnerInfos.Add(OwnerInfo);
OwnerInfo.Owner:=TheOwner;
if TheOwner is TLazPackage then if TheOwner is TLazPackage then
begin begin
OwnerInfo^.Name:=TLazPackage(TheOwner).IDAsString; OwnerInfo.Name:=TLazPackage(TheOwner).IDAsString;
OwnerInfo^.CompOptions:=TLazPackage(TheOwner).LazCompilerOptions as TBaseCompilerOptions; OwnerInfo.CompOptions:=TLazPackage(TheOwner).LazCompilerOptions as TBaseCompilerOptions;
end else if TheOwner is TLazProject then end else if TheOwner is TLazProject then
begin begin
OwnerInfo^.Name:=TLazProject(TheOwner).GetTitleOrName; OwnerInfo.Name:=TLazProject(TheOwner).GetTitleOrName;
OwnerInfo^.CompOptions:=TLazProject(TheOwner).LazCompilerOptions as TBaseCompilerOptions; OwnerInfo.CompOptions:=TLazProject(TheOwner).LazCompilerOptions as TBaseCompilerOptions;
end end
else if TheOwner=Self then begin else if TheOwner=Self then begin
// building IDE // building IDE
OwnerInfo^.Name:='#IDE'; OwnerInfo.Name:='#IDE';
LazDir:=AppendPathDelim(EnvironmentOptions.GetParsedLazarusDirectory); LazDir:=AppendPathDelim(EnvironmentOptions.GetParsedLazarusDirectory);
OwnerInfo^.SrcDirs:=LazDir+'ide' OwnerInfo.SrcDirs:=LazDir+'ide'
+';'+LazDir+'debugger' +';'+LazDir+'debugger'
+';'+LazDir+'packager' +';'+LazDir+'packager'
+';'+LazDir+'designer' +';'+LazDir+'designer'
+';'+LazDir+'converter'; +';'+LazDir+'converter';
OwnerInfo^.IncDirs:=OwnerInfo^.SrcDirs OwnerInfo.IncDirs:=OwnerInfo.SrcDirs
+';'+LazDir+'ide'+PathDelim+'include'+PathDelim+TargetOS +';'+LazDir+'ide'+PathDelim+'include'+PathDelim+TargetOS
+';'+LazDir+'ide'+PathDelim+'include'+PathDelim+GetDefaultSrcOSForTargetOS(TargetOS); +';'+LazDir+'ide'+PathDelim+'include'+PathDelim+GetDefaultSrcOSForTargetOS(TargetOS);
OwnerInfo^.UnitOutDir:=LazDir+'units'+PathDelim+TargetCPU+'-'+TargetOS+PathDelim+LCLWidgetType; OwnerInfo.UnitOutDir:=LazDir+'units'+PathDelim+TargetCPU+'-'+TargetOS+PathDelim+LCLWidgetType;
end; end;
if OwnerInfo^.CompOptions<>nil then begin if OwnerInfo.CompOptions<>nil then begin
OwnerInfo^.SrcDirs:=OwnerInfo^.CompOptions.GetPath( OwnerInfo.SrcDirs:=OwnerInfo.CompOptions.GetPath(
pcosUnitPath,icoNone,false,coptParsed,true); pcosUnitPath,icoNone,false,coptParsed,true);
OwnerInfo^.IncDirs:=OwnerInfo^.CompOptions.GetPath( OwnerInfo.IncDirs:=OwnerInfo.CompOptions.GetPath(
pcosIncludePath,icoNone,false,coptParsed,true); pcosIncludePath,icoNone,false,coptParsed,true);
if OwnerInfo^.CompOptions.UnitOutputDirectory<>'' then if OwnerInfo.CompOptions.UnitOutputDirectory<>'' then
OwnerInfo^.UnitOutDir:=OwnerInfo^.CompOptions.GetUnitOutputDirectory(false); OwnerInfo.UnitOutDir:=OwnerInfo.CompOptions.GetUnitOutputDirectory(false);
CustomOptions:=OwnerInfo^.CompOptions.ParsedOpts.GetParsedValue(pcosCustomOptions); CustomOptions:=OwnerInfo.CompOptions.ParsedOpts.GetParsedValue(pcosCustomOptions);
p:=1; p:=1;
OwnerInfo^.HasOptionUr:=FindNextFPCParameter(CustomOptions,'-Ur',p)>0; OwnerInfo.HasOptionUr:=FindNextFPCParameter(CustomOptions,'-Ur',p)>0;
end; end;
OwnerInfo^.IncDirs:=TrimSearchPath(RemoveSearchPaths(OwnerInfo^.IncDirs,OwnerInfo^.SrcDirs),''); OwnerInfo.IncDirs:=TrimSearchPath(RemoveSearchPaths(OwnerInfo.IncDirs,OwnerInfo.SrcDirs),'');
OwnerInfo^.UnitOutDir:=TrimFilename(OwnerInfo^.UnitOutDir); OwnerInfo.UnitOutDir:=TrimFilename(OwnerInfo.UnitOutDir);
OwnerInfo^.SrcDirs:=TrimSearchPath(OwnerInfo^.SrcDirs,''); OwnerInfo.SrcDirs:=TrimSearchPath(OwnerInfo.SrcDirs,'');
end; end;
procedure CollectFilesInDir(OwnerInfo: POwnerInfo; Dir: string; procedure CollectFilesInDir(OwnerInfo: TPGInterPkgOwnerInfo; Dir: string;
var SearchedDirs: string); var SearchedDirs: string; IsIncDir: boolean);
var var
Files: TStrings; Files: TStrings;
aFilename: String; aFilename: String;
Ext: String; Ext: String;
AnUnitName: String;
NewFile: TPGInterPkgFile;
begin begin
if Dir='' then exit; if Dir='' then exit;
if not FilenameIsAbsolute(Dir) then if not FilenameIsAbsolute(Dir) then
begin begin
debugln(['Inconsistency: CollectFilesInDir dir no absolute: "',Dir,'" Owner=',OwnerInfo^.Name]); debugln(['Inconsistency: CollectFilesInDir dir no absolute: "',Dir,'" Owner=',OwnerInfo.Name]);
exit; exit;
end; end;
if SearchDirectoryInSearchPath(SearchedDirs,Dir)>0 then exit; if SearchDirectoryInSearchPath(SearchedDirs,Dir)>0 then exit;
SearchedDirs+=';'+Dir;
Files:=nil; Files:=nil;
try try
CodeToolBoss.DirectoryCachePool.GetListing(Dir,Files,false); CodeToolBoss.DirectoryCachePool.GetListing(Dir,Files,false);
@ -947,29 +981,194 @@ var
begin begin
if (aFilename='') or (aFilename='.') or (aFilename='..') then continue; if (aFilename='') or (aFilename='.') or (aFilename='..') then continue;
Ext:=LowerCase(ExtractFileExt(aFilename)); Ext:=LowerCase(ExtractFileExt(aFilename));
if Ext='.ppu' then ; AnUnitName:='';
case Ext of
'.ppu','.o','.pas','.pp','.p':
if IsIncDir then
begin
AnUnitName:=ExtractFilename(aFilename);
if not IsDottedIdentifier(AnUnitName) then continue;
end;
'.inc': ;
else
continue;
end;
NewFile:=TPGInterPkgFile.Create(AppendPathDelim(Dir)+aFilename,
AnUnitName,OwnerInfo);
FullFiles.Add(NewFile);
ShortFiles.Add(NewFile);
if AnUnitName<>'' then
Units.Add(NewFile);
end; end;
finally finally
Files.Free; Files.Free;
end; end;
end; end;
procedure CollectFilesOfOwner(OwnerInfo: POwnerInfo); procedure CollectFilesOfOwner(OwnerInfo: TPGInterPkgOwnerInfo);
var var
SearchedDirs: String; SearchedDirs: String;
SearchPath: String; SearchPath: String;
p: Integer; p: Integer;
Dir: String; Dir: String;
begin begin
// find all unit and include files in src, inc and out dirs // find all unit and include FullFiles in src, inc and out dirs
SearchedDirs:=''; SearchedDirs:='';
SearchPath:=OwnerInfo^.SrcDirs+';'+OwnerInfo^.IncDirs+';'+OwnerInfo^.UnitOutDir; CollectFilesInDir(OwnerInfo,OwnerInfo.UnitOutDir,SearchedDirs,false);
SearchPath:=OwnerInfo.SrcDirs;
p:=1; p:=1;
repeat repeat
Dir:=GetNextDirectoryInSearchPath(SearchPath,p); Dir:=GetNextDirectoryInSearchPath(SearchPath,p);
if Dir='' then break; if Dir='' then break;
CollectFilesInDir(OwnerInfo,Dir,SearchedDirs); CollectFilesInDir(OwnerInfo,Dir,SearchedDirs,false);
until false; until false;
SearchPath:=OwnerInfo.IncDirs;
p:=1;
repeat
Dir:=GetNextDirectoryInSearchPath(SearchPath,p);
if Dir='' then break;
CollectFilesInDir(OwnerInfo,Dir,SearchedDirs,true);
until false;
end;
procedure RemoveSecondaryFiles;
// remove each .o file if there is an .ppu file, so that there is only one
// warning per ppu file
var
Node: TAVLTreeNode;
ONode: TAVLTreeNode;
OFile: TPGInterPkgFile;
PPUFileName: String;
SearchFile: TPGInterPkgFile;
PPUNode: TAVLTreeNode;
begin
Node:=Units.FindLowest;
while Node<>nil do begin
// for each .o file
ONode:=Node;
Node:=Units.FindSuccessor(Node);
OFile:=TPGInterPkgFile(ONode.Data);
if CompareFileExt(OFile.ShortFilename,'o')<>0 then continue;
// search corresponding .ppu
PPUFileName:=ChangeFileExt(OFile.FullFilename,'.ppu');
SearchFile:=TPGInterPkgFile.Create(PPUFileName,'',nil);
PPUNode:=FullFiles.Find(SearchFile);
SearchFile.Free;
if PPUNode=nil then continue;
// remove .o file
Units.RemovePointer(OFile);
ShortFiles.RemovePointer(OFile);
FullFiles.Delete(ONode);
end;
end;
function OwnerHasDependency(Owner1, Owner2: TPGInterPkgOwnerInfo): boolean;
// returns true if Owner1 depends on Owner2
begin
if Owner1=Owner2 then exit(true);
if Owner1.Owner is TLazPackage then
begin
if Owner2.Owner is TLazPackage then
begin
Result:=FindDependencyRecursively(
TLazPackage(Owner1.Owner).FirstRequiredDependency,
TLazPackage(Owner2.Owner))<>nil;
end else begin
// Owner1 is package, Owner2 is project/IDE => not possible
Result:=false;
end;
end else begin
// Owner1 is project or IDE => true
Result:=true;
end;
end;
procedure CheckPPUFilesInWrongDirs;
{ Check if a ppu/o of pkg A has a unit (source or ppu) in another package B
Unless A uses B and B has -Ur or A has -Ur and B uses A
=> IDE: delete+retry or ignore or cancel
=> lazbuild: warn }
var
CurNode: TAVLTreeNode;
CurFile: TPGInterPkgFile;
Ext: String;
FirstNodeSameUnitname: TAVLTreeNode;
OtherNode: TAVLTreeNode;
OtherFile: TPGInterPkgFile;
SrcFileNode: TAVLTreeNode;
SrcFile: TPGInterPkgFile;
begin
CurNode:=Units.FindLowest;
FirstNodeSameUnitname:=nil;
while CurNode<>nil do begin
CurFile:=TPGInterPkgFile(CurNode.Data);
if (FirstNodeSameUnitname=nil)
or (ComparePGInterPkgUnitnames(CurFile,FirstNodeSameUnitname)<>0) then
FirstNodeSameUnitname:=CurNode;
CurNode:=Units.FindSuccessor(CurNode);
Ext:=lowercase(ExtractFileExt(CurFile.ShortFilename));
if (Ext<>'.ppu') and (Ext<>'.o') then continue;
// check units with same name
OtherNode:=FirstNodeSameUnitname;
while (OtherNode<>nil) do begin
OtherFile:=TPGInterPkgFile(OtherNode.Data);
if (ComparePGInterPkgUnitnames(CurFile,OtherFile)<>0) then break;
// other unit with same name found
OtherNode:=Units.FindSuccessor(OtherNode);
if OtherFile.OwnerInfo=CurFile.OwnerInfo then continue;
// ppu in one package, unit with same name in another package
debugln(['CheckPPUFilesInWrongDirs duplicate units found: file1="',CurFile.FullFilename,'"(',CurFile.OwnerInfo.Name,') file2="',OtherFile.FullFilename,'"(',OtherFile.OwnerInfo.Name,')']);
if CurFile.OwnerInfo.HasOptionUr
and OtherFile.OwnerInfo.HasOptionUr then
continue;
if CurFile.OwnerInfo.HasOptionUr
and OwnerHasDependency(OtherFile.OwnerInfo,CurFile.OwnerInfo) then
continue;
if OtherFile.OwnerInfo.HasOptionUr
and OwnerHasDependency(CurFile.OwnerInfo,OtherFile.OwnerInfo) then
continue;
// check if this package has a source for this ppu
SrcFileNode:=FirstNodeSameUnitname;
SrcFile:=nil;
while SrcFileNode<>nil do begin
SrcFile:=TPGInterPkgFile(SrcFileNode.Data);
if (ComparePGInterPkgUnitnames(CurFile,SrcFile)<>0) then
begin
SrcFileNode:=nil;
break;
end;
SrcFileNode:=Units.FindSuccessor(SrcFileNode);
if SrcFile.OwnerInfo<>CurFile.OwnerInfo then continue;
if FilenameIsPascalUnit(SrcFile.ShortFilename) then
break;
end;
if SrcFileNode<>nil then
begin
// ppu with src in one package, duplicate unit in another package
// Note: This could be right if the other package does not use the src file
// Otherwise: duplicate name (-Ur has been checked)
// ToDo: => warn
debugln(['CheckPPUFilesInWrongDirs duplicate units found: file1="',CurFile.FullFilename,'"(',CurFile.OwnerInfo.Name,',source=',SrcFile.ShortFilename,') file2="',OtherFile.FullFilename,'"(',OtherFile.OwnerInfo.Name,')']);
end else begin
// ppu with no src in one package, duplicate unit in another package
// (-Ur has been checked)
// => highly unlikely that this is right
// ToDo: => warn
debugln(['CheckPPUFilesInWrongDirs duplicate units found: file1="',CurFile.FullFilename,'"(',CurFile.OwnerInfo.Name,',no source) file2="',OtherFile.FullFilename,'"(',OtherFile.OwnerInfo.Name,')']);
end;
end;
end;
end;
procedure CheckDuplicateSrcFiles;
{ Check if a src file in pkg A with the same short name exist in another package B
Unless A uses B and B has -Ur or A has -Ur and B uses A
=> IDE: ignore or cancel
=> lazbuild: warn }
begin
end; end;
var var
@ -977,7 +1176,10 @@ var
begin begin
Result:=mrOk; Result:=mrOk;
if (PkgList=nil) or (PkgList.Count=0) then exit; if (PkgList=nil) or (PkgList.Count=0) then exit;
Files:=TAVLTree.Create(@ComparePGInterPkgFullFilenames); OwnerInfos:=TObjectList.create(true);
FullFiles:=TAVLTree.Create(@ComparePGInterPkgFullFilenames);
Units:=TAVLTree.Create(@ComparePGInterPkgUnitnames);
ShortFiles:=TAVLTree.Create(@ComparePGInterPkgShortFilename);
try try
// get target OS, CPU and LCLWidgetType // get target OS, CPU and LCLWidgetType
TargetOS:='$(TargetOS)'; TargetOS:='$(TargetOS)';
@ -991,17 +1193,24 @@ begin
if LCLWidgetType='' then LCLWidgetType:=LCLPlatformDirNames[GetDefaultLCLWidgetType]; if LCLWidgetType='' then LCLWidgetType:=LCLPlatformDirNames[GetDefaultLCLWidgetType];
// get search paths // get search paths
SetLength(OwnerInfos,PkgList.Count+1); AddOwnerInfo(IDEObject);
InitOwnerInfo(@OwnerInfos[0],IDEObject); for i:=0 to PkgList.Count-1 do
for i:=1 to PkgList.Count do AddOwnerInfo(TObject(PkgList[i]));
InitOwnerInfo(@OwnerInfos[i],TObject(PkgList[i-1]));
// collect files // collect FullFiles
for i:=0 to length(OwnerInfos)-1 do for i:=0 to OwnerInfos.Count-1 do
CollectFilesOfOwner(@OwnerInfos[i]); CollectFilesOfOwner(TPGInterPkgOwnerInfo(OwnerInfos[i]));
RemoveSecondaryFiles;
// checks
CheckPPUFilesInWrongDirs;
CheckDuplicateSrcFiles;
finally finally
Files.FreeAndClear; Units.Free;
Files.Free; ShortFiles.Free;
FullFiles.FreeAndClear;
FullFiles.Free;
OwnerInfos.Free;
end; end;
end; end;
@ -1448,6 +1657,7 @@ function TLazPackageGraph.FindDependencyRecursively(
end; end;
begin begin
if FirstDependency=nil then exit(nil);
MarkAllPackagesAsNotVisited; MarkAllPackagesAsNotVisited;
Result:=Find(FirstDependency); Result:=Find(FirstDependency);
end; end;
@ -1479,6 +1689,7 @@ function TLazPackageGraph.FindDependencyRecursively(
end; end;
begin begin
if FirstDependency=nil then exit(nil);
MarkAllPackagesAsNotVisited; MarkAllPackagesAsNotVisited;
Result:=Find(FirstDependency); Result:=Find(FirstDependency);
end; end;
@ -1511,6 +1722,7 @@ function TLazPackageGraph.FindConflictRecursively(
end; end;
begin begin
if FirstDependency=nil then exit(nil);
MarkAllPackagesAsNotVisited; MarkAllPackagesAsNotVisited;
Result:=Find(FirstDependency); Result:=Find(FirstDependency);
end; end;
@ -1540,6 +1752,7 @@ function TLazPackageGraph.FindRuntimePkgOnlyRecursively(
end; end;
begin begin
if FirstDependency=nil then exit(nil);
MarkAllPackagesAsNotVisited; MarkAllPackagesAsNotVisited;
Result:=Find(FirstDependency); Result:=Find(FirstDependency);
end; end;