#-*- mode: Fundamental; tab-width: 4; -*- # ex:ts=4 # # $FreeBSD$ # # Please view me with 4 column tabs! # # _parv_WANT_PARALLEL_BUILD # _parv_ATTEMPTS_TO_LOCK # # .if !defined(_POSTMKINCLUDED) && !defined(Parallel_Pre_Include) Parallel_Pre_Include= bsd.parallel.mk _dparv_= _parv_= ##################################################### # Debugging specific tools and variable declarations _dparv_START_OUTPUT_MESSAGE= =================_PAR_PORTS_SPECIFIC_OUTPUT_============== _dparv_END_OUTPUT_MESSAGE= ==============_END_OF_PAR_PORTS_SPECIFIC_OUTPUT_============== _dparv_START_OUTPUT= ${ECHO_CMD} ${_dparv_START_OUTPUT_MESSAGE} _dparv_END_OUTPUT= ${ECHO_CMD} ${_dparv_END_OUTPUT_MESSAGE} # Use it when you need a barrier _dparv_DEBUGGING_BREAKPOINT= ${ECHO_CMD} Debugging breakpoint here...; \ ${ECHO_CMD} Press any key when you are ready to continue; \ read non_existed_var seal: @echo " <<<<<< ===== WE ARE HERE ===== >>>>>>" breakpoint: @${_dparv_DEBUGGING_BREAKPOINT} # Delay for feedback message if the directory is locked. # Just not to annoy a user with feedback message on each attempt to lock a directory. _parv_ON_LOCK_FEEDBACK_TIMEOUT?= 2 # End of Debugging specific tools and variable declarations section ##################################################### ##################################################### _parv_DEFAULT_TARGETS= all check-sanity fetch checksum extract patch configure build install _parv_IS_DEFAULT_TARGET= 0 # e.g. make -D_parv_WANT_PARALLEL_BUILD # All target will be evaluated. It is in default sequence # .if !${.TARGETS} _parv_IS_DEFAULT_TARGET= 1 .else .for _called_target in ${.TARGETS} _tmp_called_target= ${_called_target} . for _def_target in ${_parv_DEFAULT_TARGETS} . if ${_tmp_called_target} == ${_def_target} _parv_IS_DEFAULT_TARGET= 1 . endif . endfor .endfor .endif #!{.TARGETS} _parv_DEFAULT_PAR_BUILDS_NUM!= sysctl -a | grep hw.n | awk '{print $$2}' ############### THIS ENTRY IS FOR DEBUGGING PURPOSE ############### ############### Custom number of parallel builds for debugging _parv_DEFAULT_PAR_BUILDS_NUM= 3 ############### END OF PAR_PORTS SPECIFIC COMMENT LINE ############### .if !defined(_parv_PARALLEL_BUILDS_NUMBER) || ${_parv_PARALLEL_BUILDS_NUMBER} > ${_parv_DEFAULT_PAR_BUILDS_NUM} _parv_PARALLEL_BUILDS_NUMBER= ${_parv_DEFAULT_PAR_BUILDS_NUM} .endif ##################################################### ##################################################### # Commands _parv_KILL= /bin/kill _parv_KILL_SIGNAL= USR1 _parv_PKILL= /bin/pkill _parv_PKILL_FLAGS= -P _parv_UMASK= 777 DO_NADA?= ${TRUE} # End of Commands section ##################################################### ##################################################### # Locking variables and tools #PKG_DBDIR?= /var/db/pkg LOCK_DIR?= /var/db/portlocks _parv_PKG_DBDIR_LOCK_FILE= .lock _parv_LOCK_DIR_LOCK_FILE= ${PKGNAME} _parv_WAIT_FOR_LOCK_TIME= 5 _parv_WAIT_FOR_UNLOCK_TIME= 15 # Delay in seconds between attempts to lock a directory in lock loops _parv_LOCK_ATTEMPT_TIMEOUT=2 # exit status of lock script in case the directory is locked _parv_ON_LOCK_EXIT_STATUS= 2 _parv_LOCKF_EX_TEMPFAIL= 75 _parv_MAKE_LOCK_EXIT_STATUS= 158 # Senquence of commands to lock a directory using ${_parv_${_lock_dir}_LOCK_FILE}. # During evaluation of the following commands lockf(1) is holding lock on ${_parv_${_lock_dir}_LOCK_FILE} file. # Hence NO other process is able to evaluate any commands using lockf(1) # locking on the same ${_parv_${_lock_dir}_LOCK_FILE} file. # # Stalled locks cheking enabled. # # If the directory is locked this script returns ${_parv_ON_LOCK_EXIT_STATUS}. # # ${${_lock_dir}} == ${PKG_DBDIR} OR ${LOCK_DIR} # .for _lock_dir in PKG_DBDIR LOCK_DIR # _parv_PKG_DBDIR_LOCK_SEQ # _parv_LOCK_DIR_LOCK_SEQ # #_parv_${_lock_dir}_LOCK_FILE:= ${_parv_${_lock_dir}_LOCK_FILE} _parv_${_lock_dir}_LOCK_SEQ= \ ${CHMOD} ${_parv_UMASK} ${${_lock_dir}}/${_parv_${_lock_dir}_LOCK_FILE}; \ pid=$$(${CAT} ${${_lock_dir}}/${_parv_${_lock_dir}_LOCK_FILE}); \ if [ $${pid} ]; then \ ps -p $${pid} > /dev/null && status=$$? || status=$$?; \ if [ $${status} -eq 0 ]; then \ if [ ${_lock_dir} = "LOCK_DIR" ]; then \ echo heEEEEEEraaaAAAAAAAaaaa!; \ cur_pid=${.MAKE.PID}; \ while true; do \ ppid=$$( ps -o ppid -p $${cur_pid} | ${AWK} "NR==2" ); \ if [ $${ppid} -eq $${pid} ]; then \ ${_dparv_START_OUTPUT}; \ ${ECHO_CMD} "${${_lock_dir}}/${_parv_${_lock_dir}_LOCK_FILE} is locked by parent make process!"; \ ${ECHO_CMD} "We are allowed to work here"; \ ${_dparv_END_OUTPUT}; \ break; \ elif [ $${ppid} -eq 0 ]; then \ exit ${_parv_ON_LOCK_EXIT_STATUS}; \ else \ cur_pid=$${ppid}; \ fi; \ done; \ else \ exit ${_parv_ON_LOCK_EXIT_STATUS}; \ fi; \ else \ ${_dparv_START_OUTPUT}; \ ${ECHO_CMD} "Dir: ${${_lock_dir}} Stalled lock Detected!"; \ ${ECHO_CMD} "Deleting stalled lock. PID=$${pid}"; \ ${ECHO_CMD} "Locking: ${${_lock_dir}}/${_parv_${_lock_dir}_LOCK_FILE}"; \ ${_dparv_END_OUTPUT}; \ ${ECHO_CMD} ${.MAKE.PID} > ${${_lock_dir}}/${_parv_${_lock_dir}_LOCK_FILE}; \ fi; \ else \ ${_dparv_START_OUTPUT}; \ ${ECHO_CMD} "Locking: ${${_lock_dir}}/${_parv_${_lock_dir}_LOCK_FILE}"; \ ${_dparv_END_OUTPUT}; \ ${ECHO_CMD} ${.MAKE.PID} > ${${_lock_dir}}/${_parv_${_lock_dir}_LOCK_FILE}; \ fi ##################################################### # _parv_PKG_DBDIR_DO_LOCK # _parv_LOCK_DIR_DO_LOCK # This scripts handles exit status of lockf(1) call. # It substitutes exit status 75 of lockf(1) for ${_parv_ON_LOCK_EXIT_STATUS} and pushes it. # _parv_${_lock_dir}_DO_LOCK= \ lockf -k -t ${_parv_WAIT_FOR_LOCK_TIME} ${${_lock_dir}}/${_parv_${_lock_dir}_LOCK_FILE} ${SH} -c '${_parv_${_lock_dir}_LOCK_SEQ}' || { \ status=$$?; \ if [ $${status} -eq ${_parv_LOCKF_EX_TEMPFAIL} ] || \ [ $${status} -eq ${_parv_ON_LOCK_EXIT_STATUS} ]; then \ exit ${_parv_ON_LOCK_EXIT_STATUS}; \ else \ ${_dparv_START_OUTPUT}; \ ${ECHO_CMD} "Unhandled EXIT STATUS = $${status}. Terminating"; \ ${_dparv_END_OUTPUT}; \ exit 1; \ fi; \ } ##################################################### # Loops to lock directory # _parv_PKG_DBDIR_LOCK_LOOP # _parv_LOCK_DIR_LOCK_LOOP # $${attempts} - Number of attempts to lock a directory. Exetranl variable. # Default value = 1, if this var is not set. # Set this variable to -1 for infinity loop. # e.g. ( attempts=10; ${_parv_LOCK_DIR_LOCK_LOOP} ) && ..... || .... # _parv_${_lock_dir}_LOCK_LOOP= \ enable_feedback=${_parv_ON_LOCK_FEEDBACK_TIMEOUT}; \ if [ ! $${attempts} ]; then attempts=1; fi; \ while [ $${attempts} -ne 0 ]; do \ attempts=$$(( $${attempts} - 1 )); \ ( ${_parv_${_lock_dir}_DO_LOCK} ) && status=$$? || status=$$?; \ if [ $${status} -eq 0 ]; then \ exit 0; \ elif [ $${status} -eq ${_parv_ON_LOCK_EXIT_STATUS} ]; then \ if [ $$(( $${enable_feedback} % ${_parv_ON_LOCK_FEEDBACK_TIMEOUT} )) -eq 0 ]; then \ ${_dparv_START_OUTPUT}; \ ${ECHO_CMD} "Unable to lock ${${_lock_dir}}/${_parv_${_lock_dir}_LOCK_FILE}"; \ ${ECHO_CMD} "Dir: ${${_lock_dir}} is already locked by another working process ..."; \ ${ECHO_CMD} "Waiting for unlock ........................................................."; \ ${_dparv_END_OUTPUT}; \ enable_feedback=0; \ fi; \ enable_feedback=$$(( $${enable_feedback} + 1 )); \ sleep ${_parv_LOCK_ATTEMPT_TIMEOUT}; \ continue; \ else \ exit 1; \ fi; \ done; \ exit ${_parv_ON_LOCK_EXIT_STATUS} ##################################################### # _parv_PKG_DBDIR_DO_UNLOCK # _parv_LOCK_DIR_DO_UNLOCK # _parv_${_lock_dir}_DO_UNLOCK= \ lockf -k -t ${_parv_WAIT_FOR_UNLOCK_TIME} ${${_lock_dir}}/${_parv_${_lock_dir}_LOCK_FILE} ${SH} -c '{ \ pid=$$(${CAT} ${${_lock_dir}}/${_parv_${_lock_dir}_LOCK_FILE}); \ if [ ${.MAKE.PID} -eq $${pid} ]; then \ ${RM} -rf ${${_lock_dir}}/${_parv_${_lock_dir}_LOCK_FILE}; \ ${_dparv_START_OUTPUT}; \ ${ECHO_CMD} Dir: ${${_lock_dir}}/${_parv_${_lock_dir}_LOCK_FILE} is unlocked; \ ${_dparv_END_OUTPUT}; \ else \ ${_dparv_START_OUTPUT}; \ ${ECHO_CMD} Dir: ${${_lock_dir}}/${_parv_${_lock_dir}_LOCK_FILE} was locked by another process; \ ${ECHO_CMD} Leave lock file; \ ${_dparv_END_OUTPUT}; \ fi; \ }' .endfor # _lock_dir in PKG_DBDIR .CURDIR .undef _parv_${_lock_dir}_LOCK_FILE ##################################################### # _parv_CHECK_SEQ # _parv_CHECK_LOCK # The former variables Implement check for lock utility # $${pkg_name} - port to check. External variable for script. Supports shell RE. Assign this variable # appropriate value before executing this script e.g. ( pkg_name=apache-[1234]; ${_parv_CHECK_LOCK} ) || ... # Script exits with status ${_parv_ON_LOCK_EXIT_STATUS} if $${pkg_name} is locked # _parv_CHECK_SEQ= \ ${CHMOD} ${_parv_UMASK} ${LOCK_DIR}/$${pkg_name}; \ pid=\$$(${CAT} ${LOCK_DIR}/$${pkg_name}); \ if [ \$${pid} ]; then \ ps -p \$${pid} > /dev/null && status=\$$? || status=\$$?; \ if [ \$${status} -eq 0 ]; then \ ${_dparv_START_OUTPUT}; \ ${ECHO_CMD} Unable to lock ${LOCK_DIR}/$${pkg_name}; \ ${ECHO_CMD} Dir: ${LOCK_DIR} is already locked by another working process ...; \ ${_dparv_START_OUTPUT}; \ exit ${_parv_ON_LOCK_EXIT_STATUS}; \ else \ ${_dparv_START_OUTPUT}; \ ${ECHO_CMD} Dir: ${LOCK_DIR}/$${pkg_name} Stalled lock Detected!; \ ${ECHO_CMD} Deleting stalled lock. PID=\$${pid}; \ ${_dparv_END_OUTPUT}; \ fi; \ fi; \ ${RM} -rf ${LOCK_DIR}/$${pkg_name} _parv_CHECK_LOCK= \ lockf -k -t ${_parv_WAIT_FOR_LOCK_TIME} ${LOCK_DIR}/$${pkg_name} ${SH} -c "${_parv_CHECK_SEQ}" || { \ status=$$?; \ if [ $${status} -eq ${_parv_LOCKF_EX_TEMPFAIL} ] || \ [ $${status} -eq ${_parv_ON_LOCK_EXIT_STATUS} ]; then \ exit ${_parv_ON_LOCK_EXIT_STATUS}; \ else \ ${_dparv_START_OUTPUT}; \ ${ECHO_CMD} "Unhandled EXIT STATUS = $${status}. Terminating"; \ ${_dparv_END_OUTPUT}; \ exit 1; \ fi; \ } _parv_ON_LOCK_EXIT_SEQ= \ status=$$?; \ if [ $${status} -eq ${_parv_ON_LOCK_EXIT_STATUS} ]; then \ ${_parv_KILL} -${_parv_KILL_SIGNAL} ${.MAKE.PID} && \ ${_parv_PKILL} ${_parv_PKILL_FLAGS} $$$$; \ else \ exit $${status}; \ fi # End of Locking variables and tools section ##################################################### check-lock: @( pkg_name=${PKGNAME}; ${_parv_CHECK_LOCK} ) || { ${_parv_ON_LOCK_EXIT_SEQ}; } check-active-build-conflicts: .if ( defined(CONFLICTS) || defined(CONFLICTS_BUILD) || defined(CONFLICTS_INSTALL)) && !defined(DISABLE_CONFLICTS) @conflicts_with=; \ active_builds=$$(cd ${LOCK_DIR}; ${LS} -dA ${CONFLICTS} ${CONFLICTS_BUILD} ${CONFLICTS_INSTALL} 2> /dev/null || ); \ for entry in $${active_builds}; do \ ( pkg_name=$${entry}; ${_parv_CHECK_LOCK} ) || { \ status=$$?; \ if [ $${status} -eq ${_parv_ON_LOCK_EXIT_STATUS} ]; then \ conflicts_with="$${conflicts_with} $${entry}"; \ else \ exit 1; \ fi; \ }; \ done; \ if [ -n "$${conflicts_with}" ]; then \ ${_dparv_START_OUTPUT}; \ ${ECHO_MSG}; \ ${ECHO_MSG} "===> ${PKGNAME} conflicts with currently installing package(s): "; \ for entry in $${conflicts_with}; do \ ${ECHO_MSG} " $${entry}"; \ done; \ ${ECHO_MSG}; \ ${ECHO_MSG} " Please remove them first with pkg_delete(1)."; \ ${_dparv_END_OUTPUT}; \ exit 1; \ fi .endif lock-pkg-dbdir: @attempts=-1; ${_parv_PKG_DBDIR_LOCK_LOOP} unlock-pkg-dbdir: @${_parv_PKG_DBDIR_DO_UNLOCK} do-lock: @${DO_NADA} do-unlock: @${DO_NADA} .endif # !defined(_POSTMKINCLUDED) && !defined(Parallel_Pre_Include)