Add a lot of new functions to lib/ossbuild-common.bash

This commit is contained in:
Kai Tetzlaff (qnas) 2023-06-29 22:08:23 +02:00
parent e7e9990871
commit 664827d55a

531
lib/ossbuild-common.bash Normal file → Executable file
View File

@ -1,6 +1,535 @@
# Helper functions for project build scripts.
# Check is current system is running GNU/Linux based QNAP QTS OS.
[ -d "${BSL_PATH:=${HOME}/.local/lib/bash/bsl}" ] || {
echo "[ERR] please set BSL_PATH" >&2
exit 1
}
source "${BSL_PATH}/init.bash"
bsl_load
unset QUIET OSSBUILD_PROJECT
# fail on errors when non-interactive
[ -n "${PS1:-}" ] || set -euo pipefail
#D# Check is current system is running GNU/Linux based QNAP QTS OS.
is-qnap-qts() {
[ -r '/etc/os-release' ] && grep -q '^NAME="QTS"$' '/etc/os-release'
}
#D# Check if variable DRY_RUN has a value greater than ``0``.
is-dry-run() {
! [ ${DRY_RUN:-0} -eq 0 ]
}
#D# Wrapper around pushd.
#
# The wrapper provides the following features:
#
# - return ``1`` if the provided directory does not exist
# - log directory changes
ossbuild-pushd() {
local d="${1}"
bsl_run_cmd_quiet pushd "${d}" || {
[ -d "${d}" ] || {
bsl_logfe "no such dir: '$(realpath -Lm "${d}")'"
return 1
}
bsl_logfe "failed to change directory to '$(realpath -Lm "${d}")'"
return 2
}
bsl_logi "directory: '${PWD}'"
}
#D# Logged popd.
ossbuild-popd() {
bsl_run_cmd_quiet popd || return 0
bsl_logi "directory: '${PWD}'"
}
#D# Run a shell command.
#
# This wrapper provides to following features:
#
# - write executed command to stdout (set QUIET=1 to disable the logging)
# - skip the command if func:is-dry-run returns a truthy value
ossbuild-cmd() {
local cmd=("${@}")
if is-dry-run; then
echo "[DRYRUN] ${cmd[@]}" >&2
else
[ -n "${QUIET:-}" ] || echo "${cmd[@]}" >&2
"${cmd[@]}"
fi
}
#D# Writes name of project specific variable to stdout.
ossbuild-project-var() {
local name="${OSSBUILD_PROJECT^^}_${1^^}"
printf '%s' "${name//-/_}"
}
#D# Write value of project specific variable to stdout.
#
# Args:
# name (str): the name of the project specific variable
# default (any): default value to be used if variable does not exist
#
# Returns:
# exit status (int): ``0`` in case of sucecess, ``1`` in case that the
# variable does not exist and no default value was provided
#
# stdout (str): value of the variable
ossbuild-project-var-get() {
#echo "1/ossbuild-project-var-get: ${@}" >&2
local quiet=0 name vname
local -a positional
while [ ${#} -gt 0 ]; do
case "${1}" in
-q | --quiet)
((++quiet))
;;
-v)
local outvar="${2}"
shift
;;
-*)
# ignore unknown option
;;
*)
positional+=("${1}")
;;
esac
shift
done
[ "${#positional[*]}" -gt 0 ] || {
bsl_logfe "missing project variable name"
return 10
}
name="${positional[0]}"
unset default
[ "${#positional[*]}" -le 1 ] || local default="${positional[1]}"
vname="$(ossbuild-project-var "${name}")"
#echo "2/ossbuild-project-var-get: ${vname}${default:+, default=${default}}" >&2
[[ -v "${vname}" ]] || {
if [ -v default ]; then
#echo "using default: '${vname}=${default}'" >&2
local "${vname}=${default}"
else
[ "${quiet}" -gt 0 ] || bsl_loge "project variable not set: '${vname}'"
return 1
fi
}
#echo "R/ossbuild-project-var-get: ${vname}='${!vname}'" >&2
if [ -v outvar ]; then
local -n ovar_ref="${outvar}"
# shellcheck disable=SC2034
printf -v ovar_ref '%s' "${!vname}"
else
printf '%s' "${!vname}"
fi
}
#D# Set value of project specific variable.
#
# Args:
# name (str): the name of the project specific variable
# value (any): value to be assigned to the variable
#
# Returns:
# exit status (int): ``0`` (the function should never fail)
#
# stdout (str): na
ossbuild-project-var-set() {
local name="${1}" value="${2}" vname
vname="$(ossbuild-project-var "${name}")"
bsl_logi "set project variable: ${vname}='${value}'"
declare -g "${vname}=${value}"
}
#D# Unset (delete) project specific variable.
#
# Args:
# name (str): the name of the project specific variable
#
# Returns:
# exit status (int): ``0`` (the function should never fail)
#
# stdout (str): na
ossbuild-project-var-unset() {
local name="${1}" vname
vname="$(ossbuild-project-var "${name}")"
bsl_logi "unset project variable: ${vname}"
unset "${vname}"
}
#D# Run a git command (see func:ossbuild-cmd).
ossbuild-git() {
ossbuild-cmd git "${@}"
}
#D# Write latest tagged version of a github project to stdout.
ossbuild-gh-version-latest() {
local owner="${1:-$(ossbuild-project-var-get GH_OWNER '')}" \
repo="${2:-$(ossbuild-project-var-get GH_REPO '')}"
[[ -n "${owner}" && -n "${repo}" ]] || return 1
printf '%s' "$(bsl_rtrim -s 'v' "$(bsl_ghapi_releases_tag_latest "${owner}" "${repo}")")"
}
# #D# WiP: Write download URL of ...
# ossbuild-gh-download-url() {
# local owner="${1:-$(ossbuild-project-var-get GH_OWNER '')}" \
# repo="${2:-$(ossbuild-project-var-get GH_REPO '')}"
#
# [[ -n "${owner}" && -n "${repo}" ]] || return 1
# #https://github.com/${owner}/${repo}/releases/download/v23.3/protobuf-23.3.tar.gz
# }
#D# Write the version string of OSSBUILD_PROJECT to stdout.
#
# The first of the following steps which has a non-empty result is used as
# version string:
#
# 1. get value of project variable VERSION
# 2. get value of project variable DEFAULT_VERSION
# 3. value returned by ``ossbuild-gh-version-latest`` #d#
# shellcheck disable=SC2120
ossbuild-project-version() {
local version
version="$(ossbuild-project-var-get -q VERSION)" || {
version="$(ossbuild-project-var-get -q DEFAULT_VERSION)" || {
local owner="${1:-$(ossbuild-project-var-get GH_OWNER '')}" \
repo="${2:-$(ossbuild-project-var-get GH_REPO '')}"
version="$(bsl_ltrim -s 'v' "$(ossbuild-gh-version-latest "${owner}" "${repo}")")" || return 10
}
}
printf '%s' "${version}"
}
#D# Initialize a project hosted on github.com.
ossbuild-project-gh-init() {
local owner="${1}" repo="${2}" name="${3:-}"
declare -g OSSBUILD_PROJECT="${name}"
[ -n "${OSSBUILD_PROJECT}" ] || OSSBUILD_PROJECT="${repo}"
bsl_logi "initialize github project: ${OSSBUILD_PROJECT} (${owner}/${repo})"
ossbuild-project-var-set GH_OWNER "${owner}"
ossbuild-project-var-set GH_REPO "${repo}"
ossbuild-project-var-set GH_URL "https://github.com/${owner}/${repo}"
# shellcheck disable=SC2119
ossbuild-project-var-set VERSION "$(ossbuild-project-version)"
}
#D# Write value of source base path (default: ``${HOME}/work/oss``) to stdout.
#
# Returns:
# exit status (int): ``0``
#
# stdout (str): value of OSSBUILD_SRC_BASE_PATH
ossbuild-src-base() {
printf '%s' "${OSSBUILD_SRC_BASE_PATH:-${HOME}/work/oss}"
}
#D# Create OSSBUILD_SRC_BASE_PATH if it does not exist.
#
# Returns:
# exit status (int): ``0``
#
# stdout (str): na
ossbuild-src-base-create() {
local src_base
src_base="$(ossbuild-src-base)"
[ -d "${src_base}" ] || ossbuild-cmd mkdir "${src_base}"
}
#D# Write versioned project name to stdout.
#
# Args:
# version (str): [optional] project version
# name (any): [optional] project name
#
# Returns:
# exit status (int): ``0``
#
# stdout (str): project name inclusing version string
ossbuild-project-namever-gnu() {
# shellcheck disable=SC2119
local version="${1:-$(ossbuild-project-version)}" name="${2:-${OSSBUILD_PROJECT}}"
printf '%s' "${name}${version:+-${version}}"
}
#D# Write project specific archive file name to stdout.
#
# Args:
# version (str): [optional] project version
# name (any): [optional] project name
#
# Returns:
# exit status (int): ``0``
#
# stdout (str): archive file name
ossbuild-project-archive-gnu() {
# shellcheck disable=SC2119
local version="${1:-$(ossbuild-project-version)}" name="${2:-${OSSBUILD_PROJECT}}"
printf '%s' "$(ossbuild-project-namever-gnu "${@}").tar.gz"
}
#D# ...
ossbuild-project-src-path() {
local name="${1}"
printf '%s' "$(ossbuild-src-base)/${name}"
}
#D# Download a file.
ossbuild-dl() {
local url="${1}" file_or_dir="${2:-$(basename "${1}")}"
local dst
if [ -d "${file_or_dir}" ]; then
dst="${file_or_dir}/$(basename "${url}")"
elif bsl_path_relative_p "${file_or_dir}"; then
dst="$(ossbuild-project-src-path "${file_or_dir}")"
else
dst="${file_or_dir}"
fi
[ -e "${dst}" ] || ossbuild-cmd curl -qLs --fail -o "${dst}" "${url}"
[ -n "${QUIET:-}" ] || printf '%s' "${dst}"
}
#D# Download a TAR archive to OSSBUILD_SRC_BASE_PATH and unpack it.
ossbuild-src-tar() {
local url="${1}" file_or_dir="${2:-$(basename "${1}")}"
local archive
archive="$(ossbuild-dl "${url}" "${file_or_dir}")"
bsl_logd "archive:'${archive}'"
ossbuild-cmd tar -xf "${archive}" -C "$(dirname "${archive}")"
}
#D# ...
ossbuild-src-git() {
local url="${1}" prj_name="${2:-$(basename "${1}" .git)}"
local src_tree
src_tre="$(ossbuild-project-src-path "${prj_name}")"
ossbuild-git clone "${url}" "${src_tree}"
}
#D# Check if PATH is writable.
#
# Args:
# path (str): path to be checked
#
# Returns:
# exit status (int): ``0`` if PATH is writable, ``1`` otherwise
#
# stdout (str): na
ossbuild-writable-p() {
local tgt="${1}"
if [ -e "${tgt}" ]; then
test -w "${tgt}"
else
test -w "$(dirname "${p}")"
fi
}
#D# Install a file (automatically uses ``sudo`` if required).
ossbuild-install() {
local src="${1}" dst="${2}"
local cmd=('install')
ossbuild-writable-p "${dst}" || cmd=(sudo "${cmd}")
ossbuild-cmd "${cmd[@]}" "${src}" "${dst}"
}
#D# Create a symlink (automatically uses ``sudo`` if required).
ossbuild-symlink() {
local src="${1}" dst="${2}"
local cmd=('ln' '-s' '-fr')
ossbuild-writable-p "${dst}" || cmd=(sudo "${cmd}")
ossbuild-cmd "${cmd[@]}" "${src}" "${dst}"
}
#D# Check if tests are enabled.
#
# Evaluates global and project specific BUILD_TESTS variable. Tests are enabled
# if:
#
# - the project specific value of BUILD_TESTS > 0 or
# - the global value of BUILD_TESTS > 0 and the project specific value is
# not explicitly set to ``0``.
#
# Returns:
# exit status (int): ``0`` if tests should run, ``1`` otherwise
#
# stdout (str): na
ossbuild-run-tests-p() {
[ "$(ossbuild-project-var-get BUILD_TESTS "${BUILD_TESTS:-0}")" -gt 0 ]
}
#D# Write name of project build step function to stdoutif (it exists).
ossbuild-project-build-step-fn() {
local step_fn="ossbuild-${OSSBUILD_PROJECT}-${1}"
bsl_run_cmd_nostdout declare -F "${step_fn}" || return 1
printf '%s' "${step_fn}"
}
#D# Run project build step function (if it exists).
ossbuild-project-build-step-run() {
local step_fn
step_fn="$(ossbuild-project-build-step-fn "${@}")" || return 1
shift
bsl_logi "${OSSBUILD_STEP} '${OSSBUILD_PROJECT}'"
"${step_fn}" "${@}"
}
#D# Function to be called in case of errors during a project build.
ossbuild-report-error() {
bsl_loge "failed"
bsl_loge "${OSSBUILD_PROJECT}: failed to ${OSSBUILD_STEP}"
}
#D# Initialize a project.
ossbuild-project-init() {
trap ossbuild-report-error ERR
[ -v OSSBUILD_PROJECT ] || {
declare -g OSSBUILD_STEP='init'
OSSBUILD_PROJECT="$(basename "$(dirname "${BASH_SOURCE[-1]}")")"
}
ossbuild-src-base-create
[ -v PREFIX ] || {
declare -g PREFIX
if is-qnap-qts; then
PREFIX='/opt/local'
else
PREFIX='/usr/local'
fi
}
local version
ossbuild-project-var-get -q -v version VERSION || {
# shellcheck disable=SC2119
version="$(ossbuild-project-version)"
ossbuild-project-var-set VERSION "${version}"
}
}
#D# Check if project is configured to build in the source tree.
ossbuild-project-build-in-tree-p() {
[ "$(ossbuild-project-var-get SRC_TREE)" \
= "${ossbuild-project-var-get BUILD_TREE}" ]
}
ossbuild-archive-generic-prepare() {
local archive_url="${1}" src_tree="${2:-}" archive="${3:-}"
[ -n "${src_tree}" ] || \
src_tree="$(ossbuild-src-base)/$(ossbuild-project-namever-gnu)"
[ -n "${archive}" ] || archive="${src_tree}.tar.gz"
ossbuild-project-var-set ARCHIVE "${archive}"
ossbuild-project-var-set ARCHIVE_URL "${archive_url}"
ossbuild-project-var-set SRC_TREE "${src_tree}"
[ -e "${src_tree}" ] || ossbuild-src-tar "${archive_url}"
[ -d "${src_tree}" ] || {
bsl_loge "failed to create '${src_tree}'"
ossbuild-project-var-unset SRC_TREE
return 2
}
}
ossbuild-archive-gnu-prepare() {
local archive_base_url="${1}"
local src_tree archive archive_url
# shellcheck disable=SC2119
archive="$(ossbuild-project-archive-gnu)"
src_tree="$(ossbuild-src-base)/$(basename "${archive}" .tar.gz)"
archive_url="${archive_base_url}/${archive}"
ossbuild-archive-generic-prepare \
"${archive_url}" \
"${src_tree}" \
"${archive}"
}
#D# ...
ossbuild-autotools-configure-ok-p() {
local build_tree="${1}"
[ -n "${build_tree}" ] || ossbuild-project-var-get -v build_tree BUILD_TREE
local config_log="${build_tree}/config.log"
[ -e "${config_log}" ] && grep -qE '^configure: exit 0$' "${config_log}"
}
#D# ...
ossbuild-autotools-generic-build() {
local configure_extra_opts=(${@})
local src_tree build_tree
ossbuild-project-var-get -v src_tree SRC_TREE
ossbuild-project-var-get -q -v build_tree BUILD_TREE || {
build_tree="${src_tree}/ossbuild"
ossbuild-project-var-set BUILD_TREE "${build_tree}"
}
[ -d "${build_tree}" ] || mkdir -p "${build_tree}"
ossbuild-pushd "${build_tree}"
if ossbuild-autotools-configure-ok-p "${build_tree}"; then
bsl_logi "skip configure (found config.status)"
else
bsl_logi "run configure ..."
local configure=(
"${src_tree}/configure"
--prefix="${PREFIX}"
"${configure_extra_opts[@]}"
)
ossbuild-cmd "${configure[@]}"
fi
ossbuild-cmd make
ossbuild-popd
}
#D# ...
ossbuild-autotools-generic-install() {
local build_tree
ossbuild-project-var-get -v build_tree BUILD_tree
ossbuild-pushd "${build_tree}"
ossbuild-cmd sudo make install
ossbuild-popd
}
#D# Run all known project build steps.
ossbuild-project-build-steps-all() {
ossbuild-project-init
local msg=(
"building project: '${OSSBUILD_PROJECT}'"
)
local version
ossbuild-project-var-get -v version VERSION
[ -z "${version}" ] || msg+=("(${version})")
bsl_logi "${msg[@]}"
declare -g OSSBUILD_STEP
for OSSBUILD_STEP in prepare build install; do
ossbuild-project-build-step-run "${OSSBUILD_STEP}" "${@}"
done
}