iphonelazext: update in Xcode project generation, using pbx files rather than a text template. Updates project options dialog to compare changes and mark project as modified, if necessary

git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@4404 8e941d3f-bd1b-0410-a28a-d453659cc2b4
This commit is contained in:
skalogryz
2015-12-20 03:54:35 +00:00
parent a8dc2255dc
commit f8f909c0c0
13 changed files with 590 additions and 163 deletions

View File

@ -51,7 +51,6 @@ type
constructor Create; constructor Create;
procedure UpdateXcode(Sender: TObject); procedure UpdateXcode(Sender: TObject);
procedure SimRun(Sender: TObject); procedure SimRun(Sender: TObject);
//procedure isProjectClicked(Sender: TObject);
end; end;
var var
@ -349,6 +348,7 @@ var
projname : string; projname : string;
ext : string; ext : string;
exename : string;
tname : string; tname : string;
plistname : string; plistname : string;
@ -358,7 +358,6 @@ var
opt : string; opt : string;
optSim: string; optSim: string;
begin begin
FillBunldeInfo(false, Info);
// the create .plist would be used by XCode project // the create .plist would be used by XCode project
// the simulator .plist in created with InstallAppToSim. // the simulator .plist in created with InstallAppToSim.
// they differ with SDKs used // they differ with SDKs used
@ -367,43 +366,32 @@ begin
tname:=ExtractFileName( LazarusIDE.ActiveProject.MainFile.Filename); tname:=ExtractFileName( LazarusIDE.ActiveProject.MainFile.Filename);
tname:=ChangeFileExt(tname, ''); tname:=ChangeFileExt(tname, '');
plistname:=tname+'.plist'; plistname:='info.plist';
build.Add('INFOPLIST_FILE','"'+plistname+'"'); build.Add('INFOPLIST_FILE','"'+plistname+'"');
build.Add('PRODUCT_NAME','"'+tname+'"'); build.Add('PRODUCT_NAME','"'+tname+'"');
build.Add('SDKROOT',EnvOptions.GetSDKName(ProjOptions.SDK, false)); build.Add('SDKROOT',EnvOptions.GetSDKName(ProjOptions.SDK, false));
build.Add('FPC_COMPILER_PATH','"'+EnvOptions.CompilerPath+'"'); build.Add('FPC_COMPILER_PATH','"'+EnvOptions.CompilerPath+'"');
build.Add('FPC_MAIN_FILE','"'+LazarusIDE.ActiveProject.MainFile.Filename+'"'); build.Add('FPC_MAIN_FILE','"'+LazarusIDE.ActiveProject.MainFile.Filename+'"');
opt := opt:='';
' -XR'+EnvOptions.GetSDKFullPath(ProjOptions.SDK, false)+' ' +
' -FD'+IncludeTrailingPathDelimiter(EnvOptions.PlatformsBaseDir)+'iPhoneOS.platform/Developer/usr/bin ' +
EnvOptions.CommonOpt;
optSim :=
' -XR'+EnvOptions.GetSDKFullPath(ProjOptions.SDK, true)+' ' +
' -FD'+IncludeTrailingPathDelimiter(EnvOptions.PlatformsBaseDir)+'iPhoneSimulator.platform/Developer/usr/bin ' +
EnvOptions.CommonOpt;
with LazarusIDE.ActiveProject.LazCompilerOptions do begin with LazarusIDE.ActiveProject.LazCompilerOptions do begin
opt:=opt + ' ' +BreakPathsStringToOption(OtherUnitFiles, '-Fu', '\"'); opt:=opt + ' ' +BreakPathsStringToOption(OtherUnitFiles, '-Fu', '\"');
opt:=opt + ' ' +BreakPathsStringToOption(IncludePath, '-Fi', '\"'); opt:=opt + ' ' +BreakPathsStringToOption(IncludePath, '-Fi', '\"');
opt:=opt + ' ' +BreakPathsStringToOption(ObjectPath, '-Fo', '\"'); opt:=opt + ' ' +BreakPathsStringToOption(ObjectPath, '-Fo', '\"');
opt:=opt + ' ' +BreakPathsStringToOption(Libraries, '-Fl', '\"'); opt:=opt + ' ' +BreakPathsStringToOption(Libraries, '-Fl', '\"');
optSim:=optSim + ' ' +BreakPathsStringToOption(OtherUnitFiles, '-Fu', '\"');
optSim:=optSim + ' ' +BreakPathsStringToOption(IncludePath, '-Fi', '\"');
optSim:=optSim + ' ' +BreakPathsStringToOption(ObjectPath, '-Fo', '\"');
optSim:=optSim + ' ' +BreakPathsStringToOption(Libraries, '-Fl', '\"');
end; end;
build.Add('FPC_CUSTOM_OPTIONS','"'+opt+'"');
build.Add('"FPC_CUSTOM_OPTIONS[sdk=iphonesimulator*]"','"'+optSim+'"');
dir:=ResolveProjectPath('xcode'); dir:=ResolveProjectPath('xcode');
dir:=dir+'/'; dir:=dir+'/';
name:=ExtractFileName(GetProjectExeName(LazarusIDE.ActiveProject)); // not using executable name. It's driven by product name
//exename:=ExtractFileName(GetProjectExeName(LazarusIDE.ActiveProject));
//name:=exename;
ForceDirectories(dir); ForceDirectories(dir);
WriteDefInfoList( dir + GetProjectPlistName(LazarusIDE.ActiveProject),
name, name, Info); FillBunldeInfo(false, Info);
WriteDefInfoList( dir + plistname, tname, tname, Info);
projname:=ExtractFileName(LazarusIDE.ActiveProject.MainFile.Filename); projname:=ExtractFileName(LazarusIDE.ActiveProject.MainFile.Filename);
@ -417,32 +405,30 @@ begin
proj:=TStringList.Create; proj:=TStringList.Create;
templates:=nil; templates:=nil;
try try
if not FileExists(projname) then begin templates:=TStringList.Create;
templates:=TStringList.Create;
if WriteIconTo( IncludeTrailingPathDelimiter(dir)+'Icon.png') then begin if WriteIconTo( IncludeTrailingPathDelimiter(dir)+'Icon.png') then begin
templates.Values['icon']:=XCodeProjectTemplateIcon; templates.Values['icon']:=XCodeProjectTemplateIcon;
templates.Values['iconid']:=XCodeProjectTemplateIconID; templates.Values['iconid']:=XCodeProjectTemplateIconID;
templates.Values['iconfile']:=XCodeIconFile; templates.Values['iconfile']:=XCodeIconFile;
templates.Values['iconfileref']:=XCodeIconFileRef; templates.Values['iconfileref']:=XCodeIconFileRef;
end else begin end else begin
templates.Values['icon']:=''; templates.Values['icon']:='';
templates.Values['iconid']:=''; templates.Values['iconid']:='';
templates.Values['iconfile']:=''; templates.Values['iconfile']:='';
templates.Values['iconfileref']:=''; templates.Values['iconfileref']:='';
end; end;
//todo: //todo:
templates.Values['bundle']:=tname+'.app'; templates.Values['bundle']:=tname+'.app';
templates.Values['plist']:=plistname; templates.Values['plist']:=plistname;
templates.Values['targetname']:=tname; templates.Values['targetname']:=tname; // Target and Product name must match
templates.Values['productname']:=tname; templates.Values['productname']:=tname;
templates.Values['mainfile']:=LazarusIDE.ActiveProject.MainFile.Filename;
templates.Values['projoptions']:=opt;
proj.Text:=XCodeProjectTemplate; // Xcode project updated (/Users/dmitry/FPC_Laz/iphonelazext/tests/xcode/test_xcodetemplate.xcodeproj)
end else PrepareTemplateFile(proj, templates);
proj.LoadFromFile(projname);
PrepareTemplateFile(proj, templates, build);
proj.SaveToFile(projname); proj.SaveToFile(projname);
except except
on e: exception do on e: exception do
@ -453,7 +439,7 @@ begin
templates.Free; templates.Free;
build.Free; build.Free;
IDEMessagesWindow.AddMsg(strXcodeUpdated, '', 0); IDEMessagesWindow.AddMsg(Format(strXcodeUpdated,[projdir]), '', 0);
end; end;
procedure SimRunDirect; procedure SimRunDirect;
@ -496,15 +482,6 @@ begin
end; end;
end; 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;
ProjOptions.Save;
end;}
procedure Register; procedure Register;
begin begin
// IDE integration is done in constructor // IDE integration is done in constructor

