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.

279 lines
9.4 KiB

(*
* Copyright (c) 2015 - 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.
*)
[make] s/ocamlbuild/jbuilder/g Summary: Use jbuilder to build infer instead of ocamlbuild. This is mainly to get faster builds: ``` times in 10ms, ±differences measured in speedups, 4 cores | | ocb total | jb | ±total | ocb user | jb | ±user | ocb cpu | jb | ±cpu | ocb sys | jb | ±sys | |-----------------------------------+-----------+------+--------+----------+------+-------+---------+-----+------+---------+------+------| | byte from scratch | 6428 | 2456 | 2.62 | 7743 | 6662 | 1.16 | 138 | 331 | 2.40 | 1184 | 1477 | 0.80 | | native from scratch | 9841 | 4289 | 2.29 | 9530 | 8834 | 1.08 | 110 | 245 | 2.23 | 1373 | 1712 | 0.80 | | byte after native | 29578 | 1602 | 18.46 | 4514 | 4640 | 0.97 | 170 | 325 | 1.91 | 543 | 576 | 0.94 | | change infer.ml byte | 344 | 282 | 1.22 | 292 | 215 | 1.36 | 96 | 99 | 1.03 | 040 | 066 | 0.61 | | change infer.ml native | 837 | 223 | 3.75 | 789 | 174 | 4.53 | 98 | 99 | 1.01 | 036 | 47 | 0.77 | | change Config.ml byte | 451 | 339 | 1.33 | 382 | 336 | 1.14 | 97 | 122 | 1.26 | 056 | 80 | 0.70 | | change Config.ml native | 4024 | 1760 | 2.29 | 4585 | 4225 | 1.09 | 127 | 276 | 2.17 | 559 | 644 | 0.87 | | change cFrontend_config.ml byte | 348 | 643 | 0.54 | 297 | 330 | 0.90 | 96 | 67 | 0.70 | 038 | 102 | 0.37 | | change cFrontend_config.ml native | 1480 | 584 | 2.53 | 1435 | 906 | 1.58 | 106 | 185 | 1.75 | 136 | 178 | 0.76 | #+TBLFM: $4=$2/$3;f2::$7=$5/$6;f2::$10=$9/$8;f2::$13=$11/$12;f2 50 cores | | ocb total | jb | ±total | ocb user | jb | ±user | ocb cpu | jb | ±cpu | ocb sys | jb | ±sys | |---------------------+-----------+------+--------+----------+------+-------+---------+----+------+---------+------+------| | byte from scratch | 9114 | 2061 | 4.42 | 9334 | 5133 | 1.82 | | | 0/0 | 2566 | 1726 | 1.49 | | native from scratch | 13481 | 3967 | 3.40 | 12291 | 7608 | 1.62 | | | 0/0 | 3003 | 2100 | 1.43 | | byte after native | 3467 | 1476 | 2.35 | 5067 | 3912 | 1.30 | | | 0/0 | 971 | 801 | 1.21 | #+TBLFM: $4=$2/$3;f2::$7=$5/$6;f2::$10=$9/$8;f2::$13=$11/$12;f2 ``` Menu: 1. Write a jbuild file, autogenerated from jbuild.in because we need to fill in some information at build-time (really, at configure time, but TODO), such as whether or not clang is enabled. 2. Nuke lots of stuff from infer/src/Makefile that is now in the jbuild file 3. The jbuild file lives in infer/src/ so it can see all the sources. If we put it somewhere else, eg, infer/, then `jbuilder` scans too many files (all irrelevant) and takes 2.5s to start instead of .8s. Adding irrelevant directories to jbuild-ignore does not help. 4. jbuilder does not support subdirectories, so resort to listing all the source files in the generated jbuild (only source directories need to be manually listed in jbuild.in though). Still, the generated .merlin is wrong and makes merlin find source files in _build, so manually tune it to get good merlin support. We also lose some of merlin for unit tests as it cannot see their build artefacts anymore. 5. checkCopyright gets its own jbuild because it's standalone. Also, remove some deprecation warnings in checkCopyright due to the new version of Core from a while ago. 6. Drop less-used Makefile features (they had regressed anyway) such as building individual modules. Also, building mod_dep.pdf now takes all the source files available so they better build (before, it would only take the source files from the config, eg with or without clang) (that's pretty minor). 7. The toplevel is now built as a custom toplevel because that was easier. It should soon be even easier: https://github.com/janestreet/jbuilder/issues/210 8. Move BUILTINS.mli to BUILTINS.ml because jbuilder is not happy about interface files without implementations. In particular, I did not try to migrate too much of the Makefile logic to jbuilder, more can be done in the future. Reviewed By: jberdine Differential Revision: D5573661 fbshipit-source-id: 4ca6d8f
8 years ago
open! Core
module F = Format
let copyright_modified_exit_code = 1
let copyright_malformed_exit_code = 3
(* error code 2 is for OCaml uncaught exceptions *)
type comment_style =
| Line of string * bool
(** line comments, eg "#" for shell, and whether there should be a
newline before the copyright notice *)
| Block of string * string * string (** block comments, eg ("(*", "*", "*)") for ocaml *)
let comment_style_ocaml = Block ("(*", "*", "*)")
let comment_style_c = Block ("/*", "*", "*/")
let comment_style_shell = Line ("#", true)
let comment_style_make = Line ("#", false)
let comment_style_llvm = Line (";", true)
let comment_styles =
[ comment_style_ocaml
; comment_style_c
; comment_style_shell
; comment_style_llvm
; comment_style_make ]
let lang_of_com_style style =
if style = comment_style_ocaml then "ocaml"
else if style = comment_style_c then "c"
else if style = comment_style_shell then "shell"
else if style = comment_style_llvm then "llvm"
else if style = comment_style_make then "make"
else "??unknown??"
let default_start_line_of_com_style style =
match style with Line (_, true) -> 2 | Line (_, false) -> 0 | Block _ -> 0
let prefix_of_comment_style = function
| Line _
-> ""
| Block (_, inter, _)
-> String.make (String.length inter) ' '
(** If true, update the copyright message of the files. *)
let update_files = ref false
let line_contains_copyright line = String.is_substring ~substring:"opyright " line
let rec find_copyright_line lines n =
match lines with
| []
-> None
| line :: lines'
-> if line_contains_copyright line then Some n else find_copyright_line lines' (n + 1)
let find_comment_start_and_style lines_arr n =
(* are we in a line comment? *)
let cur_line_comment =
List.find comment_styles ~f:(function
| Line (s, starts_with_newline) when String.is_prefix ~prefix:s lines_arr.(n)
-> if starts_with_newline then n <> 0 else true
| _
-> false )
in
let is_start line =
match cur_line_comment with
| Some Line _
-> cur_line_comment
| _
-> List.find comment_styles ~f:(function
| Block (s, _, _)
-> String.is_substring ~substring:s line
| _
-> false )
in
let i = ref (max (n - 1) 0) in
(* hacky fake line comment to avoid an option type *)
let found = ref (-1, Line (">>>>>>>>>>>", false)) in
while !i >= 0 && fst !found = -1 do
match is_start lines_arr.(!i) with Some style -> found := (!i, style) | None -> decr i
done ;
!found
let find_comment_end lines_arr n com_style =
let is_end line =
match com_style with
| Line (s, _)
-> not (String.is_prefix ~prefix:s line)
| Block (_, _, s)
-> String.is_substring ~substring:s line
in
let i = ref (n + 1) in
let len = Array.length lines_arr in
let found = ref (len - 1) in
while !i < len && !found = len - 1 do
if is_end lines_arr.(!i) then found := !i ;
incr i
done ;
match com_style with Line _ -> !found | Block _ -> !found
(** Heuristic to check if this looks like a copyright message. *)
let looks_like_copyright_message cstart cend lines_arr =
let max_len = 100 in
let check_len () =
let ok = ref true in
for i = cstart to cend do if String.length lines_arr.(i) > max_len then ok := false done ;
!ok
in
cstart >= 0 && cend - cstart <= 10 && check_len ()
let contains_monoidics cstart cend lines_arr =
let found = ref false in
for i = cstart to cend do
if String.is_substring ~substring:"Monoidics" lines_arr.(i) then found := true
done ;
!found
let get_fb_year cstart cend lines_arr =
let found = ref None in
let do_line line =
try
let fmt_re = Str.regexp "[0-9]+" in
let _ = Str.search_forward fmt_re line 0 in
let fmt_match = Str.matched_string line in
if String.length fmt_match = 4 then
try found := Some (int_of_string fmt_match)
with _ -> ()
with Not_found -> ()
in
for i = cstart to cend do
let line = lines_arr.(i) in
if String.is_substring ~substring:"Facebook" line then do_line line
done ;
!found
let pp_copyright mono fb_year com_style fmt _prefix =
let running_comment =
match com_style
with Line (s, _) | Block (_, s, _) -> s
in
let prefix = _prefix ^ running_comment in
let pp_line str = F.fprintf fmt "%s%s@\n" prefix str in
let pp_start () =
match com_style with
| Line (_, starts_with_newline)
-> if starts_with_newline then F.fprintf fmt "@\n"
| Block (start, _, _)
-> F.fprintf fmt "%s@\n" start
in
let pp_end () =
match com_style with
| Line _
-> F.fprintf fmt "@\n"
| Block (_, _, finish)
-> F.fprintf fmt "%s%s@\n" _prefix finish
in
pp_start () ;
if mono then pp_line " Copyright (c) 2009 - 2013 Monoidics ltd." ;
pp_line (F.sprintf " Copyright (c) %d - present Facebook, Inc." fb_year) ;
pp_line " All rights reserved." ;
pp_line "" ;
pp_line " This source code is licensed under the BSD style license found in the" ;
pp_line " LICENSE file in the root directory of this source tree. An additional grant" ;
pp_line " of patent rights can be found in the PATENTS file in the same directory." ;
pp_end ()
let copyright_has_changed mono fb_year com_style prefix cstart cend lines_arr =
let old_copyright =
let r = ref "" in
for i = cstart to cend do r := !r ^ lines_arr.(i) ^ "\n" done ;
!r
in
let new_copyright =
let pp fmt = pp_copyright mono fb_year com_style fmt prefix in
Format.asprintf "%t" pp
in
old_copyright <> new_copyright
let update_file fname mono fb_year com_style prefix cstart cend lines_arr =
try
[make] s/ocamlbuild/jbuilder/g Summary: Use jbuilder to build infer instead of ocamlbuild. This is mainly to get faster builds: ``` times in 10ms, ±differences measured in speedups, 4 cores | | ocb total | jb | ±total | ocb user | jb | ±user | ocb cpu | jb | ±cpu | ocb sys | jb | ±sys | |-----------------------------------+-----------+------+--------+----------+------+-------+---------+-----+------+---------+------+------| | byte from scratch | 6428 | 2456 | 2.62 | 7743 | 6662 | 1.16 | 138 | 331 | 2.40 | 1184 | 1477 | 0.80 | | native from scratch | 9841 | 4289 | 2.29 | 9530 | 8834 | 1.08 | 110 | 245 | 2.23 | 1373 | 1712 | 0.80 | | byte after native | 29578 | 1602 | 18.46 | 4514 | 4640 | 0.97 | 170 | 325 | 1.91 | 543 | 576 | 0.94 | | change infer.ml byte | 344 | 282 | 1.22 | 292 | 215 | 1.36 | 96 | 99 | 1.03 | 040 | 066 | 0.61 | | change infer.ml native | 837 | 223 | 3.75 | 789 | 174 | 4.53 | 98 | 99 | 1.01 | 036 | 47 | 0.77 | | change Config.ml byte | 451 | 339 | 1.33 | 382 | 336 | 1.14 | 97 | 122 | 1.26 | 056 | 80 | 0.70 | | change Config.ml native | 4024 | 1760 | 2.29 | 4585 | 4225 | 1.09 | 127 | 276 | 2.17 | 559 | 644 | 0.87 | | change cFrontend_config.ml byte | 348 | 643 | 0.54 | 297 | 330 | 0.90 | 96 | 67 | 0.70 | 038 | 102 | 0.37 | | change cFrontend_config.ml native | 1480 | 584 | 2.53 | 1435 | 906 | 1.58 | 106 | 185 | 1.75 | 136 | 178 | 0.76 | #+TBLFM: $4=$2/$3;f2::$7=$5/$6;f2::$10=$9/$8;f2::$13=$11/$12;f2 50 cores | | ocb total | jb | ±total | ocb user | jb | ±user | ocb cpu | jb | ±cpu | ocb sys | jb | ±sys | |---------------------+-----------+------+--------+----------+------+-------+---------+----+------+---------+------+------| | byte from scratch | 9114 | 2061 | 4.42 | 9334 | 5133 | 1.82 | | | 0/0 | 2566 | 1726 | 1.49 | | native from scratch | 13481 | 3967 | 3.40 | 12291 | 7608 | 1.62 | | | 0/0 | 3003 | 2100 | 1.43 | | byte after native | 3467 | 1476 | 2.35 | 5067 | 3912 | 1.30 | | | 0/0 | 971 | 801 | 1.21 | #+TBLFM: $4=$2/$3;f2::$7=$5/$6;f2::$10=$9/$8;f2::$13=$11/$12;f2 ``` Menu: 1. Write a jbuild file, autogenerated from jbuild.in because we need to fill in some information at build-time (really, at configure time, but TODO), such as whether or not clang is enabled. 2. Nuke lots of stuff from infer/src/Makefile that is now in the jbuild file 3. The jbuild file lives in infer/src/ so it can see all the sources. If we put it somewhere else, eg, infer/, then `jbuilder` scans too many files (all irrelevant) and takes 2.5s to start instead of .8s. Adding irrelevant directories to jbuild-ignore does not help. 4. jbuilder does not support subdirectories, so resort to listing all the source files in the generated jbuild (only source directories need to be manually listed in jbuild.in though). Still, the generated .merlin is wrong and makes merlin find source files in _build, so manually tune it to get good merlin support. We also lose some of merlin for unit tests as it cannot see their build artefacts anymore. 5. checkCopyright gets its own jbuild because it's standalone. Also, remove some deprecation warnings in checkCopyright due to the new version of Core from a while ago. 6. Drop less-used Makefile features (they had regressed anyway) such as building individual modules. Also, building mod_dep.pdf now takes all the source files available so they better build (before, it would only take the source files from the config, eg with or without clang) (that's pretty minor). 7. The toplevel is now built as a custom toplevel because that was easier. It should soon be even easier: https://github.com/janestreet/jbuilder/issues/210 8. Move BUILTINS.mli to BUILTINS.ml because jbuilder is not happy about interface files without implementations. In particular, I did not try to migrate too much of the Makefile logic to jbuilder, more can be done in the future. Reviewed By: jberdine Differential Revision: D5573661 fbshipit-source-id: 4ca6d8f
8 years ago
let cout = Out_channel.create fname in
let fmt = F.formatter_of_out_channel cout in
for i = 0 to cstart - 1 do F.fprintf fmt "%s@." lines_arr.(i) done ;
pp_copyright mono fb_year com_style fmt prefix ;
for i = cend + 1 to Array.length lines_arr - 1 do F.fprintf fmt "%s@\n" lines_arr.(i) done ;
F.fprintf fmt "@?" ;
Out_channel.close cout
with _ -> ()
let com_style_of_lang =
[ (".ml", comment_style_ocaml)
; (".mli", comment_style_ocaml)
; (".mly", comment_style_c)
; (".mll", comment_style_ocaml)
; (".re", comment_style_c)
; (".rei", comment_style_c)
; (".c", comment_style_c)
; (".h", comment_style_c)
; (".cpp", comment_style_c)
; (".m", comment_style_c)
; (".mm", comment_style_c)
; (".ll", comment_style_llvm)
; (".java", comment_style_c)
; (".sh", comment_style_shell)
; (".py", comment_style_shell)
; ("Makefile", comment_style_make)
; (".make", comment_style_make) ]
let file_should_have_copyright fname =
List.Assoc.mem com_style_of_lang ~equal:Filename.check_suffix fname
let output_diff fname lines_arr cstart n cend len mono fb_year com_style prefix =
let range = cend - cstart in
let lang = lang_of_com_style com_style in
F.eprintf "%s (start:%d n:%d end:%d len:%d range:%d lang:%s mono:%b year:%d)@." fname cstart n
cend len range lang mono fb_year ;
for i = cstart to cend do F.printf "%s@." lines_arr.(i) done ;
F.printf "-----@." ;
F.printf "@[<v>%a@]" (pp_copyright mono fb_year com_style) prefix ;
if !update_files then update_file fname mono fb_year com_style prefix cstart cend lines_arr
let check_copyright fname =
let lines = In_channel.with_file fname ~f:In_channel.input_lines in
let lines_arr = Array.of_list lines in
match find_copyright_line lines 0 with
| None
-> if file_should_have_copyright fname then
let year = 1900 + (Unix.localtime (Unix.time ())).Unix.tm_year in
let com_style = List.Assoc.find_exn com_style_of_lang ~equal:Filename.check_suffix fname in
let prefix = prefix_of_comment_style com_style in
let start = default_start_line_of_com_style com_style in
output_diff fname lines_arr start (-1) (-1) 0 false year com_style prefix ;
Pervasives.exit copyright_modified_exit_code
| Some n
-> let line = lines_arr.(n) in
let cstart, com_style = find_comment_start_and_style lines_arr n in
let cend = find_comment_end lines_arr n com_style in
if looks_like_copyright_message cstart cend lines_arr then (
let mono = contains_monoidics cstart cend lines_arr in
match get_fb_year cstart cend lines_arr with
| None
-> F.eprintf "Can't find fb year: %s@." fname ;
Pervasives.exit copyright_malformed_exit_code
| Some fb_year
-> let prefix = prefix_of_comment_style com_style in
if copyright_has_changed mono fb_year com_style prefix cstart cend lines_arr then
let len = String.length line in
output_diff fname lines_arr cstart n cend len mono fb_year com_style prefix ;
Pervasives.exit copyright_modified_exit_code )
else (
F.eprintf "Copyright not recognized: %s@." fname ;
Pervasives.exit copyright_malformed_exit_code )
let speclist = [("-i", Arg.Set update_files, "Update copyright notice in-place")]
let usage_msg = "checkCopyright [-i] file1 ..."
let () =
let to_check = ref [] in
let add_file_to_check fname = to_check := fname :: !to_check in
Arg.parse (Arg.align speclist) add_file_to_check usage_msg ;
List.iter ~f:check_copyright (List.rev !to_check) ;
Pervasives.exit 0