mirror of
https://gitlab.com/freepascal.org/lazarus/lazarus.git
synced 2025-04-05 15:37:50 +02:00
441 lines
13 KiB
ObjectPascal
441 lines
13 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., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1335, USA. *
|
|
* *
|
|
***************************************************************************
|
|
|
|
Author: Mattias Gaertner
|
|
|
|
Abstract:
|
|
IDE dialog showing packages needing (re)build.
|
|
}
|
|
unit IDEInfoNeedBuild;
|
|
|
|
{$mode objfpc}{$H+}
|
|
|
|
interface
|
|
|
|
uses
|
|
Classes, SysUtils, Laz_AVL_Tree,
|
|
// LCL
|
|
Forms, Controls, StdCtrls, ButtonPanel, LCLType,
|
|
// LazUtils
|
|
LazUtilities, LazUTF8,
|
|
// IdeIntf
|
|
IDEWindowIntf, LazIDEIntf, ProjectIntf, PackageIntf, InputHistory,
|
|
// IDE
|
|
LazarusIDEStrConsts, PackageDefs, PackageSystem, Project,
|
|
EnvironmentOpts, IDEProcs, BuildManager;
|
|
|
|
type
|
|
TINeedBuild = (
|
|
inbNone,
|
|
inbNo,
|
|
inbNormal,
|
|
inbClean
|
|
);
|
|
|
|
TInfoNeedBuildItem = class
|
|
public
|
|
Target: TObject; // TProject, TLazPackage, LazarusIDE
|
|
Caption: string;
|
|
Filename: string;
|
|
NeedBuild: TINeedBuild;
|
|
Note: string;
|
|
end;
|
|
|
|
{ TIDEInfoNeedBuildDlg }
|
|
|
|
TIDEInfoNeedBuildDlg = class(TForm)
|
|
ButtonPanel1: TButtonPanel;
|
|
MainMemo: TMemo;
|
|
TargetComboBox: TComboBox;
|
|
TargetLabel: TLabel;
|
|
procedure FormClose(Sender: TObject; var {%H-}CloseAction: TCloseAction);
|
|
procedure FormCreate(Sender: TObject);
|
|
procedure FormDestroy(Sender: TObject);
|
|
procedure FormKeyDown(Sender: TObject; var Key: Word; {%H-}Shift: TShiftState);
|
|
procedure OnIdle(Sender: TObject; var {%H-}Done: Boolean);
|
|
procedure TargetComboBoxChange(Sender: TObject);
|
|
private
|
|
FIdleConnected: boolean;
|
|
FMainTarget: TObject;
|
|
FSkipDesignTimePackages: boolean;
|
|
FTargetToItem: TAvlTree; // tree of TInfoNeedBuildItem sorted for Target
|
|
FTargets: TFPList; // topologically sorted list of TInfoNeedBuildItem, last=main
|
|
procedure FillTargets;
|
|
function ProjectAsTarget(AProject: TProject): string;
|
|
function IDEAsTarget: string;
|
|
procedure SetIdleConnected(AValue: boolean);
|
|
function CheckNeedBuild(All: boolean): boolean;// true = complete
|
|
function GetTargets(Target: string): TFPList;
|
|
function HaveSameTargets(BuildItems, Targets: TFPList): boolean;
|
|
procedure ClearTargets;
|
|
procedure SetMainTarget(AValue: TObject);
|
|
public
|
|
property IdleConnected: boolean read FIdleConnected write SetIdleConnected;
|
|
property MainTarget: TObject read FMainTarget write SetMainTarget;
|
|
property SkipDesignTimePackages: boolean read FSkipDesignTimePackages
|
|
write FSkipDesignTimePackages;
|
|
end;
|
|
|
|
procedure ShowNeedBuildDialog;
|
|
|
|
function CompareInfoNeedBuildItemWithTargets(Info1, Info2: Pointer): integer;
|
|
function CompareTargetWithInfoNeedBuildItem(Target, Info: Pointer): integer;
|
|
|
|
implementation
|
|
|
|
{$R *.lfm}
|
|
|
|
procedure ShowNeedBuildDialog;
|
|
var
|
|
IDEInfoNeedBuildDlg: TIDEInfoNeedBuildDlg;
|
|
begin
|
|
IDEInfoNeedBuildDlg:=TIDEInfoNeedBuildDlg.Create(nil);
|
|
try
|
|
IDEInfoNeedBuildDlg.ShowModal;
|
|
finally
|
|
IDEInfoNeedBuildDlg.Free;
|
|
end;
|
|
end;
|
|
|
|
function CompareInfoNeedBuildItemWithTargets(Info1, Info2: Pointer): integer;
|
|
var
|
|
Item1: TInfoNeedBuildItem absolute Info1;
|
|
Item2: TInfoNeedBuildItem absolute Info2;
|
|
begin
|
|
Result:=ComparePointers(Item1.Target,Item2.Target);
|
|
end;
|
|
|
|
function CompareTargetWithInfoNeedBuildItem(Target, Info: Pointer): integer;
|
|
var
|
|
Item: TInfoNeedBuildItem absolute Info;
|
|
begin
|
|
Result:=ComparePointers(Target,Item.Target);
|
|
end;
|
|
|
|
{ TIDEInfoNeedBuildDlg }
|
|
|
|
procedure TIDEInfoNeedBuildDlg.FormCreate(Sender: TObject);
|
|
var
|
|
Target: String;
|
|
begin
|
|
FTargetToItem:=TAvlTree.Create(@CompareInfoNeedBuildItemWithTargets);
|
|
FTargets:=TFPList.Create;
|
|
|
|
Caption:=lisWhatNeedsBuilding;
|
|
|
|
TargetLabel.Caption:=lisTarget;
|
|
FillTargets;
|
|
Target:=InputHistories.ViewNeedBuildTarget;
|
|
if (Target<>'') and (TargetComboBox.Items.IndexOf(Target)>=0) then
|
|
TargetComboBox.Text:=Target
|
|
else
|
|
TargetComboBox.Text:=TargetComboBox.Items[0];
|
|
|
|
IDEDialogLayoutList.ApplyLayout(Self);
|
|
IdleConnected:=true;
|
|
TargetComboBox.DropDownCount:=EnvironmentOptions.DropDownCount;
|
|
end;
|
|
|
|
procedure TIDEInfoNeedBuildDlg.FormDestroy(Sender: TObject);
|
|
begin
|
|
IdleConnected:=false;
|
|
ClearTargets;
|
|
FreeAndNil(FTargetToItem);
|
|
FreeAndNil(FTargets);
|
|
MainTarget:=nil;
|
|
end;
|
|
|
|
procedure TIDEInfoNeedBuildDlg.FormKeyDown(Sender: TObject; var Key: Word;
|
|
Shift: TShiftState);
|
|
begin
|
|
if Key=VK_ESCAPE then
|
|
ModalResult:=mrCancel;
|
|
end;
|
|
|
|
procedure TIDEInfoNeedBuildDlg.OnIdle(Sender: TObject; var Done: Boolean);
|
|
begin
|
|
if CheckNeedBuild(false) then
|
|
IdleConnected:=false;
|
|
end;
|
|
|
|
procedure TIDEInfoNeedBuildDlg.TargetComboBoxChange(Sender: TObject);
|
|
begin
|
|
IdleConnected:=true;
|
|
end;
|
|
|
|
procedure TIDEInfoNeedBuildDlg.FormClose(Sender: TObject;
|
|
var CloseAction: TCloseAction);
|
|
var
|
|
Target: TCaption;
|
|
begin
|
|
IdleConnected:=false;
|
|
IDEDialogLayoutList.SaveLayout(Self);
|
|
Target:=TargetComboBox.Text;
|
|
if Target=ProjectAsTarget(Project1) then
|
|
Target:='';
|
|
InputHistories.ViewNeedBuildTarget:=Target;
|
|
end;
|
|
|
|
procedure TIDEInfoNeedBuildDlg.FillTargets;
|
|
var
|
|
sl: TStringListUTF8Fast;
|
|
i: Integer;
|
|
begin
|
|
sl:=TStringListUTF8Fast.Create;
|
|
try
|
|
for i:=0 to PackageGraph.Count-1 do
|
|
sl.Add(PackageGraph[i].Name);
|
|
sl.Sort;
|
|
sl.Insert(0,IDEAsTarget);
|
|
if Project1<>nil then
|
|
sl.Insert(0,ProjectAsTarget(Project1));
|
|
TargetComboBox.Items.Assign(sl);
|
|
finally
|
|
sl.Free;
|
|
end;
|
|
end;
|
|
|
|
function TIDEInfoNeedBuildDlg.ProjectAsTarget(AProject: TProject): string;
|
|
begin
|
|
if AProject=nil then
|
|
Result:=''
|
|
else
|
|
Result:=Format(lisProject, [AProject.Title]);
|
|
end;
|
|
|
|
function TIDEInfoNeedBuildDlg.IDEAsTarget: string;
|
|
begin
|
|
Result:=lisLazarusIDE;
|
|
end;
|
|
|
|
procedure TIDEInfoNeedBuildDlg.SetIdleConnected(AValue: boolean);
|
|
begin
|
|
if FIdleConnected=AValue then Exit;
|
|
if AValue and (ComponentState*[csDestroying,csLoading]<>[]) then exit;
|
|
FIdleConnected:=AValue;
|
|
if IdleConnected then
|
|
Application.AddOnIdleHandler(@OnIdle)
|
|
else
|
|
Application.RemoveOnIdleHandler(@OnIdle);
|
|
end;
|
|
|
|
function TIDEInfoNeedBuildDlg.CheckNeedBuild(All: boolean): boolean;
|
|
var
|
|
NewTargets: TFPList;
|
|
i: Integer;
|
|
Item: TInfoNeedBuildItem;
|
|
s: String;
|
|
Pkg: TLazPackage;
|
|
AProject: TProject;
|
|
NeedBuildAll: boolean;
|
|
NeedBuild: TModalResult;
|
|
begin
|
|
SkipDesignTimePackages:=false;
|
|
NewTargets:=GetTargets(TargetComboBox.Text);
|
|
try
|
|
if not HaveSameTargets(FTargets,NewTargets) then begin
|
|
// targets have changed
|
|
ClearTargets;
|
|
if NewTargets=nil then exit(true);
|
|
for i:=0 to NewTargets.Count-1 do begin
|
|
Item:=TInfoNeedBuildItem.Create;
|
|
Item.Target:=TObject(NewTargets[i]);
|
|
if Item.Target=LazarusIDE then begin
|
|
Item.Caption:=IDEAsTarget;
|
|
Item.Filename:=EnvironmentOptions.LazarusDirectory;
|
|
end
|
|
else if Item.Target is TProject then begin
|
|
AProject:=TProject(Item.Target);
|
|
Item.Caption:=ProjectAsTarget(AProject);
|
|
Item.Filename:=AProject.ProjectInfoFile;
|
|
SkipDesignTimePackages:=not (pfUseDesignTimePackages in AProject.Flags);
|
|
end
|
|
else if Item.Target is TLazPackage then begin
|
|
Pkg:=TLazPackage(Item.Target);
|
|
Pkg.Flags:=Pkg.Flags-[lpfNeedGroupCompile];
|
|
Item.Caption:=Pkg.IDAsString;
|
|
Item.Filename:=Pkg.Filename;
|
|
end;
|
|
FTargetToItem.Add(Item);
|
|
FTargets.Add(Item);
|
|
end;
|
|
end;
|
|
finally
|
|
NewTargets.Free;
|
|
end;
|
|
Result:=true;
|
|
|
|
// check
|
|
if (FTargets.Count>0) then
|
|
MainTarget:=TInfoNeedBuildItem(FTargets.Last).Target
|
|
else
|
|
MainTarget:=nil;
|
|
|
|
i:=0;
|
|
while i<FTargets.Count do begin
|
|
Item:=TInfoNeedBuildItem(FTargets[i]);
|
|
if Item.NeedBuild=inbNone then begin
|
|
Item.NeedBuild:=inbNo;
|
|
if Item.Target=LazarusIDE then begin
|
|
// no check available
|
|
end
|
|
else if Item.Target is TProject then begin
|
|
AProject:=TProject(Item.Target);
|
|
Item.Note:='';
|
|
NeedBuild:=MainBuildBoss.DoCheckIfProjectNeedsCompilation(AProject,
|
|
NeedBuildAll,Item.Note);
|
|
if NeedBuild=mrYes then begin
|
|
if NeedBuildAll then
|
|
Item.NeedBuild:=inbClean
|
|
else
|
|
Item.NeedBuild:=inbNormal;
|
|
end;
|
|
end
|
|
else if Item.Target is TLazPackage then begin
|
|
Pkg:=TLazPackage(Item.Target);
|
|
Item.Note:='';
|
|
NeedBuild:=PackageGraph.CheckIfPackageNeedsCompilation(
|
|
Pkg,SkipDesignTimePackages,true,NeedBuildAll,Item.Note);
|
|
if NeedBuild=mrYes then begin
|
|
PackageGraph.SetFlagDependenciesNeedBuild(Pkg);
|
|
if NeedBuildAll then
|
|
Item.NeedBuild:=inbClean
|
|
else
|
|
Item.NeedBuild:=inbNormal;
|
|
end;
|
|
end;
|
|
if not All then break;
|
|
end;
|
|
inc(i);
|
|
end;
|
|
Result:=i=FTargets.Count; // true = all checked
|
|
|
|
// update memo
|
|
s:='';
|
|
for i:=0 to FTargets.Count-1 do begin
|
|
Item:=TInfoNeedBuildItem(FTargets[i]);
|
|
s+='Target: '+Item.Caption+LineEnding;
|
|
case Item.NeedBuild of
|
|
inbNone: s+='checking ...';
|
|
inbNo: s+='No build needed.';
|
|
inbNormal: s+='Build needed.';
|
|
inbClean: s+='Clean build needed.';
|
|
end;
|
|
s+=LineEnding;
|
|
if Item.Filename<>'' then
|
|
s+='File: '+Item.Filename+LineEnding;
|
|
if Item.Note<>'' then
|
|
s+='Note: '+Item.Note+LineEnding;
|
|
s+=LineEnding;
|
|
end;
|
|
MainMemo.Lines.Text:=s;
|
|
end;
|
|
|
|
function TIDEInfoNeedBuildDlg.GetTargets(Target: string): TFPList;
|
|
|
|
function GetList(Main: TObject; FirstDependency: TPkgDependency;
|
|
ReqFlags: TPkgIntfRequiredFlags): TFPList;
|
|
begin
|
|
Result:=nil;
|
|
if Main=nil then exit;
|
|
PackageGraph.GetAllRequiredPackages(nil,FirstDependency,Result,ReqFlags);
|
|
if Result<>nil then begin
|
|
// PackageGraph.GetAllRequiredPackages starts with the inner nodes
|
|
// => reverse order
|
|
ReverseList(Result);
|
|
end
|
|
else
|
|
Result:=TFPList.Create;
|
|
Result.Add(Main);
|
|
end;
|
|
|
|
var
|
|
Pkg: TLazPackage;
|
|
ReqFlags: TPkgIntfRequiredFlags;
|
|
begin
|
|
ReqFlags:=[];
|
|
if Target=IDEAsTarget then begin
|
|
Result:=GetList(LazarusIDE,PackageGraph.FirstInstallDependency,ReqFlags);
|
|
end else if Target=ProjectAsTarget(Project1) then begin
|
|
if not (pfUseDesignTimePackages in Project1.Flags) then
|
|
Include(ReqFlags,pirSkipDesignTimeOnly);
|
|
Result:=GetList(Project1,Project1.FirstRequiredDependency,ReqFlags);
|
|
end else begin
|
|
Pkg:=PackageGraph.FindPackageWithName(Target,nil);
|
|
Result:=GetList(Pkg,Pkg.FirstRequiredDependency,ReqFlags);
|
|
end;
|
|
end;
|
|
|
|
function TIDEInfoNeedBuildDlg.HaveSameTargets(BuildItems, Targets: TFPList
|
|
): boolean;
|
|
// check if BuildItems and Targets have the same targets
|
|
var
|
|
Targets1: TFPList;
|
|
Targets2: TFPList;
|
|
i: Integer;
|
|
Target: TObject;
|
|
begin
|
|
Result:=false;
|
|
if (BuildItems=nil)<>(Targets=nil) then exit;
|
|
Targets1:=TFPList.Create;
|
|
Targets2:=TFPList.Create;
|
|
try
|
|
// create a list of targets from BuildItems and a second list from Targets
|
|
for i:=0 to BuildItems.Count-1 do
|
|
Targets1.Add(TInfoNeedBuildItem(BuildItems[i]).Target);
|
|
for i:=0 to Targets.Count-1 do begin
|
|
Target:=TObject(Targets[i]);
|
|
if (Target=LazarusIDE) or (Target is TProject) or (Target is TLazPackage) then
|
|
Targets2.Add(Target);
|
|
end;
|
|
if Targets1.Count<>Targets2.Count then exit;
|
|
// sort both lists
|
|
Targets1.Sort(@ComparePointers);
|
|
Targets2.Sort(@ComparePointers);
|
|
// compare each item
|
|
for i:=0 to Targets1.Count-1 do
|
|
if Targets1[i]<>Targets2[i] then exit;
|
|
finally
|
|
Targets1.Free;
|
|
Targets2.Free;
|
|
end;
|
|
Result:=true;
|
|
end;
|
|
|
|
procedure TIDEInfoNeedBuildDlg.ClearTargets;
|
|
begin
|
|
FTargetToItem.FreeAndClear;
|
|
FTargets.Clear;
|
|
end;
|
|
|
|
procedure TIDEInfoNeedBuildDlg.SetMainTarget(AValue: TObject);
|
|
begin
|
|
if FMainTarget=AValue then Exit;
|
|
FMainTarget:=AValue;
|
|
//debugln(['TIDEInfoNeedBuildDlg.SetMainTarget ',DbgSName(MainTarget)]);
|
|
if (MainTarget=LazarusIDE) then
|
|
MainBuildBoss.SetBuildTargetIDE
|
|
else
|
|
MainBuildBoss.SetBuildTargetProject1(true);
|
|
end;
|
|
|
|
end.
|
|
|