Skip to content

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
tim-mccarthy committed Feb 19, 2016
0 parents commit 680130b
Show file tree
Hide file tree
Showing 18 changed files with 874 additions and 0 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
*.pyc
17 changes: 17 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
Running
=======

To run, add bin/ to your PATH and invoke from the directory of the project you want to analyze:

dljc -o logs -- ant build

Where "ant build" is replaced by whatever command builds your project. Output will be emitted to logs/toplevel.log

You may also run a checking tool on the discovered java files, by invoking with the -t option and a tool to use (e.g. "-t soot", "-t inference" or "-t checker").

LICENSE
=======

Parts of the code in this directory were taken from the Facebook Infer project. Its license is available at

https://github.com/facebook/infer/blob/master/LICENSE
141 changes: 141 additions & 0 deletions bin/arg.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
import argparse
import os
import sys
import imp

DEFAULT_OUTPUT_DIRECTORY = os.path.join(os.getcwd(), 'dljc-out')

# token that identifies the end of the options for do-like-javac and the beginning
# of the compilation command
CMD_MARKER = '--'

# insert here the correspondence between module name and the list of
# compiler/build-systems it handles.
# All supported commands should be listed here
MODULE_TO_COMMAND = {
'javac': ['javac'],
'ant': ['ant'],
'gradle': ['gradle', 'gradlew'],
'mvn': ['mvn']
}

CAPTURE_PACKAGE = 'capture'
LIB_FOLDER = os.path.join(
os.path.dirname(os.path.realpath(__file__)), os.path.pardir, 'lib')

class AbsolutePathAction(argparse.Action):
"""Convert a path from relative to absolute in the arg parser"""
def __call__(self, parser, namespace, values, option_string=None):
setattr(namespace, self.dest, os.path.abspath(values))

base_parser = argparse.ArgumentParser(add_help=False)
base_group = base_parser.add_argument_group('global arguments')
base_group.add_argument('-o', '--out', metavar='<directory>',
default=DEFAULT_OUTPUT_DIRECTORY, dest='output_directory',
action=AbsolutePathAction,
help='Set the results directory')
base_group.add_argument('-t', '--tool', metavar='<tool>',
action='store',default=None,
help='choose a tool to run. Valid tools include soot, checker, and inference.')
base_group.add_argument('-c', '--checker', metavar='<checker>',
action='store',default='NullnessChecker',
help='choose a checker to check')
base_group.add_argument('-s', '--solver', metavar='<solver>',
action='store',default='checkers.inference.solver.DebugSolver',
help='solver to use on constraints')
base_group.add_argument('-afud', '--afuOutputDir', metavar='<afud>',
action='store',default='afud/',
help='Annotation File Utilities output directory')
base_group.add_argument('-m', '--mode', metavar='<mode>',
action='store',default='INFER',
help='Modes of operation: TYPECHECK, INFER, ROUNDTRIP,ROUNDTRIP_TYPECHECK')
base_group.add_argument('-i', '--incremental', action='store_true',
help='''Do not delete the results directory across
runs''')
base_group.add_argument('--log_to_stderr', action='store_true',
help='''When set, all logging will go to stderr instead
of log file''')
base_group.add_argument('-j', '--jar', metavar='<jar>',
action='store',default=None,
help='Set the path to either prog2dfg.jar or apilearner.jar.')

def get_commands():
"""Return all commands that are supported."""
#flatten and dedup the list of commands
return set(sum(MODULE_TO_COMMAND.values(), []))

def get_module_name(command):
""" Return module that is able to handle the command. None if
there is no such module."""
for module, commands in MODULE_TO_COMMAND.iteritems():
if command in commands:
return module
return None

def split_args_to_parse():
dd_index = \
sys.argv.index(CMD_MARKER) if CMD_MARKER in sys.argv else len(sys.argv)

args, cmd = sys.argv[1:dd_index], sys.argv[dd_index + 1:]
capture_module_name = os.path.basename(cmd[0]) if len(cmd) > 0 else None
mod_name = get_module_name(capture_module_name)
return args, cmd, mod_name

