[diff] support for --gen-previous-build-command-script

Summary:
This allows the user to specify a different command to build the current version of the project vs to build the previous version of the project. Here's an example:

```
infer diff --gen-previous-build-command-script "echo clang -c hello.c" ... -- clang -c hello2.c
```

By default the two build commands are the same. If the script is to be used for both the current and the previous versions, then it's on the user to run it once first, eg:

```
infer diff --gen-previous-build-command-script "./myscript.sh previous" ... -- $(./myscript.sh current)
```

Reviewed By: martinoluca

Differential Revision: D5500989

fbshipit-source-id: 7374b44
master
Jules Villard 7 years ago committed by Facebook Github Bot
parent 34d10fdede
commit 6a4e4d4b6a

1
.gitignore vendored

@ -42,6 +42,7 @@ duplicates.txt
/infer/tests/build_systems/differential_*/infer-current /infer/tests/build_systems/differential_*/infer-current
/infer/tests/build_systems/differential_*/infer-previous /infer/tests/build_systems/differential_*/infer-previous
/infer/tests/build_systems/diff/src /infer/tests/build_systems/diff/src
/infer/tests/build_systems/diff_*/src
/_release /_release
# generated by oUnit # generated by oUnit

@ -24,6 +24,7 @@ BUILD_SYSTEMS_TESTS += \
clang_with_MD_flag \ clang_with_MD_flag \
delete_results_dir \ delete_results_dir \
diff \ diff \
diff_gen_build_script \
fail_on_issue \ fail_on_issue \
j1 \ j1 \
linters \ linters \

@ -1126,6 +1126,12 @@ and frontend_stats =
CLOpt.mk_bool ~deprecated:["fs"] ~deprecated_no:["nfs"] ~long:"frontend-stats" CLOpt.mk_bool ~deprecated:["fs"] ~deprecated_no:["nfs"] ~long:"frontend-stats"
"Output statistics about the capture phase to *.o.astlog (clang only)" "Output statistics about the capture phase to *.o.astlog (clang only)"
and gen_previous_build_command_script =
CLOpt.mk_string_opt ~long:"gen-previous-build-command-script"
~in_help:CLOpt.([(Diff, manual_generic)])
~meta:"shell"
"Specify a script that outputs the build command to capture in the previous version of the project. The script should output the command on stdout. For example \"echo make\"."
and generated_classes = and generated_classes =
CLOpt.mk_path_opt ~long:"generated-classes" CLOpt.mk_path_opt ~long:"generated-classes"
~in_help:CLOpt.([(Capture, manual_java)]) ~in_help:CLOpt.([(Capture, manual_java)])
@ -1862,6 +1868,8 @@ and bugs_csv = !bugs_csv
and frontend_tests = !frontend_tests and frontend_tests = !frontend_tests
and gen_previous_build_command_script = !gen_previous_build_command_script
and generated_classes = !generated_classes and generated_classes = !generated_classes
and bugs_tests = !bugs_tests and bugs_tests = !bugs_tests

@ -405,6 +405,8 @@ val frontend_tests : bool
val frontend_stats : bool val frontend_stats : bool
val gen_previous_build_command_script : string option
val generated_classes : string option val generated_classes : string option
val headers : bool val headers : bool

@ -43,20 +43,43 @@ let save_report revision =
L.progress "Results for the %a revision stored in %s@\n" pp_revision revision report_name ; L.progress "Results for the %a revision stored in %s@\n" pp_revision revision report_name ;
report_name report_name
let gen_previous_driver_mode script =
let output, exit_or_signal = Utils.with_process_in script In_channel.input_lines in
match exit_or_signal with
| Error _ as status
-> L.(die UserError)
"*** command failed:@\n*** %s@\n*** %s@." script (Unix.Exit_or_signal.to_string_hum status)
| Ok ()
-> (* FIXME(t15553258): this won't work if the build command has arguments that contain spaces. In that case the user should be able to use an argfile for the build command instead, so not critical to fix. *)
let command = List.concat_map ~f:(String.split ~on:' ') output in
L.environment_info "Build command for the previous project version: '%s'@\n%!"
(String.concat ~sep:" " command) ;
Driver.mode_of_build_command command
let delete_capture_and_analysis_artifacts () =
let dirs_to_delete =
List.map ~f:(Filename.concat Config.results_dir)
Config.([attributes_dir_name; captured_dir_name; specs_dir_name])
in
List.iter ~f:Utils.rmtree dirs_to_delete ; List.iter ~f:Unix.mkdir_p dirs_to_delete ; ()
let diff driver_mode = let diff driver_mode =
Driver.run_prologue driver_mode ; Driver.run_prologue driver_mode ;
let changed_files = Driver.read_config_changed_files () in let changed_files = Driver.read_config_changed_files () in
(* TODO(t15553258) run gen-build script if specified *)
(* run capture *)
Driver.capture driver_mode ~changed_files ; Driver.capture driver_mode ~changed_files ;
Driver.analyze_and_report driver_mode ~changed_files ; Driver.analyze_and_report driver_mode ~changed_files ;
let current_report = Some (save_report Current) in let current_report = Some (save_report Current) in
(* Some files in the current checkout may be deleted in the old checkout. If we kept the results of the previous capture and analysis around, we would report issues on these files again in the previous checkout, which is wrong. Do not do anything too smart for now and just delete all results from the analysis of the current checkout. *)
delete_capture_and_analysis_artifacts () ;
(* TODO(t15553258) bail if nothing to analyze (configurable, some people might care about bugs (* TODO(t15553258) bail if nothing to analyze (configurable, some people might care about bugs
fixed more than about time to analyze) *) fixed more than about time to analyze) *)
checkout Previous ; checkout Previous ;
(* TODO(t15553258) run gen-build script if specified *) let previous_driver_mode =
Driver.capture driver_mode ~changed_files ; Option.value_map ~default:driver_mode ~f:gen_previous_driver_mode
Driver.analyze_and_report driver_mode ~changed_files ; Config.gen_previous_build_command_script
in
Driver.capture previous_driver_mode ~changed_files ;
Driver.analyze_and_report previous_driver_mode ~changed_files ;
checkout Current ; checkout Current ;
let previous_report = Some (save_report Previous) in let previous_report = Some (save_report Previous) in
(* compute differential *) (* compute differential *)

@ -32,6 +32,9 @@ val equal_mode : mode -> mode -> bool
val mode_from_command_line : mode Lazy.t val mode_from_command_line : mode Lazy.t
(** driver mode computed from the command-line arguments and settings in Config *) (** driver mode computed from the command-line arguments and settings in Config *)
val mode_of_build_command : string list -> mode
(** driver mode computed from the build command alone, eg [["buck"; "build"; ...]] gives [PythonCapture (BBuck, ["buck"; "build"; ...])] *)
val run_prologue : mode -> unit val run_prologue : mode -> unit
(** prepare the environment for running the given mode *) (** prepare the environment for running the given mode *)

@ -14,7 +14,9 @@ void test1() {
*s1 = 42; *s1 = 42;
} }
void test2() { void nullify(int* x) { *x = 0; }
void test2(int x) {
int* s2 = NULL; int* s2 = NULL;
*s2 = 42; nullify(s2);
} }

