File: shelf.ml

package info (click to toggle)
herdtools7 7.58-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 19,732 kB
  • sloc: ml: 128,583; ansic: 3,827; makefile: 670; python: 407; sh: 212; awk: 14
file content (136 lines) | stat: -rw-r--r-- 5,356 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
(****************************************************************************)
(*                           the diy toolsuite                              *)
(*                                                                          *)
(* Jade Alglave, University College London, UK.                             *)
(* Luc Maranget, INRIA Paris-Rocquencourt, France.                          *)
(*                                                                          *)
(* Copyright 2010-present Institut National de Recherche en Informatique et *)
(* en Automatique, ARM Ltd and the authors. All rights reserved.            *)
(*                                                                          *)
(* This software is governed by the CeCILL-B license under French law and   *)
(* abiding by the rules of distribution of free software. You can use,      *)
(* modify and/ or redistribute the software under the terms of the CeCILL-B *)
(* license as circulated by CEA, CNRS and INRIA at the following URL        *)
(* "http://www.cecill.info". We also give a copy in LICENSE.txt.            *)
(****************************************************************************)

(** An OCaml representation of a shelf.py file. *)

module StringList = struct
  let compare = Base.List.compare String.compare
  let to_ocaml_string = Base.List.to_ocaml_string Base.String.to_ocaml_string
end
module StringListOption = struct
  let compare = Base.Option.compare StringList.compare
  let to_ocaml_string = Base.Option.to_ocaml_string StringList.to_ocaml_string
end

exception ParseError of string

type t = {
  record : string ;

  cats : string list ;
  configs : string list ;
  tests : string list ;

  bells : string list option ;
  compatibilities : string list option ;
}

let compare a b = Compare.chain [
  String.compare a.record b.record ;
  StringList.compare a.cats b.cats ;
  StringList.compare a.configs b.configs ;
  StringList.compare a.tests b.tests ;
  StringListOption.compare a.bells b.bells ;
  StringListOption.compare a.compatibilities b.compatibilities ;
]

let to_ocaml_string shelf = OcamlString.record [
  "record",              Base.String.to_ocaml_string       shelf.record ;
  "cats",                StringList.to_ocaml_string        shelf.cats ;
  "configs",             StringList.to_ocaml_string        shelf.configs ;
  "tests",               StringList.to_ocaml_string        shelf.tests ;
  "bells",               StringListOption.to_ocaml_string  shelf.bells ;
  "compatibilities",     StringListOption.to_ocaml_string  shelf.compatibilities ;
]

let python = lazy
  begin
    let exists p =
      let dev_null ch = ignore (Channel.read_lines ch) in
      try
        Command.run ~stdout:dev_null ~stderr:dev_null p ["--version"] ;
        true
      with
        | Unix.Unix_error _ -> false
        | Command.Error _ -> false
    in
    match List.find_opt exists ["python"; "python3"] with
    | Some p -> p
    | None -> failwith "Could not find either python or python3"
  end

let do_list_of_file sorted path key =
  (* Shelf files are executable Python code, so this Python script imports the
   * shelf.py file, then prints the given global variables. *)
  let script chan =
    Printf.fprintf chan "import os\n" ;
    Printf.fprintf chan "import sys\n" ;
    Printf.fprintf chan "os.chdir(%s)\n" (Filename.quote (Filename.dirname path)) ;
    Printf.fprintf chan "m = {}\n" ;
    Printf.fprintf chan "if sys.version_info >= (3, 0):\n" ;
    Printf.fprintf chan "  exec(open(%s).read(), m)\n" (Filename.quote (Filename.basename path)) ;
    Printf.fprintf chan "else:\n" ;
    Printf.fprintf chan "  execfile(%s, m)\n" (Filename.quote (Filename.basename path)) ;
    Printf.fprintf chan "try:\n" ;
    Printf.fprintf chan "  v = m[%s]\n" (Filename.quote key) ;
    Printf.fprintf chan "  if isinstance(v, str):\n" ;
    Printf.fprintf chan "    print(v)\n" ;
    Printf.fprintf chan "  else:\n" ;
    Printf.fprintf chan "    for x in v:\n" ;
    Printf.fprintf chan "      print(x)\n" ;
    Printf.fprintf chan "except:\n" ;
    Printf.fprintf chan "  pass\n" ;
    close_out chan
  in
  let lines = ref [] in
  let read_lines c = lines := Channel.read_lines c in
  begin try
    Command.run ~stdin:script ~stdout:read_lines (Lazy.force python) []
  with
    Command.Error e -> failwith (Command.string_of_error e)
  end ;
  if sorted then
    List.sort String.compare !lines
  else
    !lines

let list_of_file = do_list_of_file false
and list_of_file_sorted = do_list_of_file true

let string_of_file path key =
  match list_of_file path key with
  | x :: [] -> x
  | [] -> raise (ParseError (Printf.sprintf "Missing value for key %s" key))
  | xs -> raise (ParseError (Printf.sprintf "Expected single value for key %s, found %i" key (List.length xs)))

let optional_list_of_file path key =
  match list_of_file path key with
  | [] -> None
  | xs -> Some xs

let of_file path =
  {
    record = string_of_file path "record" ;

    cats               = list_of_file path "cats" ;
    configs            = list_of_file path "cfgs" ;
    tests =
      (match list_of_file_sorted path "illustrative_tests" with
      | [] -> list_of_file_sorted path "tests"
      | xs -> xs);
    bells           = optional_list_of_file path "bells" ;
    compatibilities = optional_list_of_file path "compatibilities" ;
  }