Skip to content

Commit 1cb013e

Browse files
committed
better, simpler argument handling strategy that handles all args including builtin Make args
1 parent 97d5d4c commit 1cb013e

File tree

1 file changed

+28
-31
lines changed

1 file changed

+28
-31
lines changed

runfile.sh

Lines changed: 28 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -169,41 +169,38 @@ EOF
169169
# Done with temporary Makefile construction.
170170
# ::::::::::::::::::::::::::::::::::::::::::
171171
172-
# Handle positional command arguments if any were provided:
172+
# Runfile command argument handling:
173173
if [[ " $* " != *' --print-makefile '* ]] \
174174
&& [[ " $* " != *' --create-makefile '* ]] \
175175
&& [[ " $* " != *' --overwrite-makefile '* ]]
176+
# If outputting makefile, skip this section.
176177
then
177-
buffer="$( cat "${makefile}" )"
178-
# Cases where no named or positional arguments were provided:
179-
# Replace $(var) $(@) $(1) $(2) etc. in script with empty backtick expression ``.
180-
# This seems odd, but it allows tasks like: [[ -n $(1) ]] && echo "$1"
181-
# to work whether or not $1 positional arg was provided. Without it, make will
182-
# error out due to the script being interpreted as [[ -n ]]. With standard
183-
# quotes instead of backticks, expressions like echo "$1" will inadvertently
184-
# print the quotes.
185-
if ! (( ${#cmd_args[@]} ))
186-
then
187-
# Case where no named arguments were provided: replace $(abc) $(xyz) etc.
188-
# Note: There must be at least one lowercase letter [a-z] in these matches,
189-
# because otherwise we'd replace built in Make vars like $(MAKEFILE_LIST)
190-
# which we want to leave alone.
191-
buffer="$( echo "${buffer}" | sed -E 's!\$\([a-zA-Z0-9_]*[a-z][a-zA-Z0-9_]*\)!``!g' )"
192-
fi
193-
if ! (( ${#pos_args[@]} ))
194-
then
195-
# Case where no positional arguments were provided: replace $(@) $(1) $(2) etc.
196-
buffer="${buffer//\$([0-9@])/\`\`}"
197-
else
198-
# Replace $(@) in script with concatenation of all positional args:
199-
buffer="${buffer//\$(@)/${pos_args[*]}}"
200-
# Replace $(1) $(2) etc. in script with each individual positional arg:
201-
for arg in "${pos_args[@]}"
202-
do
203-
(( pos_arg_idx++ )) || true
204-
buffer="${buffer//\$(${pos_arg_idx})/${arg}}"
205-
done
206-
fi
178+
buffer="$(
179+
sed -E 's!(\$\((@|[0-9]+|[a-zA-Z][a-zA-Z0-9_]*)\))!\`printf '"'%s' '\1'"'\`!g' \
180+
"${makefile}"
181+
)"
182+
# Wrap all Runfile argument patterns in printf, eg. $(@) -> `printf '%s' '$(@)'`
183+
# This resolves issues with Make arg-handling behavior when args are omitted.
184+
# For example: [[ -n $(arg) ]] && echo "${arg}"
185+
# This will error if $(arg) is not specified, because Make executes: [[ -n ]]
186+
# With printf-wrapping: [[ -n `printf '%s' '$(arg)'` ]] && echo "${arg}"
187+
# Now if $(arg) is not specified, Make executes: [[ -n `printf '%s' ''` ]]
188+
# which is perfectly valid. We can't simply wrap args in quotes, because then
189+
# the quotes would be included when args are interpolated within strings. eg.
190+
# "hello: $(world)" -> "hello: '$(world)'" -> "hello: ''" (unwanted quotes)
191+
192+
# Replace $(@) in script with concatenation of all positional args:
193+
buffer="${buffer//\$(@)/${pos_args[*]}}"
194+
# Note: Perform this replacement even if no positional args were provided,
195+
# because otherwise default Make behavior interpolates ${cmd} in place of $(@).
196+
197+
# Replace $(1) $(2) etc. in script with each individual positional arg:
198+
for arg in "${pos_args[@]}"
199+
do
200+
(( pos_arg_idx++ )) || true
201+
buffer="${buffer//\$(${pos_arg_idx})/${arg}}"
202+
done
203+
207204
# Write buffer back to temporary makefile:
208205
echo "${buffer}" > "${makefile}"
209206
fi

0 commit comments

Comments
 (0)