Source file opamPinCommand.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
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
open OpamTypes
open OpamStateTypes
open OpamStd.Op
let log fmt = OpamConsole.log "COMMAND" fmt
let slog = OpamConsole.slog
let string_of_pinned opam =
let bold = OpamConsole.colorise `bold in
Printf.sprintf "pinned %s (version %s)"
(OpamStd.Option.to_string ~none:(bold "locally")
(fun u -> Printf.sprintf "to %s%s"
(bold (OpamUrl.to_string (OpamFile.URL.url u)))
(OpamStd.Option.map_default (fun s -> bold (" ("^s^")"))
"" (OpamFile.URL.subpath u)))
(OpamFile.OPAM.url opam))
(bold (OpamPackage.Version.to_string (OpamFile.OPAM.version opam)))
let read_opam_file_for_pinning ?(quiet=false) name f url =
let opam0 =
let dir = OpamFilename.dirname (OpamFile.filename f) in
let add_files = OpamUrl.local_dir url = Some dir in
let opam =
(OpamFormatUpgrade.opam_file_with_aux ~quiet ~dir ~files:add_files
~filename:f) (OpamFile.OPAM.safe_read f)
in
if opam = OpamFile.OPAM.empty then None else Some opam
in
(match opam0 with
| None ->
let warns, _ = OpamFileTools.lint_file f in
OpamConsole.error
"Invalid opam file in %s source from %s:"
(OpamPackage.Name.to_string name)
(OpamUrl.to_string url);
OpamConsole.errmsg "%s\n" (OpamFileTools.warns_to_string warns)
| Some opam ->
let warns = OpamFileTools.lint opam in
if not quiet && warns <> [] then
(OpamConsole.warning
"Failed checks on %s package definition from source at %s:"
(OpamPackage.Name.to_string name)
(OpamUrl.to_string url);
OpamConsole.errmsg "%s\n" (OpamFileTools.warns_to_string warns)));
opam0
exception Fetch_Fail of string
let get_source_definition ?version ?subpath ?locked st nv url =
let root = st.switch_global.root in
let srcdir = OpamPath.Switch.pinned_package root st.switch nv.name in
let fix opam =
OpamFile.OPAM.with_url url @@
(match version with
| Some v -> OpamFile.OPAM.with_version v
| None -> fun o -> o) @@
opam
in
let open OpamProcess.Job.Op in
let url =
let u = OpamFile.URL.url url in
match OpamUrl.local_dir u, u.OpamUrl.backend with
| Some dir, #OpamUrl.version_control ->
OpamFile.URL.with_url
(OpamUrl.of_string (OpamFilename.Dir.to_string dir))
url
| _, _ -> url
in
OpamUpdate.fetch_dev_package url srcdir ?subpath nv @@| function
| Not_available (_,s) -> raise (Fetch_Fail s)
| Up_to_date _ | Result _ ->
let subsrcdir =
match OpamFile.URL.subpath url with
| None -> srcdir
| Some subpath -> OpamFilename.Op.(srcdir / subpath) in
match OpamPinned.find_opam_file_in_source ?locked nv.name subsrcdir with
| None -> None
| Some f ->
match read_opam_file_for_pinning nv.name f (OpamFile.URL.url url) with
| None ->
let dst =
OpamFile.filename
(OpamPath.Switch.Overlay.tmp_opam root st.switch nv.name)
in
OpamFilename.copy ~src:(OpamFile.filename f) ~dst;
None
| Some opam -> Some (fix opam)
let copy_files st opam =
let name = OpamFile.OPAM.name opam in
let files =
OpamFile.OPAM.get_extra_files
~repos_roots:(OpamRepositoryState.get_root st.switch_repos)
opam
in
if files = [] then
(match OpamFile.OPAM.extra_files opam with
| Some [] | None -> ()
| Some files ->
OpamConsole.warning
"Ignoring overlay files of %s (files%s*) that were not found: %s"
(OpamPackage.Name.to_string name) Filename.dir_sep
(OpamStd.List.to_string (fun (b,_) -> OpamFilename.Base.to_string b)
files));
let destdir =
OpamPath.Switch.Overlay.files st.switch_global.root st.switch name
in
let files =
List.fold_left (fun acc (src, rel_file, hash) ->
if not (OpamFilename.exists src) then
(OpamConsole.warning "Overlay file of %s %s not found, ignoring"
(OpamPackage.Name.to_string name)
(OpamFilename.to_string src);
acc)
else
let hash =
if not (OpamHash.check_file (OpamFilename.to_string src) hash) then
if OpamFormatConfig.(!r.strict) then
OpamConsole.error_and_exit `File_error
"Hash mismatch on %s %s (strict mode)"
(OpamPackage.Name.to_string name)
(OpamFilename.to_string src)
else
(OpamConsole.warning
"Hash doesn't match for overlay file of %s %s, adjusted"
(OpamPackage.Name.to_string name)
(OpamFilename.to_string src);
OpamHash.compute (OpamFilename.to_string src))
else hash
in
OpamFilename.copy ~src ~dst:(OpamFilename.create destdir rel_file);
(rel_file, hash) :: acc)
[] files
in
OpamFile.OPAM.with_extra_files (List.rev files) opam
let edit_raw name temp_file =
let rec edit () =
if OpamStd.Sys.tty_in then
(OpamConsole.msg "Press enter to start \"%s\" (this can be customised by \
setting EDITOR or OPAMEDITOR)... "
OpamClientConfig.(!r.editor);
ignore (read_line ()));
let edited_ok =
try
Sys.command
(Printf.sprintf "%s %s"
(OpamClientConfig.(!r.editor))
(OpamFile.to_string temp_file))
= 0 &&
match OpamFilename.read (OpamFile.filename temp_file)
with "" | "\n" -> false | _ -> true
with _ -> false
in
if not edited_ok then
(OpamFilename.remove (OpamFile.filename temp_file);
OpamConsole.error "Empty file or editor error, aborting.";
None)
else
try
let warnings, opam_opt =
OpamFileTools.lint_file temp_file
in
let opam = match opam_opt with
| None ->
OpamConsole.msg "Invalid opam file:\n%s\n"
(OpamFileTools.warns_to_string warnings);
failwith "Syntax errors"
| Some opam -> opam
in
let namecheck = match OpamFile.OPAM.name_opt opam with
| Some n when n <> name ->
OpamConsole.error "Bad \"name: %S\" field, package name is %s"
(OpamPackage.Name.to_string n) (OpamPackage.Name.to_string name);
false
| _ -> true
in
let versioncheck = match OpamFile.OPAM.version_opt opam with
| None ->
OpamConsole.error "Missing \"version\" field.";
false
| Some _ -> true
in
if not namecheck || not versioncheck then failwith "Bad name/version";
match warnings with
| [] -> Some opam
| ws ->
OpamConsole.warning "The opam file didn't pass validation:";
OpamConsole.errmsg "%s\n" (OpamFileTools.warns_to_string ws);
if OpamConsole.confirm "Proceed anyway ('no' will re-edit)?"
then Some opam
else edit ()
with e ->
OpamStd.Exn.fatal e;
(match e with
| Failure _ -> ()
| e -> OpamConsole.error "%s" (Printexc.to_string e));
if OpamStd.Sys.tty_in &&
OpamConsole.confirm "Errors in %s, edit again?"
(OpamFile.to_string temp_file)
then edit ()
else None
in
match edit () with
| None -> None
| Some new_opam ->
OpamConsole.msg
"You can edit this file again with \"opam pin edit %s\", export it with \
\"opam show %s --raw\"\n"
(OpamPackage.Name.to_string name)
(OpamPackage.Name.to_string name);
Some new_opam
let edit st ?version name =
log "pin-edit %a" (slog OpamPackage.Name.to_string) name;
let nv =
try OpamPinned.package st name
with Not_found ->
OpamConsole.error_and_exit `Bad_arguments "%s is not pinned"
(OpamPackage.Name.to_string name)
in
let new_nv = match version with
| None -> nv
| Some v -> OpamPackage.create name v
in
let path f = f st.switch_global.root st.switch name in
let overlay_file = path OpamPath.Switch.Overlay.opam in
let temp_file = path OpamPath.Switch.Overlay.tmp_opam in
let current_opam = OpamSwitchState.opam_opt st nv in
if not (OpamFile.exists temp_file) then
(let base_opam = match current_opam with
| None -> OpamFileTools.template new_nv
| Some o -> OpamFile.OPAM.with_version new_nv.version o
in
OpamFile.OPAM.write_with_preserved_format
?format_from:(OpamPinned.orig_opam_file st name base_opam)
temp_file base_opam);
match edit_raw name temp_file with
| None -> st
| Some opam ->
let opam = match current_opam with
| Some cur -> OpamFile.OPAM.(with_metadata_dir (metadata_dir cur)) opam
| None -> opam
in
let opam = copy_files st opam in
match current_opam with
| Some o when OpamFile.OPAM.equal opam o ->
(OpamConsole.msg "Package metadata unchanged.\n"; st)
| _ ->
OpamFilename.remove
(OpamFile.filename (path OpamPath.Switch.Overlay.url));
OpamFilename.remove
(OpamFile.filename (path OpamPath.Switch.Overlay.descr));
let =
OpamStd.Option.default [] @@ OpamFile.OPAM.extra_files opam
in
List.iter (fun f ->
let base =
OpamFilename.Base.of_string @@
OpamFilename.remove_prefix (path OpamPath.Switch.Overlay.files) f
in
if not (List.mem_assoc base opam_extra) then
(OpamConsole.note "Removing obsolete overlay file %s"
(OpamFilename.to_string f);
OpamFilename.remove f))
(OpamFilename.rec_files (path OpamPath.Switch.Overlay.files));
OpamFile.OPAM.write_with_preserved_format ~format_from:temp_file
overlay_file
opam;
OpamFilename.remove (OpamFile.filename temp_file);
ignore OpamStd.Option.Op.(
OpamFile.OPAM.get_url opam >>= OpamUrl.local_dir >>| fun dir ->
let src_opam =
OpamStd.Option.default
(OpamFile.make OpamFilename.Op.(dir // "opam"))
(OpamPinned.find_opam_file_in_source name dir)
in
let clean_opam =
OpamFile.OPAM.with_url_opt None @*
OpamFile.OPAM.with_extra_files []
in
if (current_opam >>| fun o ->
OpamFile.OPAM.equal (clean_opam opam) (clean_opam o))
<> Some true &&
OpamConsole.confirm "Save the new opam file back to %S?"
(OpamFile.to_string src_opam) then
OpamFile.OPAM.write_with_preserved_format src_opam
(clean_opam opam)
);
let nv = OpamPackage.create name (OpamFile.OPAM.version opam) in
let st = OpamSwitchState.update_pin nv opam st in
OpamUpdate.cleanup_source st current_opam opam;
if not OpamClientConfig.(!r.show) then
OpamSwitchAction.write_selections st;
st
let version_pin st name version =
let root = st.switch_global.root in
let nv = OpamPackage.create name version in
let repo_opam =
try OpamPackage.Map.find nv st.repos_package_index
with Not_found ->
OpamConsole.error_and_exit `Not_found
"Package %s has no known version %s in the repositories"
(OpamPackage.Name.to_string name)
(OpamPackage.Version.to_string version)
in
begin match OpamPinned.package_opt st name with
| Some pinned_nv ->
let opam = OpamSwitchState.opam st pinned_nv in
if Some opam =
OpamPackage.Map.find_opt pinned_nv st.repos_package_index
then
(if pinned_nv <> nv then
(OpamConsole.note
"Package %s used to be pinned to version %s"
(OpamPackage.Name.to_string name)
(OpamPackage.Version.to_string pinned_nv.version);
OpamFilename.rmdir
(OpamPath.Switch.Overlay.package root st.switch name))
else OpamConsole.note "Pinning unchanged")
else if OpamConsole.confirm
"Package %s is already %s. Unpin and continue?"
(OpamPackage.Name.to_string name)
(string_of_pinned opam)
then
OpamFilename.rmdir
(OpamPath.Switch.Overlay.package root st.switch name)
else
(OpamConsole.msg "Aborting.\n"; OpamStd.Sys.exit_because `Aborted)
| None -> ()
end;
let st = OpamSwitchState.update_pin nv repo_opam st in
if not OpamClientConfig.(!r.show) then
OpamSwitchAction.write_selections st;
OpamConsole.msg "%s is now pinned to version %s\n"
(OpamPackage.Name.to_string name)
(OpamPackage.Version.to_string version);
st
exception Aborted
exception Nothing_to_do
let default_version st name =
try OpamPackage.version (OpamSwitchState.get_package st name)
with Not_found -> OpamPackage.Version.of_string "~dev"
let fetch_all_pins st ?working_dir pins =
let root = st.switch_global.root in
let fetched =
let cache_dir =
OpamRepositoryPath.download_cache OpamStateConfig.(!r.root_dir)
in
let command (name, url, subpath) =
let srcdir = OpamPath.Switch.pinned_package root st.switch name in
let name = OpamPackage.Name.to_string name in
OpamProcess.Job.Op.(
OpamRepository.pull_tree ~cache_dir ?subpath ?working_dir
name srcdir [] [url]
@@| fun r -> (name, url, subpath, r))
in
OpamParallel.map ~jobs:OpamStateConfig.(!r.dl_jobs) ~command pins
in
let errored, to_pin =
List.fold_left (fun (err,ok) result ->
let name, url, subpath, result = result in
match result with
| Not_available _ ->
(name, url, subpath)::err, ok
| _ -> err, (url, subpath)::ok)
([],[]) fetched
in
if errored = []
|| OpamConsole.confirm
"Could not retrieve some package sources, they will not be pinned nor \
installed:%s\n\
Continue anyway?"
(OpamStd.Format.itemize (fun (name, url, subpath) ->
name ^ ": " ^ OpamUrl.to_string url ^
(OpamStd.Option.to_string (fun s -> "("^s^")") subpath))
errored)
then
to_pin
else
OpamStd.Sys.exit_because `Aborted
let rec handle_pin_depends st nv opam =
let = OpamFile.OPAM.pin_depends opam in
let =
List.filter (fun (nv, url) ->
not (OpamPackage.Set.mem nv st.pinned &&
OpamSwitchState.primary_url st nv = Some url))
extra_pins
in
if extra_pins = [] then st else
(OpamConsole.msg
"The following additional pinnings are required by %s:\n%s"
(OpamPackage.to_string nv)
(OpamStd.Format.itemize
(fun (nv, url) -> Printf.sprintf "%s at %s"
(OpamConsole.colorise `bold (OpamPackage.to_string nv))
(OpamConsole.colorise `underline (OpamUrl.to_string url)))
extra_pins);
if OpamConsole.confirm "Pin and install them?" then
(let =
let urls_ok =
fetch_all_pins st (List.map (fun (nv, u) ->
OpamPackage.name nv, u, None) extra_pins)
in
List.filter (fun (_, url) -> List.mem (url, None) urls_ok) extra_pins
in
List.fold_left (fun st (nv, url) ->
source_pin st nv.name ~version:nv.version (Some url)
~ignore_extra_pins:true)
st extra_pins)
else if OpamConsole.confirm
"Try to install anyway, assuming `--ignore-pin-depends'?"
then st else
OpamStd.Sys.exit_because `Aborted)
and source_pin
st name
?version ?edit:(need_edit=false) ?opam:opam_opt ?(quiet=false)
?(force=false) ?(=OpamClientConfig.(!r.ignore_pin_depends))
?subpath ?locked
target_url
=
log "pin %a to %a %a%a"
(slog OpamPackage.Name.to_string) name
(slog (OpamStd.Option.to_string OpamPackage.Version.to_string)) version
(slog (OpamStd.Option.to_string ~none:"none" OpamUrl.to_string)) target_url
(slog (OpamStd.Option.to_string ~none:"" (fun x -> " ("^x^")"))) subpath;
let open OpamStd.Option.Op in
let cur_version, cur_urlf =
try
let cur_version = OpamPinned.version st name in
let nv = OpamPackage.create name cur_version in
let cur_opam = OpamSwitchState.opam st nv in
let cur_urlf = OpamFile.OPAM.url cur_opam in
let no_changes =
target_url = OpamStd.Option.map OpamFile.URL.url cur_urlf &&
(version = Some cur_version || version = None)
in
if not (quiet && no_changes) then
OpamConsole.note
"Package %s is %s %s."
(OpamPackage.Name.to_string name)
(if no_changes then "already" else "currently")
(string_of_pinned cur_opam);
if no_changes then ()
else
OpamFilename.remove
(OpamFile.filename
(OpamPath.Switch.Overlay.tmp_opam
st.switch_global.root st.switch name))
;
cur_version, cur_urlf
with Not_found ->
let version = default_version st name in
version, None
in
if not (OpamPackage.has_name st.packages name) &&
not (OpamConsole.confirm
"Package %s does not exist, create as a %s package?"
(OpamPackage.Name.to_string name)
(OpamConsole.colorise `bold "NEW"))
then raise Aborted;
(match OpamStd.Option.map OpamFile.URL.url cur_urlf, target_url with
| Some u, Some target when OpamUrl.(
u.transport <> target.transport ||
u.path <> target.path ||
u.backend <> target.backend
) ->
OpamFilename.rmdir
(OpamPath.Switch.pinned_package st.switch_global.root st.switch name)
| _ -> ());
let pin_version = version +! cur_version in
let nv = OpamPackage.create name pin_version in
let urlf = target_url >>| OpamFile.URL.create ?subpath in
let temp_file =
OpamPath.Switch.Overlay.tmp_opam st.switch_global.root st.switch name
in
let opam_local =
OpamFile.OPAM.read_opt temp_file
>>| OpamFormatUpgrade.opam_file
in
OpamFilename.remove (OpamFile.filename temp_file);
let opam_opt =
try
opam_opt >>+ fun () ->
urlf >>= fun url ->
OpamProcess.Job.run @@ get_source_definition ?version ?subpath ?locked st nv url
with Fetch_Fail err ->
if force then None else
(OpamConsole.error_and_exit `Sync_error
"Error getting source from %s:\n%s"
(OpamStd.Option.to_string OpamUrl.to_string target_url)
(OpamStd.Format.itemize (fun x -> x) [err]));
in
let opam_opt = opam_opt >>| OpamFormatUpgrade.opam_file in
let nv =
match version with
| Some _ -> nv
| None ->
OpamPackage.create name
((opam_opt >>= OpamFile.OPAM.version_opt)
+! cur_version)
in
let opam_opt =
opam_opt >>+ fun () ->
OpamPackage.Map.find_opt nv st.installed_opams >>+ fun () ->
OpamSwitchState.opam_opt st nv
in
let opam_opt =
match opam_local, opam_opt with
| Some local, None ->
OpamConsole.warning
"Couldn't retrieve opam file from versioned source, \
using the one found locally.";
Some local
| Some local, Some vers when
not OpamFile.(OPAM.effectively_equal
(OPAM.with_url URL.empty local)
(OPAM.with_url URL.empty vers)) ->
OpamConsole.warning
"%s's opam file has uncommitted changes, using the versioned one"
(OpamPackage.Name.to_string name);
opam_opt
| _ -> opam_opt
in
if not need_edit && opam_opt = None then
OpamConsole.note
"No package definition found for %s: please complete the template"
(OpamConsole.colorise `bold (OpamPackage.to_string nv));
let need_edit = need_edit || opam_opt = None in
let opam_opt =
let opam_base = match opam_opt with
| None -> OpamFileTools.template nv
| Some opam -> opam
in
let opam_base =
OpamFile.OPAM.with_url_opt urlf opam_base
in
if need_edit then
(if not (OpamFile.exists temp_file) then
OpamFile.OPAM.write_with_preserved_format
?format_from:(OpamPinned.orig_opam_file st name opam_base)
temp_file opam_base;
edit_raw name temp_file >>|
OpamFile.OPAM.(with_metadata_dir (metadata_dir opam_base))
)
else
Some opam_base
in
match opam_opt with
| None ->
OpamConsole.error_and_exit `Not_found
"No valid package definition found"
| Some opam ->
let opam =
match OpamFile.OPAM.get_url opam with
| Some _ -> opam
| None -> OpamFile.OPAM.with_url_opt urlf opam
in
let version = version +! (OpamFile.OPAM.version_opt opam +! nv.version) in
let nv = OpamPackage.create nv.name version in
let st =
if ignore_extra_pins then st
else handle_pin_depends st nv opam
in
let opam =
opam |>
OpamFile.OPAM.with_name name |>
OpamFile.OPAM.with_version version
in
OpamFilename.rmdir
(OpamPath.Switch.Overlay.package st.switch_global.root st.switch nv.name);
let opam = copy_files st opam in
OpamFile.OPAM.write_with_preserved_format
?format_from:(OpamPinned.orig_opam_file st name opam)
(OpamPath.Switch.Overlay.opam st.switch_global.root st.switch nv.name)
opam;
OpamFilename.remove (OpamFile.filename temp_file);
let st = OpamSwitchState.update_pin nv opam st in
if not OpamClientConfig.(!r.show) then
OpamSwitchAction.write_selections st;
OpamConsole.msg "%s is now %s\n"
(OpamPackage.Name.to_string name)
(string_of_pinned opam);
st
let unpin_one st nv =
let st =
{ st with pinned = OpamPackage.Set.remove nv st.pinned }
in
let repo_package =
OpamPackage.Map.filter (fun nv2 _ -> nv2.name = nv.name)
st.repos_package_index
in
let available_packages = lazy (
OpamSwitchState.compute_available_packages
st.switch_global st.switch st.switch_config ~pinned:OpamPackage.Set.empty
~opams:repo_package |>
OpamPackage.Set.union
(OpamPackage.Set.remove nv (Lazy.force st.available_packages))
) in
match OpamPackage.Map.find_opt nv st.repos_package_index,
OpamPackage.Map.find_opt nv st.installed_opams with
| None, None ->
OpamSwitchState.remove_package_metadata nv st
| Some opam, _ | None, Some opam ->
let st = OpamSwitchState.update_package_metadata nv opam st in
{ st with available_packages }
let unpin st names =
log "unpin %a"
(slog @@ OpamStd.List.concat_map " " OpamPackage.Name.to_string) names;
List.fold_left (fun st name ->
OpamFilename.rmdir
(OpamPath.Switch.pinned_package st.switch_global.root st.switch name);
OpamFilename.rmdir
(OpamPath.Switch.Overlay.package
st.switch_global.root st.switch name);
match OpamPinned.package_opt st name with
| Some nv ->
let pin_str =
OpamStd.Option.to_string ~none:"pinned"
string_of_pinned (OpamSwitchState.opam_opt st nv)
in
let st = unpin_one st nv in
if not OpamClientConfig.(!r.show) then
OpamSwitchAction.write_selections st;
OpamConsole.msg "Ok, %s is no longer %s\n"
(OpamPackage.Name.to_string name) pin_str;
st
| None ->
OpamConsole.note "%s is not pinned." (OpamPackage.Name.to_string name);
st)
st names
let list st ~short =
log "pin_list";
if short then
OpamPackage.Set.iter
(fun nv -> OpamConsole.msg "%s\n" (OpamPackage.name_to_string nv))
st.pinned
else
let lines nv =
try
let opam = OpamSwitchState.opam st nv in
let url = OpamFile.OPAM.url opam in
let kind, target =
if OpamSwitchState.is_version_pinned st nv.name then
"version", OpamPackage.Version.to_string nv.version
else
match url with
| Some url ->
let u = OpamFile.URL.url url in
let subpath =
match OpamFile.URL.subpath url with
| None -> ""
| Some s -> " ("^s^")" in
OpamUrl.string_of_backend u.OpamUrl.backend,
OpamUrl.to_string u ^ subpath
| None -> "local definition", ""
in
let state, =
try
let inst =
OpamSwitchState.find_installed_package_by_name st nv.name
in
if inst.version = nv.version then "",[]
else
OpamConsole.colorise `red "(not in sync)",
[Printf.sprintf "(installed:%s)"
(OpamConsole.colorise `bold
(OpamPackage.version_to_string inst))]
with Not_found -> OpamConsole.colorise `yellow "(uninstalled)", []
in
[ OpamPackage.to_string nv;
state;
OpamConsole.colorise `blue kind;
String.concat " " (target::extra) ]
with Not_found ->
[ OpamPackage.to_string nv;
OpamConsole.colorise `red " (no definition found)" ]
in
let table = List.map lines (OpamPackage.Set.elements st.pinned) in
OpamConsole.print_table stdout ~sep:" " (OpamStd.Format.align_table table)
let scan_sep = '^'
let scan ~normalise ~recurse ?subpath url =
let open OpamStd.Option.Op in
let pins_of_dir dir =
OpamPinned.files_in_source ~recurse ?subpath dir
|> OpamStd.List.filter_map (fun (nf, opamf, sb) ->
let opam = OpamFile.OPAM.safe_read opamf in
match (nf ++ OpamFile.OPAM.name_opt opam) with
| Some name ->
Some (name, (OpamFile.OPAM.version_opt opam), sb)
| None ->
OpamConsole.warning "Can not retrieve a package name from %s"
(OpamFilename.to_string (OpamFile.filename opamf));
None)
in
let pins, cleanup =
match OpamUrl.local_dir url with
| Some dir -> pins_of_dir dir, None
| None ->
let pin_cache_dir = OpamRepositoryPath.pin_cache url in
let cleanup = fun () ->
OpamFilename.rmdir @@ OpamRepositoryPath.pin_cache_dir ()
in
let basename =
match OpamStd.String.split (OpamUrl.basename url) '.' with
| [] ->
OpamConsole.error_and_exit `Bad_arguments
"Can not retrieve a path from '%s'"
(OpamUrl.to_string url)
| b::_ -> b
in
try
let open OpamProcess.Job.Op in
OpamProcess.Job.run @@
OpamRepository.pull_tree
~cache_dir:(OpamRepositoryPath.download_cache
OpamStateConfig.(!r.root_dir))
basename pin_cache_dir [] [url] @@| function
| Not_available (_,u) ->
OpamConsole.error_and_exit `Sync_error
"Could not retrieve %s" u
| Result _ | Up_to_date _ ->
pins_of_dir pin_cache_dir, Some cleanup
with e -> OpamStd.Exn.finalise e cleanup
in
let finalise = OpamStd.Option.default (fun () -> ()) cleanup in
OpamStd.Exn.finally finalise @@ fun () ->
if normalise then
OpamConsole.msg "%s"
(OpamStd.List.concat_map "\n"
(fun (name, version, sb) ->
Printf.sprintf "%s%s%c%s%s"
(OpamPackage.Name.to_string name)
(OpamStd.Option.to_string
(fun v -> "." ^OpamPackage.Version.to_string v) version)
scan_sep
(OpamUrl.to_string url)
(OpamStd.Option.to_string (fun sb ->
(String.make 1 scan_sep) ^ sb) sb))
pins)
else
["# Name"; "# Version"; "# Url" ] ::
List.map (fun (name, version, _sb) ->
[ OpamPackage.Name.to_string name;
(version >>| OpamPackage.Version.to_string) +! "-";
OpamUrl.to_string url;
]) pins
|> OpamStd.Format.align_table
|> OpamConsole.print_table stdout ~sep:" "
let looks_like_normalised args =
List.for_all (fun s -> OpamStd.String.contains_char s scan_sep) args
let parse_pins pins =
let separator = Re.char scan_sep in
let re =
Re.(compile @@ whole_string @@ seq [
group @@
rep1 @@ alt [ alnum; diff punct (alt [char '.'; char scan_sep]) ];
opt @@ seq [ char '.';
group @@
rep1 @@ alt [ alnum; diff punct separator ]];
separator;
group @@ rep1 @@ diff any separator;
opt @@ seq [ separator; group @@ rep1 any ];
])
in
let get s =
try
let groups = Re.exec re s in
Some ( Re.Group.(
OpamPackage.Name.of_string @@ get groups 1,
OpamStd.Option.map OpamPackage.Version.of_string
@@ OpamStd.Option.of_Not_found (get groups) 2,
OpamUrl.parse @@ get groups 3,
OpamStd.Option.of_Not_found (get groups) 4)
)
with Not_found | Failure _ -> None
in
OpamStd.List.filter_map (fun str ->
let pin = get str in
if pin = None then
(OpamConsole.warning "Argument %S is not correct" str;
None)
else pin) pins