def create_argparser(parents=[]):
parser = argparse.ArgumentParser(
parents=[base_parser] + parents,
add_help=False,
formatter_class=argparse.RawDescriptionHelpFormatter,
)
group = parser.add_argument_group(
'supported compiler/build-system commands')

supported_commands = ', '.join(get_commands())
group.add_argument(
CMD_MARKER,
metavar='<cmd>',
dest='nullarg',
default=None,
help=('Command to run the compiler/build-system. '
'Supported build commands (run `do-like-javac.py --help -- <cmd_name>` for '
'extra help, e.g. `do-like-javac.py --help -- ant`): ' + supported_commands),
)
return parser

def load_module(mod_name):
# load the 'capture' package in lib
pkg_info = imp.find_module(CAPTURE_PACKAGE, [LIB_FOLDER])
imported_pkg = imp.load_module(CAPTURE_PACKAGE, *pkg_info)
# load the requested module (e.g. make)
mod_file, mod_path, mod_descr = \
imp.find_module(mod_name, imported_pkg.__path__)
try:
return imp.load_module(
'{pkg}.{mod}'.format(pkg=imported_pkg.__name__, mod=mod_name),
mod_file, mod_path, mod_descr)
finally:
if mod_file:
mod_file.close()

def parse_args():
to_parse, cmd, mod_name = split_args_to_parse()
# get the module name (if any), then load it
imported_module = None
if mod_name:
imported_module = load_module(mod_name)

# get the module's argparser and merge it with the global argparser
module_argparser = []
if imported_module:
module_argparser.append(
imported_module.create_argparser(mod_name)
)
global_argparser = create_argparser(module_argparser)

args = global_argparser.parse_args(to_parse)

if imported_module:
return args, cmd, imported_module
else:
global_argparser.print_help()
sys.exit(os.EX_OK)
26 changes: 26 additions & 0 deletions bin/check.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import logging
import os
import sys
import platform
import pprint
import subprocess
import traceback


def run_checker(javac_commands,args):
# checker-framework javac.
javacheck = os.environ['JSR308']+"/checker-framework/checker/bin/javac"
checker_command = []
checker_command.extend([javacheck])

for jc in javac_commands:
pprint.pformat(jc)
javac_switches = jc['javac_switches']
cp = javac_switches['classpath']
java_files = ' '.join(jc['java_files'])
cmd = checker_command + ["-processor", args.checker, "-classpath", cp, java_files]
print ("Running %s" % cmd)
try:
print (subprocess.check_output(cmd, stderr=subprocess.STDOUT))
except subprocess.CalledProcessError as e:
print e.output
64 changes: 64 additions & 0 deletions bin/dljc
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
#!/usr/bin/env python2.7

import logging
import os
import sys
import platform
import pprint
import arg
import log
import soot
import infer
import check
import jprint
import randoop
import graphtools

def soot_tool(results,jars,args):
soot.run_soot(results)

def checker_tool(results,jars,args):
check.run_checker(results,args)

def inference_tool(results,jars,args):
infer.run_inference(results,args)

def print_tool(results,jars,args):
jprint.run_printer(results, jars)

def randoop_tool(results,jars,args):
randoop.run_randoop(results)

def graph_tool(results,jars,args):
graphtools.run(results,args)


def log_header():
logging.info('Running command %s', ' '.join(sys.argv))
logging.info('Platform: %s', platform.platform())
logging.info('PATH=%s', os.getenv('PATH'))
logging.info('SHELL=%s', os.getenv('SHELL'))
logging.info('PWD=%s', os.getenv('PWD'))

def main():
args, cmd, imported_module = arg.parse_args()
log.configure_logging(args.output_directory, args.incremental, args.log_to_stderr)

log_header()

javac_commands, jars = imported_module.gen_instance(cmd).capture()
logging.info('Results: %s', pprint.pformat(javac_commands))

options = {'soot' : soot_tool,
'checker' : checker_tool,
'inference' : inference_tool,
'print' : print_tool,
'randoop' : randoop_tool,
'graphtool' : graph_tool,
}

if args.tool:
options[args.tool](javac_commands,jars,args)

