1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287
|
(*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*)
(* This file is part of the DpdGraph tools. *)
(* Copyright (C) 2009-2015 Anne Pacalet (Anne.Pacalet@free.fr) *)
(* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ *)
(* This file is distributed under the terms of the *)
(* GNU Lesser General Public License Version 2.1 *)
(* (see the enclosed LICENSE file for mode details) *)
(*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*)
DECLARE PLUGIN "coq-dpdgraph.plugin"
{
open Pp
open Stdarg
let debug msg = if false then Feedback.msg_debug msg
let feedback msg = Feedback.msg_notice (str "Info: " ++ msg)
let warning msg = Feedback.msg_warning (str "Warning: " ++ msg)
let error msg = CErrors.user_err msg
let filename = ref "graph.dpd"
let get_dirlist_grefs dirlist =
let selected_gref = ref [] in
let select gref kind env sigma constr =
if Search.module_filter (SearchInside dirlist) gref kind env sigma constr then
(debug (str "Select " ++ Printer.pr_global gref);
selected_gref := gref::!selected_gref)
in
Search.generic_search (Global.env()) (Evd.from_env (Global.env())) select;
!selected_gref
let is_prop gref id =
try
let t, ctx = Typeops.type_of_global_in_context (Global.env()) gref in
(* Beware of this code, not considered entirely correct, but I don't know
how to fix it. *)
let env = Environ.push_context ~strict:false (UVars.AbstractContext.repr ctx)
(Global.env ()) in
let s = (Typeops.infer_type env t).Environ.utj_type in
Sorts.is_prop s
with _ ->
begin
warning (str "unable to determine the type of the type for " ++ str id);
false
end;;
module G = struct
module Node = struct
type t = int * Names.GlobRef.t
let id n = fst n
let gref n = snd n
let compare n1 n2 = compare (id n1) (id n2)
let equal n1 n2 = 0 = compare n1 n2
let full_name n =
let qualid =
Nametab.shortest_qualid_of_global Names.Id.Set.empty (gref n)
in Libnames.string_of_qualid qualid
let split_name n =
let qualid =
Nametab.shortest_qualid_of_global Names.Id.Set.empty (gref n) in
let dirpath, ident = Libnames.repr_qualid qualid in
let dirpath = Names.DirPath.to_string dirpath in
let dirpath = if dirpath = "<>" then "" else dirpath in
let name = Names.Id.to_string ident in
(dirpath, name)
(*
let mod_list = Names.repr_dirpath dir_path in
let rec dirname l = match l with [] -> ""
| m::[] -> Names.string_of_id m
| d::tl -> (dirname tl)^"."^(Names.string_of_id d)
in (dirname mod_list, name)
*)
end
module Edge = struct
type t = Node.t * Node.t * int
let src (n1, _n2, _nb) = n1
let dst (_n1, n2, _nb) = n2
let nb_use (_n1, _n2, nb) = nb
let compare e1 e2 =
let cmp_src = Node.compare (src e1) (src e2) in
if cmp_src = 0 then Node.compare (dst e1) (dst e2) else cmp_src
end
module Edges = Set.Make (Edge)
module GMap = Map.Make (Names.GlobRef.UserOrd)
type t = int GMap.t * Edges.t
let empty = GMap.empty, Edges.empty
(** new numbers to store global references in nodes *)
let gref_cpt = ref 0
let nb_vertex (nds, _eds) = Hashtbl.length nds
let get_node (nds, eds) gref =
try Some (GMap.find gref nds, gref)
with Not_found -> None
(** *)
let add_node ((nds, eds) as g) gref =
match get_node g gref with
| Some n -> g, n
| None ->
gref_cpt := !gref_cpt + 1;
let nds = GMap.add gref !gref_cpt nds in
let n = (!gref_cpt, gref) in
(nds, eds), n
let add_edge (nds, eds) n1 n2 nb = nds, Edges.add (n1, n2, nb) eds
(* let succ (_nds, eds) n =
let do_e e acc =
if Node.equal n (Edge.src e) then (Edge.dst e)::acc else acc
in Edges.fold do_e eds []
let pred (_nds, eds) n =
let do_e e acc =
if Node.equal n (Edge.dst e) then (Edge.src e)::acc else acc
in Edges.fold do_e eds []
*)
let iter_vertex fv (nds, _eds) =
GMap.iter (fun gref id -> fv (id, gref)) nds
let iter_edges_e fe (_nds, eds) = Edges.iter fe eds
end
(** add the dependencies of gref in the graph (gref is already in).
* If [all], add also the nodes of the dependancies that are not in,
* and return the list of the new nodes,
* If not all, don't add nodes, and return an empty list. *)
let add_gref_dpds graph ~all n_gref todo =
let gref = G.Node.gref n_gref in
debug (str "Add dpds " ++ Printer.pr_global gref);
let add_dpd dpd nb_use (g, td) = match G.get_node g dpd with
| Some n -> let g = G.add_edge g n_gref n nb_use in g, td
| None ->
if all then
let g, n = G.add_node g dpd in
let g = G.add_edge g n_gref n nb_use in
g, n::td
else g, td
in
try
let data = Searchdepend.collect_dependance gref in
let graph, todo = Searchdepend.Data.fold add_dpd data (graph, todo) in
graph, todo
with Searchdepend.NoDef gref -> (* nothing to do *) graph, todo
(** add gref node and add it to the todo list
* to process its dependencies later. *)
let add_gref_only (graph, todo) gref =
debug (str "Add " ++ Printer.pr_global gref);
let graph, n = G.add_node graph gref in
let todo = n::todo in
graph, todo
(** add the gref in [l] and build the dependencies according to [all] *)
let add_gref_list_and_dpds graph ~all l =
let graph, todo = List.fold_left add_gref_only (graph, []) l in
let rec add_gref_dpds_rec graph todo = match todo with
| [] -> graph
| n::todo ->
let graph, todo = add_gref_dpds graph ~all n todo in
add_gref_dpds_rec graph todo
in
let graph = add_gref_dpds_rec graph todo in
graph
(** Don't forget to update the README file if something is changed here *)
module Out : sig
val file : G.t -> unit
end = struct
let add_cnst_attrib acc cnst =
let env = Global.env() in
let cb = Environ.lookup_constant cnst env in
let acc = match cb.Declarations.const_body with
| Declarations.OpaqueDef _
| Declarations.Def _ -> ("body", "yes")::acc
| Declarations.Undef _ -> ("body", "no")::acc
| Declarations.Primitive _ -> ("body", "no")::acc
| Declarations.Symbol _ -> ("body", "no")::acc
in acc
let add_gref_attrib acc gref id =
let is_prop = is_prop gref id in
let acc = ("prop", if is_prop then "yes" else "no")::acc in
let acc = match gref with
| Names.GlobRef.ConstRef cnst ->
let acc = ("kind", "cnst")::acc in
add_cnst_attrib acc cnst
| Names.GlobRef.IndRef _ ->
let acc = ("kind", "inductive")::acc in
acc
| Names.GlobRef.ConstructRef _ ->
let acc = ("kind", "construct")::acc in
acc
| Names.GlobRef.VarRef _ -> assert false
in acc
let pp_attribs fmt attribs =
List.iter (fun (a,b) -> Format.fprintf fmt "%s=%s, " a b) attribs
let out_node fmt g n =
let id = G.Node.id n in
let gref = G.Node.gref n in
let dirname, name = G.Node.split_name n in
let acc = if dirname = "" then [] else [("path", "\""^dirname^"\"")] in
let acc = add_gref_attrib acc gref name in
Format.fprintf fmt "N: %d \"%s\" [%a];@." id name
pp_attribs acc
let out_edge fmt _g e =
let edge_attribs = ("weight", string_of_int (G.Edge.nb_use e))::[] in
Format.fprintf fmt "E: %d %d [%a];@."
(G.Node.id (G.Edge.src e)) (G.Node.id (G.Edge.dst e))
pp_attribs edge_attribs
let out_graph fmt g =
G.iter_vertex (out_node fmt g) g;
G.iter_edges_e (out_edge fmt g) g
let file graph =
try
let oc = open_out !filename in
feedback (str "output dependencies in file " ++ (str !filename));
out_graph (Format.formatter_of_out_channel oc) graph;
close_out oc
with Sys_error msg ->
error (str "cannot open file: " ++ (str msg));
end
let mk_dpds_graph gref =
let graph = G.empty in
let all = true in (* get all the dependencies recursively *)
let graph = add_gref_list_and_dpds graph ~all [gref] in
Out.file graph
let file_graph_depend dirlist =
let graph = G.empty in
let grefs = get_dirlist_grefs dirlist in
let all = false in (* then add the dependencies only to existing nodes *)
let graph = add_gref_list_and_dpds graph ~all grefs in
Out.file graph
let locate_mp_dirpath qid =
try Nametab.dirpath_of_module (Nametab.locate_module qid)
with Not_found ->
let msg = str "Unknown module" ++ spc() ++ Libnames.pr_qualid qid in
CErrors.user_err ?loc:qid.CAst.loc msg
}
VERNAC COMMAND EXTEND DependGraphSetFile CLASSIFIED AS QUERY
| ["Set" "DependGraph" "File" string(str)] -> { filename := str }
END
(*
VERNAC ARGUMENT EXTEND dirpath
| [ string(str) ] -> { Globnames.dirpath_of_string str }
END
VERNAC ARGUMENT EXTEND dirlist
| [ dirpath(d) dirlist(l)] -> { d::l }
| [ dirpath(d) ] -> { [d] }
END
*)
VERNAC COMMAND EXTEND DependGraph CLASSIFIED AS QUERY
| ["Print" "DependGraph" reference(ref) ] ->
{ mk_dpds_graph (Nametab.global ref) }
| ["Print" "FileDependGraph" reference_list(dl) ] ->
{ file_graph_depend (List.map locate_mp_dirpath dl) }
END
|