blob: e4536b3dce482a29a1ea2bcd4432fc66708e3e93 (
plain) (
blame)
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
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
|
#!/bin/sh
# Assume that "local" is available.
# shellcheck disable=SC2039
set -e
# Note: -u causes problems with posh, it barks at “undefined” $@ when no
# arguments provided.
test -z "$POSH_VERSION" && set -u
get_jobs_num() {
if [ -n "${TRAVIS:-}" ] ; then
# HACK: /proc/cpuinfo on Travis CI is misleading, so hardcode 1.
echo 1
else
echo $(( $(grep -c "^processor" /proc/cpuinfo) + 1 ))
fi
}
help() {
echo 'Usage:'
echo ' pvscheck.sh [--pvs URL] [--deps] [--environment-cc]'
echo ' [target-directory [branch]]'
echo ' pvscheck.sh [--pvs URL] [--recheck] [--environment-cc]'
echo ' [target-directory]'
echo ' pvscheck.sh [--pvs URL] --only-analyse [target-directory]'
echo ' pvscheck.sh [--pvs URL] --pvs-install {target-directory}'
echo ' pvscheck.sh --patch [--only-build]'
echo
echo ' --pvs: Use the specified URL as a path to pvs-studio archive.'
echo ' By default latest tested version is used.'
echo
echo ' May use "--pvs detect" to try detecting latest version.'
echo ' That assumes certain viva64.com site properties and'
echo ' may be broken by the site update.'
echo
echo ' --deps: (for regular run) Use top-level Makefile and build deps.'
echo ' Without this it assumes all dependencies are already'
echo ' installed.'
echo
echo ' --environment-cc: (for regular run and --recheck) Do not export'
echo ' CC=clang. Build is still run with CFLAGS=-O0.'
echo
echo ' --only-build: (for --patch) Only patch files in ./build directory.'
echo
echo ' --pvs-install: Only install PVS-studio to the specified location.'
echo
echo ' --patch: patch sources in the current directory.'
echo ' Does not patch already patched files.'
echo ' Does not run analysis.'
echo
echo ' --recheck: run analysis on a prepared target directory.'
echo
echo ' --only-analyse: run analysis on a prepared target directory '
echo ' without building Neovim.'
echo
echo ' target-directory: Directory where build should occur.'
echo ' Default: ../neovim-pvs'
echo
echo ' branch: Branch to check.'
echo ' Default: master.'
}
getopts_error() {
local msg="$1" ; shift
local do_help=
if test "$msg" = "--help" ; then
msg="$1" ; shift
do_help=1
fi
printf '%s\n' "$msg" >&2
if test -n "$do_help" ; then
printf '\n' >&2
help >&2
fi
echo 'return 1'
return 1
}
# Usage `eval "$(getopts_long long_defs -- positionals_defs -- "$@")"`
#
# long_defs: list of pairs of arguments like `longopt action`.
# positionals_defs: list of arguments like `action`.
#
# `action` is a space-separated commands:
#
# store_const [const] [varname] [default]
# Store constant [const] (default 1) (note: eval’ed) if argument is present
# (long options only). Assumes long option accepts no arguments.
# store [varname] [default]
# Store value. Assumes long option needs an argument.
# run {func} [varname] [default]
# Run function {func} and store its output to the [varname]. Assumes no
# arguments accepted (long options only).
# modify {func} [varname] [default]
# Like run, but assumes a single argument, passed to function {func} as $1.
#
# All actions stores empty value if neither [varname] nor [default] are
# present. [default] is evaled by top-level `eval`, so be careful. Also note
# that no arguments may contain spaces, including [default] and [const].
getopts_long() {
local positional=
local opt_bases=""
while test $# -gt 0 ; do
local arg="$1" ; shift
local opt_base=
local act=
local opt_name=
if test -z "$positional" ; then
if test "$arg" = "--" ; then
positional=0
continue
fi
act="$1" ; shift
opt_name="$(echo "$arg" | tr '-' '_')"
opt_base="longopt_$opt_name"
else
if test "$arg" = "--" ; then
break
fi
: $(( positional+=1 ))
act="$arg"
opt_name="arg_$positional"
opt_base="positional_$positional"
fi
opt_bases="$opt_bases $opt_base"
eval "local varname_$opt_base=$opt_name"
local i=0
for act_subarg in $act ; do
eval "local act_$(( i+=1 ))_$opt_base=\"\$act_subarg\""
done
done
# Process options
local positional=0
local force_positional=
while test $# -gt 0 ; do
local argument="$1" ; shift
local opt_base=
local has_equal=
local equal_arg=
local is_positional=
if test "$argument" = "--" ; then
force_positional=1
continue
elif test -z "$force_positional" && test "${argument#--}" != "$argument"
then
local opt_name="${argument#--}"
local opt_name_striparg="${opt_name%%=*}"
if test "$opt_name" = "$opt_name_striparg" ; then
has_equal=0
else
has_equal=1
equal_arg="${argument#*=}"
opt_name="$opt_name_striparg"
fi
# Use trailing x to prevent stripping newlines
opt_name="$(printf '%sx' "$opt_name" | tr '-' '_')"
opt_name="${opt_name%x}"
if test -n "$(printf '%sx' "$opt_name" | tr -d 'a-z_')" ; then
getopts_error "Option contains invalid characters: $opt_name"
fi
opt_base="longopt_$opt_name"
else
: $(( positional+=1 ))
opt_base="positional_$positional"
is_positional=1
fi
if test -n "$opt_base" ; then
eval "local occurred_$opt_base=1"
eval "local act_1=\"\${act_1_$opt_base:-}\""
eval "local varname=\"\${varname_$opt_base:-}\""
local need_val=
local func=
case "$act_1" in
(store_const)
eval "local const=\"\${act_2_${opt_base}:-1}\""
eval "local varname=\"\${act_3_${opt_base}:-$varname}\""
printf 'local %s=%s\n' "$varname" "$const"
;;
(store)
eval "varname=\"\${act_2_${opt_base}:-$varname}\""
need_val=1
;;
(run)
eval "func=\"\${act_2_${opt_base}}\""
eval "varname=\"\${act_3_${opt_base}:-$varname}\""
printf 'local %s="$(%s)"\n' "$varname" "$func"
;;
(modify)
eval "func=\"\${act_2_${opt_base}}\""
eval "varname=\"\${act_3_${opt_base}:-$varname}\""
need_val=1
;;
("")
getopts_error --help "Wrong argument: $argument"
;;
esac
if test -n "$need_val" ; then
local val=
if test -z "$is_positional" ; then
if test $has_equal = 1 ; then
val="$equal_arg"
else
if test $# -eq 0 ; then
getopts_error "Missing argument for $opt_name"
fi
val="$1" ; shift
fi
else
val="$argument"
fi
local escaped_val="'$(printf "%s" "$val" | sed "s/'/'\\\\''/g")'"
case "$act_1" in
(store)
printf 'local %s=%s\n' "$varname" "$escaped_val"
;;
(modify)
printf 'local %s="$(%s %s)"\n' "$varname" "$func" "$escaped_val"
;;
esac
fi
fi
done
# Print default values when no values were provided
local opt_base=
for opt_base in $opt_bases ; do
eval "local occurred=\"\${occurred_$opt_base:-}\""
if test -n "$occurred" ; then
continue
fi
eval "local act_1=\"\$act_1_$opt_base\""
eval "local varname=\"\$varname_$opt_base\""
case "$act_1" in
(store)
eval "local varname=\"\${act_2_${opt_base}:-$varname}\""
eval "local default=\"\${act_3_${opt_base}:-}\""
printf 'local %s=%s\n' "$varname" "$default"
;;
(store_const|run|modify)
eval "local varname=\"\${act_3_${opt_base}:-$varname}\""
eval "local default=\"\${act_4_${opt_base}:-}\""
printf 'local %s=%s\n' "$varname" "$default"
;;
esac
done
}
get_pvs_comment() {
local tgt="$1" ; shift
cat > "$tgt/pvs-comment" << EOF
// This is an open source non-commercial project. Dear PVS-Studio, please check
// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
EOF
}
install_pvs() {(
local tgt="$1" ; shift
local pvs_url="$1" ; shift
cd "$tgt"
mkdir pvs-studio
cd pvs-studio
curl -L -o pvs-studio.tar.gz "$pvs_url"
tar xzf pvs-studio.tar.gz
rm pvs-studio.tar.gz
local pvsdir="$(find . -maxdepth 1 -mindepth 1)"
find "$pvsdir" -maxdepth 1 -mindepth 1 -exec mv '{}' . \;
rmdir "$pvsdir"
)}
create_compile_commands() {(
local tgt="$1" ; shift
local deps="$1" ; shift
local environment_cc="$1" ; shift
if test -z "$environment_cc" ; then
export CC=clang
fi
export CFLAGS=' -O0 '
if test -z "$deps" ; then
mkdir -p "$tgt/build"
(
cd "$tgt/build"
cmake .. -DCMAKE_BUILD_TYPE=Debug -DCMAKE_INSTALL_PREFIX="$PWD/root"
make -j"$(get_jobs_num)"
)
else
(
cd "$tgt"
make -j"$(get_jobs_num)" CMAKE_EXTRA_FLAGS=" -DCMAKE_INSTALL_PREFIX=$PWD/root -DCMAKE_BUILD_TYPE=Debug "
)
fi
find "$tgt/build/src/nvim/auto" -name '*.test-include.c' -delete
)}
# Warning: realdir below only cares about directories unlike realpath.
#
# realpath is not available in Ubuntu trusty yet.
realdir() {(
local dir="$1"
cd "$dir"
printf '%s\n' "$PWD"
)}
patch_sources() {(
local tgt="$1" ; shift
local only_bulid="${1}" ; shift
get_pvs_comment "$tgt"
local sh_script='
pvs_comment="$(cat pvs-comment ; echo -n EOS)"
filehead="$(head -c $(( ${#pvs_comment} - 3 )) "$1" ; echo -n EOS)"
if test "x$filehead" != "x$pvs_comment" ; then
cat pvs-comment "$1" > "$1.tmp"
mv "$1.tmp" "$1"
fi
'
cd "$tgt"
if test "$only_build" != "--only-build" ; then
find \
src/nvim test/functional/fixtures test/unit/fixtures \
-name '*.c' \
-exec /bin/sh -c "$sh_script" - '{}' \;
fi
find \
build/src/nvim/auto build/config \
-name '*.c' -not -name '*.test-include.c' \
-exec /bin/sh -c "$sh_script" - '{}' \;
rm pvs-comment
)}
run_analysis() {(
local tgt="$1" ; shift
cd "$tgt"
pvs-studio-analyzer \
analyze \
--threads "$(get_jobs_num)" \
--output-file PVS-studio.log \
--verbose \
--file build/compile_commands.json \
--sourcetree-root .
plog-converter -t xml -o PVS-studio.xml PVS-studio.log
plog-converter -t errorfile -o PVS-studio.err PVS-studio.log
plog-converter -t tasklist -o PVS-studio.tsk PVS-studio.log
)}
do_check() {
local tgt="$1" ; shift
local branch="$1" ; shift
local pvs_url="$1" ; shift
local deps="$1" ; shift
local environment_cc="$1" ; shift
git clone --branch="$branch" . "$tgt"
install_pvs "$tgt" "$pvs_url"
do_recheck "$tgt" "$deps" "$environment_cc"
}
do_recheck() {
local tgt="$1" ; shift
local deps="$1" ; shift
local environment_cc="$1" ; shift
create_compile_commands "$tgt" "$deps" "$environment_cc"
do_analysis "$tgt"
}
do_analysis() {
local tgt="$1" ; shift
if test -d "$tgt/pvs-studio" ; then
local saved_pwd="$PWD"
cd "$tgt/pvs-studio"
export PATH="$PWD/bin${PATH+:}${PATH}"
cd "$saved_pwd"
fi
run_analysis "$tgt"
}
detect_url() {
local url="${1:-detect}"
if test "$url" = detect ; then
curl -L 'https://www.viva64.com/en/pvs-studio-download-linux/' \
| grep -o 'https\{0,1\}://[^"<>]\{1,\}/pvs-studio[^/"<>]*\.tgz'
else
printf '%s' "$url"
fi
}
main() {
local def_pvs_url="http://files.viva64.com/pvs-studio-6.15.21741.1-x86_64.tgz"
eval "$(
getopts_long \
help store_const \
pvs 'modify detect_url pvs_url "${def_pvs_url}"' \
patch store_const \
only-build 'store_const --only-build' \
recheck store_const \
only-analyse store_const \
pvs-install store_const \
deps store_const \
environment-cc store_const \
-- \
'modify realdir tgt "$PWD/../neovim-pvs"' \
'store branch master' \
-- "$@"
)"
if test -n "$help" ; then
help
return 0
fi
set -x
if test -n "$patch" ; then
patch_sources "$tgt" "$only_build"
elif test -n "$pvs_install" ; then
install_pvs "$tgt" "$pvs_url"
elif test -n "$recheck" ; then
do_recheck "$tgt" "$deps" "$environment_cc"
elif test -n "$only_analyse" ; then
do_analysis "$tgt"
else
do_check "$tgt" "$branch" "$pvs_url" "$deps" "$environment_cc"
fi
}
main "$@"
|