package opam-repository

  1. Overview
  2. Docs

Source file opamRepositoryRoot.ml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
(**************************************************************************)
(*                                                                        *)
(*    Copyright 2025-2026 Kate Deplaix                                    *)
(*    Copyright 2026 OCamlPro                                             *)
(*                                                                        *)
(*  All rights reserved. This file is distributed under the terms of the  *)
(*  GNU Lesser General Public License version 2.1, with the special       *)
(*  exception on linking described in the file LICENSE.                   *)
(*                                                                        *)
(**************************************************************************)

open OpamTypes

module Dir = struct
  type t = dirname

  let of_dir = Fun.id
  let to_dir = Fun.id
  let to_string = OpamFilename.Dir.to_string

  let quarantine repo_root = OpamFilename.raw_dir (to_string repo_root ^ ".new")
  let with_tmp = OpamFilename.with_tmp_dir
  let backup ~inn repo_root =
    let open OpamFilename.Op in
    inn / OpamFilename.Base.to_string (OpamFilename.basename_dir repo_root)

  let cwd = OpamFilename.cwd
  let exists = OpamFilename.exists_dir
  let remove = OpamFilename.rmdir
  let move = OpamFilename.move_dir
  let copy = OpamFilename.copy_dir
  let copy_except_vcs = OpamFilename.copy_dir_except_vcs
  let is_symlink = OpamFilename.is_symlink_dir
  let patch = OpamFilename.patch
  let make_empty = OpamFilename.mkdir
  let dirs = OpamFilename.dirs
  let is_empty = OpamFilename.dir_is_empty
  let dirname = OpamFilename.dirname_dir

  module Op = OpamFilename.Op

  module Path = OpamRepositoryPath.Make (struct
      type root = t
      type file = filename
      type dir = dirname
      type 'a typed_file = 'a OpamFile.t
      let root root name =
        let open Op in
        root / OpamRepositoryPathName.repo_d / OpamRepositoryName.to_string name
      let absolute root f = Op.(root // f)
      let absolute_dir root d = Op.(root / OpamFilename.Dir.to_string d)
      let dir_of_string = OpamFilename.raw_dir
      let to_typed_file = OpamFile.make
      module Op = Op
    end)

end

module Tgz = struct
  type t = OpamFilename.t

  let of_file = Fun.id
  let to_file = Fun.id
  let to_string = OpamFilename.to_string

  let quarantine tar = OpamFilename.raw (to_string tar ^ ".new")
  let backup ~inn tar =
    OpamFilename.create inn (OpamFilename.basename tar)

  let exists = OpamFilename.exists
  let remove = OpamFilename.remove
  let extract_in = OpamFilename.extract_in
  let download_as = OpamDownload.download_as
  let copy = OpamFilename.copy
  let move = OpamFilename.move
  let is_symlink = OpamFilename.is_symlink

  let fold = OpamTar.fold_reg_files

  let patch ~allow_unclean patch_source tar =
    let patch_source =
      match patch_source with
      | `Patch_file f -> `Patch_file (OpamFilename.to_string f)
      | `Patch_diffs _ as d -> d
    in
    OpamTar.patch ~allow_unclean patch_source tar

  let filter_files cond t =
    fold (fun acc file content ->
        if cond file then (file,content)::acc else acc)
      [] t

  let is_empty t =
    if exists t then
      let empty =
        try
          fold (fun _ _ _ -> raise Exit) true t
        with Exit -> false
      in
      Some empty
    else None

  module Path = OpamRepositoryPath.Make (struct
      type root = t
      type file = unix_filename
      type dir = unix_dirname
      type 'a typed_file = 'a OpamFile.t
      module Op = OpamFilename.Unix.Op
      let root root name =
        let open OpamFilename.Op in
        of_file (root / OpamRepositoryPathName.repo_d
                 // (OpamRepositoryName.to_string name ^ ".tar.gz"))
      let absolute _root f = OpamFilename.Unix.of_string f
      let absolute_dir _root d = d
      let dir_of_string = OpamFilename.Unix.Dir.of_string
      let to_typed_file f = OpamFile.make (OpamFilename.Unix.to_filename f)
    end)

end

let make_tar_gz = OpamTar.create ~flat:true ~except_vcs:true
let extract_in_job = OpamFilename.extract_in_job

type t =
  | Dir of Dir.t
  | Tgz of Tgz.t

let quarantine = function
  | Dir dir -> Dir (Dir.quarantine dir)
  | Tgz tgz -> Tgz (Tgz.quarantine tgz)

let backup ~inn = function
  | Dir dir -> Dir (Dir.backup ~inn dir)
  | Tgz tgz -> Tgz (Tgz.backup ~inn tgz)

let remove = function
  | Dir dir -> Dir.remove dir
  | Tgz tgz -> Tgz.remove tgz

let is_empty = function
  | Dir dir -> Dir.is_empty dir
  | Tgz tgz -> Tgz.is_empty tgz

let make_empty = function
  | Dir dir -> Dir.make_empty dir
  | Tgz _tar -> () (* Creating an empty tgz file doesn't make sense *)

let dirname = function
  | Dir dir -> OpamFilename.dirname_dir (Dir.to_dir dir)
  | Tgz tgz -> OpamFilename.dirname (Tgz.to_file tgz)

let basename = function
  | Dir dir -> OpamFilename.basename_dir (Dir.to_dir dir)
  | Tgz tgz -> OpamFilename.basename (Tgz.to_file tgz)

let to_string = function
  | Dir dir -> Dir.to_string dir
  | Tgz tgz -> Tgz.to_string tgz

let remove_prefix rr file =
  match rr with
  | Dir dir ->
    OpamFilename.remove_prefix dir file
    |> OpamFilename.Unix.of_string
  | Tgz _ -> OpamFilename.Unix.of_filename file

let remove_prefix_dir rr d =
  match rr with
  | Dir dir ->
    OpamFilename.remove_prefix_dir dir d
    |> OpamFilename.Unix.Dir.of_string
  | Tgz _ -> OpamFilename.Unix.Dir.of_dir d

let string_of_backend = function
  | Dir _ -> "dir"
  | Tgz _ -> "tgz"

let copy ~src ~dst =
  match src, dst with
  | Dir src, Dir dst -> Dir.copy ~src ~dst
  | Tgz src, Tgz dst -> Tgz.copy ~src ~dst
  | Tgz src, Dir dst -> OpamFilename.extract_in src dst
  | Dir src, Tgz dst -> make_tar_gz dst src

let move ~src ~dst =
  match src, dst with
  | Dir src, Dir dst -> Dir.move ~src ~dst
  | Tgz src, Tgz dst -> Tgz.move ~src ~dst
  | Tgz _, Dir _
  | Dir _, Tgz _ ->
    copy ~src ~dst;
    remove src

let exists = function
  | Dir dir -> Dir.exists dir
  | Tgz tgz -> Tgz.exists tgz

let is_symlink = function
  | Dir dir -> Dir.is_symlink dir
  | Tgz tgz -> Tgz.is_symlink tgz

let patch ~allow_unclean patch = function
  | Dir dir -> Dir.patch ~allow_unclean patch dir
  | Tgz tgz -> Tgz.patch ~allow_unclean patch tgz

let read_file (type a) (module R : OpamFile.IO_FILE with type t = a)
    ?(safe=false) repo_root
  : ?filename:a OpamFile.t -> string -> a =
  let rd =
    if safe then R.safe_read_from_string
    else R.read_from_string
  in
  rd ~loc:(to_string repo_root)

let delayed_read_repo = function
  | Dir dir ->
    let repo_file_path =
      OpamFilename.Op.(dir // OpamRepositoryPathName.repo_f)
      |> OpamFile.make
    in
    let read () = OpamFile.Repo.safe_read repo_file_path in
    (OpamFile.exists repo_file_path, read)
  | Tgz tgz ->
    let repo = OpamFilename.Unix.of_string OpamRepositoryPathName.repo_f in
    let repo_content =
      let exception Found of string in
      try
        Tgz.fold (fun () fname content ->
            if OpamFilename.Unix.equal fname repo then
              raise (Found content))
          () (Tgz.to_file tgz);
        None
      with Found content -> Some content
    in
    let read () =
      match repo_content with
      | None -> OpamFile.Repo.empty
      | Some content ->
        let filename = OpamFile.make (OpamFilename.Unix.to_filename repo) in
        let loc = OpamFilename.to_string tgz in
        OpamFile.Repo.safe_read_from_string ~loc ~filename content
    in
    (Option.is_some repo_content, read)

let remove_both root name =
  remove (Tgz (Tgz.Path.root root name));
  remove (Dir (Dir.Path.root root name))

let on_dir f = function
  | Dir dir -> f dir
  | Tgz tgz ->
    OpamFilename.with_tmp_dir (fun dir ->
        Tgz.extract_in tgz dir;
        let repo_dir = Dir.of_dir dir in
        let res = f repo_dir in
        make_tar_gz tgz repo_dir;
        res)

let root_exists root name =
  exists (Tgz (Tgz.Path.root root name))
  || exists (Dir (Dir.Path.root root name))