My bmk version picking up Mac icons

BlitzMax Forums/BlitzMax Module Tweaks/My bmk version picking up Mac icons

Tricky(Posted 2015) [#1]
I need to note, the tweak I did here only works on Mac, if you compile the code below in Windows or Linux you should experience no difference at all.

A thing in BlitzMax that always annoyed me is the fact that BMK always placed the BlitzMax logo as the logo for my GUI apps on Mac. Now icons are very easy to replace on Mac, I'm pretty sure there ain't no OS on which this is simpler, however if I got to re-compile the same code over and over (in order to add new features or fix bugs) I get that BMax logo back requiring me to replace it once again. To make matters even worse, OS X has a very aggressive caching system, so even if I replace the icon manually I will still see the BMax Logo, so I always have to hope the right icon is there when I zip my apps for distrubution.

The code below is the source code of BMK (in BlitzMax v1.51) and the only file that needed a tweak was bmk.bmx itself, so that makes stuff easier.

When using this version of bmk to build a GUI app on Mac (on console apps everything works the same as with the original BMK), you can do two things to make this BMK directly place your own icon into your app bundle.
Let's say we call our game "BlitzMario" and that the main build file is BlitzMario.bmx. If you place an icon with the same file name into the same folder as BlitzMario.bmx (so the icon will be named "BlitzMario.icns"), bmk will place that icon into the built app.

Another method is to place a textfile named BlitzMario.bmx.macicons in the same folder as BlitzMario.bmx and that file should contain nothing more but the full filename of the desired icon (always with full path name + extention). I myself only use that option if I make a quick toolset of multiple apps all having the same icon.

If in the example above neither BlitzMario.icns nor BlitzMario.bmx.macicons can be found, the bmk will ask if you want to try it again (you may have forgotten, so you can now set it right, and request bmk to start over) or you can make bmk put in the BlitzMax logo anyway. I also build in a cancel option in that case, however when bmk is called from MaxIDE this does not really seem to work the way it should.

I've been doing this tweak in nearly every new BlitzMax version I installed and I must say it saved me a lot of trouble messing with the icons every time I compile a GUI app.


Since Windows compiling requires just you put your icon in an .o file which you can import, no trouble arises at all in Windows, and as far as I found out Linux does not directly link icons to an executable the way Mac and Windows do (png files in the start menu, but not "inside" the app itself) so not really an issue there either I guess. :)

I felt like I should have shared this "tweak" with the community before, but in Dutch we often say "beter te laat dan nooit", meaning literally "better too late than never".

You can use this tweak any way you see fit (as long as it is within the BMax license), and you may credit me if you desire, but it's totally not required (as this "tweak" only took me 10 minutes or so, if not shorter). ;)


'
' Change History :
'  BaH 28/09/2007 - Added custom appstub compiles using -b parameter.
'                   Synched with current bmk source.
'
Strict

Framework brl.basic

Import "bmk_make.bmx"
Import "bmk_zap.bmx"
Import "bmk_bb2bmx.bmx"

?MacOS
Incbin "macos.icns"
?

If AppArgs.length<2 CmdError

Local cmd$=AppArgs[1],args$[]

args=ParseConfigArgs( AppArgs[2..] )

CreateDir BlitzMaxPath()+"/tmp"

Select cmd.ToLower()
Case "makeapp"
	SetConfigMung
	MakeApplication args,False
Case "makelib"
	SetConfigMung
	MakeApplication args,True
Case "makemods"
'	Local ms=MilliSecs()
	If opt_debug Or opt_release
		SetConfigMung
		MakeModules args
	Else
		opt_debug=True
		opt_release=False
		SetConfigMung
		MakeModules args
		opt_debug=False
		opt_release=True
		SetConfigMung
		MakeModules args
	EndIf
'	ms=MilliSecs()-ms
'	Print "ms="+ms
Case "cleanmods"
	CleanModules args
Case "zapmod"
	ZapModule args
Case "unzapmod"
	UnzapModule args
Case "listmods"
	ListModules args
