Summary: Record the db schema, infer version, and run dates into infer-out/.infer_runstate.json. This allows us to check on startup whether the results directory was generated using a compatible version of infer or not, and give a better error message in the latter case than some SQLite error about mismatching tables. This will be used in a follow-up diff to record capture phases too, and avoid relying on filesystem timestamps of the infer-out/capture/foo/ directories for reactive analysis. Had to change some tests Makefiles to make sure they do not attempt to re-use stale infer-out directories, which would now fail the run. The stale infer-out directory gets deleted if `--force-delete-results-dir` is passed (but a warning still gets printed). Reviewed By: mbouaziz Differential Revision: D6760787 fbshipit-source-id: f36f7dfmaster
							parent
							
								
									0c9b025857
								
							
						
					
					
						commit
						66ad5c3018
					
				@ -0,0 +1,23 @@
 | 
				
			||||
type infer_version = {
 | 
				
			||||
  major: int;
 | 
				
			||||
  minor: int;
 | 
				
			||||
  patch: int;
 | 
				
			||||
  commit: string;
 | 
				
			||||
}
 | 
				
			||||
 | 
				
			||||
type command = string wrap <ocaml
 | 
				
			||||
    t="InferCommand.t"
 | 
				
			||||
    wrap="InferCommand.of_string"
 | 
				
			||||
    unwrap="InferCommand.to_string"
 | 
				
			||||
>
 | 
				
			||||
 | 
				
			||||
type run_info = {
 | 
				
			||||
  date: string;
 | 
				
			||||
  command: command;
 | 
				
			||||
  infer_version: infer_version;
 | 
				
			||||
}
 | 
				
			||||
 | 
				
			||||
type t = {
 | 
				
			||||
  run_sequence: run_info list; (** successive runs that re-used the same results directory *)
 | 
				
			||||
  results_dir_format: string; (** to check if the versions of the results dir are compatible *)
 | 
				
			||||
}
 | 
				
			||||
@ -0,0 +1,68 @@
 | 
				
			||||
(*
 | 
				
			||||
 * Copyright (c) 2018 - 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! IStd
 | 
				
			||||
 | 
				
			||||
let run_time_string = Time.now () |> Time.to_string
 | 
				
			||||
 | 
				
			||||
let state0 =
 | 
				
			||||
  let open Runstate_t in
 | 
				
			||||
  { run_sequence= []
 | 
				
			||||
  ; results_dir_format=
 | 
				
			||||
      Printf.sprintf "db_filename: %s\ndb_schema: %s" ResultsDatabase.database_filename
 | 
				
			||||
        ResultsDatabase.schema_hum }
 | 
				
			||||
 | 
				
			||||
 | 
				
			||||
let state : Runstate_t.t ref = ref state0
 | 
				
			||||
 | 
				
			||||
let add_run_to_sequence () =
 | 
				
			||||
  let run =
 | 
				
			||||
    { Runstate_t.infer_version= Version.{Runstate_t.major; minor; patch; commit}
 | 
				
			||||
    ; date= run_time_string
 | 
				
			||||
    ; command= Config.command }
 | 
				
			||||
  in
 | 
				
			||||
  Runstate_t.(state := {(!state) with run_sequence= run :: !state.run_sequence})
 | 
				
			||||
 | 
				
			||||
 | 
				
			||||
let state_filename = ".infer_runstate.json"
 | 
				
			||||
 | 
				
			||||
let state_file_path = Config.results_dir ^/ state_filename
 | 
				
			||||
 | 
				
			||||
let store () =
 | 
				
			||||
  Utils.with_file_out state_file_path ~f:(fun oc ->
 | 
				
			||||
      Runstate_j.string_of_t !state |> Out_channel.output_string oc )
 | 
				
			||||
 | 
				
			||||
 | 
				
			||||
let load_and_validate () =
 | 
				
			||||
  let error msg =
 | 
				
			||||
    Printf.ksprintf
 | 
				
			||||
      (fun err_msg ->
 | 
				
			||||
        Error
 | 
				
			||||
          (Printf.sprintf
 | 
				
			||||
             "Incompatible results directory '%s':\n%s\nWas '%s' created using an older version of infer?"
 | 
				
			||||
             Config.results_dir err_msg Config.results_dir) )
 | 
				
			||||
      msg
 | 
				
			||||
  in
 | 
				
			||||
  if Sys.file_exists state_file_path <> `Yes then error "save state not found"
 | 
				
			||||
  else
 | 
				
			||||
    try
 | 
				
			||||
      let loaded_state = Ag_util.Json.from_file Runstate_j.read_t state_file_path in
 | 
				
			||||
      if not
 | 
				
			||||
           (String.equal !state.Runstate_t.results_dir_format
 | 
				
			||||
              loaded_state.Runstate_t.results_dir_format)
 | 
				
			||||
      then
 | 
				
			||||
        error "Incompatible formats: found\n  %s\n\nbut expected this format:\n  %s\n\n"
 | 
				
			||||
          loaded_state.results_dir_format !state.Runstate_t.results_dir_format
 | 
				
			||||
      else (
 | 
				
			||||
        state := loaded_state ;
 | 
				
			||||
        Ok () )
 | 
				
			||||
    with _ -> Error "error reading the save state"
 | 
				
			||||
 | 
				
			||||
 | 
				
			||||
let reset () = state := state0
 | 
				
			||||
@ -0,0 +1,22 @@
 | 
				
			||||
(*
 | 
				
			||||
 * Copyright (c) 2018 - 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! IStd
 | 
				
			||||
 | 
				
			||||
val add_run_to_sequence : unit -> unit
 | 
				
			||||
(** add an entry with the current run date *)
 | 
				
			||||
 | 
				
			||||
val store : unit -> unit
 | 
				
			||||
(** save the current state to disk *)
 | 
				
			||||
 | 
				
			||||
val load_and_validate : unit -> (unit, string) Result.t
 | 
				
			||||
(** attempt to load state from disk *)
 | 
				
			||||
 | 
				
			||||
val reset : unit -> unit
 | 
				
			||||
(** reset the in-memory state to what it would be if this were a fresh run of infer *)
 | 
				
			||||
					Loading…
					
					
				
		Reference in new issue