#!/usr/bin/env ocaml

(* ocamldep.wrapper <filename> ... <filename> - <ocamldep command>
   runs the <ocamldep command> in an environment where all of the
   <filenames> listed appear to exist. The files are created, if
   required, before the command is run, and destroyed afterwards. *)

(* An earlier version of this script acquired a lock, so as to prevent
   multiple instances of this script from interfering with one another.
   However, this did not prevent interference between this script and
   some other process (e.g., the ocaml compiler) which creates files.
   So, the lock has been removed. My suggestion is to never use this
   script in a concurrent setting. If you wish to use parallel make,
   then you might be able to use a two-level Makefile approach: first,
   compute all dependencies in a sequential manner; then, construct all
   targets in a parallel manner. *)

#load "unix.cma"
open Printf

(* Parse the command line. The arguments that precede "-" are understood
   as file names and stored in the list [xs]. The arguments that follow
   "-" are understood as a command and stored in [command]. *)

let xs =
  ref []

let command =
  ref ""

let verbose =
  ref false

let rec loop accumulating i =
  if i = Array.length Sys.argv then
    ()
  else if accumulating then
    (* [accumulating] is [true] as long as we have not found the "-" marker *)
    match Sys.argv.(i) with
    | "-v" ->
	verbose := true;
	loop true (i+1)
    | "-" ->
        (* We have found the marker. The next parameter should be the name of
	   the raw [ocamldep] command. Copy it to the command (unquoted --
	   apparently some shells do not permit quoting a command name). *)
	let i = i + 1 in
	assert (i < Array.length Sys.argv);
	command := Sys.argv.(i);
	(* Stop accumulating file names. Copy the remaining arguments into
	   the command. *)
	loop false (i+1)
    | _ ->
        (* Continue accumulating file names in [xs]. *)
	xs := Sys.argv.(i) :: !xs;
	loop true (i+1)
  else begin
    (* After we have found the "-" marker, the remaining arguments are
       copied (quoted) into the command. *)
    command := sprintf "%s %s" !command (Filename.quote Sys.argv.(i));
    loop false (i+1)
  end

let () =
  loop true 1

(* Create the required files if they don't exist, run the command,
   then destroy any files that we have created. *)

let rec loop = function
  | [] ->
      if !verbose then
	fprintf stderr "ocamldep.wrapper: running %s\n" !command;
      Sys.command !command
  | x :: xs ->
      if Sys.file_exists x then
	loop xs
      else begin
	if !verbose then
	  fprintf stderr "ocamldep.wrapper: creating fake %s\n" x;
	let c = open_out x in
	close_out c;
	let exitcode = loop xs in
	if Sys.file_exists x then begin
	  try
	    if !verbose then
	      fprintf stderr "ocamldep.wrapper: removing fake %s..." x;
	    Sys.remove x;
	    if !verbose then
	      fprintf stderr " ok\n"
	  with Sys_error _ ->
	    if !verbose then
	      fprintf stderr " failed\n"
	end;
	exitcode
      end

let () =
  exit (loop !xs)
