-
Notifications
You must be signed in to change notification settings - Fork 263
/
codereview.cmake
440 lines (405 loc) · 18.6 KB
/
codereview.cmake
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
# **********************************************************
# Copyright (c) 2010 VMware, Inc. All rights reserved.
# **********************************************************
# Dr. Memory: the memory debugger
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation;
# version 2.1 of the License, and no later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Library General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
# **********************************************************
# Copyright (c) 2009-2010 VMware, Inc. All rights reserved.
# **********************************************************
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# * Redistributions of source code must retain the above copyright notice,
# this list of conditions and the following disclaimer.
#
# * Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
#
# * Neither the name of VMware, Inc. nor the names of its contributors may be
# used to endorse or promote products derived from this software without
# specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL VMWARE, INC. OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
# DAMAGE.
###########################################################################
# Code Review Automation
# Based on DynamoRIO's script of the same name
#
# See CodeReviews.wiki for full discussion of our code review process.
# To summarize: reviewer commits to /reviews/<user>/<year>/i###-descr.diff
# with a commit log that auto-opens an Issue
# Inline comments for a regular patch diff should be the norm for now
# FIXME: we should also produce web diffs for quick reviews
# FIXME: xref discussion on using Google Code's post-commit review options
#
# Usage: prepare a notes file with comments for the reviewer after a line
# beginning "toreview:", and point to it w/ the NOTES variable
# (defult is ./diff.notes)
#
# Since we no longer invoke "make" from the soure dir (due to cmake: i#19, i#64),
# we have to invoke this explicitly "cmake -P ./codereview.cmake".
#
# Parameter list:
#
# REVIEWS:PATH = Checkout of https://drmemory.googlecode.com/svn/reviews
# NOTES:FILEPATH = File containing notes for this review.
# It must contain the string label "toreview:".
# Content prior to the label is discarded (private notes).
# Content after the label is included in the review request.
# LABEL:STRING = Name for review. Should follow "i###-descr" format where
# ### is the Issue number. For example: "i64-cmake-review".
# AUTHOR:STRING = Username on https://drmemory.googlecode.com/svn
# REVIEWER:STRING = Username on https://drmemory.googlecode.com/svn
# REVERT:BOOL = Set to ON to revert locally-added files
# COMMIT:BOOL = Set to ON to officially commit the review request
# (the default is to simply create and locally add the
# .diff and .notes files)
#
# Examples:
#
# Dry run to ensure diff and notes file are as desired:
# > cmake -DAUTHOR:STRING=derek.bruening -DREVIEWER:STRING=qin.zhao -DLABEL:STRING=i64-cmake-review -P ./codereview.cmake
# -- notes file is "diff.notes"
# -- destination is "../reviews/derek.bruening/2009/i64-cmake-review.{diff,notes}"
# -- svn: A ../reviews/derek.bruening/2009/i64-cmake-review.diff
# -- svn: A ../reviews/derek.bruening/2009/i64-cmake-review.notes
# -- ready to commit
#
# Want to abort (maybe decided to change LABEL) so undoing local svn add:
# > cmake -DAUTHOR:STRING=derek.bruening -DREVIEWER:STRING=qin.zhao -DLABEL:STRING=i64-cmake-review -DREVERT:BOOL=ON -P ./codereview.cmake
# -- notes file is "diff.notes"
# -- destination is "../reviews/derek.bruening/2009/i64-cmake-review.{diff,notes}"
# -- svn: D ../reviews/derek.bruening/2009/i64-cmake-review.diff
# -- svn: D ../reviews/derek.bruening/2009/i64-cmake-review.notes
# -- revert complete
#
# Ready to commit:
# > cmake -DAUTHOR:STRING=derek.bruening -DREVIEWER:STRING=qin.zhao -DLABEL:STRING=i64-cmake-review -DCOMMIT:BOOL=ON -P ./codereview.cmake
# -- notes file is "diff.notes"
# -- destination is "../reviews/derek.bruening/2009/i64-cmake-review.{diff,notes}"
# -- svn: A ../reviews/derek.bruening/2009/i64-cmake-review.diff
# -- svn: A ../reviews/derek.bruening/2009/i64-cmake-review.notes
# -- svn: Sending derek.bruening/2009/i64-cmake-review.diff
# -- svn: Sending derek.bruening/2009/i64-cmake-review.notes
# -- svn: Transmitting file data .
# -- svn: Committed revision 75.
# -- committed
#
# Note that you can make a cmake script that sets common variables and point at it
# with the -C parameter to cmake.
# FIXME i#66: should include pre-commit regression test suite results here
if (NOT DEFINED LABEL)
message(FATAL_ERROR "must set LABEL variable")
endif (NOT DEFINED LABEL)
if (NOT DEFINED AUTHOR)
message(FATAL_ERROR "must set AUTHOR variable")
endif (NOT DEFINED AUTHOR)
if (NOT DEFINED REVIEWER)
message(FATAL_ERROR "must set REVIEWER variable")
endif (NOT DEFINED REVIEWER)
if (NOT DEFINED NOTES)
set(NOTES diff.notes)
endif (NOT DEFINED NOTES)
if (NOT EXISTS ${NOTES})
message(FATAL_ERROR "cannot find NOTES=\"${NOTES}\"")
endif (NOT EXISTS ${NOTES})
message(STATUS "notes file is \"${NOTES}\"")
if (NOT DEFINED REVIEWS)
set(REVIEWS ../reviews)
endif (NOT DEFINED REVIEWS)
if (NOT EXISTS ${REVIEWS})
message(FATAL_ERROR "cannot find REVIEWS=\"${REVIEWS}\"")
endif (NOT EXISTS ${REVIEWS})
find_program(SVN svn DOC "subversion client")
if (NOT SVN)
message(FATAL_ERROR "svn not found")
endif (NOT SVN)
# we run svn in the review checkout
function(run_svn)
execute_process(COMMAND ${SVN} ${ARGV}
WORKING_DIRECTORY ${REVIEWS}
RESULT_VARIABLE svn_result
ERROR_VARIABLE svn_err
OUTPUT_VARIABLE svn_out)
if (svn_result OR svn_err)
message(FATAL_ERROR "*** ${SVN} ${ARGV} failed: ***\n${svn_err}")
endif (svn_result OR svn_err)
string(STRIP "${svn_out}" svn_out)
string(REGEX REPLACE "\r?\n" "\n-- svn: " svn_out "${svn_out}")
message(STATUS "svn: ${svn_out}")
endfunction(run_svn)
find_program(DIFFSTAT diffstat DOC "diff statistics displayer")
if (NOT DIFFSTAT)
message(STATUS "diffstat not found: will not have diff statistics")
endif (NOT DIFFSTAT)
# get the year
if (UNIX)
find_program(DATE date DOC "unix date")
if (NOT DATE)
message(FATAL_ERROR "date not found")
endif (NOT DATE)
execute_process(COMMAND ${DATE} +%Y
RESULT_VARIABLE date_result
ERROR_VARIABLE date_err
OUTPUT_VARIABLE year)
if (date_result OR date_err)
message(FATAL_ERROR "*** ${DATE} failed: ***\n${date_err}")
endif (date_result OR date_err)
string(STRIP "${year}" year)
else (UNIX)
find_program(CMD cmd DOC "cmd shell")
if (NOT CMD)
message(FATAL_ERROR "cmd not found")
endif (NOT CMD)
# If use forward slashes => "The syntax of the command is incorrect."
file(TO_NATIVE_PATH "${CMD}" CMD)
execute_process(COMMAND ${CMD} /c date /T
RESULT_VARIABLE date_result
ERROR_VARIABLE date_err
OUTPUT_VARIABLE date_out)
if (date_result OR date_err)
message(FATAL_ERROR "*** ${CMD} /c date /T failed: ***\n${date_err}")
endif (date_result OR date_err)
string(STRIP "${date_out}" date_out)
# result should be like this: "Fri 03/06/2009"
# cmake regexp doesn't have {N}
string(REGEX REPLACE "^....../../([0-9][0-9][0-9][0-9])" "\\1" year "${date_out}")
endif (UNIX)
# we run from the REVIEWS dir so no need to include it here
set(DEST_BASE "${AUTHOR}/${year}")
set(DEST "${DEST_BASE}/${LABEL}")
message(STATUS "destination is \"${REVIEWS}/${DEST}.{diff,notes}\"")
if (REVERT)
execute_process(COMMAND ${SVN} status ${DEST}.diff
WORKING_DIRECTORY ${REVIEWS}
RESULT_VARIABLE svn_result
ERROR_VARIABLE svn_err
OUTPUT_VARIABLE svn_out)
if (svn_result OR svn_err)
message(FATAL_ERROR "*** ${SVN} status failed: ***\n${svn_err}")
endif (svn_result OR svn_err)
if ("${svn_out}" MATCHES "^\\A")
# svn revert doesn't delete local copy if new => svn delete
# FIXME: if AUTHOR was mistyped should we remove the directories too?
run_svn("delete;--force;${DEST}.diff;${DEST}.notes")
else ("${svn_out}" MATCHES "^\\A")
run_svn("revert;${DEST}.diff;${DEST}.notes")
endif ("${svn_out}" MATCHES "^\\A")
message(STATUS "revert complete")
else (REVERT)
if (NOT EXISTS "${REVIEWS}/${DEST_BASE}")
if (NOT EXISTS "${REVIEWS}/${AUTHOR}")
run_svn("mkdir;${AUTHOR}")
endif (NOT EXISTS "${REVIEWS}/${AUTHOR}")
run_svn("mkdir;${DEST_BASE}")
endif (NOT EXISTS "${REVIEWS}/${DEST_BASE}")
set(NOTES_LOCAL "${DEST}.notes")
set(DIFF_LOCAL "${DEST}.diff")
set(NOTES_FILE "${REVIEWS}/${NOTES_LOCAL}")
set(DIFF_FILE "${REVIEWS}/${DIFF_LOCAL}")
# If someone manually created these files then this script
# will fail: we assume not already there if haven't been
# added to svn yet.
if (NOT EXISTS "${DIFF_FILE}")
file(WRITE "${DIFF_FILE}" "")
run_svn("add;${DIFF_LOCAL}")
endif (NOT EXISTS "${DIFF_FILE}")
if (NOT EXISTS "${NOTES_FILE}")
file(WRITE "${NOTES_FILE}" "")
run_svn("add;${NOTES_LOCAL}")
endif (NOT EXISTS "${NOTES_FILE}")
# We want context diffs with procedure names for better readability
# svn diff does show new files for us but we pass -N just in case
execute_process(COMMAND ${SVN} diff --diff-cmd diff -x "-c -p -N"
RESULT_VARIABLE svn_result
ERROR_VARIABLE svn_err
OUTPUT_FILE "${DIFF_FILE}")
if (svn_result OR svn_err)
message(FATAL_ERROR "*** ${SVN} diff failed: ***\n${svn_err}")
endif (svn_result OR svn_err)
file(READ ${NOTES} string)
string(REGEX REPLACE "^.*\ntoreview:\r?\n" "" pubnotes "${string}")
string(REGEX REPLACE "^toreview:\r?\n" "" pubnotes "${pubnotes}")
if ("${string}" STREQUAL "${pubnotes}")
message(FATAL_ERROR "${NOTES} is missing \"toreview:\" marker")
endif ("${string}" STREQUAL "${pubnotes}")
# Do "wc -l" ourselves
file(READ "${DIFF_FILE}" string)
string(REGEX MATCHALL "\n" newlines "${string}")
list(LENGTH newlines lines )
##################################################
# i#78: coding style checks
#
# Since these are on the diff, not the code, remember that there
# are two extra columns at the start of the line!
set(ugly "*** STYLE VIOLATION:")
# the old "make ugly" rules
# to make the regexps simpler I'm assuming no non-alphanums run up
# against keywords: assuming spaces delimit
string(REGEX MATCH "(TRACELOG\\([^l]|NOCHECKIN)" bad "${string}")
if (bad)
message("${ugly} remove debugging facilities: \"${bad}\"")
endif (bad)
string(REGEX MATCH "DO_ONCE\\( *{? *SYSLOG_INTERNAL" bad "${string}")
if (bad)
message("${ugly} use SYSLOG_INTERNAL_*_ONCE, not DO_ONCE(SYSLOG_: \"${bad}\"")
endif (bad)
# note that ASSERT_NOT_TESTED is already DO_ONCE so shouldn't see DO_ONCE on any ASSERT
string(REGEX MATCH "ONCE\\( *{? *ASSERT" bad "${string}")
if (bad)
message("${ugly} use ASSERT_CURIOSITY_ONCE, not DO_ONCE(ASSERT: \"${bad}\"")
endif (bad)
string(REGEX MATCH "\n[^-\\*][^\n]*\t" bad "${string}")
if (bad)
message("${ugly} clean TABs w/ M-x untabify (cat -T | grep \"\^I\" to see)")
endif (bad)
string(REGEX MATCH " *(if|while) *[^{\n]*\r?\n[^;\n]*\r?\n" bad "${string}")
if (bad)
message("${ugly} use {} for multi-line body: \"${bad}\"")
endif (bad)
string(REGEX MATCH " if *\\([^\n]*;" bad "${string}")
if (bad)
message("${ugly} put if body on separate line: \"${bad}\"")
endif (bad)
# loop bodies
string(REGEX MATCH "\n[^}]* while *\\([^\n]*;" bad "${string}")
if (bad)
message("${ugly} put loop body on separate line: \"${bad}\"")
endif (bad)
string(REGEX MATCH "\n[^ ]+} *while *\\([^\n]*;" bad "${string}")
if (bad)
message("${ugly} put loop body on separate line: \"${bad}\"")
endif (bad)
string(REGEX MATCH " do *{[^\n]*;" bad "${string}")
if (bad)
message("${ugly} put loop body on separate line: \"${bad}\"")
endif (bad)
string(REGEX MATCH " for *([^\n]*;[^\n]*;[^\n]*)[^\n]*;" bad "${string}")
if (bad)
message("${ugly} put loop body on separate line: \"${bad}\"")
endif (bad)
string(REGEX MATCH "\\( *([0-9]+|NULL) *== *[^0-9]" bad "${string}")
if (bad)
message("${ugly} (1 == var) vs. (var == 1): \"${bad}\"")
endif (bad)
# check that empty param list is declared void
# FIXME: should we only require for decls by only running on header files?
# But we do want to catch static decls.
string(REGEX MATCH "\n.. *[A-Za-z0-9][A-Za-z0-9_\\* ]+\\(\\);" bad "${string}")
if (bad)
message("${ugly} empty param list should be (void) not (): \"${bad}\"")
endif (bad)
# the old "make pretty_ugly"
# since we're only checking the diff and not the existing core we
# go ahead and apply all the rules
# Rules that should pass (and almost do, all have only a few violations left and
# no false positives). FIXME - should be cleaned up and moved to ugly xref 8806.
string(REGEX MATCH " (do|if|while|for|else|switch)\\(" bad "${string}")
if (bad)
message("${ugly} spacing, if( or if ( vs if (: \"${bad}\"")
endif (bad)
string(REGEX MATCH "[}\\);]( +)?(do|if|while|for|else|switch)[ \n]" bad "${string}")
if (bad)
message("${ugly} spacing, }else or } else vs } else: \"${bad}\"")
endif (bad)
# The old "really_ugly": see comments above.
# Rules that don't pass at all, either because of excessive violations in the
# codebase or because of false positives. FIXME - for rules with few or no false
# positives but numerous violations we could use a count or something to try and
# keep things from getting worse xref case 8806.
# no false positives, but numerous violations
# that's right, cmake regexp has no {N} so we do it the hard way
string(REGEX MATCH "\n[^\n][^\n][^\n][^\n][^\n][^\n][^\n][^\n][^\n][^\n][^\n][^\n][^\n][^\n][^\n][^\n][^\n][^\n][^\n][^\n][^\n][^\n][^\n][^\n][^\n][^\n][^\n][^\n][^\n][^\n][^\n][^\n][^\n][^\n][^\n][^\n][^\n][^\n][^\n][^\n][^\n][^\n][^\n][^\n][^\n][^\n][^\n][^\n][^\n][^\n][^\n][^\n][^\n][^\n][^\n][^\n][^\n][^\n][^\n][^\n][^\n][^\n][^\n][^\n][^\n][^\n][^\n][^\n][^\n][^\n][^\n][^\n][^\n][^\n][^\n][^\n][^\n][^\n][^\n][^\n][^\n][^\n][^\n][^\n][^\n][^\n][^\n][^\n][^\n][^\n][^\n][^\n]" bad "${string}")
if (bad)
message("${ugly} line is too long: \"${bad}\"")
endif (bad)
# FIXME: not bothering to try and catch multiple statements on one line
# at this time: look at old core/Makefile if interested. Keeping comment below
# for all the issues:
#
# With all the exemptions very few false positives, but still many violations
# (and prob. some false negatives). The first grep looks for lines with two ; not
# within quotes. 2nd, 3rd and 4th greps subtract out lines where the extra
# semicolon is prob. part of a comment. NOTE, for some reason doesn't
# work for matching before the * in the 3rd grep. 5th grep subtracts out for (;;)
# loop usage and 6th grep subtracts out the very common 'case: foo; break;' motif
# (even though it technically breaks the rule). Final grep subtracts out certain
# common macros whose usage often breaks the rule.
# only a few false positives, but numerous violations
string(REGEX MATCH "[A-Za-z0-9]\\*[^A-Za-z0-9/\\)\\(]" bad "${string}")
if (bad)
message("${ugly} instr_t* foo vs. instr_t *foo, etc.: \"${bad}\"")
endif (bad)
# only a few false positives, but numerous violations (even allowing for usage
# inside /* */ comments)
string(REGEX MATCH "//" bad "${string}")
if (bad)
message("${ugly} use C style comments: \"${bad}\"")
endif (bad)
# mostly false positives (we're too grammatical ;) due to using ; in comments
string(REGEX MATCH "/\\*[^\\*].*;[^\\*]\\*/" bad "${string}")
if (bad)
message("${ugly} no commented-out code: \"${bad}\"")
endif (bad)
# some false positives and numerous violations
string(REGEX MATCH "\n[^\\*\n]*long " bad "${string}")
if (bad)
message("${ugly} use int/uint instead of long/ulong: \"${bad}\"")
endif (bad)
# Only a few false positives and not that many violations (for the "( " rule at
# least). FIXME - these could probably be moved to pretty_ugly at some point.
string(REGEX MATCH "\\( +[^\\ ]+" bad "${string}")
if (bad)
message("${ugly} spacing, ( foo, bar ) vs. (foo, bar): \"${bad}\"")
endif (bad)
string(REGEX MATCH " \\)" bad "${string}")
if (bad)
message("${ugly} spacing, ( foo, bar ) vs. (foo, bar): \"${bad}\"")
endif (bad)
# no real false positives, but tons of violations (even ignoring asm listings)
string(REGEX MATCH "\n[^\\*-][^\n]*,[^ \r\n]" bad "${string}")
if (bad)
message("${ugly} put spaces after commas: \"${bad}\"")
endif (bad)
#
##################################################
# We commit the diff to review/ using special syntax to auto-generate
# an Issue that covers performing the review
file(WRITE "${NOTES_FILE}"
"new review:\nowner: ${REVIEWER}\nsummary: ${AUTHOR}/${year}/${LABEL}.diff\n")
file(APPEND "${NOTES_FILE}" "${pubnotes}")
file(APPEND "${NOTES_FILE}" "\nstats: ${lines} diff lines\n")
if (EXISTS "${DIFFSTAT}")
execute_process(COMMAND ${DIFFSTAT} "${DIFF_FILE}"
ERROR_QUIET OUTPUT_VARIABLE diffstat_out)
file(APPEND "${NOTES_FILE}" "${diffstat_out}")
endif (EXISTS "${DIFFSTAT}")
message(STATUS "ready to commit")
if (COMMIT)
run_svn("commit;--force-log;-F;${NOTES_LOCAL}")
message(STATUS "committed")
endif (COMMIT)
endif (REVERT)