=== modified file 'common.zsh' --- common.zsh 2017-12-12 22:50:13 +0000 +++ common.zsh 2017-12-12 17:03:06 +0000 @@ -8,10 +8,7 @@ OUT=$3 FUNCDIR=${0:h:a}/functions TARGET=${BASE##*/}.${${${DO:t}%.do}#default.} -typeset -g value_counter last_var __check_deps__ -value_counter=0 -typeset -ga finish_hooks args -typeset -gA vars vals +typeset -ga finish_hooks # helper that prints out error message and exits die() { @@ -32,106 +29,6 @@ done } -# Logical variable handling -ground() { - (($# == 1)) || die - (($+vals[$vars[$1]])) -} - -getvars() { - local -a ground_vars - local k v - if (($#)); then - for k in $@; do - ground $k && ground_vars+=( $k ) - done - else - for k v in ${(kv)vars}; do - (($+vals[$v])) && ground_vars+=( $k ) - done - fi - printf 'local %s\n' ${(j: :)ground_vars} - for k in $ground_vars; do - printf '%s=$vals[$vars[%s]]\n' $k $k - done -} - -setvar() { - (($# == 2)) || die - if ground $1; then - [[ $vals[$vars[$1]] == $2 ]] \ - || die "$0: Could not unify $1 (value ${(qqq)vals[$vars[$1]]}) with ${(qqq)2}" - else - vals[$vars[$1]]=$2 - fi -} - -defvar() { - # set variable only if unbound - if ! ground $1; then - setvar $1 $2 - fi -} - -fresh() { - (($# < 2)) || die - value_counter=$[value_counter + 1] - last_var=${1:-_$value_counter} - (($+vars[$last_var])) && die "$0: Variable already defined: $1" - vars[$last_var]=$value_counter -} - -unify_replace() { - # replace internal representation of var $1 with $2, effectively unifying them - local k v - for k v in ${(kv)vars}; do - [[ $v == $1 ]] && vars[$k]=$2 - done -} - -unify() { - (($# == 2)) || die - if ground $1; then - if ground $2; then - [[ $vals[$vars[$1]] == $vals[$vars[$2]] ]] \ - || die "$0: Could not unify $1 (value ${(qqq)vals[$vars[$1]]}) with $2 (value ${(qqq)vals[$vars[$2]]})" - # deduplicate value - unify_replace $vars[$1] $vars[$2] - unset "vals[$vars[$1]]" - else - unify_replace $vars[$2] $vars[$1] - fi - else - unify_replace $vars[$1] $vars[$2] - fi -} - -args() { - (($#args)) && die "$0: args already defined" - local arg - local -A unseen - unseen=( "${(kv@}vars}" ) - for arg in "$@"; do - case $arg in - (*=*) - args+=( ${arg%%=*} ) - defvar ${arg%%=*} ${arg#*=} - ;; - (*!) - args+=( ${arg%!} ) - ground ${arg%!} \ - || die "$0: Required argument ${(qqq)${arg%!}} not defined" - ;; - (*) args+=( $arg );; - esac - done - for arg in $args; do - unset "unseen[$arg]" - done - (($#unseen)) && die "$0: Undefined arguments passed: ${(k)unseen}" -} - -# argument formatting decode_argstr() { local base base=${BASE##*/} @@ -141,8 +38,8 @@ done tr <<<$base _- /+ | base64 -d } - decode_args() { + typeset -gA vars local arg decoded decoded=$(decode_argstr) \ || die "Unable to decode: ${(qqq)${BASE##*/}}" @@ -152,36 +49,27 @@ echo >&2 "Malformed argument: ${(qqq)arg}" exit 1 fi - fresh ${arg%%=*} - setvar ${arg%%=*} ${arg#*=} + if (( $+vars[${arg%%=*}] )); then + echo >&2 "Duplicate argument: ${arg%%=*}" + exit 1 + fi + vars[${arg%%=*}]=${arg#*=} done } -print_meta() { - local -a arglist unifications - local -A names - local k v - - for k in ${(o)args}; do - if (($+names[$vars[$k]])); then - names[$vars[$k]]+=" $k" - else - names[$vars[$k]]=$k - fi - done - - for k v in ${(kv)names}; do - [[ $v == *' '* ]] && unifications+=( $v ) - (( $+vals[$k] )) && arglist+=( ${v%% *}=$vals[$k] - done - - arglist=( ${(o)arglist} __check_deps__=$__check_deps__ ) - (( $#unifications )) && arglist=( __unified__=${(vF)} $arglist ) +print-meta() { + local -a arglist + local k + for k in ${(ko)vars}; do + arglist+=( "$k=$vars[$k]" ) + done printf "%s" "${(j::)arglist}" } -start_info(){ - info "called: ${(qqq)$(decode_argstr)}" +start-info(){ + local name + name=${${${DO:t}%.do}#default.} + info "entering: $name $(decode_argstr)" } add_finish_hook() { @@ -195,23 +83,51 @@ done } +finish-info(){ + local name + name=${${${DO:t}%.do}#default.} + info "finished: $name $(decode_argstr)" + (($+vars[sha256sum])) && info "$name produced: $vars[sha256sum]" + (($+vars[build_dir])) && info "$name build in: $vars[build_dir]" + (($+vars[filename])) && info "$name filename: $vars[filename]" +} + finish() { run_finish_hooks - print_meta >$OUT + finish-info + print-meta >$OUT exit 0 } -# dependency tracking +# set $vars parameter if it's empty (ie. set default value) +defvar() { + if ! (($+vars[$1])); then + vars[$1]=$2 + fi +} + +# check if variables are nonempty +checkvars() { + local var + local -a empty + for var in "$@"; do + if ! (($+vars[$var])); then + empty+=( $var ) + fi + done + (( $#empty )) && die "required parameters are empty: ${(@q)empty}" +} + dep_add_file() { local out out=$(sha256sum $1) || die - __check_deps__+='if { test -e '${(qqq)1}$' }\n' - __check_deps__+='if { pipeline -d { printf "%s\n" '${(qqq)out}$' } sha256sum -c }\n' + vars[__check_deps__]+='if { test -e '${(qqq)1}$' }\n' + vars[__check_deps__]+='if { pipeline -d { printf "%s\n" '${(qqq)out}$' } sha256sum -c }\n' } dep_add_missing() { [[ -e $1 ]] || die "$0: ${(qqq)1} exists" - __check_deps__+='if -n { test -e '${(qqq)1}$' }\n' + vars[__check_deps__]+='if -n { test -e '${(qqq)1}$' }\n' } dep_add_file_or_missing() { @@ -230,7 +146,7 @@ out=$(tar -cvp -C $1 --mtime=1970-01-01 . | sha256sum -) (( ${(j.|.)pipestatus} )) \ && die "$0: Failed to checksum directory: ${(qqq)1}" - __check_deps__+='if { pipeline -d { printf "%s\n" '${(qqq)out}'} fdmove 3 1 pipeline -d { tar -cvp --mtime=1970-01-01 -C '${(qqq)1}$' . } sha256sum -c /proc/self/fd/3 }\n' + vars[__check_deps__]+='if { pipeline -d { printf "%s\n" '${(qqq)out}'} fdmove 3 1 pipeline -d { tar -cvp --mtime=1970-01-01 -C '${(qqq)1}$' . } sha256sum -c /proc/self/fd/3 }\n' } dep_add_dir_mtimes() { @@ -239,34 +155,70 @@ out=$(tar -cvp -C $1 . | sha256sum -) (( ${(j.|.)pipestatus} )) \ && die "$0: Failed to checksum directory: ${(qqq)1}" - __check_deps__+='if { pipeline -d { printf "%s\n" '${(qqq)out}'} fdmove 3 1 pipeline -d { tar -cvp -C '${(qqq)1}$' . } sha256sum -c /proc/self/fd/3 }\n' + vars[__check_deps__]+='if { pipeline -d { printf "%s\n" '${(qqq)out}'} fdmove 3 1 pipeline -d { tar -cvp -C '${(qqq)1}$' . } sha256sum -c /proc/self/fd/3 }\n' } ifchange() { local arg redo-ifchange "$@" || die "failed to build one or more dependencies: $*" for arg in "$@"; do - __check_deps__+='if { redo-ifchange '${(qqq)arg}$' }\n' + vars[__check_deps__]+='if { redo-ifchange '${(qqq)arg}$' }\n' dep_add_file $arg done } -exit_unchanged() { +parse-previous() { + typeset -g previous_parsed has_previous + typeset -gA previous + local arg + + (($previous_parsed)) && return + + if ! [[ -f $FILE ]]; then + info "changed: missing $FILE" + previous_parsed=1 + has_previous=0 + return 1 + fi + + for arg in "${(s::Q)$(<$FILE)}"; do + if [[ "$arg" != *=* ]]; then + die "$0: Malformed argument: ${(qqq)arg}" + fi + if (( $+previous[${arg%%=*}] )); then + die "$0: Duplicate argument: ${arg%%=*}" + fi + previous[${arg%%=*}]=${arg#*=} + done + + previous_parsed=1 + has_previous=1 +} + +exit-unchanged() { info "$DO:t unchanged, skipping build" cp $FILE $OUT exit 0 } depend() { - # usage: depend [] + # TODO: edit this comment copied over from confz + # usage: require [] [-- ] # where: - # is a dependency name as defined by dofile `default..do` - # are named arguments to pass to dependency in following forms: - # foo=bar -- sets argument foo to value "bar" - # foo: -- unifies argument foo with variable foo in the caller - # foo:bar -- unifies argument foo with variable bar in the caller + # is a dependency name as defined by function confz__check + # are variable assignments in the form of: + # foo=bar -- sets ${vars[foo]} in calee to value "bar" + # :foo -- passes ${vars[foo]} from caller to calee + # ?foo=bar -- sets ${vars[foo]} in calee to value of ${vars[bar]} + # iff it is defined in the caller + # ?foo -- passes ${vars[foo]} from caller to calee iff it is defined + # in the caller + # %foo -- passes ${vars[foo]} from callee to caller + # %foo=bar -- passes ${vars[foo]} from callee + # to variable ${vars[bar]} of caller + # are arguments passed to the dependency function - local name + local name outer inner local target local -a new_vars run local -A lift @@ -277,21 +229,16 @@ # parse variable assignments while (( $# )); do case $1 in - (*=*) new_vars+=( ${1%%=*}=${1#*=} );; - (*:) - if ground ${1%:}; then - new_vars+=( ${1%:}=$vals[$vars[${1%:}]] ) - else - lift[${1%:}]=${1%:} - fi - ;; - (*:*) - if ground ${1#*:}; then - new_vars+=( ${1%:*}=$vals[$vars[${1#*:}]] ) - else - lift[${1%:*}]=${1#*:} - fi - ;; + (:*) ((${+vars[${1#:}]})) || \ + die "variable ${(qqq)1#:} not set, passed as :argument" + new_vars+=( ${1#:}="${vars[${1#:}]}" );; + (\?*=*) (($+vars[${${1#\?}#*=})) && \ + new_vars+=( ${${1#\?}%%=*}="$vars[${${1#\?}#*=}" );; + (\?*) ((${+vars[${1#\?}]})) && \ + new_vars+=( ${1#\?}="${vars[${1#\?}]}" );; + (%*=*) lift[${${1#%}%%=*}]=${${1#%}#*=};; + (%*) lift[${1#%}]=${1#%};; + (*=*) new_vars+=( "${1%%=*}"="${1#*=}" );; (--) shift; break;; (*) die "$name: unrecognised argument: ${(qqq)1}";; esac @@ -321,68 +268,24 @@ fi lift_vars[${arg%%=*}]=${arg#*=} done - if (($+lift_vars[__unified__])); then - local names var - fresh - var=$last_var - for names in $lift_vars[__unified__]; do - for name in $=names; do - if (($+lift[$name])); then - unify $var $name - fi - if (($+lift_vars[$name])); then - setvar $var $lift_vars[$name] - fi - done - done - fi - local inner outer for inner outer in ${(kv)lift}; do ((${+lift_vars[$inner]})) || \ die "variable ${(qqq)inner} not set, was requested by caller as ${(qqq)outer}" - setvar $outer ${lift_vars[$inner]} + vars[$outer]=${lift_vars[$inner]} done fi } -parse-previous() { - typeset -g previous_parsed has_previous - typeset -gA previous - local arg - - (($previous_parsed)) && return - - if ! [[ -f $FILE ]]; then - info "changed: missing $FILE" - previous_parsed=1 - has_previous=0 - return 1 - fi - - for arg in "${(s::Q)$(<$FILE)}"; do - if [[ "$arg" != *=* ]]; then - die "$0: Malformed argument: ${(qqq)arg}" - fi - if (( $+previous[${arg%%=*}] )); then - die "$0: Duplicate argument: ${arg%%=*}" - fi - previous[${arg%%=*}]=${arg#*=} - done - - previous_parsed=1 - has_previous=1 -} - if (($+functions[main])); then redo-ifchange $0:h:a/common.zsh || exit $? autoload_functions decode_args - start_info + start-info dep_add_file $DO dep_add_file common.zsh parse-previous if (($+previous[__check_deps__])) && execlineb -c $previous[__check_deps__]; then - exit_unchanged + exit-unchanged fi main || exit $? finish