################################################################################
# Generic package infrastructure
#
# This file implements an infrastructure that eases development of
# package .mk files. It should be used for all non-autotools based
# packages. Autotools-based packages should use the specialized
# autotools infrastructure in package/Makefile.autotools.in.
#
# See the Buildroot documentation for details on the usage of this
# infrastructure
#
# In terms of implementation, this generic infrastructure requires the
# .mk file to specify:
#
#   1. Metadata informations about the package: name, version,
#      download URL, etc.
#
#   2. Description of the commands to be executed to configure, build
#      and install the package
#
# The autotools infrastructure specializes this generic infrastructure
# by already implementing the configure, build and install steps.
################################################################################

# UPPERCASE Macro -- transform its argument to uppercase and replace dots and
# hyphens to underscores

# Heavily inspired by the up macro from gmsl (http://gmsl.sf.net)
# This is approx 5 times faster than forking a shell and tr, and
# as this macro is used a lot it matters
# This works by creating translation character pairs (E.G. a:A b:B)
# and then looping though all of them running $(subst from,to,text)
[FROM] := a b c d e f g h i j k l m n o p q r s t u v w x y z . -
[TO]   := A B C D E F G H I J K L M N O P Q R S T U V W X Y Z _ _

UPPERCASE = $(strip $(eval __tmp := $1) \
     $(foreach c, $(join $(addsuffix :,$([FROM])),$([TO])), \
	$(eval __tmp :=	\
		$(subst $(word 1,$(subst :, ,$c)),$(word 2,$(subst :, ,$c)),\
			$(__tmp)))) \
     $(__tmp))

define KCONFIG_ENABLE_OPT
       $(SED) "/\\<$(1)\\>/d" $(2)
       echo "$(1)=y" >> $(2)
endef

define KCONFIG_SET_OPT
       $(SED) "/\\<$(1)\\>/d" $(3)
       echo "$(1)=$(2)" >> $(3)
endef

define KCONFIG_DISABLE_OPT
       $(SED) "/\\<$(1)\\>/d" $(2)
       echo "# $(1) is not set" >> $(2)
endef

# Define extractors for different archive suffixes
INFLATE.bz2  = $(BZCAT)
INFLATE.gz   = $(ZCAT)
INFLATE.tbz  = $(BZCAT)
INFLATE.tbz2 = $(BZCAT)
INFLATE.tgz  = $(ZCAT)
INFLATE.tar  = cat

# MESSAGE Macro -- display a message in bold type
MESSAGE = echo "$(TERM_BOLD)>>> $($(PKG)_NAME) $($(PKG)_VERSION) $(1)$(TERM_RESET)"
TERM_BOLD := $(shell tput smso)
TERM_RESET := $(shell tput rmso)

# Download method commands
WGET:=$(call qstrip,$(BR2_WGET)) $(QUIET)
SVN:=$(call qstrip,$(BR2_SVN)) $(QUIET)
BZR:=$(call qstrip,$(BR2_BZR)) $(QUIET)
GIT:=$(call qstrip,$(BR2_GIT)) $(QUIET)

# Default spider mode is 'DOWNLOAD'. Other possible values are 'SOURCE_CHECK'
# used by the _source-check target and 'SHOW_EXTERNAL_DEPS', used by the
# external-deps target.
DL_MODE=DOWNLOAD

DL_DIR=$(call qstrip,$(BR2_DL_DIR))
ifeq ($(DL_DIR),)
DL_DIR:=$(TOPDIR)/dl
endif

################################################################################
# The DOWNLOAD_{GIT,SVN,BZR} helpers are in charge of getting a
# working copy of the source repository for their corresponding SCM,
# checking out the requested version / commit / tag, and create an
# archive out of it. DOWNLOAD_WGET is the normal wget-based download
# mechanism.
#
# The SOURCE_CHECK_{GIT,SVN,BZR,WGET} helpers are in charge of simply
# checking that the source is available for download. This can be used
# to make sure one will be able to get all the sources needed for
# one's build configuration.
#
# The SHOW_EXTERNAL_DEPS_{GIT,SVN,BZR,WGET} helpers simply output to
# the console the names of the files that will be downloaded, or path
# and revision of the source repositories, producing a list of all the
# "external dependencies" of a given build configuration.
################################################################################

