# Vanilla Jam compatibility
if ! $(INVOCATION_SUBDIR_SET) {

	rule FIsPrefix
	{
		# FIsPrefix <a> : <b> ;
		# Returns true, if list <a> is a prefix (a proper one or equal) of
		# list <b>, an empty list otherwise.
		local a = $(1) ;
		local b = $(2) ;
		while $(a) && $(a[1]) = $(b[1]) {
			a = $(a[2-]) ;
			b = $(b[2-]) ;
		}
	
		if $(a) {
			return ;
		} else {
			return true ;
		}
	}

	rule LocalClean { Clean $(1) : $(2) ; }

	rule LocalDepends { Depends $(1) : $(2) ; }

} # vanilla Jam compatibility

# The directory for build system specific files
OBOS_BUILD_DIR = [ FDirName $(OBOS_TOP) build ] ;

# Cache files for header scanning and jamfile caching
HCACHEFILE = header_cache ;
JCACHEFILE = jamfile_cache ;
LOCATE on $(HCACHEFILE) $(JCACHEFILE) = $(OBOS_BUILD_DIR) ;

# Include BuildConfig
{
	local buildConfig = [ GLOB $(OBOS_BUILD_DIR) : BuildConfig ] ;
	if ! $(buildConfig)
	{
		ECHO "No BuildConfig found in $(OBOS_BUILD_DIR)!" ;
		EXIT "Run ./configure in the source tree's root directory first!" ;
	}
	LOCATE on BuildConfig = $(OBOS_BUILD_DIR) ;
	include BuildConfig ;
}

# Save the platform default headers.
PLATFORM_DEFAULT_HEADERS = $(HDRS) ;

# We do not include any local BeOS system headers by default
CCFLAGS += -nostdinc ;
C++FLAGS += -nostdinc ;

# Determine if we're building on PPC or x86
# Determine mimetype of executable
# Cross compiling can come later

TARGET_CPU ?= $(OSPLAT:L) ;
OBOS_VERSION ?= R1 ;

switch $(TARGET_CPU) {
	case ppc :
	{
		if $(METROWERKS) {
			# at least parts of OpenBeOS still can be compiled with
			# the Metrowerks compiler on BeOS/PPC
			OBOS_TARGET_TYPE ?= "application/x-be-executable" ;
		} else {
			OBOS_TARGET_TYPE ?= "application/x-vnd.Be-elfexecutable" ;
		}
		DEFINES += __POWERPC__ ;
		OBOS_BOOT_PLATFORM = openfirmware ;
	}
	case x86 :
	{
		# nothing special to do here...
		OBOS_BOOT_PLATFORM = bios_ia32 ;
	}
	case * :
		Exit "Currently unsupported build platform:" $(TARGET_CPU) ;
}

# set target specific variables
{
	#Echo "Building for" $(TARGET_CPU) ;

	OBOS_TARGET ?= $(TARGET_CPU).$(OBOS_VERSION) ;
	OBOS_TARGET_TYPE ?= "application/x-vnd.Be-elfexecutable" ;
	OBOS_ARCH ?= $(TARGET_CPU) ;
	OBOS_TARGET_DEFINE ?= "ARCH_"$(TARGET_CPU) ;
}

# Enable warnings only if WARNINGS is defined
# Should be enabled by default later
#
if $(WARNINGS) {
	# For an explanation of the different warning options, see:
	# http://gcc.gnu.org/onlinedocs/gcc-2.95.3/gcc_2.html
	# to get even more warnings, add:
	# -Wwrite-strings  	(doesn't work well with some Be headers)
	# -Wundef			(dito)
	# -Wconversion		(gets you many warnings about implicit conversions)
	# -W				(gets you even more warnigs)
	CCFLAGS += -Wall -Wno-multichar -Wmissing-prototypes ;
	CCFLAGS += -Wpointer-arith -Wcast-align -Wsign-compare ;
	C++FLAGS += -Wall -Wno-multichar -Wmissing-prototypes -Wno-ctor-dtor-privacy -Woverloaded-virtual ;
	C++FLAGS += -Wpointer-arith -Wcast-align -Wsign-compare ;
} else {
	CCFLAGS += -Wno-multichar ;
	C++FLAGS += -Wno-multichar ;
}

# standard kernel C/C++ flags
KERNEL_CCFLAGS ?= -Wall -Wno-multichar -Wmissing-prototypes -finline -nostdinc ;
KERNEL_CCFLAGS += -fno-builtin -D$(OBOS_TARGET_DEFINE) ;
KERNEL_CCFLAGS += -DBOCHS_DEBUG_HACK=$(BOCHS_DEBUG_HACK) ;
KERNEL_C++FLAGS ?= -Wall -Wno-multichar -Wmissing-prototypes -finline -nostdinc ;
KERNEL_C++FLAGS += -fno-builtin -fno-exceptions -fno-rtti -D$(OBOS_TARGET_DEFINE) ;
KERNEL_C++FLAGS += -DBOCHS_DEBUG_HACK=$(BOCHS_DEBUG_HACK) ;

# We might later want to introduce debug levels or handle the whole issue
# differently. For now there's only on or off.
#
if $(DEBUG) {
	OPTIM ?= -O0 ;
	CCFLAGS += -g -DDEBUG=$(DEBUG) ;
	C++FLAGS += -g -DDEBUG=$(DEBUG)  ;
	KERNEL_CCFLAGS += -g -DDEBUG=$(DEBUG) ;
	KERNEL_C++FLAGS += -g -DDEBUG=$(DEBUG) ;
	LINKFLAGS += -g ;
} else {
	OPTIM ?= -O2 ;
}
#
# To disable for the tests OPTIM and DEBUG are overridden, set the environment
# variable NO_TEST_DEBUG.

# Instructs the Library rule to not make its object files temporary.
# This is need as some objects are used in a static library and for an
# executable.
KEEPOBJS = true ;

# under BeOS use copyattr instead of cp
if $(OS) = BEOS
{
	CP = copyattr --data ;
}

# If no OBOS_OBJECT_TARGET is not defined yet, use our default directory and
# include our "OBOS_TARGET" as subdirectory in there (to prevent different
# builds mixing objects from different targets).
if ! $(OBOS_OBJECT_TARGET) {
	OBOS_OBJECT_TARGET ?= [ FDirName $(OBOS_TOP) objects $(OBOS_TARGET) ] ;
}

# If no OBOS_DISTRO_TARGET is not defined yet, use our default directory and
# include our "OBOS_TARGET" as subdirectory in there (to prevent different
# builds mixing executables from different targets).
if ! $(OBOS_DISTRO_TARGET) {
	OBOS_DISTRO_TARGET ?= [ FDirName $(OBOS_TOP) distro $(OBOS_TARGET) ] ;
}

# Set our version number if not already set and mark it as a developer build
if ! $(OBOS_BUILD_VERSION) {
	OBOS_BUILD_VERSION ?= "1 0 0 a 1" ;
	OBOS_BUILD_DESCRIPTION ?= "Developer Build" ;
}

# If OBOS_BUILD_VERSION is set, but OBOS_BUILD_DESCRIPTION isn't, mark it as
# an unknown build.
if ! $(OBOS_BUILD_DESCRIPTION) {
	OBOS_BUILD_DESCRIPTION ?= "Unknown Build" ;
}

# Relative subdirs for distro dir
OBOS_ADDON_DIR  ?= [ FDirName $(OBOS_DISTRO_TARGET) beos system add-ons ] ;
OBOS_APPS_DIR   ?= [ FDirName $(OBOS_DISTRO_TARGET) beos apps ] ;
OBOS_BIN_DIR    ?= [ FDirName $(OBOS_DISTRO_TARGET) beos bin ] ;
OBOS_ETC_DIR    ?= [ FDirName $(OBOS_DISTRO_TARGET) beos etc ] ;
OBOS_FONTS_DIR  ?= [ FDirName $(OBOS_ETC_DIR) fonts ] ;
OBOS_KERNEL_DIR ?= [ FDirName $(OBOS_DISTRO_TARGET) beos system ] ;
OBOS_PREFS_DIR  ?= [ FDirName $(OBOS_DISTRO_TARGET) beos preferences ] ;
OBOS_SERVER_DIR ?= [ FDirName $(OBOS_DISTRO_TARGET) beos system servers ] ;
OBOS_SHLIB_DIR  ?= [ FDirName $(OBOS_DISTRO_TARGET) beos system lib ] ;
OBOS_STLIB_DIR  ?= [ FDirName $(OBOS_DISTRO_TARGET) beos system lib ] ;
OBOS_TEST_DIR   ?= [ FDirName $(OBOS_TOP) tests ] ;

