forked from intel/ethernet-linux-idpf
-
Notifications
You must be signed in to change notification settings - Fork 0
/
kcompat-lib.sh
403 lines (370 loc) · 11.5 KB
/
kcompat-lib.sh
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
#!/bin/bash
# SPDX-License-Identifier: GPL-2.0-only
# Copyright (C) 2019-2024 Intel Corporation
# to be sourced
# General shell helpers
# exit with non-zero exit code; if there is only one param:
# exit with msg $1 and exit code from last command (or 99 if = 0)
# otherwise, exit with $1 and use remaining arguments as msg
function die() {
rc=$?
if [ $# -gt 1 ]; then
rc="$1"
shift
fi
[ "$rc" -ne 0 ] || rc=99
echo >&2 "$@"
exit $rc
}
# filter out paths that are not files
# input $@, output via echo;
# note: pass `-` for stdin
# note: outputs nothing if all input files are "bad" (eg. not existing), but it
# is left for caller to decide if this is an erorr condition;
# note: whitespaces are considered "bad" as part of filename, it's an error.
function filter-out-bad-files() {
if [[ $# = 1 && "$1" = '-' ]]; then
echo -
return 0
fi
if [ $# = 0 ]; then
die 10 "no files passed, use '-' when reading from pipe (|)"
fi
local any=0 diagmsgs=/dev/stderr re=$'[\t \n]'
[ -n "${QUIET_COMPAT-}" ] && diagmsgs=/dev/null
for x in "$@"; do
if [ -e "$x" ]; then
if [[ "$x" =~ $re ]]; then
die 11 "err: filename contains whitespaces: $x."
fi
echo "$x"
any=1
else
echo >&"$diagmsgs" filtering "$x" out
fi
done
if [ $any = 0 ]; then
echo >&"$diagmsgs" 'all files (for given query) filtered out'
fi
}
# Basics of regexp explained, as a reference for mostly-C programmers:
# (bash) "regexp-$VAR-regexp" - bash' VARs are placed into "QUOTED" strings
# /\);?$/ - match end of function declaration, $ is end of string
# ^[ \t]* - (heuristic), anything but comment, eg to exclude function docs
# /STH/, /END/ - (awk), print all lines sice STH matched, up to END, inclusive
# "Whitespace only"
WB='[ \t\n]'
# Helpers below print the thing that is looked for, for further grep'ping/etc.
# That simplifies process of excluding comments or spares us state machine impl.
#
# We take advantage of current/common linux codebase formatting here.
#
# Functions in this section require input file/s passed as args
# (usually one, but more could be supplied in case of renames in kernel),
# '-' could be used as an (only) file argument to read from stdin/pipe.
# wrapper over find-something-decl() functions below, to avoid repetition
# pass $what as $1, $end as $2, and $files to look in as rest of args
function find-decl() {
test $# -ge 3 # ensure that there are at least 3 params
local what end files
what="$1"
end="$2"
shift 2
files="$(filter-out-bad-files "$@")" || die
if [ -z "$files" ]; then
return 0
fi
# shellcheck disable=SC2086
awk "
/^$WB*\*/ {next}
$what, $end
" $files
}
# yield $1 function declaration (signature), don't pass return type in $1
# looks only in files specified ($2, $3...)
function find-fun-decl() {
test $# -ge 2
local what end
what="/$WB*([(]\*)?$1$WB*($|[()])/"
end='/\);?$/'
shift
find-decl "$what" "$end" "$@"
}
# yield $1 enum declaration (type/body)
function find-enum-decl() {
test $# -ge 2
local what end
what="/^$WB*enum$WB+$1"' \{$/'
end='/\};$/'
shift
find-decl "$what" "$end" "$@"
}
# yield $1 struct declaration (type/body)
function find-struct-decl() {
test $# -ge 2
local what end
what="/^$WB*struct$WB+$1"' \{$/'
end='/^\};$/' # that's (^) different from enum-decl
shift
find-decl "$what" "$end" "$@"
}
# yield first line of $1 macro definition
function find-macro-decl() {
test $# -ge 2
local what end
# only unindented defines, only whole-word match
what="/^#define$WB+$1"'([ \t\(]|$)/'
end=1 # only first line; use find-macro-implementation-decl for full body
shift
find-decl "$what" "$end" "$@"
}
# yield full macro implementation
function find-macro-implementation-decl() {
test $# -ge 2
local what end
# only unindented defines, only whole-word match
what="/^#define$WB+$1"'([ \t\(]|$)/'
# full implementation, until a line not ending in a backslash.
# Does not handle macros with comments embedded within the definition.
end='/[^\\]$/'
shift
find-decl "$what" "$end" "$@"
}
# yield first line of $1 typedef definition (simple typedefs only)
# this probably won't handle typedef struct { \n int foo;\n};
function find-typedef-decl() {
test $# -ge 2
local what end
what="/^typedef .* $1"';$/'
end=1
shift
find-decl "$what" "$end" "$@"
}
# gen() - DSL-like function to wrap around all the other
#
# syntax:
# gen DEFINE if (KIND [METHOD of]) NAME [(matches|lacks) PATTERN|absent] in <list-of-files>
# gen DEFINE if string "actual" equals "expected"
# where:
# DEFINE is HAVE_ or NEED_ #define to print;
# `if` is there to just read it easier and made syntax easier to check;
#
# NAME is the name for what we are looking for;
#
# `if string` can be used to check if a provided string matches an expected
# value. The define will only be generated if the strings are exactly
# equal. Otherwise, the define will not be generated. When operating in
# UNIFDEF_MODE, -DDEFINE is output when the strings are equal, while
# -UDEFINE is output when the strings are not equal. This is intended
# for cases where a more complex conditional is required, such as
# generating a define when multiple different functions exist.
#
# Ex:
#
# FUNC1="$(find-fun-decl devlink_foo1 devlink.h)"
# FUNC2="$(find-fun-decl devlink_foo2 devlink.h)"
# gen HAVE_FOO_12 if string "${FUNC1:+1}${FUNC2:+1}" equals "11"
#
# KIND specifies what kind of declaration/definition we are looking for,
# could be: fun, enum, struct, method, macro, typedef,
# 'implementation of macro'
# for KIND=method, we are looking for function ptr named METHOD in struct
# named NAME (two optional args are then necessary (METHOD & of));
#
# for KIND='implementation of macro' we are looking for the full
# implementation of the macro, not just its first line. This is usually
# combined with "matches" or "lacks".
#
# next [optional] args could be used:
# matches PATTERN - use to grep for the PATTERN within definition
# (eg, for ext_ack param)
# lacks - use to add #define only if there is no match of the PATTERN,
# *but* the NAME is *found*
# absent - the NAME that we grep for must be not found
# (ie: function not exisiting)
#
# without this optional params, behavior is the same as with
# `matches .` - use to grep just for existence of NAME;
#
# `in` is there to ease syntax, similar to `if` before.
#
# <list-of-files> is just space-separate list of files to look in,
# single (-) for stdin.
#
# PATTERN is an awk pattern, will be wrapped by two slashes (/)
#
# The usual output is a list of "#define <flag>" lines for each flag that has
# a matched definition. When UNIFDEF_MODE is set to a non-zero string, the
# output is instead a sequence of "-D<flag>" for each matched definition, and
# "-U<flag>" for each definition which didn't match.
function gen() {
test $# -ge 4 || die 20 "too few arguments, $# given, at least 4 needed"
local define if_kw kind name in_kw # mandatory
local of_kw method_name operator pattern # optional
local src_line="${BASH_SOURCE[0]}:${BASH_LINENO[0]}"
define="$1"
if_kw="$2"
kind="$3"
local orig_args_cnt=$#
shift 3
[ "$if_kw" != if ] && die 21 "$src_line: 'if' keyword expected, '$if_kw' given"
case "$kind" in
string)
local actual_str expect_str equals_kw missing_fmt found_fmt
test $# -ge 3 || die 22 "$src_line: too few arguments, $orig_args_cnt given, at least 6 needed"
actual_str="$1"
equals_kw="$2"
expect_str="$3"
shift 3
if [ -z ${UNIFDEF_MODE:+1} ]; then
found_fmt="#define %s\n"
missing_fmt=""
else
found_fmt="-D%s\n"
missing_fmt="-U%s\n"
fi
if [ "${actual_str}" = "${expect_str}" ]; then
printf -- "$found_fmt" "$define"
else
printf -- "$missing_fmt" "$define"
fi
return
;;
fun|enum|struct|macro|typedef)
test $# -ge 3 || die 22 "$src_line: too few arguments, $orig_args_cnt given, at least 6 needed"
name="$1"
shift
;;
method)
test $# -ge 5 || die 22 "$src_line: too few arguments, $orig_args_cnt given, at least 8 needed"
method_name="$1"
of_kw="$2"
name="$3"
shift 3
[ "$of_kw" != of ] && die 23 "$src_line: 'of' keyword expected, '$of_kw' given"
;;
implementation)
test $# -ge 5 || die 28 "$src_line: too few arguments, $orig_args_cnt given, at least 8 needed"
of_kw="$1"
kind="$2"
name="$3"
shift 3
[ "$of_kw" != of ] && die 29 "$src_line: 'of' keyword expected, '$of_kw' given"
[ "$kind" != macro ] && die 30 "$src_line: implementation only supports 'macro', '$kind' given"
kind=macro-implementation
;;
*) die 24 "$src_line: unknown KIND ($kind) to look for" ;;
esac
operator="$1"
case "$operator" in
absent)
pattern='.'
in_kw="$2"
shift 2
;;
matches|lacks)
pattern="$2"
in_kw="$3"
shift 3
;;
in)
operator=matches
pattern='.'
in_kw=in
shift
;;
*) die 25 "$src_line: unknown OPERATOR ($operator) to look for" ;;
esac
[ "$in_kw" != in ] && die 26 "$src_line: 'in' keyword expected, '$in_kw' given"
test $# -ge 1 || die 27 "$src_line: too few arguments, at least one filename expected"
local first_decl=
if [ "$kind" = method ]; then
first_decl="$(find-struct-decl "$name" "$@")" || exit 40
# prepare params for next lookup phase
set -- - # overwrite $@ to be single dash (-)
name="$method_name"
kind=fun
elif [[ $# = 1 && "$1" = '-' ]]; then
# avoid losing stdin provided to gen() due to redirection (<<<)
first_decl="$(cat -)"
fi
local unifdef
unifdef=${UNIFDEF_MODE:+1}
# lookup the NAME
local body
body="$(find-$kind-decl "$name" "$@" <<< "$first_decl")" || exit 41
awk -v define="$define" -v pattern="$pattern" -v "$operator"=1 -v unifdef="$unifdef" '
BEGIN {
# prepend "identifier boundary" to pattern, also append
# it, but only for patterns not ending with such already
#
# eg: "foo" -> "\bfoo\b"
# "struct foo *" -> "\bstruct foo *"
# Note that mawk does not support "\b", so we have our
# own approximation, NI
NI = "[^A-Za-z0-9_]" # "Not an Indentifier"
if (!match(pattern, NI "$"))
pattern = pattern "(" NI "|$)"
pattern = "(^|" NI ")" pattern
}
/./ { not_empty = 1 }
$0 ~ pattern { found = 1 }
END {
if (unifdef) {
found_fmt="-D%s\n"
missing_fmt="-U%s\n"
} else {
found_fmt="#define %s\n"
missing_fmt=""
}
if (lacks && !found && not_empty || matches && found || absent && !found)
printf(found_fmt, define)
else if (missing_fmt)
printf(missing_fmt, define)
}
' <<< "$body"
}
# tell if given flag is enabled in .config
# return 0 if given flag is enabled, 1 otherwise
# inputs:
# $1 - flag to check (whole word, without _MODULE suffix)
# env flag $CONFIG_FILE
#
# there are two "config" formats supported, to ease up integrators lifes
# .config (without leading #~ prefix):
#~ # CONFIG_ACPI_EC_DEBUGFS is not set
#~ CONFIG_ACPI_AC=y
#~ CONFIG_ACPI_VIDEO=m
# and autoconf.h, which would be:
#~ #define CONFIG_ACPI_AC 1
#~ #define CONFIG_ACPI_VIDEO_MODULE 1
function config_has() {
grep -qE "^(#define )?$1((_MODULE)? 1|=m|=y)$" "$CONFIG_FILE"
}
# try to locate a suitable config file from KSRC
#
# On success, the CONFIG_FILE variable will be updated to reflect the full
# path to a configuration file.
#
# Depends on KSRC being set
function find_config_file() {
local -a CSP
local file
local diagmsgs=/dev/stderr
[ -n "${QUIET_COMPAT-}" ] && diagmsgs=/dev/null
if ! [ -d "${KSRC-}" ]; then
return
fi
CSP=(
"$KSRC/include/generated/autoconf.h"
"$KSRC/include/linux/autoconf.h"
"$KSRC/.config"
)
for file in "${CSP[@]}"; do
if [ -f $file ]; then
echo >&"$diagmsgs" "using CONFIG_FILE=$file"
CONFIG_FILE=$file
return
fi
done
}