################################################################################
# rebar package infrastructure for Erlang packages
#
# This file implements an infrastructure that eases development of
# package .mk files for rebar packages.  It should be used for all
# packages that use rebar as their build system.
#
# In terms of implementation, this rebar infrastructure requires the
# .mk file to only specify metadata information about the package:
# name, version, download URL, etc.
#
# We still allow the package .mk file to override what the different
# steps are doing, if needed. For example, if <PKG>_BUILD_CMDS is
# already defined, it is used as the list of commands to perform to
# build the package, instead of the default rebar behaviour. The
# package can also define some post operation hooks.
#
################################################################################

# Directories to store rebar dependencies in.
#
# These directories actually only contain symbolic links to Erlang
# applications in either $(HOST_DIR) or $(STAGING_DIR).  One needs
# them to avoid rebar complaining about missing dependencies, as this
# infrastructure tells rebar to NOT download dependencies during
# the build stage.
#
REBAR_HOST_DEPS_DIR = $(HOST_DIR)/share/rebar/deps
REBAR_TARGET_DEPS_DIR = $(STAGING_DIR)/usr/share/rebar/deps

# Tell rebar where to find the dependencies
#
REBAR_HOST_DEPS_ENV = \
	ERL_COMPILER_OPTIONS='{i, "$(REBAR_HOST_DEPS_DIR)"}' \
	ERL_EI_LIBDIR=$(HOST_DIR)/lib/erlang/lib/erl_interface-$(ERLANG_EI_VSN)/lib
REBAR_TARGET_DEPS_ENV = \
	ERL_COMPILER_OPTIONS='{i, "$(REBAR_TARGET_DEPS_DIR)"}' \
	ERL_EI_LIBDIR=$(STAGING_DIR)/usr/lib/erlang/lib/erl_interface-$(ERLANG_EI_VSN)/lib

################################################################################
# Helper functions
################################################################################

# Install an Erlang application from $(@D).
#
# i.e., define a recipe that installs the "bin ebin priv $(2)" directories
# from $(@D) to $(1)/$($(PKG)_ERLANG_LIBDIR).
#
#  argument 1 should typically be $(HOST_DIR), $(TARGET_DIR),
#	      or $(STAGING_DIR).
#  argument 2 is typically empty when installing in $(TARGET_DIR) and
#             "include" when installing in $(HOST_DIR) or
#             $(STAGING_DIR).
#
# Note: calling this function must be done with $$(call ...) because it
# expands package-related variables.
#
define install-erlang-directories
	$(INSTALL) -d $(1)/$($(PKG)_ERLANG_LIBDIR)
	for dir in bin ebin priv $(2); do                               \
		if test -d $(@D)/$$dir; then                            \
			cp -r $(@D)/$$dir $(1)/$($(PKG)_ERLANG_LIBDIR); \
		fi;                                                     \
	done
endef

# Setup a symbolic link in rebar's deps_dir to the actual location
# where an Erlang application is installed.
#
# i.e., define a recipe that creates a symbolic link
# from $($(PKG)_REBAR_DEPS_DIR)/$($(PKG)_ERLANG_APP)
# to $(1)$($(PKG)_ERLANG_LIBDIR).
#
# For target packages for example, one uses this to setup symbolic
# links from $(STAGING_DIR)/usr/share/rebar/deps/<erlang-app> to
# $(STAGING_DIR)/usr/lib/erlang/lib/<erlang-app>-<version>. This
# infrastructure points rebar at the former in order to tell rebar to
# NOT download dependencies during the build stage, and instead use
# the already available dependencies.
#
# Therefore,
#  argument 1 is $(HOST_DIR) (for host packages) or
#	      $(STAGING_DIR) (for target packages).
#
#  argument 2 is HOST (for host packages) or
#	      TARGET (for target packages).
#
# Note: calling this function must be done with $$(call ...) because it
# expands package-related variables.
#
define install-rebar-deps
	$(INSTALL) -d $(REBAR_$(2)_DEPS_DIR)
	ln -f -s $(1)/$($(PKG)_ERLANG_LIBDIR) \
		$(REBAR_$(2)_DEPS_DIR)/$($(PKG)_ERLANG_APP)
endef

# Remove the "deps" statement from a rebar.config file
define remove-rebar-config-dependencies
	$(SED) '/^{deps.*}\.$$/d' -e '/^{deps/,/}\.$$/d' \
		$($(PKG)_DIR)/rebar.config
endef


################################################################################
# inner-rebar-package -- defines how the configuration, compilation
# and installation of a rebar package should be done, implements a few
# hooks to tune the build process according to rebar specifities, and
# calls the generic package infrastructure to generate the necessary
# make targets.
#
#  argument 1 is the lowercase package name
#  argument 2 is the uppercase package name, including a HOST_ prefix
#             for host packages
#  argument 3 is the uppercase package name, without the HOST_ prefix
#             for host packages
#  argument 4 is the type (target or host)
#
################################################################################

define inner-rebar-package

# Extract just the raw package name, lowercase without the leading
# erlang- or host- prefix, as this is used by rebar to find the
# dependencies a package specifies.
#
$(2)_ERLANG_APP = $(subst -,_,$(patsubst erlang-%,%,$(patsubst host-%,%,$(1))))

