Răsfoiți Sursa

Initial Import, with multi-thread support.

git-svn-id: http://bmkng.googlecode.com/svn/trunk@2 2a3afde7-ceff-d69e-269d-2b6c215c828a
[email protected] 14 ani în urmă
părinte
comite
203444a18f
18 a modificat fișierele cu 4253 adăugiri și 0 ștergeri
  1. 370 0
      bmk.bmx
  2. 122 0
      bmk_bank.bmx
  3. 758 0
      bmk_bb2bmx.bmx
  4. 282 0
      bmk_config.bmx
  5. 481 0
      bmk_make.bmx
  6. 60 0
      bmk_modinfo.bmx
  7. 239 0
      bmk_modutil.bmx
  8. 856 0
      bmk_ng.bmx
  9. 197 0
      bmk_ng_utils.bmx
  10. 298 0
      bmk_util.bmx
  11. 138 0
      bmk_zap.bmx
  12. 53 0
      config.bmk
  13. 41 0
      core.bmk
  14. 4 0
      custom.bmk
  15. BIN
      macos.icns
  16. 239 0
      make.bmk
  17. 94 0
      readme.txt
  18. 21 0
      waitpid.c

+ 370 - 0
bmk.bmx

@@ -0,0 +1,370 @@
+'
+' Change History :
+'  BaH 26/05/2009 - Added multi-process (threading) support.
+'                   Improved custom variable overriding.
+'  BaH 18/05/2009 - Added Universal support (Mac) with -i parameter.
+'                   Added cross-compile support with -l win32.
+'  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 "Not enough parameters", True
+
+Local cmd$=AppArgs[1],args$[]
+
+args=ParseConfigArgs( AppArgs[2..] )
+
+' preload the default options
+RunCommand("default_cc_opts", Null)
+
+' load any global custom options (in BlitzMax/bin)
+LoadBMK(AppDir + "/custom.bmk")
+
+CreateDir BlitzMaxPath()+"/tmp"
+
+Select cmd.ToLower()
+Case "makeapp"
+	SetConfigMung
+	MakeApplication args,False
+Case "makelib"
+	SetConfigMung
+	MakeApplication args,True
+Case "makemods"
+	If opt_debug Or opt_release
+		SetConfigMung
+		MakeModules args
+		If opt_universal
+			SetConfigMung
+			processor.ToggleCPU()
+			MakeModules args
+			processor.ToggleCPU()
+		End If
+	
+	Else
+		opt_debug=True
+		opt_release=False
+		SetConfigMung
+		MakeModules args
+		If opt_universal
+			SetConfigMung
+			processor.ToggleCPU()
+			MakeModules args
+			processor.ToggleCPU()
+		End If
+		opt_debug=False
+		opt_release=True
+		SetConfigMung
+		MakeModules args
+		If opt_universal
+			SetConfigMung
+			processor.ToggleCPU()
+			MakeModules args
+			processor.ToggleCPU()
+		End If
+	EndIf
+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
+Case "-v"
+	VersionInfo
+Default
+	CmdError "Unknown operation '" + cmd.ToLower() + "'"
+End Select
+
+Function SetConfigMung()
+	If opt_release
+		opt_debug=False
+		opt_configmung="release"
+		If opt_threaded opt_configmung:+".mt"
+		opt_configmung="."+opt_configmung+"."+processor.Platform()+"."'+opt_arch
+	Else
+		opt_debug=True
+		opt_release=False
+		opt_configmung="debug"
+		If opt_threaded opt_configmung:+".mt"
+		opt_configmung="."+opt_configmung+"."+processor.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 "Expecting only 1 argument for makemods"
+	
+	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 "Expecting only 1 argument for cleanmods"
+	
+	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 "Execute requires at least 1 argument"
+	Else
+		If Len(args)<>1 CmdError "Expecting only 1 argument for makeapp"
+	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 )
+
+	' set some useful global variables
+	globals.SetVar("BUILDPATH", ExtractDir(opt_outfile))
+	globals.SetVar("EXEPATH", ExtractDir(opt_outfile))
+	globals.SetVar("OUTFILE", StripDir(StripExt(opt_outfile)))
+	
+	If processor.Platform() = "win32" Then
+		If makelib
+			If ExtractExt(opt_outfile).ToLower()<>"dll" opt_outfile:+".dll"
+		Else
+			If ExtractExt(opt_outfile).ToLower()<>"exe" opt_outfile:+".exe"
+		EndIf
+	EndIf
+
+	If processor.Platform() = "macos" Then
+		If opt_apptype="gui"
+	
+			Local appId$=StripDir( opt_outfile )
+	
+			Local exeDir$=opt_outfile+".app",d$,t:TStream
+	
+			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" )
+			CopyStream in,t
+			in.Close
+			t.Close
+			
+			opt_outfile=exeDir+"/Contents/MacOS/"+appId
+			
+			' Mac GUI exepath is in the bundle...
+			globals.SetVar("EXEPATH", ExtractDir(opt_outfile))
+			
+		EndIf
+	End If
+	
+	BeginMake
+	
+	MakeApp Main,makelib
+
+	If opt_universal
+
+		Local previousOutfile:String = opt_outfile
+		processor.ToggleCPU()
+		opt_outfile :+ "." + processor.CPU()
+		BeginMake
+		MakeApp Main,makelib
+		processor.ToggleCPU()
+		
+		MergeApp opt_outfile, previousOutfile
+		
+		opt_outfile = previousOutfile
+	End If
+	
+	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 "Both module name and outfile required"
+
+	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 "Expecting 1 argument for unzapmod"
+	
+	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 "Expecting 1 argument for ranlibdir"
+	
+	Ranlib args[0]
+
+End Function
+

+ 122 - 0
bmk_bank.bmx

@@ -0,0 +1,122 @@
+
+Strict
+
+Import "bmk_config.bmx"
+
+Import Pub.ZLib
+
+Function CompressBank:TBank( bank:TBank )
+
+	Local size=bank.Size()
+	Local out_size=size+size/10+32
+	Local out:TBank=TBank.Create( out_size )
+	compress out.Buf()+4,out_size,bank.Buf(),size
+	out.PokeByte 0,size
+	out.PokeByte 1,size Shr 8
+	out.PokeByte 2,size Shr 16
+	out.PokeByte 3,size Shr 24
+	out.Resize out_size+4
+	Return out
+
+End Function
+
+Function UncompressBank:TBank( bank:TBank )
+
+	Local out_size
+	out_size:|bank.PeekByte(0)
+	out_size:|bank.PeekByte(1) Shl 8
+	out_size:|bank.PeekByte(2) Shl 16
+	out_size:|bank.PeekByte(3) Shl 24
+	Local out:TBank=TBank.Create( out_size )
+	uncompress out.Buf(),out_size,bank.Buf()+4,bank.Size()-4
+	Return out
+	
+End Function
+
+Function SplitUrl( url$,server$ Var,file$ Var )
+	Local i=url.Find( "/",0 )
+	If i<>-1
+		server=url[..i]
+		file=url[i..]
+	Else
+		server=url
+		file="/"
+	EndIf
+End Function
+
+Function HTTPGetBank:TBank( url$ )
+
+	Local server$,file$
+	SplitUrl url,server,file
+	
+	Local t_server$=server
+	If opt_proxy t_server=opt_proxy
+
+	Local t_port=80
+	If opt_proxyport t_port=opt_proxyport
+	
+	Local stream:TStream=TSocketStream.CreateClient( t_server,t_port )
+	If Not stream Return
+	
+	stream.WriteLine "GET http://"+url+" HTTP/1.0"
+	stream.WriteLine "Host: "+server
+	stream.WriteLine ""
+		
+	While Not stream.Eof()
+		Local t$=stream.ReadLine()
+		If Not t Exit
+	Wend
+	
+	Local bank:TBank=TBank.Create(0)
+	Local bank_stream:TStream=TBankStream.Create( bank )
+	
+	CopyStream stream,bank_stream
+	
+	bank_stream.Close
+	stream.Close
+	
+	Return bank
+	
+End Function
+
+Function HTTPPostBank$( bank:TBank,url$ )
+
+	Local server$,file$
+	SplitUrl url,server,file
+		
+	Local t_server$=server
+	If opt_proxy t_server=opt_proxy
+
+	Local t_port=80
+	If opt_proxyport t_port=opt_proxyport
+
+	Local stream:TStream=TSocketStream.CreateClient( t_server,t_port )
+	If Not stream Return
+		
+	stream.WriteLine "POST http://"+url+" HTTP/1.0"
+	stream.WriteLine "Host: "+server
+	stream.WriteLine "Content-Type: application/octet-stream"
+	stream.WriteLine "Content-Length: "+bank.Size()
+	stream.WriteLine ""
+	
+	Local bank_stream:TStream=TBankStream.Create( bank )
+	CopyStream bank_stream,stream
+	bank_stream.Close
+	
+	While Not stream.Eof()
+		Local t$=stream.ReadLine()
+		If Not t Exit
+	Wend
+	
+	Local r$
+	While Not stream.Eof()
+		Local t$=stream.ReadLine()
+		r:+t+"~n"
+	Wend
+	
+	stream.Close
+	
+	Return r
+
+End Function
+

+ 758 - 0
bmk_bb2bmx.bmx

