summaryrefslogtreecommitdiffstats
path: root/ocamlbuild/ocaml_dependencies.ml
diff options
context:
space:
mode:
Diffstat (limited to 'ocamlbuild/ocaml_dependencies.ml')
-rw-r--r--ocamlbuild/ocaml_dependencies.ml219
1 files changed, 219 insertions, 0 deletions
diff --git a/ocamlbuild/ocaml_dependencies.ml b/ocamlbuild/ocaml_dependencies.ml
new file mode 100644
index 000000000..4a10bf1dc
--- /dev/null
+++ b/ocamlbuild/ocaml_dependencies.ml
@@ -0,0 +1,219 @@
+(***********************************************************************)
+(* ocamlbuild *)
+(* *)
+(* Nicolas Pouillard, Berke Durak, projet Gallium, INRIA Rocquencourt *)
+(* *)
+(* Copyright 2007 Institut National de Recherche en Informatique et *)
+(* en Automatique. All rights reserved. This file is distributed *)
+(* under the terms of the Q Public License version 1.0. *)
+(* *)
+(***********************************************************************)
+
+(* $Id$ *)
+(* Original author: Nicolas Pouillard *)
+open My_std
+open Log
+open Tools
+open Ocaml_utils
+
+let mydprintf fmt = dprintf 10 fmt
+
+exception Circular_dependencies of string list * string
+
+module type INPUT = sig
+ val fold_dependencies : (string -> string -> 'a -> 'a) -> 'a -> 'a
+ val fold_libraries : (string -> string list -> 'a -> 'a) -> 'a -> 'a
+ val fold_packages : (string -> string list -> 'a -> 'a) -> 'a -> 'a
+end
+
+module Make (I : INPUT) = struct
+ open I
+
+ module SMap = Map.Make(String)
+
+ module Resources = Resource.Resources
+
+ module Utils = struct
+ let add = SMap.add
+
+ let empty = SMap.empty
+
+ let find_all_set x acc =
+ try SMap.find x acc with Not_found -> Resources.empty
+
+ let smap_add_set src dst acc =
+ SMap.add src (Resources.add dst (find_all_set src acc)) acc
+
+ let print_smap pp f smap =
+ Format.fprintf f "@[<hv0>{:@[<hv2>";
+ SMap.iter begin fun k v ->
+ Format.fprintf f "@ @[<2>%S =>@ %a@];" k pp v
+ end smap;
+ Format.fprintf f "@]@,:}@]"
+
+ let print_smap_list = print_smap pp_l
+
+ let print_smap_set = print_smap Resources.print
+
+ let print_lazy pp f l = pp f !*l
+
+ let find_all_list x acc =
+ try SMap.find x acc with Not_found -> []
+
+ let find_all_rec xs map =
+ let visited = Hashtbl.create 32 in
+ let rec self x acc =
+ try
+ Hashtbl.find visited x; acc
+ with Not_found ->
+ Hashtbl.replace visited x ();
+ let acc = Resources.add x acc in
+ try Resources.fold self (SMap.find x map) acc
+ with Not_found -> acc
+ in List.fold_right self xs Resources.empty
+
+ let mkindex fold filter =
+ fold begin fun name contents acc ->
+ if filter name then
+ List.fold_right begin fun elt acc ->
+ add elt (name :: (find_all_list elt acc)) acc
+ end contents acc
+ else
+ acc
+ end empty
+
+ end
+ open Utils
+
+ let caml_transitive_closure
+ ?(caml_obj_ext="cmo")
+ ?(caml_lib_ext="cma")
+ ?(pack_mode=false)
+ ?(used_libraries=[])
+ ?(hidden_packages=[]) fns =
+
+ let valid_link_exts =
+ if pack_mode then [caml_obj_ext; "cmi"]
+ else [caml_obj_ext; caml_lib_ext] in
+
+ mydprintf "caml_transitive_closure@ ~caml_obj_ext:%S@ ~pack_mode:%b@ ~used_libraries:%a@ %a"
+ caml_obj_ext pack_mode pp_l used_libraries pp_l fns;
+
+ let packages = fold_packages (fun name _ -> Resources.add name) Resources.empty in
+ mydprintf "packages:@ %a" Resources.print packages;
+
+ let caml_obj_ext_of_cmi x =
+ if Filename.check_suffix x ".cmi" then
+ Pathname.update_extensions caml_obj_ext x
+ else x in
+
+ let maybe_caml_obj_ext_of_cmi x =
+ if pack_mode then
+ if Filename.check_suffix x ".cmi" then
+ let caml_obj = Pathname.update_extensions caml_obj_ext x in
+ if Pathname.exists_in_build_dir caml_obj then
+ caml_obj
+ else
+ x
+ else
+ x
+ else
+ if Filename.check_suffix x ".cmi" then
+ Pathname.update_extensions caml_obj_ext x
+ else x in
+
+ let not_linkable x =
+ not (List.exists (Pathname.check_extension x) valid_link_exts) in
+
+ let dependency_map =
+ fold_dependencies begin fun x y acc ->
+ let x = maybe_caml_obj_ext_of_cmi x
+ and y = maybe_caml_obj_ext_of_cmi y in
+ if x = y || not_linkable x || not_linkable y then acc
+ else smap_add_set x y acc
+ end SMap.empty in
+ mydprintf "dependency_map:@ %a" print_smap_set dependency_map;
+
+ let used_files = find_all_rec fns dependency_map in
+ mydprintf "used_files:@ %a" Resources.print used_files;
+
+ let open_packages =
+ Resources.fold begin fun file acc ->
+ if Resources.mem file packages && not (List.mem file hidden_packages)
+ then file :: acc else acc
+ end used_files [] in
+ mydprintf "open_packages:@ %a" pp_l open_packages;
+
+ let index_filter ext list x =
+ Pathname.check_extension x ext && List.mem x list in
+
+ let lib_index =
+ lazy (mkindex fold_libraries (index_filter caml_lib_ext used_libraries)) in
+ mydprintf "lib_index:@ %a" (print_lazy print_smap_list) lib_index;
+
+ let package_index =
+ lazy (mkindex fold_packages (index_filter caml_obj_ext open_packages)) in
+
+ let rec resolve_packages x =
+ match find_all_list x !*package_index with
+ | [] -> x
+ | [x] -> resolve_packages x
+ | pkgs ->
+ failwith (sbprintf "the file %S is included in more than one active open package (%a)"
+ x pp_l pkgs) in
+
+ let libs_of x = find_all_list x !*lib_index in
+
+ let lib_of x =
+ match libs_of x with
+ | [] -> None
+ | [lib] -> Some(lib)
+ | libs ->
+ failwith (sbprintf "the file %S is included in more than one active library (%a)"
+ x pp_l libs) in
+
+ let convert_dependency src dst acc =
+ let src = resolve_packages src in
+ let dst = resolve_packages dst in
+ let add_if_diff x y = if x = y then acc else smap_add_set x y acc in
+ match (lib_of src, lib_of dst) with
+ | None, None -> add_if_diff src dst
+ | Some(liba), Some(libb) -> add_if_diff liba libb
+ | Some(lib), None -> add_if_diff lib dst
+ | None, Some(lib) -> add_if_diff src lib in
+
+ let dependencies = lazy begin
+ SMap.fold begin fun k ->
+ Resources.fold (convert_dependency k)
+ end dependency_map empty
+ end in
+
+ mydprintf "dependencies:@ %a" (print_lazy print_smap_set) dependencies;
+
+ let dependencies_of x =
+ try SMap.find x !*dependencies with Not_found -> Resources.empty in
+
+ let needed = ref [] in
+ let seen = ref [] in
+ let rec aux fn =
+ if sys_file_exists fn && not (List.mem fn !needed) then begin
+ if List.mem fn !seen then raise (Circular_dependencies (!seen, fn));
+ seen := fn :: !seen;
+ Resources.iter begin fun f ->
+ if sys_file_exists f then
+ if Filename.check_suffix f ".cmi" then
+ let f' = caml_obj_ext_of_cmi f in
+ if f' <> fn then
+ if sys_file_exists f' then aux f'
+ else if pack_mode then aux f else ()
+ else ()
+ else aux f
+ end (dependencies_of fn);
+ needed := fn :: !needed
+ end
+ in
+ List.iter aux fns;
+ mydprintf "caml_transitive_closure:@ %a ->@ %a" pp_l fns pp_l !needed;
+ List.rev !needed
+
+end