summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAmadeusz Żołnowski <aidecoe@gentoo.org>2016-05-18 23:29:46 +0100
committerAmadeusz Żołnowski <aidecoe@gentoo.org>2016-05-22 23:06:16 +0100
commit1c09a26ff45d4da33f9efce2950925ee917c9e0b (patch)
tree4e4f0745eb331c0b382534fb54ff7b8d4c64973e
parentapp-portage/repoman: Add missed lxml dep for the 9999 ebuild (diff)
downloadgentoo-1c09a26ff45d4da33f9efce2950925ee917c9e0b.tar.gz
gentoo-1c09a26ff45d4da33f9efce2950925ee917c9e0b.tar.bz2
gentoo-1c09a26ff45d4da33f9efce2950925ee917c9e0b.zip
rebar.eclass: Build Erlang/OTP projects using dev-util/rebar
It is an eclass providing functions to build Erlang/OTP projects using dev-util/rebar. All packages in upcoming category dev-erlang are going to use this eclass.
-rw-r--r--eclass/rebar.eclass223
-rwxr-xr-xeclass/tests/rebar_fix_include_path.sh159
-rwxr-xr-xeclass/tests/rebar_remove_deps.sh99
-rwxr-xr-xeclass/tests/rebar_set_vsn.sh115
4 files changed, 596 insertions, 0 deletions
diff --git a/eclass/rebar.eclass b/eclass/rebar.eclass
new file mode 100644
index 000000000000..ca86e88ad8a6
--- /dev/null
+++ b/eclass/rebar.eclass
@@ -0,0 +1,223 @@
+# Copyright 1999-2016 Gentoo Foundation
+# Distributed under the terms of the GNU General Public License v2
+# $Id$
+
+# @ECLASS: rebar.eclass
+# @MAINTAINER:
+# Amadeusz Żołnowski <aidecoe@gentoo.org>
+# @AUTHOR:
+# Amadeusz Żołnowski <aidecoe@gentoo.org>
+# @BLURB: Build Erlang/OTP projects using dev-util/rebar.
+# @DESCRIPTION:
+# An eclass providing functions to build Erlang/OTP projects using
+# dev-util/rebar.
+#
+# rebar is a tool which tries to resolve dependencies itself which is by
+# cloning remote git repositories. Dependant projects are usually expected to
+# be in sub-directory 'deps' rather than looking at system Erlang lib
+# directory. Projects relying on rebar usually don't have 'install' make
+# targets. The eclass workarounds some of these problems. It handles
+# installation in a generic way for Erlang/OTP structured projects.
+
+case "${EAPI:-0}" in
+ 0|1|2|3|4|5)
+ die "Unsupported EAPI=${EAPI:-0} (too old) for ${ECLASS}"
+ ;;
+ 6)
+ ;;
+ *)
+ die "Unsupported EAPI=${EAPI} (unknown) for ${ECLASS}"
+ ;;
+esac
+
+EXPORT_FUNCTIONS src_prepare src_compile src_install
+
+RDEPEND="dev-lang/erlang"
+DEPEND="${RDEPEND}
+ dev-util/rebar
+ >=sys-apps/gawk-4.1"
+
+# @ECLASS-VARIABLE: REBAR_APP_SRC
+# @DESCRIPTION:
+# Relative path to .app.src description file.
+REBAR_APP_SRC="${REBAR_APP_SRC-src/${PN}.app.src}"
+
+# @FUNCTION: get_erl_libs
+# @RETURN: the path to Erlang lib directory
+# @DESCRIPTION:
+# Get the full path without EPREFIX to Erlang lib directory.
+get_erl_libs() {
+ echo "/usr/$(get_libdir)/erlang/lib"
+}
+
+# @FUNCTION: _rebar_find_dep
+# @INTERNAL
+# @USAGE: <project_name>
+# @RETURN: full path with EPREFIX to a Erlang package/project on success,
+# code 1 when dependency is not found and code 2 if multiple versions of
+# dependency are found.
+# @DESCRIPTION:
+# Find a Erlang package/project by name in Erlang lib directory. Project
+# directory is usually suffixed with version. It is matched to '<project_name>'
+# or '<project_name>-*'.
+_rebar_find_dep() {
+ local pn="$1"
+ local p
+ local result
+
+ pushd "${EPREFIX}$(get_erl_libs)" >/dev/null || return 1
+ for p in ${pn} ${pn}-*; do
+ if [[ -d ${p} ]]; then
+ # Ensure there's at most one matching.
+ [[ ${result} ]] && return 2
+ result="${p}"
+ fi
+ done
+ popd >/dev/null || die
+
+ [[ ${result} ]] || return 1
+ echo "${result}"
+}
+
+# @FUNCTION: erebar
+# @USAGE: <targets>
+# @DESCRIPTION:
+# Run rebar with verbose flag. Die on failure.
+erebar() {
+ debug-print-function ${FUNCNAME} "${@}"
+
+ (( $# > 0 )) || die "erebar: at least one target is required"
+
+ local -x ERL_LIBS="${EPREFIX}$(get_erl_libs)"
+ rebar -v skip_deps=true "$@" || die -n "rebar $@ failed"
+}
+
+# @FUNCTION: rebar_fix_include_path
+# @USAGE: <project_name>
+# @DESCRIPTION:
+# Fix path in rebar.config to 'include' directory of dependant project/package,
+# so it points to installation in system Erlang lib rather than relative 'deps'
+# directory.
+#
+# The function dies on failure.
+rebar_fix_include_path() {
+ debug-print-function ${FUNCNAME} "${@}"
+
+ local pn="$1"
+ local erl_libs="${EPREFIX}$(get_erl_libs)"
+ local p
+
+ p="$(_rebar_find_dep "${pn}")" \
+ || die "failed to unambiguously resolve dependency of '${pn}'"
+
+ gawk -i inplace \
+ -v erl_libs="${erl_libs}" -v pn="${pn}" -v p="${p}" '
+/^{[[:space:]]*erl_opts[[:space:]]*,/, /}[[:space:]]*\.$/ {
+ pattern = "\"(./)?deps/" pn "/include\"";
+ if (match($0, "{i,[[:space:]]*" pattern "[[:space:]]*}")) {
+ sub(pattern, "\"" erl_libs "/" p "/include\"");
+ }
+ print $0;
+ next;
+}
+1
+' rebar.config || die "failed to fix include paths in rebar.config for '${pn}'"
+}
+
+# @FUNCTION: rebar_remove_deps
+# @DESCRIPTION:
+# Remove dependencies list from rebar.config and deceive build rules that any
+# dependencies are already fetched and built. Otherwise rebar tries to fetch
+# dependencies and compile them.
+#
+# The function dies on failure.
+rebar_remove_deps() {
+ debug-print-function ${FUNCNAME} "${@}"
+
+ mkdir -p "${S}/deps" && :>"${S}/deps/.got" && :>"${S}/deps/.built" || die
+ gawk -i inplace '
+/^{[[:space:]]*deps[[:space:]]*,/, /}[[:space:]]*\.$/ {
+ if ($0 ~ /}[[:space:]]*\.$/) {
+ print "{deps, []}.";
+ }
+ next;
+}
+1
+' rebar.config || die "failed to remove deps from rebar.config"
+}
+
+# @FUNCTION: rebar_set_vsn
+# @USAGE: [<version>]
+# @DESCRIPTION:
+# Set version in project description file if it's not set.
+#
+# <version> is optional. Default is PV stripped from version suffix.
+#
+# The function dies on failure.
+rebar_set_vsn() {
+ debug-print-function ${FUNCNAME} "${@}"
+
+ local version="${1:-${PV%_*}}"
+
+ sed -e "s/vsn, git/vsn, \"${version}\"/" \
+ -i "${S}/${REBAR_APP_SRC}" \
+ || die "failed to set version in src/${PN}.app.src"
+}
+
+# @FUNCTION: rebar_src_prepare
+# @DESCRIPTION:
+# Prevent rebar from fetching and compiling dependencies. Set version in
+# project description file if it's not set.
+#
+# Existence of rebar.config is optional, but file description file must exist
+# at 'src/${PN}.app.src'.
+rebar_src_prepare() {
+ debug-print-function ${FUNCNAME} "${@}"
+
+ default
+ rebar_set_vsn
+ [[ -f rebar.config ]] && rebar_remove_deps
+}
+
+# @FUNCTION: rebar_src_configure
+# @DESCRIPTION:
+# Configure with ERL_LIBS set.
+rebar_src_configure() {
+ debug-print-function ${FUNCNAME} "${@}"
+
+ local -x ERL_LIBS="${EPREFIX}$(get_erl_libs)"
+ default
+}
+
+# @FUNCTION: rebar_src_compile
+# @DESCRIPTION:
+# Compile project with rebar.
+rebar_src_compile() {
+ debug-print-function ${FUNCNAME} "${@}"
+
+ erebar compile
+}
+
+# @FUNCTION: rebar_src_install
+# @DESCRIPTION:
+# Install BEAM files, include headers, executables and native libraries.
+# Install standard docs like README or defined in DOCS variable.
+#
+# Function expects that project conforms to Erlang/OTP structure.
+rebar_src_install() {
+ debug-print-function ${FUNCNAME} "${@}"
+
+ local bin
+ local dest="$(get_erl_libs)/${P}"
+
+ insinto "${dest}"
+ doins -r ebin
+ [[ -d include ]] && doins -r include
+ [[ -d bin ]] && for bin in bin/*; do dobin "$bin"; done
+
+ if [[ -d priv ]]; then
+ cp -pR priv "${ED}${dest}/" || die "failed to install priv/"
+ fi
+
+ einstalldocs
+}
diff --git a/eclass/tests/rebar_fix_include_path.sh b/eclass/tests/rebar_fix_include_path.sh
new file mode 100755
index 000000000000..9047f8dcd225
--- /dev/null
+++ b/eclass/tests/rebar_fix_include_path.sh
@@ -0,0 +1,159 @@
+#!/bin/bash
+# Copyright 1999-2016 Gentoo Foundation
+# Distributed under the terms of the GNU General Public License v2
+# $Id$
+
+source tests-common.sh
+
+EAPI=6
+
+inherit rebar
+
+EPREFIX="${tmpdir}/fakeroot"
+S="${WORKDIR}/${P}"
+
+setup() {
+ mkdir -p "${S}" || die
+
+ for pkg in foo-0.1.0 bar-0.1.0; do
+ mkdir -p "${EPREFIX}$(get_erl_libs)/${pkg}/include" || die
+ done
+
+ cat <<EOF >"${S}/typical.config" || die
+%%% Comment
+
+{erl_opts, [debug_info, {src_dirs, ["src"]},
+ {i, "include"},
+ {i, "deps/foo/include"},
+ {i, "../foo/include"}]}.
+
+{port_env, [{"CFLAGS", "\$CFLAGS"}, {"LDFLAGS", "\$LDFLAGS"}]}.
+EOF
+
+ cat <<EOF >"${S}/typical.config.expected" || die
+%%% Comment
+
+{erl_opts, [debug_info, {src_dirs, ["src"]},
+ {i, "include"},
+ {i, "${EPREFIX}$(get_erl_libs)/foo-0.1.0/include"},
+ {i, "../foo/include"}]}.
+
+{port_env, [{"CFLAGS", "\$CFLAGS"}, {"LDFLAGS", "\$LDFLAGS"}]}.
+EOF
+
+ cat <<EOF >"${S}/inc_one_line.config" || die
+%%% Comment
+
+{erl_opts, [debug_info, {src_dirs, ["src"]}, {i, "include"}, {i, "deps/foo/include"}, {i, "../foo/include"}]}.
+
+{port_env, [{"CFLAGS", "\$CFLAGS"}, {"LDFLAGS", "\$LDFLAGS"}]}.
+EOF
+
+ cat <<EOF >"${S}/inc_one_line.config.expected" || die
+%%% Comment
+
+{erl_opts, [debug_info, {src_dirs, ["src"]}, {i, "include"}, {i, "${EPREFIX}$(get_erl_libs)/foo-0.1.0/include"}, {i, "../foo/include"}]}.
+
+{port_env, [{"CFLAGS", "\$CFLAGS"}, {"LDFLAGS", "\$LDFLAGS"}]}.
+EOF
+}
+
+test_typical_config() {
+ local diff_rc
+ local unit_rc
+
+ # Prepare
+ cd "${S}" || die
+ cp typical.config rebar.config || die
+
+ # Run unit
+ (rebar_fix_include_path foo)
+ unit_rc=$?
+
+ # Test result
+ diff rebar.config typical.config.expected
+ diff_rc=$?
+
+ [[ ${unit_rc}${diff_rc} = 00 ]]
+}
+
+test_multiple_versions() {
+ local diff_rc
+ local unit_rc
+
+ # Prepare
+ cd "${S}" || die
+ cp typical.config rebar.config || die
+ mkdir -p "${EPREFIX}$(get_erl_libs)/foo-1.0.0/include" || die
+
+ # Run unit
+ (rebar_fix_include_path foo 2>/dev/null)
+ unit_rc=$?
+
+ # Test result
+ diff rebar.config typical.config
+ diff_rc=$?
+
+ # Clean up
+ rm -r "${EPREFIX}$(get_erl_libs)/foo-1.0.0" || die
+
+ [[ ${unit_rc}${diff_rc} = 10 ]]
+}
+
+test_not_found() {
+ local diff_rc
+ local unit_rc
+
+ # Prepare
+ cd "${S}" || die
+ cp typical.config rebar.config || die
+
+ # Run unit
+ (rebar_fix_include_path fo 2>/dev/null)
+ unit_rc=$?
+
+ # Test result
+ diff rebar.config typical.config
+ diff_rc=$?
+
+ [[ ${unit_rc}${diff_rc} = 10 ]]
+}
+
+test_includes_in_one_line() {
+ local diff_rc
+ local unit_rc
+
+ # Prepare
+ cd "${S}" || die
+ cp inc_one_line.config rebar.config || die
+
+ # Run unit
+ (rebar_fix_include_path foo)
+ unit_rc=$?
+
+ # Test result
+ diff rebar.config inc_one_line.config.expected
+ diff_rc=$?
+
+ [[ ${unit_rc}${diff_rc} = 00 ]]
+}
+
+setup
+
+tbegin "rebar_fix_include_path deals with typical config"
+test_typical_config
+tend $?
+
+tbegin "rebar_fix_include_path fails on multiple versions of dependency"
+test_multiple_versions
+tend $?
+
+tbegin "rebar_fix_include_path fails if dependency is not found"
+test_not_found
+tend $?
+
+tbegin "rebar_fix_include_path deals with all includes in one line"
+test_includes_in_one_line
+tend $?
+
+texit
diff --git a/eclass/tests/rebar_remove_deps.sh b/eclass/tests/rebar_remove_deps.sh
new file mode 100755
index 000000000000..05207a783cb4
--- /dev/null
+++ b/eclass/tests/rebar_remove_deps.sh
@@ -0,0 +1,99 @@
+#!/bin/bash
+# Copyright 1999-2016 Gentoo Foundation
+# Distributed under the terms of the GNU General Public License v2
+# $Id$
+
+source tests-common.sh
+
+EAPI=6
+
+inherit rebar
+
+EPREFIX="${tmpdir}/fakeroot"
+S="${WORKDIR}/${P}"
+
+setup() {
+ mkdir -p "${S}" || die
+
+ cat <<EOF >"${S}/rebar.config.expected" || die
+%%% Comment
+
+{port_specs, [{"priv/lib/esip_drv.so", ["c_src/esip_codec.c"]}]}.
+
+{deps, []}.
+
+{clean_files, ["c_src/esip_codec.gcda", "c_src/esip_codec.gcno"]}.
+EOF
+
+ cat <<EOF >"${S}/typical.config" || die
+%%% Comment
+
+{port_specs, [{"priv/lib/esip_drv.so", ["c_src/esip_codec.c"]}]}.
+
+{deps, [{stun, ".*", {git, "https://github.com/processone/stun", {tag, "1.0.3"}}},
+ {fast_tls, ".*", {git, "https://github.com/processone/fast_tls", {tag, "1.0.3"}}},
+ {p1_utils, ".*", {git, "https://github.com/processone/p1_utils", {tag, "1.0.3"}}}]}.
+
+{clean_files, ["c_src/esip_codec.gcda", "c_src/esip_codec.gcno"]}.
+EOF
+
+ cat <<EOF >"${S}/deps_one_line.config" || die
+%%% Comment
+
+{port_specs, [{"priv/lib/esip_drv.so", ["c_src/esip_codec.c"]}]}.
+
+{deps, [{stun, ".*", {git, "https://github.com/processone/stun", {tag, "1.0.3"}}}, {fast_tls, ".*", {git, "https://github.com/processone/fast_tls", {tag, "1.0.3"}}}, {p1_utils, ".*", {git, "https://github.com/processone/p1_utils", {tag, "1.0.3"}}}]}.
+
+{clean_files, ["c_src/esip_codec.gcda", "c_src/esip_codec.gcno"]}.
+EOF
+}
+
+test_typical_config() {
+ local diff_rc
+ local unit_rc
+
+ # Prepare
+ cd "${S}" || die
+ cp typical.config rebar.config || die
+
+ # Run unit
+ (rebar_remove_deps)
+ unit_rc=$?
+
+ # Test result
+ diff rebar.config rebar.config.expected
+ diff_rc=$?
+
+ [[ ${unit_rc}${diff_rc} = 00 ]]
+}
+
+test_deps_in_one_line() {
+ local diff_rc
+ local unit_rc
+
+ # Prepare
+ cd "${S}" || die
+ cp deps_one_line.config rebar.config || die
+
+ # Run unit
+ (rebar_remove_deps)
+ unit_rc=$?
+
+ # Test result
+ diff rebar.config rebar.config.expected
+ diff_rc=$?
+
+ [[ ${unit_rc}${diff_rc} = 00 ]]
+}
+
+setup
+
+tbegin "rebar_remove_deps deals with typical config"
+test_typical_config
+tend $?
+
+tbegin "rebar_remove_deps deals with all deps in one line"
+test_deps_in_one_line
+tend $?
+
+texit
diff --git a/eclass/tests/rebar_set_vsn.sh b/eclass/tests/rebar_set_vsn.sh
new file mode 100755
index 000000000000..2a9f5bc92777
--- /dev/null
+++ b/eclass/tests/rebar_set_vsn.sh
@@ -0,0 +1,115 @@
+#!/bin/bash
+# Copyright 1999-2016 Gentoo Foundation
+# Distributed under the terms of the GNU General Public License v2
+# $Id$
+
+source tests-common.sh
+
+EAPI=6
+
+inherit rebar
+
+EPREFIX="${tmpdir}/fakeroot"
+S="${WORKDIR}/${P}"
+
+setup() {
+ mkdir -p "${S}/src" || die
+
+ cat <<EOF >"${S}/app.src.expected" || die
+%%% Comment
+
+{application, esip,
+ [{description, "ProcessOne SIP server component in Erlang"},
+ {vsn, "0"},
+ {modules, []},
+ {registered, []},
+EOF
+
+ cat <<EOF >"${S}/app.src" || die
+%%% Comment
+
+{application, esip,
+ [{description, "ProcessOne SIP server component in Erlang"},
+ {vsn, git},
+ {modules, []},
+ {registered, []},
+EOF
+}
+
+test_typical_app_src() {
+ local diff_rc
+ local unit_rc
+
+ # Prepare
+ cd "${S}" || die
+ cp app.src "src/${PN}.app.src" || die
+
+ # Run unit
+ (rebar_set_vsn)
+ unit_rc=$?
+
+ # Test result
+ diff "src/${PN}.app.src" app.src.expected
+ diff_rc=$?
+
+ [[ ${unit_rc}${diff_rc} = 00 ]]
+}
+
+test_app_src_missing() {
+ local unit_rc
+
+ # Prepare
+ cd "${S}" || die
+ rm -f "src/${PN}.app.src" || die
+
+ # Run unit
+ (rebar_set_vsn 2>/dev/null)
+ unit_rc=$?
+
+ [[ ${unit_rc} = 1 ]]
+}
+
+test_set_custom_version() {
+ local diff_rc
+ local unit_rc
+
+ # Prepare
+ cd "${S}" || die
+ cp app.src "src/${PN}.app.src" || die
+ cat <<EOF >"${S}/custom_app.src.expected" || die
+%%% Comment
+
+{application, esip,
+ [{description, "ProcessOne SIP server component in Erlang"},
+ {vsn, "1.2.3"},
+ {modules, []},
+ {registered, []},
+EOF
+
+ # Run unit
+ (rebar_set_vsn 1.2.3)
+ unit_rc=$?
+
+ # Test result
+ diff "src/${PN}.app.src" custom_app.src.expected
+ diff_rc=$?
+
+ [[ ${unit_rc}${diff_rc} = 00 ]]
+}
+
+
+setup
+
+tbegin "rebar_set_vsn deals with typical app.src"
+test_typical_app_src
+tend $?
+
+tbegin "rebar_set_vsn fails when app.src is missing"
+test_app_src_missing
+tend $?
+
+tbegin "rebar_set_vsn sets custom version in app.src"
+test_set_custom_version
+tend $?
+
+texit