define DOWNLOAD_GIT
	test -e $(DL_DIR)/$($(PKG)_SOURCE) || \
	(pushd $(DL_DIR) > /dev/null && \
	$(GIT) clone $($(PKG)_SITE) $($(PKG)_BASE_NAME) && \
	pushd $($(PKG)_BASE_NAME) > /dev/null && \
	$(GIT) archive --format=tar --prefix=$($(PKG)_BASE_NAME)/ $($(PKG)_DL_VERSION) | \
		gzip -c > $(DL_DIR)/$($(PKG)_SOURCE) && \
	popd > /dev/null && \
	rm -rf $($(PKG)_DL_DIR) && \
	popd > /dev/null)
endef

# TODO: improve to check that the given PKG_DL_VERSION exists on the remote
# repository
define SOURCE_CHECK_GIT
  $(GIT) ls-remote --heads $($(PKG)_SITE) > /dev/null
endef

define SHOW_EXTERNAL_DEPS_GIT
  echo "$($(PKG)_SITE) [git: $($(PKG)_DL_VERSION)]"
endef


define DOWNLOAD_BZR
	test -e $(DL_DIR)/$($(PKG)_SOURCE) || \
	$(BZR) export $(DL_DIR)/$($(PKG)_SOURCE) $($(PKG)_SITE) -r $($(PKG)_DL_VERSION)
endef

define SOURCE_CHECK_BZR
	$(BZR) ls --quiet $($(PKG)_SITE) > /dev/null
endef

define SHOW_EXTERNAL_DEPS_BZR
	echo "$($(PKG)_SITE) [bzr: $($(PKG)_DL_VERSION)]"
endef


define DOWNLOAD_SVN
	test -e $(DL_DIR)/$($(PKG)_SOURCE) || \
	(pushd $(DL_DIR) > /dev/null && \
	$(SVN) export -r $($(PKG)_DL_VERSION) $($(PKG)_SITE) $($(PKG)_DL_DIR) && \
	$(TAR) czf $($(PKG)_SOURCE) $($(PKG)_BASE_NAME)/ && \
	rm -rf $($(PKG)_DL_DIR) && \
	popd > /dev/null)
endef

define SOURCE_CHECK_SVN
  $(SVN) ls $($(PKG)_SITE) > /dev/null
endef

define SHOW_EXTERNAL_DEPS_SVN
  echo "$($(PKG)_SITE) [svn: $($(PKG)_DL_VERSION)]"
endef


define DOWNLOAD_WGET
	test -e $(DL_DIR)/$(2) || \
	$(WGET) -P $(DL_DIR) $(call qstrip,$(1))/$(2)
endef

define SOURCE_CHECK_WGET
  $(WGET) --spider $(call qstrip,$(1))/$(2)
endef

define SHOW_EXTERNAL_DEPS_WGET
  echo $(2)
endef

################################################################################
# DOWNLOAD -- Download helper. Will try to download source from:
# 1) BR2_PRIMARY_SITE if enabled
# 2) Download site
# 3) BR2_BACKUP_SITE if enabled
#
# Argument 1 is the source location
# Argument 2 is the source filename
#
# E.G. use like this:
# $(call DOWNLOAD,$(FOO_SITE),$(FOO_SOURCE))
################################################################################

define DOWNLOAD
	$(Q)if test -n "$(call qstrip,$(BR2_PRIMARY_SITE))" ; then \
		$(call $(DL_MODE)_WGET,$(BR2_PRIMARY_SITE),$(2)) && exit ; \
	fi ; \
	if test -n "$(1)" ; then \
		case "$($(PKG)_SITE_METHOD)" in \
			git) $($(DL_MODE)_GIT) && exit ;; \
			svn) $($(DL_MODE)_SVN) && exit ;; \
			bzr) $($(DL_MODE)_BZR) && exit ;; \
			*) $(call $(DL_MODE)_WGET,$(1),$(2)) && exit ;; \
		esac ; \
	fi ; \
	if test -n "$(call qstrip,$(BR2_BACKUP_SITE))" ; then \
		$(call $(DL_MODE)_WGET,$(BR2_BACKUP_SITE),$(2)) && exit ; \
	fi ; \
	exit 1
