summaryrefslogtreecommitdiffstats
path: root/ocamlbuild/ocaml_utils.ml
blob: d42c884b7819b71e762179024c7eb43998ee9730 (plain)
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
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
(***********************************************************************)
(*                             ocamlbuild                              *)
(*                                                                     *)
(*  Nicolas Pouillard, Berke Durak, projet Gallium, INRIA Rocquencourt *)
(*                                                                     *)
(*  Copyright 2007 Institut National de Recherche en Informatique et   *)
(*  en Automatique.  All rights reserved.  This file is distributed    *)
(*  under the terms of the Q Public License version 1.0.               *)
(*                                                                     *)
(***********************************************************************)


(* Original author: Nicolas Pouillard *)
open My_std
open Format
open Log
open Pathname.Operators
open Tags.Operators
open Tools
open Flags
open Command;;


module S = Set.Make(String)

let flag_and_dep tags cmd_spec =
  flag tags cmd_spec;
  let ps = Command.fold_pathnames (fun p ps -> p :: ps) (Cmd cmd_spec) [] in
  dep tags ps

let stdlib_dir = lazy begin
  (* FIXME *)
  let ocamlc_where = sprintf "%s/ocamlc.where" (Pathname.pwd / !Options.build_dir) in
  let () = Command.execute ~quiet:true (Cmd(S[!Options.ocamlc; A"-where"; Sh">"; P ocamlc_where])) in
  String.chomp (read_file ocamlc_where)
end

let module_name_of_filename f = String.capitalize (Pathname.remove_extensions f)
let module_name_of_pathname x =
  module_name_of_filename (Pathname.to_string (Pathname.basename x))

let ignore_stdlib x =
  if !Options.nostdlib then false
  else
    let x' = !*stdlib_dir/((String.uncapitalize x)-.-"cmi") in
    Pathname.exists x'

let non_dependencies = ref []
let non_dependency m1 m2 =
  (* non_dependency was not supposed to accept pathnames without extension. *)
  if String.length (Pathname.get_extensions m1) = 0 then
    invalid_arg "non_dependency: no extension";
  non_dependencies := (m1, m2) :: !non_dependencies

let path_importance path x =
  if List.mem (path, x) !non_dependencies
  || (List.mem x !Options.ignore_list) then begin
    let () = dprintf 3 "This module (%s) is ignored by %s" x path in
    `ignored
  end
  else if ignore_stdlib x then `just_try else `mandatory

let expand_module include_dirs module_name exts =
  let dirname = Pathname.dirname module_name in
  let basename = Pathname.basename module_name in
  let module_name_cap = dirname/(String.capitalize basename) in
  let module_name_uncap = dirname/(String.uncapitalize basename) in
  List.fold_right begin fun include_dir ->
    List.fold_right begin fun ext acc ->
      include_dir/(module_name_uncap-.-ext) ::
      include_dir/(module_name_cap-.-ext) :: acc
    end exts
  end include_dirs []

let string_list_of_file file =
  with_input_file file begin fun ic ->
    Lexers.blank_sep_strings (Lexing.from_channel ic)
  end
let print_path_list = Pathname.print_path_list

let ocaml_ppflags tags =
  let flags = Flags.of_tags (tags++"ocaml"++"pp") in
  let reduced = Command.reduce flags in
  if reduced = N then N else S[A"-pp"; Quote reduced]

let ocaml_add_include_flag x acc =
  if x = Pathname.current_dir_name then acc else A"-I" :: A x :: acc

let ocaml_include_flags path =
  S (List.fold_right ocaml_add_include_flag (Pathname.include_dirs_of (Pathname.dirname path)) [])

let info_libraries = Hashtbl.create 103

let libraries = Hashtbl.create 103
let libraries_of m =
  try Hashtbl.find libraries m with Not_found -> []
let use_lib m lib = Hashtbl.replace libraries m (lib :: libraries_of m)

let ocaml_lib ?(extern=false) ?(byte=true) ?(native=true) ?dir ?tag_name libpath =
  let add_dir x =
    match dir with
    | Some dir -> S[A"-I"; P dir; x]
    | None -> x
  in
  let tag_name =
    match tag_name with
    | Some x -> x
    | None -> "use_" ^ Pathname.basename libpath
  in
  let flag_and_dep tags lib =
    flag tags (add_dir (A lib));
    if not extern then dep tags [lib] (* cannot happen? *)
  in
  Hashtbl.replace info_libraries tag_name (libpath, extern);
  if extern then begin
    if byte then
      flag_and_dep ["ocaml"; tag_name; "link"; "byte"] (libpath^".cma");
    if native then
      flag_and_dep ["ocaml"; tag_name; "link"; "native"] (libpath^".cmxa");
  end else begin
    if not byte && not native then
      invalid_arg "ocaml_lib: ~byte:false or ~native:false only works with ~extern:true";
  end;
  match dir with
  | None -> ()
  | Some dir -> flag ["ocaml"; tag_name; "compile"] (S[A"-I"; P dir])

let cmi_of = Pathname.update_extensions "cmi"

exception Ocamldep_error of string

let read_path_dependencies =
  let path_dependencies = Hashtbl.create 103 in
  let read path =
    let module_name = module_name_of_pathname path in
    let depends = path-.-"depends" in
    with_input_file depends begin fun ic ->
      let ocamldep_output =
        try Lexers.ocamldep_output (Lexing.from_channel ic)
        with Lexers.Error msg -> raise (Ocamldep_error(Printf.sprintf "Ocamldep.ocamldep: bad output (%s)" msg)) in
      let deps =
        List.fold_right begin fun (path, deps) acc ->
          let module_name' = module_name_of_pathname path in
          if module_name' = module_name 
          then List.union deps acc
          else raise (Ocamldep_error(Printf.sprintf "Ocamldep.ocamldep: multiple files in ocamldep output (%s not expected)" path))
        end ocamldep_output [] in
      let deps =
        if !Options.nostdlib && not (Tags.mem "nopervasives" (tags_of_pathname path)) then
          "Pervasives" :: deps
        else deps in
      let deps' = List.fold_right begin fun dep acc ->
        match path_importance path dep with
        | `ignored -> acc
        | (`just_try | `mandatory) as importance -> (importance, dep) :: acc
      end deps [] in
      Hashtbl.replace path_dependencies path
        (List.union (try Hashtbl.find path_dependencies path with Not_found -> []) deps');
      deps'
    end
  in read

let path_dependencies_of = memo read_path_dependencies