浏览代码

mx2cc tweaks, cleanups.

Mark Sibly 9 年之前
父节点
当前提交
00b0a7e7a8

+ 186 - 35
src/mx2cc/builder.monkey2

@@ -113,16 +113,23 @@ Class Builder
 		
 		Select opts.target
 		Case "desktop"
-			AR_CMD="ar"
-			CC_CMD="gcc"
+			AR_CMD= "ar"
+			CC_CMD= "gcc"
 			CXX_CMD="g++"
-			LD_CMD="g++"
-			AS_CMD="as"
+			LD_CMD= "g++"
+			AS_CMD= "as"
 		Case "emscripten"
-			AR_CMD="emar"
-			CC_CMD="emcc"
+			AR_CMD= "emar"
+			CC_CMD= "emcc"
 			CXX_CMD="em++"
-			LD_CMD="em++"
+			LD_CMD= "em++"
+			AS_CMD= "as"		'as?
+		Case "android"
+			AR_CMD= "arm-linux-androideabi-ar"
+			CC_CMD= "arm-linux-androideabi-clang"
+			CXX_CMD="arm-linux-androideabi-clang++"
+			LD_CMD= "arm-linux-androideabi-clang++"
+			AS_CMD= "arm-linux-androideabi-as"
 		End
 		
 		ppsyms["__HOSTOS__"]="~q"+HostOS+"~q"
@@ -456,19 +463,25 @@ Class Builder
 			
 			Local ext:=ExtractExt( src ).ToLower()
 						
-			Local cmd:="",scanable:=True
+			Local cmd:="",isasm:=False
+			
 			Select ext
 			Case ".c",".m"
+			
 				cmd=CC_CMD+" "+CC_OPTS.Join( " " )
+				cmd+=" -I~q"+modulesDir+"monkey/native~q"
+				
 			Case ".cc",".cxx",".cpp",".mm"
+
 				cmd=CXX_CMD+" "+CPP_OPTS.Join( " " )
+				cmd+=" -I~q"+modulesDir+"monkey/native~q"
+
 			Case ".asm",".s"
+			
 				cmd=AS_CMD
-				scanable=False
+				isasm=True
 			End
 			
-			cmd+=" -I~q"+modulesDir+"monkey/native~q"
-			
 			'Check dependancies
 			'			
 			Local objTime:=GetFileTime( obj )
@@ -479,7 +492,7 @@ Class Builder
 			
 				Local uptodate:=True
 			
-				If scanable
+				If Not isasm
 			
 					If GetFileType( deps )=FILETYPE_NONE
 					
@@ -518,7 +531,9 @@ Class Builder
 			
 			If opts.verbose>0 Print "Compiling "+src
 			
-			cmd+=" -c -o ~q"+obj+"~q ~q"+src+"~q"
+			If Not isasm cmd+=" -c"
+			
+			cmd+=" -o ~q"+obj+"~q ~q"+src+"~q"
 			
 			Exec( cmd )
 			
@@ -562,7 +577,7 @@ Class Builder
 
 			cmd+=" --preload-file ~q"+assetsDir+"@/assets~q"
 			
-		Else If HostOS="windows"
+		Else If opts.target="desktop" And HostOS="windows"
 		
 			If opts.appType="gui" cmd+=" -mwindows"
 		
@@ -570,7 +585,7 @@ Class Builder
 			assetsDir=module.outputDir+"assets/"
 			dllsDir=ExtractDir( outputFile )
 			
-		Else If HostOS="macos"
+		Else If opts.target="desktop" And HostOS="macos"
 		
 			If opts.appType="gui"
 			
@@ -611,21 +626,80 @@ Class Builder
 			
 			Endif
 			
-		Else	'linux!
+		Else If opts.target="desktop" And HostOS="linux"
 		
 			outputFile=module.outputDir+module.name
 			assetsDir=module.outputDir+"assets/"
 			dllsDir=ExtractDir( outputFile )
-
+			
+		Else If opts.target="android"
+		
+			outputFile=module.outputDir+"lib"+module.name+".so"
+			assetsDir=module.outputDir+"assets/"
+			dllsDir=ExtractDir( outputFile )
+			
 		Endif
 		
 		If opts.verbose>=0 Print "Linking "+outputFile
 		
 		DeleteDir( assetsDir,True )
+
 		CreateDir( assetsDir )
 		
-		For Local ass:=Eachin ASSET_FILES
+		Local assetFiles:=New StringMap<String>
+		
+		For Local src:=Eachin ASSET_FILES
+		
+			Local dst:=assetsDir
 		
+			Local i:=src.Find( "@/" )
+			If i<>-1
+				dst+=src.Slice( i+2 )
+				src=src.Slice( 0,i )
+				If Not dst.EndsWith( "/" ) dst+="/"
+			Endif
+			
+			Select GetFileType( src )
+			
+			Case FileType.File
+			
+				dst+=StripDir( src )
+				EnumAssetFiles( src,dst,assetFiles )
+				
+			Case FileType.Directory
+			
+				EnumAssetFiles( src,dst,assetFiles )
+			End
+			
+		Next
+		
+		CopyAssetFiles( assetFiles )
+	
+		#rem
+		For Local src:=Eachin ASSET_FILES.Backwards()
+		
+			Local dst:=assetsDir
+			
+			Local i:=src.Find( "@/" )
+			If i<>-1
+				dst+=src.Slice( i+2 )
+				src=src.Slice( 0,i )
+				If Not dst.EndsWith( "/" ) dst+="/"
+			Endif
+			
+			Select GetFileType( src )
+			
+			Case FileType.File
+			
+				CreateDir( dst )
+				dst+=StripDir( src )
+				If Not CopyFile( src,dst ) New BuildEx( "Error copying asset '"+src+"'" )
+				
+			Case FileType.Directory
+			
+				If Not CopyAll( src,dst ) New BuildEx( "Error copying asset '"+src+"'" )
+			End
+			#rem		
 			Local i:=ass.Find( "@/" )
 			If i=-1
 				CopyFile( ass,assetsDir+StripDir( ass ) )
@@ -639,7 +713,9 @@ Class Builder
 			ass=ass.Slice( 0,i )
 			
 			CopyFile( ass,dst+StripDir( ass ) )
+			#end
 		Next
+		#end
 		
 		cmd+=" -o ~q"+outputFile+"~q"
 		
@@ -649,13 +725,25 @@ Class Builder
 			lnkFiles+=" ~q"+obj+"~q"
 		Next
 		
+		If opts.target="android"
+			lnkFiles+=" -Wl,--whole-archive"
+		Endif
+		
 		For Local lib:=Eachin LD_LIBS
 			lnkFiles+=" ~q"+lib+"~q"
 		Next
+	
+		If opts.target="android"	
+			lnkFiles+=" -Wl,--no-whole-archive"
+		Endif
 		
 		lnkFiles+=" "+LD_SYSLIBS.Join( " " )
 		
-		If HostOS="windows"
+		If opts.target="android"
+			lnkFiles+=" -lstdc++"
+		Endif
+		
+		If HostOS="windows" And opts.target<>"android"
 			Local tmp:=AllocTmpFile( "lnkFiles" )
 			SaveString( lnkFiles,tmp )
 			cmd+=" -Wl,@"+tmp