endef

# Utility programs used to build packages
TAR ?= tar

# Automatically detect tar --strip-path/components option
TAR_STRIP_COMPONENTS := \
  $(shell $(TAR) --help | grep strip-path > /dev/null ; \
  if test $$? = 0 ; then \
   echo '--strip-path' ; \
  else \
   echo '--strip-components' ; \
  fi)

# Needed for the foreach loops to loop over the list of hooks, so that
# each hook call is properly separated by a newline.
define sep


endef

################################################################################
# Implicit targets -- produce a stamp file for each step of a package build
################################################################################

# Retrieve the archive
$(BUILD_DIR)/%/.stamp_downloaded:
ifeq ($(DL_MODE),DOWNLOAD)
# Only show the download message if it isn't already downloaded
	$(Q)(test -e $(DL_DIR)/$($(PKG)_SOURCE) && \
		(test -z $($(PKG)_PATCH) || test -e $(DL_DIR)$($(PKG)_PATCH))) || \
		$(call MESSAGE,"Downloading")
endif
	$(call DOWNLOAD,$($(PKG)_SITE),$($(PKG)_SOURCE))
	$(if $($(PKG)_PATCH),$(call DOWNLOAD,$($(PKG)_SITE),$($(PKG)_PATCH)))
ifeq ($(DL_MODE),DOWNLOAD)
	$(Q)mkdir -p $(@D)
	$(Q)touch $@
endif

# Unpack the archive
$(BUILD_DIR)/%/.stamp_extracted:
	@$(call MESSAGE,"Extracting")
	$(Q)mkdir -p $(@D)
	$(Q)$(INFLATE$(suffix $($(PKG)_SOURCE))) $(DL_DIR)/$($(PKG)_SOURCE) | \
	$(TAR) $(TAR_STRIP_COMPONENTS)=1 -C $(@D) $(TAR_OPTIONS) -
# some packages have messed up permissions inside
	$(Q)chmod -R ug+rw $(@D)
	$(foreach hook,$($(PKG)_POST_EXTRACT_HOOKS),$(call $(hook))$(sep))
	$(Q)touch $@

# Patch
#
# The NOHOSTPKG variable is the uppercased package name, without the
# HOST_ prefix, even for host packages. This allows to find the
# patches in the package directory, because $($(NOHOSTPKG)_NAME)
# expands to the package directory name.
#
$(BUILD_DIR)/%/.stamp_patched: NAMEVER = $($(NOHOSTPKG)_NAME)-$($(PKG)_VERSION)
$(BUILD_DIR)/%/.stamp_patched:
	@$(call MESSAGE,"Patching $($(PKG)_DIR_PREFIX)/$($(PKG)_NAME)")
	$(if $($(PKG)_PATCH),toolchain/patch-kernel.sh $(@D) $(DL_DIR) $($(PKG)_PATCH))
	$(Q)( \
	if test -d $($(PKG)_DIR_PREFIX)/$($(NOHOSTPKG)_NAME); then \
	  if test "$(wildcard $($(PKG)_DIR_PREFIX)/$($(NOHOSTPKG)_NAME)/$(NAMEVER)*.patch*)"; then \
	    toolchain/patch-kernel.sh $(@D) $($(PKG)_DIR_PREFIX)/$($(NOHOSTPKG)_NAME) $(NAMEVER)\*.patch $(NAMEVER)\*.patch.$(ARCH) || exit 1; \
	  else \
	    toolchain/patch-kernel.sh $(@D) $($(PKG)_DIR_PREFIX)/$($(NOHOSTPKG)_NAME) $($(NOHOSTPKG)_NAME)\*.patch $($(NOHOSTPKG)_NAME)\*.patch.$(ARCH) || exit 1; \
	    if test -d $($(PKG)_DIR_PREFIX)/$($(NOHOSTPKG)_NAME)/$(NAMEVER); then \
	      toolchain/patch-kernel.sh $(@D) $($(PKG)_DIR_PREFIX)/$($(NOHOSTPKG)_NAME)/$(NAMEVER) \*.patch \*.patch.$(ARCH) || exit 1; \
	    fi; \
	  fi; \
	fi; \
	)
	$(foreach hook,$($(PKG)_POST_PATCH_HOOKS),$(call $(hook))$(sep))
	$(Q)touch $@

