Codept, a whole-project dependency analyzer for OCaml Florian - - PowerPoint PPT Presentation

codept a whole project dependency analyzer for ocaml
SMART_READER_LITE
LIVE PREVIEW

Codept, a whole-project dependency analyzer for OCaml Florian - - PowerPoint PPT Presentation

Codept, a whole-project dependency analyzer for OCaml Florian octachron Angeletti INRIA ICFP 2019, OCaml workshop, 23 August 2019 Discovering dependencies $ ls a.ml b.ml c.ml d.ml e.ml f.ml atlas.ml Discovering dependencies $ ls


slide-1
SLIDE 1

Codept, a whole-project dependency analyzer for OCaml

Florian “octachron” Angeletti

INRIA

ICFP 2019, OCaml workshop, 23 August 2019

slide-2
SLIDE 2

Discovering dependencies

$ ls a.ml b.ml c.ml d.ml e.ml f.ml atlas.ml

slide-3
SLIDE 3

Discovering dependencies

$ ls a.ml b.ml c.ml d.ml e.ml f.ml atlas.ml ◮ Discovering project structure

slide-4
SLIDE 4

Discovering dependencies

$ ls a.ml b.ml c.ml d.ml e.ml f.ml atlas.ml ◮ Discovering project structure ◮ Building project

slide-5
SLIDE 5

Let’s call ocamldep

slide-6
SLIDE 6

Let’s call ocamldep

Atlas F E D C B A

slide-7
SLIDE 7

Let’s call ocamldep

The End

slide-8
SLIDE 8

Let’s call ocamldep

wait . . . a flower?

slide-9
SLIDE 9

A . . . flower ?

Atlas F E D C B A

slide-10
SLIDE 10

A . . . flower ?

Atlas F E D C B A

(* Atlas.ml *) module X = A module Y = B module Z = C module W = D module R = E module S = F

slide-11
SLIDE 11

$ ocamldep -map atlas.ml a.ml b.ml c.ml d.ml e.ml f.ml

slide-12
SLIDE 12

And submodule collisions?

B A

(* a.ml *)

  • pen B
  • pen A

(* b.ml *) module A = struct end

slide-13
SLIDE 13

Don’t forget first class modules

A

(* a.ml *) module type S = sig module A: sig end end let f () = let module M = struct module A = struct end end in (module M : S ) let () = let module M = (val f ()) in let open M in let open A in ()

slide-14
SLIDE 14

Abstract module types?

A

(* a.ml *) module type S = sig module type T module M:T end module F(X:S)= struct module M = X.M end module X = struct module type T = sig module A: sig end end module M = struct module A = struct end end end

  • pen F(X) open M open A
slide-15
SLIDE 15

Modules and compilation units

Compilation units

A concrete file or pair of files mapped to a module ◮ All compilation units are mapped to a module ◮ All modules do not come from compilation units

slide-16
SLIDE 16

Context matters

...

  • pen A
slide-17
SLIDE 17

Context matters

module A = struct ... end

  • pen A
slide-18
SLIDE 18

Context matters

(* start of the file *)

  • pen A
slide-19
SLIDE 19

Context matters

  • pen B (* Does B define a submodule A *)
  • pen A
slide-20
SLIDE 20

Dependency tracking in OCaml

Recognizing compilation units from submodules.

slide-21
SLIDE 21

Naive dependencies

◮ All actual direct dependencies are recorded ◮ So many false positives ◮ Aliases are not tracked

slide-22
SLIDE 22

OCamldep’s way: local analysis

Local analysis

module Sub = struct ... end include Sub ◮ Every modules that is not of the current compilation unit submodules is a compilation unit ◮ Nearly an over-approximation ◮ False positive: post-processing phase ◮ Alias: manual map tracking with -map option

slide-23
SLIDE 23

OCamldep’s way: local analysis

Local analysis

module Sub = struct ... end include Sub ◮ Every modules that is not of the current compilation unit submodules is a compilation unit ◮ Nearly an over-approximation ◮ False positive: post-processing phase ◮ Alias: manual map tracking with -map option

slide-24
SLIDE 24

Going further: whole-project analysis

How to get precise dependencies for a file A?

What do you need for precise dependencies

◮ A signature for the universe

slide-25
SLIDE 25

Going further: whole-project analysis

How to get precise dependencies for a file A?

What do you need for precise dependencies

◮ A signature for the universe

◮ How to deal with first-class modules?

slide-26
SLIDE 26

Going further: whole-project analysis

How to get precise dependencies for a file A?

What do you need for precise dependencies

◮ A signature for the universe

◮ How to deal with first-class modules?

◮ A signature for all compilation units

slide-27
SLIDE 27

Going further: whole-project analysis

How to get precise dependencies for a file A?

What do you need for precise dependencies

◮ A signature for the universe

◮ How to deal with first-class modules?

◮ A signature for all compilation units ◮ A signature for all dependencies

Codept core idea

◮ A dependency and signature analyzer ◮ ...than can stop on a missing signature and resume later

slide-28
SLIDE 28

Codept specification

◮ No warning, exact dependencies ◮ At worst, an over-approximation of dependencies ◮ All analysis results are serializable in machine readable formats

Secondary goal

◮ Full compatibility with ocamldep

slide-29
SLIDE 29

3 layers

◮ AST simplification ◮ Interruptible interpreter ◮ Dependency orchestration

slide-30
SLIDE 30

Simplified M2l Ast

◮ expressions ◮ patterns ◮ types ◮ classes ◮ modules ◮ module types

