'' main module, front-end
''
'' chng: sep/2004 written [v1ctor]
''       dec/2004 linux support added [lillo]
''       jan/2005 dos support added [DrV]

#include once "fb.bi"
#include once "hlp.bi"
#include once "hash.bi"
#include once "list.bi"
#include once "objinfo.bi"

#include once "file.bi"

#if defined( ENABLE_STANDALONE ) and defined( __FB_WIN32__ )
	#define ENABLE_GORC
#endif

enum
	PRINT_HOST
	PRINT_TARGET
	PRINT_X
	PRINT_FBLIBDIR
	PRINT_SHA1
	PRINT_FORK_ID
end enum

type FBC_EXTOPT
	gas         as string
	ld          as string
	gcc         as string
end type

type FBCIOFILE
	'' Input file name (usually *.bas, but also *.rc, *.res, *.xpm)
	srcfile         as string     '' input file

	'' Output .o file
	'' - for modules from the command line this points to a node from
	''   fbc.objlist, see also fbcAddObj()
	'' - for example in hCompileFbctinf(), add temporary FBCIOFILE is used,
	''   with objfile pointing to a string var on stack
	objfile         as string ptr

	'' Whether -o was used to override the default .o file name
	is_custom_objfile   as integer
end type

type FBC_OBJINF
	lang        as FB_LANG
	mt          as integer
end type

type FBCCTX
	'' For command line parsing
	optid               as integer     '' Current option
	lastmodule          as FBCIOFILE ptr '' module for last input file, so the default .o name can be overwritten with a following -o filename
	objfile             as string      '' -o filename waiting for next input file
	backend             as integer     '' FB_BACKEND_* given via -gen, or -1 if -gen wasn't given
	cputype             as integer     '' FB_CPUTYPE_* (-arch's argument), or -1
	cputype_is_native   as integer     '' Whether -arch native was used
	asmsyntax           as integer     '' FB_ASMSYNTAX_* from -asm, or -1 if not given

	emitasmonly         as integer     '' write out FB backend output file only (.asm/.c)
	keepasm             as integer     '' preserve FB backend output file (.asm/.c)
	emitfinalasmonly    as integer     '' write out final .asm file only
	keepfinalasm        as integer     '' preserve final .asm
	keepobj             as integer
	verbose             as integer
	showversion         as integer
	showhelp            as integer
	print               as integer     '' PRINT_* (-print option)

	'' Command line input
	modules             as TLIST    '' FBCIOFILE's for input .bas files
	rcs                 as TLIST    '' FBCIOFILE's for input .rc/.res files
	xpm                 as FBCIOFILE '' .xpm input file
	temps               as TSTRSET  '' Temporary files to delete at shutdown
	objlist             as TLIST    '' Objects from command line and from compilation
	libfiles            as TLIST
	libs                as TSTRSET
	libpaths            as TSTRSET
	excludedlibs        as TSTRSET  '' lib names explicitly excluded via -nodeflib option(s)

	'' Final list of libs and paths for linking
	'' (each module can have #inclibs and #libpaths and add more, and for
	'' objinfo emitting only the module-specific libs are wanted, so there
	'' are multiple lists necessary to allow each module to start fresh
	'' with the same input libs)
	finallibs           as TSTRSET
	finallibpaths       as TSTRSET

	outname             as zstring * FB_MAXPATHLEN+1
	mainname            as zstring * FB_MAXPATHLEN+1
	entry               as zstring * FB_MAXNAMELEN+1
	mainset             as integer
	mapfile             as zstring * FB_MAXPATHLEN+1
	subsystem           as zstring * FB_MAXNAMELEN+1
	extopt              as FBC_EXTOPT
#ifndef ENABLE_STANDALONE
	target              as zstring * FB_MAXNAMELEN+1  '' Target system identifier (e.g. a name like "win32", or a GNU triplet) to prefix in front of cross-compiling tool names
	targetprefix        as zstring * FB_MAXNAMELEN+1  '' same, but with "-" appended, if there was a target id given; otherwise empty.
#endif
	sysroot             as zstring * FB_MAXPATHLEN+1
	xbe_title           as zstring * FB_MAXNAMELEN+1  '' For the '-title <title>' xbox option
	nodeflibs           as integer
	nofbrt0             as integer  '' If we should exclude fbrt0.o or fbrt0pic.o (implied by nodeflibs, and optional by -nolib fbrt0.o,fbrt0pic.o)
	staticlink          as integer
	stripsymbols        as integer

	'' Compiler paths
	prefix              as zstring * FB_MAXPATHLEN+1  '' Path from -prefix or empty
	binpath             as zstring * FB_MAXPATHLEN+1  '' standalone=prefix/bin/target/  normal=prefix/<fbname>/(target|build)prefix
	incpath             as zstring * FB_MAXPATHLEN+1  '' standalone=prefix/inc          normal=prefix/include/<fbname>
	libpath             as zstring * FB_MAXPATHLEN+1  '' standalone=prefix/lib/target   normal=prefix/lib[64]/<fbname>/target

	'' Tool prefix
	buildprefix         as zstring * FB_MAXPATHLEN+1  '' command line option to override target prefix (affects tool names executed)

	objinf              as FBC_OBJINF
end type

enum FBCTOOL
	FBCTOOL_NONE = 0
	FBCTOOL_AS
	FBCTOOL_AR
	FBCTOOL_LD
	FBCTOOL_GCC
	FBCTOOL_LLC
	FBCTOOL_CLANG
	FBCTOOL_DLLTOOL
	FBCTOOL_GORC
	FBCTOOL_WINDRES
	FBCTOOL_CXBE
	FBCTOOL_DXEGEN
	FBCTOOL_EMAS
	FBCTOOL_EMAR
	FBCTOOL_EMLD
	FBCTOOL_EMCC
	FBCTOOL_ELF2DOL
	FBCTOOL__COUNT
end enum

enum FBCTOOLFLAG
	FBCTOOLFLAG_INVALID            = 0  '' tool is disabled
	FBCTOOLFLAG_ASSUME_EXISTS      = 1  '' assume the tool exists
	FBCTOOLFLAG_CAN_USE_ENVIRON    = 2  '' allow path to tool to specified by environment variable
	FBCTOOLFLAG_FOUND              = 4  '' tool was checked for
	FBCTOOLFLAG_RELYING_ON_SYSTEM  = 8  '' tool is expected to be on system PATH

	FBCTOOLFLAG_DEFAULT = FBCTOOLFLAG_ASSUME_EXISTS or FBCTOOLFLAG_CAN_USE_ENVIRON
end enum

type FBCTOOLINFO
	name as zstring * 16                  '' default name of tool to invoke
	env_variable as zstring * 16          '' environment variable to override
	flags as FBCTOOLFLAG
	path as zstring * (FB_MAXPATHLEN + 1) '' cached tool path and name
end type

#define fbctoolGetFlags( tool, f )   ((fbctoolTB( tool ).flags and (f)) <> 0)
#define fbctoolSetFlags( tool, f )   fbctoolTB( tool ).flags or= f
#define fbctoolUnsetFlags( tool, f ) fbctoolTB( tool ).flags and= not f

'' must be same order as enum FBCTOOL
static shared as FBCTOOLINFO fbctoolTB(0 to FBCTOOL__COUNT-1) = _
{ _
	/' FBCTOOL_NONE    '/ ( ""       , ""       , FBCTOOLFLAG_INVALID  ), _
	/' FBCTOOL_AS      '/ ( "as"     , "AS"     , FBCTOOLFLAG_DEFAULT  ), _
	/' FBCTOOL_AR      '/ ( "ar"     , "AR"     , FBCTOOLFLAG_DEFAULT  ), _
	/' FBCTOOL_LD      '/ ( "ld"     , "LD"     , FBCTOOLFLAG_DEFAULT  ), _
	/' FBCTOOL_GCC     '/ ( "gcc"    , "GCC"    , FBCTOOLFLAG_DEFAULT  ), _
	/' FBCTOOL_LLC     '/ ( "llc"    , "LLC"    , FBCTOOLFLAG_DEFAULT  ), _
	/' FBCTOOL_CLANG   '/ ( "clang"  , "CLANG"  , FBCTOOLFLAG_DEFAULT  ), _
	/' FBCTOOL_DLLTOOL '/ ( "dlltool", "DLLTOOL", FBCTOOLFLAG_DEFAULT  ), _
	/' FBCTOOL_GORC    '/ ( "GoRC"   , "GORC"   , FBCTOOLFLAG_DEFAULT  ), _
	/' FBCTOOL_WINDRES '/ ( "windres", "WINDRES", FBCTOOLFLAG_DEFAULT  ), _
	/' FBCTOOL_CXBE    '/ ( "cxbe"   , "CXBE"   , FBCTOOLFLAG_DEFAULT  ), _
	/' FBCTOOL_DXEGEN  '/ ( "dxe3gen", "DXEGEN" , FBCTOOLFLAG_DEFAULT  ), _
	/' FBCTOOL_EMAS    '/ ( "emcc"   , "EMAS"   , FBCTOOLFLAG_DEFAULT  ), _
	/' FBCTOOL_EMAR    '/ ( "emar"   , "EMAR"   , FBCTOOLFLAG_DEFAULT  ), _
	/' FBCTOOL_EMLD    '/ ( "emcc"   , "EMLD"   , FBCTOOLFLAG_DEFAULT  ), _
	/' FBCTOOL_EMCC    '/ ( "emcc"   , "EMCC"   , FBCTOOLFLAG_DEFAULT  ), _
	/' FBCTOOL_ELF2DOL '/ ( "elf2dol", "ELF2DOL", FBCTOOLFLAG_DEFAULT  )  _
}

declare sub fbcFindBin _
	( _
		byval tool as integer, _
		byref path as string _
	)

declare sub hPrintVersion( byval verbose as integer )
declare sub hAddDarwinFrameworks( byref ldcline as string )
declare sub fbcAddDefLib(byval libname as zstring ptr)

#macro safeKill(f)
	if( kill( f ) <> 0 ) then
	end if
#endmacro

dim shared as FBCCTX fbc

private sub fbcInit( )
	const FBC_INITFILES = 64

	fbc.backend = -1
	fbc.cputype = -1
	fbc.asmsyntax = -1

	listInit( @fbc.modules, FBC_INITFILES, sizeof(FBCIOFILE) )
	listInit( @fbc.rcs, FBC_INITFILES\4, sizeof(FBCIOFILE) )
	strsetInit( @fbc.temps, FBC_INITFILES\4 )
	strlistInit( @fbc.objlist, FBC_INITFILES )
	strlistInit( @fbc.libfiles, FBC_INITFILES\4 )
	strsetInit( @fbc.libs, FBC_INITFILES\4 )
	strsetInit( @fbc.libpaths, FBC_INITFILES\4 )
	strsetInit( @fbc.excludedlibs, FBC_INITFILES\4 )

	strsetInit(@fbc.finallibs, FBC_INITFILES\2)
	strsetInit(@fbc.finallibpaths, FBC_INITFILES\2)

	fbGlobalInit()

#ifdef ENABLE_STRIPALL
	fbc.stripsymbols = TRUE
#endif

	fbc.objinf.lang = fbGetOption( FB_COMPOPT_LANG )

	fbc.print = -1
end sub

private sub hSetOutName( )
	'' Determine the output binary/archive's name if not given via -x
	if( len( fbc.outname ) > 0 ) then
		exit sub
	end if

	'' Creating a static lib?
	if( fbGetOption( FB_COMPOPT_OUTTYPE ) = FB_OUTTYPE_STATICLIB ) then
		fbc.outname = hStripFilename( fbc.mainname ) + _
			"lib" + hStripPath( fbc.mainname ) + ".a"
		exit sub
	end if

	'' Otherwise, we're creating an .exe or DLL/shared lib
	fbc.outname = fbc.mainname

	select case( fbGetOption( FB_COMPOPT_OUTTYPE ) )
	case FB_OUTTYPE_EXECUTABLE
		select case( fbGetOption( FB_COMPOPT_TARGET ) )
		case FB_COMPTARGET_DOS, FB_COMPTARGET_CYGWIN, _
			FB_COMPTARGET_WIN32, FB_COMPTARGET_XBOX
			'' Note: XBox target creates an .exe first,
			'' then uses cxbe to turn it into an .xbe later
			fbc.outname += ".exe"
		case FB_COMPTARGET_JS
			fbc.outname += ".html"
		case FB_COMPTARGET_WII
			fbc.outname += ".dol"
		end select
	case FB_OUTTYPE_DYNAMICLIB
		select case( fbGetOption( FB_COMPOPT_TARGET ) )
		case FB_COMPTARGET_CYGWIN, FB_COMPTARGET_WIN32
			fbc.outname += ".dll"
		case FB_COMPTARGET_LINUX, FB_COMPTARGET_DARWIN, _
		     FB_COMPTARGET_FREEBSD, FB_COMPTARGET_OPENBSD, _
		     FB_COMPTARGET_NETBSD, FB_COMPTARGET_DRAGONFLY, _
		     FB_COMPTARGET_SOLARIS, FB_COMPTARGET_ILLUMOS, _
		     FB_COMPTARGET_ANDROID
			fbc.outname = hStripFilename( fbc.outname ) + _
				"lib" + hStripPath( fbc.outname ) + ".so"
		case FB_COMPTARGET_DOS
			fbc.outname += ".dxe"
		end select
	end select
end sub

private sub fbcEnd( byval errnum as integer )
	'' Clean up temporary files
	dim as TSTRSETITEM ptr file = listGetHead(@fbc.temps.list)
	while( file )
		safeKill( file->s )
		file = listGetNext( file )
	wend

	end errnum
end sub

private sub fbcAddTemp(byref file as string)
	strsetAdd(@fbc.temps, file, 0)
end sub

private sub fbcRemoveTemp(byref file as string)
	strsetDel(@fbc.temps, file)
end sub

private function fbcAddObj( byref file as string ) as string ptr
	'' .o's should be linked/archived in the order they were found on
	'' command line, so callers of this function must take care to preserve
	'' the order...
	dim as string ptr s = listNewNode( @fbc.objlist )
	*s = file
	function = s
end function

private function hGet1stOutputLineFromCommand( byref cmd as string ) as string
	var f = freefile( )
	if( open pipe( cmd, for input, as f ) <> 0 ) then
		exit function
	end if

	dim ln as string
	line input #f, ln

	close f
	return ln
end function

private function hGetClangTargetOption( ) as string
	if( (fbGetOption( FB_COMPOPT_TARGET ) = FB_COMPTARGET_WIN32) and _
		(fbGetCpuFamily( ) = FB_CPUFAMILY_AARCH64) ) then
		''
		'' The MSYS2 Windows ARM64 package uses a host-runnable clang with
		'' an ARM64 MinGW sysroot.  The compiler executable name does not
		'' encode that target, so pass the triple explicitly.
		''
		function = "--target=aarch64-w64-mingw32 "
	else
		function = ""
	end if
end function

private function fbcUseLldLinker( ) as integer
	'' win32-aarch64 builds from MSYS2 use ld.lld.exe as the practical linker.
	'' The package also provides ld.exe for compatibility, but this is actually
	'' the LLVM driver binary and does not accept all GNU ld linker-option forms
	'' that fbc currently emits.
	if( (fbGetOption( FB_COMPOPT_TARGET ) = FB_COMPTARGET_WIN32) and _
		(fbGetOption( FB_COMPOPT_BACKEND ) = FB_BACKEND_CLANG) and _
		(fbGetCpuFamily( ) = FB_CPUFAMILY_AARCH64) ) then
		return TRUE
	end if

	function = FALSE
end function

'' Pass some arguments to gcc/clang and read the results. Returns an empty string on
'' an error.
private function fbcQueryCC( byref options as string ) as string
	dim as string path

	select case( fbGetOption( FB_COMPOPT_BACKEND ) )
	case FB_BACKEND_CLANG
		fbcFindBin( FBCTOOL_CLANG, path )

	'' For gcc backend and all other backends assume we want to query gcc
	case else
		fbcFindBin( FBCTOOL_GCC, path )
	end select

	select case( fbGetCpuFamily( ) )
	case FB_CPUFAMILY_X86
		path += " -m32"
	case FB_CPUFAMILY_X86_64
		path += " -m64"
	case FB_CPUFAMILY_PPC
		path += " -m32"
	case FB_CPUFAMILY_PPC64, FB_CPUFAMILY_PPC64LE
		path += " -m64"
	end select

	if( fbGetOption( FB_COMPOPT_BACKEND ) = FB_BACKEND_CLANG ) then
		path += " " + hGetClangTargetOption( )
	end if

	path += options

	dim as integer ff = freefile( )
	if( open pipe( path, for input, as ff ) <> 0 ) then
		exit function
	end if

	dim ret as string
	line input #ff, ret

	close ff

	return ret
end function

#if defined( __FB_HAIKU__ )
private function fbcUseHaikuSecondaryX86Tools( ) as integer
	if( (fbGetOption( FB_COMPOPT_TARGET ) = FB_COMPTARGET_HAIKU) and _
		(fbIs64Bit( ) = FALSE) and _
		(len( fbc.buildprefix ) = 0) and _
		(len( fbc.targetprefix ) = 0) ) then
		function = TRUE
	end if
end function

private function fbcFindHaikuSecondaryX86Tool _
	( _
		byval tool as integer, _
		byref path as string _
	) as integer

	if( fbcUseHaikuSecondaryX86Tools( ) = FALSE ) then
		exit function
	end if

	select case tool
	case FBCTOOL_GCC
		path = fbc.prefix + "develop" + FB_HOST_PATHDIV + _
			"tools" + FB_HOST_PATHDIV + "x86" + FB_HOST_PATHDIV + _
			"bin" + FB_HOST_PATHDIV + "gcc"
		function = hFileExists( path )

	case FBCTOOL_AS
		path = fbcQueryCC( " -print-prog-name=as" )
		function = (len( path ) > 0)

	case FBCTOOL_AR
		path = fbcQueryCC( " -print-prog-name=ar" )
		function = (len( path ) > 0)

	case FBCTOOL_LD
		path = fbcQueryCC( " -print-prog-name=ld" )
		function = (len( path ) > 0)
	end select
end function
#endif

''
'' Build the path to a certain file in our lib/ directory (or, in case of
'' non-standalone, somewhere in a system directory such as /usr/lib).
''
'' standalone: Will always return the path to lib/<target>/<file>, no matter
''             whether it exists or not - because that's where it should be.
''             This way the "file not found" errors will be prettier.
''
'' normal: Will check lib/ and query gcc if not found. Querying gcc may fail,
''         because of that an empty string may be returned.
''
private function fbcBuildPathToLibFile( byval file as zstring ptr ) as string
	dim as string found

	''
	'' The Standalone build expects to have all needed files in its lib/,
	'' so it needs to do nothing but build up the path and use that.
	''
	'' Normal however wants to use the "system's" files (and only has few
	'' files in its own lib/).
	''
	'' Typically libgcc.a, crtbegin.o, crtend.o will be inside
	'' gcc's sub-directory in lib/gcc/target/version, i.e. Normal can only
	'' find them via 'gcc -print-file-name=foo' (except for hard-coding
	'' against a specific gcc target/version, but that's not a good option).
	''

	found = fbc.libpath + FB_HOST_PATHDIV + *file

	#ifdef ENABLE_STANDALONE
	'' running a standalone version of fbc to build with android
	'' ndk is not recommended, but if we are, try and query the
	'' sysroot if it wasn't already supplied.  Otherwise
	'' if with are running a standalone fbc, just return the
	'' computed library file name if we didn't explicitly pass
	'' in a -sysroot
	if( fbGetOption( FB_COMPOPT_TARGET ) <> FB_COMPTARGET_ANDROID ) then
		if( len( fbc.sysroot ) = 0 ) then
			return found
		end if
	end if
	#endif

	'' Does it exist in our lib/?
	if( hFileExists( found ) ) then
		'' Overrides anything else
		return found
	end if

	'' Not found in our lib/, query the target-specific gcc
	dim as string path
	select case( fbGetOption( FB_COMPOPT_BACKEND ) )
	case FB_BACKEND_CLANG
		fbcFindBin( FBCTOOL_CLANG, path )

	'' For gcc backend and all other backends assume we want to query gcc
	case else
		fbcFindBin( FBCTOOL_GCC, path )
	end select

	select case( fbGetCpuFamily( ) )
	case FB_CPUFAMILY_X86
		path += " -m32"
	case FB_CPUFAMILY_X86_64
		path += " -m64"
	case FB_CPUFAMILY_PPC
		path += " -m32"
	case FB_CPUFAMILY_PPC64, FB_CPUFAMILY_PPC64LE
		path += " -m64"
	end select

	if( fbGetOption( FB_COMPOPT_BACKEND ) = FB_BACKEND_CLANG ) then
		path += " " + hGetClangTargetOption( )
	end if

	if( len( fbc.sysroot ) ) then
		path += " --sysroot=" + fbc.sysroot
	end if

	path += " -print-file-name=" + *file

	found = hGet1stOutputLineFromCommand( path )
	if( len( found ) = 0 ) then
		exit function
	end if

	if( found = hStripPath( found ) ) then
		exit function
	end if

	function = found
end function

'' gcc may have a default sysroot which we need to pass on to ld. Or it may not know the sysroot.
private function fbcFindSysroot( ) as string
	'' Query the target-specific gcc
	'' (TODO: need to tell which arch and ABI we are targetting, especially on android)
	dim as string path

	select case( fbGetOption( FB_COMPOPT_BACKEND ) )
	case FB_BACKEND_CLANG
		fbcFindBin( FBCTOOL_CLANG, path )

	'' For gcc backend and all other backends assume we want to query gcc
	case else
		fbcFindBin( FBCTOOL_GCC, path )
	end select

	path += " --print-sysroot"
	return hGet1stOutputLineFromCommand( path )
end function

'' Retrieve the path to a library file, or an empty string if it can't be found.
private function fbcFindLibFile( byval file as zstring ptr ) as string
	dim as string found
	found = fbcBuildPathToLibFile( file )
	if( len( found ) > 0 ) then
		if( hFileExists( found ) = FALSE ) then
			found = ""
		end if
	end if
	function = found
end function

private sub fbcAddDefLibPath(byref path as string)
	strsetAdd(@fbc.finallibpaths, path, TRUE)
end sub

#ifndef ENABLE_STANDALONE
private sub fbcAddLibPathFor( byval libname as zstring ptr )
	dim as string path
	path = hStripFilename( fbcBuildPathToLibFile( libname ) )
	path = pathStripDiv( path )
	if( len( path ) > 0 ) then
		fbcAddDefLibPath( path )
	end if
end sub
#endif

private sub fbcFindBin _
	( _
		byval tool as integer, _
		byref path as string _
	)

	'' Re-use path from last time if possible
	if( fbctoolGetFlags( tool, FBCTOOLFLAG_FOUND ) ) then
		path = fbctoolTB( tool ).path
		exit sub
	end if

	fbctoolUnsetFlags( tool, FBCTOOLFLAG_RELYING_ON_SYSTEM )

	'' a) Use the path from the corresponding environment variable if it's set
	if( (fbctoolTB(tool).flags and FBCTOOLFLAG_CAN_USE_ENVIRON) <> 0 ) then
		path = environ( fbctoolTB(tool).env_variable )
	end if

	if( len( path ) = 0 ) then
		#if defined( __FB_HAIKU__ )
			if( fbcFindHaikuSecondaryX86Tool( tool, path ) ) then
				fbctoolTB( tool ).path = path
				fbctoolSetFlags( tool, FBCTOOLFLAG_FOUND )
				exit sub
			end if
		#endif

		'' b) Try bin/ directory
		#ifndef ENABLE_STANDALONE
			'' normal build, the build/target prefix is already appended to binpath
			if( (tool = FBCTOOL_LD) and fbcUseLldLinker( ) ) then
				path = fbc.binpath + "ld.lld" + FB_HOST_EXEEXT
			else
				path = fbc.binpath + fbctoolTB(tool).name + FB_HOST_EXEEXT
			end if
		#else
			'' standalone build, we need to use insert it here
			if( (tool = FBCTOOL_LD) and fbcUseLldLinker( ) ) then
				path = fbc.binpath + fbc.buildprefix + "ld.lld" + FB_HOST_EXEEXT
			else
				path = fbc.binpath + fbc.buildprefix + fbctoolTB(tool).name + FB_HOST_EXEEXT
			end if
		#endif

		#ifndef ENABLE_STANDALONE
			if( hFileExists( path ) = FALSE ) then
				select case fbGetOption( FB_COMPOPT_BACKEND )
				case FB_BACKEND_GCC, FB_BACKEND_CLANG
					'' c) Ask GCC where it is, if applicable (GCC might have its
					'' own copy which we must use instead of the system one)
					if( tool = FBCTOOL_AS ) then
						path = fbcQueryCC( " -print-prog-name=as" )
					elseif( tool = FBCTOOL_LD ) then
						path = fbcQueryCC( " -print-prog-name=ld" )
					end if
				case FB_BACKEND_GAS, FB_BACKEND_GAS64
					#if defined( __FB_FREEBSD__ )
						'' gas backend? and we are looking for the linker? and we're hosted freebsd?
						'' switch to ld.bfd instead...
						if( tool = FBCTOOL_LD ) then
							fbctoolTB(tool).name = "ld.bfd"
							path = fbc.binpath + fbctoolTB(tool).name + FB_HOST_EXEEXT
						end if
					#endif
				end select
			end if

			if( hFileExists( path ) = FALSE ) then
				'' d) Rely on PATH
				if( fbGetOption( FB_COMPOPT_TARGET ) <> FB_COMPTARGET_JS ) then
					if( len(fbc.buildprefix) > 0 ) then
						path = fbc.buildprefix + fbctoolTB(tool).name + FB_HOST_EXEEXT
					else
						path = fbc.targetprefix + fbctoolTB(tool).name + FB_HOST_EXEEXT
					end if
				else
					path = fbctoolTB(tool).name
				end if
				fbctoolSetFlags( tool, FBCTOOLFLAG_RELYING_ON_SYSTEM )
			end if
		#endif
	end if

	fbctoolTB( tool ).path = path
	fbctoolSetFlags( tool, FBCTOOLFLAG_FOUND )
end sub

#if defined( __FB_CYGWIN__ )
extern "c"
declare function cygwin_conv_path( byval what as uinteger, byval from_path as const any ptr, byval to_path as any ptr, byval bytes as uinteger ) as integer
end extern

