1# vim:set syntax=sh noet ts=8 sw=8: 2# $Id$ 3# 4# portshaker.subr 5# functions used by portshaker(8) 6# 7 8if [ -z "${_portshaker_subr_loaded}" ]; then 9 10_portshaker_subr_loaded="YES" 11verbose="0" 12 13if [ "${_portshaker_load_config}" != "no" ]; then 14 config_dir="${portshaker_config_dir:=@@ETCDIR@@}" 15 . ${config_dir}/portshaker.conf 16fi 17 18# User tunable variables 19 20#CP_FLAGS="-v" 21#CSUP_FLAGS="" 22CVS="${CVS:=cvs}" 23CVS_FLAGS="${CVS_FLAGS:=-q}" # Quiet 24DIFF_FLAGS="${DIFF_FLAGS:=-uN}" # Unified diff, New files 25GIT="${GIT:=git}" 26HG="${HG:=hg}" 27MAKE="${MAKE:=make}" 28PAGER="${PAGER:=less}" 29PATCH_FLAGS="${PATCH_FLAGS:=-s}" # Silent 30#PORTSNAP_FLAGS="" 31#RM_FLAGS="" 32RSYNC=${RSYNC:=rsync} 33RSYNC_FLAGS="" 34SVN="${SVN:=svn}" 35#SVN_FLAGS="" 36SVNADMIN="${SVNADMIN:=svnadmin}" 37#SVNADMIN_FLAGS="" 38VCS_ID_KEYWORDS="${VCS_ID_KEYWORDS:=FreeBSD Id MCom}" 39 40# Tinderbox related variables 41PB="${PB:=/usr/local/tinderbox}" 42TC="${TC:=${PB}/tc}" 43 44# Use pkg(8) if the old pkg_* tools are not found or if WITH_PKGNG defined in 45# make.conf 46if [ -z `/usr/bin/whereis -q -b pkg_version` ]; then 47 PKG_VERSION="pkg version" 48elif [ -f "/etc/make.conf" ]; then 49 case `make -f "/etc/make.conf" -V WITH_PKGNG` in 50 [Yy][Ee][Ss]|[Tt][Rr][Uu][Ee]|[Oo][Nn]|1) 51 PKG_VERSION="pkg version" 52 ;; 53 esac 54fi 55PKG_VERSION="${PKG_VERSION:=pkg_version}" 56 57# Convenient variables 58DIFF_IGNORE_FLAGS_MK="--ignore-matching-lines='\$\(FreeBSD\|Id\|MCom\)\$'" 59DIFF_IGNORE_FLAGS_PORT="--ignore-space-change --ignore-blank-lines --ignore-matching-lines='^\(BROKEN\|IGNORE\|PORTSCOUT\)='" 60RSYNC_ARGS="--archive --delete" 61RSYNC_EXCLUDE="--exclude packages --exclude distfiles" 62 63_keywords="update clone_to copy_to merge_to" 64_portshaker_arg="" 65 66_pretend=0 67 68_use_zfs=0 69use_zfs=${use_zfs:=0} 70 71case $use_zfs in 72[Yy][Ee][Ss]|[Tt][Rr][Uu][Ee]|[Oo][Nn]|1) 73 _use_zfs=1 74 ;; 75esac 76 77_fail_on_conflict=0 78fail_on_conflict=${fail_on_conflict:=0} 79 80case $fail_on_conflict in 81[Yy][Ee][Ss]|[Tt][Rr][Uu][Ee]|[Oo][Nn]|1) 82 _fail_on_conflict=1 83 ;; 84esac 85 86source_zfs_compression=${source_zfs_compression:=lz4} 87target_zfs_compression=${target_zfs_compression:=off} 88 89# 90# functions 91# --------- 92 93# err 94# Display error message to stderr and exit. 95# $1 - Exit code. 96# 97err() 98{ 99 exitval=$1 100 shift 101 102 printf "\\033[31;3m[Error $(date +%T)]\\033[0m $*\\n" >&2 103 exit ${exitval} 104} 105 106# warn 107# Display warning message to stderr. 108# 109warn() 110{ 111 printf "\\033[33;3m[Warn $(date +%T)]\\033[0m $*\\n" >&2 112} 113 114# info 115# Display information message to stderr. 116# 117info() 118{ 119 case ${portshaker_info} in 120 [Yy][Ee][Ss]|[Tt][Rr][Uu][Ee]|[Oo][Nn]|1) 121 printf "\\033[32;3m[Info $(date +%T)]\\033[0m $*\\n" >&2 122 esac 123} 124 125# debug 126# Display debug message to stderr. 127# 128debug() 129{ 130 case ${portshaker_debug} in 131 [Yy][Ee][Ss]|[Tt][Rr][Uu][Ee]|[Oo][Nn]|1) 132 printf "\\033[34;3m[Debug $(date +%T)]\\033[0m $*\\n" >&2 133 esac 134} 135 136# portshaker_usage 137# Display command line arguments for portshaker scripts. 138# 139portshaker_usage() 140{ 141 ( 142 echo -n "usage: $0 " 143 case ${_portshaker_arg} in 144 update) 145 echo "update" 146 ;; 147 clone_to) 148 echo "clone_to [-p] -t target [-z zfs_target_dataset]" 149 echo "Supported arguments" 150 echo " -p Show what is going to happen but do not do anything (pretend mode)" 151 echo " -t Target directory to clone the ports tree to" 152 echo " -z ZFS dataset to use and mount as target directory" 153 ;; 154 copy_to) 155 echo "copy_to -t target" 156 echo "Supported arguments" 157 echo " -t Target directory to copy the ports tree to" 158 ;; 159 merge_to) 160 echo "merge_to [-ap] [-b build] -m master -t target" 161 echo "Supported arguments:" 162 echo " -a Automatically copy port if files where touched but version is unchanged" 163 echo " -b Tinderboy build to add copied packages to" 164 echo " -m Master source ports tree (for comparing Mk/* files)" 165 echo " -p Show what is going to happen but do not do anything (pretend mode)" 166 echo " -t Target directory to merge the ports tree in" 167 ;; 168 *) 169 echo "($(echo $_keywords | sed -e "s/ /|/g"))" 170 ;; 171 esac 172 ) 1>&2 173 exit 1 174} 175 176# read_makefile_value 177# Reads the value of a variable from a Makefile in the current 178# directory. 179# Try to do it without calling make(1) at first to avoid some overhead, 180# but fallback to it if: 181# - the variable is empty; 182# (e.g. the Makefile include another Makefile that sets the value) 183# - the variable value starts with '$' 184# (e.g. PORTREVISION= ${OPENLDAP_PORTREVISION}) 185# In case of failure, return "0" 186# 187# $1 - Name of the variable to read. 188read_makefile_value() 189{ 190 value=`awk 'BEGIN { r = ""} /^'$1'\??=/ { split($0, a, /=[[:space:]]*/); r = a[2]; split(r, b, /[[:space:]]*#/); r = b[1] } END { print r } ' Makefile` 191 if [ -z "${value}" -o "${value%${value#?}}" = '$' ]; then 192 debug "Can't read variable '$1' from Makefile in '$PWD'. Falling back to make(1)." 193 value=`make -V $1` 194 if [ -z "${value}" ]; then 195 warn "Can't read variable '$1' from Makefile in '$PWD'. '0' substitued." 196 value="0" 197 fi 198 fi 199 echo $value 200} 201 202# read_file_vcs_keyword 203# Look for VCS $Keyword$ information string in the provided file. 204# $1 - Filename 205# $1 - Keyword 206read_file_vcs_keyword() 207{ 208 grep -o "\\\$$2:[^\$]*\\\$" "$1" 209} 210 211# newer_or_same_vcs_id 212# Compare VCS $Ids...$ in files and returns 0 unless the version is going 213# backwards in which case 1 is returned. 214# $1 - File to compare 215# $2 - Reference File 216newer_or_same_vcs_id() 217{ 218 _ret=0 219 220 for _keyword in ${VCS_ID_KEYWORDS}; do 221 new_rev=$(read_file_vcs_keyword "$1" ${_keyword} | cut -d' ' -f3) 222 old_rev=$(read_file_vcs_keyword "$2" ${_keyword} | cut -d' ' -f3) 223 224 if [ -z "${new_rev}" -o -z "${old_rev}" ]; then 225 continue 226 fi 227 228 _ret=`echo "${new_rev} ${old_rev}" | awk ' { 229 if ($1 > $2) { 230 print 1; 231 } else if ($1 == $2) { 232 print 0; 233 } else { 234 print -1; 235 } 236 }'` 237 238 done 239 240 #echo ${_ret} 241 242 if [ ${_ret} -eq -1 ]; then 243 return 1 244 else 245 return 0 246 fi 247} 248 249# newer_portepoch 250# Compare PORTEPOCH of two ports (``new'' and ``old'') and assert 251# that the ``new'' port is newer than the ``old'' one. 252# $1 - Directory of the ``new'' port. 253# $2 - Directory of the ``old'' port. 254# 255newer_portepoch() 256{ 257 new_epoch=$(cd $1 && read_makefile_value PORTEPOCH) 258 old_epoch=$(cd $2 && read_makefile_value PORTEPOCH) 259 260 if [ -z "${old_epoch}" -a -z "${new_epoch}" ]; then 261 return 1 262 fi 263 [ "${old_epoch}" -lt "${new_epoch}" ] 264} 265 266# newer_portversion 267# Compare PORTVERSION of two ports (``new'' and ``old'') and assert 268# that the ``new'' port is newer than the ``old'' one. 269# $1 - Directory of the ``new'' port. 270# $2 - Directory of the ``old'' port. 271# 272newer_portversion() 273{ 274 new_version=$(cd $1 && read_makefile_value PORTVERSION). 275 old_version=$(cd $2 && read_makefile_value PORTVERSION). 276 277 if [ ${old_version} = "0." -a ${new_version} = "0." ]; then 278 return 0 # Always update 279 fi 280 281 case $(${PKG_VERSION} -t ${old_version} ${new_version}) in 282 '<') return 0;; 283 '=') return 1;; 284 '>') return 2;; 285 *) err 1 "Unsupported '${PKG_VERSION}' reply";; 286 esac 287} 288 289# newer_portrevision 290# Compare PORTREVISION of two ports (``new'' and ``old'') and assert 291# that the ``new'' port is newer than the ``old'' one. 292# $1 - Directory of the ``new'' port. 293# $2 - Directory of the ``old'' port. 294# 295newer_portrevision() 296{ 297 new_revision=$(cd $1 && read_makefile_value PORTREVISION) 298 old_revision=$(cd $2 && read_makefile_value PORTREVISION) 299 300 if [ -z "${old_revision}" -a -z "${new_revision}" ]; then 301 return 1 302 fi 303 304 if [ "${old_revision:=0}" -lt "${new_revision:=0}" ]; then 305 return 0 306 elif [ "${old_revision:=0}" -gt "${new_revision:=0}" ]; then 307 return 2 308 else 309 return 1 310 fi 311} 312 313# unsubstitute_keywords 314# Unexpand keywords in source file, writing the new file in dest file. 315# $1 -- source file 316# $2 -- dest file 317unsubstitute_keywords() 318{ 319 sed -E -e 's#\$('$(echo ${VCS_ID_KEYWORDS} | sed -e 's/ /|/g')'):[^\$]*\$#$\1$#g' < $1 > $2 320} 321 322# compare_noid 323# Compare provided files ignoring any VCS Ids. Returns 0 if files are 324# identiqual. 325# $1 -- Original file 326# $2 -- New file 327compare_noid() 328{ 329 ret=1 330 original="$1" new="$2" 331 332 if [ -e "${original}" -a -e "${new}" ]; then 333 cmp -s "${original}" "${new}" 334 ret=$? 335 if [ ${ret} -ne 0 ]; then 336 # Files are different: un-substitutes keywords and try again. 337 original_noid=`mktemp -t MFC` 338 new_noid=`mktemp -t MFC` 339 340 unsubstitute_keywords "${original}" "${original_noid}" 341 unsubstitute_keywords "${new}" "${new_noid}" 342 343 eval diff ${DIFF_IGNORE_FLAGS_PORT} "${original_noid}" "${new_noid}" > /dev/null 344 ret=$? 345 rm -f ${RM_FLAGS} "${original_noid}" "${new_noid}" 346 fi 347 fi 348 return ${ret} 349} 350 351# run_portshaker_command 352# Do all the magic! 353# 354run_portshaker_command() 355{ 356 _return=0 357 358 _portshaker_arg=$1 359 shift 1 360 361 debug "${_portshaker_arg} $*" 362 363 # if ${extra_info} contains `:`, the port infrastructure will produce 364 # warnings when attempting to launch make(1) from the source directory 365 # (required when parsing e.g. PORTVERSION fails). Workaround this by 366 # substituting the `:` "reserved" char with an inocent one. 367 extra_info=`echo ${extra_info} | sed 's/:/_/g'` 368 369 name="`basename $0`${extra_info}" 370 371 _portsdir="${mirror_base_dir}/${name}" 372 373 # If the source ports tree directory does not exist, create it. 374 if [ ! -d "${mirror_base_dir}" ]; then 375 info "Creating the '${mirror_base_dir}' directory." 376 if [ $_use_zfs -eq 1 ]; then 377 err 1 "\$mirror_base_dir must be set to an existing ZFS filesystem when \$use_zfs is set to '${use_zfs}'. You have to manually create the proper ZFS filesystem according to your local setup." 378 fi 379 mkdir -p "${mirror_base_dir}" || err 1 "Failed to create '${mirror_base_dir}' directory." 380 fi 381 382 # Only proceed known commands. 383 for _elem in ${_keywords}; do 384 if [ "${_elem}" != "${_portshaker_arg}" ]; then 385 continue 386 fi 387 388 # Look for a pre-command and execute it. 389 if type ${name}_pre${_portshaker_arg} 1>/dev/null 2>&1; then 390 info "Executing pre-command '${name}_pre${_portshaker_arg}'." 391 ${name}_pre${_portshaker_arg} "$@" || exit 1 392 fi 393 394 if [ $_use_zfs -eq 1 ]; then 395 _source_dataset=`zfs list -H | awk "\\\$5 == \"${_portsdir}\" {print \\\$1}"` 396 fi 397 398 # Proceed with the command 399 case "${_portshaker_arg}" in 400 clone_to|copy_to) 401 _target="" 402 _zfs_target_dataset="" 403 while getopts aP:pt:z: _i; do 404 case "${_i}" in 405 p) _pretend=1 ;; 406 P) _poudriere_tree="$OPTARG" ;; 407 t) _target="$OPTARG" ;; 408 z) _zfs_target_dataset="$OPTARG" ;; 409 ?) portshaker_usage ;; 410 esac 411 done 412 if [ -z "${_target}" ]; then 413 portshaker_usage 414 fi 415 416 if [ ! -d "${_portsdir}" ]; then 417 err 1 "'${_portsdir}' does not exists." 418 fi 419 if [ -z "${_target}" ]; then 420 err 1 "Missing target directory." 421 fi 422 if [ ! -d "${_target}" ]; then 423 debug "Creating the '${_target}' directory." 424 mkdir -p "${_target}" || err 1 "Cannot create '${_target}' directory." 425 fi 426 if [ $_use_zfs -eq 1 ]; then 427 if [ -d "${_target}/.zfs" ]; then 428 _target_dataset=`zfs list -H | awk "\\\$5 == \"${_target}\" {print \\\$1}"` 429 elif [ -d "${_target}/ports/.zfs" ]; then 430 _target_dataset=`zfs list -H | awk "\\\$5 == \"${_target}/ports\" {print \\\$1}"` 431 fi 432 if [ -z "${_source_dataset}" ]; then 433 warn "'${_portsdir}' shall be an existing ZFS filesystem when \$use_zfs=yes. Falling back to rsync(1)." 434 _use_zfs=0 435 fi 436 fi 437 case "${_portshaker_arg}" in 438 clone_to) 439 info "Cloning '${_portsdir}' to '${_target}'." 440 if [ $_use_zfs -eq 1 ]; then 441 if [ -z "${_target_dataset}" ]; then 442 warn "No ZFS dataset already exists with mountpoint '${_target}'. Will use '${_zfs_target_dataset}'." 443 _target_dataset="$_zfs_target_dataset" 444 if [ -z "${_target_dataset}" ]; then 445 warn "No '\$zfs_target_dataset' provided (hint: use '-z zfs_target_dataset')." 446 warn "Falling back to rsync(1)." 447 _use_zfs=0 448 fi 449 fi 450 fi # _use_zfs -eq 1 451 if [ $_use_zfs -eq 1 ]; then 452 if [ -d "${_target}/.zfs" ]; then 453 debug "Running 'zfs destroy -r ${_target_dataset}'." 454 if [ $_pretend -eq 0 ]; then 455 zfs destroy -r ${_target_dataset} 456 if [ $? -ne 0 ]; then 457 echo "The following processes are using '${_target}':" 458 fstat -f "${_target}" 459 err 1 "Can't destroy ZFS filesystem '${_target_dataset}'." 460 fi 461 fi 462 fi 463 # Get the latests snapshot available. 464 _snapshot=`cd ${_portsdir}/.zfs/snapshot && ls -d portshaker-* | tail -n 1` 465 if [ -z "${_snapshot}" ]; then 466 err 1 "No snapshot for '${_source_dataset}'. Update the '${name}' source port tree then retry." 467 fi 468 debug "Running 'zfs clone ${_source_dataset}@${_snapshot} ${_target_dataset}'." 469 if [ $_pretend -eq 0 ]; then 470 zfs clone ${_source_dataset}@${_snapshot} ${_target_dataset} || err 1 "Can't clone '${_source_dataset}@${_snapshot}' to '${_target_dataset}'." 471 fi 472 debug "Running 'zfs set mountpoint="${_target}" "${_target_dataset}"'." 473 if [ $_pretend -eq 0 ]; then 474 zfs set mountpoint="${_target}" "${_target_dataset}" || err 1 "Can't set '${_target_dataset}' mountpoint to '${_target}'." 475 fi 476 debug "Running 'zfs set compression="${target_zfs_compression}" "${_target_dataset}"'." 477 if [ $_pretend -eq 0 ]; then 478 zfs set compression=${target_zfs_compression} ${_target_dataset} || warn "Cannot set ${target_zfs_compression} compression on the ${_target_dataset} ZFS filesystem." 479 fi 480 if [ -n "${_poudriere_tree}" ]; then 481 debug "Setting poudriere ports properties on '${_target_dataset}'" 482 if [ $_pretend -eq 0 ]; then 483 zfs set "poudriere:type=ports" "${_target_dataset}" 484 zfs set "poudriere:name=${_poudriere_tree}" "${_target_dataset}" 485 fi 486 fi 487 else 488 debug "Running 'rsync ${RSYNC_FLAGS} ${RSYNC_ARGS} ${RSYNC_EXCLUDE} \"${_portsdir}/\" \"${_target}\"'." 489 if [ $_pretend -eq 0 ]; then 490 rsync ${RSYNC_FLAGS} ${RSYNC_ARGS} ${RSYNC_EXCLUDE} "${_portsdir}/" "${_target}" 491 if [ $? -ne 0 ]; then 492 err 1 "Failed to clone '${_portsdir}' to '${_target}'." 493 fi 494 fi 495 fi 496 ;; 497 copy_to) 498 info "Copying '${_portsdir}' to '${_target}'." 499 debug Running "'cp -pr ${CP_FLAGS} \"${_portsdir}/\" \"${_target}\"'." 500 cp -pr ${CP_FLAGS} "${_portsdir}/" "${_target}" 501 if [ $? -ne 0 ]; then 502 err 1 "Failed to copy '${_portsdir}' to '${_target}'." 503 fi 504 ;; 505 esac 506 ;; 507 merge_to) 508 _auto_install=0 509 _master="" 510 _target="" 511 _tinderbox_builds="" 512 while getopts ab:m:pt: _i; do 513 case "${_i}" in 514 a) _auto_install=1 ;; 515 b) _tinderbox_builds="${_tinderbox_builds} $OPTARG" ;; 516 m) _master="$OPTARG" ;; 517 p) _pretend=1 ;; 518 t) _target="$OPTARG" ;; 519 ?) portshaker_usage ;; 520 esac 521 done 522 if [ -z "${_master}" -o -z "${_target}" ]; then 523 portshaker_usage 524 fi 525 526 if [ ! -d "${_portsdir}" ]; then 527 err 1 "${_portsdir} does not exists." 528 fi 529 if [ -z "${_target}" ]; then 530 err 1 "Missing target directory." 531 fi 532 if [ ! -d "${_target}" ]; then 533 err 1 "Target directory does not exist." 534 fi 535 536 info "Merging '${_portsdir}' to '${_target}'." 537 538 # Merge Mk/*.mk files 539 if [ -d "${_portsdir}/Mk" ]; then 540 if [ ! -d "${_target}/Mk" ]; then 541 err 1 "'${_target}/Mk' missing! copy / clone repository before merging." 542 fi 543 cd "${_portsdir}/Mk" || exit 1 544 for _mk in `find . -type f | sed s:"./"::`; do 545 if [ ${_mk} = "CVS" -o ${_mk} = ".svn" ]; then 546 continue 547 fi 548 if [ -e "${_target}/Mk/${_mk}" -a -e "${mirror_base_dir}/${_master}/Mk/${_mk}" ]; then 549 _mk_subdir="`dirname "${_mk}"`/" 550 _mk_file=`basename "${_mk}"` 551 _mk_patched="${_target}/Mk/${_mk_subdir}.${_mk_file}-${name}-patched" 552 if [ -e "${_mk_patched}" ]; then 553 warn "'Mk/${_mk}' has already been patched (ignoring)." 554 continue 555 fi 556 if ! newer_or_same_vcs_id "${_mk}" "${mirror_base_dir}/${_master}/Mk/${_mk}"; then 557 err 1 "'Mk/${_mk}' from '${name}' is not in sync with the one provided in the FreeBSD ports." 558 fi 559 debug "Updating 'Mk/${_mk}'." 560 if [ ${_pretend} -eq 0 ]; then 561 _patch=`diff ${DIFF_FLAGS} ${DIFF_IGNORE_FLAGS_MK} "${mirror_base_dir}/${_master}/Mk/${_mk}" "${_mk}"` 562 if [ -z "${_patch}" ]; then 563 debug "No patch required for {$_mk}" 564 elif echo "${_patch}" | (cd ${_target}/Mk && patch ${PATCH_FLAGS}); then 565 touch "${_mk_patched}" 566 else 567 err 1 "Unable to patch 'Mk/${_mk}'." 568 fi 569 fi 570 else 571 debug "Copying 'Mk/${_mk}'." 572 if [ ${_pretend} -eq 0 ]; then 573 cp ${CP_FLAGS} "${_mk}" "${_target}/Mk/${_mk}" || err 1 "Unable to copy 'Mk/${_mk}'." 574 fi 575 fi 576 done 577 fi 578 579 # To determine a port version, we sometimes need to run 580 # make. Ensure ${PORTSDIR}/Mk can be used. Since we 581 # have already merged the Mk directory, any local Mk 582 # files should be found where they are supposed to be 583 # found. 584 export PORTSDIR=${_target} 585 586 # Merge ports 587 cd "${_portsdir}" || exit 1 588 for _category in *; do 589 _regen_category_makefile=0 590 if [ ! -d ${_category} ] || [ ${_category} = "Mk" -o ${_category} = "CVS" -o ${_category} = ".svn" -o ${_category} = ".git" -o ${_category} = ".hg" -o ${_category} = "Tools" ]; then 591 continue 592 fi 593 cd ${_category} || exit 1 594 if [ ! -d "${_target}/${_category}" ]; then 595 debug "Creating category '${_category}'." 596 if [ ${_pretend} -eq 0 ]; then 597 mkdir "${_target}/${_category}" || exit 1 598 fi 599 if [ -f Makefile ]; then 600 debug "Copying '${_category}/Makefile'." 601 if [ ${_pretend} -eq 0 ]; then 602 cp ${CP_FLAGS} Makefile "${_target}/${_category}" || err 1 "Unable to copy '${_category}/Makefile'." 603 fi 604 fi 605 fi 606 for _port in *; do 607 if [ ${_port} = "CVS" -o ${_port} = ".svn" -o ${_port} = "Makefile" ]; then 608 continue 609 fi 610 _copy_port=0 611 _version_going_backward=0 612 613 debug "Processing ${_category}/${_port}." 614 615 if [ ! -f "${_port}/Makefile" ]; then 616 warn "${_category}/${_port}: No Makefile in this directory!" 617 continue 618 fi 619 620 # Track packages version to help debugging failing update paths 621 _current_pkg_version="0" 622 623 # Determine wether the port has to be merged or not 624 if [ ! -d "${_target}/${_category}/${_port}" ]; then 625 debug "${_category}/${_port} is a new port." 626 _regen_category_makefile=1 627 _copy_port=1 628 else 629 _current_pkg_version=`${MAKE} -C "${_target}/${_category}/${_port}" -V PKGVERSION` 630 # Compare port versions 631 if newer_portepoch "${_portsdir}/${_category}/${_port}" "${_target}/${_category}/${_port}"; then 632 debug "${_category}/${_port} has been updated (higher PORTEPOCH)." 633 _copy_port=1 634 else 635 newer_portversion "${_portsdir}/${_category}/${_port}" "${_target}/${_category}/${_port}" 636 case $? in 637 0) 638 debug "${_category}/${_port} has been updated (higher PORTVERSION)." 639 _copy_port=1 640 ;; 641 1) 642 newer_portrevision "${_portsdir}/${_category}/${_port}" "${_target}/${_category}/${_port}" 643 case $? in 644 0) 645 debug "${_category}/${_port} has been updated (higher PORTREVISION)." 646 _copy_port=1 647 ;; 648 1) 649 debug "${_category}/${_port} has not been updated." 650 ;; 651 2) 652 _version_going_backward=1 653 ;; 654 esac 655 ;; 656 2) 657 _version_going_backward=1 658 ;; 659 esac 660 fi 661 fi 662 if [ ${_version_going_backward} -eq 1 ]; then 663 warn "${_category}/${_port}: port version going backward (I will not merge this port)!" 664 if [ -f "${_target}/.portshaker-merged-ports" ]; then 665 awk -F: 'BEGIN { first = 1 ; } $2 == "'${_category}/${_port}'" { if (first == 1) { printf(" `-> Update path: %s", $3); } printf(" --[%s]--> %s", $1, $4); first = 0 } END { if (first == 0) { printf("\n"); } } ' < "${_target}/.portshaker-merged-ports" 666 fi 667 fi 668 # Ensure merging is consistent 669 if [ ${_copy_port} -eq 0 -a ${_version_going_backward} = 0 ]; then 670 _touched_files="" 671 672 # Look for modifications of port files 673 for _file in `find "${_portsdir}/${_category}/${_port}" -maxdepth 1 -type f -exec basename '{}' ';'`; do 674 if [ ! -e "${_target}/${_category}/${_port}/${_file}" ]; then 675 debug "${_category}/${_port}/${_file} has been created." 676 _touched_files="${_touched_files} ${_file}" 677 else 678 compare_noid "${_portsdir}/${_category}/${_port}/${_file}" "${_target}/${_category}/${_port}/${_file}" 679 if [ $? -ne 0 ]; then 680 debug "${_category}/${_port}/${_file} has been changed." 681 _touched_files="${_touched_files} ${_file}" 682 fi 683 fi 684 done 685 for _file in `find "${_target}/${_category}/${_port}" -maxdepth 1 -type f -exec basename '{}' ';'`; do 686 if [ ! -e "${_portsdir}/${_category}/${_port}/${_file}" ]; then 687 debug "${_category}/${_port}/${_file} has been removed." 688 _touched_files="${_touched_files} ${_file}" 689 fi 690 done 691 692 # Look for modification of patches 693 _patches="" 694 if [ -d "${_portsdir}/${_category}/${_port}/files" ]; then 695 for _patch in `find "${_portsdir}/${_category}/${_port}/files" -maxdepth 1 -type f -exec basename '{}' ';'`; do 696 if [ ! -e "${_target}/${_category}/${_port}/files/${_patch}" ]; then 697 debug "${_category}/${_port}/files/${_patch} has been created." 698 _patches="${_patches} ${_patch}" 699 else 700 compare_noid "${_portsdir}/${_category}/${_port}/files/${_patch}" "${_target}/${_category}/${_port}/files/${_patch}" 701 if [ $? -ne 0 ]; then 702 debug "${_category}/${_port}/files/${_patch} has been changed." 703 _patches="${_patches} ${_patch}" 704 fi 705 fi 706 done 707 fi 708 if [ -d "${_target}/${_category}/${_port}/files" ]; then 709 for _patch in `find "${_target}/${_category}/${_port}/files" -maxdepth 1 -type f -exec basename '{}' ';'`; do 710 if [ ! -e "${_portsdir}/${_category}/${_port}/files/${_patch}" ]; then 711 debug "${_category}/${_port}/files/${_patch} has been removed." 712 _patches="${_patches} ${_patch}" 713 fi 714 done 715 fi 716 for _patch in ${_patches}; do 717 compare_noid "${_portsdir}/${_category}/${_port}/files/${_patch}" "${_target}/${_category}/${_port}/files/${_patch}" 718 if [ $? -ne 0 ]; then 719 _touched_files="${_touched_files} files/${_patch}" 720 fi 721 done 722 723 # Handle touched files 724 if [ -n "${_touched_files}" ]; then 725 if [ "${_auto_install}" -eq 1 ]; then 726 warn "${_category}/${_port}:${_touched_files} changed but the port version is still the same (copying port anyway)." 727 _copy_port=1 728 elif [ ${_pretend} -eq 1 ]; then 729 warn "${_category}/${_port}:${_touched_files} changed but the port version is still the same." 730 else 731 _r="" 732 while true; do 733 case ${_r} in 734 # diff 735 d*|D*) 736 for _file in ${_touched_files}; do 737 diff ${DIFF_FLAGS} "${_target}/${_category}/${_port}/${_file}" "${_portsdir}/${_category}/${_port}/${_file}" 738 done | ${PAGER} 739 _r="" 740 ;; 741 # install 742 i*|I*) 743 _copy_port=1 744 break 745 ;; 746 # continue 747 c*|C*) 748 break 749 ;; 750 *) 751 warn "Conflict detected!" 752 echo "The target ports tree (${_target}) already have the version of ${_category}/${_port} provided by ${name}." 753 echo "However, the following file(s) has been touched:${_touched_files}." 754 echo "An update may be required." 755 756 if [ "${_fail_on_conflict}" -eq 1 ]; then 757 exit 1 758 fi 759 760 echo -n "(diff|install|continue) > " 761 read _r 762 ;; 763 esac 764 done 765 fi 766 fi 767 fi 768 # Merge port 769 if [ ${_copy_port} -eq 1 -a ${_version_going_backward} -eq 0 ]; then 770 debug "Copying '${_portsdir}/${_category}/${_port}' to '${_target}/${_category}/${_port}'." 771 if [ ${_pretend} -eq 0 ]; then 772 rm -rf ${RM_FLAGS} "${_target}/${_category}/${_port}" || err 1 "Cannot remove outdated '${_target}/${_category}/${_port}' port directory." 773 cp -pr ${CP_FLAGS} "${_portsdir}/${_category}/${_port}" "${_target}/${_category}/${_port}" || err 1 "Cannot merge ${_category}/${_port}." 774 for _special_file in CVS .svn files/CVS files/.svn; do 775 if [ -d "${_target}/${_category}/${_port}/${_special_file}" ]; then 776 rm -rf ${RM_FLAGS} "${_target}/${_category}/${_port}/${_special_file}" || err 1 "Cannot remove '${_target}/${_category}/${_port}/${_special_file}'." 777 fi 778 done 779 # Reference port in tinderbox 780 if [ -n "${_tinderbox_builds}" ]; then 781 for _build in ${_tinderbox_builds}; do 782 (cd "${PB}/scripts" && ${TC} addPort -b "${_build}" -d "${_category}/${_port}") 783 done 784 fi 785 # Keep a trace of merged ports (for speeding up tinderbox in hooks for example) 786 _new_pkg_version=`${MAKE} -C "${_target}/${_category}/${_port}" -V PKGVERSION 2> /dev/null` 787 if [ -z ${_new_pkg_version} ]; then 788 # Second chance: if it was not possible to get the package name from the tagret 789 # ports tree, try to get it from the source ports tree. This is usefull if the 790 # current port has a master port in the source ports tree that has not already 791 # been merged in the target ports tree. 792 _new_pkg_version=`${MAKE} -C "${_portsdir}/${_category}/${_port}" -V PKGVERSION 2> /dev/null` 793 fi 794 echo "${name}:${_category}/${_port}:${_current_pkg_version}:${_new_pkg_version}" >> "${_target}/.portshaker-merged-ports" 795 fi 796 else 797 debug "Not copying '${_portsdir}/${_category}/${_port}' to '${_target}/${_category}/${_port}'." 798 fi 799 done # _port 800 801 if [ ${_regen_category_makefile} -eq 1 ]; then 802 (cd ${_target}/${_category} 803 debug "Updating ${_category} Makefile." 804 if [ ${_pretend} -eq 0 ]; then 805 sed -e '/SUBDIR/,$d' Makefile > Makefile.new 806 find . -mindepth 1 -maxdepth 1 -type d -print | sort | sed -e 's|^./| SUBDIR += |' >> Makefile.new 807 sed -e '1,/SUBDIR/d' -e '/SUBDIR/d' Makefile >> Makefile.new 808 mv -f Makefile.new Makefile 809 fi 810 ) 811 fi 812 cd .. 813 done # _category 814 815 # Remove obsolete ports 816 if [ -f "${_portsdir}/MOVED" ]; then 817 info "Merging MOVED entries from '${name}'." 818 grep -v '^#' "${_portsdir}/MOVED" | while read _line; do 819 _port=`echo $_line | cut -d'|' -f1` 820 if [ -z "${_port}" ]; then 821 warn "Malformed line found in ${_portsdir}/MOVED: \"${_line}\"" 822 continue 823 fi 824 if [ -d "${_target}/${_port}" ]; then 825 info "Removing obsolete port ${_port}." 826 if [ ${_pretend} -eq 0 ]; then 827 rm -rf ${RM_FLAGS} "${_target}/${_port}" || err 1 "Cannot remove obsolete port ${_port}." 828 fi 829 else 830 debug "${_port} referenced in ${_portsdir}/MOVED does not exist." 831 fi 832 if [ ${_pretend} -eq 0 ]; then 833 echo "${_line}" >> "${_target}/MOVED" 834 fi 835 done # while read _line 836 fi 837 838 # Merge UPDATING information 839 if [ -f "${_portsdir}/UPDATING" -a -f "${_target}/UPDATING" -a ${_pretend} -eq 0 ]; then 840 info "Merging UPDATING entries from '${name}'." 841 mv "${_target}/UPDATING" "${_target}/UPDATING.orig" 842 awk -f "@@SHAREDIR@@/portshaker/merge-updating.awk" -v in1="${_target}/UPDATING.orig" -v in2="${_portsdir}/UPDATING" -v out="${_target}/UPDATING" 843 rm ${RM_FLAGS} "${_target}/UPDATING.orig" 844 fi 845 846 # Merge UIDs GIDs 847 if [ -f "${_portsdir}/UIDs" -a ${_pretend} -eq 0 ]; then 848 info "Merging UIDs from ${name}." 849 mv "${_target}/UIDs" "${_target}/UIDs.orig" 850 cat "${_portsdir}/UIDs" "${_target}/UIDs.orig" | sort -nut: -k3 > "${_target}/UIDs" 851 rm "${_target}/UIDs.orig" 852 fi 853 if [ -f "${_portsdir}/GIDs" -a ${_pretend} -eq 0 ]; then 854 info "Merging GIDs from ${name}." 855 mv "${_target}/GIDs" "${_target}/GIDs.orig" 856 cat "${_portsdir}/GIDs" "${_target}/GIDs.orig" | sort -nut: -k3 > "${_target}/GIDs" 857 rm "${_target}/GIDs.orig" 858 fi 859 ;; 860 update) 861 info "Updating '${name}' source ports tree with method '${method}'." 862 863 if [ ! -d "${_portsdir}" ] && [ ! "${method}" = "cvs" ]; then 864 debug "Creating the '${_portsdir}' directory." 865 if [ $_use_zfs -eq 1 ]; then 866 _parent=`dirname "${_portsdir}"` 867 _parent_dataset=`zfs list -H | awk "\\\$5 == \"${_parent}\" {print \\\$1}"` 868 if [ -z "${_parent_dataset}" ]; then 869 err 1 "The ${_parent} directory shall be a ZFS filesystem when \$use_zfs=yes." 870 fi 871 _source_dataset="${_parent_dataset}/${name}" 872 zfs create "${_source_dataset}" || err 1 "Cannot create the ${_source_dataset} ZFS filesystem." 873 zfs set compression=${source_zfs_compression} ${_source_dataset} || warn "Cannot set ${source_zfs_compression} compression on the ${_source_dataset} ZFS filesystem." 874 else 875 mkdir -p "${_portsdir}" || err 1 "Cannot create the '${_portsdir}' directory." 876 fi 877 fi 878 if [ ! -w "${_portsdir}" ] && [ ! "${method}" = "cvs" ]; then 879 err 1 "'${_portsdir}' is not writable: cannot update '${name}'." 880 fi 881 882 case "${method}" in 883 csup) 884 if [ -z "${csup_supfile}" ]; then 885 err 1 "\$csup_supfile is not defined." 886 fi 887 if [ "${name}" != "ports" ]; then 888 err 1 "A source ports tree updated using csup requires be named 'ports'." 889 fi 890 debug "Running 'csup ${CSUP_FLAGS} ${csup_supfile}'." 891 csup ${CSUP_FLAGS} ${csup_supfile} 892 if [ $? -ne 0 ]; then 893 err 1 "csup update failed." 894 fi 895 ;; 896 cvs) 897 if [ -z "${cvs_root}" ]; then 898 err 1 "\$cvs_root is not defined." 899 fi 900 if [ -z "${cvs_module}" ]; then 901 err 1 "\$cvs_module is not defined." 902 fi 903 if [ ! -d "${_portsdir}/CVS" ]; then 904 cd ${mirror_base_dir} 905 debug "Running '${CVS} ${CVS_FLAGS} -d\"${cvs_root}\" login'." 906 ${CVS} ${CVS_FLAGS} -d"${cvs_root}" login 907 if [ $? -ne 0 ]; then 908 err 1 "CVS login failed." 909 fi 910 debug "Running '${CVS} ${CVS_FLAGS} -d\"${cvs_root}\" checkout -d ${name} -P \"${cvs_module}\"'." 911 ${CVS} ${CVS_FLAGS} -d"${cvs_root}" checkout -d ${name} -P "${cvs_module}" 912 if [ $? -ne 0 ]; then 913 err 1 "CVS checkout failed." 914 fi 915 else 916 cd "${_portsdir}" 917 debug "Running '${CVS} ${CVS_FLAGS} update -Pd -A'." 918 ${CVS} ${CVS_FLAGS} update -Pd -A 2>&1 | sed 's|^cvs update: \(.*\) is no longer in the repository$|D \1|' 919 if [ $? -ne 0 ]; then 920 err 1 "CVS update failed." 921 fi 922 fi 923 ;; 924 git) 925 if [ -z "${git_clone_uri}" ]; then 926 err 1 "\$git_clone_uri is not defined." 927 fi 928 if [ ! -d "${_portsdir}/.git" ]; then 929 debug "Running '${GIT} clone ${git_clone_uri} ${_portsdir}'." 930 git_clone_flags="--depth 1 --branch ${git_branch:=master}" 931 ${GIT} clone "${git_clone_uri}" ${git_clone_flags} "${_portsdir}" 932 if [ $? -ne 0 ]; then 933 err 1 "git clone failed." 934 fi 935 if [ -n "${git_submodules}" ]; then 936 cd "${_portsdir}" 937 ${GIT} submodule init 938 if [ $? -ne 0 ]; then 939 err 1 "git submodule init failed." 940 fi 941 ${GIT} submodule update --recursive 942 if [ $? -ne 0 ]; then 943 err 1 "git submodule update failed." 944 fi 945 if [ -n "${git_submodules_branch}" ]; then 946 ${GIT} submodule foreach --recursive git checkout ${git_submodules_branch} 947 if [ $? -ne 0 ]; then 948 err 1 "git submodule checkout failed." 949 fi 950 fi 951 fi 952 else 953 cd "${_portsdir}" 954 debug "Running '${GIT} fetch'." 955 ${GIT} fetch 956 if [ $? -ne 0 ]; then 957 err 1 "git fetch failed." 958 fi 959 if [ -n "${git_branch}" ]; then 960 ${GIT} checkout "${git_branch}" 961 fi 962 if [ -n "${git_submodules}" ]; then 963 ${GIT} submodule foreach --recursive git fetch 964 if [ $? -ne 0 ]; then 965 err 1 "git submodule fetch failed." 966 fi 967 fi 968 debug "Running '${GIT} reset --hard origin/${git_branch:=master}'" 969 ${GIT} reset --hard origin/${git_branch:=master} 970 if [ $? -ne 0 ]; then 971 err 1 "git reset --hard origin/${git_branch:=master}." 972 fi 973 fi 974 ;; 975 hg) 976 if [ -z "${hg_clone_uri}" ]; then 977 err 1 "\$hg_clone_uri is not defined." 978 fi 979 if [ ! -d "${_portsdir}/.hg" ]; then 980 debug "Running '${HG} clone ${hg_clone_uri} ${_portsdir}'." 981 ${HG} clone "${hg_clone_uri}" "${_portsdir}" 982 if [ $? -ne 0 ]; then 983 err 1 "hg clone failed." 984 fi 985 else 986 cd "${_portsdir}" 987 debug "Running '${HG} pull'." 988 ${HG} pull 989 if [ $? -ne 0 ]; then 990 err 1 "hg pull failed." 991 fi 992 debug "Running '${HG} update'." 993 ${HG} update 994 if [ $? -ne 0 ]; then 995 err 1 "hg update failed." 996 fi 997 fi 998 ;; 999 portsnap) 1000 PORTSNAP_FLAGS="${PORTSNAP_FLAGS} -p ${_portsdir}" 1001 if [ ! -f "${_portsdir}/.portsnap.INDEX" ]; then 1002 # "portsnap alfred" <=> "portsnap fetch && portsnap update", but we need to "portsnap extract" here. 1003 debug "Running 'portsnap ${PORTSNAP_FLAGS} fetch'." 1004 portsnap ${PORTSNAP_FLAGS} fetch 1005 if [ $? -ne 0 ]; then 1006 err 1 "portsnap fetch failed." 1007 fi 1008 debug "Running 'portsnap ${PORTSNAP_FLAGS} extract'." 1009 portsnap ${PORTSNAP_FLAGS} extract 1010 if [ $? -ne 0 ]; then 1011 err 1 "portsnap extract failed." 1012 fi 1013 else 1014 debug "Running 'portsnap ${PORTSNAP_FLAGS} alfred'." 1015 portsnap ${PORTSNAP_FLAGS} alfred 1016 if [ $? -ne 0 ]; then 1017 err 1 "portsnap alfred failed." 1018 fi 1019 fi 1020 ;; 1021 rsync) 1022 if [ -z "${rsync_source_path}" ]; then 1023 err 1 "\$rsync_source_path is not defined." 1024 fi 1025 debug "Running '${RSYNC} ${RSYNC_FLAGS} ${RSYNC_ARGS} ${rsync_extra_args} ${rsync_source_path}/ ${_portsdir}/'." 1026 set -f 1027 ${RSYNC} ${RSYNC_FLAGS} ${RSYNC_ARGS} ${rsync_extra_args} "${rsync_source_path}/" "${_portsdir}/" 1028 rsync_res=$? 1029 set +f 1030 if [ $rsync_res -ne 0 ]; then 1031 err 1 "rsync update failed." 1032 fi 1033 ;; 1034 svn) 1035 if [ -z "${svn_checkout_path}" ]; then 1036 err 1 "\$svn_checkout_path is not defined." 1037 fi 1038 if [ ! -d "${_portsdir}/.svn" ]; then 1039 debug "Running '${SVN} ${SVN_FLAGS} checkout \"${svn_checkout_path}\" \"${_portsdir}\"'." 1040 ${SVN} ${SVN_FLAGS} checkout "${svn_checkout_path}" "${_portsdir}" 1041 if [ $? -ne 0 ]; then 1042 err 1 "Subversion checkout failed." 1043 fi 1044 else 1045 cd "${_portsdir}" 1046 debug "Running '${SVN} ${SVN_FLAGS} update'." 1047 ${SVN} ${SVN_FLAGS} update 1048 if [ $? -ne 0 ]; then 1049 err 1 "Subversion update failed." 1050 fi 1051 fi 1052 ;; 1053 svn+subtrees) 1054 if [ -z "${svn_checkout_path}" ]; then 1055 err 1 "\$svn_checkout_path is not defined." 1056 fi 1057 cd "${_portsdir}" 1058 if [ ! -d "${_portsdir}/.svn" ]; then 1059 debug "Running '${SVNADMIN} ${SVNADMIN_FLAGS} create \"${_portsdir}/.repos\"'." 1060 ${SVNADMIN} ${SVNADMIN_FLAGS} create "${_portsdir}/.repos" 1061 if [ $? -ne 0 ]; then 1062 err 1 "Creating subversion repository failed." 1063 fi 1064 debug "Running '${SVN} ${SVN_FLAGS} checkout \"file://${_portsdir}/.repos\" \"${_portsdir}\"'." 1065 ${SVN} ${SVN_FLAGS} checkout "file://${_portsdir}/.repos" "${_portsdir}" 1066 if [ $? -ne 0 ]; then 1067 err 1 "Subversion update failed." 1068 fi 1069 debug "Moving \"${_portsdir}/.repos\" to \"${_portsdir}/.svn/.repos\"." 1070 mv "${_portsdir}/.repos" "${_portsdir}/.svn" 1071 debug "Running '${SVN} ${SVN_FLAGS} relocate \"file://${_portsdir}/.repos\" \"file://${_portsdir}/.svn/.repos\"'." 1072 ${SVN} ${SVN_FLAGS} relocate "file://${_portsdir}/.repos" "file://${_portsdir}/.svn/.repos" 1073 if [ $? -ne 0 ]; then 1074 err 1 "Subversion relocate failed." 1075 fi 1076 fi 1077 1078 debug "Running '${SVN} ${SVN_FLAGS} propset svn:externals'." 1079 for _part in ${svn_checkout_subtrees}; do 1080 echo ${svn_checkout_path}/${_part} $(echo ${_part} | grep -oE '^[^@]+') 1081 done | ${SVN} ${SVN_FLAGS} propset svn:externals -F - . 1082 if [ $? -ne 0 ]; then 1083 err 1 "Subversion propset failed." 1084 fi 1085 debug "Running '${SVN} ${SVN_FLAGS} commit'." 1086 ${SVN} ${SVN_FLAGS} commit -m "portshaker: change svn:externals" 1087 if [ $? -ne 0 ]; then 1088 err 1 "Subversion commit failed." 1089 fi 1090 debug "Running '${SVN} ${SVN_FLAGS} update'." 1091 ${SVN} ${SVN_FLAGS} update 1092 if [ $? -ne 0 ]; then 1093 err 1 "Subversion update failed." 1094 fi 1095 ;; 1096 *) 1097 err 1 "Unsupported update method '${method}'." 1098 ;; 1099 esac 1100 1101 # If the updated ports tree is a ZFS filesystem, snapshot it. 1102 if [ $_use_zfs -eq 1 -a -d "${_portsdir}/.zfs" ]; then 1103 for p in "${_portsdir}/.zfs/snapshot/portshaker"*; do 1104 # Purge old snapshots 1105 debug "Running 'zfs destroy ${_source_dataset}@`basename ${p}`'." 1106 if [ $_pretend -eq 0 ]; then 1107 zfs destroy ${_source_dataset}@`basename ${p}` >/dev/null 2>&1 1108 fi 1109 done 1110 date=`date +%F:%T` 1111 debug "Running 'zfs snapshot ${_source_dataset}@portshaker-${date}'." 1112 if [ $_pretend -eq 0 ]; then 1113 zfs snapshot "${_source_dataset}@portshaker-${date}" || err 1 "Can't create ZFS snapshot '${_source_dataset}@portshaker-${date}'." 1114 fi 1115 fi 1116 ;; # _portshaker_arg = update 1117 esac 1118 1119 # Look for a post-command and execute it 1120 if type ${name}_post${_portshaker_arg} 1>/dev/null 2>&1; then 1121 info "Executing post-command \"${name}_post${_portshaker_arg}\"." 1122 ${name}_post${_portshaker_arg} "$@" || exit 1 1123 fi 1124 1125 return ${_return} 1126 done 1127 1128 # Invalid command 1129 echo 1>&2 "$0: unknown command '${_portshaker_arg}'" 1130 portshaker_usage 1131 # not reached 1132} 1133 1134fi 1135