iphonelazext: support of launching sim with hijacking the app output to the extension log dialog

git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@4412 8e941d3f-bd1b-0410-a28a-d453659cc2b4
This commit is contained in:
skalogryz
2016-01-01 06:45:21 +00:00
parent bb0a3ea92c
commit 861501fa99
14 changed files with 734 additions and 43 deletions

View File

@ -21,11 +21,12 @@ interface
uses
Unix, BaseUnix, process,
Classes, SysUtils, contnrs,
Graphics, Controls, Forms, Dialogs, FileUtil,
Graphics, Controls, Forms, Dialogs, LazFileUtils,
{Lazarus Interface}
LazIDEIntf, MenuIntf, ProjectIntf, IDEOptionsIntf, IDEMsgIntf,
LazIDEIntf, MenuIntf, ProjectIntf, IDEOptionsIntf, IDEMsgIntf
,IDEExternToolIntf
project_iphone_options, xcodetemplate,
,project_iphone_options, xcodetemplate, iphonelog_form,
iPhoneExtOptions, iPhoneExtStr, iPhoneBundle, lazfilesutils, iphonesimctrl;
@ -33,6 +34,12 @@ procedure Register;
implementation
procedure IDEMsg(const msg: string);
begin
IDEMessagesWindow.AddCustomMessage(mluProgress, msg);
end;
type
{ TiPhoneExtension }
@ -47,10 +54,17 @@ type
function ProjectBuilding(Sender: TObject): TModalResult;
function ProjectOpened(Sender: TObject; AProject: TLazProject): TModalResult;
//procedure OnProjOptionsChanged(Sender: TObject; Restore: Boolean);
function GetXcodeProjDirName: string;
public
SimPID : Integer;
SimLogger : TPTYReader;
constructor Create;
procedure UpdateXcode(Sender: TObject);
procedure SimRun(Sender: TObject);
procedure SimTerminate(Sender: TObject);
procedure CloseProc;
procedure OnBytesRead(Sender: TObject; const buf: string);
end;
var
@ -88,10 +102,10 @@ end;
procedure TiPhoneExtension.FillBunldeInfo(forSimulator: Boolean; var info: TiPhoneBundleInfo);
begin
Info.AppID:=ProjOptions.AppID;
Info.DisplayName:=LazarusIDE.ActiveProject.Title;
Info.iPlatform:=EnvOptions.GetSDKName(ProjOptions.SDK, forSimulator);
Info.SDKVersion:=ProjOptions.SDK;
Info.AppID:=UTF8Decode(ProjOptions.AppID);
Info.DisplayName:=UTF8Decode(LazarusIDE.ActiveProject.Title);
Info.iPlatform:=UTF8Decode(EnvOptions.GetSDKName(ProjOptions.SDK, forSimulator));
Info.SDKVersion:=UTF8Decode(ProjOptions.SDK);
Info.MainNib:=UTF8Decode(ProjOptions.MainNib);
end;
@ -111,7 +125,7 @@ var
i : Integer;
s : string;
begin
Space:=ProjOptions.SpaceName; // LazarusIDE.ActiveProject.CustomData.;
Space:=UTF8Decode(ProjOptions.SpaceName); // LazarusIDE.ActiveProject.CustomData.;
bundleName:=ExtractFileName(LazarusIDE.ActiveProject.ProjectInfoFile);
bundleName:=Copy(bundleName, 1, length(bundleName)-length(ExtractFileExt(bundleName)));
@ -122,13 +136,15 @@ begin
FillBunldeInfo(true, Info);
CreateBundle(bundleName, Space, ExtractFileName(nm), Info, RealSpace, bundlepath, exepath);
CreateBundle(UTF8Decode(bundleName), Space
, UTF8Decode(ExtractFileName(nm))
, Info, RealSpace, bundlepath, exepath);
WriteIconTo( IncludeTrailingPathDelimiter(bundlepath)+'Icon.png');
WriteIconTo( UTF8Encode(IncludeTrailingPathDelimiter(bundlepath)+'Icon.png') );
CopySymLinks(
ResolveProjectPath(ProjOptions.ResourceDir),
bundlepath,
UTF8Encode(bundlepath),
// don't copy .xib files, they're replaced by compiled nibs
'*.xib; '+ ProjOptions.ExcludeMask
);
@ -152,7 +168,7 @@ begin
end;
EnumFilesAtDir(ResolveProjectPath(ProjOptions.ResourceDir), '*.xib', xiblist);
for i:=0 to xiblist.Count-1 do begin
dstpath:=IncludeTrailingPathDelimiter(bundlepath)+ChangeFileExt(ExtractFileName(xiblist[i]), '.nib');
dstpath:=UTF8Encode(IncludeTrailingPathDelimiter(bundlepath))+ChangeFileExt(ExtractFileName(xiblist[i]), '.nib');
ExecCmdLineNoWait(Format('ibtool --compile "%s" "%s"', [dstpath, xiblist[i]]));
end;
finally
@ -213,7 +229,7 @@ begin
try
EnvOptions.GetSDKVersions(st);
if st.Count=0 then
IDEMessagesWindow.AddMsg(strWNoSDK, '', 0)
IDEMsg(strWNoSDK)
else begin
sdkver:=st[0];
ProjOptions.SDK:=sdkver;
@ -258,6 +274,7 @@ begin
RegisterIDEMenuCommand(itmProjectWindowSection, 'mnuiPhoneToXCode', strStartAtXcode, @UpdateXcode, nil);
RegisterIDEMenuCommand(itmProjectWindowSection, 'mnuiPhoneRunSim', strRunSimulator, @SimRun, nil);
RegisterIDEMenuCommand(itmProjectWindowSection, 'mnuiPhoneTermSim', strTermSimulator, @SimTerminate, nil);
end;
function TiPhoneExtension.ProjectBuilding(Sender: TObject): TModalResult;
@ -283,6 +300,20 @@ begin
Result:=mrOk;
end;
function TiPhoneExtension.GetXcodeProjDirName: string;
var
dir : string;
projname : string;
ext : string;
begin
dir:=ResolveProjectPath('xcode');
dir:=dir+'/';
projname:=ExtractFileName(LazarusIDE.ActiveProject.MainFile.Filename);
ext:=ExtractFileExt(projname);
projname:=Copy(projname, 1, length(projname)-length(ext));
Result:=dir+projname+'.xcodeproj';
end;
function TiPhoneExtension.WriteIconTo(const FullName: String): Boolean;
var
icofile : string;
@ -348,15 +379,12 @@ var
projname : string;
ext : string;
exename : string;
tname : string;
plistname : string;
Info : TiPhoneBundleInfo;
name : string;
opt : string;
optSim: string;
begin
// the create .plist would be used by XCode project
// the simulator .plist in created with InstallAppToSim.
@ -391,7 +419,11 @@ begin
ForceDirectories(dir);
FillBunldeInfo(false, Info);
WriteDefInfoList( dir + plistname, tname, tname, Info);
WriteDefInfoList(
UTF8Decode(dir + plistname)
, UTF8Decode(tname)
, UTF8Decode(tname)
, Info);
projname:=ExtractFileName(LazarusIDE.ActiveProject.MainFile.Filename);
@ -438,7 +470,7 @@ begin
templates.Free;
build.Free;
IDEMessagesWindow.AddMsg(Format(strXcodeUpdated,[projdir]), '', 0);
IDEMsg(Format(strXcodeUpdated,[projdir]));
end;
procedure SimRunDirect;
@ -450,8 +482,8 @@ begin
try
path:=IncludeTrailingPathDelimiter(EnvOptions.SimBundle)+'Contents/MacOS/iPhone Simulator';
EnvOptions.SubstituteMacros(path);
t.CommandLine:='"'+path+'"';
t.CurrentDirectory:=UTF8Encode(GetSandBoxDir(ProjOptions.SpaceName));
t.Executable:=path;
t.CurrentDirectory:=UTF8Encode(GetSandBoxDir(UTF8Decode(ProjOptions.SpaceName)));
t.Execute;
except
on E: Exception do
@ -474,11 +506,67 @@ end;
procedure TiPhoneExtension.SimRun(Sender: TObject);
var
err : string;
pid : integer;
prj : string;
s : string;
begin
CloseProc;
UpdateXcode(Sender);
// install Xcode project
prj := GetXcodeProjDirName;
IDEMsg('Build+Install Xcode project (xcodebuild)');
if not InstallXcodePrj(prj, 'iphonesimulator') then begin
IDEMsg('xcodebuild failed');
Exit;
end else
IDEMsg('xcodebuild successed, project installed');
IDEMsg('Running Simulator');
if not SimRunInstruct(err) then begin
ShowMessage('Unable to run Simulator. '+err);
IDEMsg('Unable to run Simulator. '+err);
Exit;
end;
if not Assigned(iphonelogform) then
iphonelogform:=Tiphonelogform.Create(Application)
else
iphonelogform.AddNewSheet;
SimLogger:=TPTYReader.Create;
SimLogger.OnBytesRead:=@OnBytesRead;
IDEMsg('Launching the Application on the Simulator');
if RunAppOnSim( ProjOptions.AppID, 'booted', true, pid, s) then begin
SimPID:=pid;
LLDBRediretIO(SimPID, SimLogger.PTY.FileName);
iphonelogform.Show;
end else begin
IDEMsg('Failed to launch Application.');
Exit;
end;
IDEMsg('Success. Application pid='+IntToStr(pid));
end;
procedure TiPhoneExtension.SimTerminate(Sender: TObject);
begin
CloseProc;
end;
procedure TiPhoneExtension.CloseProc;
begin
if SimPID>0 then begin
StopProc(SimPID);
SimPID:=-1;
SimLogger.Free;
SimLogger:=nil;
end;
end;
procedure TiPhoneExtension.OnBytesRead(Sender: TObject; const buf: string);
begin
if not Assigned(iphonelogform) then Exit;
iphonelogform.LogMemo.Append(buf);
end;
procedure Register;

