Skip to content

Commit

Permalink
Support not in package dependencies constraints (#11404)
Browse files Browse the repository at this point in the history
* Support not in package dependencies constraints

Signed-off-by: ArthurW <[email protected]>

* Fix after review

Signed-off-by: ArthurW <[email protected]>

---------

Signed-off-by: ArthurW <[email protected]>
  • Loading branch information
art-w authored Feb 18, 2025
1 parent d826e9b commit 17646fd
Show file tree
Hide file tree
Showing 9 changed files with 198 additions and 37 deletions.
1 change: 1 addition & 0 deletions doc/changes/11404.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
- Support `not` in package dependencies constraints (#11404, @art-w, reported by @hannesm)
10 changes: 10 additions & 0 deletions src/dune_lang/package_constraint.ml
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ module T = struct
| Bop of Relop.t * Value.t * Value.t
| And of t list
| Or of t list
| Not of t

let rec to_dyn =
let open Dyn in
Expand All @@ -48,6 +49,7 @@ module T = struct
| Bop (b, x, y) -> variant "Bop" [ Relop.to_dyn b; Value.to_dyn x; Value.to_dyn y ]
| And t -> variant "And" (List.map ~f:to_dyn t)
| Or t -> variant "Or" (List.map ~f:to_dyn t)
| Not t -> variant "Not" [ to_dyn t ]
;;

let rec compare a b =
Expand All @@ -71,6 +73,9 @@ module T = struct
| And _, _ -> Lt
| _, And _ -> Gt
| Or a, Or b -> List.compare a b ~compare
| Or _, _ -> Lt
| _, Or _ -> Gt
| Not a, Not b -> compare a b
;;
end

Expand All @@ -85,6 +90,7 @@ let rec encode c =
| Bop (op, x, y) -> triple Relop.encode Value.encode Value.encode (op, x, y)
| And conjuncts -> list sexp (string "and" :: List.map ~f:encode conjuncts)
| Or disjuncts -> list sexp (string "or" :: List.map ~f:encode disjuncts)
| Not x -> list sexp [ string "not"; encode x ]
;;

let logical_op t =
Expand Down Expand Up @@ -138,6 +144,10 @@ let decode =
; ( "or"
, let+ x = logical_op t in
Or x )
; ( "not"
, let+ x = t
and+ () = Dune_sexp.Syntax.since Stanza.syntax (3, 18) ~what:"Not operator" in
Not x )
]
in
peek_exn
Expand Down
1 change: 1 addition & 0 deletions src/dune_lang/package_constraint.mli
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ type t =
(** A binary operator applied to LHS and RHS values *)
| And of t list (** The conjunction of a list of boolean expressions *)
| Or of t list (** The disjunction of a list of boolean expressions *)
| Not of t (** The negation of a boolean expression *)

val encode : t Dune_sexp.Encoder.t
val decode : t Dune_sexp.Decoder.t
Expand Down
24 changes: 23 additions & 1 deletion src/dune_pkg/package_dependency.ml
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,12 @@ module Constraint = struct
(Value.to_opam_filter lhs, Op.to_relop_pelem op, Value.to_opam_filter rhs)))
| And conjunction -> OpamFormula.ands (List.map conjunction ~f:to_opam_condition)
| Or disjunction -> OpamFormula.ors (List.map disjunction ~f:to_opam_condition)
| Not constraint_ ->
OpamFormula.neg
(function
| OpamTypes.Constraint (op, v) -> Constraint (OpamFormula.neg_relop op, v)
| Filter f -> Filter (FNot f))
(to_opam_condition constraint_)
;;

let rec of_opam_filter (filter : OpamTypes.filter) =
Expand All @@ -104,6 +110,9 @@ module Constraint = struct
let+ lhs = of_opam_filter lhs
and+ rhs = of_opam_filter rhs in
Or [ lhs; rhs ]
| FNot constraint_ ->
let+ constraint_ = of_opam_filter constraint_ in
Not constraint_
| _ -> Error (Convert_from_opam_error.Can't_convert_opam_filter_to_condition filter)
;;

Expand Down Expand Up @@ -138,6 +147,7 @@ type context =
| Root
| Ctx_and
| Ctx_or
| Ctx_not

(* The printer in opam-file-format does not insert parentheses on its own,
but it is possible to use the [Group] constructor with a singleton to
Expand Down Expand Up @@ -165,15 +175,27 @@ let opam_constraint t : OpamParserTypes.FullPos.value =
( nopos @@ Constraint.Op.to_opam op
, Constraint.Value.to_opam x
, Constraint.Value.to_opam y ))
| And cs -> logical_op `And cs ~inner_ctx:Ctx_and ~group_needed:false
| And cs ->
let group_needed =
match context with
| Root -> false
| Ctx_and -> false
| Ctx_or -> false
| Ctx_not -> true
in
logical_op `And cs ~inner_ctx:Ctx_and ~group_needed
| Or cs ->
let group_needed =
match context with
| Root -> false
| Ctx_and -> true
| Ctx_or -> false
| Ctx_not -> true
in
logical_op `Or cs ~inner_ctx:Ctx_or ~group_needed
| Not c ->
let _c = opam_constraint Ctx_not c in
nopos (Pfxop (nopos `Not, _c))
and logical_op op cs ~inner_ctx ~group_needed =
List.map cs ~f:(opam_constraint inner_ctx) |> op_list op |> group_if group_needed
in
Expand Down
1 change: 1 addition & 0 deletions src/dune_rules/opam_create.ml
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,7 @@ let rec already_requires_odoc : Package_constraint.t -> bool = function
| Bvar var -> Dune_lang.Package_variable_name.(one_of var [ with_doc; build; post ])
| And l -> List.for_all ~f:already_requires_odoc l
| Or l -> List.exists ~f:already_requires_odoc l
| Not t -> not (already_requires_odoc t)
;;

