mirror of
https://gitlab.com/freepascal.org/lazarus/lazarus.git
synced 2025-04-05 15:37:50 +02:00
3262 lines
119 KiB
ObjectPascal
3262 lines
119 KiB
ObjectPascal
{
|
|
/***************************************************************************
|
|
buildmanager.pas
|
|
----------------
|
|
|
|
|
|
***************************************************************************/
|
|
|
|
***************************************************************************
|
|
* *
|
|
* This source is free software; you can redistribute it and/or modify *
|
|
* it under the terms of the GNU General Public License as published by *
|
|
* the Free Software Foundation; either version 2 of the License, or *
|
|
* (at your option) any later version. *
|
|
* *
|
|
* This code is distributed in the hope that it will be useful, but *
|
|
* WITHOUT ANY WARRANTY; without even the implied warranty of *
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
|
|
* General Public License for more details. *
|
|
* *
|
|
* A copy of the GNU General Public License is available on the World *
|
|
* Wide Web at <http://www.gnu.org/copyleft/gpl.html>. You can also *
|
|
* obtain it by writing to the Free Software Foundation, *
|
|
* Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1335, USA. *
|
|
* *
|
|
***************************************************************************
|
|
}
|
|
unit BuildManager;
|
|
|
|
{$mode objfpc}{$H+}
|
|
|
|
{off $DEFINE VerboseFPCSrcScan}
|
|
|
|
interface
|
|
|
|
uses
|
|
// RTL + FCL
|
|
Classes, SysUtils, Types, AVL_Tree, System.UITypes, StrUtils,
|
|
// LCL
|
|
InterfaceBase, LCLPlatformDef,
|
|
// CodeTools
|
|
ExprEval, BasicCodeTools, CodeToolManager, DefineTemplates, CodeCache,
|
|
FileProcs, CodeToolsCfgScript, LinkScanner,
|
|
// LazUtils
|
|
FPCAdds, LConvEncoding, FileUtil, LazFileUtils, LazFileCache, LazUTF8,
|
|
Laz2_XMLCfg, LazUtilities, LazStringUtils, LazMethodList, LazVersion,
|
|
AvgLvlTree,
|
|
// BuildIntf
|
|
BaseIDEIntf, IDEOptionsIntf, ProjectIntf, MacroIntf, PublishModuleIntf,
|
|
IDEExternToolIntf, CompOptsIntf, MacroDefIntf,
|
|
// IDEIntf
|
|
IDEDialogs, LazIDEIntf, IDEMsgIntf, SrcEditorIntf, InputHistory,
|
|
// IdeUtils
|
|
IdeUtilsPkgStrConsts,
|
|
// IdeConfig
|
|
LazConf, EnvironmentOpts, ModeMatrixOpts, TransferMacros, IdeConfStrConsts,
|
|
IDEProcs, etMakeMsgParser, etFPCMsgFilePool, ParsedCompilerOpts, CompilerOptions,
|
|
EditDefineTree,
|
|
// IdePackager
|
|
IdePackagerStrConsts,
|
|
// IDE
|
|
LazarusIDEStrConsts, DialogProcs, ProjectResources,
|
|
MiscOptions, ExtTools, etFPCMsgParser, etPas2jsMsgParser, Compiler,
|
|
FPCSrcScan, PackageDefs, PackageSystem, Project, ProjectIcon, BaseBuildManager,
|
|
ApplicationBundle, RunParamsOpts, IdeTransferMacros, SearchPathProcs, RunParamOptions;
|
|
|
|
const
|
|
cInvalidCompiler = 'InvalidCompiler';
|
|
|
|
type
|
|
|
|
{ TBuildManager }
|
|
|
|
TBuildManager = class(TBaseBuildManager)
|
|
private
|
|
FBuildTarget: TProject;
|
|
FUnitSetCache: TFPCUnitSetCache;
|
|
fBuildLazExtraOptions: string; // last build lazarus extra options
|
|
FUnitSetChangeStamp: integer;
|
|
FFPCSrcScans: TFPCSrcScans;
|
|
FProjectNameSpace: string;
|
|
FProjectNameSpaceCode: TCodeBuffer;
|
|
FProjectNameSpaceCodeChgStep: integer;
|
|
// Macro FPCVer
|
|
FFPCVer: string;
|
|
FFPC_FULLVERSION: integer;
|
|
FFPCVerChangeStamp: integer;
|
|
// Macro InstantFPCCache
|
|
FMacroInstantFPCCache: string;
|
|
FMacroInstantFPCCacheValid: boolean;
|
|
// current target
|
|
fTargetOS: string;
|
|
fTargetCPU: string;
|
|
fSubtarget: string;
|
|
fLCLWidgetType: string;
|
|
// cache
|
|
FFPCompilerFilename: string;
|
|
FFPCompilerFilenameStamp: Integer;
|
|
fEnv: TStringDynArray;
|
|
procedure DoOnRescanFPCDirectoryCache(Sender: TObject);
|
|
function GetTargetFilename: String;
|
|
procedure MacroSubstitution(TheMacro: TTransferMacro;
|
|
const MacroName: string; var s: string;
|
|
const {%H-}Data: PtrInt; var Handled, {%H-}Abort: boolean;
|
|
{%H-}Depth: integer);
|
|
function SubstituteCompilerOption({%H-}Options: TParsedCompilerOptions;
|
|
const UnparsedValue: string;
|
|
PlatformIndependent: boolean): string;
|
|
function MacroFuncBuildMode(const {%H-}Param: string; const {%H-}Data: PtrInt;
|
|
var {%H-}Abort: boolean): string;
|
|
function MacroFuncBuildModeCaption(const {%H-}Param: string; const {%H-}Data: PtrInt;
|
|
var {%H-}Abort: boolean): string;
|
|
function MacroFuncEnv(const Param: string; const {%H-}Data: PtrInt;
|
|
var {%H-}Abort: boolean): string;
|
|
function MacroFuncCompPath(const {%H-}s:string; const {%H-}Data: PtrInt;
|
|
var {%H-}Abort: boolean): string;
|
|
function MacroFuncFPCMsgFile(const {%H-}Param: string; const {%H-}Data: PtrInt;
|
|
var {%H-}Abort: boolean): string;
|
|
function MacroFuncFPCTarget(const {%H-}Param: string; const Data: PtrInt;
|
|
var {%H-}Abort: boolean): string;
|
|
function MacroFuncFPCVer(const {%H-}Param: string; const {%H-}Data: PtrInt;
|
|
var {%H-}Abort: boolean): string;
|
|
function MacroFuncFPC_FULLVERSION(const {%H-}Param: string; const {%H-}Data: PtrInt;
|
|
var {%H-}Abort: boolean): string;
|
|
function MacroFuncLCLWidgetType(const {%H-}Param: string; const Data: PtrInt;
|
|
var {%H-}Abort: boolean): string;
|
|
function MacroFuncLazVer(const {%H-}Param: string; const {%H-}Data: PtrInt;
|
|
var {%H-}Abort: boolean): string;
|
|
function MacroFuncMake(const {%H-}Param: string; const {%H-}Data: PtrInt;
|
|
var {%H-}Abort: boolean): string;// make utility
|
|
function MacroFuncMakeExe(const Filename: string; const {%H-}Data: PtrInt;
|
|
var {%H-}Abort: boolean): string;
|
|
function MacroFuncMakeLib(const Filename: string; const {%H-}Data: PtrInt;
|
|
var {%H-}Abort: boolean): string;
|
|
function MacroFuncInstantFPCCache(const {%H-}Param: string; const {%H-}Data: PtrInt;
|
|
var {%H-}Abort: boolean): string;// path of the instantfpc cache
|
|
function MacroFuncParams(const {%H-}Param: string; const {%H-}Data: PtrInt;
|
|
var {%H-}Abort: boolean): string;
|
|
function MacroFuncProject(const Param: string; const {%H-}Data: PtrInt;
|
|
var {%H-}Abort: boolean): string;
|
|
function MacroFuncProjFile(const {%H-}Param: string; const {%H-}Data: PtrInt;
|
|
var {%H-}Abort: boolean): string;
|
|
function MacroFuncProjIncPath(const {%H-}Param: string; const {%H-}Data: PtrInt;
|
|
var {%H-}Abort: boolean): string;
|
|
function MacroFuncProjNamespaces(const {%H-}Param: string; const {%H-}Data: PtrInt;
|
|
var {%H-}Abort: boolean): string;
|
|
function MacroFuncProjOutDir(const {%H-}Param: string; const {%H-}Data: PtrInt;
|
|
var {%H-}Abort: boolean): string;
|
|
function MacroFuncProjPath(const {%H-}Param: string; const {%H-}Data: PtrInt;
|
|
var {%H-}Abort: boolean): string;
|
|
function MacroFuncProjPublishDir(const {%H-}Param: string; const {%H-}Data: PtrInt;
|
|
var {%H-}Abort: boolean): string;
|
|
function MacroFuncProjSrcPath(const {%H-}Param: string; const {%H-}Data: PtrInt;
|
|
var {%H-}Abort: boolean): string;
|
|
function MacroFuncProjUnitPath(const {%H-}Param: string; const {%H-}Data: PtrInt;
|
|
var {%H-}Abort: boolean): string;
|
|
function MacroFuncProjVer(const {%H-}Param: string; const {%H-}Data: PtrInt;
|
|
var {%H-}Abort: boolean): string;
|
|
function MacroFuncRunCmdLine(const {%H-}Param: string; const {%H-}Data: PtrInt;
|
|
var {%H-}Abort: boolean): string;
|
|
function MacroFuncSrcOS(const {%H-}Param: string; const Data: PtrInt;
|
|
var {%H-}Abort: boolean): string;
|
|
function MacroFuncSubtarget(const {%H-}Param: string; const Data: PtrInt;
|
|
var {%H-}Abort: boolean): string;
|
|
function MacroFuncTargetCmdLine(const {%H-}Param: string; const {%H-}Data: PtrInt;
|
|
var {%H-}Abort: boolean): string;
|
|
function MacroFuncTargetCPU(const {%H-}Param: string; const Data: PtrInt;
|
|
var {%H-}Abort: boolean): string;
|
|
function MacroFuncTargetFile(const {%H-}Param: string; const {%H-}Data: PtrInt;
|
|
var {%H-}Abort: boolean): string;
|
|
function MacroFuncTargetOS(const {%H-}Param: string; const Data: PtrInt;
|
|
var {%H-}Abort: boolean): string;
|
|
function MacroFuncOutputFile(const {%H-}Param: string; const {%H-}Data: PtrInt;
|
|
var {%H-}Abort: boolean): string;
|
|
function MacroFuncIDEBuildOptions(const {%H-}Param: string; const Data: PtrInt;
|
|
var {%H-}Abort: boolean): string;
|
|
function MacroFuncPrimaryConfigPath(const {%H-}Param: string; const {%H-}Data: PtrInt;
|
|
var {%H-}Abort: boolean): string;
|
|
function MacroFuncSecondaryConfigPath(const {%H-}Param: string; const {%H-}Data: PtrInt;
|
|
var {%H-}Abort: boolean): string;
|
|
function MacroFuncFallbackOutputRoot(const {%H-}Param: string; const {%H-}Data: PtrInt;
|
|
var {%H-}Abort: boolean): string;
|
|
|
|
function CTMacroFuncProjectNamespaces(Data: Pointer): boolean;
|
|
function CTMacroFuncProjectUnitPath(Data: Pointer): boolean;
|
|
function CTMacroFuncProjectIncPath(Data: Pointer): boolean;
|
|
function CTMacroFuncProjectSrcPath(Data: Pointer): boolean;
|
|
procedure OnProjectDestroy(Sender: TObject);
|
|
procedure SetUnitSetCache(const AValue: TFPCUnitSetCache);
|
|
function GetProjectDefaultNamespace: string; // read .lpr file
|
|
protected
|
|
// command line overrides
|
|
OverrideTargetOS: string;
|
|
OverrideTargetCPU: string;
|
|
OverrideSubtarget: string;
|
|
OverrideLCLWidgetType: string;
|
|
DefaultCfgVars: TCTCfgScriptVariables;
|
|
DefaultCfgVarsBuildMacroStamp: integer;
|
|
procedure Notification(AComponent: TComponent; Operation: TOperation);
|
|
override;
|
|
function GetBuildMacroValuesHandler(Options: TLazCompilerOptions;
|
|
IncludeSelf: boolean): TCTCfgScriptVariables;
|
|
function GetActiveBuildModeName: string;
|
|
procedure AppendMatrixCustomOption(Sender: TObject;
|
|
var Options: string; Types: TBuildMatrixGroupTypes);
|
|
procedure GetMatrixOutputDirectoryOverride(Sender: TObject;
|
|
var OutDir: string; Types: TBuildMatrixGroupTypes);
|
|
function GetModeMatrixTarget(Sender: TObject): string;
|
|
function EnvironmentOptionsIsGlobalMode(const Identifier: string): boolean;
|
|
public
|
|
constructor Create(AOwner: TComponent); override;
|
|
destructor Destroy; override;
|
|
procedure SetupTransferMacros;
|
|
procedure TranslateMacros;
|
|
procedure SetupExternalTools(aToolsClass: TExternalToolsClass);
|
|
procedure SetupCompilerInterface;
|
|
procedure SetupInputHistories(aInputHist: TInputHistories);
|
|
procedure EnvOptsChanged;
|
|
|
|
function GetBuildMacroOverride(const MacroName: string): string; override;
|
|
function GetBuildMacroOverrides: TStrings; override;
|
|
function GetTargetOS: string; override;
|
|
function GetTargetCPU: string; override;
|
|
function GetSubtarget: string; override;
|
|
function GetLCLWidgetType: string; override;
|
|
function GetRunCommandLine: string; override;
|
|
function GetRunWorkingDir: string; override;
|
|
procedure WriteDebug_RunCommandLine; override;
|
|
|
|
function GetCompilerFilename: string; override;
|
|
function GetFPCompilerFilename: string; override;
|
|
function GetFPCFrontEndOptions: string; override;
|
|
function GetProjectPublishDir: string; override;
|
|
function GetProjectTargetFilename(aProject: TProject): string; override;
|
|
function GetProjectUsesAppBundle: Boolean; override;
|
|
function GetTestUnitFilename(AnUnitInfo: TUnitInfo): string; override;
|
|
function GetTestBuildDirectory: string; override;
|
|
function IsTestUnitFilename(const AFilename: string): boolean; override;
|
|
function GetTargetUnitFilename(AnUnitInfo: TUnitInfo): string; override;
|
|
|
|
procedure UpdateEnglishErrorMsgFilename;
|
|
procedure RescanCompilerDefines(ResetBuildTarget, ClearCaches,
|
|
WaitTillDone, Quiet: boolean); override;
|
|
function CompilerOnDiskChanged: boolean; override;
|
|
procedure LoadCompilerDefinesCaches;
|
|
procedure SaveCompilerDefinesCaches;
|
|
property UnitSetCache: TFPCUnitSetCache read FUnitSetCache write SetUnitSetCache;
|
|
|
|
function DoCheckIfProjectNeedsCompilation(AProject: TProject;
|
|
out NeedBuildAllFlag: boolean; var Note: string): TModalResult;
|
|
function CheckAmbiguousSources(const AFilename: string;
|
|
Compiling: boolean): TModalResult; override;
|
|
function DeleteAmbiguousFiles(const Filename:string
|
|
): TModalResult; override;
|
|
function CheckUnitPathForAmbiguousPascalFiles(const BaseDir, TheUnitPath,
|
|
CompiledExt, ContextDescription: string
|
|
): TModalResult; override;
|
|
function CreateProjectApplicationBundle: Boolean; override;
|
|
function BackupFileForWrite(const Filename: string): TModalResult; override;
|
|
|
|
function GetResourceType(AnUnitInfo: TUnitInfo): TResourceType;
|
|
function FindLRSFilename(AnUnitInfo: TUnitInfo;
|
|
UseDefaultIfNotFound: boolean): string;
|
|
function GetDefaultLRSFilename(AnUnitInfo: TUnitInfo): string;
|
|
function UpdateLRSFromLFM(AnUnitInfo: TUnitInfo; ShowAbort: boolean): TModalResult;
|
|
function UpdateProjectAutomaticFiles(TestDir: string): TModalResult; override;
|
|
|
|
// methods for building IDE (will be changed when project groups are there)
|
|
procedure SetBuildTarget(const TargetOS, TargetCPU, Subtarget, LCLWidgetType: string;
|
|
ScanFPCSrc: TScanModeFPCSources; Quiet: boolean);
|
|
procedure SetBuildTargetProject1; override; overload;
|
|
procedure SetBuildTargetProject1(Quiet: boolean; ScanFPCSrc: TScanModeFPCSources = smsfsBackground); overload;
|
|
procedure SetBuildTargetIDE(aQuiet: boolean = false); override;
|
|
function BuildTargetIDEIsDefault: boolean; override;
|
|
|
|
property FPCSrcScans: TFPCSrcScans read FFPCSrcScans;
|
|
property BuildTarget: TProject read FBuildTarget; // TProject or nil
|
|
end;
|
|
|
|
var
|
|
MainBuildBoss: TBuildManager = nil;
|
|
TheCompiler: TCompiler = nil;
|
|
|
|
procedure RunBootHandlers(ht: TLazarusIDEBootHandlerType);external name 'ideintf_LazIDEIntf_RunBootHandlers';
|
|
|
|
implementation
|
|
|
|
type
|
|
TUnitFile = record
|
|
FileUnitName: string;
|
|
Filename: string;
|
|
end;
|
|
PUnitFile = ^TUnitFile;
|
|
|
|
procedure BMLazConfMacroFunction(var s: string);
|
|
begin
|
|
if not GlobalMacroList.SubstituteStr(s) then
|
|
debugln(['BMLazConfMacroFunction failed "',s,'"']);
|
|
end;
|
|
|
|
function CompareUnitFiles(UnitFile1, UnitFile2: PUnitFile): integer;
|
|
begin
|
|
Result:=CompareIdentifiers(PChar(UnitFile1^.FileUnitName),
|
|
PChar(UnitFile2^.FileUnitName));
|
|
end;
|
|
|
|
function CompareUnitNameAndUnitFile(UnitName: PChar; UnitFile: PUnitFile): integer;
|
|
begin
|
|
Result:=CompareIdentifiers(PChar(UnitName),PChar(UnitFile^.FileUnitName));
|
|
end;
|
|
|
|
procedure OnCompilerParseStampIncreased;
|
|
begin
|
|
CodeToolBoss.DefineTree.ClearCache;
|
|
end;
|
|
|
|
{ TBuildManager }
|
|
|
|
procedure TBuildManager.OnProjectDestroy(Sender: TObject);
|
|
var
|
|
aProject: TProject;
|
|
begin
|
|
if not (Sender is TProjectIDEOptions) then
|
|
exit;
|
|
aProject:=TProjectIDEOptions(Sender).Project;
|
|
if FBuildTarget=aProject then
|
|
FBuildTarget:=nil;
|
|
end;
|
|
|
|
procedure TBuildManager.MacroSubstitution(TheMacro: TTransferMacro;
|
|
const MacroName: string; var s: string; const Data: PtrInt; var Handled,
|
|
Abort: boolean; Depth: integer);
|
|
var
|
|
VarCnt, i: Integer;
|
|
EnvStr, UpperMacroName: String;
|
|
p: SizeInt;
|
|
begin
|
|
if TheMacro=nil then begin
|
|
if s='' then begin
|
|
// default: use uppercase environment variable
|
|
VarCnt:=GetEnvironmentVariableCountUTF8;
|
|
if length(fEnv)<>VarCnt then begin
|
|
SetLength(fEnv,VarCnt);
|
|
for i:=0 to VarCnt-1 do
|
|
fEnv[i]:=GetEnvironmentStringUTF8(i+1);
|
|
end;
|
|
UpperMacroName:=UTF8UpperCase(MacroName);
|
|
for i:=0 to VarCnt-1 do begin
|
|
EnvStr:=fEnv[i];
|
|
p:=Pos('=',EnvStr);
|
|
if p<2 then continue;
|
|
{$IFDEF Windows}
|
|
if UTF8CompareText(UpperMacroName,LeftStr(EnvStr,p-1))=0 then
|
|
{$ELSE}
|
|
if (p-1=length(UpperMacroName)) and CompareMem(@UpperMacroName[1],@EnvStr[1],p-1) then
|
|
{$ENDIF}
|
|
begin
|
|
Handled:=true;
|
|
s:=copy(EnvStr,p+1,length(EnvStr));
|
|
exit;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
if ConsoleVerbosity>=0 then
|
|
DebugLn('Warning: (lazarus) Macro not defined: "'+MacroName+'".');
|
|
{$IFDEF VerboseMacroNotDefined}
|
|
DumpStack;
|
|
{$ENDIF}
|
|
s:='';
|
|
//IDEMessageDialog('Unknown Macro','Macro not defined: "'+s+'".',mtError,[mbAbort],0);
|
|
Handled:=false;
|
|
exit;
|
|
end;
|
|
end;
|
|
|
|
function TBuildManager.SubstituteCompilerOption(
|
|
Options: TParsedCompilerOptions; const UnparsedValue: string;
|
|
PlatformIndependent: boolean): string;
|
|
begin
|
|
Result:=UnparsedValue;
|
|
if PlatformIndependent then
|
|
GlobalMacroList.SubstituteStr(Result,CompilerOptionMacroPlatformIndependent)
|
|
else
|
|
GlobalMacroList.SubstituteStr(Result,CompilerOptionMacroNormal);
|
|
end;
|
|
|
|
function TBuildManager.MacroFuncBuildMode(const Param: string;
|
|
const Data: PtrInt; var Abort: boolean): string;
|
|
begin
|
|
Result:=GetActiveBuildModeName;
|
|
end;
|
|
|
|
function TBuildManager.MacroFuncBuildModeCaption(const Param: string; const Data: PtrInt;
|
|
var Abort: boolean): string;
|
|
begin
|
|
if (Project1 <> nil) and (Project1.BuildModes.Count > 1) then
|
|
Result := Project1.ActiveBuildMode.GetCaption
|
|
else
|
|
Result:='';
|
|
end;
|
|
|
|
constructor TBuildManager.Create(AOwner: TComponent);
|
|
begin
|
|
EnvironmentOptions := TEnvironmentOptions.Create;
|
|
IDEEnvironmentOptions := EnvironmentOptions;
|
|
EnvironmentOptions.IsGlobalMode:=@EnvironmentOptionsIsGlobalMode;
|
|
|
|
DefaultCfgVars:=TCTCfgScriptVariables.Create;
|
|
DefaultCfgVarsBuildMacroStamp:=CTInvalidChangeStamp;
|
|
FFPCVerChangeStamp:=CTInvalidChangeStamp;
|
|
FFPCompilerFilenameStamp:=CTInvalidChangeStamp;
|
|
MainBuildBoss:=Self;
|
|
inherited Create(AOwner);
|
|
fTargetOS:=GetCompiledTargetOS;
|
|
fTargetCPU:=GetCompiledTargetCPU;
|
|
fLCLWidgetType:=GetLCLWidgetTypeName;
|
|
FUnitSetChangeStamp:=TFPCUnitSetCache.GetInvalidChangeStamp;
|
|
|
|
OnBackupFileInteractive:=@BackupFileForWrite;
|
|
|
|
GetBuildMacroValues:=@GetBuildMacroValuesHandler;
|
|
OnAppendCustomOption:=@AppendMatrixCustomOption;
|
|
OnGetOutputDirectoryOverride:=@GetMatrixOutputDirectoryOverride;
|
|
|
|
CodeToolBoss.OnRescanFPCDirectoryCache:=@DoOnRescanFPCDirectoryCache;
|
|
end;
|
|
|
|
destructor TBuildManager.Destroy;
|
|
begin
|
|
ExternalToolList.Free; // sets ExternalToolList to nil, do not use FreeAndNil!
|
|
|
|
GetBuildMacroValues:=nil;
|
|
OnAppendCustomOption:=nil;
|
|
OnBackupFileInteractive:=nil;
|
|
|
|
FreeAndNil(FFPCSrcScans);
|
|
LazConfMacroFunc:=nil;
|
|
FreeAndNil(InputHistories);
|
|
FreeAndNil(DefaultCfgVars);
|
|
FreeAndNil(EnvironmentOptions);
|
|
|
|
if SameMethod(TMethod(CodeToolBoss.OnRescanFPCDirectoryCache),
|
|
TMethod(@DoOnRescanFPCDirectoryCache)) then
|
|
CodeToolBoss.OnRescanFPCDirectoryCache:=nil;
|
|
|
|
inherited Destroy;
|
|
MainBuildBoss:=nil;
|
|
end;
|
|
|
|
procedure TBuildManager.SetupTransferMacros;
|
|
begin
|
|
LazConfMacroFunc:=@BMLazConfMacroFunction;
|
|
GlobalMacroList:=TTransferMacroList.Create;
|
|
GlobalMacroList.OnSubstitution:=@MacroSubstitution;
|
|
IDEMacros:=TLazIDEMacros.Create;
|
|
OnParseString:=@SubstituteCompilerOption;
|
|
|
|
TIdeTransferMarcros.InitMacros(GlobalMacroList);
|
|
|
|
// project
|
|
GlobalMacroList.Add(TTransferMacro.Create('Project','',
|
|
lisProjectMacroProperties,@MacroFuncProject,[]));
|
|
GlobalMacroList.Add(TTransferMacro.Create('BuildMode','',
|
|
lisNameOfActiveBuildMode, @MacroFuncBuildMode, []));
|
|
GlobalMacroList.Add(TTransferMacro.Create('BuildModeCaption','',
|
|
lisCaptionOfActiveBuildMode, @MacroFuncBuildModeCaption, []));
|
|
GlobalMacroList.Add(TTransferMacro.Create('LCLWidgetType','',
|
|
lisLCLWidgetType,@MacroFuncLCLWidgetType,[]));
|
|
GlobalMacroList.Add(TTransferMacro.Create('TargetCPU','',
|
|
lisTargetCPU,@MacroFuncTargetCPU,[]));
|
|
GlobalMacroList.Add(TTransferMacro.Create('TargetOS','',
|
|
lisTargetOS,@MacroFuncTargetOS,[]));
|
|
GlobalMacroList.Add(TTransferMacro.Create('Subtarget','',
|
|
lisTargetCPU,@MacroFuncSubtarget,[]));
|
|
GlobalMacroList.Add(TTransferMacro.Create('SrcOS','',
|
|
lisSrcOS,@MacroFuncSrcOS,[]));
|
|
GlobalMacroList.Add(TTransferMacro.Create('CompPath','',
|
|
lisCompilerFilename,@MacroFuncCompPath,[]));
|
|
GlobalMacroList.Add(TTransferMacro.Create('FPCTarget','',
|
|
lisShortFormOfTargetCPUParamTargetOSParamSubTargetPar,
|
|
@MacroFuncFPCTarget, []));
|
|
GlobalMacroList.Add(TTransferMacro.Create('FPCVer','',
|
|
lisFPCVersionEG222, @MacroFuncFPCVer, []));
|
|
GlobalMacroList.Add(TTransferMacro.Create('FPC_FULLVERSION','',
|
|
lisFPCFullVersionEG20701, @MacroFuncFPC_FULLVERSION, []));
|
|
GlobalMacroList.Add(TTransferMacro.Create('FPCMsgFile','',
|
|
dlgFilterFPCMessageFile, @MacroFuncFPCMsgFile, []));
|
|
GlobalMacroList.Add(TTransferMacro.Create('Params','',
|
|
lisCommandLineParamsOfProgram,@MacroFuncParams,[]));
|
|
GlobalMacroList.Add(TTransferMacro.Create('ProjFile','',
|
|
lisProjectFilename,@MacroFuncProjFile,[]));
|
|
GlobalMacroList.Add(TTransferMacro.Create('ProjPath','',
|
|
lisProjectDirectory,@MacroFuncProjPath,[]));
|
|
GlobalMacroList.Add(TTransferMacro.Create('TargetFile','',
|
|
lisTargetFilenameOfProject,@MacroFuncTargetFile,[]));
|
|
GlobalMacroList.Add(TTransferMacro.Create('TargetCmdLine','',
|
|
lisTargetFilenamePlusParams,@MacroFuncTargetCmdLine,[]));
|
|
GlobalMacroList.Add(TTransferMacro.Create('RunCmdLine','',
|
|
lisLaunchingCmdLine,@MacroFuncRunCmdLine,[]));
|
|
GlobalMacroList.Add(TTransferMacro.Create('OutputFile','',
|
|
lisOutputFilenameOfProject,@MacroFuncOutputFile,[]));
|
|
GlobalMacroList.Add(TTransferMacro.Create('ProjPublishDir','',
|
|
lisPublishProjDir,@MacroFuncProjPublishDir,[]));
|
|
GlobalMacroList.Add(TTransferMacro.Create('ProjNamespaces','',
|
|
lisProjectNamespaces,@MacroFuncProjNamespaces,[]));
|
|
GlobalMacroList.Add(TTransferMacro.Create('ProjUnitPath','',
|
|
lisProjectUnitPath,@MacroFuncProjUnitPath,[]));
|
|
GlobalMacroList.Add(TTransferMacro.Create('ProjIncPath','',
|
|
lisProjectIncPath,@MacroFuncProjIncPath,[]));
|
|
GlobalMacroList.Add(TTransferMacro.Create('ProjSrcPath','',
|
|
lisProjectSrcPath,@MacroFuncProjSrcPath,[]));
|
|
GlobalMacroList.Add(TTransferMacro.Create('ProjOutDir','',
|
|
lisProjectOutDir,@MacroFuncProjOutDir,[]));
|
|
GlobalMacroList.Add(TTransferMacro.Create('ProjVer','',
|
|
lisProjectVer,@MacroFuncProjVer,[]));
|
|
GlobalMacroList.Add(TTransferMacro.Create('Env','',
|
|
lisEnvironmentVariableNameAsParameter, @MacroFuncEnv, []));
|
|
GlobalMacroList.Add(TTransferMacro.Create('MakeExe','',
|
|
lisMakeExe,@MacroFuncMakeExe,[]));
|
|
GlobalMacroList.Add(TTransferMacro.Create('MakeLib','',
|
|
lisMakeExe,@MacroFuncMakeLib,[]));
|
|
GlobalMacroList.Add(TTransferMacro.Create('Make','',
|
|
lisPathOfTheMakeUtility, @MacroFuncMake, []));
|
|
GlobalMacroList.Add(TTransferMacro.Create('InstantFPCCache','',
|
|
lisPathOfTheInstantfpcCache, @MacroFuncInstantFPCCache, []));
|
|
GlobalMacroList.Add(TTransferMacro.Create('IDEBuildOptions','',
|
|
lisIDEBuildOptions, @MacroFuncIDEBuildOptions, []));
|
|
GlobalMacroList.Add(TTransferMacro.Create('PrimaryConfigPath','',
|
|
lisPrimaryConfigPath, @MacroFuncPrimaryConfigPath, []));
|
|
GlobalMacroList.Add(TTransferMacro.Create('SecondaryConfigPath','',
|
|
lisSecondaryConfigPath, @MacroFuncSecondaryConfigPath, []));
|
|
GlobalMacroList.Add(TTransferMacro.Create('FallbackOutputRoot','',
|
|
lisSecondaryConfigPath, @MacroFuncFallbackOutputRoot, []));
|
|
GlobalMacroList.Add(TTransferMacro.Create('LAZVer','',
|
|
lisLAZVer, @MacroFuncLazVer, []));
|
|
|
|
// codetools macro functions
|
|
CodeToolBoss.DefineTree.MacroFunctions.AddExtended(
|
|
'PROJECTNAMESPACES',nil,@CTMacroFuncProjectNamespaces);
|
|
CodeToolBoss.DefineTree.MacroFunctions.AddExtended(
|
|
'PROJECTUNITPATH',nil,@CTMacroFuncProjectUnitPath);
|
|
CodeToolBoss.DefineTree.MacroFunctions.AddExtended(
|
|
'PROJECTINCPATH',nil,@CTMacroFuncProjectIncPath);
|
|
CodeToolBoss.DefineTree.MacroFunctions.AddExtended(
|
|
'PROJECTSRCPATH',nil,@CTMacroFuncProjectSrcPath);
|
|
|
|
RunBootHandlers(libhTransferMacrosCreated);
|
|
end;
|
|
|
|
procedure TBuildManager.TranslateMacros;
|
|
|
|
procedure tr(const MacroName, Description: string);
|
|
var
|
|
Macro: TTransferMacro;
|
|
begin
|
|
Macro:=GlobalMacroList.FindByName(MacroName);
|
|
if Macro=nil then exit;
|
|
Macro.Description:=Description;
|
|
end;
|
|
|
|
begin
|
|
tr('Project',lisProjectMacroProperties);
|
|
tr('BuildMode',lisNameOfActiveBuildMode);
|
|
tr('BuildModeCaption',lisCaptionOfActiveBuildMode);
|
|
tr('LCLWidgetType',lisLCLWidgetType);
|
|
tr('TargetCPU',lisTargetCPU);
|
|
tr('TargetOS',lisTargetOS);
|
|
tr('Subtarget',lisSubtarget);
|
|
tr('SrcOS',lisSrcOS);
|
|
tr('FPCTarget',lisShortFormOfTargetCPUParamTargetOSParamSubTargetPar);
|
|
tr('FPCVer',lisFPCVersionEG222);
|
|
tr('LAZVer',lisLAZVer);
|
|
tr('FPC_FULLVERSION',lisFPCFullVersionEG20701);
|
|
tr('Params',lisCommandLineParamsOfProgram);
|
|
tr('ProjFile',lisProjectFilename);
|
|
tr('ProjPath',lisProjectDirectory);
|
|
tr('TargetFile',lisTargetFilenameOfProject);
|
|
tr('TargetCmdLine',lisTargetFilenamePlusParams);
|
|
tr('RunCmdLine',lisLaunchingCmdLine);
|
|
tr('OutputFile',lisOutputFilenameOfProject);
|
|
tr('ProjPublishDir',lisPublishProjDir);
|
|
tr('ProjNamespaces',lisProjectNamespaces);
|
|
tr('ProjUnitPath',lisProjectUnitPath);
|
|
tr('ProjIncPath',lisProjectIncPath);
|
|
tr('ProjSrcPath',lisProjectSrcPath);
|
|
tr('ProjOutDir',lisProjectOutDir);
|
|
tr('ProjVer',lisProjectVer);
|
|
tr('Env',lisEnvironmentVariableNameAsParameter);
|
|
tr('FPCMsgFile',dlgFilterFPCMessageFile);
|
|
tr('MakeExe',lisMakeExe);
|
|
tr('MakeLib',lisMakeExe);
|
|
tr('Make',lisPathOfTheMakeUtility);
|
|
tr('InstantFPCCache',lisPathOfTheInstantfpcCache);
|
|
tr('IDEBuildOptions',lisIDEBuildOptions);
|
|
tr('PrimaryConfigPath',lisPrimaryConfigPath);
|
|
tr('SecondaryConfigPath',lisSecondaryConfigPath);
|
|
tr('FallbackOutputRoot',lisSecondaryConfigPath);
|
|
tr('CompPath',lisCompilerFilename);
|
|
tr('FPCSrcDir',lisFreePascalSourceDirectory);
|
|
tr('LazarusDir',lisLazarusDirectory);
|
|
tr('ExeExt',lisFileExtensionOfPrograms);
|
|
tr('LanguageID',lisLazarusLanguageID);
|
|
tr('LanguageName',lisLazarusLanguageName);
|
|
tr('TestDir',lisTestDirectory);
|
|
tr('ConfDir',lisConfigDirectory);
|
|
tr('Home',lisUserSHomeDirectory);
|
|
tr('Ext',lisTMFunctionExtractFileExtension);
|
|
tr('Path',lisTMFunctionExtractFilePath);
|
|
tr('Name',lisTMFunctionExtractFileNameExtension);
|
|
tr('NameOnly',lisTMFunctionExtractFileNameOnly);
|
|
tr('MakeDir',lisTMFunctionAppendPathDelimiter);
|
|
tr('MakeFile',lisTMFunctionChompPathDelimiter);
|
|
tr('EncloseBracket', lisTMFunctionEncloseBrackets);
|
|
end;
|
|
|
|
procedure TBuildManager.SetupExternalTools(aToolsClass: TExternalToolsClass);
|
|
var
|
|
Tools: TExternalTools;
|
|
begin
|
|
// setup the external tool queue
|
|
Tools:=aToolsClass.Create(Self);
|
|
if Tools<>ExternalToolList then
|
|
raise Exception.Create('TBuildManager.SetupExternalTools ExternalTools='+DbgSName(ExternalToolList));
|
|
EnvOptsChanged;
|
|
RegisterFPCParser;
|
|
RegisterPas2jsParser;
|
|
RegisterMakeParser;
|
|
ExternalToolList.RegisterParser(TDefaultParser);
|
|
|
|
FPCMsgFilePool:=TFPCMsgFilePool.Create(nil);
|
|
Pas2jsMsgFilePool:=TPas2jsMsgFilePool.Create(nil);
|
|
end;
|
|
|
|
procedure TBuildManager.SetupCompilerInterface;
|
|
begin
|
|
TheCompiler := TCompiler.Create;
|
|
end;
|
|
|
|
procedure TBuildManager.SetupInputHistories(aInputHist: TInputHistories);
|
|
begin
|
|
aInputHist.SetLazarusDefaultFilename;
|
|
aInputHist.Load;
|
|
end;
|
|
|
|
procedure TBuildManager.EnvOptsChanged;
|
|
begin
|
|
if EnvironmentOptions.MaxExtToolsInParallel<=0 then
|
|
ExternalToolsRef.MaxProcessCount:=DefaultMaxProcessCount
|
|
else
|
|
ExternalToolsRef.MaxProcessCount:=EnvironmentOptions.MaxExtToolsInParallel;
|
|
end;
|
|
|
|
function TBuildManager.GetBuildMacroOverride(const MacroName: string): string;
|
|
begin
|
|
Result:='';
|
|
if SysUtils.CompareText(MacroName,'TargetOS')=0 then
|
|
Result:=OverrideTargetOS
|
|
else if SysUtils.CompareText(MacroName,'TargetCPU')=0 then
|
|
Result:=OverrideTargetCPU
|
|
else if SysUtils.CompareText(MacroName,'Subtarget')=0 then
|
|
Result:=OverrideSubtarget
|
|
else if SysUtils.CompareText(MacroName,'LCLWidgetType')=0 then
|
|
Result:=OverrideLCLWidgetType;
|
|
end;
|
|
|
|
function TBuildManager.GetBuildMacroOverrides: TStrings;
|
|
begin
|
|
Result:=TStringList.Create;
|
|
if OverrideTargetOS<>'' then
|
|
Result.Values['TargetOS']:=OverrideTargetOS;
|
|
if OverrideTargetCPU<>'' then
|
|
Result.Values['TargetCPU']:=OverrideTargetCPU;
|
|
if OverrideSubtarget<>'' then
|
|
Result.Values['Subtarget']:=OverrideSubtarget;
|
|
if OverrideLCLWidgetType<>'' then
|
|
Result.Values['LCLWidgetType']:=OverrideLCLWidgetType;
|
|
end;
|
|
|
|
function TBuildManager.GetTargetOS: string;
|
|
begin
|
|
Result:=fTargetOS;
|
|
end;
|
|
|
|
function TBuildManager.GetTargetCPU: string;
|
|
begin
|
|
Result:=fTargetCPU;
|
|
//debugln(['TBuildManager.GetTargetCPU ',Result]);
|
|
end;
|
|
|
|
function TBuildManager.GetSubtarget: string;
|
|
begin
|
|
Result:=fSubtarget;
|
|
end;
|
|
|
|
function TBuildManager.GetLCLWidgetType: string;
|
|
begin
|
|
Result:=fLCLWidgetType;
|
|
end;
|
|
|
|
function TBuildManager.GetTargetFilename: String;
|
|
begin
|
|
Result := GetProjectTargetFilename(Project1);
|
|
if GetProjectUsesAppBundle then
|
|
// return command line to Application Bundle (darwin only)
|
|
Result := ExtractFileNameWithoutExt(Result) + '.app';
|
|
end;
|
|
|
|
function TBuildManager.GetRunCommandLine: string;
|
|
var
|
|
TargetFilename: string;
|
|
AMode: TRunParamsOptionsMode;
|
|
begin
|
|
Result := '';
|
|
if Project1=nil then exit;
|
|
AMode := Project1.RunParameterOptions.GetActiveMode;
|
|
if (AMode<>nil) and AMode.UseLaunchingApplication then
|
|
Result := AMode.LaunchingApplicationPathPlusParams;
|
|
|
|
if Result='' then
|
|
begin
|
|
if (AMode<>nil) then
|
|
Result := AMode.CmdLineParams;
|
|
if GlobalMacroList.SubstituteStr(Result) then
|
|
begin
|
|
TargetFilename := GetTargetFilename;
|
|
if (TargetFilename <> '')
|
|
and (TargetFilename[Length(TargetFilename)] in AllowDirectorySeparators) then
|
|
TargetFilename += ExtractFileNameOnly(
|
|
Project1.CompilerOptions.GetDefaultMainSourceFileName);
|
|
TargetFilename := '"'+TargetFilename+'"';
|
|
if Result='' then
|
|
Result:=TargetFilename
|
|
else
|
|
Result:=TargetFilename+' '+Result;
|
|
end else
|
|
Result:='';
|
|
end else begin
|
|
if not GlobalMacroList.SubstituteStr(Result) then Result:='';
|
|
end;
|
|
end;
|
|
|
|
function TBuildManager.GetRunWorkingDir: string;
|
|
var
|
|
AMode: TRunParamsOptionsMode;
|
|
begin
|
|
Result := '';
|
|
if Project1=nil then exit;
|
|
|
|
// first take the WorkDir from the active run parameters
|
|
AMode := Project1.RunParameterOptions.GetActiveMode;
|
|
if AMode<>nil then
|
|
Result := AMode.WorkingDirectory;
|
|
if not GlobalMacroList.SubstituteStr(Result) then
|
|
Result := '';
|
|
if (Result <> '') and not FilenameIsAbsolute(Result) then
|
|
Result := CreateAbsolutePath(Result, Project1.Directory);
|
|
|
|
// then use the directory of the produced exe
|
|
if Result='' then begin
|
|
Result := ExtractFilePath(BuildBoss.GetProjectTargetFilename(Project1));
|
|
if (Result <> '') and not FilenameIsAbsolute(Result) then
|
|
Result := CreateAbsolutePath(Result, Project1.Directory);
|
|
end;
|
|
|
|
// finally use the project directory
|
|
if (Result='') and (not Project1.IsVirtual) then
|
|
Result := ChompPathDelim(Project1.Directory);
|
|
end;
|
|
|
|
procedure TBuildManager.WriteDebug_RunCommandLine;
|
|
var
|
|
AMode: TRunParamsOptionsMode;
|
|
s, TargetFilename: String;
|
|
begin
|
|
s:='';
|
|
if Project1=nil then
|
|
begin
|
|
debugln(['Note: (lazarus) [TBuildManager.WriteDebug_RunCommandLine] Project1=nil RunCmdLine=[',GetRunCommandLine,']']);
|
|
end else begin
|
|
AMode := Project1.RunParameterOptions.GetActiveMode;
|
|
if AMode<>nil then
|
|
debugln(['Note: (lazarus) [TBuildManager.WriteDebug_RunCommandLine] AMode="',AMode.Name,'" AMode.WorkingDirectory=[',AMode.WorkingDirectory,']'])
|
|
else
|
|
debugln(['Note: (lazarus) [TBuildManager.WriteDebug_RunCommandLine] AMode=nil']);
|
|
if (AMode<>nil) and AMode.UseLaunchingApplication then
|
|
begin
|
|
s := AMode.LaunchingApplicationPathPlusParams;
|
|
debugln(['Note: (lazarus) [TBuildManager.WriteDebug_RunCommandLine] LaunchingApplicationPathPlusParams=[',s,']']);
|
|
end;
|
|
|
|
if s='' then
|
|
begin
|
|
// no launching app
|
|
debugln(['Note: (lazarus) [TBuildManager.WriteDebug_RunCommandLine] no LaunchingApplication']);
|
|
if (AMode<>nil) then
|
|
begin
|
|
s := AMode.CmdLineParams;
|
|
if s<>'' then
|
|
debugln(['Note: (lazarus) [TBuildManager.WriteDebug_RunCommandLine] AMode.CmdLineParams=[',s,']']);
|
|
end;
|
|
TargetFilename := GetTargetFilename;
|
|
if (TargetFilename <> '')
|
|
and (TargetFilename[Length(TargetFilename)] in AllowDirectorySeparators) then
|
|
TargetFilename += ExtractFileNameOnly(
|
|
Project1.CompilerOptions.GetDefaultMainSourceFileName);
|
|
|
|
debugln(['Note: (lazarus) [TBuildManager.WriteDebug_RunCommandLine] TargetFilename=[',TargetFilename,']']);
|
|
end;
|
|
debugln(['Note: (lazarus) [TBuildManager.WriteDebug_RunCommandLine] Project1<>nil RunCmdLine=[',GetRunCommandLine,']']);
|
|
end;
|
|
end;
|
|
|
|
function TBuildManager.GetCompilerFilename: string;
|
|
var
|
|
Opts: TProjectCompilerOptions;
|
|
begin
|
|
Result:='';
|
|
//debugln(['TBuildManager.GetCompilerFilename START FBuildTarget=',DbgSName(FBuildTarget)]);
|
|
if FBuildTarget<>nil then
|
|
begin
|
|
Opts:=FBuildTarget.CompilerOptions;
|
|
//debugln(['TBuildManager.GetCompilerFilename FBuildTarget=',DbgSName(FBuildTarget),' Path=',Opts.CompilerPath,' Build=',[crCompile,crBuild]*Opts.CompileReasons<>[],' Parsing=',Opts.ParsedOpts.Values[pcosCompilerPath].Parsing]);
|
|
// Note: even if Opts.CompileReasons are disabled, the project compiler path is used by codetools
|
|
if (Opts.CompilerPath<>'')
|
|
and (not Opts.ParsedOpts.Values[pcosCompilerPath].Parsing) then
|
|
begin
|
|
Result:=Opts.CompilerPath;
|
|
// the compiler filename is resolved twice, once for getting the default
|
|
// compiler target OS/CPU and once with the real values.
|
|
// For easier macro debugging, avoid this double resolve.
|
|
if Result='' then
|
|
// see below
|
|
else if Result='$(CompPath)' then
|
|
Result:=''
|
|
else if (Pos('$',Result)<1) and (FilenameIsAbsolute(Result)) then
|
|
Result:=TrimFilename(Result)
|
|
else begin
|
|
Result:=FBuildTarget.GetCompilerFilename;
|
|
if Result='' then
|
|
begin
|
|
Result:=cInvalidCompiler;
|
|
debugln(['Error: (lazarus) [TBuildManager.GetCompilerFilename] invalid compiler "',Opts.CompilerPath,'"']);
|
|
end;
|
|
end;
|
|
//debugln(['TBuildManager.GetCompilerFilename project compiler="',Result,'"']);
|
|
end;
|
|
end;
|
|
if Result='' then
|
|
Result:=EnvironmentOptions.GetParsedCompilerFilename;
|
|
//debugln(['TBuildManager.GetCompilerFilename END Result="',Result,'"']);
|
|
end;
|
|
|
|
function TBuildManager.GetFPCompilerFilename: string;
|
|
var
|
|
ErrMsg: string;
|
|
Kind: TPascalCompiler;
|
|
begin
|
|
if FFPCompilerFilenameStamp<>CompilerParseStamp then begin
|
|
FFPCompilerFilename:=GetCompilerFilename;
|
|
if (not IsCompilerExecutable(FFPCompilerFilename,ErrMsg,Kind,false)) or (ErrMsg<>'')
|
|
or (Kind<>pcFPC) then
|
|
FFPCompilerFilename:=EnvironmentOptions.GetParsedCompilerFilename;
|
|
FFPCompilerFilenameStamp:=CompilerParseStamp;
|
|
end;
|
|
Result:=FFPCompilerFilename;
|
|
end;
|
|
|
|
function TBuildManager.GetFPCFrontEndOptions: string;
|
|
var
|
|
s, CfgFilename: String;
|
|
Opts: TProjectCompilerOptions;
|
|
begin
|
|
Result:='';
|
|
if FBuildTarget<>nil then
|
|
begin
|
|
Opts:=FBuildTarget.CompilerOptions;
|
|
s:=ExtractFPCFrontEndParameters(Opts.CustomOptions);
|
|
if GlobalMacroList.SubstituteStr(s) then
|
|
begin
|
|
if s<>'' then
|
|
Result:=s;
|
|
end else begin
|
|
debugln(['Warning: (lazarus) [GetFPCFrontEndOptions] ignoring invalid macros in custom options for fpc frontend: "',ExtractFPCFrontEndParameters(FBuildTarget.CompilerOptions.CustomOptions),'"']);
|
|
end;
|
|
if Opts.CustomConfigFile and (Opts.ConfigFilePath<>'') then
|
|
begin
|
|
CfgFilename:=Opts.ParsedOpts.DoParseOption(Opts.ConfigFilePath, pcosCustomConfigFilePath, false);
|
|
if CfgFilename<>'' then
|
|
begin
|
|
if Result<>'' then Result+=' ';
|
|
Result+='@'+CfgFilename;
|
|
end;
|
|
end;
|
|
end;
|
|
if LazarusIDE<>nil then
|
|
if not LazarusIDE.CallHandlerGetFPCFrontEndParams(Self,Result) then begin
|
|
debugln(['Warning: TBuildManager.GetFPCFrontEndOptions: LazarusIDE.CallHandlerGetFPCFrontEndParams failed Result="',Result,'"']);
|
|
end;
|
|
Result:=UTF8Trim(Result);
|
|
end;
|
|
|
|
function TBuildManager.GetProjectPublishDir: string;
|
|
begin
|
|
if Project1<>nil then
|
|
Result:=RealPublishDir(Project1.PublishOptions)
|
|
else
|
|
Result:='';
|
|
end;
|
|
|
|
function TBuildManager.GetProjectTargetFilename(aProject: TProject): string;
|
|
var
|
|
AMode: TRunParamsOptionsMode;
|
|
begin
|
|
Result:='';
|
|
if aProject=nil then exit;
|
|
AMode := aProject.RunParameterOptions.GetActiveMode;
|
|
if AMode<>nil then
|
|
Result:=AMode.HostApplicationFilename;
|
|
GlobalMacroList.SubstituteStr(Result);
|
|
if (Result='') and (aProject.MainUnitID>=0) then begin
|
|
Result := aProject.CompilerOptions.CreateTargetFilename;
|
|
end;
|
|
end;
|
|
|
|
function TBuildManager.GetProjectUsesAppBundle: Boolean;
|
|
begin
|
|
Result := (Project1<>nil)
|
|
and ( (Project1.RunParameterOptions.GetActiveMode=nil)
|
|
or (Project1.RunParameterOptions.GetActiveMode.HostApplicationFilename = ''))
|
|
and (GetTargetOS = 'darwin') and Project1.UseAppBundle;
|
|
end;
|
|
|
|
function TBuildManager.GetTestUnitFilename(AnUnitInfo: TUnitInfo): string;
|
|
var
|
|
TestDir: String;
|
|
begin
|
|
Result:='';
|
|
if AnUnitInfo=nil then exit;
|
|
TestDir:=GetTestBuildDirectory;
|
|
if TestDir='' then exit;
|
|
Result:=ExtractFilename(AnUnitInfo.Filename);
|
|
if Result='' then exit;
|
|
Result:=TestDir+Result;
|
|
end;
|
|
|
|
function TBuildManager.GetTestBuildDirectory: string;
|
|
begin
|
|
Result:=EnvironmentOptions.GetParsedTestBuildDirectory;
|
|
end;
|
|
|
|
function TBuildManager.IsTestUnitFilename(const AFilename: string): boolean;
|
|
var
|
|
TestDir: string;
|
|
begin
|
|
Result:=false;
|
|
if (Project1<>nil) and Project1.IsVirtual then begin
|
|
TestDir:=GetTestBuildDirectory;
|
|
Result:=FileIsInPath(AFilename,TestDir);
|
|
end;
|
|
end;
|
|
|
|
function TBuildManager.GetTargetUnitFilename(AnUnitInfo: TUnitInfo): string;
|
|
begin
|
|
if Project1.IsVirtual then
|
|
Result:=GetTestUnitFilename(AnUnitInfo)
|
|
else
|
|
Result:=AnUnitInfo.Filename;
|
|
end;
|
|
|
|
procedure TBuildManager.UpdateEnglishErrorMsgFilename;
|
|
begin
|
|
if EnvironmentOptions.GetParsedLazarusDirectory<>'' then begin
|
|
CodeToolBoss.DefinePool.EnglishErrorMsgFilename:=
|
|
AppendPathDelim(EnvironmentOptions.GetParsedLazarusDirectory)+
|
|
GetForcedPathDelims('components/codetools/fpc.errore.msg');
|
|
CodeToolBoss.CompilerDefinesCache.ExtraOptions:=
|
|
AnsiQuotedStr('-Fr'+CodeToolBoss.DefinePool.EnglishErrorMsgFilename,'"');
|
|
end;
|
|
end;
|
|
|
|
procedure TBuildManager.RescanCompilerDefines(ResetBuildTarget,
|
|
ClearCaches, WaitTillDone, Quiet: boolean);
|
|
|
|
procedure AddTemplate(ADefTempl: TDefineTemplate; AddToPool: boolean;
|
|
const ErrorMsg: string);
|
|
begin
|
|
if ADefTempl = nil then
|
|
begin
|
|
DebugLn('');
|
|
DebugLn(ErrorMsg);
|
|
end else
|
|
begin
|
|
if AddToPool then
|
|
CodeToolBoss.DefinePool.Add(ADefTempl.CreateCopy(false,true,true));
|
|
CodeToolBoss.DefineTree.ReplaceRootSameName(ADefTempl);
|
|
end;
|
|
end;
|
|
|
|
function FoundSystemPPU: boolean;
|
|
var
|
|
ConfigCache: TPCTargetConfigCache;
|
|
AFilename: string;
|
|
begin
|
|
Result:=false;
|
|
ConfigCache:=UnitSetCache.GetConfigCache(false);
|
|
if ConfigCache=nil then exit;
|
|
if ConfigCache.Units=nil then exit;
|
|
AFilename:=ConfigCache.Units['system'];
|
|
if AFilename='' then exit;
|
|
if not FilenameExtIs(AFilename,'ppu',true) then exit;
|
|
Result:=true;
|
|
end;
|
|
|
|
function PPUFilesAndCompilerMatch: boolean;
|
|
// check if compiler is in another directory than the ppu files
|
|
// for example: a 'make install' installs to /usr/local/lib/fpc
|
|
// while the rpm/deb packages install to /usr/lib
|
|
var
|
|
Cfg: TPCTargetConfigCache;
|
|
Filename: String;
|
|
begin
|
|
Cfg:=UnitSetCache.GetConfigCache(false);
|
|
if Cfg=nil then exit(true);
|
|
if Cfg.Kind=pcFPC then begin
|
|
if Cfg.RealCompiler='' then begin
|
|
if ConsoleVerbosity>=0 then
|
|
debugln(['Error: (lazarus) [PPUFilesAndCompilerMatch] Compiler=',Cfg.Compiler,' RealComp=',Cfg.RealCompiler,' InPath=',Cfg.RealTargetCPUCompiler]);
|
|
IDEMessageDialog(lisCCOErrorCaption, Format(
|
|
lisCompilerDoesNotSupportTarget, [Cfg.Compiler, Cfg.TargetCPU, Cfg.TargetOS]),
|
|
mtError,[mbOk]);
|
|
exit(false);
|
|
end;
|
|
Filename:=GetPhysicalFilenameCached(Cfg.RealCompiler,true);
|
|
if (Filename='') then begin
|
|
IDEMessageDialog('Error','Compiler executable is missing: '+Cfg.RealCompiler,
|
|
mtError,[mbOk]);
|
|
exit(false);
|
|
end;
|
|
end;
|
|
Result:=true;
|
|
end;
|
|
|
|
var
|
|
TargetOS, TargetCPU, Subtarget, FPCOptions: string;
|
|
CompilerFilename: String;
|
|
FPCSrcDir: string;
|
|
ADefTempl: TDefineTemplate;
|
|
FPCSrcCache: TFPCSourceCache;
|
|
NeedUpdateFPCSrcCache: Boolean;
|
|
IgnorePath: String;
|
|
MsgResult: TModalResult;
|
|
AsyncScanFPCSrcDir: String;
|
|
UnitSetChanged: Boolean;
|
|
HasTemplate: Boolean;
|
|
CompilerErrorMsg: string;
|
|
Msg, DefCompilerFilename, ProjCompilerFilename, ProjCompilerErrorMsg,
|
|
DefCompilerErrorMsg, WorkDir: String;
|
|
CompilerKind, ProjCompilerKind, DefCompilerKind: TPascalCompiler;
|
|
begin
|
|
if ClearCaches then begin
|
|
{$IFDEF VerboseFPCSrcScan}
|
|
debugln(['TBuildManager.RescanCompilerDefines clear caches']);
|
|
{$ENDIF}
|
|
CodeToolBoss.CompilerDefinesCache.ConfigCaches.Clear;
|
|
CodeToolBoss.CompilerDefinesCache.SourceCaches.Clear;
|
|
end;
|
|
if ResetBuildTarget then
|
|
SetBuildTarget('','','','',smsfsSkip,true);
|
|
|
|
// start the compiler and ask for his settings
|
|
// provide an english message file
|
|
UpdateEnglishErrorMsgFilename;
|
|
|
|
// use current TargetOS, TargetCPU, compilerfilename and FPC source dir
|
|
TargetOS:=GetTargetOS;
|
|
TargetCPU:=GetTargetCPU;
|
|
Subtarget:=GetSubtarget;
|
|
{$IFDEF VerboseFPCSrcScan}
|
|
debugln(['TBuildManager.RescanCompilerDefines GetParsedFPCSourceDirectory needs FPCVer...']);
|
|
{$ENDIF}
|
|
CompilerFilename:=GetCompilerFilename;
|
|
IsCompilerExecutable(CompilerFilename,CompilerErrorMsg,CompilerKind,{$IFDEF VerboseFPCSrcScan}true{$ELSE}false{$ENDIF});
|
|
FPCSrcDir:=EnvironmentOptions.GetParsedFPCSourceDirectory; // needs FPCVer macro
|
|
FPCOptions:=GetFPCFrontEndOptions;
|
|
|
|
{$IFDEF VerboseFPCSrcScan}
|
|
debugln(['TMainIDE.RescanCompilerDefines START ',
|
|
' CompilerFilename=',CompilerFilename,
|
|
' Kind=',PascalCompilerNames[CompilerKind],
|
|
' TargetOS=',TargetOS,
|
|
' TargetCPU=',TargetCPU,
|
|
' Subtarget=',Subtarget,
|
|
' FPCOptions="',FPCOptions,'"',
|
|
' EnvFPCSrcDir=',EnvironmentOptions.FPCSourceDirectory,
|
|
' FPCSrcDir=',FPCSrcDir,
|
|
' WaitTillDone=',WaitTillDone,
|
|
' Quiet=',Quiet,
|
|
' ClearCaches=',ClearCaches,
|
|
'']);
|
|
{$ENDIF}
|
|
|
|
// first check the default targetos, targetcpu of the default compiler
|
|
DefCompilerFilename:=EnvironmentOptions.GetParsedCompilerFilename;
|
|
if FileExistsCached(DefCompilerFilename) then
|
|
begin
|
|
{$IFDEF VerboseFPCSrcScan}
|
|
debugln(['TBuildManager.RescanCompilerDefines reading default compiler settings']);
|
|
{$ENDIF}
|
|
UnitSetCache:=CodeToolBoss.CompilerDefinesCache.FindUnitSet(
|
|
DefCompilerFilename,'','','','',FPCSrcDir,'',true);
|
|
UnitSetCache.GetConfigCache(true);
|
|
end;
|
|
|
|
if CompilerFilename<>DefCompilerFilename then
|
|
IsCompilerExecutable(CompilerFilename,CompilerErrorMsg,CompilerKind,true);
|
|
|
|
// then check the project's compiler
|
|
if (CompilerErrorMsg<>'') then begin
|
|
Msg:='';
|
|
if (FBuildTarget<>nil)
|
|
and ([crCompile,crBuild]*FBuildTarget.CompilerOptions.CompileReasons<>[])
|
|
and (FBuildTarget.CompilerOptions.CompilerPath<>'')
|
|
then begin
|
|
ProjCompilerFilename:=FBuildTarget.GetCompilerFilename;
|
|
if not IsCompilerExecutable(ProjCompilerFilename,ProjCompilerErrorMsg,ProjCompilerKind,true)
|
|
then begin
|
|
Msg+='Project''s compiler: "'+ProjCompilerFilename+'": '+ProjCompilerErrorMsg+LineEnding;
|
|
end;
|
|
end;
|
|
|
|
if not IsCompilerExecutable(DefCompilerFilename,DefCompilerErrorMsg,DefCompilerKind,true)
|
|
then begin
|
|
Msg+='Environment compiler: "'+DefCompilerFilename+'": '+DefCompilerErrorMsg+LineEnding;
|
|
end;
|
|
if Msg='' then
|
|
Msg+='Compiler: "'+CompilerFilename+'": '+CompilerErrorMsg+LineEnding;
|
|
|
|
debugln('Warning: (lazarus) [TBuildManager.RescanCompilerDefines]: invalid compiler:');
|
|
debugln(Msg);
|
|
if not Quiet then begin
|
|
IDEMessageDialog(lisCCOErrorCaption, Format(
|
|
lisThereIsNoFreePascalCompilerEGFpcOrPpcCpuConfigured, [ExeExt,
|
|
LineEnding, Msg]), mtError, [mbOk]);
|
|
end;
|
|
UnitSetCache:=nil;
|
|
exit;
|
|
end;
|
|
|
|
// create a cache for the current project settings
|
|
{$IFDEF VerboseFPCSrcScan}
|
|
debugln(['TBuildManager.RescanCompilerDefines reading active compiler settings']);
|
|
{$ENDIF}
|
|
WorkDir:='';
|
|
if (FBuildTarget<>nil) and (not FBuildTarget.IsVirtual)
|
|
and HasFPCParamsRelativeFilename(FPCOptions) then
|
|
WorkDir:=FBuildTarget.Directory;
|
|
|
|
//debugln(['TBuildManager.RescanCompilerDefines ',CompilerFilename,' OS=',TargetOS,' CPU=',TargetCPU,' Subtarget=',Subtarget,' Options="',FPCOptions,'" WorkDir="',WorkDir,'"']);
|
|
UnitSetCache:=CodeToolBoss.CompilerDefinesCache.FindUnitSet(
|
|
CompilerFilename,TargetOS,TargetCPU,Subtarget,FPCOptions,FPCSrcDir,WorkDir,true);
|
|
|
|
NeedUpdateFPCSrcCache:=false;
|
|
//debugln(['TBuildManager.RescanCompilerDefines ',DirectoryExistsUTF8(FPCSrcDir),' ',(not WaitTillDone),' ',(not HasGUI)]);
|
|
AsyncScanFPCSrcDir:='';
|
|
if DirectoryExistsUTF8(FPCSrcDir) and ((not WaitTillDone) or (not HasGUI)) then
|
|
begin
|
|
// FPC sources are not needed
|
|
// => disable scan
|
|
FPCSrcCache:=UnitSetCache.GetSourceCache(false);
|
|
if (FPCSrcCache<>nil) and (not FPCSrcCache.Valid) then
|
|
begin
|
|
NeedUpdateFPCSrcCache:=HasGUI;
|
|
FPCSrcCache.Valid:=true;
|
|
if NeedUpdateFPCSrcCache then
|
|
begin
|
|
// start background scan of fpc source directory
|
|
//debugln(['TBuildManager.RescanCompilerDefines background scan: '+FPCSrcCache.Directory]);
|
|
AsyncScanFPCSrcDir:=FPCSrcDir;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
// scan compiler, fpc sources and create indices for quick lookup
|
|
UnitSetCache.Init;
|
|
|
|
UnitSetChanged:=(FUnitSetChangeStamp=TFPCUnitSetCache.GetInvalidChangeStamp)
|
|
or (FUnitSetChangeStamp<>UnitSetCache.ChangeStamp);
|
|
|
|
{$IFDEF VerboseFPCSrcScan}
|
|
debugln(['TBuildManager.RescanCompilerDefines UnitSet changed=',UnitSetChanged,
|
|
' ClearCaches=',ClearCaches,
|
|
' CompilerFilename=',UnitSetCache.CompilerFilename,
|
|
' TargetOS=',UnitSetCache.TargetOS,
|
|
' TargetCPU=',UnitSetCache.TargetCPU,
|
|
' Subtarget=',UnitSetCache.Subtarget,
|
|
' WorkDir=',UnitSetCache.WorkingDir,
|
|
' FPCOptions="',UnitSetCache.CompilerOptions,'"',
|
|
' RealCompiler=',UnitSetCache.GetConfigCache(false).RealCompiler,
|
|
' EnvFPCSrcDir=',EnvironmentOptions.FPCSourceDirectory,
|
|
' FPCSrcDir=',UnitSetCache.FPCSourceDirectory,
|
|
'']);
|
|
{$ENDIF}
|
|
|
|
if UnitSetChanged then begin
|
|
{$IFDEF VerboseFPCSrcScan}
|
|
debugln(['TBuildManager.RescanCompilerDefines UnitSet changed => save scan results']);
|
|
{$ENDIF}
|
|
// save caches
|
|
SaveCompilerDefinesCaches;
|
|
FUnitSetChangeStamp:=UnitSetCache.ChangeStamp;
|
|
end;
|
|
|
|
// rebuild the define templates
|
|
HasTemplate:=CodeToolBoss.DefineTree.FindDefineTemplateByName(StdDefTemplFPC,true)<>nil;
|
|
if UnitSetChanged or not HasTemplate then
|
|
begin
|
|
{$IFDEF VerboseFPCSrcScan}
|
|
debugln(['TBuildManager.RescanCompilerDefines updating FPC template UnitSetChanged=',UnitSetChanged,' OldTemplateExists=',HasTemplate]);
|
|
{$ENDIF}
|
|
// create template for FPC settings
|
|
ADefTempl:=CreateFPCTemplate(UnitSetCache,nil);
|
|
AddTemplate(ADefTempl,false,
|
|
'NOTE: Could not create Define Template for Free Pascal Compiler');
|
|
end;
|
|
|
|
// create template for FPC source directory
|
|
if HasGUI then
|
|
begin
|
|
HasTemplate:=CodeToolBoss.DefineTree.FindDefineTemplateByName(StdDefTemplFPCSrc,true)<>nil;
|
|
if UnitSetChanged or not HasTemplate then
|
|
begin
|
|
{$IFDEF VerboseFPCSrcScan}
|
|
debugln(['TBuildManager.RescanCompilerDefines updating FPC SRC template UnitSetChanged=',UnitSetChanged,' OldTemplateExists=',HasTemplate]);
|
|
{$ENDIF}
|
|
ADefTempl:=CreateFPCSourceTemplate(UnitSetCache,nil);
|
|
AddTemplate(ADefTempl,false,lisNOTECouldNotCreateDefineTemplateForFreePascal);
|
|
end;
|
|
|
|
// create compiler macros for the lazarus sources
|
|
HasTemplate:=CodeToolBoss.DefineTree.FindDefineTemplateByName(StdDefTemplLazarusSources,true)<>nil;
|
|
if (not HasTemplate)
|
|
or (fBuildLazExtraOptions<>MiscellaneousOptions.BuildLazOpts.ExtraOptions)
|
|
then begin
|
|
{$IFDEF VerboseFPCSrcScan}
|
|
debugln(['TBuildManager.RescanCompilerDefines updating Lazarus source template OldTemplateExists=',HasTemplate,' OldExtraOptions="',fBuildLazExtraOptions,'" NewExtraOptions="',MiscellaneousOptions.BuildLazOpts.ExtraOptions,'"']);
|
|
{$ENDIF}
|
|
fBuildLazExtraOptions:=MiscellaneousOptions.BuildLazOpts.ExtraOptions;
|
|
ADefTempl:=CreateLazarusSourceTemplate(
|
|
'$('+ExternalMacroStart+'LazarusDir)',
|
|
'$('+ExternalMacroStart+'LCLWidgetType)',
|
|
fBuildLazExtraOptions,nil);
|
|
AddTemplate(ADefTempl,true,
|
|
lisNOTECouldNotCreateDefineTemplateForLazarusSources);
|
|
end;
|
|
end;
|
|
|
|
CodeToolBoss.DefineTree.ClearCache;
|
|
|
|
if AsyncScanFPCSrcDir<>'' then begin
|
|
// start scanning the fpc source directory in the background
|
|
{$IFDEF VerboseFPCSrcScan}
|
|
debugln(['TBuildManager.RescanCompilerDefines scanning fpc sources:',AsyncScanFPCSrcDir]);
|
|
{$ENDIF}
|
|
if FPCSrcScans=nil then
|
|
FFPCSrcScans:=TFPCSrcScans.Create(Self);
|
|
FPCSrcScans.Scan(AsyncScanFPCSrcDir);
|
|
end;
|
|
|
|
if not Quiet then begin
|
|
// check for common installation mistakes
|
|
if not PPUFilesAndCompilerMatch then exit;
|
|
if (UnitSetCache.GetCompilerKind=pcFPC) then begin
|
|
// check if at least one fpc config is there
|
|
if (UnitSetCache.GetFirstFPCCfg='') then begin
|
|
IgnorePath:='MissingFPCCfg_'+TargetOS+'-'+TargetCPU;
|
|
if Subtarget<>'' then
|
|
IgnorePath+='-'+Subtarget;
|
|
if (InputHistories<>nil) and (InputHistories.Ignores.Find(IgnorePath)=nil)
|
|
then begin
|
|
MsgResult:=IDEMessageDialog(lisCCOWarningCaption,
|
|
lisTheCurrentFPCHasNoConfigFileItWillProbablyMissSome,
|
|
mtWarning,[mbOk,mbIgnore]);
|
|
if MsgResult=mrIgnore then
|
|
InputHistories.Ignores.Add(IgnorePath,iiidIDERestart);
|
|
end;
|
|
end;
|
|
if not FoundSystemPPU then begin
|
|
// system.ppu is missing
|
|
IDEMessageDialog(lisCCOErrorCaption,
|
|
Format(lisTheProjectUsesTargetOSAndCPUTheSystemPpuForThisTar,
|
|
[TargetOS, TargetCPU, LineEnding, LineEnding]),
|
|
mtError,[mbOk]);
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
function TBuildManager.CompilerOnDiskChanged: boolean;
|
|
var
|
|
CfgCache: TPCTargetConfigCache;
|
|
begin
|
|
Result:=false;
|
|
if UnitSetCache=nil then exit;
|
|
CfgCache:=UnitSetCache.GetConfigCache(false);
|
|
if CfgCache=nil then exit;
|
|
Result:=CfgCache.NeedsUpdate;
|
|
end;
|
|
|
|
procedure TBuildManager.LoadCompilerDefinesCaches;
|
|
var
|
|
aFilename: String;
|
|
XMLConfig: TXMLConfig;
|
|
begin
|
|
aFilename:=AppendPathDelim(GetPrimaryConfigPath)+'fpcdefines.xml';
|
|
CopySecondaryConfigFile(ExtractFilename(aFilename));
|
|
if not FileExistsUTF8(aFilename) then exit;
|
|
try
|
|
XMLConfig:=TXMLConfig.Create(aFilename);
|
|
try
|
|
CodeToolBoss.CompilerDefinesCache.LoadFromXMLConfig(XMLConfig,'');
|
|
finally
|
|
XMLConfig.Free;
|
|
end;
|
|
except
|
|
on E: Exception do begin
|
|
if ConsoleVerbosity>=0 then
|
|
debugln(['Error: (lazarus) [LoadCompilerDefinesCaches] Error reading file '+aFilename+':'+E.Message]);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TBuildManager.SaveCompilerDefinesCaches;
|
|
var
|
|
aFilename: String;
|
|
XMLConfig: TXMLConfig;
|
|
begin
|
|
aFilename:=AppendPathDelim(GetPrimaryConfigPath)+'fpcdefines.xml';
|
|
//debugln(['TBuildManager.SaveCompilerDefinesCaches check if save needed ...']);
|
|
if FileExistsCached(aFilename)
|
|
and (not CodeToolBoss.CompilerDefinesCache.NeedsSave) then
|
|
exit;
|
|
//debugln(['TBuildManager.SaveCompilerDefinesCaches saving ...']);
|
|
try
|
|
XMLConfig:=TXMLConfig.CreateClean(aFilename);
|
|
try
|
|
CodeToolBoss.CompilerDefinesCache.SaveToXMLConfig(XMLConfig,'');
|
|
finally
|
|
XMLConfig.Free;
|
|
end;
|
|
except
|
|
on E: Exception do begin
|
|
if ConsoleVerbosity>=0 then
|
|
debugln(['Error: (lazarus) [SaveCompilerDefinesCaches] Error writing file '+aFilename+':'+E.Message]);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
function TBuildManager.DoCheckIfProjectNeedsCompilation(AProject: TProject;
|
|
out NeedBuildAllFlag: boolean; var Note: string): TModalResult;
|
|
var
|
|
DbgCap: String;
|
|
StateFilename: String;
|
|
StateFileAge: LongInt;
|
|
|
|
function EditorUnitInfoModified(AnUnitInfo: TUnitInfo): boolean;
|
|
var
|
|
EditComp: TSourceEditorInterface;
|
|
begin
|
|
Result:=false;
|
|
if AnUnitInfo=nil then exit;
|
|
if AnUnitInfo.EditorInfoCount=0 then exit;
|
|
EditComp:=AnUnitInfo.EditorInfo[0].EditorComponent;
|
|
Result:=(EditComp<>nil) and EditComp.Modified;
|
|
end;
|
|
|
|
function CheckNonProjectEditorFile(AnUnitInfo: TUnitInfo): boolean;
|
|
begin
|
|
Result:=false;
|
|
if AnUnitInfo.IsPartOfProject or AnUnitInfo.IsVirtual then exit;
|
|
|
|
if EditorUnitInfoModified(AnUnitInfo) then
|
|
begin
|
|
if ConsoleVerbosity>=0 then
|
|
DebugLn(DbgCap,'Editor unit modified in source editor ',AProject.IDAsString,' ',AnUnitInfo.Filename);
|
|
Note+='Editor unit "'+AnUnitInfo.Filename+'" has been modified in source editor.'+LineEnding;
|
|
exit(true);
|
|
end;
|
|
|
|
if not FileExistsCached(AnUnitInfo.Filename) then exit;
|
|
if StateFileAge>=FileAgeCached(AnUnitInfo.Filename) then exit;
|
|
if FilenameHasPascalExt(AnUnitInfo.Filename) then
|
|
begin
|
|
if (SearchDirectoryInMaskedSearchPath(AProject.CompilerOptions.GetUnitPath(false),
|
|
ExtractFilePath(AnUnitInfo.Filename))>0)
|
|
then begin
|
|
Result:=true;
|
|
if ConsoleVerbosity>=0 then
|
|
DebugLn(DbgCap,'Editor unit in project''s unit path has changed ',AProject.IDAsString,' ',AnUnitInfo.Filename);
|
|
Note+='Editor unit "'+AnUnitInfo.Filename+'" in project''s unit search path is newer than state file:'+LineEnding
|
|
+' File age="'+FileAgeToStr(FileAgeCached(AnUnitInfo.Filename))+'"'+LineEnding
|
|
+' State file age="'+FileAgeToStr(StateFileAge)+'"'+LineEnding
|
|
+' State file='+StateFilename+LineEnding;
|
|
exit(true);
|
|
end;
|
|
end;
|
|
if (SearchDirectoryInMaskedSearchPath(AProject.CompilerOptions.GetIncludePath(false),
|
|
ExtractFilePath(AnUnitInfo.Filename))>0)
|
|
then begin
|
|
Result:=true;
|
|
if ConsoleVerbosity>=0 then
|
|
DebugLn(DbgCap,'Editor file in project''s include path has changed ',AProject.IDAsString,' ',AnUnitInfo.Filename);
|
|
Note+='Editor file "'+AnUnitInfo.Filename+'" in project''s include search path is newer than state file:'+LineEnding
|
|
+' File age="'+FileAgeToStr(FileAgeCached(AnUnitInfo.Filename))+'"'+LineEnding
|
|
+' State file age="'+FileAgeToStr(StateFileAge)+'"'+LineEnding
|
|
+' State file='+StateFilename+LineEnding;
|
|
exit(true);
|
|
end;
|
|
end;
|
|
|
|
var
|
|
CompilerFilename, SrcFilename, LFMFilename, aTargetFilename: string;
|
|
AnUnitInfo: TUnitInfo;
|
|
IcoRes: TProjectIcon;
|
|
CompilerParams: TStrings;
|
|
begin
|
|
NeedBuildAllFlag:=false;
|
|
DbgCap:='Hint: (lazarus) Project needs building: ';
|
|
|
|
// get main source filename
|
|
if not AProject.IsVirtual then begin
|
|
SrcFilename:=CreateRelativePath(AProject.MainUnitInfo.Filename,
|
|
AProject.Directory);
|
|
end else begin
|
|
SrcFilename:=GetTestUnitFilename(AProject.MainUnitInfo);
|
|
end;
|
|
|
|
CompilerFilename:=AProject.GetCompilerFilename;
|
|
//DebugLn([DbgCap,'CompilerFilename="',CompilerFilename,'" CompilerPath="',AProject.CompilerOptions.CompilerPath,'"']);
|
|
// Note: use absolute paths, because some external tools resolve symlinked directories
|
|
CompilerParams :=
|
|
AProject.CompilerOptions.MakeCompilerParams([ccloAbsolutePaths]);
|
|
try
|
|
CompilerParams.Add(SrcFilename);
|
|
|
|
//DebugLn(DbgCap,'WorkingDir="',WorkingDir,'" SrcFilename="',SrcFilename,'" CompilerFilename="',CompilerFilename,'" CompilerParams="',MergeCmdLineParams(CompilerParams,TLazCompilerOptions.ConsoleParamsMax),'"');
|
|
|
|
// check state file
|
|
StateFilename:=AProject.GetStateFilename;
|
|
Result:=AProject.LoadStateFile(false);
|
|
if Result<>mrOk then exit; // read error and user aborted
|
|
if not (lpsfStateFileLoaded in AProject.StateFlags) then begin
|
|
if ConsoleVerbosity>=0 then
|
|
DebugLn(DbgCap,'No state file for ',AProject.IDAsString);
|
|
Note+='State file "'+StateFilename+'" of '+AProject.IDAsString+' is missing.'+LineEnding;
|
|
NeedBuildAllFlag:=true;
|
|
exit(mrYes);
|
|
end;
|
|
|
|
// check if build all (-B) is needed
|
|
if (AProject.LastCompilerFilename<>CompilerFilename)
|
|
or FPCParamForBuildAllHasChanged(AProject.LastCompilerParams,CompilerParams)
|
|
or ((AProject.LastCompilerFileDate>0)
|
|
and FileExistsCached(CompilerFilename)
|
|
and (FileAgeCached(CompilerFilename)<>AProject.LastCompilerFileDate))
|
|
then
|
|
NeedBuildAllFlag:=true;
|
|
|
|
StateFileAge:=FileAgeCached(StateFilename);
|
|
|
|
// check main source file
|
|
AnUnitInfo:=AProject.MainUnitInfo;
|
|
if EditorUnitInfoModified(AnUnitInfo) then
|
|
begin
|
|
if ConsoleVerbosity>=0 then
|
|
DebugLn(DbgCap,'Main src modified in source editor ',AProject.IDAsString,' ',AnUnitInfo.Filename);
|
|
Note+='Main source "'+AnUnitInfo.Filename+'" has been modified in source editor.'+LineEnding;
|
|
exit(mrYes);
|
|
end;
|
|
if FileExistsCached(SrcFilename) and (StateFileAge<FileAgeCached(SrcFilename)) then
|
|
begin
|
|
if ConsoleVerbosity>=0 then
|
|
DebugLn(DbgCap,'SrcFile outdated ',AProject.IDAsString);
|
|
Note+='Source file "'+SrcFilename+'" of '+AProject.IDAsString+' outdated:'+LineEnding
|
|
+' Source age='+FileAgeToStr(FileAgeCached(SrcFilename))+LineEnding
|
|
+' State file age='+FileAgeToStr(StateFileAge)+LineEnding
|
|
+' State file='+StateFilename+LineEnding;
|
|
exit(mrYes);
|
|
end;
|
|
|
|
// check compiler and params
|
|
if CompilerFilename<>AProject.LastCompilerFilename then begin
|
|
if ConsoleVerbosity>=0 then begin
|
|
DebugLn(DbgCap,'Compiler filename changed for ',AProject.IDAsString);
|
|
DebugLn(' Old="',AProject.LastCompilerFilename,'"');
|
|
DebugLn(' Now="',CompilerFilename,'"');
|
|
end;
|
|
Note+='Compiler filename changed for '+AProject.IDAsString+':'+LineEnding
|
|
+' Old="'+AProject.LastCompilerFilename+'"'+LineEnding
|
|
+' Now="'+CompilerFilename+'"'+LineEnding
|
|
+' State file='+StateFilename+LineEnding;
|
|
exit(mrYes);
|
|
end;
|
|
if not FileExistsCached(CompilerFilename) then begin
|
|
if ConsoleVerbosity>=0 then begin
|
|
DebugLn(DbgCap,'Compiler file not found for ',AProject.IDAsString);
|
|
DebugLn(' File="',CompilerFilename,'"');
|
|
end;
|
|
Note+='Compiler file "'+CompilerFilename+'" not found for '+AProject.IDAsString+'.'+LineEnding;
|
|
exit(mrYes);
|
|
end;
|
|
if FileAgeCached(CompilerFilename)<>AProject.LastCompilerFileDate then begin
|
|
if ConsoleVerbosity>=0 then begin
|
|
DebugLn(DbgCap,'Compiler file changed for ',AProject.IDAsString);
|
|
DebugLn(' File="',CompilerFilename,'"');
|
|
end;
|
|
Note+='Compiler file "'+CompilerFilename+'" for '+AProject.IDAsString+' changed:'+LineEnding
|
|
+' Old="'+FileAgeToStr(AProject.LastCompilerFileDate)+'"'+LineEnding
|
|
+' Now="'+FileAgeToStr(FileAgeCached(CompilerFilename))+'"'+LineEnding
|
|
+' State file='+StateFilename+LineEnding;
|
|
exit(mrYes);
|
|
end;
|
|
if not CompilerParams.Equals(AProject.LastCompilerParams) then begin
|
|
if ConsoleVerbosity>=0 then begin
|
|
DebugLn(DbgCap,'Compiler params changed for ',AProject.IDAsString);
|
|
DebugLn(' Old="',MergeCmdLineParams(AProject.LastCompilerParams,TLazCompilerOptions.ConsoleParamsMax),'"');
|
|
DebugLn(' Now="',MergeCmdLineParams(CompilerParams,TLazCompilerOptions.ConsoleParamsMax),'"');
|
|
end;
|
|
Note+='Compiler params changed for '+AProject.IDAsString+':'+LineEnding
|
|
+' Old="'+MergeCmdLineParams(AProject.LastCompilerParams,TLazCompilerOptions.ConsoleParamsMax)+'"'+LineEnding
|
|
+' Now="'+MergeCmdLineParams(CompilerParams,TLazCompilerOptions.ConsoleParamsMax)+'"'+LineEnding
|
|
+' State file='+StateFilename+LineEnding;
|
|
exit(mrYes);
|
|
end;
|
|
|
|
// compiler and parameters are the same
|
|
// => it is possible to quick compile without -B
|
|
NeedBuildAllFlag:=false;
|
|
|
|
if not AProject.LastCompileComplete then begin
|
|
if ConsoleVerbosity>=0 then
|
|
DebugLn(DbgCap,'Compile was incomplete for ',AProject.IDAsString);
|
|
Note+='Last compile was incomplete.'+LineEnding
|
|
+' State file='+StateFilename+LineEnding;
|
|
exit(mrYes);
|
|
end;
|
|
|
|
// check all direct required packages (indirect required were already compiled above)
|
|
Result:=PackageGraph.CheckCompileNeedDueToDependencies(AProject,
|
|
AProject.FirstRequiredDependency,
|
|
not (pfUseDesignTimePackages in AProject.Flags),
|
|
StateFileAge,Note);
|
|
if Result<>mrNo then exit;
|
|
|
|
// check project files
|
|
for TLazProjectFile(AnUnitInfo) in AProject.UnitsBelongingToProject do begin
|
|
if EditorUnitInfoModified(AnUnitInfo) then
|
|
begin
|
|
if ConsoleVerbosity>=0 then
|
|
DebugLn(DbgCap,'Project file modified in source editor ',AProject.IDAsString,' ',AnUnitInfo.Filename);
|
|
Note+='Project file "'+AnUnitInfo.Filename+'" has been modified in source editor.'+LineEnding;
|
|
exit(mrYes);
|
|
end;
|
|
if (not AnUnitInfo.IsVirtual) and FileExistsCached(AnUnitInfo.Filename) then
|
|
begin
|
|
if (StateFileAge<FileAgeCached(AnUnitInfo.Filename)) then begin
|
|
if ConsoleVerbosity>=0 then
|
|
DebugLn(DbgCap,'Src has changed ',AProject.IDAsString,' ',AnUnitInfo.Filename);
|
|
Note+='File "'+AnUnitInfo.Filename+'" of '+AProject.IDAsString+' is newer than state file:'+LineEnding
|
|
+' File age="'+FileAgeToStr(FileAgeCached(AnUnitInfo.Filename))+'"'+LineEnding
|
|
+' State file age="'+FileAgeToStr(StateFileAge)+'"'+LineEnding
|
|
+' State file='+StateFilename+LineEnding;
|
|
exit(mrYes);
|
|
end;
|
|
if AnUnitInfo.ComponentName<>'' then begin
|
|
LFMFilename:=ChangeFileExt(AnUnitInfo.Filename,'.lfm');
|
|
if FileExistsCached(LFMFilename)
|
|
and (StateFileAge<FileAgeCached(LFMFilename)) then begin
|
|
if ConsoleVerbosity>=0 then
|
|
DebugLn(DbgCap,'LFM has changed ',AProject.IDAsString,' ',LFMFilename);
|
|
Note+='File "'+LFMFilename+'" of '+AProject.IDAsString+' is newer than state file:'+LineEnding
|
|
+' File age="'+FileAgeToStr(FileAgeCached(LFMFilename))+'"'+LineEnding
|
|
+' State file age="'+FileAgeToStr(StateFileAge)+'"'+LineEnding
|
|
+' State file='+StateFilename+LineEnding;
|
|
exit(mrYes);
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
// check all open editor files in unit/include path (maybe the user forgot
|
|
// to add them to the project)
|
|
for TLazProjectFile(AnUnitInfo) in AProject.UnitsWithEditorIndex do begin
|
|
if CheckNonProjectEditorFile(AnUnitInfo) then
|
|
exit(mrYes);
|
|
end;
|
|
|
|
// check project resources
|
|
IcoRes:=TProjectIcon(AProject.ProjResources[TProjectIcon]);
|
|
if (IcoRes<>nil) and (not IcoRes.IsEmpty)
|
|
and FilenameIsAbsolute(IcoRes.IcoFileName)
|
|
and FileExistsCached(IcoRes.IcoFileName)
|
|
and (StateFileAge<FileAgeCached(IcoRes.IcoFileName)) then begin
|
|
if ConsoleVerbosity>=0 then
|
|
debugln([DbgCap,'icon has changed ',
|
|
AProject.IDAsString,' "',IcoRes.IcoFileName,'"']);
|
|
Note+='Project''s ico file "'+IcoRes.IcoFileName+'" is newer than state file:'+LineEnding
|
|
+' File age="'+FileAgeToStr(FileAgeCached(IcoRes.IcoFileName))+'"'+LineEnding
|
|
+' State file age="'+FileAgeToStr(StateFileAge)+'"'+LineEnding
|
|
+' State file='+StateFilename+LineEnding;
|
|
exit(mrYes);
|
|
end;
|
|
|
|
// check target file
|
|
aTargetFilename:=AProject.CompilerOptions.CreateTargetFilename;
|
|
//debugln(['TBuildManager.DoCheckIfProjectNeedsCompilation aTargetFilename=',aTargetFilename]);
|
|
if (aTargetFilename<>'') and not FileExistsCached(aTargetFilename) then begin
|
|
if ConsoleVerbosity>=0 then
|
|
debugln([DbgCap,'missing target file "',aTargetFilename,'"']);
|
|
Note+='Project''s target file "'+aTargetFilename+'" is missing.';
|
|
exit(mrYes);
|
|
end;
|
|
|
|
finally
|
|
CompilerParams.Free;
|
|
end;
|
|
|
|
if not HasGUI then
|
|
debugln(['Hint: (lazarus) Build Project: nothing to do.']);
|
|
Result:=mrNo;
|
|
end;
|
|
|
|
procedure TBuildManager.DoOnRescanFPCDirectoryCache(Sender: TObject);
|
|
var
|
|
Files: TStringList;
|
|
FPCSrcDir: string;
|
|
begin
|
|
FPCSrcDir := EnvironmentOptions.GetParsedFPCSourceDirectory;
|
|
Files := GatherFilesInFPCSources(FPCSrcDir, nil);
|
|
if Files<>nil then
|
|
try
|
|
ApplyFPCSrcFiles(FPCSrcDir, Files);
|
|
finally
|
|
Files.Free;
|
|
end;
|
|
end;
|
|
|
|
function TBuildManager.CheckAmbiguousSources(const AFilename: string;
|
|
Compiling: boolean): TModalResult;
|
|
|
|
function DeleteAmbiguousFile(const AmbiguousFilename: string): TModalResult;
|
|
begin
|
|
if not DeleteFileUTF8(AmbiguousFilename) then begin
|
|
Result:=IDEMessageDialog(lisErrorDeletingFile,
|
|
Format(lisUnableToDeleteAmbiguousFile, [AmbiguousFilename]),
|
|
mtError,[mbOk,mbAbort]);
|
|
end else
|
|
Result:=mrOk;
|
|
end;
|
|
|
|
function RenameAmbiguousFile(const AmbiguousFilename: string): TModalResult;
|
|
var
|
|
NewFilename: string;
|
|
begin
|
|
NewFilename:=AmbiguousFilename+'.ambiguous';
|
|
if not RenameFileUTF8(AmbiguousFilename,NewFilename) then
|
|
begin
|
|
Result:=IDEMessageDialog(lisErrorRenamingFile,
|
|
Format(lisUnableToRenameAmbiguousFileTo,[AmbiguousFilename,LineEnding,NewFilename]),
|
|
mtError,[mbOk,mbAbort]);
|
|
end else
|
|
Result:=mrOk;
|
|
end;
|
|
|
|
function AddCompileWarning(const AmbiguousFilename: string): TModalResult;
|
|
begin
|
|
Result:=mrOk;
|
|
if Compiling then begin
|
|
IDEMessagesWindow.AddCustomMessage(mluError,
|
|
Format('ambiguous file found: "%s". Source file is: "%s"',
|
|
[AmbiguousFilename, AFilename]));
|
|
end;
|
|
end;
|
|
|
|
function CheckFile(const AmbiguousFilename: string): TModalResult;
|
|
begin
|
|
Result:=mrOk;
|
|
if CompareFilenames(AFilename,AmbiguousFilename)=0 then exit;
|
|
if not FileExistsCached(AmbiguousFilename) then exit;
|
|
if Compiling then begin
|
|
Result:=AddCompileWarning(AmbiguousFilename);
|
|
exit;
|
|
end;
|
|
case EnvironmentOptions.AmbiguousFileAction of
|
|
afaAsk:
|
|
begin
|
|
Result:=IDEMessageDialog(lisAmbiguousFileFound,
|
|
Format(lisThereIsAFileWithTheSameNameAndASimilarExtension,
|
|
[LineEnding, AFilename, LineEnding, AmbiguousFilename, LineEnding+LineEnding]),
|
|
mtWarning,[mbYes,mbIgnore,mbAbort]);
|
|
case Result of
|
|
mrYes: Result:=DeleteAmbiguousFile(AmbiguousFilename);
|
|
mrIgnore: Result:=mrOk;
|
|
end;
|
|
end;
|
|
|
|
afaAutoDelete:
|
|
Result:=DeleteAmbiguousFile(AmbiguousFilename);
|
|
|
|
afaAutoRename:
|
|
Result:=RenameAmbiguousFile(AmbiguousFilename);
|
|
|
|
afaWarnOnCompile:
|
|
Result:=AddCompileWarning(AmbiguousFilename);
|
|
|
|
else
|
|
Result:=mrOk;
|
|
end;
|
|
end;
|
|
|
|
var
|
|
LowExt: string;
|
|
i: integer;
|
|
begin
|
|
Result:=mrOk;
|
|
if EnvironmentOptions.AmbiguousFileAction=afaIgnore then exit;
|
|
if (EnvironmentOptions.AmbiguousFileAction=afaWarnOnCompile)
|
|
and not Compiling then exit;
|
|
|
|
if FilenameHasPascalExt(AFilename) then begin
|
|
LowExt:=lowercase(ExtractFileExt(AFilename));
|
|
for i:=Low(PascalFileExt) to High(PascalFileExt) do begin
|
|
if LowExt<>PascalFileExt[i] then begin
|
|
Result:=CheckFile(ChangeFileExt(AFilename,PascalFileExt[i]));
|
|
if Result<>mrOk then exit;
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
function TBuildManager.DeleteAmbiguousFiles(const Filename: string): TModalResult;
|
|
var
|
|
ADirectory: String;
|
|
FileInfo: TSearchRec;
|
|
ShortFilename: String;
|
|
CurFilename: String;
|
|
IsPascalUnit: Boolean;
|
|
AUnitName: String;
|
|
begin
|
|
if EnvironmentOptions.AmbiguousFileAction=afaIgnore then exit(mrOK);
|
|
if EnvironmentOptions.AmbiguousFileAction in [afaAsk,afaAutoDelete,afaAutoRename]
|
|
then begin
|
|
ADirectory:=AppendPathDelim(ExtractFilePath(Filename));
|
|
if FindFirstUTF8(ADirectory+GetAllFilesMask,faAnyFile,FileInfo)=0 then
|
|
begin
|
|
try
|
|
ShortFilename:=ExtractFileName(Filename);
|
|
IsPascalUnit:=FilenameHasPascalExt(ShortFilename);
|
|
AUnitName:=ExtractFilenameOnly(ShortFilename);
|
|
repeat
|
|
if (FileInfo.Name='.') or (FileInfo.Name='..')
|
|
or (FileInfo.Name='')
|
|
or ((FileInfo.Attr and faDirectory)<>0) then continue;
|
|
if CompareFilenames(ShortFilename,FileInfo.Name)=0 then continue;
|
|
|
|
if (SysUtils.CompareText(ShortFilename,FileInfo.Name)=0)
|
|
then begin
|
|
// same name different case => ambiguous
|
|
end else if IsPascalUnit and FilenameHasPascalExt(FileInfo.Name)
|
|
and (SysUtils.CompareText(AUnitName,ExtractFilenameOnly(FileInfo.Name))=0)
|
|
then begin
|
|
// same unit name => ambiguous
|
|
end else
|
|
continue;
|
|
|
|
CurFilename:=ADirectory+FileInfo.Name;
|
|
if EnvironmentOptions.AmbiguousFileAction=afaAsk then begin
|
|
if IDEMessageDialog(lisDeleteAmbiguousFile,
|
|
Format(lisAmbiguousFileFoundThisFileCanBeMistakenWithDelete,
|
|
[CurFilename, LineEnding, ShortFilename, LineEnding+LineEnding]),
|
|
mtConfirmation,[mbYes,mbNo])<>mrYes
|
|
then continue;
|
|
end;
|
|
if EnvironmentOptions.AmbiguousFileAction in [afaAutoDelete,afaAsk]
|
|
then begin
|
|
Result:=DeleteFileInteractive(CurFilename);
|
|
if not (Result in [mrOK,mrIgnore]) then exit(mrCancel);
|
|
end else if EnvironmentOptions.AmbiguousFileAction=afaAutoRename then
|
|
begin
|
|
Result:=BackupFileForWrite(CurFilename);
|
|
if not (Result in [mrOK,mrIgnore]) then exit(mrCancel);
|
|
if FileExistsUTF8(CurFilename) then begin
|
|
Result:=DeleteFileInteractive(CurFilename);
|
|
if not (Result in [mrOK,mrIgnore]) then exit(mrCancel);
|
|
end;
|
|
end;
|
|
until FindNextUTF8(FileInfo)<>0;
|
|
finally
|
|
FindCloseUTF8(FileInfo);
|
|
end;
|
|
end;
|
|
end;
|
|
Result:=mrOk;
|
|
end;
|
|
|
|
{-------------------------------------------------------------------------------
|
|
function TBuildManager.CheckUnitPathForAmbiguousPascalFiles(
|
|
const BaseDir, TheUnitPath, CompiledExt, ContextDescription: string
|
|
): TModalResult;
|
|
|
|
Collect all pascal files and all compiled units in the unit path and check
|
|
for ambiguous files. For example: doubles.
|
|
-------------------------------------------------------------------------------}
|
|
function TBuildManager.CheckUnitPathForAmbiguousPascalFiles(const BaseDir,
|
|
TheUnitPath, CompiledExt, ContextDescription: string): TModalResult;
|
|
|
|
procedure FreeUnitTree(var Tree: TAVLTree);
|
|
var
|
|
ANode: TAVLTreeNode;
|
|
AnUnitFile: PUnitFile;
|
|
begin
|
|
if Tree<>nil then begin
|
|
ANode:=Tree.FindLowest;
|
|
while ANode<>nil do begin
|
|
AnUnitFile:=PUnitFile(ANode.Data);
|
|
Dispose(AnUnitFile);
|
|
ANode:=Tree.FindSuccessor(ANode);
|
|
end;
|
|
Tree.Free;
|
|
Tree:=nil;
|
|
end;
|
|
end;
|
|
|
|
var
|
|
SourceUnitTree, CompiledUnitTree: TAVLTree;
|
|
ANode: TAVLTreeNode;
|
|
CurUnitName: String;
|
|
CurFilename: String;
|
|
AnUnitFile: PUnitFile;
|
|
CurUnitTree: TAVLTree;
|
|
UnitPath: String;
|
|
IgnoreAll: Boolean;
|
|
Files: TFilenameToStringTree;
|
|
Item: PStringToStringItem;
|
|
begin
|
|
Result:=mrOk;
|
|
UnitPath:=TrimSearchPath(TheUnitPath,BaseDir,true);
|
|
|
|
SourceUnitTree:=TAVLTree.Create(TListSortCompare(@CompareUnitFiles));
|
|
CompiledUnitTree:=TAVLTree.Create(TListSortCompare(@CompareUnitFiles));
|
|
Files:=TFilenameToStringTree.Create(true);
|
|
try
|
|
// collect all units (.pas, .pp, compiled units)
|
|
CollectFilesInSearchPath(UnitPath,Files,'Unit');
|
|
IgnoreAll:=false;
|
|
for Item in Files do
|
|
begin
|
|
CurFilename:=Item^.Name;
|
|
if FilenameHasPascalExt(CurFilename) then
|
|
CurUnitTree:=SourceUnitTree
|
|
else if FilenameExtIs(CurFilename,CompiledExt,true) then
|
|
CurUnitTree:=CompiledUnitTree
|
|
else
|
|
continue;
|
|
CurUnitName:=ExtractFilenameOnly(CurFilename);
|
|
if not IsValidIdent(CurUnitName) then
|
|
continue;
|
|
//DebugLn(['TBuildManager.CheckUnitPathForAmbiguousPascalFiles ',CurUnitName,' ',CurFilename]);
|
|
// check if unit already found
|
|
ANode:=CurUnitTree.FindKey(PChar(CurUnitName),
|
|
TListSortCompare(@CompareUnitNameAndUnitFile));
|
|
if (ANode<>nil) and (not IgnoreAll) then begin
|
|
if ConsoleVerbosity>=0 then
|
|
DebugLn(['Note: (lazarus) [TBuildManager.CheckUnitPathForAmbiguousPascalFiles] CurUnitName="',CurUnitName,'" CurFilename="',CurFilename,'" OtherUnitName="',PUnitFile(ANode.Data)^.FileUnitName,'" OtherFilename="',PUnitFile(ANode.Data)^.Filename,'"']);
|
|
// pascal unit exists twice
|
|
Result:=IDEQuestionDialog(lisAmbiguousUnitFound,
|
|
Format(lisTheUnitExistsTwiceInTheUnitPathOfThe,[CurUnitName,ContextDescription])
|
|
+LineEnding
|
|
+LineEnding
|
|
+'1. "'+PUnitFile(ANode.Data)^.Filename+'"'+LineEnding
|
|
+'2. "'+CurFilename+'"'+LineEnding
|
|
+LineEnding
|
|
+lisHintCheckIfTwoPackagesContainAUnitWithTheSameName,
|
|
mtWarning, [mrIgnore,
|
|
mrYesToAll, lisIgnoreAll,
|
|
mrAbort]);
|
|
case Result of
|
|
mrIgnore: ;
|
|
mrYesToAll: IgnoreAll:=true;
|
|
else exit;
|
|
end;
|
|
end;
|
|
// add unit to tree
|
|
New(AnUnitFile);
|
|
AnUnitFile^.FileUnitName:=CurUnitName;
|
|
AnUnitFile^.Filename:=CurFilename;
|
|
CurUnitTree.Add(AnUnitFile);
|
|
end;
|
|
finally
|
|
// clean up
|
|
Files.Free;
|
|
FreeUnitTree(SourceUnitTree);
|
|
FreeUnitTree(CompiledUnitTree);
|
|
end;
|
|
Result:=mrOk;
|
|
end;
|
|
|
|
function TBuildManager.CreateProjectApplicationBundle: Boolean;
|
|
var
|
|
TargetExeName: string;
|
|
begin
|
|
Result := False;
|
|
if Project1.MainUnitInfo = nil then
|
|
Exit;
|
|
TargetExeName := Project1.CompilerOptions.CreateTargetFilename;
|
|
|
|
if not (CreateApplicationBundle(TargetExeName, Project1.GetTitle, True, Project1) in
|
|
[mrOk, mrIgnore]) then
|
|
Exit;
|
|
if not (CreateAppBundleSymbolicLink(TargetExeName, True) in [mrOk, mrIgnore]) then
|
|
Exit;
|
|
Result := True;
|
|
end;
|
|
|
|
function TBuildManager.BackupFileForWrite(const Filename: string): TModalResult;
|
|
var BackupFilename, CounterFilename: string;
|
|
AText,ACaption:string;
|
|
BackupInfo: TBackupInfo;
|
|
FilePath, FileNameOnly, FileExt, SubDir: string;
|
|
i: integer;
|
|
IsPartOfProject: boolean;
|
|
begin
|
|
Result:=mrOk;
|
|
SubDir:='';
|
|
BackupFilename:='';
|
|
if not (FileExistsUTF8(Filename)) then exit;
|
|
// check if file in lpi
|
|
IsPartOfProject:=(Project1<>nil)
|
|
and (Project1.FindFile(Filename,[pfsfOnlyProjectFiles])<>nil);
|
|
// check if file in source directory of project
|
|
if (not IsPartOfProject) and (Project1<>nil)
|
|
and (SearchDirectoryInMaskedSearchPath(Project1.SourceDirectories.CreateSearchPathFromAllFiles,
|
|
ExtractFilePath(Filename))>0)
|
|
then
|
|
IsPartOfProject:=true;
|
|
// check options
|
|
if IsPartOfProject then
|
|
BackupInfo:=EnvironmentOptions.BackupInfoProjectFiles
|
|
else
|
|
BackupInfo:=EnvironmentOptions.BackupInfoOtherFiles;
|
|
if (BackupInfo.BackupType=bakNone)
|
|
or ((BackupInfo.BackupType=bakSameName) and (BackupInfo.SubDirectory='')) then
|
|
exit;
|
|
// create backup
|
|
FilePath:=ExtractFilePath(Filename);
|
|
FileExt:=ExtractFileExt(Filename);
|
|
FileNameOnly:=ExtractFilenameOnly(Filename);
|
|
SubDir:=BackupInfo.SubDirectory;
|
|
if BackupInfo.SubDirectory<>'' then
|
|
GlobalMacroList.SubstituteStr(SubDir);
|
|
if SubDir<>'' then begin
|
|
if not FilenameIsAbsolute(SubDir) then
|
|
SubDir:=TrimFilename(FilePath+SubDir);
|
|
Result:=ForceDirectoryInteractive(SubDir,[mbRetry,mbIgnore]);
|
|
if Result=mrCancel then exit;
|
|
if Result=mrIgnore then Result:=mrOk;
|
|
end;
|
|
if BackupInfo.BackupType in
|
|
[bakSymbolInFront,bakSymbolBehind,bakUserDefinedAddExt,bakSameName] then
|
|
begin
|
|
case BackupInfo.BackupType of
|
|
bakSymbolInFront:
|
|
BackupFilename:=FileNameOnly+'.~'+copy(FileExt,2,length(FileExt)-1);
|
|
bakSymbolBehind:
|
|
BackupFilename:=FileNameOnly+FileExt+'~';
|
|
bakUserDefinedAddExt:
|
|
BackupFilename:=FileNameOnly+FileExt+'.'+BackupInfo.AdditionalExtension;
|
|
bakSameName:
|
|
BackupFilename:=FileNameOnly+FileExt;
|
|
end;
|
|
if BackupInfo.SubDirectory<>'' then
|
|
BackupFilename:=AppendPathDelim(SubDir)+BackupFilename
|
|
else
|
|
BackupFilename:=FilePath+BackupFilename;
|
|
// remove old backup file
|
|
repeat
|
|
if FileExistsUTF8(BackupFilename) then begin
|
|
if not DeleteFileUTF8(BackupFilename) then begin
|
|
ACaption:=lisDeleteFileFailed;
|
|
AText:=Format(lisUnableToRemoveOldBackupFile,[BackupFilename]);
|
|
Result:=IDEMessageDialog(ACaption,AText,mtError,[mbAbort,mbRetry,mbIgnore]);
|
|
if Result=mrAbort then exit;
|
|
if Result=mrIgnore then Result:=mrOk;
|
|
end;
|
|
end;
|
|
until Result<>mrRetry;
|
|
end else begin
|
|
// backup with counter
|
|
if BackupInfo.SubDirectory<>'' then
|
|
BackupFilename:=AppendPathDelim(SubDir)+FileNameOnly+FileExt+';'
|
|
else
|
|
BackupFilename:=Filename+';';
|
|
if BackupInfo.MaxCounter<=0 then begin
|
|
// search first non existing backup filename
|
|
i:=1;
|
|
while FileExistsUTF8(BackupFilename+IntToStr(i)) do inc(i);
|
|
BackupFilename:=BackupFilename+IntToStr(i);
|
|
end else begin
|
|
// rename all backup files (increase number)
|
|
i:=1;
|
|
while FileExistsUTF8(BackupFilename+IntToStr(i))
|
|
and (i<=BackupInfo.MaxCounter) do inc(i);
|
|
if i>BackupInfo.MaxCounter then begin
|
|
dec(i);
|
|
CounterFilename:=BackupFilename+IntToStr(BackupInfo.MaxCounter);
|
|
// remove old backup file
|
|
repeat
|
|
if FileExistsUTF8(CounterFilename) then begin
|
|
if not DeleteFileUTF8(CounterFilename) then begin
|
|
ACaption:=lisDeleteFileFailed;
|
|
AText:=Format(lisUnableToRemoveOldBackupFile,[CounterFilename]);
|
|
Result:=IDEMessageDialog(ACaption,AText,mtError,[mbAbort,mbRetry,mbIgnore]);
|
|
if Result=mrAbort then exit;
|
|
if Result=mrIgnore then Result:=mrOk;
|
|
end;
|
|
end;
|
|
until Result<>mrRetry;
|
|
end;
|
|
// rename all old backup files
|
|
dec(i);
|
|
while i>=1 do begin
|
|
repeat
|
|
if not RenameFileUTF8(BackupFilename+IntToStr(i),
|
|
BackupFilename+IntToStr(i+1)) then
|
|
begin
|
|
ACaption:=lisRenameFileFailed;
|
|
AText:=Format(lisUnableToRenameFileTo,
|
|
[BackupFilename+IntToStr(i), BackupFilename+IntToStr(i+1)]);
|
|
Result:=IDEMessageDialog(ACaption,AText,mtError,
|
|
[mbAbort,mbRetry,mbIgnore]);
|
|
if Result=mrAbort then exit;
|
|
if Result=mrIgnore then Result:=mrOk;
|
|
end;
|
|
until Result<>mrRetry;
|
|
dec(i);
|
|
end;
|
|
BackupFilename:=BackupFilename+'1';
|
|
end;
|
|
end;
|
|
// backup file
|
|
repeat
|
|
if not IDEProcs.BackupFileForWrite(Filename, BackupFilename) then
|
|
begin
|
|
ACaption := lisBackupFileFailed;
|
|
AText := Format(lisUnableToBackupFileTo, [Filename, BackupFilename]);
|
|
Result := IDEMessageDialog(ACaption,AText,mterror,[mbabort,mbretry,mbignore]);
|
|
if Result = mrAbort then exit;
|
|
if Result = mrIgnore then Result := mrOk;
|
|
end
|
|
else
|
|
Result := mrOk;
|
|
until Result <> mrRetry;
|
|
end;
|
|
|
|
function TBuildManager.GetResourceType(AnUnitInfo: TUnitInfo): TResourceType;
|
|
begin
|
|
if AnUnitInfo.Source = nil then
|
|
AnUnitInfo.Source := CodeToolBoss.LoadFile(AnUnitInfo.Filename, True, False);
|
|
if (AnUnitInfo.Source <> nil) and GuessResourceType(AnUnitInfo.Source, Result) then
|
|
begin
|
|
// guessed from source
|
|
end
|
|
else
|
|
if AnUnitInfo.IsPartOfProject then
|
|
begin
|
|
// use project resource type
|
|
Result := AnUnitInfo.Project.ProjResources.ResourceType;
|
|
end
|
|
else
|
|
Result := rtLRS;
|
|
end;
|
|
|
|
function TBuildManager.FindLRSFilename(AnUnitInfo: TUnitInfo;
|
|
UseDefaultIfNotFound: boolean): string;
|
|
begin
|
|
if AnUnitInfo.IsVirtual then begin
|
|
Result:='';
|
|
end else begin
|
|
Result:=ExtractFileNameOnly(AnUnitInfo.Filename)+ResourceFileExt;
|
|
Result:=SearchFileInSearchPath(Result,'',
|
|
CodeToolBoss.GetIncludePathForDirectory(ExtractFilePath(AnUnitInfo.Filename)),
|
|
[TSPSearchFileFlag.SearchLoUpCase]);
|
|
end;
|
|
if (Result='') and UseDefaultIfNotFound then
|
|
Result:=GetDefaultLRSFilename(AnUnitInfo);
|
|
end;
|
|
|
|
function TBuildManager.GetDefaultLRSFilename(AnUnitInfo: TUnitInfo): string;
|
|
var
|
|
OutputDir: String;
|
|
begin
|
|
if AnUnitInfo.IsPartOfProject
|
|
and (not AnUnitInfo.Project.IsVirtual)
|
|
and (pfLRSFilesInOutputDirectory in Project1.Flags) then begin
|
|
OutputDir:=AnUnitInfo.Project.GetOutputDirectory;
|
|
if OutputDir<>'' then begin
|
|
Result:=AppendPathDelim(OutputDir)
|
|
+ExtractFileNameOnly(AnUnitInfo.Filename)+ResourceFileExt;
|
|
exit;
|
|
end;
|
|
end;
|
|
Result:=ChangeFileExt(AnUnitInfo.Filename,ResourceFileExt);
|
|
end;
|
|
|
|
function TBuildManager.UpdateLRSFromLFM(AnUnitInfo: TUnitInfo;
|
|
ShowAbort: boolean): TModalResult;
|
|
var
|
|
LFMFilename: String;
|
|
LRSFilename: String;
|
|
Dir: String;
|
|
begin
|
|
Result:=mrOk;
|
|
// check if there is a .lfm file
|
|
if AnUnitInfo.IsVirtual then exit;
|
|
LFMFilename:=ChangeFileExt(AnUnitInfo.Filename,'.lfm');
|
|
if not FileExistsCached(LFMFilename) then exit(mrOk);
|
|
// check if there is a .lrs file
|
|
LRSFilename:=FindLRSFilename(AnUnitInfo,true);
|
|
if LRSFilename=LFMFilename then exit;
|
|
// check if .lrs file is newer than .lfm file
|
|
if FileExistsUTF8(LRSFilename)
|
|
and (FileAgeUTF8(LFMFilename)<=FileAgeUTF8(LRSFilename))
|
|
then exit;
|
|
// the .lrs file does not exist, or is older than the .lfm file
|
|
// -> update .lrs file
|
|
Dir:=ExtractFilePath(LRSFilename);
|
|
Result:=ForceDirectoryInteractive(Dir,[mbRetry]);
|
|
if Result<>mrOk then exit;
|
|
Result:=ConvertLFMToLRSFileInteractive(LFMFilename,LRSFilename,ShowAbort);
|
|
end;
|
|
|
|
function TBuildManager.UpdateProjectAutomaticFiles(TestDir: string): TModalResult;
|
|
var
|
|
AnUnitInfo: TUnitInfo;
|
|
begin
|
|
Result:=mrOk;
|
|
// update project resource
|
|
if Project1.MainUnitID>=0 then
|
|
Project1.ProjResources.Regenerate(Project1.MainFileName, False, True, TestDir);
|
|
for TLazProjectFile(AnUnitInfo) in Project1.UnitsBelongingToProject do begin
|
|
if AnUnitInfo.HasResources then begin
|
|
case GetResourceType(AnUnitInfo) of
|
|
rtLRS:
|
|
begin
|
|
Result := UpdateLRSFromLFM(AnUnitInfo,false);
|
|
if Result = mrIgnore then Result:=mrOk;
|
|
if Result <> mrOk then exit;
|
|
end;
|
|
rtRes: // This fixed encoding of source files but only if rtRes type. Why?
|
|
begin // Source was read in every time although encoding is correct most of times.
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
function TBuildManager.MacroFuncMakeExe(const Filename: string;
|
|
const Data: PtrInt; var Abort: boolean): string;
|
|
var
|
|
CommaPos: SizeInt;
|
|
CurTargetOS: String;
|
|
CurFilename: String;
|
|
begin
|
|
CurFilename:=Filename;
|
|
CommaPos:=System.Pos(',',CurFilename);
|
|
CurTargetOS:='';
|
|
if CommaPos>1 then begin
|
|
// makeexe(targetos,filename)
|
|
CurTargetOS:=UTF8LowerCase(LeftStr(CurFilename,CommaPos-1));
|
|
if IsValidIdent(CurTargetOS) then begin
|
|
if CurTargetOS='ide' then
|
|
CurTargetOS:=GetCompiledTargetOS;
|
|
System.Delete(CurFilename,1,CommaPos);
|
|
end;
|
|
end;
|
|
if CurTargetOS='' then
|
|
CurTargetOS:=GetTargetOS;
|
|
Result:=MakeStandardExeFilename(CurTargetOS,CurFilename);
|
|
//DebugLn('TMainIDE.MacroFuncMakeExe A ',Filename,' ',Result);
|
|
end;
|
|
|
|
function TBuildManager.MacroFuncMakeLib(const Filename: string;
|
|
const Data: PtrInt; var Abort: boolean): string;
|
|
begin
|
|
Result:=MakeStandardLibFilename(GetTargetOS,Filename);
|
|
end;
|
|
|
|
function TBuildManager.MacroFuncInstantFPCCache(const Param: string;
|
|
const Data: PtrInt; var Abort: boolean): string;
|
|
var
|
|
Prog: String;
|
|
List: TStringList;
|
|
begin
|
|
if not FMacroInstantFPCCacheValid then begin
|
|
FMacroInstantFPCCache:='';
|
|
FMacroInstantFPCCacheValid:=true;
|
|
Prog:=FindDefaultExecutablePath('instantfpc'+GetExecutableExt);
|
|
if Prog<>'' then begin
|
|
List:=nil;
|
|
try
|
|
if ConsoleVerbosity>0 then
|
|
debugln(['Hint: (lazarus) [TBuildManager.MacroFuncInstantFPCCache] Exe=',Prog]);
|
|
List:=RunTool(Prog,'--get-cache','',ConsoleVerbosity<1);
|
|
if (List<>nil) and (List.Count>0) then
|
|
FMacroInstantFPCCache:=List[0];
|
|
List.Free;
|
|
except
|
|
on E: Exception do begin
|
|
if ConsoleVerbosity>=0 then
|
|
debugln(['Warning: (lazarus) [TBuildManager.MacroFuncInstantFPCCache] error running '+Prog+': '+E.Message]);
|
|
end;
|
|
end;
|
|
end;
|
|
if ConsoleVerbosity>=1 then
|
|
debugln(['Hint: (lazarus) [TBuildManager.MacroFuncInstantFPCCache] CacheDir=',FMacroInstantFPCCache]);
|
|
end;
|
|
Result:=FMacroInstantFPCCache;
|
|
end;
|
|
|
|
function TBuildManager.MacroFuncProject(const Param: string; const Data: PtrInt;
|
|
var Abort: boolean): string;
|
|
begin
|
|
if Project1<>nil then begin
|
|
if SysUtils.CompareText(Param,'SrcPath')=0 then
|
|
Result:=Project1.CompilerOptions.GetSrcPath(false)
|
|
else if SysUtils.CompareText(Param,'IncPath')=0 then
|
|
Result:=Project1.CompilerOptions.GetIncludePath(false)
|
|
else if SysUtils.CompareText(Param,'Namespaces')=0 then
|
|
Result:=Project1.CompilerOptions.GetNamespacesParsed
|
|
else if SysUtils.CompareText(Param,'UnitPath')=0 then
|
|
Result:=Project1.CompilerOptions.GetUnitPath(false)
|
|
else if SysUtils.CompareText(Param,'InfoFile')=0 then
|
|
Result:=Project1.ProjectInfoFile
|
|
else if SysUtils.CompareText(Param,'InfoDir')=0 then
|
|
Result:=ExtractFileDir(Project1.ProjectInfoFile)
|
|
else if SysUtils.CompareText(Param,'Title')=0 then
|
|
Result:=Project1.GetTitleOrName
|
|
else if SysUtils.CompareText(Param,'TitleNew')=0 then begin
|
|
Result:=Project1.GetTitleOrName;
|
|
if Result = '' then
|
|
Result := lisnewProject;
|
|
end
|
|
else if SysUtils.CompareText(Param,'OutputDir')=0 then
|
|
Result:=Project1.CompilerOptions.GetUnitOutPath(false)
|
|
else begin
|
|
Result:='<Invalid parameter for macro Project:'+Param+'>';
|
|
if ConsoleVerbosity>=0 then
|
|
debugln('Warning: (lazarus) [TMainIDE.MacroFuncProject]: ',Result);
|
|
end;
|
|
end else begin
|
|
Result:='';
|
|
end;
|
|
end;
|
|
|
|
function TBuildManager.MacroFuncLCLWidgetType(const Param: string;
|
|
const Data: PtrInt; var Abort: boolean): string;
|
|
begin
|
|
if Data=CompilerOptionMacroPlatformIndependent then
|
|
Result:='%(LCL_PLATFORM)'
|
|
else
|
|
Result:=GetLCLWidgetType;
|
|
end;
|
|
|
|
function TBuildManager.MacroFuncLazVer(const Param: string; const Data: PtrInt;
|
|
var Abort: boolean): string;
|
|
begin
|
|
if Param = '' then exit(LazarusVersionStr)
|
|
else if CompareText(Param, 'major') = 0 then result := ExtractDelimited(1, LazarusVersionStr, ['.'])
|
|
else if CompareText(Param, 'minor') = 0 then result := ExtractDelimited(2, LazarusVersionStr, ['.'])
|
|
else if CompareText(Param, 'rev' ) = 0 then result := ExtractDelimited(3, LazarusVersionStr, ['.'])
|
|
else if CompareText(Param, 'build') = 0 then result := ExtractDelimited(4, LazarusVersionStr, ['.'])
|
|
else exit(''); // invalid parameter
|
|
|
|
if result = '' then
|
|
result := '0';
|
|
end;
|
|
|
|
function TBuildManager.MacroFuncTargetCPU(const Param: string;
|
|
const Data: PtrInt; var Abort: boolean): string;
|
|
begin
|
|
if Data=CompilerOptionMacroPlatformIndependent then
|
|
Result:='%(CPU_TARGET)'
|
|
else if SysUtils.CompareText(Param,'IDE')=0 then
|
|
Result:=GetCompiledTargetCPU
|
|
else
|
|
Result:=GetTargetCPU;
|
|
end;
|
|
|
|
function TBuildManager.MacroFuncTargetOS(const Param: string;
|
|
const Data: PtrInt; var Abort: boolean): string;
|
|
begin
|
|
if Data=CompilerOptionMacroPlatformIndependent then
|
|
Result:='%(OS_TARGET)'
|
|
else if SysUtils.CompareText(Param,'IDE')=0 then
|
|
Result:=GetCompiledTargetOS
|
|
else
|
|
Result:=GetTargetOS;
|
|
end;
|
|
|
|
function TBuildManager.MacroFuncIDEBuildOptions(const Param: string;
|
|
const Data: PtrInt; var Abort: boolean): string;
|
|
begin
|
|
if Data=CompilerOptionMacroPlatformIndependent then
|
|
Result:=''
|
|
else if (MiscellaneousOptions<>nil)
|
|
and (MiscellaneousOptions.BuildLazOpts<>nil)
|
|
then
|
|
Result:=MiscellaneousOptions.BuildLazOpts.ExtraOptions
|
|
else
|
|
Result:='';
|
|
end;
|
|
|
|
function TBuildManager.MacroFuncPrimaryConfigPath(const Param: string;
|
|
const Data: PtrInt; var Abort: boolean): string;
|
|
begin
|
|
Result:=GetPrimaryConfigPath;
|
|
end;
|
|
|
|
function TBuildManager.MacroFuncSecondaryConfigPath(const Param: string;
|
|
const Data: PtrInt; var Abort: boolean): string;
|
|
begin
|
|
Result:=GetSecondaryConfigPath;
|
|
end;
|
|
|
|
function TBuildManager.MacroFuncFallbackOutputRoot(const Param: string;
|
|
const Data: PtrInt; var Abort: boolean): string;
|
|
begin
|
|
Result:=AppendPathDelim(GetPrimaryConfigPath)+'lib';
|
|
end;
|
|
|
|
function TBuildManager.MacroFuncSrcOS(const Param: string; const Data: PtrInt;
|
|
var Abort: boolean): string;
|
|
begin
|
|
if Data=CompilerOptionMacroPlatformIndependent then
|
|
Result:='%(OS_TARGET)'
|
|
else if Param<>'' then
|
|
Result:=GetDefaultSrcOSForTargetOS(Param)
|
|
else
|
|
Result:=GetDefaultSrcOSForTargetOS(GetTargetOS);
|
|
end;
|
|
|
|
function TBuildManager.MacroFuncSubtarget(const Param: string;
|
|
const Data: PtrInt; var Abort: boolean): string;
|
|
begin
|
|
if Data=CompilerOptionMacroPlatformIndependent then
|
|
Result:='%(FPC_SUBTARGET)'
|
|
else if SysUtils.CompareText(Param,'IDE')=0 then
|
|
Result:=''
|
|
else
|
|
Result:=GetSubtarget;
|
|
end;
|
|
|
|
function TBuildManager.MacroFuncFPCVer(const Param: string; const Data: PtrInt;
|
|
var Abort: boolean): string;
|
|
|
|
function TryTarget(CompilerFilename, TargetOS, TargetCPU: String): boolean;
|
|
var
|
|
ConfigCache: TPCTargetConfigCache;
|
|
begin
|
|
Result:=false;
|
|
ConfigCache:=CodeToolBoss.CompilerDefinesCache.ConfigCaches.Find(
|
|
CompilerFilename,'',TargetOS,TargetCPU,true);
|
|
if ConfigCache=nil then exit;
|
|
if ConfigCache.NeedsUpdate then begin
|
|
// ask compiler
|
|
if not ConfigCache.Update(CodeToolBoss.CompilerDefinesCache.TestFilename,'',nil)
|
|
then
|
|
exit;
|
|
end;
|
|
FFPCVer:=ConfigCache.GetFPCVer;
|
|
FFPC_FULLVERSION:=ConfigCache.GetFPC_FULLVERSION;
|
|
Result:=FFPC_FULLVERSION>0;
|
|
end;
|
|
|
|
procedure Compute;
|
|
var
|
|
TargetOS: String;
|
|
TargetCPU: String;
|
|
CompilerFilename, s: String;
|
|
begin
|
|
FFPC_FULLVERSION:=0;
|
|
if OverrideFPCVer<>'' then begin
|
|
FFPCVer:=OverrideFPCVer;
|
|
FFPC_FULLVERSION:=FPCVersionToNumber(FFPCVer);
|
|
exit;
|
|
end;
|
|
FFPCVer:={$I %FPCVERSION%}; // Version.Release.Patch
|
|
if CodeToolBoss<>nil then begin
|
|
// fetch the FPC version from the current compiler
|
|
// Not from the fpc.exe, but from the real compiler
|
|
CompilerFilename:=GetFPCompilerFilename;
|
|
if not IsCTExecutable(CompilerFilename,s) then exit;
|
|
|
|
// 1. try with project target OS/CPU
|
|
TargetOS:=GetTargetOS;
|
|
TargetCPU:=GetTargetCPU;
|
|
if IsPas2jsTargetOS(TargetOS) or IsPas2jsTargetCPU(TargetCPU) then
|
|
// skip
|
|
else if TryTarget(CompilerFilename,TargetOS,TargetCPU) then
|
|
exit;
|
|
|
|
// 2. try with IDE target OS/CPU
|
|
TargetOS:=GetCompiledTargetOS;
|
|
TargetCPU:=GetCompiledTargetCPU;
|
|
if TryTarget(CompilerFilename,TargetOS,TargetCPU) then exit;
|
|
|
|
// 3. try with no target OS/CPU - using whatever the compiler supports
|
|
TargetOS:='';
|
|
TargetCPU:='';
|
|
if TryTarget(CompilerFilename,TargetOS,TargetCPU) then exit;
|
|
end;
|
|
FFPC_FULLVERSION:=FPCVersionToNumber(FFPCVer);
|
|
end;
|
|
|
|
begin
|
|
if FFPCVerChangeStamp<>CompilerParseStamp then
|
|
begin
|
|
Compute;
|
|
FFPCVerChangeStamp:=CompilerParseStamp;
|
|
{$IFDEF VerboseFPCSrcScan}
|
|
debugln(['TBuildManager.MacroFuncFPCVer FPCVer=',FFPCVer,' FPC_FULLVERSION=',FFPC_FULLVERSION,' Stamp=',FFPCVerChangeStamp]);
|
|
{$ENDIF}
|
|
end;
|
|
Result:=FFPCVer;
|
|
end;
|
|
|
|
function TBuildManager.MacroFuncFPC_FULLVERSION(const Param: string;
|
|
const Data: PtrInt; var Abort: boolean): string;
|
|
begin
|
|
if FFPCVerChangeStamp<>CompilerParseStamp then
|
|
MacroFuncFPCVer(Param,Data,Abort);
|
|
Result:=IntToStr(FFPC_FULLVERSION);
|
|
end;
|
|
|
|
function TBuildManager.MacroFuncParams(const Param: string; const Data: PtrInt;
|
|
var Abort: boolean): string;
|
|
begin
|
|
if (Project1<>nil) and (Project1.RunParameterOptions.GetActiveMode<>nil) then
|
|
Result:=Project1.RunParameterOptions.GetActiveMode.CmdLineParams
|
|
else
|
|
Result:='';
|
|
end;
|
|
|
|
function TBuildManager.MacroFuncProjFile(const Param: string;
|
|
const Data: PtrInt; var Abort: boolean): string;
|
|
begin
|
|
if Project1<>nil then
|
|
Result:=Project1.MainFilename
|
|
else
|
|
Result:='';
|
|
end;
|
|
|
|
function TBuildManager.MacroFuncProjPath(const Param: string;
|
|
const Data: PtrInt; var Abort: boolean): string;
|
|
begin
|
|
if Project1<>nil then
|
|
Result:=Project1.Directory
|
|
else
|
|
Result:='';
|
|
end;
|
|
|
|
function TBuildManager.MacroFuncTargetFile(const Param: string;
|
|
const Data: PtrInt; var Abort: boolean): string;
|
|
begin
|
|
if Project1<>nil then
|
|
Result:=GetProjectTargetFilename(Project1)
|
|
else
|
|
Result:='';
|
|
end;
|
|
|
|
function TBuildManager.MacroFuncOutputFile(const Param: string;
|
|
const Data: PtrInt; var Abort: boolean): string;
|
|
begin
|
|
if Project1<>nil then
|
|
Result:=Project1.CompilerOptions.CreateTargetFilename
|
|
else
|
|
Result:='';
|
|
end;
|
|
|
|
function TBuildManager.MacroFuncTargetCmdLine(const Param: string;
|
|
const Data: PtrInt; var Abort: boolean): string;
|
|
begin
|
|
Result:='';
|
|
if (Project1<>nil) then begin
|
|
if (Project1.RunParameterOptions.GetActiveMode<>nil) then
|
|
Result:=Project1.RunParameterOptions.GetActiveMode.CmdLineParams;
|
|
if Result='' then
|
|
Result:='"'+GetProjectTargetFilename(Project1)+'"'
|
|
else
|
|
Result:='"'+GetProjectTargetFilename(Project1)+'" '+Result;
|
|
end;
|
|
end;
|
|
|
|
function TBuildManager.MacroFuncRunCmdLine(const Param: string;
|
|
const Data: PtrInt; var Abort: boolean): string;
|
|
begin
|
|
if Project1<>nil then
|
|
Result:=GetRunCommandLine
|
|
else
|
|
Result:='';
|
|
end;
|
|
|
|
function TBuildManager.MacroFuncProjPublishDir(const Param: string;
|
|
const Data: PtrInt; var Abort: boolean): string;
|
|
begin
|
|
Result:=GetProjectPublishDir;
|
|
end;
|
|
|
|
function TBuildManager.MacroFuncProjUnitPath(const Param: string;
|
|
const Data: PtrInt; var Abort: boolean): string;
|
|
begin
|
|
if Project1<>nil then
|
|
Result:=Project1.CompilerOptions.GetUnitPath(false)
|
|
else
|
|
Result:='';
|
|
end;
|
|
|
|
function TBuildManager.MacroFuncProjIncPath(const Param: string;
|
|
const Data: PtrInt; var Abort: boolean): string;
|
|
begin
|
|
if Project1<>nil then
|
|
Result:=Project1.CompilerOptions.GetIncludePath(false)
|
|
else
|
|
Result:='';
|
|
end;
|
|
|
|
function TBuildManager.MacroFuncProjNamespaces(const Param: string;
|
|
const Data: PtrInt; var Abort: boolean): string;
|
|
begin
|
|
if Project1<>nil then
|
|
begin
|
|
Result:=MergeWithDelimiter(GetProjectDefaultNamespace,
|
|
Project1.CompilerOptions.GetNamespacesParsed,';');
|
|
end
|
|
else
|
|
Result:='';
|
|
end;
|
|
|
|
function TBuildManager.MacroFuncProjSrcPath(const Param: string;
|
|
const Data: PtrInt; var Abort: boolean): string;
|
|
begin
|
|
if Project1<>nil then
|
|
Result:=Project1.CompilerOptions.GetSrcPath(false)
|
|
else
|
|
Result:='';
|
|
end;
|
|
|
|
function TBuildManager.MacroFuncProjOutDir(const Param: string;
|
|
const Data: PtrInt; var Abort: boolean): string;
|
|
begin
|
|
if Project1<>nil then
|
|
Result:=Project1.CompilerOptions.GetUnitOutPath(false)
|
|
else
|
|
Result:='';
|
|
end;
|
|
|
|
function TBuildManager.MacroFuncProjVer(const Param: string;
|
|
const Data: PtrInt; var Abort: boolean): string;
|
|
const
|
|
cParamNames: array of string = ('', 'major', 'minor', 'rev', 'build');
|
|
cParamDefVals: array of string = ('0.0', '0', '0', '0', '0');
|
|
var
|
|
i: integer;
|
|
begin
|
|
for i := 0 to high(cParamNames) do
|
|
if CompareText(Param, cParamNames[i]) = 0 then
|
|
begin
|
|
// check the project and whether the version is used
|
|
result := cParamDefVals[i];
|
|
if Project1 = nil then exit;
|
|
if Project1.ProjResources = nil then exit;
|
|
if Project1.ProjResources.VersionInfo = nil then exit;
|
|
if Project1.ProjResources.VersionInfo.UseVersionInfo = false then exit;
|
|
|
|
// return version or specified number
|
|
with Project1.ProjResources.VersionInfo do
|
|
case i of
|
|
1: exit(IntToStr(MajorVersionNr));
|
|
2: exit(IntToStr(MinorVersionNr));
|
|
3: exit(IntToStr(RevisionNr ));
|
|
4: exit(IntToStr(BuildNr ));
|
|
else
|
|
// return the full version number, discarding the zero revision and build
|
|
if BuildNr <> 0 then
|
|
exit(Format('%d.%d.%d.%d', [MajorVersionNr, MinorVersionNr, RevisionNr, BuildNr]))
|
|
else if RevisionNr <> 0 then
|
|
exit(Format('%d.%d.%d' , [MajorVersionNr, MinorVersionNr, RevisionNr]))
|
|
else
|
|
exit(Format('%d.%d' , [MajorVersionNr, MinorVersionNr]));
|
|
end;
|
|
end;
|
|
result := ''; // invalid parameter
|
|
end;
|
|
|
|
function TBuildManager.MacroFuncEnv(const Param: string; const Data: PtrInt;
|
|
var Abort: boolean): string;
|
|
begin
|
|
Result:=GetEnvironmentVariableUTF8(Param);
|
|
end;
|
|
|
|
function TBuildManager.MacroFuncCompPath(const s: string; const Data: PtrInt;
|
|
var Abort: boolean): string;
|
|
// if parameter is 'IDE' return the environment option
|
|
// otherwise use active project's compiler
|
|
begin
|
|
Result:='';
|
|
if CompareText(s,'IDE')<>0 then
|
|
Result:=GetCompilerFilename;
|
|
|
|
if Result='' then
|
|
Result:=EnvironmentOptions.GetParsedCompilerFilename;
|
|
end;
|
|
|
|
function TBuildManager.MacroFuncFPCMsgFile(const Param: string;
|
|
const Data: PtrInt; var Abort: boolean): string;
|
|
begin
|
|
Result:=EnvironmentOptions.GetParsedCompilerMessagesFilename;
|
|
end;
|
|
|
|
function TBuildManager.MacroFuncFPCTarget(const Param: string;
|
|
const Data: PtrInt; var Abort: boolean): string;
|
|
// works similar to FPC's macro $fpctarget:
|
|
// if subtarget is set:
|
|
// targetcpu-targetos-subtarget
|
|
// else
|
|
// targetcpu-targetos
|
|
// Supports same parameters as $TargetOS(param), i.e. $TargetOS(ide) returns
|
|
// the IDE's target, otherwise the project's target platform.
|
|
var
|
|
TargetCPU, TargetOS, SubTarget: String;
|
|
begin
|
|
Result:='';
|
|
TargetCPU:=MacroFuncTargetCPU(Param,Data,Abort);
|
|
if Abort then exit;
|
|
TargetOS:=MacroFuncTargetOS(Param,Data,Abort);
|
|
if Abort then exit;
|
|
Result:=TargetCPU+'-'+TargetOS;
|
|
if Data=CompilerOptionMacroPlatformIndependent then
|
|
exit; // omit subtarget when creating a platform independent value
|
|
|
|
SubTarget:=MacroFuncSubtarget(Param,Data,Abort);
|
|
if Abort then exit;
|
|
if SubTarget<>'' then
|
|
Result+='-'+SubTarget;
|
|
end;
|
|
|
|
function TBuildManager.MacroFuncMake(const Param: string; const Data: PtrInt;
|
|
var Abort: boolean): string;
|
|
begin
|
|
Result:=EnvironmentOptions.GetParsedMakeFilename;
|
|
if Result='' then
|
|
Result:=FindDefaultMakePath;
|
|
end;
|
|
|
|
function TBuildManager.CTMacroFuncProjectNamespaces(Data: Pointer): boolean;
|
|
var
|
|
FuncData: PReadFunctionData;
|
|
begin
|
|
FuncData:=PReadFunctionData(Data);
|
|
Result:=false;
|
|
if Project1<>nil then begin
|
|
FuncData^.Result:=MergeWithDelimiter(GetProjectDefaultNamespace,
|
|
Project1.CompilerOptions.GetNamespacesParsed(),';');
|
|
Result:=true;
|
|
end;
|
|
end;
|
|
|
|
function TBuildManager.CTMacroFuncProjectUnitPath(Data: Pointer): boolean;
|
|
var
|
|
FuncData: PReadFunctionData;
|
|
begin
|
|
FuncData:=PReadFunctionData(Data);
|
|
Result:=false;
|
|
if Project1<>nil then begin
|
|
FuncData^.Result:=Project1.CompilerOptions.GetUnitPath(false);
|
|
Result:=true;
|
|
end;
|
|
end;
|
|
|
|
function TBuildManager.CTMacroFuncProjectIncPath(Data: Pointer): boolean;
|
|
var
|
|
FuncData: PReadFunctionData;
|
|
begin
|
|
FuncData:=PReadFunctionData(Data);
|
|
Result:=false;
|
|
if Project1<>nil then begin
|
|
FuncData^.Result:=
|
|
Project1.CompilerOptions.GetIncludePath(false,coptParsed,true);
|
|
Result:=true;
|
|
end;
|
|
end;
|
|
|
|
function TBuildManager.CTMacroFuncProjectSrcPath(Data: Pointer): boolean;
|
|
var
|
|
FuncData: PReadFunctionData;
|
|
begin
|
|
FuncData:=PReadFunctionData(Data);
|
|
Result:=false;
|
|
if Project1<>nil then begin
|
|
FuncData^.Result:=Project1.CompilerOptions.GetSrcPath(false);
|
|
Result:=true;
|
|
end;
|
|
end;
|
|
|
|
procedure TBuildManager.SetUnitSetCache(const AValue: TFPCUnitSetCache);
|
|
begin
|
|
if FUnitSetCache=AValue then exit;
|
|
FUnitSetCache:=AValue;
|
|
if UnitSetCache<>nil then begin
|
|
FreeNotification(UnitSetCache);
|
|
FUnitSetChangeStamp:=UnitSetCache.GetInvalidChangeStamp;
|
|
end;
|
|
end;
|
|
|
|
function TBuildManager.GetProjectDefaultNamespace: string;
|
|
// called by codetools *before* parsing
|
|
// Important: use only basiccodetools
|
|
var
|
|
AnUnitInfo: TUnitInfo;
|
|
NameStart, NameEnd: Integer;
|
|
Code: TCodeBuffer;
|
|
ModuleType, ModuleName: string;
|
|
NestedComments: boolean;
|
|
begin
|
|
Result:='';
|
|
if Project1=nil then exit;
|
|
if not (pfMainUnitIsPascalSource in Project1.Flags) then exit;
|
|
AnUnitInfo:=Project1.MainUnitInfo;
|
|
if AnUnitInfo=nil then exit;
|
|
Code:=AnUnitInfo.Source;
|
|
if Code=nil then exit;
|
|
if (Code<>FProjectNameSpaceCode) or (Code.ChangeStep<>FProjectNameSpaceCodeChgStep) then
|
|
begin
|
|
// read namespace
|
|
FProjectNameSpace:='';
|
|
FProjectNameSpaceCode:=Code;
|
|
FProjectNameSpaceCodeChgStep:=Code.ChangeStep;
|
|
NestedComments:=CompareText(Project1.CompilerOptions.SyntaxMode,'delphi')<>0;
|
|
ModuleName:=FindModuleNameInSource(Code.Source,ModuleType,NameStart,
|
|
NameEnd,NestedComments);
|
|
FProjectNameSpace:=ChompDottedIdentifier(ModuleName);
|
|
end;
|
|
Result:=FProjectNameSpace;
|
|
end;
|
|
|
|
procedure TBuildManager.Notification(AComponent: TComponent;
|
|
Operation: TOperation);
|
|
begin
|
|
inherited Notification(AComponent, Operation);
|
|
if Operation=opRemove then begin
|
|
if FUnitSetCache=AComponent then
|
|
FUnitSetCache:=nil;
|
|
end;
|
|
end;
|
|
|
|
function TBuildManager.GetBuildMacroValuesHandler(Options: TLazCompilerOptions;
|
|
IncludeSelf: boolean): TCTCfgScriptVariables;
|
|
{off $DEFINE VerboseBuildMacros}
|
|
|
|
procedure AddAllInherited(FirstDependency: TPkgDependency;
|
|
AddTo: TCTCfgScriptVariables);
|
|
var
|
|
List: TFPList;
|
|
i: Integer;
|
|
APackage: TLazPackage;
|
|
Values: TCTCfgScriptVariables;
|
|
OtherOpts: TPkgCompilerOptions;
|
|
j: Integer;
|
|
Macro: TLazBuildMacro;
|
|
Value: PCTCfgScriptVariable;
|
|
begin
|
|
if FirstDependency=nil then exit;
|
|
List:=nil;
|
|
try
|
|
PackageGraph.GetAllRequiredPackages(nil,FirstDependency,List);
|
|
if List=nil then exit;
|
|
for i:=0 to List.Count-1 do begin
|
|
// add values of build macros of used package
|
|
APackage:=TLazPackage(List[i]);
|
|
OtherOpts:=APackage.CompilerOptions;
|
|
if OtherOpts.BuildMacros=nil then continue;
|
|
Values:=GetBuildMacroValuesHandler(OtherOpts,true);
|
|
if Values=nil then continue;
|
|
for j:=0 to OtherOpts.BuildMacros.Count-1 do begin
|
|
Macro:=OtherOpts.BuildMacros[j];
|
|
if Macro.Identifier='' then continue;
|
|
Value:=Values.GetVariable(PChar(Macro.Identifier));
|
|
if Value=nil then begin
|
|
//debugln(['AddAllInherited InhPkg=',APackage.Name,' Macro="',Macro.Identifier,'" no value']);
|
|
continue;
|
|
end else begin
|
|
//debugln(['AddAllInherited InhPkg=',APackage.Name,' Macro="',Macro.Identifier,'" Value="',dbgs(Value),'"']);
|
|
AddTo.AddOverride(Value);
|
|
end;
|
|
end;
|
|
end;
|
|
finally
|
|
List.Free;
|
|
end;
|
|
end;
|
|
|
|
procedure SetCmdLineOverrides(Values: TCTCfgScriptVariables);
|
|
var
|
|
Overrides: TStrings;
|
|
i: Integer;
|
|
begin
|
|
// set overrides (e.g. command line parameters)
|
|
Overrides:=GetBuildMacroOverrides;
|
|
try
|
|
for i:=0 to Overrides.Count-1 do
|
|
Values.Values[Overrides.Names[i]]:=Overrides.ValueFromIndex[i];
|
|
{$IFDEF VerboseBuildMacros}
|
|
debugln(['TBuildManager.OnGetBuildMacroValues cmd line overrides=',dbgstr(Overrides.Text)]);
|
|
{$ENDIF}
|
|
finally
|
|
Overrides.Free;
|
|
end;
|
|
end;
|
|
|
|
procedure SetDefaults(Values: TCTCfgScriptVariables);
|
|
var
|
|
s: String;
|
|
begin
|
|
// add the defaults
|
|
// Note: see also ide/frames/compiler_buildmacro_options.pas procedure TCompOptBuildMacrosFrame.BuildMacrosTreeViewEdited
|
|
// TargetOS
|
|
if not Values.IsDefined('TargetOS') then begin
|
|
s:='';
|
|
if FBuildTarget<>nil then
|
|
s:=FBuildTarget.CompilerOptions.TargetOS;
|
|
if s='' then
|
|
s:=fTargetOS;
|
|
if s='' then begin
|
|
{$IFDEF VerboseDefaultCompilerTarget}
|
|
debugln(['SetDefaults WARNING: no TargetOS']);
|
|
{$ENDIF}
|
|
s:=GetCompiledTargetOS;
|
|
end;
|
|
Values.Values['TargetOS']:=s;
|
|
end;
|
|
// SrcOS
|
|
if not Values.IsDefined('SrcOS') then begin
|
|
s:=GetDefaultSrcOSForTargetOS(Result.Values['TargetOS']);
|
|
Values.Values['SrcOS']:=s;
|
|
end;
|
|
// SrcOS2
|
|
if not Result.IsDefined('SrcOS2') then begin
|
|
s:=GetDefaultSrcOS2ForTargetOS(Result.Values['TargetOS']);
|
|
Values.Values['SrcOS2']:=s;
|
|
end;
|
|
// TargetCPU
|
|
if not Values.IsDefined('TargetCPU') then begin
|
|
s:='';
|
|
if FBuildTarget<>nil then
|
|
s:=FBuildTarget.CompilerOptions.TargetCPU;
|
|
if s='' then
|
|
s:=fTargetCPU;
|
|
Values.Values['TargetCPU']:=s;
|
|
if s='' then begin
|
|
{$IFDEF VerboseDefaultCompilerTarget}
|
|
debugln(['SetDefaults WARNING: no TargetCPU']);
|
|
{$ENDIF}
|
|
s:=GetCompiledTargetCPU;
|
|
end;
|
|
end;
|
|
// Subtarget
|
|
if not Values.IsDefined('Subtarget') then begin
|
|
s:='';
|
|
if FBuildTarget<>nil then
|
|
s:=FBuildTarget.CompilerOptions.Subtarget;
|
|
if s='' then
|
|
s:=fSubtarget;
|
|
Values.Values['Subtarget']:=s;
|
|
end;
|
|
// Laz_FullVersion
|
|
if not Values.IsDefined('Laz_FullVersion') then begin
|
|
SetCTCSVariableAsNumber(Values.GetVariable('Laz_FullVersion',true),laz_fullversion);
|
|
end;
|
|
end;
|
|
|
|
procedure ApplyMacroOverrides(Vars: TCTCfgScriptVariables);
|
|
var
|
|
Target: String;
|
|
ActiveMode: String;
|
|
begin
|
|
ActiveMode:=GetActiveBuildModeName;
|
|
Target:=GetModeMatrixTarget(Options);
|
|
if EnvironmentOptions<>nil then
|
|
ApplyBuildMatrixMacros(EnvironmentOptions.BuildMatrixOptions,Target,ActiveMode,Vars);
|
|
if FBuildTarget<>nil then begin
|
|
ApplyBuildMatrixMacros(FBuildTarget.BuildModes.SharedMatrixOptions,Target,ActiveMode,Vars);
|
|
ApplyBuildMatrixMacros(FBuildTarget.BuildModes.SessionMatrixOptions,Target,ActiveMode,Vars);
|
|
end;
|
|
SetCmdLineOverrides(Vars);
|
|
{$IFDEF VerboseBuildMacros}
|
|
Vars.WriteDebugReport('OnGetBuildMacroValues after applying project values');
|
|
{$ENDIF}
|
|
SetDefaults(Vars);
|
|
end;
|
|
|
|
var
|
|
ParseOpts: TParsedCompilerOptions;
|
|
Values: TCTCfgScriptVariables;
|
|
begin
|
|
Result:=nil;
|
|
|
|
ParseOpts:=TBaseCompilerOptions(Options).ParsedOpts;
|
|
if ParseOpts=nil then exit;
|
|
|
|
if IncludeSelf then begin
|
|
Result:=ParseOpts.MacroValues.Variables;
|
|
if ParseOpts.MacroValuesStamp=BuildMacroChangeStamp then exit;
|
|
|
|
// compute macro values
|
|
|
|
if ParseOpts.MacroValuesParsing then begin
|
|
if ConsoleVerbosity>=0 then
|
|
debugln(['Warning: (lazarus) [TBuildManager.OnGetBuildMacroValues] cycle computing macros of ',dbgsname(Options.Owner)]);
|
|
exit;
|
|
end;
|
|
|
|
ParseOpts.MacroValuesParsing:=true;
|
|
try
|
|
Result.Clear;
|
|
|
|
// use inherited as default
|
|
Values:=GetBuildMacroValuesHandler(Options,false);
|
|
|
|
// add macro values of self
|
|
if Values<>nil then
|
|
Result.Assign(Values);
|
|
{$IF defined(VerboseBuildMacros) or defined(DebugLCLBaseConditionals)}
|
|
if (Options.Owner is TLazPackage) and (TLazPackage(Options.Owner).Name='LCLBase') then
|
|
Result.WriteDebugReport('TBuildManager.OnGetBuildMacroValues before execute: Conditionals="'+dbgstr(Options.Conditionals),'"');
|
|
{$ENDIF}
|
|
if not ParseOpts.MacroValues.Execute(Options.Conditionals) then begin
|
|
if ConsoleVerbosity>=0 then
|
|
debugln(['Error: (lazarus) [TBuildManager.OnGetBuildMacroValues] Error: ',ParseOpts.MacroValues.GetErrorStr(0)]);
|
|
debugln(Options.Conditionals);
|
|
end;
|
|
|
|
{$IFDEF VerboseBuildMacros}
|
|
if (Options.Owner is TLazPackage) and (TLazPackage(Options.Owner).Name='LCL') then
|
|
Result.WriteDebugReport('TBuildManager.OnGetBuildMacroValues executed: '+dbgstr(Options.Conditionals),' ');
|
|
{$ENDIF}
|
|
|
|
// the macro values of the active project take precedence
|
|
ApplyMacroOverrides(Result);
|
|
|
|
ParseOpts.MacroValuesStamp:=BuildMacroChangeStamp;
|
|
finally
|
|
ParseOpts.MacroValuesParsing:=false;
|
|
end;
|
|
end else begin
|
|
Result:=ParseOpts.InheritedMacroValues;
|
|
if ParseOpts.InheritedMacroValuesStamp=BuildMacroChangeStamp then exit;
|
|
|
|
// compute inherited values
|
|
if ParseOpts.InheritedMacroValuesParsing then begin
|
|
if ConsoleVerbosity>=0 then
|
|
debugln(['Error: (lazarus) [TBuildManager.OnGetBuildMacroValues] cycle detected computing inherited macros of ',dbgsname(Options.Owner)]);
|
|
exit;
|
|
end;
|
|
ParseOpts.InheritedMacroValuesParsing:=true;
|
|
try
|
|
Result.Clear;
|
|
|
|
// add inherited
|
|
if (PackageGraph<>nil) then begin
|
|
if Options.Owner is TProject then
|
|
AddAllInherited(TProject(Options.Owner).FirstRequiredDependency,Result)
|
|
else if Options.Owner is TLazPackage then
|
|
AddAllInherited(TLazPackage(Options.Owner).FirstRequiredDependency,Result);
|
|
end;
|
|
|
|
// the macro values of the active project take precedence
|
|
ApplyMacroOverrides(Result);
|
|
|
|
ParseOpts.InheritedMacroValuesStamp:=BuildMacroChangeStamp;
|
|
finally
|
|
ParseOpts.InheritedMacroValuesParsing:=false;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
function TBuildManager.GetActiveBuildModeName: string;
|
|
begin
|
|
if FBuildTarget<>nil then
|
|
Result:=FBuildTarget.ActiveBuildMode.Identifier
|
|
else
|
|
Result:='default';
|
|
end;
|
|
|
|
procedure TBuildManager.AppendMatrixCustomOption(Sender: TObject;
|
|
var Options: string; Types: TBuildMatrixGroupTypes);
|
|
var
|
|
Target: String;
|
|
ActiveMode: String;
|
|
begin
|
|
Target:=GetModeMatrixTarget(Sender);
|
|
ActiveMode:=GetActiveBuildModeName;
|
|
if bmgtEnvironment in Types then
|
|
EnvironmentOptions.BuildMatrixOptions.AppendCustomOptions(Target,ActiveMode,Options);
|
|
if FBuildTarget<>nil then begin
|
|
if bmgtProject in Types then
|
|
FBuildTarget.BuildModes.SharedMatrixOptions.AppendCustomOptions(Target,ActiveMode,Options);
|
|
if bmgtSession in Types then
|
|
FBuildTarget.BuildModes.SessionMatrixOptions.AppendCustomOptions(Target,ActiveMode,Options);
|
|
end;
|
|
end;
|
|
|
|
procedure TBuildManager.GetMatrixOutputDirectoryOverride(Sender: TObject;
|
|
var OutDir: string; Types: TBuildMatrixGroupTypes);
|
|
var
|
|
Target: String;
|
|
ActiveMode: String;
|
|
begin
|
|
Target:=GetModeMatrixTarget(Sender);
|
|
ActiveMode:=GetActiveBuildModeName;
|
|
if bmgtEnvironment in Types then
|
|
EnvironmentOptions.BuildMatrixOptions.GetOutputDirectory(Target,ActiveMode,OutDir);
|
|
if FBuildTarget<>nil then begin
|
|
if bmgtProject in Types then
|
|
FBuildTarget.BuildModes.SharedMatrixOptions.GetOutputDirectory(Target,ActiveMode,OutDir);
|
|
if bmgtSession in Types then
|
|
FBuildTarget.BuildModes.SessionMatrixOptions.GetOutputDirectory(Target,ActiveMode,OutDir);
|
|
end;
|
|
end;
|
|
|
|
function TBuildManager.GetModeMatrixTarget(Sender: TObject): string;
|
|
begin
|
|
Result:='';
|
|
if Sender is TParsedCompilerOptions then
|
|
Sender:=TParsedCompilerOptions(Sender).Owner;
|
|
if Sender is TPkgAdditionalCompilerOptions then
|
|
exit; // matrix options are added only to normal options
|
|
if Sender is TPkgCompilerOptions then
|
|
Sender:=TPkgCompilerOptions(Sender).Owner
|
|
else if Sender is TProjectCompilerOptions then
|
|
Sender:=TProjectCompilerOptions(Sender).Owner;
|
|
if Sender is TProject then begin
|
|
Result:=BuildMatrixProjectName;
|
|
end else if Sender is TLazPackage then begin
|
|
Result:=TLazPackage(Sender).Name;
|
|
end else
|
|
Result:=BuildMatrixIDEName;
|
|
//debugln(['TBuildManager.GetModeMatrixTarget ',DbgSName(Sender),' Target="',Result,'"']);
|
|
end;
|
|
|
|
function TBuildManager.EnvironmentOptionsIsGlobalMode(const Identifier: string
|
|
): boolean;
|
|
begin
|
|
Result:=true;
|
|
if Project1=nil then exit;
|
|
if Project1.BuildModes=nil then exit;
|
|
// do not save enabled states of session modes
|
|
Result:=not Project1.BuildModes.IsSessionMode(Identifier);
|
|
end;
|
|
|
|
procedure TBuildManager.SetBuildTarget(const TargetOS, TargetCPU, Subtarget,
|
|
LCLWidgetType: string; ScanFPCSrc: TScanModeFPCSources; Quiet: boolean);
|
|
|
|
function GetEffectiveLCLWidgetType: string;
|
|
begin
|
|
if OverrideLCLWidgetType<>'' then
|
|
Result:=OverrideLCLWidgetType
|
|
else if FBuildTarget<>nil then
|
|
Result:=FBuildTarget.CompilerOptions.GetEffectiveLCLWidgetType
|
|
else
|
|
Result:='';
|
|
if (Result='') or (SysUtils.CompareText(Result,'default')=0) then
|
|
Result:=GetLCLWidgetTypeName;
|
|
Result:=lowercase(Result);
|
|
end;
|
|
|
|
var
|
|
OldTargetOS: String;
|
|
OldTargetCPU: String;
|
|
OldSubtarget: String;
|
|
OldLCLWidgetType: String;
|
|
PCTargetChanged: Boolean;
|
|
LCLTargetChanged: Boolean;
|
|
CompilerTargetOS: string;
|
|
CompilerTargetCPU: string;
|
|
CompQueryOptions, CompilerFilename: String;
|
|
begin
|
|
{$IFDEF VerboseDefaultCompilerTarget}
|
|
debugln(['TBuildManager.SetBuildTarget TargetOS="',TargetOS,'" TargetCPU="',TargetCPU,'" Subtarget="',Subtarget,'" LCLWidgetType="',LCLWidgetType,'"']);
|
|
{$ENDIF}
|
|
OldTargetOS:=fTargetOS;
|
|
OldTargetCPU:=fTargetCPU;
|
|
OldSubtarget:=fSubtarget;
|
|
OldLCLWidgetType:=fLCLWidgetType;
|
|
OverrideTargetOS:=GetFPCTargetOS(TargetOS);
|
|
OverrideTargetCPU:=GetFPCTargetCPU(TargetCPU);
|
|
OverrideSubtarget:=GetFPCSubtarget(Subtarget);
|
|
OverrideLCLWidgetType:=lowercase(LCLWidgetType);
|
|
|
|
// compute new TargetOS
|
|
if OverrideTargetOS<>'' then
|
|
fTargetOS:=OverrideTargetOS
|
|
else if FBuildTarget<>nil then
|
|
fTargetOS:=FBuildTarget.CompilerOptions.TargetOS
|
|
else
|
|
fTargetOS:='';
|
|
if SysUtils.CompareText(fTargetOS,'default')=0 then
|
|
fTargetOS:='';
|
|
|
|
// compute new TargetCPU
|
|
if OverrideTargetCPU<>'' then
|
|
fTargetCPU:=OverrideTargetCPU
|
|
else if FBuildTarget<>nil then
|
|
fTargetCPU:=FBuildTarget.CompilerOptions.TargetCPU
|
|
else
|
|
fTargetCPU:='';
|
|
if SysUtils.CompareText(fTargetCPU,'default')=0 then
|
|
fTargetCPU:='';
|
|
|
|
// compute new Subtarget
|
|
if OverrideSubtarget<>'' then
|
|
fSubtarget:=OverrideSubtarget
|
|
else if FBuildTarget<>nil then
|
|
fSubtarget:=FBuildTarget.CompilerOptions.Subtarget
|
|
else
|
|
fSubtarget:='';
|
|
if SysUtils.CompareText(fSubtarget,'default')=0 then
|
|
fSubtarget:='';
|
|
|
|
if (fTargetOS='') or (fTargetCPU='') then
|
|
begin
|
|
// use compiler default target
|
|
CompQueryOptions:='';
|
|
if fTargetCPU<>'' then
|
|
CompQueryOptions:='-P'+GetFPCTargetCPU(fTargetCPU)
|
|
else if fTargetOS<>'' then
|
|
CompQueryOptions:='-T'+GetFPCTargetOS(fTargetOS);
|
|
// Note: resolving the comiler filename requires macros
|
|
CompilerFilename:=GetCompilerFilename;
|
|
if CompilerFilename=cInvalidCompiler then
|
|
exit;
|
|
|
|
CodeToolBoss.CompilerDefinesCache.ConfigCaches.GetDefaultCompilerTarget(
|
|
CompilerFilename,CompQueryOptions,CompilerTargetOS,CompilerTargetCPU);
|
|
if fTargetOS='' then
|
|
fTargetOS:=CompilerTargetOS;
|
|
if fTargetOS='' then
|
|
fTargetOS:=GetCompiledTargetOS;
|
|
if fTargetCPU='' then
|
|
fTargetCPU:=CompilerTargetCPU;
|
|
if fTargetCPU='' then
|
|
fTargetCPU:=GetCompiledTargetCPU;
|
|
// the macros were resolved with default values
|
|
// => invalidate macros so they now use the actual values
|
|
IncreaseBuildMacroChangeStamp;
|
|
if ConsoleVerbosity>1 then
|
|
debugln(['Hint: (lazarus) [TBuildManager.SetBuildTarget] OS=',fTargetOS,' CPU=',fTargetCPU,' CompQueryOptions=',CompQueryOptions,' DefaultOS=',CompilerTargetOS,' DefaultCPU=',CompilerTargetCPU]);
|
|
end;
|
|
|
|
fTargetOS:=GetFPCTargetOS(fTargetOS);
|
|
fTargetCPU:=GetFPCTargetCPU(fTargetCPU);
|
|
|
|
PCTargetChanged:=(OldTargetOS<>fTargetOS)
|
|
or (OldTargetCPU<>fTargetCPU)
|
|
or (OldSubtarget<>fSubtarget)
|
|
or (CodeToolBoss.DefineTree.FindDefineTemplateByName(
|
|
StdDefTemplLazarusSources,true)=nil);
|
|
if PCTargetChanged then
|
|
begin
|
|
IncreaseBuildMacroChangeStamp;
|
|
CodeToolBoss.DefineTree.ClearCache;
|
|
end;
|
|
|
|
// compute new LCLWidgetType
|
|
fLCLWidgetType:=GetEffectiveLCLWidgetType;
|
|
LCLTargetChanged:=(OldLCLWidgetType<>fLCLWidgetType);
|
|
|
|
if PCTargetChanged or LCLTargetChanged then begin
|
|
if ConsoleVerbosity>=0 then
|
|
DebugLn(['Hint: (lazarus) [TBuildManager.SetBuildTarget]',
|
|
' Old=',OldTargetCPU,'-',OldTargetOS,'-',OldSubtarget,'-',OldLCLWidgetType,
|
|
' New=',fTargetCPU,'-',fTargetOS,'-',fSubtarget,'-',fLCLWidgetType,
|
|
' Changed: OS/CPU=',PCTargetChanged,' LCL=',LCLTargetChanged]);
|
|
end;
|
|
if LCLTargetChanged then
|
|
CodeToolBoss.SetGlobalValue(ExternalMacroStart+'LCLWidgetType',fLCLWidgetType);
|
|
if ScanFPCSrc<>smsfsSkip then
|
|
RescanCompilerDefines(false,false,ScanFPCSrc=smsfsWaitTillDone,Quiet);
|
|
//if (PackageGraph<>nil) and (PackageGraph.CodeToolsPackage<>nil) then
|
|
// debugln(['TBuildManager.SetBuildTarget CODETOOLS OUTDIR=',PackageGraph.CodeToolsPackage.CompilerOptions.GetUnitOutPath(true,coptParsed),
|
|
// ' ',PackageGraph.CodeToolsPackage.CompilerOptions.ParsedOpts.ParsedStamp[pcosOutputDir],' ',CompilerParseStamp]);
|
|
end;
|
|
|
|
procedure TBuildManager.SetBuildTargetProject1;
|
|
begin
|
|
SetBuildTargetProject1(true);
|
|
end;
|
|
|
|
procedure TBuildManager.SetBuildTargetProject1(Quiet: boolean;
|
|
ScanFPCSrc: TScanModeFPCSources);
|
|
begin
|
|
//debugln(['TBuildManager.SetBuildTargetProject1 START']);
|
|
FBuildTarget:=Project1;
|
|
if FBuildTarget<>nil then
|
|
FBuildTarget.IDEOptions.AddHandlerDestroy(@OnProjectDestroy);
|
|
SetBuildTarget('','','','',ScanFPCSrc,Quiet);
|
|
end;
|
|
|
|
procedure TBuildManager.SetBuildTargetIDE(aQuiet: boolean);
|
|
var
|
|
NewTargetOS: String;
|
|
NewTargetCPU: String;
|
|
NewLCLWidgetSet, NewSubtarget: String;
|
|
begin
|
|
//debugln(['TBuildManager.SetBuildTargetIDE START']);
|
|
FBuildTarget:=nil;
|
|
with MiscellaneousOptions do begin
|
|
NewTargetOS:=BuildLazOpts.TargetOS;
|
|
NewTargetCPU:=BuildLazOpts.TargetCPU;
|
|
NewSubtarget:=BuildLazOpts.Subtarget;
|
|
NewLCLWidgetSet:=LCLPlatformDirNames[BuildLazOpts.TargetPlatform];
|
|
end;
|
|
if ConsoleVerbosity>=1 then
|
|
debugln(['Hint: (lazarus) [TBuildManager.SetBuildTargetIDE] OS=',NewTargetOS,' CPU=',NewTargetCPU,' Subtarget=',NewSubtarget,' WS=',NewLCLWidgetSet]);
|
|
SetBuildTarget(NewTargetOS,NewTargetCPU,NewSubtarget,NewLCLWidgetSet,smsfsBackground,aQuiet);
|
|
end;
|
|
|
|
function TBuildManager.BuildTargetIDEIsDefault: boolean;
|
|
// check if current BuildLazarus creates the normal lazarus exe
|
|
// aka not some cross compile
|
|
var
|
|
NewTargetOS: String;
|
|
NewTargetCPU: String;
|
|
NewLCLWidgetSet: TLCLPlatform;
|
|
begin
|
|
with MiscellaneousOptions do begin
|
|
NewTargetOS:=BuildLazOpts.TargetOS;
|
|
NewTargetCPU:=BuildLazOpts.TargetCPU;
|
|
NewLCLWidgetSet:=BuildLazOpts.TargetPlatform;
|
|
end;
|
|
//debugln(['TBuildManager.BuildTargetIDEIsDefault NewTargetOS=',NewTargetOS,' Default=',GetDefaultTargetOS,' NewTargetCPU=',NewTargetCPU,' default=',GetDefaultTargetCPU,' ws=',LCLPlatformDisplayNames[NewLCLWidgetSet],' default=',LCLPlatformDisplayNames[GetDefaultLCLWidgetType]]);
|
|
Result:=((NewTargetOS='') or (CompareText(NewTargetOS, GetCompiledTargetOS)=0))
|
|
and ((NewTargetCPU='') or (CompareText(NewTargetCPU, GetCompiledTargetCPU)=0))
|
|
and (NewLCLWidgetSet<>lpNoGUI);
|
|
// Note: no need to check if CompilerFilename is the default
|
|
end;
|
|
|
|
initialization
|
|
EnvironmentOpts.GroupEnvironmentI18NCaption := @dlgGroupEnvironment;
|
|
|
|
end.
|
|
|