<commit_before>(* Copyright (C) 2015 <NAME>
 * Copyright (C) 2012 <NAME>
 * Copyright (C) 2010 <NAME>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation, with linking exceptions;
 * either version 2.1 of the License, or (at your option) any later
 * version. See COPYING file for details.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
 * 02111-1307, USA.
 *)

(* Discover available features *)

open Printf

(* +-----------------------------------------------------------------+
   | Search path                                                     |
   +-----------------------------------------------------------------+ *)

(* List of search paths for header files, mostly for MacOS
   users. libffi is installed by port systems into non-standard
   locations by default on MacOS.

   We use a hardcorded list of path + the ones from C_INCLUDE_PATH and
   LIBRARY_PATH.
*)

let ( // ) = Filename.concat

let default_search_paths =
  List.map (fun dir -> (dir ^ "/include", dir ^ "/lib")) [
    "/usr";
    "/usr/local";
    "/opt";
    "/opt/local";
    "/sw";
    "/mingw";
  ]

let is_win = Sys.os_type = "Win32"

let path_sep = if is_win then ";" else ":"

let split_path = Str.(split (regexp (path_sep ^ "+")))

let search_paths =
  let get var f =
    try
      List.map f (split_path (Sys.getenv var))
    with Not_found ->
      []
  in
  List.flatten [
    get "C_INCLUDE_PATH" (fun dir -> (dir, dir // ".." // "lib"));
    get "LIBRARY_PATH" (fun dir -> (dir // ".." // "include", dir));
    default_search_paths;
  ]

(* +-----------------------------------------------------------------+
   | Test codes                                                      |
   +-----------------------------------------------------------------+ *)

let caml_code = "
external test : unit -> unit = \"ffi_test\"
let () = test ()
"

let libffi_code = "
#include <caml/mlvalues.h>
#include <ffi.h>

CAMLprim value ffi_test()
{
  ffi_prep_closure(NULL, NULL, NULL, NULL);
  return Val_unit;
}
"

(* +-----------------------------------------------------------------+
   | Compilation                                                     |
   +-----------------------------------------------------------------+ *)

let ocamlc = ref "ocamlc"
let ext_obj = ref ".o"
let exec_name = ref "a.out"
let os_type = ref "Unix"
let ccomp_type = ref "cc"
let ffi_dir = ref ""
let is_homebrew = ref false
let homebrew_prefix = ref "/usr/local"

let log_file = ref ""
let caml_file = ref ""

(* Search for a header file in standard directories. *)
let search_header header =
  let rec loop = function
    | [] ->
        None
    | (dir_include, dir_lib) :: dirs ->
        if Sys.file_exists (dir_include // header) then
          Some (dir_include, dir_lib)
        else
          loop dirs
  in
  loop search_paths

let silent_remove filename =
  try Sys.remove filename
  with exn -> ()

let test_code (opt, lib) stub_code =
  let open Commands in
  let filename = Filename.temp_file "ctypes_libffi" ".c" in
  with_open_output_file ~filename begin fun oc ->
    let cleanup () = 
      silent_remove (Filename.(chop_extension (basename filename)) ^ !ext_obj)
    in
    unwind_protect ~cleanup (fun () ->
         output_string oc stub_code;
         Commands.command_succeeds
           "%s -custom %s %s %s %s > %s 2>&1"
           !ocamlc
           (String.concat " " (List.map (sprintf "-ccopt %s") opt))
           (Filename.quote filename)
           (Filename.quote !caml_file)
           (String.concat " " (List.map (sprintf "-cclib %s") lib))
           (Filename.quote !log_file)) ()
  end

let test_feature name test =
  begin
    fprintf stderr "testing for %s:%!" name;
    if test () then begin
      fprintf stderr " %s available\n%!" (String.make (34 - String.length name) '.');
      true
    end else begin
      fprintf stderr " %s unavailable\n%!" (String.make (34 - String.length name) '.');
      false
    end
  end

(* +-----------------------------------------------------------------+
   | pkg-config                                                      |
   +-----------------------------------------------------------------+ *)

let split = Str.(split (regexp " +"))

let brew_libffi_version flags =
  match Commands.command "brew ls libffi --versions | awk '{print $NF}' > %s 2>&1" !log_file with
    { Commands.status } when status <> 0 ->
    raise Exit
  | { Commands.stdout = "" } ->
    failwith "You need to 'brew install libffi' to get a suitably up-to-date version"
  | { Commands.stdout } ->
    String.trim stdout

let pkg_config flags =
  let output =
    if !is_homebrew then
      Commands.command
        "env PKG_CONFIG_PATH=%s/Cellar/libffi/%s/lib/pkgconfig %s/bin/pkg-config %s > %s 2>&1"
        !homebrew_prefix (brew_libffi_version ()) !homebrew_prefix flags !log_file
    else
      Commands.command "pkg-config %s > %s 2>&1" flags !log_file
  in
  match output with
    { Commands.status } when status <> 0 -> raise Exit
  | { Commands.stdout } -> split stdout

let pkg_config_flags name =
  try
    Some (ksprintf pkg_config "--cflags %s" name,
          ksprintf pkg_config "--libs %s" name)
  with Exit ->
    None

let get_homebrew_prefix log_file =
  match Commands.command "brew --prefix > %s" log_file with
    { Commands.status } when status <> 0 -> raise Exit
  | { Commands.stdout } -> String.trim stdout

let search_libffi_header () =
  match search_header "ffi.h" with
  | Some (dir_i, dir_l) ->
    (["-I" ^ dir_i], ["-L" ^ dir_l; "-lffi"])
  | None -><commit_msg>discover.ml: Don't catch Exit.<commit_after>(* Copyright (C) 2015 <NAME>
 * Copyright (C) 2012 <NAME>
 * Copyright (C) 2010 <NAME>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation, with linking exceptions;
 * either version 2.1 of the License, or (at your option) any later
 * version. See COPYING file for details.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
 * 02111-1307, USA.
 *)

(* Discover available features *)

open Printf

(* +-----------------------------------------------------------------+
   | Search path                                                     |
   +-----------------------------------------------------------------+ *)

(* List of search paths for header files, mostly for MacOS
   users. libffi is installed by port systems into non-standard
   locations by default on MacOS.

   We use a hardcorded list of path + the ones from C_INCLUDE_PATH and
   LIBRARY_PATH.
*)

let ( // ) = Filename.concat

let default_search_paths =
  List.map (fun dir -> (dir ^ "/include", dir ^ "/lib")) [
    "/usr";
    "/usr/local";
    "/opt";
    "/opt/local";
    "/sw";
    "/mingw";
  ]

let is_win = Sys.os_type = "Win32"

let path_sep = if is_win then ";" else ":"

let split_path = Str.(split (regexp (path_sep ^ "+")))

let search_paths =
  let get var f =
    try
      List.map f (split_path (Sys.getenv var))
    with Not_found ->
      []
  in
  List.flatten [
    get "C_INCLUDE_PATH" (fun dir -> (dir, dir // ".." // "lib"));
    get "LIBRARY_PATH" (fun dir -> (dir // ".." // "include", dir));
    default_search_paths;
  ]

(* +-----------------------------------------------------------------+
   | Test codes                                                      |
   +-----------------------------------------------------------------+ *)

let caml_code = "
external test : unit -> unit = \"ffi_test\"
let () = test ()
"

let libffi_code = "
#include <caml/mlvalues.h>
#include <ffi.h>

CAMLprim value ffi_test()
{
  ffi_prep_closure(NULL, NULL, NULL, NULL);
  return Val_unit;
}
"

(* +-----------------------------------------------------------------+
   | Compilation                                                     |
   +-----------------------------------------------------------------+ *)

let ocamlc = ref "ocamlc"
let ext_obj = ref ".o"
let exec_name = ref "a.out"
let os_type = ref "Unix"
let ccomp_type = ref "cc"
let ffi_dir = ref ""
let is_homebrew = ref false
let homebrew_prefix = ref "/usr/local"

let log_file = ref ""
let caml_file = ref ""

(* Search for a header file in standard directories. *)
let search_header header =
  let rec loop = function
    | [] ->
        None
    | (dir_include, dir_lib) :: dirs ->
        if Sys.file_exists (dir_include // header) then
          Some (dir_include, dir_lib)
        else
          loop dirs
  in
  loop search_paths

let silent_remove filename =
  try Sys.remove filename
  with exn -> ()

let test_code (opt, lib) stub_code =
  let open Commands in
  let filename = Filename.temp_file "ctypes_libffi" ".c" in
  with_open_output_file ~filename begin fun oc ->
    let cleanup () = 
      silent_remove (Filename.(chop_extension (basename filename)) ^ !ext_obj)
    in
    unwind_protect ~cleanup (fun () ->
         output_string oc stub_code;
         Commands.command_succeeds
           "%s -custom %s %s %s %s > %s 2>&1"
           !ocamlc
           (String.concat " " (List.map (sprintf "-ccopt %s") opt))
           (Filename.quote filename)
           (Filename.quote !caml_file)
           (String.concat " " (List.map (sprintf "-cclib %s") lib))
           (Filename.quote !log_file)) ()
  end

let test_feature name test =
  begin
    fprintf stderr "testing for %s:%!" name;
    if test () then begin
      fprintf stderr " %s available\n%!" (String.make (34 - String.length name) '.');
      true
    end else begin
      fprintf stderr " %s unavailable\n%!" (String.make (34 - String.length name) '.');
      false
    end
  end

(* +-----------------------------------------------------------------+
   | pkg-config                                                      |
   +-----------------------------------------------------------------+ *)

let split = Str.(split (regexp " +"))

let brew_libffi_version flags =
  match Commands.command "brew ls libffi --versions | awk '{print $NF}' > %s 2>&1" !log_file with
    { Commands.status } when status <> 0 ->
    raise Exit
  | { Commands.stdout = "" } ->
    failwith "You need to 'brew install libffi' to get a suitably up-to-date version"
  | { Commands.stdout } ->
    String.trim stdout

let pkg_config flags =
  let output =
    if !is_homebrew then
      Commands.command
        "env PKG_CONFIG_PATH=%s/Cellar/libffi/%s/lib/pkgconfig %s/bin/pkg-config %s > %s 2>&1"
        !homebrew_prefix (brew_libffi_version ()) !homebrew_prefix flags !log_file
    else
      Commands.command "pkg-config %s > %s 2>&1" flags !log_file
  in
  match output with
    { Commands.status } when status <> 0 -> None
  | { Commands.stdout } -> Some (split (String.trim stdout))

let pkg_config_flags name =
  match (ksprintf pkg_config "--cflags %s" name,
         ksprintf pkg_config "--libs %s" name) with
    Some opt, Some lib -> Some (opt, lib)
  | _ -> None

let get_homebrew_prefix log_file =
  match Commands.command "brew --prefix > %s" log_file with
    { Commands.status } when status <> 0 -> raise Exit
  | { Commands.stdout } -> String.trim stdout

let search_libffi_header () =
  match search_header "ffi.h" with
  | Some (dir_i, dir_l) ->
    (["-I" ^ dir_i], ["-L" ^ dir_l; "-lffi"])
  | None ->
{'fraction_non_alphanumeric': 0.19607521142950982, 'fraction_numerical': 0.007389769274981525, 'mean_word_length': 1.825986078886311, 'pattern_counts': {'":': 2, '<': 19, '<?xml version=': 0, '>': 78, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 16, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7116373', 'n_tokens_mistral': 3701, 'n_tokens_neox': 3570, 'n_words': 1183}