@@ -663,7 +751,6 @@ Class Builder
 			cmd+=lnkFiles
 		Endif
 
-'		Print cmd
 		Exec( cmd )
 		
 		For Local src:=Eachin DLL_FILES
@@ -711,6 +798,45 @@ Class Builder
 		Return outputFile
 	End
 	
+	Method CopyAssetFiles( files:StringMap<String> )
+	
+		For Local it:=Eachin files
+		
+			Local src:=it.Value
+			Local dst:=it.Key
+			
+			If CreateDir( ExtractDir( dst ) )
+			
+				If GetFileTime( dst )>=GetFileTime( src ) Continue
+				
+				If CopyFile( src,dst ) Continue
+
+			Endif
+			
+			Throw New BuildEx( "Error copying asset file '"+src+"' to '"+dst+"'" )
+		Next
+	End
+	
+	Method EnumAssetFiles( src:String,dst:String,files:StringMap<String> )
+
+		Select GetFileType( src )
+
+		Case FILETYPE_FILE
+		
+			If Not files.Contains( dst ) files[dst]=src
+			
+		Case FILETYPE_DIR
+		
+			For Local f:=Eachin LoadDir( src )
+			
+				EnumAssetFiles( src+"/"+f,dst+"/"+f,files )
+
+			Next
+		
+		End
+		
+	End
+	
 	Method CopyAll:Bool( src:String,dst:String )
 		
 		Select GetFileType( src )
@@ -719,9 +845,9 @@ Class Builder
 		
 			If Not CreateDir( ExtractDir( dst ) ) Return False
 		
-			If GetFileTime( src )>GetFileTime( dst )
+'			If GetFileTime( src )>GetFileTime( dst )
 				If Not CopyFile( src,dst ) Return False
-			Endif
+'			Endif
 			
 			Return True
 			
@@ -730,9 +856,7 @@ Class Builder
 			If Not CreateDir( dst ) Return False
 			
 			For Local file:=Eachin LoadDir( src )
-			
 				If Not CopyAll( src+"/"+file,dst+"/"+file ) Return False
-			
 			Next
 			
 			Return True
@@ -951,6 +1075,19 @@ Class Builder
 		If imported.Contains( path ) Return
 		imported[path]=True
 		
+		Local i:=path.Find( "@/" )
+		If i<>-1
+			Local src:=path.Slice( 0,i )
+			
+			If GetFileType( src )=FileType.None
+				New BuildEx( "Asset '"+src+"' not found" )
+				Return
+			Endif
+			
+			ASSET_FILES.Push( path )
+			Return
+		Endif
+		
 		Local ext:=ExtractExt( path ).ToLower()
 		
 		Local name:=StripDir( StripExt( path ) )
@@ -993,27 +1130,29 @@ Class Builder
 		Endif
 		
 		Local qpath:="~q"+path+"~q"
-		
+
 		Select ext
 		Case ".framework"
 			
-			If GetFileType( path )<>FILETYPE_DIR
+			If GetFileType( path )<>FileType.Directory
 				New BuildEx( "Framework "+qpath+" not found" )
 				Return
 			Endif
 			
 		Default
 		
-			Local tpath:=path
-		
-			Local i:=tpath.Find( "@/" )
-			If i<>-1 tpath=tpath.Slice( 0,i )
-		
-			If GetFileType( tpath )<>FILETYPE_FILE
-				New BuildEx( "File "+qpath+" not found" )
+			Select GetFileType( path )
+			Case FileType.Directory
+			
+				ASSET_FILES.Push( path )
 				Return
-			Endif
+				
+			Case FileType.None
 			
+				New BuildEx( "File "+qpath+" not found" )
+				Return
+				
+			End
 		End
 		
 		Select ext
@@ -1041,7 +1180,17 @@ Class Builder
 		
 			LD_SYSLIBS.Push( qpath )
 			
-		Case ".so",".dll",".exe"
+		Case ".so"
+		
+			If opts.target="android"		'probably all non-windows targets
+			
+				LD_SYSLIBS.Push( qpath )
+			
+			Endif
+			
+			DLL_FILES.Push( path )
+			
+		Case ".dll",".exe"
 		
 			DLL_FILES.Push( path )
 			
@@ -1084,6 +1233,8 @@ Class Builder
 	
 	Method Exec:Bool( cmd:String )
 	
+		If opts.verbose>2 Print cmd
+	
 		Local errs:=AllocTmpFile( "stderr" )
 			
 		If Not system( cmd+" 2>"+errs ) Return True

+ 214 - 180
src/mx2cc/docs/docsmaker.monkey2

@@ -9,12 +9,78 @@ Class DocsMaker
 	
 		_nav=New JsonBuffer
 	
