2018-05-02 11:56:12 +00:00
|
|
|
open Dune
|
2017-02-21 15:09:58 +00:00
|
|
|
open Import
|
2018-04-24 13:38:30 +00:00
|
|
|
open Cmdliner
|
2018-02-06 14:39:03 +00:00
|
|
|
open Fiber.O
|
2017-02-21 15:09:58 +00:00
|
|
|
|
2018-05-02 11:56:12 +00:00
|
|
|
(* Things in src/ don't depend on cmdliner to speed up the
|
|
|
|
bootstrap, so we set this reference here *)
|
2018-04-24 13:38:30 +00:00
|
|
|
let () = suggest_function := Cmdliner_suggest.value
|
2017-02-23 16:44:17 +00:00
|
|
|
|
2017-02-21 15:09:58 +00:00
|
|
|
type common =
|
2018-02-07 12:09:24 +00:00
|
|
|
{ debug_dep_path : bool
|
2018-01-25 19:07:46 +00:00
|
|
|
; debug_findlib : bool
|
|
|
|
; debug_backtraces : bool
|
2018-05-04 15:49:25 +00:00
|
|
|
; profile : string option
|
2018-01-25 19:07:46 +00:00
|
|
|
; workspace_file : string option
|
|
|
|
; root : string
|
|
|
|
; target_prefix : string
|
2018-03-02 18:44:03 +00:00
|
|
|
; only_packages : Package.Name.Set.t option
|
2018-01-25 19:07:46 +00:00
|
|
|
; capture_outputs : bool
|
|
|
|
; x : string option
|
|
|
|
; diff_command : string option
|
|
|
|
; auto_promote : bool
|
|
|
|
; force : bool
|
|
|
|
; ignore_promoted_rules : bool
|
2017-05-19 13:16:00 +00:00
|
|
|
; (* Original arguments for the external-lib-deps hint *)
|
2018-01-25 19:07:46 +00:00
|
|
|
orig_args : string list
|
2018-02-07 11:38:21 +00:00
|
|
|
; config : Config.t
|
2017-02-21 15:09:58 +00:00
|
|
|
}
|
|
|
|
|
2017-02-28 07:32:15 +00:00
|
|
|
let prefix_target common s = common.target_prefix ^ s
|
|
|
|
|
2017-05-19 13:16:00 +00:00
|
|
|
let set_common c ~targets =
|
2017-02-21 15:09:58 +00:00
|
|
|
Clflags.debug_dep_path := c.debug_dep_path;
|
2017-02-24 12:19:02 +00:00
|
|
|
Clflags.debug_findlib := c.debug_findlib;
|
2017-05-29 13:17:59 +00:00
|
|
|
Clflags.debug_backtraces := c.debug_backtraces;
|
2017-05-29 09:57:04 +00:00
|
|
|
Clflags.capture_outputs := c.capture_outputs;
|
2017-02-28 07:32:15 +00:00
|
|
|
if c.root <> Filename.current_dir_name then
|
2017-05-19 13:16:00 +00:00
|
|
|
Sys.chdir c.root;
|
2017-09-29 13:01:54 +00:00
|
|
|
Clflags.workspace_root := Sys.getcwd ();
|
2018-01-15 13:24:25 +00:00
|
|
|
Clflags.diff_command := c.diff_command;
|
2018-01-18 11:32:20 +00:00
|
|
|
Clflags.auto_promote := c.auto_promote;
|
2018-01-19 08:50:06 +00:00
|
|
|
Clflags.force := c.force;
|
2017-05-19 13:16:00 +00:00
|
|
|
Clflags.external_lib_deps_hint :=
|
|
|
|
List.concat
|
2018-05-02 11:56:12 +00:00
|
|
|
[ ["dune"; "external-lib-deps"; "--missing"]
|
2017-05-19 13:16:00 +00:00
|
|
|
; c.orig_args
|
|
|
|
; targets
|
|
|
|
]
|
2017-02-21 15:09:58 +00:00
|
|
|
|
2017-11-12 19:55:49 +00:00
|
|
|
let restore_cwd_and_execve common prog argv env =
|
2018-03-15 10:18:15 +00:00
|
|
|
let env = Env.to_unix env in
|
2017-11-12 19:55:49 +00:00
|
|
|
let prog =
|
|
|
|
if Filename.is_relative prog then
|
|
|
|
Filename.concat common.root prog
|
|
|
|
else
|
|
|
|
prog
|
|
|
|
in
|
|
|
|
Sys.chdir initial_cwd;
|
2017-08-04 07:59:35 +00:00
|
|
|
if Sys.win32 then
|
2017-11-12 19:55:49 +00:00
|
|
|
let pid = Unix.create_process_env prog argv env
|
|
|
|
Unix.stdin Unix.stdout Unix.stderr
|
|
|
|
in
|
|
|
|
match snd (Unix.waitpid [] pid) with
|
|
|
|
| WEXITED 0 -> ()
|
|
|
|
| WEXITED n -> exit n
|
|
|
|
| WSIGNALED _ -> exit 255
|
|
|
|
| WSTOPPED _ -> assert false
|
2017-08-04 07:59:35 +00:00
|
|
|
else
|
2017-11-12 19:55:49 +00:00
|
|
|
Unix.execve prog argv env
|
2017-08-04 07:59:35 +00:00
|
|
|
|
2017-02-27 15:04:49 +00:00
|
|
|
module Main = struct
|
2018-05-02 11:56:12 +00:00
|
|
|
include Dune.Main
|
2017-02-27 15:04:49 +00:00
|
|
|
|
2018-03-31 01:52:18 +00:00
|
|
|
let setup ~log ?external_lib_deps_mode common =
|
2017-03-02 18:21:19 +00:00
|
|
|
setup
|
2017-03-10 12:32:27 +00:00
|
|
|
~log
|
2018-05-02 11:56:12 +00:00
|
|
|
?workspace_file:(
|
|
|
|
Option.map common.workspace_file ~f:Path.of_string)
|
2017-03-02 18:21:19 +00:00
|
|
|
?only_packages:common.only_packages
|
2018-03-31 01:52:18 +00:00
|
|
|
?external_lib_deps_mode
|
2017-12-21 11:54:00 +00:00
|
|
|
?x:common.x
|
2018-05-04 15:49:25 +00:00
|
|
|
?profile:common.profile
|
2018-01-25 19:07:46 +00:00
|
|
|
~ignore_promoted_rules:common.ignore_promoted_rules
|
2018-03-29 15:58:41 +00:00
|
|
|
~capture_outputs:common.capture_outputs
|
2017-12-21 11:54:00 +00:00
|
|
|
()
|
2017-02-27 15:04:49 +00:00
|
|
|
end
|
|
|
|
|
2018-02-07 11:38:21 +00:00
|
|
|
module Log = struct
|
2018-05-02 11:56:12 +00:00
|
|
|
include Dune.Log
|
2018-02-07 11:38:21 +00:00
|
|
|
|
|
|
|
let create common =
|
|
|
|
Log.create ~display:common.config.display ()
|
|
|
|
end
|
|
|
|
|
|
|
|
module Scheduler = struct
|
2018-05-02 11:56:12 +00:00
|
|
|
include Dune.Scheduler
|
2018-02-07 11:38:21 +00:00
|
|
|
|
|
|
|
let go ?log ~common fiber =
|
2018-04-26 15:10:14 +00:00
|
|
|
let fiber =
|
|
|
|
Main.set_concurrency ?log common.config
|
|
|
|
>>= fun () ->
|
|
|
|
fiber
|
|
|
|
in
|
2018-02-07 11:38:21 +00:00
|
|
|
Scheduler.go ?log ~config:common.config fiber
|
|
|
|
end
|
|
|
|
|
2017-09-29 15:06:29 +00:00
|
|
|
type target =
|
|
|
|
| File of Path.t
|
2018-01-19 08:50:06 +00:00
|
|
|
| Alias_rec of Path.t
|
2017-09-29 15:06:29 +00:00
|
|
|
|
|
|
|
let request_of_targets (setup : Main.setup) targets =
|
|
|
|
let open Build.O in
|
2018-01-19 08:50:06 +00:00
|
|
|
let contexts = List.map setup.contexts ~f:(fun c -> c.Context.name) in
|
2017-09-29 15:06:29 +00:00
|
|
|
List.fold_left targets ~init:(Build.return ()) ~f:(fun acc target ->
|
|
|
|
acc >>>
|
|
|
|
match target with
|
|
|
|
| File path -> Build.path path
|
2018-01-19 08:50:06 +00:00
|
|
|
| Alias_rec path ->
|
2018-05-08 16:56:58 +00:00
|
|
|
let dir = Path.parent_exn path in
|
2018-01-19 08:50:06 +00:00
|
|
|
let name = Path.basename path in
|
|
|
|
let contexts, dir =
|
|
|
|
match Path.extract_build_context dir with
|
|
|
|
| None -> (contexts, dir)
|
|
|
|
| Some ("install", _) ->
|
|
|
|
die "Invalid alias: %s.\n\
|
2018-05-02 15:35:44 +00:00
|
|
|
There are no aliases in %s."
|
|
|
|
(Path.to_string_maybe_quoted Path.(relative build_dir "install"))
|
2018-01-19 08:50:06 +00:00
|
|
|
(Path.to_string_maybe_quoted path)
|
|
|
|
| Some (ctx, dir) -> ([ctx], dir)
|
|
|
|
in
|
|
|
|
Build_system.Alias.dep_rec_multi_contexts ~dir ~name
|
|
|
|
~file_tree:setup.file_tree ~contexts)
|
2017-09-29 15:06:29 +00:00
|
|
|
|
2017-03-01 19:19:43 +00:00
|
|
|
let do_build (setup : Main.setup) targets =
|
2018-02-06 14:39:03 +00:00
|
|
|
Build_system.do_build setup.build_system
|
2017-09-29 15:06:29 +00:00
|
|
|
~request:(request_of_targets setup targets)
|
2017-03-01 19:19:43 +00:00
|
|
|
|
2017-02-28 07:32:15 +00:00
|
|
|
let find_root () =
|
|
|
|
let cwd = Sys.getcwd () in
|
|
|
|
let rec loop counter ~candidates ~to_cwd dir =
|
2018-04-23 05:08:09 +00:00
|
|
|
let files = Sys.readdir dir |> Array.to_list |> String.Set.of_list in
|
2018-05-02 11:56:12 +00:00
|
|
|
if String.Set.mem files "dune-workspace" ||
|
|
|
|
String.Set.mem files "jbuild-workspace" then
|
2017-02-28 07:32:15 +00:00
|
|
|
cont counter ~candidates:((0, dir, to_cwd) :: candidates) dir ~to_cwd
|
2018-04-23 05:08:09 +00:00
|
|
|
else if String.Set.exists files ~f:(fun fn ->
|
2018-05-02 11:56:12 +00:00
|
|
|
String.is_prefix fn ~prefix:"jbuild-workspace") then
|
2017-02-28 07:32:15 +00:00
|
|
|
cont counter ~candidates:((1, dir, to_cwd) :: candidates) dir ~to_cwd
|
2018-05-02 11:56:12 +00:00
|
|
|
else if String.Set.mem files Dune_project.filename then
|
|
|
|
cont counter ~candidates:((2, dir, to_cwd) :: candidates) dir ~to_cwd
|
2017-02-28 07:32:15 +00:00
|
|
|
else
|
|
|
|
cont counter ~candidates dir ~to_cwd
|
|
|
|
and cont counter ~candidates ~to_cwd dir =
|
|
|
|
if counter > String.length cwd then
|
|
|
|
candidates
|
|
|
|
else
|
|
|
|
let parent = Filename.dirname dir in
|
|
|
|
if parent = dir then
|
|
|
|
candidates
|
|
|
|
else
|
|
|
|
let base = Filename.basename dir in
|
|
|
|
loop (counter + 1) parent ~candidates ~to_cwd:(base :: to_cwd)
|
|
|
|
in
|
|
|
|
match loop 0 ~candidates:[] ~to_cwd:[] cwd with
|
|
|
|
| [] -> (cwd, [])
|
|
|
|
| l ->
|
|
|
|
let lowest_priority =
|
|
|
|
List.fold_left l ~init:max_int ~f:(fun acc (prio, _, _) ->
|
|
|
|
min acc prio)
|
|
|
|
in
|
|
|
|
match List.find l ~f:(fun (prio, _, _) -> prio = lowest_priority) with
|
|
|
|
| None -> assert false
|
|
|
|
| Some (_, dir, to_cwd) -> (dir, to_cwd)
|
|
|
|
|
2018-03-02 18:44:03 +00:00
|
|
|
let package_name =
|
|
|
|
Arg.conv ((fun p -> Ok (Package.Name.of_string p)), Package.Name.pp)
|
|
|
|
|
2018-02-07 11:38:21 +00:00
|
|
|
let common_footer =
|
|
|
|
`Blocks
|
|
|
|
[ `S "BUGS"
|
|
|
|
; `P "Check bug reports at https://github.com/ocaml/dune/issues"
|
|
|
|
]
|
|
|
|
|
2017-02-21 15:09:58 +00:00
|
|
|
let copts_sect = "COMMON OPTIONS"
|
|
|
|
let help_secs =
|
|
|
|
[ `S copts_sect
|
|
|
|
; `P "These options are common to all commands."
|
|
|
|
; `S "MORE HELP"
|
|
|
|
; `P "Use `$(mname) $(i,COMMAND) --help' for help on a single command."
|
2018-02-07 11:38:21 +00:00
|
|
|
; common_footer
|
2017-02-21 15:09:58 +00:00
|
|
|
]
|
|
|
|
|
2018-02-07 11:38:21 +00:00
|
|
|
type config_file =
|
|
|
|
| No_config
|
|
|
|
| Default
|
2018-04-24 20:25:27 +00:00
|
|
|
| This of Path.t
|
2018-02-07 11:38:21 +00:00
|
|
|
|
|
|
|
let incompatible a b =
|
|
|
|
`Error (true,
|
|
|
|
sprintf
|
|
|
|
"Cannot use %s and %s simultaneously"
|
|
|
|
a b)
|
|
|
|
|
2017-02-21 15:09:58 +00:00
|
|
|
let common =
|
2017-05-19 13:16:00 +00:00
|
|
|
let dump_opt name value =
|
|
|
|
match value with
|
|
|
|
| None -> []
|
|
|
|
| Some s -> [name; s]
|
|
|
|
in
|
2017-03-02 18:21:19 +00:00
|
|
|
let make
|
|
|
|
concurrency
|
|
|
|
debug_dep_path
|
|
|
|
debug_findlib
|
2017-05-29 13:17:59 +00:00
|
|
|
debug_backtraces
|
2017-05-29 09:57:04 +00:00
|
|
|
no_buffer
|
2017-03-02 18:21:19 +00:00
|
|
|
workspace_file
|
2018-01-15 13:24:25 +00:00
|
|
|
diff_command
|
2018-01-18 11:32:20 +00:00
|
|
|
auto_promote
|
2018-01-19 08:50:06 +00:00
|
|
|
force
|
2018-01-25 19:07:46 +00:00
|
|
|
(root,
|
|
|
|
only_packages,
|
|
|
|
ignore_promoted_rules,
|
2018-02-07 11:38:21 +00:00
|
|
|
config_file,
|
2018-05-04 15:49:25 +00:00
|
|
|
profile,
|
2018-01-25 19:07:46 +00:00
|
|
|
orig)
|
2017-12-21 11:54:00 +00:00
|
|
|
x
|
2018-02-07 11:38:21 +00:00
|
|
|
display
|
2017-03-02 18:21:19 +00:00
|
|
|
=
|
2017-02-28 07:32:15 +00:00
|
|
|
let root, to_cwd =
|
|
|
|
match root with
|
|
|
|
| Some dn -> (dn, [])
|
2018-03-29 16:09:23 +00:00
|
|
|
| None ->
|
|
|
|
if Config.inside_dune then
|
|
|
|
(".", [])
|
|
|
|
else
|
|
|
|
find_root ()
|
2017-02-28 07:32:15 +00:00
|
|
|
in
|
2017-05-19 13:16:00 +00:00
|
|
|
let orig_args =
|
|
|
|
List.concat
|
2018-05-04 15:49:25 +00:00
|
|
|
[ dump_opt "--profile" profile
|
2017-05-19 13:16:00 +00:00
|
|
|
; dump_opt "--workspace" workspace_file
|
|
|
|
; orig
|
|
|
|
]
|
|
|
|
in
|
2018-02-07 11:38:21 +00:00
|
|
|
let config =
|
|
|
|
match config_file with
|
|
|
|
| No_config -> Config.default
|
2018-04-24 20:25:27 +00:00
|
|
|
| This fname -> Config.load_config_file fname
|
2018-04-12 11:43:14 +00:00
|
|
|
| Default ->
|
|
|
|
if Config.inside_dune then
|
|
|
|
Config.default
|
|
|
|
else
|
|
|
|
Config.load_user_config_file ()
|
2018-02-07 11:38:21 +00:00
|
|
|
in
|
|
|
|
let config =
|
2018-02-07 12:09:24 +00:00
|
|
|
Config.merge config
|
|
|
|
{ display
|
|
|
|
; concurrency
|
|
|
|
}
|
2018-02-07 11:38:21 +00:00
|
|
|
in
|
2018-02-15 14:04:00 +00:00
|
|
|
let config =
|
|
|
|
Config.adapt_display config
|
2018-02-25 16:35:25 +00:00
|
|
|
~output_is_a_tty:(Lazy.force Colors.stderr_supports_colors)
|
2018-02-15 14:04:00 +00:00
|
|
|
in
|
2018-02-07 12:09:24 +00:00
|
|
|
{ debug_dep_path
|
2017-02-24 12:19:02 +00:00
|
|
|
; debug_findlib
|
2017-05-29 13:17:59 +00:00
|
|
|
; debug_backtraces
|
2018-05-04 15:49:25 +00:00
|
|
|
; profile
|
2017-05-29 09:57:04 +00:00
|
|
|
; capture_outputs = not no_buffer
|
2017-02-27 15:04:49 +00:00
|
|
|
; workspace_file
|
2017-02-28 07:32:15 +00:00
|
|
|
; root
|
2017-05-19 13:16:00 +00:00
|
|
|
; orig_args
|
2017-02-28 07:32:15 +00:00
|
|
|
; target_prefix = String.concat ~sep:"" (List.map to_cwd ~f:(sprintf "%s/"))
|
2018-01-15 13:24:25 +00:00
|
|
|
; diff_command
|
2018-01-18 11:32:20 +00:00
|
|
|
; auto_promote
|
2018-01-19 08:50:06 +00:00
|
|
|
; force
|
2018-01-25 19:07:46 +00:00
|
|
|
; ignore_promoted_rules
|
2017-03-02 18:21:19 +00:00
|
|
|
; only_packages =
|
|
|
|
Option.map only_packages
|
2018-03-02 18:44:03 +00:00
|
|
|
~f:(fun s -> Package.Name.Set.of_list (
|
|
|
|
List.map ~f:Package.Name.of_string (String.split s ~on:',')))
|
2017-12-21 11:54:00 +00:00
|
|
|
; x
|
2018-02-07 11:38:21 +00:00
|
|
|
; config
|
2017-02-24 12:19:02 +00:00
|
|
|
}
|
|
|
|
in
|
2017-02-21 15:09:58 +00:00
|
|
|
let docs = copts_sect in
|
|
|
|
let concurrency =
|
2018-04-26 15:10:14 +00:00
|
|
|
let arg =
|
|
|
|
Arg.conv
|
|
|
|
((fun s ->
|
|
|
|
Result.map_error (Config.Concurrency.of_string s)
|
|
|
|
~f:(fun s -> `Msg s)),
|
|
|
|
fun pp x ->
|
|
|
|
Format.pp_print_string pp (Config.Concurrency.to_string x))
|
|
|
|
in
|
2017-02-28 19:05:04 +00:00
|
|
|
Arg.(value
|
2018-04-26 15:10:14 +00:00
|
|
|
& opt (some arg) None
|
2017-02-28 19:05:04 +00:00
|
|
|
& info ["j"] ~docs ~docv:"JOBS"
|
|
|
|
~doc:{|Run no more than $(i,JOBS) commands simultaneously.|}
|
|
|
|
)
|
|
|
|
in
|
2017-03-02 18:21:19 +00:00
|
|
|
let only_packages =
|
|
|
|
Arg.(value
|
|
|
|
& opt (some string) None
|
|
|
|
& info ["only-packages"] ~docs ~docv:"PACKAGES"
|
|
|
|
~doc:{|Ignore stanzas referring to a package that is not in $(b,PACKAGES).
|
2017-06-02 09:51:32 +00:00
|
|
|
$(b,PACKAGES) is a comma-separated list of package names.
|
2017-05-26 11:33:55 +00:00
|
|
|
Note that this has the same effect as deleting the relevant stanzas
|
|
|
|
from jbuild files. It is mostly meant for releases.
|
|
|
|
During development, it is likely that what you want instead is to
|
|
|
|
build a particular $(b,<package>.install) target.|}
|
2017-03-02 18:21:19 +00:00
|
|
|
)
|
|
|
|
in
|
2017-02-28 19:05:04 +00:00
|
|
|
let ddep_path =
|
|
|
|
Arg.(value
|
|
|
|
& flag
|
2017-03-01 19:28:44 +00:00
|
|
|
& info ["debug-dependency-path"] ~docs
|
2017-02-28 19:05:04 +00:00
|
|
|
~doc:{|In case of error, print the dependency path from
|
|
|
|
the targets on the command line to the rule that failed.
|
|
|
|
|})
|
|
|
|
in
|
|
|
|
let dfindlib =
|
|
|
|
Arg.(value
|
|
|
|
& flag
|
|
|
|
& info ["debug-findlib"] ~docs
|
|
|
|
~doc:{|Debug the findlib sub-system.|})
|
|
|
|
in
|
2017-05-29 13:17:59 +00:00
|
|
|
let dbacktraces =
|
|
|
|
Arg.(value
|
|
|
|
& flag
|
|
|
|
& info ["debug-backtraces"] ~docs
|
|
|
|
~doc:{|Always print exception backtraces.|})
|
|
|
|
in
|
2017-02-28 19:05:04 +00:00
|
|
|
let dev =
|
|
|
|
Arg.(value
|
|
|
|
& flag
|
|
|
|
& info ["dev"] ~docs
|
2018-05-04 15:49:25 +00:00
|
|
|
~doc:{|Same as $(b,--profile dev)|})
|
|
|
|
in
|
|
|
|
let profile =
|
|
|
|
Arg.(value
|
|
|
|
& opt (some string) None
|
|
|
|
& info ["profile"] ~docs
|
|
|
|
~doc:{|Select the build profile, for instance $(b,dev) or $(b,release).
|
|
|
|
The default is $(b,default).|})
|
|
|
|
in
|
|
|
|
let profile =
|
|
|
|
let merge dev profile =
|
|
|
|
match dev, profile with
|
|
|
|
| false, x -> `Ok x
|
|
|
|
| true , None -> `Ok (Some "dev")
|
|
|
|
| true , Some _ ->
|
|
|
|
`Error (true,
|
|
|
|
"Cannot use --dev and --profile simultaneously")
|
|
|
|
in
|
|
|
|
Term.(ret (const merge
|
|
|
|
$ dev
|
|
|
|
$ profile))
|
2017-02-28 19:05:04 +00:00
|
|
|
in
|
2018-02-07 11:38:21 +00:00
|
|
|
let display =
|
|
|
|
let verbose =
|
|
|
|
Arg.(value
|
|
|
|
& flag
|
|
|
|
& info ["verbose"] ~docs
|
|
|
|
~doc:"Same as $(b,--display verbose)")
|
|
|
|
in
|
|
|
|
let display =
|
|
|
|
Arg.(value
|
|
|
|
& opt (some (enum Config.Display.all)) None
|
|
|
|
& info ["display"] ~docs ~docv:"MODE"
|
2018-05-02 11:56:12 +00:00
|
|
|
~doc:{|Control the display mode of Dune.
|
2018-02-07 11:38:21 +00:00
|
|
|
See $(b,dune-config\(5\)) for more details.|})
|
|
|
|
in
|
|
|
|
let merge verbose display =
|
|
|
|
match verbose, display with
|
|
|
|
| false , None -> `Ok None
|
|
|
|
| false , Some x -> `Ok (Some x)
|
|
|
|
| true , None -> `Ok (Some Config.Display.Verbose)
|
|
|
|
| true , Some _ -> incompatible "--display" "--verbose"
|
|
|
|
in
|
|
|
|
Term.(ret (const merge $ verbose $ display))
|
2017-03-30 16:36:58 +00:00
|
|
|
in
|
2017-05-29 09:57:04 +00:00
|
|
|
let no_buffer =
|
|
|
|
Arg.(value
|
|
|
|
& flag
|
|
|
|
& info ["no-buffer"] ~docs ~docv:"DIR"
|
2018-05-02 11:56:12 +00:00
|
|
|
~doc:{|Do not buffer the output of commands executed by dune.
|
|
|
|
By default dune buffers the output of subcommands, in order
|
2017-05-29 09:57:04 +00:00
|
|
|
to prevent interleaving when multiple commands are executed
|
|
|
|
in parallel. However, this can be an issue when debugging
|
|
|
|
long running tests. With $(b,--no-buffer), commands have direct
|
|
|
|
access to the terminal. Note that as a result their output won't
|
|
|
|
be captured in the log file.
|
|
|
|
|
|
|
|
You should use this option in conjunction with $(b,-j 1),
|
|
|
|
to avoid interleaving. Additionally you should use
|
|
|
|
$(b,--verbose) as well, to make sure that commands are printed
|
|
|
|
before they are being executed.|})
|
|
|
|
in
|
2017-02-27 15:04:49 +00:00
|
|
|
let workspace_file =
|
|
|
|
Arg.(value
|
|
|
|
& opt (some file) None
|
2017-02-28 19:05:04 +00:00
|
|
|
& info ["workspace"] ~docs ~docv:"FILE"
|
2017-02-28 07:32:15 +00:00
|
|
|
~doc:"Use this specific workspace file instead of looking it up.")
|
|
|
|
in
|
2018-01-18 11:32:20 +00:00
|
|
|
let auto_promote =
|
2018-01-15 13:24:25 +00:00
|
|
|
Arg.(value
|
2018-01-18 11:32:20 +00:00
|
|
|
& flag
|
|
|
|
& info ["auto-promote"] ~docs
|
|
|
|
~doc:"Automatically promote files. This is similar to running
|
2018-05-02 11:56:12 +00:00
|
|
|
$(b,dune promote) after the build.")
|
2018-01-15 13:24:25 +00:00
|
|
|
in
|
2018-01-19 08:50:06 +00:00
|
|
|
let force =
|
|
|
|
Arg.(value
|
|
|
|
& flag
|
|
|
|
& info ["force"; "f"]
|
|
|
|
~doc:"Force actions associated to aliases to be re-executed even
|
|
|
|
if their dependencies haven't changed.")
|
|
|
|
in
|
2018-02-07 11:38:21 +00:00
|
|
|
let merged_options =
|
|
|
|
let root =
|
|
|
|
Arg.(value
|
|
|
|
& opt (some dir) None
|
|
|
|
& info ["root"] ~docs ~docv:"DIR"
|
|
|
|
~doc:{|Use this directory as workspace root instead of guessing it.
|
|
|
|
Note that this option doesn't change the interpretation of
|
|
|
|
targets given on the command line. It is only intended
|
|
|
|
for scripts.|})
|
|
|
|
in
|
|
|
|
let ignore_promoted_rules =
|
|
|
|
Arg.(value
|
|
|
|
& flag
|
|
|
|
& info ["ignore-promoted-rules"] ~docs
|
|
|
|
~doc:"Ignore rules with (mode promote)")
|
|
|
|
in
|
|
|
|
let config_file =
|
|
|
|
let config_file =
|
|
|
|
Arg.(value
|
|
|
|
& opt (some file) None
|
|
|
|
& info ["config-file"] ~docs ~docv:"FILE"
|
|
|
|
~doc:"Load this configuration file instead of the default one.")
|
2018-01-15 13:24:25 +00:00
|
|
|
in
|
2018-02-07 11:38:21 +00:00
|
|
|
let no_config =
|
|
|
|
Arg.(value
|
|
|
|
& flag
|
|
|
|
& info ["no-config"] ~docs
|
|
|
|
~doc:"Do not load the configuration file")
|
|
|
|
in
|
|
|
|
let merge config_file no_config =
|
|
|
|
match config_file, no_config with
|
|
|
|
| None , false -> `Ok (None , Default)
|
2018-04-24 20:25:27 +00:00
|
|
|
| Some fn, false -> `Ok (Some "--config-file", This (Path.of_string fn))
|
2018-02-07 11:38:21 +00:00
|
|
|
| None , true -> `Ok (Some "--no-config" , No_config)
|
|
|
|
| Some _ , true -> incompatible "--no-config" "--config-file"
|
|
|
|
in
|
|
|
|
Term.(ret (const merge $ config_file $ no_config))
|
|
|
|
in
|
|
|
|
let for_release = "for-release-of-packages" in
|
|
|
|
let frop =
|
|
|
|
Arg.(value
|
|
|
|
& opt (some string) None
|
|
|
|
& info ["p"; for_release] ~docs ~docv:"PACKAGES"
|
|
|
|
~doc:{|Shorthand for $(b,--root . --only-packages PACKAGE
|
2018-05-04 15:49:25 +00:00
|
|
|
--promote ignore --no-config --profile release).
|
2018-02-07 11:38:21 +00:00
|
|
|
You must use this option in your $(i,<package>.opam) files, in order
|
|
|
|
to build only what's necessary when your project contains multiple
|
|
|
|
packages as well as getting reproducible builds.|})
|
|
|
|
in
|
|
|
|
let merge root only_packages ignore_promoted_rules
|
2018-05-04 15:49:25 +00:00
|
|
|
(config_file_opt, config_file) profile release =
|
2018-02-07 11:38:21 +00:00
|
|
|
let fail opt = incompatible ("-p/--" ^ for_release) opt in
|
2018-05-04 15:49:25 +00:00
|
|
|
match release, root, only_packages, ignore_promoted_rules,
|
|
|
|
profile, config_file_opt with
|
|
|
|
| Some _, Some _, _, _, _, _ -> fail "--root"
|
|
|
|
| Some _, _, Some _, _, _, _ -> fail "--only-packages"
|
|
|
|
| Some _, _, _, true , _, _ -> fail "--ignore-promoted-rules"
|
|
|
|
| Some _, _, _, _, Some _, _ -> fail "--profile"
|
|
|
|
| Some _, _, _, _, _, Some s -> fail s
|
|
|
|
| Some pkgs, None, None, false, None, None ->
|
2018-01-15 13:24:25 +00:00
|
|
|
`Ok (Some ".",
|
|
|
|
Some pkgs,
|
2018-01-25 19:07:46 +00:00
|
|
|
true,
|
2018-02-07 11:38:21 +00:00
|
|
|
No_config,
|
2018-05-04 15:49:25 +00:00
|
|
|
Some "release",
|
2018-01-15 13:24:25 +00:00
|
|
|
["-p"; pkgs]
|
|
|
|
)
|
2018-05-04 15:49:25 +00:00
|
|
|
| None, _, _, _, _, _ ->
|
2018-01-15 13:24:25 +00:00
|
|
|
`Ok (root,
|
|
|
|
only_packages,
|
2018-01-25 19:07:46 +00:00
|
|
|
ignore_promoted_rules,
|
2018-02-07 11:38:21 +00:00
|
|
|
config_file,
|
2018-05-04 15:49:25 +00:00
|
|
|
profile,
|
2017-05-19 13:16:00 +00:00
|
|
|
List.concat
|
|
|
|
[ dump_opt "--root" root
|
|
|
|
; dump_opt "--only-packages" only_packages
|
2018-05-04 15:49:25 +00:00
|
|
|
; dump_opt "--profile" profile
|
2018-01-25 19:07:46 +00:00
|
|
|
; if ignore_promoted_rules then
|
|
|
|
["--ignore-promoted-rules"]
|
|
|
|
else
|
|
|
|
[]
|
2018-02-07 11:38:21 +00:00
|
|
|
; (match config_file with
|
2018-04-24 20:25:27 +00:00
|
|
|
| This fn -> ["--config-file"; Path.to_string fn]
|
2018-02-07 11:38:21 +00:00
|
|
|
| No_config -> ["--no-config"]
|
|
|
|
| Default -> [])
|
|
|
|
]
|
|
|
|
)
|
2017-04-04 15:56:14 +00:00
|
|
|
in
|
|
|
|
Term.(ret (const merge
|
|
|
|
$ root
|
|
|
|
$ only_packages
|
2018-01-25 19:07:46 +00:00
|
|
|
$ ignore_promoted_rules
|
2018-02-07 11:38:21 +00:00
|
|
|
$ config_file
|
2018-05-04 15:49:25 +00:00
|
|
|
$ profile
|
2017-04-04 15:56:14 +00:00
|
|
|
$ frop))
|
|
|
|
in
|
2017-12-21 11:54:00 +00:00
|
|
|
let x =
|
|
|
|
Arg.(value
|
|
|
|
& opt (some string) None
|
|
|
|
& info ["x"] ~docs
|
|
|
|
~doc:{|Cross-compile using this toolchain.|})
|
|
|
|
in
|
2018-01-15 13:24:25 +00:00
|
|
|
let diff_command =
|
|
|
|
Arg.(value
|
|
|
|
& opt (some string) None
|
|
|
|
& info ["diff-command"] ~docs
|
|
|
|
~doc:"Shell command to use to diff files")
|
|
|
|
in
|
2017-02-27 15:04:49 +00:00
|
|
|
Term.(const make
|
|
|
|
$ concurrency
|
|
|
|
$ ddep_path
|
|
|
|
$ dfindlib
|
2017-05-29 13:17:59 +00:00
|
|
|
$ dbacktraces
|
2017-05-29 09:57:04 +00:00
|
|
|
$ no_buffer
|
2017-02-27 15:04:49 +00:00
|
|
|
$ workspace_file
|
2018-01-15 13:24:25 +00:00
|
|
|
$ diff_command
|
2018-01-18 11:32:20 +00:00
|
|
|
$ auto_promote
|
2018-01-19 08:50:06 +00:00
|
|
|
$ force
|
2018-02-07 11:38:21 +00:00
|
|
|
$ merged_options
|
2017-12-21 11:54:00 +00:00
|
|
|
$ x
|
2018-02-07 11:38:21 +00:00
|
|
|
$ display
|
2017-02-27 15:04:49 +00:00
|
|
|
)
|
2017-02-21 15:09:58 +00:00
|
|
|
|
2017-02-24 16:29:08 +00:00
|
|
|
let installed_libraries =
|
|
|
|
let doc = "Print out libraries installed on the system." in
|
2017-05-17 13:54:50 +00:00
|
|
|
let go common na =
|
2017-05-19 13:16:00 +00:00
|
|
|
set_common common ~targets:[];
|
2018-03-29 15:58:41 +00:00
|
|
|
let env = Main.setup_env ~capture_outputs:common.capture_outputs in
|
2018-02-07 11:38:21 +00:00
|
|
|
Scheduler.go ~log:(Log.create common) ~common
|
2018-05-04 15:49:25 +00:00
|
|
|
(Context.create
|
|
|
|
(Default { targets = [Native]
|
|
|
|
; profile = "default" })
|
|
|
|
~env
|
|
|
|
>>= fun ctxs ->
|
2017-12-21 11:54:00 +00:00
|
|
|
let ctx = List.hd ctxs in
|
2017-02-28 06:01:27 +00:00
|
|
|
let findlib = ctx.findlib in
|
2017-05-17 13:54:50 +00:00
|
|
|
if na then begin
|
|
|
|
let pkgs = Findlib.all_unavailable_packages findlib in
|
2018-02-25 16:35:25 +00:00
|
|
|
let longest = String.longest_map pkgs ~f:fst in
|
2017-05-17 13:54:50 +00:00
|
|
|
let ppf = Format.std_formatter in
|
2018-02-20 11:46:10 +00:00
|
|
|
List.iter pkgs ~f:(fun (n, r) ->
|
|
|
|
Format.fprintf ppf "%-*s -> %a@\n" longest n
|
|
|
|
Findlib.Unavailable_reason.pp r);
|
2017-05-17 13:54:50 +00:00
|
|
|
Format.pp_print_flush ppf ();
|
2018-02-06 14:39:03 +00:00
|
|
|
Fiber.return ()
|
2017-05-17 13:54:50 +00:00
|
|
|
end else begin
|
|
|
|
let pkgs = Findlib.all_packages findlib in
|
2018-02-25 16:35:25 +00:00
|
|
|
let max_len = String.longest_map pkgs ~f:Findlib.Package.name in
|
2017-05-17 13:54:50 +00:00
|
|
|
List.iter pkgs ~f:(fun pkg ->
|
|
|
|
let ver =
|
2018-02-20 11:46:10 +00:00
|
|
|
Option.value (Findlib.Package.version pkg) ~default:"n/a"
|
2017-05-17 13:54:50 +00:00
|
|
|
in
|
2018-02-07 17:51:40 +00:00
|
|
|
Printf.printf "%-*s (version: %s)\n" max_len
|
|
|
|
(Findlib.Package.name pkg) ver);
|
2018-02-06 14:39:03 +00:00
|
|
|
Fiber.return ()
|
2017-05-17 13:54:50 +00:00
|
|
|
end)
|
2017-02-24 16:29:08 +00:00
|
|
|
in
|
|
|
|
( Term.(const go
|
2017-05-17 13:54:50 +00:00
|
|
|
$ common
|
|
|
|
$ Arg.(value
|
|
|
|
& flag
|
|
|
|
& info ["na"; "not-available"]
|
|
|
|
~doc:"List libraries that are not available and explain why"))
|
2017-02-24 16:29:08 +00:00
|
|
|
, Term.info "installed-libraries" ~doc
|
|
|
|
)
|
|
|
|
|
2017-02-24 15:41:52 +00:00
|
|
|
let resolve_package_install setup pkg =
|
|
|
|
match Main.package_install_file setup pkg with
|
|
|
|
| Ok path -> path
|
|
|
|
| Error () ->
|
2018-03-03 13:41:29 +00:00
|
|
|
let pkg = Package.Name.to_string pkg in
|
|
|
|
die "Unknown package %s!%s" pkg
|
|
|
|
(hint pkg
|
|
|
|
(Package.Name.Map.keys setup.packages
|
2018-05-02 11:56:12 +00:00
|
|
|
|> List.map ~f:Package.Name.to_string))
|
2017-02-24 15:41:52 +00:00
|
|
|
|
2017-04-25 15:22:17 +00:00
|
|
|
let target_hint (setup : Main.setup) path =
|
|
|
|
assert (Path.is_local path);
|
2018-05-08 16:56:58 +00:00
|
|
|
let sub_dir = Option.value ~default:path (Path.parent path) in
|
2017-04-25 15:22:17 +00:00
|
|
|
let candidates = Build_system.all_targets setup.build_system in
|
|
|
|
let candidates =
|
|
|
|
if Path.is_in_build_dir path then
|
|
|
|
candidates
|
|
|
|
else
|
|
|
|
List.map candidates ~f:(fun path ->
|
|
|
|
match Path.extract_build_context path with
|
|
|
|
| None -> path
|
|
|
|
| Some (_, path) -> path)
|
|
|
|
in
|
|
|
|
let candidates =
|
|
|
|
(* Only suggest hints for the basename, otherwise it's slow when there are lots of
|
|
|
|
files *)
|
|
|
|
List.filter_map candidates ~f:(fun path ->
|
2018-05-08 16:56:58 +00:00
|
|
|
if Path.parent_exn path = sub_dir then
|
2017-04-25 15:22:17 +00:00
|
|
|
Some (Path.to_string path)
|
|
|
|
else
|
|
|
|
None)
|
|
|
|
in
|
2018-04-23 05:08:09 +00:00
|
|
|
let candidates = String.Set.of_list candidates |> String.Set.to_list in
|
2017-04-25 15:22:17 +00:00
|
|
|
hint (Path.to_string path) candidates
|
|
|
|
|
2018-01-19 08:50:06 +00:00
|
|
|
let check_path contexts =
|
2018-02-25 16:35:25 +00:00
|
|
|
let contexts =
|
2018-04-23 05:08:09 +00:00
|
|
|
String.Set.of_list (List.map contexts ~f:(fun c -> c.Context.name))
|
2018-02-25 16:35:25 +00:00
|
|
|
in
|
2018-01-19 08:50:06 +00:00
|
|
|
fun path ->
|
|
|
|
let internal path =
|
2018-05-02 11:56:12 +00:00
|
|
|
die "This path is internal to dune: %s"
|
2018-02-25 16:35:25 +00:00
|
|
|
(Path.to_string_maybe_quoted path)
|
2018-01-19 08:50:06 +00:00
|
|
|
in
|
|
|
|
if Path.is_in_build_dir path then
|
|
|
|
match Path.extract_build_context path with
|
|
|
|
| None -> internal path
|
|
|
|
| Some (name, _) ->
|
|
|
|
if name = "" || name.[0] = '.' then internal path;
|
2018-04-23 05:08:09 +00:00
|
|
|
if not (name = "install" || String.Set.mem contexts name) then
|
2018-01-19 08:50:06 +00:00
|
|
|
die "%s refers to unknown build context: %s%s"
|
|
|
|
(Path.to_string_maybe_quoted path)
|
|
|
|
name
|
2018-04-23 05:08:09 +00:00
|
|
|
(hint name (String.Set.to_list contexts))
|
2018-01-19 08:50:06 +00:00
|
|
|
|
2017-03-30 16:36:58 +00:00
|
|
|
let resolve_targets ~log common (setup : Main.setup) user_targets =
|
2017-02-23 10:03:35 +00:00
|
|
|
match user_targets with
|
|
|
|
| [] -> []
|
|
|
|
| _ ->
|
2018-01-19 08:50:06 +00:00
|
|
|
let check_path = check_path setup.contexts in
|
2017-02-24 12:31:01 +00:00
|
|
|
let targets =
|
2017-12-11 10:23:07 +00:00
|
|
|
List.map user_targets ~f:(fun s ->
|
2018-01-19 08:50:06 +00:00
|
|
|
if String.is_prefix s ~prefix:"@" then begin
|
2017-02-24 12:31:01 +00:00
|
|
|
let s = String.sub s ~pos:1 ~len:(String.length s - 1) in
|
2017-02-28 07:32:15 +00:00
|
|
|
let path = Path.relative Path.root (prefix_target common s) in
|
2018-01-19 08:50:06 +00:00
|
|
|
check_path path;
|
2017-02-24 12:31:01 +00:00
|
|
|
if Path.is_root path then
|
2017-03-27 22:16:16 +00:00
|
|
|
die "@@ on the command line must be followed by a valid alias name"
|
2018-01-19 08:50:06 +00:00
|
|
|
else if not (Path.is_local path) then
|
|
|
|
die "@@ on the command line must be followed by a relative path"
|
2017-02-24 12:31:01 +00:00
|
|
|
else
|
2018-01-19 08:50:06 +00:00
|
|
|
Ok [Alias_rec path]
|
|
|
|
end else begin
|
2017-02-28 07:32:15 +00:00
|
|
|
let path = Path.relative Path.root (prefix_target common s) in
|
2018-01-19 08:50:06 +00:00
|
|
|
check_path path;
|
2017-02-25 14:01:08 +00:00
|
|
|
let can't_build path =
|
2017-12-11 10:23:07 +00:00
|
|
|
Error (path, target_hint setup path);
|
2017-02-25 14:01:08 +00:00
|
|
|
in
|
|
|
|
if not (Path.is_local path) then
|
2017-12-11 10:23:07 +00:00
|
|
|
Ok [File path]
|
2017-02-25 14:01:08 +00:00
|
|
|
else if Path.is_in_build_dir path then begin
|
|
|
|
if Build_system.is_target setup.build_system path then
|
2017-12-11 10:23:07 +00:00
|
|
|
Ok [File path]
|
2017-02-25 14:01:08 +00:00
|
|
|
else
|
|
|
|
can't_build path
|
|
|
|
end else
|
2017-02-25 02:14:32 +00:00
|
|
|
match
|
2018-01-19 08:50:06 +00:00
|
|
|
List.filter_map setup.contexts ~f:(fun ctx ->
|
|
|
|
let path = Path.append ctx.Context.build_dir path in
|
|
|
|
if Build_system.is_target setup.build_system path then
|
|
|
|
Some (File path)
|
|
|
|
else
|
|
|
|
None)
|
2017-02-25 02:14:32 +00:00
|
|
|
with
|
2017-02-25 14:01:08 +00:00
|
|
|
| [] -> can't_build path
|
2017-12-11 10:23:07 +00:00
|
|
|
| l -> Ok l
|
2018-01-19 08:50:06 +00:00
|
|
|
end
|
|
|
|
)
|
2017-02-23 10:03:35 +00:00
|
|
|
in
|
2018-02-07 11:38:21 +00:00
|
|
|
if common.config.display = Verbose then begin
|
2017-03-30 16:36:58 +00:00
|
|
|
Log.info log "Actual targets:";
|
2017-12-11 10:23:07 +00:00
|
|
|
let targets =
|
|
|
|
List.concat_map targets ~f:(function
|
|
|
|
| Ok targets -> targets
|
|
|
|
| Error _ -> []) in
|
2017-03-30 16:36:58 +00:00
|
|
|
List.iter targets ~f:(function
|
|
|
|
| File path ->
|
|
|
|
Log.info log @@ "- " ^ (Path.to_string path)
|
2018-01-19 08:50:06 +00:00
|
|
|
| Alias_rec path ->
|
2017-09-29 15:06:29 +00:00
|
|
|
Log.info log @@ "- recursive alias " ^
|
|
|
|
(Path.to_string_maybe_quoted path));
|
2017-03-30 16:36:58 +00:00
|
|
|
flush stdout;
|
|
|
|
end;
|
2017-09-29 15:06:29 +00:00
|
|
|
targets
|
2017-02-23 10:03:35 +00:00
|
|
|
|
2017-12-11 10:23:07 +00:00
|
|
|
let resolve_targets_exn ~log common setup user_targets =
|
|
|
|
resolve_targets ~log common setup user_targets
|
|
|
|
|> List.concat_map ~f:(function
|
|
|
|
| Error (path, hint) ->
|
|
|
|
die "Don't know how to build %a%s" Path.pp path hint
|
|
|
|
| Ok targets ->
|
|
|
|
targets)
|
|
|
|
|
2017-02-24 15:47:09 +00:00
|
|
|
let build_targets =
|
2017-03-30 16:43:36 +00:00
|
|
|
let doc = "Build the given targets, or all installable targets if none are given." in
|
2017-02-28 19:05:04 +00:00
|
|
|
let man =
|
|
|
|
[ `S "DESCRIPTION"
|
|
|
|
; `P {|Targets starting with a $(b,@) are interpreted as aliases.|}
|
|
|
|
; `Blocks help_secs
|
|
|
|
]
|
|
|
|
in
|
2017-02-23 10:23:15 +00:00
|
|
|
let name_ = Arg.info [] ~docv:"TARGET" in
|
2017-02-21 15:09:58 +00:00
|
|
|
let go common targets =
|
2017-05-19 13:16:00 +00:00
|
|
|
set_common common ~targets;
|
2018-02-07 11:38:21 +00:00
|
|
|
let log = Log.create common in
|
|
|
|
Scheduler.go ~log ~common
|
2017-03-10 12:32:27 +00:00
|
|
|
(Main.setup ~log common >>= fun setup ->
|
2017-12-11 10:23:07 +00:00
|
|
|
let targets = resolve_targets_exn ~log common setup targets in
|
2017-03-01 19:19:43 +00:00
|
|
|
do_build setup targets) in
|
2017-02-21 15:09:58 +00:00
|
|
|
( Term.(const go
|
|
|
|
$ common
|
2017-03-30 16:43:36 +00:00
|
|
|
$ Arg.(value & pos_all string ["@install"] name_))
|
2017-02-28 19:05:04 +00:00
|
|
|
, Term.info "build" ~doc ~man)
|
2017-02-21 15:09:58 +00:00
|
|
|
|
2017-02-23 13:17:25 +00:00
|
|
|
let runtest =
|
2017-02-24 16:29:08 +00:00
|
|
|
let doc = "Run tests." in
|
2017-02-28 19:05:04 +00:00
|
|
|
let man =
|
|
|
|
[ `S "DESCRIPTION"
|
|
|
|
; `P {|This is a short-hand for calling:|}
|
2018-05-02 11:56:12 +00:00
|
|
|
; `Pre {| dune build @runtest|}
|
2017-02-28 19:05:04 +00:00
|
|
|
; `Blocks help_secs
|
|
|
|
]
|
|
|
|
in
|
2017-02-23 13:17:25 +00:00
|
|
|
let name_ = Arg.info [] ~docv:"DIR" in
|
2018-01-19 08:50:06 +00:00
|
|
|
let go common dirs =
|
2017-05-19 13:16:00 +00:00
|
|
|
set_common common
|
|
|
|
~targets:(List.map dirs ~f:(function
|
|
|
|
| "" | "." -> "@runtest"
|
|
|
|
| dir when dir.[String.length dir - 1] = '/' -> sprintf "@%sruntest" dir
|
|
|
|
| dir -> sprintf "@%s/runtest" dir));
|
2018-02-07 11:38:21 +00:00
|
|
|
let log = Log.create common in
|
|
|
|
Scheduler.go ~log ~common
|
2018-01-19 08:50:06 +00:00
|
|
|
(Main.setup ~log common >>= fun setup ->
|
|
|
|
let check_path = check_path setup.contexts in
|
2017-02-23 13:17:25 +00:00
|
|
|
let targets =
|
|
|
|
List.map dirs ~f:(fun dir ->
|
2017-02-28 07:32:15 +00:00
|
|
|
let dir = Path.(relative root) (prefix_target common dir) in
|
2018-01-19 08:50:06 +00:00
|
|
|
check_path dir;
|
|
|
|
Alias_rec (Path.relative dir "runtest"))
|
2017-02-23 13:17:25 +00:00
|
|
|
in
|
2017-03-01 19:19:43 +00:00
|
|
|
do_build setup targets) in
|
2017-02-23 13:17:25 +00:00
|
|
|
( Term.(const go
|
|
|
|
$ common
|
2017-02-24 15:41:52 +00:00
|
|
|
$ Arg.(value & pos_all string ["."] name_))
|
2017-02-28 19:05:04 +00:00
|
|
|
, Term.info "runtest" ~doc ~man)
|
2017-02-23 13:17:25 +00:00
|
|
|
|
2017-05-26 10:32:32 +00:00
|
|
|
let clean =
|
|
|
|
let doc = "Clean the project." in
|
|
|
|
let man =
|
|
|
|
[ `S "DESCRIPTION"
|
2018-05-02 11:56:12 +00:00
|
|
|
; `P {|Removes files added by dune such as _build, <package>.install, and .merlin|}
|
2017-05-26 10:32:32 +00:00
|
|
|
; `Blocks help_secs
|
|
|
|
]
|
|
|
|
in
|
|
|
|
let go common =
|
|
|
|
begin
|
|
|
|
set_common common ~targets:[];
|
2018-01-19 08:50:06 +00:00
|
|
|
Build_system.files_in_source_tree_to_delete ()
|
|
|
|
|> List.iter ~f:Path.unlink_no_err;
|
2018-04-25 11:22:48 +00:00
|
|
|
Path.rm_rf Path.build_dir
|
2017-05-26 10:32:32 +00:00
|
|
|
end
|
|
|
|
in
|
|
|
|
( Term.(const go $ common)
|
|
|
|
, Term.info "clean" ~doc ~man)
|
|
|
|
|
2017-03-10 16:35:02 +00:00
|
|
|
let format_external_libs libs =
|
2018-04-23 05:43:20 +00:00
|
|
|
String.Map.to_list libs
|
2017-03-10 16:35:02 +00:00
|
|
|
|> List.map ~f:(fun (name, kind) ->
|
|
|
|
match (kind : Build.lib_dep_kind) with
|
|
|
|
| Optional -> sprintf "- %s (optional)" name
|
|
|
|
| Required -> sprintf "- %s" name)
|
|
|
|
|> String.concat ~sep:"\n"
|
|
|
|
|
2017-03-01 19:19:43 +00:00
|
|
|
let external_lib_deps =
|
|
|
|
let doc = "Print out external libraries needed to build the given targets." in
|
|
|
|
let man =
|
|
|
|
[ `S "DESCRIPTION"
|
|
|
|
; `P {|Print out the external libraries needed to build the given targets.|}
|
|
|
|
; `P {|The output of $(b,jbuild external-lib-deps @install) should be included
|
|
|
|
in what is written in your $(i,<package>.opam) file.|}
|
|
|
|
; `Blocks help_secs
|
|
|
|
]
|
|
|
|
in
|
|
|
|
let go common only_missing targets =
|
2017-05-19 13:16:00 +00:00
|
|
|
set_common common ~targets:[];
|
2018-02-07 11:38:21 +00:00
|
|
|
let log = Log.create common in
|
|
|
|
Scheduler.go ~log ~common
|
2018-03-31 01:52:18 +00:00
|
|
|
(Main.setup ~log common ~external_lib_deps_mode:true
|
2017-03-02 16:57:28 +00:00
|
|
|
>>= fun setup ->
|
2017-12-11 10:23:07 +00:00
|
|
|
let targets = resolve_targets_exn ~log common setup targets in
|
2017-09-29 15:06:29 +00:00
|
|
|
let request = request_of_targets setup targets in
|
2017-03-01 19:19:43 +00:00
|
|
|
let failure =
|
2018-04-23 05:43:20 +00:00
|
|
|
String.Map.foldi ~init:false
|
2017-09-29 15:06:29 +00:00
|
|
|
(Build_system.all_lib_deps_by_context setup.build_system ~request)
|
2018-02-25 16:35:25 +00:00
|
|
|
~f:(fun context_name lib_deps acc ->
|
2017-03-01 19:19:43 +00:00
|
|
|
let internals =
|
2018-05-04 15:49:25 +00:00
|
|
|
Super_context.internal_lib_names
|
|
|
|
(match String.Map.find setup.Main.scontexts context_name with
|
2017-03-01 19:19:43 +00:00
|
|
|
| None -> assert false
|
|
|
|
| Some x -> x)
|
|
|
|
in
|
|
|
|
let externals =
|
2018-04-23 05:43:20 +00:00
|
|
|
String.Map.filteri lib_deps ~f:(fun name _ ->
|
2018-04-23 05:08:09 +00:00
|
|
|
not (String.Set.mem internals name))
|
2017-03-01 19:19:43 +00:00
|
|
|
in
|
|
|
|
if only_missing then begin
|
|
|
|
let context =
|
2018-02-25 16:35:25 +00:00
|
|
|
match
|
|
|
|
List.find setup.contexts ~f:(fun c -> c.name = context_name)
|
|
|
|
with
|
2017-03-01 19:19:43 +00:00
|
|
|
| None -> assert false
|
|
|
|
| Some c -> c
|
|
|
|
in
|
|
|
|
let missing =
|
2018-04-23 05:43:20 +00:00
|
|
|
String.Map.filteri externals ~f:(fun name _ ->
|
2018-02-13 17:49:07 +00:00
|
|
|
not (Findlib.available context.findlib name))
|
2017-03-01 19:19:43 +00:00
|
|
|
in
|
2018-04-23 05:43:20 +00:00
|
|
|
if String.Map.is_empty missing then
|
2017-03-01 19:19:43 +00:00
|
|
|
acc
|
2018-04-23 05:43:20 +00:00
|
|
|
else if String.Map.for_alli missing
|
2018-02-25 16:35:25 +00:00
|
|
|
~f:(fun _ kind -> kind = Build.Optional)
|
2017-06-09 14:18:27 +00:00
|
|
|
then begin
|
2017-03-01 19:19:43 +00:00
|
|
|
Format.eprintf
|
2017-06-09 13:49:08 +00:00
|
|
|
"@{<error>Error@}: The following libraries are missing \
|
|
|
|
in the %s context:\n\
|
|
|
|
%s@."
|
|
|
|
context_name
|
|
|
|
(format_external_libs missing);
|
|
|
|
false
|
|
|
|
end else begin
|
|
|
|
Format.eprintf
|
|
|
|
"@{<error>Error@}: The following libraries are missing \
|
2017-03-01 19:19:43 +00:00
|
|
|
in the %s context:\n\
|
2017-05-18 13:31:31 +00:00
|
|
|
%s\n\
|
|
|
|
Hint: try: opam install %s@."
|
2017-03-01 19:19:43 +00:00
|
|
|
context_name
|
2017-05-18 13:31:31 +00:00
|
|
|
(format_external_libs missing)
|
2018-04-23 05:43:20 +00:00
|
|
|
(String.Map.to_list missing
|
2017-05-18 13:31:31 +00:00
|
|
|
|> List.filter_map ~f:(fun (name, kind) ->
|
|
|
|
match (kind : Build.lib_dep_kind) with
|
|
|
|
| Optional -> None
|
|
|
|
| Required -> Some (Findlib.root_package_name name))
|
2018-04-23 05:08:09 +00:00
|
|
|
|> String.Set.of_list
|
|
|
|
|> String.Set.to_list
|
2017-05-18 13:31:31 +00:00
|
|
|
|> String.concat ~sep:" ");
|
2017-03-01 19:19:43 +00:00
|
|
|
true
|
|
|
|
end
|
|
|
|
end else begin
|
|
|
|
Printf.printf
|
|
|
|
"These are the external library dependencies in the %s context:\n\
|
|
|
|
%s\n%!"
|
|
|
|
context_name
|
2017-03-10 16:35:02 +00:00
|
|
|
(format_external_libs externals);
|
2017-03-01 19:19:43 +00:00
|
|
|
acc
|
|
|
|
end)
|
|
|
|
in
|
2018-02-06 14:39:03 +00:00
|
|
|
if failure then raise Already_reported;
|
|
|
|
Fiber.return ())
|
2017-03-01 19:19:43 +00:00
|
|
|
in
|
|
|
|
( Term.(const go
|
|
|
|
$ common
|
|
|
|
$ Arg.(value
|
|
|
|
& flag
|
|
|
|
& info ["missing"]
|
|
|
|
~doc:{|Only print out missing dependencies|})
|
|
|
|
$ Arg.(non_empty
|
|
|
|
& pos_all string []
|
|
|
|
& Arg.info [] ~docv:"TARGET"))
|
|
|
|
, Term.info "external-lib-deps" ~doc ~man)
|
|
|
|
|
2017-05-18 23:16:48 +00:00
|
|
|
let rules =
|
|
|
|
let doc = "Dump internal rules." in
|
2017-05-18 18:05:01 +00:00
|
|
|
let man =
|
|
|
|
[ `S "DESCRIPTION"
|
2018-05-02 11:56:12 +00:00
|
|
|
; `P {|Dump Dune internal rules for the given targets.
|
2017-05-18 23:16:48 +00:00
|
|
|
If no targets are given, dump all the internal rules.|}
|
|
|
|
; `P {|By default the output is a list of S-expressions,
|
|
|
|
one S-expression per rule. Each S-expression is of the form:|}
|
|
|
|
; `Pre " ((deps (<dependencies>))\n\
|
|
|
|
\ (targets (<targets>))\n\
|
|
|
|
\ (context <context-name>)\n\
|
|
|
|
\ (action <action>))"
|
|
|
|
; `P {|$(b,<context-name>) is the context is which the action is executed.
|
|
|
|
It is omitted if the action is independant from the context.|}
|
|
|
|
; `P {|$(b,<action>) is the action following the same syntax as user actions,
|
|
|
|
as described in the manual.|}
|
2017-05-18 18:05:01 +00:00
|
|
|
; `Blocks help_secs
|
|
|
|
]
|
|
|
|
in
|
2017-05-18 23:16:48 +00:00
|
|
|
let go common out recursive makefile_syntax targets =
|
2018-04-24 20:25:27 +00:00
|
|
|
let out = Option.map ~f:Path.of_string out in
|
2017-05-19 13:16:00 +00:00
|
|
|
set_common common ~targets;
|
2018-02-07 11:38:21 +00:00
|
|
|
let log = Log.create common in
|
|
|
|
Scheduler.go ~log ~common
|
2018-03-31 01:52:18 +00:00
|
|
|
(Main.setup ~log common ~external_lib_deps_mode:true
|
2017-05-18 18:05:01 +00:00
|
|
|
>>= fun setup ->
|
2017-09-29 15:06:29 +00:00
|
|
|
let request =
|
2017-05-18 18:05:01 +00:00
|
|
|
match targets with
|
2017-09-29 15:06:29 +00:00
|
|
|
| [] -> Build.paths (Build_system.all_targets setup.build_system)
|
2017-12-11 10:23:07 +00:00
|
|
|
| _ -> resolve_targets_exn ~log common setup targets |> request_of_targets setup
|
2017-05-18 18:05:01 +00:00
|
|
|
in
|
2017-09-29 15:06:29 +00:00
|
|
|
Build_system.build_rules setup.build_system ~request ~recursive >>= fun rules ->
|
2018-02-07 15:37:12 +00:00
|
|
|
let sexp_of_action action =
|
|
|
|
Action.for_shell action |> Action.For_shell.sexp_of_t
|
|
|
|
in
|
2017-05-18 23:16:48 +00:00
|
|
|
let print oc =
|
2017-05-18 18:05:01 +00:00
|
|
|
let ppf = Format.formatter_of_out_channel oc in
|
2017-05-19 00:42:41 +00:00
|
|
|
Sexp.prepare_formatter ppf;
|
|
|
|
Format.pp_open_vbox ppf 0;
|
2017-05-18 23:16:48 +00:00
|
|
|
if makefile_syntax then begin
|
|
|
|
List.iter rules ~f:(fun (rule : Build_system.Rule.t) ->
|
2017-05-19 01:12:51 +00:00
|
|
|
Format.fprintf ppf "@[<hov 2>@{<makefile-stuff>%a:%t@}@]@,@<0>\t@{<makefile-action>%a@}@,@,"
|
|
|
|
(Format.pp_print_list ~pp_sep:Format.pp_print_space (fun ppf p ->
|
|
|
|
Format.pp_print_string ppf (Path.to_string p)))
|
2018-02-25 16:35:25 +00:00
|
|
|
(Path.Set.to_list rule.targets)
|
2017-05-19 01:12:51 +00:00
|
|
|
(fun ppf ->
|
|
|
|
Path.Set.iter rule.deps ~f:(fun dep ->
|
|
|
|
Format.fprintf ppf "@ %s" (Path.to_string dep)))
|
2018-02-07 15:37:12 +00:00
|
|
|
Sexp.pp_split_strings (sexp_of_action rule.action))
|
2017-05-18 23:16:48 +00:00
|
|
|
end else begin
|
|
|
|
List.iter rules ~f:(fun (rule : Build_system.Rule.t) ->
|
|
|
|
let sexp =
|
2018-02-25 16:35:25 +00:00
|
|
|
let paths ps =
|
|
|
|
Sexp.To_sexp.list Path.sexp_of_t (Path.Set.to_list ps)
|
|
|
|
in
|
2017-05-18 23:16:48 +00:00
|
|
|
Sexp.To_sexp.record (
|
|
|
|
List.concat
|
|
|
|
[ [ "deps" , paths rule.deps
|
|
|
|
; "targets", paths rule.targets ]
|
2017-05-27 23:48:48 +00:00
|
|
|
; (match rule.context with
|
2017-05-18 23:16:48 +00:00
|
|
|
| None -> []
|
2018-02-24 23:33:26 +00:00
|
|
|
| Some c -> ["context",
|
|
|
|
Sexp.atom_or_quoted_string c.name])
|
2018-02-07 15:37:12 +00:00
|
|
|
; [ "action" , sexp_of_action rule.action ]
|
2017-05-18 23:16:48 +00:00
|
|
|
])
|
|
|
|
in
|
2017-05-19 00:42:41 +00:00
|
|
|
Format.fprintf ppf "%a@," Sexp.pp_split_strings sexp)
|
2017-05-18 23:16:48 +00:00
|
|
|
end;
|
|
|
|
Format.pp_print_flush ppf ();
|
2018-02-06 14:39:03 +00:00
|
|
|
Fiber.return ()
|
2017-05-18 23:16:48 +00:00
|
|
|
in
|
|
|
|
match out with
|
|
|
|
| None -> print stdout
|
|
|
|
| Some fn -> Io.with_file_out fn ~f:print)
|
2017-05-18 18:05:01 +00:00
|
|
|
in
|
|
|
|
( Term.(const go
|
|
|
|
$ common
|
2017-05-18 23:16:48 +00:00
|
|
|
$ Arg.(value
|
2017-05-18 18:05:01 +00:00
|
|
|
& opt (some string) None
|
|
|
|
& info ["o"] ~docv:"FILE"
|
2017-05-18 23:16:48 +00:00
|
|
|
~doc:"Output to a file instead of stdout.")
|
|
|
|
$ Arg.(value
|
|
|
|
& flag
|
|
|
|
& info ["r"; "recursive"]
|
|
|
|
~doc:"Print all rules needed to build the transitive dependencies of the given targets.")
|
|
|
|
$ Arg.(value
|
|
|
|
& flag
|
|
|
|
& info ["m"; "makefile"]
|
|
|
|
~doc:"Output the rules in Makefile syntax.")
|
2017-05-18 18:05:01 +00:00
|
|
|
$ Arg.(value
|
|
|
|
& pos_all string []
|
|
|
|
& Arg.info [] ~docv:"TARGET"))
|
2017-05-18 23:16:48 +00:00
|
|
|
, Term.info "rules" ~doc ~man)
|
2017-05-18 18:05:01 +00:00
|
|
|
|
2017-02-25 01:45:41 +00:00
|
|
|
let opam_installer () =
|
|
|
|
match Bin.which "opam-installer" with
|
2017-02-24 15:41:52 +00:00
|
|
|
| None ->
|
|
|
|
die "\
|
|
|
|
Sorry, you need the opam-installer tool to be able to install or
|
|
|
|
uninstall packages.
|
|
|
|
|
|
|
|
I couldn't find the opam-installer binary :-("
|
|
|
|
| Some fn -> fn
|
|
|
|
|
2017-02-25 01:45:41 +00:00
|
|
|
let get_prefix context ~from_command_line =
|
2017-02-24 15:41:52 +00:00
|
|
|
match from_command_line with
|
2018-02-06 14:39:03 +00:00
|
|
|
| Some p -> Fiber.return (Path.of_string p)
|
2017-02-25 01:45:41 +00:00
|
|
|
| None -> Context.install_prefix context
|
2017-02-24 15:41:52 +00:00
|
|
|
|
2017-07-25 16:07:24 +00:00
|
|
|
let get_libdir context ~libdir_from_command_line =
|
2017-07-05 13:10:41 +00:00
|
|
|
match libdir_from_command_line with
|
2018-02-06 14:39:03 +00:00
|
|
|
| Some p -> Fiber.return (Some (Path.of_string p))
|
2017-07-25 16:07:24 +00:00
|
|
|
| None -> Context.install_ocaml_libdir context
|
2017-07-05 13:10:41 +00:00
|
|
|
|
2017-02-24 15:41:52 +00:00
|
|
|
let install_uninstall ~what =
|
2017-02-26 21:30:28 +00:00
|
|
|
let doc =
|
2018-02-25 16:35:25 +00:00
|
|
|
sprintf "%s packages using opam-installer." (String.capitalize what)
|
2017-02-26 21:30:28 +00:00
|
|
|
in
|
2017-02-24 15:41:52 +00:00
|
|
|
let name_ = Arg.info [] ~docv:"PACKAGE" in
|
2017-07-05 13:10:41 +00:00
|
|
|
let go common prefix_from_command_line libdir_from_command_line pkgs =
|
2017-05-19 13:16:00 +00:00
|
|
|
set_common common ~targets:[];
|
2017-02-25 01:45:41 +00:00
|
|
|
let opam_installer = opam_installer () in
|
2018-02-07 11:38:21 +00:00
|
|
|
let log = Log.create common in
|
|
|
|
Scheduler.go ~log ~common
|
2017-03-10 12:32:27 +00:00
|
|
|
(Main.setup ~log common >>= fun setup ->
|
2017-02-25 01:45:41 +00:00
|
|
|
let pkgs =
|
|
|
|
match pkgs with
|
2018-03-02 18:44:03 +00:00
|
|
|
| [] -> Package.Name.Map.keys setup.packages
|
2017-02-25 01:45:41 +00:00
|
|
|
| l -> l
|
|
|
|
in
|
2017-02-24 15:41:52 +00:00
|
|
|
let install_files, missing_install_files =
|
2017-02-25 14:15:52 +00:00
|
|
|
List.concat_map pkgs ~f:(fun pkg ->
|
2017-02-24 15:41:52 +00:00
|
|
|
let fn = resolve_package_install setup pkg in
|
2017-02-25 14:15:52 +00:00
|
|
|
List.map setup.contexts ~f:(fun ctx ->
|
|
|
|
let fn = Path.append ctx.Context.build_dir fn in
|
|
|
|
if Path.exists fn then
|
2018-02-25 16:35:25 +00:00
|
|
|
Left (ctx, fn)
|
2017-02-25 14:15:52 +00:00
|
|
|
else
|
2018-02-25 16:35:25 +00:00
|
|
|
Right fn))
|
2017-02-25 14:15:52 +00:00
|
|
|
|> List.partition_map ~f:(fun x -> x)
|
2017-02-24 15:41:52 +00:00
|
|
|
in
|
|
|
|
if missing_install_files <> [] then begin
|
2017-02-25 14:15:52 +00:00
|
|
|
die "The following <package>.install are missing:\n\
|
2017-02-24 15:41:52 +00:00
|
|
|
%s\n\
|
2018-05-02 11:56:12 +00:00
|
|
|
You need to run: dune build @install"
|
2017-02-24 15:41:52 +00:00
|
|
|
(String.concat ~sep:"\n"
|
2017-02-25 14:15:52 +00:00
|
|
|
(List.map missing_install_files
|
|
|
|
~f:(fun p -> sprintf "- %s" (Path.to_string p))))
|
2017-02-24 15:41:52 +00:00
|
|
|
end;
|
2018-02-25 16:35:25 +00:00
|
|
|
(match
|
|
|
|
setup.contexts, prefix_from_command_line, libdir_from_command_line
|
|
|
|
with
|
2017-07-05 13:10:41 +00:00
|
|
|
| _ :: _ :: _, Some _, _ | _ :: _ :: _, _, Some _ ->
|
2017-07-25 16:07:24 +00:00
|
|
|
die "Cannot specify --prefix or --libdir when installing \
|
|
|
|
into multiple contexts!"
|
2017-02-25 01:45:41 +00:00
|
|
|
| _ -> ());
|
2017-02-25 14:15:52 +00:00
|
|
|
let module CMap = Map.Make(Context) in
|
2017-07-25 16:07:24 +00:00
|
|
|
let install_files_by_context =
|
2018-02-25 16:35:25 +00:00
|
|
|
CMap.of_list_multi install_files |> CMap.to_list
|
2017-07-25 16:07:24 +00:00
|
|
|
in
|
2018-02-06 14:39:03 +00:00
|
|
|
Fiber.parallel_iter install_files_by_context
|
|
|
|
~f:(fun (context, install_files) ->
|
2018-05-23 16:47:56 +00:00
|
|
|
let install_files_set = Path.Set.of_list install_files in
|
2018-02-06 14:39:03 +00:00
|
|
|
get_prefix context ~from_command_line:prefix_from_command_line
|
|
|
|
>>= fun prefix ->
|
|
|
|
get_libdir context ~libdir_from_command_line
|
|
|
|
>>= fun libdir ->
|
|
|
|
Fiber.parallel_iter install_files ~f:(fun path ->
|
2018-05-23 16:47:56 +00:00
|
|
|
let purpose = Process.Build_job install_files_set in
|
2018-04-25 09:30:18 +00:00
|
|
|
Process.run ~purpose ~env:setup.env Strict opam_installer
|
2018-02-06 14:39:03 +00:00
|
|
|
([ sprintf "-%c" what.[0]
|
|
|
|
; Path.to_string path
|
|
|
|
; "--prefix"
|
|
|
|
; Path.to_string prefix
|
|
|
|
] @
|
|
|
|
match libdir with
|
|
|
|
| None -> []
|
|
|
|
| Some p -> [ "--libdir"; Path.to_string p ]
|
|
|
|
))))
|
2017-02-24 15:41:52 +00:00
|
|
|
in
|
|
|
|
( Term.(const go
|
|
|
|
$ common
|
2017-07-27 11:06:06 +00:00
|
|
|
$ Arg.(value
|
|
|
|
& opt (some dir) None
|
|
|
|
& info ["destdir"; "prefix"]
|
|
|
|
~docv:"PREFIX"
|
|
|
|
~doc:"Directory where files are copied. For instance binaries \
|
|
|
|
are copied into $(i,\\$prefix/bin), library files into \
|
2017-07-27 11:08:19 +00:00
|
|
|
$(i,\\$prefix/lib), etc... It defaults to the current opam \
|
2017-07-27 11:06:06 +00:00
|
|
|
prefix if opam is available and configured, otherwise it uses \
|
|
|
|
the same prefix as the ocaml compiler.")
|
|
|
|
$ Arg.(value
|
|
|
|
& opt (some dir) None
|
|
|
|
& info ["libdir"]
|
|
|
|
~docv:"PATH"
|
|
|
|
~doc:"Directory where library files are copied, relative to \
|
|
|
|
$(b,prefix) or absolute. If $(b,--prefix) \
|
|
|
|
is specified the default is $(i,\\$prefix/lib), otherwise \
|
|
|
|
it is the output of $(b,ocamlfind printconf destdir)"
|
|
|
|
)
|
2018-03-02 18:44:03 +00:00
|
|
|
$ Arg.(value & pos_all package_name [] name_))
|
2017-02-24 15:41:52 +00:00
|
|
|
, Term.info what ~doc ~man:help_secs)
|
|
|
|
|
|
|
|
let install = install_uninstall ~what:"install"
|
|
|
|
let uninstall = install_uninstall ~what:"uninstall"
|
|
|
|
|
2017-08-04 07:59:35 +00:00
|
|
|
let context_arg ~doc =
|
|
|
|
Arg.(value
|
|
|
|
& opt string "default"
|
|
|
|
& info ["context"] ~docv:"CONTEXT" ~doc)
|
|
|
|
|
2017-03-01 12:09:57 +00:00
|
|
|
let exec =
|
|
|
|
let doc =
|
|
|
|
"Execute a command in a similar environment as if installation was performed."
|
|
|
|
in
|
|
|
|
let man =
|
|
|
|
[ `S "DESCRIPTION"
|
2018-05-02 11:56:12 +00:00
|
|
|
; `P {|$(b,dune exec -- COMMAND) should behave in the same way as if you
|
2017-11-07 13:41:09 +00:00
|
|
|
do:|}
|
2018-05-02 11:56:12 +00:00
|
|
|
; `Pre " \\$ dune install\n\
|
2017-03-01 12:09:57 +00:00
|
|
|
\ \\$ COMMAND"
|
2018-05-02 11:56:12 +00:00
|
|
|
; `P {|In particular if you run $(b,dune exec ocaml), you will have
|
2017-11-07 13:41:09 +00:00
|
|
|
access to the libraries defined in the workspace using your usual
|
|
|
|
directives ($(b,#require) for instance)|}
|
|
|
|
; `P {|When a leading / is present in the command (absolute path), then the
|
|
|
|
path is interpreted as an absolute path|}
|
|
|
|
; `P {|When a / is present at any other position (relative path), then the
|
|
|
|
path is interpreted as relative to the build context + current
|
|
|
|
working directory (or the value of $(b,--root) when ran outside of
|
|
|
|
the project root)|}
|
2017-03-01 12:09:57 +00:00
|
|
|
; `Blocks help_secs
|
|
|
|
]
|
|
|
|
in
|
2017-12-11 10:23:07 +00:00
|
|
|
let go common context prog no_rebuild args =
|
2017-05-19 13:16:00 +00:00
|
|
|
set_common common ~targets:[];
|
2018-02-07 11:38:21 +00:00
|
|
|
let log = Log.create common in
|
|
|
|
let setup = Scheduler.go ~log ~common (Main.setup ~log common) in
|
2017-08-04 07:59:35 +00:00
|
|
|
let context = Main.find_context_exn setup ~name:context in
|
2017-12-11 10:23:07 +00:00
|
|
|
let prog_where =
|
|
|
|
match Filename.analyze_program_name prog with
|
|
|
|
| Absolute ->
|
|
|
|
`This_abs (Path.of_string prog)
|
|
|
|
| In_path ->
|
|
|
|
`Search prog
|
|
|
|
| Relative_to_current_dir ->
|
|
|
|
let prog = prefix_target common prog in
|
|
|
|
`This_rel (Path.relative context.build_dir prog) in
|
|
|
|
let targets = lazy (
|
|
|
|
(match prog_where with
|
|
|
|
| `Search p ->
|
|
|
|
[Path.relative (Config.local_install_bin_dir ~context:context.name) p]
|
|
|
|
| `This_rel p when Sys.win32 ->
|
|
|
|
[p; Path.extend_basename p ~suffix:Bin.exe]
|
|
|
|
| `This_rel p ->
|
|
|
|
[p]
|
|
|
|
| `This_abs p when Path.is_in_build_dir p ->
|
|
|
|
[p]
|
|
|
|
| `This_abs _ ->
|
|
|
|
[])
|
|
|
|
|> List.map ~f:Path.to_string
|
|
|
|
|> resolve_targets ~log common setup
|
|
|
|
|> List.concat_map ~f:(function
|
|
|
|
| Ok targets -> targets
|
|
|
|
| Error _ -> [])
|
|
|
|
) in
|
2017-11-14 07:19:58 +00:00
|
|
|
let real_prog =
|
2017-12-11 10:23:07 +00:00
|
|
|
if not no_rebuild then begin
|
|
|
|
match Lazy.force targets with
|
|
|
|
| [] -> ()
|
|
|
|
| targets ->
|
2018-02-07 11:38:21 +00:00
|
|
|
Scheduler.go ~log ~common (do_build setup targets);
|
2018-01-19 08:50:06 +00:00
|
|
|
Build_system.finalize setup.build_system
|
2017-12-11 10:23:07 +00:00
|
|
|
end;
|
|
|
|
match prog_where with
|
2017-11-14 07:19:58 +00:00
|
|
|
| `Search prog ->
|
|
|
|
let path = Config.local_install_bin_dir ~context:context.name :: context.path in
|
|
|
|
Bin.which prog ~path
|
2017-12-11 10:23:07 +00:00
|
|
|
| `This_rel prog
|
|
|
|
| `This_abs prog ->
|
2017-11-14 07:19:58 +00:00
|
|
|
if Path.exists prog then
|
|
|
|
Some prog
|
|
|
|
else if not Sys.win32 then
|
|
|
|
None
|
|
|
|
else
|
|
|
|
let prog = Path.extend_basename prog ~suffix:Bin.exe in
|
|
|
|
Option.some_if (Path.exists prog) prog
|
|
|
|
in
|
2017-12-11 10:23:07 +00:00
|
|
|
match real_prog, no_rebuild with
|
|
|
|
| None, true ->
|
|
|
|
begin match Lazy.force targets with
|
|
|
|
| [] ->
|
|
|
|
Format.eprintf "@{<Error>Error@}: Program %S not found!@." prog;
|
2018-02-06 14:39:03 +00:00
|
|
|
raise Already_reported
|
2017-12-11 10:23:07 +00:00
|
|
|
| _::_ ->
|
|
|
|
Format.eprintf "@{<Error>Error@}: Program %S isn't built yet \
|
|
|
|
you need to buid it first or remove the \
|
|
|
|
--no-build option.@." prog;
|
2018-02-06 14:39:03 +00:00
|
|
|
raise Already_reported
|
2017-12-11 10:23:07 +00:00
|
|
|
end
|
|
|
|
| None, false ->
|
2017-05-31 13:40:58 +00:00
|
|
|
Format.eprintf "@{<Error>Error@}: Program %S not found!@." prog;
|
2018-02-06 14:39:03 +00:00
|
|
|
raise Already_reported
|
2017-12-11 10:23:07 +00:00
|
|
|
| Some real_prog, _ ->
|
2017-05-31 13:40:58 +00:00
|
|
|
let real_prog = Path.to_string real_prog in
|
|
|
|
let argv = Array.of_list (prog :: args) in
|
2018-03-15 16:36:55 +00:00
|
|
|
restore_cwd_and_execve common real_prog argv context.env
|
2017-03-01 12:09:57 +00:00
|
|
|
in
|
|
|
|
( Term.(const go
|
|
|
|
$ common
|
2017-08-04 07:59:35 +00:00
|
|
|
$ context_arg ~doc:{|Run the command in this build context.|}
|
2017-03-01 12:09:57 +00:00
|
|
|
$ Arg.(required
|
|
|
|
& pos 0 (some string) None (Arg.info [] ~docv:"PROG"))
|
2017-12-11 10:23:07 +00:00
|
|
|
$ Arg.(value & flag
|
|
|
|
& info ["no-build"]
|
|
|
|
~doc:"don't rebuild target before executing")
|
2017-03-01 12:09:57 +00:00
|
|
|
$ Arg.(value
|
|
|
|
& pos_right 0 string [] (Arg.info [] ~docv:"ARGS"))
|
|
|
|
)
|
|
|
|
, Term.info "exec" ~doc ~man)
|
|
|
|
|
2017-05-07 19:42:22 +00:00
|
|
|
let subst =
|
|
|
|
let doc =
|
|
|
|
"Substitute watermarks in source files."
|
|
|
|
in
|
|
|
|
let man =
|
2017-06-01 15:34:04 +00:00
|
|
|
let var name desc =
|
|
|
|
`Blocks [`Noblank; `P ("- $(b,%%" ^ name ^ "%%), " ^ desc) ]
|
|
|
|
in
|
|
|
|
let opam field =
|
2018-02-25 16:35:25 +00:00
|
|
|
var ("PKG_" ^ String.uppercase field)
|
2017-06-01 15:34:04 +00:00
|
|
|
("contents of the $(b," ^ field ^ ":) field from the opam file")
|
|
|
|
in
|
2017-05-07 19:42:22 +00:00
|
|
|
[ `S "DESCRIPTION"
|
2017-06-01 15:34:04 +00:00
|
|
|
; `P {|Substitute $(b,%%ID%%) strings in source files, in a similar fashion to
|
2017-05-07 19:42:22 +00:00
|
|
|
what topkg does in the default configuration.|}
|
2017-06-01 15:34:04 +00:00
|
|
|
; `P {|This command is only meant to be called when a user pins a package to
|
|
|
|
its development version. Especially it replaces $(b,%%VERSION%%) strings
|
|
|
|
by the version obtained from the vcs. Currently only git is supported and
|
|
|
|
the version is obtained from the output of:|}
|
|
|
|
; `Pre {| \$ git describe --always --dirty|}
|
2018-05-02 11:56:12 +00:00
|
|
|
; `P {|$(b,dune subst) substitutes the variables that topkg substitutes with
|
2017-06-01 15:34:04 +00:00
|
|
|
the defatult configuration:|}
|
|
|
|
; var "NAME" "the name of the package"
|
|
|
|
; var "VERSION" "output of $(b,git describe --always --dirty)"
|
|
|
|
; var "VERSION_NUM" "same as $(b,%%VERSION%%) but with a potential leading \
|
|
|
|
'v' or 'V' dropped"
|
|
|
|
; var "VCS_COMMIT_ID" "commit hash from the vcs"
|
|
|
|
; opam "maintainer"
|
|
|
|
; opam "authors"
|
|
|
|
; opam "homepage"
|
|
|
|
; opam "issues"
|
|
|
|
; opam "doc"
|
|
|
|
; opam "license"
|
|
|
|
; opam "repo"
|
|
|
|
; `P {|It is not possible to customize this list. If you wish to do so you need to
|
|
|
|
configure topkg instead and use it to perform the substitution.|}
|
|
|
|
; `P {|Note that the expansion of $(b,%%NAME%%) is guessed using the following
|
|
|
|
heuristic: if all the $(b,<package>.opam) files in the current directory are
|
|
|
|
prefixed by the shortest package name, this prefix is used. Otherwise you must
|
|
|
|
specify a name with the $(b,-n) command line option.|}
|
2018-05-02 11:56:12 +00:00
|
|
|
; `P {|In order to call $(b,dune subst) when your package is pinned, add this line
|
2017-06-01 15:34:04 +00:00
|
|
|
to the $(b,build:) field of your opam file:|}
|
2018-05-02 11:56:12 +00:00
|
|
|
; `Pre {| [dune "subst"] {pinned}|}
|
2017-05-07 19:42:22 +00:00
|
|
|
; `Blocks help_secs
|
|
|
|
]
|
|
|
|
in
|
2017-05-08 15:53:12 +00:00
|
|
|
let go common name =
|
2017-05-19 13:16:00 +00:00
|
|
|
set_common common ~targets:[];
|
2018-02-07 11:38:21 +00:00
|
|
|
Scheduler.go ~common (Watermarks.subst ?name ())
|
2017-05-07 19:42:22 +00:00
|
|
|
in
|
|
|
|
( Term.(const go
|
|
|
|
$ common
|
2017-05-08 15:53:12 +00:00
|
|
|
$ Arg.(value
|
|
|
|
& opt (some string) None
|
|
|
|
& info ["n"; "name"] ~docv:"NAME"
|
|
|
|
~doc:"Use this package name instead of detecting it.")
|
2017-05-07 19:42:22 +00:00
|
|
|
)
|
|
|
|
, Term.info "subst" ~doc ~man)
|
2017-03-01 12:09:57 +00:00
|
|
|
|
2017-08-04 07:59:35 +00:00
|
|
|
let utop =
|
|
|
|
let doc = "Load library in utop" in
|
|
|
|
let man =
|
|
|
|
[ `S "DESCRIPTION"
|
2018-05-02 11:56:12 +00:00
|
|
|
; `P {|$(b,dune utop DIR) build and run utop toplevel with libraries defined in DIR|}
|
2017-08-04 07:59:35 +00:00
|
|
|
; `Blocks help_secs
|
|
|
|
] in
|
|
|
|
let go common dir ctx_name args =
|
|
|
|
let utop_target = dir |> Path.of_string |> Utop.utop_exe |> Path.to_string in
|
|
|
|
set_common common ~targets:[utop_target];
|
2018-02-07 11:38:21 +00:00
|
|
|
let log = Log.create common in
|
2017-08-04 07:59:35 +00:00
|
|
|
let (build_system, context, utop_path) =
|
|
|
|
(Main.setup ~log common >>= fun setup ->
|
|
|
|
let context = Main.find_context_exn setup ~name:ctx_name in
|
|
|
|
let setup = { setup with contexts = [context] } in
|
|
|
|
let target =
|
2017-12-11 10:23:07 +00:00
|
|
|
match resolve_targets_exn ~log common setup [utop_target] with
|
2017-08-04 07:59:35 +00:00
|
|
|
| [] -> die "no libraries defined in %s" dir
|
2017-09-29 15:06:29 +00:00
|
|
|
| [File target] -> target
|
|
|
|
| [Alias_rec _] | _::_::_ -> assert false
|
2017-08-04 07:59:35 +00:00
|
|
|
in
|
2017-09-29 15:06:29 +00:00
|
|
|
do_build setup [File target] >>| fun () ->
|
2017-08-04 07:59:35 +00:00
|
|
|
(setup.build_system, context, Path.to_string target)
|
2018-02-07 11:38:21 +00:00
|
|
|
) |> Scheduler.go ~log ~common in
|
2018-01-19 08:50:06 +00:00
|
|
|
Build_system.finalize build_system;
|
2017-11-12 19:55:49 +00:00
|
|
|
restore_cwd_and_execve common utop_path (Array.of_list (utop_path :: args))
|
2018-03-15 16:36:55 +00:00
|
|
|
context.env
|
2017-08-04 07:59:35 +00:00
|
|
|
in
|
|
|
|
let name_ = Arg.info [] ~docv:"PATH" in
|
|
|
|
( Term.(const go
|
|
|
|
$ common
|
|
|
|
$ Arg.(value & pos 0 dir "" name_)
|
|
|
|
$ context_arg ~doc:{|Select context where to build/run utop.|}
|
|
|
|
$ Arg.(value & pos_right 0 string [] (Arg.info [] ~docv:"ARGS")))
|
|
|
|
, Term.info "utop" ~doc ~man )
|
|
|
|
|
2018-01-18 11:32:20 +00:00
|
|
|
let promote =
|
|
|
|
let doc = "Promote files from the last run" in
|
|
|
|
let man =
|
|
|
|
[ `S "DESCRIPTION"
|
|
|
|
; `P {|Considering all actions of the form $(b,(diff a b)) that failed
|
2018-05-02 11:56:12 +00:00
|
|
|
in the last run of dune, $(b,dune promote) does the following:
|
2018-01-18 11:32:20 +00:00
|
|
|
|
|
|
|
If $(b,a) is present in the source tree but $(b,b) isn't, $(b,b) is
|
|
|
|
copied over to $(b,a) in the source tree. The idea behind this is that
|
|
|
|
you might use $(b,(diff file.expected file.generated)) and then call
|
2018-05-02 11:56:12 +00:00
|
|
|
$(b,dune promote) to promote the generated file.
|
2018-01-18 11:32:20 +00:00
|
|
|
|}
|
|
|
|
; `Blocks help_secs
|
|
|
|
] in
|
|
|
|
let go common =
|
|
|
|
set_common common ~targets:[];
|
2018-01-29 11:26:11 +00:00
|
|
|
(* We load and restore the digest cache as we need to clear the
|
|
|
|
cache for promoted files, due to issues on OSX. *)
|
|
|
|
Utils.Cached_digest.load ();
|
|
|
|
Action.Promotion.promote_files_registered_in_last_run ();
|
|
|
|
Utils.Cached_digest.dump ()
|
2018-01-18 11:32:20 +00:00
|
|
|
in
|
|
|
|
( Term.(const go
|
|
|
|
$ common)
|
|
|
|
, Term.info "promote" ~doc ~man )
|
|
|
|
|
2018-05-04 15:49:25 +00:00
|
|
|
let printenv =
|
|
|
|
let doc = "Print the environment of a directory" in
|
|
|
|
let man =
|
|
|
|
[ `S "DESCRIPTION"
|
|
|
|
; `P {|$(b,dune printenv DIR) prints the environment of a directory|}
|
|
|
|
; `Blocks help_secs
|
|
|
|
] in
|
|
|
|
let go common dir =
|
|
|
|
set_common common ~targets:[];
|
|
|
|
let log = Log.create common in
|
|
|
|
Scheduler.go ~log ~common (
|
|
|
|
Main.setup ~log common >>= fun setup ->
|
|
|
|
let dir = Path.of_string dir in
|
|
|
|
check_path setup.contexts dir;
|
|
|
|
let request =
|
|
|
|
let dump sctx ~dir =
|
|
|
|
let open Build.O in
|
|
|
|
Super_context.dump_env sctx ~dir
|
|
|
|
>>^ fun env ->
|
|
|
|
((Super_context.context sctx).name, env)
|
|
|
|
in
|
|
|
|
Build.all (
|
|
|
|
match Path.extract_build_context dir with
|
|
|
|
| Some (ctx, _) ->
|
|
|
|
let sctx =
|
|
|
|
String_map.find setup.scontexts ctx |> Option.value_exn
|
|
|
|
in
|
|
|
|
[dump sctx ~dir]
|
|
|
|
| None ->
|
|
|
|
String_map.values setup.scontexts
|
|
|
|
|> List.map ~f:(fun sctx ->
|
|
|
|
let dir =
|
|
|
|
Path.append (Super_context.context sctx).build_dir dir
|
|
|
|
in
|
|
|
|
dump sctx ~dir)
|
|
|
|
)
|
|
|
|
in
|
|
|
|
Build_system.do_build setup.build_system ~request
|
|
|
|
>>| fun l ->
|
|
|
|
let pp ppf = Format.fprintf ppf "@[<v1>(@,@[<v>%a@]@]@,)" (Format.pp_print_list Sexp.pp) in
|
|
|
|
match l with
|
|
|
|
| [(_, env)] ->
|
|
|
|
Format.printf "%a@." pp env
|
|
|
|
| l ->
|
|
|
|
List.iter l ~f:(fun (name, env) ->
|
|
|
|
Format.printf "@[<v2>Environment for context %s:@,%a@]@." name pp env)
|
|
|
|
)
|
|
|
|
in
|
|
|
|
( Term.(const go
|
|
|
|
$ common
|
|
|
|
$ Arg.(value & pos 0 dir "" & info [] ~docv:"PATH"))
|
|
|
|
, Term.info "printenv" ~doc ~man )
|
|
|
|
|
2018-02-07 11:38:21 +00:00
|
|
|
module Help = struct
|
|
|
|
let config =
|
2018-05-02 11:56:12 +00:00
|
|
|
("dune-config", 5, "", "Dune", "Dune manual"),
|
2018-02-07 11:38:21 +00:00
|
|
|
[ `S Manpage.s_synopsis
|
|
|
|
; `Pre "~/.config/dune/config"
|
|
|
|
; `S Manpage.s_description
|
2018-05-02 11:56:12 +00:00
|
|
|
; `P {|Unless $(b,--no-config) or $(b,-p) is passed, Dune will read a
|
2018-02-07 11:38:21 +00:00
|
|
|
configuration file from the user home directory. This file is used
|
2018-05-02 11:56:12 +00:00
|
|
|
to control various aspects of the behavior of Dune.|}
|
2018-02-07 11:38:21 +00:00
|
|
|
; `P {|The configuration file is normally $(b,~/.config/dune/config) on
|
|
|
|
Unix systems and $(b,Local Settings/dune/config) in the User home
|
|
|
|
directory on Windows. However, it is possible to specify an
|
|
|
|
alternative configuration file with the $(b,--config-file) option.|}
|
|
|
|
; `P {|This file must be written in S-expression syntax and be composed of
|
|
|
|
a list of stanzas. The following sections describe the stanzas available.|}
|
|
|
|
; `S "DISPLAY MODES"
|
|
|
|
; `P {|Syntax: $(b,\(display MODE\))|}
|
2018-05-02 11:56:12 +00:00
|
|
|
; `P {|This stanza controls how Dune reports what it is doing to the user.
|
2018-02-07 11:38:21 +00:00
|
|
|
This parameter can also be set from the command line via $(b,--display MODE).
|
|
|
|
The following display modes are available:|}
|
|
|
|
; `Blocks
|
|
|
|
(List.map ~f:(fun (x, desc) -> `I (sprintf "$(b,%s)" x, desc))
|
|
|
|
[ "progress",
|
2018-05-02 11:56:12 +00:00
|
|
|
{|This is the default, Dune shows and update a
|
2018-02-07 11:38:21 +00:00
|
|
|
status line as build goals are being completed.|}
|
|
|
|
; "quiet",
|
|
|
|
{|Only display errors.|}
|
|
|
|
; "short",
|
|
|
|
{|Print one line per command being executed, with the
|
|
|
|
binary name on the left and the reason it is being executed for
|
|
|
|
on the right.|}
|
|
|
|
; "verbose",
|
|
|
|
{|Print the full command lines of programs being
|
2018-05-02 11:56:12 +00:00
|
|
|
executed by Dune, with some colors to help differentiate
|
2018-02-07 11:38:21 +00:00
|
|
|
programs.|}
|
|
|
|
])
|
2018-02-15 14:04:00 +00:00
|
|
|
; `P {|Note that when the selected display mode is $(b,progress) and the
|
|
|
|
output is not a terminal then the $(b,quiet) mode is selected
|
2018-05-02 11:56:12 +00:00
|
|
|
instead. This rule doesn't apply when running Dune inside Emacs.
|
|
|
|
Dune detects whether it is executed from inside Emacs or not by
|
2018-02-15 14:04:00 +00:00
|
|
|
looking at the environment variable $(b,INSIDE_EMACS) that is set by
|
|
|
|
Emacs. If you want the same behavior with another editor, you can set
|
|
|
|
this variable. If your editor already sets another variable,
|
|
|
|
please open a ticket on the ocaml/dune github project so that we can
|
|
|
|
add support for it.|}
|
2018-02-07 12:09:24 +00:00
|
|
|
; `S "JOBS"
|
|
|
|
; `P {|Syntax: $(b,\(jobs NUMBER\))|}
|
2018-05-02 11:56:12 +00:00
|
|
|
; `P {|Set the maximum number of jobs Dune might run in parallel.
|
2018-02-07 12:09:24 +00:00
|
|
|
This can also be set from the command line via $(b,-j NUMBER).|}
|
|
|
|
; `P {|The default for this value is 4.|}
|
2018-02-07 11:38:21 +00:00
|
|
|
; common_footer
|
|
|
|
]
|
|
|
|
|
|
|
|
type what =
|
|
|
|
| Man of Manpage.t
|
|
|
|
| List_topics
|
|
|
|
|
|
|
|
let commands =
|
|
|
|
[ "config", Man config
|
|
|
|
; "topics", List_topics
|
|
|
|
]
|
|
|
|
|
|
|
|
let help =
|
2018-05-02 11:56:12 +00:00
|
|
|
let doc = "Additional Dune help" in
|
2018-02-07 11:38:21 +00:00
|
|
|
let man =
|
|
|
|
[ `S "DESCRIPTION"
|
2018-05-02 11:56:12 +00:00
|
|
|
; `P {|$(b,dune help TOPIC) provides additional help on the given topic.
|
2018-02-07 11:38:21 +00:00
|
|
|
The following topics are available:|}
|
|
|
|
; `Blocks (List.concat_map commands ~f:(fun (s, what) ->
|
|
|
|
match what with
|
|
|
|
| List_topics -> []
|
|
|
|
| Man ((title, _, _, _, _), _) -> [`I (sprintf "$(b,%s)" s, title)]))
|
|
|
|
; common_footer
|
|
|
|
]
|
|
|
|
in
|
|
|
|
let go man_format what =
|
|
|
|
match what with
|
|
|
|
| None ->
|
|
|
|
`Help (man_format, Some "help")
|
|
|
|
| Some (Man man_page) ->
|
|
|
|
Format.printf "%a@?" (Manpage.print man_format) man_page;
|
|
|
|
`Ok ()
|
|
|
|
| Some List_topics ->
|
|
|
|
List.filter_map commands ~f:(fun (s, what) ->
|
|
|
|
match what with
|
|
|
|
| List_topics -> None
|
|
|
|
| _ -> Some s)
|
2018-02-25 16:35:25 +00:00
|
|
|
|> List.sort ~compare:String.compare
|
2018-02-07 11:38:21 +00:00
|
|
|
|> String.concat ~sep:"\n"
|
|
|
|
|> print_endline;
|
|
|
|
`Ok ()
|
|
|
|
in
|
|
|
|
( Term.(ret (const go
|
|
|
|
$ Arg.man_format
|
|
|
|
$ Arg.(value
|
|
|
|
& pos 0 (some (enum commands)) None
|
|
|
|
& info [] ~docv:"TOPIC")
|
|
|
|
))
|
|
|
|
, Term.info "help" ~doc ~man
|
|
|
|
)
|
|
|
|
end
|
|
|
|
|
2017-02-21 15:09:58 +00:00
|
|
|
let all =
|
2017-02-24 16:29:08 +00:00
|
|
|
[ installed_libraries
|
2017-02-23 13:17:25 +00:00
|
|
|
; external_lib_deps
|
2017-02-24 15:47:09 +00:00
|
|
|
; build_targets
|
2017-02-23 13:17:25 +00:00
|
|
|
; runtest
|
2017-05-26 10:32:32 +00:00
|
|
|
; clean
|
2017-02-24 15:41:52 +00:00
|
|
|
; install
|
|
|
|
; uninstall
|
2017-03-01 12:09:57 +00:00
|
|
|
; exec
|
2017-05-07 19:42:22 +00:00
|
|
|
; subst
|
2017-05-18 23:16:48 +00:00
|
|
|
; rules
|
2017-08-04 07:59:35 +00:00
|
|
|
; utop
|
2018-01-18 11:32:20 +00:00
|
|
|
; promote
|
2018-05-04 15:49:25 +00:00
|
|
|
; printenv
|
2018-02-07 11:38:21 +00:00
|
|
|
; Help.help
|
2017-02-23 13:17:25 +00:00
|
|
|
]
|
2017-02-21 15:09:58 +00:00
|
|
|
|
2017-02-24 15:47:09 +00:00
|
|
|
let default =
|
2017-02-28 19:05:04 +00:00
|
|
|
let doc = "composable build system for OCaml" in
|
|
|
|
( Term.(ret (const (fun _ -> `Help (`Pager, None)) $ common))
|
2018-05-02 11:56:12 +00:00
|
|
|
, Term.info "dune" ~doc ~version:"%%VERSION%%"
|
2017-02-28 19:05:04 +00:00
|
|
|
~man:
|
|
|
|
[ `S "DESCRIPTION"
|
2018-05-02 11:56:12 +00:00
|
|
|
; `P {|Dune is a build system designed for OCaml projects only. It
|
2017-02-28 19:05:04 +00:00
|
|
|
focuses on providing the user with a consistent experience and takes
|
|
|
|
care of most of the low-level details of OCaml compilation. All you
|
2018-05-02 11:56:12 +00:00
|
|
|
have to do is provide a description of your project and Dune will
|
2017-02-28 19:05:04 +00:00
|
|
|
do the rest.
|
|
|
|
|}
|
|
|
|
; `P {|The scheme it implements is inspired from the one used inside Jane
|
|
|
|
Street and adapted to the open source world. It has matured over a
|
2017-08-06 09:39:42 +00:00
|
|
|
long time and is used daily by hundreds of developers, which means
|
2017-02-28 19:05:04 +00:00
|
|
|
that it is highly tested and productive.
|
|
|
|
|}
|
|
|
|
; `Blocks help_secs
|
|
|
|
]
|
|
|
|
)
|
2017-02-24 15:47:09 +00:00
|
|
|
|
2017-02-21 15:09:58 +00:00
|
|
|
let () =
|
2018-02-25 16:35:25 +00:00
|
|
|
Colors.setup_err_formatter_colors ();
|
2017-02-21 15:09:58 +00:00
|
|
|
try
|
2017-02-24 15:47:09 +00:00
|
|
|
match Term.eval_choice default all ~catch:false with
|
2017-02-21 15:09:58 +00:00
|
|
|
| `Error _ -> exit 1
|
|
|
|
| _ -> exit 0
|
2018-02-06 14:39:03 +00:00
|
|
|
with
|
|
|
|
| Fiber.Never -> exit 1
|
|
|
|
| exn ->
|
|
|
|
Report_error.report exn;
|
2017-02-21 15:09:58 +00:00
|
|
|
exit 1
|