# Path where to store the package's libs, relative to either $(HOST_DIR)
# for host packages, or $(STAGING_DIR)/usr for target packages.
#
$(2)_ERLANG_LIBDIR = \
	lib/erlang/lib/$$($$(PKG)_ERLANG_APP)-$$($$(PKG)_VERSION)

# If a host package, inherit <pkg>_USE_BUNDLED_REBAR from the target
# package, if not explicitly defined. Otherwise, default to NO.
ifndef $(2)_USE_BUNDLED_REBAR
 ifdef $(3)_USE_BUNDLED_REBAR
  $(2)_USE_BUNDLED_REBAR = $$($(3)_USE_BUNDLED_REBAR)
 else
  $(2)_USE_BUNDLED_REBAR ?= NO
 endif
endif

# If a host package, inherit <pkg>_USE_AUTOCONF from the target
# package, if not explicitly defined. Otherwise, default to NO.
ifndef $(2)_USE_AUTOCONF
 ifdef $(3)_USE_AUTOCONF
  $(2)_USE_AUTOCONF = $$($(3)_USE_AUTOCONF)
 else
  $(2)_USE_AUTOCONF ?= NO
 endif
endif

# Define the build and install commands
#
ifeq ($(4),target)

# Target packages need the erlang interpreter on the target
$(2)_DEPENDENCIES += erlang

# Used only if the package uses autotools underneath; otherwise, ignored
$(2)_CONF_ENV += $$(REBAR_TARGET_DEPS_ENV)

ifndef $(2)_BUILD_CMDS
define $(2)_BUILD_CMDS
	(cd $$(@D); \
		CC="$$(TARGET_CC)" \
		CXX="$$(TARGET_CXX)" \
		CFLAGS="$$(TARGET_CFLAGS)" \
		CXXFLAGS="$$(TARGET_CXXFLAGS)" \
		LDFLAGS="$$(TARGET_LDFLAGS)" \
		$$(REBAR_TARGET_DEPS_ENV) \
		$$(TARGET_MAKE_ENV) \
		$$($$(PKG)_REBAR_ENV) $$($$(PKG)_REBAR) deps_dir=$$(REBAR_TARGET_DEPS_DIR) compile \
	)
endef
endif

# We need to double-$ the 'call' because it wants to expand
# package-related variables
ifndef $(2)_INSTALL_STAGING_CMDS
define $(2)_INSTALL_STAGING_CMDS
	$$(call install-erlang-directories,$$(STAGING_DIR)/usr,include)
	$$(call install-rebar-deps,$$(STAGING_DIR)/usr,TARGET)
endef
endif

# We need to double-$ the 'call' because it wants to expand
# package-related variables
ifndef $(2)_INSTALL_TARGET_CMDS
define $(2)_INSTALL_TARGET_CMDS
	$$(call install-erlang-directories,$$(TARGET_DIR)/usr)
endef
endif

else # !target

# Host packages need the erlang interpreter on the host
$(2)_DEPENDENCIES += host-erlang

# Used only if the package uses autotools underneath; otherwise, ignored
$(2)_CONF_ENV += $$(REBAR_HOST_DEPS_ENV)

ifndef $(2)_BUILD_CMDS
define $(2)_BUILD_CMDS
	(cd $$(@D); \
		CC="$$(HOSTCC)" \
		CFLAGS="$$(HOST_CFLAGS)" \
		LDFLAGS="$$(HOST_LDFLAGS)" \
		$$(REBAR_HOST_DEPS_ENV) \
		$$(HOST_MAKE_ENV) \
		$$($$(PKG)_REBAR_ENV) $$($$(PKG)_REBAR) deps_dir=$$(REBAR_HOST_DEPS_DIR) compile \
	)
endef
endif

# We need to double-$ the 'call' because it wants to expand
# package-related variables
ifndef $(2)_INSTALL_CMDS
define $(2)_INSTALL_CMDS
	$$(call install-erlang-directories,$$(HOST_DIR),include)
	$$(call install-rebar-deps,$$(HOST_DIR),HOST)
endef
endif

endif # !target

# Whether to use the generic rebar or the package's bundled rebar
#
ifeq ($$($(2)_USE_BUNDLED_REBAR),YES)
$(2)_REBAR = ./rebar
else
$(2)_REBAR = rebar
$(2)_DEPENDENCIES += host-erlang-rebar
endif

$(2)_KEEP_DEPENDENCIES ?= NO

# Remove dependencies listed in rebar.config unless the package says
# otherwise
ifeq ($$($(2)_KEEP_DEPENDENCIES),NO)
$(2)_POST_PATCH_HOOKS += remove-rebar-config-dependencies
endif

# The package sub-infra to use
#
ifeq ($$($(2)_USE_AUTOCONF),YES)
$(call inner-autotools-package,$(1),$(2),$(3),$(4))
else
$(call inner-generic-package,$(1),$(2),$(3),$(4))
endif

endef # inner-rebar-package

rebar-package = $(call inner-rebar-package,$(pkgname),$(call UPPERCASE,$(pkgname)),$(call UPPERCASE,$(pkgname)),target)
host-rebar-package = $(call inner-rebar-package,host-$(pkgname),$(call UPPERCASE,host-$(pkgname)),$(call UPPERCASE,$(pkgname)),host)