1 {
2 ***************************************************************************
3 * *
4 * This source is free software; you can redistribute it and/or modify *
5 * it under the terms of the GNU General Public License as published by *
6 * the Free Software Foundation; either version 2 of the License, or *
7 * (at your option) any later version. *
8 * *
9 * This code is distributed in the hope that it will be useful, but *
10 * WITHOUT ANY WARRANTY; without even the implied warranty of *
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
12 * General Public License for more details. *
13 * *
14 * A copy of the GNU General Public License is available on the World *
15 * Wide Web at <http://www.gnu.org/copyleft/gpl.html>. You can also *
16 * obtain it by writing to the Free Software Foundation, *
17 * Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1335, USA. *
18 * *
19 ***************************************************************************
20
21 Author: Mattias Gaertner
22
23 Abstract:
24 Parser for Free Pascal Compiler output.
25 }
26 unit etFPCMsgParser;
27
28 {$mode objfpc}{$H+}
29
30 { $DEFINE VerboseFPCMsgUnitNotFound}
31
32 interface
33
34 uses
35 // RTL
36 Classes, SysUtils, strutils, math,
37 // CodeTools
38 KeywordFuncLists, CodeToolsFPCMsgs, CodeCache, FileProcs, CodeToolManager,
39 DirectoryCacher, BasicCodeTools, DefineTemplates, SourceLog, LinkScanner,
40 // LazUtils
41 LConvEncoding, LazUTF8, FileUtil, LazFileUtils, LazFileCache, LazUtilities,
42 AvgLvlTree,
43 // IDEIntf
44 IDEExternToolIntf, PackageIntf, LazIDEIntf, ProjectIntf, MacroIntf, IDEUtils,
45 // IDE
46 IDECmdLine, LazarusIDEStrConsts, EnvironmentOpts, LazConf, TransferMacros,
47 etMakeMsgParser;
48
49 const
50 FPCMsgIDCompiling = 3104;
51 FPCMsgIDLogo = 11023;
52 FPCMsgIDCantFindUnitUsedBy = 10022;
53 FPCMsgIDLinking = 9015;
54 FPCMsgIDErrorWhileLinking = 9013;
55 FPCMsgIDErrorWhileCompilingResources = 9029;
56 FPCMsgIDCallingResourceCompiler = 9028;
57 FPCMsgIDThereWereErrorsCompiling = 10026;
58 FPCMsgIDMethodIdentifierExpected = 3047;
59 FPCMsgIDIdentifierNotFound = 5000;
60 FPCMsgIDChecksumChanged = 10028;
61 FPCMsgIDUnitNotUsed = 5023; // Unit "$1" not used in $2
62 FPCMsgIDCompilationAborted = 1018;
63 FPCMsgIDLinesCompiled = 1008;
64
65 FPCMsgAttrWorkerDirectory = 'WD';
66 FPCMsgAttrMissingUnit = 'MissingUnit';
67 FPCMsgAttrUsedByUnit = 'UsedByUnit';
68 type
69 TFPCMsgFilePool = class;
70
71 { TFPCMsgFilePoolItem }
72
73 TFPCMsgFilePoolItem = class
74 private
75 FMsgFile: TFPCMsgFile;
76 FFilename: string;
77 FPool: TFPCMsgFilePool;
78 FLoadedFileAge: integer;
79 fUseCount: integer;
80 public
81 constructor Create(aPool: TFPCMsgFilePool; const aFilename: string);
82 destructor Destroy; override;
83 property Pool: TFPCMsgFilePool read FPool;
84 property Filename: string read FFilename;
85 property LoadedFileAge: integer read FLoadedFileAge;
GetMsgnull86 function GetMsg(ID: integer): TFPCMsgItem;
87 property MsgFile: TFPCMsgFile read FMsgFile;
88 property UseCount: integer read fUseCount;
89 end;
90
91 TETLoadFileEvent = procedure(aFilename: string; out s: string) of object;
92
93 { TFPCMsgFilePool }
94
95 TFPCMsgFilePool = class(TComponent)
96 private
97 fCritSec: TRTLCriticalSection;
98 FDefaultEnglishFile: string;
99 FDefaultTranslationFile: string;
100 FFiles: TFPList; // list of TFPCMsgFilePoolItem sorted for loaded
101 FOnLoadFile: TETLoadFileEvent;
102 fPendingLog: TStrings;
103 fMsgFileStamp: integer;
104 fCurrentEnglishFile: string; // valid only if fMsgFileStamp=CompilerParseStamp
105 fCurrentTranslationFile: string; // valid only if fMsgFileStamp=CompilerParseStamp
106 procedure Log(Msg: string; AThread: TThread);
107 procedure LogSync;
108 procedure SetDefaultEnglishFile(AValue: string);
109 procedure SetDefaultTranslationFile(AValue: string);
110 public
111 constructor Create(AOwner: TComponent); override;
112 destructor Destroy; override;
LoadCurrentEnglishFilenull113 function LoadCurrentEnglishFile(UpdateFromDisk: boolean;
114 AThread: TThread): TFPCMsgFilePoolItem; virtual; // don't forget UnloadFile
115 function LoadFile(aFilename: string; UpdateFromDisk: boolean;
116 AThread: TThread): TFPCMsgFilePoolItem; // don't forget UnloadFile
117 procedure UnloadFile(var aFile: TFPCMsgFilePoolItem);
118 procedure EnterCriticalsection;
119 procedure LeaveCriticalSection;
120 procedure GetMsgFileNames(CompilerFilename, TargetOS, TargetCPU: string;
121 out anEnglishFile, aTranslationFile: string); virtual; // (main thread)
122 property DefaultEnglishFile: string read FDefaultEnglishFile write SetDefaultEnglishFile;
123 property DefaulTranslationFile: string read FDefaultTranslationFile write SetDefaultTranslationFile;
124 property OnLoadFile: TETLoadFileEvent read FOnLoadFile write FOnLoadFile; // (main or workerthread)
125 end;
126
127 { TPatternToMsgID }
128
129 TPatternToMsgID = class
130 public
131 Pattern: string;
132 MsgID: integer;
133 PatternLine: integer; // line index in a multi line pattern, starting at 0
134 end;
135 PPatternToMsgID = ^TPatternToMsgID;
136
137 { TPatternToMsgIDs }
138
139 TPatternToMsgIDs = class
140 private
141 fItems: array of TPatternToMsgID;
IndexOfnull142 function IndexOf(Pattern: PChar; Insert: boolean): integer;
143 public
144 constructor Create;
145 destructor Destroy; override;
146 procedure Clear;
147 procedure Add(Pattern: string; MsgID: integer; PatternLine: integer = 0);
148 procedure AddLines(const Lines: string; MsgID: integer);
LineToMsgIDnull149 function LineToMsgID(p: PChar): integer; inline; // 0 = not found
LineToPatternnull150 function LineToPattern(p: PChar): PPatternToMsgID;
151 procedure WriteDebugReport;
152 procedure ConsistencyCheck;
153 end;
154
155 { TIDEFPCParser }
156
157 TIDEFPCParser = class(TFPCParser)
158 private
159 fCurSource: TCodeBuffer;
160 fFileExists: TFilenameToPointerTree;
161 fIncludePath: string; // only valid if fIncludePathValidForWorkerDir=Tool.WorkerDirectory
162 fIncludePathValidForWorkerDir: string;
163 fUnitPath: string; // only valid if fUnitPathValidForWorkerDir=Tool.WorkerDirectory
164 fUnitPathValidForWorkerDir: string;
165 fLastWorkerImprovedMessage: array[TExtToolParserSyncPhase] of integer;
166 fLineToMsgID: TPatternToMsgIDs;
167 fMissingFPCMsgItem: TFPCMsgItem;
168 fMsgID: Integer; // current message id given by ReadLine (-vq)
169 fMsgIsStdErr: boolean;
170 fMsgItemCantFindUnitUsedBy: TFPCMsgItem;
171 fMsgItemCompilationAborted: TFPCMsgItem;
172 fMsgItemErrorWhileCompilingResources: TFPCMsgItem;
173 fMsgItemErrorWhileLinking: TFPCMsgItem;
174 fMsgItemMethodIdentifierExpected: TFPCMsgItem;
175 fMsgItemIdentifierNotFound: TFPCMsgItem;
176 fMsgItemThereWereErrorsCompiling: TFPCMsgItem;
177 fMsgItemChecksumChanged: TFPCMsgItem;
178 fMsgItemUnitNotUsed: TFPCMsgItem;
179 fOutputIndex: integer; // current OutputIndex given by ReadLine
180 procedure FetchIncludePath(aPhase: TExtToolParserSyncPhase; MsgWorkerDir: String);
181 procedure FetchUnitPath(aPhase: TExtToolParserSyncPhase; MsgWorkerDir: String);
FileExistsnull182 function FileExists(const Filename: string; aSynchronized: boolean): boolean;
CheckForMsgIdnull183 function CheckForMsgId(p: PChar): boolean; // (MsgId) message
CheckFollowUpMessagenull184 function CheckFollowUpMessage(p: PChar): boolean;
CheckForFileLineColMessagenull185 function CheckForFileLineColMessage(p: PChar): boolean; // the normal messages: filename(y,x): Hint: ..
CheckForGeneralMessagenull186 function CheckForGeneralMessage(p: PChar): boolean; // Fatal: .., Error: ..., Panic: ..
CheckForInfosnull187 function CheckForInfos(p: PChar): boolean; // e.g. Free Pascal Compiler version 2.6.4 [2014/02/26] for i386
CheckForCompilingStatenull188 function CheckForCompilingState(p: PChar): boolean; // Compiling ..
CheckForAssemblingStatenull189 function CheckForAssemblingState(p: PChar): boolean; // Assembling ..
CheckForLinesCompilednull190 function CheckForLinesCompiled(p: PChar): boolean; // ..lines compiled..
CheckForExecutableInfonull191 function CheckForExecutableInfo(p: PChar): boolean;
CheckForLineProgressnull192 function CheckForLineProgress(p: PChar): boolean; // 600 206.521/231.648 Kb Used
CheckForLoadFromUnitnull193 function CheckForLoadFromUnit(p: PChar): Boolean;
CheckForWindresErrorsnull194 function CheckForWindresErrors(p: PChar): boolean;
CheckForLinkerErrorsnull195 function CheckForLinkerErrors(p: PChar): boolean;
CheckForAssemblerErrorsnull196 function CheckForAssemblerErrors(p: PChar): boolean;
CheckForUnspecificStdErrnull197 function CheckForUnspecificStdErr(p: PChar): boolean;
CreateMsgLinenull198 function CreateMsgLine: TMessageLine;
199 procedure AddLinkingMessages;
200 procedure AddResourceMessages;
NeedSourcenull201 function NeedSource(aPhase: TExtToolParserSyncPhase; SourceOk: boolean): boolean;
202 procedure ImproveMsgHiddenByIDEDirective(aPhase: TExtToolParserSyncPhase;
203 MsgLine: TMessageLine; SourceOK: Boolean);
204 procedure ImproveMsgSenderNotUsed(aPhase: TExtToolParserSyncPhase; MsgLine: TMessageLine);
205 procedure ImproveMsgUnitNotUsed(aPhase: TExtToolParserSyncPhase; MsgLine: TMessageLine);
206 procedure ImproveMsgUnitNotFound(aPhase: TExtToolParserSyncPhase;
207 MsgLine: TMessageLine);
208 procedure ImproveMsgLinkerUndefinedReference(aPhase: TExtToolParserSyncPhase;
209 MsgLine: TMessageLine);
210 procedure ImproveMsgIdentifierPosition(aPhase: TExtToolParserSyncPhase;
211 MsgLine: TMessageLine; SourceOK: boolean);
FindSrcViaPPUnull212 function FindSrcViaPPU(aPhase: TExtToolParserSyncPhase; MsgLine: TMessageLine;
213 const PPUFilename: string): boolean;
214 procedure Translate(p: PChar; MsgItem, TranslatedItem: TFPCMsgItem;
215 out TranslatedMsg: String; out MsgType: TMessageLineUrgency);
ReverseInstantFPCCacheDirnull216 function ReverseInstantFPCCacheDir(var aFilename: string; aSynchronized: boolean): boolean;
ReverseTestBuildDirnull217 function ReverseTestBuildDir(MsgLine: TMessageLine; var aFilename: string): boolean;
LongenFilenamenull218 function LongenFilename(MsgLine: TMessageLine; aFilename: string): string; // (worker thread)
219 protected
GetDefaultPCFullVersionnull220 function GetDefaultPCFullVersion: LongWord; virtual;
ToUTF8null221 function ToUTF8(const Line: string): string; virtual;
222 public
223 DirectoryStack: TStrings;
224 MsgFilename: string; // e.g. /path/to/fpcsrc/compiler/msg/errore.msg
225 MsgFile: TFPCMsgFilePoolItem;
226 TranslationFilename: string; // e.g. /path/to/fpcsrc/compiler/msg/errord.msg
227 TranslationFile: TFPCMsgFilePoolItem;
228 InstantFPCCache: string; // with trailing pathdelim
229 TestBuildDir: string; // with trailing pathdelim
230 VirtualProjectFiles: TFilenameToPointerTree;
231 PC_FullVersion: LongWord;
232 constructor Create(AOwner: TComponent); override;
233 destructor Destroy; override;
234 procedure Init; override; // called after macros resolved, before starting thread (main thread)
235 procedure InitReading; override; // called when process started, before first line (worker thread)
236 procedure Done; override; // called after process stopped (worker thread)
237 procedure ReadLine(Line: string; OutputIndex: integer; IsStdErr: boolean;
238 var Handled: boolean); override;
239 procedure AddMsgLine(MsgLine: TMessageLine); override;
240 procedure ImproveMessages(aPhase: TExtToolParserSyncPhase); override;
GetFPCMsgIDPatternnull241 function GetFPCMsgIDPattern(MsgID: integer): string; override;
IsMsgIDnull242 function IsMsgID(MsgLine: TMessageLine; MsgID: integer;
243 var Item: TFPCMsgItem): boolean;
CanParseSubToolnull244 class function CanParseSubTool(const SubTool: string): boolean; override;
DefaultSubToolnull245 class function DefaultSubTool: string; override;
GetMsgPatternnull246 class function GetMsgPattern(SubTool: string; MsgID: integer;
247 out Urgency: TMessageLineUrgency): string; override;
GetMsgHintnull248 class function GetMsgHint(SubTool: string; MsgID: integer): string;
249 override;
Prioritynull250 class function Priority: integer; override;
MsgLineIsIdnull251 class function MsgLineIsId(Msg: TMessageLine; MsgId: integer;
252 out Value1, Value2: string): boolean; override;
GetFPCMsgPatternnull253 class function GetFPCMsgPattern(Msg: TMessageLine): string; override;
GetFPCMsgValue1null254 class function GetFPCMsgValue1(Msg: TMessageLine): string; override;
GetFPCMsgValuesnull255 class function GetFPCMsgValues(Msg: TMessageLine; out Value1, Value2: string): boolean; override;
MsgFilePoolnull256 class function MsgFilePool: TFPCMsgFilePool; virtual;
257 end;
258
259 var
260 FPCMsgFilePool: TFPCMsgFilePool = nil;
261
262 // thread safe
FPCMsgToMsgUrgencynull263 function FPCMsgToMsgUrgency(Msg: TFPCMsgItem): TMessageLineUrgency;
FPCMsgTypeToUrgencynull264 function FPCMsgTypeToUrgency(const Typ: string): TMessageLineUrgency;
TranslateFPCMsgnull265 function TranslateFPCMsg(const Src, SrcPattern, TargetPattern: string): string;
FPCMsgFitsnull266 function FPCMsgFits(const Msg, Pattern: string;
267 VarStarts: PPChar = nil; VarEnds: PPChar = nil // 10 PChars
268 ): boolean;
GetFPCMsgValue1null269 function GetFPCMsgValue1(const Src, Pattern: string; out Value1: string): boolean;
GetFPCMsgValues2null270 function GetFPCMsgValues2(Src, Pattern: string; out Value1, Value2: string): boolean;
271
272 // not thread safe
IsFileInIDESrcDirnull273 function IsFileInIDESrcDir(Filename: string): boolean; // (main thread)
274
275 procedure RegisterFPCParser;
276
277 implementation
278
FPCMsgTypeToUrgencynull279 function FPCMsgTypeToUrgency(const Typ: string): TMessageLineUrgency;
280 begin
281 Result:=mluNone;
282 if (Typ='') or (length(Typ)<>1) then exit;
283 case UpChars[Typ[1]] of
284 'F': Result:=mluFatal;
285 'E': Result:=mluError;
286 'W': Result:=mluWarning;
287 'N': Result:=mluNote;
288 'H': Result:=mluHint;
289 'I': Result:=mluVerbose; // info
290 'L': Result:=mluProgress; // line number
291 'C': Result:=mluVerbose; // conditional: like IFDEFs
292 'U': Result:=mluVerbose2; // used: found files
293 'T': Result:=mluVerbose3; // tried: tried paths, general information
294 'D': Result:=mluDebug;
295 'X': Result:=mluProgress; // e.g. Size of Code
296 'O': Result:=mluProgress; // e.g., "press enter to continue"
297 else
298 Result:=mluNone;
299 end;
300 end;
301
FPCMsgToMsgUrgencynull302 function FPCMsgToMsgUrgency(Msg: TFPCMsgItem): TMessageLineUrgency;
303 begin
304 Result:=mluNone;
305 if Msg=nil then exit;
306 Result:=FPCMsgTypeToUrgency(Msg.ShownTyp);
307 if Result<>mluNone then exit;
308 Result:=FPCMsgTypeToUrgency(Msg.Typ);
309 if Result=mluNone then begin
310 //debugln(['FPCMsgToMsgUrgency Msg.ShownTyp="',Msg.ShownTyp,'" Msg.Typ="',Msg.Typ,'"']);
311 Result:=mluVerbose3;
312 end;
313 end;
314
IsFPCMsgVarnull315 function IsFPCMsgVar(p: PChar): boolean; inline;
316 begin
317 Result:=(p^='$') and (p[1] in ['0'..'9']);
318 end;
319
IsFPCMsgEndOrVarnull320 function IsFPCMsgEndOrVar(p: PChar): boolean; inline;
321 begin
322 Result:=(p^=#0) or IsFPCMsgVar(p);
323 end;
324
TranslateFPCMsgnull325 function TranslateFPCMsg(const Src, SrcPattern, TargetPattern: string): string;
326 { for example:
327 Src='A lines compiled, B sec C'
328 SrcPattern='$1 lines compiled, $2 sec $3'
329 TargetPattern='$1 Zeilen uebersetzt, $2 Sekunden $3'
330
331 Result='A Zeilen uebersetzt, B Sekunden C'
332 }
333 var
334 SrcPos: PChar;
335 TargetPatPos: PChar;
336 TargetPos: PChar;
337 SrcVarStarts, SrcVarEnds: array[0..9] of PChar;
338 VarUsed: array[0..9] of integer;
339 i: Integer;
340 begin
341 Result:='';
342 {$IFDEF VerboseFPCTranslate}
343 debugln(['TranslateFPCMsg Src="',Src,'" SrcPattern="',SrcPattern,'" TargetPattern="',TargetPattern,'"']);
344 {$ENDIF}
345 if (Src='') or (SrcPattern='') or (TargetPattern='') then exit;
346
347 if not FPCMsgFits(Src,SrcPattern,@SrcVarStarts[0],@SrcVarEnds[0]) then
348 exit;
349
350 for i:=Low(SrcVarStarts) to high(SrcVarStarts) do
351 VarUsed[i]:=0;
352
353 // create Target
354 SetLength(Result,length(TargetPattern)+length(Src));
355 TargetPatPos:=PChar(TargetPattern);
356 TargetPos:=PChar(Result);
357 while TargetPatPos^<>#0 do begin
358 //debugln(['TranslateFPCMsg Target ',dbgs(Pointer(TargetPatPos)),' ',ord(TargetPatPos^),' TargetPatPos="',TargetPatPos,'"']);
359 if IsFPCMsgVar(TargetPatPos) then begin
360 // insert variable
361 inc(TargetPatPos);
362 i:=ord(TargetPatPos^)-ord('0');
363 inc(TargetPatPos);
364 if SrcVarStarts[i]<>nil then begin
365 inc(VarUsed[i]);
366 if VarUsed[i]>1 then begin
367 // variable is used more than once => realloc result
368 dec(TargetPos,{%H-}PtrUInt(PChar(Result)));
369 SetLength(Result,length(Result)+SrcVarEnds[i]-SrcVarStarts[i]);
370 inc(TargetPos,{%H-}PtrUInt(PChar(Result)));
371 end;
372 SrcPos:=SrcVarStarts[i];
373 while SrcPos<SrcVarEnds[i] do begin
374 TargetPos^:=SrcPos^;
375 inc(TargetPos);
376 inc(SrcPos);
377 end;
378 end;
379 end else begin
380 // copy text from TargetPattern
381 TargetPos^:=TargetPatPos^;
382 inc(TargetPatPos);
383 inc(TargetPos);
384 end;
385 end;
386 SetLength(Result,TargetPos-PChar(Result));
387 if Result<>'' then
388 UTF8FixBroken(PChar(Result));
389
390 {$IFDEF VerboseFPCTranslate}
391 debugln(['TranslateFPCMsg Result="',Result,'"']);
392 {$ENDIF}
393 end;
394
FPCMsgFitsnull395 function FPCMsgFits(const Msg, Pattern: string; VarStarts: PPChar;
396 VarEnds: PPChar): boolean;
397 { for example:
398 Src='A lines compiled, B sec C'
399 SrcPattern='$1 lines compiled, $2 sec $3'
400
401 VarStarts and VarEnds can be nil.
402 If you need the boundaries of the parameters allocate VarStarts and VarEnds as
403 VarStarts:=GetMem(SizeOf(PChar)*10);
404 VarEnds:=GetMem(SizeOf(PChar)*10);
405 VarStarts[0] will be $0, VarStarts[1] will be $1 and so forth
406
407 }
408 var
409 MsgPos, PatPos: PChar;
410 MsgPos2, PatPos2: PChar;
411 i: Integer;
412 begin
413 Result:=false;
414 {$IFDEF VerboseFPCTranslate}
415 debugln(['FPCMsgFits Msg="',Msg,'" Pattern="',Pattern,'"']);
416 {$ENDIF}
417 if (Msg='') or (Pattern='') then exit;
418 MsgPos:=PChar(Msg);
419 PatPos:=PChar(Pattern);
420 // skip the characters of Msg copied from Pattern
421 while not IsFPCMsgEndOrVar(PatPos) do begin
422 if (MsgPos^<>PatPos^) then begin
423 // Pattern does not fit
424 {$IFDEF VerboseFPCTranslate}
425 debugln(['FPCMsgFits skipping start of Src and SrcPattern failed']);
426 {$ENDIF}
427 exit;
428 end;
429 inc(MsgPos);
430 inc(PatPos)
431 end;
432 {$IFDEF VerboseFPCTranslate}
433 debugln(['FPCMsgFits skipped start: SrcPos="',SrcPos,'" SrcPatPos="',SrcPatPos,'"']);
434 {$ENDIF}
435 if VarStarts<>nil then begin
436 FillByte(VarStarts^,SizeOf(PChar)*10,0);
437 FillByte(VarEnds^,SizeOf(PChar)*10,0);
438 end;
439 // find the parameters in Msg and store their boundaries in VarStarts, VarEnds
440 while (PatPos^<>#0) do begin
441 // read variable number
442 inc(PatPos);
443 i:=ord(PatPos^)-ord('0');
444 inc(PatPos);
445 if (VarEnds<>nil) and (VarEnds[i]=nil) then begin
446 VarStarts[i]:=MsgPos;
447 VarEnds[i]:=nil;
448 end;
449 // find the end of the parameter in Msg
450 // example: Pattern='$1 found' Msg='Ha found found'
451 repeat
452 if MsgPos^=PatPos^ then begin
453 {$IFDEF VerboseFPCTranslate}
454 debugln(['FPCMsgFits candidate for param ',i,' end: SrcPos="',SrcPos,'" SrcPatPos="',SrcPatPos,'"']);
455 {$ENDIF}
456 MsgPos2:=MsgPos;
457 PatPos2:=PatPos;
458 while (MsgPos2^=PatPos2^) and not IsFPCMsgEndOrVar(PatPos2) do begin
459 inc(MsgPos2);
460 inc(PatPos2);
461 end;
462 if IsFPCMsgEndOrVar(PatPos2) then begin
463 {$IFDEF VerboseFPCTranslate}
464 debugln(['FPCMsgFits param ',i,' end found: SrcPos2="',SrcPos2,'" SrcPatPos2="',SrcPatPos2,'"']);
465 {$ENDIF}
466 if (VarEnds<>nil) and (VarEnds[i]=nil) then
467 VarEnds[i]:=MsgPos;
468 MsgPos:=MsgPos2;
469 PatPos:=PatPos2;
470 break;
471 end;
472 {$IFDEF VerboseFPCTranslate}
473 debugln(['FPCMsgFits searching further...']);
474 {$ENDIF}
475 end else if MsgPos^=#0 then begin
476 if IsFPCMsgEndOrVar(PatPos) then begin
477 // empty parameter at end
478 if (VarEnds<>nil) and (VarEnds[i]=nil) then
479 VarEnds[i]:=MsgPos;
480 break;
481 end else begin
482 // Pattern does not fit Msg
483 {$IFDEF VerboseFPCTranslate}
484 debugln(['FPCMsgFits finding end of parameter ',i,' failed']);
485 {$ENDIF}
486 exit;
487 end;
488 end;
489 inc(MsgPos);
490 until false;
491 end;
492 Result:=true;
493 end;
494
GetFPCMsgValue1null495 function GetFPCMsgValue1(const Src, Pattern: string; out Value1: string
496 ): boolean;
497 { Pattern: 'Compiling $1'
498 Src: 'Compiling fcllaz.pas'
499 Value1: 'fcllaz.pas'
500 }
501 var
502 p: SizeInt;
503 l: SizeInt;
504 begin
505 Value1:='';
506 Result:=false;
507 if length(Src)<length(Pattern)-2 then exit;
508 p:=Pos('$1',Pattern);
509 if p<1 then exit;
510 // check start pattern
511 if (p>1) and (not CompareMem(Pointer(Src),Pointer(Pattern),p-1)) then exit;
512 // check end pattern
513 l:=length(Pattern)-p-2;
514 if (l>0)
515 and (not CompareMem(Pointer(Src)+length(Src)-l,Pointer(Pattern)+p+2,l)) then exit;
516 Value1:=copy(Src,p,length(Src)-length(Pattern)+2);
517 Result:=true;
518 end;
519
GetFPCMsgValues2null520 function GetFPCMsgValues2(Src, Pattern: string; out Value1, Value2: string
521 ): boolean;
522 { Pattern: 'Unit $1 was not found but $2 exists'
523 Src: 'Unit dialogprocs was not found but dialogpr exists'
524 Value1: 'dialogprocs'
525 Value1: 'dialogpr'
526 Not supported: '$1$2'
527 }
528 var
529 p1: SizeInt;
530 LastPattern: String;
531 p2: SizeInt;
532 MiddlePattern: String;
533 SrcP1Behind: Integer;
534 SrcP2: Integer;
535 begin
536 Result:=false;
537 Value1:='';
538 Value2:='';
539 p1:=Pos('$1',Pattern);
540 if p1<1 then exit;
541 p2:=Pos('$2',Pattern);
542 if p2<=p1+2 then exit;
543 if LeftStr(Pattern,p1-1)<>LeftStr(Src,p1-1) then exit;
544 LastPattern:=RightStr(Pattern,length(Pattern)-p2-1);
545 if RightStr(Src,length(LastPattern))<>LastPattern then exit;
546 MiddlePattern:=copy(Pattern,p1+2,p2-p1-2);
547 SrcP1Behind:=PosEx(MiddlePattern,Src,p1+2);
548 if SrcP1Behind<1 then exit;
549 Value1:=copy(Src,p1,SrcP1Behind-p1);
550 SrcP2:=SrcP1Behind+length(MiddlePattern);
551 Value2:=copy(Src,SrcP2,length(Src)-SrcP2-length(LastPattern)+1);
552 Result:=true;
553 end;
554
IsFileInIDESrcDirnull555 function IsFileInIDESrcDir(Filename: string): boolean;
556 var
557 LazDir: String;
558 begin
559 Filename:=TrimFilename(Filename);
560 if not FilenameIsAbsolute(Filename) then exit(false);
561 LazDir:=AppendPathDelim(EnvironmentOptions.GetParsedLazarusDirectory);
562 Result:=FileIsInPath(Filename,LazDir+'ide')
563 or FileIsInPath(Filename,LazDir+'debugger')
564 or FileIsInPath(Filename,LazDir+'packager')
565 or FileIsInPath(Filename,LazDir+'converter')
566 or FileIsInPath(Filename,LazDir+'designer');
567 end;
568
569 procedure RegisterFPCParser;
570 begin
571 ExternalToolList.RegisterParser(TIDEFPCParser);
572 end;
573
574 { TPatternToMsgIDs }
575
IndexOfnull576 function TPatternToMsgIDs.IndexOf(Pattern: PChar; Insert: boolean): integer;
577 var
578 l: Integer;
579 r: Integer;
580 m: Integer;
581 ItemP: PChar;
582 FindP: PChar;
583 cmp: Integer;
584 begin
585 Result:=-1;
586 l:=0;
587 r:=length(fItems)-1;
588 cmp:=0;
589 m:=0;
590 while (l<=r) do begin
591 m:=(l+r) div 2;
592 ItemP:=PChar(fItems[m].Pattern);
593 FindP:=Pattern;
594 while (ItemP^=FindP^) do begin
595 if ItemP^=#0 then
596 exit(m); // exact match
597 inc(ItemP);
598 inc(FindP);
599 end;
600 if ItemP^ in [#0,'$'] then begin
601 // Pattern longer than Item
602 if not Insert then begin
603 if (Result<0) or (length(fItems[m].Pattern)>length(fItems[Result].Pattern))
604 then
605 Result:=m;
606 end;
607 end;
608 cmp:=ord(ItemP^)-ord(FindP^);
609 if cmp<0 then
610 l:=m+1
611 else
612 r:=m-1;
613 end;
614 if Insert then begin
615 if cmp<0 then
616 Result:=m+1
617 else
618 Result:=m;
619 end;
620 end;
621
622 constructor TPatternToMsgIDs.Create;
623 begin
624
625 end;
626
627 destructor TPatternToMsgIDs.Destroy;
628 begin
629 Clear;
630 inherited Destroy;
631 end;
632
633 procedure TPatternToMsgIDs.Clear;
634 var
635 i: Integer;
636 begin
637 for i:=0 to length(fItems)-1 do
638 fItems[i].Free;
639 SetLength(fItems,0);
640 end;
641
642 procedure TPatternToMsgIDs.Add(Pattern: string; MsgID: integer;
643 PatternLine: integer);
644
645 procedure RaiseInvalidMsgID;
646 begin
647 raise Exception.Create('invalid MsgID: '+IntToStr(MsgID));
648 end;
649
650 var
651 i: Integer;
652 Item: TPatternToMsgID;
653 Cnt: Integer;
654 begin
655 if MsgID=0 then
656 RaiseInvalidMsgID;
657 Pattern:=Trim(Pattern);
658 if (Pattern='') or (Pattern[1]='$') then exit;
659 i:=IndexOf(PChar(Pattern),true);
660 Cnt:=length(fItems);
661 SetLength(fItems,Cnt+1);
662 if Cnt-i>0 then
663 Move(fItems[i],fItems[i+1],SizeOf(TPatternToMsgID)*(Cnt-i));
664 Item:=TPatternToMsgID.Create;
665 fItems[i]:=Item;
666 Item.Pattern:=Pattern;
667 Item.MsgID:=MsgID;
668 Item.PatternLine:=PatternLine;
669 end;
670
671 procedure TPatternToMsgIDs.AddLines(const Lines: string; MsgID: integer);
672 var
673 StartPos: PChar;
674 p: PChar;
675 PatternLine: Integer;
676 begin
677 PatternLine:=0;
678 p:=PChar(Lines);
679 while p^<>#0 do begin
680 StartPos:=p;
681 while not (p^ in [#0,#10,#13]) do inc(p);
682 if p>StartPos then begin
683 Add(copy(Lines,StartPos-PChar(Lines)+1,p-StartPos),MsgID,PatternLine);
684 inc(PatternLine);
685 end;
686 while p^ in [#10,#13] do inc(p);
687 end;
688 end;
689
TPatternToMsgIDs.LineToMsgIDnull690 function TPatternToMsgIDs.LineToMsgID(p: PChar): integer;
691 var
692 Item: PPatternToMsgID;
693 begin
694 Item:=LineToPattern(p);
695 if Item=nil then
696 Result:=0
697 else
698 Result:=Item^.MsgID;
699 end;
700
LineToPatternnull701 function TPatternToMsgIDs.LineToPattern(p: PChar): PPatternToMsgID;
702 var
703 i: Integer;
704 begin
705 while p^ in [' ',#9,#10,#13] do inc(p);
706 i:=IndexOf(p,false);
707 if i<0 then
708 Result:=nil
709 else
710 Result:=@fItems[i];
711 end;
712
713 procedure TPatternToMsgIDs.WriteDebugReport;
714 var
715 i: Integer;
716 begin
717 debugln(['TLineStartToMsgIDs.WriteDebugReport Count=',length(fItems)]);
718 for i:=0 to Length(fItems)-1 do begin
719 debugln([' ID=',fItems[i].MsgID,'="',fItems[i].Pattern,'"']);
720 end;
721 ConsistencyCheck;
722 end;
723
724 procedure TPatternToMsgIDs.ConsistencyCheck;
725
726 procedure E(Msg: string);
727 begin
728 raise Exception.Create(Msg);
729 end;
730
731 var
732 i: Integer;
733 Item: TPatternToMsgID;
734 begin
735 for i:=0 to Length(fItems)-1 do begin
736 Item:=fItems[i];
737 if Item.MsgID<=0 then
738 E('Item.MsgID<=0');
739 if Item.Pattern='' then
740 E('Item.Pattern empty');
741 if IndexOf(PChar(Item.Pattern),false)<>i then
742 E('IndexOf '+dbgs(i)+' "'+Item.Pattern+'" IndexOf='+dbgs(IndexOf(PChar(Item.Pattern),false)));
743 end;
744 end;
745
746 { TFPCMsgFilePool }
747
748 procedure TFPCMsgFilePool.Log(Msg: string; AThread: TThread);
749 begin
750 EnterCriticalsection;
751 try
752 fPendingLog.Add(Msg);
753 finally
754 LeaveCriticalSection;
755 end;
756 if AThread<>nil then
757 LogSync
758 else
759 TThread.Synchronize(AThread,@LogSync);
760 end;
761
762 procedure TFPCMsgFilePool.LogSync;
763 begin
764 EnterCriticalsection;
765 try
766 dbgout(fPendingLog.Text);
767 finally
768 LeaveCriticalSection;
769 end;
770 end;
771
772 procedure TFPCMsgFilePool.SetDefaultEnglishFile(AValue: string);
773 begin
774 if FDefaultEnglishFile=AValue then Exit;
775 FDefaultEnglishFile:=AValue;
776 fMsgFileStamp:=-1;
777 end;
778
779 procedure TFPCMsgFilePool.SetDefaultTranslationFile(AValue: string);
780 begin
781 if FDefaultTranslationFile=AValue then Exit;
782 FDefaultTranslationFile:=AValue;
783 fMsgFileStamp:=-1;
784 end;
785
786 constructor TFPCMsgFilePool.Create(AOwner: TComponent);
787 begin
788 inherited Create(AOwner);
789 InitCriticalSection(fCritSec);
790 FFiles:=TFPList.Create;
791 fPendingLog:=TStringList.Create;
792 fMsgFileStamp:=-1;
793 end;
794
795 destructor TFPCMsgFilePool.Destroy;
796 var
797 i: Integer;
798 Item: TFPCMsgFilePoolItem;
799 begin
800 EnterCriticalsection;
801 try
802 // free unused files
803 for i:=FFiles.Count-1 downto 0 do begin
804 Item:=TFPCMsgFilePoolItem(FFiles[i]);
805 if Item.fUseCount=0 then begin
806 Item.Free;
807 FFiles.Delete(i);
808 end else begin
809 if ExitCode=0 then
810 debugln(['TFPCMsgFilePool.Destroy file still used: ',Item.Filename]);
811 end;
812 end;
813 if FFiles.Count>0 then begin
814 if ExitCode<>0 then
815 exit;
816 raise Exception.Create('TFPCMsgFilePool.Destroy some files are still used');
817 end;
818 FreeAndNil(FFiles);
819 if FPCMsgFilePool=Self then
820 FPCMsgFilePool:=nil;
821 inherited Destroy;
822 FreeAndNil(fPendingLog);
823 finally
824 LeaveCriticalSection;
825 end;
826 DoneCriticalsection(fCritSec);
827 end;
828
TFPCMsgFilePool.LoadCurrentEnglishFilenull829 function TFPCMsgFilePool.LoadCurrentEnglishFile(UpdateFromDisk: boolean;
830 AThread: TThread): TFPCMsgFilePoolItem;
831 var
832 anEnglishFile: string;
833 aTranslationFile: string;
834 begin
835 Result:=nil;
836 GetMsgFileNames(EnvironmentOptions.GetParsedCompilerFilename,'','',
837 anEnglishFile,aTranslationFile);
838 //writeln('TFPCMsgFilePool.LoadCurrentEnglishFile ',anEnglishFile);
839 if not FilenameIsAbsolute(anEnglishFile) then exit;
840 Result:=LoadFile(anEnglishFile,UpdateFromDisk,AThread);
841 end;
842
LoadFilenull843 function TFPCMsgFilePool.LoadFile(aFilename: string; UpdateFromDisk: boolean;
844 AThread: TThread): TFPCMsgFilePoolItem;
845 var
846 IsMainThread: Boolean;
847
848 procedure ResultOutdated;
849 begin
850 // cached file needs update
851 if Result.fUseCount=0 then begin
852 FFiles.Remove(Result);
853 Result.Free;
854 end;
855 Result:=nil;
856 end;
857
FileExistsnull858 function FileExists: boolean;
859 begin
860 if IsMainThread then
861 Result:=FileExistsCached(aFilename)
862 else
863 Result:=FileExistsUTF8(aFilename);
864 end;
865
FileAgenull866 function FileAge: longint;
867 begin
868 if IsMainThread then
869 Result:=FileAgeCached(aFilename)
870 else
871 Result:=FileAgeUTF8(aFilename);
872 end;
873
874 var
875 Item: TFPCMsgFilePoolItem;
876 i: Integer;
877 NewItem: TFPCMsgFilePoolItem;
878 FileTxt: string;
879 ms: TMemoryStream;
880 Encoding: String;
881 begin
882 Result:=nil;
883 if aFilename='' then exit;
884 aFilename:=TrimAndExpandFilename(aFilename);
885 //Log('TFPCMsgFilePool.LoadFile '+aFilename,aThread);
886
887 IsMainThread:=GetThreadID=MainThreadID;
888 if UpdateFromDisk then begin
889 if not FileExists then begin
890 Log('TFPCMsgFilePool.LoadFile file not found: '+aFilename,AThread);
891 exit;
892 end;
893 end;
894 NewItem:=nil;
895 ms:=nil;
896 EnterCriticalsection;
897 try
898 // search the newest version in cache
899 for i:=FFiles.Count-1 downto 0 do begin
900 Item:=TFPCMsgFilePoolItem(FFiles[i]);
901 if CompareFilenames(Item.Filename,aFilename)<>0 then continue;
902 Result:=Item;
903 break;
904 end;
905 if UpdateFromDisk then begin
906 if (Result<>nil)
907 and (FileAge<>Result.LoadedFileAge) then
908 ResultOutdated;
909 end else if Result=nil then begin
910 // not yet loaded, not yet checked if file exists -> check now
911 if not FileExists then
912 exit;
913 end;
914
915 if Result<>nil then begin
916 // share
917 inc(Result.fUseCount);
918 end else begin
919 // load for the first time
920 NewItem:=TFPCMsgFilePoolItem.Create(Self,aFilename);
921 //Log('TFPCMsgFilePool.LoadFile '+dbgs(NewItem.FMsgFile<>nil)+' '+aFilename,aThread);
922 if Assigned(OnLoadFile) then begin
923 OnLoadFile(aFilename,FileTxt);
924 end else begin
925 ms:=TMemoryStream.Create;
926 ms.LoadFromFile(aFilename);
927 SetLength(FileTxt,ms.Size);
928 ms.Position:=0;
929 if FileTxt<>'' then
930 ms.Read(FileTxt[1],length(FileTxt));
931 end;
932 // convert encoding
933 Encoding:=GetDefaultFPCErrorMsgFileEncoding(aFilename);
934 FileTxt:=ConvertEncoding(FileTxt,Encoding,EncodingUTF8);
935 // parse
936 NewItem.FMsgFile.LoadFromText(FileTxt);
937 NewItem.FLoadedFileAge:=FileAge;
938 // load successful
939 Result:=NewItem;
940 NewItem:=nil;
941 FFiles.Add(Result);
942 inc(Result.fUseCount);
943 //log('TFPCMsgFilePool.LoadFile '+Result.Filename+' '+dbgs(Result.fUseCount),aThread);
944 end;
945 finally
946 ms.Free;
947 FreeAndNil(NewItem);
948 LeaveCriticalSection;
949 end;
950 end;
951
952 procedure TFPCMsgFilePool.UnloadFile(var aFile: TFPCMsgFilePoolItem);
953 var
954 i: Integer;
955 Item: TFPCMsgFilePoolItem;
956 Keep: Boolean;
957 begin
958 EnterCriticalsection;
959 try
960 if aFile.fUseCount<=0 then
961 raise Exception.Create('TFPCMsgFilePool.UnloadFile already freed');
962 if FFiles.IndexOf(aFile)<0 then
963 raise Exception.Create('TFPCMsgFilePool.UnloadFile unknown, maybe already freed');
964 dec(aFile.fUseCount);
965 //log('TFPCMsgFilePool.UnloadFile '+aFile.Filename+' UseCount='+dbgs(aFile.fUseCount),aThread);
966 if aFile.fUseCount>0 then exit;
967 // not used anymore
968 if not FileExistsUTF8(aFile.Filename) then begin
969 Keep:=false;
970 end else begin
971 // file still exist on disk
972 // => check if it is the newest version
973 Keep:=true;
974 for i:=FFiles.Count-1 downto 0 do begin
975 Item:=TFPCMsgFilePoolItem(FFiles[i]);
976 if Item=aFile then break;
977 if CompareFilenames(Item.Filename,aFile.Filename)<>0 then continue;
978 // there is already a newer version
979 Keep:=false;
980 break;
981 end;
982 end;
983 if Keep then begin
984 // this file is the newest version => keep it in cache
985 end else begin
986 //log('TFPCMsgFilePool.UnloadFile free: '+aFile.Filename,aThread);
987 FFiles.Remove(aFile);
988 aFile.Free;
989 end;
990 finally
991 aFile:=nil;
992 LeaveCriticalSection;
993 end;
994 end;
995
996 procedure TFPCMsgFilePool.EnterCriticalsection;
997 begin
998 System.EnterCriticalsection(fCritSec);
999 end;
1000
1001 procedure TFPCMsgFilePool.LeaveCriticalSection;
1002 begin
1003 System.LeaveCriticalsection(fCritSec);
1004 end;
1005
1006 procedure TFPCMsgFilePool.GetMsgFileNames(CompilerFilename, TargetOS,
1007 TargetCPU: string; out anEnglishFile, aTranslationFile: string);
1008 var
1009 FPCVer: String;
1010 FPCSrcDir: String;
1011 aFilename: String;
1012 CompilerKind: TPascalCompiler;
1013 begin
1014 if fMsgFileStamp<>CompilerParseStamp then begin
1015 fCurrentEnglishFile:=DefaultEnglishFile;
1016 fCurrentTranslationFile:=DefaulTranslationFile;
1017 // English msg file
1018 // => use fpcsrcdir/compiler/msg/errore.msg
1019 // the fpcsrcdir might depend on the FPC version
1020 FPCVer:=CodeToolBoss.CompilerDefinesCache.GetPCVersion(
1021 CompilerFilename,TargetOS,TargetCPU,false,CompilerKind);
1022 if CompilerKind<>pcFPC then
1023 ;// ToDo
1024 FPCSrcDir:=EnvironmentOptions.GetParsedFPCSourceDirectory(FPCVer);
1025 if FilenameIsAbsolute(FPCSrcDir) then begin
1026 // FPCSrcDir exists => use the errore.msg
1027 aFilename:=AppendPathDelim(FPCSrcDir)+GetForcedPathDelims('compiler/msg/errore.msg');
1028 if FileExistsCached(aFilename) then
1029 fCurrentEnglishFile:=aFilename;
1030 end;
1031 if not FileExistsCached(fCurrentEnglishFile) then begin
1032 // as fallback use the copy in the Codetools directory
1033 aFilename:=EnvironmentOptions.GetParsedLazarusDirectory;
1034 if FilenameIsAbsolute(aFilename) then begin
1035 aFilename:=AppendPathDelim(aFilename)+GetForcedPathDelims('components/codetools/fpc.errore.msg');
1036 if FileExistsCached(aFilename) then
1037 fCurrentEnglishFile:=aFilename;
1038 end;
1039 end;
1040 // translation msg file
1041 aFilename:=EnvironmentOptions.GetParsedCompilerMessagesFilename;
1042 if FilenameIsAbsolute(aFilename) and FileExistsCached(aFilename)
1043 and (CompareFilenames(aFilename,fCurrentEnglishFile)<>0) then
1044 fCurrentTranslationFile:=aFilename;
1045 fMsgFileStamp:=CompilerParseStamp;
1046 end;
1047 anEnglishFile:=fCurrentEnglishFile;
1048 aTranslationFile:=fCurrentTranslationFile;
1049 end;
1050
1051 { TFPCMsgFilePoolItem }
1052
1053 constructor TFPCMsgFilePoolItem.Create(aPool: TFPCMsgFilePool;
1054 const aFilename: string);
1055 begin
1056 inherited Create;
1057 FPool:=aPool;
1058 FFilename:=aFilename;
1059 FMsgFile:=TFPCMsgFile.Create;
1060 end;
1061
1062 destructor TFPCMsgFilePoolItem.Destroy;
1063 begin
1064 FreeAndNil(FMsgFile);
1065 FFilename:='';
1066 inherited Destroy;
1067 end;
1068
TFPCMsgFilePoolItem.GetMsgnull1069 function TFPCMsgFilePoolItem.GetMsg(ID: integer): TFPCMsgItem;
1070 begin
1071 Result:=FMsgFile.FindWithID(ID);
1072 end;
1073
1074 { TIDEFPCParser }
1075
1076 destructor TIDEFPCParser.Destroy;
1077 begin
1078 FreeAndNil(VirtualProjectFiles);
1079 FreeAndNil(FFilesToIgnoreUnitNotUsed);
1080 FreeAndNil(fFileExists);
1081 FreeAndNil(fCurSource);
1082 if TranslationFile<>nil then
1083 MsgFilePool.UnloadFile(TranslationFile);
1084 if MsgFile<>nil then
1085 MsgFilePool.UnloadFile(MsgFile);
1086 FreeAndNil(DirectoryStack);
1087 FreeAndNil(fLineToMsgID);
1088 inherited Destroy;
1089 end;
1090
1091 procedure TIDEFPCParser.Init;
1092
1093 procedure LoadMsgFile(aFilename: string; var List: TFPCMsgFilePoolItem);
1094 begin
1095 //debugln(['TFPCParser.Init load Msg filename=',aFilename]);
1096 if aFilename='' then
1097 debugln(['WARNING: TFPCParser.Init missing msg file'])
1098 else if (aFilename<>'') and (List=nil) then begin
1099 try
1100 List:=MsgFilePool.LoadFile(aFilename,true,nil);
1101 {$IFDEF VerboseExtToolThread}
1102 debugln(['LoadMsgFile successfully read ',aFilename]);
1103 {$ENDIF}
1104 except
1105 on E: Exception do begin
1106 debugln(['WARNING: TFPCParser.Init failed to load file '+aFilename+': '+E.Message]);
1107 end;
1108 end;
1109 end;
1110 end;
1111
1112 var
1113 i: Integer;
1114 Param: String;
1115 p: PChar;
1116 aTargetOS: String;
1117 aTargetCPU: String;
1118 aProject: TLazProject;
1119 aProjFile: TLazProjectFile;
1120 begin
1121 inherited Init;
1122
1123 PC_FullVersion:=GetDefaultPCFullVersion;
1124
1125 if MsgFilePool<>nil then begin
1126 aTargetOS:='';
1127 aTargetCPU:='';
1128 for i:=0 to Tool.Process.Parameters.Count-1 do begin
1129 Param:=Tool.Process.Parameters[i];
1130 if Param='' then continue;
1131 p:=PChar(Param);
1132 if p^<>'-' then continue;
1133 if p[1]='T' then
1134 aTargetOS:=copy(Param,3,255)
1135 else if p[1]='P' then
1136 aTargetCPU:=copy(Param,3,255);
1137 end;
1138 MsgFilePool.GetMsgFileNames(Tool.Process.Executable,aTargetOS,aTargetCPU,
1139 MsgFilename,TranslationFilename);
1140 end;
1141
1142 LoadMsgFile(MsgFilename,MsgFile);
1143 if TranslationFilename<>'' then
1144 LoadMsgFile(TranslationFilename,TranslationFile);
1145
1146 // get include search path
1147 fIncludePathValidForWorkerDir:=Tool.WorkerDirectory;
1148 fIncludePath:=CodeToolBoss.GetIncludePathForDirectory(
1149 ChompPathDelim(fIncludePathValidForWorkerDir));
1150 // get unit search path
1151 fUnitPathValidForWorkerDir:=Tool.WorkerDirectory;
1152 fUnitPath:=CodeToolBoss.GetUnitPathForDirectory(
1153 ChompPathDelim(fUnitPathValidForWorkerDir));
1154
1155 // get instantfpc cache directory
1156 InstantFPCCache:='$(InstantFPCCache)';
1157 if IDEMacros.SubstituteMacros(InstantFPCCache) then
1158 InstantFPCCache:=AppendPathDelim(InstantFPCCache)
1159 else
1160 InstantFPCCache:='';
1161
1162 // get TestBuildDir
1163 if Tool.CurrentDirectoryIsTestDir then begin
1164 // source filenames in CurrentDirectory must be reversed back
1165 // -> store the list of virtual filenames (needed by worker thread)
1166 TestBuildDir:=AppendPathDelim(ResolveDots(Tool.Process.CurrentDirectory));
1167 if VirtualProjectFiles=nil then
1168 VirtualProjectFiles:=TFilenameToPointerTree.Create(true);
1169 aProject:=LazarusIDE.ActiveProject;
1170 for i:=0 to aProject.FileCount-1 do begin
1171 aProjFile:=aProject.Files[i];
1172 if aProjFile.IsPartOfProject and (not FilenameIsAbsolute(aProjFile.Filename)) then
1173 VirtualProjectFiles[aProjFile.Filename]:=Tool;
1174 end;
1175 end else
1176 TestBuildDir:='';
1177 end;
1178
1179 procedure TIDEFPCParser.InitReading;
1180
1181 procedure AddPatternItem(MsgID: integer);
1182 var
1183 Item: TFPCMsgItem;
1184 begin
1185 Item:=MsgFile.GetMsg(MsgID);
1186 if Item<>nil then
1187 fLineToMsgID.AddLines(Item.Pattern,Item.ID);
1188 end;
1189
1190 var
1191 p: TExtToolParserSyncPhase;
1192 begin
1193 inherited InitReading;
1194
1195 fLineToMsgID.Clear;
1196 AddPatternItem(FPCMsgIDLogo);
1197 AddPatternItem(FPCMsgIDLinking);
1198 AddPatternItem(FPCMsgIDCallingResourceCompiler);
1199 //fLineToMsgID.WriteDebugReport;
1200
1201 for p:=low(fLastWorkerImprovedMessage) to high(fLastWorkerImprovedMessage) do
1202 fLastWorkerImprovedMessage[p]:=-1;
1203
1204 FreeAndNil(DirectoryStack);
1205 end;
1206
1207 procedure TIDEFPCParser.Done;
1208 begin
1209 FreeAndNil(fCurSource);
1210 inherited Done;
1211 end;
1212
CheckForCompilingStatenull1213 function TIDEFPCParser.CheckForCompilingState(p: PChar): boolean;
1214 var
1215 OldP: PChar;
1216 AFilename: string;
1217 aDir: String;
1218 MsgLine: TMessageLine;
1219 NewFilename: String;
1220 begin
1221 OldP:=p;
1222 // for example 'Compiling ./subdir/unit1.pas'
1223 if fMsgID=0 then begin
1224 if not ReadString(p,'Compiling ') then exit(false);
1225 fMsgID:=FPCMsgIDCompiling;
1226 Result:=true;
1227 end else if fMsgID=FPCMsgIDCompiling then begin
1228 Result:=true;
1229 if not ReadString(p,'Compiling ') then exit;
1230 end else begin
1231 exit(false);
1232 end;
1233 // add path to history
1234 if (p^='.') and (p[1]=PathDelim) then
1235 inc(p,2); // skip ./
1236 AFilename:=TrimFilename(p);
1237 aDir:=ExtractFilePath(AFilename);
1238 if aDir<>'' then begin
1239 // make absolute
1240 if (not FilenameIsAbsolute(aDir)) and (Tool.WorkerDirectory<>'') then begin
1241 aDir:=TrimFilename(AppendPathDelim(Tool.WorkerDirectory)+aDir);
1242 AFilename:=aDir+ExtractFileName(AFilename);
1243 end;
1244 // reverse instantfpc cache
1245 if (InstantFPCCache<>'') and (Tool.WorkerDirectory<>'')
1246 and (FilenameIsAbsolute(aDir))
1247 and (CompareFilenames(InstantFPCCache,aDir)=0) then
1248 begin
1249 NewFilename:=AppendPathDelim(Tool.WorkerDirectory)+ExtractFileName(AFilename);
1250 if FileExists(NewFilename,false) then begin
1251 AFilename:=NewFilename;
1252 aDir:=Tool.WorkerDirectory;
1253 end;
1254 end;
1255 // store directory
1256 if DirectoryStack=nil then DirectoryStack:=TStringList.Create;
1257 if (DirectoryStack.Count=0)
1258 or (DirectoryStack[DirectoryStack.Count-1]<>aDir) then
1259 DirectoryStack.Add(aDir);
1260 end;
1261 MsgLine:=CreateMsgLine;
1262 MsgLine.Urgency:=mluProgress;
1263 MsgLine.SubTool:=DefaultSubTool;
1264 MsgLine.Filename:=AFilename;
1265 MsgLine.Msg:=OldP;
1266 inherited AddMsgLine(MsgLine);
1267 Result:=true;
1268 end;
1269
CheckForAssemblingStatenull1270 function TIDEFPCParser.CheckForAssemblingState(p: PChar): boolean;
1271 var
1272 MsgLine: TMessageLine;
1273 OldP: PChar;
1274 begin
1275 Result:=fMsgID=9001;
1276 if (not Result) and (fMsgID>0) then exit;
1277 OldP:=p;
1278 if (not Result) and (not CompStr('Assembling ',p)) then exit;
1279 MsgLine:=CreateMsgLine;
1280 MsgLine.Urgency:=mluProgress;
1281 MsgLine.SubTool:=DefaultSubTool;
1282 MsgLine.Urgency:=mluProgress;
1283 MsgLine.Msg:=OldP;
1284 inherited AddMsgLine(MsgLine);
1285 Result:=true;
1286 end;
1287
TIDEFPCParser.CheckForGeneralMessagenull1288 function TIDEFPCParser.CheckForGeneralMessage(p: PChar): boolean;
1289 { check for
1290 Fatal: message
1291 Hint: (11030) Start of reading config file /etc/fpc.cfg
1292 Error: /usr/bin/ppc386 returned an error exitcode
1293 }
1294 const
1295 FrontEndFPCExitCodeError = 'returned an error exitcode';
1296 var
1297 MsgLine: TMessageLine;
1298 MsgType: TMessageLineUrgency;
1299 p2: PChar;
1300 i: Integer;
1301 TranslatedItem: TFPCMsgItem;
1302 MsgItem: TFPCMsgItem;
1303 TranslatedMsg: String;
1304
1305 procedure CheckFinalNote;
1306 // check if there was already an error message
1307 // if yes, then downgrade this message to a mluVerbose
1308 var
1309 u: TMessageLineUrgency;
1310 begin
1311 for u:=mluError to high(TMessageLineUrgency) do
1312 if Tool.WorkerMessages.UrgencyCounts[u]>0 then
1313 begin
1314 MsgType:=mluVerbose;
1315 exit;
1316 end;
1317 end;
1318
1319 begin
1320 Result:=false;
1321 MsgType:=mluNone;
1322 if ReadString(p,'Fatal: ') then begin
1323 MsgType:=mluFatal;
1324 // check for "Fatal: compilation aborted"
1325 if fMsgItemCompilationAborted=nil then begin
1326 fMsgItemCompilationAborted:=MsgFile.GetMsg(FPCMsgIDCompilationAborted);
1327 if fMsgItemCompilationAborted=nil then
1328 fMsgItemCompilationAborted:=fMissingFPCMsgItem;
1329 end;
1330 p2:=p;
1331 if (fMsgItemCompilationAborted<>fMissingFPCMsgItem)
1332 and ReadString(p2,fMsgItemCompilationAborted.Pattern) then
1333 CheckFinalNote;
1334 end
1335 else if ReadString(p,'Panic') then
1336 MsgType:=mluPanic
1337 else if ReadString(p,'Error: ') then begin
1338 // check for fpc frontend message "Error: /usr/bin/ppc386 returned an error exitcode"
1339 TranslatedMsg:=p;
1340 MsgType:=mluError;
1341 if Pos(FrontEndFPCExitCodeError,TranslatedMsg)>0 then begin
1342 fMsgID:=FPCMsgIDCompilationAborted;
1343 CheckFinalNote;
1344 end;
1345 end
1346 else if ReadString(p,'Warn: ') or ReadString(p,'Warning:') then
1347 MsgType:=mluWarning
1348 else if ReadString(p,'Note: ') then
1349 MsgType:=mluNote
1350 else if ReadString(p,'Hint: ') then
1351 MsgType:=mluHint
1352 else if ReadString(p,'Debug: ') then
1353 MsgType:=mluDebug
1354 else begin
1355 exit;
1356 end;
1357 if MsgType=mluNone then exit;
1358
1359 Result:=true;
1360 while p^ in [' ',#9] do inc(p);
1361 TranslatedMsg:='';
1362 if (p^='(') and (p[1] in ['0'..'9']) then begin
1363 p2:=p;
1364 inc(p2);
1365 i:=0;
1366 while (p2^ in ['0'..'9']) and (i<1000000) do begin
1367 i:=i*10+ord(p2^)-ord('0');
1368 inc(p2);
1369 end;
1370 if p2^=')' then begin
1371 fMsgID:=i;
1372 p:=p2+1;
1373 while p^ in [' ',#9] do inc(p);
1374 //if Pos('reading',String(p))>0 then
1375 // debugln(['TFPCParser.CheckForGeneralMessage ID=',fMsgID,' Msg=',p]);
1376 if (fMsgID>0) then begin
1377 TranslatedItem:=nil;
1378 MsgItem:=nil;
1379 if (MsgFile<>nil) then
1380 MsgItem:=MsgFile.GetMsg(fMsgID);
1381 if (TranslationFile<>nil) then
1382 TranslatedItem:=TranslationFile.GetMsg(fMsgID);
1383 Translate(p,MsgItem,TranslatedItem,TranslatedMsg,MsgType);
1384 if (TranslatedItem=nil) and (MsgItem=nil) then begin
1385 if ConsoleVerbosity>=1 then
1386 debugln(['TFPCParser.CheckForGeneralMessage msgid not found: ',fMsgID]);
1387 end;
1388 end;
1389
1390 end;
1391 end;
1392 if (MsgType>=mluError) and (fMsgID=FPCMsgIDCompilationAborted) // fatal: Compilation aborted
1393 then begin
1394 CheckFinalNote;
1395 end;
1396 MsgLine:=CreateMsgLine;
1397 MsgLine.Urgency:=MsgType;
1398 MsgLine.SubTool:=DefaultSubTool;
1399 MsgLine.Msg:=p;
1400 MsgLine.TranslatedMsg:=TranslatedMsg;
1401 AddMsgLine(MsgLine);
1402 end;
1403
CheckForLineProgressnull1404 function TIDEFPCParser.CheckForLineProgress(p: PChar): boolean;
1405 // for example: 600 206.521/231.648 Kb Used
1406 var
1407 OldP: PChar;
1408 MsgLine: TMessageLine;
1409 begin
1410 Result:=false;
1411 OldP:=p;
1412 if not ReadNumberWithThousandSep(p) then exit;
1413 if not ReadChar(p,' ') then exit;
1414 if not ReadNumberWithThousandSep(p) then exit;
1415 if not ReadChar(p,'/') then exit;
1416 if not ReadNumberWithThousandSep(p) then exit;
1417 if not ReadChar(p,' ') then exit;
1418 MsgLine:=CreateMsgLine;
1419 MsgLine.SubTool:=DefaultSubTool;
1420 MsgLine.Urgency:=mluProgress;
1421 MsgLine.Msg:=OldP;
1422 inherited AddMsgLine(MsgLine);
1423 Result:=true;
1424 end;
1425
TIDEFPCParser.CheckForLinesCompilednull1426 function TIDEFPCParser.CheckForLinesCompiled(p: PChar): boolean;
1427 var
1428 OldStart: PChar;
1429 MsgLine: TMessageLine;
1430 begin
1431 Result:=fMsgID=FPCMsgIDLinesCompiled;
1432 if (not Result) and (fMsgID>0) then exit;
1433 OldStart:=p;
1434 if not Result then begin
1435 if not ReadNumberWithThousandSep(p) then exit;
1436 if not ReadString(p,' lines compiled, ') then exit;
1437 if not ReadNumberWithThousandSep(p) then exit;
1438 end;
1439 Result:=true;
1440 MsgLine:=CreateMsgLine;
1441 MsgLine.SubTool:=DefaultSubTool;
1442 if ShowLinesCompiled then
1443 MsgLine.Urgency:=mluImportant
1444 else
1445 MsgLine.Urgency:=mluVerbose;
1446 MsgLine.Msg:=OldStart;
1447 inherited AddMsgLine(MsgLine);
1448 end;
1449
TIDEFPCParser.CheckForExecutableInfonull1450 function TIDEFPCParser.CheckForExecutableInfo(p: PChar): boolean;
1451 { For example:
1452 Size of Code: 1184256 bytes
1453 Size of initialized data: 519168 bytes
1454 Size of uninitialized data: 83968 bytes
1455 Stack space reserved: 262144 bytes
1456 Stack space commited: 4096 bytes
1457 }
1458 var
1459 OldStart: PChar;
1460 MsgLine: TMessageLine;
1461 begin
1462 Result:=(fMsgID>=9130) and (fMsgID<=9140);
1463 if (not Result) and (fMsgID>0) then exit;
1464 OldStart:=p;
1465 if (not Result) then begin
1466 if not (ReadString(p,'Size of Code: ') or
1467 ReadString(p,'Size of initialized data: ') or
1468 ReadString(p,'Size of uninitialized data: ') or
1469 ReadString(p,'Stack space reserved: ') or
1470 ReadString(p,'Stack space commited: ') or // message contains typo
1471 ReadString(p,'Stack space committed: ')) then exit;
1472 if not ReadNumberWithThousandSep(p) then exit;
1473 if not ReadString(p,' bytes') then exit;
1474 end;
1475 Result:=true;
1476 MsgLine:=CreateMsgLine;
1477 MsgLine.SubTool:=DefaultSubTool;
1478 MsgLine.Urgency:=mluProgress;
1479 MsgLine.Msg:=OldStart;
1480 inherited AddMsgLine(MsgLine);
1481 end;
1482
CheckForWindresErrorsnull1483 function TIDEFPCParser.CheckForWindresErrors(p: PChar): boolean;
1484 // example: ...\windres.exe: warning: ...
1485 var
1486 MsgLine: TMessageLine;
1487 WPos: PChar;
1488 begin
1489 Result := false;
1490 WPos:=FindSubStrI('windres',p);
1491 if WPos=nil then exit;
1492 Result:=true;
1493 MsgLine:=CreateMsgLine;
1494 MsgLine.SubTool:=SubToolFPCWindRes;
1495 MsgLine.Urgency:=mluWarning;
1496 p := wPos + 7;
1497 if CompStr('.exe', p) then
1498 inc(p, 4);
1499 MsgLine.Msg:='windres' + p;
1500 AddMsgLine(MsgLine);
1501 end;
1502
CheckForLinkerErrorsnull1503 function TIDEFPCParser.CheckForLinkerErrors(p: PChar): boolean;
1504 const
1505 patUndefinedSymbol: String = 'Undefined symbols for architecture';
1506 patLD: String = '/usr/bin/ld: ';
1507 var
1508 MsgLine: TMessageLine;
1509 Urgency: TMessageLineUrgency;
1510 s: string;
1511 begin
1512 if CompareMem(PChar(patUndefinedSymbol),p,length(patUndefinedSymbol)) then
1513 begin
1514 MsgLine:=CreateMsgLine;
1515 MsgLine.MsgID:=0;
1516 MsgLine.SubTool:=SubToolFPCLinker;
1517 MsgLine.Urgency:=mluError;
1518 MsgLine.Msg:='linker: '+p;
1519 inherited AddMsgLine(MsgLine);
1520 exit(true);
1521 end;
1522 if CompareMem(PChar(patLD),p,length(patLD)) then
1523 begin
1524 MsgLine:=CreateMsgLine;
1525 MsgLine.MsgID:=0;
1526 MsgLine.SubTool:=SubToolFPCLinker;
1527 s:=p;
1528 Urgency:=mluHint;
1529 if fMsgIsStdErr then
1530 begin
1531 Urgency:=mluWarning;
1532 if (Pos('link.res',s)>0) and (Pos(' -T',s)>0) then
1533 // /usr/bin/ld: warning: /path/link.res contains output sections; did you forget -T?
1534 Urgency:=mluVerbose;
1535 end;
1536 MsgLine.Urgency:=Urgency;
1537 MsgLine.Msg:='linker: '+s;
1538 inherited AddMsgLine(MsgLine);
1539 exit(true);
1540 end;
1541 Result:=false;
1542 end;
1543
TIDEFPCParser.CheckForAssemblerErrorsnull1544 function TIDEFPCParser.CheckForAssemblerErrors(p: PChar): boolean;
1545 // example:
1546 // <stdin>:227:9: error: unsupported directive '.stabs'
1547 var
1548 APos: PChar;
1549 s: string;
1550 MsgLine: TMessageLine;
1551 begin
1552 Result:=false;
1553 APos:=FindSubStrI('error: unsupported directive',p);
1554 if APos=nil then exit;
1555 Result:=true;
1556 MsgLine:=CreateMsgLine;
1557 MsgLine.SubTool:=SubToolFPCWindRes;
1558 MsgLine.Urgency:=mluError;
1559 s:=APos;
1560 if Pos('.stabs',s)>0 then
1561 s+='. Hint: Use another type of debug info.';
1562 MsgLine.Msg:='assembler: '+s;
1563 AddMsgLine(MsgLine);
1564 end;
1565
CheckForUnspecificStdErrnull1566 function TIDEFPCParser.CheckForUnspecificStdErr(p: PChar): boolean;
1567 var
1568 MsgLine: TMessageLine;
1569 begin
1570 if not fMsgIsStdErr then exit(false);
1571 Result:=true;
1572 MsgLine:=CreateMsgLine;
1573 MsgLine.SubTool:=SubToolFPC;
1574 MsgLine.Urgency:=mluError;
1575 MsgLine.Msg:=p;
1576 AddMsgLine(MsgLine);
1577 end;
1578
TIDEFPCParser.CheckForInfosnull1579 function TIDEFPCParser.CheckForInfos(p: PChar): boolean;
1580
ReadFPCLogonull1581 function ReadFPCLogo(PatternItem: PPatternToMsgID;
1582 out FPCVersionAsInt: LongWord): boolean;
1583 var
1584 Line: string;
1585 Ranges: TFPCMsgRanges;
1586 aRange: PFPCMsgRange;
1587 i: SizeInt;
1588 aFPCFullVersion: String;
1589 FPCVersion: integer;
1590 FPCRelease: integer;
1591 FPCPatch: integer;
1592 begin
1593 Result:=false;
1594 FPCVersionAsInt:=0;
1595 i:=Pos('$FPCFULLVERSION',PatternItem^.Pattern);
1596 if i<1 then exit;
1597 Line:=p;
1598 Ranges:=nil;
1599 try
1600 ExtractFPCMsgParameters(PatternItem^.Pattern,Line,Ranges);
1601 if Ranges.Count>0 then begin
1602 // first is $FPCFULLVERSION
1603 aRange:=@Ranges.Ranges[0];
1604 aFPCFullVersion:=copy(Line,aRange^.StartPos+1,aRange^.EndPos-aRange^.StartPos);
1605 SplitFPCVersion(aFPCFullVersion,FPCVersion,FPCRelease,FPCPatch);
1606 FPCVersionAsInt:=FPCVersion*10000+FPCRelease*100+FPCPatch;
1607 Result:=FPCVersionAsInt>0;
1608 end;
1609 // second is $FPCDATE
1610 // third is $FPCCPU
1611 finally
1612 Ranges.Free;
1613 end;
1614 end;
1615
1616 var
1617 MsgItem: TFPCMsgItem;
1618 MsgLine: TMessageLine;
1619 MsgType: TMessageLineUrgency;
1620 PatternItem: PPatternToMsgID;
1621 aFPCVersion: LongWord;
1622 begin
1623 Result:=false;
1624 PatternItem:=fLineToMsgID.LineToPattern(p);
1625 if PatternItem=nil then exit;
1626 fMsgID:=PatternItem^.MsgID;
1627 if (fMsgID=FPCMsgIDLogo) and (DirectoryStack<>nil) then begin
1628 // a new call of the compiler (e.g. when compiling via make)
1629 // => clear stack
1630 FreeAndNil(DirectoryStack);
1631 end;
1632 MsgItem:=MsgFile.GetMsg(fMsgID);
1633 if MsgItem=nil then exit;
1634 Result:=true;
1635 MsgType:=FPCMsgToMsgUrgency(MsgItem);
1636 if MsgType=mluNone then
1637 MsgType:=mluVerbose;
1638 MsgLine:=CreateMsgLine;
1639 MsgLine.SubTool:=DefaultSubTool;
1640 MsgLine.Urgency:=MsgType;
1641 if (fMsgID=FPCMsgIDLogo) and ReadFPCLogo(PatternItem,aFPCVersion) then begin
1642 if aFPCVersion<>PC_FullVersion then begin
1643 // unexpected FPC version => always show
1644 MsgLine.Urgency:=mluImportant;
1645 PC_FullVersion:=aFPCVersion;
1646 end;
1647 end;
1648 AddMsgLine(MsgLine);
1649 end;
1650
CreateMsgLinenull1651 function TIDEFPCParser.CreateMsgLine: TMessageLine;
1652 begin
1653 Result:=inherited CreateMsgLine(fOutputIndex);
1654 Result.MsgID:=fMsgID;
1655 if fMsgIsStdErr then
1656 Result.Flags:=Result.Flags+[mlfStdErr];
1657 end;
1658
1659 procedure TIDEFPCParser.AddLinkingMessages;
1660 { Add messages for all output between "Linking ..." and the
1661 current line "Error while linking"
1662
1663 For example:
1664 Linking /home/user/project1
1665 /usr/bin/ld: warning: /home/user/link.res contains output sections; did you forget -T?
1666 /usr/bin/ld: cannot find -la52
1667 project1.lpr(20,1) Error: Error while linking
1668
1669 Examples for linking errors:
1670 linkerror.o(.text$_main+0x9):linkerror.pas: undefined reference to `NonExistingFunction'
1671
1672 /path/lib/x86_64-linux/blaunit.o: In function `FORMCREATE':
1673 /path//blaunit.pas:45: undefined reference to `BLAUNIT_BLABLA'
1674
1675 Closing script ppas.sh
1676
1677 Mac OS X linker example:
1678 ld: framework not found Cocoas
1679 Note: this comes in stderr, so it might be some lines after corresponding stdout
1680
1681 Multiline Mac OS X linker example:
1682 Undefined symbols:
1683 "_exterfunc", referenced from:
1684 _PASCALMAIN in testld.o
1685 "_exterfunc2", referenced from:
1686 _PASCALMAIN in testld.o
1687 ld: symbol(s) not found
1688
1689 Linking project1
1690 Undefined symbols for architecture x86_64:
1691 "_GetCurrentEventButtonState", referenced from:
1692 _COCOAINT_TCOCOAWIDGETSET_$__GETKEYSTATE$LONGINT$$SMALLINT in cocoaint.o
1693 ld: symbol(s) not found for architecture x86_64
1694 An error occurred while linking
1695
1696 Linking IDE:
1697 (9015) Linking ../lazarus
1698 /usr/bin/ld: cannot find -lGL
1699 make[2]: *** [lazarus] Error 1
1700 make[1]: *** [idepkg] Error 2
1701 make: *** [idepkg] Error 2
1702 /home/mattias/pascal/wichtig/lazarus/ide/lazarus.pp(161,1) Error: (9013) Error while linking
1703
1704 }
1705 var
1706 i: Integer;
1707 MsgLine: TMessageLine;
1708 begin
1709 // change all low urgency messages in front of the last message to Important
1710 i:=Tool.WorkerMessages.Count-1;
1711 while i>=0 do begin
1712 MsgLine:=Tool.WorkerMessages[i];
1713 //debugln(['TIDEFPCParser.AddLinkingMessages ',i,' ',dbgs(MsgLine.Urgency),' ',MsgLine.Msg]);
1714 if MsgLine.Urgency<mluHint then
1715 MsgLine.Urgency:=mluImportant
1716 else
1717 break;
1718 dec(i);
1719 end;
1720
1721 // add all skipped lines in front of the linking error
1722 i:=Tool.WorkerMessages.Count-1;
1723 if i<0 then exit;
1724 MsgLine:=Tool.WorkerMessages[i];
1725 //debugln(['TIDEFPCParser.AddLinkingMessages MsgLine.OutputIndex=',MsgLine.OutputIndex,' fOutputIndex=',fOutputIndex]);
1726 for i:=MsgLine.OutputIndex+1 to fOutputIndex-1 do begin
1727 MsgLine:=inherited CreateMsgLine(i);
1728 MsgLine.MsgID:=0;
1729 MsgLine.SubTool:=SubToolFPCLinker;
1730 if MsgLine.Msg<>'' then
1731 MsgLine.Urgency:=mluImportant
1732 else
1733 MsgLine.Urgency:=mluVerbose2;
1734 inherited AddMsgLine(MsgLine);
1735 end;
1736 end;
1737
1738 procedure TIDEFPCParser.AddResourceMessages;
1739 { Add messages for all output between "Calling resource compiler " and the
1740 current line "Error while compiling resources"
1741
1742 For example:
1743 Calling resource compiler "/usr/bin/fpcres" with "-o /home/user/project1.or -a x86_64 -of elf -v "@/home/user/project1.reslst"" as command line
1744 Debug: parsing command line parameters
1745 ...
1746 Error: Error while compiling resources
1747 }
1748 var
1749 i: Integer;
1750 MsgLine: TMessageLine;
1751 begin
1752 // find message "Calling resource compiler ..."
1753 i:=Tool.WorkerMessages.Count-1;
1754 while (i>=0) and (Tool.WorkerMessages[i].MsgID<>FPCMsgIDCallingResourceCompiler) do
1755 dec(i);
1756 if i<0 then exit;
1757 MsgLine:=Tool.WorkerMessages[i];
1758 for i:=MsgLine.OutputIndex+1 to fOutputIndex-1 do begin
1759 MsgLine:=inherited CreateMsgLine(i);
1760 MsgLine.MsgID:=0;
1761 MsgLine.SubTool:=SubToolFPCRes;
1762 if MsgLine.Msg<>'' then
1763 MsgLine.Urgency:=mluHint
1764 else
1765 MsgLine.Urgency:=mluVerbose2;
1766 inherited AddMsgLine(MsgLine);
1767 end;
1768 end;
1769
NeedSourcenull1770 function TIDEFPCParser.NeedSource(aPhase: TExtToolParserSyncPhase;
1771 SourceOk: boolean): boolean;
1772 begin
1773 if SourceOk then exit(false);
1774 case aPhase of
1775 etpspAfterReadLine: NeedSynchronize:=true;
1776 etpspSynchronized: NeedAfterSync:=true;
1777 end;
1778 Result:=true;
1779 end;
1780
IsMsgIDnull1781 function TIDEFPCParser.IsMsgID(MsgLine: TMessageLine; MsgID: integer;
1782 var Item: TFPCMsgItem): boolean;
1783 begin
1784 if MsgLine.MsgID=MsgID then exit(true);
1785 Result:=false;
1786 if MsgLine.MsgID<>0 then exit;
1787 if MsgLine.SubTool<>DefaultSubTool then exit;
1788 if Item=nil then begin
1789 Item:=MsgFile.GetMsg(MsgID);
1790 if Item=nil then
1791 Item:=fMissingFPCMsgItem;
1792 end;
1793 if Item=fMissingFPCMsgItem then exit;
1794 if Item.PatternFits(MsgLine.Msg)<0 then exit;
1795 MsgLine.MsgID:=MsgID;
1796 Result:=true;
1797 end;
1798
1799 procedure TIDEFPCParser.ImproveMsgHiddenByIDEDirective(
1800 aPhase: TExtToolParserSyncPhase; MsgLine: TMessageLine; SourceOK: Boolean);
1801 // check for {%H-}
1802
IsHnull1803 function IsH(p: PChar): boolean; inline;
1804 begin
1805 Result:=(p^='{') and (p[1]='%') and (p[2]='H') and (p[3]='-');
1806 end;
1807
1808 var
1809 p: PChar;
1810 X: Integer;
1811 Y: Integer;
1812 HasDirective: Boolean;
1813 AbsPos: Integer; // 0-based
1814 OtherPos: Integer;
1815 AtomEnd: integer;
1816 begin
1817 if MsgLine.Urgency>=mluError then exit;
1818 if mlfHiddenByIDEDirectiveValid in MsgLine.Flags then exit;
1819 if NeedSource(aPhase,SourceOK) then
1820 exit;
1821
1822 X:=MsgLine.Column;
1823 Y:=MsgLine.Line;
1824 if (y<=fCurSource.LineCount) and (x-1<=fCurSource.GetLineLength(y-1))
1825 then begin
1826 HasDirective:=false;
1827 AbsPos:=fCurSource.GetLineStart(y-1)+x-2; // 0-based
1828 p:=PChar(fCurSource.Source)+AbsPos;
1829 //debugln(['TFPCParser.ImproveMsgHiddenByIDEDirective ',MsgLine.Filename,' ',Y,',',X,' ',copy(fCurSource.GetLine(y-1),1,x-1),'|',copy(fCurSource.GetLine(y-1),x,100),' p=',p[0],p[1],p[2]]);
1830 if IsH(p) then
1831 // directive beginning at cursor
1832 HasDirective:=true
1833 else if (x>5) and IsH(p-5) then
1834 // directive ending at cursor
1835 HasDirective:=true
1836 else begin
1837 // different compiler versions report some message positions differently.
1838 // They changed some message positions from start to end of token.
1839 // => check other end of token
1840 //debugln(['TIDEFPCParser.ImproveMsgHiddenByIDEDirective mlfLeftToken=',mlfLeftToken in MsgLine.Flags]);
1841 if mlfLeftToken in MsgLine.Flags then begin
1842 if IsIdentChar[p[-1]] then begin
1843 OtherPos:=AbsPos+1;
1844 ReadPriorPascalAtom(fCurSource.Source,OtherPos,AtomEnd);
1845 if (OtherPos>5) and (AtomEnd=AbsPos+1)
1846 and IsH(@fCurSource.Source[OtherPos-5]) then begin
1847 // for example: {%H-}identifier|
1848 HasDirective:=true;
1849 end;
1850 end;
1851 end else begin
1852 if IsIdentStartChar[p^] then begin
1853 inc(p,GetIdentLen(p));
1854 if IsH(p) then
1855 // for example: |identifier{%H-}
1856 HasDirective:=true;
1857 end;
1858 end;
1859 end;
1860 if HasDirective then begin
1861 MsgLine.Flags:=MsgLine.Flags+[mlfHiddenByIDEDirective,
1862 mlfHiddenByIDEDirectiveValid];
1863 exit;
1864 end;
1865 end;
1866 MsgLine.Flags:=MsgLine.Flags+[mlfHiddenByIDEDirectiveValid];
1867 end;
1868
1869 procedure TIDEFPCParser.ImproveMsgSenderNotUsed(
1870 aPhase: TExtToolParserSyncPhase; MsgLine: TMessageLine);
1871 // FPCMsgIDParameterNotUsed = 5024; Parameter "$1" not used
1872 begin
1873 if aPhase<>etpspAfterReadLine then exit;
1874 if (MsgLine.Urgency<=mluVerbose) then exit;
1875 // check for Sender not used
1876 if HideHintsSenderNotUsed
1877 and (MsgLine.Msg='Parameter "Sender" not used') then begin
1878 MsgLine.Urgency:=mluVerbose;
1879 end;
1880 end;
1881
1882 procedure TIDEFPCParser.ImproveMsgUnitNotUsed(aPhase: TExtToolParserSyncPhase;
1883 MsgLine: TMessageLine);
1884 // check for Unit not used message in main sources
1885 // and change urgency to merely 'verbose'
1886 begin
1887 if aPhase<>etpspAfterReadLine then exit;
1888 if (MsgLine.Urgency<=mluVerbose) then exit;
1889 if not IsMsgID(MsgLine,FPCMsgIDUnitNotUsed,fMsgItemUnitNotUsed) then exit;
1890
1891 //debugln(['TIDEFPCParser.ImproveMsgUnitNotUsed ',aPhase=etpspSynchronized,' ',MsgLine.Msg]);
1892 // unit not used
1893 if IndexInStringList(FilesToIgnoreUnitNotUsed,cstFilename,MsgLine.Filename)>=0 then
1894 begin
1895 MsgLine.Urgency:=mluVerbose;
1896 end else if HideHintsUnitNotUsedInMainSource then begin
1897 if FilenameExtIs(MsgLine.Filename, 'lpr', true) then
1898 // a lpr does not use a unit => not important
1899 MsgLine.Urgency:=mluVerbose
1900 else if FilenameIsAbsolute(MsgLine.Filename)
1901 and FileExists(ChangeFileExt(MsgLine.Filename, '.lpk'), aPhase=etpspSynchronized)
1902 then begin
1903 // a lpk does not use a unit => not important
1904 MsgLine.Urgency:=mluVerbose;
1905 end;
1906 end;
1907 end;
1908
1909 procedure TIDEFPCParser.ImproveMsgUnitNotFound(aPhase: TExtToolParserSyncPhase;
1910 MsgLine: TMessageLine);
1911
1912 procedure FixSourcePos(CodeBuf: TCodeBuffer; MissingUnitname: string);
1913 var
1914 InPos: Integer;
1915 NamePos: Integer;
1916 Tool: TCodeTool;
1917 Caret: TCodeXYPosition;
1918 NewFilename: String;
1919 begin
1920 {$IFDEF VerboseFPCMsgUnitNotFound}
1921 debugln(['TIDEFPCParser.ImproveMsgUnitNotFound File=',CodeBuf.Filename]);
1922 {$ENDIF}
1923 LazarusIDE.SaveSourceEditorChangesToCodeCache(nil);
1924 if not CodeToolBoss.FindUnitInAllUsesSections(CodeBuf,MissingUnitname,NamePos,InPos)
1925 then begin
1926 DebugLn('TIDEFPCParser.ImproveMsgUnitNotFound FindUnitInAllUsesSections failed due to syntax errors or '+MissingUnitname+' is not used in '+CodeBuf.Filename);
1927 exit;
1928 end;
1929 Tool:=CodeToolBoss.CurCodeTool;
1930 if Tool=nil then exit;
1931 if not Tool.CleanPosToCaret(NamePos,Caret) then exit;
1932 if (Caret.X>0) and (Caret.Y>0) then begin
1933 //DebugLn('QuickFixUnitNotFoundPosition Line=',dbgs(Line),' Col=',dbgs(Col));
1934 NewFilename:=Caret.Code.Filename;
1935 MsgLine.SetSourcePosition(NewFilename,Caret.Y,Caret.X);
1936 end;
1937 end;
1938
1939 procedure FindPPUFiles(MissingUnitname: string; PkgList: TFPList;
1940 PPUFiles: TStringList // Strings:PPUFilename, Objects:TIDEPackage
1941 );
1942 var
1943 i: Integer;
1944 Pkg: TIDEPackage;
1945 DirCache: TCTDirectoryCache;
1946 PPUFilename: String;
1947 UnitOutDir: String;
1948 begin
1949 if PkgList=nil then exit;
1950 for i:=0 to PkgList.Count-1 do begin
1951 Pkg:=TIDEPackage(PkgList[i]);
1952 UnitOutDir:=Pkg.LazCompilerOptions.GetUnitOutputDirectory(false);
1953 //debugln(['TQuickFixUnitNotFoundPosition.Execute ',Pkg.Name,' UnitOutDir=',UnitOutDir]);
1954 if not FilenameIsAbsolute(UnitOutDir) then continue;
1955 DirCache:=CodeToolBoss.DirectoryCachePool.GetCache(UnitOutDir,true,false);
1956 PPUFilename:=DirCache.FindFile(MissingUnitname+'.ppu',ctsfcLoUpCase);
1957 //debugln(['TQuickFixUnitNotFoundPosition.Execute ShortPPU=',PPUFilename]);
1958 if PPUFilename='' then continue;
1959 PPUFilename:=AppendPathDelim(DirCache.Directory)+PPUFilename;
1960 PPUFiles.AddObject(PPUFilename,Pkg);
1961 end;
1962 end;
1963
1964 procedure FindPPUInInstalledPkgs(MissingUnitname: string;
1965 PPUFiles: TStringList // Strings:PPUFilename, Objects:TIDEPackage
1966 );
1967 var
1968 i: Integer;
1969 Pkg: TIDEPackage;
1970 PkgList: TFPList;
1971 begin
1972 // search ppu in installed packages
1973 PkgList:=TFPList.Create;
1974 try
1975 for i:=0 to PackageEditingInterface.GetPackageCount-1 do begin
1976 Pkg:=PackageEditingInterface.GetPackages(i);
1977 if Pkg.AutoInstall=pitNope then continue;
1978 PkgList.Add(Pkg);
1979 end;
1980 FindPPUFiles(MissingUnitname,PkgList,PPUFiles);
1981 finally
1982 PkgList.Free;
1983 end;
1984 end;
1985
1986 procedure FindPPUInModuleAndDeps(MissingUnitname: string; Module: TObject;
1987 PPUFiles: TStringList // Strings:PPUFilename, Objects:TIDEPackage
1988 );
1989 var
1990 PkgList: TFPList;
1991 begin
1992 PkgList:=nil;
1993 try
1994 PackageEditingInterface.GetRequiredPackages(Module,PkgList);
1995 if (Module is TIDEPackage) then begin
1996 if PkgList=nil then
1997 PkgList:=TFPList.Create;
1998 if PkgList.IndexOf(Module)<0 then
1999 PkgList.Add(Module);
2000 end;
2001 FindPPUFiles(MissingUnitname,PkgList,PPUFiles);
2002 finally
2003 PkgList.Free;
2004 end;
2005 end;
2006
2007 procedure FindPackage(MissingUnitname: string; OnlyInstalled: boolean;
2008 out Pkg: TIDEPackage; out PkgName: string; out PkgFile: TLazPackageFile);
2009 var
2010 i: Integer;
2011 j: Integer;
2012 aFile: TLazPackageFile;
2013 CurPkg: TIDEPackage;
2014 begin
2015 PkgName:='';
2016 PkgFile:=nil;
2017 Pkg:=nil;
2018 // search unit in packages
2019 for i:=0 to PackageEditingInterface.GetPackageCount-1 do begin
2020 CurPkg:=PackageEditingInterface.GetPackages(i);
2021 if OnlyInstalled and (CurPkg.AutoInstall=pitNope) then
2022 continue;
2023 if CompareTextCT(CurPkg.Name,MissingUnitname)=0 then begin
2024 PkgName:=CurPkg.Name;
2025 Pkg:=CurPkg;
2026 break;
2027 end;
2028 for j:=0 to CurPkg.FileCount-1 do begin
2029 aFile:=CurPkg.Files[j];
2030 if not (aFile.FileType in PkgFileRealUnitTypes) then
2031 continue;
2032 if CompareTextCT(ExtractFileNameOnly(aFile.Filename),MissingUnitname)<>0
2033 then continue;
2034 if (PkgFile=nil) or (aFile.InUses and not PkgFile.InUses) then
2035 begin
2036 // a better file was found
2037 PkgFile:=aFile;
2038 PkgName:=CurPkg.Name;
2039 Pkg:=CurPkg;
2040 end;
2041 end;
2042 end;
2043 end;
2044
2045 var
2046 MissingUnitName: string;
2047 UsedByUnit: string;
2048 Filename: String;
2049 NewFilename: String;
2050 CodeBuf: TCodeBuffer;
2051 Owners: TFPList;
2052 UsedByOwner: TObject;
2053 UsedByPkg: TIDEPackage;
2054 PPUFilename: String;
2055 OnlyInstalled: Boolean;
2056 s: String;
2057 PPUFiles: TStringList; // Strings:PPUFilename, Objects:TIDEPackage
2058 i: Integer;
2059 DepOwner: TObject;
2060 TheOwner: TObject;
2061 MissingPkg: TIDEPackage;
2062 MissingPkgName: String;
2063 MissingPkgFile: TLazPackageFile;
2064 FPCUnitFilename: String;
2065 begin
2066 if MsgLine.Urgency<mluError then exit;
2067 if not IsMsgID(MsgLine,FPCMsgIDCantFindUnitUsedBy,fMsgItemCantFindUnitUsedBy)
2068 then // Can't find unit $1 used by $2
2069 exit;
2070 case aPhase of
2071 etpspAfterReadLine:
2072 begin
2073 NeedSynchronize:=true;
2074 exit;
2075 end;
2076 etpspSynchronized: ;
2077 etpspAfterSync: exit;
2078 end;
2079
2080 // in main thread
2081
2082 if not GetFPCMsgValues(MsgLine,MissingUnitName,UsedByUnit) then
2083 exit;
2084 MsgLine.Attribute[FPCMsgAttrMissingUnit]:=MissingUnitName;
2085 MsgLine.Attribute[FPCMsgAttrUsedByUnit]:=UsedByUnit;
2086
2087 {$IFDEF VerboseFPCMsgUnitNotFound}
2088 debugln(['TIDEFPCParser.ImproveMsgUnitNotFound Missing="',MissingUnitname,'" used by "',UsedByUnit,'"']);
2089 {$ENDIF}
2090
2091 CodeBuf:=nil;
2092 Filename:=MsgLine.GetFullFilename;
2093 if (CompareFilenames(ExtractFileName(Filename),'staticpackages.inc')=0)
2094 and ((ExtractFilePath(Filename)='')
2095 or (CompareFilenames(ExtractFilePath(Filename),AppendPathDelim(GetPrimaryConfigPath))=0))
2096 then begin
2097 // common case: when building the IDE a package unit is missing
2098 // staticpackages.inc(1,1) Fatal: Can't find unit sqldblaz used by Lazarus
2099 // change to lazarus.pp(1,1)
2100 Filename:=AppendPathDelim(EnvironmentOptions.GetParsedLazarusDirectory)+'ide'+PathDelim+'lazarus.pp';
2101 MsgLine.SetSourcePosition(Filename,1,1);
2102 MsgLine.Msg:=Format(lisCanTFindAValidPpu, [MissingUnitname]);
2103 end else if SysUtils.CompareText(ExtractFileNameOnly(Filename),UsedByUnit)<>0
2104 then begin
2105 // the message belongs to another unit
2106 NewFilename:='';
2107 if FilenameIsAbsolute(Filename) then
2108 begin
2109 // For example: /path/laz/main.pp(1,1) Fatal: Can't find unit lazreport used by lazarus
2110 // => search source 'lazarus' in directory
2111 NewFilename:=CodeToolBoss.DirectoryCachePool.FindUnitInDirectory(
2112 ExtractFilePath(Filename),UsedByUnit,true);
2113 end;
2114 if NewFilename='' then begin
2115 TheOwner:=nil;
2116 if Tool.Data is TIDEExternalToolData then begin
2117 TheOwner:=ExternalToolList.GetIDEObject(TIDEExternalToolData(Tool.Data));
2118 end else if Tool.Data=nil then begin
2119 {$IFDEF VerboseFPCMsgUnitNotFound}
2120 debugln(['TIDEFPCParser.ImproveMsgUnitNotFound Tool.Data=nil, ProcDir=',Tool.Process.CurrentDirectory]);
2121 {$ENDIF}
2122 end;
2123 NewFilename:=LazarusIDE.FindUnitFile(UsedByUnit,TheOwner);
2124 if NewFilename='' then begin
2125 {$IFDEF VerboseFPCMsgUnitNotFound}
2126 debugln(['TIDEFPCParser.ImproveMsgUnitNotFound unit not found: ',UsedByUnit]);
2127 {$ENDIF}
2128 end;
2129 end;
2130 if NewFilename<>'' then
2131 Filename:=NewFilename;
2132 end;
2133
2134 if FilenameIsAbsolute(Filename) or (mlfTestBuildFile in MsgLine.Flags) then begin
2135 CodeBuf:=CodeToolBoss.LoadFile(Filename,false,false);
2136 if CodeBuf=nil then begin
2137 {$IFDEF VerboseFPCMsgUnitNotFound}
2138 debugln(['TIDEFPCParser.ImproveMsgUnitNotFound unable to load unit: ',Filename]);
2139 {$ENDIF}
2140 end;
2141 end else begin
2142 {$IFDEF VerboseFPCMsgUnitNotFound}
2143 debugln(['TIDEFPCParser.ImproveMsgUnitNotFound unable to locate UsedByUnit: ',UsedByUnit,' Filename="',MsgLine.Filename,'" Attr[',FPCMsgAttrWorkerDirectory,']=',MsgLine.Attribute[FPCMsgAttrWorkerDirectory],' Tool.WorkerDirectory=',Tool.WorkerDirectory]);
2144 {$ENDIF}
2145 end;
2146
2147 // fix line and column
2148 Owners:=nil;
2149 PPUFiles:=TStringList.Create;
2150 try
2151 UsedByOwner:=nil;
2152 UsedByPkg:=nil;
2153 if CodeBuf<>nil then begin
2154 FixSourcePos(CodeBuf,MissingUnitname);
2155 Owners:=PackageEditingInterface.GetOwnersOfUnit(CodeBuf.Filename);
2156 if (Owners<>nil) and (Owners.Count>0) then begin
2157 UsedByOwner:=TObject(Owners[0]);
2158 if UsedByOwner is TIDEPackage then
2159 UsedByPkg:=TIDEPackage(UsedByOwner);
2160 end;
2161 end;
2162
2163 // if the ppu exists then improve the message
2164 if (CodeBuf<>nil) and FilenameIsAbsolute(CodeBuf.Filename) then begin
2165 {$IFDEF VerboseFPCMsgUnitNotFound}
2166 debugln(['TIDEFPCParser.ImproveMsgUnitNotFound Filename=',CodeBuf.Filename]);
2167 {$ENDIF}
2168 PPUFilename:=CodeToolBoss.DirectoryCachePool.FindCompiledUnitInCompletePath(
2169 ExtractFilePath(CodeBuf.Filename),MissingUnitname);
2170 if (PPUFilename<>'') then begin
2171 FPCUnitFilename:=CodeToolBoss.DirectoryCachePool.FindUnitInUnitSet(
2172 ExtractFilePath(CodeBuf.Filename),MissingUnitName);
2173 end else
2174 FPCUnitFilename:='';
2175 {$IFDEF VerboseFPCMsgUnitNotFound}
2176 debugln(['TIDEFPCParser.ImproveMsgUnitNotFound PPUFilename=',PPUFilename,' IsFileInIDESrcDir=',IsFileInIDESrcDir(CodeBuf.Filename)]);
2177 {$ENDIF}
2178 OnlyInstalled:=IsFileInIDESrcDir(CodeBuf.Filename);
2179 if OnlyInstalled then begin
2180 FindPPUInInstalledPkgs(MissingUnitname,PPUFiles);
2181 end else if UsedByOwner<>nil then
2182 FindPPUInModuleAndDeps(MissingUnitName,UsedByOwner,PPUFiles);
2183 {$IFDEF VerboseFPCMsgUnitNotFound}
2184 debugln(['TIDEFPCParser.ImproveMsgUnitNotFound PPUFiles in PPU path=',PPUFiles.Count]);
2185 {$ENDIF}
2186 FindPackage(MissingUnitname,OnlyInstalled,MissingPkg,MissingPkgName,MissingPkgFile);
2187 {$IFDEF VerboseFPCMsgUnitNotFound}
2188 debugln(['TIDEFPCParser.ImproveMsgUnitNotFound MissingUnitPkg=',MissingPkgName]);
2189 {$ENDIF}
2190 s:=Format(lisCannotFind, [MissingUnitname]);
2191 if UsedByUnit<>'' then
2192 s+=Format(lisUsedBy, [UsedByUnit]);
2193 if PPUFiles.Count>0 then begin
2194 // there is a ppu file in a package output directory, but the compiler
2195 // didn't like it => change message
2196 if PPUFilename='' then
2197 PPUFilename:=PPUFiles[0];
2198 s+=Format(lisIncompatiblePpu, [PPUFilename]);
2199 if PPUFiles.Count=1 then
2200 s+=Format(lisPackage3, [TIDEPackage(PPUFiles.Objects[0]).Name])
2201 else begin
2202 s+=lisMultiplePack;
2203 for i:=0 to PPUFiles.Count-1 do begin
2204 if i>0 then
2205 s+=', ';
2206 s+=TIDEPackage(PPUFiles.Objects[i]).Name;
2207 end;
2208 end;
2209 end else if PPUFilename<>'' then begin
2210 if CompareFilenames(PPUFilename,FPCUnitFilename)=0 then begin
2211 // there is ppu in the FPC units, but the compiler does not like it
2212 // => a) using a wrong compiler version (wrong fpc.cfg)
2213 // b) user units in fpc.cfg
2214 // c) fpc units not compiled with -Ur
2215 // d) wrong target platform
2216 s+=', ppu='+PPUFilename+', check your fpc.cfg';
2217 end else begin
2218 // there is a ppu file in the source path
2219 if (MissingPkg<>nil) and (MissingPkg.LazCompilerOptions.UnitOutputDirectory='')
2220 then
2221 s+='. '+lisPackageNeedsAnOutputDirectory
2222 else
2223 s+='. '+lisMakeSureAllPpuFilesOfAPackageAreInItsOutputDirecto;
2224 s+=' '+Format(lisPpuInWrongDirectory, [PPUFilename]);
2225 if MissingPkgName<>'' then
2226 s+=' '+Format(lisCleanUpPackage, [MissingPkgName]);
2227 s+='.';
2228 end;
2229 end
2230 else if (UsedByPkg<>nil) and (CompareTextCT(UsedByPkg.Name,MissingPkgName)=0)
2231 then begin
2232 // two units of a package cannot find each other
2233 s+=Format(lisCheckSearchPathPackageTryACleanRebuildCheckImpleme, [
2234 UsedByPkg.Name]);
2235 s+='.';
2236 end else if (MissingPkgName<>'')
2237 and (OnlyInstalled
2238 or ((UsedByOwner<>nil)
2239 and PackageEditingInterface.IsOwnerDependingOnPkg(UsedByOwner,MissingPkgName,DepOwner)))
2240 then begin
2241 // ppu file of an used package is missing
2242 if (MissingPkgFile<>nil) and (not MissingPkgFile.InUses) then
2243 s+=Format(lisEnableFlagUseUnitOfUnitInPackage, [MissingUnitName, MissingPkgName])
2244 else
2245 s+=Format(lisCheckIfPackageCreatesPpuCheckNothingDeletesThisFil, [
2246 MissingPkgName, MissingUnitName]);
2247 s+='.';
2248 end else begin
2249 if MissingPkgName<>'' then
2250 s+=Format(lisCheckIfPackageIsInTheDependencies, [MissingPkgName]);
2251 if UsedByOwner is TLazProject then
2252 s+=lisOfTheProjectInspector
2253 else if UsedByPkg<>nil then
2254 s+=Format(lisOfPackage, [UsedByPkg.Name]);
2255 s+='.';
2256 end;
2257 MsgLine.Msg:=s;
2258 {$IFDEF VerboseFPCMsgUnitNotFound}
2259 debugln(['TIDEFPCParser.ImproveMsgUnitNotFound Msg.Msg="',MsgLine.Msg,'"']);
2260 {$ENDIF}
2261 end;
2262 finally
2263 PPUFiles.Free;
2264 Owners.Free;
2265 end;
2266 end;
2267
2268 procedure TIDEFPCParser.ImproveMsgLinkerUndefinedReference(
2269 aPhase: TExtToolParserSyncPhase; MsgLine: TMessageLine);
2270
CheckForLinuxLDFileAndLineNumbernull2271 function CheckForLinuxLDFileAndLineNumber: boolean;
2272 { For example:
2273 /path/lib/x86_64-linux/blaunit.o: In function `FORMCREATE':
2274 /path//blaunit.pas:45: undefined reference to `BLAUNIT_BLABLA'
2275 }
2276 var
2277 p: PChar;
2278 Msg: String;
2279 aFilename: String;
2280 LineNumber: Integer;
2281 i: SizeInt;
2282 begin
2283 Result:=false;
2284 if aPhase<>etpspAfterReadLine then exit;
2285 if MsgLine.HasSourcePosition then exit;
2286 Msg:=MsgLine.Msg;
2287 p:=PChar(Msg);
2288 // check for "filename:decimals: message"
2289 // or unit1.o(.text+0x3a):unit1.pas:48: undefined reference to `DoesNotExist'
2290
2291 // read filename
2292 repeat
2293 if p^=#0 then exit;
2294 inc(p);
2295 until (p^=':') and (p[1] in ['0'..'9']);
2296 aFilename:=LeftStr(Msg,p-PChar(Msg));
2297 // check for something):filename
2298 i:=Pos('):',aFilename);
2299 if i>0 then
2300 Delete(aFilename,1,i+1);
2301 aFilename:=TrimFilename(aFilename);
2302
2303 // read line number
2304 inc(p);
2305 LineNumber:=0;
2306 while p^ in ['0'..'9'] do begin
2307 LineNumber:=LineNumber*10+ord(p^)-ord('0');
2308 if LineNumber>9999999 then exit;
2309 inc(p);
2310 end;
2311 if p^<>':' then exit;
2312 inc(p);
2313 while p^ in [' '] do inc(p);
2314
2315 Result:=true;
2316 MsgLine.Msg:=copy(Msg,p-PChar(Msg)+1,length(Msg));
2317 MsgLine.SetSourcePosition(aFilename,LineNumber,1);
2318 MsgLine.Urgency:=mluError;
2319 end;
2320
2321 function CheckForDarwinLDReferencedFrom: boolean;
2322 { For example:
2323 "_UNIT1_GIBTESNICHT", referenced from:
2324 }
2325 var
2326 MangledName: string;
2327 aComplete: boolean;
2328 aErrorMsg: string;
2329 NewCode: TCodeBuffer;
2330 NewX: integer;
2331 NewY: integer;
2332 NewTopLine: integer;
2333 begin
2334 Result:=false;
2335 if MsgLine.HasSourcePosition then exit;
2336 // check for ' "_FPC-Mangled-Identifier", referenced from:
2337 if not etFPCMsgParser.GetFPCMsgValue1(MsgLine.Msg,' "_$1", referenced from:',
2338 MangledName)
2339 then exit;
2340 Result:=true;
2341 case aPhase of
2342 etpspAfterReadLine:
2343 begin
2344 NeedSynchronize:=true;
2345 exit;
2346 end;
2347 etpspAfterSync: exit;
2348 end;
2349 // in main thread
2350 CodeToolBoss.FindFPCMangledIdentifier(MangledName,aComplete,aErrorMsg,
2351 nil,NewCode,NewX,NewY,NewTopLine);
2352 if NewCode=nil then exit;
2353 Result:=true;
2354 MsgLine.SetSourcePosition(NewCode.Filename,NewY,NewX);
2355 MsgLine.Urgency:=mluError;
2356 end;
2357
CheckForDarwinLDMangledInOnull2358 function CheckForDarwinLDMangledInO: boolean;
2359 { For example:
2360 _UNIT1_TFORM1_$__FORMCREATE$TOBJECT in unit1.o
2361 }
2362 var
2363 MangledName: string;
2364 aUnitName: string;
2365 aComplete: boolean;
2366 aErrorMsg: string;
2367 NewCode: TCodeBuffer;
2368 NewX: integer;
2369 NewY: integer;
2370 NewTopLine: integer;
2371 begin
2372 Result:=false;
2373 if MsgLine.HasSourcePosition then exit;
2374 if not etFPCMsgParser.GetFPCMsgValues2(MsgLine.Msg,' _$1 in $2.o',
2375 MangledName,aUnitName)
2376 then exit;
2377 Result:=true;
2378 case aPhase of
2379 etpspAfterReadLine:
2380 begin
2381 NeedSynchronize:=true;
2382 exit;
2383 end;
2384 etpspAfterSync: exit;
2385 end;
2386 // in main thread
2387 CodeToolBoss.FindFPCMangledIdentifier(MangledName,aComplete,aErrorMsg,
2388 nil,NewCode,NewX,NewY,NewTopLine);
2389 if NewCode=nil then exit;
2390 Result:=true;
2391 MsgLine.SetSourcePosition(NewCode.Filename,NewY,NewX);
2392 MsgLine.Urgency:=mluError;
2393 end;
2394
2395 begin
2396 if MsgLine.SubTool<>SubToolFPCLinker then exit;
2397
2398 if CheckForLinuxLDFileAndLineNumber then exit;
2399 if CheckForDarwinLDReferencedFrom then exit;
2400 if CheckForDarwinLDMangledInO then exit;
2401 end;
2402
2403 procedure TIDEFPCParser.ImproveMsgIdentifierPosition(
2404 aPhase: TExtToolParserSyncPhase; MsgLine: TMessageLine; SourceOK: boolean);
2405 { FPC sometimes reports the token after the identifier
2406 => fix the position
2407 Examples:
2408 " i :="
2409 unit1.pas(42,5) Error: (5000) Identifier not found "i"
2410
2411 "procedure TMyClass.DoIt ;"
2412 test.pas(7,26) Error: (3047) method identifier expected
2413 }
2414 const
2415 AttrPosChecked = 'PosChecked';
2416 var
2417 LineRange: TLineRange;
2418 Line, Col: Integer;
2419 p, AtomEnd: integer;
2420 Src: String;
2421 Identifier: String;
2422 NewP: Integer;
2423 begin
2424 Col:=MsgLine.Column;
2425 Line:=MsgLine.Line;
2426 if (Col<1) or (Line<1) then
2427 exit;
2428 if (Line=1) and (Col=1) then exit;
2429 if MsgLine.SubTool<>SubToolFPC then exit;
2430 if MsgLine.MsgID=0 then begin
2431 // maybe not compiled with -vq: search patterns of common messages
2432 if (not IsMsgID(MsgLine,FPCMsgIDIdentifierNotFound,fMsgItemIdentifierNotFound))
2433 and (not IsMsgID(MsgLine,FPCMsgIDMethodIdentifierExpected,fMsgItemMethodIdentifierExpected))
2434 then
2435 exit;
2436 end;
2437 if MsgLine.MsgID=FPCMsgIDMethodIdentifierExpected then
2438 Identifier:=''
2439 else begin
2440 Identifier:=GetFPCMsgValue1(MsgLine);
2441 if not IsValidIdent(Identifier) then exit;
2442 end;
2443
2444 if MsgLine.Attribute[AttrPosChecked]<>'' then exit;
2445 if NeedSource(aPhase,SourceOK) then
2446 exit;
2447 MsgLine.Attribute[AttrPosChecked]:=ClassName;
2448
2449 //DebuglnThreadLog(['Old Line=',Line,' ',MsgLine.Column]);
2450 if Line>=fCurSource.LineCount then exit;
2451 fCurSource.GetLineRange(Line-1,LineRange);
2452 //DebuglnThreadLog(['Old Range=',LineRange.StartPos,'-',LineRange.EndPos,' Str="',copy(fCurSource.Source,LineRange.StartPos,LineRange.EndPos-LineRange.StartPos),'"']);
2453 Col:=Min(Col,LineRange.EndPos-LineRange.StartPos+1);
2454 p:=LineRange.StartPos+Col-1;
2455 Src:=fCurSource.Source;
2456 if Identifier<>'' then begin
2457 // message is about a specific identifier
2458 if CompareIdentifiers(PChar(Identifier),@Src[p])=0 then begin
2459 // already pointing at the start of the identifier
2460 exit;
2461 end;
2462 end else begin
2463 // message is about any one identifier
2464 if IsIdentStartChar[Src[p]] then begin
2465 // already pointing at an identifier
2466 exit;
2467 end;
2468 end;
2469 // go to prior token
2470 //DebuglnThreadLog(['New Line=',Line,' Col=',Col,' p=',p]);
2471 NewP:=p;
2472 ReadPriorPascalAtom(Src,NewP,AtomEnd,false);
2473 if NewP<1 then exit;
2474 if Identifier<>'' then begin
2475 // message is about a specific identifier
2476 if CompareIdentifiers(PChar(Identifier),@Src[NewP])<>0 then begin
2477 // the prior token is not the identifier neither
2478 // => don't know
2479 exit;
2480 end;
2481 end else begin
2482 // message is about any one identifier
2483 if not IsIdentStartChar[Src[NewP]] then begin
2484 // the prior token is not an identifier neither
2485 // => don't know
2486 exit;
2487 end;
2488 end;
2489 fCurSource.AbsoluteToLineCol(NewP,Line,Col);
2490 //DebuglnThreadLog(['New Line=',Line,' Col=',Col,' p=',NewP]);
2491 if (Line<1) or (Col<1) then exit;
2492 if MsgLine.Urgency>=mluError then begin
2493 // position errors at start of wrong identifier, nicer for identifier completion
2494 MsgLine.SetSourcePosition(MsgLine.Filename,Line,Col);
2495 MsgLine.Flags:=MsgLine.Flags-[mlfLeftToken];
2496 end else begin
2497 // position hints at end of identifier, nicer for {%H-}
2498 MsgLine.SetSourcePosition(MsgLine.Filename,Line,Col+length(Identifier));
2499 MsgLine.Flags:=MsgLine.Flags+[mlfLeftToken];
2500 end;
2501 end;
2502
TIDEFPCParser.FindSrcViaPPUnull2503 function TIDEFPCParser.FindSrcViaPPU(aPhase: TExtToolParserSyncPhase;
2504 MsgLine: TMessageLine; const PPUFilename: string): boolean;
2505 { in main thread
2506 for example:
2507 /usr/lib/fpc/3.1.1/units/x86_64-linux/rtl/sysutils.ppu:filutil.inc(481,10) Error: (5088) ...
2508 PPUFilename=/usr/lib/fpc/3.1.1/units/x86_64-linux/rtl/sysutils.ppu
2509 Filename=filutil.inc
2510 }
2511 var
2512 i: Integer;
2513 PrevMsgLine: TMessageLine;
2514 aFilename: String;
2515 MsgWorkerDir: String;
2516 UnitSrcFilename: String;
2517 IncPath: String;
2518 Dir: String;
2519 ShortFilename: String;
2520 IncFilename: String;
2521 AnUnitName: String;
2522 InFilename: String;
2523 begin
2524 case aPhase of
2525 etpspAfterReadLine: exit(false);
2526 etpspSynchronized: ;
2527 etpspAfterSync: exit(true);
2528 end;
2529 Result:=true;
2530
2531 // in main thread
2532 i:=MsgLine.Index;
2533 aFilename:=MsgLine.Filename;
2534 //debugln(['TIDEFPCParser.FindSrcViaPPU i=',i,' PPUFilename="',PPUFilename,'" Filename="',aFilename,'"']);
2535 if (i>0) then begin
2536 PrevMsgLine:=Tool.WorkerMessages[i-1];
2537 if (PrevMsgLine.SubTool=DefaultSubTool)
2538 and (CompareFilenames(PPUFilename,PrevMsgLine.Attribute['PPU'])=0)
2539 and FilenameIsAbsolute(PrevMsgLine.Filename)
2540 and (CompareFilenames(ExtractFilename(PrevMsgLine.Filename),ExtractFilename(aFilename))=0)
2541 then begin
2542 // same file as previous message => use it
2543 MsgLine.Filename:=PrevMsgLine.Filename;
2544 exit;
2545 end;
2546 end;
2547
2548 if not FilenameIsAbsolute(PPUFilename) then
2549 begin
2550 exit;
2551 end;
2552
2553 ShortFilename:=ExtractFilename(aFilename);
2554 MsgWorkerDir:=MsgLine.Attribute[FPCMsgAttrWorkerDirectory];
2555 AnUnitName:=ExtractFilenameOnly(PPUFilename);
2556 InFilename:='';
2557 UnitSrcFilename:=CodeToolBoss.DirectoryCachePool.FindUnitSourceInCompletePath(
2558 MsgWorkerDir,AnUnitName,InFilename);
2559 //debugln(['TIDEFPCParser.FindSrcViaPPU MsgWorkerDir="',MsgWorkerDir,'" UnitSrcFilename="',UnitSrcFilename,'"']);
2560 if UnitSrcFilename<>'' then begin
2561 if CompareFilenames(ExtractFilename(UnitSrcFilename),ShortFilename)=0 then
2562 begin
2563 MsgLine.Filename:=UnitSrcFilename;
2564 exit;
2565 end;
2566 Dir:=ChompPathDelim(TrimFilename(ExtractFilePath(UnitSrcFilename)));
2567 IncPath:=CodeToolBoss.GetIncludePathForDirectory(Dir);
2568 IncFilename:=SearchFileInPath(ShortFilename,Dir,IncPath,';',ctsfcDefault);
2569 //debugln(['TIDEFPCParser.FindSrcViaPPU Dir="',Dir,'" IncPath="',IncPath,'" ShortFilename="',ShortFilename,'" IncFilename="',IncFilename,'"']);
2570 if IncFilename<>'' then begin
2571 MsgLine.Filename:=IncFilename;
2572 exit;
2573 end;
2574 end;
2575 end;
2576
2577 procedure TIDEFPCParser.Translate(p: PChar; MsgItem, TranslatedItem: TFPCMsgItem;
2578 out TranslatedMsg: String; out MsgType: TMessageLineUrgency);
2579 begin
2580 TranslatedMsg:='';
2581 MsgType:=mluNone;
2582 if TranslatedItem<>nil then
2583 MsgType:=FPCMsgToMsgUrgency(TranslatedItem);
2584 if (MsgType=mluNone) and (MsgItem<>nil) then
2585 MsgType:=FPCMsgToMsgUrgency(MsgItem);
2586 if TranslatedItem<>nil then begin
2587 if System.Pos('$',TranslatedItem.Pattern)<1 then begin
2588 TranslatedMsg:=TranslatedItem.Pattern;
2589 UTF8FixBroken(TranslatedMsg);
2590 end
2591 else if MsgItem<>nil then
2592 TranslatedMsg:=TranslateFPCMsg(p,MsgItem.Pattern,TranslatedItem.Pattern);
2593 //debugln(['TFPCParser.Translate Translation="',TranslatedMsg,'"']);
2594 end;
2595 end;
2596
ReverseInstantFPCCacheDirnull2597 function TIDEFPCParser.ReverseInstantFPCCacheDir(var aFilename: string;
2598 aSynchronized: boolean): boolean;
2599 var
2600 Reversed: String;
2601 begin
2602 Result:=false;
2603 if (InstantFPCCache='') then exit;
2604 if (CompareFilenames(ExtractFilePath(aFilename),InstantFPCCache)=0) then begin
2605 Reversed:=AppendPathDelim(Tool.WorkerDirectory)+ExtractFilename(aFilename);
2606 if FileExists(Reversed,aSynchronized) then begin
2607 aFilename:=Reversed;
2608 Result:=true;
2609 end;
2610 end;
2611 end;
2612
ReverseTestBuildDirnull2613 function TIDEFPCParser.ReverseTestBuildDir(MsgLine: TMessageLine;
2614 var aFilename: string): boolean;
2615 var
2616 Reversed: String;
2617 l: Integer;
2618 begin
2619 Result:=false;
2620 if not Tool.CurrentDirectoryIsTestDir then exit;
2621 l:=length(TestBuildDir); // Note: TestBuildDir includes trailing PathDelim
2622 if (length(aFilename)>l) and (aFilename[l]=PathDelim)
2623 and (CompareFilenames(LeftStr(aFilename,l),TestBuildDir)=0) then begin
2624 Reversed:=copy(aFilename,l+1,length(aFilename));
2625 if VirtualProjectFiles.Contains(Reversed) then begin
2626 MsgLine.Flags:=MsgLine.Flags+[mlfTestBuildFile];
2627 MsgLine.Attribute[MsgAttrDiskFilename]:=aFilename;
2628 aFilename:=Reversed;
2629 Result:=true;
2630 end
2631 end;
2632 end;
2633
2634 constructor TIDEFPCParser.Create(AOwner: TComponent);
2635 begin
2636 inherited Create(AOwner);
2637 fMissingFPCMsgItem:=TFPCMsgItem(Pointer(1));
2638 fLineToMsgID:=TPatternToMsgIDs.Create;
2639 fFileExists:=TFilenameToPointerTree.Create(false);
2640 FFilesToIgnoreUnitNotUsed:=TStringList.Create;
2641 HideHintsSenderNotUsed:=true;
2642 HideHintsUnitNotUsedInMainSource:=true;
2643 PC_FullVersion:=GetCompiledFPCVersion;
2644 end;
2645
TIDEFPCParser.FileExistsnull2646 function TIDEFPCParser.FileExists(const Filename: string; aSynchronized: boolean
2647 ): boolean;
2648 var
2649 p: Pointer;
2650 begin
2651 // check internal cache
2652 p:=fFileExists[Filename];
2653 if p=Pointer(Self) then
2654 Result:=true
2655 else if p=Pointer(fFileExists) then
2656 Result:=false
2657 else begin
2658 // check disk
2659 if aSynchronized then
2660 Result:=FileExistsCached(Filename)
2661 else
2662 Result:=FileExistsUTF8(Filename);
2663 // save result
2664 if Result then
2665 fFileExists[Filename]:=Pointer(Self)
2666 else
2667 fFileExists[Filename]:=Pointer(fFileExists);
2668 end;
2669 end;
2670
2671 procedure TIDEFPCParser.FetchIncludePath(aPhase: TExtToolParserSyncPhase;
2672 MsgWorkerDir: String);
2673 begin
2674 if MsgWorkerDir='' then
2675 MsgWorkerDir:=Tool.WorkerDirectory;
2676 if fIncludePathValidForWorkerDir<>MsgWorkerDir then begin
2677 // fetch include path from IDE
2678 case aPhase of
2679 etpspAfterReadLine:
2680 NeedSynchronize:=true;
2681 etpspSynchronized:
2682 begin
2683 fIncludePathValidForWorkerDir:=MsgWorkerDir;
2684 fIncludePath:=CodeToolBoss.GetIncludePathForDirectory(
2685 ChompPathDelim(MsgWorkerDir));
2686 {$IFDEF VerboseFPCMsgUnitNotFound}
2687 debugln(['TIDEFPCParser.FetchIncludePath ',fIncludePath]);
2688 {$ENDIF}
2689 NeedAfterSync:=true;
2690 end;
2691 end;
2692 end;
2693 end;
2694
2695 procedure TIDEFPCParser.FetchUnitPath(aPhase: TExtToolParserSyncPhase;
2696 MsgWorkerDir: String);
2697 begin
2698 if MsgWorkerDir='' then
2699 MsgWorkerDir:=Tool.WorkerDirectory;
2700 if fUnitPathValidForWorkerDir<>MsgWorkerDir then begin
2701 // fetch unit path from IDE
2702 case aPhase of
2703 etpspAfterReadLine:
2704 NeedSynchronize:=true;
2705 etpspSynchronized:
2706 begin
2707 fUnitPathValidForWorkerDir:=MsgWorkerDir;
2708 fUnitPath:=CodeToolBoss.GetUnitPathForDirectory(
2709 ChompPathDelim(MsgWorkerDir));
2710 NeedAfterSync:=true;
2711 end;
2712 end;
2713 end;
2714 end;
2715
TIDEFPCParser.CheckForMsgIdnull2716 function TIDEFPCParser.CheckForMsgId(p: PChar): boolean;
2717 var
2718 MsgItem: TFPCMsgItem;
2719 TranslatedItem: TFPCMsgItem;
2720 MsgLine: TMessageLine;
2721 TranslatedMsg: String;
2722 MsgUrgency: TMessageLineUrgency;
2723 Msg: string;
2724 begin
2725 Result:=false;
2726 if (fMsgID<1) or (MsgFile=nil) then exit;
2727 MsgItem:=MsgFile.GetMsg(fMsgID);
2728 if MsgItem=nil then exit;
2729 Result:=true;
2730 TranslatedItem:=nil;
2731 if (TranslationFile<>nil) then
2732 TranslatedItem:=TranslationFile.GetMsg(fMsgID);
2733 Translate(p,MsgItem,TranslatedItem,TranslatedMsg,MsgUrgency);
2734 Msg:=p;
2735 case fMsgID of
2736 FPCMsgIDThereWereErrorsCompiling: // There were $1 errors compiling module, stopping
2737 MsgUrgency:=mluVerbose;
2738 FPCMsgIDLinesCompiled: // n lines compiled, m sec
2739 if ShowLinesCompiled then MsgUrgency:=mluImportant;
2740 end;
2741 MsgLine:=CreateMsgLine;
2742 MsgLine.SubTool:=DefaultSubTool;
2743 MsgLine.Urgency:=MsgUrgency;
2744 MsgLine.Msg:=Msg;
2745 MsgLine.TranslatedMsg:=TranslatedMsg;
2746 AddMsgLine(MsgLine);
2747 end;
2748
TIDEFPCParser.CheckFollowUpMessagenull2749 function TIDEFPCParser.CheckFollowUpMessage(p: PChar): boolean;
2750 var
2751 i: Integer;
2752 LastMsgLine, MsgLine: TMessageLine;
2753 begin
2754 Result:=false;
2755 if (p^=' ') then begin
2756 i:=Tool.WorkerMessages.Count-1;
2757 if i<0 then exit;
2758 LastMsgLine:=Tool.WorkerMessages[i];
2759 if LastMsgLine.SubTool=SubToolFPCLinker then begin
2760 // a follow up line of the linker output
2761 Result:=true;
2762 MsgLine:=CreateMsgLine;
2763 MsgLine.MsgID:=0;
2764 MsgLine.SubTool:=SubToolFPCLinker;
2765 MsgLine.Urgency:=LastMsgLine.Urgency;
2766 MsgLine.Msg:='linker: '+p;
2767 inherited AddMsgLine(MsgLine);
2768 end;
2769 end;
2770 end;
2771
CheckForFileLineColMessagenull2772 function TIDEFPCParser.CheckForFileLineColMessage(p: PChar): boolean;
2773 { filename(line,column) Hint: message
2774 filename(line,column) Hint: (msgid) message
2775 filename(line) Hint: (msgid) message
2776 B:\file(3)name(line,column) Hint: (msgid) message
2777 /usr/lib/fpc/3.1.1/units/x86_64-linux/rtl/sysutils.ppu:filutil.inc(481,10) Error: (5088) ...
2778 }
2779 var
2780 FileStartPos: PChar;
2781 FileEndPos: PChar;
2782 LineStartPos: PChar;
2783 ColStartPos: PChar;
2784 MsgType: TMessageLineUrgency;
2785 MsgLine: TMessageLine;
2786 p2: PChar;
2787 i: Integer;
2788 TranslatedItem: TFPCMsgItem;
2789 MsgItem: TFPCMsgItem;
2790 TranslatedMsg: String;
2791 aFilename: String;
2792 Column: Integer;
2793 PPUFileStartPos: PChar;
2794 PPUFileEndPos: PChar;
2795 begin
2796 Result:=false;
2797 FileStartPos:=p;
2798 FileEndPos:=nil;
2799 PPUFileStartPos:=nil;
2800 PPUFileEndPos:=nil;
2801 // search colon and last ( in front of colon
2802 while true do begin
2803 case p^ of
2804 #0: exit;
2805 '(': FileEndPos:=p;
2806 ':':
2807 if (p-FileStartPos>5) and (p[-4]='.') and (p[-3] in ['p','P'])
2808 and (p[-2] in ['p','P']) and (p[-1] in ['u','U']) then begin
2809 // e.g. /usr/lib/fpc/3.1.1/units/x86_64-linux/rtl/sysutils.ppu:filutil.inc(481,10) Error: (5088) ...
2810 if PPUFileStartPos<>nil then exit;
2811 PPUFileStartPos:=FileStartPos;
2812 PPUFileEndPos:=p;
2813 FileStartPos:=p+1;
2814 end
2815 else if (DriveSeparator='') or (p-FileStartPos>1) then
2816 break;
2817 end;
2818 inc(p);
2819 end;
2820 if (FileEndPos=nil) or (FileEndPos-FileStartPos=0) or (FileEndPos[-1]=' ') then exit;
2821 p:=FileEndPos;
2822 inc(p); // skip bracket
2823 LineStartPos:=p;
2824 if not ReadDecimal(p) then exit;
2825 if p^=',' then begin
2826 if not ReadChar(p,',') then exit;
2827 ColStartPos:=p;
2828 if not ReadDecimal(p) then exit;
2829 end else
2830 ColStartPos:=nil;
2831 if not ReadChar(p,')') then exit;
2832 if not ReadChar(p,' ') then exit;
2833 MsgType:=mluNote;
2834 if ReadString(p,'Info:') then begin
2835 MsgType:=mluVerbose;
2836 end else if ReadString(p,'Hint:') then begin
2837 MsgType:=mluHint;
2838 end else if ReadString(p,'Note:') then begin
2839 MsgType:=mluNote;
2840 end else if ReadString(p,'Warn:') or ReadString(p,'Warning: ') then begin
2841 MsgType:=mluWarning;
2842 end else if ReadString(p,'Error:') then begin
2843 MsgType:=mluError;
2844 end else if ReadString(p,'Fatal:') then begin
2845 MsgType:=mluError;
2846 end else begin
2847 p2:=p;
2848 while not (p2^ in [':',#0,' ']) do inc(p2);
2849 if p2^=':' then begin
2850 // unknown type (maybe a translation?)
2851 p:=p2+1;
2852 end;
2853 end;
2854 while p^ in [' ',#9] do inc(p);
2855 Result:=true;
2856 TranslatedMsg:='';
2857 if (p^='(') and (p[1] in ['0'..'9']) then begin
2858 // (msgid)
2859 p2:=p;
2860 inc(p2);
2861 i:=0;
2862 while (p2^ in ['0'..'9']) and (i<1000000) do begin
2863 i:=i*10+ord(p2^)-ord('0');
2864 inc(p2);
2865 end;
2866 if p2^=')' then begin
2867 fMsgID:=i;
2868 p:=p2+1;
2869 while p^ in [' ',#9] do inc(p);
2870 //debugln(['TFPCParser.CheckForFileLineColMessage ID=',fMsgID,' Msg=',FileStartPos]);
2871 if (fMsgID>0) then begin
2872 TranslatedItem:=nil;
2873 MsgItem:=nil;
2874 if (TranslationFile<>nil) then
2875 TranslatedItem:=TranslationFile.GetMsg(fMsgID);
2876 if (MsgFile<>nil) then
2877 MsgItem:=MsgFile.GetMsg(fMsgID);
2878 if (TranslatedItem=nil) and (MsgItem=nil) then begin
2879 if ConsoleVerbosity>=1 then
2880 debugln(['TFPCParser.CheckForFileLineColMessage msgid not found: ',fMsgID]);
2881 end else begin
2882 Translate(p,MsgItem,TranslatedItem,TranslatedMsg,MsgType);
2883 if MsgType=mluNone then begin
2884 if ConsoleVerbosity>=1 then
2885 debugln(['TFPCParser.CheckForFileLineColMessage msgid has no type: ',fMsgID]);
2886 end;
2887 end;
2888 end;
2889 end;
2890 end;
2891 if ColStartPos<>nil then
2892 Column:=Str2Integer(ColStartPos,0)
2893 else
2894 Column:=0;
2895
2896 MsgLine:=CreateMsgLine;
2897 MsgLine.SubTool:=DefaultSubTool;
2898 MsgLine.Urgency:=MsgType;
2899 aFilename:=GetString(FileStartPos,FileEndPos-FileStartPos);
2900 if PPUFileStartPos<>nil then
2901 MsgLine.Attribute['PPU']:=GetString(PPUFileStartPos,PPUFileEndPos-PPUFileStartPos);
2902 MsgLine.Filename:=LongenFilename(MsgLine,aFilename);
2903 MsgLine.Line:=Str2Integer(LineStartPos,0);
2904 MsgLine.Column:=Column;
2905 MsgLine.Msg:=p;
2906 MsgLine.TranslatedMsg:=TranslatedMsg;
2907 //debugln(['TFPCParser.CheckForFileLineColMessage ',dbgs(MsgLine.Urgency)]);
2908
2909 AddMsgLine(MsgLine);
2910 end;
2911
CheckForLoadFromUnitnull2912 function TIDEFPCParser.CheckForLoadFromUnit(p: PChar): Boolean;
2913 var
2914 OldP: PChar;
2915 MsgLine: TMessageLine;
2916 begin
2917 Result:=fMsgID=10027;
2918 if (not Result) and (fMsgID>0) then exit;
2919 OldP:=p;
2920 if not Result then begin
2921 if not ReadString(p,'Load from ') then exit;
2922 while not (p^ in ['(',#0]) do inc(p);
2923 if p^<>'(' then exit;
2924 while not (p^ in [')',#0]) do inc(p);
2925 if p^<>')' then exit;
2926 if not ReadString(p,') unit ') then exit;
2927 end;
2928 MsgLine:=CreateMsgLine;
2929 MsgLine.SubTool:=DefaultSubTool;
2930 MsgLine.Urgency:=mluProgress;
2931 MsgLine.Msg:=OldP;
2932 AddMsgLine(MsgLine);
2933 Result:=true;
2934 end;
2935
2936 procedure TIDEFPCParser.ReadLine(Line: string; OutputIndex: integer;
2937 IsStdErr: boolean; var Handled: boolean);
2938 { returns true, if it is a compiler message
2939 Examples for freepascal compiler messages:
2940 Compiling <filename>
2941 Assembling <filename>
2942 Fatal: <some text>
2943 Fatal: (message id) <some text>
2944 (message id) <some text>
2945 <filename>(123,45) <ErrorType>: <some text>
2946 <filename>(123) <ErrorType>: <some text>
2947 <filename>(456) <ErrorType>: <some text> in line (123)
2948 [0.000] (3101) Macro defined: CPUAMD64
2949 <filename>(12,34) <ErrorType>: (5024) <some text>
2950 }
2951 var
2952 p: PChar;
2953 begin
2954 if Line='' then exit;
2955 Line:=ToUTF8(Line);
2956 p:=PChar(Line);
2957 fOutputIndex:=OutputIndex;
2958 fMsgID:=0;
2959 fMsgIsStdErr:=IsStdErr;
2960
2961 // skip time [0.000]
2962 if (p^='[') and (p[1] in ['0'..'9']) then begin
2963 inc(p,2);
2964 while p^ in ['0'..'9','.'] do inc(p);
2965 if p^<>']' then exit; // not a fpc message
2966 inc(p);
2967 while p^ in [' '] do inc(p);
2968 end;
2969
2970 // read message ID (000)
2971 if (p^='(') and (p[1] in ['0'..'9']) then begin
2972 inc(p);
2973 while p^ in ['0'..'9','.'] do begin
2974 if fMsgID>1000000 then exit; // not a fpc message
2975 fMsgID:=fMsgID*10+ord(p^)-ord('0');
2976 inc(p);
2977 end;
2978 if p^<>')' then exit; // not a fpc message
2979 inc(p);
2980 while p^=' ' do inc(p);
2981 end;
2982
2983 if p^ in [#0..#31,' '] then begin
2984 CheckFollowUpMessage(p);
2985 exit; // not a fpc message
2986 end;
2987
2988 Handled:=true;
2989
2990 //debugln(['TIDEFPCParser.ReadLine ',IsStdErr,' ',Line]);
2991
2992 // check for (msgid) message
2993 if CheckForMsgId(p) then exit;
2994 // check for 'filename(line,column) Error: message'
2995 if CheckForFileLineColMessage(p) then exit;
2996 // check for 'Compiling <filename>'
2997 if CheckForCompilingState(p) then exit;
2998 // check for 'Assembling <filename>'
2999 if CheckForAssemblingState(p) then exit;
3000 // check for 'Fatal: ', 'Panic: ', 'Error: ', ...
3001 if CheckForGeneralMessage(p) then exit;
3002 // check for '<line> <kb>/<kb>'...
3003 if CheckForLineProgress(p) then exit;
3004 // check for '<int> Lines compiled, <int>.<int> sec'
3005 if CheckForLinesCompiled(p) then exit;
3006 // check for infos (logo, Linking <Progname>)
3007 if CheckForInfos(p) then exit;
3008 // check for -vx output
3009 if CheckForExecutableInfo(p) then exit;
3010 // check for Load from unit
3011 if CheckForLoadFromUnit(p) then exit;
3012 // check for windres errors
3013 if CheckForWindresErrors(p) then exit;
3014 // check for linker errors
3015 if CheckForLinkerErrors(p) then exit;
3016 // check for assembler errors
3017 if CheckForAssemblerErrors(p) then exit;
3018
3019 // last: check for unknown std error
3020 if CheckForUnspecificStdErr(p) then exit;
3021
3022 {$IFDEF VerboseFPCParser}
3023 debugln('TFPCParser.ReadLine UNKNOWN: ',Line);
3024 {$ENDIF}
3025 Handled:=false;
3026 end;
3027
3028 procedure TIDEFPCParser.AddMsgLine(MsgLine: TMessageLine);
3029 begin
3030 if IsMsgID(MsgLine,FPCMsgIDErrorWhileCompilingResources,
3031 fMsgItemErrorWhileCompilingResources)
3032 then begin
3033 // Error while compiling resources
3034 AddResourceMessages;
3035 MsgLine.Msg:=MsgLine.Msg+' -> '+'Compile with -vd for more details. Check for duplicates.';
3036 MsgLine.TranslatedMsg:=MsgLine.TranslatedMsg+' -> '+lisCompileWithVdForMoreDetailsCheckForDuplicates;
3037 end
3038 else if IsMsgID(MsgLine,FPCMsgIDErrorWhileLinking,fMsgItemErrorWhileLinking) then
3039 AddLinkingMessages
3040 else if IsMsgID(MsgLine,FPCMsgIDChecksumChanged,fMsgItemChecksumChanged) then
3041 MsgLine.Urgency:=mluWarning
3042 else if IsMsgID(MsgLine,FPCMsgIDThereWereErrorsCompiling,
3043 fMsgItemThereWereErrorsCompiling)
3044 then
3045 MsgLine.Urgency:=mluVerbose;
3046 inherited AddMsgLine(MsgLine);
3047 end;
3048
TIDEFPCParser.LongenFilenamenull3049 function TIDEFPCParser.LongenFilename(MsgLine: TMessageLine; aFilename: string
3050 ): string;
3051 var
3052 ShortFilename: String;
3053 i: Integer;
3054 LastMsgLine: TMessageLine;
3055 LastFilename: String;
3056 begin
3057 Result:=TrimFilename(aFilename);
3058 if FilenameIsAbsolute(Result) then begin
3059 if ReverseInstantFPCCacheDir(Result,false) then exit;
3060 if ReverseTestBuildDir(MsgLine,Result) then exit;
3061 exit;
3062 end;
3063 if MsgLine.Attribute['PPU']<>'' then begin
3064 MsgLine.Attribute[FPCMsgAttrWorkerDirectory]:=Tool.WorkerDirectory;
3065 exit;
3066 end;
3067
3068 ShortFilename:=Result;
3069 // check last message line
3070 LastMsgLine:=Tool.WorkerMessages.GetLastLine;
3071 if (LastMsgLine<>nil) then begin
3072 if mlfTestBuildFile in LastMsgLine.Flags then
3073 LastFilename:=LastMsgLine.Attribute[MsgAttrDiskFilename]
3074 else
3075 LastFilename:=LastMsgLine.Filename;
3076 if FilenameIsAbsolute(LastFilename) then begin
3077 if (length(LastFilename)>length(ShortFilename))
3078 and (LastFilename[length(LastFilename)-length(ShortFilename)] in AllowDirectorySeparators)
3079 and (CompareFilenames(RightStr(LastFilename,length(ShortFilename)),ShortFilename)=0)
3080 then begin
3081 if mlfTestBuildFile in LastMsgLine.Flags then begin
3082 MsgLine.Attribute[MsgAttrDiskFilename]:=LastFilename;
3083 MsgLine.Flags:=MsgLine.Flags+[mlfTestBuildFile];
3084 Result:=LastMsgLine.Filename;
3085 end else begin
3086 Result:=LastFilename;
3087 ReverseTestBuildDir(MsgLine,Result);
3088 end;
3089 exit;
3090 end;
3091 end;
3092 end;
3093 // search file in the last compiling directories
3094 if DirectoryStack<>nil then begin
3095 for i:=DirectoryStack.Count-1 downto 0 do begin
3096 Result:=AppendPathDelim(DirectoryStack[i])+ShortFilename;
3097 if FileExists(Result,false) then begin
3098 ReverseTestBuildDir(MsgLine,Result);
3099 exit;
3100 end;
3101 end;
3102 end;
3103 // search file in worker directory
3104 if Tool.WorkerDirectory<>'' then begin
3105 Result:=AppendPathDelim(Tool.WorkerDirectory)+ShortFilename;
3106 if FileExists(Result,false) then begin
3107 ReverseTestBuildDir(MsgLine,Result);
3108 exit;
3109 end;
3110 end;
3111
3112 // file not found
3113 Result:=ShortFilename;
3114
3115 // save Tool.WorkerDirectory for ImproveMessage
3116 MsgLine.Attribute[FPCMsgAttrWorkerDirectory]:=Tool.WorkerDirectory;
3117 end;
3118
GetDefaultPCFullVersionnull3119 function TIDEFPCParser.GetDefaultPCFullVersion: LongWord;
3120 var
3121 Kind: TPascalCompiler;
3122 begin
3123 // get compiler version
3124 Result:=LongWord(CodeToolBoss.GetPCVersionForDirectory(Tool.WorkerDirectory,Kind));
3125 if Kind=pcFPC then ;
3126 end;
3127
ToUTF8null3128 function TIDEFPCParser.ToUTF8(const Line: string): string;
3129 begin
3130 if PC_FullVersion>=20701 then
3131 Result:=ConsoleToUTF8(Line)
3132 else begin
3133 {$IFDEF MSWINDOWS}
3134 Result:=WinCPToUTF8(Line);
3135 {$ELSE}
3136 Result:=SysToUTF8(Line);
3137 {$ENDIF}
3138 end;
3139 end;
3140
3141 procedure TIDEFPCParser.ImproveMessages(aPhase: TExtToolParserSyncPhase);
3142 var
3143 i: Integer;
3144 MsgLine: TMessageLine;
3145 aFilename: String;
3146 Y: Integer;
3147 X: Integer;
3148 Code: TCodeBuffer;
3149 SourceOK: Boolean;
3150 MsgWorkerDir: String;
3151 PrevMsgLine: TMessageLine;
3152 CmdLineParams: String;
3153 SrcFilename: String;
3154 PPUFilename: String;
3155 begin
3156 //debugln(['TIDEFPCParser.ImproveMessages START ',aSynchronized,' Last=',fLastWorkerImprovedMessage[aSynchronized],' Now=',Tool.WorkerMessages.Count]);
3157 for i:=fLastWorkerImprovedMessage[aPhase]+1 to Tool.WorkerMessages.Count-1 do
3158 begin
3159 MsgLine:=Tool.WorkerMessages[i];
3160 Y:=MsgLine.Line;
3161 X:=MsgLine.Column;
3162 if (Y>0) and (X>0)
3163 and (MsgLine.SubTool=DefaultSubTool) and (MsgLine.Filename<>'')
3164 then begin
3165 if mlfTestBuildFile in MsgLine.Flags then
3166 aFilename:=MsgLine.Attribute[MsgAttrDiskFilename]
3167 else
3168 aFilename:=MsgLine.Filename;
3169 PPUFilename:='';
3170 if (not FilenameIsAbsolute(aFilename)) then begin
3171 PPUFilename:=MsgLine.Attribute['PPU'];
3172 if PPUFilename<>'' then begin
3173 // compiler gave ppu file and relative source file
3174 if not FindSrcViaPPU(aPhase,MsgLine,PPUFilename) then continue;
3175 end;
3176 end;
3177 if (not FilenameIsAbsolute(aFilename)) then begin
3178 // short file name => 1. search the full file name in previous message
3179 if i>0 then begin
3180 PrevMsgLine:=Tool.WorkerMessages[i-1];
3181 if (PrevMsgLine.SubTool=DefaultSubTool)
3182 and FilenameIsAbsolute(PrevMsgLine.Filename)
3183 and (CompareFilenames(ExtractFilename(PrevMsgLine.Filename),ExtractFilename(aFilename))=0)
3184 then begin
3185 // same file as previous message => use it
3186 aFilename:=PrevMsgLine.Filename;
3187 MsgLine.Filename:=aFilename;
3188 end;
3189 end;
3190 end;
3191 if (not FilenameIsAbsolute(aFilename)) then begin
3192 // short file name => 2. search in include path
3193 MsgWorkerDir:=MsgLine.Attribute[FPCMsgAttrWorkerDirectory];
3194 FetchIncludePath(aPhase,MsgWorkerDir); // needs Phase etpspAfterReadLine+etpspSynchronized
3195 {$IFDEF VerboseFPCMsgUnitNotFound}
3196 if aPhase=etpspSynchronized then
3197 debugln(['TIDEFPCParser.ImproveMessages IncPath="',fIncludePath,'" aFilename="',aFilename,'" MsgWorkerDir="',MsgWorkerDir,'"']);
3198 {$ENDIF}
3199 if (aPhase in [etpspAfterReadLine,etpspAfterSync])
3200 and (fIncludePathValidForWorkerDir=MsgWorkerDir) then begin
3201 // include path is valid and in worker thread
3202 // -> search file
3203 aFilename:=FileUtil.SearchFileInPath(aFilename,MsgWorkerDir,fIncludePath,';',
3204 [FileUtil.sffSearchLoUpCase,sffFile]);
3205 if aFilename<>'' then
3206 MsgLine.Filename:=aFilename;
3207 end;
3208 end;
3209 if (not FilenameIsAbsolute(aFilename)) and (aPhase=etpspAfterReadLine)
3210 then begin
3211 CmdLineParams:=Tool.CmdLineParams;
3212 if Pos(CmdLineParams,PathDelim+'fpc'+ExeExt+' ')>0 then begin
3213 // short file name => 3. check the cmd line param source file
3214 SrcFilename:=GetFPCParameterSrcFile(Tool.CmdLineParams);
3215 if (SrcFilename<>'')
3216 and ((CompareFilenames(ExtractFilename(SrcFilename),aFilename)=0)
3217 or (CompareFilenames(ExtractFileNameOnly(SrcFilename),aFilename)=0))
3218 then begin
3219 if not FilenameIsAbsolute(SrcFilename) then begin
3220 MsgWorkerDir:=MsgLine.Attribute[FPCMsgAttrWorkerDirectory];
3221 SrcFilename:=ResolveDots(AppendPathDelim(MsgWorkerDir)+SrcFilename);
3222 end;
3223 if FilenameIsAbsolute(SrcFilename) then
3224 MsgLine.Filename:=SrcFilename;
3225 end;
3226 end;
3227 end;
3228
3229 // get source
3230 SourceOK:=false;
3231 aFilename:=MsgLine.Filename;
3232 if FilenameIsAbsolute(aFilename) or (mlfTestBuildFile in MsgLine.Flags)
3233 then begin
3234 if (fCurSource<>nil)
3235 and (CompareFilenames(aFilename,fCurSource.Filename)=0) then begin
3236 SourceOK:=true;
3237 end else begin
3238 // need source
3239 case aPhase of
3240 etpspAfterReadLine:
3241 NeedSynchronize:=true;
3242 etpspSynchronized:
3243 begin
3244 // load source file
3245 //debugln(['TFPCParser.ImproveMessages loading ',aFilename]);
3246 Code:=CodeToolBoss.LoadFile(aFilename,true,false);
3247 if Code<>nil then begin
3248 if fCurSource=nil then
3249 fCurSource:=TCodeBuffer.Create;
3250 fCurSource.Filename:=aFilename;
3251 if Code.FileOnDiskNeedsUpdate then begin
3252 // IDE buffer contains changes that are not yet saved to disk
3253 // The compiler messages are about the disk file
3254 // => load the file
3255 fCurSource.LoadFromFile(aFilename);
3256 end else begin
3257 // IDE buffer valid => just copy
3258 fCurSource.Source:=Code.Source;
3259 end;
3260 SourceOK:=true;
3261 NeedAfterSync:=true;
3262 end;
3263 end;
3264 end;
3265 end;
3266 end;
3267
3268 ImproveMsgIdentifierPosition(aPhase, MsgLine, SourceOK);
3269 ImproveMsgHiddenByIDEDirective(aPhase, MsgLine, SourceOK);
3270 ImproveMsgUnitNotUsed(aPhase, MsgLine);
3271 ImproveMsgSenderNotUsed(aPhase, MsgLine);
3272 end else if MsgLine.SubTool=SubToolFPCLinker then begin
3273 ImproveMsgLinkerUndefinedReference(aPhase, MsgLine);
3274 end;
3275 ImproveMsgUnitNotFound(aPhase, MsgLine);
3276 end;
3277 fLastWorkerImprovedMessage[aPhase]:=Tool.WorkerMessages.Count-1;
3278 end;
3279
TIDEFPCParser.CanParseSubToolnull3280 class function TIDEFPCParser.CanParseSubTool(const SubTool: string): boolean;
3281 begin
3282 Result:=(CompareText(SubTool,SubToolFPC)=0)
3283 or (CompareText(SubTool,SubToolFPCLinker)=0)
3284 or (CompareText(SubTool,SubToolFPCRes)=0);
3285 end;
3286
TIDEFPCParser.DefaultSubToolnull3287 class function TIDEFPCParser.DefaultSubTool: string;
3288 begin
3289 Result:=SubToolFPC;
3290 end;
3291
TIDEFPCParser.GetMsgHintnull3292 class function TIDEFPCParser.GetMsgHint(SubTool: string; MsgID: integer): string;
3293 var
3294 CurMsgFile: TFPCMsgFilePoolItem;
3295 MsgItem: TFPCMsgItem;
3296 begin
3297 Result:='';
3298 if CompareText(SubTool,DefaultSubTool)=0 then begin
3299 CurMsgFile:=MsgFilePool.LoadCurrentEnglishFile(false,nil);
3300 if CurMsgFile=nil then exit;
3301 try
3302 MsgItem:=CurMsgFile.GetMsg(MsgID);
3303 if MsgItem=nil then exit;
3304 Result:=MsgItem.GetTrimmedComment(false,true);
3305 finally
3306 MsgFilePool.UnloadFile(CurMsgFile);
3307 end;
3308 end;
3309 end;
3310
TIDEFPCParser.GetMsgPatternnull3311 class function TIDEFPCParser.GetMsgPattern(SubTool: string; MsgID: integer; out
3312 Urgency: TMessageLineUrgency): string;
3313 var
3314 CurMsgFile: TFPCMsgFilePoolItem;
3315 MsgItem: TFPCMsgItem;
3316 begin
3317 Result:='';
3318 Urgency:=mluNone;
3319 if CompareText(SubTool,DefaultSubTool)=0 then begin
3320 if MsgFilePool=nil then exit;
3321 CurMsgFile:=MsgFilePool.LoadCurrentEnglishFile(false,nil);
3322 if CurMsgFile=nil then exit;
3323 try
3324 MsgItem:=CurMsgFile.GetMsg(MsgID);
3325 if MsgItem=nil then exit;
3326 Result:=MsgItem.Pattern;
3327 Urgency:=FPCMsgToMsgUrgency(MsgItem);
3328 finally
3329 MsgFilePool.UnloadFile(CurMsgFile);
3330 end;
3331 end;
3332 end;
3333
TIDEFPCParser.Prioritynull3334 class function TIDEFPCParser.Priority: integer;
3335 begin
3336 Result:=SubToolFPCPriority;
3337 end;
3338
TIDEFPCParser.MsgLineIsIdnull3339 class function TIDEFPCParser.MsgLineIsId(Msg: TMessageLine; MsgId: integer; out
3340 Value1, Value2: string): boolean;
3341
GetStrnull3342 function GetStr(FromPos, ToPos: PChar): string;
3343 begin
3344 if (FromPos=nil) or (FromPos=ToPos) then
3345 Result:=''
3346 else begin
3347 SetLength(Result,ToPos-FromPos);
3348 Move(FromPos^,Result[1],ToPos-FromPos);
3349 end;
3350 end;
3351
3352 var
3353 aFPCParser: TFPCParser;
3354 Pattern: String;
3355 VarStarts: PPChar;
3356 VarEnds: PPChar;
3357 s: String;
3358 begin
3359 Value1:='';
3360 Value2:='';
3361 if Msg=nil then exit(false);
3362 if Msg.SubTool<>DefaultSubTool then exit(false);
3363 if (Msg.MsgID<>MsgId)
3364 and (Msg.MsgID<>0) then exit(false);
3365 Result:=true;
3366 aFPCParser:=GetFPCParser(Msg);
3367 if aFPCParser=nil then exit;
3368 Pattern:=aFPCParser.GetFPCMsgIDPattern(MsgId);
3369 VarStarts:=GetMem(SizeOf(PChar)*10);
3370 VarEnds:=GetMem(SizeOf(PChar)*10);
3371 s:=Msg.Msg;
3372 Result:=FPCMsgFits(s,Pattern,VarStarts,VarEnds);
3373 if Result then begin
3374 Value1:=GetStr(VarStarts[1],VarEnds[1]);
3375 Value2:=GetStr(VarStarts[2],VarEnds[2]);
3376 end;
3377 Freemem(VarStarts);
3378 Freemem(VarEnds);
3379 end;
3380
TIDEFPCParser.GetFPCMsgIDPatternnull3381 function TIDEFPCParser.GetFPCMsgIDPattern(MsgID: integer): string;
3382 var
3383 MsgItem: TFPCMsgItem;
3384 begin
3385 Result:='';
3386 if MsgID<=0 then exit;
3387 if MsgFile=nil then exit;
3388 MsgItem:=MsgFile.GetMsg(MsgID);
3389 if MsgItem=nil then exit;
3390 Result:=MsgItem.Pattern;
3391 end;
3392
TIDEFPCParser.GetFPCMsgPatternnull3393 class function TIDEFPCParser.GetFPCMsgPattern(Msg: TMessageLine): string;
3394 var
3395 aFPCParser: TFPCParser;
3396 begin
3397 Result:='';
3398 if Msg.MsgID<=0 then exit;
3399 aFPCParser:=GetFPCParser(Msg);
3400 if aFPCParser=nil then exit;
3401 Result:=aFPCParser.GetFPCMsgIDPattern(Msg.MsgID);
3402 end;
3403
TIDEFPCParser.GetFPCMsgValue1null3404 class function TIDEFPCParser.GetFPCMsgValue1(Msg: TMessageLine): string;
3405 begin
3406 Result:='';
3407 if Msg.MsgID<=0 then exit;
3408 if Msg.SubTool<>DefaultSubTool then exit;
3409 if not etFPCMsgParser.GetFPCMsgValue1(Msg.Msg,GetFPCMsgPattern(Msg),Result) then
3410 Result:='';
3411 end;
3412
TIDEFPCParser.GetFPCMsgValuesnull3413 class function TIDEFPCParser.GetFPCMsgValues(Msg: TMessageLine; out Value1,
3414 Value2: string): boolean;
3415 begin
3416 Result:=false;
3417 if Msg.MsgID<=0 then exit;
3418 if Msg.SubTool<>DefaultSubTool then exit;
3419 Result:=etFPCMsgParser.GetFPCMsgValues2(Msg.Msg,GetFPCMsgPattern(Msg),Value1,Value2);
3420 end;
3421
TIDEFPCParser.MsgFilePoolnull3422 class function TIDEFPCParser.MsgFilePool: TFPCMsgFilePool;
3423 begin
3424 Result:=FPCMsgFilePool;
3425 end;
3426
3427 initialization
3428 IDEFPCParser:=TIDEFPCParser;
3429 finalization
3430 FreeAndNil(FPCMsgFilePool);
3431
3432 end.
3433
3434