From 1fb0837ff7f97ccec925b68e3ce3568bace21695 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Nicol=C3=A1s=20Ojeda=20B=C3=A4r?= <n.oje.bar@gmail.com>
Date: Fri, 20 Oct 2023 09:06:20 +0200
Subject: [PATCH 1/5] Add 'dune cache clear' command
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Signed-off-by: Nicolás Ojeda Bär <n.oje.bar@gmail.com>
---
 bin/cache.ml                                  | 15 ++++++++++---
 src/dune_cache_storage/dune_cache_storage.ml  | 21 +++++++++++++++++++
 src/dune_cache_storage/dune_cache_storage.mli |  2 ++
 3 files changed, 35 insertions(+), 3 deletions(-)

diff --git a/bin/cache.ml b/bin/cache.ml
index 3662663b323..4e3a4e77036 100644
--- a/bin/cache.ml
+++ b/bin/cache.ml
@@ -80,18 +80,27 @@ let size =
     Cmd.info "size" ~doc ~man
   in
   Cmd.v info
-  @@ let+ machine_readble =
+  @@ let+ machine_readable =
        Arg.(
          value
          & flag
          & info [ "machine-readable" ] ~doc:"Outputs size as a plain number of bytes.")
      in
      let size = Dune_cache.Trimmer.overhead_size () in
-     if machine_readble
+     if machine_readable
      then User_message.print (User_message.make [ Pp.textf "%Ld" size ])
      else User_message.print (User_message.make [ Pp.textf "%s" (Bytes_unit.pp size) ])
 ;;
 
