* Use common prefixes for project specific build step functions: - ossbuild-prepare-<prjname> - ossbuild-build-<prjname> - ossbuild-install-<prjname> * New project specific build step function: ossbuild-configure-ok-p-<prjname> * New environment variable OSSBUILD_SKIP. Allows users to skip listed (comma separated) build steps. * Remove `-generic` from common function names. * Improve ossbuild-pushd/popd functions. * Improve doc comments. * Adapt projects to renamed functions
555 lines
16 KiB
Bash
555 lines
16 KiB
Bash
# Helper functions for project build scripts.
|
|
|
|
[ -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 "enter directory: '${PWD}'"
|
|
}
|
|
|
|
#D# Logged popd.
|
|
ossbuild-popd() {
|
|
local dirstack_size
|
|
dirstack_size="$(dirs -p | wc -l)"
|
|
[ "${dirstack_size}" -gt 1 ] || {
|
|
# directory stack is empty (the current directory is always present)
|
|
return 0
|
|
}
|
|
bsl_logi "leave directory: '${PWD}'"
|
|
bsl_run_cmd_quiet popd
|
|
[ "${dirstack_size}" -le 2 ] || 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 <PRJ>_VERSION
|
|
# 2. get value of project variable <PRJ>_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 stdout (if it exists).
|
|
ossbuild-project-build-step-fn() {
|
|
local step="${1}"
|
|
local step_fn="ossbuild-${step}-${OSSBUILD_PROJECT}"
|
|
bsl_run_cmd_nostdout declare -F "${step_fn}" || return 2
|
|
printf '%s' "${step_fn}"
|
|
}
|
|
|
|
#D# Run project build step function (if it exists).
|
|
ossbuild-project-build-step-run() {
|
|
local step="${1}" step_fn
|
|
step_fn="$(ossbuild-project-build-step-fn "${step}")" || return "${?}"
|
|
shift
|
|
bsl_logi "${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 ] || {
|
|
# use global var for error reporting
|
|
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-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-prepare \
|
|
"${archive_url}" \
|
|
"${src_tree}" \
|
|
"${archive}"
|
|
}
|
|
|
|
#D# ...
|
|
ossbuild-autotools-configure-ok-p() {
|
|
local build_tree="${1}" \
|
|
configlog="${2:-config.log}" \
|
|
regexp="${3:-^configure: exit 0$}"
|
|
[ -n "${build_tree}" ] || ossbuild-project-var-get -v build_tree BUILD_TREE
|
|
configlog="${build_tree}/${configlog}"
|
|
|
|
[ -e "${configlog}" ] || return 2
|
|
grep -qE "${regexp}" "${configlog}" || return 3
|
|
bsl_logi "${OSSBUILD_PROJECT}: reuse previous configure results" \
|
|
"(found 'configure: exit 0' in '${configlog}')"
|
|
}
|
|
|
|
#D# ...
|
|
ossbuild-autotools-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}"
|
|
|
|
local configure_ok_p_fn
|
|
configure_ok_p_fn="$(ossbuild-project-build-step-fn "configure-ok-p")" \
|
|
|| configure_ok_p_fn='ossbuild-autotools-configure-ok-p'
|
|
if ! "${configure_ok_p_fn}" "${build_tree}"; then
|
|
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-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_SKIP:=}" =~ (^|,)${OSSBUILD_STEP}(,|$) ]] || {
|
|
bsl_logi "${OSSBUILD_PROJECT}: skip ${OSSBUILD_SKIP}"
|
|
continue
|
|
}
|
|
ossbuild-project-build-step-run "${OSSBUILD_STEP}" "${@}"
|
|
done
|
|
}
|