diff --git a/.gitignore b/.gitignore index b5764daeb..68e9e51ce 100644 --- a/.gitignore +++ b/.gitignore @@ -84,6 +84,12 @@ buck-out/ /infer/lib/specs/cpp_models /infer/lib/specs/objc_models /infer/lib/specs/clean_models +/infer/lib/wrappers/c++ +/infer/lib/wrappers/cc +/infer/lib/wrappers/clang +/infer/lib/wrappers/clang++ +/infer/lib/wrappers/g++ +/infer/lib/wrappers/gcc /scripts/checkCopyright /infer/etc/clang_ast.dict /infer/src/toplevel.mlpack diff --git a/Makefile b/Makefile index 4744bb173..a0884806e 100644 --- a/Makefile +++ b/Makefile @@ -29,10 +29,20 @@ endif all: infer inferTraceBugs -$(INFERTRACEBUGS_BIN_RELPATH): - ($(REMOVE) $@ && \ - cd $(@D) && \ - $(LN_S) ../lib/python/$(@F) $(@F)) +# executable names that should point to InferClang to trigger capture +INFERCLANG_WRAPPERS_BASENAMES = c++ cc clang clang++ g++ gcc +INFERCLANG_WRAPPERS_PATHS = $(foreach cc,$(INFERCLANG_WRAPPERS_BASENAMES),$(LIB_DIR)/wrappers/$(cc)) + +$(INFERCLANG_WRAPPERS_PATHS): Makefile + $(REMOVE) $@ && \ + cd $(@D) && \ + $(LN_S) ../../bin/InferClang $(@F) + + +$(INFERTRACEBUGS_BIN_RELPATH): Makefile + $(REMOVE) $@ && \ + cd $(@D) && \ + $(LN_S) ../lib/python/$(@F) $(@F) src_build: ifeq ($(IS_FACEBOOK_TREE),yes) @@ -48,6 +58,9 @@ ifeq ($(BUILD_JAVA_ANALYZERS),yes) $(MAKE) -C $(ANNOTATIONS_DIR) endif $(MAKE) -C $(MODELS_DIR) all +ifeq ($(BUILD_C_ANALYZERS),yes) +infer: $(INFERCLANG_WRAPPERS_PATHS) +endif clang_setup: export CC="$(CC)" CFLAGS="$(CFLAGS)"; \ @@ -266,6 +279,10 @@ ifeq ($(BUILD_C_ANALYZERS),yes) @for i in $$(find infer/lib/clang_wrappers/*); do \ $(INSTALL_PROGRAM) -C $$i $(DESTDIR)$(libdir)/infer/$$i; \ done + (cd $(DESTDIR)$(libdir)/infer/infer/lib/wrappers/ && \ + $(REMOVE) $(INFERCLANG_WRAPPERS_BASENAMES) && \ + $(foreach cc,$(INFERCLANG_WRAPPERS_BASENAMES), \ + $(LN_S) ../../bin/InferClang $(cc);)) @for i in $$(find infer/lib/specs/*); do \ $(INSTALL_DATA) -C $$i $(DESTDIR)$(libdir)/infer/$$i; \ done @@ -287,10 +304,9 @@ ifeq ($(BUILD_JAVA_ANALYZERS),yes) $(INSTALL_DATA) -C $$i $(DESTDIR)$(libdir)/infer/$$i; \ done $(INSTALL_PROGRAM) -C $(INFERJAVA_BIN) $(DESTDIR)$(libdir)/infer/infer/bin/ + $(INSTALL_PROGRAM) -C $(LIB_DIR)/wrappers/javac \ + $(DESTDIR)$(libdir)/infer/infer/lib/wrappers/ endif - @for i in $$(find infer/lib/wrappers/*); do \ - $(INSTALL_PROGRAM) -C $$i $(DESTDIR)$(libdir)/infer/$$i; \ - done @for i in $$(find infer/lib/python/inferlib/* -type f); do \ $(INSTALL_DATA) -C $$i $(DESTDIR)$(libdir)/infer/$$i; \ done @@ -319,6 +335,7 @@ ifeq ($(IS_RELEASE_TREE),no) ifeq ($(BUILD_C_ANALYZERS),yes) $(MAKE) -C $(FCP_DIR) clean $(MAKE) -C $(FCP_DIR)/clang-ocaml clean + $(REMOVE) $(INFERCLANG_WRAPPERS_PATHS) endif endif $(MAKE) -C $(SRC_DIR) clean diff --git a/infer/lib/clang_wrappers/filter_args_and_run_fcp_clang b/infer/lib/clang_wrappers/filter_args_and_run_fcp_clang deleted file mode 120000 index 12a508574..000000000 --- a/infer/lib/clang_wrappers/filter_args_and_run_fcp_clang +++ /dev/null @@ -1 +0,0 @@ -filter_args_and_run_fcp_clang.sh \ No newline at end of file diff --git a/infer/lib/clang_wrappers/filter_args_and_run_fcp_clang++ b/infer/lib/clang_wrappers/filter_args_and_run_fcp_clang++ deleted file mode 120000 index 12a508574..000000000 --- a/infer/lib/clang_wrappers/filter_args_and_run_fcp_clang++ +++ /dev/null @@ -1 +0,0 @@ -filter_args_and_run_fcp_clang.sh \ No newline at end of file diff --git a/infer/lib/clang_wrappers/filter_args_and_run_fcp_clang.sh b/infer/lib/clang_wrappers/filter_args_and_run_fcp_clang.sh deleted file mode 100755 index 5878881eb..000000000 --- a/infer/lib/clang_wrappers/filter_args_and_run_fcp_clang.sh +++ /dev/null @@ -1,49 +0,0 @@ -#!/bin/bash -# Wrapper around the opensource clang meant to work around various path or library -# issues occurring when one tries to substitute Apple's version of clang with -# a different version. -# The wrapper tries to mitigate version discrepancies in clang's fatal warnings. - -SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" - -CLANG_COMPILER="${SCRIPT_DIR}/../../../facebook-clang-plugins/clang/install/bin/clang" - -# WARNING: use at your own risk, not needed in most cases -# Path that points to clang internal headers to be replaced with -# path to infer's clang internal headers. -CLANG_INCLUDE_TO_REPLACE="${FCP_CLANG_INCLUDE_TO_REPLACE}" - -CLANG_LIB_INCLUDE="${SCRIPT_DIR}/../../../facebook-clang-plugins/clang/install/lib/clang/4.0.0/include" - -if [ "${0%++}" != "$0" ]; then XX="++"; else XX=""; fi - -COMMAND=("${CLANG_COMPILER}${XX}") - -# Remove command line options not supported by the opensource compiler or the plugins. -PREV="" -for X in "$@" -do - if [ "$X" == "-fembed-bitcode-marker" \ - -o "$X" == "-fno-canonical-system-headers" ]; then - continue - elif [ "$X" == "armv7k" ] && [ "$PREV" == "-arch" ]; then - # replace armv7k arch with armv7 - COMMAND+=("armv7") - elif [ "$X" == "$CLANG_INCLUDE_TO_REPLACE" ] && [ "$PREV" == "-isystem" ]; then - COMMAND+=("$CLANG_LIB_INCLUDE") - else - COMMAND+=("$X") - fi - PREV="$X" -done - -# Never error on warnings. Clang is often more strict than Apple's version. -# These arguments are appended to override previous opposite settings. -# How it's done: surpress all the warnings, since there are no warnings, -# compiler can't elevate them to error level. -COMMAND+=("-Wno-everything") - -COMMAND+=("-include") -COMMAND+=(${SCRIPT_DIR}/"global_defines.h") - -"${COMMAND[@]}" diff --git a/infer/lib/wrappers/c++ b/infer/lib/wrappers/c++ deleted file mode 120000 index 060d289be..000000000 --- a/infer/lib/wrappers/c++ +++ /dev/null @@ -1 +0,0 @@ -clang \ No newline at end of file diff --git a/infer/lib/wrappers/cc b/infer/lib/wrappers/cc deleted file mode 120000 index 060d289be..000000000 --- a/infer/lib/wrappers/cc +++ /dev/null @@ -1 +0,0 @@ -clang \ No newline at end of file diff --git a/infer/lib/wrappers/clang b/infer/lib/wrappers/clang deleted file mode 100755 index 110ce6e73..000000000 --- a/infer/lib/wrappers/clang +++ /dev/null @@ -1,29 +0,0 @@ -#!/bin/bash - -# This is a wrapper for clang/clang++ gcc/g++ - -SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" - -if [ -z "$INFER_RESULTS_DIR" ]; then - echo '$INFER_RESULTS_DIR with a path to the results dir not provided.' > /dev/stderr - exit 1 -fi - -# invoke the right compiler looking at the final plusplus (e.g. gcc/g++ clang/clang++) -if [ "${0%++}" != "$0" ]; then INFER_XX="++"; fi -export INFER_XX - -FRONTEND_COMMAND=("$SCRIPT_DIR/../../bin/InferClang" "$@") -HOST_COMPILER_COMMAND=("$SCRIPT_DIR/../clang_wrappers/filter_args_and_run_fcp_clang$INFER_XX" "$@") - -if [ -n "$INFER_COMPILER_WRAPPER_IN_RECURSION" ]; then - if [ -z "$INFER_LISTENER" ]; then - "${HOST_COMPILER_COMMAND[@]}" - fi -else - export INFER_COMPILER_WRAPPER_IN_RECURSION="Y" - - "${FRONTEND_COMMAND[@]}" -fi - -exit $? diff --git a/infer/lib/wrappers/clang++ b/infer/lib/wrappers/clang++ deleted file mode 120000 index 060d289be..000000000 --- a/infer/lib/wrappers/clang++ +++ /dev/null @@ -1 +0,0 @@ -clang \ No newline at end of file diff --git a/infer/lib/wrappers/g++ b/infer/lib/wrappers/g++ deleted file mode 120000 index 060d289be..000000000 --- a/infer/lib/wrappers/g++ +++ /dev/null @@ -1 +0,0 @@ -clang \ No newline at end of file diff --git a/infer/lib/wrappers/gcc b/infer/lib/wrappers/gcc deleted file mode 120000 index 060d289be..000000000 --- a/infer/lib/wrappers/gcc +++ /dev/null @@ -1 +0,0 @@ -clang \ No newline at end of file diff --git a/infer/lib/xcode_wrappers/clang b/infer/lib/xcode_wrappers/clang index dfd610e07..53379bc6c 100755 --- a/infer/lib/xcode_wrappers/clang +++ b/infer/lib/xcode_wrappers/clang @@ -1,7 +1,7 @@ #!/bin/bash SCRIPT_PATH="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" -CLANG_WRAPPERS_PATH="${SCRIPT_PATH}/../clang_wrappers" +BIN_PATH="${SCRIPT_PATH}/../../bin" if [ "${0%++}" != "$0" ]; then INFER_XX="++"; fi export INFER_XX @@ -12,8 +12,9 @@ if [ -z "$INFER_RESULTS_DIR" ]; then # this redirects to the compiler without adding any FCP flag # this is because xcode requires message category info from the compiler # and invokes it without any env var set. - "$FCP_CLANG_COMPILER" "$@" - exit $? + export INFER_ARGS="-a^compile" +else + EXTRA_ARGS=-fno-cxx-modules fi -"${CLANG_WRAPPERS_PATH%/}/InferClang" "$@" -fno-cxx-modules +"${BIN_PATH}/InferClang" "$@" $EXTRA_ARGS diff --git a/infer/src/Makefile b/infer/src/Makefile index 8a0e96fc7..8386fffa3 100644 --- a/infer/src/Makefile +++ b/infer/src/Makefile @@ -244,7 +244,7 @@ rei: %.rei : %.mli refmt -assume-explicit-arity -heuristics-file unary.txt -parse ml -print re $< > $*.rei -roots:=Infer InferAnalyze InferClang CMain JMain InferPrint BuckCompilationDatabase +roots:=Infer InferAnalyze InferClang JMain InferPrint BuckCompilationDatabase clusters:=base clang java IR src_dirs:=$(shell find * -type d) diff --git a/infer/src/base/Config.ml b/infer/src/base/Config.ml index fbf7ec9ce..49e68025d 100644 --- a/infer/src/base/Config.ml +++ b/infer/src/base/Config.ml @@ -580,7 +580,7 @@ and analyzer = "Specify which analyzer to run (only one at a time is supported):\n\ - infer, eradicate, checkers, quandary: run the specified analysis\n\ - capture: run capture phase only (no analysis)\n\ - - compile: run compilation command without interfering (Java only)\n\ + - compile: run compilation command without interfering (not supported by all frontends)\n\ - crashcontext, tracing: experimental (see --crashcontext and --tracing)\n\ - linters: run linters based on the ast only (Objective-C and Objective-C++ only)" ~symbols:string_to_analyzer diff --git a/infer/src/base/Process.ml b/infer/src/base/Process.ml index f89633639..77bf80b66 100644 --- a/infer/src/base/Process.ml +++ b/infer/src/base/Process.ml @@ -25,9 +25,9 @@ let print_error_and_exit ?(exit_code=1) f el = Logging.stderr "@\nAn error occured. Please find details in %s@\n@\n%!" log_file; exit exit_code -(** Executes a command and catches a potential exeption and prints it. *) +(** Executes a command and catches a potential exception and prints it. *) let exec_command cmd args env = - try Unix.execvpe cmd args env + try Unix.execve cmd args env with (Unix.Unix_error _ as e) -> print_unix_error cmd e diff --git a/infer/src/base/Utils.ml b/infer/src/base/Utils.ml index 9098f4c55..4de908e4f 100644 --- a/infer/src/base/Utils.ml +++ b/infer/src/base/Utils.ml @@ -636,15 +636,6 @@ let with_process_in command read = Unix.close_process_in chan in do_finally f g -let with_process_full command read_out read_err = - let (out_chan, _, err_chan) as chans = Unix.open_process_full command (Unix.environment ()) in - let f () = (read_out out_chan, read_err err_chan) in - let g () = - consume_in out_chan; - consume_in err_chan; - Unix.close_process_full chans in - do_finally f g - let failwithf fmt = Format.kfprintf (fun _ -> failwith (Format.flush_str_formatter ())) Format.str_formatter fmt diff --git a/infer/src/base/Utils.mli b/infer/src/base/Utils.mli index fdcd02b40..567ea7a86 100644 --- a/infer/src/base/Utils.mli +++ b/infer/src/base/Utils.mli @@ -292,9 +292,6 @@ val consume_in : in_channel -> unit val with_process_in: string -> (in_channel -> 'a) -> ('a * Unix.process_status) -val with_process_full: string -> (in_channel -> 'a) -> (in_channel -> 'b) -> - (('a * 'b) * Unix.process_status) - val failwithf : ('a, Format.formatter, unit, 'b) format4 -> 'a val invalid_argf : ('a, Format.formatter, unit, 'b) format4 -> 'a diff --git a/infer/src/clang/Capture.re b/infer/src/clang/Capture.re index 23231a386..442fff485 100644 --- a/infer/src/clang/Capture.re +++ b/infer/src/clang/Capture.re @@ -139,12 +139,12 @@ let run_plugin_and_frontend frontend clang_args => { let capture clang_args => { let source_path = { - let argv = ClangCommand.get_argv clang_args; - /* the source file is always the last argument -cc1 clang commands */ - filename_to_absolute argv.(Array.length argv - 1) + let orig_argv = ClangCommand.get_orig_argv clang_args; + /* the source file is always the last argument of the original -cc1 clang command */ + filename_to_absolute orig_argv.(Array.length orig_argv - 1) }; Logging.out "@\n*** Beginning capture of file %s ***@\n" source_path; - if (CLocation.is_file_blacklisted source_path) { + if (Config.analyzer == Some Config.Compile || CLocation.is_file_blacklisted source_path) { Logging.out "@\n Skip the analysis of source file %s@\n@\n" source_path; /* We still need to run clang, but we don't have to attach the plugin. */ run_clang (ClangCommand.command_to_run clang_args) consume_in diff --git a/infer/src/clang/ClangCommand.re b/infer/src/clang/ClangCommand.re index 614d140e8..911f13678 100644 --- a/infer/src/clang/ClangCommand.re +++ b/infer/src/clang/ClangCommand.re @@ -12,18 +12,28 @@ type quoting_style = | DoubleQuotes | SingleQuotes; -type args = {exec: string, argv: list string, quoting_style: quoting_style}; +type args = { + exec: string, + argv: list string, + orig_argv: list string, + quoting_style: quoting_style +}; type t = | Assembly args + /** a normalized clang command that runs the assembler */ | CC1 args + /** a -cc1 clang command */ | ClangError string - | NonCCCommand args; + | ClangWarning string + | NonCCCommand args /** other commands (as, ld, ...) */; + +let fcp_dir = + Config.bin_dir /\/ Filename.parent_dir_name /\/ Filename.parent_dir_name /\/ "facebook-clang-plugins"; /** path of the plugin to load in clang */ -let plugin_path = - Config.bin_dir /\/ Filename.parent_dir_name /\/ Filename.parent_dir_name /\/ "facebook-clang-plugins" /\/ "libtooling" /\/ "build" /\/ "FacebookClangPlugin.dylib"; +let plugin_path = fcp_dir /\/ "libtooling" /\/ "build" /\/ "FacebookClangPlugin.dylib"; let test_env_var var => switch (Sys.getenv var) { @@ -41,8 +51,13 @@ let plugin_name = "BiniouASTExporter"; let syntax_only = test_env_var "FCP_RUN_SYNTAX_ONLY"; -/** specify where is located Apple's clang */ -let apple_clang = test_env_var "FCP_APPLE_CLANG"; +/** are we running as Apple's clang? */ +let has_apple_clang = + switch (Sys.getenv "FCP_APPLE_CLANG") { + | bin when bin != "" => true + | _ => false + | exception Not_found => false + }; /** whether to amend include search path with C++ model headers */ @@ -70,6 +85,58 @@ let value_of_option {argv} => value_of_argv_option argv; let has_flag {argv} flag => IList.exists (string_equal flag) argv; +let quote quoting_style => + switch quoting_style { + | DoubleQuotes => (fun s => "\"" ^ s ^ "\"") + | SingleQuotes => (fun s => "'" ^ s ^ "'") + }; + +/* Work around various path or library issues occurring when one tries to substitute Apple's version + of clang with a different version. Also mitigate version discrepancies in clang's + fatal warnings. */ +let mk_clang_compat_args args => { + /* command line options not supported by the opensource compiler or the plugins */ + let flags_blacklist = ["-fembed-bitcode-marker", "-fno-canonical-system-headers"]; + let replace_option_arg option arg => + if (string_equal option "-arch" && string_equal arg "armv7k") { + "armv7" + /* replace armv7k arch with armv7 */ + } else if ( + string_equal option "-isystem" + ) { + switch Config.clang_include_to_override { + | Some to_replace when string_equal arg to_replace => + fcp_dir /\/ "clang" /\/ "install" /\/ "lib" /\/ "clang" /\/ "4.0.0" /\/ "include" + | _ => arg + } + } else { + arg + }; + let post_args = { + let global_defines_h = Config.lib_dir /\/ "clang_wrappers" /\/ "global_defines.h"; + [ + "-Wno-everything", + /* Never error on warnings. Clang is often more strict than Apple's version. These arguments + are appended at the end to override previous opposite settings. How it's done: suppress + all the warnings, since there are no warnings, compiler can't elevate them to error + level. */ + "-include", + global_defines_h + ] + }; + let rec filter_unsupported_args_and_swap_includes (prev, res) => + fun + | [] => IList.rev (IList.rev post_args @ res) + | [flag, ...tl] when IList.mem string_equal flag flags_blacklist => + filter_unsupported_args_and_swap_includes (flag, res) tl + | [arg, ...tl] => { + let res' = [replace_option_arg prev arg, ...res]; + filter_unsupported_args_and_swap_includes (arg, res') tl + }; + let clang_arguments = filter_unsupported_args_and_swap_includes ("", []) args.argv; + {...args, argv: clang_arguments} +}; + let mk quoting_style argv => { let argv_list = Array.to_list argv; let is_assembly = { @@ -83,7 +150,11 @@ let mk quoting_style argv => { present. */ string_equal argv.(1) "-cc1as" || assembly_language }; - let args = {exec: argv.(0), argv: IList.tl argv_list, quoting_style}; + let args = + switch argv_list { + | [exec, ...argv_no_exec] => {exec, orig_argv: argv_no_exec, argv: argv_no_exec, quoting_style} + | [] => failwith "argv cannot be an empty list" + }; if is_assembly { Assembly args } else if (argv.(1) == "-cc1") { @@ -95,13 +166,9 @@ let mk quoting_style argv => { } }; -let command_to_run {exec, argv, quoting_style} => { - let quote = - switch quoting_style { - | DoubleQuotes => (fun s => "\"" ^ s ^ "\"") - | SingleQuotes => (fun s => "'" ^ s ^ "'") - }; - Printf.sprintf "'%s' %s" exec (IList.map quote argv |> String.concat " ") +let command_to_run args => { + let {exec, argv, quoting_style} = mk_clang_compat_args args; + Printf.sprintf "'%s' %s" exec (IList.map (quote quoting_style) argv |> String.concat " ") }; let with_exec exec args => {...args, exec}; @@ -127,7 +194,7 @@ let with_plugin_args args => { YojsonASTExporter plugin are used. Since the -plugin argument disables the generation of .o files, we invoke apple clang again to generate the expected artifacts. This will keep xcodebuild plus all the sub-steps happy. */ - if apple_clang {"-plugin"} else {"-add-plugin"}, + if has_apple_clang {"-plugin"} else {"-add-plugin"}, plugin_name, "-plugin-arg-" ^ plugin_name, "-", @@ -144,4 +211,4 @@ let prepend_args args clang_args => {...clang_args, argv: args @ clang_args.argv let append_args args clang_args => {...clang_args, argv: clang_args.argv @ args}; -let get_argv {exec, argv} => Array.of_list [exec, ...argv]; +let get_orig_argv {exec, orig_argv} => Array.of_list [exec, ...orig_argv]; diff --git a/infer/src/clang/ClangCommand.rei b/infer/src/clang/ClangCommand.rei index 050c605e0..4eadabedc 100644 --- a/infer/src/clang/ClangCommand.rei +++ b/infer/src/clang/ClangCommand.rei @@ -14,6 +14,7 @@ type t = | CC1 args /** a -cc1 clang command */ | ClangError string + | ClangWarning string | NonCCCommand args /** other commands (as, ld, ...) */; type quoting_style = @@ -47,7 +48,7 @@ let prepend_args: list string => args => args; let append_args: list string => args => args; -let get_argv: args => array string; +let get_orig_argv: args => array string; /** updates the executable to be run */ diff --git a/infer/src/clang/InferClang.re b/infer/src/clang/InferClang.re index 2ddd3a7e0..c12dbc724 100644 --- a/infer/src/clang/InferClang.re +++ b/infer/src/clang/InferClang.re @@ -21,7 +21,9 @@ let normalize (args: array string) :list ClangCommand.t => Logging.out "InferClang got toplevel -cc1 command@\n"; [ClangCommand.CC1 args] | NonCCCommand args => - let clang_hashhashhash = ClangCommand.prepend_arg "-###" args |> ClangCommand.command_to_run; + let clang_hashhashhash = + Printf.sprintf + "%s 2>&1" (ClangCommand.prepend_arg "-###" args |> ClangCommand.command_to_run); Logging.out "clang -### invocation: %s@\n" clang_hashhashhash; let normalized_commands = ref []; let one_line line => @@ -31,12 +33,16 @@ let normalize (args: array string) :list ClangCommand.t => /* split by whitespace */ Str.split (Str.regexp_string "\" \"") |> Array.of_list |> ClangCommand.mk ClangCommand.DoubleQuotes + } else if ( + Str.string_match (Str.regexp "clang[^ :]*: warning: ") line 0 + ) { + ClangCommand.ClangWarning line } else { ClangCommand.ClangError line }; let commands_or_errors = /* commands generated by `clang -### ...` start with ' "/absolute/path/to/binary"' */ - Str.regexp " \"/\\|clang\\(\\|++\\): error:"; + Str.regexp " \"/\\|clang[^ :]*: \\(error\\|warning\\): "; let consume_input i => try ( while true { @@ -50,7 +56,7 @@ let normalize (args: array string) :list ClangCommand.t => | End_of_file => () }; /* collect stdout and stderr output together (in reverse order) */ - with_process_full clang_hashhashhash consume_input consume_input |> ignore; + with_process_in clang_hashhashhash consume_input |> ignore; normalized_commands := IList.rev !normalized_commands; /* Discard assembly commands. This may make the list of commands empty, in which case we'll run the original clang command. We could be smarter about this and try to execute the assembly @@ -69,18 +75,19 @@ let normalize (args: array string) :list ClangCommand.t => /* discard assembly commands -- see above */ Logging.out "InferClang got toplevel assembly command@\n"; [] - | ClangError _ => + | ClangError _ + | ClangWarning _ => /* we cannot possibly get this from the command-line... */ assert false }; -let execute_clang_command (clang_cmd: ClangCommand.t) => +let execute_clang_command (clang_cmd: ClangCommand.t) => { + /* reset logging, otherwise we might print into the logs of the previous file that was compiled */ + Logging.set_log_file_identifier None; switch clang_cmd { | CC1 args => /* this command compiles some code; replace the invocation of clang with our own clang and plugin */ - /* reset logging, otherwise we might print into the logs of the previous file that was compiled */ - Logging.set_log_file_identifier None; Logging.out "Capturing -cc1 command: %s@\n" (ClangCommand.command_to_run args); Capture.capture args | ClangError error => @@ -88,6 +95,7 @@ let execute_clang_command (clang_cmd: ClangCommand.t) => `clang -###` pretty much never fails, but warns of failures on stderr instead. */ Logging.err "%s" error; exit 1 + | ClangWarning warning => Logging.err "%s@\n" warning | Assembly args => /* We shouldn't get any assembly command at this point */ (if Config.debug_mode {failwithf} else {Logging.err}) @@ -96,41 +104,51 @@ let execute_clang_command (clang_cmd: ClangCommand.t) => /* Non-compilation (eg, linking) command. Run the command as-is. It will not get captured further since `clang -### ...` will only output commands that invoke binaries using their absolute paths. */ - Logging.out "Executing raw command: %s@\n" (ClangCommand.command_to_run args); - Process.create_process_and_wait (ClangCommand.get_argv args) - }; + let argv = ClangCommand.get_orig_argv args; + Logging.out "Executing raw command: %s@\n" (String.concat " " (Array.to_list argv)); + Process.create_process_and_wait argv + } +}; let () = { let xx_suffix = - try (Sys.getenv "INFER_XX") { - | Not_found => "" + if (string_is_suffix "++" Sys.argv.(0)) { + "++" + } else { + try (Sys.getenv "INFER_XX") { + | Not_found => "" + } }; let args = Array.copy Sys.argv; /* make sure we don't call ourselves recursively */ args.(0) = CFrontend_config.clang_bin xx_suffix; let commands = normalize args; - if (commands == []) { - /* No command to execute after -###, let's execute the original command - instead. - - In particular, this can happen when - - there are only assembly commands to execute, which we skip, or - - the user tries to run `infer -- clang -c file_that_does_not_exist.c`. In this case, this - will fail with the appropriate error message from clang instead of silently analyzing 0 - files. */ - Logging.out - "WARNING: `clang -### ` returned an empty set of commands to run and no error. Will run the original command directly:@\n %s@\n" - (String.concat " " @@ Array.to_list args); - Process.create_process_and_wait args - } else { - IList.iter execute_clang_command commands - }; /* xcodebuild projects may require the object files to be generated by the Apple compiler, eg to generate precompiled headers compatible with Apple's clang. */ - switch (Sys.getenv "FCP_APPLE_CLANG") { - | bin => - args.(0) = bin ^ xx_suffix; + let should_run_original_command = + switch (Sys.getenv "FCP_APPLE_CLANG") { + | bin => + let bin_xx = bin ^ xx_suffix; + Logging.out "Will run Apple clang %s" bin_xx; + args.(0) = bin_xx; + true + | exception Not_found => false + }; + IList.iter execute_clang_command commands; + if (commands == [] || should_run_original_command) { + if (commands == []) { + /* No command to execute after -###, let's execute the original command + instead. + + In particular, this can happen when + - there are only assembly commands to execute, which we skip, or + - the user tries to run `infer -- clang -c file_that_does_not_exist.c`. In this case, this + will fail with the appropriate error message from clang instead of silently analyzing 0 + files. */ + Logging.out + "WARNING: `clang -### ` returned an empty set of commands to run and no error. Will run the original command directly:@\n %s@\n" + (String.concat " " @@ Array.to_list args) + }; Process.create_process_and_wait args - | exception Not_found => () } }; diff --git a/infer/src/clang/cFrontend_config.ml b/infer/src/clang/cFrontend_config.ml index d46ef46ae..8dc0a1fa5 100644 --- a/infer/src/clang/cFrontend_config.ml +++ b/infer/src/clang/cFrontend_config.ml @@ -39,7 +39,10 @@ let ckcomponent_cl = "CKComponent" let ckcomponentcontroller_cl = "CKComponentController" (** script to run our own clang *) -let clang_bin xx = Config.lib_dir // "clang_wrappers" // "filter_args_and_run_fcp_clang" ^ xx +let clang_bin xx = + Config.bin_dir // Filename.parent_dir_name // Filename.parent_dir_name // + "facebook-clang-plugins" // + "clang" // "install" // "bin" // "clang" ^ xx let class_method = "class" let class_type = "Class" let copy = "copy" diff --git a/infer/src/integration/BuckCompilationDatabase.ml b/infer/src/integration/BuckCompilationDatabase.ml index 582fbac32..1043a59af 100644 --- a/infer/src/integration/BuckCompilationDatabase.ml +++ b/infer/src/integration/BuckCompilationDatabase.ml @@ -27,9 +27,9 @@ let swap_command cmd = Config.wrappers_dir // clang else if Utils.string_is_suffix clangplusplus cmd then Config.wrappers_dir // clangplusplus - else assert false -(* The command in the compilation database json - emited by buck can only be clang or clang++ *) + else + (* The command in the compilation database json emitted by buck can only be clang or clang++ *) + failwithf "Unexpected command name in Buck compilation database: %s" cmd (** Read the files to compile from the changed files index. *) let read_files_to_compile () = @@ -135,7 +135,7 @@ let run_compilation_file compilation_database file = try let compilation_data = StringMap.find file !compilation_database in Unix.chdir compilation_data.dir; - let args = Array.of_list compilation_data.args in + let args = Array.of_list (compilation_data.command::compilation_data.args) in let env = Array.append (Unix.environment()) (Array.of_list [ @@ -172,6 +172,7 @@ let get_compilation_database changed_files = match fst @@ Utils.with_process_in buck_targets Std.input_list with | [] -> Logging.stdout "There are no files to process, exiting."; exit 0 | lines -> + Logging.out "Reading compilation database from:@\n%s@\n" (String.concat "\n" lines); let scan_output compilation_database_files chan = Scanf.sscanf chan "%s %s" (fun target file -> StringMap.add target file compilation_database_files) in diff --git a/infer/tests/codetoanalyze/c/errors/assertions/assert_example.c b/infer/tests/codetoanalyze/c/errors/assertions/assert_example.c deleted file mode 120000 index a5102a199..000000000 --- a/infer/tests/codetoanalyze/c/errors/assertions/assert_example.c +++ /dev/null @@ -1 +0,0 @@ -../../frontend/assertions/assert_example.c \ No newline at end of file