@ -9,6 +9,8 @@ TESTS_DIR = ../..
INFER_OUT = infer-out INFER_OUT = infer-out
include $(TESTS_DIR)/diff.make include $(TESTS_DIR)/diff.make
CLEAN_EXTRA = src
SRC_DIR = $(CURDIR)/../codetoanalyze SRC_DIR = $(CURDIR)/../codetoanalyze
INFER_OPTIONS = \ INFER_OPTIONS = \
--previous-to-current-script '$(COPY) $(SRC_DIR)/some_bugs.c src/hello.c' \ --previous-to-current-script '$(COPY) $(SRC_DIR)/some_bugs.c src/hello.c' \
@ -19,7 +21,8 @@ INFER_OPTIONS = \
SOURCES = $(SRC_DIR)/some_bugs.c $(SRC_DIR)/some_different_bugs.c SOURCES = $(SRC_DIR)/some_bugs.c $(SRC_DIR)/some_different_bugs.c
$(INFER_OUT)/differential/introduced.json: $(SOURCES) $(CLANG_DEPS) changed_files.txt $(INFER_OUT)/differential/introduced.json: $(SOURCES) $(CLANG_DEPS) changed_files.txt \
$(MAKEFILE_LIST)
$(QUIET)$(MKDIR_P) src $(QUIET)$(MKDIR_P) src
$(QUIET)$(COPY) $(SRC_DIR)/some_bugs.c src/hello.c $(QUIET)$(COPY) $(SRC_DIR)/some_bugs.c src/hello.c
$(QUIET)$(call silent_on_success,Running diff analysis in $(TEST_REL_DIR),\ $(QUIET)$(call silent_on_success,Running diff analysis in $(TEST_REL_DIR),\

@ -1 +1 @@
src/hello.c, test2, 2, NULL_DEREFERENCE, [start of procedure test2()] src/hello.c, test2, 2, NULL_DEREFERENCE, [start of procedure test2(),start of procedure nullify()]

@ -0,0 +1,32 @@
# Copyright (c) 2017 - 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.
TESTS_DIR = ../..
INFER_OUT = infer-out
include $(TESTS_DIR)/diff.make
CLEAN_EXTRA = src
SRC_DIR = $(CURDIR)/../codetoanalyze
INFER_OPTIONS = \
--previous-to-current-script 'true' \
--current-to-previous-script '$(COPY) $(SRC_DIR)/some_bugs.c src/' \
--gen-previous-build-command-script 'echo "clang -c src/some_bugs.c"' \
--changed-files-index changed_files.txt \
--file-renamings file_renamings.json \
--report-hook '/bin/true' \
-- clang -c src/some_different_bugs.c
SOURCES = $(SRC_DIR)/some_bugs.c $(SRC_DIR)/some_different_bugs.c
$(INFER_OUT)/differential/introduced.json: $(SOURCES) $(CLANG_DEPS) changed_files.txt \
file_renamings.json $(MAKEFILE_LIST)
$(QUIET)$(MKDIR_P) src
$(QUIET)$(COPY) $(SRC_DIR)/some_different_bugs.c src/
$(QUIET)$(call silent_on_success,Running diff analysis in $(TEST_REL_DIR),\
$(INFER_BIN) --no-keep-going -o $(INFER_OUT) --project-root $(CURDIR) diff \
$(INFER_OPTIONS))

@ -0,0 +1,2 @@
src/some_bugs.c
src/some_different_bugs.c

@ -0,0 +1,6 @@
[
{
"previous": "src/some_bugs.c",
"current": "src/some_different_bugs.c"
}
]

@ -0,0 +1 @@
src/some_bugs.c, test2, 2, NULL_DEREFERENCE, [start of procedure test2(),start of procedure nullify()]

@ -0,0 +1 @@
src/some_different_bugs.c, test3, 3, MEMORY_LEAK, [start of procedure test3(),Condition is true]

@ -0,0 +1 @@
src/some_different_bugs.c, test1, 2, NULL_DEREFERENCE, [start of procedure test1()]
Loading…
Cancel
Save