View File

@ -21,6 +21,7 @@ interface
uses uses
Classes, SysUtils, IDEOptionsIntf, LazIDEIntf, ProjectIntf, MacroIntf, Classes, SysUtils, IDEOptionsIntf, LazIDEIntf, ProjectIntf, MacroIntf,
CompOptsIntf,
iPhoneBundle, XMLConf, XcodeUtils, FileUtil, iphonesimctrl; iPhoneBundle, XMLConf, XcodeUtils, FileUtil, iphonesimctrl;
const const
@ -40,8 +41,11 @@ type
fResourceDir : String; fResourceDir : String;
fExcludeMask : String; fExcludeMask : String;
fMainNib : String; fMainNib : String;
fResFiles : TStrings;
public public
//constructor Create; override;
constructor Create; constructor Create;
destructor Destroy; override;
class function GetGroupCaption: String; override; class function GetGroupCaption: String; override;
class function GetInstance: TAbstractIDEOptions; override; class function GetInstance: TAbstractIDEOptions; override;
function Load: Boolean; function Load: Boolean;
@ -54,6 +58,7 @@ type
property ResourceDir: String read fResourceDir write fResourceDir; property ResourceDir: String read fResourceDir write fResourceDir;
property ExcludeMask: String read fExcludeMask write fExcludeMask; property ExcludeMask: String read fExcludeMask write fExcludeMask;
property MainNib: String read fMainNib write fMainNib; property MainNib: String read fMainNib write fMainNib;
property ResFiles: TStrings read fResFiles;
end; end;
{ TiPhoneEnvironmentOptions } { TiPhoneEnvironmentOptions }
@ -149,6 +154,7 @@ const
optResourceDir = 'iPhone/ResourceDir'; optResourceDir = 'iPhone/ResourceDir';
optExcludeMask = 'iPhone/ExcludeMask'; optExcludeMask = 'iPhone/ExcludeMask';
optMainNib = 'iPhone/MainNib'; optMainNib = 'iPhone/MainNib';
optResFiles = 'iPhone/ResFiles';
function EnvOptions: TiPhoneEnvironmentOptions; function EnvOptions: TiPhoneEnvironmentOptions;
begin begin
@ -403,14 +409,22 @@ begin
fAppID:='com.mycompany.myapplication'; fAppID:='com.mycompany.myapplication';
fSpaceName:=''; fSpaceName:='';
DataWritten:=false; DataWritten:=false;
fResFiles.Clear;
end; end;
constructor TiPhoneProjectOptions.Create; constructor TiPhoneProjectOptions.Create;
begin begin
inherited Create; inherited Create;
fResFiles := TStringList.Create;
Reset; Reset;
end; end;
destructor TiPhoneProjectOptions.Destroy;
begin
fResFiles.Free;
inherited Destroy;
end;
class function TiPhoneProjectOptions.GetGroupCaption: String; class function TiPhoneProjectOptions.GetGroupCaption: String;
begin begin
Result:='iPhone'; Result:='iPhone';
@ -435,24 +449,42 @@ begin
else fResourceDir:=DefaultResourceDir; else fResourceDir:=DefaultResourceDir;
if CustomData.Contains(optExcludeMask) then fExcludeMask:=CustomData.Values[optExcludeMask]; if CustomData.Contains(optExcludeMask) then fExcludeMask:=CustomData.Values[optExcludeMask];
if CustomData.Contains(optMainNib) then fMainNib:=CustomData.Values[optMainNib]; if CustomData.Contains(optMainNib) then fMainNib:=CustomData.Values[optMainNib];
if CustomData.Contains(optResFiles) then ResFiles.Text:=CustomData.Values[optResFiles];
end; end;
end; end;
function TiPhoneProjectOptions.Save: Boolean; function TiPhoneProjectOptions.Save: Boolean;
const const
BoolStr : array[Boolean] of String = ('false', 'true'); BoolStr : array[Boolean] of String = ('false', 'true');
var
modflag: Boolean;
begin begin
Result:=True; Result:=True;
{do not write iPhone related info to non-iPhone projects} {do not write iPhone related info to non-iPhone projects}
if DataWritten or fisiPhone then if DataWritten or fisiPhone then
with LazarusIDE.ActiveProject do begin with LazarusIDE.ActiveProject do begin
CustomData.Values[optisIPhone] := BoolStr[fisiPhone];
CustomData.Values[optSDK]:=fSDK; modflag:=false;
CustomData.Values[optAppID]:=fAppID; modflag:=(CustomData.Values[optisIPhone] <> BoolStr[fisiPhone])
CustomData.Values[optSpaceName]:=fSpaceName; or (CustomData.Values[optSDK]<>fSDK)
CustomData.Values[optResourceDir]:=fResourceDir; or (CustomData.Values[optAppID]<>fAppID)
CustomData.Values[optExcludeMask]:=fExcludeMask; or (CustomData.Values[optSpaceName]<>fSpaceName)
CustomData.Values[optMainNib]:=fMainNib; or (CustomData.Values[optResourceDir]<>fResourceDir)
or (CustomData.Values[optExcludeMask]<>fExcludeMask)
or (CustomData.Values[optMainNib]<>fMainNib)
or (CustomData.Values[optResFiles]<>ResFiles.Text);
if modflag then begin
LazarusIDE.ActiveProject.Modified:=true;
CustomData.Values[optisIPhone] := BoolStr[fisiPhone];
CustomData.Values[optSDK]:=fSDK;
CustomData.Values[optAppID]:=fAppID;
CustomData.Values[optSpaceName]:=fSpaceName;
CustomData.Values[optResourceDir]:=fResourceDir;
CustomData.Values[optExcludeMask]:=fExcludeMask;
CustomData.Values[optMainNib]:=fMainNib;
CustomData.Values[optResFiles]:=ResFiles.Text;
end;
end; end;
end; end;

View File

@ -30,7 +30,7 @@ resourcestring
strPtrOptAppID = 'Application ID'; strPtrOptAppID = 'Application ID';
strPtrOptAppIDHint = 'It''s recommended by Apple to use domain-structured identifier i.e. "com.mycompany.myApplication"'; strPtrOptAppIDHint = 'It''s recommended by Apple to use domain-structured identifier i.e. "com.mycompany.myApplication"';
strXcodeUpdated = 'Xcode project updated'; strXcodeUpdated = 'Xcode project updated (%s)';
strWNoSDKSelected = 'Warning: SDK is not selected using %s'; strWNoSDKSelected = 'Warning: SDK is not selected using %s';
strWNoSDK = 'Warning: No SDK available. Linking might fail.'; strWNoSDK = 'Warning: No SDK available. Linking might fail.';

View File

@ -1,12 +1,13 @@
<?xml version="1.0"?> <?xml version="1.0" encoding="UTF-8"?>
<CONFIG> <CONFIG>
<Package Version="4"> <Package Version="4">
<Name Value="iphonelazext"/> <Name Value="iphonelazext"/>
<AddToProjectUsesSection Value="True"/> <Type Value="DesignTime"/>
<Author Value="Dmitry 'skalogryz' Boyarintsev"/> <Author Value="Dmitry 'skalogryz' Boyarintsev"/>
<CompilerOptions> <CompilerOptions>
<Version Value="11"/> <Version Value="11"/>
<SearchPaths> <SearchPaths>
<OtherUnitFiles Value="pbx"/>
<UnitOutputDirectory Value="lib/$(TargetCPU)-$(TargetOS)"/> <UnitOutputDirectory Value="lib/$(TargetCPU)-$(TargetOS)"/>
</SearchPaths> </SearchPaths>
<Parsing> <Parsing>
@ -16,15 +17,15 @@
</Parsing> </Parsing>
<Other> <Other>
<CompilerMessages> <CompilerMessages>
<IgnoredMessages idx4055="True" idx4079="True" idx4080="True" idx4081="True" idx5024="True" idx5057="True" idx5060="True"/> <IgnoredMessages idx5091="True" idx5060="True" idx5057="True" idx5024="True" idx4081="True" idx4080="True" idx4079="True" idx4055="True"/>
</CompilerMessages> </CompilerMessages>
<CompilerPath Value="$(CompPath)"/> <CustomOptions Value="-Sm -dPACKAGE_PATH:='$(PkgDir)'"/>
</Other> </Other>
</CompilerOptions> </CompilerOptions>
<Description Value="iPhone Development Lazarus extension"/> <Description Value="iPhone Development Lazarus extension"/>
<License Value="LGPL"/> <License Value="LGPL"/>
<Version Minor="7"/> <Version Minor="8"/>
<Files Count="14"> <Files Count="21">
<Item1> <Item1>
<Filename Value="ideext.pas"/> <Filename Value="ideext.pas"/>
<HasRegisterProc Value="True"/> <HasRegisterProc Value="True"/>
@ -68,22 +69,52 @@
</Item10> </Item10>
<Item11> <Item11>
<Filename Value="lazfilesutils.pas"/> <Filename Value="lazfilesutils.pas"/>
<UnitName Value="lazfilesutils"/> <UnitName Value="LazFilesUtils"/>
</Item11> </Item11>
<Item12> <Item12>
<Filename Value="xcodeutils.pas"/> <Filename Value="xcodeutils.pas"/>
<UnitName Value="xcodeutils"/> <UnitName Value="XcodeUtils"/>
</Item12> </Item12>
<Item13> <Item13>
<Filename Value="newxibdialog.pas"/> <Filename Value="newxibdialog.pas"/>
<UnitName Value="newxibdialog"/> <UnitName Value="newXibDialog"/>
</Item13> </Item13>
<Item14> <Item14>
<Filename Value="xibfile.pas"/> <Filename Value="xibfile.pas"/>
<UnitName Value="xibfile"/> <UnitName Value="xibfile"/>
</Item14> </Item14>
<Item15>
<Filename Value="pbx/pbxcontainer.pas"/>
<AddToUsesPkgSection Value="False"/>
<UnitName Value="pbxcontainer"/>
</Item15>
<Item16>
<Filename Value="pbx/pbxfile.pas"/>
<AddToUsesPkgSection Value="False"/>
<UnitName Value="pbxfile"/>
</Item16>
<Item17>
<Filename Value="pbx/xcodeproj.pas"/>
<AddToUsesPkgSection Value="False"/>
<UnitName Value="xcodeproj"/>
</Item17>
<Item18>
<Filename Value="plistfile.pas"/>
<UnitName Value="PlistFile"/>
</Item18>
<Item19>
<Filename Value="pbx/xcodeprojutils.pas"/>
<UnitName Value="xcodeprojutils"/>
</Item19>
<Item20>
<Filename Value="iphonesimctrl.pas"/>
<UnitName Value="iphonesimctrl"/>
</Item20>
<Item21>
<Filename Value="res/buildscript.sh"/>
<Type Value="Text"/>
</Item21>
</Files> </Files>
<Type Value="DesignTime"/>
<RequiredPkgs Count="3"> <RequiredPkgs Count="3">
<Item1> <Item1>
<PackageName Value="LCL"/> <PackageName Value="LCL"/>

View File

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

View File

@ -25,7 +25,8 @@ uses
function ResolveProjectPath(const path: string; project: TLazProject = nil): string; function ResolveProjectPath(const path: string; project: TLazProject = nil): string;
function BreakPathsStringToOption(const Paths, Switch: String; const Quotes: string = '"'; project: TLazProject = nil): String; function BreakPathsStringToOption(const Paths, Switch: String;
const Quotes: string = '"'; project: TLazProject = nil; AResolvePath: Boolean = false): String;
function RelativeToFullPath(const BasePath, Relative: string): String; function RelativeToFullPath(const BasePath, Relative: string): String;
function NeedQuotes(const path: string): Boolean; function NeedQuotes(const path: string): Boolean;
@ -152,7 +153,7 @@ begin
end; end;
end; end;
function BreakPathsStringToOption(const Paths, Switch, Quotes: String; project: TLazProject): String; function BreakPathsStringToOption(const Paths, Switch, Quotes: String; project: TLazProject; AResolvePath: Boolean): String;
var var
i, j : Integer; i, j : Integer;
fixed : String; fixed : String;
@ -168,7 +169,7 @@ begin
if Paths[i]=';' then begin if Paths[i]=';' then begin
fixed:=Trim(Copy(paths,j, i-j) ); fixed:=Trim(Copy(paths,j, i-j) );
if fixed<>'' then begin if fixed<>'' then begin
fixed:=ResolveProjectPath(fixed, project); if AResolvePath then fixed:=ResolveProjectPath(fixed, project);
Result:=Result+' ' + Switch + QuoteStrIfNeeded(fixed, quotes); Result:=Result+' ' + Switch + QuoteStrIfNeeded(fixed, quotes);
end; end;
j:=i+1; j:=i+1;
@ -176,7 +177,7 @@ begin
fixed:=Trim(Copy(paths,j, length(paths)-j+1) ); fixed:=Trim(Copy(paths,j, length(paths)-j+1) );
if fixed<>'' then begin if fixed<>'' then begin
fixed:=ResolveProjectPath(fixed, project); if AResolvePath then fixed:=ResolveProjectPath(fixed, project);
Result:=Result+' ' + Switch + QuoteStrIfNeeded(fixed, quotes); Result:=Result+' ' + Switch + QuoteStrIfNeeded(fixed, quotes);
end; end;
end; end;

View File

@ -85,10 +85,14 @@ type
TPBXKeyValue = class(TFPHashObjectList) TPBXKeyValue = class(TFPHashObjectList)
protected protected
function AddVal(const name: string; atype: TPBXValueType): TPBXValue; function AddVal(const name: string; atype: TPBXValueType): TPBXValue;
function GetValStr(const name: string): string;
procedure AddValStr(const name, avalue: string);
public public
function AddStr(const name: string; const avalue: string = ''): TPBXValue; function AddStr(const name: string; const avalue: string = ''): TPBXValue;
function AddStrArray(const name: string): TPBXValue; function AddStrArray(const name: string): TPBXValue;
function AddKeyVal(const name: string): TPBXValue; function AddKeyVal(const name: string): TPBXValue;
// it must be "public, not published"
property Str[const name: string]: string read GetValStr write AddValStr;
end; end;
TPBXFileInfo = record TPBXFileInfo = record
@ -135,10 +139,16 @@ procedure PBXWriteStrArray( w: TPBXWriter; list: TPBXStringArray );
procedure PBXWriteKeyValue( w: TPBXWriter; kv: TPBXKeyValue ); procedure PBXWriteKeyValue( w: TPBXWriter; kv: TPBXKeyValue );
procedure PBXWriteObj(pbx: PBXObject; w: TPBXWriter; WriteEmpty: TStrings); procedure PBXWriteObj(pbx: PBXObject; w: TPBXWriter; WriteEmpty: TStrings);
{ assigns reference IDs to each object in the list. "ID" is 24 charactewr long hex-string }
procedure PBXAssignRef(list: TList); procedure PBXAssignRef(list: TList);
{ Returns the list of objects that should populate the "objects" section of pbx file }
procedure PBXGatherObjects(obj: TObject; srz: TList); procedure PBXGatherObjects(obj: TObject; srz: TList);
procedure PBXKeyValsCopy(src, dst: TPBXKeyValue);
procedure PBXValueCopy(src, dst: TPBXValue);
procedure PBXStringArrayCopy(src, dst: TPBXStringArray);
implementation implementation
var var
@ -170,6 +180,48 @@ begin
Result:=True; Result:=True;
end; end;
procedure PBXStringArrayCopy(src, dst: TPBXStringArray);
begin
if not Assigned(src) or not Assigned(dst) then Exit;
src.Assign(dst);
end;
procedure PBXValueCopy(src, dst: TPBXValue);
var
dv : TPBXValue;
begin
if not Assigned(src) or not Assigned(dst) then Exit;
dst.valType:=src.valType;
case dst.valType of
vtString: dst.str:=src.str;
vtArrayOfStr: begin
if not Assigned(dst.arr) then dst.arr:=TPBXStringArray.Create;
PBXStringArrayCopy(src.arr, dst.arr);
end;
vtKeyVal: begin
if not Assigned(dst.keyval) then dst.keyval:=TPBXKeyValue.Create(true);
PBXKeyValsCopy(src.keyval, dst.keyval);
end;
end;
end;
procedure PBXKeyValsCopy(src, dst: TPBXKeyValue);
var
svl : TPBXValue;
nm : string;
i : Integer;
dvl : TPBXValue;
begin
if not Assigned(src) or not Assigned(dst) then Exit;
for i:=0 to src.Count-1 do begin
nm:=src.NameOfIndex(i);
svl:=TPBXValue(src.Items[i]);
dvl:=dst.AddVal(nm, svl.valType);
PBXValueCopy(svl, dvl);
end;
end;
procedure TestContainer(const buf: string); procedure TestContainer(const buf: string);
var var
c : TPBXContainer; c : TPBXContainer;
@ -215,6 +267,11 @@ end;
{ TPBXKeyValue } { TPBXKeyValue }
procedure TPBXKeyValue.AddValStr(const name, AValue: string);
begin
AddStr(name, Avalue);
end;
function TPBXKeyValue.AddVal(const name: string; atype: TPBXValueType): TPBXValue; function TPBXKeyValue.AddVal(const name: string; atype: TPBXValueType): TPBXValue;
begin begin
Result:=TPBXValue.Create; Result:=TPBXValue.Create;
@ -226,6 +283,16 @@ begin
Add(name, Result); Add(name, Result);
end; end;
function TPBXKeyValue.GetValStr(const name: string): string;
var
vl : TPBXValue;
begin
vl:=TPBXValue(Self.Find(name));
if not Assigned(vl)
then Result:=''
else Result:=vl.str;
end;
function TPBXKeyValue.AddStr(const name: string; const avalue: string): TPBXValue; function TPBXKeyValue.AddStr(const name: string; const avalue: string): TPBXValue;
begin begin
Result:=AddVal(name, vtString); Result:=AddVal(name, vtString);
@ -242,6 +309,7 @@ begin
Result:=AddVal(name, vtKeyVal); Result:=AddVal(name, vtKeyVal);
end; end;
{ TPBXReref } { TPBXReref }
constructor TPBXReref.Create(ainstance: TObject; const apropname, aref: string); constructor TPBXReref.Create(ainstance: TObject; const apropname, aref: string);
@ -666,8 +734,8 @@ begin
vl:=IntToStr(GetInt64Prop(pbx, p^[i])); vl:=IntToStr(GetInt64Prop(pbx, p^[i]));
isstr:=(vl<>'') or (WriteEmpty.indexOf(nm)>=0); isstr:=(vl<>'') or (WriteEmpty.indexOf(nm)>=0);
end else if p^[i].PropType.Kind = tkBool then begin end else if p^[i].PropType.Kind = tkBool then begin
vl:=IntToStr(GetOrdProp(pbx, p^[i])); isstr:=PtrUInt(p^[i].Default)<>PtrUInt(p^[i].GetProc);
isstr:=true; if isstr then vl:=IntToStr(GetOrdProp(pbx, p^[i]));
end; end;
if isstr then begin if isstr then begin

View File

@ -17,7 +17,8 @@ interface
* {} - is an object (just like json) * * {} - is an object (just like json) *
* escaping characters with C-style escaping: * * escaping characters with C-style escaping: *
* * quotes (") * * * quotes (") *
* * line breaks (note OSX is typically using \n, unline Unix \r) * * * line breaks (note OSX is typically using \n, unlike Unix \r) *
* *
* * * *
* PBXScanner - scans through the file * * PBXScanner - scans through the file *
* PBXParser - parses the file, returning a higher level entities of the file: * * PBXParser - parses the file, returning a higher level entities of the file: *
@ -177,6 +178,7 @@ var
i : Integer; i : Integer;
k : Integer; k : Integer;
begin begin
Result:='';
k:=0; k:=0;
for i:=1 to length(v) do begin for i:=1 to length(v) do begin
if not (v[i] in IdentName) then begin if not (v[i] in IdentName) then begin
@ -435,6 +437,7 @@ begin
LastComment:=''; LastComment:='';
Name:=''; Name:='';
Value:=''; Value:='';
Result:=etError;
case fState of case fState of
stInit : stInit :
case scanner.FetchToken of case scanner.FetchToken of
@ -700,6 +703,7 @@ var
lvl : Integer; lvl : Integer;
tk : TPBXEntity; tk : TPBXEntity;
begin begin
Result:=false;
if not Assigned(p) then Exit; if not Assigned(p) then Exit;
lvl:=p.Level; lvl:=p.Level;
while (p.Level>=lvl) do begin while (p.Level>=lvl) do begin

View File

@ -17,10 +17,15 @@ unit xcodeproj;
* * any objects within key-value tables are freed * * * any objects within key-value tables are freed *
* Alternative solution - implement ref counting! * * Alternative solution - implement ref counting! *
* * * *
* PBXShellScriptBuildPhase - the give script must be using MacOS line breaks *
* which are \n (#13). Using unix line breaks \r (#10) will cause issues *
* in Xcode. *
--------------------------------------------------------------------------------} --------------------------------------------------------------------------------}
interface interface
{$ifdef fpc}{$mode delphi}{$endif}
uses uses
Classes, SysUtils, Classes, SysUtils,
typinfo, pbxcontainer; typinfo, pbxcontainer;
@ -49,10 +54,16 @@ type
fdefaultConfigurationIsVisible: string; fdefaultConfigurationIsVisible: string;
fdefaultConfigurationName: string; fdefaultConfigurationName: string;
fbuildConfigurations: TPBXObjectsList; fbuildConfigurations: TPBXObjectsList;
function GetConfigItem(i: integer): XCBuildConfiguration;
function GetCount: integer;
public public
constructor Create; override; constructor Create; override;
destructor Destroy; override; destructor Destroy; override;
function addConfig(const aname: string): XCBuildConfiguration; function addConfig(const aname: string): XCBuildConfiguration;
// Count and Items are just for convenience. MUST NOT BE in "published" section
property Count: integer read GetCount;
property Items[i: integer]: XCBuildConfiguration read GetConfigItem; default;
published published
property buildConfigurations: TPBXObjectsList read fbuildConfigurations; property buildConfigurations: TPBXObjectsList read fbuildConfigurations;
property defaultConfigurationIsVisible: string read fdefaultConfigurationIsVisible write fdefaultConfigurationIsVisible; property defaultConfigurationIsVisible: string read fdefaultConfigurationIsVisible write fdefaultConfigurationIsVisible;
@ -76,17 +87,19 @@ type
{ PBXFileReference } { PBXFileReference }
// these files might not be physically present in for the project.
// i.e. a bundle .app file doesn't physically exists until it's actual "built" by the xcode
PBXFileReference = class(PBXObject) PBXFileReference = class(PBXObject)
private private
FexplicitFileType: string; FexplicitFileType: string;
FincludeInIndex: string; FincludeInIndex: Boolean;
FlastKnownFileType: string; FlastKnownFileType: string;
Fname: string; Fname: string;
Fpath: string; Fpath: string;
FsourceTree: string; FsourceTree: string;
published published
property explicitFileType: string read FexplicitFileType write FexplicitFileType; property explicitFileType: string read FexplicitFileType write FexplicitFileType;
property includeInIndex: string read FincludeInIndex write FincludeInIndex; property includeInIndex: Boolean read FincludeInIndex write FincludeInIndex;
property lastKnownFileType: string read flastKnownFileType write flastKnownFileType; property lastKnownFileType: string read flastKnownFileType write flastKnownFileType;
property name: string read Fname write Fname; property name: string read Fname write Fname;
property path: string read Fpath write Fpath; property path: string read Fpath write Fpath;
@ -135,6 +148,7 @@ type
foutputPaths: TPBXStringArray; foutputPaths: TPBXStringArray;
fshellpath: string; fshellpath: string;
fshellScript: string; fshellScript: string;
fshowEnvVarsInLog: Boolean;
public public
constructor Create; override; constructor Create; override;
destructor Destroy; override; destructor Destroy; override;
@ -143,6 +157,7 @@ type
property outputPaths: TPBXStringArray read foutputPaths; property outputPaths: TPBXStringArray read foutputPaths;
property shellPath: string read fshellpath write fshellPath; property shellPath: string read fshellpath write fshellPath;
property shellScript: string read fshellScript write fshellScript; property shellScript: string read fshellScript write fshellScript;
property showEnvVarsInLog: Boolean read fshowEnvVarsInLog write fshowEnvVarsInLog default true;
end; end;
{ PBXGroup } { PBXGroup }
@ -197,10 +212,27 @@ type
property dependencies: TPBXObjectsList read fdependencies; property dependencies: TPBXObjectsList read fdependencies;
property name: string read fname write fname; property name: string read fname write fname;
property productName: string read fproductName write fproductName; // = ttestGame; property productName: string read fproductName write fproductName; // = ttestGame;
property productReference: PBXObject read fproductReference write fproductReference; // = 0AFA6EAD19F60EFE004C8FD9 /* ttestGame.app */; property productReference: PBXObject read fproductReference write fproductReference; // producut resulting file
property productType: string read fproductType write fproductType; // = "com.apple.product-type.application"; property productType: string read fproductType write fproductType; // = "com.apple.product-type.application";
end; end;
{ PBXLegacyTarget }
PBXLegacyTarget = class(PBXObject)
private
fpassBuildSettingsInEnvironment : Boolean;
fbuildArgumentsString : string;
fbuildToolPath : string;
fbuildWorkingDirectory : string;
public
constructor Create; override;
published
property buildArgumentsString: string read fbuildArgumentsString write fbuildArgumentsString;
property buildToolPath: string read fbuildToolPath write fbuildToolPath;
property buildWorkingDirectory: string read fbuildWorkingDirectory write fbuildWorkingDirectory;
property passBuildSettingsInEnvironment: Boolean read fpassBuildSettingsInEnvironment write fpassBuildSettingsInEnvironment;
end;
{ PBXTargetDependency } { PBXTargetDependency }
// mmgt: // mmgt:
@ -285,19 +317,28 @@ const
//FILETYPE_SCRIPT = 'text.script.sh'; //FILETYPE_SCRIPT = 'text.script.sh';
FILETYPE_EXEC = 'compiled.mach-o.executable'; FILETYPE_EXEC = 'compiled.mach-o.executable';
FILETYPE_MACHO = FILETYPE_EXEC; FILETYPE_MACHO = FILETYPE_EXEC;
FILETYPE_BUNDLE = 'wrapper.application';
FILETYPE_PLIST = 'text.plist.xml';
FILETYPE_OBJC = 'sourcecode.c.objc';
function FileRefCreate(const afilename: string; const filetype: string = ''): PBXFileReference; function FileRefCreate(const afilename: string; const filetype: string = ''): PBXFileReference;
const const
GROUPSRC_ABSOLUTE = '<absolute>'; SRCTREE_ABSOLUTE = '<absolute>'; // path is absolute path
GROUPSRC_GROUP = '<group>'; SRCTREE_GROUP = '<group>'; // path is relative to the parent group
SRCTREE_PRODUCT = 'BUILT_PRODUCTS_DIR'; // path is relative to the product build directory
SRCTREE_PROJECT = 'SOURCE_ROOT'; // path is relative for .xcodeproj directory location
SRCTREE_DEV = 'DEVELOPER_DIR'; // path is relative to developer dir
SRCTREE_SDK = 'SDKROOT'; // path is relative to selected SDK dir
function GroupCreate(const aname: string; const srcTree: string = GROUPSRC_GROUP): PBXGroup; function GroupCreate(const aname: string; const srcTree: string = SRCTREE_GROUP): PBXGroup;
//todo: need a rountine to update the path whenever the project is saved //todo: need a rountine to update the path whenever the project is saved
function GroupCreateRoot(const projectfolder: string = ''): PBXGroup; function GroupCreateRoot(const projectfolder: string = ''): PBXGroup;
const const
PRODTYPE_TOOL = 'com.apple.product-type.tool'; PRODTYPE_TOOL = 'com.apple.product-type.tool';
PRODTYPE_APP = 'com.apple.product-type.application'; // use it for OSX / iOS app targets
// prodtype of app type should have productReference to the result bundle! (.app) file
// //
// PBXSourcesBuildPhase (sources) - is part of a PBXNativeTarget // PBXSourcesBuildPhase (sources) - is part of a PBXNativeTarget
@ -309,9 +350,38 @@ const
//0AA67B671A04929900CF0DD7 /* CopyFiles */, //0AA67B671A04929900CF0DD7 /* CopyFiles */,
//); //);
const
TARGET_IOS_8_0 = '8.0';
TARGET_IOS_8_1 = '8.1';
procedure ConfigIOS(cfg: XCBuildConfiguration; const targetiOS: string);
const
CFG_SDKROOT = 'SDKROOT';
CFG_IOSTRG = 'IPHONEOS_DEPLOYMENT_TARGET';
CFG_DEVICE = 'TARGET_DEVICE_FAMILY';
CFG_DEVICE_ALL = '1,2';
CFG_DEVICE_IPHONE = '1';
CFG_DEVICE_IPAD = '2';
implementation implementation
procedure ConfigIOS(cfg: XCBuildConfiguration; const targetiOS: string);
begin
if not Assigned(cfg) then Exit;
cfg.buildSettings.AddStr(CFG_IOSTRG, targetiOS);
cfg.buildSettings.AddStr(CFG_SDKROOT, 'iphoneos');
cfg.buildSettings.AddStr(CFG_DEVICE, CFG_DEVICE_ALL);
end;
{ PBXLegacyTarget }
constructor PBXLegacyTarget.Create;
begin
inherited Create;
fpassBuildSettingsInEnvironment:=true;
end;
{ PBXTargetDependency } { PBXTargetDependency }
destructor PBXTargetDependency.Destroy; destructor PBXTargetDependency.Destroy;
@ -388,12 +458,22 @@ begin
targets.Add(Result); targets.Add(Result);
Result._headerComment:=aname; Result._headerComment:=aname;
Result.name:=aname; Result.name:=aname;
// productName? Result.productName:=aname;
// productReference - is a resulting file
end; end;
{ XCConfigurationList } { XCConfigurationList }
function XCConfigurationList.GetConfigItem(i: integer): XCBuildConfiguration;
begin
if (i<0) or (i>=fbuildConfigurations.Count) then Result:=nil
else Result:=XCBuildConfiguration(fbuildConfigurations[i]);
end;
function XCConfigurationList.GetCount: integer;
begin
Result:=fbuildConfigurations.Count;
end;
constructor XCConfigurationList.Create; constructor XCConfigurationList.Create;
begin begin
inherited Create; inherited Create;
@ -406,7 +486,8 @@ begin
inherited Destroy; inherited Destroy;
end; end;
function XCConfigurationList.AddConfig(const aname: string): XCBuildConfiguration; function XCConfigurationList.addConfig(const aname: string
): XCBuildConfiguration;
begin begin
Result:=XCBuildConfiguration.Create; Result:=XCBuildConfiguration.Create;
Result.name:=aname; Result.name:=aname;
@ -448,7 +529,7 @@ begin
fchildren.Add(Result); fchildren.Add(Result);
Result.name:=aname; Result.name:=aname;
Result._headerComment:=aname; Result._headerComment:=aname;
Result.sourceTree:=GROUPSRC_GROUP; Result.sourceTree:=SRCTREE_GROUP;
end; end;
function PBXGroup.findGroup(const aname: string): PBXGroup; function PBXGroup.findGroup(const aname: string): PBXGroup;
@ -536,7 +617,7 @@ begin
p.buildConfigurationList._headerComment:='Build configuration list for PBXProject'; p.buildConfigurationList._headerComment:='Build configuration list for PBXProject';
p.buildConfigurationList.defaultConfigurationIsVisible:='0'; p.buildConfigurationList.defaultConfigurationIsVisible:='0';
cfg:=p.buildConfigurationList.addConfig('Default'); cfg:=p.buildConfigurationList.addConfig('Debug');
cfg:=p.buildConfigurationList.addConfig('Release'); cfg:=p.buildConfigurationList.addConfig('Release');
// default name must be present // default name must be present
p.buildConfigurationList.defaultConfigurationName:='Release'; p.buildConfigurationList.defaultConfigurationName:='Release';
@ -609,7 +690,7 @@ end;
function GroupCreateRoot(const projectfolder: string): PBXGroup; function GroupCreateRoot(const projectfolder: string): PBXGroup;
begin begin
Result:=GroupCreate('', GROUPSRC_ABSOLUTE); Result:=GroupCreate('', SRCTREE_GROUP);
Result.path:=projectfolder; Result.path:=projectfolder;
Result._headerComment:=projectfolder; Result._headerComment:=projectfolder;
end; end;

View File

@ -0,0 +1,55 @@
unit xcodeprojutils;
{$mode delphi}
interface
uses
Classes, SysUtils, xcodeproj;
// proj name is used for ".xcodeprj" bundle-directory
// hostdir is the hosting directory for the project
// prj - the content of the project
function ProjectWriteStruct(prj: PBXProject; const projName: string; const HostDir: string): Boolean;
const
ProjExt = '.xcodeproj';
ProjFileName = 'project.pbxproj';
implementation
function ProjectWriteStruct(prj: PBXProject; const projName: string; const HostDir: string): Boolean;
var
prjdir : string;
s : string;
fs : TFileStream;
fn : string;
begin
if HostDir = ''
then prjdir:=IncludeTrailingPathDelimiter(GetCurrentDir)+projName+ProjExt
else prjdir:=IncludeTrailingPathDelimiter(HostDir)+projName+ProjExt;
Result:=ForceDirectories(prjdir);
if not Result then Exit;
fn:=IncludeTrailingPathDelimiter(prjdir)+ProjFileName;
s:=ProjectWrite(prj);
try
fs:=TFileStream.Create(fn, fmCreate);
try
if length(s)>0 then begin
fs.Write(s[1], length(s));
fs.Size:=length(s);
end;
Result:=true;
finally
fs.Free;
end;
except
Result:=false;
end;
end;
end.

