fpc/packages/pastojs/src/pas2jscompilerpp.pp
2018-12-02 18:41:38 +00:00

263 lines
7.4 KiB
ObjectPascal

{
This file is part of the Free Component Library (FCL)
Copyright (c) 2018 Michael Van Canneyt
Pascal to Javascript converter class.
See the file COPYING.FPC, included in this distribution,
for details about the copyright.
This program 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.
**********************************************************************
Abstract:
Pas2JS compiler Preprocessor support. Can depend on filesystem.
}
unit pas2jscompilerpp;
{$mode objfpc}{$H+}
interface
uses
Classes, SysUtils, pas2jscompiler, jswriter, FPPJSSrcMap, contnrs;
Type
{ TPas2JSFSPostProcessorSupport }
TPas2JSFSPostProcessorSupport = Class(TPas2JSPostProcessorSupport)
Private
FPostProcs: TObjectList;
function CmdListAsStr(CmdList: TStrings): string;
Public
Constructor Create(aCompiler: TPas2JSCompiler); override;
Destructor Destroy; override;
Procedure Clear; override;
Procedure WriteUsedTools; override;
Procedure AddPostProcessor(const Cmd: String); override;
Procedure CallPostProcessors(const JSFileName: String; aWriter: TPas2JSMapper); override;
function Execute(const JSFilename: String; Cmd: TStringList; JS: TJSWriterString): TJSWriterString;
end;
implementation
uses process, pas2jslogger, pas2jsutils, pas2jsfileutils;
function TPas2JSFSPostProcessorSupport.CmdListAsStr(CmdList: TStrings): string;
var
i: Integer;
begin
Result:='';
for i:=0 to CmdList.Count-1 do
begin
if Result<>'' then Result+=' ';
Result+=QuoteStr(CmdList[i]);
end;
end;
constructor TPas2JSFSPostProcessorSupport.Create(aCompiler: TPas2JSCompiler);
begin
inherited Create(aCompiler);
FPostProcs:=TObjectList.Create; // Owns objects
end;
destructor TPas2JSFSPostProcessorSupport.Destroy;
begin
FreeAndNil(FPostProcs);
inherited Destroy;
end;
procedure TPas2JSFSPostProcessorSupport.Clear;
begin
FPostProcs.Clear;
end;
procedure TPas2JSFSPostProcessorSupport.WriteUsedTools;
Var
I : integer;
PostProc : TStringList;
begin
// post processors
for i:=0 to FPostProcs.Count-1 do
begin
PostProc:=TStringList(FPostProcs[i]);
Compiler.Log.LogMsgIgnoreFilter(nPostProcessorInfoX,[CmdListAsStr(PostProc)]);
end;
end;
procedure TPas2JSFSPostProcessorSupport.AddPostProcessor(const Cmd: String);
Var
PostProc : TStringList;
S : String;
begin
PostProc:=TStringList.Create;
FPostProcs.Add(PostProc);
SplitCmdLineParams(Cmd,PostProc);
if PostProc.Count<1 then
Compiler.ParamFatal('-Jpcmd executable missing');
// check executable
S:=Compiler.FS.ExpandExecutable(PostProc[0]);
if (S='') then
Compiler.ParamFatal('-Jpcmd executable "'+S+'" not found');
PostProc[0]:=S;
end;
procedure TPas2JSFSPostProcessorSupport.CallPostProcessors(const JSFileName: String; aWriter: TPas2JSMapper);
var
i: Integer;
JS, OrigJS: TJSWriterString;
begin
if FPostProcs.Count=0 then exit;
OrigJS:=aWriter.AsString;
JS:=OrigJS;
for i:=0 to FPostProcs.Count-1 do
JS:=Execute(JSFilename,TStringList(FPostProcs[i]),JS);
if JS<>OrigJS then
begin
aWriter.AsString:=JS;
if aWriter.SrcMap<>nil then
aWriter.SrcMap.Clear;
end;
end;
function TPas2JSFSPostProcessorSupport.Execute(const JSFilename: String; Cmd: TStringList; JS: TJSWriterString): TJSWriterString;
const
BufSize = 65536;
var
Exe: String;
TheProcess: TProcess;
WrittenBytes, ReadBytes: LongInt;
Buf, s, ErrBuf: string;
OutputChunks: TStringList;
CurExitCode, i, InPos: Integer;
begin
Result:='';
Buf:='';
Exe:=Cmd[0];
if Compiler.ShowDebug or Compiler.ShowUsedTools then
Compiler.Log.LogMsgIgnoreFilter(nPostProcessorRunX,[QuoteStr(JSFilename)+' | '+CmdListAsStr(Cmd)]);
if Compiler.FS.DirectoryExists(Exe) then
raise EFOpenError.Create('post processor "'+Exe+'" is a directory');
if not FileIsExecutable(Exe) then
raise EFOpenError.Create('post processor "'+Exe+'" is a not executable');
try
TheProcess := TProcess.Create(nil);
OutputChunks:=TStringList.Create;
try
TheProcess.Executable := Exe;
for i:=1 to Cmd.Count-1 do
TheProcess.Parameters.Add(Cmd[i]);
TheProcess.Options:= [poUsePipes];
TheProcess.ShowWindow := swoHide;
//TheProcess.CurrentDirectory:=WorkingDirectory;
TheProcess.Execute;
ErrBuf:='';
SetLength(Buf,BufSize);
InPos:=1;
repeat
// read stderr and log immediately as warnings
repeat
if TheProcess.Stderr.NumBytesAvailable=0 then break;
ReadBytes:=TheProcess.Stderr.Read(Buf[1],BufSize);
if ReadBytes=0 then break;
ErrBuf+=LeftStr(Buf,ReadBytes);
repeat
i:=1;
while (i<=length(ErrBuf)) and (i<128) and not (ErrBuf[i] in [#10,#13]) do
inc(i);
if i>length(ErrBuf) then break;
Compiler.Log.LogMsg(nPostProcessorWarnX,[LeftStr(ErrBuf,i)]);
if (i<=length(ErrBuf)) and (ErrBuf[i] in [#10,#13]) then
begin
// skip linebreak
if (i<length(ErrBuf)) and (ErrBuf[i+1] in [#10,#13])
and (ErrBuf[i]<>ErrBuf[i+1]) then
inc(i,2)
else
inc(i);
end;
Delete(ErrBuf,1,i-1);
until false;
until false;
// write to stdin
if InPos<length(JS) then
begin
i:=length(JS)-InPos+1;
if i>BufSize then i:=BufSize;
WrittenBytes:=TheProcess.Input.Write(JS[InPos],i);
inc(InPos,WrittenBytes);
if InPos>length(JS) then
TheProcess.CloseInput;
end else
WrittenBytes:=0;
// read stdout
if TheProcess.Output.NumBytesAvailable=0 then
ReadBytes:=0
else
ReadBytes:=TheProcess.Output.Read(Buf[1],BufSize);
if ReadBytes>0 then
OutputChunks.Add(LeftStr(Buf,ReadBytes));
if (WrittenBytes=0) and (ReadBytes=0) then
begin
if not TheProcess.Running then break;
Sleep(10); // give tool some time
end;
until false;
TheProcess.WaitOnExit;
CurExitCode:=TheProcess.ExitCode;
// concatenate output chunks
ReadBytes:=0;
for i:=0 to OutputChunks.Count-1 do
inc(ReadBytes,length(OutputChunks[i]));
SetLength(Result,ReadBytes);
ReadBytes:=0;
for i:=0 to OutputChunks.Count-1 do
begin
s:=OutputChunks[i];
if s='' then continue;
System.Move(s[1],Result[ReadBytes+1],length(s));
inc(ReadBytes,length(s));
end;
finally
OutputChunks.Free;
TheProcess.Free;
end;
except
on E: Exception do begin
if Compiler.ShowDebug then
Compiler.Log.LogExceptionBackTrace(E);
Compiler.Log.LogPlain('Error: '+E.Message);
Compiler.Log.LogMsg(nPostProcessorFailX,[CmdListAsStr(Cmd)]);
Compiler.Terminate(ExitCodeToolError);
end
{$IFDEF Pas2js}
else HandleJSException('[20181118170506] TPas2jsCompiler.CallPostProcessor Cmd: '+CmdListAsStr(Cmd),JSExceptValue,true);
{$ENDIF}
end;
if CurExitCode<>0 then
begin
Compiler.Log.LogMsg(nPostProcessorFailX,[CmdListAsStr(Cmd)]);
Compiler.Terminate(ExitCodeToolError);
end;
if Compiler.ShowDebug or Compiler.ShowUsedTools then
Compiler.Log.LogMsgIgnoreFilter(nPostProcessorFinished,[]);
end;
end.