xref: /rk3399_ARM-atf/make_helpers/utilities.mk (revision 138a326cef1eb22cd893b93bb8f664f6be9d5cce)
14731c00bSChris Kay#
2c3273703SChris Kay# Copyright (c) 2024-2025, Arm Limited and Contributors. All rights reserved.
34731c00bSChris Kay#
44731c00bSChris Kay# SPDX-License-Identifier: BSD-3-Clause
54731c00bSChris Kay#
64731c00bSChris Kay
74731c00bSChris Kayspace :=
84731c00bSChris Kayspace := $(space) $(space)
94731c00bSChris Kaycomma := ,
104731c00bSChris Kay
114731c00bSChris Kaynull := �
1246a898f9SChris Kay
1346a898f9SChris Kaylparen := (
1437cd6184SChris Kayrparen := )
154731c00bSChris Kay
164731c00bSChris Kaycompat-path = $(subst $(space),$(null),$(1))
174731c00bSChris Kaydecompat-path = $(subst $(null), ,$(1))
184731c00bSChris Kay
194731c00bSChris Kayabsolute-path = $(call decompat-path,$(abspath $(call compat-path,$(1))))
204731c00bSChris Kayreal-path = $(call decompat-path,$(realpath $(call compat-path,$(1))))
214731c00bSChris Kay
224731c00bSChris Kayfile-name = $(call decompat-path,$(notdir $(call compat-path,$(1))))
234731c00bSChris Kaydirectory-name = $(call decompat-path,$(dir $(call compat-path,$(1))))
244731c00bSChris Kay
254731c00bSChris Kayescape-shell = '$(subst ','\'',$(1))'
263af4eb50SChris Kay
273af4eb50SChris Kay#
28a57b94ecSChris Kay# The grouped-target symbol. Grouped targets are not supported on versions of
29a57b94ecSChris Kay# GNU Make <= 4.2, which was most recently packaged with Ubuntu 20.04.
30a57b94ecSChris Kay#
31a57b94ecSChris Kay
32a57b94ecSChris Kay& := $(if $(filter grouped-target,$(.FEATURES)),&)
33a57b94ecSChris Kay
34a57b94ecSChris Kay#
353af4eb50SChris Kay# Upper-case a string value.
363af4eb50SChris Kay#
373af4eb50SChris Kay# Parameters:
383af4eb50SChris Kay#
393af4eb50SChris Kay#   - $(1): The string to upper-case.
403af4eb50SChris Kay#
413af4eb50SChris Kay# Example usage:
423af4eb50SChris Kay#
433af4eb50SChris Kay#     $(call uppercase,HeLlO wOrLd) # "HELLO WORLD"
443af4eb50SChris Kay#
453af4eb50SChris Kay
463af4eb50SChris Kayuppercase = $(shell echo $(call escape-shell,$(1)) | tr '[:lower:]' '[:upper:]')
473af4eb50SChris Kay
483af4eb50SChris Kay#
493af4eb50SChris Kay# Lower-case a string value.
503af4eb50SChris Kay#
513af4eb50SChris Kay# Parameters:
523af4eb50SChris Kay#
533af4eb50SChris Kay#   - $(1): The string to lower-case.
543af4eb50SChris Kay#
553af4eb50SChris Kay# Example usage:
563af4eb50SChris Kay#
573af4eb50SChris Kay#     $(call lowercase,HeLlO wOrLd) # "hello world"
583af4eb50SChris Kay#
593af4eb50SChris Kay
603af4eb50SChris Kaylowercase = $(shell echo $(call escape-shell,$(1)) | tr '[:upper:]' '[:lower:]')
610dfa3deaSChris Kay
620dfa3deaSChris Kay#
630dfa3deaSChris Kay# Determine the "truthiness" of a value.
640dfa3deaSChris Kay#
650dfa3deaSChris Kay# Parameters:
660dfa3deaSChris Kay#
670dfa3deaSChris Kay#   - $(1): The value to determine the truthiness of.
680dfa3deaSChris Kay#
690dfa3deaSChris Kay# A value is considered to be falsy if it is:
700dfa3deaSChris Kay#
710dfa3deaSChris Kay#   - empty, or
720dfa3deaSChris Kay#   - equal to "0", "N", "NO", "F" or "FALSE" after upper-casing.
730dfa3deaSChris Kay#
740dfa3deaSChris Kay# If the value is truthy then the value is returned as-is, otherwise no value
750dfa3deaSChris Kay# is returned.
760dfa3deaSChris Kay#
770dfa3deaSChris Kay# Example usage:
780dfa3deaSChris Kay#
790dfa3deaSChris Kay#     truthy := y
800dfa3deaSChris Kay#     truthy-bool := $(call bool,$(truthy)) # "y"
810dfa3deaSChris Kay#
820dfa3deaSChris Kay#     falsy := n
830dfa3deaSChris Kay#     falsy-bool := $(call bool,$(falsy)) # <empty>
840dfa3deaSChris Kay#
850dfa3deaSChris Kay
860dfa3deaSChris Kaybool = $(filter-out 0 n no f false,$(call lowercase,$(1)))
870dfa3deaSChris Kay
880dfa3deaSChris Kay#
890dfa3deaSChris Kay# Determine the "truthiness" of a value, returning 0 or 1.
900dfa3deaSChris Kay#
910dfa3deaSChris Kay# Parameters:
920dfa3deaSChris Kay#
930dfa3deaSChris Kay#   - $(1): The value to determine the truthiness of.
940dfa3deaSChris Kay#
950dfa3deaSChris Kay# A value is considered to be falsy if it is:
960dfa3deaSChris Kay#
970dfa3deaSChris Kay#   - empty, or
980dfa3deaSChris Kay#   - equal to "0", "N", "NO", "F" or "FALSE" after upper-casing.
990dfa3deaSChris Kay#
1000dfa3deaSChris Kay# If the value is truthy then the value is returned as-is, otherwise no value
1010dfa3deaSChris Kay# is returned.
1020dfa3deaSChris Kay#
1030dfa3deaSChris Kay# Example usage:
1040dfa3deaSChris Kay#
1050dfa3deaSChris Kay#     truthy := y
1060dfa3deaSChris Kay#     truthy-bool := $(call bool,$(truthy)) # "1"
1070dfa3deaSChris Kay#
1080dfa3deaSChris Kay#     falsy := n
1090dfa3deaSChris Kay#     falsy-bool := $(call bool,$(falsy)) # "0"
1100dfa3deaSChris Kay#
1110dfa3deaSChris Kay
1120dfa3deaSChris Kaybool-01 = $(if $(call bool,$(1)),1,0)
113d2867397SChris Kay
114d2867397SChris Kay#
115d2867397SChris Kay# Determine whether a variable is defined or not.
116d2867397SChris Kay#
117d2867397SChris Kay# Parameters:
118d2867397SChris Kay#
119d2867397SChris Kay#   - $(1): The variable to check.
120d2867397SChris Kay#
121d2867397SChris Kay# Example usage:
122d2867397SChris Kay#
123d2867397SChris Kay#     xyz-defined := $(call defined,xyz) # <empty>
124d2867397SChris Kay#
125d2867397SChris Kay#     xyz :=
126d2867397SChris Kay#     xyz-defined := $(call defined,xyz) # <non-empty>
127d2867397SChris Kay#
128d2867397SChris Kay#     xyz := hello
129d2867397SChris Kay#     xyz-defined := $(call defined,xyz) # <non-empty>
130d2867397SChris Kay#
131d2867397SChris Kay
132d2867397SChris Kaydefined = $(call bool,$(filter-out undefined,$(origin $(1))))
133c3273703SChris Kay
134c3273703SChris Kay#
1350fcee05fSHarrison Mutai# Extract include directories from compiler flags and convert them to absolute
1360fcee05fSHarrison Mutai# paths.
1370fcee05fSHarrison Mutai#
1380fcee05fSHarrison Mutai# Parameters:
1390fcee05fSHarrison Mutai#
1400fcee05fSHarrison Mutai#   - $(1): A list of C compiler flags.
1410fcee05fSHarrison Mutai#
1420fcee05fSHarrison Mutai# Example:
1430fcee05fSHarrison Mutai#
1440fcee05fSHarrison Mutai#     includes := $(call include-dirs, -nostdlib -Iinclude-dir) # /absolute/path/to/include-dir
1450fcee05fSHarrison Mutai#
1460fcee05fSHarrison Mutai
1470fcee05fSHarrison Mutaiinclude-dirs-pattern := $(call escape-shell,-I\s*("[^"]*"|'[^']*'|\S+))
1480fcee05fSHarrison Mutaiinclude-dirs = $(shell \
1490fcee05fSHarrison Mutai	printf '%s' $(call escape-shell,$1) | \
1500fcee05fSHarrison Mutai	perl -nle 'print $$1 while /'$(include-dirs-pattern)'/g' | \
1510fcee05fSHarrison Mutai	xargs realpath \
1520fcee05fSHarrison Mutai)
1530fcee05fSHarrison Mutai
1540fcee05fSHarrison Mutai#
155c3273703SChris Kay# Determine the path to a program.
156c3273703SChris Kay#
157c3273703SChris Kay# Parameters:
158c3273703SChris Kay#
159c3273703SChris Kay#   - $(1): The program to search for.
160c3273703SChris Kay#
161c3273703SChris Kay# Example usage:
162c3273703SChris Kay#
163c3273703SChris Kay#     path-to-gcc := $(call which,gcc) # "/usr/bin/gcc"
164c3273703SChris Kay#
165c3273703SChris Kay
166c3273703SChris Kaywhich = $(shell command -v $(call escape-shell,$(1)) 2>/dev/null)
1676eb35c60SChris Kay
1686eb35c60SChris Kay#
1696eb35c60SChris Kay# Temporarily bind variables while expanding text (scoped "with").
1706eb35c60SChris Kay#
1716eb35c60SChris Kay# Creates temporary variable bindings, expands a body of text with those
1726eb35c60SChris Kay# bindings in effect, then restores all affected variables to their previous
1736eb35c60SChris Kay# values and flavors (or undefines them if they did not exist). This provides a
1746eb35c60SChris Kay# "let"-style scope for variable assignments during text expansion.
1756eb35c60SChris Kay#
1766eb35c60SChris Kay# This function is modelled on the `let` function introduced in GNU Make 4.4:
1776eb35c60SChris Kay#
1786eb35c60SChris Kay#   https://www.gnu.org/software/make/manual/html_node/Let-Function.html
1796eb35c60SChris Kay#
1806eb35c60SChris Kay# Binding specifiers (space-separated in `$(1)`):
1816eb35c60SChris Kay#
1826eb35c60SChris Kay#   - l:name  Bind from the word list in `$(2)`. Bindings are applied
1836eb35c60SChris Kay#             left-to-right; the last `l` binding receives all remaining words
1846eb35c60SChris Kay#             (which may be empty). If there are more `l` names than words, the
1856eb35c60SChris Kay#             excess names are bound to empty values.
1866eb35c60SChris Kay#
1876eb35c60SChris Kay#   - p:name  Bind from subsequent call arguments (`$(2)`, `$(3)`, `$(4)`, ...),
1886eb35c60SChris Kay#             in left-to-right order. If `l` bindings are present, these
1896eb35c60SChris Kay#             arguments instead start from `$(3)`, following the word list.
1906eb35c60SChris Kay#
1916eb35c60SChris Kay#   -   name  Treated as `l:name`.
1926eb35c60SChris Kay#
1936eb35c60SChris Kay# Parameters:
1946eb35c60SChris Kay#
1956eb35c60SChris Kay#   - $(1): Space-separated binding specifiers.
1966eb35c60SChris Kay#   - $(2): The list value used by `l` bindings, if present.
1976eb35c60SChris Kay#   - $(2|3..N-1): Values for `p` bindings, in order (optional).
1986eb35c60SChris Kay#   - $(N): The text to expand with the temporary bindings active.
1996eb35c60SChris Kay#
2006eb35c60SChris Kay# Evaluation and restoration:
2016eb35c60SChris Kay#
2026eb35c60SChris Kay#   - All function arguments in Make are expanded at the call site. The text
2036eb35c60SChris Kay#     must therefore be escaped (write `$$` to produce a literal `$`), or
2046eb35c60SChris Kay#     supplied via `$(value ...)` to avoid premature expansion.
2056eb35c60SChris Kay#
2066eb35c60SChris Kay#   - Whitespace in `l`-style bindings is processed in terms of Make words; if
2076eb35c60SChris Kay#     you need to preserve whitespace then prefer `p` bindings.
2086eb35c60SChris Kay#
2096eb35c60SChris Kay#   - Variables are assigned as simple (`:=`) during the text expansion. After
2106eb35c60SChris Kay#     the text is expanded, each variable is restored to its previous state with
2116eb35c60SChris Kay#     its original flavor (simple or recursive), or undefined if it did not
2126eb35c60SChris Kay#     exist. Origins (e.g., command line, environment) are not preserved.
2136eb35c60SChris Kay#
2146eb35c60SChris Kay# Examples:
2156eb35c60SChris Kay#
2166eb35c60SChris Kay#   # Basic list destructuring (two names from a list):
2176eb35c60SChris Kay#   $(call with,foo bar,10 20,$$(foo) $$(bar)) # "10 20"
2186eb35c60SChris Kay#
2196eb35c60SChris Kay#   # Last list binding receives the remainder:
2206eb35c60SChris Kay#   $(call with,head tail,1 2 3 4,[$$(head)] [$$(tail)]) # "[1] [2 3 4]"
2216eb35c60SChris Kay#
2226eb35c60SChris Kay#   # Extra list names bind to empty values:
2236eb35c60SChris Kay#   $(call with,x y,9,x=<$$(x)> y=<$$(y)>) # "x=<9> y=<>"
2246eb35c60SChris Kay#
2256eb35c60SChris Kay#   # Parameter-only bindings start in `$(2)`:
2266eb35c60SChris Kay#   $(call with,p:x p:y,foo,bar,$$(x)-$$(y)) # "foo-bar"
2276eb35c60SChris Kay#
2286eb35c60SChris Kay#   # Parameter bindings start in `$(3)` when list bindings are specified:
2296eb35c60SChris Kay#   $(call with,l:lhs p:op l:rhs,10 20,+,$$(lhs) $$(op) $$(rhs)) # "10 + 20"
2306eb35c60SChris Kay#
2316eb35c60SChris Kay#   # Variables are restored after expansion, with flavor preserved:
2326eb35c60SChris Kay#
2336eb35c60SChris Kay#   x := outer-x
2346eb35c60SChris Kay#   y  = outer-y
2356eb35c60SChris Kay#
2366eb35c60SChris Kay#   $(info $(call with,x y,inner-x inner-y,$$(x) $$(y))) # "inner-x inner-y"
2376eb35c60SChris Kay#
2386eb35c60SChris Kay#   $(info $(x) ($(flavor x))) # "outer-x (simple)"
2396eb35c60SChris Kay#   $(info $(y) ($(flavor y))) # "outer-y (recursive)"
2406eb35c60SChris Kay#
2416eb35c60SChris Kay#   # Passing the text via `$(value ...)` to avoid `$$` escaping:
2426eb35c60SChris Kay#
2436eb35c60SChris Kay#   text = [$(head)] [$(tail)]
2446eb35c60SChris Kay#   $(call with,head tail,1 2 3 4,$(value text)) # "[1] [2 3 4]"
2456eb35c60SChris Kay#
2466eb35c60SChris Kay#   # Nested usage:
2476eb35c60SChris Kay#
2486eb35c60SChris Kay#   $(call with,a b,foo bar, \
2496eb35c60SChris Kay#       $$(call with,c d,baz qux,$$$$(a) $$$$(b) $$$$(c) $$$$(d)))
2506eb35c60SChris Kay#   # "foo bar baz qux"
2516eb35c60SChris Kay#
2526eb35c60SChris Kay
2536eb35c60SChris Kaywith = $(with.ns.push)$(eval $(value with.core))$(with.ns.pop)
2546eb35c60SChris Kay
2556eb35c60SChris Kaywith.ns = with.ns.$(with.ns.stack.head)
2566eb35c60SChris Kay
2576eb35c60SChris Kaywith.ns.stack :=
2586eb35c60SChris Kaywith.ns.stack.head = $(words $(with.ns.stack))
2596eb35c60SChris Kay
2606eb35c60SChris Kaywith.ns.push = $(eval with.ns.stack += $(with.ns.stack.head))
2616eb35c60SChris Kaywith.ns.pop = $($(with.ns).result)$(eval $(value with.ns.pop.1))
2626eb35c60SChris Kay
2636eb35c60SChris Kaydefine with.ns.pop.1 =
2646eb35c60SChris Kay        $(foreach variable,$(filter $(with.ns).%,$(.VARIABLES)),$\
2656eb35c60SChris Kay                $(eval undefine $(variable)))
2666eb35c60SChris Kay
2676eb35c60SChris Kay        with.ns.stack := $(wordlist 2,$(with.ns.stack.head),$(with.ns.stack))
2686eb35c60SChris Kayendef
2696eb35c60SChris Kay
2706eb35c60SChris Kaywith.bind.norm = $\
2716eb35c60SChris Kay        $(if $(findstring :,$(1)),$\
2726eb35c60SChris Kay                $(or $(filter l: p:,$(firstword $(subst :,: ,$(1)))),$\
2736eb35c60SChris Kay                        $(error invalid binding specifier: $(1)))$\
2746eb35c60SChris Kay                $(or $(filter-out %:,$(word 2,$(subst :,: ,$(1)))),$\
2756eb35c60SChris Kay                        $(error invalid binding specifier: $(1))),$\
2766eb35c60SChris Kay                l:$(1))
2776eb35c60SChris Kay
2786eb35c60SChris Kaywith.bind.kind = $(word 1,$(subst :, ,$(call with.bind.norm,$(1))))
2796eb35c60SChris Kaywith.bind.name = $(word 2,$(subst :, ,$(call with.bind.norm,$(1))))
2806eb35c60SChris Kay
2816eb35c60SChris Kaydefine with.core =
2826eb35c60SChris Kay        # Parse and record binding list/kinds/names from `$(1)`
2836eb35c60SChris Kay        $(with.ns).bind.list := $(foreach b,$(1),$(call with.bind.norm,$(b)))
2846eb35c60SChris Kay        $(with.ns).bind.names := $(foreach b,$(1),$(call with.bind.name,$(b)))
2856eb35c60SChris Kay        $(with.ns).bind.kinds := $(foreach b,$(1),$(call with.bind.kind,$(b)))
2866eb35c60SChris Kay
2876eb35c60SChris Kay        # Create a 1..=(N_bindings) list of binding indices
2886eb35c60SChris Kay        $(with.ns).bind.idx :=
2896eb35c60SChris Kay        $(with.ns).bind.next = $(words 0 $($(with.ns).bind.idx))
2906eb35c60SChris Kay
2916eb35c60SChris Kay        $(foreach bind,$($(with.ns).bind.list),$\
2926eb35c60SChris Kay                $(eval $(with.ns).bind.idx += $($(with.ns).bind.next)))
2936eb35c60SChris Kay
2946eb35c60SChris Kay        # Create a 2..=(N_arguments) list pointing to the text argument
2956eb35c60SChris Kay        $(with.ns).text.idx :=
2966eb35c60SChris Kay        $(with.ns).text.next = $(words 1 2 $($(with.ns).text.idx))
2976eb35c60SChris Kay        $(with.ns).text = $($($(with.ns).text.next))
2986eb35c60SChris Kay
2996eb35c60SChris Kay        # Snapshot original flavors/values of all variables to be overwritten
3006eb35c60SChris Kay        $(foreach bind.name,$($(with.ns).bind.names),$\
3016eb35c60SChris Kay        $(foreach bind.name.ns,$(with.ns).bind.names[$(bind.name)],$\
3026eb35c60SChris Kay                $(eval $(bind.name.ns).flavor := $(flavor $(bind.name))$\
3036eb35c60SChris Kay                $(eval $(bind.name.ns).value = $(value $(bind.name))))))
3046eb35c60SChris Kay
3056eb35c60SChris Kay        # Initialize per-kind buckets (e.g., `l`, `p`)
3066eb35c60SChris Kay        $(foreach bind.kind,$(sort $($(with.ns).bind.kinds)),$\
3076eb35c60SChris Kay                $(eval $(with.ns).bind.kind[$(bind.kind)] := ))
3086eb35c60SChris Kay
3096eb35c60SChris Kay        # Distribute binding indices into kind buckets
3106eb35c60SChris Kay        $(foreach bind.i,$($(with.ns).bind.idx),$\
3116eb35c60SChris Kay        $(foreach bind.kind,$(word $(bind.i),$($(with.ns).bind.kinds)),$\
3126eb35c60SChris Kay                $(eval $(with.ns).bind.kind[$(bind.kind)] += $(bind.i))))
3136eb35c60SChris Kay
3146eb35c60SChris Kay        # Per-kind setup (e.g., to set up index vectors before binding)
3156eb35c60SChris Kay        $(foreach bind.kind,$(sort $($(with.ns).bind.kinds)),$\
3166eb35c60SChris Kay        $(foreach bind.kind.ns,$(with.ns).bind.kind[$(bind.kind)],$\
3176eb35c60SChris Kay                $(eval $(value with.core.$(bind.kind)))))
3186eb35c60SChris Kay
3196eb35c60SChris Kay        # Perform binding from left to right
3206eb35c60SChris Kay        $(foreach bind.i,$($(with.ns).bind.idx),$\
3216eb35c60SChris Kay        $(foreach bind.name,$(word $(bind.i),$($(with.ns).bind.names)),$\
3226eb35c60SChris Kay        $(foreach bind.kind,$(word $(bind.i),$($(with.ns).bind.kinds)),$\
3236eb35c60SChris Kay        $(foreach bind.kind.ns,$(with.ns).bind.kind[$(bind.kind)],$\
3246eb35c60SChris Kay                $(eval $(value with.core.$(bind.kind).bind))))))
3256eb35c60SChris Kay
3266eb35c60SChris Kay        # Capture the expansion result from the current text pointer
3276eb35c60SChris Kay        $(eval $(with.ns).result := $($(with.ns).text))
3286eb35c60SChris Kay
3296eb35c60SChris Kay        # Restore originals (flavor/value) or undefine if previously absent
3306eb35c60SChris Kay        $(foreach bind.name,$($(with.ns).bind.names),$\
3316eb35c60SChris Kay        $(foreach bind.name.ns,$(with.ns).bind.names[$(bind.name)],$\
3326eb35c60SChris Kay                $(eval $(value with.core.restore))))
3336eb35c60SChris Kayendef
3346eb35c60SChris Kay
3356eb35c60SChris Kaydefine with.core.l =
3366eb35c60SChris Kay        # Create a 1..=(N_largs) list capturing the unbound `l` words
3376eb35c60SChris Kay        $(bind.kind.ns).words.idx :=
3386eb35c60SChris Kay        $(bind.kind.ns).words.next = $(words 1 $($(bind.kind.ns).words.idx))
3396eb35c60SChris Kay        $(bind.kind.ns).words = $\
3406eb35c60SChris Kay                $(wordlist $($(bind.kind.ns).words.next),$(words $(2)),$(2))
3416eb35c60SChris Kay
3426eb35c60SChris Kay        # Increment the text pointer
3436eb35c60SChris Kay        $(with.ns).text.idx += $($(with.ns).text.next)
3446eb35c60SChris Kayendef
3456eb35c60SChris Kay
3466eb35c60SChris Kaydefine with.core.l.bind =
3476eb35c60SChris Kay        # Bind this name to the next unbound word
3486eb35c60SChris Kay        $(bind.name) := $(firstword $($(bind.kind.ns).words))
3496eb35c60SChris Kay
3506eb35c60SChris Kay        # If this is the last `l` binding, absorb the remaining words
3516eb35c60SChris Kay        ifeq ($($(bind.kind.ns).words.next),$(words $($(bind.kind.ns))))
3526eb35c60SChris Kay                $(bind.name) := $($(bind.kind.ns).words)
3536eb35c60SChris Kay        endif
3546eb35c60SChris Kay
3556eb35c60SChris Kay        # Nudge the word pointer forward
3566eb35c60SChris Kay        $(bind.kind.ns).words.idx += $($(bind.kind.ns).words.next)
3576eb35c60SChris Kayendef
3586eb35c60SChris Kay
3596eb35c60SChris Kaydefine with.core.p =
3606eb35c60SChris Kay        # Compute the parameter index that `p` bindings start at
3616eb35c60SChris Kay        $(bind.kind.ns).param.offset := 1 2
3626eb35c60SChris Kay
3636eb35c60SChris Kay        # When `l` bindings are present, `p` values shift right
3646eb35c60SChris Kay        ifneq ($(filter l,$($(with.ns).bind.kinds)),)
3656eb35c60SChris Kay                $(bind.kind.ns).param.offset += 3
3666eb35c60SChris Kay        endif
3676eb35c60SChris Kay
3686eb35c60SChris Kay        # Create an N_poff..=N_pargs list capturing the unbound `p` arguments
3696eb35c60SChris Kay        $(bind.kind.ns).param.idx :=
3706eb35c60SChris Kay        $(bind.kind.ns).param.next = $\
3716eb35c60SChris Kay                $(words $($(bind.kind.ns).param.offset) $\
3726eb35c60SChris Kay                        $($(bind.kind.ns).param.idx))
3736eb35c60SChris Kay        $(bind.kind.ns).param = $($(lastword $($(bind.kind.ns).param.idx)))
3746eb35c60SChris Kayendef
3756eb35c60SChris Kay
3766eb35c60SChris Kaydefine with.core.p.bind =
3776eb35c60SChris Kay        # Mark the next parameter as bound
3786eb35c60SChris Kay        $(bind.kind.ns).param.idx += $($(bind.kind.ns).param.next)
3796eb35c60SChris Kay
3806eb35c60SChris Kay        # Bind this name to the next unbound argument
3816eb35c60SChris Kay        $(bind.name) := $($(bind.kind.ns).param)
3826eb35c60SChris Kay
3836eb35c60SChris Kay        # Increment the text pointer
3846eb35c60SChris Kay        $(with.ns).text.idx += $($(with.ns).text.next)
3856eb35c60SChris Kayendef
3866eb35c60SChris Kay
3876eb35c60SChris Kaydefine with.core.restore =
3886eb35c60SChris Kay        ifeq ($($(bind.name.ns).flavor),simple)
3896eb35c60SChris Kay                $(eval $(bind.name) := $(value $(bind.name.ns).value))
3906eb35c60SChris Kay        else ifeq ($($(bind.name.ns).flavor),recursive)
3916eb35c60SChris Kay                $(eval $(bind.name) = $(value $(bind.name.ns).value))
3926eb35c60SChris Kay        else ifeq ($($(bind.name.ns).flavor),undefined)
3936eb35c60SChris Kay                undefine $(bind.name)
3946eb35c60SChris Kay        endif
3956eb35c60SChris Kayendef
39610cb835fSChris Kay
39710cb835fSChris Kay#
39810cb835fSChris Kay# Quote a string for safe use as a shell word.
39910cb835fSChris Kay#
40010cb835fSChris Kay# Takes the input string `$(1)` and escapes any single quotes it contains so
40110cb835fSChris Kay# that the result can be safely used as a literal shell argument. The output is
40210cb835fSChris Kay# wrapped in single quotes to ensure that whitespace and special characters are
40310cb835fSChris Kay# preserved exactly when passed to the shell.
40410cb835fSChris Kay#
40510cb835fSChris Kay# This function is useful when constructing shell commands dynamically, since it
40610cb835fSChris Kay# guarantees that arbitrary values are quoted correctly and will not be
40710cb835fSChris Kay# misinterpreted by the shell.
40810cb835fSChris Kay#
40910cb835fSChris Kay# Parameters:
41010cb835fSChris Kay#
41110cb835fSChris Kay#   - $(1): The string to quote for safe shell usage.
41210cb835fSChris Kay#
41310cb835fSChris Kay# Examples:
41410cb835fSChris Kay#
41510cb835fSChris Kay#   $(call shell-quote,foo) # "'foo'"
41610cb835fSChris Kay#   $(call shell-quote,bar baz) # "'bar baz'"
41710cb835fSChris Kay#   $(call shell-quote,foo 'bar baz' qux) # "'foo '\''bar baz'\'' qux'"
41810cb835fSChris Kay#
41910cb835fSChris Kay
42010cb835fSChris Kayshell-quote = '$(subst ','\'',$(1))'
4215980fa7cSChris Kay
4225980fa7cSChris Kay#
4235980fa7cSChris Kay# Parse a shell fragment and extract the N-th word.
4245980fa7cSChris Kay#
4255980fa7cSChris Kay# Parses the shell fragment given by `$(2)` using the shell's word-splitting and
4265980fa7cSChris Kay# quoting rules, then prints the `$(1)`-th shell word in the result. If the
4275980fa7cSChris Kay# index is out of range then this function evaluates to an empty string.
4285980fa7cSChris Kay#
4295980fa7cSChris Kay# This function is useful when working with lists that may contain whitespace or
4305980fa7cSChris Kay# quoted values, since it relies on the shell to do the parsing rather than
4315980fa7cSChris Kay# Make's own word functions. Whitespace is preserved in the return value.
4325980fa7cSChris Kay#
4335980fa7cSChris Kay# Parameters:
4345980fa7cSChris Kay#
4355980fa7cSChris Kay#   - $(1): The 1-based index of the word to extract.
4365980fa7cSChris Kay#   - $(2): The shell fragment to parse.
4375980fa7cSChris Kay#
4385980fa7cSChris Kay# Example usage:
4395980fa7cSChris Kay#
4405980fa7cSChris Kay#       $(call shell-word,1,foo 'bar baz' qux) # "foo"
4415980fa7cSChris Kay#       $(call shell-word,2,foo 'bar baz' qux) # "bar baz"
4425980fa7cSChris Kay#       $(call shell-word,3,foo 'bar baz' qux) # "qux"
4435980fa7cSChris Kay#       $(call shell-word,4,foo 'bar baz' qux) # <empty>
4445980fa7cSChris Kay#
4455980fa7cSChris Kay
4465980fa7cSChris Kayshell-word = $(shell $(shell-word.sh))
4475980fa7cSChris Kay
4485980fa7cSChris Kaydefine shell-word.sh =
4495980fa7cSChris Kay        set -Cefu -- '' $(2);
4505980fa7cSChris Kay
4515980fa7cSChris Kay        n=$(call shell-quote,$(1));
4525980fa7cSChris Kay
4535980fa7cSChris Kay        shift "$${n}";
4545980fa7cSChris Kay        printf '%s' "$${1:-}";
4555980fa7cSChris Kayendef
4568165d826SChris Kay
4578165d826SChris Kay#
4588165d826SChris Kay# Parse a shell fragment and count the number of shell words.
4598165d826SChris Kay#
4608165d826SChris Kay# Parses the shell fragment given by `$(1)` using the shell's word-splitting and
4618165d826SChris Kay# quoting rules, then prints the total number of words in the result.
4628165d826SChris Kay#
4638165d826SChris Kay# This function is useful when working with lists that may contain whitespace or
4648165d826SChris Kay# quoted values, since it relies on the shell to do the parsing rather than
4658165d826SChris Kay# Make's own word functions.
4668165d826SChris Kay#
4678165d826SChris Kay# Parameters:
4688165d826SChris Kay#
4698165d826SChris Kay#   - $(1): The shell fragment to parse.
4708165d826SChris Kay#
4718165d826SChris Kay# Example usage:
4728165d826SChris Kay#
4738165d826SChris Kay#       $(call shell-words,) # "0"
4748165d826SChris Kay#       $(call shell-words,foo) # "1"
4758165d826SChris Kay#       $(call shell-words,foo bar baz) # "3"
4768165d826SChris Kay#       $(call shell-words,foo 'bar baz' qux) # "3"
4778165d826SChris Kay#
4788165d826SChris Kay
4798165d826SChris Kayshell-words = $(shell $(shell-words.sh))
4808165d826SChris Kayshell-words.sh = set -Cefu -- $(1); printf '%s' "$$\#";
4819a782d40SChris Kay
4829a782d40SChris Kay#
4839a782d40SChris Kay# Parse a shell fragment and extract a sequence of shell words.
4849a782d40SChris Kay#
4859a782d40SChris Kay# Parses the shell fragment given by `$(1)` using the shell's word-splitting and
4869a782d40SChris Kay# quoting rules, then extracts the words from index `$(2)` up to but not
4879a782d40SChris Kay# including index `$(3)`. Each extracted shell word is returned sanitized for
4889a782d40SChris Kay# safe use in the shell.
4899a782d40SChris Kay#
4909a782d40SChris Kay# If `$(3)` is omitted, it defaults to one past the total number of words in the
4919a782d40SChris Kay# string, allowing you to express "all words starting from `$(2)`".
4929a782d40SChris Kay#
4939a782d40SChris Kay# This function is useful for safely selecting and passing subsequences of
4949a782d40SChris Kay# shell-parsed arguments into other shell commands, ensuring correct handling
4959a782d40SChris Kay# of whitespace and special characters.
4969a782d40SChris Kay#
4979a782d40SChris Kay# Parameters:
4989a782d40SChris Kay#
4999a782d40SChris Kay#   - $(1): The shell fragment to parse.
5009a782d40SChris Kay#   - $(2): The 1-based start index of the slice (default: 1).
5013dc69bcbSChris Kay#   - $(3): The 1-based end index of the slice (exclusive, optional).
5029a782d40SChris Kay#
5039a782d40SChris Kay# Example usage:
5049a782d40SChris Kay#
5059a782d40SChris Kay#       $(call shell-slice,foo 'bar baz' qux)     # "'foo' 'bar baz' 'qux'"
5069a782d40SChris Kay#       $(call shell-slice,foo 'bar baz' qux,1,3) # "'foo' 'bar baz'"
5079a782d40SChris Kay#       $(call shell-slice,foo 'bar baz' qux,2)   # "'bar baz' 'qux'"
5089a782d40SChris Kay#       $(call shell-slice,foo 'bar baz' qux,2,4) # "'bar baz' 'qux'"
5099a782d40SChris Kay#       $(call shell-slice,foo 'bar baz' qux,2,5) # "'bar baz' 'qux'"
5109a782d40SChris Kay#
5119a782d40SChris Kay
5129a782d40SChris Kayshell-slice = $(shell $(shell-slice.sh))
5139a782d40SChris Kay
5149a782d40SChris Kaydefine shell-slice.sh =
5159a782d40SChris Kay        set -Cefu -- $(1);
5169a782d40SChris Kay
5179a782d40SChris Kay        n=$(if $(2),$(call shell-quote,$(2)),1);
5183dc69bcbSChris Kay        m=$(if $(3),$(call shell-quote,$(3)),$$(($$# + 1)));
5199a782d40SChris Kay
5209a782d40SChris Kay        printf '%s\n' "$$@" $\
5213dc69bcbSChris Kay                | sed -n "$${n},$${m}{ $${m}!p }; $${m}q" $\
5229a782d40SChris Kay                | sed "s/'/'\\\\''/g; s/^/'/; s/\$$/'/";
5239a782d40SChris Kayendef
524a75ab9a7SChris Kay
525a75ab9a7SChris Kay#
526a75ab9a7SChris Kay# Join shell words with a custom delimiter.
527a75ab9a7SChris Kay#
528a75ab9a7SChris Kay# Parses the shell fragment given by `$(1)` using the shell's word-splitting and
529a75ab9a7SChris Kay# quoting rules, then joins the resulting words together with the delimiter
530a75ab9a7SChris Kay# specified by `$(2)`. If no delimiter is provided, no delimiter is used.
531a75ab9a7SChris Kay#
532a75ab9a7SChris Kay# This function is useful for safely rejoining a sequence of shell-parsed
533a75ab9a7SChris Kay# arguments into a single string with controlled separators, ensuring that
534a75ab9a7SChris Kay# whitespace and quoting are preserved correctly.
535a75ab9a7SChris Kay#
536a75ab9a7SChris Kay# Parameters:
537a75ab9a7SChris Kay#
538a75ab9a7SChris Kay#   - $(1): The shell fragment to parse and join.
539a75ab9a7SChris Kay#   - $(2): The delimiter to insert between words (optional).
540a75ab9a7SChris Kay#
541a75ab9a7SChris Kay# Example usage:
542a75ab9a7SChris Kay#
543a75ab9a7SChris Kay#       $(call shell-join,foo 'bar baz' qux) # "foobar bazqux"
544a75ab9a7SChris Kay#       $(call shell-join,foo 'bar baz' qux,:) # "foo:bar baz:qux"
545a75ab9a7SChris Kay#       $(call shell-join,foo 'bar baz' qux,;) # "foo;bar baz;qux"
546a75ab9a7SChris Kay#
547a75ab9a7SChris Kay
548a75ab9a7SChris Kayshell-join = $(shell $(shell-join.sh))
549a75ab9a7SChris Kay
550a75ab9a7SChris Kaydefine shell-join.sh =
551a75ab9a7SChris Kay        set -Cefu -- $(1);
552a75ab9a7SChris Kay
553a75ab9a7SChris Kay        delimiter=$(call shell-quote,$(2));
554a75ab9a7SChris Kay
555a75ab9a7SChris Kay        printf '%s' "$${1:-}";
556a75ab9a7SChris Kay        shift 1;
557a75ab9a7SChris Kay
558a75ab9a7SChris Kay        while [ "$$#" -gt 0 ]; do
559a75ab9a7SChris Kay                printf '%s%s' "$${delimiter}" "$${1}";
560a75ab9a7SChris Kay                shift 1;
561a75ab9a7SChris Kay        done
562a75ab9a7SChris Kayendef
56337cd6184SChris Kay
56437cd6184SChris Kay#
56537cd6184SChris Kay# Apply a function to each shell word in a fragment.
56637cd6184SChris Kay#
56737cd6184SChris Kay# Parses the shell fragment given by `$(2)` into words using the shell's
56837cd6184SChris Kay# word-splitting and quoting rules. For each word, the function `$(1)` is
5691d5ae1e5SChris Kay# invoked with the word as its first argument and the 1-based index of the word
5701d5ae1e5SChris Kay# as its second argument. The results are concatenated and returned, separated
5711d5ae1e5SChris Kay# by whitespace.
57237cd6184SChris Kay#
57337cd6184SChris Kay# This function is useful when you want to process each shell word from a
57437cd6184SChris Kay# fragment through another function, while preserving correct handling of
57537cd6184SChris Kay# whitespace and quoting.
57637cd6184SChris Kay#
57737cd6184SChris Kay# Parameters:
57837cd6184SChris Kay#
57937cd6184SChris Kay#   - $(1): The function to apply to each word.
58037cd6184SChris Kay#   - $(2): The shell fragment to parse into words.
58137cd6184SChris Kay#
58237cd6184SChris Kay# Example usage:
58337cd6184SChris Kay#
58437cd6184SChris Kay#       $(call shell-map,words,foo 'bar baz' qux) # "1 2 1"
58537cd6184SChris Kay#       $(call shell-map,uppercase,foo 'bar baz' qux) # "FOO BAR BAZ QUX"
58637cd6184SChris Kay#
58737cd6184SChris Kay#       shout = $(1)!
58837cd6184SChris Kay#       $(call shell-map,shout,foo 'bar baz' qux) # "foo! bar baz! qux!"
58937cd6184SChris Kay#
59037cd6184SChris Kay#       make-binary = /bin/$(1)
59137cd6184SChris Kay#       $(call shell-map,make-binary,cp "ls" 'sh') # "/bin/cp /bin/ls /bin/sh"
59237cd6184SChris Kay#
5931d5ae1e5SChris Kay#       index-label = $(1):$(2)
5941d5ae1e5SChris Kay#       $(call shell-map,index-label,foo 'bar baz') # "foo:1 bar baz:2"
5951d5ae1e5SChris Kay#
59637cd6184SChris Kay
59737cd6184SChris Kayshell-map = $(call with,,$(shell $(shell-map.sh)))
59837cd6184SChris Kay
59937cd6184SChris Kaydefine shell-map.sh =
60037cd6184SChris Kay        set -Cefu -- $(2);
60137cd6184SChris Kay
60237cd6184SChris Kay        function=$(call shell-quote,$(1));
6031d5ae1e5SChris Kay        index=1;
60437cd6184SChris Kay
60537cd6184SChris Kay        for argument in "$$@"; do
60637cd6184SChris Kay                sanitized=$$(printf '%s' "$${argument}" $\
607*cfc2d766SChris Kay                        | sed -e 's/[$$]/$$$$/g; s/,/$${comma}/g' $\
608*cfc2d766SChris Kay                                -e 's/(/$${lparen}/g; s/)/$${rparen}/g');
60937cd6184SChris Kay
6101d5ae1e5SChris Kay                printf '$$(call %s,%s,%s)\n' $\
6111d5ae1e5SChris Kay                        "$${function}" "$${sanitized}" "$${index}";
6121d5ae1e5SChris Kay
6131d5ae1e5SChris Kay                index=$$((index + 1));
61437cd6184SChris Kay        done
61537cd6184SChris Kayendef
616a72154ceSChris Kay
617a72154ceSChris Kay#
618a72154ceSChris Kay# Resolve a program name or shell fragment to a safely quoted shell command.
619a72154ceSChris Kay#
620a72154ceSChris Kay# Attempts to locate the program given by `$(1)` on the system `PATH`. If the
621a72154ceSChris Kay# program is found, its name is returned wrapped in single quotes so it can be
622a72154ceSChris Kay# used safely in a shell command. If the program cannot be found, then the
623a72154ceSChris Kay# argument is instead parsed as a shell fragment and returned as a sanitized
624a72154ceSChris Kay# sequence of words, ensuring whitespace and quoting are preserved correctly.
625a72154ceSChris Kay#
626a72154ceSChris Kay# This function is useful when dynamically constructing shell command lines
627a72154ceSChris Kay# that may include either well-known executables or arbitrary user-supplied
628a72154ceSChris Kay# fragments. It guarantees that the result is safe to embed in shell commands,
629a72154ceSChris Kay# regardless of whether it resolves to a `PATH` entry or a literal fragment.
630a72154ceSChris Kay#
631a72154ceSChris Kay# Parameters:
632a72154ceSChris Kay#
633a72154ceSChris Kay#   - $(1): The program name or shell fragment to resolve.
634a72154ceSChris Kay#
635a72154ceSChris Kay# Example usage:
636a72154ceSChris Kay#
637a72154ceSChris Kay#       $(call shell-program,sh)    # "'sh'"
638a72154ceSChris Kay#       $(call shell-program,sh -c) # "'sh' '-c'"
639a72154ceSChris Kay#
640a72154ceSChris Kay#       # If the program exists and is executable:
641a72154ceSChris Kay#
642a72154ceSChris Kay#       $(call shell-program,/foo bar/sh)      # "'/foo bar/sh'"
643a72154ceSChris Kay#       $(call shell-program,"/foo bar/sh" -c) # "'/foo bar/sh' '-c'"
644a72154ceSChris Kay#
645a72154ceSChris Kay#       # If the program does not exist or is not executable:
646a72154ceSChris Kay#
647a72154ceSChris Kay#       $(call shell-program,/foo bar/sh)    # "'/foo' 'bar/sh'"
648a72154ceSChris Kay#       $(call shell-program,/foo bar/sh -c) # "'/foo' 'bar/sh' '-c'"
649a72154ceSChris Kay#
650a72154ceSChris Kay
651a72154ceSChris Kayshell-program = $\
652a72154ceSChris Kay        $(if $(call which,$(1)),$\
653a72154ceSChris Kay                $(call shell-quote,$(1)),$\
654a72154ceSChris Kay                $(call shell-slice,$(1)))
655