Skip to content

Commit

Permalink
Try to make it easier to maintain for packages (and distros) (#471)
Browse files Browse the repository at this point in the history
* Try to make it easier to maintain for packages

* Act on test results: we need the _pkgs suffix to identify distro

* Act on self-review: be more specific in README

* Act on test results: make sure build header appears before first entries

* Simplify code for our specific use case

The _apk, _rpm and _dpkg functions do an echo (they build a string)
That string is used to both output what failed, but also be evaluated
to probe for a given package install

* Act on test results: further normalise ID string

In RHEL the value is "rhel", with the quote
(and since we're not sourcing the file, but parsing it, we
need to do some extra work)

* Act on self-review results: simplify probe code

We're already, out of the functions, doing the
/dev/null + 2>&1 dance

* Act on test results: on rhel g++ is called gcc-c++

* Act on local tests: add missing Alpine/apk dep. automake

* Act on local tests: go back to a more robust dpkg-query check

* "Remove" opensuse package detection

This appears to require zypper to be done, so can probably wait for
community contribution to happen

* Act on local test results: make commands easier to spot

* Act on review comment: make true/false yes/no instead of 1/0

For readability purposes

* Act on review comment: merge log sentences

* Act on self-review: be consistent in writing
  • Loading branch information
paulo-ferraz-oliveira committed Oct 9, 2023
1 parent 04f209c commit aad7ea3
Show file tree
Hide file tree
Showing 2 changed files with 135 additions and 97 deletions.
11 changes: 9 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@

Easy building and installing of [Erlang/OTP](https://www.erlang.org) instances.

`kerl` aims to be shell agnostic and its only dependencies, excluding what's
required to actually build Erlang/OTP, are `curl` and `git`.
`kerl` aims to be shell agnostic (it runs in a POSIX shell) and its only dependencies,
excluding what's required to actually build Erlang/OTP, are `curl` and `git`.

All is done so that, once a specific release has been built, creating a new
installation is as fast as possible.
Expand Down Expand Up @@ -360,6 +360,13 @@ Directory in which `kerl` will clone Git repositories for building.

### Build configuration

#### `KERL_CHECK_BUILD_PACKAGES`

Default: yes (Enabled)
Kerl will try to probe your Linux distro for build-required packages logging
where the probes fail. You can turn off this behaviour by setting the
environment variable to something other than 1.

#### `KERL_AUTOCLEAN`

Default: 1 (Enabled)
Expand Down
221 changes: 126 additions & 95 deletions kerl
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,7 @@ KERL_GIT_DIR=${KERL_GIT_DIR:="${KERL_BASE_DIR:?}"/gits}
KERL_GIT_BASE=https://raw.githubusercontent.com/kerl/kerl/master
KERL_COLORIZE=${KERL_COLORIZE:=$KERL_COLOR_AVAILABLE}
KERL_INCLUDE_RELEASE_CANDIDATES=${KERL_INCLUDE_RELEASE_CANDIDATES:=no}
KERL_CHECK_BUILD_PACKAGES=${KERL_CHECK_BUILD_PACKAGES:="yes"}

if [ -n "$OTP_GITHUB_URL" ]; then
_OGU="$OTP_GITHUB_URL"
Expand Down Expand Up @@ -524,93 +525,6 @@ assert_build_name_unused() {
fi
}

_check_required_pkgs() {
has_dpkg=$(command -v dpkg)
has_rpm=$(command -v rpm)
has_apk=$(command -v apk)
if [ -n "$has_dpkg" ] || [ -n "$has_rpm" ] || [ -n "$has_apk" ]; then
# found either dpkg, rpm or apk (or maybe even both!)
if [ -n "$has_dpkg" ] && [ -n "$has_rpm" ]; then
l=w stderr 'WARNING: You appear to have BOTH rpm and dpkg. This is very strange. No package checks done.'
elif [ -n "$has_dpkg" ] && [ -n "$has_apk" ]; then
l=w stderr 'WARNING: You appear to have BOTH apk and dpkg. This is very strange. No package checks done.'
elif [ -n "$has_rpm" ] && [ -n "$has_apk" ]; then
l=w stderr 'WARNING: You appear to have BOTH apk and rpm. This is very strange. No package checks done.'
elif [ -n "$has_dpkg" ]; then
_check_dpkg
elif [ -n "$has_rpm" ]; then
_check_rpm
elif [ -n "$has_apk" ]; then
_check_apk
fi
fi
}

_dpkg_is_installed() {
# gratefully stolen from
# https://superuser.com/questions/427318/test-if-a-package-is-installed-in-apt
# returns 0 (true) if found, 1 otherwise
dpkg-query -Wf'${db:Status-abbrev}' "$1" 2>/dev/null | \grep -q '^i'
}

_check_dpkg() {
required='
libssl-dev
make
automake
autoconf
libncurses5-dev
gcc
g++
'
for pkg in $required; do
if ! _dpkg_is_installed "$pkg"; then
l=w stderr "WARNING: It appears that a required development package '$pkg' is not installed."
fi
done
}

_rpm_is_installed() {
rpm --quiet -q "$1" >/dev/null 2>&1
}

_check_rpm() {
required='
openssl-devel
make
automake
autoconf
ncurses-devel
gcc
g++
'
for pkg in $required; do
if ! _rpm_is_installed "$pkg"; then
l=w stderr "WARNING: It appears a required development package '$pkg' is not installed."
fi
done
}

_apk_is_installed() {
apk -e info "$1" >/dev/null 2>&1
}

_check_apk() {
required='
openssl-dev
make
autoconf
ncurses-dev
gcc
g++
'
for pkg in $required; do
if ! _apk_is_installed "$pkg"; then
l=w stderr "WARNING: It appears a required development package '$pkg' is not installed."
fi
done
}

do_git_build() {
git_url=$1
git_version=$2
Expand Down Expand Up @@ -754,7 +668,127 @@ _flags() {
esac
}

_dpkg() {
# gratefully stolen from
# https://superuser.com/questions/427318/test-if-a-package-is-installed-in-apt
# returns 0 (true) if found, 1 otherwise
echo "dpkg-query -Wf'\${db:Status-abbrev}' \"$1\" 2>/dev/null | \grep -q '^i'"
}

_rpm() {
echo "rpm -q \"$1\""
}

_apk() {
echo "apk -e info \"$1\""
}

# To add package guessing magic for your Linux distro/package,
# create a variable named _KPP_<distro>_pkgs where the content
# is an array with packages, and then create a unique probe
# command, in variable _KPP_<distro>_probe to check from the package manager.
# It should return 0 if package installation is Ok, non-0 otherwise.

_KPP_alpine_pkgs="openssl-dev make automake autoconf ncurses-dev gcc g++"
_KPP_alpine_probe="_apk"

_KPP_debian_pkgs="libssl-dev make automake autoconf libncurses5-dev gcc g++"
_KPP_debian_probe="_dpkg"

_KPP_fedora_pkgs="openssl-devel make automake autoconf ncurses-devel gcc g++"
_KPP_fedora_probe="_rpm"

_KPP_linuxmint_pkgs="libssl-dev make automake autoconf libncurses5-dev gcc g++"
_KPP_linuxmint_probe="_dpkg"

_KPP_rhel_pkgs="openssl-devel make automake autoconf ncurses-devel gcc gcc-c++"
_KPP_rhel_probe="_rpm"

_KPP_ubuntu_pkgs="libssl-dev make automake autoconf libncurses5-dev gcc g++"
_KPP_ubuntu_probe="_dpkg"

parse_os_release() {
# $1: path to os-release
# returns:
# - 2 if release ID is not found in os-release file
os_release_id0=$(\grep "^ID=" "$1")
os_release_id=$(echo "${os_release_id0}" | \sed 's|.*=||' | \sed 's|"||g')
os_release_pretty_name0=$(\grep "^PRETTY_NAME=" "$1")
os_release_pretty_name=$(echo "${os_release_pretty_name0}" | \sed 's|.*=||' | \sed 's|"||g')
echo "[packages] Found ${os_release_pretty_name} with ID ${os_release_id}" >>"$LOGFILE"
if [ -z "${os_release_id}" ]; then
echo "[packages] Release ID not found in $1" >>"$LOGFILE"
echo "2"
else
echo "[packages] Release ID found in $1: ${os_release_id}" >>"$LOGFILE"
echo "${os_release_id}"
fi
}

get_id_from_os_release_files() {
# returns:
# 1 - if no release files exist
files="/etc/os-release /usr/lib/os-release"
for file in ${files}; do
if [ -f "${file}" ]; then
parsed=$(parse_os_release "${file}")
if [ "${parsed}" != "2" ]; then
break
fi
fi
done

if [ -z "${parsed}" ]; then
echo "[packages] Found no file in: ${files}. Bailing out..." >>"$LOGFILE"
echo "1"
fi

echo "${parsed}"
}

probe_pkgs() {
os_release_id=$(get_id_from_os_release_files)

if [ "${os_release_id}" = "1" ]; then
msg="[packages] Unable to determine Linux distro (no release files); not checking build packages."
echo "${msg}" >>"$LOGFILE"
l=w stderr "${msg}"
return
elif [ "${os_release_id}" = "2" ]; then
msg="[packages] Unable to determine Linux distro (no ID); not checking build packages."
echo "${msg}" >>"$LOGFILE"
l=w stderr "${msg}"
return
fi

kpp=$(eval echo \$_KPP_"${os_release_id}"_pkgs)
if [ -n "${kpp}" ]; then
echo "[packages] Found package declarations for your Linux distro: ${os_release_id}" >>"$LOGFILE"
for pkg in ${kpp}; do
cmd=$(eval echo "\$_KPP_${os_release_id}_probe ${pkg}")
probe=$(${cmd})
eval "${probe}" >/dev/null 2>&1
probe_res=$?
if [ "${probe_res}" != 0 ]; then
msg="[packages] Probe failed for ${pkg} (distro: ${os_release_id}): probe \"${probe}\" returned ${probe_res}"
echo "${msg}" >>"$LOGFILE"
l=w stderr "${msg}"
else
echo "[packages] Probe success for ${pkg} (distro: ${os_release_id})!" >>"$LOGFILE"
fi
done
else
msg="[packages] Unknown Linux distro ${os_release_id}; not checking build packages."
echo "${msg}" >>"$LOGFILE"
l=w stderr "${msg}"
fi
}

_do_build() {
LOGFILE="$KERL_BUILD_DIR/$2/otp_build_$1.log"
# Add a build date and env in logfile
echo "*** $(date) - kerl build $1 ***" >>"$LOGFILE"

case "$KERL_SYSTEM" in
Darwin)
# Ensure that the --enable-darwin-64bit flag is set on all macOS
Expand All @@ -778,23 +812,20 @@ _do_build() {
fi
;;
Linux)
# we are going to check here to see if the Linux uses dpkg or rpms
#
# this is a "best effort" attempt to discover if a Linux has the
# We implement a "best effort" attempt to discover if a Linux distro has the
# packages needed to build Erlang. We will always assume the user
# knows better than us and are going to go ahead and try to build
# Erlang anyway. But at least it will be a clear warning to the
# Erlang anyway. But at least there will be a clear warning to the
# user if a build fails.
_check_required_pkgs
if [ "${KERL_CHECK_BUILD_PACKAGES}" = "yes" ]; then
probe_pkgs
fi
;;
*) ;;
esac

ERL_TOP="$KERL_BUILD_DIR/$2/otp_src_$1"
cd "$ERL_TOP" || exit 1
LOGFILE="$KERL_BUILD_DIR/$2/otp_build_$1.log"
# Add a build date and env in logfile
echo "*** $(date) - kerl build $1 ***" >>"$LOGFILE"
env | grep KERL_BUILD_ | xargs -n1 echo "*" >>"$LOGFILE"

# Set configuation flags given applications white/black lists
Expand Down

0 comments on commit aad7ea3

Please sign in to comment.