OBOS_KERNEL_CONFIG = config.$(OBOS_ARCH).ini ;
OBOS_KERNEL = kernel.$(OBOS_ARCH) ;
OBOS_FLOPPY = floppy.$(OBOS_ARCH) ;

rule SetupIncludes
{
	# XXX add "opengl" later
	local os_includes = add-ons add-ons/file_system add-ons/graphics add-ons/input_server add-ons/screen_saver add-ons/tracker app device drivers game interface kernel media mail midi midi2 net storage support translation ;

	# Overwrite any exiting content when changing HDRS. This rule may be invoked multiple times.

	# Use headers directory, to allow to do things like include <posix/string.h>
	HDRS = [ FDirName $(OBOS_TOP) headers ] ;

	# Use posix headers directory
	HDRS += [ FDirName $(OBOS_TOP) headers posix ] ;

	# Use public OS header directories
	HDRS += [ PublicHeaders $(os_includes) ] ;

	# Used as a fallback, the R5 header directories (we should remove this as soon as possible)
	HDRS += /boot/develop/headers/posix /boot/develop/headers/cpp ;

	# The platform dependent headers.
	HDRS += $(PLATFORM_HEADERS) ;
}

rule SetupR5Includes
{
	# Unsets HDRS, so that the OBOS headers do not `shadow' the system headers.
	HDRS = ;
}

rule SetupDefaultIncludes
{
	# Resets HDRS to the default headers for the build platform.
	HDRS = $(PLATFORM_DEFAULT_HEADERS) ;
}

#-------------------------------------------------------------------------------
# Things Jam needs in order to work :)
#-------------------------------------------------------------------------------

rule Object
{
	# This is basically the original Jambase 2.4 Object rule stripped by
	# comments. Only the final switch statement has been changed to allow
	# intermediate C++ files for Yacc and Lex.

	LocalClean clean : $(<) ;

	MakeLocate $(<) : $(LOCATE_TARGET) ;
	SEARCH on $(>) = $(SEARCH_SOURCE) ;

	HDRS on $(<) = $(SEARCH_SOURCE) $(SUBDIRHDRS) $(HDRS) ;

	HDRRULE on $(>) = HdrRule ;
	HDRSCAN on $(>) = $(HDRPATTERN) ;
	HDRSEARCH on $(>) = 
		$(SEARCH_SOURCE:E) $(SUBDIRHDRS) $(HDRS) $(STDHDRS) ;

	HDRGRIST on $(>) = $(HDRGRIST) ;

	DEFINES on $(<) += $(DEFINES) ;

	# if source is not .c, generate .c with specific rule

	switch $(>:S)
	{
	    case .asm : As $(<) : $(>) ;
	    case .c :	Cc $(<) : $(>) ;
	    case .C :	C++ $(<) : $(>) ;
	    case .cc :	C++ $(<) : $(>) ;
	    case .cpp : C++ $(<) : $(>) ;
	    case .f :	Fortran $(<) : $(>) ;
	    case .l :	if [ on $(2) return $(GENERATE_C++) ] {
						C++ $(<) : $(<:S=.cpp) ;
						LexC++ $(<:S=.cpp) : $(>) ;
					} else {
						Cc $(<) : $(<:S=.c) ;
						Lex $(<:S=.c) : $(>) ;
					}
	    case .s :	As $(<) : $(>) ;
	    case .y :	if [ on $(2) return $(GENERATE_C++) ] {
						C++ $(<) : $(<:S=.cpp) ;
						Bison $(<:S=.cpp) : $(>) ;
					} else {
						Cc $(<) : $(<:S=$(YACCGEN)) ;
						Yacc $(<:S=$(YACCGEN)) : $(>) ;
					}
	    case * :	UserObject $(<) : $(>) ;
	}
}

rule UserObject
{
	switch $(2)
	{
	case *.S    : assemble $(1) : $(2) ;
	case *.o    : return ;
	case *      : ECHO "unknown suffix on" $(2) ;
	}
}

# Override the default to give "prettier" command lines.
actions Cc 
{
	$(CC) -c "$(2)" $(CCFLAGS) $(CCDEFS) $(CCHDRS) -o "$(1)" ;
}

actions C++
{
	$(C++) -c "$(2)" $(C++FLAGS) $(CCDEFS) $(CCHDRS) -o "$(1)" ;
}


#-------------------------------------------------------------------------------
# General High-level OBOS target rules
#-------------------------------------------------------------------------------

rule App
{
	# App <name> : <sources> : <libraries> ;
	SetupIncludes ;
	SetupObjectsDir ;
	Main $(1) : $(2) ;
	MakeLocate $(1) : $(OBOS_APPS_DIR) ;
	LinkSharedOSLibs $(1) : $(3) ;
	LINKFLAGS on $(1) = [ on $(1) return $(LINKFLAGS) ]
						-Xlinker -soname=_APP_ ;
}

rule BinCommand
{
	# BinCommand <name> : <sources> : <libraries> ;
	SetupIncludes ;
	SetupObjectsDir ;
	Main $(1) : $(2) ;
	MakeLocate $(1) : $(OBOS_BIN_DIR) ;
	LinkSharedOSLibs $(1) : $(3) ;
	LINKFLAGS on $(1) = [ on $(1) return $(LINKFLAGS) ]
						-Xlinker -soname=_APP_ ;
}

rule StdBinCommands
{
	# StdBinCommands <sources> : <libs> ;
	SetupIncludes ;
	SetupObjectsDir ;
	local libs = $(2) ;
	local source ;
	for source in $(1)
	{
		local target = $(source:S=) ;
		target = [ FGristFiles $(target) ] ;

		BinCommand $(target) : $(source) : $(libs) ;
	}
}

rule Preference
{
	# Preference <name> : <sources> : <libraries> ;
	SetupIncludes ;
	SetupObjectsDir ;
	Main $(1) : $(2) ;
	MakeLocate $(1) : $(OBOS_PREFS_DIR) ;
	LinkSharedOSLibs $(1) : $(3) ;
	LINKFLAGS on $(1) = [ on $(1) return $(LINKFLAGS) ]
						-Xlinker -soname=_APP_ ;
}

rule Server
{
	# Server <name> : <sources> : <libraries> ;
	
	SetupIncludes ;
	SetupObjectsDir ;
	Main $(1) : $(2) ;
	MakeLocate $(1) : $(OBOS_SERVER_DIR) ;
	LinkSharedOSLibs $(1) : $(3) ;
	LINKFLAGS on $(1) = [ on $(1) return $(LINKFLAGS) ]
						-Xlinker -soname=_APP_ ;
}

# test pseudo targets
NOTFILE obostests ;
NOTFILE r5tests ;

rule CommonTestLib
{
	# CommonTestLib <target> : <sources> : <obos libraries> 
	#	: <r5 libraries> : <test libraries> : <public headers>;
	# Builds a unit test for both OBOS and R5 modules.
	# <target> The name of the target.
	# <sources> The list of sources.
	# <obos libraries> A list of link libraries for the OBOS tests (as passed
	# to LinkSharedOSLibs).
	# <r5 libraries> A list of link libraries for the R5 tests (as passed
	# to LinkSharedOSLibs).
	# <test libraries> A list of link libraries for both OBOS tests and R5 tests
	# that have a common name (i.e. specify libx.so and the OBOS tests will link
	# to libx.so and the R5 tests will link to libx_r5.so).
	# <public headers> A list of public header dirs (as passed to
	# UsePublicHeaders).

	TestLib $(1) : $(2) : [ FDirName $(OBOS_TEST_DIR) unittester lib ] : $(3) $(5) : $(6) ;
	R5TestLib $(1) : $(2) : [ FDirName $(OBOS_TEST_DIR) unittester_r5 lib ] : $(4) [ R5SharedLibraryNames $(5) ] ;
}

