mirror of
https://gitlab.com/freepascal.org/lazarus/lazarus.git
synced 2025-04-06 02:18:03 +02:00
351 lines
9.1 KiB
ObjectPascal
351 lines
9.1 KiB
ObjectPascal
{
|
|
***************************************************************************
|
|
* *
|
|
* This source is free software; you can redistribute it and/or modify *
|
|
* it under the terms of the GNU General Public License as published by *
|
|
* the Free Software Foundation; either version 2 of the License, or *
|
|
* (at your option) any later version. *
|
|
* *
|
|
* This code is distributed in the hope that it will be useful, but *
|
|
* WITHOUT ANY WARRANTY; without even the implied warranty of *
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
|
|
* General Public License for more details. *
|
|
* *
|
|
* A copy of the GNU General Public License is available on the World *
|
|
* Wide Web at <http://www.gnu.org/copyleft/gpl.html>. You can also *
|
|
* obtain it by writing to the Free Software Foundation, *
|
|
* Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
|
|
* *
|
|
***************************************************************************
|
|
|
|
Author: Mattias Gaertner
|
|
|
|
Abstract:
|
|
Parser for GNU 'make' output.
|
|
}
|
|
unit etMakeMsgParser;
|
|
|
|
{$mode objfpc}{$H+}
|
|
|
|
interface
|
|
|
|
uses
|
|
// RTL
|
|
Classes, SysUtils,
|
|
// CodeTools
|
|
CodeToolsStructs, KeywordFuncLists,
|
|
// LazUtils
|
|
FileUtil, LazFileUtils,
|
|
// IDEIntf
|
|
IDEExternToolIntf;
|
|
|
|
const
|
|
MakeMsgIDEnteringDirectory = 1;
|
|
MakeMsgIDLeavingDirectory = 2;
|
|
|
|
type
|
|
|
|
{ TIDEMakeParser
|
|
Parse lines of 'make' tool. Finding the current directory is needed by other parsers. }
|
|
|
|
TIDEMakeParser = class(TMakeParser)
|
|
protected
|
|
fIsFileExecutable: TFilenameToPointerTree;
|
|
function IsFileExecutable(const Filename: string): boolean;
|
|
public
|
|
DirectoryStack: TStrings;
|
|
DefaultDirectory: string;
|
|
constructor Create(AOwner: TComponent); override;
|
|
destructor Destroy; override;
|
|
procedure InitReading; override;
|
|
procedure ReadLine(Line: string; OutputIndex: integer; var Handled: boolean); override;
|
|
class function DefaultSubTool: string; override;
|
|
class function Priority: integer; override;
|
|
end;
|
|
|
|
function CompStr(const SubStr: string; p: PChar): boolean;
|
|
function CompStrI(const SubStr: string; p: PChar): boolean;
|
|
function FindSubStrI(const SubStr: string; p: PChar): PChar;
|
|
function GetString(p: PChar; MaxLen: integer): string;
|
|
function Str2Integer(p: PChar; const Default: integer): integer;
|
|
function ReadDecimal(var p: PChar): boolean;
|
|
function ReadNumberWithThousandSep(var p: PChar): boolean;
|
|
function ReadChar(var p: PChar; c: char): boolean; inline;
|
|
function ReadString(var p: PChar; const Find: string): boolean; inline;
|
|
|
|
procedure RegisterMakeParser;
|
|
|
|
implementation
|
|
|
|
function CompStr(const SubStr: string; p: PChar): boolean;
|
|
var
|
|
s: PChar;
|
|
begin
|
|
Result:=false;
|
|
if (SubStr='') or (p=nil) then exit;
|
|
s:=PChar(SubStr);
|
|
while (s^<>#0) and (p^=s^) do begin
|
|
inc(p);
|
|
inc(s);
|
|
end;
|
|
Result:=s^=#0;
|
|
end;
|
|
|
|
function CompStrI(const SubStr: string; p: PChar): boolean;
|
|
var
|
|
s: PChar;
|
|
begin
|
|
Result:=false;
|
|
if (SubStr='') or (p=nil) then exit;
|
|
s:=PChar(SubStr);
|
|
while (s^<>#0) and (UpChars[p^]=UpChars[s^]) do begin
|
|
inc(p);
|
|
inc(s);
|
|
end;
|
|
Result:=s^=#0;
|
|
end;
|
|
|
|
function FindSubStrI(const SubStr: string; p: PChar): PChar;
|
|
var
|
|
s: PChar;
|
|
begin
|
|
Result:=nil;
|
|
if (SubStr='') or (p=nil) then exit;
|
|
s:=PChar(SubStr);
|
|
while p^<>#0 do begin
|
|
if (UpChars[p^]=UpChars[s^]) and CompStrI(SubStr,p) then begin
|
|
Result:=p;
|
|
exit;
|
|
end;
|
|
inc(p);
|
|
end;
|
|
end;
|
|
|
|
function GetString(p: PChar; MaxLen: integer): string;
|
|
var
|
|
e: PChar;
|
|
len: Integer;
|
|
begin
|
|
e:=p;
|
|
len:=0;
|
|
while (e^<>#0) and (len<MaxLen) do begin
|
|
inc(e);
|
|
inc(len);
|
|
end;
|
|
SetLength(Result,len);
|
|
if len>0 then
|
|
System.Move(p^,Result[1],len);
|
|
end;
|
|
|
|
function Str2Integer(p: PChar; const Default: integer): integer;
|
|
var
|
|
Negated: Boolean;
|
|
i: int64;
|
|
begin
|
|
i:=0;
|
|
if p^='-' then begin
|
|
Negated:=true;
|
|
inc(p);
|
|
end else
|
|
Negated:=false;
|
|
while p^ in ['0'..'9'] do begin
|
|
i:=i*10+ord(p^)-ord('0');
|
|
if (i>High(Result)) then begin
|
|
Result:=Default;
|
|
exit;
|
|
end;
|
|
inc(p);
|
|
end;
|
|
Result:=i;
|
|
if Negated then
|
|
Result:=-Result;
|
|
end;
|
|
|
|
function ReadDecimal(var p: PChar): boolean;
|
|
var
|
|
OldP: PChar;
|
|
begin
|
|
OldP:=p;
|
|
while p^ in ['0'..'9'] do inc(p);
|
|
Result:=(OldP<p) and (p-OldP<10);
|
|
end;
|
|
|
|
function ReadNumberWithThousandSep(var p: PChar): boolean;
|
|
var
|
|
OldP: PChar;
|
|
begin
|
|
OldP:=p;
|
|
repeat
|
|
case p^ of
|
|
'0'..'9': ;
|
|
'.':
|
|
if p=OldP then exit(false)
|
|
else if p[1]='.' then exit(false);
|
|
else break;
|
|
end;
|
|
inc(p);
|
|
until false;
|
|
Result:=(OldP<p) and (p-OldP<20);
|
|
end;
|
|
|
|
function ReadChar(var p: PChar; c: char): boolean;
|
|
begin
|
|
Result:=p^=c;
|
|
if Result then inc(p);
|
|
end;
|
|
|
|
function ReadString(var p: PChar; const Find: string): boolean;
|
|
begin
|
|
Result:=CompStr(Find,p);
|
|
if Result then inc(p,length(Find));
|
|
end;
|
|
|
|
procedure RegisterMakeParser;
|
|
begin
|
|
ExternalToolList.RegisterParser(TIDEMakeParser);
|
|
end;
|
|
|
|
{ TIDEMakeParser }
|
|
|
|
function TIDEMakeParser.IsFileExecutable(const Filename: string): boolean;
|
|
var
|
|
p: Pointer;
|
|
begin
|
|
p:=fIsFileExecutable[Filename];
|
|
if p=Pointer(Self) then
|
|
Result:=true
|
|
else if p=Pointer(fIsFileExecutable) then
|
|
Result:=false
|
|
else begin
|
|
Result:=FileIsExecutable(Filename);
|
|
if Result then
|
|
fIsFileExecutable[Filename]:=Pointer(Self)
|
|
else
|
|
fIsFileExecutable[Filename]:=Pointer(fIsFileExecutable);
|
|
end;
|
|
end;
|
|
|
|
constructor TIDEMakeParser.Create(AOwner: TComponent);
|
|
begin
|
|
inherited Create(AOwner);
|
|
fIsFileExecutable:=TFilenameToPointerTree.Create(false);
|
|
end;
|
|
|
|
destructor TIDEMakeParser.Destroy;
|
|
begin
|
|
FreeAndNil(fIsFileExecutable);
|
|
FreeAndNil(DirectoryStack);
|
|
inherited Destroy;
|
|
end;
|
|
|
|
procedure TIDEMakeParser.InitReading;
|
|
begin
|
|
DefaultDirectory:=Tool.WorkerDirectory;
|
|
inherited InitReading;
|
|
end;
|
|
|
|
procedure TIDEMakeParser.ReadLine(Line: string; OutputIndex: integer;
|
|
var Handled: boolean);
|
|
{ returns true, if it is a make/gmake message
|
|
Examples for make messages:
|
|
make[1]: Entering directory `<filename>'
|
|
make[1]: Leaving directory `<filename>'
|
|
make[1]: *** [<filename>] Killed
|
|
make <command>
|
|
/bin/cp <options>
|
|
}
|
|
const
|
|
EnterDirPattern = ']: Entering directory `';
|
|
LeavingDirPattern = ']: Leaving directory `';
|
|
MakeMsgPattern = ']: *** [';
|
|
var
|
|
MsgLine: TMessageLine;
|
|
p: PChar;
|
|
Filename, Dir: string;
|
|
Run: PChar;
|
|
begin
|
|
if Line='' then exit;
|
|
p:=PChar(Line);
|
|
if ReadString(p,'make[') or ReadString(p,'make.exe[') then begin
|
|
Handled:=true;
|
|
|
|
MsgLine:=CreateMsgLine(OutputIndex);
|
|
MsgLine.SubTool:=SubToolMake;
|
|
MsgLine.Urgency:=mluVerbose;
|
|
MsgLine.Msg:=Line;
|
|
|
|
while not (p^ in [']',#0]) do inc(p);
|
|
if ReadString(p,EnterDirPattern) then begin
|
|
// entering directory
|
|
MsgLine.MsgID:=MakeMsgIDEnteringDirectory;
|
|
if DefaultDirectory='' then DefaultDirectory:=Tool.WorkerDirectory;
|
|
if (Tool.WorkerDirectory<>'') then begin
|
|
if (DirectoryStack=nil) then DirectoryStack:=TStringList.Create;
|
|
DirectoryStack.Add(Tool.WorkerDirectory);
|
|
end;
|
|
Dir:=p;
|
|
if (Dir<>'') and (Dir[length(Dir)]='''') then
|
|
Dir:=copy(Dir,1,length(Dir)-1);
|
|
Tool.WorkerDirectory:=Dir;
|
|
end else if ReadString(p,LeavingDirPattern) then begin
|
|
// leaving directory
|
|
MsgLine.MsgID:=MakeMsgIDLeavingDirectory;
|
|
if (DirectoryStack<>nil) and (DirectoryStack.Count>0) then begin
|
|
Tool.WorkerDirectory:=DirectoryStack[DirectoryStack.Count-1];
|
|
DirectoryStack.Delete(DirectoryStack.Count-1);
|
|
end else begin
|
|
// leaving what directory?
|
|
Tool.WorkerDirectory:=DefaultDirectory;
|
|
end;
|
|
end else if ReadString(p,MakeMsgPattern) then begin
|
|
MsgLine.Msg:=p-1;
|
|
end;
|
|
AddMsgLine(MsgLine);
|
|
exit;
|
|
end else if ReadString(p,'make ') then begin
|
|
// e.g. make --assume-new=lazbuild.lpr lazbuild
|
|
Handled:=true;
|
|
|
|
MsgLine:=CreateMsgLine(OutputIndex);
|
|
MsgLine.SubTool:=SubToolMake;
|
|
MsgLine.Urgency:=mluVerbose;
|
|
MsgLine.Msg:=Line;
|
|
AddMsgLine(MsgLine);
|
|
exit;
|
|
end;
|
|
|
|
if not (p^ in [#0,' ',#9]) then begin
|
|
// check for command <option>
|
|
Run:=p;
|
|
while not (Run^ in [' ',#9,#0]) do inc(Run);
|
|
if Run^<>#0 then begin
|
|
SetLength(Filename,Run-p);
|
|
System.Move(p^,Filename[1],length(Filename));
|
|
Filename:=TrimFilename(Filename);
|
|
if FilenameIsAbsolute(Filename)
|
|
and ((GetExeExt='') or (ExtractFileExt(Filename)=GetExeExt))
|
|
and IsFileExecutable(Filename) then begin
|
|
Handled:=true;
|
|
MsgLine:=CreateMsgLine(OutputIndex);
|
|
MsgLine.SubTool:=SubToolMake;
|
|
MsgLine.Urgency:=mluVerbose;
|
|
MsgLine.Msg:=Line;
|
|
AddMsgLine(MsgLine);
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
class function TIDEMakeParser.DefaultSubTool: string;
|
|
begin
|
|
Result:=SubToolMake;
|
|
end;
|
|
|
|
class function TIDEMakeParser.Priority: integer;
|
|
begin
|
|
Result:=SubToolMakePriority;
|
|
end;
|
|
|
|
end.
|
|
|