{"id":19825886,"url":"https://github.com/octachron/codept","last_synced_at":"2025-04-09T17:24:12.313Z","repository":{"id":13764812,"uuid":"70633592","full_name":"Octachron/codept","owner":"Octachron","description":"Contextual Ocaml DEPendencies Tool:  alternative ocaml dependency analyzer","archived":false,"fork":false,"pushed_at":"2025-03-18T22:39:27.000Z","size":1795,"stargazers_count":59,"open_issues_count":7,"forks_count":10,"subscribers_count":8,"default_branch":"master","last_synced_at":"2025-04-02T11:51:26.830Z","etag":null,"topics":["dependency-analysis","ocaml"],"latest_commit_sha":null,"homepage":"","language":"OCaml","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Octachron.png","metadata":{"files":{"readme":"README.md","changelog":"Changelog.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2016-10-11T20:42:20.000Z","updated_at":"2025-03-18T22:39:31.000Z","dependencies_parsed_at":"2023-02-17T22:00:29.687Z","dependency_job_id":"a21390aa-12d4-46fc-90b9-4ba3c59494b5","html_url":"https://github.com/Octachron/codept","commit_stats":{"total_commits":991,"total_committers":8,"mean_commits":123.875,"dds":"0.038345105953582204","last_synced_commit":"db65b76903577eb4f0064238ed60eb7ca7d91c69"},"previous_names":[],"tags_count":10,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Octachron%2Fcodept","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Octachron%2Fcodept/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Octachron%2Fcodept/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Octachron%2Fcodept/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Octachron","download_url":"https://codeload.github.com/Octachron/codept/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248075559,"owners_count":21043614,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["dependency-analysis","ocaml"],"created_at":"2024-11-12T11:08:58.224Z","updated_at":"2025-04-09T17:24:12.288Z","avatar_url":"https://github.com/Octachron.png","language":"OCaml","funding_links":[],"categories":[],"sub_categories":[],"readme":"Codept intends to be a dependency solver for OCaml projects and an alternative to ocamldep. Compared to ocamldep, codept major features are:\n\n* whole project analysis\n* extensive warning and error messages\n* json or s-expression format for dependencies\n* uniform handling of delayed alias dependencies\n* full support for nested module hierarchy\n* (experimental) full dependencies, when dependencies up to transitive closure are not enough\n\nBoth ocamldep and codept compute an over-approximation of the dependency graph of OCaml projects. However, codept uses whole project analysis to reduce the number of fictitious dependencies inferred at the project scale, whereas ocamldep is, by design, limited to local file analysis.\n\nConsequently, bugs notwithstanding, codept computes an exact dependency graph in any situation that does not involve unknown external modules or first class modules, and is still reliable in some standard use cases of first class modules (see this [riddle](tests/cases/riddle.ml) as an illustration of why first class modules can be problematic).\n\nMoreover, codept will emit warning messages any time it encounters a source of potential inaccuracies in the dependency graph. In other words, if no warnings are emitted by codept, the computed dependencies are ensured to be exact.\n\n\nAnother important point is that codept's whole project analysis feature makes it possible to handle uniformly the delayed dependency aspect of module aliases introduced by the compiler `-no-alias-deps` option.\n\nNested module hierarchy is also fully supported by codept starting with\nversion 0.10. In particular, when the `-nested` option is enabled, a file with path\n`dir_1/…/dir_N/a.ml` will be mapped to the module `Dir_1. … . Dir_n. A ` instead\nof just `A`.\n\nAt last, in situation where dependencies up to transitive closure are not precise enough, codept's experimental `-expand-deps` option can track more precisely type aliases induced dependencies, making it easier to track all cmi files required to compile a given file for instance.\n\nBasic performance measures indicate that the average time increase when compared to ocamldepranges between 10% to 50%.\n\n### Cycle detection\n\nAs an illustration of codept error messages, in presence of a cycle in the\ndependency graph, `codept` will emit a warning message detailing both the cycle\nand where the cycles was introduced in the source tree.\n\nFor instance, on the following set of four files `a.ml`, `b.ml`, `c.ml` and\n`d.ml`\n```Ocaml\n(* a.ml *)\nopen B\n```\n```OCaml\n(* b.ml *)\n2;;\nopen C\n```\n\n```OCaml\n(* c.ml *)\n2;\n3;;\nopen D\n```\n\n\n```OCaml\n(* d.ml *)\n2;\n3;\n4;;\nopen A\n```\ncodept yields:\n\n```bash\n$ codept a.ml b.ml c.ml d.ml\n[Fatal error]: Solver failure\n   −Circular dependencies:\n      A −(a.ml:l2.5−6)⟶\n      B −(b.ml:l3.5−6)⟶\n      C −(c.ml:l4.5−6)⟶\n      D −(d.ml:l5.5−6)⟶ A\n```\n\nBy default, this error is a fatal error and codept stops here.\nWhen prototyping, it can be useful to ignore this setback and compute approximate\ndependencies even in presence of cycle. This can be achieved using\nthe `-k` option that sets up `codept` to ignore any somehow recoverable errors.\nNote that the cycle approximation is not yet specified ans is especially unwieldy\ncombined with the `-no-alias-deps` option.\n\n\n\n### First class module limitations\nIn presence of first class modules, codept may infer fictitious dependencies.\nMore precisely, if a first class module whose signature cannot be locally inferred at the point of binding is opened or included, then subsequent access to submodules of this first class module will generate fictitious dependencies. For instance, for a file `a.ml` such as\n```OCaml\n(* a.ml *)\nmodule type S = sig module B: sig end end\nlet f (x : (module S)) = x\nlet g x =\n  let (module M) = f x in\n  let open M in\n  let open B in\n  ()\n\n```\n`codept a.ml` generates a fictitious `B` dependency and one warning\n```\n[Warning]: a.ml:l6.11−12,\n  First-class module M was opened while its signature was unknown.\n```\n\n# Codept overview\n\nIn more details, `codept` works by combining together three main ingredients:\n\n- an AST, called M2l, specialized to handle only module level constructions\n  (see [M2l](lib/m2l.mli) )\n\n- an interruptible outliner which given an environment and a\n  `m2l` Ast computes either the signature represented by the m2l ast, or in\n  presence of non-resolved dependencies, a simplified m2l ast.\n  (see [Outliner](lib/outliner.mli))\n\n- an environment module that tracks resolved names, external module sources and\n  dependencies. (see [Envt](lib/envt.mli)).\n\nCurrently, these three elements are then used in one of the two basic solvers implemented (see [Solver](lib/solver.mli)).\n\n\nGiven a list of \".ml\" and \".mli\" files and a starting environment, the default solver iterates over the list of unresolved files and try to compute their signature.\nIf the computation is successful, the resulting signature is added to the current environment and the current file is removed from the list of unresolved files. Otherwise the solver continues its iteration.\n\nThe directed solved start with a list of leaf modules and then trace back the\nancestors of those leaf modules.\n\nCycles and non-resolvable dependencies are detected when the solver does not make any progress after one cycle of iterations over unresolved files.\n\n# Usage\n\nCodept can be used as a drop-in replacement for ocamldep, on Linux at least.\nMore tests are needed on other platforms. Unfortunately, most of OCaml build systems\nare built around ocamldep limitations and would not benefit directly from replacing ocamldep by codept.\n\nA possible exception is self-cycle detection: invoking codept\non the following \"a.ml\" file\n```\n(* a.ml *)\nopen A\n```\nyields directly a circular dependency error:\n```\n[Fatal error]: Solver failure\n  −Circular dependencies:  A −(a.ml:l2.5−6)⟶ A\n\n```\n\n\nSee the [integration](#integration) section for a better overview\non how to use codept with ocaml build tools.\n\n\n## Compatibility with ocamldep\nMost of the ocamldep options are also supported by codept.\n\nHowever, some of the ocamldep options are slightly reinterpreted:\n\n  * `-as-map \u003cfile\u003e` and `-map \u003cfile\u003e` are both reinterpreted to use the\n  codept specific `-no-alias-deps` option which provides a better handling of\n  delayed alias dependencies.\n\n  * `-open \u003cmodule\u003e` does not open the module `\u003cmodule\u003e` when analyzing the\n    `\u003cmodule.ml\u003e` or `\u003cmodule.mli\u003e`\n\n  * `-allow-approx` uses a new experimental heuristic for parsing syntactically\n    invalid file that might be more precise − or brittle. More tests are needed.\n    See also the more generic `-k` option.\n\nAnother possible difference between codept and ocamldep output is codept built-in detection of dependency cycles. With default options, cycles triggers a fatal error message within codept and stops the current analysis. If the option `-k` is specified,\nthe analysis try to go on by ignoring the submodule structure of cycle when inside the cycle.\n\n\n## Codept-only options\n\n### Structured output\n\nSome new options enables to serialize files in s-expression format for ulterior\nuses by codept:\n\n  * *`-deps`, `-json`, `-sexp`\n  print the inferred module using either a json or a s-expression format.\n  By default `-deps` uses `json`. The corresponding json schema can be found\n  at [json-schemata/deps].\n\n  * `-sig`\n  export the inferred module signatures in a structured format that can\n  be read directly by codept (default:s-expression)\n\n  * `-m2l`\n  export the m2l ast in a structured format (that can be parsed by codept)\n  (default:s-expression)\n\n### Solver and outliner options\n\nSome new options modify the behavior of either the solver or the outliner used\nby codept\n\n  * `-closed-world`\n    stop the analysis as soon as a non-resolvable module is identified. Contrarily, codept default mode assumes that non-resolvable module have for signature `sig end` (this approximation can only lead to an over-approximation of dependencies).\n\n\n  * `-expand-deps`\n    computes exact dependencies, rather than a subset of dependencies  that is equivalent to the exact dependency set up to transitive closure\n\n  * `-k`\n    ignore most recoverable errors and keep going\n\n  * `-L \u003cdir\u003e`\n    use cmi files in directory `\u003cdir\u003e` to resolve unknown module names during the analysis.\n\n  * `-no-alias-deps`\n    delay alias dependency up to the use point of the alias.\n    For instance, in the code fragment `module M = A open M` the `A` dependency is recorded only when the module `M` is opened in `open M` not during the definition of the alias.\n\n\n   * `-o filename`\n     set the output file for the subsequent modes. Multiple outputs can be specified for the same invocation of codept.\n\n   * `-ancestors-of modulename`\n     only analyze files which are an ancestor of the module `modulename`.Note that the input name is capitalized and extension are removed to avoid some discomfort.\n\n\n   * `-extension-node bool`\n   decide what to do with extension nodes, if `bool` is true, extension node are considered as transparent and analyzed, otherwise they are left alone. Note that ocaml built-in extension nodes (i.e. `[%extension_constructor … ]` nodes)  are always analyzed and are not affected by this option.\n\n\n  * `-sig-only`\n  delete the information that are not necessary for computing signatures\n\n### Findlib options\n\nAll options available with `ocamlfind ocamldep` are also available for `codept`, in particular:\n\n  * `-pkg \u003cmodule_name\u003e` or `package \u003cmodule_name\u003e `\n    equivalent to `-L $(ocamlfind query module_name)`\n\n\n### Miscellaneous output\n\nOther new options explore codept possibilities and intermediary representations\n\n  * ` -m2l-info`\n    print a human-readable `m2l` intermediary representation of the source files\n    rather than their dependencies\n\n  * `-info`\n    print a textual representation of the result of codept analysis.\n\n  * `-dot`\n    export the dependency graph in the graphviz format\n\n\n  * `-inner-modules`, `-unknown-modules` and `-extern-modules`\n    refine the `-modules` option by splitting the list of dependencies\n    in three subsets:\n      *  inner modules are the one provided to `codept` directly through the\n         command line,\n      *  external modules are modules discovered due to either the `-pkg`\n         or `-L` options or precomputed package (like the standard library),\n      *  unknown modules are the one that could not be resolved.\n\n\n### Warning and error messages\n\nWarning and error messages, referred together as fault messages can be controled\nextensively:\n\n  * `-fatal level` set the fatal level for faults.\n\n  * `-fault-doc` lists all possible fault messages\n\n  * `-fault path.name=level` set the level of the fault, level can vary from\n            `info`, `notification`, `warning`, `error` to `critical`.\n\n  * `-verbosity level` selects the minimal level of displayed fault messages.\n\n\nFor a more exhaustive list of options, see the codept's man page.\n\n# Installation\n\nFor the dev version:\n`opam pin add codept https://github.com/Octachron/codept.git`\n\nVersion 0.9 is currently avaliable on opam.\n\n# Integration with build tools\n\nLike `ocamldep`, codept can be used to generate `.depends` file for integration\nwith a makefile based infrastructure.\n\nCombining `codept` with `ocamlbuild` currently requires more efforts.\nA library is provided by the`codept.ocamlbuild` subpackage to ease\nsuch integration. Depending on the constraints, `codept` can be enabled\nby writing the following simple ocamlbuild file\n```OCaml\n(*myocamlbuild.ml*)\nCodept_ocamlbuild.dispatch ()\n```\nand adding the `-plugin-tag \"package(codept.ocamlbuild)\"` option to ocamlbuild\ncommand line:\n```\nocamlbuild -plugin-tag \"package(codept.ocamlbuild)\"  …\n```\n\nBetter integration with existing tools is still a work in progress.\n\n\n# Comparison between ocamldep and codept\n\nTo precise the difference between ocamldep and codept, ocamldep introduces fictional dependencies when opening foreign submodules within a given unit. For instance, if we have two files, `a.ml`\n```OCaml\n(* a.ml *)\nopen B\nopen C\nopen D\n```\nand b.ml\n```OCaml\n(* b.ml *)\nmodule C = struct end\nmodule D = struct end\n```\n`ocamldep -modules a.ml b.ml` produces\n```\na.ml: B C D\nb.ml:\n```\nIn this output, the dependencies `C` and `D` for the unit A are fictitious.\nContrarily, `codept -modules a.ml b.ml` uses all the information available in both file at once and produces the exact dependency graph in this situation.\n```\na.ml: B\nb.ml:\n```\n\nNotwithstanding bugs, codept should compute exact dependency graphs when no first class modules are opened or included. In the presence of opened or included first class modules, codept will still compute the exact dependency graph if the signature of the module is present in the pattern binding the module name, for instance with\n\n```OCaml\n(* a.ml *)\nmodule type S = sig module C:sig end end\n```\n\n```OCaml\n(* b.ml *)\nopen A\nlet f (module M:S) =\n  let open M in\n  let open C in\n  ()\n```\n\n`codept -modules a.ml b.ml`  produces\n```\na.ml:\nb.ml: A\n```\nas expected.\n\nHowever, if the inference of the first class module signature is more involved, codept will produce an inexact dependency graph:\n\n```OCaml\n(* a.ml *)\nmodule type S = sig module B: sig end end\nlet f (x : (module S)) = x\nlet g x =\n  let (module M) = f x in\n  let open M in\n  let open B in\n  ()\n\n```\ngives with `codept -modules a.ml` a fictitious `B` dependency\n```\na.ml: B\n```\nand emits a warning (and a notification)\n```\n[Warning]: a.ml:l6.11−12,\nfirst-class module M was opened while its signature was unknown.\n[Notification]: a.ml:l7.11−12,\na non-resolvable module, B, has been replaced by an approximation\n```\nTo avoid this situation, a possible fix is to add back a signature annotation:\n\n```OCaml\n(* a.ml *)\nmodule type S = sig module B: sig end end\nlet f (x : (module S)) = x\nlet g x =\n  let (module M: S) = f x in\n  let open M in\n  let open B in\n  ()\n\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Foctachron%2Fcodept","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Foctachron%2Fcodept","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Foctachron%2Fcodept/lists"}