rule TestLib
{
	# TestLib <target> : <sources> : <dest> : <libraries> : <public headers>
	# Builds a unit test library for an OBOS module.
	# <target> The name of the target.
	# <sources> The list of sources.
	# <dest> The directory for the target (as passed to FDirName).
	# <libraries> A list of link libraries (as passed to LinkSharedOSLibs).
	# <public headers> A list of public header dirs (as passed to
	# UsePublicHeaders).

	local target = $(1) ;
	local sources = $(2) ;
	local dest = $(3) ;
	local libraries = $(4) ;
	local headerDirs = $(5) ;
	local objects = [ FGristFiles $(sources:S=$(SUFOBJ)) ] ;

	# Our Main replacement.
	MainFromObjects $(target) : $(objects) ;
	TestObjects $(sources) : $(headerDirs) ;

	MakeLocate $(target) : $(dest) ;
	Depends $(target) : libcppunit.so ;
	Depends obostests : $(target) ;
	LinkSharedOSLibs $(target) : libcppunit.so $(libraries) ;
	LINKFLAGS on $(target) = $(LINKFLAGS) -nostart -Xlinker -soname=\"$(target)\" ;
}

rule R5TestLib
{
	# R5TestLib <target> : <sources> : <dest> : <libraries>
	# Builds a unit test for an R5 module. "_r5" is appended to the object
	# and the target name.
	# <target> The name of the target.
	# <sources> The list of sources.
	# <dest> The directory for the target (as passed to FDirName).
	# <libraries> A list of link libraries (as passed to LinkSharedOSLibs).

	local target = $(1:B)_r5$(1:S) ;
	local sources = $(2) ;
	local dest = $(3) ;
	local libraries = $(4) ;
	local objects = [ R5ObjectNames $(sources) ] ;

	# Our Main replacement.
	MainFromObjects $(target) : $(objects) ;
	TestObjects $(sources) : : true ;

	MakeLocate $(target) : $(dest) ;
	Depends $(target) : libcppunit.so ;
	Depends r5tests : $(target) ;
	LinkSharedOSLibs $(target) : libcppunit.so $(libraries) ;
	LINKFLAGS on $(target) = $(LINKFLAGS) -nostart -Xlinker -soname=\"$(target)\" ;
}

rule CommonUnitTest
{
	# CommonUnitTest <target> : <sources> : <dest> : <obos libraries> 
	#	: <r5 libraries> : <public headers>;
	# Builds a unit test for both OBOS and R5 modules.
	# <target> The name of the target.
	# <sources> The list of sources.
	# <dest> The directory for the target (as passed to FDirName).
	# <obos libraries> A list of link libraries for the OBOS tests (as passed
	# to LinkSharedOSLibs).
	# <r5 libraries> A list of link libraries for the R5 tests (as passed
	# to LinkSharedOSLibs).
	# <public headers> A list of public header dirs (as passed to
	# UsePublicHeaders).

	UnitTest $(1) : $(2) : $(3) : $(4) : $(6) ;
	R5UnitTest $(1) : $(2) : $(3) : $(5) ;
}

rule UnitTest
{
	# UnitTest <target> : <sources> : <dest> : <libraries> : <public headers>
	# Builds a unit test for an OBOS module.
	# <target> The name of the target.
	# <sources> The list of sources.
	# <dest> The directory for the target (as passed to FDirName).
	# <libraries> A list of link libraries (as passed to LinkSharedOSLibs).
	# <public headers> A list of public header dirs (as passed to
	# UsePublicHeaders).

	local target = $(1) ;
	local sources = $(2) ;
	local dest = $(3) ;
	local libraries = $(4) ;
	local headerDirs = $(5) ;
	local objects = [ FGristFiles $(sources:S=$(SUFOBJ)) ] ;

	# Our Main replacement.
	MainFromObjects $(target) : $(objects) ;
	TestObjects $(sources) : $(headerDirs) ;

	MakeLocate $(target) : [ FDirName $(OBOS_TEST_DIR) $(dest) ] ;
	Depends $(target) : libcppunit.so ;
	Depends obostests : $(target) ;
	LinkSharedOSLibs $(target) : libcppunit.so $(libraries) ;
}

rule R5UnitTest
{
	# R5UnitTest <target> : <sources> : <dest> : <libraries>
	# Builds a unit test for an R5 module. "_r5" is appended to the object
	# and the target name.
	# <target> The name of the target.
	# <sources> The list of sources.
	# <dest> The directory for the target (as passed to FDirName).
	# <libraries> A list of link libraries (as passed to LinkSharedOSLibs).

	local target = $(1)_r5 ;
	local sources = $(2) ;
	local dest = $(3) ;
	local libraries = $(4) ;
	local objects = [ R5ObjectNames $(sources) ] ;

	# Our Main replacement.
	MainFromObjects $(target) : $(objects) ;
	TestObjects $(sources) : : true ;

	MakeLocate $(target) : [ FDirName $(OBOS_TEST_DIR) $(dest) ] ;
	Depends $(target) : libcppunit.so ;
	Depends r5tests : $(target) ;
	LinkSharedOSLibs $(target) : libcppunit.so $(libraries) ;
}

rule R5ObjectNames
{
	# R5ObjectNames <sources> ;
	# Returns a list of gristed object names given a list of source file names.
	# Moreover each object names gets "_r5" inserted before the object suffix.
	local objects = $(1:S=)_r5 ;
	return [ FGristFiles $(objects:S=$(SUFOBJ)) ] ;
}

rule R5Objects
{
	# R5Objects <sources>
	# Similar to Objects, but appends "_r5" to the object file names and
	# removes `-nostdinc' from the CC and C++ flags to enable system headers.
	# <sources> The source files.

	# Remove `-nostdinc' from CCFLAGS and C++FLAGS.
	local oldCCFLAGS = $(CCFLAGS) ;
	local oldC++FLAGS = $(C++FLAGS) ;
	CCFLAGS = [ Filter $(CCFLAGS) : -nostdinc ] ;
	C++FLAGS = [ Filter $(C++FLAGS) : -nostdinc ] ;

	local sources = $(1) ;
	local source ;
	for source in [ FGristFiles $(sources) ]
	{
		local object = [ R5ObjectNames $(source) ] ;
		Object $(object) : $(source) ;
		LocalDepends obj : $(object) ;
	}

	# Reset CCFLAGS and C++FLAGS to original values.
	CCFLAGS = $(oldCCFLAGS) ;
	C++FLAGS = $(oldC++FLAGS) ;
}

rule TestObjects
{
	# TestLib <sources> : <public headers> : <r5>
	# Compiles objects for tests.
	# <sources> The list of sources.
	# <public headers> A list of public header dirs (as passed to
	# UsePublicHeaders).
	# <r5> If set, "_r5" is appended to the object file names and
	# <public headers> is ignored. Furthermore the pre-processor macro
	# TEST_R5 is defined, TEST_OBOS otherwise.

	local sources = $(1) ;
	local headerDirs = $(2) ;
	local r5 = $(3) ;
	local objects ;

	# Turn optimization off.
	if ! $(NO_TEST_DEBUG) {
		local optim = $(OPTIM) ;
		OPTIM = ;
	}

	SetupObjectsDir ;

	# compile
	if $(r5) {
		SetupR5Includes ;
		objects = [ R5ObjectNames $(sources) ] ;
		R5Objects $(sources) ;
	} else {
		SetupIncludes ;
		objects = [ FGristFiles $(sources:S=$(SUFOBJ)) ] ;
		Objects $(sources) ;
	}

	# set headers/defines
	UseCppUnitObjectHeaders $(sources) : $(objects) ;
	if $(r5) {
		ObjectDefines $(objects) : TEST_R5 ;
	} else {
		UsePublicObjectHeaders $(sources) : $(headerDirs) : $(objects) ;
		ObjectDefines $(objects) : TEST_OBOS ;
	}

	if ! $(NO_TEST_DEBUG) {
		# Turn debugging on. That is usually desired for test code.
		ObjectCcFlags $(objects) : "-g" ;
		ObjectC++Flags $(objects) : "-g" ;

		# Turn optimization on again.
		OPTIM = $(optim) ;
	}
}