View File

@ -19,7 +19,7 @@ unit iPhoneBundle;
interface
uses
Classes, SysUtils;
Classes, SysUtils, LazFileUtils;
const
platform_iPhoneSim = 'iphonesimulator';
@ -61,7 +61,6 @@ function RandomSpaceName: WideString;
implementation
uses
FileUtil,
iPhoneExtOptions;
function RandomSpaceName: WideString;
@ -253,7 +252,7 @@ begin
]);
if FileExists(InfoFileName) then DeleteFile(InfoFileName);
fs:=TFileStream.Create(InfoFileName, fmCreate or fmOpenWrite);
fs:=TFileStream.Create(UTF8Encode(InfoFileName), fmCreate or fmOpenWrite);
try
if s<>'' then fs.Write(s[1], length(s));
finally

View File

@ -21,8 +21,7 @@ interface
uses
Classes, SysUtils, IDEOptionsIntf, LazIDEIntf, ProjectIntf, MacroIntf,
CompOptsIntf,
iPhoneBundle, XMLConf, XcodeUtils, FileUtil, iphonesimctrl;
iPhoneBundle, XMLConf, XcodeUtils, LazFileUtils, iphonesimctrl;
const
DefaultResourceDir = 'Resources';
@ -247,7 +246,7 @@ end;
function GetDefaultPlatformPath: WideString;
begin
result := '/Applications/Xcode.app/Contents/Developer/Platforms';
if not DirectoryExistsUTF8(result) then
if not DirectoryExistsUTF8(UTF8Encode(result)) then
Result:='/Developer/Platforms';
end;
@ -266,9 +265,9 @@ end;
constructor TiPhoneEnvironmentOptions.Create;
begin
inherited Create;
fPlatformsBaseDir := GetDefaultPlatformPath;
fSimAppsPath := GetDefaultSimAppPath;
fSimBundle := GetDefaultSimBundlePath;
fPlatformsBaseDir := UTF8Encode(GetDefaultPlatformPath);
fSimAppsPath := UTF8Encode(GetDefaultSimAppPath);
fSimBundle := UTF8Encode(GetDefaultSimBundlePath);
fCompilerPath := '/usr/local/bin/fpc';
fVersions:=TStringList.Create;
fDeviceList:=TList.Create;
@ -444,7 +443,7 @@ begin
if CustomData.Contains(optSDK) then fSDK:=CustomData.Values[optSDK];
if CustomData.Contains(optAppID) then fAppID:=CustomData.Values[optAppID];
fSpaceName:=CustomData.Values[optSpaceName];
if fSpaceName='' then fSpaceName:=RandomSpaceName;
if fSpaceName='' then fSpaceName:=UTF8Encode(RandomSpaceName);
if CustomData.Contains(optResourceDir) then fResourceDir:=CustomData.Values[optResourceDir]
else fResourceDir:=DefaultResourceDir;
if CustomData.Contains(optExcludeMask) then fExcludeMask:=CustomData.Values[optExcludeMask];

