1 # gnomake.sh - POSIX Makefile.sh utility functions
2 # Copyright (C) 2017 Kyle J. McKay
7 ## Utility functions for Makefile.sh scripts
10 # prevent crazy "sh" implementations from exporting functions into environment
13 # always run in errexit mode
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
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
36 { "unset" -f command unset unalias "$1"; } >/dev
/null
2>&1 ||
:
37 { "unalias" -a ||
unalias -m "*"; } >/dev
/null
2>&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
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}"
54 test z
"${GNO_PD_OPT+set}" != z
"set" ||
return 0
56 if test z
"$1" = z
; then
57 case "${MAKEFLAGS%%[ =]*}" in *w
*)
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"
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
76 test z
"$1" != z ||
return 1
77 test $# -ge 2 ||
return 0
81 set -- "$vplus_v_" "" "$vplus_val_"
82 unset vplus_v_ vplus_val_
83 eval set -- '"$1"' "\"\$$1\"" '"$3"'
84 set -- "$1" "${2:+$2 }$3"
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
93 test z
"$1" != z ||
return 1
94 test $# -ge 2 ||
return 0
98 set -- "$vpre_v_" "" "$vpre_val_"
99 unset vpre_v_ vpre_val_
100 eval set -- '"$1"' "\"\$$1\"" '"$3"'
101 set -- "$1" "$3${2:+ $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)
109 eval "set -- \"\${$1}\" \"$2\""
111 set -- "$(printf '%s\nZ\n' "$1" | sed "s
/'/'\\\''/g")" "$2"
112 set -- "${1%??}" "$2"
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
125 for _item in "$@"; do
126 _result="$_result ${_item%$_sfx}"
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
140 for _item in "$@"; do
141 _result="$_result $_item$_sfx"
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
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
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
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
189 for _item in "$@"; do
190 eval "_exp=\"\$(printf ' %s
' $_item)\""
191 [ " $_item" = "$_exp" ] && ! [ -e "$_item" ] || _result="$_result$_exp"
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
205 set -- $(printf '%s
\n' "$@" | LC_ALL=C sort -u)
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
223 set -- $(awk -v "fo=$_fo" -f - "$_pat" "$*"<<'EOT'
225 gsub(/[][*?+.|{}()^$\\]/, "\\\\&", 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))
238 function qm(s, _l, _c, _a, _i, _g) {
239 if (!(_c = split(s, _l, " "))) return "^$"
240 if (_c == 1) return "^" qp(_l[1]) "$"
243 for (_i = 1; _i <= _c; ++_i) {
244 _a = _a _g qp(_l[_i])
254 cnt = split(vals, va, " ")
255 for (i=1; i<=cnt; ++i)
256 if ((va[i] ~ qpat) == !fo) print va[i]
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
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
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.)
290 [ "$1" != "$2" ] || return 0
294 for _targ in "$@"; do
295 printf '%s
: %s
\n' "$_targ" "${_targ%$_strip}$_add"
297 unset _strip _add _targ
300 # always turn on allexport now