const CCP_POSIX_TO_WIN_A = 0

private function hCygwinExecPath( byref path as string ) as string
	dim as string result

	result = path
	hReplaceSlash( strptr( result ), asc( "/" ) )

	if( left( result, 1 ) = "/" ) then
		dim as integer bytes = cygwin_conv_path( CCP_POSIX_TO_WIN_A, strptr( result ), NULL, 0 )

		if( (bytes > 0) and (bytes < 32768) ) then
			dim as string converted = space( bytes )

			if( cygwin_conv_path( CCP_POSIX_TO_WIN_A, strptr( result ), strptr( converted ), bytes ) = 0 ) then
				dim as integer nulpos = instr( converted, chr( 0 ) )
				if( nulpos > 0 ) then
					converted = left( converted, nulpos - 1 )
				end if
				hReplaceSlash( strptr( converted ), asc( "/" ) )
				return converted
			end if
		end if
	end if

	function = result
end function
#endif

private function fbcRunBin _
	( _
		byval action as zstring ptr, _
		byval tool as integer, _
		byref ln as string _
	) as integer

	dim as integer result = any
	dim as string path

	fbcFindBin( tool, path )
#if defined( __FB_CYGWIN__ )
	path = hCygwinExecPath( path )
#endif

	if( fbc.verbose ) then
		print *action + ": ", path + " " + ln
	end if

	'' Always use exec() on Unix or for standalone because
	'' - Unix exec() already searches the PATH, so shell() isn't needed,
	'' - standalone doesn't use system-wide tools
	#if defined( __FB_UNIX__ ) or defined( ENABLE_STANDALONE )
		result = exec( path, ln )
	#else
		'' Found at bin/?
		if( fbctoolGetFlags( tool, FBCTOOLFLAG_RELYING_ON_SYSTEM ) = FALSE ) then
			result = exec( path, ln )
		else
			result = shell( path + " " + ln )
		end if
	#endif

	if( result = 0 ) then
		function = TRUE
	elseif( result < 0 ) then
		errReportEx( FB_ERRMSG_EXEMISSING, path, -1, FB_ERRMSGOPT_ADDCOLON or FB_ERRMSGOPT_ADDQUOTES )
	else
		'' Report bad exit codes only in verbose mode; normally the
		'' program should already have shown an error message, and the
		'' exit code is only interesting for debugging purposes.
		if( fbc.verbose ) then
			print *action + " failed: '" + path + "' terminated with exit code " + str( result )
		end if
	end if
end function

