mirror of
https://gitlab.com/freepascal.org/lazarus/lazarus.git
synced 2025-04-06 00:58:04 +02:00
6727 lines
227 KiB
ObjectPascal
6727 lines
227 KiB
ObjectPascal
{
|
|
/***************************************************************************
|
|
pkgmanager.pas
|
|
--------------
|
|
|
|
|
|
***************************************************************************/
|
|
|
|
***************************************************************************
|
|
* *
|
|
* 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:
|
|
TPkgManager is the class for the global PkgBoss variable, which controls
|
|
the whole package system in the IDE.
|
|
}
|
|
unit PkgManager;
|
|
|
|
{$mode objfpc}{$H+}
|
|
|
|
interface
|
|
|
|
{$I ide.inc}
|
|
|
|
{$DEFINE UseLRS}
|
|
{off $DEFINE VerbosePkgEditDrag}
|
|
|
|
uses
|
|
{$IFDEF IDE_MEM_CHECK}
|
|
MemCheck,
|
|
{$ENDIF}
|
|
// RTL, FCL
|
|
TypInfo, Math, Classes, SysUtils, Contnrs, AVL_Tree,
|
|
// LCL
|
|
Forms, Controls, Dialogs, Menus, ComCtrls, LResources,
|
|
// LazUtils
|
|
LazUTF8, Laz2_XMLCfg, LazTracer, LazUtilities, LazStringUtils,
|
|
LazFileUtils, LazFileCache, StringHashList, AvgLvlTree, ObjectLists, Translations,
|
|
// Codetools
|
|
CodeToolsConfig, CodeToolManager, CodeCache, BasicCodeTools,
|
|
FileProcs, CodeTree, CTUnitGraph,
|
|
// BuildIntf
|
|
ProjPackIntf, ProjectIntf, PackageIntf, PackageDependencyIntf, PackageLinkIntf,
|
|
NewItemIntf, CompOptsIntf, IDEExternToolIntf, MacroIntf,
|
|
// IdeIntf
|
|
IDECommands, MenuIntf, IDEWindowIntf, LazIDEIntf, IDEMsgIntf, SrcEditorIntf,
|
|
IdeIntfStrConsts, ComponentReg, ComponentEditors, PropEdits, IDEDialogs,
|
|
UnitResources, InputHistory,
|
|
// IdeConfig
|
|
EnvironmentOpts, IDEOptionDefs, ModeMatrixOpts, RecentListProcs,
|
|
SearchPathProcs, TransferMacros, IDECmdLine, IDEProcs,
|
|
// FCL registration
|
|
LazarusPackageIntf,
|
|
// IDE
|
|
LazarusIDEStrConsts, DialogProcs, MiscOptions,
|
|
Project, ProjPackEditing, AddToPackageDlg, PackageDefs, PackageLinks, PackageSystem,
|
|
OpenInstalledPkgDlg, PkgGraphExplorer, BrokenDependenciesDlg, CompilerOptions,
|
|
IDETranslations, BuildLazDialog, NewDialog, FindInFilesDlg,
|
|
ProjectInspector, PackageEditor, SourceEditor, ProjPackChecks, AddFileToAPackageDlg,
|
|
PublishModuleDlg, PkgLinksDlg, InterPkgConflictFiles, InstallPkgSetDlg,
|
|
ConfirmPkgListDlg, NewPkgComponentDlg, BaseBuildManager, BasePkgManager,
|
|
MainBar, MainIntf, MainBase;
|
|
|
|
type
|
|
|
|
TPackagePackageArray = specialize TObjectArray<TLazPackageID, TLazPackageID>;
|
|
TOwnerPackageArray = specialize TObjectArray<TObject, TLazPackageID>;
|
|
|
|
{ TPkgManager }
|
|
|
|
TPkgManager = class(TBasePkgManager)
|
|
private
|
|
// event handlers - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
// package editor
|
|
function PackageEditorAddToProject(Sender: TObject; APackage: TLazPackage;
|
|
OnlyTestIfPossible: boolean): TModalResult;
|
|
function PackageEditorCompilePackage(Sender: TObject; APackage: TLazPackage;
|
|
CompileClean, CompileRequired: boolean): TModalResult;
|
|
procedure PackageEditorCopyMoveFiles(Sender: TObject);
|
|
function PackageEditorCreateFile(Sender: TObject;
|
|
Params: TAddToPkgResult): TModalResult;
|
|
function PackageEditorCreateMakefile(Sender: TObject;
|
|
APackage: TLazPackage): TModalResult;
|
|
function PackageEditorCreateFpmakeFile(Sender: TObject;
|
|
APackage: TLazPackage): TModalResult;
|
|
function PackageEditorDeleteAmbiguousFiles(Sender: TObject;
|
|
{%H-}APackage: TLazPackage; const Filename: string): TModalResult;
|
|
procedure PackageEditorDragDropTreeView(Sender, Source: TObject; X, Y: Integer);
|
|
function PackageEditorDragOverTreeView(Sender, Source: TObject; X, Y: Integer;
|
|
out TargetTVNode: TTreeNode; out TargetTVType: TTreeViewInsertMarkType): boolean;
|
|
function PackageEditorFindInFiles(Sender: TObject; APackage: TLazPackage): TModalResult;
|
|
function PackageEditorInstallPackage(Sender: TObject;
|
|
APackage: TLazPackage): TModalResult;
|
|
function PackageEditorOpenPackage(Sender: TObject; APackage: TLazPackage): TModalResult;
|
|
function PackageEditorOpenPkgFile(Sender: TObject; PkgFile: TPkgFile): TModalResult;
|
|
function PackageEditorPublishPackage(Sender: TObject; APackage: TLazPackage): TModalResult;
|
|
function PackageEditorRevertPackage(Sender: TObject; APackage: TLazPackage): TModalResult;
|
|
function PackageEditorSavePackage(Sender: TObject; APackage: TLazPackage;
|
|
SaveAs: boolean): TModalResult;
|
|
function PackageEditorUninstallPackage(Sender: TObject;
|
|
APackage: TLazPackage): TModalResult;
|
|
function PackageEditorViewPkgSource(Sender: TObject;
|
|
APackage: TLazPackage): TModalResult;
|
|
procedure AfterWritePackage(Sender: TObject; Restore: boolean);
|
|
procedure BeforeReadPackage(Sender: TObject);
|
|
procedure PackageEditorFreeEditor(APackage: TLazPackage);
|
|
function PackageGraphCheckInterPkgFiles(IDEObject: TObject;
|
|
PkgList: TFPList; out FilesChanged: boolean): boolean;
|
|
// package graph
|
|
function PackageGraphExplorerOpenPackage(Sender: TObject;
|
|
APackage: TLazPackage): TModalResult;
|
|
function PackageGraphExplorerOpenProject(Sender: TObject;
|
|
AProject: TProject): TModalResult;
|
|
function PackageGraphExplorerUninstallPackage(Sender: TObject;
|
|
APackage: TLazPackage): TModalResult;
|
|
procedure PackageGraphAddPackage(Pkg: TLazPackage);
|
|
procedure PackageGraphBeginUpdate(Sender: TObject);
|
|
procedure PackageGraphChangePackageName(APackage: TLazPackage;
|
|
const OldName: string);
|
|
procedure PackageGraphDeletePackage(APackage: TLazPackage);
|
|
procedure PackageGraphDependencyModified(ADependency: TPkgDependency);
|
|
procedure PackageGraphEndUpdate(Sender: TObject; GraphChanged: boolean);
|
|
procedure PackageGraphFindFPCUnit(const AUnitName, Directory: string;
|
|
var Filename: string);
|
|
// menu
|
|
procedure MainIDEitmPkgOpenPackageFileClick(Sender: TObject);
|
|
procedure MainIDEitmPkgPkgGraphClick(Sender: TObject);
|
|
procedure MainIDEitmPkgEditInstallPkgsClick(Sender: TObject);
|
|
procedure MainIDEitmPkgAddCurFileToPkgClick(Sender: TObject);
|
|
procedure MainIDEitmPkgNewComponentClick(Sender: TObject);
|
|
procedure MainIDEitmPkgOpenPackageOfCurUnitClicked(Sender: TObject);
|
|
procedure MainIDEitmOpenRecentPackageClicked(Sender: TObject);
|
|
procedure MainIDEitmPkgOpenLoadedPackageClicked(Sender: TObject);
|
|
procedure MainIDEitmPkgNewPackageClick(Sender: TObject);
|
|
procedure MainIDEitmPackageLinksClicked(Sender: TObject);
|
|
|
|
// source editor
|
|
procedure OpenPackageForCurrentSrcEditFile(Sender: TObject);
|
|
|
|
// LCL
|
|
procedure ApplicationIdleHandler(Sender: TObject; var {%H-}Done: Boolean);
|
|
|
|
// misc
|
|
procedure GetDependencyOwnerDescription(Dependency: TPkgDependency;
|
|
out Description: string);
|
|
procedure GetDependencyOwnerDirectory(Dependency: TPkgDependency;
|
|
out Directory: string);
|
|
procedure PackageFileLoaded(Sender: TObject);
|
|
function DoBeforeCompilePackages(aPkgList: TFPList): TModalResult;
|
|
function LoadDependencyList(FirstDependency: TPkgDependency;
|
|
Quiet: boolean): TModalResult;
|
|
procedure CreateIDEWindow(Sender: TObject; aFormName: string;
|
|
var AForm: TCustomForm; DoDisableAutoSizing: boolean);
|
|
function PackageGraphSrcEditFileIsModified(const SrcFilename: string
|
|
): boolean;
|
|
public
|
|
// component palette
|
|
procedure IDEComponentPaletteOpenPackage(Sender: TObject); override;
|
|
procedure IDEComponentPaletteOpenUnit(Sender: TObject); override;
|
|
// end event handlers - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
|
|
private
|
|
// helper functions
|
|
FLastLazarusSrcDir: string;
|
|
{$IFDEF UseLRS}
|
|
FIconLRSSource: string;
|
|
{$ENDIF}
|
|
function DoShowSavePackageAsDialog(APackage: TLazPackage): TModalResult;
|
|
function CheckPackageGraphForCompilation(APackage: TLazPackage;
|
|
FirstDependency: TPkgDependency;
|
|
const Directory: string;
|
|
ShowAbort: boolean): TModalResult;
|
|
procedure SaveAutoInstallDependencies;
|
|
procedure LoadStaticCustomPackages;
|
|
function LoadInstalledPackage(const PackageName: string;
|
|
AddToAutoInstall: boolean; var Quiet: boolean): TLazPackage;
|
|
procedure LoadAutoInstallPackages;
|
|
procedure AddUnitToProjectMainUsesSection(AProject: TProject;
|
|
const AnUnitName, AnUnitInFilename: string);
|
|
procedure AddToIconResource(const aIconFile, aResName: string);
|
|
// move files
|
|
function CheckDrag(Sender, Source: TObject; X, Y: Integer;
|
|
out SrcFilesEdit, TargetFilesEdit: IFilesEditorInterface;
|
|
out aFileCount, aDependencyCount, aDirectoryCount: integer;
|
|
out TargetTVNode: TTreeNode; out TargetTVType: TTreeViewInsertMarkType
|
|
): boolean;
|
|
procedure FilesEditDragDrop(Sender, Source: TObject; X, Y: Integer);
|
|
function MoveFiles(TargetFilesEdit, SrcFilesEdit: IFilesEditorInterface;
|
|
TargetDirectory: string): boolean;
|
|
function MoveFiles(TargetFilesEdit, SrcFilesEdit: IFilesEditorInterface;
|
|
IDEFiles: TFPList; TargetDirectory: string): boolean;
|
|
function CopyMoveFiles(Sender: TObject): boolean;
|
|
function ResolveBrokenDependenciesOnline(ABrokenDependencies: TFPList): TModalResult;
|
|
function ShowBrokenDependenciesReport(Dependencies: TFPList): TModalResult;
|
|
// Components
|
|
function FilterMissingDepsForUnit(const UnitFilename: string;
|
|
InputPackageList: TPackagePackageArray;
|
|
out OutputPackageList: TOwnerPackageArray): TModalResult;
|
|
function GetUnitsAndDepsForComps(ComponentClasses: TClassList;
|
|
out PackageList: TPackagePackageArray; out UnitList: TStringList): TModalResult;
|
|
public
|
|
constructor Create(TheOwner: TComponent); override;
|
|
destructor Destroy; override;
|
|
|
|
// initialization and menu
|
|
procedure ConnectMainBarEvents; override;
|
|
procedure ConnectSourceNotebookEvents; override;
|
|
procedure SetupMainBarShortCuts; override;
|
|
procedure SetRecentPackagesMenu; override;
|
|
procedure AddToMenuRecentPackages(const Filename: string);
|
|
procedure SaveSettings; override;
|
|
procedure ProcessCommand(Command: word; var Handled: boolean); override;
|
|
procedure OnSourceEditorPopupMenu(const AddMenuItemProc: TAddMenuItemProc); override;
|
|
procedure TranslateResourceStrings; override;
|
|
|
|
// files
|
|
function GetDefaultSaveDirectoryForFile(const Filename: string): string; override;
|
|
function OnRenameFile(const OldFilename, NewFilename: string;
|
|
IsPartOfProject: boolean): TModalResult; override;
|
|
function FindIncludeFileInProjectDependencies(aProject: TProject;
|
|
const Filename: string): string; override;
|
|
function GetOwnersOfUnit(const UnitFilename: string): TFPList; override;
|
|
procedure ExtendOwnerListWithUsedByOwners(OwnerList: TFPList); override;
|
|
function GetSourceFilesOfOwners(OwnerList: TFPList): TStrings; override;
|
|
function GetUnitsOfOwners(OwnerList: TFPList; Flags: TPkgIntfGatherUnitTypes): TStrings; override;
|
|
function GetPossibleOwnersOfUnit(const UnitFilename: string;
|
|
Flags: TPkgIntfOwnerSearchFlags): TFPList; override;
|
|
function GetPackageOfCurrentSourceEditor(out APackage: TIDEPackage): TPkgFile;
|
|
function GetPackageOfSourceEditor(out APackage: TIDEPackage; ASrcEdit: TObject): TLazPackageFile; override;
|
|
function FindVirtualUnitSource(PkgFile: TPkgFile): string;
|
|
function SearchFile(const AFilename: string;
|
|
SearchFlags: TSearchIDEFileFlags;
|
|
InObject: TObject): TPkgFile; override;
|
|
function SearchUnitInDesigntimePackages(const AnUnitName: string;
|
|
InObject: TObject): TPkgFile; override;
|
|
function ShowFindInPackageFilesDlg(APackage: TLazPackage): TModalResult;
|
|
|
|
// package graph
|
|
function AddPackageToGraph(APackage: TLazPackage): TModalResult;
|
|
procedure DoShowPackageGraph(Show: boolean);
|
|
procedure DoShowPackageGraphPathList(PathList: TFPList); override;
|
|
function CheckUserSearchPaths(aCompilerOptions: TBaseCompilerOptions): TModalResult; override;
|
|
procedure LazarusSrcDirChanged; override;
|
|
function GetPackageCount: integer; override;
|
|
function GetPackages(Index: integer): TIDEPackage; override;
|
|
function FindPackageWithName(const PkgName: string; IgnorePackage: TIDEPackage = nil): TIDEPackage; override;
|
|
function FindInstalledPackageWithUnit(const AnUnitName: string
|
|
): TIDEPackage; override;
|
|
function IsPackageInstalled(const PkgName: string): TIDEPackage; override;
|
|
function IsOwnerDependingOnPkg(AnOwner: TObject; const PkgName: string;
|
|
out DependencyOwner: TObject): boolean; override;
|
|
procedure GetRequiredPackages(AnOwner: TObject; out PkgList: TFPList;
|
|
Flags: TPkgIntfRequiredFlags = []); override;
|
|
function AddDependencyToOwners(OwnerList: TFPList; APackage: TIDEPackage;
|
|
OnlyTestIfPossible: boolean = false): TModalResult; override;
|
|
function AddDependencyToUnitOwners(const OwnedFilename,
|
|
RequiredUnitname: string): TModalResult; override;
|
|
function RedirectPackageDependency(APackage: TIDEPackage): TIDEPackage; override;
|
|
procedure GetPackagesChangedOnDisk(out ListOfPackages: TStringList; IgnoreModifiedFlag: boolean = False); override;
|
|
function RevertPackages(APackageList: TStringList): TModalResult; override;
|
|
|
|
// project
|
|
function OpenProjectDependencies(AProject: TProject;
|
|
ReportMissing: boolean): TModalResult; override;
|
|
function CheckProjectHasInstalledPackages(AProject: TProject;
|
|
Interactive: boolean): TModalResult; override;
|
|
function CanOpenDesignerForm(AnUnitInfo: TUnitInfo;
|
|
Interactive: boolean): TModalResult; override;
|
|
function AddProjectDependency(AProject: TProject; APackage: TLazPackage;
|
|
OnlyTestIfPossible: boolean = false): TModalResult; override;
|
|
function AddProjectDependency(AProject: TProject;
|
|
ADependency: TPkgDependency): TModalResult; override;
|
|
function AddProjectDependencies(AProject: TProject; const Packages: string;
|
|
OnlyTestIfPossible: boolean = false): TModalResult; override;
|
|
function ProjectInspectorAddDependency(Sender: TObject;
|
|
ADependency: TPkgDependency): TModalResult; override;
|
|
function ProjectInspectorRemoveDependency(Sender: TObject;
|
|
ADependency: TPkgDependency): TModalResult; override;
|
|
function ProjectInspectorReAddDependency(Sender: TObject;
|
|
ADependency: TPkgDependency): TModalResult; override;
|
|
procedure ProjectInspectorDragDropTreeView(Sender, Source: TObject;
|
|
X, Y: Integer); override;
|
|
function ProjectInspectorDragOverTreeView(Sender, Source: TObject;
|
|
X, Y: Integer; out TargetTVNode: TTreeNode;
|
|
out TargetTVType: TTreeViewInsertMarkType): boolean; override;
|
|
procedure ProjectInspectorCopyMoveFiles(Sender: TObject); override;
|
|
|
|
// package editors
|
|
function CanClosePackageEditor(APackage: TLazPackage): TModalResult; override;
|
|
function CanCloseAllPackageEditors: TModalResult; override;
|
|
function DoOpenPkgFile(PkgFile: TPkgFile): TModalResult;
|
|
function DoNewPackage: TModalResult; override;
|
|
function DoShowLoadedPkgDlg: TModalResult; override;
|
|
function DoOpenPackage(APackage: TLazPackage; Flags: TPkgOpenFlags;
|
|
ShowAbort: boolean): TModalResult; override;
|
|
function DoOpenPackageWithName(const APackageName: string;
|
|
Flags: TPkgOpenFlags; ShowAbort: boolean): TModalResult; override;
|
|
function DoOpenPackageFile(AFilename: string;
|
|
Flags: TPkgOpenFlags;
|
|
ShowAbort: boolean): TModalResult; override;
|
|
function IsPackageEditorForm(AForm: TCustomForm): boolean; override;
|
|
procedure OpenHiddenModifiedPackages; override;
|
|
function DoSavePackage(APackage: TLazPackage;
|
|
Flags: TPkgSaveFlags): TModalResult; override;
|
|
function DoSaveAllPackages(Flags: TPkgSaveFlags): TModalResult; override;
|
|
function DoClosePackageEditor(APackage: TLazPackage): TModalResult; override;
|
|
function DoCloseAllPackageEditors: TModalResult; override;
|
|
function DoAddActiveUnitToAPackage: TModalResult;
|
|
function DoNewPackageComponent: TModalResult;
|
|
function SavePackageFiles(APackage: TLazPackage): TModalResult;
|
|
function WarnAboutMissingPackageFiles(APackage: TLazPackage): TModalResult;
|
|
function AddPackageDependency(APackage: TLazPackage; const ReqPackage: string;
|
|
OnlyTestIfPossible: boolean = false): TModalResult; override;
|
|
function ApplyDependency(CurDependency: TPkgDependency): TModalResult; override;
|
|
function GetPackageOfEditorItem(Sender: TObject): TIDEPackage; override;
|
|
|
|
// package compilation
|
|
function DoCompileProjectDependencies(AProject: TProject;
|
|
Flags: TPkgCompileFlags): TModalResult; override;
|
|
function DoCompilePackage(APackage: TIDEPackage; Flags: TPkgCompileFlags;
|
|
ShowAbort: boolean): TModalResult; override;
|
|
function DoCreatePackageMakefile(APackage: TLazPackage;
|
|
ShowAbort: boolean): TModalResult;
|
|
function DoCreatePackageFpmakefile(APackage: TLazPackage;
|
|
ShowAbort: boolean): TModalResult;
|
|
|
|
// package installation
|
|
procedure LoadInstalledPackages; override;
|
|
procedure UnloadInstalledPackages;
|
|
function DoInstallPackage(APackage: TLazPackage): TModalResult;
|
|
function DoUninstallPackage(APackage: TLazPackage;
|
|
Flags: TPkgUninstallFlags; ShowAbort: boolean): TModalResult;
|
|
function CheckInstallPackageList(InstallPkgIDList: TObjectList;
|
|
UninstallPkgIDList: TObjectList = nil; Flags: TPkgInstallInIDEFlags = []
|
|
): boolean; override;
|
|
function InstallPackages(PkgIdList: TObjectList;
|
|
Flags: TPkgInstallInIDEFlags = []): TModalResult; override;
|
|
function UninstallPackage(APackage: TIDEPackage; ShowAbort: boolean): TModalResult; override;
|
|
procedure DoTranslatePackage(APackage: TLazPackage);
|
|
function DoOpenPackageSource(APackage: TLazPackage): TModalResult;
|
|
function DoCompileAutoInstallPackages(Flags: TPkgCompileFlags;
|
|
OnlyBase: boolean): TModalResult; override;
|
|
function DoSaveAutoInstallConfig: TModalResult; override;
|
|
function DoPublishPackage(APackage: TLazPackage; Flags: TPkgSaveFlags;
|
|
ShowDialog: boolean): TModalResult;
|
|
|
|
// components
|
|
function AddUnitDepsForCompClasses(const UnitFilename: string;
|
|
ComponentClasses: TClassList; Quiet: boolean): TModalResult; override;
|
|
function GetUsableComponentUnits(CurRoot: TPersistent): TFPList; override; // list of TUnitInfo
|
|
procedure IterateComponentNames(CurRoot: TPersistent; TypeData: PTypeData;
|
|
Proc: TGetStrProc); override;
|
|
function FindUsableComponent(CurRoot: TPersistent;
|
|
const ComponentPath: string): TComponent; override;
|
|
function FindReferencedRootComponent(CurRoot: TPersistent;
|
|
const ComponentName: string): TComponent; override;
|
|
end;
|
|
|
|
|
|
{ TLazPackageDescriptors }
|
|
|
|
TLazPackageDescriptors = class(TPackageDescriptors)
|
|
private
|
|
fDestroying: boolean;
|
|
fItems: TFPList; // list of TProjectDescriptor
|
|
protected
|
|
function GetItems(Index: integer): TPackageDescriptor; override;
|
|
public
|
|
constructor Create;
|
|
destructor Destroy; override;
|
|
function Count: integer; override;
|
|
function GetUniqueName(const Name: string): string; override;
|
|
function IndexOf(const Name: string): integer; override;
|
|
function FindByName(const Name: string): TPackageDescriptor; override;
|
|
procedure RegisterDescriptor(Descriptor: TPackageDescriptor); override;
|
|
procedure UnregisterDescriptor(Descriptor: TPackageDescriptor); override;
|
|
procedure AddDefaultPackageDescriptors;
|
|
public
|
|
property Items[Index: integer]: TPackageDescriptor read GetItems; default;
|
|
end;
|
|
|
|
|
|
{ TPackageDescriptorStd }
|
|
|
|
TPackageDescriptorStd = class(TPackageDescriptor)
|
|
public
|
|
constructor Create; override;
|
|
function GetLocalizedName: string; override;
|
|
function GetLocalizedDescription: string; override;
|
|
end;
|
|
|
|
var
|
|
LazPackageDescriptors: TLazPackageDescriptors;
|
|
|
|
implementation
|
|
|
|
const
|
|
constNewPackageName = 'NewPackage'; //must be valid Pascal identifier, thus should not be allowed to be translated
|
|
|
|
{ TPkgManager }
|
|
|
|
procedure TPkgManager.MainIDEitmPkgOpenPackageFileClick(Sender: TObject);
|
|
var
|
|
OpenDialog: TOpenDialog;
|
|
AFilename: string;
|
|
I: Integer;
|
|
OpenFlags: TPkgOpenFlags;
|
|
begin
|
|
OpenDialog:=TOpenDialog.Create(nil);
|
|
try
|
|
InputHistories.ApplyFileDialogSettings(OpenDialog);
|
|
OpenDialog.Title:=lisOpenPackageFile;
|
|
OpenDialog.Options:=OpenDialog.Options+[ofAllowMultiSelect];
|
|
OpenDialog.Filter:=dlgFilterLazarusPackage+' (*.lpk)|*.lpk'
|
|
+'|'+dlgFilterAll+' ('+FileMask+')|'+FileMask;
|
|
if OpenDialog.Execute and (OpenDialog.Files.Count>0) then begin
|
|
OpenFlags:=[pofAddToRecent];
|
|
For I := 0 to OpenDialog.Files.Count-1 do
|
|
Begin
|
|
AFilename:=CleanAndExpandFilename(OpenDialog.Files.Strings[i]);
|
|
if i<OpenDialog.Files.Count-1 then
|
|
Include(OpenFlags,pofMultiOpen)
|
|
else
|
|
Exclude(OpenFlags,pofMultiOpen);
|
|
if DoOpenPackageFile(AFilename,OpenFlags,true)=mrAbort then begin
|
|
break;
|
|
end;
|
|
end;
|
|
end;
|
|
InputHistories.StoreFileDialogSettings(OpenDialog);
|
|
finally
|
|
OpenDialog.Free;
|
|
end;
|
|
end;
|
|
|
|
procedure TPkgManager.MainIDEitmPkgPkgGraphClick(Sender: TObject);
|
|
begin
|
|
DoShowPackageGraph(true);
|
|
end;
|
|
|
|
procedure TPkgManager.MainIDEitmPkgEditInstallPkgsClick(Sender: TObject);
|
|
var
|
|
RebuildIDE: Boolean;
|
|
PkgIDList: TObjectList;
|
|
begin
|
|
if ShowEditInstallPkgsDialog(PackageGraph.FirstInstallDependency, PkgIDList, RebuildIDE) then
|
|
if RebuildIDE
|
|
then InstallPackages(PkgIDList, [piiifSkipChecks, piiifClear, piiifRebuildIDE])
|
|
else InstallPackages(PkgIDList, [piiifSkipChecks, piiifClear]);
|
|
FreeThenNil(PkgIDList);
|
|
end;
|
|
|
|
procedure TPkgManager.IDEComponentPaletteOpenPackage(Sender: TObject);
|
|
begin
|
|
if (Sender=nil) or (not (Sender is TLazPackage)) then exit;
|
|
DoOpenPackage(TLazPackage(Sender),[],false);
|
|
end;
|
|
|
|
procedure TPkgManager.IDEComponentPaletteOpenUnit(Sender: TObject);
|
|
var
|
|
PkgComponent: TPkgComponent;
|
|
PkgFile: TPkgFile;
|
|
Filename: String;
|
|
begin
|
|
if (Sender=nil) then exit;
|
|
if (Sender is TPkgFile) then
|
|
DoOpenPkgFile(TPkgFile(Sender))
|
|
else if (Sender is TPkgComponent) then begin
|
|
PkgComponent:=TPkgComponent(Sender);
|
|
PkgFile:=PkgComponent.PkgFile;
|
|
if PkgFile=nil then exit;
|
|
Filename:='';
|
|
if PkgFile.FileType=pftVirtualUnit then
|
|
Filename:=FindVirtualUnitSource(PkgFile);
|
|
if Filename='' then
|
|
Filename:=PkgFile.GetFullFilename;
|
|
MainIDE.DoOpenFileAndJumpToIdentifier(
|
|
Filename,PkgComponent.ComponentClass.ClassName,
|
|
-1, -1, // open page somewhere
|
|
[ofOnlyIfExists,ofAddToRecent,ofRegularFile,ofConvertMacros]);
|
|
end;
|
|
end;
|
|
|
|
procedure TPkgManager.GetDependencyOwnerDescription(
|
|
Dependency: TPkgDependency; out Description: string);
|
|
begin
|
|
GetDescriptionOfDependencyOwner(Dependency,Description);
|
|
end;
|
|
|
|
procedure TPkgManager.GetDependencyOwnerDirectory(Dependency: TPkgDependency;
|
|
out Directory: string);
|
|
begin
|
|
GetDirectoryOfDependencyOwner(Dependency,Directory);
|
|
end;
|
|
|
|
procedure TPkgManager.PackageFileLoaded(Sender: TObject);
|
|
begin
|
|
DoCallNotifyHandler(pihtPackageFileLoaded,Sender);
|
|
end;
|
|
|
|
function TPkgManager.DoBeforeCompilePackages(aPkgList: TFPList): TModalResult;
|
|
// called before a bunch of packages are compiled
|
|
|
|
function GetIgnorePkgOutDirID(CurPkg: TLazPackage): string;
|
|
begin
|
|
Result:='PkgOutDir#'+CurPkg.Filename+':'+CurPkg.GetOutputDirectory;
|
|
end;
|
|
|
|
var
|
|
PkgWithProjOverriddenOutDirs: TFPList;
|
|
i: Integer;
|
|
CurPkg: TLazPackage;
|
|
OutDir: String;
|
|
IgnoreItem: TIgnoreIDEQuestionItem;
|
|
s: String;
|
|
begin
|
|
Result:=mrOk;
|
|
if MainIDEBar=nil then exit; // not interactive
|
|
if InputHistories=nil then exit;
|
|
|
|
if not Assigned(OnGetOutputDirectoryOverride) then exit;
|
|
PkgWithProjOverriddenOutDirs:=TFPList.Create;
|
|
try
|
|
for i:=0 to aPkgList.Count-1 do
|
|
begin
|
|
CurPkg:=TLazPackage(aPkgList[i]);
|
|
OutDir:='';
|
|
OnGetOutputDirectoryOverride(CurPkg,OutDir,[bmgtProject,bmgtSession]);
|
|
if OutDir<>'' then begin
|
|
IgnoreItem:=InputHistories.Ignores.Find(GetIgnorePkgOutDirID(CurPkg));
|
|
if (IgnoreItem=nil) then
|
|
PkgWithProjOverriddenOutDirs.Add(CurPkg);
|
|
end;
|
|
end;
|
|
if PkgWithProjOverriddenOutDirs.Count>0 then
|
|
begin
|
|
s:='';
|
|
for i:=0 to PkgWithProjOverriddenOutDirs.Count-1 do begin
|
|
CurPkg:=TLazPackage(PkgWithProjOverriddenOutDirs[i]);
|
|
OutDir:=CreateRelativePath(CurPkg.GetOutputDirectory,CurPkg.Directory);
|
|
s+=CurPkg.Name+': '+OutDir+LineEnding;
|
|
end;
|
|
if IDEMessageDialog(lisConfirmation,
|
|
Format(lisPkgTheProjectOverridesTheOutputDirectoryOfTheFollowin,
|
|
[LineEnding, LineEnding+LineEnding, s]),
|
|
mtWarning, [mbOk, mbCancel])<>mrOk
|
|
then
|
|
exit(mrCancel);
|
|
// remember the answer
|
|
for i:=0 to PkgWithProjOverriddenOutDirs.Count-1 do begin
|
|
CurPkg:=TLazPackage(PkgWithProjOverriddenOutDirs[i]);
|
|
InputHistories.Ignores.Add(GetIgnorePkgOutDirID(CurPkg),iiidForever);
|
|
end;
|
|
end;
|
|
finally
|
|
PkgWithProjOverriddenOutDirs.Free;
|
|
end;
|
|
end;
|
|
|
|
function TPkgManager.LoadDependencyList(FirstDependency: TPkgDependency;
|
|
Quiet: boolean): TModalResult;
|
|
var
|
|
CurDependency: TPkgDependency;
|
|
OpenResult: TLoadPackageResult;
|
|
begin
|
|
Result:=mrCancel;
|
|
// load all packages
|
|
CurDependency:=FirstDependency;
|
|
while CurDependency<>nil do begin
|
|
OpenResult:=PackageGraph.OpenDependency(CurDependency,false);
|
|
if OpenResult<>lprSuccess then begin
|
|
if not Quiet then
|
|
IDEMessageDialog(lisCCOErrorCaption,
|
|
Format(lisUnableToLoadPackage, [CurDependency.AsString]),
|
|
mtError,[mbCancel]);
|
|
exit;
|
|
end;
|
|
CurDependency:=CurDependency.NextRequiresDependency;
|
|
end;
|
|
Result:=mrOk;
|
|
end;
|
|
|
|
procedure TPkgManager.OpenPackageForCurrentSrcEditFile(Sender: TObject);
|
|
var
|
|
APackage: TIDEPackage;
|
|
begin
|
|
GetPackageOfCurrentSourceEditor(APackage);
|
|
if APackage is TLazPackage then
|
|
DoOpenPackage(TLazPackage(APackage),[],false);
|
|
end;
|
|
|
|
procedure TPkgManager.CreateIDEWindow(Sender: TObject; aFormName: string; var
|
|
AForm: TCustomForm; DoDisableAutoSizing: boolean);
|
|
var
|
|
APackageName: String;
|
|
NewDependency: TPkgDependency;
|
|
APackage: TLazPackage;
|
|
LoadResult: TLoadPackageResult;
|
|
begin
|
|
//debugln(['TPkgManager.CreateIDEWindow ',aFormName]);
|
|
if SysUtils.CompareText(aFormName,NonModalIDEWindowNames[nmiwPkgGraphExplorer])=0
|
|
then begin
|
|
DoShowPackageGraph(false);
|
|
AForm:=PackageGraphExplorer;
|
|
if DoDisableAutoSizing then
|
|
AForm.DisableAutoSizing{$IFDEF DebugDisableAutoSizing}('TPkgManager.CreateIDEWindow'){$ENDIF};
|
|
end else if SysUtils.CompareText(PackageEditorWindowPrefix,
|
|
copy(aFormName,1,length(PackageEditorWindowPrefix)))=0
|
|
then begin
|
|
APackageName:=copy(aFormName,length(PackageEditorWindowPrefix)+1,length(aFormName));
|
|
if not IsValidPkgName(APackageName) then exit;
|
|
NewDependency:=TPkgDependency.Create;
|
|
try
|
|
NewDependency.PackageName:=APackageName;
|
|
NewDependency.DependencyType:=pdtLazarus;
|
|
LoadResult:=PackageGraph.OpenDependency(NewDependency,false);
|
|
if LoadResult<>lprSuccess then exit;
|
|
finally
|
|
NewDependency.Free;
|
|
end;
|
|
APackage:=PackageGraph.FindPackageWithName(APackageName,nil);
|
|
if APackage=nil then exit;
|
|
AForm:=PackageEditors.CreateEditor(APackage,DoDisableAutoSizing);
|
|
end;
|
|
end;
|
|
|
|
function TPkgManager.PackageGraphSrcEditFileIsModified(const SrcFilename: string
|
|
): boolean;
|
|
var
|
|
SrcEdit: TSourceEditor;
|
|
begin
|
|
SrcEdit:=SourceEditorManager.SourceEditorIntfWithFilename(SrcFilename);
|
|
Result:=(SrcEdit<>nil) and SrcEdit.Modified;
|
|
end;
|
|
|
|
procedure TPkgManager.MainIDEitmPkgAddCurFileToPkgClick(Sender: TObject);
|
|
begin
|
|
DoAddActiveUnitToAPackage;
|
|
end;
|
|
|
|
procedure TPkgManager.MainIDEitmPkgNewComponentClick(Sender: TObject);
|
|
begin
|
|
DoNewPackageComponent;
|
|
end;
|
|
|
|
procedure TPkgManager.MainIDEitmPkgOpenPackageOfCurUnitClicked(Sender: TObject);
|
|
var
|
|
ActiveSourceEditor: TSourceEditorInterface;
|
|
ActiveUnitInfo: TUnitInfo;
|
|
PkgFile: TPkgFile;
|
|
begin
|
|
MainIDE.GetCurrentUnitInfo(ActiveSourceEditor,ActiveUnitInfo);
|
|
if ActiveSourceEditor=nil then exit;
|
|
PkgFile:=PackageGraph.FindFileInAllPackages(ActiveUnitInfo.Filename,true,
|
|
not ActiveUnitInfo.IsPartOfProject);
|
|
if PkgFile=nil then
|
|
IDEMessageDialog(lisProjAddPackageNotFound,
|
|
lisPkgThisFileIsNotInAnyLoadedPackage, mtInformation, [mbCancel])
|
|
else
|
|
DoOpenPackageFile(PkgFile.LazPackage.Filename,[pofAddToRecent],false);
|
|
end;
|
|
|
|
procedure TPkgManager.AfterWritePackage(Sender: TObject; Restore: boolean);
|
|
var
|
|
Pkg: TLazPackage;
|
|
begin
|
|
Pkg := (Sender as TPackageIDEOptions).Package;
|
|
//debugln(['TPkgManager.AfterWritePackage ',Pkg, ' Restore=',Restore]);
|
|
Pkg.DefineTemplates.AllChanged(false);
|
|
if Restore then
|
|
Pkg.RestoreOptions;
|
|
end;
|
|
|
|
procedure TPkgManager.BeforeReadPackage(Sender: TObject);
|
|
begin
|
|
(Sender as TPackageIDEOptions).Package.BackupOptions;
|
|
end;
|
|
|
|
function TPkgManager.PackageEditorCompilePackage(Sender: TObject;
|
|
APackage: TLazPackage; CompileClean, CompileRequired: boolean): TModalResult;
|
|
var
|
|
Flags: TPkgCompileFlags;
|
|
begin
|
|
Flags:=[];
|
|
if CompileClean then Include(Flags,pcfCleanCompile);
|
|
if CompileRequired then Include(Flags,pcfCompileDependenciesClean);
|
|
//debugln('TPkgManager.OnPackageEditorCompilePackage OS=',Globals.TargetOS);
|
|
Result:=DoCompilePackage(APackage,Flags,false);
|
|
end;
|
|
|
|
procedure TPkgManager.PackageEditorCopyMoveFiles(Sender: TObject);
|
|
begin
|
|
CopyMoveFiles(Sender);
|
|
end;
|
|
|
|
function TPkgManager.PackageEditorCreateMakefile(Sender: TObject;
|
|
APackage: TLazPackage): TModalResult;
|
|
begin
|
|
Result:=DoCreatePackageMakefile(APackage,false);
|
|
end;
|
|
|
|
function TPkgManager.PackageEditorCreateFpmakeFile(Sender: TObject;
|
|
APackage: TLazPackage): TModalResult;
|
|
begin
|
|
Result:=DoCreatePackageFpmakefile(APackage,false);
|
|
end;
|
|
|
|
{$IFDEF UseLRS}
|
|
procedure TPkgManager.AddToIconResource(const aIconFile, aResName: string);
|
|
var
|
|
BinFileStream: TFileStream;
|
|
ResMemStream: TMemoryStream;
|
|
ResType: String;
|
|
OldLen, NewLen: integer;
|
|
begin
|
|
try
|
|
BinFileStream:=TFileStream.Create(aIconFile,fmOpenRead);
|
|
try
|
|
ResMemStream:=TMemoryStream.Create;
|
|
try
|
|
Assert(BinFileStream.Position=0, 'TPkgManager.AddToIconResource: Stream.Position > 0');
|
|
ResType:=UpperCase(ExtractFileExt(aIconFile));
|
|
if ResType<>'' then
|
|
Delete(ResType, 1, 1);
|
|
BinaryToLazarusResourceCode(BinFileStream,ResMemStream,aResName,ResType);
|
|
ResMemStream.Position:=0;
|
|
OldLen:=Length(FIconLRSSource);
|
|
NewLen:=ResMemStream.Size;
|
|
if NewLen>0 then begin
|
|
SetLength(FIconLRSSource,OldLen+NewLen);
|
|
ResMemStream.Read(FIconLRSSource[OldLen+1],NewLen);
|
|
end;
|
|
finally
|
|
ResMemStream.Free;
|
|
end;
|
|
finally
|
|
BinFileStream.Free;
|
|
end;
|
|
except
|
|
on E: Exception do begin
|
|
MessageDlg(lisCCOErrorCaption,
|
|
Format(lisErrorLoadingFile2,[aIconFile]) + LineEnding + E.Message,
|
|
mtError, [mbCancel], 0);
|
|
end;
|
|
end;
|
|
end;
|
|
{$ELSE}
|
|
// ToDo: Use FPC's resource type (.res)
|
|
{$ENDIF}
|
|
|
|
function TPkgManager.PackageEditorCreateFile(Sender: TObject;
|
|
Params: TAddToPkgResult): TModalResult;
|
|
var
|
|
LE: String;
|
|
UsesLine: String;
|
|
NewSource: String;
|
|
UnitDirectives: String;
|
|
IconLRSFilename: String;
|
|
ResName: String;
|
|
CodeBuf: TCodeBuffer;
|
|
begin
|
|
Result:=mrCancel;
|
|
// create icon resource
|
|
if Params.IconNormFile<>'' then
|
|
begin
|
|
IconLRSFilename:=ChangeFileExt(Params.UnitFilename,'')+'_icon.lrs';
|
|
CodeBuf:=CodeToolBoss.CreateFile(IconLRSFilename);
|
|
if CodeBuf=nil then begin
|
|
debugln(['Error: (lazarus) [TPkgManager.PackageEditorCreateFile] file create failed: ',IconLRSFilename]);
|
|
exit;
|
|
end;
|
|
FIconLRSSource:='';
|
|
ResName:=ExtractFileNameOnly(Params.NewClassName);
|
|
AddToIconResource(Params.IconNormFile, ResName);
|
|
if Params.Icon150File<>'' then
|
|
AddToIconResource(Params.Icon150File, ResName+'_150');
|
|
if Params.Icon200File<>'' then
|
|
AddToIconResource(Params.Icon200File, ResName+'_200');
|
|
CodeBuf.Source:=FIconLRSSource;
|
|
Result:=SaveCodeBuffer(CodeBuf);
|
|
if Result<>mrOk then exit;
|
|
end
|
|
else
|
|
IconLRSFilename:='';
|
|
// create sourcecode
|
|
UsesLine:='Classes, SysUtils';
|
|
if PackageGraph.FindDependencyRecursively(Params.Pkg.FirstRequiredDependency,'LCL')<>nil
|
|
then
|
|
UsesLine:=UsesLine+', LResources, Forms, Controls, Graphics, Dialogs';
|
|
if (System.Pos(Params.UsedUnitname,UsesLine)<1) and (Params.UsedUnitname<>'') then
|
|
UsesLine:=UsesLine+', '+Params.UsedUnitname;
|
|
UnitDirectives:='{$mode objfpc}{$H+}';
|
|
if Params.Pkg<>nil then
|
|
UnitDirectives:=TFileDescPascalUnit.CompilerOptionsToUnitDirectives(
|
|
Params.Pkg.CompilerOptions);
|
|
LE:=LineEnding;
|
|
NewSource:=
|
|
'unit '+Params.Unit_Name+';'+LE
|
|
+LE
|
|
+UnitDirectives+LE
|
|
+LE
|
|
+'interface'+LE
|
|
+LE
|
|
+'uses'+LE
|
|
+' '+UsesLine+';'+LE
|
|
+LE
|
|
+'type'+LE
|
|
+' '+Params.NewClassName+' = class('+Params.AncestorType+')'+LE
|
|
+' private'+LE
|
|
+LE
|
|
+' protected'+LE
|
|
+LE
|
|
+' public'+LE
|
|
+LE
|
|
+' published'+LE
|
|
+LE
|
|
+' end;'+LE
|
|
+LE
|
|
+'procedure Register;'+LE
|
|
+LE
|
|
+'implementation'+LE
|
|
+LE
|
|
+'procedure Register;'+LE
|
|
+'begin'+LE;
|
|
if IconLRSFilename<>'' then
|
|
NewSource:=NewSource
|
|
+' {$I '+ExtractFileName(IconLRSFilename)+'}'+LE;
|
|
NewSource:=NewSource
|
|
+' RegisterComponents('''+Params.PageName+''',['+Params.NewClassName+']);'+LE
|
|
+'end;'+LE
|
|
+LE
|
|
+'end.'+LE;
|
|
|
|
FileDescriptorUnit.Owner:=Params.Pkg;
|
|
try
|
|
Result:=MainIDE.DoNewEditorFile(FileDescriptorUnit,
|
|
Params.UnitFilename,NewSource,
|
|
[nfOpenInEditor,nfIsNotPartOfProject,nfSave,nfAddToRecent]);
|
|
finally
|
|
FileDescriptorUnit.Owner:=nil;
|
|
end;
|
|
end;
|
|
|
|
function TPkgManager.PackageEditorDeleteAmbiguousFiles(Sender: TObject;
|
|
APackage: TLazPackage; const Filename: string): TModalResult;
|
|
begin
|
|
Result:=BuildBoss.DeleteAmbiguousFiles(Filename);
|
|
end;
|
|
|
|
procedure TPkgManager.PackageEditorDragDropTreeView(Sender, Source: TObject;
|
|
X, Y: Integer);
|
|
begin
|
|
FilesEditDragDrop(Sender, Source, X, Y);
|
|
end;
|
|
|
|
function TPkgManager.PackageEditorDragOverTreeView(Sender, Source: TObject;
|
|
X, Y: Integer; out TargetTVNode: TTreeNode;
|
|
out TargetTVType: TTreeViewInsertMarkType): boolean;
|
|
var
|
|
aFileCount: integer;
|
|
aDependencyCount: integer;
|
|
aDirectoryCount: integer;
|
|
TargetFilesEdit: IFilesEditorInterface;
|
|
SrcFilesEdit: IFilesEditorInterface;
|
|
begin
|
|
Result:=CheckDrag(Sender, Source, X, Y, TargetFilesEdit, SrcFilesEdit, aFileCount,
|
|
aDependencyCount, aDirectoryCount, TargetTVNode, TargetTVType);
|
|
end;
|
|
|
|
function TPkgManager.PackageEditorFindInFiles(Sender: TObject;
|
|
APackage: TLazPackage): TModalResult;
|
|
begin
|
|
Result:=ShowFindInPackageFilesDlg(APackage);
|
|
end;
|
|
|
|
function TPkgManager.PackageEditorAddToProject(Sender: TObject;
|
|
APackage: TLazPackage; OnlyTestIfPossible: boolean): TModalResult;
|
|
begin
|
|
Result:=AddProjectDependency(Project1,APackage,OnlyTestIfPossible);
|
|
end;
|
|
|
|
function TPkgManager.PackageEditorInstallPackage(Sender: TObject;
|
|
APackage: TLazPackage): TModalResult;
|
|
begin
|
|
Result:=DoInstallPackage(APackage);
|
|
end;
|
|
|
|
function TPkgManager.PackageEditorPublishPackage(Sender: TObject;
|
|
APackage: TLazPackage): TModalResult;
|
|
begin
|
|
Result:=DoPublishPackage(APackage,[],true);
|
|
end;
|
|
|
|
function TPkgManager.PackageEditorRevertPackage(Sender: TObject;
|
|
APackage: TLazPackage): TModalResult;
|
|
begin
|
|
if (not FilenameIsAbsolute(APackage.Filename))
|
|
or (not FileExistsUTF8(APackage.Filename)) then
|
|
exit(mrCancel);
|
|
Result:=DoOpenPackageFile(APackage.Filename,[pofRevert],false);
|
|
end;
|
|
|
|
function TPkgManager.PackageEditorUninstallPackage(Sender: TObject;
|
|
APackage: TLazPackage): TModalResult;
|
|
begin
|
|
Result:=DoUninstallPackage(APackage,[],false);
|
|
end;
|
|
|
|
function TPkgManager.PackageEditorOpenPkgFile(Sender: TObject;
|
|
PkgFile: TPkgFile): TModalResult;
|
|
begin
|
|
Result:=DoOpenPkgFile(PkgFile);
|
|
end;
|
|
|
|
procedure TPkgManager.PackageEditorFreeEditor(APackage: TLazPackage);
|
|
begin
|
|
APackage.Editor:=nil;
|
|
PackageGraph.ClosePackage(APackage);
|
|
end;
|
|
|
|
function TPkgManager.PackageGraphCheckInterPkgFiles(IDEObject: TObject;
|
|
PkgList: TFPList; out FilesChanged: boolean): boolean;
|
|
begin
|
|
Result:=CheckInterPkgFiles(IDEObject,PkgList,FilesChanged);
|
|
end;
|
|
|
|
function TPkgManager.PackageEditorOpenPackage(Sender: TObject;
|
|
APackage: TLazPackage): TModalResult;
|
|
begin
|
|
Result:=DoOpenPackage(APackage,[],false);
|
|
end;
|
|
|
|
function TPkgManager.PackageEditorSavePackage(Sender: TObject;
|
|
APackage: TLazPackage; SaveAs: boolean): TModalResult;
|
|
begin
|
|
if SaveAs then
|
|
Result:=DoSavePackage(APackage,[psfSaveAs])
|
|
else
|
|
Result:=DoSavePackage(APackage,[]);
|
|
end;
|
|
|
|
function TPkgManager.PackageEditorViewPkgSource(Sender: TObject;
|
|
APackage: TLazPackage): TModalResult;
|
|
begin
|
|
Result:=DoOpenPackageSource(APackage);
|
|
end;
|
|
|
|
procedure TPkgManager.PackageGraphBeginUpdate(Sender: TObject);
|
|
begin
|
|
if PackageGraphExplorer<>nil then PackageGraphExplorer.BeginUpdate;
|
|
end;
|
|
|
|
procedure TPkgManager.PackageGraphChangePackageName(APackage: TLazPackage;
|
|
const OldName: string);
|
|
begin
|
|
if PackageGraphExplorer<>nil then
|
|
PackageGraphExplorer.UpdatePackageName(APackage,OldName);
|
|
end;
|
|
|
|
procedure TPkgManager.PackageGraphDeletePackage(APackage: TLazPackage);
|
|
begin
|
|
if APackage.Editor<>nil then begin
|
|
APackage.Editor.Hide;
|
|
APackage.Editor.Free;
|
|
end;
|
|
end;
|
|
|
|
procedure TPkgManager.PackageGraphDependencyModified(ADependency: TPkgDependency);
|
|
var
|
|
DepOwner: TObject;
|
|
begin
|
|
DepOwner:=ADependency.Owner;
|
|
if DepOwner is TLazPackage then
|
|
TLazPackage(DepOwner).Modified:=true
|
|
else if DepOwner is TProject then
|
|
TProject(DepOwner).Modified:=true;
|
|
end;
|
|
|
|
function TPkgManager.PackageGraphExplorerOpenPackage(Sender: TObject;
|
|
APackage: TLazPackage): TModalResult;
|
|
begin
|
|
Result:=DoOpenPackage(APackage,[pofAddToRecent],false);
|
|
end;
|
|
|
|
function TPkgManager.PackageGraphExplorerOpenProject(Sender: TObject;
|
|
AProject: TProject): TModalResult;
|
|
begin
|
|
if AProject<>Project1 then exit(mrCancel);
|
|
MainIDE.DoShowProjectInspector;
|
|
Result:=mrOk;
|
|
end;
|
|
|
|
procedure TPkgManager.PackageGraphAddPackage(Pkg: TLazPackage);
|
|
begin
|
|
if FileExistsUTF8(Pkg.FileName) then LazPackageLinks.AddUserLink(Pkg);
|
|
if PackageGraphExplorer<>nil then
|
|
PackageGraphExplorer.UpdatePackageAdded(Pkg);
|
|
end;
|
|
|
|
procedure TPkgManager.PackageGraphEndUpdate(Sender: TObject; GraphChanged: boolean);
|
|
begin
|
|
if GraphChanged then IncreaseCompilerParseStamp;
|
|
if PackageGraphExplorer<>nil then begin
|
|
if GraphChanged then PackageGraphExplorer.UpdateAll;
|
|
PackageGraphExplorer.EndUpdate;
|
|
end;
|
|
if GraphChanged then begin
|
|
if PackageEditors<>nil then
|
|
PackageEditors.UpdateAllEditors(false);
|
|
if ProjInspector<>nil then
|
|
ProjInspector.UpdateRequiredPackages;
|
|
DoCallNotifyHandler(pihtGraphChanged,Self);
|
|
end;
|
|
end;
|
|
|
|
procedure TPkgManager.PackageGraphFindFPCUnit(const AUnitName,
|
|
Directory: string; var Filename: string);
|
|
begin
|
|
if (Directory<>'') and not FilenameIsAbsolute(Directory) then
|
|
RaiseGDBException(Directory);
|
|
//DebugLn('TPkgManager.PackageGraphFindFPCUnit "',Directory,'"');
|
|
Filename:=CodeToolBoss.DirectoryCachePool.FindUnitInUnitLinks(Directory, AUnitName);
|
|
end;
|
|
|
|
function TPkgManager.PackageGraphExplorerUninstallPackage(Sender: TObject;
|
|
APackage: TLazPackage): TModalResult;
|
|
begin
|
|
Result:=DoUninstallPackage(APackage,[],false);
|
|
end;
|
|
|
|
procedure TPkgManager.MainIDEitmPkgNewPackageClick(Sender: TObject);
|
|
begin
|
|
DoNewPackage;
|
|
end;
|
|
|
|
procedure TPkgManager.MainIDEitmPkgOpenLoadedPackageClicked(Sender: TObject);
|
|
begin
|
|
DoShowLoadedPkgDlg;
|
|
end;
|
|
|
|
procedure TPkgManager.MainIDEitmPackageLinksClicked(Sender: TObject);
|
|
begin
|
|
ShowPackageLinks;
|
|
end;
|
|
|
|
procedure TPkgManager.MainIDEitmOpenRecentPackageClicked(Sender: TObject);
|
|
|
|
procedure UpdateEnvironment;
|
|
begin
|
|
SetRecentPackagesMenu;
|
|
MainIDE.SaveEnvironment;
|
|
end;
|
|
|
|
var
|
|
AFilename: string;
|
|
begin
|
|
// Hint holds the full filename, Caption may have a shortened form.
|
|
AFileName:=(Sender as TIDEMenuItem).Hint;
|
|
if DoOpenPackageFile(AFilename,[pofAddToRecent],false)=mrOk then begin
|
|
UpdateEnvironment;
|
|
end else begin
|
|
// open failed
|
|
if not FileExistsUTF8(AFilename) then begin
|
|
// file does not exist -> delete it from recent file list
|
|
EnvironmentOptions.RemoveFromRecentPackageFiles(AFilename);
|
|
UpdateEnvironment;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TPkgManager.ApplicationIdleHandler(Sender: TObject; var Done: Boolean);
|
|
begin
|
|
if PackageGraph = nil then Exit;
|
|
if MainIDE.ToolStatus<>itNone then exit;
|
|
if (Screen.ActiveCustomForm<>nil)
|
|
and (fsModal in Screen.ActiveCustomForm.FormState) then exit;
|
|
PackageGraph.CloseUnneededPackages;
|
|
end;
|
|
|
|
function TPkgManager.DoShowSavePackageAsDialog(APackage: TLazPackage): TModalResult;
|
|
var
|
|
OldPkgFilename: String;
|
|
SaveDialog: TSaveDialog;
|
|
NewFileName: String;
|
|
NewPkgName: String;
|
|
ConflictPkg: TLazPackage;
|
|
PkgFile: TPkgFile;
|
|
LowerFilename: String;
|
|
BrokenDependencies: TFPList;
|
|
RenameDependencies: Boolean;
|
|
OldPkgName: String;
|
|
NewMainUnitFileName: String;
|
|
|
|
procedure RenamePackageInProject;
|
|
var
|
|
AProject: TProject;
|
|
OldUnitName: String;
|
|
NewUnitName: String;
|
|
begin
|
|
AProject:=Project1;
|
|
if (pfMainUnitIsPascalSource in AProject.Flags)
|
|
and (AProject.MainUnitInfo<>nil) then begin
|
|
OldUnitName:=OldPkgName;
|
|
NewUnitName:=APackage.Name;
|
|
if (OldUnitName<>NewUnitName) then begin
|
|
MainIDE.SaveSourceEditorChangesToCodeCache(nil);
|
|
if CodeToolBoss.RenameUsedUnit(
|
|
AProject.MainUnitInfo.Source,OldUnitName,NewUnitName,'')
|
|
then
|
|
AProject.MainUnitInfo.Modified:=true;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
begin
|
|
OldPkgFilename:=APackage.Filename;
|
|
OldPkgName:=APackage.Name;
|
|
|
|
SaveDialog:=TSaveDialog.Create(nil);
|
|
try
|
|
InputHistories.ApplyFileDialogSettings(SaveDialog);
|
|
SaveDialog.Title:=Format(lisPkgMangSavePackageLpk, [APackage.IDAsString]);
|
|
SaveDialog.Filter:=dlgFilterLazarusPackage+' (*.lpk)|*.lpk'
|
|
+'|'+dlgFilterAll+' ('+FileMask+')|'+FileMask;
|
|
if APackage.HasDirectory then
|
|
SaveDialog.InitialDir:=APackage.Directory;
|
|
|
|
// build a nice package filename suggestion
|
|
NewFileName:=APackage.Name+'.lpk';
|
|
SaveDialog.FileName:=NewFileName;
|
|
|
|
repeat
|
|
Result:=mrCancel;
|
|
|
|
if not SaveDialog.Execute then begin
|
|
// user cancels
|
|
Result:=mrCancel;
|
|
exit;
|
|
end;
|
|
NewFileName:=CleanAndExpandFilename(SaveDialog.Filename);
|
|
NewPkgName:=ExtractFileNameOnly(NewFilename);
|
|
if APackage.MainUnitHasPkgName then
|
|
NewMainUnitFileName:=ChangeFileExt(NewFileName,'.pas')
|
|
else
|
|
NewMainUnitFileName:='';
|
|
|
|
if PackageEditors.FindEditor(NewPkgName) <> nil then
|
|
begin
|
|
Result:=IDEMessageDialog(lisPkgMangInvalidPackageName,
|
|
Format(lisPkgMangSaveAsAlreadyOpenedPackage, [NewPkgName]),
|
|
mtInformation,[mbRetry,mbAbort]);
|
|
if Result=mrAbort then exit;
|
|
continue; // try again
|
|
end;
|
|
|
|
// check file extension
|
|
if ExtractFileExt(NewFilename)='' then begin
|
|
// append extension
|
|
NewFileName:=NewFileName+'.lpk';
|
|
end
|
|
else if not FilenameExtIs(NewFilename,'.lpk',true) then begin
|
|
Result:=IDEMessageDialog(lisPkgMangInvalidPackageFileExtension,
|
|
lisPkgMangPackagesMustHaveTheExtensionLpk,
|
|
mtInformation,[mbRetry,mbAbort]);
|
|
if Result=mrAbort then exit;
|
|
continue; // try again
|
|
end;
|
|
|
|
// check filename
|
|
if not IsValidPkgName(NewPkgName) then begin
|
|
Result:=IDEMessageDialog(lisPkgMangInvalidPackageName,
|
|
Format(lisPkgMangThePackageNameIsNotAValidPackageNamePleaseChooseAn,
|
|
[NewPkgName, LineEnding]),
|
|
mtInformation,[mbRetry,mbAbort]);
|
|
if Result=mrAbort then exit;
|
|
continue; // try again
|
|
end;
|
|
|
|
// apply naming conventions
|
|
|
|
if lowercase(NewPkgName) <> NewPkgName then
|
|
begin
|
|
LowerFilename:=ExtractFilePath(NewFilename)+lowercase(ExtractFileName(NewFilename));
|
|
case EnvironmentOptions.CharcaseFileAction of
|
|
ccfaAsk:
|
|
if IDEMessageDialog(lisPkgMangRenameFileLowercase,
|
|
Format(lisPkgMangShouldTheFileRenamedLowercaseTo,[LineEnding, LowerFilename]),
|
|
mtConfirmation,[mbYes,mbNo])=mrYes
|
|
then
|
|
NewFileName:=LowerFilename;
|
|
ccfaAutoRename: NewFileName:=LowerFilename;
|
|
ccfaIgnore: ;
|
|
end;
|
|
end;
|
|
|
|
// check unit name conflict
|
|
if NewMainUnitFileName<>'' then
|
|
begin
|
|
PkgFile:=APackage.FindUnit(NewPkgName);
|
|
if PkgFile<>nil then begin
|
|
Result:=IDEMessageDialog(lisNameConflict,
|
|
lisThePackageAlreadyContainsAUnitWithThisName,
|
|
mtWarning,[mbRetry,mbAbort]);
|
|
if Result=mrAbort then exit;
|
|
continue; // try again
|
|
end;
|
|
end;
|
|
|
|
// check package name conflict
|
|
ConflictPkg:=PackageGraph.FindPackageWithName(NewPkgName,APackage);
|
|
if ConflictPkg<>nil then begin
|
|
Result:=IDEMessageDialog(lisPkgMangPackageNameAlreadyExists,
|
|
Format(lisPkgMangThereIsAlreadyAnotherPackageWithTheName,
|
|
[NewPkgName, LineEnding, ConflictPkg.IDAsString, LineEnding, ConflictPkg.Filename]),
|
|
mtInformation,[mbRetry,mbAbort,mbIgnore]);
|
|
if Result=mrAbort then exit;
|
|
if Result<>mrIgnore then continue; // try again
|
|
end;
|
|
|
|
// check file name conflict with project
|
|
if (NewMainUnitFileName<>'')
|
|
and (Project1.ProjectUnitWithFilename(NewMainUnitFileName)<>nil) then begin
|
|
Result:=IDEMessageDialog(lisPkgMangFilenameIsUsedByProject,
|
|
Format(lisPkgMangTheFileNameIsPartOfTheCurrentProject,[NewFilename,LineEnding]),
|
|
mtInformation,[mbRetry,mbAbort]);
|
|
if Result=mrAbort then exit;
|
|
continue; // try again
|
|
end;
|
|
|
|
// check file name conflicts with files in other packages
|
|
if (NewMainUnitFileName<>'') then
|
|
begin
|
|
PkgFile:=PackageGraph.FindFileInAllPackages(NewMainUnitFileName,true,false);
|
|
if PkgFile<>nil then begin
|
|
Result:=IDEMessageDialog(lisPkgMangFilenameIsUsedByOtherPackage,
|
|
Format(lisPkgMangTheFileNameIsUsedByThePackageInFile, [NewFilename, LineEnding,
|
|
PkgFile.LazPackage.IDAsString, LineEnding, PkgFile.LazPackage.Filename]),
|
|
mtWarning,[mbRetry,mbAbort]);
|
|
if Result=mrAbort then exit;
|
|
continue; // try again
|
|
end;
|
|
end;
|
|
|
|
// check for broken dependencies
|
|
BrokenDependencies:=PackageGraph.GetBrokenDependenciesWhenChangingPkgID(
|
|
APackage,NewPkgName,APackage.Version);
|
|
RenameDependencies:=false;
|
|
try
|
|
if BrokenDependencies.Count>0 then begin
|
|
Result:=ShowBrokenDependencies(BrokenDependencies);
|
|
if Result=mrOK then // = Yes
|
|
RenameDependencies:=true
|
|
else if Result<>mrClose then // <> Ignore
|
|
exit;
|
|
end;
|
|
finally
|
|
BrokenDependencies.Free;
|
|
end;
|
|
|
|
// check existing file
|
|
if (CompareFilenames(NewFileName,OldPkgFilename)<>0) then
|
|
begin
|
|
if FileExistsUTF8(NewFileName) then begin
|
|
Result:=IDEMessageDialog(lisPkgMangReplaceFile,
|
|
Format(lisPkgMangReplaceExistingFile, [NewFilename]),
|
|
mtConfirmation,[mbOk,mbCancel]);
|
|
if Result<>mrOk then exit;
|
|
end;
|
|
if FileExistsUTF8(NewMainUnitFileName) then
|
|
begin
|
|
Result:=IDEMessageDialog(lisPkgMangReplaceFile,
|
|
Format(lisPkgMangReplaceExistingFile, [NewFilename]),
|
|
mtConfirmation,[mbOk,mbCancel]);
|
|
if Result<>mrOk then exit;
|
|
end;
|
|
end;
|
|
|
|
// check if new file is read/writable
|
|
Result:=CheckCreatingFile(NewFileName,true);
|
|
if Result=mrAbort then exit;
|
|
|
|
until Result<>mrRetry;
|
|
finally
|
|
InputHistories.StoreFileDialogSettings(SaveDialog);
|
|
SaveDialog.Free;
|
|
end;
|
|
|
|
// set filename
|
|
APackage.Filename:=NewFilename;
|
|
if Assigned(APackage.Editor) then
|
|
APackage.Editor.LazPackage := APackage;//force package editor name change!
|
|
|
|
// rename package
|
|
PackageGraph.ChangePackageID(APackage,NewPkgName,APackage.Version,
|
|
RenameDependencies,true);
|
|
SaveAutoInstallDependencies;
|
|
RenamePackageInProject;
|
|
|
|
//update LastOpenPackages list
|
|
EnvironmentOptions.LastOpenPackages.Remove(OldPkgFilename);
|
|
EnvironmentOptions.LastOpenPackages.Add(NewFileName);
|
|
MainIDE.SaveEnvironment;
|
|
|
|
// clean up old package file to reduce ambiguousities
|
|
if FileExistsUTF8(OldPkgFilename)
|
|
and (CompareFilenames(OldPkgFilename,NewFilename)<>0) then begin
|
|
if IDEMessageDialog(lisPkgMangDeleteOldPackageFile,
|
|
Format(lisPkgMangDeleteOldPackageFile2, [OldPkgFilename]),
|
|
mtConfirmation,[mbYes,mbNo])=mrYes
|
|
then begin
|
|
if DeleteFileUTF8(OldPkgFilename) then begin
|
|
RemoveFromRecentList(OldPkgFilename,
|
|
EnvironmentOptions.RecentPackageFiles,rltFile);
|
|
end else begin
|
|
IDEMessageDialog(lisPkgMangDeleteFailed,
|
|
Format(lisPkgMangUnableToDeleteFile, [OldPkgFilename]), mtError, [mbOk]);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
// success
|
|
Result:=mrOk;
|
|
end;
|
|
|
|
function TPkgManager.CheckPackageGraphForCompilation(APackage: TLazPackage;
|
|
FirstDependency: TPkgDependency; const Directory: string; ShowAbort: boolean
|
|
): TModalResult;
|
|
var
|
|
PathList: TFPList;
|
|
Dependency: TPkgDependency;
|
|
PkgFile1,PkgFile2: TPkgFile;
|
|
ConflictPkg: TLazPackage;
|
|
s: String;
|
|
Btns: TMsgDlgButtons;
|
|
PkgList: TFPList;
|
|
i: Integer;
|
|
begin
|
|
{$IFDEF VerbosePkgCompile}
|
|
debugln('TPkgManager.CheckPackageGraphForCompilation A');
|
|
{$ENDIF}
|
|
if ShowAbort
|
|
then Btns := [mbCancel] // will be replaced to Ignore
|
|
else Btns := [mbOK];
|
|
PathList:=nil;
|
|
PkgList:=nil;
|
|
try
|
|
// check for unsaved packages
|
|
PathList:=PackageGraph.FindUnsavedDependencyPath(APackage,FirstDependency);
|
|
if PathList<>nil then begin
|
|
DoShowPackageGraphPathList(PathList);
|
|
Result:=IDEMessageDialogAb(lisPkgMangUnsavedPackage,
|
|
lisPkgMangThereIsAnUnsavedPackageInTheRequiredPackages,
|
|
mtError,[mbCancel],ShowAbort);
|
|
exit;
|
|
end;
|
|
|
|
// check for broken dependencies
|
|
PathList:=PackageGraph.FindBrokenDependencyPath(APackage,FirstDependency);
|
|
if PathList<>nil then begin
|
|
if (PathList.Count=1) then begin
|
|
Dependency:=TPkgDependency(PathList[0]);
|
|
if Dependency is TPkgDependency then begin
|
|
// check if project
|
|
if Dependency.Owner is TProject then begin
|
|
MainIDE.DoShowProjectInspector;
|
|
Result:=IDEMessageDialogAb(lisPkgMangBrokenDependency,
|
|
Format(lisPkgMangTheProjectRequiresThePackageButItWasNotFound,
|
|
[Dependency.AsString, LineEnding]),
|
|
mtError,Btns,ShowAbort);
|
|
if not ShowAbort then
|
|
Result := mrCancel; // User confirmed error, implicitly cancel the action
|
|
exit;
|
|
end;
|
|
end;
|
|
end;
|
|
DoShowPackageGraphPathList(PathList);
|
|
Result:=IDEMessageDialogAb(lisPkgMangBrokenDependency,
|
|
lisPkgMangRequiredPackagesWereNotFound,
|
|
mtError,Btns,ShowAbort);
|
|
if not ShowAbort then
|
|
Result := mrCancel; // User confirmed error, implicitly cancel the action
|
|
exit;
|
|
end;
|
|
|
|
// check for cycle dependencies
|
|
PathList:=PackageGraph.FindCycleDependencyPath(APackage,FirstDependency);
|
|
if PathList<>nil then begin
|
|
DoShowPackageGraphPathList(PathList);
|
|
Result:=IDEMessageDialogAb(lisPkgMangCircularDependencies,
|
|
lisPkgMangThereIsACircularDependency,
|
|
mtError,Btns,ShowAbort);
|
|
if not ShowAbort then
|
|
Result := mrCancel; // User confirmed error, implicitly cancel the action
|
|
exit;
|
|
end;
|
|
|
|
// check for all used package with wrong
|
|
PackageGraph.GetAllRequiredPackages(APackage,FirstDependency,PkgList);
|
|
if (PkgList<>nil) then begin
|
|
for i:=0 to PkgList.Count-1 do begin
|
|
Result:=CheckUserSearchPaths(TLazPackage(PkgList[i]).CompilerOptions);
|
|
if Result<>mrOk then
|
|
exit(mrCancel);
|
|
end;
|
|
end;
|
|
|
|
// check for a package that compiles to the default FPC search path
|
|
PathList:=PackageGraph.FindPkgOutputInFPCSearchPath(APackage,FirstDependency);
|
|
if PathList<>nil then begin
|
|
ConflictPkg:=TObject(PathList[PathList.Count-1]) as TLazPackage;
|
|
DoShowPackageGraphPathList(PathList);
|
|
Result:=IDEMessageDialogAb(lisPkgMangCircularDependencies,
|
|
Format(lisPkgMangThePackageIsCompiledAutomaticallyAndItsOutputDirec,
|
|
[ConflictPkg.Name, ConflictPkg.GetOutputDirectory, LineEnding+LineEnding, LineEnding]),
|
|
mtError,Btns,ShowAbort);
|
|
if not ShowAbort then
|
|
Result := mrCancel; // User confirmed error, implicitly cancel the action
|
|
exit;
|
|
end;
|
|
|
|
// check for ambiguous units between packages
|
|
if PackageGraph.FindAmbiguousUnits(APackage,FirstDependency,
|
|
PkgFile1,PkgFile2,ConflictPkg)
|
|
then begin
|
|
if (PkgFile1<>nil) and (PkgFile2<>nil) then begin
|
|
s:=Format(lisPkgMangThereAreTwoUnitsWithTheSameName1From2From,
|
|
[LineEnding+LineEnding, PkgFile1.Filename, PkgFile1.LazPackage.IDAsString,
|
|
LineEnding, PkgFile2.Filename, PkgFile2.LazPackage.IDAsString]) + LineEnding;
|
|
end else if (PkgFile1<>nil) and (ConflictPkg<>nil) then begin
|
|
s:=Format(lisPkgMangThereIsAUnitWithTheSameNameAsAPackage1From2,
|
|
[LineEnding+LineEnding, PkgFile1.Filename, PkgFile1.LazPackage.IDAsString,
|
|
LineEnding, ConflictPkg.IDAsString]) + LineEnding;
|
|
end else
|
|
s:='Internal inconsistency FindAmbiguousUnits: '
|
|
+'Please report this bug and how you got here.'+LineEnding;
|
|
Result:=IDEMessageDialogAb(lisPkgMangAmbiguousUnitsFound, Format(
|
|
lisPkgMangBothPackagesAreConnectedThisMeansEitherOnePackageU, [s]),
|
|
mtError,Btns,ShowAbort);
|
|
if not ShowAbort then
|
|
Result := mrCancel; // User confirmed error, implicitly cancel the action
|
|
exit;
|
|
end;
|
|
|
|
// check for ambiguous units between packages and FPC units
|
|
if PackageGraph.FindFPCConflictUnit(APackage,FirstDependency,Directory,
|
|
@PackageGraphFindFPCUnit,PkgFile1,ConflictPkg)
|
|
then begin
|
|
if (ConflictPkg<>nil) then begin
|
|
s:=Format(lisPkgMangThereIsAFPCUnitWithTheSameNameAsAPackage,
|
|
[LineEnding+LineEnding, ConflictPkg.IDAsString]) + LineEnding;
|
|
end else if (PkgFile1<>nil) then begin
|
|
s:=Format(lisPkgMangThereIsAFPCUnitWithTheSameNameFrom,
|
|
[LineEnding+LineEnding, PkgFile1.Filename, PkgFile1.LazPackage.IDAsString]) + LineEnding;
|
|
end else
|
|
s:='Internal inconsistency FindFPCConflictUnits: '
|
|
+'Please report this bug and how you got here.'+LineEnding;
|
|
Result:=IDEMessageDialogAb(lisPkgMangAmbiguousUnitsFound, s,
|
|
mtError,Btns,ShowAbort);
|
|
if not ShowAbort then
|
|
Result := mrCancel; // User confirmed error, implicitly cancel the action
|
|
exit;
|
|
end;
|
|
|
|
finally
|
|
PkgList.Free;
|
|
PathList.Free;
|
|
end;
|
|
|
|
{$IFDEF VerbosePkgCompile}
|
|
debugln('TPkgManager.CheckPackageGraphForCompilation END');
|
|
{$ENDIF}
|
|
Result:=mrOk;
|
|
end;
|
|
|
|
procedure TPkgManager.SaveAutoInstallDependencies;
|
|
var
|
|
Dependency: TPkgDependency;
|
|
sl: TStringListUTF8Fast;
|
|
begin
|
|
sl:=TStringListUTF8Fast.Create;
|
|
Dependency:=PackageGraph.FirstInstallDependency;
|
|
while Dependency<>nil do begin
|
|
if (Dependency.LoadPackageResult=lprSuccess)
|
|
and (not Dependency.RequiredPackage.Missing)
|
|
and (not PackageGraph.IsCompiledInBasePackage(Dependency.PackageName))
|
|
and (not (Dependency.RequiredPackage.PackageType in [lptRunTime,lptRunTimeOnly]))
|
|
then begin
|
|
if sl.IndexOf(Dependency.PackageName)<0 then begin
|
|
sl.Add(Dependency.PackageName);
|
|
//DebugLn('TPkgManager.SaveAutoInstallDependencies A ',Dependency.PackageName);
|
|
end;
|
|
end;
|
|
Dependency:=Dependency.NextRequiresDependency;
|
|
end;
|
|
MiscellaneousOptions.BuildLazProfiles.StaticAutoInstallPackages.Assign(sl);
|
|
MiscellaneousOptions.Save;
|
|
sl.Free;
|
|
end;
|
|
|
|
procedure TPkgManager.LoadStaticCustomPackages;
|
|
var
|
|
StaticPackages: TFPList;
|
|
StaticPackage: PRegisteredPackage;
|
|
i: Integer;
|
|
APackage: TLazPackage;
|
|
Quiet: Boolean;
|
|
begin
|
|
StaticPackages:=LazarusPackageIntf.RegisteredPackages;
|
|
if StaticPackages=nil then exit;
|
|
Quiet:=GetSkipCheck(skcMissingPackageFile) or GetSkipCheck(skcAll);
|
|
PackageGraph.AbortRegistration:=false;
|
|
// register components in Lazarus packages
|
|
for i:=0 to StaticPackages.Count-1 do begin
|
|
StaticPackage:=PRegisteredPackage(StaticPackages[i]);
|
|
//debugln(['TPkgManager.LoadStaticCustomPackages ',StaticPackage^.Name]);
|
|
// check package name
|
|
if not IsValidPkgName(StaticPackage^.Name) then begin
|
|
DebugLn('Warning: (lazarus) [TPkgManager.LoadStaticCustomPackages] Invalid Package Name: "',
|
|
BinaryStrToText(StaticPackage^.Name),'"');
|
|
continue;
|
|
end;
|
|
// check RegisterFCLBaseComponents procedure
|
|
if (StaticPackage^.RegisterProc=nil) then begin
|
|
DebugLn('Warning: (lazarus) [TPkgManager.LoadStaticCustomPackages]',
|
|
' Package "',StaticPackage^.Name,'" has no register procedure.');
|
|
continue;
|
|
end;
|
|
// load package
|
|
APackage:=LoadInstalledPackage(StaticPackage^.Name,KeepInstalledPackages,Quiet);
|
|
PackageGraph.RegisterStaticPackage(APackage,StaticPackage^.RegisterProc);
|
|
end;
|
|
PackageGraph.SortAutoInstallDependencies;
|
|
ClearRegisteredPackages;
|
|
end;
|
|
|
|
function TPkgManager.LoadInstalledPackage(const PackageName: string;
|
|
AddToAutoInstall: boolean; var Quiet: boolean): TLazPackage;
|
|
var
|
|
NewDependency: TPkgDependency;
|
|
PackageList: TStringList;
|
|
begin
|
|
//DebugLn('TPkgManager.LoadInstalledPackage PackageName="',PackageName,'" Quiet=',Quiet);
|
|
NewDependency:=TPkgDependency.Create;
|
|
NewDependency.Owner:=Self;
|
|
NewDependency.DependencyType:=pdtLazarus;
|
|
NewDependency.PackageName:=PackageName;
|
|
PackageGraph.OpenInstalledDependency(NewDependency,pitStatic,Quiet);
|
|
Result:=NewDependency.RequiredPackage;
|
|
if AddToAutoInstall and (Result<>nil) then begin
|
|
if FindDependencyByNameInList(
|
|
PackageGraph.FirstInstallDependency,pddRequires,PackageName)=nil
|
|
then begin
|
|
NewDependency.RequiredPackage.AutoInstall:=pitStatic;
|
|
NewDependency.AddToList(PackageGraph.FirstInstallDependency,pddRequires)
|
|
end else
|
|
NewDependency.Free;
|
|
PackageList:=MiscellaneousOptions.BuildLazProfiles.StaticAutoInstallPackages;
|
|
if PackageList.IndexOf(PackageName)<0 then
|
|
PackageList.Add(PackageName);
|
|
end else begin
|
|
NewDependency.Free;
|
|
end;
|
|
end;
|
|
|
|
procedure TPkgManager.LoadAutoInstallPackages;
|
|
begin
|
|
FLastLazarusSrcDir:=EnvironmentOptions.GetParsedLazarusDirectory;
|
|
PackageGraph.LoadAutoInstallPackages(
|
|
MiscellaneousOptions.BuildLazProfiles.StaticAutoInstallPackages);
|
|
end;
|
|
|
|
procedure TPkgManager.AddUnitToProjectMainUsesSection(AProject: TProject;
|
|
const AnUnitName, AnUnitInFilename: string);
|
|
begin
|
|
// add unit to project main source file
|
|
if (pfMainUnitIsPascalSource in AProject.Flags)
|
|
and (pfMainUnitHasUsesSectionForAllUnits in AProject.Flags)
|
|
and (AProject.MainUnitInfo<>nil) then begin
|
|
//debugln('TPkgManager.AddUnitToProjectMainUsesSection B ',AnUnitName);
|
|
if (AnUnitName<>'') then begin
|
|
MainIDE.SaveSourceEditorChangesToCodeCache(nil);
|
|
if CodeToolBoss.AddUnitToMainUsesSectionIfNeeded(
|
|
AProject.MainUnitInfo.Source,AnUnitName,AnUnitInFilename)
|
|
then
|
|
AProject.MainUnitInfo.Modified:=true;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
function TPkgManager.CheckDrag(Sender, Source: TObject; X, Y: Integer; out
|
|
SrcFilesEdit, TargetFilesEdit: IFilesEditorInterface; out aFileCount,
|
|
aDependencyCount, aDirectoryCount: integer; out TargetTVNode: TTreeNode; out
|
|
TargetTVType: TTreeViewInsertMarkType): boolean;
|
|
|
|
function GetFilesEditIntf(o: TObject): IFilesEditorInterface;
|
|
var
|
|
PkgEdit: TPackageEditorForm;
|
|
aProjInsp: TProjectInspectorForm;
|
|
begin
|
|
Result:=nil;
|
|
if o is TTreeView then begin
|
|
PkgEdit:=PackageEditors.TreeViewToPkgEditor(TTreeView(o));
|
|
if PkgEdit<>nil then
|
|
Result:=PkgEdit
|
|
else begin
|
|
aProjInsp:=ProjInspector.TreeViewToInspector(TTreeView(o));
|
|
if aProjInsp<>nil then
|
|
Result:=aProjInsp;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
var
|
|
i: Integer;
|
|
TVNode: TTreeNode;
|
|
NodeData: TPENodeData;
|
|
Item: TObject;
|
|
Directory: String;
|
|
TV: TTreeView;
|
|
begin
|
|
Result:=false;
|
|
SrcFilesEdit:=nil;
|
|
TargetFilesEdit:=nil;
|
|
aFileCount:=0;
|
|
aDependencyCount:=0;
|
|
aDirectoryCount:=0;
|
|
TargetTVNode:=nil;
|
|
TargetTVType:=tvimNone;
|
|
|
|
// get source
|
|
SrcFilesEdit:=GetFilesEditIntf(Source);
|
|
if (SrcFilesEdit=nil) then begin
|
|
{$IFDEF VerbosePkgEditDrag}
|
|
debugln(['TPkgManager.CheckDrag failed: unknown src=',DbgSName(Source)]);
|
|
{$ENDIF}
|
|
exit;
|
|
end;
|
|
if SrcFilesEdit.FilesOwnerReadOnly
|
|
or (not FilenameIsAbsolute(SrcFilesEdit.FilesBaseDirectory)) then begin
|
|
{$IFDEF VerbosePkgEditDrag}
|
|
debugln(['TPkgManager.CheckDrag failed: src=',DbgSName(SrcFilesEdit.FilesOwner),' readonly=',SrcFilesEdit.FilesOwnerReadOnly,' basedir=',SrcFilesEdit.FilesBaseDirectory]);
|
|
{$ENDIF}
|
|
exit;
|
|
end;
|
|
|
|
// get target
|
|
TargetFilesEdit:=GetFilesEditIntf(Sender);
|
|
if (TargetFilesEdit=nil) then begin
|
|
{$IFDEF VerbosePkgEditDrag}
|
|
debugln(['TPkgManager.CheckDrag failed: unknown target=',DbgSName(Sender)]);
|
|
{$ENDIF}
|
|
exit;
|
|
end;
|
|
if TargetFilesEdit.FilesOwnerReadOnly
|
|
or (not FilenameIsAbsolute(TargetFilesEdit.FilesBaseDirectory)) then begin
|
|
{$IFDEF VerbosePkgEditDrag}
|
|
debugln(['TPkgManager.CheckDrag failed: target=',DbgSName(TargetFilesEdit.FilesOwner),' readonly=',SrcFilesEdit.FilesOwnerReadOnly,' basedir=',SrcFilesEdit.FilesBaseDirectory]);
|
|
{$ENDIF}
|
|
exit;
|
|
end;
|
|
|
|
//debugln(['TPkgManager.CheckDrag Src=',SrcFilesEdit.FilesOwnerName,' Target=',TargetFilesEdit.FilesOwnerName]);
|
|
|
|
// check items
|
|
aFileCount:=0;
|
|
aDependencyCount:=0;
|
|
aDirectoryCount:=0;
|
|
for i:=0 to SrcFilesEdit.FilesEditTreeView.SelectionCount-1 do begin
|
|
TVNode:=SrcFilesEdit.FilesEditTreeView.Selections[i];
|
|
if SrcFilesEdit.GetNodeDataItem(TVNode,NodeData,Item) then begin
|
|
if NodeData.Removed then exit; // removed things cannot be moved
|
|
if Item is TIDEOwnedFile then begin
|
|
if (Item is TUnitInfo) and (TUnitInfo(Item)=TUnitInfo(Item).Project.MainUnitInfo)
|
|
then
|
|
continue;
|
|
inc(aFileCount);
|
|
end else if Item is TPkgDependency then begin
|
|
inc(aDependencyCount);
|
|
end;
|
|
end else if SrcFilesEdit.IsDirectoryNode(TVNode) then begin
|
|
inc(aDirectoryCount);
|
|
end;
|
|
end;
|
|
if aFileCount+aDependencyCount+aDirectoryCount=0 then begin
|
|
{$IFDEF VerbosePkgEditDrag}
|
|
debugln(['TPkgManager.CheckDrag failed: nothing useful dragged']);
|
|
{$ENDIF}
|
|
exit;
|
|
end;
|
|
if aDirectoryCount>0 then begin
|
|
{$IFDEF VerbosePkgEditDrag}
|
|
debugln(['TPkgManager.CheckDrag failed: move directory is not implemented']);
|
|
{$ENDIF}
|
|
exit;
|
|
end;
|
|
if aDependencyCount>0 then begin
|
|
{$IFDEF VerbosePkgEditDrag}
|
|
debugln(['TPkgManager.CheckDrag failed: move dependency is not implemented']);
|
|
{$ENDIF}
|
|
exit;
|
|
end;
|
|
if Sign(aFileCount)+Sign(aDependencyCount)+Sign(aDirectoryCount)>1 then begin
|
|
// more than one type, but only one type can be dragged
|
|
{$IFDEF VerbosePkgEditDrag}
|
|
debugln(['TPkgManager.CheckDrag failed: more than one type Files=',aFileCount,' Deps=',aDependencyCount,' Dirs=',aDirectoryCount]);
|
|
{$ENDIF}
|
|
exit;
|
|
end;
|
|
|
|
TV:=TargetFilesEdit.FilesEditTreeView;
|
|
TargetTVNode:=TV.GetNodeAt(X,Y);
|
|
if TargetTVNode=nil then begin
|
|
if aDependencyCount>0 then begin
|
|
TargetTVNode:=TargetFilesEdit.TVNodeRequiredPackages;
|
|
end else begin
|
|
TargetTVNode:=TargetFilesEdit.TVNodeFiles;
|
|
end;
|
|
TargetTVType:=tvimAsFirstChild;
|
|
end;
|
|
if TargetFilesEdit.GetNodeDataItem(TargetTVNode,NodeData,Item) then begin
|
|
// move to specific position is not yet supported
|
|
// => redirect to parent nodes
|
|
repeat
|
|
TargetTVNode:=TargetTVNode.Parent;
|
|
if TargetTVNode=nil then
|
|
exit;
|
|
until (TargetTVNode=TargetFilesEdit.TVNodeFiles)
|
|
or (TargetTVNode=TargetFilesEdit.TVNodeRequiredPackages)
|
|
or TargetFilesEdit.IsDirectoryNode(TargetTVNode);
|
|
TargetTVType:=tvimAsFirstChild;
|
|
end;
|
|
if TargetFilesEdit.IsDirectoryNode(TargetTVNode)
|
|
or (TargetTVNode=TargetFilesEdit.TVNodeFiles)
|
|
then begin
|
|
Directory:=TargetFilesEdit.GetNodeFilename(TargetTVNode);
|
|
if not FilenameIsAbsolute(Directory) then begin
|
|
{$IFDEF VerbosePkgEditDrag}
|
|
debugln(['TPkgManager.CheckDrag: invalid target directory="',Directory,'"']);
|
|
{$ENDIF}
|
|
exit;
|
|
end;
|
|
TargetTVType:=tvimAsFirstChild;
|
|
if aFileCount>0 then begin
|
|
// drag files
|
|
end else if aDirectoryCount>0 then begin
|
|
// drag directory
|
|
{$IFDEF VerbosePkgEditDrag}
|
|
debugln(['TPkgManager.CheckDrag drag directory not implemented yet']);
|
|
{$ENDIF}
|
|
exit;
|
|
end else begin
|
|
{$IFDEF VerbosePkgEditDrag}
|
|
debugln(['TPkgManager.CheckDrag failed: expected files or directory']);
|
|
{$ENDIF}
|
|
exit;
|
|
end;
|
|
end else if TargetTVNode=TargetFilesEdit.TVNodeRequiredPackages then begin
|
|
if aDependencyCount=0 then exit;
|
|
// drag dependencies
|
|
TargetTVType:=tvimAsFirstChild;
|
|
{$IFDEF VerbosePkgEditDrag}
|
|
debugln(['TPkgManager.CheckDrag drag dependencies not implemented yet']);
|
|
{$ENDIF}
|
|
exit;
|
|
end else begin
|
|
{$IFDEF VerbosePkgEditDrag}
|
|
debugln(['TPkgManager.CheckDrag failed: invalid target node: ',TargetTVNode.Text]);
|
|
{$ENDIF}
|
|
exit;
|
|
end;
|
|
|
|
if (SrcFilesEdit=TargetFilesEdit)
|
|
and (TargetTVNode.Selected or TargetTVNode.MultiSelected)
|
|
then begin
|
|
{$IFDEF VerbosePkgEditDrag}
|
|
debugln(['TPkgManager.CheckDrag failed: target is selected']);
|
|
{$ENDIF}
|
|
exit;
|
|
end;
|
|
|
|
Result:=true;
|
|
end;
|
|
|
|
procedure TPkgManager.FilesEditDragDrop(Sender, Source: TObject; X, Y: Integer);
|
|
var
|
|
aFileCount: integer;
|
|
aDependencyCount: integer;
|
|
aDirectoryCount: integer;
|
|
TargetTVNode: TTreeNode;
|
|
TargetTVType: TTreeViewInsertMarkType;
|
|
NodeData: TPENodeData;
|
|
Item: TObject;
|
|
aFile: TIDEOwnedFile;
|
|
Directory: String;
|
|
SrcFilesEdit: IFilesEditorInterface;
|
|
TargetFilesEdit: IFilesEditorInterface;
|
|
begin
|
|
if not CheckDrag(Sender, Source, X, Y, SrcFilesEdit, TargetFilesEdit,
|
|
aFileCount, aDependencyCount, aDirectoryCount, TargetTVNode, TargetTVType)
|
|
then begin
|
|
ShowMessage('drop failed, dragover was wrong');
|
|
exit;
|
|
end;
|
|
|
|
{$IFDEF VerbosePkgEditDrag}
|
|
debugln(['TPkgManager.FilesEditDragDrop START Src=',SrcFilesEdit.FilesOwnerName,' Target=',TargetFilesEdit.FilesOwnerName,' FileCount=',aFileCount,' DepCount=',aDependencyCount,' DirCount=',aDirectoryCount]);
|
|
{$ENDIF}
|
|
if TargetFilesEdit.GetNodeDataItem(TargetTVNode,NodeData,Item) then begin
|
|
if Item is TIDEOwnedFile then begin
|
|
aFile:=TIDEOwnedFile(Item);
|
|
if aFileCount=0 then exit;
|
|
// drag files
|
|
Directory:=ExtractFilePath(aFile.GetFullFilename);
|
|
{$IFDEF VerbosePkgEditDrag}
|
|
debugln(['TPkgManager.FilesEditDragDrop drag files to directory of ',aFile.Filename]);
|
|
{$ENDIF}
|
|
MoveFiles(TargetFilesEdit,SrcFilesEdit,Directory);
|
|
end else if Item is TPkgDependency then begin
|
|
if aDependencyCount=0 then exit;
|
|
// ToDo: drag dependencies
|
|
{$IFDEF VerbosePkgEditDrag}
|
|
debugln(['TPkgManager.FilesEditDragDrop: drag dependencies']);
|
|
{$ENDIF}
|
|
ShowMessage('Not implemented yet: drag dependencies');
|
|
end;
|
|
end else if TargetFilesEdit.IsDirectoryNode(TargetTVNode)
|
|
or (TargetTVNode=TargetFilesEdit.TVNodeFiles)
|
|
then begin
|
|
Directory:=TargetFilesEdit.GetNodeFilename(TargetTVNode);
|
|
if aFileCount>0 then begin
|
|
// drag files
|
|
{$IFDEF VerbosePkgEditDrag}
|
|
debugln(['TPkgManager.FilesEditDragDrop drag files to ',TargetFilesEdit.FilesBaseDirectory]);
|
|
{$ENDIF}
|
|
MoveFiles(TargetFilesEdit,SrcFilesEdit,Directory);
|
|
end else if aDirectoryCount>0 then begin
|
|
// drag directory
|
|
{$IFDEF VerbosePkgEditDrag}
|
|
debugln(['TPkgManager.FilesEditDragDrop: drag directory']);
|
|
{$ENDIF}
|
|
ShowMessage('Not implemented yet: drag directory');
|
|
end else begin
|
|
ShowMessage('I cannot drag that to a directory');
|
|
end;
|
|
end else if TargetTVNode=TargetFilesEdit.TVNodeRequiredPackages then begin
|
|
if aDependencyCount=0 then exit;
|
|
// ToDo: drag dependencies
|
|
{$IFDEF VerbosePkgEditDrag}
|
|
debugln(['TPkgManager.FilesEditDragDrop: drag dependencies']);
|
|
{$ENDIF}
|
|
ShowMessage('Not implemented yet: drag dependencies');
|
|
end else begin
|
|
{$IFDEF VerbosePkgEditDrag}
|
|
if TargetTVNode=nil then
|
|
debugln(['TPkgManager.FilesEditDragDrop TargetTVNode=nil'])
|
|
else
|
|
debugln(['TPkgManager.FilesEditDragDrop TargetTVNode="',TargetTVNode.Text,'"']);
|
|
{$ENDIF}
|
|
end;
|
|
{$IFDEF VerbosePkgEditDrag}
|
|
debugln(['TPkgManager.FilesEditDragDrop END']);
|
|
{$ENDIF}
|
|
end;
|
|
|
|
function TPkgManager.MoveFiles(TargetFilesEdit, SrcFilesEdit: IFilesEditorInterface;
|
|
TargetDirectory: string): boolean;
|
|
var
|
|
Files: TFPList; // list of TPkgFile
|
|
i: Integer;
|
|
TVNode: TTreeNode;
|
|
NodeData: TPENodeData;
|
|
Item: TObject;
|
|
begin
|
|
Result:=false;
|
|
if not FilenameIsAbsolute(TargetDirectory) then begin
|
|
{$IFDEF VerbosePkgEditDrag}
|
|
debugln(['TPkgManager.MoveFiles invalid TargetDirectory=',TargetDirectory]);
|
|
{$ENDIF}
|
|
exit;
|
|
end;
|
|
{$IFDEF VerbosePkgEditDrag}
|
|
debugln(['TPkgManager.MoveFiles Target=',TargetFilesEdit.FilesOwnerName,' Src=',SrcFilesEdit.FilesOwnerName,' Dir="',TargetDirectory,'"']);
|
|
{$ENDIF}
|
|
Files:=TFPList.Create;
|
|
try
|
|
for i:=0 to SrcFilesEdit.FilesEditTreeView.SelectionCount-1 do begin
|
|
TVNode:=SrcFilesEdit.FilesEditTreeView.Selections[i];
|
|
if not SrcFilesEdit.GetNodeDataItem(TVNode, NodeData, Item) then continue;
|
|
if NodeData.Removed then continue;
|
|
if not (Item is TIDEOwnedFile) then continue;
|
|
if (Item is TUnitInfo) and (TUnitInfo(Item)=TUnitInfo(Item).Project.MainUnitInfo)
|
|
then
|
|
continue;
|
|
Files.Add(Item);
|
|
end;
|
|
if Files.Count=0 then begin
|
|
{$IFDEF VerbosePkgEditDrag}
|
|
debugln(['TPkgManager.MoveFiles no file feasable for moving']);
|
|
{$ENDIF}
|
|
exit(true);
|
|
end;
|
|
|
|
Result:=MoveFiles(TargetFilesEdit,SrcFilesEdit,Files,TargetDirectory);
|
|
finally
|
|
Files.Free;
|
|
end;
|
|
end;
|
|
|
|
function TPkgManager.MoveFiles(TargetFilesEdit, SrcFilesEdit: IFilesEditorInterface;
|
|
IDEFiles: TFPList; TargetDirectory: string): boolean;
|
|
var
|
|
ChangedFilenames: TFilenameToStringTree; // old to new file name
|
|
AllChangedFilenames: TFilenameToStringTree; // including resouce files
|
|
NewFileToOldOwnedFile: TFilenameToPointerTree; // filename to TIDEOwnedFile
|
|
DeleteOld: Boolean;
|
|
UnitFilenameToResFileList: TFilenameToPointerTree; // filename to TStringList
|
|
SrcDirToPkg: TFilenameToPointerTree;
|
|
SrcPackage, TargetPackage: TLazPackage;
|
|
SrcProject, TargetProject: TProject;
|
|
SrcIsTarget: Boolean;
|
|
|
|
procedure DeleteNonExistingPkgFiles;
|
|
var
|
|
i: Integer;
|
|
CurFile: TIDEOwnedFile;
|
|
aFilename: String;
|
|
begin
|
|
// ignore non existing files
|
|
for i:=IDEFiles.Count-1 downto 0 do begin
|
|
CurFile:=TIDEOwnedFile(IDEFiles[i]);
|
|
aFilename:=CurFile.GetFullFilename;
|
|
if not FileExistsCached(aFilename) then begin
|
|
{$IFDEF VerbosePkgEditDrag}
|
|
debugln(['TPkgManager.MoveFiles WARNING: file not found: ',aFilename]);
|
|
{$ENDIF}
|
|
IDEFiles.Delete(i);
|
|
end;
|
|
|
|
if (CurFile is TUnitInfo) and (TUnitInfo(CurFile)=TUnitInfo(CurFile).Project.MainUnitInfo)
|
|
then begin
|
|
{$IFDEF VerbosePkgEditDrag}
|
|
debugln(['TPkgManager.MoveFiles WARNING: main unit of project cannot be moved: ',aFilename]);
|
|
{$ENDIF}
|
|
IDEFiles.Delete(i);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
function GetPkgProj(FilesEdit: IFilesEditorInterface; out aPkg: TLazPackage;
|
|
out aProject: TProject): boolean;
|
|
var
|
|
MainUnit: TUnitInfo;
|
|
Code: TCodeBuffer;
|
|
Tool: TCodeTool;
|
|
begin
|
|
Result:=false;
|
|
aPkg:=nil;
|
|
aProject:=nil;
|
|
if not FilenameIsAbsolute(FilesEdit.FilesBaseDirectory) then begin
|
|
{$IFDEF VerbosePkgEditDrag}
|
|
debugln(['TPkgManager.MoveFiles base dir not absolute: ',FilesEdit.FilesBaseDirectory]);
|
|
{$ENDIF}
|
|
exit;
|
|
end;
|
|
|
|
if FilesEdit.FilesOwner is TLazPackage then begin
|
|
aPkg:=TLazPackage(FilesEdit.FilesOwner);
|
|
Result:=true;
|
|
end else if FilesEdit.FilesOwner is TProject then begin
|
|
aProject:=TProject(FilesEdit.FilesOwner);
|
|
MainUnit:=aProject.MainUnitInfo;
|
|
if (MainUnit<>nil) and (pfMainUnitIsPascalSource in aProject.Flags) then
|
|
begin
|
|
// check project main source for syntax errors
|
|
if LoadCodeBuffer(Code,MainUnit.Filename,[lbfUpdateFromDisk,lbfCheckIfText],false)<>mrOk
|
|
then exit;
|
|
if not CodeToolBoss.Explore(Code,Tool,true) then begin
|
|
{$IFDEF VerbosePkgEditDrag}
|
|
debugln(['TPkgManager.MoveFiles project main source has errors: ',Code.Filename]);
|
|
{$ENDIF}
|
|
LazarusIDE.DoJumpToCodeToolBossError;
|
|
exit;
|
|
end;
|
|
end;
|
|
Result:=true;
|
|
end;
|
|
end;
|
|
|
|
function FileIsUnit(aFile: TIDEOwnedFile): boolean;
|
|
begin
|
|
if aFile is TPkgFile then
|
|
Result:=TPkgFile(aFile).FileType in PkgFileRealUnitTypes
|
|
else
|
|
Result:=FilenameIsPascalSource(aFile.Filename);
|
|
end;
|
|
|
|
procedure AddResFile(ResFiles: TStringList; ResFile: string);
|
|
var
|
|
NewResFile: String;
|
|
begin
|
|
if not FilenameIsAbsolute(ResFile) then exit;
|
|
if AllChangedFilenames.Contains(ResFile) then exit;
|
|
if IndexInRecentList(ResFiles,rltFile,ResFile)>=0 then exit;
|
|
if not FileExistsCached(ResFile) then exit;
|
|
ResFiles.Add(ResFile);
|
|
NewResFile:=TargetDirectory+ExtractFilename(ResFile);
|
|
AllChangedFilenames[ResFile]:=NewResFile;
|
|
end;
|
|
|
|
function CollectFiles(out MoveFileCount: integer): boolean;
|
|
var
|
|
i: Integer;
|
|
aFile: TIDEOwnedFile;
|
|
OldFilename: String;
|
|
NewFilename: String;
|
|
ResFileList: TStringList;
|
|
UnitResArr: TUnitResourcefileFormatArr;
|
|
j: Integer;
|
|
aFilename: String;
|
|
S2PItem: PStringToPointerTreeItem;
|
|
begin
|
|
Result:=false;
|
|
MoveFileCount:=0;
|
|
for i:=0 to IDEFiles.Count-1 do begin
|
|
aFile:=TIDEOwnedFile(IDEFiles[i]);
|
|
OldFilename:=aFile.GetFullFilename;
|
|
NewFilename:=TargetDirectory+ExtractFilename(OldFilename);
|
|
|
|
// check if two copied/moved files will get the same new file name
|
|
if NewFileToOldOwnedFile.Contains(NewFilename) then begin
|
|
IDEMessageDialog(lisConflictDetected,
|
|
Format(lisTwoMovedFilesWillHaveTheSameFileNameIn,
|
|
[#13, aFile.Filename, #13, TIDEOwnedFile(NewFileToOldOwnedFile[NewFilename]).Filename,
|
|
#13, TargetFilesEdit.FilesOwnerName]), mtError, [mbCancel]);
|
|
exit;
|
|
end;
|
|
NewFileToOldOwnedFile[NewFilename]:=aFile;
|
|
|
|
if CompareFilenames(NewFilename,OldFilename)<>0 then begin
|
|
// file be copied/moved to another directory
|
|
debugln(['Hint: (lazarus) CollectFiles Old="',OldFilename,'"']);
|
|
debugln(['Hint: (lazarus) New="',NewFilename,'"']);
|
|
inc(MoveFileCount);
|
|
ChangedFilenames[OldFilename]:=NewFilename;
|
|
AllChangedFilenames[OldFilename]:=NewFilename;
|
|
|
|
// check resource file
|
|
if FileIsUnit(aFile) then begin
|
|
ResFileList:=TStringList.Create;
|
|
UnitFilenameToResFileList[OldFilename]:=ResFileList;
|
|
AddResFile(ResFileList,ChangeFileExt(OldFilename,'.lfm'));
|
|
AddResFile(ResFileList,ChangeFileExt(OldFilename,'.dfm'));
|
|
AddResFile(ResFileList,ChangeFileExt(OldFilename,'.lrs'));
|
|
UnitResArr:=GetUnitResourcefileFormats;
|
|
for j:=0 to length(UnitResArr)-1 do begin
|
|
aFilename:=UnitResArr[j].GetUnitResourceFilename(OldFilename,true);
|
|
AddResFile(ResFileList,aFilename);
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
// remove res files, that are in IDEFiles
|
|
for S2PItem in UnitFilenameToResFileList do begin
|
|
OldFilename:=S2PItem^.Name;
|
|
ResFileList:=TStringList(S2PItem^.Value);
|
|
for i:=ResFileList.Count-1 downto 0 do begin
|
|
if ChangedFilenames.Contains(ResFileList[i]) then
|
|
ResFileList.Delete(i);
|
|
end;
|
|
end;
|
|
|
|
Result:=true;
|
|
end;
|
|
|
|
function CheckNewFilesDoNotExist: boolean;
|
|
var
|
|
S2SItem: PStringToStringItem;
|
|
OldFilename: String;
|
|
NewFilename: String;
|
|
ConflictFile: TIDEOwnedFile;
|
|
CurName: String;
|
|
ShortFilename: String;
|
|
r: TModalResult;
|
|
i: Integer;
|
|
WarnUnitClash: Boolean;
|
|
WarnNameClash: Boolean;
|
|
Cnt: Integer;
|
|
SrcEdit: TSourceEditor;
|
|
begin
|
|
Result:=false;
|
|
WarnUnitClash:=true;
|
|
WarnNameClash:=true;
|
|
for S2SItem in AllChangedFilenames do begin
|
|
OldFilename:=S2SItem^.Name;
|
|
NewFilename:=S2SItem^.Value;
|
|
if CompareFilenames(OldFilename,NewFilename)=0 then continue;
|
|
|
|
// check file does not exist
|
|
if FileExistsCached(NewFilename) then begin
|
|
IDEMessageDialog(lisConflictDetected,
|
|
Format(lisThereIsAlreadyAFileIn, [#13, NewFilename, #13,
|
|
TargetFilesEdit.FilesOwnerName]), mtError, [mbCancel]);
|
|
exit;
|
|
end;
|
|
|
|
// close source editor
|
|
repeat
|
|
SrcEdit:=SourceEditorManager.SourceEditorIntfWithFilename(NewFilename);
|
|
if SrcEdit=nil then break;
|
|
if LazarusIDE.DoCloseEditorFile(SrcEdit,[cfSaveFirst,
|
|
cfCloseDependencies,cfSaveDependencies])<>mrOk then exit;
|
|
until false;
|
|
|
|
if (not SrcIsTarget) then begin
|
|
// warn duplicate names
|
|
if FilenameIsPascalUnit(NewFilename) then begin
|
|
// warn duplicate unit name
|
|
CurName:=ExtractFileNameOnly(NewFilename);
|
|
if TargetPackage<>nil then
|
|
ConflictFile:=TargetPackage.FindUnit(CurName,true)
|
|
else if TargetProject<>nil then
|
|
ConflictFile:=TargetProject.UnitWithUnitname(CurName)
|
|
else
|
|
ConflictFile:=nil;
|
|
if (ConflictFile<>nil) and WarnUnitClash then begin
|
|
ShortFilename:=NewFilename;
|
|
ShortFilename:=CreateRelativePath(ShortFilename,TargetFilesEdit.FilesBaseDirectory);
|
|
r:=IDEMessageDialog(lisDuplicateUnit,
|
|
Format(lisThereIsAlreadyAUnitInOldNewYouHaveToMakeSur, [
|
|
CurName, TargetFilesEdit.FilesOwnerName, #13,
|
|
ConflictFile.GetShortFilename(true), #13,
|
|
ShortFilename, #13, #13, #13])
|
|
,mtWarning,[mbYes,mbYesToAll,mbCancel]);
|
|
case r of
|
|
mrYes: ;
|
|
mrYesToAll: WarnUnitClash:=false;
|
|
else exit;
|
|
end;
|
|
end;
|
|
end else begin
|
|
// warn duplicate file
|
|
if TargetPackage<>nil then
|
|
Cnt:=TargetPackage.FileCount
|
|
else if TargetProject<>nil then
|
|
Cnt:=TargetProject.FileCount
|
|
else
|
|
Cnt:=0;
|
|
for i:=0 to Cnt-1 do begin
|
|
if not WarnNameClash then continue;
|
|
if TargetPackage<>nil then
|
|
ConflictFile:=TargetPackage.Files[i]
|
|
else if TargetProject<>nil then
|
|
ConflictFile:=TargetProject.Files[i]
|
|
else
|
|
continue;
|
|
ShortFilename:=ExtractFilename(NewFilename);
|
|
CurName:=ExtractFileName(ConflictFile.Filename);
|
|
if (AnsiCompareText(CurName,ShortFilename)<>0)
|
|
and (CompareFilenames(CurName,ShortFilename)<>0) then
|
|
continue;
|
|
// name clash on this or other platforms => warn
|
|
ShortFilename:=NewFilename;
|
|
ShortFilename:=CreateRelativePath(ShortFilename,TargetFilesEdit.FilesBaseDirectory);
|
|
r:=IDEMessageDialog(lisDuplicateFileName,
|
|
Format(lisThereIsAlreadyAFileInOldNewContinue, [CurName,
|
|
TargetFilesEdit.FilesOwnerName, #13,
|
|
ConflictFile.GetShortFilename(true), #13,
|
|
ShortFilename, #13, #13])
|
|
,mtWarning,[mbYes,mbYesToAll,mbCancel]);
|
|
case r of
|
|
mrYes: ;
|
|
mrYesToAll: WarnNameClash:=false;
|
|
else exit;
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
Result:=true;
|
|
end;
|
|
|
|
function CloseSrcEditors: boolean;
|
|
var
|
|
i: Integer;
|
|
SrcEdit: TSourceEditorInterface;
|
|
begin
|
|
for i:=SourceEditorManagerIntf.SourceEditorCount-1 downto 0 do begin
|
|
SrcEdit:=SourceEditorManagerIntf.SourceEditors[i];
|
|
if not AllChangedFilenames.Contains(SrcEdit.FileName) then continue;
|
|
if LazarusIDE.DoCloseEditorFile(SrcEdit,
|
|
[cfSaveFirst,cfCloseDependencies])<>mrOk
|
|
then begin
|
|
{$IFDEF VerbosePkgEditDrag}
|
|
debugln(['CloseSrcEditors failed']);
|
|
{$ENDIF}
|
|
exit(false);
|
|
end;
|
|
end;
|
|
Result:=true;
|
|
end;
|
|
|
|
function ClearOldCompiledFiles: boolean;
|
|
var
|
|
OutDir: String;
|
|
CurFiles: TStrings;
|
|
OutFilename: String;
|
|
CurUnitName: String;
|
|
S2SItem: PStringToStringItem;
|
|
OldFilename: String;
|
|
SeparateOutDir: Boolean;
|
|
r: TModalResult;
|
|
begin
|
|
Result:=false;
|
|
// => clear output directory of Src
|
|
if SrcPackage<>nil then begin
|
|
if PackageGraph.PreparePackageOutputDirectory(SrcPackage,true)<>mrOk then
|
|
begin
|
|
{$IFDEF VerbosePkgEditDrag}
|
|
debugln(['TPkgManager.MoveFiles PreparePackageOutputDirectory failed']);
|
|
{$ENDIF}
|
|
exit;
|
|
end;
|
|
end else if SrcProject<>nil then begin
|
|
OutDir:=ChompPathDelim(SrcProject.GetOutputDirectory);
|
|
if not FilenameIsAbsolute(OutDir) then exit(true);
|
|
CurFiles:=nil;
|
|
try
|
|
CodeToolBoss.DirectoryCachePool.GetListing(OutDir,CurFiles,false);
|
|
for OutFilename in CurFiles do begin
|
|
CurUnitName:=ExtractFilenameOnly(OutFilename);
|
|
for S2SItem in ChangedFilenames do begin
|
|
OldFilename:=S2SItem^.Name;
|
|
if not FilenameIsPascalSource(OldFilename) then continue;
|
|
if CompareTextCT(CurUnitName,ExtractFileNameOnly(OldFilename))<>0 then
|
|
continue;
|
|
// output filename and source have same unitname
|
|
SeparateOutDir:=CompareFilenames(ChompPathDelim(ExtractFilePath(OldFilename)),OutDir)<>0;
|
|
if FilenameExtIn(OutFilename,['ppu','o','ppl','rst','lrt'])
|
|
or (SeparateOutDir and FilenameExtIn(OutFilename,['lrs','lfm'])) then
|
|
begin
|
|
// automatically created file found => delete
|
|
r:=DeleteFileInteractive(OutFilename,[mbCancel,mbIgnore]);
|
|
if not (r in [mrOk,mrIgnore]) then exit;
|
|
end;
|
|
break;
|
|
end;
|
|
end;
|
|
finally
|
|
CurFiles.Free;
|
|
end;
|
|
end;
|
|
Result:=true;
|
|
end;
|
|
|
|
function CheckUsesSection(Tool: TCodeTool; UsesNode: TCodeTreeNode;
|
|
NewUnitFilename: string): boolean;
|
|
// true if no warnings
|
|
var
|
|
AnUnitName, AnUnitInFilename: string;
|
|
OldUsedUnitCode: TCodeBuffer;
|
|
OldUsedUnitFilename: string;
|
|
Node: TCodeTreeNode;
|
|
NamePos: Integer;
|
|
OldCompiledUnitname: String;
|
|
CodePos: TCodeXYPosition;
|
|
Msg: String;
|
|
PkgName: String;
|
|
UsedPkg: TLazPackage;
|
|
NewUsedUnitFilename: String;
|
|
begin
|
|
Result:=true;
|
|
if UsesNode=nil then exit;
|
|
// check that all used units are available in the target package
|
|
Node:=UsesNode.FirstChild;
|
|
while Node<>nil do begin
|
|
// read unit name
|
|
AnUnitInFilename:='';
|
|
AnUnitName:=Tool.ExtractUsedUnitName(Node,@AnUnitInFilename);
|
|
NamePos:=Node.StartPos;
|
|
Node:=Node.NextBrother;
|
|
if AnUnitName='' then continue;
|
|
// find unit file
|
|
OldUsedUnitCode:=Tool.FindUnitSource(AnUnitName,AnUnitInFilename,false,NamePos);
|
|
if (OldUsedUnitCode=nil) then begin
|
|
// no source found
|
|
// => search for ppu
|
|
OldCompiledUnitname:=AnUnitName+'.ppu';
|
|
OldUsedUnitFilename:=Tool.DirectoryCache.FindCompiledUnitInCompletePath(
|
|
OldCompiledUnitname,false);
|
|
if OldUsedUnitFilename='' then begin
|
|
// unit not found
|
|
// (that is ok, e.g. if the unit is used on another platform)
|
|
// => only warn
|
|
Msg:=Format(lisUnitNotFound, [AnUnitName]);
|
|
if not Tool.CleanPosToCaret(NamePos,CodePos) then continue;
|
|
Result:=false;
|
|
IDEMessagesWindow.AddCustomMessage(mluWarning,Msg,
|
|
CodePos.Code.Filename, CodePos.Y, CodePos.X, lisMoveFiles);
|
|
continue;
|
|
end;
|
|
end else begin
|
|
// unit found
|
|
OldUsedUnitFilename:=OldUsedUnitCode.Filename;
|
|
if AllChangedFilenames.Contains(OldUsedUnitFilename) then begin
|
|
// this unit will be moved too => ok
|
|
continue;
|
|
end;
|
|
end;
|
|
// OldUsedUnitFilename is now either a .pas/pp/p or .ppu file
|
|
|
|
// search unit in new position
|
|
NewUsedUnitFilename:=CodeToolBoss.DirectoryCachePool.FindUnitSourceInCompletePath(
|
|
ExtractFilePath(NewUnitFilename),AnUnitName,AnUnitInFilename);
|
|
if (NewUsedUnitFilename='') and (AnUnitInFilename='') then
|
|
NewUsedUnitFilename:=CodeToolBoss.DirectoryCachePool.FindCompiledUnitInCompletePath(
|
|
ExtractFilePath(NewUnitFilename),AnUnitName);
|
|
if CompareFilenames(OldUsedUnitFilename,NewUsedUnitFilename)=0 then
|
|
continue;
|
|
// not found or a different unit found
|
|
|
|
if not Tool.CleanPosToCaret(NamePos,CodePos) then continue;
|
|
|
|
// find package of used unit
|
|
PkgName:='';
|
|
UsedPkg:=TLazPackage(SrcDirToPkg[ExtractFilePath(OldUsedUnitFilename)]);
|
|
if UsedPkg<>nil then
|
|
PkgName:=UsedPkg.Name;
|
|
|
|
if NewUsedUnitFilename='' then begin
|
|
// at the new position the unit cannot be found
|
|
if PkgName='' then begin
|
|
Msg:=Format(lisUnitNotFoundAtNewPosition, [AnUnitName, NewUnitFilename]);
|
|
end else begin
|
|
Msg:=Format(lisUnitRequiresPackage, [AnUnitName, PkgName]);
|
|
end;
|
|
end else begin
|
|
// a different unit will be used
|
|
Msg:=Format(lisDifferentUnitFoundAtNewPosition, [AnUnitName,
|
|
NewUnitFilename]);
|
|
end;
|
|
|
|
Result:=false;
|
|
IDEMessagesWindow.AddCustomMessage(mluWarning,Msg,
|
|
CodePos.Code.Filename, CodePos.Y, CodePos.X, lisMoveFiles);
|
|
end;
|
|
end;
|
|
|
|
function CheckUsesSections: boolean;
|
|
// check that all used units are available in the target package
|
|
var
|
|
i: Integer;
|
|
aFile: TIDEOwnedFile;
|
|
OldFilename: String;
|
|
Code: TCodeBuffer;
|
|
Tool: TCodeTool;
|
|
NewFilename: String;
|
|
begin
|
|
if SrcIsTarget then
|
|
exit(true);
|
|
// moving files to another package/project
|
|
if (SrcPackage<>nil) and (PackageGraph.FindDependencyRecursively(
|
|
TargetFilesEdit.FirstRequiredDependency,SrcPackage)<>nil)
|
|
then begin
|
|
// units are moved to higher level package/project
|
|
// => no check needed
|
|
exit(true);
|
|
end;
|
|
|
|
// check that all used units are available in the target
|
|
Result:=true;
|
|
for i:=0 to IDEFiles.Count-1 do begin
|
|
aFile:=TIDEOwnedFile(IDEFiles[i]);
|
|
if not FileIsUnit(aFile) then continue;
|
|
OldFilename:=aFile.GetFullFilename;
|
|
NewFilename:=ChangedFilenames[OldFilename];
|
|
if CompareFilenames(ExtractFilePath(OldFilename),ExtractFilePath(NewFilename))=0
|
|
then continue;
|
|
if LoadCodeBuffer(Code,OldFilename,[lbfUpdateFromDisk,lbfCheckIfText],false)<>mrOk
|
|
then exit;
|
|
CodeToolBoss.Explore(Code,Tool,false);
|
|
if not CheckUsesSection(Tool,Tool.FindMainUsesNode,NewFilename) then
|
|
Result:=false;
|
|
if not CheckUsesSection(Tool,Tool.FindImplementationUsesNode,NewFilename) then
|
|
Result:=false;
|
|
end;
|
|
if not Result then begin
|
|
if IDEMessageDialog(lisCCOWarningCaption,
|
|
lisMovingTheseUnitsWillBreakTheirUsesSectionsSeeMessa,
|
|
mtWarning,[mbIgnore,mbCancel])<>mrIgnore
|
|
then
|
|
exit;
|
|
Result:=true;
|
|
end;
|
|
end;
|
|
|
|
function ExtendSearchPaths: boolean;
|
|
var
|
|
i: Integer;
|
|
aFile: TIDEOwnedFile;
|
|
NewDir: String;
|
|
NewUnitPaths: String;
|
|
NewIncPaths: String;
|
|
OldFilename: String;
|
|
FileType: TPkgFileType;
|
|
begin
|
|
NewUnitPaths:='';
|
|
NewIncPaths:='';
|
|
for i:=0 to IDEFiles.Count-1 do begin
|
|
aFile:=TIDEOwnedFile(IDEFiles[i]);
|
|
OldFilename:=aFile.GetFullFilename;
|
|
NewDir:=ChompPathDelim(ExtractFilePath(ChangedFilenames[OldFilename]));
|
|
if aFile is TPkgFile then
|
|
FileType:=TPkgFile(aFile).FileType
|
|
else
|
|
FileType:=FileNameToPkgFileType(OldFilename);
|
|
case FileType of
|
|
pftUnit,pftMainUnit:
|
|
MergeSearchPaths(NewUnitPaths,NewDir);
|
|
pftInclude:
|
|
MergeSearchPaths(NewIncPaths,NewDir);
|
|
end;
|
|
end;
|
|
// unit paths
|
|
if (NewUnitPaths<>'') and not TargetFilesEdit.ExtendUnitSearchPath(NewUnitPaths)
|
|
then begin
|
|
{$IFDEF VerbosePkgEditDrag}
|
|
debugln(['ExtendSearchPaths ExtendUnitSearchPath failed: NewUnitPaths="',NewUnitPaths,'"']);
|
|
{$ENDIF}
|
|
exit(false);
|
|
end;
|
|
// include paths
|
|
if (NewIncPaths<>'') and not TargetFilesEdit.ExtendIncSearchPath(NewIncPaths)
|
|
then begin
|
|
{$IFDEF VerbosePkgEditDrag}
|
|
debugln(['ExtendSearchPaths ExtendIncSearchPath failed: NewIncPaths="',NewIncPaths,'"']);
|
|
{$ENDIF}
|
|
exit(false);
|
|
end;
|
|
Result:=true;
|
|
end;
|
|
|
|
function MoveOrCopyFile(OldFilename: string;
|
|
MovedFiles: TFilenameToPointerTree): boolean;
|
|
var
|
|
NewFilename: String;
|
|
r: TModalResult;
|
|
OldPkgFile: TPkgFile;
|
|
NewPkgFile: TPkgFile;
|
|
NewFileType: TPkgFileType;
|
|
NewUnitName: String;
|
|
NewCompPrio: TComponentPriority;
|
|
NewResourceBaseClass: TPFComponentBaseClass;
|
|
NewHasRegisterProc: Boolean;
|
|
NewAddToUses: Boolean;
|
|
OldProjFile: TUnitInfo;
|
|
Code: TCodeBuffer;
|
|
NewProjFile: TUnitInfo;
|
|
begin
|
|
Result:=false;
|
|
// check if needed
|
|
NewFilename:=TargetDirectory+ExtractFilename(OldFilename);
|
|
if CompareFilenames(NewFilename,OldFilename)=0 then
|
|
exit(true);
|
|
// check if already moved
|
|
if MovedFiles.Contains(OldFilename) then
|
|
exit(true);
|
|
MovedFiles[OldFilename]:=Self;
|
|
// copy or move file
|
|
if FileExistsUTF8(OldFilename) then begin
|
|
if DeleteOld then begin
|
|
{$IFDEF VerbosePkgEditDrag}
|
|
debugln(['MoveOrCopyFile rename "',OldFilename,'" to "',NewFilename,'"']);
|
|
{$ENDIF}
|
|
r:=RenameFileWithErrorDialogs(OldFilename,NewFilename,[mbAbort,mbIgnore]);
|
|
end else begin
|
|
{$IFDEF VerbosePkgEditDrag}
|
|
debugln(['MoveOrCopyFile copy "',OldFilename,'" to "',NewFilename,'"']);
|
|
{$ENDIF}
|
|
r:=CopyFileWithErrorDialogs(OldFilename,NewFilename,[mbAbort,mbIgnore]);
|
|
end;
|
|
if not (r in [mrIgnore,mrOK]) then begin
|
|
debugln(['Error: (lazarus) MoveOrCopyFile: rename/copy failed: "',OldFilename,'" to "',NewFilename,'"']);
|
|
exit;
|
|
end;
|
|
end else begin
|
|
if IDEMessageDialog(lisCCOWarningCaption,
|
|
Format(lisFileNotFound5, [#13, OldFilename]), mtWarning, [mbIgnore,
|
|
mbCancel])<>mrIgnore
|
|
then
|
|
exit;
|
|
end;
|
|
|
|
OldPkgFile:=nil;
|
|
OldProjFile:=nil;
|
|
if SrcPackage<>nil then begin
|
|
OldPkgFile:=SrcPackage.FindPkgFile(OldFilename,true,false);
|
|
if OldPkgFile=nil then begin
|
|
{$IFDEF VerbosePkgEditDrag}
|
|
debugln(['MoveOrCopyFile old file not in lpk: "',OldFilename,'" pkg=',SrcPackage.Name]);
|
|
{$ENDIF}
|
|
// this is a resource file
|
|
// => do not create an entry in the target
|
|
exit(true);
|
|
end;
|
|
end else if SrcProject<>nil then begin
|
|
OldProjFile:=SrcProject.UnitInfoWithFilename(OldFilename,[pfsfOnlyProjectFiles]);
|
|
if OldProjFile=nil then begin
|
|
{$IFDEF VerbosePkgEditDrag}
|
|
debugln(['MoveOrCopyFile old file not in lpi: "',OldFilename,'"']);
|
|
{$ENDIF}
|
|
// this is a resource file
|
|
// => do not create an entry in the target
|
|
exit(true);
|
|
end;
|
|
end else begin
|
|
raise Exception.Create('implement me');
|
|
end;
|
|
|
|
if OldPkgFile<>nil then begin
|
|
NewUnitName:=OldPkgFile.Unit_Name;
|
|
NewFileType:=OldPkgFile.FileType;
|
|
if NewFileType=pftMainUnit then NewFileType:=pftUnit;
|
|
NewCompPrio:=OldPkgFile.ComponentPriority;
|
|
NewResourceBaseClass:=OldPkgFile.ResourceBaseClass;
|
|
NewHasRegisterProc:=OldPkgFile.HasRegisterProc;
|
|
NewAddToUses:=OldPkgFile.AddToUsesPkgSection;
|
|
end else begin
|
|
NewUnitName:=OldProjFile.Unit_Name;
|
|
NewFileType:=FileNameToPkgFileType(OldFilename);
|
|
NewCompPrio:=ComponentPriorityNormal;
|
|
NewResourceBaseClass:=OldProjFile.ResourceBaseClass;
|
|
NewHasRegisterProc:=false;
|
|
NewAddToUses:=true;
|
|
if NewFileType=pftUnit then begin
|
|
Code:=CodeToolBoss.LoadFile(OldFilename,true,false);
|
|
if (Code<>nil) and (TargetPackage<>nil) then
|
|
NewHasRegisterProc:=CodeToolBoss.HasInterfaceRegisterProc(Code);
|
|
end;
|
|
end;
|
|
|
|
NewPkgFile:=nil;
|
|
NewProjFile:=nil;
|
|
if TargetPackage<>nil then begin
|
|
// create new TPkgFile
|
|
NewPkgFile:=TargetPackage.FindPkgFile(NewFilename,true,false);
|
|
if NewPkgFile=nil then begin
|
|
{$IFDEF VerbosePkgEditDrag}
|
|
debugln(['MoveOrCopyFile create new "',NewFilename,'" pkg=',TargetPackage.Name]);
|
|
{$ENDIF}
|
|
NewPkgFile:=TargetPackage.AddFile(NewFilename,NewUnitName,
|
|
NewFileType,[],NewCompPrio.Category);
|
|
end else begin
|
|
NewPkgFile.Unit_Name:=NewUnitName;
|
|
NewFileType:=NewFileType;
|
|
NewPkgFile.FileType:=NewFileType;
|
|
end;
|
|
NewPkgFile.ComponentPriority:=NewCompPrio;
|
|
NewPkgFile.ResourceBaseClass:=NewResourceBaseClass;
|
|
NewPkgFile.HasRegisterProc:=NewHasRegisterProc;
|
|
if NewAddToUses
|
|
and (TargetPackage.FindUsedUnit(ExtractFileNameOnly(NewFilename),NewPkgFile)<>nil)
|
|
then begin
|
|
// another unit with this name is already used
|
|
NewPkgFile.AddToUsesPkgSection:=false;
|
|
end else begin
|
|
NewPkgFile.AddToUsesPkgSection:=NewAddToUses;
|
|
end;
|
|
end else if TargetProject<>nil then begin
|
|
// create new TUnitInfo
|
|
|
|
NewProjFile:=TargetProject.UnitInfoWithFilename(NewFilename);
|
|
if NewProjFile=nil then begin
|
|
NewProjFile:=TUnitInfo.Create(nil);
|
|
NewProjFile.Filename:=NewFilename;
|
|
TargetProject.AddFile(NewProjFile,false);
|
|
end;
|
|
NewProjFile.IsPartOfProject:=true;
|
|
NewProjFile.ResourceBaseClass:=NewResourceBaseClass;
|
|
if OldProjFile<>nil then begin
|
|
NewProjFile.HasResources:=OldProjFile.HasResources;
|
|
NewProjFile.ComponentName:=OldProjFile.ComponentName;
|
|
NewProjFile.ComponentResourceName:=OldProjFile.ComponentResourceName;
|
|
NewProjFile.BuildFileIfActive:=OldProjFile.BuildFileIfActive;
|
|
NewProjFile.RunFileIfActive:=OldProjFile.RunFileIfActive;
|
|
NewProjFile.DefaultSyntaxHighlighter:=OldProjFile.DefaultSyntaxHighlighter;
|
|
NewProjFile.DisableI18NForLFM:=OldProjFile.DisableI18NForLFM;
|
|
NewProjFile.CustomDefaultHighlighter:=OldProjFile.CustomDefaultHighlighter;
|
|
end;
|
|
if (not SrcIsTarget)
|
|
and (pfMainUnitHasUsesSectionForAllUnits in TargetProject.Flags) then
|
|
begin
|
|
CodeToolBoss.AddUnitToMainUsesSection(
|
|
TargetProject.MainUnitInfo.Source,NewUnitName,'');
|
|
CodeToolBoss.SourceChangeCache.Apply;
|
|
TargetProject.MainUnitInfo.Modified:=true;
|
|
end;
|
|
end else begin
|
|
raise Exception.Create('implement me');
|
|
end;
|
|
|
|
// delete old
|
|
if DeleteOld then begin
|
|
{$IFDEF VerbosePkgEditDrag}
|
|
debugln(['MoveOrCopyFile delete "',OldFilename,'" from=',SrcFilesEdit.FilesOwnerName]);
|
|
{$ENDIF}
|
|
if OldPkgFile<>nil then begin
|
|
SrcPackage.DeleteFile(OldPkgFile);
|
|
end else if OldProjFile<>nil then begin
|
|
OldProjFile.IsPartOfProject:=false;
|
|
if (not SrcIsTarget)
|
|
and (pfMainUnitHasUsesSectionForAllUnits in SrcProject.Flags) then
|
|
begin
|
|
CodeToolBoss.RemoveUnitFromAllUsesSections(
|
|
SrcProject.MainUnitInfo.Source,NewUnitName);
|
|
CodeToolBoss.SourceChangeCache.Apply;
|
|
SrcProject.MainUnitInfo.Modified:=true;
|
|
end;
|
|
end else begin
|
|
raise Exception.Create('implement me');
|
|
end;
|
|
end;
|
|
TargetFilesEdit.UpdateAll;
|
|
SrcFilesEdit.UpdateAll;
|
|
Result:=true;
|
|
end;
|
|
|
|
function MoveOrCopyFiles: boolean;
|
|
var
|
|
i: Integer;
|
|
OldFilename: String;
|
|
MovedFiles: TFilenameToPointerTree;
|
|
ResFileList: TStringList;
|
|
j: Integer;
|
|
begin
|
|
Result:=false;
|
|
TargetFilesEdit.BeginUpdate;
|
|
SrcFilesEdit.BeginUpdate;
|
|
MovedFiles:=TFilenameToPointerTree.Create(false);
|
|
try
|
|
for i:=0 to IDEFiles.Count-1 do begin
|
|
OldFilename:=TIDEOwnedFile(IDEFiles[i]).GetFullFilename;
|
|
if not MoveOrCopyFile(OldFilename,MovedFiles) then exit;
|
|
ResFileList:=TStringList(UnitFilenameToResFileList[OldFilename]);
|
|
if ResFileList=nil then continue;
|
|
for j:=0 to ResFileList.Count-1 do
|
|
if not MoveOrCopyFile(ResFileList[j],MovedFiles) then exit;
|
|
end;
|
|
finally
|
|
MovedFiles.Free;
|
|
SrcFilesEdit.EndUpdate;
|
|
TargetFilesEdit.EndUpdate;
|
|
end;
|
|
Result:=true;
|
|
end;
|
|
|
|
var
|
|
MoveFileCount: Integer;
|
|
MsgResult: TModalResult;
|
|
begin
|
|
Result:=false;
|
|
|
|
{$IFDEF VerbosePkgEditDrag}
|
|
debugln(['TPkgManager.MoveFiles Self=',TargetFilesEdit.FilesOwnerName,' Src=',SrcFilesEdit.FilesOwnerName,' Dir="',TargetDirectory,'" FileCount=',IDEFiles.Count]);
|
|
{$ENDIF}
|
|
if not GetPkgProj(SrcFilesEdit,SrcPackage,SrcProject) then begin
|
|
{$IFDEF VerbosePkgEditDrag}
|
|
debugln(['TPkgManager.MoveFiles invalid src=',DbgSName(SrcFilesEdit.FilesOwner)]);
|
|
{$ENDIF}
|
|
exit;
|
|
end;
|
|
if not GetPkgProj(TargetFilesEdit,TargetPackage,TargetProject) then begin
|
|
{$IFDEF VerbosePkgEditDrag}
|
|
debugln(['TPkgManager.MoveFiles invalid target=',DbgSName(TargetFilesEdit.FilesOwner)]);
|
|
{$ENDIF}
|
|
exit;
|
|
end;
|
|
|
|
DeleteNonExistingPkgFiles;
|
|
if IDEFiles.Count=0 then begin
|
|
{$IFDEF VerbosePkgEditDrag}
|
|
debugln(['TPkgManager.MoveFiles PkgFiles.Count=0']);
|
|
{$ENDIF}
|
|
exit(true);
|
|
end;
|
|
|
|
if TargetFilesEdit.FilesOwnerReadOnly then begin
|
|
IDEMessageDialog(lisTargetIsReadOnly,
|
|
Format(lisTheTargetIsNotWritable, [TargetFilesEdit.FilesOwnerName]),
|
|
mtError, [mbCancel]);
|
|
exit;
|
|
end;
|
|
|
|
if not FilenameIsAbsolute(TargetDirectory) then begin
|
|
{$IFDEF VerbosePkgEditDrag}
|
|
debugln(['TPkgManager.MoveFiles invalid target dir=',TargetDirectory]);
|
|
{$ENDIF}
|
|
exit;
|
|
end;
|
|
TargetDirectory:=AppendPathDelim(TargetDirectory);
|
|
|
|
// check TargetDirectory
|
|
if CheckDirectoryIsWritable(TargetDirectory)<>mrOk then begin
|
|
debugln(['Warning: (lazarus) TPkgManager.MoveFiles not writable TargetDirectory=',TargetDirectory]);
|
|
exit;
|
|
end;
|
|
|
|
SrcIsTarget:=SrcFilesEdit.FilesOwner=TargetFilesEdit.FilesOwner;
|
|
|
|
IDEMessagesWindow.Clear;
|
|
|
|
NewFileToOldOwnedFile:=TFilenameToPointerTree.Create(false);
|
|
ChangedFilenames:=TFilenameToStringTree.Create(false);
|
|
AllChangedFilenames:=TFilenameToStringTree.Create(false);
|
|
UnitFilenameToResFileList:=TFilenameToPointerTree.Create(false);
|
|
UnitFilenameToResFileList.FreeValues:=true;
|
|
SrcDirToPkg:=nil;
|
|
try
|
|
// collect all affected files including resource files
|
|
if not CollectFiles(MoveFileCount) then begin
|
|
{$IFDEF VerbosePkgEditDrag}
|
|
debugln(['TPkgManager.MoveFiles CollectFiles failed']);
|
|
{$ENDIF}
|
|
exit;
|
|
end;
|
|
|
|
// check if new position is free
|
|
if not CheckNewFilesDoNotExist then begin
|
|
{$IFDEF VerbosePkgEditDrag}
|
|
debugln(['TPkgManager.MoveFiles CheckNewFilesDoNotExist failed']);
|
|
{$ENDIF}
|
|
exit;
|
|
end;
|
|
|
|
if (MoveFileCount=0) and (SrcIsTarget) then begin
|
|
// no move, only change order in package
|
|
// ToDo: check this case in ItemsTreeViewDragDrop
|
|
ShowMessage('Changing order via drag and drop is not implemented.');
|
|
exit;
|
|
end;
|
|
|
|
// ask for confirmation
|
|
if IDEFiles.Count=MoveFileCount then begin
|
|
MsgResult:=IDEQuestionDialog(lisMoveOrCopyFiles,
|
|
Format(lisMoveOrCopyFileSFromToTheDirectoryOfPackage, [
|
|
IntToStr(MoveFileCount),
|
|
SrcFilesEdit.FilesOwnerName, #13,
|
|
TargetDirectory, #13,
|
|
TargetFilesEdit.FilesOwnerName]),
|
|
mtConfirmation, [100, lisMove, 101, lisCopy, mrCancel]);
|
|
case MsgResult of
|
|
100: DeleteOld:=true;
|
|
101: DeleteOld:=false;
|
|
else exit;
|
|
end;
|
|
end else begin
|
|
if IDEMessageDialog(lisMoveFiles2,
|
|
Format(lisMoveFileSFromToTheDirectoryOf, [
|
|
IntToStr(MoveFileCount),
|
|
SrcFilesEdit.FilesOwnerName, #13,
|
|
TargetDirectory, #13,
|
|
TargetFilesEdit.FilesOwnerName]),
|
|
mtConfirmation,[mbOk,mbCancel])<>mrOK
|
|
then exit;
|
|
DeleteOld:=true;
|
|
end;
|
|
|
|
// fetch used packages
|
|
SrcDirToPkg:=PackageGraph.GetMapSourceDirectoryToPackage;
|
|
|
|
// check uses sections
|
|
if not CheckUsesSections then begin
|
|
{$IFDEF VerbosePkgEditDrag}
|
|
debugln(['TPkgManager.MoveFiles CheckUsesSections failed']);
|
|
{$ENDIF}
|
|
exit;
|
|
end;
|
|
|
|
if DeleteOld then begin
|
|
// close files and res files in source editor
|
|
if not CloseSrcEditors then begin
|
|
{$IFDEF VerbosePkgEditDrag}
|
|
debugln(['TPkgManager.MoveFiles CloseSrcEditors failed']);
|
|
{$ENDIF}
|
|
exit;
|
|
end;
|
|
end;
|
|
|
|
if (not SrcIsTarget) then begin
|
|
// files will be moved to another package/project
|
|
if not ClearOldCompiledFiles then begin
|
|
{$IFDEF VerbosePkgEditDrag}
|
|
debugln(['TPkgManager.MoveFiles ClearOldCompiledFiles failed']);
|
|
{$ENDIF}
|
|
exit;
|
|
end;
|
|
end;
|
|
|
|
// extend unit/include path of LazPackage
|
|
if not ExtendSearchPaths then begin
|
|
{$IFDEF VerbosePkgEditDrag}
|
|
debugln(['TPkgManager.MoveFiles ExtendSearchPaths failed']);
|
|
{$ENDIF}
|
|
exit;
|
|
end;
|
|
|
|
// move/copy files
|
|
if not MoveOrCopyFiles then begin
|
|
{$IFDEF VerbosePkgEditDrag}
|
|
debugln(['TPkgManager.MoveFiles MoveOrCopyFiles failed']);
|
|
{$ENDIF}
|
|
exit;
|
|
end;
|
|
|
|
Result:=true;
|
|
finally
|
|
SrcDirToPkg.Free;
|
|
UnitFilenameToResFileList.Free;
|
|
AllChangedFilenames.Free;
|
|
ChangedFilenames.Free;
|
|
NewFileToOldOwnedFile.Free;
|
|
end;
|
|
end;
|
|
|
|
function TPkgManager.CopyMoveFiles(Sender: TObject): boolean;
|
|
var
|
|
SelDirDlg: TSelectDirectoryDialog;
|
|
FilesEdit: IFilesEditorInterface;
|
|
TargetDir: String;
|
|
begin
|
|
Result:=false;
|
|
if Sender is TPackageEditorForm then
|
|
FilesEdit:=TPackageEditorForm(Sender)
|
|
else if Sender is TProjectInspectorForm then
|
|
FilesEdit:=TProjectInspectorForm(Sender)
|
|
else begin
|
|
debugln(['Error: (lazarus) TPkgManager.CopyMoveFiles wrong Sender: ',DbgSName(Sender)]);
|
|
exit;
|
|
end;
|
|
SelDirDlg:=TSelectDirectoryDialog.Create(nil);
|
|
try
|
|
SelDirDlg.InitialDir:=FilesEdit.FilesBaseDirectory;
|
|
SelDirDlg.Title:=lisSelectTargetDirectory;
|
|
SelDirDlg.Options:=SelDirDlg.Options+[ofPathMustExist,ofFileMustExist];
|
|
if not SelDirDlg.Execute then exit;
|
|
TargetDir:=CleanAndExpandDirectory(SelDirDlg.FileName);
|
|
Result:=MoveFiles(FilesEdit,FilesEdit,TargetDir);
|
|
finally
|
|
SelDirDlg.Free;
|
|
end;
|
|
end;
|
|
|
|
constructor TPkgManager.Create(TheOwner: TComponent);
|
|
begin
|
|
inherited Create(TheOwner);
|
|
OnGetDependencyOwnerDescription:=@GetDependencyOwnerDescription;
|
|
OnGetDependencyOwnerDirectory:=@GetDependencyOwnerDirectory;
|
|
OnPackageFileLoaded:=@PackageFileLoaded;
|
|
|
|
// package links
|
|
LazPackageLinks:=TLazPackageLinks.Create;
|
|
PkgLinks:=LazPackageLinks;
|
|
LazPackageLinks.UpdateAll;
|
|
|
|
// package graph
|
|
PackageGraph:=TLazPackageGraph.Create;
|
|
PackageGraphInterface:=PackageGraph;
|
|
PackageGraph.OnAddPackage:=@PackageGraphAddPackage;
|
|
PackageGraph.OnBeforeCompilePackages:=@DoBeforeCompilePackages;
|
|
PackageGraph.OnBeginUpdate:=@PackageGraphBeginUpdate;
|
|
PackageGraph.OnChangePackageName:=@PackageGraphChangePackageName;
|
|
PackageGraph.OnCheckInterPkgFiles:=@PackageGraphCheckInterPkgFiles;
|
|
PackageGraph.OnDeleteAmbiguousFiles:=@BuildBoss.DeleteAmbiguousFiles;
|
|
PackageGraph.OnDeletePackage:=@PackageGraphDeletePackage;
|
|
PackageGraph.OnDependencyModified:=@PackageGraphDependencyModified;
|
|
PackageGraph.OnEndUpdate:=@PackageGraphEndUpdate;
|
|
PackageGraph.OnTranslatePackage:=@DoTranslatePackage;
|
|
PackageGraph.OnUninstallPackage:=@DoUninstallPackage;
|
|
PackageGraph.OnSrcEditFileIsModified:=@PackageGraphSrcEditFileIsModified;
|
|
|
|
// package editors
|
|
PackageEditors:=TPackageEditors.Create;
|
|
PackageEditors.OnAddToProject:=@PackageEditorAddToProject;
|
|
PackageEditors.OnAfterWritePackage:=@AfterWritePackage;
|
|
PackageEditors.OnBeforeReadPackage:=@BeforeReadPackage;
|
|
PackageEditors.OnCompilePackage:=@PackageEditorCompilePackage;
|
|
PackageEditors.OnCopyMoveFiles:=@PackageEditorCopyMoveFiles;
|
|
PackageEditors.OnCreateFpmakeFile:=@PackageEditorCreateFpmakeFile;
|
|
PackageEditors.OnCreateMakefile:=@PackageEditorCreateMakefile;
|
|
PackageEditors.OnCreateNewFile:=@PackageEditorCreateFile;
|
|
PackageEditors.OnDeleteAmbiguousFiles:=@PackageEditorDeleteAmbiguousFiles;
|
|
PackageEditors.OnDragDropTreeView:=@PackageEditorDragDropTreeView;
|
|
PackageEditors.OnDragOverTreeView:=@PackageEditorDragOverTreeView;
|
|
PackageEditors.OnShowFindInFiles:=@PackageEditorFindInFiles;
|
|
PackageEditors.OnFreeEditor:=@PackageEditorFreeEditor;
|
|
PackageEditors.OnGetIDEFileInfo:=@MainIDE.GetIDEFileState;
|
|
PackageEditors.OnInstallPackage:=@PackageEditorInstallPackage;
|
|
PackageEditors.OnOpenFile:=@MainIDE.DoOpenMacroFile;
|
|
PackageEditors.OnOpenPackage:=@PackageEditorOpenPackage;
|
|
PackageEditors.OnOpenPkgFile:=@PackageEditorOpenPkgFile;
|
|
PackageEditors.OnPublishPackage:=@PackageEditorPublishPackage;
|
|
PackageEditors.OnRevertPackage:=@PackageEditorRevertPackage;
|
|
PackageEditors.OnSavePackage:=@PackageEditorSavePackage;
|
|
PackageEditors.OnUninstallPackage:=@PackageEditorUninstallPackage;
|
|
PackageEditors.OnViewPackageSource:=@PackageEditorViewPkgSource;
|
|
|
|
// package macros
|
|
CodeToolBoss.DefineTree.MacroFunctions.AddExtended(
|
|
'PKGDIR',nil,@PackageGraph.MacroFunctionCTPkgDir);
|
|
CodeToolBoss.DefineTree.MacroFunctions.AddExtended(
|
|
'PKGSRCPATH',nil,@PackageGraph.MacroFunctionCTPkgSrcPath);
|
|
CodeToolBoss.DefineTree.MacroFunctions.AddExtended(
|
|
'PKGUNITPATH',nil,@PackageGraph.MacroFunctionCTPkgUnitPath);
|
|
CodeToolBoss.DefineTree.MacroFunctions.AddExtended(
|
|
'PKGINCPATH',nil,@PackageGraph.MacroFunctionCTPkgIncPath);
|
|
CodeToolBoss.DefineTree.MacroFunctions.AddExtended(
|
|
'PKGNAME',nil,@PackageGraph.MacroFunctionCTPkgName);
|
|
CodeToolBoss.DefineTree.MacroFunctions.AddExtended(
|
|
'PKGOUTDIR',nil,@PackageGraph.MacroFunctionCTPkgOutDir);
|
|
|
|
LazPackageDescriptors:=TLazPackageDescriptors.Create;
|
|
LazPackageDescriptors.AddDefaultPackageDescriptors;
|
|
|
|
// idle handler
|
|
Application.AddOnIdleHandler(@ApplicationIdleHandler,true);
|
|
end;
|
|
|
|
destructor TPkgManager.Destroy;
|
|
begin
|
|
FreeThenNil(LazPackageDescriptors);
|
|
PackageGraph.FreeAutoInstallDependencies;
|
|
FreeThenNil(PackageGraphExplorer);
|
|
FreeThenNil(PackageEditors);
|
|
FreeThenNil(PackageGraph);
|
|
FreeThenNil(LazPackageLinks);
|
|
FreeThenNil(PackageDependencies);
|
|
inherited Destroy;
|
|
end;
|
|
|
|
procedure TPkgManager.ConnectMainBarEvents;
|
|
begin
|
|
with MainIDEBar do begin
|
|
itmPkgNewPackage.OnClick:=@MainIDEitmPkgNewPackageClick;
|
|
itmPkgOpenLoadedPackage.OnClick:=@MainIDEitmPkgOpenLoadedPackageClicked;
|
|
itmPkgOpenPackageFile.OnClick:=@MainIDEitmPkgOpenPackageFileClick;
|
|
itmPkgOpenPackageOfCurUnit.OnClick:=@MainIDEitmPkgOpenPackageOfCurUnitClicked;
|
|
itmPkgAddCurFileToPkg.OnClick:=@MainIDEitmPkgAddCurFileToPkgClick;
|
|
itmPkgAddNewComponentToPkg.OnClick:=@MainIDEitmPkgNewComponentClick;
|
|
itmPkgPkgGraph.OnClick:=@MainIDEitmPkgPkgGraphClick;
|
|
itmPkgPackageLinks.OnClick:=@MainIDEitmPackageLinksClicked;
|
|
itmPkgEditInstallPkgs.OnClick:=@MainIDEitmPkgEditInstallPkgsClick;
|
|
end;
|
|
|
|
SetRecentPackagesMenu;
|
|
|
|
IDEWindowCreators.Add(NonModalIDEWindowNames[nmiwPkgGraphExplorer],
|
|
nil,@CreateIDEWindow,'250','200','','');
|
|
IDEWindowCreators.Add(PackageEditorWindowPrefix,
|
|
nil,@CreateIDEWindow,'250','200','','');
|
|
RegisterStandardPackageEditorMenuItems;
|
|
end;
|
|
|
|
procedure TPkgManager.ConnectSourceNotebookEvents;
|
|
begin
|
|
|
|
end;
|
|
|
|
procedure TPkgManager.SetupMainBarShortCuts;
|
|
begin
|
|
|
|
end;
|
|
|
|
procedure TPkgManager.SetRecentPackagesMenu;
|
|
begin
|
|
MainIDE.SetRecentSubMenu(itmPkgOpenRecent,
|
|
EnvironmentOptions.RecentPackageFiles,
|
|
@MainIDEitmOpenRecentPackageClicked);
|
|
end;
|
|
|
|
procedure TPkgManager.AddToMenuRecentPackages(const Filename: string);
|
|
begin
|
|
EnvironmentOptions.AddToRecentPackageFiles(Filename);
|
|
SetRecentPackagesMenu;
|
|
MainIDE.SaveEnvironment;
|
|
end;
|
|
|
|
procedure TPkgManager.SaveSettings;
|
|
begin
|
|
|
|
end;
|
|
|
|
function TPkgManager.GetDefaultSaveDirectoryForFile(const Filename: string): string;
|
|
var
|
|
APackage: TLazPackage;
|
|
PkgFile: TPkgFile;
|
|
begin
|
|
Result:='';
|
|
if FilenameIsAbsolute(Filename) then
|
|
exit(ExtractFilePath(Filename));
|
|
PkgFile:=PackageGraph.FindFileInAllPackages(Filename,true,true);
|
|
if PkgFile=nil then exit;
|
|
APackage:=PkgFile.LazPackage;
|
|
if APackage.IsVirtual or (not APackage.HasDirectory) then exit;
|
|
Result:=APackage.Directory;
|
|
end;
|
|
|
|
procedure TPkgManager.LoadInstalledPackages;
|
|
begin
|
|
IDEComponentPalette.BeginUpdate;
|
|
try
|
|
PackageGraph.LoadStaticBasePackages;
|
|
LoadStaticCustomPackages;
|
|
LoadAutoInstallPackages;
|
|
finally
|
|
IDEComponentPalette.EndUpdate;
|
|
end;
|
|
end;
|
|
|
|
procedure TPkgManager.UnloadInstalledPackages;
|
|
var
|
|
Dependency: TPkgDependency;
|
|
begin
|
|
// unbind and free auto installed packages
|
|
while PackageGraph.FirstInstallDependency<>nil do begin
|
|
Dependency:=PackageGraph.FirstInstallDependency;
|
|
Dependency.RequiredPackage:=nil;
|
|
Dependency.RemoveFromList(PackageGraph.FirstInstallDependency,pddRequires);
|
|
Dependency.Free;
|
|
end;
|
|
end;
|
|
|
|
procedure TPkgManager.ProcessCommand(Command: word; var Handled: boolean);
|
|
begin
|
|
Handled:=true;
|
|
case Command of
|
|
ecOpenPackage: MainIDEitmPkgOpenLoadedPackageClicked(Self);
|
|
ecOpenPackageFile: MainIDEitmPkgOpenPackageFileClick(Self);
|
|
ecOpenPackageOfCurUnit: MainIDEitmPkgOpenPackageOfCurUnitClicked(Self);
|
|
ecAddCurFileToPkg: MainIDEitmPkgAddCurFileToPkgClick(Self);
|
|
ecPackageGraph: MainIDEitmPkgPkgGraphClick(Self);
|
|
ecEditInstallPkgs: MainIDEitmPkgEditInstallPkgsClick(Self);
|
|
else
|
|
Handled:=false;
|
|
end;
|
|
end;
|
|
|
|
procedure TPkgManager.OnSourceEditorPopupMenu(const AddMenuItemProc: TAddMenuItemProc);
|
|
var
|
|
APackage: TIDEPackage;
|
|
begin
|
|
GetPackageOfCurrentSourceEditor(APackage);
|
|
if APackage<>nil then
|
|
AddMenuItemProc(Format(lisOpenPackage2, [APackage.Name]), true,
|
|
@OpenPackageForCurrentSrcEditFile);
|
|
end;
|
|
|
|
procedure TPkgManager.TranslateResourceStrings;
|
|
var
|
|
PkgList: TFPList;
|
|
i: Integer;
|
|
begin
|
|
PkgList:=nil;
|
|
PackageGraph.GetAllRequiredPackages(nil,
|
|
PackageGraph.FirstInstallDependency,PkgList);
|
|
if PkgList=nil then exit;
|
|
for i:=0 to PkgList.Count-1 do
|
|
if TObject(PkgList[i]) is TLazPackage then
|
|
DoTranslatePackage(TLazPackage(PkgList[i]));
|
|
PkgList.Free;
|
|
end;
|
|
|
|
procedure TPkgManager.DoTranslatePackage(APackage: TLazPackage);
|
|
var
|
|
TranslatedUnits: TStringHashList;
|
|
|
|
function UnitTranslated(const AnUnitName: string): boolean;
|
|
begin
|
|
Result:=(TranslatedUnits<>nil) and (TranslatedUnits.Find(AnUnitName)>=0);
|
|
end;
|
|
|
|
procedure TranslateUnit(const AFilename, AnUnitName: string);
|
|
begin
|
|
//DebugLn(['TranslateUnit AFilename="',AFilename,'" AnUnitName="',AnUnitName,'"']);
|
|
if TranslatedUnits=nil then
|
|
TranslatedUnits:=TStringHashList.Create(false);
|
|
TranslatedUnits.Add(AnUnitName);
|
|
TranslateUnitResourceStrings(AnUnitName,AFilename);
|
|
end;
|
|
|
|
procedure TranslateWithFileMask(APackage: TLazPackage;
|
|
const Directory, Language: string);
|
|
var
|
|
CurUnitName: string;
|
|
CurLang: string;
|
|
Files: TStrings;
|
|
Filename: String;
|
|
begin
|
|
if Language='' then exit;
|
|
Files:=nil;
|
|
try
|
|
CodeToolBoss.DirectoryCachePool.GetListing(Directory,Files,false);
|
|
for Filename in Files do begin
|
|
if GetPOFilenameParts(Filename,CurUnitName,CurLang)
|
|
and IsValidUnitName(CurUnitName)
|
|
and (CurLang=Language)
|
|
and (not UnitTranslated(CurUnitName))
|
|
and (APackage.FindUnit(CurUnitName)<>nil)
|
|
then begin
|
|
TranslateUnit(AppendPathDelim(Directory)+Filename,CurUnitName);
|
|
end;
|
|
end;
|
|
finally
|
|
Files.Free;
|
|
end;
|
|
end;
|
|
|
|
var
|
|
Directory: String;
|
|
Lang: String;
|
|
FallbackLang: String;
|
|
Language: String;
|
|
begin
|
|
//DebugLn(['TPkgManager.DoTranslatePackage ',APackage.Name,' from ', APackage.POOutputDirectory]);
|
|
if (APackage.POOutputDirectory='') then exit;
|
|
Directory:=AppendPathDelim(APackage.GetPOOutDirectory);
|
|
|
|
Language:=EnvironmentOptions.LanguageID;
|
|
if Language='' then begin
|
|
Lang:=SystemLanguageID.LanguageID;
|
|
FallbackLang:=SystemLanguageID.LanguageCode;
|
|
end else begin
|
|
Lang:=Language;
|
|
FallbackLang:='';
|
|
end;
|
|
|
|
//DebugLn(['TPkgManager.DoTranslatePackage ', APackage.Name,' from ', APackage.POOutputDirectory,', Translated=',APackage.Translated,' Lang=',Lang]);
|
|
if APackage.Translated=Lang then exit;
|
|
APackage.Translated:=Lang;
|
|
|
|
TranslatedUnits:=nil;
|
|
try
|
|
//DebugLn(['TPkgManager.DoTranslatePackage ',APackage.Name,' Directory=',Directory,' Lang=',Lang,' FallbackLang=',FallbackLang]);
|
|
TranslateWithFileMask(APackage,Directory,Lang);
|
|
if FallbackLang<>Lang then
|
|
TranslateWithFileMask(APackage,Directory,FallbackLang);
|
|
finally
|
|
TranslatedUnits.Free;
|
|
MainIDEInterface.PackageTranslated(APackage);
|
|
end;
|
|
end;
|
|
|
|
function TPkgManager.AddPackageToGraph(APackage: TLazPackage): TModalResult;
|
|
var
|
|
ConflictPkg: TLazPackage;
|
|
Link: TPackageLink;
|
|
begin
|
|
// check Package Name
|
|
if not IsValidPkgName(APackage.Name) then begin
|
|
Result:=IDEMessageDialog(lisPkgMangInvalidPackageName2,
|
|
Format(lisPkgMangThePackageNameOfTheFileIsInvalid,
|
|
[APackage.Name, LineEnding, APackage.Filename]),
|
|
mtError,[mbCancel,mbAbort]);
|
|
exit;
|
|
end;
|
|
|
|
// check if Package with same name is already loaded
|
|
ConflictPkg:=PackageGraph.FindPackageWithName(APackage.Name,nil);
|
|
if ConflictPkg<>nil then begin
|
|
if not PackageGraph.PackageCanBeReplaced(ConflictPkg,APackage) then begin
|
|
Result:=IDEMessageDialog(lisPkgMangPackageConflicts,
|
|
Format(lisPkgMangThereIsAlreadyAPackageLoadedFromFile,
|
|
[ConflictPkg.IDAsString, LineEnding, ConflictPkg.Filename, LineEnding, LineEnding]),
|
|
mtError,[mbCancel,mbAbort]);
|
|
exit;
|
|
end;
|
|
|
|
if ConflictPkg.Modified and (not ConflictPkg.ReadOnly) then begin
|
|
Result:=IDEMessageDialog(lisPkgMangSavePackage,
|
|
Format(lisPkgMangLoadingPackageWillReplacePackage, [
|
|
APackage.IDAsString, ConflictPkg.IDAsString, LineEnding,
|
|
ConflictPkg.Filename, LineEnding, LineEnding+LineEnding, ConflictPkg.Filename]),
|
|
mtConfirmation,[mbYes,mbNo,mbCancel,mbAbort]);
|
|
if Result=mrNo then Result:=mrOk;
|
|
if Result=mrYes then begin
|
|
Result:=DoSavePackage(ConflictPkg,[]);
|
|
end;
|
|
if Result<>mrOk then exit;
|
|
end;
|
|
|
|
// replace package
|
|
PackageGraph.ReplacePackage(ConflictPkg,APackage);
|
|
end else begin
|
|
// add to graph
|
|
PackageGraph.AddPackage(APackage);
|
|
end;
|
|
|
|
// save package file links
|
|
//DebugLn(['TPkgManager.AddPackageToGraph ',APackage.Name]);
|
|
Link:=LazPackageLinks.AddUserLink(APackage);
|
|
if Link<>nil then
|
|
begin
|
|
//debugln(['Hint: (lazarus) TPkgManager.AddPackageToGraph LinkLastUsed=',DateToCfgStr(Link.LastUsed,DateTimeAsCfgStrFormat),' ',dbgs(Link.Origin)]);
|
|
LazPackageLinks.SaveUserLinks;
|
|
end;
|
|
|
|
Result:=mrOk;
|
|
end;
|
|
|
|
function TPkgManager.ResolveBrokenDependenciesOnline(ABrokenDependencies: TFPList): TModalResult;
|
|
var
|
|
Dependency: TPkgDependency;
|
|
I: Integer;
|
|
PkgLinks: TList;
|
|
PkgsStr: String;
|
|
PackageLink: TPackageLink;
|
|
begin
|
|
Result := mrCancel;
|
|
PkgLinks := TList.Create;
|
|
try
|
|
PkgsStr := '';
|
|
for I := 0 to ABrokenDependencies.Count - 1 do begin
|
|
Dependency := TPkgDependency(ABrokenDependencies[i]);
|
|
PackageLink := LazPackageLinks.FindLinkWithPkgName(Dependency.AsString);
|
|
if (PackageLink <> nil) {and (PackageLink.Origin = ploOnline)} then begin
|
|
PkgLinks.Add(PackageLink);
|
|
Dependency.LoadPackageResult:=lprAvailableOnline;
|
|
if PkgsStr = '' then
|
|
PkgsStr := '"' + PackageLink.Name + '"'
|
|
else
|
|
PkgsStr := PkgsStr + ', ' + '"' + PackageLink.Name + '"';
|
|
end;
|
|
end;
|
|
if PkgLinks.Count > 0 then begin
|
|
if IDEMessageDialog(lisNotInstalledPackages, Format(lisInstallPackagesMsg,[PkgsStr]),
|
|
mtConfirmation, [mbYes, mbNo]) = mrYes then
|
|
Result := OPMInterface.InstallPackages(PkgLinks);
|
|
end;
|
|
finally
|
|
PkgLinks.Free;
|
|
end;
|
|
end;
|
|
|
|
function TPkgManager.OpenProjectDependencies(AProject: TProject;
|
|
ReportMissing: boolean): TModalResult;
|
|
var
|
|
BrokenDependencies: TFPList;
|
|
OpmRes: TModalResult;
|
|
Dependency: TPkgDependency;
|
|
IgnorePackage: TLazPackage;
|
|
begin
|
|
Result := mrOk;
|
|
OpmRes := mrOk;
|
|
|
|
Dependency:=AProject.FirstRequiredDependency;
|
|
while Dependency<>nil do begin
|
|
IgnorePackage:=PackageGraph.FindPackageWithName(Dependency.PackageName,nil);
|
|
if (IgnorePackage<>nil) and Dependency.IsCompatible(IgnorePackage) then
|
|
IgnorePackage:=nil;
|
|
PackageGraph.OpenDependency(Dependency,false,IgnorePackage);
|
|
Dependency:=Dependency.NextRequiresDependency;
|
|
end;
|
|
|
|
if ReportMissing then begin
|
|
BrokenDependencies := PackageGraph.FindAllBrokenDependencies(nil,
|
|
AProject.FirstRequiredDependency);
|
|
if Assigned(BrokenDependencies) then
|
|
begin
|
|
if Assigned(OPMInterface) then
|
|
begin
|
|
OpmRes := ResolveBrokenDependenciesOnline(BrokenDependencies);
|
|
FreeAndNil(BrokenDependencies);
|
|
BrokenDependencies := PackageGraph.FindAllBrokenDependencies(nil,
|
|
AProject.FirstRequiredDependency);
|
|
end;
|
|
Result := ShowBrokenDependenciesReport(BrokenDependencies);
|
|
BrokenDependencies.Free;
|
|
end;
|
|
end;
|
|
LazPackageLinks.SaveUserLinks;
|
|
if OpmRes = mrRetry then // mrRetry means the IDE must be rebuilt.
|
|
MainIDEInterface.DoBuildLazarus([])
|
|
end;
|
|
|
|
function TPkgManager.AddProjectDependency(AProject: TProject;
|
|
APackage: TLazPackage; OnlyTestIfPossible: boolean): TModalResult;
|
|
var
|
|
NewDependency: TPkgDependency;
|
|
ProvidingAPackage: TLazPackage;
|
|
ConflictDependency: TPkgDependency;
|
|
begin
|
|
Result:=mrCancel;
|
|
|
|
// check if there is a dependency, that requires another version
|
|
ConflictDependency:=PackageGraph.FindConflictRecursively(
|
|
AProject.FirstRequiredDependency,APackage);
|
|
if ConflictDependency<>nil then begin
|
|
DebugLn(['Error: (lazarus) [TPkgManager.AddProjectDependency] ',APackage.IDAsString,' conflicts with ',ConflictDependency.AsString]);
|
|
Result:=mrCancel;
|
|
exit;
|
|
end;
|
|
|
|
// check if the dependency is already there
|
|
if FindDependencyByNameInList(AProject.FirstRequiredDependency,pddRequires,
|
|
APackage.Name)<>nil
|
|
then begin
|
|
// package already there
|
|
Result:=mrOk;
|
|
exit;
|
|
end;
|
|
|
|
ProvidingAPackage:=PackageGraph.FindPackageProvidingName(
|
|
AProject.FirstRequiredDependency,APackage.Name);
|
|
if ProvidingAPackage<>nil then
|
|
begin
|
|
// package is already provided by another package
|
|
DebugLn(['Error: (lazarus) [TPkgManager.AddProjectDependency] ',APackage.Name,' is already provided by ',ProvidingAPackage.IDAsString]);
|
|
Result:=mrOk;
|
|
exit;
|
|
end;
|
|
|
|
if OnlyTestIfPossible then
|
|
exit(mrOk);
|
|
// add a dependency for the package to the project
|
|
NewDependency:=APackage.CreateDependencyWithOwner(AProject);
|
|
Result:=AddProjectDependency(AProject,NewDependency);
|
|
end;
|
|
|
|
function TPkgManager.AddProjectDependency(AProject: TProject;
|
|
ADependency: TPkgDependency): TModalResult;
|
|
begin
|
|
Result:=mrOk;
|
|
AProject.AddRequiredDependency(ADependency);
|
|
PackageGraph.OpenDependency(ADependency,false);
|
|
Project1.DefineTemplates.AllChanged(false);
|
|
if (ADependency.RequiredPackage<>nil)
|
|
and (not ADependency.RequiredPackage.Missing)
|
|
and ADependency.RequiredPackage.AddToProjectUsesSection
|
|
and ((ADependency.RequiredPackage.PackageType<>lptDesignTime)
|
|
or (pfUseDesignTimePackages in AProject.Flags))
|
|
then begin
|
|
AddUnitToProjectMainUsesSection(AProject,
|
|
ExtractFileNameOnly(ADependency.RequiredPackage.GetCompileSourceFilename),'');
|
|
end;
|
|
end;
|
|
|
|
function TPkgManager.AddProjectDependencies(AProject: TProject;
|
|
const Packages: string; OnlyTestIfPossible: boolean): TModalResult;
|
|
var
|
|
RequiredPackages: TStrings;
|
|
i: Integer;
|
|
PkgName: string;
|
|
APackage: TLazPackage;
|
|
begin
|
|
Result:=mrOk;
|
|
RequiredPackages:=SplitString(Packages,';');
|
|
try
|
|
for i:=0 to RequiredPackages.Count-1 do begin
|
|
PkgName:=Trim(RequiredPackages[i]);
|
|
if not IsValidPkgName(PkgName) then continue;
|
|
APackage:=PackageGraph.FindPackageWithName(PkgName,nil);
|
|
if APackage=nil then begin
|
|
DebugLn(['Error: (lazarus) [TPkgManager.AddProjectDependencies] package not found: ',PkgName]);
|
|
if OnlyTestIfPossible then
|
|
exit(mrCancel);
|
|
continue;
|
|
end;
|
|
Result:=AddProjectDependency(AProject,APackage,OnlyTestIfPossible);
|
|
if Result<>mrOk then exit;
|
|
end;
|
|
finally
|
|
RequiredPackages.Free;
|
|
end;
|
|
end;
|
|
|
|
function TPkgManager.CheckProjectHasInstalledPackages(AProject: TProject;
|
|
Interactive: boolean): TModalResult;
|
|
var
|
|
MissingUnits: TFPList;
|
|
i: Integer;
|
|
PkgFile: TPkgFile;
|
|
Msg: String;
|
|
PkgList: TObjectList;
|
|
begin
|
|
Result:=mrOk;
|
|
MissingUnits:=PackageGraph.FindNotInstalledRegisterUnits(nil,
|
|
AProject.FirstRequiredDependency);
|
|
if MissingUnits<>nil then begin
|
|
if Interactive then begin
|
|
Msg:=Format(lisProbablyYouNeedToInstallSomePackagesForBeforeConti,
|
|
[LineEnding+LineEnding, LineEnding, LineEnding+LineEnding]) + LineEnding+LineEnding;
|
|
PkgList:=TObjectList.Create(false);
|
|
try
|
|
for i:=0 to MissingUnits.Count-1 do begin
|
|
PkgFile:=TPkgFile(MissingUnits[i]);
|
|
if PkgList.IndexOf(PkgFile.LazPackage)<0 then
|
|
PkgList.Add(PkgFile.LazPackage);
|
|
Msg:=Format(lisUnitInPackage,
|
|
[Msg, PkgFile.Unit_Name, PkgFile.LazPackage.IDAsString]) + LineEnding;
|
|
end;
|
|
Result:=IDEQuestionDialog(lisPackageNeedsInstallation, Msg,
|
|
mtWarning, [mrIgnore,'Continue without install',
|
|
mrYes,'Install these packages',
|
|
mrCancel,'Cancel','IsDefault']);
|
|
if Result=mrIgnore then begin
|
|
// continue
|
|
end else if Result=mrYes then
|
|
begin
|
|
// install
|
|
AProject.AutoOpenDesignerFormsDisabled:=true;
|
|
InstallPackages(PkgList,[piiifRebuildIDE]);
|
|
Result:=mrAbort;
|
|
end else begin
|
|
// do not warn again
|
|
AProject.AutoOpenDesignerFormsDisabled:=true;
|
|
end;
|
|
finally
|
|
PkgList.Free;
|
|
end;
|
|
end else
|
|
Result:=mrCancel;
|
|
MissingUnits.Free;
|
|
end;
|
|
end;
|
|
|
|
function TPkgManager.DoNewPackage: TModalResult;
|
|
var
|
|
NewPackage: TLazPackage;
|
|
begin
|
|
Result:=mrCancel;
|
|
// create a new package with standard dependencies
|
|
NewPackage:=PackageGraph.CreateNewPackage(constNewPackageName);
|
|
PackageGraph.AddDependencyToPackage(NewPackage,
|
|
PackageGraph.FCLPackage.CreateDependencyWithOwner(NewPackage));
|
|
NewPackage.Modified:=false;
|
|
|
|
// open a package editor
|
|
PackageEditors.OpenEditor(NewPackage,true);
|
|
|
|
Result:=DoSavePackage(NewPackage,[psfSaveAs]);
|
|
end;
|
|
|
|
function TPkgManager.DoShowLoadedPkgDlg: TModalResult;
|
|
var
|
|
APackage: TLazPackage;
|
|
begin
|
|
Result:=ShowOpenLoadedPkgDlg(APackage);
|
|
if (Result<>mrOk) then exit;
|
|
Result:=DoOpenPackage(APackage,[pofAddToRecent],false);
|
|
end;
|
|
|
|
function TPkgManager.DoOpenPackage(APackage: TLazPackage;
|
|
Flags: TPkgOpenFlags; ShowAbort: boolean): TModalResult;
|
|
var
|
|
AFilename: String;
|
|
begin
|
|
AFilename:=APackage.Filename;
|
|
// revert: if possible and wanted
|
|
if (pofRevert in Flags) and (FileExistsCached(AFilename)) then
|
|
exit(DoOpenPackageFile(AFilename,Flags,ShowAbort));
|
|
// open a package editor
|
|
PackageEditors.OpenEditor(APackage,true);
|
|
PackageGraph.RebuildDefineTemplates;
|
|
// add to recent packages
|
|
if (pofAddToRecent in Flags) and FileExistsCached(AFilename) then
|
|
AddToMenuRecentPackages(AFilename);
|
|
Result:=mrOk;
|
|
end;
|
|
|
|
function TPkgManager.DoOpenPackageWithName(const APackageName: string;
|
|
Flags: TPkgOpenFlags; ShowAbort: boolean): TModalResult;
|
|
var
|
|
APackage: TLazPackage;
|
|
NewDependency: TPkgDependency;
|
|
LoadResult: TLoadPackageResult;
|
|
begin
|
|
Result:=mrCancel;
|
|
if not IsValidPkgName(APackageName) then exit;
|
|
NewDependency:=TPkgDependency.Create;
|
|
try
|
|
NewDependency.PackageName:=APackageName;
|
|
LoadResult:=PackageGraph.OpenDependency(NewDependency,ShowAbort);
|
|
if LoadResult<>lprSuccess then exit;
|
|
finally
|
|
NewDependency.Free;
|
|
end;
|
|
APackage:=PackageGraph.FindPackageWithName(APackageName,nil);
|
|
if APackage=nil then exit;
|
|
Result:=DoOpenPackage(APackage,Flags,ShowAbort);
|
|
end;
|
|
|
|
function TPkgManager.DoOpenPackageFile(AFilename: string; Flags: TPkgOpenFlags;
|
|
ShowAbort: boolean): TModalResult;
|
|
|
|
procedure DoQuestionDlg(const Caption, Message: string);
|
|
begin
|
|
if pofMultiOpen in Flags then
|
|
Result:=IDEQuestionDialog(Caption, Message,
|
|
mtError, [mrIgnore, lisPkgMangSkipThisPackage,
|
|
mrAbort])
|
|
else
|
|
Result:=IDEQuestionDialog(Caption, Message, mtError, [mrAbort])
|
|
end;
|
|
|
|
var
|
|
APackage: TLazPackage;
|
|
XMLConfig: TXMLConfig;
|
|
AlternativePkgName: String;
|
|
Code: TCodeBuffer;
|
|
OpenEditor: Boolean;
|
|
begin
|
|
// replace macros
|
|
if pofConvertMacros in Flags then
|
|
if not GlobalMacroList.SubstituteStr(AFilename) then exit(mrCancel);
|
|
|
|
AFilename:=GetPhysicalFilenameCached(CleanAndExpandFilename(AFilename),false);
|
|
|
|
// check file extension
|
|
if not ( FilenameExtIs(AFilename,'lpk',true) or (pofRevert in Flags) ) then begin
|
|
DoQuestionDlg(lisPkgMangInvalidFileExtension,
|
|
Format(lisPkgMangTheFileIsNotALazarusPackage, [AFilename]));
|
|
RemoveFromRecentList(AFilename,EnvironmentOptions.RecentPackageFiles,rltFile);
|
|
SetRecentPackagesMenu;
|
|
exit;
|
|
end;
|
|
|
|
// check filename
|
|
AlternativePkgName:=ExtractFileNameOnly(AFilename);
|
|
if (not (pofRevert in Flags))
|
|
and (not IsValidPkgName(AlternativePkgName))
|
|
then begin
|
|
DoQuestionDlg(lisPkgMangInvalidPackageFilename,
|
|
Format(lisPkgMangThePackageFileNameInIsNotAValidLazarusPackageName,
|
|
[AlternativePkgName, LineEnding, AFilename]));
|
|
RemoveFromRecentList(AFilename,EnvironmentOptions.RecentPackageFiles,rltFile);
|
|
SetRecentPackagesMenu;
|
|
exit;
|
|
end;
|
|
|
|
// add to recent packages
|
|
if pofAddToRecent in Flags then begin
|
|
AddToMenuRecentPackages(AFilename);
|
|
end;
|
|
|
|
OpenEditor:=not (pofDoNotOpenEditor in Flags);
|
|
|
|
// check if package is already loaded
|
|
APackage:=PackageGraph.FindPackageWithFilename(AFilename);
|
|
if (APackage=nil) or (pofRevert in Flags) then begin
|
|
// package not yet loaded or it should be reloaded
|
|
|
|
if (pofRevert in Flags)
|
|
and ((APackage=nil) or (APackage.Editor=nil)) then
|
|
OpenEditor:=false;
|
|
|
|
if not FileExistsUTF8(AFilename) then begin
|
|
IDEMessageDialog(lisFileNotFound,
|
|
Format(lisPkgMangFileNotFound, [AFilename]),
|
|
mtError,[mbCancel]);
|
|
RemoveFromRecentList(AFilename,EnvironmentOptions.RecentPackageFiles,rltFile);
|
|
SetRecentPackagesMenu;
|
|
Result:=mrCancel;
|
|
exit;
|
|
end;
|
|
|
|
// create a new package
|
|
Result:=mrCancel;
|
|
APackage:=TLazPackage.Create;
|
|
try
|
|
// load the package file
|
|
try
|
|
XMLConfig:=TCodeBufXMLConfig.Create(nil);
|
|
try
|
|
APackage.Filename:=AFilename;
|
|
Result:=LoadXMLConfigFromCodeBuffer(AFilename,XMLConfig,
|
|
Code,[lbfUpdateFromDisk,lbfRevert],ShowAbort);
|
|
if Result<>mrOk then exit;
|
|
APackage.LPKSource:=Code;
|
|
APackage.LoadFromXMLConfig(XMLConfig,'Package/');
|
|
finally
|
|
XMLConfig.Free;
|
|
end;
|
|
except
|
|
on E: Exception do begin
|
|
DoQuestionDlg(lisPkgMangErrorReadingPackage,
|
|
Format(lisPkgUnableToReadPackageFileError, [AFilename, LineEnding, E.Message]));
|
|
exit;
|
|
end;
|
|
end;
|
|
|
|
// newly loaded is not modified
|
|
APackage.Modified:=false;
|
|
|
|
// check if package name and file name correspond
|
|
if (SysUtils.CompareText(AlternativePkgName,APackage.Name)<>0) then begin
|
|
Result:=IDEMessageDialog(lisPkgMangFilenameDiffersFromPackagename,
|
|
Format(lisPkgMangTheFilenameDoesNotCorrespondToThePackage,
|
|
[ExtractFileName(AFilename), APackage.Name, LineEnding, AlternativePkgName]),
|
|
mtConfirmation,[mbYes,mbCancel,mbAbort]);
|
|
if Result<>mrYes then exit;
|
|
APackage.Name:=AlternativePkgName;
|
|
end;
|
|
|
|
// integrate it into the graph
|
|
Result:=AddPackageToGraph(APackage);
|
|
finally
|
|
if Result<>mrOk then APackage.Free;
|
|
end;
|
|
end;
|
|
|
|
if OpenEditor then
|
|
Result:=DoOpenPackage(APackage,[],ShowAbort)
|
|
else
|
|
Result:=mrOk;
|
|
|
|
LazPackageLinks.SaveUserLinks;
|
|
|
|
// the source editor highlighting depends on the compiler mode
|
|
MainIDEInterface.UpdateHighlighters;
|
|
end;
|
|
|
|
function TPkgManager.IsPackageEditorForm(AForm: TCustomForm): boolean;
|
|
begin
|
|
Result:=AForm is TPackageEditorForm;
|
|
end;
|
|
|
|
procedure TPkgManager.OpenHiddenModifiedPackages;
|
|
var
|
|
i: Integer;
|
|
APackage: TLazPackage;
|
|
begin
|
|
for i:=0 to PackageGraph.Count-1 do begin
|
|
APackage:=PackageGraph.Packages[i];
|
|
if (APackage.Editor=nil) and APackage.Modified
|
|
and (APackage.UserIgnoreChangeStamp<>APackage.ChangeStamp) then begin
|
|
PackageEditors.OpenEditor(APackage,false);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
function TPkgManager.DoSavePackage(APackage: TLazPackage;
|
|
Flags: TPkgSaveFlags): TModalResult;
|
|
var
|
|
XMLConfig: TCodeBufXMLConfig;
|
|
PkgLink: TPackageLink;
|
|
Code: TCodeBuffer;
|
|
begin
|
|
// do not save during compilation
|
|
if not (MainIDE.ToolStatus in [itNone,itDebugger]) then begin
|
|
Result:=mrAbort;
|
|
exit;
|
|
end;
|
|
|
|
if APackage.IsVirtual then Include(Flags,psfSaveAs);
|
|
|
|
if not ( (psfSaveAs in Flags) or APackage.ReadOnly or APackage.Modified
|
|
or FileExistsCached(APackage.Filename)
|
|
or (APackage.UserIgnoreChangeStamp<>APackage.ChangeStamp )) then
|
|
begin
|
|
Result:=mrOk;
|
|
exit;
|
|
end;
|
|
|
|
// save new or changed files
|
|
Result:=SavePackageFiles(APackage);
|
|
if Result<>mrOk then exit;
|
|
|
|
// warn about missing files
|
|
Result:=WarnAboutMissingPackageFiles(APackage);
|
|
if Result<>mrOk then exit;
|
|
|
|
// save editor files to codetools
|
|
MainIDE.SaveSourceEditorChangesToCodeCache(nil);
|
|
|
|
// save package
|
|
if (psfSaveAs in Flags) then begin
|
|
Result:=DoShowSavePackageAsDialog(APackage);
|
|
if Result<>mrOk then exit;
|
|
end;
|
|
|
|
// backup old file
|
|
Result:=BuildBoss.BackupFileForWrite(APackage.Filename);
|
|
if Result=mrAbort then exit;
|
|
|
|
// delete ambiguous files
|
|
Result:=BuildBoss.DeleteAmbiguousFiles(APackage.Filename);
|
|
if Result=mrAbort then exit;
|
|
|
|
// save
|
|
try
|
|
XMLConfig:=TCodeBufXMLConfig.Create(nil);
|
|
try
|
|
XMLConfig.Clear;
|
|
XMLConfig.KeepFileAttributes:=true;
|
|
APackage.SaveToXMLConfig(XMLConfig,'Package/');
|
|
Code:=nil;
|
|
Result:=SaveXMLConfigToCodeBuffer(APackage.Filename,XMLConfig,Code,true);
|
|
if Result<>mrOk then exit;
|
|
APackage.LPKSource:=Code;
|
|
PkgLink:=LazPackageLinks.AddUserLink(APackage);
|
|
if PkgLink<>nil then begin
|
|
PkgLink.LPKFileDate:=FileDateToDateTimeDef(FileAgeUTF8(APackage.Filename));
|
|
PkgLink.LPKFileDateValid:=true;
|
|
LazPackageLinks.SaveUserLinks;
|
|
end;
|
|
finally
|
|
XMLConfig.Free;
|
|
end;
|
|
except
|
|
on E: Exception do begin
|
|
Result:=IDEMessageDialog(lisPkgMangErrorWritingPackage,
|
|
Format(lisPkgMangUnableToWritePackageToFileError,
|
|
[APackage.IDAsString, LineEnding, APackage.Filename, LineEnding, E.Message]),
|
|
mtError,[mbAbort,mbCancel]);
|
|
exit;
|
|
end;
|
|
end;
|
|
|
|
// success
|
|
APackage.Modified:=false;
|
|
// add to recent
|
|
if (psfSaveAs in Flags) then begin
|
|
AddToMenuRecentPackages(APackage.Filename);
|
|
end;
|
|
|
|
if APackage.Editor<>nil then
|
|
APackage.Editor.UpdateAll(true);
|
|
Result:=mrOk;
|
|
end;
|
|
|
|
procedure TPkgManager.DoShowPackageGraph(Show: boolean);
|
|
begin
|
|
if PackageGraphExplorer=nil then begin
|
|
PackageGraphExplorer:=TPkgGraphExplorerDlg.Create(Application);
|
|
PackageGraphExplorer.OnOpenPackage:=@PackageGraphExplorerOpenPackage;
|
|
PackageGraphExplorer.OnOpenProject:=@PackageGraphExplorerOpenProject;
|
|
PackageGraphExplorer.OnUninstallPackage:=@PackageGraphExplorerUninstallPackage;
|
|
end;
|
|
if Show then
|
|
IDEWindowCreators.ShowForm(PackageGraphExplorer,true);
|
|
end;
|
|
|
|
function TPkgManager.DoCloseAllPackageEditors: TModalResult;
|
|
var
|
|
APackage: TLazPackage;
|
|
begin
|
|
while PackageEditors.Count>0 do begin
|
|
APackage:=PackageEditors.Editors[PackageEditors.Count-1].LazPackage;
|
|
Result:=DoClosePackageEditor(APackage);
|
|
if Result<>mrOk then exit;
|
|
end;
|
|
Result:=mrOk;
|
|
end;
|
|
|
|
procedure TPkgManager.DoShowPackageGraphPathList(PathList: TFPList);
|
|
begin
|
|
DoShowPackageGraph(true);
|
|
PackageGraphExplorer.ShowPath(PathList);
|
|
end;
|
|
|
|
function TPkgManager.ShowBrokenDependenciesReport(Dependencies: TFPList): TModalResult;
|
|
var
|
|
Msg: String;
|
|
i: Integer;
|
|
ADependency: TPkgDependency;
|
|
begin
|
|
Result:=mrOk;
|
|
if (Dependencies=nil) or (Dependencies.Count=0) then exit;
|
|
if Dependencies.Count=1 then
|
|
Msg:=lisPkgMangTheFollowingPackageFailedToLoad
|
|
else
|
|
Msg:=lisPkgMangTheFollowingPackagesFailedToLoad;
|
|
Msg:=Msg+LineEnding+LineEnding;
|
|
for i:=0 to Dependencies.Count-1 do begin
|
|
ADependency:=TPkgDependency(Dependencies[i]);
|
|
Msg:=Msg+ADependency.AsString+LineEnding;
|
|
end;
|
|
|
|
// give some hints
|
|
ADependency:=TPkgDependency(Dependencies[0]);
|
|
if (ADependency.Owner is TProject) then begin
|
|
// broken dependency used by project -> show project inspector
|
|
if ADependency.Owner=Project1 then begin
|
|
MainIDE.DoShowProjectInspector;
|
|
Msg:=Format(lisSeeProjectProjectInspector, [Msg]);
|
|
end;
|
|
end;
|
|
|
|
Result:=IDEMessageDialog(lisMissingPackages, Msg, mtError, [mbOk]);
|
|
end;
|
|
|
|
function TPkgManager.CheckUserSearchPaths(aCompilerOptions: TBaseCompilerOptions
|
|
): TModalResult;
|
|
var
|
|
aPackage: TLazPackage;
|
|
CurUnitPath: String;
|
|
CurSrcPath: String;
|
|
CurOutPath: String;
|
|
SrcDirToPkg: TFilenameToPointerTree;
|
|
|
|
function GetPkgOfSrcDirToPkg(Dir: string): TLazPackage;
|
|
var
|
|
MaskType: TSPMaskType;
|
|
Item: PStringToPointerTreeItem;
|
|
CurDir: String;
|
|
begin
|
|
MaskType:=GetSPMaskType(Dir);
|
|
if MaskType=TSPMaskType.None then
|
|
begin
|
|
Result:=TLazPackage(SrcDirToPkg[Dir]);
|
|
exit;
|
|
end;
|
|
Dir:=ExtractFilePath(Dir);
|
|
for Item in SrcDirToPkg do
|
|
begin
|
|
CurDir:=Item^.Name;
|
|
if FileIsInSPDirectory(AppendPathDelim(CurDir),Dir,MaskType) then
|
|
exit(TLazPackage(Item^.Value));
|
|
end;
|
|
Result:=nil;
|
|
end;
|
|
|
|
function CheckPathContainsDirOfOtherPkg(Option: TParsedCompilerOptString
|
|
): TModalResult;
|
|
var
|
|
aSearchPath: String;
|
|
p: Integer;
|
|
Dir: String;
|
|
OtherPackage: TLazPackage;
|
|
aType: String;
|
|
s: String;
|
|
begin
|
|
Result:=mrOk;
|
|
case Option of
|
|
pcosUnitPath:
|
|
begin
|
|
aType:='other unit files search path (aka unit path)';
|
|
aSearchPath:=CurUnitPath;
|
|
end;
|
|
pcosSrcPath:
|
|
begin
|
|
aType:='other sources path';
|
|
aSearchPath:=CurSrcPath;
|
|
end;
|
|
else
|
|
exit;
|
|
end;
|
|
p:=1;
|
|
repeat
|
|
Dir:=GetNextDirectoryInSearchPath(aSearchPath,p);
|
|
if Dir='' then break;
|
|
Dir:=ChompPathDelim(Dir);
|
|
if not FilenameIsAbsolute(Dir) then continue;
|
|
OtherPackage:=GetPkgOfSrcDirToPkg(Dir);
|
|
if (OtherPackage<>nil) and (OtherPackage<>aPackage) then begin
|
|
// search path contains source directory of another package
|
|
if Option=pcosIncludePath then;
|
|
s:=aType+' of "'+aCompilerOptions.GetOwnerName+'" contains "'+Dir+'", which belongs to package "'+OtherPackage.Name+'"';
|
|
debugln(['Warning: (lazarus) [TPkgManager.CheckUserSearchPaths]: ',s]);
|
|
{ ToDo: find out
|
|
- which path it is in the unparsed path
|
|
- if there is already the dependency
|
|
- if the dependency can be added
|
|
and ask the user to delete the path and to add the dependency
|
|
|
|
if the user has already answered this question in the past, just warn }
|
|
// warn user
|
|
IDEMessagesWindow.AddCustomMessage(mluWarning,s);
|
|
exit;
|
|
end;
|
|
until false;
|
|
end;
|
|
|
|
function CheckOutPathContainsSources: TModalResult;
|
|
var
|
|
Files: TStrings;
|
|
i: Integer;
|
|
aFilename: String;
|
|
s: String;
|
|
begin
|
|
Result:=mrOk;
|
|
if aPackage=nil then exit;
|
|
if not FilenameIsAbsolute(CurOutPath) then exit;
|
|
Files:=nil;
|
|
CodeToolBoss.DirectoryCachePool.GetListing(CurOutPath,Files,false);
|
|
try
|
|
for i:=0 to Files.Count-1 do begin
|
|
aFilename:=Files[i];
|
|
if FilenameIsPascalUnit(aFilename) then begin
|
|
// warning: packages output path contain unit source
|
|
s:=Format(lisOutputDirectoryOfContainsPascalUnitSource, [
|
|
aCompilerOptions.GetOwnerName, aFilename]);
|
|
debugln(['Warning: (lazarus) [CheckOutPathContainsSources]: ',s]);
|
|
{ ToDo: if the OutPath is not the default: ask user and change it }
|
|
IDEMessagesWindow.AddCustomMessage(mluWarning,s);
|
|
exit;
|
|
end;
|
|
end;
|
|
finally
|
|
Files.Free;
|
|
end;
|
|
end;
|
|
|
|
function CheckSrcPathIsInUnitPath: TModalResult;
|
|
// warn: SrcPath should not contain directories of UnitPath
|
|
var
|
|
p: Integer;
|
|
UnparsedUnitPath: String;
|
|
UnparsedSrcPath: String;
|
|
Dir: String;
|
|
s: String;
|
|
begin
|
|
Result:=mrOk;
|
|
UnparsedUnitPath:=aCompilerOptions.OtherUnitFiles;
|
|
UnparsedSrcPath:=aCompilerOptions.SrcPath;
|
|
p:=1;
|
|
repeat
|
|
Dir:=GetNextDirectoryInSearchPath(UnparsedSrcPath,p);
|
|
if Dir='' then exit;
|
|
if SearchDirectoryInMaskedSearchPath(UnparsedUnitPath,Dir)>0 then begin
|
|
// Note: when changing this, update TQuickFixSrcPathOfPkgContains_OpenPkg
|
|
s:=Format(lisOtherSourcesPathOfPackageContainsDirectoryWhichIsA, [
|
|
aCompilerOptions.GetOwnerName, Dir]);
|
|
debugln(['Warning: (lazarus) [CheckSrcPathIsInUnitPath]: ',s]);
|
|
{ ToDo: ask user and remove dir from unit path }
|
|
IDEMessagesWindow.AddCustomMessage(mluWarning,s);
|
|
exit;
|
|
end;
|
|
until false;
|
|
end;
|
|
|
|
begin
|
|
Result:=mrOk;
|
|
if aCompilerOptions.CompilerPath='' then exit; // not a normal Pascal project
|
|
|
|
aPackage:=nil;
|
|
if aCompilerOptions.Owner is TLazPackage then
|
|
aPackage:=TLazPackage(aCompilerOptions.Owner)
|
|
else if not (aCompilerOptions.Owner is TProject) then
|
|
exit;
|
|
|
|
if (aPackage<>nil) and (aPackage.AutoUpdate=pupManually) then exit;
|
|
|
|
CurUnitPath:=aCompilerOptions.ParsedOpts.GetParsedValue(pcosUnitPath);
|
|
CurSrcPath:=aCompilerOptions.ParsedOpts.GetParsedValue(pcosSrcPath);
|
|
CurOutPath:=aCompilerOptions.ParsedOpts.GetParsedValue(pcosOutputDir);
|
|
//debugln(['TPkgManager.CheckUserSearchPaths CompOpts=',aCompilerOptions.GetOwnerName,' UnitPath="',CurUnitPath,'" IncPath="',CurIncPath,'" SrcPath="',CurSrcPath,'" OutPath="',CurOutPath,'"']);
|
|
|
|
// create mapping source-directory to package
|
|
SrcDirToPkg:=PackageGraph.GetMapSourceDirectoryToPackage(aPackage);
|
|
try
|
|
Result:=CheckPathContainsDirOfOtherPkg(pcosUnitPath);
|
|
if Result<>mrOk then exit;
|
|
|
|
Result:=CheckPathContainsDirOfOtherPkg(pcosSrcPath);
|
|
if Result<>mrOk then exit;
|
|
|
|
Result:=CheckOutPathContainsSources;
|
|
if Result<>mrOk then exit;
|
|
|
|
Result:=CheckSrcPathIsInUnitPath;
|
|
if Result<>mrOk then exit;
|
|
|
|
// ToDo: check if SrcPath is in inherited SrcPath
|
|
// ToDo: check if UnitPath is in inherited UnitPath
|
|
finally
|
|
SrcDirToPkg.Free;
|
|
end;
|
|
end;
|
|
|
|
procedure TPkgManager.LazarusSrcDirChanged;
|
|
const
|
|
LazDirMacro = '$(LazarusDir)';
|
|
var
|
|
NewLazarusSrcDir: String;
|
|
OldLazarusSrcDir: String;
|
|
VisitedPkgs: TStringToStringTree;
|
|
ReloadPkgs: TStringList;
|
|
|
|
function PkgInOldLazarusDir(APackage: TLazPackage): boolean;
|
|
begin
|
|
Result:=FileIsInPath(APackage.Filename,OldLazarusSrcDir)
|
|
or PackageGraph.IsCompiledInBasePackage(APackage.Name)
|
|
or (SysUtils.CompareText(copy(APackage.Filename,1,length(LazDirMacro)),LazDirMacro)=0)
|
|
end;
|
|
|
|
procedure GatherLazarusSrcPackages(APackage: TLazPackage);
|
|
var
|
|
ADependency: TPkgDependency;
|
|
begin
|
|
if APackage=nil then exit;
|
|
if VisitedPkgs.Contains(APackage.Name) then exit;
|
|
VisitedPkgs[APackage.Name]:='1';
|
|
// search the dependencies first
|
|
ADependency:=APackage.FirstRequiredDependency;
|
|
while ADependency<>nil do begin
|
|
GatherLazarusSrcPackages(ADependency.RequiredPackage);
|
|
ADependency:=ADependency.NextRequiresDependency;
|
|
end;
|
|
if PkgInOldLazarusDir(APackage) then begin
|
|
// this package was from the old lazarus source directory
|
|
ReloadPkgs.Add(APackage.Name);
|
|
end;
|
|
end;
|
|
|
|
function ReloadPkg(APackage: TLazPackage): boolean;
|
|
var
|
|
Link: TPackageLink;
|
|
MsgResult: TModalResult;
|
|
Filename: String;
|
|
begin
|
|
Result:=true;
|
|
if APackage=nil then exit;
|
|
if not PkgInOldLazarusDir(APackage) then exit;
|
|
// this package was from the old lazarus source directory
|
|
// check if there is a package in the new version
|
|
Link:=LazPackageLinks.FindLinkWithPkgName(APackage.Name);
|
|
if Link<>nil then begin
|
|
Filename:=TrimFilename(Link.LPKFilename);
|
|
if not FilenameIsAbsolute(Filename) then
|
|
Filename:=AppendPathDelim(NewLazarusSrcDir)+Filename;
|
|
if FileIsInPath(Filename,NewLazarusSrcDir)
|
|
and FileExistsUTF8(Filename) then
|
|
begin
|
|
DebugLn(['Hint: (lazarus) [TPkgManager.LazarusSrcDirChanged] load: ',Filename]);
|
|
// open package in new lazarus source directory
|
|
MsgResult:=DoOpenPackageFile(Filename,[pofDoNotOpenEditor,pofRevert],true);
|
|
if MsgResult=mrAbort then exit(false);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
var
|
|
i: Integer;
|
|
begin
|
|
if PackageGraph=nil then exit;
|
|
OldLazarusSrcDir:=FLastLazarusSrcDir;
|
|
NewLazarusSrcDir:=EnvironmentOptions.GetParsedLazarusDirectory;
|
|
FLastLazarusSrcDir:=NewLazarusSrcDir;
|
|
if CompareFilenames(OldLazarusSrcDir,NewLazarusSrcDir)=0 then exit;
|
|
debugln(['Hint: (lazarus) [TPkgManager.LazarusSrcDirChanged] loading new lpl files from ',
|
|
LazPackageLinks.GetGlobalLinkDirectory]);
|
|
if LazPackageLinks.IsUpdating then
|
|
debugln(['Warning: (lazarus) [TPkgManager.LazarusSrcDirChanged] inconsistency: LazPackageLinks are locked']);
|
|
LazPackageLinks.UpdateGlobalLinks;
|
|
|
|
VisitedPkgs:=TStringToStringTree.Create(false);
|
|
ReloadPkgs:=TStringList.Create;
|
|
try
|
|
// collect candidates
|
|
for i:=0 to PackageGraph.Count-1 do
|
|
GatherLazarusSrcPackages(PackageGraph.Packages[i]);
|
|
// reload
|
|
for i:=0 to ReloadPkgs.Count-1 do
|
|
ReloadPkg(PackageGraph.FindPackageWithName(ReloadPkgs[i],nil));
|
|
finally
|
|
ReloadPkgs.Free;
|
|
VisitedPkgs.Free;
|
|
end;
|
|
end;
|
|
|
|
function TPkgManager.GetPackageCount: integer;
|
|
begin
|
|
Result:=PackageGraph.Count;
|
|
end;
|
|
|
|
function TPkgManager.GetPackages(Index: integer): TIDEPackage;
|
|
begin
|
|
Result:=PackageGraph.Packages[Index];
|
|
end;
|
|
|
|
function TPkgManager.FindPackageWithName(const PkgName: string;
|
|
IgnorePackage: TIDEPackage): TIDEPackage;
|
|
begin
|
|
Result:=PackageGraph.FindPackageWithName(PkgName, IgnorePackage as TLazPackage);
|
|
end;
|
|
|
|
function TPkgManager.FindInstalledPackageWithUnit(const AnUnitName: string
|
|
): TIDEPackage;
|
|
var
|
|
PkgFile: TPkgFile;
|
|
begin
|
|
PkgFile:=PackageGraph.FindUnitInInstalledPackages(AnUnitName, true);
|
|
if PkgFile=nil then
|
|
Result:=nil
|
|
else
|
|
Result:=PkgFile.LazPackage;
|
|
end;
|
|
|
|
function TPkgManager.IsPackageInstalled(const PkgName: string): TIDEPackage;
|
|
var
|
|
LazPackage: TLazPackage;
|
|
begin
|
|
Result := nil;
|
|
LazPackage:=PackageGraph.FindPackageWithName(PkgName, nil);
|
|
if (LazPackage<>nil) and (LazPackage.Installed<>pitNope) then
|
|
Result:=LazPackage
|
|
end;
|
|
|
|
function TPkgManager.RedirectPackageDependency(APackage: TIDEPackage): TIDEPackage;
|
|
begin
|
|
Result:=APackage;
|
|
if Result=PackageGraph.LCLBasePackage then begin
|
|
// Older Lazarus does not have a LCLBase and a component does not work
|
|
// without an LCLBase implementation, so we have to use LCL instead.
|
|
Result:=PackageGraph.LCLPackage;
|
|
end;
|
|
end;
|
|
|
|
function TPkgManager.DoCompileProjectDependencies(AProject: TProject;
|
|
Flags: TPkgCompileFlags): TModalResult;
|
|
var
|
|
CompilePolicy: TPackageUpdatePolicy;
|
|
begin
|
|
// check graph for cycles and broken dependencies
|
|
if not (pcfDoNotCompileDependencies in Flags) then begin
|
|
Result:=CheckPackageGraphForCompilation(nil,
|
|
AProject.FirstRequiredDependency,
|
|
AProject.Directory,false);
|
|
if Result<>mrOk then exit;
|
|
end;
|
|
|
|
// save all open files
|
|
if not (pcfDoNotSaveEditorFiles in Flags) then begin
|
|
Result:=MainIDE.DoSaveForBuild(crCompile);
|
|
if Result<>mrOk then exit;
|
|
end;
|
|
|
|
PackageGraph.BeginUpdate(false);
|
|
try
|
|
// automatically compile required packages
|
|
if not (pcfDoNotCompileDependencies in Flags) then begin
|
|
CompilePolicy:=pupAsNeeded;
|
|
if pcfCompileDependenciesClean in Flags then
|
|
CompilePolicy:=pupOnRebuildingAll;
|
|
Result:=PackageGraph.CompileRequiredPackages(nil,
|
|
AProject.FirstRequiredDependency,
|
|
not (pfUseDesignTimePackages in AProject.Flags),
|
|
CompilePolicy);
|
|
if Result<>mrOk then exit;
|
|
end;
|
|
finally
|
|
PackageGraph.EndUpdate;
|
|
end;
|
|
|
|
Result:=mrOk;
|
|
end;
|
|
|
|
function TPkgManager.DoCompilePackage(APackage: TIDEPackage;
|
|
Flags: TPkgCompileFlags; ShowAbort: boolean): TModalResult;
|
|
var
|
|
OldToolStatus: TLazToolStatus;
|
|
begin
|
|
Result:=mrCancel;
|
|
|
|
DebugLn('Hint: (lazarus) compile package ',APackage.IDAsString,' Flags=',PkgCompileFlagsToString(Flags));
|
|
|
|
if APackage.IsVirtual then exit;
|
|
|
|
Result:=MainIDE.PrepareForCompile;
|
|
if Result<>mrOk then exit;
|
|
Assert(APackage is TLazPackage, 'TPkgManager.DoCompilePackage: APackage is not TLazPackage');
|
|
|
|
// check graph for circles and broken dependencies
|
|
if not (pcfDoNotCompileDependencies in Flags) then begin
|
|
Result:=CheckPackageGraphForCompilation(TLazPackage(APackage),nil,APackage.Directory,ShowAbort);
|
|
if Result<>mrOk then exit;
|
|
end;
|
|
|
|
// save all open files
|
|
{$IFDEF VerboseSaveForBuild}
|
|
DebugLn('TPkgManager.DoCompilePackage ',APackage.IDAsString,' Flags=',PkgCompileFlagsToString(Flags));
|
|
{$ENDIF}
|
|
if not (pcfDoNotSaveEditorFiles in Flags) then begin
|
|
Result:=MainIDE.DoSaveForBuild(crCompile);
|
|
if Result<>mrOk then exit;
|
|
end;
|
|
|
|
// check user search paths
|
|
Result:=CheckUserSearchPaths(TBaseCompilerOptions(APackage.LazCompilerOptions));
|
|
if Result<>mrOk then exit;
|
|
|
|
// compile
|
|
if LazarusIDE<>nil then begin
|
|
OldToolStatus:=LazarusIDE.ToolStatus;
|
|
LazarusIDE.ToolStatus:=itBuilder;
|
|
end;
|
|
Result:=PackageGraph.CompilePackage(TLazPackage(APackage),Flags,false);
|
|
if LazarusIDE<>nil then
|
|
LazarusIDE.ToolStatus:=OldToolStatus;
|
|
end;
|
|
|
|
function TPkgManager.DoCreatePackageMakefile(APackage: TLazPackage;
|
|
ShowAbort: boolean): TModalResult;
|
|
begin
|
|
Result:=DoCompilePackage(APackage,[pcfDoNotCompileDependencies,
|
|
pcfDoNotCompilePackage,pcfCreateMakefile],ShowAbort);
|
|
end;
|
|
|
|
function TPkgManager.DoCreatePackageFpmakefile(APackage: TLazPackage;
|
|
ShowAbort: boolean): TModalResult;
|
|
begin
|
|
Result:=DoCompilePackage(APackage,[pcfDoNotCompileDependencies,
|
|
pcfDoNotCompilePackage,pcfCreateFpmakeFile],ShowAbort);
|
|
end;
|
|
|
|
function TPkgManager.OnRenameFile(const OldFilename, NewFilename: string;
|
|
IsPartOfProject: boolean): TModalResult;
|
|
var
|
|
OldPackage: TLazPackage;
|
|
OldPkgFile: TPkgFile;
|
|
NewPkgFile: TPkgFile;
|
|
begin
|
|
Result:=mrOk;
|
|
if (OldFilename=NewFilename) then
|
|
exit;
|
|
//debugln('TPkgManager.OnRenameFile A OldFilename="',OldFilename,'" New="',NewFilename,'"');
|
|
OldPkgFile:=PackageGraph.FindFileInAllPackages(OldFilename,true,not IsPartOfProject);
|
|
if (OldPkgFile=nil) or (OldPkgFile.LazPackage.ReadOnly) then
|
|
exit;
|
|
OldPackage:=OldPkgFile.LazPackage;
|
|
debugln('Hint: (lazarus) [TPkgManager.OnRenameFile] OldPackage="',OldPackage.Name);
|
|
NewPkgFile:=PackageGraph.FindFileInAllPackages(NewFilename,true,false);
|
|
if (NewPkgFile<>nil) and (OldPackage<>NewPkgFile.LazPackage) then exit;
|
|
|
|
OldPkgFile.Filename:=NewFilename;
|
|
if OldPackage.Editor<>nil then
|
|
OldPackage.Editor.UpdateAll(true);
|
|
OldPackage.Modified:=true;
|
|
end;
|
|
|
|
{------------------------------------------------------------------------------
|
|
function TPkgManager.FindIncludeFileInProjectDependencies(Project1: TProject;
|
|
const Filename: string): string;
|
|
|
|
Search filename in the include paths of all required packages
|
|
------------------------------------------------------------------------------}
|
|
function TPkgManager.FindIncludeFileInProjectDependencies(aProject: TProject;
|
|
const Filename: string): string;
|
|
var
|
|
APackage: TLazPackage;
|
|
IncPath: String;
|
|
PkgList: TFPList;
|
|
i: Integer;
|
|
begin
|
|
Result:='';
|
|
if FilenameIsAbsolute(Filename) then
|
|
exit(Filename);
|
|
PkgList:=nil;
|
|
PackageGraph.GetAllRequiredPackages(nil,aProject.FirstRequiredDependency,
|
|
PkgList,[pirCompileOrder]);
|
|
if PkgList=nil then exit;
|
|
try
|
|
for i:=0 to PkgList.Count-1 do begin
|
|
APackage:=TLazPackage(PkgList[i]);
|
|
IncPath:=APackage.CompilerOptions.GetIncludePath(false);
|
|
Result:=SearchFileInSearchPath(Filename,APackage.Directory,IncPath);
|
|
if Result<>'' then exit;
|
|
end;
|
|
finally
|
|
PkgList.Free;
|
|
end;
|
|
end;
|
|
|
|
type
|
|
TPackageIterateHelper = class
|
|
public
|
|
PackageNames: TStrings;
|
|
PackageList: TStrings;
|
|
procedure AddDependency(APackageID: TLazPackageID);
|
|
end;
|
|
|
|
procedure TPackageIterateHelper.AddDependency(APackageID: TLazPackageID);
|
|
begin
|
|
{ are we looking for this package? }
|
|
if PackageNames.IndexOf(APackageID.Name)<0 then
|
|
Exit;
|
|
{ was the package already added? }
|
|
if PackageList.IndexOf(APackageID.Name)>=0 then
|
|
Exit;
|
|
PackageList.AddObject(APackageID.Name,APackageID);
|
|
end;
|
|
|
|
function TPkgManager.AddUnitDepsForCompClasses(const UnitFilename: string;
|
|
ComponentClasses: TClassList; Quiet: boolean): TModalResult;
|
|
var
|
|
UnitBuf: TCodeBuffer;
|
|
UnitNames: TStringList;
|
|
MissingDependencies: TOwnerPackageArray;
|
|
|
|
function LoadAndParseUnitBuf: TModalResult;
|
|
begin
|
|
if not CodeToolBoss.GatherExternalChanges then begin
|
|
Result:=mrCancel;
|
|
MainIDE.DoJumpToCodeToolBossError;
|
|
exit;
|
|
end;
|
|
UnitBuf:=CodeToolBoss.LoadFile(UnitFilename,false,false);
|
|
if UnitBuf=nil then begin
|
|
Result:=IDEMessageDialog(lisErrorLoadingFile,
|
|
Format(lisLoadingFailed, [UnitFilename]),
|
|
mtError,[mbCancel,mbAbort]);
|
|
exit;
|
|
end;
|
|
Result:=mrOk;
|
|
end;
|
|
|
|
function RemoveExistingUnitnames: TModalResult;
|
|
var
|
|
ImplementationUsesSection: TStrings;
|
|
MainUsesSection: TStrings;
|
|
j: LongInt;
|
|
i: Integer;
|
|
begin
|
|
Result:=LoadAndParseUnitBuf;
|
|
if Result<>mrOk then exit;
|
|
MainUsesSection:=nil;
|
|
ImplementationUsesSection:=nil;
|
|
try
|
|
if not CodeToolBoss.FindUsedUnitNames(UnitBuf,MainUsesSection,
|
|
ImplementationUsesSection)
|
|
then begin
|
|
MainIDE.DoJumpToCodeToolBossError;
|
|
exit;
|
|
end;
|
|
for i:=0 to MainUsesSection.Count-1 do begin
|
|
j:=UnitNames.IndexOf(MainUsesSection[i]);
|
|
if j>=0 then UnitNames.Delete(j);
|
|
end;
|
|
finally
|
|
MainUsesSection.Free;
|
|
ImplementationUsesSection.Free;
|
|
end;
|
|
Result:=mrOk;
|
|
end;
|
|
|
|
function AskUser: TModalResult;
|
|
var
|
|
UsesAdditions: String;
|
|
UnitOwner: TObject;
|
|
RequiredPackage: TLazPackageID;
|
|
i: Integer;
|
|
PackageAdditions: String;
|
|
Msg: String;
|
|
begin
|
|
UsesAdditions:='';
|
|
for i:=0 to UnitNames.Count-1 do begin
|
|
if UsesAdditions<>'' then UsesAdditions:=UsesAdditions+', ';
|
|
UsesAdditions:=UsesAdditions+UnitNames[i];
|
|
end;
|
|
//DebugLn('TPkgManager.AddUnitDepsForCompClasses UsesAdditions=',UsesAdditions);
|
|
PackageAdditions:='';
|
|
if MissingDependencies<>nil then begin
|
|
for i:=0 to MissingDependencies.Count-1 do begin
|
|
UnitOwner:=MissingDependencies[i];
|
|
RequiredPackage:=MissingDependencies.Objects[i];
|
|
if RequiredPackage is TIDEPackage then
|
|
RequiredPackage:=RedirectPackageDependency(TIDEPackage(RequiredPackage));
|
|
if UnitOwner is TProject then begin
|
|
PackageAdditions:=Format(lisPkgMangAddingNewDependencyForProjectPackage,
|
|
[PackageAdditions, TProject(UnitOwner).GetTitle, RequiredPackage.Name]) + LineEnding+LineEnding;
|
|
end else if UnitOwner is TLazPackage then begin
|
|
PackageAdditions:=Format(lisPkgMangAddingNewDependencyForPackagePackage,
|
|
[PackageAdditions, TLazPackage(UnitOwner).Name, RequiredPackage.Name]) + LineEnding+LineEnding;
|
|
end;
|
|
end;
|
|
end;
|
|
//DebugLn('TPkgManager.AddUnitDepsForCompClasses PackageAdditions=',PackageAdditions);
|
|
Msg:='';
|
|
if UsesAdditions<>'' then begin
|
|
Msg:=Format(lisPkgMangTheFollowingUnitsWillBeAddedToTheUsesSectionOf,
|
|
[Msg, LineEnding, UnitFilename, LineEnding, UsesAdditions]) + LineEnding+LineEnding;
|
|
end;
|
|
if PackageAdditions<>'' then begin
|
|
Msg:=Msg+PackageAdditions;
|
|
end;
|
|
if Msg<>'' then begin
|
|
Result:=IDEMessageDialog(lisConfirmChanges,Msg,mtConfirmation,[mbOk,mbAbort]);
|
|
exit;
|
|
end;
|
|
Result:=mrOk;
|
|
end;
|
|
|
|
function AddDependencies: TModalResult;
|
|
var
|
|
i: Integer;
|
|
UnitOwner: TObject;
|
|
RequiredPackage: TLazPackageID;
|
|
PkgDependency: TPkgDependency;
|
|
begin
|
|
if MissingDependencies<>nil then begin
|
|
for i:=0 to MissingDependencies.Count-1 do begin
|
|
UnitOwner:=MissingDependencies[i];
|
|
RequiredPackage:=MissingDependencies.Objects[i];
|
|
if RequiredPackage is TIDEPackage then
|
|
RequiredPackage:=RedirectPackageDependency(TIDEPackage(RequiredPackage));
|
|
if UnitOwner is TProject then begin
|
|
DebugLn('Hint: (lazarus) [TPkgManager.AddUnitDepsForCompClasses] Adding Project Dependency ',TProject(UnitOwner).GetTitle,' -> ',RequiredPackage.Name);
|
|
if RequiredPackage is TLazPackage then
|
|
AddProjectDependency(TProject(UnitOwner),TLazPackage(RequiredPackage))
|
|
else begin
|
|
PkgDependency:=TPkgDependency.Create;
|
|
PkgDependency.PackageName:=RequiredPackage.Name;
|
|
AddProjectDependency(TProject(UnitOwner),PkgDependency);
|
|
end;
|
|
end else if UnitOwner is TLazPackage then begin
|
|
DebugLn('Hint: (lazarus) [TPkgManager.AddUnitDepsForCompClasses] Adding Package Dependency ',TLazPackage(UnitOwner).Name,' -> ',RequiredPackage.Name);
|
|
AddPackageDependency(TLazPackage(UnitOwner),RequiredPackage.Name);
|
|
end;
|
|
end;
|
|
end;
|
|
Result:=mrOk;
|
|
end;
|
|
|
|
function AddUsedUnits: TModalResult;
|
|
var
|
|
i: Integer;
|
|
begin
|
|
Result:=LoadAndParseUnitBuf;
|
|
if Result<>mrOk then exit;
|
|
for i:=0 to UnitNames.Count-1 do begin
|
|
DebugLn('Hint: (lazarus) [TPkgManager.AddUnitDepsForCompClasses] Extending Uses ',
|
|
UnitBuf.Filename,' ',UnitNames[i]);
|
|
if not CodeToolBoss.AddUnitToMainUsesSection(UnitBuf,UnitNames[i],'') then
|
|
MainIDE.DoJumpToCodeToolBossError;
|
|
end;
|
|
Result:=mrOk;
|
|
end;
|
|
|
|
var
|
|
Dependencies: TPackagePackageArray;
|
|
UnitInfo: TUnitInfo;
|
|
begin
|
|
UnitNames:=nil;
|
|
Dependencies:=nil;
|
|
MissingDependencies:=nil;
|
|
try
|
|
Result:=GetUnitsAndDepsForComps(ComponentClasses, Dependencies, UnitNames);
|
|
if Result<>mrOk then exit;
|
|
// Frame instances are not registered components, UnitNames is not assigned.
|
|
// Find the frame from the project units.
|
|
if UnitNames=nil then begin
|
|
UnitNames:=TStringList.Create;
|
|
Assert(ComponentClasses.Count=1, 'TPkgManager.AddUnitDepsForCompClasses: '
|
|
+ IntToStr(ComponentClasses.Count) + ' frame classes requested.');
|
|
UnitInfo:=Project1.UnitWithComponentClass(TComponentClass(ComponentClasses[0]));
|
|
if UnitInfo=nil then exit(mrCancel);
|
|
UnitNames.Add(UnitInfo.Unit_Name);
|
|
end;
|
|
if (Dependencies<>nil) then
|
|
begin
|
|
Result:=FilterMissingDepsForUnit(UnitFilename,Dependencies,MissingDependencies);
|
|
if Result<>mrOk then exit;
|
|
end;
|
|
|
|
Result:=RemoveExistingUnitnames;
|
|
if Result<>mrOk then exit;
|
|
|
|
if (UnitNames.Count=0) // no change needed
|
|
and ((MissingDependencies=nil) or (MissingDependencies.Count=0)) then exit;
|
|
|
|
if not Quiet then begin
|
|
Result:=AskUser;
|
|
if Result<>mrOk then exit;
|
|
end;
|
|
|
|
Result:=AddDependencies;
|
|
if Result<>mrOk then exit;
|
|
|
|
Result:=AddUsedUnits;
|
|
if Result<>mrOk then exit;
|
|
|
|
Result:=mrOk;
|
|
finally
|
|
UnitNames.Free;
|
|
Dependencies.Free;
|
|
MissingDependencies.Free;
|
|
end;
|
|
end;
|
|
|
|
function TPkgManager.GetUnitsAndDepsForComps(ComponentClasses: TClassList;
|
|
out PackageList: TPackagePackageArray; out UnitList: TStringList): TModalResult;
|
|
// returns a list of packages and units needed to use the Component in the unit
|
|
var
|
|
CurPackages: TStringListUTF8Fast;
|
|
|
|
procedure AddPkgDep(PkgFile: TPkgFile);
|
|
var
|
|
RequiredPackage: TLazPackageID;
|
|
begin
|
|
if PkgFile=nil then exit;
|
|
RequiredPackage:=PkgFile.LazPackage;
|
|
RequiredPackage:=RedirectPackageDependency(TIDEPackage(RequiredPackage));
|
|
if RequiredPackage=nil then exit;
|
|
CurPackages.Add(RequiredPackage.Name);
|
|
end;
|
|
|
|
var
|
|
CurClassID: Integer;
|
|
CurUnitIdx, CurPackageIdx: Integer;
|
|
CurCompClass: TClass;
|
|
CurRegComp: TRegisteredComponent;
|
|
PkgFile: TPkgFile;
|
|
CurUnitName: String;
|
|
CurCompReqs: TComponentRequirements;
|
|
ExistingPkgs, AllPackages, CurUnitNames: TStringListUTF8Fast;
|
|
Helper: TPackageIterateHelper;
|
|
begin
|
|
Result:=mrCancel;
|
|
PackageList:=nil;
|
|
UnitList:=nil;
|
|
ExistingPkgs:=nil;
|
|
AllPackages:=TStringListUTF8Fast.Create;
|
|
AllPackages.Sorted:=True;
|
|
AllPackages.Duplicates:=dupIgnore;
|
|
CurPackages:=nil;
|
|
CurUnitNames:=TStringListUTF8Fast.Create;
|
|
try
|
|
for CurClassID:=0 to ComponentClasses.Count-1 do
|
|
begin
|
|
CurCompClass:=ComponentClasses[CurClassID];
|
|
CurRegComp:=IDEComponentPalette.FindRegComponent(CurCompClass);
|
|
if CurRegComp is TPkgComponent then
|
|
begin
|
|
CurUnitName:='';
|
|
CurUnitNames.Clear;
|
|
CurCompReqs:=nil;
|
|
if UnitList=nil then
|
|
begin
|
|
UnitList:=TStringListUTF8Fast.Create;
|
|
UnitList.Sorted:=True;
|
|
UnitList.Duplicates:=dupIgnore;
|
|
end;
|
|
if CurPackages=nil then
|
|
begin
|
|
CurPackages:=TStringListUTF8Fast.Create;
|
|
CurPackages.Sorted:=True;
|
|
CurPackages.Duplicates:=dupIgnore;
|
|
end else
|
|
CurPackages.Clear;
|
|
|
|
if CurRegComp.ComponentClass<>nil then
|
|
begin
|
|
CurUnitName:=CurRegComp.ComponentClass.UnitName;
|
|
CurCompReqs:=GetComponentRequirements(CurRegComp.ComponentClass);
|
|
end;
|
|
//DebugLn(['TPkgManager.GetUnitsAndDepsForComps: CurCompClass=',DbgSName(CurCompClass),' CurUnitName=',CurUnitName,' CurCompReq=',DbgSName(CurCompReqs)]);
|
|
if CurUnitName='' then
|
|
CurUnitName:=CurRegComp.GetUnitName;
|
|
//Assert(CurUnitNames.IndexOf(CurUnitName)<0,
|
|
// 'TPkgManager.GetUnitsAndDepsForComps: Name already in CurUnitNames.');
|
|
CurUnitNames.Add(CurUnitName);
|
|
if CurCompReqs<>nil then
|
|
CurCompReqs.RequiredUnits(CurUnitNames);
|
|
for CurUnitIdx:=0 to CurUnitNames.Count-1 do
|
|
begin
|
|
CurUnitName:=CurUnitNames[CurUnitIdx];
|
|
UnitList.Add(CurUnitName);
|
|
PkgFile:=PackageGraph.FindUnitInAllPackages(CurUnitName,true);
|
|
AddPkgDep(PkgFile);
|
|
end;
|
|
// dont forget to add the designtime package
|
|
AddPkgDep(TPkgComponent(CurRegComp).PkgFile);
|
|
if CurCompReqs<>nil then
|
|
CurCompReqs.RequiredPkgs(CurPackages);// let addons add extra pkgs
|
|
AllPackages.AddStrings(CurPackages);
|
|
end;
|
|
end; // for CurClassID:=...
|
|
if AllPackages.Count>0 then
|
|
begin
|
|
ExistingPkgs:=TStringListUTF8Fast.Create;
|
|
ExistingPkgs.Sorted:=True;
|
|
ExistingPkgs.Duplicates:=dupIgnore;
|
|
Helper:=TPackageIterateHelper.Create;
|
|
try
|
|
Helper.PackageNames:=AllPackages;
|
|
Helper.PackageList:=ExistingPkgs;
|
|
PackageGraph.IteratePackages(fpfSearchAllExisting,@Helper.AddDependency);
|
|
finally
|
|
Helper.Free;
|
|
end;
|
|
PackageList:=TPackagePackageArray.Create;
|
|
for CurPackageIdx:=0 to ExistingPkgs.Count-1 do
|
|
PackageList.Add(TLazPackageID(ExistingPkgs.Objects[CurPackageIdx]));
|
|
end;
|
|
finally
|
|
CurUnitNames.Free;
|
|
AllPackages.Free;
|
|
CurPackages.Free;
|
|
ExistingPkgs.Free;
|
|
end;
|
|
Result:=mrOk;
|
|
end;
|
|
|
|
function TPkgManager.FilterMissingDepsForUnit(const UnitFilename: string;
|
|
InputPackageList: TPackagePackageArray;
|
|
out OutputPackageList: TOwnerPackageArray): TModalResult;
|
|
// returns a list of packages that are not yet used by the project the unit belongs to
|
|
var
|
|
UnitOwners: TFPList;
|
|
UnitOwner: TObject;
|
|
FirstDependency: TPkgDependency;
|
|
CurOwnerID, CurPackageIdx: Integer;
|
|
RequiredPackage: TLazPackageID;
|
|
begin
|
|
Result:=mrCancel;
|
|
OutputPackageList:=nil;
|
|
if (InputPackageList=nil) or (InputPackageList.Count=0) then
|
|
Exit(mrOK);
|
|
UnitOwners:=GetOwnersOfUnit(UnitFilename);
|
|
if (UnitOwners<>nil) then begin
|
|
for CurOwnerID:=0 to UnitOwners.Count-1 do begin
|
|
UnitOwner:=TObject(UnitOwners[CurOwnerID]);
|
|
if UnitOwner is TProject then
|
|
FirstDependency:=TProject(UnitOwner).FirstRequiredDependency
|
|
else if UnitOwner is TLazPackage then
|
|
FirstDependency:=TLazPackage(UnitOwner).FirstRequiredDependency
|
|
else
|
|
FirstDependency:=nil;
|
|
for CurPackageIdx:=0 to InputPackageList.Count-1 do begin
|
|
RequiredPackage:=InputPackageList.Items[CurPackageIdx];
|
|
if (RequiredPackage<>nil)
|
|
and (RequiredPackage<>UnitOwner)
|
|
and (FindCompatibleDependencyInList(FirstDependency,pddRequires,RequiredPackage)=nil)
|
|
and (PackageGraph.FindPackageProvidingName(FirstDependency,RequiredPackage.Name)=nil)
|
|
then begin
|
|
if OutputPackageList=nil then
|
|
OutputPackageList:=TOwnerPackageArray.Create;
|
|
OutputPackageList.AddObject(UnitOwner,RequiredPackage);
|
|
//debugln(['TPkgManager.FilterMissingDependenciesForUnit A ',UnitOwner.ClassName,' ',RequiredPackage.Name]);
|
|
//if TObject(OutputPackageList[OutputPackageList.Count-1])<>UnitOwner then RaiseGDBException('A');
|
|
//if TObject(OutputPackageList.Objects[OutputPackageList.Count-1])<>RequiredPackage then RaiseGDBException('B');
|
|
end;
|
|
end;
|
|
end;
|
|
UnitOwners.Free;
|
|
end else begin
|
|
DebugLn(['Warning: (lazarus) [TPkgManager.FilterMissingDependenciesForUnit] unit has no owner: ',UnitFilename]);
|
|
end;
|
|
Result:=mrOk;
|
|
end;
|
|
|
|
function TPkgManager.GetOwnersOfUnit(const UnitFilename: string): TFPList;
|
|
begin
|
|
Result:=GetPossibleOwnersOfUnit(UnitFilename,[]);
|
|
end;
|
|
|
|
procedure TPkgManager.ExtendOwnerListWithUsedByOwners(OwnerList: TFPList);
|
|
// use items (packages and projects) in OwnerList as leaves and create the
|
|
// list of all packages and projects using them.
|
|
// The result will be the topologically sorted list of projects and packages
|
|
// using the projects/packages in OwnerList, beginning with the top levels.
|
|
var
|
|
AddedNonPackages: TFPList;
|
|
|
|
procedure AddUsedByOwners(ADependenyOwner: TObject);
|
|
var
|
|
LazPackage: TLazPackage;
|
|
Dependency: TPkgDependency;
|
|
begin
|
|
if ADependenyOwner is TProject then begin
|
|
if AddedNonPackages.IndexOf(ADependenyOwner)>=0 then exit;
|
|
AddedNonPackages.Add(ADependenyOwner);
|
|
OwnerList.Add(ADependenyOwner);
|
|
end else if ADependenyOwner is TLazPackage then begin
|
|
LazPackage:=TLazPackage(ADependenyOwner);
|
|
if lpfVisited in LazPackage.Flags then exit;
|
|
LazPackage.Flags:=LazPackage.Flags+[lpfVisited];
|
|
Dependency:=LazPackage.FirstUsedByDependency;
|
|
while Dependency<>nil do begin
|
|
AddUsedByOwners(Dependency.Owner);
|
|
Dependency:=Dependency.NextUsedByDependency;
|
|
end;
|
|
OwnerList.Add(LazPackage);
|
|
end;
|
|
end;
|
|
|
|
var
|
|
i: Integer;
|
|
OldOwnerList: TFPList;
|
|
begin
|
|
OldOwnerList:=TFPList.Create;
|
|
for i:=0 to OwnerList.Count-1 do
|
|
OldOwnerList.Add(OwnerList[i]);
|
|
OwnerList.Clear;
|
|
AddedNonPackages:=TFPList.Create;
|
|
PackageGraph.MarkAllPackagesAsNotVisited;
|
|
for i:=0 to OldOwnerList.Count-1 do
|
|
AddUsedByOwners(TObject(OldOwnerList[i]));
|
|
AddedNonPackages.Free;
|
|
OldOwnerList.Free;
|
|
end;
|
|
|
|
function TPkgManager.GetSourceFilesOfOwners(OwnerList: TFPList): TStrings;
|
|
|
|
procedure AddFile(TheOwner: TObject; const Filename: string);
|
|
begin
|
|
if Result=nil then
|
|
Result:=TStringList.Create;
|
|
Result.AddObject(Filename,TheOwner);
|
|
end;
|
|
|
|
var
|
|
CurOwner: TObject;
|
|
CurPackage: TLazPackage;
|
|
CurPkgFile: TPkgFile;
|
|
CurProject: TProject;
|
|
CurUnit: TUnitInfo;
|
|
i: Integer;
|
|
j: Integer;
|
|
begin
|
|
Result:=nil;
|
|
if OwnerList=nil then exit;
|
|
for i:=0 to OwnerList.Count-1 do begin
|
|
CurOwner:=TObject(OwnerList[i]);
|
|
if CurOwner is TLazPackage then begin
|
|
CurPackage:=TLazPackage(CurOwner);
|
|
for j:=0 to CurPackage.FileCount-1 do begin
|
|
CurPkgFile:=CurPackage.Files[j];
|
|
if CurPkgFile.FileType in PkgFileUnitTypes then
|
|
AddFile(CurOwner,CurPkgFile.GetFullFilename);
|
|
end;
|
|
end else if CurOwner is TProject then begin
|
|
CurProject:=TProject(CurOwner);
|
|
CurUnit:=CurProject.FirstPartOfProject;
|
|
while CurUnit<>nil do begin
|
|
if FilenameIsPascalSource(CurUnit.Filename) then
|
|
AddFile(CurOwner,CurUnit.Filename);
|
|
CurUnit:=CurUnit.NextPartOfProject;
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
function TPkgManager.GetUnitsOfOwners(OwnerList: TFPList;
|
|
Flags: TPkgIntfGatherUnitTypes): TStrings;
|
|
var
|
|
Units: TFilenameToPointerTree;
|
|
Graph: TUsesGraph;
|
|
|
|
procedure AddUnit(ExpFilename: string);
|
|
begin
|
|
if not FileExistsCached(ExpFilename) then exit;
|
|
if Units.Contains(ExpFilename) then exit;
|
|
Units[ExpFilename]:=nil;
|
|
end;
|
|
|
|
procedure AddStartModule(ExpFilename: string);
|
|
begin
|
|
AddUnit(ExpFilename);
|
|
Graph.AddStartUnit(ExpFilename);
|
|
end;
|
|
|
|
var
|
|
i, j: Integer;
|
|
CurOwner: TObject;
|
|
CurProject: TProject;
|
|
CurPackage: TLazPackage;
|
|
ProjFile: TLazProjectFile;
|
|
PkgFile: TPkgFile;
|
|
Completed: boolean;
|
|
Node: TAVLTreeNode;
|
|
UGUnit: TUGUnit;
|
|
begin
|
|
debugln(['TPkgManager.GetUnitsOfOwners piguListed=',piguListed in Flags,' piguUsed=',piguUsed in Flags,' piguAllUsed=',piguAllUsed in Flags]);
|
|
Result:=TStringList.Create;
|
|
if (OwnerList=nil) or (OwnerList.Count=0) then exit;
|
|
|
|
Units:=TFilenameToPointerTree.Create(false);
|
|
Graph:=TUsesGraph.Create;
|
|
try
|
|
|
|
for i:=0 to OwnerList.Count-1 do
|
|
begin
|
|
CurOwner:=TObject(OwnerList[i]);
|
|
if CurOwner is TProject then
|
|
begin
|
|
CurProject:=TProject(CurOwner);
|
|
if (pfMainUnitIsPascalSource in CurProject.Flags)
|
|
and (CurProject.MainUnitInfo<>nil) then
|
|
AddStartModule(CurProject.MainUnitInfo.GetFullFilename);
|
|
if piguListed in Flags then
|
|
begin
|
|
for j:=0 to CurProject.FileCount-1 do
|
|
begin
|
|
ProjFile:=CurProject.Files[j];
|
|
if not FilenameIsPascalUnit(ProjFile.Filename) then continue;
|
|
AddStartModule(ProjFile.GetFullFilename);
|
|
end;
|
|
end;
|
|
end else if CurOwner is TLazPackage then
|
|
begin
|
|
CurPackage:=TLazPackage(CurOwner);
|
|
if piguListed in Flags then
|
|
begin
|
|
for j:=0 to CurPackage.FileCount-1 do
|
|
begin
|
|
PkgFile:=CurPackage.Files[j];
|
|
if not (PkgFile.FileType in PkgFileRealUnitTypes) then continue;
|
|
AddStartModule(PkgFile.GetFullFilename);
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
if Units.Count=0 then
|
|
begin
|
|
debugln(['TPkgManager.GetUnitsOfOwners no start modules END']);
|
|
exit; // no start modules
|
|
end;
|
|
|
|
if [piguUsed,piguAllUsed]*Flags<>[] then
|
|
begin
|
|
// parse units recursively
|
|
Graph.AddSystemUnitAsTarget;
|
|
if piguAllUsed in Flags then
|
|
begin
|
|
// gather all used units
|
|
end else if piguUsed in Flags then
|
|
begin
|
|
// ignore units of other packages
|
|
for i:=0 to PackageGraph.Count-1 do
|
|
begin
|
|
CurPackage:=PackageGraph[i];
|
|
if OwnerList.IndexOf(CurPackage)>=0 then continue;
|
|
for j:=0 to CurPackage.FileCount-1 do
|
|
begin
|
|
PkgFile:=CurPackage.Files[j];
|
|
if not (PkgFile.FileType in PkgFileRealUnitTypes) then continue;
|
|
Graph.AddIgnoreUnit(PkgFile.GetFullFilename);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
// parse
|
|
Graph.Parse(true,Completed);
|
|
if Completed then ;
|
|
|
|
// add parsed units
|
|
Node:=Graph.FilesTree.FindLowest;
|
|
while Node<>nil do
|
|
begin
|
|
UGUnit:=TUGUnit(Node.Data);
|
|
if Graph.IgnoreFilesTree.Find(UGUnit)=nil then
|
|
Units[UGUnit.Filename]:=nil;
|
|
Node:=Node.Successor;
|
|
end;
|
|
end;
|
|
|
|
Units.GetNames(Result);
|
|
|
|
finally
|
|
Graph.Free;
|
|
Units.Free;
|
|
end;
|
|
end;
|
|
|
|
function TPkgManager.GetPossibleOwnersOfUnit(const UnitFilename: string;
|
|
Flags: TPkgIntfOwnerSearchFlags): TFPList;
|
|
var
|
|
SrcDir: String;// ExtractFilePath(UnitFilename);
|
|
|
|
procedure SearchInProject(AProject: TProject);
|
|
var
|
|
BaseDir: String;
|
|
ProjectDirs: String;
|
|
Add: Boolean;
|
|
begin
|
|
if AProject=nil then exit;
|
|
Add:=false;
|
|
|
|
// check if in units
|
|
if not (piosfExcludeOwned in Flags) then begin
|
|
//DebugLn(['SearchInProject ',AProject.ProjectInfoFile,' UnitFilename=',UnitFilename]);
|
|
if (CompareFilenames(UnitFilename,AProject.ProjectInfoFile)=0)
|
|
or (AProject.UnitInfoWithFilename(UnitFilename,[pfsfOnlyProjectFiles])<>nil)
|
|
then
|
|
Add:=true;
|
|
end;
|
|
|
|
BaseDir:=ExtractFilePath(AProject.ProjectInfoFile);
|
|
|
|
// check if in virtual project
|
|
if (not Add)
|
|
and (piosfIncludeSourceDirectories in Flags)
|
|
and (BaseDir='')
|
|
and (ExtractFilePath(UnitFilename)='') then
|
|
Add:=true;
|
|
|
|
if (not Add)
|
|
and (piosfIncludeSourceDirectories in Flags)
|
|
and FilenameIsAbsolute(UnitFilename)
|
|
and (BaseDir<>'') then begin
|
|
// search in project source directories
|
|
ProjectDirs:=AProject.LazCompilerOptions.OtherUnitFiles+';.';
|
|
if IDEMacros.CreateAbsoluteSearchPath(ProjectDirs,BaseDir) then begin
|
|
if FindPathInSearchPath(PChar(SrcDir),length(SrcDir),
|
|
PChar(ProjectDirs),length(ProjectDirs))<>nil
|
|
then
|
|
Add:=true;
|
|
end;
|
|
end;
|
|
|
|
if Add then
|
|
Result.Add(AProject);
|
|
end;
|
|
|
|
var
|
|
PkgFile: TPkgFile;
|
|
CurPackage: TLazPackage;
|
|
i: Integer;
|
|
begin
|
|
//DebugLn(['TPkgManager.GetPossibleOwnersOfUnit ',UnitFilename]);
|
|
Result:=TFPList.Create;
|
|
|
|
SrcDir:=ExtractFilePath(UnitFilename);
|
|
|
|
// ToDo: create a cache
|
|
|
|
SearchInProject(Project1);
|
|
|
|
// find all packages owning file
|
|
if piosfIncludeSourceDirectories in Flags then begin
|
|
PackageGraph.FindPossibleOwnersOfUnit(UnitFilename,Result);
|
|
end else if not (piosfExcludeOwned in Flags) then begin
|
|
PkgFile:=PackageGraph.FindFileInAllPackages(UnitFilename,true,true);
|
|
if (PkgFile<>nil) and (PkgFile.LazPackage<>nil) then
|
|
Result.Add(PkgFile.LazPackage);
|
|
//debugln(['TPkgManager.GetPossibleOwnersOfUnit ',UnitFilename,' ',PkgFile<>nil,' ',(PkgFile<>nil) and (PkgFile.LazPackage<>nil),' Result.Count=',Result.Count]);
|
|
// check package source files (they usually do not have a TPkgFile)
|
|
for i:=0 to PackageGraph.Count-1 do begin
|
|
CurPackage:=PackageGraph.Packages[i];
|
|
if ((CompareFilenames(UnitFilename,CurPackage.GetSrcFilename)=0)
|
|
or (CompareFilenames(UnitFilename,CurPackage.Filename)=0))
|
|
and (Result.IndexOf(CurPackage)<0) then
|
|
Result.Add(CurPackage);
|
|
end;
|
|
end;
|
|
|
|
// clean up
|
|
if Result.Count=0 then
|
|
FreeThenNil(Result);
|
|
end;
|
|
|
|
function TPkgManager.GetPackageOfCurrentSourceEditor(out APackage: TIDEPackage): TPkgFile;
|
|
var
|
|
SrcEdit: TSourceEditor;
|
|
begin
|
|
Result:=nil;
|
|
APackage:=nil;
|
|
SrcEdit:=SourceEditorManager.GetActiveSE;
|
|
if SrcEdit=nil then exit;
|
|
Result := TPkgFile(GetPackageOfSourceEditor(APackage, SrcEdit));
|
|
end;
|
|
|
|
function TPkgManager.GetPackageOfSourceEditor(out APackage: TIDEPackage;
|
|
ASrcEdit: TObject): TLazPackageFile;
|
|
var
|
|
Filename: String;
|
|
i: Integer;
|
|
begin
|
|
Result:=nil;
|
|
APackage:=nil;
|
|
if ASrcEdit=nil then exit;
|
|
Filename:=TSourceEditor(ASrcEdit).FileName;
|
|
Result:=SearchFile(Filename,[],nil);
|
|
if Result<>nil then begin
|
|
APackage:=Result.LazPackage;
|
|
exit;
|
|
end;
|
|
for i:=0 to PackageGraph.Count-1 do begin
|
|
APackage:=PackageGraph[i];
|
|
if CompareFilenames(TLazPackage(APackage).GetSrcFilename,FileName)=0 then
|
|
exit;
|
|
end;
|
|
APackage:=nil;
|
|
end;
|
|
|
|
function TPkgManager.IsOwnerDependingOnPkg(AnOwner: TObject;
|
|
const PkgName: string; out DependencyOwner: TObject): boolean;
|
|
var
|
|
FirstDep: TPkgDependency;
|
|
Dep: TPkgDependency;
|
|
begin
|
|
Result:=false;
|
|
DependencyOwner:=nil;
|
|
if (AnOwner=nil) or (PkgName='') then exit;
|
|
if AnOwner is TProject then
|
|
FirstDep:=TProject(AnOwner).FirstRequiredDependency
|
|
else if AnOwner is TLazPackage then begin
|
|
if CompareDottedIdentifiers(PChar(TLazPackage(AnOwner).Name),
|
|
PChar(PkgName))=0
|
|
then begin
|
|
DependencyOwner:=AnOwner;
|
|
exit(true);
|
|
end;
|
|
FirstDep:=TLazPackage(AnOwner).FirstRequiredDependency;
|
|
end else
|
|
exit(false);
|
|
if PackageGraph=nil then exit;
|
|
Dep:=PackageGraph.FindDependencyRecursively(FirstDep,PkgName);
|
|
if Dep=nil then exit;
|
|
DependencyOwner:=Dep.Owner;
|
|
Result:=true;
|
|
end;
|
|
|
|
procedure TPkgManager.GetRequiredPackages(AnOwner: TObject; out PkgList: TFPList;
|
|
Flags: TPkgIntfRequiredFlags);
|
|
var
|
|
Dependency: TPkgDependency;
|
|
begin
|
|
PkgList:=nil;
|
|
Dependency:=nil;
|
|
if AnOwner is TProject then
|
|
Dependency:=TProject(AnOwner).FirstRequiredDependency
|
|
else if AnOwner is TLazPackage then
|
|
Dependency:=TLazPackage(AnOwner).FirstRequiredDependency
|
|
else if AnOwner=PkgBoss then
|
|
Dependency:=PackageGraph.FirstInstallDependency;
|
|
if Dependency=nil then exit;
|
|
PackageGraph.GetAllRequiredPackages(nil,Dependency,PkgList,Flags);
|
|
end;
|
|
|
|
function TPkgManager.AddDependencyToOwners(OwnerList: TFPList;
|
|
APackage: TIDEPackage; OnlyTestIfPossible: boolean): TModalResult;
|
|
var
|
|
i: Integer;
|
|
Item: TObject;
|
|
NewDependency: TPkgDependency;
|
|
ADependency: TPkgDependency;
|
|
r: TModalResult;
|
|
Pkg: TLazPackage;
|
|
begin
|
|
Pkg:=APackage as TLazPackage;
|
|
if not OnlyTestIfPossible then begin
|
|
Result:=AddDependencyToOwners(OwnerList,APackage,true);
|
|
if Result<>mrOk then exit;
|
|
end;
|
|
|
|
Result:=mrCancel;
|
|
for i:=0 to OwnerList.Count-1 do begin
|
|
Item:=TObject(OwnerList[i]);
|
|
if Item=APackage then continue;
|
|
if Item is TProject then begin
|
|
Result:=AddProjectDependency(TProject(Item),Pkg,OnlyTestIfPossible);
|
|
if Result<>mrOk then exit;
|
|
end
|
|
else if Item is TLazPackage then begin
|
|
NewDependency:=TPkgDependency.Create;
|
|
try
|
|
NewDependency.PackageName:=APackage.Name;
|
|
r:=TPkgFileCheck.AddingDependency(TLazPackage(Item),NewDependency,false);
|
|
if r=mrCancel then exit;
|
|
if (not OnlyTestIfPossible) and (r<>mrIgnore) then begin
|
|
ADependency:=NewDependency;
|
|
NewDependency:=nil;
|
|
PackageGraph.AddDependencyToPackage(TLazPackage(Item),ADependency);
|
|
end;
|
|
finally
|
|
NewDependency.Free;
|
|
end;
|
|
end;
|
|
end;
|
|
Result:=mrOk;
|
|
end;
|
|
|
|
function TPkgManager.DoOpenPkgFile(PkgFile: TPkgFile): TModalResult;
|
|
var
|
|
Filename: String;
|
|
begin
|
|
if (PkgFile.FileType=pftVirtualUnit) then
|
|
Filename:=FindVirtualUnitSource(PkgFile)
|
|
else
|
|
Filename:=PkgFile.GetFullFilename;
|
|
if Filename<>'' then
|
|
Result:=MainIDE.DoOpenEditorFile(Filename,-1,-1,
|
|
[ofOnlyIfExists,ofAddToRecent,ofRegularFile]);
|
|
end;
|
|
|
|
function TPkgManager.FindVirtualUnitSource(PkgFile: TPkgFile): string;
|
|
begin
|
|
Result:='';
|
|
if (PkgFile.FileType=pftVirtualUnit)
|
|
and (PkgFile.LazPackage<>nil)
|
|
and (not FileExistsUTF8(PkgFile.Filename)) then begin
|
|
Result:=MainIDE.FindSourceFile(PkgFile.GetShortFilename(false),
|
|
PkgFile.LazPackage.Directory,[]);
|
|
end;
|
|
end;
|
|
|
|
function TPkgManager.SearchFile(const AFilename: string;
|
|
SearchFlags: TSearchIDEFileFlags; InObject: TObject): TPkgFile;
|
|
var
|
|
APackage: TLazPackage;
|
|
CurFilename: String;
|
|
begin
|
|
if InObject is TLazPackage then begin
|
|
APackage:=TLazPackage(InObject);
|
|
CurFilename:=AFilename;
|
|
APackage.ShortenFilename(CurFilename,true);
|
|
Result:=APackage.SearchShortFilename(CurFilename,SearchFlags);
|
|
if Result<>nil then exit;
|
|
end;
|
|
if not (siffDoNotCheckAllPackages in SearchFlags) then begin
|
|
Result := PackageGraph.FindFileInAllPackages(AFilename, True, True);
|
|
if Result<>nil then exit;
|
|
end;
|
|
Result:=nil;
|
|
end;
|
|
|
|
function TPkgManager.SearchUnitInDesigntimePackages(const AnUnitName: string;
|
|
InObject: TObject): TPkgFile;
|
|
var
|
|
i: Integer;
|
|
APackage: TLazPackage;
|
|
begin
|
|
if InObject is TLazPackage then begin
|
|
APackage:=TLazPackage(InObject);
|
|
Result:=APackage.FindUnit(AnUnitName);
|
|
if Result<>nil then exit;
|
|
end;
|
|
for i:=0 to PackageGraph.Count-1 do begin
|
|
APackage:=PackageGraph[i];
|
|
if APackage.Installed=pitNope then continue;
|
|
Result:=APackage.FindUnit(AnUnitName);
|
|
if Result<>nil then exit;
|
|
end;
|
|
Result:=nil;
|
|
end;
|
|
|
|
function TPkgManager.ShowFindInPackageFilesDlg(APackage: TLazPackage): TModalResult;
|
|
var
|
|
Dlg: TLazFindInFilesDialog;
|
|
begin
|
|
Result:=mrOk;
|
|
Dlg:=FindInFilesDialog;
|
|
Dlg.FindInSearchPath(APackage.SourceDirectories.CreateSearchPathFromAllFiles);
|
|
end;
|
|
|
|
function TPkgManager.AddDependencyToUnitOwners(const OwnedFilename,
|
|
RequiredUnitname: string): TModalResult;
|
|
var
|
|
OwnersList: TFPList;
|
|
RequiredPkgFile: TPkgFile;
|
|
RequiredPkg: TLazPackage;
|
|
begin
|
|
Result:=mrCancel;
|
|
//DebugLn(['TPkgManager.AddDependencyToUnitOwners RequiredUnitname=',RequiredUnitname,' OwnedFilename=',OwnedFilename]);
|
|
|
|
// find needed package
|
|
RequiredPkgFile:=SearchUnitInDesigntimePackages(RequiredUnitName,nil);
|
|
if RequiredPkgFile=nil then begin
|
|
DebugLn(['Note: (lazarus) [TPkgManager.AddDependencyToUnitOwners] unit not in designtime package: ',RequiredUnitName]);
|
|
exit;
|
|
end;
|
|
RequiredPkg:=RequiredPkgFile.LazPackage;
|
|
|
|
// find owners of unit (package or project)
|
|
OwnersList:=GetOwnersOfUnit(OwnedFilename);
|
|
try
|
|
if (OwnersList=nil) or (OwnersList.Count=0) then begin
|
|
DebugLn(['Note: (lazarus) TPkgManager.AddDependencyToUnitOwners Owner not found of unit ',OwnedFilename]);
|
|
exit;
|
|
end;
|
|
// add package dependency
|
|
//DebugLn(['TPkgManager.AddDependencyToUnitOwners ',dbgsName(TObject(OwnersList[0])),' ',RequiredPkg.IDAsString]);
|
|
RequiredPkg:=TLazPackage(RedirectPackageDependency(RequiredPkg));
|
|
Result:=AddDependencyToOwners(OwnersList,RequiredPkg,false);
|
|
finally
|
|
OwnersList.Free;
|
|
end;
|
|
end;
|
|
|
|
procedure TPkgManager.GetPackagesChangedOnDisk(out ListOfPackages: TStringList;
|
|
IgnoreModifiedFlag: boolean);
|
|
begin
|
|
if PackageGraph=nil then exit;
|
|
PackageGraph.GetPackagesChangedOnDisk(ListOfPackages, IgnoreModifiedFlag);
|
|
end;
|
|
|
|
function TPkgManager.RevertPackages(APackageList: TStringList): TModalResult;
|
|
var
|
|
i: Integer;
|
|
APackage: TLazPackage;
|
|
Filename: String;
|
|
begin
|
|
if APackageList=nil then exit(mrOk);
|
|
for i:=0 to APackageList.Count-1 do begin
|
|
APackage:=TLazPackage(APackageList.Objects[i]);
|
|
Filename:=APackageList[i];
|
|
if Filename='' then
|
|
Filename:=APackage.Filename;
|
|
debugln(['Hint: (lazarus) [TPkgManager.RevertPackages] BEFORE Old=',APackage.Filename,' New=',Filename,' ',FileExistsCached(Filename)]);
|
|
if FileExistsCached(Filename) then
|
|
Result:=DoOpenPackageFile(Filename,[pofRevert],true)
|
|
else begin
|
|
APackage.LPKSource:=nil;
|
|
APackage.Missing:=true;
|
|
Result:=mrCancel;
|
|
end;
|
|
debugln(['Hint: (lazarus) [TPkgManager.RevertPackages] AFTER ',PackageGraph.FindPackageWithFilename(Filename)<>nil]);
|
|
if Result=mrAbort then exit;
|
|
end;
|
|
Result:=mrOk;
|
|
end;
|
|
|
|
function TPkgManager.DoAddActiveUnitToAPackage: TModalResult;
|
|
var
|
|
ActiveSourceEditor: TSourceEditorInterface;
|
|
ActiveUnitInfo: TUnitInfo;
|
|
PkgFile: TPkgFile;
|
|
Filename: String;
|
|
begin
|
|
MainIDE.GetCurrentUnitInfo(ActiveSourceEditor,ActiveUnitInfo);
|
|
if ActiveSourceEditor=nil then exit(mrAbort);
|
|
|
|
Filename:=ActiveUnitInfo.Filename;
|
|
|
|
// check if filename is absolute
|
|
if ActiveUnitInfo.IsVirtual or (not FileExistsUTF8(Filename)) then begin
|
|
Result:=IDEMessageDialog(lisPkgMangFileNotSaved,
|
|
lisPkgMangPleaseSaveTheFileBeforeAddingItToAPackage, mtWarning,[mbCancel]);
|
|
exit;
|
|
end;
|
|
|
|
// check if file is part of project
|
|
if ActiveUnitInfo.IsPartOfProject then begin
|
|
Result:=IDEMessageDialog(lisPkgMangFileIsInProject,
|
|
Format(lisPkgMangWarningTheFileBelongsToTheCurrentProject,[Filename,LineEnding]),
|
|
mtWarning,[mbIgnore,mbCancel]);
|
|
if Result<>mrIgnore then exit;
|
|
end;
|
|
|
|
// check if file is already in a package
|
|
PkgFile:=PackageGraph.FindFileInAllPackages(Filename,true,true);
|
|
if PkgFile<>nil then begin
|
|
Result:=IDEMessageDialog(lisPkgMangFileIsAlreadyInPackage,
|
|
Format(lisPkgMangTheFileIsAlreadyInThePackage,
|
|
[Filename, LineEnding, PkgFile.LazPackage.IDAsString]),
|
|
mtWarning,[mbIgnore,mbCancel]);
|
|
if Result<>mrIgnore then exit;
|
|
end;
|
|
|
|
Result:=ShowAddFileToAPackageDlg(Filename);
|
|
end;
|
|
|
|
function TPkgManager.DoNewPackageComponent: TModalResult;
|
|
var
|
|
APackage: TLazPackage;
|
|
SaveFlags: TPkgSaveFlags;
|
|
CurEditor: TPackageEditorForm;
|
|
begin
|
|
Result:=ShowNewPkgComponentDialog(APackage);
|
|
if Result<>mrOk then exit;
|
|
SaveFlags:=[];
|
|
if APackage=nil then begin
|
|
// create new package
|
|
// create a new package with standard dependencies
|
|
APackage:=PackageGraph.CreateNewPackage(constNewPackageName);
|
|
PackageGraph.AddDependencyToPackage(APackage,
|
|
PackageGraph.IDEIntfPackage.CreateDependencyWithOwner(APackage));
|
|
APackage.Modified:=false;
|
|
Include(SaveFlags,psfSaveAs);
|
|
end;
|
|
// open a package editor
|
|
CurEditor:=PackageEditors.OpenEditor(APackage,true);
|
|
// save
|
|
Result:=DoSavePackage(APackage,SaveFlags);
|
|
if Result<>mrOk then exit;
|
|
Result:=CurEditor.ShowNewCompDialog; // show new component dialog
|
|
end;
|
|
|
|
function TPkgManager.SavePackageFiles(APackage: TLazPackage): TModalResult;
|
|
var
|
|
i: Integer;
|
|
AFile: TPkgFile;
|
|
AFilename: String;
|
|
SaveFlags: TSaveFlags;
|
|
SrcEdit: TSourceEditor;
|
|
begin
|
|
Result:=mrOk;
|
|
for i:=0 to APackage.FileCount-1 do begin
|
|
AFile:=APackage.Files[i];
|
|
if AFile.FileType=pftVirtualUnit then continue;
|
|
AFilename:=AFile.Filename;
|
|
if System.Pos('$(',AFilename)>0 then begin
|
|
// filename contains macros -> skip
|
|
//debugln(['TPkgManager.SavePackageFiles macros ',AFilename]);
|
|
continue;
|
|
end;
|
|
// check if open in editor
|
|
SrcEdit:=SourceEditorManager.SourceEditorIntfWithFilename(AFilename);
|
|
if SrcEdit=nil then
|
|
begin
|
|
// not open in source editor => skip
|
|
//debugln(['TPkgManager.SavePackageFiles no src edit ',AFilename]);
|
|
continue;
|
|
end;
|
|
SaveFlags:=[sfCanAbort];
|
|
if not FilenameIsAbsolute(AFilename) then
|
|
SaveFlags:=[sfSaveAs];
|
|
Result:=LazarusIDE.DoSaveEditorFile(SrcEdit,SaveFlags);
|
|
if Result=mrIgnore then Result:=mrOk;
|
|
if Result<>mrOk then begin
|
|
debugln(['Error: (lazarus) [TPkgManager.SavePackageFiles] failed writing "',AFilename,'"']);
|
|
exit;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
function TPkgManager.WarnAboutMissingPackageFiles(APackage: TLazPackage): TModalResult;
|
|
var
|
|
i: Integer;
|
|
AFile: TPkgFile;
|
|
AFilename: String;
|
|
begin
|
|
Result:=mrOk;
|
|
for i:=0 to APackage.FileCount-1 do begin
|
|
AFile:=APackage.Files[i];
|
|
if AFile.FileType=pftVirtualUnit then continue;
|
|
AFilename:=AFile.GetFullFilename;
|
|
if System.Pos('$(',AFilename)>0 then begin
|
|
// filename contains macros -> skip
|
|
continue;
|
|
end;
|
|
if FilenameIsAbsolute(AFilename) and FileExistsCached(AFilename) then
|
|
continue;
|
|
Result:=IDEQuestionDialog(lisPkgSysPackageFileNotFound,
|
|
Format(lisPkgMangTheFileOfPackageWasNotFound, [AFilename, APackage.IDAsString]),
|
|
mtWarning, [mrIgnore,mrAbort]);
|
|
if Result<>mrAbort then
|
|
Result:=mrOk;
|
|
// one warning is enough
|
|
exit;
|
|
end;
|
|
end;
|
|
|
|
function TPkgManager.AddPackageDependency(APackage: TLazPackage;
|
|
const ReqPackage: string; OnlyTestIfPossible: boolean): TModalResult;
|
|
var
|
|
NewDependency: TPkgDependency;
|
|
ADependency: TPkgDependency;
|
|
begin
|
|
NewDependency:=TPkgDependency.Create;
|
|
try
|
|
NewDependency.PackageName:=ReqPackage;
|
|
Result:=TPkgFileCheck.AddingDependency(APackage,NewDependency,false);
|
|
if Result=mrIgnore then exit(mrOk);
|
|
if Result<>mrOk then exit;
|
|
if not OnlyTestIfPossible then begin
|
|
ADependency:=NewDependency;
|
|
NewDependency:=nil;
|
|
PackageGraph.AddDependencyToPackage(APackage,ADependency);
|
|
Result:=mrOk;
|
|
end;
|
|
finally
|
|
NewDependency.Free;
|
|
end;
|
|
end;
|
|
|
|
function TPkgManager.ApplyDependency(CurDependency: TPkgDependency
|
|
): TModalResult;
|
|
// apply
|
|
var
|
|
OldPkg: TLazPackage;
|
|
PkgEdit: TPackageEditorForm;
|
|
begin
|
|
Result:=mrOk;
|
|
OldPkg:=CurDependency.RequiredPackage;
|
|
if (OldPkg<>nil) and CurDependency.IsCompatible(OldPkg) then
|
|
exit(mrOk);
|
|
|
|
PkgEdit:=PackageEditors.FindEditor(OldPkg);
|
|
if PkgEdit<>nil then
|
|
begin
|
|
if PkgEdit.CanCloseEditor<>mrOk then
|
|
exit(mrCancel);
|
|
end;
|
|
|
|
// Try to load the package again. Min/max version may have changed.
|
|
CurDependency.LoadPackageResult := lprUndefined;
|
|
// This calls UpdateRequiredPackages from PackageGraph.OnEndUpdate,
|
|
// and also updates all package editors which is useless here.
|
|
if PackageGraph.OpenDependency(CurDependency, False, OldPkg)<>lprSuccess then
|
|
Result:=mrCancel;
|
|
//fForcedFlags:=[pefNeedUpdateRequiredPkgs];
|
|
end;
|
|
|
|
function TPkgManager.GetPackageOfEditorItem(Sender: TObject): TIDEPackage;
|
|
begin
|
|
Result:=nil;
|
|
while (Sender is TMenuItem) and (TMenuItem(Sender).Parent<>nil) do
|
|
Sender:=TMenuItem(Sender).Parent;
|
|
if (Sender is TMenuItem) and (TMenuItem(Sender).Menu<>nil)
|
|
then
|
|
Sender:=TMenuItem(Sender).Menu;
|
|
if (Sender is TComponent) and (TComponent(Sender).Owner is TCustomForm) then
|
|
Sender:=TCustomForm(TComponent(Sender).Owner);
|
|
if Sender is TPackageEditorForm then
|
|
Result:=TPackageEditorForm(Sender).LazPackage;
|
|
end;
|
|
|
|
function TPkgManager.DoInstallPackage(APackage: TLazPackage): TModalResult;
|
|
var
|
|
PkgList: TFPList;
|
|
FPMakeList: TFPList;
|
|
|
|
function GetPkgListIndex(APackage: TLazPackage): integer;
|
|
begin
|
|
Result:=PkgList.Count-1;
|
|
while (Result>=0) and (TLazPackage(PkgList[Result])<>APackage) do
|
|
dec(Result);
|
|
end;
|
|
|
|
function WarnForSuspiciousPackage(APackage: TLazPackage): TModalResult;
|
|
var
|
|
IgnorePath: String;
|
|
UnitPath: String;
|
|
begin
|
|
if APackage.UsageOptions.IncludePath<>'' then
|
|
begin
|
|
IgnorePath:='InstallPkgAddsIncPath/'+APackage.Filename;
|
|
if InputHistories.Ignores.Find(IgnorePath)=nil then
|
|
begin
|
|
Result:=IDEQuestionDialog(lisSuspiciousIncludePath,
|
|
Format(lisThePackageAddsThePathToTheIncludePathOfTheIDEThisI, [
|
|
APackage.IDAsString, dbgstr(APackage.UsageOptions.IncludePath), LineEnding]
|
|
),
|
|
mtWarning, [mrYes, lisContinue,
|
|
mrYesToAll, lisContinueAndDoNotAskAgain,
|
|
mrCancel]);
|
|
case Result of
|
|
mrYes: ;
|
|
mrYesToAll:
|
|
InputHistories.Ignores.Add(IgnorePath,iiidForever);
|
|
else
|
|
exit(mrCancel);
|
|
end;
|
|
end;
|
|
end;
|
|
UnitPath:=Trim(GetForcedPathDelims(APackage.UsageOptions.UnitPath));
|
|
while (UnitPath<>'') and (UnitPath[1]=';') do
|
|
UnitPath:=copy(UnitPath,2,Length(UnitPath));
|
|
while (UnitPath<>'') and (RightStr(UnitPath,1)=';') do
|
|
UnitPath:=copy(UnitPath,1,Length(UnitPath)-1);
|
|
UnitPath:=ChompPathDelim(TrimFilename(UnitPath));
|
|
if SysUtils.CompareText(UnitPath,'$(PkgOutDir)')<>0 then
|
|
begin
|
|
IgnorePath:='InstallPkgAddsUnitPath/'+APackage.Filename;
|
|
if InputHistories.Ignores.Find(IgnorePath)=nil then
|
|
begin
|
|
Result:=IDEQuestionDialog(lisSuspiciousUnitPath,
|
|
Format(lisThePackageAddsThePathToTheUnitPathOfTheIDEThisIsPr, [
|
|
APackage.IDAsString, dbgstr(APackage.UsageOptions.UnitPath), LineEnding]),
|
|
mtWarning, [mrYes, lisContinue,
|
|
mrYesToAll, lisContinueAndDoNotAskAgain,
|
|
mrCancel]);
|
|
case Result of
|
|
mrYes: ;
|
|
mrYesToAll:
|
|
InputHistories.Ignores.Add(IgnorePath,iiidForever);
|
|
else
|
|
exit(mrCancel);
|
|
end;
|
|
end;
|
|
end;
|
|
Result:=mrOk;
|
|
end;
|
|
|
|
var
|
|
Dependency: TPkgDependency;
|
|
i: Integer;
|
|
s: String;
|
|
NeedSaving: Boolean;
|
|
RequiredPackage: TLazPackage;
|
|
BuildIDEFlags: TBuildLazarusFlags;
|
|
Msg: string;
|
|
Btns: TMsgDlgButtons;
|
|
ConflictDep: TPkgDependency;
|
|
begin
|
|
if not MainIDE.DoResetToolStatus([rfInteractive]) then exit(mrCancel);
|
|
|
|
try
|
|
BuildBoss.SetBuildTargetIDE;
|
|
|
|
PackageGraph.BeginUpdate(true);
|
|
PkgList:=nil;
|
|
FPMakeList:=nil;
|
|
try
|
|
PackageGraph.ParseBasePackages(false);
|
|
|
|
// check if package is designtime package
|
|
if APackage.PackageType in [lptRunTime,lptRunTimeOnly] then begin
|
|
Btns:=[mbAbort];
|
|
if APackage.PackageType=lptRunTime then
|
|
Include(Btns,mbIgnore);
|
|
Result:=IDEMessageDialog(lisPkgMangPackageIsNoDesigntimePackage,
|
|
Format(lisPkgMangThePackageIsARuntimeOnlyPackageRuntimeOnlyPackages,
|
|
[APackage.IDAsString, LineEnding]),
|
|
mtError,Btns);
|
|
if Result<>mrIgnore then exit;
|
|
end;
|
|
// check if package requires a runtime only package
|
|
ConflictDep:=PackageGraph.FindRuntimePkgOnlyRecursively(
|
|
APackage.FirstRequiredDependency);
|
|
if ConflictDep<>nil then begin
|
|
IDEQuestionDialog(lisNotADesigntimePackage,
|
|
Format(lisThePackageCanNotBeInstalledBecauseItRequiresWhichI, [
|
|
APackage.Name, ConflictDep.AsString]),
|
|
mtError,
|
|
[mrCancel]
|
|
);
|
|
exit;
|
|
end;
|
|
|
|
// save package
|
|
if APackage.IsVirtual or APackage.Modified then begin
|
|
Result:=DoSavePackage(APackage,[]);
|
|
if Result<>mrOk then exit;
|
|
end;
|
|
|
|
// check consistency
|
|
Result:=CheckPackageGraphForCompilation(APackage,nil,
|
|
EnvironmentOptions.GetParsedLazarusDirectory,false);
|
|
if Result<>mrOk then exit;
|
|
|
|
// get all required packages, which will also be auto installed
|
|
APackage.GetAllRequiredPackages(PkgList,FPMakeList,false);
|
|
if PkgList=nil then PkgList:=TFPList.Create;
|
|
|
|
// remove packages already marked for installation
|
|
for i:=PkgList.Count-1 downto 0 do begin
|
|
RequiredPackage:=TLazPackage(PkgList[i]);
|
|
if (RequiredPackage.AutoInstall<>pitNope) then
|
|
PkgList.Delete(i);
|
|
end;
|
|
|
|
// now PkgList contains only the required packages that were added to the
|
|
// list of installation packages
|
|
// => show the user the list
|
|
if PkgList.Count>0 then begin
|
|
s:='';
|
|
for i:=0 to PkgList.Count-1 do begin
|
|
RequiredPackage:=TLazPackage(PkgList[i]);
|
|
s:=s+RequiredPackage.IDAsString+LineEnding;
|
|
end;
|
|
if PkgList.Count=0 then
|
|
Msg:=Format(lisPkgMangInstallingThePackageWillAutomaticallyInstallThePac,
|
|
[APackage.IDAsString])
|
|
else
|
|
Msg:=Format(lisPkgMangInstallingThePackageWillAutomaticallyInstallThePac2,
|
|
[APackage.IDAsString]);
|
|
Result:=IDEMessageDialog(lisPkgMangAutomaticallyInstalledPackages,
|
|
Msg+LineEnding+s,mtConfirmation,[mbOk,mbCancel]);
|
|
if Result<>mrOk then exit;
|
|
end;
|
|
|
|
// warn for packages with suspicious settings
|
|
Result:=WarnForSuspiciousPackage(APackage);
|
|
if Result<>mrOk then exit;
|
|
for i:=0 to PkgList.Count-1 do begin
|
|
RequiredPackage:=TLazPackage(PkgList[i]);
|
|
Result:=WarnForSuspiciousPackage(RequiredPackage);
|
|
if Result<>mrOk then exit;
|
|
end;
|
|
|
|
// add packages to auto installed packages
|
|
if GetPkgListIndex(APackage)<0 then
|
|
PkgList.Add(APackage);
|
|
NeedSaving:=false;
|
|
for i:=0 to PkgList.Count-1 do begin
|
|
RequiredPackage:=TLazPackage(PkgList[i]);
|
|
if RequiredPackage.AutoInstall=pitNope then begin
|
|
RequiredPackage.AutoInstall:=pitStatic;
|
|
Dependency:=RequiredPackage.CreateDependencyWithOwner(Self);
|
|
Dependency.AddToList(PackageGraph.FirstInstallDependency,pddRequires);
|
|
PackageGraph.OpenDependency(Dependency,false);
|
|
NeedSaving:=true;
|
|
end;
|
|
end;
|
|
finally
|
|
PackageGraph.EndUpdate;
|
|
PkgList.Free;
|
|
FPMakeList.Free;
|
|
end;
|
|
|
|
if NeedSaving then begin
|
|
PackageGraph.SortAutoInstallDependencies;
|
|
SaveAutoInstallDependencies;
|
|
end;
|
|
|
|
// save IDE build configs, so user can build IDE on command line
|
|
BuildIDEFlags:=[blfDontClean,blfOnlyIDE];
|
|
Result:=MainIDE.DoSaveBuildIDEConfigs(BuildIDEFlags);
|
|
if Result<>mrOk then exit;
|
|
finally
|
|
BuildBoss.SetBuildTargetProject1;
|
|
end;
|
|
|
|
// ask user to rebuild Lazarus now
|
|
Result:=IDEMessageDialog(lisPkgMangRebuildLazarus,
|
|
Format(lisPkgMangThePackageWasMarkedForInstallationCurrentlyLazarus,
|
|
[APackage.IDAsString, LineEnding, LineEnding+LineEnding]),
|
|
mtConfirmation,[mbYes,mbNo]);
|
|
if Result<>mrYes then begin
|
|
Result:=mrOk;
|
|
exit;
|
|
end;
|
|
|
|
// rebuild Lazarus
|
|
Result:=MainIDE.DoBuildLazarus(BuildIDEFlags);
|
|
if Result<>mrOk then exit;
|
|
|
|
Result:=mrOk;
|
|
end;
|
|
|
|
function TPkgManager.DoUninstallPackage(APackage: TLazPackage;
|
|
Flags: TPkgUninstallFlags; ShowAbort: boolean): TModalResult;
|
|
var
|
|
DependencyPath: TFPList;
|
|
ParentPackage: TLazPackage;
|
|
Dependency: TPkgDependency;
|
|
BuildIDEFlags: TBuildLazarusFlags;
|
|
begin
|
|
if (APackage.Installed=pitNope) and (APackage.AutoInstall=pitNope) then exit;
|
|
|
|
// check if package is required by auto install package
|
|
DependencyPath:=PackageGraph.FindAutoInstallDependencyPath(APackage);
|
|
if DependencyPath<>nil then begin
|
|
DoShowPackageGraphPathList(DependencyPath);
|
|
ParentPackage:=TLazPackage(DependencyPath[0]);
|
|
Result:=IDEMessageDialogAb(lisPkgMangPackageIsRequired,
|
|
Format(lisPkgMangThePackageIsRequiredByWhichIsMarkedForInstallation,
|
|
[APackage.IDAsString, ParentPackage.IDAsString, LineEnding]),
|
|
mtError,[mbCancel],ShowAbort);
|
|
exit;
|
|
end;
|
|
|
|
// check if package is a lazarus base package
|
|
if PackageGraph.IsCompiledInBasePackage(APackage.Name) then begin
|
|
Result:=IDEMessageDialogAb(lisUninstallImpossible,
|
|
Format(lisThePackageCanNotBeUninstalledBecauseItIsNeededByTh,[APackage.Name]),
|
|
mtError,[mbCancel],ShowAbort);
|
|
exit;
|
|
end;
|
|
|
|
// confirm uninstall package
|
|
if not (puifDoNotConfirm in Flags) then begin
|
|
Result:=IDEMessageDialogAb(lisPkgMangUninstallPackage,
|
|
Format(lisPkgMangUninstallPackage2, [APackage.IDAsString]),
|
|
mtConfirmation,[mbYes,mbCancel],ShowAbort);
|
|
if Result<>mrYes then exit;
|
|
end;
|
|
|
|
PackageGraph.BeginUpdate(true);
|
|
try
|
|
// save package
|
|
if APackage.IsVirtual or APackage.Modified then begin
|
|
Result:=DoSavePackage(APackage,[]);
|
|
if Result<>mrOk then exit;
|
|
end;
|
|
|
|
PackageGraph.ParseBasePackages(false);
|
|
|
|
// remove package from auto installed packages
|
|
if APackage.AutoInstall<>pitNope then begin
|
|
APackage.AutoInstall:=pitNope;
|
|
Dependency:=FindCompatibleDependencyInList(PackageGraph.FirstInstallDependency,
|
|
pddRequires,APackage);
|
|
if Dependency<>nil then begin
|
|
Dependency.RemoveFromList(PackageGraph.FirstInstallDependency,pddRequires);
|
|
Dependency.Free;
|
|
PackageGraph.SortAutoInstallDependencies;
|
|
end;
|
|
SaveAutoInstallDependencies;
|
|
end;
|
|
|
|
// save IDE build configs, so user can build IDE on command line
|
|
BuildIDEFlags:=[blfDontClean,blfOnlyIDE];
|
|
Result:=MainIDE.DoSaveBuildIDEConfigs(BuildIDEFlags);
|
|
if Result<>mrOk then exit;
|
|
|
|
if not (puifDoNotBuildIDE in Flags) then begin
|
|
// ask user to rebuilt Lazarus now
|
|
Result:=IDEMessageDialog(lisPkgMangRebuildLazarus,
|
|
Format(lisPkgMangThePackageWasMarkedCurrentlyLazarus,
|
|
[APackage.IDAsString, LineEnding, LineEnding+LineEnding]),
|
|
mtConfirmation,[mbYes,mbNo]);
|
|
if Result=mrNo then begin
|
|
Result:=mrOk;
|
|
exit;
|
|
end;
|
|
|
|
// rebuild Lazarus
|
|
Result:=MainIDE.DoBuildLazarus(BuildIDEFlags);
|
|
if Result<>mrOk then exit;
|
|
end;
|
|
finally
|
|
PackageGraph.EndUpdate;
|
|
end;
|
|
Result:=mrOk;
|
|
end;
|
|
|
|
function TPkgManager.UninstallPackage(APackage: TIDEPackage; ShowAbort: boolean): TModalResult;
|
|
begin
|
|
Assert(APackage is TLazPackage, 'TPkgManager.DoUninstallPackage: APackage is not TLazPackage');
|
|
Result := DoUninstallPackage(TLazPackage(APackage), [puifDoNotConfirm, puifDoNotBuildIDE], ShowAbort);
|
|
end;
|
|
|
|
function TPkgManager.CheckInstallPackageList(InstallPkgIDList: TObjectList;
|
|
UninstallPkgIDList: TObjectList; Flags: TPkgInstallInIDEFlags): boolean;
|
|
var
|
|
NewFirstAutoInstallDependency: TPkgDependency;
|
|
|
|
procedure DeleteDependency(ADependency: TPkgDependency);
|
|
var
|
|
i: Integer;
|
|
PkgID: TLazPackageID;
|
|
PkgName: string;
|
|
begin
|
|
PkgName := ADependency.PackageName; // DeleteDependencyInList destroys ADependency -> don't use it anymore!
|
|
DeleteDependencyInList(ADependency,NewFirstAutoInstallDependency,pddRequires);
|
|
if piiifRemoveConflicts in Flags then
|
|
for i:=InstallPkgIDList.Count-1 downto 0 do begin
|
|
PkgID:=TLazPackageID(InstallPkgIDList[i]);
|
|
if SysUtils.CompareText(PkgID.Name,PkgName)=0 then
|
|
InstallPkgIDList.Delete(i); // PkgID is automatically destroyed
|
|
end;
|
|
end;
|
|
|
|
var
|
|
PkgList, PkgPath, Dependencies: TFPList;
|
|
i, j: Integer;
|
|
APackage: TLazPackage;
|
|
ADependency: TPkgDependency;
|
|
NextDependency: TPkgDependency;
|
|
SaveFlags: TPkgSaveFlags;
|
|
ConflictDep: TPkgDependency;
|
|
PkgID: TLazPackageID;
|
|
s: String;
|
|
begin
|
|
Result:=false;
|
|
PkgList:=nil;
|
|
Dependencies:=TFPList.Create;
|
|
try
|
|
// create new auto install dependency InstallPkgIDList
|
|
ListPkgIDToDependencyList(InstallPkgIDList,NewFirstAutoInstallDependency,
|
|
pddRequires,Self,true);
|
|
|
|
// load all required packages
|
|
if LoadDependencyList(NewFirstAutoInstallDependency,piiifQuiet in Flags)<>mrOk then exit;
|
|
|
|
// remove all top level runtime packages from the list
|
|
// Note: it's ok if a designtime package uses a runtime package
|
|
ADependency:=NewFirstAutoInstallDependency;
|
|
while ADependency<>nil do begin
|
|
NextDependency:=ADependency.NextRequiresDependency;
|
|
if (ADependency.RequiredPackage<>nil) then begin
|
|
if (ADependency.RequiredPackage.PackageType in [lptRunTime,lptRunTimeOnly])
|
|
then begin
|
|
// top level dependency on runtime package => delete
|
|
DeleteDependency(ADependency);
|
|
end else begin
|
|
ConflictDep:=PackageGraph.FindRuntimePkgOnlyRecursively(
|
|
ADependency.RequiredPackage.FirstRequiredDependency);
|
|
//debugln(['TPkgManager.CheckInstallPackageList ',ADependency.RequiredPackage.Name,' ',ConflictDep<>nil]);
|
|
if ConflictDep<>nil then begin
|
|
if piiifRemoveConflicts in Flags then begin
|
|
// can remove conflict
|
|
if not (piiifQuiet in Flags)
|
|
and (IDEQuestionDialog(lisNotADesigntimePackage,
|
|
Format(lisThePackageCanNotBeInstalledBecauseItRequiresWhichI, [
|
|
ADependency.RequiredPackage.Name, ConflictDep.AsString]),
|
|
mtError,
|
|
[mrYes, Format(lisUninstall, [ADependency.RequiredPackage.Name]), mrCancel]
|
|
)<>mrYes)
|
|
then
|
|
exit;
|
|
end else begin
|
|
// can not remove conflict
|
|
if not (piiifQuiet in Flags) then
|
|
IDEQuestionDialog(lisNotADesigntimePackage,
|
|
Format(lisThePackageCanNotBeInstalledBecauseItRequiresWhichI, [
|
|
ADependency.RequiredPackage.Name, ConflictDep.AsString]),
|
|
mtError,[mrCancel]);
|
|
exit;
|
|
end;
|
|
// dependency needs a runtime only package => delete
|
|
DeleteDependency(ADependency);
|
|
end;
|
|
end;
|
|
end;
|
|
ADependency:=NextDependency;
|
|
end;
|
|
|
|
PackageGraph.GetAllRequiredPackages(nil,NewFirstAutoInstallDependency,PkgList);
|
|
|
|
// try save all modified packages
|
|
for i:=0 to PkgList.Count-1 do begin
|
|
APackage:=TLazPackage(PkgList[i]);
|
|
if (not APackage.UserReadOnly)
|
|
and (APackage.IsVirtual or APackage.Modified) then begin
|
|
SaveFlags:=[];
|
|
if DoSavePackage(APackage,SaveFlags)<>mrOk then exit;
|
|
end;
|
|
end;
|
|
|
|
if UninstallPkgIDList<>nil then
|
|
begin
|
|
// check uninstall dependencies
|
|
for i:=0 to UninstallPkgIDList.Count-1 do
|
|
begin
|
|
PkgID:=TLazPackageID(UninstallPkgIDList[i]);
|
|
APackage:=PackageGraph.FindPackageWithID(PkgID);
|
|
if APackage=nil then continue;
|
|
if PkgList.IndexOf(APackage)<0 then continue;
|
|
// this uninstall package is needed by the install packages
|
|
// -> search which ones
|
|
Dependencies.Clear;
|
|
s:='';
|
|
ADependency:=NewFirstAutoInstallDependency;
|
|
while ADependency<>nil do begin
|
|
PkgPath:=PackageGraph.FindPath(ADependency.RequiredPackage,nil,APackage.Name);
|
|
if PkgPath<>nil then
|
|
begin
|
|
PkgPath.Free;
|
|
s:=s+ADependency.PackageName+sLineBreak;
|
|
Dependencies.Add(ADependency);
|
|
end;
|
|
ADependency:=ADependency.NextRequiresDependency;
|
|
end;
|
|
if s='' then continue;
|
|
case IDEQuestionDialog(lisUninstallFail,
|
|
Format(lisThePackageIsUsedBy, [APackage.IDAsString])+sLineBreak +s,
|
|
mtConfirmation, [mrYes, lisUninstallThemToo, mrCancel]
|
|
) of
|
|
mrYes:
|
|
begin
|
|
for j:=0 to Dependencies.Count-1 do
|
|
DeleteDependency(TPkgDependency(Dependencies[j]));
|
|
end;
|
|
else
|
|
exit(false);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
Result:=true;
|
|
finally
|
|
FreeDependencyList(NewFirstAutoInstallDependency,pddRequires);
|
|
PkgList.Free;
|
|
Dependencies.Free;
|
|
end;
|
|
end;
|
|
|
|
function TPkgManager.InstallPackages(PkgIdList: TObjectList;
|
|
Flags: TPkgInstallInIDEFlags): TModalResult;
|
|
|
|
procedure CreateChangeReport(
|
|
OldDependencyList, NewDependencyList: TPkgDependency; Report: TStrings);
|
|
var
|
|
CurDependency: TPkgDependency;
|
|
OldDependency: TPkgDependency;
|
|
NewDependency: TPkgDependency;
|
|
s: String;
|
|
begin
|
|
// list all packages, that will be installed
|
|
CurDependency:=NewDependencyList;
|
|
while CurDependency<>nil do begin
|
|
s:=CurDependency.AsString;
|
|
OldDependency:=FindDependencyByNameInList(OldDependencyList,pddRequires,
|
|
CurDependency.PackageName);
|
|
if OldDependency=nil then begin
|
|
// newly installed
|
|
s:=s+'|'+lisPkgMgrNew;
|
|
Report.Add(s);
|
|
end;
|
|
CurDependency:=CurDependency.NextRequiresDependency;
|
|
end;
|
|
|
|
// list all packages, that will be removed
|
|
CurDependency:=OldDependencyList;
|
|
while CurDependency<>nil do begin
|
|
NewDependency:=FindDependencyByNameInList(NewDependencyList,pddRequires,
|
|
CurDependency.PackageName);
|
|
if NewDependency=nil then
|
|
// this package will be removed
|
|
Report.Add('|'+lisPkgMgrRemove+'|'+CurDependency.AsString);
|
|
CurDependency:=CurDependency.NextRequiresDependency;
|
|
end;
|
|
|
|
// list all packages, that are kept
|
|
CurDependency:=NewDependencyList;
|
|
while CurDependency<>nil do begin
|
|
s:=CurDependency.AsString;
|
|
OldDependency:=FindDependencyByNameInList(OldDependencyList,pddRequires,
|
|
CurDependency.PackageName);
|
|
if OldDependency<>nil then begin
|
|
// stay installed
|
|
if CurDependency.AsString<>OldDependency.AsString then
|
|
s:=s+'|'+lisPkgMgrKeep+'|'+OldDependency.AsString;
|
|
Report.Add(s);
|
|
end;
|
|
CurDependency:=CurDependency.NextRequiresDependency;
|
|
end;
|
|
end;
|
|
|
|
procedure AddPkg(Pkg: TLazPackage);
|
|
var
|
|
i: Integer;
|
|
PkgID: TLazPackageID;
|
|
begin
|
|
if Pkg=nil then exit;
|
|
i:=PkgIdList.Count-1;
|
|
while (i>=0) and (TLazPackageID(PkgIdList[i]).Compare(Pkg)<>0) do
|
|
dec(i);
|
|
if i>=0 then exit;
|
|
PkgID:=TLazPackageID.Create;
|
|
PkgID.AssignID(Pkg);
|
|
PkgIdList.Add(PkgID);
|
|
end;
|
|
|
|
var
|
|
NewFirstAutoInstallDependency: TPkgDependency;
|
|
BuildIDEFlags: TBuildLazarusFlags;
|
|
Report: TStringList;
|
|
PkgList: TFPList;
|
|
RequiredPackage: TLazPackage;
|
|
i: Integer;
|
|
CurDependency: TPkgDependency;
|
|
begin
|
|
Result:=mrCancel;
|
|
NewFirstAutoInstallDependency:=nil;
|
|
PkgList:=nil;
|
|
try
|
|
PackageGraph.ParseBasePackages(false);
|
|
|
|
if not (piiifClear in Flags) then
|
|
begin
|
|
// add existing install packages to list
|
|
NewFirstAutoInstallDependency:=PackageGraph.FirstInstallDependency;
|
|
while NewFirstAutoInstallDependency<>nil do begin
|
|
AddPkg(NewFirstAutoInstallDependency.RequiredPackage);
|
|
NewFirstAutoInstallDependency:=NewFirstAutoInstallDependency.NextRequiresDependency;
|
|
end;
|
|
end;
|
|
|
|
// add base packages
|
|
if PackageGraph.ParseBasePackages(true) then begin
|
|
for i:=0 to PackageGraph.SrcBasePackages.Count-1 do begin
|
|
RequiredPackage:=PackageGraph.FindPackageWithName(PackageGraph.SrcBasePackages[i],nil);
|
|
AddPkg(RequiredPackage);
|
|
end;
|
|
end;
|
|
|
|
if not (piiifSkipChecks in Flags) then
|
|
begin
|
|
if not CheckInstallPackageList(PkgIDList,nil,Flags*[piiifQuiet,piiifRemoveConflicts]) then
|
|
exit(mrCancel);
|
|
end;
|
|
|
|
// create new auto install dependency PkgIDList
|
|
ListPkgIDToDependencyList(PkgIDList,NewFirstAutoInstallDependency,
|
|
pddRequires,Self,true);
|
|
|
|
PackageGraph.SortDependencyListTopologicallyOld(NewFirstAutoInstallDependency,
|
|
false);
|
|
|
|
if not (piiifQuiet in Flags) then
|
|
begin
|
|
// tell the user, which packages will stay, which will be removed and
|
|
// which will be newly installed
|
|
try
|
|
Report:=TStringList.Create;
|
|
CreateChangeReport(
|
|
PackageGraph.FirstInstallDependency,NewFirstAutoInstallDependency,
|
|
Report);
|
|
if not ConfirmPackageList(Report) then exit(mrCancel);
|
|
finally
|
|
Report.Free;
|
|
end;
|
|
end;
|
|
|
|
// try to commit changes -> replace install list
|
|
PackageGraph.BeginUpdate(true);
|
|
try
|
|
// get all required packages
|
|
//debugln('TPkgManager.MainIDEitmPkgEditInstallPkgsClick GetAllRequiredPackages for ',DependencyListAsString(NewFirstAutoInstallDependency,pddRequires));
|
|
if LoadDependencyList(NewFirstAutoInstallDependency,false)<>mrOk then exit(mrCancel);
|
|
PackageGraph.GetAllRequiredPackages(nil,NewFirstAutoInstallDependency,PkgList);
|
|
|
|
// mark packages for installation
|
|
//debugln('TPkgManager.MainIDEitmPkgEditInstallPkgsClick mark packages for installation');
|
|
for i:=0 to PkgList.Count-1 do begin
|
|
RequiredPackage:=TLazPackage(PkgList[i]);
|
|
if RequiredPackage.AutoInstall=pitNope then begin
|
|
RequiredPackage.AutoInstall:=pitStatic;
|
|
end;
|
|
end;
|
|
|
|
// mark packages for uninstall
|
|
//debugln('TPkgManager.MainIDEitmPkgEditInstallPkgsClick mark packages for uninstall');
|
|
CurDependency:=PackageGraph.FirstInstallDependency;
|
|
while CurDependency<>nil do begin
|
|
if (CurDependency.RequiredPackage<>nil)
|
|
and (not PackageGraph.IsCompiledInBasePackage(CurDependency.PackageName)) then
|
|
CurDependency.RequiredPackage.AutoInstall:=pitNope;
|
|
CurDependency:=CurDependency.NextRequiresDependency;
|
|
end;
|
|
|
|
// replace install list
|
|
//debugln('TPkgManager.MainIDEitmPkgEditInstallPkgsClick replace install list');
|
|
FreeDependencyList(PackageGraph.FirstInstallDependency,pddRequires);
|
|
PackageGraph.FirstInstallDependency:=NewFirstAutoInstallDependency;
|
|
NewFirstAutoInstallDependency:=nil;
|
|
finally
|
|
PackageGraph.EndUpdate;
|
|
end;
|
|
|
|
// save package list
|
|
//debugln('TPkgManager.MainIDEitmPkgEditInstallPkgsClick save package list');
|
|
PackageGraph.SortAutoInstallDependencies;
|
|
SaveAutoInstallDependencies;
|
|
|
|
// save IDE build configs, so user can build IDE on command line
|
|
BuildIDEFlags:=[blfDontClean,blfOnlyIDE];
|
|
if MainIDE.DoSaveBuildIDEConfigs(BuildIDEFlags)<>mrOk then exit(mrCancel);
|
|
|
|
if piiifRebuildIDE in Flags then
|
|
begin
|
|
// rebuild Lazarus
|
|
if MainIDE.DoBuildLazarus(BuildIDEFlags)<>mrOk then exit(mrCancel);
|
|
end;
|
|
|
|
finally
|
|
FreeDependencyList(NewFirstAutoInstallDependency,pddRequires);
|
|
PkgList.Free;
|
|
end;
|
|
Result:=mrOk;
|
|
end;
|
|
|
|
function TPkgManager.DoOpenPackageSource(APackage: TLazPackage): TModalResult;
|
|
var
|
|
Filename: String;
|
|
begin
|
|
Result:=mrCancel;
|
|
if APackage.IsVirtual then begin
|
|
IDEMessageDialog(lisCCOErrorCaption,
|
|
lisPkgMangThisIsAVirtualPackageItHasNoSourceYetPleaseSaveThe,
|
|
mtError, [mbCancel]);
|
|
exit;
|
|
end;
|
|
Filename:=APackage.GetSrcFilename;
|
|
if (not FilenameIsAbsolute(Filename)) or (not FileExistsCached(Filename)) then begin
|
|
IDEMessageDialog(lisCCOErrorCaption, lisPkgMangPleaseCompileThePackageFirst,
|
|
mtError,[mbCancel]);
|
|
exit;
|
|
end;
|
|
Result:=MainIDE.DoOpenEditorFile(Filename,-1,-1,[ofRegularFile]);
|
|
end;
|
|
|
|
function TPkgManager.DoCompileAutoInstallPackages(Flags: TPkgCompileFlags;
|
|
OnlyBase: boolean): TModalResult;
|
|
var
|
|
Dependency: TPkgDependency;
|
|
OldDependency: TPkgDependency;
|
|
Dependencies: TPkgDependency;
|
|
AutoRemove: Boolean;
|
|
CompilePolicy: TPackageUpdatePolicy;
|
|
begin
|
|
PackageGraph.BeginUpdate(false);
|
|
Dependencies:=PackageGraph.FirstInstallDependency;
|
|
try
|
|
if OnlyBase then
|
|
begin
|
|
// create the list of base packages
|
|
OldDependency:=PackageGraph.FirstInstallDependency;
|
|
Dependencies:=nil;
|
|
while OldDependency<>nil do begin
|
|
if (OldDependency.RequiredPackage<>nil)
|
|
and PackageGraph.IsCompiledInBasePackage(OldDependency.RequiredPackage.Name) then
|
|
begin
|
|
Dependency:=TPkgDependency.Create;
|
|
Dependency.Assign(OldDependency);
|
|
Dependency.AddToEndOfList(Dependencies,pddRequires);
|
|
end;
|
|
OldDependency:=OldDependency.NextRequiresDependency;
|
|
end;
|
|
Dependencies:=GetFirstDependency(Dependencies,pddRequires);
|
|
PackageGraph.OpenRequiredDependencyList(Dependencies);
|
|
end;
|
|
|
|
// check every installed package if it was loaded correctly
|
|
Dependency:=Dependencies;
|
|
AutoRemove:=false;
|
|
while Dependency<>nil do begin
|
|
OldDependency:=Dependency;
|
|
Dependency:=Dependency.NextRequiresDependency;
|
|
if OldDependency.LoadPackageResult<>lprSuccess then begin
|
|
if not AutoRemove then begin
|
|
Result:=IDEMessageDialog(lisProjAddPackageNotFound,
|
|
Format(lisPkgMangThePackageIsMarkedForInstallationButCanNotBeFound,
|
|
[OldDependency.AsString, LineEnding]),
|
|
mtError,[mbYes,mbYesToAll,mbAbort]);
|
|
case Result of
|
|
mrYes: ;
|
|
mrYesToAll: AutoRemove:=true;
|
|
else
|
|
SaveAutoInstallDependencies;
|
|
exit;
|
|
end;
|
|
end;
|
|
OldDependency.RemoveFromList(PackageGraph.FirstInstallDependency,pddRequires);
|
|
OldDependency.Free;
|
|
end;
|
|
end;
|
|
SaveAutoInstallDependencies;
|
|
|
|
// check consistency
|
|
Result:=CheckPackageGraphForCompilation(nil,Dependencies,
|
|
EnvironmentOptions.GetParsedLazarusDirectory,false);
|
|
if Result<>mrOk then begin
|
|
if ConsoleVerbosity>0 then
|
|
debugln(['Error: (lazarus) [TPkgManager.DoCompileAutoInstallPackages] CheckPackageGraphForCompilation failed']);
|
|
exit;
|
|
end;
|
|
//DebugLn(['TPkgManager.DoCompileAutoInstallPackages LCLUnitPath=',PackageGraph.LCLPackage.CompilerOptions.GetUnitPath(true)]);
|
|
|
|
// save all open files
|
|
if not (pcfDoNotSaveEditorFiles in Flags) then begin
|
|
Result:=MainIDE.DoSaveForBuild(crCompile);
|
|
if Result<>mrOk then begin
|
|
if ConsoleVerbosity>0 then
|
|
debugln(['Error: (lazarus) [TPkgManager.DoCompileAutoInstallPackages] MainIDE.DoSaveForBuild failed']);
|
|
exit;
|
|
end;
|
|
end;
|
|
|
|
// compile all auto install dependencies
|
|
CompilePolicy:=pupAsNeeded;
|
|
if pcfCompileDependenciesClean in Flags then
|
|
CompilePolicy:=pupOnRebuildingAll;
|
|
Result:=PackageGraph.CompileRequiredPackages(nil,Dependencies,false,
|
|
CompilePolicy);
|
|
if Result<>mrOk then begin
|
|
if ConsoleVerbosity>0 then
|
|
debugln(['Error: (lazarus) [TPkgManager.DoCompileAutoInstallPackages] PackageGraph.CompileRequiredPackages failed']);
|
|
exit;
|
|
end;
|
|
|
|
finally
|
|
if OnlyBase then
|
|
FreeDependencyList(Dependencies,pddRequires);
|
|
PackageGraph.EndUpdate;
|
|
end;
|
|
Result:=mrOk;
|
|
end;
|
|
|
|
function TPkgManager.DoSaveAutoInstallConfig: TModalResult;
|
|
var
|
|
TargetDir: String;
|
|
begin
|
|
TargetDir:=MiscellaneousOptions.BuildLazProfiles.Current.TargetDirectory;
|
|
IDEMacros.SubstituteMacros(TargetDir);
|
|
TargetDir:=TrimFilename(TargetDir);
|
|
if not ForceDirectory(TargetDir) then begin
|
|
Result:=IDEMessageDialog(lisPkgMangUnableToCreateDirectory,
|
|
Format(lisPkgMangUnableToCreateTargetDirectoryForLazarus,
|
|
[LineEnding, TargetDir, LineEnding]),
|
|
mtError,[mbCancel,mbAbort]);
|
|
exit;
|
|
end;
|
|
|
|
Result:=PackageGraph.SaveAutoInstallConfig;
|
|
end;
|
|
|
|
function TPkgManager.DoPublishPackage(APackage: TLazPackage;
|
|
Flags: TPkgSaveFlags; ShowDialog: boolean): TModalResult;
|
|
begin
|
|
// show the publish dialog
|
|
if ShowDialog then begin
|
|
Result:=ShowPublishDialog(APackage.PublishOptions);
|
|
if Result<>mrOk then exit;
|
|
end;
|
|
|
|
// save package
|
|
Result:=DoSavePackage(APackage,Flags);
|
|
if Result<>mrOk then exit;
|
|
|
|
// publish package
|
|
Result:=PublishAModule(APackage.PublishOptions);
|
|
end;
|
|
|
|
function TPkgManager.GetUsableComponentUnits(CurRoot: TPersistent): TFPList;
|
|
var
|
|
FMainUnitInfo: TUnitInfo;
|
|
FMainUnitInfoValid: boolean;
|
|
FMainOwner: TObject;
|
|
FMainOwnerValid: boolean;
|
|
|
|
function MainUnitInfo: TUnitInfo;
|
|
begin
|
|
if not FMainUnitInfoValid then
|
|
begin
|
|
if CurRoot is TComponent then
|
|
FMainUnitInfo := Project1.UnitWithComponent(TComponent(CurRoot));
|
|
FMainUnitInfoValid := True;
|
|
end;
|
|
Result := FMainUnitInfo;
|
|
end;
|
|
|
|
function MainOwner: TObject;
|
|
var
|
|
Owners: TFPList;
|
|
begin
|
|
if not FMainOwnerValid then
|
|
begin
|
|
if MainUnitInfo <> nil then
|
|
begin
|
|
if MainUnitInfo.IsPartOfProject then
|
|
FMainOwner := Project1
|
|
else
|
|
begin
|
|
Owners := GetOwnersOfUnit(MainUnitInfo.Filename);
|
|
if (Owners <> nil) and (Owners.Count > 0) then
|
|
FMainOwner := TObject(Owners[0]);
|
|
Owners.Free;
|
|
end;
|
|
end;
|
|
FMainOwnerValid := True;
|
|
end;
|
|
Result := FMainOwner;
|
|
end;
|
|
|
|
procedure CheckUnit(AnUnitInfo: TUnitInfo);
|
|
var
|
|
Owners: TFPList;
|
|
OtherOwner: TObject;
|
|
APackage: TLazPackage;
|
|
ConflictDependency: TPkgDependency;
|
|
FirstDependency: TPkgDependency;
|
|
begin
|
|
if (AnUnitInfo.Component=nil)
|
|
or (AnUnitInfo.Component=CurRoot) then
|
|
exit;
|
|
// check if the component can be used
|
|
// A component can only be used, if it has a CreateForm statement in the lpr
|
|
// A unit can not be used, if it has no owner (project/package).
|
|
// And a unit can not be used, if it belongs to a higher level package.
|
|
// For example: Package A uses Package B.
|
|
// A can use units of B, but B can not use units of A.
|
|
if AnUnitInfo.IsPartOfProject and MainUnitInfo.IsPartOfProject then
|
|
begin
|
|
// both units belong to the project => ok
|
|
end else if AnUnitInfo.IsPartOfProject then
|
|
begin
|
|
// AnUnitInfo belongs to Project, but MainUnitInfo does not
|
|
// A project unit can only be used by the project => not allowed
|
|
exit;
|
|
end else
|
|
begin
|
|
Owners:=GetOwnersOfUnit(AnUnitInfo.Filename);
|
|
if (Owners=nil) or (Owners.Count=0) then begin
|
|
// AnUnitInfo does not belong to a project or package
|
|
// => this unit can not be used
|
|
Owners.Free;
|
|
exit;
|
|
end;
|
|
OtherOwner:=TObject(Owners[0]);
|
|
Owners.Free;
|
|
if OtherOwner=MainOwner then begin
|
|
// both units belong to the same owner => ok
|
|
end else if (OtherOwner is TLazPackage) then begin
|
|
// check if MainOwner can use the package
|
|
APackage:=TLazPackage(OtherOwner);
|
|
if MainOwner is TProject then
|
|
FirstDependency:=TProject(MainOwner).FirstRequiredDependency
|
|
else if MainOwner is TLazPackage then
|
|
FirstDependency:=TLazPackage(MainOwner).FirstRequiredDependency
|
|
else
|
|
exit;
|
|
ConflictDependency:=PackageGraph.FindConflictRecursively(
|
|
FirstDependency,APackage);
|
|
if ConflictDependency<>nil then exit;
|
|
if MainOwner is TLazPackage then begin
|
|
// check if package already uses MainOwner
|
|
ConflictDependency:=PackageGraph.FindDependencyRecursively(
|
|
APackage.FirstRequiredDependency,TLazPackage(MainOwner).Name);
|
|
if ConflictDependency<>nil then exit;
|
|
end;
|
|
end else begin
|
|
// AnUnitInfo does not belong to a Package => can not be used
|
|
exit;
|
|
end;
|
|
end;
|
|
// this unit can be used -> add components
|
|
if Result=nil then
|
|
Result:=TFPList.Create;
|
|
Result.Add(AnUnitInfo);
|
|
end;
|
|
|
|
var
|
|
AnUnitInfo: TUnitInfo;
|
|
begin
|
|
Result:=nil;
|
|
if not (CurRoot is TComponent) then exit;
|
|
FMainOwner:=nil;
|
|
FMainOwnerValid:=false;
|
|
FMainUnitInfo:=nil;
|
|
FMainUnitInfoValid:=false;
|
|
if (MainOwner=nil) or (MainUnitInfo=nil) then exit;
|
|
// search all open designer forms (can be hidden)
|
|
AnUnitInfo:=Project1.FirstUnitWithComponent;
|
|
while AnUnitInfo<>nil do begin
|
|
CheckUnit(AnUnitInfo);
|
|
AnUnitInfo:=AnUnitInfo.NextUnitWithComponent;
|
|
end;
|
|
end;
|
|
|
|
procedure TPkgManager.IterateComponentNames(CurRoot: TPersistent;
|
|
TypeData: PTypeData; Proc: TGetStrProc);
|
|
|
|
procedure CheckComponent(aRoot: TComponent);
|
|
var
|
|
i: integer;
|
|
CurName: String;
|
|
begin
|
|
if aRoot = nil then exit;
|
|
if (aRoot <> CurRoot) and (aRoot is TypeData^.ClassType) then
|
|
Proc(aRoot.Name);
|
|
for i := 0 to aRoot.ComponentCount - 1 do
|
|
if (aRoot.Components[i] is TypeData^.ClassType) then
|
|
begin
|
|
CurName := aRoot.Components[i].Name;
|
|
if aRoot <> CurRoot then
|
|
CurName := aRoot.Name + '.' + CurName;
|
|
Proc(CurName);
|
|
end;
|
|
end;
|
|
|
|
var
|
|
UnitList: TFPList;
|
|
i: Integer;
|
|
begin
|
|
if not (CurRoot is TComponent) then exit;
|
|
CheckComponent(TComponent(CurRoot));
|
|
UnitList := GetUsableComponentUnits(CurRoot);
|
|
if UnitList = nil then exit;
|
|
try
|
|
for i := 0 to UnitList.Count - 1 do
|
|
CheckComponent(TUnitInfo(UnitList[i]).Component);
|
|
finally
|
|
UnitList.Free;
|
|
end;
|
|
end;
|
|
|
|
function TPkgManager.FindReferencedRootComponent(CurRoot: TPersistent;
|
|
const ComponentName: string): TComponent;
|
|
var
|
|
UnitList: TFPList;
|
|
ARoot: TComponent;
|
|
i: integer;
|
|
begin
|
|
//DebugLn(['search ', ComponentName, ' CurRoot = ', dbgsName(CurRoot)]);
|
|
Result := nil;
|
|
UnitList := GetUsableComponentUnits(CurRoot);
|
|
if UnitList = nil then
|
|
Exit;
|
|
try
|
|
for i := 0 to UnitList.Count - 1 do
|
|
begin
|
|
ARoot := TUnitInfo(UnitList[i]).Component;
|
|
DebugLn(['Hint: (lazarus) [TPkgManager.FindReferencedRootComponent] Root=',dbgsName(CurRoot),' Searched="',ComponentName,'" other root=',dbgsName(ARoot)]);
|
|
if (ARoot <> nil) and (SysUtils.CompareText(ComponentName, ARoot.Name) = 0) then
|
|
begin
|
|
Result := ARoot;
|
|
break;
|
|
end;
|
|
end;
|
|
finally
|
|
UnitList.Free;
|
|
end;
|
|
//DebugLn('search end');
|
|
end;
|
|
|
|
function TPkgManager.FindUsableComponent(CurRoot: TPersistent;
|
|
const ComponentPath: string): TComponent;
|
|
|
|
procedure CheckComponent(const RootName, SubPath: string; aRoot: TComponent);
|
|
var
|
|
i: integer;
|
|
begin
|
|
if aRoot = nil then exit;
|
|
if (SysUtils.CompareText(RootName, aRoot.Name) <> 0) then exit;
|
|
|
|
if SubPath = '' then
|
|
begin
|
|
Result := aRoot;
|
|
Exit;
|
|
end;
|
|
|
|
for i := 0 to aRoot.ComponentCount - 1 do
|
|
if SysUtils.CompareText(aRoot.Components[i].Name, SubPath) = 0 then
|
|
begin
|
|
Result := aRoot.Components[i];
|
|
exit;
|
|
end;
|
|
end;
|
|
|
|
var
|
|
UnitList: TFPList;
|
|
SubPath: String;
|
|
p: LongInt;
|
|
RootName: String;
|
|
i: Integer;
|
|
begin
|
|
Result := nil;
|
|
if not (CurRoot is TComponent) then exit;
|
|
SubPath := ComponentPath;
|
|
p := System.Pos('.',SubPath);
|
|
if p < 1 then
|
|
RootName := ''
|
|
else begin
|
|
RootName := copy(ComponentPath, 1, p - 1);
|
|
SubPath := copy(SubPath, p + 1, length(SubPath));
|
|
end;
|
|
if (RootName = '') or (SysUtils.CompareText(RootName, TComponent(CurRoot).Name) = 0) then
|
|
CheckComponent(TComponent(CurRoot).Name, SubPath, TComponent(CurRoot));
|
|
if (p < 1) then
|
|
if Result = nil then
|
|
begin
|
|
RootName := SubPath;
|
|
SubPath := '';
|
|
end
|
|
else
|
|
exit;
|
|
UnitList := GetUsableComponentUnits(CurRoot);
|
|
if UnitList = nil then exit;
|
|
try
|
|
for i := 0 to UnitList.Count-1 do
|
|
begin
|
|
CheckComponent(RootName, SubPath, TUnitInfo(UnitList[i]).Component);
|
|
if Result <> nil then exit;
|
|
end;
|
|
finally
|
|
UnitList.Free;
|
|
end;
|
|
end;
|
|
|
|
function TPkgManager.ProjectInspectorAddDependency(Sender: TObject;
|
|
ADependency: TPkgDependency): TModalResult;
|
|
begin
|
|
Result:=AddProjectDependency(Project1,ADependency);
|
|
end;
|
|
|
|
function TPkgManager.ProjectInspectorRemoveDependency(Sender: TObject;
|
|
ADependency: TPkgDependency): TModalResult;
|
|
var
|
|
ShortUnitName: String;
|
|
Dummy: Boolean;
|
|
begin
|
|
Result:=mrOk;
|
|
Project1.RemoveRequiredDependency(ADependency);
|
|
//debugln('TPkgManager.OnProjectInspectorRemoveDependency A');
|
|
Project1.DefineTemplates.AllChanged(false);
|
|
if (Project1.MainUnitID>=0)
|
|
and (pfMainUnitIsPascalSource in Project1.Flags)
|
|
then begin
|
|
MainIDE.SaveSourceEditorChangesToCodeCache(nil);
|
|
ShortUnitName:=ADependency.PackageName;
|
|
//debugln('TPkgManager.OnProjectInspectorRemoveDependency B ShortUnitName="',ShortUnitName,'"');
|
|
if (ShortUnitName<>'') then begin
|
|
Dummy:=CodeToolBoss.RemoveUnitFromAllUsesSections(
|
|
Project1.MainUnitInfo.Source,ShortUnitName);
|
|
if Dummy then
|
|
Project1.MainUnitInfo.Modified:=true
|
|
else begin
|
|
MainIDEInterface.DoJumpToCodeToolBossError;
|
|
Result:=mrCancel;
|
|
exit;
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
function TPkgManager.ProjectInspectorReAddDependency(Sender: TObject;
|
|
ADependency: TPkgDependency): TModalResult;
|
|
begin
|
|
Result:=mrOk;
|
|
Project1.ReaddRemovedDependency(ADependency);
|
|
PackageGraph.OpenDependency(ADependency,false);
|
|
if (ADependency.RequiredPackage<>nil)
|
|
and (not ADependency.RequiredPackage.Missing) then begin
|
|
AddUnitToProjectMainUsesSection(Project1,ADependency.PackageName,'');
|
|
end;
|
|
end;
|
|
|
|
procedure TPkgManager.ProjectInspectorDragDropTreeView(Sender, Source: TObject;
|
|
X, Y: Integer);
|
|
begin
|
|
{$IFDEF VerbosePkgEditDrag}
|
|
debugln(['TPkgManager.OnProjectInspectorDragDropTreeView START']);
|
|
{$ENDIF}
|
|
FilesEditDragDrop(Sender, Source, X, Y);
|
|
{$IFDEF VerbosePkgEditDrag}
|
|
debugln(['TPkgManager.OnProjectInspectorDragDropTreeView END']);
|
|
{$ENDIF}
|
|
end;
|
|
|
|
function TPkgManager.ProjectInspectorDragOverTreeView(Sender,
|
|
Source: TObject; X, Y: Integer; out TargetTVNode: TTreeNode; out
|
|
TargetTVType: TTreeViewInsertMarkType): boolean;
|
|
var
|
|
SrcFilesEdit: IFilesEditorInterface;
|
|
TargetFilesEdit: IFilesEditorInterface;
|
|
aFileCount: integer;
|
|
aDependencyCount: integer;
|
|
aDirectoryCount: integer;
|
|
begin
|
|
{$IFDEF VerbosePkgEditDrag}
|
|
debugln(['TPkgManager.OnProjectInspectorDragOverTreeView ']);
|
|
{$ENDIF}
|
|
Result:=CheckDrag(Sender, Source, X, Y, SrcFilesEdit, TargetFilesEdit,
|
|
aFileCount, aDependencyCount, aDirectoryCount, TargetTVNode, TargetTVType);
|
|
end;
|
|
|
|
procedure TPkgManager.ProjectInspectorCopyMoveFiles(Sender: TObject);
|
|
begin
|
|
CopyMoveFiles(Sender);
|
|
end;
|
|
|
|
function TPkgManager.CanClosePackageEditor(APackage: TLazPackage): TModalResult;
|
|
begin
|
|
Result:=APackage.Editor.CanCloseEditor;
|
|
end;
|
|
|
|
function TPkgManager.CanCloseAllPackageEditors: TModalResult;
|
|
var
|
|
APackage: TLazPackage;
|
|
i: Integer;
|
|
begin
|
|
for i:=0 to PackageEditors.Count-1 do begin
|
|
APackage:=PackageEditors.Editors[i].LazPackage;
|
|
Result:=CanClosePackageEditor(APackage);
|
|
if Result<>mrOk then exit;
|
|
end;
|
|
Result:=mrOk;
|
|
end;
|
|
|
|
function TPkgManager.CanOpenDesignerForm(AnUnitInfo: TUnitInfo;
|
|
Interactive: boolean): TModalResult;
|
|
var
|
|
AProject: TProject;
|
|
begin
|
|
Result:=mrCancel;
|
|
if AnUnitInfo=nil then exit;
|
|
AProject:=AnUnitInfo.Project;
|
|
if AProject=nil then exit;
|
|
Result:=CheckProjectHasInstalledPackages(AProject,Interactive);
|
|
end;
|
|
|
|
function TPkgManager.DoClosePackageEditor(APackage: TLazPackage): TModalResult;
|
|
begin
|
|
if APackage.Editor<>nil then
|
|
APackage.Editor.Free;
|
|
Result:=mrOk;
|
|
end;
|
|
|
|
function TPkgManager.DoSaveAllPackages(Flags: TPkgSaveFlags): TModalResult;
|
|
var
|
|
AllSaved: Boolean;
|
|
i: Integer;
|
|
CurPackage: TLazPackage;
|
|
begin
|
|
try
|
|
repeat
|
|
AllSaved:=true;
|
|
i:=0;
|
|
while i<PackageGraph.Count do begin
|
|
CurPackage:=PackageGraph[i];
|
|
if CurPackage.Modified and (not CurPackage.ReadOnly)
|
|
and (not (lpfSkipSaving in CurPackage.Flags)) then begin
|
|
Result:=DoSavePackage(CurPackage,Flags);
|
|
if Result=mrIgnore then begin
|
|
CurPackage.Flags:=CurPackage.Flags+[lpfSkipSaving];
|
|
Result:=mrOk;
|
|
end;
|
|
if Result<>mrOk then exit;
|
|
AllSaved:=false;
|
|
end;
|
|
inc(i);
|
|
end;
|
|
until AllSaved;
|
|
finally
|
|
// clear all lpfSkipSaving flags
|
|
for i:=0 to PackageGraph.Count-1 do begin
|
|
CurPackage:=PackageGraph[i];
|
|
CurPackage.Flags:=CurPackage.Flags-[lpfSkipSaving];
|
|
end;
|
|
end;
|
|
Result:=mrOk;
|
|
end;
|
|
|
|
{ TLazPackageDescriptors }
|
|
|
|
function TLazPackageDescriptors.GetItems(Index: integer): TPackageDescriptor;
|
|
begin
|
|
Result:=TPackageDescriptor(FItems[Index]);
|
|
end;
|
|
|
|
constructor TLazPackageDescriptors.Create;
|
|
begin
|
|
PackageDescriptors:=Self;
|
|
FItems:=TFPList.Create;
|
|
end;
|
|
|
|
destructor TLazPackageDescriptors.Destroy;
|
|
var
|
|
i: Integer;
|
|
begin
|
|
fDestroying:=true;
|
|
for i:=Count-1 downto 0 do Items[i].Release;
|
|
FItems.Free;
|
|
FItems:=nil;
|
|
PackageDescriptors:=nil;
|
|
inherited Destroy;
|
|
end;
|
|
|
|
function TLazPackageDescriptors.Count: integer;
|
|
begin
|
|
Result:=FItems.Count;
|
|
end;
|
|
|
|
function TLazPackageDescriptors.GetUniqueName(const Name: string): string;
|
|
var
|
|
i: Integer;
|
|
begin
|
|
Result:=Name;
|
|
if IndexOf(Result)<0 then exit;
|
|
i:=0;
|
|
repeat
|
|
inc(i);
|
|
Result:=Name+IntToStr(i);
|
|
until IndexOf(Result)<0;
|
|
end;
|
|
|
|
function TLazPackageDescriptors.IndexOf(const Name: string): integer;
|
|
begin
|
|
Result:=Count-1;
|
|
while (Result>=0) and (AnsiCompareText(Name,Items[Result].Name)<>0) do
|
|
dec(Result);
|
|
end;
|
|
|
|
function TLazPackageDescriptors.FindByName(const Name: string): TPackageDescriptor;
|
|
var
|
|
i: LongInt;
|
|
begin
|
|
i:=IndexOf(Name);
|
|
if i>=0 then
|
|
Result:=Items[i]
|
|
else
|
|
Result:=nil;
|
|
end;
|
|
|
|
procedure TLazPackageDescriptors.RegisterDescriptor(Descriptor: TPackageDescriptor);
|
|
begin
|
|
if Descriptor.Name='' then
|
|
raise Exception.Create('TLazPackageDescriptors.RegisterDescriptor Descriptor.Name empty');
|
|
Descriptor.Name:=GetUniqueName(Descriptor.Name);
|
|
FItems.Add(Descriptor);
|
|
end;
|
|
|
|
procedure TLazPackageDescriptors.UnregisterDescriptor(Descriptor: TPackageDescriptor);
|
|
var
|
|
i: LongInt;
|
|
begin
|
|
if fDestroying then exit;
|
|
i:=FItems.IndexOf(Descriptor);
|
|
if i<0 then
|
|
raise Exception.Create('TLazPackageDescriptors.UnregisterDescriptor');
|
|
FItems.Delete(i);
|
|
Descriptor.Release;
|
|
end;
|
|
|
|
procedure TLazPackageDescriptors.AddDefaultPackageDescriptors;
|
|
begin
|
|
NewIDEItems.Add(TNewLazIDEItemCategoryPackage.Create(PkgDescGroupName));
|
|
RegisterPackageDescriptor(TPackageDescriptorStd.Create);
|
|
end;
|
|
|
|
{ TPackageDescriptorStd }
|
|
|
|
constructor TPackageDescriptorStd.Create;
|
|
begin
|
|
inherited Create;
|
|
Name:=PkgDescNameStandard;
|
|
end;
|
|
|
|
function TPackageDescriptorStd.GetLocalizedName: string;
|
|
begin
|
|
Result:=lisPackage;
|
|
end;
|
|
|
|
function TPackageDescriptorStd.GetLocalizedDescription: string;
|
|
begin
|
|
Result:=Format(lisNewDlgCreateANewStandardPackageAPackageIsACollectionOfUn,
|
|
[LineEnding]);
|
|
end;
|
|
|
|
end.
|
|
|