70 usage() { |
70 usage() { |
71 echo "usage: $0 [-q|--quiet] [-v|--verbose] [-s|--sequential] [--] <command> [commands...]" > ${status_output} |
71 echo "usage: $0 [-q|--quiet] [-v|--verbose] [-s|--sequential] [--] <command> [commands...]" > ${status_output} |
72 exit 1 |
72 exit 1 |
73 } |
73 } |
74 |
74 |
75 |
|
76 if [ "x" = "x$command" ] ; then |
75 if [ "x" = "x$command" ] ; then |
77 echo "ERROR: No command to hg supplied!" |
76 echo "ERROR: No command to hg supplied!" |
78 usage |
77 usage |
|
78 fi |
|
79 |
|
80 # Check if we can use fifos for monitoring sub-process completion. |
|
81 on_windows=`uname -s | egrep -ic -e 'cygwin|msys'` |
|
82 if [ ${on_windows} = "1" ]; then |
|
83 # cygwin has (2014-04-18) broken (single writer only) FIFOs |
|
84 # msys has (2014-04-18) no FIFOs. |
|
85 have_fifos="false" |
|
86 else |
|
87 have_fifos="true" |
79 fi |
88 fi |
80 |
89 |
81 # Clean out the temporary directory that stores the pid files. |
90 # Clean out the temporary directory that stores the pid files. |
82 tmp=/tmp/forest.$$ |
91 tmp=/tmp/forest.$$ |
83 rm -f -r ${tmp} |
92 rm -f -r ${tmp} |
208 (PYTHONUNBUFFERED=true hg${global_opts} serve -A ${status_output} -E ${status_output} --pid-file ${tmp}/serve.pid --web-conf ${tmp}/serve.web-conf; echo "$?" > ${tmp}/serve.pid.rc ) 2>&1 & |
217 (PYTHONUNBUFFERED=true hg${global_opts} serve -A ${status_output} -E ${status_output} --pid-file ${tmp}/serve.pid --web-conf ${tmp}/serve.web-conf; echo "$?" > ${tmp}/serve.pid.rc ) 2>&1 & |
209 ) 2>&1 | sed -e "s@^@serve: @" > ${status_output} |
218 ) 2>&1 | sed -e "s@^@serve: @" > ${status_output} |
210 ) & |
219 ) & |
211 else |
220 else |
212 # Run the supplied command on all repos in parallel. |
221 # Run the supplied command on all repos in parallel. |
|
222 |
|
223 # n is the number of subprocess started or which might still be running. |
213 n=0 |
224 n=0 |
|
225 if [ $have_fifos = "true" ]; then |
|
226 # if we have fifos use them to detect command completion. |
|
227 mkfifo ${tmp}/fifo |
|
228 exec 3<>${tmp}/fifo |
|
229 if [ "${sflag}" = "true" ] ; then |
|
230 # force sequential |
|
231 at_a_time=1 |
|
232 fi |
|
233 fi |
|
234 |
214 for i in ${repos} ${repos_extra} ; do |
235 for i in ${repos} ${repos_extra} ; do |
215 n=`expr ${n} '+' 1` |
236 n=`expr ${n} '+' 1` |
216 repopidfile=`echo ${i} | sed -e 's@./@@' -e 's@/@_@g'` |
237 repopidfile=`echo ${i} | sed -e 's@./@@' -e 's@/@_@g'` |
217 reponame=`echo ${i} | sed -e :a -e 's/^.\{1,20\}$/ &/;ta'` |
238 reponame=`echo ${i} | sed -e :a -e 's/^.\{1,20\}$/ &/;ta'` |
218 pull_base="${pull_default}" |
239 pull_base="${pull_default}" |
219 for j in $repos_extra ; do |
240 for j in $repos_extra ; do |
220 if [ "$i" = "$j" ] ; then |
241 if [ "$i" = "$j" ] ; then |
221 pull_base="${pull_extra}" |
242 pull_base="${pull_extra}" |
222 fi |
243 fi |
223 done |
244 done |
|
245 pull_base="`echo ${pull_base} | sed -e 's@[/]*$@@'`" |
224 ( |
246 ( |
225 ( |
247 ( |
226 if [ "${command}" = "clone" -o "${command}" = "fclone" -o "${command}" = "tclone" ] ; then |
248 if [ "${command}" = "clone" -o "${command}" = "fclone" -o "${command}" = "tclone" ] ; then |
227 pull_newrepo="`echo ${pull_base}/${i} | sed -e 's@\([^:]/\)//*@\1@g'`" |
249 pull_newrepo="${pull_base}/${i}" |
228 path="`dirname ${i}`" |
250 path="`dirname ${i}`" |
229 if [ "${path}" != "." ] ; then |
251 if [ "${path}" != "." ] ; then |
230 times=0 |
252 times=0 |
231 while [ ! -d "${path}" ] ## nested repo, ensure containing dir exists |
253 while [ ! -d "${path}" ] ## nested repo, ensure containing dir exists |
232 do |
254 do |
235 echo "${path} still not created, waiting..." > ${status_output} |
257 echo "${path} still not created, waiting..." > ${status_output} |
236 fi |
258 fi |
237 sleep 5 |
259 sleep 5 |
238 done |
260 done |
239 fi |
261 fi |
240 echo "hg clone ${pull_newrepo} ${i}" > ${status_output} |
262 echo "hg${global_opts} clone ${pull_newrepo} ${i}" > ${status_output} |
241 (PYTHONUNBUFFERED=true hg${global_opts} clone ${pull_newrepo} ${i}; echo "$?" > ${tmp}/${repopidfile}.pid.rc ) 2>&1 & |
263 (PYTHONUNBUFFERED=true hg${global_opts} clone ${pull_newrepo} ${i}; echo "$?" > ${tmp}/${repopidfile}.pid.rc ) 2>&1 & |
242 else |
264 else |
243 echo "cd ${i} && hg${global_opts} ${command} ${command_args}" > ${status_output} |
265 echo "cd ${i} && hg${global_opts} ${command} ${command_args}" > ${status_output} |
244 cd ${i} && (PYTHONUNBUFFERED=true hg${global_opts} ${command} ${command_args}; echo "$?" > ${tmp}/${repopidfile}.pid.rc ) 2>&1 & |
266 cd ${i} && (PYTHONUNBUFFERED=true hg${global_opts} ${command} ${command_args}; echo "$?" > ${tmp}/${repopidfile}.pid.rc ) 2>&1 & |
245 fi |
267 fi |
246 |
268 |
247 echo $! > ${tmp}/${repopidfile}.pid |
269 echo $! > ${tmp}/${repopidfile}.pid |
248 ) 2>&1 | sed -e "s@^@${reponame}: @" > ${status_output} |
270 ) 2>&1 | sed -e "s@^@${reponame}: @" > ${status_output} |
|
271 if [ $have_fifos = "true" ]; then |
|
272 echo "${reponame}" >&3 |
|
273 fi |
249 ) & |
274 ) & |
250 |
275 |
251 if [ `expr ${n} '%' ${at_a_time}` -eq 0 -a "${sflag}" = "false" ] ; then |
276 if [ $have_fifos = "true" ]; then |
252 sleep 2 |
277 # check on count of running subprocesses and possibly wait for completion |
253 echo "Waiting 5 secs before spawning next background command." > ${status_output} |
278 if [ ${at_a_time} -lt ${n} ] ; then |
254 sleep 3 |
279 # read will block until there are completed subprocesses |
255 fi |
280 while read repo_done; do |
256 |
281 n=`expr ${n} '-' 1` |
257 if [ "${sflag}" = "true" ] ; then |
282 if [ ${n} -lt ${at_a_time} ] ; then |
|
283 # we should start more subprocesses |
|
284 break; |
|
285 fi |
|
286 done <&3 |
|
287 fi |
|
288 else |
|
289 if [ "${sflag}" = "false" ] ; then |
|
290 # Compare completions to starts |
|
291 completed="`(ls -1 ${tmp}/*.pid.rc 2> /dev/null | wc -l) || echo 0`" |
|
292 while [ ${at_a_time} -lt `expr ${n} '-' ${completed}` ] ; do |
|
293 # sleep a short time to give time for something to complete |
|
294 sleep 1 |
|
295 completed="`(ls -1 ${tmp}/*.pid.rc 2> /dev/null | wc -l) || echo 0`" |
|
296 done |
|
297 else |
|
298 # complete this task before starting another. |
258 wait |
299 wait |
259 fi |
300 fi |
260 done |
301 fi |
261 fi |
302 done |
262 |
303 fi |
263 # Wait for all hg commands to complete |
304 |
|
305 # Wait for all subprocesses to complete |
264 wait |
306 wait |
265 |
307 |
266 # Terminate with exit 0 only if all subprocesses were successful |
308 # Terminate with exit 0 only if all subprocesses were successful |
267 ec=0 |
309 ec=0 |
268 if [ -d ${tmp} ]; then |
310 if [ -d ${tmp} ]; then |
269 for rc in ${tmp}/*.pid.rc ; do |
311 for rc in ${tmp}/*.pid.rc ; do |
270 exit_code=`cat ${rc} | tr -d ' \n\r'` |
312 exit_code=`cat ${rc} | tr -d ' \n\r'` |
271 if [ "${exit_code}" != "0" ] ; then |
313 if [ "${exit_code}" != "0" ] ; then |
272 repo="`echo ${rc} | sed -e s@^${tmp}@@ -e 's@/*\([^/]*\)\.pid\.rc$@\1@' -e 's@_@/@g'`" |
314 repo="`echo ${rc} | sed -e s@^${tmp}@@ -e 's@/*\([^/]*\)\.pid\.rc$@\1@' -e 's@_@/@g'`" |
273 echo "WARNING: ${repo} exited abnormally." > ${status_output} |
315 echo "WARNING: ${repo} exited abnormally ($exit_code)" > ${status_output} |
274 ec=1 |
316 ec=1 |
275 fi |
317 fi |
276 done |
318 done |
277 fi |
319 fi |
278 exit ${ec} |
320 exit ${ec} |