summaryrefslogtreecommitdiffhomepage
path: root/subr.ex
diff options
context:
space:
mode:
authorLucía Andrea Illanes Albornoz <lucia@luciaillanes.de>2023-02-17 19:29:28 +0100
committerLucía Andrea Illanes Albornoz <lucia@luciaillanes.de>2023-02-17 19:29:28 +0100
commite9fa0774ed2e7e030a68f5b0ae51fe6dd69fe492 (patch)
tree37e46c2578bd8f4f435073db01abc514976da8a8 /subr.ex
parent56495632fc8bf612766a9c431e37ff27a903e8c6 (diff)
downloadmidipix_build-e9fa0774ed2e7e030a68f5b0ae51fe6dd69fe492.tar.bz2
midipix_build-e9fa0774ed2e7e030a68f5b0ae51fe6dd69fe492.tar.xz
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
Diffstat (limited to 'subr.ex')
-rw-r--r--subr.ex/ex_init.subr397
-rw-r--r--subr.ex/ex_pkg.subr324
-rw-r--r--subr.ex/ex_pkg_dispatch.subr378
-rw-r--r--subr.ex/ex_pkg_env.subr178
-rw-r--r--subr.ex/ex_pkg_exec.subr219
-rw-r--r--subr.ex/ex_pkg_restart.subr331
6 files changed, 1827 insertions, 0 deletions
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