rule R5SharedLibraryNames
{
	# R5SharedLibraryNames <sources> ;
	# Returns a list of shared library names given a list of file names. NO
	# GRISTING IS PERFORMED :-) However, each library names gets "_r5" inserted
	# before the shared lib suffix.
	return $(1:S=)_r5.so ;
}

rule SimpleTest
{
	# UnitTest <target> : <sources> : <libraries>
	# Builds a unit test for an OBOS module.
	# <target> The name of the target.
	# <sources> The list of sources.
	# <dest> The directory for the target (as passed to FDirName).
	# <libraries> A list of link libraries (as passed to LinkSharedOSLibs).
	# <public headers> A list of public header dirs (as passed to
	# UsePublicHeaders).

	local target = $(1) ;
	local sources = $(2) ;
	local libraries = $(3) ;
	local relPath = [ FRelPath src tests : $(SUBDIR_TOKENS) ] ;

	# Turn optimization off.
	if ! $(NO_TEST_DEBUG) {
		local optim = $(OPTIM) ;
		OPTIM = ;
	}

	SetupIncludes ;
	SetupObjectsDir ;
	MakeLocateObjects $(sources) ;
	Main $(target) : $(sources) ;
	MakeLocate $(target) : [ FDirName $(OBOS_TEST_DIR) $(relPath) ] ;
	Depends obostests : $(target) ;
	LinkSharedOSLibs $(target) : $(libraries) ;
	ObjectDefines $(sources) : TEST_OBOS ;

	if ! $(NO_TEST_DEBUG) {
		# Turn debugging on. That is usually desired for test code.
		ObjectCcFlags $(sources) : "-g" ;
		ObjectC++Flags $(sources) : "-g" ;

		# Turn optimization on again.
		OPTIM = $(optim) ;
	}
}

rule Addon
{
	# Addon <name> : <relpath> : <sources> : <is executable> : <libraries> ;
	# <name>: Name of the add-on.
	# <relpath>: Path where the add-on shall live relative to the add-on dir.
	# <sources>: Source files.
	# <is executable>: true, if the target shall be executable as well.
	# <libraries>: Libraries to be linked against.

	local isExecutable = $(4) ;
	
	SetupIncludes ;
	SetupObjectsDir ;
	Main $(1) : $(3) ;

	# Create output dir path for addon
	local targetdir;
	targetdir = [ FDirName $(OBOS_ADDON_DIR) $(2) ] ;

	MakeLocate $(1) : $(targetdir) ;

	local linkFlags = -Xlinker -soname=\"$(1)\" ;
	if $(isExecutable) != true {
		linkFlags = -nostart $(linkFlags) ;
	}
	LINKFLAGS on $(1) = [ on $(1) return $(LINKFLAGS) ] $(linkFlags) ;
	LinkSharedOSLibs $(1) : $(5) ;
}

rule R5KernelAddon
{
	# R5KernelAddon <name> : <relpath> : <sources> ;	

	local sources = $(3) ;

	Addon $(1) : $(2) : $(3) ;
	ObjectCcFlags $(sources) : -D_KERNEL_MODE=1 -no-fpic ;
	ObjectC++Flags $(sources) : -D_KERNEL_MODE=1 -no-fpic
								-fno-exceptions -fno-rtti ;
	LINKFLAGS on $(1) = [ on $(1) return $(LINKFLAGS) ] -nostdlib ;
	LinkSharedOSLibs $(1) : /boot/develop/lib/x86/_KERNEL_ ;
}

rule Translator
{
	# Translator <name> : <sources> : <libraries> ;
	SetupIncludes ;
	SetupObjectsDir ;
	Main $(1) : $(2) ;
	LinkSharedOSLibs $(1) : $(3) ;

	# Create output dir path for translator
	local targetdir;
	targetdir = [ FDirName $(OBOS_ADDON_DIR) Translators ] ;
	MakeLocate $(1) : $(targetdir) ;
}

rule MakeLocateObjects
{
	# MakeLocateObjects <sources_or_objects> ;
	
	local _objs = [ FGristFiles $(1:S=$(SUFOBJ)) ] ;
	
	for o in $(_objs)
	{
		local dir = $(o:D) ;
		if $(dir) {
			MakeLocate $(o) : [ FDirName $(LOCATE_TARGET) $(dir) ] ;
		} else {
			MakeLocate $(o) : $(LOCATE_TARGET) ;
		}
	}
}

rule StaticLibrary
{
	# StaticLibrary <name> : <sources> ;
	
	SetupIncludes ;
	SetupObjectsDir ;
	MakeLocateObjects $(2) ;
	Library lib$(<).a : $(>) ;
	MakeLocate lib$(<).a : $(OBOS_STLIB_DIR) ;
}

rule R5KernelStaticLibrary
{
	# R5KernelStaticLibrary <name> : <sources> ;
	
	local lib = lib$(1).a ;
	local sources = $(2) ;

	SetupIncludes ;
	SetupObjectsDir ;
	MakeLocateObjects $(sources) ;
	Library $(lib) : $(sources) ;
	ObjectCcFlags $(sources) : -D_KERNEL_MODE=1 -no-fpic ;
	ObjectC++Flags $(sources) : -D_KERNEL_MODE=1 -no-fpic
								-fno-exceptions -fno-rtti ;
}

rule MergeObjectFromObjects
{
	# MergeObjectFromObjects <name> : <objects> ;
	local objects = [ FGristFiles $(2) ] ;
	MakeLocate $(1) : $(LOCATE_TARGET) ;
	Depends $(1) : $(objects) ;
	LINK on $(1) = ld ;
	MergeObjectFromObjects1 $(1) : $(objects) ;
}

actions MergeObjectFromObjects1
{
	$(LINK) -r $(2) -o $(1) ;
}

rule MergeObject
{
	# MergeObject <name> : <sources> ;
	SetupIncludes ;
	SetupObjectsDir ;
	MakeLocateObjects $(2) ;
	Objects $(2) ;
	MergeObjectFromObjects $(1) : $(2:S=$(SUFOBJ)) ;
}

rule SharedLibraryFromObjects
{
	# SharedLibraryFromObjects <name> : <objects> : <libraries> ;
	local _lib = lib$(1).so ;
	MainFromObjects $(_lib) : $(2) ;
	MakeLocate $(_lib) : $(OBOS_SHLIB_DIR) ;
	LINKFLAGS on $(_lib) = [ on $(_lib) return $(LINKFLAGS) ]
						   -nostart -Xlinker -soname=\"$(_lib)\" ;
	LinkSharedOSLibs $(_lib) : $(3) ;
}

rule SharedLibrary
{
	# SharedLibrary <name> : <sources> : <libraries> ;
	SetupIncludes ;
	SetupObjectsDir ;
	MakeLocateObjects $(2) ;
	Objects $(2) ;
	SharedLibraryFromObjects $(1) : $(2:S=$(SUFOBJ)) : $(3) ;
}

rule LinkSharedOSLibs
{
	# LinkSharedOSLibs <name> : <libs> ;
	# Valid elements for <libs> are e.g. "be" or "libopenbeos.so" or
	# "/boot/.../libfoo.so". If the basename starts with "lib" or the thingy
	# has a dirname or grist, it is added to the NEEDLIBS variable (i.e. the
	# file will be bound!), otherwise it is prefixed "-l" and added to
	# LINKLIBS. If you want to specify a target that isn't a library and
	# also has neither grist nor a dirname, you can prepend "<nogrist>" as
	# grist; it will be stripped by this rule.
	
	for i in $(>)
	{
		local isfile = ;
		if $(i:D) || $(i:G) {
			isfile = true ;
			if $(i:G) = <nogrist> {
				i = $(i:G=) ;
			}
		} else {
			switch $(i:B)
			{
				# XXX: _APP_ and _KERNEL_ should not be needed for ELF.
				case _APP_ : isfile = true ;
				case _KERNEL_ : isfile = true ;
				case *.so     : isfile = true ;
				case *.a      : isfile = true ;
				case lib*	: isfile = true ;
				case *	: isfile = ;
			}
		}
		if $(isfile) {
			NEEDLIBS on $(1) = [ on $(1) return $(NEEDLIBS) ] $(i) ;
			Depends $(1) : $(i) ;
		} else {
			LINKLIBS on $(1) = [ on $(1) return $(LINKLIBS) ] -l$(i) ;
		}
	}
}