# Configure
$(BUILD_DIR)/%/.stamp_configured:
	$(foreach hook,$($(PKG)_PRE_CONFIGURE_HOOKS),$(call $(hook))$(sep))
	@$(call MESSAGE,"Configuring")
	$($(PKG)_CONFIGURE_CMDS)
	$(foreach hook,$($(PKG)_POST_CONFIGURE_HOOKS),$(call $(hook))$(sep))
	$(Q)touch $@

# Build
$(BUILD_DIR)/%/.stamp_built::
	@$(call MESSAGE,"Building")
	$($(PKG)_BUILD_CMDS)
	$(foreach hook,$($(PKG)_POST_BUILD_HOOKS),$(call $(hook))$(sep))
	$(Q)touch $@

# Install to host dir
$(BUILD_DIR)/%/.stamp_host_installed:
	@$(call MESSAGE,'Installing to host directory')
	$($(PKG)_INSTALL_CMDS)
	$(foreach hook,$($(PKG)_POST_INSTALL_HOOKS),$(call $(hook))$(sep))
	$(Q)touch $@

# Install to staging dir
$(BUILD_DIR)/%/.stamp_staging_installed:
	@$(call MESSAGE,'Installing to staging directory')
	$($(PKG)_INSTALL_STAGING_CMDS)
	$(foreach hook,$($(PKG)_POST_INSTALL_STAGING_HOOKS),$(call $(hook))$(sep))
	$(Q)touch $@

# Install to target dir
$(BUILD_DIR)/%/.stamp_target_installed:
	@$(call MESSAGE,"Installing to target")
	$($(PKG)_INSTALL_TARGET_CMDS)
	$(foreach hook,$($(PKG)_POST_INSTALL_TARGET_HOOKS),$(call $(hook))$(sep))
	$(Q)touch $@

# Clean package
$(BUILD_DIR)/%/.stamp_cleaned:
	@$(call MESSAGE,"Cleaning up")
	$($(PKG)_CLEAN_CMDS)
	rm -f $(@D)/.stamp_built

# Uninstall package from target and staging
$(BUILD_DIR)/%/.stamp_uninstalled:
	@$(call MESSAGE,"Uninstalling")
	$($(PKG)_UNINSTALL_STAGING_CMDS)
	rm -f $($(PKG)_TARGET_INSTALL_STAGING)
	$($(PKG)_UNINSTALL_TARGET_CMDS)
	rm -f $($(PKG)_TARGET_INSTALL_TARGET) $($(PKG)_HOOK_POST_INSTALL)

# Remove package sources
$(BUILD_DIR)/%/.stamp_dircleaned:
	rm -Rf $(@D)

################################################################################
# GENTARGETS_INNER -- generates the make targets needed to build a
# generic package
#
#  argument 1 is the lowercase package name
#  argument 2 is the uppercase package name, including an HOST_ prefix
#             for host packages
#  argument 3 is the uppercase package name, without the HOST_ prefix
#             for host packages
#  argument 4 is the package directory prefix
#  argument 5 is the type (target or host)
################################################################################

define GENTARGETS_INNER

# Define default values for various package-related variables, if not
# already defined. For some variables (version, source, site and
# subdir), if they are undefined, we try to see if a variable without
# the HOST_ prefix is defined. If so, we use such a variable, so that
# these informations have only to be specified once, for both the
# target and host packages of a given .mk file.

$(2)_TYPE                       =  $(5)
$(2)_NAME			=  $(1)

