mirror of
https://gitlab.com/freepascal.org/lazarus/lazarus.git
synced 2025-04-05 13:58:04 +02:00
2165 lines
66 KiB
ObjectPascal
2165 lines
66 KiB
ObjectPascal
{
|
|
ToDo:
|
|
- menu item View -> Project Groups
|
|
- update files when project/package/file changes in IDE
|
|
- update dependencies when changed in IDE
|
|
- update when files changed on disk
|
|
- show active build mode, active project
|
|
- upate menu items enabled state
|
|
- "find" as in the Messages window
|
|
- find in files
|
|
- options: show file names with relative paths
|
|
- options: show icons, show text, show icons+text
|
|
- "New" button to create a package/project/file and add to project groups
|
|
- clean function, like the Run / Clean up and build dialog
|
|
- drag and drop within the editor
|
|
- order targets
|
|
- move targets between sub groups
|
|
- move file to another project
|
|
- save session in project group, allowing to quickly switch the active project
|
|
- load sub projects in IDE to use code navigation for files not in the active project
|
|
- find references in files
|
|
|
|
- menu item: open project in new IDE instance
|
|
- add menu items for project for all project inspector functions.
|
|
- add menu items for package for all package editor functions.
|
|
- add root node for packages: when opening a package editor add node instead
|
|
- multiple project groups in editor
|
|
}
|
|
unit ProjectGroup;
|
|
|
|
{$mode objfpc}{$H+}
|
|
|
|
interface
|
|
|
|
uses
|
|
Classes, SysUtils, contnrs,
|
|
// LazUtils
|
|
LazFileUtils, FileUtil, LazFileCache, LazConfigStorage, Laz2_XMLCfg,
|
|
LazTracer, LazUtilities, AvgLvlTree, LazStringUtils,
|
|
// LCL
|
|
Controls, Forms, Dialogs,
|
|
// CodeTools
|
|
FileProcs, CodeToolManager, CodeCache,
|
|
// BuildIntf
|
|
PackageIntf, PackageDependencyIntf, PackageLinkIntf, ProjectIntf, CompOptsIntf,
|
|
BaseIDEIntf, IDEExternToolIntf, MacroIntf, MacroDefIntf,
|
|
// IdeIntf
|
|
MenuIntf, LazIDEIntf, IDEDialogs, IDECommands, IDEMsgIntf, ToolBarIntf,
|
|
// ProjectGroups
|
|
ProjectGroupIntf, ProjectGroupStrConst;
|
|
|
|
const
|
|
PGOptionsFileName = 'projectgroupsoptions.xml';
|
|
PGFileVersion = 2;
|
|
|
|
type
|
|
{ TIDECompileTarget }
|
|
|
|
TIDECompileTarget = class(TPGCompileTarget)
|
|
private
|
|
FBuildModes: TObjectList;
|
|
FFiles: TStringList;
|
|
FRequiredPackages: TObjectList; // list of TPGDependency
|
|
function CheckIDEIsReadyForBuild: boolean;
|
|
function CompileUsingLazBuild(const AAction: TPGTargetAction; aBuildMode: string = ''): TPGActionResult;
|
|
protected
|
|
function GetBuildModeCount: integer; override;
|
|
function GetBuildModes(Index: integer): TPGBuildMode; override;
|
|
function GetFileCount: integer; override;
|
|
function GetFiles(Index: integer): string; override;
|
|
function GetRequiredPackageCount: integer; override;
|
|
function GetRequiredPackages(Index: integer): TPGDependency; override;
|
|
procedure LoadPackage;
|
|
procedure LoadProject;
|
|
procedure LoadProject_GroupSettings(XMLConfig: TXMLConfig; aPath: string; FileVersion: Integer);
|
|
procedure SaveProject_GroupSettings(XMLConfig: TXMLConfig; aPath: string);
|
|
procedure LoadProjectGroup(Recursively: boolean);
|
|
function ProjectAction(AAction: TPGTargetAction; StartBuildMode: string = ''): TPGActionResult;
|
|
function PackageAction(AAction: TPGTargetAction): TPGActionResult;
|
|
function ProjectGroupAction(AAction: TPGTargetAction): TPGActionResult;
|
|
function PascalFileAction(AAction: TPGTargetAction): TPGActionResult;
|
|
function ExternalToolAction(AAction: TPGTargetAction): TPGActionResult;
|
|
function PerformAction(AAction: TPGTargetAction): TPGActionResult; override;
|
|
function PerformNextTarget(AAction: TPGTargetAction): TPGActionResult;
|
|
procedure ActiveChanged(Sender: TPGCompileTarget); override;
|
|
procedure SetParent(NewParent: TPGCompileTarget); virtual;
|
|
public
|
|
destructor Destroy; override;
|
|
procedure LoadTarget(Recursively: boolean); virtual;
|
|
procedure LoadGroupSettings(XMLConfig: TXMLConfig; aPath: string; FileVersion: Integer);
|
|
procedure SaveGroupSettings(XMLConfig: TXMLConfig; aPath: string);
|
|
procedure UnLoadTarget; virtual;
|
|
procedure Modified; override;
|
|
function PerformBuildModeAction(AAction: TPGTargetAction;
|
|
aModeIdentifier: string): TPGActionResult; override;
|
|
end;
|
|
|
|
// Since a project group iself is also a target, we need a target to represent
|
|
// the root projectgroup.
|
|
|
|
{ TRootProjectGroupTarget }
|
|
|
|
TRootProjectGroupTarget = class(TIDECompileTarget)
|
|
protected
|
|
procedure SetTargetType(AValue: TPGTargetType); override;
|
|
public
|
|
constructor Create(aOwner: TProjectGroup);
|
|
Destructor Destroy; override;
|
|
end;
|
|
|
|
TTargetEvent = procedure(Sender: TObject; Target: TPGCompileTarget) of object;
|
|
TTargetExchangeEvent = procedure(Sender: TObject; Target1,Target2: TPGCompileTarget) of object;
|
|
|
|
{ TIDEProjectGroup }
|
|
|
|
TIDEProjectGroup = class(TProjectGroup)
|
|
private
|
|
FActiveTarget: TPGCompileTarget;
|
|
FOnFileNameChange: TNotifyEvent;
|
|
FOnTargetActiveChanged: TTargetEvent;
|
|
FOnTargetInserted: TTargetEvent;
|
|
FOnTargetDeleted: TTargetEvent;
|
|
FOnTargetsExchanged: TTargetExchangeEvent;
|
|
FTargets: TFPObjectList;
|
|
protected
|
|
procedure SetFileName(AValue: String); override;
|
|
function GetTarget(Index: Integer): TPGCompileTarget; override;
|
|
function GetTargetCount: Integer; override;
|
|
function GetActiveTarget: TPGCompileTarget; override;
|
|
procedure SetActiveTarget(AValue: TPGCompileTarget); override;
|
|
procedure DoTargetDeleted(Sender: TObject; Target: TPGCompileTarget);
|
|
procedure DoTargetInserted(Sender: TObject; Target: TPGCompileTarget);
|
|
public
|
|
constructor Create(aCompileTarget: TIDECompileTarget);
|
|
destructor Destroy; override;
|
|
procedure Clear;
|
|
procedure CheckInvalidCycle(const aFilename: string);
|
|
function IndexOfTarget(const Target: TPGCompileTarget): Integer; override;
|
|
function AddTarget(Const AFileName: String): TPGCompileTarget; override;
|
|
function InsertTarget(const Target: TPGCompileTarget; Index: Integer
|
|
): Integer; override;
|
|
procedure RemoveTarget(Index: Integer); override;
|
|
procedure ExchangeTargets(OldIndex, NewIndex: Integer); override;
|
|
procedure ActiveTargetChanged(T: TPGCompileTarget);
|
|
function UpdateMissing: boolean; override;
|
|
function LoadFromFile(Options: TProjectGroupLoadOptions): Boolean;
|
|
function SaveToFile: Boolean;
|
|
property OnFileNameChange: TNotifyEvent Read FOnFileNameChange Write FOnFileNameChange;
|
|
property OnTargetInserted: TTargetEvent Read FOnTargetInserted Write FOnTargetInserted;
|
|
property OnTargetDeleted: TTargetEvent Read FOnTargetDeleted Write FOnTargetDeleted;
|
|
property OnTargetActiveChanged: TTargetEvent Read FOnTargetActiveChanged Write FOnTargetActiveChanged;
|
|
property OnTargetsExchanged: TTargetExchangeEvent Read FOnTargetsExchanged Write FOnTargetsExchanged;
|
|
end;
|
|
|
|
{ TIDEProjectGroupOptions }
|
|
|
|
TIDEProjectGroupOptions = class
|
|
private
|
|
FBuildCommandToCompileTarget: Boolean;
|
|
FChangeStamp: integer;
|
|
FLastGroupFile: string;
|
|
FLastSavedChangeStamp: integer;
|
|
FOpenLastGroupOnStart: Boolean;
|
|
FRecentProjectGroups: TStringList;
|
|
FShowTargetPaths: boolean;
|
|
function GetModified: boolean;
|
|
procedure SetBuildCommandToCompileTarget(const AValue: Boolean);
|
|
procedure SetLastGroupFile(const AValue: string);
|
|
procedure SetModified(AValue: boolean);
|
|
procedure SetOpenLastGroupOnStart(const AValue: Boolean);
|
|
procedure SetShowTargetPaths(AValue: boolean);
|
|
public
|
|
constructor Create;
|
|
destructor Destroy; override;
|
|
procedure SaveSafe;
|
|
procedure LoadSafe;
|
|
procedure SaveToFile(aFilename: string);
|
|
procedure LoadFromFile(aFilename: string);
|
|
// changestamp
|
|
procedure IncreaseChangeStamp;
|
|
property ChangeStamp: integer read FChangeStamp;
|
|
property Modified: boolean read GetModified write SetModified;
|
|
// recent project groups
|
|
property RecentProjectGroups: TStringList read FRecentProjectGroups;
|
|
procedure AddToRecentProjectGroups(aFilename: string);
|
|
// misc
|
|
property LastGroupFile: string read FLastGroupFile write SetLastGroupFile;
|
|
property OpenLastGroupOnStart: Boolean read FOpenLastGroupOnStart write SetOpenLastGroupOnStart;
|
|
property ShowTargetPaths: boolean read FShowTargetPaths write SetShowTargetPaths;
|
|
property BuildCommandToCompileTarget: Boolean read FBuildCommandToCompileTarget write SetBuildCommandToCompileTarget;
|
|
end;
|
|
|
|
{ TPGUndoItem }
|
|
|
|
TPGUndoItem = class
|
|
end;
|
|
|
|
{ TPGUndoDelete }
|
|
|
|
TPGUndoDelete = class(TPGUndoItem)
|
|
public
|
|
Group: TIDEProjectGroup;
|
|
Target: TIDECompileTarget; // owned by this undo item
|
|
InFrontFile, BehindFile: string;
|
|
destructor Destroy; override;
|
|
end;
|
|
|
|
{ TIDEProjectGroupManager }
|
|
|
|
TIDEProjectGroupManager = Class(TProjectGroupManager)
|
|
private
|
|
FIdleConnected: boolean;
|
|
FOnEditorOptionsChanged: TNotifyEvent;
|
|
FUndoList: TObjectList; // list of TPGUndoItem
|
|
FRedoList: TObjectList; // list of TPGUndoItem
|
|
FOptions: TIDEProjectGroupOptions;
|
|
procedure AddToRecentGroups(aFilename: string);
|
|
function GetNewFileName: Boolean;
|
|
function GetPGSrcPaths(const s: string; const {%H-}Data: PtrInt;
|
|
var Abort: boolean): string;
|
|
procedure OnIdle(Sender: TObject; var {%H-}Done: Boolean);
|
|
procedure SetIdleConnected(const AValue: boolean);
|
|
procedure AddSrcPathOfFile(SrcPaths: TFilenameToStringTree; Filename: string);
|
|
procedure AddProjectSrcPaths(Target: TIDECompileTarget; SrcPaths, {%H-}LPKFiles: TFilenameToStringTree);
|
|
procedure AddPackageSrcPaths(Target: TIDECompileTarget; SrcPaths, LPKFiles: TFilenameToStringTree);
|
|
procedure AddPackageNameSrcPaths(PkgName, PreferredFile, DefaultFile: string; SrcPaths, LPKFiles: TFilenameToStringTree);
|
|
procedure AddLPKSrcPaths(LPKFilename: string; SrcPaths, LPKFiles: TFilenameToStringTree);
|
|
procedure AddGroupSrcPaths(Group: TProjectGroup; SrcPaths, LPKFiles: TFilenameToStringTree);
|
|
protected
|
|
FIDEStarted: boolean;
|
|
FProjectGroup: TIDEProjectGroup;
|
|
protected
|
|
function GetCurrentProjectGroup: TProjectGroup; override;
|
|
function ShowProjectGroupEditor(BringToFront: boolean): Boolean;
|
|
procedure TargetDeleting(Group: TIDEProjectGroup; Index: integer);
|
|
function GroupExists(Group: TIDEProjectGroup): boolean;
|
|
public
|
|
constructor Create;
|
|
destructor Destroy; override;
|
|
procedure UpdateRecentProjectGroupMenu;
|
|
function CheckSaved: Boolean;
|
|
// Events for main menu
|
|
procedure DoNewClick(Sender: TObject);
|
|
procedure DoOpenClick(Sender: TObject);
|
|
procedure DoOpenRecentClick(Sender: TObject);
|
|
procedure DoSaveClick(Sender: TObject);
|
|
procedure DoSaveAsClick(Sender: TObject);
|
|
// Public interface
|
|
function CanUndo: boolean; override;
|
|
function CanRedo: boolean; override;
|
|
procedure Undo; override;
|
|
procedure Redo; override;
|
|
function NewProjectGroup(AddActiveProject: boolean; BringToFront: boolean = true): boolean; override;
|
|
function LoadProjectGroup(AFileName: string; AOptions: TProjectGroupLoadOptions): boolean; override;
|
|
function SaveProjectGroup: boolean; override;
|
|
function GetSrcPaths: string; override;
|
|
public
|
|
property Options: TIDEProjectGroupOptions read FOptions;
|
|
property IdleConnected: boolean read FIdleConnected write SetIdleConnected;
|
|
property OnEditorOptionsChanged: TNotifyEvent read FOnEditorOptionsChanged write FOnEditorOptionsChanged;
|
|
end;
|
|
|
|
TEditProjectGroupHandler = procedure(Sender: TObject; AProjectGroup: TProjectGroup; BringToFront: boolean);
|
|
// Method variant.
|
|
TEditProjectGroupEvent = procedure(Sender: TObject; AProjectGroup: TProjectGroup; BringToFront: boolean) of object;
|
|
|
|
var
|
|
OnShowProjectGroupEditor: TEditProjectGroupHandler; // Takes precedence
|
|
OnShowProjectGroupEditorEvent: TEditProjectGroupEvent; // method variant
|
|
|
|
IDEProjectGroupManager: TIDEProjectGroupManager;
|
|
|
|
ViewProjGrpShortCutX: TIDEShortCut;
|
|
ProjectGroupEditorMenuRoot: TIDEMenuSection = nil;
|
|
PGEditMenuSectionFiles, // e.g. sort files, clean up files
|
|
PGEditMenuSectionAddRemove, // e.g. add unit, add dependency
|
|
PGEditMenuSectionCompile, // e.g. build clean, create Makefile
|
|
PGEditMenuSectionUse, // Target up/down
|
|
PGEditMenuSectionMisc: TIDEMenuSection; // e.g. options
|
|
PGOpenRecentSubMenu: TIDEMenuSection;
|
|
|
|
const
|
|
ProjectGroupCmdCategoryName = 'ProjectGroups';
|
|
var
|
|
PGCmdCategory: TIDECommandCategory;
|
|
|
|
// IDE main bar menu items
|
|
ViewProjectGroupsCommand: TIDECommand;
|
|
ViewProjectGroupsButtonCommand: TIDEButtonCommand;
|
|
CmdOpenProjectGroup: TIDECommand;
|
|
MnuCmdOpenProjectGroup: TIDEMenuCommand;
|
|
CmdSaveProjectGroup: TIDECommand;
|
|
MnuCmdSaveProjectGroup: TIDEMenuCommand;
|
|
CmdSaveProjectGroupAs: TIDECommand;
|
|
MnuCmdSaveProjectGroupAs: TIDEMenuCommand;
|
|
CmdNewProjectGroup: TIDECommand;
|
|
MnuCmdNewProjectGroup: TIDEMenuCommand;
|
|
|
|
// editor menu items
|
|
MnuCmdTargetAdd,
|
|
MnuCmdTargetRemove,
|
|
MnuCmdTargetEarlier,
|
|
MnuCmdTargetActivate,
|
|
MnuCmdTargetLater,
|
|
MnuCmdTargetCompile,
|
|
MnuCmdTargetCompileClean,
|
|
MnuCmdTargetCompileFromHere,
|
|
MnuCmdTargetCompileCleanFromHere,
|
|
MnuCmdTargetInstall,
|
|
MnuCmdTargetOpen,
|
|
MnuCmdTargetRun,
|
|
MnuCmdTargetProperties,
|
|
MnuCmdTargetUninstall,
|
|
MnuCmdTargetCopyFilename: TIDEMenuCommand;
|
|
MnuCmdProjGrpUndo: TIDEMenuCommand;
|
|
MnuCmdProjGrpRedo: TIDEMenuCommand;
|
|
MnuCmdProjGrpOptions: TIDEMenuCommand;
|
|
|
|
function LoadXML(aFilename: string; Quiet: boolean): TXMLConfig;
|
|
function CreateXML(aFilename: string; Quiet: boolean): TXMLConfig;
|
|
function GetLazBuildFilename: string;
|
|
|
|
implementation
|
|
|
|
function LoadXML(aFilename: string; Quiet: boolean): TXMLConfig;
|
|
var
|
|
Code: TCodeBuffer;
|
|
begin
|
|
Result:=nil;
|
|
aFilename:=TrimFilename(aFilename);
|
|
if (aFilename='') or (not FilenameIsAbsolute(aFilename)) then begin
|
|
debugln(['Error: (lazarus) [TIDECompileTarget.LoadXML] invalid filename "',aFilename,'"']);
|
|
if not Quiet then
|
|
IDEMessageDialog(lisInvalidFile, Format(lisInvalidXmlFileName, [aFilename]), mtError, [mbOk]);
|
|
exit;
|
|
end;
|
|
Code:=CodeToolBoss.LoadFile(aFilename,true,false);
|
|
if Code=nil then begin
|
|
debugln(['Error: (lazarus) [TIDECompileTarget.LoadXML] unable to load file "',aFilename,'"']);
|
|
if not Quiet then
|
|
IDEMessageDialog(lisReadError, Format(lisUnableToLoadFile, [aFilename]), mtError, [mbOk]);
|
|
exit;
|
|
end;
|
|
try
|
|
Result:=TXMLConfig.CreateWithSource(aFilename,Code.Source);
|
|
Result.Modified:=false;
|
|
except
|
|
on E: Exception do begin
|
|
debugln(['Error: (lazarus) [TIDECompileTarget.LoadXML] xml syntax error in "',aFilename,'": '+E.Message]);
|
|
if not Quiet then
|
|
IDEMessageDialog(lisReadError, Format(lisXMLSyntaxErrorInFile, [aFilename, E.Message]), mtError, [mbOk]);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
function CreateXML(aFilename: string; Quiet: boolean): TXMLConfig;
|
|
begin
|
|
Result:=nil;
|
|
aFilename:=TrimFilename(aFilename);
|
|
if (aFilename='') or (not FilenameIsAbsolute(aFilename)) then begin
|
|
debugln(['Error: (lazarus) [TIDECompileTarget.CreateXML] invalid filename "',aFilename,'"']);
|
|
exit;
|
|
end;
|
|
try
|
|
Result:=TXMLConfig.CreateClean(aFilename);
|
|
except
|
|
on E: Exception do begin
|
|
debugln(['Error: (lazarus) [TIDECompileTarget.CreateXML] unable to create file "',aFilename,'": '+E.Message]);
|
|
if not Quiet then
|
|
IDEMessageDialog(lisWriteError, Format(lisUnableToCreateFile, [aFilename, E.Message]), mtError, [mbOk]);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
function GetLazBuildFilename: string;
|
|
begin
|
|
// first check the lazbuild executable in the lazarus directory
|
|
Result:='$(LazarusDir)'+PathDelim+'lazbuild'+ExeExt;
|
|
IDEMacros.SubstituteMacros(Result);
|
|
if FileExistsCached(Result) then
|
|
exit;
|
|
// then search in PATH
|
|
Result:=FindDefaultExecutablePath('lazbuild'+ExeExt);
|
|
end;
|
|
|
|
{ TPGUndoDelete }
|
|
|
|
destructor TPGUndoDelete.Destroy;
|
|
begin
|
|
FreeAndNil(Target);
|
|
inherited Destroy;
|
|
end;
|
|
|
|
{ TIDEProjectGroupOptions }
|
|
|
|
function TIDEProjectGroupOptions.GetModified: boolean;
|
|
begin
|
|
Result:=FLastSavedChangeStamp<>FChangeStamp
|
|
end;
|
|
|
|
procedure TIDEProjectGroupOptions.SetBuildCommandToCompileTarget(
|
|
const AValue: Boolean);
|
|
begin
|
|
if FBuildCommandToCompileTarget=AValue then Exit;
|
|
FBuildCommandToCompileTarget:=AValue;
|
|
IncreaseChangeStamp;
|
|
end;
|
|
|
|
procedure TIDEProjectGroupOptions.SetLastGroupFile(const AValue: string);
|
|
begin
|
|
if FLastGroupFile=AValue then Exit;
|
|
FLastGroupFile:=AValue;
|
|
IncreaseChangeStamp;
|
|
end;
|
|
|
|
procedure TIDEProjectGroupOptions.SetModified(AValue: boolean);
|
|
begin
|
|
if AValue then
|
|
IncreaseChangeStamp
|
|
else
|
|
FLastSavedChangeStamp:=FChangeStamp;
|
|
end;
|
|
|
|
procedure TIDEProjectGroupOptions.SetOpenLastGroupOnStart(const AValue: Boolean
|
|
);
|
|
begin
|
|
if FOpenLastGroupOnStart=AValue then Exit;
|
|
FOpenLastGroupOnStart:=AValue;
|
|
IncreaseChangeStamp;
|
|
end;
|
|
|
|
procedure TIDEProjectGroupOptions.SetShowTargetPaths(AValue: boolean);
|
|
begin
|
|
if FShowTargetPaths=AValue then Exit;
|
|
FShowTargetPaths:=AValue;
|
|
IncreaseChangeStamp;
|
|
end;
|
|
|
|
constructor TIDEProjectGroupOptions.Create;
|
|
begin
|
|
FRecentProjectGroups:=TStringList.Create;
|
|
FOpenLastGroupOnStart:=true;
|
|
end;
|
|
|
|
destructor TIDEProjectGroupOptions.Destroy;
|
|
begin
|
|
FreeAndNil(FRecentProjectGroups);
|
|
inherited Destroy;
|
|
end;
|
|
|
|
procedure TIDEProjectGroupOptions.SaveSafe;
|
|
begin
|
|
try
|
|
SaveToFile(PGOptionsFileName);
|
|
Modified:=false;
|
|
except
|
|
on E: Exception do
|
|
debugln(['Error: (lazarus) [TIDEProjectGroupOptions.SaveSafe] ',E.Message]);
|
|
end;
|
|
end;
|
|
|
|
procedure TIDEProjectGroupOptions.LoadSafe;
|
|
begin
|
|
try
|
|
LoadFromFile(PGOptionsFileName);
|
|
except
|
|
on E: Exception do
|
|
debugln(['Error: (lazarus) [TIDEProjectGroupOptions.LoadSafe] ',E.Message]);
|
|
end;
|
|
Modified:=false;
|
|
end;
|
|
|
|
procedure TIDEProjectGroupOptions.SaveToFile(aFilename: string);
|
|
var
|
|
Cfg: TConfigStorage;
|
|
begin
|
|
Cfg:=GetIDEConfigStorage(aFilename,false);
|
|
try
|
|
Cfg.SetValue('RecentProjectGroups/',FRecentProjectGroups);
|
|
Cfg.SetDeleteValue('OpenLastGroupOnStart/Value',OpenLastGroupOnStart,true);
|
|
Cfg.SetDeleteValue('LastGroupFile/Value',LastGroupFile,'');
|
|
Cfg.SetDeleteValue('ShowTargetPaths/Value',ShowTargetPaths,false);
|
|
Cfg.SetDeleteValue('BuildCommandToCompileTarget/Value',BuildCommandToCompileTarget,false);
|
|
finally
|
|
Cfg.Free;
|
|
end;
|
|
end;
|
|
|
|
procedure TIDEProjectGroupOptions.LoadFromFile(aFilename: string);
|
|
var
|
|
Cfg: TConfigStorage;
|
|
begin
|
|
Cfg:=GetIDEConfigStorage(aFilename,true);
|
|
try
|
|
Cfg.GetValue('RecentProjectGroups/',FRecentProjectGroups);
|
|
OpenLastGroupOnStart:=Cfg.GetValue('OpenLastGroupOnStart/Value',true);
|
|
LastGroupFile:=Cfg.GetValue('LastGroupFile/Value','');
|
|
ShowTargetPaths:=Cfg.GetValue('ShowTargetPaths/Value',false);
|
|
BuildCommandToCompileTarget:=Cfg.GetValue('BuildCommandToCompileTarget/Value',false);
|
|
finally
|
|
Cfg.Free;
|
|
end;
|
|
end;
|
|
|
|
procedure TIDEProjectGroupOptions.AddToRecentProjectGroups(aFilename: string);
|
|
var
|
|
i: Integer;
|
|
begin
|
|
FRecentProjectGroups.Insert(0,aFilename);
|
|
for i:=FRecentProjectGroups.Count-1 downto 1 do
|
|
if CompareFilenames(FRecentProjectGroups[i],aFilename)=0 then
|
|
FRecentProjectGroups.Delete(i);
|
|
while FRecentProjectGroups.Count>30 do
|
|
FRecentProjectGroups.Delete(FRecentProjectGroups.Count-1);
|
|
end;
|
|
|
|
procedure TIDEProjectGroupOptions.IncreaseChangeStamp;
|
|
begin
|
|
LUIncreaseChangeStamp(FChangeStamp);
|
|
end;
|
|
|
|
{ TIDEProjectGroupManager }
|
|
|
|
function TIDEProjectGroupManager.CheckSaved: Boolean;
|
|
begin
|
|
if (FProjectGroup=nil) or (not FProjectGroup.Modified) then exit(true);
|
|
case IDEQuestionDialog(lisProjectGroupModified,
|
|
Format(lisProjectGroupModifiedConfirm,[FProjectGroup.FileName]),
|
|
mtWarning,
|
|
[mrYes,lisSavePG,
|
|
mrNo,lisDiscard,
|
|
mrAbort,lisAbort],'') of
|
|
mrYes :
|
|
begin
|
|
SaveProjectGroup;
|
|
Result:=true;
|
|
end;
|
|
mrNo :
|
|
begin
|
|
FProjectGroup.Modified:=False;
|
|
Result:=True;
|
|
end
|
|
else
|
|
Result:=False;
|
|
end;
|
|
end;
|
|
|
|
function TIDEProjectGroupManager.GetCurrentProjectGroup: TProjectGroup;
|
|
begin
|
|
Result:=FProjectGroup;
|
|
end;
|
|
|
|
function TIDEProjectGroupManager.ShowProjectGroupEditor(BringToFront: boolean
|
|
): Boolean;
|
|
begin
|
|
Result:=Assigned(FProjectGroup);
|
|
if Result then
|
|
begin
|
|
if Assigned(OnShowProjectGroupEditor) then
|
|
OnShowProjectGroupEditor(FProjectGroup,FProjectGroup,BringToFront)
|
|
else if Assigned(OnShowProjectGroupEditorEvent) then
|
|
OnShowProjectGroupEditorEvent(FProjectGroup,FProjectGroup,BringToFront)
|
|
else
|
|
Result:=False;
|
|
end;
|
|
end;
|
|
|
|
procedure TIDEProjectGroupManager.TargetDeleting(Group: TIDEProjectGroup;
|
|
Index: integer);
|
|
var
|
|
UndoDelete: TPGUndoDelete;
|
|
Target: TIDECompileTarget;
|
|
begin
|
|
UndoDelete:=TPGUndoDelete.Create;
|
|
FUndoList.Add(UndoDelete);
|
|
Target:=Group.Targets[Index] as TIDECompileTarget;
|
|
UndoDelete.Target:=Target;
|
|
UndoDelete.Group:=Group;
|
|
if Index>0 then
|
|
UndoDelete.InFrontFile:=Group.Targets[Index-1].Filename;
|
|
if Index+1<Group.TargetCount then
|
|
UndoDelete.BehindFile:=Group.Targets[Index+1].Filename;
|
|
end;
|
|
|
|
function TIDEProjectGroupManager.GroupExists(Group: TIDEProjectGroup): boolean;
|
|
|
|
function HasGroupRecursive(CurGroup: TProjectGroup): boolean;
|
|
var
|
|
i: Integer;
|
|
Target: TPGCompileTarget;
|
|
begin
|
|
if CurGroup=Group then exit(true);
|
|
for i:=0 to CurGroup.TargetCount-1 do
|
|
begin
|
|
Target:=CurGroup.Targets[i];
|
|
if Target.ProjectGroup=nil then continue;
|
|
if HasGroupRecursive(Target.ProjectGroup) then exit(true);
|
|
end;
|
|
Result:=false;
|
|
end;
|
|
|
|
begin
|
|
Result:=HasGroupRecursive(GetCurrentProjectGroup);
|
|
end;
|
|
|
|
constructor TIDEProjectGroupManager.Create;
|
|
begin
|
|
inherited Create;
|
|
FOptions:=TIDEProjectGroupOptions.Create;
|
|
FUndoList:=TObjectList.Create(true);
|
|
FRedoList:=TObjectList.Create(true);
|
|
IdleConnected:=true;
|
|
|
|
IDEMacros.Add(TTransferMacro.Create('PGSrcPaths', '', lisProjectGroupsSourcePaths
|
|
, @GetPGSrcPaths, []));
|
|
end;
|
|
|
|
destructor TIDEProjectGroupManager.Destroy;
|
|
begin
|
|
FreeAndNil(FUndoList);
|
|
FreeAndNil(FRedoList);
|
|
FreeAndNil(FProjectGroup);
|
|
FreeAndNil(FOptions);
|
|
inherited Destroy;
|
|
end;
|
|
|
|
procedure TIDEProjectGroupManager.UpdateRecentProjectGroupMenu;
|
|
var
|
|
i, l: Integer;
|
|
Item: TIDEMenuItem;
|
|
aFilename: String;
|
|
begin
|
|
i:=0;
|
|
l:=0;
|
|
while l<Options.RecentProjectGroups.Count do begin
|
|
aFilename:=Options.RecentProjectGroups[l];
|
|
inc(l);
|
|
if not FileExists(aFilename) then
|
|
continue;
|
|
if i<PGOpenRecentSubMenu.Count then begin
|
|
Item:=PGOpenRecentSubMenu[i];
|
|
Item.Caption:=aFilename;
|
|
end
|
|
else begin
|
|
Item:=RegisterIDEMenuCommand(PGOpenRecentSubMenu,'OpenRecentProjectGroup'+IntToStr(i),aFilename,@DoOpenRecentClick);
|
|
end;
|
|
inc(i);
|
|
end;
|
|
while i<PGOpenRecentSubMenu.Count do
|
|
PGOpenRecentSubMenu[i].Free;
|
|
end;
|
|
|
|
procedure TIDEProjectGroupManager.DoNewClick(Sender: TObject);
|
|
begin
|
|
NewProjectGroup(true);
|
|
end;
|
|
|
|
procedure TIDEProjectGroupManager.DoOpenClick(Sender: TObject);
|
|
var
|
|
F: TOpenDialog;
|
|
begin
|
|
if Not CheckSaved then
|
|
Exit;
|
|
F:=TOpenDialog.Create(Nil);
|
|
With F do
|
|
try
|
|
InitIDEFileDialog(F);
|
|
F.Options:=[ofFileMustExist,ofEnableSizing];
|
|
F.Filter:=lisLazarusProjectGroupsLpg+'|*.lpg|'+lisAllFiles+'|'+AllFilesMask;
|
|
if F.Execute then
|
|
LoadProjectGroup(FileName,[pgloLoadRecursively]);
|
|
StoreIDEFileDialog(F);
|
|
finally
|
|
F.Free;
|
|
end;
|
|
end;
|
|
|
|
procedure TIDEProjectGroupManager.DoOpenRecentClick(Sender: TObject);
|
|
var
|
|
Item: TIDEMenuCommand;
|
|
aFilename: String;
|
|
begin
|
|
Item:=Sender as TIDEMenuCommand;
|
|
aFilename:=Item.Caption;
|
|
//debugln(['TIDEProjectGroupManager.DoOpenRecentClick ',aFilename]);
|
|
LoadProjectGroup(aFilename,[pgloBringToFront, pgloLoadRecursively]);
|
|
end;
|
|
|
|
procedure TIDEProjectGroupManager.DoSaveClick(Sender: TObject);
|
|
begin
|
|
SaveProjectGroup;
|
|
end;
|
|
|
|
function TIDEProjectGroupManager.GetNewFileName: Boolean;
|
|
var
|
|
Dlg: TSaveDialog;
|
|
begin
|
|
Result:=False;
|
|
Dlg:=IDESaveDialogClass.Create(nil);
|
|
try
|
|
Dlg.FileName:=FProjectGroup.FileName;
|
|
InitIDEFileDialog(Dlg);
|
|
Dlg.Options:=[ofOverwritePrompt,ofPathMustExist,ofEnableSizing];
|
|
Dlg.Filter:=lisLazarusProjectGroupsLpg+'|*.lpg|'+lisAllFiles+'|'+AllFilesMask;
|
|
Dlg.DefaultExt:='.lpg';
|
|
Result:=Dlg.Execute;
|
|
if Result then begin
|
|
FProjectGroup.FileName:=TrimAndExpandFilename(Dlg.FileName);
|
|
end;
|
|
finally
|
|
StoreIDEFileDialog(Dlg);
|
|
Dlg.Free;
|
|
end;
|
|
end;
|
|
|
|
function TIDEProjectGroupManager.GetPGSrcPaths(const s: string;
|
|
const Data: PtrInt; var Abort: boolean): string;
|
|
begin
|
|
Abort:=false;
|
|
if (s<>'') and (ConsoleVerbosity>=0) then
|
|
debugln(['Hint: (lazarus) [TIDEProjectGroupManager.GetPGSrcPaths] ignoring macro PGSrcPaths parameter "',s,'"']);
|
|
Result:=GetSrcPaths;
|
|
end;
|
|
|
|
procedure TIDEProjectGroupManager.OnIdle(Sender: TObject; var Done: Boolean);
|
|
begin
|
|
if FIDEStarted then
|
|
begin
|
|
IdleConnected:=false;
|
|
exit;
|
|
end;
|
|
|
|
if Screen.GetCurrentModalForm<>nil then
|
|
exit;
|
|
FIDEStarted:=true;
|
|
if (CurrentProjectGroup=nil)
|
|
and Options.OpenLastGroupOnStart
|
|
and (Options.LastGroupFile<>'')
|
|
and FileExistsCached(Options.LastGroupFile) then
|
|
begin
|
|
LoadProjectGroup(Options.LastGroupFile,[pgloLoadRecursively,pgloSkipInvalid]);
|
|
end;
|
|
IdleConnected:=false;
|
|
end;
|
|
|
|
procedure TIDEProjectGroupManager.SetIdleConnected(const AValue: boolean);
|
|
begin
|
|
if FIdleConnected=AValue then Exit;
|
|
FIdleConnected:=AValue;
|
|
if IdleConnected then
|
|
Application.AddOnIdleHandler(@OnIdle)
|
|
else
|
|
Application.RemoveOnIdleHandler(@OnIdle);
|
|
end;
|
|
|
|
procedure TIDEProjectGroupManager.AddSrcPathOfFile(
|
|
SrcPaths: TFilenameToStringTree; Filename: string);
|
|
var
|
|
SrcPath: String;
|
|
begin
|
|
//debugln(['TIDEProjectGroupManager.AddSrcPathOfFile ',Filename]);
|
|
SrcPath:=ChompPathDelim(ExtractFilePath(ResolveDots(Filename)));
|
|
SrcPaths[SrcPath]:='1';
|
|
end;
|
|
|
|
procedure TIDEProjectGroupManager.AddProjectSrcPaths(Target: TIDECompileTarget;
|
|
SrcPaths, LPKFiles: TFilenameToStringTree);
|
|
var
|
|
aProject: TLazProject;
|
|
p, i: Integer;
|
|
Paths, Path: String;
|
|
begin
|
|
aProject:=LazarusIDE.ActiveProject;
|
|
if (aProject<>nil)
|
|
and (CompareFilenames(aProject.ProjectInfoFile,Target.Filename)=0) then
|
|
begin
|
|
// active project, can be virtual
|
|
//debugln(['TIDEProjectGroupManager.AddProjectSrcPaths Active project']);
|
|
AddSrcPathOfFile(SrcPaths,aProject.ProjectInfoFile);
|
|
Paths:=aProject.LazCompilerOptions.GetSrcPath(false)
|
|
+';'+aProject.LazCompilerOptions.GetUnitPath(false)
|
|
+';'+aProject.LazCompilerOptions.GetIncludePath(false);
|
|
//debugln(['TIDEProjectGroupManager.AddProjectSrcPaths Active project Paths="',Paths,'"']);
|
|
p:=1;
|
|
repeat
|
|
Path:=GetNextDelimitedItem(Paths,';',p);
|
|
if Path='' then continue;
|
|
if p>length(Paths) then break;
|
|
SrcPaths[Path]:='1';
|
|
until false;
|
|
end else begin
|
|
// lpi on disk -> use files in Target
|
|
//debugln(['TIDEProjectGroupManager.AddProjectSrcPaths Inactive project']);
|
|
AddSrcPathOfFile(SrcPaths,Target.Filename);
|
|
for i:=0 to Target.FileCount-1 do
|
|
AddSrcPathOfFile(SrcPaths,Target.Files[i]);
|
|
end;
|
|
end;
|
|
|
|
procedure TIDEProjectGroupManager.AddPackageSrcPaths(Target: TIDECompileTarget;
|
|
SrcPaths, LPKFiles: TFilenameToStringTree);
|
|
begin
|
|
AddLPKSrcPaths(Target.Filename,SrcPaths,LPKFiles);
|
|
end;
|
|
|
|
procedure TIDEProjectGroupManager.AddPackageNameSrcPaths(PkgName,
|
|
PreferredFile, DefaultFile: string; SrcPaths, LPKFiles: TFilenameToStringTree
|
|
);
|
|
var
|
|
LPKFilename: String;
|
|
Link: TPackageLink;
|
|
begin
|
|
if not IsValidPkgName(PkgName) then exit;
|
|
if FilenameIsAbsolute(PreferredFile) and FileExistsCached(PreferredFile) then
|
|
LPKFilename:=PreferredFile
|
|
else if FilenameIsAbsolute(DefaultFile) and FileExistsCached(DefaultFile) then
|
|
LPKFilename:=DefaultFile
|
|
else begin
|
|
Link:=PkgLinks.FindLinkWithPkgName(PkgName);
|
|
if Link=nil then begin
|
|
debugln(['Warning: (lazarus) [TIDEProjectGroupManager.AddPackageNameSrcPaths] package "',PkgName,'" not found']);
|
|
exit;
|
|
end;
|
|
LPKFilename:=Link.GetEffectiveFilename;
|
|
if not FilenameIsAbsolute(LPKFilename) then
|
|
exit;
|
|
end;
|
|
AddLPKSrcPaths(LPKFilename,SrcPaths,LPKFiles);
|
|
end;
|
|
|
|
procedure TIDEProjectGroupManager.AddLPKSrcPaths(LPKFilename: string; SrcPaths,
|
|
LPKFiles: TFilenameToStringTree);
|
|
var
|
|
xml: TXMLConfig;
|
|
Path, SubPath, CurFilename,
|
|
Paths: String;
|
|
Cnt, i, p: Integer;
|
|
Pkg: TIDEPackage;
|
|
LegacyList: Boolean;
|
|
begin
|
|
if LPKFiles.Contains(LPKFilename) then exit;
|
|
//debugln(['TIDEProjectGroupManager.AddLPKSrcPaths ',LPKFilename]);
|
|
for i:=0 to PackageEditingInterface.GetPackageCount-1 do
|
|
begin
|
|
Pkg:=PackageEditingInterface.GetPackages(i);
|
|
if CompareFilenames(Pkg.Filename,LPKFilename)=0 then
|
|
begin
|
|
// loaded package, can be virtual
|
|
//debugln(['TIDEProjectGroupManager.AddPackageSrcPaths LOADED Pkg.Filename=',Pkg.Filename]);
|
|
AddSrcPathOfFile(SrcPaths,Pkg.Filename);
|
|
Paths:=Pkg.LazCompilerOptions.GetSrcPath(false)
|
|
+';'+Pkg.LazCompilerOptions.GetUnitPath(false)
|
|
+';'+Pkg.LazCompilerOptions.GetIncludePath(false);
|
|
//debugln(['TIDEProjectGroupManager.AddPackageSrcPaths LOADED Paths=',Paths]);
|
|
p:=1;
|
|
repeat
|
|
Path:=GetNextDelimitedItem(Paths,';',p);
|
|
if Path='' then continue;
|
|
if p>length(Paths) then break;
|
|
SrcPaths[Path]:='1';
|
|
until false;
|
|
exit;
|
|
end;
|
|
end;
|
|
// not loaded lpk -> parse xml
|
|
// Note: do not open package, as this might clash with active packages
|
|
xml:=LoadXML(LPKFilename,true);
|
|
try
|
|
if xml=nil then exit;
|
|
AddSrcPathOfFile(SrcPaths,LPKFilename);
|
|
//BaseDir:=ExtractFilePath(LPKFilename);
|
|
// list of files
|
|
Path:='Files/';
|
|
LegacyList:=xml.IsLegacyList(Path);
|
|
Cnt:=xml.GetListItemCount(Path, 'Item', LegacyList);
|
|
for i:=0 to Cnt-1 do begin
|
|
SubPath:=Path+xml.GetListItemXPath('Item', i, LegacyList, True)+'/';
|
|
CurFilename:=xml.GetValue(SubPath+'Filename/Value','');
|
|
if CurFilename='' then continue;
|
|
AddSrcPathOfFile(SrcPaths,CurFilename);
|
|
end;
|
|
finally
|
|
xml.Free;
|
|
end;
|
|
end;
|
|
|
|
procedure TIDEProjectGroupManager.AddGroupSrcPaths(Group: TProjectGroup;
|
|
SrcPaths, LPKFiles: TFilenameToStringTree);
|
|
var
|
|
i: Integer;
|
|
Target: TIDECompileTarget;
|
|
begin
|
|
if Group=nil then exit;
|
|
//debugln(['TIDEProjectGroupManager.AddGroupSrcPaths ',Group.FileName,' Group.TargetCount=',Group.TargetCount]);
|
|
for i:=0 to Group.TargetCount-1 do
|
|
begin
|
|
Target:=TIDECompileTarget(Group.Targets[i]);
|
|
case Target.TargetType of
|
|
ttProject: AddProjectSrcPaths(Target,SrcPaths,LPKFiles);
|
|
ttPackage: AddPackageSrcPaths(Target,SrcPaths,LPKFiles);
|
|
ttProjectGroup: AddGroupSrcPaths(Target.ProjectGroup,SrcPaths,LPKFiles);
|
|
ttPascalFile: AddSrcPathOfFile(SrcPaths,Target.Filename);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TIDEProjectGroupManager.AddToRecentGroups(aFilename: string);
|
|
begin
|
|
Options.AddToRecentProjectGroups(AFileName);
|
|
Options.SaveSafe;
|
|
UpdateRecentProjectGroupMenu;
|
|
end;
|
|
|
|
procedure TIDEProjectGroupManager.DoSaveAsClick(Sender: TObject);
|
|
begin
|
|
if FProjectGroup=nil then exit;
|
|
if GetNewFileName then
|
|
SaveProjectGroup;
|
|
end;
|
|
|
|
function TIDEProjectGroupManager.CanUndo: boolean;
|
|
begin
|
|
Result:=FUndoList.Count>0;
|
|
end;
|
|
|
|
function TIDEProjectGroupManager.CanRedo: boolean;
|
|
begin
|
|
Result:=FRedoList.Count>0;
|
|
end;
|
|
|
|
procedure TIDEProjectGroupManager.Undo;
|
|
var
|
|
Item: TPGUndoItem;
|
|
UndoDelete: TPGUndoDelete;
|
|
Target: TIDECompileTarget;
|
|
Group: TIDEProjectGroup;
|
|
i: Integer;
|
|
begin
|
|
if not CanUndo then exit;
|
|
Item:=TPGUndoItem(FUndoList[FUndoList.Count-1]);
|
|
FUndoList.OwnsObjects:=false;
|
|
FUndoList.Delete(FUndoList.Count-1);
|
|
FUndoList.OwnsObjects:=true;
|
|
try
|
|
if Item is TPGUndoDelete then
|
|
begin
|
|
UndoDelete:=TPGUndoDelete(Item);
|
|
Target:=UndoDelete.Target;
|
|
UndoDelete.Target:=nil;
|
|
Group:=UndoDelete.Group;
|
|
if GroupExists(Group) then
|
|
begin
|
|
if Group.IndexOfTarget(Target.Filename)>=0 then
|
|
exit;
|
|
i:=Group.IndexOfTarget(UndoDelete.InFrontFile);
|
|
if i>=0 then
|
|
inc(i)
|
|
else begin
|
|
i:=Group.IndexOfTarget(UndoDelete.BehindFile);
|
|
if i<0 then
|
|
i:=Group.TargetCount;
|
|
end;
|
|
Group.InsertTarget(Target,i);
|
|
end;
|
|
end;
|
|
finally
|
|
Item.Free;
|
|
end;
|
|
end;
|
|
|
|
procedure TIDEProjectGroupManager.Redo;
|
|
begin
|
|
|
|
end;
|
|
|
|
function TIDEProjectGroupManager.NewProjectGroup(AddActiveProject: boolean;
|
|
BringToFront: boolean): boolean;
|
|
var
|
|
AProject: TLazProject;
|
|
aTarget: TIDECompileTarget;
|
|
begin
|
|
Result:=true;
|
|
|
|
if Not CheckSaved then
|
|
Exit(false);
|
|
FreeAndNil(FProjectGroup);
|
|
|
|
FProjectGroup:=TIDEProjectGroup.Create(nil);
|
|
MnuCmdSaveProjectGroupAs.Enabled:=true;
|
|
|
|
if AddActiveProject then begin
|
|
// add current project
|
|
AProject:=LazarusIDE.ActiveProject;
|
|
if (AProject<>nil) and FilenameIsAbsolute(AProject.ProjectInfoFile)
|
|
and FileExistsCached(AProject.ProjectInfoFile) then begin
|
|
aTarget:=FProjectGroup.AddTarget(AProject.ProjectInfoFile) as TIDECompileTarget;
|
|
aTarget.LoadTarget(true);
|
|
end;
|
|
end;
|
|
|
|
ShowProjectGroupEditor(BringToFront);
|
|
end;
|
|
|
|
function TIDEProjectGroupManager.LoadProjectGroup(AFileName: string;
|
|
AOptions: TProjectGroupLoadOptions): boolean;
|
|
begin
|
|
AFileName:=TrimAndExpandFilename(AFileName);
|
|
if Not CheckSaved then
|
|
Exit(false);
|
|
FreeAndNil(FProjectGroup);
|
|
|
|
AddToRecentGroups(AFileName);
|
|
FProjectGroup:=TIDEProjectGroup.Create(nil);
|
|
FProjectGroup.FileName:=AFileName;
|
|
Result:=FProjectGroup.LoadFromFile(AOptions);
|
|
if not Result then
|
|
exit;
|
|
if not (pgloSkipDialog in AOptions) then
|
|
ShowProjectGroupEditor(pgloBringToFront in AOptions);
|
|
MnuCmdSaveProjectGroupAs.Enabled:=true;
|
|
|
|
Result:=true;
|
|
end;
|
|
|
|
function TIDEProjectGroupManager.SaveProjectGroup: boolean;
|
|
begin
|
|
Result:=true;
|
|
if not Assigned(FProjectGroup) then
|
|
exit;
|
|
if (FProjectGroup.FileName<>'') or GetNewFileName then begin
|
|
Result:=FProjectGroup.SaveToFile;
|
|
AddToRecentGroups(FProjectGroup.FileName);
|
|
end;
|
|
end;
|
|
|
|
function TIDEProjectGroupManager.GetSrcPaths: string;
|
|
var
|
|
SrcPaths, LPKFiles: TFilenameToStringTree;
|
|
s: PStringToStringItem;
|
|
begin
|
|
Result:='';
|
|
if not Assigned(FProjectGroup) then exit;
|
|
LPKFiles:=TFilenameToStringTree.Create(false);
|
|
SrcPaths:=TFilenameToStringTree.Create(false);
|
|
try
|
|
AddGroupSrcPaths(FProjectGroup,SrcPaths,LPKFiles);
|
|
for s in SrcPaths do begin
|
|
if s^.Name='' then continue;
|
|
if Result<>'' then
|
|
Result:=Result+';';
|
|
Result:=Result+s^.Name;
|
|
end;
|
|
finally
|
|
SrcPaths.Free;
|
|
LPKFiles.Free;
|
|
end;
|
|
end;
|
|
|
|
{ TRootProjectGroupTarget }
|
|
|
|
procedure TRootProjectGroupTarget.SetTargetType(AValue: TPGTargetType);
|
|
begin
|
|
if (AValue<>ttProjectGroup) then
|
|
Raise Exception.Create(lisErronlyProjectGroupAllowed);
|
|
inherited SetTargetType(AValue);
|
|
end;
|
|
|
|
constructor TRootProjectGroupTarget.Create(aOwner: TProjectGroup);
|
|
begin
|
|
inherited Create(nil);
|
|
TargetType:=ttProjectGroup;
|
|
FProjectGroup:=aOwner;
|
|
Filename:=ProjectGroup.FileName;
|
|
end;
|
|
|
|
destructor TRootProjectGroupTarget.Destroy;
|
|
begin
|
|
FProjectGroup:=Nil;
|
|
inherited Destroy;
|
|
end;
|
|
|
|
{ TIDEProjectGroup }
|
|
|
|
procedure TIDEProjectGroup.SetFileName(AValue: String);
|
|
begin
|
|
if FileName=AValue then Exit;
|
|
inherited SetFileName(AValue);
|
|
if Assigned(FOnFileNameChange) then
|
|
FOnFileNameChange(Self);
|
|
end;
|
|
|
|
function TIDEProjectGroup.GetTarget(Index: Integer): TPGCompileTarget;
|
|
begin
|
|
Result:=TPGCompileTarget(FTargets[Index]);
|
|
end;
|
|
|
|
function TIDEProjectGroup.GetTargetCount: Integer;
|
|
begin
|
|
Result:=FTargets.Count;
|
|
end;
|
|
|
|
function TIDEProjectGroup.GetActiveTarget: TPGCompileTarget;
|
|
begin
|
|
Result:=FActiveTarget;
|
|
end;
|
|
|
|
procedure TIDEProjectGroup.SetActiveTarget(AValue: TPGCompileTarget);
|
|
begin
|
|
if AValue=FActiveTarget then exit;
|
|
if FActiveTarget<>nil then
|
|
FActiveTarget.DeActivate;
|
|
if AValue<>nil then
|
|
AValue.Activate;
|
|
end;
|
|
|
|
procedure TIDEProjectGroup.DoTargetDeleted(Sender: TObject;
|
|
Target: TPGCompileTarget);
|
|
begin
|
|
if Assigned(OnTargetDeleted) then
|
|
OnTargetDeleted(Sender,Target);
|
|
if Parent<>nil then
|
|
TIDEProjectGroup(Parent).DoTargetDeleted(Sender,Target);
|
|
end;
|
|
|
|
procedure TIDEProjectGroup.DoTargetInserted(Sender: TObject;
|
|
Target: TPGCompileTarget);
|
|
begin
|
|
if Assigned(OnTargetInserted) then
|
|
OnTargetInserted(Sender,Target);
|
|
if Parent<>nil then
|
|
TIDEProjectGroup(Parent).DoTargetInserted(Sender,Target);
|
|
end;
|
|
|
|
constructor TIDEProjectGroup.Create(aCompileTarget: TIDECompileTarget);
|
|
begin
|
|
inherited Create;
|
|
if aCompileTarget=nil then begin
|
|
FSelfTarget:=TRootProjectGroupTarget.Create(Self);
|
|
end else begin
|
|
FSelfTarget:=aCompileTarget;
|
|
if FSelfTarget.Parent<>nil then
|
|
FParent:=FSelfTarget.Parent.ProjectGroup;
|
|
end;
|
|
FTargets:=TFPObjectList.Create(True);
|
|
end;
|
|
|
|
destructor TIDEProjectGroup.Destroy;
|
|
begin
|
|
FreeAndNil(FTargets);
|
|
if Parent=nil then
|
|
FreeAndNil(FSelfTarget);
|
|
inherited Destroy;
|
|
end;
|
|
|
|
procedure TIDEProjectGroup.Clear;
|
|
begin
|
|
FTargets.Clear;
|
|
end;
|
|
|
|
procedure TIDEProjectGroup.CheckInvalidCycle(const aFilename: string);
|
|
var
|
|
Group: TProjectGroup;
|
|
begin
|
|
Group:=Self;
|
|
while Group<>nil do begin
|
|
if CompareFilenames(AFileName,Group.FileName)=0 then
|
|
raise Exception.Create(lisInvalidCycleAProjectGroupCannotHaveItselfAsTarget);
|
|
Group:=Group.Parent;
|
|
end;
|
|
end;
|
|
|
|
function TIDEProjectGroup.IndexOfTarget(const Target: TPGCompileTarget): Integer;
|
|
begin
|
|
Result:=FTargets.IndexOf(Target);
|
|
end;
|
|
|
|
function TIDEProjectGroup.AddTarget(const AFileName: String): TPGCompileTarget;
|
|
begin
|
|
Result:=Nil;
|
|
if not FilenameIsAbsolute(AFileName) then
|
|
RaiseGDBException('TIDEProjectGroup.AddTarget [20190629165305] '+AFileName);
|
|
CheckInvalidCycle(AFileName);
|
|
Result:=TIDECompileTarget.Create(SelfTarget);
|
|
Result.FileName:=AFileName;
|
|
FTargets.Add(Result);
|
|
IncreaseChangeStamp;
|
|
DoTargetInserted(Self,Result);
|
|
end;
|
|
|
|
function TIDEProjectGroup.InsertTarget(const Target: TPGCompileTarget;
|
|
Index: Integer): Integer;
|
|
begin
|
|
if Target=nil then
|
|
RaiseGDBException('TIDEProjectGroup.InsertTarget [20190629165001]');
|
|
if Target.Parent<>nil then
|
|
RaiseGDBException(Target.Filename);
|
|
CheckInvalidCycle(Target.FileName);
|
|
if Index<0 then
|
|
RaiseGDBException('TIDEProjectGroup.InsertTarget [20190629165007]');
|
|
if Index>TargetCount then
|
|
RaiseGDBException('TIDEProjectGroup.InsertTarget [20190629165009]');
|
|
FTargets.Insert(Index,Target);
|
|
TIDECompileTarget(Target).SetParent(SelfTarget);
|
|
IncreaseChangeStamp;
|
|
DoTargetInserted(Self,Target);
|
|
Result:=FTargets.IndexOf(Target);
|
|
end;
|
|
|
|
procedure TIDEProjectGroup.RemoveTarget(Index: Integer);
|
|
var
|
|
Target: TPGCompileTarget;
|
|
begin
|
|
Target:=Targets[Index];
|
|
IDEProjectGroupManager.TargetDeleting(Self,Index);
|
|
Target.DeActivate;
|
|
FTargets.OwnsObjects:=false;
|
|
FTargets.Delete(Index);
|
|
FTargets.OwnsObjects:=true;
|
|
TIDECompileTarget(Target).SetParent(nil);
|
|
Modified:=true;
|
|
DoTargetDeleted(Self,Target);
|
|
end;
|
|
|
|
procedure TIDEProjectGroup.ExchangeTargets(OldIndex, NewIndex: Integer);
|
|
var
|
|
Root: TIDEProjectGroup;
|
|
begin
|
|
if OldIndex=NewIndex then exit;
|
|
FTargets.Exchange(OldIndex,NewIndex);
|
|
Root:=TIDEProjectGroup(GetRootGroup);
|
|
if Assigned(Root.OnTargetsExchanged) then
|
|
Root.OnTargetsExchanged(Self,GetTarget(OldIndex),GetTarget(NewIndex));
|
|
IncreaseChangeStamp;
|
|
end;
|
|
|
|
procedure TIDEProjectGroup.ActiveTargetChanged(T: TPGCompileTarget);
|
|
var
|
|
Root: TIDEProjectGroup;
|
|
begin // T=Nil means the compilation ended and the GUI should be restored.
|
|
if Assigned(T) then
|
|
if T.Active then
|
|
FActiveTarget:=T
|
|
else
|
|
if FActiveTarget=T then
|
|
FActiveTarget:=nil;
|
|
Root:=TIDEProjectGroup(GetRootGroup);
|
|
if Assigned(Root.OnTargetActiveChanged) then
|
|
Root.OnTargetActiveChanged(Self,T);
|
|
end;
|
|
|
|
function TIDEProjectGroup.UpdateMissing: boolean;
|
|
var
|
|
i: Integer;
|
|
Target: TPGCompileTarget;
|
|
Missing: Boolean;
|
|
begin
|
|
Result:=false;
|
|
for i:=0 to TargetCount-1 do
|
|
begin
|
|
Target:=Targets[i];
|
|
Missing:=not FileExistsCached(Target.Filename);
|
|
if Target.Missing<>Missing then begin
|
|
Target.Missing:=Missing;
|
|
Result:=true;
|
|
end;
|
|
// todo sub groups
|
|
end;
|
|
if Result then
|
|
if ProjectGroupManager.Editor<>nil then
|
|
ProjectGroupManager.Editor.Invalidate;
|
|
end;
|
|
|
|
function TIDEProjectGroup.LoadFromFile(Options: TProjectGroupLoadOptions
|
|
): Boolean;
|
|
Var
|
|
ARoot: String;
|
|
TargetFileName: String;
|
|
BaseDir, APath, ATargetPath: String;
|
|
XMLConfig: TXMLConfig;
|
|
i,ACount, FileVersion: Integer;
|
|
Target: TIDECompileTarget;
|
|
aGroup: TProjectGroup;
|
|
Changed, IsLegacyList: Boolean;
|
|
begin
|
|
Result:=false;
|
|
if not FilenameIsAbsolute(FileName) then exit;
|
|
if not FileExistsCached(Filename) then exit;
|
|
|
|
Clear;
|
|
|
|
aGroup:=Parent;
|
|
while aGroup<>nil do begin
|
|
if CompareFilenames(aGroup.FileName,Filename)=0 then
|
|
exit; // circular
|
|
aGroup:=aGroup.Parent;
|
|
end;
|
|
|
|
Changed:=false;
|
|
BaseDir:=AppendPathDelim(ExtractFilePath(FileName));
|
|
try
|
|
XMLConfig := LoadXML(Filename,pgloSkipDialog in Options);
|
|
try
|
|
ARoot:='ProjectGroup/';
|
|
FileVersion := XMLConfig.GetValue(ARoot+'FileVersion',0);
|
|
ATargetPath := ARoot+'Targets/';
|
|
IsLegacyList := (FileVersion<=1) or XMLConfig.IsLegacyList(ATargetPath);
|
|
ACount:=XMLConfig.GetListItemCount(ATargetPath, 'Target', IsLegacyList);
|
|
for i:=0 to ACount-1 do
|
|
begin
|
|
Target:=Nil;
|
|
APath:=ATargetPath+XMLConfig.GetListItemXPath('Target', i, IsLegacyList, False)+'/';
|
|
TargetFileName:=XMLConfig.GetValue(APath+'FileName','');
|
|
TargetFileName:=TrimFilename(GetForcedPathDelims(TargetFileName));
|
|
if not FilenameIsAbsolute(TargetFileName) then
|
|
TargetFileName:=TrimFilename(BaseDir+TargetFileName);
|
|
if (TargetFileName<>'') and FileExistsCached(TargetFileName) then begin
|
|
Target:=TIDECompileTarget(AddTarget(TargetFileName));
|
|
if pgloLoadRecursively in Options then
|
|
Target.LoadTarget(true);
|
|
end
|
|
else if (pgloRemoveInvalid in Options) then
|
|
begin
|
|
// remove = do not load it
|
|
end
|
|
else if (pgloSkipInvalid in options) then
|
|
begin
|
|
Target:=TIDECompileTarget(AddTarget(TargetFileName));
|
|
Target.Missing:=True;
|
|
end
|
|
else if (pgloErrorInvalid in options) then
|
|
begin
|
|
Changed:=true;
|
|
exit;
|
|
end
|
|
else
|
|
case IDEQuestionDialog(lisErrTargetDoesNotExist,
|
|
Format(lisErrNoSuchFile,[TargetFileName]),mtWarning,
|
|
[mrYes,lisRemoveTarget,
|
|
mrNo,lisAbortLoadingProjectGroup,
|
|
mrYesToAll,lisSkipAllTargets],'') of
|
|
mrYes: ;
|
|
mrNo:
|
|
exit;
|
|
mrYesToAll:
|
|
begin
|
|
Target:=TIDECompileTarget(AddTarget(TargetFileName));
|
|
Target.Missing:=True;
|
|
Include(Options,pgloSkipInvalid);
|
|
end;
|
|
else
|
|
exit;
|
|
end;
|
|
if Target<>nil then
|
|
Target.LoadGroupSettings(XMLConfig,APath,FileVersion)
|
|
else
|
|
Changed:=true;
|
|
end;
|
|
finally
|
|
Modified:=Changed;
|
|
XMLConfig.Free;
|
|
end;
|
|
Result:=true;
|
|
except
|
|
on E: Exception do begin
|
|
IDEMessageDialog(lisReadError, Format(lisErrorReadingProjectGroupFile, [Filename, #13, E.Message]),
|
|
mtError,[mbOk]);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
function TIDEProjectGroup.SaveToFile: Boolean;
|
|
Var
|
|
TargetPath: String;
|
|
RelativeFileName: String;
|
|
ARoot, APath, ATargetsPath: String;
|
|
XMLConfig: TXMLConfig;
|
|
i,ACount: Integer;
|
|
aTarget: TIDECompileTarget;
|
|
SubPG: TIDEProjectGroup;
|
|
begin
|
|
Result:=True;
|
|
// save .lpg
|
|
try
|
|
XMLConfig := CreateXML(FileName,false);
|
|
try
|
|
TargetPath:=ExtractFilePath(FileName);
|
|
ARoot:='ProjectGroup/';
|
|
XMLConfig.SetValue(ARoot+'FileVersion',PGFileVersion);
|
|
ATargetsPath := ARoot+'Targets/';
|
|
XMLConfig.SetListItemCount(ATargetsPath, TargetCount, False);
|
|
ACount:=0;
|
|
For i:=0 to TargetCount-1 do
|
|
begin
|
|
aTarget:=TIDECompileTarget(GetTarget(i));
|
|
APath:=ATargetsPath+XMLConfig.GetListItemXPath('Target', i, False, False)+'/';
|
|
RelativeFileName:=ExtractRelativepath(TargetPath,aTarget.FileName);
|
|
StringReplace(RelativeFileName,'\','/',[rfReplaceAll]); // normalize, so that files look the same x-platform, for less svn changes
|
|
XMLConfig.SetDeleteValue(APath+'FileName',RelativeFileName,'');
|
|
aTarget.SaveGroupSettings(XMLConfig,APath);
|
|
Inc(ACount);
|
|
end;
|
|
XMLConfig.Flush;
|
|
finally
|
|
XMLConfig.Free;
|
|
end;
|
|
except
|
|
on E: Exception do begin
|
|
IDEMessageDialog(lisWriteError, Format(lisUnableToWriteProjectGroupFile, [Filename, #13, E.Message]),
|
|
mtError,[mbOk]);
|
|
Result:=false;
|
|
end;
|
|
end;
|
|
if not Result then exit;
|
|
// save nested .plg
|
|
For i:=0 to TargetCount-1 do
|
|
begin
|
|
aTarget:=TIDECompileTarget(GetTarget(i));
|
|
if aTarget.TargetType=ttProjectGroup then
|
|
begin
|
|
SubPG:=TIDEProjectGroup(aTarget.ProjectGroup);
|
|
if not SubPG.SaveToFile then
|
|
exit(false);
|
|
end;
|
|
end;
|
|
|
|
Modified:=False;
|
|
Result:=true;
|
|
end;
|
|
|
|
{ TIDECompileTarget }
|
|
|
|
procedure TIDECompileTarget.LoadTarget(Recursively: boolean);
|
|
begin
|
|
case TargetType of
|
|
ttProject: LoadProject;
|
|
ttPackage: LoadPackage;
|
|
ttProjectGroup: LoadProjectGroup(Recursively);
|
|
end;
|
|
end;
|
|
|
|
procedure TIDECompileTarget.LoadGroupSettings(XMLConfig: TXMLConfig;
|
|
aPath: string; FileVersion: Integer);
|
|
begin
|
|
case TargetType of
|
|
ttProject: LoadProject_GroupSettings(XMLConfig,aPath,FileVersion);
|
|
end;
|
|
if not Missing then
|
|
if XMLConfig.GetValue(APath+'Active',False) then
|
|
Activate;
|
|
end;
|
|
|
|
procedure TIDECompileTarget.SaveGroupSettings(XMLConfig: TXMLConfig;
|
|
aPath: string);
|
|
begin
|
|
case TargetType of
|
|
ttProject: SaveProject_GroupSettings(XMLConfig,aPath);
|
|
end;
|
|
XMLConfig.SetDeleteValue(APath+'Active',Active and not Missing,False);
|
|
end;
|
|
|
|
procedure TIDECompileTarget.UnLoadTarget;
|
|
begin
|
|
if FBuildModes<>nil then
|
|
FreeAndNil(FBuildModes);
|
|
if FFiles<>nil then
|
|
FreeAndNil(FFiles);
|
|
if FRequiredPackages<>nil then
|
|
FreeAndNil(FRequiredPackages);
|
|
if (FProjectGroup<>nil) and (FProjectGroup.Parent<>nil) then
|
|
FreeAndNil(FProjectGroup);
|
|
end;
|
|
|
|
destructor TIDECompileTarget.Destroy;
|
|
begin
|
|
UnLoadTarget;
|
|
inherited Destroy;
|
|
end;
|
|
|
|
procedure TIDECompileTarget.Modified;
|
|
var
|
|
PG: TProjectGroup;
|
|
begin
|
|
PG:=GetOwnerProjectGroup;
|
|
PG.Modified:=true;
|
|
end;
|
|
|
|
function TIDECompileTarget.PerformBuildModeAction(AAction: TPGTargetAction;
|
|
aModeIdentifier: string): TPGActionResult;
|
|
begin
|
|
if TargetType<>ttProject then
|
|
exit(arNotAllowed);
|
|
Result:=ProjectAction(AAction,aModeIdentifier);
|
|
end;
|
|
|
|
function TIDECompileTarget.CompileUsingLazBuild(const AAction: TPGTargetAction;
|
|
aBuildMode: string): TPGActionResult;
|
|
var
|
|
FPCParser: TFPCParser;
|
|
Params: TStringList;
|
|
WorkingDir: String;
|
|
LazBuildFilename: String;
|
|
CompileHint: String;
|
|
ToolTitle, ToolKind: String;
|
|
Tool: TAbstractExternalTool;
|
|
begin
|
|
Result:=arFailed;
|
|
case TargetType of
|
|
ttProject:
|
|
begin
|
|
ToolTitle := Format(lisCompileProject, [Filename]);
|
|
if aBuildMode<>'' then
|
|
ToolTitle += Format(lisBuildMode, [aBuildMode]);
|
|
ToolKind := lisOtherProject;
|
|
end;
|
|
ttPackage:
|
|
begin
|
|
ToolTitle := Format(lisCompilePackage, [Filename]);
|
|
ToolKind := lisPackage;
|
|
end;
|
|
else exit;
|
|
end;
|
|
|
|
CompileHint := Format(lisProjectGroup2, [Parent.Filename + LineEnding]);
|
|
|
|
LazBuildFilename:=GetLazBuildFilename;
|
|
if LazBuildFilename='' then begin
|
|
IDEMessageDialog(lisLazbuildNotFound, Format(lisTheLazbuildWasNotFound, [ExeExt])
|
|
, mtError, [mbOk]);
|
|
exit(arFailed);
|
|
end;
|
|
|
|
WorkingDir:='$(LazarusDir)';
|
|
IDEMacros.SubstituteMacros(WorkingDir);
|
|
|
|
Params:=TStringList.Create;
|
|
if AAction in [taCompileClean,taCompileCleanFromHere] then
|
|
Params.Add('-B');
|
|
if aBuildMode<>'' then
|
|
Params.Add('--build-mode='+aBuildMode);
|
|
Params.Add(Filename);
|
|
|
|
Tool:=ExternalToolList.Add(ToolTitle);
|
|
Tool.Reference(Self, ClassName);
|
|
try
|
|
Tool.Data:=TIDEExternalToolData.Create(ToolKind,
|
|
ExtractFileNameOnly(Filename), Filename);
|
|
Tool.FreeData:=true;
|
|
Tool.Hint:=CompileHint;
|
|
Tool.Process.Executable:=LazBuildFilename;
|
|
Tool.Process.Parameters:=Params;
|
|
Tool.Process.CurrentDirectory:=WorkingDir;
|
|
FPCParser:=Tool.AddParsers(SubToolFPC) as TFPCParser;
|
|
FPCParser.HideHintsSenderNotUsed:=true; //not AProject.CompilerOptions.ShowHintsForSenderNotUsed;
|
|
FPCParser.HideHintsUnitNotUsedInMainSource:=true; //not AProject.CompilerOptions.ShowHintsForUnusedUnitsInMainSrc;
|
|
FPCParser.UseTranslationUrgency:=false;
|
|
//if (not AProject.CompilerOptions.ShowHintsForUnusedUnitsInMainSrc)
|
|
//and (AProject.MainFilename<>'') then
|
|
// FPCParser.FilesToIgnoreUnitNotUsed.Add(AProject.MainFilename);
|
|
Tool.AddParsers(SubToolMake);
|
|
|
|
if not CallRunLazbuildHandlers(Tool) then
|
|
exit(arFailed);
|
|
|
|
DebugLn(['CompileUsingLazBuild: Calling "', LazBuildFilename, '" with parameters']);
|
|
Params.Delimiter:=' ';
|
|
DebugLn([' ', Params.DelimitedText]);
|
|
Tool.Execute;
|
|
Tool.WaitForExit;
|
|
if Tool.ErrorMessage='' then
|
|
Result:=arOK;
|
|
finally
|
|
Tool.Release(Self);
|
|
Params.Free;
|
|
end;
|
|
end;
|
|
|
|
function TIDECompileTarget.CheckIDEIsReadyForBuild: boolean;
|
|
begin
|
|
// check toolstatus
|
|
if LazarusIDE.ToolStatus<>itNone then begin
|
|
IDEMessageDialog(lisBePatient, lisThereIsStillAnotherBuildInProgress,
|
|
mtInformation, [mbOk]);
|
|
exit(false);
|
|
end;
|
|
Result:=true;
|
|
end;
|
|
|
|
function TIDECompileTarget.GetBuildModeCount: integer;
|
|
begin
|
|
if FBuildModes=nil then
|
|
Result:=0
|
|
else
|
|
Result:=FBuildModes.Count;
|
|
end;
|
|
|
|
function TIDECompileTarget.GetBuildModes(Index: integer): TPGBuildMode;
|
|
begin
|
|
Result:=TPGBuildMode(FBuildModes[Index]);
|
|
end;
|
|
|
|
function TIDECompileTarget.GetFileCount: integer;
|
|
begin
|
|
if FFiles=nil then
|
|
Result:=0
|
|
else
|
|
Result:=FFiles.Count;
|
|
end;
|
|
|
|
function TIDECompileTarget.GetFiles(Index: integer): string;
|
|
begin
|
|
Result:=FFiles[Index];
|
|
end;
|
|
|
|
function TIDECompileTarget.GetRequiredPackageCount: integer;
|
|
begin
|
|
if FRequiredPackages<>nil then
|
|
Result:=FRequiredPackages.Count
|
|
else
|
|
Result:=0;
|
|
end;
|
|
|
|
function TIDECompileTarget.GetRequiredPackages(Index: integer): TPGDependency;
|
|
begin
|
|
Result:=TPGDependency(FRequiredPackages[Index]);
|
|
end;
|
|
|
|
procedure TIDECompileTarget.LoadPackage;
|
|
var
|
|
MR: TModalResult;
|
|
I: Integer;
|
|
Pkg, RequiredPkg: TIDEPackage;
|
|
PkgName: String;
|
|
PkgList: TFPList;
|
|
begin
|
|
if FFiles<>nil then exit; // already loaded
|
|
|
|
debugln(['TIDECompileTarget.LoadPackage ',Filename]);
|
|
FFiles:=TStringList.Create;
|
|
FRequiredPackages:=TObjectList.Create(True);
|
|
|
|
PkgName:=ExtractFileUnitname(Filename,true);
|
|
if PkgName='' then begin
|
|
debugln(['Warning: (lazarus) [TIDECompileTarget.LoadPackage] invalid package filename "',Filename,'"']);
|
|
exit;
|
|
end;
|
|
|
|
Pkg:=PackageEditingInterface.FindPackageWithName(PkgName);
|
|
if Pkg=nil then begin
|
|
MR:=PackageEditingInterface.DoOpenPackageFile(Filename,
|
|
[pofDoNotOpenEditor],False);
|
|
if MR<>mrOk then begin
|
|
debugln(['Warning: (lazarus) [TIDECompileTarget.LoadPackage] DoOpenPackageFile failed on file "',Filename,'"']);
|
|
exit;
|
|
end;
|
|
Pkg:=PackageEditingInterface.FindPackageWithName(PkgName);
|
|
if Pkg=nil then begin
|
|
debugln(['Warning: (lazarus) [TIDECompileTarget.LoadPackage] DoOpenPackageFile failed pkgname="',PkgName,'" on file "',Filename,'"']);
|
|
exit;
|
|
end;
|
|
end;
|
|
if CompareFilenames(Pkg.Filename,Filename)<>0 then begin
|
|
debugln(['Warning: (lazarus) [TIDECompileTarget.LoadPackage] there is already a package with that name: wanted="',Filename,'" loaded="',Pkg.Filename,'"']);
|
|
exit;
|
|
end;
|
|
|
|
// load list of file
|
|
for i:=0 to Pkg.FileCount-1 do
|
|
FFiles.Add(Pkg.Files[i].Filename);
|
|
|
|
// load list of required package
|
|
PkgList:=nil;
|
|
try
|
|
PackageEditingInterface.GetRequiredPackages(Pkg,PkgList,[pirNotRecursive]);
|
|
if PkgList<>nil then
|
|
for i:=0 to PkgList.Count-1 do begin
|
|
RequiredPkg:=TIDEPackage(PkgList[i]);
|
|
PkgName:=ExtractFileUnitname(RequiredPkg.Filename,true);
|
|
FRequiredPackages.Add(TPGDependency.Create(Self,PkgName));
|
|
end;
|
|
finally
|
|
PkgList.Free;
|
|
end;
|
|
end;
|
|
|
|
procedure TIDECompileTarget.LoadProject;
|
|
var
|
|
AProject: TLazProject;
|
|
i, Cnt, ALPIFileVersion: Integer;
|
|
ProjFile: TLazProjectFile;
|
|
PkgList: TFPList;
|
|
Pkg: TIDEPackage;
|
|
PkgName, Path, SubPath, CurFilename, BaseDir, BuildMode: String;
|
|
xml: TXMLConfig;
|
|
LazBuildMode: TLazProjectBuildMode;
|
|
LegacyList: Boolean;
|
|
begin
|
|
if FFiles<>nil then exit; // already loaded
|
|
|
|
//debugln(['TIDECompileTarget.LoadProject ',Filename]);
|
|
FBuildModes:=TObjectList.Create(True);
|
|
FFiles:=TStringList.Create;
|
|
FRequiredPackages:=TObjectList.Create(True);
|
|
|
|
AProject:=LazarusIDE.ActiveProject;
|
|
if (AProject<>nil) and (CompareFilenames(AProject.ProjectInfoFile,Filename)=0)
|
|
then begin
|
|
// load from active project
|
|
for i:=0 to AProject.FileCount-1 do begin
|
|
ProjFile:=AProject.Files[i];
|
|
if not ProjFile.IsPartOfProject then continue;
|
|
FFiles.Add(ProjFile.Filename);
|
|
end;
|
|
|
|
// load dependencies from active project
|
|
PkgList:=nil;
|
|
try
|
|
PackageEditingInterface.GetRequiredPackages(AProject,PkgList,[pirNotRecursive,pirCompileOrder]);
|
|
if PkgList<>nil then begin
|
|
for i:=0 to PkgList.Count-1 do begin
|
|
Pkg:=TIDEPackage(PkgList[i]);
|
|
PkgName:=ExtractFileUnitname(Pkg.Filename,true);
|
|
FRequiredPackages.Add(TPGDependency.Create(Self,PkgName));
|
|
end;
|
|
end;
|
|
finally
|
|
PkgList.Free;
|
|
end;
|
|
|
|
// load buildmodes
|
|
for i:=0 to AProject.LazBuildModes.Count-1 do begin
|
|
LazBuildMode:=AProject.LazBuildModes.BuildModes[i];
|
|
FBuildModes.Add(TPGBuildMode.Create(Self,LazBuildMode.Identifier,false));
|
|
end;
|
|
end else begin
|
|
// load from .lpi file
|
|
|
|
xml:=LoadXML(Filename,true);
|
|
try
|
|
if xml<>nil then begin
|
|
// load list of files from lpi
|
|
BaseDir:=ExtractFilePath(Filename);
|
|
Path:='ProjectOptions/';
|
|
ALPIFileVersion := xml.GetValue(Path+'Version/Value',0);
|
|
|
|
Path:='ProjectOptions/Units/';
|
|
LegacyList:=(ALPIFileVersion<=11) or xml.IsLegacyList(Path);
|
|
Cnt:=xml.GetListItemCount(Path, 'Unit', LegacyList);
|
|
for i := 0 to Cnt - 1 do begin
|
|
SubPath:=Path+xml.GetListItemXPath('Unit', i, LegacyList)+'/';
|
|
if not xml.GetValue(SubPath+'IsPartOfProject/Value',False) then
|
|
continue;
|
|
CurFilename:=xml.GetValue(SubPath+'Filename/Value','');
|
|
if CurFilename='' then continue;
|
|
if not FilenameIsAbsolute(CurFilename) then
|
|
CurFilename:=TrimFilename(BaseDir+CurFilename);
|
|
FFiles.Add(CurFilename);
|
|
end;
|
|
|
|
// load list of RequiredPackages from lpi
|
|
Path:='ProjectOptions/RequiredPackages/';
|
|
LegacyList:=(ALPIFileVersion<=11) or xml.IsLegacyList(Path);
|
|
Cnt:=xml.GetListItemCount(Path, 'Item', LegacyList);
|
|
for i:=0 to Cnt-1 do begin
|
|
SubPath:=Path+xml.GetListItemXPath('Item', i, LegacyList, True)+'/';
|
|
PkgName:=xml.GetValue(SubPath+'PackageName/Value','');
|
|
if PkgName='' then continue;
|
|
FRequiredPackages.Add(TPGDependency.Create(Self,PkgName));
|
|
end;
|
|
|
|
// load build modes
|
|
Path:='ProjectOptions/BuildModes/';
|
|
LegacyList:=(ALPIFileVersion<=11) or xml.IsLegacyList(Path);
|
|
Cnt:=xml.GetListItemCount(Path, 'Item', LegacyList);
|
|
for i:=0 to Cnt-1 do begin
|
|
SubPath:=Path+xml.GetListItemXPath('Item', i, LegacyList, True)+'/';
|
|
BuildMode:=xml.GetValue(SubPath+'Name','');
|
|
// load/store compile in lpg
|
|
if BuildMode<>'' then
|
|
FBuildModes.Add(TPGBuildMode.Create(Self,BuildMode,false));
|
|
end;
|
|
xml.Modified := False;
|
|
end;
|
|
finally
|
|
xml.Free;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TIDECompileTarget.LoadProject_GroupSettings(XMLConfig: TXMLConfig;
|
|
aPath: string; FileVersion: Integer);
|
|
var
|
|
Cnt, i: Integer;
|
|
SubPath, aName, RootPath: String;
|
|
aMode: TPGBuildMode;
|
|
IsLegacyList: Boolean;
|
|
begin
|
|
RootPath := aPath;
|
|
aPath := aPath+'BuildModes/';
|
|
IsLegacyList := (FileVersion<=1) or XMLConfig.IsLegacyList(aPath);
|
|
Cnt:=XMLConfig.GetListItemCount(aPath, 'Mode', IsLegacyList);
|
|
for i:=0 to Cnt-1 do begin
|
|
if IsLegacyList then
|
|
SubPath:=RootPath+'Mode'+IntToStr(i+1)+'/' // important: legacy list has wrong structure here - the mode elements are not within the BuildModes element
|
|
else
|
|
SubPath:=aPath+XMLConfig.GetListItemXPath('Mode', i, IsLegacyList, True)+'/';
|
|
aName:=XMLConfig.GetValue(SubPath+'Name','');
|
|
aMode:=FindBuildMode(aName);
|
|
if aMode=nil then continue;
|
|
aMode.Compile:=XMLConfig.GetValue(SubPath+'Compile',false);
|
|
end;
|
|
end;
|
|
|
|
procedure TIDECompileTarget.SaveProject_GroupSettings(XMLConfig: TXMLConfig;
|
|
aPath: string);
|
|
var
|
|
i: Integer;
|
|
SubPath: String;
|
|
aMode: TPGBuildMode;
|
|
begin
|
|
aPath := aPath+'BuildModes/';
|
|
XMLConfig.SetListItemCount('Mode', BuildModeCount, False);
|
|
for i:=0 to BuildModeCount-1 do begin
|
|
SubPath:=aPath+XMLConfig.GetListItemXPath('Mode', i, False, True)+'/';
|
|
aMode:=BuildModes[i];
|
|
XMLConfig.SetDeleteValue(SubPath+'Name',aMode.Identifier,'');
|
|
XMLConfig.SetDeleteValue(SubPath+'Compile',aMode.Compile,false);
|
|
end;
|
|
end;
|
|
|
|
procedure TIDECompileTarget.LoadProjectGroup(Recursively: boolean);
|
|
var
|
|
PG: TIDEProjectGroup;
|
|
Flags: TProjectGroupLoadOptions;
|
|
begin
|
|
if ProjectGroup<>nil then exit;
|
|
|
|
debugln(['TIDECompileTarget.LoadProjectGroup ',Filename]);
|
|
PG:=TIDEProjectGroup.Create(Self);
|
|
FProjectGroup:=PG;
|
|
PG.FileName:=Self.FileName;
|
|
Flags:=[];
|
|
if Recursively then
|
|
Include(Flags,pgloLoadRecursively);
|
|
PG.LoadFromFile(Flags);
|
|
end;
|
|
|
|
function TIDECompileTarget.ProjectAction(AAction: TPGTargetAction;
|
|
StartBuildMode: string): TPGActionResult;
|
|
var
|
|
R: TCompileReason;
|
|
i: Integer;
|
|
aMode: TPGBuildMode;
|
|
aProject: TLazProject;
|
|
begin
|
|
Result:=arFailed;
|
|
aProject:=LazarusIDE.ActiveProject;
|
|
if (aProject<>nil)
|
|
and (CompareFilenames(aProject.ProjectInfoFile,Filename)=0)
|
|
then begin
|
|
// project loaded => use IDE functions
|
|
|
|
if StartBuildMode<>'' then begin
|
|
// switch to build mode
|
|
if CompareText(StartBuildMode,aProject.ActiveBuildModeID)<>0 then
|
|
begin
|
|
if not CheckIDEIsReadyForBuild then exit;
|
|
aProject.ActiveBuildModeID:=StartBuildMode;
|
|
end;
|
|
end;
|
|
|
|
case AAction of
|
|
taSettings :
|
|
begin
|
|
if not ExecuteIDECommand(Self,ecProjectOptions) then
|
|
Result:=arOK;
|
|
end;
|
|
taCompile,
|
|
taCompileClean,
|
|
taCompileFromHere,
|
|
taCompileCleanFromHere:
|
|
begin
|
|
if not CheckIDEIsReadyForBuild then exit;
|
|
// save project
|
|
if LazarusIDE.DoSaveProject([])<>mrOk then exit;
|
|
R:= crCompile;
|
|
if (AAction in [taCompileClean,taCompileCleanFromHere]) then
|
|
R:= crBuild;
|
|
if BuildModeCount>1 then begin
|
|
i:=0;
|
|
if StartBuildMode<>'' then begin
|
|
i:=aProject.LazBuildModes.IndexOf(StartBuildMode);
|
|
if i<0 then exit;
|
|
end;
|
|
while i<BuildModeCount do begin
|
|
aMode:=BuildModes[i];
|
|
inc(i);
|
|
debugln(['TIDECompileTarget.ProjectAction ',(aMode.Identifier<>StartBuildMode),' ',aMode.Identifier,' StartBuildMode=',StartBuildMode,' ',AAction in [taCompileFromHere,taCompileCleanFromHere]]);
|
|
if (aMode.Identifier<>StartBuildMode) and (not aMode.Compile) then continue;
|
|
// switch build mode
|
|
aProject.ActiveBuildModeID:=aMode.Identifier;
|
|
if aProject.ActiveBuildModeID<>aMode.Identifier
|
|
then begin
|
|
IDEMessageDialog(lisBuildModeNotFound, Format(lisBuildModeNotFound2, [aMode.Identifier]), mtError, [mbOk]);
|
|
exit;
|
|
end;
|
|
// compile project in active buildmode
|
|
if LazarusIDE.DoBuildProject(R,[])<>mrOk then
|
|
exit;
|
|
if (StartBuildMode<>'') and not (AAction in [taCompileFromHere,taCompileCleanFromHere]) then
|
|
exit(arOK);
|
|
StartBuildMode:='';
|
|
end;
|
|
end else begin
|
|
// compile default buildmode
|
|
if LazarusIDE.DoBuildProject(R,[])<>mrOk then
|
|
exit;
|
|
end;
|
|
Result:=arOK;
|
|
if AAction in [taCompileFromHere,taCompileCleanFromHere] then
|
|
Result:=PerformNextTarget(AAction);
|
|
end;
|
|
taRun :
|
|
begin
|
|
if LazarusIDE.DoRunProject<>mrOk then exit;
|
|
Result:=arOk;
|
|
end;
|
|
end;
|
|
end else begin
|
|
// project not loaded => use lazbuild
|
|
case AAction of
|
|
taOpen,taSettings:
|
|
begin
|
|
// open project
|
|
if LazarusIDE.DoOpenProjectFile(Filename,[ofAddToRecent])<>mrOk then
|
|
exit;
|
|
if AAction=taSettings then
|
|
if not ExecuteIDECommand(Self,ecProjectOptions) then
|
|
exit;
|
|
Result:=arOK;
|
|
end;
|
|
taCompile,
|
|
taCompileClean,
|
|
taCompileFromHere,
|
|
taCompileCleanFromHere:
|
|
begin
|
|
if not CheckIDEIsReadyForBuild then exit;
|
|
// save files
|
|
if LazarusIDE.DoSaveProject([])<>mrOk then exit;
|
|
LazarusIDE.ToolStatus:=itBuilder;
|
|
try
|
|
if BuildModeCount>1 then begin
|
|
IDEMessagesWindow.Clear;
|
|
i:=0;
|
|
if StartBuildMode<>'' then begin
|
|
while (i<BuildModeCount) and (CompareText(BuildModes[i].Identifier,StartBuildMode)<>0)
|
|
do inc(i);
|
|
end;
|
|
while i<BuildModeCount do begin
|
|
aMode:=BuildModes[i];
|
|
inc(i);
|
|
if (aMode.Identifier<>StartBuildMode) and (not aMode.Compile) then continue;
|
|
// run lazbuild as external tool
|
|
Result:=CompileUsingLazBuild(AAction,aMode.Identifier);
|
|
if Result<>arOK then exit;
|
|
|
|
if (StartBuildMode<>'') and not (AAction in [taCompileFromHere,taCompileCleanFromHere]) then
|
|
exit(arOK);
|
|
StartBuildMode:='';
|
|
end;
|
|
end else begin
|
|
IDEMessagesWindow.Clear;
|
|
// run lazbuild as external tool
|
|
Result:=CompileUsingLazBuild(AAction);
|
|
if Result<>arOK then exit;
|
|
end;
|
|
finally
|
|
LazarusIDE.ToolStatus:=itNone;
|
|
end;
|
|
Result:=arOK;
|
|
if AAction in [taCompileFromHere,taCompileCleanFromHere] then
|
|
Result:=PerformNextTarget(AAction);
|
|
end;
|
|
taRun:
|
|
begin
|
|
// open project, then run
|
|
if LazarusIDE.DoOpenProjectFile(Filename,[ofAddToRecent])<>mrOk then
|
|
exit;
|
|
if LazarusIDE.DoRunProject<>mrOk then
|
|
exit;
|
|
Result:=arOk;
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
function TIDECompileTarget.PackageAction(AAction: TPGTargetAction): TPGActionResult;
|
|
begin
|
|
Result:=arFailed;
|
|
|
|
case AAction of
|
|
taOpen,
|
|
taSettings:
|
|
begin
|
|
if PackageEditingInterface.DoOpenPackageFile(FileName,[],False)<>mrOk then
|
|
exit(arFailed);
|
|
Result:=arOK;
|
|
end;
|
|
taCompile,
|
|
taCompileClean,
|
|
taCompileFromHere,
|
|
taCompileCleanFromHere:
|
|
begin
|
|
if not CheckIDEIsReadyForBuild then exit;
|
|
// compile independent of active project => use lazbuild
|
|
Result:=CompileUsingLazBuild(AAction);
|
|
if Result<>arOK then exit;
|
|
if AAction in [taCompileFromHere,taCompileCleanFromHere] then
|
|
Result:=PerformNextTarget(AAction);
|
|
end;
|
|
taInstall: ; // ToDo install
|
|
taUninstall: ; // ToDo uninstall
|
|
end;
|
|
end;
|
|
|
|
function TIDECompileTarget.ProjectGroupAction(AAction: TPGTargetAction
|
|
): TPGActionResult;
|
|
var
|
|
i: Integer;
|
|
aTarget: TIDECompileTarget;
|
|
begin
|
|
Result:=arFailed;
|
|
|
|
case AAction of
|
|
taOpen:
|
|
ProjectGroupManager.LoadProjectGroup(FileName,[]);
|
|
taSettings: ;
|
|
taCompile,
|
|
taCompileClean:
|
|
try
|
|
for i:=0 to ProjectGroup.TargetCount-1 do begin
|
|
aTarget:=TIDECompileTarget(ProjectGroup.Targets[i]);
|
|
if AAction in aTarget.AllowedActions then begin
|
|
ActiveChanged(aTarget);
|
|
if aTarget.PerformAction(AAction)<>arOk then
|
|
exit;
|
|
end;
|
|
end;
|
|
Result:=arOk;
|
|
finally
|
|
ActiveChanged(Nil);
|
|
end;
|
|
taCompileFromHere,
|
|
taCompileCleanFromHere:
|
|
begin
|
|
if AAction = taCompileCleanFromHere then begin
|
|
if ProjectGroupAction(taCompileClean)<>arOK then
|
|
exit;
|
|
end
|
|
else
|
|
if ProjectGroupAction(taCompile)<>arOK then
|
|
exit;
|
|
Result:=arOK;
|
|
aTarget:=TIDECompileTarget(GetNext(true));
|
|
ActiveChanged(aTarget);
|
|
if aTarget=nil then exit;
|
|
Result:=aTarget.PerformAction(AAction);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
function TIDECompileTarget.PascalFileAction(AAction: TPGTargetAction
|
|
): TPGActionResult;
|
|
begin
|
|
Result:=arFailed;
|
|
case AAction of
|
|
taOpen,
|
|
taSettings:
|
|
begin
|
|
if LazarusIDE.DoOpenEditorFile(Filename,-1,-1,[ofAddToRecent,ofRegularFile])<>mrOK then
|
|
exit;
|
|
if AAction=taSettings then
|
|
if LazarusIDE.DoConfigureBuildFile<>mrOk then exit;
|
|
Result:=arOK;
|
|
end;
|
|
taCompile,
|
|
taCompileClean,
|
|
taCompileFromHere,
|
|
taCompileCleanFromHere:
|
|
begin
|
|
if not CheckIDEIsReadyForBuild then exit;
|
|
if LazarusIDE.DoBuildFile(false,Filename)<>mrOK then
|
|
exit;
|
|
Result:=arOK;
|
|
if AAction in [taCompileFromHere,taCompileCleanFromHere] then
|
|
Result:=PerformNextTarget(AAction);
|
|
end;
|
|
taRun:
|
|
begin
|
|
if not CheckIDEIsReadyForBuild then exit;
|
|
if LazarusIDE.DoRunFile(Filename)<>mrOK then
|
|
exit;
|
|
Result:=arOK;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
function TIDECompileTarget.ExternalToolAction(AAction: TPGTargetAction
|
|
): TPGActionResult;
|
|
begin
|
|
Result:=arFailed;
|
|
debugln(['TIDECompileTarget.ExternalToolAction ToDo']);
|
|
// ToDo
|
|
case AAction of
|
|
taSettings: ;
|
|
taCompile,
|
|
taCompileClean,
|
|
taCompileFromHere,
|
|
taCompileCleanFromHere:
|
|
begin
|
|
if AAction in [taCompileFromHere,taCompileCleanFromHere] then
|
|
Result:=PerformNextTarget(AAction);
|
|
end;
|
|
taRun: ;
|
|
end;
|
|
end;
|
|
|
|
function TIDECompileTarget.PerformAction(AAction: TPGTargetAction): TPGActionResult;
|
|
begin
|
|
case TargetType of
|
|
ttProject: Result:=ProjectAction(AAction);
|
|
ttPackage: Result:=PackageAction(AAction);
|
|
ttProjectGroup: Result:=ProjectGroupAction(AAction);
|
|
ttPascalFile: Result:=PascalFileAction(AAction);
|
|
ttExternalTool: Result:=ExternalToolAction(AAction);
|
|
end;
|
|
end;
|
|
|
|
function TIDECompileTarget.PerformNextTarget(AAction: TPGTargetAction
|
|
): TPGActionResult;
|
|
var
|
|
aTarget: TIDECompileTarget;
|
|
begin
|
|
aTarget:=TIDECompileTarget(GetNext(false));
|
|
while (aTarget<>nil) do
|
|
begin
|
|
if AAction in aTarget.AllowedActions then
|
|
exit(aTarget.PerformAction(AAction));
|
|
aTarget:=TIDECompileTarget(aTarget.GetNext(false));
|
|
end;
|
|
Result:=arOK;
|
|
end;
|
|
|
|
procedure TIDECompileTarget.ActiveChanged(Sender: TPGCompileTarget);
|
|
begin
|
|
(GetRootProjectGroup as TIDEProjectGroup).ActiveTargetChanged(Sender);
|
|
end;
|
|
|
|
procedure TIDECompileTarget.SetParent(NewParent: TPGCompileTarget);
|
|
begin
|
|
FParent:=NewParent;
|
|
end;
|
|
|
|
end.
|
|
|