xref: /rk3399_ARM-atf/make_helpers/utilities.mk (revision 10cb835fcab2cf4a909cf829de6671e51bc1bab7)
1#
2# Copyright (c) 2024-2025, Arm Limited and Contributors. All rights reserved.
3#
4# SPDX-License-Identifier: BSD-3-Clause
5#
6
7space :=
8space := $(space) $(space)
9comma := ,
10
11null := �
12
13compat-path = $(subst $(space),$(null),$(1))
14decompat-path = $(subst $(null), ,$(1))
15
16absolute-path = $(call decompat-path,$(abspath $(call compat-path,$(1))))
17real-path = $(call decompat-path,$(realpath $(call compat-path,$(1))))
18
19file-name = $(call decompat-path,$(notdir $(call compat-path,$(1))))
20directory-name = $(call decompat-path,$(dir $(call compat-path,$(1))))
21
22escape-shell = '$(subst ','\'',$(1))'
23
24#
25# The grouped-target symbol. Grouped targets are not supported on versions of
26# GNU Make <= 4.2, which was most recently packaged with Ubuntu 20.04.
27#
28
29& := $(if $(filter grouped-target,$(.FEATURES)),&)
30
31#
32# Upper-case a string value.
33#
34# Parameters:
35#
36#   - $(1): The string to upper-case.
37#
38# Example usage:
39#
40#     $(call uppercase,HeLlO wOrLd) # "HELLO WORLD"
41#
42
43uppercase = $(shell echo $(call escape-shell,$(1)) | tr '[:lower:]' '[:upper:]')
44
45#
46# Lower-case a string value.
47#
48# Parameters:
49#
50#   - $(1): The string to lower-case.
51#
52# Example usage:
53#
54#     $(call lowercase,HeLlO wOrLd) # "hello world"
55#
56
57lowercase = $(shell echo $(call escape-shell,$(1)) | tr '[:upper:]' '[:lower:]')
58
59#
60# Determine the "truthiness" of a value.
61#
62# Parameters:
63#
64#   - $(1): The value to determine the truthiness of.
65#
66# A value is considered to be falsy if it is:
67#
68#   - empty, or
69#   - equal to "0", "N", "NO", "F" or "FALSE" after upper-casing.
70#
71# If the value is truthy then the value is returned as-is, otherwise no value
72# is returned.
73#
74# Example usage:
75#
76#     truthy := y
77#     truthy-bool := $(call bool,$(truthy)) # "y"
78#
79#     falsy := n
80#     falsy-bool := $(call bool,$(falsy)) # <empty>
81#
82
83bool = $(filter-out 0 n no f false,$(call lowercase,$(1)))
84
85#
86# Determine the "truthiness" of a value, returning 0 or 1.
87#
88# Parameters:
89#
90#   - $(1): The value to determine the truthiness of.
91#
92# A value is considered to be falsy if it is:
93#
94#   - empty, or
95#   - equal to "0", "N", "NO", "F" or "FALSE" after upper-casing.
96#
97# If the value is truthy then the value is returned as-is, otherwise no value
98# is returned.
99#
100# Example usage:
101#
102#     truthy := y
103#     truthy-bool := $(call bool,$(truthy)) # "1"
104#
105#     falsy := n
106#     falsy-bool := $(call bool,$(falsy)) # "0"
107#
108
109bool-01 = $(if $(call bool,$(1)),1,0)
110
111#
112# Determine whether a variable is defined or not.
113#
114# Parameters:
115#
116#   - $(1): The variable to check.
117#
118# Example usage:
119#
120#     xyz-defined := $(call defined,xyz) # <empty>
121#
122#     xyz :=
123#     xyz-defined := $(call defined,xyz) # <non-empty>
124#
125#     xyz := hello
126#     xyz-defined := $(call defined,xyz) # <non-empty>
127#
128
129defined = $(call bool,$(filter-out undefined,$(origin $(1))))
130
131#
132# Extract include directories from compiler flags and convert them to absolute
133# paths.
134#
135# Parameters:
136#
137#   - $(1): A list of C compiler flags.
138#
139# Example:
140#
141#     includes := $(call include-dirs, -nostdlib -Iinclude-dir) # /absolute/path/to/include-dir
142#
143
144include-dirs-pattern := $(call escape-shell,-I\s*("[^"]*"|'[^']*'|\S+))
145include-dirs = $(shell \
146	printf '%s' $(call escape-shell,$1) | \
147	perl -nle 'print $$1 while /'$(include-dirs-pattern)'/g' | \
148	xargs realpath \
149)
150
151#
152# Determine the path to a program.
153#
154# Parameters:
155#
156#   - $(1): The program to search for.
157#
158# Example usage:
159#
160#     path-to-gcc := $(call which,gcc) # "/usr/bin/gcc"
161#
162
163which = $(shell command -v $(call escape-shell,$(1)) 2>/dev/null)
164
165#
166# Temporarily bind variables while expanding text (scoped "with").
167#
168# Creates temporary variable bindings, expands a body of text with those
169# bindings in effect, then restores all affected variables to their previous
170# values and flavors (or undefines them if they did not exist). This provides a
171# "let"-style scope for variable assignments during text expansion.
172#
173# This function is modelled on the `let` function introduced in GNU Make 4.4:
174#
175#   https://www.gnu.org/software/make/manual/html_node/Let-Function.html
176#
177# Binding specifiers (space-separated in `$(1)`):
178#
179#   - l:name  Bind from the word list in `$(2)`. Bindings are applied
180#             left-to-right; the last `l` binding receives all remaining words
181#             (which may be empty). If there are more `l` names than words, the
182#             excess names are bound to empty values.
183#
184#   - p:name  Bind from subsequent call arguments (`$(2)`, `$(3)`, `$(4)`, ...),
185#             in left-to-right order. If `l` bindings are present, these
186#             arguments instead start from `$(3)`, following the word list.
187#
188#   -   name  Treated as `l:name`.
189#
190# Parameters:
191#
192#   - $(1): Space-separated binding specifiers.
193#   - $(2): The list value used by `l` bindings, if present.
194#   - $(2|3..N-1): Values for `p` bindings, in order (optional).
195#   - $(N): The text to expand with the temporary bindings active.
196#
197# Evaluation and restoration:
198#
199#   - All function arguments in Make are expanded at the call site. The text
200#     must therefore be escaped (write `$$` to produce a literal `$`), or
201#     supplied via `$(value ...)` to avoid premature expansion.
202#
203#   - Whitespace in `l`-style bindings is processed in terms of Make words; if
204#     you need to preserve whitespace then prefer `p` bindings.
205#
206#   - Variables are assigned as simple (`:=`) during the text expansion. After
207#     the text is expanded, each variable is restored to its previous state with
208#     its original flavor (simple or recursive), or undefined if it did not
209#     exist. Origins (e.g., command line, environment) are not preserved.
210#
211# Examples:
212#
213#   # Basic list destructuring (two names from a list):
214#   $(call with,foo bar,10 20,$$(foo) $$(bar)) # "10 20"
215#
216#   # Last list binding receives the remainder:
217#   $(call with,head tail,1 2 3 4,[$$(head)] [$$(tail)]) # "[1] [2 3 4]"
218#
219#   # Extra list names bind to empty values:
220#   $(call with,x y,9,x=<$$(x)> y=<$$(y)>) # "x=<9> y=<>"
221#
222#   # Parameter-only bindings start in `$(2)`:
223#   $(call with,p:x p:y,foo,bar,$$(x)-$$(y)) # "foo-bar"
224#
225#   # Parameter bindings start in `$(3)` when list bindings are specified:
226#   $(call with,l:lhs p:op l:rhs,10 20,+,$$(lhs) $$(op) $$(rhs)) # "10 + 20"
227#
228#   # Variables are restored after expansion, with flavor preserved:
229#
230#   x := outer-x
231#   y  = outer-y
232#
233#   $(info $(call with,x y,inner-x inner-y,$$(x) $$(y))) # "inner-x inner-y"
234#
235#   $(info $(x) ($(flavor x))) # "outer-x (simple)"
236#   $(info $(y) ($(flavor y))) # "outer-y (recursive)"
237#
238#   # Passing the text via `$(value ...)` to avoid `$$` escaping:
239#
240#   text = [$(head)] [$(tail)]
241#   $(call with,head tail,1 2 3 4,$(value text)) # "[1] [2 3 4]"
242#
243#   # Nested usage:
244#
245#   $(call with,a b,foo bar, \
246#       $$(call with,c d,baz qux,$$$$(a) $$$$(b) $$$$(c) $$$$(d)))
247#   # "foo bar baz qux"
248#
249
250with = $(with.ns.push)$(eval $(value with.core))$(with.ns.pop)
251
252with.ns = with.ns.$(with.ns.stack.head)
253
254with.ns.stack :=
255with.ns.stack.head = $(words $(with.ns.stack))
256
257with.ns.push = $(eval with.ns.stack += $(with.ns.stack.head))
258with.ns.pop = $($(with.ns).result)$(eval $(value with.ns.pop.1))
259
260define with.ns.pop.1 =
261        $(foreach variable,$(filter $(with.ns).%,$(.VARIABLES)),$\
262                $(eval undefine $(variable)))
263
264        with.ns.stack := $(wordlist 2,$(with.ns.stack.head),$(with.ns.stack))
265endef
266
267with.bind.norm = $\
268        $(if $(findstring :,$(1)),$\
269                $(or $(filter l: p:,$(firstword $(subst :,: ,$(1)))),$\
270                        $(error invalid binding specifier: $(1)))$\
271                $(or $(filter-out %:,$(word 2,$(subst :,: ,$(1)))),$\
272                        $(error invalid binding specifier: $(1))),$\
273                l:$(1))
274
275with.bind.kind = $(word 1,$(subst :, ,$(call with.bind.norm,$(1))))
276with.bind.name = $(word 2,$(subst :, ,$(call with.bind.norm,$(1))))
277
278define with.core =
279        # Parse and record binding list/kinds/names from `$(1)`
280        $(with.ns).bind.list := $(foreach b,$(1),$(call with.bind.norm,$(b)))
281        $(with.ns).bind.names := $(foreach b,$(1),$(call with.bind.name,$(b)))
282        $(with.ns).bind.kinds := $(foreach b,$(1),$(call with.bind.kind,$(b)))
283
284        # Create a 1..=(N_bindings) list of binding indices
285        $(with.ns).bind.idx :=
286        $(with.ns).bind.next = $(words 0 $($(with.ns).bind.idx))
287
288        $(foreach bind,$($(with.ns).bind.list),$\
289                $(eval $(with.ns).bind.idx += $($(with.ns).bind.next)))
290
291        # Create a 2..=(N_arguments) list pointing to the text argument
292        $(with.ns).text.idx :=
293        $(with.ns).text.next = $(words 1 2 $($(with.ns).text.idx))
294        $(with.ns).text = $($($(with.ns).text.next))
295
296        # Snapshot original flavors/values of all variables to be overwritten
297        $(foreach bind.name,$($(with.ns).bind.names),$\
298        $(foreach bind.name.ns,$(with.ns).bind.names[$(bind.name)],$\
299                $(eval $(bind.name.ns).flavor := $(flavor $(bind.name))$\
300                $(eval $(bind.name.ns).value = $(value $(bind.name))))))
301
302        # Initialize per-kind buckets (e.g., `l`, `p`)
303        $(foreach bind.kind,$(sort $($(with.ns).bind.kinds)),$\
304                $(eval $(with.ns).bind.kind[$(bind.kind)] := ))
305
306        # Distribute binding indices into kind buckets
307        $(foreach bind.i,$($(with.ns).bind.idx),$\
308        $(foreach bind.kind,$(word $(bind.i),$($(with.ns).bind.kinds)),$\
309                $(eval $(with.ns).bind.kind[$(bind.kind)] += $(bind.i))))
310
311        # Per-kind setup (e.g., to set up index vectors before binding)
312        $(foreach bind.kind,$(sort $($(with.ns).bind.kinds)),$\
313        $(foreach bind.kind.ns,$(with.ns).bind.kind[$(bind.kind)],$\
314                $(eval $(value with.core.$(bind.kind)))))
315
316        # Perform binding from left to right
317        $(foreach bind.i,$($(with.ns).bind.idx),$\
318        $(foreach bind.name,$(word $(bind.i),$($(with.ns).bind.names)),$\
319        $(foreach bind.kind,$(word $(bind.i),$($(with.ns).bind.kinds)),$\
320        $(foreach bind.kind.ns,$(with.ns).bind.kind[$(bind.kind)],$\
321                $(eval $(value with.core.$(bind.kind).bind))))))
322
323        # Capture the expansion result from the current text pointer
324        $(eval $(with.ns).result := $($(with.ns).text))
325
326        # Restore originals (flavor/value) or undefine if previously absent
327        $(foreach bind.name,$($(with.ns).bind.names),$\
328        $(foreach bind.name.ns,$(with.ns).bind.names[$(bind.name)],$\
329                $(eval $(value with.core.restore))))
330endef
331
332define with.core.l =
333        # Create a 1..=(N_largs) list capturing the unbound `l` words
334        $(bind.kind.ns).words.idx :=
335        $(bind.kind.ns).words.next = $(words 1 $($(bind.kind.ns).words.idx))
336        $(bind.kind.ns).words = $\
337                $(wordlist $($(bind.kind.ns).words.next),$(words $(2)),$(2))
338
339        # Increment the text pointer
340        $(with.ns).text.idx += $($(with.ns).text.next)
341endef
342
343define with.core.l.bind =
344        # Bind this name to the next unbound word
345        $(bind.name) := $(firstword $($(bind.kind.ns).words))
346
347        # If this is the last `l` binding, absorb the remaining words
348        ifeq ($($(bind.kind.ns).words.next),$(words $($(bind.kind.ns))))
349                $(bind.name) := $($(bind.kind.ns).words)
350        endif
351
352        # Nudge the word pointer forward
353        $(bind.kind.ns).words.idx += $($(bind.kind.ns).words.next)
354endef
355
356define with.core.p =
357        # Compute the parameter index that `p` bindings start at
358        $(bind.kind.ns).param.offset := 1 2
359
360        # When `l` bindings are present, `p` values shift right
361        ifneq ($(filter l,$($(with.ns).bind.kinds)),)
362                $(bind.kind.ns).param.offset += 3
363        endif
364
365        # Create an N_poff..=N_pargs list capturing the unbound `p` arguments
366        $(bind.kind.ns).param.idx :=
367        $(bind.kind.ns).param.next = $\
368                $(words $($(bind.kind.ns).param.offset) $\
369                        $($(bind.kind.ns).param.idx))
370        $(bind.kind.ns).param = $($(lastword $($(bind.kind.ns).param.idx)))
371endef
372
373define with.core.p.bind =
374        # Mark the next parameter as bound
375        $(bind.kind.ns).param.idx += $($(bind.kind.ns).param.next)
376
377        # Bind this name to the next unbound argument
378        $(bind.name) := $($(bind.kind.ns).param)
379
380        # Increment the text pointer
381        $(with.ns).text.idx += $($(with.ns).text.next)
382endef
383
384define with.core.restore =
385        ifeq ($($(bind.name.ns).flavor),simple)
386                $(eval $(bind.name) := $(value $(bind.name.ns).value))
387        else ifeq ($($(bind.name.ns).flavor),recursive)
388                $(eval $(bind.name) = $(value $(bind.name.ns).value))
389        else ifeq ($($(bind.name.ns).flavor),undefined)
390                undefine $(bind.name)
391        endif
392endef
393
394#
395# Quote a string for safe use as a shell word.
396#
397# Takes the input string `$(1)` and escapes any single quotes it contains so
398# that the result can be safely used as a literal shell argument. The output is
399# wrapped in single quotes to ensure that whitespace and special characters are
400# preserved exactly when passed to the shell.
401#
402# This function is useful when constructing shell commands dynamically, since it
403# guarantees that arbitrary values are quoted correctly and will not be
404# misinterpreted by the shell.
405#
406# Parameters:
407#
408#   - $(1): The string to quote for safe shell usage.
409#
410# Examples:
411#
412#   $(call shell-quote,foo) # "'foo'"
413#   $(call shell-quote,bar baz) # "'bar baz'"
414#   $(call shell-quote,foo 'bar baz' qux) # "'foo '\''bar baz'\'' qux'"
415#
416
417shell-quote = '$(subst ','\'',$(1))'
418