# Keep the package version that may contain forward slashes in the _DL_VERSION
# variable, then replace all forward slashes ('/') by underscores ('_') to
# sanitize the package version that is used in paths, directory and file names.
# Forward slashes may appear in the package's version when pointing to a
# version control system branch or tag, for example remotes/origin/1_10_stable.
$(2)_DL_VERSION	= $($(2)_VERSION)
ifndef $(2)_VERSION
 ifdef $(3)_VERSION
  $(2)_VERSION = $($(3)_VERSION)
 else
  $(2)_VERSION = undefined
 endif
else
 $(2)_VERSION = $(subst /,_,$($(2)_VERSION))
endif

$(2)_BASE_NAME	=  $(1)-$$($(2)_VERSION)
$(2)_DL_DIR	=  $$(DL_DIR)/$$($(2)_BASE_NAME)
$(2)_DIR	=  $$(BUILD_DIR)/$$($(2)_BASE_NAME)

ifndef $(2)_SOURCE
 ifdef $(3)_SOURCE
  $(2)_SOURCE = $($(3)_SOURCE)
 else
  $(2)_SOURCE			?= $$($(2)_BASE_NAME).tar.gz
 endif
endif

ifndef $(2)_PATCH
 ifdef $(3)_PATCH
  $(2)_PATCH = $($(3)_PATCH)
 endif
endif

ifndef $(2)_SITE
 ifdef $(3)_SITE
  $(2)_SITE = $($(3)_SITE)
 else
  $(2)_SITE			?= \
	http://$$(BR2_SOURCEFORGE_MIRROR).dl.sourceforge.net/sourceforge/$(1)
 endif
endif