#if defined( __FB_WIN32__ ) or defined( __FB_DOS__ )
private function hPutLdArgsIntoFile( byref ldcline as string ) as integer
	dim as string argsfile, ln
	dim as integer f = any

	argsfile = hStripFilename( fbc.outname ) + "ldopt.tmp"

	f = freefile( )
	if( open( argsfile, for output, as #f ) ) then
		exit function
	end if

	''
	'' MinGW ld (including the MinGW-to-DJGPP cross-compiling ld) treats \
	'' backslashes in @files (response files) as escape sequence, so \ must
	'' be escaped as \\. ld seems to behave pretty much like Unixish shells
	'' would: all \'s indicate an escape sequence. (For reference,
	'' binutils/libiberty source code: expandargv(), buildargv())
	''
	'' With DJGPP ld however, \ chars do not seem to indicate escape
	'' sequences, despite the DJGPP FAQ (http://www.delorie.com/djgpp/v2faq/faq16_3.html)
	'' which says that \ is special in some cases such as \" or \\.
	'' Thus we mustn't (and don't need to) use \\ when using DJGPP ld.
	''
	ln = ldcline
	#ifdef __FB_WIN32__
		ln = hReplace( ln, $"\", $"\\" )
	#endif

	print #f, ln

	close #f

	'' Clean up the @file if -R wasn't given
	if( fbc.keepasm = FALSE ) then
		fbcAddTemp( argsfile )
	end if

	if( fbc.verbose ) then
		print "ld options in '" & argsfile & "': ", ldcline
	end if

	ldcline = "@" + argsfile
	function = TRUE
end function
#endif

private function clearDefList(byref deffile as string) as integer
	dim as integer fi = freefile()
	if (open(deffile, for input, as #fi)) then
		return FALSE
	end if

	dim as string cleaned = hStripExt(deffile) + ".clean.def"
	dim as integer fo = freefile()
	if (open(cleaned, for output, as #fo)) then
		close #fi
		return FALSE
	end if

	dim as string ln
	while (eof(fi) = FALSE)
		line input #fi, ln

		if (right(ln, 4) = "DATA") then
			ln = left(ln, len(ln) - 4)
		end if

		print #fo, ln
	wend

	close #fo
	close #fi

	kill(deffile)
	return (name(cleaned, deffile) = 0)
end function

private function hGenerateEmptyDefFile( byref deffile as string ) as integer
	var f = freefile( )
	if( open( deffile, for output, as #f ) ) then
		exit function
	end if

	print #f, "EXPORTS"

	close #f
	function = TRUE
end function

private function makeImpLib _
	( _
		byref dllname as string, _
		byref deffile as string _
	) as integer

	'' for some weird reason, LD will declare all functions exported as if they were
	'' from DATA segment, causing an exception (UPPERCASE'd symbols assumption??)
	if( clearDefList( deffile ) = FALSE ) then
		exit function
	end if

	'' If the .def file is empty (happens if there were no EXPORTs),
	'' then add a single "EXPORTS" line, otherwise dlltool will complain
	'' about a syntax error. (ld --output-def should probably do this
	'' automatically, or dlltool should be fixed, but oh well)
	if( filelen( deffile ) = 0 ) then
		if( hGenerateEmptyDefFile( deffile ) = FALSE ) then
			exit function
		end if
	end if

	dim as string ln
	ln += "--def """ + deffile + """"
	ln += " --dllname """ + hStripPath( fbc.outname ) + """"
	ln += " --output-lib """ + hStripFilename( fbc.outname ) + "lib" + dllname + ".dll.a"""

	if( fbcRunBin( "creating import library", FBCTOOL_DLLTOOL, ln ) = FALSE ) then
		exit function
	end if

	'' Clean up the .def file if -R wasn't given
	if( fbc.keepasm = FALSE ) then
		fbcAddTemp( deffile )
	end if

	function = TRUE
end function

'' Find a library file and wrap it into ""'s for passing it on the ld command
'' line. Or if it couldn't be found, show an error.
private function hFindLib( byval file as zstring ptr ) as string
	dim as string found = fbcBuildPathToLibFile( file )
	if( len( found ) > 0 ) then
		function = " """ + found + """"
	else
		errReportEx( FB_ERRMSG_FILENOTFOUND, file, -1 )
	end if
end function

#include once "fbc-platform.bi"

private function fbcLinkerIsGold( ) as integer
	'' This is needed otherwise it will wrongly pass --version into the linker on Solaris
	'' caused the linker version to be printed everytime we compile with fbc
	if( (fbGetOption( FB_COMPOPT_TARGET ) = FB_COMPTARGET_SOLARIS) or _
	    (fbGetOption( FB_COMPOPT_TARGET ) = FB_COMPTARGET_ILLUMOS) ) then
		return FALSE
	else
		dim ldcmd as string
		fbcFindBin( FBCTOOL_LD, ldcmd )
		ldcmd += " --version"
		return (instr( hGet1stOutputLineFromCommand( ldcmd ), "GNU gold" ) > 0)
	end if
end function

'' Check whether we're using the gold linker.
private function fbcIsUsingGoldLinker( ) as integer
	'' gold only supports ELF, we only need to check for it when targetting ELF.
	if( fbTargetSupportsELF( ) ) then
		return fbcLinkerIsGold( )
	end if
	return FALSE
end function

private function hArmLinuxUsesHardFloatAbi( ) as integer
#ifndef ENABLE_STANDALONE
	'' GNU ARM triplets encode the floating-point ABI in the OS suffix:
	'' gnueabihf is hard-float, while plain gnueabi is soft-float.
	if( instr( lcase( fbc.target ), "gnueabihf" ) > 0 ) then
		return TRUE
	elseif( instr( lcase( fbc.target ), "gnueabi" ) > 0 ) then
		return FALSE
	end if
#endif

	select case as const FB_DEFAULT_CPUTYPE_ARM
	case FB_CPUTYPE_ARMV6_FP, FB_CPUTYPE_ARMV7A_FP
		return TRUE
	case else
		return FALSE
	end select
end function

private function hLinkFiles( ) as integer
	dim as string ldcline, dllname, deffile
	dim as string xbox_xbe_outname
	dim as string wii_dol_outname

	function = FALSE

	hSetOutName( )

	if( fbGetOption( FB_COMPOPT_TARGET ) = FB_COMPTARGET_XBOX ) then
		if( lcase( right( fbc.outname, 4 ) ) = ".xbe" ) then
			xbox_xbe_outname = fbc.outname
			fbc.outname = hStripExt( fbc.outname ) + ".exe"
		end if
	end if

	if( fbGetOption( FB_COMPOPT_TARGET ) = FB_COMPTARGET_WII ) then
		if( lcase( right( fbc.outname, 4 ) ) = ".dol" ) then
			wii_dol_outname = fbc.outname
			fbc.outname = hStripExt( fbc.outname ) + ".elf"
		end if
	end if

	select case( fbGetOption( FB_COMPOPT_TARGET ) )
	case FB_COMPTARGET_WIN32
		select case( fbGetCpuFamily( ) )
		case FB_CPUFAMILY_X86
			ldcline += "-m i386pe "
		case FB_CPUFAMILY_X86_64
			ldcline += "-m i386pep "
		case FB_CPUFAMILY_AARCH64
			ldcline += "-m arm64pe "
		end select
	case FB_COMPTARGET_LINUX
		select case( fbGetCpuFamily( ) )
		case FB_CPUFAMILY_X86
			ldcline += "-m elf_i386 "
		case FB_CPUFAMILY_X86_64
			ldcline += "-m elf_x86_64 "
		case FB_CPUFAMILY_ARM
			ldcline += "-m armelf_linux_eabi "
		end select
	case FB_COMPTARGET_SOLARIS
		select case( fbGetCpuFamily( ) )
		case FB_CPUFAMILY_X86_64
			''
			'' Solaris ld does not choose the output ELF class from the
			'' startup objects or library search paths.  Without -64, an
			'' x86_64 link still runs in the linker's default 32-bit mode
			'' and rejects the 64-bit runtime archives as incompatible.
			''
			ldcline += "-64 "
		end select
	case FB_COMPTARGET_ILLUMOS
		select case( fbGetCpuFamily( ) )
		case FB_CPUFAMILY_X86_64
			''
			'' illumos builds commonly use GNU ld.  Its Solaris/x86_64
			'' emulation must be selected explicitly; otherwise ld keeps
			'' the default 32-bit Solaris mode and rejects 64-bit archives.
			''
			ldcline += "-m elf_x86_64_sol2 "
		end select
	case FB_COMPTARGET_HAIKU
		select case( fbGetCpuFamily( ) )
		case FB_CPUFAMILY_X86
			ldcline += "-m elf_i386_haiku "
		case FB_CPUFAMILY_X86_64
			ldcline += "-m elf_x86_64_haiku "
		case FB_CPUFAMILY_ARM
			ldcline += "-m armelf_linux_eabi "
		end select
	case FB_COMPTARGET_ANDROID
		if( (len( fbc.sysroot ) = 0) and _
		    (fbGetOption( FB_COMPOPT_BACKEND ) = FB_BACKEND_GCC) ) then
			'' Newer NDKs use clang which doesn't support -print-sysroot,
			'' and we don't need it.
			'' GCC in older NDKs may need the sysroot before calling hFindLib().
			'' At least in older NDKs, if a standalone NDK toolchain (for a particular
			'' platform+arch) is used then gcc --print-sysroot returns the correct
			'' sysroot. Otherwise, running directly from a full NDK install, it prints a
			'' /tmp/... directory, and the user needs to manually pass the correct sysroot.
			'' The link-time sysroot should be $NDK/platforms/android-$API/arch-$ARCH
			'' (Since NDK r15, the sysroot for the C headers is $NDK/sysroot, but we
			'' don't need them.)
			fbc.sysroot = fbcFindSysroot( )
			if( left( fbc.sysroot, 5 ) = "/tmp/" ) then
				errReportWarnEx( FB_WARNINGMSG_MISSINGANDROIDSYSROOT, , 0 )
			end if
		end if

		'' The compiler runtime support library, usually called libgcc on other platforms
		'' (even when using clang) or when using gcc in older NDK, varies by target, something
		'' like libclang_rt.builtins-aarch64-android.a
		var args = ""
#ifndef ENABLE_STANDALONE
		if( len( fbc.target ) > 0 ) then
			'' Without the correct -target, clang just returns the host libgcc.
			args = " -target " & fbc.target
		end if
#endif
		args &= " -print-libgcc-file-name"
		var path = fbcQueryCC( args )
		ldcline &= path & " "

		'' androideabi-v7a ABI requires extra linker options; apparently
		'' others don't. See https://developer.android.com/ndk/guides/standalone_toolchain.html
		if( fbGetOption( FB_COMPOPT_CPUTYPE ) = FB_CPUTYPE_ARMV7A ) then
			ldcline += "--fix-cortex-a8 "
		end if
		'' Optional?
		' select case( fbGetCpuFamily( ) )
		' case FB_CPUFAMILY_X86
		'   ldcline += "-m elf_i386 "
		' end select
	case FB_COMPTARGET_DARWIN
		'' Let the Darwin compiler driver choose the native architecture and
		'' startup/runtime defaults instead of pushing raw ld -arch flags.
	case FB_COMPTARGET_WII
		'' Let the devkitPPC GCC driver choose newlib/libgcc startup support.
	case FB_COMPTARGET_NETBSD
		ldcline += " -rpath /usr/X11R7/lib/ "
		ldcline += " -rpath /usr/pkg/lib/ "
	end select

	'' Set executable name
	if( fbGetOption( FB_COMPOPT_TARGET ) = FB_COMPTARGET_XBOX ) then
		ldcline += "-out:" + QUOTE + fbc.outname + QUOTE
	else
		ldcline += "-o " + QUOTE + fbc.outname + QUOTE
	end if

	'' dll dos targets need to run DXE3GEN
	if (fbGetOption( FB_COMPOPT_TARGET ) = FB_COMPTARGET_DOS) and _
	   (fbGetOption( FB_COMPOPT_OUTTYPE ) = FB_OUTTYPE_DYNAMICLIB) then
		'' Building an import library is usable but need to watch
		'' the initialization order.  libfb exports need to be
		'' initialized before trying to load the DXE.  FYI, if this
		'' is handled at load time by the loader code (global
		'' constructor) user may have issues with the ctor order.
		'' Which means that user is expected to have called
		'' DyLibLoad("") in a global ctor and no other global ctors
		'' that depend on libfb functions are defined
		''
		ldcline += " -I ""lib" + hStripExt( fbc.outname ) + "_il.a"""
		''
		'' the -U switch allows the DXE to be created with unresolved
		'' symbols.  FYI, we won't know which symbols are needed until
		'' the loader tries to load the DXE.  This seems to be the only
		'' kind we can make (i.e. not possible to build a fully resolved DXE
		'' because dxe3gen seems to only resolve with in the current DXE and
		'' not any statically linked library)
		''
		ldcline += " -U"
		''
		scope
			dim as string ptr objfile = listGetHead( @fbc.objlist )
			while( objfile )
				ldcline += " """ + *objfile + """"
				objfile = listGetNext( objfile )
			wend
		end scope
		scope
			dim as string ptr libfile = listGetHead(@fbc.libfiles)
			if (libfile) then
				ldcline +=  " -lc"
			end if
			while (libfile)
				ldcline += " """ + *libfile + """"
				libfile = listGetNext(libfile)
			wend
		end scope
		#ifdef __FB_DOS__
			'' windows (maybe others) version of DXE3GEN doesn't seem to be able
			'' to handle @ldopt.tmp argument when cross compiling - only write to
			'' lpopt.tmp if we are hosted on DOS
			if( hPutLdArgsIntoFile( ldcline ) = FALSE ) then
				exit function
			end if
		#endif
		#ifdef ENABLE_STANDALONE
			dim as string dxepath = environ( "DXE_LD_LIBRARY_PATH" )
			if( dxepath = "" ) then
				setenviron "DXE_LD_LIBRARY_PATH=" + fbc.libpath + FB_HOST_PATHDIV
				if( fbc.verbose ) then
					print "DXE_LD_LIBRARY_PATH=" + fbc.libpath + FB_HOST_PATHDIV
				end if
			end if
		#endif
		function = fbcRunBin( "making DXE", FBCTOOL_DXEGEN, ldcline )
		exit function
	end if

	select case as const fbGetOption( FB_COMPOPT_TARGET )
	case FB_COMPTARGET_CYGWIN
		if( fbGetOption( FB_COMPOPT_OUTTYPE ) = FB_OUTTYPE_DYNAMICLIB ) then
			dllname = hStripPath( hStripExt( fbc.outname ) )
			ldcline += " --shared -e _cygwin_dll_entry --enable-auto-image-base --dll-search-prefix=cyg"
		else
			'' set default subsystem mode
			if( len( fbc.subsystem ) = 0 ) then
				if( fbGetOption( FB_COMPOPT_MODEVIEW ) = FB_MODEVIEW_GUI ) then
					fbc.subsystem = "windows"
				else
					fbc.subsystem = "console"
				end if
			else
				if( fbc.subsystem = "gui" ) then
					fbc.subsystem = "windows"
				end if
			end if

			ldcline += " -subsystem " + fbc.subsystem
		end if

	case FB_COMPTARGET_WIN32

		'' set default subsystem mode
		if( len( fbc.subsystem ) = 0 ) then
			if( fbGetOption( FB_COMPOPT_MODEVIEW ) = FB_MODEVIEW_GUI ) then
				fbc.subsystem = "windows"
			else
				fbc.subsystem = "console"
			end if
		else
			if( fbc.subsystem = "gui" ) then
				fbc.subsystem = "windows"
			end if
		end if

		ldcline += " -subsystem " + fbc.subsystem

		if( fbGetOption( FB_COMPOPT_OUTTYPE ) = FB_OUTTYPE_DYNAMICLIB ) then
			''
			dllname = hStripPath( hStripExt( fbc.outname ) )

			'' create a dll
			ldcline += " --dll --enable-stdcall-fixup"

			'' Specify the DLL entry-point, like gcc:
			'' with underscore and @N stdcall suffix on x86,
			'' but without that for x86_64.
			if( fbGetCpuFamily( ) = FB_CPUFAMILY_X86 ) then
				ldcline += " -e _DllMainCRTStartup@12"
			else
				ldcline += " -e DllMainCRTStartup"
			end if
		end if

	case FB_COMPTARGET_DARWIN
		if( fbcDarwinPlatformAddDynamicLibOptions( ldcline, dllname ) = FALSE ) then
			exit function
		end if

	case FB_COMPTARGET_LINUX, FB_COMPTARGET_HAIKU, _
	     FB_COMPTARGET_FREEBSD, FB_COMPTARGET_OPENBSD, _
	     FB_COMPTARGET_NETBSD, FB_COMPTARGET_DRAGONFLY, _
	     FB_COMPTARGET_SOLARIS, FB_COMPTARGET_ILLUMOS, _
	     FB_COMPTARGET_ANDROID

		if( fbGetOption( FB_COMPOPT_OUTTYPE ) = FB_OUTTYPE_DYNAMICLIB ) then
			dllname = hStripPath( hStripExt( fbc.outname ) )
			ldcline += " -shared -h" + hStripPath( fbc.outname )

			'' Turn libfoo into foo, so it can be checked against -l foo below
			if( left( dllname, 3 ) = "lib" ) then
				dllname = right( dllname, len( dllname ) - 3 )
			end if
		else
			select case as const fbGetOption( FB_COMPOPT_TARGET )
			case FB_COMPTARGET_FREEBSD
				ldcline += " -dynamic-linker /libexec/ld-elf.so.1"
			case FB_COMPTARGET_DRAGONFLY
				ldcline += " -dynamic-linker /libexec/ld-elf.so.2"
			case FB_COMPTARGET_SOLARIS
				if( fbGetCpuFamily( ) = FB_CPUFAMILY_X86_64 ) then
					ldcline += " -I /lib/64/ld.so.1"
				else
					ldcline += " -I /lib/ld.so.1"
				end if
			case FB_COMPTARGET_ILLUMOS
				if( fbGetCpuFamily( ) = FB_CPUFAMILY_X86_64 ) then
					ldcline += " -I /lib/64/ld.so.1"
				else
					ldcline += " -I /lib/ld.so.1"
				end if
			case FB_COMPTARGET_LINUX
				select case( fbGetCpuFamily( ) )
				case FB_CPUFAMILY_X86
					#ifdef ENABLE_MUSL_DYNAMIC_LINKER
						ldcline += " -dynamic-linker /lib/ld-musl-i386.so.1"
					#else
					ldcline += " -dynamic-linker /lib/ld-linux.so.2"
					#endif
				case FB_CPUFAMILY_X86_64
					#ifdef ENABLE_MUSL_DYNAMIC_LINKER
						ldcline += " -dynamic-linker /lib/ld-musl-x86_64.so.1"
					#else
					ldcline += " -dynamic-linker /lib64/ld-linux-x86-64.so.2"
					#endif
				case FB_CPUFAMILY_ARM
					#ifdef ENABLE_MUSL_DYNAMIC_LINKER
						if( hArmLinuxUsesHardFloatAbi( ) ) then
							ldcline += " -dynamic-linker /lib/ld-musl-armhf.so.1"
						else
							ldcline += " -dynamic-linker /lib/ld-musl-arm.so.1"
						end if
					#else
						if( hArmLinuxUsesHardFloatAbi( ) ) then
							ldcline += " -dynamic-linker /lib/ld-linux-armhf.so.3"
						else
							ldcline += " -dynamic-linker /lib/ld-linux.so.3"
						end if
					#endif
				case FB_CPUFAMILY_AARCH64
					#ifdef ENABLE_MUSL_DYNAMIC_LINKER
						ldcline += " -dynamic-linker /lib/ld-musl-aarch64.so.1"
					#else
					ldcline += " -dynamic-linker /lib/ld-linux-aarch64.so.1"
					#endif
				case FB_CPUFAMILY_PPC
					#ifdef ENABLE_MUSL_DYNAMIC_LINKER
						ldcline += " -dynamic-linker /lib/ld-musl-powerpc.so.1"
					#else
						ldcline += " -dynamic-linker /lib/ld.so.1"
					#endif
				case FB_CPUFAMILY_PPC64
					#ifdef ENABLE_MUSL_DYNAMIC_LINKER
						ldcline += " -dynamic-linker /lib/ld-musl-powerpc64.so.1"
					#else
						ldcline += " -dynamic-linker /lib64/ld64.so.1"
					#endif
				case FB_CPUFAMILY_PPC64LE
					#ifdef ENABLE_MUSL_DYNAMIC_LINKER
						ldcline += " -dynamic-linker /lib/ld-musl-powerpc64le.so.1"
					#else
						ldcline += " -dynamic-linker /lib64/ld64.so.2"
					#endif
				case FB_CPUFAMILY_RISCV32
					#ifdef ENABLE_MUSL_DYNAMIC_LINKER
						ldcline += " -dynamic-linker /lib/ld-musl-riscv32.so.1"
					#else
						ldcline += " -dynamic-linker /lib/ld-linux-riscv32-ilp32.so.1"
					#endif
				case FB_CPUFAMILY_RISCV64
					#ifdef ENABLE_MUSL_DYNAMIC_LINKER
						ldcline += " -dynamic-linker /lib/ld-musl-riscv64.so.1"
					#else
						ldcline += " -dynamic-linker /lib/ld-linux-riscv64-lp64d.so.1"
					#endif
				case FB_CPUFAMILY_S390X
					#ifdef ENABLE_MUSL_DYNAMIC_LINKER
						ldcline += " -dynamic-linker /lib/ld-musl-s390x.so.1"
					#else
						ldcline += " -dynamic-linker /lib/ld64.so.1"
					#endif
				case FB_CPUFAMILY_LOONGARCH64
					#ifdef ENABLE_MUSL_DYNAMIC_LINKER
						ldcline += " -dynamic-linker /lib/ld-musl-loongarch64.so.1"
					#else
						ldcline += " -dynamic-linker /lib64/ld-linux-loongarch-lp64d.so.1"
					#endif
				end select
			case FB_COMPTARGET_HAIKU
				'' Haiku executables are linked through the shared-object path.
				'' Bind definitions within the executable so RIP-relative inline
				'' assembly references to local FB symbols remain linkable.
				ldcline += " -shared -no-undefined -Bsymbolic"
			case FB_COMPTARGET_NETBSD
				ldcline += " -dynamic-linker /usr/libexec/ld.elf_so"
			case FB_COMPTARGET_OPENBSD
				ldcline += " -dynamic-linker /usr/libexec/ld.so"
			case FB_COMPTARGET_ANDROID
				ldcline += " -dynamic-linker /system/bin/linker"
			end select
		end if

		'' Add all symbols to the dynamic symbol table
		'' The Solaris linker doesn't support --export-dynamic
		'' I don't know the equivalent option for it so I just disable this for Solaris
		'' Don't know the side effects, though
		'' But able to have shared library generated successfully afterward
		if( (fbGetOption( FB_COMPOPT_OUTTYPE ) = FB_OUTTYPE_DYNAMICLIB) or _
			fbGetOption( FB_COMPOPT_EXPORT ) ) and _
			(fbGetOption( FB_COMPOPT_TARGET ) <> FB_COMPTARGET_SOLARIS) and _
			(fbGetOption( FB_COMPOPT_TARGET ) <> FB_COMPTARGET_ILLUMOS) and _
			(fbGetOption( FB_COMPOPT_TARGET ) <> FB_COMPTARGET_DARWIN) then
			ldcline += " --export-dynamic"
		end if
		fbcDarwinPlatformAddExportDynamic( ldcline )

	case FB_COMPTARGET_XBOX
		'' nxdk-link is lld's MSVC-compatible linker frontend.  The common
		'' GNU ld flags used by the old OpenXDK path are not accepted here.
		'' automount_d only contains startup registration, so force its symbol
		'' in exactly as nxdk's own makefiles do.
		ldcline += " -safeseh:no"
		ldcline += " -include:_automount_d_drive"

	case FB_COMPTARGET_JS
		dim as integer js_link_optimize = fbGetOption( FB_COMPOPT_OPTIMIZELEVEL )
		if( js_link_optimize < 2 ) then
			''
			'' Asyncify keeps SLEEP and old input loops cooperative in the
			'' browser, but it also rewrites reachable control flow.  At -O0
			'' large QB-era programs can exceed browser wasm function limits
			'' after that rewrite.  Keep the final Emscripten link at O2 or
			'' better even when the BASIC compiler itself is not optimizing.
			''
			js_link_optimize = 2
		end if
		ldcline += " -O" + str( js_link_optimize )

		static as zstring*32 emscripten_options(...) = _
		{ _
			"CASE_INSENSITIVE_FS=1", _
			"TOTAL_MEMORY=67108864", _
			"ALLOW_MEMORY_GROWTH=1", _
			"RETAIN_COMPILER_SETTINGS=1", _
			"ASYNCIFY=1", _
			"ASYNCIFY_STACK_SIZE=65536" _
		}
			'"WARN_UNALIGNED=1", _

		ldcline += " -Wno-warn-absolute-paths"
		for i as integer = 0 to ubound(emscripten_options)
			ldcline += " -s " + emscripten_options(i)
		next

		ldcline += " --shell-file" + hFindLib("fb_shell.html")
		ldcline += " --post-js" + hFindLib("fb_rtlib.js")
		if( ((len(fbc.subsystem) = 0) and (fbGetOption( FB_COMPOPT_MODEVIEW ) = FB_MODEVIEW_CONSOLE)) or _
		    (fbc.subsystem = "console") ) then
			ldcline += " --post-js" + hFindLib("termlib_min.js")
		end if

	end select

	if (fbGetOption( FB_COMPOPT_TARGET ) = FB_COMPTARGET_DOS) then
		'' For DJGPP, the custom ldscript must always be used,
		'' to get ctors/dtors into the correct order that lets
		'' fbrt0's c/dtor be the first/last respectively.
		'' (needed until binutils' default DJGPP ldscripts are fixed)
		ldcline += " -T """ + fbc.libpath + (FB_HOST_PATHDIV + "i386go32.x""")
	else
		'' Supplementary ld script to drop the fbctinf objinfo section
		''  - only if objinfo is enabled
		''  - only with ld.bfd, not ld.gold, because gold doesn't support this kind
		''    of linker script (results in broken binaries).

		'' Solaris uses it own linker
		''  - don't know if it support this kind of linker script or not
		''  - so just disable it for the solaris target, too
		''  - the solaris linker will cause major problems
		''  - because of imcompatibilities with the GNU linker
		''  - we can only know if when really testing on the platform
		''  - and adapt the code over time
		if( fbGetOption( FB_COMPOPT_OBJINFO ) and _
			(fbGetOption( FB_COMPOPT_TARGET ) <> FB_COMPTARGET_DARWIN) and _
			(fbGetOption( FB_COMPOPT_TARGET ) <> FB_COMPTARGET_SOLARIS) and _
			(fbGetOption( FB_COMPOPT_TARGET ) <> FB_COMPTARGET_ILLUMOS) and _
			( fbGetOption( FB_COMPOPT_TARGET ) <> FB_COMPTARGET_JS ) and _
			( fbGetOption( FB_COMPOPT_TARGET ) <> FB_COMPTARGET_XBOX ) and _
			( fbGetOption( FB_COMPOPT_TARGET ) <> FB_COMPTARGET_WII ) and _
			( not fbcUseLldLinker( ) ) and _
			(not fbcIsUsingGoldLinker( )) ) then
			ldcline += " -T """ + fbc.libpath + (FB_HOST_PATHDIV + "fbextra.x""")
		end if
	end if

	select case as const fbGetOption( FB_COMPOPT_TARGET )
	case FB_COMPTARGET_CYGWIN, FB_COMPTARGET_WIN32
		'' stack size
		dim as integer stacksize = fbGetOption(FB_COMPOPT_STACKSIZE)
		ldcline += " --stack " + str(stacksize) + "," + str(stacksize)

		if( fbGetOption( FB_COMPOPT_OUTTYPE ) = FB_OUTTYPE_DYNAMICLIB ) then
			'' When building DLLs, we also create an import library, as
			'' convenience for the user. There are several ways to do this:
			''    a) ld --output-def + dlltool
			''    b) ld --output-def --out-implib
			''    c) pexports + dlltool
			'' At the moment it seems like "ld --out-implib" alone
			'' does not work, and b) shows a verbose message that
			'' wouldn't be nice to have, so a) is the best way.
			deffile = hStripExt( fbc.outname ) + ".def"
			ldcline += " --output-def """ + deffile + """"
		end if

	case FB_COMPTARGET_XBOX
		'' set entry point
		ldcline += " -entry:WinMainCRTStartup"
		'' nxdk-link supplies a small 64 KiB default.  Pass fbc's stack
		'' setting after the wrapper defaults so fbcunit and programs with
		'' large local buffers use the same default reserve as Win32.
		ldcline += " -stack:" + str( fbGetOption( FB_COMPOPT_STACKSIZE ) )
	case FB_COMPTARGET_OPENBSD
		'' OpenBSD executables are PIE and enter through __start.
		if( fbGetOption( FB_COMPOPT_OUTTYPE ) = FB_OUTTYPE_EXECUTABLE ) then
			ldcline += " -pie -e __start"
		end if

	end select

	if (fbc.staticlink) and (fbGetOption( FB_COMPOPT_TARGET ) <> FB_COMPTARGET_XBOX) then
		ldcline += " -Bstatic"
	end if

	if( fbGetOption( FB_COMPOPT_PIC ) and (fbGetOption( FB_COMPOPT_TARGET ) <> FB_COMPTARGET_XBOX) ) then
		if( fbGetOption( FB_COMPOPT_OUTTYPE ) = FB_OUTTYPE_EXECUTABLE ) then
			'' NOTE: For android 4.0-, user will need to pass in '-Wl -no-pie'
			''       to override this default.
			''       On android 4.1+ this is accepted, and on android 5.0+
			''       is required. For android 4.0- this will probably link
			''       but then segfault when executed on the device.

			ldcline += " -pie"
		else
			'' Dynamic library: no flag needed
		end if
	end if

	if( len( fbc.mapfile ) > 0) then
		if( fbGetOption( FB_COMPOPT_TARGET ) = FB_COMPTARGET_XBOX ) then
			ldcline += " -map:" + fbc.mapfile
		elseif( fbGetOption( FB_COMPOPT_TARGET ) = FB_COMPTARGET_WII ) then
			ldcline += " -Wl,-Map," + fbc.mapfile
		else
			ldcline += " -Map " + fbc.mapfile
		end if
	end if

	if( fbGetOption( FB_COMPOPT_DEBUGINFO ) = FALSE ) then
		if( fbGetOption( FB_COMPOPT_PROFILE ) <> FB_PROFILE_OPT_GMON ) then
			if(( fbGetOption( FB_COMPOPT_TARGET ) <> FB_COMPTARGET_DARWIN ) and _
				( fbGetOption( FB_COMPOPT_TARGET ) <> FB_COMPTARGET_JS )) then

				if( fbc.stripsymbols and (fbGetOption( FB_COMPOPT_TARGET ) <> FB_COMPTARGET_XBOX) ) then
					ldcline += " -s"
				end if
			end if
		end if
	end if

	'' Add the library search paths
	scope
		dim as TSTRSETITEM ptr i = listGetHead(@fbc.finallibpaths.list)

		dim as string L
		if( fbGetOption( FB_COMPOPT_TARGET ) <> FB_COMPTARGET_JS ) then
			L = " -L """
		else
			L = " -L"""
		end if

		if( fbGetOption( FB_COMPOPT_TARGET ) <> FB_COMPTARGET_XBOX ) then
			while (i)
				ldcline += L + i->s + """"
				i = listGetNext(i)
			wend
		end if
	end scope

	'' And the sysroot
	if( len( fbc.sysroot ) ) then
		ldcline += " --sysroot=" + fbc.sysroot
	end if

	'' crt begin objects
	select case as const fbGetOption( FB_COMPOPT_TARGET )
	case FB_COMPTARGET_CYGWIN
		if( fbGetOption( FB_COMPOPT_OUTTYPE ) = FB_OUTTYPE_DYNAMICLIB ) then
			ldcline += hFindLib( "crtbeginS.o" )
		else
			'' TODO
			ldcline += hFindLib( "crt0.o" )
			'' additional support for gmon
			if( fbGetOption( FB_COMPOPT_PROFILE ) = FB_PROFILE_OPT_GMON ) then
				ldcline += hFindLib( "gcrt0.o" )
			end if
		end if

	case FB_COMPTARGET_WIN32
		if( fbGetOption( FB_COMPOPT_OUTTYPE ) = FB_OUTTYPE_DYNAMICLIB ) then
			ldcline += hFindLib( "dllcrt2.o" )
		else
			ldcline += hFindLib( "crt2.o" )
			'' additional support for gmon
			if( fbGetOption( FB_COMPOPT_PROFILE ) = FB_PROFILE_OPT_GMON  ) then
				ldcline += hFindLib( "gcrt2.o" )
			end if
		end if

		ldcline += hFindLib( "crtbegin.o" )

	case FB_COMPTARGET_DOS
		if( fbGetOption( FB_COMPOPT_PROFILE ) = FB_PROFILE_OPT_GMON  ) then
			ldcline += hFindLib( "gcrt0.o" )
		else
			ldcline += hFindLib( "crt0.o" )
		end if

	case FB_COMPTARGET_LINUX, FB_COMPTARGET_HAIKU, FB_COMPTARGET_DARWIN, _
		FB_COMPTARGET_FREEBSD, FB_COMPTARGET_OPENBSD, _
		FB_COMPTARGET_NETBSD, FB_COMPTARGET_DRAGONFLY, _
		FB_COMPTARGET_SOLARIS, FB_COMPTARGET_ILLUMOS

		if( fbGetOption( FB_COMPOPT_OUTTYPE ) = FB_OUTTYPE_EXECUTABLE) then
				if( fbGetOption( FB_COMPOPT_PROFILE ) ) then
					select case as const fbGetOption( FB_COMPOPT_TARGET )
					case FB_COMPTARGET_OPENBSD, FB_COMPTARGET_NETBSD
						ldcline += hFindLib( "gcrt0.o" )
					case FB_COMPTARGET_DARWIN
						'' Darwin's compiler driver supplies the startup objects.
					case FB_COMPTARGET_HAIKU
						'' no gcrt1.o on Haiku
					case else
					ldcline += hFindLib( "gcrt1.o" )
				end select
				else
					select case as const fbGetOption( FB_COMPOPT_TARGET )
					case FB_COMPTARGET_OPENBSD, FB_COMPTARGET_NETBSD
						ldcline += hFindLib( "crt0.o" )
					case FB_COMPTARGET_DARWIN
						'' Darwin's compiler driver supplies the startup objects.
					case FB_COMPTARGET_HAIKU
						'' no crt1.o on Haiku
					case else
					ldcline += hFindLib( "crt1.o" )
				end select
			end if
		end if

		if (fbGetOption( FB_COMPOPT_TARGET ) <> FB_COMPTARGET_DARWIN) then
			'' All have crti.o, except OpenBSD and Darwin
			if (fbGetOption( FB_COMPOPT_TARGET ) <> FB_COMPTARGET_OPENBSD) then
				ldcline += hFindLib( "crti.o" )
			end if

			select case as const fbGetOption( FB_COMPOPT_TARGET )
			case FB_COMPTARGET_OPENBSD
				ldcline += hFindLib( "crtbegin.o" )
			case FB_COMPTARGET_HAIKU
				ldcline += hFindLib( "crtbeginS.o" )
				ldcline += hFindLib( "start_dyn.o" )
				ldcline += hFindLib( "init_term_dyn.o" )
			case else
				if( fbGetOption( FB_COMPOPT_PIC ) ) then
					ldcline += hFindLib( "crtbeginS.o" )
				else
					ldcline += hFindLib( "crtbegin.o" )
				end if
			end select
		end if

	case FB_COMPTARGET_ANDROID
		if( fbGetOption( FB_COMPOPT_OUTTYPE ) = FB_OUTTYPE_EXECUTABLE) then
			if( fbc.staticlink ) then
				ldcline += hFindLib( "crtbegin_static.o" )
			else
				ldcline += hFindLib( "crtbegin_dynamic.o" )
			end if
		else
			'' FB_OUTTYPE_DYNAMICLIB
			ldcline += hFindLib( "crtbegin_so.o" )
		end if

	case FB_COMPTARGET_XBOX
		'' nxdk's CRT startup object is provided by libpdclib.lib below.

	end select

	if( fbc.nofbrt0 = FALSE ) then
		'' don't add the fbrt0|fbrt1 if compiling for javascript, because global
		'' constructors and destructors are not supported by emscripten
		if( fbGetOption( FB_COMPOPT_TARGET ) <> FB_COMPTARGET_JS ) then
			ldcline += " """ + fbc.libpath + FB_HOST_PATHDIV
			select case fbGetOption( FB_COMPOPT_PROFILE )
			case FB_PROFILE_OPT_CALLS
				if( fbGetOption( FB_COMPOPT_PIC ) ) then
					ldcline += "fbrt1pic.o"
				else
					ldcline += "fbrt1.o"
				end if
			case FB_PROFILE_OPT_CYCLES
				if( fbGetOption( FB_COMPOPT_PIC ) ) then
					ldcline += "fbrt2pic.o"
				else
					ldcline += "fbrt2.o"
				end if
			case else
				if( fbGetOption( FB_COMPOPT_PIC ) ) then
					ldcline += "fbrt0pic.o"
				else
					ldcline += "fbrt0.o"
				end if
			end select
			ldcline += """"
		end if
	end if

	scope
		dim as string ptr objfile = listGetHead( @fbc.objlist )
		while( objfile )
			ldcline += " """ + *objfile + """"
			objfile = listGetNext( objfile )
		wend
	end scope

	dim as integer addsolarislibmearly = FALSE
	if( fbc.nodeflibs = FALSE ) then
		select case as const fbGetOption( FB_COMPOPT_TARGET )
		case FB_COMPTARGET_SOLARIS, FB_COMPTARGET_ILLUMOS
			'' Solaris libc exports some libm entry points too.  If a user
			'' library appears before -lm and causes libc to be loaded first,
			'' calls such as pow() can bind to libc's older entry point instead
			'' of libm's.  Keep the default math library early in the group so
			'' the runtime behavior matches GCC's normal Solaris link order.
			scope
				dim as TSTRSETITEM ptr i = listGetHead(@fbc.finallibs.list)
				while (i)
					if( i->s = "m" ) then
						addsolarislibmearly = TRUE
						exit while
					end if
					i = listGetNext(i)
				wend
			end scope
		end select
	end if

	'' Begin of lib group
	'' All libraries are passed inside -( -) so we don't need to worry as
	'' much about their order and/or listing them repeatedly. (Not supported by Darwin ld)
	if ( fbGetOption( FB_COMPOPT_TARGET ) <> FB_COMPTARGET_DARWIN ) then
		if( (fbGetOption( FB_COMPOPT_TARGET ) <> FB_COMPTARGET_JS) and _
		    (fbGetOption( FB_COMPOPT_TARGET ) <> FB_COMPTARGET_XBOX) ) then
			if( fbGetOption( FB_COMPOPT_TARGET ) = FB_COMPTARGET_WII ) then
				ldcline += " -Wl,--start-group"
			elseif( fbcUseLldLinker( ) ) then
				ldcline += " --start-group"
			else
				ldcline += " ""-("""
			end if
		end if
	end if

	if( addsolarislibmearly ) then
		ldcline += " -lm"
	end if

	'' Add libraries passed by file name
	scope
		dim as string ptr libfile = listGetHead(@fbc.libfiles)
		while (libfile)
			ldcline += " """ + *libfile + """"
			libfile = listGetNext(libfile)
		wend
	end scope

	'' Add libraries from command-line, those found during parsing, and
	'' the default ones
	scope
		dim as TSTRSETITEM ptr i = listGetHead(@fbc.finallibs.list)
		dim as integer checkdllname = (fbGetOption(FB_COMPOPT_OUTTYPE) = FB_OUTTYPE_DYNAMICLIB)
		while (i)
			'' Prevent linking DLLs against their own import library,
			'' or .so's against themselves (ld will fail to read in
			'' its output file...)
			if ((checkdllname = FALSE) orelse (i->s <> dllname)) then
				if( addsolarislibmearly andalso (i->s = "m") ) then
					'' Already emitted before user libraries, see above.
				elseif( fbGetOption( FB_COMPOPT_TARGET ) = FB_COMPTARGET_XBOX ) then
					hAddXboxLibArchive( ldcline, i->s )
				else
					dim as string libname = i->s
					libname = fbcPlatformMapLibName( libname )
					ldcline += " -l" + libname
				end if
			end if
			i = listGetNext(i)
		wend
	end scope

	hAddDarwinFrameworks( ldcline )
	if( fbGetOption( FB_COMPOPT_TARGET ) = FB_COMPTARGET_XBOX ) then
		hAddXboxNxdkLibs( ldcline )
	end if

	if (fbGetOption( FB_COMPOPT_TARGET ) <> FB_COMPTARGET_DARWIN) then
		if( (fbGetOption( FB_COMPOPT_TARGET ) <> FB_COMPTARGET_JS) and _
		    (fbGetOption( FB_COMPOPT_TARGET ) <> FB_COMPTARGET_XBOX) ) then
			'' End of lib group
			if( fbGetOption( FB_COMPOPT_TARGET ) = FB_COMPTARGET_WII ) then
				ldcline += " -Wl,--end-group"
			elseif( fbcUseLldLinker( ) ) then
				ldcline += " --end-group"
			else
				ldcline += " ""-)"""
			end if
		else
			if( fbGetOption( FB_COMPOPT_TARGET ) = FB_COMPTARGET_JS ) then
				ldcline += " -lfb"
			end if
		end if
	end if

	'' crt end
	select case as const fbGetOption( FB_COMPOPT_TARGET )
	case FB_COMPTARGET_CYGWIN
		if( fbGetOption( FB_COMPOPT_OUTTYPE ) = FB_OUTTYPE_DYNAMICLIB ) then
			ldcline += hFindLib( "crtend.o" )
		end if

	case FB_COMPTARGET_LINUX, FB_COMPTARGET_FREEBSD, _
		FB_COMPTARGET_OPENBSD, FB_COMPTARGET_NETBSD, _
		FB_COMPTARGET_DRAGONFLY, FB_COMPTARGET_SOLARIS, _
		FB_COMPTARGET_ILLUMOS
		if( fbGetOption( FB_COMPOPT_PIC ) ) then
			ldcline += hFindLib( "crtendS.o" )
		else
			ldcline += hFindLib( "crtend.o" )
		end if
		if (fbGetOption( FB_COMPOPT_TARGET ) <> FB_COMPTARGET_OPENBSD) then
			ldcline += hFindLib( "crtn.o" )
		end if

	case FB_COMPTARGET_ANDROID
		if( fbGetOption( FB_COMPOPT_OUTTYPE ) = FB_OUTTYPE_EXECUTABLE) then
			ldcline += hFindLib( "crtend_android.o" )
		else
			'' FB_OUTTYPE_DYNAMICLIB
			ldcline += hFindLib( "crtend_so.o" )
		end if

	case FB_COMPTARGET_HAIKU
		ldcline += hFindLib( "crtn.o" )
		ldcline += hFindLib( "crtendS.o" )

	case FB_COMPTARGET_WIN32
		ldcline += hFindLib( "crtend.o" )

	end select

	fbcDarwinPlatformAddCompilerDriverLinkerOptions( ldcline )

	'' This is required for 64-bit modules on *nix-y platforms
	'' for the unwind tables to have any effect
	'' Windows doesn't need this option
	select case as const fbGetOption( FB_COMPOPT_TARGET )
	case FB_COMPTARGET_LINUX, FB_COMPTARGET_HAIKU, FB_COMPTARGET_FREEBSD, _
		FB_COMPTARGET_OPENBSD, FB_COMPTARGET_NETBSD, _
		FB_COMPTARGET_DRAGONFLY
		dim as long outtype = fbGetOption( FB_COMPOPT_OUTTYPE )
		if outtype = FB_OUTTYPE_EXECUTABLE OrElse outtype = FB_OUTTYPE_DYNAMICLIB Then
			dim as long cpufamily = fbGetCpuFamily( )
			if cpufamily = FB_CPUFAMILY_X86_64 OrElse cpufamily = FB_CPUFAMILY_AARCH64 OrElse _
				cpuFamily = FB_CPUFAMILY_PPC64 OrElse cpufamily = FB_CPUFAMILY_PPC64LE Then
				ldcline += " --eh-frame-hdr"
			end if
		end if
	end select

	if( fbGetOption( FB_COMPOPT_TARGET ) = FB_COMPTARGET_JS ) then
		''
		'' Keep the wasm link optimized, but do not ask Emscripten to run
		'' its optional HTML/JavaScript minifier afterwards.  On Windows,
		'' the Node-based html-minifier wrapper can crash after the wasm
		'' output has already been produced, turning a good link into a
		'' failed build for browser programs.
		''
		ldcline += " --minify 0"
	end if

	'' extra options
	ldcline += " " + fbc.extopt.ld

	'' On some systems there are certain command line length limits which we
	'' can easily hit with our ld invocation, especially when linking huge
	'' programs, with lots of *.o files with long file names, such as the
	'' FB test suite.
	'' Typically > 127 chars but < 8k, but with huge programs, even > 32k.
	''
	'' On DOS there's a 127 char command line length limit. Our ld command
	'' line will typically be much longer than that, so we use ld's @file
	'' feature ("ld @file") and put the command line into that file.
	''
	'' The same happens on Win32 when we're invoking DOS .exes (some people
	'' use DOS binutils, instead of proper Win32-to-DOS binutils, to compile
	'' for DOS on Win32).
	''
	'' On Win32, there are multiple command line length limits to worry
	'' about:
	''    - cmd.exe (applies to FB shell()): 8192 on Windows XP+,
	''      2047 on Windows NT 4.0/2000
	''    - 32767 for CreateProcess() (applies to FB exec())
	'' fbcRunBin() can use either shell() or exec() depending on whether
	'' it's a normal/standalone build and whether the binutils were found.
	'' For standalone, it will always use exec(), but for non-standalone,
	'' we don't know what it'll do, so we should use the minimum limit.
	'' For shell() the full command line will also include the ld.exe
	'' command, i.e. "[<target>-]ld.exe ", which reduces the amount of room
	'' left over for the ld arguments.
	''
	'' On Linux/BSD systems there's typically some 100k or 200k limit which
	'' we usually don't hit.
	#ifdef __FB_DOS__
		if( hPutLdArgsIntoFile( ldcline ) = FALSE ) then
			exit function
		end if
	#elseif defined( __FB_WIN32__ )
		dim forcefile As Long
		dim targetprefixlen As ULong
		#ifdef ENABLE_STANDALONE
			'' "cross"-compiling? djgpp cross tools and emscripten
			'' build tools can't seem to handle the long command line
			'' created when linking ./tests/fbc-tests
			select case fbGetOption( FB_COMPOPT_TARGET )
			case FB_COMPTARGET_DOS, FB_COMPTARGET_JS
				forcefile = 1
			end select
		#else
			targetprefixlen = len( fbc.targetprefix )
		#endif
		dim toolnamelen as integer = len( "ld.exe " ) + _
			iif( targetprefixlen > len( fbc.buildprefix ), targetprefixlen, len( fbc.buildprefix ) )
		if( forcefile OrElse (fbGetOption( FB_COMPOPT_TARGET ) = FB_COMPTARGET_DOS) or _
			(len( ldcline ) > (2047 - toolnamelen)) ) then
			if( hPutLdArgsIntoFile( ldcline ) = FALSE ) then
				exit function
			end if
		end if
	#endif

	'' invoke ld
	var ld = fbcPlatformGetLinkerTool( )

	if( fbcRunBin( "linking", ld, ldcline ) = FALSE ) then
		exit function
	end if

	if( fbcDarwinPlatformBuildGuiAppBundle( ) = FALSE ) then
		exit function
	end if

	select case as const fbGetOption( FB_COMPOPT_TARGET )
	case FB_COMPTARGET_DOS
		'' patch the exe to change the stack size
		dim as integer f = freefile()

		if (open(fbc.outname, for binary, access read write, as #f) <> 0) then
			exit function
		end if

		dim value as long = clng( fbGetOption( FB_COMPOPT_STACKSIZE ) )
		put #f, 533, value

		close #f

	case FB_COMPTARGET_CYGWIN, FB_COMPTARGET_WIN32
		if( fbGetOption( FB_COMPOPT_OUTTYPE ) = FB_OUTTYPE_DYNAMICLIB ) then
			'' Create the .dll.a import library from the generated .def
			if (makeImpLib(dllname, deffile) = FALSE) then
				exit function
			end if
		end if

	case FB_COMPTARGET_XBOX
		'' Turn .exe into .xbe
		dim as string cxbepath, cxbecline
		dim as integer res = any

		if( len( xbox_xbe_outname ) = 0 ) then
			xbox_xbe_outname = hStripExt( fbc.outname ) + ".xbe"
		end if

		'' xbe title
		if( len(fbc.xbe_title) = 0 ) then
			fbc.xbe_title = hStripPath( hStripExt( xbox_xbe_outname ) )
		end if

		cxbecline = "-TITLE:" + QUOTE + fbc.xbe_title + (QUOTE + " ")

		if( fbGetOption( FB_COMPOPT_DEBUGINFO ) ) then
			cxbecline += "-DUMPINFO:" + QUOTE + hStripExt(xbox_xbe_outname) + (".cxbe" + QUOTE)
		end if

		'' output xbe filename
		cxbecline += " -OUT:" + QUOTE + xbox_xbe_outname + QUOTE

		'' input exe filename
		cxbecline += " " + QUOTE + fbc.outname + QUOTE

		'' don't echo cxbe output
		if( fbc.verbose = FALSE ) then
			cxbecline += " >nul"
		end if

		'' invoke cxbe (exe -> xbe)
		if( fbc.verbose ) then
			print "cxbe: ", cxbecline
		end if

		fbcFindBin( FBCTOOL_CXBE, cxbepath )

		'' have to use shell instead of exec in order to use >nul
		res = shell(cxbepath + " " + cxbecline)
		if( res <> 0 ) then
			if( fbc.verbose ) then
				print "cxbe failed: exit code " & res
			end if
			exit function
		end if

		'' remove .exe
		kill fbc.outname

	case FB_COMPTARGET_WII
		'' Turn the linked ELF into a bootable DOL image.
		if( len( wii_dol_outname ) = 0 ) then
			wii_dol_outname = hStripExt( fbc.outname ) + ".dol"
		end if

		if( fbcRunBin( "making DOL", FBCTOOL_ELF2DOL, QUOTE + fbc.outname + QUOTE + " " + QUOTE + wii_dol_outname + QUOTE ) = FALSE ) then
			exit function
		end if

		kill fbc.outname

	end select

	function = TRUE

end function

private sub hReadObjinfo( )
	dim as string dat
	dim as integer lang = any

	#macro hReportErr( num )
		errReportWarnEx( num, objinfoGetFilename( ), -1 )
	#endmacro

	do
		select case as const( objinfoReadNext( dat ) )
		case OBJINFO_LIB
			strsetAdd( @fbc.finallibs, dat, FALSE )
			if( dat = "sfx" OR dat = "sfxpic" OR dat = "sfxmt" OR dat = "sfxmtpic" ) then
				fbSetOption( FB_COMPOPT_FBSFX, TRUE )
			end if

		case OBJINFO_LIBPATH
			strsetAdd( @fbc.finallibpaths, dat, FALSE )

		case OBJINFO_MT
			if( fbc.objinf.mt = FALSE ) then
				hReportErr( FB_WARNINGMSG_MIXINGMTMODES )

				fbc.objinf.mt = TRUE
				fbSetOption( FB_COMPOPT_MULTITHREADED, TRUE )
			end if

		case OBJINFO_GFX
			fbSetOption( FB_COMPOPT_FBGFX, TRUE )

		case OBJINFO_LANG
			lang = fbGetLangId( dat )

			'' bad objinfo value?
			if( lang = FB_LANG_INVALID ) then
				lang = FB_LANG_FB
			end if

			if( lang <> fbc.objinf.lang ) then
				hReportErr( FB_WARNINGMSG_MIXINGLANGMODES )
				fbc.objinf.lang = lang
				fbSetOption( FB_COMPOPT_LANG, lang )
			end if

		case OBJINFO_SFX
			fbSetOption( FB_COMPOPT_FBSFX, TRUE )

		case else
			exit do
		end select
	loop

	objinfoReadEnd( )
end sub

private sub hCollectObjinfo( )
	dim as string ptr s = any
	dim as TSTRSETITEM ptr i = any

	'' for each object passed in the cmd-line
	s = listGetHead( @fbc.objlist )
	while( s )
		objinfoReadObj( *s )
		hReadObjinfo( )
		s = listGetNext( s )
	wend

	'' for each library found (must be done after processing all objects)
	i = listGetHead( @fbc.finallibs.list )
	while( i )
		'' Not default?
		if( i->userdata = FALSE ) then
			objinfoReadLib( i->s, @fbc.finallibpaths.list )
			hReadObjinfo( )
		end if
		i = listGetNext( i )
	wend

	'' Search libs given as *.a input files instead of -l or #inclib
	s = listGetHead( @fbc.libfiles )
	while( s )
		objinfoReadLibfile( *s )
		hReadObjinfo( )
		s = listGetNext( s )
	wend
end sub

private sub hFatalInvalidOption _
	( _
		byref arg as string, _
		byval is_source as integer _
	)
	'' if 'is_source' then show a line number, otherwise it's the actual command line and line number is undefined
	errReportEx( FB_ERRMSG_INVALIDCMDOPTION, QUOTE + arg + QUOTE, iif( is_source, 0, -1 ) )
	fbcEnd( 1 )
end sub

private sub hCheckWaitingObjfile( )
	if( len( fbc.objfile ) > 0 ) then
		errReportEx( FB_ERRMSG_OBJFILEWITHOUTINPUTFILE, "-o " & fbc.objfile, -1 )
		fbc.objfile = ""
	end if
end sub

private sub hSetIofile _
	( _
		byval module as FBCIOFILE ptr, _
		byref srcfile as string, _
		byval is_rc as integer _
	)

	module->srcfile = srcfile

	'' No objfile name set yet (from the -o <file> option)?
	if( len( fbc.objfile ) = 0 ) then
		module->is_custom_objfile = FALSE

		'' Choose default *.o name based on input file name
		if( is_rc ) then
#ifdef ENABLE_GORC
			'' GoRC only accepts *.obj
			'' foo.rc -> foo.obj, so there is no collision with foo.bas' foo.o
			fbc.objfile = hStripExt( srcfile ) + ".obj"
#else
			'' windres doesn't care, so we use the default *.o
			'' foo.rc -> foo.rc.o to avoid collision with foo.bas' foo.o
			fbc.objfile = srcfile + ".o"
#endif
		else
			'' foo.bas -> foo.o
			fbc.objfile = hStripExt( srcfile ) + ".o"
		end if

		'' Since there was no preceding -o for this module, allow
		'' -o <file> to follow later
		fbc.lastmodule = module
	else
		module->is_custom_objfile = TRUE
	end if

	module->objfile = fbcAddObj( fbc.objfile )
	fbc.objfile = ""

end sub

private sub hAddBas( byref basfile as string )
	hSetIofile( listNewNode( @fbc.modules ), basfile, FALSE )
end sub

type FBGNUOSINFO
	gnuid       as zstring ptr  '' Part of GNU triplet identifying a certain OS
	os          as integer      '' Corresponding FB_COMPTARGET_*
end type

type FBGNUARCHINFO
	gnuid       as zstring ptr  '' Part of GNU triplet identifying a certain architecture
	cputype     as integer      '' Corresponding FB_CPUTYPE_*
end type

'' OS name strings recognized when parsing GNU triplets (-target option)
dim shared as FBGNUOSINFO gnuosmap(0 to ...) => _
{ _
	(@"android"    , FB_COMPTARGET_ANDROID  ), _ '' Must appear before linux
	(@"nuttx"      , FB_COMPTARGET_NUTTX    ), _
	(@"linux"      , FB_COMPTARGET_LINUX    ), _
	(@"haiku"      , FB_COMPTARGET_HAIKU    ), _
	(@"mingw"      , FB_COMPTARGET_WIN32    ), _
	(@"djgpp"      , FB_COMPTARGET_DOS      ), _
	(@"msdosdjgpp" , FB_COMPTARGET_DOS      ), _
	(@"cygwin"     , FB_COMPTARGET_CYGWIN   ), _
	(@"darwin"     , FB_COMPTARGET_DARWIN   ), _
	(@"freebsd"    , FB_COMPTARGET_FREEBSD  ), _
	(@"dragonfly"  , FB_COMPTARGET_DRAGONFLY), _
	(@"solaris"    , FB_COMPTARGET_SOLARIS  ), _
	(@"illumos"    , FB_COMPTARGET_ILLUMOS  ), _
	(@"sunos"      , FB_COMPTARGET_ILLUMOS  ), _
	(@"netbsd"     , FB_COMPTARGET_NETBSD   ), _
	(@"openbsd"    , FB_COMPTARGET_OPENBSD  ), _
	(@"xbox"       , FB_COMPTARGET_XBOX     ), _
	(@"wii"        , FB_COMPTARGET_WII      )  _
}

'' Architectures recognized when parsing GNU triplets (-target option)
dim shared as FBGNUARCHINFO gnuarchmap(0 to ...) => _
{ _
	(@"i386"       , FB_CPUTYPE_386            ), _
	(@"i486"       , FB_CPUTYPE_486            ), _
	(@"i586"       , FB_CPUTYPE_586            ), _
	(@"i686"       , FB_CPUTYPE_686            ), _
	(@"x86"        , FB_DEFAULT_CPUTYPE_X86    ), _
	(@"x86_64"     , FB_DEFAULT_CPUTYPE_X86_64 ), _
	(@"amd64"      , FB_DEFAULT_CPUTYPE_X86_64 ), _
	(@"armv5te"    , FB_CPUTYPE_ARMV5TE        ), _
	(@"armv6"      , FB_CPUTYPE_ARMV6          ), _
	(@"armv6+fp"   , FB_CPUTYPE_ARMV6_FP       ), _
	(@"armv7a"     , FB_CPUTYPE_ARMV7A         ), _
	(@"armv7a+fp"  , FB_CPUTYPE_ARMV7A_FP      ), _
	(@"arm"        , FB_DEFAULT_CPUTYPE_ARM    ), _
	(@"aarch64"    , FB_DEFAULT_CPUTYPE_AARCH64), _
	(@"arm64"      , FB_DEFAULT_CPUTYPE_AARCH64), _
	(@"ppc"        , FB_DEFAULT_CPUTYPE_PPC    ), _
	(@"powerpc"    , FB_DEFAULT_CPUTYPE_PPC    ), _
	(@"ppc64"      , FB_DEFAULT_CPUTYPE_PPC64  ), _
	(@"powerpc64"  , FB_DEFAULT_CPUTYPE_PPC64  ),  _
	(@"ppc64le"    , FB_DEFAULT_CPUTYPE_PPC64LE), _
	(@"powerpc64le", FB_DEFAULT_CPUTYPE_PPC64LE), _
	(@"riscv32"    , FB_DEFAULT_CPUTYPE_RISCV32), _
	(@"rv32"       , FB_DEFAULT_CPUTYPE_RISCV32), _
	(@"rv32imac"   , FB_DEFAULT_CPUTYPE_RISCV32), _
	(@"riscv64"    , FB_DEFAULT_CPUTYPE_RISCV64), _
	(@"rv64"       , FB_DEFAULT_CPUTYPE_RISCV64), _
	(@"rv64gc"     , FB_DEFAULT_CPUTYPE_RISCV64), _
	(@"s390x"      , FB_DEFAULT_CPUTYPE_S390X  ), _
	(@"loongarch64", FB_DEFAULT_CPUTYPE_LOONGARCH64)  _
}

'' Identify OS (FB_COMPTARGET_*) and architecture (FB_CPUTYPE_*) in a GNU
'' triplet string (gcc toolchain target name).
private sub hParseGnuTriplet _
	( _
		byref arg as string, _
		byval separator as integer, _
		byref os as integer, _
		byref cputype as integer _
	)
	dim arch as string

	'' Search for OS, it be anywere in the triplet:
	''    mingw32              -> mingw
	''    arm-linux-gnueabihf  -> linux
	''    arm-linux-androideabi-> android
	''    i686-w64-mingw32     -> mingw
	''    i686-pc-linux-gnu    -> linux
	''    i386-pc-msdosdjgpp   -> dos386
	''    i486-pc-msdosdjgpp   -> dos486
	''    i586-pc-msdosdjgpp   -> dos586
	for i as integer = 0 to ubound( gnuosmap )
		if( instr( arg, *gnuosmap(i).gnuid ) > 0 ) then
			os = gnuosmap(i).os
			exit for
		end if
	next

	'' If the triplet has at least two components (<arch>-<...>),
	'' extract the first (the architecture) and try to identify it.
	if( separator > 0 ) then
		arch = left( arg, separator - 1 )
		for i as integer = 0 to ubound( gnuarchmap )
			if( arch = *gnuarchmap(i).gnuid ) then
				cputype = gnuarchmap(i).cputype
				exit for
			end if
		next
	end if

end sub

function fbCpuTypeFromGNUArchInfo( byref arch as string ) as integer
	for i as integer = 0 to ubound( gnuarchmap )
		if( arch = *gnuarchmap(i).gnuid ) then
			return gnuarchmap(i).cputype
		end if
	next
	return -1
end function

type FBOSARCHINFO
	targetid    as zstring ptr  '' -target option argument
	os          as integer      '' FB_COMPTARGET_*
	cputype     as integer      '' FB_CPUTYPE_*
end type

'' Simple free-form arguments accepted by -target option
dim shared as FBOSARCHINFO fbosarchmap(0 to ...) => _
{ _
	_ '' win32/win64 refer to specific OS/arch combinations
	(@"win32"  , FB_COMPTARGET_WIN32  , FB_DEFAULT_CPUTYPE_X86   ), _
	(@"win64"  , FB_COMPTARGET_WIN32  , FB_DEFAULT_CPUTYPE_X86_64), _
	(@"win32-aarch64", FB_COMPTARGET_WIN32, FB_DEFAULT_CPUTYPE_AARCH64), _
	_ '' dragonfly is 64 bit only
	(@"dragonfly", FB_COMPTARGET_DRAGONFLY, FB_DEFAULT_CPUTYPE_X86_64), _
	_ '' solaris is 64 bit only
	(@"solaris", FB_COMPTARGET_SOLARIS, FB_DEFAULT_CPUTYPE_X86_64), _
	_ '' illumos is 64 bit only
	(@"illumos", FB_COMPTARGET_ILLUMOS, FB_DEFAULT_CPUTYPE_X86_64), _
	_
	_ '' OS given without arch, using the default arch, except for dos/xbox
	_ ''  which only work with x86, so we can always default to x86 for them.
	_ '' (these are supported for backwards compatibility with x86-only FB)
	_ '' When targetting android assume cross-compiling.
	_ '' armv7a is the default arch for android ndk r11 and later
	(@"dos"    , FB_COMPTARGET_DOS    , FB_DEFAULT_CPUTYPE_X86   ), _
	(@"xbox"   , FB_COMPTARGET_XBOX   , FB_DEFAULT_CPUTYPE_X86   ), _
	(@"cygwin" , FB_COMPTARGET_CYGWIN , FB_DEFAULT_CPUTYPE       ), _
	(@"darwin" , FB_COMPTARGET_DARWIN , FB_DEFAULT_CPUTYPE       ), _
	(@"freebsd", FB_COMPTARGET_FREEBSD, FB_DEFAULT_CPUTYPE       ), _
	(@"linux"  , FB_COMPTARGET_LINUX  , FB_DEFAULT_CPUTYPE       ), _
	(@"haiku"  , FB_COMPTARGET_HAIKU  , FB_DEFAULT_CPUTYPE       ), _
	(@"android", FB_COMPTARGET_ANDROID, FB_CPUTYPE_ARMV7A        ), _
	(@"netbsd" , FB_COMPTARGET_NETBSD , FB_DEFAULT_CPUTYPE       ), _
	(@"openbsd", FB_COMPTARGET_OPENBSD, FB_DEFAULT_CPUTYPE       ), _
	(@"wii"    , FB_COMPTARGET_WII    , FB_DEFAULT_CPUTYPE_PPC   ), _
	(@"nuttx"  , FB_COMPTARGET_NUTTX  , FB_DEFAULT_CPUTYPE_RISCV32)  _
}

''
'' Parse the -target option's argument.
''
'' Examples:
''    -target win32           ->    Windows + default x86 arch
''    -target win64           ->    Windows + x86_64
''    -target win32-aarch64   ->    Windows + AArch64
''    -target dos             ->    DOS + x86
''    -target linux           ->    Linux + default arch
''    -target linux-x86       ->    Linux + default x86 arch
''    -target linux-x86_64    ->    Linux + x86_64
''    -target android         ->    Android + ARMv7a (the default ARM)
''    ...
''
'' The normal (non-standalone) build also accepts GNU triplets:
'' (the rough format is <arch>-<vendor>-<os> but it can vary a lot)
''    -target i686-pc-linux-gnu        ->    Linux + i686
''    -target arm-linux-gnueabihf      ->    Linux + default ARM arch
''    -target arm-android              ->    android-arm, armv7-a, 32bit
''    -target android -arch armv5      ->    android-arm, armv5te, 32bit
''    -target armv5te-linux-android    ->    android-arm, armv5te, 32bit
''    -target android -arch armv7      ->    android-arm, armv7-a, 32bit
''    -target arm-linux-android        ->    android-arm, armv7-a, 32bit
''    -target armv7a-linux-android     ->    android-arm, armv7-a, 32bit
''    -target armv7a-linux-androideabi ->    android-arm, armv7-a, 32bit
''    -target i686-linux-android       ->    android-x86, 686, 32bit
''    -target x86_64-w64-mingw32       ->    Windows + x86_64
''    ...
''
'' The normal build uses the -target argument as prefix for binutils/gcc tools.
'' This allows fbc to work well with gcc/binutils cross-compiling toolchains,
'' for example: -target i686-pc-mingw32 causes it to use i686-pc-mingw32-ld
'' instead of the native ld.
''
'' Something like -target win32 is mostly useful for the standalone build, where
'' binutils/gcc tools are arranged into the bin/win32/ld.exe etc. directory
'' layout. -target win32 is less useful for the normal build, because typically
'' binutils/gcc toolchains for cross-compiling to Windows are named something
'' like "i686-pc-mingw32", not just "win32". Nevertheless, even the normal build
'' should accept these options -- it can be useful for debugging purposes or
'' with the -print option. Furthermore, people (unnecessarily) specify -target
'' for native compilation (e.g. using -target linux on Linux), and this supports
'' that.
''
'' This function should just do parsing, without any validation. It would be
'' nice if
''    -target linux-x86_64
'' would just be the same as
''    -target linux -arch x86_64
'' Thus, any validation should be done later when the command line has been
'' parsed completely.
''
'' It's up to the caller to report an error if only one of OS/arch (but not
'' both) could be identified.
''
private sub hParseTargetArg _
	( _
		byref arg as string, _
		byref os as integer, _
		byref cputype as integer, _
		byref is_gnu_triplet as integer _
	)

	os = -1
	cputype = -1
	is_gnu_triplet = FALSE

	'' Case-insensitive so "-target WIN32" etc. works too
	var lcasearg = lcase( arg )

	'' Check for simple arguments (dos, linux, win32, etc.)
	for i as integer = 0 to ubound( fbosarchmap )
		if( lcasearg = *fbosarchmap(i).targetid ) then
			os = fbosarchmap(i).os
			cputype = fbosarchmap(i).cputype
			exit sub
		end if
	next

	'' <os>-<cpufamily>
	var separator = instr( arg, "-" )
	if( separator > 0 ) then
		os = fbIdentifyOs( left( lcasearg, separator - 1 ) )
		cputype = fbDefaultCpuTypeFromCpuFamilyId( os, right( lcasearg, len( lcasearg ) - separator ) )

		'' allow normalizing on gnu arch types to determine the standalone targetid
		#ifdef ENABLE_STANDALONE
			if( (os < 0) and (cputype < 0) ) then
				cputype = fbCpuTypeFromGNUArchInfo( right( lcasearg, len( lcasearg ) - separator ) )
			end if
		#endif
	end if

	'' Normal build: Check for GNU triplets, if the above checks failed.
	#ifndef ENABLE_STANDALONE
		if( (os < 0) and (cputype < 0) ) then
			hParseGnuTriplet( arg, separator, os, cputype )
			is_gnu_triplet = TRUE
		end if
	#else
		if( (os < 0) and (cputype < 0) ) then
			hParseGnuTriplet( arg, separator, os, cputype )
		end if
	#endif
end sub

enum
	OPT_A = 0
	OPT_ARCH
	OPT_ASM
	OPT_B
	OPT_BUILDPREFIX
	OPT_C
	OPT_CKEEPOBJ
	OPT_D
	OPT_DLL
	OPT_DYLIB
	OPT_E
	OPT_EARRAY
	OPT_EARRAYDIMS
	OPT_EASSERT
	OPT_EDEBUG
	OPT_EDEBUGINFO
	OPT_ELOCATION
	OPT_ENULLPTR
	OPT_EUNWIND
	OPT_ENTRY
	OPT_EX
	OPT_EXX
	OPT_EXPORT
	OPT_FBGFX
	OPT_FORCELANG
	OPT_FPMODE
	OPT_FPU
	OPT_G
	OPT_GEN
	OPT_HELP
	OPT_I
	OPT_INCLUDE
	OPT_L
	OPT_LANG
	OPT_LIB
	OPT_M
	OPT_MAP
	OPT_MAXERR
	OPT_MT
	OPT_NODEFLIBS
	OPT_NOERRLINE
	OPT_NOLIB
	OPT_NOOBJINFO
	OPT_NOSTRIP
	OPT_O
	OPT_OPTIMIZE
	OPT_P
	OPT_PIC
	OPT_PP
	OPT_PREFIX
	OPT_PRINT
	OPT_PROFGEN
	OPT_PROFILE
	OPT_R
	OPT_RKEEPASM
	OPT_RR
	OPT_RRKEEPASM
	OPT_S
	OPT_SHOWINCLUDES
	OPT_STATIC
	OPT_STRIP
	OPT_SYSROOT
	OPT_T
	OPT_TARGET
	OPT_TITLE
	OPT_V
	OPT_VEC
	OPT_VERSION
	OPT_W
	OPT_WA
	OPT_WC
	OPT_WL
	OPT_X
	OPT_Z
	OPT__COUNT
end enum

type FBC_CMDLINE_OPTION
	takes_argument as boolean          '' true = option requires argument
	allowed_in_source as boolean       '' true = can be used with #cmdline directive
	parser_restart as boolean          '' true = restart of parser is required when used with #cmdline directive
	fbc_restart as integer             '' true = major restart of fbc required
end type

dim shared as FBC_CMDLINE_OPTION cmdlineOptionTB(0 to (OPT__COUNT - 1)) = _
{ _
	( TRUE , TRUE , FALSE, FALSE ), _ '' OPT_A            add files to link, affects link
	( TRUE , TRUE , TRUE , TRUE  ), _ '' OPT_ARCH         affects major initialization
	( TRUE , TRUE , FALSE, TRUE  ), _ '' OPT_ASM          affects major initialization,affects second stage compile
	( TRUE , TRUE , FALSE, FALSE ), _ '' OPT_B            adds files to compile
	( TRUE , TRUE , FALSE, TRUE  ), _ '' OPT_BUILDPREFIX  affects tools executed (fbcSetupCompilerPaths(), so restart is required)
	( FALSE, TRUE , TRUE , FALSE ), _ '' OPT_C            affects code generation / compile / assemble /link process
	( FALSE, TRUE , FALSE, FALSE ), _ '' OPT_CKEEPOBJ     affects removal of temporary files
	( TRUE , TRUE , FALSE, TRUE  ), _ '' OPT_D            add symbols to current source also, not just the preDefines, affects global defines
	( FALSE, TRUE , TRUE , TRUE  ), _ '' OPT_DLL          affects major initialization, affects output format
	( FALSE, TRUE , TRUE , TRUE  ), _ '' OPT_DYLIB        affects major initialization, affects output format
	( FALSE, TRUE , TRUE , FALSE ), _ '' OPT_E            affects code generation
	( FALSE, TRUE , TRUE , FALSE ), _ '' OPT_EARRAY       affects code generation
	( FALSE, TRUE , TRUE , FALSE ), _ '' OPT_EARRAYDIMS   affects code generation
	( FALSE, TRUE , TRUE , FALSE ), _ '' OPT_EASSERT      affects code generation
	( FALSE, TRUE , TRUE , FALSE ), _ '' OPT_EDEBUG       affects code generation
	( FALSE, TRUE , TRUE , FALSE ), _ '' OPT_EDEBUGINFO   affects code generation, affects link
	( FALSE, TRUE , TRUE , FALSE ), _ '' OPT_ELOCATION    affects code generation
	( FALSE, TRUE , TRUE , FALSE ), _ '' OPT_ENULLPTR     affects code generation
	( FALSE, TRUE , TRUE , FALSE ), _ '' OPT_EUNWIND      affects code generation
	( TRUE , TRUE , TRUE , TRUE  ), _ '' OPT_ENTRY        affects major initialization, affects code generation
	( FALSE, TRUE , TRUE , FALSE ), _ '' OPT_EX           affects code generation
	( FALSE, TRUE , TRUE , FALSE ), _ '' OPT_EXX          affects code generation
	( FALSE, TRUE , TRUE , FALSE ), _ '' OPT_EXPORT       affects code generation
	( FALSE, TRUE , FALSE, FALSE ), _ '' OPT_FBGFX        affects link
	( TRUE , TRUE , TRUE , FALSE ), _ '' OPT_FORCELANG    never allow, command line only
	( TRUE , TRUE , TRUE , TRUE  ), _ '' OPT_FPMODE       affects major initialization, affects code generation
	( TRUE , TRUE , TRUE , TRUE  ), _ '' OPT_FPU          affects major initialization,affects code generation, affects second stage compile, affects link
	( FALSE, TRUE , TRUE , FALSE ), _ '' OPT_G            affects code generation, affects link
	( TRUE , TRUE , TRUE , TRUE  ), _ '' OPT_GEN          affects major initialization
	( FALSE, FALSE, FALSE, FALSE ), _ '' OPT_HELP         never allow, real command line only, makes no sense to have in source
	( TRUE , TRUE , TRUE , TRUE  ), _ '' OPT_I            add include path before the default one
	( TRUE , TRUE , TRUE , TRUE  ), _ '' OPT_INCLUDE      restart required to inject preInclude
	( TRUE , TRUE , FALSE, FALSE ), _ '' OPT_L            affects link, same as #inclib
	( TRUE , TRUE , TRUE , FALSE ), _ '' OPT_LANG         affects code generation, affects initialization
	( FALSE, TRUE , TRUE , TRUE  ), _ '' OPT_LIB          affects major initialization, affects output format
	( TRUE , TRUE , TRUE , TRUE  ), _ '' OPT_M            affects major initialization for all modules
	( TRUE , TRUE , FALSE, FALSE ), _ '' OPT_MAP          affects output files
	( TRUE , TRUE , FALSE, FALSE ), _ '' OPT_MAXERR       affects compile process
	( FALSE, TRUE , TRUE , FALSE ), _ '' OPT_MT           affects link, __FB_MT__
	( FALSE, TRUE , FALSE, FALSE ), _ '' OPT_NODEFLIBS    affects link
	( FALSE, TRUE , FALSE, FALSE ), _ '' OPT_NOERRLINE    affects compiler output display
	( TRUE , TRUE , FALSE, FALSE ), _ '' OPT_NOLIB        affects link
	( FALSE, TRUE , FALSE, FALSE ), _ '' OPT_NOOBJINFO    affects post compile process
	( FALSE, TRUE , FALSE, FALSE ), _ '' OPT_NOSTRIP      affects link
	( TRUE , TRUE , TRUE , TRUE  ), _ '' OPT_O            affects output file naming with initialization before compile
	( TRUE , TRUE , FALSE, FALSE ), _ '' OPT_OPTIMIZE     affects link
	( TRUE , TRUE , FALSE, FALSE ), _ '' OPT_P            affects link, same as #libpath
	( FALSE, TRUE , FALSE, TRUE  ), _ '' OPT_PIC          affects major initialization, affects link
	( FALSE, TRUE , FALSE, TRUE  ), _ '' OPT_PP           affects major initialization
	( TRUE , TRUE , FALSE, TRUE  ), _ '' OPT_PREFIX       affects major initialization
	( TRUE , FALSE, FALSE, FALSE ), _ '' OPT_PRINT        never allow, makes no sense to have in source
	( TRUE , TRUE , TRUE , TRUE  ), _ '' OPT_PROFGEN      affects major initialization, affects initialization, affects code generation, affects link
	( FALSE, TRUE , TRUE , TRUE  ), _ '' OPT_PROFILE      affects major initialization, affects initialization, affects code generation, affects link
	( FALSE, TRUE , TRUE , FALSE ), _ '' OPT_R            affects compile / assemble / link process, removal of temporary files
	( FALSE, TRUE , TRUE , FALSE ), _ '' OPT_RKEEPASM     affects removal of temporary files
	( FALSE, TRUE , TRUE , FALSE ), _ '' OPT_RR           affects compile / assemble / link process, removal of temporary files
	( FALSE, TRUE , TRUE , FALSE ), _ '' OPT_RRKEEPASM    affects removal of temporary files
	( TRUE , TRUE , FALSE, FALSE ), _ '' OPT_S            affects link
	( FALSE, TRUE , FALSE, TRUE  ), _ '' OPT_SHOWINCLUDES affects compiler output display
	( FALSE, TRUE , FALSE, FALSE ), _ '' OPT_STATIC       affects link
	( FALSE, TRUE , FALSE, FALSE ), _ '' OPT_STRIP        affects link
	( TRUE,  TRUE , FALSE, FALSE ), _ '' OPT_SYSROOT      affects link
	( TRUE , TRUE , FALSE, FALSE ), _ '' OPT_T            affects link
	( TRUE , TRUE , TRUE , TRUE  ), _ '' OPT_TARGET       affects major initialization
	( TRUE , TRUE , FALSE, FALSE ), _ '' OPT_TITLE        affects link
	( FALSE, TRUE , FALSE, FALSE ), _ '' OPT_V            affects nothing
	( TRUE , TRUE , TRUE , TRUE  ), _ '' OPT_VEC          affects major initialization, affects code generation
	( FALSE, TRUE , FALSE, FALSE ), _ '' OPT_VERSION      print version information
	( TRUE , TRUE , FALSE, FALSE ), _ '' OPT_W            affects compiler display output
	( TRUE , TRUE , FALSE, FALSE ), _ '' OPT_WA           affects assembly
	( TRUE , TRUE , FALSE, FALSE ), _ '' OPT_WC           affects second stage compile
	( TRUE , TRUE , FALSE, FALSE ), _ '' OPT_WL           affects link
	( TRUE , TRUE , FALSE, FALSE ), _ '' OPT_X            affects output file
	( TRUE , TRUE , TRUE , TRUE  )  _ '' OPT_Z            affects various - code generation
}

private sub handleOpt _
	( _
		byval optid as integer, _
		byref arg as string, _
		byval is_source as integer _
	)

	select case as const (optid)
	case OPT_A
		fbcAddObj( arg )

	case OPT_ARCH
		'' Set cputype later, so it overrides -target
		fbc.cputype_is_native = (arg = "native")
		fbc.cputype = fbIdentifyFbcArch( arg )
		if( fbc.cputype < 0 ) then
			hFatalInvalidOption( "-arch " + arg, is_source )
		end if

	case OPT_ASM
		select case( arg )
		case "att"
			fbc.asmsyntax = FB_ASMSYNTAX_ATT
		case "intel"
			fbc.asmsyntax = FB_ASMSYNTAX_INTEL
		case else
			hFatalInvalidOption( arg, is_source )
		end select

	case OPT_B
		hAddBas( arg )

	case OPT_BUILDPREFIX
		fbc.buildprefix = arg

	case OPT_C
		'' -c changes the output type from exe/lib/dll to object,
		'' overwriting previous -dll, -lib or the default exe.
		fbSetOption( FB_COMPOPT_OUTTYPE, FB_OUTTYPE_OBJECT )
		fbc.keepobj = TRUE

	case OPT_CKEEPOBJ
		fbc.keepobj = TRUE

	case OPT_D
		fbAddPreDefine(arg)

	case OPT_DLL, OPT_DYLIB
		fbSetOption( FB_COMPOPT_OUTTYPE, FB_OUTTYPE_DYNAMICLIB )

	case OPT_E
		fbSetOption( FB_COMPOPT_ERRORCHECK, TRUE )
		fbSetOption( FB_COMPOPT_UNWINDINFO, TRUE )

	case OPT_EARRAY
		fbSetOption( FB_COMPOPT_ARRAYBOUNDCHECK, TRUE )

	case OPT_EARRAYDIMS
		fbSetOption( FB_COMPOPT_ARRAYDIMSCHECK, TRUE )

	case OPT_EASSERT
		fbSetOption( FB_COMPOPT_ASSERTIONS, TRUE )

	case OPT_EDEBUG
		fbSetOption( FB_COMPOPT_DEBUG, TRUE )

	case OPT_EDEBUGINFO
		fbSetOption( FB_COMPOPT_DEBUGINFO, TRUE )

	case OPT_ELOCATION
		fbSetOption( FB_COMPOPT_ERRLOCATION, TRUE )

	case OPT_ENULLPTR
		fbSetOption( FB_COMPOPT_NULLPTRCHECK, TRUE )

	case OPT_EUNWIND
		fbSetOption( FB_COMPOPT_UNWINDINFO, TRUE )

	case OPT_ENTRY
		fbc.entry = arg

	case OPT_EX
		fbSetOption( FB_COMPOPT_ERRORCHECK, TRUE )
		fbSetOption( FB_COMPOPT_RESUMEERROR, TRUE )
		fbSetOption( FB_COMPOPT_UNWINDINFO, TRUE )

	case OPT_EXX
		fbSetOption( FB_COMPOPT_ERRORCHECK, TRUE )
		fbSetOption( FB_COMPOPT_RESUMEERROR, TRUE )
		fbSetOption( FB_COMPOPT_EXTRAERRCHECK, TRUE )
		fbSetOption( FB_COMPOPT_ERRLOCATION, TRUE )
		fbSetOption( FB_COMPOPT_ARRAYBOUNDCHECK, TRUE )
		fbSetOption( FB_COMPOPT_ARRAYDIMSCHECK, TRUE )
		fbSetOption( FB_COMPOPT_NULLPTRCHECK, TRUE )
		fbSetOption( FB_COMPOPT_UNWINDINFO, TRUE )

	case OPT_EXPORT
		fbSetOption( FB_COMPOPT_EXPORT, TRUE )

	case OPT_FBGFX
		fbSetOption( FB_COMPOPT_FBGFX, TRUE )

	case OPT_FORCELANG
		dim as integer value = fbGetLangId(strptr(arg))
		if( value = FB_LANG_INVALID ) then
			hFatalInvalidOption( arg, is_source )
		end if

		'' show a warning only if forcelang was already set and
		'' we are handling this from a #cmdline.  We'd like that
		'' -forcelang from the real command line takes priority
		'' over source code #cmdline "-forcelang"
		if( is_source and fbGetOption( FB_COMPOPT_FORCELANG ) ) then
			errReportWarn( FB_WARNINGMSG_CMDLINEOVERRIDES )
		else
			fbSetOption( FB_COMPOPT_LANG, value )
			fbSetOption( FB_COMPOPT_FORCELANG, TRUE )
			fbc.objinf.lang = value

			if( is_source ) then
				fbSetOption( FB_COMPOPT_RESTART_LANG, value )
			end if
		end if

	case OPT_FPMODE
		dim as integer value = any

		select case ucase(arg)
		case "PRECISE"
			value = FB_FPMODE_PRECISE
		case "FAST"
			value = FB_FPMODE_FAST
		case else
			hFatalInvalidOption( arg, is_source )
		end select

		fbSetOption( FB_COMPOPT_FPMODE, value )

	case OPT_FPU
		dim as integer value = any

		select case ucase(arg)
		case "X87", "FPU"
			value = FB_FPUTYPE_FPU
		case "SSE"
			value = FB_FPUTYPE_SSE
		case "NEON"
			value = FB_FPUTYPE_NEON
		case else
			hFatalInvalidOption( arg, is_source )
		end select

		fbSetOption( FB_COMPOPT_FPUTYPE, value )

	case OPT_G
		fbSetOption( FB_COMPOPT_DEBUG, TRUE )
		fbSetOption( FB_COMPOPT_DEBUGINFO, TRUE )
		fbSetOption( FB_COMPOPT_ASSERTIONS, TRUE )

	case OPT_GEN
		select case( lcase( arg ) )
		case "gas"
			fbc.backend = FB_BACKEND_GAS
		case "gcc"
			fbc.backend = FB_BACKEND_GCC
		case "clang"
			fbc.backend = FB_BACKEND_CLANG
		case "llvm"
			fbc.backend = FB_BACKEND_LLVM
		Case "gas64"
			fbc.backend = FB_BACKEND_GAS64
		case else
			hFatalInvalidOption( arg, is_source )
		end select

	case OPT_HELP
		fbc.showhelp = TRUE

	case OPT_I
		fbAddIncludePath(arg)

	case OPT_INCLUDE
		fbAddPreInclude(arg)

	case OPT_L
		strsetAdd(@fbc.libs, arg, FALSE)

	case OPT_LANG
		dim as integer value = fbGetLangId( strptr(arg) )
		if( value = FB_LANG_INVALID ) then
			hFatalInvalidOption( arg, is_source )
		end if

		'' don't let '-lang' option or #cmdline "-lang" overide -forcelang
		if( fbGetOption( FB_COMPOPT_FORCELANG ) = FALSE ) then
			fbSetOption( FB_COMPOPT_LANG, value )
			fbc.objinf.lang = value

			if( is_source ) then
				fbSetOption( FB_COMPOPT_RESTART_LANG, value )
			endif
		end if

	case OPT_LIB
		fbSetOption( FB_COMPOPT_OUTTYPE, FB_OUTTYPE_STATICLIB )

	case OPT_M
		fbc.mainname = arg
		fbc.mainset = TRUE

	case OPT_MAP
		fbc.mapfile = arg

	case OPT_MAXERR
		dim as integer value = any

		if( arg = "inf" ) then
			value = FB_ERR_INFINITE
		else
			value = clng( arg )
			if( value <= 0 ) then
				hFatalInvalidOption( arg, is_source )
			end if
		end if

		fbSetOption( FB_COMPOPT_MAXERRORS, value )

	case OPT_MT
		fbSetOption( FB_COMPOPT_MULTITHREADED, TRUE )
		fbc.objinf.mt = TRUE

	case OPT_NODEFLIBS
		fbc.nodeflibs = TRUE
		fbc.nofbrt0 = TRUE

	case OPT_NOERRLINE
		fbSetOption( FB_COMPOPT_SHOWERROR, FALSE )

	case OPT_NOLIB
		dim libs() as string
		hSplitStr(arg, ",", libs())
		for i as integer = lbound(libs) to ubound(libs)
			if len(libs(i)) > 0 then
				strsetAdd(@fbc.excludedlibs, libs(i), 0 /'unused userdata'/)
			end if
		next

	case OPT_NOOBJINFO
		fbSetOption( FB_COMPOPT_OBJINFO, FALSE )

	case OPT_NOSTRIP
		fbc.stripsymbols = FALSE

	case OPT_O
		'' Error if there already is an -o waiting to be assigned
		hCheckWaitingObjfile( )

		'' Assign it to the last module, if it doesn't have an
		'' -o filename yet, or store it for later otherwise.
		if( fbc.lastmodule ) then
			*fbc.lastmodule->objfile = arg
			fbc.lastmodule->is_custom_objfile = TRUE
		else
			fbc.objfile = arg
		end if

	case OPT_OPTIMIZE
		dim as integer value = any

		if (arg = "max") then
			value = 3
		else
			value = clng( arg )
			if (value < 0) then
				value = 0
			elseif (value > 3) then
				value = 3
			end if
		end if

		fbSetOption( FB_COMPOPT_OPTIMIZELEVEL, value )

	case OPT_P
		strsetAdd(@fbc.libpaths, pathStripDiv( pathNormalizeHost( arg ) ), FALSE)

	case OPT_PIC
		fbSetOption( FB_COMPOPT_PIC, TRUE )

	case OPT_PP
		'' -pp doesn't change the output type, but like -r we want to
		'' stop fbc very early.
		fbSetOption( FB_COMPOPT_PPONLY, TRUE )
		fbc.emitasmonly = TRUE

	case OPT_PREFIX
		fbc.prefix = pathStripDiv( pathNormalizeHost( arg ) )
		hReplaceSlash( fbc.prefix, asc( FB_HOST_PATHDIV ) )

	case OPT_PRINT
		select case( arg )
		case "host"   : fbc.print = PRINT_HOST
		case "target" : fbc.print = PRINT_TARGET
		case "x"      : fbc.print = PRINT_X
		case "fblibdir" : fbc.print = PRINT_FBLIBDIR
		case "sha-1"  : fbc.print = PRINT_SHA1
		case "fork-id": fbc.print = PRINT_FORK_ID
		case else
			hFatalInvalidOption( arg, is_source )
		end select

	case OPT_PROFILE
		fbSetOption( FB_COMPOPT_PROFILE, FB_PROFILE_OPT_GMON )

	case OPT_PROFGEN
		select case( arg )
		case "default", "gmon"
			fbSetOption( FB_COMPOPT_PROFILE, FB_PROFILE_OPT_GMON )
		case "fb"
			fbSetOption( FB_COMPOPT_PROFILE, FB_PROFILE_OPT_CALLS )
		case "cycles"
			fbSetOption( FB_COMPOPT_PROFILE, FB_PROFILE_OPT_CYCLES )
		case else
			hFatalInvalidOption( arg, is_source )
		end select

	case OPT_R
		'' -r changes the output type to .o, like -c, i.e. -m may have
		'' to be used to mark the main module, just like -c.
		fbSetOption( FB_COMPOPT_OUTTYPE, FB_OUTTYPE_OBJECT )
		'' -r will stop fbc earlier than -c though.
		fbc.emitasmonly = TRUE
		fbc.keepasm = TRUE

	case OPT_RKEEPASM
		fbc.keepasm = TRUE

	case OPT_RR
		fbSetOption( FB_COMPOPT_OUTTYPE, FB_OUTTYPE_OBJECT )
		fbc.emitfinalasmonly = TRUE
		fbc.keepfinalasm = TRUE

	case OPT_RRKEEPASM
		fbc.keepfinalasm = TRUE

	case OPT_S
		fbc.subsystem = arg
		select case lcase( arg )
		case "gui", "windows"
			fbSetOption( FB_COMPOPT_MODEVIEW, FB_MODEVIEW_GUI )
		case "console"
			fbSetOption( FB_COMPOPT_MODEVIEW, FB_MODEVIEW_CONSOLE )

		end select

	case OPT_SHOWINCLUDES
		fbSetOption( FB_COMPOPT_SHOWINCLUDES, TRUE )

	case OPT_STATIC
		fbc.staticlink = TRUE

	case OPT_STRIP
		fbc.stripsymbols = TRUE

	case OPT_SYSROOT
		fbc.sysroot = arg

	case OPT_T
		fbSetOption( FB_COMPOPT_STACKSIZE, clng( arg ) * 1024 )

	case OPT_TARGET
		dim as integer os, cputype, is_gnu_triplet
		hParseTargetArg( arg, os, cputype, is_gnu_triplet )

		if( (os < 0) or (cputype < 0) ) then
			hFatalInvalidOption( arg, is_source )
		end if

		'' Store the OS/cputype, overwriting the values from any
		'' previous -target options.
		fbSetOption( FB_COMPOPT_TARGET, os )
		fbSetOption( FB_COMPOPT_CPUTYPE, cputype )

		#ifndef ENABLE_STANDALONE
			'' Normal build: Store the original -target argument
			'' for use as prefix for binutils/gcc tools, but only
			'' when cross-compiling or if it's really a GNU triplet.
			if( (os <> FB_DEFAULT_TARGET) or _
				(cputype <> FB_DEFAULT_CPUTYPE) or _
				is_gnu_triplet ) then
				fbc.target = arg
				fbc.targetprefix = fbc.target + "-"
			end if
		#endif

	case OPT_TITLE
		fbc.xbe_title = arg

	case OPT_V
		fbc.verbose = TRUE

	case OPT_VEC
		dim as integer value = any

		select case (ucase(arg))
		case "NONE", "0"
			value = FB_VECTORIZE_NONE
		case "1"
			value = FB_VECTORIZE_NORMAL
		case "2"
			value = FB_VECTORIZE_INTRATREE
		case else
			hFatalInvalidOption( arg, is_source )
		end select

		fbSetOption( FB_COMPOPT_VECTORIZE, value )

	case OPT_VERSION
		if( is_source ) then
			if( fbc.showversion = FALSE ) then
				hPrintVersion( fbc.verbose )
			end if
		end if
		fbc.showversion = TRUE

	case OPT_W
		dim as integer value = FB_WARNINGMSGS_LOWEST_LEVEL - 1

		select case (arg)
		case "all"
			value = FB_WARNINGMSGS_LOWEST_LEVEL

		case "none"
			value = FB_WARNINGMSGS_HIGHEST_LEVEL + 1

		case "param"
			fbSetOption( FB_COMPOPT_PEDANTICCHK, _
				fbGetOption( FB_COMPOPT_PEDANTICCHK ) or FB_PDCHECK_PARAMMODE )

		case "escape"
			fbSetOption( FB_COMPOPT_PEDANTICCHK, _
				fbGetOption( FB_COMPOPT_PEDANTICCHK ) or FB_PDCHECK_ESCSEQ )

		case "next"
			fbSetOption( FB_COMPOPT_PEDANTICCHK, _
				fbGetOption( FB_COMPOPT_PEDANTICCHK ) or FB_PDCHECK_NEXTVAR )

		case "signedness"
			fbSetOption( FB_COMPOPT_PEDANTICCHK, _
				fbGetOption( FB_COMPOPT_PEDANTICCHK ) or FB_PDCHECK_SIGNEDNESS )

		case "constness"
			fbSetOption( FB_COMPOPT_PEDANTICCHK, _
						fbGetOption( FB_COMPOPT_PEDANTICCHK ) or FB_PDCHECK_CONSTNESS )
			value = FB_WARNINGMSGS_LOWEST_LEVEL

		case "funcptr"
			fbSetOption( FB_COMPOPT_PEDANTICCHK, _
				fbGetOption( FB_COMPOPT_PEDANTICCHK ) or FB_PDCHECK_CASTFUNCPTR )
			value = FB_WARNINGMSGS_LOWEST_LEVEL

		case "suffix"
			fbSetOption( FB_COMPOPT_PEDANTICCHK, _
				fbGetOption( FB_COMPOPT_PEDANTICCHK ) or FB_PDCHECK_SUFFIX )

		case "pedantic"
			fbSetOption( FB_COMPOPT_PEDANTICCHK, FB_PDCHECK_DEFAULT )
			if( value > FB_WARNINGMSGS_DEFAULT_LEVEL ) then
				value = FB_WARNINGMSGS_DEFAULT_LEVEL
			end if

		case "error"
			fbSetOption( FB_COMPOPT_PEDANTICCHK, _
				fbGetOption( FB_COMPOPT_PEDANTICCHK ) or FB_PDCHECK_ERROR )

		case "upcast"
			fbSetOption( FB_COMPOPT_PEDANTICCHK, _
				fbGetOption( FB_COMPOPT_PEDANTICCHK ) or FB_PDCHECK_UPCAST )

		case else
			value = clng( arg )
		end select

		if( value >= FB_WARNINGMSGS_LOWEST_LEVEL ) then
			fbSetOption( FB_COMPOPT_WARNINGLEVEL, value )
		end if

	case OPT_WA
		fbc.extopt.gas += " " + hReplace( arg, ",", " " ) + " "

	case OPT_WC
		fbc.extopt.gcc += " " + hReplace( arg, ",", " " ) + " "

	case OPT_WL
		fbc.extopt.ld += " " + hReplace( arg, ",", " " ) + " "

	case OPT_X
		fbc.outname = arg

	case OPT_Z
		select case( lcase( arg ) )
		case "gosub-setjmp"
			fbSetOption( FB_COMPOPT_GOSUBSETJMP, TRUE )
		case "valist-as-ptr"
			fbSetOption( FB_COMPOPT_VALISTASPTR, TRUE )
		case "no-thiscall"
			fbSetOption( FB_COMPOPT_NOTHISCALL, TRUE )
		case "no-fastcall"
			fbSetOption( FB_COMPOPT_NOFASTCALL, TRUE )
		case "fbrt"
			fbSetOption( FB_COMPOPT_FBRT, TRUE )
		case "nocmdline"
			fbSetOption( FB_COMPOPT_NOCMDLINE, TRUE )
		case "retinflts"
			fbSetOption( FB_COMPOPT_RETURNINFLTS, TRUE )
		case "nobuiltins"
			fbSetOption( FB_COMPOPT_NOBUILTINS, TRUE )
		case "optabstract"
			fbSetOption( FB_COMPOPT_OPTABSTRACT, TRUE )
		case else
			hFatalInvalidOption( arg, is_source )
		end select

	end select
end sub

private function parseOption(byval opt as zstring ptr) as integer
	#macro CHECK(opttext, optid)
		if (*opt = opttext) then
			return optid
		end if
	#endmacro

	#macro ONECHAR(optid)
		if (cptr(ubyte ptr, opt)[1] = 0) then
			return optid
		end if
	#endmacro

	select case as const (cptr(ubyte ptr, opt)[0])
	case asc("a")
		ONECHAR(OPT_A)
		CHECK("arch", OPT_ARCH)
		CHECK("asm", OPT_ASM)

	case asc("b")
		ONECHAR(OPT_B)
		CHECK("buildprefix", OPT_BUILDPREFIX)

	case asc("c")
		ONECHAR(OPT_C)

	case asc("C")
		ONECHAR(OPT_CKEEPOBJ)

	case asc("d")
		ONECHAR(OPT_D)
		CHECK("dll", OPT_DLL)
		CHECK("dylib", OPT_DYLIB)

	case asc("e")
		ONECHAR(OPT_E)
		CHECK("ex", OPT_EX)
		CHECK("earray", OPT_EARRAY)
		CHECK("earraydims", OPT_EARRAYDIMS)
		CHECK("eassert", OPT_EASSERT)
		CHECK("edebug", OPT_EDEBUG)
		CHECK("edebuginfo", OPT_EDEBUGINFO)
		CHECK("elocation", OPT_ELOCATION)
		CHECK("enullptr", OPT_ENULLPTR)
		CHECK("eunwind", OPT_EUNWIND)
		CHECK("entry", OPT_ENTRY)
		CHECK("exx", OPT_EXX)
		CHECK("export", OPT_EXPORT)

	case asc("f")
		CHECK("fbgfx", OPT_FBGFX)
		CHECK("forcelang", OPT_FORCELANG)
		CHECK("fpmode", OPT_FPMODE)
		CHECK("fpu", OPT_FPU)

	case asc("g")
		ONECHAR(OPT_G)
		CHECK("gen", OPT_GEN)

	case asc( "h" )
		CHECK( "help", OPT_HELP )

	case asc("i")
		ONECHAR(OPT_I)
		CHECK("include", OPT_INCLUDE)

	case asc("l")
		ONECHAR(OPT_L)
		CHECK("lang", OPT_LANG)
		CHECK("lib", OPT_LIB)

	case asc("m")
		ONECHAR(OPT_M)
		CHECK("map", OPT_MAP)
		CHECK("maxerr", OPT_MAXERR)
		CHECK("mt", OPT_MT)

	case asc("n")
		CHECK("noerrline", OPT_NOERRLINE)
		CHECK("nodeflibs", OPT_NODEFLIBS)
		CHECK("nolib", OPT_NOLIB)
		CHECK("noobjinfo", OPT_NOOBJINFO)
		CHECK("nostrip", OPT_NOSTRIP)

	case asc("o")
		ONECHAR(OPT_O)

	case asc("O")
		ONECHAR(OPT_OPTIMIZE)

	case asc("p")
		ONECHAR(OPT_P)
		CHECK("pic", OPT_PIC)
		CHECK("pp", OPT_PP)
		CHECK("prefix", OPT_PREFIX)
		CHECK("print", OPT_PRINT)
		CHECK("profile", OPT_PROFILE)
		CHECK("profgen", OPT_PROFGEN)

	case asc("r")
		ONECHAR(OPT_R)
		CHECK("rr", OPT_RR)

	case asc("R")
		ONECHAR(OPT_RKEEPASM)
		CHECK("RR", OPT_RRKEEPASM)

	case asc("s")
		ONECHAR(OPT_S)
		CHECK("showincludes", OPT_SHOWINCLUDES)
		CHECK("static", OPT_STATIC)
		CHECK("strip", OPT_STRIP)
		CHECK("sysroot", OPT_SYSROOT)

	case asc("t")
		ONECHAR(OPT_T)
		CHECK("target", OPT_TARGET)
		CHECK("title", OPT_TITLE)

	case asc("v")
		ONECHAR(OPT_V)
		CHECK("vec", OPT_VEC)
		CHECK("version", OPT_VERSION)

	case asc("w")
		ONECHAR(OPT_W)

	case asc("W")
		CHECK("Wa", OPT_WA)
		CHECK("Wl", OPT_WL)
		CHECK("Wc", OPT_WC)

	case asc("x")
		ONECHAR(OPT_X)

	case asc("z")
		ONECHAR(OPT_Z)

	case asc( "-" )
		CHECK( "-version", OPT_VERSION )
		CHECK( "-help", OPT_HELP )

	end select

	return -1
end function

declare sub parseArgsFromFile _
	( _
		byref filename as string, _
		byval is_source as integer _
	)

private sub handleArg _
	( _
		byref arg as string, _
		byval is_source as integer, _
		byval is_file as integer _
	)
	'' If the previous option wants this argument as parameter,
	'' call the handler with it, now that it's known.
	'' Note: Anything is accepted, even if it starts with '-' or '@'.
	if( fbc.optid >= 0 ) then
		'' Complain about empty next argument
		if (len(arg) = 0) then
			hFatalInvalidOption( arg, is_source )
		end if

		handleOpt( fbc.optid, arg, is_source )
		fbc.optid = -1
		return
	end if

	if (len(arg) = 0) then
		'' Ignore empty argument
		return
	end if

	select case (arg[0])
	case asc("-")
		dim as zstring ptr opt = strptr(arg) + 1

		'' Complain about '-' only
		if (cptr(ubyte ptr, opt)[0] = 0) then
			'' Incomplete command line option
			hFatalInvalidOption( arg, is_source )
		end if

		'' Parse the option after the '-'
		dim as integer optid = parseOption(opt)
		if (optid < 0) then
			'' Unrecognized command line option
			hFatalInvalidOption( arg, is_source )
		end if

		'' Are we in source and option not allowed in source?
		if( is_source ) then
			if( not cmdlineOptionTB( optid ).allowed_in_source ) then
				hFatalInvalidOption( arg, is_source )
			endif
		end if

		'' Does this option take a parameter?
		if( cmdlineOptionTB( optid ).takes_argument ) then
			'' Delay handling it, until the next argument is known.
			fbc.optid = optid
		else
			'' Handle this option now
			handleOpt( optid, arg, is_source )
		end if

		'' even if the handling of the option is delayed, check the restart options here
		if( is_source ) then
			if( cmdlineOptionTB( optid ).parser_restart ) then
				fbRestartBeginRequest( FB_RESTART_PARSER_CMDLINE )
			end if

			if( cmdlineOptionTB( optid ).fbc_restart ) then
				fbRestartBeginRequest( FB_RESTART_FBC_CMDLINE )
			end if
		end if

	case asc("@")
		'' Maximum nesting/recursion level
		const MAX_LEVELS = 128
		static as integer reclevel = 0

		if (reclevel > MAX_LEVELS) then
			'' Options file nesting level too deep (recursion?)
			errReportEx( FB_ERRMSG_RECLEVELTOODEEP, arg, -1 )
			fbcEnd(1)
		end if

		'' Cut off the '@' at the front to get just the file name
		arg = right(arg, len(arg) - 1)

		'' Complain about '@' only
		if (len(arg) = 0) then
			'' Missing file name after '@'
			hFatalInvalidOption( arg, is_source )
		end if

		'' Recursively read in the additional options from the file
		reclevel += 1
		parseArgsFromFile( arg, is_source )
		reclevel -= 1

	case else
		'' Input file, get its extension to determine what it is
		dim as string ext = hGetFileExt(arg)

		#if defined(__FB_WIN32__) or _
			defined(__FB_DOS__) or _
			defined(__FB_CYGWIN__)
			'' For case in-sensitive file systems
			ext = lcase(ext)
		#endif

		select case (ext)
		case "bas"
			hAddBas( arg )

		case "o"
			fbcAddObj( arg )

		case "a"
			strlistAppend( @fbc.libfiles, arg )

		case "rc", "res"
			hSetIofile( listNewNode( @fbc.rcs ), arg, TRUE )

		case "xpm"
			'' Can have only one .xpm, or the fb_program_icon
			'' symbol will be duplicated
			if( len( fbc.xpm.srcfile ) > 0 ) then
				hFatalInvalidOption( arg, is_source )
			end if

			hSetIofile( @fbc.xpm, arg, TRUE )

		case else
			'' Input file without or with unknown extension
			hFatalInvalidOption( arg, is_source )

		end select
	end select
end sub

sub fbcParseArgsFromString _
	( _
		byval args_in as zstring ptr, _
		byval is_source as integer, _
		byval is_file as integer _
	)

	dim as string args = *args_in
	dim as string arg

	'' Parse the line containing command line arguments,
	'' separated by spaces. Double- and single-quoted strings
	'' are handled too, but nothing else.
	do
		dim as integer length = len(args)
		if (length = 0) then
			exit do
		end if

		dim as integer i = 0
		dim as integer quotech = 0

		while (i < length)
			dim as integer ch = args[i]

			select case as const (ch)
			case asc(" ")
				if (quotech = 0) then
					exit while
				end if

			case asc(""""), asc("'")
				if (quotech = ch) then
					'' String closed
					quotech = 0
				elseif (quotech = 0) then
					'' String opened
					quotech = ch
				end if

			end select

			i += 1
		wend

		if (i = 0) then
			'' Just space, skip it
			i = 1
		else
			arg = left(args, i)
			arg = trim(arg)
			arg = strUnquote(arg)
			handleArg( arg, is_source, is_file )
		end if

		args = right(args, length - i)
	loop

end sub

private sub parseArgsFromFile _
	( _
		byref filename as string, _
		byval is_source as integer _
	)
	dim as integer f = freefile()
	if (open(filename, for input, as #f)) then
		errReportEx( FB_ERRMSG_FILEACCESSERROR, filename, -1 )
		fbcEnd(1)
	end if

	dim as string args

	while (eof(f) = FALSE)
		line input #f, args
		args = trim(args)
		fbcParseArgsFromString( strptr( args ), is_source, TRUE )
	wend

	close #f
end sub

'' Whether a target needs shared libraries to be built with PIC.
'' (Note: Android 5.0+ also need executables to be built with PIC (gcc -pie argument),
'' but Android <4.1 didn't support PIE executables. We assume 4.1+.)
private function hTargetNeedsPIC( ) as integer
	function = FALSE
	if( fbGetOption( FB_COMPOPT_TARGET ) = FB_COMPTARGET_OPENBSD ) then
		function = TRUE
		exit function
	end if
	if( fbGetCpuFamily( ) <> FB_CPUFAMILY_X86 ) then
		select case as const( fbGetOption( FB_COMPOPT_TARGET ) )
		case FB_COMPTARGET_LINUX, FB_COMPTARGET_FREEBSD, _
		     FB_COMPTARGET_NETBSD, _
		     FB_COMPTARGET_DRAGONFLY, FB_COMPTARGET_SOLARIS, _
		     FB_COMPTARGET_ILLUMOS, _
		     FB_COMPTARGET_ANDROID
			function = TRUE
		end select
	else
		'' On android-x86, PIC is necessary even to access globals in dynamic
		'' libraries, because the runtime linker doesn't support usual relocation types.
		'' GCC defaults to -fPIC anyway, but we need to be aware of whether PIC is used.
		if( fbGetOption( FB_COMPOPT_TARGET ) = FB_COMPTARGET_ANDROID ) then
			function = TRUE
		end if
	end if
end function

private sub hParseArgs( byval argc as integer, byval argv as zstring ptr ptr )
	fbc.optid = -1

	'' Note: ignoring argv[0], assuming it's the path used to run fbc
	dim as string arg
	for i as integer = 1 to (argc - 1)
		arg = *argv[i]
		handleArg( arg, FALSE, FALSE )
	next

	'' Waiting for argument to an option? If the user did something like
	'' 'fbc foo.bas -o' this shows the error.
	if (fbc.optid >= 0) then
		'' Missing argument for command line option
		hFatalInvalidOption( *argv[argc - 1], FALSE )
	end if
end sub

private sub hCheckArgs()
	'' In case there was an '-o <file>', but no corresponding input file,
	'' this will report the error.
	hCheckWaitingObjfile( )

	''
	'' Check for incompatible options etc.
	''
	select case( fbGetOption( FB_COMPOPT_FPUTYPE ) )
	case FB_FPUTYPE_FPU
		if( fbGetOption( FB_COMPOPT_VECTORIZE ) >= FB_VECTORIZE_NORMAL ) then
			errReportEx( FB_ERRMSG_OPTIONREQUIRESSSE, "", -1 )
			fbcEnd( 1 )
		end if
	case FB_FPUTYPE_SSE
		if( (fbGetCpuFamily( ) <> FB_CPUFAMILY_X86) and _
		    (fbGetCpuFamily( ) <> FB_CPUFAMILY_X86_64) ) then
			errReportEx( FB_ERRMSG_SSEREQUIRESX86, "", -1 )
			fbcEnd( 1 )
		end if
	case FB_FPUTYPE_NEON
		if( (fbGetCpuFamily( ) <> FB_CPUFAMILY_ARM) and _
		    (fbGetCpuFamily( ) <> FB_CPUFAMILY_AARCH64) ) then
			errReportEx( FB_ERRMSG_NEONREQUIRESARM, "", -1 )
			fbcEnd( 1 )
		end if
	end select

	'' 1. The compiler (fb.bas) starts with default target settings for
	''    native compilation.

	'' 2. -target option handling has already switched the target if given.

	'' 3. -arch overrides any other arch settings.
	if( fbc.cputype >= 0 ) then
		fbSetOption( FB_COMPOPT_CPUTYPE, fbc.cputype )
	end if

	'' NEON implies at least armv7-a
	if( (fbGetOption( FB_COMPOPT_FPUTYPE ) = FB_FPUTYPE_NEON) and _
	    (fbGetOption( FB_COMPOPT_CPUTYPE ) < FB_CPUTYPE_ARMV7A) ) then
		fbSetOption( FB_COMPOPT_CPUTYPE, FB_CPUTYPE_ARMV7A )
	end if

	'' 4. Check for target/arch conflicts, e.g. dos and non-x86
	if( (fbGetOption( FB_COMPOPT_TARGET ) = FB_COMPTARGET_DOS) and _
		(fbGetCpuFamily( ) <> FB_CPUFAMILY_X86) ) then
		errReportEx( FB_ERRMSG_DOSWITHNONX86, fbGetFbcArch( ), -1 )
		fbcEnd( 1 )
	end if

	if( (fbGetOption( FB_COMPOPT_TARGET ) = FB_COMPTARGET_DOS) and _
		fbGetOption( FB_COMPOPT_MULTITHREADED ) ) then
		errReportEx( FB_ERRMSG_INVALIDCMDOPTION, "-mt", -1 )
		fbcEnd( 1 )
	end if

	if( (fbGetOption( FB_COMPOPT_TARGET ) = FB_COMPTARGET_JS) and _
		fbGetOption( FB_COMPOPT_MULTITHREADED ) ) then
		errReportEx( FB_ERRMSG_INVALIDCMDOPTION, "-mt", -1 )
		fbcEnd( 1 )
	end if

	if( fbGetOption( FB_COMPOPT_TARGET ) = FB_COMPTARGET_XBOX ) then
		'' The nxdk Xbox package uses worker-backed graphics and sound
		'' services.  Prefer the thread-safe runtime by default so Xbox
		'' programs do not need to pass -mt just to use the normal package
		'' backends safely.
		fbSetOption( FB_COMPOPT_MULTITHREADED, TRUE )
		fbc.objinf.mt = TRUE
	end if

	'' 4.5. Enable -pic automatically when building a Unix shared library,
	''      an OpenBSD executable, or an Android executable.
	if( (fbGetOption( FB_COMPOPT_OUTTYPE ) = FB_OUTTYPE_DYNAMICLIB) or _
	    (fbGetOption( FB_COMPOPT_TARGET ) = FB_COMPTARGET_OPENBSD) or _
	    (fbGetOption( FB_COMPOPT_TARGET ) = FB_COMPTARGET_ANDROID) ) then
		if( hTargetNeedsPIC( ) ) then
			fbSetOption( FB_COMPOPT_PIC, TRUE )
		end if
	end if

	'' Complain if -pic was given in cases where it's not needed/supported
	if( fbGetOption( FB_COMPOPT_PIC ) ) then
		if( hTargetNeedsPIC( ) = FALSE ) then
			errReportEx( FB_ERRMSG_PICNOTSUPPORTEDFORTARGET, "", -1 )
		end if
	end if

	'' 5. Select default backend based on selected arch, e.g. when compiling
	''    for x86-64 or ARM, we shouldn't default to -gen gas anymore (as
	''    long as it doesn't support it).
	''
	'' This should be done no matter whether compiling for the native system
	'' or cross-compiling. Even on a 64bit x86_64 host where
	'' FB_DEFAULT_BACKEND is -gen gcc, we still prefer using -gen gas when
	'' cross-compiling to 32bit x86.
	'' (Apple gas assembler has such broken support for intel syntax
	'' (see https://discussions.apple.com/message/10163960#10163960)
	'' that it can't work for non-trivial programs, so default to -gen gcc.)
	if( fbGetOption( FB_COMPOPT_TARGET ) = FB_COMPTARGET_XBOX ) then
		'' nxdk is clang/LLVM based and does not provide the old xbox-as
		'' toolchain expected by the GAS backend.
		fbSetOption( FB_COMPOPT_BACKEND, FB_BACKEND_CLANG )
	elseif( (fbGetOption( FB_COMPOPT_TARGET ) = FB_COMPTARGET_WIN32) and _
		(fbGetCpuFamily( ) = FB_CPUFAMILY_AARCH64) ) then
		'' Windows ARM64 toolchains are LLVM/Clang based in the supported
		'' MSYS2 environment.  Prefer clang so assembly is handled by the
		'' compiler driver instead of assuming a GNU as driver exists.
		fbSetOption( FB_COMPOPT_BACKEND, FB_BACKEND_CLANG )
	elseif( (fbGetCpuFamily( ) = FB_CPUFAMILY_X86) and _
		(fbGetOption(FB_COMPOPT_TARGET) <> FB_COMPTARGET_DARWIN) ) then
		fbSetOption( FB_COMPOPT_BACKEND, FB_BACKEND_GAS )
	else
		fbSetOption( FB_COMPOPT_BACKEND, FB_BACKEND_GCC )
	end if

	'' gas doesn't currently support PIC
	if( (fbGetOption( FB_COMPOPT_BACKEND ) = FB_BACKEND_GAS) and _
	    fbGetOption( FB_COMPOPT_PIC ) ) then
		fbSetOption( FB_COMPOPT_BACKEND, FB_BACKEND_GCC )
	end if

	'' 6. -gen overrides any other backend setting.
	if( fbc.backend >= 0 ) then
		fbSetOption( FB_COMPOPT_BACKEND, fbc.backend )
	end if

	'' 7. Check whether backend supports the target/arch.
	'' -gen gas with non-x86 arch or with PIC isn't possible.
	'' -gen gas64 with non-x86_64 or with PIC isn't possible.
	if( ((fbGetOption( FB_COMPOPT_BACKEND ) = FB_BACKEND_GAS) and _
	    (fbGetCpuFamily( ) <> FB_CPUFAMILY_X86)) or _
	    ((fbGetOption( FB_COMPOPT_BACKEND ) = FB_BACKEND_GAS64) and _
	    (fbGetCpuFamily( ) <> FB_CPUFAMILY_X86_64)) ) then
		errReportEx( FB_ERRMSG_GENGASWITHNONX86, fbGetFbcArch( ), -1 )
		fbcEnd( 1 )
	end if

	if( (fbGetOption( FB_COMPOPT_BACKEND ) = FB_BACKEND_GAS) and _
	    fbGetOption( FB_COMPOPT_PIC ) ) then
		errReportEx( FB_ERRMSG_GENGASWITHPIC, "", -1 )
		fbcEnd( 1 )
	end if

	'' Resource scripts are only allowed for win32 & co,
	select case as const (fbGetOption(FB_COMPOPT_TARGET))
	case FB_COMPTARGET_WIN32, FB_COMPTARGET_CYGWIN, FB_COMPTARGET_XBOX

	case else
		dim as FBCIOFILE ptr rc = listGetHead(@fbc.rcs)
		if (rc) then
			errReportEx(FB_ERRMSG_RCFILEWRONGTARGET, rc->srcfile, -1)
			fbcEnd(1)
		end if
	end select

	'' The embedded .xpm is only useful for the X11 gfxlib
	select case as const (fbGetOption(FB_COMPOPT_TARGET))
	case FB_COMPTARGET_LINUX, FB_COMPTARGET_DARWIN, _
		FB_COMPTARGET_FREEBSD, FB_COMPTARGET_OPENBSD, _
		FB_COMPTARGET_NETBSD, FB_COMPTARGET_DRAGONFLY, _
		FB_COMPTARGET_SOLARIS, FB_COMPTARGET_ILLUMOS

	case else
		if (len(fbc.xpm.srcfile) > 0) then
			errReportEx(FB_ERRMSG_RCFILEWRONGTARGET, fbc.xpm.srcfile, -1)
			fbcEnd(1)
		end if
	end select

	'' On darwin need to change the default asm syntax when using gen gcc because
	'' most C compilers on OSX seem to be configured without intel syntax support;
	'' probably because Apple as and llvm-mc have horribly broken intel support.
	if( (fbGetOption( FB_COMPOPT_TARGET ) = FB_COMPTARGET_DARWIN) and _
		(fbGetOption( FB_COMPOPT_BACKEND ) <> FB_BACKEND_GAS) ) then
		fbSetOption( FB_COMPOPT_ASMSYNTAX, FB_ASMSYNTAX_ATT )
	end if

	if( fbc.asmsyntax >= 0 ) then
		'' -asm only applies to x86 and x86_64
		select case( fbGetCpuFamily( ) )
		case FB_CPUFAMILY_X86, FB_CPUFAMILY_X86_64
		case else
			errReportEx( FB_ERRMSG_ASMOPTIONGIVENFORNONX86, fbGetTargetId( ), -1 )
		end select

		'' -gen gas only supports -asm intel
		select case fbGetOption( FB_COMPOPT_BACKEND )
		case FB_BACKEND_GAS, FB_BACKEND_GAS64
			if( fbc.asmsyntax <> FB_ASMSYNTAX_INTEL ) then
				errReportEx( FB_ERRMSG_GENGASWITHOUTINTEL, "", -1 )
			end if
		end select

		'' -asm overrides the target's default
		fbSetOption( FB_COMPOPT_ASMSYNTAX, fbc.asmsyntax )
	end if

	'' Update the stacksize for the current target options if
	'' stacksize was never set yet by passing a negative stacksize
	fbSetOption( FB_COMPOPT_STACKSIZE, -1 )

	'' TODO: Check whether subsystem/stacksize/xboxtitle were set and
	'' complain about it when the target doesn't allow it, or just
	'' ignore silently (that might not even be too bad for portability)?
end sub

'' Determine base/prefix path
private sub fbcDeterminePrefix( )
	'' Not already set from -prefix command line option?
	if( len( fbc.prefix ) = 0 ) then
		'' Then default to exepath() or the hard-coded prefix
		#ifdef ENABLE_PREFIX
			fbc.prefix = ENABLE_PREFIX + FB_HOST_PATHDIV
		#else
			fbc.prefix = pathStripDiv( exepath( ) ) + FB_HOST_PATHDIV
			#ifndef ENABLE_STANDALONE
				'' Non-standalone fbc is in prefix/bin,
				'' just add '..' to get to prefix
				fbc.prefix += ".." + FB_HOST_PATHDIV
			#endif
		#endif
	else
		fbc.prefix = pathStripDiv( fbc.prefix ) + FB_HOST_PATHDIV
	end if

	fbc.prefix = pathStripDiv( pathNormalizeHost( fbc.prefix ) ) + FB_HOST_PATHDIV
end sub

private sub fbcSetupCompilerPaths( )
	''
	'' Standalone (classic FB):
	''
	''    bin/os[-arch]/
	''    inc/
	''    lib/os[-arch]/
	''
	'' Normal (unix-style):
	''
	''    bin/[target-]
	''    include/freebasic[suffix]/
	''    lib/freebasic[suffix]/{target | os[-arch]}/
	''
	'' x86 standalone traditionally uses the win32/dos/linux subdirs in bin/
	'' and lib/, named after the target OS. For other architectures, the
	'' arch name needs to be added to distinguish the subdir from the x86
	'' version. (especially for cross-compiling)
	''
	'' Normal has additional support for gcc targets (e.g. i686-pc-mingw32),
	'' which have to be prefixed to the executable names of cross-compiling
	'' tools in the bin/ directory (e.g. bin/i686-pc-mingw32-ld). However,
	'' for native compilation, no target is prefixed to bin/ tools at all.
	''
	'' Normal uses include/freebasic/ and lib/freebasic/ to hold FB includes
	'' and libraries, to stay out of the way of the C ones in include/ and
	'' lib/ and to conform to Linux distro packaging standards.
	''
	'' With ENABLE_LIB64, we put 64bit libs into
	''    lib64/freebasic/<target>/
	'' instead of the default
	''    lib/freebasic/<target>/
	'' lib/ is then used for 32bit libs only. This is useful for some Linux
	'' distros which use lib/ and lib64/ this way.
	''
	'' - The paths are not terminated with [back]slashes here,
	''   except for the bin/ path. fbcFindBin() expects to only have to
	''   append the file name, for example:
	''     "prefix/bin/win32/" + "as.exe"
	''     "prefix/bin/" + "ld"
	''     "prefix/bin/i686-w64-mingw32-" + "ld"
	''
	''
	'' NOTE: Android armeabi and armeabi-v7a ABIs have the same FB target id,
	'' arm-android.  We could have a separate target id called armv7a-android,
	'' but the improvements to performance of libfb would be pretty minor
	'' anyway (there is very little use of floating point), so it's simpler to
	'' just use libraries built for armeabi for both abis. libfbgfx and other
	'' libraries would be a different matter, so in future maybe change this.

	dim as string targetid = fbGetTargetId( )

	#if defined( __FB_OPENBSD__ )
		#ifndef ENABLE_STANDALONE
			if( (fbGetOption( FB_COMPOPT_TARGET ) = FB_COMPTARGET_OPENBSD) and _
				(len( fbc.targetprefix ) = 0) and _
				(len( fbc.buildprefix ) = 0) ) then
				fbctoolTB(FBCTOOL_GCC).name = "egcc"
			end if
		#endif
	#endif

#ifdef ENABLE_STANDALONE
	'' Use default target name
	fbc.binpath = fbc.prefix + "bin" + FB_HOST_PATHDIV + targetid + FB_HOST_PATHDIV
	fbc.incpath = fbc.prefix + "inc"
	fbc.libpath = fbc.prefix + "lib" + FB_HOST_PATHDIV + targetid
#else
	dim as string fbname
	#ifdef __FB_DOS__
		'' Our subdirectory in include/ and lib/ is usually called
		'' freebasic/, but on DOS that's too long... of course almost
		'' no targetid or suffix can be used either.
		fbname = "freebas"
	#else
		fbname = "freebasic"
	#endif
	#ifdef ENABLE_SUFFIX
		fbname += ENABLE_SUFFIX
	#endif

	dim libdirname as string = "lib"
	#ifdef ENABLE_LIB64
		if( fbIs64Bit( ) ) then
			libdirname = "lib64"
		end if
	#endif

	if( len(fbc.buildprefix) > 0 ) then
		fbc.binpath = fbc.prefix + "bin"     + FB_HOST_PATHDIV + fbc.buildprefix
	else
		fbc.binpath = fbc.prefix + "bin"     + FB_HOST_PATHDIV + fbc.targetprefix
	end if

	#if defined( __FB_HAIKU__ )
		if( (fbIs64Bit( ) = FALSE) and _
			(fbGetOption( FB_COMPOPT_TARGET ) = FB_COMPTARGET_HAIKU) and _
			(len( fbc.buildprefix ) = 0) and _
			(len( fbc.targetprefix ) = 0) ) then
			dim as string secondarybin = fbc.prefix + "develop" + _
				FB_HOST_PATHDIV + "tools" + FB_HOST_PATHDIV + _
				"x86" + FB_HOST_PATHDIV + "bin" + FB_HOST_PATHDIV
			if( hFileExists( secondarybin + fbctoolTB(FBCTOOL_LD).name + FB_HOST_EXEEXT ) ) then
				fbc.binpath = secondarybin
			end if
		end if
	#endif

	''
	'' Haiku keeps development headers in /develop/headers instead of the
	'' more typical /include tree used by the other hosted Unix targets.
	''
	'' Keep the compiler's built-in default include path aligned with the
	'' install layout from mk/layout.mk, otherwise installed core headers
	'' such as fbgfx.bi, crt.bi, vbcompat.bi, and fbthread.bi appear to be
	'' missing even though the package installed them correctly.
	''
	#ifdef __FB_HAIKU__
		fbc.incpath = fbc.prefix + "develop" + FB_HOST_PATHDIV + "headers" + FB_HOST_PATHDIV + fbname
	#else
		fbc.incpath = fbc.prefix + "include" + FB_HOST_PATHDIV + fbname
	#endif
	fbc.libpath = fbc.prefix + libdirname + FB_HOST_PATHDIV + fbname + FB_HOST_PATHDIV + targetid
#endif
end sub

private sub fbcPrintTargetInfo( )
	var s = fbGetTargetId( )
	s += ", " + *fbGetFbcArch( )
	s += ", " & fbGetBits( ) & "bit"
	#ifndef ENABLE_STANDALONE
		if( len( fbc.target ) > 0 ) then
			s += " (" + fbc.target + ")"
		end if
	#endif
	print "target:", s
	print "backend:", fbGetBackendName( fbGetOption( FB_COMPOPT_BACKEND ) )
end sub

private sub fbcDetermineMainName( )
	'' Determine the main module path/name if not given via -m
	if (len(fbc.mainname) = 0) then
		'' 1) First input .bas module
		dim as FBCIOFILE ptr m = listGetHead( @fbc.modules )
		if( m ) then
			fbc.mainname = m->srcfile
		else
			'' 2) First input .o
			dim as string ptr objf = listGetHead( @fbc.objlist )
			if( objf <> NULL ) then
				fbc.mainname = *objf
			else
				'' 3) Neither input .bas nor .o, that is rare,
				'' but happens in this case:
				''      $ fbc a.bas -lib -m a
				''      $ fbc b.bas -lib
				''      $ fbc -l a -l b
				'' Usually -x is used too though, so this
				'' fallback name won't be seen often.
				'' This name should be 8.3 compatible (for DOS)
				fbc.mainname = "unnamed"
			end if
		end if
		fbc.mainname = hStripExt(fbc.mainname)
	end if
end sub

private function hGetTempFileTag( ) as string
	static as string tag

	if( len( tag ) = 0 ) then
		''
		'' Intermediate backend files are deleted automatically unless the user
		'' asked to keep them.  Keep their names away from normal source names
		'' such as foo.c so a compile in the source directory cannot overwrite
		'' and then delete an existing file.
		''
		tag = ".fbc-" + hex( cuint( timer( ) * 1000.0 ), 8 )
	end if

	function = tag
end function

private function hCompileStage2DirectlyToObj( ) as integer
	select case( fbGetOption( FB_COMPOPT_TARGET ) )
	case FB_COMPTARGET_JS, FB_COMPTARGET_XBOX
		function = TRUE
	case FB_COMPTARGET_WIN32
		if( ((fbGetOption( FB_COMPOPT_BACKEND ) = FB_BACKEND_CLANG) or _
		     (fbGetOption( FB_COMPOPT_BACKEND ) = FB_BACKEND_GCC)) and _
		    (fbGetCpuFamily( ) = FB_CPUFAMILY_AARCH64) ) then
			''
			'' The Windows ARM64 toolchain is clang/LLVM based.  Let the C
			'' compiler produce COFF objects directly instead of sending C
			'' backend assembly through a GNU as style stage.
			''
			function = TRUE
		end if
	case else
		function = FALSE
	end select
end function

'' Build the intermediate file name for the given module and step
private function hGetAsmName _
	( _
		byval module as FBCIOFILE ptr, _
		byval stage as integer _
	) as string

	dim as zstring ptr ext = any
	dim as string asmfile

	'' Based on the objfile name so it's also affected by -o
	asmfile = hStripExt( *module->objfile )

	if( stage = 1 ) then
		if( (fbc.keepasm = FALSE) and (fbc.emitasmonly = FALSE) ) then
			asmfile += hGetTempFileTag( )
		end if
	elseif( hCompileStage2DirectlyToObj( ) = FALSE ) then
		if( (fbc.keepfinalasm = FALSE) and (fbc.emitfinalasmonly = FALSE) ) then
			asmfile += hGetTempFileTag( )
		end if
	end if

	if( hCompileStage2DirectlyToObj( ) = FALSE ) then
		ext = @".asm"
	else
		ext = @".o"
	end if
	if( stage = 1 ) then
		select case( fbGetOption( FB_COMPOPT_BACKEND ) )
		case FB_BACKEND_GCC, FB_BACKEND_CLANG
			ext = @".c"
		case FB_BACKEND_LLVM
			ext = @".ll"
		end select
	end if

	asmfile += *ext

	function = asmfile
end function

private sub hCompileBas _
	( _
		byval module as FBCIOFILE ptr, _
		byval is_main as integer, _
		byval is_fbctinf as integer, _
		byval module_count as integer _
	)

	dim as integer prevlang = any, prevouttype = any
	dim as string asmfile, pponlyfile

	asmfile = hGetAsmName( module, 1 )

	'' -pp?
	if( fbGetOption( FB_COMPOPT_PPONLY ) ) then
		'' Re-use the full -o path/filename for the -pp output file,
		'' since no .o will be generated anyways (if -o was given)
		pponlyfile = *module->objfile
		if( module->is_custom_objfile = FALSE ) then
			'' Otherwise, use a default file name
			pponlyfile = hStripExt( pponlyfile ) + ".pp.bas"
		end if
	end if

	if( fbc.verbose ) then
		print "compiling: ", module->srcfile; " -o "; asmfile;
		if( fbGetOption( FB_COMPOPT_PPONLY ) ) then
			print " -pp " + pponlyfile;
		end if
		if( is_main ) then
			print " (main module)";
		elseif( is_fbctinf ) then
			print " (FB compile-time info)";
		end if
		print
	end if

	'' Restarting with a new lang option?
	'' We need to initialize with the restart lang
	if( fbGetOption( FB_COMPOPT_RESTART_LANG ) <> FB_LANG_INVALID ) then
		fbSetOption( FB_COMPOPT_LANG, fbGetOption( FB_COMPOPT_RESTART_LANG ) )
	end if

	'' preserve orginal values that might have to restored
	'' (e.g. -lang mode could be overwritten while parsing due to #lang,
	'' but that shouldn't affect other modules)
	prevlang = fbGetOption( FB_COMPOPT_LANG )
	prevouttype = fbGetOption( FB_COMPOPT_OUTTYPE )

	if( is_fbctinf ) then
		'' Switch to -c mode temporarily to get the compiler to write objinfo
		fbSetOption( FB_COMPOPT_OUTTYPE, FB_OUTTYPE_OBJECT )
	end if

	do
		'' Clean up stage 1 output (FB backend's output, *.asm/*.c/*.ll),
		'' unless -R was given, and additionally in case of -gen gas, unless -RR
		'' was given (because for -gen gas, the FB backend's .asm output is also
		'' the final .asm which -RR is supposed to preserve).
		if( (not fbc.keepasm) and _
			(((fbGetOption( FB_COMPOPT_BACKEND ) <> FB_BACKEND_GAS) and (fbGetOption( FB_COMPOPT_BACKEND ) <> FB_BACKEND_GAS64) ) or _
			(not fbc.keepfinalasm)) ) then
			fbcAddTemp( asmfile )

		'' first module? handle side effects of #cmdline
		elseif( module_count = 1 ) then
			'' Keep the asm file.  If the keep option was in a #cmdline then
			'' the temporary file probably was already added to the fbc.temps
			'' list on the first pass in to the parser (unless real command
			'' line also had an option to keep the asm file).

			if( fbRestartGetCount() > 0 ) then
				fbcRemoveTemp( asmfile )
			end if
		end if

		'' init the parser (note: initializes env)
		fbInit( is_main, fbc.entry, module_count )

		if( is_fbctinf ) then
			'' Let the compiler know about all libs collected so far,
			'' so the fbctinf module represents all the other modules
			'' compiled/included in this fbc invocation.
			fbSetLibs( @fbc.finallibs, @fbc.finallibpaths )
		else
			'' Add only the libs and paths passed on the command line,
			'' so this module will only include objinfo for those libs
			'' and the ones found while parsing it, but not unrelated
			'' libs from other modules.
			fbSetLibs( @fbc.libs, @fbc.libpaths )
		end if

		fbCompile( module->srcfile, asmfile, pponlyfile, is_main )

		'' If there were any errors during parsing, just exit without
		'' doing anything else.
		if( errGetCount( ) > 0 ) then
			fbcEnd( 1 )
		end if

		'' Don't restart unless asked for
		if( fbShouldRestart( ) = FALSE ) then
			exit do
		end if

		'' Close the request to restart the parser
		fbRestartEndRequest( FB_RESTART_PARSER )

		'' Shutdown the parser before restarting
		fbEnd( )

		'' Still have restart set?  It must be a request to restart fbc
		if( fbShouldRestart( ) ) then
			'' Restore original #lang? only if we didn't set a new lang to restart with
			if( fbGetOption( FB_COMPOPT_RESTART_LANG ) = FB_LANG_INVALID ) then
				fbSetOption( FB_COMPOPT_LANG, prevlang )
			end if
			exit sub
		end if
	loop

	'' (unnecessary for the empty fbctinf module, it won't add anything new)
	if( is_fbctinf = FALSE ) then
		'' Update the list of libs and paths with the ones found when parsing
		fbGetLibs( @fbc.finallibs, @fbc.finallibpaths )
	end if

	'' Shutdown the parser
	fbEnd( )

	'' Restore original options
	if( is_fbctinf ) then
		fbSetOption( FB_COMPOPT_OUTTYPE, prevouttype )
	end if
	fbSetOption( FB_COMPOPT_LANG, prevlang )
end sub

private sub hCompileModules( )
	dim as integer ismain = any, checkmain = any
	dim as string mainfile
	dim as FBCIOFILE ptr module = any

	ismain = FALSE

	select case fbGetOption( FB_COMPOPT_OUTTYPE )
	case FB_OUTTYPE_EXECUTABLE, FB_OUTTYPE_DYNAMICLIB
		checkmain = TRUE
	case else
		'' When building an object or a library (-c/-r, -lib), nothing
		'' is compiled with ismain = TRUE until -m was given for it.
		'' This makes sense because -c is usually used to compile
		'' single modules of which only a very specific one is the
		'' main one (nobody would want -c to include main() everywhere),
		'' and because -lib is for making libraries which generally
		'' don't include a main module for programs to use.
		checkmain = fbc.mainset
	end select

	if( checkmain ) then
		'' Note: This causes the path given with -m to be ignored in
		'' the ismain check below. This is good because -m is easier
		'' to use that way (e.g. fbc ../../main.bas -m main), and bad
		'' because then modules with the same name but in different
		'' directories will both be seen as the main one.
		mainfile = hStripPath( fbc.mainname )
	end if

	module = listGetHead( @fbc.modules )

	if( module = NULL ) then
		'' No input .bas files to compile - make sure to add the libs
		'' from the command line to the final lists anyways.
		strsetCopy( @fbc.finallibs, @fbc.libs )
		strsetCopy( @fbc.finallibpaths, @fbc.libpaths )
		exit sub
	end if

	'' We have input .bas files to compile - hCompileBas() will take care of
	'' copying the command line libs into the final lists:
	'' 1. into the compiler
	''    (fbc.libs -> fbSetLibs() -> compiler)
	'' 2. compiler collects additional #inclibs etc...
	'' 3. and copy back into final lists
	''    (compiler -> fbGetLibs() -> fbc.finallibs)

	dim as integer module_count = 0
	do
		if( checkmain ) then
			ismain = (mainfile = hStripPath( hStripExt( module->srcfile ) ))
			'' Note: checking continues for all modules, because
			'' "the" main module could be passed multiple times,
			'' and it makes sense to always treat it the same,
			'' so that <fbc 1.bas 1.bas -c> generates the same 1.o
			'' twice and <fbc 1.bas 1.bas> causes a duplicated
			'' definition of main().
			/'checkmain = not ismain'/
		end if

		module_count += 1
		hCompileBas( module, ismain, FALSE, module_count )

		if( fbShouldRestart( ) ) then
			exit sub
		end if

		module = listGetNext( module )
	loop while( module )
end sub

private function hParseXpm _
	( _
		byref xpmfile as string, _
		byref code as string _
	) as integer

	code += !"\ndim shared as zstring ptr "
	code += "fb_program_icon_data"
	code += !"(0 to ...) = _\n{ _\n"

	dim as integer f = freefile( )
	if( open( xpmfile, for input, as #f ) ) then
		exit function
	end if

	dim as string ln

	'' Check for the header line
	line input #f, ln
	if( ucase( ln ) <> "/* XPM */" ) then
		'' Invalid XPM header
		close #f
		exit function
	end if

	'' Check for lines containing strings (color and pixel lines)
	'' Other lines (declaration line, empty lines, C comments, ...) aren't
	'' explicitely handled, but should automatically be ignored, as long as
	'' they don't contain strings.
	dim as integer saw_rows = FALSE
	while( eof( f ) = FALSE )
		line input #f, ln

		'' Strip everything in front of the first '"'
		ln = right( ln, len( ln ) - (instr( ln, """" ) - 1) )

		'' Strip everything behind the second '"'
		ln = left( ln, instr( 2, ln, """" ) )

		'' Got something left?
		if( len( ln ) > 0 ) then
			'' Add an entry to the array, in a new line,
			'' separated by a comma, if it's not the first one.
			if( saw_rows ) then
				code += !", _\n"
			end if
			code += !"\t@" + ln
			saw_rows = TRUE
		end if
	wend

	close #f

	if( saw_rows = FALSE ) then
		'' No image data found
		exit function
	end if

	'' Line break after the last entry
	code += !" _ \n"

	code += !"}\n\n"

	'' Symbol for the gfxlib
	code += !"extern as zstring ptr ptr fb_program_icon alias ""fb_program_icon""\n"
	code += "dim shared as zstring ptr ptr fb_program_icon = " & _
					!"@fb_program_icon_data(0)\n"

	function = TRUE
end function

'' Turns the .xpm icon resource into a .bas file,
'' then compiles that using the normal FB compilation process.
private function hCompileXpm( ) as integer
	dim as string xpmfile, code
	dim as integer fo = any

	if( len( fbc.xpm.srcfile ) = 0 ) then
		return TRUE
	end if

	'' Remember *.xpm file name
	xpmfile = fbc.xpm.srcfile

	'' Set *.bas name based on input file name or -o <file>:
	if( len( *fbc.xpm.objfile ) > 0 ) then
		fbc.xpm.srcfile = hStripExt( *fbc.xpm.objfile )
	end if

	'' foo.xpm -> foo.xpm.bas to avoid collision with foo.bas
	fbc.xpm.srcfile &= ".bas"

	if( fbc.verbose ) then
		print "parsing xpm: ", xpmfile & " -o " & fbc.xpm.srcfile
	end if

	if( hParseXpm( xpmfile, code ) = FALSE ) then
		'' TODO: show error message
		exit function
	end if

	fo = freefile( )
	if( open( fbc.xpm.srcfile, for output, as #fo ) ) then
		'' TODO: show error message
		exit function
	end if
	print #fo, code;
	close #fo

	'' Clean up the temp .bas if -R wasn't given
	if( fbc.keepasm = FALSE ) then
		fbcAddTemp( fbc.xpm.srcfile )
	end if

	hCompileBas( @fbc.xpm, FALSE, FALSE, -1 )
	function = TRUE
end function

private function hCompileStage2Module( byval module as FBCIOFILE ptr ) as integer
	dim as string ln, asmfile
	dim as integer directtoobj = hCompileStage2DirectlyToObj( )

	asmfile = hGetAsmName( module, 2 )
	if( directtoobj ) then
		asmfile = *module->objfile
	end if

	'' Clean up stage 2 output (the final .asm for -gen gcc/llvm) unless
	'' -RR was given.
	if( (not fbc.keepfinalasm) and _
		((directtoobj = FALSE) or _
		(not fbc.keepobj)) ) then
		fbcAddTemp( asmfile )
	end if

	select case( fbGetOption( FB_COMPOPT_BACKEND ) )
	case FB_BACKEND_GCC, FB_BACKEND_CLANG
		dim as boolean ism64target = false

		if( fbGetOption( FB_COMPOPT_BACKEND ) = FB_BACKEND_CLANG ) then
			ln += hGetClangTargetOption( )
		end if

		select case( fbGetCpuFamily( ) )
		case FB_CPUFAMILY_X86
			ln += "-m32 "
		case FB_CPUFAMILY_X86_64
			ln += "-m64 "
			ism64Target = True
		case FB_CPUFAMILY_AARCH64, FB_CPUFAMILY_PPC64, _
		     FB_CPUFAMILY_PPC64LE, FB_CPUFAMILY_RISCV64, _
		     FB_CPUFAMILY_S390X, FB_CPUFAMILY_LOONGARCH64
			ism64Target = True
		end select

		if( fbGetOption( FB_COMPOPT_TARGET ) <> FB_COMPTARGET_JS ) then
			'' GCC doesn't recognize the -march option and PowerPC combination
			'' and recommendeds the -mcpu option be used for PowerPC.
			select case fbGetCpuFamily( )
			case FB_CPUFAMILY_PPC, FB_CPUFAMILY_PPC64, FB_CPUFAMILY_PPC64LE
				if( fbc.cputype_is_native ) then
					ln += "-mcpu=native "
				else
					ln += "-mcpu=" + *fbGetGccArch( ) + " "
				end if
			case else
				if( fbc.cputype_is_native ) then
					ln += "-march=native "
				else
					ln += "-march=" + *fbGetGccArch( ) + " "
				end if
			end select

			if( fbGetCpuFamily( ) = FB_CPUFAMILY_RISCV32 ) then
				ln += "-mabi=ilp32 "
			end if
		end if

		if( (fbGetOption( FB_COMPOPT_TARGET ) = FB_COMPTARGET_ANDROID) and _
		    (fbGetOption( FB_COMPOPT_CPUTYPE ) = FB_CPUTYPE_ARMV7A) ) then
			'' The following options enforce the androideabi-v7a ABI
			'' From https://developer.android.com/ndk/guides/standalone_toolchain.html
			ln += "-mfloat-abi=softfp -mfpu=vfpv3-d16 "
		end if

		fbcDarwinPlatformAddCCompilerOptions( ln )

		if( fbGetOption( FB_COMPOPT_PIC ) ) then
			ln += "-fPIC "
		end if

		if( directtoobj = FALSE ) then

			'' Even though clang's assembly output is syntax compatible with gnu-as,
			'' clang may produce extra directives not understood by gnu-as
			if( fbGetOption( FB_COMPOPT_BACKEND ) = FB_BACKEND_CLANG ) then
				ln += "-fno-integrated-as "
			end if

			'' generate assembly
			ln += "-S "

			'' don't use any standard libraries or includes
			ln += "-nostdlib -nostdinc "

			'' enable all warnings
			ln += "-Wall "

			'' -Wno-unused-but-set-variable and the warning it suppresses were introduced
			'' in GCC 4.6. Don't pass that flag to avoid an error on earlier GCC. As a
			'' result, to disable the warning on 4.6+ need to disable all unused warnings...
			'' ln += "-Wno-unused-label -Wno-unused-function -Wno-unused-variable "
			'' ln += "-Wno-unused-but-set-variable "
			ln += "-Wno-unused "

		else
			'' Some clang-based targets do not have a useful external assembler
			'' stage here.  Compile the generated C directly to object code and
			'' skip hAssembleModule() for them.
			ln += "-c -nostdlib -nostdinc -Wall -Wno-unused-label " + _
				"-Wno-unused-function -Wno-unused-variable -Wno-unused-but-set-variable "
			if( fbGetOption( FB_COMPOPT_TARGET ) = FB_COMPTARGET_JS ) then
				ln += "-Wno-warn-absolute-paths "
			end if

		end if

		'' Don't warn about non-standard main() signature
		'' (we emit "ubyte **argv" instead of "char **argv")
		ln += "-Wno-main "

		'' helps finding ir-hlc bugs
		ln += "-Werror-implicit-function-declaration "

		ln += "-O" + str( fbGetOption( FB_COMPOPT_OPTIMIZELEVEL ) ) + " "

		'' Do not let gcc make assumptions about pointers; FB isn't strict about it.
		ln += "-fno-strict-aliasing "

		'' Ignore .ident directives on win32 targets to prevent identification strings
		'' from accumulating in the final binary (each .ident string from every object
		'' module is added to the final binary even when strings are identical).
		select case as const( fbGetOption( FB_COMPOPT_TARGET ) )
		case FB_COMPTARGET_WIN32
			ln += "-fno-ident "
		end select

		'' The rtlib sets its own rounding mode, don't let gcc make assumptions.
		'' (Should this be skipped for ARM rather than Android?)
		if( fbGetOption( FB_COMPOPT_TARGET ) <> FB_COMPTARGET_JS andalso _
		    fbGetOption( FB_COMPOPT_TARGET ) <> FB_COMPTARGET_ANDROID ) then
			ln += "-frounding-math "
		end if

		'' ?
		ln += "-fno-math-errno "

		'' Note: we shouldn't use some options like e.g. -ffast-math, because
		'' they cause incompatibilities with the ASM backend. For example:
		''    dim as double d = INF
		''    print d - d
		'' prints -NaN (IND) under the ASM backend because the FPU does the
		'' subtraction, however with the C backend with, gcc -ffast-math
		'' optimizes out the subtraction (even under -O0) and inserts 0 instead.

		'' Define signed integer overflow
		ln += "-fwrapv "

		'' Avoid gcc exception handling bloat
		ln += "-fno-exceptions -fno-asynchronous-unwind-tables "

		'' But enable unwind-tables on x64, these GREATLY increase the accuracy of
		'' debuggers and crash tools across platforms for minimal overhead
		if( (ism64Target = TRUE) or (fbGetOption( FB_COMPOPT_UNWINDINFO ) = TRUE) ) then
			ln += "-funwind-tables "
		else
			ln += "-fno-unwind-tables "
		end if

		'' Prevent format string errors on gcc 9.x. (enabled by default with '-Wall')
		'' TODO: fbc currently emits the ZSTRING type as 'uint8' when it
		'' should probably preserve the 'char' type.  In C, there are 3
		'' distinct types, 'char', 'unsigned char', 'signed char'.
		'' See ir-hlc.bas:hEmitType()
		ln += "-Wno-format "

		'' Newer GCC versions diagnose some const-qualified pointer helper calls
		'' more strictly.  The generated C is an intermediate representation owned
		'' by fbc, so keep that compatibility detail out of user programs.
		ln += "-Wno-incompatible-pointer-types "

		if( fbGetOption( FB_COMPOPT_BACKEND ) = FB_BACKEND_CLANG ) then
			ln += "-Wno-builtin-requires-header "
		end if

		if( fbGetOption( FB_COMPOPT_DEBUGINFO ) ) then
			ln += "-g "
		end if

		if( fbGetOption( FB_COMPOPT_PROFILE ) = FB_PROFILE_OPT_GMON ) then
			ln += "-pg "
		end if

		if( fbGetOption( FB_COMPOPT_FPUTYPE ) = FB_FPUTYPE_SSE ) then
			if( fbGetOption( FB_COMPOPT_TARGET ) = FB_COMPTARGET_ANDROID ) then
				'' Guaranteed to be present
				ln += "-mfpmath=sse -mssse3 "
			else
				ln += "-mfpmath=sse -msse2 "
			end if
		elseif( fbGetOption( FB_COMPOPT_FPUTYPE ) = FB_FPUTYPE_NEON ) then
			'' NEON is not IEEE 754-compliant (except in armv8+), so
			'' gcc will not use it without this.
			ln += "-mfpu=neon -funsafe-math-optimizations "
		end if

		select case( fbGetCpuFamily( ) )
		case FB_CPUFAMILY_X86, FB_CPUFAMILY_X86_64
			if( fbGetOption( FB_COMPOPT_ASMSYNTAX ) = FB_ASMSYNTAX_INTEL ) then
				ln += "-masm=intel "
			end if
		end select

	case FB_BACKEND_LLVM
		select case( fbGetCpuFamily( ) )
		case FB_CPUFAMILY_X86
			ln += "-march=x86 "
		case FB_CPUFAMILY_X86_64
			ln += "-march=x86-64 "
		case FB_CPUFAMILY_ARM
			ln += "-march=arm "
		case FB_CPUFAMILY_AARCH64
			'' From the GCC manual:
			'' -march=name
			'' Specify the name of the target architecture and,
			'' optionally, one or more feature modifiers. This option
			'' has the form -march=arch{+[no]feature}*.
			''
			'' The permissible values for arch are
			'' 'armv8-a'
			'' 'armv8.1-a' = 'armv8-a' + ARMv8.1-A
			'' 'armv8.2-a' = 'armv8.1-a' + ARMv8.2-A
			'' 'armv8.3-a' = 'armv8.2-a' + ARMv8.3-A
			'' 'armv8.4-a' = 'armv8.3-a' + ARMv8.4-A
			'' 'armv8.5-a' = 'armv8.4-a' + ARMv8.5-A
			'' 'native' = architecture of the host system
			''
			'' It enables the '+crc', '+lse', and '+rdma' features.
			''
			'' The value 'native' is available on native AArch64
			'' GNU/Linux and causes the compiler to pick the
			'' architecture of the host system. This option has no
			'' effect if the compiler is unable to recognize the
			'' architecture of the host system, The permissible
			'' values for feature are listed in the sub-section on
			'' ['-march' and '-mcpu' Feature Modifiers]. Where
			'' conflicting feature modifiers are specified, the
			'' right-most feature is used. GCC uses name to determine
			'' what kind of instructions it can emit when generating
			'' assembly code. If '-march' is specified without either
			'' of '-mtune' or '-mcpu' also being specified, the code
			'' is tuned to perform well across a range of target
			'' processors implementing the target architecture.

			ln += "-march=armv8-a "
		case FB_CPUFAMILY_PPC
			ln += "-mcpu=powerpc "
		case FB_CPUFAMILY_PPC64
			ln += "-mcpu=powerpc64 "
		case FB_CPUFAMILY_PPC64LE
			ln += "-mcpu=powerpc64le "
		end select

		if( fbGetOption( FB_COMPOPT_PIC ) ) then
			ln += "-relocation-model=pic "
		end if

		ln += "-O" + str( fbGetOption( FB_COMPOPT_OPTIMIZELEVEL ) ) + " "

		select case( fbGetCpuFamily( ) )
		case FB_CPUFAMILY_X86, FB_CPUFAMILY_X86_64
			if( fbGetOption( FB_COMPOPT_ASMSYNTAX ) = FB_ASMSYNTAX_INTEL ) then
				ln += "--x86-asm-syntax=intel "
			end if
		end select

	end select

	ln += """" + hGetAsmName( module, 1 ) + """ "
	ln += "-o """ + asmfile + """"
	ln += fbc.extopt.gcc

	dim as FBCTOOL ccompiler = FBCTOOL_NONE

	select case( fbGetOption( FB_COMPOPT_BACKEND ) )
	case FB_BACKEND_GCC
		ccompiler = FBCTOOL_GCC
		if( fbGetOption( FB_COMPOPT_TARGET ) = FB_COMPTARGET_JS ) then
			ccompiler = FBCTOOL_EMCC
		end if
		function = fbcRunBin( "compiling C", ccompiler, ln )
	case FB_BACKEND_CLANG
		ccompiler = FBCTOOL_CLANG
		function = fbcRunBin( "compiling C", ccompiler, ln )
	case FB_BACKEND_LLVM
		ccompiler = FBCTOOL_LLC
		function = fbcRunBin( "compiling LLVM IR", ccompiler, ln )
	end select
end function

private sub hCompileStage2Modules( )
	dim as FBCIOFILE ptr module = listGetHead( @fbc.modules )
	while( module )
		if( hCompileStage2Module( module ) = FALSE ) then
			fbcEnd( 1 )
		end if
		module = listGetNext( module )
	wend
end sub

private function hAssembleModule( byval module as FBCIOFILE ptr ) as integer
	dim as string ln

	dim as FBCTOOL assembler = FBCTOOL_NONE

	if( hCompileStage2DirectlyToObj( ) ) then
		function = TRUE
		exit function
	end if

#ifdef ENABLE_STANDALONE
	if( assembler = FBCTOOL_NONE ) then
		if( fbGetOption( FB_COMPOPT_BACKEND ) = FB_BACKEND_CLANG ) then
			assembler = FBCTOOL_CLANG
		end if
	end if
#endif

	if( assembler = FBCTOOL_NONE ) then
		select case fbGetOption( FB_COMPOPT_TARGET )
		case FB_COMPTARGET_ANDROID
			'' Current NDK toolchains are clang/LLVM based and do not provide
			'' the old GNU as driver expected by the generic Unix path.
			assembler = FBCTOOL_CLANG
		case FB_COMPTARGET_JS
			'' We will skip assemble stage, since it is
			'' already performed by Emscripten
			'' to re-enable assembly, change to FBCTOOL_EMAS
			assembler = FBCTOOL_NONE
		case else
			assembler = FBCTOOL_AS
		end select
	end if

	'' Still no assembler selected? Then we are skipping the assembly
	'' and letting the ccompiler generate the object file directly
	if( assembler = FBCTOOL_NONE ) then
		function = TRUE
		exit function
	end if

	select case assembler
	case FBCTOOL_CLANG
		ln += hGetClangTargetOption( )
		ln += "-c "
		select case( fbGetCpuFamily( ) )
		case FB_CPUFAMILY_X86, FB_CPUFAMILY_X86_64
			if( fbGetOption( FB_COMPOPT_ASMSYNTAX ) = FB_ASMSYNTAX_INTEL ) then
				ln += "-masm=intel "
			end if
		end select
	case else
		select case( fbGetCpuFamily( ) )
		case FB_CPUFAMILY_X86
			if (fbGetOption( FB_COMPOPT_TARGET ) = FB_COMPTARGET_DARWIN) then
				ln += "-arch i386 "
			else
				ln += "--32 "
			endif
		case FB_CPUFAMILY_X86_64
			if (fbGetOption( FB_COMPOPT_TARGET ) = FB_COMPTARGET_DARWIN) then
				ln += "-arch x86_64 "
			else
				ln += "--64 "
			endif
		end select

		if( fbGetOption( FB_COMPOPT_DEBUGINFO ) = FALSE ) then
			if (fbGetOption( FB_COMPOPT_TARGET ) <> FB_COMPTARGET_DARWIN) then
				if( fbGetOption( FB_COMPOPT_TARGET ) <> FB_COMPTARGET_JS ) then
					ln += "--strip-local-absolute "
				end if
			endif
		end if
	end select

	fbcDarwinPlatformAddAssemblerOptions( ln )

	ln += """" + hGetAsmName( module, 2 ) + """ "
	ln += "-o """ + *module->objfile + """"
	ln += fbc.extopt.gas

	if( fbcRunBin( "assembling", assembler, ln ) = FALSE ) then
		exit function
	end if

	'' Clean up the .o if -C wasn't given
	if( fbc.keepobj = FALSE ) then
		fbcAddTemp( *module->objfile )
	end if

	function = TRUE
end function

private sub hAssembleModules( )
	dim as FBCIOFILE ptr module = listGetHead( @fbc.modules )
	while( module )
		if( hAssembleModule( module ) = FALSE ) then
			fbcEnd( 1 )
		end if
		module = listGetNext( module )
	wend
end sub

private function hAssembleRc( byval rc as FBCIOFILE ptr ) as integer
#ifdef ENABLE_GORC
	'' Using GoRC for the classical native win32 standalone build
	'' Note: GoRC /fo doesn't accept anything except *.obj, not even *.o,
	'' so we need to make it *.obj and then rename it afterwards.

	dim as integer need_rename = FALSE

	'' Ensure to use *.obj so GoRC accepts it
	if( hGetFileExt( *rc->objfile ) <> "obj" ) then
		need_rename = TRUE
		*rc->objfile += ".obj"
	end if

	'' Change the include env var to point to the (hopefully present)
	'' win/rc/*.h headers.
	dim as string oldinclude = trim( environ( "INCLUDE" ) )
	setenviron "INCLUDE=" + fbc.incpath + _
			(FB_HOST_PATHDIV + "win" + FB_HOST_PATHDIV + "rc")

	dim as string ln = "/ni /nw /o "

	if( fbGetCpuFamily( ) = FB_CPUFAMILY_X86_64 ) then
		ln += "/machine X64 "
	end if

	ln &= "/fo """ & *rc->objfile & """"
	ln &= " """ & rc->srcfile & """"

	if( fbcRunBin( "compiling rc", FBCTOOL_GORC, ln ) = FALSE ) then
		exit function
	end if

	'' restore the include env var
	if( len( oldinclude ) > 0 ) then
		setenviron "INCLUDE=" + oldinclude
	end if

	if( need_rename ) then
		dim as string badname = *rc->objfile
		*rc->objfile = hStripExt( *rc->objfile )
		'' Rename back so it will be found by ld/the user
		function = (name( badname, *rc->objfile ) = 0)
	else
		function = TRUE
	end if
#else
	'' Using binutils' windres for all other setups (e.g. cross-compiling
	'' linux -> win32)
	'' Note: windres uses gcc -E to preprocess the .rc by default,
	'' that may not be 100% compatible to GoRC.

	dim as string ln = "--output-format=coff --include-dir=."
	ln += " """ + rc->srcfile + """"
	ln += " """ + *rc->objfile + """"

	function = fbcRunBin( "compiling rc", FBCTOOL_WINDRES, ln )
#endif

	'' Clean up the .o if -C wasn't given
	if( fbc.keepobj = FALSE ) then
		fbcAddTemp( *rc->objfile )
	end if
end function

private sub hAssembleRcs( )
	'' Compile .rc/.res files
	dim as FBCIOFILE ptr rc = listGetHead( @fbc.rcs )
	while( rc )
		if( hAssembleRc( rc ) = FALSE ) then
			fbcEnd( 1 )
		end if
		rc = listGetNext( rc )
	wend
end sub

private sub hAssembleXpm( )
	if( len( fbc.xpm.srcfile ) > 0 ) then
		if( fbGetOption( FB_COMPOPT_BACKEND ) <> FB_BACKEND_GAS ) then
			hCompileStage2Module( @fbc.xpm )
		end if
		if( hAssembleModule( @fbc.xpm ) = FALSE ) then
			fbcEnd( 1 )
		end if
	end if
end sub

private function hGetFbctinfAnchorName( ) as string
	dim as ulongint hash = 2166136261ull

	for i as integer = 1 to len( fbc.outname )
		hash xor= culngint( asc( mid( fbc.outname, i, 1 ) ) )
		hash *= 16777619ull
		hash and= &hFFFFFFFFull
	next

	function = "__fb_ctinf_anchor_" + lcase( hex( cuint( hash ), 8 ) )
end function

private function hCompileFbctinf( ) as integer
	dim as FBCIOFILE fbctinf
	dim as string objfile
	dim as integer fo = any

	'' Compile an empty .bas into the fbctinf object file
	'' (it will contain only objinfo)
	fbctinf.srcfile = FB_INFOSEC_BASNAME
	objfile = FB_INFOSEC_OBJNAME
	fbctinf.objfile = @objfile

	if( fbc.verbose ) then
		print "creating: ", fbctinf.srcfile
	end if

	'' Create the empty .bas file
	fo = freefile( )
	if( open( fbctinf.srcfile, for output, as #fo ) ) then
		exit function
	end if
	if( fbGetOption( FB_COMPOPT_TARGET ) = FB_COMPTARGET_DARWIN ) then
		''
		'' Apple ar/ranlib warns about archive members with no symbols.
		'' The objinfo member is intentionally metadata-only, so add a
		'' unique, unreferenced symbol to keep Darwin archives quiet.
		''
		print #fo, "sub " + hGetFbctinfAnchorName( ) + "()"
		print #fo, "end sub"
	end if
	close #fo

	'' Clean up the temp .bas if -R wasn't given
	if( fbc.keepasm = FALSE ) then
		fbcAddTemp( fbctinf.srcfile )
	end if

	hCompileBas( @fbctinf, FALSE, TRUE, -1 )
	if( fbGetOption( FB_COMPOPT_BACKEND ) <> FB_BACKEND_GAS ) then
		hCompileStage2Module( @fbctinf )
	end if
	function = hAssembleModule( @fbctinf )
end function

private function hArchiveFiles( ) as integer
	hSetOutName( )

	'' Remove lib*.a if it already exists, because ar doesn't overwrite
	safeKill( fbc.outname )

	dim as string ln = "-rsc " + QUOTE + fbc.outname + (QUOTE + " ")

	if( fbGetOption( FB_COMPOPT_OBJINFO ) and _
		(fbGetOption( FB_COMPOPT_TARGET ) <> FB_COMPTARGET_JS) and _
		(not fbIsCrossComp( )) ) then
		if( hCompileFbctinf( ) ) then
			'' The objinfo reader expects the fbctinf object to be
			'' the first object file in libraries, so it must be
			'' specified first on the archiver command line:
			ln += QUOTE + FB_INFOSEC_OBJNAME + QUOTE + " "
		end if
		fbcAddTemp( FB_INFOSEC_OBJNAME )
	end if

	dim as string ptr objfile = listGetHead( @fbc.objlist )
	while( objfile )
		ln += """" + *objfile + """ "
		objfile = listGetNext( objfile )
	wend

	var ar = FBCTOOL_AR
	if( fbGetOption( FB_COMPOPT_TARGET ) = FB_COMPTARGET_JS ) then
		ar = FBCTOOL_EMAR
	end if

'' See comment in hLinkFiles about command line lengths
#if defined( __FB_WIN32__ ) or defined( __FB_DOS__ )
	dim targetprefixlen as ulong
	#ifndef ENABLE_STANDALONE
		targetprefixlen = len( fbc.targetprefix )
	#endif
	dim toolnamelen as integer = len( fbctoolTB( ar ).name + ".exe" ) + _
		iif( targetprefixlen > len( fbc.buildprefix ), targetprefixlen, len( fbc.buildprefix ) )
	if( (fbGetOption( FB_COMPOPT_TARGET ) = FB_COMPTARGET_DOS) or _
		(len( ln ) > (2047 - toolnamelen)) ) then
		if( hPutLdArgsIntoFile( ln ) = FALSE ) then
			exit function
		end if
	end if
#endif

	'' invoke ar
	function = fbcRunBin( "archiving", ar, ln )
end function

private sub hSetDefaultLibPaths( )
	'' compiler's lib/
	fbcAddDefLibPath( fbc.libpath )

	'' and the current path
	fbcAddDefLibPath( "." )

#ifndef ENABLE_STANDALONE
	'' Add gcc's private lib directory, to find libgcc
	'' This is for installing into Unix-like systems, and not for
	'' standalone, which has libgcc in the main lib/.
	select case fbGetOption( FB_COMPOPT_TARGET )
	case FB_COMPTARGET_ANDROID
		'' We don't pass -lgcc to ld (it's probably not called libgcc)
	case FB_COMPTARGET_JS
		'' We let emcc handle linking
	case else
		fbcAddLibPathFor( "libgcc.a" )

		#ifndef DISABLE_STDCXX_PATH
			'' we don't specifically need c++, but for some users that do want to
			'' interop with c++ and to allow some of the tests/cpp tests to pass
			'' it's helpful to also query for a c++ library.
			select case fbGetOption( FB_COMPOPT_TARGET )
			case FB_COMPTARGET_FREEBSD
				fbcAddLibPathFor( "libc++.so" )
			case FB_COMPTARGET_DOS
				fbcAddLibPathFor( "libstdcxx.a" )
			case else
				fbcAddLibPathFor( "libstdc++.so" )
			end select
		#endif
	end select

	fbcPlatformAddDefaultLibPaths( )
#endif
end sub

private sub fbcAddDefLib(byval libname as zstring ptr)
	strsetAdd(@fbc.finallibs, *libname, TRUE)
end sub

private function hGetFbLibNameSuffix( ) as string
	dim s as string
	dim as integer target = fbGetOption( FB_COMPOPT_TARGET )
	if( fbGetOption( FB_COMPOPT_MULTITHREADED ) ) then
		s += "mt"
	end if
	if( target = FB_COMPTARGET_OPENBSD ) then
		s += "pic"
	elseif( fbGetOption( FB_COMPOPT_PIC ) ) then
		s += "pic"
	end if
	function = s
end function

private sub hAddDarwinFrameworks( byref ldcline as string )
	fbcPlatformAddLinkerFrameworks( ldcline )
end sub

private sub hAddDefaultLibs( )
	'' select the right FB rtlib
	if( fbGetOption( FB_COMPOPT_FBRT ) ) then
		fbcAddDefLib( "fbrt" + hGetFbLibNameSuffix( ) )
	else
		fbcAddDefLib( "fb" + hGetFbLibNameSuffix( ) )
	end if

	'' and the gfxlib, if gfx functions were used
	if( fbGetOption( FB_COMPOPT_FBGFX ) ) then
		fbcAddDefLib( "fbgfx" + hGetFbLibNameSuffix( ) )
		fbcPlatformAddGfxLibs( )
	end if

	'' and the sound runtime, if sound functions were used
	if( fbGetOption( FB_COMPOPT_FBSFX ) ) then
		fbcAddDefLib( "sfx" + hGetFbLibNameSuffix( ) )
		fbcPlatformAddSfxLibs( )
	end if

	fbcPlatformAddDefaultLibs( )

end sub

private sub hExcludeLibsFromLink( )
	'' Remove any excluded libs from fbc.finallibs list
	dim as TSTRSETITEM ptr i = listGetHead(@fbc.excludedlibs.list)
	while i
		select case i->s
		case "fbrt0.o", "fbrt0pic.o", "fbrt1.o", "fbrt1pic.o", "fbrt2.o", "fbrt2pic.o"
			fbc.nofbrt0 = TRUE
		case else
			strsetDel(@fbc.finallibs, i->s)
		end select
		i = listGetNext(i)
	wend
end sub

private sub hPrintOptions( byval verbose as integer )
	'' Note: must print each line separately to let the rtlib print the
	'' proper line endings even if redirected to file/pipe, hard-coding \n
	'' here isn't enough for DOS/Windows.

	print "usage: fbc [options] <input files>"
	print "input files:"
	print "  *.a = static library, *.o = object file, *.bas = source"
	print "  *.rc = resource script, *.res = compiled resource (win32)"
	print "  *.xpm = icon resource (*nix/*bsd)"
	print "options:"
	print "  @<file>          Read more command line arguments from a file"
	print "  -a <file>        Treat file as .o/.a input file"
	print "  -arch <type>     Set target architecture (default: 686)"
	print "  -asm att|intel   Set asm format (-gen gcc|llvm, x86 or x86_64 only)"
	print "  -b <file>        Treat file as .bas input file"
	if( verbose ) then
	print "  -buildprefix <name>  specify prefix on tool names (as, ar, ld)"
	end if
	print "  -c               Compile only, do not link"
	print "  -C               Preserve temporary .o files"
	print "  -d <name>[=<val>]  Add a global #define"
	print "  -dll             Same as -dylib"
	print "  -dylib           Create a DLL (win32) or shared library (*nix/*BSD)"
	print "  -e               Enable runtime error checking"

	if( verbose ) then
	print "  -earray          Enable array bounds checking"
	print "  -earraydims      Enable array dimensions checking"
	print "  -eassert         Enable assert() and assertwarn() checking"
	print "  -edebug          Enable __FB_DEBUG__"
	print "  -edebuginfo      Add debug info"
	print "  -elocation       Enable error location reporting"
	print "  -enullptr        Enable null-pointer checking"
	print "  -eunwind         Enable call stack unwind information"
	print "  -entry <name>    Change the entry point of the program from main()"
	end if

	print "  -ex              -e plus RESUME support"
	print "  -exx             -ex plus array bounds/null-pointer checking"
	print "  -export          Export symbols for dynamic linkage"
	if( verbose ) then
	print "  -fbgfx           Link to the appropriate libfbgfx variant (normally automatic)"
	end if
	print "  -forcelang <name>  Override #lang statements in source code"
	if( verbose ) then
	print "  -fpmode fast|precise  Select floating-point math accuracy/speed"
	print "  -fpu x87|sse|neon  Set target FPU"
	end if
	print "  -g               Add debug info, enable __FB_DEBUG__, and enable assert()"

	if( verbose ) then
	print "  -gen gas         Select GNU gas 32-bit assembler backend"
	print "  -gen gas64       Select GNU gas 64-bit assembler backend"
	print "  -gen gcc         Select GNU gcc C backend"
	print "  -gen llvm        Select LLVM backend"
	print "  -gen clang       Select clang C backend"
	else
	print "  -gen <backend>   Select code generation backend (gas|gas64|gcc|llvm|clang)"
	end if

	print "  [-]-help         Show this help output; use '-help -v' to show verbose help"
	print "  -i <path>        Add an include file search path"
	print "  -include <file>  Pre-#include a file for each input .bas"
	print "  -l <name>        Link in a library"
	print "  -lang <name>     Select FB dialect: fb, deprecated, fblite, qb"
	print "  -lib             Create a static library"
	print "  -m <name>        Specify main module (default if not -c: first input .bas)"
	print "  -map <file>      Save linking map to file"
	print "  -maxerr <n>      Only show <n> errors"
	print "  -mt              Use thread-safe FB runtime"
	print "  -nodeflibs       Do not include the default libraries when linking"
	print "  -noerrline       Do not show source context in error messages"
	print "  -nolib <a,b,c>   Do not include the specified libraries when linking"
	print "  -noobjinfo       Do not read/write compile-time info from/to .o and .a files"
	print "  -nostrip         Do not strip symbol information from the output file"
	print "  -o <file>        Set .o (or -pp .bas) file name for prev/next input file"
	print "  -O <value>       Optimization level (default: 0)"
	print "  -p <path>        Add a library search path"
	print "  -pic             Generate position-independent code (non-x86 Unix shared libs)"
	print "  -pp              Write out preprocessed input file (.pp.bas) only"
	print "  -prefix <path>   Set the compiler prefix path"
	print "  -print host|target  Display host/target system name"
	print "  -print fblibdir  Display the compiler's lib/ path"
	print "  -print x         Display output binary/library file name (if known)"
	if( verbose ) then
	print "  -print fork-id   Display compiler's fork identifier (if set)"
	print "  -print sha-1     Display compiler's source code commit sha-1 (if known)"
	end if
	print "  -profile         Enable function profiling"
	print "  -profgen         Set the profiling code generation type (gmon|fb|cycles)"
	print "  -r               Write out .asm/.c/.ll (-gen gas/gcc/llvm) only"
	print "  -rr              Write out the final .asm only"
	print "  -R               Preserve temporary .asm/.c/.ll/.def files"
	print "  -RR              Preserve the final .asm file"
	print "  -s console|gui   Select application subsystem"
	print "  -showincludes    Display a tree of file names of #included files"
	print "  -static          Prefer static libraries over dynamic ones when linking"
	print "  -strip           Omit all symbol information from the output file"
	print "  -sysroot <path>  Linker sysroot, needed by some cross-compiling toolchains"
	print "  -t <value>       Set .exe stack size in kbytes, default: 1024 (win32/dos/xbox)"
	if( verbose ) then
	'' !!!TODO!!! provide more examples of available targets
	print "  -target <name>   Set cross-compilation target"
	else
	print "  -target <name>   Set cross-compilation target"
	end if
	print "  -title <name>    Set XBE display title (xbox)"
	print "  -v               Be verbose"
	print "  -vec <n>         Automatic vectorization level (default: 0)"
	print "  [-]-version      Show compiler version"
	print "  -w all|pedantic|<n>  Set min warning level: all, pedantic or a value"
	if( verbose ) then
	print "  -w all           Enable all warnings"
	print "  -w none          Disable all warnings"
	print "  -w param         Enable parameter warnings"
	print "  -w escape        Enable string escape sequence warnings"
	print "  -w next          Enable next statement warnings"
	print "  -w signedness    Enable type signedness warnings"
	print "  -w constness     Enable const type warnings"
	print "  -w suffix        Enable invalid suffix warnings"
	print "  -w error         Report warnings as errors"
	print "  -w upcast        Enable warning when up-casting discards initializers"
	end if
	print "  -Wa <a,b,c>      Pass options to 'as'"
	print "  -Wc <a,b,c>      Pass options to 'gcc' (-gen gcc) or 'llc' (-gen llvm)"
	print "  -Wl <a,b,c>      Pass options to 'ld'"
	print "  -x <file>        Set output executable/library file name"

	if( verbose ) then
	print "  -z fbrt          Link with 'fbrt' instead of 'fb' runtime library"
	print "  -z gosub-setjmp  Use setjmp/longjmp to implement GOSUB"
	print "  -z no-thiscall   Don't use '__thiscall' calling convention"
	print "  -z no-fastcall   Don't use '__fastcall' calling convention"
	print "  -z nobuiltins    Disable all non-required builtin procedure definitions"
	print "  -z nocmdline     Disable #cmdline source directives"
	print "  -z optabstract   Only supports optimizing purely abstract types"
	print "  -z retinflts     Enable returning some types in floating point registers"
	print "  -z valist-as-ptr Use pointer expressions to implement CVA_*() macros"
	else
	print "  -z <option>      Extended options (see fbc -help -v)"
	end if

end sub

private sub hAppendConfigInfo( byref config as string, byval info as zstring ptr )
	if( len( config ) > 0 ) then
		config += ", "
	end if
	config += *info
end sub

private sub hPrintVersion( byval verbose as integer )
	dim as string config
	dim as string version = FB_VERSION
	if( FB_REV > 0 ) then
		version += "-" + str(FB_REV)
	end if

	print "FreeBASIC Compiler - Version " + version + _
		" (" + FB_BUILD_DATE_ISO + "), built for " + fbGetHostId( ) + " (" & fbGetHostBits( ) & "bit)"
	print "Copyright (C) 2004-2025 The FreeBASIC development team."

	#ifdef ENABLE_STANDALONE
		hAppendConfigInfo( config, "standalone" )
	#endif

	#ifdef ENABLE_PREFIX
		hAppendConfigInfo( config, "prefix: '" + ENABLE_PREFIX + "'" )
	#endif

	if( len( config ) > 0 ) then
		print config
	end if

	if( verbose ) then
		fbcPrintTargetInfo( )
		if( FB_BUILD_SHA1 > "" ) then
			print "source sha-1: " & FB_BUILD_SHA1
		end if
		if( FB_BUILD_FORK_ID > "" ) then
			print "fbc fork id:  " & FB_BUILD_FORK_ID
		end if
	end if
end sub

	fbcInit( )

	if( __FB_ARGC__ = 1 ) then
		hPrintOptions( FALSE )
		fbcEnd( 1 )
	end if

	hParseArgs( __FB_ARGC__, __FB_ARGV__ )

	hCheckArgs( )

	if( fbc.showversion ) then
		hPrintVersion( fbc.verbose )
		fbcEnd( 0 )
	end if

	if( fbc.verbose ) then
		fbc.showversion = TRUE
		hPrintVersion( FALSE )
	end if

	'' Show help if -help was given
	if( fbc.showhelp ) then
		hPrintOptions( fbc.verbose )
		fbcEnd( 1 )
	end if

	do
		fbcDeterminePrefix( )
		fbcSetupCompilerPaths( )

		if( fbc.verbose ) then
			fbcPrintTargetInfo( )
		end if

		'' Tell the compiler about the default include path (added after
		'' the command line ones, so those will be searched first)
		fbAddIncludePath( fbc.incpath )

		var have_input_files = (listGetHead( @fbc.modules   ) <> NULL) or _
			(listGetHead( @fbc.objlist   ) <> NULL) or _
			(listGetHead( @fbc.libs.list ) <> NULL) or _
			(listGetHead( @fbc.libfiles  ) <> NULL)

		'' Answer -print query, if any, and stop
		'' The -print option is intended to allow shell scripts, makefiles, etc.
		'' to query information from fbc.
		if( fbc.print >= 0 ) then
			select case( fbc.print )
			case PRINT_HOST
				print fbGetHostId( )
			case PRINT_TARGET
				print fbGetTargetId( )
			case PRINT_X
				'' If we have input files, -print x should give the output name that we'd normally get.
				'' However, a plain "fbc -print x" without input files should just give the .exe extension.
				if( have_input_files ) then
					fbcDetermineMainName( )
				end if
				hSetOutName( )
				print fbc.outname
			case PRINT_FBLIBDIR
				print fbc.libpath
			case PRINT_SHA1
				print FB_BUILD_SHA1
			case PRINT_FORK_ID
				print FB_BUILD_FORK_ID
			end select
			fbcEnd( 0 )
		end if

		fbcDetermineMainName( )

		'' Show help if there are no input files
		if( have_input_files = FALSE ) then
			hPrintOptions( fbc.verbose )
			fbcEnd( 1 )
		end if

		''
		'' Compile .bas modules
		''
		hCompileModules( )

		if( fbShouldRestart( ) = FALSE ) then
			exit do
		end if

		fbRestartEndRequest( FB_RESTART_FBC_CMDLINE )

		'' we are restarting, so show errors again
		errPreInit( )

		'' command line arguments have changed, check them again
		hCheckArgs( )

		if( fbc.verbose ) then
			print "Restarting fbc ..."
		end if
	loop

	if( hCompileXpm( ) = FALSE ) then
		fbcEnd( 1 )
	end if

	if( fbc.emitasmonly ) then
		fbcEnd( 0 )
	end if

	if( (fbGetOption( FB_COMPOPT_BACKEND ) <> FB_BACKEND_GAS)  and _
		fbGetOption( FB_COMPOPT_BACKEND ) <> FB_BACKEND_GAS64 ) then
		''
		'' Compile intermediate .c modules produced by -gen gcc
		''
		hCompileStage2Modules( )
	end if

	if( fbc.emitfinalasmonly ) then
		fbcEnd( 0 )
	end if

	''
	'' Assemble into .o files
	''
	hAssembleModules( )
	hAssembleRcs( )
	hAssembleXpm( )

	'' Stop for -c
	if( fbGetOption( FB_COMPOPT_OUTTYPE ) = FB_OUTTYPE_OBJECT ) then
		fbcEnd( 0 )
	end if

	'' Set the default lib paths before scanning for other libs
	hSetDefaultLibPaths( )

	'' Scan objects and libraries for more libraries and paths,
	'' before adding the default libs, which don't need to be searched,
	'' because they don't contain objinfo anyways.
	if( fbGetOption( FB_COMPOPT_OBJINFO ) and _
		(fbGetOption( FB_COMPOPT_TARGET ) <> FB_COMPTARGET_JS) and _
		(not fbIsCrossComp( )) ) then
		hCollectObjinfo( )
	end if

	if( fbGetOption( FB_COMPOPT_OUTTYPE ) = FB_OUTTYPE_STATICLIB ) then
		if( hArchiveFiles( ) = FALSE ) then
			fbcEnd( 1 )
		end if
		fbcEnd( 0 )
	end if

	'' Link

	'' Add default libs for linking, unless -nodeflibs was given
	'' Note: These aren't added into objinfo sections of objects or
	'' static libraries. Only the non-default libs are needed there.
	if( fbc.nodeflibs = FALSE ) then
		hAddDefaultLibs( )
	end if

	hExcludeLibsFromLink( )

	if( hLinkFiles( ) = FALSE ) then
		fbcEnd( 1 )
	end if

	fbcEnd( 0 )