if __name__ == '__main__':
main()
42 changes: 42 additions & 0 deletions bin/graphtools.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import logging
import os
import sys
import platform
import pprint
import subprocess
import traceback


def run(javac_commands, args):
run_tool(args.jar, javac_commands, args.output_directory)


def run_tool(jarfile, javac_commands, outdir):
# first add the call to the soot jar.
tool_command = []
tool_command.extend(["java", "-jar", jarfile])

pp = pprint.PrettyPrinter(indent=2)
for jc in javac_commands:
pp.pformat(jc)
#jc['java_files']
javac_switches = jc['javac_switches']
class_dir = os.path.abspath(javac_switches['d'])

java_files = jc['java_files']
java_files_file = os.path.join(os.getcwd(), '__java_file_names.txt')
with open(java_files_file, 'w') as f:
for s in java_files:
f.write(s)
f.write("\n")

current_outdir = os.path.join(outdir, class_dir.replace(os.getcwd(),'').replace(os.sep,"_"))

cmd = tool_command + ["-o", current_outdir, "-j", class_dir, "-source", java_files_file ]
print ("Running %s" % ' '.join(cmd))
try:
print (subprocess.check_output(cmd, stderr=subprocess.STDOUT))
except:
print ('calling {cmd} failed\n{trace}'.format(cmd=' '.join(cmd),trace=traceback.format_exc()))


32 changes: 32 additions & 0 deletions bin/infer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import logging
import os
import sys
import platform
import pprint
import subprocess
import traceback


def run_inference(javac_commands,args):

# the dist directory if CFI.
CFI_dist = os.environ['JSR308']+"/checker-framework-inference/dist"
CFI_command = []

CFI_command.extend(["java"])

for jc in javac_commands:
pprint.pformat(jc)
javac_switches = jc['javac_switches']
target_cp = javac_switches['classpath']
java_files = ' '.join(jc['java_files'])
cp = target_cp +":"+ CFI_dist + "/checker.jar:" + CFI_dist + "/plume.jar:" + \
CFI_dist + "/checker-framework-inference.jar"
cmd = CFI_command + ["-classpath", cp, "checkers.inference.InferenceLauncher" ,
"--checker" ,args.checker, "--solver", args.solver ,
"--mode" , args.mode ,"--targetclasspath", target_cp, "-afud", args.afuOutputDir, java_files]
print ("Running %s" % cmd)
try:
print (subprocess.check_output(cmd, stderr=subprocess.STDOUT))
except:
print ('calling {cmd} failed\n{trace}'.format(cmd=' '.join(cmd),trace=traceback.format_exc()))
15 changes: 15 additions & 0 deletions bin/jprint.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import logging
import os
import sys
import platform
import pprint
import subprocess
import traceback

def run_printer(javac_commands, jars):
pp = pprint.PrettyPrinter(indent=2)
for jc in javac_commands:
pp.pprint(jc)
javac_switches = jc['javac_switches']
print("Target JARs (experimental):")
pp.pprint(jars)
40 changes: 40 additions & 0 deletions bin/log.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# Copyright (c) 2013 - present Facebook, Inc.
# All rights reserved.
#
# This source code is licensed under the BSD style license found in the
# LICENSE file in the root directory of this source tree. An additional grant
# of patent rights can be found in the PATENTS file in the same directory.

import os
import shutil
import logging

FORMAT = '[%(levelname)s] %(message)s'
LOG_FILE = 'toplevel.log'

def remove_output_directory(output_directory):
# it is safe to ignore errors here because recreating the
# output_directory will fail later
shutil.rmtree(output_directory, True)

def create_results_dir(results_dir):
try:
os.mkdir(results_dir)
except OSError:
pass

def configure_logging(output_directory, incremental, log_to_stderr):
#if not incremental:
# remove_output_directory(output_directory)

create_results_dir(output_directory)

if log_to_stderr:
logging.basicConfig(level=logging.INFO, format=FORMAT)
else:
logging.basicConfig(level=logging.INFO,
format=FORMAT,
filename=os.path.join(output_directory, LOG_FILE),
filemode='w')

# vim: set sw=4 ts=4 et:
Loading

0 comments on commit 680130b

Please sign in to comment.