#!/bin/bash # Copyright 1999-2012 Gentoo Foundation # Distributed under the terms of the GNU General Public License v2 # For routines we want to use in ebuild-helpers/ but don't want to # expose to the general ebuild environment. source "${PORTAGE_BIN_PATH}"/isolated-functions.sh || exit 1 # # API functions for doing parallel processing # proc makeopts_jobs { # Copied from eutils.eclass:makeopts_jobs() local jobs=$(echo " ${MAKEOPTS} " | \ sed -r -n 's:.*[[:space:]](-j|--jobs[=[:space:]])[[:space:]]*([0-9]+).*:\2:p') echo ${jobs:-1} } proc __multijob_init { # Setup a pipe for children to write their pids to when they finish. # We have to allocate two fd's because POSIX has undefined behavior # when using one single fd for both read and write. #487056 # However, opening an fd for read or write only will block until the # opposite end is opened as well. Thus we open the first fd for both # read and write to not block ourselve, but use it for reading only. # The second fd really is opened for write only, as Cygwin supports # just one single read fd per FIFO. #583962 local pipe=$(mktemp -t multijob.XXXXXX) rm -f ${pipe} mkfifo -m 600 ${pipe} __redirect_alloc_fd mj_read_fd ${pipe} __redirect_alloc_fd mj_write_fd ${pipe} '>' rm -f ${pipe} # See how many children we can fork based on the user's settings. setvar mj_max_jobs = $(makeopts_jobs "$@") setvar mj_num_jobs = '0' } proc __multijob_child_init { trap 'echo ${BASHPID:-$(__bashpid)} $? >&'${mj_write_fd} EXIT trap 'exit 1' INT TERM } proc __multijob_finish_one { local pid ret read -r -u ${mj_read_fd} pid ret : $(( --mj_num_jobs )) return ${ret} } proc __multijob_finish { local ret=0 { __multijob_finish_one : $(( ret |= $? )) } # Let bash clean up its internal child tracking state. wait return ${ret} } proc __multijob_post_fork { : $(( ++mj_num_jobs )) if [[ ${mj_num_jobs} -ge ${mj_max_jobs} ]] { __multijob_finish_one } return $? } # @FUNCTION: __redirect_alloc_fd # @USAGE: [redirection] # @DESCRIPTION: # Find a free fd and redirect the specified file via it. Store the new # fd in the specified variable. Useful for the cases where we don't care # about the exact fd #. proc __redirect_alloc_fd { local var=$1 file=$2 redir=${3:-"<>"} if [[ $(( (BASH_VERSINFO[0] << 8) + BASH_VERSINFO[1] )) -ge $(( (4 << 8) + 1 )) ]] { # Newer bash provides this functionality. eval "exec {${var}}${redir}'${file}'" } else { # Need to provide the functionality ourselves. local fd=10 local fddir=/dev/fd # Prefer /proc/self/fd if available (/dev/fd # doesn't work on solaris, see bug #474536). [[ -d /proc/self/fd ]] && setvar fddir = '/proc/self/fd' while : { # Make sure the fd isn't open. It could be a char device, # or a symlink (possibly broken) to something else. if [[ ! -e ${fddir}/${fd} ]] && [[ ! -L ${fddir}/${fd} ]] { eval "exec ${fd}${redir}'${file}'" && break } [[ ${fd} -gt 1024 ]] && die 'could not locate a free temp fd !?' : $(( ++fd )) } : $(( ${var} = fd )) } }