diff --git a/components/iphonelazext/ideext.pas b/components/iphonelazext/ideext.pas
index ff4f5621b..0d50c3d72 100644
--- a/components/iphonelazext/ideext.pas
+++ b/components/iphonelazext/ideext.pas
@@ -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;
diff --git a/components/iphonelazext/iphonebundle.pas b/components/iphonelazext/iphonebundle.pas
index 72c4b448c..73f857d71 100644
--- a/components/iphonelazext/iphonebundle.pas
+++ b/components/iphonelazext/iphonebundle.pas
@@ -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
diff --git a/components/iphonelazext/iphoneextoptions.pas b/components/iphonelazext/iphoneextoptions.pas
index 885ba44fa..58f934a35 100644
--- a/components/iphonelazext/iphoneextoptions.pas
+++ b/components/iphonelazext/iphoneextoptions.pas
@@ -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];
diff --git a/components/iphonelazext/iphoneextstr.pas b/components/iphonelazext/iphoneextstr.pas
index c4c454f81..fd575373b 100644
--- a/components/iphonelazext/iphoneextstr.pas
+++ b/components/iphonelazext/iphoneextstr.pas
@@ -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';
diff --git a/components/iphonelazext/iphonelazext.lpk b/components/iphonelazext/iphonelazext.lpk
index 9117df079..c82bd5aa1 100644
--- a/components/iphonelazext/iphonelazext.lpk
+++ b/components/iphonelazext/iphonelazext.lpk
@@ -12,9 +12,16 @@
-
+
+
+
+
+
+
+
+
@@ -25,7 +32,7 @@
-
+
@@ -114,6 +121,10 @@
+
+
+
+
diff --git a/components/iphonelazext/iphonelazext.pas b/components/iphonelazext/iphonelazext.pas
index 93ea962a5..71435191d 100644
--- a/components/iphonelazext/iphonelazext.pas
+++ b/components/iphonelazext/iphonelazext.pas
@@ -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
diff --git a/components/iphonelazext/iphonelog_form.lfm b/components/iphonelazext/iphonelog_form.lfm
new file mode 100644
index 000000000..17baf9b87
--- /dev/null
+++ b/components/iphonelazext/iphonelog_form.lfm
@@ -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
diff --git a/components/iphonelazext/iphonelog_form.pas b/components/iphonelazext/iphonelog_form.pas
new file mode 100644
index 000000000..c3dea480f
--- /dev/null
+++ b/components/iphonelazext/iphonelog_form.pas
@@ -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.
+
diff --git a/components/iphonelazext/iphonesimctrl.pas b/components/iphonelazext/iphonesimctrl.pas
index 639748347..98b9b396d 100644
--- a/components/iphonelazext/iphonesimctrl.pas
+++ b/components/iphonelazext/iphonesimctrl.pas
@@ -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 (bkoutputstring[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.
diff --git a/components/iphonelazext/lazfilesutils.pas b/components/iphonelazext/lazfilesutils.pas
index ccb534b5f..59e5ee484 100644
--- a/components/iphonelazext/lazfilesutils.pas
+++ b/components/iphonelazext/lazfilesutils.pas
@@ -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('');
diff --git a/components/iphonelazext/plistfile.pas b/components/iphonelazext/plistfile.pas
index d6bab884b..1008bed16 100644
--- a/components/iphonelazext/plistfile.pas
+++ b/components/iphonelazext/plistfile.pas
@@ -292,7 +292,6 @@ end;
function ReadValByNode(valnode: TDomNode): TPListValue;
var
- t : string;
tp : TPlistType;
begin
Result:=nil;
diff --git a/components/iphonelazext/project_iphone_options.lfm b/components/iphonelazext/project_iphone_options.lfm
index 540475e4a..c15546c84 100644
--- a/components/iphonelazext/project_iphone_options.lfm
+++ b/components/iphonelazext/project_iphone_options.lfm
@@ -7,7 +7,7 @@ object iPhoneProjectOptionsEditor: TiPhoneProjectOptionsEditor
ClientWidth = 598
OnClick = FrameClick
TabOrder = 0
- DesignLeft = 408
+ DesignLeft = 410
DesignTop = 201
object chkisPhone: TCheckBox
Left = 16
diff --git a/components/iphonelazext/project_iphone_options.pas b/components/iphonelazext/project_iphone_options.pas
index 6279d298b..a72fbfaa6 100644
--- a/components/iphonelazext/project_iphone_options.pas
+++ b/components/iphonelazext/project_iphone_options.pas
@@ -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;
diff --git a/components/iphonelazext/xibfile.pas b/components/iphonelazext/xibfile.pas
index ffd7aed87..7faae6613 100644
--- a/components/iphonelazext/xibfile.pas
+++ b/components/iphonelazext/xibfile.pas
@@ -1,6 +1,6 @@
unit xibfile;
-{$mode objfpc}
+{$mode objfpc}{$h+}
interface