|
|
|
(*
|
|
|
|
* Copyright (c) 2015-present, Facebook, Inc.
|
|
|
|
*
|
|
|
|
* This source code is licensed under the MIT license found in the
|
|
|
|
* LICENSE file in the root directory of this source tree.
|
|
|
|
*)
|
|
|
|
|
[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
|
|
|
|
|
|
|
|
type copyright_event = CopyrightMalformed | CopyrightModified
|
|
|
|
|
|
|
|
exception CopyrightEvent of copyright_event
|
|
|
|
|
|
|
|
let exit_code_of_event = function CopyrightModified -> 1 | CopyrightMalformed -> 3
|
|
|
|
|
|
|
|
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 * bool (** block comments, eg ("(*", "*", "*)") for ocaml *)
|
|
|
|
[@@deriving compare]
|
|
|
|
|
|
|
|
let equal_comment_style = [%compare.equal: comment_style]
|
|
|
|
|
|
|
|
let comment_style_al = Line ("//", false)
|
|
|
|
|
|
|
|
let comment_style_c = Block ("/*", "*", "*/", false)
|
|
|
|
|
|
|
|
let comment_style_llvm = Line (";", true)
|
|
|
|
|
|
|
|
let comment_style_m4 = Line ("dnl", false)
|
|
|
|
|
|
|
|
let comment_style_make = Line ("#", false)
|
|
|
|
|
|
|
|
let comment_style_ocaml = Block ("(*", "*", "*)", false)
|
|
|
|
|
|
|
|
let comment_style_php = Line ("//", true)
|
|
|
|
|
|
|
|
let comment_style_python = Line ("#", false)
|
|
|
|
|
|
|
|
let comment_style_shell = Line ("#", true)
|
|
|
|
|
|
|
|
let comment_styles_lang =
|
|
|
|
[ (comment_style_al, "AL")
|
|
|
|
; (comment_style_c, "C")
|
|
|
|
; (comment_style_llvm, "LLVM")
|
|
|
|
; (comment_style_m4, "M4")
|
|
|
|
; (comment_style_make, "Makefile")
|
|
|
|
; (comment_style_ocaml, "OCaml")
|
|
|
|
; (comment_style_php, "PHP")
|
|
|
|
; (comment_style_python, "python")
|
|
|
|
; (comment_style_shell, "shell") ]
|
|
|
|
|
|
|
|
|
|
|
|
let lang_of_comment_style style =
|
|
|
|
List.Assoc.find_exn ~equal:equal_comment_style comment_styles_lang style
|
|
|
|
|
|
|
|
|
|
|
|
let comment_styles = List.map comment_styles_lang ~f:fst
|
|
|
|
|
|
|
|
let starts_with_newline = function
|
|
|
|
| Line (_, starts_with_newline) | Block (_, _, _, starts_with_newline) ->
|
|
|
|
starts_with_newline
|
|
|
|
|
|
|
|
|
|
|
|
let default_start_line_of_com_style style = if starts_with_newline style then 1 else 0
|
|
|
|
|
|
|
|
let indent_of_comment_style = function
|
|
|
|
| Line _ ->
|
|
|
|
""
|
|
|
|
| Block (_, inter, _, _) ->
|
|
|
|
String.make (String.length inter) ' '
|
|
|
|
|
|
|
|
|
|
|
|
let keep_going = ref false
|
|
|
|
|
|
|
|
(** If true, update the copyright message of the files. *)
|
|
|
|
let update_files = ref false
|
|
|
|
|
|
|
|
let show_diff = ref false
|
|
|
|
|
|
|
|
let line_contains_copyright line = String.is_substring ~substring:"opyright " line
|
|
|
|
|
|
|
|
let find_copyright_line lines =
|
|
|
|
List.findi lines ~f:(fun _ line -> line_contains_copyright line) |> Option.map ~f:fst
|
|
|
|
|
|
|
|
|
|
|
|
let array_rev_find_mapi_from array ~from ~f =
|
|
|
|
let i = ref from in
|
|
|
|
let found = ref None in
|
|
|
|
while !i >= 0 && Option.is_none !found do
|
|
|
|
found := f !i array.(!i) ;
|
|
|
|
decr i
|
|
|
|
done ;
|
|
|
|
!found
|
|
|
|
|
|
|
|
|
|
|
|
let find_comment_start_and_style lines 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.(n) ->
|
|
|
|
if starts_with_newline then n <> 0 else true
|
|
|
|
| _ ->
|
|
|
|
false )
|
|
|
|
in
|
|
|
|
let is_start i line =
|
|
|
|
match cur_line_comment with
|
|
|
|
| Some (Line _) ->
|
|
|
|
cur_line_comment
|
|
|
|
| Some (Block _) | None ->
|
|
|
|
List.find comment_styles ~f:(function
|
|
|
|
| Block (s, _, _, starts_with_newline) when String.is_substring ~substring:s line ->
|
|
|
|
if starts_with_newline then i <> 0 else true
|
|
|
|
| _ ->
|
|
|
|
false )
|
|
|
|
in
|
|
|
|
let find_in_line i line = is_start i line |> Option.map ~f:(fun style -> (i, style)) in
|
|
|
|
array_rev_find_mapi_from lines ~from:n ~f:find_in_line
|
|
|
|
|
|
|
|
|
|
|
|
let find_comment_end lines n com_style =
|
|
|
|
let is_end line =
|
|
|
|
match com_style with
|
|
|
|
| Line (s, _) ->
|
|
|
|
(not (String.is_prefix ~prefix:s line), `After)
|
|
|
|
| Block (_, _, s, _) ->
|
|
|
|
(String.is_substring ~substring:s line, `OnIt)
|
|
|
|
in
|
|
|
|
let i = ref (n + 1) in
|
|
|
|
let len = Array.length lines in
|
|
|
|
let found = ref (len - 1) in
|
|
|
|
while !i < len && !found = len - 1 do
|
|
|
|
( match is_end lines.(!i) with
|
|
|
|
| true, `OnIt ->
|
|
|
|
found := !i
|
|
|
|
| true, `After ->
|
|
|
|
found := !i - 1
|
|
|
|
| false, _ ->
|
|
|
|
() ) ;
|
|
|
|
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 =
|
|
|
|
let max_len = 100 in
|
|
|
|
let check_len () =
|
|
|
|
let ok = ref true in
|
|
|
|
for i = cstart to cend do
|
|
|
|
if String.length lines.(i) > max_len then ok := false
|
|
|
|
done ;
|
|
|
|
!ok
|
|
|
|
in
|
|
|
|
cstart >= 0 && cend - cstart <= 10 && check_len ()
|
|
|
|
|
|
|
|
|
|
|
|
let contains_string ~substring cstart cend lines =
|
|
|
|
let found = ref false in
|
|
|
|
for i = cstart to cend do
|
|
|
|
if String.is_substring ~substring lines.(i) then found := true
|
|
|
|
done ;
|
|
|
|
!found
|
|
|
|
|
|
|
|
|
|
|
|
let contains_monoidics cstart cend lines = contains_string ~substring:"Monoidics" cstart cend lines
|
|
|
|
|
|
|
|
let contains_ropas cstart cend lines = contains_string ~substring:"ROPAS" cstart cend lines
|
|
|
|
|
|
|
|
let get_copyright_year cstart cend lines =
|
|
|
|
let found = ref None in
|
|
|
|
let do_line line =
|
|
|
|
try
|
|
|
|
let fmt_re = Str.regexp "[0-9]+" in
|
|
|
|
ignore (Str.search_forward fmt_re line 0) ;
|
|
|
|
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 Caml.Not_found -> ()
|
|
|
|
in
|
|
|
|
for i = cstart to cend do
|
|
|
|
let line = lines.(i) in
|
|
|
|
if String.is_substring ~substring:"Copyright" line then do_line line
|
|
|
|
done ;
|
|
|
|
!found
|
|
|
|
|
|
|
|
|
|
|
|
let pp_copyright ~monoidics ~ropas ~copyright_year com_style fmt =
|
|
|
|
let running_comment = match com_style with Line (s, _) | Block (_, s, _, _) -> s in
|
|
|
|
let indent = indent_of_comment_style com_style in
|
|
|
|
let pp_line str =
|
|
|
|
F.kfprintf (fun fmt -> F.fprintf fmt "@\n") fmt ("%s%s" ^^ str) indent running_comment
|
|
|
|
in
|
|
|
|
let pp_start () =
|
|
|
|
match com_style with Line _ -> () | Block (start, _, _, _) -> F.fprintf fmt "%s@\n" start
|
|
|
|
in
|
|
|
|
let pp_end () =
|
|
|
|
match com_style with
|
|
|
|
| Line _ ->
|
|
|
|
()
|
|
|
|
| Block (_, _, finish, _) ->
|
|
|
|
F.fprintf fmt "%s%s@\n" indent finish
|
|
|
|
in
|
|
|
|
pp_start () ;
|
|
|
|
if ropas then (
|
|
|
|
pp_line " Copyright (c) 2016-present, Programming Research Laboratory (ROPAS)" ;
|
|
|
|
pp_line " Seoul National University, Korea" ;
|
|
|
|
pp_line " Copyright (c) 2017-present, Facebook, Inc." )
|
|
|
|
else (
|
|
|
|
if monoidics then pp_line " Copyright (c) 2009-2013, Monoidics ltd." ;
|
|
|
|
pp_line " Copyright (c) %d-present, Facebook, Inc." copyright_year ) ;
|
|
|
|
pp_line "" ;
|
|
|
|
pp_line " This source code is licensed under the MIT license found in the" ;
|
|
|
|
pp_line " LICENSE file in the root directory of this source tree." ;
|
|
|
|
pp_end ()
|
|
|
|
|
|
|
|
|
|
|
|
let copyright_has_changed fname lines ~notice_range:(cstart, cend) ~monoidics ~ropas
|
|
|
|
~copyright_year com_style =
|
|
|
|
let old_copyright =
|
|
|
|
let r = ref "" in
|
|
|
|
for i = cstart to cend do
|
|
|
|
r := !r ^ lines.(i) ^ "\n"
|
|
|
|
done ;
|
|
|
|
!r
|
|
|
|
in
|
|
|
|
let new_copyright =
|
|
|
|
Format.asprintf "%t" (pp_copyright ~monoidics ~ropas ~copyright_year com_style)
|
|
|
|
in
|
|
|
|
let changed = not (String.equal old_copyright new_copyright) in
|
|
|
|
if !show_diff && changed then (
|
|
|
|
let with_suffix fname suff = Filename.basename fname ^ suff in
|
|
|
|
let orig_fname = with_suffix fname ".orig" in
|
|
|
|
let new_fname = with_suffix fname ".new" in
|
|
|
|
Out_channel.write_lines orig_fname [old_copyright] ;
|
|
|
|
Out_channel.write_lines new_fname [new_copyright] ;
|
|
|
|
() ) ;
|
|
|
|
changed
|
|
|
|
|
|
|
|
|
|
|
|
let com_style_of_lang =
|
|
|
|
[ (".ac", comment_style_m4)
|
|
|
|
; (".al", comment_style_al)
|
|
|
|
; (".atd", comment_style_ocaml)
|
|
|
|
; (".c", comment_style_c)
|
|
|
|
; (".cpp", comment_style_c)
|
|
|
|
; (".h", comment_style_c)
|
|
|
|
; (".inc", comment_style_c)
|
|
|
|
; (".java", comment_style_c)
|
|
|
|
; (".ll", comment_style_llvm)
|
|
|
|
; (".m", comment_style_c)
|
|
|
|
; (".m4", comment_style_m4)
|
|
|
|
; (".make", comment_style_make)
|
|
|
|
; (".mk", comment_style_make)
|
|
|
|
; (".ml", comment_style_ocaml)
|
|
|
|
; (".mli", comment_style_ocaml)
|
|
|
|
; (".mll", comment_style_ocaml)
|
|
|
|
; (".mly", comment_style_c)
|
|
|
|
; (".mm", comment_style_c)
|
|
|
|
; (".php", comment_style_php)
|
|
|
|
; (".py", comment_style_python)
|
|
|
|
; (".re", comment_style_c)
|
|
|
|
; (".rei", comment_style_c)
|
|
|
|
; (".sh", comment_style_shell)
|
|
|
|
; ("dune.in", comment_style_ocaml)
|
|
|
|
; ("dune.common.in", comment_style_ocaml)
|
|
|
|
; ("dune-common.in", comment_style_ocaml)
|
|
|
|
; ("dune-workspace.in", comment_style_llvm)
|
|
|
|
; ("Makefile", comment_style_make) ]
|
|
|
|
|
|
|
|
|
|
|
|
let comment_style_of_filename fname =
|
|
|
|
List.Assoc.find com_style_of_lang ~equal:Filename.check_suffix fname
|
|
|
|
|
|
|
|
|
|
|
|
let output_diff ~fname lines ?notice_range ?(monoidics = false) ?(ropas = false) ~copyright_year
|
|
|
|
com_style =
|
|
|
|
let lang = lang_of_comment_style com_style in
|
|
|
|
let pp_range_opt fmt = function
|
|
|
|
| None ->
|
|
|
|
F.pp_print_string fmt "none"
|
|
|
|
| Some (s, e) ->
|
|
|
|
F.fprintf fmt "%d-%d" s e
|
|
|
|
in
|
|
|
|
F.eprintf "%s (lang:%s notice:%a monoidics:%b ropas:%b year:%d)\n%!" fname lang pp_range_opt
|
|
|
|
notice_range monoidics ropas copyright_year ;
|
|
|
|
let pp_newfile fmt =
|
|
|
|
let copy_lines_before, copy_lines_after =
|
|
|
|
match notice_range with
|
|
|
|
| Some (s, e) ->
|
|
|
|
(s - 1, e + 1)
|
|
|
|
| None ->
|
|
|
|
let insert_notice_at = default_start_line_of_com_style com_style in
|
|
|
|
(insert_notice_at - 1, insert_notice_at)
|
|
|
|
in
|
|
|
|
for i = 0 to copy_lines_before do
|
|
|
|
F.fprintf fmt "%s\n" lines.(i)
|
|
|
|
done ;
|
|
|
|
if
|
|
|
|
starts_with_newline com_style && copy_lines_before > 0 && lines.(copy_lines_before - 1) <> ""
|
|
|
|
then F.fprintf fmt "@\n" ;
|
|
|
|
pp_copyright ~monoidics ~ropas ~copyright_year com_style fmt ;
|
|
|
|
for i = copy_lines_after to Array.length lines - 1 do
|
|
|
|
F.fprintf fmt "%s\n" lines.(i)
|
|
|
|
done ;
|
|
|
|
F.fprintf fmt "%!"
|
|
|
|
in
|
|
|
|
if !update_files then
|
|
|
|
Out_channel.with_file fname ~f:(fun cout ->
|
|
|
|
let fmt = F.formatter_of_out_channel cout in
|
|
|
|
pp_newfile fmt )
|
|
|
|
else pp_newfile F.std_formatter
|
|
|
|
|
|
|
|
|
|
|
|
let check_copyright fname =
|
|
|
|
let lines_list = In_channel.read_lines fname in
|
|
|
|
let lines = Array.of_list lines_list in
|
|
|
|
match (find_copyright_line lines_list, comment_style_of_filename fname) with
|
|
|
|
| None, None ->
|
|
|
|
()
|
|
|
|
| None, Some com_style ->
|
|
|
|
let copyright_year = 1900 + (Unix.localtime (Unix.time ())).Unix.tm_year in
|
|
|
|
output_diff ~fname lines ~copyright_year com_style ;
|
|
|
|
raise (CopyrightEvent CopyrightModified)
|
|
|
|
| Some n, fname_com_style -> (
|
|
|
|
let cstart, contents_com_style =
|
|
|
|
find_comment_start_and_style lines n |> Option.value ~default:(0, Line ("#", false))
|
|
|
|
in
|
|
|
|
let com_style =
|
|
|
|
match fname_com_style with
|
|
|
|
| None ->
|
|
|
|
contents_com_style
|
|
|
|
| Some fname_com_style ->
|
|
|
|
let inferred_styles_agree =
|
|
|
|
match (fname_com_style, contents_com_style) with
|
|
|
|
| Line (fs, _), Line (cs, _) ->
|
|
|
|
String.equal fs cs
|
|
|
|
| Block (fstart, fbody, fend, _), Block (cstart, cbody, cend, _) ->
|
|
|
|
String.equal fstart cstart && String.equal fbody cbody && String.equal fend cend
|
|
|
|
| _ ->
|
|
|
|
false
|
|
|
|
in
|
|
|
|
if not inferred_styles_agree then (
|
|
|
|
F.eprintf
|
|
|
|
"Inferred comment style doesn't match the filename '%s':@\n\
|
|
|
|
From the filename I was expecting the %s comment style, but looking inside the \
|
|
|
|
file I found %s instead.@."
|
|
|
|
fname
|
|
|
|
(lang_of_comment_style fname_com_style)
|
|
|
|
(lang_of_comment_style contents_com_style) ;
|
|
|
|
raise (CopyrightEvent CopyrightMalformed) ) ;
|
|
|
|
fname_com_style
|
|
|
|
in
|
|
|
|
(* hack to detect shebangs regardless of the inferred style *)
|
|
|
|
let com_style =
|
|
|
|
match com_style with
|
|
|
|
| Line ("#", false) when String.is_prefix ~prefix:"#!" lines.(0) ->
|
|
|
|
comment_style_shell
|
|
|
|
| _ ->
|
|
|
|
com_style
|
|
|
|
in
|
|
|
|
let cend = find_comment_end lines n com_style in
|
|
|
|
if not (looks_like_copyright_message cstart cend lines) then (
|
|
|
|
F.eprintf "Copyright not recognized: %s@." fname ;
|
|
|
|
raise (CopyrightEvent CopyrightMalformed) ) ;
|
|
|
|
let monoidics = contains_monoidics cstart cend lines in
|
|
|
|
let ropas = contains_ropas cstart cend lines in
|
|
|
|
match get_copyright_year cstart cend lines with
|
|
|
|
| None ->
|
|
|
|
F.eprintf "Can't find copyright year: %s@." fname ;
|
|
|
|
raise (CopyrightEvent CopyrightMalformed)
|
|
|
|
| Some copyright_year ->
|
|
|
|
if
|
|
|
|
copyright_has_changed fname lines ~notice_range:(cstart, cend) ~monoidics ~ropas
|
|
|
|
~copyright_year com_style
|
|
|
|
then (
|
|
|
|
output_diff ~fname lines ~notice_range:(cstart, cend) ~monoidics ~ropas ~copyright_year
|
|
|
|
com_style ;
|
|
|
|
raise (CopyrightEvent CopyrightModified) ) )
|
|
|
|
|
|
|
|
|
|
|
|
let speclist =
|
|
|
|
[ ("-i", Arg.Set update_files, "Update copyright notice in-place")
|
|
|
|
; ("--inplace", Arg.Set update_files, "Update copyright notice in-place")
|
|
|
|
; ("-k", Arg.Set keep_going, "Exit with code 0 no matter what")
|
|
|
|
; ("--keep-going", Arg.Set keep_going, "Exit with code 0 no matter what")
|
|
|
|
; ( "--show-diff"
|
|
|
|
, Arg.Set show_diff
|
|
|
|
, "Write file.orig and file.new files to inspect the differences found" ) ]
|
|
|
|
|
|
|
|
|
|
|
|
let usage_msg = "checkCopyright [-i] [-k] [--show-diff] file1 ..."
|
|
|
|
|
|
|
|
let () =
|
|
|
|
let to_check = ref [] in
|
|
|
|
let add_file_to_check fname =
|
|
|
|
(* hack: LICENSE looks copyrightable but is not... *)
|
|
|
|
if not (String.is_prefix ~prefix:"LICENSE" (Filename.basename fname)) then
|
|
|
|
to_check := fname :: !to_check
|
|
|
|
in
|
|
|
|
Arg.parse (Arg.align speclist) add_file_to_check usage_msg ;
|
|
|
|
let to_check = List.rev !to_check in
|
|
|
|
let exit_code = ref 0 in
|
|
|
|
List.iter to_check ~f:(fun file ->
|
|
|
|
try check_copyright file with CopyrightEvent event ->
|
|
|
|
if not !keep_going then exit_code := exit_code_of_event event ) ;
|
|
|
|
exit !exit_code
|