{ /*************************************************************************** ApplicationBundle.pas - Lazarus IDE unit ------------------------------------------ ***************************************************************************/ *************************************************************************** * * * This source is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This code 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. See the GNU * * General Public License for more details. * * * * A copy of the GNU General Public License is available on the World * * Wide Web at . You can also * * obtain it by writing to the Free Software Foundation, * * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * * *************************************************************************** Abstract: Application Bundle utilities } unit ApplicationBundle; {$mode objfpc}{$H+} interface uses Classes, SysUtils, Forms, Controls, Dialogs, FileUtil, LazFileUtils, DialogProcs; type EApplicationBundleException = Exception; { TApplicationPropertyList } TApplicationPropertyList = class(TStringList) public constructor Create(const ExeName: String; Title: String = ''; const Version: String = '0.1'); end; function CreateApplicationBundle(const Filename: String; Title: String = ''; Recreate: boolean = false): TModalResult; function CreateAppBundleSymbolicLink(const {%H-}Filename: String; {%H-}Recreate: boolean = false): TModalResult; const ApplicationBundleExt = '.app'; ContentsDirName = 'Contents'; MacOSDirName = 'MacOS'; ResourcesDirName = 'Resources'; PropertyListFileName = 'Info.plist'; PackageInfoFileName = 'PkgInfo'; PackageInfoHeader = 'APPL????'; implementation { TApplicationPropertyList } constructor TApplicationPropertyList.Create(const ExeName: String; Title: String; const Version: String = '0.1'); begin inherited Create; if Title = '' then Title := ExeName; Add(''); Add(''); Add(''); Add(''); Add(' CFBundleDevelopmentRegion'); Add(' English'); Add(' CFBundleExecutable'); Add(' ' + ExeName + ''); Add(' CFBundleName'); Add(' ' + Title + ''); Add(' CFBundleIdentifier'); Add(' com.company.' + ExeName + ''); Add(' CFBundleInfoDictionaryVersion'); Add(' 6.0'); Add(' CFBundlePackageType'); Add(' APPL'); Add(' CFBundleSignature'); Add(' ' + Copy(ExeName + '????', 1, 4) + ''); Add(' CFBundleShortVersionString'); Add(' ' + Version + ''); Add(' CFBundleVersion'); Add(' 1'); Add(' CSResourcesFileMapped'); Add(' '); // for accepting files dropped on the dock icon: Add(' CFBundleDocumentTypes'); Add(' '); Add(' '); Add(' CFBundleTypeRole'); Add(' Viewer'); Add(' CFBundleTypeExtensions'); Add(' '); Add(' *'); Add(' '); Add(' CFBundleTypeOSTypes'); Add(' '); Add(' fold'); Add(' disk'); Add(' ****'); Add(' '); Add(' '); Add(' '); // needed for retina Add(' NSHighResolutionCapable'); Add(' '); Add(''); Add(''); end; function CreateApplicationBundle(const Filename: String; Title: String; Recreate: boolean): TModalResult; var AppBundleDir: String; ContentsDir: String; MacOSDir: String; ResourcesDir: String; sl: TStrings; begin AppBundleDir := ExtractFileNameWithoutExt(Filename) + ApplicationBundleExt + PathDelim; if not Recreate and DirectoryExistsUTF8(AppBundleDir) then exit(mrOk); // create 'applicationname.app/Contents/MacOS/' directory ContentsDir := AppBundleDir + ContentsDirName + PathDelim; MacOSDir := ContentsDir + MacOSDirName + PathDelim; Result:=ForceDirectoryInteractive(MacOSDir,[mbIgnore,mbRetry]); if Result<>mrOk then exit; // create Info.plist file sl:=TApplicationPropertyList.Create(ExtractFileNameOnly(Filename), Title); Result:=SaveStringListToFile(ContentsDir + PropertyListFileName,'Info.plist part of Application bundle',sl); sl.Free; if Result<>mrOk then exit; // create PkgInfo file 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 ResourcesDir:=ContentsDir + ResourcesDirName + PathDelim; Result:=ForceDirectoryInteractive(ResourcesDir,[mbIgnore,mbRetry]); if Result<>mrOk then exit; Result:=mrOk; end; function CreateAppBundleSymbolicLink(const Filename: String; Recreate: boolean): TModalResult; {$IFDEF UNIX} var ShortExeName: String; LinkFilename: String; {$ENDIF} begin {$IFDEF UNIX} ShortExeName := ExtractFileNameOnly(Filename); LinkFilename := ExtractFileNameWithoutExt(Filename) + ApplicationBundleExt + PathDelim + ContentsDirName + PathDelim + MacOSDirName + PathDelim + ShortExeName; if (not Recreate) and (FileExistsUTF8(LinkFilename)) then exit(mrOk); Result:=CreateSymlinkInteractive(LinkFilename,'..' + PathDelim + '..' + PathDelim + '..' + PathDelim + ShortExeName,[mbIgnore,mbRetry]); {$ELSE} Result:=mrIgnore; {$ENDIF} end; end.