let insert_odoc_dep depends =
Expand Down
36 changes: 0 additions & 36 deletions test/blackbox-tests/test-cases/dune-project-meta/binops.t

This file was deleted.

107 changes: 107 additions & 0 deletions test/blackbox-tests/test-cases/dune-project-meta/operators.t
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
Using binary operators for dependencies
---------------------------------------

Not supported before 2.1:

$ cat > dune-project <<EOF
> (lang dune 2.0)
> (name foo)
> (generate_opam_files true)
> (package
> (name foo)
> (depends (conf-libX11 (<> :os win32))))
> EOF

$ dune build @install
File "dune-project", line 6, characters 23-37:
6 | (depends (conf-libX11 (<> :os win32))))
^^^^^^^^^^^^^^
Error: Passing two arguments to <> is only available since version 2.1 of the
dune language. Please update your dune-project file to have (lang dune 2.1).
[1]

Supported since 2.1:

$ cat > dune-project <<EOF
> (lang dune 2.1)
> (name foo)
> (generate_opam_files true)
> (package
> (name foo)
> (depends (conf-libX11 (<> :os win32))))
> EOF

$ dune build @install
$ grep conf-libX11 foo.opam
"conf-libX11" {os != "win32"}


Using negation operator for dependencies
----------------------------------------

Not supported before 3.18:

$ cat > dune-project <<EOF
> (lang dune 3.17)
> (generate_opam_files)
> (package
> (name foo)
> (allow_empty)
> (depends
> (ocp-indent
> (not :with-test))))
> EOF
$ dune build
File "dune-project", line 8, characters 4-20:
8 | (not :with-test))))
^^^^^^^^^^^^^^^^
Error: Not operator is only available since version 3.18 of the dune
language. Please update your dune-project file to have (lang dune 3.18).
[1]

Supported since 3.18:

$ cat > dune-project <<EOF
> (lang dune 3.18)
> (generate_opam_files)
> (package
> (name foo)
> (allow_empty)
> (depends
> (ocp-indent
> (not
> (or
> (and
> (not :with-test)
> (>= 1.0))
> (not
> (and
> (not (= :os win32))
> (not (>= 1.5)))))))
> (not (>= 2.0))))
> EOF
$ dune build
$ cat foo.opam
# This file is generated by dune, edit dune-project instead
opam-version: "2.0"
depends: [
"dune" {>= "3.18"}
"ocp-indent" {!(!with-test & >= "1.0" | !(!os = "win32" & !>= "1.5"))}
"not" {>= "2.0"}
"odoc" {with-doc}
]
build: [
["dune" "subst"] {dev}
[
"dune"
"build"
"-p"
name
"-j"
jobs
"@install"
"@runtest" {with-test}
"@doc" {with-doc}
]
]
x-maintenance-intent: ["(latest)"]
43 changes: 43 additions & 0 deletions test/blackbox-tests/test-cases/pkg/additional-constraints.t
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,46 @@ Notice that the constraints field doesn't introduce additional packages. The
Solution for dune.lock:
- bar.1.0.0
- foo.1.0.0

Constraint negation is supported since 3.18:

$ cat >dune-workspace <<EOF
> (lang dune 3.18)
> (lock_dir
> (constraints doesnotexist (foo (not (= 1.0.0))) (bar (not (= 1.0.0))))
> (repositories mock))
> (repository
> (name mock)
> (url "file://$(pwd)/mock-opam-repository"))
> EOF

There are no valid version of foo at the moment:

$ solve_project <<EOF
> (lang dune 3.18)
> (package
> (name x)
> (depends foo bar))
> EOF
Error: Unable to solve dependencies for the following lock directories:
Lock directory dune.lock:
Couldn't solve the package dependency formula.
Selected candidates: bar.1.9.1 x.dev
- foo -> (problem)
No usable implementations:
foo.1.0.0: Package does not satisfy constraints of local package x
[1]

If we add one:

$ mkpkg foo 0.9.0

$ solve_project <<EOF
> (lang dune 3.18)
> (package
> (name x)
> (depends foo bar))
> EOF
Solution for dune.lock:
- bar.1.9.1
- foo.0.9.0
12 changes: 12 additions & 0 deletions test/blackbox-tests/test-cases/pkg/opam-solver-or.t
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,17 @@ which is completely omitted from the solution).
- a2.0.0.2
- b.0.0.2

Same solution if a1 only known version is excluded:

$ mkpkg b 0.0.2 <<EOF
> depends: [ "a1" {!= "0.0.1" } | "a2" {= "0.0.2" } ]
> EOF

$ solve b
Solution for dune.lock:
- a2.0.0.2
- b.0.0.2

Update a2.0.0.2 marking it as avoid-version which should tell the
solver to try to find a solution which doesn't include it.

Expand All @@ -65,3 +76,4 @@ $ mkpkg a2 0.0.2 <<EOF
Solution for dune.lock:
- a2.0.0.2
- b.0.0.2

0 comments on commit 17646fd

Please sign in to comment.