rule LinkStaticOSLibs
{
	# LinkStaticOSLibs <name> : <libs> ;
	
	for i in $(>)
	{
		LINKLIBS on $(<) = $(LINKLIBS) -l $(i) ;
	}
}

rule AddResources
{
	# AddResources <name> : <resourcefiles> ;

	local resfiles = [ FGristFiles $(2) ] ;
	SEARCH on $(resfiles) += $(SEARCH_SOURCE) ;

	for file in $(resfiles) {
		if $(file:S) = .rdef {
			local rdef = $(file) ;
			file = $(rdef:S=.rsrc) ;
			ResComp $(file) : $(rdef) ;
		}
		RESFILES on $(1) += $(file) ;
	}
}

rule ResComp
{
	# ResComp <resource file> : <rdef file> ;
	#
	# <resource file> and <rdef file> must be gristed.

	SetupObjectsDir ;

	SEARCH on $(2) += $(SEARCH_SOURCE) ;
	MakeLocate $(1) : $(LOCATE_TARGET) ;
	Depends $(1) : $(2) rc ;
	LocalClean clean : $(1) ;
	ResComp1 $(1) : rc $(2) ;
}

actions ResComp1
{
	$(2[1]) -o $(1) $(2[2-])
}

rule SourceHdrs
{
	# SourceHdrs <sources> : <headers> [ : <gristed objects> ] ;
	#
	# Is a wrapper for ObjectHdrs, that passes <sources> and <headers> or,
	# if supplied <objects> and <headers>, and also adjusts HDRSEARCH (not
	# done by ObjectHdrs).

	local sources = [ FGristFiles $(1) ] ;
	local headers = $(2) ;
	local objects = $(3) ;

	if $(objects) {
		ObjectHdrs $(objects) : $(headers) ;
	} else {
		ObjectHdrs $(sources) : $(headers) ;
	}

	# Also add the header search dirs to HDRSEARCH. Note, that these dirs
	# will be listed after the STDHDRS (if any), but that's better than not
	# being listed at all.
	HDRSEARCH on $(sources) += $(headers) ;
}

rule PublicHeaders
{
	# PublicHeaders <group list>
	#
	# Returns the directory names for the public header dirs identified by
	# <group list>.

	local list = $(1) ;
	local dirs = [ FDirName $(OBOS_TOP) headers os ] ;

	for i in $(list) {
		dirs += [ FDirName $(OBOS_TOP) headers os $(i) ] ;
	}
	return $(dirs) ;
}

rule PrivateHeaders
{
	# PrivateHeaders <group list>
	#
	# Returns the directory names for the private header dirs identified by
	# <group list>.

	local list = $(1) ;
	local dirs ;
	for i in $(list) {
		dirs += [ FDirName $(OBOS_TOP) headers private $(i) ] ;
	}
	return $(dirs) ;
}

rule LibraryHeaders
{
	# LibraryHeaders <group list>
	#
	# Returns the directory names for the library header dirs identified by
	# <group list>.

	local list = $(1) ;
	local dirs ;
	for i in $(list) {
		dirs += [ FDirName $(OBOS_TOP) headers libs $(i) ] ;
	}
	return $(dirs) ;
}

rule ArchHeaders
{
	# usage: ArchHeaders <arch> ;
	#
	# <arch> specifies the architecture (e.g. x86).

	return [ FDirName $(OBOS_TOP) headers private kernel arch $(1) ] ;
}

rule UsePublicHeaders
{
	# UsePublicHeaders <group list> ;
	#
	# Adds the public C header dirs given by <group list> to the header search
	# dirs of the subdirectory.
	# NOTE: This rule must be invoked *before* the rule that builds the
	# objects.

	UseHeaders [ PublicHeaders $(1) ] ;
}

rule UsePublicObjectHeaders
{
	# UsePublicObjectHeaders <sources> : <group list> [ : <objects> ] ;
	#
	# Adds the public C header dirs given by <group list> to the header search
	# dirs of either the object targets of <sources> or if supplied to
	# <objects>. Also adjusts HDRSEARCH of <sources>.
	# NOTE: This rule must be invoked *after* the rule that builds the objects.

	SourceHdrs $(1) : [ PublicHeaders $(2) ] : $(3) ;
}

rule UsePrivateHeaders
{
	# UsePrivateHeaders <group list> ;
	#
	# Adds the private C header dirs given by <group list> to the header search
	# dirs of the subdirectory.
	# NOTE: This rule must be invoked *before* the rule that builds the objects.

	UseHeaders [ PrivateHeaders $(1) ] ;
}

rule UsePrivateObjectHeaders
{
	# UsePrivateObjectHeaders <sources> : <group list> [ : <objects> ] ;
	#
	# Adds the private C header dirs given by <group list> to the header search
	# dirs of either the object targets of <sources> or if supplied to
	# <objects>. Also adjusts HDRSEARCH of <sources>.
	# NOTE: This rule must be invoked *after* the rule that builds the objects.

	SourceHdrs $(1) : [ PrivateHeaders $(2) ] : $(3) ;
}

rule UseHeaders
{
	# UseHeaders <headers> ;
	#
	# Adds the C header dirs <headers> to the header search
	# dirs of the subdirectory.
	# NOTE: This rule must be invoked *before* the rule that builds the objects.

	local header ;
	for header in $(1) {
		SubDirHdrs $(header) ;
	}
}

rule UseCppUnitHeaders
{
	SubDirHdrs [ FDirName $(OBOS_TOP) headers tools cppunit ] ;
}

rule UseCppUnitObjectHeaders
{
	# UseCppUnitObjectHeaders <sources> [ : <objects> ] ;
	SourceHdrs $(1) : [ FDirName $(OBOS_TOP) headers tools cppunit ] : $(2) ;
}

rule UseArchHeaders
{
	# usage: UseArchHeaders <arch> ;
	#
	# <arch> specifies the architecture (e.g. x86).
	# NOTE: This rule must be invoked *before* the rule that builds the objects.

	local headers = [ ArchHeaders $(1) ] ;
	local opt = -D$(OBOS_TARGET_DEFINE) ;

	SubDirCcFlags $(opt)  ;
	SubDirC++Flags $(opt)  ;
	HDRS += $(headers) ;
}

rule UseArchObjectHeaders
{
	# usage: UseArchObjectHeaders <sources> : <arch> : [ <objects> ] ;
	#
	# <arch> specifies the architecture (e.g. x86).
	# <sources_or_objects> Source or object files.
	# NOTE: This rule must be invoked *after* the rule that builds the objects.

	local sources = $(1) ;
	local headers = [ ArchHeaders $(2) ] ;
	local objects = $(3) ;
	local targets ;
	if $(objects) {
		targets = $(objects) ;
	} else {
		targets = $(sources) ;
	}
	local opt = -D$(OBOS_TARGET_DEFINE) ;

	ObjectCcFlags $(targets) : $(opt)  ;
	ObjectC++Flags $(targets) : $(opt)  ;
	SourceHdrs $(sources) : $(headers) : $(objects) ;
}

rule UsePosixHeaders
{
	# XXX changed to do nothing
}

rule UsePosixObjectHeaders
{
	# UsePosixObjectHeaders <sources> [ : <objects> ] ;
	#
	# Adds the POSIX header dir to the header search
	# dirs of either the object targets of <sources> or if supplied to
	# <objects>. Also adjusts HDRSEARCH of <sources>.
	# NOTE: This rule must be invoked *after* the rule that builds the objects.

	SourceHdrs $(1) : [ FDirName $(OBOS_TOP) headers posix ] : $(2) ;
}

