t4200: test the out-of-date checking code
[topgit/pro.git] / gnomake.sh
blob26d7efb3ed8e1e388764696e677779d89d1074d3
1 # gnomake.sh - POSIX Makefile.sh utility functions
2 # Copyright (C) 2017 Kyle J. McKay
3 # All rights reserved
4 # License GPL2
6 ##
7 ## Utility functions for Makefile.sh scripts
8 ##
10 # prevent crazy "sh" implementations from exporting functions into environment
11 set +a
13 # always run in errexit mode
14 set -e
16 # configsh sources config.sh if it exists and then sets
17 # CONFIGMAK to either Makefile.mt (if there is no config.mak)
18 # or "config.mak" if there is.
20 # If MKTOP is set, they are looked for there instead of in
21 # the current directory.
23 # wrap it up for safety
24 configsh() {
25 ! [ -f "${MKTOP:+$MKTOP/}config.sh" ] ||
26 . ./"${MKTOP:+$MKTOP/}config.sh"
27 # now set CONFIGMAK and make it an absolute path
28 [ -n "$CONFIGMAK" ] || CONFIGMAK="${MKTOP:+$MKTOP/}config.mak"
29 [ -f "$CONFIGMAK" ] || CONFIGMAK="${MKTOP:+$MKTOP/}Makefile.mt"
30 case "$CONFIGMAK" in */?*);;*) CONFIGMAK="./$CONFIGMAK"; esac
31 CONFIGMAK="$(cd "${CONFIGMAK%/*}" && pwd)/${CONFIGMAK##*/}"
34 # "which" but using only POSIX shell built-ins
35 cmd_path() (
36 { "unset" -f command unset unalias "$1"; } >/dev/null 2>&1 || :
37 { "unalias" -a || unalias -m "*"; } >/dev/null 2>&1 || :
38 command -v "$1"
41 # set GNO_PD_OPT to --no-print-directory
42 # - unless GNO_PD_OPT is already set (even if to empty)
43 # - unless "w" appears in MAKEFLAGS (check skipped if first arg is not-empty)
44 # - the $(MAKE) binary does NOT contain GNUmakefile
45 # - the $(MAKE) binary does NOT produce a "GNU Make ..." --version line
46 set_gno_pd_opt() {
47 if test z"$GNO_PD_OPT" = z"--no-print-directory"; then
48 # It likes to sneak back in so rip it back out
49 case "$MAKEFLAGS" in "w"*)
50 MAKEFLAGS="${MAKEFLAGS#w}"
51 esac
52 return 0
54 test z"${GNO_PD_OPT+set}" != z"set" || return 0
55 GNO_PD_OPT=
56 if test z"$1" = z; then
57 case "${MAKEFLAGS%%[ =]*}" in *w*)
58 return 0
59 esac
61 set -- "$("${MAKE:-make}" --no-print-directory --version 2>/dev/null)" || :
62 case "$1" in "GNU "[Mm]"ake "*)
63 GNO_PD_OPT="--no-print-directory"
64 esac
65 return 0
68 # VAR += VAL
69 # => vplus VAR VAL
71 # vplus appends the second and following arguments (concatenated with a space)
72 # to the variable named by the first with a space if it's set and non-empty
73 # or otherwise it's just set. If there are no arguments nothing is done but
74 # appending an empty string will set the variable to empty if it's not set
75 vplus() {
76 test z"$1" != z || return 1
77 test $# -ge 2 || return 0
78 vplus_v_="$1"
79 shift
80 vplus_val_="$*"
81 set -- "$vplus_v_" "" "$vplus_val_"
82 unset vplus_v_ vplus_val_
83 eval set -- '"$1"' "\"\$$1\"" '"$3"'
84 set -- "$1" "${2:+$2 }$3"
85 eval "$1="'"$2"'
88 # vpre prepends the second and following arguments (concatenated with a space)
89 # to the variable named by the first with a space if it's set and non-empty
90 # or otherwise it's just set. If there are no arguments nothing is done but
91 # appending an empty string will set the variable to empty if it's not set
92 vpre() {
93 test z"$1" != z || return 1
94 test $# -ge 2 || return 0
95 vpre_v_="$1"
96 shift
97 vpre_val_="$*"
98 set -- "$vpre_v_" "" "$vpre_val_"
99 unset vpre_v_ vpre_val_
100 eval set -- '"$1"' "\"\$$1\"" '"$3"'
101 set -- "$1" "$3${2:+ $2}"
102 eval "$1="'"$2"'
105 # stores the single-quoted value of the variable name passed as
106 # the first argument into the variable name passed as the second
107 # (use quotevar 3 varname "$value" to quote a value directly)
108 quotevar() {
109 eval "set -- \"\${$1}\" \"$2\""
110 case "$1" in *"'"*)
111 set -- "$(printf '%s\nZ\n' "$1" | sed "s/'/'\\\''/g")" "$2"
112 set -- "${1%??}" "$2"
113 esac
114 eval "$2=\"'$1'\""
117 # The result(s) of stripping the second argument from the end of the
118 # third and following argument(s) is joined using a space and stored
119 # in the variable named by the first argument
120 v_strip_sfx() {
121 _var="$1"
122 _sfx="$2"
123 shift 2
124 _result=
125 for _item in "$@"; do
126 _result="$_result ${_item%$_sfx}"
127 done
128 eval "$_var="'"${_result# }"'
129 unset _var _sfx _result _item
132 # The result(s) of appending the second argument to the end of the
133 # third and following argument(s) is joined using a space and stored
134 # in the variable named by the first argument
135 v_add_sfx() {
136 _var="$1"
137 _sfx="$2"
138 shift 2
139 _result=
140 for _item in "$@"; do
141 _result="$_result $_item$_sfx"
142 done
143 eval "$_var="'"${_result# }"'
144 unset _var _sfx _result _item
147 # The result(s) of stripping the second argument from the end of the
148 # fourth and following argument(s) and then appending the third argument is
149 # joined using a space and stored in the variable named by the first argument
150 v_stripadd_sfx() {
151 _var2="$1"
152 _stripsfx="$2"
153 _addsfx="$3"
154 shift 3
155 v_strip_sfx _result2 "$_stripsfx" "$@"
156 v_add_sfx "$_var2" "$_addsfx" $_result2
157 unset _var2 _stripsfx _addsfx _result2
160 # The second and following argument(s) are joined with a space and
161 # stored in the variable named by the first argument
162 v_strip_() {
163 _var="$1"
164 shift
165 eval "$_var="'"$*"'
166 unset _var
169 # The second and following argument(s) are joined with a space and then
170 # the result has leading and trailing whitespace removed and internal
171 # whitespace sequences replaced with a single space and is then stored
172 # in the variable named by the first argument and pathname expansion is
173 # disabled during the stripping process
174 v_strip() {
175 _var="$1"
176 shift
177 set -f
178 v_strip_ "$_var" $*
179 set +f
182 # Expand the second and following argument(s) using pathname expansion but
183 # skipping any that have no match and join all the results using a space and
184 # store that in the variable named by the first argument
185 v_wildcard() {
186 _var="$1"
187 shift
188 _result=
189 for _item in "$@"; do
190 eval "_exp=\"\$(printf ' %s' $_item)\""
191 [ " $_item" = "$_exp" ] && ! [ -e "$_item" ] || _result="$_result$_exp"
192 done
193 eval "$_var="'"${_result# }"'
194 unset _var _result _item _exp
197 # Sort the second and following argument(s) removing duplicates and join all the
198 # results using a space and store that in the variable named by the first argument
199 v_sort() {
200 _var="$1"
201 _saveifs="$IFS"
202 shift
203 IFS='
205 set -- $(printf '%s\n' "$@" | LC_ALL=C sort -u)
206 IFS="$_saveifs"
207 eval "$_var="'"$*"'
208 unset _var _saveifs
211 # Filter the fourth and following argument(s) according to the space-separated
212 # list of '%' pattern(s) in the third argument doing a "filter-out" instead of
213 # a "filter" if the second argument is true and join all the results using a
214 # space and store that in the variable named by the first argument
215 v_filter_() {
216 _var="$1"
217 _fo="$2"
218 _pat="$3"
219 _saveifs="$IFS"
220 shift 3
221 IFS='
223 set -- $(awk -v "fo=$_fo" -f - "$_pat" "$*"<<'EOT'
224 function qr(p) {
225 gsub(/[][*?+.|{}()^$\\]/, "\\\\&", p)
226 return p
228 function qp(p) {
229 if (match(p, /\\*%/)) {
230 return qr(substr(p, 1, RSTART - 1)) \
231 substr(p, RSTART, RLENGTH - (2 - RLENGTH % 2)) \
232 (RLENGTH % 2 ? ".*" : "%") \
233 qr(substr(p, RSTART + RLENGTH))
235 else
236 return qr(p)
238 function qm(s, _l, _c, _a, _i, _g) {
239 if (!(_c = split(s, _l, " "))) return "^$"
240 if (_c == 1) return "^" qp(_l[1]) "$"
241 _a = ""
242 _g = "^("
243 for (_i = 1; _i <= _c; ++_i) {
244 _a = _a _g qp(_l[_i])
245 _g = "|"
247 return _a ")$"
249 BEGIN {exit}
250 END {
251 pat = ARGV[1]
252 vals = ARGV[2]
253 qpat = qm(pat)
254 cnt = split(vals, va, " ")
255 for (i=1; i<=cnt; ++i)
256 if ((va[i] ~ qpat) == !fo) print va[i]
260 IFS="$_saveifs"
261 eval "$_var="'"$*"'
262 unset _var _fo _pat _saveifs
265 # Filter the third and following argument(s) according to the space-separated
266 # list of '%' pattern(s) in the second argument and join all the results using
267 # a space and store that in the variable named by the first argument
268 v_filter() {
269 _var="$1"
270 shift
271 v_filter_ "$_var" "" "$@"
274 # Filter out the third and following argument(s) according to the space-separated
275 # list of '%' pattern(s) in the second argument and join all the results using
276 # a space and store that in the variable named by the first argument
277 v_filter_out() {
278 _var="$1"
279 shift
280 v_filter_ "$_var" "1" "$@"
283 # Write the third and following target arguments out as target with a dependency
284 # line(s) to standard output where each line is created by stripping the target
285 # argument suffix specified by the first argument ('' to strip nothing) and
286 # adding the suffix specified by the second argument ('' to add nothing).
287 # Does nothing if "$1" = "$2". (Set $1 = " " and $2 = "" to write out
288 # dependency lines with no prerequisites.)
289 write_auto_deps() {
290 [ "$1" != "$2" ] || return 0
291 _strip="$1"
292 _add="$2"
293 shift 2
294 for _targ in "$@"; do
295 printf '%s: %s\n' "$_targ" "${_targ%$_strip}$_add"
296 done
297 unset _strip _add _targ
300 # always turn on allexport now
301 set -a