@@ -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