You've already forked lazarus-ccr
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:
@@ -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;
|
||||
|
@@ -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
|
||||
|
@@ -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];
|
||||
|
@@ -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';
|
||||
|
@@ -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>
|
||||
|
@@ -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
|
||||
|
||||
|
63
components/iphonelazext/iphonelog_form.lfm
Normal file
63
components/iphonelazext/iphonelog_form.lfm
Normal 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
|
81
components/iphonelazext/iphonelog_form.pas
Normal file
81
components/iphonelazext/iphonelog_form.pas
Normal 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.
|
||||
|
@@ -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.
|
||||
|
||||
|
@@ -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('');
|
||||
|
@@ -292,7 +292,6 @@ end;
|
||||
|
||||
function ReadValByNode(valnode: TDomNode): TPListValue;
|
||||
var
|
||||
t : string;
|
||||
tp : TPlistType;
|
||||
begin
|
||||
Result:=nil;
|
||||
|
@@ -7,7 +7,7 @@ object iPhoneProjectOptionsEditor: TiPhoneProjectOptionsEditor
|
||||
ClientWidth = 598
|
||||
OnClick = FrameClick
|
||||
TabOrder = 0
|
||||
DesignLeft = 408
|
||||
DesignLeft = 410
|
||||
DesignTop = 201
|
||||
object chkisPhone: TCheckBox
|
||||
Left = 16
|
||||
|
@@ -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;
|
||||
|
@@ -1,6 +1,6 @@
|
||||
unit xibfile;
|
||||
|
||||
{$mode objfpc}
|
||||
{$mode objfpc}{$h+}
|
||||
|
||||
interface
|
||||
|
||||
|
Reference in New Issue
Block a user