View File

@ -22,6 +22,7 @@ resourcestring
striPhoneProject = 'iPhone Project';
strStartAtXcode = 'Update Xcode project';
strRunSimulator = 'Run iPhone Simulator';
strTermSimulator = 'Terminate Project App on Sim';
strPrjOptTitle = 'iPhone specific';
strPrjOptIsiPhone = 'is iPhone application project';

View File

@ -12,9 +12,16 @@
</SearchPaths>
<Parsing>
<SyntaxOptions>
<UseAnsiStrings Value="False"/>
<CStyleOperator Value="False"/>
<AllowLabel Value="False"/>
<CPPInline Value="False"/>
</SyntaxOptions>
</Parsing>
<CodeGeneration>
<Optimizations>
<OptimizationLevel Value="0"/>
</Optimizations>
</CodeGeneration>
<Other>
<CompilerMessages>
<IgnoredMessages idx5091="True" idx5060="True" idx5057="True" idx5024="True" idx4081="True" idx4080="True" idx4079="True" idx4055="True"/>
@ -25,7 +32,7 @@
<Description Value="iPhone Development Lazarus extension"/>
<License Value="LGPL"/>
<Version Minor="8"/>
<Files Count="21">
<Files Count="22">
<Item1>
<Filename Value="ideext.pas"/>
<HasRegisterProc Value="True"/>
@ -114,6 +121,10 @@
<Filename Value="res/buildscript.sh"/>
<Type Value="Text"/>
</Item21>
<Item22>
<Filename Value="iphonelog_form.pas"/>
<UnitName Value="iphonelog_form"/>
</Item22>
</Files>
<RequiredPkgs Count="3">
<Item1>

