From 4cb61aa7428128283c45ee3ca90f01c4805c8148 Mon Sep 17 00:00:00 2001 From: ZyX Date: Tue, 2 May 2017 17:56:30 +0300 Subject: [PATCH 1/5] pvscheck: Create getopts_long implementation Needed as argument list is growing large and I absolutely do not find short options provided by getopts being particularly readable for unfamiliar commands. [ci skip] --- scripts/pvscheck.sh | 256 ++++++++++++++++++++++++++++++++++++-------- 1 file changed, 211 insertions(+), 45 deletions(-) diff --git a/scripts/pvscheck.sh b/scripts/pvscheck.sh index 2af38d7711..51ecaf0c46 100755 --- a/scripts/pvscheck.sh +++ b/scripts/pvscheck.sh @@ -1,5 +1,8 @@ #!/bin/sh 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() { local num="$(cat /proc/cpuinfo | grep -c "^processor")" @@ -36,6 +39,178 @@ help() { echo ' Default: master.' } +getopts_error() { + printf '%s\n' "$1" >&2 + 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 + ;; + 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() { cat > pvs-comment << EOF // This is an open source non-commercial project. Dear PVS-Studio, please check @@ -62,18 +237,16 @@ install_pvs() { cd .. } -create_compile_commands() { +create_compile_commands() {( + export CC=clang + export CFLAGS=' -O0 ' + mkdir build cd build - env \ - CC=clang \ - CFLAGS=' -O0 ' \ - cmake .. -DCMAKE_BUILD_TYPE=Debug -DCMAKE_INSTALL_PREFIX="$PWD/root" + cmake .. -DCMAKE_BUILD_TYPE=Debug -DCMAKE_INSTALL_PREFIX="$PWD/root" make -j"$(get_jobs_num)" find src/nvim/auto -name '*.test-include.c' -delete - - cd .. -} +)} patch_sources() { get_pvs_comment @@ -87,7 +260,7 @@ patch_sources() { fi ' - if test "x$1" != "x--only-build" ; then + if test "${1:-}" != "--only-build" ; then find \ src/nvim test/functional/fixtures test/unit/fixtures \ -name '*.c' \ @@ -117,9 +290,9 @@ run_analysis() { } do_check() { - local tgt="$1" - local branch="$2" - local pvs_url="$3" + local tgt="$1" ; shift + local branch="$1" ; shift + local pvs_url="$1" ; shift git clone --branch="$branch" . "$tgt" @@ -133,7 +306,7 @@ do_check() { } do_recheck() { - local tgt="${1}" + local tgt="$1" cd "$tgt" @@ -143,49 +316,42 @@ do_recheck() { } detect_url() { - curl -L 'https://www.viva64.com/en/pvs-studio-download-linux/' \ - | grep -o 'https\{0,1\}://[^"<>]\{1,\}/pvs-studio[^/"<>]*\.tgz' + 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 pvs_url="http://files.viva64.com/pvs-studio-6.15.21741.1-x86_64.tgz" + 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 \ + -- \ + 'store tgt "$PWD/../neovim-pvs"' \ + 'store branch master' \ + -- "$@" + )" - if test "$1" = "--help" ; then + if test -n "$help" ; then help - return + return 0 fi set -x - if test "$1" = "--pvs" ; then - shift - pvs_url="$1" ; shift - - if test "$pvs_url" = "detect" ; then - pvs_url="$(detect_url)" - fi + if test -n "$patch" ; then + patch_sources "$only_build" + return $? fi - if test "$1" = "--patch" ; then - shift - if test "$1" = "--only-build" ; then - shift - patch_sources --only-build - else - patch_sources - fi - exit $? - fi - - local recheck= - if test "$1" = "--recheck" ; then - recheck=1 - shift - fi - - local tgt="${1:-$PWD/../neovim-pvs}" - local branch="${2:-master}" - if test -z "$recheck" ; then do_check "$tgt" "$branch" "$pvs_url" else From 68945ead6327aa7050fd3a82f5803e007ab5d979 Mon Sep 17 00:00:00 2001 From: ZyX Date: Tue, 2 May 2017 18:08:48 +0300 Subject: [PATCH 2/5] pvscheck: Refactor script so that it only `cd`s in a subshell [ci skip] --- scripts/pvscheck.sh | 66 +++++++++++++++++++++++++++++---------------- 1 file changed, 43 insertions(+), 23 deletions(-) diff --git a/scripts/pvscheck.sh b/scripts/pvscheck.sh index 51ecaf0c46..7e965f5edf 100755 --- a/scripts/pvscheck.sh +++ b/scripts/pvscheck.sh @@ -212,15 +212,20 @@ getopts_long() { } get_pvs_comment() { - cat > pvs-comment << EOF + 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 pvs_url="$1" +install_pvs() {( + local tgt="$1" ; shift + local pvs_url="$1" ; shift + + cd "$tgt" mkdir pvs-studio cd pvs-studio @@ -231,25 +236,36 @@ install_pvs() { local pvsdir="$(find . -maxdepth 1 -mindepth 1)" find "$pvsdir" -maxdepth 1 -mindepth 1 -exec mv '{}' . \; rmdir "$pvsdir" +)} - export PATH="$PWD/bin${PATH+:}${PATH}" - - cd .. +adjust_path() { + 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 } create_compile_commands() {( + local tgt="$1" ; shift + export CC=clang export CFLAGS=' -O0 ' - mkdir build - cd build + mkdir "$tgt/build" + cd "$tgt/build" + cmake .. -DCMAKE_BUILD_TYPE=Debug -DCMAKE_INSTALL_PREFIX="$PWD/root" make -j"$(get_jobs_num)" find src/nvim/auto -name '*.test-include.c' -delete )} -patch_sources() { - get_pvs_comment +patch_sources() {( + local tgt="$1" ; shift + local only_bulid="${1:-}" + + get_pvs_comment "$tgt" local sh_script=' pvs_comment="$(cat pvs-comment ; echo -n EOS)" @@ -260,7 +276,9 @@ patch_sources() { fi ' - if test "${1:-}" != "--only-build" ; then + cd "$tgt" + + if test "$only_build" != "--only-build" ; then find \ src/nvim test/functional/fixtures test/unit/fixtures \ -name '*.c' \ @@ -273,9 +291,13 @@ patch_sources() { -exec /bin/sh -c "$sh_script" - '{}' \; rm pvs-comment -} +)} + +run_analysis() {( + local tgt="$1" ; shift + + cd "$tgt" -run_analysis() { pvs-studio-analyzer \ analyze \ --threads "$(get_jobs_num)" \ @@ -287,7 +309,7 @@ run_analysis() { 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 @@ -296,23 +318,21 @@ do_check() { git clone --branch="$branch" . "$tgt" - cd "$tgt" + install_pvs "$tgt" "$pvs_url" - install_pvs "$pvs_url" + adjust_path "$tgt" - create_compile_commands + create_compile_commands "$tgt" - run_analysis + run_analysis "$tgt" } do_recheck() { local tgt="$1" - cd "$tgt" + adjust_path "$tgt" - export PATH="$PWD/pvs-studio/bin${PATH+:}${PATH}" - - run_analysis + run_analysis "$tgt" } detect_url() { @@ -348,7 +368,7 @@ main() { set -x if test -n "$patch" ; then - patch_sources "$only_build" + patch_sources "$only_build" "$tgt" return $? fi From 97806ee6d682ca8912f95825c485d96410802138 Mon Sep 17 00:00:00 2001 From: ZyX Date: Tue, 2 May 2017 18:17:12 +0300 Subject: [PATCH 3/5] pvscheck: Add --pvs-install mode [ci skip] --- scripts/pvscheck.sh | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/scripts/pvscheck.sh b/scripts/pvscheck.sh index 7e965f5edf..39de763fe2 100755 --- a/scripts/pvscheck.sh +++ b/scripts/pvscheck.sh @@ -15,6 +15,7 @@ help() { echo 'Usage:' echo ' pvscheck.sh [--pvs URL] [target-directory [branch]]' echo ' pvscheck.sh [--pvs URL] [--recheck] [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.' @@ -24,12 +25,14 @@ help() { echo ' That assumes certain viva64.com site properties and' echo ' may be broken by the site update.' 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 ' --only-build: Only patch files in ./build directory.' - echo echo ' --recheck: run analysis on a prepared target directory.' echo echo ' target-directory: Directory where build should occur.' @@ -354,6 +357,7 @@ main() { patch store_const \ only-build 'store_const --only-build' \ recheck store_const \ + pvs-install store_const \ -- \ 'store tgt "$PWD/../neovim-pvs"' \ 'store branch master' \ @@ -369,13 +373,12 @@ main() { if test -n "$patch" ; then patch_sources "$only_build" "$tgt" - return $? - fi - - if test -z "$recheck" ; then - do_check "$tgt" "$branch" "$pvs_url" - else + elif test -n "$pvs_install" ; then + install_pvs "$tgt" "$pvs_url" + elif test -n "$recheck" ; then do_recheck "$tgt" + else + do_check "$tgt" "$branch" "$pvs_url" fi } From 3638d28f68969c81242b8d7345f1d0348fce497d Mon Sep 17 00:00:00 2001 From: ZyX Date: Tue, 2 May 2017 18:24:29 +0300 Subject: [PATCH 4/5] pvscheck: Add --deps to build with all dependencies [ci skip] --- scripts/pvscheck.sh | 27 ++++++++++++++++++++------- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/scripts/pvscheck.sh b/scripts/pvscheck.sh index 39de763fe2..da55965d36 100755 --- a/scripts/pvscheck.sh +++ b/scripts/pvscheck.sh @@ -13,7 +13,7 @@ get_jobs_num() { help() { echo 'Usage:' - echo ' pvscheck.sh [--pvs URL] [target-directory [branch]]' + echo ' pvscheck.sh [--pvs URL] [--deps] [target-directory [branch]]' echo ' pvscheck.sh [--pvs URL] [--recheck] [target-directory]' echo ' pvscheck.sh [--pvs URL] --pvs-install {target-directory}' echo ' pvscheck.sh --patch [--only-build]' @@ -25,6 +25,10 @@ help() { 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 ' --only-build: (for --patch) Only patch files in ./build directory.' echo echo ' --pvs-install: Only install PVS-studio to the specified location.' @@ -252,15 +256,22 @@ adjust_path() { create_compile_commands() {( local tgt="$1" ; shift + local deps="$1" ; shift export CC=clang export CFLAGS=' -O0 ' - mkdir "$tgt/build" - cd "$tgt/build" + if test -z "$deps" ; then + mkdir "$tgt/build" + cd "$tgt/build" - cmake .. -DCMAKE_BUILD_TYPE=Debug -DCMAKE_INSTALL_PREFIX="$PWD/root" - make -j"$(get_jobs_num)" + 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 src/nvim/auto -name '*.test-include.c' -delete )} @@ -318,6 +329,7 @@ do_check() { local tgt="$1" ; shift local branch="$1" ; shift local pvs_url="$1" ; shift + local deps="$1" ; shift git clone --branch="$branch" . "$tgt" @@ -325,7 +337,7 @@ do_check() { adjust_path "$tgt" - create_compile_commands "$tgt" + create_compile_commands "$tgt" "$deps" run_analysis "$tgt" } @@ -358,6 +370,7 @@ main() { only-build 'store_const --only-build' \ recheck store_const \ pvs-install store_const \ + deps store_const \ -- \ 'store tgt "$PWD/../neovim-pvs"' \ 'store branch master' \ @@ -378,7 +391,7 @@ main() { elif test -n "$recheck" ; then do_recheck "$tgt" else - do_check "$tgt" "$branch" "$pvs_url" + do_check "$tgt" "$branch" "$pvs_url" "$deps" fi } From 0c6e0460b6f15dce8dd5f517712093d843d76058 Mon Sep 17 00:00:00 2001 From: ZyX Date: Tue, 2 May 2017 18:33:41 +0300 Subject: [PATCH 5/5] pvscheck: When using --recheck rerun build [ci skip] --- scripts/pvscheck.sh | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/scripts/pvscheck.sh b/scripts/pvscheck.sh index da55965d36..67834cca8a 100755 --- a/scripts/pvscheck.sh +++ b/scripts/pvscheck.sh @@ -262,7 +262,7 @@ create_compile_commands() {( export CFLAGS=' -O0 ' if test -z "$deps" ; then - mkdir "$tgt/build" + mkdir -p "$tgt/build" cd "$tgt/build" cmake .. -DCMAKE_BUILD_TYPE=Debug -DCMAKE_INSTALL_PREFIX="$PWD/root" @@ -347,6 +347,8 @@ do_recheck() { adjust_path "$tgt" + create_compile_commands "$tgt" "$deps" + run_analysis "$tgt" }