forked from ocaml/dune
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathvcs.ml
193 lines (172 loc) · 4.05 KB
/
vcs.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
open Stdune
module Utils = Dune_engine.Utils
module Process = Dune_engine.Process
module Kind = struct
type t =
| Git
| Hg
let of_dir_name = function
| ".git" -> Some Git
| ".hg" -> Some Hg
| _ -> None
;;
let of_dir_contents set =
match Filename.Set.find set ~f:(fun s -> Option.is_some (of_dir_name s)) with
| None -> None
| Some s -> Some (Option.value_exn (of_dir_name s))
;;
let to_dyn t =
Dyn.Variant
( (match t with
| Git -> "Git"
| Hg -> "Hg")
, [] )
;;
let equal = ( = )
end
module T = struct
type t =
{ root : Path.t
; kind : Kind.t
}
let to_dyn { root; kind } =
Dyn.record [ "root", Path.to_dyn root; "kind", Kind.to_dyn kind ]
;;
let equal { root = ra; kind = ka } { root = rb; kind = kb } =
Path.equal ra rb && Kind.equal ka kb
;;
(* No need to hash the kind as there is only only kind per directory *)
let hash t = Path.hash t.root
end
include T
let git, hg =
let get prog =
lazy
(match Bin.which ~path:(Env_path.path Env.initial) prog with
| Some x -> x
| None -> Utils.program_not_found prog ~loc:None)
in
get "git", get "hg"
;;
let select git hg t =
Memo.of_non_reproducible_fiber
(match t.kind with
| Git -> git t
| Hg -> hg t)
;;
let prog t =
Lazy.force
(match t.kind with
| Git -> git
| Hg -> hg)
;;
let run t args =
let open Fiber.O in
let+ s =
Process.run_capture ~display:Quiet Strict (prog t) args ~dir:t.root ~env:Env.initial
in
String.trim s
;;
let git_accept () : (_, _) Process.Failure_mode.t =
Accept (Predicate.create (fun x -> Int.equal x 0 || Int.equal x 128))
;;
let run_git t args =
let res =
Process.run_capture
(git_accept ())
~display:Quiet
(prog t)
args
~dir:t.root
~env:Env.initial
~stderr_to:(Process.Io.file Dev_null.path Out)
in
let open Fiber.O in
let+ res = res in
match res with
| Ok s -> Some (String.trim s)
| Error 128 -> None
| Error _ -> assert false
;;
let hg_describe t =
let open Fiber.O in
let* s = run t [ "log"; "--rev"; "."; "-T"; "{latesttag} {latesttagdistance}" ] in
let+ id = run t [ "id"; "-i" ] in
let id, dirty_suffix =
match String.drop_suffix id ~suffix:"+" with
| Some id -> id, "-dirty"
| None -> id, ""
in
let s =
let s, dist = Option.value_exn (String.rsplit2 s ~on:' ') in
match s with
| "null" -> id
| _ ->
(match Int.of_string dist with
| Some 1 -> s
| Some n -> sprintf "%s-%d-%s" s (n - 1) id
| None -> sprintf "%s-%s-%s" s dist id)
in
s ^ dirty_suffix
;;
let make_fun name ~git ~hg =
let memo = Memo.create name ~input:(module T) (select git hg) in
Staged.stage (Memo.exec memo)
;;
let describe =
Staged.unstage
@@ make_fun
"vcs-describe"
~git:(fun t -> run_git t [ "describe"; "--always"; "--dirty"; "--abbrev=7" ])
~hg:(fun x ->
let open Fiber.O in
let+ res = hg_describe x in
Some res)
;;
let commit_id =
Staged.unstage
@@ make_fun
"vcs-commit-id"
~git:(fun t -> run_git t [ "rev-parse"; "HEAD" ])
~hg:(fun t ->
let open Fiber.O in
let+ res = run t [ "id"; "-i" ] in
Some res)
;;
let files =
let run_zero_separated_hg t args =
Process.run_capture_zero_separated
Strict
(prog t)
args
~display:Quiet
~dir:t.root
~env:Env.initial
in
let run_zero_separated_git t args =
let open Fiber.O in
let+ res =
Process.run_capture_zero_separated
(git_accept ())
(prog t)
args
~display:Quiet
~dir:t.root
~env:Env.initial
in
match res with
| Ok s -> s
| Error 128 -> []
| Error _ -> assert false
in
let f run args t =
let open Fiber.O in
let+ l = run t args in
List.map l ~f:Path.in_source
in
Staged.unstage
@@ make_fun
"vcs-files"
~git:(f run_zero_separated_git [ "ls-tree"; "-z"; "-r"; "--name-only"; "HEAD" ])
~hg:(f run_zero_separated_hg [ "files"; "-0" ])
;;