File: configure.ml

package info (click to toggle)
ocaml-obuild 0.1.11-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 796 kB
  • sloc: ml: 6,570; sh: 171; ansic: 34; makefile: 11
file content (231 lines) | stat: -rw-r--r-- 8,028 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
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
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
open Ext.Fugue
open Ext.Filepath
open Ext.Compat
open Ext
open Helper
open Printf
open Gconf

exception ConfigChanged of string
exception ToolNotFound of filename
exception ConfigurationMissingKey of string
exception ConfigurationTypeMismatch of string * string * string
exception ConfigureScriptFailed of string

type flag_action =
  | SetFlag of string
  | ClearFlag of string

let getDigestKV () =
  let digest = Project.digest () in
  [ ("obuild-digest", digest) ]

let generateMlFile project file flags =
  Utils.generateFile file (fun add ->
      add "(* autogenerated file by obuild. do not modify *)\n";
      add (sprintf "let project_name = \"%s\"\n" project.Analyze.project_file.Project.name);
      add (sprintf "let project_version = \"%s\"\n" project.Analyze.project_file.Project.version);
      (* TODO escape name properly *)
      List.iter (fun (name, v) -> add (sprintf "let project_flag_%s = %b\n" name v)) flags)

let generateCFile project file flags =
  Utils.generateFile file (fun add ->
      add "/* autogenerated file by obuild. do not modify */\n";
      add (sprintf "#define PROJECT_NAME \"%s\"\n" project.Analyze.project_file.Project.name);
      add (sprintf "#define PROJECT_VERSION \"%s\"\n" project.Analyze.project_file.Project.version);
      (* TODO escape name properly *)
      List.iter
        (fun (name, v) ->
          add (sprintf "#define PROJECT_FLAG_%s %d\n" (string_uppercase name) (if v then 1 else 0)))
        flags)

let makeSetup digestKV project flags =
  hashtbl_fromList
    (digestKV
    @ hashtbl_toList project.Analyze.project_ocamlcfg
    @ List.map (fun (opt, v) -> (opt, string_of_bool v)) (Gconf.get_target_options ())
    @ List.map (fun (flagname, flagval) -> ("flag-" ^ flagname, string_of_bool flagval)) flags)

let sanityCheck () =
  let (_ : string) = Prog.getOcamlOpt () in
  let (_ : string) = Prog.getOcamlC () in
  let (_ : string) = Prog.getOcamlDep () in
  ()

let comparekvs reason setup l =
  List.iter
    (fun (k, v) ->
      try
        let v' = Hashtbl.find setup k in
        if v' <> v then
          raise (ConfigChanged reason)
      with Not_found -> raise (ConfigChanged reason))
    l

let comparekvs_hashtbl reason setup l =
  Hashtbl.iter
    (fun k v ->
      try
        let v' = Hashtbl.find setup k in
        if v' <> v then
          raise (ConfigChanged reason)
      with Not_found -> raise (ConfigChanged reason))
    l

let execute_configure_script proj_file =
  match proj_file.Project.configure_script with
  | None -> ()
  | Some script -> (
      let args = [ Prog.getOcaml (); fp_to_string script ] in
      match Process.run args with
      | Process.Success (_, warnings, _) -> print_warnings warnings
      | Process.Failure er -> raise (ConfigureScriptFailed er))

let create_dist project flags =
  verbose Verbose "configuration changed, deleting dist\n%!";
  Filesystem.removeDirContent Dist.build_path;
  Dist.remove_dead_links ();
  verbose Verbose "auto-generating configuration files\n%!";
  let autogenDir = Dist.create_build Dist.Autogen in
  generateMlFile project (autogenDir </> fn "path_generated.ml") flags;
  generateCFile project (autogenDir </> fn "obuild_macros.h") flags

let get_assoc name assoc =
  try
    let v = List.assoc name assoc in
    Some v
  with Not_found -> None

let get_flags_value proj_file setup_flags user_flags =
  List.map
    (fun flag ->
      let name = flag.Project.Flag.name in
      let def = flag.Project.Flag.default in
      let override = ref (get_assoc name setup_flags) in
      List.iter
        (fun tw ->
          match tw with
          | ClearFlag s -> if s = name then override := Some false
          | SetFlag s -> if s = name then override := Some true)
        user_flags;
      match (!override, def) with
      | None, None -> (name, false)
      | None, Some v -> (name, v)
      | Some v, _ -> (name, v))
    proj_file.Project.flags

