Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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

Loading