View File

@ -1,57 +1,59 @@
inherited iPhoneProjectOptionsEditor: TiPhoneProjectOptionsEditor object iPhoneProjectOptionsEditor: TiPhoneProjectOptionsEditor
Height = 474 Left = 0
Width = 620 Height = 466
ClientHeight = 474 Top = 0
ClientWidth = 620 Width = 598
ClientHeight = 466
ClientWidth = 598
OnClick = FrameClick OnClick = FrameClick
TabOrder = 0 TabOrder = 0
DesignLeft = 513 DesignLeft = 408
DesignTop = 97 DesignTop = 201
object chkisPhone: TCheckBox[0] object chkisPhone: TCheckBox
Left = 16 Left = 16
Height = 18 Height = 18
Top = 16 Top = 16
Width = 199 Width = 191
Caption = 'is iPhone application project' Caption = 'is iPhone application project'
OnChange = chkisPhoneChange OnChange = chkisPhoneChange
TabOrder = 0 TabOrder = 0
end end
object lblAppID: TLabel[1] object lblAppID: TLabel
Left = 16 Left = 16
Height = 18 Height = 16
Top = 88 Top = 88
Width = 87 Width = 89
Caption = 'Application ID' Caption = 'Application ID'
ParentColor = False ParentColor = False
end end
object edtAppID: TEdit[2] object edtAppID: TEdit
Left = 112 Left = 112
Height = 22 Height = 22
Top = 85 Top = 85
Width = 490 Width = 468
Anchors = [akTop, akLeft, akRight] Anchors = [akTop, akLeft, akRight]
TabOrder = 1 TabOrder = 1
Text = 'com.mycompany.myapp' Text = 'com.mycompany.myapp'
end end
object lblAppIDHint: TLabel[3] object lblAppIDHint: TLabel
Left = 16 Left = 16
Height = 14 Height = 12
Top = 117 Top = 117
Width = 493 Width = 497
Caption = 'It''s recommended by Apple to use domain-structured name, i.e. com.mycompany.myApplication as ID' Caption = 'It''s recommended by Apple to use domain-structured name, i.e. com.mycompany.myApplication as ID'
Font.Height = -10 Font.Height = -10
ParentColor = False ParentColor = False
ParentFont = False ParentFont = False
end end
object lblSDKVer: TLabel[4] object lblSDKVer: TLabel
Left = 16 Left = 16
Height = 18 Height = 16
Top = 51 Top = 51
Width = 80 Width = 79
Caption = 'SDK version:' Caption = 'SDK version:'
ParentColor = False ParentColor = False
end end
object cmbSDKs: TComboBox[5] object cmbSDKs: TComboBox
Left = 112 Left = 112
Height = 20 Height = 20
Top = 48 Top = 48
@ -61,12 +63,12 @@ inherited iPhoneProjectOptionsEditor: TiPhoneProjectOptionsEditor
Style = csDropDownList Style = csDropDownList
TabOrder = 2 TabOrder = 2
end end
object edtResDir: TEdit[6] object edtResDir: TEdit
AnchorSideRight.Control = btnShowInFinder AnchorSideRight.Control = btnShowInFinder
Left = 120 Left = 120
Height = 22 Height = 22
Top = 174 Top = 174
Width = 355 Width = 332
Anchors = [akTop, akLeft, akRight] Anchors = [akTop, akLeft, akRight]
BorderSpacing.Right = 10 BorderSpacing.Right = 10
OnChange = edtResDirChange OnChange = edtResDirChange
@ -74,56 +76,56 @@ inherited iPhoneProjectOptionsEditor: TiPhoneProjectOptionsEditor
TabOrder = 3 TabOrder = 3
Text = 'Resources' Text = 'Resources'
end end
object Label1: TLabel[7] object Label1: TLabel
Left = 16 Left = 16
Height = 18 Height = 16
Top = 176 Top = 176
Width = 89 Width = 89
Caption = 'Resources dir:' Caption = 'Resources dir:'
ParentColor = False ParentColor = False
end end
object edtExclude: TEdit[8] object edtExclude: TEdit
Left = 120 Left = 120
Height = 22 Height = 22
Top = 224 Top = 224
Width = 482 Width = 460
Anchors = [akTop, akLeft, akRight] Anchors = [akTop, akLeft, akRight]
OnChange = edtExcludeChange OnChange = edtExcludeChange
TabOrder = 4 TabOrder = 4
Text = '.svn' Text = '.svn'
end end
object Label2: TLabel[9] object Label2: TLabel
Left = 16 Left = 16
Height = 18 Height = 16
Top = 224 Top = 224
Width = 81 Width = 82
Caption = 'Ignore mask:' Caption = 'Ignore mask:'
ParentColor = False ParentColor = False
end end
object Label3: TLabel[10] object Label3: TLabel
Left = 16 Left = 16
Height = 14 Height = 12
Top = 200 Top = 200
Width = 364 Width = 359
Caption = 'It''s recommended to set resource dir as a relative (to project .lpr file) path' Caption = 'It''s recommended to set resource dir as a relative (to project .lpr file) path'
Font.Height = -10 Font.Height = -10
ParentColor = False ParentColor = False
ParentFont = False ParentFont = False
end end
object Label4: TLabel[11] object Label4: TLabel
Left = 16 Left = 16
Height = 18 Height = 16
Top = 256 Top = 256
Width = 54 Width = 56
Caption = 'Nib files:' Caption = 'Nib files:'
ParentColor = False ParentColor = False
end end
object nibFilesBox: TCheckListBox[12] object nibFilesBox: TCheckListBox
Left = 120 Left = 120
Height = 180 Height = 76
Top = 256 Top = 256
Width = 482 Width = 460
Anchors = [akTop, akLeft, akRight, akBottom] Anchors = [akTop, akLeft, akRight]
ItemHeight = 0 ItemHeight = 0
OnClickCheck = nibFilesBoxClickCheck OnClickCheck = nibFilesBoxClickCheck
OnItemClick = nibFilesBoxItemClick OnItemClick = nibFilesBoxItemClick
@ -132,54 +134,71 @@ inherited iPhoneProjectOptionsEditor: TiPhoneProjectOptionsEditor
PopupMenu = nibsPopup PopupMenu = nibsPopup
TabOrder = 5 TabOrder = 5
end end
object Label5: TLabel[13] object Label5: TLabel
AnchorSideTop.Control = nibFilesBox AnchorSideTop.Control = nibFilesBox
AnchorSideTop.Side = asrBottom AnchorSideTop.Side = asrBottom
Left = 120 Left = 120
Height = 14 Height = 12
Top = 436 Top = 332
Width = 246 Width = 247
Caption = 'The checked one is the main Nib of the application' Caption = 'The checked one is the main Nib of the application'
Font.Height = -10 Font.Height = -10
ParentColor = False ParentColor = False
ParentFont = False ParentFont = False
end end
object btnShowInFinder: TButton[14] object btnShowInFinder: TButton
Left = 485 Left = 462
Height = 20 Height = 20
Top = 176 Top = 176
Width = 117 Width = 118
Anchors = [akTop, akRight] Anchors = [akTop, akRight]
AutoSize = True AutoSize = True
Caption = 'Show in Finder' Caption = 'Show in Finder'
OnClick = btnShowInFinderClick OnClick = btnShowInFinderClick
TabOrder = 6 TabOrder = 6
end end
object btnAddXib: TButton[15] object btnAddXib: TButton
AnchorSideLeft.Control = btnRemoveXib AnchorSideLeft.Control = btnRemoveXib
AnchorSideRight.Control = btnRemoveXib AnchorSideRight.Control = btnRemoveXib
AnchorSideRight.Side = asrBottom AnchorSideRight.Side = asrBottom
Left = 40 Left = 40
Height = 20 Height = 20
Top = 283 Top = 283
Width = 74 Width = 77
Anchors = [akTop, akLeft, akRight] Anchors = [akTop, akLeft, akRight]
AutoSize = True AutoSize = True
Caption = 'Add' Caption = 'Add'
OnClick = btnAddXibClick OnClick = btnAddXibClick
TabOrder = 7 TabOrder = 7
end end
object btnRemoveXib: TButton[16] object btnRemoveXib: TButton
Left = 40 Left = 40
Height = 20 Height = 20
Top = 312 Top = 312
Width = 74 Width = 77
AutoSize = True AutoSize = True
Caption = 'Remove' Caption = 'Remove'
OnClick = btnRemoveXibClick OnClick = btnRemoveXibClick
TabOrder = 8 TabOrder = 8
end end
object nibsPopup: TPopupMenu[17] object memResFiles: TMemo
Left = 120
Height = 106
Top = 352
Width = 458
Anchors = [akTop, akLeft, akRight, akBottom]
ScrollBars = ssVertical
TabOrder = 9
end
object Label6: TLabel
Left = 16
Height = 16
Top = 352
Width = 90
Caption = 'Included Files:'
ParentColor = False
end
object nibsPopup: TPopupMenu
OnPopup = nibsPopupPopup OnPopup = nibsPopupPopup
left = 160 left = 160
top = 272 top = 272

