Summary: This changes executions of the former InferClang into a function call. In particular, it can be called several times per execution. The new InferClang must be called as if it was clang, and knows how to run clang with our plugin to get the AST of the source file. Reviewed By: akotulski Differential Revision: D3981017 fbshipit-source-id: 7af6490master
parent
caa7534a71
commit
c53bfe3159
@ -1,157 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
# Copyright (c) 2016 - 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.
|
|
||||||
|
|
||||||
# Given the arguments a `clang -cc1 ...` command, attaches a plugin to
|
|
||||||
# the clang command, then run our own clang with all the arguments
|
|
||||||
# (passing through filter_args_and_run_fcp_clang.sh) and pipe the
|
|
||||||
# output to InferClang.
|
|
||||||
|
|
||||||
#### Configuration ####
|
|
||||||
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
|
|
||||||
BIN_DIR="${SCRIPT_DIR}/../../bin"
|
|
||||||
ETC_DIR="${SCRIPT_DIR}/../../etc"
|
|
||||||
|
|
||||||
# path to the wrapped clang compiler to invoke
|
|
||||||
CLANG_COMPILER="${SCRIPT_DIR}/filter_args_and_run_fcp_clang"
|
|
||||||
# extension of the file containing the clang cmd intercepted
|
|
||||||
CMD_FILE_EXT=".sh"
|
|
||||||
# path of the plugin to load in clang
|
|
||||||
PLUGIN_PATH="${SCRIPT_DIR}/../../../facebook-clang-plugins/libtooling/build/FacebookClangPlugin.dylib"
|
|
||||||
# name of the plugin to use
|
|
||||||
PLUGIN_NAME="BiniouASTExporter"
|
|
||||||
# output directory of the plugin
|
|
||||||
RESULTS_DIR="$FCP_RESULTS_DIR"
|
|
||||||
# this skips the creation of .o files
|
|
||||||
SYNTAX_ONLY="$FCP_RUN_SYNTAX_ONLY"
|
|
||||||
# extra arguments to pass during the execution of the infer frontend
|
|
||||||
INFER_FRONTEND_ARGS=($FCP_INFER_FRONTEND_ARGS)
|
|
||||||
# this fails the execution of clang if the frontend fails
|
|
||||||
REPORT_FRONTEND_FAILURE="$FCP_REPORT_FRONTEND_FAILURE"
|
|
||||||
# enable debug mode (to get more data saved to disk for future inspections)
|
|
||||||
DEBUG_MODE="$FCP_DEBUG_MODE"
|
|
||||||
# specify where is located Apple's clang
|
|
||||||
APPLE_CLANG="$FCP_APPLE_CLANG"
|
|
||||||
# whether to amend include search path with C++ model headers
|
|
||||||
INFER_CXX_MODELS="$FCP_INFER_CXX_MODELS"
|
|
||||||
|
|
||||||
# invariants that this script expects
|
|
||||||
if [ -z "$RESULTS_DIR" ]; then
|
|
||||||
echo '$FCP_RESULTS_DIR with the name of the output directory not provided.' 1>&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
if [ "$1" != "-cc1" ]; then
|
|
||||||
echo "$0 expects to be run with -cc1" 1>&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# we know the first argument is "-cc1"
|
|
||||||
shift
|
|
||||||
|
|
||||||
|
|
||||||
# Functions
|
|
||||||
function get_option_argument {
|
|
||||||
# retrieves the value passed to an argument of a clang command
|
|
||||||
OPT="$1"
|
|
||||||
shift
|
|
||||||
while [ -n "$1" ] && [ "$1" != "$OPT" ]; do shift; done
|
|
||||||
echo "$2"
|
|
||||||
}
|
|
||||||
|
|
||||||
function has_flag {
|
|
||||||
# return if the given flag is part of the given command or not
|
|
||||||
local FLAG="$1"
|
|
||||||
shift
|
|
||||||
while [ -n "$1" ] && [ "$1" != "$FLAG" ]; do shift; done
|
|
||||||
[ -n "$1" ];
|
|
||||||
}
|
|
||||||
|
|
||||||
# Main
|
|
||||||
INPUT_ARGUMENTS=("$@")
|
|
||||||
|
|
||||||
# -cc1 has to be the first argument or clang will think it runs in driver mode
|
|
||||||
CLANG_CMD=("${CLANG_COMPILER}${XX}" "-cc1")
|
|
||||||
|
|
||||||
# It's important to place this option before other -isystem options.
|
|
||||||
if [ -n "$INFER_CXX_MODELS" ]; then
|
|
||||||
CLANG_CMD+=("-isystem" "${SCRIPT_DIR}/../../models/cpp/include")
|
|
||||||
fi
|
|
||||||
|
|
||||||
# (t7400979) this is a workaround to avoid that clang crashes when the -fmodules flag
|
|
||||||
# and the 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 [ -n "$APPLE_CLANG" ]; then
|
|
||||||
ADD_PLUGIN_FLAG="-plugin"
|
|
||||||
else
|
|
||||||
ADD_PLUGIN_FLAG="-add-plugin"
|
|
||||||
fi
|
|
||||||
|
|
||||||
CLANG_CMD+=(
|
|
||||||
"-load"
|
|
||||||
"${PLUGIN_PATH}"
|
|
||||||
"$ADD_PLUGIN_FLAG"
|
|
||||||
"${PLUGIN_NAME}"
|
|
||||||
"-plugin-arg-${PLUGIN_NAME}"
|
|
||||||
"-"
|
|
||||||
"-plugin-arg-${PLUGIN_NAME}"
|
|
||||||
"PREPEND_CURRENT_DIR=1")
|
|
||||||
|
|
||||||
# add the remaining arguments
|
|
||||||
CLANG_CMD+=("$@")
|
|
||||||
|
|
||||||
# the source file is at the end of the command, match it with the wanted extensions
|
|
||||||
SOURCE_FILENAME="${INPUT_ARGUMENTS[${#INPUT_ARGUMENTS[@]} - 1]}"
|
|
||||||
|
|
||||||
if ! [[ "$SOURCE_FILENAME" = /* ]]; then
|
|
||||||
SOURCE_FILENAME="$(pwd)/$SOURCE_FILENAME"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# add fsyntax-only to the end of arg list to override previous options
|
|
||||||
if [ -n "$SYNTAX_ONLY" ]; then
|
|
||||||
CLANG_CMD+=("-fsyntax-only")
|
|
||||||
fi
|
|
||||||
|
|
||||||
LANGUAGE=$(get_option_argument "-x" "${INPUT_ARGUMENTS[@]}")
|
|
||||||
if [ -n "$LANGUAGE" ]; then INFER_FRONTEND_ARGS+=("-x" "$LANGUAGE"); fi
|
|
||||||
if has_flag "-fobjc-arc" "${INPUT_ARGUMENTS[@]}"; then
|
|
||||||
INFER_FRONTEND_ARGS+=("-fobjc-arc");
|
|
||||||
fi
|
|
||||||
|
|
||||||
INFER_FRONTEND_CMD=(
|
|
||||||
"${BIN_DIR}/InferClang"
|
|
||||||
"-c" "$SOURCE_FILENAME"
|
|
||||||
"-results_dir" "$RESULTS_DIR"
|
|
||||||
"${INFER_FRONTEND_ARGS[@]}")
|
|
||||||
|
|
||||||
if [ -n "$DEBUG_MODE" ]; then
|
|
||||||
OBJECT_FILENAME="$(get_option_argument "-o" "${INPUT_ARGUMENTS[@]}")"
|
|
||||||
# Emit the clang command with the extra args piped to InferClang
|
|
||||||
echo "${CLANG_CMD[@]} " \
|
|
||||||
"| tee ${OBJECT_FILENAME}.biniou " \
|
|
||||||
"| ${INFER_FRONTEND_CMD[@]}" \
|
|
||||||
> "${OBJECT_FILENAME}${CMD_FILE_EXT}"
|
|
||||||
echo "bdump -x -d ${ETC_DIR}/clang_ast.dict -w '!!DUMMY!!' ${OBJECT_FILENAME}.biniou " \
|
|
||||||
"> ${OBJECT_FILENAME}.bdump" \
|
|
||||||
>> "${OBJECT_FILENAME}${CMD_FILE_EXT}"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# run clang and pipe its output to InferClang, or flush it in case the latter crashes
|
|
||||||
"${CLANG_CMD[@]}" | \
|
|
||||||
("${INFER_FRONTEND_CMD[@]}" || \
|
|
||||||
{ EC=$?; cat > /dev/null; exit $EC; }) 2>&1
|
|
||||||
STATUSES=("${PIPESTATUS[@]}")
|
|
||||||
STATUS="${STATUSES[0]}"
|
|
||||||
INFER_STATUS="${STATUSES[1]}"
|
|
||||||
|
|
||||||
# if clang fails, then fail, otherwise, fail with the frontend's exitcode if required
|
|
||||||
if [ "$STATUS" == 0 ] && [ -n "$REPORT_FRONTEND_FAILURE" ]; then
|
|
||||||
STATUS="$INFER_STATUS"
|
|
||||||
fi
|
|
||||||
|
|
||||||
exit $STATUS
|
|
@ -0,0 +1,180 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2016 - 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
open! Utils;
|
||||||
|
|
||||||
|
|
||||||
|
/** this fails the execution of clang if the frontend fails */
|
||||||
|
let report_frontend_failure = not Config.failures_allowed;
|
||||||
|
|
||||||
|
|
||||||
|
/** enable debug mode (to get more data saved to disk for future inspections) */
|
||||||
|
let debug_mode = Config.debug_mode || Config.frontend_stats;
|
||||||
|
|
||||||
|
let buffer_len = 262143;
|
||||||
|
|
||||||
|
let catch_biniou_buffer_errors f x =>
|
||||||
|
try (f x) {
|
||||||
|
| Invalid_argument "Bi_inbuf.refill_from_channel" =>
|
||||||
|
Logging.err "WARNING: biniou buffer too short, skipping the file@\n";
|
||||||
|
assert false
|
||||||
|
};
|
||||||
|
|
||||||
|
/* This function reads the json file in fname, validates it, and encoded in the AST data structure
|
||||||
|
defined in Clang_ast_t. */
|
||||||
|
let validate_decl_from_file fname =>
|
||||||
|
catch_biniou_buffer_errors (Ag_util.Biniou.from_file len::buffer_len Clang_ast_b.read_decl) fname;
|
||||||
|
|
||||||
|
let validate_decl_from_channel chan =>
|
||||||
|
catch_biniou_buffer_errors
|
||||||
|
(Ag_util.Biniou.from_channel len::buffer_len Clang_ast_b.read_decl) chan;
|
||||||
|
|
||||||
|
let register_perf_stats_report source_file => {
|
||||||
|
let stats_dir = Filename.concat Config.results_dir Config.frontend_stats_dir_name;
|
||||||
|
let abbrev_source_file = DB.source_file_encoding source_file;
|
||||||
|
let stats_file = Config.perf_stats_prefix ^ "_" ^ abbrev_source_file ^ ".json";
|
||||||
|
create_dir Config.results_dir;
|
||||||
|
create_dir stats_dir;
|
||||||
|
PerfStats.register_report_at_exit (Filename.concat stats_dir stats_file)
|
||||||
|
};
|
||||||
|
|
||||||
|
let init_global_state_for_capture_and_linters source_file => {
|
||||||
|
Logging.set_log_file_identifier (Some (Filename.basename (DB.source_file_to_string source_file)));
|
||||||
|
register_perf_stats_report source_file;
|
||||||
|
Config.curr_language := Config.Clang;
|
||||||
|
DB.Results_dir.init source_file;
|
||||||
|
Clang_ast_main.reset_cache ();
|
||||||
|
CFrontend_config.reset_global_state ()
|
||||||
|
};
|
||||||
|
|
||||||
|
let run_clang_frontend trans_unit_ctx ast_source => {
|
||||||
|
let init_time = Unix.gettimeofday ();
|
||||||
|
let print_elapsed () => {
|
||||||
|
let elapsed = Unix.gettimeofday () -. init_time;
|
||||||
|
Logging.out "Elapsed: %07.3f seconds.@\n" elapsed
|
||||||
|
};
|
||||||
|
let (ast_filename, ast_decl) =
|
||||||
|
switch ast_source {
|
||||||
|
| `File path => (path, validate_decl_from_file path)
|
||||||
|
| `Pipe chan => (
|
||||||
|
"stdin of " ^ DB.source_file_to_string trans_unit_ctx.CFrontend_config.source_file,
|
||||||
|
validate_decl_from_channel chan
|
||||||
|
)
|
||||||
|
};
|
||||||
|
let (decl_index, stmt_index, type_index, ivar_to_property_index) = Clang_ast_main.index_node_pointers ast_decl;
|
||||||
|
CFrontend_config.pointer_decl_index := decl_index;
|
||||||
|
CFrontend_config.pointer_stmt_index := stmt_index;
|
||||||
|
CFrontend_config.pointer_type_index := type_index;
|
||||||
|
CFrontend_config.ivar_to_property_index := ivar_to_property_index;
|
||||||
|
CFrontend_config.json := ast_filename;
|
||||||
|
Logging.out "Clang frontend action is %s@\n" Config.clang_frontend_action_string;
|
||||||
|
Logging.out
|
||||||
|
"Start %s of AST from %s@\n" Config.clang_frontend_action_string !CFrontend_config.json;
|
||||||
|
if Config.clang_frontend_do_lint {
|
||||||
|
CFrontend_checkers_main.do_frontend_checks trans_unit_ctx ast_decl
|
||||||
|
};
|
||||||
|
if Config.clang_frontend_do_capture {
|
||||||
|
CFrontend.do_source_file trans_unit_ctx ast_decl
|
||||||
|
};
|
||||||
|
Logging.out "End translation AST file %s... OK!@\n" !CFrontend_config.json;
|
||||||
|
print_elapsed ()
|
||||||
|
};
|
||||||
|
|
||||||
|
let run_clang clang_command read =>
|
||||||
|
switch (with_process_in clang_command read) {
|
||||||
|
| (res, Unix.WEXITED 0) => res
|
||||||
|
| (_, Unix.WEXITED n) =>
|
||||||
|
/* exit with the same error code as clang in case of compilation failure */
|
||||||
|
exit n
|
||||||
|
| _ => exit 1
|
||||||
|
};
|
||||||
|
|
||||||
|
let run_plugin_and_frontend frontend clang_args => {
|
||||||
|
let clang_command = ClangCommand.command_to_run (ClangCommand.with_plugin_args clang_args);
|
||||||
|
if debug_mode {
|
||||||
|
/* -cc1 clang commands always set -o explicitly */
|
||||||
|
let object_filename = Option.get (ClangCommand.value_of_option clang_args "-o");
|
||||||
|
/* Emit the clang command with the extra args piped to InferClang */
|
||||||
|
let frontend_script_fname = Printf.sprintf "%s.sh" object_filename;
|
||||||
|
let debug_script_out = open_out frontend_script_fname;
|
||||||
|
let debug_script_fmt = Format.formatter_of_out_channel debug_script_out;
|
||||||
|
let biniou_fname = Printf.sprintf "%s.biniou" object_filename;
|
||||||
|
Format.fprintf debug_script_fmt "%s \\@\n > %s@\n" clang_command biniou_fname;
|
||||||
|
let infer_clang_options =
|
||||||
|
String.concat
|
||||||
|
"^"
|
||||||
|
(
|
||||||
|
(
|
||||||
|
try [Unix.getenv "INFER_ARGS"] {
|
||||||
|
| Not_found => []
|
||||||
|
}
|
||||||
|
)
|
||||||
|
@ [
|
||||||
|
"--clang-biniou-file",
|
||||||
|
biniou_fname
|
||||||
|
]
|
||||||
|
);
|
||||||
|
Format.fprintf
|
||||||
|
debug_script_fmt
|
||||||
|
"INFER_ARGS=\"%s\" %s@\n"
|
||||||
|
infer_clang_options
|
||||||
|
(ClangCommand.with_exec Sys.executable_name clang_args |> ClangCommand.command_to_run);
|
||||||
|
Format.fprintf
|
||||||
|
debug_script_fmt
|
||||||
|
"bdump -x -d \"%s/clang_ast.dict\" -w '!!DUMMY!!' %s \\@\n > %s.bdump"
|
||||||
|
Config.etc_dir
|
||||||
|
biniou_fname
|
||||||
|
object_filename;
|
||||||
|
close_out debug_script_out
|
||||||
|
};
|
||||||
|
run_clang clang_command frontend
|
||||||
|
};
|
||||||
|
|
||||||
|
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)
|
||||||
|
};
|
||||||
|
Logging.out "@\n*** Beginning capture of file %s ***@\n" source_path;
|
||||||
|
if (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
|
||||||
|
} else {
|
||||||
|
let source_file = CLocation.source_file_from_path source_path;
|
||||||
|
init_global_state_for_capture_and_linters source_file;
|
||||||
|
let trans_unit_ctx = {
|
||||||
|
let clang_langs =
|
||||||
|
CFrontend_config.[("c", C), ("objective-c", ObjC), ("c++", CPP), ("objective-c++", ObjCPP)];
|
||||||
|
let lang =
|
||||||
|
switch (ClangCommand.value_of_option clang_args "-x") {
|
||||||
|
| Some lang_opt when IList.mem_assoc string_equal lang_opt clang_langs =>
|
||||||
|
IList.assoc string_equal lang_opt clang_langs
|
||||||
|
| _ => assert false
|
||||||
|
};
|
||||||
|
{CFrontend_config.source_file: source_file, lang}
|
||||||
|
};
|
||||||
|
Config.arc_mode := ClangCommand.has_flag clang_args "-fobjc-arc";
|
||||||
|
try (
|
||||||
|
switch Config.clang_biniou_file {
|
||||||
|
| Some fname => run_clang_frontend trans_unit_ctx (`File fname)
|
||||||
|
| None =>
|
||||||
|
run_plugin_and_frontend
|
||||||
|
(fun chan_in => run_clang_frontend trans_unit_ctx (`Pipe chan_in)) clang_args
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
| exc =>
|
||||||
|
if report_frontend_failure {
|
||||||
|
raise exc
|
||||||
|
}
|
||||||
|
};
|
||||||
|
()
|
||||||
|
}
|
||||||
|
};
|
@ -0,0 +1,10 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2016 - 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
let capture: ClangCommand.args => unit;
|
@ -0,0 +1,153 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2016 - 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
open! Utils;
|
||||||
|
|
||||||
|
type quoting_style = | DoubleQuotes | SingleQuotes;
|
||||||
|
|
||||||
|
type args = {exec: string, argv: list string, quoting_style: quoting_style};
|
||||||
|
|
||||||
|
type t = | Assembly of args | CC1 of args | ClangError of string | NonCCCommand of args;
|
||||||
|
|
||||||
|
|
||||||
|
/** 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 test_env_var var =>
|
||||||
|
switch (Sys.getenv var) {
|
||||||
|
| "1" => true
|
||||||
|
| _ => false
|
||||||
|
| exception Not_found => false
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/** name of the plugin to use */
|
||||||
|
let plugin_name = "BiniouASTExporter";
|
||||||
|
|
||||||
|
|
||||||
|
/** this skips the creation of .o files */
|
||||||
|
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";
|
||||||
|
|
||||||
|
|
||||||
|
/** whether to amend include search path with C++ model headers */
|
||||||
|
let infer_cxx_models = Config.cxx_experimental;
|
||||||
|
|
||||||
|
let value_of_argv_option argv opt_name =>
|
||||||
|
IList.fold_left
|
||||||
|
(
|
||||||
|
fun (prev_arg, result) arg => {
|
||||||
|
let result' =
|
||||||
|
if (Option.is_some result) {
|
||||||
|
result
|
||||||
|
} else if (string_equal opt_name prev_arg) {
|
||||||
|
Some arg
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
(arg, result')
|
||||||
|
}
|
||||||
|
)
|
||||||
|
("", None)
|
||||||
|
argv |> snd;
|
||||||
|
|
||||||
|
let value_of_option {argv} => value_of_argv_option argv;
|
||||||
|
|
||||||
|
let has_flag {argv} flag => IList.exists (string_equal flag) argv;
|
||||||
|
|
||||||
|
let mk quoting_style argv => {
|
||||||
|
let argv_list = Array.to_list argv;
|
||||||
|
let is_assembly =
|
||||||
|
/* whether language is set to "assembler" or "assembler-with-cpp" */
|
||||||
|
{
|
||||||
|
let assembly_language =
|
||||||
|
switch (value_of_argv_option argv_list "-x") {
|
||||||
|
| Some lang => string_is_prefix "assembler" lang
|
||||||
|
| _ => false
|
||||||
|
};
|
||||||
|
/* Detect -cc1as or assembly language commands. -cc1as is always the first argument if
|
||||||
|
present. */
|
||||||
|
string_equal argv.(1) "-cc1as" || assembly_language
|
||||||
|
};
|
||||||
|
let args = {exec: argv.(0), argv: IList.tl argv_list, quoting_style};
|
||||||
|
if is_assembly {
|
||||||
|
Assembly args
|
||||||
|
} else if (argv.(1) == "-cc1") {
|
||||||
|
CC1
|
||||||
|
/* -cc1 is always the first argument if present. */
|
||||||
|
args
|
||||||
|
} else {
|
||||||
|
NonCCCommand args
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
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 with_exec exec args => {...args, exec};
|
||||||
|
|
||||||
|
let with_plugin_args args => {
|
||||||
|
let cons a b => [a, ...b];
|
||||||
|
let do_if cond action x =>
|
||||||
|
if cond {
|
||||||
|
action x
|
||||||
|
} else {
|
||||||
|
x
|
||||||
|
};
|
||||||
|
let rev_args_before =
|
||||||
|
[] |>
|
||||||
|
/* -cc1 has to be the first argument or clang will think it runs in driver mode */
|
||||||
|
cons "-cc1" |>
|
||||||
|
/* It's important to place this option before other -isystem options. */
|
||||||
|
do_if infer_cxx_models (IList.rev_append ["-isystem", Config.cpp_models_dir]) |>
|
||||||
|
IList.rev_append [
|
||||||
|
"-load",
|
||||||
|
plugin_path,
|
||||||
|
/* (t7400979) this is a workaround to avoid that clang crashes when the -fmodules flag and the
|
||||||
|
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"
|
||||||
|
},
|
||||||
|
plugin_name,
|
||||||
|
"-plugin-arg-" ^ plugin_name,
|
||||||
|
"-",
|
||||||
|
"-plugin-arg-" ^ plugin_name,
|
||||||
|
"PREPEND_CURRENT_DIR=1"
|
||||||
|
];
|
||||||
|
let args_after = [] |> do_if syntax_only (cons "-fsyntax-only");
|
||||||
|
{...args, argv: IList.rev_append rev_args_before (args.argv @ args_after)}
|
||||||
|
};
|
||||||
|
|
||||||
|
let prepend_arg arg clang_args => {...clang_args, argv: [arg, ...clang_args.argv]};
|
||||||
|
|
||||||
|
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];
|
@ -0,0 +1,55 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2016 - 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
type args;
|
||||||
|
|
||||||
|
type t =
|
||||||
|
| Assembly of args
|
||||||
|
/** a normalized clang command that runs the assembler */
|
||||||
|
| CC1 of args
|
||||||
|
/** a -cc1 clang command */
|
||||||
|
| ClangError of string
|
||||||
|
| NonCCCommand of args /** other commands (as, ld, ...) */;
|
||||||
|
|
||||||
|
type quoting_style =
|
||||||
|
| DoubleQuotes /** the arguments are ready to be enclosed in "double quotes" */
|
||||||
|
| SingleQuotes /** the arguments are ready to be enclodes in 'single quotes' */;
|
||||||
|
|
||||||
|
|
||||||
|
/** [mk qs argv] finds the type of command depending on its arguments [argv]. The quoting style of
|
||||||
|
the arguments have to be provided, so that the command may be run later on. */
|
||||||
|
let mk: quoting_style => array string => t;
|
||||||
|
|
||||||
|
|
||||||
|
/** change an args object into a string ready to be passed to a shell to be executed */
|
||||||
|
let command_to_run: args => string;
|
||||||
|
|
||||||
|
|
||||||
|
/** whether the command has this flag set in its arguments */
|
||||||
|
let has_flag: args => string => bool;
|
||||||
|
|
||||||
|
|
||||||
|
/** the value passed to an option in the arguments of a command */
|
||||||
|
let value_of_option: args => string => option string;
|
||||||
|
|
||||||
|
|
||||||
|
/** add the arguments needed to attach the facebook-clang-plugins plugin */
|
||||||
|
let with_plugin_args: args => args;
|
||||||
|
|
||||||
|
let prepend_arg: string => args => args;
|
||||||
|
|
||||||
|
let prepend_args: list string => args => args;
|
||||||
|
|
||||||
|
let append_args: list string => args => args;
|
||||||
|
|
||||||
|
let get_argv: args => array string;
|
||||||
|
|
||||||
|
|
||||||
|
/** updates the executable to be run */
|
||||||
|
let with_exec: string => args => args;
|
@ -1,94 +0,0 @@
|
|||||||
(*
|
|
||||||
* 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.
|
|
||||||
*)
|
|
||||||
|
|
||||||
open! Utils
|
|
||||||
|
|
||||||
(* Take as input an ast file and a C or ObjectiveC file such that the ast file
|
|
||||||
corresponds to the compilation of the C file with clang.
|
|
||||||
Parse the ast file into a data structure and translates it into a cfg. *)
|
|
||||||
|
|
||||||
module L = Logging
|
|
||||||
|
|
||||||
let buffer_len = 262143
|
|
||||||
|
|
||||||
(* This function reads the json file in fname, validates it, and encoded in the AST data structure*)
|
|
||||||
(* defined in Clang_ast_t. *)
|
|
||||||
let validate_decl_from_file fname =
|
|
||||||
try
|
|
||||||
Ag_util.Biniou.from_file ~len:buffer_len Clang_ast_b.read_decl fname
|
|
||||||
with (Invalid_argument "Bi_inbuf.refill_from_channel") ->
|
|
||||||
Logging.out "WARNING: biniou buffer too short, skipping the file\n";
|
|
||||||
assert false
|
|
||||||
|
|
||||||
let validate_decl_from_stdin () =
|
|
||||||
try
|
|
||||||
Ag_util.Biniou.from_channel ~len:buffer_len Clang_ast_b.read_decl stdin
|
|
||||||
with (Invalid_argument "Bi_inbuf.refill_from_channel") ->
|
|
||||||
Logging.out "WARNING: biniou buffer too short, skipping the file\n";
|
|
||||||
assert false
|
|
||||||
|
|
||||||
let register_perf_stats_report source_file =
|
|
||||||
let stats_dir = Filename.concat Config.results_dir Config.frontend_stats_dir_name in
|
|
||||||
let abbrev_source_file = DB.source_file_encoding source_file in
|
|
||||||
let stats_file = Config.perf_stats_prefix ^ "_" ^ abbrev_source_file ^ ".json" in
|
|
||||||
create_dir Config.results_dir ;
|
|
||||||
create_dir stats_dir ;
|
|
||||||
PerfStats.register_report_at_exit (Filename.concat stats_dir stats_file)
|
|
||||||
|
|
||||||
let init_global_state_for_capture_and_linters source_file =
|
|
||||||
register_perf_stats_report source_file;
|
|
||||||
DB.Results_dir.init source_file
|
|
||||||
|
|
||||||
let do_run source_file ast_path =
|
|
||||||
let init_time = Unix.gettimeofday () in
|
|
||||||
let print_elapsed () =
|
|
||||||
let elapsed = Unix.gettimeofday () -. init_time in
|
|
||||||
Logging.out "Elapsed: %07.3f seconds.\n" elapsed in
|
|
||||||
try
|
|
||||||
let ast_filename, ast_decl =
|
|
||||||
match ast_path with
|
|
||||||
| Some path ->
|
|
||||||
path, validate_decl_from_file path
|
|
||||||
| None ->
|
|
||||||
"stdin of " ^ DB.source_file_to_string source_file, validate_decl_from_stdin () in
|
|
||||||
let decl_index, stmt_index, type_index, ivar_to_property_index =
|
|
||||||
Clang_ast_main.index_node_pointers ast_decl in
|
|
||||||
CFrontend_config.pointer_decl_index := decl_index;
|
|
||||||
CFrontend_config.pointer_stmt_index := stmt_index;
|
|
||||||
CFrontend_config.pointer_type_index := type_index;
|
|
||||||
CFrontend_config.ivar_to_property_index := ivar_to_property_index;
|
|
||||||
CFrontend_config.json := ast_filename;
|
|
||||||
Logging.out "Clang frontend action is %s\n" Config.clang_frontend_action_string;
|
|
||||||
Logging.out "Start %s of AST from %s\n" Config.clang_frontend_action_string
|
|
||||||
!CFrontend_config.json;
|
|
||||||
init_global_state_for_capture_and_linters source_file;
|
|
||||||
let translation_unit_context = CFrontend_config.{source_file; lang=Config.clang_lang} in
|
|
||||||
if Config.clang_frontend_do_lint then
|
|
||||||
CFrontend_checkers_main.do_frontend_checks translation_unit_context ast_decl;
|
|
||||||
if Config.clang_frontend_do_capture then
|
|
||||||
CFrontend.do_source_file translation_unit_context ast_decl;
|
|
||||||
Logging.out "End translation AST file %s... OK!@\n" !CFrontend_config.json;
|
|
||||||
print_elapsed ();
|
|
||||||
with
|
|
||||||
(Yojson.Json_error s) as exc ->
|
|
||||||
Logging.err_debug "%s\n" s;
|
|
||||||
print_elapsed ();
|
|
||||||
raise exc
|
|
||||||
|
|
||||||
let () =
|
|
||||||
let source_file =
|
|
||||||
(match Config.source_file with
|
|
||||||
| Some path ->
|
|
||||||
Logging.set_log_file_identifier (Some (Filename.basename path));
|
|
||||||
CLocation.check_source_file path;
|
|
||||||
CLocation.source_file_from_path path
|
|
||||||
| None ->
|
|
||||||
Logging.err_debug "Incorrect command line arguments\n";
|
|
||||||
Config.print_usage_exit ()) in
|
|
||||||
do_run source_file Config.ast_file
|
|
@ -1,16 +0,0 @@
|
|||||||
(*
|
|
||||||
* 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.
|
|
||||||
*)
|
|
||||||
|
|
||||||
open! Utils
|
|
||||||
|
|
||||||
(** Main module of InferClang. Take as input AST files produced by clang during compilation and
|
|
||||||
their corresponding C/C++/ObjectiveC source files.
|
|
||||||
|
|
||||||
Parse the arguments, parse and validate the input AST into a data structure and translates it
|
|
||||||
into a cfg. *)
|
|
Loading…
Reference in new issue