testlib: allow "test_plan'?'"
[topgit/pro.git] / Makefile.sh
blob76764ffa389afc4a70e26c55b7337143d2d1e217
1 # Makefile.sh - POSIX Makefile scripting adjunct for TopGit
2 # Copyright (C) 2017 Kyle J. McKay
3 # All rights reserved
4 # License GPL2
6 # Set MAKEFILESH_DEBUG to get:
7 # 1. All defined environment variales saved to Makefile.var
8 # 2. set -x
9 # 3. set -v if MAKEFILESH_DEBUG contains "v"
11 if [ -n "$MAKEFILESH_DEBUG" ]; then
12 _setcmd="set -x"
13 case "$MAKEFILESH_DEBUG" in *"v"*) _setcmd="set -vx"; esac
14 eval "$_setcmd && unset _setcmd"
17 # prevent crazy "sh" implementations from exporting functions into environment
18 set +a
20 # common defines for all makefile(s)
21 defines() {
22 POUND="#"
24 # Update if you add any code that requires a newer version of git
25 : "${GIT_MINIMUM_VERSION:=1.8.5}"
27 # This avoids having this in no less than three different places!
28 TG_STATUS_HELP_USAGE="st[atus] [-v] [--exit-code]"
30 # These are initialized here so config.sh or config.mak can change them
31 # They are deliberately left with '$(...)' constructs for make to expand
32 # so that if config.mak just sets prefix they all automatically change
33 [ -n "$prefix" ] || prefix="$HOME"
34 [ -n "$bindir" ] || bindir='$(prefix)/bin'
35 [ -n "$cmddir" ] || cmddir='$(prefix)/libexec/topgit'
36 [ -n "$sharedir" ] || sharedir='$(prefix)/share/topgit'
37 [ -n "$hooksdir" ] || hooksdir='$(cmddir)/hooks'
40 # wrap it up for safe returns
41 # $1 is the current build target, if any
42 makefile() {
44 v_wildcard commands_in 'tg-[!-]*.sh'
45 v_wildcard utils_in 'tg--*.sh'
46 v_wildcard awk_in 'awk/*.awk'
47 v_wildcard hooks_in 'hooks/*.sh'
48 v_wildcard helpers_in 't/helper/*.sh'
50 v_strip_sfx commands_out .sh $commands_in
51 v_strip_sfx utils_out .sh $utils_in
52 v_strip_sfx awk_out .awk $awk_in
53 v_strip_sfx hooks_out .sh $hooks_in
54 v_strip_sfx helpers_out .sh $helpers_in
56 v_stripadd_sfx help_out .sh .txt tg-help.sh tg-status.sh $commands_in
57 v_stripadd_sfx html_out .sh .html tg-help.sh tg-status.sh tg-tg.sh $commands_in
59 DEPFILE="Makefile.dep"
61 write_auto_deps '' '.sh' tg $commands_out $utils_out $hooks_out $helpers_out
62 write_auto_deps '' '.awk' $awk_out
63 } >"$DEPFILE"
65 : "${SHELL_PATH:=/bin/sh}" "${AWK_PATH:=awk}"
66 version="$(
67 test -d .git && git describe --match "topgit-[0-9]*" --abbrev=4 --dirty 2>/dev/null |
68 sed -e 's/^topgit-//')" || :
70 # config.sh is wrapped up for return safety
71 configsh
73 # config.sh may not unset these
74 : "${SHELL_PATH:=/bin/sh}" "${AWK_PATH:=awk}"
76 case "$AWK_PATH" in */*) AWK_PREFIX=;; *) AWK_PREFIX="/usr/bin/"; esac
77 quotevar SHELL_PATH SHELL_PATH_SQ
78 quotevar AWK_PATH AWK_PATH_SQ
80 v_strip version "$version"
81 version_arg=
82 [ -z "$version" ] || version_arg="-e 's/TG_VERSION=.*/TG_VERSION=\"$version\"/'"
84 DESTDIRBOOL="No"
85 [ -z "$DESTDIR" ] || DESTDIRBOOL="Yes"
87 [ -z "$MAKEFILESH_DEBUG" ] || {
88 printenv | LC_ALL=C grep '^[_A-Za-z][_A-Za-z0-9]*=' | LC_ALL=C sort
89 } >"Makefile.var"
91 # Force TG-BUILD-SETTINGS to be updated now if needed
92 ${MAKE:-make} -f Makefile.mak FORCE_SETTINGS_BUILD=FORCE TG-BUILD-SETTINGS
94 # end of wrapper
98 ## Utility Functions
101 # wrap it up for safety
102 configsh() {
103 ! [ -f "${MKTOP:+$MKTOP/}config.sh" ] ||
104 . ./"${MKTOP:+$MKTOP/}config.sh"
105 # now set CONFIGMAK and make it an absolute path
106 [ -n "$CONFIGMAK" ] || CONFIGMAK="${MKTOP:+$MKTOP/}config.mak"
107 [ -f "$CONFIGMAK" ] || CONFIGMAK="${MKTOP:+$MKTOP/}Makefile.mt"
108 case "$CONFIGMAK" in */?*);;*) CONFIGMAK="./$CONFIGMAK"; esac
109 CONFIGMAK="$(cd "${CONFIGMAK%/*}" && pwd)/${CONFIGMAK##*/}"
112 cmd_path() (
113 { "unset" -f command unset unalias "$1"; } >/dev/null 2>&1 || :
114 { "unalias" -a || unalias -m "*"; } >/dev/null 2>&1 || :
115 command -v "$1"
118 # stores the single-quoted value of the variable name passed as
119 # the first argument into the variable name passed as the second
120 # (use quotevar 3 varname "$value" to quote a value directly)
121 quotevar() {
122 eval "set -- \"\${$1}\" \"$2\""
123 case "$1" in *"'"*)
124 set -- "$(printf '%s\nZ\n' "$1" | sed "s/'/'\\\''/g")" "$2"
125 set -- "${1%??}" "$2"
126 esac
127 eval "$2=\"'$1'\""
130 # The result(s) of stripping the second argument from the end of the
131 # third and following argument(s) is joined using a space and stored
132 # in the variable named by the first argument
133 v_strip_sfx() {
134 _var="$1"
135 _sfx="$2"
136 shift 2
137 _result=
138 for _item in "$@"; do
139 _result="$_result ${_item%$_sfx}"
140 done
141 eval "$_var="'"${_result# }"'
142 unset _var _sfx _result _item
145 # The result(s) of appending the second argument to the end of the
146 # third and following argument(s) is joined using a space and stored
147 # in the variable named by the first argument
148 v_add_sfx() {
149 _var="$1"
150 _sfx="$2"
151 shift 2
152 _result=
153 for _item in "$@"; do
154 _result="$_result $_item$_sfx"
155 done
156 eval "$_var="'"${_result# }"'
157 unset _var _sfx _result _item
160 # The result(s) of stripping the second argument from the end of the
161 # fourth and following argument(s) and then appending the third argument is
162 # joined using a space and stored in the variable named by the first argument
163 v_stripadd_sfx() {
164 _var2="$1"
165 _stripsfx="$2"
166 _addsfx="$3"
167 shift 3
168 v_strip_sfx _result2 "$_stripsfx" "$@"
169 v_add_sfx "$_var2" "$_addsfx" $_result2
170 unset _var2 _stripsfx _addsfx _result2
173 # The second and following argument(s) are joined with a space and
174 # stored in the variable named by the first argument
175 v_strip_() {
176 _var="$1"
177 shift
178 eval "$_var="'"$*"'
179 unset _var
182 # The second and following argument(s) are joined with a space and then
183 # the result has leading and trailing whitespace removed and internal
184 # whitespace sequences replaced with a single space and is then stored
185 # in the variable named by the first argument and pathname expansion is
186 # disabled during the stripping process
187 v_strip() {
188 _var="$1"
189 shift
190 set -f
191 v_strip_ "$_var" $*
192 set +f
195 # Expand the second and following argument(s) using pathname expansion but
196 # skipping any that have no match and join all the results using a space and
197 # store that in the variable named by the first argument
198 v_wildcard() {
199 _var="$1"
200 shift
201 _result=
202 for _item in "$@"; do
203 eval "_exp=\"\$(printf ' %s' $_item)\""
204 [ " $_item" = "$_exp" ] && ! [ -e "$_item" ] || _result="$_result$_exp"
205 done
206 eval "$_var="'"${_result# }"'
207 unset _var _result _item _exp
210 # Sort the second and following argument(s) removing duplicates and join all the
211 # results using a space and store that in the variable named by the first argument
212 v_sort() {
213 _var="$1"
214 _saveifs="$IFS"
215 shift
216 IFS='
218 set -- $(printf '%s\n' "$@" | LC_ALL=C sort -u)
219 IFS="$_saveifs"
220 eval "$_var="'"$*"'
221 unset _var _saveifs
224 # Filter the fourth and following argument(s) according to the space-separated
225 # list of '%' pattern(s) in the third argument doing a "filter-out" instead of
226 # a "filter" if the second argument is true and join all the results using a
227 # space and store that in the variable named by the first argument
228 v_filter_() {
229 _var="$1"
230 _fo="$2"
231 _pat="$3"
232 _saveifs="$IFS"
233 shift 3
234 IFS='
236 set -- $(awk -v "fo=$_fo" -f - "$_pat" "$*"<<'EOT'
237 function qr(p) {
238 gsub(/[][*?+.|{}()^$\\]/, "\\\\&", p)
239 return p
241 function qp(p) {
242 if (match(p, /\\*%/)) {
243 return qr(substr(p, 1, RSTART - 1)) \
244 substr(p, RSTART, RLENGTH - (2 - RLENGTH % 2)) \
245 (RLENGTH % 2 ? ".*" : "%") \
246 qr(substr(p, RSTART + RLENGTH))
248 else
249 return qr(p)
251 function qm(s, _l, _c, _a, _i, _g) {
252 if (!(_c = split(s, _l, " "))) return "^$"
253 if (_c == 1) return "^" qp(_l[1]) "$"
254 _a = ""
255 _g = "^("
256 for (_i = 1; _i <= _c; ++_i) {
257 _a = _a _g qp(_l[_i])
258 _g = "|"
260 return _a ")$"
262 BEGIN {exit}
263 END {
264 pat = ARGV[1]
265 vals = ARGV[2]
266 qpat = qm(pat)
267 cnt = split(vals, va, " ")
268 for (i=1; i<=cnt; ++i)
269 if ((va[i] ~ qpat) == !fo) print va[i]
273 IFS="$_saveifs"
274 eval "$_var="'"$*"'
275 unset _var _fo _pat _saveifs
278 # Filter the third and following argument(s) according to the space-separated
279 # list of '%' pattern(s) in the second argument and join all the results using
280 # a space and store that in the variable named by the first argument
281 v_filter() {
282 _var="$1"
283 shift
284 v_filter_ "$_var" "" "$@"
287 # Filter out the third and following argument(s) according to the space-separated
288 # list of '%' pattern(s) in the second argument and join all the results using
289 # a space and store that in the variable named by the first argument
290 v_filter_out() {
291 _var="$1"
292 shift
293 v_filter_ "$_var" "1" "$@"
296 # Write the third and following target arguments out as target with a dependency
297 # line(s) to standard output where each line is created by stripping the target
298 # argument suffix specified by the first argument ('' to strip nothing) and
299 # adding the suffix specified by the second argument ('' to add nothing).
300 # Does nothing if "$1" = "$2". (Set $1 = " " and $2 = "" to write out
301 # dependency lines with no prerequisites.)
302 write_auto_deps() {
303 [ "$1" != "$2" ] || return 0
304 _strip="$1"
305 _add="$2"
306 shift 2
307 for _targ in "$@"; do
308 printf '%s: %s\n' "$_targ" "${_targ%$_strip}$_add"
309 done
310 unset _strip _add _targ
314 ## Run "makefile" now unless MKTOP is set
317 set -ea
318 defines
319 [ -n "$MKTOP" ] || makefile "$@"