OILS / test / lint.sh View on Github | oilshell.org

314 lines, 176 significant
1#!/usr/bin/env bash
2#
3# Run tools to maintain the coding style.
4#
5# Usage:
6# test/lint.sh <function name>
7
8set -o nounset
9set -o pipefail
10set -o errexit
11shopt -s strict:all 2>/dev/null || true # dogfood for OSH
12
13REPO_ROOT=$(cd "$(dirname $0)/.."; pwd)
14readonly REPO_ROOT
15
16source build/common.sh
17source build/dev-shell.sh # python2 and python3
18source devtools/common.sh # banner
19source devtools/run-task.sh # run-task
20
21#
22# C++
23#
24
25get-cpplint() {
26 mkdir -p _tmp
27 wget --directory _tmp \
28 https://raw.githubusercontent.com/google/styleguide/gh-pages/cpplint/cpplint.py
29 chmod +x _tmp/cpplint.py
30}
31
32cpplint() {
33 # we don't have subdir names on the header guard
34 _tmp/cpplint.py --filter \
35 -readability/todo,-legal/copyright,-build/header_guard,-build/include,-whitespace/comments "$@"
36}
37
38#
39# Space checks
40#
41
42find-tabs() {
43 devtools/repo.sh find-src-files \
44 | egrep -v 'tools/(xargs|find)' \
45 | xargs grep -n $'\t'
46}
47
48find-long-lines() {
49 # Exclude URLs
50 devtools/repo.sh find-src-files \
51 | xargs grep -n '^.\{81\}' | grep -v 'http'
52}
53
54#
55# pyflakes-based lint
56#
57
58oils-lint() {
59 local lang=$1 # py2 or py3
60 shift
61
62 PYTHONPATH=.:~/wedge/oils-for-unix.org/pkg/pyflakes/2.4.0 test/${lang}_lint.py "$@"
63 #PYTHONPATH=.:vendor/pyflakes-2.4.0 test/oils_lint.py "$@"
64}
65
66py2-lint() {
67 oils-lint py2 "$@"
68}
69
70py3-lint() {
71 oils-lint py3 "$@"
72}
73
74# TODO: Use devtools/repo.sh instead of this hard-coded list
75readonly -a CODE_DIRS=(
76 asdl bin builtin core data_lang doctools frontend osh tools yaks ysh
77
78 prebuilt
79 pyext
80 lazylex
81 benchmarks
82 build
83
84 #pylib
85 #test
86)
87
88py2-files-to-lint() {
89 if false; then
90 # TODO: This is better
91 # Although we should filter by $2
92
93 devtools/repo.sh py-manifest \
94 | egrep -v 'opy/|tools/find/|tools/xargs/' \
95 | awk '$1 == "py2" { print $2 }'
96 return
97 fi
98
99 for dir in "${CODE_DIRS[@]}"; do
100 for name in $dir/*.py; do
101 echo $name
102 done
103 done | grep -v 'NINJA_subgraph' # leave out for now
104}
105
106py2() {
107 banner 'Linting Python 2 code'
108
109 # syntax_abbrev.py doesn't stand alone
110 py2-files-to-lint | grep -v '_abbrev.py' | xargs $0 py2-lint
111}
112
113py3-files() {
114 for f in mycpp/*.py; do
115 echo $f
116 done
117}
118
119py3() {
120 banner 'Linting Python 3 code'
121
122 py3-files | xargs $0 py3-lint
123}
124
125all-py() {
126 py2
127 py3
128}
129
130#
131# More Python, including Python 3
132#
133
134mycpp-files() {
135 for f in mycpp/*.py; do
136 case $f in
137 */NINJA_subgraph.py)
138 continue
139 ;;
140 esac
141
142 echo $f
143 done
144}
145
146#
147# Main
148#
149
150# Hook for soil
151soil-run() {
152 if test -n "${TRAVIS_SKIP:-}"; then
153 echo "TRAVIS_SKIP: Skipping $0"
154 return
155 fi
156
157 #flake8-all
158
159 # Our new lint script
160 all-py
161
162 check-shebangs
163}
164
165#
166# Adjust and Check shebang lines. It matters for developers on different distros.
167#
168
169find-files-to-lint() {
170 ### Similar to find-prune / find-src-files
171
172 # don't touch mycpp yet because it's in Python 3
173 # build has build/dynamic_deps.py which needs the -S
174 find . \
175 -name '_*' -a -prune -o \
176 -name 'Python-*' -a -prune -o \
177 "$@"
178}
179
180find-py() {
181 find-files-to-lint \
182 -name 'build' -a -prune -o \
183 -name '*.py' -a -print "$@"
184}
185
186find-sh() {
187 find-files-to-lint -name '*.sh' -a -print "$@"
188}
189
190print-if-has-shebang() {
191 read first < $1
192 [[ "$first" == '#!'* ]] && echo $1
193}
194
195not-executable() {
196 find-py -a ! -executable -a -print | xargs -n 1 -- $0 print-if-has-shebang
197}
198
199executable-py() {
200 find-py -a -executable -a -print | xargs -n 1 -- echo
201}
202
203# Make all shebangs consistent.
204# - Specify python2 because on some distros 'python' is python3
205# - Use /usr/bin/env because it works better with virtualenv?
206#
207# https://stackoverflow.com/questions/9309940/sed-replace-first-line
208#
209# e.g. cat edit.list, change the first line
210
211replace-py-shebang() {
212 sed -i '1c#!/usr/bin/env python2' "$@"
213}
214
215replace-bash-shebang() {
216 sed -i '1c#!/usr/bin/env bash' "$@"
217}
218
219# NOTE: no ^ anchor because of print-first-line
220
221readonly BAD_PY='#!.*/usr/bin/python'
222readonly BAD_BASH='#!.*/bin/bash'
223
224bad-py() {
225 find-py -a -print | xargs -- egrep "$BAD_PY"
226 #grep '^#!.*/bin/bash ' */*.sh
227
228 find-py -a -print | xargs -- egrep -l "$BAD_PY" | xargs $0 replace-py-shebang
229}
230
231bad-bash() {
232 # these files don't need shebangs
233 #grep -l '^#!' spec/*.test.sh | xargs -- sed -i '1d'
234
235 #find-sh -a -print | xargs -- grep "$BAD_BASH"
236
237 find-sh -a -print | xargs -- egrep -l "$BAD_BASH" | xargs $0 replace-bash-shebang
238}
239
240print-first-line() {
241 local path=$1
242
243 read line < "$path"
244 echo "$path: $line" # like grep output
245}
246
247check-shebangs() {
248 set +o errexit
249
250 if true; then
251 find-py | xargs -d $'\n' -n 1 -- $0 print-first-line | egrep "$BAD_PY"
252 if test $? -ne 1; then
253 die "FAIL: Found bad Python shebangs"
254 fi
255 fi
256
257 find-sh | xargs -d $'\n' -n 1 -- $0 print-first-line | egrep "$BAD_BASH"
258 if test $? -ne 1; then
259 die "FAIL: Found bad bash shebangs"
260 fi
261
262 echo 'PASS: check-shebangs'
263}
264
265#
266# sprintf -- What do we need in mycpp?
267#
268
269sp-formats() {
270 egrep --no-filename --only-matching '%.' */*.py | sort | uniq -c | sort -n
271}
272
273# 122 instances of these. %() for named
274sp-rare() {
275 egrep --color=always '%[^srd ]' */*.py | egrep -v 'Python-|_test.py'
276}
277
278#
279# inherit
280#
281
282# 56 instances of inheritance
283inheritance() {
284 grep ^class {osh,core,ysh,frontend}/*.py \
285 | egrep -v '_test|object'
286}
287
288# 18 unique base classes.
289# TODO: Maybe extract this automatically with OPy?
290# Or does the MyPy AST have enough?
291# You can collect method defs in the decl phase. Or in the forward_decl phase?
292
293base-classes() {
294 inheritance | egrep -o '\(.*\)' | sort | uniq -c | sort -n
295}
296
297translation() {
298 set +o errexit
299
300 metrics/source-code.sh osh-files \
301 | xargs egrep -n 'IndexError|KeyError'
302 local status=$?
303
304 echo
305
306 # 4 occurrences
307 # source builtin, core/process.py, etc.
308
309 metrics/source-code.sh osh-files \
310 | xargs egrep -n 'finally:'
311 #| xargs egrep -n -A 1 'finally:'
312}
313
314run-task "$@"