7 from subprocess
import call
9 SCRIPTS_DIR
= os
.path
.dirname(os
.path
.realpath(__file__
))
10 PROJECTS_DIR
= os
.path
.join(SCRIPTS_DIR
, "projects")
11 DEFAULT_LLVM_DIR
= os
.path
.realpath(
12 os
.path
.join(SCRIPTS_DIR
, os
.path
.pardir
, os
.path
.pardir
, os
.path
.pardir
)
16 def add(parser
, args
):
18 from ProjectMap
import ProjectInfo
20 if args
.source
== "git" and (args
.origin
== "" or args
.commit
== ""):
21 parser
.error("Please provide both --origin and --commit if source is 'git'")
23 if args
.source
!= "git" and (args
.origin
!= "" or args
.commit
!= ""):
25 "Options --origin and --commit don't make sense when " "source is not 'git'"
28 project
= ProjectInfo(
29 args
.name
[0], args
.mode
, args
.source
, args
.origin
, args
.commit
32 SATestAdd
.add_new_project(project
)
35 def build(parser
, args
):
38 SATestBuild
.VERBOSE
= args
.verbose
40 projects
= get_projects(parser
, args
)
41 tester
= SATestBuild
.RegressionTester(
44 args
.override_compiler
,
45 args
.extra_analyzer_config
,
50 tests_passed
= tester
.test_all()
53 sys
.stderr
.write("ERROR: Tests failed.\n")
57 def compare(parser
, args
):
61 CmpRuns
.HistogramType
.RELATIVE
.value
,
62 CmpRuns
.HistogramType
.LOG_RELATIVE
.value
,
63 CmpRuns
.HistogramType
.ABSOLUTE
.value
,
66 if args
.histogram
is not None and args
.histogram
not in choices
:
68 "Incorrect histogram type, available choices are {}".format(choices
)
71 dir_old
= CmpRuns
.ResultsDirectory(args
.old
[0], args
.root_old
)
72 dir_new
= CmpRuns
.ResultsDirectory(args
.new
[0], args
.root_new
)
74 CmpRuns
.dump_scan_build_results_diff(
77 show_stats
=args
.show_stats
,
78 stats_only
=args
.stats_only
,
79 histogram
=args
.histogram
,
80 verbose_log
=args
.verbose_log
,
84 def update(parser
, args
):
85 import SATestUpdateDiffs
86 from ProjectMap
import ProjectMap
88 project_map
= ProjectMap()
89 for project
in project_map
.projects
:
90 SATestUpdateDiffs
.update_reference_results(project
, args
.git
)
93 def benchmark(parser
, args
):
94 from SATestBenchmark
import Benchmark
96 projects
= get_projects(parser
, args
)
97 benchmark
= Benchmark(projects
, args
.iterations
, args
.output
)
101 def benchmark_compare(parser
, args
):
102 import SATestBenchmark
104 SATestBenchmark
.compare(args
.old
, args
.new
, args
.output
)
107 def get_projects(parser
, args
):
108 from ProjectMap
import ProjectMap
, Size
110 project_map
= ProjectMap()
111 projects
= project_map
.projects
113 def filter_projects(projects
, predicate
, force
=False):
116 enabled
=(force
or project
.enabled
) and predicate(project
)
118 for project
in projects
122 projects_arg
= args
.projects
.split(",")
123 available_projects
= [project
.name
for project
in projects
]
125 # validate that given projects are present in the project map file
126 for manual_project
in projects_arg
:
127 if manual_project
not in available_projects
:
129 "Project '{project}' is not found in "
130 "the project map file. Available projects are "
131 "{all}.".format(project
=manual_project
, all
=available_projects
)
134 projects
= filter_projects(
135 projects
, lambda project
: project
.name
in projects_arg
, force
=True
139 max_size
= Size
.from_str(args
.max_size
)
140 except ValueError as e
:
141 parser
.error("{}".format(e
))
143 projects
= filter_projects(projects
, lambda project
: project
.size
<= max_size
)
148 def docker(parser
, args
):
149 if len(args
.rest
) > 0:
150 if args
.rest
[0] != "--":
151 parser
.error("REST arguments should start with '--'")
152 args
.rest
= args
.rest
[1:]
159 sys
.exit(docker_run(args
, " ".join(args
.rest
)))
162 def docker_build_image():
163 sys
.exit(call("docker build --tag satest-image {}".format(SCRIPTS_DIR
), shell
=True))
166 def docker_shell(args
):
168 # First we need to start the docker container in a waiting mode,
169 # so it doesn't do anything, but most importantly keeps working
170 # while the shell session is in progress.
171 docker_run(args
, "--wait", "--detach")
172 # Since the docker container is running, we can actually connect to it
173 call("docker exec -it satest bash", shell
=True)
175 except KeyboardInterrupt:
182 def docker_run(args
, command
, docker_args
=""):
185 "docker run --rm --name satest "
186 "-v {llvm}:/llvm-project "
188 "-v {clang}:/analyzer "
189 "-v {scripts}:/scripts "
190 "-v {projects}:/projects "
192 "satest-image:latest {command}".format(
193 llvm
=args
.llvm_project_dir
,
194 build
=args
.build_dir
,
195 clang
=args
.clang_dir
,
197 projects
=PROJECTS_DIR
,
198 docker_args
=docker_args
,
204 except KeyboardInterrupt:
208 def docker_cleanup():
209 print("Please wait for docker to clean up")
210 call("docker stop satest", shell
=True)
214 parser
= argparse
.ArgumentParser()
215 subparsers
= parser
.add_subparsers()
218 add_parser
= subparsers
.add_parser(
219 "add", help="Add a new project for the analyzer testing."
221 # TODO: Add an option not to build.
222 # TODO: Set the path to the Repository directory.
223 add_parser
.add_argument("name", nargs
=1, help="Name of the new project")
224 add_parser
.add_argument(
230 help="Build mode: 0 for single file project, "
232 "2 for single file c++11 project",
234 add_parser
.add_argument(
238 choices
=["script", "git", "zip"],
239 help="Source type of the new project: "
240 "'git' for getting from git "
241 "(please provide --origin and --commit), "
242 "'zip' for unpacking source from a zip file, "
243 "'script' for downloading source by running "
246 add_parser
.add_argument(
247 "--origin", action
="store", default
="", help="Origin link for a git repository"
249 add_parser
.add_argument(
250 "--commit", action
="store", default
="", help="Git hash for a commit to checkout"
252 add_parser
.set_defaults(func
=add
)
255 build_parser
= subparsers
.add_parser(
257 help="Build projects from the project map and compare results with "
260 build_parser
.add_argument(
265 help="0 to fail on runtime errors, 1 to fail "
266 "when the number of found bugs are different "
267 "from the reference, 2 to fail on any "
268 "difference from the reference. Default is 0.",
270 build_parser
.add_argument(
275 help="Regenerate reference output.",
277 build_parser
.add_argument(
278 "--override-compiler",
281 help="Call scan-build with " "--override-compiler option.",
283 build_parser
.add_argument(
289 help="Number of projects to test concurrently",
291 build_parser
.add_argument(
292 "--extra-analyzer-config",
293 dest
="extra_analyzer_config",
296 help="Arguments passed to to -analyzer-config",
298 build_parser
.add_argument(
300 dest
="extra_checkers",
303 help="Extra checkers to enable",
305 build_parser
.add_argument(
309 help="Comma-separated list of projects to test",
311 build_parser
.add_argument(
315 help="Maximum size for the projects to test",
317 build_parser
.add_argument("-v", "--verbose", action
="count", default
=0)
318 build_parser
.set_defaults(func
=build
)
321 cmp_parser
= subparsers
.add_parser(
323 help="Comparing two static analyzer runs in terms of "
324 "reported warnings and execution time statistics.",
326 cmp_parser
.add_argument(
329 help="Prefix to ignore on source files for " "OLD directory",
334 cmp_parser
.add_argument(
337 help="Prefix to ignore on source files for " "NEW directory",
342 cmp_parser
.add_argument(
345 help="Write additional information to LOG " "[default=None]",
351 cmp_parser
.add_argument(
356 help="Only show statistics on reports",
358 cmp_parser
.add_argument(
363 help="Show change in statistics",
365 cmp_parser
.add_argument(
369 help="Show histogram of paths differences. " "Requires matplotlib",
371 cmp_parser
.add_argument("old", nargs
=1, help="Directory with old results")
372 cmp_parser
.add_argument("new", nargs
=1, help="Directory with new results")
373 cmp_parser
.set_defaults(func
=compare
)
376 upd_parser
= subparsers
.add_parser(
378 help="Update static analyzer reference results based on the previous "
379 "run of SATest build. Assumes that SATest build was just run.",
381 upd_parser
.add_argument(
382 "--git", action
="store_true", help="Stage updated results using git."
384 upd_parser
.set_defaults(func
=update
)
387 dock_parser
= subparsers
.add_parser(
388 "docker", help="Run regression system in the docker."
391 dock_parser
.add_argument(
394 help="Build docker image for running tests.",
396 dock_parser
.add_argument(
397 "--shell", action
="store_true", help="Start a shell on docker."
399 dock_parser
.add_argument(
400 "--llvm-project-dir",
402 default
=DEFAULT_LLVM_DIR
,
403 help="Path to LLVM source code. Defaults "
404 "to the repo where this script is located. ",
406 dock_parser
.add_argument(
410 help="Path to a directory where docker should " "build LLVM code.",
412 dock_parser
.add_argument(
416 help="Path to find/install LLVM installation.",
418 dock_parser
.add_argument(
420 nargs
=argparse
.REMAINDER
,
422 help="Additional args that will be forwarded " "to the docker's entrypoint.",
424 dock_parser
.set_defaults(func
=docker
)
426 # benchmark subcommand
427 bench_parser
= subparsers
.add_parser(
428 "benchmark", help="Run benchmarks by building a set of projects multiple times."
431 bench_parser
.add_argument(
437 help="Number of iterations for building each " "project.",
439 bench_parser
.add_argument(
443 default
="benchmark.csv",
444 help="Output csv file for the benchmark results",
446 bench_parser
.add_argument(
450 help="Comma-separated list of projects to test",
452 bench_parser
.add_argument(
456 help="Maximum size for the projects to test",
458 bench_parser
.set_defaults(func
=benchmark
)
460 bench_subparsers
= bench_parser
.add_subparsers()
461 bench_compare_parser
= bench_subparsers
.add_parser(
462 "compare", help="Compare benchmark runs."
464 bench_compare_parser
.add_argument(
468 help="Benchmark reference results to " "compare agains.",
470 bench_compare_parser
.add_argument(
471 "--new", action
="store", required
=True, help="New benchmark results to check."
473 bench_compare_parser
.add_argument(
474 "-o", "--output", action
="store", required
=True, help="Output file for plots."
476 bench_compare_parser
.set_defaults(func
=benchmark_compare
)
478 args
= parser
.parse_args()
479 args
.func(parser
, args
)
482 if __name__
== "__main__":