type expression = | Open of module_expr | Include of module_expr | SigInclude of module_type | Bind of module_expr bind | Bind_sig of module_type bind | Bind_rec of module_expr bind list | Minor of annotation | Extension_node of extension and ...

Full OCaml Parsetree: 960 LOC Simplified (M2l) AST : 80 LOC

slide-31
SLIDE 31

module M = struct module X = struct end end

  • pen M open X
  • pen B

module C = struct end [module M = [module X = [] (l2.2-l3.5)] (l1.0-l4.3)

  • pen [M](l5.0-6)
  • pen [X](l5.7-13)
  • pen [B](l6.0-6)

module C = [](l7.0-21) ]

slide-32
SLIDE 32

Interruptible interpreter

◮ How to represent partial evaluation result partial result, , partial AST

slide-33
SLIDE 33

Interruptible interpreter

◮ How to represent partial evaluation result partial result, , partial AST ◮ : a still unknown module name

slide-34
SLIDE 34

Interruptible interpreter

◮ How to represent partial evaluation result partial result, , partial AST ◮ : a still unknown module name

Zipper

◮ Add holes to the AST data type ◮ Holes are to be filled by the environment

slide-35
SLIDE 35

[module M = [module X = [] (l2.2-l3.5)] (l1.0-l4.3)

  • pen [M](l5.0-6)
  • pen [X](l5.7-13)
  • pen [B](l6.0-6)

module C = [](l7.0-21) ] Computation halted at: ... open B? [module C = [] (l7.0-21)]

slide-36
SLIDE 36

Zipper example

... and module_expr = | Ident of Paths.Simple.t | Apply of { f: module_expr ; x:module_expr } ... type 'hole me = | Ident: path_in_context me | Apply_left: M2l.module_expr

  • > M2l.module_expr me

| Apply_right: module_expr

  • > M2l.module_expr me

| ...

slide-37
SLIDE 37

Evaluation

◮ Try to fill all holes ◮ Fail if there is a hole that the environment doesn’t know how to fill ◮ Return the signature and dependencies otherwise

slide-38
SLIDE 38

Orchestration

◮ Different strategies to compute whole-project signature and dependencies ◮ What to do with cycles?

slide-39
SLIDE 39

Cycles

◮ Report them? ◮ Try to remove them and go on with the rest of the computation?

A H B G C D E F

slide-40
SLIDE 40

Codept in the real world

How well does codept fare against its specification?

slide-41
SLIDE 41

Alias tracking

A C B Atlas D E F

(* Atlas.ml *) module X = A module Y = B module Z = C module W = D module R = E module S = F

slide-42
SLIDE 42

And submodule collisions?

A B

(* a.ml *)

  • pen B
  • pen A

(* b.ml *) module A = struct end

slide-43
SLIDE 43

Don’t forget first class modules

A

(* a.ml *) ... let () = let module M = (val f ()) in let open M in let open A in () [Warning]: a.ml:l7.6-12, first-class module M was opened while its signature was unknown. Local solution: (* a.ml *) ... let module M: S = (val f ()) in ...

slide-44
SLIDE 44

Abstract module types?

Work-in-progress.

slide-45
SLIDE 45

Performances

Slower than ocamldep Not the right question:

slide-46
SLIDE 46

Library core, ocamldep compatible executable

◮ Codept executable, fully compatible with ocamldep

slide-47
SLIDE 47

Library core, ocamldep compatible executable

◮ Codept executable, fully compatible with ocamldep ◮ Core library, to be published on version 1.0

slide-48
SLIDE 48

Library core, ocamldep compatible executable

◮ Codept executable, fully compatible with ocamldep ◮ Core library, to be published on version 1.0 ◮ Too many options, a lighter executable planned

slide-49
SLIDE 49

Machine readable output

◮ JSON and sexp format available ◮ for signature and dependencies ◮ for the M2l AST

slide-50
SLIDE 50

{ "version" : [0, 10, 3], "dependencies" : [{ "file" : "a.ml", "deps" : [["C"], ["B"], ["Atlas"]] }, { "file" : "atlas.ml" }, { "file" : "b.ml", "deps" : [["C" { "file" : "c.ml", "deps" : [["Atlas"]] }, { "file" : "d.ml", "deps" : [["Atlas"]] }, { "file" : "e.ml", "deps" : [["Atlas"]] }, { "file" : "f.ml", "deps" : [["Atlas"]] }], "local" : [{ "module" : ["A"], "ml" : "a.ml" }, { "module" : ["Atlas"], "ml" : "atlas.ml" }, { "module" : ["B"], "ml" : "b.ml" }, { "module" : ["C"], "ml" { "module" : ["D"], "ml" : "d.ml" }, { "module" : ["E"], "ml" { "module" : ["F"], "ml" : "f.ml" }] } Support incremental compilation

slide-51
SLIDE 51

Perspectives

◮ Library publication ◮ Lightweight executable ◮ Multi-zipper ◮ Dune integration

Dune integration

◮ Not that straightforward: a full new layer of dependency computation ◮ But more opportunities for caching Past features from the future ◮ Full support for decoupling module names from filenames ◮ Full support for nested namespaces with -nested

slide-52
SLIDE 52

Thanks!

slide-53
SLIDE 53

Nearly an over-approximation

(* a.ml *) module Sub = struct module SubSub = struct end end

  • pen B
  • pen Sub
  • pen SubSub

(* b.ml *) module Sub = struct end