mirror of
https://gitlab.com/freepascal.org/lazarus/lazarus.git
synced 2025-11-21 06:00:16 +01:00
1103 lines
37 KiB
ObjectPascal
1103 lines
37 KiB
ObjectPascal
{
|
|
***************************************************************************
|
|
* *
|
|
* This source is free software; you can redistribute it and/or modify *
|
|
* it under the terms of the GNU General Public License as published by *
|
|
* the Free Software Foundation; either version 2 of the License, or *
|
|
* (at your option) any later version. *
|
|
* *
|
|
* This code is distributed in the hope that it will be useful, but *
|
|
* WITHOUT ANY WARRANTY; without even the implied warranty of *
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
|
|
* General Public License for more details. *
|
|
* *
|
|
* A copy of the GNU General Public License is available on the World *
|
|
* Wide Web at <http://www.gnu.org/copyleft/gpl.html>. You can also *
|
|
* obtain it by writing to the Free Software Foundation, *
|
|
* Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
|
|
* *
|
|
***************************************************************************
|
|
|
|
Author: Mattias Gaertner
|
|
|
|
Abstract:
|
|
Called after checking what packages need compile.
|
|
Check source files and compiled files for name conflicts between packages.
|
|
|
|
ToDo:
|
|
- project compiler option verbosity: dialog on duplicate files
|
|
- save date ignore
|
|
- use date ignore
|
|
- clear ignore on
|
|
- clean build
|
|
- error cant find include file
|
|
unit_f_cant_find_ppu=10022_F_Can't find unit $1 used by $2
|
|
unit_u_ppu_invalid_header=10007_U_PPU Invalid Header (no PPU at the begin)
|
|
unit_f_cant_compile_unit=10021_F_Can't compile unit $1, no sources available
|
|
unit_f_cant_find_ppu=10022_F_Can't find unit $1 used by $2
|
|
unit_w_unit_name_error=10023_W_Unit $1 was not found but $2 exists
|
|
unit_f_unit_name_error=10024_F_Unit $1 searched but $2 found
|
|
unit_u_recompile_crc_change=10028_U_Recompiling $1, checksum changed for $2
|
|
unit_u_recompile_source_found_alone=10029_U_Recompiling $1, source found only
|
|
unit_u_recompile_staticlib_is_older=10030_U_Recompiling unit, static lib is older than ppufile
|
|
unit_u_recompile_sharedlib_is_older=10031_U_Recompiling unit, shared lib is older than ppufile
|
|
unit_u_recompile_obj_and_asm_older=10032_U_Recompiling unit, obj and asm are older than ppufile
|
|
unit_u_recompile_obj_older_than_asm=10033_U_Recompiling unit, obj is older than asm
|
|
unit_w_cant_compile_unit_with_changed_incfile=10040_W_Can't recompile unit $1, but found modifed include files
|
|
unit_u_source_modified=10041_U_File $1 is newer than the one used for creating PPU file $2
|
|
}
|
|
unit InterPkgConflictFiles;
|
|
|
|
{$mode objfpc}{$H+}
|
|
|
|
interface
|
|
|
|
uses
|
|
// RTL + FCL + LCL
|
|
Classes, SysUtils, types, math, contnrs, InterfaceBase,
|
|
Forms, ComCtrls, Controls, ButtonPanel, Themes, Graphics, StdCtrls, Buttons,
|
|
// CodeTools
|
|
BasicCodeTools, DefineTemplates, CodeToolManager, FileProcs,
|
|
// LazUtils
|
|
LazFileUtils, LazFileCache, AvgLvlTree,
|
|
// IDEIntf
|
|
ProjectIntf, CompOptsIntf, IDEWindowIntf, LazIDEIntf, IDEMsgIntf, IDEExternToolIntf,
|
|
// IDE
|
|
CompilerOptions, EnvironmentOpts, IDEProcs, DialogProcs, LazarusIDEStrConsts,
|
|
TransferMacros, LazConf, PackageDefs, PackageSystem;
|
|
|
|
type
|
|
TPGInterPkgOwnerInfo = class
|
|
public
|
|
Name: string;
|
|
Owner: TObject;
|
|
HasOptionUr: boolean;
|
|
CompOptions: TBaseCompilerOptions;
|
|
BaseDir: string;
|
|
SrcDirs: string; // unitpath without inherited
|
|
IncDirs: string; // incpath without inherited and without SrcDirs
|
|
UnitOutDir: string; // can be empty -> if empty FPC creates ppu in SrcDirs
|
|
end;
|
|
|
|
{ TPGInterPkgFile }
|
|
|
|
TPGInterPkgFile = class
|
|
public
|
|
FullFilename: string;
|
|
ShortFilename: string;
|
|
AnUnitName: string;
|
|
OwnerInfo: TPGInterPkgOwnerInfo;
|
|
constructor Create(TheFullFilename, TheUnitName: string; Owner: TPGInterPkgOwnerInfo);
|
|
end;
|
|
TPGInterPkgFileArray = array of TPGInterPkgFile;
|
|
|
|
{ TPGIPAmbiguousFileGroup }
|
|
|
|
TPGIPAmbiguousFileGroup = class
|
|
public
|
|
CompiledFiles: TPGInterPkgFileArray;
|
|
Sources: TPGInterPkgFileArray;
|
|
function Add(SrcFile, PPUFile: TPGInterPkgFile): integer;
|
|
function IndexOfOwner(OwnerInfo: TPGInterPkgOwnerInfo): integer;
|
|
procedure Switch(Index1, Index2: integer);
|
|
end;
|
|
|
|
TPGIPCategory = (
|
|
pgipOrphanedCompiled,
|
|
pgipDuplicateSource
|
|
);
|
|
|
|
{ TPGIPConflictsDialog }
|
|
|
|
TPGIPConflictsDialog = class(TForm)
|
|
ButtonPanel1: TButtonPanel;
|
|
ConflictsTreeView: TTreeView;
|
|
IDEDialogLayoutStorage1: TIDEDialogLayoutStorage;
|
|
ImageList1: TImageList;
|
|
procedure ConflictsTreeViewAdvancedCustomDrawItem(Sender: TCustomTreeView;
|
|
Node: TTreeNode; {%H-}State: TCustomDrawState; Stage: TCustomDrawStage;
|
|
var {%H-}PaintImages, {%H-}DefaultDraw: Boolean);
|
|
procedure ConflictsTreeViewMouseDown(Sender: TObject; {%H-}Button: TMouseButton;
|
|
{%H-}Shift: TShiftState; X, Y: Integer);
|
|
procedure DeleteSelectedFilesButtonClick(Sender: TObject);
|
|
procedure FormClose(Sender: TObject; var {%H-}CloseAction: TCloseAction);
|
|
procedure FormCreate(Sender: TObject);
|
|
procedure OkButtonClick(Sender: TObject);
|
|
private
|
|
DeleteSelectedFilesButton: TButton;
|
|
FImgIndexChecked: integer;
|
|
FImgIndexUnchecked: integer;
|
|
FCategoryNodes: array[TPGIPCategory] of TTreeNode;
|
|
procedure UpdateButtons;
|
|
procedure IgnoreConflicts;
|
|
public
|
|
FileGroups: TObjectList; // list of TPGIPAmbiguousFileGroup
|
|
FilesChanged: boolean;
|
|
procedure Init(Groups: TObjectList);
|
|
end;
|
|
|
|
function CheckInterPkgFiles(IDEObject: TObject;
|
|
PkgList: TFPList; out FilesChanged: boolean
|
|
): boolean; // returns false if user cancelled
|
|
function FilenameIsCompiledSource(aFilename: string): boolean;
|
|
|
|
implementation
|
|
|
|
{$R *.lfm}
|
|
|
|
function ComparePGInterPkgFullFilenames(File1, File2: Pointer): integer;
|
|
var
|
|
F1: TPGInterPkgFile absolute File1;
|
|
F2: TPGInterPkgFile absolute File2;
|
|
begin
|
|
Result:=CompareFilenames(F1.FullFilename,F2.FullFilename);
|
|
end;
|
|
|
|
function ComparePGInterPkgUnitnames(File1, File2: Pointer): integer;
|
|
var
|
|
F1: TPGInterPkgFile absolute File1;
|
|
F2: TPGInterPkgFile absolute File2;
|
|
begin
|
|
Assert(Assigned(F1), 'ComparePGInterPkgUnitnames: File1=Nil.');
|
|
Assert(Assigned(F2), 'ComparePGInterPkgUnitnames: File2=Nil.');
|
|
Result:=CompareDottedIdentifiers(PChar(F1.AnUnitName),PChar(F2.AnUnitName));
|
|
end;
|
|
|
|
function ComparePGInterPkgShortFilename(File1, File2: Pointer): integer;
|
|
var
|
|
F1: TPGInterPkgFile absolute File1;
|
|
F2: TPGInterPkgFile absolute File2;
|
|
begin
|
|
// compare case insensitive to find cross platform duplicates
|
|
Result:=CompareFilenamesIgnoreCase(F1.ShortFilename,F2.ShortFilename);
|
|
end;
|
|
|
|
{ TPGIPAmbiguousFileGroup }
|
|
|
|
function TPGIPAmbiguousFileGroup.Add(SrcFile, PPUFile: TPGInterPkgFile
|
|
): integer;
|
|
begin
|
|
if (SrcFile=nil) and (PPUFile=nil) then
|
|
RaiseException('');
|
|
if (SrcFile<>nil) and (PPUFile<>nil) and (PPUFile.OwnerInfo<>SrcFile.OwnerInfo) then
|
|
RaiseException('bug: not equal: PPUFile.OwnerInfo='+PPUFile.OwnerInfo.Name+' SrcFile.OwnerInfo='+SrcFile.OwnerInfo.Name);
|
|
if (SrcFile<>nil) and FilenameIsCompiledSource(SrcFile.ShortFilename) then
|
|
RaiseException('bug: src is compiled file: SrcFile.Filename='+SrcFile.FullFilename);
|
|
if (PPUFile<>nil) and not FilenameIsCompiledSource(PPUFile.ShortFilename) then
|
|
RaiseException('bug: compiled file is source:'+PPUFile.FullFilename);
|
|
Result:=length(CompiledFiles);
|
|
SetLength(CompiledFiles,Result+1);
|
|
SetLength(Sources,Result+1);
|
|
Sources[Result]:=SrcFile;
|
|
CompiledFiles[Result]:=PPUFile
|
|
end;
|
|
|
|
function TPGIPAmbiguousFileGroup.IndexOfOwner(OwnerInfo: TPGInterPkgOwnerInfo
|
|
): integer;
|
|
begin
|
|
Result:=length(Sources)-1;
|
|
while (Result>=0) do
|
|
begin
|
|
if (Sources[Result]<>nil) then
|
|
begin
|
|
if (Sources[Result].OwnerInfo=OwnerInfo) then exit;
|
|
end else begin
|
|
if (CompiledFiles[Result].OwnerInfo=OwnerInfo) then exit;
|
|
end;
|
|
dec(Result);
|
|
end;
|
|
end;
|
|
|
|
procedure TPGIPAmbiguousFileGroup.Switch(Index1, Index2: integer);
|
|
var
|
|
aFile: TPGInterPkgFile;
|
|
begin
|
|
aFile:=Sources[Index1]; Sources[Index1]:=Sources[Index2]; Sources[Index2]:=aFile;
|
|
aFile:=CompiledFiles[Index1]; CompiledFiles[Index1]:=CompiledFiles[Index2]; CompiledFiles[Index2]:=aFile;
|
|
end;
|
|
|
|
{ TPGIPConflictsDialog }
|
|
|
|
procedure TPGIPConflictsDialog.ConflictsTreeViewAdvancedCustomDrawItem(
|
|
Sender: TCustomTreeView; Node: TTreeNode; State: TCustomDrawState;
|
|
Stage: TCustomDrawStage; var PaintImages, DefaultDraw: Boolean);
|
|
var
|
|
Detail: TThemedButton;
|
|
Details: TThemedElementDetails;
|
|
aSize: TSize;
|
|
NodeRect: Classes.TRect;
|
|
r: TRect;
|
|
begin
|
|
if Stage<>cdPostPaint then exit;
|
|
if TObject(Node.Data) is TPGIPAmbiguousFileGroup then begin
|
|
if Node.ImageIndex=FImgIndexChecked then
|
|
Detail := tbCheckBoxCheckedNormal
|
|
else
|
|
Detail := tbCheckBoxUncheckedNormal;
|
|
Details := ThemeServices.GetElementDetails(Detail);
|
|
aSize := ThemeServices.GetDetailSize(Details);
|
|
NodeRect:=Node.DisplayRect(false);
|
|
r:=Bounds(Node.DisplayIconLeft+(ImageList1.Width-aSize.cx) div 2,
|
|
NodeRect.Top+(NodeRect.Bottom-NodeRect.Top-aSize.cy) div 2,
|
|
aSize.cx,aSize.cy);
|
|
ThemeServices.DrawElement(ConflictsTreeView.Canvas.Handle,Details,r);
|
|
end;
|
|
end;
|
|
|
|
procedure TPGIPConflictsDialog.ConflictsTreeViewMouseDown(Sender: TObject;
|
|
Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
|
|
var
|
|
Node: TTreeNode;
|
|
begin
|
|
Node:=ConflictsTreeView.GetNodeAt(X,Y);
|
|
if Node=nil then exit;
|
|
if TObject(Node.Data) is TPGIPAmbiguousFileGroup then begin
|
|
if (X>=Node.DisplayIconLeft) and (X<Node.DisplayTextLeft) then begin
|
|
if Node.ImageIndex=FImgIndexChecked then
|
|
Node.ImageIndex:=FImgIndexUnchecked
|
|
else
|
|
Node.ImageIndex:=FImgIndexChecked;
|
|
Node.SelectedIndex:=Node.ImageIndex;
|
|
UpdateButtons;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TPGIPConflictsDialog.DeleteSelectedFilesButtonClick(Sender: TObject);
|
|
|
|
function DeleteFileAndAssociates(aFile: TPGInterPkgFile): boolean;
|
|
var
|
|
aFilename: String;
|
|
begin
|
|
if aFile=nil then exit(true);
|
|
aFilename:=aFile.FullFilename;
|
|
{$IFDEF VerboseCheckInterPkgFiles}
|
|
debugln(['DeleteFileGroup ',aFilename]);
|
|
{$ENDIF}
|
|
if DeleteFileInteractive(aFilename)<>mrOk then exit(false);
|
|
if FilenameIsPascalUnit(aFilename) then
|
|
begin
|
|
// unit source -> delete compiled files and resources
|
|
DeleteFileUTF8(ChangeFileExt(aFilename,'.ppu'));
|
|
DeleteFileUTF8(ChangeFileExt(aFilename,'.o'));
|
|
DeleteFileUTF8(ChangeFileExt(aFilename,'.rst'));
|
|
DeleteFileUTF8(ChangeFileExt(aFilename,'.rsj'));
|
|
DeleteFileUTF8(ChangeFileExt(aFilename,'.lfm'));
|
|
DeleteFileUTF8(ChangeFileExt(aFilename,'.dfm'));
|
|
DeleteFileUTF8(ChangeFileExt(aFilename,'.xfm'));
|
|
end else if FilenameIsCompiledSource(aFilename) then begin
|
|
// compiled file -> delete compiled files. Keep sources.
|
|
DeleteFileUTF8(ChangeFileExt(aFilename,'.ppu'));
|
|
DeleteFileUTF8(ChangeFileExt(aFilename,'.o'));
|
|
DeleteFileUTF8(ChangeFileExt(aFilename,'.rst'));
|
|
DeleteFileUTF8(ChangeFileExt(aFilename,'.rsj'));
|
|
if FileExistsCached(ChangeFileExt(aFilename,'.pas'))
|
|
or FileExistsCached(ChangeFileExt(aFilename,'.pp'))
|
|
or FileExistsCached(ChangeFileExt(aFilename,'.p')) then begin
|
|
// delete only compiled file
|
|
end else begin
|
|
// no source in this directory => delete copied lfm file
|
|
DeleteFileUTF8(ChangeFileExt(aFilename,'.lfm'));
|
|
DeleteFileUTF8(ChangeFileExt(aFilename,'.dfm'));
|
|
DeleteFileUTF8(ChangeFileExt(aFilename,'.xfm'));
|
|
end;
|
|
end;
|
|
Result:=true;
|
|
end;
|
|
|
|
var
|
|
Node: TTreeNode;
|
|
NextNode: TTreeNode;
|
|
FileGroup: TPGIPAmbiguousFileGroup;
|
|
IndexInGroup: integer;
|
|
ConflictCount: Integer;
|
|
begin
|
|
ConflictsTreeView.Items.BeginUpdate;
|
|
try
|
|
Node:=ConflictsTreeView.Items.GetFirstNode;
|
|
IndexInGroup:=-1;
|
|
ConflictCount:=0;
|
|
while Node<>nil do
|
|
begin
|
|
NextNode:=Node.GetNext;
|
|
if TObject(Node.Data) is TPGIPAmbiguousFileGroup then
|
|
begin
|
|
FileGroup:=TPGIPAmbiguousFileGroup(Node.Data);
|
|
inc(IndexInGroup);
|
|
if Node.ImageIndex=FImgIndexChecked then
|
|
begin
|
|
if not DeleteFileAndAssociates(FileGroup.Sources[IndexInGroup]) then exit;
|
|
if not DeleteFileAndAssociates(FileGroup.CompiledFiles[IndexInGroup]) then exit;
|
|
end;
|
|
if ((FileGroup.Sources[IndexInGroup]<>nil)
|
|
and FileExistsUTF8(FileGroup.Sources[IndexInGroup].FullFilename))
|
|
or ((FileGroup.CompiledFiles[IndexInGroup]<>nil)
|
|
and FileExistsUTF8(FileGroup.CompiledFiles[IndexInGroup].FullFilename))
|
|
then
|
|
inc(ConflictCount);
|
|
if IndexInGroup=length(FileGroup.Sources)-1 then
|
|
begin
|
|
if ConflictCount<=1 then begin
|
|
// conflict does not exist anymore
|
|
FilesChanged:=true;
|
|
Node:=Node.Parent;
|
|
NextNode:=Node.GetNextSkipChildren;
|
|
Node.Delete;
|
|
end;
|
|
IndexInGroup:=-1;
|
|
ConflictCount:=0;
|
|
end;
|
|
end;
|
|
Node:=NextNode;
|
|
end;
|
|
finally
|
|
ConflictsTreeView.Items.EndUpdate;
|
|
UpdateButtons;
|
|
end;
|
|
end;
|
|
|
|
procedure TPGIPConflictsDialog.FormClose(Sender: TObject;
|
|
var CloseAction: TCloseAction);
|
|
begin
|
|
IDEDialogLayoutList.SaveLayout(Self);
|
|
end;
|
|
|
|
procedure TPGIPConflictsDialog.FormCreate(Sender: TObject);
|
|
var
|
|
Details: TThemedElementDetails;
|
|
aSize: TSize;
|
|
Img: TBitmap;
|
|
begin
|
|
IDEDialogLayoutList.ApplyLayout(Self,Width,Height);
|
|
|
|
DeleteSelectedFilesButton:=TButton.Create(Self);
|
|
with DeleteSelectedFilesButton do
|
|
begin
|
|
Name:='DeleteSelectedFilesButton';
|
|
Caption:='Delete selected files';
|
|
Align:=alLeft;
|
|
AutoSize:=true;
|
|
OnClick:=@DeleteSelectedFilesButtonClick;
|
|
Parent:=ButtonPanel1;
|
|
end;
|
|
|
|
ButtonPanel1.OKButton.Kind:=bkIgnore;
|
|
ButtonPanel1.OKButton.Caption:='Ignore';
|
|
ButtonPanel1.OKButton.OnClick:=@OkButtonClick;
|
|
|
|
Details := ThemeServices.GetElementDetails(tbCheckBoxCheckedNormal);
|
|
aSize := ThemeServices.GetDetailSize(Details);
|
|
ImageList1.Width:=Max(16,aSize.cx);
|
|
ImageList1.Height:=Max(16,aSize.cy);
|
|
// add empty images
|
|
Img:=TBitmap.Create;
|
|
Img.TransparentMode:=tmFixed;
|
|
Img.TransparentColor:=0;
|
|
Img.Transparent:=true;
|
|
Img.SetSize(ImageList1.Width,ImageList1.Height);
|
|
FImgIndexChecked:=ImageList1.Add(Img,nil);
|
|
FImgIndexUnchecked:=ImageList1.Add(Img,nil);
|
|
Img.Free;
|
|
end;
|
|
|
|
procedure TPGIPConflictsDialog.OkButtonClick(Sender: TObject);
|
|
begin
|
|
IgnoreConflicts;
|
|
end;
|
|
|
|
procedure TPGIPConflictsDialog.UpdateButtons;
|
|
var
|
|
Node: TTreeNode;
|
|
DeleteCount: Integer;
|
|
ConflictCount: Integer;
|
|
begin
|
|
DeleteCount:=0;
|
|
ConflictCount:=0;
|
|
Node:=ConflictsTreeView.Items.GetFirstNode;
|
|
while Node<>nil do begin
|
|
if TObject(Node.Data) is TPGIPAmbiguousFileGroup then
|
|
begin
|
|
inc(ConflictCount);
|
|
if Node.ImageIndex=FImgIndexChecked then
|
|
inc(DeleteCount);
|
|
end;
|
|
Node:=Node.GetNext;
|
|
end;
|
|
DeleteSelectedFilesButton.Enabled:=DeleteCount>0;
|
|
if ConflictCount=0 then
|
|
IgnoreConflicts;
|
|
end;
|
|
|
|
procedure TPGIPConflictsDialog.IgnoreConflicts;
|
|
begin
|
|
// ToDo
|
|
ModalResult:=mrOk;
|
|
end;
|
|
|
|
procedure TPGIPConflictsDialog.Init(Groups: TObjectList);
|
|
|
|
function AddChild(ParentNode: TTreeNode; Caption: string): TTreeNode;
|
|
begin
|
|
Result:=ConflictsTreeView.Items.AddChild(ParentNode,Caption);
|
|
end;
|
|
|
|
var
|
|
i, j: Integer;
|
|
ItemNode: TTreeNode;
|
|
s: String;
|
|
FileGroupNode: TTreeNode;
|
|
FileGroup: TPGIPAmbiguousFileGroup;
|
|
SrcFile: TPGInterPkgFile;
|
|
CompiledFile: TPGInterPkgFile;
|
|
CurFile: TPGInterPkgFile;
|
|
c: TPGIPCategory;
|
|
begin
|
|
FileGroups:=Groups;
|
|
|
|
ConflictsTreeView.Items.BeginUpdate;
|
|
ConflictsTreeView.Items.Clear;
|
|
ConflictsTreeView.Images:=ImageList1;
|
|
for c in TPGIPCategory do
|
|
FCategoryNodes[c]:=nil;
|
|
for i:=0 to FileGroups.Count-1 do
|
|
begin
|
|
FileGroup:=TPGIPAmbiguousFileGroup(FileGroups[i]);
|
|
|
|
// category
|
|
if FileGroup.Sources[0]=nil then
|
|
begin
|
|
// orphaned compiled file
|
|
CurFile:=FileGroup.CompiledFiles[0];
|
|
c:=pgipOrphanedCompiled;
|
|
if FCategoryNodes[c]=nil then
|
|
FCategoryNodes[c]:=
|
|
ConflictsTreeView.Items.Add(nil,'Orphaned compiled files');
|
|
end else begin
|
|
// duplicate source file
|
|
CurFile:=FileGroup.Sources[0];
|
|
c:=pgipDuplicateSource;
|
|
if FCategoryNodes[c]=nil then
|
|
FCategoryNodes[c]:=
|
|
ConflictsTreeView.Items.Add(nil,'Duplicate source files');
|
|
end;
|
|
|
|
// file group
|
|
s:=ExtractFilename(CurFile.ShortFilename);
|
|
FileGroupNode:=AddChild(FCategoryNodes[c],s);
|
|
|
|
for j:=0 to length(FileGroup.Sources)-1 do
|
|
begin
|
|
SrcFile:=FileGroup.Sources[j];
|
|
CompiledFile:=FileGroup.CompiledFiles[j];
|
|
|
|
if SrcFile<>nil then
|
|
CurFile:=SrcFile
|
|
else
|
|
CurFile:=CompiledFile;
|
|
|
|
s:=ExtractFilename(CurFile.ShortFilename);
|
|
if CurFile.OwnerInfo.Owner is TLazPackage then
|
|
s+=' of package '+CurFile.OwnerInfo.Name
|
|
else
|
|
s+=' of '+CurFile.OwnerInfo.Name;
|
|
ItemNode:=AddChild(FileGroupNode,s);
|
|
if SrcFile=nil then
|
|
ItemNode.ImageIndex:=FImgIndexChecked // default: delete
|
|
else
|
|
ItemNode.ImageIndex:=FImgIndexUnchecked; // default: keep
|
|
ItemNode.SelectedIndex:=ItemNode.ImageIndex;
|
|
ItemNode.Data:=FileGroup;
|
|
begin
|
|
// file paths of compiled and src
|
|
if CompiledFile<>nil then
|
|
AddChild(ItemNode,'Compiled: '+CompiledFile.FullFilename);
|
|
if SrcFile<>nil then
|
|
AddChild(ItemNode,'Source: '+SrcFile.FullFilename)
|
|
else
|
|
AddChild(ItemNode,'No source found');
|
|
end;
|
|
end;
|
|
end;
|
|
// expand all nodes
|
|
for c in TPGIPCategory do
|
|
if FCategoryNodes[c]<>nil then
|
|
FCategoryNodes[c].Expand(true);
|
|
|
|
ConflictsTreeView.Items.EndUpdate;
|
|
|
|
UpdateButtons;
|
|
end;
|
|
|
|
{ TPGInterPkgFile }
|
|
|
|
constructor TPGInterPkgFile.Create(TheFullFilename, TheUnitName: string;
|
|
Owner: TPGInterPkgOwnerInfo);
|
|
begin
|
|
FullFilename:=TheFullFilename;
|
|
ShortFilename:=ExtractFileName(FullFilename);
|
|
AnUnitName:=TheUnitName;
|
|
OwnerInfo:=Owner;
|
|
end;
|
|
|
|
function CheckInterPkgFiles(IDEObject: TObject; PkgList: TFPList; out
|
|
FilesChanged: boolean): boolean;
|
|
{ Scan all source and output directories (Note: they are already cached, because
|
|
this method is called after the checks if a compile is needed).
|
|
Report strange ppu files and duplicate file names.
|
|
|
|
IDEObject can be a TProject, TLazPackage or TLazPackageGraph(building IDE)
|
|
PkgList is list of TLazPackage
|
|
}
|
|
var
|
|
OwnerInfos: TObjectList; // list of TPGInterPkgOwnerInfo
|
|
TargetOS: String;
|
|
TargetCPU: String;
|
|
LCLWidgetType: String;
|
|
FullFiles: TAvgLvlTree; // tree of TPGInterPkgFile sorted for FullFilename
|
|
Units: TAvgLvlTree; // tree of TPGInterPkgFile sorted for AnUnitName
|
|
ShortFiles: TAvgLvlTree; // tree of TPGInterPkgFile sorted for ShortFilename
|
|
AmbiguousFileGroups: TObjectList; // list of TPGIPAmbiguousFileGroup
|
|
|
|
procedure AddOwnerInfo(TheOwner: TObject);
|
|
var
|
|
LazDir: String;
|
|
CustomOptions: String;
|
|
p: Integer;
|
|
OwnerInfo: TPGInterPkgOwnerInfo;
|
|
begin
|
|
OwnerInfo:=TPGInterPkgOwnerInfo.Create;
|
|
OwnerInfos.Add(OwnerInfo);
|
|
OwnerInfo.Owner:=TheOwner;
|
|
if TheOwner is TLazPackage then
|
|
begin
|
|
OwnerInfo.Name:=TLazPackage(TheOwner).IDAsString;
|
|
OwnerInfo.CompOptions:=TLazPackage(TheOwner).LazCompilerOptions as TBaseCompilerOptions;
|
|
end else if TheOwner is TLazProject then
|
|
begin
|
|
OwnerInfo.Name:=TLazProject(TheOwner).GetTitleOrName;
|
|
OwnerInfo.CompOptions:=TLazProject(TheOwner).LazCompilerOptions as TBaseCompilerOptions;
|
|
end
|
|
else if TheOwner=PackageGraph then begin
|
|
// building IDE
|
|
OwnerInfo.Name:='#IDE';
|
|
LazDir:=AppendPathDelim(EnvironmentOptions.GetParsedLazarusDirectory);
|
|
OwnerInfo.BaseDir:=LazDir;
|
|
OwnerInfo.SrcDirs:=LazDir+'ide'
|
|
+';'+LazDir+'debugger'
|
|
+';'+LazDir+'packager'
|
|
+';'+LazDir+'designer'
|
|
+';'+LazDir+'converter';
|
|
OwnerInfo.IncDirs:=OwnerInfo.SrcDirs
|
|
+';'+LazDir+'ide'+PathDelim+'include'+PathDelim+TargetOS
|
|
+';'+LazDir+'ide'+PathDelim+'include'+PathDelim+GetDefaultSrcOSForTargetOS(TargetOS);
|
|
OwnerInfo.UnitOutDir:=LazDir+'units'+PathDelim+TargetCPU+'-'+TargetOS+PathDelim+LCLWidgetType;
|
|
end;
|
|
if OwnerInfo.CompOptions<>nil then begin
|
|
OwnerInfo.BaseDir:=OwnerInfo.CompOptions.BaseDirectory;
|
|
OwnerInfo.SrcDirs:=OwnerInfo.CompOptions.GetPath(
|
|
pcosUnitPath,icoNone,false,coptParsed,true);
|
|
OwnerInfo.IncDirs:=OwnerInfo.CompOptions.GetPath(
|
|
pcosIncludePath,icoNone,false,coptParsed,true);
|
|
if OwnerInfo.CompOptions.UnitOutputDirectory<>'' then
|
|
OwnerInfo.UnitOutDir:=OwnerInfo.CompOptions.GetUnitOutputDirectory(false);
|
|
CustomOptions:=OwnerInfo.CompOptions.ParsedOpts.GetParsedValue(pcosCustomOptions);
|
|
p:=1;
|
|
OwnerInfo.HasOptionUr:=FindNextFPCParameter(CustomOptions,'-Ur',p)>0;
|
|
end;
|
|
OwnerInfo.IncDirs:=TrimSearchPath(RemoveSearchPaths(OwnerInfo.IncDirs,OwnerInfo.SrcDirs),'');
|
|
OwnerInfo.UnitOutDir:=TrimFilename(OwnerInfo.UnitOutDir);
|
|
OwnerInfo.SrcDirs:=TrimSearchPath(OwnerInfo.SrcDirs,'');
|
|
{$IFDEF VerboseCheckInterPkgFiles}
|
|
debugln(['AddOwnerInfo Name="',OwnerInfo.Name,'"',
|
|
' SrcDirs="',CreateRelativeSearchPath(OwnerInfo.SrcDirs,OwnerInfo.BaseDir),'"',
|
|
' IncDirs="',CreateRelativeSearchPath(OwnerInfo.IncDirs,OwnerInfo.BaseDir),'"',
|
|
' UnitOutDir="',CreateRelativeSearchPath(OwnerInfo.UnitOutDir,OwnerInfo.BaseDir),'"',
|
|
'']);
|
|
{$ENDIF}
|
|
end;
|
|
|
|
procedure CollectFilesInDir(OwnerInfo: TPGInterPkgOwnerInfo; Dir: string;
|
|
var SearchedDirs: string; {%H-}IsIncDir: boolean);
|
|
var
|
|
Files: TStrings;
|
|
aFilename: String;
|
|
Ext: String;
|
|
AnUnitName: String;
|
|
NewFile: TPGInterPkgFile;
|
|
begin
|
|
if Dir='' then exit;
|
|
if not FilenameIsAbsolute(Dir) then
|
|
begin
|
|
debugln(['Inconsistency: CollectFilesInDir dir no absolute: "',Dir,'" Owner=',OwnerInfo.Name]);
|
|
exit;
|
|
end;
|
|
if SearchDirectoryInSearchPath(SearchedDirs,Dir)>0 then exit;
|
|
SearchedDirs+=';'+Dir;
|
|
Files:=nil;
|
|
try
|
|
CodeToolBoss.DirectoryCachePool.GetListing(Dir,Files,false);
|
|
for aFilename in Files do
|
|
begin
|
|
if (aFilename='') or (aFilename='.') or (aFilename='..') then continue;
|
|
if CompareFilenames(aFilename,'fpmake.pp')=0 then continue;
|
|
Ext:=LowerCase(ExtractFileExt(aFilename));
|
|
AnUnitName:='';
|
|
case Ext of
|
|
'.ppu','.o','.rst','.rsj','.pas','.pp','.p':
|
|
begin
|
|
AnUnitName:=ExtractFileNameOnly(aFilename);
|
|
if not IsDottedIdentifier(AnUnitName) then continue;
|
|
end;
|
|
'.inc', '.lfm', '.dfm': ;
|
|
else
|
|
continue;
|
|
end;
|
|
NewFile:=TPGInterPkgFile.Create(AppendPathDelim(Dir)+aFilename,
|
|
AnUnitName,OwnerInfo);
|
|
FullFiles.Add(NewFile);
|
|
ShortFiles.Add(NewFile);
|
|
if AnUnitName<>'' then
|
|
Units.Add(NewFile);
|
|
end;
|
|
finally
|
|
Files.Free;
|
|
end;
|
|
end;
|
|
|
|
procedure CollectFilesOfOwner(OwnerInfo: TPGInterPkgOwnerInfo);
|
|
var
|
|
SearchedDirs: String;
|
|
SearchPath: String;
|
|
p: Integer;
|
|
Dir: String;
|
|
begin
|
|
// find all unit and include FullFiles in src, inc and out dirs
|
|
SearchedDirs:='';
|
|
CollectFilesInDir(OwnerInfo,OwnerInfo.UnitOutDir,SearchedDirs,false);
|
|
SearchPath:=OwnerInfo.SrcDirs;
|
|
p:=1;
|
|
repeat
|
|
Dir:=GetNextDirectoryInSearchPath(SearchPath,p);
|
|
if Dir='' then break;
|
|
CollectFilesInDir(OwnerInfo,Dir,SearchedDirs,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: TAvgLvlTreeNode;
|
|
ONode: TAvgLvlTreeNode;
|
|
OFile: TPGInterPkgFile;
|
|
PPUFileName: String;
|
|
SearchFile: TPGInterPkgFile;
|
|
PPUNode: TAvgLvlTreeNode;
|
|
begin
|
|
Node:=Units.FindLowest;
|
|
while Node<>nil do begin
|
|
// for each .o file
|
|
ONode:=Node;
|
|
Node:=Node.Successor;
|
|
OFile:=TPGInterPkgFile(ONode.Data);
|
|
if not FilenameIsCompiledSource(OFile.ShortFilename) then continue;
|
|
if CompareFileExt(OFile.ShortFilename,'.ppu',false)=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
|
|
ShortFiles.RemovePointer(OFile);
|
|
FullFiles.RemovePointer(OFile);
|
|
Units.Delete(ONode);
|
|
OFile.Free;
|
|
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:=PackageGraph.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;
|
|
|
|
function OptionUrAllowsDuplicate(File1, File2: TPGInterPkgFile): boolean;
|
|
begin
|
|
Result:=true;
|
|
if File1.OwnerInfo.HasOptionUr
|
|
and File2.OwnerInfo.HasOptionUr then
|
|
exit;
|
|
if File1.OwnerInfo.HasOptionUr
|
|
and OwnerHasDependency(File2.OwnerInfo,File1.OwnerInfo) then
|
|
exit;
|
|
if File2.OwnerInfo.HasOptionUr
|
|
and OwnerHasDependency(File1.OwnerInfo,File2.OwnerInfo) then
|
|
exit;
|
|
Result:=false;
|
|
end;
|
|
|
|
function CheckIfFilesCanConflict(FileGroup: TPGIPAmbiguousFileGroup;
|
|
File1, File2: TPGInterPkgFile): boolean;
|
|
var
|
|
FileDir1: String;
|
|
FileDir2: String;
|
|
begin
|
|
Result:=false;
|
|
// report only one unit per package
|
|
if File1.OwnerInfo=File2.OwnerInfo then exit;
|
|
if (FileGroup<>nil) and (FileGroup.IndexOfOwner(File1.OwnerInfo)>=0)
|
|
then exit;
|
|
// check -Ur
|
|
if OptionUrAllowsDuplicate(File2,File1) then
|
|
exit;
|
|
// check shared directories
|
|
if CompareFilenames(File2.FullFilename,File1.FullFilename)=0 then
|
|
begin
|
|
// Two packages share directories
|
|
// It would would require a lenghty codetools check to find out if
|
|
// this is right or wrong
|
|
// => skip
|
|
exit;
|
|
end;
|
|
FileDir1:=ExtractFilePath(File1.FullFilename);
|
|
FileDir2:=ExtractFilePath(File2.FullFilename);
|
|
if (FindPathInSearchPath(FileDir1,File2.OwnerInfo.SrcDirs)>0)
|
|
or (FindPathInSearchPath(FileDir2,File1.OwnerInfo.SrcDirs)>0) then
|
|
begin
|
|
// File1 in SrcDirs of file owner 2
|
|
// or File2 in SrcDirs of file owner 1
|
|
// => a warning about sharing source directories is enough
|
|
// don't warn every shared file
|
|
// => skip
|
|
exit;
|
|
end;
|
|
|
|
Result:=true;
|
|
end;
|
|
|
|
procedure FindUnitSourcePPU(var TheUnit: TPGInterPkgFile; out UnitPPU: TPGInterPkgFile);
|
|
// find in same package the source of a ppu, or the ppu of a source
|
|
var
|
|
SearchPPU: Boolean;
|
|
AnUnitName: string;
|
|
|
|
function FindOther(Node: TAvgLvlTreeNode; Left: boolean): TPGInterPkgFile;
|
|
var
|
|
IsPPU: Boolean;
|
|
begin
|
|
while Node<>nil do begin
|
|
Result:=TPGInterPkgFile(Node.Data);
|
|
if CompareFilenames(ExtractFileNameOnly(Result.ShortFilename),
|
|
AnUnitName)<>0 then break;
|
|
if (TheUnit.OwnerInfo=Result.OwnerInfo) then
|
|
begin
|
|
IsPPU:=FilenameIsCompiledSource(Result.ShortFilename);
|
|
if SearchPPU=IsPPU then exit;
|
|
end;
|
|
if Left then
|
|
Node:=Node.Precessor
|
|
else
|
|
Node:=Node.Successor;
|
|
end;
|
|
Result:=nil;
|
|
end;
|
|
|
|
var
|
|
StartNode: TAvgLvlTreeNode;
|
|
h: TPGInterPkgFile;
|
|
begin
|
|
UnitPPU:=nil;
|
|
AnUnitName:=ExtractFileNameOnly(TheUnit.ShortFilename);
|
|
SearchPPU:=FilenameIsPascalUnit(TheUnit.ShortFilename); // search opposite
|
|
StartNode:=ShortFiles.FindPointer(TheUnit);
|
|
UnitPPU:=FindOther(StartNode,true);
|
|
if UnitPPU=nil then
|
|
UnitPPU:=FindOther(StartNode,false);
|
|
if not SearchPPU then begin
|
|
h:=TheUnit;
|
|
TheUnit:=UnitPPU;
|
|
UnitPPU:=h;
|
|
end;
|
|
end;
|
|
|
|
procedure CheckDuplicateUnits;
|
|
{ Check two or more packages have the same unit (ppu/o/pas/pp/p)
|
|
Unless A uses B and B has -Ur or A has -Ur and B uses A }
|
|
var
|
|
CurNode: TAvgLvlTreeNode;
|
|
CurUnit: TPGInterPkgFile;
|
|
FirstNodeSameUnitname: TAvgLvlTreeNode;
|
|
OtherNode: TAvgLvlTreeNode;
|
|
OtherFile: TPGInterPkgFile;
|
|
PPUFile: TPGInterPkgFile;
|
|
FileGroup: TPGIPAmbiguousFileGroup;
|
|
OtherPPUFile: TPGInterPkgFile;
|
|
i: Integer;
|
|
Msg: String;
|
|
SrcFile: TPGInterPkgFile;
|
|
begin
|
|
CurNode:=Units.FindLowest;
|
|
FirstNodeSameUnitname:=nil;
|
|
while CurNode<>nil do begin
|
|
CurUnit:=TPGInterPkgFile(CurNode.Data);
|
|
if (FirstNodeSameUnitname=nil)
|
|
or (ComparePGInterPkgUnitnames(CurUnit,TPGInterPkgFile(FirstNodeSameUnitname.Data))<>0) then
|
|
FirstNodeSameUnitname:=CurNode;
|
|
CurNode:=CurNode.Successor;
|
|
if CurUnit.OwnerInfo.HasOptionUr then continue;
|
|
|
|
// CurUnit is an unit without -Ur
|
|
// => check units with same name
|
|
FileGroup:=nil;
|
|
PPUFile:=nil;
|
|
SrcFile:=nil;
|
|
OtherNode:=FirstNodeSameUnitname;
|
|
while OtherNode<>nil do begin
|
|
OtherFile:=TPGInterPkgFile(OtherNode.Data);
|
|
if (ComparePGInterPkgUnitnames(CurUnit,OtherFile)<>0) then break;
|
|
// other unit with same name found
|
|
OtherNode:=OtherNode.Successor;
|
|
|
|
if not CheckIfFilesCanConflict(FileGroup,CurUnit,OtherFile) then
|
|
continue;
|
|
//debugln(['CheckPPUFilesInWrongDirs duplicate units found: file1="',CurUnit.FullFilename,'"(',CurUnit.OwnerInfo.Name,') file2="',OtherFile.FullFilename,'"(',OtherFile.OwnerInfo.Name,')']);
|
|
FindUnitSourcePPU(OtherFile,OtherPPUFile);
|
|
if FileGroup=nil then begin
|
|
SrcFile:=CurUnit;
|
|
FindUnitSourcePPU(SrcFile,PPUFile);
|
|
end;
|
|
if (SrcFile<>nil) and (OtherFile<>nil)
|
|
and (CompareFilenames(SrcFile.FullFilename,OtherFile.FullFilename)=0) then
|
|
begin
|
|
// two packages share source directories
|
|
// -> do not warn single files
|
|
continue;
|
|
end;
|
|
|
|
if (PPUFile<>nil) and (OtherPPUFile<>nil)
|
|
and (CompareFilenames(PPUFile.FullFilename,OtherPPUFile.FullFilename)=0)
|
|
and (OtherFile=nil) then begin
|
|
// the same ppu is in both packages
|
|
// ... and only one package has a source
|
|
// for example: two packages share output directories
|
|
// => ok
|
|
continue;
|
|
end;
|
|
|
|
if FileGroup=nil then begin
|
|
FileGroup:=TPGIPAmbiguousFileGroup.Create;
|
|
FileGroup.Add(SrcFile,PPUFile);
|
|
AmbiguousFileGroups.Add(FileGroup);
|
|
end;
|
|
FileGroup.Add(OtherFile,OtherPPUFile);
|
|
if (PPUFile<>nil) and (OtherPPUFile=nil) then
|
|
begin
|
|
// put the orphaned ppu at top
|
|
FileGroup.Switch(0,length(FileGroup.Sources)-1);
|
|
end;
|
|
end;
|
|
|
|
// create Warnings
|
|
if FileGroup<>nil then begin
|
|
for i:=0 to length(FileGroup.Sources)-1 do
|
|
begin
|
|
SrcFile:=FileGroup.Sources[i];
|
|
PPUFile:=FileGroup.CompiledFiles[i];
|
|
if SrcFile<>nil then
|
|
begin
|
|
Msg:=Format(lisDuplicateUnitIn, [SrcFile.AnUnitName, SrcFile.
|
|
OwnerInfo.Name]);
|
|
if PPUFile<>nil then
|
|
Msg+=', ppu="'+PPUFile.FullFilename+'"';
|
|
Msg+=', source="'+SrcFile.FullFilename+'"';
|
|
end else begin
|
|
Msg:=Format(lisDuplicateUnitIn, [PPUFile.AnUnitName, PPUFile.
|
|
OwnerInfo.Name]);
|
|
Msg+=', orphaned ppu "'+PPUFile.FullFilename+'"';
|
|
end;
|
|
if IDEMessagesWindow<>nil then
|
|
IDEMessagesWindow.AddCustomMessage(mluNote,Msg)
|
|
else
|
|
debugln('Warning: (lazarus) ',Msg);
|
|
end;
|
|
end;
|
|
|
|
// all duplicates of this unitname were found -> skip to next unitname
|
|
CurNode:=OtherNode;
|
|
end;
|
|
end;
|
|
|
|
procedure CheckDuplicateSrcFiles;
|
|
{ Check if a src file in pkg A exists 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 }
|
|
var
|
|
CurNode: TAvgLvlTreeNode;
|
|
CurFile: TPGInterPkgFile;
|
|
FirstNodeSameShortName: TAvgLvlTreeNode;
|
|
OtherNode: TAvgLvlTreeNode;
|
|
OtherFile: TPGInterPkgFile;
|
|
FileGroup: TPGIPAmbiguousFileGroup;
|
|
i: Integer;
|
|
Msg: String;
|
|
begin
|
|
CurNode:=ShortFiles.FindLowest;
|
|
FirstNodeSameShortName:=nil;
|
|
while CurNode<>nil do begin
|
|
CurFile:=TPGInterPkgFile(CurNode.Data);
|
|
if (FirstNodeSameShortName=nil)
|
|
or (ComparePGInterPkgShortFilename(CurFile,TPGInterPkgFile(FirstNodeSameShortName.Data))<>0) then
|
|
FirstNodeSameShortName:=CurNode;
|
|
CurNode:=CurNode.Successor;
|
|
if CurFile.AnUnitName<>'' then
|
|
continue; // units were already checked in CheckDuplicateUnits
|
|
|
|
// check files with same short name
|
|
FileGroup:=nil;
|
|
OtherNode:=FirstNodeSameShortName;
|
|
while OtherNode<>nil do begin
|
|
OtherFile:=TPGInterPkgFile(OtherNode.Data);
|
|
if (ComparePGInterPkgShortFilename(CurFile,OtherFile)<>0) then break;
|
|
OtherNode:=OtherNode.Successor;
|
|
|
|
if OtherFile.AnUnitName<>'' then
|
|
continue; // units were already checked in CheckDuplicateUnits
|
|
|
|
// other file with same short name found
|
|
if not CheckIfFilesCanConflict(FileGroup,CurFile,OtherFile) then
|
|
continue;
|
|
|
|
if FileGroup=nil then begin
|
|
FileGroup:=TPGIPAmbiguousFileGroup.Create;
|
|
FileGroup.Add(CurFile,nil);
|
|
AmbiguousFileGroups.Add(FileGroup);
|
|
end;
|
|
FileGroup.Add(OtherFile,nil);
|
|
end;
|
|
|
|
// create Warnings
|
|
if FileGroup<>nil then begin
|
|
for i:=0 to length(FileGroup.Sources)-1 do
|
|
begin
|
|
CurFile:=FileGroup.Sources[i];
|
|
Msg:='Duplicate file "'+ExtractFileName(CurFile.ShortFilename)+'"';
|
|
Msg+=' in "'+CurFile.OwnerInfo.Name+'"';
|
|
Msg+=', path="'+CurFile.FullFilename+'"';
|
|
if IDEMessagesWindow<>nil then
|
|
IDEMessagesWindow.AddCustomMessage(mluWarning,Msg)
|
|
else
|
|
debugln('Warning: (lazarus) ',Msg);
|
|
end;
|
|
end;
|
|
|
|
// all duplicates of this file were found -> skip to next group
|
|
CurNode:=OtherNode;
|
|
end;
|
|
end;
|
|
|
|
var
|
|
i: Integer;
|
|
{$IFDEF EnableCheckInterPkgFiles}
|
|
Dlg: TPGIPConflictsDialog;
|
|
{$ENDIF}
|
|
begin
|
|
Result:=true;
|
|
FilesChanged:=false;
|
|
if (PkgList=nil) or (PkgList.Count=0) then exit;
|
|
OwnerInfos:=TObjectList.create(true);
|
|
FullFiles:=TAvgLvlTree.Create(@ComparePGInterPkgFullFilenames);
|
|
Units:=TAvgLvlTree.Create(@ComparePGInterPkgUnitnames);
|
|
ShortFiles:=TAvgLvlTree.Create(@ComparePGInterPkgShortFilename);
|
|
AmbiguousFileGroups:=TObjectList.create(true);
|
|
{$IFDEF EnableCheckInterPkgFiles}
|
|
Dlg:=nil;
|
|
{$ENDIF}
|
|
try
|
|
// get target OS, CPU and LCLWidgetType
|
|
TargetOS:='$(TargetOS)';
|
|
GlobalMacroList.SubstituteStr(TargetOS);
|
|
if TargetOS='' then TargetOS:=GetCompiledTargetOS;
|
|
TargetCPU:='$(TargetCPU)';
|
|
GlobalMacroList.SubstituteStr(TargetCPU);
|
|
if TargetCPU='' then TargetCPU:=GetCompiledTargetCPU;
|
|
LCLWidgetType:='$(LCLWidgetType)';
|
|
GlobalMacroList.SubstituteStr(LCLWidgetType);
|
|
if LCLWidgetType='' then LCLWidgetType:=LCLPlatformDirNames[GetDefaultLCLWidgetType];
|
|
|
|
{$IFDEF VerboseCheckInterPkgFiles}
|
|
debugln(['CheckInterPkgFiles TargetOS=',TargetOS,' TargetCPU=',TargetCPU,' LCLWidgetType=',LCLWidgetType]);
|
|
{$ENDIF}
|
|
|
|
// get search paths
|
|
AddOwnerInfo(IDEObject);
|
|
for i:=0 to PkgList.Count-1 do
|
|
AddOwnerInfo(TObject(PkgList[i]));
|
|
|
|
// collect FullFiles
|
|
for i:=0 to OwnerInfos.Count-1 do
|
|
CollectFilesOfOwner(TPGInterPkgOwnerInfo(OwnerInfos[i]));
|
|
RemoveSecondaryFiles;
|
|
|
|
// checks
|
|
CheckDuplicateUnits;
|
|
CheckDuplicateSrcFiles;
|
|
if (AmbiguousFileGroups.Count=0) then exit;
|
|
|
|
// show warnings
|
|
if LazarusIDE<>nil then begin
|
|
{$IFDEF EnableCheckInterPkgFiles}
|
|
// IDE
|
|
Dlg:=TPGIPConflictsDialog.Create(nil);
|
|
Dlg.Init(AmbiguousFileGroups);
|
|
if Dlg.ShowModal<>mrOK then exit(false);
|
|
FilesChanged:=Dlg.FilesChanged;
|
|
{$ENDIF}
|
|
end;
|
|
finally
|
|
{$IFDEF EnableCheckInterPkgFiles}
|
|
Dlg.Free;
|
|
{$ENDIF}
|
|
AmbiguousFileGroups.Free;
|
|
Units.Free;
|
|
ShortFiles.Free;
|
|
FullFiles.FreeAndClear;
|
|
FullFiles.Free;
|
|
OwnerInfos.Free;
|
|
end;
|
|
end;
|
|
|
|
function FilenameIsCompiledSource(aFilename: string): boolean;
|
|
var
|
|
Ext: String;
|
|
begin
|
|
Ext:=lowercase(ExtractFileExt(aFilename));
|
|
Result:=(Ext='.ppu') or (Ext='.o') or (Ext='.rst') or (Ext='.rsj');
|
|
end;
|
|
|
|
end.
|
|
|