Legend:
Page
Library
Module
Module type
Parameter
Class
Class type
Source
Page
Library
Module
Module type
Parameter
Class
Class type
Source
opamRepositoryState.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 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315
(**************************************************************************) (* *) (* Copyright 2012-2020 OCamlPro *) (* Copyright 2012 INRIA *) (* *) (* 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 open OpamStd.Op open OpamStateTypes let log fmt = OpamConsole.log "RSTATE" fmt let slog = OpamConsole.slog module Cache = struct type t = { cached_repofiles: (repository_name * OpamFile.Repo.t) list; cached_opams: (repository_name * OpamFile.OPAM.t OpamPackage.Map.t) list; } module C = OpamCached.Make (struct type nonrec t = t let name = "repository" end) let remove () = let root = OpamStateConfig.(!r.root_dir) in let cache_dir = OpamPath.state_cache_dir root in let remove_cache_file file = if OpamFilename.check_suffix file ".cache" then OpamFilename.remove file in List.iter remove_cache_file (OpamFilename.files cache_dir) let marshall rt = (* Repository without remote are not cached, they are intended to be manually edited *) let filter_out_nourl repos_map = OpamRepositoryName.Map.filter (fun name _ -> try (OpamRepositoryName.Map.find name rt.repositories).repo_url <> OpamUrl.empty with Not_found -> false) repos_map in { cached_repofiles = OpamRepositoryName.Map.bindings (filter_out_nourl rt.repos_definitions); cached_opams = OpamRepositoryName.Map.bindings (filter_out_nourl rt.repo_opams); } let file rt = OpamPath.state_cache rt.repos_global.root let save rt = remove (); C.save (file rt) (marshall rt) let save_new rt = C.save (file rt) (marshall rt) let load root = let file = OpamPath.state_cache root in match C.load file with | Some cache -> Some (OpamRepositoryName.Map.of_list cache.cached_repofiles, OpamRepositoryName.Map.of_list cache.cached_opams) | None -> None end let load_opams_from_dir repo_name repo_root = if OpamConsole.disp_status_line () || OpamConsole.verbose () then OpamConsole.status_line "Processing: [%s: loading data]" (OpamConsole.colorise `blue (OpamRepositoryName.to_string repo_name)); (* FIXME: why is this different from OpamPackage.list ? *) let rec aux r dir = if OpamFilename.exists_dir dir then let fnames = Sys.readdir (OpamFilename.Dir.to_string dir) in if Array.exists (fun f -> f = "opam") fnames then match OpamFileTools.read_repo_opam ~repo_name ~repo_root dir with | Some opam -> (try let nv = OpamPackage.of_string OpamFilename.(Base.to_string (basename_dir dir)) in OpamPackage.Map.add nv opam r with Failure _ -> log "ERR: directory name not a valid package: ignored %s" OpamFilename.(to_string Op.(dir // "opam")); r) | None -> log "ERR: Could not load %s, ignored" OpamFilename.(to_string Op.(dir // "opam")); r else Array.fold_left (fun r name -> aux r OpamFilename.Op.(dir / name)) r fnames else r in Fun.protect (fun () -> aux OpamPackage.Map.empty (OpamRepositoryPath.packages_dir repo_root)) ~finally:OpamConsole.clear_status let load_repo repo repo_root = let t = OpamConsole.timer () in let repo_def = OpamFile.Repo.safe_read (OpamRepositoryPath.repo repo_root) |> OpamFile.Repo.with_root_url repo.repo_url in let opams = load_opams_from_dir repo.repo_name repo_root in log "loaded opam files from repo %s in %.3fs" (OpamRepositoryName.to_string repo.repo_name) (t ()); repo_def, opams (* Cleaning directories follows the repo path pattern: TMPDIR/opam-tmp-dir/repo-dir, defined in [load]. *) let clean_repo_tmp tmp_dir = if Lazy.is_val tmp_dir then (let dir = Lazy.force tmp_dir in OpamFilename.rmdir dir; let parent = OpamFilename.dirname_dir dir in if OpamFilename.dir_is_empty parent then OpamFilename.rmdir parent) let remove_from_repos_tmp rt name = try clean_repo_tmp (Hashtbl.find rt.repos_tmp name); Hashtbl.remove rt.repos_tmp name with Not_found -> () let cleanup rt = Hashtbl.iter (fun _ tmp_dir -> clean_repo_tmp tmp_dir) rt.repos_tmp; Hashtbl.clear rt.repos_tmp let get_root_raw root repos_tmp name = match Hashtbl.find repos_tmp name with | lazy repo_root -> repo_root | exception Not_found -> OpamRepositoryPath.root root name let get_root rt name = get_root_raw rt.repos_global.root rt.repos_tmp name let get_repo_root rt repo = get_root_raw rt.repos_global.root rt.repos_tmp repo.repo_name let load lock_kind gt = OpamFormatUpgrade.as_necessary_repo_switch_light_upgrade lock_kind `Repo gt; log "LOAD-REPOSITORY-STATE %@ %a" (slog OpamFilename.Dir.to_string) gt.root; let lock = OpamFilename.flock lock_kind (OpamPath.repos_lock gt.root) in let repos_map = OpamStateConfig.Repos.safe_read ~lock_kind gt in if OpamStateConfig.is_newer_than_self gt then log "root version (%s) is greater than running binary's (%s); \ load with best-effort (read-only)" (OpamVersion.to_string (OpamFile.Config.opam_root_version gt.config)) (OpamVersion.to_string (OpamFile.Config.root_version)); let mk_repo name url_opt = { repo_name = name; repo_url = OpamStd.Option.Op.((url_opt >>| fst) +! OpamUrl.empty); repo_trust = OpamStd.Option.Op.(url_opt >>= snd); } in let uncached = (* Don't cache repositories without remote, as they should be editable in-place *) OpamRepositoryName.Map.filter (fun _ url -> url = None) repos_map in let repositories = OpamRepositoryName.Map.mapi mk_repo repos_map in let repos_tmp_root = lazy (OpamFilename.mk_tmp_dir ()) in let repos_tmp = Hashtbl.create 23 in OpamRepositoryName.Map.iter (fun name repo -> let uncompressed_root = OpamRepositoryPath.root gt.root repo.repo_name in let tar = OpamRepositoryPath.tar gt.root repo.repo_name in if not (OpamFilename.exists_dir uncompressed_root) && OpamFilename.exists tar then let tmp = lazy ( let tmp_root = Lazy.force repos_tmp_root in try (* We rely on this path pattern to clean the repo. cf. [clean_repo_tmp] *) OpamFilename.extract_in tar tmp_root; OpamFilename.Op.(tmp_root / OpamRepositoryName.to_string name) with Failure s -> OpamFilename.remove tar; OpamConsole.error_and_exit `Aborted "%s.\nRun `opam update --repositories %s` to fix the issue" s (OpamRepositoryName.to_string name); ) in Hashtbl.add repos_tmp name tmp ) repositories; let make_rt repos_definitions opams = let rt = { repos_global = (gt :> unlocked global_state); repos_lock = lock; repos_tmp; repositories; repos_definitions; repo_opams = opams; } in OpamStd.Sys.at_exit (fun () -> cleanup rt); rt in match Cache.load gt.root with | Some (repofiles, opams) when OpamRepositoryName.Map.is_empty uncached -> log "Cache found"; make_rt repofiles opams | Some (repofiles, opams) -> log "Cache found, loading repositories without remote only"; OpamFilename.with_flock_upgrade `Lock_read lock @@ fun _ -> let repofiles, opams = OpamRepositoryName.Map.fold (fun name url (defs, opams) -> let repo = mk_repo name url in let repo_def, repo_opams = load_repo repo (get_root_raw gt.root repos_tmp name) in OpamRepositoryName.Map.add name repo_def defs, OpamRepositoryName.Map.add name repo_opams opams) uncached (repofiles, opams) in make_rt repofiles opams | None -> log "No cache found"; OpamFilename.with_flock_upgrade `Lock_read lock @@ fun _ -> let repofiles, opams = OpamRepositoryName.Map.fold (fun name url (defs, opams) -> let repo = mk_repo name url in let repo_def, repo_opams = load_repo repo (get_root_raw gt.root repos_tmp name) in OpamRepositoryName.Map.add name repo_def defs, OpamRepositoryName.Map.add name repo_opams opams) repos_map (OpamRepositoryName.Map.empty, OpamRepositoryName.Map.empty) in let rt = make_rt repofiles opams in Cache.save_new rt; rt let find_package_opt rt repo_list nv = List.fold_left (function | None -> fun repo_name -> OpamStd.Option.Op.( OpamRepositoryName.Map.find_opt repo_name rt.repo_opams >>= OpamPackage.Map.find_opt nv >>| fun opam -> repo_name, opam ) | some -> fun _ -> some) None repo_list let build_index rt repo_list = List.fold_left (fun acc repo_name -> try let repo_opams = OpamRepositoryName.Map.find repo_name rt.repo_opams in OpamPackage.Map.union (fun a _ -> a) acc repo_opams with Not_found -> (* A repo is unavailable, error should have been already reported *) acc) OpamPackage.Map.empty repo_list let get_repo rt name = OpamRepositoryName.Map.find name rt.repositories let unlock ?cleanup:(cln=true) rt = if cln then cleanup rt; OpamSystem.funlock rt.repos_lock; (rt :> unlocked repos_state) let drop ?cleanup rt = let _ = unlock ?cleanup rt in () let with_write_lock ?dontblock rt f = if OpamStateConfig.is_newer_than_self rt.repos_global then OpamConsole.error_and_exit `Locked "The opam root has been upgraded by a newer version of opam-state \ and cannot be written to"; let ret, rt = OpamFilename.with_flock_upgrade `Lock_write ?dontblock rt.repos_lock @@ fun _ -> f ({ rt with repos_lock = rt.repos_lock } : rw repos_state) (* We don't actually change the field value, but this makes restricting the phantom lock type possible *) in ret, { rt with repos_lock = rt.repos_lock } let with_ lock gt f = let rt = load lock gt in OpamStd.Exn.finally (fun () -> drop rt) (fun () -> f rt) let write_config rt = OpamFile.Repos_config.write (OpamPath.repos_config rt.repos_global.root) (OpamRepositoryName.Map.map (fun r -> if r.repo_url = OpamUrl.empty then None else Some (r.repo_url, r.repo_trust)) rt.repositories) let check_last_update () = if OpamCoreConfig.(!r.debug_level) < 0 then () else let last_update = OpamFilename.written_since (OpamPath.state_cache (OpamStateConfig.(!r.root_dir))) in if last_update > float_of_int (3600*24*21) then OpamConsole.note "It seems you have not updated your repositories \ for a while. Consider updating them with:\n%s\n" (OpamConsole.colorise `bold "opam update");