Source file opam.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
open Action.Syntax
let find_git () =
let is_git p = Action.is_dir Fpath.(p / ".git") in
let app_opt p d = match p with None -> d | Some p -> Fpath.(d // p) in
let rec find p path =
if Fpath.is_root p then Action.ok None
else
let* has_git = is_git p in
if has_git then Action.ok (Some path)
else find (Fpath.parent p) (Some (app_opt path (Fpath.base p)))
in
let* cwd = Action.pwd () in
let* cwd = find (Fpath.parent cwd) None in
match cwd with
| None -> Action.ok None
| Some subdir ->
let git_branch =
Bos.Cmd.(v "git" % "rev-parse" % "--abbrev-ref" % "HEAD")
in
let* branch = Action.(run_cmd_out ~err:`Null git_branch) in
let git_remote = Bos.Cmd.(v "git" % "remote" % "get-url" % "origin") in
let+ git_url = Action.(run_cmd_out ~err:`Null git_remote) in
Some (subdir, branch, git_url)
module Endpoint = struct
type t = {
scheme : [ `SSH of string | `Git | `HTTP | `HTTPS | `Scheme of string ];
port : int option;
path : string;
hostname : string;
}
let of_string str =
let open Rresult in
let parse_ssh str =
let len = String.length str in
Emile.of_string_raw ~off:0 ~len str
|> R.reword_error (R.msgf "%a" Emile.pp_error)
>>= fun (consumed, m) ->
match
Astring.String.cut ~sep:":" (String.sub str consumed (len - consumed))
with
| Some ("", path) ->
let path = "/" ^ path in
let local =
List.map
(function `Atom x -> x | `String x -> Fmt.str "%S" x)
m.Emile.local
in
let user = String.concat "." local in
let hostname =
match fst m.Emile.domain with
| `Domain vs -> String.concat "." vs
| `Literal v -> v
| `Addr (Emile.IPv4 v) -> Ipaddr.V4.to_string v
| `Addr (Emile.IPv6 v) -> Ipaddr.V6.to_string v
| `Addr (Emile.Ext (k, v)) -> Fmt.str "%s:%s" k v
in
R.ok { scheme = `SSH user; path; port = None; hostname }
| _ -> R.error_msg "Invalid SSH pattern"
in
let parse_uri str =
let uri = Uri.of_string str in
let path = Uri.path uri in
match (Uri.scheme uri, Uri.host uri, Uri.port uri) with
| Some "git", Some hostname, port ->
R.ok { scheme = `Git; path; port; hostname }
| Some "http", Some hostname, port ->
R.ok { scheme = `HTTP; path; port; hostname }
| Some "https", Some hostname, port ->
R.ok { scheme = `HTTPS; path; port; hostname }
| Some scheme, Some hostname, port ->
R.ok { scheme = `Scheme scheme; path; port; hostname }
| _ -> R.error_msgf "Invalid uri: %a" Uri.pp uri
in
match (parse_ssh str, parse_uri str) with
| Ok v, _ -> Ok v
| _, Ok v -> Ok v
| Error _, Error _ -> R.error_msgf "Invalid endpoint: %s" str
end
let guess_src () =
let git_info =
match Action.run @@ find_git () with
| Error _ | Ok None -> None
| Ok (Some (subdir, branch, git_url)) -> Some (subdir, branch, git_url)
in
match git_info with
| None -> (None, None)
| Some (subdir, branch, origin) ->
let public =
match Endpoint.of_string origin with
| Ok
{ Endpoint.scheme = `Scheme scheme; port = None; path; hostname; _ }
->
Fmt.str "%s://%s%s" scheme hostname path
| Ok
{
Endpoint.scheme = `Scheme scheme;
port = Some port;
path;
hostname;
_;
} ->
Fmt.str "%s://%s:%d%s" scheme hostname port path
| Ok { Endpoint.port = None; path; hostname; _ } ->
Fmt.str "git+https://%s%s" hostname path
| Ok { Endpoint.port = Some port; path; hostname; _ } ->
Fmt.str "git+https://%s:%d%s" hostname port path
| _ -> "git+https://invalid/endpoint"
in
(subdir, Some (Fmt.str "%s#%s" public branch))
type t = {
name : string;
depends : Package.t list;
configure : string option;
pre_build : (Fpath.t option -> string) option;
lock_location : (Fpath.t option -> string -> string) option;
build : (Fpath.t option -> string) option;
install : Install.t;
extra_repo : (string * string) list;
pins : (string * string) list;
src : string option;
subdir : Fpath.t option;
opam_name : string;
}
let v ?configure ?pre_build ?lock_location ?build ?(install = Install.empty)
?( = []) ?(depends = []) ?(pins = []) ?subdir ~src ~opam_name name
=
let subdir, src =
match src with
| `Auto ->
let subdir', src = guess_src () in
((match subdir with None -> subdir' | Some _ as s -> s), src)
| `None -> (subdir, None)
| `Some d -> (subdir, Some d)
in
{
name;
depends;
configure;
pre_build;
lock_location;
build;
install;
extra_repo;
pins;
src;
subdir;
opam_name;
}
let pp_packages ppf packages =
Fmt.pf ppf "\n %a\n"
Fmt.(list ~sep:(any "\n ") (Package.pp ~surround:"\""))
packages
let pp_pins ppf = function
| [] -> ()
| pins ->
let pp_pin ppf (package, url) = Fmt.pf ppf "[\"%s\" %S]" package url in
Fmt.pf ppf "@.pin-depends: [ @[<hv>%a@]@ ]@."
Fmt.(list ~sep:(any "@ ") pp_pin)
pins
let pp_src ppf = function
| None -> ()
| Some src -> Fmt.pf ppf {|@.url { src: %S }|} src
let pp_switch_package ppf s = Fmt.pf ppf "%S" s
let pp ppf t =
let pp_cmd = function
| None -> ""
| Some cmd ->
Fmt.str {|"sh" "-exc" "%a%s"|}
Fmt.(option ~none:(any "") (any "cd " ++ Fpath.pp ++ any " && "))
t.subdir cmd
in
let pp_with_sub ppf = function
| None -> ()
| Some f -> Fmt.string ppf (f t.subdir)
in
let pp_repo =
Fmt.(
list ~sep:(any "\n")
(brackets (pair ~sep:(any " ") (quote string) (quote string))))
in
let switch_packages =
List.filter_map
(fun p ->
match Package.scope p with
| `Switch -> Some (Package.name p)
| `Monorepo -> None)
t.depends
in
Fmt.pf ppf
{|opam-version: "2.0"
maintainer: "dummy"
authors: "dummy"
homepage: "dummy"
bug-reports: "dummy"
dev-repo: "git://dummy"
synopsis: "Unikernel %s - switch dependencies"
description: """
It assumes that local dependencies are already
fetched.
"""
build: [%a]
install: [%a]
depends: [%a]
x-mirage-opam-lock-location: %S
x-mirage-configure: [%s]
x-mirage-pre-build: [%a]
x-mirage-extra-repo: [%a]
x-opam-monorepo-opam-provided: [%a]
%a%a|}
t.name pp_with_sub t.build
(Install.pp_opam ?subdir:t.subdir ())
t.install pp_packages t.depends
(Option.fold ~none:""
~some:(fun l -> l t.subdir t.opam_name)
t.lock_location)
(pp_cmd t.configure) pp_with_sub t.pre_build pp_repo t.extra_repo
(Fmt.list ~sep:(Fmt.any " ") pp_switch_package)
switch_packages pp_src t.src pp_pins t.pins