View File

@ -21,7 +21,8 @@ interface
uses uses
Classes,SysUtils,FileUtil,LResources,Forms,StdCtrls,CheckLst,Buttons, Dialogs, Classes,SysUtils,FileUtil,LResources,Forms,StdCtrls,CheckLst,Buttons, Dialogs,
Menus,IDEOptionsIntf,ProjectIntf,LazIDEIntf,iPhoneExtStr, Menus,IDEOptionsIntf,ProjectIntf,LazIDEIntf,iPhoneExtStr,
iPhoneExtOptions, Controls, LazFilesUtils, XcodeUtils, newXibDialog, xibfile; iPhoneExtOptions, Controls, LazFilesUtils, XcodeUtils, newXibDialog, xibfile
,CompOptsIntf;
type type
@ -32,6 +33,8 @@ type
btnAddXib:TButton; btnAddXib:TButton;
btnRemoveXib:TButton; btnRemoveXib:TButton;
Label5:TLabel; Label5:TLabel;
Label6: TLabel;
memResFiles: TMemo;
mnuDump:TMenuItem; mnuDump:TMenuItem;
mnuOpenIB:TMenuItem; mnuOpenIB:TMenuItem;
nibFilesBox:TCheckListBox; nibFilesBox:TCheckListBox;
@ -72,8 +75,8 @@ type
SelXibFile : String; SelXibFile : String;
ResDirChanged : Boolean; ResDirChanged : Boolean;
fOnChanged : TNotifyEvent; //fOnChanged : TNotifyEvent;
procedure DoChanged; //procedure DoChanged;
procedure RefreshXIBList; procedure RefreshXIBList;
@ -86,7 +89,7 @@ type
procedure Setup(ADialog: TAbstractOptionsEditorDialog); override; procedure Setup(ADialog: TAbstractOptionsEditorDialog); override;
procedure ReadSettings(AOptions: TAbstractIDEOptions); override; procedure ReadSettings(AOptions: TAbstractIDEOptions); override;
procedure WriteSettings(AOptions: TAbstractIDEOptions); override; procedure WriteSettings(AOptions: TAbstractIDEOptions); override;
property OnChanged: TNotifyEvent read fOnChanged write fOnChanged; //property OnChanged: TNotifyEvent read fOnChanged write fOnChanged;
end; end;
implementation implementation
@ -281,10 +284,10 @@ begin
mnuDump.Enabled:=SelXibFile<>'' mnuDump.Enabled:=SelXibFile<>''
end; end;
procedure TiPhoneProjectOptionsEditor.DoChanged; //procedure TiPhoneProjectOptionsEditor.DoChanged;
begin //begin
if Assigned(fOnChanged) then fOnChanged(Self); //if Assigned(fOnChanged) then fOnChanged(Self);
end; //end;
procedure TiPhoneProjectOptionsEditor.RefreshXIBList; procedure TiPhoneProjectOptionsEditor.RefreshXIBList;
var var
@ -513,7 +516,7 @@ procedure TiPhoneProjectOptionsEditor.ReadSettings(AOptions: TAbstractIDEOptions
var var
i : Integer; i : Integer;
begin begin
with TiPhoneProjectOptions(AOptions) do with ProjOptions do
begin begin
Load; Load;
chkisPhone.Checked:=isIPhoneApp; chkisPhone.Checked:=isIPhoneApp;
@ -526,14 +529,16 @@ begin
edtAppID.Text:=AppID; edtAppID.Text:=AppID;
edtResDir.Text:=ResourceDir; edtResDir.Text:=ResourceDir;
edtExclude.Text:=ExcludeMask; edtExclude.Text:=ExcludeMask;
memResFiles.Text:=ResFiles.Text;
end; end;
RefreshXIBList; RefreshXIBList;
if TiPhoneProjectOptions(AOptions).MainNib<>'' then begin if ProjOptions.MainNib<>'' then begin
i:=nibFilesBox.Items.IndexOf(TiPhoneProjectOptions(AOptions).MainNib); i:=nibFilesBox.Items.IndexOf(ProjOptions.MainNib);
if i>=0 then nibFilesBox.Checked[i]:=True; if i>=0 then nibFilesBox.Checked[i]:=True;
end; end;
SetControlsEnabled(chkisPhone.Checked); // is iPhone project SetControlsEnabled(chkisPhone.Checked); // is iPhone project
end; end;
procedure TiPhoneProjectOptionsEditor.WriteSettings(AOptions: TAbstractIDEOptions); procedure TiPhoneProjectOptionsEditor.WriteSettings(AOptions: TAbstractIDEOptions);
@ -548,7 +553,7 @@ begin
Break; Break;
end; end;
with TiPhoneProjectOptions(AOptions) do with ProjOptions do
begin begin
isIPhoneApp:=chkisPhone.Checked; isIPhoneApp:=chkisPhone.Checked;
SDK:=cmbSDKs.Caption; SDK:=cmbSDKs.Caption;
@ -556,14 +561,16 @@ begin
ResourceDir:=edtResDir.Text; ResourceDir:=edtResDir.Text;
ExcludeMask:=edtExclude.Text; ExcludeMask:=edtExclude.Text;
MainNib:=amainnib; MainNib:=amainnib;
ResFiles.Text:=memResFiles.Text;
Save; Save;
DoChanged;
end; end;
DoOnChange;
end; end;
class function TiPhoneProjectOptionsEditor.SupportedOptionsClass: TAbstractIDEOptionsClass; class function TiPhoneProjectOptionsEditor.SupportedOptionsClass: TAbstractIDEOptionsClass;
begin begin
Result:=TiPhoneProjectOptions; //Result:=TiPhoneProjectOptions;
Result:=TLazCompilerOptions;
end; end;
const const

View File

@ -19,9 +19,13 @@ unit xcodetemplate;
interface interface
uses uses
Classes, SysUtils, contnrs; Classes, SysUtils, contnrs, xcodeproj;
procedure PrepareTemplateFile(Src, TemplateValues: TStrings; BuildSettings: TFPStringHashTable); procedure PrepareTemplateFile_(Src, TemplateValues: TStrings; BuildSettings: TFPStringHashTable);
procedure PrepareTemplateFile(Src, TemplateValues: TStrings);
procedure UpdateBldConfig(const proj: PBXProject; optName, optVal: string);
procedure UpdateMainFile(const proj: PBXProject; mainfile: string);
procedure UpdateCompileOpts(const proj: PBXProject; options: string);
const const
XCodeProjectTemplateIconID : AnsiString ='0AE3FFA610F3C9AF00A9B007,'; XCodeProjectTemplateIconID : AnsiString ='0AE3FFA610F3C9AF00A9B007,';
@ -141,10 +145,10 @@ const
' COPY_PHASE_STRIP = YES;'#10+ ' COPY_PHASE_STRIP = YES;'#10+
' FPC_OUTPUT_FILE = $BUILT_PRODUCTS_DIR/$EXECUTABLE_PATH;'#10+ ' FPC_OUTPUT_FILE = $BUILT_PRODUCTS_DIR/$EXECUTABLE_PATH;'#10+
' FPC_COMPILER_OPTIONS = "-Parm -o$FPC_OUTPUT_FILE $FPC_CUSTOM_OPTIONS";'#10+ ' FPC_COMPILER_OPTIONS = "-Parm -o$FPC_OUTPUT_FILE $FPC_CUSTOM_OPTIONS";'#10+
' "FPC_COMPILER_OPTIONS[sdk=iphonesimulator*]" = "-Tiphonesim -Pi386 -o$FPC_OUTPUT_FILE $FPC_CUSTOM_OPTIONS";'#10+ ' "FPC_COMPILER_OPTIONS[sdk=iphonesimulator*]" = "-Tiphonesim -Pi386 -o$FPC_OUTPUT_FILE $FPC_CUSTOM_OPTIONS";'#10+
' FPC_COMPILER_PATH = ;'#10+ ' FPC_COMPILER_PATH = ;'#10+
' FPC_CUSTOM_OPTIONS = ;'#10+ ' FPC_CUSTOM_OPTIONS = ;'#10+
' "FPC_CUSTOM_OPTIONS[sdk=iphonesimulator*]" = ;'#10+ ' "FPC_CUSTOM_OPTIONS[sdk=iphonesimulator*]" = ;'#10+
' FPC_MAIN_FILE = ;'#10+ ' FPC_MAIN_FILE = ;'#10+
' SDKROOT = iphoneos2.0;'#10+ ' SDKROOT = iphoneos2.0;'#10+
' VALID_ARCHS = "armv6 armv7";'#10+ ' VALID_ARCHS = "armv6 armv7";'#10+
@ -199,6 +203,41 @@ const
' rootObject = 0A52AE8310F0D05300478C4F /* Project object */;'#10+ ' rootObject = 0A52AE8310F0D05300478C4F /* Project object */;'#10+
'}'#10; '}'#10;
BuildScript =
'## start'#13
+'echo "compiling FPC project"'#13
+''#13
+'export RESULT_EXE=${BUILT_PRODUCTS_DIR}/${EXECUTABLE_PATH}'#13
+'export IOSHEADERS='#13
+'cd $FPC_MAIN_DIR'#13
+'#rm $RESULT_EXE'#13
+'export TargetCPU=${PLATFORM_PREFERRED_ARCH}'#13
+''#13
+'if [ "${PLATFORM_NAME}" == "iphonesimulator" ]; then'#13
+' export TargetOS="iphonesim"'#13
+'fi'#13
+'export Target=${TargetCPU}-${TargetOS}'#13
+''#13
+'pwd'#13
+'echo ${RESULT_EXE}'#13
+''#13
+'${FPC_DIR}fpc -T${TargetOS} -P${TargetCPU} -MDelphi -Scghi -O1 -l -dIPHONEALL \'#13
+' ${FPC_CUSTOM_OPTIONS} \'#13
//-Fu~/iOS_6_0 -Fu.
+'-Filib/${Target} -FUlib/${Target} \'#13
+'-XR${SDKROOT} -FD${PLATFORM_DEVELOPER_BIN_DIR} $FPC_MAIN_FILE \'#13
+' -o${RESULT_EXE}'#13
+'export RES=$?'#13
+''#13
+'if [ $RES != 0 ]; then'#13
+' exit $RES'#13
+'fi'#13
+''#13
+'echo ${RESULT_EXE}'#13
+''#13
+'exit $FPCRES'#13;
implementation implementation
function GetValueName(const Source: String; idx: Integer): String; function GetValueName(const Source: String; idx: Integer): String;
@ -244,7 +283,7 @@ begin
end; end;
end; end;
procedure PrepareTemplateFile(Src, TemplateValues: TStrings; BuildSettings: TFPStringHashTable); procedure PrepareTemplateFile_(Src, TemplateValues: TStrings; BuildSettings: TFPStringHashTable);
//todo: Better code to update XCode project file! //todo: Better code to update XCode project file!
var var
i, j : Integer; i, j : Integer;
@ -286,6 +325,119 @@ begin
end; end;
end; end;
procedure UpdateBldConfig(const proj: PBXProject; optName, optVal: string);
var
trg : PBXNativeTarget;
cfg : XCBuildConfiguration;
i : integer;
j : integer;
begin
for i:=0 to proj.targets.Count-1 do begin
trg := PBXNativeTarget(proj.targets[i]);
for j:=0 to trg.buildConfigurationList.Count-1 do begin
cfg:=XCBuildConfiguration(trg.buildConfigurationList.buildConfigurations[j]);
cfg.buildSettings.str[optName]:=optVal;
end;
end;
end;
procedure UpdateCompileOpts(const proj: PBXProject; options: string);
var
opt : string;
begin
//UpdateBldConfig(proj, 'FPC_CUSTOM_OPTIONS', options);
UpdateBldConfig(proj, 'FPC_CUSTOM_OPTIONS', '');
opt:=options;
opt:=StringReplace(opt,'$(TargetCPU)','$arch',[rfReplaceAll, rfIgnoreCase]);
opt:=StringReplace(opt,'$(TargetOS)','iphone',[rfReplaceAll, rfIgnoreCase]);
UpdateBldConfig(proj, 'FPC_CUSTOM_OPTIONS[sdk=iphoneos*]', opt);
opt:=options;
opt:=StringReplace(opt,'$(TargetCPU)','$arch',[rfReplaceAll, rfIgnoreCase]);
opt:=StringReplace(opt,'$(TargetOS)','iphonesim',[rfReplaceAll, rfIgnoreCase]);
UpdateBldConfig(proj, 'FPC_CUSTOM_OPTIONS[sdk=iphonesimulator*]', opt);
end;
procedure UpdateMainFile(const proj: PBXProject; mainfile: string);
begin
UpdateBldConfig(proj, 'FPC_MAIN_FILE', mainfile);
UpdateBldConfig(proj, 'FPC_MAIN_DIR', ExtractFileDir(mainfile));
end;
procedure PrepareTemplateFile(Src, TemplateValues: TStrings);
var
prj : PBXProject;
trg : PBXNativeTarget;
cfg : XCBuildConfiguration;
fr : PBXFileReference;
grp : PBXGroup;
scr : PBXShellScriptBuildPhase;
i : integer;
plist : string;
targetName : string;
bundle : string;
main : string;
begin
prj:=ProjectCreate3_2;
targetName:=TemplateValues.Values['targetname'];
bundle:=TemplateValues.Values['bundle'];
plist:=TemplateValues.Values['plist'];
if plist='' then plist:='info.plist';
main:=TemplateValues.Values['mainfile'];
trg:=ProjectAddTarget(prj, targetName);
for i:=0 to prj.buildConfigurationList.Count-1 do begin
cfg:=prj.buildConfigurationList[i];
ConfigIOS(cfg, TARGET_IOS_8_1);
// Enable Build Active Architecture Only When Debugging
if cfg.name='Debug' then cfg.buildSettings.AddStr('ONLY_ACTIVE_ARCH','YES');
end;
// adding application type
trg.productName:=TemplateValues.Values['productname'];
trg.productType:=PRODTYPE_APP;
// target configuration
trg.buildConfigurationList:=XCConfigurationList.Create;
// Debug
cfg:=trg.buildConfigurationList.addConfig('Debug');
cfg.buildSettings.AddStr('INFOPLIST_FILE', '$(SRCROOT)/'+plist);
cfg.buildSettings.AddStr('PRODUCT_NAME', trg.productName);
// Build
cfg:=trg.buildConfigurationList.addConfig('Release');
cfg.buildSettings.AddStr('INFOPLIST_FILE', '$(SRCROOT)/'+plist);
cfg.buildSettings.AddStr('PRODUCT_NAME', trg.productName);
trg.buildConfigurationList.defaultConfigurationName:='Debug';
// Adding the ".app" directory for the bundle and bind it to the target
fr:=FileRefCreate(bundle, FILETYPE_MACHO);
fr.sourceTree:= SRCTREE_PRODUCT;
trg.productReference:=fr;
// Creating "content" for the directory. It should also contain .plist
grp:=prj.mainGroup.addSubGroup(targetName); // name must match to the target name!
grp:=grp.addSubGroup('Supporting Files'); // name must match!
// creating a reference to info.plist. It's located at "xcode" folder.
// Thus at the same directar as .xcodeproj dir
fr:=FileRefCreate(plist, FILETYPE_PLIST );
fr.sourceTree:=SRCTREE_PROJECT;
fr.path:=plist;
grp.children.Add( fr );
scr:=TargetAddRunScript(trg);
scr.shellScript:=BuildScript;
scr.showEnvVarsInLog:=true;
UpdateMainFile(prj, main);
UpdateCompileOpts(prj, TemplateValues.Values['projoptions']);
src.Text:=ProjectWrite(prj);
end;
end. end.