diff --git a/components/iphonelazext/environment_iphone_options.lfm b/components/iphonelazext/environment_iphone_options.lfm new file mode 100644 index 000000000..72fb76d23 --- /dev/null +++ b/components/iphonelazext/environment_iphone_options.lfm @@ -0,0 +1,130 @@ +inherited iPhoneSpecificOptions: TiPhoneSpecificOptions + Height = 439 + Width = 485 + ClientHeight = 439 + ClientWidth = 485 + DesignLeft = 631 + DesignTop = 144 + object lblRTLUtils: TLabel[0] + Left = 11 + Height = 18 + Top = 128 + Width = 92 + Caption = 'RTL units path' + ParentColor = False + end + object lblCompilerPath: TLabel[1] + Left = 11 + Height = 18 + Top = 96 + Width = 55 + Caption = 'Compiler' + ParentColor = False + end + object edtCompilerPath: TEdit[2] + Left = 125 + Height = 22 + Top = 96 + Width = 347 + Anchors = [akTop, akLeft, akRight] + TabOrder = 0 + Text = 'edtCompilerPath' + end + object lblXCodeProject: TLabel[3] + Left = 11 + Height = 18 + Top = 72 + Width = 395 + Caption = 'Specific ARM compiler settings (for generated XCode projects)' + ParentColor = False + ParentFont = False + end + object edtRTLPath: TEdit[4] + Left = 125 + Height = 22 + Top = 128 + Width = 347 + Anchors = [akTop, akLeft, akRight] + TabOrder = 1 + Text = 'edtRTLPath' + end + object edtCompilerOptions: TEdit[5] + Left = 125 + Height = 22 + Top = 160 + Width = 347 + Anchors = [akTop, akLeft, akRight] + TabOrder = 2 + Text = 'edtCompilerOptions' + end + object lblCmpOptions: TLabel[6] + Left = 11 + Height = 18 + Top = 160 + Width = 106 + Caption = 'Compiler options' + ParentColor = False + ParentFont = False + OnClick = lblCmpOptionsClick + end + object Label5: TLabel[7] + Left = 11 + Height = 18 + Top = 16 + Width = 97 + Caption = 'Platforms path:' + ParentColor = False + ParentFont = False + end + object edtPlatformsPath: TEdit[8] + Left = 125 + Height = 22 + Top = 16 + Width = 347 + Anchors = [akTop, akLeft, akRight] + TabOrder = 3 + Text = 'edtPlatformsPath' + end + object lblSimSettings: TLabel[9] + Left = 11 + Height = 18 + Top = 232 + Width = 161 + Caption = 'iPhone Simulator settings' + ParentColor = False + end + object edtSimBundle: TEdit[10] + Left = 160 + Height = 22 + Top = 262 + Width = 312 + Anchors = [akTop, akLeft, akRight] + TabOrder = 4 + Text = 'edtSimBundle' + end + object edtSimApps: TEdit[11] + Left = 160 + Height = 22 + Top = 294 + Width = 312 + Anchors = [akTop, akLeft, akRight] + TabOrder = 5 + Text = 'edtSimApps' + end + object lblSimBundle: TLabel[12] + Left = 11 + Height = 18 + Top = 263 + Width = 111 + Caption = 'iPhoneSim bundle' + ParentColor = False + end + object lblSimAppPath: TLabel[13] + Left = 11 + Height = 18 + Top = 294 + Width = 141 + Caption = 'Sim Applications path:' + ParentColor = False + end +end diff --git a/components/iphonelazext/environment_iphone_options.lrs b/components/iphonelazext/environment_iphone_options.lrs new file mode 100644 index 000000000..04025482b --- /dev/null +++ b/components/iphonelazext/environment_iphone_options.lrs @@ -0,0 +1,39 @@ +{ This is an automatically generated lazarus resource file } + +LazarusResources.Add('TiPhoneSpecificOptions','FORMDATA',[ + 'TPF0'#241#22'TiPhoneSpecificOptions'#21'iPhoneSpecificOptions'#6'Height'#3 + +#183#1#5'Width'#3#229#1#12'ClientHeight'#3#183#1#11'ClientWidth'#3#229#1#10 + +'DesignLeft'#3'w'#2#9'DesignTop'#3#144#0#0#242#2#0#6'TLabel'#11'lblRTLUtils' + +#4'Left'#2#11#6'Height'#2#18#3'Top'#3#128#0#5'Width'#2'\'#7'Caption'#6#14'RT' + +'L units path'#11'ParentColor'#8#0#0#242#2#1#6'TLabel'#15'lblCompilerPath'#4 + +'Left'#2#11#6'Height'#2#18#3'Top'#2'`'#5'Width'#2'7'#7'Caption'#6#8'Compiler' + +#11'ParentColor'#8#0#0#242#2#2#5'TEdit'#15'edtCompilerPath'#4'Left'#2'}'#6'H' + +'eight'#2#22#3'Top'#2'`'#5'Width'#3'['#1#7'Anchors'#11#5'akTop'#6'akLeft'#7 + +'akRight'#0#8'TabOrder'#2#0#4'Text'#6#15'edtCompilerPath'#0#0#242#2#3#6'TLab' + +'el'#15'lblXCodeProject'#4'Left'#2#11#6'Height'#2#18#3'Top'#2'H'#5'Width'#3 + +#139#1#7'Caption'#6'=Specific ARM compiler settings (for generated XCode pro' + +'jects)'#11'ParentColor'#8#10'ParentFont'#8#0#0#242#2#4#5'TEdit'#10'edtRTLPa' + +'th'#4'Left'#2'}'#6'Height'#2#22#3'Top'#3#128#0#5'Width'#3'['#1#7'Anchors'#11 + +#5'akTop'#6'akLeft'#7'akRight'#0#8'TabOrder'#2#1#4'Text'#6#10'edtRTLPath'#0#0 + +#242#2#5#5'TEdit'#18'edtCompilerOptions'#4'Left'#2'}'#6'Height'#2#22#3'Top'#3 + +#160#0#5'Width'#3'['#1#7'Anchors'#11#5'akTop'#6'akLeft'#7'akRight'#0#8'TabOr' + +'der'#2#2#4'Text'#6#18'edtCompilerOptions'#0#0#242#2#6#6'TLabel'#13'lblCmpOp' + +'tions'#4'Left'#2#11#6'Height'#2#18#3'Top'#3#160#0#5'Width'#2'j'#7'Caption'#6 + +#16'Compiler options'#11'ParentColor'#8#10'ParentFont'#8#7'OnClick'#7#18'lbl' + +'CmpOptionsClick'#0#0#242#2#7#6'TLabel'#6'Label5'#4'Left'#2#11#6'Height'#2#18 + +#3'Top'#2#16#5'Width'#2'a'#7'Caption'#6#15'Platforms path:'#11'ParentColor'#8 + +#10'ParentFont'#8#0#0#242#2#8#5'TEdit'#16'edtPlatformsPath'#4'Left'#2'}'#6'H' + +'eight'#2#22#3'Top'#2#16#5'Width'#3'['#1#7'Anchors'#11#5'akTop'#6'akLeft'#7 + +'akRight'#0#8'TabOrder'#2#3#4'Text'#6#16'edtPlatformsPath'#0#0#242#2#9#6'TLa' + +'bel'#14'lblSimSettings'#4'Left'#2#11#6'Height'#2#18#3'Top'#3#232#0#5'Width' + +#3#161#0#7'Caption'#6#25'iPhone Simulator settings'#11'ParentColor'#8#0#0#242 + +#2#10#5'TEdit'#12'edtSimBundle'#4'Left'#3#160#0#6'Height'#2#22#3'Top'#3#6#1#5 + +'Width'#3'8'#1#7'Anchors'#11#5'akTop'#6'akLeft'#7'akRight'#0#8'TabOrder'#2#4 + +#4'Text'#6#12'edtSimBundle'#0#0#242#2#11#5'TEdit'#10'edtSimApps'#4'Left'#3 + +#160#0#6'Height'#2#22#3'Top'#3'&'#1#5'Width'#3'8'#1#7'Anchors'#11#5'akTop'#6 + +'akLeft'#7'akRight'#0#8'TabOrder'#2#5#4'Text'#6#10'edtSimApps'#0#0#242#2#12#6 + +'TLabel'#12'lblSimBundle'#4'Left'#2#11#6'Height'#2#18#3'Top'#3#7#1#5'Width'#2 + +'o'#7'Caption'#6#16'iPhoneSim bundle'#11'ParentColor'#8#0#0#242#2#13#6'TLabe' + +'l'#13'lblSimAppPath'#4'Left'#2#11#6'Height'#2#18#3'Top'#3'&'#1#5'Width'#3 + +#141#0#7'Caption'#6#22'Sim Applications path:'#11'ParentColor'#8#0#0#0 +]); diff --git a/components/iphonelazext/environment_iphone_options.pas b/components/iphonelazext/environment_iphone_options.pas new file mode 100644 index 000000000..42b0e87a8 --- /dev/null +++ b/components/iphonelazext/environment_iphone_options.pas @@ -0,0 +1,119 @@ +{ + ***************************************************************************** + * * + * This file is part of the iPhone Laz Extension * + * * + * See the file COPYING.modifiedLGPL.txt, included in this distribution, * + * for details about the copyright. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * + * * + ***************************************************************************** +} +unit environment_iphone_options; + +{$mode objfpc}{$H+} + +interface + +uses + Classes, SysUtils, FileUtil, LResources, Forms, StdCtrls, + IDEOptionsIntf, ProjectIntf, + iPhoneExtOptions; + +type + + { TiPhoneSpecificOptions } + + TiPhoneSpecificOptions = class(TAbstractIDEOptionsEditor) + edtCompilerPath: TEdit; + edtRTLPath: TEdit; + edtCompilerOptions: TEdit; + edtPlatformsPath: TEdit; + edtSimBundle: TEdit; + edtSimApps: TEdit; + lblRTLUtils: TLabel; + lblSimAppPath: TLabel; + lblCompilerPath: TLabel; + lblXCodeProject: TLabel; + lblCmpOptions: TLabel; + Label5: TLabel; + lblSimSettings: TLabel; + lblSimBundle: TLabel; + procedure lblCmpOptionsClick(Sender: TObject); + private + { private declarations } + public + { public declarations } + function GetTitle: String; override; + class function SupportedOptionsClass: TAbstractIDEOptionsClass; override; + procedure Setup(ADialog: TAbstractOptionsEditorDialog); override; + procedure ReadSettings(AOptions: TAbstractIDEOptions); override; + procedure WriteSettings(AOptions: TAbstractIDEOptions); override; + end; + +implementation + +{ TiPhoneSpecificOptions } + +procedure TiPhoneSpecificOptions.lblCmpOptionsClick(Sender: TObject); +begin + +end; + +function TiPhoneSpecificOptions.GetTitle: String; +begin + Result:='Files'; +end; + +procedure TiPhoneSpecificOptions.Setup(ADialog: TAbstractOptionsEditorDialog); +begin + +end; + +procedure TiPhoneSpecificOptions.ReadSettings(AOptions: TAbstractIDEOptions); +var + opt : TiPhoneEnvironmentOptions; +begin + if not Assigned(AOptions) or not (AOptions is TiPhoneEnvironmentOptions) then Exit; + opt:=TiPhoneEnvironmentOptions(AOptions); + opt.Load; + + edtPlatformsPath.Text := opt.PlatformsBaseDir; + edtCompilerPath.Text := opt.CompilerPath; + edtRTLPath.Text := opt.BaseRTLPath; + edtCompilerOptions.Text := opt.CommonOpt; + edtSimBundle.Text := opt.SimBundle; + edtSimApps.Text:= opt.SimAppsPath; +end; + +procedure TiPhoneSpecificOptions.WriteSettings(AOptions: TAbstractIDEOptions); +var + opt : TiPhoneEnvironmentOptions; +begin + if not Assigned(AOPtions) or not (AOptions is TiPhoneEnvironmentOptions) then Exit; + opt:=TiPhoneEnvironmentOptions(AOptions); + + opt.PlatformsBaseDir:=edtPlatformsPath.Text; + opt.CompilerPath:=edtCompilerPath.Text; + opt.BaseRTLPath:=edtRTLPath.Text; + opt.CommonOpt:=edtCompilerOptions.Text; + opt.SimBundle:=edtSimBundle.Text; + opt.SimAppsPath:=edtSimApps.Text; + + opt.Save; +end; + +class function TiPhoneSpecificOptions.SupportedOptionsClass: TAbstractIDEOptionsClass; +begin + Result:=TiPhoneEnvironmentOptions; +end; + +initialization + {$I environment_iphone_options.lrs} + RegisterIDEOptionsEditor(iPhoneEnvGroup, TiPhoneSpecificOptions, iPhoneEnvGroup+1); + +end. + diff --git a/components/iphonelazext/ideext.pas b/components/iphonelazext/ideext.pas new file mode 100644 index 000000000..24f109ad3 --- /dev/null +++ b/components/iphonelazext/ideext.pas @@ -0,0 +1,442 @@ +{ + ***************************************************************************** + * * + * This file is part of the iPhone Laz Extension * + * * + * See the file COPYING.modifiedLGPL.txt, included in this distribution, * + * for details about the copyright. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * + * * + ***************************************************************************** +} +unit ideext; + +{$mode objfpc}{$H+} + +interface + +uses + Unix, BaseUnix, process, + Classes, SysUtils, + Graphics, Controls, Forms, Dialogs, FileUtil, + {Lazarus Interface} + LazIDEIntf, MenuIntf, ProjectIntf, IDEOptionsIntf, PackageIntf, IDEMsgIntf, + + project_iphone_options, xcodetemplate, + + iPhoneExtOptions, iPhoneExtStr, iPhoneBundle, lazfilesutils; + +procedure Register; + +implementation + +type + { TiPhoneExtension } + + TiPhoneExtension = class(TObject) + protected + procedure FillBunldeInfo(forSimulator: Boolean; var info: TiPhoneBundleInfo); + procedure InstallAppToSim; + function FixCustomOptions(const Options: String; isRealDevice: Boolean): String; + function ProjectBuilding(Sender: TObject): TModalResult; + function ProjectOpened(Sender: TObject; AProject: TLazProject): TModalResult; + function WriteIconTo(const FullName: String): Boolean; + public + isiPhoneMenu :TIDEMenuCommand; + constructor Create; + procedure UpdateXCode(Sender: TObject); + procedure SimRun(Sender: TObject); + procedure isProjectClicked(Sender: TObject); + end; + +var + Extension : TiPhoneExtension = nil; + +function GetProjectPlistName(Project: TLazProject): String; +var + ext : String; +begin + if Project.LazCompilerOptions.TargetFilename<>'' then + Result:=Project.LazCompilerOptions.TargetFilename + else begin + Result:=Project.MainFile.Filename; + ext:=ExtractFileExt(Result); + Result:=Copy(Result, 1, length(Result)-length( ext ) ); + end; + Result:=Result+'.plist'; +end; + +function GetProjectExeName(Project: TLazProject): String; +var + ext : String; +begin + if Project.LazCompilerOptions.TargetFilename<>'' then + Result:=Project.LazCompilerOptions.TargetFilename + else begin + Result:=Project.MainFile.Filename; + ext:=ExtractFileExt(Result); + Result:=Copy(Result, 1, length(Result)-length( ext ) ); + end; + Project.LongenFilename(Result); +end; + +{ TiPhoneExtension } + +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; +end; + +procedure TiPhoneExtension.InstallAppToSim; +var + bundlename : string; + bundlepath : WideString; + exepath : WideString; + nm : String; + symlink : String; + Space : WideString; + RealSpace : WideString; + Info : TiPhoneBundleInfo; +begin + Space:=ProjOptions.SpaceName; // LazarusIDE.ActiveProject.CustomData.; + + {todo:} + + bundleName:=ExtractFileName(LazarusIDE.ActiveProject.ProjectInfoFile); + bundleName:=Copy(bundleName, 1, length(bundleName)-length(ExtractFileExt(bundleName))); + + nm:=GetProjectExeName(LazarusIDE.ActiveProject); + + FillBunldeInfo(true, Info); + CreateBundle(bundleName, Space, ExtractFileName(nm), Info, RealSpace, bundlepath, exepath); + + WriteIconTo( IncludeTrailingPathDelimiter(bundlepath)+'Icon.png'); + + if nm<>'' then begin + symlink:=UTF8Encode(exepath); + FpUnlink(symlink); + fpSymlink(PChar(nm), PChar(symlink)); + end; +end; + +function FindParam(const Source, ParamKey: String; var idx: Integer; var Content: String): Boolean; +var + i : Integer; + quoted : Boolean; +begin + Result:=false; + idx:=0; + for i := 1 to length(Source)-1 do + if (Source[i]='-') and ((i=1) or (Source[i-1] in [#9,#32,#10,#13])) then + if Copy(Source, i, 3) = ParamKey then begin + idx:=i; + Result:=true; + Break; + end; + + if not Result then Exit; + + i:=idx+3; + quoted:=(i<=length(Source)) and (Source[i]='"'); + + if not quoted then begin + for i:=i to length(Source) do + if (Source[i] in [#9,#32,#10,#13]) then begin + Content:=Copy(Source, idx+3, i-idx); + Exit; + end; + end else begin + i:=i+1; + for i:=i to length(Source) do + if (Source[i] = '"') then begin + Content:=Copy(Source, idx+3, i-idx+1); + Exit; + end; + end; + Content:=Copy(Source, idx+3, length(Source)-1); +end; + +function TiPhoneExtension.FixCustomOptions(const Options: String; isRealDevice: Boolean): String; +var + prm : string; + rawprm : string; + idx : Integer; + needfix : Boolean; + sdkuse : String; + sdkname : string; +begin + + if isRealDevice + then EnvOptions.GetDeviceSDK(ProjOptions.SDK, sdkname, sdkuse) + else EnvOptions.GetSimSDK(ProjOptions.SDK, sdkname, sdkuse); + sdkuse:=sdkuse; + + Result:=Options; + if FindParam(Result, '-XR', idx, rawprm) then begin + if (rawprm='') or(rawprm[1]<>'"') + then prm:=rawprm + else prm:=rawprm; + + // there might be -XR option, and it might be the same as selected SDK + needfix:=sdkuse<>prm; + + // there's -XR option, but it doesn't match the selection options + if needfix then + Delete(Result, idx, length(rawprm)+3); + + end else begin + //there's no -XR string in custom options + needfix:=true; + idx:=1; + sdkuse:=sdkuse+' '; + end; + + if needfix then Insert('-XR'+sdkuse, Result, idx); +end; + +constructor TiPhoneExtension.Create; +begin + inherited Create; + LazarusIDE.AddHandlerOnProjectOpened(@ProjectOpened); + LazarusIDE.AddHandlerOnProjectBuilding(@ProjectBuilding); + + RegisterIDEMenuCommand(itmProjectWindowSection, 'mnuiPhoneSeparator', '-', nil, nil); + + isiPhoneMenu:=RegisterIDEMenuCommand(itmProjectWindowSection, 'mnuiPhoneProject', striPhoneProject, @isProjectClicked, nil); + + RegisterIDEMenuCommand(itmProjectWindowSection, 'mnuiPhoneToXCode', strStartAtXCode, @UpdateXCode, nil); + RegisterIDEMenuCommand(itmProjectWindowSection, 'mnuiPhoneRunSim', strRunSimulator, @SimRun, nil); +end; + +function TiPhoneExtension.ProjectBuilding(Sender: TObject): TModalResult; +begin + //writeln('project building: ', Sender.ClassName); + Result:=mrOk; + if not Assigned(LazarusIDE.ActiveProject) or not ProjOptions.isIPhoneApp then Exit; + + LazarusIDE.ActiveProject.LazCompilerOptions.CustomOptions := + FixCustomOptions( LazarusIDE.ActiveProject.LazCompilerOptions.CustomOptions, false ); + + //const InfoFileName, BundleName, ExeName: WideString; const info: TiPhoneBundleInfo): Boolean; + {MessageDlg('CustomOptions fixed = '+ LazarusIDE.ActiveProject.LazCompilerOptions.CustomOptions, + mtInformation, [mbOK], 0);} + + InstallAppToSim; + Result:=mrOk; +end; + +function TiPhoneExtension.ProjectOpened(Sender: TObject; AProject: TLazProject): TModalResult; +begin + ProjOptions.Reset; + ProjOptions.Load; + isiPhoneMenu.Checked:=ProjOptions.isIPhoneApp; + Result:=mrOk; +end; + +function TiPhoneExtension.WriteIconTo(const FullName: String): Boolean; +var + icofile : string; + ico : TIcon; + png : TPortableNetworkGraphic; + i, idx : Integer; +const + iPhoneIconSize=57; +begin + Result:=false; + //todo: find a better way of getting the file + icofile:=ChangeFileExt(LazarusIDE.ActiveProject.MainFile.Filename, '.ico'); + LazarusIDE.ActiveProject.LongenFilename(icofile); + if not FileExists(icofile) then begin + // no icon. it should be deleted! + DeleteFile(FullName); + Exit; + end; + + try + ico:=TIcon.Create; + png:=TPortableNetworkGraphic.Create; + try + png.Width:=iPhoneIconSize; + png.Height:=iPhoneIconSize; + ico.LoadFromFile(icofile); + idx:=-1; + for i:=0 to ico.Count- 1 do begin + ico.Current:=i; + if (ico.Width=iPhoneIconSize) and (ico.Height=iPhoneIconSize) then begin + idx:=i; + Break; + end; + end; + if (idx<0) and (ico.Count>0) then idx:=0; + + ico.Current:=idx; + + if (ico.Width=iPhoneIconSize) and (ico.Height=iPhoneIconSize) then + png.Assign(ico) + else begin + //resize to adjust the image + png.Canvas.CopyRect( Rect(0,0, iPhoneIconSize, iPhoneIconSize), ico.Canvas, Rect(0,0, ico.Width, ico.Height)); + end; + + png.SaveToFile(FullName); + finally + ico.free; + png.free; + end; + Result:=true; + except + end; +end; + +procedure TiPhoneExtension.UpdateXCode(Sender: TObject); +var + templates : TStringList; + build : TStringList; + dir : string; + projdir : string; + proj : TStringList; + projname : string; + + ext : string; + tname : string; + plistname : string; + + Info : TiPhoneBundleInfo; + name : string; + + opt : string; +begin + FillBunldeInfo(false, Info); + // the create .plist would be used by XCode project + // the simulator .plist in created with InstallAppToSim. + // they differ with SDKs used + + build := TStringList.Create; + + tname:=ExtractFileName( ChangeFileExt(LazarusIDE.ActiveProject.MainFile.Filename, '')); + plistname:=tname+'.plist'; + + build.Values['INFOPLIST_FILE'] := '"'+plistname+'"'; + build.Values['PRODUCT_NAME'] := '"'+tname+'"'; + build.Values['SDKROOT'] := EnvOptions.GetSDKName(ProjOptions.SDK, false); + build.Values['FPC_COMPILER_PATH'] := '"'+EnvOptions.CompilerPath+'"'; + build.Values['FPC_MAIN_FILE'] := '"'+LazarusIDE.ActiveProject.MainFile.Filename+'"'; + opt := + ' -XR'+EnvOptions.GetSDKFullPath(ProjOptions.SDK, false)+' ' + + ' -FD'+IncludeTrailingPathDelimiter(EnvOptions.PlatformsBaseDir)+'iPhoneOS.platform/Developer/usr/bin ' + + EnvOptions.CommonOpt; + + with LazarusIDE.ActiveProject.LazCompilerOptions do begin + opt:=opt + ' ' +BreakPathsStringToOption(OtherUnitFiles, '-Fu', '\"'); + opt:=opt + ' ' +BreakPathsStringToOption(IncludePath, '-Fi', '\"'); + opt:=opt + ' ' +BreakPathsStringToOption(ObjectPath, '-Fo', '\"'); + opt:=opt + ' ' +BreakPathsStringToOption(Libraries, '-Fl', '\"'); + end; + + build.Values['FPC_CUSTOM_OPTIONS'] :='"'+opt+'"'; + + dir:='xcode'; + LazarusIDE.ActiveProject.LongenFilename(dir); + dir:=dir+'/'; + + name:=ExtractFileName(GetProjectExeName(LazarusIDE.ActiveProject)); + ForceDirectories(dir); + WriteDefInfoList( dir + GetProjectPlistName(LazarusIDE.ActiveProject), + name, name, Info); + + + projname:=ExtractFileName(LazarusIDE.ActiveProject.MainFile.Filename); + ext:=ExtractFileExt(projname); + projname:=Copy(projname, 1, length(projname)-length(ext)); + + projdir:=dir+projname+'.xcodeproj'; + ForceDirectories(projdir); + + projname:=IncludeTrailingPathDelimiter(projdir)+'project.pbxproj'; + proj:=TStringList.Create; + templates:=nil; + try + if not FileExists(projname) then begin + templates:=TStringList.Create; + + if WriteIconTo( IncludeTrailingPathDelimiter(dir)+'Icon.png') then begin + templates.Values['icon']:=XCodeProjectTemplateIcon; + templates.Values['iconid']:=XCodeProjectTemplateIconID; + templates.Values['iconfile']:=XCodeIconFile; + templates.Values['iconfileref']:=XCodeIconFileRef; + end else begin + templates.Values['icon']:=''; + templates.Values['iconid']:=''; + templates.Values['iconfile']:=''; + templates.Values['iconfileref']:=''; + end; + + //todo: + templates.Values['bundle']:=tname+'.app'; + templates.Values['plist']:=plistname; + templates.Values['targetname']:=tname; + templates.Values['productname']:=tname; + + proj.Text:=XCodeProjectTemplate; + end else + proj.LoadFromFile(projname); + + PrepareTemplateFile(proj, templates, build); + proj.SaveToFile(projname); + except + on e: exception do + ShowMessage(e.Message); + end; + + proj.Free; + templates.Free; + build.Free; + + IDEMessagesWindow.AddMsg(strXcodeUpdated, '', 0); +end; + +procedure TiPhoneExtension.SimRun(Sender: TObject); +var + t : TProcess; + path : String; +begin + t :=TProcess.Create(nil); + try + path:=IncludeTrailingPathDelimiter(EnvOptions.SimBundle)+'Contents/MacOS/iPhone Simulator'; + t.CommandLine:='"'+path+'"'; + t.Execute; + except + on E: Exception do + MessageDlg(E.Message, mtInformation, [mbOK], 0); + end; + t.Free; +end; + +procedure TiPhoneExtension.isProjectClicked(Sender: TObject); +begin + if not Assigned(Sender) or not Assigned(LazarusIDE.ActiveProject) then Exit; + TIDEMenuCommand(Sender).Checked:=not TIDEMenuCommand(Sender).Checked; + ProjOptions.isIPhoneApp:=TIDEMenuCommand(Sender).Checked; + //SwitchProject(TIDEMenuCommand(Sender).Checked); +end; + +procedure Register; +begin + // IDE integration is done in constructor + Extension := TiPhoneExtension.Create; + EnvOptions.Load; +end; + +initialization + +finalization + Extension.Free; + +end. + diff --git a/components/iphonelazext/ioptionsdialog.lfm b/components/iphonelazext/ioptionsdialog.lfm new file mode 100644 index 000000000..9a254163d --- /dev/null +++ b/components/iphonelazext/ioptionsdialog.lfm @@ -0,0 +1,98 @@ +object Form1: TForm1 + Left = 301 + Height = 431 + Top = 268 + Width = 629 + Caption = 'iPhone options' + ClientHeight = 431 + ClientWidth = 629 + LCLVersion = '0.9.29' + object PageControl1: TPageControl + Left = 16 + Height = 347 + Top = 16 + Width = 597 + ActivePage = TabSheet2 + Anchors = [akTop, akLeft, akRight, akBottom] + TabIndex = 1 + TabOrder = 0 + object TabSheet1: TTabSheet + Caption = 'iPhone Project Options' + end + object TabSheet2: TTabSheet + Caption = 'Global Options' + ClientHeight = 308 + ClientWidth = 591 + object Label1: TLabel + Left = 20 + Height = 18 + Top = 64 + Width = 119 + Alignment = taRightJustify + Caption = 'iPhone Sim Bundle:' + ParentColor = False + end + object Edit1: TEdit + Left = 149 + Height = 22 + Top = 60 + Width = 416 + Anchors = [akTop, akLeft, akRight] + TabOrder = 0 + Text = 'Edit1' + end + object Label2: TLabel + Left = 37 + Height = 18 + Top = 116 + Width = 102 + Caption = 'FPC arm-darwin:' + ParentColor = False + end + object Edit2: TEdit + Left = 149 + Height = 22 + Top = 116 + Width = 416 + Anchors = [akTop, akLeft, akRight] + TabOrder = 1 + Text = 'Edit2' + end + object Edit3: TEdit + Left = 149 + Height = 22 + Top = 28 + Width = 416 + TabOrder = 2 + Text = 'Edit3' + end + object Label3: TLabel + Left = 9 + Height = 18 + Top = 28 + Width = 130 + Alignment = taRightJustify + Caption = 'iPhone Sim SDKs dir:' + ParentColor = False + end + end + end + object Button1: TButton + Left = 328 + Height = 20 + Top = 384 + Width = 75 + Anchors = [akLeft, akBottom] + Caption = 'Save' + TabOrder = 1 + end + object Button2: TButton + Left = 232 + Height = 20 + Top = 384 + Width = 75 + Anchors = [akLeft, akBottom] + Caption = 'Cancel' + TabOrder = 2 + end +end diff --git a/components/iphonelazext/ioptionsdialog.lrs b/components/iphonelazext/ioptionsdialog.lrs new file mode 100644 index 000000000..600755647 --- /dev/null +++ b/components/iphonelazext/ioptionsdialog.lrs @@ -0,0 +1,29 @@ +{ This is an automatically generated lazarus resource file } + +LazarusResources.Add('TForm1','FORMDATA',[ + 'TPF0'#6'TForm1'#5'Form1'#4'Left'#3'-'#1#6'Height'#3#175#1#3'Top'#3#12#1#5'Wi' + +'dth'#3'u'#2#7'Caption'#6#14'iPhone options'#12'ClientHeight'#3#175#1#11'Cli' + +'entWidth'#3'u'#2#10'LCLVersion'#6#6'0.9.29'#0#12'TPageControl'#12'PageContr' + +'ol1'#4'Left'#2#16#6'Height'#3'['#1#3'Top'#2#16#5'Width'#3'U'#2#10'ActivePag' + +'e'#7#9'TabSheet2'#7'Anchors'#11#5'akTop'#6'akLeft'#7'akRight'#8'akBottom'#0 + +#8'TabIndex'#2#1#8'TabOrder'#2#0#0#9'TTabSheet'#9'TabSheet1'#7'Caption'#6#22 + +'iPhone Project Options'#0#0#9'TTabSheet'#9'TabSheet2'#7'Caption'#6#14'Globa' + +'l Options'#12'ClientHeight'#3'4'#1#11'ClientWidth'#3'O'#2#0#6'TLabel'#6'Lab' + +'el1'#4'Left'#2#20#6'Height'#2#18#3'Top'#2'@'#5'Width'#2'w'#9'Alignment'#7#14 + +'taRightJustify'#7'Caption'#6#18'iPhone Sim Bundle:'#11'ParentColor'#8#0#0#5 + +'TEdit'#5'Edit1'#4'Left'#3#149#0#6'Height'#2#22#3'Top'#2'<'#5'Width'#3#160#1 + +#7'Anchors'#11#5'akTop'#6'akLeft'#7'akRight'#0#8'TabOrder'#2#0#4'Text'#6#5'E' + +'dit1'#0#0#6'TLabel'#6'Label2'#4'Left'#2'%'#6'Height'#2#18#3'Top'#2't'#5'Wid' + +'th'#2'f'#7'Caption'#6#15'FPC arm-darwin:'#11'ParentColor'#8#0#0#5'TEdit'#5 + +'Edit2'#4'Left'#3#149#0#6'Height'#2#22#3'Top'#2't'#5'Width'#3#160#1#7'Anchor' + +'s'#11#5'akTop'#6'akLeft'#7'akRight'#0#8'TabOrder'#2#1#4'Text'#6#5'Edit2'#0#0 + +#5'TEdit'#5'Edit3'#4'Left'#3#149#0#6'Height'#2#22#3'Top'#2#28#5'Width'#3#160 + +#1#8'TabOrder'#2#2#4'Text'#6#5'Edit3'#0#0#6'TLabel'#6'Label3'#4'Left'#2#9#6 + +'Height'#2#18#3'Top'#2#28#5'Width'#3#130#0#9'Alignment'#7#14'taRightJustify' + +#7'Caption'#6#20'iPhone Sim SDKs dir:'#11'ParentColor'#8#0#0#0#0#7'TButton'#7 + +'Button1'#4'Left'#3'H'#1#6'Height'#2#20#3'Top'#3#128#1#5'Width'#2'K'#7'Ancho' + +'rs'#11#6'akLeft'#8'akBottom'#0#7'Caption'#6#4'Save'#8'TabOrder'#2#1#0#0#7'T' + +'Button'#7'Button2'#4'Left'#3#232#0#6'Height'#2#20#3'Top'#3#128#1#5'Width'#2 + +'K'#7'Anchors'#11#6'akLeft'#8'akBottom'#0#7'Caption'#6#6'Cancel'#8'TabOrder' + +#2#2#0#0#0 +]); diff --git a/components/iphonelazext/ioptionsdialog.pas b/components/iphonelazext/ioptionsdialog.pas new file mode 100644 index 000000000..f1bd40051 --- /dev/null +++ b/components/iphonelazext/ioptionsdialog.pas @@ -0,0 +1,7 @@ +unit iOptionsDialog; + +interface + +implementation + +end. diff --git a/components/iphonelazext/iphonebundle.pas b/components/iphonelazext/iphonebundle.pas new file mode 100644 index 000000000..c5f6242e8 --- /dev/null +++ b/components/iphonelazext/iphonebundle.pas @@ -0,0 +1,224 @@ +{ + ***************************************************************************** + * * + * This file is part of the iPhone Laz Extension * + * * + * See the file COPYING.modifiedLGPL.txt, included in this distribution, * + * for details about the copyright. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * + * * + ***************************************************************************** +} +unit iPhoneBundle; + +{$mode objfpc}{$H+} + +interface + +uses + Classes, SysUtils; + +const + platform_iPhoneSim = 'iphonesimulator'; + sdk_iPhoneSim2_0 = 'iphonesimulator2.0'; + sdk_iPhoneSim2_1 = 'iphonesimulator2.1'; + sdk_iPhoneSim2_2 = 'iphonesimulator2.2'; + sdk_iPhoneSim3_0 = 'iphonesimulator3.0'; + sdk_iPhoneSim3_1 = 'iphonesimulator3.1'; + +type + TiPhoneBundleInfo = record + DisplayName : WideString; {if DisplayName='' then DisplayName=BundleName} + iPlatform : WideString; + SDKVersion : WideString; + AppID : WideString; + end; + +function GetUserHomeDir: WideString; + +function GetiPhoneSimUserPath(const UserHomeDir: WideString=''): WideString; + +procedure MakeSimSpaceStruct(const BundleName: WideString; var BundleAppDir: WideString); +procedure MakeSimSpaceStruct(const iPhoneSimUserPath, BundleName: WideString; var BundleAppDir: WideString); +procedure MakeSimSpaceStruct(const iPhoneSimUserPath, SpaceName, BundleName: WideString; var BundleAppDir: WideString); + +function GetBundleExeName(const BundleAppDir, ExeName: WideString): WideString; + +procedure WritePkgFile(const FileName: WideString); +function WriteDefInfoList(const InfoFileName, BundleName, ExeName: WideString; const info: TiPhoneBundleInfo): Boolean; + +procedure CreateBundle(const BundleName, ExeName: WideString; const Info: TiPhoneBundleInfo; var FullBundlePath, FullExeName: WideString); +procedure CreateBundle(const BundleName, SpaceName, ExeName: WideString; const Info: TiPhoneBundleInfo; var RealSpace, FullBundlePath, FullExeName: WideString); + +function AddPathDelim(const w: WideString): WideString; + +function RandomSpaceName: WideString; + +implementation + +function RandomSpaceName: WideString; +var + g : TGUID; + id : String; +begin + CreateGUID(g); + id:=GUIDToString(g); + id:=Copy(id, 2, length(id)-2); + Result:=id; +end; + + +procedure CreateBundle(const BundleName, SpaceName, ExeName: WideString; const Info: TiPhoneBundleInfo; var RealSpace, FullBundlePath, FullExeName: WideString); +var + appdir : WideString; +begin + if SpaceName='' then + RealSpace:=RandomSpaceName + else + RealSpace:=SpaceName; + + MakeSimSpaceStruct(GetiPhoneSimUserPath, RealSpace, BundleName, appdir); + FullBundlePath:=appdir; + appdir:=AddPathDelim(appdir); + FullExeName:=appdir+ExeName; + WritePkgFile(appdir+'PkgInfo'); + WriteDefInfoList(appdir+'Info.plist', BundleName, ExeName, Info); +end; + +procedure CreateBundle(const BundleName, ExeName: WideString; const Info: TiPhoneBundleInfo; var FullBundlePath, FullExeName: WideString); +var + sp : WideString; +begin + CreateBundle(BundleName, '', ExeName, Info, sp, FullBundlePath, FullExeName); +end; + +function AddPathDelim(const w: WideString): WideString; +begin + if w='' then + Result:=PathDelim + else if w[length(w)]<>PathDelim then + Result:=w+PathDelim; +end; + +function GetUserHomeDir: WideString; +begin + Result:=UTF8Decode(GetUserDir); +end; + +function GetiPhoneSimUserPath(const UserHomeDir: WideString=''): WideString; +var + nm : WideString; +begin + if UserHomeDir = '' then nm:=GetUserHomeDir + else nm:=UserHomeDir; + + if nm='' then Exit; + if nm[length(nm)]<>'/' then nm:=nm+'/'; + Result:=nm+'Library/Application Support/iPhone Simulator/User/Applications/'; +end; + +{ +~/Library/Application Support/iPhone Simulator/Users/%SPACENAME% + +%SPACENAME%/Applications +%SPACENAME%/Applications/%AppBundle.app% +%SPACENAME%/Documents +%SPACENAME%/tmp +} +procedure MakeSimSpaceStruct(const iPhoneSimUserPath, SpaceName, BundleName: WideString; var BundleAppDir: WideString); +var + path8 : String; + space8 : String; + p : string; +begin + path8:=UTF8Encode(iPhoneSimUserPath); + space8:=UTF8Encode(SpaceName); + + p:=IncludeTrailingPathDelimiter(IncludeTrailingPathDelimiter(path8)+space8); + BundleAppDir:=UTF8Decode(p+UTF8Encode(BundleName)+'.app'); + ForceDirectories(UTF8Encode(BundleAppDir)); + ForceDirectories(p+'Documents'); + ForceDirectories(p+'tmp'); + +end; + +procedure MakeSimSpaceStruct(const iPhoneSimUserPath, BundleName: WideString; var BundleAppDir: WideString); +begin + MakeSimSpaceStruct(iPhoneSimUserPath, RandomSpaceName, BundleName, BundleAppDir); +end; + +procedure MakeSimSpaceStruct(const BundleName: WideString; var BundleAppDir: WideString); +begin + MakeSimSpaceStruct( GetiPhoneSimUserPath, BundleName, BundleAppDir); +end; + +function GetBundleExeName(const BundleAppDir, ExeName: WideString): WideString; +begin + Result:=AddPathDelim(BundleAppDir)+ExeName; +end; + +procedure WritePkgFile(const FileName: WideString); +var + fs : TFileStream; + s : String; +begin + fs:=TFileStream.Create( UTF8Encode(FileName), fmCreate); + s:='APPL????'; + fs.Write(s[1], length(s)); + fs.Free; +end; + +function WriteDefInfoList(const InfoFileName, BundleName, ExeName: WideString; const info: TiPhoneBundleInfo): Boolean; +const + BundleFormat : AnsiString = + ''#10+ + ''#10+ + ''#10+ + ''#10+ + ' CFBundleDevelopmentRegion'#10+ ' English'#10+ + ' CFBundleDisplayName'#10+ ' %s'#10+ {display name} + ' CFBundleExecutable'#10+ ' %s'#10+ {exe name} + ' CFBundleIdentifier'#10+ ' %s'#10+ {company + bundle name} + ' CFBundleInfoDictionaryVersion'#10+ ' 6.0'#10+ + ' CFBundleName'#10+ ' %s'#10+ {bundle name} + ' CFBundlePackageType'#10+ ' APPL'#10+ + ' CFBundleSignature'#10+ ' ????'#10+ + ' CFBundleSupportedPlatforms'#10+ ' '#10+' %s'#10+' '#10+ {platform} + ' CFBundleVersion'#10+ ' 1.0'#10+ + ' DTPlatformName'#10+ ' %s'#10+ {platform} + ' DTSDKName'#10+ ' %s'#10+ {sdk version} + ' LSRequiresIPhoneOS'#10+ ' '#10+ + ''#10+ + ''; +var + dispName : WideString; + s : String; + fs : TFileStream; +begin + Result:=false; + if BundleName='' then Exit; + + dispName:=info.DisplayName; + if dispName='' then dispName:=BundleName; + + with info do + s:=Format( BundleFormat, + [ UTF8Encode(dispName), + UTF8Encode(ExeName), + UTF8Encode(AppID), + UTF8Encode(BundleName), + UTF8Encode(iPlatform), + UTF8Encode(iPlatform), + UTF8Encode(SDKVersion) + ]); + if FileExists(InfoFileName) then DeleteFile(InfoFileName); + fs:=TFileStream.Create(InfoFileName, fmCreate or fmOpenWrite); + if s<>'' then fs.Write(s[1], length(s)); + fs.Free; +end; + +end. + diff --git a/components/iphonelazext/iphoneextoptions.pas b/components/iphonelazext/iphoneextoptions.pas new file mode 100644 index 000000000..fae2117f7 --- /dev/null +++ b/components/iphonelazext/iphoneextoptions.pas @@ -0,0 +1,331 @@ +{ + ***************************************************************************** + * * + * This file is part of the iPhone Laz Extension * + * * + * See the file COPYING.modifiedLGPL.txt, included in this distribution, * + * for details about the copyright. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * + * * + ***************************************************************************** +} + +unit iPhoneExtOptions; + +{$mode objfpc}{$H+} + +interface + +uses + Classes, SysUtils, IDEOptionsIntf, LazIDEIntf, ProjectIntf, iPhoneBundle, XMLConf; + +type + + { TiPhoneProjectOptions } + + TiPhoneProjectOptions = class(TAbstractIDEProjectOptions) + private + fisiPhone : Boolean; + fAppID : string; + fSDK : string; + DataWritten : Boolean; + fSpaceName : string; + public + constructor Create; + class function GetGroupCaption: string; override; + class function GetInstance: TAbstractIDEOptions; override; + function Load: Boolean; + function Save: Boolean; + procedure Reset; + property isIPhoneApp: Boolean read fisIPhone write fisIPhone; + property SDK: string read fSDK write fSDK; + property AppID: string read fAppID write fAppID; + property SpaceName: string read fSpaceName write fSpaceName; + end; + + { TiPhoneEnvironmentOptions } + + TiPhoneEnvironmentOptions = class(TAbstractIDEEnvironmentOptions) + private + fPlatformsBaseDir : string; + fCompilerPath : string; + fBaseRTLPath : string; + fCommonOpt : string; + fSimAppsPath : string; + fSimBundle : string; + protected + function XMLFileName: string; + public + constructor Create; + class function GetGroupCaption: string; override; + class function GetInstance: TAbstractIDEOptions; override; + + function Load: Boolean; + function Save: Boolean; + + function GetSimSDK(const ProjSDK: string; var SDKName, SDKFullPath: string): Boolean; + function GetDeviceSDK(const ProjSDK: string; var SDKName, SDKFullPath: string): Boolean; + + function GetSDKName(const SDKVer: string; simulator: Boolean): string; + function GetSDKFullPath(const SDKVer: string; simulator: Boolean): string; + + procedure GetSDKVersions(Strings: TStringList); + + + property PlatformsBaseDir: string read fPlatformsBaseDir write fPlatformsBaseDir; + property CompilerPath: string read fCompilerPath write fCompilerPath; + property BaseRTLPath: string read fBaseRTLPath write fBaseRTLPath; + property CommonOpt: string read fCommonOpt write fCommonOpt; + + property SimBundle: string read fSimBundle write fSimBundle; + property SimAppsPath: string read fSimAppsPath write fSimAppsPath; + end; + +function EnvOptions: TiPhoneEnvironmentOptions; +function ProjOptions: TiPhoneProjectOptions; + +var + iPhoneEnvGroup : Integer; + iPhonePrjGroup : Integer; + +implementation + +var + fEnvOptions : TiPhoneEnvironmentOptions = nil; + fProjOptions : TiPhoneProjectOptions = nil; + +const + DefaultXMLName = 'iphoneextconfig.xml'; + + optisIphone = 'iPhone/isiPhoneApp'; + optSDK = 'iPhone/SDK'; + optAppID = 'iPhone/AppID'; + optSpaceName = 'iPhone/SimSpaceName'; + + +function EnvOptions: TiPhoneEnvironmentOptions; +begin + if not Assigned(fEnvOptions) then + fEnvOptions:=TiPhoneEnvironmentOptions.Create; + Result:=fEnvOptions; +end; + +function ProjOptions: TiPhoneProjectOptions; +begin + if not Assigned(fProjOptions) then + fProjOptions:=TiPhoneProjectOptions.Create; + Result:=fProjOptions; +end; + +procedure InitOptions; +begin + iPhoneEnvGroup := GetFreeIDEOptionsGroupIndex(GroupEnvironment); + iPhonePrjGroup := GetFreeIDEOptionsGroupIndex(GroupProject); + RegisterIDEOptionsGroup(iPhoneEnvGroup, TiPhoneEnvironmentOptions); + RegisterIDEOptionsGroup(iPhonePrjGroup, TiPhoneProjectOptions); +end; + +procedure FreeOptions; +begin + fEnvOptions.Free; +end; + +{ TiPhoneEnvironmentOptions } + +class function TiPhoneEnvironmentOptions.GetGroupCaption: string; +begin + Result:='iPhone Environment'; +end; + +class function TiPhoneEnvironmentOptions.GetInstance: TAbstractIDEOptions; +begin + Result:=EnvOptions; +end; + +function TiPhoneEnvironmentOptions.XMLFileName: String; +begin + Result:=IncludeTrailingPathDelimiter(LazarusIDE.GetPrimaryConfigPath)+DefaultXMLName; +end; + +function GetDefaultPlatformPath: WideString; +begin + //todo: + Result:='/Developer/Platforms'; +end; + +function GetDefaultSimBundlePath: WideString; +begin + //todo: + Result:=IncludeTrailingPathDelimiter(GetDefaultPlatformPath)+ + 'iPhoneSimulator.platform/Developer/Applications/iPhone Simulator.app'; +end; + +function GetDefaultSimAppPath: WideSTring; +begin + //todo: + Result:=IncludeTrailingPathDelimiter(GetUserDir)+ + 'Library/Applications Support/iPhone Simulator/User/Applications/'; +end; + +constructor TiPhoneEnvironmentOptions.Create; +begin + inherited Create; + fPlatformsBaseDir := GetDefaultPlatformPath; + fSimAppsPath := GetDefaultSimAppPath; + fSimBundle := GetDefaultSimBundlePath; + fCompilerPath := '/usr/local/bin/fpc'; +end; + + +function TiPhoneEnvironmentOptions.Load: Boolean; +var + xmlcfg : TXMLConfig; +begin + Result:=true; + try + xmlcfg := TXMLConfig.Create(nil); + try + xmlcfg.RootName:='config'; + xmlcfg.Filename:=XMLFileName; + fPlatformsBaseDir := UTF8Encode(xmlcfg.GetValue('Platforms', fPlatformsBaseDir )); + fCompilerPath := UTF8Encode(xmlcfg.GetValue('Compiler', fCompilerPath)); + fBaseRTLPath := UTF8Encode(xmlcfg.GetValue('RTLPath', fBaseRTLPath)); + fCommonOpt := UTF8Encode(xmlcfg.GetValue('CompilerOptions', fCommonOpt)); + fSimBundle := UTF8Encode(xmlcfg.GetValue('SimBundle', fSimBundle)); + fSimAppsPath := UTF8Encode(xmlcfg.GetValue('SimAppPath', fSimAppsPath)); + finally + xmlcfg.Free; + end; + except + Result:=false; + end; +end; + +function TiPhoneEnvironmentOptions.Save: Boolean; +var + xmlcfg : TXMLConfig; +begin + Result:=true; + try + xmlcfg := TXMLConfig.Create(nil); + try + xmlcfg.RootName:='config'; + xmlcfg.Filename:=XMLFileName; + xmlcfg.SetValue('Platforms', UTF8Decode(fPlatformsBaseDir)); + xmlcfg.SetValue('Compiler', UTF8Decode(fCompilerPath)); + xmlcfg.SetValue('RTLPath', UTF8Decode(fBaseRTLPath)); + xmlcfg.SetValue('CompilerOptions', UTF8Decode(fCommonOpt)); + xmlcfg.SetValue('SimBundle', UTF8Decode(fSimBundle)); + xmlcfg.SetValue('SimAppPath', UTF8Decode(fSimAppsPath)); + finally + xmlcfg.Free; + end; + except + Result:=false; + end; + +end; + +function TiPhoneEnvironmentOptions.GetSimSDK(const ProjSDK: string; var SDKName, SDKFullPath: string): Boolean; +const + iPhoneSimSDKDir = 'iPhoneSimulator2.0.sdk'; +begin + SDKName:=sdk_iPhoneSim2_0; + SDKFullPath:=IncludeTrailingPathDelimiter(fPlatformsBaseDir)+'iPhoneSimulator.platform/Developer/SDKs/'+iPhoneSimSDKDir; + Result:=true; +end; + +function TiPhoneEnvironmentOptions.GetDeviceSDK(const ProjSDK: string; var SDKName, SDKFullPath: string): Boolean; +begin + SDKName:='iphoneos2.0'; + SDKFullPath:=''; + Result:=false; +end; + +function TiPhoneEnvironmentOptions.GetSDKName(const SDKVer: string; + simulator: Boolean): string; +begin + if simulator then Result:='iphonesimulator2.0' + else Result:='iphoneos2.0'; +end; + +function TiPhoneEnvironmentOptions.GetSDKFullPath(const SDKVer: string; simulator: Boolean): string; +begin + if simulator then + Result := IncludeTrailingPathDelimiter(fPlatformsBaseDir)+'iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator2.0.sdk' + else + Result := IncludeTrailingPathDelimiter(fPlatformsBaseDir)+'iPhoneOS.platform/Developer/SDKs/iPhoneOS2.0.sdk'; +end; + +procedure TiPhoneEnvironmentOptions.GetSDKVersions(Strings: TStringList); +begin + //todo: + Strings.Add('iPhone OS 2.0'); +end; + +{ TiPhoneProjectOptions } + +procedure TiPhoneProjectOptions.Reset; +begin + fisiPhone:=false; + fSDK:='iPhone 2.0'; + fAppID:='com.mycompany.myapplication'; + fSpaceName:=''; + DataWritten:=false; +end; + +constructor TiPhoneProjectOptions.Create; +begin + inherited Create; + Reset; +end; + +class function TiPhoneProjectOptions.GetGroupCaption: string; +begin + Result:='iPhone'; +end; + +class function TiPhoneProjectOptions.GetInstance: TAbstractIDEOptions; +begin + Result:=ProjOptions; +end; + +function TiPhoneProjectOptions.Load: Boolean; +begin + Result:=True; + with LazarusIDE.ActiveProject do begin + DataWritten:=CustomData.Contains(optisIphone); + fisiPhone:=(DataWritten) and (CustomData.Values[optisIphone] = 'true'); + 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; + end; +end; + +function TiPhoneProjectOptions.Save: Boolean; +const + BoolStr : array[Boolean] of string = ('false', 'true'); +begin + Result:=True; + {do not write iPhone related info to non-iPhone projects} + if DataWritten or fisiPhone then + with LazarusIDE.ActiveProject do begin + CustomData.Values[optisIPhone] := BoolStr[fisiPhone]; + CustomData.Values[optSDK]:=fSDK; + CustomData.Values[optAppID]:=fAppID; + CustomData.Values[optSpaceName]:=fSpaceName; + end; +end; + +initialization + InitOptions; + +finalization + FreeOptions; + +end. + diff --git a/components/iphonelazext/iphoneextstr.pas b/components/iphonelazext/iphoneextstr.pas new file mode 100644 index 000000000..06f89fed8 --- /dev/null +++ b/components/iphonelazext/iphoneextstr.pas @@ -0,0 +1,38 @@ +{ + ***************************************************************************** + * * + * This file is part of the iPhone Laz Extension * + * * + * See the file COPYING.modifiedLGPL.txt, included in this distribution, * + * for details about the copyright. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * + * * + ***************************************************************************** +} +unit iPhoneExtStr; + +{$mode objfpc}{$H+} + +interface + +resourcestring + striPhoneProject = 'iPhone Project'; + strStartAtXCode = 'Update XCode project'; + strRunSimulator = 'Run iPhone Simulator'; + + strPrjOptTitle = 'iPhone specific'; + strPrjOptIsiPhone = 'is iPhone application project'; + strPrjOptSDKver = 'SDK version:'; + strPrjOptCheckSDK = 'Check available SDKs'; + strPtrOptAppID = 'Application ID'; + strPtrOptAppIDHint = 'It''s recommended by Apple to use domain-structured identifier i.e. "com.mycompany.myApplication"'; + + strXcodeUpdated = 'Xcode project updated'; + +implementation + +end. + diff --git a/components/iphonelazext/iphonelazext.lpk b/components/iphonelazext/iphonelazext.lpk new file mode 100644 index 000000000..1f593d2a4 --- /dev/null +++ b/components/iphonelazext/iphonelazext.lpk @@ -0,0 +1,90 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/components/iphonelazext/iphonelazext.pas b/components/iphonelazext/iphonelazext.pas new file mode 100644 index 000000000..aa84b986c --- /dev/null +++ b/components/iphonelazext/iphonelazext.pas @@ -0,0 +1,23 @@ +{ This file was automatically created by Lazarus. do not edit! + This source is only used to compile and install the package. + } + +unit iphonelazext; + +interface + +uses + ideext, iPhoneExtStr, iPhoneBundle, XCodeProject, + environment_iphone_options, project_iphone_options, iPhoneExtOptions, + xcodetemplate, lazfilesutils, LazarusPackageIntf; + +implementation + +procedure Register; +begin + RegisterUnit('ideext', @ideext.Register); +end; + +initialization + RegisterPackage('iphonelazext', @Register); +end. diff --git a/components/iphonelazext/lazfilesutils.pas b/components/iphonelazext/lazfilesutils.pas new file mode 100644 index 000000000..599b6ff75 --- /dev/null +++ b/components/iphonelazext/lazfilesutils.pas @@ -0,0 +1,137 @@ +{ + ***************************************************************************** + * * + * This file is part of the iPhone Laz Extension * + * * + * See the file COPYING.modifiedLGPL.txt, included in this distribution, * + * for details about the copyright. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * + * * + ***************************************************************************** +} +unit lazfilesutils; + +{$mode objfpc}{$H+} + +interface + +uses + Classes, SysUtils, FileUtil, + LazIDEIntf,ProjectIntf; + + +function ResolveProjectPath(const path: string; project: TLazProject = nil): string; + +function BreakPathsStringToOption(const Paths, Switch: String; const Quotes: string = '"'; project: TLazProject = nil): String; + +function RelativeToFullPath(const BasePath, Relative: string): String; +function NeedQuotes(const path: string): Boolean; + +implementation + +function GetNextDir(const Path: string; var index: integer; var Name: string): Boolean; +var + i : Integer; +begin + Result:=index<=length(Path); + if not Result then Exit; + + if Path[index]=PathDelim then inc(index); + Result:=index<=length(Path); + if not Result then Exit; + + for i:=index to length(Path) do + if Path[i]=PathDelim then begin + Name:=Copy(Path, index, i - index); + index:=i+1; + Exit; + end; + Name:=Copy(Path, index, length(Path) - index+1); + index:=length(Path)+1; +end; + +function RelativeToFullPath(const BasePath, Relative: string): String; +var + i : integer; + nm : string; +begin + Result:=ExcludeTrailingPathDelimiter(BasePath); + i:=1; + while GetNextDir(Relative, i, nm) do + if nm = '..' then + Result:=ExtractFileDir(Result) + else if nm <> '.' then + Result:=IncludeTrailingPathDelimiter(Result)+nm; +end; + +function NeedQuotes(const path: string): Boolean; +var + i : integer; +const + SpaceChars = [#32,#9]; +begin + for i:=1 to length(path) do + if path[i] in SpaceChars then begin + Result:=true; + Exit; + end; + Result:=false; +end; + +function QuoteStrIfNeeded(const path: string; const quotes: String): String; +begin + if NeedQuotes(path) then + Result:=quotes+path+quotes + else + Result:=path; +end; + +function ResolveProjectPath(const path: string; project: TLazProject): string; +var + base : string; +begin + if project=nil then project:=LazarusIDE.ActiveProject; + + if FilenameIsAbsolute(Path) then + Result:=Path + else begin + base:=''; + base:=ExtractFilePath(project.ProjectInfoFile); + Result:=RelativeToFullPath(base, Path); + end; +end; + +function BreakPathsStringToOption(const Paths, Switch, Quotes: String; project: TLazProject): String; +var + i, j : Integer; + fixed : String; +begin + Result:=''; + if not Assigned(project) then + project:=LazarusIDE.ActiveProject; + + if not Assigned(project) then Exit; + + j:=1; + for i:=1 to length(paths)-1 do + if Paths[i]=';' then begin + fixed:=Trim(Copy(paths,j, i-j) ); + if fixed<>'' then begin + fixed:=ResolveProjectPath(fixed, project); + Result:=Result+' ' + Switch + QuoteStrIfNeeded(fixed, quotes); + end; + j:=i+1; + end; + + fixed:=Trim(Copy(paths,j, length(paths)-j+1) ); + if fixed<>'' then begin + fixed:=ResolveProjectPath(fixed, project); + Result:=Result+' ' + Switch + QuoteStrIfNeeded(fixed, quotes); + end; +end; + +end. + diff --git a/components/iphonelazext/project_iphone_options.lfm b/components/iphonelazext/project_iphone_options.lfm new file mode 100644 index 000000000..4aace2fdf --- /dev/null +++ b/components/iphonelazext/project_iphone_options.lfm @@ -0,0 +1,90 @@ +inherited iPhoneProjectOptionsEditor: TiPhoneProjectOptionsEditor + Height = 374 + Width = 618 + ClientHeight = 374 + ClientWidth = 618 + OnClick = FrameClick + DesignLeft = 355 + DesignTop = 224 + object chkisPhone: TCheckBox[0] + Left = 16 + Height = 18 + Top = 16 + Width = 199 + Caption = 'is iPhone application project' + TabOrder = 0 + end + object lblAppID: TLabel[1] + Left = 16 + Height = 18 + Top = 88 + Width = 87 + Caption = 'Application ID' + ParentColor = False + end + object edtAppID: TEdit[2] + Left = 112 + Height = 22 + Top = 85 + Width = 488 + Anchors = [akTop, akLeft, akRight] + TabOrder = 1 + Text = 'com.mycompany.myapp' + end + object lblAppIDHint: TLabel[3] + Left = 16 + Height = 14 + Top = 117 + Width = 493 + Caption = 'It''s recommended by Apple to use domain-structured name, i.e. com.mycompany.myApplication as ID' + Font.Height = -10 + ParentColor = False + ParentFont = False + end + object lblSDKVer: TLabel[4] + Left = 16 + Height = 18 + Top = 51 + Width = 80 + Caption = 'SDK version:' + ParentColor = False + end + object cmbSDKs: TComboBox[5] + Left = 112 + Height = 20 + Top = 48 + Width = 184 + ItemHeight = 0 + ItemIndex = 0 + Items.Strings = ( + 'iPhone 2.0' + 'iPhone 2.1' + 'iPhone 2.2' + 'iPhone 2.2.1' + 'iPhone 3.0' + 'iPhone 3.1' + 'iPhone 3.1.2' + ) + OnChange = cmbSDKsChange + Style = csDropDownList + TabOrder = 2 + Text = 'iPhone 2.0' + end + object btnCheckSDK: TButton[6] + Left = 312 + Height = 20 + Top = 48 + Width = 158 + AutoSize = True + Caption = 'Check available SDKs' + TabOrder = 3 + end + object lblXibFiles: TLabel[7] + Left = 16 + Height = 18 + Top = 168 + Width = 219 + Caption = '.XIB files might appear here soon...' + ParentColor = False + end +end diff --git a/components/iphonelazext/project_iphone_options.lrs b/components/iphonelazext/project_iphone_options.lrs new file mode 100644 index 000000000..9dd87cbaf --- /dev/null +++ b/components/iphonelazext/project_iphone_options.lrs @@ -0,0 +1,29 @@ +{ This is an automatically generated lazarus resource file } + +LazarusResources.Add('TiPhoneProjectOptionsEditor','FORMDATA',[ + 'TPF0'#241#27'TiPhoneProjectOptionsEditor'#26'iPhoneProjectOptionsEditor'#6'H' + +'eight'#3'v'#1#5'Width'#3'j'#2#12'ClientHeight'#3'v'#1#11'ClientWidth'#3'j'#2 + +#7'OnClick'#7#10'FrameClick'#10'DesignLeft'#3'c'#1#9'DesignTop'#3#224#0#0#242 + +#2#0#9'TCheckBox'#10'chkisPhone'#4'Left'#2#16#6'Height'#2#18#3'Top'#2#16#5'W' + +'idth'#3#199#0#7'Caption'#6#29'is iPhone application project'#8'TabOrder'#2#0 + +#0#0#242#2#1#6'TLabel'#8'lblAppID'#4'Left'#2#16#6'Height'#2#18#3'Top'#2'X'#5 + +'Width'#2'W'#7'Caption'#6#14'Application ID'#11'ParentColor'#8#0#0#242#2#2#5 + +'TEdit'#8'edtAppID'#4'Left'#2'p'#6'Height'#2#22#3'Top'#2'U'#5'Width'#3#232#1 + +#7'Anchors'#11#5'akTop'#6'akLeft'#7'akRight'#0#8'TabOrder'#2#1#4'Text'#6#19 + +'com.mycompany.myapp'#0#0#242#2#3#6'TLabel'#12'lblAppIDHint'#4'Left'#2#16#6 + +'Height'#2#14#3'Top'#2'u'#5'Width'#3#237#1#7'Caption'#6'_It''s recommended b' + +'y Apple to use domain-structured name, i.e. com.mycompany.myApplication as ' + +'ID'#11'Font.Height'#2#246#11'ParentColor'#8#10'ParentFont'#8#0#0#242#2#4#6 + +'TLabel'#9'lblSDKVer'#4'Left'#2#16#6'Height'#2#18#3'Top'#2'3'#5'Width'#2'P'#7 + +'Caption'#6#12'SDK version:'#11'ParentColor'#8#0#0#242#2#5#9'TComboBox'#7'cm' + +'bSDKs'#4'Left'#2'p'#6'Height'#2#20#3'Top'#2'0'#5'Width'#3#184#0#10'ItemHeig' + +'ht'#2#0#9'ItemIndex'#2#0#13'Items.Strings'#1#6#10'iPhone 2.0'#6#10'iPhone 2' + +'.1'#6#10'iPhone 2.2'#6#12'iPhone 2.2.1'#6#10'iPhone 3.0'#6#10'iPhone 3.1'#6 + +#12'iPhone 3.1.2'#0#8'OnChange'#7#13'cmbSDKsChange'#5'Style'#7#14'csDropDown' + +'List'#8'TabOrder'#2#2#4'Text'#6#10'iPhone 2.0'#0#0#242#2#6#7'TButton'#11'bt' + +'nCheckSDK'#4'Left'#3'8'#1#6'Height'#2#20#3'Top'#2'0'#5'Width'#3#158#0#8'Aut' + +'oSize'#9#7'Caption'#6#20'Check available SDKs'#8'TabOrder'#2#3#0#0#242#2#7#6 + +'TLabel'#11'lblXibFiles'#4'Left'#2#16#6'Height'#2#18#3'Top'#3#168#0#5'Width' + +#3#219#0#7'Caption'#6'$.XIB files might appear here soon...'#11'ParentColor' + +#8#0#0#0 +]); diff --git a/components/iphonelazext/project_iphone_options.pas b/components/iphonelazext/project_iphone_options.pas new file mode 100644 index 000000000..e3dba44a5 --- /dev/null +++ b/components/iphonelazext/project_iphone_options.pas @@ -0,0 +1,131 @@ +{ + ***************************************************************************** + * * + * This file is part of the iPhone Laz Extension * + * * + * See the file COPYING.modifiedLGPL.txt, included in this distribution, * + * for details about the copyright. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * + * * + ***************************************************************************** +} +unit project_iphone_options; + +{$mode objfpc}{$H+} + +interface + +uses + Classes, SysUtils, FileUtil, LResources, Forms, StdCtrls, + IDEOPtionsIntf, ProjectIntf, + iPhoneExtStr, iPhoneExtOptions; + +type + + { TiPhoneProjectOptionsEditor } + + TiPhoneProjectOptionsEditor = class(TAbstractIDEOptionsEditor) + btnCheckSDK: TButton; + chkisPhone: TCheckBox; + cmbSDKs: TComboBox; + edtAppID: TEdit; + lblXibFiles: TLabel; + lblAppID: TLabel; + lblAppIDHint: TLabel; + lblSDKVer: TLabel; + procedure cmbSDKsChange(Sender: TObject); + procedure FrameClick(Sender: TObject); + private + { private declarations } + public + { public declarations } + class function SupportedOptionsClass: TAbstractIDEOptionsClass; override; + function GetTitle: String; override; + procedure Setup(ADialog: TAbstractOptionsEditorDialog); override; + procedure ReadSettings(AOptions: TAbstractIDEOptions); override; + procedure WriteSettings(AOptions: TAbstractIDEOptions); override; + end; + + +procedure EnableiPhoneAppProject(project: TLazProject); + +implementation + +procedure EnableiPhoneAppProject(project: TLazProject); +begin + +end; + +{ TiPhoneProjectOptionsEditor } + +procedure TiPhoneProjectOptionsEditor.cmbSDKsChange(Sender: TObject); +begin + +end; + +procedure TiPhoneProjectOptionsEditor.FrameClick(Sender: TObject); +begin + +end; + +function TiPhoneProjectOptionsEditor.GetTitle: String; +begin + Result:=strPrjOptTitle; +end; + +procedure TiPhoneProjectOptionsEditor.Setup(ADialog: TAbstractOptionsEditorDialog); +begin + chkisPhone.Caption := strPrjOptIsiPhone; + lblSDKVer.Caption := strPrjOptSDKver; + btnCheckSDK.Caption := strPrjOptCheckSDK; + lblAppID.Caption := strPtrOptAppID; + lblAppIDHint.Caption := strPtrOptAppIDHint; +end; + +procedure TiPhoneProjectOptionsEditor.ReadSettings(AOptions: TAbstractIDEOptions); +var + i : Integer; +begin + + with TiPhoneProjectOptions(AOptions) do + begin + Load; + chkisPhone.Checked:=isIPhoneApp; + + i:=cmbSDKs.Items.IndexOf(SDK); + if (i<0) and (cmbSDKs.Items.Count>0) then i:=0; + cmbSDKs.ItemIndex:=i; + + edtAppID.Text:=AppID; + end; + +end; + +procedure TiPhoneProjectOptionsEditor.WriteSettings(AOptions: TAbstractIDEOptions); +begin + with TiPhoneProjectOptions(AOptions) do + begin + isIPhoneApp:=chkisPhone.Checked; + SDK:=cmbSDKs.Caption; + AppID:=edtAppID.Text; + Save; + end; +end; + +class function TiPhoneProjectOptionsEditor.SupportedOptionsClass: TAbstractIDEOptionsClass; +begin + Result:=TiPhoneProjectOptions; +end; + +const + iPhoneOptions = 1; + +initialization + {$I project_iphone_options.lrs} + RegisterIDEOptionsEditor(iPhonePrjGroup, TiPhoneProjectOptionsEditor, iPhoneOptions); + +end. + diff --git a/components/iphonelazext/xcodeproject.pas b/components/iphonelazext/xcodeproject.pas new file mode 100644 index 000000000..649c93983 --- /dev/null +++ b/components/iphonelazext/xcodeproject.pas @@ -0,0 +1,949 @@ +{ + ***************************************************************************** + * * + * This file is part of the iPhone Laz Extension * + * * + * See the file COPYING.modifiedLGPL.txt, included in this distribution, * + * for details about the copyright. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * + * * + ***************************************************************************** +} +unit XCodeProject; + +{$mode objfpc}{$H+} +// todo: re-write, using RTTI +{$TYPEINFO ON} + +interface + +uses + contnrs, Classes, SysUtils, TypInfo; + +const + UTF8Mark = '// !$*UTF8*$!'; + + FileType_AppWrapper = 'wrapper.application'; + FileType_Bundle = FileType_AppWrapper; + FileType_PList = 'text.plist.xml'; + // sourceTree = BUILT_PRODUCTS_DIR + +type + TXCodeObjectID = string[24]; + + TStringValues = class(TStringList); + + { TXCodeObject } + TXCodeObject = class(TObject) + private + fOwner : TXCodeObject; + fChild : TFPList; + fValues : TStringList; + fID : TXCodeObjectID; + protected + procedure AddObject(const AName: String; AObject: TXCodeObject); + function GetChild(i: Integer): TXCodeObject; + function GetCount: Integer; + procedure Clear; + function GetValue(const AName: String): String; + procedure SetValue(const AName: String; const AValue: String); + public + class function isa: ShortString; virtual; abstract; + constructor Create; + destructor Destroy; override; + + property Child[i: integer]: TXCodeObject read GetChild; + property ChildCount: Integer read GetCount; + property Owner: TXCodeObject read fOwner; + property ID: TXCodeObjectID read fID; + + property StrValue[const AName: String]: String read GetValue write SetValue; + function GetChildObject(const AName: String): TXCodeObject; + function GetObjectArray(const AName: String): TFPObjectList; + function GetStringArray(const AName: String): TStringList; + function GetStringValues(const AName: String): TStringValues; + end; + TXCodeObjectClass = class of TXCodeObject; + + { TPBXFileReference } + + TPBXFileReference = class(TXCodeObject) + private + function GetExplicitFileType: string; + function GetPath: String; + function GetSourceTree: String; + procedure SetExplicitFileType(const AValue: string); + procedure SetPath(const AValue: String); + procedure SetSourceTree(const AValue: String); + public + class function isa: ShortString; override; + property explicitFileType: string read GetExplicitFileType write SetExplicitFileType; + property path : String read GetPath write SetPath; + property sourceTree: String read GetSourceTree write SetSourceTree; + end; +// 0A7E1DA610EAD478000D7855 /* iPhoneApp.app */ = + + {isa = PBXFileReference; + explicitFileType = wrapper.application; + includeInIndex = 0; + path = iPhoneApp.app; + sourceTree = BUILT_PRODUCTS_DIR; } + +{isa = PBXFileReference; + lastKnownFileType = text.plist.xml; + path = "iPhoneApp-Info.plist"; sourceTree = ""; } + + + + TXCodeBuildPhase = class(TXCodeObject); + + { TPBXShellScriptBuildPhase } + + TPBXShellScriptBuildPhase = class(TXCodeBuildPhase) + private + function GetBuildMask: Integer; + function GetShellPath: string; + function GetShellScript: String; + procedure SetBuildMask(const AValue: Integer); + procedure SetShellPath(const AValue: string); + procedure SetShellScript(const AValue: String); + public + constructor Create; + class function isa: ShortString; override; + property BuildActionMask : Integer read GetBuildMask write SetBuildMask; + property ShellPath : string read GetShellPath write SetShellPath; + property ShellScript : String read GetShellScript write SetShellScript; + end; + + { TPBXBuildFile } + + TPBXBuildFile = class(TXCodeObject) + public + fileRef : TPBXFileReference; + constructor Create; + destructor Destroy; override; + class function isa: ShortString; override; + end; + + { TXCBuildConfiguration } + + TXCBuildConfiguration = class(TXCodeObject) + private + function GetName: String; + procedure SetName(const AValue: String); + function GetBuildSettings: TStringValues; + public + class function isa: ShortString; override; + constructor Create; + destructor Destroy; override; + property name: String read GetName write SetName; + property Settings: TStringValues read GetBuildSettings; + end; + + { TXCConfigurationList } + + TXCConfigurationList = class(TXCodeObject) + private + fConfigs : TFPObjectList; + protected + function GetConfig(i: integer): TXCBuildConfiguration; + function GetCount: Integer; + + function GetName: string; + procedure SetName(const AValue: string); + function GetVisible: Boolean; + procedure SetVisible(AVisible: Boolean); + public + class function isa: ShortString; override; + constructor Create; + destructor Destroy; override; + function AddList: TXCBuildConfiguration; + property Config[i: Integer] : TXCBuildConfiguration read GetConfig; + property ConfigCount: Integer read GetCount; + property defaultVisible: Boolean read GetVisible write SetVisible; + property defaulConfigName: string read GetName write SetName; + end; + + + { TPBXNativeTarget } + + TPBXNativeTarget = class(TXCodeObject) + private + fConfigList : TXCConfigurationList; + fRef : TPBXFileReference; + function GetName: String; + function GetProductFile: TPBXFileReference; + function GetProductName: String; + function GetProductType: String; + procedure SetName(const AValue: String); + procedure SetProductName(const AValue: String); + procedure SetProductType(const AValue: String); + + function GetConfigList: TXCConfigurationList; + public + constructor Create; + class function isa: ShortString; override; + function AddBuildConfig: TXCBuildConfiguration; + function AddScriptPhase: TPBXShellScriptBuildPhase; + property name: String read GetName write SetName; + property productName: String read GetProductName write SetProductName; + property productType: String read GetProductType write SetProductType; + property productFile: TPBXFileReference read GetProductFile; + end; + + { TPBXProject } + + TPBXProject = class(TXCodeObject) + private + fConfigList : TXCConfigurationList; + function GetDirPath: string; + function GetRoot: String; + procedure SetDirPath(const AValue: string); + procedure SetRoot(const AValue: String); + protected + function GetConfigList: TXCConfigurationList; + function GetTargets: TFPObjectList; + + function GetVersion: String; + procedure SetVersion(const AVersion: string); + public + constructor Create; + class function isa: ShortString; override; + function AddTarget: TPBXNativeTarget; + function AddBuildConfig: TXCBuildConfiguration; + property DirPath: string read GetDirPath write SetDirPath; + property Root: String read GetRoot write SetRoot; + property compatibilityVersion: String read GetVersion write SetVersion; + end; + + { TPBXGroup } + + TPBXGroup = class(TXCodeObject) + protected + function GetPath: String; + procedure SetPath(const APath: String); + function GetSourceTree: String; + procedure SetSourceTree(const ATree: String); + public + class function isa: ShortString; override; + property Path: String read GetPath write SetPath; + property sourceTree: String read GetSourceTree write SetSourceTree; + end; + +procedure WritePBXProjFile(RootObject: TXCodeObject; Dst: TStream); +procedure WritePBXProjFile(RootObject: TXCodeObject; const FileName: String); + +implementation + +const + BoolVal : array[Boolean] of String = ('false', 'true'); + +function isQuotedStr(const s: string): Boolean; +begin + Result:=(length(s)>=2) and (s[1] = '"') and (s[length(s)]='"'); +end; + +function QuotedStr(const s: string): String; +begin + if isQuotedStr(s) then Result:=s + else Result:='"'+s+'"'; +end; + +function UnquoteStr(const s: string): String; +begin + if not isQuotedStr(s) then Result:=s + else Result:=Copy(s, 2, length(s)-2); +end; + + +function GetBoolValue(xobj: TXCodeObject; const AName: String): boolean; +begin + Result:=xobj.StrValue[AName]=BoolVal[True]; +end; + +procedure SetBoolValue(xobj: TXCodeObject; const AName: String; AValue: Boolean); +begin + xobj.StrValue[AName]:=BoolVal[AValue]; +end; + +{ TXCConfigurationList } + +function TXCConfigurationList.GetName: string; +begin + Result:=StrValue['defaultConfigurationName']; +end; + +procedure TXCConfigurationList.SetName(const AValue: string); +begin + StrValue['defaultConfigurationName']:=AValue; +end; + +function TXCConfigurationList.GetVisible: Boolean; +begin + Result:=GetBoolValue(Self, 'defaultConfigurationIsVisible'); +end; + +procedure TXCConfigurationList.SetVisible(AVisible: Boolean); +begin + SetBoolValue(Self, 'defaultConfigurationIsVisible', AVisible); +end; + +function TXCConfigurationList.GetConfig(i: integer): TXCBuildConfiguration; +begin + Result:=TXCBuildConfiguration(fConfigs[i]); +end; + +function TXCConfigurationList.GetCount: Integer; +begin + Result:=fConfigs.Count; +end; + +class function TXCConfigurationList.isa: ShortString; +begin + Result:='XCConfigurationList'; +end; + +constructor TXCConfigurationList.Create; +begin + inherited Create; + fConfigs:=GetObjectArray('buildConfigurations'); +end; + +destructor TXCConfigurationList.Destroy; +begin + inherited Destroy; +end; + +function TXCConfigurationList.AddList: TXCBuildConfiguration; +begin + Result:=TXCBuildConfiguration.Create; + AddObject('', Result); + fConfigs.Add(Result); +end; + +{ TXCBuildConfiguration } + +constructor TXCBuildConfiguration.Create; +begin + inherited Create; + GetStringValues('buildSettings'); +end; + +destructor TXCBuildConfiguration.Destroy; +begin + inherited Destroy; +end; + +function TXCBuildConfiguration.GetName: String; +begin + Result:=GetValue('name'); +end; + +procedure TXCBuildConfiguration.SetName(const AValue: String); +begin + SetValue('name', AValue); +end; + +function TXCBuildConfiguration.getBuildSettings: TStringValues; +begin + Result:=GetStringValues('buildSettings'); +end; + +class function TXCBuildConfiguration.isa: ShortString; +begin + Result:='XCBuildConfiguration'; +end; + +{ TPBXGroup } + +function TPBXGroup.GetPath: String; +begin + Result:=StrValue['Path']; +end; + +procedure TPBXGroup.SetPath(const APath: String); +begin + StrValue['Path']:=APath; +end; + +function TPBXGroup.GetSourceTree: String; +begin + Result:=StrValue['sourceTree']; +end; + +procedure TPBXGroup.SetSourceTree(const ATree: String); +begin + StrValue['sourceTree']:=ATree; +end; + +class function TPBXGroup.isa: ShortString; +begin + Result:='PBXGroup'; +end; + +{ TPBXFileReference } + +class function TPBXFileReference.isa: ShortString; +begin + Result:='PBXFileReference'; +end; + +function TPBXFileReference.GetExplicitFileType: string; +begin + Result:=GetValue('explicitFileType'); +end; + +function TPBXFileReference.GetPath: String; +begin + Result:=GetValue('path'); +end; + +function TPBXFileReference.GetSourceTree: String; +begin + Result:=GetValue('sourceTree'); +end; + +procedure TPBXFileReference.SetExplicitFileType(const AValue: string); +begin + SetValue('explicitFileType', AValue); +end; + +procedure TPBXFileReference.SetPath(const AValue: String); +begin + SetValue('path', AValue); +end; + +procedure TPBXFileReference.SetSourceTree(const AValue: String); +begin + SetValue('sourceTree', AValue); +end; + +{ TPBXBuildFile } + +constructor TPBXBuildFile.Create; +begin + fileRef:=TPBXFileReference.Create; +end; + +destructor TPBXBuildFile.Destroy; +begin + inherited Destroy; +end; + +class function TPBXBuildFile.isa: ShortString; +begin + Result:='PBXBuildFile'; +end; + +{ TXCodeObject } + +procedure TXCodeObject.AddObject(const AName: String; AObject: TXCodeObject); +begin + AObject.fOwner:=Self; + fChild.Add(AObject); + if AName<>'' then + fValues.AddObject(AName, AObject); +end; + +function TXCodeObject.GetChild(i: Integer): TXCodeObject; +begin + Result:=TXCodeObject(fChild[i]); +end; + +function TXCodeObject.GetCount: Integer; +begin + Result:=fChild.Count; +end; + +procedure TXCodeObject.Clear; +var + i : integer; +begin + for i:=0 to fValues.Count-1 do begin + if Assigned(fValues.Objects[i]) and not (fValues.Objects[i] is TXCodeObject) then + fValues.Objects[i].Free; + end; + fValues.Clear; + + for i:=0 to fChild.Count-1 do + TObject(fChild[i]).Free; + fChild.Clear; +end; + +function TXCodeObject.GetValue(const AName: String): String; +begin + Result:=fValues.Values[AName]; +end; + +procedure TXCodeObject.SetValue(const AName, AValue: String); +begin + fValues.Values[AName]:=Avalue; +end; + +constructor TXCodeObject.Create; +var + q : Qword; +begin + inherited; + fChild:=TFPList.Create; + fValues:=TStringList.Create; + Q:=PtrUInt(Self); + fID:=hexStr(q, 24); +end; + +destructor TXCodeObject.Destroy; +begin + Clear; + fChild.Free; + fValues.Free; + inherited Destroy; +end; + +function TXCodeObject.GetChildObject(const AName: String): TXCodeObject; +var + i : integer; +begin + Result:=nil; + i:=fValues.IndexOfName(AName); + if i>=0 then + if fValues.Objects[i] is TXCodeObject then + Result:=TXCodeObject(fValues.Objects[i]); +end; + +function TXCodeObject.GetObjectArray(const AName: String): TFPObjectList; +var + i : integer; +begin + i:=fValues.IndexOfName(AName); + if i<0 then begin + Result:=TFPObjectList.Create(false); + fValues.AddObject(AName, Result); + end else begin + if not (fValues.Objects[i] is TFPObjectList) then + Result:=nil + else + Result:= TFPObjectList(fValues.Objects[i]); + end; +end; + +function TXCodeObject.GetStringArray(const AName: String): TStringList; +var + i : integer; +begin + i:=fValues.IndexOfName(AName); + if i<0 then begin + Result:=TStringList.Create; + fValues.AddObject(AName, Result); + end else begin + if not (fValues.Objects[i] is TStringList) then + Result:=nil + else + Result:= TStringList(fValues.Objects[i]); + end; +end; + +function TXCodeObject.GetStringValues(const AName: String): TStringValues; +var + i : integer; +begin + i:=fValues.IndexOfName(AName); + if i<0 then begin + Result:=TStringValues.Create; + fValues.Values[AName]:='?'; + i:=fValues.IndexOfName(AName); + fValues.Objects[i]:=Result; + //i:=fValues.AddObject(AName + ' = ', Result); + end else begin + if not (fValues.Objects[i] is TStringValues) then + Result:=nil + else + Result:= TStringValues(fValues.Objects[i]); + end; +end; + +type + + { TPBXWriter } + + TPBXWriter = class(TObject) + private + fDst : TStream; + fPrefix : String; + fIndent : String; + protected + procedure WriteItems(data:TObject;arg:pointer); + public + constructor Create(Dst: TStream); + destructor Destroy; override; + procedure Indent; + procedure Unindent; + procedure WriteLn(const utf8: AnsiString); + end; + +{ TPBXWriter } + +procedure TPBXWriter.WriteItems(data: TObject; arg: pointer); +var + list : TFPList; + i : Integer; + j : Integer; + k : Integer; + xobj : TXCodeObject; + vobj : TObject; + sublist : TFPObjectList; + st : TStringList; + prefix : String; +begin + list:=TFPList(data); + if not Assigned(list) or (list.Count=0) then Exit; + + for i:=0 to list.Count-1 do begin + xobj:=TXCodeObject(list[i]); + + Self.WriteLn( xobj.ID + ' = {'); + Self.Indent; + + Self.Writeln('isa = ' + xobj.isa+';'); + + for j:=0 to xobj.fValues.Count - 1 do begin + vobj:=xobj.fValues.Objects[j]; + if not Assigned(vobj) then begin + prefix:=xobj.fValues.Names[j] + ' = '; + Writeln(prefix + xobj.fValues.ValueFromIndex[j]+';') + end else begin + if vobj is TXCodeObject then begin + prefix:=xobj.fValues[j] + ' = '; + Writeln(prefix + TXCodeObject(vobj).ID+';') + end else if vobj is TStringValues then begin + st:=TStringValues(vobj); + prefix:=xobj.fValues.Names[j]+' = '; + Self.Writeln(prefix + ' {'); + Self.Indent; + for k:=0 to st.Count-1 do + Self.WritelN(st.Names[k] + ' = ' + st.ValueFromIndex[k] + ';' ); + Self.Unindent; + Self.Writeln('};') + end else if vobj is TStringList then begin + prefix:=xobj.fValues[j] + ' = '; + st:=TStringValues(vobj); + Self.Writeln(prefix + ' ('); + Self.Indent; + for k:=0 to st.Count-1 do + Self.WritelN(' ' + st[k] + ',' ); + Self.Unindent; + Self.Writeln(');') + end else if vobj is TFPObjectList then begin + prefix:=xobj.fValues[j] + ' = '; + sublist:=TFPObjectList(vobj); + Self.Writeln(prefix + '('); + Self.Indent; + for k:=0 to sublist.Count-1 do + Self.WritelN(TXCodeObject(sublist[k]).ID+','); + Self.Unindent; + Self.Writeln(');') + end; + end; + end; + + Self.Unindent; + Self.WritelN('};'); + end; +end; + +constructor TPBXWriter.Create(Dst: TStream); +begin + inherited Create; + fDst:=Dst; + fIndent:=#9; +end; + +destructor TPBXWriter.Destroy; +begin + inherited Destroy; +end; + +procedure TPBXWriter.Indent; +begin + fPrefix:=fPrefix+fIndent; +end; + +procedure TPBXWriter.Unindent; +begin + fPrefix:=Copy(fPrefix,1, length(fPrefix)-length(fIndent)); +end; + +procedure TPBXWriter.WriteLn(const utf8: AnsiString); +const + LF : string = #10; +begin + if utf8='' then Exit; + if fPrefix<>'' then fDst.Write(fPrefix[1], length(fPrefix)); + fDst.Write(utf8[1], length(utf8)); + fDst.Write(LF[1], length(LF)); +end; + +procedure WritePBXProjFile(RootObject: TXCodeObject; Dst: TStream); +var + lists : TFPHashObjectList; // list of TFPList + items : TFPList; + p : TXCodeObject; + stack : TFPList; + i : Integer; + xobjcount : Integer; + wr : TPBXWriter; +begin + if RootObject=nil then Exit; + + lists:=TFPHashObjectList.Create(True); + + stack:=TFPList.Create; + xobjcount:=1; + stack.Add(RootObject); + while stack.Count > 0 do begin + p:= TXCodeObject(stack[0]); + items:=TFPList(lists.Find(p.isa)); + if not Assigned(items) then begin + items:=TFPList.Create; + lists.Add(p.isa, items); + end; + items.Add(p); + inc(xobjcount, p.ChildCount); + for i:=0 to p.ChildCount-1 do stack.Add(p.Child[i]); + stack.Delete(0); + end; + stack.Free; + + wr:=TPBXWriter.Create(Dst); + wr.WriteLn(UTF8Mark); + wr.WriteLn('{'); + wr.Indent; + wr.WriteLn('archiveVersion = 1;'); + wr.WriteLn('classes = {};'); + wr.WriteLn('objectVersion = 45;'); + wr.WriteLn('objects = { '); + wr.Indent; + lists.ForEachCall(@wr.WriteItems, nil); + wr.Unindent; + wr.WriteLn('};'); + wr.WriteLn('rootObject = '+RootObject.ID+';'); + wr.Unindent; + wr.WriteLn('}'); + wr.Free; + + lists.Free; +end; + +procedure WritePBXProjFile(RootObject: TXCodeObject; const FileName: String); +var + fs : TFileStream; +begin + fs:=TFileStream.Create(FileName, fmCreate); + WritePBXProjFile(RootOBject, fs); + fs.Free; +end; + +{ TXCProject } + +function TPBXProject.GetDirPath: string; +begin + Result:=UnquoteStr(StrValue['projectDirPath']); +end; + +function TPBXProject.GetRoot: String; +begin + Result:=UnquoteStr(StrValue['projectRoot']); +end; + +procedure TPBXProject.SetDirPath(const AValue: string); +begin + StrValue['projectDirPath']:=QuotedStr(AValue); +end; + +procedure TPBXProject.SetRoot(const AValue: String); +begin + StrValue['projectRoot']:=QuotedStr(AValue); +end; + +function TPBXProject.GetConfigList: TXCConfigurationList; +begin + if not Assigned(fConfigList) then begin + fConfigList:=TXCConfigurationList.Create; + AddObject('buildConfigurationList', fConfigList); + end; + Result:=fConfigList; +end; + +function TPBXProject.GetTargets: TFPObjectList; +begin + Result:=GetObjectArray('targets'); +end; + +function TPBXProject.GetVersion: String; +begin + Result:=GetValue('compatibilityVersion'); +end; + +procedure TPBXProject.SetVersion(const AVersion: string); +begin + SetValue('compatibilityVersion', AVersion); +end; + +constructor TPBXProject.Create; +begin + inherited Create; + Root:=''; + DirPath:=''; +end; + +class function TPBXProject.isa: ShortString; +begin + Result:='PBXProject'; +end; + +function TPBXProject.AddTarget: TPBXNativeTarget; +var + list : TFPObjectList; +begin + // adding a target forces configuration list + //if fConfigList=nil then GetConfigList; + + list:=GetObjectArray('targets'); + Result:=TPBXNativeTarget.Create; + AddObject('', Result); + list.Add(Result); +end; + +function TPBXProject.AddBuildConfig: TXCBuildConfiguration; +begin + Result:=GetConfigList.AddList; +end; + +{ TPBXNativeTarget } + +function TPBXNativeTarget.GetName: String; +begin + Result:=GetValue('name'); +end; + +function TPBXNativeTarget.GetProductFile: TPBXFileReference; +begin + if not Assigned(fRef) then begin + fRef:=TPBXFileReference.Create; + AddObject('productReference', fRef); + end; + Result:=fRef; +end; + +procedure TPBXNativeTarget.SetName(const AValue: String); +begin + SetValue('name', AValue); +end; + +function TPBXNativeTarget.GetProductName: String; +begin + Result:=GetValue('productName'); +end; + +procedure TPBXNativeTarget.SetProductName(const AValue: String); +begin + SetValue('productName', AValue); +end; + +function TPBXNativeTarget.GetProductType: String; +begin + Result:=GetValue('productType'); +end; + +procedure TPBXNativeTarget.SetProductType(const AValue: String); +begin + SetValue('productType', AValue) +end; + +function TPBXNativeTarget.GetConfigList: TXCConfigurationList; +begin + if not Assigned(fConfigList) then begin + fConfigList:=TXCConfigurationList.Create; + AddObject('buildConfigurationList', fConfigList); + end; + Result:=fConfigList; +end; + +constructor TPBXNativeTarget.Create; +begin + inherited Create; + productType := '"com.apple.product-type.application"'; + GetObjectArray('buildPhases'); + GetObjectArray('buildRules'); + GetObjectArray('dependencies'); +end; + +class function TPBXNativeTarget.isa: ShortString; +begin + Result:='PBXNativeTarget'; +end; + +function TPBXNativeTarget.AddBuildConfig: TXCBuildConfiguration; +begin + Result:=GetConfigList.AddList; +end; + +function TPBXNativeTarget.AddScriptPhase: TPBXShellScriptBuildPhase; +begin + Result:=TPBXShellScriptBuildPhase.Create; + AddObject('', Result); + GetObjectArray('buildPhases').Add(Result); +end; + +{ TPBXShellScriptBuildPhase } + +function TPBXShellScriptBuildPhase.GetBuildMask: Integer; +begin + Result:=StrToIntDef( GetValue('buildActionMask'), 0); +end; + +procedure TPBXShellScriptBuildPhase.SetBuildMask(const AValue: Integer); +begin + SetValue('buildActionMask', IntToStr(AValue)); +end; + +function TPBXShellScriptBuildPhase.GetShellPath: string; +begin + Result:=GetValue('shellPath'); +end; + +procedure TPBXShellScriptBuildPhase.SetShellPath(const AValue: string); +begin + SetValue('shellPath', AValue); +end; + +function TPBXShellScriptBuildPhase.GetShellScript: String; +begin + Result:=UnquoteStr(GetValue('shellScript')); +end; + +procedure TPBXShellScriptBuildPhase.SetShellScript(const AValue: String); +begin + SetValue('shellScript', QuotedStr(AValue)); +end; + +constructor TPBXShellScriptBuildPhase.Create; +begin + inherited Create; + ShellPath:='/bin/sh'; + GetObjectArray('files'); + GetObjectArray('inputPaths'); + GetObjectArray('outputPaths'); +end; + +class function TPBXShellScriptBuildPhase.isa: ShortString; +begin + Result:='PBXShellScriptBuildPhase'; +end; + +end. + + + diff --git a/components/iphonelazext/xcodetemplate.pas b/components/iphonelazext/xcodetemplate.pas new file mode 100644 index 000000000..3c642d861 --- /dev/null +++ b/components/iphonelazext/xcodetemplate.pas @@ -0,0 +1,287 @@ +{ + ***************************************************************************** + * * + * This file is part of the iPhone Laz Extension * + * * + * See the file COPYING.modifiedLGPL.txt, included in this distribution, * + * for details about the copyright. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * + * * + ***************************************************************************** +} +unit xcodetemplate; + +{$mode objfpc}{$H+} + +interface + +uses + Classes, SysUtils, contnrs; + +procedure PrepareTemplateFile(Src, TemplateValues, BuildSettings: TStrings); + +const + XCodeProjectTemplateIconID : AnsiString ='0AE3FFA610F3C9AF00A9B007,'; + XCodeProjectTemplateIcon : AnsiString = + '0AE3FFA610F3C9AF00A9B007 /* Icon.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = Icon.png; sourceTree = ""; };'; + + XCodeIconFile : AnsiString = '0A2C67AE10F3CFB800F48811,'; + XCodeIconFileRef : AnsiString = + '0A2C67AE10F3CFB800F48811 /* Icon.png in Resources */ = {isa = PBXBuildFile; fileRef = 0AE3FFA610F3C9AF00A9B007 /* Icon.png */; };'; + + + XCodeProjectTemplate : AnsiString = + '// !$*UTF8*$!'#10+ + '{'#10+ + ' archiveVersion = 1;'#10+ + ' classes = {'#10+ + ' };'#10+ + ' objectVersion = 45;'#10+ + ' objects = {'#10+ + ' '#10+ + ' ??iconfileref'#10+ + ' '#10+ + '/* Begin PBXFileReference section */'#10+ + ' 0A85A8AE10F0D28700AB8400 /* ??bundle */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = ??bundle; sourceTree = BUILT_PRODUCTS_DIR; };'#10+ + ' 0A85A8B110F0D28700AB8400 /* ??plist */ = {isa = PBXFileReference; explicitFileType = text.plist.xml; name = ??plist; path = ??plist; sourceTree = SOURCE_ROOT; };'#10+ + ' ??icon'#10+ + '/* End PBXFileReference section */'#10+ + ''#10+ + '/* Begin PBXGroup section */'#10+ + ' 0A52AE8110F0D05300478C4F = {'#10+ + ' isa = PBXGroup;'#10+ + ' children = ('#10+ + ' ??iconid'#10+ + ' 0A85A8AF10F0D28700AB8400 /* Products */,'#10+ + ' 0A85A8B110F0D28700AB8400 /* ??plist */,'#10+ + ' );'#10+ + ' sourceTree = "";'#10+ + ' };'#10+ + ' 0A85A8AF10F0D28700AB8400 /* Products */ = {'#10+ + ' isa = PBXGroup;'#10+ + ' children = ('#10+ + ' 0A85A8AE10F0D28700AB8400 /* ??bundle */,'#10+ + ' );'#10+ + ' name = Products;'#10+ + ' sourceTree = "";'#10+ + ' };'#10+ + '/* End PBXGroup section */'#10+ + ''#10+ + '/* Begin PBXNativeTarget section */'#10+ + ' 0A85A8AD10F0D28700AB8400 = {'#10+ + ' isa = PBXNativeTarget;'#10+ + ' buildConfigurationList = 0A85A8B410F0D28800AB8400 /* Build configuration list for PBXNativeTarget */;'#10+ + ' buildPhases = ('#10+ + ' 0A85A8B810F0D2D400AB8400 /* ShellScript */,'#10+ + ' 0A2C67A610F3CEFA00F48811 /* Resources */,'#10+ + ' );'#10+ + ' buildRules = ('#10+ + ' );'#10+ + ' dependencies = ('#10+ + ' );'#10+ + ' name = ??targetname;'#10+ + ' productName = ??productname;'#10+ + ' productReference = 0A85A8AE10F0D28700AB8400 /* ??bundle */;'#10+ + ' productType = "com.apple.product-type.application";'#10+ + ' };'#10+ + '/* End PBXNativeTarget section */'#10+ + ''#10+ + '/* Begin PBXProject section */'#10+ + ' 0A52AE8310F0D05300478C4F /* Project object */ = {'#10+ + ' isa = PBXProject;'#10+ + ' buildConfigurationList = 0A52AE8610F0D05300478C4F /* Build configuration list for PBXProject "project1" */;'#10+ + ' compatibilityVersion = "Xcode 3.1";'#10+ + ' hasScannedForEncodings = 0;'#10+ + ' mainGroup = 0A52AE8110F0D05300478C4F;'#10+ + ' productRefGroup = 0A85A8AF10F0D28700AB8400 /* Products */;'#10+ + ' projectDirPath = "";'#10+ + ' projectRoot = "";'#10+ + ' targets = ('#10+ + ' 0A85A8AD10F0D28700AB8400,'#10+ + ' );'#10+ + ' };'#10+ + '/* End PBXProject section */'#10+ + ''#10+ + '/* Begin PBXResourcesBuildPhase section */'#10+ + ' 0A2C67A610F3CEFA00F48811 /* Resources */ = {'#10+ + ' isa = PBXResourcesBuildPhase;'#10+ + ' buildActionMask = 2147483647;'#10+ + ' files = ('#10+ + ' ??iconfile'#10+ + ' );'#10+ + ' runOnlyForDeploymentPostprocessing = 0;'#10+ + ' };'#10+ + '/* End PBXResourcesBuildPhase section */'#10+ + ''#10+ + '/* Begin PBXShellScriptBuildPhase section */'#10+ + ' 0A85A8B810F0D2D400AB8400 /* ShellScript */ = {'#10+ + ' isa = PBXShellScriptBuildPhase;'#10+ + ' buildActionMask = 2147483647;'#10+ + ' files = ('#10+ + ' );'#10+ + ' inputPaths = ('#10+ + ' );'#10+ + ' outputPaths = ('#10+ + ' );'#10+ + ' runOnlyForDeploymentPostprocessing = 0;'#10+ + ' shellPath = /bin/sh;'#10+ + ' shellScript = "if [ x\"$ACTION\" != \"xbuild\" ]; then\n # in case running scripts during cleaning gets fixed\n exit 0\nfi\n\necho $FPC_COMPILER_PATH $FPC_COMPILER_OPTIONS $FPC_MAIN_FILE\n\n$FPC_COMPILER_PATH $FPC_COMPILER_OPTIONS $FPC_MAIN_FILE";'#10+ + ' };'#10+ + '/* End PBXShellScriptBuildPhase section */'#10+ + ''#10+ + '/* Begin XCBuildConfiguration section */'#10+ + ' 0A52AE8510F0D05300478C4F /* Release */ = {'#10+ + ' isa = XCBuildConfiguration;'#10+ + ' buildSettings = {'#10+ + ' ARCHS = "$(ARCHS_STANDARD_32_BIT)";'#10+ + ' COPY_PHASE_STRIP = YES;'#10+ + ' FPC_OUTPUT_FILE = $BUILT_PRODUCTS_DIR/$EXECUTABLE_PATH;'#10+ + ' FPC_COMPILER_OPTIONS = "-Parm -o$FPC_OUTPUT_FILE $FPC_CUSTOM_OPTIONS";'#10+ + ' FPC_COMPILER_PATH = ;'#10+ + ' FPC_CUSTOM_OPTIONS = ;'#10+ + ' FPC_MAIN_FILE = ;'#10+ + ' SDKROOT = iphoneos2.0;'#10+ + ' VALID_ARCHS = "armv6 armv7";'#10+ + ' };'#10+ + ' name = Release;'#10+ + ' };'#10+ + ' 0A85A8B310F0D28800AB8400 /* Release */ = {'#10+ + ' isa = XCBuildConfiguration;'#10+ + ' buildSettings = {'#10+ + ' ALWAYS_SEARCH_USER_PATHS = YES;'#10+ + ' COPY_PHASE_STRIP = YES;'#10+ + ' DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";'#10+ + ' GCC_ENABLE_FIX_AND_CONTINUE = NO;'#10+ + ' GCC_PRECOMPILE_PREFIX_HEADER = YES;'#10+ + ' GCC_PREFIX_HEADER = "$(SYSTEM_LIBRARY_DIR)/Frameworks/UIKit.framework/Headers/UIKit.h";'#10+ + ' INFOPLIST_FILE = ??plist;'#10+ + ' INSTALL_PATH = "$(HOME)/Applications";'#10+ + ' OTHER_LDFLAGS = ('#10+ + ' "-framework",'#10+ + ' Foundation,'#10+ + ' "-framework",'#10+ + ' UIKit,'#10+ + ' );'#10+ + ' PREBINDING = NO;'#10+ + ' PRODUCT_NAME = ??productname;'#10+ + ' SDKROOT = iphoneos2.0;'#10+ + ' ZERO_LINK = NO;'#10+ + ' };'#10+ + ' name = Release;'#10+ + ' };'#10+ + '/* End XCBuildConfiguration section */'#10+ + ' '#10+ + '/* Begin XCConfigurationList section */'#10+ + ' 0A52AE8610F0D05300478C4F /* Build configuration list for PBXProject "project1" */ = {'#10+ + ' isa = XCConfigurationList;'#10+ + ' buildConfigurations = ('#10+ + ' 0A52AE8510F0D05300478C4F /* Release */,'#10+ + ' );'#10+ + ' defaultConfigurationIsVisible = 0;'#10+ + ' defaultConfigurationName = Release;'#10+ + ' };'#10+ + ' 0A85A8B410F0D28800AB8400 /* Build configuration list for PBXNativeTarget */ = {'#10+ + ' isa = XCConfigurationList;'#10+ + ' buildConfigurations = ('#10+ + ' 0A85A8B310F0D28800AB8400 /* Release */,'#10+ + ' );'#10+ + ' defaultConfigurationIsVisible = 0;'#10+ + ' defaultConfigurationName = Release;'#10+ + ' };'#10+ + '/* End XCConfigurationList section */'#10+ + ' };'#10+ + ' rootObject = 0A52AE8310F0D05300478C4F /* Project object */;'#10+ + '}'#10; + +implementation + +function GetValueName(const Source: String; idx: Integer): String; +var + i : integer; +const + //todo: expand symbols charset + Symbols: set of char = [#9, #32, #10,#13, + '=',':',';','-','+','*','/','\','!','@','#', + '$','%','^','&','(',')','~','`','''','"' ]; +begin + for i:=idx to length(Source) do + if Source[i] in Symbols then begin + Result:=Copy(Source, idx, i-idx); + Exit; + end; + Result:=Copy(Source, idx, length(Source)-idx+1); +end; + +function ChangeValues(const Prefix, Source: String; Values: TStrings): String; +var + i : integer; + nm : string; + v : string; +begin + Result:=Source; + i:=Pos(Prefix, Result); + while i>0 do begin + nm:=GetValueName(Result, i+length(Prefix)); + Delete(Result, i, length(Prefix)+length(nm)); + v:=Values.Values[nm]; + + if Pos(Prefix, v) <= 0 then // don't allow circular prefix used, to avoid infinite loops + Insert(v, Result, i); + i:=Pos(Prefix, Result); + end; +end; + +procedure PrepareTemplateFile(Src, TemplateValues, BuildSettings: TStrings); +//todo: Better code to update XCode project file! +var + i, j : Integer; + nm, s, v : String; + isSettings : Boolean; + buildhash : TFPStringHashTable; + +begin + if not Assigned(Src) then Exit; + + if Assigned(TemplateValues) then + for i:=0 to Src.Count-1 do + Src[i]:=ChangeValues('??', Src[i], TemplateValues); + + isSettings:=false; + + if Assigned(BuildSettings) and (BuildSettings.Count>0) then begin + buildhash := TFPStringHashTable.Create; + + for i :=0 to BuildSettings.Count-1 do + buildhash.Add(BuildSettings.Names[i], BuildSettings.ValueFromIndex[i]); + + for i:=0 to Src.Count-1 do begin + if not isSettings then + isSettings:=Pos('buildSettings', Src[i])>0 + else begin + if Trim(Src[i])='};' then + isSettings:=false + else begin + j:=1; + s:=Src[i]; + while (j<=length(s)) and (s[j] in [#9, #32]) do + inc(j); + + nm:=GetValueName(s, j); + + if Assigned(buildhash.Find(nm)) then begin + v:=buildhash.Items[nm]; + Src[i]:=Copy(Src[i], 1, j-1)+nm+ ' = ' + v + ';'; + end; + end; + end; {of else} + end; + buildhash.Free; + end; +end; + + +end. +