Case "modstatus"
	ModuleStatus args
Case "syncmods" 
	SyncModules args
Case "convertbb"
	ConvertBB args
Case "ranlibdir"
	RanlibDir args
Default
	CmdError
End Select

Function SetConfigMung()
	If opt_release
		opt_debug=False
		opt_configmung="release"
		If opt_threaded opt_configmung:+".mt"
		opt_configmung="."+opt_configmung+"."+cfg_platform+"."+opt_arch
	Else
		opt_debug=True
		opt_release=False
		opt_configmung="debug"
		If opt_threaded opt_configmung:+".mt"
		opt_configmung="."+opt_configmung+"."+cfg_platform+"."+opt_arch
	EndIf
End Function

Function SetModfilter( t$ )

	opt_modfilter=t.ToLower()

	If opt_modfilter="*"
		opt_modfilter=""
	Else If opt_modfilter[opt_modfilter.length-1]<>"." 
		opt_modfilter:+"."
	EndIf
	
End Function

Function MakeModules( args$[] )

	If args.length>1 CmdError
	
	If args.length SetModfilter args[0] Else opt_modfilter=""
	
	Local mods:TList=EnumModules()
	
	BeginMake

	MakeMod "brl.blitz"
	
	For Local name$=EachIn mods
		MakeMod name
	Next
	
End Function

Function CleanModules( args$[] )

	If args.length>1 CmdError
	
	If args.length SetModfilter args[0] Else opt_modfilter=""
	
	Local mods:TList=EnumModules()

	Local name$
	For name=EachIn mods
	
		If (name+".").Find(opt_modfilter)<>0 Continue
		
		Print "Cleaning:"+name

		Local path$=ModulePath(name)
		
		DeleteDir path+"/.bmx",True
		
		If Not opt_kill Continue
		
		For Local f$=EachIn LoadDir( path )
		
			Local p$=path+"/"+f
			Select FileType(p)
			Case FILETYPE_DIR
				If f<>"doc"
					DeleteDir p,True
				EndIf
			Case FILETYPE_FILE
				Select ExtractExt(f).tolower()
				Case "i","a","txt","htm","html"
					'nop
				Default
					DeleteFile p
				End Select
			End Select

		Next
	Next

End Function

Function MakeApplication( args$[],makelib )
	If opt_execute
		If Len(args)=0 CmdError
	Else
		If Len(args)<>1 CmdError
	EndIf

	Local Main$=RealPath( args[0] )
	
	Select ExtractExt(Main).ToLower()
	Case ""
		Main:+".bmx"
	Case "c","cpp","cxx","mm","bmx"
	Default
		Throw "Unrecognized app source file type:"+ExtractExt(Main)
	End Select

	If FileType(Main)<>FILETYPE_FILE Throw "Unable to open source file '"+Main+"'"
	
	If Not opt_outfile opt_outfile=StripExt( Main )

?Win32
	If makelib
		If ExtractExt(opt_outfile).ToLower()<>"dll" opt_outfile:+".dll"
	Else
		If ExtractExt(opt_outfile).ToLower()<>"exe" opt_outfile:+".exe"
	EndIf
?

