mirror of
https://gitlab.com/freepascal.org/lazarus/lazarus.git
synced 2025-08-19 00:19:26 +02:00
IDE: auto creating application bundle on compile
git-svn-id: trunk@16387 -
This commit is contained in:
parent
d84640530a
commit
b161bf5808
@ -37,7 +37,8 @@ uses
|
|||||||
{$IFDEF UNIX}
|
{$IFDEF UNIX}
|
||||||
BaseUnix,
|
BaseUnix,
|
||||||
{$ENDIF}
|
{$ENDIF}
|
||||||
Classes, SysUtils, FileUtil;
|
Classes, SysUtils, FileUtil, Forms, Controls, Dialogs,
|
||||||
|
DialogProcs;
|
||||||
|
|
||||||
type
|
type
|
||||||
EApplicationBundleException = Exception;
|
EApplicationBundleException = Exception;
|
||||||
@ -49,8 +50,8 @@ type
|
|||||||
constructor Create(const ExeName: String; Title: String = ''; const Version: String = '0.1');
|
constructor Create(const ExeName: String; Title: String = ''; const Version: String = '0.1');
|
||||||
end;
|
end;
|
||||||
|
|
||||||
procedure CreateApplicationBundle(const Filename: String; Title: String = '');
|
function CreateApplicationBundle(const Filename: String; Title: String = ''; Recreate: boolean = false): TModalResult;
|
||||||
procedure CreateSymbolicLink(const Filename: String);
|
function CreateAppBundleSymbolicLink(const Filename: String; Recreate: boolean = false): TModalResult;
|
||||||
|
|
||||||
const
|
const
|
||||||
ApplicationBundleExt = '.app';
|
ApplicationBundleExt = '.app';
|
||||||
@ -122,60 +123,47 @@ begin
|
|||||||
Add('</plist>');
|
Add('</plist>');
|
||||||
end;
|
end;
|
||||||
|
|
||||||
procedure CreateDirectoryInteractive(const Directory: String);
|
function CreateApplicationBundle(const Filename: String; Title: String;
|
||||||
begin
|
Recreate: boolean): TModalResult;
|
||||||
if not CreateDirUTF8(Directory) then
|
|
||||||
EApplicationBundleException.CreateFmt(rsCreatingDirFailed, [Directory]);
|
|
||||||
end;
|
|
||||||
|
|
||||||
procedure CreateApplicationBundle(const Filename: String; Title: String);
|
|
||||||
var
|
var
|
||||||
AppBundleDir: String;
|
AppBundleDir: String;
|
||||||
ContentsDir: String;
|
ContentsDir: String;
|
||||||
MacOSDir: String;
|
MacOSDir: String;
|
||||||
ResourcesDir: String;
|
ResourcesDir: String;
|
||||||
|
sl: TStringList;
|
||||||
procedure CreatePackageInfoFile(const Path: String);
|
|
||||||
var
|
|
||||||
S: TStringList;
|
|
||||||
begin
|
|
||||||
S := TStringList.Create;
|
|
||||||
try
|
|
||||||
S.Add(PackageInfoHeader);
|
|
||||||
S.SaveToFile(UTF8ToSys(Path + PackageInfoFileName));
|
|
||||||
finally
|
|
||||||
S.Free;
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
|
|
||||||
begin
|
begin
|
||||||
AppBundleDir := ExtractFileNameWithoutExt(Filename) + ApplicationBundleExt + PathDelim;
|
AppBundleDir := ExtractFileNameWithoutExt(Filename) + ApplicationBundleExt + PathDelim;
|
||||||
// create 'applicationname.app/' directory
|
if not Recreate and DirectoryExistsUTF8(AppBundleDir) then exit(mrOk);
|
||||||
CreateDirectoryInteractive(AppBundleDir);
|
|
||||||
begin
|
|
||||||
// create 'applicationname.app/Contents/' directory
|
|
||||||
ContentsDir := AppBundleDir + ContentsDirName + PathDelim;
|
|
||||||
CreateDirectoryInteractive(ContentsDir);
|
|
||||||
begin
|
|
||||||
// create 'applicationname.app/Contents/MacOS/' directory
|
// create 'applicationname.app/Contents/MacOS/' directory
|
||||||
|
ContentsDir := AppBundleDir + ContentsDirName + PathDelim;
|
||||||
MacOSDir := ContentsDir + MacOSDirName + PathDelim;
|
MacOSDir := ContentsDir + MacOSDirName + PathDelim;
|
||||||
CreateDirectoryInteractive(MacOSDir);
|
Result:=ForceDirectoryInteractive(MacOSDir,[mbIgnore,mbRetry]);
|
||||||
|
if Result<>mrOk then exit;
|
||||||
|
|
||||||
// create Info.plist file
|
// create Info.plist file
|
||||||
with TApplicationPropertyList.Create(ExtractFileNameOnly(Filename), Title) do
|
sl:=TApplicationPropertyList.Create(ExtractFileNameOnly(Filename), Title);
|
||||||
SaveToFile(UTF8ToSys(ContentsDir + PropertyListFileName));
|
Result:=SaveStringListToFile(ContentsDir + PropertyListFileName,'Info.plist part of Application bundle',sl);
|
||||||
|
sl.Free;
|
||||||
|
if Result<>mrOk then exit;
|
||||||
|
|
||||||
// create PkgInfo file
|
// create PkgInfo file
|
||||||
CreatePackageInfoFile(ContentsDir);
|
sl:=TStringList.Create;
|
||||||
|
sl.Add(PackageInfoHeader);
|
||||||
|
Result:=SaveStringListToFile(ContentsDir+PackageInfoFileName,'PkgInfo part of Application bundle',sl);
|
||||||
|
sl.Free;
|
||||||
|
if Result<>mrOk then exit;
|
||||||
|
|
||||||
// create 'applicationname.app/Contents/Resources/' directory
|
// create 'applicationname.app/Contents/Resources/' directory
|
||||||
ResourcesDir:=ContentsDir + ResourcesDirName + PathDelim;
|
ResourcesDir:=ContentsDir + ResourcesDirName + PathDelim;
|
||||||
CreateDirectoryInteractive(ResourcesDir);
|
Result:=ForceDirectoryInteractive(ResourcesDir,[mbIgnore,mbRetry]);
|
||||||
end;
|
if Result<>mrOk then exit;
|
||||||
end;
|
|
||||||
|
Result:=mrOk;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
procedure CreateSymbolicLink(const Filename: String);
|
function CreateAppBundleSymbolicLink(const Filename: String;
|
||||||
|
Recreate: boolean): TModalResult;
|
||||||
{$IFDEF UNIX}
|
{$IFDEF UNIX}
|
||||||
var
|
var
|
||||||
ShortExeName: String;
|
ShortExeName: String;
|
||||||
@ -186,14 +174,11 @@ begin
|
|||||||
ShortExeName := ExtractFileNameOnly(Filename);
|
ShortExeName := ExtractFileNameOnly(Filename);
|
||||||
LinkFilename := ExtractFileNameWithoutExt(Filename) + ApplicationBundleExt + PathDelim +
|
LinkFilename := ExtractFileNameWithoutExt(Filename) + ApplicationBundleExt + PathDelim +
|
||||||
ContentsDirName + PathDelim + MacOSDirName + PathDelim + ShortExeName;
|
ContentsDirName + PathDelim + MacOSDirName + PathDelim + ShortExeName;
|
||||||
if FPSymLink(PChar('..' + PathDelim + '..' + PathDelim + '..' + PathDelim + ShortExeName),
|
if (not Recreate) and (FileExistsUTF8(LinkFilename)) then exit(mrOk);
|
||||||
PChar(LinkFilename)) <> 0 then
|
Result:=CreateSymlinkInteractive(LinkFilename,'..' + PathDelim + '..' + PathDelim + '..' + PathDelim + ShortExeName,[mbIgnore,mbRetry]);
|
||||||
raise EApplicationBundleException.CreateFmt(rsCreatingSymLinkFailed, [LinkFilename]);
|
|
||||||
{$ELSE}
|
{$ELSE}
|
||||||
raise EApplicationBundleException.Create(rsCreatingSymLinkNotSupported);
|
Result:=mrIgnore;
|
||||||
{$ENDIF}
|
{$ENDIF}
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
end.
|
end.
|
||||||
|
@ -88,6 +88,8 @@ function CheckCreatingFile(const AFilename: string;
|
|||||||
function CheckFileIsWritable(const Filename: string;
|
function CheckFileIsWritable(const Filename: string;
|
||||||
ErrorButtons: TMsgDlgButtons): TModalResult;
|
ErrorButtons: TMsgDlgButtons): TModalResult;
|
||||||
function ChooseSymlink(var Filename: string): TModalResult;
|
function ChooseSymlink(var Filename: string): TModalResult;
|
||||||
|
function CreateSymlinkInteractive(const LinkFilename, TargetFilename: string;
|
||||||
|
ErrorButtons: TMsgDlgButtons): TModalResult;
|
||||||
function ForceDirectoryInteractive(Directory: string;
|
function ForceDirectoryInteractive(Directory: string;
|
||||||
ErrorButtons: TMsgDlgButtons): TModalResult;
|
ErrorButtons: TMsgDlgButtons): TModalResult;
|
||||||
function DeleteFileInteractive(const Filename: string;
|
function DeleteFileInteractive(const Filename: string;
|
||||||
@ -104,6 +106,11 @@ procedure NotImplementedDialog(const Feature: string);
|
|||||||
|
|
||||||
implementation
|
implementation
|
||||||
|
|
||||||
|
{$IFDEF Unix}
|
||||||
|
uses
|
||||||
|
baseunix;
|
||||||
|
{$ENDIF}
|
||||||
|
|
||||||
function BackupFileInteractive(const Filename: string): TModalResult;
|
function BackupFileInteractive(const Filename: string): TModalResult;
|
||||||
begin
|
begin
|
||||||
if Assigned(OnBackupFileInteractive) then
|
if Assigned(OnBackupFileInteractive) then
|
||||||
@ -495,6 +502,25 @@ begin
|
|||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
function CreateSymlinkInteractive(const LinkFilename, TargetFilename: string;
|
||||||
|
ErrorButtons: TMsgDlgButtons): TModalResult;
|
||||||
|
var i: integer;
|
||||||
|
Dir: string;
|
||||||
|
begin
|
||||||
|
{$IFDEF Unix}
|
||||||
|
if FpReadLink(LinkFilename)=TargetFilename then exit(mrOk);
|
||||||
|
while FPSymLink(PChar(TargetFilename),PChar(LinkFilename)) <> 0 do begin
|
||||||
|
Result:=IDEMessageDialog(lisCodeToolsDefsWriteError, Format(
|
||||||
|
lisUnableToCreateLinkWithTarget, ['"',
|
||||||
|
LinkFilename, '"', '"', TargetFilename, '"']),
|
||||||
|
mtError,ErrorButtons+[mbCancel],'');
|
||||||
|
if Result<>mrRetry then exit;
|
||||||
|
end;
|
||||||
|
{$ELSE}
|
||||||
|
Result:=mrIgnore;
|
||||||
|
{$ENDIF}
|
||||||
|
end;
|
||||||
|
|
||||||
function ForceDirectoryInteractive(Directory: string;
|
function ForceDirectoryInteractive(Directory: string;
|
||||||
ErrorButtons: TMsgDlgButtons): TModalResult;
|
ErrorButtons: TMsgDlgButtons): TModalResult;
|
||||||
var i: integer;
|
var i: integer;
|
||||||
|
@ -3833,6 +3833,8 @@ resourcestring
|
|||||||
lisDeleteSelectedFiles = 'Delete selected files';
|
lisDeleteSelectedFiles = 'Delete selected files';
|
||||||
lisAddDirectory = 'Add directory';
|
lisAddDirectory = 'Add directory';
|
||||||
lisAddFilesOfDirectory = 'Add files of directory';
|
lisAddFilesOfDirectory = 'Add files of directory';
|
||||||
|
lisUnableToCreateLinkWithTarget = 'Unable to create link %s%s%s with '
|
||||||
|
+'target %s%s%s';
|
||||||
|
|
||||||
implementation
|
implementation
|
||||||
|
|
||||||
|
17
ide/main.pp
17
ide/main.pp
@ -95,7 +95,7 @@ uses
|
|||||||
IDEProtocol,
|
IDEProtocol,
|
||||||
// compile
|
// compile
|
||||||
Compiler, CompilerOptions, CompilerOptionsDlg, CheckCompilerOpts,
|
Compiler, CompilerOptions, CompilerOptionsDlg, CheckCompilerOpts,
|
||||||
W32VersionInfo, ImExportCompilerOpts, InfoBuild,
|
ApplicationBundle, W32VersionInfo, ImExportCompilerOpts, InfoBuild,
|
||||||
// projects
|
// projects
|
||||||
Project, ProjectDefs, NewProjectDlg, ProjectOpts,
|
Project, ProjectDefs, NewProjectDlg, ProjectOpts,
|
||||||
PublishProjectDlg, ProjectInspector, PackageDefs,
|
PublishProjectDlg, ProjectInspector, PackageDefs,
|
||||||
@ -9203,6 +9203,7 @@ var
|
|||||||
VersionInfo: TProjectVersionInfo;
|
VersionInfo: TProjectVersionInfo;
|
||||||
NeedBuildAllFlag: Boolean;
|
NeedBuildAllFlag: Boolean;
|
||||||
UnitOutputDirectory: String;
|
UnitOutputDirectory: String;
|
||||||
|
TargetExeName: String;
|
||||||
begin
|
begin
|
||||||
if Project1.MainUnitInfo=nil then begin
|
if Project1.MainUnitInfo=nil then begin
|
||||||
// this project has not source to compile
|
// this project has not source to compile
|
||||||
@ -9338,6 +9339,20 @@ begin
|
|||||||
if Result<>mrOk then exit;
|
if Result<>mrOk then exit;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
// create application bundle
|
||||||
|
if Project1.UseAppBundle and (Project1.MainUnitID>=0)
|
||||||
|
and (MainBuildBoss.GetLCLWidgetType(true)='carbon')
|
||||||
|
then begin
|
||||||
|
if Project1.IsVirtual then
|
||||||
|
TargetExeName := EnvironmentOptions.GetTestBuildDirectory + ExtractFilename(Project1.MainUnitInfo.Filename)
|
||||||
|
else
|
||||||
|
TargetExeName := Project1.CompilerOptions.CreateTargetFilename(Project1.MainFilename);
|
||||||
|
Result:=CreateApplicationBundle(TargetExeName, Project1.Title);
|
||||||
|
if not (Result in [mrOk,mrIgnore]) then exit;
|
||||||
|
Result:=CreateAppBundleSymbolicLink(TargetExeName);
|
||||||
|
if not (Result in [mrOk,mrIgnore]) then exit;
|
||||||
|
end;
|
||||||
|
|
||||||
// execute compilation tool 'Before'
|
// execute compilation tool 'Before'
|
||||||
if not (pbfSkipTools in Flags) then begin
|
if not (pbfSkipTools in Flags) then begin
|
||||||
ToolBefore:=TProjectCompilationToolOptions(
|
ToolBefore:=TProjectCompilationToolOptions(
|
||||||
|
@ -48,26 +48,16 @@ type
|
|||||||
{ TProjectOptionsDialog }
|
{ TProjectOptionsDialog }
|
||||||
|
|
||||||
TProjectOptionsDialog = class(TForm)
|
TProjectOptionsDialog = class(TForm)
|
||||||
CreateAppBundleButton: TButton;
|
|
||||||
EnableI18NCheckBox: TCheckBox;
|
|
||||||
I18NGroupBox: TGroupBox;
|
|
||||||
PanelOtherLabels: TPanel;
|
|
||||||
PODBtnPanel: TPanel;
|
|
||||||
PoOutDirLabel: TLabel;
|
|
||||||
|
|
||||||
Notebook: TNotebook;
|
Notebook: TNotebook;
|
||||||
ApplicationPage: TPage;
|
ApplicationPage: TPage;
|
||||||
FormsPage: TPage;
|
FormsPage: TPage;
|
||||||
MiscPage: TPage;
|
MiscPage: TPage;
|
||||||
LazDocPage: TPage;
|
LazDocPage: TPage;
|
||||||
i18nPage: TPage;
|
|
||||||
POOutDirButton: TButton;
|
|
||||||
POOutDirEdit: TEdit;
|
|
||||||
SavePage: TPage;
|
SavePage: TPage;
|
||||||
UseAppBundleCheckBox: TCheckBox;
|
|
||||||
UseXPManifestCheckBox: TCheckBox;
|
|
||||||
VersionInfoPage: TPage;
|
VersionInfoPage: TPage;
|
||||||
|
i18nPage: TPage;
|
||||||
|
|
||||||
|
// General
|
||||||
AppSettingsGroupBox: TGroupBox;
|
AppSettingsGroupBox: TGroupBox;
|
||||||
OutputSettingsGroupBox: TGroupBox;
|
OutputSettingsGroupBox: TGroupBox;
|
||||||
SelectDirectoryDialog: TSelectDirectoryDialog;
|
SelectDirectoryDialog: TSelectDirectoryDialog;
|
||||||
@ -75,6 +65,10 @@ type
|
|||||||
TitleEdit: TEdit;
|
TitleEdit: TEdit;
|
||||||
TargetFileLabel: TLabel;
|
TargetFileLabel: TLabel;
|
||||||
TargetFileEdit: TEdit;
|
TargetFileEdit: TEdit;
|
||||||
|
PanelOtherLabels: TPanel;
|
||||||
|
CreateAppBundleButton: TButton;
|
||||||
|
UseAppBundleCheckBox: TCheckBox;
|
||||||
|
UseXPManifestCheckBox: TCheckBox;
|
||||||
|
|
||||||
// Forms
|
// Forms
|
||||||
FormsAutoCreatedLabel: TLabel;
|
FormsAutoCreatedLabel: TLabel;
|
||||||
@ -134,6 +128,14 @@ type
|
|||||||
CopyrightLabel: TLabel;
|
CopyrightLabel: TLabel;
|
||||||
AdditionalInfoForm: TVersionInfoAdditinalInfoForm;
|
AdditionalInfoForm: TVersionInfoAdditinalInfoForm;
|
||||||
|
|
||||||
|
// i18n
|
||||||
|
POOutDirButton: TButton;
|
||||||
|
POOutDirEdit: TEdit;
|
||||||
|
EnableI18NCheckBox: TCheckBox;
|
||||||
|
I18NGroupBox: TGroupBox;
|
||||||
|
PODBtnPanel: TPanel;
|
||||||
|
PoOutDirLabel: TLabel;
|
||||||
|
|
||||||
// buttons at bottom
|
// buttons at bottom
|
||||||
HelpButton: TBitBtn;
|
HelpButton: TBitBtn;
|
||||||
CancelButton: TBitBtn;
|
CancelButton: TBitBtn;
|
||||||
@ -211,19 +213,14 @@ var
|
|||||||
begin
|
begin
|
||||||
Result := False;
|
Result := False;
|
||||||
if AProject.MainUnitInfo = nil then Exit;
|
if AProject.MainUnitInfo = nil then Exit;
|
||||||
try
|
|
||||||
if AProject.IsVirtual then
|
if AProject.IsVirtual then
|
||||||
TargetExeName := EnvironmentOptions.GetTestBuildDirectory + ExtractFilename(AProject.MainUnitInfo.Filename)
|
TargetExeName := EnvironmentOptions.GetTestBuildDirectory + ExtractFilename(AProject.MainUnitInfo.Filename)
|
||||||
else
|
else
|
||||||
TargetExeName := AProject.CompilerOptions.CreateTargetFilename(AProject.MainFilename);
|
TargetExeName := AProject.CompilerOptions.CreateTargetFilename(AProject.MainFilename);
|
||||||
|
|
||||||
CreateApplicationBundle(TargetExeName, AProject.Title);
|
if not (CreateApplicationBundle(TargetExeName, AProject.Title,true) in [mrOk,mrIgnore]) then exit;
|
||||||
CreateSymbolicLink(TargetExeName);
|
if not (CreateAppBundleSymbolicLink(TargetExeName,true) in [mrOk,mrIgnore]) then exit;
|
||||||
Result := True;
|
Result := True;
|
||||||
except
|
|
||||||
on E: Exception do
|
|
||||||
MessageDlg(lisABCreationFailed + E.Message, mtError, [mbCancel], 0);
|
|
||||||
end;
|
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function ProjectSessionStorageToLocalizedName(s: TProjectSessionStorage
|
function ProjectSessionStorageToLocalizedName(s: TProjectSessionStorage
|
||||||
|
Loading…
Reference in New Issue
Block a user