• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

ocaml / dune / 28911

03 Nov 2024 06:06PM UTC coverage: 6.913% (-0.004%) from 6.917%
28911

push

github

web-flow
pkg: Use `filtered_formula` to represent dependencies (#10918)

* pkg: Pass-through `filtered_formula` from opam files to solver

The `Dependency_set.t` representation can't deal with disjunctions but
in most cases that is not even necessary as the set gets turned into a
`filtered_formula` again. Thus it might be easier to keep the original
representation and implement the necessary dependency set functionality
on top of that.

Signed-off-by: Marek Kubica <marek@tarides.com>

* Remove unused `Dependency_set`

Signed-off-by: Marek Kubica <marek@tarides.com>

* Add a test showing that the disjunction in OPAM files is supported now

Signed-off-by: Marek Kubica <marek@tarides.com>

* Move `filtered_formula` into our own module

Signed-off-by: Marek Kubica <marek@tarides.com>

* Determine the hash from the Sexp

Signed-off-by: Marek Kubica <marek@tarides.com>

* Move reachability into the formula

Signed-off-by: Marek Kubica <marek@tarides.com>

* Add test for dependency formula changes

Signed-off-by: Marek Kubica <marek@tarides.com>

* Clean up the awkward API

Signed-off-by: Marek Kubica <marek@tarides.com>

* Promote expected hash changes

Signed-off-by: Marek Kubica <marek@tarides.com>

* Update the test wording and show the difference

Signed-off-by: Marek Kubica <marek@tarides.com>

* test(pkg): demonstrate unreachable packages being included

Signed-off-by: Rudi Grinberg <me@rgrinberg.com>
Signed-off-by: Marek Kubica <marek@tarides.com>

* Do not include post dependencies in reachable packages

Signed-off-by: Marek Kubica <marek@tarides.com>

* Replace sexp by dyn

Signed-off-by: Marek Kubica <marek@tarides.com>

* Promote expected hash changes

Signed-off-by: Marek Kubica <marek@tarides.com>

* `post` deps are excluded now

Signed-off-by: Marek Kubica <marek@tarides.com>

* Simplify

Signed-off-by: Marek Kubica <marek@tarides.com>

* Use `Resolve_opam_formula` to determine depe... (continued)

0 of 58 new or added lines in 6 files covered. (0.0%)

4 existing lines in 3 files now uncovered.

2933 of 42427 relevant lines covered (6.91%)

26850.21 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/src/dune_pkg_outdated/dune_pkg_outdated.ml
1
open Import
2

3
type candidate =
4
  { is_immediate_dep_of_local_package : bool
5
  ; name : Package_name.t
6
  ; outdated_version : Package_version.t
7
  ; newer_version : Package_version.t
8
  }
9

10
type result =
11
  | Better_candidate of candidate
12
  | Package_not_found of Package_name.t
13
  | Package_is_best_candidate
14

15
type t = result list
16

17
let total_number_of_packages l = List.length l
×
18

19
let outdated_packages l =
20
  List.filter_map l ~f:(function
×
21
    | Better_candidate entry -> Some entry
×
22
    | _ -> None)
×
23
;;
24

25
let number_of_outdated_packages l = outdated_packages l |> List.length
×
26

27
let number_of_outdated_packages_that_are_immediate_deps l =
28
  outdated_packages l
×
29
  |> List.filter ~f:(fun x -> x.is_immediate_dep_of_local_package)
×
30
  |> List.length
×
31
;;
32

33
let packages_that_were_not_found l =
34
  List.filter_map l ~f:(function
×
35
    | Package_not_found name -> Some name
×
36
    | _ -> None)
×
37
;;
38

39
let explain_results_to_user results ~transitive ~lock_dir_path =
40
  let number_of_outdated_immediate_deps =
×
41
    number_of_outdated_packages_that_are_immediate_deps results
42
  in
43
  let number_of_outdated_deps = number_of_outdated_packages results in
×
44
  let total_number_of_deps = total_number_of_packages results in
×
45
  (* Depending on the number of immediate outdated and transitive outdated dependencies
46
     we give different messages. Therefore we need to determine what we have. *)
47
  let transitive_status =
×
48
    if number_of_outdated_deps = 0
49
    then `No_transitive_deps_outdated
×
50
    else if number_of_outdated_deps = total_number_of_deps
×
51
    then `All_transitive_deps_outdated
×
52
    else `Some_transitive_deps_outdated
×
53
  in
54
  let transitive_helper ~all_of =
55
    if transitive || number_of_outdated_immediate_deps = number_of_outdated_deps
×
56
    then []
×
57
    else
58
      [ Pp.text
×
59
          ("Showing immediate dependencies, use --transitive to see "
60
           ^ if all_of then "them all." else "the rest.")
×
61
      ]
62
  in
63
  let packages_in_lockdir_are ~all_of count =
64
    (Pp.tag User_message.Style.Warning
×
65
     @@ Pp.textf
×
66
          "%d/%d packages in %s are outdated."
67
          count
68
          total_number_of_deps
69
          (Path.Source.to_string_maybe_quoted lock_dir_path))
×
70
    :: transitive_helper ~all_of
71
  in
72
  match transitive_status with
73
  (* If there are no outdated transitive deps then everything is up to date. *)
74
  | `No_transitive_deps_outdated ->
×
75
    [ Pp.tag User_message.Style.Success
×
76
      @@ Pp.textf "%s is up to date." (Path.Source.to_string_maybe_quoted lock_dir_path)
×
77
    ]
78
  | `All_transitive_deps_outdated ->
×
79
    packages_in_lockdir_are ~all_of:true number_of_outdated_deps
80
  | `Some_transitive_deps_outdated ->
×
81
    packages_in_lockdir_are ~all_of:false number_of_outdated_deps
82
;;
83

84
let better_candidate
85
  ~repos
86
  ~(local_packages : Dune_pkg.Local_package.t Package_name.Map.t)
87
  (pkg : Lock_dir.Pkg.t)
88
  =
89
  let open Fiber.O in
×
90
  let pkg_name = pkg.info.name |> Package_name.to_string |> OpamPackage.Name.of_string in
×
91
  let is_immediate_dep_of_local_package =
×
92
    Package_name.Map.exists local_packages ~f:(fun local_package ->
93
      Dune_pkg.Local_package.(
×
94
        for_solver local_package
NEW
95
        |> (fun x -> x.dependencies)
×
NEW
96
        |> Dune_pkg.Dependency_formula.to_filtered_formula)
×
97
      |> OpamFilter.filter_deps
98
           ~build:true
99
           ~post:false
100
           ~dev:false
101
           ~default:false
102
           ~test:false
103
           ~doc:false
104
      |> OpamFormula.atoms
×
105
      |> List.exists ~f:(fun (name', _) -> OpamPackage.Name.equal pkg_name name'))
×
106
  in
107
  let+ all_versions =
×
108
    Opam_repo.load_all_versions repos pkg_name
×
109
    >>| OpamPackage.Version.Map.values
×
110
    >>| List.map ~f:Resolved_package.opam_file
×
111
  in
112
  match
×
113
    List.max all_versions ~f:(fun x y ->
114
      Ordering.of_int
×
115
        (OpamPackage.Version.compare (OpamFile.OPAM.version x) (OpamFile.OPAM.version y)))
×
116
  with
117
  | None -> Package_not_found pkg.info.name
×
118
  | Some newest_opam_file ->
×
119
    let version = OpamFile.OPAM.version newest_opam_file in
120
    (match
×
121
       Package_version.to_opam_package_version pkg.info.version
122
       |> OpamPackage.Version.compare version
×
123
       |> Ordering.of_int
×
124
     with
125
     | Lt | Eq -> Package_is_best_candidate
×
126
     | Gt ->
×
127
       Better_candidate
128
         { is_immediate_dep_of_local_package
129
         ; name = pkg.info.name
130
         ; newer_version = version |> Package_version.of_opam_package_version
×
131
         ; outdated_version = pkg.info.version
132
         })
133
;;
134

135
let pp results ~transitive ~lock_dir_path =
136
  let outdated_packages =
×
137
    match
138
      List.filter_map
139
        (outdated_packages results)
×
140
        ~f:
141
          (fun
142
            { is_immediate_dep_of_local_package; name; outdated_version; newer_version }
143
          ->
144
          (* If --transitive is passed, then we always print the available package. If
145
             not, then we only print it if it is an immediate dependency of a local
146
             package. *)
147
          if transitive || is_immediate_dep_of_local_package
×
148
          then
149
            (* CR-someday alizter: Create table printing helpers in Console and use
150
               those to align output. *)
151
            Some
×
152
              (Pp.concat
×
153
                 [ Pp.verbatim (Dune_lang.Package_name.to_string name)
×
154
                 ; Pp.space
155
                 ; Pp.tag
×
156
                     (User_message.Style.Ansi_styles [ `Fg_bright_red ])
157
                     (Pp.verbatim (Package_version.to_string outdated_version))
×
158
                 ; Pp.text " < "
×
159
                 ; Pp.tag
×
160
                     (User_message.Style.Ansi_styles [ `Fg_bright_green ])
161
                     (Pp.verbatim (Package_version.to_string newer_version))
×
162
                 ])
163
          else None)
×
164
    with
165
    | [] -> []
×
166
    | outdated_packages -> [ Pp.enumerate ~f:Fun.id outdated_packages ]
×
167
  in
168
  explain_results_to_user ~transitive ~lock_dir_path results @ outdated_packages
×
169
  |> Pp.concat_map ~sep:Pp.space ~f:Pp.box
170
  |> Pp.vbox
×
171
;;
172

173
let find ~repos ~local_packages packages =
174
  Package_name.Map.to_list_map packages ~f:(fun _name pkg ->
×
175
    better_candidate ~repos ~local_packages pkg)
×
176
  |> Fiber.all_concurrently
×
177
;;
178

179
module For_tests = struct
180
  type nonrec result = result
181

182
  let package_is_best_candidate = Package_is_best_candidate
183

184
  let better_candidate
185
    ~is_immediate_dep_of_local_package
186
    ~name
187
    ~newer_version
188
    ~outdated_version
189
    =
190
    Better_candidate
×
191
      { is_immediate_dep_of_local_package
192
      ; name = Package_name.of_string name
×
193
      ; newer_version
194
      ; outdated_version
195
      }
196
  ;;
197

198
  let explain_results = explain_results_to_user
199
  let pp = pp
200
end
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc