################################################################################
# Generic package infrastructure
#
# This file implements an infrastructure that eases development of
# package .mk files. It should be used for packages that do not rely
# on a well-known build system for which Buildroot has a dedicated
# infrastructure (so far, Buildroot has special support for
# autotools-based and CMake-based packages).
#
# 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
################################################################################

################################################################################
# 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
	$(if $($(PKG)_SOURCE),$(call DOWNLOAD,$($(PKG)_SITE)/$($(PKG)_SOURCE)))
	$(if $($(PKG)_PATCH),$(call DOWNLOAD,$($(PKG)_SITE)/$($(PKG)_PATCH)))
	$(foreach hook,$($(PKG)_POST_DOWNLOAD_HOOKS),$(call $(hook))$(sep))
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)
	$($(PKG)_EXTRACT_CMDS)
# some packages have messed up permissions inside
	$(Q)chmod -R +rw $(@D)
	$(foreach hook,$($(PKG)_POST_EXTRACT_HOOKS),$(call $(hook))$(sep))
	$(Q)touch $@

# Rsync the source directory if the <pkg>_OVERRIDE_SRCDIR feature is
# used.
$(BUILD_DIR)/%/.stamp_rsynced:
	@$(call MESSAGE,"Syncing from source dir $(SRCDIR)")
	@test -d $(SRCDIR) || (echo "ERROR: $(SRCDIR) does not exist" ; exit 1)
	rsync -au $(SRCDIR)/ $(@D)
	$(Q)touch $@

# Handle the SOURCE_CHECK and SHOW_EXTERNAL_DEPS cases for rsynced
# packages
$(BUILD_DIR)/%/.stamp_rsync_sourced:
ifeq ($(DL_MODE),SOURCE_CHECK)
	test -d $(SRCDIR)
else ifeq ($(DL_MODE),SHOW_EXTERNAL_DEPS)
	echo "file://$(SRCDIR)"
else
	@true # Nothing to do to source a local package
endif

