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 # The CONFIGDEPS variable will be set to the ones that exist
24 # (for use in dependency lines).
26 # wrap it up for safety
28 [ z
"$MKTOP" = z
"/" ] || MKTOP
="${MKTOP%/}"
30 ! [ -f "${MKTOP:+$MKTOP/}config.sh" ] ||
{
31 . .
/"${MKTOP:+$MKTOP/}config.sh"
32 CONFIGDEPS
="${CONFIGDEPS:+$CONFIGDEPS }${MKTOP:+$MKTOP/}config.sh"
34 # now set CONFIGMAK and make it an absolute path
35 [ -n "$CONFIGMAK" ] || CONFIGMAK
="${MKTOP:+$MKTOP/}config.mak"
36 if [ -f "$CONFIGMAK" ]; then
37 CONFIGDEPS
="${CONFIGDEPS:+$CONFIGDEPS }$CONFIGMAK"
39 CONFIGMAK
="${MKTOP:+$MKTOP/}Makefile.mt"
41 case "$CONFIGMAK" in */?
*);;*) CONFIGMAK
="./$CONFIGMAK"; esac
42 CONFIGMAK
="$(cd "${CONFIGMAK%/*}" && pwd)/${CONFIGMAK##*/}"
45 # "which" but using only POSIX shell built-ins
47 { "unset" -f command unset unalias "$1"; } >/dev
/null
2>&1 ||
:
48 { "unalias" -a ||
unalias -m "*"; } >/dev
/null
2>&1 ||
:
52 # set GNO_PD_OPT to --no-print-directory
53 # - unless GNO_PD_OPT is already set (even if to empty)
54 # - unless "w" appears in MAKEFLAGS (check skipped if first arg is not-empty)
55 # - the $(MAKE) binary does NOT contain GNUmakefile
56 # - the $(MAKE) binary does NOT produce a "GNU Make ..." --version line
58 if test z
"$GNO_PD_OPT" = z
"--no-print-directory"; then
59 # It likes to sneak back in so rip it back out
60 case "$MAKEFLAGS" in "w"*)
61 MAKEFLAGS
="${MAKEFLAGS#w}"
65 test z
"${GNO_PD_OPT+set}" != z
"set" ||
return 0
67 if test z
"$1" = z
; then
68 case "${MAKEFLAGS%%[ =]*}" in *w
*)
72 set -- "$("${MAKE:-make}" --no-print-directory --version 2>/dev/null)" ||
:
73 case "$1" in "GNU "[Mm
]"ake "*)
74 GNO_PD_OPT
="--no-print-directory"
82 # vplus appends the second and following arguments (concatenated with a space)
83 # to the variable named by the first with a space if it's set and non-empty
84 # or otherwise it's just set. If there are no arguments nothing is done but
85 # appending an empty string will set the variable to empty if it's not set
87 test z
"$1" != z ||
return 1
88 test $# -ge 2 ||
return 0
92 set -- "$vplus_v_" "" "$vplus_val_"
93 unset vplus_v_ vplus_val_
94 eval set -- '"$1"' "\"\$$1\"" '"$3"'
95 set -- "$1" "${2:+$2 }$3"
99 # vpre prepends the second and following arguments (concatenated with a space)
100 # to the variable named by the first with a space if it's set and non-empty
101 # or otherwise it's just set. If there are no arguments nothing is done but
102 # appending an empty string will set the variable to empty if it's not set
104 test z
"$1" != z ||
return 1
105 test $# -ge 2 ||
return 0
109 set -- "$vpre_v_" "" "$vpre_val_"
110 unset vpre_v_ vpre_val_
111 eval set -- '"$1"' "\"\$$1\"" '"$3"'
112 set -- "$1" "$3${2:+ $2}"
116 # stores the single-quoted value of the variable name passed as
117 # the first argument into the variable name passed as the second
118 # (use quotevar 3 varname "$value" to quote a value directly)
120 eval "set -- \"\${$1}\" \"$2\""
122 set -- "$(printf '%s\nZ\n' "$1" | sed "s
/'/'\\\''/g")" "$2"
123 set -- "${1%??}" "$2"
128 # The result(s) of stripping the second argument from the end of the
129 # third and following argument(s) is joined using a space and stored
130 # in the variable named by the first argument
136 for _item in "$@"; do
137 _result="$_result ${_item%$_sfx}"
139 eval "$_var="'"${_result# }"'
140 unset _var _sfx _result _item
143 # The result(s) of appending the second argument to the end of the
144 # third and following argument(s) is joined using a space and stored
145 # in the variable named by the first argument
151 for _item in "$@"; do
152 _result="$_result $_item$_sfx"
154 eval "$_var="'"${_result# }"'
155 unset _var _sfx _result _item
158 # The result(s) of stripping the second argument from the end of the
159 # fourth and following argument(s) and then appending the third argument is
160 # joined using a space and stored in the variable named by the first argument
166 v_strip_sfx _result2 "$_stripsfx" "$@"
167 v_add_sfx "$_var2" "$_addsfx" $_result2
168 unset _var2 _stripsfx _addsfx _result2
171 # The second and following argument(s) are joined with a space and
172 # stored in the variable named by the first argument
180 # The second and following argument(s) are joined with a space and then
181 # the result has leading and trailing whitespace removed and internal
182 # whitespace sequences replaced with a single space and is then stored
183 # in the variable named by the first argument and pathname expansion is
184 # disabled during the stripping process
193 # Expand the second and following argument(s) using pathname expansion but
194 # skipping any that have no match and join all the results using a space and
195 # store that in the variable named by the first argument
200 for _item in "$@"; do
201 eval "_exp=\"\$(printf ' %s
' $_item)\""
202 [ " $_item" = "$_exp" ] && ! [ -e "$_item" ] || _result="$_result$_exp"
204 eval "$_var="'"${_result# }"'
205 unset _var _result _item _exp
208 # Sort the second and following argument(s) removing duplicates and join all the
209 # results using a space and store that in the variable named by the first argument
216 set -- $(printf '%s
\n' "$@" | LC_ALL=C sort -u)
222 # Filter the fourth and following argument(s) according to the space-separated
223 # list of '%' pattern(s) in the third argument doing a "filter-out" instead of
224 # a "filter" if the second argument is true and join all the results using a
225 # space and store that in the variable named by the first argument
234 set -- $(awk -v "fo=$_fo" -f - "$_pat" "$*"<<'EOT'
236 gsub(/[][*?+.|{}()^$\\]/, "\\\\&", p)
240 if (match(p, /\\*%/)) {
241 return qr(substr(p, 1, RSTART - 1)) \
242 substr(p, RSTART, RLENGTH - (2 - RLENGTH % 2)) \
243 (RLENGTH % 2 ? ".*" : "%") \
244 qr(substr(p, RSTART + RLENGTH))
249 function qm(s, _l, _c, _a, _i, _g) {
250 if (!(_c = split(s, _l, " "))) return "^$"
251 if (_c == 1) return "^" qp(_l[1]) "$"
254 for (_i = 1; _i <= _c; ++_i) {
255 _a = _a _g qp(_l[_i])
265 cnt = split(vals, va, " ")
266 for (i=1; i<=cnt; ++i)
267 if ((va[i] ~ qpat) == !fo) print va[i]
273 unset _var _fo _pat _saveifs
276 # Filter the third and following argument(s) according to the space-separated
277 # list of '%' pattern(s) in the second argument and join all the results using
278 # a space and store that in the variable named by the first argument
282 v_filter_ "$_var" "" "$@"
285 # Filter out the third and following argument(s) according to the space-separated
286 # list of '%' pattern(s) in the second argument and join all the results using
287 # a space and store that in the variable named by the first argument
291 v_filter_ "$_var" "1" "$@"
294 # Write the third and following target arguments out as target with a dependency
295 # line(s) to standard output where each line is created by stripping the target
296 # argument suffix specified by the first argument ('' to strip nothing) and
297 # adding the suffix specified by the second argument ('' to add nothing).
298 # Does nothing if "$1" = "$2". (Set $1 = " " and $2 = "" to write out
299 # dependency lines with no prerequisites.)
301 [ "$1" != "$2" ] || return 0
305 for _targ in "$@"; do
306 printf '%s
: %s
\n' "$_targ" "${_targ%$_strip}$_add"
308 unset _strip _add _targ
311 # always turn on allexport now