Skip to content

Commit bd29b15

Browse files
authored
pkg: relax version constraints for ocamlformat dev tool (ocaml#11019)
Previously dune would install the exact version of ocamlformat specified in the .ocamlformat file in the project's root. This prevents alternative distributions of the ocamlformat package from being used, as these typically append a suffix to the version number. The motivation for this change is supporting a binary dev tools distribution where the ocamlformat package is named like "ocamlformat.0.26.2+binary", and we want that package to be installable when the .ocamlformat file of a project specifies "version=0.26.2". Signed-off-by: Stephen Sherratt <[email protected]>
1 parent 2fa3ab4 commit bd29b15

File tree

2 files changed

+100
-4
lines changed

2 files changed

+100
-4
lines changed

bin/lock_dev_tool.ml

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,31 @@ let is_enabled =
1313
| `Disabled -> false)
1414
;;
1515

16+
(* Returns a version constraint accepting (almost) all versions whose prefix is
17+
the given version. This allows alternative distributions of packages to be
18+
chosen, such as choosing "ocamlformat.0.26.2+binary" when .ocamlformat
19+
contains "version=0.26.2". *)
20+
let relaxed_version_constraint_of_version version =
21+
let open Dune_lang in
22+
let min_version = Package_version.to_string version in
23+
(* The goal here is to add a suffix to [min_version] to construct a version
24+
number higher than than any version number likely to appear with
25+
[min_version] as a prefix. "_" is the highest ascii symbol that can appear
26+
in version numbers, excluding "~" which has a special meaning. It's
27+
conceivable that one or two consecutive "_" characters may be used in a
28+
version, so this appends "___" to [min_version].
29+
30+
Read more at: https://opam.ocaml.org/doc/Manual.html#Version-ordering
31+
*)
32+
let max_version = min_version ^ "___MAX_VERSION" in
33+
Package_constraint.And
34+
[ Package_constraint.Uop
35+
(Relop.Gte, Package_constraint.Value.String_literal min_version)
36+
; Package_constraint.Uop
37+
(Relop.Lte, Package_constraint.Value.String_literal max_version)
38+
]
39+
;;
40+
1641
(* The solver satisfies dependencies for local packages, but dev tools
1742
are not local packages. As a workaround, create an empty local package
1843
which depends on the dev tool package. *)
@@ -24,10 +49,7 @@ let make_local_package_wrapping_dev_tool ~dev_tool ~dev_tool_version ~extra_depe
2449
let open Dune_lang in
2550
let open Package_dependency in
2651
let constraint_ =
27-
Option.map dev_tool_version ~f:(fun version ->
28-
Package_constraint.Uop
29-
( Relop.Eq
30-
, Package_constraint.Value.String_literal (Package_version.to_string version) ))
52+
Option.map dev_tool_version ~f:relaxed_version_constraint_of_version
3153
in
3254
{ name = dev_tool_pkg_name; constraint_ }
3355
in
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
Check that dune can choose a version of ocamlformat with a suffix (e.g.
2+
0.24+foo) to satisfy a .ocamlformat config that specifies a matching version
3+
without the suffix.
4+
$ . ./helpers.sh
5+
$ mkrepo
6+
$ make_project_with_dev_tool_lockdir
7+
8+
Fake ocamlformat package that appends a comment with the ocamlformat version to the end of the file:
9+
$ ocamlformat_package() {
10+
> cat <<EOF
11+
> install: [
12+
> [ "sh" "-c" "echo '#!/bin/sh' > %{bin}%/ocamlformat" ]
13+
> [ "sh" "-c" "echo 'cat \$2' >> %{bin}%/ocamlformat" ]
14+
> [ "sh" "-c" "echo 'echo \$2 | grep .*.ml >/dev/null && echo \"(* formatted with fake ocamlformat %{version}% *)\"' >> %{bin}%/ocamlformat" ]
15+
> [ "sh" "-c" "chmod a+x %{bin}%/ocamlformat" ]
16+
> ]
17+
> EOF
18+
> }
19+
20+
Make some fake ocamlformat packages:
21+
$ ocamlformat_package | mkpkg ocamlformat 0.24+foo
22+
$ ocamlformat_package | mkpkg ocamlformat 0.25+bar
23+
24+
Initial file:
25+
$ cat foo.ml
26+
let () = print_endline "Hello, world"
27+
28+
This should choose the 0.24+foo version:
29+
$ echo "version=0.24" > .ocamlformat
30+
$ DUNE_CONFIG__LOCK_DEV_TOOL=enabled dune fmt
31+
Solution for dev-tools.locks/ocamlformat:
32+
- ocamlformat.0.24+foo
33+
File "foo.ml", line 1, characters 0-0:
34+
Error: Files _build/default/foo.ml and _build/default/.formatted/foo.ml
35+
differ.
36+
Promoting _build/default/.formatted/foo.ml to foo.ml.
37+
[1]
38+
$ cat foo.ml
39+
let () = print_endline "Hello, world"
40+
(* formatted with fake ocamlformat 0.24+foo *)
41+
42+
This should choose the 0.24+bar version:
43+
$ echo "version=0.25" > .ocamlformat
44+
$ rm -rf dev-tools.locks
45+
$ DUNE_CONFIG__LOCK_DEV_TOOL=enabled dune fmt
46+
Solution for dev-tools.locks/ocamlformat:
47+
- ocamlformat.0.25+bar
48+
File "foo.ml", line 1, characters 0-0:
49+
Error: Files _build/default/foo.ml and _build/default/.formatted/foo.ml
50+
differ.
51+
Promoting _build/default/.formatted/foo.ml to foo.ml.
52+
[1]
53+
$ cat foo.ml
54+
let () = print_endline "Hello, world"
55+
(* formatted with fake ocamlformat 0.24+foo *)
56+
(* formatted with fake ocamlformat 0.25+bar *)
57+
58+
This should fail as there is no version matching 0.24.1:
59+
$ echo "version=0.24.1" > .ocamlformat
60+
$ rm -rf dev-tools.locks
61+
$ DUNE_CONFIG__LOCK_DEV_TOOL=enabled dune fmt
62+
Error: Unable to solve dependencies for the following lock directories:
63+
Lock directory dev-tools.locks/ocamlformat:
64+
Can't find all required versions.
65+
Selected: ocamlformat_dev_tool_wrapper.dev
66+
- ocamlformat -> (problem)
67+
ocamlformat_dev_tool_wrapper dev requires >= 0.24.1 & <=
68+
0.24.1___MAX_VERSION
69+
Rejected candidates:
70+
ocamlformat.0.25+bar: Incompatible with restriction: >= 0.24.1 & <=
71+
0.24.1___MAX_VERSION
72+
ocamlformat.0.24+foo: Incompatible with restriction: >= 0.24.1 & <=
73+
0.24.1___MAX_VERSION
74+
[1]

0 commit comments

Comments
 (0)