+let clear =
+  let info =
+    let doc = "Clear the Dune cache." in
+    let man = [ `P "Remove any traces of the Dune cache." ] in
+    Cmd.info "clear" ~doc ~man
+  in
+  Cmd.v info @@ Term.(const Dune_cache_storage.clear $ const ())
+;;
+
 let command =
   let info =
     let doc = "Manage Dune's shared cache of build artifacts." in
@@ -104,5 +113,5 @@ let command =
     in
     Cmd.info "cache" ~doc ~man
   in
-  Cmd.group info [ trim; size ]
+  Cmd.group info [ trim; size; clear ]
 ;;
diff --git a/src/dune_cache_storage/dune_cache_storage.ml b/src/dune_cache_storage/dune_cache_storage.ml
index f812adfc364..bea1de2b1e8 100644
--- a/src/dune_cache_storage/dune_cache_storage.ml
+++ b/src/dune_cache_storage/dune_cache_storage.ml
@@ -338,3 +338,24 @@ let with_temp_file ?(prefix = "dune") ~suffix f =
 let with_temp_dir ?(prefix = "dune") ~suffix f =
   Fiber_util.Temp.with_temp_dir ~parent_dir:Layout.temp_dir ~prefix ~suffix ~f
 ;;
+
+let clear () =
+  let rm_rf path = Path.rm_rf ~allow_external:true path in
+  let rmdir path =
+    try Path.rmdir path with
+    | Unix.Unix_error ((ENOENT | ENOTEMPTY), _, _) -> ()
+  in
+  let rm_rf_all versions dir =
+    List.iter versions ~f:(fun version ->
+      let dir = dir version in
+      rm_rf dir;
+      Option.iter ~f:rmdir (Path.parent dir))
+  in
+  rm_rf_all Version.Metadata.all Layout.Versioned.metadata_storage_dir;
+  rm_rf_all Version.File.all Layout.Versioned.file_storage_dir;
+  rm_rf_all Version.Value.all Layout.Versioned.value_storage_dir;
+  rm_rf Layout.temp_dir;
+  (* Do not catch errors when deleting the root directory so that they are
+     reported to the user. *)
+  Path.rmdir Layout.root_dir
+;;
diff --git a/src/dune_cache_storage/dune_cache_storage.mli b/src/dune_cache_storage/dune_cache_storage.mli
index c1c69748f46..85503886345 100644
--- a/src/dune_cache_storage/dune_cache_storage.mli
+++ b/src/dune_cache_storage/dune_cache_storage.mli
@@ -138,3 +138,5 @@ module Raw_value : sig
     -> content_digest:Digest.t
     -> Util.Write_result.t
 end
+
+val clear : unit -> unit

From dd1331ab51b36ca9c60a3377fffa932421e1abcf Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Nicol=C3=A1s=20Ojeda=20B=C3=A4r?= <n.oje.bar@gmail.com>
Date: Fri, 5 Jan 2024 10:58:07 +0100
Subject: [PATCH 2/5] Add test
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Signed-off-by: Nicolás Ojeda Bär <n.oje.bar@gmail.com>
---
 .../test-cases/dune-cache/clear.t             | 50 +++++++++++++++++++
 1 file changed, 50 insertions(+)
 create mode 100644 test/blackbox-tests/test-cases/dune-cache/clear.t

diff --git a/test/blackbox-tests/test-cases/dune-cache/clear.t b/test/blackbox-tests/test-cases/dune-cache/clear.t
new file mode 100644
index 00000000000..8a5616d7cb6
--- /dev/null
+++ b/test/blackbox-tests/test-cases/dune-cache/clear.t
@@ -0,0 +1,50 @@
+Test for the "dune cache clear" command.
+
+  $ export DUNE_CACHE=enabled
+  $ export DUNE_CACHE_ROOT=$PWD/dune-cache
+
+  $ cat >dune-project <<EOF
+  > (lang dune 3.10)
+  > EOF
+
+  $ cat >dune <<EOF
+  > (rule (with-stdout-to foo (progn)))
+  > EOF
+
+  $ dune build
+
+  $ find $DUNE_CACHE_ROOT -type d | sort -u
+  $TESTCASE_ROOT/dune-cache
+  $TESTCASE_ROOT/dune-cache/files
+  $TESTCASE_ROOT/dune-cache/files/v4
+  $TESTCASE_ROOT/dune-cache/files/v4/11
+  $TESTCASE_ROOT/dune-cache/files/v4/4d
+  $TESTCASE_ROOT/dune-cache/files/v4/b8
+  $TESTCASE_ROOT/dune-cache/meta
+  $TESTCASE_ROOT/dune-cache/meta/v5
+  $TESTCASE_ROOT/dune-cache/meta/v5/2f
+  $TESTCASE_ROOT/dune-cache/meta/v5/39
+  $TESTCASE_ROOT/dune-cache/meta/v5/4d
+  $TESTCASE_ROOT/dune-cache/temp
+  $TESTCASE_ROOT/dune-cache/values
+  $TESTCASE_ROOT/dune-cache/values/v3
+
+  $ dune cache clear
+
+  $ ! test -d $DUNE_CACHE_ROOT
+
+Next let us add some extra directories/files and check that they are not deleted
+by mistake.
+
+  $ dune build
+
+  $ mkdir -p $DUNE_CACHE_ROOT/extra; touch $DUNE_CACHE_ROOT/extra1 $DUNE_CACHE_ROOT/extra/extra2
+
+  $ dune cache clear
+  Error:
+  rmdir($TESTCASE_ROOT/dune-cache): Directory not empty
+  [1]
+
+  $ find $DUNE_CACHE_ROOT -type f | sort -u
+  $TESTCASE_ROOT/dune-cache/extra/extra2
+  $TESTCASE_ROOT/dune-cache/extra1

From ea911e8cef0838672edf9c283ab040c5b4ac85ce Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Nicol=C3=A1s=20Ojeda=20B=C3=A4r?= <n.oje.bar@gmail.com>
Date: Fri, 5 Jan 2024 11:09:47 +0100
Subject: [PATCH 3/5] Changes
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Signed-off-by: Nicolás Ojeda Bär <n.oje.bar@gmail.com>
---
 doc/changes/8975.md | 2 ++
 1 file changed, 2 insertions(+)
 create mode 100644 doc/changes/8975.md

diff --git a/doc/changes/8975.md b/doc/changes/8975.md
new file mode 100644
index 00000000000..2232165ae5d
--- /dev/null
+++ b/doc/changes/8975.md
@@ -0,0 +1,2 @@
+- Add command `dune cache clear` to completely delete all traces of the Dune
+  cache. (#8975, @nojb)

From a0a0f2739ded13f886ef2025b1133dc57b8cc0bb Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Nicol=C3=A1s=20Ojeda=20B=C3=A4r?= <n.oje.bar@gmail.com>
Date: Fri, 5 Jan 2024 11:24:13 +0100
Subject: [PATCH 4/5] Make test reproducible
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Signed-off-by: Nicolás Ojeda Bär <n.oje.bar@gmail.com>
---
 .../test-cases/dune-cache/clear.t             | 20 +++++--------------
 1 file changed, 5 insertions(+), 15 deletions(-)

diff --git a/test/blackbox-tests/test-cases/dune-cache/clear.t b/test/blackbox-tests/test-cases/dune-cache/clear.t
index 8a5616d7cb6..e4cee9b0084 100644
--- a/test/blackbox-tests/test-cases/dune-cache/clear.t
+++ b/test/blackbox-tests/test-cases/dune-cache/clear.t
@@ -13,21 +13,11 @@ Test for the "dune cache clear" command.
 
   $ dune build
 
-  $ find $DUNE_CACHE_ROOT -type d | sort -u
-  $TESTCASE_ROOT/dune-cache
-  $TESTCASE_ROOT/dune-cache/files
-  $TESTCASE_ROOT/dune-cache/files/v4
-  $TESTCASE_ROOT/dune-cache/files/v4/11
-  $TESTCASE_ROOT/dune-cache/files/v4/4d
-  $TESTCASE_ROOT/dune-cache/files/v4/b8
-  $TESTCASE_ROOT/dune-cache/meta
-  $TESTCASE_ROOT/dune-cache/meta/v5
-  $TESTCASE_ROOT/dune-cache/meta/v5/2f
-  $TESTCASE_ROOT/dune-cache/meta/v5/39
-  $TESTCASE_ROOT/dune-cache/meta/v5/4d
-  $TESTCASE_ROOT/dune-cache/temp
-  $TESTCASE_ROOT/dune-cache/values
-  $TESTCASE_ROOT/dune-cache/values/v3
+  $ ls $DUNE_CACHE_ROOT | sort -u
+  files
+  meta
+  temp
+  values
 
   $ dune cache clear
 

From 6202077345694eb719818350dbdd1224fc28a3ec Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Nicol=C3=A1s=20Ojeda=20B=C3=A4r?= <n.oje.bar@gmail.com>
Date: Fri, 5 Jan 2024 11:24:34 +0100
Subject: [PATCH 5/5] Accept test results
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Signed-off-by: Nicolás Ojeda Bär <n.oje.bar@gmail.com>
---
 test/blackbox-tests/test-cases/dune-cache/cache-man.t | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/test/blackbox-tests/test-cases/dune-cache/cache-man.t b/test/blackbox-tests/test-cases/dune-cache/cache-man.t
index 9aaee801818..52d92ec3fbe 100644
--- a/test/blackbox-tests/test-cases/dune-cache/cache-man.t
+++ b/test/blackbox-tests/test-cases/dune-cache/cache-man.t
@@ -13,6 +13,9 @@ Here we observe the documentation for the dune cache commands.
          functionality soon.
   
   COMMANDS
+         clear [OPTION]…
+             Clear the Dune cache.
+  
          size [--machine-readable] [OPTION]…
              Query the size of the Dune cache.