View File

@ -10,7 +10,7 @@ uses
ideext, iPhoneExtStr, iPhoneBundle, XCodeProject,
environment_iphone_options, project_iphone_options, iPhoneExtOptions,
xcodetemplate, LazFilesUtils, XcodeUtils, newXibDialog, xibfile, PlistFile,
xcodeprojutils, iphonesimctrl, LazarusPackageIntf;
xcodeprojutils, iphonesimctrl, iphonelog_form, LazarusPackageIntf;
implementation

View File

@ -0,0 +1,63 @@
object iphonelogform: Tiphonelogform
Left = 443
Height = 342
Top = 233
Width = 501
Caption = 'iPhone Application Log'
ClientHeight = 342
ClientWidth = 501
OnCreate = FormCreate
OnShow = FormShow
LCLVersion = '1.7'
object PageControl1: TPageControl
Left = 0
Height = 316
Top = 26
Width = 501
ActivePage = LogSheet1
Align = alClient
TabIndex = 0
TabOrder = 0
object LogSheet1: TTabSheet
Caption = 'Log'
ClientHeight = 277
ClientWidth = 495
object Memo1: TMemo
Left = 0
Height = 277
Top = 0
Width = 495
Align = alClient
Font.Name = 'Monaco'
Lines.Strings = (
'Memo1'
''
''
''
)
ParentFont = False
ScrollBars = ssBoth
TabOrder = 0
end
end
end
object Panel1: TPanel
Left = 0
Height = 26
Top = 0
Width = 501
Align = alTop
ClientHeight = 26
ClientWidth = 501
TabOrder = 1
object chkStayOnTop: TCheckBox
Left = 8
Height = 18
Top = 5
Width = 94
Caption = 'Stay On Top'
OnChange = chkStayOnTopChange
TabOrder = 0
end
end
end

View File

