如果包在 opam 中有特定版本,则安装依赖项

Install dependency if a package has a specific version in opam

如果 ocaml 包版本高于 4.14.0,我想指定我的 opam 包依赖于 camlp-streams。在我的 opam 文件中,我放置了以下行:

depends: [
  "ocaml" {>= "4.07.0"}
  "dune" {build & >= "2.8.0"}
  "camlp-streams" {>= "5.0" & "ocaml" >= "4.14.0" }
]

但是即使 ocaml 版本低于 4.14.0,当我 opam install --deps-only package.opam 它会安装 camlp-streams.

有没有办法告诉 opam 在存在特定版本的另一个包时不要安装该包?由于我不知道如何告诉 opam 向我展示它如何解决我的约束,因此调试我的 opam 文件有点困难。

我想我找到了可行的解决方案:

depends: [
  ( "ocaml" {>= "4.07.0" & < "4.14.0" } | ( "ocaml" { >= "4.14.0" } & "camlp-streams" ))
  "dune" {build & >= "2.8.0"}
]

这允许我指定仅当 ocaml 版本低于 4.14.0 时我依赖于 ocamlcamlp-streams 如果 ocaml版本大于4.14.0

"ocaml" >= "4.14.0" 表达式只是使用 Debian 版本排序将字符串 "ocaml" 与字符串 "4.14.0" 进行比较。由于 "ocaml" 大于 "4.14.0",它始终计算为真,因此始终安装依赖项。

depends 字段使用 filtered package formulas. A filtered package formula allows us to specify both filters and package version constraints 指定依赖项。该公式分两个阶段进行评估。在第一阶段,对公式中的所有过滤器进行评估,并将过滤器表达式评估为 true 的那些包从列表中删除。第一阶段后,过滤表达式从公式中移除,生成的仅包含版本约束的包公式被传递给约束求解器。

例如,

depends: [
  "linux-support-package" {>= "4.0.0" & os = "linux"}
]

将仅在 Linux 操作系统上安装 linux-support-package。在第一阶段,如果全局变量 os 等于 linux,过滤器将评估为真并产生最终包公式 {>= "4.0.0" & os = "linux"}.

请注意,过滤器中的变量未被引用,过滤器中只能使用全局变量和开关变量(有关此类列表,请参阅 opam var)。不幸的是,OCaml 版本没有全局或开关变量,因为它被认为只是一个包。此外,如果允许在 depends 字段的过滤器表达式中引用包,那么正确的语法将是 ocaml.version >= "4.14.0".

现在,当我们具备这些知识时,我们有哪些选择?您自己的答案中引用的第一个选项是使用互斥公式的析取。它会起作用,但有一些警告。首先是,如果您的析取不是排他性的,即有一组满足这两个子句的包,那么求解器将以 non-deterministic 的方式选择一个任意集合,这绝对不是您想要的!当公式增长时,很难保持唯一不变性。另一个警告是你的包的依赖集不再是静态的 属性 而是开关状态的函数。这将使维护和调试包成为一项更艰巨的任务。最后,在你的包公式中(而不是在版本约束中)有分离和连接会给约束求解器带来很大压力,最终会导致性能问题甚至超时。

传统的解决方案,但具有不同的语义,将使用 depopts 字段。它仍然不允许您对 OCaml 版本进行参数化,但您可以在配置脚本中使用过滤器来执行此操作,例如

build: [
  [
    "./configure"
    "--%{camlp-streams:installed}%-streams" {ocaml:version >= "4.14.0"}
  ]
  [make]
]


depopts: [
  "camlp-streams" {>= "5.0"}
]

现在,当您安装包(我们将其命名为 foo)时,camlp-streams 将永远不会被显式安装。但是,如果您明确指定 camlp-streams,例如,

opam install foo camlp-streams

或者如果 camlp-streams 已经安装,那么只有当 OCaml 的版本大于 4.14.0 时,构建期间你的 configure 脚本才会用 --enable-camlp-streams 调用。此外,camlp-streams 将在您的包之前构建,如果 camlp-streams 包在您的包之后安装,那么您的包将被重建。

这是指定您可以在 opam-repository 中找到的可选依赖项的默认方法。在 post-install 消息中宣传增强主包的可选包也是一个好习惯。

在极少数情况下,当且仅当存在其他包时才真正需要您的包,那么您可以依赖析取(来自您的答案)或通过虚拟包指定此类依赖项。事实上,一个包有两个替代方案,版本 1 不引入对 camlp-streams

的依赖
opam-version: "2.0"
name: "conf-foo-camlp-streams"
version: "1"

conflicts: [
  "ocaml" {>= "4.14.0"}
]

和版本 2,

opam-version: "2.0"
name: "conf-foo-camlp-streams"
version: "2"

depends: [
  "camlp-streams" {>= "5.0"}
]

conflicts: [
  "ocaml" {< "4.14.0"}
]

并指定 conf-foo-camlp-streams (在任何地方用你的包名替换 foo 标签)作为你的包的依赖而不限制它的版本。现在,求解器很容易 select 基于与 OCaml 版本的冲突的正确替代方案,并且只有在选择第二个替代方案时才安装 camlp-streams 。这接近析取解决方案,但更冗长。另一方面,它应该减轻求解器的压力。