lazarus-ccr/components/iphonelazext/iphoneextoptions.pas

608 lines
18 KiB
ObjectPascal

{
*****************************************************************************
* *
* 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, MacroIntf,
iPhoneBundle, XMLConf, XcodeUtils
, LazFileUtils, LazFilesUtils
, iphonesimctrl, xcodeproj;
const
DefaultResourceDir = 'Resources';
type
{ TiPhoneProjectOptions }
TiPhoneProjectOptions = class(TAbstractIDEOptions)
private
fisiPhone : Boolean;
fAppID : String;
fSDK : String;
DataWritten : Boolean;
fSpaceName : String;
fResourceDir : String;
fExcludeMask : String;
fMainNib : String;
fResFiles : TStrings;
public
//constructor Create; override;
constructor Create;
destructor Destroy; override;
class function GetGroupCaption: String; override;
class function GetInstance: TAbstractIDEOptions; override;
function Load(AProject: TLazProject = nil): Boolean;
function Save(AProject: TLazProject = nil): 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;
property ResourceDir: String read fResourceDir write fResourceDir;
property ExcludeMask: String read fExcludeMask write fExcludeMask;
property MainNib: String read fMainNib write fMainNib;
property ResFiles: TStrings read fResFiles;
end;
{ TiPhoneEnvironmentOptions }
TSDKInfo = class(TObject)
devName : String;
devPath : String;
simName : String;
simPath : String;
options : String;
end;
TiPhoneEnvironmentOptions = class(TAbstractIDEEnvironmentOptions)
private
fPlatformsBaseDir : String;
fCompilerPath : String;
fBaseRTLPath : String;
fCommonOpt : String;
fSimAppsPath : String;
fSimBundle : String;
fDefaultSDK : String;
fDefaultSimType : String;
fDefaultDeviceID : String;
fScriptTemplate : String;
fVersions : TStringList;
fDeviceList : TList;
function GetDevice(i: integer): TSimDevice;
function GetDeviceCount: Integer;
protected
function XMLFileName: String;
procedure ClearVersionsInfo;
procedure FoundSDK(const Version, DevSDKName, DevSDKPath, SimSDKName, SimSDKPath: String);
function GetSDKInfo(const Version: String): TSDKInfo;
public
constructor Create;
destructor Destroy; override;
class function GetGroupCaption: String; override;
class function GetInstance: TAbstractIDEOptions; override;
function Load: Boolean;
function Save: Boolean;
function GetSDKName(const SDKVer: String; simulator: Boolean): String;
function GetSDKFullPath(const SDKVer: String; simulator: Boolean): String;
function SubstituteMacros(var s: string): boolean;
procedure GetSDKVersions(Strings: TStrings);
procedure RefreshVersions;
procedure DeviceListClear;
procedure DeviceListReload;
property ScriptTemplate: String read fScriptTemplate write fScriptTemplate;
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;
property DefaultSDK: String read fDefaultSDK write fDefaultSDK;
property DefaultSimType: String read fDefaultSimType write fDefaultSimType; // it's currently Simulator via instruments
property DefaultDeviceID: String read fDefaultDeviceID write fDefaultDeviceID;
property DeviceCount: Integer read GetDeviceCount;
property Device[i: integer]: TSimDevice read GetDevice;
end;
function EnvOptions: TiPhoneEnvironmentOptions;
function ProjOptions: TiPhoneProjectOptions;
var
iPhoneEnvGroup : Integer;
iPhonePrjGroup : Integer;
function LazToXcodeProjFile(AProject: TLazProject): string;
function LazToXcodePlistFile(AProject: TLazProject): string;
procedure ReadUploadFilesList(pbx: PBXProject; lst: TStrings);
const
Default_ScriptTemplate = 'def_buildscript.sh';
function DefaultScriptTemplateFileName(const packageDir: string): string;
implementation
function DefaultScriptTemplateFileName(const packageDir: string): string;
begin
if packageDir <> '' then
Result:=IncludeTrailingPathDelimiter(packageDir)+Default_ScriptTemplate
else
Result:='';
end;
var
fEnvOptions : TiPhoneEnvironmentOptions = nil;
fProjOptions : TiPhoneProjectOptions = nil;
const
DefaultXMLName = 'iphoneextconfig.xml';
optisIphone = 'iPhone/isiPhoneApp';
optSDK = 'iPhone/SDK';
optAppID = 'iPhone/AppID';
optSpaceName = 'iPhone/SimSpaceName';
optResourceDir = 'iPhone/ResourceDir';
optExcludeMask = 'iPhone/ExcludeMask';
optMainNib = 'iPhone/MainNib';
optResFiles = 'iPhone/ResFiles';
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 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.GetDevice(i: integer): TSimDevice;
begin
if (i<0) and (i>=fDeviceList.Count) then Result:=nil
else Result:=TSimDevice(fDeviceList[i]);
end;
function TiPhoneEnvironmentOptions.GetDeviceCount: Integer;
begin
Result:=fDeviceList.Count;
end;
function TiPhoneEnvironmentOptions.XMLFileName: String;
begin
Result:=IncludeTrailingPathDelimiter(LazarusIDE.GetPrimaryConfigPath)+DefaultXMLName;
end;
procedure TiPhoneEnvironmentOptions.ClearVersionsInfo;
var
i : Integer;
begin
for i:=0 to fVersions.Count-1 do begin
fVersions.Objects[i].Free;
fVersions.Objects[i]:=nil;
end;
fVersions.Clear;
end;
procedure TiPhoneEnvironmentOptions.FoundSDK(const Version, DevSDKName,
DevSDKPath, SimSDKName, SimSDKPath: String);
var
info: TSDKInfo;
begin
info:=TSDKInfo.Create;
info.devName:=DevSDKName;
info.devPath:=DevSDKPath;
info.simName:=SimSDKName;
info.simPath:=SimSDKPath;
fVersions.AddObject(Version, info);
end;
function TiPhoneEnvironmentOptions.GetSDKInfo(const Version: String): TSDKInfo;
var
i : Integer;
begin
i:=fVersions.IndexOf(Version);
if i<0 then Result:=nil
else Result:=TSDKInfo(fVersions.Objects[i]);
end;
function GetDefaultPlatformPath: WideString;
begin
result := '/Applications/Xcode.app/Contents/Developer/Platforms';
if not DirectoryExistsUTF8(UTF8Encode(result)) then
Result:='/Developer/Platforms';
end;
function GetDefaultSimBundlePath: WideString;
begin
Result:='$(iOSPlatformsPath)' +
'iPhoneSimulator.platform/Developer/Applications/iPhone Simulator.app';
end;
function GetDefaultSimAppPath: WideSTring;
begin
Result:='$(home)'+
'Library/Application Support/iPhone Simulator/$(iOSSDK)/Applications/';
end;
constructor TiPhoneEnvironmentOptions.Create;
begin
inherited Create;
fPlatformsBaseDir := UTF8Encode(GetDefaultPlatformPath);
fSimAppsPath := UTF8Encode(GetDefaultSimAppPath);
fSimBundle := UTF8Encode(GetDefaultSimBundlePath);
fCompilerPath := '/usr/local/bin/fpc';
fVersions:=TStringList.Create;
fDeviceList:=TList.Create;
end;
destructor TiPhoneEnvironmentOptions.Destroy;
begin
ClearVersionsInfo;
fVersions.Free;
DeviceListClear;
fDeviceList.Free;
inherited Destroy;
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', UTF8Decode(fPlatformsBaseDir) ));
fCompilerPath := UTF8Encode(xmlcfg.GetValue('Compiler', UTF8Decode(fCompilerPath)));
fBaseRTLPath := UTF8Encode(xmlcfg.GetValue('RTLPath', UTF8Decode(fBaseRTLPath)));
fCommonOpt := UTF8Encode(xmlcfg.GetValue('CompilerOptions', UTF8Decode(fCommonOpt)));
fSimBundle := UTF8Encode(xmlcfg.GetValue('SimBundle', UTF8Decode(fSimBundle)));
fSimAppsPath := UTF8Encode(xmlcfg.GetValue('SimAppPath', UTF8Decode(fSimAppsPath)));
fDefaultSDK := UTF8Encode(xmlcfg.GetValue('DefaultSDK', UTF8Decode(fDefaultSDK)));
fDefaultDeviceID := UTF8Encode(xmlcfg.GetValue('DefaultDevice', UTF8Decode(fDefaultDeviceID)));
fScriptTemplate := UTF8Encode(xmlcfg.GetValue('ScriptTemplate', UTF8Decode(fScriptTemplate)));
RefreshVersions;
if (fDefaultSDK = '') and (fVersions.Count>0) then
fDefaultSDK:=fVersions[0];
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));
xmlcfg.SetValue('DefaultSDK', UTF8Decode(fDefaultSDK));
xmlcfg.SetValue('DefaultDevice', UTF8Decode(fDefaultDeviceID));
xmlcfg.SetValue('ScriptTemplate', UTF8Decode(fScriptTemplate));
xmlcfg.Flush;
finally
xmlcfg.Free;
end;
except
Result:=false;
end;
end;
function TiPhoneEnvironmentOptions.GetSDKName(const SDKVer: String; simulator: Boolean): String;
var
info : TSDKInfo;
begin
info:=GetSDKInfo(SDKVer);
if not Assigned(info) then Result:=''
else begin
if simulator then Result:=info.simName
else Result:=info.devName;
end;
end;
function TiPhoneEnvironmentOptions.GetSDKFullPath(const SDKVer: String; simulator: Boolean): String;
var
info : TSDKInfo;
begin
info:=GetSDKInfo(SDKVer);
if not Assigned(info) then Result:=''
else begin
if simulator then Result:=info.simPath
else Result:=info.devPath;
end;
end;
function TiPhoneEnvironmentOptions.SubstituteMacros(var s: string): boolean;
begin
s := StringReplace(s, '$(iOSPlatformsPath)', IncludeTrailingPathDelimiter(PlatformsBaseDir), [rfReplaceAll, rfIgnoreCase]);
s := StringReplace(s, '$(iOSSDK)', DefaultSDK, [rfReplaceAll, rfIgnoreCase]);
result := IDEMacros.SubstituteMacros(s);
end;
procedure TiPhoneEnvironmentOptions.GetSDKVersions(Strings: TStrings);
var
i : Integer;
begin
for i:=0 to fVersions.Count-1 do
Strings.Add( fVersions[i] );
end;
procedure TiPhoneEnvironmentOptions.RefreshVersions;
begin
ClearVersionsInfo;
ScanForSDK(EnvOptions.PlatformsBaseDir, @FoundSDK);
end;
procedure TiPhoneEnvironmentOptions.DeviceListClear;
var
i : integer;
begin
for i:=0 to fDeviceList.Count-1 do
TObject(fDeviceList[i]).Free;
fDeviceList.Clear;
end;
procedure TiPhoneEnvironmentOptions.DeviceListReload;
begin
ListDevice(fDeviceList);
end;
{ TiPhoneProjectOptions }
procedure TiPhoneProjectOptions.Reset;
begin
fisiPhone:=false;
fSDK:='iPhone 2.0';
fAppID:='com.mycompany.myapplication';
fSpaceName:='';
DataWritten:=false;
fResFiles.Clear;
end;
constructor TiPhoneProjectOptions.Create;
begin
inherited Create;
fResFiles := TStringList.Create;
Reset;
end;
destructor TiPhoneProjectOptions.Destroy;
begin
fResFiles.Free;
inherited Destroy;
end;
class function TiPhoneProjectOptions.GetGroupCaption: String;
begin
Result:='iPhone';
end;
class function TiPhoneProjectOptions.GetInstance: TAbstractIDEOptions;
begin
Result:=ProjOptions;
end;
procedure ReadUploadFilesList(pbx: PBXProject; lst: TStrings);
var
trg : PBXNativeTarget;
i : Integer;
res : PBXResourcesBuildPhase;
bf : PBXBuildFile;
grp : PBXGroup;
p : string;
begin
if not Assigned(pbx) or not Assigned(lst) or not Assigned(pbx.targets)
or (pbx.targets.Count=0) then Exit;
trg:=PBXNativeTarget(pbx.targets.Items[0]);
res := PBXResourcesBuildPhase(TargetFindBuildPhase(trg, PBXResourcesBuildPhase));
if not Assigned(res) then begin
writeln('failed');
Exit;
end;
grp:=pbx.mainGroup.findGroup('Resources', true); // name must match to the target name!
for i:=0 to res.files.Count-1 do begin
bf:=PBXBuildFile(res.files[i]);
// making sure that the file is one of "Resources" files
p:=bf.fileRef.path;
if Assigned(grp.findFileRefByPathName(p)) then
lst.Add(p);
end;
end;
procedure FileNamesRelateiveToProject(st: TStrings);
var
i : integer;
s : string;
begin
//todo: it shouldn't really be hardcoded
if not Assigned(st) then Exit;
for i:=0 to st.Count-1 do begin
s:=st[i];
if s='' then Continue;
if Pos('../',s)=1 then
s:=Copy(s, 4, length(s));
st[i]:=s;
end;
end;
function TiPhoneProjectOptions.Load(AProject: TLazProject): Boolean;
var
projname : string;
pbx : PBXProject;
st : TStringList;
begin
Result:=True;
if not Assigned(AProject) then
AProject:=LazarusIDE.ActiveProject;
with AProject 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:=UTF8Encode(RandomSpaceName);
if CustomData.Contains(optResourceDir) then fResourceDir:=CustomData.Values[optResourceDir]
else fResourceDir:=DefaultResourceDir;
if CustomData.Contains(optExcludeMask) then fExcludeMask:=CustomData.Values[optExcludeMask];
if CustomData.Contains(optMainNib) then fMainNib:=CustomData.Values[optMainNib];
projname:=LazToXcodeProjFile(AProject);
if FileExists(projname) then begin
ProjectLoadFromFile(projname, pbx);
if Assigned(pbx) then begin
st:=TStringList.Create;
try
ReadUploadFilesList(pbx, st);
FileNamesRelateiveToProject(st);
ResFiles.Assign(st);
finally
st.Free;
pbx.Free;
end;
end;
end;
//todo: this must go away. the only intention is to cleanup the old code
if CustomData.Contains(optResFiles) then begin
CustomData.Remove(optResFiles);
end;
end;
end;
function TiPhoneProjectOptions.Save(AProject: TLazProject): Boolean;
const
BoolStr : array[Boolean] of String = ('false', 'true');
var
modflag: Boolean;
begin
Result:=True;
{do not write iPhone related info to non-iPhone projects}
if DataWritten or fisiPhone then
with LazarusIDE.ActiveProject do begin
modflag:=false;
modflag:=(CustomData.Values[optisIPhone] <> BoolStr[fisiPhone])
or (CustomData.Values[optSDK]<>fSDK)
or (CustomData.Values[optAppID]<>fAppID)
or (CustomData.Values[optSpaceName]<>fSpaceName)
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;
function LazToXcodeProjFile(AProject: TLazProject): string;
begin
if not Assigned(AProject) then
AProject:=LazarusIDE.ActiveProject;
Result:=
IncludeTrailingPathDelimiter( ResolveProjectPath('xcode', AProject) )
+ ChangeFileExt( ExtractFileName(LazarusIDE.ActiveProject.MainFile.Filename),'.xcodeproj')
+ PathDelim
+'project.pbxproj';
end;
function LazToXcodePlistFile(AProject: TLazProject): string;
begin
if not Assigned(AProject) then
AProject:=LazarusIDE.ActiveProject;
Result:=
IncludeTrailingPathDelimiter( ResolveProjectPath('xcode', AProject) )
+'info.plist';
end;
initialization
finalization
FreeOptions;
end.