rule SymLink
{
	# SymLink <target> : <source> : <makeDefaultDependencies> ;
	# Links <target> to <source>.
	# <source> is the exact link contents. No binding is done.
	# <makeDefaultDependencies> If true, <target> will be made a dependency
	# of the `all' pseudo target, i.e. it will be made by default, and removed
	# on `jam clean'.

	local target = $(1) ;
	local source = $(2) ;
	local makeDefaultDependencies = $(3) ;
	if ! $(makeDefaultDependencies) {
		makeDefaultDependencies = true ;
	}
	LINKCONTENTS on $(target) = $(source) ;
	SymLink1 $(target) ;
	if $(makeDefaultDependencies) = true {
		LocalDepends files : $(target) ;
		LocalClean clean : $(target) ;
	}
}

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

rule RelSymLink
{
	# RelSymLink <link> : <link target> : <makeDefaultDependencies> ;
	# 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.
	# <makeDefaultDependencies> If true (which is the default), <link> will be
	# made a dependency of the `files' pseudo target, i.e. it will be made by
	# default, and removed on `jam clean'.

	local target = $(1) ;
	local source = $(2) ;
	local makeDefaultDependencies = $(3) ;
	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 = [ FSplitPath $(targetDir) ] ;
	local sourceComponents = [ FSplitPath $(sourcePath) ] ;

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

rule AbsSymLink
{
	# AbsSymLink <link> : <link target> : <link dir>
	#			: <makeDefaultDependencies> ;
	# 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>.
	# <makeDefaultDependencies> If true (which is the default), <link> will be
	# made a dependency of the `files' pseudo target, i.e. it will be made by
	# default, and removed on `jam clean'.

	local makeDefaultDependencies = $(4) ;
	if ! $(makeDefaultDependencies) {
		makeDefaultDependencies = true ;
	}

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

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

rule HaikuInstall installAndUninstall : dir : sources : installgrist
	: installRule : targets
{
	# Usage: HaikuInstall <[ install [ and uninstall ] pseudotarget ]>
	#	: <directory> : <sources to install> : [ <installgrist> ]
	#	: [ <install rule> ] : [ <targets> ] ;

	local install = $(installAndUninstall[1]) ;
	install ?= install ;
	local uninstall = $(installAndUninstall[2]) ;
	uninstall ?= un$(install) ;
	installgrist ?= $(INSTALLGRIST) ;
	installRule ?= Install ;

	targets ?= $(sources) ;
	targets = $(targets:G=$(installgrist)) ;

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

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

	local source ;
	for source in $(sources) {
		local target = $(targets[1]) ;
		targets = $(targets[2-]) ;

		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 InstallAbsSymLinkAdapter
{
	# InstallAbsSymLinkAdapter <link> : <link target>
	if ! [ on $(2) return $(TARGET) ] {
		TARGET on $(2) = [ on $(2) return $(SEARCH) ] ;
	}
	AbsSymLink $(1) : $(2) : : false ;
}

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

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

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


rule UnarchiveObjects
{
	# UnarchiveObjects <target objects> : <static object>

	MakeLocateArch $(1) ;
	Depends $(1) : $(2) ;
	SEARCH on $(2) = $(SEARCH_SOURCE) ;
}

actions UnarchiveObjects
{
	( cd $(1[1]:D) && $(TARGET_AR) $(TARGET_UNARFLAGS) "$(2)" $(1:BS) )
}


rule ExtractArchive directory : entries : archiveFile : grist
{
	# ExtractArchive <directory> : <entries> : <archiveFile> [ : <grist> ]
	#
	# Extract the archive file target <archiveFile> to directory <directory>.
	# The rule can be called multiple times for different <entries> for the same
	# <directory> and <archiveFile> combo.
	#
	# <directory> - The directory into which to extract the archive file. The
	#               directory is created by this rule and it is the target
	#               that the extract action is associated with.
	# <entries>   - The entries of the archive file one is interested in. The
	#               rule always extracts the complete archive file, from the
	#               given entries the rule creates targets (using <grist>)
	#               representing the extracted entries. Those targets are
	#               returned by the rule.
	# <archiveFile> - The archive file target to extract.
	# <grist>     - The grist used to create targets from <entries>. Defaults to
	#               "extracted".

	grist ?= extracted ;

	# Turn the entries into targets to build.
	local targets ;
	local entry ;
	for entry in $(entries) {
		local target = $(entry:G=$(grist)) ;
		targets += $(target) ;
	}

	LOCATE on $(targets) = $(directory) ;
	Depends $(targets) : $(directory) $(archiveFile) ;
	NoUpdate $(targets) ;

	# one-time initialization for the main target (the directory)
	if ! [ on $(directory) return $(INITIALIZED) ] {
		# make sure the parent dir exists
		local parentDir = $(directory:PG=dir) ;
		Depends $(directory) : $(parentDir) ;
		MkDir $(parentDir) ;

		NoUpdate $(directory) ;
		Depends $(directory) : $(archiveFile) ;
		switch $(archiveFile:S)
		{
			case .zip 	: ExtractZipArchive1 $(targets) : $(directory)
							$(archiveFile) ;
			case .tgz 	: ExtractTarArchive1 $(targets) : $(directory)
							$(archiveFile) ;
			case ""		: Exit "ExtractArchive: No archive passed" ;
			case * 		: Exit "ExtractArchive: Unhandled archive extension:
							$(archiveFile:S)" ;
		}
		INITIALIZED on $(directory) = 1 ;
	}

	# Use a dummy rule so that it looks to jam like the targets are actually
	# built from the directory target.
	ExtractArchiveDummy $(targets) : $(directory) ;

	return $(targets) ;
}

actions ExtractZipArchive1
{
	mkdir -p $(2[1])
	unzip -q -u -o -d $(2[1]) $(2[2])
}

actions ExtractTarArchive1
{
	mkdir -p $(2[1])
	tar -C $(2[1]) -xf $(2[2])
}

actions ExtractArchiveDummy
{
}

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 CopySetHaikuRevision target : source
{
	# CopySetHaikuRevision <target> : <source>
	#
	# Copy <source> to <target>, writing the SVN revision of the working root
	# directory into the haiku revision section of <target>.
	#
	# <target> - Output file target. Gristed and located target.
	# <source> - ELF object to be copied. Gristed and located target.

	# If existent, make the target depend on the .svn/entries file in the
	# root directory, so it gets updated when the revision changes due to
	# "svn up".
	if [ Glob [ FDirName $(HAIKU_TOP) .svn ] : entries ] {
		local svnEntries = <haiku-rootdir-svn>entries ;
		SEARCH on $(svnEntries) = [ FDirName $(HAIKU_TOP) .svn ] ;
		Depends $(target) : $(svnEntries) ;
	} else if [ Glob [ FDirName $(HAIKU_TOP) .git ] : index ] {
		local gitIndex = <haiku-rootdir-git>index ;
		SEARCH on $(gitIndex) = [ FDirName $(HAIKU_TOP) .git ] ;
		Depends $(target) : $(gitIndex) ;
	} else if [ Glob [ FDirName $(HAIKU_TOP) .hg ] : store ] {
		local hgStore = <haiku-rootdir-hg>store ;
		SEARCH on $(hgStore) = [ FDirName $(HAIKU_TOP) .hg ] ;
		Depends $(target) : $(hgStore) ;
	}

	HAIKU_INCLUDE_IN_IMAGE on $(target)
		= [ on $(source) return $(HAIKU_INCLUDE_IN_IMAGE) ] ;

	Depends $(target) : <build>copyattr <build>set_haiku_revision $(source) ;
	CopySetHaikuRevision1 $(target)
		: <build>copyattr <build>set_haiku_revision $(source) ;
}

actions CopySetHaikuRevision1
{
	$(HOST_ADD_BUILD_COMPATIBILITY_LIB_DIR)

	. $(HAIKU_TOP)/build/scripts/determine_haiku_revision
	determineHaikuRevision $(HAIKU_TOP) $(HAIKU_BUILD_OUTPUT_DIR)

	$(2[1]) --data $(2[3]) $(1) &&
	$(2[2]) $(1) ${revision}
}

rule DataFileToSourceFile sourceFile : dataFile : dataVariable : sizeVariable
{
	sourceFile = [ FGristFiles $(sourceFile) ] ;
	MakeLocateCommonPlatform $(sourceFile) ;

	sizeVariable ?= $(dataVariable)Size ;

	DATA_VARIABLE on $(sourceFile) = $(dataVariable) ;
	SIZE_VARIABLE on $(sourceFile) = $(sizeVariable) ;

	Depends $(sourceFile) : <build>data_to_source $(dataFile) ;
	DataFileToSourceFile1 $(sourceFile) : <build>data_to_source $(dataFile) ;
	LocalClean clean : $(sourceFile) ;
}

actions DataFileToSourceFile1
{
	$(HOST_ADD_BUILD_COMPATIBILITY_LIB_DIR)
	$(2[1]) $(DATA_VARIABLE) $(SIZE_VARIABLE) $(2[2]) $(1)
}

rule DownloadLocatedFile target : url
{
	URL on $(target) = $(url) ;

	DownloadLocatedFile1 $(target) ;
}

actions DownloadLocatedFile1
{
	wget -O "$(1)" $(URL)
}

rule DownloadFile file : url
{
	file = $(file:G=download) ;

	# Request the download only once.
	if [ on $(file) return $(HAIKU_FILE_DOWNLOAD) ] {
		return $(file) ;
	}

	HAIKU_FILE_DOWNLOAD on $(file) = 1 ;

	MakeLocate $(file) : $(HAIKU_DOWNLOAD_DIR) ;
	DownloadLocatedFile $(file) : $(url) ;

	return $(file) ;
}

