  type project_scenario_variable

  let c_project_scenario_variable : project_scenario_variable structure typ =
    structure "project_scenario_variable"

  let scenario_var_name = field c_project_scenario_variable "name" string

  let scenario_var_value = field c_project_scenario_variable "value" string

  let () = seal c_project_scenario_variable

  let create_project_unit_provider =
    foreign ~from:c_lib "${capi.get_name("create_project_unit_provider")}"
    (string @-> string @-> ptr c_project_scenario_variable @-> string
     @-> string @-> raisable c_type)

  let dec_ref =
    let f =
      foreign ~from:c_lib "${capi.get_name("dec_ref_unit_provider")}"
        (c_type @-> raisable void)
    in
    (fun v ->
      f (!@ v))

  let wrap v =
    allocate ~finalise:dec_ref (ptr void) v

  let for_project
      ?(project = "")
      ?(scenario_vars = [])
      ?(target = "")
      ?(runtime = "")
      project_file =
    (* One more to store (null, null)*)
    let scenario_vars_len = List.length scenario_vars + 1 in
    let c_scenario_vars =
      (* Allocates a fresh array with given size, fill with zeros. Thus,
       the last cell is already (null, null) *)
      allocate_n c_project_scenario_variable ~count:scenario_vars_len
    in
    let fill_scenario_vars i (name, value) =
      let c_struct = make c_project_scenario_variable in
      setf c_struct scenario_var_name name ;
      setf c_struct scenario_var_value value ;
      c_scenario_vars +@ i <-@ c_struct
    in
    List.iteri fill_scenario_vars scenario_vars ;
    let result = create_project_unit_provider
                 project_file project
                 c_scenario_vars
                 target runtime
    in
    if is_null result then
      raise (InvalidProjectError ("Cannot open or interpret project " ^ project_file))
    else
      wrap result

  let create_auto_provider =
    foreign ~from:c_lib "${capi.get_name("create_auto_provider")}"
      (ptr (ptr char) @-> string @-> raisable c_type)

  let auto input_files =
    (* Convert the names of the input files into pointers to C strings. We used
       to use the high-level type [Ctypes.string] type, but this was causing
       memory corruption problems. We switched to [ptr char] instead. *)
    let cstrings =
      List.map (fun f -> CArray.(start (of_string f))) input_files
    in
    (* Add a null pointer at the end. This is part of the LAL calling
       convention. *)
    let null_ptr = from_voidp char Ctypes.null in
    let cstrings_null = List.rev_append cstrings [null_ptr] in
    (* Create an array with all these pointers *)
    let array = CArray.of_list (ptr char) cstrings_null in
    let ptr = CArray.start array in
    let result = create_auto_provider ptr "" in
    wrap result