let check_extra_tools proj_file =
  let syspath = Utils.get_system_paths () in
  List.iter
    (fun tool ->
      try
        let _ = Utils.find_in_paths syspath tool in
        ()
      with Utils.FileNotFoundInPaths _ -> raise (ToolNotFound tool))
    proj_file.Project.extra_tools

let get_flags hash =
  Hashtbl.fold
    (fun k v acc ->
      if string_startswith "flag-" k then
        (string_drop 5 k, bool_of_string v) :: acc
      else
        acc)
    hash []

let bool_of_opt hashtable k =
  let get_opt k =
    try Hashtbl.find hashtable k with Not_found -> raise (ConfigurationMissingKey k)
  in
  let v = get_opt k in
  try bool_of_string v with Failure _ -> raise (ConfigurationTypeMismatch (k, "bool", v))

let set_opts hashtable =
  (* load the environment *)
  let opts = Gconf.get_target_options_keys () in
  List.iter (fun k -> Gconf.set_target_options k (bool_of_opt hashtable k)) opts

let check_ocaml () =
  let ocamlCfg = Prog.getOcamlConfig () in
  let ocaml_ver = Hashtbl.find ocamlCfg "version" in
  let ver = string_split '.' ocaml_ver in
  (match ver with
  | major :: minor :: _ ->
      if int_of_string major < 4 then gconf.bin_annot <- false;
      if int_of_string major > 4 && int_of_string minor > 1 then gconf.short_path <- true
  | _ -> gconf.bin_annot <- false);
  ocamlCfg

let run proj_file user_flags user_opts =
  Dist.create_maybe ();
  let _ = check_ocaml () in
  let digestKV = getDigestKV () in
  execute_configure_script proj_file;
  let configure = try Some (Dist.read_configure ()) with _ -> None in
  let configure_flags =
    match configure with
    | None -> []
    | Some h ->
        (* set opts and return the flags *)
        Hashtbl.iter
          (fun k _ ->
            if not (string_startswith "flag-" k) then
              Gconf.set_target_options k (bool_of_opt h k))
          h;
        get_flags h
  in
  let flags = get_flags_value proj_file configure_flags user_flags in
  verbose Debug "  configure flag: [%s]\n"
    (Utils.showList "," (fun (n, v) -> n ^ "=" ^ string_of_bool v) flags);
  check_extra_tools proj_file;
  let project = Analyze.prepare proj_file flags in
  (* let's set the user opts before saving the setup file *)
  List.iter (fun (o, v) -> Gconf.set_target_options o v) user_opts;
  let currentSetup = makeSetup digestKV project flags in
  let actualSetup = try Some (Dist.read_setup ()) with _ -> None in
  let projectSystemChanged =
    match actualSetup with
    | None -> true
    | Some stp -> (
        (* TODO harcoded for now till we do all the checks. *)
        try
          comparekvs "setup" stp (hashtbl_toList currentSetup);
          (* FORCED should be false *) true
        with _ -> true)
  in

  if projectSystemChanged then (
    create_dist project flags;
    (* write setup file *)
    verbose Verbose "Writing new setup\n%!";
    Dist.write_setup currentSetup)

let check proj_file reconf setup =
  let ocamlCfg = check_ocaml () in
  let digestKV = getDigestKV () in
  (* check if the environment changed. *)
  comparekvs_hashtbl "ocaml config" setup ocamlCfg;
  (* if the digest of .obuild changed, let's reconfigure *)
  let reconfigure =
    try
      comparekvs "digest" setup digestKV;
      false
    with e -> if reconf then true else raise e
  in
  (* user_flags are also restored from setup file *)
  let setup_flags = get_flags setup in
  let flags = get_flags_value proj_file setup_flags [] in
  (* .obuild changed, maybe we should compare a little bit deeper to not retriggerd reconf too often ... *)
  if reconfigure then (
    (* let's call configure-script if available, however we don't care about the content of dist/configure *)
    execute_configure_script proj_file;
    verbose Debug "  configure flag: [%s]\n"
      (Utils.showList "," (fun (n, v) -> n ^ "=" ^ string_of_bool v) flags);
    check_extra_tools proj_file;
    let project = Analyze.prepare proj_file flags in
    create_dist project flags;
    (* write setup file *)
    verbose Verbose "Writing new setup\n%!";
    let current_setup = makeSetup digestKV project flags in
    Dist.write_setup current_setup);
  flags