mirror of
https://gitlab.com/freepascal.org/lazarus/lazarus.git
synced 2025-04-06 02:57:59 +02:00
6721 lines
236 KiB
ObjectPascal
6721 lines
236 KiB
ObjectPascal
{
|
|
/***************************************************************************
|
|
packagesystem.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:
|
|
The package registration.
|
|
}
|
|
unit PackageSystem;
|
|
|
|
{$mode objfpc}{$H+}
|
|
|
|
interface
|
|
|
|
{off $DEFINE IDE_MEM_CHECK}
|
|
{$DEFINE StopOnRegError}
|
|
|
|
uses
|
|
{$IFDEF IDE_MEM_CHECK}
|
|
MemCheck,
|
|
{$ENDIF}
|
|
// FPC
|
|
Classes, SysUtils, Contnrs, StrUtils, AVL_Tree, fpmkunit, System.UITypes,
|
|
// LazUtils
|
|
FileUtil, LazFileCache, LazLoggerBase, LazUtilities, LazFileUtils, LazUTF8,
|
|
Laz2_XMLCfg, Laz2_XMLRead, LazStringUtils, LazTracer, AvgLvlTree, FPCAdds,
|
|
// codetools
|
|
FileProcs, DefineTemplates, CodeToolManager, CodeCache, DirectoryCacher,
|
|
BasicCodeTools, NonPascalCodeTools, SourceChanger,
|
|
// BuildIntf
|
|
IDEExternToolIntf, MacroDefIntf, ProjectIntf, CompOptsIntf, LazMsgWorker,
|
|
FppkgIntf, PackageDependencyIntf, PackageLinkIntf, PackageIntf, ComponentReg,
|
|
// IDEIntf
|
|
LazIDEIntf,
|
|
// Package registration
|
|
LazarusPackageIntf,
|
|
// IdeUtils
|
|
IdeUtilsPkgStrConsts, DialogProcs, IDETranslations,
|
|
// IdeConfig
|
|
EnvironmentOpts, LazConf, TransferMacros, IDEProcs, SearchPathProcs,
|
|
ParsedCompilerOpts, CompilerOptions, FppkgHelper,
|
|
// IdePackager
|
|
IdePackagerStrConsts, PackageLinks, PackageDefs, PkgSysBasePkgs;
|
|
|
|
const
|
|
MakefileCompileVersion = 2;
|
|
// 2 : changed macro format from %() to $()
|
|
|
|
type
|
|
TFindPackageFlag = (
|
|
fpfSearchInInstalledPckgs,
|
|
fpfSearchInAutoInstallPckgs,
|
|
fpfSearchInPckgsWithEditor,
|
|
fpfSearchInLoadedPkgs,
|
|
fpfSearchInPkgLinks,
|
|
fpfPkgLinkMustExist, // check if .lpk file exists
|
|
fpfIgnoreVersion
|
|
);
|
|
TFindPackageFlags = set of TFindPackageFlag;
|
|
|
|
const
|
|
fpfSearchEverywhere =
|
|
[fpfSearchInInstalledPckgs,fpfSearchInAutoInstallPckgs,
|
|
fpfSearchInPckgsWithEditor,fpfSearchInPkgLinks,fpfSearchInLoadedPkgs];
|
|
fpfSearchAllExisting = fpfSearchEverywhere+[fpfPkgLinkMustExist];
|
|
|
|
type
|
|
TPkgUninstallFlag = (
|
|
puifDoNotConfirm,
|
|
puifDoNotBuildIDE
|
|
);
|
|
TPkgVerbosityFlag = (
|
|
pvPkgSearch // write debug messsages what packages are searched and found
|
|
);
|
|
TPkgVerbosityFlags = set of TPkgVerbosityFlag;
|
|
TPkgUninstallFlags = set of TPkgUninstallFlag;
|
|
// Events
|
|
TShowMsgEvent = function(aUrgency: TMessageLineUrgency;
|
|
aMsg, aSrcFilename: string; aLineNumber, aColumn: integer;
|
|
aViewCaption: string): TMessageLine of object;
|
|
TPkgAddedEvent = procedure(APackage: TLazPackage) of object;
|
|
TPkgDeleteEvent = procedure(APackage: TLazPackage) of object;
|
|
TPkgUninstall = function(APackage: TLazPackage;
|
|
Flags: TPkgUninstallFlags; ShowAbort: boolean): TModalResult of object;
|
|
TPkgTranslate = procedure(APackage: TLazPackage) of object;
|
|
TDependencyModifiedEvent = procedure(ADependency: TPkgDependency) of object;
|
|
TPkgGraphEndUpdateEvent = procedure(Sender: TObject; GraphChanged: boolean) of object;
|
|
TFindFPCUnitEvent = procedure(const AUnitName, Directory: string;
|
|
var Filename: string) of object;
|
|
TPkgDeleteAmbiguousFiles = function(const Filename: string): TModalResult of object;
|
|
TOnBeforeCompilePackages = function(aPkgList: TFPList): TModalResult of object;
|
|
TOnCheckInterPkgFiles = function(IDEObject: TObject; PkgList: TFPList;
|
|
out FilesChanged: boolean): boolean of object;
|
|
TSrcEditFileIsModifiedEvent = function(const SrcFilename: string): boolean of object;
|
|
|
|
{ TLazPkgGraphBuildItem }
|
|
|
|
TLazPkgGraphBuildItem = class(TComponent)
|
|
private
|
|
FLazPackage: TLazPackage;
|
|
fTools: TFPList; // list of TExternalTools
|
|
function GetTools(Index: integer): TAbstractExternalTool;
|
|
procedure SetLazPackage(AValue: TLazPackage);
|
|
protected
|
|
procedure Notification(AComponent: TComponent; Operation: TOperation);
|
|
override;
|
|
public
|
|
constructor Create(AOwner: TComponent); override;
|
|
destructor Destroy; override;
|
|
procedure Clear;
|
|
function Add(Tool: TAbstractExternalTool): integer;
|
|
function Count: integer; inline;
|
|
function GetDummyTool: TAbstractExternalTool;
|
|
function GetFirstOrDummy: TAbstractExternalTool;
|
|
function GetLastOrDummy: TAbstractExternalTool;
|
|
property Tools[Index: integer]: TAbstractExternalTool read GetTools; default;
|
|
property LazPackage: TLazPackage read FLazPackage write SetLazPackage;
|
|
end;
|
|
|
|
{ TLazPkgGraphExtToolData }
|
|
|
|
TLazPkgGraphExtToolData = class(TIDEExternalToolData)
|
|
public
|
|
Pkg: TLazPackage;
|
|
BuildItem: TLazPkgGraphBuildItem;
|
|
SrcPPUFilename: string;
|
|
CompilerFilename: string;
|
|
CompilerParams: TStrings;
|
|
ErrorMessage: string;
|
|
constructor Create(aKind, aModuleName, aFilename: string); override;
|
|
destructor Destroy; override;
|
|
end;
|
|
|
|
TLazPackageGraph = class;
|
|
|
|
{ TLazPackageGraphFileCachePackageInfo }
|
|
|
|
TLazPackageGraphFileCachePackageInfo = record
|
|
FileName: string;
|
|
ChangeStamp: Integer;
|
|
end;
|
|
|
|
{ TLazPackageGraphFileCache }
|
|
|
|
TLazPackageGraphFileCache = class(TObject)
|
|
private
|
|
FGraph: TLazPackageGraph;
|
|
FPackageInfo: array of TLazPackageGraphFileCachePackageInfo;
|
|
FFilesList: TFilenameToPointerTree;
|
|
FRemovedFilesList: TFilenameToPointerTree;
|
|
function NeedsUpdate: Boolean;
|
|
procedure Update;
|
|
public
|
|
constructor Create(AOwner: TLazPackageGraph);
|
|
destructor Destroy; override;
|
|
public
|
|
function FindFileInAllPackages(TheFilename: string;
|
|
IgnoreDeleted, FindVirtualFile: boolean): TPkgFile;
|
|
end;
|
|
|
|
{ TLazPackageGraph }
|
|
|
|
TLazPackageGraph = class(TPackageGraphInterface)
|
|
private
|
|
FAbortRegistration: boolean;
|
|
fChanged: boolean;
|
|
FCodeToolsPackage: TLazPackage;
|
|
FErrorMsg: string;
|
|
FFCLPackage: TLazPackage;
|
|
FBuildIntfPackage: TLazPackage;
|
|
FIDEIntfPackage: TLazPackage;
|
|
FDebuggerIntfPackage: TLazPackage;
|
|
FIdePackagerPackage: TLazPackage;
|
|
FIdeProjectPackage: TLazPackage;
|
|
FIdeUtilsPkgPackage: TLazPackage;
|
|
FLazDebuggerIntfPackage: TLazPackage;
|
|
FLazDebuggerGdbmiPackage: TLazPackage;
|
|
FIdeDebuggerPackage: TLazPackage;
|
|
FIdeConfigPackage: TLazPackage;
|
|
FItems: TFPList; // unsorted list of TLazPackage
|
|
FLazarusBasePackages: TFPList;
|
|
FLazUtilsPackage: TLazPackage;
|
|
FLCLBasePackage: TLazPackage;
|
|
FLCLPackage: TLazPackage;
|
|
FOnAddPackage: TPkgAddedEvent;
|
|
FOnBeforeCompilePackages: TOnBeforeCompilePackages;
|
|
FOnBeginUpdate: TNotifyEvent;
|
|
FOnChangePackageName: TPkgChangeNameEvent;
|
|
FOnCheckInterPkgFiles: TOnCheckInterPkgFiles;
|
|
FOnDeleteAmbiguousFiles: TPkgDeleteAmbiguousFiles;
|
|
FOnDeletePackage: TPkgDeleteEvent;
|
|
FOnDependencyModified: TDependencyModifiedEvent;
|
|
FOnEndUpdate: TPkgGraphEndUpdateEvent;
|
|
FOnShowMessage: TShowMsgEvent;
|
|
FOnSrcEditFileIsModified: TSrcEditFileIsModifiedEvent;
|
|
FOnTranslatePackage: TPkgTranslate;
|
|
FOnUninstallPackage: TPkgUninstall;
|
|
FQuietRegistration: boolean;
|
|
FRegistrationFile: TPkgFile;
|
|
FRegistrationPackage: TLazPackage;
|
|
FRegistrationUnitName: string;
|
|
FSrcBasePackages: TStringListUTF8Fast;
|
|
FSrcBasePackagesFilename: string;
|
|
FSrcBasePackagesFileChangeStep: integer;
|
|
FSynEditPackage: TLazPackage;
|
|
FLazControlsPackage: TLazPackage;
|
|
FTree: TAVLTree; // sorted tree of TLazPackage
|
|
FUpdateLock: integer;
|
|
FLockedChangeStamp: int64;
|
|
FHasCompiledFpmakePackages: Boolean;
|
|
FVerbosity: TPkgVerbosityFlags;
|
|
FFindFileCache: TLazPackageGraphFileCache;
|
|
function CreateDefaultPackage: TLazPackage;
|
|
function GetCount: Integer;
|
|
function GetPackages(Index: integer): TLazPackage;
|
|
procedure DoDependencyChanged(Dependency: TPkgDependency);
|
|
procedure SetRegistrationPackage(const AValue: TLazPackage);
|
|
procedure UpdateBrokenDependenciesToPackage(APackage: TLazPackage);
|
|
function OpenDependencyWithPackageLink(Dependency: TPkgDependency;
|
|
PkgLink: TPackageLink; ShowAbort: boolean): TModalResult;
|
|
function DeleteAmbiguousFiles(const Filename: string): TModalResult;
|
|
procedure AddMessage(TheUrgency: TMessageLineUrgency; const Msg, Filename: string);
|
|
function OutputDirectoryIsWritable(APackage: TLazPackage; Directory: string;
|
|
Verbose: boolean): boolean;
|
|
function GetPackageCompilerParams(APackage: TLazPackage): TStrings;
|
|
function CheckIfCurPkgOutDirNeedsCompile(APackage: TLazPackage;
|
|
CheckDependencies, SkipDesignTimePackages, GroupCompile: boolean;
|
|
out NeedBuildAllFlag, ConfigChanged, DependenciesChanged: boolean;
|
|
var Note: string): TModalResult;
|
|
function LoadPackageCompiledStateFile(APackage: TLazPackage; o: TPkgOutputDir;
|
|
StateFile: string; IgnoreErrors, ShowAbort: boolean): TModalResult;
|
|
procedure InvalidateStateFile(APackage: TLazPackage);
|
|
procedure ExtToolBuildStopped(Sender: TObject);
|
|
procedure PkgModify(Sender: TObject);
|
|
protected
|
|
procedure IncChangeStamp; override;
|
|
public
|
|
constructor Create;
|
|
destructor Destroy; override;
|
|
procedure Clear;
|
|
procedure Delete(Index: integer);
|
|
function Count: integer; // number of Packages
|
|
procedure BeginUpdate(Change: boolean);
|
|
procedure EndUpdate;
|
|
function Updating: boolean;
|
|
procedure RebuildDefineTemplates;
|
|
function MacroFunctionPkgDir(const s: string; const {%H-}Data: PtrInt;
|
|
var {%H-}Abort: boolean): string;
|
|
function MacroFunctionPkgSrcPath(const s: string; const {%H-}Data: PtrInt;
|
|
var {%H-}Abort: boolean): string;
|
|
function MacroFunctionPkgUnitPath(const s: string; const {%H-}Data: PtrInt;
|
|
var {%H-}Abort: boolean): string;
|
|
function MacroFunctionPkgIncPath(const s: string; const {%H-}Data: PtrInt;
|
|
var {%H-}Abort: boolean): string;
|
|
function MacroFunctionPkgName(const s: string; const {%H-}Data: PtrInt;
|
|
var {%H-}Abort: boolean): string;
|
|
function MacroFunctionPkgOutDir(const s: string; const {%H-}Data: PtrInt;
|
|
var {%H-}Abort: boolean): string;
|
|
function MacroFunctionCTPkgDir(Data: Pointer): boolean;
|
|
function MacroFunctionCTPkgSrcPath(Data: Pointer): boolean;
|
|
function MacroFunctionCTPkgUnitPath(Data: Pointer): boolean;
|
|
function MacroFunctionCTPkgIncPath(Data: Pointer): boolean;
|
|
function MacroFunctionCTPkgName(Data: Pointer): boolean;
|
|
function MacroFunctionCTPkgOutDir(Data: Pointer): boolean;
|
|
function GetPackageFromMacroParameter(const TheID: string;
|
|
out APackage: TLazPackage): boolean;
|
|
function SrcEditFileIsModified(const SrcFilename: string): boolean;
|
|
public
|
|
// searching
|
|
function CheckIfPackageCanBeClosed(APackage: TLazPackage): boolean;
|
|
function CreateUniquePkgName(Prefix: string;
|
|
IgnorePackage: TLazPackage): string;
|
|
function CreateUniqueUnitName(const Prefix: string): string;
|
|
function DependencyExists(Dependency: TPkgDependency;
|
|
Flags: TFindPackageFlags): boolean;
|
|
function FindBrokenDependencyPath(APackage: TLazPackage;
|
|
FirstDependency: TPkgDependency): TFPList;
|
|
function FindAllBrokenDependencies(APackage: TLazPackage;
|
|
FirstDependency: TPkgDependency): TFPList;
|
|
function FindCycleDependencyPath(APackage: TLazPackage;
|
|
FirstDependency: TPkgDependency): TFPList;
|
|
function FindPath(StartPackage: TLazPackage; StartDependency: TPkgDependency;
|
|
const EndPackageName: string): TFPList;
|
|
function FindPkgOutputInFPCSearchPath(APackage: TLazPackage;
|
|
FirstDependency: TPkgDependency): TFPList; // find a package with auto compile and output dir is in FPC default search path
|
|
function FindUnsavedDependencyPath(APackage: TLazPackage;
|
|
FirstDependency: TPkgDependency): TFPList;
|
|
function FindNotInstalledRegisterUnits(APackage: TLazPackage;
|
|
FirstDependency: TPkgDependency): TFPList;
|
|
function FindAutoInstallDependencyPath(ChildPackage: TLazPackage): TFPList;
|
|
function FindAmbiguousUnits(APackage: TLazPackage;
|
|
FirstDependency: TPkgDependency;
|
|
out File1, File2: TPkgFile;
|
|
out ConflictPkg: TLazPackage): boolean;
|
|
function FindFPCConflictUnit(APackage: TLazPackage;
|
|
FirstDependency: TPkgDependency;
|
|
const Directory: string;
|
|
OnFindFPCUnit: TFindFPCUnitEvent;
|
|
var File1: TPkgFile;
|
|
var ConflictPkg: TLazPackage): boolean;
|
|
function FindFileInAllPackages(const TheFilename: string;
|
|
IgnoreDeleted, FindVirtualFile: boolean): TPkgFile;
|
|
procedure FindPossibleOwnersOfUnit(const TheFilename: string;
|
|
OwnerList: TFPList);
|
|
function FindLowestPkgNodeByName(const PkgName: string): TAVLTreeNode;
|
|
function FindNextSameName(ANode: TAVLTreeNode): TAVLTreeNode;
|
|
function FindNodeOfDependency(Dependency: TPkgDependency;
|
|
Flags: TFindPackageFlags): TAVLTreeNode;
|
|
function FindOpenPackage(Dependency: TPkgDependency;
|
|
Flags: TFindPackageFlags): TLazPackage;
|
|
function FindPackageWithName(const PkgName: string;
|
|
IgnorePackage: TLazPackage): TLazPackage;
|
|
function FindPackageWithFilename(const TheFilename: string): TLazPackage;
|
|
function FindPackageWithID(PkgID: TLazPackageID): TLazPackage;
|
|
function FindPackageWithIDMask(PkgIDMask: TLazPackageID): TLazPackage;
|
|
function FindPackageProvidingName(FirstDependency: TPkgDependency;
|
|
const Name: string): TLazPackage;
|
|
function FindDependencyRecursively(FirstDependency: TPkgDependency;
|
|
PkgID: TLazPackageID): TPkgDependency;
|
|
function FindDependencyRecursively(FirstDependency: TPkgDependency;
|
|
const PkgName: string): TPkgDependency;
|
|
function FindConflictRecursively(FirstDependency: TPkgDependency;
|
|
PkgID: TLazPackageID): TPkgDependency;
|
|
function FindRuntimePkgOnlyRecursively(FirstDependency: TPkgDependency
|
|
): TPkgDependency;
|
|
function FindUnit(StartPackage: TLazPackage; const TheUnitName: string;
|
|
WithRequiredPackages, IgnoreDeleted: boolean): TPkgFile;
|
|
function FindUnitInAllPackages(const TheUnitName: string;
|
|
IgnoreDeleted: boolean): TPkgFile;
|
|
function FindUnitInInstalledPackages(const TheUnitName: string;
|
|
IgnoreDeleted: boolean): TPkgFile;
|
|
function GetMapSourceDirectoryToPackage(IgnorePackage: TLazPackage = nil): TFilenameToPointerTree;
|
|
function EstimateCompileLoad(APackage: TLazPackage): int64;
|
|
function PackageCanBeReplaced(OldPackage, NewPackage: TLazPackage): boolean;
|
|
function PackageIsNeeded(APackage: TLazPackage): boolean;
|
|
function PackageNameExists(const PkgName: string;
|
|
IgnorePackage: TLazPackage): boolean;
|
|
procedure GetConnectionsTree(FirstDependency: TPkgDependency;
|
|
var PkgList: TFPList; var Tree: TPkgPairTree);
|
|
function GetBrokenDependenciesWhenChangingPkgID(APackage: TLazPackage;
|
|
const NewName: string; NewVersion: TPkgVersion): TFPList;
|
|
procedure GetPackagesChangedOnDisk(out ListOfPackages: TStringList; IgnoreModifiedFlag: boolean = False); // returns list of new filename and TLazPackage
|
|
procedure GetAllRequiredPackages(APackage: TLazPackage; // if not nil then ignore FirstDependency and do not add APackage to Result
|
|
FirstDependency: TPkgDependency;
|
|
out List, FPMakeList: TFPList;
|
|
Flags: TPkgIntfRequiredFlags = [];
|
|
MinPolicy: TPackageUpdatePolicy = low(TPackageUpdatePolicy)
|
|
); overload; // for single search use FindDependencyRecursively
|
|
procedure GetAllRequiredPackages(APackage: TLazPackage; // if not nil then ignore FirstDependency and do not add APackage to Result
|
|
FirstDependency: TPkgDependency;
|
|
out List: TFPList;
|
|
Flags: TPkgIntfRequiredFlags = [];
|
|
MinPolicy: TPackageUpdatePolicy = low(TPackageUpdatePolicy)
|
|
); overload;
|
|
procedure SortDependencyListTopologicallyOld(
|
|
var FirstDependency: TPkgDependency; TopLevelFirst: boolean);
|
|
procedure IterateAllComponentClasses(Event: TIterateComponentClassesEvent);
|
|
procedure IterateComponentClasses(APackage: TLazPackage;
|
|
Event: TIterateComponentClassesEvent;
|
|
WithUsedPackages, WithRequiredPackages: boolean);
|
|
procedure IteratePackages(Flags: TFindPackageFlags;
|
|
Event: TIteratePackagesEvent);
|
|
procedure IteratePackagesSorted(Flags: TFindPackageFlags;
|
|
Event: TIteratePackagesEvent);
|
|
procedure MarkAllPackagesAsNotVisited;
|
|
procedure MarkAllDependencies(MarkPackages: boolean;
|
|
AddMarkerFlags, RemoveMarkerFlags: TPkgMarkerFlags);
|
|
procedure MarkAllRequiredPackages(FirstDependency: TPkgDependency);
|
|
procedure MarkNeededPackages;
|
|
procedure ConsistencyCheck;
|
|
public
|
|
// packages handling
|
|
function CreateNewPackage(const Prefix: string): TLazPackage;
|
|
procedure AddPackage(APackage: TLazPackage);
|
|
procedure ReplacePackage(var OldPackage: TLazPackage; NewPackage: TLazPackage);
|
|
procedure ClosePackage(APackage: TLazPackage);
|
|
procedure CloseUnneededPackages;
|
|
procedure ChangePackageID(APackage: TLazPackage;
|
|
const NewName: string; NewVersion: TPkgVersion;
|
|
RenameDependencies, RenameMacros: boolean);
|
|
function SavePackageCompiledState(APackage: TLazPackage;
|
|
const CompilerFilename: string; CompilerParams: TStrings;
|
|
Complete, MainPPUExists, ShowAbort: boolean): TModalResult;
|
|
function LoadPackageCompiledState(APackage: TLazPackage;
|
|
IgnoreErrors, ShowAbort: boolean): TModalResult;
|
|
procedure SetFlagDependenciesNeedBuild(Pkg: TLazPackage);
|
|
function CheckCompileNeedDueToFPCUnits(TheOwner: TObject;
|
|
StateFileAge: longint; var Note: string): boolean;
|
|
function CheckCompileNeedDueToDependencies(TheOwner: TObject;
|
|
FirstDependency: TPkgDependency;
|
|
SkipDesignTimePackages: boolean; StateFileAge: longint;
|
|
var Note: string): TModalResult;
|
|
function CheckIfPackageNeedsCompilation(APackage: TLazPackage;
|
|
SkipDesignTimePackages, GroupCompile: boolean;
|
|
var NeedBuildAllFlag: boolean; // pass true to force a build
|
|
var Note: string): TModalResult;
|
|
function PreparePackageOutputDirectory(APackage: TLazPackage;
|
|
CleanUp: boolean): TModalResult;
|
|
function GetFallbackOutputDir(APackage: TLazPackage): string;
|
|
function CheckAmbiguousPackageUnits(APackage: TLazPackage): TModalResult;
|
|
function SavePackageMainSource(APackage: TLazPackage;
|
|
{%H-}Flags: TPkgCompileFlags; ShowAbort: boolean): TModalResult;
|
|
function CompileRequiredPackages(APackage: TLazPackage;
|
|
FirstDependency: TPkgDependency;
|
|
SkipDesignTimePackages: boolean;
|
|
Policy: TPackageUpdatePolicy): TModalResult;
|
|
function CompilePackage(APackage: TLazPackage; Flags: TPkgCompileFlags;
|
|
ShowAbort: boolean;
|
|
BuildItem: TLazPkgGraphBuildItem = nil): TModalResult;
|
|
function CompilePackageUsingFPMake(APackageName: string; Flags: TPkgCompileFlags;
|
|
ShowAbort: boolean;
|
|
BuildItem: TLazPkgGraphBuildItem = nil): TModalResult;
|
|
function ConvertPackageRSTFiles(APackage: TLazPackage): TModalResult;
|
|
function WriteMakefileCompiled(APackage: TLazPackage;
|
|
TargetCompiledFile, UnitPath, IncPath, OtherOptions: string): TModalResult;
|
|
function WriteMakeFile(APackage: TLazPackage): TModalResult;
|
|
function WriteFpmake(APackage: TLazPackage): TModalResult;
|
|
public
|
|
// installed packages
|
|
FirstInstallDependency: TPkgDependency;
|
|
function ParseBasePackages(Verbose: boolean): boolean; // read list from current sources
|
|
function SrcBasePackagesNeedLazbuild: string; // check if compiled-in and source base pkg list differ that a built using make is needed
|
|
procedure LoadStaticBasePackages;
|
|
procedure LoadAutoInstallPackages(PkgList: TStringList);
|
|
procedure SortAutoInstallDependencies;
|
|
function GetIDEInstallPackageOptions(
|
|
var InheritedOptionStrings: TInheritedCompOptsStrings): string;
|
|
function SaveAutoInstallConfig: TModalResult;// for the uses section
|
|
function IsCompiledInBasePackage(PackageName: string): boolean;
|
|
procedure FreeAutoInstallDependencies;
|
|
procedure WarnSuspiciousCompilerOptions(ViewCaption, Target: string; CompilerParams: TStrings);
|
|
public
|
|
// registration
|
|
procedure RegisterUnitHandler(const TheUnitName: string;
|
|
RegisterProc: TRegisterProc);
|
|
procedure RegisterComponentsHandler(const Page: string;
|
|
ComponentClasses: array of TComponentClass);
|
|
procedure RegistrationError(const Msg: string);
|
|
procedure RegisterStaticPackage(APackage: TLazPackage;
|
|
RegisterProc: TRegisterProc);
|
|
procedure CallRegisterProc(RegisterProc: TRegisterProc);
|
|
public
|
|
// dependency handling
|
|
procedure AddDependencyToPackage(APackage: TLazPackage;
|
|
Dependency: TPkgDependency);
|
|
procedure AddDependencyToPackage(APackage, RequiredPackage: TLazPackage);
|
|
procedure RemoveDependencyFromPackage(APackage: TLazPackage;
|
|
Dependency: TPkgDependency; AddToRemovedList: boolean);
|
|
function OpenDependency(Dependency: TPkgDependency;
|
|
ShowAbort: boolean; IgnorePackage: TLazPackage = nil): TLoadPackageResult;
|
|
function FindAlternativeLPK(APackage: TLazPackage): string;
|
|
procedure OpenInstalledDependency(Dependency: TPkgDependency;
|
|
InstallType: TPackageInstallType; var Quiet: boolean);
|
|
procedure OpenRequiredDependencyList(FirstDependency: TPkgDependency);
|
|
procedure MoveRequiredDependencyUp(ADependency: TPkgDependency);
|
|
procedure MoveRequiredDependencyDown(ADependency: TPkgDependency);
|
|
public
|
|
// properties
|
|
property AbortRegistration: boolean read FAbortRegistration
|
|
write FAbortRegistration;
|
|
property QuietRegistration: boolean read FQuietRegistration
|
|
write FQuietRegistration;
|
|
property ErrorMsg: string read FErrorMsg write FErrorMsg;
|
|
property Packages[Index: integer]: TLazPackage read GetPackages; default; // see Count for the number
|
|
property UpdateLock: integer read FUpdateLock;
|
|
property Verbosity: TPkgVerbosityFlags read FVerbosity write FVerbosity;
|
|
|
|
// base packages
|
|
property SrcBasePackages: TStringListUTF8Fast read FSrcBasePackages;
|
|
property FCLPackage: TLazPackage read FFCLPackage;
|
|
property LCLBasePackage: TLazPackage read FLCLBasePackage;
|
|
property LCLPackage: TLazPackage read FLCLPackage;
|
|
property SynEditPackage: TLazPackage read FSynEditPackage;
|
|
property LazControlsPackage: TLazPackage read FLazControlsPackage;
|
|
property LazUtilsPackage: TLazPackage read FLazUtilsPackage;
|
|
property CodeToolsPackage: TLazPackage read FCodeToolsPackage;
|
|
property BuildIntfPackage: TLazPackage read FBuildIntfPackage;
|
|
property IDEIntfPackage: TLazPackage read FIDEIntfPackage;
|
|
property LazDebuggerIntfPackage: TLazPackage read FLazDebuggerIntfPackage;
|
|
property DebuggerIntfPackage: TLazPackage read FDebuggerIntfPackage;
|
|
property LazDebuggerGdbmiPackage: TLazPackage read FLazDebuggerGdbmiPackage;
|
|
property IdeDebuggerPackage: TLazPackage read FIdeDebuggerPackage;
|
|
property IdeUtilsPkgPackage: TLazPackage read FIdeUtilsPkgPackage;
|
|
property IdeConfigPackage: TLazPackage read FIdeConfigPackage;
|
|
property IdePackagerPackage: TLazPackage read FIdePackagerPackage;
|
|
property IdeProjectPackage: TLazPackage read FIdeProjectPackage;
|
|
property LazarusBasePackages: TFPList read FLazarusBasePackages;
|
|
|
|
// events
|
|
property OnAddPackage: TPkgAddedEvent read FOnAddPackage write FOnAddPackage;
|
|
property OnBeforeCompilePackages: TOnBeforeCompilePackages read
|
|
FOnBeforeCompilePackages write FOnBeforeCompilePackages;
|
|
property OnBeginUpdate: TNotifyEvent read FOnBeginUpdate write FOnBeginUpdate;
|
|
property OnChangePackageName: TPkgChangeNameEvent read FOnChangePackageName
|
|
write FOnChangePackageName;
|
|
property OnCheckInterPkgFiles: TOnCheckInterPkgFiles
|
|
read FOnCheckInterPkgFiles write FOnCheckInterPkgFiles;
|
|
property OnDeleteAmbiguousFiles: TPkgDeleteAmbiguousFiles
|
|
read FOnDeleteAmbiguousFiles write FOnDeleteAmbiguousFiles;
|
|
property OnDeletePackage: TPkgDeleteEvent read FOnDeletePackage
|
|
write FOnDeletePackage;
|
|
property OnDependencyModified: TDependencyModifiedEvent
|
|
read FOnDependencyModified write FOnDependencyModified;
|
|
property OnEndUpdate: TPkgGraphEndUpdateEvent read FOnEndUpdate write FOnEndUpdate;
|
|
property OnShowMessage: TShowMsgEvent read FOnShowMessage write FOnShowMessage;
|
|
property OnSrcEditFileIsModified: TSrcEditFileIsModifiedEvent read FOnSrcEditFileIsModified
|
|
write FOnSrcEditFileIsModified;
|
|
property OnTranslatePackage: TPkgTranslate read FOnTranslatePackage
|
|
write FOnTranslatePackage;
|
|
property OnUninstallPackage: TPkgUninstall read FOnUninstallPackage
|
|
write FOnUninstallPackage;
|
|
|
|
// set during calling Register procedures
|
|
property RegistrationFile: TPkgFile read FRegistrationFile;
|
|
property RegistrationPackage: TLazPackage read FRegistrationPackage
|
|
write SetRegistrationPackage;
|
|
property RegistrationUnitName: string read FRegistrationUnitName;
|
|
end;
|
|
|
|
var
|
|
PackageGraph: TLazPackageGraph = nil;
|
|
|
|
function ExtractFPCParamsForBuildAll(CompParams: TStrings): TStrings;
|
|
function ExtractMakefileCompiledParams(CompParams: TStrings;
|
|
CreateReduced: boolean = false;
|
|
BaseDir: string = ''; MakeRelative: boolean = false): TStringList;
|
|
function FPCParamNeedsBuildAll(const Param: String): boolean;
|
|
function FPCParamForBuildAllHasChanged(OldParams, NewParams: TStrings): boolean;
|
|
function RemoveFPCVerbosityParams(CompParams: TStrings): TStrings;
|
|
function WriteCompilerCfgFile(CfgFilename: string; CompilerParams: TStrings;
|
|
out CmdLineParams: TStrings): TCodeBuffer;
|
|
|
|
|
|
implementation
|
|
|
|
procedure RegisterComponentsGlobalHandler(const Page: string;
|
|
ComponentClasses: array of TComponentClass);
|
|
begin
|
|
PackageGraph.RegisterComponentsHandler(Page,ComponentClasses);
|
|
end;
|
|
|
|
procedure RegisterNoIconGlobalHandler(ComponentClasses: array of TComponentClass);
|
|
begin
|
|
PackageGraph.RegisterComponentsHandler('',ComponentClasses);
|
|
end;
|
|
|
|
function ExtractFPCParamsForBuildAll(CompParams: TStrings): TStrings;
|
|
var
|
|
i: integer;
|
|
p: String;
|
|
begin
|
|
Result:=TStringList.Create;
|
|
for i:=0 to CompParams.Count-1 do
|
|
begin
|
|
p:=CompParams[i];
|
|
if FPCParamNeedsBuildAll(p) then
|
|
Result.Add(p);
|
|
end;
|
|
end;
|
|
|
|
function ExtractMakefileCompiledParams(CompParams: TStrings;
|
|
CreateReduced: boolean; BaseDir: string; MakeRelative: boolean): TStringList;
|
|
var
|
|
AllPaths: TStringList;
|
|
Path: String;
|
|
Reduced: String;
|
|
|
|
procedure AddSearchPath(Typ: string);
|
|
begin
|
|
AllPaths.Values[Typ]:=MergeSearchPaths(AllPaths.Values[Typ],Path);
|
|
end;
|
|
|
|
var
|
|
i: integer;
|
|
Param: String;
|
|
begin
|
|
Result:=TStringList.Create;
|
|
Reduced:='';
|
|
AllPaths:=Result;
|
|
for i:=0 to CompParams.Count-1 do
|
|
begin
|
|
Param:=CompParams[i];
|
|
if (length(Param)>1) and (Param[1]='-') then begin
|
|
case Param[2] of
|
|
'F':
|
|
// search paths
|
|
if length(Param)>3 then begin
|
|
Path:=copy(Param,4,length(Param));
|
|
if (Path[1] in ['''','"']) then
|
|
Path:=AnsiDequotedStr(Path,Path[1]);
|
|
case Param[3] of
|
|
'u': begin AddSearchPath('UnitPath'); continue; end;
|
|
'U': begin AllPaths.Values['UnitOutputDir']:=Path; continue; end;
|
|
'i': begin AddSearchPath('IncPath'); continue; end;
|
|
'o': begin AddSearchPath('ObjectPath'); continue; end;
|
|
'l': begin AddSearchPath('LibPath'); continue; end;
|
|
end;
|
|
end;
|
|
'v':
|
|
// verbosity
|
|
continue;
|
|
'i','l':
|
|
// information
|
|
continue;
|
|
'B':
|
|
// build clean
|
|
continue;
|
|
'C':
|
|
if (Param='-Cg')
|
|
and TargetNeedsFPCOptionCG(FPCAdds.GetCompiledTargetOS,FPCAdds.GetCompiledTargetCPU)
|
|
then begin
|
|
// the -Cg parameter is added automatically on Linux, but not in the
|
|
// Makefile.compiled, because that is platform independent.
|
|
// -> ignore
|
|
continue;
|
|
end;
|
|
end;
|
|
end;
|
|
if Reduced<>'' then
|
|
Reduced+=' ';
|
|
Reduced+=Param;
|
|
end;
|
|
if BaseDir<>'' then begin
|
|
for i:=0 to AllPaths.Count-1 do begin
|
|
Path:=AllPaths.ValueFromIndex[i];
|
|
if MakeRelative then
|
|
AllPaths[i]:=AllPaths.Names[i]+'='+CreateRelativeSearchPath(Path,BaseDir)
|
|
else
|
|
AllPaths[i]:=AllPaths.Names[i]+'='+CreateAbsoluteSearchPath(Path,BaseDir);
|
|
end;
|
|
end;
|
|
if CreateReduced then
|
|
AllPaths.Values['Reduced']:=UTF8Trim(Reduced,[]);
|
|
end;
|
|
|
|
function FPCParamNeedsBuildAll(const Param: String): boolean;
|
|
{ Some compiler flags require a clean build -B, because the compiler
|
|
does not recompile/update some ppu itself.
|
|
Remove all flags that do not require build all:
|
|
-B build all
|
|
-e executable
|
|
-E omit linking stage
|
|
-F file name and paths
|
|
-I includepath
|
|
-i information
|
|
-k linker flags
|
|
-l logo
|
|
-o name of the executable
|
|
-P target cpu
|
|
-s quick build option, do not call assembler and linker
|
|
-T target OS
|
|
-v verbosity
|
|
-V Append '-<x>' to the used compiler binary name
|
|
-X Executable options
|
|
}
|
|
begin
|
|
Result:=(length(Param)<=2)
|
|
or (Param[1]<>'-')
|
|
or not (Param[2] in ['l','F','B','E','e','I','i','o','s','T','P','v','X','k']);
|
|
end;
|
|
|
|
function FPCParamForBuildAllHasChanged(OldParams, NewParams: TStrings): boolean;
|
|
var
|
|
i, j, OldCount, NewCount: Integer;
|
|
begin
|
|
i:=0;
|
|
j:=0;
|
|
if OldParams<>nil then
|
|
OldCount:=OldParams.Count
|
|
else
|
|
OldCount:=0;
|
|
if NewParams<>nil then
|
|
NewCount:=NewParams.Count
|
|
else
|
|
NewCount:=0;
|
|
repeat
|
|
while (i<OldCount) and not FPCParamNeedsBuildAll(OldParams[i]) do
|
|
inc(i);
|
|
while (j<NewCount) and not FPCParamNeedsBuildAll(NewParams[j]) do
|
|
inc(j);
|
|
if i=OldCount then
|
|
begin
|
|
if j=NewCount then
|
|
exit(false) // nothing relevant changed
|
|
else
|
|
exit(true); // new relevant param
|
|
end else
|
|
begin
|
|
if j=NewCount then
|
|
exit(true) // a relevant param vanished
|
|
else if OldParams[i]<>NewParams[j] then
|
|
exit(true); // relevant param changed
|
|
end;
|
|
inc(i);
|
|
inc(j);
|
|
until false;
|
|
end;
|
|
|
|
function RemoveFPCVerbosityParams(CompParams: TStrings): TStrings;
|
|
{ Some compiler flags have no impact on the produced files.
|
|
Remove all flags that do not require a rebuild:
|
|
-l -B -i -v }
|
|
var
|
|
i: integer;
|
|
Param: String;
|
|
begin
|
|
Result:=TStringListUTF8Fast.Create;
|
|
for i:=0 to CompParams.Count-1 do
|
|
begin
|
|
Param:=CompParams[i];
|
|
if (length(Param)>1) and (Param[1]='-') and (Param[2] in ['l','B','i','v']) then
|
|
// verbosity
|
|
else
|
|
Result.Add(Param);
|
|
end;
|
|
end;
|
|
|
|
function WriteCompilerCfgFile(CfgFilename: string; CompilerParams: TStrings;
|
|
out CmdLineParams: TStrings): TCodeBuffer;
|
|
var
|
|
Src, Param: String;
|
|
i: Integer;
|
|
begin
|
|
Result:=nil;
|
|
CmdLineParams:=TStringListUTF8Fast.Create;
|
|
CmdLineParams.Add('@'+CfgFilename);
|
|
Src:='# Auto generated by Lazarus. Do not edit.'+LineEnding;
|
|
for i:=CompilerParams.Count-1 downto 0 do
|
|
begin
|
|
Param:=CompilerParams[i];
|
|
if (Param[1]='@')
|
|
or (Param='n')
|
|
or (Param[1]<>'-') then
|
|
CmdLineParams.Insert(1,Param)
|
|
else begin
|
|
Src+=Param+LineEnding;
|
|
CompilerParams.Delete(i);
|
|
end;
|
|
end;
|
|
|
|
Result:=CodeToolBoss.LoadFile(CfgFilename,true,true);
|
|
if (Result=nil) and FileExistsCached(CfgFilename) then
|
|
exit; // failed loading old cfg
|
|
if (Result<>nil) and (Result.Source=Src) then
|
|
exit; // nothing changed -> skip
|
|
if Result=nil then
|
|
Result:=CodeToolBoss.CreateFile(CfgFilename);
|
|
Result.Source:=Src;
|
|
end;
|
|
|
|
{ TLazPackageGraphFileCache }
|
|
|
|
constructor TLazPackageGraphFileCache.Create(AOwner: TLazPackageGraph);
|
|
begin
|
|
inherited Create;
|
|
|
|
FGraph := AOwner;
|
|
SetLength(FPackageInfo, 0);
|
|
FFilesList := TFilenameToPointerTree.Create(false);
|
|
FRemovedFilesList := TFilenameToPointerTree.Create(false);
|
|
end;
|
|
|
|
destructor TLazPackageGraphFileCache.Destroy;
|
|
begin
|
|
SetLength(FPackageInfo, 0);
|
|
FreeAndNil(FFilesList);
|
|
FreeAndNil(FRemovedFilesList);
|
|
|
|
inherited Destroy;
|
|
end;
|
|
|
|
function TLazPackageGraphFileCache.FindFileInAllPackages(
|
|
TheFilename: string; IgnoreDeleted, FindVirtualFile: boolean): TPkgFile;
|
|
|
|
procedure FindFileInStrList(const Files: TFilenameToPointerTree);
|
|
begin
|
|
Result:=TPkgFile(Files[TheFilename]);
|
|
if (Result<>nil) and (not FindVirtualFile)
|
|
and (not FilenameIsAbsolute(Result.Filename)) then
|
|
Result := nil;
|
|
end;
|
|
|
|
begin
|
|
Result:=nil;
|
|
|
|
if NeedsUpdate then
|
|
Update;
|
|
|
|
FindFileInStrList(FFilesList);
|
|
if Result<>nil then Exit;
|
|
if not IgnoreDeleted then
|
|
FindFileInStrList(FRemovedFilesList);
|
|
end;
|
|
|
|
function TLazPackageGraphFileCache.NeedsUpdate: Boolean;
|
|
var
|
|
I: Integer;
|
|
begin
|
|
if FGraph.Count <> Length(FPackageInfo) then
|
|
Exit(True);
|
|
|
|
for I := 0 to FGraph.Count-1 do
|
|
if (FPackageInfo[I].ChangeStamp <> FGraph[I].ChangeStamp)
|
|
or (FPackageInfo[I].FileName<>FGraph[I].Filename)
|
|
then
|
|
Exit(True);
|
|
|
|
Result := False;
|
|
end;
|
|
|
|
procedure TLazPackageGraphFileCache.Update;
|
|
var
|
|
I, L: Integer;
|
|
xPck: TLazPackage;
|
|
PkgFile: TPkgFile;
|
|
begin
|
|
SetLength(FPackageInfo, FGraph.Count);
|
|
FFilesList.Clear;
|
|
FRemovedFilesList.Clear;
|
|
for I := 0 to FGraph.Count-1 do
|
|
begin
|
|
xPck := FGraph[I];
|
|
FPackageInfo[I].ChangeStamp := xPck.ChangeStamp;
|
|
FPackageInfo[I].FileName := xPck.Filename;
|
|
|
|
for L := 0 to xPck.FileCount-1 do
|
|
begin
|
|
PkgFile := xPck.Files[L];
|
|
FFilesList[PkgFile.GetFullFilename]:=PkgFile;
|
|
FFilesList[PkgFile.Filename]:=PkgFile;
|
|
end;
|
|
for L := 0 to xPck.RemovedFilesCount-1 do
|
|
begin
|
|
PkgFile := xPck.RemovedFiles[L];
|
|
FRemovedFilesList[PkgFile.GetFullFilename]:=PkgFile;
|
|
FRemovedFilesList[PkgFile.Filename]:=PkgFile;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
{ TLazPkgGraphBuildItem }
|
|
|
|
// inline
|
|
function TLazPkgGraphBuildItem.Count: integer;
|
|
begin
|
|
Result:=fTools.Count;
|
|
end;
|
|
|
|
function TLazPkgGraphBuildItem.GetDummyTool: TAbstractExternalTool;
|
|
begin
|
|
Result:=ExternalToolList.AddDummy('Dummy tool for building package '+LazPackage.Name);
|
|
Add(Result);
|
|
end;
|
|
|
|
function TLazPkgGraphBuildItem.GetFirstOrDummy: TAbstractExternalTool;
|
|
begin
|
|
if Count>0 then
|
|
Result:=TAbstractExternalTool(fTools[0])
|
|
else
|
|
Result:=GetDummyTool;
|
|
end;
|
|
|
|
function TLazPkgGraphBuildItem.GetLastOrDummy: TAbstractExternalTool;
|
|
begin
|
|
if Count>0 then
|
|
Result:=TAbstractExternalTool(fTools[Count-1])
|
|
else
|
|
Result:=GetDummyTool;
|
|
end;
|
|
|
|
function TLazPkgGraphBuildItem.GetTools(Index: integer): TAbstractExternalTool;
|
|
begin
|
|
Result:=TAbstractExternalTool(fTools[Index]);
|
|
end;
|
|
|
|
procedure TLazPkgGraphBuildItem.SetLazPackage(AValue: TLazPackage);
|
|
begin
|
|
if FLazPackage=AValue then Exit;
|
|
if FLazPackage<>nil then
|
|
FLazPackage.RemoveFreeNotification(Self);
|
|
FLazPackage:=AValue;
|
|
if FLazPackage<>nil then
|
|
FLazPackage.FreeNotification(Self);
|
|
end;
|
|
|
|
procedure TLazPkgGraphBuildItem.Notification(AComponent: TComponent;
|
|
Operation: TOperation);
|
|
begin
|
|
inherited Notification(AComponent, Operation);
|
|
if Operation=opRemove then begin
|
|
if LazPackage=AComponent then
|
|
LazPackage:=nil;
|
|
end;
|
|
end;
|
|
|
|
constructor TLazPkgGraphBuildItem.Create(AOwner: TComponent);
|
|
begin
|
|
inherited;
|
|
fTools:=TFPList.Create;
|
|
end;
|
|
|
|
destructor TLazPkgGraphBuildItem.Destroy;
|
|
begin
|
|
Clear;
|
|
FreeAndNil(fTools);
|
|
inherited Destroy;
|
|
end;
|
|
|
|
procedure TLazPkgGraphBuildItem.Clear;
|
|
var
|
|
i: Integer;
|
|
Tool: TAbstractExternalTool;
|
|
begin
|
|
{$IFDEF VerboseCheckInterPkgFiles}
|
|
debugln(['TLazPkgGraphBuildItem.Clear ',LazPackage.IDAsString]);
|
|
{$ENDIF}
|
|
for i:=Count-1 downto 0 do begin
|
|
Tool:=Tools[i];
|
|
if Tool.Data is TLazPkgGraphExtToolData then
|
|
TLazPkgGraphExtToolData(Tool.Data).BuildItem:=nil;
|
|
Tool.Release(Self);
|
|
end;
|
|
fTools.Clear;
|
|
end;
|
|
|
|
function TLazPkgGraphBuildItem.Add(Tool: TAbstractExternalTool): integer;
|
|
begin
|
|
if Tool=nil then exit(-1);
|
|
Tool.Reference(Self,'TLazPkgGraphBuildItem.Add');
|
|
if Tool.Data is TLazPkgGraphExtToolData then
|
|
TLazPkgGraphExtToolData(Tool.Data).BuildItem:=Self;
|
|
Result:=fTools.Add(Tool);
|
|
end;
|
|
|
|
{ TLazPkgGraphExtToolData }
|
|
|
|
constructor TLazPkgGraphExtToolData.Create(aKind, aModuleName, aFilename: string);
|
|
begin
|
|
inherited Create(aKind, aModuleName, aFilename);
|
|
CompilerParams:=TStringListUTF8Fast.Create;
|
|
end;
|
|
|
|
destructor TLazPkgGraphExtToolData.Destroy;
|
|
begin
|
|
FreeAndNil(CompilerParams);
|
|
inherited Destroy;
|
|
end;
|
|
|
|
{ TLazPackageGraph }
|
|
|
|
procedure TLazPackageGraph.DoDependencyChanged(Dependency: TPkgDependency);
|
|
begin
|
|
fChanged:=true;
|
|
if Assigned(OnDependencyModified) then OnDependencyModified(Dependency);
|
|
end;
|
|
|
|
function TLazPackageGraph.GetPackages(Index: integer): TLazPackage;
|
|
begin
|
|
Result:=TLazPackage(FItems[Index]);
|
|
end;
|
|
|
|
procedure TLazPackageGraph.SetRegistrationPackage(const AValue: TLazPackage);
|
|
begin
|
|
if FRegistrationPackage=AValue then exit;
|
|
FRegistrationPackage:=AValue;
|
|
LazarusPackageIntf.RegisterUnitProc:=@RegisterUnitHandler;
|
|
RegisterComponentsProc:=@RegisterComponentsGlobalHandler;
|
|
RegisterNoIconProc:=@RegisterNoIconGlobalHandler;
|
|
end;
|
|
|
|
procedure TLazPackageGraph.UpdateBrokenDependenciesToPackage(
|
|
APackage: TLazPackage);
|
|
var
|
|
ANode: TAVLTreeNode;
|
|
Dependency: TPkgDependency;
|
|
begin
|
|
BeginUpdate(false);
|
|
ANode:=FindLowestPkgDependencyNodeWithName(APackage.Name);
|
|
while ANode<>nil do begin
|
|
Dependency:=TPkgDependency(ANode.Data);
|
|
if (Dependency.LoadPackageResult<>lprSuccess)
|
|
and Dependency.IsCompatible(APackage) then begin
|
|
Dependency.LoadPackageResult:=lprUndefined;
|
|
OpenDependency(Dependency,false);
|
|
end;
|
|
ANode:=FindNextPkgDependencyNodeWithSameName(ANode);
|
|
end;
|
|
EndUpdate;
|
|
end;
|
|
|
|
function TLazPackageGraph.OpenDependencyWithPackageLink(
|
|
Dependency: TPkgDependency; PkgLink: TPackageLink; ShowAbort: boolean
|
|
): TModalResult;
|
|
var
|
|
AFilename: String;
|
|
NewPackage: TLazPackage;
|
|
XMLConfig: TXMLConfig;
|
|
Code: TCodeBuffer;
|
|
OldPackage: TLazPackage;
|
|
begin
|
|
NewPackage:=nil;
|
|
XMLConfig:=nil;
|
|
BeginUpdate(false);
|
|
try
|
|
AFilename:=PkgLink.GetEffectiveFilename;
|
|
if pvPkgSearch in Verbosity then
|
|
debugln(['Info: (lazarus) Open dependency: trying "'+Dependency.PackageName+'" in '+dbgs(PkgLink.Origin)+' links: "'+PkgLink.GetEffectiveFilename+'" ...']);
|
|
//debugln(['TLazPackageGraph.OpenDependencyWithPackageLink AFilename=',AFilename,' ',PkgLink.Origin=ploGlobal]);
|
|
if not FileExistsUTF8(AFilename) then begin
|
|
DebugLn('Note: (lazarus) Invalid Package Link: file "'+AFilename+'" does not exist.');
|
|
PkgLink.LPKFileDateValid:=false;
|
|
exit(mrCancel);
|
|
end;
|
|
if DirectoryExistsUTF8(AFilename) then begin
|
|
DebugLn('Note: (lazarus) Invalid Package Link: file "'+AFilename+'" is a directory.');
|
|
PkgLink.LPKFileDateValid:=false;
|
|
exit(mrCancel);
|
|
end;
|
|
if pvPkgSearch in Verbosity then
|
|
debugln(['Info: (lazarus) Open dependency: package file found: "'+AFilename+'". Parsing lpk ...']);
|
|
try
|
|
PkgLink.LPKFileDate:=FileDateToDateTimeDef(FileAgeUTF8(AFilename));
|
|
PkgLink.LPKFileDateValid:=true;
|
|
XMLConfig:=TXMLConfig.Create(nil);
|
|
NewPackage:=TLazPackage.Create;
|
|
NewPackage.BeginUpdate;
|
|
NewPackage.Filename:=AFilename;
|
|
NewPackage.OnModifySilently := @PkgModify;
|
|
Result:=LoadXMLConfigFromCodeBuffer(AFilename,XMLConfig,
|
|
Code,[lbfUpdateFromDisk,lbfRevert],ShowAbort);
|
|
if Result<>mrOk then exit;
|
|
NewPackage.LoadFromXMLConfig(XMLConfig,'Package/');
|
|
NewPackage.LPKSource:=Code;
|
|
except
|
|
on E: Exception do begin
|
|
DebugLn('Error: (lazarus) unable to read file "'+AFilename+'" ',E.Message);
|
|
exit(mrCancel);
|
|
end;
|
|
end;
|
|
if not NewPackage.IsMakingSense then begin
|
|
DebugLn('Error: (lazarus) invalid package file "'+AFilename+'".');
|
|
exit(mrCancel);
|
|
end;
|
|
if SysUtils.CompareText(PkgLink.Name,NewPackage.Name)<>0 then begin
|
|
DebugLn('Error: (lazarus) package file "'+AFilename+'" and name "'+NewPackage.Name+'" mismatch.');
|
|
exit(mrCancel);
|
|
end;
|
|
// ok
|
|
if pvPkgSearch in Verbosity then
|
|
debugln('Info: (lazarus) Open dependency ['+Dependency.PackageName+']: Success: "'+NewPackage.Filename+'"');
|
|
Result:=mrOk;
|
|
Dependency.RequiredPackage:=NewPackage;
|
|
Dependency.LoadPackageResult:=lprSuccess;
|
|
OldPackage:=FindPackageWithName(NewPackage.Name,NewPackage);
|
|
if OldPackage=nil then
|
|
AddPackage(NewPackage)
|
|
else
|
|
ReplacePackage(OldPackage,NewPackage);
|
|
finally
|
|
if Assigned(NewPackage) then
|
|
NewPackage.EndUpdate;
|
|
if Result<>mrOk then
|
|
NewPackage.Free;
|
|
EndUpdate;
|
|
FreeAndNil(XMLConfig);
|
|
end;
|
|
end;
|
|
|
|
function TLazPackageGraph.DeleteAmbiguousFiles(const Filename: string
|
|
): TModalResult;
|
|
begin
|
|
if Assigned(OnDeleteAmbiguousFiles) then
|
|
Result:=OnDeleteAmbiguousFiles(Filename)
|
|
else
|
|
Result:=mrOk;
|
|
end;
|
|
|
|
procedure TLazPackageGraph.AddMessage(TheUrgency: TMessageLineUrgency;
|
|
const Msg, Filename: string);
|
|
begin
|
|
if Assigned(FOnShowMessage) then
|
|
FOnShowMessage(TheUrgency, Msg, Filename, 0, 0, '')
|
|
else
|
|
DebugLn([MessageLineUrgencyNames[TheUrgency],': (lazarus) ',Msg,' Filename="',Filename,'"']);
|
|
end;
|
|
|
|
function TLazPackageGraph.OutputDirectoryIsWritable(APackage: TLazPackage;
|
|
Directory: string; Verbose: boolean): boolean;
|
|
var
|
|
CurDir: String;
|
|
begin
|
|
Result:=false;
|
|
//debugln(['TLazPackageGraph.OutputDirectoryIsWritable ',Directory]);
|
|
if not FilenameIsAbsolute(Directory) then
|
|
exit;
|
|
Directory:=ChompPathDelim(Directory);
|
|
if DirPathExistsCached(Directory) then begin
|
|
// the directory exist => check writable
|
|
Result:=DirectoryIsWritableCached(Directory);
|
|
exit;
|
|
end;
|
|
// the directory does not exist => check parent directory for writable
|
|
CurDir:=Directory;
|
|
repeat
|
|
CurDir:=ChompPathDelim(ExtractFilePath(CurDir));
|
|
until (CurDir='') or (CurDir=Directory) or DirPathExistsCached(CurDir);
|
|
if not DirectoryIsWritableCached(CurDir) then
|
|
exit(false);
|
|
// the directory does not exist, and its parent is writable => try creating it
|
|
if not ForceDirectoriesUTF8(Directory) then begin
|
|
if Verbose then begin
|
|
LazMessageWorker(lisPkgMangUnableToCreateDirectory,
|
|
Format(lisPkgMangUnableToCreateOutputDirectoryForPackage,
|
|
[Directory, LineEnding, APackage.IDAsString]),
|
|
mtError,[mbCancel]);
|
|
end;
|
|
debugln(['Error: (lazarus) unable to create package output directory "',Directory,'" of package "',APackage.IDAsString,'"']);
|
|
exit;
|
|
end;
|
|
Result:=true;
|
|
end;
|
|
|
|
function TLazPackageGraph.GetPackageCompilerParams(APackage: TLazPackage
|
|
): TStrings;
|
|
begin
|
|
Result:=APackage.CompilerOptions.MakeCompilerParams(
|
|
APackage.CompilerOptions.DefaultMakeOptionsFlags+[ccloAbsolutePaths]);
|
|
Result.Add(CreateRelativePath(APackage.GetSrcFilename,APackage.Directory));
|
|
end;
|
|
|
|
constructor TLazPackageGraph.Create;
|
|
begin
|
|
OnGetAllRequiredPackages:=@GetAllRequiredPackages;
|
|
FTree:=TAVLTree.Create(@CompareLazPackageID);
|
|
FItems:=TFPList.Create;
|
|
FLazarusBasePackages:=TFPList.Create;
|
|
FSrcBasePackages:=TStringListUTF8Fast.Create;
|
|
if GlobalMacroList<>nil then begin
|
|
GlobalMacroList.Add(TTransferMacro.Create('PkgDir','',
|
|
lisPkgMacroPackageDirectoryParameterIsPackageID, @MacroFunctionPkgDir, []));
|
|
GlobalMacroList.Add(TTransferMacro.Create('PkgSrcPath','',
|
|
lisPkgMacroPackageSourceSearchPathParameterIsPackageID,
|
|
@MacroFunctionPkgSrcPath,[]));
|
|
GlobalMacroList.Add(TTransferMacro.Create('PkgUnitPath','',
|
|
lisPkgMacroPackageUnitSearchPathParameterIsPackageID,
|
|
@MacroFunctionPkgUnitPath,[]));
|
|
GlobalMacroList.Add(TTransferMacro.Create('PkgIncPath','',
|
|
lisPkgMacroPackageIncludeFilesSearchPathParameterIsPackageID,
|
|
@MacroFunctionPkgIncPath,[]));
|
|
GlobalMacroList.Add(TTransferMacro.Create('PkgName','',
|
|
lisPkgMacroPackageNameParameterIsPackageID,
|
|
@MacroFunctionPkgName,[]));
|
|
GlobalMacroList.Add(TTransferMacro.Create('PkgOutDir','',
|
|
lisPkgMacroPackageOutputDirectoryParameterIsPackageID,
|
|
@MacroFunctionPkgOutDir,[]));
|
|
end;
|
|
end;
|
|
|
|
destructor TLazPackageGraph.Destroy;
|
|
begin
|
|
if LazarusPackageIntf.RegisterUnitProc=@RegisterUnitHandler then
|
|
LazarusPackageIntf.RegisterUnitProc:=nil;
|
|
if RegisterComponentsProc=@RegisterComponentsGlobalHandler then
|
|
RegisterComponentsProc:=nil;
|
|
if RegisterNoIconProc=@RegisterNoIconGlobalHandler then
|
|
RegisterNoIconProc:=nil;
|
|
if OnGetAllRequiredPackages=@GetAllRequiredPackages then
|
|
OnGetAllRequiredPackages:=nil;
|
|
Clear;
|
|
FreeAndNil(FLazarusBasePackages);
|
|
FreeAndNil(FSrcBasePackages);
|
|
FreeAndNil(FItems);
|
|
FreeAndNil(FTree);
|
|
FreeAndNil(FFindFileCache);
|
|
inherited Destroy;
|
|
end;
|
|
|
|
procedure TLazPackageGraph.Clear;
|
|
var
|
|
i: Integer;
|
|
begin
|
|
FLazarusBasePackages.Clear;
|
|
for i:=FItems.Count-1 downto 0 do Delete(i);
|
|
end;
|
|
|
|
procedure TLazPackageGraph.Delete(Index: integer);
|
|
var
|
|
CurPkg: TLazPackage;
|
|
begin
|
|
CurPkg:=Packages[Index];
|
|
if lpfDestroying in CurPkg.Flags then exit;
|
|
|
|
BeginUpdate(true);
|
|
CurPkg.Flags:=CurPkg.Flags+[lpfDestroying];
|
|
CurPkg.DefineTemplates.Active:=false;
|
|
|
|
if Assigned(OnDeletePackage) then OnDeletePackage(CurPkg);
|
|
|
|
if CurPkg=FCLPackage then
|
|
FFCLPackage:=nil
|
|
else if CurPkg=BuildIntfPackage then
|
|
FBuildIntfPackage:=nil
|
|
else if CurPkg=LCLBasePackage then
|
|
FLCLBasePackage:=nil
|
|
else if CurPkg=LCLPackage then
|
|
FLCLPackage:=nil
|
|
else if CurPkg=IDEIntfPackage then
|
|
FIDEIntfPackage:=nil
|
|
else if CurPkg=LazDebuggerIntfPackage then
|
|
FLazDebuggerIntfPackage:=nil
|
|
else if CurPkg=DebuggerIntfPackage then
|
|
FDebuggerIntfPackage:=nil
|
|
else if CurPkg=LazDebuggerGdbmiPackage then
|
|
FLazDebuggerGdbmiPackage:=nil
|
|
else if CurPkg=IdeUtilsPkgPackage then
|
|
FIdeUtilsPkgPackage:=nil
|
|
else if CurPkg=IdeConfigPackage then
|
|
FIdeConfigPackage:=nil
|
|
else if CurPkg=IdePackagerPackage then
|
|
FIdePackagerPackage:=nil
|
|
else if CurPkg=IdeProjectPackage then
|
|
FIdeProjectPackage:=nil
|
|
else if CurPkg=IdeDebuggerPackage then
|
|
FIdeDebuggerPackage:=nil
|
|
else if CurPkg=SynEditPackage then
|
|
FSynEditPackage:=nil
|
|
else if CurPkg=LazControlsPackage then
|
|
FLazControlsPackage:=nil
|
|
else if CurPkg=LazUtilsPackage then
|
|
FLazUtilsPackage:=nil
|
|
else if CurPkg=CodeToolsPackage then
|
|
FCodeToolsPackage:=nil;
|
|
FLazarusBasePackages.Remove(CurPkg);
|
|
|
|
FItems.Delete(Index);
|
|
FTree.Remove(CurPkg);
|
|
CurPkg.Free;
|
|
EndUpdate;
|
|
end;
|
|
|
|
function TLazPackageGraph.Count: integer;
|
|
begin
|
|
Result:=FItems.Count;
|
|
end;
|
|
|
|
procedure TLazPackageGraph.BeginUpdate(Change: boolean);
|
|
begin
|
|
inc(FUpdateLock);
|
|
if FUpdateLock=1 then begin
|
|
fChanged:=Change;
|
|
FLockedChangeStamp:=0;
|
|
FHasCompiledFpmakePackages := False;
|
|
if Assigned(OnBeginUpdate) then OnBeginUpdate(Self);
|
|
end else
|
|
fChanged:=fChanged or Change;
|
|
end;
|
|
|
|
procedure TLazPackageGraph.EndUpdate;
|
|
begin
|
|
if FUpdateLock<=0 then RaiseGDBException('TLazPackageGraph.EndUpdate');
|
|
dec(FUpdateLock);
|
|
if FUpdateLock=0 then begin
|
|
if FLockedChangeStamp>0 then
|
|
IncChangeStamp;
|
|
if Assigned(OnEndUpdate) then OnEndUpdate(Self,fChanged);
|
|
end;
|
|
end;
|
|
|
|
function TLazPackageGraph.Updating: boolean;
|
|
begin
|
|
Result:=FUpdateLock>0;
|
|
end;
|
|
|
|
procedure TLazPackageGraph.RebuildDefineTemplates;
|
|
var
|
|
i: Integer;
|
|
begin
|
|
for i:=0 to Count-1 do
|
|
Packages[i].DefineTemplates.AllChanged(false);
|
|
end;
|
|
|
|
function TLazPackageGraph.MacroFunctionPkgDir(const s: string;
|
|
const Data: PtrInt; var Abort: boolean): string;
|
|
var
|
|
APackage: TLazPackage;
|
|
begin
|
|
if GetPackageFromMacroParameter(s,APackage) then
|
|
Result:=APackage.Directory
|
|
else
|
|
Result:='';
|
|
end;
|
|
|
|
function TLazPackageGraph.MacroFunctionPkgSrcPath(const s: string;
|
|
const Data: PtrInt; var Abort: boolean): string;
|
|
var
|
|
APackage: TLazPackage;
|
|
begin
|
|
if GetPackageFromMacroParameter(s,APackage) then
|
|
Result:=APackage.SourceDirectories.CreateSearchPathFromAllFiles
|
|
else
|
|
Result:='';
|
|
end;
|
|
|
|
function TLazPackageGraph.MacroFunctionPkgUnitPath(const s: string;
|
|
const Data: PtrInt; var Abort: boolean): string;
|
|
var
|
|
APackage: TLazPackage;
|
|
begin
|
|
if GetPackageFromMacroParameter(s,APackage) then
|
|
Result:=APackage.GetUnitPath(false)
|
|
else
|
|
Result:='';
|
|
end;
|
|
|
|
function TLazPackageGraph.MacroFunctionPkgIncPath(const s: string;
|
|
const Data: PtrInt; var Abort: boolean): string;
|
|
var
|
|
APackage: TLazPackage;
|
|
begin
|
|
if GetPackageFromMacroParameter(s,APackage) then
|
|
Result:=APackage.GetIncludePath(false)
|
|
else
|
|
Result:='';
|
|
end;
|
|
|
|
function TLazPackageGraph.MacroFunctionPkgName(const s: string;
|
|
const Data: PtrInt; var Abort: boolean): string;
|
|
begin
|
|
Result := s;
|
|
end;
|
|
|
|
function TLazPackageGraph.MacroFunctionPkgOutDir(const s: string;
|
|
const Data: PtrInt; var Abort: boolean): string;
|
|
var
|
|
APackage: TLazPackage;
|
|
begin
|
|
if GetPackageFromMacroParameter(s,APackage) then
|
|
Result:=APackage.GetOutputDirectory
|
|
else
|
|
Result:='';
|
|
end;
|
|
|
|
function TLazPackageGraph.MacroFunctionCTPkgDir(Data: Pointer): boolean;
|
|
var
|
|
FuncData: PReadFunctionData;
|
|
APackage: TLazPackage;
|
|
begin
|
|
FuncData:=PReadFunctionData(Data);
|
|
Result:=GetPackageFromMacroParameter(FuncData^.Param,APackage);
|
|
if Result then
|
|
FuncData^.Result:=APackage.Directory;
|
|
end;
|
|
|
|
function TLazPackageGraph.MacroFunctionCTPkgSrcPath(Data: Pointer): boolean;
|
|
var
|
|
FuncData: PReadFunctionData;
|
|
APackage: TLazPackage;
|
|
begin
|
|
FuncData:=PReadFunctionData(Data);
|
|
Result:=GetPackageFromMacroParameter(FuncData^.Param,APackage);
|
|
if Result then
|
|
FuncData^.Result:=APackage.GetUnitPath(false)+';'+APackage.GetSrcPath(false);
|
|
end;
|
|
|
|
function TLazPackageGraph.MacroFunctionCTPkgUnitPath(Data: Pointer): boolean;
|
|
var
|
|
FuncData: PReadFunctionData;
|
|
APackage: TLazPackage;
|
|
begin
|
|
FuncData:=PReadFunctionData(Data);
|
|
Result:=GetPackageFromMacroParameter(FuncData^.Param,APackage);
|
|
if Result then
|
|
FuncData^.Result:=APackage.GetUnitPath(false);
|
|
end;
|
|
|
|
function TLazPackageGraph.MacroFunctionCTPkgIncPath(Data: Pointer): boolean;
|
|
var
|
|
FuncData: PReadFunctionData;
|
|
APackage: TLazPackage;
|
|
begin
|
|
FuncData:=PReadFunctionData(Data);
|
|
Result:=GetPackageFromMacroParameter(FuncData^.Param,APackage);
|
|
if Result then
|
|
FuncData^.Result:=APackage.GetIncludePath(false);
|
|
end;
|
|
|
|
function TLazPackageGraph.MacroFunctionCTPkgName(Data: Pointer): boolean;
|
|
var
|
|
FuncData: PReadFunctionData;
|
|
begin
|
|
FuncData:=PReadFunctionData(Data);
|
|
FuncData^.Result:=GetIdentifier(PChar(FuncData^.Param));
|
|
Result:=true;
|
|
end;
|
|
|
|
function TLazPackageGraph.MacroFunctionCTPkgOutDir(Data: Pointer): boolean;
|
|
var
|
|
FuncData: PReadFunctionData;
|
|
APackage: TLazPackage;
|
|
begin
|
|
FuncData:=PReadFunctionData(Data);
|
|
Result:=GetPackageFromMacroParameter(FuncData^.Param,APackage);
|
|
if Result then
|
|
FuncData^.Result:=APackage.GetOutputDirectory;
|
|
end;
|
|
|
|
function TLazPackageGraph.GetPackageFromMacroParameter(const TheID: string;
|
|
out APackage: TLazPackage): boolean;
|
|
var
|
|
PkgID: TLazPackageID;
|
|
begin
|
|
PkgID:=TLazPackageID.Create;
|
|
if PkgID.StringToID(TheID) then begin
|
|
APackage:=FindPackageWithIDMask(PkgID);
|
|
if APackage=nil then begin
|
|
DebugLn('Warning: (lazarus) unknown macro package id "',TheID,'"');
|
|
end;
|
|
end else begin
|
|
APackage:=nil;
|
|
DebugLn('Warning: (lazarus) invalid macro package id "',TheID,'"');
|
|
end;
|
|
PkgID.Free;
|
|
Result:=APackage<>nil;
|
|
end;
|
|
|
|
function TLazPackageGraph.SrcEditFileIsModified(const SrcFilename: string
|
|
): boolean;
|
|
begin
|
|
if Assigned(OnSrcEditFileIsModified) then
|
|
Result:=OnSrcEditFileIsModified(SrcFilename)
|
|
else
|
|
Result:=false;
|
|
end;
|
|
|
|
function TLazPackageGraph.FindLowestPkgNodeByName(const PkgName: string
|
|
): TAVLTreeNode;
|
|
var
|
|
PriorNode: TAVLTreeNode;
|
|
begin
|
|
Result:=nil;
|
|
if PkgName='' then exit;
|
|
Result:=FTree.FindKey(PChar(PkgName),@CompareNameWithPackageID);
|
|
while Result<>nil do begin
|
|
PriorNode:=FTree.FindPrecessor(Result);
|
|
if (PriorNode=nil)
|
|
or (SysUtils.CompareText(PkgName,TLazPackage(PriorNode.Data).Name)<>0) then
|
|
break;
|
|
Result:=PriorNode;
|
|
end;
|
|
end;
|
|
|
|
function TLazPackageGraph.FindNextSameName(ANode: TAVLTreeNode): TAVLTreeNode;
|
|
var
|
|
NextNode: TAVLTreeNode;
|
|
begin
|
|
Result:=nil;
|
|
if ANode=nil then exit;
|
|
NextNode:=FTree.FindSuccessor(ANode);
|
|
if (NextNode=nil)
|
|
or (SysUtils.CompareText(TLazPackage(ANode.Data).Name,
|
|
TLazPackage(NextNode.Data).Name)<>0)
|
|
then exit;
|
|
Result:=NextNode;
|
|
end;
|
|
|
|
function TLazPackageGraph.FindNodeOfDependency(Dependency: TPkgDependency;
|
|
Flags: TFindPackageFlags): TAVLTreeNode;
|
|
var
|
|
CurPkg: TLazPackage;
|
|
begin
|
|
// search in all packages with the same name
|
|
Result:=FindLowestPkgNodeByName(Dependency.PackageName);
|
|
while Result<>nil do begin
|
|
CurPkg:=TLazPackage(Result.Data);
|
|
// check version
|
|
if (not (fpfIgnoreVersion in Flags))
|
|
and (not Dependency.IsCompatible(CurPkg)) then begin
|
|
Result:=FindNextSameName(Result);
|
|
continue;
|
|
end;
|
|
// check loaded packages
|
|
if (fpfSearchInLoadedPkgs in Flags) then exit;
|
|
// check installed packages
|
|
if (fpfSearchInInstalledPckgs in Flags)
|
|
and (CurPkg.Installed<>pitNope) then exit;
|
|
// check autoinstall packages
|
|
if (fpfSearchInAutoInstallPckgs in Flags)
|
|
and (CurPkg.AutoInstall<>pitNope) then exit;
|
|
// check packages with opened editor
|
|
if (fpfSearchInPckgsWithEditor in Flags) and (CurPkg.Editor<>nil) then exit;
|
|
// search next package node with same name
|
|
Result:=FindNextSameName(Result);
|
|
end;
|
|
end;
|
|
|
|
function TLazPackageGraph.FindOpenPackage(Dependency: TPkgDependency;
|
|
Flags: TFindPackageFlags): TLazPackage;
|
|
var
|
|
ANode: TAVLTreeNode;
|
|
begin
|
|
ANode:=FindNodeOfDependency(Dependency,Flags);
|
|
if ANode<>nil then
|
|
Result:=TLazPackage(ANode.Data)
|
|
else
|
|
Result:=nil;
|
|
end;
|
|
|
|
function TLazPackageGraph.FindPackageWithName(const PkgName: string;
|
|
IgnorePackage: TLazPackage): TLazPackage;
|
|
var
|
|
ANode: TAVLTreeNode;
|
|
begin
|
|
Result:=nil;
|
|
ANode:=FindLowestPkgNodeByName(PkgName);
|
|
if ANode<>nil then begin
|
|
Result:=TLazPackage(ANode.Data);
|
|
if Result=IgnorePackage then begin
|
|
Result:=nil;
|
|
ANode:=FindNextSameName(ANode);
|
|
if ANode<>nil then
|
|
Result:=TLazPackage(ANode.Data);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
function TLazPackageGraph.FindPackageWithID(PkgID: TLazPackageID): TLazPackage;
|
|
var
|
|
ANode: TAVLTreeNode;
|
|
begin
|
|
ANode:=FTree.Find(PkgID);
|
|
if ANode<>nil then
|
|
Result:=TLazPackage(ANode.Data)
|
|
else
|
|
Result:=nil;
|
|
end;
|
|
|
|
function TLazPackageGraph.FindPackageWithIDMask(PkgIDMask: TLazPackageID
|
|
): TLazPackage;
|
|
var
|
|
ANode: TAVLTreeNode;
|
|
begin
|
|
ANode:=FTree.FindKey(PkgIDMask,@ComparePkgIDMaskWithPackageID);
|
|
if ANode<>nil then
|
|
Result:=TLazPackage(ANode.Data)
|
|
else
|
|
Result:=nil;
|
|
end;
|
|
|
|
function TLazPackageGraph.FindPackageProvidingName(
|
|
FirstDependency: TPkgDependency;
|
|
const Name: string): TLazPackage;
|
|
|
|
function Search(ADependency: TPkgDependency; out Found: TLazPackage): boolean;
|
|
begin
|
|
Found:=nil;
|
|
while ADependency<>nil do begin
|
|
Found:=ADependency.RequiredPackage;
|
|
//DebugLn(['Search ',Found.Name,' ',Found.ProvidesPackage(Name),' "',Found.Provides.Text,'"']);
|
|
if (Found<>nil) and (not (lpfVisited in Found.Flags)) then begin
|
|
Found.Flags:=Found.Flags+[lpfVisited];
|
|
if Found.ProvidesPackage(Name)
|
|
or Search(Found.FirstRequiredDependency,Found) then
|
|
exit(true);
|
|
end;
|
|
ADependency:=ADependency.NextRequiresDependency;
|
|
end;
|
|
Found:=nil;
|
|
Result:=false;
|
|
end;
|
|
|
|
begin
|
|
MarkAllPackagesAsNotVisited;
|
|
Search(FirstDependency,Result);
|
|
end;
|
|
|
|
function TLazPackageGraph.FindDependencyRecursively(
|
|
FirstDependency: TPkgDependency; PkgID: TLazPackageID): TPkgDependency;
|
|
// returns one compatible dependency for PkgID
|
|
|
|
function Find(CurDependency: TPkgDependency): TPkgDependency;
|
|
var
|
|
RequiredPackage: TLazPackage;
|
|
begin
|
|
while CurDependency<>nil do begin
|
|
if CurDependency.IsCompatible(PkgID) then begin
|
|
Result:=CurDependency;
|
|
exit;
|
|
end;
|
|
if (CurDependency.DependencyType=pdtLazarus) and (CurDependency.LoadPackageResult=lprSuccess) then begin
|
|
RequiredPackage:=CurDependency.RequiredPackage;
|
|
if (not (lpfVisited in RequiredPackage.Flags)) then begin
|
|
RequiredPackage.Flags:=RequiredPackage.Flags+[lpfVisited];
|
|
Result:=Find(RequiredPackage.FirstRequiredDependency);
|
|
if Result<>nil then exit;
|
|
end;
|
|
end;
|
|
CurDependency:=CurDependency.NextRequiresDependency;
|
|
end;
|
|
Result:=nil;
|
|
end;
|
|
|
|
begin
|
|
if FirstDependency=nil then exit(nil);
|
|
MarkAllPackagesAsNotVisited;
|
|
Result:=Find(FirstDependency);
|
|
end;
|
|
|
|
function TLazPackageGraph.FindDependencyRecursively(
|
|
FirstDependency: TPkgDependency; const PkgName: string): TPkgDependency;
|
|
// returns one compatible dependency for PkgName
|
|
|
|
function Find(CurDependency: TPkgDependency): TPkgDependency;
|
|
var
|
|
RequiredPackage: TLazPackage;
|
|
begin
|
|
while CurDependency<>nil do begin
|
|
if SysUtils.CompareText(CurDependency.PackageName,PkgName)=0 then begin
|
|
Result:=CurDependency;
|
|
exit;
|
|
end;
|
|
if (CurDependency.DependencyType=pdtLazarus) and (CurDependency.LoadPackageResult=lprSuccess) then begin
|
|
RequiredPackage:=CurDependency.RequiredPackage;
|
|
if (not (lpfVisited in RequiredPackage.Flags)) then begin
|
|
RequiredPackage.Flags:=RequiredPackage.Flags+[lpfVisited];
|
|
Result:=Find(RequiredPackage.FirstRequiredDependency);
|
|
if Result<>nil then exit;
|
|
end;
|
|
end;
|
|
CurDependency:=CurDependency.NextRequiresDependency;
|
|
end;
|
|
Result:=nil;
|
|
end;
|
|
|
|
begin
|
|
if FirstDependency=nil then exit(nil);
|
|
MarkAllPackagesAsNotVisited;
|
|
Result:=Find(FirstDependency);
|
|
end;
|
|
|
|
function TLazPackageGraph.FindConflictRecursively(
|
|
FirstDependency: TPkgDependency; PkgID: TLazPackageID): TPkgDependency;
|
|
// returns one conflicting dependency for PkgID
|
|
|
|
function Find(CurDependency: TPkgDependency): TPkgDependency;
|
|
var
|
|
RequiredPackage: TLazPackage;
|
|
begin
|
|
while CurDependency<>nil do begin
|
|
if (SysUtils.CompareText(CurDependency.PackageName,PkgID.Name)=0)
|
|
and (not CurDependency.IsCompatible(PkgID)) then begin
|
|
Result:=CurDependency;
|
|
exit;
|
|
end;
|
|
if CurDependency.LoadPackageResult=lprSuccess then begin
|
|
RequiredPackage:=CurDependency.RequiredPackage;
|
|
if Assigned(RequiredPackage) and (not (lpfVisited in RequiredPackage.Flags)) then begin
|
|
RequiredPackage.Flags:=RequiredPackage.Flags+[lpfVisited];
|
|
Result:=Find(RequiredPackage.FirstRequiredDependency);
|
|
if Result<>nil then exit;
|
|
end;
|
|
end;
|
|
CurDependency:=CurDependency.NextRequiresDependency;
|
|
end;
|
|
Result:=nil;
|
|
end;
|
|
|
|
begin
|
|
if FirstDependency=nil then exit(nil);
|
|
MarkAllPackagesAsNotVisited;
|
|
Result:=Find(FirstDependency);
|
|
end;
|
|
|
|
function TLazPackageGraph.FindRuntimePkgOnlyRecursively(
|
|
FirstDependency: TPkgDependency): TPkgDependency;
|
|
// returns one dependency using a runtime only package
|
|
|
|
function Find(CurDependency: TPkgDependency): TPkgDependency;
|
|
var
|
|
RequiredPackage: TLazPackage;
|
|
begin
|
|
while CurDependency<>nil do begin
|
|
if (CurDependency.LoadPackageResult=lprSuccess) and (CurDependency.DependencyType=pdtLazarus) then begin
|
|
RequiredPackage:=CurDependency.RequiredPackage;
|
|
if (not (lpfVisited in RequiredPackage.Flags)) then begin
|
|
if RequiredPackage.PackageType=lptRunTimeOnly then
|
|
exit(CurDependency);
|
|
RequiredPackage.Flags:=RequiredPackage.Flags+[lpfVisited];
|
|
Result:=Find(RequiredPackage.FirstRequiredDependency);
|
|
if Result<>nil then exit;
|
|
end;
|
|
end;
|
|
CurDependency:=CurDependency.NextRequiresDependency;
|
|
end;
|
|
Result:=nil;
|
|
end;
|
|
|
|
begin
|
|
if FirstDependency=nil then exit(nil);
|
|
MarkAllPackagesAsNotVisited;
|
|
Result:=Find(FirstDependency);
|
|
end;
|
|
|
|
function TLazPackageGraph.FindUnit(StartPackage: TLazPackage;
|
|
const TheUnitName: string;
|
|
WithRequiredPackages, IgnoreDeleted: boolean): TPkgFile;
|
|
var
|
|
ADependency: TPkgDependency;
|
|
ARequiredPackage: TLazPackage;
|
|
begin
|
|
Result:=StartPackage.FindUnit(TheUnitName,IgnoreDeleted);
|
|
if Result<>nil then exit;
|
|
// search also in all required packages
|
|
if WithRequiredPackages then begin
|
|
ADependency:=StartPackage.FirstRequiredDependency;
|
|
while ADependency<>nil do begin
|
|
ARequiredPackage:=FindOpenPackage(ADependency,[fpfSearchInInstalledPckgs]);
|
|
if ARequiredPackage<>nil then begin
|
|
Result:=ARequiredPackage.FindUnit(TheUnitName,IgnoreDeleted);
|
|
if Result<>nil then exit;
|
|
end;
|
|
ADependency:=ADependency.NextRequiresDependency;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
function TLazPackageGraph.FindUnitInAllPackages(
|
|
const TheUnitName: string; IgnoreDeleted: boolean): TPkgFile;
|
|
var
|
|
Cnt: Integer;
|
|
i: Integer;
|
|
begin
|
|
Cnt:=Count;
|
|
for i:=0 to Cnt-1 do begin
|
|
Result:=FindUnit(Packages[i],TheUnitName,false,IgnoreDeleted);
|
|
if Result<>nil then exit;
|
|
end;
|
|
Result:=nil;
|
|
end;
|
|
|
|
function TLazPackageGraph.FindUnitInInstalledPackages(
|
|
const TheUnitName: string; IgnoreDeleted: boolean): TPkgFile;
|
|
var
|
|
Cnt: Integer;
|
|
i: Integer;
|
|
Pkg: TLazPackage;
|
|
begin
|
|
Cnt:=Count;
|
|
for i:=0 to Cnt-1 do begin
|
|
Pkg:=Packages[i];
|
|
if Pkg.Installed=pitNope then continue;
|
|
Result:=FindUnit(Pkg,TheUnitName,false,IgnoreDeleted);
|
|
if Result<>nil then exit;
|
|
end;
|
|
Result:=nil;
|
|
end;
|
|
|
|
function TLazPackageGraph.GetMapSourceDirectoryToPackage(
|
|
IgnorePackage: TLazPackage): TFilenameToPointerTree;
|
|
var
|
|
i: Integer;
|
|
aPackage: TLazPackage;
|
|
SearchPath: String;
|
|
p: Integer;
|
|
Dir: String;
|
|
begin
|
|
Result:=TFilenameToPointerTree.Create(false);
|
|
for i:=0 to Count-1 do begin
|
|
aPackage:=Packages[i];
|
|
if aPackage.IsVirtual then continue;
|
|
if aPackage=IgnorePackage then continue;
|
|
SearchPath:=aPackage.SourceDirectories.CreateSearchPathFromAllFiles;
|
|
p:=1;
|
|
repeat
|
|
Dir:=GetNextDirectoryInSearchPath(SearchPath,p);
|
|
if Dir='' then break;
|
|
Dir:=ChompPathDelim(Dir);
|
|
Result[Dir]:=aPackage;
|
|
until false;
|
|
end;
|
|
end;
|
|
|
|
function TLazPackageGraph.EstimateCompileLoad(APackage: TLazPackage): int64;
|
|
var
|
|
PkgFile: TPkgFile;
|
|
i: Integer;
|
|
begin
|
|
Result:=1;
|
|
for i:=0 to APackage.FileCount-1 do begin
|
|
PkgFile:=APackage.Files[i];
|
|
if PkgFile.FileType in [pftUnit,pftMainUnit,pftInclude,pftLFM] then begin
|
|
inc(Result,CodeToolBoss.DirectoryCachePool.FileSize(PkgFile.Filename));
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
function TLazPackageGraph.FindFileInAllPackages(const TheFilename: string;
|
|
IgnoreDeleted, FindVirtualFile: boolean): TPkgFile;
|
|
begin
|
|
if FFindFileCache=nil then
|
|
FFindFileCache := TLazPackageGraphFileCache.Create(Self);
|
|
|
|
Result := FFindFileCache.FindFileInAllPackages(TheFilename, IgnoreDeleted, FindVirtualFile);
|
|
end;
|
|
|
|
procedure TLazPackageGraph.FindPossibleOwnersOfUnit(const TheFilename: string;
|
|
OwnerList: TFPList);
|
|
var
|
|
Cnt: Integer;
|
|
i: Integer;
|
|
APackage: TLazPackage;
|
|
SrcDir: String;
|
|
|
|
function SrcDirInPath(Dirs: String): boolean;
|
|
begin
|
|
Result:=FindPathInSearchPath(PChar(SrcDir),length(SrcDir),
|
|
PChar(Dirs),length(Dirs))<>nil;
|
|
end;
|
|
|
|
begin
|
|
if not FilenameIsAbsolute(TheFilename) then exit;
|
|
Cnt:=Count;
|
|
SrcDir:=ExtractFilePath(TheFilename);
|
|
for i:=0 to Cnt-1 do begin
|
|
APackage:=Packages[i];
|
|
if APackage.IsVirtual then continue;
|
|
// source directories + unit path without inherited paths + base directory + output directory
|
|
if SrcDirInPath(APackage.CompilerOptions.GetParsedPath(pcosUnitPath,icoNone,false,true))
|
|
or SrcDirInPath(APackage.SourceDirectories.CreateSearchPathFromAllFiles)
|
|
or SrcDirInPath(APackage.GetOutputDirectory)
|
|
then
|
|
OwnerList.Add(APackage);
|
|
end;
|
|
end;
|
|
|
|
function TLazPackageGraph.FindPackageWithFilename(const TheFilename: string
|
|
): TLazPackage;
|
|
var
|
|
Cnt: Integer;
|
|
i: Integer;
|
|
begin
|
|
Cnt:=Count;
|
|
for i:=0 to Cnt-1 do begin
|
|
Result:=Packages[i];
|
|
if Result.IsVirtual then continue;
|
|
if CompareFilenames(TheFilename,Result.Filename)=0 then
|
|
exit;
|
|
end;
|
|
Result:=nil;
|
|
end;
|
|
|
|
function TLazPackageGraph.CreateUniqueUnitName(const Prefix: string): string;
|
|
begin
|
|
Result:=Prefix;
|
|
while FindUnitInAllPackages(Result,false)<>nil do
|
|
Result:=CreateNextIdentifier(Result);
|
|
end;
|
|
|
|
function TLazPackageGraph.PackageNameExists(const PkgName: string;
|
|
IgnorePackage: TLazPackage): boolean;
|
|
var
|
|
ANode: TAVLTreeNode;
|
|
begin
|
|
Result:=false;
|
|
if PkgName<>'' then begin
|
|
ANode:=FindLowestPkgNodeByName(PkgName);
|
|
if (ANode<>nil) and (IgnorePackage=TLazPackage(ANode.Data)) then
|
|
ANode:=FindNextSameName(ANode);
|
|
Result:=ANode<>nil;
|
|
end;
|
|
end;
|
|
|
|
procedure TLazPackageGraph.PkgModify(Sender: TObject);
|
|
begin
|
|
IncChangeStamp;
|
|
end;
|
|
|
|
function TLazPackageGraph.DependencyExists(Dependency: TPkgDependency;
|
|
Flags: TFindPackageFlags): boolean;
|
|
begin
|
|
Result:=true;
|
|
if FindNodeOfDependency(Dependency,Flags)<>nil then exit;
|
|
if FindPackageWithName(Dependency.PackageName,nil)=nil then begin
|
|
// no package with same name open
|
|
// -> try package links
|
|
if fpfSearchInPkgLinks in Flags then
|
|
if LazPackageLinks.FindLinkWithDependency(Dependency)<>nil then exit;
|
|
end else begin
|
|
// there is already a package with this name open, but the wrong version
|
|
end;
|
|
Result:=false;
|
|
end;
|
|
|
|
function TLazPackageGraph.CreateUniquePkgName(Prefix: string;
|
|
IgnorePackage: TLazPackage): string;
|
|
var
|
|
i: Integer;
|
|
begin
|
|
if not IsValidPkgName(Prefix) then
|
|
RaiseGDBException('invalid pkg name "'+Prefix+'"');
|
|
// try Prefix alone
|
|
if not PackageNameExists(Prefix,IgnorePackage) then begin
|
|
Result:=Prefix;
|
|
end else begin
|
|
// try Prefix + number
|
|
i:=1;
|
|
while PackageNameExists(Prefix+IntToStr(i),IgnorePackage) do inc(i);
|
|
Result:=Prefix+IntToStr(i);
|
|
end;
|
|
end;
|
|
|
|
function TLazPackageGraph.CreateNewPackage(const Prefix: string): TLazPackage;
|
|
begin
|
|
BeginUpdate(true);
|
|
Result:=TLazPackage.CreateAndClear;
|
|
Result.OnModifySilently:=@PkgModify;
|
|
Result.Name:=CreateUniquePkgName(Prefix,nil);
|
|
AddPackage(Result);
|
|
EndUpdate;
|
|
end;
|
|
|
|
procedure TLazPackageGraph.ConsistencyCheck;
|
|
begin
|
|
CheckList(FItems,true,true,true);
|
|
end;
|
|
|
|
procedure TLazPackageGraph.RegisterUnitHandler(const TheUnitName: string;
|
|
RegisterProc: TRegisterProc);
|
|
begin
|
|
if AbortRegistration then exit;
|
|
|
|
ErrorMsg:='';
|
|
FRegistrationFile:=nil;
|
|
FRegistrationUnitName:='';
|
|
|
|
// check package
|
|
if FRegistrationPackage=nil then begin
|
|
RegistrationError('Unit: '+TheUnitName);
|
|
exit;
|
|
end;
|
|
try
|
|
// check unitname
|
|
FRegistrationUnitName:=TheUnitName;
|
|
if not IsValidUnitName(FRegistrationUnitName) then begin
|
|
RegistrationError(Format(lisPkgSysInvalidUnitname, [FRegistrationUnitName]));
|
|
exit;
|
|
end;
|
|
// check unit file
|
|
FRegistrationFile:=FindUnit(FRegistrationPackage,FRegistrationUnitName,true,true);
|
|
if FRegistrationFile=nil then begin
|
|
if not (FRegistrationPackage.Missing) then begin
|
|
// lpk exists, but file is missing => warn
|
|
FRegistrationFile:=
|
|
FRegistrationPackage.FindUnit(FRegistrationUnitName,false);
|
|
if FRegistrationFile=nil then begin
|
|
RegistrationError(Format(
|
|
lisPkgSysUnitWasNotFoundInTheLpkFileProbablyThisLpkFileWasN, [
|
|
FRegistrationUnitName, LineEnding]));
|
|
end else begin
|
|
if not (pffReportedAsRemoved in FRegistrationFile.Flags) then begin
|
|
RegistrationError(
|
|
Format(lisPkgSysUnitWasRemovedFromPackageLpk, [
|
|
FRegistrationUnitName]));
|
|
FRegistrationFile.Flags:=FRegistrationFile.Flags+[pffReportedAsRemoved];
|
|
end;
|
|
end;
|
|
end;
|
|
exit;
|
|
end;
|
|
CallRegisterProc(RegisterProc);
|
|
// clean up
|
|
finally
|
|
FRegistrationUnitName:='';
|
|
FRegistrationFile:=nil;
|
|
end;
|
|
end;
|
|
|
|
procedure TLazPackageGraph.RegisterComponentsHandler(const Page: string;
|
|
ComponentClasses: array of TComponentClass);
|
|
var
|
|
i: integer;
|
|
CurComponent: TComponentClass;
|
|
NewPkgComponent: TPkgComponent;
|
|
CurClassname: string;
|
|
begin
|
|
{$IFDEF IDE_MEM_CHECK}
|
|
CheckHeap('TLazPackageGraph.RegisterComponentsHandler Page='+Page);
|
|
{$ENDIF}
|
|
if AbortRegistration or (Low(ComponentClasses)>High(ComponentClasses)) then
|
|
exit;
|
|
|
|
ErrorMsg:='';
|
|
|
|
// check package
|
|
if FRegistrationPackage=nil then begin
|
|
RegistrationError('');
|
|
exit;
|
|
end;
|
|
// check unit file
|
|
if FRegistrationFile=nil then begin
|
|
RegistrationError(lisPkgSysCanNotRegisterComponentsWithoutUnit);
|
|
exit;
|
|
end;
|
|
// register components
|
|
for i:=Low(ComponentClasses) to High(ComponentClasses) do begin
|
|
CurComponent:=ComponentClasses[i];
|
|
if (CurComponent=nil) then continue;
|
|
{$IFNDEF StopOnRegError}
|
|
try
|
|
{$ENDIF}
|
|
CurClassname:=CurComponent.Classname;
|
|
if not IsValidIdent(CurClassname) then begin
|
|
RegistrationError(lisPkgSysInvalidComponentClass);
|
|
continue;
|
|
end;
|
|
CurClassname:=CurComponent.UnitName+'/'+CurClassname;
|
|
{$IFNDEF StopOnRegError}
|
|
except
|
|
on E: Exception do begin
|
|
RegistrationError(E.Message);
|
|
continue;
|
|
end;
|
|
end;
|
|
{$ENDIF}
|
|
if (IDEComponentPalette<>nil)
|
|
and (IDEComponentPalette.FindRegComponent(CurComponent.ClassType)<>nil) then
|
|
RegistrationError(Format(lisPkgSysComponentClassAlreadyDefined,[CurClassname]));
|
|
if AbortRegistration then exit;
|
|
// add the component to the package owning the file
|
|
// (e.g. a designtime package can register units of a runtime packages)
|
|
NewPkgComponent:=
|
|
FRegistrationFile.LazPackage.AddComponent(FRegistrationFile,Page,CurComponent);
|
|
//DebugLn('TLazPackageGraph.RegisterComponentsHandler Page="',Page,
|
|
// '" CurComponent=',CurClassname,' FRegistrationFile=',FRegistrationFile.Filename);
|
|
if IDEComponentPalette<>nil then
|
|
IDEComponentPalette.AddRegComponent(NewPkgComponent);
|
|
end;
|
|
end;
|
|
|
|
procedure TLazPackageGraph.RegistrationError(const Msg: string);
|
|
var
|
|
DlgResult: Integer;
|
|
IgnoreAll: Integer;
|
|
begin
|
|
// create nice and useful error message
|
|
|
|
// current registration package
|
|
if FRegistrationPackage=nil then begin
|
|
ErrorMsg:=lisPkgSysRegisterUnitWasCalledButNoPackageIsRegistering;
|
|
end else begin
|
|
ErrorMsg:='Package: "'+FRegistrationPackage.IDAsString+'"';
|
|
if FRegistrationPackage.Missing then
|
|
ErrorMsg:=Format(lisPkgSysTheLpkFileWasNotFound, [ErrorMsg, LineEnding])
|
|
else
|
|
ErrorMsg:=Format(lisPkgSysLPKFilename, [ErrorMsg, LineEnding, FRegistrationPackage.Filename]);
|
|
// current unitname
|
|
if FRegistrationUnitName<>'' then
|
|
ErrorMsg:=Format(lisPkgSysUnitName, [ErrorMsg, LineEnding, FRegistrationUnitName]);
|
|
// current file
|
|
if FRegistrationFile<>nil then
|
|
ErrorMsg:=Format(lisPkgSysFileName, [ErrorMsg, LineEnding, FRegistrationFile.Filename]);
|
|
end;
|
|
// append message
|
|
if Msg<>'' then
|
|
ErrorMsg:=ErrorMsg+LineEnding+LineEnding+Msg;
|
|
debugln(['Error: (lazarus) register failed: ',dbgstr(ErrorMsg)]);
|
|
|
|
if AbortRegistration or QuietRegistration then exit;
|
|
|
|
// tell user
|
|
IgnoreAll:=mrLast+1;
|
|
DlgResult:=LazQuestionWorker(lisPkgSysPackageRegistrationError, ErrorMsg,
|
|
mtError, [mrIgnore,
|
|
IgnoreAll, lispIgnoreAll,
|
|
mrAbort]);
|
|
if DlgResult=IgnoreAll then
|
|
QuietRegistration:=true;
|
|
if DlgResult=mrAbort then
|
|
AbortRegistration:=true;
|
|
end;
|
|
|
|
procedure TLazPackageGraph.ExtToolBuildStopped(Sender: TObject);
|
|
var
|
|
PkgCompileTool: TAbstractExternalTool;
|
|
Data: TLazPkgGraphExtToolData;
|
|
aPackage: TLazPackage;
|
|
SrcF: String;
|
|
SrcPPUFileExists: Boolean;
|
|
MsgResult: TModalResult;
|
|
begin
|
|
PkgCompileTool:=Sender as TAbstractExternalTool;
|
|
Data:=PkgCompileTool.Data as TLazPkgGraphExtToolData;
|
|
aPackage:=Data.Pkg;
|
|
//debugln(['TLazPackageGraph.ExtToolBuildStopped aPackage=',aPackage.IDAsString]);
|
|
|
|
// check if main ppu file was created
|
|
SrcF:=Data.SrcPPUFilename;
|
|
SrcPPUFileExists:=(SrcF<>'') and FileExistsUTF8(SrcF);
|
|
// write state file
|
|
MsgResult:=SavePackageCompiledState(APackage,
|
|
Data.CompilerFilename,Data.CompilerParams,
|
|
PkgCompileTool.ErrorMessage='',SrcPPUFileExists,false);
|
|
if MsgResult<>mrOk then begin
|
|
Data.ErrorMessage:='SavePackageCompiledState failed';
|
|
PkgCompileTool.ErrorMessage:=Data.ErrorMessage;
|
|
DebugLn(['Error: (lazarus) failed to write package .compiled file: ',APackage.IDAsString,' File=',aPackage.GetStateFilename]);
|
|
exit;
|
|
end;
|
|
Data.ErrorMessage:=PkgCompileTool.ErrorMessage;
|
|
|
|
if Data.ErrorMessage<>'' then exit;
|
|
|
|
// update .po files
|
|
if (APackage.POOutputDirectory<>'') then begin
|
|
MsgResult:=ConvertPackageRSTFiles(APackage);
|
|
if MsgResult<>mrOk then begin
|
|
Data.ErrorMessage:='ConvertPackageRSTFiles failed';
|
|
PkgCompileTool.ErrorMessage:=Data.ErrorMessage;
|
|
DebugLn('Error: (lazarus) failed to update .po files: ',APackage.IDAsString);
|
|
SrcF:=Format(lisUpdatingPoFilesFailedForPackage, [APackage.IDAsString]);
|
|
FOnShowMessage(mluError, SrcF, '', 0, 0, '');
|
|
exit;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
function TLazPackageGraph.CreateDefaultPackage: TLazPackage;
|
|
begin
|
|
Result:=TLazPackage.CreateAndClear;
|
|
with Result do begin
|
|
Missing:=true;
|
|
UserReadOnly:=true;
|
|
Name:='DefaultPackage';
|
|
Filename:=GetForcedPathDelims('$(LazarusDir)/components/custom/customdummy.lpk');
|
|
Version.SetValues(1,0,1,1);
|
|
Author:='Anonymous';
|
|
AutoInstall:=pitStatic;
|
|
AutoUpdate:=pupManually;
|
|
Description:=lisPkgSysThisIsTheDefaultPackageUsedOnlyForComponents;
|
|
PackageType:=lptDesignTime;
|
|
Installed:=pitStatic;
|
|
CompilerOptions.UnitOutputDirectory:='';
|
|
Translated:=SystemLanguageID.LanguageID;
|
|
|
|
// add unit paths
|
|
UsageOptions.UnitPath:='$(PkgOutDir)';
|
|
|
|
// add requirements
|
|
AddRequiredDependency(LCLPackage.CreateDependencyWithOwner(Result));
|
|
AddRequiredDependency(SynEditPackage.CreateDependencyWithOwner(Result));
|
|
|
|
Modified:=false;
|
|
OnModifySilently:=@PkgModify;
|
|
end;
|
|
IncChangeStamp;
|
|
end;
|
|
|
|
function TLazPackageGraph.GetCount: Integer;
|
|
begin
|
|
Result:=FItems.Count;
|
|
end;
|
|
|
|
procedure TLazPackageGraph.AddPackage(APackage: TLazPackage);
|
|
|
|
procedure SetBasePackage(var BasePackage: TLazPackage);
|
|
begin
|
|
if BasePackage=APackage then exit;
|
|
if BasePackage<>nil then
|
|
RaiseGDBException('TLazPackageGraph.AddPackage Pkg='+APackage.IDAsString+' conflicts with existing base package');
|
|
BasePackage:=APackage;
|
|
end;
|
|
|
|
var
|
|
Dependency: TPkgDependency;
|
|
begin
|
|
BeginUpdate(true);
|
|
FTree.Add(APackage);
|
|
FItems.Add(APackage);
|
|
|
|
if IsCompiledInBasePackage(APackage.Name) then begin
|
|
APackage.Installed:=pitStatic;
|
|
APackage.AutoInstall:=pitStatic;
|
|
if SysUtils.CompareText(APackage.Name,'FCL')=0 then begin
|
|
SetBasePackage(FFCLPackage);
|
|
APackage.SetAllComponentPriorities(FCLCompPriority);
|
|
end
|
|
else if SysUtils.CompareText(APackage.Name,'LazUtils')=0 then
|
|
SetBasePackage(FLazUtilsPackage)
|
|
else if SysUtils.CompareText(APackage.Name,'BuildIntf')=0 then begin
|
|
SetBasePackage(FBuildIntfPackage);
|
|
APackage.SetAllComponentPriorities(IDEIntfCompPriority);
|
|
end
|
|
else if SysUtils.CompareText(APackage.Name,'LCLBase')=0 then begin
|
|
SetBasePackage(FLCLBasePackage);
|
|
APackage.SetAllComponentPriorities(LCLCompPriority);
|
|
end
|
|
else if SysUtils.CompareText(APackage.Name,'LCL')=0 then begin
|
|
SetBasePackage(FLCLPackage);
|
|
APackage.SetAllComponentPriorities(LCLCompPriority);
|
|
end
|
|
else if SysUtils.CompareText(APackage.Name,'IDEIntf')=0 then begin
|
|
SetBasePackage(FIDEIntfPackage);
|
|
APackage.SetAllComponentPriorities(IDEIntfCompPriority);
|
|
end
|
|
else if SysUtils.CompareText(APackage.Name,'LazDebuggerIntf')=0 then
|
|
SetBasePackage(FLazDebuggerIntfPackage)
|
|
else if SysUtils.CompareText(APackage.Name,'DebuggerIntf')=0 then
|
|
SetBasePackage(FDebuggerIntfPackage)
|
|
else if SysUtils.CompareText(APackage.Name,'LazDebuggerGdbmi')=0 then
|
|
SetBasePackage(FLazDebuggerGdbmiPackage)
|
|
else if SysUtils.CompareText(APackage.Name,'IdeDebugger')=0 then
|
|
SetBasePackage(FIdeDebuggerPackage)
|
|
else if SysUtils.CompareText(APackage.Name,'IdeUtilsPkg')=0 then
|
|
SetBasePackage(FIdeUtilsPkgPackage)
|
|
else if SysUtils.CompareText(APackage.Name,'IdeConfig')=0 then
|
|
SetBasePackage(FIdeConfigPackage)
|
|
else if SysUtils.CompareText(APackage.Name,'IdePackagerConfig')=0 then
|
|
SetBasePackage(FIdePackagerPackage)
|
|
else if SysUtils.CompareText(APackage.Name,'IdeProject')=0 then
|
|
SetBasePackage(FIdeProjectPackage)
|
|
else if SysUtils.CompareText(APackage.Name,'SynEdit')=0 then
|
|
SetBasePackage(FSynEditPackage)
|
|
else if SysUtils.CompareText(APackage.Name,'LazControls')=0 then
|
|
SetBasePackage(FLazControlsPackage)
|
|
else if SysUtils.CompareText(APackage.Name,'CodeTools')=0 then
|
|
SetBasePackage(FCodeToolsPackage);
|
|
if FLazarusBasePackages.IndexOf(APackage)<0 then
|
|
FLazarusBasePackages.Add(APackage);
|
|
end;
|
|
|
|
// open all required dependencies
|
|
Dependency:=APackage.FirstRequiredDependency;
|
|
while Dependency<>nil do begin
|
|
OpenDependency(Dependency,false);
|
|
Dependency:=Dependency.NextRequiresDependency;
|
|
end;
|
|
|
|
// update all missing dependencies
|
|
UpdateBrokenDependenciesToPackage(APackage);
|
|
|
|
// activate define templates
|
|
if Assigned(APackage.DefineTemplates) then
|
|
APackage.DefineTemplates.Active:=true
|
|
else // By Juha:
|
|
// Happened when an old package with the same name was replaced. Cannot reproduce.
|
|
DebugLn(['TLazPackageGraph.AddPackage: APackage.DefineTemplates=Nil']);
|
|
if Assigned(OnAddPackage) then
|
|
OnAddPackage(APackage);
|
|
EndUpdate;
|
|
end;
|
|
|
|
procedure TLazPackageGraph.ReplacePackage(var OldPackage: TLazPackage;
|
|
NewPackage: TLazPackage);
|
|
|
|
procedure MoveInstalledComponents(OldPkgFile: TPkgFile);
|
|
var
|
|
NewPkgFile: TPkgFile;
|
|
OldUnitName: String;
|
|
PkgComponent: TPkgComponent;
|
|
begin
|
|
if (OldPkgFile.ComponentCount>0) then begin
|
|
OldUnitName:=OldPkgFile.Unit_Name;
|
|
if OldUnitName='' then RaiseGDBException('MoveInstalledComponents');
|
|
NewPkgFile:=NewPackage.FindUnit(OldUnitName,false);
|
|
if NewPkgFile=nil then begin
|
|
NewPkgFile:=NewPackage.AddRemovedFile(OldPkgFile.Filename,OldUnitName,
|
|
OldPkgFile.FileType,OldPkgFile.Flags,
|
|
OldPkgFile.ComponentPriority.Category);
|
|
end;
|
|
while OldPkgFile.ComponentCount>0 do begin
|
|
PkgComponent:=OldPkgFile.Components[0];
|
|
PkgComponent.PkgFile:=NewPkgFile;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
var
|
|
OldInstalled: TPackageInstallType;
|
|
OldAutoInstall: TPackageInstallType;
|
|
OldEditor: TBasePackageEditor;
|
|
i: Integer;
|
|
begin
|
|
if pvPkgSearch in Verbosity then
|
|
debugln(['Info: (lazarus) replacing package "'+OldPackage.Filename+'" with "'+NewPackage.Filename+'"']);
|
|
BeginUpdate(true);
|
|
// save flags
|
|
OldInstalled:=OldPackage.Installed;
|
|
OldAutoInstall:=OldPackage.AutoInstall;
|
|
OldEditor:=OldPackage.Editor;
|
|
if OldEditor<>nil then
|
|
OldEditor.LazPackage:=nil;
|
|
// migrate components
|
|
for i:=0 to OldPackage.FileCount-1 do
|
|
MoveInstalledComponents(OldPackage.Files[i]);
|
|
for i:=0 to OldPackage.RemovedFilesCount-1 do
|
|
MoveInstalledComponents(OldPackage.RemovedFiles[i]);
|
|
// delete old package
|
|
Delete(fItems.IndexOf(OldPackage));
|
|
OldPackage:=nil;
|
|
// restore flags
|
|
NewPackage.Installed:=OldInstalled;
|
|
NewPackage.AutoInstall:=OldAutoInstall;
|
|
// add package to graph
|
|
AddPackage(NewPackage);
|
|
if OldEditor<>nil then
|
|
OldEditor.LazPackage:=NewPackage;
|
|
EndUpdate;
|
|
end;
|
|
|
|
procedure TLazPackageGraph.LoadStaticBasePackages;
|
|
|
|
procedure LoadLazarusBasePackage(PkgName: string);
|
|
var
|
|
Dependency: TPkgDependency;
|
|
Quiet: Boolean;
|
|
begin
|
|
if FindDependencyByNameInList(FirstInstallDependency,pddRequires,PkgName)<>nil
|
|
then
|
|
exit;
|
|
Dependency:=TPkgDependency.Create;
|
|
Dependency.Owner:=Self;
|
|
Dependency.PackageName:=PkgName;
|
|
Dependency.DependencyType:=pdtLazarus;
|
|
Dependency.AddToList(FirstInstallDependency,pddRequires);
|
|
Quiet:=false;
|
|
OpenInstalledDependency(Dependency,pitStatic,Quiet);
|
|
end;
|
|
|
|
var
|
|
bp: TLazarusIDEBasePkg;
|
|
begin
|
|
for bp in TLazarusIDEBasePkg do
|
|
LoadLazarusBasePackage(LazarusIDEBasePkgNames[bp]);
|
|
|
|
SortAutoInstallDependencies;
|
|
end;
|
|
|
|
procedure TLazPackageGraph.LoadAutoInstallPackages(PkgList: TStringList);
|
|
var
|
|
i: Integer;
|
|
PackageName: string;
|
|
Dependency: TPkgDependency;
|
|
begin
|
|
for i:=0 to PkgList.Count-1 do begin
|
|
PackageName:=PkgList[i];
|
|
if not IsValidPkgName(PackageName) then continue;
|
|
Dependency:=FindDependencyByNameInList(FirstInstallDependency,
|
|
pddRequires,PackageName);
|
|
//DebugLn('TLazPackageGraph.LoadAutoInstallPackages ',dbgs(Dependency),' ',PackageName);
|
|
if Dependency<>nil then continue;
|
|
Dependency:=TPkgDependency.Create;
|
|
Dependency.Owner:=Self;
|
|
Dependency.DependencyType:=pdtLazarus;
|
|
Dependency.PackageName:=PackageName;
|
|
Dependency.AddToList(FirstInstallDependency,pddRequires);
|
|
if OpenDependency(Dependency,false)<>lprSuccess then begin
|
|
LazMessageWorker(lisPkgMangUnableToLoadPackage,
|
|
Format(lisPkgMangUnableToOpenThePackage, [PackageName, LineEnding]),
|
|
mtWarning,[mbOk]);
|
|
continue;
|
|
end;
|
|
if not Dependency.RequiredPackage.Missing then
|
|
Dependency.RequiredPackage.AutoInstall:=pitStatic;
|
|
end;
|
|
SortAutoInstallDependencies;
|
|
end;
|
|
|
|
procedure TLazPackageGraph.SortAutoInstallDependencies;
|
|
begin
|
|
// sort install dependencies, so that lower packages come first
|
|
SortDependencyListTopologicallyOld(PackageGraph.FirstInstallDependency,
|
|
false);
|
|
end;
|
|
|
|
function TLazPackageGraph.GetIDEInstallPackageOptions(
|
|
var InheritedOptionStrings: TInheritedCompOptsStrings): string;
|
|
|
|
procedure AddOption(const s: string);
|
|
begin
|
|
if s='' then exit;
|
|
if Result='' then
|
|
Result:=s
|
|
else
|
|
Result:=Result+' '+s;
|
|
end;
|
|
|
|
var
|
|
PkgList: TFPList;
|
|
AddOptionsList: TFPList;
|
|
ConfigDir: String;
|
|
begin
|
|
Result:='';
|
|
// get all required packages
|
|
PkgList:=nil;
|
|
GetAllRequiredPackages(nil,FirstInstallDependency,PkgList,[pirCompileOrder]);
|
|
if PkgList=nil then exit;
|
|
// get all usage options
|
|
AddOptionsList:=GetUsageOptionsList(PkgList);
|
|
PkgList.Free;
|
|
if AddOptionsList<>nil then begin
|
|
// combine options of same type
|
|
GatherInheritedOptions(AddOptionsList,coptParsed,InheritedOptionStrings);
|
|
AddOptionsList.Free;
|
|
end;
|
|
|
|
// convert options to compiler parameters
|
|
Result:=InheritedOptionsToCompilerParameters(InheritedOptionStrings,[]);
|
|
|
|
// add activate-static-packages option
|
|
AddOption('-dAddStaticPkgs');
|
|
|
|
// add include path to config directory
|
|
ConfigDir:=AppendPathDelim(GetPrimaryConfigPath);
|
|
AddOption(PrepareCmdLineOption('-Fi'+UTF8ToSys(ConfigDir)));
|
|
end;
|
|
|
|
function TLazPackageGraph.SaveAutoInstallConfig: TModalResult;
|
|
var
|
|
ConfigDir: String;
|
|
StaticPackagesInc: String;
|
|
StaticPckIncludeFile: String;
|
|
PkgList: TFPList;
|
|
APackage: TLazPackage;
|
|
i: Integer;
|
|
begin
|
|
ConfigDir:=AppendPathDelim(GetPrimaryConfigPath);
|
|
|
|
// create auto install package list for the Lazarus uses section
|
|
PkgList:=nil;
|
|
try
|
|
GetAllRequiredPackages(nil,FirstInstallDependency,PkgList,[pirCompileOrder]);
|
|
StaticPackagesInc:='// In case of duplicate identifier errors, see lazarus.pp'+LineEnding;
|
|
if PkgList<>nil then begin
|
|
for i:=0 to PkgList.Count-1 do begin
|
|
APackage:=TLazPackage(PkgList[i]);
|
|
if (APackage=nil)
|
|
or APackage.Missing
|
|
or IsCompiledInBasePackage(APackage.Name)
|
|
or (APackage.PackageType in [lptRunTime,lptRunTimeOnly])
|
|
then continue;
|
|
|
|
if FSrcBasePackages.IndexOf(APackage.Name)>0 then
|
|
begin
|
|
debugln(['Note: (lazarus) TLazPackageGraph.SaveAutoInstallConfig: omitting base package "',APackage.Name,'"']);
|
|
continue;
|
|
end;
|
|
|
|
StaticPackagesInc:=StaticPackagesInc
|
|
+ExtractFileNameOnly(APackage.GetCompileSourceFilename)
|
|
+','+LineEnding;
|
|
end;
|
|
end;
|
|
finally
|
|
PkgList.Free;
|
|
end;
|
|
StaticPckIncludeFile:=ConfigDir+'staticpackages.inc';
|
|
Result:=SaveLazStringToFile(StaticPckIncludeFile,StaticPackagesInc,[],
|
|
lisPkgMangstaticPackagesConfigFile);
|
|
end;
|
|
|
|
function TLazPackageGraph.IsCompiledInBasePackage(PackageName: string): boolean;
|
|
var
|
|
bp: TLazarusIDEBasePkg;
|
|
begin
|
|
for bp in TLazarusIDEBasePkg do
|
|
if SameText(PackageName,LazarusIDEBasePkgNames[bp]) then
|
|
exit(true);
|
|
Result:=false;
|
|
end;
|
|
|
|
procedure TLazPackageGraph.FreeAutoInstallDependencies;
|
|
var
|
|
Dependency: TPkgDependency;
|
|
begin
|
|
while Assigned(FirstInstallDependency) do
|
|
begin
|
|
Dependency:=FirstInstallDependency;
|
|
Dependency.RequiredPackage:=nil;
|
|
Dependency.RemoveFromList(FirstInstallDependency,pddRequires);
|
|
Dependency.Free;
|
|
end;
|
|
end;
|
|
|
|
procedure TLazPackageGraph.WarnSuspiciousCompilerOptions(ViewCaption, Target: string;
|
|
CompilerParams: TStrings);
|
|
var
|
|
ParsedParams: TObjectList;
|
|
i: Integer;
|
|
Param: TFPCParamValue;
|
|
Msg: String;
|
|
begin
|
|
if FOnShowMessage=nil then exit;
|
|
ParsedParams:=TObjectList.Create(true);
|
|
try
|
|
ParseFPCParameters(CompilerParams,ParsedParams);
|
|
for i:=0 to ParsedParams.Count-1 do begin
|
|
Param:=TFPCParamValue(ParsedParams[i]);
|
|
if fpfValueChanged in Param.Flags then begin
|
|
Msg:='';
|
|
if Param.Kind in [fpkBoolean,fpkValue] then begin
|
|
if Param.Name<>'M' then // Many -M options are allowed.
|
|
Msg:=Format(lisPassingCompilerOptionTwiceWithDifferentValues, [Param.Name]);
|
|
end
|
|
else if Param.Kind=fpkDefine then
|
|
Msg:=Format(lisPassingCompilerDefineTwiceWithDifferentValues, [Param.Name]);
|
|
if Msg='' then continue;
|
|
if Target<>'' then Msg:=Target+' '+Msg;
|
|
FOnShowMessage(mluNote, Msg, '', 0, 0, ViewCaption);
|
|
end;
|
|
end;
|
|
finally
|
|
ParsedParams.Free;
|
|
end;
|
|
end;
|
|
|
|
procedure TLazPackageGraph.ClosePackage(APackage: TLazPackage);
|
|
begin
|
|
if (lpfDestroying in APackage.Flags) or PackageIsNeeded(APackage) then exit;
|
|
CloseUnneededPackages;
|
|
end;
|
|
|
|
procedure TLazPackageGraph.MarkNeededPackages;
|
|
var
|
|
StackPtr, i: Integer;
|
|
Pkg, RequiredPackage: TLazPackage;
|
|
Dependency: TPkgDependency;
|
|
PkgStack: PLazPackage;
|
|
begin
|
|
if Count=0 then exit;
|
|
// create stack
|
|
GetMem(PkgStack,SizeOf(Pointer)*Count);
|
|
StackPtr:=0;
|
|
// put all needed packages on stack and set lpfNeeded
|
|
for i:=0 to FItems.Count-1 do begin
|
|
Pkg:=TLazPackage(FItems[i]);
|
|
if PackageIsNeeded(Pkg) then begin
|
|
Pkg.Flags:=Pkg.Flags+[lpfNeeded];
|
|
PkgStack[StackPtr]:=Pkg;
|
|
inc(StackPtr);
|
|
end else
|
|
Pkg.Flags:=Pkg.Flags-[lpfNeeded];
|
|
end;
|
|
// mark all needed packages
|
|
while StackPtr>0 do begin
|
|
// get needed package from stack
|
|
dec(StackPtr);
|
|
Pkg:=PkgStack[StackPtr];
|
|
// put all required packages on stack
|
|
Dependency:=Pkg.FirstRequiredDependency;
|
|
while Dependency<>nil do begin
|
|
if (Dependency.DependencyType=pdtLazarus) and (Dependency.LoadPackageResult=lprSuccess) then begin
|
|
RequiredPackage:=Dependency.RequiredPackage;
|
|
if (not (lpfNeeded in RequiredPackage.Flags)) then begin
|
|
RequiredPackage.Flags:=RequiredPackage.Flags+[lpfNeeded];
|
|
PkgStack[StackPtr]:=RequiredPackage;
|
|
inc(StackPtr);
|
|
end;
|
|
end;
|
|
Dependency:=Dependency.NextRequiresDependency;
|
|
end;
|
|
end;
|
|
// clean up
|
|
FreeMem(PkgStack);
|
|
end;
|
|
|
|
function TLazPackageGraph.FindBrokenDependencyPath(APackage: TLazPackage;
|
|
FirstDependency: TPkgDependency): TFPList;
|
|
// returns the first broken dependency (broken = not loaded)
|
|
// the first items are TLazPackage, the last item is a TPkgDependency
|
|
|
|
procedure FindBroken(Dependency: TPkgDependency; var PathList: TFPList);
|
|
var
|
|
RequiredPackage: TLazPackage;
|
|
begin
|
|
while Dependency<>nil do begin
|
|
if Dependency.DependencyType=pdtFPMake then begin
|
|
// FPMake dependency have no RequiredPackage -> ignore
|
|
end else if Dependency.LoadPackageResult=lprSuccess then begin
|
|
// dependency ok
|
|
if Dependency.DependencyType=pdtLazarus then begin
|
|
RequiredPackage:=Dependency.RequiredPackage;
|
|
if not (lpfVisited in RequiredPackage.Flags) then begin
|
|
RequiredPackage.Flags:=RequiredPackage.Flags+[lpfVisited];
|
|
FindBroken(RequiredPackage.FirstRequiredDependency,PathList);
|
|
if PathList<>nil then begin
|
|
// broken dependency found -> add current package to list
|
|
PathList.Insert(0,RequiredPackage);
|
|
exit;
|
|
end;
|
|
end;
|
|
end;
|
|
end else begin
|
|
// broken dependency found
|
|
PathList:=TFPList.Create;
|
|
PathList.Add(Dependency);
|
|
exit;
|
|
end;
|
|
Dependency:=Dependency.NextRequiresDependency;
|
|
end;
|
|
end;
|
|
|
|
begin
|
|
Result:=nil;
|
|
if (Count=0) then exit;
|
|
MarkAllPackagesAsNotVisited;
|
|
if APackage<>nil then begin
|
|
APackage.Flags:=APackage.Flags+[lpfVisited];
|
|
FirstDependency:=APackage.FirstRequiredDependency;
|
|
end;
|
|
FindBroken(FirstDependency,Result);
|
|
if (Result<>nil) and (APackage<>nil) then
|
|
Result.Insert(0,APackage);
|
|
end;
|
|
|
|
function TLazPackageGraph.FindAllBrokenDependencies(APackage: TLazPackage;
|
|
FirstDependency: TPkgDependency): TFPList;
|
|
// returns the list of broken dependencies (TPkgDependency)
|
|
|
|
procedure FindBroken(Dependency: TPkgDependency; var DepList: TFPList);
|
|
var
|
|
RequiredPackage: TLazPackage;
|
|
begin
|
|
while Dependency<>nil do begin
|
|
if Dependency.DependencyType=pdtFPMake then begin
|
|
// FPMake dependency have no RequiredPackage -> ignore
|
|
end else if Dependency.LoadPackageResult=lprSuccess then begin
|
|
// dependency ok
|
|
RequiredPackage:=Dependency.RequiredPackage;
|
|
if not (lpfVisited in RequiredPackage.Flags) then begin
|
|
RequiredPackage.Flags:=RequiredPackage.Flags+[lpfVisited];
|
|
FindBroken(RequiredPackage.FirstRequiredDependency,DepList);
|
|
end;
|
|
end else begin
|
|
// broken dependency found
|
|
if (DepList=nil) or (DepList.IndexOf(Dependency)<0) then begin
|
|
if DepList=nil then
|
|
DepList:=TFPList.Create;
|
|
DepList.Add(Dependency);
|
|
end;
|
|
end;
|
|
Dependency:=Dependency.NextRequiresDependency;
|
|
end;
|
|
end;
|
|
|
|
begin
|
|
Result:=nil;
|
|
if (Count=0) then exit;
|
|
MarkAllPackagesAsNotVisited;
|
|
if APackage<>nil then begin
|
|
APackage.Flags:=APackage.Flags+[lpfVisited];
|
|
FirstDependency:=APackage.FirstRequiredDependency;
|
|
end;
|
|
FindBroken(FirstDependency,Result);
|
|
end;
|
|
|
|
function TLazPackageGraph.FindCycleDependencyPath(APackage: TLazPackage;
|
|
FirstDependency: TPkgDependency): TFPList;
|
|
|
|
procedure FindCycle(Dependency: TPkgDependency; var PathList: TFPList);
|
|
var
|
|
RequiredPackage: TLazPackage;
|
|
begin
|
|
while Dependency<>nil do begin
|
|
if Dependency.LoadPackageResult=lprSuccess then begin
|
|
// dependency ok
|
|
RequiredPackage:=Dependency.RequiredPackage;
|
|
if Dependency.DependencyType=pdtLazarus then begin
|
|
if lpfCycle in RequiredPackage.Flags then begin
|
|
// cycle detected
|
|
PathList:=TFPList.Create;
|
|
PathList.Add(RequiredPackage);
|
|
exit;
|
|
end;
|
|
if not (lpfVisited in RequiredPackage.Flags) then begin
|
|
RequiredPackage.Flags:=RequiredPackage.Flags+[lpfVisited,lpfCycle];
|
|
FindCycle(RequiredPackage.FirstRequiredDependency,PathList);
|
|
if PathList<>nil then begin
|
|
// cycle detected
|
|
// -> add current package to list
|
|
PathList.Insert(0,RequiredPackage);
|
|
exit;
|
|
end;
|
|
RequiredPackage.Flags:=RequiredPackage.Flags-[lpfCycle];
|
|
end;
|
|
end;
|
|
end;
|
|
Dependency:=Dependency.NextRequiresDependency;
|
|
end;
|
|
end;
|
|
|
|
var
|
|
i: Integer;
|
|
Pkg: TLazPackage;
|
|
begin
|
|
Result:=nil;
|
|
if (Count=0) then exit;
|
|
// mark all packages as not visited and cycle free
|
|
for i:=FItems.Count-1 downto 0 do begin
|
|
Pkg:=TLazPackage(FItems[i]);
|
|
Pkg.Flags:=Pkg.Flags-[lpfVisited,lpfCycle];
|
|
end;
|
|
if APackage<>nil then begin
|
|
APackage.Flags:=APackage.Flags+[lpfVisited];
|
|
FirstDependency:=APackage.FirstRequiredDependency;
|
|
end;
|
|
FindCycle(FirstDependency,Result);
|
|
if (Result<>nil) and (APackage<>nil) then
|
|
Result.Insert(0,APackage);
|
|
end;
|
|
|
|
function TLazPackageGraph.FindPath(StartPackage: TLazPackage;
|
|
StartDependency: TPkgDependency; const EndPackageName: string): TFPList;
|
|
|
|
procedure Find(Dependency: TPkgDependency; var PathList: TFPList);
|
|
var
|
|
RequiredPackage: TLazPackage;
|
|
begin
|
|
while Dependency<>nil do begin
|
|
if SysUtils.CompareText(Dependency.PackageName,EndPackageName)=0 then begin
|
|
// path found
|
|
PathList:=TFPList.Create;
|
|
if (Dependency.DependencyType=pdtLazarus) and (Dependency.LoadPackageResult=lprSuccess) then begin
|
|
PathList.Add(Dependency.RequiredPackage);
|
|
end else begin
|
|
PathList.Add(Dependency);
|
|
end;
|
|
exit;
|
|
end;
|
|
if (Dependency.DependencyType=pdtLazarus) and (Dependency.LoadPackageResult=lprSuccess) then begin
|
|
// dependency ok
|
|
RequiredPackage:=Dependency.RequiredPackage;
|
|
if not (lpfVisited in RequiredPackage.Flags) then begin
|
|
RequiredPackage.Flags:=RequiredPackage.Flags+[lpfVisited];
|
|
Find(RequiredPackage.FirstRequiredDependency,PathList);
|
|
if PathList<>nil then begin
|
|
// path found
|
|
// -> add current package to list
|
|
PathList.Insert(0,RequiredPackage);
|
|
exit;
|
|
end;
|
|
end;
|
|
end;
|
|
Dependency:=Dependency.NextRequiresDependency;
|
|
end;
|
|
end;
|
|
|
|
begin
|
|
Result:=nil;
|
|
if (Count=0) then exit;
|
|
MarkAllPackagesAsNotVisited;
|
|
if StartPackage<>nil then begin
|
|
StartPackage.Flags:=StartPackage.Flags+[lpfVisited];
|
|
StartDependency:=StartPackage.FirstRequiredDependency;
|
|
end;
|
|
Find(StartDependency,Result);
|
|
if (Result<>nil) and (StartPackage<>nil) then
|
|
Result.Insert(0,StartPackage);
|
|
end;
|
|
|
|
function TLazPackageGraph.FindPkgOutputInFPCSearchPath(APackage: TLazPackage;
|
|
FirstDependency: TPkgDependency): TFPList;
|
|
var
|
|
CfgCache: TPCTargetConfigCache;
|
|
|
|
function CheckPkg(Pkg: TLazPackage; var PathList: TFPList): boolean;
|
|
var
|
|
OutputDir: String;
|
|
i: Integer;
|
|
Dir: String;
|
|
begin
|
|
Result:=true;
|
|
if (Pkg=nil) then exit;
|
|
Pkg.Flags:=Pkg.Flags+[lpfVisited];
|
|
if (Pkg.FirstRequiredDependency=nil)
|
|
or (Pkg.GetActiveBuildMethod=bmFPMake) // Packages compiled by FPMake almost always change units in the default fpc-search path. Checking of changed dependencies should be done using the mechanisms of fppkg.
|
|
or Pkg.IsVirtual or (Pkg.AutoUpdate<>pupAsNeeded) then exit;
|
|
// this package is compiled automatically and has dependencies
|
|
OutputDir:=ChompPathDelim(Pkg.GetOutputDirectory);
|
|
if OutputDir='' then exit;
|
|
for i:=0 to CfgCache.UnitPaths.Count-1 do begin
|
|
Dir:=ChompPathDelim(CfgCache.UnitPaths[i]);
|
|
if CompareFilenames(Dir,OutputDir)=0 then begin
|
|
// this package changes the units in the default FPC search path
|
|
// => a cycle, because the dependencies use FPC search path too
|
|
Result:=false;
|
|
PathList:=TFPList.Create;
|
|
PathList.Add(Pkg);
|
|
exit;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure CheckDependencyList(Dependency: TPkgDependency; var PathList: TFPList);
|
|
var
|
|
RequiredPackage: TLazPackage;
|
|
begin
|
|
while Dependency<>nil do begin
|
|
if (Dependency.DependencyType=pdtLazarus) and (Dependency.LoadPackageResult=lprSuccess) then begin
|
|
RequiredPackage:=Dependency.RequiredPackage;
|
|
if not (lpfVisited in RequiredPackage.Flags) then begin
|
|
if CheckPkg(RequiredPackage,PathList) then exit;
|
|
CheckDependencyList(RequiredPackage.FirstRequiredDependency,PathList);
|
|
if PathList<>nil then begin
|
|
// cycle detected
|
|
// -> add current package to list
|
|
PathList.Insert(0,RequiredPackage);
|
|
exit;
|
|
end;
|
|
end;
|
|
end;
|
|
Dependency:=Dependency.NextRequiresDependency;
|
|
end;
|
|
end;
|
|
|
|
var
|
|
UnitSet: TFPCUnitSetCache;
|
|
begin
|
|
Result:=nil;
|
|
MarkAllPackagesAsNotVisited;
|
|
UnitSet:=CodeToolBoss.GetUnitSetForDirectory('');
|
|
if UnitSet=nil then exit;
|
|
CfgCache:=UnitSet.GetConfigCache(false);
|
|
if (CfgCache=nil) or (CfgCache.UnitPaths=nil) then exit;
|
|
if APackage<>nil then begin
|
|
if not CheckPkg(APackage,Result) then exit;
|
|
if FirstDependency=nil then
|
|
FirstDependency:=APackage.FirstRequiredDependency;
|
|
end;
|
|
CheckDependencyList(FirstDependency,Result);
|
|
end;
|
|
|
|
function TLazPackageGraph.FindUnsavedDependencyPath(APackage: TLazPackage;
|
|
FirstDependency: TPkgDependency): TFPList;
|
|
|
|
procedure FindUnsaved(Dependency: TPkgDependency; var PathList: TFPList);
|
|
var
|
|
RequiredPackage: TLazPackage;
|
|
begin
|
|
while Dependency<>nil do begin
|
|
if (Dependency.DependencyType=pdtLazarus) and (Dependency.LoadPackageResult=lprSuccess) then begin
|
|
// dependency ok
|
|
RequiredPackage:=Dependency.RequiredPackage;
|
|
if RequiredPackage.Modified then begin
|
|
// unsaved package detected
|
|
PathList:=TFPList.Create;
|
|
PathList.Add(RequiredPackage);
|
|
exit;
|
|
end;
|
|
if not (lpfVisited in RequiredPackage.Flags) then begin
|
|
RequiredPackage.Flags:=RequiredPackage.Flags+[lpfVisited];
|
|
FindUnsaved(RequiredPackage.FirstRequiredDependency,PathList);
|
|
if PathList<>nil then begin
|
|
// unsaved package detected
|
|
// -> add current package to list
|
|
PathList.Insert(0,RequiredPackage);
|
|
exit;
|
|
end;
|
|
end;
|
|
end;
|
|
Dependency:=Dependency.NextRequiresDependency;
|
|
end;
|
|
end;
|
|
|
|
begin
|
|
Result:=nil;
|
|
if (Count=0) or (APackage=nil) then exit;
|
|
MarkAllPackagesAsNotVisited;
|
|
if APackage<>nil then begin
|
|
APackage.Flags:=APackage.Flags+[lpfVisited];
|
|
FirstDependency:=APackage.FirstRequiredDependency;
|
|
end;
|
|
FindUnsaved(FirstDependency,Result);
|
|
if (Result<>nil) and (APackage<>nil) then
|
|
Result.Insert(0,APackage);
|
|
end;
|
|
|
|
function TLazPackageGraph.FindNotInstalledRegisterUnits(
|
|
APackage: TLazPackage; FirstDependency: TPkgDependency): TFPList;
|
|
// returns the list of required units (TPkgFile) with a Register procedure,
|
|
// that are not installed in the IDE
|
|
|
|
procedure FindNotInstalledRegisterUnit(Dependency: TPkgDependency;
|
|
var UnitList: TFPList);
|
|
var
|
|
RequiredPackage: TLazPackage;
|
|
i: Integer;
|
|
APkgFile: TPkgFile;
|
|
begin
|
|
while Dependency<>nil do begin
|
|
if (Dependency.DependencyType=pdtLazarus)
|
|
and (Dependency.LoadPackageResult=lprSuccess) then begin
|
|
// dependency ok
|
|
RequiredPackage:=Dependency.RequiredPackage;
|
|
if not (lpfVisited in RequiredPackage.Flags) then begin
|
|
if (RequiredPackage.Installed=pitNope)
|
|
and (RequiredPackage.PackageType in [lptDesignTime,lptRunAndDesignTime])
|
|
then begin
|
|
// package not installed and can be installed
|
|
for i:=0 to RequiredPackage.FileCount-1 do begin
|
|
APkgFile:=RequiredPackage.Files[i];
|
|
if APkgFile.HasRegisterProc then begin
|
|
// unit with register procedure -> add
|
|
if UnitList=nil then
|
|
UnitList:=TFPList.Create;
|
|
UnitList.Add(APkgFile);
|
|
end;
|
|
end;
|
|
end;
|
|
RequiredPackage.Flags:=RequiredPackage.Flags+[lpfVisited];
|
|
FindNotInstalledRegisterUnit(RequiredPackage.FirstRequiredDependency,UnitList);
|
|
end;
|
|
end;
|
|
Dependency:=Dependency.NextRequiresDependency;
|
|
end;
|
|
end;
|
|
|
|
begin
|
|
Result:=nil;
|
|
if (Count=0) then exit;
|
|
MarkAllPackagesAsNotVisited;
|
|
if APackage<>nil then begin
|
|
APackage.Flags:=APackage.Flags+[lpfVisited];
|
|
FirstDependency:=APackage.FirstRequiredDependency;
|
|
end;
|
|
FindNotInstalledRegisterUnit(FirstDependency,Result);
|
|
end;
|
|
|
|
function TLazPackageGraph.FindAutoInstallDependencyPath(
|
|
ChildPackage: TLazPackage): TFPList;
|
|
|
|
procedure FindAutoInstallParent(APackage: TLazPackage);
|
|
var
|
|
ParentPackage: TLazPackage;
|
|
Dependency: TPkgDependency;
|
|
begin
|
|
Dependency:=APackage.FirstUsedByDependency;
|
|
while Dependency<>nil do begin
|
|
if Dependency.Owner is TLazPackage then begin
|
|
ParentPackage:=TLazPackage(Dependency.Owner);
|
|
if not (lpfVisited in ParentPackage.Flags) then begin
|
|
ParentPackage.Flags:=ParentPackage.Flags+[lpfVisited];
|
|
if ParentPackage.AutoInstall<>pitNope then begin
|
|
// auto install parent found
|
|
if Result=nil then Result:=TFPList.Create;
|
|
Result.Add(ParentPackage);
|
|
Result.Add(APackage);
|
|
exit;
|
|
end;
|
|
FindAutoInstallParent(ParentPackage);
|
|
if Result<>nil then begin
|
|
// build path
|
|
Result.Add(APackage);
|
|
exit;
|
|
end;
|
|
end;
|
|
end;
|
|
Dependency:=Dependency.NextRequiresDependency;
|
|
end;
|
|
end;
|
|
|
|
begin
|
|
Result:=nil;
|
|
MarkAllPackagesAsNotVisited;
|
|
ChildPackage.Flags:=ChildPackage.Flags+[lpfVisited];
|
|
FindAutoInstallParent(ChildPackage);
|
|
end;
|
|
|
|
function TLazPackageGraph.FindAmbiguousUnits(APackage: TLazPackage;
|
|
FirstDependency: TPkgDependency; out File1, File2: TPkgFile; out
|
|
ConflictPkg: TLazPackage): boolean;
|
|
// check if two connected packages have units with the same name
|
|
// Connected means here: a Package1 is directly required by a Package2
|
|
// or: a Package1 and a Package2 are directly required by a Package3
|
|
// returns true, if ambiguous units found
|
|
// There can either be a conflict between two files (File1,File2)
|
|
// or between a file and a package (File1,ConflictPkg)
|
|
var
|
|
PackageTreeOfUnitTrees: TAVLTree; // tree of TPkgUnitsTree
|
|
|
|
function GetUnitsTreeOfPackage(Pkg: TLazPackage): TPkgUnitsTree;
|
|
var
|
|
ANode: TAVLTreeNode;
|
|
PkgFile: TPkgFile;
|
|
i: Integer;
|
|
begin
|
|
// for first time: create PackageTreeOfUnitTrees
|
|
if PackageTreeOfUnitTrees=nil then
|
|
PackageTreeOfUnitTrees:=TAVLTree.Create(TListSortCompare(@CompareUnitsTree));
|
|
// search UnitsTree for package
|
|
ANode:=PackageTreeOfUnitTrees.FindKey(Pkg, TListSortCompare(@ComparePackageWithUnitsTree));
|
|
if ANode<>nil then begin
|
|
Result:=TPkgUnitsTree(ANode.Data);
|
|
exit;
|
|
end;
|
|
// first time: create tree of units for Pkg
|
|
Result:=TPkgUnitsTree.Create(Pkg);
|
|
PackageTreeOfUnitTrees.Add(Result);
|
|
for i:=0 to Pkg.FileCount-1 do begin
|
|
PkgFile:=Pkg.Files[i];
|
|
if (PkgFile.FileType in PkgFileRealUnitTypes) and (PkgFile.Unit_Name<>'') then
|
|
Result.Add(PkgFile);
|
|
end;
|
|
end;
|
|
|
|
function FindAmbiguousUnitsBetween2Packages(Pkg1,Pkg2: TLazPackage): boolean;
|
|
var
|
|
i: Integer;
|
|
PkgFile1: TPkgFile;
|
|
PkgFile2: TPkgFile;
|
|
UnitsTreeOfPkg2: TPkgUnitsTree;
|
|
begin
|
|
Result:=false;
|
|
if Pkg1=Pkg2 then exit;
|
|
if (Pkg1.FileCount=0) or (Pkg2.FileCount=0) then exit;
|
|
UnitsTreeOfPkg2:=GetUnitsTreeOfPackage(Pkg2);
|
|
// check if a unit of Pkg2 has the same name as Pkg1
|
|
PkgFile2:=UnitsTreeOfPkg2.FindPkgFileWithUnitName(Pkg1.Name);
|
|
if PkgFile2<>nil then begin
|
|
File1:=PkgFile2;
|
|
ConflictPkg:=Pkg1;
|
|
Result:=true;
|
|
exit;
|
|
end;
|
|
for i:=0 to Pkg1.FileCount-1 do begin
|
|
PkgFile1:=Pkg1.Files[i];
|
|
if (PkgFile1.FileType in PkgFileRealUnitTypes)
|
|
and (PkgFile1.Unit_Name<>'') then begin
|
|
// check if a unit of Pkg1 exists in Pkg2
|
|
PkgFile2:=UnitsTreeOfPkg2.FindPkgFileWithUnitName(PkgFile1.Unit_Name);
|
|
if PkgFile2<>nil then begin
|
|
File1:=PkgFile1;
|
|
File2:=PkgFile2;
|
|
Result:=true;
|
|
exit;
|
|
end;
|
|
// check if a unit of Pkg1 has the same name as Pkg2
|
|
if SysUtils.CompareText(PkgFile1.Unit_Name,Pkg2.Name)=0 then begin
|
|
File1:=PkgFile1;
|
|
ConflictPkg:=Pkg2;
|
|
Result:=true;
|
|
exit;
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
var
|
|
PkgList: TFPList;
|
|
ConnectionsTree: TPkgPairTree;
|
|
ANode: TAVLTreeNode;
|
|
Pair: TPkgPair;
|
|
begin
|
|
Result:=false;
|
|
if APackage<>nil then begin
|
|
FirstDependency:=APackage.FirstRequiredDependency;
|
|
end;
|
|
File1:=nil;
|
|
File2:=nil;
|
|
ConflictPkg:=nil;
|
|
ConnectionsTree:=nil;
|
|
PkgList:=nil;
|
|
PackageTreeOfUnitTrees:=nil;
|
|
GetConnectionsTree(FirstDependency,PkgList,ConnectionsTree);
|
|
try
|
|
if ConnectionsTree=nil then exit;
|
|
ANode:=ConnectionsTree.FindLowest;
|
|
while ANode<>nil do begin
|
|
Pair:=TPkgPair(ANode.Data);
|
|
Result:=FindAmbiguousUnitsBetween2Packages(Pair.Package1,Pair.Package2);
|
|
if Result then exit;
|
|
ANode:=ConnectionsTree.FindSuccessor(ANode);
|
|
end;
|
|
finally
|
|
if PackageTreeOfUnitTrees<>nil then begin
|
|
PackageTreeOfUnitTrees.FreeAndClear;
|
|
PackageTreeOfUnitTrees.Free;
|
|
end;
|
|
ConnectionsTree.Free;
|
|
PkgList.Free;
|
|
end;
|
|
Result:=false;
|
|
end;
|
|
|
|
function TLazPackageGraph.FindFPCConflictUnit(APackage: TLazPackage;
|
|
FirstDependency: TPkgDependency; const Directory: string;
|
|
OnFindFPCUnit: TFindFPCUnitEvent;
|
|
var File1: TPkgFile; var ConflictPkg: TLazPackage): boolean;
|
|
|
|
function CheckUnitName(const AnUnitName: string): boolean;
|
|
var Filename: string;
|
|
begin
|
|
Result:=false;
|
|
if AnUnitName='' then exit;
|
|
Filename:='';
|
|
OnFindFPCUnit(AnUnitName,Directory,Filename);
|
|
Result:=Filename<>'';
|
|
end;
|
|
|
|
function CheckDependencyList(ADependency: TPkgDependency): boolean; forward;
|
|
|
|
function CheckPackage(Pkg1: TLazPackage): boolean;
|
|
var
|
|
Cnt: Integer;
|
|
i: Integer;
|
|
CurFile: TPkgFile;
|
|
begin
|
|
Result:=false;
|
|
if (Pkg1=nil) or (lpfVisited in Pkg1.Flags)
|
|
or (Pkg1=FFCLPackage) or (Pkg1=FLCLBasePackage) or (Pkg1=FLCLPackage) then exit;
|
|
Pkg1.Flags:=Pkg1.Flags+[lpfVisited];
|
|
Result:=CheckUnitName(Pkg1.Name);
|
|
if Result then begin
|
|
ConflictPkg:=Pkg1;
|
|
exit;
|
|
end;
|
|
Cnt:=Pkg1.FileCount;
|
|
for i:=0 to Cnt-1 do begin
|
|
CurFile:=Pkg1.Files[i];
|
|
if (CurFile.FileType in PkgFileRealUnitTypes)
|
|
and (pffAddToPkgUsesSection in CurFile.Flags) then begin
|
|
Result:=CheckUnitName(CurFile.Unit_Name);
|
|
if Result then begin
|
|
File1:=CurFile;
|
|
exit;
|
|
end;
|
|
end;
|
|
end;
|
|
Result:=CheckDependencyList(Pkg1.FirstRequiredDependency);
|
|
end;
|
|
|
|
function CheckDependencyList(ADependency: TPkgDependency): boolean;
|
|
begin
|
|
Result:=false;
|
|
while ADependency<>nil do begin
|
|
Result:=CheckPackage(ADependency.RequiredPackage);
|
|
if Result then exit;
|
|
ADependency:=ADependency.NextDependency[pddRequires];
|
|
end;
|
|
end;
|
|
|
|
begin
|
|
Result:=false;
|
|
if (Directory<>'') and not FilenameIsAbsolute(Directory) then
|
|
RaiseGDBException(Directory);
|
|
File1:=nil;
|
|
ConflictPkg:=nil;
|
|
MarkAllPackagesAsNotVisited;
|
|
if APackage<>nil then
|
|
Result:=CheckPackage(APackage)
|
|
else
|
|
Result:=CheckDependencyList(FirstDependency);
|
|
end;
|
|
|
|
procedure TLazPackageGraph.MarkAllPackagesAsNotVisited;
|
|
var
|
|
i: Integer;
|
|
Pkg: TLazPackage;
|
|
begin
|
|
// mark all packages as not visited
|
|
for i:=FItems.Count-1 downto 0 do begin
|
|
Pkg:=TLazPackage(FItems[i]);
|
|
Pkg.Flags:=Pkg.Flags-[lpfVisited];
|
|
end;
|
|
end;
|
|
|
|
procedure TLazPackageGraph.MarkAllDependencies(
|
|
MarkPackages: boolean; AddMarkerFlags, RemoveMarkerFlags: TPkgMarkerFlags);
|
|
var
|
|
i: Integer;
|
|
Pkg: TLazPackage;
|
|
Dependency: TPkgDependency;
|
|
begin
|
|
// mark all dependencies of all packages as not visited
|
|
for i:=FItems.Count-1 downto 0 do begin
|
|
Pkg:=TLazPackage(FItems[i]);
|
|
if MarkPackages then
|
|
Pkg.Flags:=Pkg.Flags-[lpfVisited];
|
|
Dependency:=Pkg.FirstRequiredDependency;
|
|
while Dependency<>nil do begin
|
|
Dependency.MarkerFlags:=
|
|
Dependency.MarkerFlags+AddMarkerFlags-RemoveMarkerFlags;
|
|
Dependency:=Dependency.NextRequiresDependency;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TLazPackageGraph.MarkAllRequiredPackages(
|
|
FirstDependency: TPkgDependency);
|
|
var
|
|
Dependency: TPkgDependency;
|
|
RequiredPackage: TLazPackage;
|
|
begin
|
|
Dependency:=FirstDependency;
|
|
while Dependency<>nil do begin
|
|
if (Dependency.DependencyType=pdtLazarus) and (Dependency.LoadPackageResult=lprSuccess) then begin
|
|
RequiredPackage:=Dependency.RequiredPackage;
|
|
if not (lpfVisited in RequiredPackage.Flags) then begin
|
|
RequiredPackage.Flags:=RequiredPackage.Flags+[lpfVisited];
|
|
MarkAllRequiredPackages(RequiredPackage.FirstRequiredDependency);
|
|
end;
|
|
end;
|
|
Dependency:=Dependency.NextRequiresDependency;
|
|
end;
|
|
end;
|
|
|
|
procedure TLazPackageGraph.CloseUnneededPackages;
|
|
var
|
|
i: Integer;
|
|
begin
|
|
BeginUpdate(false);
|
|
MarkNeededPackages;
|
|
for i:=FItems.Count-1 downto 0 do
|
|
if not (lpfNeeded in Packages[i].Flags) then begin
|
|
if ConsoleVerbosity>=0 then
|
|
debugln(['Hint: closing unneeded package "',Packages[i].Name,'"']);
|
|
Delete(i);
|
|
end;
|
|
EndUpdate;
|
|
end;
|
|
|
|
procedure TLazPackageGraph.ChangePackageID(APackage: TLazPackage;
|
|
const NewName: string; NewVersion: TPkgVersion; RenameDependencies,
|
|
RenameMacros: boolean);
|
|
var
|
|
Dependency: TPkgDependency;
|
|
NextDependency: TPkgDependency;
|
|
OldPkgName: String;
|
|
i: Integer;
|
|
Macro: TLazBuildMacro;
|
|
OldMacroName: String;
|
|
BaseCompOpts: TBaseCompilerOptions;
|
|
begin
|
|
OldPkgName:=APackage.Name;
|
|
if (OldPkgName=NewName) and (APackage.Version.Compare(NewVersion)=0) then
|
|
exit; // fit exactly
|
|
|
|
BeginUpdate(true);
|
|
|
|
if RenameMacros then
|
|
begin
|
|
// rename macros
|
|
for i:=0 to APackage.CompilerOptions.BuildMacros.Count-1 do
|
|
begin
|
|
Macro:=APackage.CompilerOptions.BuildMacros[i];
|
|
if SysUtils.CompareText(OldPkgName,copy(Macro.Identifier,1,length(OldPkgName)))=0
|
|
then begin
|
|
OldMacroName:=Macro.Identifier;
|
|
Macro.Identifier:=NewName+copy(OldMacroName,length(OldPkgName)+1,256);
|
|
BaseCompOpts:=TBaseCompilerOptions(APackage.CompilerOptions);
|
|
BaseCompOpts.RenameMacro(OldMacroName,Macro.Identifier,true);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
// cut or fix all dependencies, that became incompatible
|
|
Dependency:=APackage.FirstUsedByDependency;
|
|
while Dependency<>nil do begin
|
|
NextDependency:=Dependency.NextUsedByDependency;
|
|
if not Dependency.IsCompatible(NewName,NewVersion) then begin
|
|
if RenameDependencies then begin
|
|
Dependency.MakeCompatible(NewName,NewVersion);
|
|
if Assigned(OnDependencyModified) then OnDependencyModified(Dependency);
|
|
end else begin
|
|
// remove dependency from the used-by list of the required package
|
|
Dependency.RequiredPackage:=nil;
|
|
end;
|
|
end;
|
|
Dependency:=NextDependency;
|
|
end;
|
|
|
|
// change ID
|
|
FTree.Remove(APackage);
|
|
APackage.ChangeID(NewName,NewVersion);
|
|
FTree.Add(APackage);
|
|
|
|
// update old broken dependencies
|
|
UpdateBrokenDependenciesToPackage(APackage);
|
|
|
|
if Assigned(OnChangePackageName) then
|
|
OnChangePackageName(APackage,OldPkgName);
|
|
|
|
// no try-finally needed, because above fails only for fatal reasons
|
|
EndUpdate;
|
|
end;
|
|
|
|
function TLazPackageGraph.SavePackageCompiledState(APackage: TLazPackage;
|
|
const CompilerFilename: string; CompilerParams: TStrings; Complete,
|
|
MainPPUExists, ShowAbort: boolean): TModalResult;
|
|
var
|
|
XMLConfig: TXMLConfig;
|
|
StateFile: String;
|
|
CompilerFileDate: Integer;
|
|
o: TPkgOutputDir;
|
|
Stats: TPkgLastCompileStats;
|
|
begin
|
|
Result:=mrCancel;
|
|
StateFile:=APackage.GetStateFilename;
|
|
try
|
|
CompilerFileDate:=FileAgeCached(CompilerFilename);
|
|
|
|
o:=APackage.GetOutputDirType;
|
|
Stats:=APackage.LastCompile[o];
|
|
Stats.LazarusVersion:=LazarusVersionStr;
|
|
Stats.CompilerFilename:=CompilerFilename;
|
|
Stats.CompilerFileDate:=CompilerFileDate;
|
|
Stats.Params.Assign(CompilerParams);
|
|
Stats.Complete:=Complete;
|
|
Stats.ViaMakefile:=false;
|
|
|
|
XMLConfig:=TXMLConfig.CreateClean(StateFile);
|
|
try
|
|
XMLConfig.SetValue('Lazarus/Version',Stats.LazarusVersion);
|
|
XMLConfig.SetValue('Compiler/Value',CompilerFilename);
|
|
XMLConfig.SetValue('Compiler/Date',CompilerFileDate);
|
|
XMLConfig.SetValue('Params/Value',MergeCmdLineParams(CompilerParams));
|
|
XMLConfig.SetDeleteValue('Complete/Value',Complete,true);
|
|
XMLConfig.SetDeleteValue('Complete/MainPPUExists',MainPPUExists,true);
|
|
InvalidateFileStateCache;
|
|
XMLConfig.Flush;
|
|
finally
|
|
XMLConfig.Free;
|
|
end;
|
|
Stats.StateFileName:=StateFile;
|
|
Stats.StateFileDate:=FileAgeCached(StateFile);
|
|
Stats.StateFileLoaded:=true;
|
|
except
|
|
on E: Exception do begin
|
|
Result:=LazMessageDialogAb(lisPkgMangErrorWritingFile,
|
|
Format(lisPkgMangUnableToWriteStateFileOfPackageError,
|
|
[StateFile, LineEnding, APackage.IDAsString, LineEnding, E.Message]),
|
|
mtError,[mbCancel],ShowAbort);
|
|
exit;
|
|
end;
|
|
end;
|
|
|
|
Result:=mrOk;
|
|
end;
|
|
|
|
function TLazPackageGraph.LoadPackageCompiledState(APackage: TLazPackage;
|
|
IgnoreErrors, ShowAbort: boolean): TModalResult;
|
|
var
|
|
StateFile: String;
|
|
o: TPkgOutputDir;
|
|
begin
|
|
StateFile:=APackage.GetStateFilename;
|
|
o:=APackage.GetOutputDirType;
|
|
Result:=LoadPackageCompiledStateFile(APackage,o,StateFile,IgnoreErrors,ShowAbort);
|
|
end;
|
|
|
|
function TLazPackageGraph.CheckCompileNeedDueToFPCUnits(TheOwner: TObject;
|
|
StateFileAge: longint; var Note: string): boolean;
|
|
var
|
|
AProject: TLazProject;
|
|
Pkg: TLazPackage;
|
|
ID: String;
|
|
Dir: string;
|
|
UnitSetID: String;
|
|
HasChanged: boolean;
|
|
Cache: TFPCUnitSetCache;
|
|
CfgCache: TPCTargetConfigCache;
|
|
Node: TAVLTreeNode;
|
|
Item: PStringToStringItem;
|
|
Filename: String;
|
|
UnitToSrcTree: TStringToStringTree;
|
|
CurUnitName: String;
|
|
PkgOutDirs: TFilenameToStringTree;
|
|
i: Integer;
|
|
begin
|
|
Result:=false;
|
|
if TheOwner=nil then exit;
|
|
if TheOwner is TLazPackage then begin
|
|
Pkg:=TLazPackage(TheOwner);
|
|
if Pkg.IsVirtual then exit;
|
|
Dir:=Pkg.DirectoryExpanded;
|
|
ID:=Pkg.Name;
|
|
end else if TheOwner is TLazProject then begin
|
|
AProject:=TLazProject(TheOwner);
|
|
Dir:=ExtractFilePath(AProject.ProjectInfoFile);
|
|
ID:=ExtractFileName(AProject.ProjectInfoFile);
|
|
end else
|
|
exit;
|
|
if (Dir='') or (not FilenameIsAbsolute(Dir)) then
|
|
exit;
|
|
UnitSetID:=CodeToolBoss.GetUnitSetIDForDirectory(Dir);
|
|
if UnitSetID='' then exit;
|
|
Cache:=CodeToolBoss.CompilerDefinesCache.FindUnitSetWithID(UnitSetID,HasChanged,false);
|
|
if Cache=nil then exit;
|
|
CfgCache:=Cache.GetConfigCache(false);
|
|
if CfgCache=nil then exit;
|
|
if CfgCache.Units=nil then exit;
|
|
UnitToSrcTree:=Cache.GetUnitToSourceTree(false);
|
|
PkgOutDirs:=TFilenameToStringTree.Create(false);
|
|
try
|
|
for i:=0 to Count-1 do
|
|
PkgOutDirs[AppendPathDelim(Packages[i].CompilerOptions.GetUnitOutPath(false))]:='1';
|
|
|
|
Node:=CfgCache.Units.Tree.FindLowest;
|
|
while Node<>nil do begin
|
|
Item:=PStringToStringItem(Node.Data);
|
|
Node:=CfgCache.Units.Tree.FindSuccessor(Node);
|
|
Filename:=Item^.Value;
|
|
if PkgOutDirs.Contains(ExtractFilePath(Filename)) then begin
|
|
// a package output directory is in the global search path
|
|
// => ignore
|
|
continue;
|
|
end;
|
|
CurUnitName:=Item^.Name;
|
|
if (UnitToSrcTree<>nil) and (UnitToSrcTree.Count>0)
|
|
and (not UnitToSrcTree.Contains(CurUnitName)) then begin
|
|
// this unit has no source in the FPC source directory
|
|
// probably an user unit reachable through a unit path in fpc.cfg
|
|
continue;
|
|
end;
|
|
if SrcEditFileIsModified(Filename) then begin
|
|
if ConsoleVerbosity>=0 then
|
|
debugln(['Hint: (lazarus) global unit "',Filename,'" of package ',ID,' has been modified in source editor']);
|
|
Note+='Global unit "'+Filename+'" of '+ID+' has been modified in source editor'+LineEnding;
|
|
exit(true);
|
|
end;
|
|
if FileAgeCached(Filename)>StateFileAge then begin
|
|
if ConsoleVerbosity>=0 then
|
|
debugln(['Hint: (lazarus) global unit "',Filename,'" is newer than state file of package ',ID]);
|
|
Note+='Global unit "'+Filename+'" is newer than state file of '+ID+':'+LineEnding
|
|
+' Unit age='+FileAgeToStr(FileAgeCached(Filename))+LineEnding
|
|
+' State file age='+FileAgeToStr(StateFileAge)+LineEnding;
|
|
exit(true);
|
|
end;
|
|
end;
|
|
finally
|
|
PkgOutDirs.Free;
|
|
end;
|
|
end;
|
|
|
|
function TLazPackageGraph.CheckCompileNeedDueToDependencies(TheOwner: TObject;
|
|
FirstDependency: TPkgDependency; SkipDesignTimePackages: boolean;
|
|
StateFileAge: longint; var Note: string): TModalResult;
|
|
|
|
function GetOwnerID: string;
|
|
begin
|
|
OnGetDependencyOwnerDescription(FirstDependency,Result);
|
|
end;
|
|
|
|
var
|
|
Dependency: TPkgDependency;
|
|
RequiredPackage: TLazPackage;
|
|
OtherStateFile: String;
|
|
o: TPkgOutputDir;
|
|
begin
|
|
Dependency:=FirstDependency;
|
|
if Dependency=nil then begin
|
|
// no dependencies
|
|
// => check FPC units
|
|
if CheckCompileNeedDueToFPCUnits(TheOwner,StateFileAge,Note) then
|
|
exit(mrYes);
|
|
Result:=mrNo;
|
|
exit;
|
|
end;
|
|
|
|
while Dependency<>nil do begin
|
|
if (Dependency.LoadPackageResult=lprSuccess) then begin
|
|
RequiredPackage:=Dependency.RequiredPackage;
|
|
if Dependency.DependencyType=pdtFPMake
|
|
then begin
|
|
// skip
|
|
end else if SkipDesignTimePackages and (RequiredPackage.PackageType=lptDesignTime) then
|
|
// skip
|
|
else if RequiredPackage.IsVirtual then
|
|
// skip
|
|
else if RequiredPackage.Missing then
|
|
// skip
|
|
else begin
|
|
// check compile state file of required package
|
|
Result:=LoadPackageCompiledState(RequiredPackage,false,true);
|
|
if Result<>mrOk then begin
|
|
// file broken, user was told that file is broken and user had a
|
|
// choice of cancel or cancel all (=mrAbort).
|
|
// File broken means that the pkgname.compiled file has an invalid
|
|
// format, syntax error. The user or some external tool has altered
|
|
// the file. Maybe on purpose.
|
|
// The IDE should not silently replace the file.
|
|
// => pass the mrcancel/mrabort to the caller
|
|
Note+='unable to load state file of '+RequiredPackage.IDAsString;
|
|
exit;
|
|
end;
|
|
Result:=mrYes;
|
|
o:=RequiredPackage.GetOutputDirType;
|
|
if not RequiredPackage.LastCompile[o].StateFileLoaded then begin
|
|
DebugLn('Hint: (lazarus) Missing state file for ',RequiredPackage.IDAsString,': ',RequiredPackage.GetStateFilename);
|
|
Note+='Package '+RequiredPackage.IDAsString+' has no state file "'+RequiredPackage.GetStateFilename+'".'+LineEnding;
|
|
exit;
|
|
end;
|
|
if StateFileAge<RequiredPackage.LastCompile[o].StateFileDate then begin
|
|
DebugLn('Hint: (lazarus) State file of ',RequiredPackage.IDAsString,' is newer than state file of ',GetOwnerID);
|
|
Note+='State file of '+RequiredPackage.IDAsString+' is newer than state file of '+GetOwnerID+LineEnding
|
|
+' '+RequiredPackage.IDAsString+'='+FileAgeToStr(RequiredPackage.LastCompile[o].StateFileDate)+LineEnding
|
|
+' '+GetOwnerID+'='+FileAgeToStr(StateFileAge)+LineEnding;
|
|
exit;
|
|
end;
|
|
// check output state file of required package
|
|
if RequiredPackage.OutputStateFile<>'' then begin
|
|
OtherStateFile:=RequiredPackage.OutputStateFile;
|
|
GlobalMacroList.SubstituteStr(OtherStateFile);
|
|
if not FilenameIsAbsolute(OtherStateFile) then
|
|
OtherStateFile:=AppendPathDelim(RequiredPackage.Directory)+OtherStateFile;
|
|
if FilenameIsAbsolute(OtherStateFile)
|
|
and FileExistsCached(OtherStateFile)
|
|
and (FileAgeCached(OtherStateFile)>StateFileAge) then begin
|
|
DebugLn('Hint: (lazarus) State file of ',RequiredPackage.IDAsString,' "',OtherStateFile,'" (',
|
|
FileAgeToStr(FileAgeCached(OtherStateFile)),')'
|
|
,' is newer than state file ',GetOwnerID,'(',FileAgeToStr(StateFileAge),')');
|
|
Note+='State file of used package is newer than state file:'+LineEnding
|
|
+' Used package '+RequiredPackage.IDAsString+', file="'+OtherStateFile+'", '
|
|
+' age='+FileAgeToStr(FileAgeCached(OtherStateFile))+LineEnding
|
|
+' package '+GetOwnerID+', age='+FileAgeToStr(StateFileAge)+LineEnding;
|
|
Result:=mrYes;
|
|
exit;
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
Dependency:=Dependency.NextRequiresDependency;
|
|
end;
|
|
Result:=mrNo;
|
|
end;
|
|
|
|
function TLazPackageGraph.CheckIfPackageNeedsCompilation(APackage: TLazPackage;
|
|
SkipDesignTimePackages, GroupCompile: boolean; var NeedBuildAllFlag: boolean;
|
|
var Note: string): TModalResult;
|
|
var
|
|
OutputDir: String;
|
|
NewOutputDir: String;
|
|
ConfigChanged: boolean;
|
|
DependenciesChanged: boolean;
|
|
DefResult: TModalResult;
|
|
OldNeedBuildAllFlag, IsDefDirWritable, ForceBuild: Boolean;
|
|
OldOverride: String;
|
|
begin
|
|
Result:=mrYes;
|
|
{$IFDEF VerbosePkgCompile}
|
|
debugln('TLazPackageGraph.CheckIfPackageNeedsCompilation A ',APackage.IDAsString);
|
|
{$ENDIF}
|
|
ForceBuild:=NeedBuildAllFlag;
|
|
if ForceBuild then begin
|
|
// user demands to rebuild the package
|
|
end else begin
|
|
if (APackage.AutoUpdate=pupManually) then
|
|
exit(mrNo);
|
|
// check the current output directory
|
|
Result:=CheckIfCurPkgOutDirNeedsCompile(APackage,
|
|
true,SkipDesignTimePackages,GroupCompile,
|
|
NeedBuildAllFlag,ConfigChanged,DependenciesChanged,Note);
|
|
if Result=mrNo then begin
|
|
// the current output is valid
|
|
exit;
|
|
end;
|
|
end;
|
|
|
|
// the current output directory needs compilation
|
|
OutputDir:=APackage.GetOutputDirectory(false);
|
|
IsDefDirWritable:=OutputDirectoryIsWritable(APackage,OutputDir,false);
|
|
|
|
if APackage.CompilerOptions.ParsedOpts.OutputDirectoryOverride='' then
|
|
begin
|
|
// the last compile was put to the normal/default output directory
|
|
if IsDefDirWritable then
|
|
begin
|
|
// the normal output directory is writable => keep using it
|
|
exit;
|
|
end;
|
|
debugln(['Hint: (lazarus) normal output directory of package ',APackage.IDAsString,' is not writable: "',OutputDir,'"']);
|
|
// the normal output directory is not writable
|
|
// => try the fallback directory
|
|
NewOutputDir:=GetFallbackOutputDir(APackage);
|
|
if (NewOutputDir=OutputDir) or (NewOutputDir='') then begin
|
|
Note+='Normal output directory is not writable. There is no fallback.'+LineEnding;
|
|
exit;
|
|
end;
|
|
Note+='Normal output directory is not writable, switching to fallback.'+LineEnding;
|
|
APackage.CompilerOptions.ParsedOpts.OutputDirectoryOverride:=NewOutputDir;
|
|
if ForceBuild then
|
|
Result:=mrYes
|
|
else
|
|
Result:=CheckIfCurPkgOutDirNeedsCompile(APackage,
|
|
true,SkipDesignTimePackages,GroupCompile,
|
|
NeedBuildAllFlag,ConfigChanged,DependenciesChanged,Note);
|
|
end else begin
|
|
// the last compile was put to the fallback output directory
|
|
|
|
if not IsDefDirWritable then begin
|
|
if ForceBuild then begin
|
|
// => keep using the fallback directory
|
|
exit;
|
|
end;
|
|
if not ConfigChanged then begin
|
|
// some source files have changed, not the compiler parameters
|
|
// => keep using the fallback directory
|
|
exit;
|
|
end;
|
|
if DependenciesChanged then begin
|
|
// dependencies have changed
|
|
// => switching to the not writable default output directory is not possible
|
|
// => keep using the fallback directory
|
|
exit;
|
|
end;
|
|
// maybe the user switched the settings back to default
|
|
// => try using the default output directory
|
|
end;
|
|
|
|
OldOverride:=APackage.CompilerOptions.ParsedOpts.OutputDirectoryOverride;
|
|
APackage.CompilerOptions.ParsedOpts.OutputDirectoryOverride:='';
|
|
if ConsoleVerbosity>=0 then
|
|
debugln(['Hint: (lazarus) trying the default output directory of package ',APackage.IDAsString]);
|
|
OldNeedBuildAllFlag:=NeedBuildAllFlag;
|
|
DefResult:=CheckIfCurPkgOutDirNeedsCompile(APackage,
|
|
true,SkipDesignTimePackages,GroupCompile,
|
|
NeedBuildAllFlag,ConfigChanged,DependenciesChanged,Note);
|
|
if IsDefDirWritable or (DefResult=mrNo) then begin
|
|
// switching back to the default output directory
|
|
debugln(['Hint: (lazarus) switching back to the normal output directory: "',APackage.GetOutputDirectory,'" Package ',APackage.IDAsString]);
|
|
Note+='Switching back to default output directory.'+LineEnding;
|
|
exit(DefResult);
|
|
end;
|
|
// neither the default nor the fallback is valid
|
|
// => switch back to the fallback
|
|
if ConsoleVerbosity>=0 then
|
|
debugln(['Hint: (lazarus) switching back to fallback output directory package ',APackage.IDAsString]);
|
|
APackage.CompilerOptions.ParsedOpts.OutputDirectoryOverride:=OldOverride;
|
|
NeedBuildAllFlag:=OldNeedBuildAllFlag;
|
|
end;
|
|
end;
|
|
|
|
function TLazPackageGraph.CheckIfCurPkgOutDirNeedsCompile(
|
|
APackage: TLazPackage; CheckDependencies, SkipDesignTimePackages,
|
|
GroupCompile: boolean; out NeedBuildAllFlag, ConfigChanged,
|
|
DependenciesChanged: boolean; var Note: string): TModalResult;
|
|
// returns: mrYes, mrNo, mrCancel, mrAbort
|
|
var
|
|
StateFilename: String;
|
|
StateFileAge: Integer;
|
|
i: Integer;
|
|
CurFile: TPkgFile;
|
|
LastParams: TStrings;
|
|
LastPaths, CurPaths: TStringList;
|
|
CompilerParams: TStrings;
|
|
OldValue: string;
|
|
NewValue: string;
|
|
o: TPkgOutputDir;
|
|
Stats: TPkgLastCompileStats;
|
|
SrcPPUFile: String;
|
|
AFilename: String;
|
|
CompilerFilename, SrcFilename: string;
|
|
LFMFilename: String;
|
|
ReducedParams, ReducedLastParams: TStrings;
|
|
begin
|
|
Result:=mrYes;
|
|
{$IFDEF VerbosePkgCompile}
|
|
debugln('TLazPackageGraph.CheckIfCurPkgOutDirNeedsCompile A ',APackage.IDAsString);
|
|
{$ENDIF}
|
|
NeedBuildAllFlag:=false;
|
|
ConfigChanged:=false;
|
|
DependenciesChanged:=false;
|
|
|
|
if APackage.AutoUpdate=pupManually then
|
|
exit(mrNo);
|
|
|
|
SrcFilename:=APackage.GetSrcFilename;
|
|
CompilerFilename:=APackage.GetCompilerFilename;
|
|
// Note: use absolute paths, because some external tools resolve symlinked directories
|
|
CompilerParams:=GetPackageCompilerParams(APackage);
|
|
try
|
|
o:=APackage.GetOutputDirType;
|
|
Stats:=APackage.LastCompile[o];
|
|
//debugln(['TLazPackageGraph.CheckIfCurPkgOutDirNeedsCompile Last="',ExtractCompilerParamsForBuildAll(APackage.LastCompilerParams),'" Now="',ExtractCompilerParamsForBuildAll(CompilerParams),'"']);
|
|
|
|
// check state file
|
|
StateFilename:=APackage.GetStateFilename;
|
|
Result:=LoadPackageCompiledState(APackage,false,true);
|
|
if Result<>mrOk then
|
|
exit; // read error and user aborted
|
|
if not Stats.StateFileLoaded then begin
|
|
// package was not compiled via Lazarus nor via Makefile/fpmake
|
|
DebugLn('Hint: (lazarus) Missing state file of ',APackage.IDAsString,': ',StateFilename);
|
|
Note+='Missing state file "'+StateFilename+'".'+LineEnding;
|
|
NeedBuildAllFlag:=true;
|
|
ConfigChanged:=true;
|
|
exit(mrYes);
|
|
end;
|
|
|
|
if (o=podFallback) and (Stats.LazarusVersion<>LazarusVersionStr) then
|
|
begin
|
|
// package in fallback directory was compiled by another Lazarus -> rebuild
|
|
DebugLn('Hint: (lazarus) State file of ',APackage.IDAsString,' from Lazarus "',Stats.LazarusVersion,'" instead of "',LazarusVersionStr,'": ',StateFilename);
|
|
Note+='State file "'+StateFilename+'" from another Lazarus version "'+Stats.LazarusVersion+'".'+LineEnding;
|
|
NeedBuildAllFlag:=true;
|
|
ConfigChanged:=true;
|
|
exit(mrYes);
|
|
end;
|
|
|
|
// check if build all (-B) is needed
|
|
if (Stats.CompilerFilename<>CompilerFilename)
|
|
or FPCParamForBuildAllHasChanged(Stats.Params,CompilerParams)
|
|
or ((Stats.CompilerFileDate>0)
|
|
and FileExistsCached(CompilerFilename)
|
|
and (FileAgeCached(CompilerFilename)<>Stats.CompilerFileDate))
|
|
then begin
|
|
NeedBuildAllFlag:=true;
|
|
ConfigChanged:=true;
|
|
end;
|
|
|
|
if GroupCompile and (lpfNeedGroupCompile in APackage.Flags) then begin
|
|
//debugln(['TLazPackageGraph.CheckIfCurPkgOutDirNeedsCompile dependencies will be rebuilt']);
|
|
Note+='Dependencies will be rebuilt.'+LineEnding;
|
|
DependenciesChanged:=true;
|
|
exit(mrYes);
|
|
end;
|
|
|
|
StateFileAge:=FileAgeUTF8(StateFilename);
|
|
|
|
// check compiler and params
|
|
LastParams:=APackage.LastCompile[o].Params;
|
|
if Stats.ViaMakefile then begin
|
|
// the package was compiled via Makefile/fpmake
|
|
if ConsoleVerbosity>=1 then
|
|
debugln(['Hint: (lazarus) package ',APackage.IDAsString,' was compiled via "make" with parameters "',MergeCmdLineParams(LastParams,TLazCompilerOptions.ConsoleParamsMax),'"']);
|
|
|
|
CurPaths:=nil;
|
|
LastPaths:=nil;
|
|
try
|
|
CurPaths:=ExtractMakefileCompiledParams(CompilerParams,true);
|
|
LastPaths:=ExtractMakefileCompiledParams(LastParams,true);
|
|
|
|
//debugln(['TLazPackageGraph.CheckIfCurPkgOutDirNeedsCompile CompilerParams="',CompilerParams,'" UnitPaths="',CurPaths.Values['UnitPath'],'"']);
|
|
// compare custom options
|
|
OldValue:=LastPaths.Values['Reduced'];
|
|
NewValue:=CurPaths.Values['Reduced'];
|
|
if NewValue<>OldValue then begin
|
|
DebugLn('Hint: (lazarus) Compiler custom params changed for ',APackage.IDAsString);
|
|
DebugLn(' Old="',OldValue,'"');
|
|
DebugLn(' Now="',NewValue,'"');
|
|
DebugLn(' State file="',Stats.StateFileName,'"');
|
|
Note+='Compiler custom parameters changed:'+LineEnding
|
|
+' Old="'+OldValue+'"'+LineEnding
|
|
+' Now="'+NewValue+'"'+LineEnding
|
|
+' State file="'+Stats.StateFileName+'"'+LineEnding;
|
|
ConfigChanged:=true;
|
|
exit(mrYes);
|
|
end;
|
|
// compare unit paths
|
|
OldValue:=TrimSearchPath(LastPaths.Values['UnitPath'],APackage.Directory,true);
|
|
NewValue:=TrimSearchPath(CurPaths.Values['UnitPath'],APackage.Directory,true);
|
|
if NewValue<>OldValue then begin
|
|
DebugLn('Hint: (lazarus) Compiler unit paths changed for ',APackage.IDAsString);
|
|
DebugLn(' Old="',OldValue,'"');
|
|
DebugLn(' Now="',NewValue,'"');
|
|
DebugLn(' State file="',Stats.StateFileName,'"');
|
|
Note+='Compiler unit paths changed:'+LineEnding
|
|
+' Old="'+OldValue+'"'+LineEnding
|
|
+' Now="'+NewValue+'"'+LineEnding
|
|
+' State file="'+Stats.StateFileName+'"'+LineEnding;
|
|
ConfigChanged:=true;
|
|
exit(mrYes);
|
|
end;
|
|
// compare include paths
|
|
OldValue:=TrimSearchPath(LastPaths.Values['IncPath'],APackage.Directory,true);
|
|
NewValue:=TrimSearchPath(CurPaths.Values['IncPath'],APackage.Directory,true);
|
|
if NewValue<>OldValue then begin
|
|
DebugLn('Hint: (lazarus) Compiler include paths changed for ',APackage.IDAsString);
|
|
DebugLn(' Old="',OldValue,'"');
|
|
DebugLn(' Now="',NewValue,'"');
|
|
DebugLn(' State file="',Stats.StateFileName,'"');
|
|
Note+='Compiler include paths changed:'+LineEnding
|
|
+' Old="'+OldValue+'"'+LineEnding
|
|
+' Now="'+NewValue+'"'+LineEnding
|
|
+' State file="'+Stats.StateFileName+'"'+LineEnding;
|
|
ConfigChanged:=true;
|
|
exit(mrYes);
|
|
end;
|
|
finally
|
|
CurPaths.Free;
|
|
LastPaths.Free;
|
|
end;
|
|
end else begin
|
|
ReducedParams:=RemoveFPCVerbosityParams(CompilerParams);
|
|
ReducedLastParams:=RemoveFPCVerbosityParams(LastParams);
|
|
try
|
|
if not ReducedParams.Equals(ReducedLastParams) then begin
|
|
// package was compiled by Lazarus
|
|
DebugLn('Hint: (lazarus) Compiler params changed for ',APackage.IDAsString);
|
|
DebugLn(' Old="',dbgstr(MergeCmdLineParams(ReducedLastParams,TLazCompilerOptions.ConsoleParamsMax)),'"');
|
|
DebugLn(' Now="',dbgstr(MergeCmdLineParams(ReducedParams,TLazCompilerOptions.ConsoleParamsMax)),'"');
|
|
DebugLn(' State file="',Stats.StateFileName,'"');
|
|
Note+='Compiler parameters changed:'+LineEnding
|
|
+' Old="'+dbgstr(MergeCmdLineParams(ReducedLastParams,TLazCompilerOptions.ConsoleParamsMax))+'"'+LineEnding
|
|
+' Now="'+dbgstr(MergeCmdLineParams(ReducedParams,TLazCompilerOptions.ConsoleParamsMax))+'"'+LineEnding
|
|
+' State file="'+Stats.StateFileName+'"'+LineEnding;
|
|
ConfigChanged:=true;
|
|
exit(mrYes);
|
|
end;
|
|
finally
|
|
ReducedParams.Free;
|
|
ReducedLastParams.Free;
|
|
end;
|
|
end;
|
|
|
|
// compiler
|
|
if (not Stats.ViaMakefile)
|
|
and (CompilerFilename<>Stats.CompilerFilename) then begin
|
|
DebugLn('Hint: (lazarus) Compiler filename changed for ',APackage.IDAsString);
|
|
DebugLn(' Old="',Stats.CompilerFilename,'"');
|
|
DebugLn(' Now="',CompilerFilename,'"');
|
|
DebugLn(' State file="',Stats.StateFileName,'"');
|
|
Note+='Compiler filename changed:'+LineEnding
|
|
+' Old="'+Stats.CompilerFilename+'"'+LineEnding
|
|
+' Now="'+CompilerFilename+'"'+LineEnding
|
|
+' State file="'+Stats.StateFileName+'"'+LineEnding;
|
|
exit(mrYes);
|
|
end;
|
|
if not FileExistsCached(CompilerFilename) then begin
|
|
DebugLn('Hint: (lazarus) Compiler filename not found for ',APackage.IDAsString);
|
|
DebugLn(' File="',CompilerFilename,'"');
|
|
DebugLn(' State file="',Stats.StateFileName,'"');
|
|
Note+='Compiler file "'+CompilerFilename+'" not found.'+LineEnding
|
|
+' State file="'+Stats.StateFileName+'"'+LineEnding;
|
|
exit(mrYes);
|
|
end;
|
|
if (not Stats.ViaMakefile)
|
|
and (FileAgeCached(CompilerFilename)<>Stats.CompilerFileDate) then begin
|
|
DebugLn('Hint: (lazarus) Compiler file changed for ',APackage.IDAsString);
|
|
DebugLn(' File="',CompilerFilename,'"');
|
|
DebugLn(' State file="',Stats.StateFileName,'"');
|
|
Note+='Compiler file "'+CompilerFilename+'" changed:'+LineEnding
|
|
+' Old='+FileAgeToStr(Stats.CompilerFileDate)+LineEnding
|
|
+' Now='+FileAgeToStr(FileAgeCached(CompilerFilename))+LineEnding
|
|
+' State file="'+Stats.StateFileName+'"'+LineEnding;
|
|
exit(mrYes);
|
|
end;
|
|
|
|
// check main source file
|
|
if (SrcFilename<>'') then
|
|
begin
|
|
if not FileExistsCached(SrcFilename) then begin
|
|
DebugLn('Hint: (lazarus) source file missing of ',APackage.IDAsString,': ',SrcFilename);
|
|
Note+='Source file "'+SrcFilename+'" missing.'+LineEnding;
|
|
exit(mrYes);
|
|
end;
|
|
if StateFileAge<FileAgeCached(SrcFilename) then begin
|
|
DebugLn('Hint: (lazarus) source disk file modified of ',APackage.IDAsString,': ',SrcFilename);
|
|
Note+='Source file "'+SrcFilename+'" modified:'+LineEnding
|
|
+' Source file age='+FileAgeToStr(FileAgeCached(SrcFilename))+LineEnding
|
|
+' State file="'+Stats.StateFileName+'"'+LineEnding
|
|
+' State file age='+FileAgeToStr(StateFileAge)+LineEnding;
|
|
exit(mrYes);
|
|
end;
|
|
if SrcEditFileIsModified(SrcFilename) then begin
|
|
DebugLn('Hint: (lazarus) source editor file of ',APackage.IDAsString,': ',SrcFilename);
|
|
Note+='Source file "'+SrcFilename+'" modified in source editor.'+LineEnding;
|
|
exit(mrYes);
|
|
end;
|
|
// check main source ppu file
|
|
if Stats.MainPPUExists then begin
|
|
SrcPPUFile:=APackage.GetSrcPPUFilename;
|
|
if not FileExistsCached(SrcPPUFile) then begin
|
|
DebugLn('Hint: (lazarus) main ppu file missing of ',APackage.IDAsString,': ',SrcPPUFile);
|
|
Note+='Main ppu file "'+SrcPPUFile+'" missing.'+LineEnding;
|
|
exit(mrYes);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
//debugln(['TLazPackageGraph.CheckIfCurPkgOutDirNeedsCompile ',APackage.Name,' Last="',APackage.LastCompilerParams,'" Now="',CompilerParams,'"']);
|
|
|
|
// compiler and parameters are the same
|
|
// quick compile is possible
|
|
NeedBuildAllFlag:=false;
|
|
|
|
if not Stats.Complete then begin
|
|
DebugLn('Hint: (lazarus) Last compile was incomplete for ',APackage.IDAsString);
|
|
DebugLn(' State file="',Stats.StateFileName,'"');
|
|
Note+='Last compile was incomplete.'+LineEnding
|
|
+' State file="'+Stats.StateFileName+'"'+LineEnding;
|
|
exit(mrYes);
|
|
end;
|
|
|
|
if CheckDependencies then begin
|
|
// check all required packages
|
|
Result:=CheckCompileNeedDueToDependencies(APackage,
|
|
APackage.FirstRequiredDependency,SkipDesignTimePackages,StateFileAge,
|
|
Note);
|
|
if Result<>mrNo then begin
|
|
DependenciesChanged:=true;
|
|
exit(mrYes);
|
|
end;
|
|
end;
|
|
|
|
// check package files
|
|
if StateFileAge<FileAgeCached(APackage.Filename) then begin
|
|
DebugLn('Hint: (lazarus) State file older than lpk ',APackage.IDAsString);
|
|
DebugLn(' State file="',Stats.StateFileName,'"');
|
|
Note+='State file older than lpk:'+LineEnding
|
|
+' State file age='+FileAgeToStr(StateFileAge)+LineEnding
|
|
+' State file="'+Stats.StateFileName+'"'+LineEnding
|
|
+' LPK age='+FileAgeToStr(FileAgeCached(APackage.Filename))+LineEnding;
|
|
exit(mrYes);
|
|
end;
|
|
for i:=0 to APackage.FileCount-1 do begin
|
|
CurFile:=APackage.Files[i];
|
|
//debugln(['TLazPackageGraph.CheckIfPackageNeedsCompilation CurFile.Filename="',CurFile.Filename,'" Exists=',FileExistsUTF8(CurFile.Filename),' NewerThanStateFile=',StateFileAge<FileAgeCached(CurFile.Filename)]);
|
|
AFilename:=CurFile.GetFullFilename;
|
|
if SrcEditFileIsModified(AFilename) then begin
|
|
DebugLn('Hint: (lazarus) Source editor file has been modified ',APackage.IDAsString,' ',CurFile.Filename);
|
|
Note+='';
|
|
end;
|
|
if FileExistsCached(AFilename)
|
|
and (StateFileAge<FileAgeCached(AFilename)) then begin
|
|
DebugLn('Hint: (lazarus) Source file has changed ',APackage.IDAsString,' ',CurFile.Filename);
|
|
DebugLn(' State file="',Stats.StateFileName,'"');
|
|
Note+='State file older than source "'+AFilename+'"'+LineEnding
|
|
+' State file age='+FileAgeToStr(StateFileAge)+LineEnding
|
|
+' State file="'+Stats.StateFileName+'"'+LineEnding
|
|
+' Src file age='+FileAgeToStr(FileAgeCached(AFilename))+LineEnding;
|
|
exit(mrYes);
|
|
end;
|
|
if FilenameIsPascalUnit(AFilename) then begin
|
|
LFMFilename:=ChangeFileExt(AFilename,'.lfm');
|
|
if FileExistsCached(LFMFilename)
|
|
and (StateFileAge<FileAgeCached(LFMFilename)) then begin
|
|
DebugLn('Hint: (lazarus) LFM has changed ',APackage.IDAsString,' ',LFMFilename);
|
|
DebugLn(' State file="',Stats.StateFileName,'"');
|
|
Note+='State file older than resource "'+LFMFilename+'"'+LineEnding
|
|
+' State file age='+FileAgeToStr(StateFileAge)+LineEnding
|
|
+' State file="'+Stats.StateFileName+'"'+LineEnding
|
|
+' Resource file age='+FileAgeToStr(FileAgeCached(LFMFilename))+LineEnding;
|
|
exit(mrYes);
|
|
end;
|
|
end;
|
|
end;
|
|
finally
|
|
CompilerParams.Free;
|
|
end;
|
|
|
|
{$IFDEF VerbosePkgCompile}
|
|
debugln('TLazPackageGraph.CheckIfCurPkgOutDirNeedsCompile END ',APackage.IDAsString);
|
|
{$ENDIF}
|
|
Result:=mrNo;
|
|
end;
|
|
|
|
function TLazPackageGraph.LoadPackageCompiledStateFile(APackage: TLazPackage;
|
|
o: TPkgOutputDir; StateFile: string; IgnoreErrors, ShowAbort: boolean
|
|
): TModalResult;
|
|
var
|
|
Stats: TPkgLastCompileStats;
|
|
StateFileAge, MakefileVersion: LongInt;
|
|
XMLConfig: TXMLConfig;
|
|
MakefileValue, Params: String;
|
|
begin
|
|
Stats:=APackage.LastCompile[o];
|
|
if not FileExistsCached(StateFile) then begin
|
|
//DebugLn('TLazPackageGraph.LoadPackageCompiledStateFile file not found: ',StateFile);
|
|
Stats.StateFileLoaded:=false;
|
|
Result:=mrOk;
|
|
exit;
|
|
end;
|
|
|
|
// read the state file
|
|
StateFileAge:=FileAgeCached(StateFile);
|
|
if (not Stats.StateFileLoaded)
|
|
or (Stats.StateFileDate<>StateFileAge)
|
|
or (Stats.StateFileName<>StateFile) then begin
|
|
Stats.StateFileLoaded:=false;
|
|
Stats.LazarusVersion:='';
|
|
Stats.Complete:=false;
|
|
Stats.CompilerFilename:='';
|
|
Stats.StateFileName:=StateFile;
|
|
Stats.StateFileDate:=StateFileAge;
|
|
try
|
|
XMLConfig:=TXMLConfig.Create(StateFile);
|
|
try
|
|
Stats.LazarusVersion:=XMLConfig.GetValue('Lazarus/Version','');
|
|
Stats.CompilerFilename:=XMLConfig.GetValue('Compiler/Value','');
|
|
Stats.CompilerFileDate:=XMLConfig.GetValue('Compiler/Date',0);
|
|
Stats.Complete:=XMLConfig.GetValue('Complete/Value',true);
|
|
Stats.MainPPUExists:=XMLConfig.GetValue('Complete/MainPPUExists',true);
|
|
MakefileValue:=XMLConfig.GetValue('Makefile/Value','');
|
|
Stats.Params.Clear;
|
|
Params:=XMLConfig.GetValue('Params/Value','');
|
|
if (MakefileValue='') then
|
|
Stats.ViaMakefile:=false
|
|
else begin
|
|
Stats.ViaMakefile:=true;
|
|
MakefileVersion:=StrToIntDef(MakefileValue,0);
|
|
if MakefileVersion<2 then begin
|
|
// old versions used %(
|
|
Stats.CompilerFilename:=StringReplace(Stats.CompilerFilename,'%(','$(',[rfReplaceAll]);
|
|
Params:=StringReplace(Params,'%(','$(',[rfReplaceAll]);
|
|
end;
|
|
ForcePathDelims(Stats.CompilerFilename);
|
|
ForcePathDelims(Params);
|
|
Params:=StringReplace(Params,'$(CPU_TARGET)','$(TargetCPU)',[rfReplaceAll]);
|
|
Params:=StringReplace(Params,'$(OS_TARGET)','$(TargetOS)',[rfReplaceAll]);
|
|
Params:=StringReplace(Params,'$(LCL_PLATFORM)','$(LCLWidgetType)',[rfReplaceAll]);
|
|
Params:=APackage.SubstitutePkgMacros(Params,false);
|
|
end;
|
|
SplitCmdLineParams(Params,Stats.Params);
|
|
finally
|
|
XMLConfig.Free;
|
|
end;
|
|
Stats.StateFileLoaded:=true;
|
|
except
|
|
on E: EXMLReadError do begin
|
|
// invalid XML
|
|
debugln(['Warning: (lazarus) package "',APackage.IDAsString,'": syntax error in ',StateFile,' => need clean build.']);
|
|
Stats.StateFileLoaded:=true;
|
|
end;
|
|
on E: Exception do begin
|
|
if IgnoreErrors then begin
|
|
Result:=mrOk;
|
|
end else begin
|
|
Result:=LazMessageDialogAb(lisPkgMangErrorReadingFile,
|
|
Format(lisPkgMangUnableToReadStateFileOfPackageError,
|
|
[StateFile, LineEnding, APackage.IDAsString, LineEnding, E.Message]),
|
|
mtError,[mbCancel],ShowAbort);
|
|
end;
|
|
exit;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
Result:=mrOk;
|
|
end;
|
|
|
|
procedure TLazPackageGraph.InvalidateStateFile(APackage: TLazPackage);
|
|
begin
|
|
APackage.LastCompile[APackage.GetOutputDirType].StateFileLoaded:=false
|
|
end;
|
|
|
|
function TLazPackageGraph.CompileRequiredPackages(APackage: TLazPackage;
|
|
FirstDependency: TPkgDependency; SkipDesignTimePackages: boolean;
|
|
Policy: TPackageUpdatePolicy): TModalResult;
|
|
var
|
|
BuildItems: TObjectList;
|
|
|
|
function PkgToBuildItem(Pkg: TLazPackage): TLazPkgGraphBuildItem;
|
|
var
|
|
i: Integer;
|
|
begin
|
|
for i:=0 to BuildItems.Count-1 do begin
|
|
Result:=TLazPkgGraphBuildItem(BuildItems[i]);
|
|
if Result.LazPackage=Pkg then exit;
|
|
end;
|
|
Result:=nil;
|
|
end;
|
|
|
|
var
|
|
PkgList: TFPList;
|
|
FPMakeList: TFPList;
|
|
i: Integer;
|
|
Flags: TPkgCompileFlags;
|
|
ReqFlags: TPkgIntfRequiredFlags;
|
|
CurPkg: TLazPackage;
|
|
BuildItem: TLazPkgGraphBuildItem;
|
|
j: Integer;
|
|
aDependency: TPkgDependency;
|
|
RequiredBuildItem: TLazPkgGraphBuildItem;
|
|
Tool1: TAbstractExternalTool;
|
|
Tool2: TAbstractExternalTool;
|
|
ToolGroup: TExternalToolGroup;
|
|
FilesChanged: boolean;
|
|
begin
|
|
{$IFDEF VerbosePkgCompile}
|
|
debugln('TLazPackageGraph.CompileRequiredPackages A MinPolicy=',dbgs(Policy),' SkipDesignTimePackages=',SkipDesignTimePackages);
|
|
{$ENDIF}
|
|
ReqFlags:=[pirCompileOrder];
|
|
if SkipDesignTimePackages then
|
|
Include(ReqFlags,pirSkipDesignTimeOnly);
|
|
GetAllRequiredPackages(APackage,FirstDependency,PkgList,FPMakeList,ReqFlags,Policy);
|
|
if (PkgList<>nil) or (FPMakeList<>nil) then begin
|
|
//DebugLn('TLazPackageGraph.CompileRequiredPackages B Count=',IntToStr(PkgList.Count));
|
|
BuildItems:=nil;
|
|
ToolGroup:=nil;
|
|
BeginUpdate(false);
|
|
try
|
|
if (PkgList<>nil) then begin
|
|
for i:=PkgList.Count-1 downto 0 do begin
|
|
CurPkg:=TLazPackage(PkgList[i]);
|
|
if SkipDesignTimePackages and (CurPkg.PackageType=lptDesignTime) then
|
|
PkgList.Delete(i);
|
|
CurPkg.Flags:=CurPkg.Flags-[lpfNeedGroupCompile];
|
|
end;
|
|
if Assigned(OnBeforeCompilePackages) then
|
|
begin
|
|
Result:=OnBeforeCompilePackages(PkgList);
|
|
if Result<>mrOk then exit;
|
|
end;
|
|
|
|
// prepare output directories, basic checks
|
|
Flags:=[pcfDoNotCompileDependencies,pcfDoNotSaveEditorFiles,pcfGroupCompile];
|
|
if SkipDesignTimePackages then
|
|
Include(Flags,pcfSkipDesignTimePackages);
|
|
if Policy=pupAsNeeded then
|
|
Include(Flags,pcfOnlyIfNeeded)
|
|
else
|
|
Include(Flags,pcfCleanCompile);
|
|
repeat
|
|
BuildItems:=TObjectList.Create(true);
|
|
for i:=0 to PkgList.Count-1 do begin
|
|
CurPkg:=TLazPackage(PkgList[i]);
|
|
if CurPkg.AutoUpdate=pupManually then continue;
|
|
BuildItem:=TLazPkgGraphBuildItem.Create(nil);
|
|
BuildItem.LazPackage:=CurPkg;
|
|
BuildItems.Add(BuildItem);
|
|
Result:=CompilePackage(CurPkg,Flags,false,BuildItem);
|
|
if Result<>mrOk then exit;
|
|
|
|
if (BuildItem<>nil) and (not (lpfNeedGroupCompile in CurPkg.Flags))
|
|
then begin
|
|
// package is up-to-date
|
|
//debugln(['TLazPackageGraph.CompileRequiredPackages no build needed: pkg=',CurPkg.Name]);
|
|
BuildItems.Remove(BuildItem);
|
|
end;
|
|
end;
|
|
|
|
if FirstDependency<>nil then
|
|
begin
|
|
if not OnCheckInterPkgFiles(FirstDependency.Owner,PkgList,FilesChanged)
|
|
then exit(mrCancel);
|
|
if FilesChanged then
|
|
FreeAndNil(BuildItems);
|
|
end;
|
|
until BuildItems<>nil;
|
|
|
|
// add tool dependencies
|
|
for i:=0 to BuildItems.Count-1 do begin
|
|
BuildItem:=TLazPkgGraphBuildItem(BuildItems[i]);
|
|
CurPkg:=BuildItem.LazPackage;
|
|
if BuildItem.Count=0 then continue;
|
|
|
|
// add tools to ToolGroup
|
|
if ToolGroup=nil then
|
|
ToolGroup:=TExternalToolGroup.Create(nil);
|
|
for j:=0 to BuildItem.Count-1 do
|
|
BuildItem[j].Group:=ToolGroup;
|
|
|
|
// estimate load
|
|
for j:=0 to BuildItem.Count-1 do begin
|
|
Tool1:=BuildItem[j];
|
|
if Tool1.Data is TLazPkgGraphExtToolData then begin
|
|
Tool1.EstimatedLoad:=EstimateCompileLoad(CurPkg);
|
|
//debugln(['TLazPackageGraph.CompileRequiredPackages ',CurPkg.Name,' EstimatedLoad=',Tool1.EstimatedLoad]);
|
|
end;
|
|
end;
|
|
|
|
// add dependencies between tools of this package (execute before, compile, after)
|
|
for j:=1 to BuildItem.Count-1 do begin
|
|
Tool1:=BuildItem[j-1];
|
|
Tool2:=BuildItem[j];
|
|
Tool2.AddExecuteBefore(Tool1);
|
|
end;
|
|
|
|
// add dependencies between packages
|
|
aDependency:=CurPkg.FirstRequiredDependency;
|
|
// ToDo: Add fpmake-dependencies!
|
|
while aDependency<>nil do begin
|
|
RequiredBuildItem:=PkgToBuildItem(aDependency.RequiredPackage);
|
|
aDependency:=aDependency.NextRequiresDependency;
|
|
if RequiredBuildItem=nil then continue;
|
|
if not (lpfNeedGroupCompile in RequiredBuildItem.LazPackage.Flags) then
|
|
continue;
|
|
Tool1:=BuildItem.GetFirstOrDummy;
|
|
Tool2:=RequiredBuildItem.GetLastOrDummy;
|
|
Tool1.AddExecuteBefore(Tool2);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
if Assigned(FppkgInterface) and (FppkgInterface.InstallFPMakeDependencies) and Assigned(FPMakeList) then begin
|
|
Flags:=[pcfDoNotCompileDependencies,pcfDoNotSaveEditorFiles,pcfGroupCompile];
|
|
if SkipDesignTimePackages then
|
|
Include(Flags,pcfSkipDesignTimePackages);
|
|
if Policy=pupAsNeeded then
|
|
Include(Flags,pcfOnlyIfNeeded)
|
|
else
|
|
Include(Flags,pcfCleanCompile);
|
|
|
|
BuildItems:=TObjectList.Create(true);
|
|
for i:=0 to FPMakeList.Count-1 do begin
|
|
aDependency:=TPkgDependency(FPMakeList[i]);
|
|
BuildItem:=TLazPkgGraphBuildItem.Create(nil);
|
|
BuildItems.Add(BuildItem);
|
|
Result:=CompilePackageUsingFPMake(aDependency.PackageName,Flags,false,BuildItem);
|
|
if Result<>mrOk then exit;
|
|
end;
|
|
|
|
// add tool dependencies
|
|
for i:=0 to BuildItems.Count-1 do begin
|
|
BuildItem:=TLazPkgGraphBuildItem(BuildItems[i]);
|
|
|
|
if BuildItem.Count=0 then continue;
|
|
|
|
// Make sure that all FPMake-buildtools are executed after each other
|
|
// (It is not safe to run them simultaneously)
|
|
if i > 0 then
|
|
BuildItem.GetFirstOrDummy.AddExecuteBefore(TLazPkgGraphBuildItem(BuildItems[i-1]).GetFirstOrDummy);
|
|
|
|
// add tools to ToolGroup
|
|
if ToolGroup=nil then
|
|
ToolGroup:=TExternalToolGroup.Create(nil);
|
|
for j:=0 to BuildItem.Count-1 do
|
|
BuildItem[j].Group:=ToolGroup;
|
|
|
|
// add dependencies between tools of this package
|
|
for j:=1 to BuildItem.Count-1 do begin
|
|
Tool1:=BuildItem[j-1];
|
|
Tool2:=BuildItem[j];
|
|
Tool2.AddExecuteBefore(Tool1);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
if ToolGroup=nil then exit(mrOk);
|
|
|
|
// execute
|
|
ToolGroup.Execute;
|
|
ToolGroup.WaitForExit;
|
|
if ToolGroup.ErrorMessage<>'' then begin
|
|
debugln(['Error: (lazarus) [TLazPackageGraph.CompileRequiredPackages] "',ToolGroup.ErrorMessage,'"']);
|
|
exit(mrCancel);
|
|
end;
|
|
finally
|
|
FreeAndNil(ToolGroup);
|
|
FreeAndNil(BuildItems);
|
|
FreeAndNil(PkgList);
|
|
FreeAndNil(FPMakeList);
|
|
EndUpdate;
|
|
end;
|
|
end;
|
|
{$IFDEF VerbosePkgCompile}
|
|
debugln('TLazPackageGraph.CompileRequiredPackages END ');
|
|
{$ENDIF}
|
|
Result:=mrOk;
|
|
end;
|
|
|
|
procedure TLazPackageGraph.SetFlagDependenciesNeedBuild(Pkg: TLazPackage);
|
|
// set flag lpfNeedGroupCompile in all dependent packages
|
|
var
|
|
ADependency: TPkgDependency;
|
|
begin
|
|
if Pkg=nil then exit;
|
|
if lpfNeedGroupCompile in Pkg.Flags then exit;
|
|
Pkg.Flags:=Pkg.Flags+[lpfNeedGroupCompile];
|
|
ADependency:=Pkg.FirstUsedByDependency;
|
|
while ADependency<>nil do begin
|
|
if ADependency.Owner is TLazPackage then
|
|
SetFlagDependenciesNeedBuild(TLazPackage(ADependency.Owner));
|
|
ADependency:=ADependency.NextUsedByDependency;
|
|
end;
|
|
end;
|
|
|
|
function TLazPackageGraph.CompilePackage(APackage: TLazPackage;
|
|
Flags: TPkgCompileFlags; ShowAbort: boolean; BuildItem: TLazPkgGraphBuildItem
|
|
): TModalResult;
|
|
|
|
function GetIgnoreIdentifier: string;
|
|
begin
|
|
Result:='install_package_compile_failed:'+APackage.Filename;
|
|
end;
|
|
|
|
var
|
|
PkgCompileTool: TAbstractExternalTool;
|
|
FPCParser: TFPCParser;
|
|
CompilerFilename: String;
|
|
CompilePolicy: TPackageUpdatePolicy;
|
|
NeedBuildAllFlag, NeedBuildAll: Boolean;
|
|
CompilerParams, CmdLineParams: TStrings;
|
|
Note: String;
|
|
WorkingDir: String;
|
|
ToolTitle, CfgFilename: String;
|
|
ExtToolData: TLazPkgGraphExtToolData;
|
|
BuildMethod: TBuildMethod;
|
|
CfgCode: TCodeBuffer;
|
|
begin
|
|
Result:=mrCancel;
|
|
|
|
//DebugLn('TLazPackageGraph.CompilePackage A ',APackage.IDAsString,' Flags=',PkgCompileFlagsToString(Flags));
|
|
|
|
if APackage.IsVirtual then begin
|
|
DebugLn(['Error: (lazarus) compile failed because virtual: ',APackage.Filename]);
|
|
exit;
|
|
end;
|
|
|
|
BeginUpdate(false);
|
|
try
|
|
// automatically compile required packages
|
|
if not (pcfDoNotCompileDependencies in Flags) then begin
|
|
if pcfCompileDependenciesClean in Flags then
|
|
CompilePolicy:=pupOnRebuildingAll
|
|
else
|
|
CompilePolicy:=pupAsNeeded;
|
|
Result:=CompileRequiredPackages(APackage,nil,
|
|
pcfSkipDesignTimePackages in Flags,CompilePolicy);
|
|
if Result<>mrOk then begin
|
|
DebugLn(['Error: (lazarus) Compile required packages failed: ',APackage.IDAsString]);
|
|
exit;
|
|
end;
|
|
end;
|
|
|
|
if not APackage.CompilerOptions.HasCommands then begin
|
|
// package provides no compilation
|
|
Result:=mrOk;
|
|
exit;
|
|
end;
|
|
|
|
// check if compilation is needed and if a clean build is needed
|
|
NeedBuildAllFlag:=pcfCleanCompile in Flags;
|
|
Note:='';
|
|
Result:=CheckIfPackageNeedsCompilation(APackage,
|
|
pcfSkipDesignTimePackages in Flags,
|
|
pcfGroupCompile in Flags,
|
|
NeedBuildAllFlag,Note);
|
|
if Note<>'' then
|
|
Note:='Compile reason: '+Note;
|
|
if (pcfOnlyIfNeeded in Flags) then begin
|
|
if Result=mrNo then begin
|
|
//DebugLn(['TLazPackageGraph.CompilePackage ',APackage.IDAsString,' does not need compilation.']);
|
|
Result:=mrOk;
|
|
exit;
|
|
end;
|
|
if Result<>mrYes then begin
|
|
DebugLn(['Error: (lazarus) [CheckIfPackageNeedsCompilation] failed: ',APackage.IDAsString]);
|
|
exit;
|
|
end;
|
|
end;
|
|
|
|
CompilerParams:=nil;
|
|
CmdLineParams:=nil;
|
|
try
|
|
if (BuildItem=nil) and (LazarusIDE<>nil) then
|
|
LazarusIDE.MainBarSubTitle:=APackage.Name;
|
|
|
|
if pcfGroupCompile in Flags then
|
|
SetFlagDependenciesNeedBuild(APackage);
|
|
|
|
// auto increase version
|
|
// ToDo
|
|
|
|
Result:=PreparePackageOutputDirectory(APackage,pcfCleanCompile in Flags);
|
|
if Result<>mrOk then begin
|
|
DebugLn('Error: (lazarus) [TLazPackageGraph.CompilePackage] PreparePackageOutputDirectory failed: ',APackage.IDAsString);
|
|
exit;
|
|
end;
|
|
|
|
// create package main source file
|
|
Result:=SavePackageMainSource(APackage,Flags,ShowAbort);
|
|
if Result<>mrOk then begin
|
|
DebugLn('Error: (lazarus) [TLazPackageGraph.CompilePackage] SavePackageMainSource failed: ',APackage.IDAsString);
|
|
exit;
|
|
end;
|
|
|
|
// check ambiguous units
|
|
Result:=CheckAmbiguousPackageUnits(APackage);
|
|
if Result<>mrOk then begin
|
|
DebugLn('Error: (lazarus) [TLazPackageGraph.CompilePackage] CheckAmbiguousPackageUnits failed: ',APackage.IDAsString);
|
|
exit;
|
|
end;
|
|
|
|
// create Makefile
|
|
if ((pcfCreateMakefile in Flags)
|
|
or (APackage.CompilerOptions.CreateMakefileOnBuild)) then begin
|
|
Result:=WriteMakeFile(APackage);
|
|
if Result<>mrOk then begin
|
|
DebugLn('Error: (lazarus) [TLazPackageGraph.CompilePackage] DoWriteMakefile failed: ',APackage.IDAsString);
|
|
exit;
|
|
end;
|
|
end;
|
|
|
|
// create fpmake.pp
|
|
if ((pcfCreateFpmakeFile in Flags)
|
|
or (APackage.GetActiveBuildMethod = bmFPMake)
|
|
or ((APackage.CompilerOptions.CreateMakefileOnBuild) and (APackage.BuildMethod in [bmBoth, bmFPMake]) and Assigned(FppkgInterface))) then begin
|
|
Result:=WriteFpmake(APackage);
|
|
if Result<>mrOk then begin
|
|
DebugLn('Error: (lazarus) [TLazPackageGraph.CompilePackage] DoWriteFpmakeFile failed: ',APackage.IDAsString);
|
|
exit;
|
|
end;
|
|
end;
|
|
|
|
// run compilation tool 'Before'
|
|
if not (pcfDoNotCompilePackage in Flags) then begin
|
|
WorkingDir:=APackage.Directory;
|
|
ToolTitle:='Package '+APackage.IDAsString+': '+lisExecutingCommandBefore;
|
|
if BuildItem<>nil then
|
|
begin
|
|
// run later
|
|
BuildItem.Add(APackage.CompilerOptions.ExecuteBefore.CreateExtTool(
|
|
WorkingDir,ToolTitle,Note));
|
|
end else begin
|
|
// run now
|
|
Result:=APackage.CompilerOptions.ExecuteBefore.Execute(WorkingDir,
|
|
ToolTitle,Note);
|
|
if Result<>mrOk then begin
|
|
DebugLn(['Error: (lazarus) [TLazPackageGraph.CompilePackage] ExecuteBefore failed: ',APackage.IDAsString]);
|
|
exit;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
if Assigned(LazarusIDE) then
|
|
begin
|
|
Result := LazarusIDE.DoCallPackageBuildingHandler(APackage);
|
|
if Result<>mrOK then
|
|
Exit;
|
|
end;
|
|
|
|
// create external tool to run the compiler
|
|
//DebugLn('TLazPackageGraph.CompilePackage WorkingDir="',APackage.Directory,'"');
|
|
|
|
if (not APackage.CompilerOptions.SkipCompiler)
|
|
and (not (pcfDoNotCompilePackage in Flags)) then begin
|
|
NeedBuildAll:=false;
|
|
BuildMethod:=APackage.GetActiveBuildMethod;
|
|
if BuildMethod=bmLazarus then begin
|
|
CompilerFilename:=APackage.GetCompilerFilename;
|
|
|
|
// change compiler parameters for compiling clean
|
|
CompilerParams:=GetPackageCompilerParams(APackage);
|
|
if (pcfCleanCompile in Flags) or NeedBuildAllFlag then
|
|
NeedBuildAll:=true;
|
|
|
|
WarnSuspiciousCompilerOptions('Compile checks','package '+APackage.IDAsString+':',CompilerParams);
|
|
|
|
if APackage.CompilerOptions.WriteConfigFile then
|
|
begin
|
|
CfgFilename:=APackage.GetWriteConfigFilePath;
|
|
CfgCode:=WriteCompilerCfgFile(CfgFilename,CompilerParams,CmdLineParams);
|
|
if CfgCode=nil then begin
|
|
LazMessageWorker(lisReadError,Format(lisUnableToReadFile2,
|
|
[CfgFilename]),mtError,[mbOk]);
|
|
exit(mrCancel);
|
|
end;
|
|
if CfgCode.FileOnDiskNeedsUpdate and (SaveCodeBuffer(CfgCode)<>mrOk) then
|
|
exit(mrCancel);
|
|
end;
|
|
end else begin
|
|
CompilerFilename:='fppkg';
|
|
CompilerParams:=TStringListUTF8Fast.Create;
|
|
CompilerParams.Add('install');
|
|
CompilerParams.Add('--skipbroken');
|
|
CompilerParams.Add('--broken');
|
|
// Do not recompile all broken fppkg packages on each package that is
|
|
// installed, but run 'fppkg fixbroken' once.
|
|
FHasCompiledFpmakePackages := True;
|
|
end;
|
|
|
|
ExtToolData:=TLazPkgGraphExtToolData.Create(IDEToolCompilePackage,
|
|
APackage.Name,APackage.Filename);
|
|
|
|
PkgCompileTool:=ExternalToolList.Add(Format(lisPkgMangCompilePackage, [APackage.IDAsString]));
|
|
PkgCompileTool.Data:=ExtToolData;
|
|
PkgCompileTool.FreeData:=true;
|
|
if BuildItem<>nil then
|
|
BuildItem.Add(PkgCompileTool)
|
|
else
|
|
PkgCompileTool.Reference(Self,Classname);
|
|
try
|
|
FPCParser:=TFPCParser(PkgCompileTool.AddParsers(SubToolFPC));
|
|
//debugln(['TLazPackageGraph.CompilePackage ',APackage.Name,' ',APackage.CompilerOptions.ShowHintsForUnusedUnitsInMainSrc,' ',APackage.MainUnit.Filename]);
|
|
if (APackage.MainUnit<>nil)
|
|
and (not APackage.CompilerOptions.ShowHintsForUnusedUnitsInMainSrc) then
|
|
FPCParser.FilesToIgnoreUnitNotUsed.Add(APackage.MainUnit.Filename);
|
|
FPCParser.HideHintsSenderNotUsed:=not APackage.CompilerOptions.ShowHintsForSenderNotUsed;
|
|
FPCParser.HideHintsUnitNotUsedInMainSource:=not APackage.CompilerOptions.ShowHintsForUnusedUnitsInMainSrc;
|
|
PkgCompileTool.AddParsers(SubToolMake);
|
|
PkgCompileTool.Process.CurrentDirectory:=APackage.Directory;
|
|
PkgCompileTool.Process.Executable:=CompilerFilename;
|
|
PkgCompileTool.Process.Parameters:=CompilerParams;
|
|
if NeedBuildAll then
|
|
PkgCompileTool.Process.Parameters.Add('-B');
|
|
PkgCompileTool.Hint:=Note;
|
|
ExtToolData.Pkg:=APackage;
|
|
ExtToolData.SrcPPUFilename:=APackage.GetSrcPPUFilename;
|
|
ExtToolData.CompilerFilename:=CompilerFilename;
|
|
ExtToolData.CompilerParams.Assign(CompilerParams);
|
|
PkgCompileTool.AddHandlerOnStopped(@ExtToolBuildStopped);
|
|
if BuildItem<>nil then
|
|
begin
|
|
// run later
|
|
end else begin
|
|
// run now
|
|
PkgCompileTool.Execute;
|
|
//debugln(['TLazPackageGraph.CompilePackage BEFORE WaitForExit: ',APackage.IDAsString]);
|
|
PkgCompileTool.WaitForExit;
|
|
//debugln(['TLazPackageGraph.CompilePackage AFTER WaitForExit: ',APackage.IDAsString,' ExtToolData.ErrorMessage=',ExtToolData.ErrorMessage]);
|
|
if ExtToolData.ErrorMessage<>'' then
|
|
exit(mrCancel);
|
|
end;
|
|
finally
|
|
if BuildItem=nil then
|
|
PkgCompileTool.Release(Self);
|
|
end;
|
|
end;
|
|
|
|
if not (pcfDoNotCompilePackage in Flags) then begin
|
|
if FHasCompiledFpmakePackages and (BuildItem = nil) then begin
|
|
// Make sure all dependees of changed FPMake-packages are
|
|
// recompiled
|
|
|
|
PkgCompileTool := ExternalToolList.Add('Recompile and install broken Fppkg packages');
|
|
PkgCompileTool.Reference(Self,Classname);
|
|
|
|
FPCParser:=TFPCParser(PkgCompileTool.AddParsers(SubToolFPC));
|
|
FPCParser.HideHintsSenderNotUsed:=not APackage.CompilerOptions.ShowHintsForSenderNotUsed;
|
|
FPCParser.HideHintsUnitNotUsedInMainSource:=not APackage.CompilerOptions.ShowHintsForUnusedUnitsInMainSrc;
|
|
PkgCompileTool.Process.CurrentDirectory:=APackage.Directory;
|
|
PkgCompileTool.Process.Executable:='fppkg';
|
|
PkgCompileTool.CmdLineParams:='fixbroken';
|
|
PkgCompileTool.Hint:='Check for Fppkg packages that depend on just installed packages and recompile them';
|
|
|
|
PkgCompileTool.Execute();
|
|
PkgCompileTool.WaitForExit;
|
|
if PkgCompileTool.ErrorMessage<>'' then begin
|
|
DebugLn(['Error: (lazarus) [TLazPackageGraph.CompilePackage] Fppkg FixBroken failed']);
|
|
// Note: messages window already contains error message
|
|
exit(mrCancel);
|
|
end;
|
|
end;
|
|
|
|
// run compilation tool 'After'
|
|
WorkingDir:=APackage.Directory;
|
|
ToolTitle:='Package '+APackage.IDAsString+': '+lisExecutingCommandAfter;
|
|
if BuildItem<>nil then
|
|
begin
|
|
// run later
|
|
BuildItem.Add(APackage.CompilerOptions.ExecuteAfter.CreateExtTool(
|
|
WorkingDir,ToolTitle,Note));
|
|
end else begin
|
|
// run now
|
|
Result:=APackage.CompilerOptions.ExecuteAfter.Execute(WorkingDir,
|
|
ToolTitle,Note);
|
|
if Result<>mrOk then begin
|
|
DebugLn(['Error: (lazarus) [TLazPackageGraph.CompilePackage] ExecuteAfter failed: ',APackage.IDAsString]);
|
|
// Note: messages window already contains error message
|
|
exit;
|
|
end;
|
|
end;
|
|
end;
|
|
Result:=mrOk;
|
|
finally
|
|
if CmdLineParams<>CompilerParams then
|
|
CmdLineParams.Free;
|
|
CompilerParams.Free;
|
|
if (BuildItem=nil) and (LazarusIDE<>nil) then
|
|
LazarusIDE.MainBarSubTitle:='';
|
|
end;
|
|
finally
|
|
PackageGraph.EndUpdate;
|
|
end;
|
|
end;
|
|
|
|
function TLazPackageGraph.CompilePackageUsingFPMake(APackageName: string; Flags: TPkgCompileFlags;
|
|
ShowAbort: boolean; BuildItem: TLazPkgGraphBuildItem): TModalResult;
|
|
|
|
var
|
|
PkgCompileTool: TAbstractExternalTool;
|
|
CompilerFilename: String;
|
|
EffectiveCompilerParams: String;
|
|
begin
|
|
Result:=mrCancel;
|
|
if ShowAbort then ;
|
|
|
|
//DebugLn('TLazPackageGraph.CompilePackageAsFPMake A ',APackageName,' Flags=',PkgCompileFlagsToString(Flags));
|
|
BeginUpdate(false);
|
|
try
|
|
try
|
|
if (BuildItem=nil) and (LazarusIDE<>nil) then
|
|
LazarusIDE.MainBarSubTitle:=APackageName;
|
|
|
|
// create external tool to run the compiler
|
|
//DebugLn('TLazPackageGraph.CompilePackageFPMake');
|
|
|
|
if (not (pcfDoNotCompilePackage in Flags)) then begin
|
|
CompilerFilename:='fppkg';
|
|
EffectiveCompilerParams:='install -b '+APackageName;
|
|
|
|
PkgCompileTool:=ExternalToolList.Add(Format(lisPkgMangCompilePackage, [APackageName]));
|
|
if BuildItem<>nil then
|
|
BuildItem.Add(PkgCompileTool)
|
|
else
|
|
PkgCompileTool.Reference(Self,Classname);
|
|
try
|
|
PkgCompileTool.AddParsers(SubToolFPC);
|
|
PkgCompileTool.AddParsers(SubToolMake);
|
|
PkgCompileTool.Process.Executable:=CompilerFilename;
|
|
PkgCompileTool.CmdLineParams:=EffectiveCompilerParams;
|
|
if BuildItem<>nil then
|
|
begin
|
|
// run later
|
|
end else begin
|
|
// run now
|
|
PkgCompileTool.Execute;
|
|
//debugln(['TLazPackageGraph.CompileFPMakePackage BEFORE WaitForExit: ',APackageName]);
|
|
PkgCompileTool.WaitForExit;
|
|
//debugln(['TLazPackageGraph.CompileFPMakePackage AFTER WaitForExit: ',APackageName,' ExtToolData.ErrorMessage=',ExtToolData.ErrorMessage]);
|
|
end;
|
|
finally
|
|
if BuildItem=nil then
|
|
PkgCompileTool.Release(Self);
|
|
end;
|
|
end;
|
|
Result:=mrOk;
|
|
finally
|
|
if (BuildItem=nil) and (LazarusIDE<>nil) then
|
|
LazarusIDE.MainBarSubTitle:='';
|
|
end;
|
|
finally
|
|
PackageGraph.EndUpdate;
|
|
end;
|
|
end;
|
|
|
|
function TLazPackageGraph.ConvertPackageRSTFiles(APackage: TLazPackage
|
|
): TModalResult;
|
|
var
|
|
PkgOutputDirectory: String;
|
|
POOutputDirectory: String;
|
|
begin
|
|
Result:=mrOK;
|
|
if (APackage.POOutputDirectory='') then exit;// nothing to do
|
|
POOutputDirectory:=AppendPathDelim(APackage.GetPOOutDirectory);
|
|
|
|
// create output directory if not exists
|
|
if not DirectoryExistsUTF8(POOutputDirectory) then begin
|
|
Result:=ForceDirectoryInteractive(POOutputDirectory,[mbRetry,mbIgnore]);
|
|
if Result<>mrOk then begin
|
|
if Result=mrIgnore then Result:=mrOk;
|
|
DebugLn(['Note: (lazarus) [TLazPackageGraph.ConvertPackageRSTFiles] unable to create directory ',POOutputDirectory]);
|
|
exit;
|
|
end;
|
|
end;
|
|
|
|
// find all .rst files in package output directory
|
|
if not DirectoryIsWritableCached(POOutputDirectory) then begin
|
|
// this package is read only
|
|
DebugLn(['Warning: (lazarus) [TLazPackageGraph.ConvertPackageRSTFiles] skipping read only directory '+POOutputDirectory]);
|
|
exit(mrOK);
|
|
end;
|
|
|
|
PkgOutputDirectory:=AppendPathDelim(APackage.GetOutputDirectory);
|
|
if not ConvertRSTFiles(PkgOutputDirectory,POOutputDirectory) then begin
|
|
DebugLn(['Error: (lazarus) [TLazPackageGraph.ConvertPackageRSTFiles] unable to update .po files PkgOutputDirectory=',PkgOutputDirectory,' RSTOutputDirectory=',POOutputDirectory]);
|
|
exit(mrCancel);
|
|
end;
|
|
Result:=mrOK;
|
|
end;
|
|
|
|
function TLazPackageGraph.WriteMakefileCompiled(APackage: TLazPackage;
|
|
TargetCompiledFile, UnitPath, IncPath, OtherOptions: string): TModalResult;
|
|
var
|
|
XMLConfig: TXMLConfig;
|
|
s: String;
|
|
begin
|
|
try
|
|
XMLConfig:=TXMLConfig.Create(TargetCompiledFile);
|
|
try
|
|
XMLConfig.SetValue('Makefile/Value',MakefileCompileVersion);
|
|
s:='';
|
|
if UnitPath<>'' then
|
|
s:=s+' -Fu'+SwitchPathDelims(UnitPath,pdsUnix);
|
|
if IncPath<>'' then
|
|
s:=s+' -Fi'+SwitchPathDelims(IncPath,pdsUnix);
|
|
if OtherOptions<>'' then
|
|
s:=s+' '+OtherOptions;
|
|
// do no write the unit output directory
|
|
// it is not needed because it is the location of the Makefile.compiled
|
|
s:=s+' '+SwitchPathDelims(CreateRelativePath(APackage.GetSrcFilename,APackage.Directory),pdsUnix);
|
|
s:=UTF8Trim(s);
|
|
if ConsoleVerbosity>1 then
|
|
debugln(['Hint: (lazarus) writing ',TargetCompiledFile,' IncPath="',IncPath,'" UnitPath="',UnitPath,'" Custom="',OtherOptions,'" Makefile.compiled="',TargetCompiledFile,'"']);
|
|
XMLConfig.SetValue('Params/Value',s);
|
|
if XMLConfig.Modified then begin
|
|
InvalidateFileStateCache;
|
|
XMLConfig.Flush;
|
|
end else begin
|
|
if ConsoleVerbosity>1 then
|
|
debugln(['Hint: (lazarus) not writing ',TargetCompiledFile,', because nothing changed']);
|
|
end;
|
|
finally
|
|
XMLConfig.Free;
|
|
end;
|
|
except
|
|
on E: Exception do begin
|
|
Result:=LazMessageWorker(lisPkgMangErrorWritingFile,
|
|
Format(lisPkgMangUnableToWriteStateFileOfPackageError,
|
|
[TargetCompiledFile, LineEnding, APackage.IDAsString, LineEnding, E.Message]),
|
|
mtError,[mbCancel],'');
|
|
exit;
|
|
end;
|
|
end;
|
|
Result:=mrOk;
|
|
end;
|
|
|
|
function TLazPackageGraph.WriteMakeFile(APackage: TLazPackage): TModalResult;
|
|
var
|
|
PathDelimNeedsReplace: Boolean;
|
|
|
|
procedure Replace(var s: string; const SearchTxt, ReplaceTxt: string);
|
|
var
|
|
p: LongInt;
|
|
begin
|
|
repeat
|
|
p:=Pos(SearchTxt,s);
|
|
if p<=1 then break;
|
|
ReplaceSubstring(s,p,length(SearchTxt),ReplaceTxt);
|
|
until false;
|
|
end;
|
|
|
|
function ConvertPIMacrosToMakefileMacros(const s: string): string;
|
|
begin
|
|
Result:=s;
|
|
Replace(Result,'%(','$(');
|
|
end;
|
|
|
|
function ConvertLazarusToMakefileSearchPath(const s: string): string;
|
|
begin
|
|
Result:=ConvertPIMacrosToMakefileMacros(s);
|
|
Result:=CreateRelativeSearchPath(TrimSearchPath(Result,''),APackage.Directory);
|
|
Replace(Result,';',' ');
|
|
if PathDelimNeedsReplace then
|
|
Replace(Result,PathDelim,'/');
|
|
end;
|
|
|
|
function ConvertLazarusToMakefileCompiledSearchPath(const s: string): string;
|
|
begin
|
|
Result:=ConvertPIMacrosToMakefileMacros(s);
|
|
Result:=CreateRelativeSearchPath(TrimSearchPath(Result,''),APackage.Directory);
|
|
if PathDelimNeedsReplace then
|
|
Replace(Result,PathDelim,'/');
|
|
end;
|
|
|
|
function ConvertLazarusToMakefileDirectory(const s: string): string;
|
|
begin
|
|
Result:=ConvertPIMacrosToMakefileMacros(s);
|
|
Result:=CreateRelativePath(TrimFilename(Result),APackage.Directory);
|
|
if PathDelimNeedsReplace then
|
|
Replace(Result,PathDelim,'/');
|
|
// trim trailing PathDelim, as windows does not like it
|
|
Result:=ChompPathDelim(Result);
|
|
end;
|
|
|
|
function ConvertLazarusOptionsToMakefileOptions(const s: string): string;
|
|
begin
|
|
Result:=ConvertPIMacrosToMakefileMacros(s);
|
|
if PathDelimNeedsReplace then
|
|
Replace(Result,PathDelim,'/');
|
|
end;
|
|
|
|
var
|
|
s, Last: String;
|
|
e: string;
|
|
SrcFilename: String;
|
|
MainUnitName: String;
|
|
MakefileFPCFilename: String;
|
|
UnitOutputPath: String;
|
|
UnitPath: String;
|
|
FPCMakeTool: TAbstractExternalTool;
|
|
CodeBuffer: TCodeBuffer;
|
|
MainSrcFile: String;
|
|
CustomOptions: String;
|
|
IncPath: String;
|
|
MakefileCompiledFilename: String;
|
|
OtherOptions: String;
|
|
FormUnitPath: String;
|
|
FormIncPath: String;
|
|
Executable: String;
|
|
DistCleanDir: String;
|
|
NeedFPCMake: Boolean;
|
|
List: TStringListUTF8Fast;
|
|
begin
|
|
Result:=mrCancel;
|
|
PathDelimNeedsReplace:=PathDelim<>'/';
|
|
|
|
if not DirectoryIsWritableCached(APackage.Directory) then begin
|
|
// The Makefile.fpc is only needed for custom building.
|
|
// If the package directory is not writable, then the user does not want to
|
|
// custom build
|
|
// => silently skip
|
|
DebugLn(['Error: (lazarus) Skipping writing Makefile, because package directory is not writable: ',APackage.Directory]);
|
|
Result:=mrOk;
|
|
exit;
|
|
end;
|
|
MakefileFPCFilename:=AppendPathDelim(APackage.Directory)+'Makefile.fpc';
|
|
MakefileCompiledFilename:=AppendPathDelim(APackage.Directory)+'Makefile.compiled';
|
|
|
|
SrcFilename:=APackage.GetSrcFilename;
|
|
MainUnitName:=lowercase(ExtractFileNameOnly((SrcFilename)));
|
|
UnitPath:=APackage.CompilerOptions.GetUnitPath(true,
|
|
coptParsedPlatformIndependent);
|
|
IncPath:=APackage.CompilerOptions.GetIncludePath(true,
|
|
coptParsedPlatformIndependent,false);
|
|
UnitOutputPath:=APackage.CompilerOptions.GetUnitOutPath(true,
|
|
coptParsedPlatformIndependent);
|
|
CustomOptions:=APackage.CompilerOptions.GetCustomOptions(
|
|
coptParsedPlatformIndependent);
|
|
List:=APackage.CompilerOptions.MakeCompilerParams(
|
|
[ccloDoNotAppendOutFileOption,ccloNoMacroParams]);
|
|
OtherOptions:=MergeCmdLineParams(List);
|
|
List.Free;
|
|
|
|
// remove path delimiter at the end, or else it will fail on windows
|
|
UnitOutputPath:=ConvertLazarusToMakefileDirectory(
|
|
ChompPathDelim(UnitOutputPath));
|
|
|
|
// "make distclean" should delete all variable output directories
|
|
// For example if output directory is units/$(CPU_TARGET)-$(OS_TARGET)/$(LCL_PLATFORM)
|
|
// distclean should delete units/*
|
|
// It uses deltree, so make sure, that it does not delete ./* or ../*
|
|
DistCleanDir:=UnitOutputPath;
|
|
while Pos('$(',ExtractFilename(DistCleanDir))>0 do begin
|
|
Last:=DistCleanDir;
|
|
DistCleanDir:=ChompPathDelim(ExtractFilePath(DistCleanDir));
|
|
s:=ExtractFileName(DistCleanDir);
|
|
if (s='') or (s='.') or (s='..') then begin
|
|
DistCleanDir:=Last;
|
|
break;
|
|
end;
|
|
end;
|
|
s:=ExtractFileName(DistCleanDir);
|
|
if (s='') or (s='.') or (s='..') then
|
|
DistCleanDir:='' // do not delete potential source directories
|
|
else
|
|
DistCleanDir+='/*';
|
|
|
|
MainSrcFile:=CreateRelativePath(SrcFilename,APackage.Directory);
|
|
CustomOptions:=ConvertLazarusOptionsToMakefileOptions(CustomOptions);
|
|
OtherOptions:=ConvertLazarusOptionsToMakefileOptions(OtherOptions);
|
|
if ConsoleVerbosity>0 then
|
|
debugln(['Hint: (lazarus) [TLazPackageGraph.WriteMakeFile] Custom="',CustomOptions,'" Other="',OtherOptions,'"']);
|
|
if CustomOptions<>'' then
|
|
if OtherOptions<>'' then
|
|
OtherOptions:=OtherOptions+' '+CustomOptions
|
|
else
|
|
OtherOptions:=CustomOptions;
|
|
if ConsoleVerbosity>0 then
|
|
debugln(['Hint: (lazarus) [TLazPackageGraph.WriteMakeFile] Other="',OtherOptions,'"']);
|
|
|
|
// ---- Makefile.compiled ----------------------------------------------------
|
|
|
|
//DebugLn('TPkgManager.DoWriteMakefile ',APackage.Name,' makefile UnitPath="',UnitPath,'"');
|
|
FormUnitPath:=ConvertLazarusToMakefileCompiledSearchPath(UnitPath);
|
|
FormIncPath:=ConvertLazarusToMakefileCompiledSearchPath(IncPath);
|
|
Result:=WriteMakefileCompiled(APackage,MakefileCompiledFilename,FormUnitPath,
|
|
FormIncPath,OtherOptions);
|
|
if Result<>mrOK then exit;
|
|
|
|
// ---- Makefile.fpc ---------------------------------------------------------
|
|
|
|
//DebugLn('TPkgManager.DoWriteMakefile ',APackage.Name,' makefile UnitPath="',UnitPath,'"');
|
|
FormUnitPath:=ConvertLazarusToMakefileSearchPath(UnitPath);
|
|
FormIncPath:=ConvertLazarusToMakefileSearchPath(IncPath);
|
|
|
|
e:=LineEnding;
|
|
s:='';
|
|
s:=s+'# File generated automatically by Lazarus Package Manager'+e;
|
|
s:=s+'#'+e;
|
|
s:=s+'# Makefile.fpc for '+APackage.IDAsString+e;
|
|
s:=s+'#'+e;
|
|
s:=s+'# This file was generated on '+DateToStr(Now)+''+e;
|
|
s:=s+''+e;
|
|
s:=s+'[package]'+e;
|
|
s:=s+'name='+lowercase(APackage.Name)+e;
|
|
s:=s+'version='+APackage.Version.AsString+e;
|
|
s:=s+''+e;
|
|
s:=s+'[compiler]'+e;
|
|
s:=s+'unittargetdir='+UnitOutputPath+e;
|
|
if UnitPath<>'' then
|
|
s:=s+'unitdir='+FormUnitPath+e;
|
|
if IncPath<>'' then
|
|
s:=s+'includedir='+FormIncPath+e;
|
|
s:=s+'options='+OtherOptions+' $(DBG_OPTIONS)'+e;
|
|
s:=s+''+e;
|
|
s:=s+'[target]'+e;
|
|
s:=s+'units='+MainSrcFile+e;
|
|
s:=s+''+e;
|
|
s:=s+'[clean]'+e;
|
|
s:=s+'files=$(wildcard $(COMPILER_UNITTARGETDIR)/*$(OEXT)) \'+e;
|
|
s:=s+' $(wildcard $(COMPILER_UNITTARGETDIR)/*$(PPUEXT)) \'+e;
|
|
s:=s+' $(wildcard $(COMPILER_UNITTARGETDIR)/*$(RSTEXT)) \'+e;
|
|
if (TrimFilename(UnitOutputPath)<>'') and (TrimFilename(UnitOutputPath)<>'.')
|
|
then begin
|
|
s:=s+' $(wildcard $(COMPILER_UNITTARGETDIR)/*.lfm) \'+e;
|
|
s:=s+' $(wildcard $(COMPILER_UNITTARGETDIR)/*.res) \'+e;
|
|
end;
|
|
s:=s+' $(wildcard $(COMPILER_UNITTARGETDIR)/*.compiled) \'+e;
|
|
s:=s+' $(wildcard *$(OEXT)) $(wildcard *$(PPUEXT)) $(wildcard *$(RSTEXT))'+e;
|
|
s:=s+''+e;
|
|
s:=s+'[prerules]'+e;
|
|
s:=s+'# LCL Platform'+e;
|
|
s:=s+'ifndef LCL_PLATFORM'+e;
|
|
s:=s+'ifeq ($(OS_TARGET),win32)'+e;
|
|
s:=s+'LCL_PLATFORM=win32'+e;
|
|
s:=s+'else'+e;
|
|
s:=s+'ifeq ($(OS_TARGET),win64)'+e;
|
|
s:=s+'LCL_PLATFORM=win32'+e;
|
|
s:=s+'else'+e;
|
|
s:=s+'ifeq ($(OS_TARGET),darwin)'+e;
|
|
s:=s+'LCL_PLATFORM=cocoa'+e;
|
|
s:=s+'else'+e;
|
|
s:=s+'LCL_PLATFORM=gtk2'+e;
|
|
s:=s+'endif'+e;
|
|
s:=s+'endif'+e;
|
|
s:=s+'endif'+e;
|
|
s:=s+'endif'+e;
|
|
s:=s+'export LCL_PLATFORM'+e;
|
|
s+=e;
|
|
s:=s+'DBG_OPTIONS='+e;
|
|
s:=s+'ifeq ($(OS_TARGET),darwin)'+e;
|
|
s:=s+'DBG_OPTIONS=-gw'+e;
|
|
s:=s+'endif'+e;
|
|
|
|
s:=s+''+e;
|
|
s:=s+'[rules]'+e;
|
|
s:=s+'.PHONY: cleartarget compiled all'+e;
|
|
s:=s+''+e;
|
|
s:=s+'cleartarget:'+e;
|
|
s:=s+' -$(DEL) $(COMPILER_UNITTARGETDIR)/'+MainUnitName+'$(PPUEXT)'+e;
|
|
s:=s+''+e;
|
|
s:=s+'compiled:'+e;
|
|
s:=s+' $(CPPROG) -f Makefile.compiled $(COMPILER_UNITTARGETDIR)/'+lowercase(APackage.Name)+'.compiled'+e;
|
|
s:=s+''+e;
|
|
s:=s+'all: cleartarget $(COMPILER_UNITTARGETDIR) '+MainUnitName+'$(PPUEXT) compiled'+e;
|
|
if DistCleanDir<>'' then begin
|
|
s:=s+''+e;
|
|
s:=s+'distclean: clean'+e;
|
|
s:=s+' ${DELTREE} '+DistCleanDir+e;
|
|
end;
|
|
|
|
//DebugLn('TPkgManager.DoWriteMakefile [',s,']');
|
|
|
|
CodeBuffer:=CodeToolBoss.LoadFile(MakefileFPCFilename,true,true);
|
|
if CodeBuffer=nil then begin
|
|
CodeBuffer:=CodeToolBoss.CreateFile(MakefileFPCFilename);
|
|
if CodeBuffer=nil then begin
|
|
if not DirectoryIsWritableCached(ExtractFilePath(MakefileFPCFilename))
|
|
then begin
|
|
// the package source is read only => ignore
|
|
exit(mrOk);
|
|
end;
|
|
debugln(['Error: (lazarus) [TLazPackageGraph.WriteMakeFile] unable to create file '+MakefileFPCFilename]);
|
|
exit(mrCancel);
|
|
end;
|
|
end;
|
|
|
|
NeedFPCMake:=false;
|
|
if ExtractCodeFromMakefile(CodeBuffer.Source)<>ExtractCodeFromMakefile(s)
|
|
then begin
|
|
// Makefile.fpc changed
|
|
CodeBuffer.Source:=s;
|
|
|
|
//debugln('TPkgManager.DoWriteMakefile MakefileFPCFilename="',MakefileFPCFilename,'"');
|
|
Result:=SaveCodeBufferToFile(CodeBuffer,MakefileFPCFilename);
|
|
if Result<>mrOk then begin
|
|
if not DirectoryIsWritableCached(ExtractFilePath(MakefileFPCFilename)) then
|
|
begin
|
|
// the package source is read only => no problem
|
|
if ConsoleVerbosity>0 then
|
|
debugln(['Note: (lazarus) [TLazPackageGraph.WriteMakeFile] not writing "',MakefileFPCFilename,'" because dir not writable, ignoring...']);
|
|
Result:=mrOk;
|
|
end;
|
|
exit;
|
|
end;
|
|
NeedFPCMake:=true;
|
|
end;
|
|
|
|
Executable:=FindFPCTool('fpcmake'+GetExecutableExt,
|
|
EnvironmentOptions.GetParsedCompilerFilename);
|
|
if FileIsExecutableCached(Executable) then begin
|
|
if (not NeedFPCMake)
|
|
and (FileAgeUTF8(MakefileFPCFilename)<FileAgeCached(Executable)) then begin
|
|
if ConsoleVerbosity>=0 then
|
|
debugln(['Hint: (lazarus) [TLazPackageGraph.WriteMakeFile] "',Executable,'" is newer than "',MakefileFPCFilename,'"']);
|
|
NeedFPCMake:=true;// fpcmake is newer than Makefile.fpc
|
|
end;
|
|
if not NeedFPCMake then
|
|
exit(mrOk);
|
|
end else
|
|
Executable:='fpcmake'+GetExecutableExt;
|
|
|
|
// call fpcmake to create the Makefile
|
|
FPCMakeTool:=ExternalToolList.Add(
|
|
Format(lisIDEInfoCreatingMakefileForPackage, [APackage.IDAsString]));
|
|
FPCMakeTool.Process.CurrentDirectory:=APackage.Directory;
|
|
FPCMakeTool.Process.Executable:=Executable;
|
|
FPCMakeTool.CmdLineParams:='-q -TAll';
|
|
FPCMakeTool.EnvironmentOverrides.Add(
|
|
'FPCDIR='+EnvironmentOptions.GetParsedFPCSourceDirectory);
|
|
FPCMakeTool.Execute;
|
|
FPCMakeTool.WaitForExit;
|
|
Result:=mrOk;
|
|
end;
|
|
|
|
function TLazPackageGraph.WriteFpmake(APackage: TLazPackage): TModalResult;
|
|
var
|
|
PathDelimNeedsReplace: Boolean;
|
|
|
|
function ConvertPIMacrosToMakefileMacros(const s: string): string;
|
|
begin
|
|
result := StringsReplace(s, ['%(LCL_PLATFORM)', '%(CPU_TARGET)', '%(OS_TARGET)'],
|
|
['$(LCLWidgetType)','$(CPU)', '$(OS)'],[rfReplaceAll, rfIgnoreCase]);
|
|
result := StringReplace(result,'%(','$(',[rfReplaceAll]);
|
|
end;
|
|
|
|
function ConvertLazarusToFpmakeSearchPath(const s: string): string;
|
|
begin
|
|
Result:=ConvertPIMacrosToMakefileMacros(s);
|
|
Result:=CreateRelativeSearchPath(TrimSearchPath(Result,''),APackage.Directory);
|
|
if PathDelimNeedsReplace then
|
|
Result:=StringReplace(Result,PathDelim,'/',[rfReplaceAll]);
|
|
end;
|
|
|
|
function ConvertLazarusToMakefileDirectory(const s: string): string;
|
|
begin
|
|
Result:=ConvertPIMacrosToMakefileMacros(s);
|
|
Result:=CreateRelativePath(TrimFilename(Result),APackage.Directory);
|
|
if PathDelimNeedsReplace then
|
|
Result:=StringReplace(Result,PathDelim,'/',[rfReplaceAll]);
|
|
// trim trailing PathDelim, as windows does not like it
|
|
Result:=ChompPathDelim(Result);
|
|
end;
|
|
|
|
function StringToFpmakeOptionGroup(const OptionName,Options:string; OptPrefix: string = ''): string;
|
|
var
|
|
sl: TStrings;
|
|
i: Integer;
|
|
begin
|
|
result := '';
|
|
sl := SplitString(Options,';');
|
|
try
|
|
for i := 0 to sl.Count-1 do
|
|
result := result + OptionName+'('''+OptPrefix+sl.Strings[i]+''');'+LineEnding;
|
|
finally
|
|
sl.Free;
|
|
end;
|
|
end;
|
|
|
|
function ConvertLazarusOptionsToFpmakeOptions(const s: string): string;
|
|
begin
|
|
Result:=ConvertPIMacrosToMakefileMacros(s);
|
|
Result := StringReplace(Result,' ',';',[rfReplaceAll]);
|
|
if PathDelimNeedsReplace then
|
|
Result:=StringReplace(Result,PathDelim,'/',[rfReplaceAll]);
|
|
end;
|
|
|
|
function PkgVersionToFPVersionString(const v: TPkgVersion): string;
|
|
var
|
|
FPVersion: TFPVersion;
|
|
begin
|
|
FPVersion := TFPVersion.Create;
|
|
try
|
|
FPVersion.Major := v.Major;
|
|
FPVersion.Minor := v.Minor;
|
|
FPVersion.Micro := v.Release;
|
|
FPVersion.Build := v.Build;
|
|
Result := FPVersion.AsString;
|
|
finally
|
|
FPVersion.Free;
|
|
end;
|
|
end;
|
|
|
|
function TextToString(const s: string): string;
|
|
var
|
|
LineEndingStr: string;
|
|
i: Integer;
|
|
begin
|
|
LineEndingStr := '';
|
|
for i := 1 to length(LineEnding) do
|
|
LineEndingStr := LineEndingStr + '#' + IntToStr(Ord(String(sLineBreak)[i]));
|
|
Result := AnsiQuotedStr(Trim(s), '''');
|
|
Result := ReplaceStr(Result, #13#10, ''''+LineEndingStr+'''');
|
|
Result := ReplaceStr(Result, #10, ''''+LineEndingStr+'''');
|
|
end;
|
|
|
|
var
|
|
s: String;
|
|
e: string;
|
|
SrcFilename: String;
|
|
FpmakeFPCFilename: String;
|
|
UnitPath: String;
|
|
OriginalCode: String;
|
|
CodeBuffer: TCodeBuffer;
|
|
MainSrcFile: String;
|
|
CustomOptions: String;
|
|
IncPath: String;
|
|
OtherOptions: String;
|
|
i: Integer;
|
|
ARequirement: TPkgDependency;
|
|
FPmakeCompiledFilename: String;
|
|
List: TStringListUTF8Fast;
|
|
begin
|
|
Result:=mrCancel;
|
|
PathDelimNeedsReplace:=PathDelim<>'/';
|
|
|
|
if not DirectoryIsWritableCached(APackage.Directory) then begin
|
|
// The fpmake.pp is only needed for custom building.
|
|
// If the package directory is not writable, then the user does not want to
|
|
// custom build
|
|
// => silently skip
|
|
DebugLn(['Note: (lazarus) Skipping writing fpmake.pp, because package directory is not writable: ',APackage.Directory]);
|
|
Result:=mrOk;
|
|
exit;
|
|
end;
|
|
FpmakeFPCFilename:=AppendPathDelim(APackage.Directory)+'fpmake.pp';
|
|
FPmakeCompiledFilename:=AppendPathDelim(APackage.Directory)+lowercase(APackage.Name)+'.compiled';
|
|
|
|
SrcFilename:=APackage.GetSrcFilename;
|
|
|
|
UnitPath:=APackage.CompilerOptions.ParsedOpts.GetParsedPIValue(pcosUnitPath);
|
|
UnitPath:=CreateRelativeSearchPath(UnitPath,APackage.CompilerOptions.BaseDirectory);
|
|
UnitPath:=MergeSearchPaths(UnitPath, '.');
|
|
|
|
IncPath:=APackage.CompilerOptions.GetIncludePath(true,
|
|
coptParsedPlatformIndependent,false);
|
|
CustomOptions:=APackage.CompilerOptions.GetCustomOptions(
|
|
coptParsedPlatformIndependent);
|
|
if ConsoleVerbosity>0 then
|
|
debugln('Hint: (lazarus) Writing fpmake.pp: CustomOptions (orig): ',CustomOptions);
|
|
List:=APackage.CompilerOptions.MakeCompilerParams(
|
|
[ccloDoNotAppendOutFileOption,ccloNoMacroParams]);
|
|
if ConsoleVerbosity>0 then
|
|
debugln('Hint: (lazarus) Writing fpmake.pp: OtherOptions (orig): ',MergeCmdLineParams(List,TLazCompilerOptions.ConsoleParamsMax));
|
|
OtherOptions:=MergeCmdLineParams(List);
|
|
List.Free;
|
|
|
|
// write compiled file
|
|
Result:=WriteMakefileCompiled(APackage,FPmakeCompiledFilename,UnitPath,
|
|
IncPath,OtherOptions);
|
|
if Result<>mrOK then exit;
|
|
|
|
//DebugLn('TPkgManager.DoWriteMakefile ',APackage.Name,' makefile UnitPath="',UnitPath,'"');
|
|
UnitPath:=ConvertLazarusToFpmakeSearchPath(UnitPath);
|
|
IncPath:=ConvertLazarusToFpmakeSearchPath(IncPath);
|
|
// remove path delimiter at the end, or else it will fail on windows
|
|
MainSrcFile:=CreateRelativePath(SrcFilename,APackage.Directory);
|
|
CustomOptions:=ConvertLazarusOptionsToFpmakeOptions(CustomOptions);
|
|
if ConsoleVerbosity>0 then
|
|
debugln('Hint: (lazarus) Writing fpmake.pp: CustomOptions (fpmake format): ',CustomOptions);
|
|
|
|
OtherOptions:=ConvertLazarusOptionsToFpmakeOptions(OtherOptions);
|
|
if ConsoleVerbosity>0 then
|
|
debugln('Hint: (lazarus) Writing fpmake.pp: OtherOptions (fpmake format): ',OtherOptions);
|
|
|
|
e:=LineEnding;
|
|
s:='';
|
|
s:=s+'{'+e;
|
|
s:=s+' File generated automatically by Lazarus Package Manager'+e;
|
|
if Assigned(FppkgInterface) then
|
|
s := s + ' Created with the Fppkgpackagemanager package installed'+e;
|
|
s:=s+''+e;
|
|
s:=s+' fpmake.pp for '+APackage.IDAsString+e;
|
|
s:=s+''+e;
|
|
s:=s+' This file was generated on '+DateToStr(Now)+''+e;
|
|
s:=s+'}'+e;
|
|
s:=s+''+e;
|
|
s:=s+'{$ifndef ALLPACKAGES} '+e;
|
|
s:=s+'{$mode objfpc}{$H+}'+e;
|
|
s:=s+'program fpmake;'+e;
|
|
s:=s+''+e;
|
|
s:=s+'uses fpmkunit;'+e;
|
|
s:=s+'{$endif ALLPACKAGES}'+e;
|
|
s:=s+''+e;
|
|
s:=s+'procedure add_'+APackage.Name+'(const ADirectory: string);'+e;
|
|
s:=s+''+e;
|
|
s:=s+'var'+e;
|
|
s:=s+' P : TPackage;'+e;
|
|
s:=s+' T : TTarget;'+e;
|
|
s:=s+' D : TDependency;'+e;
|
|
s:=s+''+e;
|
|
|
|
if Assigned(FppkgInterface) then
|
|
s := s + FppkgInterface.ConstructFpMakeInterfaceSection(APackage);
|
|
|
|
s:=s+'begin'+e;
|
|
s:=s+' with Installer do'+e;
|
|
s:=s+' begin'+e;
|
|
s:=s+' P:=AddPackage('''+lowercase(APackage.Name)+''');'+e;
|
|
|
|
s:=s+' P.Version:='''+PkgVersionToFPVersionString(APackage.Version)+''';'+e;
|
|
s:=s+''+e;
|
|
s:=s+' P.Directory:=ADirectory;'+e;
|
|
s:=s+''+e;
|
|
|
|
if APackage.Author<>'' then
|
|
s:=s+' P.Author:='+TextToString(APackage.Author)+';'+e;
|
|
if APackage.License<>'' then
|
|
s:=s+' P.License:='+TextToString(APackage.License)+';'+e;
|
|
if APackage.Description<>'' then
|
|
s:=s+' P.Description:='+TextToString(APackage.Description)+';'+e;
|
|
|
|
s:=s+''+e;
|
|
if APackage.PackageType in [lptDesignTime, lptRunAndDesignTime] then
|
|
s:=s+' P.Flags.Add(''LazarusDsgnPkg'');'+e+e;
|
|
|
|
ARequirement := APackage.FirstRequiredDependency;
|
|
while assigned(ARequirement) do
|
|
begin
|
|
s:=s+' D := P.Dependencies.Add('''+lowercase(ARequirement.PackageName)+''');'+e;
|
|
ARequirement := ARequirement.NextRequiresDependency;
|
|
end;
|
|
|
|
s := s + StringToFpmakeOptionGroup(' P.Options.Add',OtherOptions);
|
|
s := s + StringToFpmakeOptionGroup(' P.Options.Add',CustomOptions);
|
|
s := s + StringToFpmakeOptionGroup(' P.IncludePath.Add',IncPath);
|
|
s := s + StringToFpmakeOptionGroup(' P.UnitPath.Add', UnitPath);
|
|
|
|
if Assigned(FppkgInterface) then
|
|
s := s + FppkgInterface.ConstructFpMakeImplementationSection(APackage);
|
|
|
|
s:=s+' T:=P.Targets.AddUnit('''+MainSrcFile+''');'+e;
|
|
if Assigned(FppkgInterface) then
|
|
s := s + FppkgInterface.ConstructFpMakeDependenciesFileSection(APackage)
|
|
else
|
|
begin
|
|
for i := 0 to APackage.FileCount-1 do
|
|
if (APackage.Files[i].FileType=pftUnit) then
|
|
s:=s+' t.Dependencies.AddUnit('''+ExtractFileNameOnly(APackage.Files[i].Filename)+''');'+e;
|
|
|
|
s:=s+''+e;
|
|
|
|
for i := 0 to APackage.FileCount-1 do
|
|
if (APackage.Files[i].FileType=pftUnit) then
|
|
begin
|
|
if (pffAddToPkgUsesSection in APackage.Files[i].Flags) then
|
|
s:=s+' T:=P.Targets.AddUnit('''+CreateRelativePath(APackage.Files[i].Filename,APackage.Directory)+''');'+e
|
|
else
|
|
begin
|
|
s:=s+' P.Targets.AddImplicitUnit('''+CreateRelativePath(APackage.Files[i].Filename,APackage.Directory)+''');'+e;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
s:=s+''+e;
|
|
s:=s+' // copy the compiled file, so the IDE knows how the package was compiled'+e;
|
|
s:=s+' P.Sources.AddSrc('''+ExtractFileName(FPmakeCompiledFilename)+''');'+e;
|
|
s:=s+' P.InstallFiles.Add('''+ExtractFileName(FPmakeCompiledFilename)+''',AllOSes,''$(unitinstalldir)'');'+e;
|
|
|
|
s:=s+''+e;
|
|
s:=s+' end;'+e;
|
|
s:=s+'end;'+e;
|
|
|
|
s:=s+''+e;
|
|
s:=s+'{$ifndef ALLPACKAGES}'+e;
|
|
s:=s+'begin'+e;
|
|
s:=s+' add_'+APackage.Name+'('''');'+e;
|
|
s:=s+' Installer.Run;'+e;
|
|
s:=s+'end.'+e;
|
|
s:=s+'{$endif ALLPACKAGES}'+e;
|
|
|
|
CodeBuffer:=CodeToolBoss.LoadFile(FpmakeFPCFilename,true,true);
|
|
if CodeBuffer=nil then begin
|
|
CodeBuffer:=CodeToolBoss.CreateFile(FpmakeFPCFilename);
|
|
if CodeBuffer=nil then begin
|
|
if not DirectoryIsWritableCached(ExtractFilePath(FpmakeFPCFilename))
|
|
then begin
|
|
// the package source is read only => ignore
|
|
exit(mrOk);
|
|
end;
|
|
debugln(['Error: (lazarus) unable to create fpmake.pp file '+FpmakeFPCFilename]);
|
|
exit(mrCancel);
|
|
end;
|
|
end;
|
|
|
|
OriginalCode:=CodeToolBoss.ExtractCodeWithoutComments(CodeBuffer);
|
|
CodeBuffer.Source:=s;
|
|
if OriginalCode=CodeToolBoss.ExtractCodeWithoutComments(CodeBuffer)
|
|
then begin
|
|
// nothing important has changed in fpmake.pp => do not write to disk
|
|
Result:=mrOk;
|
|
exit;
|
|
end;
|
|
CodeBuffer.Source:=s;
|
|
|
|
//debugln('TPkgManager.DoWriteMakefile MakefileFPCFilename="',FpmakeFPCFilename,'"');
|
|
Result:=SaveCodeBufferToFile(CodeBuffer,FpmakeFPCFilename);
|
|
if Result<>mrOk then begin
|
|
if not DirectoryIsWritableCached(ExtractFilePath(FpmakeFPCFilename)) then
|
|
begin
|
|
// the package source is read only => skip silently
|
|
Result:=mrOk;
|
|
end;
|
|
exit;
|
|
end;
|
|
if ConsoleVerbosity>0 then
|
|
debugln(['Hint: (lazarus) wrote fpmake.pp: ',FpmakeFPCFilename]);
|
|
|
|
Result:=mrOk;
|
|
end;
|
|
|
|
function TLazPackageGraph.ParseBasePackages(Verbose: boolean): boolean;
|
|
var
|
|
LazDir, SrcFilename, Atom, PkgName: String;
|
|
Code: TCodeBuffer;
|
|
p, AtomStart: integer;
|
|
begin
|
|
Result:=false;
|
|
LazDir:=EnvironmentOptions.GetParsedLazarusDirectory;
|
|
if (LazDir='') or not FilenameIsAbsolute(LazDir) then
|
|
begin
|
|
if Verbose then
|
|
debugln(['Error: (lazarus) TLazPackageGraph.ParseBasePackages missing LazarusDir "',LazDir,'"']);
|
|
exit;
|
|
end;
|
|
SrcFilename:=AppendPathDelim(LazDir)+'ide'+PathDelim+'packages'+PathDelim+'idepackager'+PathDelim+'pkgsysbasepkgs.pas';
|
|
if not FileExistsCached(SrcFilename) then
|
|
begin
|
|
if Verbose then
|
|
debugln(['Error: (lazarus) TLazPackageGraph.ParseBasePackages file not found: "',SrcFilename,'"']);
|
|
exit;
|
|
end;
|
|
Code:=CodeToolBoss.LoadFile(SrcFilename,true,false);
|
|
if Code=nil then
|
|
begin
|
|
if Verbose then
|
|
debugln(['Error: (lazarus) TLazPackageGraph.ParseBasePackages failed to load "',SrcFilename,'"']);
|
|
exit;
|
|
end;
|
|
if (FSrcBasePackagesFilename=SrcFilename)
|
|
and (FSrcBasePackagesFileChangeStep=Code.FileChangeStep) then
|
|
exit(true); // cache valid
|
|
|
|
FSrcBasePackagesFilename:=SrcFilename;
|
|
FSrcBasePackagesFileChangeStep:=Code.FileChangeStep;
|
|
FSrcBasePackages.Clear;
|
|
|
|
if SearchCodeInSource(Code.Source,'LazarusIDEBasePkgNames:',1,p,false)<1 then
|
|
begin
|
|
if Verbose then
|
|
debugln(['Error: (lazarus) TLazPackageGraph.ParseBasePackages failed to find LazarusIDEBasePkgNames in "',SrcFilename,'"']);
|
|
exit;
|
|
end;
|
|
AtomStart:=p;
|
|
repeat
|
|
Atom:=ReadNextPascalAtom(Code.Source,p,AtomStart);
|
|
if (Atom='') or (Atom=')') then break;
|
|
if Atom[1]='''' then
|
|
begin
|
|
PkgName:=copy(Atom,2,length(Atom)-2);
|
|
if IsValidPkgName(PkgName) then
|
|
FSrcBasePackages.Add(PkgName);
|
|
end;
|
|
until false;
|
|
Result:=true;
|
|
end;
|
|
|
|
function TLazPackageGraph.SrcBasePackagesNeedLazbuild: string;
|
|
var
|
|
i: Integer;
|
|
PkgName, aFilename: String;
|
|
bp: TLazarusIDEBasePkg;
|
|
Pkg: TLazPackage;
|
|
begin
|
|
Result:='';
|
|
if not ParseBasePackages(true) then
|
|
exit('Unable to parse base package list.');
|
|
|
|
// check if all source base packages will be installed
|
|
for i:=0 to FSrcBasePackages.Count-1 do
|
|
begin
|
|
PkgName:=FSrcBasePackages[i];
|
|
if IsCompiledInBasePackage(PkgName) then
|
|
continue;
|
|
// new base package
|
|
if FindDependencyByNameInList(FirstInstallDependency,pddRequires,PkgName)<>nil
|
|
then
|
|
exit; // it will be installed anyway -> ok
|
|
// the sources need a base package, that this IDE will not install
|
|
// -> better use lazbuild for building
|
|
exit('Sources need a new base package "'+PkgName+'"');
|
|
end;
|
|
|
|
// check if all compiled-in base packages are also source base packages
|
|
for bp in TLazarusIDEBasePkg do
|
|
begin
|
|
PkgName:=LazarusIDEBasePkgNames[bp];
|
|
if FSrcBasePackages.IndexOf(PkgName)>=0 then continue;
|
|
// sources do not listen this as base package
|
|
Pkg:=FindPackageWithName(PkgName,nil);
|
|
if Pkg=nil then continue;
|
|
if Pkg.IsVirtual then
|
|
exit('Sources do not use "'+PkgName+'" as base package.'); // avoid IDE package check errors and use lazbuild
|
|
aFilename:=Pkg.GetResolvedFilename(true);
|
|
if aFilename='' then
|
|
exit('Sources do not use "'+PkgName+'" as base package.'); // avoid IDE package check errors and use lazbuild
|
|
if not FileExistsCached(aFilename) then
|
|
exit('Sources do not use "'+PkgName+'" as base package.'); // avoid IDE package check errors and use lazbuild
|
|
end;
|
|
end;
|
|
|
|
function TLazPackageGraph.PreparePackageOutputDirectory(APackage: TLazPackage;
|
|
CleanUp: boolean): TModalResult;
|
|
var
|
|
OutputDir, NewOutputDir: String;
|
|
StateFile: String;
|
|
PkgSrcDir: String;
|
|
i: Integer;
|
|
CurFile: TPkgFile;
|
|
OutputFileName: String;
|
|
DeleteAllFilesInOutputDir: Boolean;
|
|
DirCache: TCTDirectoryCache;
|
|
CleanFiles: TStrings;
|
|
begin
|
|
// get output directory
|
|
OutputDir:=APackage.GetOutputDirectory;
|
|
//debugln(['TLazPackageGraph.PreparePackageOutputDirectory OutputDir="',OutputDir,'"']);
|
|
|
|
// Note: The OutputDirectoryOverride is set prior in CheckIfPackageNeedsCompilation
|
|
|
|
DeleteAllFilesInOutputDir:=false;
|
|
if not OutputDirectoryIsWritable(APackage,OutputDir,false) then
|
|
begin
|
|
// the output directory is not writable
|
|
debugln(['Error: (lazarus) [TLazPackageGraph.PreparePackageOutputDirectory] failed to create writable directory (',APackage.IDAsString,'): ',OutputDir]);
|
|
Result:=mrCancel;
|
|
end else if APackage.CompilerOptions.ParsedOpts.OutputDirectoryOverride<>''
|
|
then
|
|
// package is already using the fallback directory
|
|
DeleteAllFilesInOutputDir:=true
|
|
else if CleanUp then begin
|
|
// package is not using the fallback directory
|
|
|
|
// delete fallback if it exists
|
|
NewOutputDir:=GetFallbackOutputDir(APackage);
|
|
if DirPathExistsCached(NewOutputDir) then
|
|
DeleteDirectory(NewOutputDir,false);
|
|
|
|
// check if the output directory contains sources
|
|
DeleteAllFilesInOutputDir:=APackage.HasSeparateOutputDirectory;
|
|
end;
|
|
//debugln(['TLazPackageGraph.PreparePackageOutputDirectory ',APackage.Name,' DeleteAllFilesInOutputDir=',DeleteAllFilesInOutputDir]);
|
|
|
|
StateFile:=APackage.GetStateFilename;
|
|
PkgSrcDir:=ExtractFilePath(APackage.GetSrcFilename);
|
|
|
|
// delete old Compile State file
|
|
if FileExistsUTF8(StateFile) and not DeleteFileUTF8(StateFile) then begin
|
|
Result:=LazMessageWorker(lisPkgMangUnableToDeleteFilename,
|
|
Format(lisPkgMangUnableToDeleteOldStateFileForPackage,
|
|
[StateFile, LineEnding, APackage.IDAsString]),
|
|
mtError,[mbCancel,mbAbort]);
|
|
exit;
|
|
end;
|
|
InvalidateFileStateCache(StateFile);
|
|
InvalidateStateFile(APackage);
|
|
|
|
// create the package src directory
|
|
if not ForceDirectoriesUTF8(PkgSrcDir) then begin
|
|
Result:=LazMessageWorker(lisPkgMangUnableToCreateDirectory,
|
|
Format(lisPkgMangUnableToCreatePackageSourceDirectoryForPackage,
|
|
[PkgSrcDir, LineEnding, APackage.IDAsString]),
|
|
mtError,[mbCancel,mbAbort]);
|
|
exit;
|
|
end;
|
|
|
|
// clean up if wanted
|
|
if CleanUp then begin
|
|
if DeleteAllFilesInOutputDir then begin
|
|
// delete all files in output directory
|
|
DirCache:=CodeToolBoss.DirectoryCachePool.GetCache(OutputDir,true,false);
|
|
if DirCache<>nil then begin
|
|
CleanFiles:=TStringList.Create;
|
|
try
|
|
DirCache.GetFiles(CleanFiles,false);
|
|
for i:=0 to CleanFiles.Count-1 do begin
|
|
OutputFileName:=AppendPathDelim(OutputDir)+CleanFiles[i];
|
|
if ConsoleVerbosity>1 then
|
|
debugln(['Hint: (lazarus) cleaning up package output directory of '+APackage.IDAsString+': '+OutputFileName]);
|
|
Result:=DeleteFileInteractive(OutputFileName,[mbIgnore,mbAbort]);
|
|
if Result in [mrCancel,mrAbort] then exit;
|
|
end;
|
|
finally
|
|
CleanFiles.Free;
|
|
end;
|
|
end;
|
|
end else begin
|
|
// delete .ppu/.o file of each registered unit
|
|
for i:=0 to APackage.FileCount-1 do begin
|
|
CurFile:=APackage.Files[i];
|
|
if not (CurFile.FileType in PkgFileUnitTypes) then continue;
|
|
OutputFileName:=AppendPathDelim(OutputDir)+CurFile.Unit_Name+'.ppu';
|
|
if ConsoleVerbosity>1 then
|
|
debugln(['Hint: (lazarus) cleaning up package output directory of '+APackage.IDAsString+': '+OutputFileName]);
|
|
Result:=DeleteFileInteractive(OutputFileName,[mbIgnore,mbAbort]);
|
|
if Result in [mrCancel,mrAbort] then exit;
|
|
OutputFileName:=ChangeFileExt(OutputFileName,'.o');
|
|
if ConsoleVerbosity>1 then
|
|
debugln(['Hint: (lazarus) cleaning up package output directory of '+APackage.IDAsString+': '+OutputFileName]);
|
|
Result:=DeleteFileInteractive(OutputFileName,[mbIgnore,mbAbort]);
|
|
if Result in [mrCancel,mrAbort] then exit;
|
|
end;
|
|
end;
|
|
InvalidateFileStateCache;
|
|
end;
|
|
|
|
Result:=mrOk;
|
|
end;
|
|
|
|
function TLazPackageGraph.GetFallbackOutputDir(APackage: TLazPackage): string;
|
|
var
|
|
Dir: String;
|
|
begin
|
|
// use the default output directory, if it is relative
|
|
// (this way the fallback creates the same amount of target directories)
|
|
Dir:=APackage.CompilerOptions.ParsedOpts.Values[pcosOutputDir].UnparsedValue;
|
|
Dir:=APackage.SubstitutePkgMacros(Dir,false);
|
|
if FilenameIsAbsolute(Dir) then begin
|
|
// it is not relative => create a default one
|
|
Dir:='$(TargetOS)-$(TargetCPU)';
|
|
end;
|
|
Dir:='$(FallbackOutputRoot)'+PathDelim+APackage.Name+PathDelim+Dir;
|
|
GlobalMacroList.SubstituteStr(Dir);
|
|
Dir:=TrimFilename(Dir);
|
|
if ConsoleVerbosity>=0 then
|
|
debugln(['Hint: (lazarus) Fallback output directory of ',APackage.Name,': ',Dir]);
|
|
Result:=Dir;
|
|
end;
|
|
|
|
function TLazPackageGraph.CheckAmbiguousPackageUnits(APackage: TLazPackage
|
|
): TModalResult;
|
|
var
|
|
i: Integer;
|
|
CurFile: TPkgFile;
|
|
CurUnitName: String;
|
|
SrcDirs: String;
|
|
PkgDir: String;
|
|
PkgOutputDir: String;
|
|
YesToAll: Boolean;
|
|
|
|
function CheckFile(const ShortFilename: string): TModalResult;
|
|
var
|
|
AmbiguousFilename: String;
|
|
SearchFlags: TSPSearchFileFlags;
|
|
begin
|
|
Result:=mrOk;
|
|
SearchFlags:=[];
|
|
if CompareFilenames(PkgDir,PkgOutputDir)=0 then
|
|
Include(SearchFlags,TSPSearchFileFlag.DontSearchInBasePath);
|
|
repeat
|
|
AmbiguousFilename:=SearchFileInSearchPath(ShortFilename,PkgDir,SrcDirs,SearchFlags);
|
|
if (AmbiguousFilename='') then exit;
|
|
if not YesToAll then
|
|
Result:=LazMessageWorker(lisAmbiguousUnitFound,
|
|
Format(lisTheFileWasFoundInOneOfTheSourceDirectoriesOfThePac,
|
|
[AmbiguousFilename, LineEnding, APackage.IDAsString, LineEnding+LineEnding]),
|
|
mtWarning,[mbYes,mbYesToAll,mbNo,mbAbort])
|
|
else
|
|
Result:=mrYesToAll;
|
|
if Result=mrNo then
|
|
Result:=mrOk;
|
|
if Result in [mrYes,mrYesToAll] then begin
|
|
YesToAll:=Result=mrYesToAll;
|
|
if (not DeleteFileUTF8(AmbiguousFilename))
|
|
and (LazMessageWorker(lisPkgMangDeleteFailed,
|
|
Format(lisDeletingOfFileFailed, [AmbiguousFilename]),
|
|
mtError, [mbIgnore, mbCancel])<>mrIgnore)
|
|
then
|
|
exit(mrCancel);
|
|
Result:=mrOk;
|
|
end else
|
|
break;
|
|
until false;
|
|
end;
|
|
|
|
begin
|
|
Result:=mrOk;
|
|
case EnvironmentOptions.AmbiguousFileAction of
|
|
afaIgnore: exit;
|
|
afaAutoDelete: YesToAll:=true;
|
|
else YesToAll:=false;
|
|
end;
|
|
// search in every source directory for compiled versions of the units
|
|
// A source directory is a directory with a used unit and it is not the output
|
|
// directory
|
|
SrcDirs:=APackage.GetSourceDirs(true,true);
|
|
PkgOutputDir:=AppendPathDelim(APackage.GetOutputDirectory);
|
|
SrcDirs:=RemoveSearchPaths(SrcDirs,PkgOutputDir);
|
|
if SrcDirs='' then exit;
|
|
PkgDir:=AppendPathDelim(APackage.Directory);
|
|
for i:=0 to APackage.FileCount-1 do begin
|
|
CurFile:=APackage.Files[i];
|
|
if CurFile.FileType<>pftUnit then continue;
|
|
CurUnitName:=lowercase(CurFile.Unit_Name);
|
|
if CurUnitName='' then continue;
|
|
Result:=CheckFile(CurUnitName+'.ppu');
|
|
if Result<>mrOk then exit;
|
|
Result:=CheckFile(CurUnitName+'.ppl');
|
|
if Result<>mrOk then exit;
|
|
end;
|
|
Result:=mrOk;
|
|
end;
|
|
|
|
function TLazPackageGraph.SavePackageMainSource(APackage: TLazPackage;
|
|
Flags: TPkgCompileFlags; ShowAbort: boolean): TModalResult;
|
|
var
|
|
PkgUnitName, SrcFilename, UsedUnits, Src: String;
|
|
i: Integer;
|
|
e: String;
|
|
CurFile: TPkgFile;
|
|
CodeBuffer: TCodeBuffer;
|
|
CurUnitName: String;
|
|
RegistrationCode: String;
|
|
HeaderSrc: String;
|
|
OldShortenSrc: String;
|
|
NeedsRegisterProcCall: boolean;
|
|
CurSrcUnitName: String;
|
|
NewShortenSrc: String;
|
|
BeautifyCodeOptions: TBeautifyCodeOptions;
|
|
AddedUnitNames: TStringToStringTree;
|
|
AFilename: String;
|
|
|
|
procedure UseUnit(AnUnitName: string);
|
|
begin
|
|
if AddedUnitNames.Contains(AnUnitName) then exit;
|
|
if CompareDottedIdentifiers(PChar(AnUnitName),PChar(PkgUnitName))=0 then exit;
|
|
AddedUnitNames.Add(AnUnitName,'');
|
|
if UsedUnits<>'' then
|
|
UsedUnits:=UsedUnits+', ';
|
|
UsedUnits:=UsedUnits+AnUnitName;
|
|
end;
|
|
|
|
begin
|
|
{$IFDEF VerbosePkgCompile}
|
|
debugln('TLazPackageGraph.SavePackageMainSource A');
|
|
{$ENDIF}
|
|
SrcFilename:=APackage.GetSrcFilename;
|
|
|
|
// delete ambiguous files
|
|
Result:=DeleteAmbiguousFiles(SrcFilename);
|
|
if Result=mrAbort then begin
|
|
DebugLn('Error: (lazarus) [TLazPackageGraph.SavePackageMainSource] DoDeleteAmbiguousFiles failed');
|
|
exit;
|
|
end;
|
|
|
|
// get unit name
|
|
PkgUnitName := ExtractFileNameOnly(SrcFilename);
|
|
if CompareDottedIdentifiers(PChar(APackage.Name), PChar(PkgUnitName))=0 then
|
|
PkgUnitName := APackage.Name;
|
|
|
|
// collect unitnames
|
|
e:=LineEnding;
|
|
UsedUnits:='';
|
|
RegistrationCode:='';
|
|
AddedUnitNames:=TStringToStringTree.Create(false);
|
|
try
|
|
for i:=0 to APackage.FileCount-1 do begin
|
|
CurFile:=APackage.Files[i];
|
|
if CurFile.FileType=pftMainUnit then continue;
|
|
// update unitname
|
|
AFilename:=CurFile.GetFullFilename;
|
|
if FilenameIsPascalUnit(AFilename)
|
|
and (CurFile.FileType in PkgFileUnitTypes) then begin
|
|
NeedsRegisterProcCall:=CurFile.HasRegisterProc
|
|
and (APackage.PackageType in [lptDesignTime,lptRunAndDesignTime]);
|
|
|
|
AFilename:=CodeToolBoss.DirectoryCachePool.FindDiskFilename(AFilename,true);
|
|
CurUnitName:=ExtractFileNameOnly(AFilename);
|
|
|
|
if not (NeedsRegisterProcCall or CurFile.AddToUsesPkgSection) then
|
|
continue;
|
|
|
|
if CurUnitName=lowercase(CurUnitName) then begin
|
|
// the filename is all lowercase, so we can use the nicer unitname from
|
|
// the source.
|
|
|
|
CodeBuffer:=CodeToolBoss.LoadFile(AFilename,false,false);
|
|
if CodeBuffer<>nil then begin
|
|
// if the unit is edited, the unitname is probably already cached
|
|
CurSrcUnitName:=CodeToolBoss.GetCachedSourceName(CodeBuffer);
|
|
// if not then parse it
|
|
if SysUtils.CompareText(CurSrcUnitName,CurUnitName)<>0 then
|
|
CurSrcUnitName:=CodeToolBoss.GetSourceName(CodeBuffer,false);
|
|
// if it makes sense, update unitname
|
|
if SysUtils.CompareText(CurSrcUnitName,CurFile.Unit_Name)=0 then
|
|
CurFile.Unit_Name:=CurSrcUnitName;
|
|
end;
|
|
if SysUtils.CompareText(CurUnitName,CurFile.Unit_Name)=0 then
|
|
CurUnitName:=CurFile.Unit_Name
|
|
else
|
|
CurFile.Unit_Name:=CurUnitName;
|
|
end;
|
|
|
|
if (CurUnitName='') or (not IsValidUnitName(CurUnitName)) then begin
|
|
AddMessage(mluError,Format('invalid unit name in package %s',[APackage.IDAsString]),CurFile.Filename);
|
|
continue;
|
|
end;
|
|
|
|
UseUnit(CurUnitName);
|
|
if NeedsRegisterProcCall then begin
|
|
RegistrationCode:=RegistrationCode+
|
|
' RegisterUnit('''+CurUnitName+''',@'+CurUnitName+'.Register);'+e;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
// append registration code only for design time packages
|
|
if (APackage.PackageType in [lptDesignTime,lptRunAndDesignTime]) then begin
|
|
RegistrationCode:=
|
|
'procedure Register;'+e
|
|
+'begin'+e
|
|
+RegistrationCode
|
|
+'end;'+e
|
|
+e
|
|
+'initialization'+e
|
|
+' RegisterPackage('''+APackage.Name+''',@Register);'
|
|
+e;
|
|
UseUnit('LazarusPackageIntf');
|
|
end;
|
|
|
|
finally
|
|
AddedUnitNames.Free;
|
|
end;
|
|
// create source
|
|
BeautifyCodeOptions:=CodeToolBoss.SourceChangeCache.BeautifyCodeOptions;
|
|
// do not translate to avoid svn updates
|
|
HeaderSrc:= '{ This file was automatically created by Lazarus. Do not edit!'+e
|
|
+' This source is only used to compile and install the package.'+e
|
|
+' }'+e+e;
|
|
// leave the unit case the same as the package name (e.g: package name LazReport, unit name lazreport)
|
|
Src:='unit '+ PkgUnitName +';'+e
|
|
+e
|
|
+'{$warn 5023 off : no warning about unused units}'+e
|
|
+'interface'+e
|
|
+e;
|
|
Src:=BeautifyCodeOptions.BeautifyStatement(Src,0);
|
|
Src:=HeaderSrc+Src;
|
|
if UsedUnits<>'' then
|
|
Src:=Src
|
|
+'uses'+e
|
|
+BreakString(GetIndentStr(BeautifyCodeOptions.Indent)+UsedUnits+';',
|
|
BeautifyCodeOptions.LineLength,BeautifyCodeOptions.Indent)+e
|
|
+e;
|
|
Src:=Src+BeautifyCodeOptions.BeautifyStatement(
|
|
'implementation'+e
|
|
+e
|
|
+RegistrationCode
|
|
+'end.'+e,0);
|
|
|
|
// check if old code is already uptodate
|
|
Result:=LoadCodeBuffer(CodeBuffer,SrcFilename,[lbfQuiet,lbfCheckIfText,
|
|
lbfUpdateFromDisk,lbfCreateClearOnError],ShowAbort);
|
|
if Result<>mrOk then begin
|
|
DebugLn('Error: (lazarus) [TLazPackageGraph.SavePackageMainSource] LoadCodeBuffer ',SrcFilename,' failed');
|
|
exit;
|
|
end;
|
|
// ignore comments
|
|
OldShortenSrc:=CodeToolBoss.ExtractCodeWithoutComments(CodeBuffer,true);
|
|
NewShortenSrc:=CleanCodeFromComments(Src,
|
|
CodeToolBoss.GetNestedCommentsFlagForFile(CodeBuffer.Filename),true);
|
|
// ignore case and spaces
|
|
if CompareTextIgnoringSpace(OldShortenSrc,NewShortenSrc,false)=0 then begin
|
|
Result:=mrOk;
|
|
exit;
|
|
end;
|
|
if OldShortenSrc<>NewShortenSrc then begin
|
|
{$IFDEF VerbosePkgCompile}
|
|
DebugLn('TLazPackageGraph.SavePackageMainSource Src changed ',dbgs(length(OldShortenSrc)),' ',dbgs(length(NewShortenSrc)));
|
|
{$ENDIF}
|
|
end;
|
|
|
|
// save source
|
|
Result:=SaveLazStringToFile(SrcFilename,Src,[],lisPkgMangpackageMainSourceFile);
|
|
if Result<>mrOk then begin
|
|
DebugLn('Error: (lazarus) [TLazPackageGraph.SavePackageMainSource] SaveLazStringToFile ',SrcFilename,' failed');
|
|
exit;
|
|
end;
|
|
|
|
Result:=mrOk;
|
|
end;
|
|
|
|
function TLazPackageGraph.GetBrokenDependenciesWhenChangingPkgID(
|
|
APackage: TLazPackage; const NewName: string; NewVersion: TPkgVersion
|
|
): TFPList;
|
|
var
|
|
Dependency: TPkgDependency;
|
|
begin
|
|
Result:=TFPList.Create;
|
|
// find all dependencies, that will become incompatible
|
|
Dependency:=APackage.FirstUsedByDependency;
|
|
while Dependency<>nil do begin
|
|
if not Dependency.IsCompatible(NewName,NewVersion) then
|
|
Result.Add(Dependency);
|
|
Dependency:=Dependency.NextUsedByDependency;
|
|
end;
|
|
end;
|
|
|
|
procedure TLazPackageGraph.GetPackagesChangedOnDisk(out
|
|
ListOfPackages: TStringList; IgnoreModifiedFlag: boolean);
|
|
// if package source is changed in IDE (codetools)
|
|
// then changes on disk are ignored
|
|
var
|
|
APackage: TLazPackage;
|
|
i: Integer;
|
|
NewFilename: String;
|
|
HaveUpdatedGlobalPkgLinks: Boolean;
|
|
|
|
procedure UpdateGlobalLinks;
|
|
begin
|
|
if not HaveUpdatedGlobalPkgLinks then
|
|
begin
|
|
LazPackageLinks.UpdateGlobalLinks;
|
|
HaveUpdatedGlobalPkgLinks:=true;
|
|
end;
|
|
end;
|
|
|
|
begin
|
|
ListOfPackages:=nil;
|
|
MarkNeededPackages;
|
|
HaveUpdatedGlobalPkgLinks:=false;
|
|
for i:=FItems.Count-1 downto 0 do begin
|
|
APackage:=TLazPackage(FItems[i]);
|
|
if (not (lpfNeeded in APackage.Flags))
|
|
or APackage.Modified
|
|
or APackage.IsVirtual
|
|
then
|
|
continue;
|
|
NewFilename:=APackage.Filename;
|
|
if FileExistsCached(APackage.Filename) then begin
|
|
if (APackage.LPKSource<>nil)
|
|
and (not APackage.LPKSource.FileNeedsUpdate(IgnoreModifiedFlag)) then
|
|
continue;
|
|
// a lpk has changed, this might include dependencies => reload lpl files
|
|
UpdateGlobalLinks;
|
|
end else begin
|
|
// lpk has vanished -> search alternative => reload lpl files
|
|
UpdateGlobalLinks;
|
|
NewFilename:=PackageGraph.FindAlternativeLPK(APackage);
|
|
if (NewFilename='') and (APackage.Missing or (APackage.LPKSource=nil)) then
|
|
continue; // no lpk found again => do not show again
|
|
end;
|
|
if ListOfPackages=nil then
|
|
ListOfPackages:=TStringListUTF8Fast.Create;
|
|
ListOfPackages.AddObject(NewFilename,APackage);
|
|
end;
|
|
end;
|
|
|
|
procedure TLazPackageGraph.IncChangeStamp;
|
|
begin
|
|
{$push}{$R-} // range check off
|
|
if FUpdateLock = 0 then
|
|
Inc(FChangeStamp)
|
|
else
|
|
Inc(FLockedChangeStamp)
|
|
{$pop}
|
|
end;
|
|
|
|
procedure TLazPackageGraph.SortDependencyListTopologicallyOld(
|
|
var FirstDependency: TPkgDependency; TopLevelFirst: boolean);
|
|
// Sort dependency list topologically.
|
|
// If TopLevelFirst is true then packages that need others come first
|
|
var
|
|
Dependency: TPkgDependency;
|
|
BucketStarts: PInteger;
|
|
MaxLvl: Integer;
|
|
BucketCount: Integer;
|
|
DependencyCount: Integer;
|
|
Dependencies: PPkgDependency;
|
|
i: Integer;
|
|
j: Integer;
|
|
CurLvl: LongInt;
|
|
List: TFPList;
|
|
begin
|
|
GetAllRequiredPackages(nil,FirstDependency,List);
|
|
List.Free;
|
|
|
|
// Bucket sort dependencies
|
|
MaxLvl:=0;
|
|
Dependency:=FirstDependency;
|
|
DependencyCount:=0;
|
|
while Dependency<>nil do begin
|
|
if Dependency.RequiredPackage<>nil then begin
|
|
if MaxLvl<Dependency.RequiredPackage.TopologicalLevel then
|
|
MaxLvl:=Dependency.RequiredPackage.TopologicalLevel;
|
|
end;
|
|
Dependency:=Dependency.NextRequiresDependency;
|
|
inc(DependencyCount);
|
|
end;
|
|
if (MaxLvl=0) or (DependencyCount<=1) then exit;
|
|
|
|
//debugln('TLazPackageGraph.SortDependencyListTopologically A MaxLvl=',dbgs(MaxLvl),' ',dbgs(DependencyCount));
|
|
// compute BucketStarts
|
|
BucketCount:=MaxLvl+1;
|
|
GetMem(BucketStarts,SizeOf(Integer)*BucketCount);
|
|
FillChar(BucketStarts^,SizeOf(Integer)*BucketCount,0);
|
|
Dependency:=FirstDependency;
|
|
while Dependency<>nil do begin
|
|
if Dependency.RequiredPackage<>nil then
|
|
CurLvl:=Dependency.RequiredPackage.TopologicalLevel
|
|
else
|
|
CurLvl:=0;
|
|
if CurLvl+1<BucketCount then
|
|
inc(BucketStarts[CurLvl+1]);
|
|
Dependency:=Dependency.NextRequiresDependency;
|
|
end;
|
|
for i:=2 to MaxLvl do
|
|
BucketStarts[i]:=BucketStarts[i]+BucketStarts[i-1];
|
|
BucketStarts[0]:=0;
|
|
|
|
// put Dependencies into buckets
|
|
GetMem(Dependencies,SizeOf(Pointer)*DependencyCount);
|
|
FillChar(Dependencies^,SizeOf(Pointer)*DependencyCount,0);
|
|
Dependency:=FirstDependency;
|
|
while Dependency<>nil do begin
|
|
if Dependency.RequiredPackage<>nil then
|
|
CurLvl:=Dependency.RequiredPackage.TopologicalLevel
|
|
else
|
|
CurLvl:=0;
|
|
if Dependencies[BucketStarts[CurLvl]]<>nil then
|
|
RaiseGDBException('');
|
|
Dependencies[BucketStarts[CurLvl]]:=Dependency;
|
|
inc(BucketStarts[CurLvl]);
|
|
Dependency:=Dependency.NextRequiresDependency;
|
|
end;
|
|
|
|
// optional: reverse order
|
|
if TopLevelFirst then begin
|
|
i:=0;
|
|
j:=DependencyCount-1;
|
|
while (i<j) do begin
|
|
Dependency:=Dependencies[i];
|
|
Dependencies[i]:=Dependencies[j];
|
|
Dependencies[j]:=Dependency;
|
|
inc(i);
|
|
dec(j);
|
|
end;
|
|
end;
|
|
|
|
// commit order
|
|
FirstDependency:=Dependencies[0];
|
|
for i:=0 to DependencyCount-1 do begin
|
|
Dependency:=Dependencies[i];
|
|
//debugln('TLazPackageGraph.SortDependencyListTopologically A ',Dependency.AsString);
|
|
if i=0 then
|
|
Dependency.PrevDependency[pddRequires]:=nil
|
|
else
|
|
Dependency.PrevDependency[pddRequires]:=Dependencies[i-1];
|
|
if i=DependencyCount-1 then
|
|
Dependency.NextDependency[pddRequires]:=nil
|
|
else
|
|
Dependency.NextDependency[pddRequires]:=Dependencies[i+1];
|
|
end;
|
|
|
|
// clean up
|
|
FreeMem(BucketStarts);
|
|
FreeMem(Dependencies);
|
|
end;
|
|
|
|
function TLazPackageGraph.CheckIfPackageCanBeClosed(APackage: TLazPackage
|
|
): boolean;
|
|
begin
|
|
MarkNeededPackages;
|
|
Result:=lpfNeeded in APackage.Flags;
|
|
end;
|
|
|
|
function TLazPackageGraph.PackageIsNeeded(APackage: TLazPackage): boolean;
|
|
// check if package is currently in use (installed, autoinstall, editor open,
|
|
// or used by a project)
|
|
// !!! it does not check if any needed package needs this package
|
|
var
|
|
ADependency: TPkgDependency;
|
|
begin
|
|
Result:=true;
|
|
// check if package is open, installed or will be installed
|
|
if (APackage.Installed<>pitNope) or (APackage.AutoInstall<>pitNope)
|
|
or APackage.Modified
|
|
or (APackage.Editor<>nil)
|
|
or (APackage.HoldPackageCount>0) then
|
|
begin
|
|
exit;
|
|
end;
|
|
// check if used by project
|
|
ADependency:=APackage.FirstUsedByDependency;
|
|
while ADependency<>nil do begin
|
|
if ADependency.Owner is TLazProject then
|
|
exit;
|
|
ADependency:=ADependency.NextUsedByDependency;
|
|
end;
|
|
Result:=false;
|
|
end;
|
|
|
|
function TLazPackageGraph.PackageCanBeReplaced(
|
|
OldPackage, NewPackage: TLazPackage): boolean;
|
|
begin
|
|
if SysUtils.CompareText(OldPackage.Name,NewPackage.Name)<>0 then
|
|
RaiseGDBException('TLazPackageGraph.PackageCanBeReplaced');
|
|
|
|
Result:=true;
|
|
end;
|
|
|
|
procedure TLazPackageGraph.RegisterStaticPackage(APackage: TLazPackage;
|
|
RegisterProc: TRegisterProc);
|
|
var
|
|
PkgList: TFPList;
|
|
i: Integer;
|
|
Pkg: TLazPackage;
|
|
begin
|
|
if AbortRegistration then exit;
|
|
//DebugLn(['TLazPackageGraph.RegisterStaticPackage ',APackage.IDAsString]);
|
|
|
|
// translate (load resourcestrings) package and dependencies
|
|
if Assigned(OnTranslatePackage) then
|
|
begin
|
|
PkgList:=nil;
|
|
try
|
|
PackageGraph.GetAllRequiredPackages(APackage,APackage.FirstRequiredDependency,PkgList,[]);
|
|
if PkgList<>nil then
|
|
begin
|
|
for i:=0 to PkgList.Count-1 do
|
|
begin
|
|
Pkg:=TLazPackage(PkgList[i]);
|
|
if (Pkg.Translated='') and (Pkg.POOutputDirectory<>'') then
|
|
OnTranslatePackage(Pkg);
|
|
end;
|
|
end;
|
|
finally
|
|
PkgList.Free;
|
|
end;
|
|
if (APackage.Translated='') and (APackage.POOutputDirectory<>'') then
|
|
OnTranslatePackage(APackage);
|
|
end;
|
|
|
|
RegistrationPackage:=APackage;
|
|
CallRegisterProc(RegisterProc);
|
|
APackage.Registered:=true;
|
|
RegistrationPackage:=nil;
|
|
end;
|
|
|
|
procedure TLazPackageGraph.CallRegisterProc(RegisterProc: TRegisterProc);
|
|
begin
|
|
if AbortRegistration then exit;
|
|
|
|
// check registration procedure
|
|
if RegisterProc=nil then begin
|
|
RegistrationError(lisPkgSysRegisterProcedureIsNil);
|
|
exit;
|
|
end;
|
|
{$IFNDEF StopOnRegError}
|
|
try
|
|
{$ENDIF}
|
|
// call the registration procedure
|
|
RegisterProc();
|
|
{$IFNDEF StopOnRegError}
|
|
except
|
|
on E: Exception do begin
|
|
RegistrationError(E.Message);
|
|
end;
|
|
end;
|
|
{$ENDIF}
|
|
end;
|
|
|
|
procedure TLazPackageGraph.AddDependencyToPackage(APackage: TLazPackage;
|
|
Dependency: TPkgDependency);
|
|
begin
|
|
BeginUpdate(true);
|
|
APackage.AddRequiredDependency(Dependency);
|
|
Dependency.LoadPackageResult:=lprUndefined;
|
|
OpenDependency(Dependency,false);
|
|
EndUpdate;
|
|
end;
|
|
|
|
procedure TLazPackageGraph.AddDependencyToPackage(APackage,
|
|
RequiredPackage: TLazPackage);
|
|
var
|
|
NewDependency: TPkgDependency;
|
|
begin
|
|
NewDependency:=TPkgDependency.Create;
|
|
NewDependency.DependencyType:=pdtLazarus;
|
|
NewDependency.PackageName:=RequiredPackage.Name;
|
|
AddDependencyToPackage(APackage,NewDependency);
|
|
end;
|
|
|
|
procedure TLazPackageGraph.RemoveDependencyFromPackage(APackage: TLazPackage;
|
|
Dependency: TPkgDependency; AddToRemovedList: boolean);
|
|
begin
|
|
BeginUpdate(true);
|
|
if AddToRemovedList then
|
|
APackage.RemoveRequiredDependency(Dependency)
|
|
else
|
|
APackage.DeleteRequiredDependency(Dependency);
|
|
IncreaseBuildMacroChangeStamp;
|
|
EndUpdate;
|
|
end;
|
|
|
|
function TLazPackageGraph.OpenDependency(Dependency: TPkgDependency;
|
|
ShowAbort: boolean; IgnorePackage: TLazPackage): TLoadPackageResult;
|
|
|
|
procedure OpenFile(AFilename: string);
|
|
var
|
|
PkgLink: TPackageLink;
|
|
begin
|
|
PkgLink:=LazPackageLinks.AddUserLink(AFilename, Dependency.PackageName);
|
|
if (PkgLink<>nil) then begin
|
|
PkgLink.Reference;
|
|
try
|
|
if OpenDependencyWithPackageLink(Dependency,PkgLink,false)<>mrOk then
|
|
LazPackageLinks.RemoveUserLink(PkgLink);
|
|
finally
|
|
PkgLink.Release;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
var
|
|
ANode: TAVLTreeNode;
|
|
CurDir: String;
|
|
AFilename: String;
|
|
MsgResult: TModalResult;
|
|
APackage: TLazPackage;
|
|
PreferredFilename: string;
|
|
PkgLink: TPackageLink;
|
|
IgnoreFiles: TFilenameToStringTree;
|
|
i: Integer;
|
|
begin
|
|
if Dependency.LoadPackageResult=lprUndefined then begin
|
|
if pvPkgSearch in Verbosity then
|
|
debugln('Info: (lazarus) Open dependency '+Dependency.AsString(true,true)+' ...');
|
|
//debugln(['TLazPackageGraph.OpenDependency ',Dependency.PackageName,' ',Dependency.DefaultFilename,' Prefer=',Dependency.PreferDefaultFilename]);
|
|
BeginUpdate(false);
|
|
// search compatible package in opened packages
|
|
ANode:=FindNodeOfDependency(Dependency,fpfSearchEverywhere);
|
|
if (ANode<>nil) then begin
|
|
// there is already a loaded package that fits name and version
|
|
APackage:=TLazPackage(ANode.Data);
|
|
Dependency.RequiredPackage:=APackage;
|
|
Dependency.LoadPackageResult:=lprSuccess;
|
|
if pvPkgSearch in Verbosity then
|
|
debugln(['Info: (lazarus) Open dependency ['+Dependency.PackageName+']: Success: was already loaded']);
|
|
end;
|
|
// load preferred package
|
|
if (Dependency.DefaultFilename<>'') and Dependency.PreferDefaultFilename
|
|
then begin
|
|
PreferredFilename:=Dependency.FindDefaultFilename;
|
|
//debugln(['TLazPackageGraph.OpenDependency checking preferred Prefer=',PreferredFilename]);
|
|
if (PreferredFilename<>'')
|
|
and ((Dependency.RequiredPackage=nil)
|
|
or ((Dependency.RequiredPackage.FindUsedByDepPrefer(Dependency)=nil)
|
|
and (CompareFilenames(PreferredFilename,Dependency.RequiredPackage.Filename)<>0)))
|
|
then begin
|
|
if pvPkgSearch in Verbosity then
|
|
debugln(['Info: (lazarus) Open dependency ['+Dependency.PackageName+']: trying resolved preferred filename: "'+PreferredFilename+'" ...']);
|
|
OpenFile(PreferredFilename);
|
|
end;
|
|
end;
|
|
if Dependency.LoadPackageResult=lprUndefined then begin
|
|
// no compatible package open yet
|
|
Dependency.RequiredPackage:=nil;
|
|
Dependency.LoadPackageResult:=lprNotFound;
|
|
APackage:=FindPackageWithName(Dependency.PackageName,IgnorePackage);
|
|
if APackage=nil then begin
|
|
// no compatible package with same name open
|
|
if Dependency.DependencyType=pdtLazarus then begin
|
|
// -> try package links
|
|
IgnoreFiles:=nil;
|
|
try
|
|
repeat
|
|
PkgLink:=LazPackageLinks.FindLinkWithDependencyWithIgnore(Dependency,IgnoreFiles);
|
|
if (PkgLink=nil) then break;
|
|
//debugln(['TLazPackageGraph.OpenDependency PkgLink=',PkgLink.GetEffectiveFilename,' global=',PkgLink.Origin=ploGlobal]);
|
|
PkgLink.Reference;
|
|
try
|
|
MsgResult:=OpenDependencyWithPackageLink(Dependency,PkgLink,ShowAbort);
|
|
if MsgResult=mrOk then break;
|
|
if IgnoreFiles=nil then
|
|
IgnoreFiles:=TFilenameToStringTree.Create(false);
|
|
IgnoreFiles[PkgLink.GetEffectiveFilename]:='1';
|
|
LazPackageLinks.RemoveUserLink(PkgLink);
|
|
finally
|
|
PkgLink.Release;
|
|
end;
|
|
until MsgResult=mrAbort;
|
|
finally
|
|
IgnoreFiles.Free;
|
|
end;
|
|
// try defaultfilename
|
|
if (Dependency.LoadPackageResult=lprNotFound)
|
|
and (Dependency.DefaultFilename<>'') then begin
|
|
AFilename:=Dependency.FindDefaultFilename;
|
|
if AFilename<>'' then begin
|
|
if pvPkgSearch in Verbosity then
|
|
debugln(['Info: (lazarus) Open dependency ['+Dependency.PackageName+']: trying resolved default filename: "'+PreferredFilename+'" ...']);
|
|
OpenFile(AFilename);
|
|
end;
|
|
end;
|
|
// try in owner directory (some projects put all their packages into one directory)
|
|
if Dependency.LoadPackageResult=lprNotFound then begin
|
|
CurDir:=GetDependencyOwnerDirectory(Dependency);
|
|
if (CurDir<>'') then begin
|
|
if pvPkgSearch in Verbosity then
|
|
debugln(['Info: (lazarus) Open dependency ['+Dependency.PackageName+']: trying in owner directory "'+AppendPathDelim(CurDir)+'" ...']);
|
|
AFilename:=CodeToolBoss.DirectoryCachePool.FindDiskFilename(
|
|
AppendPathDelim(CurDir)+Dependency.PackageName+'.lpk');
|
|
if FileExistsCached(AFilename) then begin
|
|
OpenFile(AFilename);
|
|
end;
|
|
end;
|
|
end;
|
|
// try a package that provides this package
|
|
if Dependency.LoadPackageResult=lprNotFound then begin
|
|
for i:=0 to Count-1 do begin
|
|
APackage:=Packages[i];
|
|
if APackage=Dependency.Owner then continue;
|
|
if APackage.ProvidesPackage(Dependency.PackageName) then begin
|
|
Dependency.RequiredPackage:=APackage;
|
|
Dependency.LoadPackageResult:=lprSuccess;
|
|
if pvPkgSearch in Verbosity then
|
|
debugln(['Info: (lazarus) Open dependency ['+Dependency.PackageName+']: Success. Package "'+APackage.IDAsString+'" provides '+Dependency.AsString]);
|
|
break;
|
|
end;
|
|
end;
|
|
end;
|
|
end else begin
|
|
// FPMake-dependency
|
|
if TFppkgHelper.Instance.HasPackage(Dependency.PackageName) then
|
|
Dependency.LoadPackageResult:=lprSuccess
|
|
else
|
|
Dependency.LoadPackageResult:=lprNotFound;
|
|
end;
|
|
end else begin
|
|
// there is already a package with this name, but wrong version open
|
|
// -> unable to load this dependency due to conflict
|
|
debugln('Error: (lazarus) Open dependency found incompatible package: searched for '
|
|
+Dependency.AsString(true,false)+', but found '+APackage.IDAsString);
|
|
if IsCompiledInBasePackage(APackage.Name) then
|
|
begin
|
|
//debugln(['Note: (lazarus) LazarusDir="',EnvironmentOptions.GetParsedLazarusDirectory,'"']);
|
|
// wrong base package
|
|
if (EnvironmentOptions.LazarusDirectory='')
|
|
or (not DirPathExistsCached(EnvironmentOptions.GetParsedLazarusDirectory))
|
|
then begin
|
|
// the lazarus directory is not set
|
|
debugln(['Note: (lazarus) The Lazarus directory is not set. Pass parameter --lazarusdir.']);
|
|
end
|
|
else if not DirPathExistsCached(LazPackageLinks.GetGlobalLinkDirectory)
|
|
then begin
|
|
debugln(['Note: (lazarus) The lpl directory is missing. Check that the Lazarus (--lazarusdir) directory is correct.']);
|
|
end;
|
|
end;
|
|
if APackage.Missing then
|
|
begin
|
|
debugln(['Note: (lazarus) The lpk (',APackage.Filename,') is missing for dependency=',Dependency.AsString])
|
|
end;
|
|
Dependency.LoadPackageResult:=lprLoadError;
|
|
end;
|
|
end;
|
|
fChanged:=true;
|
|
IncreaseBuildMacroChangeStamp;
|
|
EndUpdate;
|
|
end;
|
|
Result:=Dependency.LoadPackageResult;
|
|
end;
|
|
|
|
function TLazPackageGraph.FindAlternativeLPK(APackage: TLazPackage): string;
|
|
var
|
|
IgnoreFiles: TFilenameToStringTree;
|
|
|
|
procedure IgnoreLPK(LPKFilename: string);
|
|
begin
|
|
IgnoreFiles[LPKFilename]:='1';
|
|
end;
|
|
|
|
function ParseLPK(var LPKFilename: string; Version: TPkgVersion): boolean;
|
|
var
|
|
Code: TCodeBuffer;
|
|
XMLConfig: TXMLConfig;
|
|
Path: String;
|
|
FileVersion: Integer;
|
|
begin
|
|
Result:=false;
|
|
LPKFilename:=TrimFilename(LPKFilename);
|
|
if IgnoreFiles[LPKFilename]='1' then exit;
|
|
IgnoreLPK(LPKFilename);
|
|
if not FilenameIsAbsolute(LPKFilename) then exit;
|
|
Code:=CodeToolBoss.LoadFile(LPKFilename,true,false);
|
|
if Code=nil then exit;
|
|
try
|
|
XMLConfig:=TXMLConfig.CreateWithSource(LPKFilename,Code.Source);
|
|
try
|
|
Path:='Package/';
|
|
FileVersion:=XMLConfig.GetValue(Path+'Version',0);
|
|
PkgVersionLoadFromXMLConfig(Version,XMLConfig,Path+'Version/',FileVersion);
|
|
Result:=true;
|
|
finally
|
|
XMLConfig.Free;
|
|
end;
|
|
except
|
|
on E: Exception do begin
|
|
debugln(['Error: (lazarus) error reading "'+LPKFilename+'": '+E.Message]);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
var
|
|
Dependency: TPkgDependency;
|
|
Version: TPkgVersion;
|
|
PkgLink: TPackageLink;
|
|
Filename: String;
|
|
BaseDir: String;
|
|
begin
|
|
Version:=TPkgVersion.Create;
|
|
IgnoreFiles:=TFilenameToStringTree.Create(false);
|
|
try
|
|
// first check for preferred filenames in dependencies
|
|
Dependency:=APackage.FirstUsedByDependency;
|
|
while Dependency<>nil do begin
|
|
if (Dependency.DefaultFilename<>'') and Dependency.PreferDefaultFilename
|
|
then begin
|
|
Result:=Dependency.FindDefaultFilename;
|
|
if ParseLPK(Result,Version) then
|
|
exit;
|
|
end;
|
|
Dependency:=Dependency.NextUsedByDependency;
|
|
end;
|
|
|
|
// find nearest package link to old lpk
|
|
// for example
|
|
// if old was /path/to/lazarus/comp/bla.lpk
|
|
// then a /path/to/lazarus/comp/design/bla.lpk
|
|
// is better than a /path/to/other/lazarus/comp/bla.lpk
|
|
Dependency:=APackage.FirstUsedByDependency;
|
|
if Dependency<>nil then begin
|
|
Result:='';
|
|
BaseDir:=TrimFilename(APackage.Directory);
|
|
repeat
|
|
PkgLink:=LazPackageLinks.FindLinkWithDependencyWithIgnore(Dependency,IgnoreFiles);
|
|
if PkgLink=nil then break;
|
|
Filename:=PkgLink.GetEffectiveFilename;
|
|
if ParseLPK(Filename,Version) then begin
|
|
// candidate found
|
|
if (Result='')
|
|
or (length(CreateRelativePath(Filename,BaseDir))<length(CreateRelativePath(Result,BaseDir)))
|
|
then
|
|
Result:=Filename;
|
|
end;
|
|
until false;
|
|
if Result<>'' then exit;
|
|
end;
|
|
|
|
// last check for default filenames in dependencies
|
|
Dependency:=APackage.FirstUsedByDependency;
|
|
while Dependency<>nil do begin
|
|
if (Dependency.DefaultFilename<>'')
|
|
and (not Dependency.PreferDefaultFilename) then
|
|
begin
|
|
Result:=Dependency.FindDefaultFilename;
|
|
if ParseLPK(Result,Version) then
|
|
exit;
|
|
end;
|
|
Dependency:=Dependency.NextUsedByDependency;
|
|
end;
|
|
|
|
// nothing found via dependencies
|
|
// search in links
|
|
PkgLink:=LazPackageLinks.FindLinkWithPkgNameWithIgnore(APackage.Name,IgnoreFiles);
|
|
if PkgLink<>nil then begin
|
|
Result:=PkgLink.GetEffectiveFilename;
|
|
exit;
|
|
end;
|
|
finally
|
|
IgnoreFiles.Free;
|
|
Version.Free;
|
|
end;
|
|
Result:='';
|
|
end;
|
|
|
|
procedure TLazPackageGraph.OpenInstalledDependency(Dependency: TPkgDependency;
|
|
InstallType: TPackageInstallType; var Quiet: boolean);
|
|
var
|
|
BrokenPackage: TLazPackage;
|
|
CurResult: TModalResult;
|
|
IsBasePkg: Boolean;
|
|
begin
|
|
OpenDependency(Dependency,false);
|
|
if Dependency.LoadPackageResult<>lprSuccess then begin
|
|
// a valid lpk file of the installed package can not be found
|
|
IsBasePkg:=IsCompiledInBasePackage(Dependency.PackageName);
|
|
// -> create a broken package
|
|
BrokenPackage:=TLazPackage.CreateAndClear;
|
|
with BrokenPackage do begin
|
|
BeginUpdate;
|
|
Missing:=true;
|
|
UserReadOnly:=true;
|
|
Name:=Dependency.PackageName;
|
|
Filename:='';
|
|
Version.SetValues(0,0,0,0);
|
|
Author:='?';
|
|
License:='?';
|
|
AutoUpdate:=pupManually;
|
|
Description:=lisPkgSysThisPackageIsInstalledButTheLpkFileWasNotFound;
|
|
PackageType:=lptDesignTime;
|
|
Installed:=pitStatic;
|
|
AutoInstall:=pitNope;
|
|
if IsBasePkg then
|
|
AutoInstall:=pitStatic
|
|
else
|
|
AutoInstall:=pitNope;
|
|
CompilerOptions.UnitOutputDirectory:='';
|
|
|
|
// add lazarus registration unit path
|
|
UsageOptions.UnitPath:='';
|
|
|
|
Modified:=false;
|
|
OnModifySilently:=@PkgModify;
|
|
EndUpdate;
|
|
end;
|
|
AddPackage(BrokenPackage);
|
|
//DebugLn('TLazPackageGraph.OpenInstalledDependency ',BrokenPackage.IDAsString,' ',dbgs(ord(BrokenPackage.AutoInstall)));
|
|
if (not Quiet) and DirPathExistsCached(LazPackageLinks.GetGlobalLinkDirectory)
|
|
then begin
|
|
// tell the user
|
|
CurResult:=LazQuestionWorker(lisPkgSysPackageFileNotFound,
|
|
Format(lisPkgSysThePackageIsInstalledButNoValidPackageFileWasFound,
|
|
[BrokenPackage.Name, LineEnding]),
|
|
mtError, [mrOk, mrYesToAll, lisSkipTheseWarnings]);
|
|
if CurResult=mrYesToAll then
|
|
Quiet:=true;
|
|
end;
|
|
|
|
// open it
|
|
if OpenDependency(Dependency,false)<>lprSuccess then
|
|
RaiseGDBException('TLazPackageGraph.OpenInstalledDependency');
|
|
end;
|
|
Dependency.RequiredPackage.Installed:=InstallType;
|
|
end;
|
|
|
|
procedure TLazPackageGraph.OpenRequiredDependencyList(
|
|
FirstDependency: TPkgDependency);
|
|
var
|
|
Dependency: TPkgDependency;
|
|
begin
|
|
Dependency:=FirstDependency;
|
|
while Dependency<>nil do begin
|
|
OpenDependency(Dependency,false);
|
|
Dependency:=Dependency.NextRequiresDependency;
|
|
end;
|
|
end;
|
|
|
|
procedure TLazPackageGraph.MoveRequiredDependencyUp(ADependency: TPkgDependency);
|
|
begin
|
|
if (ADependency=nil) or (ADependency.Removed) or (ADependency.Owner=nil)
|
|
or (ADependency.PrevRequiresDependency=nil)
|
|
or (not (ADependency.Owner is TLazPackage))
|
|
then exit;
|
|
BeginUpdate(true);
|
|
TLazPackage(ADependency.Owner).MoveRequiredDependencyUp(ADependency);
|
|
EndUpdate;
|
|
end;
|
|
|
|
procedure TLazPackageGraph.MoveRequiredDependencyDown(ADependency: TPkgDependency);
|
|
begin
|
|
if (ADependency=nil) or (ADependency.Removed) or (ADependency.Owner=nil)
|
|
or (ADependency.NextRequiresDependency=nil)
|
|
or (not (ADependency.Owner is TLazPackage))
|
|
then exit;
|
|
BeginUpdate(true);
|
|
TLazPackage(ADependency.Owner).MoveRequiredDependencyDown(ADependency);
|
|
EndUpdate;
|
|
end;
|
|
|
|
procedure TLazPackageGraph.IterateComponentClasses(APackage: TLazPackage;
|
|
Event: TIterateComponentClassesEvent; WithUsedPackages,
|
|
WithRequiredPackages: boolean);
|
|
var
|
|
ARequiredPackage: TLazPackage;
|
|
ADependency: TPkgDependency;
|
|
begin
|
|
APackage.IterateComponentClasses(Event,WithUsedPackages);
|
|
// iterate through all required packages
|
|
if WithRequiredPackages then begin
|
|
ADependency:=APackage.FirstRequiredDependency;
|
|
while ADependency<>nil do begin
|
|
ARequiredPackage:=FindOpenPackage(ADependency,[fpfSearchInInstalledPckgs]);
|
|
if ARequiredPackage<>nil then begin
|
|
ARequiredPackage.IterateComponentClasses(Event,false);
|
|
end;
|
|
ADependency:=ADependency.NextRequiresDependency;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TLazPackageGraph.IterateAllComponentClasses(
|
|
Event: TIterateComponentClassesEvent);
|
|
var
|
|
Cnt: Integer;
|
|
i: Integer;
|
|
begin
|
|
Cnt:=Count;
|
|
for i:=0 to Cnt-1 do
|
|
IterateComponentClasses(Packages[i],Event,false,false);
|
|
end;
|
|
|
|
procedure TLazPackageGraph.IteratePackages(Flags: TFindPackageFlags;
|
|
Event: TIteratePackagesEvent);
|
|
var
|
|
CurPkg: TLazPackage;
|
|
i: Integer;
|
|
begin
|
|
// iterate opened packages
|
|
for i:=0 to FItems.Count-1 do begin
|
|
CurPkg:=Packages[i];
|
|
// check installed packages
|
|
if ((fpfSearchInInstalledPckgs in Flags) and (CurPkg.Installed<>pitNope))
|
|
// check autoinstall packages
|
|
or ((fpfSearchInAutoInstallPckgs in Flags) and (CurPkg.AutoInstall<>pitNope))
|
|
// check packages with opened editor
|
|
or ((fpfSearchInPckgsWithEditor in Flags) and (CurPkg.Editor<>nil))
|
|
then begin
|
|
Event(CurPkg);
|
|
end;
|
|
end;
|
|
// iterate in package links
|
|
if (fpfSearchInPkgLinks in Flags) then begin
|
|
LazPackageLinks.IteratePackages(fpfPkgLinkMustExist in Flags,Event);
|
|
end;
|
|
end;
|
|
|
|
procedure TLazPackageGraph.IteratePackagesSorted(Flags: TFindPackageFlags;
|
|
Event: TIteratePackagesEvent);
|
|
var
|
|
ANode: TAVLTreeNode;
|
|
CurPkg: TLazPackage;
|
|
begin
|
|
ANode:=FTree.FindLowest;
|
|
while ANode<>nil do begin
|
|
CurPkg:=TLazPackage(ANode.Data);
|
|
// check installed packages
|
|
if ((fpfSearchInInstalledPckgs in Flags) and (CurPkg.Installed<>pitNope))
|
|
// check autoinstall packages
|
|
or ((fpfSearchInAutoInstallPckgs in Flags) and (CurPkg.AutoInstall<>pitNope))
|
|
// check packages with opened editor
|
|
or ((fpfSearchInPckgsWithEditor in Flags) and (CurPkg.Editor<>nil))
|
|
then
|
|
Event(CurPkg);
|
|
ANode:=FTree.FindSuccessor(ANode);
|
|
end;
|
|
end;
|
|
|
|
procedure TLazPackageGraph.GetAllRequiredPackages(APackage: TLazPackage;
|
|
FirstDependency: TPkgDependency; out List, FPMakeList: TFPList;
|
|
Flags: TPkgIntfRequiredFlags; MinPolicy: TPackageUpdatePolicy);
|
|
// returns packages in topological order, beginning with the top level package
|
|
|
|
procedure GetTopologicalOrder(CurDependency: TPkgDependency;
|
|
out HighestLevel: integer);
|
|
var
|
|
RequiredPackage: TLazPackage;
|
|
Dependency: TPkgDependency;
|
|
DepLevel: integer;
|
|
begin
|
|
HighestLevel:=0;
|
|
while CurDependency<>nil do begin
|
|
Dependency:=CurDependency;
|
|
CurDependency:=CurDependency.NextRequiresDependency;
|
|
//debugln('TLazPackageGraph.GetAllRequiredPackages A ',Dependency.AsString,' ',dbgs(ord(Dependency.LoadPackageResult)),' ',dbgs(ord(lprSuccess)));
|
|
if Dependency.LoadPackageResult<>lprSuccess then continue;
|
|
//debugln('TLazPackageGraph.GetAllRequiredPackages B ',Dependency.AsString);
|
|
if Dependency.DependencyType=pdtLazarus then begin
|
|
RequiredPackage:=Dependency.RequiredPackage;
|
|
if (lpfVisited in RequiredPackage.Flags) then begin
|
|
// already visited
|
|
if HighestLevel<RequiredPackage.TopologicalLevel then
|
|
HighestLevel:=RequiredPackage.TopologicalLevel;
|
|
continue;
|
|
end;
|
|
RequiredPackage.Flags:=RequiredPackage.Flags+[lpfVisited];
|
|
if ord(RequiredPackage.AutoUpdate)<ord(MinPolicy) then
|
|
continue; // skip manually updated packages
|
|
if (pirSkipDesignTimeOnly in Flags)
|
|
and (RequiredPackage.PackageType=lptDesignTime) then
|
|
continue; // skip designtime (only) packages
|
|
if not (pirNotRecursive in Flags) then begin
|
|
GetTopologicalOrder(RequiredPackage.FirstRequiredDependency,DepLevel);
|
|
RequiredPackage.TopologicalLevel:=DepLevel+1;
|
|
if HighestLevel<RequiredPackage.TopologicalLevel then
|
|
HighestLevel:=RequiredPackage.TopologicalLevel;
|
|
end;
|
|
if List=nil then List:=TFPList.Create;
|
|
List.Add(RequiredPackage);
|
|
end else begin
|
|
// FPMake dependency
|
|
// ToDo: Handle package-dependencies (or not?) and version-checks
|
|
if FPMakeList=nil then FPMakeList := TFPList.Create;
|
|
FPMakeList.Add(Dependency);
|
|
end;
|
|
// add package behind its requirements
|
|
end;
|
|
end;
|
|
|
|
var
|
|
i: Integer;
|
|
j: Integer;
|
|
DepLevel: integer;
|
|
begin
|
|
List:=nil;
|
|
FPMakeList:=nil;
|
|
MarkAllPackagesAsNotVisited;
|
|
if APackage<>nil then begin
|
|
FirstDependency:=APackage.FirstRequiredDependency;
|
|
APackage.Flags:=APackage.Flags+[lpfVisited];
|
|
end;
|
|
// create topological list, beginning with the leaves
|
|
GetTopologicalOrder(FirstDependency,DepLevel);
|
|
if List=nil then exit;
|
|
MergeSort(List,@CompareLazPackageTopologicallyAndName);
|
|
if not (pirCompileOrder in Flags) then begin
|
|
// reverse list order
|
|
i:=0;
|
|
j:=List.Count-1;
|
|
while i<j do begin
|
|
List.Exchange(i,j);
|
|
inc(i);
|
|
dec(j);
|
|
end;
|
|
end;
|
|
//for i:=0 to List.Count-1 do
|
|
// debugln(['TLazPackageGraph.GetAllRequiredPackages ',i,'/',List.Count-1,' ',TLazPackage(List[i]).Name,' ',TLazPackage(List[i]).TopologicalLevel]);
|
|
end;
|
|
|
|
procedure TLazPackageGraph.GetAllRequiredPackages(APackage: TLazPackage;
|
|
FirstDependency: TPkgDependency; out List: TFPList;
|
|
Flags: TPkgIntfRequiredFlags; MinPolicy: TPackageUpdatePolicy);
|
|
var
|
|
FPMakeList: TFPList;
|
|
begin
|
|
FPMakeList := nil;
|
|
try
|
|
GetAllRequiredPackages(APackage, FirstDependency, List, FPMakeList, Flags, MinPolicy);
|
|
finally
|
|
FPMakeList.Free;
|
|
end;
|
|
end;
|
|
|
|
procedure TLazPackageGraph.GetConnectionsTree(FirstDependency: TPkgDependency;
|
|
var PkgList: TFPList; var Tree: TPkgPairTree);
|
|
|
|
procedure AddConnection(Pkg1, Pkg2: TLazPackage);
|
|
begin
|
|
if Pkg1=Pkg2 then exit;
|
|
if Tree=nil then
|
|
Tree:=TPkgPairTree.Create;
|
|
Tree.AddPairIfNotExists(Pkg1,Pkg2);
|
|
end;
|
|
|
|
procedure AddConnections(StartDependency: TPkgDependency);
|
|
// add every connection between owner and required package
|
|
// and between two children
|
|
var
|
|
OwnerPackage: TLazPackage;
|
|
Dependency1: TPkgDependency;
|
|
Dependency2: TPkgDependency;
|
|
Pkg1: TLazPackage;
|
|
Pkg2: TLazPackage;
|
|
begin
|
|
if StartDependency=nil then exit;
|
|
if (StartDependency.Owner is TLazPackage) then
|
|
OwnerPackage:=TLazPackage(StartDependency.Owner)
|
|
else
|
|
OwnerPackage:=nil;
|
|
Dependency1:=StartDependency;
|
|
while Dependency1<>nil do begin
|
|
Pkg1:=Dependency1.RequiredPackage;
|
|
if Pkg1<>nil then begin
|
|
// add connection between owner and required package
|
|
if OwnerPackage<>nil then
|
|
AddConnection(OwnerPackage,Pkg1);
|
|
// add connections between any two direct required packages
|
|
Dependency2:=StartDependency;
|
|
while Dependency2<>nil do begin
|
|
Pkg2:=Dependency2.RequiredPackage;
|
|
if Pkg2<>nil then
|
|
AddConnection(Pkg1,Pkg2);
|
|
Dependency2:=Dependency2.NextDependency[pddRequires];
|
|
end;
|
|
end;
|
|
Dependency1:=Dependency1.NextDependency[pddRequires];
|
|
end;
|
|
end;
|
|
|
|
var
|
|
i: Integer;
|
|
Pkg: TLazPackage;
|
|
begin
|
|
if Tree<>nil then Tree.FreeAndClear;
|
|
GetAllRequiredPackages(nil,FirstDependency,PkgList);
|
|
if PkgList=nil then exit;
|
|
AddConnections(FirstDependency);
|
|
for i:=0 to PkgList.Count-1 do begin
|
|
Pkg:=TLazPackage(PkgList[i]);
|
|
AddConnections(Pkg.FirstRequiredDependency);
|
|
end;
|
|
end;
|
|
|
|
end.
|
|
|
|
|