diff --git a/infer/lib/python/infer b/infer/lib/python/infer index 367b072f3..530c27b07 100755 --- a/infer/lib/python/infer +++ b/infer/lib/python/infer @@ -15,7 +15,7 @@ import sys import inferlib from inferlib import analyze, config, utils - +from inferlib.capture import make CAPTURE_PACKAGE = 'capture' @@ -32,7 +32,7 @@ MODULE_TO_COMMAND = { 'buck': ['buck'], 'gradle': ['gradle', 'gradlew'], 'javac': ['javac'], - 'make': ['make', 'clang', 'clang++', 'cc', 'gcc', 'g++'], + 'make': make.SUPPORTED_COMMANDS, 'xcodebuild': ['xcodebuild'], 'mvn': ['mvn'] } diff --git a/infer/lib/python/inferlib/capture/make.py b/infer/lib/python/inferlib/capture/make.py index ca0ac3d0b..04e133afe 100644 --- a/infer/lib/python/inferlib/capture/make.py +++ b/infer/lib/python/inferlib/capture/make.py @@ -25,6 +25,9 @@ infer -- make all infer -- clang -c srcfile.m infer -- gcc -c srcfile.c''' +ALIASED_COMMANDS = ['clang', 'clang++', 'cc', 'gcc', 'g++'] +BUILD_COMMANDS = ['make', 'waf'] +SUPPORTED_COMMANDS = ALIASED_COMMANDS + BUILD_COMMANDS def gen_instance(*args): return MakeCapture(*args) @@ -37,7 +40,12 @@ create_argparser = \ class MakeCapture: def __init__(self, args, cmd): self.args = args - self.cmd = [os.path.basename(cmd[0])] + cmd[1:] + self.cmd = cmd + command_name = os.path.basename(cmd[0]) + if command_name in ALIASED_COMMANDS: + # remove absolute paths for these commands as we want to + # substitue our own wrappers instead using a PATH trick + cmd[0] = command_name def get_envvars(self): env_vars = dict(os.environ) diff --git a/infer/tests/build_systems/build_integration_tests.py b/infer/tests/build_systems/build_integration_tests.py index 41308bdc3..3ec94f43f 100755 --- a/infer/tests/build_systems/build_integration_tests.py +++ b/infer/tests/build_systems/build_integration_tests.py @@ -54,7 +54,7 @@ REPORT_FIELDS = [ CODETOANALYZE_DIR = os.path.join(SCRIPT_DIR, 'codetoanalyze') EXPECTED_OUTPUTS_DIR = os.path.join(SCRIPT_DIR, 'expected_outputs') -ALL_TESTS = ['ant', 'buck', 'gradle', 'make', 'locale'] +ALL_TESTS = ['ant', 'buck', 'gradle', 'make', 'locale', 'waf'] to_test = ALL_TESTS @@ -272,6 +272,21 @@ class BuildIntegrationTest(unittest.TestCase): original = os.path.join(EXPECTED_OUTPUTS_DIR, 'locale_report.json') do_test(errors, original) + def test_waf_integration(self): + if 'waf' not in to_test: + print('\nSkipping waf integration test') + return + + print('\nRunning waf integration test') + root = os.path.join(CODETOANALYZE_DIR, 'make') + errors = run_analysis( + root, + ['make', 'clean'], + ['./waf', 'build'], + INFER_EXECUTABLE) + original = os.path.join(EXPECTED_OUTPUTS_DIR, 'waf_report.json') + do_test(errors, original) + if __name__ == '__main__': # hackish capturing of the arguments after '--' diff --git a/infer/tests/build_systems/codetoanalyze/make/waf b/infer/tests/build_systems/codetoanalyze/make/waf new file mode 100755 index 000000000..e55efea07 --- /dev/null +++ b/infer/tests/build_systems/codetoanalyze/make/waf @@ -0,0 +1,11 @@ +#!/bin/sh + +# mock waf implementation + +# waf likes to hardcode paths to compilers during ./waf configure, so +# you should run `infer -- ./waf configure` to capture compiler calls +my_cc=$(which gcc) + +echo "compiling" +# compile everything with the hardcoded compiler +make CC="$my_cc" diff --git a/infer/tests/build_systems/expected_outputs/waf_report.json b/infer/tests/build_systems/expected_outputs/waf_report.json new file mode 100644 index 000000000..b43a81460 --- /dev/null +++ b/infer/tests/build_systems/expected_outputs/waf_report.json @@ -0,0 +1,7 @@ +[ + { + "bug_type": "NULL_DEREFERENCE", + "file": "utf8_in_function_names.c", + "procedure": "test_\uc131\uacf5" + } +] \ No newline at end of file