# Patch
#
# The RAWNAME variable is the lowercased package name, which allows to
# find the package directory (typically package/<pkgname>) and the
# prefix of the patches
$(BUILD_DIR)/%/.stamp_patched: NAMEVER = $(RAWNAME)-$($(PKG)_VERSION)
$(BUILD_DIR)/%/.stamp_patched:
	@$(call MESSAGE,"Patching $($(PKG)_DIR_PREFIX)/$(RAWNAME)")
	$(foreach hook,$($(PKG)_PRE_PATCH_HOOKS),$(call $(hook))$(sep))
	$(if $($(PKG)_PATCH),support/scripts/apply-patches.sh $(@D) $(DL_DIR) $($(PKG)_PATCH))
	$(Q)( \
	if test -d $($(PKG)_DIR_PREFIX)/$(RAWNAME); then \
	  if test "$(wildcard $($(PKG)_DIR_PREFIX)/$(RAWNAME)/$(NAMEVER)*.patch*)"; then \
	    support/scripts/apply-patches.sh $(@D) $($(PKG)_DIR_PREFIX)/$(RAWNAME) $(NAMEVER)\*.patch $(NAMEVER)\*.patch.$(ARCH) || exit 1; \
	  else \
	    support/scripts/apply-patches.sh $(@D) $($(PKG)_DIR_PREFIX)/$(RAWNAME) $(RAWNAME)\*.patch $(RAWNAME)\*.patch.$(ARCH) || exit 1; \
	    if test -d $($(PKG)_DIR_PREFIX)/$(RAWNAME)/$(NAMEVER); then \
	      support/scripts/apply-patches.sh $(@D) $($(PKG)_DIR_PREFIX)/$(RAWNAME)/$(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 images dir
$(BUILD_DIR)/%/.stamp_images_installed:
	@$(call MESSAGE,"Installing to images directory")
	$($(PKG)_INSTALL_IMAGES_CMDS)
	$(foreach hook,$($(PKG)_POST_INSTALL_IMAGES_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
# Uninstall commands tend to fail, so remove the stamp files first
$(BUILD_DIR)/%/.stamp_uninstalled:
	@$(call MESSAGE,"Uninstalling")
	rm -f $($(PKG)_TARGET_INSTALL_STAGING)
	rm -f $($(PKG)_TARGET_INSTALL_TARGET)
	$($(PKG)_UNINSTALL_STAGING_CMDS)
	$($(PKG)_UNINSTALL_TARGET_CMDS)

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

################################################################################
# inner-generic-package -- 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 inner-generic-package

# 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)
$(2)_RAWNAME			=  $(patsubst host-%,%,$(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.
ifndef $(2)_VERSION
 ifdef $(3)_VERSION
  $(2)_DL_VERSION = $($(3)_VERSION)
  $(2)_VERSION = $(subst /,_,$($(3)_VERSION))
 else
  $(2)_VERSION = undefined
  $(2)_DL_VERSION = undefined
 endif
else
  $(2)_DL_VERSION = $($(2)_VERSION)
  $(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)

ifneq ($$($(2)_OVERRIDE_SRCDIR),)
$(2)_VERSION = custom
endif

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

ifeq ($$($(2)_SITE_METHOD),local)
ifeq ($$($(2)_OVERRIDE_SRCDIR),)
$(2)_OVERRIDE_SRCDIR = $($(2)_SITE)
endif
endif

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

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

$(2)_LICENSE			?= unknown

$(2)_DEPENDENCIES ?= $(filter-out $(1),$(patsubst host-host-%,host-%,$(addprefix host-,$($(3)_DEPENDENCIES))))

$(2)_INSTALL_STAGING		?= NO
$(2)_INSTALL_IMAGES		?= 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_IMAGES =	$$($(2)_DIR)/.stamp_images_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_RSYNC =	        $$($(2)_DIR)/.stamp_rsynced
$(2)_TARGET_RSYNC_SOURCE =      $$($(2)_DIR)/.stamp_rsync_sourced
$(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

# default extract command
$(2)_EXTRACT_CMDS ?= \
	$$(if $$($(2)_SOURCE),$$(INFLATE$$(suffix $$($(2)_SOURCE))) $(DL_DIR)/$$($(2)_SOURCE) | \
	$(TAR) $(TAR_STRIP_COMPONENTS)=1 -C $$($(2)_DIR) $(TAR_OPTIONS) -)

# post-steps hooks
$(2)_POST_DOWNLOAD_HOOKS        ?=
$(2)_POST_EXTRACT_HOOKS         ?=
$(2)_PRE_PATCH_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  ?=
$(2)_POST_INSTALL_IMAGES_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 $(1)-install-images
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

ifeq ($$($(2)_INSTALL_IMAGES),YES)
$(1)-install-images:	$(1)-build \
			$$($(2)_TARGET_INSTALL_IMAGES)
else
$(1)-install-images:
endif

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

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

ifeq ($$($(2)_OVERRIDE_SRCDIR),)
# In the normal case (no package override), the sequence of steps is
#  source, by downloading
#  depends
#  extract
#  patch
#  configure
$(1)-configure:		$(1)-patch $(1)-depends \
			$$($(2)_TARGET_CONFIGURE)

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

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

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

$(1)-source:		$$($(2)_TARGET_SOURCE)
else
# In the package override case, the sequence of steps
#  source, by rsyncing
#  depends
#  configure
$(1)-configure:		$(1)-depends \
			$$($(2)_TARGET_CONFIGURE)

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

$(1)-rsync:		$$($(2)_TARGET_RSYNC)

$(1)-source:		$$($(2)_TARGET_RSYNC_SOURCE)
endif

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

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

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

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

$(1)-clean-for-rebuild:
ifneq ($$($(2)_OVERRIDE_SRCDIR),)
			rm -f $$($(2)_TARGET_RSYNC)
endif
			rm -f $$($(2)_TARGET_BUILD)
			rm -f $$($(2)_TARGET_INSTALL_STAGING)
			rm -f $$($(2)_TARGET_INSTALL_TARGET)
			rm -f $$($(2)_TARGET_INSTALL_IMAGES)
			rm -f $$($(2)_TARGET_INSTALL_HOST)

$(1)-rebuild:		$(1)-clean-for-rebuild all

$(1)-clean-for-reconfigure: $(1)-clean-for-rebuild
			rm -f $$($(2)_TARGET_CONFIGURE)

$(1)-reconfigure:	$(1)-clean-for-reconfigure all

# 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_IMAGES):		PKG=$(2)
$$($(2)_TARGET_INSTALL_HOST):           PKG=$(2)
$$($(2)_TARGET_BUILD):			PKG=$(2)
$$($(2)_TARGET_CONFIGURE):		PKG=$(2)
$$($(2)_TARGET_RSYNC):                  SRCDIR=$$($(2)_OVERRIDE_SRCDIR)
$$($(2)_TARGET_RSYNC):                  PKG=$(2)
$$($(2)_TARGET_RSYNC_SOURCE):		SRCDIR=$$($(2)_OVERRIDE_SRCDIR)
$$($(2)_TARGET_RSYNC_SOURCE):		PKG=$(2)
$$($(2)_TARGET_PATCH):			PKG=$(2)
$$($(2)_TARGET_PATCH):			RAWNAME=$(patsubst host-%,%,$(1))
$$($(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)

# Compute the name of the Kconfig option that correspond to the
# package being enabled. We handle three cases: the special Linux
# kernel case, the bootloaders case, and the normal packages case.
ifeq ($(1),linux)
$(2)_KCONFIG_VAR = BR2_LINUX_KERNEL
else ifeq ($(4),boot/)
$(2)_KCONFIG_VAR = BR2_TARGET_$(2)
else
$(2)_KCONFIG_VAR = BR2_PACKAGE_$(2)
endif

# legal-info: declare dependencies and set values used later for the manifest
ifneq ($$($(2)_LICENSE),PROPRIETARY)
ifneq ($$($(2)_SITE_METHOD),local)
ifneq ($$($(2)_SITE_METHOD),override)
# Packages that have a tarball need it downloaded and extracted beforehand
$(1)-legal-info: $(1)-extract $(REDIST_SOURCES_DIR)
$(2)_MANIFEST_TARBALL = $$($(2)_SOURCE)
ifneq ($$($(2)_LICENSE_FILES),)
$(2)_MANIFEST_LICENSE_FILES = $$($(2)_LICENSE_FILES)
endif
endif
endif
endif
# defaults for packages without tarball or license files
$(2)_MANIFEST_TARBALL ?= not saved
$(2)_MANIFEST_LICENSE_FILES ?= not saved

# legal-info: produce legally relevant info.
$(1)-legal-info:
# Packages without a source are assumed to be part of Buildroot, skip them.
ifneq ($(call qstrip,$$($(2)_SOURCE)),)
ifeq ($$($(2)_LICENSE),PROPRIETARY)
# Proprietary packages: nothing to save
else ifeq ($$($(2)_SITE_METHOD),local)
# Packages without a tarball: don't save and warn
	@$(call legal-warning-pkg-savednothing,$$($(2)_RAWNAME),local)
else ifeq ($$($(2)_SITE_METHOD),override)
	@$(call legal-warning-pkg-savednothing,$$($(2)_RAWNAME),override)
else
# Other packages
# Save license files if defined
ifeq ($(call qstrip,$$($(2)_LICENSE_FILES)),)
	@$(call legal-license-nofiles,$$($(2)_RAWNAME))
	@$(call legal-warning-pkg,$$($(2)_RAWNAME),cannot save license ($(2)_LICENSE_FILES not defined))
else
	@for F in $$($(2)_LICENSE_FILES); do \
		$(call legal-license-file,$$($(2)_RAWNAME),$$$${F},$$($(2)_DIR)/$$$${F}); \
		done
endif
# Copy the source tarball (just hardlink if possible)
	@cp -l $(DL_DIR)/$$($(2)_SOURCE) $(REDIST_SOURCES_DIR) 2>/dev/null || \
	   cp $(DL_DIR)/$$($(2)_SOURCE) $(REDIST_SOURCES_DIR)
endif
	@$(call legal-manifest,$$($(2)_RAWNAME),$$($(2)_VERSION),$$($(2)_LICENSE),$$($(2)_MANIFEST_LICENSE_FILES),$$($(2)_MANIFEST_TARBALL))
endif # ifneq ($(call qstrip,$$($(2)_SOURCE)),)

# add package to the general list of targets if requested by the buildroot
# configuration
ifeq ($$($$($(2)_KCONFIG_VAR)),y)

TARGETS += $(1)
PACKAGES_PERMISSIONS_TABLE += $$($(2)_PERMISSIONS)$$(sep)
PACKAGES_DEVICES_TABLE += $$($(2)_DEVICES)$$(sep)

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
else ifeq ($$($(2)_SITE_METHOD),scp)
DL_TOOLS_DEPENDENCIES += scp ssh
else ifeq ($$($(2)_SITE_METHOD),hg)
DL_TOOLS_DEPENDENCIES += hg
endif # SITE_METHOD

DL_TOOLS_DEPENDENCIES += $(firstword $(INFLATE$(suffix $($(2)_SOURCE))))

endif # $(2)_KCONFIG_VAR
endef # inner-generic-package

################################################################################
# generic-package -- the target generator macro for generic packages
################################################################################

# In the case of target packages, keep the package name "pkg"
generic-package = $(call inner-generic-package,$(call pkgname),$(call UPPERCASE,$(call pkgname)),$(call UPPERCASE,$(call pkgname)),$(call pkgparentdir),target)
# In the case of host packages, turn the package name "pkg" into "host-pkg"
host-generic-package = $(call inner-generic-package,host-$(call pkgname),$(call UPPERCASE,host-$(call pkgname)),$(call UPPERCASE,$(call pkgname)),$(call pkgparentdir),host)

# :mode=makefile:
