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.
178 lines
6.7 KiB
178 lines
6.7 KiB
(*
|
|
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
*
|
|
* This source code is licensed under the MIT license found in the
|
|
* LICENSE file in the root directory of this source tree.
|
|
*)
|
|
|
|
open! IStd
|
|
module F = Format
|
|
module L = Logging
|
|
|
|
(* example build report json output
|
|
[
|
|
{
|
|
"success" : true,
|
|
"results" : {
|
|
"//annotations:annotations_infer" : {
|
|
"success" : true,
|
|
"type" : "BUILT_LOCALLY",
|
|
"output" : "buck-out/gen/annotations/annotations_infer/infer_out"
|
|
},
|
|
"//module2:module2_infer" : {
|
|
"success" : true,
|
|
"type" : "BUILT_LOCALLY",
|
|
"output" : "buck-out/gen/module2/module2_infer/infer_out"
|
|
},
|
|
"//module1:module1_infer" : {
|
|
"success" : true,
|
|
"type" : "BUILT_LOCALLY",
|
|
"output" : "buck-out/gen/module1/module1_infer/infer_out"
|
|
},
|
|
"//module3:module1_infer" : {
|
|
"success" : "SUCCESS",
|
|
"type" : "BUILT_LOCALLY",
|
|
"outputs" : {
|
|
"DEFAULT" : [ "buck-out/gen/module1/module3_infer/infer_out" ]
|
|
}
|
|
}
|
|
},
|
|
"failures" : { }
|
|
}%
|
|
]
|
|
*)
|
|
|
|
(** Read the build report json file buck produced, and parse into a list of pairs
|
|
[(target, output-path)]. NB contrary to what buck documentation says, the output path is always
|
|
present even when the target is locally cached. *)
|
|
let read_and_parse_report build_report =
|
|
let get_json_field fieldname = function
|
|
| `Assoc fields ->
|
|
List.Assoc.find fields ~equal:String.equal fieldname
|
|
| _ ->
|
|
None
|
|
in
|
|
let parse_target (target, json) =
|
|
let path_opt =
|
|
match get_json_field "output" json with
|
|
| Some (`String str) ->
|
|
Some str
|
|
| _ -> (
|
|
match get_json_field "outputs" json |> Option.bind ~f:(get_json_field "DEFAULT") with
|
|
| Some (`List [`String str]) ->
|
|
Some str
|
|
| _ ->
|
|
None )
|
|
in
|
|
match path_opt with
|
|
| None ->
|
|
L.internal_error "Could not parse target json: %s@." (Yojson.Basic.to_string json) ;
|
|
None
|
|
| Some path ->
|
|
Some (target, path)
|
|
in
|
|
let parse_results = function
|
|
| `Assoc results ->
|
|
(* NB this will simply skip unparseable targets *)
|
|
List.filter_map results ~f:parse_target |> Option.some
|
|
| _ ->
|
|
None
|
|
in
|
|
Yojson.Basic.from_file build_report |> get_json_field "results" |> Option.bind ~f:parse_results
|
|
|
|
|
|
(** Function for processing paths in a buck build report and generating an [infer-deps.txt] file.
|
|
Given a pair [(buck_target, output_path)],
|
|
|
|
- if [output_path] contains a capture DB, then generate the appropriate deps line;
|
|
- if [output_path] contains an [infer-deps.txt] file, expand and inline it;
|
|
- if [output_path] is a dummy target used in the combined genrule integration for clang targets,
|
|
read its contents, parse them as an output directory path and apply the above two tests to
|
|
that *)
|
|
let expand_target acc (target, target_path) =
|
|
let expand_dir acc (target, target_path) =
|
|
(* invariant: [target_path] is absolute *)
|
|
let db_file = ResultsDirEntryName.get_path ~results_dir:target_path CaptureDB in
|
|
match Sys.file_exists db_file with
|
|
| `Yes ->
|
|
(* there is a capture DB at this path, so terminate expansion and generate deps line *)
|
|
let line = Printf.sprintf "%s\t-\t%s" target target_path in
|
|
line :: acc
|
|
| `No | `Unknown -> (
|
|
(* no capture DB was found, so look for, and inline, an [infer-deps.txt] file *)
|
|
let infer_deps =
|
|
ResultsDirEntryName.get_path ~results_dir:target_path CaptureDependencies
|
|
in
|
|
match Sys.file_exists infer_deps with
|
|
| `Yes ->
|
|
Utils.with_file_in infer_deps
|
|
~f:(In_channel.fold_lines ~init:acc ~f:(fun acc line -> line :: acc))
|
|
| `No | `Unknown ->
|
|
L.internal_error "No capture DB or infer-deps file in %s@." target_path ;
|
|
acc )
|
|
in
|
|
let target_path =
|
|
if Filename.is_absolute target_path then target_path else Config.project_root ^/ target_path
|
|
in
|
|
match Sys.is_directory target_path with
|
|
| `Yes ->
|
|
(* output path is directory, so should contain either a capture DB or an [infer-deps.txt] file *)
|
|
expand_dir acc (target, target_path)
|
|
| `No | `Unknown -> (
|
|
(* output path is not a directory, so assume it's an intermediate genrule output containing the
|
|
output path of the underlying capture target *)
|
|
match Utils.read_file target_path with
|
|
| Ok [new_target_path] ->
|
|
expand_dir acc (target, new_target_path)
|
|
| Ok _ ->
|
|
L.internal_error "Couldn't parse intermediate deps file %s@." target_path ;
|
|
acc
|
|
| Error error ->
|
|
L.internal_error "Error %s@\nCouldn't read intermediate deps file %s@." error target_path ;
|
|
acc )
|
|
|
|
|
|
let infer_deps_of_build_report build_report =
|
|
match read_and_parse_report build_report with
|
|
| None ->
|
|
L.die InternalError "Couldn't parse buck build report: %s@." build_report
|
|
| Some target_path_list ->
|
|
let infer_deps_lines =
|
|
List.fold target_path_list ~init:[] ~f:expand_target
|
|
|> List.dedup_and_sort ~compare:String.compare
|
|
in
|
|
let infer_deps = ResultsDir.get_path CaptureDependencies in
|
|
Utils.with_file_out infer_deps ~f:(fun out_channel ->
|
|
Out_channel.output_lines out_channel infer_deps_lines )
|
|
|
|
|
|
let capture build_cmd =
|
|
let prog, buck_cmd = (List.hd_exn build_cmd, List.tl_exn build_cmd) in
|
|
L.progress "Querying buck for java flavor capture targets...@." ;
|
|
let time0 = Mtime_clock.counter () in
|
|
let BuckFlavors.{command; rev_not_targets; targets} =
|
|
BuckFlavors.add_flavors_to_buck_arguments JavaFlavor ~extra_flavors:[] buck_cmd
|
|
in
|
|
L.progress "Found %d java flavor capture targets in %a.@." (List.length targets) Mtime.Span.pp
|
|
(Mtime_clock.count time0) ;
|
|
let all_args = List.rev_append rev_not_targets targets in
|
|
let build_report_file =
|
|
Filename.temp_file ~in_dir:(ResultsDir.get_path Temporary) "buck_build_report" ".json"
|
|
in
|
|
let updated_buck_cmd =
|
|
(* make buck tell us where in buck-out are the capture directories for merging *)
|
|
(prog :: command :: "--build-report" :: build_report_file :: Buck.config JavaFlavor)
|
|
@ Config.buck_build_args_no_inline
|
|
@ Buck.store_args_in_file ~identifier:"java_flavor_build" all_args
|
|
in
|
|
L.(debug Capture Quiet)
|
|
"Processed buck command '%a'@." (Pp.seq F.pp_print_string) updated_buck_cmd ;
|
|
if List.is_empty targets then L.external_warning "WARNING: found no buck targets to analyze.@."
|
|
else
|
|
let time0 = Mtime_clock.counter () in
|
|
Buck.wrap_buck_call ~label:"build" updated_buck_cmd |> ignore ;
|
|
infer_deps_of_build_report build_report_file ;
|
|
L.progress "Java flavor capture took %a.@." Mtime.Span.pp (Mtime_clock.count time0) ;
|
|
ResultsDir.RunState.set_merge_capture true ;
|
|
()
|