Legend:
Page
Library
Module
Module type
Parameter
Class
Class type
Source
Page
Library
Module
Module type
Parameter
Class
Class type
Source
opamRepositoryBackend.ml1 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(**************************************************************************) (* *) (* Copyright 2015-2019 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 let log ?level fmt = OpamConsole.log "REPO_BACKEND" ?level fmt let slog = OpamConsole.slog type update = | Update_full of OpamRepositoryRoot.t | Update_patch of (filename * Patch.t list) | Update_empty | Update_err of exn module type S = sig val name: OpamUrl.backend val pull_url: ?full_fetch:bool -> ?cache_dir:dirname -> ?subpath:subpath -> dirname -> OpamHash.t option -> url -> filename option download OpamProcess.job val fetch_repo_update: repository_name -> ?cache_dir:dirname -> OpamRepositoryRoot.t -> url -> update OpamProcess.job val repo_update_complete: OpamRepositoryRoot.t -> url -> unit OpamProcess.job val revision: dirname -> string option OpamProcess.job val sync_dirty: ?subpath:subpath -> dirname -> url -> filename option download OpamProcess.job val get_remote_url: ?hash:string -> dirname -> url option OpamProcess.job end let compare r1 r2 = compare r1.repo_name r2.repo_name let to_string r = Printf.sprintf "%s from %s" (OpamRepositoryName.to_string r.repo_name) (OpamUrl.to_string r.repo_url) let to_json r = `O [ ("name", OpamRepositoryName.to_json r.repo_name); ("kind", `String (OpamUrl.string_of_backend r.repo_url.OpamUrl.backend)); ] let check_digest filename = function | Some expected when OpamRepositoryConfig.(!r.force_checksums) <> Some false -> (match OpamHash.mismatch (OpamFilename.to_string filename) expected with | None -> true | Some bad_hash -> OpamConsole.error "Bad checksum for %s: expected %s\n\ \ got %s\n\ Metadata might be out of date, in this case use `opam update`." (OpamFilename.to_string filename) (OpamHash.to_string expected) (OpamHash.to_string bad_hash); false) | _ -> true let job_text name label = OpamProcess.Job.with_text (Printf.sprintf "[%s: %s]" (OpamConsole.colorise `green (OpamRepositoryName.to_string name)) label) (** DIFF *) (* We put back the prefix for patch -p1 harmonisation *) let add_prefix repo1 repo2 = let prefix repo = OpamRepositoryRoot.basename repo |> OpamFilename.Base.to_string in let p1 x = prefix repo1 ^ "/" ^ x in let p2 x = prefix repo2 ^ "/" ^ x in fun patch -> let operation = match patch.Patch.operation with | Patch.Create f -> Patch.Create (p2 f) | Patch.Delete f -> Patch.Delete (p1 f) | Patch.Edit (f1, f2) -> Patch.Edit (p1 f1, p2 f2) | Patch.Git_ext (f1, f2, ext) -> Patch.Git_ext (p1 f1, p2 f2, ext) in {patch with operation} let get_diff repo1 repo2 = let chrono = OpamConsole.timer () in log "diff: %a" (fun fmt () -> if OpamFilename.Dir.equal (OpamRepositoryRoot.dirname repo1) (OpamRepositoryRoot.dirname repo2) then Format.fprintf fmt "%s/{%s,%s}" (OpamFilename.Dir.to_string (OpamRepositoryRoot.dirname repo1)) (OpamFilename.Base.to_string (OpamRepositoryRoot.basename repo1)) (OpamFilename.Base.to_string (OpamRepositoryRoot.basename repo2)) else Format.fprintf fmt "%s %s vs %s %s" (OpamRepositoryRoot.string_of_backend repo1) (OpamRepositoryRoot.to_string repo1) (OpamRepositoryRoot.string_of_backend repo2) (OpamRepositoryRoot.to_string repo2)) (); let get_contents = let get_tgz_contents tgz = OpamRepositoryRoot.Tgz.fold (fun acc filename content -> OpamStd.String.Map.add (OpamFilename.Unix.to_string filename) content acc) OpamStd.String.Map.empty tgz in let read_dir_contents dir = let fail s = failwith (s ^ " are unsupported") in (* Recursively read directory contents into a string map. Returns a map from relative file paths to their contents. *) let rec aux acc prefix current_dir = let entries = OpamSystem.get_files_except_vcs current_dir in List.fold_left (fun acc entry -> let full_path = Filename.concat current_dir entry in let relative_path = match prefix with | None -> entry | Some prefix -> prefix ^ "/" ^ entry in let stat = Unix.lstat full_path in match stat.Unix.st_kind with | Unix.S_REG -> let content = OpamSystem.read full_path in OpamStd.String.Map.add relative_path content acc | Unix.S_DIR -> aux acc (Some relative_path) full_path | Unix.S_LNK -> fail "Symlinks" | Unix.S_CHR -> fail "Character devices" | Unix.S_BLK -> fail "Block devices" | Unix.S_FIFO -> fail "Named pipes" | Unix.S_SOCK -> fail "Sockets") acc entries in aux OpamStd.String.Map.empty None dir in function | OpamRepositoryRoot.Dir dir -> read_dir_contents (OpamRepositoryRoot.Dir.to_string dir) | OpamRepositoryRoot.Tgz tgz -> get_tgz_contents tgz in let contents1 = get_contents repo1 in let contents2 = get_contents repo2 in let get_content_diffs filename contents1 content2 diffs seen = (* Compute content diffs for a single file. Compares [content2] (new) against [contents1] (old state map). Adds [filename] to [seen] set and generates a diff if contents differ. Returns updated (diffs, seen) accumulator pair *) let seen = OpamStd.String.Set.add filename seen in match OpamStd.String.Map.find_opt filename contents1 with | Some content1 when String.equal content1 content2 -> (diffs, seen) | content1_opt -> let content1 = Option.map (fun c -> (filename, c)) content1_opt in let content2 = Some (filename, content2) in match Patch.diff content1 content2 with | None -> (diffs, seen) | Some diff -> (diff :: diffs, seen) in let diffs, seen = OpamStd.String.Map.fold (fun filename content2 (diffs, seen) -> get_content_diffs filename contents1 content2 diffs seen) contents2 ([], OpamStd.String.Set.empty) in let diffs = (* NOTE: putting the deletions first in the list allows us to have a simpler implementation of OpamPatch. This might not be needed in the future if OpamPatch supports git apply style reordering. *) OpamStd.String.Map.fold (fun filename content diffs -> if OpamStd.String.Set.mem filename seen then diffs else match Patch.diff (Some (filename, content)) None with | None -> diffs | Some diff -> diff :: diffs) contents1 diffs in match diffs with | [] -> log "Internal diff (empty) done in %.2fs." (chrono ()); None | diffs -> log "Internal diff (non-empty, %a changed files) done in %.2fs." (slog (fun l -> string_of_int (List.length l))) diffs (chrono ()); let patch = OpamSystem.temp_file ~auto_clean:false "patch" in let patch_file = OpamFilename.of_string patch in let file_diffs = List.map (add_prefix repo1 repo2) diffs in OpamFilename.write patch_file (Format.asprintf "%a" Patch.pp_list file_diffs); Some (patch_file, diffs)