From e9fa0774ed2e7e030a68f5b0ae51fe6dd69fe492 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luc=C3=ADa=20Andrea=20Illanes=20Albornoz?= Date: Fri, 17 Feb 2023 19:29:28 +0100 Subject: Make everything a bit faster. 0) Issues several prayers and sacrifices to Enki under threat of a terrible deluge sent down by Ellil 1) Convert fork-write/read exprs to be non-forking 2) Pass mostly everything by reference 3) Don't bother cleaning the variable namespace because Bourne shell is an abomination 4) Removes broken ./pkgtool.sh -s, --restart-at, --update-diff & ./build.sh --dump-{in,on-abort} 5) Cleanup --- subr.ex/ex_init.subr | 397 +++++++++++++++++++++++++++++++++++++++++++ subr.ex/ex_pkg.subr | 324 +++++++++++++++++++++++++++++++++++ subr.ex/ex_pkg_dispatch.subr | 378 ++++++++++++++++++++++++++++++++++++++++ subr.ex/ex_pkg_env.subr | 178 +++++++++++++++++++ subr.ex/ex_pkg_exec.subr | 219 ++++++++++++++++++++++++ subr.ex/ex_pkg_restart.subr | 331 ++++++++++++++++++++++++++++++++++++ 6 files changed, 1827 insertions(+) create mode 100644 subr.ex/ex_init.subr create mode 100644 subr.ex/ex_pkg.subr create mode 100644 subr.ex/ex_pkg_dispatch.subr create mode 100644 subr.ex/ex_pkg_env.subr create mode 100644 subr.ex/ex_pkg_exec.subr create mode 100644 subr.ex/ex_pkg_restart.subr (limited to 'subr.ex') diff --git a/subr.ex/ex_init.subr b/subr.ex/ex_init.subr new file mode 100644 index 00000000..9f5462b6 --- /dev/null +++ b/subr.ex/ex_init.subr @@ -0,0 +1,397 @@ +# +# set +o errexit -o noglob -o nounset is assumed. +# + +exp_setrstatus() { + local _epsrs_rstatus="${1#\$}" _epsrs_status="${2}"; + eval ${_epsrs_rstatus}=\"${_epsrs_status}\"; + return 0; +}; + +# +# ex_init_env() - initialise build environment +# @_rstatus: reference to out variable of status string on failure +# @_rhname: reference to out variable of build hostname +# @_ruser: reference to out variable of build user +# @_name_base: base name for messages and theme file(s) +# +# Return: zero (0) on success, non-zero (>0) on failure +# +ex_init_env() { + local _eie_rstatus="${1#\$}" _eie_rhname="${2#\$}" _eie_ruser="${3#\$}" \ + _eie_name_base="${4}" \ + _eie_fname="" _eie_lang="${LANG:-C}" _eie_lang_="" _eie_name="" \ + _eie_rc=0; + _eie_lang="${_eie_lang%%_*}"; + + if ! cd "${0%/*}"; then + _eie_rc=1; + exp_setrstatus "${_eie_rstatus}" 'failed to change working directory to \`'"${0%/*}"''\''.'; + elif ! umask 022; then + _eie_rc=1; + exp_setrstatus "${_eie_rstatus}" 'failed to set umask(2).'; + elif ! eval ${_eie_rhname}=\"\$\(hostname\)\"; then + _eie_rc=1; + exp_setrstatus "${_eie_rstatus}" 'failed to obtain hostname.'; + elif ! eval ${_eie_ruser}=\"\$\(id -nu\)\"; then + _eie_rc=1; + exp_setrstatus "${_eie_rstatus}" 'failed to obtain username.'; + else + for _eie_fname in \ + $(find subr.ex -name *.subr) \ + $(find subr.pkg -name *.subr) \ + $(find subr.rtl -name *.subr) \ + "etc/${_eie_name_base}.theme" \ + ; + do + if ! . "${_eie_fname}"; then + _eie_rc=1; + exp_setrstatus "${_eie_rstatus}" 'failed to source \`'"${_eie_fname}"''\''.'; + break; + fi; + done; + + if [ "${_eie_rc}" -eq 0 ]; then + if [ -e "${_eie_name_base}.local" ]; then + if ! . "${_eie_name_base}.local"; then + _eie_rc=1; + exp_setrstatus "${_eie_rstatus}" 'failed to source \`'"${_eie_name_base}"'.local'\''.'; + fi; + fi; + fi; + + if [ "${_eie_rc}" -eq 0 ]; then + for _eie_name in ${_eie_name_base} rtl; do + for _eie_lang_ in ${_eie_lang} C; do + _eie_fname="etc/${_eie_name}.msgs.${_eie_lang_}"; + if [ -e "${_eie_fname}" ]; then + if ! . "${_eie_fname}"; then + _eie_rc=1; + exp_setrstatus "${_eie_rstatus}" 'failed to source \`'"${_eie_fname}"''\''.'; + break; + fi; + + if [ -e "${_eie_fname}.local" ]; then + if ! . "${_eie_fname}.local"; then + _eie_rc=1; + exp_setrstatus "${_eie_rstatus}" 'failed to source \`'"${_eie_fname}"'.local'\''.'; + fi; + fi; + break; + fi; + done; + + if [ "${_eie_rc}" -ne 0 ]; then + break; + fi; + done; + fi; + fi; + export LANG=C LC_ALL=C; + + return "${_eie_rc}"; +}; + +# +# ex_init_getopts() - process command-line arguments +# @_rstatus: reference to out variable of status string on failure +# @_fn: reference to function called to process command-line argument +# @_optstring: getopts(1) optstring +# @...: command-line arguments as "${@}" +# +# Return: zero (0) on success, non-zero (>0) on failure +# +ex_init_getopts() { + local _eig_rstatus="${1#\$}" _eig_fn="${2}" _eig_optstring="${3}" \ + _eig_arg="" _eig_fn_rc=0 _eig_opt="" _eig_shiftfl=0 \ + OPTARG="" OPTIND=0; + shift 3; + + if ! "${_eig_fn}" init "${_eig_rstatus}"; then + return 1; + fi; + + while [ "${#}" -gt 0 ]; do + case "${1}" in + --*) + "${_eig_fn}" longopt "${_eig_rstatus}" "${1}"; + _eig_fn_rc="${?}"; + + case "${_eig_fn_rc}" in + 0) ;; + 1) return 1; ;; + + *) shift "$((${_eig_fn_rc} - 1))"; + continue; ;; + esac; + ;; + esac; + + if getopts "${_eig_optstring}" _eig_opt; then + "${_eig_fn}" opt "${_eig_rstatus}" "${_eig_opt}" "${OPTARG:-}" "${@}"; + _eig_fn_rc="${?}"; + + case "${_eig_fn_rc}" in + 0) ;; + 1) return 1; ;; + + *) shift "$((${_eig_fn_rc} - 1))"; + continue; ;; + esac; + else + "${_eig_fn}" nonopt "${_eig_rstatus}" "${@}"; + _eig_fn_rc="${?}"; + + case "${_eig_fn_rc}" in + 0) ;; + 1) return 1; ;; + + *) shift "$((${_eig_fn_rc} - 1))"; + continue; ;; + esac; + fi; + done; + + if ! "${_eig_fn}" done "${_eig_rstatus}"; then + return 1; + fi; + + return 0; +}; + +# +# ex_init_help() - display usage screen and exit if requested in command-line arguments +# @_rstatus: reference to out variable of status string on failure +# @_args_long: optional list of long (prefixed with `--') arguments +# @_name_base: base name for usage screen file +# @_optstring: getopts(1) optstring +# +# Return: zero (0) on success, non-zero (>0) on failure +# +ex_init_help() { + local _eih_rstatus="${1#\$}" _eih_args_long="${2}" \ + _eih_name_base="${3}" _eih_optstring="${4}" \ + _eih_arg_long="" _eih_opt="" _eih_shiftfl=0; + shift 4; + + while [ "${#}" -gt 0 ]; do + case "${1}" in + -h|--help) + if [ -t 1 ]; then + cat "etc/${_eih_name_base}.usage"; + else + sed 's/\[[0-9]\+m//g' "etc/${_eih_name_base}.usage"; + fi; + exit 0; + ;; + + *=*) shift; continue; + ;; + + *) _eih_shiftfl=0; + for _eih_arg_long in ${_eih_args_long}; do + if [ "${_eih_arg_long}" = "${1}" ]; then + _eih_shiftfl=1; + fi; + done; + if [ "${_eih_shiftfl}" = 1 ]; then + shift; continue; + fi; + ;; + esac; + + if getopts "${_eih_optstring}" _eih_opt 2>/dev/null; then + case "${_eih_opt}" in + h) + if [ -t 1 ]; then + cat "etc/${_eih_name_base}.usage"; + else + sed 's/\[[0-9]\+m//g' "etc/${_eih_name_base}.usage"; + fi; + exit 0; + ;; + esac; + shift $((${OPTIND}-1)); OPTIND=1; + else + shift; + fi; + done; + + return 0; +}; + +# +# ex_init_files() - initialise build files +# @_rstatus: reference to out variable of status string on failure +# @_rclean_builds: reference to in variable of -C argument value +# @_rdist: reference to in variable of -D argument value +# @_build_log_fname: absolute pathname to build log file +# @_build_log_last_fname: absolute pathname to last build log file +# @_build_status_in_progress_fname: absolute pathname to build-in-progress status file +# @_check_path_vars: list of pathname variables to check +# @_clear_env_vars_except: list of environment variables to not unset when clearing the environment +# @_clear_prefix_paths: list of directory pathnames to clear in the top-level prefix +# @_dlcachedir: absolute pathname to download cache directory +# @_prefix: absolute pathname to top-level prefix +# @_prefix_rpm: absolute pathname to RPM files prefix +# @_workdir: absolute pathname to build-specific temporary directory +# +# Return: zero (0) on success, non-zero (>0) on failure +# +ex_init_files() { + local _eif_rstatus="${1#\$}" _eif_rclean_builds="${2#\$}" _eif_rdist="${3#\$}" \ + _eif_build_log_fname="${4}" _eif_build_log_last_fname="${5}" \ + _eif_build_status_in_progress_fname="${6}" _eif_check_path_vars="${7}" \ + _eif_clear_env_vars_except="${8}" _eif_clear_prefix_paths="${9}" \ + _eif_dlcachedir="${10}" _eif_prefix="${11}" _eif_prefix_rpm="${12}" \ + _eif_workdir="${13}" \ + _eif_log_last_fname="" _eif_log_last_num=1 _eif_rc=0; + + if ! rtl_fileop mkdir "${_eif_dlcachedir}" "${_eif_workdir}"\ + || rtl_lmatch "${_eif_rdist}" "rpm" ","\ + && ! rtl_fileop mkdir "${_eif_prefix_rpm}"; then + _eif_rc=1; + rtl_setrstatus "${_eif_rstatus}" 'cannot create build directories.'; + elif [ -e "${_eif_build_status_in_progress_fname}" ]; then + _eif_rc=1; + rtl_setrstatus "${_eif_rstatus}" 'another build targeting this architecture and build type is currently in progress.'; + elif ! rtl_clean_env "${_eif_clear_env_vars_except}"; then + _eif_rc=1; + rtl_setrstatus "${_eif_rstatus}" 'failed to clean environment.'; + elif ! rtl_check_path_vars "${_eif_rstatus}" "${_eif_check_path_vars}"; then + _eif_rc=1; + else + export TMP="${_eif_workdir}" TMPDIR="${_eif_workdir}"; + touch "${_eif_build_status_in_progress_fname}"; + + if [ -e "${_eif_build_log_fname}" ]; then + while [ -e "${_eif_build_log_fname}.${_eif_log_last_num}" ]; do + : $((_eif_log_last_num+=1)); + done; + + _eif_log_last_fname="${_eif_build_log_fname}.${_eif_log_last_num}"; + rtl_fileop mv "${_eif_build_log_fname}" "${_eif_log_last_fname}"; + rtl_fileop ln_symbolic "${_eif_log_last_fname}" "${_eif_build_log_last_fname}"; + fi; + + rtl_fileop touch "${_eif_build_log_fname}"; rtl_log_set_fname "${_eif_build_log_fname}"; + if rtl_lmatch "${_eif_rclean_builds}" $"prefix" ","; then + trap "rm -f \"${_eif_build_status_in_progress_fname}\" 2>/dev/null; + + rtl_log_msg \"fatalexit\" \"${MSG_build_aborted}\"" HUP INT TERM USR1 USR2; + rtl_log_msg "info" "${MSG_build_clean_prefix}"; + + for _eif_pname in ${_eif_clear_prefix_paths}; do + if ! rtl_fileop rm "${_eif_prefix}/${_eif_pname}"; then + _eif_rc=1; + rtl_setrstatus "${_eif_rstatus}" 'failed to remove \`'"${_eif_prefix:+${_eif_prefix}/}${_eif_pname}'"'.'; + break; + fi; + done; + + trap - HUP INT TERM USR1 USR2; + fi; + + export PATH="${_eif_prefix}/bin${PATH:+:${PATH}}"; + fi; + + return "${_eif_rc}"; +}; + +# +# ex_init_logging() - initialise build logging +# @_rstatus: reference to out variable of status string on failure +# @_rverbose_tags: reference to inout variable of -V argument value +# @_verbose: -[vV] argument value +# +# Return: zero (0) on success, non-zero (>0) on failure +# +ex_init_logging() { + local _eil_rstatus="${1#\$}" _eil_rverbose_tags="${2#\$}" _eil_verbose="${3}" \ + _eil_tag="" _eil_tags="" _eil_tags_enable="" _eil_rc=0; + + rtl_log_clear_tags; + case "${_eil_verbose}" in + + 0) if eval [ \"\${#${_eil_rverbose_tags}}\" -eq 0 ]; then + rtl_log_enable_tags "${LOG_TAGS_normal}"; + fi; + ;; + + 1) rtl_log_enable_tags "${LOG_TAGS_verbose}"; ;; + + *) _eil_rc=1; + rtl_setrstatus "${_eil_rstatus}" 'invalid verbosity level (max. -v)'; + ;; + + esac; + + if [ "${_eil_rc}" -eq 0 ]; then + eval _eil_tags="\${${_eil_rverbose_tags}}"; + case "${_eil_tags}" in + + +*) rtl_log_enable_tags "${LOG_TAGS_normal}"; + eval ${_eil_rverbose_tags}="\${${_eil_rverbose_tags}#+}"; + ;; + + *) ;; + + esac; + + rtl_llift2 "${_eil_rverbose_tags}" \$_eil_tags "," " "; + + for _eil_tag in ${_eil_tags}; do + case "${_eil_tag}" in + + all) rtl_log_enable_tags "${LOG_TAGS_all}"; ;; + + clear|none) rtl_log_clear_tags; ;; + + normal) rtl_log_enable_tags "${LOG_TAGS_normal}"; ;; + + verbose) rtl_log_enable_tags "${LOG_TAGS_verbose}"; ;; + + *) rtl_lsearch_patternl2 \$LOG_TAGS_all \$_eil_tags_enable "${_eil_tag}" ","; + if [ "${#_eil_tags_enable}" -gt 0 ]; then + rtl_log_enable_tags "${_eil_tags_enable}"; + else + _eil_rc=1; + rtl_setrstatus "${_eil_rstatus}" 'invalid log tag or tag pattern \`'"${_eil_tag}"''\''.'; + break; + fi; + ;; + + esac; + done; + fi; + + return "${_eil_rc}"; +}; + +# +# ex_init_prereqs() - initialise build prerequisite commands +# @_rstatus: reference to out variable of status string on failure +# @_prereqs: list of prerequisite commands +# +# Return: zero (0) on success, non-zero (>0) on failure +# +ex_init_prereqs() { + local _eip_rstatus="${1#\$}" _eip_prereqs="${2}" \ + _eip_rc=0; + + if ! rtl_check_prereqs "${_eip_rstatus}" ${_eip_prereqs}; then + _eip_rc=1; + elif ! awk -V 2>/dev/null | grep -q "^GNU Awk "; then + _eip_rc=1; + rtl_setrstatus "${_eip_rstatus}" 'awk(1) in \$PATH must be GNU Awk.'; + elif ! (FNAME="$(mktemp)" && { trap "rm -f \"\${FNAME}\"" EXIT; \ + sed -i'' -e '' "${FNAME}" >/dev/null 2>&1; }); + then + _eip_rc=1; + rtl_setrstatus "${_eip_rstatus}" 'sed(1) in \${PATH} does not support the \`-i'\'' option.'; + fi; + + return "${_eip_rc}"; +}; + +# vim:filetype=sh textwidth=0 diff --git a/subr.ex/ex_pkg.subr b/subr.ex/ex_pkg.subr new file mode 100644 index 00000000..6a5670be --- /dev/null +++ b/subr.ex/ex_pkg.subr @@ -0,0 +1,324 @@ +# +# set +o errexit -o noglob -o nounset is assumed. +# + +# +# ex_pkg_check_depends() - check single named package for unsatisfied dependencies +# @_checkfl: enable (1) or inhibit (0) dependency expansion +# @_rpkg_disabled: reference to in variable of list of disabled packages +# @_rpkg_finished: reference to in variable of list of finished packages +# @_pkg_name: single package name +# @_rpkg_names: reference to in variable of list of package names +# @_workdir: pathname to build-specific temporary directory +# +# Return: zero (0) given no outstanding dependencies, non-zero (>0) otherwise +# Side effects: ${PKG_${_PKG_NAME}_DEPENDS_FULL} may be mutated +# +ex_pkg_check_depends() { + local _epcd_checkfl="${1}" _epcd_rpkg_disabled="${2}" _epcd_rpkg_finished="${3#\$}" \ + _epcd_pkg_name="${4}" _epcd_rpkg_names="${5#\$}" _epcd_workdir="${6}" \ + _epcd_dependfl=0 _epcd_depends="" _epcd_pkg_name_depend=""; + + if [ "${_epcd_checkfl}" -eq 1 ]; then + if ! rtl_get_var_unsafe \$_epcd_depends -u "PKG_"${_epcd_pkg_name}"_DEPENDS_FULL"\ + || [ "${_epcd_depends:+1}" != 1 ]; + then + if rtl_get_var_unsafe \$_epcd_depends -u "PKG_"${_epcd_pkg_name}"_DEPENDS"; + then + eval PKG_"${_epcd_pkg_name}"_DEPENDS_FULL='$(rtl_uniq ${_epcd_depends})'; + else + return 0; + fi; + fi; + + for _epcd_pkg_name_depend in ${_epcd_depends}; do + if ! rtl_lmatch "${_epcd_rpkg_disabled}" "${_epcd_pkg_name_depend}"\ + && ! rtl_lmatch "${_epcd_rpkg_finished}" "${_epcd_pkg_name_depend}"\ + && ! ex_pkg_state_test2 "${_epcd_workdir}" "${_epcd_pkg_name_depend}" finish; + then + if ! rtl_lmatch "${_epcd_rpkg_names}" "${_epcd_pkg_name_depend}"; then + rtl_log_msg "fatalexit" "${MSG_build_unknown_dep}" "${_epcd_pkg_name_depend}" "${_epcd_pkg_name}"; + else + _epcd_dependfl=1; break; + fi; + fi; + done; + fi; + + return "${_epcd_dependfl}"; +}; + +# +# ex_pkg_find_package() - find build group a single named package belongs to +# @_rgroup_name: reference to out variable of build group name +# @_group_names: build group names +# @_pkg_name: single named package +# +# Return: zero (0) on success, non-zero (>0) if package not found, group name on stdout if package was found. +# +ex_pkg_find_package() { + local _epfp_rgroup_name="${1#\$}" _epfp_group_names="${2}" _epfp_pkg_name="${3}" \ + _epfp_foundfl=0 _epfp_group_name="" _epfp_pkg_names=""; + + for _epfp_group_name in ${_epfp_group_names}; do + if rtl_get_var_unsafe \$_epfp_pkg_names -u "${_epfp_group_name}_PACKAGES"\ + && [ "${_epfp_pkg_names:+1}" = 1 ]\ + && rtl_lmatch \$_epfp_pkg_names "${_epfp_pkg_name}"; then + _epfp_foundfl=1; break; + fi; + done; + + case "${_epfp_foundfl:-0}" in + 0) eval ${_epfp_rgroup_name}=; + return 1; ;; + + 1) eval ${_epfp_rgroup_name}='${_epfp_group_name}'; + return 0; ;; + esac; +}; + +# +# ex_pkg_get_packages() - get list of packages belonging to single named build group +# @_rpkg_names: reference to out variable of package names +# @_group_name: build group name +# +# Return: zero (0) on success, non-zero (>0) on failure, list of package names on stdout on success. +# +ex_pkg_get_packages() { + local _epgp_rpkg_names="${1#\$}" _epgp_group_name="${2}" \ + _epgp_pkg_names=""; + + if rtl_get_var_unsafe \$_epgp_pkg_names -u "${_epgp_group_name}_PACKAGES"\ + && [ "${_epgp_pkg_names:+1}" = 1 ]; then + eval ${_epgp_rpkg_names}='${_epgp_pkg_names}'; + return 0; + else + eval ${_epgp_rpkg_names}=; + return 1; + fi; +}; + +# +# ex_pkg_load_vars() - load build variables +# @_rstatus: out reference to status string +# +# Return: zero (0) on success, non-zero (>0) on failure, build variables post-return on success. +# +ex_pkg_load_vars() { + local _eplv_rstatus="${1#\$}" \ + _eplv_rc=0 _eplv_fname=""; + + if ! rtl_lmatch \$ARCH "nt32 nt64"; then + _eplv_rc=1; + rtl_setrstatus "${_eplv_rstatus}" 'Error: invalid architecture \`'"${ARCH}"''\''.'; + elif ! rtl_lmatch \$BUILD_KIND "debug release"; then + _eplv_rc=1; + rtl_setrstatus "${_eplv_rstatus}" 'Error: unknown build type \`'"${BUILD_KIND}"''\''.'; + else + case "${ARCH}" in + nt32) DEFAULT_TARGET="i686-nt32-midipix"; ;; + nt64) DEFAULT_TARGET="x86_64-nt64-midipix"; ;; + esac; + + for _eplv_fname in \ + "${HOME}/midipix_build.vars" \ + "${HOME}/.midipix_build.vars" \ + ../midipix_build.vars \ + ./midipix.env; + do + if [ -r "${_eplv_fname}" ]; then + rtl_fileop source "${_eplv_fname}"; + fi; + done; + + if [ "${PREFIX:+1}" != 1 ]; then + _eplv_rc=1; + rtl_setrstatus "${_eplv_rstatus}" 'Error: ${PREFIX} empty or unset.'; + fi; + fi; + + return "${_eplv_rc}"; +}; + +# +# ex_pkg_load_groups() - load all available build groups +# @_rgroups: reference to out variable of build groups +# @_rgroups_noauoto: optional reference to out variable of build groups not built automatically +# @_rgroup_auto: reference to in variable of flag controlling whether to build group automatically +# @_rgroup_target: reference to in variable of build group targets +# +# Return: zero (0) on success, non-zero (>0) on failure. +# +ex_pkg_load_groups() { + local _eplg_rgroups="${1#\$}" _eplg_rgroups_noauto="${2#\$}" \ + _eplg_rgroup_auto="${3#\$}" _eplg_rgroup_target="${4#\$}" \ + _eplg_build_groups="" _eplg_build_groups_noauto="" \ + _eplg_fname="" _eplg_group="" _eplg_groups=""; + + for _eplg_fname in $(find ./groups -name *.group | sort); do + rtl_fileop source_opt "${_eplg_fname}"; + + if eval [ \"\${${_eplg_rgroup_target}:+1}\" = 1 ]; then + eval _eplg_group=\"\${${_eplg_rgroup_target}}\"; + eval unset ${_eplg_rgroup_target}; + else + _eplg_group="${_eplg_fname##*/}"; + _eplg_group="${_eplg_group%.group}"; + _eplg_group="${_eplg_group#*.}"; + fi; + + if ! rtl_lmatch \$_eplg_groups "${_eplg_group}"; then + rtl_lconcat \$_eplg_groups "${_eplg_group}"; + if eval [ \"\${${_eplg_rgroup_auto}:+1}\" = 1 ]; then + if eval [ \"\${${_eplg_rgroup_auto}:-0}\" -ne 0 ]; then + rtl_lconcat \$_eplg_build_groups "${_eplg_group}"; + else + rtl_lconcat \$_eplg_build_groups_noauto "${_eplg_group}"; + fi; + eval unset ${_eplg_rgroup_auto}; + else + rtl_lconcat \$_eplg_build_groups "${_eplg_group}"; + fi; + fi; + done; + + _eplg_build_groups="$(rtl_uniq "${_eplg_build_groups}")"; + eval ${_eplg_rgroups}=\"${_eplg_build_groups}\"; + + if [ "${_eplg_rgroups_noauto:+1}" = 1 ]; then + _eplg_build_groups_noauto="$(rtl_uniq "${_eplg_build_groups_noauto}")"; + eval ${_eplg_rgroups_noauto}=\"${_eplg_build_groups_noauto}\"; + fi; + + return 0; +}; + +# +# ex_pkg_unfold_depends() - unfold list of package names into dependency-expanded set of complete, disabled, finished, and outstanding package names +# @_rdisabled: reference to inout variable of disabled packages +# @_rfinished: reference to inout variable of finished packages +# @_rnames: reference to out variable of package names +# @_checkfl: enable (1) or inhibit (0) dependency expansion +# @_forcefl: enable (1) or inhibit (0) forcibly rebuilding finished packages +# @_group_name: build group name +# @_pkg_names: list of package names +# @_restart: optional whitespace-separated list of package names to rebuild +# @_test_finished: only exclude disabled packages (0,) split finished packages +# @_workdir: pathname to build-specific temporary directory +# +# Return: zero (0) on success, non-zero (>0) on failure. +# +ex_pkg_unfold_depends() { + local _epud_rdisabled="${1#\$}" _epud_rfinished="${2#\$}" _epud_rnames="${3#\$}" \ + _epud_checkfl="${4}" _epud_forcefl="${5}" _epud_group_name="${6}" \ + _epud_pkg_names="${7}" _epud_restart="${8}" _epud_test_finished="${9}" \ + _epud_workdir="${10}" \ + _epud_pkg_disabled="" _epud_pkg_force="" _epud_pkg_name="" \ + _epud_pkg_names_new="" _epud_restartfl=0; + + if [ "${_epud_restart:+1}" = 1 ]\ + && ! rtl_lmatch \$_epud_restart "ALL LAST"; then + rtl_lsearch \$_epud_pkg_names "${_epud_restart}"; + fi; + if [ "${_epud_restart:+1}" = 1 ]\ + && [ "${_epud_checkfl:-0}" -eq 1 ]; then + rtl_lunfold_depends 'PKG_${_rld_name}_DEPENDS' \$_epud_pkg_names ${_epud_pkg_names}; + _epud_pkg_names="$(rtl_uniq ${_epud_pkg_names})"; + fi; + + for _epud_pkg_name in ${_epud_pkg_names}; do + if [ "${_epud_restart}" = "ALL" ]\ + || rtl_lmatch \$_epud_restart "${_epud_pkg_name}"; then + _epud_restartfl=1; + else + _epud_restartfl=0; + fi; + + if rtl_get_var_unsafe \$_epud_pkg_disabled -u "PKG_${_epud_pkg_name}_DISABLED"\ + && [ "${_epud_pkg_disabled}" = 1 ]; + then + rtl_lconcat "${_epud_rdisabled}" "${_epud_pkg_name}"; + + elif [ "${_epud_test_finished:-1}" -eq 1 ]\ + && ex_pkg_state_test2 "${_epud_workdir}" "${_epud_pkg_name}" finish\ + && [ "${_epud_restartfl:-0}" -eq 0 ]\ + && [ "${_epud_forcefl:-0}" -ne 1 ]\ + && rtl_get_var_unsafe \$_epud_pkg_force -u "${_epud_group_name}_FORCE"\ + && [ "${_epud_pkg_force}" != 1 ]; + then + rtl_lconcat "${_epud_rfinished}" "${_epud_pkg_name}"; + + else + rtl_lconcat \$_epud_pkg_names_new "${_epud_pkg_name}"; + fi; + done; + + eval ${_epud_rdisabled}='$(rtl_uniq2 "${_epud_rdisabled}")'; + eval ${_epud_rfinished}='$(rtl_uniq2 "${_epud_rfinished}")'; + eval ${_epud_rnames}='$(rtl_uniq "${_epud_pkg_names_new}")'; + + return 0; +}; + +# +# ex_pkg_unfold_rdepends() - unfold list of package names into reverse dependency-expanded set of complete, disabled, finished, and outstanding package names +# @_rdisabled: reference to inout variable of disabled packages +# @_rfinished: reference to inout variable of finished packages +# @_rnames: reference to out variable of package names +# @_group_name: build group name +# @_pkg_names: list of package names +# @_restart: optional whitespace-separated list of package names to rebuild +# @_test_finished: only exclude disabled packages (0,) split finished packages +# @_workdir: pathname to build-specific temporary directory +# +# Return: zero (0) on success, non-zero (>0) on failure. +# +ex_pkg_unfold_rdepends() { + local _epur_rdisabled="${1#\$}" _epur_rfinished="${2#\$}" _epur_rnames="${3#\$}" \ + _epur_group_name="${4}" _epur_pkg_names="${5}" _epur_restart="${6}" \ + _epur_test_finished="${7}" _epur_workdir="${8}" \ + _epur_depends="" _epur_disabled=0 _epur_force=0 _epur_pkg_depends="" \ + _epur_pkg_name="" _epur_pkg_names_new="" _epur_pkg_name_depend="" \ + _epur_pkg_rdepends=""; + + for _epur_pkg_name_depend in ${_epur_restart}; do + for _epur_pkg_name in ${_epur_pkg_names}; do + if [ "${_epur_pkg_name}" = "${_epur_pkg_name_depend}" ]; then + continue; + + elif rtl_get_var_unsafe \$_epur_depends -u "PKG_"${_epur_pkg_name}"_DEPENDS"\ + && rtl_lunfold_depends 'PKG_${_rld_name}_DEPENDS' \$_epur_pkg_depends ${_epur_depends}\ + && [ "${_epur_pkg_depends:+1}" = 1 ]\ + && rtl_lmatch \$_epur_pkg_depends "${_epur_pkg_name_depend}"; + then + if rtl_get_var_unsafe \$_epur_disabled -u "PKG_${_epur_pkg_name}_DISABLED"\ + && [ "${_epur_disabled}" = 1 ]; + then + rtl_lconcat "${_epur_rdisabled}" "${_epur_pkg_name}"; + + elif [ "${_epur_test_finished}" -eq 1 ]\ + && ex_pkg_state_test2 "${_epur_workdir}" "${_epur_pkg_name}" finish\ + && rtl_get_var_unsafe \$_epur_force -u "${_epur_group_name}_FORCE"\ + && [ "${_epur_force}" != 1 ]; + then + rtl_lconcat "${_epur_rfinished}" "${_epur_pkg_name}"; + + elif [ "${_epur_test_finished:-1}" -eq 0 ]\ + || ! ex_pkg_state_test2 "${_epur_workdir}" "${_epur_pkg_name}" finish\ + || ( rtl_get_var_unsafe \$_epur_force -u "${_epur_group_name}_FORCE"\ + && [ "${_epur_force}" = 1 ] ); + then + rtl_lconcat \$_epur_pkg_names_new "${_epur_pkg_name}"; + fi; + fi; + done; + done; + + eval ${_epur_rdisabled}='$(rtl_uniq2 "${_epur_rdisabled}")'; + eval ${_epur_rfinished}='$(rtl_uniq2 "${_epur_rfinished}")'; + eval ${_epur_rnames}='$(rtl_uniq "${_epur_pkg_names_new}")'; + + return 0; +}; + +# vim:filetype=sh textwidth=0 diff --git a/subr.ex/ex_pkg_dispatch.subr b/subr.ex/ex_pkg_dispatch.subr new file mode 100644 index 00000000..f89b874b --- /dev/null +++ b/subr.ex/ex_pkg_dispatch.subr @@ -0,0 +1,378 @@ +# +# set +o errexit -o noglob -o nounset is assumed. +# + +# +# exp_pkg_dispatch_complete() - XXX +# @_dispatch_fn: top-level dispatch function name +# @_group_names: build group name(s) +# @_pkg_disabled: list of disabled packages +# @_pkg_finished: list of finished packages +# +# Return: zero (0) on success, non-zero (>0) on failure. +# +exp_pkg_dispatch_complete() { + local _epdc_dispatch_fn="${1}" _epdc_group_name="${2}" _epdc_pkg_disabled="${3}" \ + _epdc_pkg_finished="${4}" \ + _epdc_pkg_name=""; + + for _epdc_pkg_name in ${_epdc_pkg_disabled}; do + "${_epdc_dispatch_fn}" disabled_pkg "${_epdc_group_name}" "${_epdc_pkg_name}"; + done; + for _epdc_pkg_name in ${_epdc_pkg_finished}; do + "${_epdc_dispatch_fn}" skipped_pkg "${_epdc_group_name}" "${_epdc_pkg_name}"; + done; + + return 0; +}; + +# +# exp_pkg_dispatch_expand_packages() - expand build group name to list of packages ordered and filtered according to dependency and restart constraints +# @_rdisabled: reference to out variable of disabled packages +# @_rfinished: reference to out variable of finished packages +# @_rnames: reference to out variable of package names +# @_checkfl: enable (1) or inhibit (0) dependency expansion +# @_forcefl: enable (1) or inhibit (0) forcibly rebuilding finished packages +# @_group_name: build group name +# @_restart: optional whitespace-separated list of package names to rebuild +# @_reversefl: unfold reverse dependencies (1) or dependencies (0) +# @_workdir: pathname to build-specific temporary directory +# +# Return: zero (0) on success, non-zero (>0) on failure. +# +exp_pkg_dispatch_expand_packages() { + local _epdep_rdisabled="${1#\$}" _epdep_rfinished="${2#\$}" _epdep_rnames="${3#\$}" \ + _epdep_checkfl="${4}" _epdep_forcefl="${5}" _epdep_group_name="${6}" \ + _epdep_restart="${7}" _epdep_reversefl="${8}" _epdep_workdir="${9}" \ + _epdep_pkg_names=""; + + eval ${_epdep_rdisabled}=; + eval ${_epdep_rfinished}=; + eval ${_epdep_rnames}=; + + if rtl_get_var_unsafe \$_epdep_pkg_names -u "${_epdep_group_name}_PACKAGES"\ + && [ "${_epdep_pkg_names:+1}" = 1 ]; then + if [ "${_epdep_reversefl:-0}" -eq 0 ]; then + ex_pkg_unfold_depends \ + "${_epdep_rdisabled}" "${_epdep_rfinished}" \ + "${_epdep_rnames}" "${_epdep_checkfl}" "${_epdep_forcefl}" \ + "${_epdep_group_name}" "${_epdep_pkg_names}" \ + "${_epdep_restart}" 1 "${_epdep_workdir}"; + else + ex_pkg_unfold_rdepends \ + "${_epdep_rdisabled}" "${_epdep_rfinished}" \ + "${_epdep_rnames}" "${_epdep_group_name}" \ + "${_epdep_pkg_names}" "${_epdep_restart}" 1 \ + "${_epdep_workdir}"; + fi; + fi; + + eval ${_epdep_rdisabled}='$(rtl_uniq2 "${_epdep_rdisabled}")'; + eval ${_epdep_rfinished}='$(rtl_uniq2 "${_epdep_rfinished}")'; + eval ${_epdep_rnames}='$(rtl_uniq2 "${_epdep_rnames}")'; + + return 0; +}; + +# +# exp_pkg_dispatch_group() - dispatch a single build group +# @_rdispatch_count: reference to inout variable of dispatcher count +# @_rdispatch_count_cur: reference to inout variable of current dispatcher count +# @_dispatch_count_max: maximum dispatcher count +# @_dispatch_group_cur: current group +# @_dispatch_group_max: maximum group +# @_rdispatch_njobs: reference to inout variable of dispatcher count +# @_rdispatch_wait: reference to inout variable of package names in a wait state +# @_rpkg_disabled: reference to in variable of list of disabled packages +# @_rpkg_finished: reference to inout variable of list of finished packages +# @_rpkg_names: reference to inout variable of list of package names +# @_build_steps_default: list of default build steps +# @_build_vars_default: list of default build variables +# @_checkfl: enable (1) or inhibit (0) dependency expansion +# @_dispatch_fn: top-level dispatch function name +# @_group_name: build group name +# @_njobs_max: maximum count of simultaneous jobs +# @_pipe_path: pathname to build FIFO +# @_restart_at: optional comma-separated list of build steps at which to rebuild or ALL or LAST +# @_workdir: pathname to build-specific temporary directory +# +# Return: zero (0) on success, non-zero (>0) on failure. +# +exp_pkg_dispatch_group() { + local _epdg_rdispatch_count="${1#\$}" _epdg_rdispatch_count_cur="${2#\$}" _epdg_dispatch_count_max="${3#\$}" \ + _epdg_dispatch_group_cur="${4#\$}" _epdg_dispatch_group_max="${5#\$}" _epdg_rdispatch_njobs="${6#\$}" \ + _epdg_rdispatch_wait="${7#\$}" _epdg_rpkg_disabled="${8#\$}" _epdg_rpkg_finished="${9#\$}" \ + _epdg_rpkg_names="${10#\$}" _epdg_build_steps_default="${11}" _epdg_build_vars_default="${12}" \ + _epdg_checkfl="${13}" _epdg_dispatch_fn="${14}" _epdg_group_name="${15}" _epdg_njobs_max="${16}" \ + _epdg_pipe_path="${17}" _epdg_restart_at="${18}" _epdg_workdir="${19}" \ + _epdg_perc_group=0 _epdg_perc_pkg=0 _epdg_pipe_msg="" _epdg_pkg_name="" _epdg_rc=0; + + rtl_fileop mkfifo "${_epdg_pipe_path}"; + while true; do + while eval [ \"\${${_epdg_rdispatch_njobs}:-0}\" -gt 0 ]\ + && read _epdg_pipe_msg; + do + + case "${_epdg_pipe_msg%% *}" in + + done) _epdg_pkg_name="${_epdg_pipe_msg#done * }"; + eval : '$(('${_epdg_rdispatch_count_cur}'+=1))'; + eval : '$(('${_epdg_rdispatch_njobs}'-=1))'; + + rtl_lconcat "${_epdg_rpkg_finished}" "${_epdg_pkg_name}"; + rtl_percentage2 \$_epdg_dispatch_group_cur \$_epdg_dispatch_group_max \$_epdg_perc_group; + rtl_percentage2 "${_epdg_rdispatch_count_cur}" \$_epdg_dispatch_count_max \$_epdg_perc_pkg; + + "${_epdg_dispatch_fn}" \ + finish_pkg ${_epdg_pipe_msg#done } \ + "${_epdg_dispatch_count_max}" \ + "${_epdg_perc_group}" "${_epdg_perc_pkg}"; + + rtl_lfilter "${_epdg_rpkg_names}" "${_epdg_pkg_name}"; + rtl_lfilter "${_epdg_rdispatch_wait}" "${_epdg_pkg_name}"; + + if eval [ \"\${${_epdg_rpkg_names}:+1}\" = 1 ]\ + && [ "${_epdg_rc}" -eq 0 ]; + then + if eval [ \"\${${_epdg_rdispatch_njobs}:-0}\" -ne \"\${_epdg_njobs_max}\" ]; then + exp_pkg_dispatch_packages \ + "${_epdg_rdispatch_count}" \ + "${_epdg_rdispatch_count_cur}" \ + "${_epdg_dispatch_count_max}" \ + "${_epdg_dispatch_group_cur}" \ + "${_epdg_dispatch_group_max}" \ + "${_epdg_rdispatch_njobs}" \ + "${_epdg_rdispatch_wait}" \ + "${_epdg_rpkg_disabled}" \ + "${_epdg_rpkg_finished}" "${_epdg_rpkg_names}" \ + "${_epdg_build_steps_default}" \ + "${_epdg_build_vars_default}" "${_epdg_checkfl}" \ + "${_epdg_dispatch_fn}" "${_epdg_group_name}" \ + "${_epdg_njobs_max}" "${_epdg_pipe_path}" \ + "${_epdg_restart_at}" "${_epdg_workdir}"; + fi; + elif eval [ \"\${${_epdg_rdispatch_njobs}:-0}\" -eq 0 ]; then + break; + fi; + ;; + + fail) eval : '$(('${_epdg_rdispatch_njobs}'-=1))'; + _epdg_rc=1; + "${_epdg_dispatch_fn}" \ + fail_pkg ${_epdg_pipe_msg#fail } \ + "${_epdg_dispatch_count_max}"; + ;; + + msg_pkg) + "${_epdg_dispatch_fn}" msg_pkg ${_epdg_pipe_msg#msg_pkg }; + ;; + + step) "${_epdg_dispatch_fn}" step_pkg ${_epdg_pipe_msg#step }; + ;; + + esac; done <>"${_epdg_pipe_path}"; + + if eval [ \"\${${_epdg_rpkg_names}:+1}\" = 1 ]\ + && [ "${_epdg_rc}" -eq 0 ]; + then + if eval [ \"\${${_epdg_rdispatch_njobs}:-0}\" -ne \"\${_epdg_njobs_max}\" ]; then + exp_pkg_dispatch_packages \ + "${_epdg_rdispatch_count}" \ + "${_epdg_rdispatch_count_cur}" \ + "${_epdg_dispatch_count_max}" \ + "${_epdg_dispatch_group_cur}" \ + "${_epdg_dispatch_group_max}" \ + "${_epdg_rdispatch_njobs}" \ + "${_epdg_rdispatch_wait}" \ + "${_epdg_rpkg_disabled}" \ + "${_epdg_rpkg_finished}" "${_epdg_rpkg_names}" \ + "${_epdg_build_steps_default}" \ + "${_epdg_build_vars_default}" "${_epdg_checkfl}" \ + "${_epdg_dispatch_fn}" "${_epdg_group_name}" \ + "${_epdg_njobs_max}" "${_epdg_pipe_path}" \ + "${_epdg_restart_at}" "${_epdg_workdir}"; + fi; + elif eval [ \"\${${_epdg_rdispatch_njobs}:-0}\" -eq 0 ]; then + break; + fi; + done; + + rtl_fileop rm "${_epdg_pipe_path}"; + + return "${_epdg_rc}"; +}; + +# +# exp_pkg_dispatch_packages() - dispatch set of packages +# @_rdispatch_count: reference to inout variable of dispatcher count +# @_dispatch_count_cur: current dispatcher count +# @_dispatch_count_max: maximum dispatcher count +# @_dispatch_group_cur: current group +# @_dispatch_group_max: maximum group +# @_rdispatch_njobs: reference to inout variable of dispatcher count +# @_rdispatch_wait: reference to inout variable of package names in a wait state +# @_rpkg_disabled: reference to in variable of list of disabled packages +# @_rpkg_finished: reference to inout variable of list of finished packages +# @_rpkg_names: reference to inout variable of list of package names +# @_build_steps_default: list of default build steps +# @_build_vars_default: list of default build variables +# @_checkfl: enable (1) or inhibit (0) dependency expansion +# @_dispatch_fn: top-level dispatch function name +# @_group_name: build group name +# @_njobs_max: maximum count of simultaneous jobs +# @_pipe_path: pathname to parent-child process FIFO +# @_restart_at: optional comma-separated list of build steps at which to rebuild or ALL or LAST +# @_workdir: pathname to build-specific temporary directory +# +# Return: zero (0) on success, non-zero (>0) on failure. +# +exp_pkg_dispatch_packages() { + local _epdp2_rdispatch_count="${1#\$}" _epdp2_dispatch_count_cur="${2}" _epdp2_dispatch_count_max="${3}" \ + _epdp2_dispatch_group_cur="${4}" _epdp2_dispatch_group_max="${5}" _epdp2_rdispatch_njobs="${6#\$}" \ + _epdp2_rdispatch_wait="${7#\$}" _epdp2_rpkg_disabled="${8#\$}" _epdp2_rpkg_finished="${9#\$}" \ + _epdp2_rpkg_names="${10#\$}" _epdp2_build_steps_default="${11}" _epdp2_build_vars_default="${12}" \ + _epdp2_checkfl="${13}" _epdp2_dispatch_fn="${14}" _epdp2_group_name="${15}" _epdp2_njobs_max="${16}" \ + _epdp2_pipe_path="${17}" _epdp2_restart_at="${18}" _epdp2_workdir="${19}" \ + _epdp2_foundfl=0 _epdp2_njob=0 _epdp2_pkg_depends="" _epdp2_pkg_name=""; + + while eval [ \"\${${_epdp2_rdispatch_njobs}}\" -lt \"\${_epdp2_njobs_max}\" ]; do + _epdp2_foundfl=0; + eval _epdp2_pkg_names="\${${_epdp2_rpkg_names}}"; + for _epdp2_pkg_name in ${_epdp2_pkg_names}; do + if ! rtl_lmatch "${_epdp2_rdispatch_wait}" "${_epdp2_pkg_name}"\ + && ex_pkg_check_depends \ + "${_epdp2_checkfl}" "${_epdp2_rpkg_disabled}" \ + "${_epdp2_rpkg_finished}" "${_epdp2_pkg_name}" \ + "${_epdp2_rpkg_names}" "${_epdp2_workdir}"; + then + ex_pkg_exec \ + "${_epdp2_rdispatch_count}" \ + "${_epdp2_dispatch_count_cur}" "${_epdp2_dispatch_count_max}" \ + "${_epdp2_dispatch_group_cur}" "${_epdp2_dispatch_group_max}" \ + "${_epdp2_rdispatch_njobs}" "${_epdp2_rdispatch_wait}" \ + "${_epdp2_build_steps_default}" \ + "${_epdp2_build_vars_default}" "${_epdp2_dispatch_fn}" \ + "${_epdp2_group_name}" "${_epdp2_pipe_path}" \ + "${_epdp2_pkg_name}" "${_epdp2_restart_at}" \ + "${_epdp2_workdir}"; + _epdp2_foundfl=1; break; + fi; + done; + + if [ "${_epdp2_foundfl:-0}" -eq 0 ]; then + break; + fi; + done; + + return "${_epdp2_foundfl}"; +}; + +# +# ex_pkg_dispatch() - dispatch a set of build group +# @_rdispatch_wait: reference to out variable of package names in a wait state +# @_build_steps_default: list of default build steps +# @_build_vars_default: list of default build variables +# @_dispatch_fn: top-level dispatch function name +# @_group_names: build group name(s) +# @_groups_inhibit_deps: inhibit group-group dependency expansion +# @_njobs_max: maximum count of simultaneous jobs +# @_pipe_path: pathname to build FIFO +# @_restart: optional whitespace-separated list of package names to rebuild +# @_restart_at: optional comma-separated list of build steps at which to rebuild or ALL or LAST +# @_restart_recursive: optional flag specifiying either no dependency expansion (0,) dependency expansion (1,) dependency expansion and forcibly rebuild (2,) forcibly rebuild reverse dependencies (3.) +# @_workdir: pathname to build-specific temporary directory +# +# Return: zero (0) on success, non-zero (>0) on failure. +# +ex_pkg_dispatch() { + local _epd_rdispatch_wait="${1#\$}" _epd_build_steps_default="${2}" _epd_build_vars_default="${3}" \ + _epd_dispatch_fn="${4}" _epd_group_names="${5}" _epd_groups_inhibit_deps="${6}" _epd_njobs_max="${7}" \ + _epd_pipe_path="${8}" _epd_restart="${9}" _epd_restart_at="${10}" _epd_restart_recursive="${11}" \ + _epd_workdir="${12}" \ + _epd_checkfl=1 _epd_forcefl=0 _epd_perc_group=0 \ + _epd_pkg_disabled="" _epd_pkg_finished="" _epd_pkg_names="" _epd_pkg_name="" \ + _epd_pkg_dispatch_count=0 _epd_pkg_dispatch_count_cur=0 _epd_pkg_dispatch_count_max=0 \ + _epd_pkg_dispatch_group_cur=0 _epd_pkg_dispatch_group_max=0 \ + _epd_pkg_dispatch_njobs=0 \ + _epd_rc=0 _epd_reversefl=0; + + case "${_epd_groups_inhibit_deps:-0}" in + 0) rtl_lunfold_depends '${_rld_name}_GROUP_DEPENDS' \$_epd_group_names ${_epd_group_names}; + _epd_group_names="$(rtl_uniq2 \$_epd_group_names)"; + esac; + + if [ "${_epd_restart:+1}" = 1 ]; then + case "${_epd_restart_recursive:-0}" in + 0) _epd_checkfl=0; _epd_forcefl=0; _epd_reversefl=0; ;; + 1) _epd_checkfl=1; _epd_forcefl=0; _epd_reversefl=0; ;; + 2) _epd_checkfl=1; _epd_forcefl=1; _epd_reversefl=0; ;; + 3) _epd_checkfl=1; _epd_forcefl=1; _epd_reversefl=1; ;; + esac; + fi; + + rtl_llength \$_epd_pkg_dispatch_group_max \$_epd_group_names; + + for _epd_group_name in ${_epd_group_names}; do + _epd_pkg_disabled=""; + _epd_pkg_finished=""; + _epd_pkg_names=""; + eval ${_epd_rdispatch_wait}=; + _epd_pkg_dispatch_count=0; + _epd_pkg_dispatch_count_cur=0; + _epd_pkg_dispatch_count_max=0; + _epd_pkg_dispatch_njobs=0; + + rtl_percentage "${_epd_pkg_dispatch_group_cur}" "${_epd_pkg_dispatch_group_max}" \$_epd_perc_group; + + if "${_epd_dispatch_fn}" \ + start_group "${_epd_group_name}" \ + "" "${_epd_pkg_dispatch_group_cur}" \ + "${_epd_pkg_dispatch_group_max}" \ + "${_epd_perc_group}"; + then + if rtl_fileop mkdir "${_epd_workdir}"\ + && rtl_log_msg "verbose" "${MSG_build_resolving_deps}" "${_epd_group_name}"\ + && exp_pkg_dispatch_expand_packages \ + \$_epd_pkg_disabled \$_epd_pkg_finished \ + \$_epd_pkg_names "${_epd_checkfl}" \ + "${_epd_forcefl}" "${_epd_group_name}" \ + "${_epd_restart}" "${_epd_reversefl}" \ + "${_epd_workdir}" \ + && exp_pkg_dispatch_complete \ + "${_epd_dispatch_fn}" "${_epd_group_name}" \ + "${_epd_pkg_disabled}" "${_epd_pkg_finished}" \ + && rtl_log_msg "verbose" "${MSG_build_resolved_deps}" "${_epd_group_name}"\ + && rtl_llength \$_epd_pkg_dispatch_count_max \$_epd_pkg_names\ + && [ "${_epd_pkg_dispatch_count_max}" -gt 0 ]; + then + exp_pkg_dispatch_group \ + \$_epd_pkg_dispatch_count \$_epd_pkg_dispatch_count_cur \ + "${_epd_pkg_dispatch_count_max}" \ + "${_epd_pkg_dispatch_group_cur}" "${_epd_pkg_dispatch_group_max}" \ + \$_epd_pkg_dispatch_njobs "${_epd_rdispatch_wait}" \ + \$_epd_pkg_disabled \$_epd_pkg_finished \$_epd_pkg_names \ + "${_epd_build_steps_default}" "${_epd_build_vars_default}" \ + "${_epd_checkfl}" "${_epd_dispatch_fn}" "${_epd_group_name}" \ + "${_epd_njobs_max}" "${_epd_pipe_path}" "${_epd_restart_at}" \ + "${_epd_workdir}"; + _epd_rc="${?}"; + fi; + + : $((_epd_pkg_dispatch_group_cur+=1)); + rtl_percentage "${_epd_pkg_dispatch_group_cur}" "${_epd_pkg_dispatch_group_max}" \$_epd_perc_group; + + "${_epd_dispatch_fn}" \ + finish_group "${_epd_group_name}" \ + "" "${_epd_pkg_dispatch_group_cur}" \ + "${_epd_pkg_dispatch_group_max}" \ + "${_epd_perc_group}"; + if [ "${_epd_rc}" -ne 0 ]; then + break; + fi; + fi; + done; + + return "${_epd_rc}"; +}; + +# vim:filetype=sh textwidth=0 diff --git a/subr.ex/ex_pkg_env.subr b/subr.ex/ex_pkg_env.subr new file mode 100644 index 00000000..265262ae --- /dev/null +++ b/subr.ex/ex_pkg_env.subr @@ -0,0 +1,178 @@ +# +# set +o errexit -o noglob -o nounset is assumed. +# + +# +# exp_pkg_env_defaults() - set package variable defaults for single named package +# @_build_steps_default: list of default build steps +# @_pkg_name: single package name +# @_workdir: pathname to build-specific temporary directory +# +# Return: zero (0) on success, non-zero (>0) on failure +# +exp_pkg_env_defaults() { + local _epped_build_steps_default="${1}" _epped_pkg_name="${2}" _epped_workdir="${3}"; + + : ${PKG_NAME:="${_epped_pkg_name}"}; + : ${MIDIPIX_BUILD_PWD:="$(pwd)"}; + : ${PKG_BASE_DIR:="${_epped_workdir}/${_epped_pkg_name}-${PKG_BUILD_TYPE}-${PKG_TARGET}"}; + + if [ "${PKG_BUILD_STEPS_DISABLE:+1}" = 1 ]; then + rtl_lfilter2 \$_epped_build_steps_default \$PKG_BUILD_STEPS "${PKG_BUILD_STEPS_DISABLE:-}"; + else + : ${PKG_BUILD_STEPS:="${_epped_build_steps_default}"}; + fi; + + if [ "${PKG_URL:+1}" = 1 ]; then + : ${PKG_FNAME:="${PKG_URL##*/}"}; + fi; + + if [ "${PKG_SUBDIR:+1}" != 1 ]; then + if [ "${PKG_URLS_GIT:+1}" = 1 ]\ + && [ "${PKG_FNAME:+1}" = 1 ]; then + rtl_log_msg "fatalexit" "${MSG_pkg_fail_missing_vars}"; + elif [ "${PKG_URLS_GIT:+1}" = 1 ]; then + PKG_SUBDIR="${PKG_URLS_GIT%%=*}"; + else case "${PKG_FNAME:-}" in + *.t*) PKG_SUBDIR="${PKG_FNAME%%.t*}"; ;; + *) PKG_SUBDIR="${_epped_pkg_name}"; ;; + esac; fi; + fi; + + if [ "${PKG_BUILD_DIR:+1}" != 1 ]; then + case "${PKG_IN_TREE:-0}" in + 0) PKG_BUILD_DIR="obj"; ;; + 1) PKG_BUILD_DIR="${PKG_SUBDIR}"; ;; + esac; + fi; + + PKG_BUILD_DIR="${PKG_BASE_DIR}/${PKG_BUILD_DIR}"; + PKG_CONFIGURE="${PKG_BASE_DIR}/${PKG_CONFIGURE:-${PKG_SUBDIR}/configure}"; + PKG_DESTDIR="${PKG_BASE_DIR}/${PKG_DESTDIR:-destdir}"; + PKG_DESTDIR_HOST="${PKG_BASE_DIR}/${PKG_DESTDIR_HOST:-destdir_host}"; + + return 0; +}; + +# +# exp_pkg_env_set() - set package variables for single named package +# @_build_vars_default: list of default build variables +# @_group_name: build group name +# @_pkg_name: single package name +# +# Sets package variables from either defaults, defaults specific to build type, +# build group, package to inherit from if any, or package for a single named +# package, and exports variables optionally named in ${PKG_ENV_VARS_EXTRA}. +# +# Return: zero (0) on success, non-zero (>0) on failure +# +exp_pkg_env_set() { + local _eppes_build_vars_default="${1}" _eppes_group_name="${2}" _eppes_pkg_name="${3}" \ + _eppes_cmd_name="" _eppes_lvars="" _eppes_var_prefixes="" _eppes_vars_set="" \ + _eppes_vars_unset="" _eppes_vname="" _eppes_vnames="" IFS IFS0; + + rtl_set_vars _eppes_vars_set BUILD_TYPE "DEFAULT ${_eppes_group_name} PKG_${_eppes_pkg_name}"; + rtl_set_vars _eppes_vars_set INHERIT_FROM "PKG_${_eppes_pkg_name}"; + _eppes_var_prefixes="DEFAULT DEFAULT_${PKG_BUILD_TYPE} ${_eppes_group_name}"; + rtl_toupper \$_eppes_var_prefixes; + rtl_lfilter2 \$_eppes_build_vars_default \$_eppes_vnames BUILD_TYPE; + + for _eppes_vname in ${_eppes_vnames}; do + if [ "${PKG_INHERIT_FROM:+1}" = 1 ]; then + _eppes_lvars="PKG_${PKG_INHERIT_FROM} PKG_${PKG_INHERIT_FROM}_${BUILD_KIND} PKG_${_eppes_pkg_name} PKG_${_eppes_pkg_name}_${BUILD_KIND}"; + rtl_toupper \$_eppes_lvars; + rtl_lconcat2 \$_eppes_lvars \$_eppes_var_prefixes "${_eppes_lvars}"; + rtl_set_vars _eppes_vars_set "${_eppes_vname}" "${_eppes_lvars}"; + else + _eppes_lvars="PKG_${_eppes_pkg_name} PKG_${_eppes_pkg_name}_${BUILD_KIND}"; + rtl_toupper \$_eppes_lvars; + rtl_lconcat2 \$_eppes_lvars \$_eppes_var_prefixes "${_eppes_lvars}"; + rtl_set_vars _eppes_vars_set "${_eppes_vname}" "${_eppes_lvars}"; + fi; + done; + + IFS0="${IFS:- }"; IFS=":"; for _eppes_vname in ${PKG_ENV_VARS_EXTRA:-}; do + export "${_eppes_vname}"; + done; IFS="${IFS0}"; + + for _eppes_vname in AR CC CXX PKG_CONFIG RANLIB; do + if eval [ '"${PKG_'"${_eppes_vname}"':+1}"' = 1 ]\ + && eval [ '"${PKG_'"${_eppes_vname}"'#/}"' = '"${_eppes_cmd_name:=${PKG_'"${_eppes_vname}"'}}"' ]; then + eval PKG_${_eppes_vname}='$(which "${_eppes_cmd_name}")'; + fi; _eppes_cmd_name=""; + done; + + return 0; +}; + +# +# ex_pkg_env() - set package variables for single named package +# @_build_steps_default: list of default build steps +# @_build_vars_default: list of default build variables +# @_group_name: build group name +# @_pkg_name: single package name +# @_restart_at: optional comma-separated list of build steps at which to rebuild or ALL or LAST +# @_workdir: pathname to build-specific temporary directory +# +# Return: zero (0) on success, non-zero (>0) on failure +# +ex_pkg_env() { + local _epe_build_steps_default="${1}" _epe_build_vars_default="${2}" _epe_group_name="${3}" \ + _epe_pkg_name="${4}" _epe_restart_at="${5}" _epe_workdir="${6}" \ + _epe_inherit_from="" _epe_vars_file="" _epe_vname=""; + + if rtl_get_var_unsafe \$_epe_inherit_from -u "PKG_${_epe_pkg_name}_INHERIT_FROM"\ + && [ "${_epe_inherit_from:+1}" = 1 ]; then + rtl_get_var_unsafe \$_epe_vars_file -u "PKG_${_epe_inherit_from}_VARS_FILE"; + else + rtl_get_var_unsafe \$_epe_vars_file -u "PKG_${_epe_pkg_name}_VARS_FILE"; + fi; + if [ "${_epe_vars_file:+1}" != 1 ]; then + _epe_vars_file="vars/${_epe_pkg_name}.vars"; + fi; + + rtl_fileop source_opt "${_epe_vars_file}" "${_epe_group_name}/${_epe_pkg_name}.${_epe_group_name}"; + if ! exp_pkg_env_set "${_epe_build_vars_default}" "${_epe_group_name}" "${_epe_pkg_name}"\ + || ! exp_pkg_env_defaults "${_epe_build_steps_default}" "${_epe_pkg_name}" "${_epe_workdir}"; then + return 1; + fi; + + return 0; +}; + +# +# ex_pkg_state_set() - update build step status for single named package +# @_pkg_name: single package name +# @_build_step: build step set status of +# [@${@}]: optional list of build steps to invalidate status of +# +# Return: zero (0) on success, non-zero (>0) on failure +# +ex_pkg_state_set() { + rtl_state_set "${BUILD_WORKDIR}" "${@}"; +}; + + +ex_pkg_state_set2() { + local _epss2_workdir="${1}"; shift; + rtl_state_set "${_epss2_workdir}" "${@}"; +}; + +# +# ex_pkg_state_test() - test build step status of single named package +# @_pkg_name: single package name +# @_build_step: build step to test status of +# @_restart_at: optional comma-separated list of build steps at which to rebuild or ALL or LAST +# +# Return: zero (0) on success, non-zero (>0) on failure +# +ex_pkg_state_test() { + rtl_state_test "${BUILD_WORKDIR}" "${@}"; +}; + +ex_pkg_state_test2() { + local _epst2_workdir="${1}"; shift; + rtl_state_test "${_epst2_workdir}" "${@}"; +}; + +# vim:filetype=sh diff --git a/subr.ex/ex_pkg_exec.subr b/subr.ex/ex_pkg_exec.subr new file mode 100644 index 00000000..1c4e13dc --- /dev/null +++ b/subr.ex/ex_pkg_exec.subr @@ -0,0 +1,219 @@ +# +# set +o errexit -o noglob -o nounset is assumed. +# + +# +# exp_pkg_exec_pre() - XXX +# @_group_name: build group name +# @_pkg_name: single package name +# @_restart_at: optional comma-separated list of build steps at which to rebuild or ALL or LAST +# @_workdir: pathname to build-specific temporary directory +# +# Return: zero (0) on success, non-zero (>0) on failure +# +exp_pkg_exec_pre() { + local _eppep_group_name="${1}" _eppep_pkg_name="${2}" _eppep_restart_at="${3}" _eppep_workdir="${4}"; + + if [ "${PKG_URL:+1}" != 1 ]\ + && [ "${PKG_URLS_GIT:+1}" != 1 ]\ + && [ "${PKG_VERSION:+1}" != 1 ]\ + && [ "${PKG_INSTALL_FILES:+1}" != 1 ]\ + && [ "${PKG_INSTALL_FILES_V2:+1}" != 1 ]\ + && ! rtl_test_cmd "pkg_${_eppep_pkg_name}_all"; + then + "${_eppep_dispatch_fn}" missing_pkg "${_eppep_group_name}" "${_eppep_pkg_name}"; + return 1; + elif ! ex_pkg_state_test2 \ + "${_eppep_workdir}" \ + "${_eppep_pkg_name}" "start" \ + "${_eppep_restart_at}"; + then + if [ "${PKG_NO_CLEAN_BASE_DIR:-0}" -eq 0 ]\ + && ! rtl_fileop rm "${PKG_BASE_DIR}" "${PKG_BUILD_DIR}" "${PKG_DESTDIR}" "${PKG_DESTDIR_HOST}"\ + || ! rtl_fileop mkdir "${PKG_BASE_DIR}"; + then + return 1; + fi; + + if ! rtl_fileop mkdir "${PKG_BUILD_DIR}" "${PKG_DESTDIR}"\ + || ! ex_pkg_state_set2 "${_eppep_workdir}" "${_eppep_pkg_name}" "start"; + then + return 1; + fi; + elif ! rtl_exists_any "${PKG_BASE_DIR}" "${PKG_BUILD_DIR}" "${PKG_DESTDIR}" "${PKG_DESTDIR_HOST}"\ + && ! rtl_fileop mkdir "${PKG_BASE_DIR}" "${PKG_BUILD_DIR}" "${PKG_DESTDIR}" "${PKG_DESTDIR_HOST}"; + then + return 1 + fi; + + rtl_fileop cd "${PKG_BUILD_DIR}"; + return "${?}"; +}; + +# +# exp_pkg_exec_step() - XXX +# @_group_name: build group name +# @_pkg_name: single package name +# @_restart_at: optional comma-separated list of build steps at which to rebuild or ALL or LAST +# @_step: build step to execute +# +# Return: zero (0) on success, non-zero (>0) on failure +# +exp_pkg_exec_step() { + local _eppes_group_name="${1}" _eppes_pkg_name="${2}" _eppes_restart_at="${3}" \ + _eppes_step="${4}" \ + _eppes_fn_name="" _eppes_pkg_step_fn="" _eppes_rc=0; + + if rtl_test_cmd "pkg_${_eppes_pkg_name}_${_eppes_step}"; then + _eppes_pkg_step_fn="pkg_${_eppes_pkg_name}_${_eppes_step}"; + else + _eppes_pkg_step_fn="pkg_${_eppes_step}"; + fi; + + for _eppes_fn_name in \ + "pkg_${_eppes_pkg_name}_${_eppes_step}_pre" \ + "${_eppes_pkg_step_fn}" \ + "pkg_${_eppes_pkg_name}_${_eppes_step}_post"; + do + if rtl_test_cmd "${_eppes_fn_name}"\ + && ! "${_eppes_fn_name}" \ + "${_eppes_group_name}" "${_eppes_pkg_name}" \ + "${_eppes_restart_at}"; + then + _eppes_rc=1; break; + fi; + done; + + return "${_eppes_rc}"; +}; + +# +# exp_pkg_exec() - XXX +# @_dispatch_fn: top-level dispatch function name +# @_group_name: build group name +# @_pkg_name: single package name +# @_restart_at: optional comma-separated list of build steps at which to rebuild or ALL or LAST +# @_workdir: pathname to build-specific temporary directory +# +# Return: zero (0) on success, non-zero (>0) on failure +# +exp_pkg_exec() { + local _eppe_dispatch_fn="${1}" _eppe_group_name="${2}" _eppe_pkg_name="${3}" \ + _eppe_restart_at="${4}" _eppe_workdir="${5}" \ + _eppe_build_step_last="" _eppe_rc=0 _eppe_step=""; + + if ! exp_pkg_exec_pre \ + "${_eppe_group_name}" "${_eppe_pkg_name}" \ + "${_eppe_restart_at}" "${_eppe_workdir}"\ + || ! "${_eppe_dispatch_fn}" \ + start_pkg_child "${_eppe_group_name}" \ + "${_eppe_pkg_name}"; + then + _eppe_rc=1; + elif rtl_test_cmd "pkg_${_eppe_pkg_name}_all"; then + "pkg_${_eppe_pkg_name}_all" \ + "${_eppe_group_name}" "${_eppe_pkg_name}" \ + "${_eppe_restart_at}"; + _eppe_rc="${?}"; + else + set -- ${PKG_BUILD_STEPS}; + while [ ${#} -gt 0 ]; do + _eppe_step="${1}"; shift; + + if [ "${#_eppe_restart_at}" -gt 0 ]\ + && [ "${_eppe_restart_at}" != "ALL" ]\ + && [ "${_eppe_restart_at}" != "LAST" ]\ + && ! rtl_lmatch \$_eppe_restart_at "${_eppe_step}" ","; then + continue; + fi; + + if [ "${_eppe_step}" = "finish" ]; then + ex_pkg_state_set2 "${_eppe_workdir}" "${_eppe_pkg_name}" finish; break; + elif [ "${PKG_FORCE:-0}" -eq 0 ]\ + && ex_pkg_state_test2 "${_eppe_workdir}" "${_eppe_pkg_name}" "${_eppe_step}" "${_eppe_restart_at}"; + then + continue; + elif ! exp_pkg_exec_step \ + "${_eppe_group_name}" "${_eppe_pkg_name}" \ + "${_eppe_restart_at}" "${_eppe_step}"; + then + _eppe_rc=1; break; + else printf "step %s %s %s\n" "${_eppe_group_name}" "${_eppe_pkg_name}" "${_eppe_step}" >&3; + ex_pkg_state_set2 "${_eppe_workdir}" "${_eppe_pkg_name}" "${_eppe_step}" "${@}"; + fi; + done; + fi; + + return "${_eppe_rc}"; +}; + +# +# ex_pkg_exec() - dispatch single named packages +# @_rdispatch_count: reference to inout variable of dispatcher count +# @_dispatch_count_cur: current dispatcher count +# @_dispatch_count_max: maximum dispatcher count +# @_dispatch_group_cur: current group +# @_dispatch_group_max: maximum group +# @_rdispatch_njobs: reference to inout variable of dispatcher count +# @_rdispatch_wait: reference to inout variable of package names in a wait state +# @_build_steps_default: list of default build steps +# @_build_vars_default: list of default build variables +# @_dispatch_fn: top-level dispatch function name +# @_group_name: build group name +# @_pipe_path: pathname to parent-child process FIFO +# @_pkg_name: single package name +# @_restart_at: optional comma-separated list of build steps at which to rebuild or ALL or LAST +# @_workdir: pathname to build-specific temporary directory +# +# Return: zero (0) on success, non-zero (>0) on failure. +# +ex_pkg_exec() { + local _epdp_rdispatch_count="${1#\$}" _epdp_dispatch_count_cur="${2}" _epdp_dispatch_count_max="${3}" \ + _epdp_dispatch_group_cur="${4}" _epdp_dispatch_group_max="${5}" _epdp_rdispatch_njobs="${6#\$}" \ + _epdp_rdispatch_wait="${7#\$}" _epdp_build_steps_default="${8}" _epdp_build_vars_default="${9}" \ + _epdp_dispatch_fn="${10}" _epdp_group_name="${11}" _epdp_pipe_path="${12}" _epdp_pkg_name="${13}" \ + _epdp_restart_at="${14}" _epdp_workdir="${15}" \ + _epdp_dispatch_count_new=0 _epdp_perc_group=0 _epdp_perc_pkg=0; + + rtl_percentage2 \$_epdp_dispatch_group_cur \$_epdp_dispatch_group_max \$_epdp_perc_group; + rtl_percentage2 \$_epdp_dispatch_count_cur \$_epdp_dispatch_count_max \$_epdp_perc_pkg; + + eval _epdp_dispatch_count_new='$((${'"${_epdp_rdispatch_count}"'}+1))'; + if "${_epdp_dispatch_fn}" \ + start_pkg "${_epdp_group_name}" \ + "${_epdp_pkg_name}" \ + "${_epdp_dispatch_count_new}" \ + "${_epdp_dispatch_count_max}" \ + "${_epdp_perc_group}" "${_epdp_perc_pkg}"; + then + eval : '$(('${_epdp_rdispatch_njobs}'+=1))'; + eval ${_epdp_rdispatch_count}=\"\${_epdp_dispatch_count_new}\"; + rtl_lconcat "${_epdp_rdispatch_wait}" "${_epdp_pkg_name}"; + + (trap "if [ \${?} -eq 0 ]; then \ + printf \"done %s %s %d\n\" \"${_epdp_group_name}\" \"${_epdp_pkg_name}\" \"${_epdp_dispatch_count_new}\" >&3; \ + else \ + printf \"fail %s %s %d\n\" \"${_epdp_group_name}\" \"${_epdp_pkg_name}\" \"${_epdp_dispatch_count_new}\" >&3; \ + pkill -U "${$}"; \ + fi;" EXIT HUP INT TERM USR1 USR2; + set +o errexit -o noglob -o nounset; BUILD_IS_PARENT=0; rtl_log_set_fname ""; rtl_log_set_no_attr 1; + + if ex_pkg_env \ + "${_epdp_build_steps_default}" "${_epdp_build_vars_default}" \ + "${_epdp_group_name}" "${_epdp_pkg_name}" \ + "${_epdp_restart_at}" "${_epdp_workdir}"; + then + exp_pkg_exec \ + "${_epdp_dispatch_fn}" "${_epdp_group_name}" \ + "${_epdp_pkg_name}" "${_epdp_restart_at}" \ + "${_epdp_workdir}"; + else + return 1; + fi;) 1>"${_epdp_workdir}/${_epdp_pkg_name}_stderrout.log" 2>&1 3>"${_epdp_pipe_path}" & + return "${?}"; + else + return 1; + fi; +}; + +# vim:filetype=sh diff --git a/subr.ex/ex_pkg_restart.subr b/subr.ex/ex_pkg_restart.subr new file mode 100644 index 00000000..d93adcbd --- /dev/null +++ b/subr.ex/ex_pkg_restart.subr @@ -0,0 +1,331 @@ +# +# set +o errexit -o noglob -o nounset is assumed. +# + +# +# exp_pkg_check_restart_at() - XXX +# @_rstatus: out reference to status string +# @_rspec_at: in reference to restart build step list +# +# Calling convention: in ref. @_rspec_at +# Returns: zero (0) on success, non-zero (>0) on failure +# +exp_pkg_check_restart_at() { + local _epcra_rstatus="${1#\$}" _epcra_rspec_at="${2#\$}" \ + _epcra_len=0 _epcra_rc=0 _epcra_spec_at=""; + + if ! rtl_llift2 "${_epcra_rspec_at}" \$_epcra_spec_at "," " "\ + || ! rtl_lfilter \$_epcra_spec_at "${DEFAULT_BUILD_STEPS} ALL LAST"\ + || ! rtl_llength \$_epcra_spec_at \$_epcra_len; then + _epcra_rc=1; + elif [ "${_epcra_len}" -gt 0 ]; then + _epcra_rc=1; + rtl_setrstatus "${_epcra_rstatus}" 'unknown build step(s) \`'"${_epcra_spec_at}"''\'''; + fi; + + return "${_epcra_rc}"; +}; + +# +# exp_pkg_expand_restart_at_spec() - XXX +# @_rstatus: out reference to status string +# @_rset: in reference to restart virtual build step set +# @_rspec_at: inout reference to restart build step list +# +# Calling convention: in ref. @_rset, inout ref. @_rspec_at +# Returns: zero (0) on success, non-zero (>0) on failure +# +exp_pkg_expand_restart_at_spec() { + local _eperas_rstatus="${1#\$}" _eperas_rset="${2#\$}" _eperas_rspec_at="${3#\$}" \ + _eperas_at="" _eperas_rc=0 _eperas_spec_at="" _eperas_spec_at_=""; + eval _eperas_spec_at='${'"${_eperas_rspec_at}"'}'; + + case "${_eperas_spec_at}" in + ALL|LAST|"") + ;; + + ^*) + _eperas_spec_at="${_eperas_spec_at#^}"; + if exp_pkg_expand_restart_at_virtual \ + "${_eperas_rstatus}" \ + "${_eperas_spec_at}" \$_eperas_spec_at \ + "${_eperas_rset}" \ + && exp_pkg_check_restart_at "${_eperas_rstatus}" \$_eperas_spec_at; + then + rtl_llift2 \$DEFAULT_BUILD_STEPS "${_eperas_rspec_at}" " " ","; + rtl_llift2 \$_eperas_spec_at \$_eperas_spec_at_ "," " "; + for _eperas_at in ${_eperas_spec_at_}; do + rtl_lfilter "${_eperas_rspec_at}" "${_eperas_at}" ","; + done; rtl_lfilter "${_eperas_rspec_at}" "finish" ","; + else + _eperas_rc=1; + fi; ;; + + \<=*|\<*|\>=*|\>*) + exp_pkg_expand_restart_at_spec_cmp \ + "${_eperas_rset}" "${_eperas_rspec_at}"; _eperas_rc="${?}"; ;; + + *) + if ! exp_pkg_expand_restart_at_virtual \ + "${_eperas_rstatus}" \ + "${_eperas_spec_at}" "${_eperas_rspec_at}" \ + "${_eperas_rset}"; then + _eperas_rc=1; + fi; ;; + esac; + + if [ "${_eperas_rc}" -eq 0 ]; then + if ! exp_pkg_check_restart_at "${_eperas_rstatus}" "${_eperas_rspec_at}"; then + _epprs_rc=1; + elif eval [ '"${'"${_eperas_rspec_at}"':+1}"' != 1 ]; then + _epprs_rc=1; + rtl_setrstatus "${_eperas_rstatus}" 'zero-length build step list'; + fi; + fi; + + return "${_eperas_rc}"; +}; + +# +# exp_pkg_expand_restart_at_spec_cmp() - XXX +# @_rstatus: out reference to status string +# @_rset: in reference to restart virtual build step set +# @_rspec_at: inout reference to restart build step list +# +# Calling convention: in ref. @_rset, inout ref. @_rspec_at +# Returns: zero (0) on success, non-zero (>0) on failure +# +exp_pkg_expand_restart_at_spec_cmp() { + local _eperasc_rstatus="${1#\$}" _eperasc_rset="${2#\$}" _eperasc_rspec_at="${3#\$}" \ + _eperasc_at="" _eperasc_eqfl="" _eperasc_foundfl="" _eperasc_ltfl="" _eperasc_rc=0 \ + _eperasc_spec_at="" _eperasc_spec_at0=""; + eval _eperasc_spec_at0='${'"${_eperasc_rspec_at}"'}'; + + [ "${_eperasc_spec_at0#<}" = "${_eperasc_spec_at0}" ]; _eperasc_ltfl="${?}"; + if [ "${_eperasc_spec_at0#[<>]=}" != "${_eperasc_spec_at0}" ]; then + _eperasc_spec_at0="${_eperasc_spec_at0#[<>]=}"; _eperasc_eqfl=1; + else + _eperasc_spec_at0="${_eperasc_spec_at0#[<>]}"; _eperasc_eqfl=0; + fi; _eperasc_spec_at=""; + + if exp_pkg_expand_restart_at_virtual \ + "${_eperasc_rstatus}" \ + "${_eperasc_spec_at0%%,*}" \$_eperasc_spec_at0 \ + "${_eperasc_rset}" \ + && exp_pkg_check_restart_at "${_eperasc_rstatus}" \$_eperasc_spec_at0; then + if [ \( "${_eperasc_eqfl}" -eq 1 \) -a \( "${_eperasc_ltfl}" -eq 1 \) ]\ + || [ \( "${_eperasc_eqfl}" -eq 0 \) -a \( "${_eperasc_ltfl}" -eq 0 \) ]; then + _eperasc_spec_at0="${_eperasc_spec_at0##*,}"; + elif [ \( "${_eperasc_eqfl}" -eq 1 \) -a \( "${_eperasc_ltfl}" -eq 0 \) ]\ + || [ \( "${_eperasc_eqfl}" -eq 0 \) -a \( "${_eperasc_ltfl}" -eq 1 \) ]; then + _eperasc_spec_at0="${_eperasc_spec_at0%%,*}"; + fi; + + _eperasc_foundfl=0; for _eperasc_at in ${DEFAULT_BUILD_STEPS}; do + if [ "${_eperasc_ltfl}" -eq 1 ]; then + if [ "${_eperasc_at}" = "${_eperasc_spec_at0%%,*}" ]; then + if [ "${_eperasc_eqfl}" -eq 1 ]; then + _eperasc_spec_at="${_eperasc_spec_at:+${_eperasc_spec_at},}${_eperasc_at}"; + fi; break; + fi; + else + if [ "${_eperasc_at}" = "${_eperasc_spec_at0%%,*}" ]; then + _eperasc_foundfl=1; [ "${_eperasc_eqfl}" -eq 0 ] && continue; + fi; [ "${_eperasc_foundfl}" -eq 0 ] && continue; + fi; + _eperasc_spec_at="${_eperasc_spec_at:+${_eperasc_spec_at},}${_eperasc_at}"; + done; + else + _eperasc_rc=1; + fi; + + eval ${_eperasc_rspec_at}='${_eperasc_spec_at}'; + return "${_eperasc_rc}"; +}; + +# +# exp_pkg_expand_restart_at_virtual() - XXX +# @_rstatus: out reference to status string +# @_spec_at: restart build step list +# @_rspec_at_new: out reference to new restart build step list +# @_rset: inout reference to restart virtual build step set +# +# Calling convention: inout ref. @_rspec_recursive +# Returns: zero (0) on success, non-zero (>0) on failure +# +exp_pkg_expand_restart_at_virtual() { + local _epera_rstatus="${1#\$}" _epera_spec_at="${2}" _epera_rspec_at_new="${3#\$}" \ + _epera_rset="${4#\$}" \ + _epera_at="" _epera_IFS0="${IFS:- }" _epera_rc=0 _epera_spec_at_new="" \ + IFS; + + eval ${_epera_rspec_at_new}=; + IFS=","; set -- ${_epera_spec_at}; IFS="${_epera_IFS0}"; + while [ "${#}" -gt 0 ]; do + _epera_at="${1}"; shift; + if [ "${_epera_at#@}" != "${_epera_at}" ]; then + _epera_at="${_epera_at#@}"; + _epera_rc=1; + rtl_setrstatus "${_epera_rstatus}" 'invalid virtual build step \`'"${_epera_at}"''\'''; + if [ "${_epera_at%[!0-9a-zA-Z_]*}" != "${_epera_at}" ]; then + _epera_rc=1; + + elif eval [ '"${'"${_epera_rset}${_epera_at}"':+1}"' = 1 ]; then + eval _epera_at='${'"${_epera_rset}${_epera_at}"'}'; + else + _epera_rc=1; + rtl_setrstatus "${_epera_rstatus}" 'unknown virtual build step \`'"${_epera_at}"''\'''; + fi; + fi; + eval ${_epera_rspec_at_new}='${'"${_epera_rspec_at_new}"':+${'"${_epera_rspec_at_new}"'},}${_epera_at}'; + done; + + return "${_epera_rc}"; +}; + +# +# exp_pkg_expand_restart_recursive() - XXX +# @_rspec: inout reference to restart {specification,package name list} +# @_rrecursive: out reference to recursion flag +# +# Calling convention: inout ref. @_rspec, out ref. @_rrecursive +# Returns: zero (0) on success, non-zero (>0) on failure +# +exp_pkg_expand_restart_recursive() { + local _eperr_rspec="${1#\$}" _eperr_rrecursive="${2#\$}" _eperr_spec=""; + eval _eperr_spec='${'"${_eperr_rspec}"'}'; + + case "${_eperr_spec}" in + + \*\*\*[a-zA-Z]*) + eval ${_eperr_rspec}='${_eperr_spec#\*\*\*}' ${_eperr_rrecursive}=3; ;; + \*\*[a-zA-Z]*) eval ${_eperr_rspec}='${_eperr_spec#\*\*}' ${_eperr_rrecursive}=2; ;; + \*[a-zA-Z]*) eval ${_eperr_rspec}='${_eperr_spec#\*}' ${_eperr_rrecursive}=1; ;; + ALL) eval ${_eperr_rrecursive}=2; ;; + LAST) eval ${_eperr_rrecursive}=0; ;; + + esac; + + return 0; +}; + +# +# exp_pkg_expand_restart_spec() - XXX +# @_rstatus: out reference to status string +# @_rspec: inout reference to restart {specification,package name list} +# @_rspec_at: out reference to restart build step list +# +# Calling convention: inout ref. @_rspec, out ref. @_rspec_at, out ref. @_rrecursive +# Returns: zero (0) on success, non-zero (>0) on failure +# +exp_pkg_expand_restart_spec() { + local _epers_rstatus="${1#\$}" _epers_rspec="${2#\$}" _epers_rspec_at="${3#\$}" \ + _epers_last_pkg="" _epers_rc=0 _epers_spec="" _epers_spec_at="" _epers_spec_at0=""; + eval _epers_spec='${'"${_epers_rspec}"'}'; + + case "${_epers_spec}" in + "") eval ${_epers_rspec_at}=; ;; + ALL) eval ${_epers_rspec_at}=ALL; ;; + LAST|LAST:*) + case "${_epers_spec}" in + LAST) eval ${_epers_rspec_at}=LAST; ;; + LAST:*) eval ${_epers_rspec_at}='${_epers_spec#LAST:}'; ;; + esac; + if [ "${DEFAULT_BUILD_LAST_FAILED_PKG_FNAME:+1}" = 1 ]\ + && [ -e "${DEFAULT_BUILD_LAST_FAILED_PKG_FNAME}" ]; then + if read -r _epers_last_pkg <"${DEFAULT_BUILD_LAST_FAILED_PKG_FNAME}"\ + && rtl_fileop rm "${DEFAULT_BUILD_LAST_FAILED_PKG_FNAME}"; then + eval ${_epers_rspec}='${_epers_last_pkg}'; + else + _epers_rc=1; + rtl_setrstatus "${_epers_rstatus}" 'failed to read or clear status of last failed package \`'"${_epers_last_pkg}"''\'''; + fi; + else + _epers_rc=1; + rtl_setrstatus "${_epers_rstatus}" 'cannot rebuild last failed package'; + fi; ;; + + *:*) eval ${_epers_rspec}='${_epers_spec%:*}' ${_epers_rspec_at}='${_epers_spec#*:}'; ;; + *) eval ${_epers_rspec_at}= ${_epers_rspec_at}=ALL; ;; + esac; + + return "${_epers_rc}"; +}; + +# +# exp_pkg_init_restart_at_virtual() - XXX +# @_rset: out reference to restart virtual build step set +# +# Calling convention: out ref. @_rset +# Returns: zero (0) on success, non-zero (>0) on failure +# +exp_pkg_init_restart_at_virtual() { + local _eperav_rset="${1#\$}" _eperav_step="" _eperav_step_virtual="" _epera_steps=""; + + rtl_lfilter2 \$DEFAULT_BUILD_STEPS \$_epera_steps "finish"; + for _eperav_step in ${_epera_steps}; do + _eperav_step_virtual="${_eperav_step%%_*}"; + if eval [ '"${'"${_eperav_rset}${_eperav_step_virtual}"':+1}"' != 1 ]; then + eval ${_eperav_rset}='"${'"${_eperav_rset}"':+${'"${_eperav_rset}"'},}${_eperav_step_virtual}"'; + fi; + eval ${_eperav_rset}${_eperav_step_virtual}='"${'"${_eperav_rset}${_eperav_step_virtual}"':+${'"${_eperav_rset}${_eperav_step_virtual}"'},}${_eperav_step}"'; + done; + + return 0; +}; + +# +# ex_pkg_process_restart_spec() - XXX +# @_rstatus: out reference to status string +# @_rspec: inout reference to restart {specification,package name list} +# @_rspec_at: out reference to restart build step list +# @_rrecursive: out reference to restart recursion flag +# +# Calling convention: inout ref. @_rspec, out ref. @_rspec_at +# Returns: zero (0) on success, non-zero (>0) on failure +# +ex_pkg_process_restart_spec() { + local _epprs_rstatus="${1#\$}" _epprs_rspec="${2#\$}" _epprs_rspec_at="${3#\$}" \ + _epprs_rrecursive="${4#\$}" \ + _epprs_at="" _epprs_rc=0 _epprs_spec_at_new="" _epprs_step="" _epprs_step1="" \ + _epprs_virtual_set=""; + + if eval [ '"${'"${_epprs_rspec}"':+1}"' = 1 ]; then + if exp_pkg_init_restart_at_virtual \$_epprs_virtual_set \ + && exp_pkg_expand_restart_spec "${_epprs_rstatus}" "${_epprs_rspec}" \$_epprs_spec_at_new \ + && exp_pkg_expand_restart_recursive "${_epprs_rspec}" "${_epprs_rrecursive}" \ + && exp_pkg_expand_restart_at_spec "${_epprs_rstatus}" \$_epprs_virtual_set \$_epprs_spec_at_new; + then + + eval ${_epprs_rspec_at}=; + case "${_epprs_spec_at_new}" in + + ALL|LAST) + eval ${_epprs_rspec_at}='${_epprs_spec_at_new}'; ;; + + *) + for _epprs_at in ${DEFAULT_BUILD_STEPS}; do + if rtl_lmatch \$_epprs_at "${_epprs_spec_at_new}" ","; then + eval ${_epprs_rspec_at}='${'"${_epprs_rspec_at}"':+${'"${_epprs_rspec_at}"'},}${_epprs_at}'; + fi; + done; + if eval [ '"${'"${_epprs_rspec_at}"'##*,}"' != "finish" ]; then + rtl_lfilter2 "${_epprs_rspec_at}" \$_epprs_step "clean,finish" ","; _epprs_step="${_epprs_step##*,}"; + rtl_lfilter2 \$DEFAULT_BUILD_STEPS \$_epprs_step1 "clean finish"; _epprs_step1="${_epprs_step1##* }"; + if [ "${_epprs_step}" = "${_epprs_step1}" ]; then + eval ${_epprs_rspec_at}='${'"${_epprs_rspec_at}"':+${'"${_epprs_rspec_at}"'},}finish'; + fi; + fi; ;; + + esac; + rtl_llift "${_epprs_rspec}" "," " " || _epprs_rc=1; + else + _epprs_rc=1; + fi; + rtl_sunset \$_epprs_virtual_set; + fi; + + return "${_epprs_rc}"; +}; + +# vim:filetype=sh -- cgit v1.2.3