{ ***************************************************************************** * * * 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.