open StdLabels

module Ocaml_version : sig
  type t

  val v407 : t
  val v408 : t
  val current : t
  val compare : t -> t -> int
end = struct
  type t = int * int

  let parse s =
    try
      let d1 = String.index_from s 0 '.' in
      let d2 =
        try String.index_from s (d1 + 1) '.' with
        | Not_found -> String.length s
      in
      let p1 = int_of_string (String.sub s ~pos:0 ~len:d1) in
      let p2 = int_of_string (String.sub s ~pos:(d1 + 1) ~len:(d2 - d1 - 1)) in
      p1, p2
    with
    | _ -> failwith (Printf.sprintf "Invalid ocaml version %S" s)
  ;;

  let v407 = parse "4.07"
  let v408 = parse "4.08"
  let current = parse Sys.ocaml_version

  let compare ((a1, b1) : t) ((a2, b2) : t) =
    match compare a1 a2 with
    | 0 -> compare b1 b2
    | c -> c
  ;;
end

let () =
  let ocaml_where, oc =
    match Sys.argv with
    | [| _; "-ocaml-where"; ocaml_where; "-o"; fn |] -> ocaml_where, open_out fn
    | _ -> failwith "bad command line arguments"
  in
  let pr fmt = Printf.fprintf oc (fmt ^^ "\n") in
  pr "(* This file is automatically generated *)";
  pr "";
  if Ocaml_version.(compare current v407) >= 0
  then pr "include Stdlib"
  else (
    (* The cma format is documented in typing/cmo_format.mli in the compiler sources *)
    let ic =
      let ( ^/ ) = Filename.concat in
      try open_in_bin (ocaml_where ^/ "stdlib" ^/ "stdlib.cma") with
      | Sys_error _ -> open_in_bin (ocaml_where ^/ "stdlib.cma")
    in
    let len_magic_number = String.length Config.cma_magic_number in
    let magic_number = really_input_string ic len_magic_number in
    assert (magic_number = Config.cma_magic_number);
    let toc_pos = input_binary_int ic in
    seek_in ic toc_pos;
    let toc : Cmo_format.library = input_value ic in
    close_in ic;
    let units =
      List.map toc.lib_units ~f:(fun cu -> cu.Cmo_format.cu_name)
      |> List.sort ~cmp:String.compare
    in
    let max_len =
      List.fold_left units ~init:0 ~f:(fun acc unit -> max acc (String.length unit))
    in
    List.iter units ~f:(fun u -> pr "module %-*s = %s" max_len u u);
    pr "";
    pr "include Pervasives");
  pr "";
  if Ocaml_version.(compare current v407) < 0 then pr "module Float  = struct end";
  if Ocaml_version.(compare current v408) < 0
  then (
    pr "module Bool   = struct end";
    pr "module Int    = struct end";
    pr "module Option = struct end";
    pr "module Result = struct end";
    pr "module Unit   = struct end";
    pr "module Fun    = struct end");
  pr "";
  pr "exception Not_found = Not_found"
;;