rule UseLibraryHeaders
{
	# UseLibraryHeaders <group list> ;
	#
	# Adds the library header dirs given by <group list> to the header search
	# dirs of the subdirectory.
	# NOTE: This rule must be invoked *before* the rule that builds the objects.

	UseHeaders [ LibraryHeaders $(1) ] ;
}

rule SplitPath
{
	# SplitPath <path> ;
	# Decomposes a path into its components.
	local path = $(1:G=) ;
	local components ;
	# $(path:D) for "/" is "/". Therefore the second condition.
	while $(path:D) && $(path:D) != $(path)
	{
		# Note: $(path:B) returns "." for "..", but $(path:D=) is fine.
		components = $(path:D=) $(components) ;
		path = $(path:D) ;
	}
	components = $(path) $(components) ;
	return $(components) ;
}

rule PrependObjectHdrs
{
	# PrependObjectHdrs <objects_or_sources> : <dirs> ;
	# Prepends <dirs> to the list of header search dirs of the objects
	# specified by <objects_or_sources>. The HDRS variable will not be
	# changed, only CCHDRS.
	# Note: A subsequent ObjectHdrs invocation will therefore undo the
	# effect of this rule.
	# NOTE: This is a hack.

	local objects = [ FGristFiles $(1:S=$(SUFOBJ)) ] ;
	local dirs = $(2) ;
	for object in $(objects) {
		# Don't change HDRS to avoid screwing up the header scanning.
		PREPENDED_HDRS on $(object)
			= $(dirs) [ on $(object) return $(PREPENDED_HDRS) ] ;
		CCHDRS on $(object)
			= [ FIncludes [ on $(object) return $(PREPENDED_HDRS) $(HDRS) ] ] ;
	}
}

rule SymLink
{
	# SymLink <target> : <source> ;
	# Links <target> to <source>.
	# <source> is the exact link contents. No binding is done.
	LINKCONTENTS on $(1) = $(2) ;
	SymLink1 $(1) ;
	LocalDepends all : $(target) ;
}

actions SymLink1
{
	$(RM) "$(1)" && $(LN) -s "$(LINKCONTENTS)" "$(1)"
}

rule RelSymLink
{
	# RelSymLink <link> : <link target>
	# Creates a relative symbolic link from <link> to <link target>.
	# <link> and <link target> can be usual targets. They may have a grist
	# and don't need to have any dirname. Their LOCATE variables are used to
	# find their locations.

	local target = $(1) ;
	local source = $(2) ;
	local targetDir = [ on $(target) FDirName $(LOCATE[1]) $(target:D) ] ;
	local sourceDir = [ on $(source) FDirName $(LOCATE[1]) $(source:D) ] ;
	local sourcePath = $(source:G=) ;
	sourcePath = $(sourcePath:D=$(sourceDir)) ;
	local targetDirComponents = [ SplitPath $(targetDir) ] ;
	local sourceComponents = [ SplitPath $(sourcePath) ] ;

	SymLink $(target)
		: [ FRelPath $(targetDirComponents) : $(sourceComponents) ] ;
	NOUPDATE $(target) ;
	Depends $(target) : $(source) ;
	LocalClean clean : $(target) ;
}

rule AbsSymLink
{
	# AbsSymLink <link> : <link target> : <link dir>
	# Creates an absolute symbolic link from <link> to <link target>.
	# <link> and <link target> must be usual targets. If <link dir> is
	# given, then it is set as LOCATE directory on <link>.

	LocalDepends files : $(1) ;
	Depends $(1) : $(2) ;
	if $(3) {
		MakeLocate $(1) : $(3) ;
	}
	SEARCH on $(2) += $(SEARCH_SOURCE) ;
	LocalClean clean : $(1) ;
}