@@ -0,0 +1,758 @@
+
+Strict
+
+' bmk_bb2bmx.bmx v0.4
+' by [email protected]
+
+' converts BlitzBasic source files to BlitzMax
+
+' history
+
+' AND OR operators -> bitwise if not followed by conditional operators =<>  
+' Min and Max -> _Min and _Max
+' HandleFromObject and HandleToObject added to bbtype.bmx 
+' link and list fields in bbtype -> _link and _list
+' IncrementArrays$(code$) adds 1 to all array dimensions of array declarations
+' ReadString(TStream) function added and FreeSound(sound)->Release(sound)
+' hidepointer -> hidemouse showpointer -> showmouse
+' delete obj -> obj.remove
+' readpixel->dev.blitz3d.readpixel writepixel->dev.blitz3d.writepixel
+' seekfile -> seekstream  filepos -> streampos
+' channelvolume->SetChannelVolume soundvolume->
+' readbytes -> ReadBank writebytes-> WriteBankq
+' xor->~ getkey->waitkey mousewait->waitmouse
+' handle -> HandleFromObject object -> HandleToObject
+' stop -> DebugStop
+' .label -> #label
+' param[n]->param[] in function declarations
+' added TypePrefix$ to help avoid type name / variable name clashes
+' processes include files
+' output bbtype.bmx and bbvkey.bmx files (thanks to Terabit) 
+' moved to bmk_bb2bmx
+' inline comments replace ; with '
+' command separators replace : with ;
+' type specifiers replace . with :
+' field separators replace \ with .
+' boolean operators replace and or with & |
+' array declarations replace Dim x(100) with Global x[100+1]
+' graphics commands Line,Rect,Oval,Text -> DrawLine,DrawRect,DrawOval,DrawText
+' implement old style Type handling for Each First Last Before After Insert
+' Str becomes String
+' Delete Each list -> list.Clear()
+' Delete -> Release
+' Data->DefData Read->ReadData
+' KeyDown->VKeyDown KeyHit->VKeyHit
+' native Color and ClsColor commands added to header
+
+' not supported / stubbed out
+
+' no float->boolean so error is "bitwise operators can only be used with integers"
+' MouseZSpeed(), FreeBank(bank), Locate( x,y )
+' LoopSound(sound), ChannelPitch(channel,hz),PlayCDTrack( track,mode=0 )
+
+Import brl.retro
+
+Const TAB$=Chr(9)
+
+Global TypePrefix$="bb"
+
+Function main()
+	Local AppArgs$[]=["bb2bmx","bob.bb"]
+	Local srcfile$,destfile$
+	Local n=Len AppArgs
+	If n<2
+		Print "bb2bmx srcfile.bb [destfile.bmx]"
+		End
+	EndIf
+	srcfile$=AppArgs[1]
+	destfile$=Replace$(srcfile,".bb",".bmx")
+	If n>2 destfile$=AppArgs[2]
+	bb2bmx(srcfile,destfile)
+End Function
+
+Function ConvertBB(args$[])
+	Local srcfile$,destfile$
+	If Len args<1 Throw "convertbb option requires a filename"
+	srcfile$=args[0]
+	If Len args>1 destfile=args[1]
+	bb2bmx srcfile,destfile
+End Function
+
+Global Includes:TList
+Global Complete:TList
+Global TypeLists:TList
+
+Function bb2bmx(srcfile$,destfile$)
+	Local	s$,currdir$,inc$,done$
+	Local	destinclude$
+	Local	src:TList,isrc:TList
+	Local	dest:TList,idest:TList
+	Local	incs:TList
+
+	Includes=New TList
+	TypeLists=New TList
+	Complete=New TList
+	
+	currdir=CurrentDir()
+		
+	src=ReadTextFile(srcfile)
+	If Not src Throw "bb2bmx failed to open "+srcfile
+
+	If Not destfile	destfile$=Replace$(srcfile,".bb",".bmx")
+	ChangeDir ExtractDir(srcfile)
+
+	WriteVKeyFile
+	WriteBBTypeFile
+	
+	dest=New TList	
+
+	Print "converting "+srcfile
+	ConvertSourceList(src,dest)
+
+' process includes
+
+	While Not Includes.IsEmpty()
+		incs=Includes
+		Includes=New TList
+		For srcfile=EachIn incs
+
+' check if include already converted
+
+			For done=EachIn complete
+				If done=srcfile Exit
+			Next
+			If done=srcfile Continue
+
+			complete.AddLast srcfile			
+			isrc=ReadTextFile(srcfile)
+			If Not isrc Throw "bb2bmx failed to open included file "+srcfile
+			ChangeDir ExtractDir(srcfile)
+
+			idest=New TList
+			Print "converting "+srcfile
+
+			ConvertSourceList(isrc,idest)
+			destinclude$=Replace$(srcfile,".bb",".bmx")
+			WriteTextFile(destinclude,idest)
+		Next
+	Wend
+	
+	ChangeDir(currdir)
+
+	For s$=EachIn TypeLists
+		dest.addfirst s$
+	Next
+		
+	dest.addfirst ""
+	dest.addfirst "Import "+Chr(34)+"bbvkey.bmx"+Chr(34)
+	dest.addfirst "Import "+Chr(34)+"bbtype.bmx"+Chr(34)
+
+	WriteTextFile(destfile,dest)
+	
+End Function
+
+Function ProcessInclude$(s$)
+	Local	path$,inc$
+	Local	q,r
+	
+	q=Instr(s,Chr(34))
+	If q=0 Return s$	
+	r=Instr(s,Chr(34),q+1)
+	If r=0 Return s$
+	path$=s[q..r-1]
+
+	path=RealPath(path$)
+	For inc=EachIn Includes
+		If inc=path Exit
+	Next
+	If inc<>path
+		Includes.AddLast(path)	
+	EndIf
+
+	Return Replace$(s$,".bb",".bmx")
+End Function
+
+Function FindToken(s$,t$,p)
+	Local	l$,c
+	t=Lower(t)
+	l=Lower(s)
+	Repeat
+		p=Instr(l,t,p+1)
+		If Not p Exit
+		If p>1
+			c=l[p-2]
+			If c>47 And c<58 Continue	'0-9
+			If c>95 And c<122 Continue	'a-z
+			If c=Asc("_") Continue
+		EndIf
+		If p+Len(t)-1<Len(l)
+			c=l[p+Len(t)-1]
+			If c>47 And c<58 Continue	'0-9
+			If c>95 And c<122 Continue	'a-z
+			If c=Asc("_") Continue
+		EndIf
+		Return p
+	Forever
+End Function
+
+Function ReplaceToken$(s$,t$,r$)
+	Local	l$,p,c
+	Repeat
+		p=FindToken(s,t,p)
+		If Not p Exit
+		s=s[..p-1]+r$+s[p+Len(t)-1..]	
+		p:+Len r$
+	Forever
+	Return s
+End Function
+
+Function FindEOC(s$,p)	'return position of next space or command separator
+	Local	q,r
+	q=Instr(s," ",p)
+	If q=0 q=Len s+1
+	r=Instr(s,";",p)
+	If r And r<q q=r
+	r=Instr(s,"<",p)
+	If r And r<q q=r
+	r=Instr(s,">",p)
+	If r And r<q q=r
+	r=Instr(s,"=",p)
+	If r And r<q q=r
+	r=Instr(s,"-",p)
+	If r And r<q q=r
+	r=Instr(s,"+",p)
+	If r And r<q q=r
+	r=Instr(s,"*",p)
+	If r And r<q q=r
+	Return q
+End Function
+
+Function StripDims$(s$)	'remove dimensions in blitz arrays for blitzmax function parameters
+	Local	p,q
+	p=1
+	Repeat
+		p=Instr(s$,"[",p)
+		If p=0 Exit
+		q=Instr(s$,"]",p)
+		If q=0 Exit
+		s$=s$[..p]+s$[q-1..]
+		p=p+1	
+	Forever
+	Return s
+End Function
+
+Function IncrementArrays$(s$)	'replace all arguments inside [] with 
+	Local p,q,a$
+	p=Instr(s$,"[")
+	If p=0 Return
+	p=Instr(s$,"=")
+	If p 
+'		print "IncrementArray rejecting s$="+s$
+		Return s$
+	EndIf
+	p=0
+	While True
+		p=Instr(s$,"[",p)
+		If p=0 Exit
+		q=Instr(s$,"]",p)
+		If q=0 Exit
+		a$=s$[p..q-1]
+		a$=Replace(a$,",","+1,")
+		a$=a$+"+1"
+		s$=s$[..p]+a$+s$[q-1..]
+		p=p+Len(a$)+1
+	Wend
+	Return s$
+End Function
+
+Function ConvertBoolOps$(s$) ' if AND OR not followed by < > = convert to bool operator & | 
+	Local	p,q,r,t	
+
+	While True
+		t=FindToken(s$,"And",p)
+		If t=0 Exit		
+		q=Len(s$)
+		r=FindToken(s$,"Then",t)
+		If r And r<q q=r
+		r=Instr(s$,";",t)
+		If r And r<q q=r
+' q is end of check	
+		r=Instr(s$,"=",t);If r And r<=q p=q;Continue 	
+		r=Instr(s$,"<",t);If r And r<=q p=q;Continue 	
+		r=Instr(s$,">",t);If r And r<=q p=q;Continue 	
+		s$=s[..t-1]+"&"+s[t+2..]
+	Wend
+	p=0
+	While True
+		t=FindToken(s$,"Or",p)
+		If t=0 Exit		
+		q=Len(s$)
+		r=FindToken(s$,"Then",t)
+		If r And r<q q=r
+		r=Instr(s$,";",t)
+		If r And r<q q=r
+' q is end of check	
+		r=Instr(s$,"=",t);If r And r<=q p=q;Continue 	
+		r=Instr(s$,"<",t);If r And r<=q p=q;Continue 	
+		r=Instr(s$,">",t);If r And r<=q p=q;Continue 	
+		s$=s[..t-1]+"|"+s[t+1..]
+	Wend
+	Return s$
+End Function
+
+Function Convert$(s$)	'substring of source line before rem and between any string literals
+	Local	p,q,r,c,n$
+' replace : command separator
+	s$=Replace(s$,":",";")
+'.label->#label	
+	p=Instr(Trim(s),".")
+	If p=1 
+		p=Instr(s,".")
+		s="#"+s[p..]
+	EndIf
+' replace . type specifier
+	p=1
+	Repeat
+		p=Instr(s,".",p+1)
+		If Not p Exit
+		c=s[p]
+		If c>47 And c<58 Continue	'ignore if decimal point
+		s=s[..p-1]+":"+TypePrefix$+s[p..]	
+	Forever
+' replace \ field separator
+	s$=Replace(s$,"\",".")
+' update BASIC API name changes
+	s$=ReplaceToken(s,"freesound","Release")
+	s$=ReplaceToken(s,"channelpan","SetChannelPan")
+	s$=ReplaceToken(s,"filepos","StreamPos")
+	s$=ReplaceToken(s,"seekfile","SeekStream")
+	s$=ReplaceToken(s,"channelvolume","SetChannelVolume")
+	s$=ReplaceToken(s,"readbytes","ReadBank")
+	s$=ReplaceToken(s,"writebytes","WriteBank")
+	s$=ReplaceToken(s,"mousewait","WaitMouse")
+' hidepointer -> hidemouse showpointer -> showmouse
+	s$=ReplaceToken(s,"hidepointer","HideMouse")
+	s$=ReplaceToken(s,"showpointer","ShowMouse")
+' replace boolean operators
+	s$=ReplaceToken(s,"xor","~~")
+	s$=ReplaceToken(s,"stop","DebugStop")
+
+Rem
+	s$=ReplaceToken(s,"and","&")
+	s$=ReplaceToken(s,"or","|")
+	s$=ReplaceToken(s,"chr$","chr")
+
+	s$=ReplaceToken(s,"line","DrawLine")
+	s$=ReplaceToken(s,"rect","DrawRect")
+	s$=ReplaceToken(s,"oval","DrawOval")
+	s$=ReplaceToken(s,"text","DrawText")
+	s$=ReplaceToken(s,"color","SetColor")
+	s$=ReplaceToken(s,"clscolor","SetCLSColor")
+EndRem
+	s$=ReplaceToken(s,"str","String")
+	s$=ReplaceToken(s,"read","ReadData")
+	s$=ReplaceToken(s,"data","DefData")
+	s$=ReplaceToken(s,"restore","RestoreData")
+	s$=ReplaceToken(s,"keydown","VKeyDown")
+	s$=ReplaceToken(s,"keyhit","VKeyHit")
+	s$=ReplaceToken(s,"getkey","WaitKey")
+	s$=ReplaceToken(s,"readpixel","dev.blitz3d.ReadPixel")	'ReadPixelBuffer")
+	s$=ReplaceToken(s,"writepixel","dev.blitz3d.WritePixel")
+	s$=ReplaceToken(s,"graphicswidth","dev.blitz3d.GraphicsWidth")
+	s$=ReplaceToken(s,"graphicsheight","dev.blitz3d.GraphicsHeight")
+
+	s$=ReplaceToken(s,"min","fmin")
+	s$=ReplaceToken(s,"max","fmax")
+
+' delete obj -> obj.remove
+	p=0
+	Repeat
+		p=FindToken(s,"delete",p)
+		If Not p Exit
+		If Lower(s[p+5..p+10])=" each" s=s[..p+5]+"Each "+TypePrefix$+Trim(s[p+10]);Continue		
+		If Lower(s[p+5..p+11])=" first" s=s[..p+5]+"First "+TypePrefix$+Trim(s[p+11]);Continue		
+		If Lower(s[p+5..p+10])=" last" s=s[..p+5]+"Last "+TypePrefix$+Trim(s[p+10]);Continue		
+		q=Instr(s," ",p+7)
+		If q=0 q=Len s+1
+		r=Instr(s,";",p+7)
+		If r And r<q q=r	
+		s=s[..p-1]+s[p+6..q-1]+".Remove()"+s[q-1..]		'p+6
+		p=q
+	Forever
+
+	s$=ConvertBoolOps(s)
+
+	s$=ReplaceToken(s,"delete","Release")
+
+' handle(obj) -> HandleFromObject(obj) object.blah(handle) -> HandleToObject(handle)
+
+	s$=ReplaceToken(s,"handle","HandleFromObject")
+	p=0
+	Repeat
+		p=FindToken(s,"object",p)
+		If p=0 Exit
+		q=Instr(s,":",p)
+		If q=0 Exit
+		r=Instr(s,"(",q)
+		If r=0 Exit
+		n$=s[q..r-1]
+		c=1
+		q=r
+		While c
+			If r>Len(s) Exit
+			If s[r]="(" c:+1
+			If s[r]=")" c:-1
+			r:+1
+		Wend
+		s=s[..p-1]+n$+"(HandleToObject"+s[q-1..r-1]+")"+s[r..]
+		p=p+Len(n)+16
+	Forever
+
+' replace New Type with New prefix_type 
+	p=0
+	Repeat
+		p=FindToken(s,"New",p)
+		If Not p Exit
+		s=s[..p+3]+TypePrefix$+s[p+3..]
+		p=p+1
+	Forever
+' replace Dim var(size) with Global var[size] 
+	p=0
+	Repeat
+		p=FindToken(s,"Dim",p)
+		If Not p Exit
+		s=s[..p-1]+"Global"+s[p+2..]
+		Repeat
+			p=Instr(s,"(",p)
+			If Not p Exit
+			s=s[..p-1]+"["+s[p..]
+			p=Instr(s,")",p)
+			If Not p Exit
+			s=s[..p-1]+"]"+s[p..]	'was +1
+		Forever
+		s=IncrementArrays(s$)
+		Exit	'no multiple dim calls pre
+'		if not p exit
+	Forever
+' replace function(param[4]) with function(param[])
+	p=0
+	Repeat
+		p=FindToken(s,"Function",p)
+		If Not p Exit
+		p=Instr(s,"(",p)
+		If Not p Exit
+		q=Instr(s,")",p)
+		If Not q Exit
+		n$=StripDims(s[p..q-1])
+		s=s[..p]+n+s[q-1..]		
+		Exit	
+	Forever	
+' for b=each bob -> for b=eachin bob.list
+	p=0
+	Repeat
+		p=FindToken(s,"Each",p)
+		If Not p Exit
+		q=FindEOC(s,p+6)
+		s=s[..p-1]+"EachIn "+Trim(s[p+4..q-1])+"_list"+s[q-1..]		
+		p=q
+	Forever
+' First class
+	p=0
+	Repeat
+		p=FindToken(s,"First",p)
+		If Not p Exit
+		q=FindEOC(s,p+7)
+		n$=Trim(s[p+5..q-1])
+		s=s[..p-1]+TypePrefix+n+"("+n+"_list.First())"+s[q-1..]		
+		p=q+Len(n)*2+14
+	Forever
+' Last class
+	p=0
+	Repeat
+		p=FindToken(s,"Last",p)
+		If Not p Exit
+		q=FindEOC(s,p+6)
+		n$=Trim(s[p+4..q-1])
+		s=s[..p-1]+TypePrefix+n+"("+n+"_list.Last())"+s[q-1..]		
+		p=q+Len(n)*2+13
+	Forever	
+' Insert b Before|After bob -> b.InsertAfter(bob)
+	p=0
+	Repeat
+		p=FindToken(s,"Insert",p)
+		If Not p Exit
+		q=Instr(s," ",p+7)
+		If Not q Exit
+		n$=Lower(s[q..])
+		If n$[..6]="before"
+			n$="Before"
+			r=q+7
+		ElseIf n$[..5]="after"
+			n$="After"
+			r=q+6
+		Else
+			Exit
+		EndIf
+		c=FindEOC(s,r+1)
+		s=s[..p-1]+s[p+6..q-1]+".Insert"+n$+"("+s[r..c-1]+")"+s[c..]	'n$+ simon was here
+		p=c
+	Forever
+' After b -> b.After() 	
+	p=0
+	Repeat
+		p=FindToken(s,"After",p)
+		If Not p Exit
+		q=Instr(s," ",p+7)
+		If q=0 q=Len s+1
+		r=Instr(s,";",p+7)
+		If r And r<q q=r
+		s=s[..p-1]+s[p+5..q-1]+".After()"+s[q-1..]		
+		p=q
+	Forever
+' Before b -> b.Before() 	
+	p=0
+	Repeat
+		p=FindToken(s,"Before",p)
+		If Not p Exit
+		q=Instr(s," ",p+8)
+		If q=0 q=Len s+1
+		r=Instr(s,";",p+8)
+		If r And r<q q=r
+		s=s[..p-1]+s[p+6..q-1]+".Before()"+s[q-1..]		
+		p=q
+	Forever
+	Return s
+End Function
+
+Function FixQuotes$(q$)
+	Local	p,n
+	Repeat
+		p=Instr(q,Chr(34),p)
+		If p=0 Exit
+		p=p+1
+		n=n+1
+	Forever
+	If n&1 Return q$+Chr(34)	'add one for odd quoted lines
+	Return q
+End Function
+
+Function ConvertSourceList(src:TList,dest:TList)
+	Local	in$,out$,l$,p,q,r,inrem,name$
+	Local	strings[]
+	
+	For in=EachIn src
+		l$=Lower$(in)
+		If l$="rem" l$="rem "
+		If inrem
+' terminate remarks		
+			out$=in$
+			p=Instr(l$,"endrem")
+			If p<>1 p=Instr(l$,"end rem")
+			If p=1
+				inrem=0
+			EndIf
+		Else
+' parse strings and remarks
+			out$=""
+			in$=fixquotes(in$)
+			While in$
+				q=Instr(in$,Chr(34))
+				r=Instr(in$,";")
+				inrem=Instr(l$,"rem ")		
+				If inrem And inrem<q q=0
+				If r And r<q q=0
+				If q
+					r=Instr(in$,Chr(34),q+1)
+					If r=0 r=Len(in$)
+					out$=out$+Convert(in$[..q-1])+in$[q-1..r]
+					in$=in$[r..]	
+					Continue				
+				EndIf
+				If inrem And inrem<r r=0
+				If r
+					out$=out$+Convert(in$[..r-1])+"'"+in$[r..]
+					Exit
+				EndIf
+				If inrem
+					out$=out$+Convert(in$[..inrem-1])+in$[inrem-1..]
+					Exit
+				EndIf
+				out$=out$+Convert(in$)
+				Exit
+			Wend
+		EndIf
+	
+		l$=Lower(out)
+			
+' type handling extras		
+		If l$[..5]="type "
+			name$=out$[5..]
+			p=Instr(name," ")
+			q=Instr(name,";")
+			If (Not p) p=Len name
+			If q And q<p p=q
+			If p name=name[..p]
+			name=Trim(name)
+			p=p+5		
+			
+			TypeLists.AddLast "Global "+name+"_list:TList=new TList"
+			dest.AddLast "Type "+TypePrefix$+name+" Extends TBBType"+out$[p..]
+			dest.AddLast ""
+			dest.AddLast TAB$+"Method New()"
+			dest.AddLast TAB$+TAB$+"Add("+name+"_list)"
+			dest.AddLast TAB$+"End Method"
+			dest.AddLast ""
+			dest.AddLast TAB$+"Method After:"+TypePrefix$+name+"()"
+			dest.AddLast TAB$+TAB$+"Local t:TLink"
+			dest.AddLast TAB$+TAB$+"t=_link.NextLink()"
+			dest.AddLast TAB$+TAB$+"If t Return "+TypePrefix$+name+"(t.Value())"
+			dest.AddLast TAB$+"End Method"
+			dest.AddLast ""
+			dest.AddLast TAB$+"Method Before:"+TypePrefix$+name+"()"
+			dest.AddLast TAB$+TAB$+"Local t:TLink"
+			dest.AddLast TAB$+TAB$+"t=_link.PrevLink()"
+			dest.AddLast TAB$+TAB$+"If t Return "+TypePrefix$+name+"(t.Value())"
+			dest.AddLast TAB$+"End Method"
+			dest.AddLast ""
+			Continue
+		EndIf
+
+' Include "blah.bb" ->	Include "blah.bmx"
+		
+		If l$[..8]="include " Or l$[..8]="include"+Chr$(34)
+			out=ProcessInclude(out)
+		EndIf
+		
+		dest.AddLast out
+		
+	Next
+End Function
+
+Function ReadTextFile:TList(file$)
+	Local	f:TStream,n
+	Local	txt:TList
+	txt=New TList
+	f=ReadStream(file)
+	If Not f Return Null
+	While Not Eof(f)
+		txt.AddLast ReadLine(f)
+		n:+1
+	Wend
+	Return txt
+End Function
+
+Function WriteTextFile(file$,txt:TList)
+	Local	f:TStream,l$
+	f=WriteStream(file)
+	If Not f Return
+	For l$=EachIn txt
+		WriteLine f,l
+	Next
+	CloseStream f
+End Function
+
+Function WriteVKeyFile()
+	Local dest:TStream=WriteFile("bbvkey.bmx")
+	If Not dest Return
+	WriteLine dest,"' virtual key support for legacy Blitz apps"
+	WriteLine dest,""
+	WriteLine dest,"Global VKEY[]=[.."
+	WriteLine dest,"0,KEY_ESCAPE,KEY_1,KEY_2,KEY_3,KEY_4,KEY_5,KEY_6,KEY_7,KEY_8,KEY_9,KEY_0,.."
+	WriteLine dest,"KEY_MINUS,KEY_EQUALS,KEY_BACKSPACE,KEY_TAB,KEY_Q,KEY_W,KEY_E,KEY_R,KEY_T,.."
+	WriteLine dest,"KEY_Y,KEY_U,KEY_I,KEY_O,KEY_P,KEY_OPENBRACKET,KEY_CLOSEBRACKET,KEY_RETURN,.."
+	WriteLine dest,"KEY_LCONTROL,KEY_A,KEY_S,KEY_D,KEY_F,KEY_G,KEY_H,KEY_J,KEY_K,KEY_L,.."
+	WriteLine dest,"KEY_SEMICOLON,KEY_QUOTES,KEY_TILDE,KEY_LSHIFT,KEY_BACKSLASH,.."
+	WriteLine dest,"KEY_Z,KEY_X,KEY_C,KEY_V,KEY_B,KEY_N,KEY_M,KEY_COMMA,KEY_PERIOD,KEY_SLASH,.."
+	WriteLine dest,"KEY_RSHIFT,KEY_NUMMULTIPLY,KEY_ALT,KEY_SPACE,KEY_CAPSLOCK,.."
+	WriteLine dest,"KEY_F1,KEY_F2,KEY_F3,KEY_F4,KEY_F5,KEY_F6,KEY_F7,KEY_F8,KEY_F9,KEY_F10,.."
+	WriteLine dest,"KEY_NUMLOCK,KEY_SCROLL,KEY_NUM7,KEY_NUM8,KEY_NUM9,KEY_NUMSUBTRACT,KEY_NUM4,.."
+	WriteLine dest,"KEY_NUM5,KEY_NUM6,KEY_NUMADD,KEY_NUM1,KEY_NUM2,KEY_NUM3,KEY_NUM0,.."
+	WriteLine dest,"KEY_NUMDECIMAL,KEY_NUMSLASH,KEY_F11,KEY_F12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,.."
+	WriteLine dest,"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,.."
+	WriteLine dest,"KEY_EQUALS,0,0,KEY_MEDIA_PREV_TRACK,0,0,0,0,0,0,0,0,KEY_MEDIA_NEXT_TRACK,0,0,.."
+	WriteLine dest,"KEY_ENTER,KEY_RCONTROL,0,0,KEY_VOLUME_MUTE,0,KEY_MEDIA_PLAY_PAUSE,0,.."
+	WriteLine dest,"KEY_MEDIA_STOP,0,0,0,0,0,0,0,0,0,KEY_VOLUME_DOWN,0,KEY_VOLUME_UP,0,.."
+	WriteLine dest,"KEY_BROWSER_HOME,KEY_DECIMAL,0,KEY_NUMDIVIDE,0,KEY_SCREEN,.."
+	WriteLine dest,"0,0,0,0,0,0,0,0,0,0,0,0,0,KEY_PAUSE,0,KEY_HOME,KEY_UP,KEY_PAGEUP,0,.."
+	WriteLine dest,"KEY_LEFT,0,KEY_RIGHT,0,KEY_END,KEY_DOWN,KEY_PAGEDOWN,KEY_INSERT,KEY_DELETE,.."
+	WriteLine dest,"0,0,0,0,0,0,0,KEY_LWIN,KEY_RWIN,0,0,0,0,0,0,0,0,KEY_BROWSER_SEARCH,.."
+	WriteLine dest,"KEY_BROWSER_FAVORITES,KEY_BROWSER_REFRESH,KEY_BROWSER_STOP,KEY_BROWSER_FORWARD,.."
+	WriteLine dest,"KEY_BROWSER_BACK,0,KEY_LAUNCH_MAIL,KEY_LAUNCH_MEDIA_SELECT]"
+	WriteLine dest,""	
+	WriteLine dest,"Function VKeyDown(key);return KeyDown(VKEY[key]);End Function"
+	WriteLine dest,"Function VKeyHit(key);return KeyHit(VKEY[key]);End Function"
+	WriteLine dest,""
+	WriteLine dest,"'currently unsupported in BlitzMax"	
+	WriteLine dest,""
+	WriteLine dest,"Function Locate( x,y );return 0;End Function"
+	WriteLine dest,"Function MouseZSpeed();return 0;End Function"
+	WriteLine dest,"Function FreeBank(bank);return 0;End Function"
+	WriteLine dest,"Function LoopSound(sound);return 0;End Function"
+	WriteLine dest,"Function ChannelPitch(channel,hz);return 0;End Function"
+	WriteLine dest,"Function PlayCDTrack( track,mode=0 );return 0;End Function"
+	WriteLine dest,"Function SoundVolume( sound,volume# );return 0;End Function"
+	WriteLine dest,""
+	CloseFile dest
+End Function
+	
+Function WriteBBTypeFile()
+	Local dest:TStream=WriteFile("bbtype.bmx")
+	If Not dest Return
+	WriteLine dest,"' BBType adds legacy Type functionality to BlitzMax Type"
+	WriteLine dest,""
+	WriteLine dest,"Type TBBType"
+	WriteLine dest,""
+	WriteLine dest,"	Field _list:TList"
+	WriteLine dest,"	Field _link:TLink"
+	WriteLine dest,""
+	WriteLine dest,"	Method Add(t:TList)"
+	WriteLine dest,"		_list=t"
+	WriteLine dest,"		_link=_list.AddLast(self)"
+	WriteLine dest,"	End Method"
+	WriteLine dest,""
+	WriteLine dest,"	Method InsertBefore(t:TBBType)"
+	WriteLine dest,"		_link.Remove"
+	WriteLine dest,"		_link=_list.InsertBeforeLink(self,t._link)"
+	WriteLine dest,"	End Method"
+	WriteLine dest,""
+	WriteLine dest,"	Method InsertAfter(t:TBBType)"
+	WriteLine dest,"		_link.Remove"
+	WriteLine dest,"		_link=_list.InsertAfterLink(self,t._link)"
+	WriteLine dest,"	End Method"
+	WriteLine dest,""
+	WriteLine dest,"	Method Remove()"
+	WriteLine dest,"		_list.remove self"
+	WriteLine dest,"	End Method"
+	WriteLine dest,""
+	WriteLine dest,"End Type"
+	WriteLine dest,""
+	WriteLine dest,"Function DeleteLast(t:TBBType)"
+	WriteLine dest,"	if t TBBType(t._list.Last()).Remove()"
+	WriteLine dest,"End Function"
+	WriteLine dest,""
+	WriteLine dest,"Function DeleteFirst(t:TBBType)"
+	WriteLine dest,"	if t TBBType(t._list.First()).Remove()"
+	WriteLine dest,"End Function"
+	WriteLine dest,""
+	WriteLine dest,"Function DeleteEach(t:TBBType)"
+	WriteLine dest,"	if t t._list.Clear()"
+	WriteLine dest,"End Function"
+	WriteLine dest,""
+	WriteLine dest,"Function ReadString$(in:TStream)"
+	WriteLine dest,"	local	length"
+	WriteLine dest,"	length=readint(in)"
+	WriteLine dest,"	if length>0 and length<1024*1024 return brl.stream.readstring(in,length)"
+	WriteLine dest,"End Function"
+	WriteLine dest,""
+	WriteLine dest,"Function HandleToObject:Object(obj:Object)"
+	WriteLine dest,"	Return obj"
+	WriteLine dest,"End Function"
+	WriteLine dest,""
+	WriteLine dest,"Function HandleFromObject(obj:Object)"
+	WriteLine dest,"	Local h=HandleToObject(obj)"
+	WriteLine dest,"	Return h"
+	WriteLine dest,"End Function"
+	WriteLine dest,""
+	WriteLine dest,""
+	CloseFile dest
+End Function

+ 282 - 0
bmk_config.bmx

@@ -0,0 +1,282 @@
+
+Strict
+
+Import BRL.MaxUtil
+
+Import Pub.MacOS
+
+Const BMK_VERSION:String = "2.00"
+
+Const ALL_SRC_EXTS$="bmx;i;c;m;h;cpp;cxx;mm;hpp;hxx;s"
+
+Global opt_arch$
+Global opt_server$
+Global opt_outfile$
+Global opt_framework$
+Global opt_apptype$="console"
+Global opt_debug=False
+Global opt_threaded=False
+Global opt_release=False
+Global opt_configmung$=""
+Global opt_kill=False
+Global opt_username$="nobody"
+Global opt_password$="anonymous"
+Global opt_modfilter$="."
+Global opt_all=False
+Global opt_quiet=False
+Global opt_verbose=False
+Global opt_execute=False
+Global opt_proxy$
+Global opt_proxyport
+Global opt_traceheaders
+Global opt_appstub$="brl.appstub" ' BaH 28/9/2007
+Global opt_universal=False
+Global opt_target_platform:String
+
+Global opt_dumpbuild
+
+'Global cfg_platform$
+Global macos_version
+
+?MacOS
+
+'cfg_platform="macos"
+Gestalt Asc("s")Shl 24|Asc("y")Shl 16|Asc("s")Shl 8|Asc("v"),macos_version
+
+?MacOsPPC
+If is_pid_native(0) opt_arch="ppc" Else opt_arch="x86"
+
+?MacOsX86
+If is_pid_native(0) opt_arch="x86" Else opt_arch="ppc"
+
+?Win32
+
+opt_arch="x86"
+'cfg_platform="win32"
+
+?Linux
+
+opt_arch="x86"
+'cfg_platform="linux"
+
+?
+
+ChangeDir LaunchDir
+
+Function CmdError(details:String = Null, fullUsage:Int = False)
+	Local s:String = "Command line error"
+	If details Then
+		s:+ " : " + details
+	End If
+	s:+ "~n"
+	
+	s:+ Usage(fullUsage)
+	
+	Throw s
+End Function
+
+Function ParseConfigArgs$[]( args$[] )
+
+	Local n
+	
+	If getenv_( "BMKDUMPBUILD" )
+		opt_dumpbuild=1
+		opt_quiet=True
+	EndIf
+	
+	For n=0 Until args.length
+		Local arg$=args[n]
+		If arg[..1]<>"-" Exit
+		Select arg[1..]
+		Case "a"
+			opt_all=True
+		Case "q"
+			opt_quiet=True
+		Case "v"
+			opt_verbose=True
+		Case "x"
+			opt_execute=True
+		Case "d"
+			opt_debug=True
+			opt_release=False
+		Case "r"
+			opt_debug=False
+			opt_release=True
+		Case "h"
+			opt_threaded=True
+		Case "k"
+			opt_kill=True
+		Case "z"
+			opt_traceheaders=True
+		Case "y"
+			n:+1
+			If n=args.length CmdError "Missing arg for '-y'"
+			opt_proxy=args[n]
+			Local i=opt_proxy.Find(":")
+			If i<>-1
+				opt_proxyport=Int( opt_proxy[i+1..] )
+				opt_proxy=opt_proxy[..i]
+			EndIf
+		Case "g"
+			n:+1
+			If n=args.length CmdError "Missing arg for '-g'"
+			opt_arch=args[n].ToLower()
+		Case "t"
+			n:+1
+			If n=args.length CmdError "Missing arg for '-t'"
+			opt_apptype=args[n].ToLower()
+		Case "o"
+			n:+1
+			If n=args.length CmdError "Missing arg for '-o'"
+			opt_outfile=args[n]
+		Case "f"
+			n:+1
+			If n=args.length CmdError "Missing arg for '-f'"
+			opt_framework=args[n]
+		Case "s"
+			n:+1
+			If n=args.length CmdError "Missing arg for '-s'"
+			opt_server=args[n]
+		Case "u"
+			n:+1
+			If n=args.length CmdError "Missing arg for '-u'"
+			opt_username=args[n]
+		Case "p"
+			n:+1
+			If n=args.length CmdError "Missing arg for '-p'"
+			opt_password=args[n]
+		Case "b"
+			n:+1
+			If n=args.length CmdError "Missing arg for '-b'"
+			opt_appstub=args[n]
+		Case "i"
+?macos
+			' this is mac only... pah!
+			opt_universal = True
+?
+		Case "l"
+			n:+1
+			If n=args.length CmdError "Missing arg for '-l'"
+			opt_target_platform=args[n].ToLower()
+			If opt_target_platform <> "win32" And opt_target_platform <> "mac" And opt_target_platform <> "linux" CmdError "Not valid platform : '" + opt_target_platform + "'"
+		Default
+			CmdError "Invalid option '" + arg[1..] + "'"
+		End Select
+	Next
+	
+	Return args[n..]
+
+End Function
+
+
+Function CQuote$( t$ )
+	If t And t[0]=Asc("-") Return t
+	For Local i=0 Until t.length
+		If t[i]=Asc(".") Continue
+		If t[i]=Asc("/") Continue
+'If processor.Platform() = "win32"
+		If t[i]=Asc("\") Continue
+'End If
+		If t[i]=Asc("_") Or t[i]=Asc("-") Continue
+		If t[i]>=Asc("0") And t[i]<=Asc("9") Continue
+		If t[i]>=Asc("A") And t[i]<=Asc("Z") Continue
+		If t[i]>=Asc("a") And t[i]<=Asc("z") Continue
+		Return "~q"+t+"~q"
+	Next
+	Return t
+End Function
+
+Function CharIsDigit:Int( ch:Int )
+	Return ch>=Asc("0") And ch<=Asc("9")
+End Function
+
+Function CharIsAlpha:Int( ch:Int )
+	Return ch=Asc("_") Or (ch>=Asc("a") And ch<=Asc("z")) Or (ch>=Asc("A") And ch<=Asc("Z"))
+End Function
+
+
+Function Usage:String(fullUsage:Int = False)
+	Local s:String = "~nUsage: bmk <operation> [options] source~n~n"
+
+	If Not fullUsage Then
+		s:+ "(start bmk with no parameters for more usage information)~n~n"
+	Else
+		s:+ "Operations :~n"
+		s:+ "~tmakeapp~n"
+		s:+ "~t~tBuilds an application from a single root source file."
+		s:+ "~n~n"
+		s:+ "~tmakemods~n"
+		s:+ "~t~tBuilds a set of modules."
+		s:+ "~n~n"
+		s:+ "Options :~n"
+		s:+ "~t-a~n"
+		s:+ "~t~tRecompile all source/modules regardless of timestamp. By default, only those modified~n" + ..
+		    "~t~tsince the last build are recompiled."
+		s:+ "~n~n"
+		s:+ "~t-b <custom appstub module>~n"
+		s:+ "~t~tBuilds an app using a custom appstub (i.e. not BRL.Appstub).~n"
+		s:+ "~t~tThis can be useful when you want more control over low-level application state."
+		s:+ "~n~n"
+		s:+ "~t-d~n"
+		s:+ "~t~tBuilds a debug version. (This is the default for makeapp)."
+		s:+ "~n~n"
+		s:+ "~t-h~n"
+		s:+ "~t~tBuild multithreaded version. (By default, the single threaded version is built.)"
+		s:+ "~n~n"
+		s:+ "~t-i~n"
+		s:+ "~t~tCreates a Universal build on Mac x86 systems.~n"
+		s:+ "~t~t(see documentation for full list of requirements)"
+		s:+ "~n~n"
+		s:+ "~t-l <target platfom>~n"
+		s:+ "~t~tCross-compiles to the specific target platform.~n"
+		s:+ "~t~tCurrently, only win32 is supported as a target platform on Mac and Linux systems.~n"
+		s:+ "~t~t(see documentation for full list of requirements)"
+		s:+ "~n~n"
+		s:+ "~t-o <output file>~n"
+		s:+ "~t~tSpecify output file. (makeapp only)~n"
+		s:+ "~t~tBy default, the output file is placed into the same directory as the root source file."
+		s:+ "~n~n"
+		s:+ "~t-q~n"
+		s:+ "~t~tQuiet build."
+		s:+ "~n~n"
+		s:+ "~t-r~n"
+		s:+ "~t~tBuilds a release version."
+		s:+ "~n~n"
+		s:+ "~t-t <app type>~n"
+		s:+ "~t~tSpecify application type. (makeapp only)~n"
+		s:+ "~t~tShould be either 'console' or 'gui' (without single quote!).~n"
+		s:+ "~t~tThe default is console."
+		s:+ "~n~n"
+		s:+ "~t-v~n"
+		s:+ "~t~tVerbose (noisy) build."
+		s:+ "~n~n"
+		s:+ "~t-x~n"
+		s:+ "~t~tExecute built application. (makeapp only)"
+		s:+ "~n~n"
+	End If
+	
+	Return s
+End Function
+
+Function VersionInfo()
+	Local s:String = "bmk "
+	s:+ BMK_VERSION + " "
+?threaded
+	s:+ "mt-"
+?
+?win32
+	s:+ "win32"
+?linux
+	s:+ "linux"
+?macos
+	s:+ "macos"
+?
+	s:+ "-"
+?x86
+	s:+ "x86"
+?ppc
+	s:+ "ppc"
+?
+	Print s + "~n"
+End Function
+

+ 481 - 0
bmk_make.bmx

@@ -0,0 +1,481 @@
+
+Strict
+
+Import "bmk_modutil.bmx"
+
+Rem
+Experimental speedup hack by Mark!
+
+Should allow you to modify non-interface affecting code without triggering lots of recompiles.
+
+Works by determining whether blah.bmx's .i file physically changes after blah.bmx is compiled.
+
+If not, then anything importing blah.bmx may not need to be recompiled.
+
+Uses a new '.i2' file which is updated only when actual .i file content changes.
+End Rem
+Global EXPERIMENTAL_SPEEDUP
+
+Local t$=getenv_( "BMK_SPEEDUP" )
+If t EXPERIMENTAL_SPEEDUP=True
+
+Type TFile
+
+	Field path$,time
+
+	Function Create:TFile( path$,files:TList, time:Int = 0 )
+		Local f:TFile=New TFile
+		f.path=path
+		If time Then
+			f.time = time
+		Else
+			f.time=FileTime(path)
+		End If
+		If files files.AddFirst f
+		Return f
+	End Function
+
+End Type
+
+Global make:TMake = New TMake
+
+
+Type TMake
+
+	Method New()
+		LuaRegisterObject Self,"make"
+	End Method
+
+	'Method CC(src_path:String)
+	'	MakeSrc(RealPath(src_path), True)
+	'End Method
+	
+	Method Make(src_path:String)
+		MakeSrc(RealPath(src_path), True)
+	End Method
+
+End Type
+
+
+
+Global cc_opts$
+Global bcc_opts$
+Global app_main$
+Global app_type$
+Global src_files:TList
+Global obj_files:TList
+Global lnk_files:TList
+Global tmp_stack:TList
+Global ext_files:TList
+
+Function Push( o:Object )
+	tmp_stack.AddLast o
+End Function
+
+Function Pop:Object() 
+	Return tmp_stack.RemoveLast()
+End Function
+
+Function FindFile:TFile( path$,files:TList )
+	path=path.ToLower()
+	Local f:TFile
+	For f=EachIn files
+		If f.path.ToLower()=path Return f
+	Next
+End Function
+
+Function MaxTime( files:TList )
+	Local f:TFile,t
+	For f=EachIn files
+		If f.time>t t=f.time
+	Next
+	Return t
+End Function
+
+Function FilePaths:TList( files:TList )
+	Local f:TFile,p:TList=New TList
+	For f=EachIn files
+		p.AddLast f.path
+	Next
+	Return p
+End Function
+
+Function AddList( src:TList,dst:TList )
+	Local t:Object
+	For t=EachIn src
+		dst.AddLast t
+	Next
+End Function
+
+Function BeginMake()
+	cc_opts=Null
+	bcc_opts=Null
+	app_main=Null
+	src_files=New TList
+	obj_files=New TList
+	lnk_files=New TList
+	tmp_stack=New TList
+	ext_files=New TList
+	opt_framework=""
+End Function
+
+'returns mod interface file
+Function MakeMod:TFile( mod_name$ )
+
+	Local path$=ModulePath(mod_name)
+	Local id$=ModuleIdent(mod_name)
+	Local src_path$=path+"/"+id+".bmx"
+	Local arc_path$=path+"/"+id+opt_configmung+processor.CPU()+".a"
+	Local iface_path$=path+"/"+id+opt_configmung+processor.CPU()+".i"
+
+	mod_opts = New TModOpt ' BaH
+	
+	Local iface:TFile=FindFile( iface_path,src_files )
+	If iface Return iface
+
+' commented out, because it throws asserts all the time in debug mode. So,
+' either it shouldn't be checking it here, or something else isn't right. I vote the former.
+'	Assert Not FindFile( arc_path,lnk_files )
+
+	Local arc:TFile=TFile.Create( arc_path,Null )
+
+	If (mod_name+".").Find(opt_modfilter)=0 And FileType(src_path)=FILETYPE_FILE
+
+		globals.PushAll()
+		Push cc_opts
+		Push bcc_opts
+		Push obj_files
+
+		globals.SetVar("universal", String(opt_universal))
+		
+		cc_opts=""
+		'cc_opts:+" -I"+CQuote(path)
+		globals.AddOption("cc_opts", "filepath", "-I"+CQuote(path))
+		'cc_opts:+" -I"+CQuote(ModulePath(""))
+		globals.AddOption("cc_opts", "modulepath", "-I"+CQuote(ModulePath("")))
+		If opt_release Then
+			'cc_opts:+" -DNDEBUG"
+			globals.AddOption("cc_opts", "nodebug", "-DNDEBUG")
+		End If
+		If opt_threaded Then
+			'cc_opts:+" -DTHREADED"
+			globals.AddOption("cc_opts", "threaded", "-DTHREADED")
+		End If
+
+		bcc_opts=" -g "+processor.CPU()
+		bcc_opts:+" -m "+mod_name$
+		If opt_quiet bcc_opts:+" -q"
+		If opt_verbose bcc_opts:+" -v"
+		If opt_release bcc_opts:+" -r"
+		If opt_threaded bcc_opts:+" -h"
+
+		obj_files=New TList
+		
+		Local force_build:Int = False
+		If Not FileType(iface_path) Then
+			' if the interface file is missing... we *really* want to force a recompile
+			force_build = True
+		End If
+
+		MakeSrc src_path,True, force_build
+
+?threaded
+		processManager.WaitForThreads()
+?			
+		If MaxTime( obj_files )>arc.time Or opt_all
+			If Not opt_quiet Print "Archiving:"+StripDir(arc_path)
+			CreateArc arc_path,FilePaths( obj_files )
+			arc.time=FileTime(arc_path)
+		EndIf
+
+		obj_files=TList(Pop())
+		bcc_opts=String(Pop())
+		cc_opts=String(Pop())
+		globals.PopAll()
+	EndIf
+
+	Local src:TFile=MakeSrc( iface_path,False )
+
+	lnk_files.AddFirst arc
+
+	Return src
+
+End Function
+
+'adds to obj_files
+'returns input src file
+Function MakeSrc:TFile( src_path$,buildit, force_build:Int = False )
+'Print "MakeSrc : " + src_path
+	Local src:TFile=FindFile( src_path,src_files )
+	If src Return src
+
+	If FileType(src_path)<>FILETYPE_FILE Return
+
+	src=TFile.Create( src_path,src_files )
+
+	Local src_file:TSourceFile=ParseSourceFile( src_path )
+	If Not src_file Return
+	
+	Local main_file=(src_path=app_main)
+	
+	Local keep_opts:TModOpt = mod_opts ' BaH
+	If mod_opts Then
+		globals.SetVar("mod_ccopts", String(mod_opts.cc_opts))
+	End If
+	
+	If main_file
+		If src_file.framewk
+			If opt_framework Throw "Framework already specified on commandline"
+			opt_framework=src_file.framewk
+			bcc_opts:+" -f "+opt_framework
+			MakeMod opt_framework
+		Else
+			If app_type="bmx"
+				For Local t$=EachIn EnumModules()
+					If t.Find("brl.")=0 Or t.Find("pub.")=0
+						If t<>"brl.blitz" And t<>opt_appstub MakeMod t
+					EndIf
+				Next
+			EndIf
+		EndIf
+	Else If src_file.framewk
+		Throw "Framework must appear in main source file"
+	EndIf
+	
+	mod_opts = keep_opts ' BaH
+	If mod_opts Then
+		globals.SetVar("mod_ccopts", String(mod_opts.cc_opts))
+	End If
+	
+	globals.PushAll()
+	push cc_opts
+	Push CurrentDir()
+	
+	ChangeDir ExtractDir( src_path )
+	
+	Local src_ext$=ExtractExt( src_path ).ToLower()
+	
+	Local src_type:Int = String(RunCommand("source_type", [src_ext])).ToInt()
+
+	If src_type & (SOURCE_BMX | SOURCE_IFACE)
+		'incbins
+		For Local inc$=EachIn src_file.incbins
+			Local time=FileTime( inc )
+			If time>src.time src.time=time
+		Next
+		'includes
+		For Local inc$=EachIn src_file.includes
+			Local inc_ext$=ExtractExt(inc).ToLower()
+			If Match(inc_ext,"bmx")
+				Local dep:TFile=MakeSrc(RealPath(inc),False)
+				If Not dep Continue
+				If dep.time>src.time src.time=dep.time
+			Else
+				Throw "Unrecognized Include file type: "+inc
+			EndIf
+		Next
+
+		'module imports
+		For Local imp$=EachIn src_file.modimports
+			Local dep:TFile=MakeMod(imp)
+			If Not dep Continue
+			'cc_opts:+" -I"+CQuote(ExtractDir(dep.path))
+			globals.AddOption("cc_opts", "include_"+imp, "-I"+CQuote(ExtractDir(dep.path)))
+			If dep.time>src.time src.time=dep.time
+		Next
+
+		mod_opts = keep_opts ' BaH
+		If mod_opts Then
+			globals.SetVar("mod_ccopts", String(mod_opts.cc_opts))
+		End If
+
+		For Local imp$=EachIn mod_opts.ld_opts ' BaH
+			ext_files.AddLast TModOpt.setPath(imp, ExtractDir(src_path))
+		Next
+
+		'quoted imports
+		For Local imp$=EachIn src_file.imports
+			If imp[0]=Asc("-")
+				ext_files.AddLast imp
+				Continue
+			EndIf
+			Local imp_ext$=ExtractExt(imp).ToLower()
+			If Match( imp_ext,"h;hpp;hxx" )
+				'cc_opts:+" -I"+CQuote(RealPath(ExtractDir(imp)))
+				globals.AddOption("cc_opts", "include_" + imp, "-I"+CQuote(RealPath(ExtractDir(imp))))
+			Else If Match( imp_ext,"o;a;lib" )
+				ext_files.AddLast RealPath(imp)
+			Else If Match( imp_ext,ALL_SRC_EXTS )
+
+				Local dep:TFile=MakeSrc(RealPath(imp),True)
+
+				If Not dep Or Not Match( imp_ext,"bmx;i" ) Continue
+				
+				If EXPERIMENTAL_SPEEDUP And Match( imp_ext,"bmx" )
+					Local p$=ExtractDir( dep.path )+"/.bmx"
+					Local i_path$=p+"/"+StripDir( dep.path )+opt_configmung+processor.CPU()+".i2"
+					If FileType( i_path )=FILETYPE_FILE
+						Local i_time=FileTime( i_path )
+						If i_time>src.time src.time=i_time
+					Else
+						If dep.time>src.time src.time=dep.time
+					EndIf
+				Else
+					If dep.time>src.time src.time=dep.time
+				EndIf
+				
+			Else
+				Throw "Unrecognized Import file type: "+imp
+			EndIf
+		Next
+	Else If src_type & (SOURCE_C | SOURCE_HEADER) 'If Match( src_ext,"c;m;cpp;cxx;mm;h;hpp;hxx" )
+		For Local inc$=EachIn src_file.includes
+			Local inc_ext$=ExtractExt(inc).ToLower()
+			Local inc_type:Int = String(RunCommand("source_type", [inc_ext])).ToInt()
+			If Not inc_type & SOURCE_HEADER 'Match(inc_ext,"h;hpp;hxx")
+				Continue
+			EndIf
+			Local path$=RealPath(inc)
+			Local dep:TFile=MakeSrc(path,False)
+			If dep And dep.time>src.time src.time=dep.time
+			If Not opt_traceheaders Continue
+			Local src$=StripExt(path)+".cpp"
+			If FileType(src)<>FILETYPE_FILE
+				src=""
+			EndIf
+			If Not src Continue
+			MakeSrc src,True
+		Next
+	EndIf
+	
+	If buildit And src_type & (SOURCE_BMX | SOURCE_C | SOURCE_ASM) 'Match( src_ext,"bmx;c;m;cpp;cxx;mm;s;asm;cc" )
+
+		Local p$=ExtractDir( src_path )+"/.bmx"
+		
+		If opt_dumpbuild Or FileType( p )=FILETYPE_NONE
+			CreateDir p
+			'Sys "mkdir "+p   'Windows no likey...
+		EndIf
+		
+		If FileType( p )<>FILETYPE_DIR Throw "Unable to create temporary directory"
+
+		Local obj_path$=p+"/"+StripDir( src_path )
+		If main_file obj_path:+"."+opt_apptype
+		obj_path:+opt_configmung+processor.CPU()+".o"
+		
+		Local obj:TFile
+		Local time:Int
+		
+		' Has the source been changed since we last compiled?
+		If src.time>FileTime( obj_path ) Or opt_all Or force_build
+
+			' pragmas
+			For Local pragma:String = EachIn src_file.pragmas
+				processor.ProcessPragma(pragma)
+			Next
+
+			If Not opt_quiet Print "Compiling:"+StripDir(src_path)
+			Select src_type
+			Case SOURCE_BMX
+				Local opts$=bcc_opts
+				If main_file opts=" -t "+opt_apptype+opts
+			
+				CompileBMX src_path,obj_path,opts
+						
+				If EXPERIMENTAL_SPEEDUP
+					Local i_path$=StripExt( obj_path )+".i"
+
+					If FileType( i_path )=FILETYPE_FILE
+				
+						Local i_path2$=i_path+"2",update=True
+
+						If Not opt_all And FileType( i_path2 )=FILETYPE_FILE And src.time=FileTime( src.path )
+							If FileSize( i_path )=FileSize( i_path2 )
+								Local i_bytes:Byte[]=LoadByteArray( i_path )
+								Local i_bytes2:Byte[]=LoadByteArray( i_path2 )
+								If i_bytes.length=i_bytes2.length And memcmp_( i_bytes,i_bytes2,i_bytes.length )=0
+									update=False
+								EndIf
+							EndIf
+						EndIf
+						If update CopyFile i_path,i_path2
+					EndIf
+				EndIf
+
+
+			Case SOURCE_C '"c","m","cpp","cxx","mm"
+				CompileC src_path,obj_path,cc_opts
+?threaded
+				time_(Varptr time)
+?
+
+			Case SOURCE_ASM '"s","asm"
+				Assemble src_path,obj_path
+			End Select
+
+		EndIf
+
+		obj = TFile.Create( obj_path,obj_files, time )
+		lnk_files.AddFirst obj
+	EndIf
+
+	ChangeDir String(Pop())
+	cc_opts=String(Pop())
+	globals.PopAll()
+	
+	Return src
+	
+End Function
+
+Function MakeApp:TFile( Main$,makelib )
+
+	app_main=Main
+	
+	cc_opts=""
+
+	globals.AddOption("cc_opts", "modulepath", "-I"+CQuote(ModulePath("")))
+	If opt_release Then
+		globals.AddOption("cc_opts", "nodebug", "-DNDEBUG")
+	End If
+
+	globals.SetVar("universal", String(opt_universal))
+
+	bcc_opts=" -g "+processor.CPU()
+	If opt_quiet bcc_opts:+" -q"
+	If opt_verbose bcc_opts:+" -v"
+	If opt_release bcc_opts:+" -r"
+	If opt_threaded bcc_opts:+" -h"
+	If opt_framework bcc_opts:+" -f "+opt_framework
+	
+	Local app_ext$=ExtractExt( app_main ).ToLower()
+	Local _type:Int = String(RunCommand("source_type", [app_ext])).ToInt()
+	Select _type
+	Case SOURCE_BMX
+		app_type="bmx"
+		MakeMod "brl.blitz"
+		MakeSrc Main,True
+		MakeMod opt_appstub
+	Case SOURCE_C '"c","cpp","cxx","mm"
+		app_type="c/c++"
+		If opt_framework MakeMod opt_framework
+		MakeSrc Main,True
+	Default
+		Throw "Unrecognized app source file extension:"+app_ext
+	End Select
+	
+?threaded
+		processManager.WaitForThreads()
+?
+	If MaxTime( lnk_files )>FileTime( opt_outfile ) Or opt_all
+		If Not opt_quiet Print "Linking:"+StripDir( opt_outfile )
+		lnk_files=FilePaths( lnk_files )
+		AddList ext_files,lnk_files
+		LinkApp opt_outfile,lnk_files,makelib
+	EndIf
+	
+	' post process
+	LoadBMK(ExtractDir(Main) + "/post.bmk")
+	
+	app_main=""
+
+End Function

+ 60 - 0
bmk_modinfo.bmx

@@ -0,0 +1,60 @@
+
+Strict
+
+Import "bmk_modutil.bmx"
+Import "bmk_util.bmx"
+
+Type TInfo
+	Field info:TList=New TList
+	
+	Method Find$( key$ )
+		key=key.ToLower()+":"
+		For Local t$=EachIn info
+			If t.ToLower()[..Len(key)]=key Return t[Len(key)..].Trim()
+		Next
+	End Method
+	
+	Method ReadFromStream:TModInfo( stream:TStream )
+		While Not stream.Eof()
+			Local t$=stream.ReadLine()
+			If Not t Return
+			info.AddLast t
+		Wend
+	End Method
+End Type
+
+Type TModInfo Extends TInfo
+
+	Field name$
+	Field version#
+	Field modprivs$
+	Field modserver$
+	Field serverinfo:Object
+
+	Function CreateFromModule:TModInfo( name$ )
+		Local path$=ModuleInterface( name,"release."+processor.Platform()+"."+processor.CPU() )
+		If FileType(path)<>FILETYPE_FILE Return
+		Local src:TSourceFile=ParseSourceFile( path )
+		If Not src Return
+		Local modinfo:TModInfo=New TModInfo
+		modinfo.name=name
+		modinfo.info=src.info
+		modinfo.info.AddFirst "Module: "+name
+		modinfo.version=Float( modinfo.Find( "Version" ) )
+		modinfo.modserver=modinfo.Find( "ModServer" )
+		Return modinfo
+	End Function
+	
+	Function CreateFromStream:TModInfo( stream:TStream )
+		Local modinfo:TModInfo=New TModInfo
+		modinfo.ReadFromStream stream
+		modinfo.name=modinfo.Find( "Module" )
+		If Not modinfo.name Return
+		modinfo.version=Float( modinfo.Find( "Version" ) )
+		modinfo.modprivs=modinfo.Find( "ModPrivs" )
+		modinfo.modserver=modinfo.Find( "ModServer" )
+		Return modinfo
+	End Function
+
+End Type
+

+ 239 - 0
bmk_modutil.bmx

@@ -0,0 +1,239 @@
+
+Strict
+
+Import BRL.MaxUtil
+Import BRL.TextStream
+
+Import "bmk_util.bmx"
+
+Const SOURCE_UNKNOWN:Int = 0
+Const SOURCE_BMX:Int = $01
+Const SOURCE_IFACE:Int = $02
+Const SOURCE_C:Int = $04
+Const SOURCE_HEADER:Int = $08
+Const SOURCE_ASM:Int = $10
+'Const SOURCE_PYTHON:Int = $20
+'Const SOURCE_PERL:Int = $40
+'Const SOURCE_RUBY:Int = $80
+' etc ?
+
+Type TSourceFile
+	Field ext$		'one of: "bmx", "i", "c", "cpp", "m", "s", "h"
+	Field path$
+	Field modid$
+	Field framewk$
+	Field info:TList=New TList
+
+	Field modimports:TList=New TList
+	
+	Field imports:TList=New TList
+	Field includes:TList=New TList
+	Field incbins:TList=New TList
+	
+	Field declids:TList=New TList
+	
+	Field pragmas:TList = New TList
+End Type
+
+Function ValidSourceExt( ext:Int )
+	If ext & $FFFF Then
+		Return True
+	End If
+	Return False
+End Function
+
+Function ParseSourceFile:TSourceFile( path$ )
+
+	If FileType(path)<>FILETYPE_FILE Return
+
+	Local ext$=ExtractExt( path ).ToLower()
+	Local exti:Int = String(RunCommand("source_type", [ext])).ToInt()
+
+	If Not ValidSourceExt( exti ) Return
+
+	Local file:TSourceFile=New TSourceFile
+	file.ext=ext
+	file.path=path
+	
+	Local str$=LoadText( path )
+
+	Local pos,in_rem,cc=True
+
+	While pos<Len(str)
+
+		Local eol=str.Find( "~n",pos )
+		If eol=-1 eol=Len(str)
+
+		Local line$=str[pos..eol].Trim()
+		pos=eol+1
+
+		Local pragmaLine:String
+		Local n:Int = line.Find("@")
+		If n <> -1 And line[n+1..n+4] = "bmk" Then
+			pragmaLine = line[n+4..]
+		End If
+		
+		Select exti
+		Case SOURCE_BMX, SOURCE_IFACE
+
+			n=line.Find( "'" )
+			If n<>-1 line=line[..n]
+			
+			If Not line And Not pragmaLine Continue
+
+			Local lline$=line.Tolower()
+
+			If in_rem
+				If lline[..6]="endrem" Or lline[..7]="end rem" 
+					in_rem=False
+				EndIf
+				Continue
+			Else If lline[..3]="rem"
+				in_rem=True
+				Continue
+			EndIf
+
+			If lline[..1]="?"
+				Local t$=lline[1..].Trim()
+				
+				Local cNot
+				If t.StartsWith( "not " )
+					cNot=True
+					t=t[4..].Trim()
+				EndIf
+
+				t = t.toLower()
+				Select t
+				Case ""
+					cc=True
+				Case "debug"
+					cc=opt_debug
+				Case "threaded"
+					cc=opt_threaded
+?x86
+				Case "x86" cc=processor.CPU()="x86"
+?ppc
+				Case "ppc" cc=processor.CPU()="ppc"
+?
+				Case "win32" 
+					cc=False
+					If processor.Platform() = "win32"
+						cc=True
+					End If
+				Case "win32x86"
+					cc=False
+					If processor.Platform() = "win32"
+						cc=opt_arch="x86"
+					End If
+				Case "win32ppc"
+					cc=False
+					If processor.Platform() = "win32"
+						cc=opt_arch="ppc"
+					End If
+				Case "linux"
+					cc=False
+					If processor.Platform() = "linux"
+						 cc=True
+					End If
+				Case "linuxx86"
+					cc=False
+					If processor.Platform() = "linux"
+						 cc=opt_arch="x86"
+					End If
+				Case "linuxppc"
+					cc=False
+					If processor.Platform() = "linux"
+						 cc=opt_arch="ppc"
+					End If
+				Case "macos"
+					cc=False
+					If processor.Platform() = "macos"
+						cc=True
+					End If
+				Case "macosx86"
+					cc=False
+					If processor.Platform() = "macos"
+						 cc=processor.CPU()="x86"
+					End If
+				Case "macosppc"
+					cc=False
+					If processor.Platform() = "macos"
+						 cc=processor.CPU()="ppc"
+					End If
+				Default
+					cc=False
+				End Select
+				If cNot cc=Not cc
+				Continue
+			EndIf
+
+			If Not cc Continue
+			
+			Local i:Int
+			' new pragma stuff
+			If pragmaLine Then
+				Local lpragma:String = pragmaLine.ToLower()
+				i = 0
+				While i<lpragma.length And Not (CharIsAlpha(lpragma[i]) Or CharIsDigit(lpragma[i]))
+					i:+1
+				Wend
+				file.pragmas.AddLast pragmaLine[i..]
+			End If
+
+			If Not CharIsAlpha( lline[0] ) Continue
+
+			i=1
+			While i<lline.length And (CharIsAlpha(lline[i]) Or CharIsDigit(lline[i]))
+				i:+1
+			Wend
+			If i=lline.length Continue
+			
+			Local key$=lline[..i]
+			
+			Local val$=line[i..].Trim(),qval$,qext$
+			If val.length>1 And val[0]=34 And val[val.length-1]=34
+				qval=val[1..val.length-1]
+			EndIf
+
+			Select key
+			Case "module"
+				file.modid=val.ToLower()
+			Case "framework"
+				file.framewk=val.ToLower()
+			Case "import"
+				If qval
+					file.imports.AddLast qval
+				Else
+					file.modimports.AddLast val.ToLower()
+				EndIf
+			Case "incbin"
+				If qval
+					file.incbins.AddLast qval
+				EndIf
+			Case "include"
+				If qval
+					file.includes.AddLast qval
+				EndIf
+			Case "moduleinfo"
+				If qval
+					file.info.AddLast qval
+					If mod_opts mod_opts.addOption(qval) ' BaH
+				EndIf
+			End Select
+		Case SOURCE_C, SOURCE_HEADER '"c","m","h","cpp","cxx","hpp","hxx"
+			If line[..8]="#include"
+				Local val$=line[8..].Trim(),qval$,qext$
+				If val.length>1 And val[0]=34 And val[val.length-1]=34
+					qval=val[1..val.length-1]
+				EndIf
+				If qval
+					file.includes.AddLast qval
+				EndIf
+			EndIf
+		End Select
+
+	Wend
+	
+	Return file
+
+End Function

+ 856 - 0
bmk_ng.bmx

@@ -0,0 +1,856 @@
+SuperStrict
+
+Import BRL.Reflection
+Import BRL.Map
+Import BRL.LinkedList
+
+?threaded
+Import BRL.Threads
+?
+?Not win32
+Import "waitpid.c"
+?
+
+Import "bmk_config.bmx"
+Import "bmk_ng_utils.bmx"
+
+
+Global commands:TMap = New TMap
+Global processor:TBMK = New TBMK
+Global globals:TBMKGlobals = New TBMKGlobals
+
+' load in the base stuff
+LoadBMK(AppDir + "/core.bmk")
+LoadBMK(AppDir + "/make.bmk")
+' optional
+LoadBMK(AppDir + "/config.bmk")
+
+' add some defaults
+If processor.Platform() = "macos"
+	globals.SetVar("macos_version", String(macos_version))
+End If
+globals.SetVar("cc_opts", New TOptionVariable)
+globals.SetVar("ld_opts", New TOptionVariable)
+
+Function LoadBMK(path:String)
+	processor.LoadBMK(path)
+End Function
+
+' this is the core bmk processor.
+Type TBMK
+
+	Method New()
+		LuaRegisterObject Self,"bmk"
+	End Method
+
+	' loads a .bmk, stores any functions, and runs any commands.
+	Method LoadBMK(path:String)
+
+		Local str:String
+		Try
+			If FileType(path) = 1 Then
+				str = LoadText( path )
+			Else
+				Return
+			End If
+		Catch e:Object
+			Try
+				If FileType(AppDir + "/" + path) = 1 Then
+					str = LoadText( AppDir + "/" + path )
+				Else
+					Return
+				End If
+			Catch e:Object
+				' we tried... twice
+				' fail silently...
+				Return
+			End Try
+		End Try
+	
+		Local pos:Int, inDefine:Int, text:String, name:String
+	
+		While pos < str.length
+	
+			Local eol:Int = str.Find( "~n",pos )
+			If eol = -1 Then
+				eol = str.length
+			End If
+	
+			Local line:String = str[pos..eol].Trim()
+			pos = eol+1
+			
+			ProcessLine(line, inDefine, text, name)
+
+			' anything else?
+		Wend
+	End Method
+	
+	' processes a pragma
+	Method ProcessPragma(line:String)
+		Local inDefine:Int, text:String, name:String
+		ProcessLine(line, inDefine, text, name)
+	End Method
+	
+	Method ProcessLine(line:String, inDefine:Int Var, text:String Var, name:String Var)
+	
+		If line.StartsWith("#") Then
+			Return
+		End If
+		
+		Local lline:String = line.ToLower()
+		
+		If line.StartsWith("@") Then
+			
+			If lline[1..].StartsWith("define") Then
+				
+				inDefine = True
+				name = line[8..].Trim()
+	
+				Local cmd:TBMKCommand = New TBMKCommand
+				cmd.name = name
+				commands.Insert(name.ToLower(), cmd)
+				
+				Return
+			End If
+			
+			If lline[1..].StartsWith("end") Then
+			
+				If inDefine Then
+					Local cmd:TBMKCommand = TBMKCommand(commands.ValueForKey(name.ToLower()))
+					cmd.LoadCommand(text)
+	
+					text = ""
+					inDefine = False
+				End If
+				
+				Return
+			End If
+			
+		End If
+	
+		If inDefine Then
+			text:+ line + "~n"
+			Return
+		End If
+		
+		If line.length = 0 Then
+			Return
+		End If
+		
+		' find command, and run
+		Local i:Int=1
+		While i < lline.length And (CharIsAlpha(lline[i]) Or CharIsDigit(lline[i]))
+			i:+1
+		Wend
+		'If i = lline.length Then
+		'	Continue
+		'End If
+		
+		Local command:String = lline[..i]
+		Local cmd:TBMKCommand = TBMKCommand(commands.ValueForKey(command))
+		
+		' this is a command!
+		If cmd Then
+			cmd.RunCommand(line[i+1..])
+			Return
+		End If
+		
+		' what's left?
+		' setting a variable?
+		i = line.Find("=")
+		If i <> -1 Then
+			' hmm. maybe a variable...
+			Local variable:String = line[..i].Trim()
+			Local value:String = Parse(line[i+1..].Trim())
+			globals.SetVar(variable, value)
+		End If
+	
+	End Method
+	
+	Method Parse:String(str:String)
+
+		Local done:Int
+		
+		While Not done
+		
+			Local pos:Int, restart:Int, changed:Int
+			While pos < str.length And Not restart
+		
+				Local eol:Int = str.Find( "~n",pos )
+				If eol = -1 Then
+					eol = str.length
+				End If
+		
+				Local line:String = str[pos..eol].Trim()
+				pos = eol+1
+				
+				Local i:Int
+				While i < line.length
+					i = line.find("%", i)
+					If i = -1 Then
+						i = line.length
+						Continue
+					End If
+					Local start:Int = i
+					i:+ 1
+				
+					While i < line.length And (CharIsAlpha(line[i]) Or CharIsDigit(line[i]))
+						i:+1
+					Wend
+					
+					If i > start Then
+						If line[i..i+1] = "%" Then
+							i:+ 1
+							Local toReplace:String = line[start..i]
+							' we want to replace this with something, so we
+							' will look in the globals list and env for a match.
+							' Otherwise, it will swap % with $, and leave it as is.
+							Local with:String = FindValue(toReplace)
+							If with Then
+								str = str.Replace(toReplace, with)
+								restart = True
+							End If
+						End If
+					End If
+					
+				
+				Wend
+				
+				
+			Wend
+			
+			
+			If Not restart Then
+				done = True
+			End If
+			
+		Wend
+
+		Return str
+	End Method
+	
+	Method FindValue:String(variable:String)
+		Local plainVar:String = variable.Replace("%", "")
+		Local value:String = globals.Get(plainVar)
+		
+		If value Then
+			Return value
+		End If
+		
+		' look for environment variable ?
+		Local env:String = getenv_(plainVar)
+		If env Then
+			Return env
+		End If
+		
+		' return the original
+		Return variable.Replace("%", "$")
+	End Method
+	
+	' quotes a string, if required (does it have spaces in it?)
+	Method Quote:String(t:String)
+		Return CQuote(t)
+	End Method
+	
+	' returns the platform as a string
+	Method Platform:String()
+		If Not opt_target_platform Then
+			' the native target platform
+?macos
+			Return "macos"
+?linux
+			Return "linux"
+?win32
+			Return "win32"
+?
+		Else
+			' the custom target platform
+			Return opt_target_platform
+		End If
+	End Method
+	
+	Field cputypes:String[] = ["ppc", "x86"]
+?ppc
+	Field cputype:Int = 0
+?x86
+	Field cputype:Int = 1
+?
+	
+	' returns the cpu type, as a string
+	Method CPU:String()
+		Return cputypes[cputype]
+	End Method
+	
+	Method ToggleCPU()
+		If opt_universal Then
+			cputype = 1 - cputype
+		End If
+	End Method
+	
+	Method Sys:Int(cmd:String)
+		If Int(globals.Get("verbose")) Or opt_verbose
+			Print cmd
+		Else If Int(globals.Get("dumpbuild"))
+			Local p$=cmd
+			p = p.Replace( BlitzMaxPath()+"/","./" )
+			WriteStdout p+"~n"
+			Local t$="mkdir "
+			If cmd.StartsWith( t ) And FileType( cmd[t.length..] ) Return False
+		EndIf
+		Return system_( cmd )
+	End Method
+
+	Method MultiSys:Int(cmd:String, src:String)
+		If Int(globals.Get("verbose")) Or opt_verbose
+			Print cmd
+		Else If Int(globals.Get("dumpbuild"))
+			Local p$=cmd
+			p = p.Replace( BlitzMaxPath()+"/","./" )
+			WriteStdout p+"~n"
+			Local t$="mkdir "
+			If cmd.StartsWith( t ) And FileType( cmd[t.length..] ) Return False
+		EndIf
+		
+?threaded
+		processManager.DoSystem(cmd, src)
+?Not threaded
+		Return system_( cmd )
+?
+	End Method
+
+	Method ThrowNew(e:String)
+		Throw e
+	End Method
+	
+	Method Call(name:String, args:String[])
+		RunCommand(name, args)
+	End Method
+
+	Method AddArg(option:String, extra:String)
+		Local args:String[] = [option]
+		If extra Then
+			args:+ [extra]
+		End If
+		
+		ParseConfigArgs args
+	End Method
+
+	Method Option:String(key:String, defaultValue:String)
+		Local value:String = globals.Get(key)
+		
+		If Not value Then
+			Return defaultValue
+		Else
+			Return value
+		End If
+	End Method
+	
+End Type
+
+
+' stores variables, as well as a variable stack which can be pushed and popped.
+Type TBMKGlobals
+
+	' current value of variables
+	Field vars:TMap = New TMap
+	' variable stack
+	Field stack:TMap = New TMap
+	
+	Method New()
+		LuaRegisterObject Self,"globals"
+	End Method
+
+	' sets the variable with value
+	Method SetVar(variable:String, value:Object)
+		vars.Insert(variable.ToUpper(), value)
+	End Method
+	
+	' returns the current value for variable
+	Method Get:String(variable:String)
+		Local obj:Object = vars.ValueForKey(variable.ToUpper())
+		If obj Then
+			Return obj.ToString()
+		End If
+	End Method
+	
+	Method GetRawVar:Object(variable:String)
+		Local obj:Object = vars.ValueForKey(variable.ToUpper())
+		If TOptionVariable(obj) Then
+			' return a copy of the object - any changes to this won't affect the current value.
+			Return TOptionVariable(obj).Clone()
+		End If
+		Return obj
+	End Method
+	
+	' push the variable onto the stack (save the value)
+	Method Push(variable:String)
+		variable = variable.ToUpper()
+		
+		Local list:TList = TList(stack.ValueForKey(variable))
+		If Not list Then
+			list = New TList
+			stack.Insert(variable, list)
+		End If
+		
+		list.AddLast(GetRawVar(variable))
+	End Method
+	
+	' pop the variable from the stack (load the value)
+	Method Pop(variable:String)
+		variable = variable.ToUpper()
+	
+		Local list:TList = TList(stack.ValueForKey(variable))
+		If list And Not list.IsEmpty() Then
+			SetVar(variable, list.RemoveLast())
+		End If
+	End Method
+	
+	' push all the variables
+	Method PushAll()
+		For Local v:String = EachIn vars.Keys()
+			Push(v)
+		Next
+	End Method
+	
+	' pop all the variables
+	Method PopAll()
+		For Local v:String = EachIn vars.Keys()
+			Pop(v)
+		Next
+	End Method
+	
+	' adds value to the end of variable
+	Method Add(variable:String, value:String)
+		variable = variable.ToUpper()
+
+		Local v:Object = vars.ValueForKey(variable)
+		If Not TOptionVariable(v) Then
+			SetVar(variable, String(v) + " " + value)
+		End If
+
+	End Method
+
+	Method AddOption(variable:String, key:String, value:String)
+		variable = variable.ToUpper()
+
+		Local v:Object = vars.ValueForKey(variable)
+		If TOptionVariable(v) Then
+			TOptionVariable(v).AddVar(key, value)
+		Else
+			SetVar(variable, String(v) + " " + value)
+		End If
+
+	End Method
+
+	' only appropriate for TOptionVariables
+	Method RemoveVar(variable:String, name:String)
+		variable = variable.ToUpper()
+		Local v:Object = vars.ValueForKey(variable)
+		If TOptionVariable(v) Then
+			TOptionVariable(v).RemoveVar(name)
+		End If
+	End Method
+	
+End Type
+
+Type TOpt
+	Field name:String
+	Field value:String
+End Type
+
+' holds a list of options.
+' useful for storing a list of cc_opts, for example.
+' the list can be modified as required, and cloned during push/pop calls.
+Type TOptionVariable
+
+	Field options:TMap = New TMap
+	Field orderedOptions:TList = New TList
+	
+	Method AddVar(name:String, value:String)', insertBefore:Int = False)
+		Local opt:TOpt = New TOpt
+	
+		If Not name Then
+			Global count:Int
+			count:+1
+			name = "VAR" + count
+			opt.name = name
+		End If
+		opt.value = value
+		
+		options.Insert(name, opt)
+		orderedOptions.AddLast(opt)
+		
+	End Method
+	
+	' finds and removes a matching value
+	Method RemoveVar(name:String)
+		Local opt:TOpt = TOpt(options.ValueForKey(name))
+		options.Remove(opt)
+		orderedOptions.Remove(opt)
+	End Method
+	
+	Method ToString:String()
+		Local s:String = " "
+		
+		For Local opt:TOpt = EachIn orderedOptions
+			s:+ opt.value + " "
+		Next
+		Return s
+	End Method
+	
+	' create an exact copy of me
+	Method Clone:TOptionVariable()
+		Local me:TOptionVariable = New TOptionVariable
+		For Local name:String = EachIn options.Keys()
+			me.options.insert(name, options.ValueForKey(name))
+		Next
+		For Local opt:TOpt = EachIn orderedOptions
+			me.orderedOptions.AddLast(opt)
+		Next
+		Return me
+	End Method
+
+End Type
+
+Function RunCommand:Object(command:String, args:String[])
+	Local cmd:TBMKCommand = TBMKCommand(commands.ValueForKey(command.ToLower()))
+	If cmd Then
+		' we need to add the "arg0" string to the front of the array
+		Local all:String
+		For Local i:Int = 0 Until args.length
+			Local arg:String = args[i]
+			all:+ CQuote$(arg) + " "
+		Next
+		args = [ all.Trim() ] + args
+		' now we can run the command
+		Return cmd.RunCommandArgs(args)
+	End If
+End Function
+
+' a bmk function/command
+Type TBMKCommand
+
+	Field name:String
+	Field command:String
+
+	Field argCount:Int = 0
+
+	Field class:TLuaClass
+	Field instance:TLuaObject
+	
+	Method LoadCommand(cmd:String)
+		
+		cmd = WrapVariables(ParseArgs(cmd))
+		
+	
+		Local code:String = "function bmk_" + name + "(...)~n" + ..
+			GetArgs() + ..
+			"nvl = function(a1,a2) if a1 == nil then return a2 else return a1 end end~n" + ..
+			cmd + ..
+			"end"
+
+		class = New TLuaClass.SetSourceCode( code )
+		instance = New TLuaObject.Init( class, Null )
+
+	End Method
+	
+	Method RunCommand:Object(args:String)
+		Return RunCommandArgs([args] + ExtractArgs(args))
+	End Method
+	
+	' This assumes we have arg0 + other args
+	Method RunCommandArgs:Object(args:Object[])
+		Return instance.invoke("bmk_" + name, args)
+	End Method
+
+	' handles quotes and arrays [].
+	' [] inside quotes are ignored.	
+	Method ExtractArgs:Object[](args:String)
+		Local argArray:Object[]
+
+		Local arg:String, arr:String[]
+		Local i:Int, inString:Int, inArray:Int
+		While i < args.length
+			Local c:String = args[i..i+1]
+			i:+ 1
+			
+			If c = "~q" Then
+				If inString Then
+					If Not inArray Then
+						argArray:+ [ arg ]
+					Else
+						arr:+ [ arg ]
+					End If
+
+					arg = ""
+					inString = False
+					Continue
+				Else
+					arg = ""
+					inString = True
+					Continue
+				End If
+			End If
+			
+			If c = " " And Not inString Then
+				If arg Then
+					If Not inArray Then
+						argArray:+ [ arg ]
+					Else
+						arr:+ [ arg ]
+					End If
+					arg = ""
+				End If
+				Continue
+			End If
+			
+			If c = "[" And Not inString Then
+				If Not inArray Then
+					inArray = True
+					arr = Null
+					arg = ""
+					Continue
+				End If
+			End If
+			
+			If c = "]" And Not inString Then
+				If inArray Then
+					If arg Then
+						arr:+ [ arg ]
+					End If
+					inArray = False
+					argArray:+ [ arr ]
+					arr = Null
+					arg = ""
+					Continue
+				End If
+			End If
+			
+			
+			arg:+ c
+			
+		Wend
+		
+		If arg Then
+			If arr Then
+				arr:+ [arg]
+				argArray:+ [arr]
+			Else
+				argArray:+ [arg]
+			End If
+		Else
+			If arr Then
+				argArray:+ [arr]
+			End If
+		End If
+		
+		Return argArray
+	End Method
+	
+	Method ParseArgs:String(cmd:String)
+		' This needs to process the command text to work out what args are used.
+		' so, for example, arg0, arg1 and arg2.
+		' That way, we generate the correct functionality when we build the function code.
+
+		Local pos:Int
+		While pos < cmd.length
+	
+			Local eol:Int = cmd.Find( "~n",pos )
+			If eol = -1 Then
+				eol = cmd.length
+			End If
+	
+			Local line:String = cmd[pos..eol].Trim()
+			pos = eol+1
+
+			Local i:Int
+			While i < line.length
+				i = line.find("arg", i)
+				If i = -1 Then
+					i = line.length
+					Continue
+				End If
+				
+				i:+ 3
+				Local start:Int = i
+			
+				While i < line.length And CharIsDigit(line[i])
+					i:+1
+				Wend
+
+				Local num:Int = line[start..i].ToInt()
+				If num Then
+					argCount = Max(argCount, num)
+				End If
+				
+			Wend
+		Wend
+		
+		Return cmd
+		
+	End Method
+	
+	Method GetArgs:String()
+		Local args:String = "local arg0"
+		Local rep:String = "arg0 = bmk.Parse(arg0)~n"
+		
+		If argCount > 0 Then
+			For Local i:Int = 1 To argCount
+				args:+ ",arg" + i
+				rep :+ "arg" + i + " = bmk.Parse(arg" + i + ")~n"
+			Next
+		End If
+		
+		args :+ " = unpack(arg)~n"
+		args :+ rep
+
+		Return args
+	End Method
+	
+	Method WrapVariables:String(str:String)
+
+		Local done:Int
+		
+		While Not done
+		
+			Local pos:Int, restart:Int, changed:Int
+			While pos < str.length And Not restart
+		
+				Local eol:Int = str.Find( "~n",pos )
+				If eol = -1 Then
+					eol = str.length
+				End If
+		
+				Local line:String = str[pos..eol].Trim()
+				pos = eol+1
+				
+				Local i:Int
+				While i < line.length
+					i = line.find("%", i)
+					If i = -1 Then
+						i = line.length
+						Continue
+					End If
+					Local start:Int = i
+					i:+ 1
+				
+					While i < line.length And (CharIsAlpha(line[i]) Or CharIsDigit(line[i]))
+						i:+1
+					Wend
+					
+					If i > start Then
+						If line[i..i+1] = "%" Then
+							i:+ 1
+							Local toReplace:String = line[start..i]
+
+							Local with:String = "globals.Get(~q" + toReplace.Replace("%", "") + "~q)"
+							str = str.Replace(toReplace, with)
+							restart = True
+						End If
+					End If
+					
+				Wend
+				
+			Wend
+			
+			If Not restart Then
+				done = True
+			End If
+			
+		Wend
+
+		Return str
+	End Method
+
+End Type
+
+?threaded
+Type TProcessManager
+	
+	Field cpuCount:Int
+	
+	Field threads:TList = New TList
+
+	Method New()
+		cpuCount = GetCoreCount() + 1
+	End Method
+
+	Method CheckThreads()
+		While threads.Count() = cpuCount
+			For Local thread:TThread = EachIn threads
+				If Not thread.Running() Then
+					threads.Remove(thread)
+				End If
+			Next
+			Delay 100
+		Wend
+	End Method
+	
+	Method WaitForThreads()
+		While threads.Count()
+			For Local thread:TThread = EachIn threads
+				If Not thread.Running() Then
+					threads.Remove(thread)
+				End If
+			Next
+			Delay 100
+		Wend
+	End Method
+	
+	Method DoSystem(cmd:String, src:String)
+		threads.AddLast(CreateThread(TProcessTask._DoTasks, New TProcessTask.Create(cmd, src)))
+
+		CheckThreads()
+	End Method
+
+End Type
+
+?Not win32
+Extern
+	Function fork:Int()
+	Function bmx_waitpid:Int(pid:Int)
+	Function bmx_system(cmd:Byte Ptr)
+End Extern
+?
+
+Type TProcessTask
+
+	Field command:String
+	Field source:String
+	
+	Method Create:TProcessTask(cmd:String, src:String)
+		command = cmd
+		source = src
+		Return Self
+	End Method
+
+	Function _DoTasks:Object(data:Object)
+		Return TProcessTask(data).DoTasks()
+	End Function
+	
+	Method DoTasks:Object()
+		Local res:Int
+		
+?Not win32
+		Local pid:Int = fork()
+		If Not pid Then
+			bmx_system(command)
+		Else
+			res = bmx_waitpid(pid)
+		End If
+?win32
+		res = system_(command)
+?
+
+		If res Then
+			Local s:String = "Build Error: failed to compile (" + res + ") " + source
+			Print s + "~n"
+			Throw s
+		End If
+	End Method
+
+End Type
+
+?threaded
+Global processManager:TProcessManager = New TProcessManager
+?

+ 197 - 0
bmk_ng_utils.bmx

@@ -0,0 +1,197 @@
+SuperStrict
+
+Import BRL.MaxUtil
+Import BRL.FileSystem
+Import BRL.System
+Import BRL.MaxLua
+
+'Import "lua_object.bmx"
+
+Global utils:TMaxUtils = New TMaxUtils
+Global fsys:TSystem = New TSystem
+
+' Access to BRL.MaxUtil
+Type TMaxUtils
+
+	Method New()
+		LuaRegisterObject Self,"utils"
+	End Method
+
+	Method BlitzMaxPath:String()
+		Return BRL.MaxUtil.BlitzMaxPath()
+	End Method
+	
+	Method ModulePath:String( modid$ )
+		Return BRL.MaxUtil.ModulePath(modid)
+	End Method
+
+	Method ModuleIdent:String( modid$ )
+		Return BRL.MaxUtil.ModuleIdent(modid)
+	End Method
+
+	Method ModuleSource:String( modid$ )
+		Return BRL.MaxUtil.ModuleSource(modid)
+	End Method
+
+	Method ModuleArchive:String( modid$,mung$="" )
+		Return BRL.MaxUtil.ModuleArchive(modid, mung)
+	End Method
+
+	Method ModuleInterface:String( modid$,mung$="" )
+		Return BRL.MaxUtil.ModuleInterface(modid, mung)
+	End Method
+
+End Type
+
+' Access to BRL.FileSystem and BRL.System
+Type TSystem
+
+	Method New()
+		LuaRegisterObject Self,"sys"
+	End Method
+
+	Method FixPath:String(path:String, dirPath:Int = False)
+		Local p:String = path
+		BRL.FileSystem.FixPath(p, dirPath)
+		Return p
+	End Method
+	
+	Method StripDir$( path$ )
+		Return BRL.FileSystem.StripDir(path)
+	End Method
+
+	Method StripExt$( path$ )
+		Return BRL.FileSystem.StripExt(path)
+	End Method
+
+	Method StripAll$( path$ )
+		Return BRL.FileSystem.StripAll(path)
+	End Method
+
+	Method StripSlash$( path$ )
+		Return BRL.FileSystem.StripSlash(path)
+	End Method
+
+	Method ExtractDir$( path$ )
+		Return BRL.FileSystem.ExtractDir(path)
+	End Method
+
+	Method ExtractExt$( path$ )
+		Return BRL.FileSystem.ExtractExt(path)
+	End Method
+
+	Method CurrentDir$()
+		Return BRL.FileSystem.CurrentDir()
+	End Method
+
+	Method RealPath$( path$ )
+		Return BRL.FileSystem.RealPath(path)
+	End Method
+
+	Method FileType:Int( path$ )
+		Return BRL.FileSystem.FileType(path)
+	End Method
+
+	Method CreateFile:Int( path$ )
+		Return BRL.FileSystem.CreateFile(path)
+	End Method
+
+	Method CreateDir:Int( path$,recurse:Int=False )
+		Return BRL.FileSystem.CreateDir(path, recurse)
+	End Method
+
+	Method DeleteFile:Int( path$ )
+		Return BRL.FileSystem.DeleteFile(path)
+	End Method
+
+	Method RenameFile:Int( oldpath$,newpath$ )
+		Return BRL.FileSystem.RenameFile(oldpath, newpath)
+	End Method
+
+	Method CopyFile:Int( src$,dst$ )
+		Return BRL.FileSystem.CopyFile(src, dst)
+	End Method
+
+	Method CopyDir:Int( src$,dst$ )
+		Return BRL.FileSystem.CopyDir(src, dst)
+	End Method
+
+	Method DeleteDir:Int( path$,recurse:Int=False )
+		Return BRL.FileSystem.DeleteDir(path, recurse)
+	End Method
+
+	Method ChangeDir:Int( path$ )
+		Return BRL.FileSystem.ChangeDir(path)
+	End Method
+	
+	
+	' System
+	Method CurrentDate:String()
+		Return BRL.System.CurrentDate()
+	End Method
+	
+	Method CurrentTime:String()
+		Return BRL.System.CurrentTime()
+	End Method
+
+	Method Notify(text:String, serious:Int = False)
+		BRL.System.Notify(text, serious)
+	End Method
+	
+	Method OpenURL(url:String)
+		BRL.System.OpenURL(url)
+	End Method
+	
+End Type
+
+Extern
+?macos
+	Function sysctlbyname:Int(name:Byte Ptr, count:Int Ptr, size:Int Ptr, a:Byte Ptr, b:Int)
+?linux
+	Function popen:Int(command:Byte Ptr, Mode:Byte Ptr)
+?
+End Extern
+?win32
+' http://msdn.microsoft.com/en-us/library/ms724958(VS.85).aspx
+Extern "win32"
+	Function GetSystemInfo(info:Byte Ptr)
+End Extern
+
+Type SYSTEM_INFO
+	Field wProcessorArchitecture:Short
+	Field wReserved:Short
+	Field dwPageSize:Int
+	Field lpMinimumApplicationAddress:Byte Ptr
+	Field lpMaximumApplicationAddress:Byte Ptr
+	Field dwActiveProcessorMask:Int
+	Field dwNumberOfProcessors:Int
+	Field dwProcessorType:Int
+	Field dwAllocationGranularity:Int
+	Field wProcessorLevel:Short
+	Field wProcessorRevision:Short
+End Type
+?
+
+Function GetCoreCount:Int()
+	Global count:Int
+
+	If Not count Then
+?macos
+		Local l:Int = 4
+		sysctlbyname("hw.ncpu", Varptr count, Varptr l,Null,0)
+?linux
+		Local buf:Byte[128]
+		Local fp:Int = popen("/bin/cat /proc/cpuinfo |grep -c '^processor'", "r")
+		fread_(buf, 1, 127, fp)
+		fclose_(fp)
+		count = String.FromCString(buf).ToInt()
+?win32
+		Local info:SYSTEM_INFO = New SYSTEM_INFO
+		GetSystemInfo(info)
+		count = info.dwNumberOfProcessors
+?
+	End If
+
+	Return count
+End Function
+

+ 298 - 0
bmk_util.bmx

@@ -0,0 +1,298 @@
+
+Strict
+
+Import "bmk_config.bmx"
+Import "bmk_ng.bmx"
+
+'OS X Nasm doesn't work? Used to produce incorrect reloc offsets - haven't checked for a while 
+Const USE_NASM=False
+
+Const CC_WARNINGS=False'True
+
+Type TModOpt ' BaH
+	Field cc_opts:String = ""
+	Field ld_opts:TList = New TList
+	
+	Method addOption(qval:String)
+		If qval.startswith("CC_OPTS") Then
+			cc_opts:+ " " + qval[qval.find(":") + 1..].Trim()
+		ElseIf qval.startswith("LD_OPTS") Then
+			Local opt:String = qval[qval.find(":") + 1..].Trim()
+			
+			If opt.startsWith("-L") Then
+				opt = "-L" + CQuote(opt[2..])
+			End If
+			
+			ld_opts.addLast opt
+		End If
+	End Method
+	
+	Method hasCCopt:Int(value:String)
+		Return cc_opts.find(value) >= 0
+	End Method
+
+	Method hasLDopt:Int(value:String)
+		For Local opt:String = EachIn ld_opts
+			If opt.find(value) >= 0 Then
+				Return True
+			End If
+		Next
+		Return False
+	End Method
+
+	Function setPath:String(value:String, path:String)
+		Return value.Replace("%PWD%", path)
+	End Function
+	
+End Type
+
+Global mod_opts:TModOpt ' BaH
+
+Function Match( ext$,pat$ )
+	Return (";"+pat+";").Find( ";"+ext+";" )<>-1
+End Function
+
+Function HTTPEsc$( t$ )
+	t=t.Replace( " ","%20" )
+	Return t
+End Function
+
+Function Sys( cmd$ )
+	If opt_verbose
+		Print cmd
+	Else If opt_dumpbuild
+		Local p$=cmd
+		p=p.Replace( BlitzMaxPath()+"/","./" )
+		WriteStdout p+"~n"
+		Local t$="mkdir "
+		If cmd.StartsWith( t ) And FileType( cmd[t.length..] ) Return
+	EndIf
+	Return system_( cmd )
+End Function
+
+Function Ranlib( dir$ )
+	For Local f$=EachIn LoadDir( dir )
+		Local p$=dir+"/"+f
+		Select FileType( p )
+		Case FILETYPE_DIR
+			Ranlib p
+		Case FILETYPE_FILE
+			If ExtractExt(f).ToLower()="a" Sys "ranlib "+p
+		End Select
+	Next
+End Function
+
+Function Assemble( src$,obj$ )
+	RunCommand("assemble", [src, obj])
+End Function
+
+Function CompileC( src$,obj$,opts$ )
+	RunCommand("CompileC", [src, obj, opts])
+End Function
+
+Function CompileBMX( src$,obj$,opts$ )
+	DeleteFile obj
+
+	Local azm$=StripExt(obj)+".s"
+	
+?threaded
+		processManager.WaitForThreads()
+?			
+	RunCommand("CompileBMX", [src, azm, opts])
+
+	' it would be nice to be able to call this from the script... but we need more refactoring first :-p
+	Assemble azm,obj
+End Function
+
+Function CreateArc( path$ , oobjs:TList )
+	DeleteFile path
+	Local cmd$,t$
+	
+	If processor.Platform() = "win32"
+		For t$=EachIn oobjs
+			If Len(cmd)+Len(t)>1000
+				If Sys( cmd )
+					DeleteFile path
+					Throw "Build Error: Failed to create archive "+path
+				EndIf
+				cmd=""
+			EndIf
+			If Not cmd cmd= processor.Option("path_to_ar", "ar") + " -r "+CQuote(path)
+			cmd:+" "+CQuote(t)
+		Next
+	End If
+	
+	If processor.Platform() = "macos"
+		cmd="libtool -o "+CQuote(path)
+		For Local t$=EachIn oobjs
+			cmd:+" "+CQuote(t)
+		Next
+	End If
+	
+	If processor.Platform() = "linux"
+		For Local t$=EachIn oobjs
+			If Len(cmd)+Len(t)>1000
+				If Sys( cmd )
+					DeleteFile path
+					Throw "Build Error: Failed to create archive "+path
+				EndIf
+				cmd=""
+			EndIf
+			If Not cmd cmd="ar -r "+CQuote(path)
+			cmd:+" "+CQuote(t)
+		Next
+	End If
+
+	If cmd And Sys( cmd )
+		DeleteFile path
+		Throw "Build Error: Failed to create archive "+path
+	EndIf
+End Function
+
+Function LinkApp( path$,lnk_files:TList,makelib )
+	DeleteFile path
+
+	Local cmd$
+	Local files$
+	Local tmpfile$=BlitzMaxPath()+"/tmp/ld.tmp"
+	
+	If processor.Platform() = "macos"
+		cmd="g++"
+		If processor.CPU()="ppc" 
+			cmd:+" -arch ppc" 
+		Else
+			cmd:+" -arch i386 -read_only_relocs suppress"
+		EndIf
+		If macos_version>=$1050
+			cmd:+" -mmacosx-version-min=10.3"
+		EndIf
+	
+		cmd:+" -o "+CQuote( path )
+	'	cmd:+" -bind_at_load"
+		cmd:+" "+CQuote( "-L"+CQuote( BlitzMaxPath()+"/lib" ) )
+	
+		If Not opt_dumpbuild cmd:+" -filelist "+CQuote( tmpfile )
+		
+		For Local t$=EachIn lnk_files
+			If opt_dumpbuild Or (t[..1]="-")
+				cmd:+" "+t 
+			Else
+				files:+t+Chr(10)
+			EndIf
+		Next
+		cmd:+" -lSystem -framework CoreServices -framework CoreFoundation"
+	End If
+	
+	If processor.Platform() = "win32"
+		cmd=CQuote(processor.Option("path_to_ld", BlitzMaxPath()+"/bin/ld.exe"))+" -s -stack 4194304"
+		If opt_apptype="gui" cmd:+" -subsystem windows"
+		If makelib cmd:+" -shared"
+		
+		cmd:+" -o "+CQuote( path )
+		cmd:+" "+CQuote( "-L"+CQuote( BlitzMaxPath()+"/lib") ) ' the BlitzMax lib folder 
+		cmd:+" "+CQuote( "-L"+CQuote( processor.Option("path_to_mingw_lib", BlitzMaxPath()+"/lib") ) )
+		If globals.Get("path_to_mingw_lib2") Then
+			cmd:+" "+CQuote( "-L"+CQuote( processor.Option("path_to_mingw_lib2", BlitzMaxPath()+"/lib") ) )
+		End If
+		If globals.Get("path_to_mingw_lib3") Then
+			cmd:+" "+CQuote( "-L"+CQuote( processor.Option("path_to_mingw_lib3", BlitzMaxPath()+"/lib") ) )
+		End If
+	
+		If makelib
+			Local imp$=StripExt(path)+".a"
+			Local def$=StripExt(path)+".def"
+			If FileType( def )<>FILETYPE_FILE Throw "Cannot locate .def file"
+			cmd:+" "+def
+			cmd:+" --out-implib "+imp
+			files:+"~n"+CQuote( processor.Option("path_to_mingw_lib", BlitzMaxPath()+"/lib") + "/dllcrt2.o" )
+		Else
+			files:+"~n"+CQuote( processor.Option("path_to_mingw_lib2", BlitzMaxPath()+"/lib") + "/crtbegin.o" )
+			files:+"~n"+CQuote( processor.Option("path_to_mingw_lib", BlitzMaxPath()+"/lib") + "/crt2.o" )
+		EndIf
+	
+		Local xpmanifest$
+		For Local f$=EachIn lnk_files
+			Local t$=CQuote( f )
+			If opt_dumpbuild Or (t[..1]="-" And t[..2]<>"-l")
+				cmd:+" "+t
+			Else
+				If f.EndsWith( "/win32maxguiex.mod/xpmanifest.o" )
+					xpmanifest=t
+				Else
+					files:+"~n"+t
+				EndIf
+			EndIf
+		Next
+		If xpmanifest files:+"~n"+xpmanifest
+		
+		cmd:+" "+CQuote( tmpfile )
+	
+		files:+"~n-lgdi32 -lwsock32 -lwinmm -ladvapi32"
+		files:+" -lstdc++ -lmingwex"
+		
+		' for a native Win32 runtiime of mingw 3.4.5, this needs to appear early.
+		If Not processor.Option("path_to_mingw", "") Then
+			files:+" -lmingw32"
+		End If
+
+		files:+" -lgcc -lmoldname -lmsvcrt -luser32 -lkernel32 "
+
+		If processor.Option("path_to_mingw", "") Then
+			' for a non-native Win32 runtime, this needs to appear last.
+			' (Actually, also for native gcc 4.x, but I dunno how we'll handle that yet!)
+			files:+"-lmingw32 "
+		End If
+		
+		If Not makelib
+			files:+" "+CQuote( processor.Option("path_to_mingw_lib2", BlitzMaxPath()+"/lib") + "/crtend.o" )
+		EndIf
+		
+		files="INPUT("+files+")"
+	End If
+	
+	If processor.Platform() = "linux"
+		cmd$="g++"
+		cmd:+" -m32 -s -Os --eh-frame-hdr -pthread"
+		cmd:+" -o "+CQuote( path )
+		cmd:+" "+CQuote( tmpfile )
+		cmd:+" -L/usr/lib32"
+		cmd:+" -L/usr/X11R6/lib"
+		cmd:+" -L/usr/lib"
+		cmd:+" -L"+CQuote( BlitzMaxPath()+"/lib" )
+	
+		For Local t$=EachIn lnk_files
+			t=CQuote(t)
+			If opt_dumpbuild Or (t[..1]="-" And t[..2]<>"-l")
+				cmd:+" "+t
+			Else
+				files:+" "+t
+			EndIf
+		Next
+	
+		files="INPUT("+files+")"
+	End If
+	
+	Local t$=getenv_( "BMK_LD_OPTS" )
+	If t 
+		cmd:+" "+t
+	EndIf
+
+	Local stream:TStream=WriteStream( tmpfile )
+	stream.WriteBytes files.ToCString(),files.length
+	stream.Close
+	
+	If Sys( cmd ) Throw "Build Error: Failed to link "+path
+
+End Function
+
+Function MergeApp(fromFile:String, toFile:String)
+
+	If Not opt_quiet Print "Merging:"+StripDir(fromFile) + " + " + StripDir(toFile)
+
+	Local cmd:String = "lipo -create ~q" + fromFile + "~q ~q" + toFile + "~q -output ~q" + toFile + "~q"
+	
+	If Sys( cmd ) Throw "Merge Error: Failed to merge " + toFile
+	
+	DeleteFile fromFile
+
+End Function

+ 138 - 0
bmk_zap.bmx

@@ -0,0 +1,138 @@
+
+Strict
+
+Import "bmk_modutil.bmx"
+Import "bmk_bank.bmx"
+Import "bmk_modinfo.bmx"
+
+Function Zap( path$,stream:TStream )
+
+	If Not path Return False
+
+	Local name$=StripDir( path )
+	
+	Local skip=False
+	
+	If name[..1]="."
+		skip=True
+	Else If name.ToLower().EndsWith( ".bak" )
+		skip=True
+	EndIf
+	
+	If skip
+		stream.WriteLine ""
+		Return True
+	EndIf
+	
+	Local Mode=FileMode( path )
+
+	Select FileType(path)
+	Case FILETYPE_NONE
+		Print "Error zapping file "+path
+		Return
+	Case FILETYPE_FILE
+		Local size=FileSize(path)
+		stream.WriteLine name
+		stream.WriteLine Mode
+		stream.WriteLine size
+		Local from_stream:TStream=ReadStream(path)
+		CopyBytes from_stream,stream,size
+		from_stream.Close
+	Case FILETYPE_DIR
+		Local dir$[]=LoadDir( path )
+		Local size=Len( dir )
+		stream.WriteLine name
+		stream.WriteLine -Mode
+		stream.WriteLine size
+		For Local t$=EachIn dir
+			If Not Zap( path+"/"+t,stream ) Return
+		Next
+	End Select
+	
+	Return True
+	
+End Function
+
+Function Unzap( dir$,stream:TStream )
+
+	Local name$=stream.ReadLine()
+	If Not name Return True
+	
+	Local Mode=Int( stream.ReadLine() )
+	Local size=Int( stream.ReadLine() )
+
+	Local path$=dir+"/"+name
+	
+	If Mode<0
+		Mode=-Mode
+		CreateDir path
+		For Local k=0 Until size
+			If Not Unzap( path,stream ) Return
+		Next
+	Else
+		DeleteFile path
+		Local to_stream:TStream=WriteStream(path)
+		CopyBytes stream,to_stream,size
+		to_stream.Close
+	EndIf
+
+	SetFileMode path,Mode
+	Return True
+
+End Function
+
+Function ZapMod( name$,stream:TStream )
+
+	Local path$=ModuleInterface( name,"release."+opt_target_platform+"."+opt_arch )
+
+	If FileType(path)<>FILETYPE_FILE 
+		Print "Failed to find module"
+		Return
+	EndIf
+	
+	Local src:TSourceFile=ParseSourceFile( path )
+	stream.WriteLine "Module: "+name
+	For Local t$=EachIn src.info
+		stream.WriteLine t
+	Next
+	stream.WriteLine ""
+
+	Local bank:TBank=TBank.Create(0)
+	Local bank_stream:TStream=TBankStream.Create( bank ) 
+	If Not Zap( ModulePath(name),bank_stream ) Throw "Failed to publish module"
+	bank_stream.Close
+	
+	bank=CompressBank( bank )
+	bank_stream=TBankStream.Create( bank )
+	CopyStream bank_stream,stream
+	bank_stream.Close
+
+End Function
+
+Function UnzapMod( stream:TStream )
+
+	Local modinfo:TModInfo=TModInfo.CreateFromStream( stream )
+	
+	Local path$=ModulePath( modinfo.name )
+	If Not CreateDir( path,True ) Throw "Unable to create module directory"
+	DeleteDir path,True
+	
+	Local bank:TBank=TBank.Create(0)
+	Local bank_stream:TStream=TBankStream.Create( bank )
+	CopyStream stream,bank_stream
+	bank_stream.Close
+	
+	bank=UncompressBank( bank )
+	bank_stream=TBankStream.Create( bank )
+	If Not Unzap( ExtractDir(path),bank_stream )
+		Print "Failed to Unzap module"
+		Return
+	EndIf
+	bank_stream.Close
+	
+?MacOS
+	Ranlib path
+?
+	Return True
+
+End Function

+ 53 - 0
config.bmk

@@ -0,0 +1,53 @@
+#
+# BMK Cross-compiler configuration
+#
+# These paths determine where various MingW and Wine paths and files are on your
+# system.
+# The names of the binaries can change between distributions.
+#
+# ----------------------
+#
+# Please edit the following paths and filenames
+#
+
+# The full path to the mingw32 folder
+#
+path_to_mingw=/usr/local/i386-mingw32-3.4.5
+
+# The names of the compiler utils (ar, ld, gcc, g++)
+# (you can find these in the bin folder)
+#
+name_of_ar=i386-mingw32-ar
+name_of_ld=i386-mingw32-ld
+name_of_gcc=i386-mingw32-gcc
+name_of_gpp=i386-mingw32-g++
+
+
+# The path in which crt2.o resides.
+#
+path_to_mingw_lib=%path_to_mingw%/i386-mingw32/lib
+
+# The path where crtbegin.o and crtend.o reside
+# 
+path_to_mingw_lib2=%path_to_mingw%/lib/gcc/i386-mingw32/3.4.5
+
+
+# The path to Wine executable
+#
+path_to_wine=/usr/bin/wine
+
+
+# ----------------------
+#
+# No need to edit these. They are configured by the above settings.
+#
+#
+path_to_mingw_include=%path_to_mingw%/include
+path_to_mingw_lib3=%path_to_mingw%/i386-mingw32/lib
+
+path_to_ar=%path_to_mingw%/bin/%name_of_ar%
+path_to_ld=%path_to_mingw%/bin/%name_of_ld%
+path_to_gcc=%path_to_mingw%/bin/%name_of_gcc%
+path_to_gpp=%path_to_mingw%/bin/%name_of_gpp%
+
+

+ 41 - 0
core.bmk

@@ -0,0 +1,41 @@
+#!BMK
+
+@define echo
+	print(arg0)
+@end
+
+@define include
+	bmk.LoadBMK(arg1)
+@end
+
+@define push
+	globals.Push(arg1)
+@end
+
+@define pop
+	globals.Pop(arg1)
+@end
+
+@define pushall
+	globals.PushAll()
+@end
+
+@define popall
+	globals.PopAll()
+@end
+
+@define notify
+	sys.Notify(arg0)
+@end
+
+@define openurl
+	sys.OpenURL(arg1)
+@end
+
+@define addarg
+	local option = nvl(arg1, "")
+	local extra = nvl(arg2, "")
+	if option ~= "" then
+		bmk.AddArg(option, extra)
+	end
+@end

+ 4 - 0
custom.bmk

@@ -0,0 +1,4 @@
+
+addccopt optimization -O3
+
+addwin32ccopt arch -march=pentium3

BIN
macos.icns


+ 239 - 0
make.bmk

@@ -0,0 +1,239 @@
+#!BMK
+
+@define assemble
+	local src = nvl(arg1, %infile%)
+	local obj = nvl(arg2, %outfile%)
+
+	sys.DeleteFile(obj)
+	
+	if bmk.Platform() == "macos" then
+		if bmk.CPU() == "ppc" then
+			cmd = "as -arch ppc"
+		else
+			cmd = "as -arch i386"
+		end
+		cmd = cmd .. " -W -o " .. bmk.Quote(obj) .. " " .. bmk.Quote(src)
+	else
+		if bmk.Platform() == "win32" then
+			local prefix = bmk.Option("path_to_wine", "")
+			if prefix ~= "" then
+				prefix = prefix .. " "
+			end
+			
+			cmd = prefix .. bmk.Quote(utils.BlitzMaxPath() .. "/bin/fasm.exe") .. " "
+		else
+			cmd = bmk.Quote(utils.BlitzMaxPath() .. "/bin/fasm") .. " "
+		end
+		
+		if bmk.Platform() == "linux" then
+			cmd = cmd .. " -m32768 "
+		end
+		
+		cmd = cmd .. bmk.Quote(src) .. " " .. bmk.Quote(obj)
+	end
+
+	if bmk.Sys(cmd) ~= 0 then
+		bmk.ThrowNew("Build Error: Failed to assemble " .. src)
+	end
+@end
+
+@define compileBMX
+	local src = nvl(arg1, %infile%)
+	local obj = nvl(arg2, %outfile%)
+	local opts = nvl(arg3, %options%)
+
+	sys.DeleteFile(obj)
+	
+	local bcc_app = "bcc"
+
+	# if we are doing a universal build, be sure and call the correct bcc!
+	if %universal% == "1" and bmk.CPU() == "ppc" then
+		bcc_app = bcc_app .. "_ppc"
+	end
+	
+	if bmk.Platform() == "win32" then
+		bcc_app = bcc_app .. ".exe"
+	end
+	
+	local prefix = ""
+	if bmk.Platform() == "win32" then
+		prefix = bmk.Option("path_to_wine", "")
+		if prefix ~= "" then
+			prefix = prefix .. " "
+		end
+	end
+
+	azm = sys.StripExt(obj) .. ".s"
+	cmd = prefix .. bmk.Quote(utils.BlitzMaxPath() .. "/bin/" .. bcc_app) .." " .. opts .. " -o " .. bmk.Quote(azm) .. " " .. bmk.Quote(src)
+
+	if bmk.Sys( cmd ) ~= 0 then
+		bmk.ThrowNew("Build Error: failed to compile " .. src)
+	end
+
+	if bmk.Platform() == "macos" and bmk.CPU() == "x86" then
+		cmd = bmk.Quote(utils.BlitzMaxPath() .. "/bin/fasm2as") .. " " .. bmk.Quote(azm)
+			if bmk.Sys( cmd ) ~= 0 then
+				bmk.ThrowNew("Fasm2as failed - please contact BRL!")
+		end
+	end
+
+	# TODO : we can't call another generated function from here... so we'd need to make sure this is called elsewhere.
+	#assemble(nil, azm, obj)
+@end
+
+@define compileC
+	local src = nvl(arg1, %infile%)
+	local obj = nvl(arg2, %outfile%)
+	local opts = arg3
+
+	sys.DeleteFile(obj)
+
+	#opts = opts .. " " .. %cc_opts% .. " " .. %BMK_CC_OPTS%
+	
+	local ext = sys.ExtractExt(src)
+
+	if bmk.Platform() == "macos" then
+		cmd = "gcc"
+		
+		if ext == "cpp" or ext == "cxx" or ext == "mm" or ext == "cc" then
+			cmd = "g++"
+		end
+
+		if bmk.CPU() == "ppc" then
+			cmd = cmd .. " -arch ppc "
+		else
+			cmd = cmd .. " -arch i386 "
+		end
+		
+	elseif bmk.Platform() == "win32" then
+		cmd = bmk.Option("path_to_gcc", "gcc")
+		if ext == "cpp" or ext == "cxx" or ext == "mm" or ext == "cc" then
+			cmd = bmk.Option("path_to_gpp", "g++")
+		end
+		
+	elseif bmk.Platform() == "linux" then
+	
+		cmd = "gcc"
+	end
+
+	# disable warnings ?
+	if %CC_WARNINGS% == "" then
+		opts = opts .. " -w"
+	end
+
+	cmd = cmd .. opts .. %cc_opts% .. %mod_ccopts% .. " -o " .. bmk.Quote(obj) .. " " .. bmk.Quote(src)
+
+	if bmk.MultiSys( cmd, src ) ~= 0 then
+		bmk.ThrowNew("Build Error: failed to compile " .. src)
+	end
+
+@end
+
+@define addlib
+	globals.Add("libs", arg1)
+@end
+
+## adds a cc_opt option
+@define addccopt
+	globals.AddOption("cc_opts", arg1, arg2)
+@end
+
+@define addwin32ccopt
+	if bmk.Platform() == "win32" then
+		globals.AddOption("cc_opts", arg1, arg2)
+	end
+@end
+
+@define addmacccopt
+	if bmk.Platform() == "macos" then
+		globals.AddOption("cc_opts", arg1, arg2)
+	end
+@end
+
+@define addmacppcccopt
+	if bmk.Platform() == "macos" and bmk.CPU() == "ppc" then
+		globals.AddOption("cc_opts", arg1, arg2)
+	end
+@end
+
+@define addmacx86ccopt
+	if bmk.Platform() == "macos" and bmk.CPU() == "x86" then
+		globals.AddOption("cc_opts", arg1, arg2)
+	end
+@end
+
+@define addlinuxccopt
+	if bmk.Platform() == "linux" then
+		globals.AddOption("cc_opts", arg1, arg2)
+	end
+@end
+
+## removes a cc_opt option
+@define rmccopt
+	globals.Remove("cc_opts", arg1)
+@end
+
+@define addldopt
+	globals.AddOption("ld_opts", arg1, arg2)
+@end
+
+# compiles the specified file, using the current options.
+@define make
+	make.Make(arg1)
+@end
+
+# the default ccopts
+# used for compiling c-type files
+@define default_cc_opts
+
+	if bmk.Platform() == "macos" then
+		
+		# compile for 10.3 ?
+		# macos version >= 0x1050 ?
+		if tonumber(%macos_version%) >= 4176 then
+			globals.Addoption("cc_opts", "osversion", "-mmacosx-version-min=10.3")
+		end
+	elseif bmk.Platform() == "win32" then
+
+		globals.AddOption("cc_opts", "arch", "-march=pentium")
+		globals.AddOption("cc_opts", "fastmath", "-ffast-math")
+		
+	elseif bmk.Platform() == "linux" then
+		globals.AddOption("cc_opts", "aliasing", "-fno-strict-aliasing")
+		globals.AddOption("cc_opts", "arch", "-m32")
+		globals.AddOption("cc_opts", "fancymath", "-mfancy-math-387")
+	
+	end
+
+	globals.AddOption("cc_opts", "exceptions", "-fno-exceptions")
+	globals.AddOption("cc_opts", "linker", "-c")
+	globals.AddOption("cc_opts", "optimization", "-Os")
+
+@end
+
+
+# Supported file extensions
+#
+# SOURCE_UNKNOWN = 0
+# SOURCE_BMX = 1
+# SOURCE_IFACE = 2
+# SOURCE_C = 4
+# SOURCE_HEADER = 8
+# SOURCE_ASM = 16
+@define source_type
+	local ext = ";" .. arg0 .. ";"
+	
+	if string.find(";bmx;", ext) then
+		return 1
+	elseif string.find(";i;", ext) then
+		return 2
+	elseif string.find(";c;m;cc;cpp;cxx;mm;", ext) then
+		return 4
+	elseif string.find(";h;hpp;hxx;", ext) then
+		return 8
+	elseif string.find(";s;asm;", ext) then
+		return 16
+	end
+	
+	return 0
+@end

+ 94 - 0
readme.txt


+ 21 - 0
waitpid.c

@@ -0,0 +1,21 @@
+#include <stdio.h>
+#include <sys/wait.h>
+
+int bmx_waitpid(pid_t pid) {
+
+	int status;
+	pid_t p = waitpid(pid, &status, WUNTRACED);
+	
+	return WEXITSTATUS(status);
+}
+
+void bmx_system(const char * c) {
+	int res = system(c);
+	
+	// don't ask..
+	if (res) {
+		res = 1;
+	}
+
+	exit(res);
+}