
git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@1214 8e941d3f-bd1b-0410-a28a-d453659cc2b4
492 lines
14 KiB
ObjectPascal
492 lines
14 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 ideext;
|
|
|
|
{$mode objfpc}{$H+}
|
|
|
|
interface
|
|
|
|
uses
|
|
Unix, BaseUnix, process,
|
|
Classes, SysUtils,
|
|
Graphics, Controls, Forms, Dialogs, FileUtil,
|
|
{Lazarus Interface}
|
|
LazIDEIntf, MenuIntf, ProjectIntf, IDEOptionsIntf, IDEMsgIntf,
|
|
|
|
project_iphone_options, xcodetemplate,
|
|
|
|
iPhoneExtOptions, iPhoneExtStr, iPhoneBundle, lazfilesutils;
|
|
|
|
procedure Register;
|
|
|
|
implementation
|
|
|
|
type
|
|
{ TiPhoneExtension }
|
|
|
|
TiPhoneExtension = class(TObject)
|
|
protected
|
|
procedure FillBunldeInfo(forSimulator: Boolean; var info: TiPhoneBundleInfo);
|
|
procedure InstallAppToSim;
|
|
function FixCustomOptions(const Options: String; isRealDevice: Boolean): String;
|
|
|
|
function WriteIconTo(const FullName: String): Boolean;
|
|
|
|
function ProjectBuilding(Sender: TObject): TModalResult;
|
|
function ProjectOpened(Sender: TObject; AProject: TLazProject): TModalResult;
|
|
procedure OnProjOptionsChanged(Sender: TObject);
|
|
public
|
|
//isiPhoneMenu :TIDEMenuCommand;
|
|
constructor Create;
|
|
procedure UpdateXcode(Sender: TObject);
|
|
procedure SimRun(Sender: TObject);
|
|
//procedure isProjectClicked(Sender: TObject);
|
|
end;
|
|
|
|
var
|
|
Extension : TiPhoneExtension = nil;
|
|
|
|
function GetProjectPlistName(Project: TLazProject): String;
|
|
var
|
|
ext : String;
|
|
begin
|
|
if Project.LazCompilerOptions.TargetFilename<>'' then
|
|
Result:=ExtractFileName(Project.LazCompilerOptions.TargetFilename)
|
|
else begin
|
|
Result:=ExtractFileName(Project.MainFile.Filename);
|
|
ext:=ExtractFileExt(Result);
|
|
Result:=Copy(Result, 1, length(Result)-length( ext ) );
|
|
end;
|
|
Result:=Result+'.plist';
|
|
end;
|
|
|
|
function GetProjectExeName(Project: TLazProject): String;
|
|
var
|
|
ext : String;
|
|
begin
|
|
if Project.LazCompilerOptions.TargetFilename<>'' then
|
|
Result:=Project.LazCompilerOptions.TargetFilename
|
|
else begin
|
|
Result:=Project.MainFile.Filename;
|
|
ext:=ExtractFileExt(Result);
|
|
Result:=Copy(Result, 1, length(Result)-length( ext ) );
|
|
end;
|
|
Project.LongenFilename(Result);
|
|
end;
|
|
|
|
{ TiPhoneExtension }
|
|
|
|
procedure TiPhoneExtension.FillBunldeInfo(forSimulator: Boolean; var info: TiPhoneBundleInfo);
|
|
begin
|
|
Info.AppID:=ProjOptions.AppID;
|
|
Info.DisplayName:=LazarusIDE.ActiveProject.Title;
|
|
Info.iPlatform:=EnvOptions.GetSDKName(ProjOptions.SDK, forSimulator);
|
|
Info.SDKVersion:=ProjOptions.SDK;
|
|
Info.MainNib:=UTF8Decode(ProjOptions.MainNib);
|
|
end;
|
|
|
|
procedure TiPhoneExtension.InstallAppToSim;
|
|
var
|
|
bundlename : string;
|
|
bundlepath : WideString;
|
|
exepath : WideString;
|
|
nm : String;
|
|
dstpath : String;
|
|
Space : WideString;
|
|
RealSpace : WideString;
|
|
Info : TiPhoneBundleInfo;
|
|
|
|
xiblist : TStringList;
|
|
i : Integer;
|
|
begin
|
|
Space:=ProjOptions.SpaceName; // LazarusIDE.ActiveProject.CustomData.;
|
|
|
|
bundleName:=ExtractFileName(LazarusIDE.ActiveProject.ProjectInfoFile);
|
|
bundleName:=Copy(bundleName, 1, length(bundleName)-length(ExtractFileExt(bundleName)));
|
|
|
|
nm:=GetProjectExeName(LazarusIDE.ActiveProject);
|
|
|
|
FillBunldeInfo(true, Info);
|
|
|
|
CreateBundle(bundleName, Space, ExtractFileName(nm), Info, RealSpace, bundlepath, exepath);
|
|
|
|
WriteIconTo( IncludeTrailingPathDelimiter(bundlepath)+'Icon.png');
|
|
|
|
CopySymLinks(
|
|
ResolveProjectPath(ProjOptions.ResourceDir),
|
|
bundlepath,
|
|
// don't copy .xib files, they're replaced by compiled nibs
|
|
'*.xib; '+ ProjOptions.ExcludeMask
|
|
);
|
|
|
|
if nm<>'' then begin
|
|
dstpath:=UTF8Encode(exepath);
|
|
FpUnlink(dstpath);
|
|
fpSymlink(PChar(nm), PChar(dstpath));
|
|
end;
|
|
|
|
xiblist := TStringList.Create;
|
|
try
|
|
EnumFilesAtDir(ResolveProjectPath(ProjOptions.ResourceDir), '*.xib', xiblist);
|
|
for i:=0 to xiblist.Count-1 do begin
|
|
dstpath:=IncludeTrailingPathDelimiter(bundlepath)+ChangeFileExt(ExtractFileName(xiblist[i]), '.nib');
|
|
ExecCmdLineNoWait(Format('ibtool --compile "%s" "%s"', [dstpath, xiblist[i]]));
|
|
writeln('compile from: ', xiblist[i]);
|
|
writeln(' to: ', dstpath);
|
|
end;
|
|
finally
|
|
xiblist.free;
|
|
end;
|
|
|
|
//todo: compile .xib files to .nibs
|
|
end;
|
|
|
|
function FindParam(const Source, ParamKey: String; var idx: Integer; var Content: String): Boolean;
|
|
var
|
|
i : Integer;
|
|
quoted : Boolean;
|
|
begin
|
|
Result:=false;
|
|
idx:=0;
|
|
for i := 1 to length(Source)-1 do
|
|
if (Source[i]='-') and ((i=1) or (Source[i-1] in [#9,#32,#10,#13])) then
|
|
if Copy(Source, i, 3) = ParamKey then begin
|
|
idx:=i;
|
|
Result:=true;
|
|
Break;
|
|
end;
|
|
|
|
if not Result then Exit;
|
|
|
|
i:=idx+3;
|
|
quoted:=(i<=length(Source)) and (Source[i]='"');
|
|
|
|
if not quoted then begin
|
|
for i:=i to length(Source) do
|
|
if (Source[i] in [#9,#32,#10,#13]) then begin
|
|
Content:=Copy(Source, idx+3, i-idx);
|
|
Exit;
|
|
end;
|
|
end else begin
|
|
i:=i+1;
|
|
for i:=i to length(Source) do
|
|
if (Source[i] = '"') then begin
|
|
Content:=Copy(Source, idx+3, i-idx+1);
|
|
Exit;
|
|
end;
|
|
end;
|
|
Content:=Copy(Source, idx+3, length(Source)-1);
|
|
end;
|
|
|
|
function TiPhoneExtension.FixCustomOptions(const Options: String; isRealDevice: Boolean): String;
|
|
var
|
|
prm : string;
|
|
rawprm : string;
|
|
idx : Integer;
|
|
needfix : Boolean;
|
|
sdkuse : String;
|
|
sdkver : String;
|
|
st : TStringList;
|
|
begin
|
|
sdkver:=ProjOptions.SDK;
|
|
if sdkver='' then begin
|
|
st := TStringList.Create;
|
|
try
|
|
EnvOptions.GetSDKVersions(st);
|
|
if st.Count=0 then
|
|
IDEMessagesWindow.AddMsg(strWNoSDK, '', 0)
|
|
else begin
|
|
sdkver:=st[0];
|
|
ProjOptions.SDK:=sdkver;
|
|
end;
|
|
finally
|
|
st.Free;
|
|
end;
|
|
end;
|
|
sdkuse:=EnvOptions.GetSDKFullPath(sdkver, not isRealDevice);
|
|
|
|
Result:=Options;
|
|
if FindParam(Result, '-XR', idx, rawprm) then begin
|
|
if (rawprm='') or(rawprm[1]<>'"')
|
|
then prm:=rawprm
|
|
else prm:=rawprm;
|
|
|
|
// there might be -XR option, and it might be the same as selected SDK
|
|
needfix:=sdkuse<>prm;
|
|
|
|
// there's -XR option, but it doesn't match the selection options
|
|
if needfix then
|
|
Delete(Result, idx, length(rawprm)+3);
|
|
|
|
end else begin
|
|
//there's no -XR string in custom options
|
|
needfix:=true;
|
|
idx:=1;
|
|
sdkuse:=sdkuse+' ';
|
|
end;
|
|
|
|
if needfix then Insert('-XR'+sdkuse, Result, idx);
|
|
end;
|
|
|
|
constructor TiPhoneExtension.Create;
|
|
begin
|
|
inherited Create;
|
|
LazarusIDE.AddHandlerOnProjectOpened(@ProjectOpened);
|
|
LazarusIDE.AddHandlerOnProjectBuilding(@ProjectBuilding);
|
|
ProjOptions.OnAfterWrite:=@OnProjOptionsChanged;
|
|
|
|
RegisterIDEMenuCommand(itmProjectWindowSection, 'mnuiPhoneSeparator', '-', nil, nil);
|
|
|
|
//isiPhoneMenu:=RegisterIDEMenuCommand(itmProjectWindowSection, 'mnuiPhoneProject', striPhoneProject, @isProjectClicked, nil);
|
|
|
|
RegisterIDEMenuCommand(itmProjectWindowSection, 'mnuiPhoneToXCode', strStartAtXCode, @UpdateXCode, nil);
|
|
RegisterIDEMenuCommand(itmProjectWindowSection, 'mnuiPhoneRunSim', strRunSimulator, @SimRun, nil);
|
|
end;
|
|
|
|
function TiPhoneExtension.ProjectBuilding(Sender: TObject): TModalResult;
|
|
begin
|
|
Result:=mrOk;
|
|
if not Assigned(LazarusIDE.ActiveProject) or not ProjOptions.isIPhoneApp then Exit;
|
|
|
|
LazarusIDE.ActiveProject.LazCompilerOptions.CustomOptions :=
|
|
FixCustomOptions( LazarusIDE.ActiveProject.LazCompilerOptions.CustomOptions, false );
|
|
|
|
//const InfoFileName, BundleName, ExeName: WideString; const info: TiPhoneBundleInfo): Boolean;
|
|
{MessageDlg('CustomOptions fixed = '+ LazarusIDE.ActiveProject.LazCompilerOptions.CustomOptions,
|
|
mtInformation, [mbOK], 0);}
|
|
|
|
InstallAppToSim;
|
|
Result:=mrOk;
|
|
end;
|
|
|
|
function TiPhoneExtension.ProjectOpened(Sender: TObject; AProject: TLazProject): TModalResult;
|
|
begin
|
|
ProjOptions.Reset;
|
|
ProjOptions.Load;
|
|
//isiPhoneMenu.Checked:=ProjOptions.isIPhoneApp;
|
|
Result:=mrOk;
|
|
end;
|
|
|
|
function TiPhoneExtension.WriteIconTo(const FullName: String): Boolean;
|
|
var
|
|
icofile : string;
|
|
ico : TIcon;
|
|
png : TPortableNetworkGraphic;
|
|
i, idx : Integer;
|
|
const
|
|
iPhoneIconSize=57;
|
|
begin
|
|
Result:=false;
|
|
//todo: find a better way of getting the file
|
|
icofile:=ChangeFileExt(LazarusIDE.ActiveProject.MainFile.Filename, '.ico');
|
|
LazarusIDE.ActiveProject.LongenFilename(icofile);
|
|
if not FileExists(icofile) then begin
|
|
// no icon. it should be deleted!
|
|
DeleteFile(FullName);
|
|
Exit;
|
|
end;
|
|
|
|
try
|
|
ico:=TIcon.Create;
|
|
png:=TPortableNetworkGraphic.Create;
|
|
try
|
|
png.Width:=iPhoneIconSize;
|
|
png.Height:=iPhoneIconSize;
|
|
ico.LoadFromFile(icofile);
|
|
idx:=-1;
|
|
for i:=0 to ico.Count- 1 do begin
|
|
ico.Current:=i;
|
|
if (ico.Width=iPhoneIconSize) and (ico.Height=iPhoneIconSize) then begin
|
|
idx:=i;
|
|
Break;
|
|
end;
|
|
end;
|
|
if (idx<0) and (ico.Count>0) then idx:=0;
|
|
|
|
ico.Current:=idx;
|
|
|
|
if (ico.Width=iPhoneIconSize) and (ico.Height=iPhoneIconSize) then
|
|
png.Assign(ico)
|
|
else begin
|
|
//resize to adjust the image
|
|
png.Canvas.CopyRect( Rect(0,0, iPhoneIconSize, iPhoneIconSize), ico.Canvas, Rect(0,0, ico.Width, ico.Height));
|
|
end;
|
|
|
|
png.SaveToFile(FullName);
|
|
finally
|
|
ico.free;
|
|
png.free;
|
|
end;
|
|
Result:=true;
|
|
except
|
|
end;
|
|
end;
|
|
|
|
procedure TiPhoneExtension.OnProjOptionsChanged(Sender: TObject);
|
|
begin
|
|
//isiPhoneMenu.Checked:=ProjOptions.isIPhoneApp;
|
|
end;
|
|
|
|
procedure TiPhoneExtension.UpdateXcode(Sender: TObject);
|
|
var
|
|
templates : TStringList;
|
|
build : TStringList;
|
|
dir : string;
|
|
projdir : string;
|
|
proj : TStringList;
|
|
projname : string;
|
|
|
|
ext : string;
|
|
tname : string;
|
|
plistname : string;
|
|
|
|
Info : TiPhoneBundleInfo;
|
|
name : string;
|
|
|
|
opt : string;
|
|
begin
|
|
FillBunldeInfo(false, Info);
|
|
// the create .plist would be used by XCode project
|
|
// the simulator .plist in created with InstallAppToSim.
|
|
// they differ with SDKs used
|
|
|
|
build := TStringList.Create;
|
|
|
|
tname:=ExtractFileName( LazarusIDE.ActiveProject.MainFile.Filename);
|
|
tname:=ChangeFileExt(tname, '');
|
|
plistname:=tname+'.plist';
|
|
|
|
build.Values['INFOPLIST_FILE'] := '"'+plistname+'"';
|
|
build.Values['PRODUCT_NAME'] := '"'+tname+'"';
|
|
build.Values['SDKROOT'] := EnvOptions.GetSDKName(ProjOptions.SDK, false);
|
|
build.Values['FPC_COMPILER_PATH'] := '"'+EnvOptions.CompilerPath+'"';
|
|
build.Values['FPC_MAIN_FILE'] := '"'+LazarusIDE.ActiveProject.MainFile.Filename+'"';
|
|
opt :=
|
|
' -XR'+EnvOptions.GetSDKFullPath(ProjOptions.SDK, false)+' ' +
|
|
' -FD'+IncludeTrailingPathDelimiter(EnvOptions.PlatformsBaseDir)+'iPhoneOS.platform/Developer/usr/bin ' +
|
|
EnvOptions.CommonOpt;
|
|
|
|
with LazarusIDE.ActiveProject.LazCompilerOptions do begin
|
|
opt:=opt + ' ' +BreakPathsStringToOption(OtherUnitFiles, '-Fu', '\"');
|
|
opt:=opt + ' ' +BreakPathsStringToOption(IncludePath, '-Fi', '\"');
|
|
opt:=opt + ' ' +BreakPathsStringToOption(ObjectPath, '-Fo', '\"');
|
|
opt:=opt + ' ' +BreakPathsStringToOption(Libraries, '-Fl', '\"');
|
|
end;
|
|
|
|
build.Values['FPC_CUSTOM_OPTIONS'] :='"'+opt+'"';
|
|
|
|
dir:='xcode';
|
|
LazarusIDE.ActiveProject.LongenFilename(dir);
|
|
dir:=dir+'/';
|
|
|
|
name:=ExtractFileName(GetProjectExeName(LazarusIDE.ActiveProject));
|
|
ForceDirectories(dir);
|
|
WriteDefInfoList( dir + GetProjectPlistName(LazarusIDE.ActiveProject),
|
|
name, name, Info);
|
|
|
|
|
|
projname:=ExtractFileName(LazarusIDE.ActiveProject.MainFile.Filename);
|
|
ext:=ExtractFileExt(projname);
|
|
projname:=Copy(projname, 1, length(projname)-length(ext));
|
|
|
|
projdir:=dir+projname+'.xcodeproj';
|
|
ForceDirectories(projdir);
|
|
|
|
projname:=IncludeTrailingPathDelimiter(projdir)+'project.pbxproj';
|
|
proj:=TStringList.Create;
|
|
templates:=nil;
|
|
try
|
|
if not FileExists(projname) then begin
|
|
templates:=TStringList.Create;
|
|
|
|
if WriteIconTo( IncludeTrailingPathDelimiter(dir)+'Icon.png') then begin
|
|
templates.Values['icon']:=XCodeProjectTemplateIcon;
|
|
templates.Values['iconid']:=XCodeProjectTemplateIconID;
|
|
templates.Values['iconfile']:=XCodeIconFile;
|
|
templates.Values['iconfileref']:=XCodeIconFileRef;
|
|
end else begin
|
|
templates.Values['icon']:='';
|
|
templates.Values['iconid']:='';
|
|
templates.Values['iconfile']:='';
|
|
templates.Values['iconfileref']:='';
|
|
end;
|
|
|
|
//todo:
|
|
templates.Values['bundle']:=tname+'.app';
|
|
templates.Values['plist']:=plistname;
|
|
templates.Values['targetname']:=tname;
|
|
templates.Values['productname']:=tname;
|
|
|
|
proj.Text:=XCodeProjectTemplate;
|
|
end else
|
|
proj.LoadFromFile(projname);
|
|
|
|
PrepareTemplateFile(proj, templates, build);
|
|
proj.SaveToFile(projname);
|
|
except
|
|
on e: exception do
|
|
ShowMessage(e.Message);
|
|
end;
|
|
|
|
proj.Free;
|
|
templates.Free;
|
|
build.Free;
|
|
|
|
IDEMessagesWindow.AddMsg(strXcodeUpdated, '', 0);
|
|
end;
|
|
|
|
procedure TiPhoneExtension.SimRun(Sender: TObject);
|
|
var
|
|
t : TProcess;
|
|
path : String;
|
|
begin
|
|
t :=TProcess.Create(nil);
|
|
try
|
|
//ProjectBuilding(nil);
|
|
path:=IncludeTrailingPathDelimiter(EnvOptions.SimBundle)+'Contents/MacOS/iPhone Simulator';
|
|
t.CommandLine:='"'+path+'"';
|
|
t.CurrentDirectory:=UTF8Encode(GetSandBoxDir(ProjOptions.SpaceName));
|
|
t.Execute;
|
|
except
|
|
on E: Exception do
|
|
MessageDlg(E.Message, mtInformation, [mbOK], 0);
|
|
end;
|
|
t.Free;
|
|
end;
|
|
|
|
{procedure TiPhoneExtension.isProjectClicked(Sender: TObject);
|
|
begin
|
|
if not Assigned(Sender) or not Assigned(LazarusIDE.ActiveProject) then Exit;
|
|
TIDEMenuCommand(Sender).Checked:=not TIDEMenuCommand(Sender).Checked;
|
|
ProjOptions.isiPhoneApp:=TIDEMenuCommand(Sender).Checked;
|
|
ProjOptions.Save;
|
|
end;}
|
|
|
|
procedure Register;
|
|
begin
|
|
// IDE integration is done in constructor
|
|
Extension := TiPhoneExtension.Create;
|
|
EnvOptions.Load;
|
|
EnvOptions.RefreshVersions;
|
|
end;
|
|
|
|
initialization
|
|
|
|
finalization
|
|
Extension.Free;
|
|
|
|
end.
|
|
|