{
*****************************************************************************
* *
* 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, LazFileUtils, PlistFile;
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;
MainNib : WideString;
end;
function GetiPhoneSimUserPath: 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;
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 GetBundleFullDir(const BundleName, SpaceName: WideString): WideString;
function GetSandBoxDir(const SpaceName: WideString): WideString;
function AddPathDelim(const w: WideString): WideString;
function RandomSpaceName: WideString;
implementation
(* Build script:
## start
echo "compiling FPC project"
export RESULT_EXE=${BUILT_PRODUCTS_DIR}/${EXECUTABLE_PATH}
export IOSHEADERS=
cd $FPC_MAIN_DIR
export TargetCPU=${PLATFORM_PREFERRED_ARCH}
export OPT32=${FPC_OPT_A32}
export OPT64=${FPC_OPT_A64}
export CPU32="arm"
export CPU64="aarch64"
export TargetOS="darwin"
if [ "${PLATFORM_NAME}" == "iphonesimulator" ]; then
export OPT32=${FPC_OPT_I32}
export OPT64=${FPC_OPT_I64}
export CPU32="i386"
export CPU64="x86_64"
export TargetOS="iphonesim"
fi
# 64-bit compilation
export Result64=${RESULT_EXE}_64
export TargetCPU=${CPU64}
export Target=${TargetCPU}-${TargetOS}
## making output directory
export outdir=lib/${Target}
mkdir -p ${outdir}
echo ${FPC_DIR}fpc -T${TargetOS} -P${TargetCPU} -MDelphi -Scghi -O3 -l -dIPHONEALL \
${FPC_CUSTOM_OPTIONS} ${OPT64} \
-Filib/${Target} -FUlib/${Target} \
-XR${SDKROOT} -FD${PLATFORM_DEVELOPER_BIN_DIR} $FPC_MAIN_FILE \
-o${Result64}
${FPC_DIR}fpc -T${TargetOS} -P${TargetCPU} -MDelphi -Scghi -O3 -l -dIPHONEALL \
${FPC_CUSTOM_OPTIONS} ${OPT64} \
-Filib/${Target} -FUlib/${Target} \
-XR${SDKROOT} -FD${PLATFORM_DEVELOPER_BIN_DIR} $FPC_MAIN_FILE \
-k-F/Users/dmitry/FPC_laz/paseng/tests/testBomber3UI -k-t \
-o${Result64}
export RES=$?
if [ $RES != 0 ]; then
exit $RES
fi
# 32-bit complication
export Result32=${RESULT_EXE}_32
export TargetCPU=${CPU32}
export Target=${TargetCPU}-${TargetOS}
## making output directory
export outdir=lib/${Target}
mkdir -p ${outdir}
echo ${FPC_DIR}fpc -T${TargetOS} -P${TargetCPU} -MDelphi -Scghi -Cg -O3 -l -dIPHONEALL \
${FPC_CUSTOM_OPTIONS} ${OPT32} \
-Filib/${Target} -FUlib/${Target} \
-XR${SDKROOT} -FD${PLATFORM_DEVELOPER_BIN_DIR} $FPC_MAIN_FILE \
-k-F/Users/dmitry/FPC_laz/paseng/tests/testBomber3UI \
-o${Result32}
${FPC_DIR}fpc -T${TargetOS} -P${TargetCPU} -MDelphi -Scghi -Cg -O3 -l -dIPHONEALL \
${FPC_CUSTOM_OPTIONS} ${OPT32} \
-Filib/${Target} -FUlib/${Target} \
-XR${SDKROOT} -FD${PLATFORM_DEVELOPER_BIN_DIR} $FPC_MAIN_FILE \
-k-F/Users/dmitry/FPC_laz/paseng/tests/testBomber3UI \
-o${Result32}
export RES=$?
if [ $RES != 0 ]; then
exit $RES
fi
lipo -create ${Result32} ${Result64} -output ${RESULT_EXE}
rm ${Result32}
rm ${Result64}
exit $FPCRES
*)
uses
iPhoneExtOptions;
function RandomSpaceName: WideString;
var
g : TGUID;
id : String;
begin
CreateGUID(g);
id:=GUIDToString(g);
id:=Copy(id, 2, length(id)-2);
Result:=UTF8Decode(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;
function GetBundleFullDir(const BundleName, SpaceName: WideString): WideString;
var
path8 : String;
space8 : String;
p : string;
begin
path8:=UTF8Encode(GetiPhoneSimUserPath);
space8:=UTF8Encode(SpaceName);
p:=IncludeTrailingPathDelimiter(IncludeTrailingPathDelimiter(path8)+space8);
Result:=UTF8Decode(p+UTF8Encode(BundleName)+'.app');
end;
function GetSandBoxDir(const SpaceName: WideString): WideString;
var
path8 : String;
space8 : String;
p : string;
begin
path8:=UTF8Encode(GetiPhoneSimUserPath);
space8:=UTF8Encode(SpaceName);
p:=IncludeTrailingPathDelimiter(IncludeTrailingPathDelimiter(path8)+space8);
if DirectoryExistsUTF8(p) then
Result:=UTF8Decode(p)
else if DirectoryExistsUTF8(path8) then
result:=UTF8Decode(path8)
else
result :='';
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 GetiPhoneSimUserPath: WideString;
var
s : String;
begin
s := EnvOptions.SimAppsPath;
EnvOptions.SubstituteMacros(s);
result := UTF8Decode(s);
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;
procedure InitDefaultPlist(pl: TPListFile);
var
arr : TPListValue;
begin
SetStr(pl, 'CFBundleDevelopmentRegion', 'English');
SetStr(pl, 'CFBundleDisplayName', '');
SetStr(pl, 'CFBundleExecutable', '');
SetStr(pl, 'CFBundleIdentifier', '');
SetStr(pl, 'CFBundleInfoDictionaryVersion', '6.0');
SetStr(pl, 'CFBundleName', '');
SetStr(pl, 'CFBundlePackageType', 'APPL');
SetStr(pl, 'CFBundleSignature', '????');
SetArr(pl, 'CFBundleSupportedPlatforms');
SetStr(pl, 'CFBundleVersion', '1.0');
SetStr(pl, 'DTPlatformName', '');
SetStr(pl, 'DTSDKName', '');
SetBool(pl, 'LSRequiresIPhoneOS', true);
arr:=SetArr(pl, 'UISupportedInterfaceOrientations');
AddStr(arr, 'UIInterfaceOrientationPortrait');
AddStr(arr, 'UIInterfaceOrientationLandscapeLeft');
end;
function WriteDefInfoList(const InfoFileName, BundleName, ExeName: WideString; const info: TiPhoneBundleInfo): Boolean;
var
pl : TPListFile;
arr : TPListValue;
begin
Result:=false;
pl := TPListFile.Create;
try
if not FileExists(InfoFileName) then begin
InitDefaultPlist(pl);
end else
LoadFromFile( UTF8Encode(InfoFileName), pl);
SetStr(pl, 'CFBundleDisplayName', info.DisplayName);
SetStr(pl, 'CFBundleExecutable', ExeName);
SetStr(pl, 'CFBundleIdentifier', info.AppID);
SetStr(pl, 'CFBundleName', BundleName);
arr:=SetArr(pl, 'CFBundleSupportedPlatforms');
SetStr(arr, 0, info.iPlatform);
SetStr(pl, 'DTPlatformName', info.iPlatform);
SetStr(pl, 'DTSDKName', info.SDKVersion);
Result:=SaveToXMLFile(pl, UTF8Encode(InfoFileName));
finally
pl.Free;
end;
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+ // must be present for AppStore deployment!
' CFBundleSignature'#10+ ' ????'#10+
' CFBundleSupportedPlatforms'#10+ ' '#10+' %s'#10+' '#10+ {platform}
'%s'+ // optional MainNib name
' CFBundleVersion'#10+ ' 1.0'#10+
' DTPlatformName'#10+ ' %s'#10+ {platform}
' DTSDKName'#10+ ' %s'#10+ {sdk version}
' LSRequiresIPhoneOS'#10+ ' '#10+
' UISupportedInterfaceOrientations'#10+
' '#10+
' UIInterfaceOrientationPortrait'#10+
' UIInterfaceOrientationLandscapeLeft'#10+
' '#10+
''#10+
'';
function MainNibString(const NibName: WideString): AnsiString;
begin
if NibName='' then Result:=''
else Result:='NSMainNibFile'+UTF8Encode(NibName)+''#10;
end;
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),
MainNibString(info.MainNib),
UTF8Encode(iPlatform),
UTF8Encode(SDKVersion)
]);
if FileExists(InfoFileName) then DeleteFile(InfoFileName);
fs:=TFileStream.Create(UTF8Encode(InfoFileName), fmCreate or fmOpenWrite);
try
if s<>'' then fs.Write(s[1], length(s));
finally
fs.Free;
end;
end;
end.