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
let () = B0_scope.open_lib ~module':__MODULE__ "dune"
open B0_std
open Result.Syntax
let write_file ~dry_run file content =
let* () =
if dry_run
then Ok ()
else Os.File.write ~force:true ~make_path:true file content
in
Log.stdout (fun m -> m ~header:"WROTE" "%a" Fpath.pp file);
if dry_run
then Ok (Log.stdout (fun m -> m "%s@\n" content))
else Ok ()
let library_stanza ppf u =
let requires = B0_unit.find_meta B0_ocaml.requires u in
let requires = Option.value ~default:[] requires in
let requires = List.map B0_ocaml.Libname.to_string requires in
let library = Option.get (B0_unit.find_meta B0_ocaml.library u) in
let library = B0_ocaml.Libname.to_string library in
let mod_names = match B0_unit.find_meta B0_ocaml.modsrcs u with
| None -> []
| Some m ->
if true then [] else
let modname (_, src) = B0_ocaml.Modsrc.modname src in
List.map modname (B0_ocaml.Modname.Map.bindings (Fut.sync m))
in
Fmt.pf ppf
"@[<v1>(library@,(public_name %s)@,(wrapped false)@,@[<1>(libraries %a)@]@,\
@[<1>(modules %a)@])@]"
library Fmt.(list ~sep:sp string) requires Fmt.(list string) mod_names
let dune_file libs =
Fmt.str "@[<v>(include_subdirs unqualified)@,@[<v>%a@]@]"
Fmt.(list ~sep:Fmt.(cut ++ cut) library_stanza) libs
let write_dune_file ~dry_run (scope_dir, libs) =
let file = Fpath.(scope_dir / "dune") in
write_file ~dry_run file (dune_file libs)
let dune_project () =
Fmt.str "@[<v>(lang dune 3.8)@,(generate_opam_files false)@]"
let ocaml_libs_by_scope us =
let add_by_scope_dir acc u =
if not (B0_unit.mem_meta B0_ocaml.library u) then acc else
Log.if_error ~use:acc @@
let* scope_dir = B0_unit.scope_dir' u in
Ok (Fpath.Map.add_to_list scope_dir u acc)
in
List.fold_left add_by_scope_dir Fpath.Map.empty us
let file ~env ~units ~dry_run =
let dry_run = true in
Log.if_error ~use:Os.Exit.no_such_name @@
let* units = B0_unit.get_list_or_hint ~all_if_empty:true units in
let ocaml_libs = Fpath.Map.bindings (ocaml_libs_by_scope units) in
let root_dir = B0_env.root_dir env in
let root_project = Fpath.(root_dir / "dune-project") in
let* () = write_file ~dry_run root_project (dune_project ()) in
let* () = List.iter_stop_on_error (write_dune_file ~dry_run) ocaml_libs in
Ok Os.Exit.ok
open Cmdliner
open Cmdliner.Term.Syntax
let units ~right:r =
let doc = "The $(docv) to act on. All of them if unspecified." in
Arg.(value & pos_right r string [] & info [] ~doc ~docv:"UNIT")
let dune_cmd env u =
let man =
[ `S Manpage.s_see_also;
`P "Consult $(b,odig doc b0) for the b0 dune manual."]
in
let export =
let doc = "Best-effort dune file generation" in
let man = [
`P "$(cmd) tries to derive dune files for OCaml \
libraries defined in the project. This is a best-effort \
procedure the files may need tweaking and since $(b,b0)'s build \
model is more dynamic this may fail. But it should work well \
on simple libraries.";
`P "Use option $(b,--dry-run) to check the files that would be written \
and their contents.";
`Blocks man ]
in
Cmd.make (Cmd.info "file" ~doc ~man) @@
let+ units = units ~right:(-1)
and+ dry_run =
let doc = "Do not write the files action, just print them." in
Arg.(value & flag & info ["dry-run"] ~doc)
in
file ~env ~units ~dry_run
in
let man =
[ `S Cmdliner.Manpage.s_description;
`P "$(cmd) helps with $(b,dune) \
see the b0 dune manual in $(b,odig doc b0) and \
invoke the subcommands with $(b,--help) for more \
information.";
`Blocks man]
in
let name = B0_unit.name u and doc = B0_unit.doc u in
Cmd.group (Cmd.info name ~doc ~man) @@
[export]
let unit =
let doc = "dune support" in
B0_unit.of_cmdliner_cmd "" dune_cmd ~doc
let () = B0_scope.close ()