You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
125 lines
5.0 KiB
125 lines
5.0 KiB
/*
|
|
* 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 a clang command, normalize it via `clang -###` if needed to get a clear view of what work
|
|
is being done and which source files are being compiled, if any, then replace compilation
|
|
commands by our own clang with our plugin attached for each source file. */
|
|
open! IStd;
|
|
|
|
type action_item =
|
|
| Command ClangCommand.t
|
|
| ClangError string
|
|
| ClangWarning string;
|
|
|
|
|
|
/** Given a list of arguments for clang [args], return a list of new commands to run according to
|
|
the results of `clang -### [args]`. */
|
|
let normalize prog::prog args::args :list action_item => {
|
|
let cmd = ClangCommand.mk ClangQuotes.SingleQuotes prog::prog args::args;
|
|
let clang_hashhashhash =
|
|
Printf.sprintf
|
|
"%s 2>&1"
|
|
(
|
|
ClangCommand.prepend_arg "-###" cmd |>
|
|
/* c++ modules are not supported, so let clang know in case it was passed
|
|
"-fmodules". Unfortunately we cannot know accurately if "-fmodules" was passed because we
|
|
don't go into argument files at this point ("clang -### ..." will do that for us), so we
|
|
also pass "-Qunused-arguments" to silence the potential warning that "-fno-cxx-modules"
|
|
was ignored. Moreover, "-fno-cxx-modules" is only accepted by the clang driver so we have
|
|
to pass it now. */
|
|
ClangCommand.append_args ["-fno-cxx-modules", "-Qunused-arguments"] |> ClangCommand.command_to_run
|
|
);
|
|
Logging.out "clang -### invocation: %s@\n" clang_hashhashhash;
|
|
let normalized_commands = ref [];
|
|
let one_line line =>
|
|
if (String.is_prefix prefix::" \"" line) {
|
|
Command (
|
|
switch (
|
|
/* massage line to remove edge-cases for splitting */
|
|
"\"" ^ line ^ " \"" |>
|
|
/* split by whitespace */
|
|
Str.split (Str.regexp_string "\" \"")
|
|
) {
|
|
| [prog, ...args] => ClangCommand.mk ClangQuotes.EscapedDoubleQuotes prog::prog args::args
|
|
| [] => failwith "ClangWrapper: argv cannot be empty"
|
|
}
|
|
)
|
|
} else if (
|
|
Str.string_match (Str.regexp "clang[^ :]*: warning: ") line 0
|
|
) {
|
|
ClangWarning line
|
|
} else {
|
|
ClangError line
|
|
};
|
|
let commands_or_errors =
|
|
/* commands generated by `clang -### ...` start with ' "/absolute/path/to/binary"' */
|
|
Str.regexp " \"/\\|clang[^ :]*: \\(error\\|warning\\): ";
|
|
let consume_input i =>
|
|
try (
|
|
while true {
|
|
let line = input_line i;
|
|
/* keep only commands and errors */
|
|
if (Str.string_match commands_or_errors line 0) {
|
|
normalized_commands := [one_line line, ...!normalized_commands]
|
|
}
|
|
}
|
|
) {
|
|
| End_of_file => ()
|
|
};
|
|
/* collect stdout and stderr output together (in reverse order) */
|
|
Utils.with_process_in clang_hashhashhash consume_input |> ignore;
|
|
normalized_commands := IList.rev !normalized_commands;
|
|
!normalized_commands
|
|
};
|
|
|
|
let exec_action_item =
|
|
fun
|
|
| ClangError error => {
|
|
/* An error in the output of `clang -### ...`. Outputs the error and fail. This is because
|
|
`clang -###` pretty much never fails, but warns of failures on stderr instead. */
|
|
Logging.stderr "%s@." error;
|
|
exit 1
|
|
}
|
|
| ClangWarning warning => Logging.stderr "%s@\n" warning
|
|
| Command clang_cmd => Capture.capture clang_cmd;
|
|
|
|
let exe prog::prog args::args => {
|
|
let xx_suffix = String.is_suffix suffix::"++" prog ? "++" : "";
|
|
/* use clang in facebook-clang-plugins */
|
|
let clang_xx = CFrontend_config.clang_bin xx_suffix;
|
|
let commands = normalize prog::clang_xx args::args;
|
|
/* xcodebuild projects may require the object files to be generated by the Apple compiler, eg to
|
|
generate precompiled headers compatible with Apple's clang. */
|
|
let (prog, should_run_original_command) =
|
|
switch Config.fcp_apple_clang {
|
|
| Some bin =>
|
|
let bin_xx = bin ^ xx_suffix;
|
|
Logging.out "Will run Apple clang %s" bin_xx;
|
|
(bin_xx, true)
|
|
| None => (clang_xx, false)
|
|
};
|
|
IList.iter exec_action_item commands;
|
|
if (List.is_empty commands || should_run_original_command) {
|
|
if (List.is_empty 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 sep::" " @@ [prog, ...args])
|
|
};
|
|
Process.create_process_and_wait prog::prog args::args
|
|
}
|
|
};
|