terom@60: #!/bin/bash terom@52: # vim: set ft=sh : terom@52: # terom@52: # Dependency-based updates + utils terom@52: terom@52: ## Compare the given output file with all given source files: terom@52: # terom@52: # check_update $out ${deps[@]} && do_update $out ... || ... terom@52: # terom@52: # Returns true if the output file needs to be updated. terom@52: function check_update { terom@52: # target terom@85: local out="$1"; shift terom@52: terom@52: debug "$out" terom@52: terom@52: # need update? terom@52: local update= terom@52: terom@52: if [ ${#@} == 0 ]; then terom@52: debug " update: unknown deps" terom@52: update=y terom@52: terom@85: elif [ ! -e "$out" ]; then terom@52: debug " update: dest missing" terom@52: update=y terom@52: terom@52: elif [ $UPDATE_FORCE ]; then terom@52: debug " update: forced" terom@52: update=y terom@52: fi terom@52: terom@52: # check deps terom@52: for dep in "$@"; do terom@52: # don't bother checking if already figured out terom@52: [ $update ] && continue terom@52: terom@52: # check terom@84: if [ ! -e "$dep" ]; then terom@85: warn "$out: Missing source: $dep" terom@52: terom@85: elif [ "$out" -ot "$dep" ]; then terom@52: debug " update: $dep" terom@52: update=y terom@52: else terom@52: debug " check: $dep" terom@52: fi terom@52: done terom@52: terom@52: [ ! $update ] && debug " up-to-date" terom@52: terom@52: # return terom@52: [ $update ] terom@52: } terom@52: terom@52: ## Generate updated output file from given command's stdout: terom@52: # terom@52: # do_update $out $BIN/cmd --args terom@52: # terom@52: # Writes output to a temporary .new file, optionally shows a diff of changes, and commits terom@52: # the new version to $out (unless noop'd). terom@52: function do_update { terom@85: local out="$1"; shift terom@85: local tmp="$out.new" terom@52: terom@52: debug "$out" terom@85: cmd "$@" > "$tmp" terom@52: terom@52: # compare terom@85: if [ -e "$out" ] && [ $UPDATE_DIFF ]; then terom@52: debug " changes:" terom@52: terom@52: # terse terom@85: indent " " diff --unified=1 "$out" "$tmp" || true terom@52: fi terom@52: terom@52: # deploy terom@52: if [ $UPDATE_NOOP ]; then terom@52: # cleanup terom@52: debug " no-op" terom@52: terom@85: cmd rm "$tmp" terom@52: else terom@52: # commit terom@52: debug " deploy" terom@52: terom@85: cmd mv "$tmp" "$out" terom@52: fi terom@52: } terom@52: terom@52: ## Compare symlink to target: terom@52: # terom@52: # check_link $lnk $tgt && do_link $lnk $tgt || ... terom@52: # terom@52: # Tests if the symlink exists, and the target matches. terom@52: # Fails if the target does not exist. terom@52: function check_link { terom@85: local lnk="$1" terom@85: local tgt="$2" terom@52: terom@85: [ ! -e "$tgt" ] && fail "$tgt: target does not exist" terom@52: terom@85: [ ! -e "$lnk" ] || [ $(readlink "$lnk") != "$tgt" ] terom@52: } terom@52: terom@52: ## Update symlink to point to target: terom@52: # terom@52: # do_link $lnk $tgt terom@52: # terom@52: function do_link { terom@85: local lnk="$1" terom@85: local tgt="$2" terom@52: terom@85: cmd ln -sf "$tgt" "$lnk" terom@52: } terom@52: terom@85: ## Read include paths from file terom@85: function read_zone_includes { terom@85: cmd sed -n -E 's/^\$INCLUDE\s+"(.+)"/\1/p' "$@" terom@85: } terom@85: terom@85: ## (cached) include paths for zone file terom@85: function zone_includes { terom@85: local cache="$1" terom@85: local src="$2" terom@87: local prefix="${3:-}" terom@85: terom@85: if [ ! -e "$cache" -o "$cache" -ot "$src" ]; then terom@85: read_zone_includes "$src" > "$cache" terom@85: fi terom@85: terom@85: while read include; do terom@87: echo -n "$prefix$include " terom@85: done < "$cache" terom@85: } terom@87: terom@87: ## Search for prefix-matching includes in zone file terom@87: function zone_includes_grep { terom@87: local cache="$1" terom@87: local src="$2" terom@87: local prefix="$3" terom@87: terom@87: for include in $(zone_includes $cache $src); do terom@87: if [ "${include#$prefix}" != "$include" ]; then terom@87: echo -n " ${include#$prefix}" terom@87: fi terom@87: done terom@87: }