-		_md=New MarkdownBuffer( Lambda:String( link:String )
+		_md=New MarkdownBuffer( "",Lambda:String( link:String )
 			Return ResolveLink( link,_linkScope )
 		End )
 		
 	End
 	
+	Method MakeManPages( module:Module )
+	
+		Local path:=RealPath( module.baseDir+"docs/manual.md" )
+		If GetFileType( path )<>FileType.File Return
+		
+		DeleteDir( module.baseDir+"docs/__MANPAGES__",True )
+		CreateDir( module.baseDir+"docs/__MANPAGES__" )
+		
+		Local pages:=ManPage.MakeManPages( path,module.baseDir,Lambda:String( link:String )
+			Return ResolveLink( link,_linkScope )
+		End )
+		
+		Local template:=stringio.LoadString( module.baseDir+"docs/manuals_page_template.html" )
+		If Not template template=stringio.LoadString( "docs/manuals_page_template.html" )
+		
+		For Local it:=Eachin pages
+		
+			Local path:=it.Key		'source path
+			Local page:=it.Value	'ManPage
+			
+			Local dir:=ExtractDir( path ),cd:=".."
+			While dir<>module.baseDir+"docs/" And Not IsRootDir( dir )
+				dir=ExtractDir( dir )
+				cd="../"+cd
+			Wend
+			
+			Local mod_dir:="../.."
+			Local mx2_dir:=mod_dir+"/../.."
+			
+			Local html:=template.Replace( "${CONTENT}",page.HtmlSource )
+			
+			html=html.Replace( "${MX2_DIR}",mx2_dir )
+			html=html.Replace( "${MOD_DIR}",mod_dir )
+			html=html.Replace( "${CD}",cd )
+			
+			SaveString( html,page.Page )
+		
+		Next
+		
+		Local page:=pages[path]
+		Local node:=page.RootNode
+		Local json:=node.ToJson( RealPath( "modules" ) ).ToJson()
+		
+		SaveString( json,module.baseDir+"docs/__MANPAGES__/index.js" )
+	End
+	
+	Method MakeModulePage:String( module:Module )
+	
+		Local mdocs:=module.baseDir+"docs/module.md"
+		
+		Local md:=stringio.LoadString( mdocs )
+		If Not md Return ""
+		
+		_md.Emit( md )
+		
+		Local docs:=_md.Flush()
+		
+		docs=docs.Replace( "${CD}",".." )
+		
+		Local page:="module"
+		
+		SavePage( docs,page )
+		
+		Return page
+	End
+	
 	Method MakeDocs:String( module:Module )
 	
 		_module=module
@@ -38,16 +104,11 @@ Class DocsMaker
 			nmspaceDocs[nmspace.Name]+=fscope.fdecl.docs
 		Next
 		
-		Local page:=""
-		Local mdocs:=_module.baseDir+"docs/module.md"
-		If GetFileType( mdocs )=FileType.File
-			Local md:=stringio.LoadString( mdocs )
-			_linkScope=Null
-			_md.Emit( md )
-			page="module"
-'			page=_module.name+"-Default"
-			SavePage( _md.Flush(),page )
-		Endif
+		_linkScope=Null
+		
+		MakeManPages( module )
+		
+		Local page:=MakeModulePage( module )
 
 		BeginNode( _module.name,page )
 		
@@ -64,85 +125,6 @@ Class DocsMaker
 		Return tree
 	End
 	
-	'Kludgy as!
-	'
-	'Converts the 'TOC' tree generated by stackedit into a json nav tree...
-	'
-	Method MakeLangNav:String()
-	
-		#rem
-		Local md:=stringio.LoadString( "docs/The Monkey2 Language.md" )
-		_md.Emit( md )
-		Local html:=_md.Flush()
-		
-		Local src:=stringio.LoadString( "docs/monkey2-language-template.html" )
-		src=src.Replace( "${CONTENT}",html )
-		
-		stringio.SaveString( src,"docs/The Monkey2 Language.html" )
-		#End
-		
-		Local src:=stringio.LoadString( "docs/The Monkey2 Language.html" )
-		
-		src=src.Replace( "~r~n","~n" )
-		
-		Local tag1:="<p><div class=~qtoc~q>"
-		Local tag2:="</div>"
-		
-		Local i:=src.Find( tag1 )
-		If i=-1
-			Print "Can't find lang TOC div"
-			Return ""
-		Endif
-
-		Local i2:=src.Find( tag2,i+tag1.Length )
-		If i2=-1
-			Print "Can't find lang TOC /div"
-			Return ""
-		Endif
-		
-		src=src.Slice( i+tag1.Length,i2 )
-		
-		Local nest:=0
-		
-		For Local line:=Eachin src.Split( "~n" )
-		
-			If line="</ul>"
-			
-				If nest
-					_nav.Emit( "]}" )
-					nest-=1
-				Endif
-			
-			Else If line="</li>"
-			
-			Else If line.StartsWith( "<li>" )
-			
-				Local i:=line.Find( "<a href=~q#" )
-				If i=-1 Continue
-				Local i2:=line.Find( "~q>",i+10 )
-				If i2=-1 Continue
-				Local i3:=line.Find( "</a>",i2+2 )
-				If i3=-1 Continue
-				
-				Local href:=line.Slice( i+10,i2 )
-				Local text:=line.Slice( i2+2,i3 )
-				
-				If line.EndsWith( "<ul>" )
-					nest+=1
-					_nav.Emit( "{text:~q"+text+"~q,data:{topic:~q"+href+"~q},children:[" )
-				Else If line.EndsWith( "</li>" )
-					_nav.Emit( "{text:~q"+text+"~q,data:{topic:~q"+href+"~q}}" )
-				Endif
-				
-			Endif
-			
-		Next
-		
-		Local tree:=_nav.Flush()
-		
-		Return tree
-	End
-	
 	Private
 	
 	Field _nav:JsonBuffer
@@ -366,10 +348,10 @@ Class DocsMaker
 	End
 	
 	Method SavePage( docs:String,page:String )
+
 		page=page.Replace( ".","-" )
-		docs=_pageTemplate.Replace( "${CONTENT}",docs )
 		
-'		Print "Saving page:"+_pagesDir+page+".html"
+		docs=_pageTemplate.Replace( "${CONTENT}",docs )
 		
 		stringio.SaveString( docs,_pagesDir+page+".html" )
 	End
@@ -411,7 +393,7 @@ Class DocsMaker
 			For Local type:=Eachin ctype.types
 				args+=","+TypeName( type,prefix )
 			Next
-			If args args="\< "+args.Slice( 1 )+" \>"
+			If args args="\<"+args.Slice( 1 )+"\>"
 			
 			If ctype.instanceOf ctype=ctype.instanceOf
 			
@@ -432,8 +414,8 @@ Class DocsMaker
 		
 		Local atype:=TCast<ArrayType>( type )
 		If atype
-			If atype.rank=1 Return TypeName( atype.elemType,prefix )+"\[ \]"
-			Return TypeName( atype.elemType,prefix )+"\[ ,,,,,,,,,".Slice( 0,atype.rank+2 )+" \]"
+			If atype.rank=1 Return TypeName( atype.elemType,prefix )+"\[\]"
+			Return TypeName( atype.elemType,prefix )+"\[,,,,,,,,,".Slice( 0,atype.rank )+"\]"
 		End
 		
 		Local ftype:=TCast<FuncType>( type )
@@ -460,54 +442,95 @@ Class DocsMaker
 		Local fscope:=scope.FindFile()
 		Local nmspace:=fscope.nmspace
 		Local module:=fscope.fdecl.module
-		_md.Emit( "_Module: &lt;"+module.name+"&gt;_  " )
-		_md.Emit( "_Namespace:_ _"+MakeLink( NamespacePath( nmspace ),nmspace )+"_" )
-		_md.EmitBr()
-		_md.Emit( "#### "+DeclName( decl,scope ) )
-		_md.EmitBr()
+		
+		_md.CurrentDir=ExtractDir( fscope.fdecl.path )
+		
+		Local md:=module.name+":"+MakeLink( NamespacePath( nmspace ),nmspace )+"."+DeclName( decl,scope )
+		
+		_md.Emit( "_"+md+"_" )
+		
+	End
+	
+	Method EmitDocs( docs:String )
+	
+		Local lines:=docs.Split( "~n" )
+		
+		Local indent:=0
+		If lines.Length>1
+			indent=10000
+			For Local i:=1 Until lines.Length
+				If Not lines[i].Trim() Continue
+				indent=Min( indent,FindNonSpc( lines[i] ) )
+			Next
+			If indent=10000 indent=0
+			lines[0]=" ".Dup( indent )+lines[0].Trim()
+		Endif
+		
+		_md.Emit( lines,indent )
+	End
+	
+	Method EmitDocs( decl:Decl )
+	
+		EmitDocs( decl.docs )
 	End
 	
 	Method DocsHidden:Bool( decl:Decl )
-		Return (decl.IsPrivate And Not decl.docs) Or decl.docs.StartsWith( "@hidden" )
+		Return (Not decl.IsPublic And Not decl.docs) Or decl.docs.StartsWith( "@hidden" )
 	End
 	
-	Method EmitMembers( kind:String,scope:Scope,inherited:Bool )
+	Method DocsHidden:Bool( decl:Decl,access:Int )
+		If access And (decl.flags & access) Return DocsHidden( decl )
+		Return True
+	End
 	
-		Local init:=True
+	Method EmitMembers( kind:String,scope:Scope,access:Int,inherited:int )
+	
+		Local tag:=""
+		
+		If inherited>=0
+		
+			If inherited tag="Inherited "
+			
+			Select access
+			Case DECL_PUBLIC tag+="Public "
+			Case DECL_PROTECTED tag+="Protected "
+			End
+		Endif
 	
+		Local init:=True
+		
 		For Local node:=Eachin scope.nodes
 		
 			Local atype:=Cast<AliasType>( node.Value )
 			If atype
 				If kind<>"alias" Continue
 				Local decl:=atype.adecl
-				If DocsHidden( decl ) Continue
-				If inherited<>(scope<>atype.scope) Continue
+				If DocsHidden( decl,access ) Continue
+				If inherited>=0 And inherited<>(scope<>atype.scope) Continue
 				
 				If init
 					init=False
 					_md.EmitBr()
-					_md.Emit( "| Aliases | &nbsp; |" )
+					_md.Emit( "| "+tag+"Aliases | &nbsp; |" )
 					_md.Emit( "|:---|:---" )
 				Endif
 				
 				_md.Emit( "| "+DeclIdent( decl,atype.scope )+" | "+DeclDesc( decl )+" |" )
 				Continue
 			Endif
-				
 
 			Local ctype:=Cast<ClassType>( node.Value )
 			If ctype
 				Local decl:=ctype.cdecl
 				If kind<>decl.kind Continue
-				If DocsHidden( decl ) Continue
-				If inherited<>(scope<>ctype.scope.outer) Continue
+				If DocsHidden( decl,access ) Continue
+				If inherited>=0 And inherited<>(scope<>ctype.scope.outer) Continue
 				
 				If init
 					init=False
 					Local kinds:=kind.Capitalize() + (kind="class" ? "es" Else "s")
 					_md.EmitBr()
-					_md.Emit( "| "+kinds+" | |" )
+					_md.Emit( "| "+tag+kinds+" | |" )
 					_md.Emit( "|:---|:---|" )
 				Endif
 				
@@ -519,13 +542,13 @@ Class DocsMaker
 			If etype
 				If kind<>"enum" Continue
 				Local decl:=etype.edecl
-				If DocsHidden( decl ) Continue
-				If inherited<>(scope<>etype.scope.outer) Continue
+				If DocsHidden( decl,access ) Continue
+				If inherited>=0 And inherited<>(scope<>etype.scope.outer) Continue
 				
 				If init
 					init=False
 					_md.EmitBr()
-					_md.Emit( "| Enums | |" )
+					_md.Emit( "| "+tag+"Enums | |" )
 					_md.Emit( "|:---|:---|" )
 				Endif
 
@@ -537,13 +560,13 @@ Class DocsMaker
 			If vvar
 				Local decl:=vvar.vdecl
 				If kind<>decl.kind Continue
-				If DocsHidden( decl ) Continue
-				If inherited<>(scope<>vvar.scope) Continue
+				If DocsHidden( decl,access ) Continue
+				If inherited>=0 And inherited<>(scope<>vvar.scope) Continue
 
 				If init
 					init=False
 					_md.EmitBr()
-					_md.Emit( "| "+kind.Capitalize()+"s | |" )
+					_md.Emit( "| "+tag+kind.Capitalize()+"s | |" )
 					_md.Emit( "|:---|:---|" )
 				Endif
 
@@ -555,13 +578,13 @@ Class DocsMaker
 			If plist
 				If kind<>"property" Continue
 				Local decl:=plist.pdecl
-				If DocsHidden( decl ) Continue
-				If inherited<>(scope<>plist.scope) Continue
+				If DocsHidden( decl,access ) Continue
+				If inherited>=0 And inherited<>(scope<>plist.scope) Continue
 				
 				If init
 					init=False
 					_md.EmitBr()
-					_md.Emit( "| Properties | |" )
+					_md.Emit( "| "+tag+"Properties | |" )
 					_md.Emit( "|:---|:---|" )
 				Endif
 
@@ -575,8 +598,8 @@ Class DocsMaker
 
 				For Local func:=Eachin flist.funcs
 					Local decl:=func.fdecl
-					If DocsHidden( decl ) Continue
-					If inherited<>(scope<>func.scope) Continue
+					If DocsHidden( decl,access ) Continue
+					If inherited>=0 And inherited<>(scope<>func.scope) Continue
 					
 					If kind="constructor" 
 						If decl.ident<>"new" Continue
@@ -589,7 +612,7 @@ Class DocsMaker
 					If init
 						init=False
 						_md.EmitBr()
-						_md.Emit( "| "+kind.Capitalize()+"s | |" )
+						_md.Emit( "| "+tag+kind.Capitalize()+"s | |" )
 						_md.Emit( "|:---|:---|" )
 					Endif
 					
@@ -612,6 +635,15 @@ Class DocsMaker
 
 	End
 	
+	Method EmitNode( decl:Decl,scope:Scope,page:String="" )
+
+		Local id:=decl.ident
+		If id.StartsWith( "@" ) id=id.Slice( 1 ).Capitalize()	
+	
+		EmitNode( id,scope,page )
+
+	End
+	
 	Method EndNode()
 
 		_nav.Emit( "] }" )
@@ -626,15 +658,11 @@ Class DocsMaker
 	End
 	
 	Method EmitLeaf( decl:Decl,page:String="" )
-
-		EmitLeaf( decl.ident,page )
-
-	End
-	
-	Method EmitNode( decl:Decl,scope:Scope,page:String="" )
 	
-		EmitNode( decl.ident,scope,page )
+		Local id:=decl.ident
+		If id.StartsWith( "@" ) id=id.Slice( 1 ).Capitalize()	
 
+		EmitLeaf( id,page )
 	End
 	
 	Method EmitNode( name:String,scope:Scope,page:String="" )
@@ -700,20 +728,24 @@ Class DocsMaker
 	Method EmitNamespace( nmspace:NamespaceScope,docs:String )
 
 		_linkScope=nmspace
+		
+		Local md:=_module.name+":"+nmspace.Name
+		
+		_md.Emit( "_"+md+"_" )
 	
-		_md.Emit( "_Module: &lt;"+_module.name+"&gt;_  " )
-		_md.Emit( "_Namespace: "+nmspace.Name+"_" )
+'		_md.Emit( "_Module: &lt;"+_module.name+"&gt;_  " )
+'		_md.Emit( "_Namespace: "+nmspace.Name+"_" )
 		
-		EmitMembers( "alias",nmspace,True )
-		EmitMembers( "enum",nmspace,True )
-		EmitMembers( "struct",nmspace,True )
-		EmitMembers( "class",nmspace,True )
-		EmitMembers( "interface",nmspace,True )
-		EmitMembers( "const",nmspace,True )
-		EmitMembers( "global",nmspace,True )
-		EmitMembers( "function",nmspace,True )
+		EmitMembers( "alias",nmspace,DECL_PUBLIC,-1 )
+		EmitMembers( "enum",nmspace,DECL_PUBLIC,-1 )
+		EmitMembers( "struct",nmspace,DECL_PUBLIC,-1 )
+		EmitMembers( "class",nmspace,DECL_PUBLIC,-1 )
+		EmitMembers( "interface",nmspace,DECL_PUBLIC,-1 )
+		EmitMembers( "const",nmspace,DECL_PUBLIC,-1 )
+		EmitMembers( "global",nmspace,DECL_PUBLIC,-1 )
+		EmitMembers( "function",nmspace,DECL_PUBLIC,-1 )
 		
-		_md.Emit( docs )
+		EmitDocs( docs )
 
 		docs=_md.Flush()
 		
@@ -731,23 +763,21 @@ Class DocsMaker
 			If Not vvar Or vvar.transFile.module<>_module Continue
 			
 			Local decl:=vvar.vdecl
-			If decl.kind<>kind Or DocsHidden( decl ) Continue
+			If decl.kind<>kind Or DocsHidden( decl,DECL_PUBLIC ) Continue
 		
 			_linkScope=vvar.scope
 
 			EmitHeader( decl,vvar.scope )
 		
-			_md.Emit( "##### "+decl.kind.Capitalize()+" "+DeclIdent( decl )+" : "+TypeName( vvar.type,vvar.scope ) )
-		
-			_md.Emit( decl.docs )
+			_md.Emit( "##### "+decl.kind.Capitalize()+" "+DeclIdent( decl )+":"+TypeName( vvar.type,vvar.scope ) )
+			
+			EmitDocs( decl )
 		
 			Local docs:=_md.Flush()
 
 			Local page:=DeclPath( vvar.vdecl,vvar.scope )
 			SavePage( docs,page )
 			
-'			Print "save page:"+page
-			
 			EmitLeaf( vvar.vdecl,page )
 			
 		Next
@@ -762,15 +792,15 @@ Class DocsMaker
 			If Not atype Continue
 			
 			Local decl:=atype.adecl
-			If decl.kind<>kind Or DocsHidden( decl ) Continue
+			If decl.kind<>kind Or DocsHidden( decl,DECL_PUBLIC ) Continue
 		
 			_linkScope=atype.scope
 		
 			EmitHeader( decl,atype.scope )
 		
-			_md.Emit( "##### Alias "+DeclIdent( decl,True )+" : "+TypeName( atype._alias,atype.scope ) )
+			_md.Emit( "##### Alias "+DeclIdent( decl,True )+":"+TypeName( atype._alias,atype.scope ) )
 		
-			_md.Emit( decl.docs )
+			EmitDocs( decl )
 		
 			Local docs:=_md.Flush()
 
@@ -791,7 +821,7 @@ Class DocsMaker
 			If Not etype Continue
 			
 			Local decl:=etype.edecl
-			If decl.kind<>kind Or DocsHidden( decl ) Continue
+			If decl.kind<>kind Or DocsHidden( decl,DECL_PUBLIC ) Continue
 			
 			_linkScope=etype.scope.outer
 
@@ -799,7 +829,7 @@ Class DocsMaker
 		
 			_md.Emit( "##### Enum "+DeclIdent( decl ) )
 		
-			_md.Emit( decl.docs )
+			EmitDocs( decl )
 		
 			Local docs:=_md.Flush()
 
@@ -820,7 +850,7 @@ Class DocsMaker
 			If Not ctype Or ctype.transFile.module<>_module Continue
 			
 			Local decl:=ctype.cdecl
-			If decl.kind<>kind Or DocsHidden( decl ) Continue
+			If decl.kind<>kind Or DocsHidden( decl,DECL_PUBLIC ) Continue
 			
 			_linkScope=ctype.scope	'.outer
 			
@@ -860,24 +890,30 @@ Class DocsMaker
 			
 			_md.Emit( "##### "+decl.kind.Capitalize()+" "+DeclIdent( decl,True )+xtends+implments+mods )
 			
-			_md.Emit( decl.docs )
-			
-			For Local inh:=0 Until 1
-				EmitMembers( "alias",ctype.scope,inh )
-				EmitMembers( "enum",ctype.scope,inh )
-				EmitMembers( "struct",ctype.scope,inh )
-				EmitMembers( "class",ctype.scope,inh )
-				EmitMembers( "interface",ctype.scope,inh )
-				EmitMembers( "const",ctype.scope,inh )
-				EmitMembers( "global",ctype.scope,inh )
-				EmitMembers( "field",ctype.scope,inh )
-				EmitMembers( "property",ctype.scope,inh )
-				EmitMembers( "constructor",ctype.scope,inh )
-				EmitMembers( "operator",ctype.scope,inh )
-				EmitMembers( "method",ctype.scope,inh )
-				EmitMembers( "function",ctype.scope,inh )
+			EmitDocs( decl )
+			
+			Local access:=DECL_PUBLIC
+			For Local acc:=0 Until 2
+			
+				Local inh:=False
+				
+				EmitMembers( "alias",ctype.scope,access,inh )
+				EmitMembers( "enum",ctype.scope,access,inh )
+				EmitMembers( "struct",ctype.scope,access,inh )
+				EmitMembers( "class",ctype.scope,access,inh )
+				EmitMembers( "interface",ctype.scope,access,inh )
+				EmitMembers( "const",ctype.scope,access,inh )
+				EmitMembers( "global",ctype.scope,access,inh )
+				EmitMembers( "field",ctype.scope,access,inh )
+				EmitMembers( "property",ctype.scope,access,inh )
+				EmitMembers( "constructor",ctype.scope,access,inh )
+				EmitMembers( "operator",ctype.scope,access,inh )
+				EmitMembers( "method",ctype.scope,access,inh )
+				EmitMembers( "function",ctype.scope,access,inh )
+				
+				access=DECL_PROTECTED
 			End
-		
+
 			Local docs:=_md.Flush()
 
 			Local page:=DeclPath( ctype.cdecl,ctype.scope.outer )
@@ -896,7 +932,7 @@ Class DocsMaker
 			If Not plist Continue
 			
 			Local decl:=plist.pdecl
-			If decl.kind<>kind Or DocsHidden( decl ) Continue
+			If decl.kind<>kind Or DocsHidden( decl,DECL_PUBLIC ) Continue
 
 			Local func:=plist.getFunc
 			If Not func 
@@ -910,9 +946,9 @@ Class DocsMaker
 			
 			EmitHeader( decl,func.scope )
 			
-			_md.Emit( "##### Property "+DeclIdent( decl )+" : "+TypeName( type,func.scope ) )
+			_md.Emit( "##### Property "+DeclIdent( decl )+":"+TypeName( type,func.scope ) )
 			
-			_md.Emit( decl.docs )
+			EmitDocs( decl )
 			
 			Local docs:=_md.Flush()
 			
@@ -937,7 +973,7 @@ Class DocsMaker
 			For Local func:=Eachin flist.funcs
 				Local decl:=func.fdecl
 				
-				If DocsHidden( decl ) Continue
+				If DocsHidden( decl,DECL_PUBLIC ) Continue
 				
 				If kind="constructor"
 					If decl.ident<>"new" Continue
@@ -965,21 +1001,19 @@ Class DocsMaker
 					Local type:=TypeName( func.ftype.argTypes[i],func.scope )
 					Local init:=""
 					If func.fdecl.type.params[i].init
-						init=" ="+func.fdecl.type.params[i].init.ToString()
+						init="="+func.fdecl.type.params[i].init.ToString()
 					Endif
-					params+=" , "+ident+" : "+type+init
+					If params params+=", "
+					params+=ident+":"+type+init
 				Next
-				params=params.Slice( 3 )
 				
-				_md.Emit( "##### "+tkind+DeclIdent( decl,True )+" : "+TypeName( func.ftype.retType,func.scope )+" ( "+params+" ) " )
+				_md.Emit( "##### "+tkind+DeclIdent( decl,True )+":"+TypeName( func.ftype.retType,func.scope )+"( "+params+" )" )
 	
 			Next
 			
 			If Not buf Continue
 			
-			For Local doc:=Eachin buf
-				_md.Emit( doc )
-			Next
+			EmitDocs( buf.Join( "~n" ) )
 		
 			Local docs:=_md.Flush()
 			

+ 139 - 76
src/mx2cc/docs/markdownbuffer.monkey2

@@ -1,6 +1,20 @@
 
 Namespace mx2.docs
 
+Function FindSpc:Int( str:String )
+	For Local i:=0 Until str.Length
+		If str[i]<=32 Return i
+	Next
+	Return str.Length
+End
+
+Function FindNonSpc:Int( str:String )
+	For Local i:=0 Until str.Length
+		If str[i]>32 Return i
+	Next
+	Return -1
+End
+
 Function Slugify:String( str:String )
 	Local st:=0
 	While st<str.Length And Not IsIdent( str[st] )
@@ -26,94 +40,143 @@ Class MarkdownBuffer
 
 	Alias LinkResolver:String( link:String )
 
-	Method New( linkResolver:LinkResolver=Null )
+	Alias TagHandler:Bool( tag:String,arg:String )
+	
+	Method New( url:String )
+	End
+
+	Method New( currentDir:String,linkResolver:LinkResolver=Null,tagHandler:TagHandler=Null )
+		_currentDir=currentDir
 		_linkResolver=linkResolver
+		_tagHandler=tagHandler
+	End
+	
+	Property CurrentDir:String()
+	
+		Return _currentDir
+	
+	Setter( currentDir:String )
+
+		_currentDir=currentDir
 	End
 	
 	Property Label:String()
+	
 		Return _label
 	End
-
-	Method Emit( markdown:String )
 	
-		If Not markdown.Contains( "~n" )
-			_buf.Push( ReplaceLinks( markdown ) )
+	Method Emit( line:String )
+	
+		If Not line.Contains( "~n" )
+			_buf.Push( ReplaceLinks( line ) )
 			Return
 		Endif
 		
-		Local lines:=markdown.Split( "~n" )
-		
-		For Local i:=0 Until lines.Length
+		Emit( line.Split( "~n" ) )
+	End
+
+	Method Emit( lines:String[],indent:Int=0 )
+	
+		Local i:=0
+		While i<lines.Length
 		
-			Local line:=lines[i].Trim()
-			
-			#rem
-			If line.StartsWith( "#" )
-				Local j:=FindSpc( line )
-				Local id:=line.Slice( j ).Trim()
-				Local slug:=Slugify( id )
-				_buf.Push( "<h"+j+" id='"+slug+"'>"+id+"</h"+j+">" )
-				continue
-			End
-			#End
+			Local line:=lines[i].Slice( indent )
+			i+=1
 			
-			If line.StartsWith( "@" )
-
-				Local j:=FindSpc( line )
-				Local id:=line.Slice( 1,j )
-				line=line.Slice( j ).Trim()
+			If Not line.StartsWith( "@" )
+				Emit( line )
+				Continue
+			Endif
+			
+			Local j:=FindSpc( line )
+			Local tag:=line.Slice( 1,j )
+			Local arg:=line.Slice( j ).Trim()
 				
-				Select id
-				Case "label"
+			Select tag
+			Case "label"
+			
+				_label=arg
 				
-					_label=line
-					
-				Case "param"
+			Case "param"
+			
+				_params.Push( arg )
 				
-					_params.Push( line )
-					
-				Case "return"
+			Case "return"
+			
+				_return=arg
 				
-					_return=line
-					
-				Case "example"
+			Case "example"
+			
+				_buf.Push( "```" )
+				
+				While i<lines.Length
 				
-					Local indent:=FindChar( lines[i] )
+					Local line:=lines[i]
 					i+=1
 					
-					_buf.Push( "```" )
-					
-					Local buf:=New StringStack
+					If line.Trim()="@end" Exit
 					
-					While i<lines.Length
-						Local line:=lines[i]
-						If line.Trim().StartsWith( "@end" ) Exit
-						i+=1
-						line=line.Slice( indent )
-						If line.StartsWith( "\#" ) line=line.Slice( 1 )
-						buf.Push( line )
-					Wend
+					line=line.Slice( indent )
 					
-					_buf.Push( buf.Join( "~n" ).Trim() )
+					If line.StartsWith( "#" ) line="\"+line
 					
+					_buf.Push( line )
+				Wend
+				
+				_buf.Push( "```" )
+				
+			Case "deprecated"
+			
+			Case "include"
+			
+				If arg.Length>1 And arg.StartsWith( "~q" ) And arg.EndsWith( "~q" ) arg=arg.Slice( 1,-1 )
+				
+				Local path:=_currentDir+arg
+				
+				Local text:=stringio.LoadString( path )
+				If Not text
+					Print "Failed to @include '"+path+"'"
+					Continue
+				Endif
+				
+				Select ExtractExt( arg ).ToLower()
+				Case ".monkey2"
+				
+					_buf.Push( "<a href=~q${CD}/"+arg+"~q class=~qnostyle~q>~n" )
 					_buf.Push( "```" )
+					_buf.Push( text )
+					_buf.Push( "```" )
+					_buf.Push( "~n</a>" )
 					
-				Case "see"
+				Case ".html"
 				
-					Continue
+					_buf.Push( text )
+					
+				Case ".txt"
 				
-				Default
+					_buf.Push( "```" )
+					
+					_buf.Push( text )
+					
+					_buf.Push( "```" )
+					
+				Case ".md"
 				
-					Print "MarkdownBuffer: unrecognized '"+lines[i]+"'"
+					Emit( text )
 					
+				Default
+					Print "Unrecognized @include file type '"+ExtractExt( arg )+"'"
 				End
-
-				Continue
-			Endif
 			
-			_buf.Push( ReplaceLinks( line ) )
+			Default
 			
-		Next
+				If Not _tagHandler( tag,arg )
+					Print "MarkdownBuffer: unrecognized directive @"+tag+" in "+line
+				Endif
+
+			End
+
+		Wend
 	
 	End
 	
@@ -148,9 +211,7 @@ Class MarkdownBuffer
 		Local markdown:=_buf.Join( "~n" ).Trim()+"~n"
 		
 		_buf.Clear()
-		
 		_return=""
-		
 		_label=""
 		
 		Local docs:=hoedown.MarkdownToHtml( markdown )
@@ -160,29 +221,31 @@ Class MarkdownBuffer
 	
 	Private
 	
+	Class Node
+		Field indent:Int
+		Field parent:Node
+		Field children:=New Stack<Node>
+		Field slug:String
+		Field url:String	'starts with module name eg: "monkey/"
+		
+		Method New( indent:Int=0,parent:Node=Null )
+			Self.indent=indent
+			Self.parent=parent
+			If parent parent.children.Push( Self )
+		End
+	End
+
+	Field _currentDir:String	
 	Field _linkResolver:LinkResolver
+	Field _tagHandler:TagHandler
+	
+	Field _nodeStack:Stack<Node>
+	
 	Field _buf:=New StringStack
 	Field _params:=New StringStack
 	Field _return:String
 	Field _label:String
 	
-	Field _toc:=New JsonBuffer
-	Field _tocNest:Int
-	
-	Method FindSpc:Int( str:String )
-		For Local i:=0 Until str.Length
-			If str[i]<=32 Return i
-		Next
-		Return str.Length
-	End
-
-	Method FindChar:Int( str:String )
-		For Local i:=0 Until str.Length
-			If str[i]>32 Return i
-		Next
-		Return -1
-	End
-	
 	Method ReplaceLinks:String( line:String )
 	
 		Repeat

+ 1 - 1
src/mx2cc/mx2.monkey2

@@ -48,4 +48,4 @@ Using lib.c
 ' 3) edit .sh and .bat files to use new version (common.sh, common.bat)
 ' 4) ./rebuildall
 '
-Const MX2CC_VERSION:="1.0.2"
+Const MX2CC_VERSION:="1.0.3"

+ 60 - 40
src/mx2cc/mx2cc.monkey2

@@ -11,6 +11,7 @@ Using mx2.docs
 #Import "docs/docsmaker.monkey2"
 #Import "docs/jsonbuffer.monkey2"
 #Import "docs/markdownbuffer.monkey2"
+#Import "docs/manpage.monkey2"
 
 Using libc..
 Using std..
@@ -18,11 +19,11 @@ Using mx2..
 
 Global StartDir:String
 
-Const TestArgs:="mx2cc makemods"
+Const TestArgs:="mx2cc makedocs mojox"
 
-'Const TestArgs:="mx2cc makedocs monkey std mojo"
+'Const TestArgs:="mx2cc makemods -clean std"' -target=android"
 
-'Const TestArgs:="mx2cc makeapp src/mx2cc/test.monkey2"
+'Const TestArgs:="mx2cc makeapp -clean src/mx2cc/test.monkey2"
 
 'Const TestArgs:="mx2cc makeapp src/ted2/ted2.monkey2"
 
@@ -53,7 +54,8 @@ Function Main()
 	
 	If args.Length<2
 
-		Print "Usage: mx2cc makeapp|makemods|makedocs [-run] [-clean] [-verbose] [-target=desktop|emscripten] [-config=debug|release] source|modules..."
+		Print "Usage: mx2cc makeapp|makemods|makedocs [-build|-run] [-clean] [-verbose[=1|2|3]] [-target=desktop|emscripten] [-config=debug|release] [-apptype=gui|console] source|modules..."
+		Print "Defaults: -run -target=desktop -config=debug -apptype=gui"
 
 #If __CONFIG__="release"
 		exit_(0)
@@ -62,6 +64,8 @@ Function Main()
 		
 	Endif
 	
+	Local ok:=False
+	
 	Try
 	
 		Local cmd:=args[1]
@@ -69,24 +73,25 @@ Function Main()
 		
 		Select cmd
 		Case "makeapp"
-			MakeApp( args )
+			ok=MakeApp( args )
 		Case "makemods"
-			MakeMods( args )
+			ok=MakeMods( args )
 		Case "makedocs"
-			MakeDocs( args )
+			ok=MakeDocs( args )
 		Default
 			Fail( "Unrecognized mx2cc command: '"+cmd+"'" )
 		End
 		
 	Catch ex:BuildEx
 	
-		Fail( "Build error." )
+		Fail( "Internal mx2cc build error" )
 		
 	End
 	
+	If Not ok libc.exit_( 1 )
 End
 
-Function MakeApp( args:String[] )
+Function MakeApp:Bool( args:String[] )
 
 	Local opts:=New BuildOpts
 	opts.productType="app"
@@ -116,24 +121,26 @@ Function MakeApp( args:String[] )
 	Local builder:=New Builder( opts )
 	
 	builder.Parse()
-	If builder.errors.Length Return
+	If builder.errors.Length Return False
 	
 	builder.Semant()
-	If builder.errors.Length Return
+	If builder.errors.Length Return False
 	
 	builder.Translate()
-	If builder.errors.Length Return
+	If builder.errors.Length Return False
 
 	builder.Compile()
-	If builder.errors.Length Return
+	If builder.errors.Length Return False
 
 	Local app:=builder.Link()
-	If builder.errors.Length Return
+	If builder.errors.Length Return False
 	
 	If Not opts.run Print "Application built:"+app
+	
+	Return True
 End
 
-Function MakeMods( args:String[] )
+Function MakeMods:Bool( args:String[] )
 
 	Local opts:=New BuildOpts
 	opts.productType="module"
@@ -147,6 +154,8 @@ Function MakeMods( args:String[] )
 
 	If Not args args=EnumModules()
 	
+	Local errs:=0
+	
 	For Local modid:=Eachin args
 	
 		Local path:="modules/"+modid+"/"+modid+".monkey2"
@@ -161,22 +170,25 @@ Function MakeMods( args:String[] )
 		Local builder:=New Builder( opts )
 		
 		builder.Parse()
-		If builder.errors.Length Continue
+		If builder.errors.Length errs+=1;Continue
 
 		builder.Semant()
-		If builder.errors.Length Continue
+		If builder.errors.Length errs+=1;Continue
 		
 		builder.Translate()
-		If builder.errors.Length Continue
+		If builder.errors.Length errs+=1;Continue
 		
 		builder.Compile()
-		If builder.errors.Length Continue
+		If builder.errors.Length errs+=1;Continue
 
 		builder.Link()
+		If builder.errors.Length errs+=1
 	Next
+	
+	Return errs=0
 End
 
-Function MakeDocs( args:String[] )
+Function MakeDocs:Bool( args:String[] )
 
 	Local opts:=New BuildOpts
 	opts.productType="module"
@@ -193,7 +205,7 @@ Function MakeDocs( args:String[] )
 	
 	Local docsMaker:=New DocsMaker
 	
-	Local mx2_api:=""
+	Local errs:=0
 	
 	For Local modid:=Eachin args
 
@@ -209,28 +221,36 @@ Function MakeDocs( args:String[] )
 		Local builder:=New Builder( opts )
 
 		builder.Parse()
-		If builder.errors.Length Continue
+		If builder.errors.Length errs+=1;Continue
 		
 		builder.Semant()
-		If builder.errors.Length Continue
+		If builder.errors.Length errs+=1;Continue
+		
+		docsMaker.MakeDocs( builder.modules.Top )
+	Next
+	
+	Local api_indices:=New StringStack
+	Local man_indices:=New StringStack
+	
+	For Local modid:=Eachin EnumModules()
+	
+		Local index:=LoadString( "modules/"+modid+"/docs/__MANPAGES__/index.js" )
+		If index man_indices.Push( index )
 		
-		Local tree:=docsMaker.MakeDocs( builder.modules.Top )
+		index=LoadString( "modules/"+modid+"/docs/__PAGES__/index.js" )
+		If index api_indices.Push( index )
 		
-		If mx2_api mx2_api+=","
-		mx2_api+=tree
-
 	Next
 	
-'	stringio.SaveString( "mx2_api_tree="+mx2_api+";","docs/api-tree.js" )
-	Local mods:=stringio.LoadString( "docs/modules_template.html" )
-	mods=mods.Replace( "${MX2_API}",mx2_api )
-	stringio.SaveString( mods,"docs/modules.html" )
+	Local page:=LoadString( "docs/modules_template.html" )
+	page=page.Replace( "${API_INDEX}",api_indices.Join( "," ) )
+	SaveString( page,"docs/modules.html" )
 	
-	Local lang_nav:=docsMaker.MakeLangNav()
-	Local lang:=stringio.LoadString( "docs/language_template.html" )
-	lang=lang.Replace( "${LANG_NAV}",lang_nav )
-	stringio.SaveString( lang,"docs/language.html" )
+	page=LoadString( "docs/manuals_template.html" )
+	page=page.Replace( "${MAN_INDEX}",man_indices.Join( "," ) )
+	SaveString( page,"docs/manuals.html" )
 	
+	Return True
 End
 
 Function ParseOpts:String[]( opts:BuildOpts,args:String[] )
@@ -270,10 +290,10 @@ Function ParseOpts:String[]( opts:BuildOpts,args:String[] )
 			End
 		Case "-target"
 			Select val
-			Case "desktop","emscripten"
+			Case "desktop","emscripten","android","ios"
 				opts.target=val
 			Default
-				Fail( "Invalid value for 'target' option: '"+val+"' - must be 'desktop' or 'emscripten'" )
+				Fail( "Invalid value for 'target' option: '"+val+"' - must be 'desktop', 'emscripten', 'android' or 'ios'" )
 			End
 		Case "-config"
 			Select val
@@ -284,10 +304,10 @@ Function ParseOpts:String[]( opts:BuildOpts,args:String[] )
 			End
 		Case "-verbose"
 			Select val
-			Case "0","1","2","-1"
+			Case "0","1","2","3","-1"
 				opts.verbose=Int( val )
 			Default
-				Fail( "Invalid value for 'verbose' option: '"+val+"' - must be '0', '1', '2' or '-1'" )
+				Fail( "Invalid value for 'verbose' option: '"+val+"' - must be '0', '1', '2', '3' or '-1'" )
 			End
 		Default
 			Fail( "Invalid option: '"+opt+"'" )
@@ -405,5 +425,5 @@ Function Fail( msg:String )
 	Print ""
 	Print msg
 		
-	exit_( -1 )
+	exit_( 1 )
 End

+ 7 - 2
src/mx2cc/parser.monkey2

@@ -2024,10 +2024,15 @@ Class Parser
 				If _ccnest=_ifnest 
 					p.Bump()
 					Local path:=p.ParseString()
+					
 					If path.StartsWith( "<" ) And path.EndsWith( ">" )
+					
 						If Not ExtractExt( path ) path=path.Slice( 0,-1 )+".monkey2>"
-					Else
-						If Not ExtractExt( path ) And Not path.Contains( "@/" )path+=".monkey2"
+						
+					Else If Not path.Contains( "@/" ) And Not path.EndsWith( "/" )
+					
+						If Not ExtractExt( path ) path+=".monkey2"
+						
 					Endif
 					_imports.Push( path )
 				Endif

+ 28 - 5
src/mx2cc/scope.monkey2

@@ -364,21 +364,44 @@ Class Block Extends Scope
 		
 		Local vvar:=Cast<VarValue>( node )
 		If vvar
+		
 			Select vvar.vdecl.kind
 			Case "local","param","capture"
-				If Cast<Block>( vvar.scope ).func<>func
+			
+				Local vfunc:=Cast<Block>( vvar.scope ).func
+				
+				If vfunc<>func
 				
 					If func.fdecl.kind<>"lambda" Return Null
 					
-'					Print "Capturing "+vvar.vdecl.ident
+					'capture vvar...
+					'
+					'find all funcs (lambdas) up to vvar func
+					'
+					Local tfunc:=func,funcs:=New Stack<FuncValue>
+
+					While tfunc<>vfunc
+						funcs.Push( tfunc )
+						tfunc=Cast<Block>( tfunc.block.outer ).func
+					Wend
+					
+					While Not funcs.Empty
+					
+						Local tfunc:=funcs.Pop()
+						
+						'FXIME: this pollutes outer func scope with captured var name.
+						'
+						vvar=New VarValue( "capture",vvar.vdecl.ident,vvar,tfunc.block )
+						tfunc.block.Insert( vvar.vdecl.ident,vvar )
+						tfunc.captures.Push( vvar )
+					
+					Wend
 					
-					vvar=New VarValue( "capture",vvar.vdecl.ident,vvar,func.block )
-					func.block.Insert( vvar.vdecl.ident,vvar )
-					func.captures.Push( vvar )
 					node=vvar
 					
 				Endif
 			End
+			
 		Endif
 		
 		Return node.ToValue( func.selfValue )

+ 15 - 25
src/mx2cc/test.monkey2

@@ -1,34 +1,24 @@
 
-Namespace test
-
-Enum MyEnum
-	X=1,Y,Z=10
-End
-
-Class Map<T>
-
-	Enum Color
-		Red,Black
-	End
-
-	Field color:Color
-		
-	Method New()
-		color=Color.Red
-	End
-End
+#Import "docs@/mojox/filebrowser_filetypes"
 
+Namespace test
 
 Function Main()
 
-	Local map:=New Map<Int>
-
-	Local x:=MyEnum.X
-	Local y:=MyEnum.Y
+	Local t:=1
 	
-	Local t:=x|y
+	Local x:=Lambda()
 	
-	t|=MyEnum.Z
+		Local y:=Lambda()
+		
+			Print t
+		
+		End
+		
+		Local t:=10
+	
+	End
 	
-End
+	x()
 
+End

+ 17 - 0
src/mx2cc/type.monkey2

@@ -249,12 +249,29 @@ Class PrimType Extends Type
 	
 		If type=Self Return 0
 		
+		Select type
+		Case StringType
+
+			If IsNumeric Return MAX_DISTANCE
+
+		Case CStringClass,WStringClass,Utf8StringClass
+		
+			If Self=StringType Or IsNumeric Return MAX_DISTANCE
+			
+		Default
+		
+			If TCast<PrimType>( type ) Return MAX_DISTANCE
+		
+		End
+		
+		#rem
 		If TCast<PrimType>( type ) Return MAX_DISTANCE
 		
 		Select type
 		Case CStringClass,WStringClass,Utf8StringClass
 			Return MAX_DISTANCE
 		End
+		#end
 		
 		Return -1
 	End