?MacOS
	If opt_apptype="gui"

		Local appId$=StripDir( opt_outfile )

		Local exeDir$=opt_outfile+".app",d$,t:TStream

		Local IconFile$ = "incbin::macos.icns" ' Tricky's custom icon routine.
		Local Ok = False ' Tricky's custom icon routine.

		d=exeDir+"/Contents/MacOS"
		Select FileType( d )
		Case FILETYPE_NONE
			CreateDir d,True
			If FileType( d )<>FILETYPE_DIR
				Throw "Unable to create application directory"
			EndIf
		Case FILETYPE_FILE
			Throw "Unable to create application directory"
		Case FILETYPE_DIR
		End Select
		
		d=exeDir+"/Contents/Resources"
		Select FileType( d )
		Case FILETYPE_NONE
			CreateDir d
			If FileType( d )<>FILETYPE_DIR
				Throw "Unable to create resources directory"
			EndIf
		Case FILETYPE_FILE
			Throw "Unable to create resources directory"
		Case FILETYPE_DIR
		End Select

		t=WriteStream( exeDir+"/Contents/Info.plist" )
		If Not t Throw "Unable to create Info.plist"
		t.WriteLine "<?xml version=~q1.0~q encoding=~qUTF-8~q?>"
		t.WriteLine "<!DOCTYPE plist PUBLIC ~q-//Apple Computer//DTD PLIST 1.0//EN~q ~qhttp://www.apple.com/DTDs/PropertyList-1.0.dtd~q>"
		t.WriteLine "<plist version=~q1.0~q>"
		t.WriteLine "<dict>"
		t.WriteLine "~t<key>CFBundleExecutable</key>"
		t.WriteLine "~t<string>"+appId+"</string>"
		t.WriteLine "~t<key>CFBundleIconFile</key>"
		t.WriteLine "~t<string>"+appId+"</string>"
		t.WriteLine "~t<key>CFBundlePackageType</key>"
		t.WriteLine "~t<string>APPL</string>"
		t.WriteLine "</dict>"
		t.WriteLine "</plist>"
		t.Close

		t=WriteStream( exeDir+"/Contents/Resources/"+appId+".icns" )
		If Not t Throw "Unable to create icons"
		'Local in:TStream=ReadStream( "incbin::macos.icns" )
		' Tricky's custom icon routine. Start!
		Repeat
		If FileType(StripExt(args[0])+".icns")=1
			Print "Same named Icon found!"
			IconFile = StripExt(args[0])+".icns"
			ok=True
		ElseIf FileType(args[0]+".macicons")
			Print "Reading data from: "+args[0]+".macicons"
			iconfile  = Trim(LoadString(args[0]+".macicons"))
			Ok=True
		Else
			Print "WARNING! No Icon Data Found! What do you want to do?"
			Print "0 = Try again"
			Print "1 = Use the BlitzMax Logo for the icon"
			Print "2 = Cancel this build"
			Local k
			Repeat k=Input("Please enter your choice: ").ToInt() Until K>=0 And K<=2
			Select K
				Case 0
					Ok=False
				Case 1
					Ok=True
				Case 3
					End
				End Select	
			EndIf
		Until Ok
		Print "Using icon file: "+iconfile		
		Local in:TStream=ReadStream( iconfile )
		CopyStream in,t
		in.Close
		t.Close
		
		opt_outfile=exeDir+"/Contents/MacOS/"+appId
		
	EndIf
?
	BeginMake
	
	MakeApp Main,makelib
	
	If opt_execute

		Print "Executing:"+StripDir( opt_outfile )

		Local cmd$=CQuote( opt_outfile )
		For Local i=1 Until args.length
			cmd:+" "+CQuote( args[i] )
		Next
		
		Sys cmd
		
	EndIf

End Function

Function ZapModule( args$[] )
	If Len(args)<>2 CmdError

	Local modname$=args[0].ToLower()
	Local outfile$=RealPath( args[1] )

	Local stream:TStream=WriteStream( outfile )
	If Not stream Throw "Unable to open output file"
	
	ZapMod modname,stream
	
	stream.Close
End Function

Function UnzapModule( args$[] )
	If Len(args)<>1 CmdError
	
	Local infile$=args[0]
	
	Local stream:TStream=ReadStream( infile )
	If Not stream Throw "Unable to open input file"
	
	UnzapMod stream
	
	stream.Close
End Function

Function ListModules( args$[],modid$="" )
	If Len(args)<>0 CmdError
	
	Throw "Todo!"

End Function

Function ModuleStatus( args$[] )
	If Len(args)<>1 CmdError
	
	ListModules Null,args[0]

End Function

Function SyncModules( args$[] )
	If args.length CmdError
	
	If Sys( BlitzMaxPath()+"/bin/syncmods" ) Throw "SyncMods error"
	
End Function

Function RanlibDir( args$[] )
	If args.length<>1 CmdError
	
	Ranlib args[0]

End Function