lazarus/components/codetools/definetemplates.pas

8807 lines
286 KiB
ObjectPascal

{
***************************************************************************
* *
* This source is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This code is distributed in the hope that it will be useful, but *
* WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
* General Public License for more details. *
* *
* A copy of the GNU General Public License is available on the World *
* Wide Web at <http://www.gnu.org/copyleft/gpl.html>. You can also *
* obtain it by writing to the Free Software Foundation, *
* Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
* *
***************************************************************************
Author: Mattias Gaertner
Abstract:
This unit is a support unit for the code tools. It manages compilation
information, which is not stored in the source, like Makefile information
and compiler command line options. This information is needed to
successfully find the right units, include files, predefined variables,
etc..
The information is stored in a TDefineTree, which contains nodes of type
TDefineTemplate. Each TDefineTemplate is a tree of defines, undefines,
definerecurses, ifdefs, ifndefs, elses, elseifs, directories ... .
Simply give a TDefineTree a directory and it will return all predefined
variables for that directory. These values can be used to parse a unit in
the directory.
TDefineTree can be saved to and loaded from a XML file.
The TDefinePool contains a list of TDefineTemplate trees, and can generate
some default templates for Lazarus and FPC sources.
}
unit DefineTemplates;
{$mode objfpc}{$H+}
{ $Define VerboseDefineCache}
{ $Define VerboseFPCSrcScan}
{ $Define ShowTriedFiles}
interface
uses
Classes, SysUtils, CodeToolsStrConsts, ExprEval, DirectoryCacher,
BasicCodeTools, Laz_XMLCfg, AVL_Tree, CodeToolsStructs,
Process, KeywordFuncLists, FileProcs;
const
ExternalMacroStart = ExprEval.ExternalMacroStart;
// Standard Template Names (do not translate them)
StdDefTemplFPC = 'Free Pascal Compiler';
StdDefTemplFPCSrc = 'Free Pascal sources';
StdDefTemplLazarusSources = 'Lazarus sources';
StdDefTemplLazarusSrcDir = 'Lazarus source directory';
StdDefTemplLazarusBuildOpts = 'Lazarus build options';
StdDefTemplLCLProject = 'LCL project';
// Standard macros
DefinePathMacroName = ExternalMacroStart+'DefinePath';
UnitPathMacroName = ExternalMacroStart+'UnitPath';
IncludePathMacroName = ExternalMacroStart+'IncPath';
SrcPathMacroName = ExternalMacroStart+'SrcPath';
PPUSrcPathMacroName = ExternalMacroStart+'PPUSrcPath';
DCUSrcPathMacroName = ExternalMacroStart+'DCUSrcPath';
CompiledSrcPathMacroName = ExternalMacroStart+'CompiledSrcPath';
UnitLinksMacroName = ExternalMacroStart+'UnitLinks';
UnitSetMacroName = ExternalMacroStart+'UnitSet';
FPCUnitPathMacroName = ExternalMacroStart+'FPCUnitPath';
TargetOSMacroName = ExternalMacroStart+'TargetOS';
TargetCPUMacroName = ExternalMacroStart+'TargetCPU';
DefinePathMacro = '$('+DefinePathMacroName+')'; // the path of the define template
UnitPathMacro = '$('+UnitPathMacroName+')';
IncludePathMacro = '$('+IncludePathMacroName+')';
SrcPathMacro = '$('+SrcPathMacroName+')';
PPUSrcPathMacro = '$('+PPUSrcPathMacroName+')';
DCUSrcPathMacro = '$('+DCUSrcPathMacroName+')';
CompiledSrcPathMacro = '$('+CompiledSrcPathMacroName+')';
UnitLinksMacro = '$('+UnitLinksMacroName+')';
UnitSetMacro = '$('+UnitSetMacroName+')';
FPCUnitPathMacro = '$('+FPCUnitPathMacroName+')';
TargetOSMacro = '$('+TargetOSMacroName+')';
TargetCPUMacro = '$('+TargetCPUMacroName+')';
// virtual directories
VirtualDirectory='VIRTUALDIRECTORY';
VirtualTempDir='TEMPORARYDIRECTORY';
// FPC operating systems and processor types
FPCOperatingSystemNames: array[1..28] of shortstring =(
'linux',
'win32','win64','wince',
'darwin','macos',
'freebsd','netbsd','openbsd',
'go32v2',
'os2',
'beos','haiku',
'amiga','atari','solaris', 'qnx', 'netware','wdosx',
'palmos','emx','watcom','morphos','netwlibc',
'gba','nds','embedded','symbian'
);
FPCOperatingSystemAlternativeNames: array[1..2] of shortstring =(
'unix', 'win' // see GetDefaultSrcOSForTargetOS
);
FPCOperatingSystemAlternative2Names: array[1..1] of shortstring =(
'bsd' // see GetDefaultSrcOS2ForTargetOS
);
FPCProcessorNames: array[1..6] of shortstring =(
'i386', 'powerpc', 'm68k', 'x86_64', 'sparc', 'arm'
);
FPCSyntaxModes: array[1..5] of shortstring = (
'FPC', 'ObjFPC', 'Delphi', 'TP', 'MacPas'
);
Lazarus_CPU_OS_Widget_Combinations: array[1..62] of shortstring = (
'i386-linux-gtk',
'i386-linux-gtk2',
'i386-linux-qt',
'i386-linux-fpgui',
'i386-linux-nogui',
'i386-freebsd-gtk',
'i386-freebsd-gtk2',
'i386-freebsd-qt',
'i386-freebsd-nogui',
'i386-openbsd-gtk',
'i386-openbsd-gtk2',
'i386-openbsd-qt',
'i386-openbsd-nogui',
'i386-netbsd-gtk',
'i386-netbsd-gtk2',
'i386-netbsd-qt',
'i386-netbsd-nogui',
'i386-win32-win32',
'i386-win32-gtk2',
'i386-win32-qt',
'i386-win32-fpgui',
'i386-win32-nogui',
'i386-wince-wince',
'i386-wince-fpgui',
'i386-wince-nogui',
'i386-darwin-gtk',
'i386-darwin-gtk2',
'i386-darwin-carbon',
'i386-darwin-qt',
'i386-darwin-fpgui',
'i386-darwin-nogui',
'i386-haiku-qt',
'i386-haiku-nogui',
'powerpc-darwin-gtk',
'powerpc-darwin-gtk2',
'powerpc-darwin-carbon',
'powerpc-linux-gtk',
'powerpc-linux-gtk2',
'powerpc-linux-nogui',
'sparc-linux-gtk',
'sparc-linux-gtk2',
'sparc-linux-nogui',
'arm-wince-wince',
'arm-wince-fpgui',
'arm-wince-nogui',
'arm-linux-gtk',
'arm-linux-gtk2',
'arm-linux-qt',
'arm-linux-nogui',
'x86_64-freebsd-gtk',
'x86_64-freebsd-gtk2',
'x86_64-freebsd-qt',
'x86_64-freebsd-fpgui',
'x86_64-freebsd-nogui',
'x86_64-linux-gtk',
'x86_64-linux-gtk2',
'x86_64-linux-qt',
'x86_64-linux-fpgui',
'x86_64-linux-nogui',
'x86_64-win64-win32',
'x86_64-win64-fpgui',
'x86_64-win64-nogui'
);
type
//---------------------------------------------------------------------------
// TDefineTemplate stores a define action, the variablename and the value
TDefineAction = (
da_None,
da_Block,
da_Define,
da_DefineRecurse,
da_Undefine,
da_UndefineRecurse,
da_UndefineAll,
da_If,
da_IfDef,
da_IfNDef,
da_ElseIf,
da_Else,
da_Directory
);
const
DefineActionBlocks = [da_Block, da_Directory, da_If, da_IfDef, da_IfNDef,
da_ElseIf, da_Else];
DefineActionDefines = [da_Define,da_DefineRecurse,da_Undefine,
da_UndefineRecurse,da_UndefineAll];
DefineActionNames: array[TDefineAction] of string = (
'None', 'Block', 'Define', 'DefineRecurse', 'Undefine', 'UndefineRecurse',
'UndefineAll', 'If', 'IfDef', 'IfNDef', 'ElseIf', 'Else', 'Directory'
);
var
DefineActionImages: array[TDefineAction] of integer;
AutogeneratedImage: Integer;
type
TDefineTree = class;
TDefineTemplateFlag = (
dtfAutoGenerated
);
TDefineTemplateFlags = set of TDefineTemplateFlag;
TDefineTemplate = class
private
FChildCount: integer;
FFirstChild: TDefineTemplate;
FLastChild: TDefineTemplate;
FMarked: boolean;
FMergeNameBehind: string;
FMergeNameInFront: string;
FNext: TDefineTemplate;
FParent: TDefineTemplate;
FPrior: TDefineTemplate;
public
Name: string;
Description: string;
Variable: string;
Value: string;
Action: TDefineAction;
Flags: TDefineTemplateFlags;
Owner: TObject;
class procedure MergeTemplates(ParentDefTempl: TDefineTemplate;
var FirstSibling, LastSibling:TDefineTemplate;
SourceTemplate: TDefineTemplate; WithSiblings: boolean;
const NewNamePrefix: string);
class procedure MergeXMLConfig(ParentDefTempl: TDefineTemplate;
var FirstSibling, LastSibling:TDefineTemplate;
XMLConfig: TXMLConfig; const Path, NewNamePrefix: string);
constructor Create(const AName, ADescription, AVariable, AValue: string;
AnAction: TDefineAction);
constructor Create;
destructor Destroy; override;
procedure ConsistencyCheck;
procedure CalcMemSize(Stats: TCTMemStats);
function CreateCopy(OnlyMarked: boolean = false;
WithSiblings: boolean = true;
WithChilds: boolean = true): TDefineTemplate;
function CreateMergeCopy: TDefineTemplate;
function FindByName(const AName: string;
WithSubChilds, WithNextSiblings: boolean): TDefineTemplate;
function FindChildByName(const AName: string): TDefineTemplate;
function FindRoot: TDefineTemplate;
function FindUniqueName(const Prefix: string): string;
function GetFirstSibling: TDefineTemplate;
function HasDefines(OnlyMarked, WithSiblings: boolean): boolean;
function IsAutoGenerated: boolean;
function IsEqual(ADefineTemplate: TDefineTemplate;
CheckSubNodes, CheckNextSiblings: boolean): boolean;
function Level: integer;
function LoadFromXMLConfig(XMLConfig: TXMLConfig; const Path: string;
ClearOldSiblings, WithMergeInfo: boolean): boolean;
function SelfOrParentContainsFlag(AFlag: TDefineTemplateFlag): boolean;
procedure AddChild(ADefineTemplate: TDefineTemplate);
procedure ReplaceChild(ADefineTemplate: TDefineTemplate);
function DeleteChild(const AName: string): boolean;
procedure Assign(ADefineTemplate: TDefineTemplate; WithSubNodes,
WithNextSiblings, ClearOldSiblings: boolean); virtual;
procedure AssignValues(ADefineTemplate: TDefineTemplate);
procedure Clear(WithSiblings: boolean);
procedure CreateMergeInfo(WithSiblings, OnlyMarked: boolean);
procedure InheritMarks(WithSiblings, WithChilds, Down, Up: boolean);
procedure InsertBehind(APrior: TDefineTemplate);
procedure InsertInFront(ANext: TDefineTemplate);
procedure MoveToLast(Child: TDefineTemplate);
procedure LoadValuesFromXMLConfig(XMLConfig: TXMLConfig; const Path: string;
WithMergeInfo: boolean);
procedure MarkFlags(const MustFlags, NotFlags: TDefineTemplateFlags;
WithSiblings, WithChilds: boolean);
procedure MarkNodes(WithSiblings, WithChilds: boolean);
procedure MarkOwnedBy(TheOwner: TObject;
const MustFlags, NotFlags: TDefineTemplateFlags;
WithSiblings, WithChilds: boolean);
procedure RemoveFlags(TheFlags: TDefineTemplateFlags);
procedure RemoveLeaves(TheOwner: TObject; const MustFlags,
NotFlags: TDefineTemplateFlags;
WithSiblings: boolean;
var FirstDefTemplate: TDefineTemplate);
procedure RemoveMarked(WithSiblings: boolean;
var FirstDefTemplate: TDefineTemplate);
procedure RemoveOwner(TheOwner: TObject; WithSiblings: boolean);
procedure ReverseMarks(WithSiblings, WithChilds: boolean);
procedure SaveToXMLConfig(XMLConfig: TXMLConfig; const Path: string;
WithSiblings, OnlyMarked,
WithMergeInfo, UpdateMergeInfo: boolean);
procedure SaveValuesToXMLConfig(XMLConfig: TXMLConfig; const Path: string;
WithMergeInfo: boolean);
procedure SetDefineOwner(NewOwner: TObject; WithSiblings: boolean);
procedure SetFlags(AddFlags, SubFlags: TDefineTemplateFlags;
WithSiblings: boolean);
procedure Unbind;
procedure UnmarkNodes(WithSiblings, WithChilds: boolean);
procedure WriteDebugReport(OnlyMarked: boolean);
function GetNext: TDefineTemplate;
function GetNextSkipChildren: TDefineTemplate;
public
property ChildCount: integer read FChildCount;
property FirstChild: TDefineTemplate read FFirstChild;
property LastChild: TDefineTemplate read FLastChild;
property Marked: boolean read FMarked write FMarked;
property Next: TDefineTemplate read FNext;
property Parent: TDefineTemplate read FParent;
property Prior: TDefineTemplate read FPrior;
property MergeNameInFront: string read FMergeNameInFront write FMergeNameInFront;
property MergeNameBehind: string read FMergeNameBehind write FMergeNameBehind;
end;
//---------------------------------------------------------------------------
//
{ TDirectoryDefines }
TDirectoryDefines = class
public
Path: string;
Values: TExpressionEvaluator;
constructor Create;
destructor Destroy; override;
procedure CalcMemSize(Stats: TCTMemStats);
end;
TOnGetVirtualDirectoryDefines = procedure(Sender: TDefineTree;
Defines: TDirectoryDefines) of object;
//---------------------------------------------------------------------------
// TDefineTree caches the define values for directories
TOnReadValue = procedure(Sender: TObject; const VariableName: string;
var Value: string; var Handled: boolean) of object;
TOnGetVirtualDirectoryAlias = procedure(Sender: TObject;
var RealDir: string) of object;
TReadFunctionData = record
Param: string;
Result: string;
end;
PReadFunctionData = ^TReadFunctionData;
TDefTreeCalculate = procedure(Tree: TDefineTree; Node: TDefineTemplate;
ValueParsed: boolean; const ParsedValue: string;
ExpressionCalculated: boolean; const ExpressionResult: string;
Execute: boolean) of object;
TDefineTree = class
private
FDirectoryCachePool: TCTDirectoryCachePool;
FFirstDefineTemplate: TDefineTemplate;
FCache: TAVLTree; // tree of TDirectoryDefines
FDefineStrings: TStringTree;
FChangeStep: integer;
FErrorDescription: string;
FErrorTemplate: TDefineTemplate;
FMacroFunctions: TKeyWordFunctionList;
FMacroVariables: TKeyWordFunctionList;
FOnCalculate: TDefTreeCalculate;
FOnGetVirtualDirectoryAlias: TOnGetVirtualDirectoryAlias;
FOnGetVirtualDirectoryDefines: TOnGetVirtualDirectoryDefines;
FOnPrepareTree: TNotifyEvent;
FOnReadValue: TOnReadValue;
FVirtualDirCache: TDirectoryDefines;
function Calculate(DirDef: TDirectoryDefines): boolean;
procedure IncreaseChangeStep;
procedure SetDirectoryCachePool(const AValue: TCTDirectoryCachePool);
procedure RemoveDoubles(Defines: TDirectoryDefines);
protected
function FindDirectoryInCache(const Path: string): TDirectoryDefines;
function GetDirDefinesForDirectory(const Path: string;
WithVirtualDir: boolean): TDirectoryDefines;
function GetDirDefinesForVirtualDirectory: TDirectoryDefines;
function MacroFuncExtractFileExt(Data: Pointer): boolean;
function MacroFuncExtractFilePath(Data: Pointer): boolean;
function MacroFuncExtractFileName(Data: Pointer): boolean;
function MacroFuncExtractFileNameOnly(Data: Pointer): boolean;
procedure DoClearCache;
procedure DoPrepareTree;
public
property RootTemplate: TDefineTemplate
read FFirstDefineTemplate write FFirstDefineTemplate;
property ChangeStep: integer read FChangeStep;
property ErrorTemplate: TDefineTemplate read FErrorTemplate;
property ErrorDescription: string read FErrorDescription;
property OnGetVirtualDirectoryAlias: TOnGetVirtualDirectoryAlias
read FOnGetVirtualDirectoryAlias write FOnGetVirtualDirectoryAlias;
property OnGetVirtualDirectoryDefines: TOnGetVirtualDirectoryDefines
read FOnGetVirtualDirectoryDefines write FOnGetVirtualDirectoryDefines;
property OnReadValue: TOnReadValue read FOnReadValue write FOnReadValue;
property OnPrepareTree: TNotifyEvent read FOnPrepareTree write FOnPrepareTree;
property OnCalculate: TDefTreeCalculate read FOnCalculate write FOnCalculate;
property MacroFunctions: TKeyWordFunctionList read FMacroFunctions;
property MacroVariables: TKeyWordFunctionList read FMacroVariables;
public
constructor Create;
destructor Destroy; override;
procedure ConsistencyCheck;
procedure CalcMemSize(Stats: TCTMemStats);
function ExtractNonAutoCreated: TDefineTemplate;
function ExtractTemplatesOwnedBy(TheOwner: TObject; const MustFlags,
NotFlags: TDefineTemplateFlags): TDefineTemplate;
function FindDefineTemplateByName(const AName: string;
OnlyRoots: boolean): TDefineTemplate;
function GetCompiledSrcPathForDirectory(const Directory: string): string;
function GetDCUSrcPathForDirectory(const Directory: string): string;
function GetDefinesForDirectory(const Path: string;
WithVirtualDir: boolean): TExpressionEvaluator;
function GetDefinesForVirtualDirectory: TExpressionEvaluator;
function GetIncludePathForDirectory(const Directory: string): string;
function GetLastRootTemplate: TDefineTemplate;
function GetPPUSrcPathForDirectory(const Directory: string): string;
function GetSrcPathForDirectory(const Directory: string): string;
function GetUnitPathForDirectory(const Directory: string): string;
function IsEqual(SrcDefineTree: TDefineTree): boolean;
procedure Add(ADefineTemplate: TDefineTemplate);
procedure AddChild(ParentTemplate, NewDefineTemplate: TDefineTemplate);
procedure AddFirst(ADefineTemplate: TDefineTemplate);
procedure MoveToLast(ADefineTemplate: TDefineTemplate);
procedure Assign(SrcDefineTree: TDefineTree);
procedure AssignNonAutoCreated(SrcDefineTree: TDefineTree);
procedure Clear;
procedure ClearCache;
procedure MarkNonAutoCreated;
procedure MarkTemplatesOwnedBy(TheOwner: TObject;
const MustFlags, NotFlags: TDefineTemplateFlags);
procedure MergeDefineTemplates(SourceTemplate: TDefineTemplate;
const NewNamePrefix: string);
procedure MergeTemplates(SourceTemplate: TDefineTemplate;
const NewNamePrefix: string);
procedure ReadValue(const DirDef: TDirectoryDefines;
const PreValue, CurDefinePath: string; out NewValue: string);
procedure RemoveDefineTemplate(ADefTempl: TDefineTemplate);
procedure RemoveMarked;
procedure RemoveRootDefineTemplateByName(const AName: string);
procedure RemoveTemplatesOwnedBy(TheOwner: TObject;
const MustFlags, NotFlags: TDefineTemplateFlags);
procedure ReplaceChild(ParentTemplate, NewDefineTemplate: TDefineTemplate;
const ChildName: string);
procedure ReplaceRootSameName(ADefineTemplate: TDefineTemplate);
procedure ReplaceRootSameName(const Name: string;
ADefineTemplate: TDefineTemplate);
procedure ReplaceRootSameNameAddFirst(ADefineTemplate: TDefineTemplate);
procedure WriteDebugReport;
property DirectoryCachePool: TCTDirectoryCachePool read FDirectoryCachePool write SetDirectoryCachePool;
end;
//---------------------------------------------------------------------------
{ TDefinePool }
TDefinePoolProgress = procedure(Sender: TObject;
Index, MaxIndex: integer; // MaxIndex=-1 if unknown
const Msg: string;
var Abort: boolean) of object;
TDefinePool = class
private
FEnglishErrorMsgFilename: string;
FItems: TFPList; // list of TDefineTemplate;
FOnProgress: TDefinePoolProgress;
function GetItems(Index: integer): TDefineTemplate;
procedure SetEnglishErrorMsgFilename(const AValue: string);
function CheckAbort(ProgressID, MaxIndex: integer; const Msg: string
): boolean;
public
property Items[Index: integer]: TDefineTemplate read GetItems; default;
function Count: integer;
procedure Add(ADefineTemplate: TDefineTemplate);
procedure Insert(Index: integer; ADefineTemplate: TDefineTemplate);
procedure Delete(Index: integer);
procedure Move(SrcIndex, DestIndex: integer);
property EnglishErrorMsgFilename: string
read FEnglishErrorMsgFilename write SetEnglishErrorMsgFilename;
// FPC templates
function CreateFPCTemplate(const CompilerPath, CompilerOptions,
TestPascalFile: string;
out UnitSearchPath, TargetOS,
TargetProcessor: string;
Owner: TObject): TDefineTemplate;
function GetFPCVerFromFPCTemplate(Template: TDefineTemplate;
out FPCVersion, FPCRelease, FPCPatch: integer): boolean;
function CreateFPCSrcTemplate(const FPCSrcDir, UnitSearchPath, PPUExt,
DefaultTargetOS, DefaultProcessorName: string;
UnitLinkListValid: boolean; var UnitLinkList: string;
Owner: TObject): TDefineTemplate;
function CreateFPCCommandLineDefines(const Name, CmdLine: string;
RecursiveDefines: boolean;
Owner: TObject;
AlwaysCreate: boolean = false): TDefineTemplate;
// Lazarus templates
function CreateLazarusSrcTemplate(
const LazarusSrcDir, WidgetType, ExtraOptions: string;
Owner: TObject): TDefineTemplate;
function CreateLCLProjectTemplate(const LazarusSrcDir, WidgetType,
ProjectDir: string; Owner: TObject): TDefineTemplate;
// Delphi templates
function CreateDelphiSrcPath(DelphiVersion: integer;
const PathPrefix: string): string;
function CreateDelphiCompilerDefinesTemplate(DelphiVersion: integer;
Owner: TObject): TDefineTemplate;
function CreateDelphiDirectoryTemplate(const DelphiDirectory: string;
DelphiVersion: integer; Owner: TObject): TDefineTemplate;
function CreateDelphiProjectTemplate(const ProjectDir,
DelphiDirectory: string; DelphiVersion: integer;
Owner: TObject): TDefineTemplate;
// Kylix templates
function CreateKylixCompilerDefinesTemplate(KylixVersion: integer;
Owner: TObject): TDefineTemplate;
function CreateKylixSrcPath(KylixVersion: integer;
const PathPrefix: string): string;
function CreateKylixDirectoryTemplate(const KylixDirectory: string;
KylixVersion: integer; Owner: TObject): TDefineTemplate;
function CreateKylixProjectTemplate(const ProjectDir,
KylixDirectory: string; KylixVersion: integer;
Owner: TObject): TDefineTemplate;
constructor Create;
destructor Destroy; override;
procedure Clear;
property OnProgress: TDefinePoolProgress read FOnProgress write FOnProgress;
procedure ConsistencyCheck;
procedure WriteDebugReport;
procedure CalcMemSize(Stats: TCTMemStats);
end;
{ TFPCSourceRule }
TFPCSourceRule = class
public
Filename: string;
Score: integer;
Targets: string; // comma separated list of OS, CPU, e.g. win32,unix,i386 or * for all
function FitsTargets(const FilterTargets: string): boolean;
function FitsFilename(const aFilename: string): boolean;
function IsEqual(Rule: TFPCSourceRule): boolean;
procedure Assign(Rule: TFPCSourceRule);
end;
{ TFPCSourceRules }
TFPCSourceRules = class
private
FChangeStamp: integer;
FItems: TFPList;// list of TFPCSourceRule
FScore: integer;
FTargets: string;
function GetItems(Index: integer): TFPCSourceRule;
procedure SetTargets(const AValue: string);
public
constructor Create;
destructor Destroy; override;
procedure Clear;
function IsEqual(Rules: TFPCSourceRules): boolean;
procedure Assign(Rules: TFPCSourceRules);
function Clone: TFPCSourceRules;
property Items[Index: integer]: TFPCSourceRule read GetItems; default;
function Count: integer;
function Add(const Filename: string): TFPCSourceRule;
function GetDefaultTargets(TargetOS, TargetCPU: string): string;
procedure GetRulesForTargets(Targets: string;
var RulesSortedForFilenameStart: TAVLTree);
function GetScore(Filename: string;
RulesSortedForFilenameStart: TAVLTree): integer;
property Score: integer read FScore write FScore; // used for Add
property Targets: string read FTargets write SetTargets; // used for Add, e.g. win32,unix,bsd or * for all
property ChangeStamp: integer read FChangeStamp;
procedure IncreaseChangeStamp;
end;
var
DefaultFPCSourceRules: TFPCSourceRules;
const
DefineTemplateFlagNames: array[TDefineTemplateFlag] of shortstring = (
'AutoGenerated'
);
type
TFPCInfoType = (
fpciCompilerDate, // -iD Return compiler date
fpciShortVersion, // -iV Return short compiler version
fpciFullVersion, // -iW Return full compiler version
fpciCompilerOS, // -iSO Return compiler OS
fpciCompilerProcessor, // -iSP Return compiler host processor
fpciTargetOS, // -iTO Return target OS
fpciTargetProcessor // -iTP Return target processor
);
TFPCInfoTypes = set of TFPCInfoType;
TFPCInfoStrings = array[TFPCInfoType] of string;
const
fpciAll = [low(TFPCInfoType)..high(TFPCInfoType)];
type
{ TFPCConfigFileState
Store if a config file exists and its modification date }
TFPCConfigFileState = class
public
Filename: string;
FileExists: boolean;
FileDate: longint;
constructor Create(const aFilename: string;
aFileExists: boolean; aFileDate: longint);
function Equals(Other: TFPCConfigFileState; CheckDate: boolean): boolean; reintroduce;
procedure LoadFromXMLConfig(XMLConfig: TXMLConfig; const Path: string);
procedure SaveToXMLConfig(XMLConfig: TXMLConfig; const Path: string);
end;
{ TFPCConfigFileStateList
list of TFPCConfigFileState }
TFPCConfigFileStateList = class
private
fItems: TFPList;
function GetItems(Index: integer): TFPCConfigFileState;
public
constructor Create;
destructor Destroy; override;
procedure Clear;
procedure Assign(List: TFPCConfigFileStateList);
function Equals(List: TFPCConfigFileStateList; CheckDates: boolean): boolean; reintroduce;
function Add(aFilename: string; aFileExists: boolean;
aFileDate: longint): TFPCConfigFileState;
function Count: integer;
property Items[Index: integer]: TFPCConfigFileState read GetItems; default;
procedure LoadFromXMLConfig(XMLConfig: TXMLConfig; const Path: string);
procedure SaveToXMLConfig(XMLConfig: TXMLConfig; const Path: string);
end;
TFPCTargetConfigCaches = class;
{ TFPCTargetConfigCache
Storing all information (maros, search paths) of one compiler
with one specific TargetOS and TargetCPU. }
TFPCTargetConfigCache = class(TComponent)
private
FChangeStamp: integer;
public
// key
TargetOS: string; // will be passed lowercase
TargetCPU: string; // will be passed lowercase
Compiler: string; // full file name
CompilerOptions: string;
// values
CompilerDate: longint;
RealCompiler: string; // when Compiler is fpc, this is the real compiler (e.g. ppc386)
RealCompilerDate: longint;
RealTargetOS: string;
RealTargetCPU: string;
RealCompilerInPath: string; // the ppc<target> in PATH
ConfigFiles: TFPCConfigFileStateList;
UnitPaths: TStrings;
Defines: TStringToStringTree; // macro to value
Undefines: TStringToStringTree; // macro
Units: TStringToStringTree; // unit name to file name
ErrorMsg: string;
ErrorTranslatedMsg: string;
Caches: TFPCTargetConfigCaches;
HasPPUs: boolean;
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
procedure Clear; // values, not keys
function Equals(Item: TFPCTargetConfigCache;
CompareKey: boolean = true): boolean; reintroduce;
procedure Assign(Source: TPersistent); override;
procedure LoadFromXMLConfig(XMLConfig: TXMLConfig; const Path: string);
procedure SaveToXMLConfig(XMLConfig: TXMLConfig; const Path: string);
procedure LoadFromFile(Filename: string);
procedure SaveToFile(Filename: string);
function NeedsUpdate: boolean;
function Update(TestFilename: string; ExtraOptions: string = '';
const OnProgress: TDefinePoolProgress = nil): boolean;
function FindRealCompilerInPath(aTargetCPU: string; ResolveLinks: boolean): string;
function GetFPCVer(out FPCVersion, FPCRelease, FPCPatch: integer): boolean;
procedure IncreaseChangeStamp;
property ChangeStamp: integer read FChangeStamp;
end;
{ TFPCTargetConfigCaches
List of TFPCTargetConfigCache }
TFPCTargetConfigCaches = class(TComponent)
private
FChangeStamp: integer;
fItems: TAVLTree; // tree of TFPCTargetConfigCache
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
procedure Clear;
function Equals(Caches: TFPCTargetConfigCaches): boolean; reintroduce;
procedure Assign(Source: TPersistent); override;
procedure LoadFromXMLConfig(XMLConfig: TXMLConfig; const Path: string);
procedure SaveToXMLConfig(XMLConfig: TXMLConfig; const Path: string);
procedure LoadFromFile(Filename: string);
procedure SaveToFile(Filename: string);
procedure IncreaseChangeStamp;
property ChangeStamp: integer read FChangeStamp;
function Find(CompilerFilename, CompilerOptions, TargetOS, TargetCPU: string;
CreateIfNotExists: boolean): TFPCTargetConfigCache;
end;
TFPCSourceCaches = class;
{ TFPCSourceCache
All source files of one FPC source directory }
TFPCSourceCache = class(TComponent)
private
FChangeStamp: integer;
public
Directory: string;
Valid: boolean;
Files: TStringList;
Caches: TFPCSourceCaches;
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
procedure Clear;
procedure Assign(Source: TPersistent); override;
function Equals(Cache: TFPCSourceCache): boolean; reintroduce;
procedure LoadFromXMLConfig(XMLConfig: TXMLConfig; const Path: string);
procedure SaveToXMLConfig(XMLConfig: TXMLConfig; const Path: string);
procedure LoadFromFile(Filename: string);
procedure SaveToFile(Filename: string);
procedure Update(const OnProgress: TDefinePoolProgress = nil);
procedure Update(var NewFiles: TStringList); // NewFiles is used for Files! do not free NewFiles
procedure IncreaseChangeStamp;
property ChangeStamp: integer read FChangeStamp;
end;
{ TFPCSourceCaches }
TFPCSourceCaches = class(TComponent)
private
FChangeStamp: integer;
fItems: TAVLTree; // tree of TFPCSourceCacheItem
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
procedure Clear;
procedure Assign(Source: TPersistent); override;
function Equals(Caches: TFPCSourceCaches): boolean; reintroduce;
procedure LoadFromXMLConfig(XMLConfig: TXMLConfig; const Path: string);
procedure SaveToXMLConfig(XMLConfig: TXMLConfig; const Path: string);
procedure LoadFromFile(Filename: string);
procedure SaveToFile(Filename: string);
procedure IncreaseChangeStamp;
property ChangeStamp: integer read FChangeStamp;
function Find(Directory: string;
CreateIfNotExists: boolean): TFPCSourceCache;
end;
TFPCDefinesCache = class;
TFPCUnitToSrcCacheFlag = (
fuscfSrcRulesNeedUpdate,
fuscfUnitTreeNeedsUpdate
);
TFPCUnitToSrcCacheFlags = set of TFPCUnitToSrcCacheFlag;
{ TFPCUnitSetCache
Unit name to FPC source file.
Specific to one compiler, targetos, targetcpu and FPC source directory. }
TFPCUnitSetCache = class(TComponent)
private
FCaches: TFPCDefinesCache;
FChangeStamp: integer;
FCompilerFilename: string;
FCompilerOptions: string;
FFPCSourceDirectory: string;
FTargetCPU: string;
FTargetOS: string;
FConfigCache: TFPCTargetConfigCache;
fSourceCache: TFPCSourceCache;
fSourceRules: TFPCSourceRules;
fRulesStampOfConfig: integer; // fSourceCache.ChangeStamp while creation of fFPCSourceRules
fUnitToSourceTree: TStringToStringTree; // unit name to file name (maybe relative)
fUnitStampOfFiles: integer; // fSourceCache.ChangeStamp at creation of fUnitToSourceTree
fUnitStampOfRules: integer; // fSourceRules.ChangeStamp at creation of fUnitToSourceTree
fSrcDuplicates: TStringToStringTree; // unit to semicolon separated list of files
fFlags: TFPCUnitToSrcCacheFlags;
procedure SetCompilerFilename(const AValue: string);
procedure SetCompilerOptions(const AValue: string);
procedure SetFPCSourceDirectory(const AValue: string);
procedure SetTargetCPU(const AValue: string);
procedure SetTargetOS(const AValue: string);
procedure ClearConfigCache;
procedure ClearSourceCache;
protected
procedure Notification(AComponent: TComponent; Operation: TOperation);
override;
public
constructor Create(TheOwner: TComponent); override;
destructor Destroy; override;
procedure Clear;
procedure Init;
property Caches: TFPCDefinesCache read FCaches;
property CompilerFilename: string read FCompilerFilename write SetCompilerFilename;
property CompilerOptions: string read FCompilerOptions write SetCompilerOptions;
property TargetOS: string read FTargetOS write SetTargetOS; // case insensitive, will be passed lowercase
property TargetCPU: string read FTargetCPU write SetTargetCPU; // case insensitive, will be passed lowercase
property FPCSourceDirectory: string read FFPCSourceDirectory write SetFPCSourceDirectory;
function GetConfigCache(AutoUpdate: boolean): TFPCTargetConfigCache;
function GetSourceCache(AutoUpdate: boolean): TFPCSourceCache;
function GetSourceRules(AutoUpdate: boolean): TFPCSourceRules;
function GetUnitToSourceTree(AutoUpdate: boolean): TStringToStringTree; // unit name to file name (maybe relative)
function GetSourceDuplicates(AutoUpdate: boolean): TStringToStringTree; // unit to semicolon separated list of files
function GetUnitSrcFile(const AUnitName: string;
MustHavePPU: boolean = true;
SkipPPUCheckIfNoneExists: boolean = true): string;
property ChangeStamp: integer read FChangeStamp;
class function GetInvalidChangeStamp: integer;
procedure IncreaseChangeStamp;
function GetUnitSetID: string;
end;
{ TFPCDefinesCache }
TFPCDefinesCache = class(TComponent)
private
FConfigCaches: TFPCTargetConfigCaches;
FConfigCachesSaveStamp: integer;
FExtraOptions: string;
FSourceCaches: TFPCSourceCaches;
FSourceCachesSaveStamp: integer;
FTestFilename: string;
fUnitToSrcCaches: TFPList; // list of TFPCUnitSetCache
procedure SetConfigCaches(const AValue: TFPCTargetConfigCaches);
procedure SetSourceCaches(const AValue: TFPCSourceCaches);
procedure ClearUnitToSrcCaches;
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
procedure Clear;
procedure LoadFromXMLConfig(XMLConfig: TXMLConfig; const Path: string);
procedure SaveToXMLConfig(XMLConfig: TXMLConfig; const Path: string);
procedure LoadFromFile(Filename: string);
procedure SaveToFile(Filename: string);
function NeedsSave: boolean;
property SourceCaches: TFPCSourceCaches read FSourceCaches write SetSourceCaches;
property ConfigCaches: TFPCTargetConfigCaches read FConfigCaches write SetConfigCaches;
property TestFilename: string read FTestFilename write FTestFilename; // an empty file to test the compiler, will be auto created
property ExtraOptions: string read FExtraOptions write FExtraOptions; // additional compiler options not used as key
function FindUnitSet(const CompilerFilename, TargetOS, TargetCPU,
Options, FPCSrcDir: string;
CreateIfNotExists: boolean): TFPCUnitSetCache;
function FindUnitSetWithID(const UnitSetID: string; out Changed: boolean;
CreateIfNotExists: boolean): TFPCUnitSetCache;
function GetUnitSetID(CompilerFilename, TargetOS, TargetCPU, Options,
FPCSrcDir: string; ChangeStamp: integer): string;
procedure ParseUnitSetID(const ID: string; out CompilerFilename,
TargetOS, TargetCPU, Options, FPCSrcDir: string;
out ChangeStamp: integer);
end;
function DefineActionNameToAction(const s: string): TDefineAction;
function DefineTemplateFlagsToString(Flags: TDefineTemplateFlags): string;
function GetDefaultSrcOSForTargetOS(const TargetOS: string): string;
function GetDefaultSrcOS2ForTargetOS(const TargetOS: string): string;
function GetDefaultSrcCPUForTargetCPU(const TargetCPU: string): string;
procedure SplitLazarusCPUOSWidgetCombo(const Combination: string;
var CPU, OS, WidgetSet: string);
function GetCompiledTargetOS: string;
function GetCompiledTargetCPU: string;
function GetDefaultCompilerFilename(const TargetCPU: string = ''): string;
function GetFPCTargetOS(TargetOS: string): string;
function GetFPCTargetCPU(TargetCPU: string): string;
// functions to quickly setup some defines
function CreateDefinesInDirectories(const SourcePaths, FlagName: string
): TDefineTemplate;
function GatherFiles(Directory, ExcludeDirMask, IncludeFileMask: string;
MaxLevel: integer; const OnProgress: TDefinePoolProgress): TStringList; // thread safe
function GatherFilesInFPCSources(Directory: string;
const OnProgress: TDefinePoolProgress): TStringList; // thread safe
function MakeRelativeFileList(Files: TStrings; out BaseDir: string): TStringList;
function Compress1FileList(Files: TStrings): TStringList;
function Decompress1FileList(Files: TStrings): TStringList;
function RunTool(const Filename, Params: string;
WorkingDirectory: string = ''): TStringList;
function ParseFPCInfo(FPCInfo: string; InfoTypes: TFPCInfoTypes;
out Infos: TFPCInfoStrings): boolean;
function RunFPCInfo(const CompilerFilename: string;
InfoTypes: TFPCInfoTypes; const Options: string =''): string;
function SplitFPCVersion(const FPCVersionString: string;
out FPCVersion, FPCRelease, FPCPatch: integer): boolean;
function ParseFPCVerbose(List: TStrings; // fpc -va output
out ConfigFiles: TStrings; // prefix '-' for file not found, '+' for found and read
out CompilerFilename: string; // what compiler is used by fpc
out UnitPaths: TStrings; // unit search paths
out Defines, Undefines: TStringToStringTree): boolean;
function RunFPCVerbose(const CompilerFilename, TestFilename: string;
out ConfigFiles: TStrings;
out TargetCompilerFilename: string;
out UnitPaths: TStrings;
out Defines, Undefines: TStringToStringTree;
const Options: string = ''): boolean;
function GatherUnitsInSearchPaths(SearchPaths: TStrings;
const OnProgress: TDefinePoolProgress): TStringToStringTree; // unit names to full file name
procedure AdjustFPCSrcRulesForPPUPaths(Units: TStringToStringTree;
Rules: TFPCSourceRules);
function GatherUnitsInFPCSources(Files: TStringList;
TargetOS: string = ''; TargetCPU: string = '';
Duplicates: TStringToStringTree = nil; // unit to semicolon separated list of files
Rules: TFPCSourceRules = nil;
const DebugUnitName: string = ''): TStringToStringTree;
function CreateFPCTemplate(Config: TFPCTargetConfigCache;
Owner: TObject): TDefineTemplate; overload;
function CreateFPCTemplate(Config: TFPCUnitSetCache;
Owner: TObject): TDefineTemplate; overload;
function CreateFPCSrcTemplate(Config: TFPCUnitSetCache;
Owner: TObject): TDefineTemplate; overload;
procedure CheckPPUSources(PPUFiles, // unitname to filename
UnitToSource, // unitname to file name
UnitToDuplicates: TStringToStringTree; // unitname to semicolon separated list of files
var Duplicates, Missing: TStringToStringTree);
procedure LoadFPCCacheFromFile(Filename: string;
var Configs: TFPCTargetConfigCaches; var Sources: TFPCSourceCaches);
procedure SaveFPCCacheToFile(Filename: string;
Configs: TFPCTargetConfigCaches; Sources: TFPCSourceCaches);
procedure ReadMakefileFPC(const Filename: string; List: TStrings);
procedure ParseMakefileFPC(const Filename, SrcOS: string;
var Dirs, SubDirs: string);
function CompareFPCSourceRulesViaFilenameStart(Rule1, Rule2: Pointer): integer;
function CompareFPCTargetConfigCacheItems(CacheItem1, CacheItem2: Pointer): integer;
function CompareFPCSourceCacheItems(CacheItem1, CacheItem2: Pointer): integer;
function CompareDirectoryWithFPCSourceCacheItem(AString, CacheItem: Pointer): integer;
implementation
type
TUnitNameLink = class
public
Unit_Name: string;
Filename: string;
ConflictFilename: string;
MacroCount: integer;
UsedMacroCount: integer;
Score: integer;
end;
function CompareUnitNameLinks(Link1, Link2: Pointer): integer;
var
UnitLink1: TUnitNameLink absolute Link1;
UnitLink2: TUnitNameLink absolute Link2;
begin
Result:=CompareNames(UnitLink1.Unit_Name,UnitLink2.Unit_Name);
end;
function CompareUnitNameWithUnitNameLink(Name, Link: Pointer): integer;
var
UnitLink: TUnitNameLink absolute Link;
begin
Result:=CompareNames(AnsiString(Name),UnitLink.Unit_Name);
end;
// some useful functions
function GatherFiles(Directory, ExcludeDirMask, IncludeFileMask: string;
MaxLevel: integer; const OnProgress: TDefinePoolProgress): TStringList;
{ ExcludeDirMask: check FilenameIsMatching vs the short file name of a directory
IncludeFileMask: check FilenameIsMatching vs the short file name of a file
}
var
Files: TAVLTree; // tree of ansistring
FileCount: integer;
Abort: boolean;
procedure Add(Filename: string);
var
s: String;
begin
if Filename='' then exit;
// increase refcount
s:=Filename;
// add
Files.Add(PChar(s));
// keep refcount
Pointer(s):=nil;
end;
procedure Search(CurDir: string; Level: integer);
var
FileInfo: TSearchRec;
ShortFilename: String;
Filename: String;
begin
if Level>MaxLevel then exit;
//DebugLn(['Search CurDir=',CurDir]);
if FindFirstUTF8(Directory+CurDir+FileMask,faAnyFile,FileInfo)=0 then begin
repeat
inc(FileCount);
if (FileCount mod 100=0) and Assigned(OnProgress) then begin
OnProgress(nil,0,-1,'Scanned files: '+IntToStr(FileCount),Abort);
if Abort then break;
end;
ShortFilename:=FileInfo.Name;
if (ShortFilename='') or (ShortFilename='.') or (ShortFilename='..') then
continue;
//debugln(['Search ShortFilename=',ShortFilename,' IsDir=',(FileInfo.Attr and faDirectory)>0]);
Filename:=CurDir+ShortFilename;
if (FileInfo.Attr and faDirectory)>0 then begin
// directory
if (ExcludeDirMask='')
or (not FilenameIsMatching(ExcludeDirMask,ShortFilename,true))
then begin
Search(Filename+PathDelim,Level+1);
if Abort then break;
end else begin
//DebugLn(['Search DIR MISMATCH ',Filename]);
end;
end else begin
// file
if (IncludeFileMask='')
or FilenameIsMatching(IncludeFileMask,ShortFilename,true) then begin
//DebugLn(['Search ADD ',Filename]);
Add(Filename);
end else begin
//DebugLn(['Search MISMATCH ',Filename]);
end;
end;
until FindNextUTF8(FileInfo)<>0;
end;
FindCloseUTF8(FileInfo);
end;
var
Node: TAVLTreeNode;
s: String;
NodeMgr: TAVLTreeNodeMemManager;
begin
Result:=nil;
Files:=TAVLTree.Create(@CompareAnsiStringFilenames);
NodeMgr:=TAVLTreeNodeMemManager.Create;
Files.SetNodeManager(NodeMgr);
Abort:=false;
try
FileCount:=0;
Directory:=CleanAndExpandDirectory(Directory);
Search('',0);
finally
if not Abort then
Result:=TStringList.Create;
Node:=Files.FindLowest;
while Node<>nil do begin
Pointer(s):=Node.Data;
if Result<>nil then Result.Add(s);
s:='';
Node:=Files.FindSuccessor(Node);
end;
FreeAndNil(Files);
FreeAndNil(NodeMgr);
end;
end;
function GatherFilesInFPCSources(Directory: string;
const OnProgress: TDefinePoolProgress): TStringList;
begin
Result:=GatherFiles(Directory,'{.svn,CVS}',
'{*.pas,*.pp,*.p,*.inc,Makefile.fpc}',8,OnProgress);
end;
function MakeRelativeFileList(Files: TStrings; out BaseDir: string
): TStringList;
var
BaseDirLen: Integer;
i: Integer;
Filename: string;
begin
BaseDir:='';
Result:=TStringList.Create;
if (Files=nil) or (Files.Count=0) then exit;
Result.Assign(Files);
// delete empty lines
for i:=Result.Count-1 downto 0 do
if Result[i]='' then Result.Delete(i);
if Result.Count=0 then exit;
// find shortest common BaseDir
BaseDir:=ChompPathDelim(ExtractFilepath(Result[0]));
BaseDirLen:=length(BaseDir);
for i:=1 to Result.Count-1 do begin
Filename:=Result[i];
while (BaseDirLen>0) do begin
if (BaseDirLen<=length(Filename))
and ((BaseDirLen=length(Filename)) or (Filename[BaseDirLen+1]=PathDelim))
and (CompareFilenames(BaseDir,copy(Filename,1,BaseDirLen))=0) then
break;
BaseDir:=ChompPathDelim(ExtractFilePath(copy(BaseDir,1,BaseDirLen-1)));
BaseDirLen:=length(BaseDir);
end;
end;
// create relative paths
if BaseDir<>'' then
for i:=0 to Result.Count-1 do begin
Filename:=Result[i];
Filename:=copy(Filename,BaseDirLen+1,length(Filename));
if (Filename<>'') and (Filename[1]=PathDelim) then
System.Delete(Filename,1,1);
Result[i]:=Filename;
end;
end;
function Compress1FileList(Files: TStrings): TStringList;
var
i: Integer;
Filename: string;
LastFilename: String;
p: Integer;
begin
Result:=TStringList.Create;
LastFilename:='';
for i:=0 to Files.Count-1 do begin
Filename:=TrimFilename(Files[i]);
p:=1;
while (p<=length(Filename)) and (p<=length(LastFilename))
and (Filename[p]=LastFilename[p]) do
inc(p);
Result.Add(IntToStr(p-1)+':'+copy(Filename,p,length(Filename)));
LastFilename:=Filename;
end;
end;
function Decompress1FileList(Files: TStrings): TStringList;
var
LastFilename: String;
i: Integer;
Filename: string;
p: Integer;
Same: Integer;
begin
Result:=TStringList.Create;
LastFilename:='';
try
for i:=0 to Files.Count-1 do begin
Filename:=Files[i];
p:=1;
Same:=0;
while (p<=length(Filename)) and (Filename[p] in ['0'..'9']) do begin
Same:=Same*10+ord(Filename[p])-ord('0');
inc(p);
end;
inc(p);
Filename:=copy(LastFilename,1,Same)+copy(Filename,p,length(Filename));
Result.Add(Filename);
LastFilename:=Filename;
end;
except
end;
end;
function RunTool(const Filename, Params: string;
WorkingDirectory: string): TStringList;
var
buf: string;
TheProcess: TProcess;
OutputLine: String;
OutLen: Integer;
LineStart, i: Integer;
CmdLine: String;
begin
if not FileIsExecutable(Filename) then exit(nil);
Result:=TStringList.Create;
try
debugln(['RunTool ',Filename,' ',Params]);
TheProcess := TProcess.Create(nil);
try
CmdLine:=Filename;
if (System.Pos(' ',CmdLine)>0) and (CmdLine[1]<>'"') then
CmdLine:='"'+CmdLine+'"';
if Params<>'' then
CmdLine:=CmdLine+' '+Params;
//DebugLn(['RunTool ',Params]);
TheProcess.CommandLine := UTF8ToSys(CmdLine);
TheProcess.Options:= [poUsePipes, poStdErrToOutPut];
TheProcess.ShowWindow := swoHide;
TheProcess.CurrentDirectory:=UTF8ToSys(WorkingDirectory);
TheProcess.Execute;
OutputLine:='';
SetLength(buf,4096);
repeat
if (TheProcess.Output<>nil) then begin
OutLen:=TheProcess.Output.Read(Buf[1],length(Buf));
end else
OutLen:=0;
LineStart:=1;
i:=1;
while i<=OutLen do begin
if Buf[i] in [#10,#13] then begin
OutputLine:=OutputLine+copy(Buf,LineStart,i-LineStart);
Result.Add(OutputLine);
OutputLine:='';
if (i<OutLen) and (Buf[i+1] in [#10,#13]) and (Buf[i]<>Buf[i+1])
then
inc(i);
LineStart:=i+1;
end;
inc(i);
end;
OutputLine:=copy(Buf,LineStart,OutLen-LineStart+1);
until OutLen=0;
TheProcess.WaitOnExit;
finally
TheProcess.Free;
end;
except
FreeAndNil(Result);
end;
end;
function ParseFPCInfo(FPCInfo: string; InfoTypes: TFPCInfoTypes;
out Infos: TFPCInfoStrings): boolean;
var
i: TFPCInfoType;
p: PChar;
StartPos: PChar;
begin
Result:=false;
if FPCInfo='' then exit(InfoTypes=[]);
if copy(FPCInfo,1,6)='Error:' then exit(false);
p:=PChar(FPCInfo);
for i:=low(TFPCInfoType) to high(TFPCInfoType) do begin
if not (i in InfoTypes) then continue;
StartPos:=p;
while (p^<>' ') do inc(p);
if p=StartPos then exit(false);
Infos[i]:=copy(FPCInfo,StartPos-PChar(FPCInfo)+1,p-StartPos);
// skip space
inc(p);
end;
Result:=true;
end;
function RunFPCInfo(const CompilerFilename: string;
InfoTypes: TFPCInfoTypes; const Options: string): string;
var
Params: String;
List: TStringList;
begin
Result:='';
Params:='';
if fpciCompilerDate in InfoTypes then Params:=Params+'D';
if fpciShortVersion in InfoTypes then Params:=Params+'V';
if fpciFullVersion in InfoTypes then Params:=Params+'W';
if fpciCompilerOS in InfoTypes then Params:=Params+'SO';
if fpciCompilerProcessor in InfoTypes then Params:=Params+'SP';
if fpciTargetOS in InfoTypes then Params:=Params+'TO';
if fpciTargetProcessor in InfoTypes then Params:=Params+'TP';
if Params='' then exit;
Params:='-i'+Params;
if Options<>'' then
Params:=Params+' '+Options;
List:=nil;
try
List:=RunTool(CompilerFilename,Params);
if (List=nil) or (List.Count<1) then exit;
Result:=List[0];
if copy(Result,1,6)='Error:' then Result:='';
finally
List.free;
end;
end;
function SplitFPCVersion(const FPCVersionString: string; out FPCVersion,
FPCRelease, FPCPatch: integer): boolean;
// for example 2.5.1
var
p: PChar;
function ReadWord(out v: integer): boolean;
var
Empty: Boolean;
begin
v:=0;
Empty:=true;
while (p^ in ['0'..'9']) do begin
if v>10000 then exit(false);
v:=v*10+ord(p^)-ord('0');
inc(p);
Empty:=false;
end;
Result:=not Empty;
end;
begin
Result:=false;
FPCVersion:=0;
FPCRelease:=0;
FPCPatch:=0;
if FPCVersionString='' then exit;
p:=PChar(FPCVersionString);
if not ReadWord(FPCVersion) then exit;
if (p^<>'.') then exit;
inc(p);
if not ReadWord(FPCRelease) then exit;
if (p^<>'.') then exit;
inc(p);
if not ReadWord(FPCPatch) then exit;
Result:=true;
end;
function ParseFPCVerbose(List: TStrings; out ConfigFiles: TSTrings;
out CompilerFilename: string; out UnitPaths: TStrings;
out Defines, Undefines: TStringToStringTree): boolean;
procedure UndefineSymbol(const MacroName: string);
begin
//DebugLn(['UndefineSymbol ',MacroName]);
Defines.Remove(MacroName);
Undefines[MacroName]:='';
end;
procedure DefineSymbol(const MacroName, Value: string);
begin
//DebugLn(['DefineSymbol ',MacroName]);
Undefines.Remove(MacroName);
Defines[MacroName]:=Value;
end;
procedure ProcessOutputLine(Line: string);
var
SymbolName, SymbolValue, UpLine, NewPath: string;
i, len, CurPos: integer;
Filename: String;
begin
//DebugLn(['ProcessOutputLine ',Line]);
len := length(Line);
if len <= 6 then Exit; // shortest match
CurPos := 1;
// skip timestamp e.g. [0.306]
if Line[CurPos] = '[' then begin
repeat
inc(CurPos);
if CurPos > len then Exit;
until line[CurPos] = ']';
Inc(CurPos, 2); // skip space too
if len - CurPos < 6 then Exit; // shortest match
end;
UpLine:=UpperCaseStr(Line);
//DebugLn(['ProcessOutputLine ',Line]);
case UpLine[CurPos] of
'M':
if StrLComp(@UpLine[CurPos], 'MACRO ', 6) = 0 then begin
// skip keyword macro
Inc(CurPos, 6);
if (StrLComp(@UpLine[CurPos], 'DEFINED: ', 9) = 0) then begin
Inc(CurPos, 9);
SymbolName:=copy(UpLine, CurPos, len);
DefineSymbol(SymbolName,'');
Exit;
end;
if (StrLComp(@UpLine[CurPos], 'UNDEFINED: ', 11) = 0) then begin
Inc(CurPos, 11);
SymbolName:=copy(UpLine,CurPos,len);
UndefineSymbol(SymbolName);
Exit;
end;
// MACRO something...
i := CurPos;
while (i <= len) and (Line[i]<>' ') do inc(i);
SymbolName:=copy(UpLine,CurPos,i-CurPos);
CurPos := i + 1; // skip space
if StrLComp(@UpLine[CurPos], 'SET TO ', 7) = 0 then begin
Inc(CurPos, 7);
SymbolValue:=copy(Line, CurPos, len);
DefineSymbol(SymbolName, SymbolValue);
end;
end;
'U':
if (StrLComp(@UpLine[CurPos], 'USING UNIT PATH: ', 17) = 0) then begin
Inc(CurPos, 17);
NewPath:=SetDirSeparators(copy(Line,CurPos,len));
if not FilenameIsAbsolute(NewPath) then
NewPath:=ExpandFileNameUTF8(AnsiToUtf8(NewPath));
NewPath:=ChompPathDelim(TrimFilename(NewPath));
{$IFDEF VerboseFPCSrcScan}
DebugLn('Using unit path: "',NewPath,'"');
{$ENDIF}
UnitPaths.Add(NewPath);
end;
'C':
if StrLComp(@UpLine[CurPos], 'CONFIGFILE SEARCH: ', 19) = 0 then
begin
// skip keywords
Inc(CurPos, 19);
Filename:=SetDirSeparators(copy(Line,CurPos,length(Line)));
ConfigFiles.Add('-'+Filename);
end else if StrLComp(@UpLine[CurPos], 'COMPILER: ', 10) = 0 then begin
// skip keywords
Inc(CurPos, 10);
CompilerFilename:=copy(Line,CurPos,length(Line));
end;
'R':
if StrLComp(@UpLine[CurPos], 'READING OPTIONS FROM FILE ', 26) = 0 then
begin
// skip keywords
Inc(CurPos, 26);
Filename:=SetDirSeparators(copy(Line,CurPos,length(Line)));
if (ConfigFiles.Count>0)
and (ConfigFiles[ConfigFiles.Count-1]='-'+Filename) then
ConfigFiles.Delete(ConfigFiles.Count-1);
ConfigFiles.Add('+'+copy(Line,CurPos,length(Line)));
end;
end;
end;
var
i: Integer;
begin
Result:=false;
ConfigFiles:=TStringList.Create;
CompilerFilename:='';
UnitPaths:=TStringList.Create;
Defines:=TStringToStringTree.Create(false);
Undefines:=TStringToStringTree.Create(false);
try
for i:=0 to List.Count-1 do
ProcessOutputLine(List[i]);
Result:=true;
finally
if not Result then begin
FreeAndNil(ConfigFiles);
FreeAndNil(UnitPaths);
FreeAndNil(Undefines);
FreeAndNil(Defines);
end;
end;
end;
function RunFPCVerbose(const CompilerFilename, TestFilename: string;
out ConfigFiles: TStrings; out TargetCompilerFilename: string;
out UnitPaths: TStrings; out Defines, Undefines: TStringToStringTree;
const Options: string): boolean;
var
Params: String;
Filename: String;
WorkDir: String;
List: TStringList;
fs: TFileStream;
begin
Result:=false;
ConfigFiles:=nil;
TargetCompilerFilename:='';
UnitPaths:=nil;
Defines:=nil;
Undefines:=nil;
// create empty file
try
fs:=TFileStream.Create(UTF8ToSys(TestFilename),fmCreate);
fs.Free;
except
debugln(['RunFPCVerbose unable to create test file '+TestFilename]);
exit;
end;
Params:='-va';
if Options<>'' then
Params:=Params+' '+Options;
Filename:=ExtractFileName(TestFilename);
WorkDir:=ExtractFilePath(TestFilename);
Params:=Params+' '+Filename;
List:=nil;
try
//DebugLn(['RunFPCVerbose ',CompilerFilename,' ',Params,' ',WorkDir]);
List:=RunTool(CompilerFilename,Params,WorkDir);
if (List=nil) or (List.Count=0) then begin
debugln(['RunFPCVerbose failed: ',CompilerFilename,' ',Params]);
exit;
end;
Result:=ParseFPCVerbose(List,ConfigFiles,TargetCompilerFilename,
UnitPaths,Defines,Undefines);
finally
List.Free;
DeleteFileUTF8(TestFilename);
end;
end;
function GatherUnitsInSearchPaths(SearchPaths: TStrings;
const OnProgress: TDefinePoolProgress): TStringToStringTree;
{ returns a stringtree,
where name is unitname and value is the full file name
SearchPaths are searched from last to start
first found wins
pas, pp, p wins vs ppu
}
var
i: Integer;
Directory: String;
FileCount: Integer;
Abort: boolean;
FileInfo: TSearchRec;
ShortFilename: String;
Filename: String;
Ext: String;
Unit_Name: String;
begin
Result:=TStringToStringTree.Create(false);
FileCount:=0;
Abort:=false;
for i:=SearchPaths.Count-1 downto 0 do begin
Directory:=CleanAndExpandDirectory(SearchPaths[i]);
if FindFirstUTF8(Directory+FileMask,faAnyFile,FileInfo)=0 then begin
repeat
inc(FileCount);
if (FileCount mod 100=0) and Assigned(OnProgress) then begin
OnProgress(nil, 0, -1, Format(ctsScannedFiles, [IntToStr(FileCount)]
), Abort);
if Abort then break;
end;
ShortFilename:=FileInfo.Name;
if (ShortFilename='') or (ShortFilename='.') or (ShortFilename='..') then
continue;
//debugln(['GatherUnitsInSearchPaths ShortFilename=',ShortFilename,' IsDir=',(FileInfo.Attr and faDirectory)>0]);
Filename:=Directory+ShortFilename;
Ext:=LowerCase(ExtractFileExt(ShortFilename));
if (Ext='.pas') or (Ext='.pp') or (Ext='.p') or (Ext='.ppu') then begin
Unit_Name:=ExtractFileNameOnly(Filename);
if (not Result.Contains(Unit_Name))
or ((Ext<>'.ppu') and (CompareFileExt(Result[Unit_Name],'ppu',false)=0))
then
Result[Unit_Name]:=Filename;
end;
until FindNextUTF8(FileInfo)<>0;
end;
FindCloseUTF8(FileInfo);
end;
end;
procedure AdjustFPCSrcRulesForPPUPaths(Units: TStringToStringTree;
Rules: TFPCSourceRules);
var
Filename: string;
Rule: TFPCSourceRule;
begin
if Units.CaseSensitive then
raise Exception.Create('AdjustFPCSrcRulesForPPUPaths Units is case sensitive');
// check unit httpd
Filename:=Units['httpd'];
if Filename<>'' then begin
Filename:=ChompPathDelim(ExtractFilePath(Filename));
Rule:=Rules.Add('packages/'+ExtractFileName(Filename));
Rule.Score:=10;
Rule.Targets:='*';
//DebugLn(['AdjustFPCSrcRulesForPPUPaths ',Rule.Filename,' ',Filename]);
end;
end;
function GatherUnitsInFPCSources(Files: TStringList; TargetOS: string;
TargetCPU: string; Duplicates: TStringToStringTree;
Rules: TFPCSourceRules; const DebugUnitName: string): TStringToStringTree;
{ returns tree unit name to file name (maybe relative)
}
function CountMatches(Targets, aTxt: PChar): integer;
// check how many of the comma separated words in Targets are in words of aTxt
var
TxtStartPos: PChar;
TargetPos: PChar;
TxtPos: PChar;
begin
Result:=0;
if (aTxt=nil) or (Targets=nil) then exit;
TxtStartPos:=aTxt;
while true do begin
while (not (IsIdentChar[TxtStartPos^])) do begin
if TxtStartPos^=#0 then exit;
inc(TxtStartPos);
end;
//DebugLn(['CountMatches TxtStartPos=',TxtStartPos]);
TargetPos:=Targets;
repeat
while (TargetPos^=',') do inc(TargetPos);
if TargetPos^=#0 then break;
//DebugLn(['CountMatches TargetPos=',TargetPos]);
TxtPos:=TxtStartPos;
while (TxtPos^=TargetPos^) and (not (TargetPos^ in [#0,','])) do begin
inc(TargetPos);
inc(TxtPos);
end;
//DebugLn(['CountMatches Test TargetPos=',TargetPos,' TxtPos=',TxtPos]);
if (TargetPos^ in [#0,',']) and (not IsIdentChar[TxtPos^]) then begin
// the target fits
//DebugLn(['CountMatches FITS']);
inc(Result);
end;
// try next target
while not (TargetPos^ in [#0,',']) do inc(TargetPos);
until TargetPos^=#0;
// next txt word
while IsIdentChar[TxtStartPos^] do inc(TxtStartPos);
end;
end;
var
i: Integer;
Filename: string;
Links: TAVLTree;
Unit_Name: String;
LastDirectory: String;
LastDirScore: Integer;
Directory: String;
DirScore: LongInt;
Node: TAVLTreeNode;
Link: TUnitNameLink;
TargetRules: TAVLTree;
Score: LongInt;
Targets: string;
begin
Result:=nil;
if (Files=nil) or (Files.Count=0) then exit;
if (Duplicates<>nil) and Duplicates.CaseSensitive then
raise Exception.Create('GatherUnitsInFPCSources: Duplicates case sensitive');
// get default targets
if Rules=nil then Rules:=DefaultFPCSourceRules;
Targets:=Rules.GetDefaultTargets(TargetOS,TargetOS);
TargetRules:=nil;
Links:=TAVLTree.Create(@CompareUnitNameLinks);
try
// get Score rules for duplicate units
Rules.GetRulesForTargets(Targets,TargetRules);
//DebugLn(['GatherUnitsInFPCSources ',Rules.GetScore('packages/h',TargetRules)]);
//exit;
if (TargetRules<>nil) and (TargetRules.Count=0) then
FreeAndNil(TargetRules);
LastDirectory:='';
LastDirScore:=0;
for i:=0 to Files.Count-1 do begin
Filename:=Files[i];
if (CompareFileExt(Filename,'PAS',false)=0)
or (CompareFileExt(Filename,'PP',false)=0)
or (CompareFileExt(Filename,'P',false)=0)
then begin
if CompareFilenameOnly(PChar(Filename),length(Filename),'fpmake',6,true)=0
then
continue; // skip the fpmake.pp files
// Filename is a pascal unit source
Directory:=ExtractFilePath(Filename);
if LastDirectory=Directory then begin
// same directory => reuse directory Score
DirScore:=LastDirScore;
end else begin
// a new directory => recompute directory score
// default heuristic: add one point for every target in directory
DirScore:=CountMatches(PChar(Targets),PChar(Directory));
end;
Score:=DirScore;
// apply target rules
if TargetRules<>nil then
inc(Score,Rules.GetScore(Filename,TargetRules));
// add or update unitlink
Unit_Name:=ExtractFileNameOnly(Filename);
Node:=Links.FindKey(Pointer(Unit_Name),@CompareUnitNameWithUnitNameLink);
if Node<>nil then begin
// duplicate unit
Link:=TUnitNameLink(Node.Data);
if Link.Score<Score then begin
// found a better unit
if (DebugUnitName<>'') and (SysUtils.CompareText(Unit_Name,DebugUnitName)=0)
then
debugln(['GatherUnitsInFPCSources UnitName=',Unit_Name,' File=',Filename,' Score=',Score,' => better than ',Link.Score]);
Link.Unit_Name:=Unit_Name;
Link.Filename:=Filename;
Link.ConflictFilename:='';
Link.Score:=Score;
end else if Link.Score=Score then begin
// unit with same Score => maybe a conflict
// be deterministic and choose the highest
if (DebugUnitName<>'') and (SysUtils.CompareText(Unit_Name,DebugUnitName)=0)
then
debugln(['GatherUnitsInFPCSources UnitName=',Unit_Name,' File=',Filename,' Score=',Score,' => duplicate']);
if CompareStr(Filename,Link.Filename)>0 then begin
if Link.ConflictFilename<>'' then
Link.ConflictFilename:=Link.ConflictFilename+';'+Link.Filename
else
Link.ConflictFilename:=Link.Filename;
Link.Filename:=Filename;
end else begin
Link.ConflictFilename:=Link.ConflictFilename+';'+Filename;
end;
end;
end else begin
// new unit source found => add to list
if (DebugUnitName<>'') and (SysUtils.CompareText(Unit_Name,DebugUnitName)=0)
then
debugln(['GatherUnitsInFPCSources UnitName=',Unit_Name,' File=',Filename,' Score=',Score]);
Link:=TUnitNameLink.Create;
Link.Unit_Name:=Unit_Name;
Link.Filename:=Filename;
Link.Score:=Score;
Links.Add(Link);
end;
LastDirectory:=Directory;
LastDirScore:=DirScore;
end;
end;
Result:=TStringToStringTree.Create(false);
Node:=Links.FindLowest;
while Node<>Nil do begin
Link:=TUnitNameLink(Node.Data);
Result[Link.Unit_Name]:=Link.Filename;
if (Link.ConflictFilename<>'') and (Link.Score>0) then begin
//DebugLn(['GatherUnitsInFPCSources Ambiguous: ',Link.Score,' ',Link.Filename,' ',Link.ConflictFilename]);
if Duplicates<>nil then
Duplicates[Link.Unit_Name]:=Link.Filename+';'+Link.ConflictFilename;
end;
Node:=Links.FindSuccessor(Node);
end;
finally
TargetRules.Free;
Links.FreeAndClear;
Links.Free;
end;
end;
function CreateFPCTemplate(Config: TFPCTargetConfigCache; Owner: TObject
): TDefineTemplate;
var
Node: TAVLTreeNode;
StrItem: PStringToStringTreeItem;
NewDefTempl: TDefineTemplate;
TargetOS: String;
SrcOS: String;
SrcOS2: String;
TargetCPU: String;
begin
Result:=TDefineTemplate.Create(StdDefTemplFPC,
ctsFreePascalCompilerInitialMacros,'','',da_Block);
// define #TargetOS
TargetOS:=Config.TargetOS;
NewDefTempl:=TDefineTemplate.Create('Define TargetOS',
ctsDefaultFPCTargetOperatingSystem,
ExternalMacroStart+'TargetOS',TargetOS,da_DefineRecurse);
Result.AddChild(NewDefTempl);
// define #SrcOS
SrcOS:=GetDefaultSrcOSForTargetOS(TargetOS);
if SrcOS='' then SrcOS:=TargetOS;
NewDefTempl:=TDefineTemplate.Create('Define SrcOS',
ctsDefaultFPCSourceOperatingSystem,
ExternalMacroStart+'SrcOS',SrcOS,da_DefineRecurse);
Result.AddChild(NewDefTempl);
// define #SrcOS2
SrcOS2:=GetDefaultSrcOS2ForTargetOS(TargetOS);
if SrcOS2='' then SrcOS2:=TargetOS;
NewDefTempl:=TDefineTemplate.Create('Define SrcOS2',
ctsDefaultFPCSource2OperatingSystem,
ExternalMacroStart+'SrcOS2',SrcOS2,da_DefineRecurse);
Result.AddChild(NewDefTempl);
// define #TargetProcessor
TargetCPU:=Config.TargetCPU;
NewDefTempl:=TDefineTemplate.Create('Define TargetProcessor',
ctsDefaultFPCTargetProcessor,
ExternalMacroStart+'TargetProcessor',TargetCPU,
da_DefineRecurse);
Result.AddChild(NewDefTempl);
if Config.Defines<>nil then begin
Node:=Config.Defines.Tree.FindLowest;
while Node<>nil do begin
StrItem:=PStringToStringTreeItem(Node.Data);
NewDefTempl:=TDefineTemplate.Create('Define '+StrItem^.Name,
'Macro',StrItem^.Name,StrItem^.Value,da_DefineRecurse);
Result.AddChild(NewDefTempl);
Node:=Config.Defines.Tree.FindSuccessor(Node);
end;
end;
if Config.Undefines<>nil then begin
Node:=Config.Defines.Tree.FindLowest;
while Node<>nil do begin
StrItem:=PStringToStringTreeItem(Node.Data);
NewDefTempl:=TDefineTemplate.Create('Undefine '+StrItem^.Name,
'Macro',StrItem^.Name,'',da_UndefineRecurse);
Result.AddChild(NewDefTempl);
Node:=Config.Defines.Tree.FindSuccessor(Node);
end;
end;
Result.SetFlags([dtfAutoGenerated],[],false);
Result.SetDefineOwner(Owner,true);
end;
function CreateFPCTemplate(Config: TFPCUnitSetCache; Owner: TObject
): TDefineTemplate; overload;
begin
Result:=CreateFPCTemplate(Config.GetConfigCache(false),Owner);
Result.AddChild(TDefineTemplate.Create('UnitSet','UnitSet identifier',
UnitSetMacroName,Config.GetUnitSetID,da_DefineRecurse));
end;
function CreateFPCSrcTemplate(Config: TFPCUnitSetCache; Owner: TObject
): TDefineTemplate;
var
Dir, SrcOS, SrcOS2, TargetProcessor,
IncPathMacro: string;
DS: char; // dir separator
function d(const Filenames: string): string;
begin
Result:=SetDirSeparators(Filenames);
end;
procedure AddProcessorTypeDefine(ParentDefTempl: TDefineTemplate);
// some FPC source files expects defines 'i386' instead of 'CPUi386'
// define them automatically with IF..THEN constructs
var
i: Integer;
CPUName: String;
IfTemplate: TDefineTemplate;
begin
// FPC defines CPUxxx defines (e.g. CPUI386, CPUPOWERPC).
// These defines are created by the compiler depending
// on xxx defines (i386, powerpc).
// Create:
// IF CPUi386 then define i386
// IF CPUpowerpc then define powerpc
// ...
for i:=Low(FPCProcessorNames) to high(FPCProcessorNames) do begin
CPUName:=FPCProcessorNames[i];
IfTemplate:=TDefineTemplate.Create('IFDEF CPU'+CPUName,
'IFDEF CPU'+CPUName,'CPU'+CPUName,'',da_IfDef);
IfTemplate.AddChild(TDefineTemplate.Create('DEFINE '+CPUName,
'DEFINE '+CPUName,CPUName,'',da_DefineRecurse));
ParentDefTempl.AddChild(IfTemplate);
end;
end;
procedure AddSrcOSDefines(ParentDefTempl: TDefineTemplate);
var
IfTargetOSIsNotSrcOS: TDefineTemplate;
RTLSrcOSDir: TDefineTemplate;
IfTargetOSIsNotSrcOS2: TDefineTemplate;
RTLSrcOS2Dir: TDefineTemplate;
begin
// if TargetOS<>SrcOS
IfTargetOSIsNotSrcOS:=TDefineTemplate.Create(
'IF TargetOS<>SrcOS',
ctsIfTargetOSIsNotSrcOS,'',''''+TargetOSMacro+'''<>'''+SrcOS+'''',da_If);
// rtl/$(#SrcOS)
RTLSrcOSDir:=TDefineTemplate.Create('SrcOS',SrcOS,'',
SrcOS,da_Directory);
IfTargetOSIsNotSrcOS.AddChild(RTLSrcOSDir);
RTLSrcOSDir.AddChild(TDefineTemplate.Create('Include Path',
'include path',
ExternalMacroStart+'IncPath',IncPathMacro+';inc',
da_Define));
RTLSrcOSDir.AddChild(TDefineTemplate.Create('Include Path',
'include path to TargetProcessor directories',
ExternalMacroStart+'IncPath',IncPathMacro+';'+TargetProcessor,
da_Define));
ParentDefTempl.AddChild(IfTargetOSIsNotSrcOS);
// if TargetOS<>SrcOS2
IfTargetOSIsNotSrcOS2:=TDefineTemplate.Create(
'IF TargetOS is not SrcOS2',
ctsIfTargetOSIsNotSrcOS,'',''''+TargetOSMacro+'''<>'''+SrcOS2+'''',da_If);
// rtl/$(#SrcOS2)
RTLSrcOS2Dir:=TDefineTemplate.Create('SrcOS2',SrcOS2,'',
SrcOS2,da_Directory);
IfTargetOSIsNotSrcOS2.AddChild(RTLSrcOS2Dir);
RTLSrcOS2Dir.AddChild(TDefineTemplate.Create('Include Path',
'include path to TargetProcessor directories',
ExternalMacroStart+'IncPath',IncPathMacro+';'+TargetProcessor,
da_DefineRecurse));
ParentDefTempl.AddChild(IfTargetOSIsNotSrcOS2);
end;
var
DefTempl, MainDir, FCLDir, RTLDir, RTLOSDir, PackagesDir, CompilerDir,
UtilsDir, DebugSvrDir: TDefineTemplate;
s: string;
FCLDBDir: TDefineTemplate;
FCLDBInterbaseDir: TDefineTemplate;
InstallerDir: TDefineTemplate;
IFTempl: TDefineTemplate;
FCLBaseDir: TDefineTemplate;
FCLBaseSrcDir: TDefineTemplate;
PackagesFCLAsyncDir: TDefineTemplate;
PackagesExtraDir: TDefineTemplate;
PkgExtraGraphDir: TDefineTemplate;
PkgExtraAMunitsDir: TDefineTemplate;
FCLSubSrcDir: TDefineTemplate;
FCLSubDir: TDefineTemplate;
Ok: Boolean;
FPCSrcDir: String;
begin
FPCSrcDir:=Config.FPCSourceDirectory;
{$IFDEF VerboseFPCSrcScan}
DebugLn('CreateFPCSrcTemplate FPCSrcDir="',FPCSrcDir,'"');
{$ENDIF}
Result:=nil;
Ok:=false;
try
if (FPCSrcDir='') or (not DirPathExists(FPCSrcDir)) then begin
DebugLn(['CreateFPCSrcTemplate FPCSrcDir does not exist: FPCSrcDir="',FPCSrcDir,'"']);
exit;
end;
DS:=PathDelim;
Dir:=AppendPathDelim(FPCSrcDir);
SrcOS:='$('+ExternalMacroStart+'SrcOS)';
SrcOS2:='$('+ExternalMacroStart+'SrcOS2)';
TargetProcessor:='$('+ExternalMacroStart+'TargetProcessor)';
IncPathMacro:='$('+ExternalMacroStart+'IncPath)';
Result:=TDefineTemplate.Create(StdDefTemplFPCSrc,
Format(ctsFreePascalSourcesPlusDesc,['RTL, FCL, Packages, Compiler']),
'','',da_Block);
// The free pascal sources build a world of their own,
// reset search paths
MainDir:=TDefineTemplate.Create('Free Pascal Source Directory',
ctsFreePascalSourceDir,'',FPCSrcDir,da_Directory);
Result.AddChild(MainDir);
DefTempl:=TDefineTemplate.Create('Reset SrcPath',
ctsSrcPathInitialization,ExternalMacroStart+'SrcPath','',da_DefineRecurse);
MainDir.AddChild(DefTempl);
DefTempl:=TDefineTemplate.Create('Reset UnitPath',
ctsUnitPathInitialization,ExternalMacroStart+'UnitPath','',da_DefineRecurse);
MainDir.AddChild(DefTempl);
// turn Nested comments on
DefTempl:=TDefineTemplate.Create('Nested Comments',
ctsNestedCommentsOn,ExternalMacroStart+'NestedComments','',da_DefineRecurse);
MainDir.AddChild(DefTempl);
// rtl
RTLDir:=TDefineTemplate.Create('RTL',ctsRuntimeLibrary,'','rtl',da_Directory);
MainDir.AddChild(RTLDir);
// rtl include paths
s:=IncPathMacro
+';'+Dir+'rtl'+DS+'objpas'+DS
+';'+Dir+'rtl'+DS+'objpas'+DS+'sysutils'
+';'+Dir+'rtl'+DS+'objpas'+DS+'classes'
+';'+Dir+'rtl'+DS+'inc'+DS
+';'+Dir+'rtl'+DS+'inc'+DS+'graph'+DS
+';'+Dir+'rtl'+DS+SrcOS+DS
+';'+Dir+'rtl'+DS+TargetOSMacro+DS
+';'+Dir+'rtl'+DS+SrcOS2+DS
+';'+Dir+'rtl'+DS+SrcOS2+DS+TargetProcessor
+';'+Dir+'rtl'+DS+TargetProcessor+DS
+';'+Dir+'rtl'+DS+TargetOSMacro+DS+TargetProcessor+DS;
RTLDir.AddChild(TDefineTemplate.Create('Include Path',
Format(ctsIncludeDirectoriesPlusDirs,
['objpas, inc,'+TargetProcessor+','+SrcOS]),
ExternalMacroStart+'IncPath',s,da_DefineRecurse));
// rtl/$(#TargetOS)
RTLOSDir:=TDefineTemplate.Create('TargetOS','Target OS','',
TargetOSMacro,da_Directory);
s:=IncPathMacro
+';'+Dir+'rtl'+DS+TargetOSMacro+DS+SrcOS+'inc' // e.g. rtl/win32/inc/
+';'+Dir+'rtl'+DS+TargetOSMacro+DS+TargetProcessor+DS
;
RTLOSDir.AddChild(TDefineTemplate.Create('Include Path',
Format(ctsIncludeDirectoriesPlusDirs,[TargetProcessor]),
ExternalMacroStart+'IncPath',
s,da_DefineRecurse));
s:=SrcPathMacro
+';'+Dir+'rtl'+DS+'objpas'+DS;
RTLOSDir.AddChild(TDefineTemplate.Create('Src Path',
Format(ctsAddsDirToSourcePath,[TargetProcessor]),
ExternalMacroStart+'SrcPath',s,da_DefineRecurse));
RTLDir.AddChild(RTLOSDir);
// rtl: IF SrcOS=win then add include path rtl/win/wininc
IFTempl:=TDefineTemplate.Create('If SrcOS=win','If SrcOS=win',
'',''''+SrcOS+'''=''win''',da_If);
IFTempl.AddChild(TDefineTemplate.Create('Include Path',
Format(ctsIncludeDirectoriesPlusDirs,['wininc']),
ExternalMacroStart+'IncPath',
IncPathMacro
+';'+Dir+'rtl'+DS+'win'+DS+'wininc'
+';'+Dir+'rtl'+DS+'win',
da_DefineRecurse));
RTLDir.AddChild(IFTempl);
// rtl: IF TargetOS=darwin then add include path rtl/freebsd
IFTempl:=TDefineTemplate.Create('If TargetOS=darwin','If TargetOS=darwin',
'',''''+TargetOSMacro+'''=''darwin''',da_If);
IFTempl.AddChild(TDefineTemplate.Create('Include Path',
Format(ctsIncludeDirectoriesPlusDirs,['rtl'+DS+'freebsd']),
ExternalMacroStart+'IncPath',
IncPathMacro
+';'+Dir+'rtl'+DS+'freebsd',
da_DefineRecurse));
RTLDir.AddChild(IFTempl);
// add processor and SrcOS alias defines for the RTL
AddProcessorTypeDefine(RTLDir);
AddSrcOSDefines(RTLDir);
// fcl
FCLDir:=TDefineTemplate.Create('FCL',ctsFreePascalComponentLibrary,'','fcl',
da_Directory);
MainDir.AddChild(FCLDir);
FCLDir.AddChild(TDefineTemplate.Create('Include Path',
Format(ctsIncludeDirectoriesPlusDirs,['inc,'+SrcOS]),
ExternalMacroStart+'IncPath',
d( DefinePathMacro+'/inc/'
+';'+DefinePathMacro+'/classes/'
+';'+DefinePathMacro+'/'+TargetOSMacro+DS // TargetOS before SrcOS !
+';'+DefinePathMacro+'/'+SrcOS+DS
+';'+IncPathMacro)
,da_DefineRecurse));
// fcl/db
FCLDBDir:=TDefineTemplate.Create('DB','DB','','db',da_Directory);
FCLDir.AddChild(FCLDBDir);
FCLDBInterbaseDir:=TDefineTemplate.Create('interbase','interbase','',
'interbase',da_Directory);
FCLDBDir.AddChild(FCLDBInterbaseDir);
FCLDBInterbaseDir.AddChild(TDefineTemplate.Create('SrcPath',
'SrcPath addition',
ExternalMacroStart+'SrcPath',
d(Dir+'/packages/base/ibase;'+SrcPathMacro)
,da_Define));
// packages
PackagesDir:=TDefineTemplate.Create('Packages',ctsPackageDirectories,'',
'packages',da_Directory);
MainDir.AddChild(PackagesDir);
// packages/fcl-base
FCLBaseDir:=TDefineTemplate.Create('FCL-base',
ctsFreePascalComponentLibrary,'','fcl-base',
da_Directory);
PackagesDir.AddChild(FCLBaseDir);
// packages/fcl-base/src
FCLBaseSrcDir:=TDefineTemplate.Create('src',
'src','','src',
da_Directory);
FCLBaseDir.AddChild(FCLBaseSrcDir);
FCLBaseSrcDir.AddChild(TDefineTemplate.Create('Include Path',
Format(ctsIncludeDirectoriesPlusDirs,['inc,'+SrcOS]),
ExternalMacroStart+'IncPath',
d( DefinePathMacro+'/inc/'
+';'+DefinePathMacro+'/'+TargetOSMacro+DS // TargetOS before SrcOS !
+';'+DefinePathMacro+'/'+SrcOS+DS
+';'+IncPathMacro)
,da_DefineRecurse));
// packages/fcl-process
FCLSubDir:=TDefineTemplate.Create('FCL-process',
'fcl-process','','fcl-process',
da_Directory);
PackagesDir.AddChild(FCLSubDir);
// packages/fcl-process/src
FCLSubSrcDir:=TDefineTemplate.Create('src',
'src','','src',
da_Directory);
FCLSubDir.AddChild(FCLSubSrcDir);
FCLSubSrcDir.AddChild(TDefineTemplate.Create('Include Path',
Format(ctsIncludeDirectoriesPlusDirs,['inc,'+SrcOS]),
ExternalMacroStart+'IncPath',
d( DefinePathMacro+'/'+TargetOSMacro+DS // TargetOS before SrcOS !
+';'+DefinePathMacro+'/'+SrcOS+DS
+';'+IncPathMacro)
,da_DefineRecurse));
// packages/fcl-async
PackagesFCLAsyncDir:=TDefineTemplate.Create('fcl-async','fcl-async','','fcl-async',da_Directory);
PackagesDir.AddChild(PackagesFCLAsyncDir);
// packages/fcl-async/src
PackagesFCLAsyncDir.AddChild(TDefineTemplate.Create('Include Path',
Format(ctsIncludeDirectoriesPlusDirs,['packages/fcl-async/src']),
ExternalMacroStart+'IncPath',
d( DefinePathMacro+'/src/'
+';'+IncPathMacro)
,da_DefineRecurse));
// packages/extra
PackagesExtraDir:=TDefineTemplate.Create('extra','extra','','extra',da_Directory);
PackagesDir.AddChild(PackagesExtraDir);
// packages/extra/graph
PkgExtraGraphDir:=TDefineTemplate.Create('graph','graph','','graph',
da_Directory);
PackagesExtraDir.AddChild(PkgExtraGraphDir);
PkgExtraGraphDir.AddChild(TDefineTemplate.Create('Include Path',
Format(ctsIncludeDirectoriesPlusDirs,['inc']),
ExternalMacroStart+'IncPath',
d( DefinePathMacro+'/inc/'
+';'+IncPathMacro)
,da_DefineRecurse));
// packages/extra/amunits
PkgExtraAMunitsDir:=TDefineTemplate.Create('amunits','amunits','','amunits',
da_Directory);
PackagesExtraDir.AddChild(PkgExtraAMunitsDir);
PkgExtraAMunitsDir.AddChild(TDefineTemplate.Create('Include Path',
Format(ctsIncludeDirectoriesPlusDirs,['inc']),
ExternalMacroStart+'IncPath',
d( DefinePathMacro+'/inc/'
+';'+IncPathMacro)
,da_DefineRecurse));
// utils
UtilsDir:=TDefineTemplate.Create('Utils',ctsUtilsDirectories,'',
'utils',da_Directory);
MainDir.AddChild(UtilsDir);
// utils/debugsvr
DebugSvrDir:=TDefineTemplate.Create('DebugSvr','Debug Server','',
'debugsvr',da_Directory);
UtilsDir.AddChild(DebugSvrDir);
DebugSvrDir.AddChild(TDefineTemplate.Create('Interface Path',
Format(ctsAddsDirToSourcePath,['..']),ExternalMacroStart+'SrcPath',
'..;'+ExternalMacroStart+'SrcPath',da_DefineRecurse));
// installer
InstallerDir:=TDefineTemplate.Create('Installer',ctsInstallerDirectories,'',
'installer',da_Directory);
InstallerDir.AddChild(TDefineTemplate.Create('SrcPath','SrcPath addition',
ExternalMacroStart+'SrcPath',
SrcPathMacro+';'+Dir+'ide;'+Dir+'fv',da_Define));
MainDir.AddChild(InstallerDir);
// compiler
CompilerDir:=TDefineTemplate.Create('Compiler',ctsCompiler,'','compiler',
da_Directory);
AddProcessorTypeDefine(CompilerDir);
CompilerDir.AddChild(TDefineTemplate.Create('SrcPath','SrcPath addition',
ExternalMacroStart+'SrcPath',
SrcPathMacro+';'+Dir+TargetProcessor,da_Define));
CompilerDir.AddChild(TDefineTemplate.Create('IncPath','IncPath addition',
ExternalMacroStart+'IncPath',
IncPathMacro+';'+Dir+'compiler',da_DefineRecurse));
MainDir.AddChild(CompilerDir);
// compiler/utils
UtilsDir:=TDefineTemplate.Create('utils',ctsUtilsDirectories,'',
'utils',da_Directory);
UtilsDir.AddChild(TDefineTemplate.Create('SrcPath','SrcPath addition',
ExternalMacroStart+'SrcPath',
SrcPathMacro+';..',da_Define));
CompilerDir.AddChild(UtilsDir);
Result.SetDefineOwner(Owner,true);
Result.SetFlags([dtfAutoGenerated],[],false);
Ok:=true;
finally
if not ok then
FreeAndNil(Result);
end;
end;
procedure CheckPPUSources(PPUFiles, UnitToSource,
UnitToDuplicates: TStringToStringTree;
var Duplicates, Missing: TStringToStringTree);
var
Node: TAVLTreeNode;
Item: PStringToStringTreeItem;
Unit_Name: String;
Filename: String;
SrcFilename: string;
DuplicateFilenames: string;
begin
if PPUFiles.CaseSensitive then
raise Exception.Create('CheckPPUSources PPUFiles is case sensitive');
if UnitToSource.CaseSensitive then
raise Exception.Create('CheckPPUSources UnitToSource is case sensitive');
if UnitToDuplicates.CaseSensitive then
raise Exception.Create('CheckPPUSources UnitToDuplicates is case sensitive');
if (Duplicates<>nil) and Duplicates.CaseSensitive then
raise Exception.Create('CheckPPUSources Duplicates is case sensitive');
if (Missing<>nil) and Missing.CaseSensitive then
raise Exception.Create('CheckPPUSources Missing is case sensitive');
Node:=PPUFiles.Tree.FindLowest;
while Node<>nil do begin
Item:=PStringToStringTreeItem(Node.Data);
Unit_Name:=Item^.Name;
Filename:=Item^.Value;
if CompareFileExt(Filename,'.ppu',false)=0 then begin
SrcFilename:=UnitToSource[Unit_Name];
if SrcFilename<>'' then begin
DuplicateFilenames:=UnitToDuplicates[Unit_Name];
if (DuplicateFilenames<>'') and (Duplicates<>nil) then
Duplicates[Unit_Name]:=DuplicateFilenames;
end else begin
if Missing<>nil then
Missing[Unit_Name]:=Filename;
end;
end;
Node:=PPUFiles.Tree.FindSuccessor(Node);
end;
end;
procedure LoadFPCCacheFromFile(Filename: string;
var Configs: TFPCTargetConfigCaches; var Sources: TFPCSourceCaches);
var
XMLConfig: TXMLConfig;
begin
if Configs=nil then Configs:=TFPCTargetConfigCaches.Create(nil);
if Sources=nil then Sources:=TFPCSourceCaches.Create(nil);
if not FileExistsUTF8(Filename) then exit;
XMLConfig:=TXMLConfig.Create(Filename);
try
Configs.LoadFromXMLConfig(XMLConfig,'FPCConfigs/');
Sources.LoadFromXMLConfig(XMLConfig,'FPCSourceDirectories/');
finally
XMLConfig.Free;
end;
end;
procedure SaveFPCCacheToFile(Filename: string; Configs: TFPCTargetConfigCaches;
Sources: TFPCSourceCaches);
var
XMLConfig: TXMLConfig;
begin
XMLConfig:=TXMLConfig.CreateClean(Filename);
try
Configs.SaveToXMLConfig(XMLConfig,'FPCConfigs/');
Sources.SaveToXMLConfig(XMLConfig,'FPCSourceDirectories/');
finally
XMLConfig.Free;
end;
end;
procedure ReadMakefileFPC(const Filename: string; List: TStrings);
var
MakefileFPC: TStringList;
i: Integer;
Line: string;
p: LongInt;
NameValue: String;
begin
MakefileFPC:=TStringList.Create;
MakefileFPC.LoadFromFile(UTF8ToSys(Filename));
i:=0;
while i<MakefileFPC.Count do begin
Line:=MakefileFPC[i];
if Line='' then begin
end else if (Line[1]='[') then begin
// start of section
p:=System.Pos(']',Line);
if p<1 then p:=length(Line);
List.Add(Line);
end else if (Line[1] in ['a'..'z','A'..'Z','0'..'9','_']) then begin
// start of name=value pair
NameValue:=Line;
repeat
p:=length(NameValue);
while (p>=1) and (NameValue[p] in [' ',#9]) do dec(p);
//List.Add(' NameValue="'+NameValue+'" p='+IntToStr(p)+' "'+NameValue[p]+'"');
if (p>=1) and (NameValue[p]='\')
and ((p=1) or (NameValue[p-1]<>'\')) then begin
// append next line
NameValue:=copy(NameValue,1,p-1);
inc(i);
if i>=MakefileFPC.Count then break;
NameValue:=NameValue+MakefileFPC[i];
end else break;
until false;
List.Add(NameValue);
end;
inc(i);
end;
MakefileFPC.Free;
end;
procedure ParseMakefileFPC(const Filename, SrcOS: string;
var Dirs, SubDirs: string);
function MakeSearchPath(const s: string): string;
var
SrcPos: Integer;
DestPos: Integer;
begin
// check how much space is needed
SrcPos:=1;
DestPos:=0;
while (SrcPos<=length(s)) do begin
if s[SrcPos] in [#0..#31] then begin
// space is a delimiter
inc(SrcPos);
// skip multiple spaces
while (SrcPos<=length(s)) and (s[SrcPos] in [#0..#31]) do inc(SrcPos);
if (DestPos>0) and (SrcPos<=length(s)) then begin
inc(DestPos);// add semicolon
end;
end else begin
inc(DestPos);
inc(SrcPos);
end;
end;
// allocate space
SetLength(Result,DestPos);
// create semicolon delimited search path
SrcPos:=1;
DestPos:=0;
while (SrcPos<=length(s)) do begin
if s[SrcPos] in [#0..#32] then begin
// space is a delimiter
inc(SrcPos);
// skip multiple spaces
while (SrcPos<=length(s)) and (s[SrcPos] in [#0..#32]) do inc(SrcPos);
if (DestPos>0) and (SrcPos<=length(s)) then begin
inc(DestPos);// add semicolon
Result[DestPos]:=';';
end;
end else begin
inc(DestPos);
Result[DestPos]:=s[SrcPos];
inc(SrcPos);
end;
end;
end;
var
Params: TStringList;
i: Integer;
Line: string;
p: LongInt;
Name: String;
SubDirsName: String;
begin
SubDirs:='';
Dirs:='';
Params:=TStringList.Create;
try
ReadMakefileFPC(Filename,Params);
SubDirsName:='';
if SrcOS<>'' then
SubDirsName:='dirs_'+SrcOS;
for i:=0 to Params.Count-1 do begin
Line:=Params[i];
if Line='' then continue;
if (Line[1] in ['a'..'z','A'..'Z','0'..'9','_']) then begin
p:=System.Pos('=',Line);
if p<1 then continue;
Name:=copy(Line,1,p-1);
if Name=SubDirsName then begin
SubDirs:=MakeSearchPath(copy(Line,p+1,length(Line)));
end else if Name='dirs' then begin
Dirs:=MakeSearchPath(copy(Line,p+1,length(Line)));
end;
end;
end;
except
on e: Exception do begin
debugln('ParseMakefileFPC Filename=',Filename,' E.Message=',E.Message);
end;
end;
Params.Free;
end;
function CompareFPCSourceRulesViaFilenameStart(Rule1, Rule2: Pointer): integer;
var
SrcRule1: TFPCSourceRule absolute Rule1;
SrcRule2: TFPCSourceRule absolute Rule2;
begin
Result:=CompareStr(SrcRule1.Filename,SrcRule2.Filename);
end;
function CompareFPCTargetConfigCacheItems(CacheItem1, CacheItem2: Pointer): integer;
var
Item1: TFPCTargetConfigCache absolute CacheItem1;
Item2: TFPCTargetConfigCache absolute CacheItem2;
begin
Result:=CompareStr(Item1.TargetOS,Item2.TargetOS);
if Result<>0 then exit;
Result:=CompareStr(Item1.TargetCPU,Item2.TargetCPU);
if Result<>0 then exit;
Result:=CompareFilenames(Item1.Compiler,Item2.Compiler);
end;
function CompareFPCSourceCacheItems(CacheItem1, CacheItem2: Pointer): integer;
var
Src1: TFPCSourceCache absolute CacheItem1;
Src2: TFPCSourceCache absolute CacheItem2;
begin
Result:=CompareStr(Src1.Directory,Src2.Directory);
end;
function CompareDirectoryWithFPCSourceCacheItem(AString, CacheItem: Pointer
): integer;
var
Src: TFPCSourceCache absolute CacheItem;
begin
Result:=CompareStr(AnsiString(AString),Src.Directory);
end;
function DefineActionNameToAction(const s: string): TDefineAction;
begin
for Result:=Low(TDefineAction) to High(TDefineAction) do
if CompareText(s,DefineActionNames[Result])=0 then exit;
Result:=da_None;
end;
function DefineTemplateFlagsToString(Flags: TDefineTemplateFlags): string;
var f: TDefineTemplateFlag;
begin
Result:='';
for f:=Low(TDefineTemplateFlag) to High(TDefineTemplateFlag) do begin
if f in Flags then begin
if Result<>'' then Result:=Result+',';
Result:=Result+DefineTemplateFlagNames[f];
end;
end;
end;
function CompareUnitLinkNodes(NodeData1, NodeData2: pointer): integer;
var Link1, Link2: TUnitNameLink;
begin
Link1:=TUnitNameLink(NodeData1);
Link2:=TUnitNameLink(NodeData2);
Result:=CompareText(Link1.Unit_Name,Link2.Unit_Name);
end;
function CompareUnitNameWithUnitLinkNode(AUnitName: Pointer;
NodeData: pointer): integer;
begin
Result:=CompareText(String(AUnitName),TUnitNameLink(NodeData).Unit_Name);
end;
function CompareDirectoryDefines(NodeData1, NodeData2: pointer): integer;
var DirDef1, DirDef2: TDirectoryDefines;
begin
DirDef1:=TDirectoryDefines(NodeData1);
DirDef2:=TDirectoryDefines(NodeData2);
Result:=CompareFilenames(DirDef1.Path,DirDef2.Path);
end;
function GetDefaultSrcOSForTargetOS(const TargetOS: string): string;
begin
Result:='';
if (CompareText(TargetOS,'linux')=0)
or (CompareText(TargetOS,'freebsd')=0)
or (CompareText(TargetOS,'netbsd')=0)
or (CompareText(TargetOS,'openbsd')=0)
or (CompareText(TargetOS,'darwin')=0)
or (CompareText(TargetOS,'solaris')=0)
or (CompareText(TargetOS,'haiku')=0)
then
Result:='unix'
else
if (CompareText(TargetOS,'win32')=0)
or (CompareText(TargetOS,'win64')=0)
or (CompareText(TargetOS,'wince')=0)
then
Result:='win';
end;
function GetDefaultSrcOS2ForTargetOS(const TargetOS: string): string;
begin
Result:='';
if (CompareText(TargetOS,'freebsd')=0)
or (CompareText(TargetOS,'netbsd')=0)
or (CompareText(TargetOS,'openbsd')=0)
or (CompareText(TargetOS,'darwin')=0)
then
Result:='bsd';
end;
function GetDefaultSrcCPUForTargetCPU(const TargetCPU: string): string;
begin
Result:='';
if (CompareText(TargetCPU,'i386')=0)
or (CompareText(TargetCPU,'x86_64')=0)
then
Result:='x86';
end;
procedure SplitLazarusCPUOSWidgetCombo(const Combination: string;
var CPU, OS, WidgetSet: string);
var
StartPos, EndPos: integer;
begin
StartPos:=1;
EndPos:=StartPos;
while (EndPos<=length(Combination)) and (Combination[EndPos]<>'-') do
inc(EndPos);
CPU:=copy(Combination,StartPos,EndPos-StartPos);
StartPos:=EndPos+1;
EndPos:=StartPos;
while (EndPos<=length(Combination)) and (Combination[EndPos]<>'-') do
inc(EndPos);
OS:=copy(Combination,StartPos,EndPos-StartPos);
StartPos:=EndPos+1;
EndPos:=StartPos;
while (EndPos<=length(Combination)) and (Combination[EndPos]<>'-') do
inc(EndPos);
WidgetSet:=copy(Combination,StartPos,EndPos-StartPos);
end;
function GetCompiledTargetOS: string;
begin
Result:=lowerCase({$I %FPCTARGETOS%});
end;
function GetCompiledTargetCPU: string;
begin
Result:=lowerCase({$I %FPCTARGETCPU%});
end;
function GetDefaultCompilerFilename(const TargetCPU: string = ''): string;
begin
if TargetCPU='' then
Result:='fpc'
else if SysUtils.CompareText(TargetCPU,'i386')=0 then
Result:='ppc386'
else if SysUtils.CompareText(TargetCPU,'powerpc')=0 then
Result:='ppcppc'
else if SysUtils.CompareText(TargetCPU,'sparc')=0 then
Result:='ppcsparc'
else if SysUtils.CompareText(TargetCPU,'m68k')=0 then
Result:='ppc86k'
else if SysUtils.CompareText(TargetCPU,'alpha')=0 then
Result:='ppcalpha'
else if SysUtils.CompareText(TargetCPU,'x86_64')=0 then
Result:='ppcx64'
else if SysUtils.CompareText(TargetCPU,'arm')=0 then
Result:='ppcarm'
else
Result:='fpc';
Result:=Result+ExeExt;
end;
function GetFPCTargetOS(TargetOS: string): string;
begin
Result:=lowercase(TargetOS);
end;
function GetFPCTargetCPU(TargetCPU: string): string;
begin
Result:=LowerCase(TargetCPU);
end;
function CreateDefinesInDirectories(const SourcePaths, FlagName: string
): TDefineTemplate;
var
StartPos: Integer;
EndPos: LongInt;
CurDirectory: String;
DirsTempl: TDefineTemplate;
DirTempl: TDefineTemplate;
SetFlagTempl: TDefineTemplate;
begin
// create a block template for the directories
DirsTempl:=TDefineTemplate.Create(FlagName,
'Block of directories to set '+FlagName,
'','',da_Block);
// create a define flag for every directory
StartPos:=1;
while StartPos<=length(SourcePaths) do begin
EndPos:=StartPos;
while (EndPos<=length(SourcePaths)) and (SourcePaths[EndPos]<>';') do
inc(EndPos);
if EndPos>StartPos then begin
CurDirectory:=copy(SourcePaths,StartPos,EndPos-StartPos);
DirTempl:=TDefineTemplate.Create('FlagDirectory','FlagDirectory',
'',CurDirectory,da_Directory);
SetFlagTempl:=TDefineTemplate.Create(FlagName,FlagName,
FlagName,'1',da_Define);
DirTempl.AddChild(SetFlagTempl);
DirsTempl.AddChild(DirTempl);
end;
StartPos:=EndPos+1;
end;
Result:=DirsTempl;
end;
procedure InitDefaultFPCSourceRules;
begin
DefaultFPCSourceRules:=TFPCSourceRules.Create;
with DefaultFPCSourceRules do begin
// put into an include file for easy edit via an editor
{$I fpcsrcrules.inc}
end;
end;
{ TDefineTemplate }
procedure TDefineTemplate.MarkFlags(
const MustFlags, NotFlags: TDefineTemplateFlags;
WithSiblings, WithChilds: boolean);
var
ANode: TDefineTemplate;
begin
ANode:=Self;
while ANode<>nil do begin
ANode.FMarked:=ANode.FMarked
or (((ANode.Flags*MustFlags)=MustFlags)
and (ANode.Flags*NotFlags=[]));
if (ANode.FirstChild<>nil) and WithChilds then
ANode.FirstChild.MarkFlags(MustFlags,NotFlags,true,true);
if not WithSiblings then break;
ANode:=ANode.Next;
end;
end;
procedure TDefineTemplate.MarkOwnedBy(TheOwner: TObject;
const MustFlags, NotFlags: TDefineTemplateFlags;
WithSiblings, WithChilds: boolean);
var
ANode: TDefineTemplate;
begin
ANode:=Self;
while ANode<>nil do begin
ANode.FMarked:=ANode.FMarked
or ((ANode.Owner=TheOwner)
and ((ANode.Flags*MustFlags)=MustFlags)
and (ANode.Flags*NotFlags=[]));
if (ANode.FirstChild<>nil) and WithChilds then
ANode.FirstChild.MarkOwnedBy(TheOwner,MustFlags,NotFlags,true,true);
if not WithSiblings then break;
ANode:=ANode.Next;
end;
end;
procedure TDefineTemplate.MarkNodes(WithSiblings, WithChilds: boolean);
var
ANode: TDefineTemplate;
begin
ANode:=Self;
while ANode<>nil do begin
ANode.FMarked:=true;
if (ANode.FirstChild<>nil) and WithChilds then
ANode.FirstChild.MarkNodes(true,true);
if not WithSiblings then break;
ANode:=ANode.Next;
end;
end;
procedure TDefineTemplate.ReverseMarks(WithSiblings, WithChilds: boolean);
var
ANode: TDefineTemplate;
begin
ANode:=Self;
while ANode<>nil do begin
ANode.FMarked:=not ANode.FMarked;
if (ANode.FirstChild<>nil) and WithChilds then
ANode.FirstChild.MarkNodes(true,true);
if not WithSiblings then break;
ANode:=ANode.Next;
end;
end;
procedure TDefineTemplate.InheritMarks(WithSiblings, WithChilds, Down,
Up: boolean);
var
ANode: TDefineTemplate;
ChildNode: TDefineTemplate;
begin
ANode:=Self;
while ANode<>nil do begin
if WithChilds then begin
ChildNode:=ANode.FirstChild;
while ChildNode<>nil do begin
if Down and ANode.FMarked then
ChildNode.FMarked:=true;
ChildNode.InheritMarks(false,true,Down,Up);
if Up and ChildNode.FMarked then
ANode.FMarked:=true;
ChildNode:=ChildNode.Next;
end;
end;
if not WithSiblings then break;
ANode:=ANode.Next;
end;
end;
procedure TDefineTemplate.UnmarkNodes(WithSiblings, WithChilds: boolean);
var
ANode: TDefineTemplate;
begin
ANode:=Self;
while ANode<>nil do begin
ANode.FMarked:=false;
if (ANode.FirstChild<>nil) and WithChilds then
ANode.FirstChild.UnmarkNodes(true,true);
if not WithSiblings then break;
ANode:=ANode.Next;
end;
end;
procedure TDefineTemplate.RemoveMarked(WithSiblings: boolean;
var FirstDefTemplate: TDefineTemplate);
var ANode, NextNode: TDefineTemplate;
begin
ANode:=Self;
while ANode<>nil do begin
NextNode:=ANode.Next;
if ANode.FirstChild<>nil then begin
ANode.FirstChild.RemoveMarked(true,FirstDefTemplate);
end;
if ANode.FMarked and (ANode.FirstChild=nil) then begin
if ANode=FirstDefTemplate then FirstDefTemplate:=ANode.Next;
ANode.Unbind;
ANode.Free;
end;
if not WithSiblings then break;
ANode:=NextNode;
end;
end;
procedure TDefineTemplate.RemoveOwner(TheOwner: TObject; WithSiblings: boolean);
var
ANode: TDefineTemplate;
begin
ANode:=Self;
while ANode<>nil do begin
if ANode.FFirstChild<>nil then
ANode.FFirstChild.RemoveOwner(TheOwner,true);
if ANode.Owner=TheOwner then ANode.Owner:=nil;
if not WithSiblings then break;
ANode:=ANode.Next;
end;
end;
procedure TDefineTemplate.RemoveLeaves(TheOwner: TObject; const MustFlags,
NotFlags: TDefineTemplateFlags; WithSiblings: boolean;
var FirstDefTemplate: TDefineTemplate);
var ANode, NextNode: TDefineTemplate;
begin
ANode:=Self;
while ANode<>nil do begin
NextNode:=ANode.Next;
if ANode.FirstChild<>nil then
ANode.FirstChild.RemoveLeaves(TheOwner,MustFlags,NotFlags,true,
FirstDefTemplate);
if ANode.FirstChild=nil then begin
// this is a leaf
if ((ANode.Owner=TheOwner)
and ((ANode.Flags*MustFlags)=MustFlags)
and (ANode.Flags*NotFlags=[]))
then begin
if ANode=FirstDefTemplate then
FirstDefTemplate:=ANode.Next;
ANode.Unbind;
ANode.Free;
end;
end;
if not WithSiblings then break;
ANode:=NextNode;
end;
end;
procedure TDefineTemplate.AddChild(ADefineTemplate: TDefineTemplate);
// add as last child
begin
if ADefineTemplate=nil then exit;
if ADefineTemplate.Parent<>nil then
raise Exception.Create('TDefineTemplate.AddChild');
if LastChild=nil then begin
while ADefineTemplate<>nil do begin
ADefineTemplate.fParent:=Self;
if ADefineTemplate.Prior=nil then FFirstChild:=ADefineTemplate;
if ADefineTemplate.Next=nil then FLastChild:=ADefineTemplate;
inc(FChildCount);
ADefineTemplate:=ADefineTemplate.Next;
end;
end else begin
ADefineTemplate.InsertBehind(LastChild);
end;
end;
procedure TDefineTemplate.ReplaceChild(ADefineTemplate: TDefineTemplate);
var
OldTempl: TDefineTemplate;
begin
OldTempl:=FindChildByName(ADefineTemplate.Name);
if OldTempl<>nil then begin
ADefineTemplate.InsertInFront(OldTempl);
OldTempl.UnBind;
OldTempl.Free;
end else
AddChild(ADefineTemplate);
end;
function TDefineTemplate.DeleteChild(const AName: string): boolean;
var
OldTempl: TDefineTemplate;
begin
OldTempl:=FindChildByName(AName);
if OldTempl<>nil then begin
Result:=true;
OldTempl.Unbind;
OldTempl.Free;
end else
Result:=false;
end;
procedure TDefineTemplate.InsertBehind(APrior: TDefineTemplate);
// insert this and all next siblings behind APrior
var ANode, LastSibling, NewParent: TDefineTemplate;
begin
if APrior=nil then exit;
NewParent:=APrior.Parent;
if Parent<>nil then begin
ANode:=Self;
while ANode<>nil do begin
if ANode=APrior then
raise Exception.Create('internal error: '
+'TDefineTemplate.InsertBehind: APrior=ANode');
dec(Parent.FChildCount);
ANode.FParent:=nil;
ANode:=ANode.Next;
end;
end;
LastSibling:=Self;
while LastSibling.Next<>nil do LastSibling:=LastSibling.Next;
FParent:=NewParent;
if Parent<>nil then begin
ANode:=Self;
while (ANode<>nil) do begin
ANode.FParent:=Parent;
inc(Parent.FChildCount);
ANode:=ANode.Next;
end;
if Parent.LastChild=APrior then Parent.FLastChild:=LastSibling;
end;
FPrior:=APrior;
LastSibling.FNext:=APrior.Next;
APrior.FNext:=Self;
if LastSibling.Next<>nil then LastSibling.Next.FPrior:=LastSibling;
end;
procedure TDefineTemplate.InsertInFront(ANext: TDefineTemplate);
// insert this and all next siblings in front of ANext
var ANode, LastSibling: TDefineTemplate;
begin
if ANext=nil then exit;
if FParent<>nil then begin
ANode:=Self;
while ANode<>nil do begin
if ANode=ANext then
raise Exception.Create('internal error: '
+'TDefineTemplate.InsertInFront: ANext=ANode');
dec(FParent.FChildCount);
ANode.FParent:=nil;
ANode:=ANode.Next;
end;
end;
LastSibling:=Self;
while LastSibling.Next<>nil do LastSibling:=LastSibling.Next;
FParent:=ANext.Parent;
if Parent<>nil then begin
ANode:=Self;
while ANode<>nil do begin
ANode.FParent:=Parent;
inc(Parent.FChildCount);
ANode:=ANode.Next;
end;
if Parent.FirstChild=ANext then Parent.FFirstChild:=Self;
end;
FPrior:=ANext.Prior;
if Prior<>nil then Prior.FNext:=Self;
LastSibling.FNext:=ANext;
ANext.FPrior:=LastSibling;
end;
procedure TDefineTemplate.MoveToLast(Child: TDefineTemplate);
var
Node: TDefineTemplate;
begin
if Child.Next=nil then exit;
Node:=Child.Next;
while Node.Next<>nil do Node:=Node.Next;
Child.Unbind;
Child.InsertBehind(Node);
end;
procedure TDefineTemplate.Assign(ADefineTemplate: TDefineTemplate;
WithSubNodes, WithNextSiblings, ClearOldSiblings: boolean);
var ChildTemplate, CopyTemplate, NextTemplate: TDefineTemplate;
begin
Clear(ClearOldSiblings);
if ADefineTemplate=nil then exit;
AssignValues(ADefineTemplate);
if WithSubNodes then begin
ChildTemplate:=ADefineTemplate.FirstChild;
if ChildTemplate<>nil then begin
CopyTemplate:=TDefineTemplate.Create;
AddChild(CopyTemplate);
CopyTemplate.Assign(ChildTemplate,true,true,false);
end;
end;
if WithNextSiblings then begin
NextTemplate:=ADefineTemplate.Next;
if NextTemplate<>nil then begin
CopyTemplate:=TDefineTemplate.Create;
CopyTemplate.InsertBehind(Self);
CopyTemplate.Assign(NextTemplate,WithSubNodes,true,false);
end;
end;
end;
procedure TDefineTemplate.AssignValues(ADefineTemplate: TDefineTemplate);
begin
Name:=ADefineTemplate.Name;
Description:=ADefineTemplate.Description;
Variable:=ADefineTemplate.Variable;
Value:=ADefineTemplate.Value;
Action:=ADefineTemplate.Action;
Flags:=ADefineTemplate.Flags;
MergeNameInFront:=ADefineTemplate.MergeNameInFront;
MergeNameBehind:=ADefineTemplate.MergeNameBehind;
Owner:=ADefineTemplate.Owner;
end;
procedure TDefineTemplate.Unbind;
begin
if FPrior<>nil then FPrior.FNext:=FNext;
if FNext<>nil then FNext.FPrior:=FPrior;
if FParent<>nil then begin
if FParent.FFirstChild=Self then FParent.FFirstChild:=FNext;
if FParent.FLastChild=Self then FParent.FLastChild:=FPrior;
dec(FParent.FChildCount);
end;
FNext:=nil;
FPrior:=nil;
FParent:=nil;
end;
procedure TDefineTemplate.Clear(WithSiblings: boolean);
begin
while FFirstChild<>nil do FFirstChild.Free;
if WithSiblings then
while FNext<>nil do FNext.Free;
Name:='';
Description:='';
Value:='';
Variable:='';
Flags:=[];
end;
constructor TDefineTemplate.Create;
begin
inherited Create;
end;
constructor TDefineTemplate.Create(const AName, ADescription, AVariable,
AValue: string; AnAction: TDefineAction);
begin
inherited Create;
Name:=AName;
Description:=ADescription;
Variable:=AVariable;
Value:=AValue;
Action:=AnAction;
end;
function TDefineTemplate.CreateCopy(OnlyMarked: boolean;
WithSiblings: boolean; WithChilds: boolean): TDefineTemplate;
var LastNewNode, NewNode, ANode: TDefineTemplate;
begin
Result:=nil;
LastNewNode:=nil;
ANode:=Self;
while ANode<>nil do begin
if (not OnlyMarked) or (ANode.FMarked) then begin
// copy node
NewNode:=TDefineTemplate.Create;
NewNode.Assign(ANode,false,false,false);
if LastNewNode<>nil then
NewNode.InsertBehind(LastNewNode)
else
Result:=NewNode;
LastNewNode:=NewNode;
// copy childs
if WithChilds and (ANode.FirstChild<>nil) then begin
NewNode:=ANode.FirstChild.CreateCopy(OnlyMarked,true,true);
if NewNode<>nil then
LastNewNode.AddChild(NewNode);
end;
end;
if not WithSiblings then break;
ANode:=ANode.Next;
end;
end;
function TDefineTemplate.CreateMergeCopy: TDefineTemplate;
begin
CreateMergeInfo(false,false);
Result:=TDefineTemplate.Create;
Result.Assign(Self,true,false,false);
end;
function TDefineTemplate.FindRoot: TDefineTemplate;
begin
Result:=Self;
repeat
if Result.Parent<>nil then
Result:=Result.Parent
else if Result.Prior<>nil then
Result:=Result.Prior
else
break;
until false;
end;
destructor TDefineTemplate.Destroy;
begin
Clear(false);
Unbind;
inherited Destroy;
end;
function TDefineTemplate.LoadFromXMLConfig(XMLConfig: TXMLConfig;
const Path: string; ClearOldSiblings, WithMergeInfo: boolean): boolean;
var IndexedPath: string;
i, LvlCount: integer;
DefTempl, LastDefTempl: TDefineTemplate;
NewChild: TDefineTemplate;
begin
Clear(ClearOldSiblings);
LvlCount:=XMLConfig.GetValue(Path+'Count/Value',0);
DefTempl:=nil;
for i:=1 to LvlCount do begin
if i=1 then begin
DefTempl:=Self;
LastDefTempl:=Prior;
end else begin
LastDefTempl:=DefTempl;
DefTempl:=TDefineTemplate.Create;
DefTempl.InsertBehind(LastDefTempl);
end;
IndexedPath:=Path+'Node'+IntToStr(i)+'/';
DefTempl.LoadValuesFromXMLConfig(XMLConfig,IndexedPath,WithMergeInfo);
// load childs
if XMLConfig.GetValue(IndexedPath+'Count/Value',0)>0 then begin
NewChild:=TDefineTemplate.Create;
DefTempl.AddChild(NewChild);
if not NewChild.LoadFromXMLConfig(XMLConfig,IndexedPath,
false,WithMergeInfo) then
begin
Result:=false; exit;
end;
end;
end;
Result:=true;
end;
procedure TDefineTemplate.LoadValuesFromXMLConfig(XMLConfig: TXMLConfig;
const Path: string; WithMergeInfo: boolean);
var f: TDefineTemplateFlag;
begin
Name:=XMLConfig.GetValue(Path+'Name/Value','no name');
Description:=XMLConfig.GetValue(Path+'Description/Value','');
Value:=XMLConfig.GetValue(Path+'Value/Value','');
Variable:=XMLConfig.GetValue(Path+'Variable/Value','');
Action:=DefineActionNameToAction(
XMLConfig.GetValue(Path+'Action/Value',''));
Flags:=[];
for f:=Low(TDefineTemplateFlag) to High(TDefineTemplateFlag) do begin
if (f<>dtfAutoGenerated)
and (XMLConfig.GetValue(Path+'Flags/'+DefineTemplateFlagNames[f],false))
then
Include(Flags,f);
end;
if WithMergeInfo then begin
MergeNameInFront:=XMLConfig.GetValue(Path+'MergeNameInFront/Value','');
MergeNameBehind:=XMLConfig.GetValue(Path+'MergeNameInFront/Value','');
end else begin
MergeNameInFront:='';
MergeNameBehind:='';
end;
end;
procedure TDefineTemplate.SaveValuesToXMLConfig(XMLConfig: TXMLConfig;
const Path: string; WithMergeInfo: boolean);
var
f: TDefineTemplateFlag;
begin
XMLConfig.SetDeleteValue(Path+'Name/Value',Name,'');
XMLConfig.SetDeleteValue(Path+'Description/Value',Description,'');
XMLConfig.SetDeleteValue(Path+'Value/Value',Value,'');
XMLConfig.SetDeleteValue(Path+'Variable/Value',Variable,'');
XMLConfig.SetDeleteValue(Path+'Action/Value',
DefineActionNames[Action],
DefineActionNames[da_None]);
for f:=Low(TDefineTemplateFlag) to High(TDefineTemplateFlag) do begin
if (f<>dtfAutoGenerated) then
XMLConfig.SetDeleteValue(
Path+'Flags/'+DefineTemplateFlagNames[f]
,f in Flags,false);
end;
if WithMergeInfo then begin
XMLConfig.SetDeleteValue(Path+'MergeNameInFront/Value',
MergeNameInFront,'');
XMLConfig.SetDeleteValue(Path+'MergeNameBehind/Value',
MergeNameBehind,'');
end else begin
XMLConfig.SetDeleteValue(Path+'MergeNameInFront/Value','','');
XMLConfig.SetDeleteValue(Path+'MergeNameBehind/Value','','');
end;
end;
procedure TDefineTemplate.SaveToXMLConfig(XMLConfig: TXMLConfig;
const Path: string;
WithSiblings, OnlyMarked, WithMergeInfo, UpdateMergeInfo: boolean);
var IndexedPath: string;
Index, LvlCount: integer;
DefTempl: TDefineTemplate;
begin
if UpdateMergeInfo then CreateMergeInfo(WithSiblings,OnlyMarked);
DefTempl:=Self;
LvlCount:=0;
while DefTempl<>nil do begin
inc(LvlCount);
DefTempl:=DefTempl.Next;
end;
DefTempl:=Self;
Index:=0;
repeat
if (DefTempl.FMarked) or (not OnlyMarked) then begin
// save node
inc(Index);
IndexedPath:=Path+'Node'+IntToStr(Index)+'/';
DefTempl.SaveValuesToXMLConfig(XMLConfig,IndexedPath,WithMergeInfo);
// save childs
if DefTempl.FFirstChild<>nil then
DefTempl.FirstChild.SaveToXMLConfig(XMLConfig,IndexedPath,
true,OnlyMarked,
WithMergeInfo,false)
else
XMLConfig.SetDeleteValue(IndexedPath+'Count/Value',0,0);
end;
if not WithSiblings then break;
DefTempl:=DefTempl.Next;
until DefTempl=nil;
XMLConfig.SetDeleteValue(Path+'Count/Value',Index,0);
end;
procedure TDefineTemplate.CreateMergeInfo(WithSiblings, OnlyMarked: boolean);
var
DefTempl: TDefineTemplate;
begin
DefTempl:=Self;
repeat
if (DefTempl.FMarked) or (not OnlyMarked) then begin
if DefTempl.Prior<>nil then
DefTempl.MergeNameInFront:=DefTempl.Prior.Name
else
DefTempl.MergeNameInFront:='';
if DefTempl.Next<>nil then
DefTempl.MergeNameBehind:=DefTempl.Next.Name
else
DefTempl.MergeNameBehind:='';
// update childs
if DefTempl.FFirstChild<>nil then
DefTempl.FirstChild.CreateMergeInfo(true,OnlyMarked);
end;
if not WithSiblings then break;
DefTempl:=DefTempl.Next;
until DefTempl=nil;
end;
class procedure TDefineTemplate.MergeXMLConfig(ParentDefTempl: TDefineTemplate;
var FirstSibling, LastSibling: TDefineTemplate;
XMLConfig: TXMLConfig; const Path, NewNamePrefix: string);
var
SrcNode: TDefineTemplate;
begin
SrcNode:=TDefineTemplate.Create;
SrcNode.LoadFromXMLConfig(XMLConfig,Path,false,true);
MergeTemplates(ParentDefTempl,FirstSibling,LastSibling,SrcNode,true,
NewNamePrefix);
SrcNode.Clear(true);
SrcNode.Free;
end;
class procedure TDefineTemplate.MergeTemplates(ParentDefTempl: TDefineTemplate;
var FirstSibling, LastSibling: TDefineTemplate;
SourceTemplate: TDefineTemplate; WithSiblings: boolean;
const NewNamePrefix: string);
// merge SourceTemplate. This will keep SourceTemplate untouched
var
NewNode, PosNode: TDefineTemplate;
Inserted: boolean;
SrcNode: TDefineTemplate;
begin
SrcNode:=SourceTemplate;
while SrcNode<>nil do begin
// merge all source nodes
NewNode:=SrcNode.CreateCopy(false,false,false);
Inserted:=false;
if NewNode.Name<>'' then begin
// node has a name -> test if already exists
PosNode:=FirstSibling;
while (PosNode<>nil)
and (CompareText(PosNode.Name,NewNode.Name)<>0) do
PosNode:=PosNode.Next;
if PosNode<>nil then begin
// node with same name already exists -> check if it is a copy
if NewNode.IsEqual(PosNode,false,false) then begin
// node already exists
NewNode.Free;
NewNode:=PosNode;
end else begin
// node has same name, but different values
// -> rename node
NewNode.Name:=NewNode.FindUniqueName(NewNamePrefix+NewNode.Name);
// insert behind PosNode
NewNode.InsertBehind(PosNode);
end;
Inserted:=true;
end;
end;
if not Inserted then begin
// node name is unique or empty -> insert node
if NewNode.MergeNameInFront<>'' then begin
// last time, node was inserted behind MergeNameInFront
// -> search MergeNameInFront
PosNode:=LastSibling;
while (PosNode<>nil)
and (CompareText(PosNode.Name,NewNode.MergeNameInFront)<>0) do
PosNode:=PosNode.Prior;
if PosNode<>nil then begin
// MergeNameInFront found -> insert behind
NewNode.InsertBehind(PosNode);
Inserted:=true;
end;
end;
if not Inserted then begin
if NewNode.MergeNameBehind<>'' then begin
// last time, node was inserted in front of MergeNameBehind
// -> search MergeNameBehind
PosNode:=FirstSibling;
while (PosNode<>nil)
and (CompareText(PosNode.Name,NewNode.MergeNameBehind)<>0) do
PosNode:=PosNode.Next;
if PosNode<>nil then begin
// MergeNameBehind found -> insert in front
NewNode.InsertInFront(PosNode);
Inserted:=true;
end;
end;
end;
if not Inserted then begin
// no merge position found -> add as last
if LastSibling<>nil then begin
NewNode.InsertBehind(LastSibling);
end else if ParentDefTempl<>nil then begin
ParentDefTempl.AddChild(NewNode);
end;
end;
end;
// NewNode is now inserted -> update FirstSibling and LastSibling
if FirstSibling=nil then begin
FirstSibling:=NewNode;
LastSibling:=NewNode;
end;
while FirstSibling.Prior<>nil do
FirstSibling:=FirstSibling.Prior;
while LastSibling.Next<>nil do
LastSibling:=LastSibling.Next;
// merge childs
MergeTemplates(NewNode,NewNode.FFirstChild,NewNode.FLastChild,
SrcNode.FirstChild,true,NewNamePrefix);
if not WithSiblings then break;
SrcNode:=SrcNode.Next;
end;
end;
procedure TDefineTemplate.ConsistencyCheck;
var RealChildCount: integer;
DefTempl: TDefineTemplate;
begin
RealChildCount:=0;
DefTempl:=FFirstChild;
if DefTempl<>nil then begin
if DefTempl.Prior<>nil then begin
// not first child
RaiseCatchableException('');
end;
while DefTempl<>nil do begin
if DefTempl.Parent<>Self then begin
DebugLn(' C: DefTempl.Parent<>Self: ',Name,',',DefTempl.Name);
RaiseCatchableException('');
end;
if (DefTempl.Next<>nil) and (DefTempl.Next.Prior<>DefTempl) then
RaiseCatchableException('');
if (DefTempl.Prior<>nil) and (DefTempl.Prior.Next<>DefTempl) then
RaiseCatchableException('');
DefTempl.ConsistencyCheck;
DefTempl:=DefTempl.Next;
inc(RealChildCount);
end;
end;
if (Parent<>nil) then begin
if (Prior=nil) and (Parent.FirstChild<>Self) then
RaiseCatchableException('');
if (Next=nil) and (Parent.LastChild<>Self) then
RaiseCatchableException('');
end;
if RealChildCount<>FChildCount then
RaiseCatchableException('');
end;
procedure TDefineTemplate.CalcMemSize(Stats: TCTMemStats);
var
Child: TDefineTemplate;
begin
Stats.Add('TDefineTemplate Instance Count',1);
Stats.Add('TDefineTemplate',
PtrUInt(InstanceSize)
+MemSizeString(FMergeNameBehind)
+MemSizeString(FMergeNameInFront)
+MemSizeString(Name)
+MemSizeString(Description)
+MemSizeString(Variable)
+MemSizeString(Value)
+MemSizeString(Value)
);
Child:=FFirstChild;
while Child<>nil do begin
Child.CalcMemSize(Stats);
Child:=Child.Next;
end;
end;
procedure TDefineTemplate.SetDefineOwner(NewOwner: TObject;
WithSiblings: boolean);
var
ANode: TDefineTemplate;
begin
ANode:=Self;
while ANode<>nil do begin
ANode.Owner:=NewOwner;
if ANode.FFirstChild<>nil then
ANode.FFirstChild.SetDefineOwner(NewOwner,true);
if not WithSiblings then exit;
ANode:=ANode.Next;
end;
end;
procedure TDefineTemplate.SetFlags(AddFlags, SubFlags: TDefineTemplateFlags;
WithSiblings: boolean);
var
ANode: TDefineTemplate;
begin
ANode:=Self;
while ANode<>nil do begin
ANode.Flags:=ANode.Flags+AddFlags-SubFlags;
if ANode.FFirstChild<>nil then
ANode.FFirstChild.SetFlags(AddFlags,SubFlags,true);
if not WithSiblings then exit;
ANode:=ANode.Next;
end;
end;
procedure TDefineTemplate.WriteDebugReport(OnlyMarked: boolean);
procedure WriteNode(ANode: TDefineTemplate; const Prefix: string);
var ActionStr: string;
begin
if ANode=nil then exit;
if (not OnlyMarked) or (ANode.Marked) then begin
ActionStr:=DefineActionNames[ANode.Action];
DebugLn(Prefix+'Self='+DbgS(ANode),
' Name="'+ANode.Name,'"',
' Next='+DbgS(ANode.Next),
' Prior='+DbgS(ANode.Prior),
' Action='+ActionStr,
' Flags=['+DefineTemplateFlagsToString(ANode.Flags),']',
' Marked='+dbgs(ANode.Marked)
);
DebugLn(Prefix+' + Description="',ANode.Description,'"');
DebugLn(Prefix+' + Variable="',ANode.Variable,'"');
DebugLn(Prefix+' + Value="',ANode.Value,'"');
end;
WriteNode(ANode.FirstChild,Prefix+' ');
WriteNode(ANode.Next,Prefix);
end;
begin
WriteNode(Self,' ');
end;
function TDefineTemplate.GetNext: TDefineTemplate;
begin
if FirstChild<>nil then
exit(FirstChild);
Result:=GetNextSkipChildren;
end;
function TDefineTemplate.GetNextSkipChildren: TDefineTemplate;
begin
Result:=Self;
while (Result<>nil) do begin
if Result.Next<>nil then begin
Result:=Result.Next;
exit;
end;
Result:=Result.Parent;
end;
end;
function TDefineTemplate.HasDefines(OnlyMarked, WithSiblings: boolean): boolean;
var
CurTempl: TDefineTemplate;
begin
Result:=true;
CurTempl:=Self;
while CurTempl<>nil do begin
if ((not OnlyMarked) or (CurTempl.FMarked))
and (CurTempl.Action in DefineActionDefines) then exit;
// go to next
if CurTempl.FFirstChild<>nil then
CurTempl:=CurTempl.FFirstChild
else if (CurTempl.FNext<>nil)
and (WithSiblings or (CurTempl.Parent<>Parent)) then
CurTempl:=CurTempl.FNext
else begin
// search uncle
repeat
CurTempl:=CurTempl.Parent;
if (CurTempl=Parent)
or ((CurTempl.Parent=Parent) and not WithSiblings) then begin
Result:=false;
exit;
end;
until (CurTempl.FNext<>nil);
CurTempl:=CurTempl.FNext;
end;
end;
Result:=false;
end;
function TDefineTemplate.IsEqual(ADefineTemplate: TDefineTemplate;
CheckSubNodes, CheckNextSiblings: boolean): boolean;
var SrcNode, DestNode: TDefineTemplate;
begin
Result:=(ADefineTemplate<>nil)
and (Name=ADefineTemplate.Name)
and (Description=ADefineTemplate.Description)
and (Variable=ADefineTemplate.Variable)
and (Value=ADefineTemplate.Value)
and (Action=ADefineTemplate.Action)
and (Flags=ADefineTemplate.Flags)
and (Owner=ADefineTemplate.Owner);
if not Result then begin
exit;
end;
if CheckSubNodes then begin
if (ChildCount<>ADefineTemplate.ChildCount) then begin
Result:=false;
exit;
end;
SrcNode:=FirstChild;
DestNode:=ADefineTemplate.FirstChild;
if SrcNode<>nil then begin
Result:=SrcNode.IsEqual(DestNode,CheckSubNodes,true);
if not Result then exit;
end;
end;
if CheckNextSiblings then begin
SrcNode:=Next;
DestNode:=ADefineTemplate.Next;
while (SrcNode<>nil) and (DestNode<>nil) do begin
Result:=SrcNode.IsEqual(DestNode,CheckSubNodes,false);
if not Result then exit;
SrcNode:=SrcNode.Next;
DestNode:=DestNode.Next;
end;
Result:=(SrcNode=nil) and (DestNode=nil);
if not Result then begin
DebugLn('TDefineTemplate.IsEqual DIFF 3 ',Name,' ',
ADefineTemplate.Name,' ',dbgs(ChildCount),' ',dbgs(ADefineTemplate.ChildCount));
end;
end;
end;
function TDefineTemplate.IsAutoGenerated: boolean;
begin
Result:=SelfOrParentContainsFlag(dtfAutoGenerated);
end;
procedure TDefineTemplate.RemoveFlags(TheFlags: TDefineTemplateFlags);
var ANode: TDefineTemplate;
begin
ANode:=Self;
while ANode<>nil do begin
Flags:=Flags-TheFlags;
if FirstChild<>nil then FirstChild.RemoveFlags(TheFlags);
ANode:=ANode.Next;
end;
end;
function TDefineTemplate.Level: integer;
var ANode: TDefineTemplate;
begin
Result:=-1;
ANode:=Self;
while ANode<>nil do begin
inc(Result);
ANode:=ANode.Parent;
end;
end;
function TDefineTemplate.GetFirstSibling: TDefineTemplate;
begin
Result:=Self;
while Result.Prior<>nil do Result:=Result.Prior;
end;
function TDefineTemplate.SelfOrParentContainsFlag(
AFlag: TDefineTemplateFlag): boolean;
var Node: TDefineTemplate;
begin
Node:=Self;
while (Node<>nil) do begin
if AFlag in Node.Flags then begin
Result:=true;
exit;
end;
Node:=Node.Parent;
end;
Result:=false;
end;
function TDefineTemplate.FindChildByName(const AName: string): TDefineTemplate;
begin
if FirstChild<>nil then begin
Result:=FirstChild.FindByName(AName,false,true)
end else
Result:=nil;
end;
function TDefineTemplate.FindByName(const AName: string; WithSubChilds,
WithNextSiblings: boolean): TDefineTemplate;
var ANode: TDefineTemplate;
begin
if CompareText(AName,Name)=0 then begin
Result:=Self;
end else begin
if WithSubChilds and (FirstChild<>nil) then
Result:=FirstChild.FindByName(AName,true,true)
else
Result:=nil;
if (Result=nil) and WithNextSiblings then begin
ANode:=Next;
while (ANode<>nil) do begin
Result:=ANode.FindByName(AName,WithSubChilds,false);
if Result<>nil then break;
ANode:=ANode.Next;
end;
end;
end;
end;
function TDefineTemplate.FindUniqueName(const Prefix: string): string;
var Root: TDefineTemplate;
i: integer;
begin
Root:=FindRoot;
i:=0;
repeat
inc(i);
Result:=Prefix+IntToStr(i);
until Root.FindByName(Result,true,true)=nil;
end;
{ TDirectoryDefines }
constructor TDirectoryDefines.Create;
begin
inherited Create;
Values:=TExpressionEvaluator.Create;
Path:='';
end;
destructor TDirectoryDefines.Destroy;
begin
Values.Free;
inherited Destroy;
end;
procedure TDirectoryDefines.CalcMemSize(Stats: TCTMemStats);
begin
Stats.Add('TDirectoryDefines',PtrUInt(InstanceSize)
+MemSizeString(Path));
if Values<>nil then
Stats.Add('TDirectoryDefines.Values',Values.CalcMemSize(false,nil));
end;
{ TDefineTree }
procedure TDefineTree.Clear;
begin
if FFirstDefineTemplate<>nil then begin
FFirstDefineTemplate.Clear(true);
FFirstDefineTemplate.Free;
FFirstDefineTemplate:=nil;
end;
ClearCache;
end;
function TDefineTree.IsEqual(SrcDefineTree: TDefineTree): boolean;
begin
Result:=false;
if SrcDefineTree=nil then exit;
if (FFirstDefineTemplate=nil) xor (SrcDefineTree.FFirstDefineTemplate=nil)
then exit;
if (FFirstDefineTemplate<>nil)
and (not FFirstDefineTemplate.IsEqual(
SrcDefineTree.FFirstDefineTemplate,true,true))
then exit;
Result:=true;
end;
procedure TDefineTree.Assign(SrcDefineTree: TDefineTree);
begin
if IsEqual(SrcDefineTree) then exit;
Clear;
if SrcDefineTree.FFirstDefineTemplate<>nil then begin
FFirstDefineTemplate:=TDefineTemplate.Create;
FFirstDefineTemplate.Assign(SrcDefineTree.FFirstDefineTemplate,
true,true,true);
end;
end;
procedure TDefineTree.AssignNonAutoCreated(SrcDefineTree: TDefineTree);
var
SrcNonAutoCreated: TDefineTemplate;
begin
MarkNonAutoCreated;
RemoveMarked;
SrcNonAutoCreated:=SrcDefineTree.ExtractNonAutoCreated;
if SrcNonAutoCreated=nil then exit;
//DebugLn('TDefineTree.AssignNonAutoCreated A Front=',SrcNonAutoCreated.MergeNameInFront,' Behind=',SrcNonAutoCreated.MergeNameBehind);
MergeTemplates(SrcNonAutoCreated,'');
SrcNonAutoCreated.Clear(true);
SrcNonAutoCreated.Free;
FFirstDefineTemplate.CreateMergeInfo(true,false);
//DebugLn('TDefineTree.AssignNonAutoCreated B Front=',FFirstDefineTemplate.MergeNameInFront,' Behind=',FFirstDefineTemplate.MergeNameBehind);
end;
procedure TDefineTree.ClearCache;
begin
if (FCache.Count=0) and (FVirtualDirCache=nil) then exit;
DoClearCache;
end;
constructor TDefineTree.Create;
begin
inherited Create;
FFirstDefineTemplate:=nil;
FCache:=TAVLTree.Create(@CompareDirectoryDefines);
FDefineStrings:=TStringTree.Create;
FMacroFunctions:=TKeyWordFunctionList.Create;
FMacroFunctions.AddExtended('Ext',nil,@MacroFuncExtractFileExt);
FMacroFunctions.AddExtended('PATH',nil,@MacroFuncExtractFilePath);
FMacroFunctions.AddExtended('NAME',nil,@MacroFuncExtractFileName);
FMacroFunctions.AddExtended('NAMEONLY',nil,@MacroFuncExtractFileNameOnly);
FMacroVariables:=TKeyWordFunctionList.Create;
end;
destructor TDefineTree.Destroy;
begin
Clear;
FMacroVariables.Free;
FMacroFunctions.Free;
FCache.Free;
FreeAndNil(FDefineStrings);
inherited Destroy;
end;
function TDefineTree.GetLastRootTemplate: TDefineTemplate;
begin
Result:=FFirstDefineTemplate;
if Result=nil then exit;
while Result.Next<>nil do Result:=Result.Next;
end;
function TDefineTree.FindDirectoryInCache(
const Path: string): TDirectoryDefines;
var cmp: integer;
ANode: TAVLTreeNode;
begin
ANode:=FCache.Root;
while (ANode<>nil) do begin
cmp:=CompareFilenames(Path,TDirectoryDefines(ANode.Data).Path);
if cmp<0 then
ANode:=ANode.Left
else if cmp>0 then
ANode:=ANode.Right
else
break;
end;
if ANode<>nil then
Result:=TDirectoryDefines(ANode.Data)
else
Result:=nil;
end;
function TDefineTree.GetDirDefinesForDirectory(const Path: string;
WithVirtualDir: boolean): TDirectoryDefines;
var
ExpPath: String;
begin
//DebugLn('[TDefineTree.GetDirDefinesForDirectory] "',Path,'"');
if (Path<>'') or (not WithVirtualDir) then begin
DoPrepareTree;
ExpPath:=TrimFilename(Path);
if (ExpPath<>'') and (ExpPath[length(ExpPath)]<>PathDelim) then
ExpPath:=ExpPath+PathDelim;
Result:=FindDirectoryInCache(ExpPath);
if Result=nil then begin
Result:=TDirectoryDefines.Create;
Result.Path:=ExpPath;
//DebugLn('[TDefineTree.GetDirDefinesForDirectory] B ',ExpPath,' ');
if Calculate(Result) then begin
//DebugLn('[TDefineTree.GetDirDefinesForDirectory] C success');
RemoveDoubles(Result);
FCache.Add(Result);
end else begin
Result.Free;
Result:=nil;
end;
end;
end else begin
Result:=GetDirDefinesForVirtualDirectory;
end;
end;
function TDefineTree.GetDirDefinesForVirtualDirectory: TDirectoryDefines;
begin
DoPrepareTree;
if FVirtualDirCache=nil then begin
//DebugLn('################ TDefineTree.GetDirDefinesForVirtualDirectory');
FVirtualDirCache:=TDirectoryDefines.Create;
FVirtualDirCache.Path:=VirtualDirectory;
if Calculate(FVirtualDirCache) then begin
//DebugLn('TDefineTree.GetDirDefinesForVirtualDirectory ');
RemoveDoubles(FVirtualDirCache);
end else begin
FVirtualDirCache.Free;
FVirtualDirCache:=nil;
end;
end;
Result:=FVirtualDirCache;
end;
function TDefineTree.MacroFuncExtractFileExt(Data: Pointer): boolean;
var
FuncData: PReadFunctionData;
begin
FuncData:=PReadFunctionData(Data);
FuncData^.Result:=ExtractFileExt(FuncData^.Param);
Result:=true;
end;
function TDefineTree.MacroFuncExtractFilePath(Data: Pointer): boolean;
var
FuncData: PReadFunctionData;
begin
FuncData:=PReadFunctionData(Data);
FuncData^.Result:=ExtractFilePath(FuncData^.Param);
Result:=true;
end;
function TDefineTree.MacroFuncExtractFileName(Data: Pointer): boolean;
var
FuncData: PReadFunctionData;
begin
FuncData:=PReadFunctionData(Data);
FuncData^.Result:=ExtractFileName(FuncData^.Param);
Result:=true;
end;
function TDefineTree.MacroFuncExtractFileNameOnly(Data: Pointer): boolean;
var
FuncData: PReadFunctionData;
begin
FuncData:=PReadFunctionData(Data);
FuncData^.Result:=ExtractFileNameOnly(FuncData^.Param);
Result:=true;
end;
procedure TDefineTree.DoClearCache;
begin
{$IFDEF VerboseDefineCache}
DebugLn('TDefineTree.DoClearCache A +++++++++');
{$ENDIF}
if FCache<>nil then FCache.FreeAndClear;
if FVirtualDirCache<>nil then begin
FVirtualDirCache.Free;
FVirtualDirCache:=nil;
end;
IncreaseChangeStep;
FDefineStrings.Clear;
end;
procedure TDefineTree.DoPrepareTree;
begin
if Assigned(OnPrepareTree) then OnPrepareTree(Self);
end;
procedure TDefineTree.RemoveMarked;
begin
if FFirstDefineTemplate=nil then exit;
FFirstDefineTemplate.RemoveMarked(true,FFirstDefineTemplate);
ClearCache;
end;
procedure TDefineTree.MarkNonAutoCreated;
begin
if FFirstDefineTemplate=nil then exit;
with FFirstDefineTemplate do begin
// clear marks
UnmarkNodes(true,true);
// mark each non autocreated node
MarkFlags([],[dtfAutoGenerated],true,true);
// mark every parent with a marked child
InheritMarks(true,true,false,true);
end;
end;
function TDefineTree.GetUnitPathForDirectory(const Directory: string): string;
var Evaluator: TExpressionEvaluator;
begin
Evaluator:=GetDefinesForDirectory(Directory,true);
if Evaluator<>nil then begin
Result:=Evaluator.Variables[UnitPathMacroName];
end else begin
Result:='';
end;
end;
function TDefineTree.GetIncludePathForDirectory(const Directory: string
): string;
var Evaluator: TExpressionEvaluator;
begin
Evaluator:=GetDefinesForDirectory(Directory,true);
if Evaluator<>nil then begin
Result:=Evaluator.Variables[IncludePathMacroName];
end else begin
Result:='';
end;
end;
function TDefineTree.GetSrcPathForDirectory(const Directory: string): string;
var Evaluator: TExpressionEvaluator;
begin
Evaluator:=GetDefinesForDirectory(Directory,true);
if Evaluator<>nil then begin
Result:=Evaluator.Variables[SrcPathMacroName];
end else begin
Result:='';
end;
end;
function TDefineTree.GetPPUSrcPathForDirectory(const Directory: string
): string;
var Evaluator: TExpressionEvaluator;
begin
Evaluator:=GetDefinesForDirectory(Directory,true);
if Evaluator<>nil then begin
Result:=Evaluator.Variables[PPUSrcPathMacroName];
end else begin
Result:='';
end;
end;
function TDefineTree.GetDCUSrcPathForDirectory(const Directory: string
): string;
var Evaluator: TExpressionEvaluator;
begin
Evaluator:=GetDefinesForDirectory(Directory,true);
if Evaluator<>nil then begin
Result:=Evaluator.Variables[DCUSrcPathMacroName];
end else begin
Result:='';
end;
end;
function TDefineTree.GetCompiledSrcPathForDirectory(const Directory: string
): string;
var
Evaluator: TExpressionEvaluator;
begin
Evaluator:=GetDefinesForDirectory(Directory,true);
if Evaluator<>nil then begin
Result:=Evaluator.Variables[CompiledSrcPathMacroName];
end else begin
Result:='';
end;
end;
function TDefineTree.GetDefinesForDirectory(
const Path: string; WithVirtualDir: boolean): TExpressionEvaluator;
var
DirDef: TDirectoryDefines;
begin
DirDef:=GetDirDefinesForDirectory(Path,WithVirtualDir);
if DirDef<>nil then
Result:=DirDef.Values
else
Result:=nil;
end;
function TDefineTree.GetDefinesForVirtualDirectory: TExpressionEvaluator;
var
DirDef: TDirectoryDefines;
begin
DirDef:=GetDirDefinesForVirtualDirectory;
if DirDef<>nil then
Result:=DirDef.Values
else
Result:=nil;
end;
procedure TDefineTree.ReadValue(const DirDef: TDirectoryDefines;
const PreValue, CurDefinePath: string; out NewValue: string);
var
Buffer: PChar;
BufferPos: integer;
BufferSize: integer;
ValuePos: integer;
function SearchBracketClose(const s: string; Position:integer): integer;
var BracketClose:char;
sLen: Integer;
begin
if s[Position]='(' then
BracketClose:=')'
else
BracketClose:='{';
inc(Position);
sLen:=length(s);
while (Position<=sLen) and (s[Position]<>BracketClose) do begin
if s[Position]=SpecialChar then
inc(Position)
else if (s[Position] in ['(','{']) then
Position:=SearchBracketClose(s,Position);
inc(Position);
end;
Result:=Position;
end;
function ExecuteMacroFunction(const FuncName, Params: string): string;
var
FuncData: TReadFunctionData;
begin
FuncData.Param:=Params;
FuncData.Result:='';
FMacroFunctions.DoDataFunction(PChar(Pointer(FuncName)),length(FuncName),
@FuncData);
Result:=FuncData.Result;
end;
function ExecuteMacroVariable(var MacroVariable: string): boolean;
var
FuncData: TReadFunctionData;
begin
FuncData.Param:=MacroVariable;
FuncData.Result:='';
Result:=FMacroVariables.DoDataFunction(
PChar(Pointer(MacroVariable)),length(MacroVariable),@FuncData);
if Result then
MacroVariable:=FuncData.Result;
end;
procedure GrowBuffer(MinSize: integer);
var
NewSize: Integer;
begin
if MinSize<=BufferSize then exit;
NewSize:=MinSize*2+100;
ReAllocMem(Buffer,NewSize);
BufferSize:=NewSize;
end;
procedure CopyStringToBuffer(const Src: string);
begin
if Src='' then exit;
Move(Src[1],Buffer[BufferPos],length(Src));
inc(BufferPos,length(Src));
end;
procedure CopyFromValueToBuffer(Len: integer);
begin
if Len=0 then exit;
Move(NewValue[ValuePos],Buffer[BufferPos],Len);
inc(BufferPos,Len);
inc(ValuePos,Len);
end;
function Substitute(const CurValue: string; ValueLen: integer;
MacroStart: integer; var MacroEnd: integer): boolean;
var
MacroFuncNameEnd: Integer;
MacroFuncNameLen: Integer;
MacroStr: String;
MacroFuncName: String;
NewMacroLen: Integer;
MacroParam: string;
OldMacroLen: Integer;
Handled: Boolean;
MacroVarName: String;
begin
Result:=false;
MacroFuncNameEnd:=MacroEnd;
MacroFuncNameLen:=MacroFuncNameEnd-MacroStart-1;
MacroEnd:=SearchBracketClose(CurValue,MacroFuncNameEnd)+1;
if MacroEnd>ValueLen+1 then exit;
OldMacroLen:=MacroEnd-MacroStart;
// Macro found
if MacroFuncNameLen>0 then begin
MacroFuncName:=copy(CurValue,MacroStart+1,MacroFuncNameLen);
// Macro function -> substitute macro parameter first
ReadValue(DirDef,copy(CurValue,MacroFuncNameEnd+1
,MacroEnd-MacroFuncNameEnd-2),CurDefinePath,MacroParam);
// execute the macro function
//debugln('Substitute MacroFuncName="',MacroFuncName,'" MacroParam="',MacroParam,'"');
MacroStr:=ExecuteMacroFunction(MacroFuncName,MacroParam);
end else begin
// Macro variable
MacroVarName:=copy(CurValue,MacroStart+2,MacroEnd-MacroStart-3);
MacroStr:=MacroVarName;
//DebugLn('**** MacroVarName=',MacroVarName,' ',DirDef.Values.Variables[MacroVarName]);
//DebugLn('DirDef.Values=',DirDef.Values.AsString);
if MacroVarName=DefinePathMacroName then begin
MacroStr:=CurDefinePath;
end else if DirDef.Values.IsDefined(MacroVarName) then begin
MacroStr:=DirDef.Values.Variables[MacroVarName];
end else begin
Handled:=false;
if Assigned(FOnReadValue) then begin
MacroParam:=MacroVarName;
MacroStr:='';
FOnReadValue(Self,MacroParam,MacroStr,Handled);
end;
if not Handled then begin
MacroStr:=MacroVarName;
Handled:=ExecuteMacroVariable(MacroStr);
end;
if not Handled then begin
MacroStr:='';
end;
end;
end;
NewMacroLen:=length(MacroStr);
GrowBuffer(BufferPos+NewMacroLen-OldMacroLen+ValueLen-ValuePos+1);
// copy text between this macro and last macro
CopyFromValueToBuffer(MacroStart-ValuePos);
// copy macro value to buffer
CopyStringToBuffer(MacroStr);
ValuePos:=MacroEnd;
Result:=true;
end;
procedure SetNewValue;
var
RestLen: Integer;
begin
if Buffer=nil then exit;
// write rest to buffer
RestLen:=length(NewValue)-ValuePos+1;
if RestLen>0 then begin
GrowBuffer(BufferPos+RestLen);
Move(NewValue[ValuePos],Buffer[BufferPos],RestLen);
inc(BufferPos,RestLen);
end;
// copy the buffer into NewValue
//DebugLn(' [ReadValue] Old="',copy(NewValue,1,100),'"');
SetLength(NewValue,BufferPos);
if BufferPos>0 then
Move(Buffer^,NewValue[1],BufferPos);
//DebugLn(' [ReadValue] New="',copy(NewValue,1,100),'"');
// clean up
FreeMem(Buffer);
Buffer:=nil;
end;
var MacroStart,MacroEnd: integer;
ValueLen: Integer;
begin
// DebugLn(' [ReadValue] A "',copy(PreValue,1,100),'"');
NewValue:=PreValue;
if NewValue='' then exit;
MacroStart:=1;
ValueLen:=length(NewValue);
Buffer:=nil;
BufferSize:=0;
BufferPos:=0; // position in buffer
ValuePos:=1; // same position in value
while MacroStart<=ValueLen do begin
// search for macro
while (MacroStart<=ValueLen) and (NewValue[MacroStart]<>'$') do begin
if (NewValue[MacroStart]=SpecialChar) then inc(MacroStart);
inc(MacroStart);
end;
if MacroStart>ValueLen then break;
// read macro function name
MacroEnd:=MacroStart+1;
while (MacroEnd<=ValueLen)
and (NewValue[MacroEnd] in ['0'..'9','A'..'Z','a'..'z','_']) do
inc(MacroEnd);
// read macro name / parameters
if (MacroEnd<ValueLen) and (NewValue[MacroEnd] in ['(','{']) then
begin
if not Substitute(NewValue,ValueLen,MacroStart,MacroEnd) then break;
end;
MacroStart:=MacroEnd;
end;
if Buffer<>nil then SetNewValue;
end;
procedure TDefineTree.MarkTemplatesOwnedBy(TheOwner: TObject; const MustFlags,
NotFlags: TDefineTemplateFlags);
begin
if FFirstDefineTemplate=nil then exit;
with FFirstDefineTemplate do begin
// unmark all nodes
UnmarkNodes(true,true);
// mark each node in filter
MarkOwnedBy(TheOwner,MustFlags,NotFlags,true,true);
// mark every parent, that has a marked child
InheritMarks(true,true,false,true);
end;
end;
procedure TDefineTree.RemoveTemplatesOwnedBy(TheOwner: TObject;
const MustFlags, NotFlags: TDefineTemplateFlags);
begin
if FFirstDefineTemplate=nil then exit;
FFirstDefineTemplate.RemoveLeaves(TheOwner,MustFlags,NotFlags,true,
FFirstDefineTemplate);
FFirstDefineTemplate.RemoveOwner(TheOwner,true);
ClearCache;
end;
function TDefineTree.ExtractTemplatesOwnedBy(TheOwner: TObject;
const MustFlags, NotFlags: TDefineTemplateFlags): TDefineTemplate;
begin
Result:=nil;
if FFirstDefineTemplate=nil then exit;
MarkTemplatesOwnedBy(TheOwner,MustFlags,NotFlags);
with FFirstDefineTemplate do begin
// store some information, so that merging the nodes will result in old order
CreateMergeInfo(true,false);
// extract marked nodes
Result:=CreateCopy(true,true,true);
end;
end;
function TDefineTree.ExtractNonAutoCreated: TDefineTemplate;
begin
Result:=nil;
if FFirstDefineTemplate=nil then exit;
MarkNonAutoCreated;
with FFirstDefineTemplate do begin
// store some information, so that merging the nodes will result in old order
CreateMergeInfo(true,false);
// extract marked nodes
Result:=CreateCopy(true,true,true);
end;
end;
procedure TDefineTree.MergeTemplates(SourceTemplate: TDefineTemplate;
const NewNamePrefix: string);
var
LastDefTempl: TDefineTemplate;
begin
LastDefTempl:=GetLastRootTemplate;
TDefineTemplate.MergeTemplates(nil,FFirstDefineTemplate,LastDefTempl,
SourceTemplate,true,NewNamePrefix);
ClearCache;
end;
function TDefineTree.Calculate(DirDef: TDirectoryDefines): boolean;
// calculates the values for a single directory
// returns false on error
var
ExpandedDirectory, EvalResult, TempValue: string;
procedure CalculateTemplate(DefTempl: TDefineTemplate; const CurPath: string);
procedure CalculateIfChilds;
begin
// execute childs
CalculateTemplate(DefTempl.FirstChild,CurPath);
// jump to end of else templates
while (DefTempl.Next<>nil)
and (DefTempl.Next.Action in [da_Else,da_ElseIf])
do begin
if Assigned(OnCalculate) then
OnCalculate(Self,DefTempl,false,'',false,'',false);
DefTempl:=DefTempl.Next;
end;
end;
// procedure CalculateTemplate(DefTempl: TDefineTemplate; const CurPath: string);
var SubPath, TempValue: string;
begin
while DefTempl<>nil do begin
//DebugLn(' [CalculateTemplate] CurPath="',CurPath,'" DefTempl.Name="',DefTempl.Name,'"');
case DefTempl.Action of
da_Block:
// calculate children
begin
if Assigned(OnCalculate) then
OnCalculate(Self,DefTempl,false,'',false,'',true);
CalculateTemplate(DefTempl.FirstChild,CurPath);
end;
da_Define:
// Define for a single Directory (not SubDirs)
begin
if FilenameIsMatching(CurPath,ExpandedDirectory,true) then begin
ReadValue(DirDef,DefTempl.Value,CurPath,TempValue);
if Assigned(OnCalculate) then
OnCalculate(Self,DefTempl,true,TempValue,false,'',true);
DirDef.Values.Variables[DefTempl.Variable]:=TempValue;
end else begin
if Assigned(OnCalculate) then
OnCalculate(Self,DefTempl,false,'',false,'',false);
end;
end;
da_DefineRecurse:
// Define for current and sub directories
begin
ReadValue(DirDef,DefTempl.Value,CurPath,TempValue);
if Assigned(OnCalculate) then
OnCalculate(Self,DefTempl,true,TempValue,false,'',true);
DirDef.Values.Variables[DefTempl.Variable]:=TempValue;
end;
da_Undefine:
// Undefine for a single Directory (not SubDirs)
if FilenameIsMatching(CurPath,ExpandedDirectory,true) then begin
if Assigned(OnCalculate) then
OnCalculate(Self,DefTempl,false,'',false,'',true);
DirDef.Values.Undefine(DefTempl.Variable);
end else begin
if Assigned(OnCalculate) then
OnCalculate(Self,DefTempl,false,'',false,'',false);
end;
da_UndefineRecurse:
// Undefine for current and sub directories
begin
if Assigned(OnCalculate) then
OnCalculate(Self,DefTempl,false,'',false,'',true);
DirDef.Values.Undefine(DefTempl.Variable);
end;
da_UndefineAll:
// Undefine every value for current and sub directories
begin
if Assigned(OnCalculate) then
OnCalculate(Self,DefTempl,false,'',false,'',true);
DirDef.Values.Clear;
end;
da_If, da_ElseIf:
begin
// test expression in value
ReadValue(DirDef,DefTempl.Value,CurPath,TempValue);
EvalResult:=DirDef.Values.Eval(TempValue,true);
if Assigned(OnCalculate) then
OnCalculate(Self,DefTempl,true,TempValue,true,EvalResult,EvalResult='1');
//debugln('da_If,da_ElseIf: DefTempl.Value="',DbgStr(DefTempl.Value),'" CurPath="',CurPath,'" TempValue="',TempValue,'" EvalResult=',EvalResult);
if DirDef.Values.ErrorPosition>=0 then begin
FErrorDescription:=Format(ctsSyntaxErrorInExpr,[TempValue]);
FErrorTemplate:=DefTempl;
//debugln(['CalculateTemplate "',FErrorDescription,'"']);
end else if EvalResult='1' then
CalculateIfChilds;
end;
da_IfDef:
// test if variable is defined
begin
//DebugLn('da_IfDef A Name=',DefTempl.Name,
// ' Variable=',DefTempl.Variable,
// ' Is=',dbgs(DirDef.Values.IsDefined(DefTempl.Variable)),
// ' CurPath="',CurPath,'"',
// ' Values.Count=',dbgs(DirDef.Values.Count));
if DirDef.Values.IsDefined(DefTempl.Variable) then begin
if Assigned(OnCalculate) then
OnCalculate(Self,DefTempl,false,'',false,'',true);
CalculateIfChilds;
end else begin
if Assigned(OnCalculate) then
OnCalculate(Self,DefTempl,false,'',false,'',false);
end;
end;
da_IfNDef:
// test if variable is not defined
if not DirDef.Values.IsDefined(DefTempl.Variable) then begin
if Assigned(OnCalculate) then
OnCalculate(Self,DefTempl,false,'',false,'',true);
CalculateIfChilds;
end else begin
if Assigned(OnCalculate) then
OnCalculate(Self,DefTempl,false,'',false,'',false);
end;
da_Else:
// execute childs
begin
if Assigned(OnCalculate) then
OnCalculate(Self,DefTempl,false,'',false,'',true);
CalculateTemplate(DefTempl.FirstChild,CurPath);
end;
da_Directory:
begin
// template for a sub directory
ReadValue(DirDef,DefTempl.Value,CurPath,TempValue);
// CurPath can be ''
SubPath:=AppendPathDelim(CurPath)+TempValue;
// test if ExpandedDirectory is part of SubPath
if FilenameIsMatching(SubPath,ExpandedDirectory,false) then begin
if Assigned(OnCalculate) then
OnCalculate(Self,DefTempl,true,SubPath,false,'',true);
CalculateTemplate(DefTempl.FirstChild,SubPath);
end else begin
if Assigned(OnCalculate) then
OnCalculate(Self,DefTempl,true,SubPath,false,'',false);
end;
end;
end;
if ErrorTemplate<>nil then exit;
if DefTempl<>nil then
DefTempl:=DefTempl.Next;
end;
end;
// function TDefineTree.Calculate(DirDef: TDirectoryDefines): boolean;
begin
{$IFDEF VerboseDefineCache}
DebugLn('[TDefineTree.Calculate] ++++++ "',DirDef.Path,'"');
{$ENDIF}
Result:=true;
FErrorTemplate:=nil;
ExpandedDirectory:=DirDef.Path;
if (ExpandedDirectory=VirtualDirectory)
and Assigned(OnGetVirtualDirectoryAlias) then
OnGetVirtualDirectoryAlias(Self,ExpandedDirectory);
if (ExpandedDirectory<>VirtualDirectory) then begin
ReadValue(DirDef,ExpandedDirectory,'',TempValue);
ExpandedDirectory:=TempValue;
end;
DirDef.Values.Clear;
// compute the result of all matching DefineTemplates
CalculateTemplate(FFirstDefineTemplate,'');
if (ExpandedDirectory=VirtualDirectory)
and (Assigned(OnGetVirtualDirectoryDefines)) then
OnGetVirtualDirectoryDefines(Self,DirDef);
Result:=(ErrorTemplate=nil);
end;
procedure TDefineTree.IncreaseChangeStep;
begin
if FChangeStep<>$7fffffff then
inc(FChangeStep)
else
FChangeStep:=-$7fffffff;
if DirectoryCachePool<>nil then DirectoryCachePool.IncreaseConfigTimeStamp;
end;
procedure TDefineTree.SetDirectoryCachePool(const AValue: TCTDirectoryCachePool
);
begin
if FDirectoryCachePool=AValue then exit;
FDirectoryCachePool:=AValue;
end;
procedure TDefineTree.RemoveDoubles(Defines: TDirectoryDefines);
// use only one copy of each ansistring
begin
if Defines=nil then exit;
Defines.Values.RemoveDoubles(@FDefineStrings.ReplaceString);
end;
procedure TDefineTree.Add(ADefineTemplate: TDefineTemplate);
// add as last
var LastDefTempl: TDefineTemplate;
begin
if ADefineTemplate=nil then exit;
if RootTemplate=nil then
RootTemplate:=ADefineTemplate
else begin
// add as last
LastDefTempl:=RootTemplate;
while LastDefTempl.Next<>nil do
LastDefTempl:=LastDefTempl.Next;
ADefineTemplate.InsertBehind(LastDefTempl);
end;
ClearCache;
end;
procedure TDefineTree.AddFirst(ADefineTemplate: TDefineTemplate);
// add as first
begin
if ADefineTemplate=nil then exit;
if RootTemplate=nil then
RootTemplate:=ADefineTemplate
else begin
RootTemplate.InsertBehind(ADefineTemplate);
RootTemplate:=ADefineTemplate;
end;
ClearCache;
end;
procedure TDefineTree.MoveToLast(ADefineTemplate: TDefineTemplate);
begin
if (ADefineTemplate.Next=nil) and (ADefineTemplate.Parent=nil) then exit;
ADefineTemplate.Unbind;
if FFirstDefineTemplate=ADefineTemplate then FFirstDefineTemplate:=nil;
Add(ADefineTemplate);
end;
function TDefineTree.FindDefineTemplateByName(
const AName: string; OnlyRoots: boolean): TDefineTemplate;
begin
Result:=RootTemplate;
if RootTemplate<>nil then
Result:=RootTemplate.FindByName(AName,not OnlyRoots,true)
else
Result:=nil;
end;
procedure TDefineTree.ReplaceRootSameName(const Name: string;
ADefineTemplate: TDefineTemplate);
// if there is a DefineTemplate with the same name then replace it
// else add as last
var OldDefineTemplate: TDefineTemplate;
begin
if (Name='') then exit;
OldDefineTemplate:=FindDefineTemplateByName(Name,true);
if OldDefineTemplate<>nil then begin
if not OldDefineTemplate.IsEqual(ADefineTemplate,true,false) then begin
ClearCache;
end;
if ADefineTemplate<>nil then
ADefineTemplate.InsertBehind(OldDefineTemplate);
if OldDefineTemplate=FFirstDefineTemplate then
FFirstDefineTemplate:=FFirstDefineTemplate.Next;
OldDefineTemplate.Unbind;
OldDefineTemplate.Free;
end else
Add(ADefineTemplate);
end;
procedure TDefineTree.RemoveRootDefineTemplateByName(const AName: string);
var ADefTempl: TDefineTemplate;
begin
ADefTempl:=FindDefineTemplateByName(AName,true);
if ADefTempl<>nil then RemoveDefineTemplate(ADefTempl);
end;
procedure TDefineTree.RemoveDefineTemplate(ADefTempl: TDefineTemplate);
var
HadDefines: Boolean;
begin
if ADefTempl=FFirstDefineTemplate then
FFirstDefineTemplate:=FFirstDefineTemplate.Next;
HadDefines:=ADefTempl.HasDefines(false,false);
ADefTempl.Unbind;
ADefTempl.Free;
if HadDefines then ClearCache;
end;
procedure TDefineTree.ReplaceChild(ParentTemplate,
NewDefineTemplate: TDefineTemplate; const ChildName: string);
// if there is a DefineTemplate with the same name then replace it
// else add as last
var OldDefineTemplate: TDefineTemplate;
begin
if (ChildName='') or (ParentTemplate=nil) then exit;
OldDefineTemplate:=ParentTemplate.FindChildByName(ChildName);
if OldDefineTemplate<>nil then begin
if not OldDefineTemplate.IsEqual(NewDefineTemplate,true,false) then begin
ClearCache;
end;
if NewDefineTemplate<>nil then
NewDefineTemplate.InsertBehind(OldDefineTemplate);
if OldDefineTemplate=FFirstDefineTemplate then
FFirstDefineTemplate:=FFirstDefineTemplate.Next;
OldDefineTemplate.Unbind;
OldDefineTemplate.Free;
end else begin
ClearCache;
ParentTemplate.AddChild(NewDefineTemplate);
end;
end;
procedure TDefineTree.AddChild(ParentTemplate,
NewDefineTemplate: TDefineTemplate);
begin
ClearCache;
ParentTemplate.AddChild(NewDefineTemplate);
end;
procedure TDefineTree.ReplaceRootSameName(ADefineTemplate: TDefineTemplate);
begin
if (ADefineTemplate=nil) then exit;
ReplaceRootSameName(ADefineTemplate.Name,ADefineTemplate);
end;
procedure TDefineTree.ReplaceRootSameNameAddFirst(
ADefineTemplate: TDefineTemplate);
var OldDefineTemplate: TDefineTemplate;
begin
if ADefineTemplate=nil then exit;
OldDefineTemplate:=FindDefineTemplateByName(ADefineTemplate.Name,true);
if OldDefineTemplate<>nil then begin
if not OldDefineTemplate.IsEqual(ADefineTemplate,true,false) then begin
ClearCache;
end;
ADefineTemplate.InsertBehind(OldDefineTemplate);
if OldDefineTemplate=FFirstDefineTemplate then
FFirstDefineTemplate:=FFirstDefineTemplate.Next;
OldDefineTemplate.Unbind;
OldDefineTemplate.Free;
end else
AddFirst(ADefineTemplate);
end;
procedure TDefineTree.MergeDefineTemplates(SourceTemplate: TDefineTemplate;
const NewNamePrefix: string);
var
LastDefTempl: TDefineTemplate;
begin
if SourceTemplate=nil then exit;
// import new defines
LastDefTempl:=GetLastRootTemplate;
TDefineTemplate.MergeTemplates(nil,FFirstDefineTemplate,LastDefTempl,
SourceTemplate,true,NewNamePrefix);
ClearCache;
end;
procedure TDefineTree.ConsistencyCheck;
var
CurResult: LongInt;
begin
if FFirstDefineTemplate<>nil then
FFirstDefineTemplate.ConsistencyCheck;
CurResult:=FCache.ConsistencyCheck;
if CurResult<>0 then
RaiseCatchableException(IntToStr(CurResult));
end;
procedure TDefineTree.CalcMemSize(Stats: TCTMemStats);
var
Node: TAVLTreeNode;
begin
Stats.Add('TDefineTree',PtrUInt(InstanceSize)
+MemSizeString(FErrorDescription)
);
if FMacroFunctions<>nil then
Stats.Add('TDefineTree.FMacroFunctions',FMacroFunctions.CalcMemSize);
if FMacroVariables<>nil then
Stats.Add('TDefineTree.FMacroVariables',FMacroVariables.CalcMemSize);
if FFirstDefineTemplate<>nil then
FFirstDefineTemplate.CalcMemSize(Stats);
if FVirtualDirCache<>nil then
FVirtualDirCache.CalcMemSize(Stats);
if FDefineStrings<>nil then
Stats.Add('TDefineTree.FDefineStrings',FDefineStrings.CalcMemSize);
if FCache<>nil then begin
Stats.Add('TDefineTree.FCache.Count',FCache.Count);
Node:=FCache.FindLowest;
while Node<>nil do begin
TDirectoryDefines(Node.Data).CalcMemSize(Stats);
Node:=FCache.FindSuccessor(Node);
end;
end;
end;
procedure TDefineTree.WriteDebugReport;
begin
DebugLn('TDefineTree.WriteDebugReport');
if FFirstDefineTemplate<>nil then
FFirstDefineTemplate.WriteDebugReport(false)
else
DebugLn(' No templates defined');
DebugLn(FCache.ReportAsString);
DebugLn('');
ConsistencyCheck;
end;
{ TDefinePool }
constructor TDefinePool.Create;
begin
inherited Create;
FItems:=TFPList.Create;
end;
destructor TDefinePool.Destroy;
begin
Clear;
FItems.Free;
inherited Destroy;
end;
procedure TDefinePool.Clear;
var i: integer;
begin
for i:=0 to Count-1 do begin
Items[i].Clear(true);
Items[i].Free;
end;
FItems.Clear;
end;
function TDefinePool.GetItems(Index: integer): TDefineTemplate;
begin
Result:=TDefineTemplate(FItems[Index]);
end;
procedure TDefinePool.SetEnglishErrorMsgFilename(const AValue: string);
begin
if FEnglishErrorMsgFilename=AValue then exit;
FEnglishErrorMsgFilename:=AValue;
end;
function TDefinePool.CheckAbort(ProgressID, MaxIndex: integer;
const Msg: string): boolean;
begin
Result:=false;
if Assigned(OnProgress) then
OnProgress(Self,ProgressID,MaxIndex,Msg,Result);
end;
procedure TDefinePool.Add(ADefineTemplate: TDefineTemplate);
begin
if ADefineTemplate<>nil then
FItems.Add(ADefineTemplate);
end;
procedure TDefinePool.Insert(Index: integer; ADefineTemplate: TDefineTemplate);
begin
FItems.Insert(Index,ADefineTemplate);
end;
procedure TDefinePool.Delete(Index: integer);
begin
Items[Index].Clear(true);
Items[Index].Free;
FItems.Delete(Index);
end;
procedure TDefinePool.Move(SrcIndex, DestIndex: integer);
begin
FItems.Move(SrcIndex,DestIndex);
end;
function TDefinePool.Count: integer;
begin
Result:=FItems.Count;
end;
function TDefinePool.CreateFPCTemplate(
const CompilerPath, CompilerOptions, TestPascalFile: string;
out UnitSearchPath, TargetOS, TargetProcessor: string;
Owner: TObject): TDefineTemplate;
// create symbol definitions for the freepascal compiler
// To get reliable values the compiler itself is asked for
var
LastDefTempl: TDefineTemplate;
procedure AddTemplate(NewDefTempl: TDefineTemplate);
begin
if NewDefTempl=nil then exit;
if LastDefTempl<>nil then
NewDefTempl.InsertBehind(LastDefTempl);
LastDefTempl:=NewDefTempl;
end;
function FindSymbol(const SymbolName: string): TDefineTemplate;
begin
Result:=LastDefTempl;
while (Result<>nil)
and (Comparetext(Result.Variable,SymbolName)<>0) do
Result:=Result.Prior;
end;
procedure DefineSymbol(const SymbolName, SymbolValue: string;
const Description: string = '');
var NewDefTempl: TDefineTemplate;
Desc: String;
begin
NewDefTempl:=FindSymbol(SymbolName);
if NewDefTempl=nil then begin
if Description<>'' then
Desc:=Description
else
Desc:=ctsDefaultFPCSymbol;
NewDefTempl:=TDefineTemplate.Create('Define '+SymbolName,
Desc,SymbolName,SymbolValue,da_DefineRecurse);
AddTemplate(NewDefTempl);
end else begin
NewDefTempl.Value:=SymbolValue;
end;
end;
procedure UndefineSymbol(const SymbolName: string);
var
ADefTempl: TDefineTemplate;
begin
ADefTempl:=FindSymbol(SymbolName);
if ADefTempl=nil then exit;
if LastDefTempl=ADefTempl then LastDefTempl:=ADefTempl.Prior;
ADefTempl.Unbind;
ADefTempl.Free;
end;
procedure ProcessOutputLine(var Line: string);
var
SymbolName, SymbolValue, UpLine, NewPath: string;
i, len, curpos: integer;
begin
len := length(Line);
if len <= 6 then Exit; // shortest match
CurPos := 1;
// strip timestamp e.g. [0.306]
if Line[CurPos] = '[' then begin
repeat
inc(CurPos);
if CurPos > len then Exit;
until line[CurPos] = ']';
Inc(CurPos, 2); // skip space too
if len - CurPos < 6 then Exit; // shortest match
end;
UpLine:=UpperCaseStr(Line);
//DebugLn(['ProcessOutputLine ',Line]);
case UpLine[CurPos] of
'M':
if StrLComp(@UpLine[CurPos], 'MACRO ', 6) = 0 then begin
// no macro
Inc(CurPos, 6);
if (StrLComp(@UpLine[CurPos], 'DEFINED: ', 9) = 0) then begin
Inc(CurPos, 9);
SymbolName:=copy(UpLine, CurPos, len);
DefineSymbol(SymbolName,'');
Exit;
end;
if (StrLComp(@UpLine[CurPos], 'UNDEFINED: ', 11) = 0) then begin
Inc(CurPos, 11);
SymbolName:=copy(UpLine,CurPos,len);
UndefineSymbol(SymbolName);
Exit;
end;
// MACRO something...
i := CurPos;
while (i <= len) and (Line[i]<>' ') do inc(i);
SymbolName:=copy(UpLine,CurPos,i-CurPos);
CurPos := i + 1; // skip space
if StrLComp(@UpLine[CurPos], 'SET TO ', 7) = 0 then begin
Inc(CurPos, 7);
SymbolValue:=copy(Line, CurPos, len);
DefineSymbol(SymbolName, SymbolValue);
end;
end;
'U':
if (StrLComp(@UpLine[CurPos], 'USING UNIT PATH: ', 17) = 0) then begin
Inc(CurPos, 17);
NewPath:=copy(Line,CurPos,len);
if not FilenameIsAbsolute(NewPath) then
NewPath:=ExpandFileNameUTF8(NewPath);
{$IFDEF VerboseFPCSrcScan}
DebugLn('Using unit path: "',NewPath,'"');
{$ENDIF}
UnitSearchPath:=UnitSearchPath+NewPath+';';
end;
end;
end;
var CmdLine: string;
i, OutLen, LineStart: integer;
TheProcess: TProcess;
OutputLine, Buf: String;
NewDefTempl: TDefineTemplate;
SrcOS: string;
SrcOS2: String;
Step: String;
begin
Result:=nil;
//DebugLn('TDefinePool.CreateFPCTemplate PPC386Path="',CompilerPath,'" FPCOptions="',CompilerOptions,'"');
if TestPascalFile='' then begin
DebugLn(['WARNING: TDefinePool.CreateFPCTemplate TestPascalFile empty']);
end;
UnitSearchPath:='';
TargetOS:='';
SrcOS:='';
TargetProcessor:='';
if (CompilerPath='') or (not FileIsExecutable(CompilerPath)) then exit;
LastDefTempl:=nil;
// find all initial compiler macros and all unit paths
// -> ask compiler with the -va switch
SetLength(Buf,1024);
Step:='Init';
try
CmdLine:=CompilerPath+' -va ';
if FileExistsCached(EnglishErrorMsgFilename) then
CmdLine:=CmdLine+'-Fr'+EnglishErrorMsgFilename+' ';
if CompilerOptions<>'' then
CmdLine:=CmdLine+CompilerOptions+' ';
CmdLine:=CmdLine+TestPascalFile;
DebugLn('TDefinePool.CreateFPCTemplate CmdLine="',CmdLine,'"');
TheProcess := TProcess.Create(nil);
TheProcess.CommandLine := UTF8ToSys(CmdLine);
TheProcess.Options:= [poUsePipes, poStdErrToOutPut];
TheProcess.ShowWindow := swoHide;
Step:='Running '+CmdLine;
try
TheProcess.Execute;
OutputLine:='';
repeat
if (TheProcess.Output<>nil) then begin
OutLen:=TheProcess.Output.Read(Buf[1],length(Buf));
end else
OutLen:=0;
LineStart:=1;
i:=1;
while i<=OutLen do begin
if Buf[i] in [#10,#13] then begin
OutputLine:=OutputLine+copy(Buf,LineStart,i-LineStart);
ProcessOutputLine(OutputLine);
OutputLine:='';
if (i<OutLen) and (Buf[i+1] in [#10,#13]) and (Buf[i]<>Buf[i+1])
then
inc(i);
LineStart:=i+1;
end;
inc(i);
end;
OutputLine:=copy(Buf,LineStart,OutLen-LineStart+1);
until OutLen=0;
TheProcess.WaitOnExit;
finally
//DebugLn('TDefinePool.CreateFPCTemplate Run with -va: OutputLine="',OutputLine,'"');
TheProcess.Free;
end;
DefineSymbol(FPCUnitPathMacroName,UnitSearchPath,'FPC default unit search path');
//DebugLn('TDefinePool.CreateFPCTemplate First done UnitSearchPath="',UnitSearchPath,'"');
// ask for target operating system -> ask compiler with switch -iTO
CmdLine:=CompilerPath;
if CompilerOptions<>'' then
CmdLine:=CmdLine+' '+CompilerOptions;
CmdLine:=CmdLine+' -iTO';
TheProcess := TProcess.Create(nil);
TheProcess.CommandLine := UTF8ToSys(CmdLine);
TheProcess.Options:= [poUsePipes, poStdErrToOutPut];
TheProcess.ShowWindow := swoHide;
Step:='Running '+CmdLine;
try
TheProcess.Execute;
if (TheProcess.Output<>nil) then
OutLen:=TheProcess.Output.Read(Buf[1],length(Buf))
else
OutLen:=0;
i:=1;
while i<=OutLen do begin
if Buf[i] in [#10,#13] then begin
// define #TargetOS
TargetOS:=copy(Buf,1,i-1);
NewDefTempl:=TDefineTemplate.Create('Define TargetOS',
ctsDefaultFPCTargetOperatingSystem,
ExternalMacroStart+'TargetOS',TargetOS,da_DefineRecurse);
AddTemplate(NewDefTempl);
// define #SrcOS
SrcOS:=GetDefaultSrcOSForTargetOS(TargetOS);
if SrcOS='' then SrcOS:=TargetOS;
NewDefTempl:=TDefineTemplate.Create('Define SrcOS',
ctsDefaultFPCSourceOperatingSystem,
ExternalMacroStart+'SrcOS',SrcOS,da_DefineRecurse);
AddTemplate(NewDefTempl);
// define #SrcOS2
SrcOS2:=GetDefaultSrcOS2ForTargetOS(TargetOS);
if SrcOS2='' then SrcOS2:=TargetOS;
NewDefTempl:=TDefineTemplate.Create('Define SrcOS2',
ctsDefaultFPCSource2OperatingSystem,
ExternalMacroStart+'SrcOS2',SrcOS2,da_DefineRecurse);
AddTemplate(NewDefTempl);
break;
end;
inc(i);
end;
TheProcess.WaitOnExit;
//DebugLn('TDefinePool.CreateFPCTemplate target OS done');
finally
//DebugLn('TDefinePool.CreateFPCTemplate Run with -iTO: OutputLine="',OutputLine,'"');
TheProcess.Free;
end;
// ask for target processor -> ask compiler with switch -iTP
TheProcess := TProcess.Create(nil);
CmdLine:=CompilerPath;
if CompilerOptions<>'' then
CmdLine:=CmdLine+' '+CompilerOptions;
CmdLine:=CmdLine+' -iTP';
TheProcess.CommandLine := UTF8ToSys(CmdLine);
TheProcess.Options:= [poUsePipes, poStdErrToOutPut];
TheProcess.ShowWindow := swoHide;
Step:='Running '+CmdLine;
try
TheProcess.Execute;
if TheProcess.Output<>nil then
OutLen:=TheProcess.Output.Read(Buf[1],length(Buf))
else
OutLen:=0;
i:=1;
while i<=OutLen do begin
if Buf[i] in [#10,#13] then begin
TargetProcessor:=copy(Buf,1,i-1);
NewDefTempl:=TDefineTemplate.Create('Define TargetProcessor',
ctsDefaultFPCTargetProcessor,
ExternalMacroStart+'TargetProcessor',TargetProcessor,
da_DefineRecurse);
AddTemplate(NewDefTempl);
break;
end;
inc(i);
end;
TheProcess.WaitOnExit;
//DebugLn('TDefinePool.CreateFPCTemplate target CPU done');
finally
//DebugLn('TDefinePool.CreateFPCTemplate Run with -iTP: OutputLine="',OutputLine,'"');
TheProcess.Free;
end;
// add
if (LastDefTempl<>nil) then begin
Result:=TDefineTemplate.Create(StdDefTemplFPC,
ctsFreePascalCompilerInitialMacros,'','',da_Block);
Result.AddChild(LastDefTempl.GetFirstSibling);
Result.SetFlags([dtfAutoGenerated],[],false);
//DebugLn('TDefinePool.CreateFPCTemplate FPC defines done');
end;
except
on E: Exception do begin
DebugLn('ERROR: TDefinePool.CreateFPCTemplate (',Step,'): ',E.Message);
end;
end;
if Result<>nil then
Result.SetDefineOwner(Owner,true);
end;
function TDefinePool.GetFPCVerFromFPCTemplate(Template: TDefineTemplate; out
FPCVersion, FPCRelease, FPCPatch: integer): boolean;
var
p: Integer;
function ReadInt(const VarName: string; var AnInteger: integer): boolean;
var
StartPos: Integer;
begin
StartPos:=p;
AnInteger:=0;
while (p<=length(VarName)) and (VarName[p] in ['0'..'9']) do begin
AnInteger:=AnInteger*10+(ord(VarName[p])-ord('0'));
if AnInteger>=100 then begin
Result:=false;
exit;
end;
inc(p);
end;
Result:=StartPos<p;
end;
function ReadVersion(const VarName: string;
out NewVersion, NewRelease, NewPatch: integer): integer;
begin
Result:=0;
if (length(VarName)>3) and (VarName[1] in ['V','v'])
and (VarName[2] in ['E','e']) and (VarName[3] in ['R','r'])
and (VarName[4] in ['0'..'9']) then begin
p:=4;
if not ReadInt(VarName,NewVersion) then exit;
inc(Result);
if (p>=length(VarName)) or (VarName[p]<>'_') then exit;
inc(p);
if not ReadInt(VarName,NewRelease) then exit;
inc(Result);
if (p>=length(VarName)) or (VarName[p]<>'_') then exit;
inc(p);
if not ReadInt(VarName,NewPatch) then exit;
inc(Result);
end;
end;
var
Def: TDefineTemplate;
VarName: String;
BestCount: integer;
NewCount: LongInt;
NewVersion: integer;
NewRelease: integer;
NewPatch: integer;
begin
Result:=false;
FPCVersion:=0;
FPCRelease:=0;
FPCPatch:=0;
BestCount:=0;
Def:=Template;
while Def<>nil do begin
if Def.Action in [da_Define,da_DefineRecurse] then begin
VarName:=Def.Variable;
NewCount:=ReadVersion(VarName,NewVersion,NewRelease,NewPatch);
if NewCount>BestCount then begin
BestCount:=NewCount;
FPCVersion:=NewVersion;
if NewCount>1 then FPCRelease:=NewRelease;
if NewCount>2 then FPCPatch:=NewPatch;
if NewCount=3 then exit;
end;
end;
Def:=Def.Next;
end;
end;
function TDefinePool.CreateFPCSrcTemplate(
const FPCSrcDir, UnitSearchPath, PPUExt, DefaultTargetOS,
DefaultProcessorName: string;
UnitLinkListValid: boolean; var UnitLinkList: string;
Owner: TObject): TDefineTemplate;
var
Dir, SrcOS, SrcOS2, TargetProcessor, UnitLinks,
IncPathMacro: string;
DS: char; // dir separator
UnitTree: TAVLTree; // tree of TDefTemplUnitNameLink
DefaultSrcOS, DefaultSrcOS2: string;
ProgressID: integer;
function d(const Filenames: string): string;
begin
Result:=SetDirSeparators(Filenames);
end;
function GatherUnits: boolean; forward;
function FindUnitLink(const AnUnitName: string): TUnitNameLink;
var ANode: TAVLTreeNode;
cmp: integer;
begin
if UnitTree=nil then GatherUnits;
ANode:=UnitTree.Root;
while ANode<>nil do begin
Result:=TUnitNameLink(ANode.Data);
cmp:=CompareText(AnUnitName,Result.Unit_Name);
if cmp<0 then
ANode:=ANode.Left
else if cmp>0 then
ANode:=ANode.Right
else
exit;
end;
Result:=nil;
end;
function GatherUnits: boolean;
function FileNameMacroCount(const AFilename: string): integer;
// count number of macros in filename
// a macro looks like this '$(name)' without a SpecialChar in front
// macronames can contain macros themselves
var i: integer;
begin
Result:=0;
i:=1;
while (i<=length(AFilename)) do begin
if (AFilename[i]=SpecialChar) then
inc(i,2)
else if (AFilename[i]='$') then begin
inc(i);
if (i<=length(AFilename)) and (AFilename[i]='(') then
inc(Result);
end else
inc(i);
end;
end;
function BuildMacroFilename(const AFilename: string;
var MacroCount, UsedMacroCount: integer): string;
// replace Operating System and Processor Type with macros
// MacroCount = number of macros are in the filename
// UsedMacroCount = number of macros fitting to the current settings
var DirStart, DirEnd, i: integer;
DirName: string;
function ReplaceDir(const MacroValue, DefaultMacroValue,
MacroName: string): boolean;
begin
Result:=false;
if CompareText(MacroValue,DirName)=0 then begin
// this is a macro
if CompareText(DirName,DefaultMacroValue)=0 then begin
// the current settings would replace the macro to fit this filename
inc(UsedMacroCount);
end;
BuildMacroFilename:=copy(BuildMacroFilename,1,DirStart-1)+MacroName+
copy(BuildMacroFilename,DirEnd,length(BuildMacroFilename)-DirEnd+1);
inc(DirEnd,length(MacroName)-length(DirName));
DirName:=MacroName;
Result:=true;
end;
end;
begin
MacroCount:=0;
Result:=copy(AFilename,length(Dir)+1,length(AFilename)-length(Dir));
DirStart:=1;
while (DirStart<=length(Result)) do begin
while (DirStart<=length(Result)) and (Result[DirStart]=PathDelim)
do
inc(DirStart);
DirEnd:=DirStart;
while (DirEnd<=length(Result)) and (Result[DirEnd]<>PathDelim) do
inc(DirEnd);
if DirEnd>length(Result) then break;
if DirEnd>DirStart then begin
DirName:=copy(Result,DirStart,DirEnd-DirStart);
// replace operating system
for i:=Low(FPCOperatingSystemNames) to High(FPCOperatingSystemNames)
do
if ReplaceDir(FPCOperatingSystemNames[i],DefaultTargetOS,TargetOSMacro)
then
break;
// replace operating system class
for i:=Low(FPCOperatingSystemAlternativeNames)
to High(FPCOperatingSystemAlternativeNames)
do
if ReplaceDir(FPCOperatingSystemAlternativeNames[i],DefaultSrcOS,
SrcOS)
then
break;
// replace operating system secondary class
for i:=Low(FPCOperatingSystemAlternative2Names)
to High(FPCOperatingSystemAlternative2Names)
do
if ReplaceDir(FPCOperatingSystemAlternative2Names[i],DefaultSrcOS2,
SrcOS2)
then
break;
// replace processor type
for i:=Low(FPCProcessorNames) to High(FPCProcessorNames) do
if ReplaceDir(FPCProcessorNames[i],DefaultProcessorName,
TargetProcessor)
then
break;
end;
DirStart:=DirEnd;
end;
Result:=Dir+Result;
end;
function IsSpecialDirectory(Dir, SpecialDir: string): boolean;
var
p1: Integer;
p2: Integer;
begin
p1:=length(Dir);
p2:=length(SpecialDir);
if (p1>=1) and (Dir[p1]=PathDelim) then dec(p1);
if (p2>=1) and (SpecialDir[p2]=PathDelim) then dec(p2);
while (p1>=1) and (p2>=1)
and (UpChars[Dir[p1]]=UpChars[SpecialDir[p2]]) do begin
dec(p1);
dec(p2);
end;
Result:=(p2=0) and ((p1=0) or (Dir[p1]=PathDelim));
end;
function BrowseDirectory(ADirPath: string; Priority: integer): boolean;
const
IgnoreDirs: array[1..16] of shortstring =(
'.', '..', 'CVS', '.svn', 'examples', 'example', 'tests', 'fake',
'ide', 'demo', 'docs', 'template', 'fakertl', 'install', 'installer',
'compiler'
);
var
AFilename, Ext, AUnitName, MacroFileName: string;
FileInfo: TSearchRec;
NewUnitLink, OldUnitLink: TUnitNameLink;
i: integer;
MacroCount, UsedMacroCount: integer;
MakeFileFPC: String;
SubDirs, GlobalSubDirs, TargetSubDirs: String;
SubPriority: Integer;
begin
Result:=true;
{$IFDEF VerboseFPCSrcScan}
DebugLn('Browse ',ADirPath);
{$ENDIF}
if ADirPath='' then exit;
ADirPath:=AppendPathDelim(ADirPath);
// check for special directories
if IsSpecialDirectory(ADirPath,'packages'+PathDelim+'amunits') then begin
{$IFDEF VerboseFPCSrcScan}
DebugLn(['BrowseDirectory skip ',ADirPath]);
{$ENDIF}
exit;
end;
inc(ProgressID);
if CheckAbort(ProgressID,-1,'') then exit(false);
// read Makefile.fpc to get some hints
MakeFileFPC:=ADirPath+'Makefile.fpc';
SubDirs:='';
if FileExistsUTF8(MakeFileFPC) then begin
ParseMakefileFPC(MakeFileFPC,DefaultTargetOS,GlobalSubDirs,TargetSubDirs);
SubDirs:=GlobalSubDirs;
if TargetSubDirs<>'' then begin
if SubDirs<>'' then
SubDirs:=SubDirs+';';
SubDirs:=SubDirs+TargetSubDirs;
end;
//debugln('BrowseDirectory ADirPath="',ADirPath,'" SubDirs="',SubDirs,'" SrcOS="',DefaultTargetOS,'"');
end;
// set directory priority
if System.Pos(Dir+'rtl'+PathDelim,ADirPath)>0 then
inc(Priority);
if System.Pos(Dir+'packages'+PathDelim+'fcl',ADirPath)>0 then // packages/fcl*
inc(Priority);
// search sources .pp,.pas
if FindFirstUTF8(ADirPath+FileMask,faAnyFile,FileInfo)=0 then begin
repeat
AFilename:=FileInfo.Name;
if (AFilename='') or (AFilename='.') or (AFilename='..') then
continue;
//debugln('Browse Filename=',AFilename,' IsDir=',(FileInfo.Attr and faDirectory)>0);
i:=High(IgnoreDirs);
while (i>=Low(IgnoreDirs)) and (AFilename<>IgnoreDirs[i]) do dec(i);
//if CompareText(AFilename,'fcl')=0 then
// debugln('Browse ',AFilename,' IsDir=',(FileInfo.Attr and faDirectory)>0,' Ignore=',i>=Low(IgnoreDirs));
if i>=Low(IgnoreDirs) then continue;
AFilename:=ADirPath+AFilename;
if (FileInfo.Attr and faDirectory)>0 then begin
// directory -> recursively
// ToDo: prevent cycling in links
SubPriority:=0;
if CompareFilenames(AFilename,Dir+'rtl')=0
then begin
// units in 'rtl' have higher priority than other directories
inc(SubPriority);
end;
if (SubDirs<>'')
and (FindPathInSearchPath(@FileInfo.Name[1],length(FileInfo.Name),
PChar(SubDirs),length(SubDirs))<>nil)
then begin
// units in directories compiled by the Makefile have higher prio
inc(SubPriority);
end;
if not BrowseDirectory(AFilename,SubPriority) then exit(false);
end else begin
Ext:=UpperCaseStr(ExtractFileExt(AFilename));
if (Ext='.PP') or (Ext='.PAS') or (Ext='.P') then begin
// pascal unit found
AUnitName:=FileInfo.Name;
AUnitName:=copy(AUnitName,1,length(AUnitName)-length(Ext));
if AUnitName<>'' then begin
OldUnitLink:=FindUnitLink(AUnitName);
MacroCount:=0;
UsedMacroCount:=0;
MacroFileName:=
BuildMacroFileName(AFilename,MacroCount,UsedMacroCount);
if OldUnitLink=nil then begin
// first unit with this name
NewUnitLink:=TUnitNameLink.Create;
NewUnitLink.Unit_Name:=AUnitName;
NewUnitLink.FileName:=MacroFileName;
NewUnitLink.MacroCount:=MacroCount;
NewUnitLink.UsedMacroCount:=UsedMacroCount;
NewUnitLink.Score:=Priority;
UnitTree.Add(NewUnitLink);
end else begin
{ there is another unit with this name
the decision which filename is the right one is based on a
simple heuristic:
- a filename with macros is preferred above one without
This skips the templates.
- A macro fitting better with the current settings
is preferred. For example:
If the current OS is linux then on fpc 1.0.x:
$(#FPCSrcDir)/fcl/classes/$(#TargetOS)/classes.pp
- A unit in the rtl is preferred above one in the fcl
FPC stores a unit many times, if there is different version
for each Operating System or Processor Type. And sometimes
units are stored in a combined OS (e.g. 'unix').
Therefore every occurence of such values is replaced by a
macro. And filenames without macros are always deleted if
there is a filename with a macro. (The filename without
macro is only used by the FPC team as a template source
for the OS specific).
If there are several macro filenames for the same unit, the
filename with the highest number of default values is used.
For example:
classes.pp can be found in several places
In fpc 1.0.x:
<FPCSrcDir>/rtl/amiga/classes.pp
<FPCSrcDir>/fcl/amiga/classes.pp
<FPCSrcDir>/fcl/beos/classes.pp
<FPCSrcDir>/fcl/qnx/classes.pp
<FPCSrcDir>/fcl/sunos/classes.pp
<FPCSrcDir>/fcl/template/classes.pp
<FPCSrcDir>/fcl/classes/freebsd/classes.pp
<FPCSrcDir>/fcl/classes/go32v2/classes.pp
<FPCSrcDir>/fcl/classes/linux/classes.pp
<FPCSrcDir>/fcl/classes/netbsd/classes.pp
<FPCSrcDir>/fcl/classes/openbsd/classes.pp
<FPCSrcDir>/fcl/classes/os2/classes.pp
<FPCSrcDir>/fcl/classes/win32/classes.pp
In fpc 1.9.x/2.0.x:
<FPCSrcDir>/rtl/win32/classes.pp
<FPCSrcDir>/rtl/watcom/classes.pp
<FPCSrcDir>/rtl/go32v2/classes.pp
<FPCSrcDir>/rtl/netwlibc/classes.pp
<FPCSrcDir>/rtl/netbsd/classes.pp
<FPCSrcDir>/rtl/linux/classes.pp
<FPCSrcDir>/rtl/os2/classes.pp
<FPCSrcDir>/rtl/freebsd/classes.pp
<FPCSrcDir>/rtl/openbsd/classes.pp
<FPCSrcDir>/rtl/netware/classes.pp
<FPCSrcDir>/rtl/darwin/classes.pp
<FPCSrcDir>/rtl/morphos/classes.pp
<FPCSrcDir>/fcl/sunos/classes.pp
<FPCSrcDir>/fcl/beos/classes.pp
<FPCSrcDir>/fcl/qnx/classes.pp
<FPCSrcDir>/fcl/classes/win32/classes.pp
<FPCSrcDir>/fcl/classes/go32v2/classes.pp
<FPCSrcDir>/fcl/classes/netbsd/classes.pp
<FPCSrcDir>/fcl/classes/linux/classes.pp
<FPCSrcDir>/fcl/classes/os2/classes.pp
<FPCSrcDir>/fcl/classes/freebsd/classes.pp
<FPCSrcDir>/fcl/classes/openbsd/classes.pp
<FPCSrcDir>/fcl/template/classes.pp
<FPCSrcDir>/fcl/amiga/classes.pp
This means, there are several possible macro filenames:
$(#FPCSrcDir)/rtl/$(#TargetOS)/classes.pp
$(#FPCSrcDir)/fcl/$(#TargetOS)/classes.pp
$(#FPCSrcDir)/fcl/classes/$(#TargetOS)/classes.pp
Example: libc.pp
<FPCSrcDir>/rtl/netwlibc/libc.pp
<FPCSrcDir>/packages/base/libc/libc.pp
There are no macros and no templates. This is a special case.
}
if (AUnitName='libc')
and (System.Pos(AppendPathDelim(FPCSrcDir)+'packages'+PathDelim,ADirPath)>0)
then begin
// <FPCSrcDir>/rtl/netwlibc/libc.pp
// <FPCSrcDir>/packages/base/libc/libc.pp
inc(Priority,2);
end;
//DebugLn(['BrowseDirectory duplicate found: ',AUnitName,' OldUnitLink.Filename=',OldUnitLink.Filename,' MacroFileName=',MacroFileName,' Priority=',Priority,' OldUnitLink.Priority=',OldUnitLink.Score]);
if (Priority>OldUnitLink.Score)
or ((Priority=OldUnitLink.Score)
and (UsedMacroCount>OldUnitLink.UsedMacroCount))
then begin
// take the new macro filename
OldUnitLink.Filename:=MacroFileName;
OldUnitLink.MacroCount:=MacroCount;
OldUnitLink.Score:=Priority;
end;
end;
end;
end;
end;
until FindNextUTF8(FileInfo)<>0;
end;
FindCloseUTF8(FileInfo);
end;
begin
if UnitTree<>nil then exit(true);
UnitTree:=TAVLTree.Create(@CompareUnitLinkNodes);
Result:=BrowseDirectory(Dir,0);
end;
procedure AddFPCSourceLinkForUnit(const AnUnitName: string);
var UnitLink: TUnitNameLink;
s: string;
begin
// search
if AnUnitName='' then exit;
UnitLink:=FindUnitLink(AnUnitName);
{$IFDEF VerboseFPCSrcScan}
DbgOut('AddFPCSourceLinkForUnit ',AnUnitName,' ');
if UnitLink<>nil then
DebugLn(' -> ',UnitLink.Filename)
else
DebugLn('MISSING');
{$ELSE}
if UnitLink=nil then
DebugLn(['WARNING: unable to find source of fpc unit ',AnUnitName]);
{$ENDIF}
if UnitLink=nil then exit;
s:=AnUnitName+' '+UnitLink.Filename+LineEnding;
UnitLinkList:=UnitLinkList+s;
end;
function FindStandardPPUSources: boolean;
var PathStart, PathEnd: integer;
ADirPath, AUnitName: string;
FileInfo: TSearchRec;
CurMask: String;
begin
Result:=false;
{$IFDEF VerboseFPCSrcScan}
DebugLn('FindStandardPPUSources ..');
{$ENDIF}
// try every ppu file in every reachable directory (CompUnitPath)
if UnitLinkListValid then exit(true);
UnitLinkList:='';
PathStart:=1;
CurMask:=PPUExt;
if CurMask='' then CurMask:='.ppu';
if CurMask[1]<>'.' then
CurMask:='.'+CurMask;
CurMask:='*'+CurMask;
//DebugLn('FindStandardPPUSources UnitSearchPath="',UnitSearchPath,'"');
while PathStart<=length(UnitSearchPath) do begin
while (PathStart<=length(UnitSearchPath))
and (UnitSearchPath[PathStart]=';') do
inc(PathStart);
PathEnd:=PathStart;
// extract single path from unit search path
while (PathEnd<=length(UnitSearchPath))
and (UnitSearchPath[PathEnd]<>';') do
inc(PathEnd);
if PathEnd>PathStart then begin
ADirPath:=copy(UnitSearchPath,PathStart,PathEnd-PathStart);
{$IFDEF VerboseFPCSrcScan}
DebugLn('FindStandardPPUSources Searching ',CurMask,' in ',ADirPath);
{$ENDIF}
inc(ProgressID);
if CheckAbort(ProgressID,-1,'') then exit(false);
// search all ppu files in this directory
if FindFirstUTF8(ADirPath+CurMask,faAnyFile,FileInfo)=0 then begin
repeat
AUnitName:=lowercase(ExtractFileNameOnly(FileInfo.Name));
{$IFDEF VerboseFPCSrcScan}
DebugLn('FindStandardPPUSources Found: ',AUnitName);
{$ENDIF}
if (UnitTree=nil) and (not GatherUnits) then exit;
AddFPCSourceLinkForUnit(AUnitName);
if (UnitTree=nil) or (UnitTree.Count=0) then exit;
until FindNextUTF8(FileInfo)<>0;
end;
FindCloseUTF8(FileInfo);
end;
PathStart:=PathEnd;
end;
UnitLinkListValid:=true;
Result:=true;
end;
procedure AddProcessorTypeDefine(ParentDefTempl: TDefineTemplate);
// some FPC source files expects defines 'i386' instead of 'CPUi386'
// define them automatically with IF..THEN constructs
var
i: Integer;
CPUName: String;
IfTemplate: TDefineTemplate;
begin
// FPC defines CPUxxx defines (e.g. CPUI386, CPUPOWERPC).
// These defines are created by the compiler depending
// on xxx defines (i386, powerpc).
// Create:
// IF CPUi386 then define i386
// IF CPUpowerpc then define powerpc
// ...
for i:=Low(FPCProcessorNames) to high(FPCProcessorNames) do begin
CPUName:=FPCProcessorNames[i];
IfTemplate:=TDefineTemplate.Create('IFDEF CPU'+CPUName,
'IFDEF CPU'+CPUName,'CPU'+CPUName,'',da_IfDef);
IfTemplate.AddChild(TDefineTemplate.Create('DEFINE '+CPUName,
'DEFINE '+CPUName,CPUName,'',da_DefineRecurse));
ParentDefTempl.AddChild(IfTemplate);
end;
end;
procedure AddSrcOSDefines(ParentDefTempl: TDefineTemplate);
var
IfTargetOSIsNotSrcOS: TDefineTemplate;
RTLSrcOSDir: TDefineTemplate;
IfTargetOSIsNotSrcOS2: TDefineTemplate;
RTLSrcOS2Dir: TDefineTemplate;
begin
// if TargetOS<>SrcOS
IfTargetOSIsNotSrcOS:=TDefineTemplate.Create(
'IF TargetOS<>SrcOS',
ctsIfTargetOSIsNotSrcOS,'',''''+TargetOSMacro+'''<>'''+SrcOS+'''',da_If);
// rtl/$(#SrcOS)
RTLSrcOSDir:=TDefineTemplate.Create('SrcOS',SrcOS,'',
SrcOS,da_Directory);
IfTargetOSIsNotSrcOS.AddChild(RTLSrcOSDir);
RTLSrcOSDir.AddChild(TDefineTemplate.Create('Include Path',
'include path',
ExternalMacroStart+'IncPath',IncPathMacro+';inc',
da_Define));
RTLSrcOSDir.AddChild(TDefineTemplate.Create('Include Path',
'include path to TargetProcessor directories',
ExternalMacroStart+'IncPath',IncPathMacro+';'+TargetProcessor,
da_Define));
ParentDefTempl.AddChild(IfTargetOSIsNotSrcOS);
// if TargetOS<>SrcOS2
IfTargetOSIsNotSrcOS2:=TDefineTemplate.Create(
'IF TargetOS is not SrcOS2',
ctsIfTargetOSIsNotSrcOS,'',''''+TargetOSMacro+'''<>'''+SrcOS2+'''',da_If);
// rtl/$(#SrcOS2)
RTLSrcOS2Dir:=TDefineTemplate.Create('SrcOS2',SrcOS2,'',
SrcOS2,da_Directory);
IfTargetOSIsNotSrcOS2.AddChild(RTLSrcOS2Dir);
RTLSrcOS2Dir.AddChild(TDefineTemplate.Create('Include Path',
'include path to TargetProcessor directories',
ExternalMacroStart+'IncPath',IncPathMacro+';'+TargetProcessor,
da_DefineRecurse));
ParentDefTempl.AddChild(IfTargetOSIsNotSrcOS2);
end;
var
DefTempl, MainDir, FCLDir, RTLDir, RTLOSDir, PackagesDir, CompilerDir,
UtilsDir, DebugSvrDir: TDefineTemplate;
s: string;
FCLDBDir: TDefineTemplate;
FCLDBInterbaseDir: TDefineTemplate;
InstallerDir: TDefineTemplate;
IFTempl: TDefineTemplate;
FCLBaseDir: TDefineTemplate;
FCLBaseSrcDir: TDefineTemplate;
PackagesFCLAsyncDir: TDefineTemplate;
PackagesExtraDir: TDefineTemplate;
PkgExtraGraphDir: TDefineTemplate;
PkgExtraAMunitsDir: TDefineTemplate;
FCLSubSrcDir: TDefineTemplate;
FCLSubDir: TDefineTemplate;
Ok: Boolean;
begin
{$IFDEF VerboseFPCSrcScan}
DebugLn('CreateFPCSrcTemplate ',FPCSrcDir,': length(UnitSearchPath)=',DbgS(length(UnitSearchPath)),' Valid=',DbgS(UnitLinkListValid),' PPUExt=',PPUExt);
{$ENDIF}
if UnitSearchPath='' then begin
DebugLn(['Note: TDefinePool.CreateFPCSrcTemplate UnitSearchPath empty']);
end;
Result:=nil;
ProgressID:=0;
Ok:=false;
try
if (FPCSrcDir='') or (not DirPathExists(FPCSrcDir)) then begin
DebugLn(['TDefinePool.CreateFPCSrcTemplate FPCSrcDir does not exist: FPCSrcDir="',FPCSrcDir,'"']);
exit;
end;
DS:=PathDelim;
Dir:=AppendPathDelim(FPCSrcDir);
SrcOS:='$('+ExternalMacroStart+'SrcOS)';
SrcOS2:='$('+ExternalMacroStart+'SrcOS2)';
TargetProcessor:='$('+ExternalMacroStart+'TargetProcessor)';
IncPathMacro:='$('+ExternalMacroStart+'IncPath)';
DefaultSrcOS:=GetDefaultSrcOSForTargetOS(DefaultTargetOS);
DefaultSrcOS2:=GetDefaultSrcOS2ForTargetOS(DefaultTargetOS);
Result:=TDefineTemplate.Create(StdDefTemplFPCSrc,
Format(ctsFreePascalSourcesPlusDesc,['RTL, FCL, Packages, Compiler']),
'','',da_Block);
// try to find for every reachable ppu file the unit file in the FPC sources
UnitLinks:=UnitLinksMacroName;
UnitTree:=nil;
if not FindStandardPPUSources then exit;
DefTempl:=TDefineTemplate.Create('FPC Unit Links',
ctsSourceFilenamesForStandardFPCUnits,
UnitLinks,UnitLinkList,da_DefineRecurse);
Result.AddChild(DefTempl);
// The free pascal sources build a world of their own,
// reset search paths
MainDir:=TDefineTemplate.Create('Free Pascal Source Directory',
ctsFreePascalSourceDir,'',FPCSrcDir,da_Directory);
Result.AddChild(MainDir);
DefTempl:=TDefineTemplate.Create('Reset SrcPath',
ctsSrcPathInitialization,ExternalMacroStart+'SrcPath','',da_DefineRecurse);
MainDir.AddChild(DefTempl);
DefTempl:=TDefineTemplate.Create('Reset UnitPath',
ctsUnitPathInitialization,ExternalMacroStart+'UnitPath','',da_DefineRecurse);
MainDir.AddChild(DefTempl);
// turn Nested comments on
DefTempl:=TDefineTemplate.Create('Nested Comments',
ctsNestedCommentsOn,ExternalMacroStart+'NestedComments','',da_DefineRecurse);
MainDir.AddChild(DefTempl);
// enable FPDocSystem to find compiler functions like writeln and readln
{DefTempl:=TDefineTemplate.Create('FPDocSystem',
ctsFPDocSystemOn,'FPDocSystem','',da_DefineRecurse);
MainDir.AddChild(DefTempl);}
// rtl
RTLDir:=TDefineTemplate.Create('RTL',ctsRuntimeLibrary,'','rtl',da_Directory);
MainDir.AddChild(RTLDir);
// rtl include paths
s:=IncPathMacro
+';'+Dir+'rtl'+DS+'objpas'+DS
+';'+Dir+'rtl'+DS+'objpas'+DS+'sysutils'
+';'+Dir+'rtl'+DS+'objpas'+DS+'classes'
+';'+Dir+'rtl'+DS+'inc'+DS
+';'+Dir+'rtl'+DS+'inc'+DS+'graph'+DS
+';'+Dir+'rtl'+DS+SrcOS+DS
+';'+Dir+'rtl'+DS+TargetOSMacro+DS
+';'+Dir+'rtl'+DS+SrcOS2+DS
+';'+Dir+'rtl'+DS+SrcOS2+DS+TargetProcessor
+';'+Dir+'rtl'+DS+TargetProcessor+DS
+';'+Dir+'rtl'+DS+TargetOSMacro+DS+TargetProcessor+DS;
RTLDir.AddChild(TDefineTemplate.Create('Include Path',
Format(ctsIncludeDirectoriesPlusDirs,
['objpas, inc,'+TargetProcessor+','+SrcOS]),
ExternalMacroStart+'IncPath',s,da_DefineRecurse));
// rtl/$(#TargetOS)
RTLOSDir:=TDefineTemplate.Create('TargetOS','Target OS','',
TargetOSMacro,da_Directory);
s:=IncPathMacro
+';'+Dir+'rtl'+DS+TargetOSMacro+DS+SrcOS+'inc' // e.g. rtl/win32/inc/
+';'+Dir+'rtl'+DS+TargetOSMacro+DS+TargetProcessor+DS
;
RTLOSDir.AddChild(TDefineTemplate.Create('Include Path',
Format(ctsIncludeDirectoriesPlusDirs,[TargetProcessor]),
ExternalMacroStart+'IncPath',
s,da_DefineRecurse));
s:=SrcPathMacro
+';'+Dir+'rtl'+DS+'objpas'+DS;
RTLOSDir.AddChild(TDefineTemplate.Create('Src Path',
Format(ctsAddsDirToSourcePath,[TargetProcessor]),
ExternalMacroStart+'SrcPath',s,da_DefineRecurse));
RTLDir.AddChild(RTLOSDir);
// rtl: IF SrcOS=win then add include path rtl/win/wininc
IFTempl:=TDefineTemplate.Create('If SrcOS=win','If SrcOS=win',
'',''''+SrcOS+'''=''win''',da_If);
IFTempl.AddChild(TDefineTemplate.Create('Include Path',
Format(ctsIncludeDirectoriesPlusDirs,['wininc']),
ExternalMacroStart+'IncPath',
IncPathMacro
+';'+Dir+'rtl'+DS+'win'+DS+'wininc'
+';'+Dir+'rtl'+DS+'win',
da_DefineRecurse));
RTLDir.AddChild(IFTempl);
// rtl: IF TargetOS=darwin then add include path rtl/freebsd
IFTempl:=TDefineTemplate.Create('If TargetOS=darwin','If TargetOS=darwin',
'',''''+TargetOSMacro+'''=''darwin''',da_If);
IFTempl.AddChild(TDefineTemplate.Create('Include Path',
Format(ctsIncludeDirectoriesPlusDirs,['rtl'+DS+'freebsd']),
ExternalMacroStart+'IncPath',
IncPathMacro
+';'+Dir+'rtl'+DS+'freebsd',
da_DefineRecurse));
RTLDir.AddChild(IFTempl);
// add processor and SrcOS alias defines for the RTL
AddProcessorTypeDefine(RTLDir);
AddSrcOSDefines(RTLDir);
// fcl
FCLDir:=TDefineTemplate.Create('FCL',ctsFreePascalComponentLibrary,'','fcl',
da_Directory);
MainDir.AddChild(FCLDir);
FCLDir.AddChild(TDefineTemplate.Create('Include Path',
Format(ctsIncludeDirectoriesPlusDirs,['inc,'+SrcOS]),
ExternalMacroStart+'IncPath',
d( DefinePathMacro+'/inc/'
+';'+DefinePathMacro+'/classes/'
+';'+DefinePathMacro+'/'+TargetOSMacro+DS // TargetOS before SrcOS !
+';'+DefinePathMacro+'/'+SrcOS+DS
+';'+IncPathMacro)
,da_DefineRecurse));
// fcl/db
FCLDBDir:=TDefineTemplate.Create('DB','DB','','db',da_Directory);
FCLDir.AddChild(FCLDBDir);
FCLDBInterbaseDir:=TDefineTemplate.Create('interbase','interbase','',
'interbase',da_Directory);
FCLDBDir.AddChild(FCLDBInterbaseDir);
FCLDBInterbaseDir.AddChild(TDefineTemplate.Create('SrcPath',
'SrcPath addition',
ExternalMacroStart+'SrcPath',
d(Dir+'/packages/base/ibase;'+SrcPathMacro)
,da_Define));
// packages
PackagesDir:=TDefineTemplate.Create('Packages',ctsPackageDirectories,'',
'packages',da_Directory);
MainDir.AddChild(PackagesDir);
// packages/fcl-base
FCLBaseDir:=TDefineTemplate.Create('FCL-base',
ctsFreePascalComponentLibrary,'','fcl-base',
da_Directory);
PackagesDir.AddChild(FCLBaseDir);
// packages/fcl-base/src
FCLBaseSrcDir:=TDefineTemplate.Create('src',
'src','','src',
da_Directory);
FCLBaseDir.AddChild(FCLBaseSrcDir);
FCLBaseSrcDir.AddChild(TDefineTemplate.Create('Include Path',
Format(ctsIncludeDirectoriesPlusDirs,['inc,'+SrcOS]),
ExternalMacroStart+'IncPath',
d( DefinePathMacro+'/inc/'
+';'+DefinePathMacro+'/'+TargetOSMacro+DS // TargetOS before SrcOS !
+';'+DefinePathMacro+'/'+SrcOS+DS
+';'+IncPathMacro)
,da_DefineRecurse));
// packages/fcl-process
FCLSubDir:=TDefineTemplate.Create('FCL-process',
'fcl-process','','fcl-process',
da_Directory);
PackagesDir.AddChild(FCLSubDir);
// packages/fcl-process/src
FCLSubSrcDir:=TDefineTemplate.Create('src',
'src','','src',
da_Directory);
FCLSubDir.AddChild(FCLSubSrcDir);
FCLSubSrcDir.AddChild(TDefineTemplate.Create('Include Path',
Format(ctsIncludeDirectoriesPlusDirs,['inc,'+SrcOS]),
ExternalMacroStart+'IncPath',
d( DefinePathMacro+'/'+TargetOSMacro+DS // TargetOS before SrcOS !
+';'+DefinePathMacro+'/'+SrcOS+DS
+';'+IncPathMacro)
,da_DefineRecurse));
// packages/fcl-async
PackagesFCLAsyncDir:=TDefineTemplate.Create('fcl-async','fcl-async','','fcl-async',da_Directory);
PackagesDir.AddChild(PackagesFCLAsyncDir);
// packages/fcl-async/src
PackagesFCLAsyncDir.AddChild(TDefineTemplate.Create('Include Path',
Format(ctsIncludeDirectoriesPlusDirs,['packages/fcl-async/src']),
ExternalMacroStart+'IncPath',
d( DefinePathMacro+'/src/'
+';'+IncPathMacro)
,da_DefineRecurse));
// packages/extra
PackagesExtraDir:=TDefineTemplate.Create('extra','extra','','extra',da_Directory);
PackagesDir.AddChild(PackagesExtraDir);
// packages/extra/graph
PkgExtraGraphDir:=TDefineTemplate.Create('graph','graph','','graph',
da_Directory);
PackagesExtraDir.AddChild(PkgExtraGraphDir);
PkgExtraGraphDir.AddChild(TDefineTemplate.Create('Include Path',
Format(ctsIncludeDirectoriesPlusDirs,['inc']),
ExternalMacroStart+'IncPath',
d( DefinePathMacro+'/inc/'
+';'+IncPathMacro)
,da_DefineRecurse));
// packages/extra/amunits
PkgExtraAMunitsDir:=TDefineTemplate.Create('amunits','amunits','','amunits',
da_Directory);
PackagesExtraDir.AddChild(PkgExtraAMunitsDir);
PkgExtraAMunitsDir.AddChild(TDefineTemplate.Create('Include Path',
Format(ctsIncludeDirectoriesPlusDirs,['inc']),
ExternalMacroStart+'IncPath',
d( DefinePathMacro+'/inc/'
+';'+IncPathMacro)
,da_DefineRecurse));
// utils
UtilsDir:=TDefineTemplate.Create('Utils',ctsUtilsDirectories,'',
'utils',da_Directory);
MainDir.AddChild(UtilsDir);
// utils/debugsvr
DebugSvrDir:=TDefineTemplate.Create('DebugSvr','Debug Server','',
'debugsvr',da_Directory);
UtilsDir.AddChild(DebugSvrDir);
DebugSvrDir.AddChild(TDefineTemplate.Create('Interface Path',
Format(ctsAddsDirToSourcePath,['..']),ExternalMacroStart+'SrcPath',
'..;'+ExternalMacroStart+'SrcPath',da_DefineRecurse));
// installer
InstallerDir:=TDefineTemplate.Create('Installer',ctsInstallerDirectories,'',
'installer',da_Directory);
InstallerDir.AddChild(TDefineTemplate.Create('SrcPath','SrcPath addition',
ExternalMacroStart+'SrcPath',
SrcPathMacro+';'+Dir+'ide;'+Dir+'fv',da_Define));
MainDir.AddChild(InstallerDir);
// compiler
CompilerDir:=TDefineTemplate.Create('Compiler',ctsCompiler,'','compiler',
da_Directory);
AddProcessorTypeDefine(CompilerDir);
CompilerDir.AddChild(TDefineTemplate.Create('SrcPath','SrcPath addition',
ExternalMacroStart+'SrcPath',
SrcPathMacro+';'+Dir+TargetProcessor,da_Define));
CompilerDir.AddChild(TDefineTemplate.Create('IncPath','IncPath addition',
ExternalMacroStart+'IncPath',
IncPathMacro+';'+Dir+'compiler',da_DefineRecurse));
MainDir.AddChild(CompilerDir);
// compiler/utils
UtilsDir:=TDefineTemplate.Create('utils',ctsUtilsDirectories,'',
'utils',da_Directory);
UtilsDir.AddChild(TDefineTemplate.Create('SrcPath','SrcPath addition',
ExternalMacroStart+'SrcPath',
SrcPathMacro+';..',da_Define));
CompilerDir.AddChild(UtilsDir);
// clean up
if UnitTree<>nil then begin
UnitTree.FreeAndClear;
UnitTree.Free;
end;
Result.SetDefineOwner(Owner,true);
Result.SetFlags([dtfAutoGenerated],[],false);
Ok:=true;
finally
if not ok then
FreeAndNil(Result);
if (ProgressID>0) and Assigned(OnProgress) then
OnProgress(Self,ProgressID,ProgressID,'',Ok);
end;
end;
function TDefinePool.CreateDelphiSrcPath(DelphiVersion: integer;
const PathPrefix: string): string;
begin
case DelphiVersion of
1..5:
Result:=PathPrefix+'Source/Rtl/Win;'
+PathPrefix+'Source/Rtl/Sys;'
+PathPrefix+'Source/Rtl/Corba;'
+PathPrefix+'Source/Vcl;';
else
// 6 and above
Result:=PathPrefix+'Source/Rtl/Win;'
+PathPrefix+'Source/Rtl/Sys;'
+PathPrefix+'Source/Rtl/Common;'
+PathPrefix+'Source/Rtl/Corba40;'
+PathPrefix+'Source/Vcl;';
end;
end;
function TDefinePool.CreateLazarusSrcTemplate(
const LazarusSrcDir, WidgetType, ExtraOptions: string;
Owner: TObject): TDefineTemplate;
function D(const Filename: string): string;
begin
Result:=SetDirSeparators(Filename);
end;
var
MainDir, DirTempl, SubDirTempl, IntfDirTemplate, IfTemplate, ElseTemplate,
LCLUnitsDir, LCLUnitsCPUOSDir, LCLUnitsCPUOSWidgetSetDir,
SubTempl: TDefineTemplate;
TargetOS, SrcOS, SrcPath, IncPath: string;
i: Integer;
CurCPU, CurOS, CurWidgetSet, ExtraSrcPath: string;
LCLWidgetSetDir: TDefineTemplate;
IDEIntfDir: TDefineTemplate;
ToolsInstallDirTempl: TDefineTemplate;
CurCPUOS: String;
SynEditDirTempl, SynEditUnitsDirTempl: TDefineTemplate;
LazControlsDirTempl, LazControlsUnitsDirTempl: TDefineTemplate;
CodeToolsDirTempl: TDefineTemplate;
CodeToolsUnitsDirTempl: TDefineTemplate;
FPGUIPlatformTempl: TDefineTemplate;
AllWidgetSets: String;
p: Integer;
begin
Result:=nil;
if (LazarusSrcDir='') or (WidgetType='') then exit;
//TargetCPU:='$('+ExternalMacroStart+'TargetCPU)';
TargetOS:='$('+ExternalMacroStart+'TargetOS)';
SrcOS:='$('+ExternalMacroStart+'SrcOS)';
SrcPath:='$('+ExternalMacroStart+'SrcPath)';
IncPath:='$('+ExternalMacroStart+'IncPath)';
AllWidgetSets:='';
for i:=Low(Lazarus_CPU_OS_Widget_Combinations)
to High(Lazarus_CPU_OS_Widget_Combinations) do
begin
SplitLazarusCPUOSWidgetCombo(Lazarus_CPU_OS_Widget_Combinations[i],
CurCPU,CurOS,CurWidgetSet);
if not HasDelimitedItem(AllWidgetSets,';',CurWidgetSet) then begin
if AllWidgetSets<>'' then
AllWidgetSets:=AllWidgetSets+';';
AllWidgetSets:=AllWidgetSets+CurWidgetSet;
end;
end;
// <LazarusSrcDir>
MainDir:=TDefineTemplate.Create(
StdDefTemplLazarusSrcDir, ctsDefsForLazarusSources,'',LazarusSrcDir,
da_Directory);
// clear src path
MainDir.AddChild(TDefineTemplate.Create('Clear SrcPath','Clear SrcPath',
ExternalMacroStart+'SrcPath','',da_DefineRecurse));
// if SrcOS<>win
IfTemplate:=TDefineTemplate.Create('IF '''+SrcOS+'''<>''win''',
ctsIfTargetOSIsNotWin32,'',''''+SrcOS+'''<>''win''',da_If);
// then define #SrcPath := #SrcPath;lcl/nonwin32
IfTemplate.AddChild(TDefineTemplate.Create('win32api for non win',
Format(ctsAddsDirToSourcePath,[d(LazarusSrcDir+'/lcl/nonwin32')]),
ExternalMacroStart+'SrcPath',
d(LazarusSrcDir+'/lcl/nonwin32;')+SrcPath,da_DefineRecurse));
MainDir.AddChild(IfTemplate);
// turn Nested comments on
MainDir.AddChild(TDefineTemplate.Create('Nested Comments',
ctsNestedCommentsOn,ExternalMacroStart+'NestedComments','',da_DefineRecurse));
// define 'LCL'
MainDir.AddChild(TDefineTemplate.Create('define LCL',
ctsDefineLCL,'LCL',WidgetType,da_DefineRecurse));
// define LCLwidgetset, e.g. LCLcarbon, LCLgtk, LCLgtk2
p:=1;
repeat
CurWidgetSet:=GetNextDelimitedItem(AllWidgetSets,';',p);
if CurWidgetSet='' then break;
IfTemplate:=TDefineTemplate.Create('IF '''+WidgetType+'''='''+CurWidgetSet+'''',
ctsDefineLCLWidgetset,'',''''+WidgetType+'''='''+CurWidgetSet+'''',da_If);
// then define LCLgtk, LCLgtk2, LCLcarbon, ...
IfTemplate.AddChild(TDefineTemplate.Create('Define LCL'+CurWidgetSet,
ctsDefineLCLWidgetset,'LCL'+CurWidgetSet,'',da_DefineRecurse));
MainDir.AddChild(IfTemplate);
until false;
// <LazarusSrcDir>/include
// (does not need special setup)
// <LazarusSrcDir>/ide
DirTempl:=TDefineTemplate.Create('ide',ctsIDEDirectory,
'','ide',da_Directory);
DirTempl.AddChild(TDefineTemplate.Create('IDE path addition',
Format(ctsAddsDirToSourcePath,['designer, debugger, synedit, ...']),
ExternalMacroStart+'SrcPath',
d(LazarusSrcDir+'/ide;'
+LazarusSrcDir+'/ide/frames;'
+LazarusSrcDir+'/designer;'
+LazarusSrcDir+'/debugger;'
+LazarusSrcDir+'/debugger/frames;'
+LazarusSrcDir+'/converter;'
+LazarusSrcDir+'/packager;'
+LazarusSrcDir+'/packager/registration;'
+LazarusSrcDir+'/components/custom;'
+LazarusSrcDir+'/components/mpaslex;')
,da_DefineRecurse));
DirTempl.AddChild(TDefineTemplate.Create('IDEIntf path addition',
Format(ctsAddsDirToSourcePath,['ideintf']),
ExternalMacroStart+'SrcPath',
d(LazarusSrcDir+'/ideintf;'
+SrcPath)
,da_DefineRecurse));
DirTempl.AddChild(TDefineTemplate.Create('SynEdit path addition',
Format(ctsAddsDirToSourcePath,['synedit']),
ExternalMacroStart+'SrcPath',
d(LazarusSrcDir+'/components/synedit;'
+SrcPath)
,da_DefineRecurse));
DirTempl.AddChild(TDefineTemplate.Create('LazControls path addition',
Format(ctsAddsDirToSourcePath,['lazcontrols']),
ExternalMacroStart+'SrcPath',
d(LazarusSrcDir+'/components/lazcontrols;'
+SrcPath)
,da_DefineRecurse));
DirTempl.AddChild(TDefineTemplate.Create('CodeTools path addition',
Format(ctsAddsDirToSourcePath,['codetools']),
ExternalMacroStart+'SrcPath',
d(LazarusSrcDir+'/components/codetools;'
+SrcPath)
,da_DefineRecurse));
DirTempl.AddChild(TDefineTemplate.Create('LCL path addition',
Format(ctsAddsDirToSourcePath,['lcl']),
ExternalMacroStart+'SrcPath',
d(LazarusSrcDir+'/lcl;'
+LazarusSrcDir+'/lcl/interfaces/'+WidgetType+';'
+SrcPath)
,da_DefineRecurse));
// include path addition
DirTempl.AddChild(TDefineTemplate.Create('includepath addition',
Format(ctsSetsIncPathTo,['include, include/TargetOS, include/SrcOS']),
ExternalMacroStart+'IncPath',
d(LazarusSrcDir+'/ide/include;'
+LazarusSrcDir+'/ide/include/'+TargetOS+';'
+LazarusSrcDir+'/ide/include/'+SrcOS),
da_DefineRecurse));
MainDir.AddChild(DirTempl);
// <LazarusSrcDir>/designer
DirTempl:=TDefineTemplate.Create('Designer',ctsDesignerDirectory,
'','designer',da_Directory);
DirTempl.AddChild(TDefineTemplate.Create('LCL path addition',
Format(ctsAddsDirToSourcePath,['lcl']),
SrcPathMacroName,
d('../lcl'
+';../lcl/interfaces/'+WidgetType)
+';'+SrcPath
,da_Define));
DirTempl.AddChild(TDefineTemplate.Create('main path addition',
Format(ctsAddsDirToSourcePath,[ctsLazarusMainDirectory]),
SrcPathMacroName,
d('../ide;../packager;')+SrcPath
,da_Define));
DirTempl.AddChild(TDefineTemplate.Create('components path addition',
Format(ctsAddsDirToSourcePath,['synedit']),
ExternalMacroStart+'SrcPath',
d('../ideintf;'
+'../components/synedit;'
+'../components/codetools;'
+'../components/lazcontrols;'
+'../components/custom;'
+'jitform;')
+SrcPath
,da_Define));
DirTempl.AddChild(TDefineTemplate.Create('includepath addition',
Format(ctsIncludeDirectoriesPlusDirs,['include']),
ExternalMacroStart+'IncPath',
d('../ide/include;../ide/include/'+TargetOS),
da_Define));
// <LazarusSrcDir>/designer/jitform
SubDirTempl:=TDefineTemplate.Create('JITForm',ctsJITFormDirectory,
'','jitform',da_Directory);
SubDirTempl.AddChild(TDefineTemplate.Create('LCL path addition',
Format(ctsAddsDirToSourcePath,['lcl']),
SrcPathMacroName,
d('../../lcl'
+';../../lcl/interfaces/'+WidgetType)
+';'+SrcPath
,da_Define));
DirTempl.AddChild(SubDirTempl);
// <LazarusSrcDir>/designer/units
SubDirTempl:=TDefineTemplate.Create('Designer Units',
ctsDesignerUnitsDirectory,'','units',da_Directory);
SubDirTempl.AddChild(TDefineTemplate.Create('CompiledSrcPath',
ctsCompiledSrcPath,CompiledSrcPathMacroName,d('../jitform/'),
da_Define));
DirTempl.AddChild(SubDirTempl);
MainDir.AddChild(DirTempl);
// <LazarusSrcDir>/images
// <LazarusSrcDir>/debugger
DirTempl:=TDefineTemplate.Create('Debugger',ctsDebuggerDirectory,
'','debugger',da_Directory);
DirTempl.AddChild(TDefineTemplate.Create('LCL path addition',
Format(ctsAddsDirToSourcePath,['lcl, components']),
ExternalMacroStart+'SrcPath',
d(LazarusSrcDir+'/debugger'
+';'+LazarusSrcDir+'/debugger/frames'
+';'+LazarusSrcDir+'/ide'
+';'+LazarusSrcDir+'/ideintf'
+';'+LazarusSrcDir+'/components/codetools'
+';'+LazarusSrcDir+'/lcl'
+';'+LazarusSrcDir+'/lcl/interfaces/'+WidgetType)
+';'+SrcPath
,da_DefineRecurse));
MainDir.AddChild(DirTempl);
// <LazarusSrcDir>/converter
DirTempl:=TDefineTemplate.Create('Converter',ctsDebuggerDirectory,
'','converter',da_Directory);
DirTempl.AddChild(TDefineTemplate.Create('LCL path addition',
Format(ctsAddsDirToSourcePath,['lcl, components']),
ExternalMacroStart+'SrcPath',
d('../ide'
+';../ideintf'
+';../components/codetools'
+';../components/synedit'
+';../components/lazcontrols'
+';../packager'
+';../debugger'
+';../designer'
+';../lcl'
+';../lcl/interfaces/'+WidgetType)
+';'+SrcPath
,da_Define));
MainDir.AddChild(DirTempl);
// <LazarusSrcDir>/packager
DirTempl:=TDefineTemplate.Create('Packager',ctsDesignerDirectory,
'','packager',da_Directory);
DirTempl.AddChild(TDefineTemplate.Create('src path addition',
Format(ctsAddsDirToSourcePath,['lcl synedit codetools lazcontrols ideintf']),
SrcPathMacroName,
d(LazarusSrcDir+'/lcl'
+';'+LazarusSrcDir+'/lcl/interfaces/'+WidgetType
+';'+LazarusSrcDir+'/ide'
+';'+LazarusSrcDir+'/ideintf'
+';'+LazarusSrcDir+'/components/synedit'
+';'+LazarusSrcDir+'/components/lazcontrols'
+';'+LazarusSrcDir+'/components/codetools'
+';'+LazarusSrcDir+'/packager/frames'
+';'+LazarusSrcDir+'/packager/registration'
+';'+SrcPath)
,da_DefineRecurse));
DirTempl.AddChild(TDefineTemplate.Create('includepath addition',
Format(ctsIncludeDirectoriesPlusDirs,['include']),
ExternalMacroStart+'IncPath',
d('../ide/include;../ide/include/'+TargetOS),
da_Define));
// <LazarusSrcDir>/packager/frames
SubDirTempl:=TDefineTemplate.Create('Frames',
'Frames','','frames',da_Directory);
DirTempl.AddChild(SubDirTempl);
SubDirTempl.AddChild(TDefineTemplate.Create('src path addition',
Format(ctsAddsDirToSourcePath,['ide']),
SrcPathMacroName,
d(LazarusSrcDir+'/ide;'+SrcPath)
,da_Define));
// <LazarusSrcDir>/packager/registration
SubDirTempl:=TDefineTemplate.Create('Registration',
ctsPackagerRegistrationDirectory,'','registration',da_Directory);
DirTempl.AddChild(SubDirTempl);
// <LazarusSrcDir>/packager/units
SubDirTempl:=TDefineTemplate.Create('Packager Units',
ctsPackagerUnitsDirectory,'','units',da_Directory);
SubDirTempl.AddChild(TDefineTemplate.Create('CompiledSrcPath',
ctsCompiledSrcPath,CompiledSrcPathMacroName,
LazarusSrcDir+d('/packager/registration'),
da_DefineRecurse));
DirTempl.AddChild(SubDirTempl);
MainDir.AddChild(DirTempl);
// <LazarusSrcDir>/ideintf
IDEIntfDir:=TDefineTemplate.Create('IDEIntf',ctsIDEIntfDirectory,
'','ideintf',da_Directory);
IDEIntfDir.AddChild(TDefineTemplate.Create('LCL path addition',
Format(ctsAddsDirToSourcePath,['lcl']),
SrcPathMacroName,
d('../components/codetools'
+';../packager/registration'
+';../lcl'
+';../lcl/interfaces/'+WidgetType)
+';'+SrcPath
,da_Define));
IDEIntfDir.AddChild(TDefineTemplate.Create('CompiledSrcPath',
ctsCompiledSrcPath,CompiledSrcPathMacroName,
LazarusSrcDir+d('/ideintf'),
da_DefineRecurse));
MainDir.AddChild(IDEIntfDir);
// <LazarusSrcDir>/examples
DirTempl:=TDefineTemplate.Create('Examples',
Format(ctsNamedDirectory,['Examples']),
'','examples',da_Directory);
DirTempl.AddChild(TDefineTemplate.Create('LCL path addition',
Format(ctsAddsDirToSourcePath,['lcl']),
ExternalMacroStart+'SrcPath',
d('../lcl'
+';../lcl/interfaces/'+WidgetType+';'+SrcPath)
,da_Define));
MainDir.AddChild(DirTempl);
// <LazarusSrcDir>/lcl
DirTempl:=TDefineTemplate.Create('LCL',Format(ctsNamedDirectory,['LCL']),
'','lcl',da_Directory);
DirTempl.AddChild(TDefineTemplate.Create('IncludePath',
Format(ctsIncludeDirectoriesPlusDirs,['include']),
ExternalMacroStart+'IncPath',
'include',da_Define));
DirTempl.AddChild(TDefineTemplate.Create('LCL path addition',
Format(ctsAddsDirToSourcePath,['forms']),
ExternalMacroStart+'SrcPath','forms;'+SrcPath,da_Define));
DirTempl.AddChild(TDefineTemplate.Create('LCL path addition',
Format(ctsAddsDirToSourcePath,['widgetset']),
ExternalMacroStart+'SrcPath','widgetset;'+SrcPath,da_Define));
// #FPGUIPlatform
FPGUIPlatformTempl:=TDefineTemplate.Create('Define FPGUIPlatform',
'Define FPGUIPlatform','','',da_Block);
IfTemplate:=TDefineTemplate.Create('IFDEF windows',
ctsIfDefWindows, 'windows', '', da_IfDef);
// then set #FPGUIPlatform to gdi
IfTemplate.AddChild(TDefineTemplate.Create('#FPGUIPlatform:=gdi',
'#FPGUIPlatform:=gdi',
ExternalMacroStart+'FPGUIPlatform','gdi',da_DefineRecurse));
FPGUIPlatformTempl.AddChild(IfTemplate);
ElseTemplate:=TDefineTemplate.Create('Else',
ctsElse, '', '', da_Else);
// then set #FPGUIPlatform to x11
ElseTemplate.AddChild(TDefineTemplate.Create('#FPGUIPlatform:=x11',
'#FPGUIPlatform:=x11',
ExternalMacroStart+'FPGUIPlatform','x11',da_DefineRecurse));
FPGUIPlatformTempl.AddChild(ElseTemplate);
DirTempl.AddChild(FPGUIPlatformTempl);
MainDir.AddChild(DirTempl);
// <LazarusSrcDir>/lcl/forms
LCLWidgetSetDir:=TDefineTemplate.Create('forms',Format(ctsNamedDirectory,['WidgetSet']),
'','forms',da_Directory);
LCLWidgetSetDir.AddChild(TDefineTemplate.Create('LCL path addition',
Format(ctsAddsDirToSourcePath,['..']),
ExternalMacroStart+'SrcPath','..;'+SrcPath,da_Define));
DirTempl.AddChild(LCLWidgetSetDir);
// <LazarusSrcDir>/lcl/widgetset
LCLWidgetSetDir:=TDefineTemplate.Create('widgetset',Format(ctsNamedDirectory,['WidgetSet']),
'','widgetset',da_Directory);
LCLWidgetSetDir.AddChild(TDefineTemplate.Create('LCL path addition',
Format(ctsAddsDirToSourcePath,['..']),
ExternalMacroStart+'SrcPath','..;'+SrcPath,da_Define));
DirTempl.AddChild(LCLWidgetSetDir);
// <LazarusSrcDir>/lcl/units
LCLUnitsDir:=TDefineTemplate.Create('units',Format(ctsNamedDirectory,['Units']),
'','units',da_Directory);
DirTempl.AddChild(LCLUnitsDir);
for i:=Low(Lazarus_CPU_OS_Widget_Combinations)
to High(Lazarus_CPU_OS_Widget_Combinations) do
begin
SplitLazarusCPUOSWidgetCombo(Lazarus_CPU_OS_Widget_Combinations[i],
CurCPU,CurOS,CurWidgetSet);
// <LazarusSrcDir>/lcl/units/<TargetCPU>-<TargetOS>
// these directories contain the output of the LCL (excluding the interfaces)
CurCPUOS:=CurCPU+'-'+CurOS;
LCLUnitsCPUOSDir:=LCLUnitsDir.FindChildByName(CurCPUOS);
if LCLUnitsCPUOSDir=nil then begin
LCLUnitsCPUOSDir:=TDefineTemplate.Create(CurCPUOS,
Format(ctsNamedDirectory,[CurCPUOS]),
'',CurCPUOS,da_Directory);
LCLUnitsDir.AddChild(LCLUnitsCPUOSDir);
ExtraSrcPath:='../..;../../widgetset';
if CurOS<>'win32' then
ExtraSrcPath:=ExtraSrcPath+';../../nonwin32';
LCLUnitsCPUOSDir.AddChild(TDefineTemplate.Create('CompiledSrcPath',
ctsSrcPathForCompiledUnits,CompiledSrcPathMacroName,
d(ExtraSrcPath),da_Define));
end;
// <LazarusSrcDir>/lcl/units/<TargetCPU>-<TargetOS>/<WidgetSet>
// these directories contain the output of the LCL interfaces
LCLUnitsCPUOSWidgetSetDir:=LCLUnitsCPUOSDir.FindChildByName(CurWidgetSet);
if LCLUnitsCPUOSWidgetSetDir=nil then begin
LCLUnitsCPUOSWidgetSetDir:=TDefineTemplate.Create(CurWidgetSet,
Format(ctsNamedDirectory,[CurWidgetSet]),
'',CurWidgetSet,da_Directory);
LCLUnitsCPUOSDir.AddChild(LCLUnitsCPUOSWidgetSetDir);
ExtraSrcPath:='../../../interfaces/'+CurWidgetSet;
if (CurWidgetSet='carbon') then
ExtraSrcPath:=ExtraSrcPath+';../../../interfaces/carbon/pascocoa/appkit;../../../interfaces/carbon/objc';
if (CurWidgetSet='fpgui') then
ExtraSrcPath:=ExtraSrcPath
+';../../../interfaces/fpgui/corelib'
+';../../../interfaces/fpgui/corelib/$('+ExternalMacroStart+'FPGUIPlatform)'
+';../../../interfaces/fpgui/gui';
LCLUnitsCPUOSWidgetSetDir.AddChild(
TDefineTemplate.Create('CompiledSrcPath',
ctsSrcPathForCompiledUnits,CompiledSrcPathMacroName,
d(ExtraSrcPath),da_Define));
end;
end;
// <LazarusSrcDir>/lcl/interfaces
SubDirTempl:=TDefineTemplate.Create('interfaces',
ctsWidgetDirectory,'','interfaces',da_Directory);
// add lcl to the source path of all widget set directories
SubDirTempl.AddChild(TDefineTemplate.Create('LCL Path',
Format(ctsAddsDirToSourcePath,['lcl']),ExternalMacroStart+'SrcPath',
LazarusSrcDir+d('/lcl;')
+LazarusSrcDir+d('/lcl/widgetset;')
+SrcPath,
da_DefineRecurse));
DirTempl.AddChild(SubDirTempl);
// <LazarusSrcDir>/lcl/interfaces/gtk
IntfDirTemplate:=TDefineTemplate.Create('gtk',
ctsIntfDirectory,'','gtk',da_Directory);
SubDirTempl.AddChild(IntfDirTemplate);
// <LazarusSrcDir>/lcl/interfaces/gtk2
IntfDirTemplate:=TDefineTemplate.Create('gtk2',
ctsGtk2IntfDirectory,'','gtk2',da_Directory);
SubDirTempl.AddChild(IntfDirTemplate);
// <LazarusSrcDir>/lcl/interfaces/win32
// no special
// <LazarusSrcDir>/lcl/interfaces/wince
IntfDirTemplate:=TDefineTemplate.Create('wince',
ctsIntfDirectory,'','wince',da_Directory);
// then define wince1
IntfDirTemplate.AddChild(TDefineTemplate.Create('Define wince1',
ctsDefineMacroWinCE1,'wince1','',da_Define));
SubDirTempl.AddChild(IntfDirTemplate);
// <LazarusSrcDir>/lcl/interfaces/carbon
IntfDirTemplate:=TDefineTemplate.Create('carbon',
ctsIntfDirectory,'','carbon',da_Directory);
// then define carbon1
IntfDirTemplate.AddChild(TDefineTemplate.Create('Define carbon1',
ctsDefineMacroCarbon1,'carbon1','',da_Define));
// add 'pascocoa/appkit' to the SrcPath
IntfDirTemplate.AddChild(TDefineTemplate.Create('SrcPath',
Format(ctsAddsDirToSourcePath,['pascocoa']),ExternalMacroStart+'SrcPath',
d('pascocoa/appkit;pascocoa/foundation;')+SrcPath,da_Define));
SubDirTempl.AddChild(IntfDirTemplate);
// <LazarusSrcDir>/lcl/interfaces/qt
IntfDirTemplate:=TDefineTemplate.Create('qt',
ctsIntfDirectory,'','qt',da_Directory);
// then define qt1
IntfDirTemplate.AddChild(TDefineTemplate.Create('Define qt1',
ctsDefineMacroQT1,'qt1','',da_Define));
SubDirTempl.AddChild(IntfDirTemplate);
// <LazarusSrcDir>/lcl/interfaces/fpgui
IntfDirTemplate:=TDefineTemplate.Create('fpgui',
ctsIntfDirectory,'','fpgui',da_Directory);
// add unit paths
IntfDirTemplate.AddChild(TDefineTemplate.Create('Add gui, corelib to SrcPath',
Format(ctsAddsDirToSourcePath,['gui, corelib']),
ExternalMacroStart+'SrcPath',
d(LazarusSrcDir+'/lcl/interfaces/fpgui/gui')
+';'+d(LazarusSrcDir+'/lcl/interfaces/fpgui/corelib')
+';'+d(LazarusSrcDir+'/lcl/interfaces/fpgui/corelib/$('+ExternalMacroStart+'FPGUIPlatform)')
+';'+SrcPath
,da_DefineRecurse));
// and include path
IntfDirTemplate.AddChild(TDefineTemplate.Create('Add corelib to IncPath',
Format(ctsAddsDirToIncludePath,['corelib']),
ExternalMacroStart+'IncPath',
d(LazarusSrcDir+'/lcl/interfaces/fpgui/corelib')
+';'+d(LazarusSrcDir+'/lcl/interfaces/fpgui/corelib/$('+ExternalMacroStart+'FPGUIPlatform)')
+';'+IncPath
,da_Define));
SubDirTempl.AddChild(IntfDirTemplate);
// <LazarusSrcDir>/components
DirTempl:=TDefineTemplate.Create('Components',ctsComponentsDirectory,
'','components',da_Directory);
DirTempl.AddChild(TDefineTemplate.Create('LCL Path',
Format(ctsAddsDirToSourcePath,['lcl']),
ExternalMacroStart+'SrcPath',
d(LazarusSrcDir+'/lcl'
+';'+LazarusSrcDir+'/lcl/interfaces/'+WidgetType)
+';'+SrcPath
,da_DefineRecurse));
// <LazarusSrcDir>/components/synedit
SynEditDirTempl:=TDefineTemplate.Create('synedit',
'SynEdit','','synedit',da_Directory);
SynEditDirTempl.AddChild(TDefineTemplate.Create('IDEIntf Path',
Format(ctsAddsDirToSourcePath,['ideintf']),
ExternalMacroStart+'SrcPath',
d(LazarusSrcDir+'/ideintf')
+';'+SrcPath
,da_DefineRecurse));
// <LazarusSrcDir>/components/synedit/units
SynEditUnitsDirTempl:=TDefineTemplate.Create('synedit output directory',
'units','','units',da_Directory);
SynEditDirTempl.AddChild(SynEditUnitsDirTempl);
SynEditUnitsDirTempl.AddChild(TDefineTemplate.Create('CompiledSrcPath',
ctsSrcPathForCompiledUnits,
ExternalMacroStart+'CompiledSrcPath',
d(LazarusSrcDir+'components/synedit')
,da_DefineRecurse));
DirTempl.AddChild(SynEditDirTempl);
// <LazarusSrcDir>/components/lazcontrols
LazControlsDirTempl:=TDefineTemplate.Create('lazcontrols',
'LazControls','','lazcontrols',da_Directory);
// <LazarusSrcDir>/components/lazcontrols/lib
LazControlsUnitsDirTempl:=TDefineTemplate.Create('lazcontrols output directory',
'lib','','lib',da_Directory);
LazControlsDirTempl.AddChild(LazControlsUnitsDirTempl);
LazControlsUnitsDirTempl.AddChild(TDefineTemplate.Create('CompiledSrcPath',
ctsSrcPathForCompiledUnits,
ExternalMacroStart+'CompiledSrcPath',
d(LazarusSrcDir+'components/lazcontrols')
,da_DefineRecurse));
DirTempl.AddChild(LazControlsDirTempl);
// <LazarusSrcDir>/components/codetools/units
CodeToolsDirTempl:=TDefineTemplate.Create('codetools',
'CodeTools','','codetools',da_Directory);
CodeToolsUnitsDirTempl:=TDefineTemplate.Create('codetools output directory',
'units','','units',da_Directory);
CodeToolsDirTempl.AddChild(CodeToolsUnitsDirTempl);
CodeToolsUnitsDirTempl.AddChild(TDefineTemplate.Create('CompiledSrcPath',
ctsSrcPathForCompiledUnits,
ExternalMacroStart+'CompiledSrcPath',
d(LazarusSrcDir+'components/codetools')
,da_DefineRecurse));
DirTempl.AddChild(CodeToolsDirTempl);
// <LazarusSrcDir>/components/custom
SubDirTempl:=TDefineTemplate.Create('Custom Components',
ctsCustomComponentsDirectory,
'','custom',da_Directory);
SubDirTempl.AddChild(TDefineTemplate.Create('lazarus standard components',
Format(ctsAddsDirToSourcePath,['synedit']),
ExternalMacroStart+'SrcPath',
d('../synedit;')
+SrcPath
,da_DefineRecurse));
DirTempl.AddChild(SubDirTempl);
MainDir.AddChild(DirTempl);
// <LazarusSrcDir>/tools
DirTempl:=TDefineTemplate.Create('Tools',
ctsToolsDirectory,'','tools',da_Directory);
DirTempl.AddChild(TDefineTemplate.Create('LCL path addition',
Format(ctsAddsDirToSourcePath,['lcl']),
ExternalMacroStart+'SrcPath',
d('../lcl;../lcl/interfaces/'+WidgetType
+';../components/codetools')
+';'+SrcPath
,da_Define));
// <LazarusSrcDir>/tools/install
ToolsInstallDirTempl:=TDefineTemplate.Create('Install',
ctsInstallDirectory,'','install',da_Directory);
DirTempl.AddChild(ToolsInstallDirTempl);
ToolsInstallDirTempl.AddChild(TDefineTemplate.Create('LCL path addition',
Format(ctsAddsDirToSourcePath,['lcl']),
ExternalMacroStart+'SrcPath',
d('../../lcl;../../lcl/interfaces/'+WidgetType
+';../../components/codetools')
+';'+SrcPath
,da_Define));
MainDir.AddChild(DirTempl);
// extra options
SubTempl:=CreateFPCCommandLineDefines(StdDefTemplLazarusBuildOpts,
ExtraOptions,true,Owner);
MainDir.AddChild(SubTempl);
// put it all into a block
if MainDir<>nil then begin
Result:=TDefineTemplate.Create(StdDefTemplLazarusSources,
ctsLazarusSources,'','',da_Block);
Result.AddChild(MainDir);
end;
Result.SetDefineOwner(Owner,true);
Result.SetFlags([dtfAutoGenerated],[],false);
end;
function TDefinePool.CreateLCLProjectTemplate(
const LazarusSrcDir, WidgetType, ProjectDir: string;
Owner: TObject): TDefineTemplate;
var DirTempl: TDefineTemplate;
begin
Result:=nil;
if (LazarusSrcDir='') or (WidgetType='') or (ProjectDir='') then exit;
DirTempl:=TDefineTemplate.Create('ProjectDir',ctsAnLCLProject,
'',ProjectDir,da_Directory);
DirTempl.AddChild(TDefineTemplate.Create('LCL',
Format(ctsAddsDirToSourcePath,['lcl']),
ExternalMacroStart+'SrcPath',
LazarusSrcDir+PathDelim+'lcl;'
+LazarusSrcDir+PathDelim+'lcl'+PathDelim+'interfaces'
+PathDelim+WidgetType
+';$('+ExternalMacroStart+'SrcPath)'
,da_DefineRecurse));
Result:=TDefineTemplate.Create(StdDefTemplLCLProject,
'LCL Project','','',da_Block);
Result.AddChild(DirTempl);
Result.SetDefineOwner(Owner,true);
Result.SetFlags([dtfAutoGenerated],[],false);
end;
function TDefinePool.CreateDelphiCompilerDefinesTemplate(
DelphiVersion: integer; Owner: TObject): TDefineTemplate;
var
DefTempl: TDefineTemplate;
VerMacro: String;
begin
DefTempl:=TDefineTemplate.Create('Delphi'+IntToStr(DelphiVersion)
+' Compiler Defines',
Format(ctsOtherCompilerDefines,['Delphi'+IntToStr(DelphiVersion)]),
'','',da_Block);
DefTempl.AddChild(TDefineTemplate.Create('Reset',
ctsResetAllDefines,
'','',da_UndefineAll));
DefTempl.AddChild(TDefineTemplate.Create('Define macro DELPHI',
Format(ctsDefineMacroName,['DELPHI']),
'DELPHI','',da_DefineRecurse));
DefTempl.AddChild(TDefineTemplate.Create('Define macro FPC_DELPHI',
Format(ctsDefineMacroName,['FPC_DELPHI']),
'FPC_DELPHI','',da_DefineRecurse));
DefTempl.AddChild(TDefineTemplate.Create('Define macro MSWINDOWS',
Format(ctsDefineMacroName,['MSWINDOWS']),
'MSWINDOWS','',da_DefineRecurse));
// version
case DelphiVersion of
3: VerMacro:='VER_110';
4: VerMacro:='VER_125';
5: VerMacro:='VER_130';
6: VerMacro:='VER_140';
else
// else define Delphi 7
VerMacro:='VER_150';
end;
DefTempl.AddChild(TDefineTemplate.Create('Define macro '+VerMacro,
Format(ctsDefineMacroName,[VerMacro]),
VerMacro,'',da_DefineRecurse));
DefTempl.AddChild(TDefineTemplate.Create(
Format(ctsDefineMacroName,[ExternalMacroStart+'Compiler']),
'Define '+ExternalMacroStart+'Compiler variable',
ExternalMacroStart+'Compiler','DELPHI',da_DefineRecurse));
Result:=DefTempl;
Result.SetDefineOwner(Owner,true);
end;
function TDefinePool.CreateDelphiDirectoryTemplate(
const DelphiDirectory: string; DelphiVersion: integer;
Owner: TObject): TDefineTemplate;
var MainDirTempl: TDefineTemplate;
begin
MainDirTempl:=TDefineTemplate.Create('Delphi'+IntToStr(DelphiVersion)
+' Directory',
Format(ctsNamedDirectory,['Delphi'+IntToStr(DelphiVersion)]),
'',DelphiDirectory,da_Directory);
MainDirTempl.AddChild(CreateDelphiCompilerDefinesTemplate(DelphiVersion,Owner));
MainDirTempl.AddChild(TDefineTemplate.Create('SrcPath',
Format(ctsSetsSrcPathTo,['RTL, VCL']),
ExternalMacroStart+'SrcPath',
SetDirSeparators(
CreateDelphiSrcPath(DelphiVersion,DefinePathMacro+'/')+'$(#SrcPath)'),
da_DefineRecurse));
Result:=MainDirTempl;
Result.SetDefineOwner(Owner,true);
end;
function TDefinePool.CreateDelphiProjectTemplate(
const ProjectDir, DelphiDirectory: string;
DelphiVersion: integer; Owner: TObject): TDefineTemplate;
var MainDirTempl: TDefineTemplate;
begin
MainDirTempl:=TDefineTemplate.Create('Delphi'+IntToStr(DelphiVersion)+' Project',
Format(ctsNamedProject,['Delphi'+IntToStr(DelphiVersion)]),
'',ProjectDir,da_Directory);
MainDirTempl.AddChild(
CreateDelphiCompilerDefinesTemplate(DelphiVersion,Owner));
MainDirTempl.AddChild(TDefineTemplate.Create(
'Define '+ExternalMacroStart+'DelphiDir',
Format(ctsDefineMacroName,[ExternalMacroStart+'DelphiDir']),
ExternalMacroStart+'DelphiDir',DelphiDirectory,da_DefineRecurse));
MainDirTempl.AddChild(TDefineTemplate.Create('SrcPath',
Format(ctsAddsDirToSourcePath,['Delphi RTL+VCL']),
ExternalMacroStart+'SrcPath',
SetDirSeparators(CreateDelphiSrcPath(DelphiVersion,'$(#DelphiDir)/')
+'$(#SrcPath)'),
da_DefineRecurse));
Result:=MainDirTempl;
Result.SetDefineOwner(Owner,true);
end;
function TDefinePool.CreateKylixCompilerDefinesTemplate(KylixVersion: integer;
Owner: TObject): TDefineTemplate;
var
DefTempl: TDefineTemplate;
begin
DefTempl:=TDefineTemplate.Create('Kylix'+IntToStr(KylixVersion)
+' Compiler Defines',
Format(ctsOtherCompilerDefines,['Kylix'+IntToStr(KylixVersion)]),
'','',da_Block);
DefTempl.AddChild(TDefineTemplate.Create('Reset',
ctsResetAllDefines,
'','',da_UndefineAll));
DefTempl.AddChild(TDefineTemplate.Create('Define macro KYLIX',
Format(ctsDefineMacroName,['KYLIX']),
'KYLIX','',da_DefineRecurse));
DefTempl.AddChild(TDefineTemplate.Create('Define macro FPC_DELPHI',
Format(ctsDefineMacroName,['FPC_DELPHI']),
'FPC_DELPHI','',da_DefineRecurse));
DefTempl.AddChild(TDefineTemplate.Create('Define macro LINUX',
Format(ctsDefineMacroName,['LINUX']),
'LINUX','',da_DefineRecurse));
DefTempl.AddChild(TDefineTemplate.Create('Define macro CPU386',
Format(ctsDefineMacroName,['CPU386']),
'CPU386','',da_DefineRecurse));
// version
case KylixVersion of
1:
DefTempl.AddChild(TDefineTemplate.Create('Define macro VER_125',
Format(ctsDefineMacroName,['VER_125']),
'VER_125','',da_DefineRecurse));
2:
DefTempl.AddChild(TDefineTemplate.Create('Define macro VER_130',
Format(ctsDefineMacroName,['VER_130']),
'VER_130','',da_DefineRecurse));
else
// else define Kylix 3
DefTempl.AddChild(TDefineTemplate.Create('Define macro VER_140',
Format(ctsDefineMacroName,['VER_140']),
'VER_140','',da_DefineRecurse));
end;
DefTempl.AddChild(TDefineTemplate.Create(
Format(ctsDefineMacroName,[ExternalMacroStart+'Compiler']),
'Define '+ExternalMacroStart+'Compiler variable',
ExternalMacroStart+'Compiler','DELPHI',da_DefineRecurse));
Result:=DefTempl;
Result.SetDefineOwner(Owner,true);
end;
function TDefinePool.CreateKylixSrcPath(KylixVersion: integer;
const PathPrefix: string): string;
begin
Result:=PathPrefix+'source/rtl/linux;'
+PathPrefix+'source/rtl/sys;'
+PathPrefix+'source/rtl/common;'
+PathPrefix+'source/rtl/corba40;'
+PathPrefix+'source/rtle;'
+PathPrefix+'source/rtl/clx';
end;
function TDefinePool.CreateKylixDirectoryTemplate(const KylixDirectory: string;
KylixVersion: integer; Owner: TObject): TDefineTemplate;
var MainDirTempl: TDefineTemplate;
begin
MainDirTempl:=TDefineTemplate.Create('Kylix'+IntToStr(KylixVersion)
+' Directory',
Format(ctsNamedDirectory,['Kylix'+IntToStr(KylixVersion)]),
'',KylixDirectory,da_Directory);
MainDirTempl.AddChild(CreateKylixCompilerDefinesTemplate(KylixVersion,Owner));
MainDirTempl.AddChild(TDefineTemplate.Create('SrcPath',
Format(ctsSetsSrcPathTo,['RTL, CLX']),
ExternalMacroStart+'SrcPath',
SetDirSeparators(CreateKylixSrcPath(KylixVersion,DefinePathMacro+'/')
+'$(#SrcPath)'),
da_DefineRecurse));
Result:=MainDirTempl;
Result.SetDefineOwner(Owner,true);
end;
function TDefinePool.CreateKylixProjectTemplate(const ProjectDir,
KylixDirectory: string; KylixVersion: integer; Owner: TObject
): TDefineTemplate;
var MainDirTempl: TDefineTemplate;
begin
MainDirTempl:=TDefineTemplate.Create('Kylix'+IntToStr(KylixVersion)+' Project',
Format(ctsNamedProject,['Kylix'+IntToStr(KylixVersion)]),
'',ProjectDir,da_Directory);
MainDirTempl.AddChild(
CreateDelphiCompilerDefinesTemplate(KylixVersion,Owner));
MainDirTempl.AddChild(TDefineTemplate.Create(
'Define '+ExternalMacroStart+'KylixDir',
Format(ctsDefineMacroName,[ExternalMacroStart+'KylixDir']),
ExternalMacroStart+'KylixDir',KylixDirectory,da_DefineRecurse));
MainDirTempl.AddChild(TDefineTemplate.Create('SrcPath',
Format(ctsAddsDirToSourcePath,['Kylix RTL+VCL']),
ExternalMacroStart+'SrcPath',
SetDirSeparators(CreateKylixSrcPath(KylixVersion,'$(#KylixDir)/')
+'$(#SrcPath)'),
da_DefineRecurse));
Result:=MainDirTempl;
Result.SetDefineOwner(Owner,true);
end;
function TDefinePool.CreateFPCCommandLineDefines(const Name, CmdLine: string;
RecursiveDefines: boolean; Owner: TObject; AlwaysCreate: boolean): TDefineTemplate;
procedure CreateMainTemplate;
begin
if Result=nil then
Result:=TDefineTemplate.Create(Name,ctsCommandLineParameters,'','',
da_Block);
end;
procedure AddDefine(const AName, ADescription, AVariable, AValue: string;
AnAction: TDefineAction);
var
NewTempl: TDefineTemplate;
begin
if AName='' then exit;
NewTempl:=TDefineTemplate.Create(AName, ADescription, AVariable, AValue,
AnAction);
CreateMainTemplate;
Result.AddChild(NewTempl);
end;
procedure AddDefine(const AName, ADescription, AVariable, AValue: string);
var
NewAction: TDefineAction;
begin
if RecursiveDefines then
NewAction:=da_DefineRecurse
else
NewAction:=da_Define;
AddDefine(AName,ADescription,AVariable,AValue,NewAction);
end;
procedure AddDefine(const AParam: string);
var
Identifier: String;
AValue: String;
begin
Identifier:=GetIdentifier(PChar(AParam));
if Identifier='' then exit;
AValue:='';
if length(Identifier)<length(AParam) then begin
if copy(AParam,length(Identifier)+1,2)=':=' then
AValue:=copy(AParam,length(Identifier)+3,length(AParam));
end;
AddDefine('Define '+Identifier,ctsDefine+AParam,Identifier,AValue);
end;
procedure AddUndefine(const AParam: string);
var
NewAction: TDefineAction;
Identifier: String;
begin
if RecursiveDefines then
NewAction:=da_UndefineRecurse
else
NewAction:=da_Undefine;
Identifier:=GetIdentifier(PChar(AParam));
AddDefine('Undefine '+Identifier,ctsUndefine+AParam,Identifier,'',NewAction);
end;
procedure AddDefineUndefine(const AName: string; Define: boolean);
begin
if Define then
AddDefine(AName)
else
AddUndefine(AName);
end;
var
StartPos, EndPos: Integer;
s: string;
CompilerMode: String;
begin
Result:=nil;
if AlwaysCreate then
CreateMainTemplate;
EndPos:=1;
CompilerMode:='';
while ReadNextFPCParameter(CmdLine,EndPos,StartPos) do begin
if (StartPos<length(CmdLine)) and (CmdLine[StartPos]='-') then begin
// a parameter
case CmdLine[StartPos+1] of
'd':
begin
// define
s:=copy(CmdLine,StartPos+2,EndPos-StartPos-2);
AddDefine(s);
end;
'u':
begin
// undefine
s:=copy(CmdLine,StartPos+2,EndPos-StartPos-2);
AddUndefine(s);
end;
'S':
begin
// syntax
inc(StartPos,2);
while StartPos<EndPos do begin
case CmdLine[StartPos] of
'2': CompilerMode:='ObjFPC';
'd': CompilerMode:='Delphi';
'o': CompilerMode:='TP';
'p': CompilerMode:='GPC';
end;
inc(StartPos);
end;
end;
'M':
begin
// syntax
inc(StartPos,2);
CompilerMode:=copy(CmdLine,StartPos,EndPos-StartPos);
end;
end;
end;
end;
if CompilerMode<>'' then begin
AddDefineUndefine('FPC_FPC',SysUtils.CompareText(CompilerMode,'FPC')=0);
AddDefineUndefine('FPC_ObjFPC',SysUtils.CompareText(CompilerMode,'ObjFPC')=0);
AddDefineUndefine('FPC_Delphi',SysUtils.CompareText(CompilerMode,'Delphi')=0);
AddDefineUndefine('FPC_TP',SysUtils.CompareText(CompilerMode,'TP')=0);
AddDefineUndefine('FPC_GPC',SysUtils.CompareText(CompilerMode,'GPC')=0);
AddDefineUndefine('FPC_MACPAS',SysUtils.CompareText(CompilerMode,'MACPAS')=0);
end;
Result.SetDefineOwner(Owner,true);
end;
procedure TDefinePool.ConsistencyCheck;
var i: integer;
begin
for i:=0 to Count-1 do
Items[i].ConsistencyCheck;
end;
procedure TDefinePool.WriteDebugReport;
var i: integer;
begin
DebugLn('TDefinePool.WriteDebugReport');
for i:=0 to Count-1 do
Items[i].WriteDebugReport(false);
ConsistencyCheck;
end;
procedure TDefinePool.CalcMemSize(Stats: TCTMemStats);
var
i: Integer;
begin
Stats.Add('TDefinePool',PtrUInt(InstanceSize)
+MemSizeString(FEnglishErrorMsgFilename));
if FItems<>nil then begin
Stats.Add('TDefinePool.Count',Count);
for i:=0 to Count-1 do
Items[i].CalcMemSize(Stats);
end;
end;
{ TFPCSourceRules }
function TFPCSourceRules.GetItems(Index: integer): TFPCSourceRule;
begin
Result:=TFPCSourceRule(FItems[Index]);
end;
procedure TFPCSourceRules.SetTargets(const AValue: string);
begin
if FTargets=AValue then exit;
FTargets:=LowerCase(AValue);
end;
constructor TFPCSourceRules.Create;
begin
FItems:=TFPList.Create;
end;
destructor TFPCSourceRules.Destroy;
begin
Clear;
FreeAndNil(FItems);
inherited Destroy;
end;
procedure TFPCSourceRules.Clear;
var
i: Integer;
begin
if FItems.Count=0 then exit;
for i:=0 to FItems.Count-1 do
TObject(FItems[i]).Free;
IncreaseChangeStamp;
end;
function TFPCSourceRules.IsEqual(Rules: TFPCSourceRules): boolean;
var
i: Integer;
begin
Result:=false;
if Count<>Rules.Count then exit;
for i:=0 to Count-1 do
if not Items[i].IsEqual(Rules[i]) then exit;
Result:=true;
end;
procedure TFPCSourceRules.Assign(Rules: TFPCSourceRules);
var
i: Integer;
SrcRule: TFPCSourceRule;
Rule: TFPCSourceRule;
begin
if IsEqual(Rules) then exit;
Clear;
for i:=0 to Rules.Count-1 do begin
SrcRule:=Rules[i];
Rule:=Add(SrcRule.Filename);
Rule.Assign(SrcRule);
end;
IncreaseChangeStamp;
end;
function TFPCSourceRules.Clone: TFPCSourceRules;
begin
Result:=TFPCSourceRules.Create;
Result.Assign(Self);
end;
function TFPCSourceRules.Count: integer;
begin
Result:=FItems.Count;
end;
function TFPCSourceRules.Add(const Filename: string): TFPCSourceRule;
begin
Result:=TFPCSourceRule.Create;
Result.Score:=Score;
Result.Targets:=Targets;
//DebugLn(['TFPCSourceRules.Add Targets="',Result.Targets,'" Priority=',Result.Score]);
Result.Filename:=lowercase(SetDirSeparators(Filename));
FItems.Add(Result);
IncreaseChangeStamp;
end;
function TFPCSourceRules.GetDefaultTargets(TargetOS, TargetCPU: string): string;
var
SrcOS: String;
SrcOS2: String;
SrcCPU: String;
begin
if TargetOS='' then
TargetOS:=GetCompiledTargetOS;
if TargetCPU='' then
TargetCPU:=GetCompiledTargetCPU;
Result:=TargetOS+','+TargetCPU;
SrcOS:=GetDefaultSrcOSForTargetOS(TargetOS);
SrcOS2:=GetDefaultSrcOS2ForTargetOS(TargetOS);
SrcCPU:=GetDefaultSrcCPUForTargetCPU(TargetCPU);
if SrcOS<>'' then Result:=Result+','+SrcOS;
if SrcOS2<>'' then Result:=Result+','+SrcOS2;
if SrcCPU<>'' then Result:=Result+','+SrcCPU;
end;
procedure TFPCSourceRules.GetRulesForTargets(Targets: string;
var RulesSortedForFilenameStart: TAVLTree);
var
i: Integer;
begin
if RulesSortedForFilenameStart=nil then
RulesSortedForFilenameStart:=
TAVLTree.Create(@CompareFPCSourceRulesViaFilenameStart);
for i:=0 to Count-1 do
if Items[i].FitsTargets(Targets) then
RulesSortedForFilenameStart.Add(Items[i]);
end;
function TFPCSourceRules.GetScore(Filename: string;
RulesSortedForFilenameStart: TAVLTree): integer;
var
Node: TAVLTreeNode;
Rule: TFPCSourceRule;
cmp: LongInt;
Cnt: Integer;
begin
Result:=0;
if Filename='' then exit;
Filename:=LowerCase(Filename);
{Node:=RulesSortedForFilenameStart.FindLowest;
while Node<>nil do begin
Rule:=TFPCSourceRule(Node.Data);
DebugLn(['TFPCSourceRules.GetScore Rule: ',Rule.Score,' ',Rule.Filename]);
Node:=RulesSortedForFilenameStart.FindSuccessor(Node);
end;}
// find first rule for Filename
Node:=RulesSortedForFilenameStart.Root;
while true do begin
Rule:=TFPCSourceRule(Node.Data);
cmp:=CompareStr(Filename,Rule.Filename);
//DebugLn(['TFPCSourceRules.GetScore Rule.Filename=',Rule.Filename,' Filename=',Filename,' cmp=',cmp]);
if cmp=0 then
break;
if cmp<0 then begin
if Node.Left<>nil then
Node:=Node.Left
else
break;
end else begin
if Node.Right<>nil then
Node:=Node.Right
else
break;
end;
end;
{ The rules are sorted for the file name. Shorter file names comes before
longer ones.
packages/httpd20/examples
packages/httpd22
packages/httpd22/examples
A filename packages/httpd22/examples matches
packages/httpd22
and packages/httpd22/examples
If a file name has no exact match the binary search for packages/httpd22/e
can either point to
packages/httpd22
or packages/httpd22/examples
}
// run through all fitting rules (the Filename is >= Rule.Filename)
Cnt:=0;
while Node<>nil do begin
inc(Cnt);
Rule:=TFPCSourceRule(Node.Data);
if Rule.FitsFilename(Filename) then
inc(Result,Rule.Score)
else if Cnt>1 then
break;
Node:=RulesSortedForFilenameStart.FindPrecessor(Node);
end;
end;
procedure TFPCSourceRules.IncreaseChangeStamp;
begin
if FChangeStamp<High(FChangeStamp) then
inc(FChangeStamp)
else
FChangeStamp:=Low(FChangeStamp);
end;
{ TFPCSourceRule }
function TFPCSourceRule.FitsTargets(const FilterTargets: string): boolean;
var
FilterStartPos: PChar;
TargetPos: PChar;
FilterPos: PChar;
begin
//DebugLn(['TFPCSourceRule.FitsTargets FilterTargets="',FilterTargets,'" Targets="',Targets,'"']);
if Targets='*' then exit(true);
if (Targets='') or (FilterTargets='') then exit(false);
FilterStartPos:=PChar(FilterTargets);
while true do begin
while (FilterStartPos^=',') do inc(FilterStartPos);
if FilterStartPos^=#0 then exit(false);
TargetPos:=PChar(Targets);
repeat
while (TargetPos^=',') do inc(TargetPos);
if TargetPos^=#0 then break;
FilterPos:=FilterStartPos;
while (FilterPos^=TargetPos^) and (not (FilterPos^ in [#0,','])) do begin
inc(TargetPos);
inc(FilterPos);
end;
if (TargetPos^ in [#0,',']) then begin
// the target fits
exit(true);
end;
// try next target
while not (TargetPos^ in [#0,',']) do inc(TargetPos);
until TargetPos^=#0;
// next target filter
while not (FilterStartPos^ in [#0,',']) do inc(FilterStartPos);
end;
Result:=false;
end;
function TFPCSourceRule.FitsFilename(const aFilename: string): boolean;
begin
Result:=(length(Filename)<=length(aFilename))
and CompareMem(Pointer(Filename),Pointer(aFilename),length(Filename));
end;
function TFPCSourceRule.IsEqual(Rule: TFPCSourceRule): boolean;
begin
Result:=false;
if (Filename<>Rule.Filename)
or (Score<>Rule.Score)
or (Targets<>Rule.Targets) then
exit;
Result:=true;
end;
procedure TFPCSourceRule.Assign(Rule: TFPCSourceRule);
begin
Filename:=Rule.Filename;
Score:=Rule.Score;
Targets:=Rule.Targets;
end;
{ TFPCTargetConfigCacheItem }
constructor TFPCTargetConfigCache.Create(AOwner: TComponent);
begin
FChangeStamp:=CTInvalidChangeStamp;
inherited Create(AOwner);
ConfigFiles:=TFPCConfigFileStateList.Create;
if Owner is TFPCTargetConfigCaches then
Caches:=TFPCTargetConfigCaches(Owner);
end;
destructor TFPCTargetConfigCache.Destroy;
begin
Clear;
FreeAndNil(ConfigFiles);
inherited Destroy;
end;
procedure TFPCTargetConfigCache.Clear;
begin
CompilerDate:=0;
CompilerOptions:='';
RealCompiler:='';
RealCompilerDate:=0;
RealTargetCPU:='';
RealTargetOS:='';
RealCompilerInPath:='';
HasPPUs:=false;
ConfigFiles.Clear;
ErrorMsg:='';
ErrorTranslatedMsg:='';
FreeAndNil(Defines);
FreeAndNil(Undefines);
FreeAndNil(UnitPaths);
FreeAndNil(Units);
end;
function TFPCTargetConfigCache.Equals(Item: TFPCTargetConfigCache;
CompareKey: boolean): boolean;
function CompareStrings(List1, List2: TStrings): boolean;
var
List1Empty: Boolean;
List2Empty: Boolean;
begin
Result:=false;
List1Empty:=(List1=nil) or (List1.Count=0);
List2Empty:=(List2=nil) or (List2.Count=0);
if (List1Empty<>List2Empty) then exit;
if (not List1Empty) and (not List1.Equals(List2)) then exit;
Result:=true;
end;
function CompareStringTrees(Tree1, Tree2: TStringToStringTree): boolean;
var
Tree1Empty: Boolean;
Tree2Empty: Boolean;
begin
Result:=false;
Tree1Empty:=(Tree1=nil) or (Tree1.Tree.Count=0);
Tree2Empty:=(Tree2=nil) or (Tree2.Tree.Count=0);
if (Tree1Empty<>Tree2Empty) then exit;
if (not Tree1Empty) and (not Tree1.Equals(Tree2)) then exit;
Result:=true;
end;
begin
Result:=false;
if CompareKey then begin
if (TargetOS<>Item.TargetOS)
or (TargetCPU<>Item.TargetCPU)
or (Compiler<>Item.Compiler)
or (CompilerOptions<>Item.CompilerOptions)
then
exit;
end;
if (CompilerDate<>Item.CompilerDate)
or (RealCompiler<>Item.RealCompiler)
or (RealCompilerDate<>Item.RealCompilerDate)
or (RealTargetOS<>Item.RealTargetOS)
or (RealTargetCPU<>Item.RealTargetCPU)
or (RealCompilerInPath<>Item.RealCompilerInPath)
or (HasPPUs<>Item.HasPPUs)
or (not ConfigFiles.Equals(Item.ConfigFiles,true))
then
exit;
if not CompareStringTrees(Defines,Item.Defines) then exit;
if not CompareStringTrees(Undefines,Item.Undefines) then exit;
if not CompareStrings(UnitPaths,Item.UnitPaths) then exit;
if not CompareStringTrees(Units,Item.Units) then exit;
Result:=true;
end;
procedure TFPCTargetConfigCache.Assign(Source: TPersistent);
var
Item: TFPCTargetConfigCache;
begin
if Source is TFPCTargetConfigCache then begin
Item:=TFPCTargetConfigCache(Source);
// keys
TargetOS:=Item.TargetOS;
TargetCPU:=Item.TargetCPU;
Compiler:=Item.Compiler;
CompilerOptions:=Item.CompilerOptions;
// values
CompilerDate:=Item.CompilerDate;
RealCompiler:=Item.RealCompiler;
RealCompilerDate:=Item.RealCompilerDate;
RealTargetOS:=Item.RealTargetOS;
RealTargetCPU:=Item.RealTargetCPU;
RealCompilerInPath:=Item.RealCompilerInPath;
HasPPUs:=Item.HasPPUs;
ConfigFiles.Assign(Item.ConfigFiles);
if Item.Defines<>nil then begin
if Defines=nil then Defines:=TStringToStringTree.Create(false);
Defines.Assign(Item.Defines);
end else begin
FreeAndNil(Defines);
end;
if Item.Undefines<>nil then begin
if Undefines=nil then Undefines:=TStringToStringTree.Create(false);
Undefines.Assign(Item.Undefines);
end else begin
FreeAndNil(Undefines);
end;
if Item.UnitPaths<>nil then begin
if UnitPaths=nil then UnitPaths:=TStringList.Create;
UnitPaths.Assign(Item.UnitPaths);
end else begin
FreeAndNil(UnitPaths);
end;
if Item.Units<>nil then begin
if Units=nil then Units:=TStringToStringTree.Create(false);
Units.Assign(Item.Units);
end else begin
FreeAndNil(Units);
end;
ErrorMsg:=Item.ErrorMsg;
ErrorTranslatedMsg:=Item.ErrorTranslatedMsg;
end else
inherited Assign(Source);
end;
procedure TFPCTargetConfigCache.LoadFromXMLConfig(XMLConfig: TXMLConfig;
const Path: string);
var
Cnt: integer;
SubPath: String;
DefineName, DefineValue: String;
s: String;
i: Integer;
p: Integer;
StartPos: Integer;
List: TStringList;
UnitList: TStringList;
Unit_Name: String;
Filename: String;
BaseDir: String;
begin
Clear;
TargetOS:=XMLConfig.GetValue(Path+'TargetOS','');
TargetCPU:=XMLConfig.GetValue(Path+'TargetCPU','');
Compiler:=XMLConfig.GetValue(Path+'Compiler/File','');
CompilerOptions:=XMLConfig.GetValue(Path+'Compiler/Options','');
CompilerDate:=XMLConfig.GetValue(Path+'Compiler/Date',0);
RealCompiler:=XMLConfig.GetValue(Path+'RealCompiler/File','');
RealCompilerDate:=XMLConfig.GetValue(Path+'RealCompiler/Date',0);
RealTargetOS:=XMLConfig.GetValue(Path+'RealCompiler/OS','');
RealTargetCPU:=XMLConfig.GetValue(Path+'RealCompiler/CPU','');
RealCompilerInPath:=XMLConfig.GetValue(Path+'RealCompiler/InPath','');
HasPPUs:=XMLConfig.GetValue(Path+'HasPPUs',true);
ConfigFiles.LoadFromXMLConfig(XMLConfig,Path+'Configs/');
// defines: format: Define<Number>/Name,Value
Cnt:=XMLConfig.GetValue(Path+'Defines/Count',0);
for i:=1 to Cnt do begin
SubPath:=Path+'Defines/Macro'+IntToStr(i)+'/';
DefineName:=UpperCaseStr(XMLConfig.GetValue(SubPath+'Name',''));
if (DefineName='') or (not IsValidIdent(DefineName)) then begin
DebugLn(['TFPCTargetConfigCache.LoadFromXMLConfig invalid define name ',DefineName]);
continue;
end;
DefineValue:=XMLConfig.GetValue(SubPath+'Value','');
if Defines=nil then
Defines:=TStringToStringTree.Create(false);
Defines[DefineName]:=DefineValue;
end;
// undefines: format: Undefines/Value and comma separated list of names
s:=XMLConfig.GetValue(Path+'Undefines/Values','');
if s<>'' then begin
p:=1;
while (p<=length(s)) do begin
StartPos:=1;
while (p<=length(s)) and (s[p]<>';') do inc(p);
DefineName:=copy(s,StartPos,p-StartPos);
if (DefineName<>'') and IsValidIdent(DefineName) then begin
if Undefines=nil then
Undefines:=TStringToStringTree.Create(false);
Undefines[DefineName]:='';
end;
inc(p);
end;
end;
// UnitPaths: format: semicolon separated compressed list
List:=TStringList.Create;
try
s:=XMLConfig.GetValue(Path+'UnitPaths/Value','');
List.Delimiter:=';';
List.StrictDelimiter:=true;
List.DelimitedText:=s;
UnitPaths:=Decompress1FileList(List);
BaseDir:=TrimFilename(AppendPathDelim(XMLConfig.GetValue(Path+'UnitPaths/BaseDir','')));
if BaseDir<>'' then
for i:=0 to UnitPaths.Count-1 do
UnitPaths[i]:=ChompPathDelim(TrimFilename(BaseDir+UnitPaths[i]))
else
for i:=UnitPaths.Count-1 downto 0 do
if UnitPaths[i]='' then
UnitPaths.Delete(i)
else
UnitPaths[i]:=ChompPathDelim(TrimFilename(UnitPaths[i]));
// do not sort, order is important (e.g. for httpd.ppu)
finally
List.Free;
end;
// units: format: Units/Values semicolon separated list of compressed filename
List:=TStringList.Create;
UnitList:=nil;
try
s:=XMLConfig.GetValue(Path+'Units/Value','');
List.Delimiter:=';';
List.StrictDelimiter:=true;
List.DelimitedText:=s;
UnitList:=Decompress1FileList(List);
for i:=0 to UnitList.Count-1 do begin
Filename:=TrimFilename(UnitList[i]);
Unit_Name:=ExtractFileNameOnly(Filename);
if (Unit_Name='') or not IsValidIdent(Unit_Name) then begin
DebugLn(['TFPCTargetConfigCache.LoadFromXMLConfig invalid unitname: ',s]);
continue;
end;
if Units=nil then
Units:=TStringToStringTree.Create(false);
Units[Unit_Name]:=Filename;
end;
finally
List.Free;
UnitList.Free;
end;
end;
procedure TFPCTargetConfigCache.SaveToXMLConfig(XMLConfig: TXMLConfig;
const Path: string);
var
Node: TAVLTreeNode;
Item: PStringToStringTreeItem;
Cnt: Integer;
SubPath: String;
UnitList: TStringList;
Filename: String;
List: TStringList;
s: String;
BaseDir: string;
RelativeUnitPaths: TStringList;
begin
XMLConfig.SetDeleteValue(Path+'TargetOS',TargetOS,'');
XMLConfig.SetDeleteValue(Path+'TargetCPU',TargetCPU,'');
XMLConfig.SetDeleteValue(Path+'Compiler/File',Compiler,'');
XMLConfig.SetDeleteValue(Path+'Compiler/Options',CompilerOptions,'');
XMLConfig.SetDeleteValue(Path+'Compiler/Date',CompilerDate,0);
XMLConfig.SetDeleteValue(Path+'RealCompiler/File',RealCompiler,'');
XMLConfig.SetDeleteValue(Path+'RealCompiler/Date',RealCompilerDate,0);
XMLConfig.SetDeleteValue(Path+'RealCompiler/OS',RealTargetOS,'');
XMLConfig.SetDeleteValue(Path+'RealCompiler/CPU',RealTargetCPU,'');
XMLConfig.SetDeleteValue(Path+'RealCompiler/InPath',RealCompilerInPath,'');
XMLConfig.SetDeleteValue(Path+'HasPPUs',HasPPUs,true);
ConfigFiles.SaveToXMLConfig(XMLConfig,Path+'Configs/');
// Defines: write as Define<Number>/Name,Value
Cnt:=0;
if Defines<>nil then begin
Node:=Defines.Tree.FindLowest;
while Node<>nil do begin
Item:=PStringToStringTreeItem(Node.Data);
if (Item^.Name<>'') and IsValidIdent(Item^.Name) then begin
inc(Cnt);
SubPath:=Path+'Defines/Macro'+IntToStr(Cnt)+'/';
XMLConfig.SetDeleteValue(SubPath+'Name',Item^.Name,'');
XMLConfig.SetDeleteValue(SubPath+'Value',Item^.Value,'');
end;
Node:=Defines.Tree.FindSuccessor(Node);
end;
end;
XMLConfig.SetDeleteValue(Path+'Defines/Count',Cnt,0);
// Undefines: write as Undefines/Value and comma separated list of names
Cnt:=0;
s:='';
if Undefines<>nil then begin
Node:=Undefines.Tree.FindLowest;
while Node<>nil do begin
Item:=PStringToStringTreeItem(Node.Data);
inc(Cnt);
if s<>'' then s:=s+',';
s:=s+Item^.Name;
Node:=Undefines.Tree.FindSuccessor(Node);
end;
end;
XMLConfig.SetDeleteValue(Path+'Undefines/Values',s,'');
// UnitPaths: write as semicolon separated compressed list
s:='';
BaseDir:='';
if UnitPaths<>nil then begin
List:=nil;
RelativeUnitPaths:=nil;
try
RelativeUnitPaths:=MakeRelativeFileList(UnitPaths,BaseDir);
List:=Compress1FileList(RelativeUnitPaths);
// do not sort, order is important (e.g. for httpd.ppu)
List.Delimiter:=';';
List.StrictDelimiter:=true;
s:=List.DelimitedText;
finally
RelativeUnitPaths.Free;
List.Free;
end;
end;
XMLConfig.SetDeleteValue(Path+'UnitPaths/BaseDir',BaseDir,'');
XMLConfig.SetDeleteValue(Path+'UnitPaths/Value',s,'');
// Units: Units/Values semicolon separated list of compressed filenames
// Units contains thousands of file names. This needs compression.
s:='';
List:=nil;
UnitList:=TStringList.Create;
try
if Units<>nil then begin
// Create a string list of filenames
Node:=Units.Tree.FindLowest;
while Node<>nil do begin
Item:=PStringToStringTreeItem(Node.Data);
Filename:=Item^.Value;
UnitList.Add(Filename);
Node:=Units.Tree.FindSuccessor(Node);
end;
// Sort the strings.
UnitList.CaseSensitive:=true;
UnitList.Sort;
// Compress the file names
List:=Compress1FileList(UnitList);
// and write the semicolon separated list
List.Delimiter:=';';
List.StrictDelimiter:=true;
s:=List.DelimitedText;
end;
finally
List.Free;
UnitList.Free;
end;
XMLConfig.SetDeleteValue(Path+'Units/Value',s,'');
end;
procedure TFPCTargetConfigCache.LoadFromFile(Filename: string);
var
XMLConfig: TXMLConfig;
begin
XMLConfig:=TXMLConfig.Create(Filename);
try
LoadFromXMLConfig(XMLConfig,'FPCConfig/');
finally
XMLConfig.Free;
end;
end;
procedure TFPCTargetConfigCache.SaveToFile(Filename: string);
var
XMLConfig: TXMLConfig;
begin
XMLConfig:=TXMLConfig.CreateClean(Filename);
try
SaveToXMLConfig(XMLConfig,'FPCConfig/');
finally
XMLConfig.Free;
end;
end;
function TFPCTargetConfigCache.NeedsUpdate: boolean;
var
i: Integer;
Cfg: TFPCConfigFileState;
AFilename: String;
begin
Result:=true;
if (not FileExistsCached(Compiler)) then begin
debugln(['TFPCTargetConfigCache.NeedsUpdate compiler file missing "',Compiler,'"']);
exit;
end;
if (FileAgeCached(Compiler)<>CompilerDate) then begin
debugln(['TFPCTargetConfigCache.NeedsUpdate compiler file changed "',Compiler,'" FileAge=',FileAgeCached(Compiler),' StoredAge=',CompilerDate]);
exit;
end;
if (RealCompiler<>'') and (CompareFilenames(RealCompiler,Compiler)<>0)
then begin
if (not FileExistsCached(RealCompiler))
or (FileAgeCached(RealCompiler)<>RealCompilerDate) then begin
debugln(['TFPCTargetConfigCache.NeedsUpdate real compiler file changed "',RealCompiler,'"']);
exit;
end;
// fpc searches via PATH for the real compiler, resolves any symlink
// and that is the RealCompiler
// check if PATH
AFilename:=FindRealCompilerInPath(TargetCPU,true);
if RealCompilerInPath<>AFilename then begin
debugln(['TFPCTargetConfigCache.NeedsUpdate real compiler in PATH changed from "',RealCompilerInPath,'" to "',AFilename,'"']);
exit;
end;
end;
for i:=0 to ConfigFiles.Count-1 do begin
Cfg:=ConfigFiles[i];
if Cfg.Filename='' then continue;
if FileExistsCached(Cfg.Filename)<>Cfg.FileExists then begin
debugln(['TFPCTargetConfigCache.NeedsUpdate config fileexists changed "',Cfg.Filename,'"']);
exit;
end;
if Cfg.FileExists and (FileAgeCached(Cfg.Filename)<>Cfg.FileDate) then begin
debugln(['TFPCTargetConfigCache.NeedsUpdate config file changed "',Cfg.Filename,'"']);
exit;
end;
end;
Result:=false;
end;
procedure TFPCTargetConfigCache.IncreaseChangeStamp;
begin
CTIncreaseChangeStamp(FChangeStamp);
if Caches<>nil then
Caches.IncreaseChangeStamp;
end;
function TFPCTargetConfigCache.Update(TestFilename: string;
ExtraOptions: string; const OnProgress: TDefinePoolProgress): boolean;
var
OldOptions: TFPCTargetConfigCache;
CfgFiles: TStrings;
i: Integer;
Filename: string;
CfgFileExists: Boolean;
CfgFileDate: Integer;
Info: String;
Infos: TFPCInfoStrings;
begin
OldOptions:=TFPCTargetConfigCache.Create(nil);
CfgFiles:=nil;
try
// remember old state to find out if something changed
OldOptions.Assign(Self);
Clear;
debugln(['TFPCTargetConfigCache.Update ',Compiler,' TargetOS=',TargetOS,' TargetCPU=',TargetCPU,' CompilerOptions=',CompilerOptions,' ExtraOptions=',ExtraOptions,' PATH=',GetEnvironmentVariableUTF8('PATH')]);
CompilerDate:=FileAgeCached(Compiler);
if FileExistsCached(Compiler) then begin
if CompilerOptions<>'' then
ExtraOptions:=CompilerOptions+' '+ExtraOptions;
if TargetOS<>'' then
ExtraOptions:='-T'+LowerCase(TargetOS)+' '+ExtraOptions;
if TargetCPU<>'' then
ExtraOptions:='-P'+LowerCase(TargetCPU)+' '+ExtraOptions;
ExtraOptions:=Trim(ExtraOptions);
// get real OS and CPU
Info:=RunFPCInfo(Compiler,[fpciTargetOS,fpciTargetProcessor],ExtraOptions);
if ParseFPCInfo(Info,[fpciTargetOS,fpciTargetProcessor],Infos) then begin
RealTargetOS:=Infos[fpciTargetOS];
RealTargetCPU:=Infos[fpciTargetProcessor];
end else begin
RealTargetOS:=TargetOS;
if RealTargetOS='' then
RealTargetOS:=GetCompiledTargetOS;
RealTargetCPU:=TargetCPU;
if RealTargetCPU='' then
RealTargetCPU:=GetCompiledTargetCPU;
end;
RealCompilerInPath:=FindRealCompilerInPath(TargetCPU,true);
// run fpc and parse output
HasPPUs:=false;
RunFPCVerbose(Compiler,TestFilename,CfgFiles,RealCompiler,UnitPaths,
Defines,Undefines,ExtraOptions);
if UnitPaths<>nil then
for i:=0 to UnitPaths.Count-1 do
UnitPaths[i]:=ChompPathDelim(TrimFilename(UnitPaths[i]));
// store the real compiler file and date
if (RealCompiler<>'') and FileExistsCached(RealCompiler) then
RealCompilerDate:=FileAgeCached(RealCompiler);
// store the list of tried and read cfg files
if CfgFiles<>nil then begin
for i:=0 to CfgFiles.Count-1 do begin
Filename:=CfgFiles[i];
if Filename='' then continue;
CfgFileExists:=Filename[1]='+';
Filename:=copy(Filename,2,length(Filename));
CfgFileDate:=0;
if CfgFileExists then
CfgFileDate:=FileAgeCached(Filename);
ConfigFiles.Add(Filename,CfgFileExists,CfgFileDate);
end;
end;
// gather all units in all unit search paths
if (UnitPaths<>nil) and (UnitPaths.Count>0) then
Units:=GatherUnitsInSearchPaths(UnitPaths,OnProgress)
else begin
debugln(['TFPCTargetConfigCache.Update WARNING: no unit paths: ',Compiler,' ',ExtraOptions]);
Units:=TStringToStringTree.Create(false);
end;
// check if the system ppu exists
HasPPUs:=CompareFileExt(Units['system'],'ppu',false)=0;
end;
// check for changes
if not Equals(OldOptions) then begin
IncreaseChangeStamp;
debugln(['TFPCTargetConfigCache.Update: has changed']);
end;
Result:=true;
finally
CfgFiles.Free;
OldOptions.Free;
end;
end;
function TFPCTargetConfigCache.FindRealCompilerInPath(aTargetCPU: string;
ResolveLinks: boolean): string;
begin
if aTargetCPU='' then
aTargetCPU:=GetCompiledTargetCPU;
Result:=GetDefaultCompilerFilename(aTargetCPU);
if Result='' then exit;
Result:=SearchFileInPath(Result,GetCurrentDirUTF8,
GetEnvironmentVariableUTF8('PATH'),PathSeparator,ctsfcDefault);
if Result='' then exit;
if ResolveLinks then
Result:=TryReadAllLinks(Result);
end;
function TFPCTargetConfigCache.GetFPCVer(out FPCVersion, FPCRelease,
FPCPatch: integer): boolean;
var
v: string;
begin
v:={$I %FPCVERSION%};
Result:=SplitFPCVersion(v,FPCVersion,FPCRelease,FPCPatch);
if Defines<>nil then begin
FPCVersion:=StrToIntDef(Defines['FPC_VERSION'],FPCVersion);
FPCRelease:=StrToIntDef(Defines['FPC_RELEASE'],FPCRelease);
FPCPatch:=StrToIntDef(Defines['FPC_PATCH'],FPCPatch);
end;
end;
{ TFPCTargetConfigCaches }
constructor TFPCTargetConfigCaches.Create(AOwner: TComponent);
begin
FChangeStamp:=CTInvalidChangeStamp;
inherited Create(AOwner);
fItems:=TAVLTree.Create(@CompareFPCTargetConfigCacheItems);
end;
destructor TFPCTargetConfigCaches.Destroy;
begin
Clear;
FreeAndNil(fItems);
inherited Destroy;
end;
procedure TFPCTargetConfigCaches.Clear;
begin
if fItems.Count=0 then exit;
fItems.FreeAndClear;
IncreaseChangeStamp;
end;
function TFPCTargetConfigCaches.Equals(Caches: TFPCTargetConfigCaches): boolean;
var
Node1, Node2: TAVLTreeNode;
Item1: TFPCTargetConfigCache;
Item2: TFPCTargetConfigCache;
begin
Result:=false;
if Caches.fItems.Count<>fItems.Count then exit;
Node1:=fItems.FindLowest;
Node2:=Caches.fItems.FindLowest;
while Node1<>nil do begin
Item1:=TFPCTargetConfigCache(Node1.Data);
Item2:=TFPCTargetConfigCache(Node2.Data);
if not Item1.Equals(Item2) then exit;
Node1:=fItems.FindSuccessor(Node1);
Node2:=Caches.fItems.FindSuccessor(Node2);
end;
Result:=true;
end;
procedure TFPCTargetConfigCaches.Assign(Source: TPersistent);
var
Caches: TFPCTargetConfigCaches;
Node: TAVLTreeNode;
SrcItem: TFPCTargetConfigCache;
NewItem: TFPCTargetConfigCache;
begin
if Source is TFPCTargetConfigCaches then begin
Caches:=TFPCTargetConfigCaches(Source);
if Equals(Caches) then exit; // no change, keep ChangeStamp
Clear;
Node:=Caches.fItems.FindLowest;
while Node<>nil do begin
SrcItem:=TFPCTargetConfigCache(Node.Data);
NewItem:=TFPCTargetConfigCache.Create(Self);
NewItem.Assign(SrcItem);
fItems.Add(NewItem);
Node:=Caches.fItems.FindSuccessor(Node);
end;
IncreaseChangeStamp;
end else
inherited Assign(Source);
end;
procedure TFPCTargetConfigCaches.LoadFromXMLConfig(XMLConfig: TXMLConfig;
const Path: string);
var
Cnt: integer;
i: Integer;
Item: TFPCTargetConfigCache;
begin
Clear;
Cnt:=XMLConfig.GetValue(Path+'Count',0);
for i:=1 to Cnt do begin
Item:=TFPCTargetConfigCache.Create(Self);
Item.LoadFromXMLConfig(XMLConfig,Path+'Item'+IntToStr(i)+'/');
if (Item.Compiler<>'') then
fItems.Add(Item)
else
Item.Free;
end;
IncreaseChangeStamp;
end;
procedure TFPCTargetConfigCaches.SaveToXMLConfig(XMLConfig: TXMLConfig;
const Path: string);
var
Node: TAVLTreeNode;
Item: TFPCTargetConfigCache;
i: Integer;
begin
Node:=fItems.FindLowest;
i:=0;
while Node<>nil do begin
Item:=TFPCTargetConfigCache(Node.Data);
inc(i);
Item.SaveToXMLConfig(XMLConfig,Path+'Item'+IntToStr(i)+'/');
Node:=fItems.FindSuccessor(Node);
end;
XMLConfig.SetDeleteValue(Path+'Count',i,0);
end;
procedure TFPCTargetConfigCaches.LoadFromFile(Filename: string);
var
XMLConfig: TXMLConfig;
begin
XMLConfig:=TXMLConfig.Create(Filename);
try
LoadFromXMLConfig(XMLConfig,'FPCConfigs/');
finally
XMLConfig.Free;
end;
end;
procedure TFPCTargetConfigCaches.SaveToFile(Filename: string);
var
XMLConfig: TXMLConfig;
begin
XMLConfig:=TXMLConfig.CreateClean(Filename);
try
SaveToXMLConfig(XMLConfig,'FPCConfigs/');
finally
XMLConfig.Free;
end;
end;
procedure TFPCTargetConfigCaches.IncreaseChangeStamp;
begin
CTIncreaseChangeStamp(FChangeStamp);
end;
function TFPCTargetConfigCaches.Find(CompilerFilename, CompilerOptions,
TargetOS, TargetCPU: string; CreateIfNotExists: boolean
): TFPCTargetConfigCache;
var
Node: TAVLTreeNode;
Cmp: TFPCTargetConfigCache;
begin
Cmp:=TFPCTargetConfigCache.Create(Self);
try
Cmp.Compiler:=CompilerFilename;
Cmp.CompilerOptions:=CompilerOptions;
Cmp.TargetOS:=TargetOS;
Cmp.TargetCPU:=TargetCPU;
Node:=fItems.Find(cmp);
if Node<>nil then begin
Result:=TFPCTargetConfigCache(Node.Data);
end else if CreateIfNotExists then begin
Result:=cmp;
cmp:=nil;
fItems.Add(Result);
end else begin
Result:=nil;
end;
finally
Cmp.Free;
end;
end;
{ TFPCConfigFileStateList }
function TFPCConfigFileStateList.GetItems(Index: integer): TFPCConfigFileState;
begin
Result:=TFPCConfigFileState(fItems[Index]);
end;
constructor TFPCConfigFileStateList.Create;
begin
fItems:=TFPList.Create;
end;
destructor TFPCConfigFileStateList.Destroy;
begin
Clear;
FreeAndNil(fItems);
inherited Destroy;
end;
procedure TFPCConfigFileStateList.Clear;
var
i: Integer;
begin
for i:=0 to fItems.Count-1 do
TObject(fItems[i]).Free;
fItems.Clear;
end;
procedure TFPCConfigFileStateList.Assign(List: TFPCConfigFileStateList);
var
i: Integer;
Item: TFPCConfigFileState;
begin
Clear;
for i:=0 to List.Count-1 do begin
Item:=List[i];
Add(Item.Filename,Item.FileExists,Item.FileDate);
end;
end;
function TFPCConfigFileStateList.Equals(List: TFPCConfigFileStateList;
CheckDates: boolean): boolean;
var
i: Integer;
begin
Result:=false;
if Count<>List.Count then exit;
for i:=0 to Count-1 do
if not Items[i].Equals(List[i],CheckDates) then exit;
Result:=true;
end;
function TFPCConfigFileStateList.Add(aFilename: string; aFileExists: boolean;
aFileDate: longint): TFPCConfigFileState;
begin
Result:=TFPCConfigFileState.Create(aFilename,aFileExists,aFileDate);
fItems.Add(Result);
end;
function TFPCConfigFileStateList.Count: integer;
begin
Result:=fItems.Count;
end;
procedure TFPCConfigFileStateList.LoadFromXMLConfig(XMLConfig: TXMLConfig;
const Path: string);
var
Cnt: integer;
Item: TFPCConfigFileState;
i: Integer;
begin
Cnt:=XMLConfig.GetValue(Path+'Count',0);
for i:=1 to Cnt do begin
Item:=TFPCConfigFileState.Create('',false,0);
Item.LoadFromXMLConfig(XMLConfig,Path+'Item'+IntToStr(i)+'/');
fItems.Add(Item);
end;
end;
procedure TFPCConfigFileStateList.SaveToXMLConfig(XMLConfig: TXMLConfig;
const Path: string);
var
i: Integer;
begin
for i:=1 to Count do
Items[i-1].SaveToXMLConfig(XMLConfig,Path+'Item'+IntToStr(i)+'/');
XMLConfig.SetDeleteValue(Path+'Count',Count,0);
end;
{ TFPCConfigFileState }
constructor TFPCConfigFileState.Create(const aFilename: string;
aFileExists: boolean; aFileDate: longint);
begin
Filename:=aFilename;
FileExists:=aFileExists;
FileDate:=aFileDate;
end;
function TFPCConfigFileState.Equals(Other: TFPCConfigFileState;
CheckDate: boolean): boolean;
begin
Result:=false;
if (Filename<>Other.Filename) or (FileExists<>Other.FileExists) then exit;
if CheckDate and FileExists and (FileDate<>Other.FileDate) then exit;
Result:=true;
end;
procedure TFPCConfigFileState.LoadFromXMLConfig(XMLConfig: TXMLConfig;
const Path: string);
begin
Filename:=XMLConfig.GetValue(Path+'Filename','');
FileExists:=XMLConfig.GetValue(Path+'Exists',false);
FileDate:=XMLConfig.GetValue(Path+'Date',0);
end;
procedure TFPCConfigFileState.SaveToXMLConfig(XMLConfig: TXMLConfig;
const Path: string);
begin
XMLConfig.SetDeleteValue(Path+'Filename',Filename,'');
XMLConfig.SetDeleteValue(Path+'Exists',FileExists,false);
XMLConfig.SetDeleteValue(Path+'Date',FileDate,0);
end;
{ TFPCSourceCacheItem }
constructor TFPCSourceCache.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
Files:=TStringList.Create;
Valid:=false;
FChangeStamp:=CTInvalidChangeStamp;
if Owner is TFPCSourceCaches then
Caches:=TFPCSourceCaches(Owner);
end;
destructor TFPCSourceCache.Destroy;
begin
FreeAndNil(Files);
inherited Destroy;
end;
procedure TFPCSourceCache.Clear;
begin
FreeAndNil(Files);
Valid:=false;
end;
procedure TFPCSourceCache.Assign(Source: TPersistent);
var
Cache: TFPCSourceCache;
begin
if Source is TFPCSourceCache then begin
Cache:=TFPCSourceCache(Source);
Directory:=Cache.Directory;
Files.Assign(Cache.Files);
Valid:=Cache.Valid;
end else
inherited Assign(Source);
end;
function TFPCSourceCache.Equals(Cache: TFPCSourceCache): boolean;
begin
Result:=false;
if Valid<>Cache.Valid then exit;
if Directory<>Cache.Directory then exit;
if not Files.Equals(Cache.Files) then exit;
Result:=true;
end;
procedure TFPCSourceCache.LoadFromXMLConfig(XMLConfig: TXMLConfig;
const Path: string);
var
List: TStringList;
begin
Clear;
List:=nil;
try
Valid:=XMLConfig.GetValue(Path+'Valid',true);
Directory:=XMLConfig.GetValue(Path+'Directory','');
List:=TStringList.Create;
List.StrictDelimiter:=true;
List.Delimiter:=';';
List.DelimitedText:=XMLConfig.GetValue(Path+'Files','');
FreeAndNil(Files);
Files:=Decompress1FileList(List);
finally
if Files=nil then Files:=TStringList.Create;
List.Free;
end;
end;
procedure TFPCSourceCache.SaveToXMLConfig(XMLConfig: TXMLConfig;
const Path: string);
var
List: TStringList;
s: String;
begin
List:=nil;
try
XMLConfig.SetDeleteValue(Path+'Valid',Valid,true);
XMLConfig.SetDeleteValue(Path+'Directory',Directory,'');
if Files<>nil then begin
List:=Compress1FileList(Files);
List.StrictDelimiter:=true;
List.Delimiter:=';';
s:=List.DelimitedText;
end else
s:='';
XMLConfig.SetDeleteValue(Path+'Files',s,'');
finally
List.Free;
end;
end;
procedure TFPCSourceCache.LoadFromFile(Filename: string);
var
XMLConfig: TXMLConfig;
begin
XMLConfig:=TXMLConfig.Create(Filename);
try
LoadFromXMLConfig(XMLConfig,'FPCSourceDirectory/');
finally
XMLConfig.Free;
end;
end;
procedure TFPCSourceCache.SaveToFile(Filename: string);
var
XMLConfig: TXMLConfig;
begin
XMLConfig:=TXMLConfig.CreateClean(Filename);
try
SaveToXMLConfig(XMLConfig,'FPCSourceDirectory/');
finally
XMLConfig.Free;
end;
end;
procedure TFPCSourceCache.Update(const OnProgress: TDefinePoolProgress);
var
NewFiles: TStringList;
begin
Valid:=false;
NewFiles:=GatherFilesInFPCSources(Directory,OnProgress);
Update(NewFiles);
end;
procedure TFPCSourceCache.Update(var NewFiles: TStringList);
var
OldFiles: TStringList;
OldValid: Boolean;
begin
OldFiles:=Files;
OldValid:=Valid;
try
Files:=NewFiles;
NewFiles:=nil;
Valid:=true;
if (Valid<>OldValid)
or ((Files=nil)<>(OldFiles=nil))
or ((Files<>nil) and (Files.Text<>OldFiles.Text)) then begin
IncreaseChangeStamp;
debugln(['TFPCSourceCache.Update ',Directory,' has changed.']);
end;
finally
OldFiles.Free;
end;
end;
procedure TFPCSourceCache.IncreaseChangeStamp;
begin
CTIncreaseChangeStamp(FChangeStamp);
if Caches<>nil then
Caches.IncreaseChangeStamp;
end;
{ TFPCSourceCache }
constructor TFPCSourceCaches.Create(AOwner: TComponent);
begin
FChangeStamp:=CTInvalidChangeStamp;
inherited Create(AOwner);
fItems:=TAVLTree.Create(@CompareFPCSourceCacheItems);
end;
destructor TFPCSourceCaches.Destroy;
begin
Clear;
FreeAndNil(fItems);
inherited Destroy;
end;
procedure TFPCSourceCaches.Clear;
begin
if fItems.Count=0 then exit;
fItems.FreeAndClear;
IncreaseChangeStamp;
end;
procedure TFPCSourceCaches.Assign(Source: TPersistent);
var
Caches: TFPCSourceCaches;
SrcItem: TFPCSourceCache;
NewItem: TFPCSourceCache;
Node: TAVLTreeNode;
begin
if Source is TFPCSourceCaches then begin
Caches:=TFPCSourceCaches(Source);
if Equals(Caches) then exit; // keep ChangeStamp if equal
Clear;
Node:=Caches.fItems.FindLowest;
while Node<>nil do begin
SrcItem:=TFPCSourceCache(Node.Data);
NewItem:=TFPCSourceCache.Create(Self);
NewItem.Assign(SrcItem);
fItems.Add(NewItem);
Node:=Caches.fItems.FindSuccessor(Node);
end;
IncreaseChangeStamp;
end else
inherited Assign(Source);
end;
function TFPCSourceCaches.Equals(Caches: TFPCSourceCaches): boolean;
var
Node1, Node2: TAVLTreeNode;
Item1: TFPCSourceCache;
Item2: TFPCSourceCache;
begin
Result:=false;
if Caches.fItems.Count<>fItems.Count then exit;
Node1:=fItems.FindLowest;
Node2:=Caches.fItems.FindLowest;
while Node1<>nil do begin
Item1:=TFPCSourceCache(Node1.Data);
Item2:=TFPCSourceCache(Node2.Data);
if not Item1.Equals(Item2) then exit;
Node1:=fItems.FindSuccessor(Node1);
Node2:=Caches.fItems.FindSuccessor(Node2);
end;
Result:=true;
end;
procedure TFPCSourceCaches.LoadFromXMLConfig(XMLConfig: TXMLConfig;
const Path: string);
var
Cnt: integer;
i: Integer;
Item: TFPCSourceCache;
begin
Clear;
Cnt:=XMLConfig.GetValue(Path+'Count',0);
for i:=1 to Cnt do begin
Item:=TFPCSourceCache.Create(Self);
Item.LoadFromXMLConfig(XMLConfig,Path+'Item'+IntToStr(i)+'/');
if (Item.Directory='') or (fItems.Find(Item)<>nil) then
Item.Free
else
fItems.Add(Item);
end;
end;
procedure TFPCSourceCaches.SaveToXMLConfig(XMLConfig: TXMLConfig;
const Path: string);
var
Node: TAVLTreeNode;
Item: TFPCSourceCache;
Cnt: Integer;
begin
Cnt:=0;
Node:=fItems.FindLowest;
while Node<>nil do begin
Item:=TFPCSourceCache(Node.Data);
if Item.Directory<>'' then begin
inc(Cnt);
Item.SaveToXMLConfig(XMLConfig,Path+'Item'+IntToStr(Cnt)+'/');
end;
Node:=fItems.FindSuccessor(Node);
end;
XMLConfig.SetDeleteValue(Path+'Count',Cnt,0);
end;
procedure TFPCSourceCaches.LoadFromFile(Filename: string);
var
XMLConfig: TXMLConfig;
begin
XMLConfig:=TXMLConfig.Create(Filename);
try
LoadFromXMLConfig(XMLConfig,'FPCSourceDirectories/');
finally
XMLConfig.Free;
end;
end;
procedure TFPCSourceCaches.SaveToFile(Filename: string);
var
XMLConfig: TXMLConfig;
begin
XMLConfig:=TXMLConfig.CreateClean(Filename);
try
SaveToXMLConfig(XMLConfig,'FPCSourceDirectories/');
finally
XMLConfig.Free;
end;
end;
procedure TFPCSourceCaches.IncreaseChangeStamp;
begin
CTIncreaseChangeStamp(FChangeStamp);
end;
function TFPCSourceCaches.Find(Directory: string;
CreateIfNotExists: boolean): TFPCSourceCache;
var
Node: TAVLTreeNode;
begin
Directory:=ChompPathDelim(TrimFilename(Directory));
Node:=fItems.FindKey(PChar(Directory),@CompareDirectoryWithFPCSourceCacheItem);
if Node<>nil then begin
Result:=TFPCSourceCache(Node.Data);
end else if CreateIfNotExists then begin
Result:=TFPCSourceCache.Create(Self);
Result.Directory:=Directory;
fItems.Add(Result);
end else begin
Result:=nil;
end;
end;
{ TFPCDefinesCache }
procedure TFPCDefinesCache.SetConfigCaches(const AValue: TFPCTargetConfigCaches
);
begin
if FConfigCaches=AValue then exit;
FConfigCaches:=AValue;
FConfigCachesSaveStamp:=Low(FConfigCachesSaveStamp);
end;
procedure TFPCDefinesCache.SetSourceCaches(const AValue: TFPCSourceCaches);
begin
if FSourceCaches=AValue then exit;
FSourceCaches:=AValue;
FSourceCachesSaveStamp:=low(FSourceCachesSaveStamp);
end;
procedure TFPCDefinesCache.ClearUnitToSrcCaches;
var
i: Integer;
begin
for i:=0 to fUnitToSrcCaches.Count-1 do
TObject(fUnitToSrcCaches[i]).Free;
fUnitToSrcCaches.Clear;
end;
constructor TFPCDefinesCache.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
ConfigCaches:=TFPCTargetConfigCaches.Create(nil);
SourceCaches:=TFPCSourceCaches.Create(nil);
fUnitToSrcCaches:=TFPList.Create;
end;
destructor TFPCDefinesCache.Destroy;
begin
ClearUnitToSrcCaches;
FreeAndNil(FConfigCaches);
FreeAndNil(FSourceCaches);
FreeAndNil(fUnitToSrcCaches);
inherited Destroy;
end;
procedure TFPCDefinesCache.Clear;
begin
ClearUnitToSrcCaches;
if ConfigCaches<>nil then ConfigCaches.Clear;
if SourceCaches<>nil then SourceCaches.Clear;
end;
procedure TFPCDefinesCache.LoadFromXMLConfig(XMLConfig: TXMLConfig;
const Path: string);
begin
if ConfigCaches<>nil then begin
ConfigCaches.LoadFromXMLConfig(XMLConfig,Path+'FPCConfigs/');
FConfigCachesSaveStamp:=ConfigCaches.ChangeStamp;
end;
if SourceCaches<>nil then begin
SourceCaches.LoadFromXMLConfig(XMLConfig,Path+'FPCSources/');
FSourceCachesSaveStamp:=SourceCaches.ChangeStamp;
end;
end;
procedure TFPCDefinesCache.SaveToXMLConfig(XMLConfig: TXMLConfig;
const Path: string);
begin
//debugln(['TFPCDefinesCache.SaveToXMLConfig ']);
if ConfigCaches<>nil then begin
ConfigCaches.SaveToXMLConfig(XMLConfig,Path+'FPCConfigs/');
FConfigCachesSaveStamp:=ConfigCaches.ChangeStamp;
end;
if SourceCaches<>nil then begin
SourceCaches.SaveToXMLConfig(XMLConfig,Path+'FPCSources/');
FSourceCachesSaveStamp:=SourceCaches.ChangeStamp;
end;
end;
procedure TFPCDefinesCache.LoadFromFile(Filename: string);
var
XMLConfig: TXMLConfig;
begin
XMLConfig:=TXMLConfig.Create(Filename);
try
LoadFromXMLConfig(XMLConfig,'');
finally
XMLConfig.Free;
end;
end;
procedure TFPCDefinesCache.SaveToFile(Filename: string);
var
XMLConfig: TXMLConfig;
begin
XMLConfig:=TXMLConfig.CreateClean(Filename);
try
SaveToXMLConfig(XMLConfig,'');
finally
XMLConfig.Free;
end;
end;
function TFPCDefinesCache.NeedsSave: boolean;
begin
Result:=true;
if (ConfigCaches<>nil) and (ConfigCaches.ChangeStamp<>FConfigCachesSaveStamp)
then exit;
if (SourceCaches<>nil) and (SourceCaches.ChangeStamp<>FSourceCachesSaveStamp)
then exit;
Result:=false;
end;
function TFPCDefinesCache.FindUnitSet(const CompilerFilename, TargetOS,
TargetCPU, Options, FPCSrcDir: string; CreateIfNotExists: boolean
): TFPCUnitSetCache;
var
i: Integer;
begin
for i:=0 to fUnitToSrcCaches.Count-1 do begin
Result:=TFPCUnitSetCache(fUnitToSrcCaches[i]);
if (CompareFilenames(Result.CompilerFilename,CompilerFilename)=0)
and (SysUtils.CompareText(Result.TargetOS,TargetOS)=0)
and (SysUtils.CompareText(Result.TargetCPU,TargetCPU)=0)
and (CompareFilenames(Result.FPCSourceDirectory,FPCSrcDir)=0)
and (Result.CompilerOptions=Options)
then
exit;
end;
if CreateIfNotExists then begin
Result:=TFPCUnitSetCache.Create(Self);
Result.CompilerFilename:=CompilerFilename;
Result.CompilerOptions:=Options;
Result.TargetOS:=TargetOS;
Result.TargetCPU:=TargetCPU;
Result.FPCSourceDirectory:=FPCSrcDir;
fUnitToSrcCaches.Add(Result);
end else
Result:=nil;
end;
function TFPCDefinesCache.FindUnitSetWithID(const UnitSetID: string; out
Changed: boolean; CreateIfNotExists: boolean): TFPCUnitSetCache;
var
CompilerFilename, TargetOS, TargetCPU, Options, FPCSrcDir: string;
ChangeStamp: integer;
begin
ParseUnitSetID(UnitSetID,CompilerFilename, TargetOS, TargetCPU,
Options, FPCSrcDir, ChangeStamp);
//debugln(['TFPCDefinesCache.FindUnitToSrcCache UnitSetID="',dbgstr(UnitSetID),'" CompilerFilename="',CompilerFilename,'" TargetOS="',TargetOS,'" TargetCPU="',TargetCPU,'" Options="',Options,'" FPCSrcDir="',FPCSrcDir,'" ChangeStamp=',ChangeStamp,' exists=',FindUnitToSrcCache(CompilerFilename, TargetOS, TargetCPU,Options, FPCSrcDir,false)<>nil]);
Result:=FindUnitSet(CompilerFilename, TargetOS, TargetCPU,
Options, FPCSrcDir, false);
if Result<>nil then begin
Changed:=ChangeStamp<>Result.ChangeStamp;
end else if CreateIfNotExists then begin
Changed:=true;
Result:=FindUnitSet(CompilerFilename, TargetOS, TargetCPU,
Options, FPCSrcDir, true);
end else
Changed:=false;
end;
function TFPCDefinesCache.GetUnitSetID(CompilerFilename, TargetOS, TargetCPU,
Options, FPCSrcDir: string; ChangeStamp: integer): string;
begin
Result:='CompilerFilename='+CompilerFilename+LineEnding
+'TargetOS='+TargetOS+LineEnding
+'TargetCPU='+TargetCPU+LineEnding
+'Options='+Options+LineEnding
+'FPCSrcDir='+FPCSrcDir+LineEnding
+'Stamp='+IntToStr(ChangeStamp);
end;
procedure TFPCDefinesCache.ParseUnitSetID(const ID: string;
out CompilerFilename, TargetOS, TargetCPU, Options, FPCSrcDir: string;
out ChangeStamp: integer);
var
NameStartPos: PChar;
function NameFits(p: PChar): boolean;
var
p1: PChar;
begin
p1:=NameStartPos;
while (FPUpChars[p1^]=FPUpChars[p^]) and (p^<>#0) do begin
inc(p1);
inc(p);
end;
Result:=p1^='=';
end;
var
ValueStartPos: PChar;
ValueEndPos: PChar;
Value: String;
begin
CompilerFilename:='';
TargetCPU:='';
TargetOS:='';
Options:='';
FPCSrcDir:='';
ChangeStamp:=0;
if ID='' then exit;
// read the lines with name=value
NameStartPos:=PChar(ID);
while NameStartPos^<>#0 do begin
while (NameStartPos^ in [#10,#13]) do inc(NameStartPos);
ValueStartPos:=NameStartPos;
while not (ValueStartPos^ in ['=',#10,#13,#0]) do inc(ValueStartPos);
if ValueStartPos^<>'=' then exit;
inc(ValueStartPos);
ValueEndPos:=ValueStartPos;
while not (ValueEndPos^ in [#10,#13,#0]) do inc(ValueEndPos);
Value:=copy(ID,ValueStartPos-PChar(ID)+1,ValueEndPos-ValueStartPos);
//debugln(['TFPCDefinesCache.ParseUnitSetID Name=',copy(ID,NameStartPos-PChar(ID)+1,ValueStartPos-NameStartPos-1),' Value="',Value,'"']);
case NameStartPos^ of
'c','C':
if NameFits('CompilerFilename') then
CompilerFilename:=Value;
'f','F':
if NameFits('FPCSrcDir') then
FPCSrcDir:=Value;
'o','O':
if NameFits('Options') then
Options:=Value;
's','S':
if NameFits('Stamp') then
ChangeStamp:=StrToIntDef(Value,0);
't','T':
if NameFits('TargetOS') then
TargetOS:=Value
else if NameFits('TargetCPU') then
TargetCPU:=Value;
end;
NameStartPos:=ValueEndPos;
end;
end;
{ TFPCUnitSetCache }
procedure TFPCUnitSetCache.SetCompilerFilename(const AValue: string);
var
NewFilename: String;
begin
NewFilename:=CleanAndExpandFilename(AValue);
if FCompilerFilename=NewFilename then exit;
FCompilerFilename:=NewFilename;
ClearConfigCache;
end;
procedure TFPCUnitSetCache.SetCompilerOptions(const AValue: string);
begin
if FCompilerOptions=AValue then exit;
FCompilerOptions:=AValue;
ClearConfigCache;
end;
procedure TFPCUnitSetCache.SetFPCSourceDirectory(const AValue: string);
var
NewValue: String;
begin
NewValue:=CleanAndExpandDirectory(AValue);
if FFPCSourceDirectory=NewValue then exit;
FFPCSourceDirectory:=NewValue;
ClearSourceCache;
end;
procedure TFPCUnitSetCache.SetTargetCPU(const AValue: string);
begin
if FTargetCPU=AValue then exit;
FTargetCPU:=AValue;
ClearConfigCache;
end;
procedure TFPCUnitSetCache.SetTargetOS(const AValue: string);
begin
if FTargetOS=AValue then exit;
FTargetOS:=AValue;
ClearConfigCache;
end;
procedure TFPCUnitSetCache.ClearConfigCache;
begin
FConfigCache:=nil;
fFlags:=fFlags+[fuscfUnitTreeNeedsUpdate,fuscfSrcRulesNeedUpdate];
end;
procedure TFPCUnitSetCache.ClearSourceCache;
begin
fSourceCache:=nil;
Include(fFlags,fuscfUnitTreeNeedsUpdate);
end;
procedure TFPCUnitSetCache.Notification(AComponent: TComponent;
Operation: TOperation);
begin
inherited Notification(AComponent, Operation);
if Operation=opRemove then begin
if FConfigCache=AComponent then
ClearConfigCache;
if fSourceCache=AComponent then
ClearSourceCache;
end;
end;
constructor TFPCUnitSetCache.Create(TheOwner: TComponent);
begin
inherited Create(TheOwner);
FChangeStamp:=CTInvalidChangeStamp;
FCaches:=TheOwner as TFPCDefinesCache;
fUnitToSourceTree:=TStringToStringTree.Create(false);
fSrcDuplicates:=TStringToStringTree.Create(false);
fSourceRules:=TFPCSourceRules.Create;
fFlags:=[fuscfUnitTreeNeedsUpdate,fuscfSrcRulesNeedUpdate];
end;
destructor TFPCUnitSetCache.Destroy;
begin
FreeAndNil(fSourceRules);
FreeAndNil(fUnitToSourceTree);
FreeAndNil(fSrcDuplicates);
inherited Destroy;
end;
procedure TFPCUnitSetCache.Clear;
begin
end;
procedure TFPCUnitSetCache.Init;
begin
GetUnitToSourceTree(True);
end;
function TFPCUnitSetCache.GetConfigCache(AutoUpdate: boolean
): TFPCTargetConfigCache;
begin
if CompilerFilename='' then
raise Exception.Create('TFPCUnitToSrcCache.GetConfigCache missing CompilerFilename');
if Caches.TestFilename='' then
raise Exception.Create('TFPCUnitToSrcCache.GetConfigCache missing TestFilename');
if FConfigCache=nil then begin
FConfigCache:=Caches.ConfigCaches.Find(CompilerFilename,CompilerOptions,
TargetOS,TargetCPU,true);
FConfigCache.FreeNotification(Self);
end;
if AutoUpdate and FConfigCache.NeedsUpdate then
FConfigCache.Update(Caches.TestFilename,Caches.ExtraOptions);
Result:=FConfigCache;
end;
function TFPCUnitSetCache.GetSourceCache(AutoUpdate: boolean
): TFPCSourceCache;
begin
if FPCSourceDirectory='' then
raise Exception.Create('TFPCUnitToSrcCache.GetSourceCache missing FPCSourceDirectory');
if fSourceCache=nil then begin
fSourceCache:=Caches.SourceCaches.Find(FPCSourceDirectory,true);
fSourceCache.FreeNotification(Self);
end;
if AutoUpdate and (not fSourceCache.Valid) then
fSourceCache.Update(nil);
Result:=fSourceCache;
end;
function TFPCUnitSetCache.GetSourceRules(AutoUpdate: boolean
): TFPCSourceRules;
var
Cfg: TFPCTargetConfigCache;
NewRules: TFPCSourceRules;
begin
Cfg:=GetConfigCache(AutoUpdate);
if (fuscfSrcRulesNeedUpdate in fFlags)
or (fRulesStampOfConfig<>Cfg.ChangeStamp) then begin
Exclude(fFlags,fuscfSrcRulesNeedUpdate);
NewRules:=DefaultFPCSourceRules.Clone;
try
if Cfg.Units<>nil then
AdjustFPCSrcRulesForPPUPaths(Cfg.Units,NewRules);
fSourceRules.Assign(NewRules); // increases ChangeStamp if something changed
fRulesStampOfConfig:=Cfg.ChangeStamp;
finally
NewRules.Free;
end;
end;
Result:=fSourceRules;
end;
function TFPCUnitSetCache.GetUnitToSourceTree(AutoUpdate: boolean
): TStringToStringTree;
var
Src: TFPCSourceCache;
SrcRules: TFPCSourceRules;
NewUnitToSourceTree: TStringToStringTree;
NewSrcDuplicates: TStringToStringTree;
ConfigCache: TFPCTargetConfigCache;
begin
Src:=GetSourceCache(AutoUpdate);
SrcRules:=GetSourceRules(AutoUpdate);
if (fuscfUnitTreeNeedsUpdate in fFlags)
or (fUnitStampOfFiles<>Src.ChangeStamp)
or (fUnitStampOfRules<>SrcRules.ChangeStamp) then begin
Exclude(fFlags,fuscfUnitTreeNeedsUpdate);
ConfigCache:=GetConfigCache(false);
NewSrcDuplicates:=nil;
NewUnitToSourceTree:=nil;
try
NewSrcDuplicates:=TStringToStringTree.Create(false);
NewUnitToSourceTree:=GatherUnitsInFPCSources(Src.Files,
ConfigCache.RealTargetOS,ConfigCache.RealTargetCPU,
NewSrcDuplicates,SrcRules);
if NewUnitToSourceTree=nil then
NewUnitToSourceTree:=TStringToStringTree.Create(false);
// ToDo: add/replace sources in PPU search paths
if not fUnitToSourceTree.Equals(NewUnitToSourceTree) then begin
fUnitToSourceTree.Assign(NewUnitToSourceTree);
IncreaseChangeStamp;
end;
if not fSrcDuplicates.Equals(NewSrcDuplicates) then begin
fSrcDuplicates.Assign(NewSrcDuplicates);
IncreaseChangeStamp;
end;
fUnitStampOfFiles:=Src.ChangeStamp;
fUnitStampOfRules:=SrcRules.ChangeStamp;
finally
NewUnitToSourceTree.Free;
NewSrcDuplicates.Free;
end;
end;
Result:=fUnitToSourceTree;
end;
function TFPCUnitSetCache.GetSourceDuplicates(AutoUpdate: boolean
): TStringToStringTree;
begin
GetUnitToSourceTree(AutoUpdate);
Result:=fSrcDuplicates;
end;
function TFPCUnitSetCache.GetUnitSrcFile(const AUnitName: string;
MustHavePPU: boolean; SkipPPUCheckIfNoneExists: boolean): string;
var
Tree: TStringToStringTree;
ConfigCache: TFPCTargetConfigCache;
begin
Result:='';
Tree:=GetUnitToSourceTree(false);
if MustHavePPU then begin
ConfigCache:=GetConfigCache(false);
if (ConfigCache.Units<>nil)
and (CompareFileExt(ConfigCache.Units[AUnitName],'ppu',false)<>0)
then begin
// unit has no ppu in the FPC ppu search path
if ConfigCache.HasPPUs then begin
// but there are other ppu files
exit;
end else begin
// no ppu exists at all
// => the fpc is not installed properly for this target
if not SkipPPUCheckIfNoneExists then
exit;
// => search directly in the sources
// this allows cross editing even if FPC is not installed for this target
end;
end;
end;
if Tree<>nil then begin
Result:=Tree[AUnitName];
if Result<>'' then
Result:=FPCSourceDirectory+Result;
end;
end;
class function TFPCUnitSetCache.GetInvalidChangeStamp: integer;
begin
Result:=CTInvalidChangeStamp;
end;
procedure TFPCUnitSetCache.IncreaseChangeStamp;
begin
CTIncreaseChangeStamp(FChangeStamp);
end;
function TFPCUnitSetCache.GetUnitSetID: string;
begin
Result:=Caches.GetUnitSetID(CompilerFilename,TargetOS,TargetCPU,
CompilerOptions,FPCSourceDirectory,ChangeStamp);
end;
initialization
InitDefaultFPCSourceRules;
finalization
FreeAndNil(DefaultFPCSourceRules);
end.