ifndef $(2)_SITE_METHOD
 ifdef $(3)_SITE_METHOD
  $(2)_SITE_METHOD = $($(3)_SITE_METHOD)
 else
	# Try automatic detection using the scheme part of the URI
	$(2)_SITE_METHOD = $(firstword $(subst ://, ,$(call qstrip,$($(2)_SITE))))
 endif
endif

$(2)_DEPENDENCIES		?=
$(2)_INSTALL_STAGING		?= NO
$(2)_INSTALL_TARGET		?= YES
$(2)_DIR_PREFIX			= $(if $(4),$(4),$(TOP_SRCDIR)/package)

# define sub-target stamps
$(2)_TARGET_INSTALL_TARGET =	$$($(2)_DIR)/.stamp_target_installed
$(2)_TARGET_INSTALL_STAGING =	$$($(2)_DIR)/.stamp_staging_installed
$(2)_TARGET_INSTALL_HOST =      $$($(2)_DIR)/.stamp_host_installed
$(2)_TARGET_BUILD =		$$($(2)_DIR)/.stamp_built
$(2)_TARGET_CONFIGURE =		$$($(2)_DIR)/.stamp_configured
$(2)_TARGET_PATCH =		$$($(2)_DIR)/.stamp_patched
$(2)_TARGET_EXTRACT =		$$($(2)_DIR)/.stamp_extracted
$(2)_TARGET_SOURCE =		$$($(2)_DIR)/.stamp_downloaded
$(2)_TARGET_UNINSTALL =		$$($(2)_DIR)/.stamp_uninstalled
$(2)_TARGET_CLEAN =		$$($(2)_DIR)/.stamp_cleaned
$(2)_TARGET_DIRCLEAN =		$$($(2)_DIR)/.stamp_dircleaned

# post-steps hooks
$(2)_POST_EXTRACT_HOOKS         ?=
$(2)_POST_PATCH_HOOKS           ?=
$(2)_PRE_CONFIGURE_HOOKS        ?=
$(2)_POST_CONFIGURE_HOOKS       ?=
$(2)_POST_BUILD_HOOKS           ?=
$(2)_POST_INSTALL_HOOKS         ?=
$(2)_POST_INSTALL_STAGING_HOOKS ?=
$(2)_POST_INSTALL_TARGET_HOOKS  ?=

# human-friendly targets and target sequencing
$(1):			$(1)-install

ifeq ($$($(2)_TYPE),host)
$(1)-install:	        $(1)-install-host
else
$(1)-install:		$(1)-install-staging $(1)-install-target
endif

ifeq ($$($(2)_INSTALL_TARGET),YES)
$(1)-install-target:	$(1)-build \
			$$($(2)_TARGET_INSTALL_TARGET)
else
$(1)-install-target:
endif

ifeq ($$($(2)_INSTALL_STAGING),YES)
$(1)-install-staging:	$(1)-build \
			$$($(2)_TARGET_INSTALL_STAGING)
else
$(1)-install-staging:
endif

$(1)-install-host:      $(1)-build $$($(2)_TARGET_INSTALL_HOST)

$(1)-build:		$(1)-configure \
			$$($(2)_TARGET_BUILD)

$(1)-configure:		$(1)-patch \
			$$($(2)_TARGET_CONFIGURE)

$(1)-patch:		$(1)-extract $$($(2)_TARGET_PATCH)

$(1)-extract:		$(1)-depends \
			$$($(2)_TARGET_EXTRACT)

$(1)-depends:		$(1)-source $$($(2)_DEPENDENCIES)

$(1)-show-depends:
			@echo $$($(2)_DEPENDENCIES)

$(1)-source:		$$($(2)_TARGET_SOURCE)

$(1)-uninstall:		$(1)-configure $$($(2)_TARGET_UNINSTALL)

$(1)-clean:		$(1)-uninstall \
			$$($(2)_TARGET_CLEAN)

$(1)-dirclean:		$$($(2)_TARGET_DIRCLEAN)

# define the PKG variable for all targets, containing the
# uppercase package variable prefix
$$($(2)_TARGET_INSTALL_TARGET):		PKG=$(2)
$$($(2)_TARGET_INSTALL_STAGING):	PKG=$(2)
$$($(2)_TARGET_INSTALL_HOST):           PKG=$(2)
$$($(2)_TARGET_BUILD):			PKG=$(2)
$$($(2)_TARGET_CONFIGURE):		PKG=$(2)
$$($(2)_TARGET_PATCH):			PKG=$(2)
$$($(2)_TARGET_PATCH):			NOHOSTPKG=$(3)
$$($(2)_TARGET_EXTRACT):		PKG=$(2)
$$($(2)_TARGET_SOURCE):			PKG=$(2)
$$($(2)_TARGET_UNINSTALL):		PKG=$(2)
$$($(2)_TARGET_CLEAN):			PKG=$(2)
$$($(2)_TARGET_DIRCLEAN):		PKG=$(2)

# add package to the general list of targets if requested by the buildroot
# configuration

ifeq ($$(BR2_PACKAGE_$(2)),y)

TARGETS += $(1)

ifeq ($$($(2)_SITE_METHOD),svn)
DL_TOOLS_DEPENDENCIES += svn
else ifeq ($$($(2)_SITE_METHOD),git)
DL_TOOLS_DEPENDENCIES += git
else ifeq ($$($(2)_SITE_METHOD),bzr)
DL_TOOLS_DEPENDENCIES += bzr
endif # SITE_METHOD

endif # BR2_PACKAGE_$(2)
endef # GENTARGETS_INNER

################################################################################
# GENTARGETS -- the target generator macro for generic packages
#
# Argument 1 is the package directory prefix [mandatory]
# Argument 2 is the lowercase package name   [mandatory]
# Argument 3 is "target" or "host"           [optional, default: "target"]
################################################################################

define GENTARGETS
ifeq ($(3),host)
# In the case of host packages, turn the package name "pkg" into "host-pkg"
$(call GENTARGETS_INNER,$(3)-$(2),$(call UPPERCASE,$(3)-$(2)),$(call UPPERCASE,$(2)),$(1),host)
else
# In the case of target packages, keep the package name "pkg"
$(call GENTARGETS_INNER,$(2),$(call UPPERCASE,$(2)),$(call UPPERCASE,$(2)),$(1),target)
endif
endef

# :mode=makefile:
