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