@ -0,0 +1,81 @@
unit iphonelog_form;
{$mode objfpc}{$H+}
interface
uses
Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, ComCtrls,
StdCtrls, ExtCtrls;
type
{ Tiphonelogform }
Tiphonelogform = class(TForm)
chkStayOnTop: TCheckBox;
LogSheet1: TTabSheet;
Memo1: TMemo;
PageControl1: TPageControl;
Panel1: TPanel;
procedure chkStayOnTopChange(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure FormShow(Sender: TObject);
private
{ private declarations }
fLogMemo: TMemo;
public
{ public declarations }
function AddNewSheet: TTabSheet;
procedure CaptionLog(sh: TTabSheet);
property LogMemo: TMemo read fLogMemo;
end;
var
iphonelogform: Tiphonelogform = nil;
implementation
{$R *.lfm}
{ Tiphonelogform }
procedure Tiphonelogform.chkStayOnTopChange(Sender: TObject);
begin
if chkStayOnTop.Checked
then Self.FormStyle:=fsStayOnTop
else Self.FormStyle:=fsNormal;
end;
procedure Tiphonelogform.FormCreate(Sender: TObject);
begin
fLogMemo:=Memo1;
CaptionLog(LogSheet1);
end;
procedure Tiphonelogform.FormShow(Sender: TObject);
begin
end;
function Tiphonelogform.AddNewSheet: TTabSheet;
var
m : TMemo;
begin
Result:=PageControl1.AddTabSheet;
CaptionLog(Result);
m:=TMemo.Create(Result);
m.Parent:=Result;
m.Align:=Memo1.Align;
m.ScrollBars:=Memo1.ScrollBars;
m.Font.Assign(Memo1.Font);
PageControl1.ActivePage:=Result;
fLogMemo:=m;
end;
procedure Tiphonelogform.CaptionLog(sh: TTabSheet);
begin
sh.Caption:='Log '+FormatDateTime('hh-nn', now);
end;
end.

View File

@ -5,9 +5,11 @@ unit iphonesimctrl;
interface
uses
{$ifdef unix}
BaseUnix, Unix, termio,
{$endif}
Classes, SysUtils, process
, jsonparser, fpjson
{$ifdef unix}, BaseUnix{$endif} // for StopProc()
;
procedure RunSim(const SimName: string);
@ -26,6 +28,99 @@ type
function ListDevice(lst: TList): Boolean;
function InstallXcodePrj(const project, sdk: string): Boolean;
type
{ TRWProcess }
TRWProcess = class(TObject)
protected
process : TProcess;
bytesread : integer;
stderrbytesread : integer;
outputstring : string;
stderrstring : string;
exitstatus : Integer;
procedure CloseProcess;
procedure ReadProc(TimeOut: Integer);
public
constructor Create;
destructor Destroy; override;
function WriteLn(const s: string): Boolean;
function ReadLn(var s: string): Boolean;
function HasLine: Boolean;
procedure Await(TimeOut: integer = -1);
procedure Run(const exename: string; const commands: array of string;
const curdir: string);
procedure Terminate(exitCode: integer);
function isRunning: Boolean;
end;
const
DEF_LLDB_EXENAME = 'lldb';
procedure LLDBRediretIO(const exename: string; pid, ttyfn: string); overload;
procedure LLDBRediretIO(const pid, ttyfn: string); overload;
procedure LLDBRediretIO(pid: integer; const ttyfn: string); overload;
{$ifdef unix}
type
Ptermios = ^termios;
Pwinsize = ^winsize;
function openpty(amaster:pcint; aslave:pcint;
name:Pchar; termp:Ptermios; winp:Pwinsize):longint;cdecl;external clib name 'openpty';
type
TReadEvent = procedure (Sender: TObject; const buf: string) of object;
{ TReadThread }
TReadThread = class(TThread)
protected
readfd: cint;
waitbuf: string;
fOnInputBytes: TReadEvent;
procedure Execute; override;
procedure DoInputBytes;
public
constructor Create(afd: cint);
property OnInputBytes: TReadEvent read fOnInputBytes write fOnInputBytes;
end;
{ TPTY }
TPTY = class(TObject)
private
fSlave : cint;
fMaster : cint;
fFileName : string;
public
constructor Create;
destructor Destroy; override;
property Master: cint read fMaster;
property Slave: cint read fSlave;
property FileName: string read fFileName;
end;
{ TPTYReader }
TPTYReader = class(TObject)
private
fPTY : TPTY;
fOnBytesRead: TReadEvent;
fThread : TReadThread;
fLog : string;
protected
procedure OnInput(Sender: TObject; const Buf: string);
public
constructor Create;
destructor Destroy; override;
property PTY: TPTY read fPTY;
property OnBytesRead: TReadEvent read fOnBytesRead write fOnBytesRead;
property Log: string read fLog;
end;
{$endif}
implementation
procedure RunSim(const SimName: string);
@ -147,5 +242,360 @@ begin
end;
end;
function InstallXcodePrj(const project, sdk: string): Boolean;
var
outstr: string;
begin
writeln('project = ', project);
writeln('sdk = ', sdk);
Result:=RunCommand('xcodebuild', ['install', '-project' ,project, '-sdk',sdk], outstr);
if not Result then Exit;
end;
Const
READ_BYTES = 65536;
{ TRWProcess }
procedure TRWProcess.Run(const exename: string;
const commands: array of string; const curdir: string);
var
i : integer;
begin
if Assigned(process) then CloseProcess;
process:=TProcess.create(nil);
process.Executable:=exename;
if curdir<>'' then process.CurrentDirectory:=curdir;
if high(commands)>=0 then
for i:=low(commands) to high(commands) do
begin
process.Parameters.add(commands[i]);
end;
process.Options := [poUsePipes];
process.Execute;
end;
procedure TRWProcess.Terminate(exitCode: integer);
begin
if Assigned(process) then process.Terminate(exitCode);
end;
function TRWProcess.isRunning: Boolean;
begin
Result:=Assigned(process) and (process.Running);
end;
procedure TRWProcess.CloseProcess;
begin
process.Free;
process:=nil;
end;
procedure TRWProcess.ReadProc(TimeOut: Integer);
var
numbytes : integer;
available : integer;
outputlength : integer;
stderrlength : integer;
stderrnumbytes : integer;
l : integer;
begin
outputlength:=0;
stderrbytesread:=0;
stderrlength:=0;
try
while process.Running do begin
// Only call ReadFromStream if Data from corresponding stream
// is already available, otherwise, on linux, the read call
// is blocking, and thus it is not possible to be sure to handle
// big data amounts bboth on output and stderr pipes. PM.
available:=process.Output.NumBytesAvailable;
if available > 0 then begin
while (BytesRead + available > length(outputstring)) do begin
outputlength:=length(outputstring) + READ_BYTES;
l:=length(outputstring);
Setlength(outputstring,outputlength);
FillChar(outputstring[l+1], length(outputstring)-l, #0);
end;
NumBytes := process.Output.Read(outputstring[1+bytesread], available);
if NumBytes > 0 then
Inc(BytesRead, NumBytes);
end
// The check for assigned(P.stderr) is mainly here so that
// if we use poStderrToOutput in p.Options, we do not access invalid memory.
else if assigned(process.stderr) and (process.StdErr.NumBytesAvailable > 0) then begin
available:=process.StdErr.NumBytesAvailable;
while (StderrBytesRead + available > length(stderrstring)) do begin
stderrlength:=length(stderrstring) + READ_BYTES;
l:=length(stderrstring);
Setlength(stderrstring,stderrlength);
FillChar(stderrstring[l+1], length(stderrstring)-l, #0);
end;
StderrNumBytes := process.StdErr.Read(stderrstring[1+StderrBytesRead], available);
if StderrNumBytes > 0 then
Inc(StderrBytesRead, StderrNumBytes);
end else begin
Sleep(100);
if TimeOut>0 then begin
TimeOut:=TimeOut-100;
if TimeOut<=0 then Exit;
// This is Exit, not Break to prevent reading "available"
end;
end;
end;
// Get left output after end of execution
available:=process.Output.NumBytesAvailable;
while available > 0 do begin
if (BytesRead + available > outputlength) then begin
outputlength:=BytesRead + READ_BYTES;
Setlength(outputstring,outputlength);
end;
NumBytes := process.Output.Read(outputstring[1+bytesread], available);
if NumBytes > 0 then Inc(BytesRead, NumBytes);
available:=process.Output.NumBytesAvailable;
end;
setlength(outputstring,BytesRead);
while assigned(process.stderr) and (process.Stderr.NumBytesAvailable > 0) do begin
available:=process.Stderr.NumBytesAvailable;
if (StderrBytesRead + available > stderrlength) then begin
stderrlength:=StderrBytesRead + READ_BYTES;
Setlength(stderrstring,stderrlength);
end;
StderrNumBytes := process.StdErr.Read(stderrstring[1+StderrBytesRead], available);
if StderrNumBytes > 0 then Inc(StderrBytesRead, StderrNumBytes);
end;
setlength(stderrstring,StderrBytesRead);
exitstatus:=process.exitstatus;
except
on e : Exception do begin
setlength(outputstring,BytesRead);
end;
end;
end;
constructor TRWProcess.Create;
begin
inherited Create;
end;
destructor TRWProcess.Destroy;
begin
CloseProcess;
inherited Destroy;
end;
function TRWProcess.WriteLn(const s: string): Boolean;
var
e : string;
begin
if not Assigned(process) or (not process.Running) then begin
Result:=false;
Exit;
end;
process.Input.Write(s[1], length(s));
e:=LineEnding;
process.Input.Write(e[1], length(e));
Result:=true;
end;
function TRWProcess.ReadLn(var s: string): Boolean;
var
bk: integer;
i: integer;
begin
s:='';
if not Assigned(process) or (outputstring='') then begin
Result:=false;
Exit;
end;
// read remaining bytes
if (not process.Running) and (process.Output.NumBytesAvailable>0) then
ReadProc(-1);
bk:=Pos(#10, outputstring);
if bk<=0 then bk:=Pos(#13, outputstring);
if not process.Running and (bk<=0) then begin
s:=outputstring;
outputstring:='';
bytesread:=0;
Result:=true;
Exit;
end;
Result:=bk>0;
if not Result then Exit;
i:=bk;
if (bk<length(outputstring)) and (outputstring[bk+1] in [#10,#13])
and (outputstring[bk]<>outputstring[bk+1]) then
inc(i);
s:=Copy(outputstring, 1, bk-1);
outputstring:=Copy(outputstring, i+1, length(outputstring));
dec(bytesread, i);
end;
function TRWProcess.HasLine: Boolean;
var
bk: integer;
begin
if not Assigned(process) or (outputstring='') then begin
Result:=false;
Exit;
end;
// read remaining bytes
if (not process.Running) and (process.Output.NumBytesAvailable>0) then
ReadProc(-1);
bk:=Pos(#10, outputstring);
if bk<=0 then bk:=Pos(#13, outputstring);
Result:=(process.Running and (bk>0))
or ((not process.Running) and (length(outputstring)>0));
end;
procedure TRWProcess.Await(TimeOut: integer);
begin
if not Assigned(process) or (not process.Running) then Exit;
ReadProc(TimeOut);
end;
procedure LLDBRediretIO(const pid, ttyfn: string); overload;
begin
LLDBRediretIO(DEF_LLDB_EXENAME, pid, ttyfn);
end;
procedure LLDBRediretIO(pid: integer; const ttyfn: string);
begin
LLDBRediretIO(DEF_LLDB_EXENAME, IntToStr(pid), ttyfn);
end;
procedure LLDBRediretIO(const exename: string; pid, ttyfn: string);
var
p : TRWProcess;
cmd : array[0..1] of string;
s : string;
begin
cmd[0]:='-p';
cmd[1]:=pid;
s:='';
p := TRWProcess.Create;
try
p.Run(exename, cmd, '');
p.WriteLn('version');
p.WriteLn('breakpoint set --name main');
p.WriteLn('breakpoint command add 1');
p.WriteLn('p (int) dup2 ( (int) open("'+ttyfn+'",1), 2 )');
p.WriteLn('p (int) dup2 ( (int) open("'+ttyfn+'",1), 1 )');
p.WriteLn('detach');
p.WriteLn('DONE');
p.WriteLn('c');
repeat
p.Await(300);
while p.HasLine do begin
p.ReadLn(s);
if (Pos('Process', s)>0) and (Pos('detached',s)>0) then begin
p.Writeln('exit');
//p.Terminate;
end;
end;
until not p.isRunning;
finally
p.Free;
end;
end;
{$ifdef unix}
{ TPTYReader }
procedure TPTYReader.OnInput(Sender: TObject; const Buf: string);
begin
fLog:=fLog+Buf;
if Assigned(OnBytesRead) then
OnBytesRead(Self, Buf);
end;
constructor TPTYReader.Create;
begin
inherited Create;
fPTY:=TPTY.Create;
fThread:=TReadThread.Create(fPTY.Master);
fThread.OnInputBytes:=OnInput;
fThread.Start;
end;
destructor TPTYReader.Destroy;
begin
fPTY.Free;
fThread.Terminate;
fThread.WaitFor;
fThread.Free;
inherited Destroy;
end;
{ TPTY }
constructor TPTY.Create;
var
ttyname : string;
res : integer;
begin
inherited Create;
SetLength(ttyname, 1024);
res:=openpty(@fMaster, @fSlave, @ttyname[1], nil, nil);
if res=0 then fFileName:=Copy(ttyname, 1, StrLen(@ttyname[1]));
end;
destructor TPTY.Destroy;
begin
// Slave must be closed before Master!
fpclose(fSlave);
fpclose(fMaster);
inherited Destroy;
end;
{ TReadThread }
procedure TReadThread.Execute;
var
buf: string;
sz: integer;
begin
SetLength(buf, 1024);
while not Terminated do begin
sz:=FpRead(readfd, buf[1], length(Buf));
if sz>0 then begin
waitbuf:=copy(buf, 1, sz);
Synchronize(DoInputBytes);
waitbuf:='';
end else if sz<=0 then begin
Break;
end;
end;
end;
procedure TReadThread.DoInputBytes;
begin
if Assigned(OnInputBytes) then OnInputBytes(Self, waitbuf);
end;
constructor TReadThread.Create(afd: cint);
begin
inherited Create(true);
readfd:=afd;
end;
{$endif}
end.

View File

@ -20,7 +20,7 @@ interface
uses
{$ifdef Unix}BaseUnix,{$endif}
Classes, SysUtils, FileUtil, Masks,
Classes, SysUtils, FileUtil, LazFileUtils, Masks,
LazIDEIntf, ProjectIntf, process;
function ResolveProjectPath(const path: string; project: TLazProject = nil): string;
@ -225,11 +225,11 @@ end;
function ExecCmdLineStdOut(const CmdLineUtf8: AnsiString; var StdOut: string; var ErrCode: LongWord): Boolean;
var
OurCommand : String;
OutputLines : TStringList;
//OurCommand : String;
//OutputLines : TStringList;
MemStream : TStringStream;
OurProcess : TProcess;
NumBytes : LongInt;
//NumBytes : LongInt;
begin
// A temp Memorystream is used to buffer the output
MemStream := TStringStream.Create('');

View File

@ -292,7 +292,6 @@ end;
function ReadValByNode(valnode: TDomNode): TPListValue;
var
t : string;
tp : TPlistType;
begin
Result:=nil;

View File

@ -7,7 +7,7 @@ object iPhoneProjectOptionsEditor: TiPhoneProjectOptionsEditor
ClientWidth = 598
OnClick = FrameClick
TabOrder = 0
DesignLeft = 408
DesignLeft = 410
DesignTop = 201
object chkisPhone: TCheckBox
Left = 16

View File

@ -19,7 +19,7 @@ unit project_iphone_options;
interface
uses
Classes,SysUtils,FileUtil,LResources,Forms,StdCtrls,CheckLst,Buttons, Dialogs,
Classes,SysUtils,FileUtil,LazFileUtils,LResources,Forms,StdCtrls,CheckLst,Buttons, Dialogs,
Menus,IDEOptionsIntf,ProjectIntf,LazIDEIntf,iPhoneExtStr,
iPhoneExtOptions, Controls, LazFilesUtils, XcodeUtils, newXibDialog, xibfile
,CompOptsIntf;

View File

@ -1,6 +1,6 @@
unit xibfile;
{$mode objfpc}
{$mode objfpc}{$h+}
interface