Skip to content

Instantly share code, notes, and snippets.

@shubhamkumar13
Created May 29, 2025 12:44
Show Gist options
  • Save shubhamkumar13/38a051594e0e4a45963d8305ffa7c340 to your computer and use it in GitHub Desktop.
Save shubhamkumar13/38a051594e0e4a45963d8305ffa7c340 to your computer and use it in GitHub Desktop.
Tutorial for simulating a revdep failure

a. Create 2 OPAM packages

  1. These stub packages which will be used to simulate what a revdep failure looks like.
    The respective directory structure.

     stub-b/
     ├── opam
     ├── dune-project
     └── src/
         ├── dune
         └── b.ml
    
     stub-a/
     ├── opam
     ├── dune-project
     └── src/
         ├── dune
         └── a.ml
  2. Then use a template where A <- B (A needs B). So we need to make 2 opam files.

    2.1 Contents of a file : stub-b/opam

      opam-version: "2.0"
      name: "stub-b"
      version: "0.1.0"
      synopsis: "Example dependency package"
      description: "Simple demonstration package for OPAM dependencies"
      maintainer: "[email protected]"
      authors: ["You"]
      license: "MIT"
      build: [
        ["dune" "build" "-p" name]
      ]
      depends: [
        "dune" {>= "2.0"}
        "ocaml" {>= "4.08.0"}
      ]
    

    2.2 Contents of file : stub-a/opam

      opam-version: "2.0"
      name: "stub-a"
      version: "0.1.0"
      synopsis: "Package depending on stub-b"
      description: "Demonstrates dependency on another OPAM package"
      maintainer: "[email protected]"
      authors: ["You"]
      license: "MIT"
      build: [
        ["dune" "build" "-p" name]
      ]
      depends: [
        "dune" {>= "2.0"}
        "ocaml" {>= "4.08.0"}
        "stub-b" # stub-a <- stub-b
      ]
    
  3. Then define dune-project at the root of the OPAM packages.

    3.1 Contents of stub-b/dune-project

      (lang dune 3.0)
      (name stub-b)
      (version 0.1.0)
      (implicit_transitive_deps false)  ; Crucial for dependency resolution
    
      (package
       (name stub-b)
       (version 0.1.0))
    

    3.2 Contents of stub-a/dune-project

      (lang dune 3.0)
      (name stub-a)
      (version 0.1.0)
      (implicit_transitive_deps false)  ; Crucial for dependency resolution
    
      (package
       (name stub-a)
       (version 0.1.0))
    
  4. And also define local dune file stub-a/src/dune and stub-b/src/dune

    stub-a/src/dune

      (library
      (public_name stub-a)
      (name a)
      (libraries stub-b))  ; Declares dependency
    

    and stub-b/src/dune

       (library
       (public_name stub-b)
       (name b))
    
  5. We also don't need to define a.ml and b.ml. I am defining it to make this a readable example. Calling B from A.

      (* stub-b/src/b.ml *)
      let greet () = "Hello from Package B!"

    and

      (* stub-a/src/a.ml *)
      let run () = "Package A says: " ^ B.greet ()

b. Create a local OPAM repository

  1. Like the opam-repository maintained by folks on opam we are going to make a custom local index which contains stub-a and stub-b

       my-opam-repo/
       ├── packages/
       │   ├── stub-b.0.1.0/
       │   │   └── opam
       │   └── stub-a.0.1.0/
       │       └── opam
       └── repo
    
  2. repo file is used to let the opam-cli recognize this as an opam-repository and when asked to install packages stub-b and stub-a go through the directory opam-version key-value pair is enough for our goal.

      echo 'opam-version: "2.0"' > my-opam-repo/repo
    
  3. Unlike other package registries OPAM repos doesn't store/get a tarball. THere is a specific section for URLs identifying their src and their checksum to locate the package source. While adding packages to opam repository ocaml folks often follow the convention of <package-name>.<version>. For stub-a that would look like stub-a.0.1.0 and similarly for stub-b - stub-b.0.1.0. These directories only contain a single opam file. Quite similar to the opam file where the package is defined.

    # stub-a
    opam-version: "2.0"
     name: "stub-a"
     version: "0.1.0"
     synopsis: "Package depending on stub-b"
     description: "Demonstrates dependency on another OPAM package"
     maintainer: "[email protected]"
     authors: ["You"]
     license: "MIT"
     build: [
       ["dune" "build" "-p" name]
     ]
     depends: [
       "dune" {>= "2.0"}
       "ocaml" {>= "4.08.0"}
       "stub-b"
     ]
     
     # stub-b
     opam-version: "2.0"
     name: "stub-b"
     version: "0.1.0"
     synopsis: "Dependency package"
     build: [
       ["dune" "build" "-p" name]
     ]
     depends: [
       "dune" {>= "2.0"}
       "ocaml" {>= "4.08.0"}
     ]
    
  4. Pinning packages (give an example of what pinning does briefly and also pin it)

  5. Update the stub-b/src/b.ml file to show breaking changes.

  6. Bump up the package version from 0.1.0 to 0.2.0

  7. Re-pin and display the error which would look like initially

 The following actions will be performed:
 === install 1 package
   ∗ stub-a 0.1.0 (pinned)

 Proceed with ∗ 1 installation? [y/n] y

 <><> Processing actions <><><><><><><><><><><><><><><><><><><><><><><><><><><><>
 [ERROR] The compilation of stub-a.0.1.0 failed at "dune build -p stub-a".

 #=== ERROR while compiling stub-a.0.1.0 =======================================#
 # context     2.3.0 | linux/x86_64 | ocaml-base-compiler.5.1.1 | pinned(file:///home/sk/my-opam-pkgs/stub-a)
 # path        ~/.opam/revdep-test/.opam-switch/build/stub-a.0.1.0
 # command     ~/.opam/opam-init/hooks/sandbox.sh build dune build -p stub-a
 # exit-code   1
 # env-file    ~/.opam/log/stub-a-198184-81ec23.env
 # output-file ~/.opam/log/stub-a-198184-81ec23.out
 ### output ###
 # (cd _build/default && /home/sk/.opam/revdep-test/bin/ocamlc.opt -w -40 -g -bin-annot -I src/.a.objs/byte -I /home/sk/.opam/revdep-test/lib/stub-b -no-alias-deps -o src/.a.objs/byte/a.cmo -c -impl src/a.ml)
 # File "src/a.ml", line 2, characters 31-33:
 # 2 |   "Package A says: " ^ B.greet ()
 #                                    ^^
 # Error: This expression has type unit but an expression was expected of type
 #          string

And after fixing the API to accomodate stub-b show the error for conflict

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment