[clang] convert filter_args_and_run_fcp_clang to OCaml

Summary:
This also adds `-a compile` support to `InferClang`. This is needed for the
`xcodebuild` integration, which is hard to fold into the same binary as the
rest.

Reviewed By: jberdine

Differential Revision: D4008262

fbshipit-source-id: 0bbd53f
master
Jules Villard 8 years ago committed by Facebook Github Bot
parent 274d96a440
commit 9535c4d89e

6
.gitignore vendored

@ -84,6 +84,12 @@ buck-out/
/infer/lib/specs/cpp_models /infer/lib/specs/cpp_models
/infer/lib/specs/objc_models /infer/lib/specs/objc_models
/infer/lib/specs/clean_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 /scripts/checkCopyright
/infer/etc/clang_ast.dict /infer/etc/clang_ast.dict
/infer/src/toplevel.mlpack /infer/src/toplevel.mlpack

@ -29,10 +29,20 @@ endif
all: infer inferTraceBugs all: infer inferTraceBugs
$(INFERTRACEBUGS_BIN_RELPATH): # executable names that should point to InferClang to trigger capture
($(REMOVE) $@ && \ INFERCLANG_WRAPPERS_BASENAMES = c++ cc clang clang++ g++ gcc
cd $(@D) && \ INFERCLANG_WRAPPERS_PATHS = $(foreach cc,$(INFERCLANG_WRAPPERS_BASENAMES),$(LIB_DIR)/wrappers/$(cc))
$(LN_S) ../lib/python/$(@F) $(@F))
$(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: src_build:
ifeq ($(IS_FACEBOOK_TREE),yes) ifeq ($(IS_FACEBOOK_TREE),yes)
@ -48,6 +58,9 @@ ifeq ($(BUILD_JAVA_ANALYZERS),yes)
$(MAKE) -C $(ANNOTATIONS_DIR) $(MAKE) -C $(ANNOTATIONS_DIR)
endif endif
$(MAKE) -C $(MODELS_DIR) all $(MAKE) -C $(MODELS_DIR) all
ifeq ($(BUILD_C_ANALYZERS),yes)
infer: $(INFERCLANG_WRAPPERS_PATHS)
endif
clang_setup: clang_setup:
export CC="$(CC)" CFLAGS="$(CFLAGS)"; \ export CC="$(CC)" CFLAGS="$(CFLAGS)"; \
@ -266,6 +279,10 @@ ifeq ($(BUILD_C_ANALYZERS),yes)
@for i in $$(find infer/lib/clang_wrappers/*); do \ @for i in $$(find infer/lib/clang_wrappers/*); do \
$(INSTALL_PROGRAM) -C $$i $(DESTDIR)$(libdir)/infer/$$i; \ $(INSTALL_PROGRAM) -C $$i $(DESTDIR)$(libdir)/infer/$$i; \
done 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 \ @for i in $$(find infer/lib/specs/*); do \
$(INSTALL_DATA) -C $$i $(DESTDIR)$(libdir)/infer/$$i; \ $(INSTALL_DATA) -C $$i $(DESTDIR)$(libdir)/infer/$$i; \
done done
@ -287,10 +304,9 @@ ifeq ($(BUILD_JAVA_ANALYZERS),yes)
$(INSTALL_DATA) -C $$i $(DESTDIR)$(libdir)/infer/$$i; \ $(INSTALL_DATA) -C $$i $(DESTDIR)$(libdir)/infer/$$i; \
done done
$(INSTALL_PROGRAM) -C $(INFERJAVA_BIN) $(DESTDIR)$(libdir)/infer/infer/bin/ $(INSTALL_PROGRAM) -C $(INFERJAVA_BIN) $(DESTDIR)$(libdir)/infer/infer/bin/
$(INSTALL_PROGRAM) -C $(LIB_DIR)/wrappers/javac \
$(DESTDIR)$(libdir)/infer/infer/lib/wrappers/
endif 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 \ @for i in $$(find infer/lib/python/inferlib/* -type f); do \
$(INSTALL_DATA) -C $$i $(DESTDIR)$(libdir)/infer/$$i; \ $(INSTALL_DATA) -C $$i $(DESTDIR)$(libdir)/infer/$$i; \
done done
@ -319,6 +335,7 @@ ifeq ($(IS_RELEASE_TREE),no)
ifeq ($(BUILD_C_ANALYZERS),yes) ifeq ($(BUILD_C_ANALYZERS),yes)
$(MAKE) -C $(FCP_DIR) clean $(MAKE) -C $(FCP_DIR) clean
$(MAKE) -C $(FCP_DIR)/clang-ocaml clean $(MAKE) -C $(FCP_DIR)/clang-ocaml clean
$(REMOVE) $(INFERCLANG_WRAPPERS_PATHS)
endif endif
endif endif
$(MAKE) -C $(SRC_DIR) clean $(MAKE) -C $(SRC_DIR) clean

@ -1 +0,0 @@
filter_args_and_run_fcp_clang.sh

@ -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[@]}"

@ -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 $?

@ -1,7 +1,7 @@
#!/bin/bash #!/bin/bash
SCRIPT_PATH="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" 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 if [ "${0%++}" != "$0" ]; then INFER_XX="++"; fi
export INFER_XX export INFER_XX
@ -12,8 +12,9 @@ if [ -z "$INFER_RESULTS_DIR" ]; then
# this redirects to the compiler without adding any FCP flag # this redirects to the compiler without adding any FCP flag
# this is because xcode requires message category info from the compiler # this is because xcode requires message category info from the compiler
# and invokes it without any env var set. # and invokes it without any env var set.
"$FCP_CLANG_COMPILER" "$@" export INFER_ARGS="-a^compile"
exit $? else
EXTRA_ARGS=-fno-cxx-modules
fi fi
"${CLANG_WRAPPERS_PATH%/}/InferClang" "$@" -fno-cxx-modules "${BIN_PATH}/InferClang" "$@" $EXTRA_ARGS

@ -244,7 +244,7 @@ rei:
%.rei : %.mli %.rei : %.mli
refmt -assume-explicit-arity -heuristics-file unary.txt -parse ml -print re $< > $*.rei 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 clusters:=base clang java IR
src_dirs:=$(shell find * -type d) src_dirs:=$(shell find * -type d)

@ -580,7 +580,7 @@ and analyzer =
"Specify which analyzer to run (only one at a time is supported):\n\ "Specify which analyzer to run (only one at a time is supported):\n\
- infer, eradicate, checkers, quandary: run the specified analysis\n\ - infer, eradicate, checkers, quandary: run the specified analysis\n\
- capture: run capture phase only (no 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\ - crashcontext, tracing: experimental (see --crashcontext and --tracing)\n\
- linters: run linters based on the ast only (Objective-C and Objective-C++ only)" - linters: run linters based on the ast only (Objective-C and Objective-C++ only)"
~symbols:string_to_analyzer ~symbols:string_to_analyzer

@ -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; Logging.stderr "@\nAn error occured. Please find details in %s@\n@\n%!" log_file;
exit exit_code 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 = let exec_command cmd args env =
try Unix.execvpe cmd args env try Unix.execve cmd args env
with (Unix.Unix_error _ as e) -> with (Unix.Unix_error _ as e) ->
print_unix_error cmd e print_unix_error cmd e

@ -636,15 +636,6 @@ let with_process_in command read =
Unix.close_process_in chan in Unix.close_process_in chan in
do_finally f g 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 = let failwithf fmt =
Format.kfprintf (fun _ -> failwith (Format.flush_str_formatter ())) Format.kfprintf (fun _ -> failwith (Format.flush_str_formatter ()))
Format.str_formatter fmt Format.str_formatter fmt

@ -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_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 failwithf : ('a, Format.formatter, unit, 'b) format4 -> 'a
val invalid_argf : ('a, Format.formatter, unit, 'b) format4 -> 'a val invalid_argf : ('a, Format.formatter, unit, 'b) format4 -> 'a

@ -139,12 +139,12 @@ let run_plugin_and_frontend frontend clang_args => {
let capture clang_args => { let capture clang_args => {
let source_path = { let source_path = {
let argv = ClangCommand.get_argv clang_args; let orig_argv = ClangCommand.get_orig_argv clang_args;
/* the source file is always the last argument -cc1 clang commands */ /* the source file is always the last argument of the original -cc1 clang command */
filename_to_absolute argv.(Array.length argv - 1) filename_to_absolute orig_argv.(Array.length orig_argv - 1)
}; };
Logging.out "@\n*** Beginning capture of file %s ***@\n" source_path; 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; 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. */ /* 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 run_clang (ClangCommand.command_to_run clang_args) consume_in

@ -12,18 +12,28 @@ type quoting_style =
| DoubleQuotes | DoubleQuotes
| SingleQuotes; | 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 = type t =
| Assembly args | Assembly args
/** a normalized clang command that runs the assembler */
| CC1 args | CC1 args
/** a -cc1 clang command */
| ClangError string | 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 */ /** path of the plugin to load in clang */
let plugin_path = let plugin_path = fcp_dir /\/ "libtooling" /\/ "build" /\/ "FacebookClangPlugin.dylib";
Config.bin_dir /\/ Filename.parent_dir_name /\/ Filename.parent_dir_name /\/ "facebook-clang-plugins" /\/ "libtooling" /\/ "build" /\/ "FacebookClangPlugin.dylib";
let test_env_var var => let test_env_var var =>
switch (Sys.getenv var) { switch (Sys.getenv var) {
@ -41,8 +51,13 @@ let plugin_name = "BiniouASTExporter";
let syntax_only = test_env_var "FCP_RUN_SYNTAX_ONLY"; let syntax_only = test_env_var "FCP_RUN_SYNTAX_ONLY";
/** specify where is located Apple's clang */ /** are we running as Apple's clang? */
let apple_clang = test_env_var "FCP_APPLE_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 */ /** 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 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 mk quoting_style argv => {
let argv_list = Array.to_list argv; let argv_list = Array.to_list argv;
let is_assembly = { let is_assembly = {
@ -83,7 +150,11 @@ let mk quoting_style argv => {
present. */ present. */
string_equal argv.(1) "-cc1as" || assembly_language 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 { if is_assembly {
Assembly args Assembly args
} else if (argv.(1) == "-cc1") { } else if (argv.(1) == "-cc1") {
@ -95,13 +166,9 @@ let mk quoting_style argv => {
} }
}; };
let command_to_run {exec, argv, quoting_style} => { let command_to_run args => {
let quote = let {exec, argv, quoting_style} = mk_clang_compat_args args;
switch quoting_style { Printf.sprintf "'%s' %s" exec (IList.map (quote quoting_style) argv |> String.concat " ")
| DoubleQuotes => (fun s => "\"" ^ s ^ "\"")
| SingleQuotes => (fun s => "'" ^ s ^ "'")
};
Printf.sprintf "'%s' %s" exec (IList.map quote argv |> String.concat " ")
}; };
let with_exec exec args => {...args, exec}; 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 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 files, we invoke apple clang again to generate the expected artifacts. This will keep
xcodebuild plus all the sub-steps happy. */ 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_name,
"-plugin-arg-" ^ 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 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];

@ -14,6 +14,7 @@ type t =
| CC1 args | CC1 args
/** a -cc1 clang command */ /** a -cc1 clang command */
| ClangError string | ClangError string
| ClangWarning string
| NonCCCommand args /** other commands (as, ld, ...) */; | NonCCCommand args /** other commands (as, ld, ...) */;
type quoting_style = type quoting_style =
@ -47,7 +48,7 @@ let prepend_args: list string => args => args;
let append_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 */ /** updates the executable to be run */

@ -21,7 +21,9 @@ let normalize (args: array string) :list ClangCommand.t =>
Logging.out "InferClang got toplevel -cc1 command@\n"; Logging.out "InferClang got toplevel -cc1 command@\n";
[ClangCommand.CC1 args] [ClangCommand.CC1 args]
| NonCCCommand 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; Logging.out "clang -### invocation: %s@\n" clang_hashhashhash;
let normalized_commands = ref []; let normalized_commands = ref [];
let one_line line => let one_line line =>
@ -31,12 +33,16 @@ let normalize (args: array string) :list ClangCommand.t =>
/* split by whitespace */ /* split by whitespace */
Str.split (Str.regexp_string "\" \"") |> Array.of_list |> Str.split (Str.regexp_string "\" \"") |> Array.of_list |>
ClangCommand.mk ClangCommand.DoubleQuotes ClangCommand.mk ClangCommand.DoubleQuotes
} else if (
Str.string_match (Str.regexp "clang[^ :]*: warning: ") line 0
) {
ClangCommand.ClangWarning line
} else { } else {
ClangCommand.ClangError line ClangCommand.ClangError line
}; };
let commands_or_errors = let commands_or_errors =
/* commands generated by `clang -### ...` start with ' "/absolute/path/to/binary"' */ /* commands generated by `clang -### ...` start with ' "/absolute/path/to/binary"' */
Str.regexp " \"/\\|clang\\(\\|++\\): error:"; Str.regexp " \"/\\|clang[^ :]*: \\(error\\|warning\\): ";
let consume_input i => let consume_input i =>
try ( try (
while true { while true {
@ -50,7 +56,7 @@ let normalize (args: array string) :list ClangCommand.t =>
| End_of_file => () | End_of_file => ()
}; };
/* collect stdout and stderr output together (in reverse order) */ /* 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; normalized_commands := IList.rev !normalized_commands;
/* Discard assembly commands. This may make the list of commands empty, in which case we'll run /* 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 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 */ /* discard assembly commands -- see above */
Logging.out "InferClang got toplevel assembly command@\n"; Logging.out "InferClang got toplevel assembly command@\n";
[] []
| ClangError _ => | ClangError _
| ClangWarning _ =>
/* we cannot possibly get this from the command-line... */ /* we cannot possibly get this from the command-line... */
assert false 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 { switch clang_cmd {
| CC1 args => | CC1 args =>
/* this command compiles some code; replace the invocation of clang with our own clang and /* this command compiles some code; replace the invocation of clang with our own clang and
plugin */ 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); Logging.out "Capturing -cc1 command: %s@\n" (ClangCommand.command_to_run args);
Capture.capture args Capture.capture args
| ClangError error => | 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. */ `clang -###` pretty much never fails, but warns of failures on stderr instead. */
Logging.err "%s" error; Logging.err "%s" error;
exit 1 exit 1
| ClangWarning warning => Logging.err "%s@\n" warning
| Assembly args => | Assembly args =>
/* We shouldn't get any assembly command at this point */ /* We shouldn't get any assembly command at this point */
(if Config.debug_mode {failwithf} else {Logging.err}) (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 /* 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 further since `clang -### ...` will only output commands that invoke binaries using their
absolute paths. */ absolute paths. */
Logging.out "Executing raw command: %s@\n" (ClangCommand.command_to_run args); let argv = ClangCommand.get_orig_argv args;
Process.create_process_and_wait (ClangCommand.get_argv args) Logging.out "Executing raw command: %s@\n" (String.concat " " (Array.to_list argv));
}; Process.create_process_and_wait argv
}
};
let () = { let () = {
let xx_suffix = let xx_suffix =
try (Sys.getenv "INFER_XX") { if (string_is_suffix "++" Sys.argv.(0)) {
| Not_found => "" "++"
} else {
try (Sys.getenv "INFER_XX") {
| Not_found => ""
}
}; };
let args = Array.copy Sys.argv; let args = Array.copy Sys.argv;
/* make sure we don't call ourselves recursively */ /* make sure we don't call ourselves recursively */
args.(0) = CFrontend_config.clang_bin xx_suffix; args.(0) = CFrontend_config.clang_bin xx_suffix;
let commands = normalize args; 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 -### <args>` 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 /* xcodebuild projects may require the object files to be generated by the Apple compiler, eg to
generate precompiled headers compatible with Apple's clang. */ generate precompiled headers compatible with Apple's clang. */
switch (Sys.getenv "FCP_APPLE_CLANG") { let should_run_original_command =
| bin => switch (Sys.getenv "FCP_APPLE_CLANG") {
args.(0) = bin ^ xx_suffix; | 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 -### <args>` 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 Process.create_process_and_wait args
| exception Not_found => ()
} }
}; };

@ -39,7 +39,10 @@ let ckcomponent_cl = "CKComponent"
let ckcomponentcontroller_cl = "CKComponentController" let ckcomponentcontroller_cl = "CKComponentController"
(** script to run our own clang *) (** 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_method = "class"
let class_type = "Class" let class_type = "Class"
let copy = "copy" let copy = "copy"

@ -27,9 +27,9 @@ let swap_command cmd =
Config.wrappers_dir // clang Config.wrappers_dir // clang
else if Utils.string_is_suffix clangplusplus cmd then else if Utils.string_is_suffix clangplusplus cmd then
Config.wrappers_dir // clangplusplus Config.wrappers_dir // clangplusplus
else assert false else
(* The command in the compilation database json (* The command in the compilation database json emitted by buck can only be clang or clang++ *)
emited 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. *) (** Read the files to compile from the changed files index. *)
let read_files_to_compile () = let read_files_to_compile () =
@ -135,7 +135,7 @@ let run_compilation_file compilation_database file =
try try
let compilation_data = StringMap.find file !compilation_database in let compilation_data = StringMap.find file !compilation_database in
Unix.chdir compilation_data.dir; 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 let env = Array.append
(Unix.environment()) (Unix.environment())
(Array.of_list [ (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 match fst @@ Utils.with_process_in buck_targets Std.input_list with
| [] -> Logging.stdout "There are no files to process, exiting."; exit 0 | [] -> Logging.stdout "There are no files to process, exiting."; exit 0
| lines -> | lines ->
Logging.out "Reading compilation database from:@\n%s@\n" (String.concat "\n" lines);
let scan_output compilation_database_files chan = let scan_output compilation_database_files chan =
Scanf.sscanf chan "%s %s" Scanf.sscanf chan "%s %s"
(fun target file -> StringMap.add target file compilation_database_files) in (fun target file -> StringMap.add target file compilation_database_files) in

@ -1 +0,0 @@
../../frontend/assertions/assert_example.c
Loading…
Cancel
Save