actions AbsSymLink
{
	target="$(2)"
	case "$target" in
		/*) ;;
		*) target=`pwd`/"$target";;
	esac
	$(RM) "$(1)" && $(LN) -s "$target" "$(1)"
}

rule OBOSInstall
{
	# Usage: OBOSInstall <[ install [ and uninstall ] pseudotarget ]>
	#					 : <directory> : <sources to install>
	#					 : [ <installgrist> ] : [ <install rule> ] ;
	local install = $(1[1]) ;
	install ?= install ;
	local uninstall = $(1[2]) ;
	uninstall ?= un$(install) ;
	local dir = $(2) ;
	local sources = $(3) ;
	local installgrist = $(4) ;
	installgrist ?= $(INSTALLGRIST) ;
	local installRule = $(5) ;
	installRule ?= Install ;
	local targets = $(sources:G=$(installgrist)) ;

	Depends $(install) : $(targets) ;
	Clean $(uninstall) : $(targets) ;

	SEARCH on $(sources) += $(SEARCH_SOURCE) ;
	MakeLocate $(targets) : $(dir) ;

	local source ;
	for source in $(sources) {
		local target = $(source:G=$(installgrist)) ;

		Depends $(target) : $(source) ;
		$(installRule) $(target) : $(source) ;

		if [ on $(target) return $(MODE) ] {
			Chmod $(target) ;
		}

		if $(OWNER) && $(CHOWN) { 
			Chown $(target) ;
			OWNER on $(target) = $(OWNER) ;
		}

		if $(GROUP) && $(CHGRP) { 
			Chgrp $(target) ;
			GROUP on $(target) = $(GROUP) ;
		}
	}
}

rule OBOSInstallAbsSymLink
{
	# Usage: OBOSInstallAbsSymLink <[ install [ and uninstall ] pseudotarget ]>
	#							   : <directory> : <sources to install>
	#							   : [ <installgrist> ] ;
	OBOSInstall $(1) : $(2) : $(3) : $(4) : AbsSymLink ;
}

rule InstallRelSymLinkAdapter
{
	# InstallRelSymLinkAdapter <link> : <link target>
	if ! [ on $(2) return $(TARGET) ] {
		TARGET on $(2) = [ on $(2) return $(SEARCH) ] ;
	}
	RelSymLink $(1) : $(2) ;
}

rule OBOSInstallRelSymLink
{
	# Usage: OBOSInstallRelSymLink <[ install [ and uninstall ] pseudotarget ]>
	#							   : <directory> : <sources to install>
	#							   : [ <installgrist> ] ;
	OBOSInstall $(1) : $(2) : $(3) : $(4) : InstallRelSymLinkAdapter ;
}


#-------------------------------------------------------------------------------
# Low-level OBOS utility rules
#-------------------------------------------------------------------------------
rule SetupObjectsDir
{
	local rel_objectsdir;
	
	# Copy subdir tokens except the first, as that will be "sources", and we
	# do not want to include that :)
	rel_objectsdir = [ FDirName $(SUBDIR_TOKENS[2-]) ] ;
	LOCATE_TARGET = [ FDirName $(OBOS_OBJECT_TARGET) $(rel_objectsdir) ] ;
	LOCATE_SOURCE = $(LOCATE_TARGET) ;
	SEARCH_SOURCE = [ Filter $(SEARCH_SOURCE) : $(LOCATE_TARGET) ]
					$(LOCATE_TARGET) ;
}

#-------------------------------------------------------------------------------
# Link rule/action are overwritten as they don't handle linking files who's name 
# contain spaces very well. Also adds resources and version to executable.
#-------------------------------------------------------------------------------
rule Link
{
	# Note: RESFILES must be set before invocation.
	MODE on $(<) = $(EXEMODE) ;
	on $(1) XRes $(1) : $(RESFILES) ;
	Chmod $(<) ;
	SetType $(1) ;
	MimeSet $(1) ;
	SetVersion $(1) ;
}

actions Link bind NEEDLIBS
{
	$(LINK) $(LINKFLAGS) -o "$(1)" $(UNDEFS) "$(2)" "$(NEEDLIBS)" $(LINKLIBS) ;
}

rule LexC++
{
	Depends $(1) : $(2) ;
	MakeLocate $(1) : $(LOCATE_SOURCE) ;
	LocalClean clean : $(1) ;
}

actions LexC++
{
	$(LEX) -o$(1) $(2)
}

rule Bison
{
	local _h ;

	_h = $(1).h ;

	MakeLocate $(<) $(_h) : $(LOCATE_SOURCE) ;

    Depends $(<) $(_h) : $(>) ;
    Bison1 $(<) $(_h) : $(>) ;
    LocalClean clean : $(<) $(_h) ;

	# make sure someone includes $(_h) else it will be
	# a deadly independent target

	Includes $(<) : $(_h) ;
}

actions Bison1
{
	bison $(YACCFLAGS) -o $(1[1]) $(2)
}

# BeOS specific rules

rule XRes
{
	# XRes <target> : <resource files>
	if $(2)
	{
		Depends $(1) : $(2) ;
		XRes1 $(1) : $(2) ;
	}
}

rule XRes1 { }

rule SetVersion
{
	# SetVersion <target>
}

rule SetType
{
	# SetType <target>
}

rule MimeSet
{
	# SetType <target>
}


if $(OS) = BEOS
{

actions XRes1
{
	xres -o "$(1)" "$(2)" ;
}

actions SetVersion
{
	setversion "$(1)" -system $(OBOS_BUILD_VERSION) -short "$(OBOS_BUILD_DESCRIPTION)" ;
}

actions SetType
{
	settype -t $(OBOS_TARGET_TYPE) "$(1)" ;
}

actions MimeSet
{
	mimeset -f "$(1)" ;
}

}	# if BEOS


rule assemble
{
	Depends $(<) : $(>) ;
	ASFLAGS on $(<) += $(ASFLAGS) $(SUBDIRASFLAGS) ;
	ASHDRS on $(<) = [ FIncludes $(SEARCH_SOURCE) $(SUBDIRHDRS) $(HDRS) ] ;
}

actions assemble
{
	$(CC) -c "$(2)" -O2 $(ASFLAGS) -D_ASSEMBLER $(KERNEL_CCFLAGS) $(ASHDRS) -o "$(1)" ;
}

# Overridden to allow spaces in file names.
actions Chmod1
{
	$(CHMOD) "$(MODE)" "$(1)"
}

# Overridden to allow spaces in file names.
actions piecemeal together existing Clean
{
	$(RM) "$(>)"
}

rule ObjectReference
{
	# ObjectReference <reference object> : <source object>
	# Makes <reference object> refer to the same file as <source object>.
	# The filenames must of course be identical.
	# <source object> must have already been LOCATEd.

	local ref = $(1) ;
	local source = $(2) ;
	if $(ref) != $(source) {
		Depends $(ref) : $(source) ;
		LOCATE on $(ref) = [ on $(source) return $(LOCATE) ] ;
	}
}

rule ObjectReferences
{
	# ObjectReferences <source objects>
	# Creates local references to <source objects>, i.e. identifiers with the
	# current grist referring to the same files. <source objects> must have
	# already been LOCATEd.

	local source ;
	for source in $(1) {
		ObjectReference [ FGristFiles $(source) ] : $(source) ;
	}
}

rule Filter
{
	# Filter <list> : <excludes> ;
	# Removes all occurrences of <excludes> in <list>.

	local list = $(1) ;
	local excludes = $(2) ;
	local newList ;
	local item ;
	for item in $(list) {
		local skip ;
		local exclude ;
		for exclude in $(excludes) {
			if $(item) = $(exclude) {
				skip = true ;
			}
		}
		if ! $(skip) {
			newList += $(item) ;
		}
	}
	return $(newList) ;
}


## Kernel stuff!

rule SetupKernel
{
	# Usage SetupKernel <sources_or_objects> : <extra_cc_flags>;

	local _objs = [ FGristFiles $(1:S=$(SUFOBJ)) ] ;

	#Setup Kernel header directories
	local public_kernel_includes = add-ons/file_system add-ons/graphics device drivers kernel storage support ;
	local private_kernel_includes = kernel libroot ;
	# Use posix headers directory
	HDRS = [ FDirName $(OBOS_TOP) headers posix ] ;
	# Use public OS header directories
	HDRS += [ PublicHeaders $(public_kernel_includes) ] ;
	# Use private directories
	HDRS += [ PrivateHeaders $(private_kernel_includes) ] ;
	# The platform dependent headers.
	HDRS += $(PLATFORM_HEADERS) ;

	UseArchHeaders $(OBOS_ARCH) ;

	SetupObjectsDir ;
	
	CCFLAGS on $(_objs) = $(KERNEL_CCFLAGS) $(2) ;
	C++FLAGS on $(_objs) = $(KERNEL_C++FLAGS) $(2) ;
}

rule KernelObjects
{
	SetupKernel $(1) : $(2) ;

	Objects $(1) ;
}

rule KernelLd
{
	# KernelLd <name> : <objs> : <linkerscript> : <args> : <gcc_off> : <config_section> ;

	SetupKernel $(2) ;
	LINK on $(1) = ld ;

	LINKFLAGS on $(1) = $(4) ;
	if $(3) { LINKFLAGS on $(1) += --script=$(3) ; }

	# Remove any preset LINKLIBS
	LINKLIBS on $(1) =  ;

	# Show that we depend on the libraries we need
	LocalClean clean : $(1) ;
	LocalDepends all : $(1) ;
	Depends $(1) : $(2) ;

	if $(6) {
		for i in $(6) {
			KernelConfigSection $(i) : elf32 : $(1) ;
		}
	}
	
	MakeLocate $(1) : $(LOCATE_TARGET) ; 

	# Add the platform specific static libs (libgcc.a).
	if ! $(5) {
		LINKLIBS on $(1) += $(PLATFORM_LINKLIBS) ;
	}
}

actions KernelLd
{
	$(LINK) $(LINKFLAGS) -o "$(1)" "$(2)" $(LINKLIBS) ;
}

rule KernelMergeObject
{
	# KernelMergeObject <name> : <sources> : <extra CFLAGS> ;

	SetupKernel $(2) : $(3) ;

	MakeLocateObjects $(2) ;
	Objects $(2) ;
	MergeObjectFromObjects $(1) : $(2:S=$(SUFOBJ)) ;
}

rule KernelStaticLibrary
{
	# Usage KernelStaticLibrary <name> : <sources> : <extra cc flags>  ;
	# This is designed to take a set of sources and libraries and create
	# a file called lib<name>.a

	SetupKernel $(2) : $(3) ;

	MakeLocateObjects $(2) ;
	Library $(1) : $(2) ;
}

rule KernelStaticLibraryObjects
{
	# Usage KernelStaticLibrary <name> : <sources> ;
	# This is designed to take a set of sources and libraries and create
	# a file called <name>

	SetupKernel $(2) ;

	# Show that we depend on the libraries we need
	LocalClean clean : $(1) ;
	LocalDepends all : $(1) ;
	Depends $(1) : $(2) ;

	MakeLocate $(1) : $(LOCATE_TARGET) ;
}

actions KernelStaticLibraryObjects
{
	ar -r "$(1)" "$(2)" ;
}

rule BuildPlatformMain
{
	# Usage BuildPlatformMain <target> : <sources> ;
	SetupObjectsDir ;
	SetupDefaultIncludes ;

	# Remove `-nostdinc' from CCFLAGS and C++FLAGS.
	local oldCCFLAGS = $(CCFLAGS) ;
	local oldC++FLAGS = $(C++FLAGS) ;
	CCFLAGS = [ Filter $(CCFLAGS) : -nostdinc ] ;
	C++FLAGS = [ Filter $(C++FLAGS) : -nostdinc ] ;

	Main $(1) : $(2) ;

	# Reset CCFLAGS and C++FLAGS to original values.
	CCFLAGS = $(oldCCFLAGS) ;
	C++FLAGS = $(oldC++FLAGS) ;
}

rule BuildPlatformTest
{
	# Usage BuildPlatformTest <target> : <sources> ;

	local target = $(1) ;
	local sources = $(2) ;

	BuildPlatformMain $(target) : $(sources) ;
	local relPath ;
	if [ FIsPrefix src tests : $(SUBDIR_TOKENS) ] {
		relPath = $(SUBDIR_TOKENS[3-]) ;
	} else {
		relPath = $(SUBDIR_TOKENS[2-]) ;
	}
	MakeLocate $(target) : [ FDirName $(OBOS_TEST_DIR) $(relPath) ] ;
}

rule KernelConfigSection
{
	# KernelConfigSection <section> : <type> : <file>  ;

	SECTION_NAMES on $(OBOS_KERNEL_CONFIG) += $(1) ;
	SECTION_TYPES on $(OBOS_KERNEL_CONFIG) += $(2) ;
	SECTION_FILES on $(OBOS_KERNEL_CONFIG) += $(3) ;

	Depends $(OBOS_KERNEL_CONFIG) : $(3) ;
}

rule WriteKernelConfig
{
	# usage: WriteKernelConfig <target> ;

	LocalDepends files : $(1) ;
	
	MakeLocate $(1) : $(OBOS_OBJECT_TARGET) ;
	
	LocalClean clean : $(1) ;
}

actions WriteKernelConfig bind SECTION_FILES
{
	target="$(1)"
	echo "# OpenBeOS Kernel Config File" > "$target"
	echo "# Automatically generated - do not edit!" >> "$target"
	count=0
	for section in "$(SECTION_NAMES)" ; do
		count=`expr $count + 1`
		eval section$count="$section"
	done
	i=1
	for type in "$(SECTION_TYPES)" ; do
		eval type$i="$type"
		i=`expr $i + 1`
	done
	i=1
	for file in "$(SECTION_FILES)" ; do
		eval file$i="$file"
		i=`expr $i + 1`
	done
	for i in `seq $count` ; do
		eval section="\$section$i"
		eval type="\$type$i"
		eval file="\$file$i"
		echo "" >> "$target"
		echo "["$section"]" >> "$target"
		echo "type="$type >> "$target"
		case "$file" in
			/*) ;;
			*) file=`pwd`/"$file";;
		esac
		echo "file="$file >> "$target"
	done
}

rule BuildKernel
{
	# Usage BuildKernel <target> : <config_file> ;
	local kernel = $(1) ;
	local configFile = $(2) ;
	local bootmaker = bootmaker ;
	
	LocalDepends all : $(kernel) ;
	Depends $(kernel) : $(configFile) $(bootmaker) ;
	LocalClean clean : $(kernel) ;
	MakeLocate $(kernel) : $(LOCATE_TARGET) ;

	BOOT_MAKER on $(kernel) = $(bootmaker) ;
}

actions BuildKernel bind BOOT_MAKER
{
	"$(BOOT_MAKER)" --strip-debug --strip-binary strip "$(2)" -o "$(1)" ;
	echo ""
	echo "Kernel linked!"
	echo ""
}

rule KernelFloppyImage
{
	# Usage KernelFloppyImage <target> : <kernel> : <bootblock> ;
	local floppy = $(1) ;
	local kernel = $(2) ;
	local bootblock = $(3) ;
	local makeflop = makeflop ;

	LocalDepends all : $(floppy) ;
	Depends $(floppy) : $(kernel) $(bootblock) $(makeflop) ;
	LocalClean clean : $(floppy) ;
	MakeLocate $(floppy) : $(OBOS_OBJECT_TARGET) ;

	BOOT_BLOCK on $(floppy) = $(bootblock) ;
	MAKE_FLOP on $(floppy) = $(makeflop) ;
}

# This may be a bit verbose, but I think it's useful to show what's
# going on, at least in this early stage of development.
actions KernelFloppyImage bind BOOT_BLOCK bind MAKE_FLOP
{
	"$(MAKE_FLOP)" "-p $(shell expr 18 \* 2 \* 512)" "$(BOOT_BLOCK)" "$(2)" "$(1)" ;

	echo ""
	echo "*************************************************"
	echo "*         Kernel build completed!               *"
	echo "*    Boot image for a 1.44M floppy created      *"
	echo "*************************************************"
	echo ""
	echo "Floppy image is $(1)"
	echo "The following command will write it to a floppy on BeOS"
	echo "  dd if=$(1) of=/dev/disk/floppy/raw bs=18k"
	echo "Alternatively you can run"
	echo "  ./configure --floppy /dev/disk/floppy/raw"
	echo "once and build + write the image subsequently via"
	echo "  jam installfloppy"
	echo ""
}

rule InstallFloppy
{
	# InstallFloppy <target> : <floppy>
	# "dd"s <floppy> to $(FLOPPY_PATH).

	local target = $(1) ;
	local floppy = $(2) ;

	NotFile $(target) ;
	Always $(target) ;
	Depends $(target) : $(floppy) ;
}

actions InstallFloppy
{
	if [ -z $(FLOPPY_PATH) ] ; then
		echo "Can't install floppy: FLOPPY_PATH not set."
		echo "run: ./configure --floppy <floppy path>"
		echo
		exit 0
	fi
	dd if=$(2) of=$(FLOPPY_PATH) bs=18k
}

#-------------------------------------------------------------------------------
# FreeType 2 specific rules and variables
#-------------------------------------------------------------------------------

FT2_INCLUDE = [ FDirName $(OBOS_TOP) headers libs freetype2 ] ;
FT2_SRC     = [ FDirName $(OBOS_TOP) src libs freetype2 ] ;

FT2_LIB     = freetype ;

FT2_COMPONENTS ?= gzip       # support for gzip-compressed files.
                  autohint   # auto-hinter
                  base       # base component (public APIs)
                  bdf        # BDF font driver
                  cache      # cache sub-system
                  cff        # CFF/CEF font driver
                  cid        # Postscript CID-keyed font driver
                  pcf        # PCF font driver
                  pfr        # PFR/TrueDoc font driver
                  psaux      # Common Postscript routines module
                  pshinter   # Postscript hinter module
                  psnames    # Postscript names handling
                  raster     # Monochrome rasterizer
                  smooth     # Anti-aliased rasterizer
                  sfnt       # SFNT-based format support routines
                  truetype   # TrueType font driver
                  type1      # Postscript Type 1 font driver
                  type42     # Postscript Type 42 (embedded TrueType) driver
                  winfonts   # Windows FON/FNT font driver
                  ;

rule UseFreeTypeHeaders
{
	SubDirHdrs $(FT2_INCLUDE) ;
}

rule UseFreeTypeObjectHeaders
{
	# UseFreeTypeObjectHeaders <sources> [ : <objects> ] ;
	SourceHdrs $(1) : $(FT2_INCLUDE) : $(2) ;
}

rule FT2_SubDir
{
	# FT2_SubDir <dir>
	# <dir>: Components of a directory in the original hierarchy.
	local dir = $(1) ;
	local topDir ;
	switch $(dir[1])
	{
		case "include"	: topDir = $(FT2_INCLUDE) ;
		case src		: topDir = $(FT2_SRC) ;
		case *			: ECHO "Unknown FreeType2 directory: " $(dir) ;
	}
	return [ FDirName $(topDir) $(dir[2-]) ] ;
}

rule FT2_Library
{
	# FT2_Library <libname> : <sources>
	# Builds objects from sources and adds the objects to the list of objects
	# to be linked into the library.
	# <libname> The name of the library.
	# <sources> The sources.

	local library = lib$(1).so ;
	local sources = $(2) ;
	SetupIncludes ;
	SetupObjectsDir ;
	MakeLocateObjects $(sources) ;
	Objects $(sources) ;
	LIBRARY_OBJECTS on $(library) += [ FGristFiles $(sources:S=$(SUFOBJ)) ] ;
}

rule FT2_LinkLibrary
{
	# FT2_LinkLibrary <libname>
	# Links the library from the objects build with FT2_LIBRARY before.

	local library = lib$(1).so ;
	local objects = [ on $(library) return $(LIBRARY_OBJECTS) ] ;
	ObjectReferences $(objects) ;
	objects = [ FGristFiles $(objects) ] ;
	SharedLibraryFromObjects $(1) : $(objects) ;
}