New upstream version 1.6.3
Stephane Glondu authored 4 years ago
Stephane Glondu committed 4 years ago
0 | 1.6.3 (08/01/2019) | |
1 | ------------------ | |
2 | ||
3 | - Fix merlin handling of private module visibility (#1653 @bobot) | |
4 | ||
5 | - skip directories when looking up programs in the PATH (#1628, fixes | |
6 | #1616, @diml) | |
7 | ||
8 | - Fix preprocessing for libraries with `(include_subdirs ..)` (#1624, fix #1626, | |
9 | @nojb, @rgrinberg) | |
10 | ||
11 | - Do not generate targets for archive that don't match the `modes` field. | |
12 | (#1632, fix #1617, @rgrinberg) | |
13 | ||
14 | - When executing actions, open files lazily and close them as soon as | |
15 | possible in order to reduce the maximum number of file descriptors | |
16 | opened by Dune (#1635, #1643, fixes #1633, @jonludlam, @rgrinberg, | |
17 | @diml) | |
18 | ||
19 | - Do not generate targets for archive that don't match the `modes` field. | |
20 | (#1632, fix #1617, @rgrinberg) | |
21 | ||
22 | - Get the correct environment node for multi project workspaces (#1648, | |
23 | @rgrinberg) | |
24 | ||
25 | - Fix an issue causing menhir generated parsers to fail to build in | |
26 | some cases. The fix is to systematically use `-short-paths` when | |
27 | calling `ocamlc -i` (#1743, fix #1504, @diml) | |
28 | ||
0 | 29 | 1.6.2 (05/12/2018) |
1 | 30 | ------------------ |
2 | 31 |
0 | 0 | PREFIX_ARG := $(if $(PREFIX),--prefix $(PREFIX),) |
1 | 1 | LIBDIR_ARG := $(if $(LIBDIR),--libdir $(LIBDIR),) |
2 | 2 | INSTALL_ARGS := $(PREFIX_ARG) $(LIBDIR_ARG) |
3 | BIN := ./_build/default/bin/main_dune.exe | |
3 | BIN := ./_build_bootstrap/default/bin/main_dune.exe | |
4 | 4 | |
5 | 5 | -include Makefile.dev |
6 | 6 | |
14 | 14 | ocaml bootstrap.ml |
15 | 15 | |
16 | 16 | install: |
17 | $(BIN) install $(INSTALL_ARGS) dune | |
17 | $(BIN) install $(INSTALL_ARGS) dune --build-dir _build_bootstrap | |
18 | 18 | |
19 | 19 | uninstall: |
20 | $(BIN) uninstall $(INSTALL_ARGS) dune | |
20 | $(BIN) uninstall $(INSTALL_ARGS) dune --build-dir _build_bootstrap | |
21 | 21 | |
22 | 22 | reinstall: uninstall reinstall |
23 | 23 | |
43 | 43 | |
44 | 44 | clean: |
45 | 45 | rm -f ./boot.exe $(wildcard ./bootstrap.cmi ./bootstrap.cmo ./bootstrap.exe) |
46 | $(BIN) clean | |
46 | $(BIN) clean || true | |
47 | rm -rf _build_bootstrap | |
47 | 48 | |
48 | 49 | distclean: clean |
49 | 50 | rm -f src/setup.ml |
116 | 116 | ```sh |
117 | 117 | $ ocaml bootstrap.ml |
118 | 118 | $ ./boot.exe |
119 | $ ./_build/default/bin/main_dune.exe install dune | |
119 | $ ./_build_bootstrap/default/bin/main_dune.exe install dune | |
120 | 120 | ``` |
121 | 121 | |
122 | 122 | Support |
7 | 7 | - cd "%APPVEYOR_BUILD_FOLDER%" |
8 | 8 | - ocaml bootstrap.ml |
9 | 9 | - boot.exe |
10 | - copy _build\install\default\bin\dune.exe dune.exe | |
10 | - copy _build_bootstrap\install\default\bin\dune.exe dune.exe | |
11 | 11 | - dune.exe build @test\blackbox-tests\windows-diff |
12 | 12 | |
13 | 13 | artifacts: |
14 | 14 | - path: _build/log |
15 | 15 | name: build-log |
16 | - path: _build_bootstrap/log | |
17 | name: build_bootstrap-log |
823 | 823 | in |
824 | 824 | (term, Term.info "exec" ~doc ~man) |
825 | 825 | |
826 | (** A string that is "1.6.2" but not expanded by [dune subst] *) | |
826 | (** A string that is "1.6.3" but not expanded by [dune subst] *) | |
827 | 827 | let literal_version = |
828 | 828 | "%%" ^ "VERSION%%" |
829 | 829 | |
1195 | 1195 | `Help (`Pager, None) |
1196 | 1196 | in |
1197 | 1197 | (term, |
1198 | Term.info "dune" ~doc ~version:"1.6.2" | |
1198 | Term.info "dune" ~doc ~version:"1.6.3" | |
1199 | 1199 | ~man: |
1200 | 1200 | [ `S "DESCRIPTION" |
1201 | 1201 | ; `P {|Dune is a build system designed for OCaml projects only. It |
0 | version: "1.6.2" | |
0 | version: "1.6.3" | |
1 | 1 | opam-version: "2.0" |
2 | 2 | maintainer: "opensource@janestreet.com" |
3 | 3 | authors: ["Jane Street Group, LLC <opensource@janestreet.com>"] |
6 | 6 | ; purpose : Process.purpose |
7 | 7 | } |
8 | 8 | |
9 | let get_std_output : _ -> Process.std_output_to = function | |
10 | | None -> Terminal | |
11 | | Some (fn, oc) -> | |
12 | Opened_file { filename = fn | |
13 | ; tail = false | |
14 | ; desc = Channel oc } | |
15 | ||
16 | ||
17 | let exec_run_direct ~ectx ~dir ~env ~stdout_to ~stderr_to prog args = | |
9 | let exec_run ~ectx ~dir ~env ~stdout_to ~stderr_to prog args = | |
18 | 10 | begin match ectx.context with |
19 | 11 | | None |
20 | 12 | | Some { Context.for_host = None; _ } -> () |
34 | 26 | ~purpose:ectx.purpose |
35 | 27 | prog args |
36 | 28 | |
37 | let exec_run ~stdout_to ~stderr_to = | |
38 | let stdout_to = get_std_output stdout_to in | |
39 | let stderr_to = get_std_output stderr_to in | |
40 | exec_run_direct ~stdout_to ~stderr_to | |
41 | ||
42 | 29 | let exec_echo stdout_to str = |
43 | Fiber.return | |
44 | (match stdout_to with | |
45 | | None -> print_string str; flush stdout | |
46 | | Some (_, oc) -> output_string oc str) | |
30 | Fiber.return (output_string (Process.Output.channel stdout_to) str) | |
47 | 31 | |
48 | 32 | let rec exec t ~ectx ~dir ~env ~stdout_to ~stderr_to = |
49 | 33 | match (t : Action.t) with |
59 | 43 | | Redirect (Stdout, fn, Echo s) -> |
60 | 44 | Io.write_file fn (String.concat s ~sep:" "); |
61 | 45 | Fiber.return () |
62 | | Redirect (outputs, fn, Run (Ok prog, args)) -> | |
63 | let out = Process.File fn in | |
64 | let stdout_to, stderr_to = | |
65 | match outputs with | |
66 | | Stdout -> (out, get_std_output stderr_to) | |
67 | | Stderr -> (get_std_output stdout_to, out) | |
68 | | Outputs -> (out, out) | |
69 | in | |
70 | exec_run_direct ~ectx ~dir ~env ~stdout_to ~stderr_to prog args | |
71 | 46 | | Redirect (outputs, fn, t) -> |
72 | 47 | redirect ~ectx ~dir outputs fn t ~env ~stdout_to ~stderr_to |
73 | 48 | | Ignore (outputs, t) -> |
77 | 52 | | Echo strs -> exec_echo stdout_to (String.concat strs ~sep:" ") |
78 | 53 | | Cat fn -> |
79 | 54 | Io.with_file_in fn ~f:(fun ic -> |
80 | let oc = | |
81 | match stdout_to with | |
82 | | None -> stdout | |
83 | | Some (_, oc) -> oc | |
84 | in | |
85 | Io.copy_channels ic oc); | |
55 | Io.copy_channels ic (Process.Output.channel stdout_to)); | |
86 | 56 | Fiber.return () |
87 | 57 | | Copy (src, dst) -> |
88 | 58 | Io.copy_file ~src ~dst (); |
194 | 164 | Fiber.return () |
195 | 165 | |
196 | 166 | and redirect outputs fn t ~ectx ~dir ~env ~stdout_to ~stderr_to = |
197 | let oc = Io.open_out fn in | |
198 | let out = Some (fn, oc) in | |
167 | let out = Process.Output.file fn in | |
199 | 168 | let stdout_to, stderr_to = |
200 | 169 | match outputs with |
201 | 170 | | Stdout -> (out, stderr_to) |
203 | 172 | | Outputs -> (out, out) |
204 | 173 | in |
205 | 174 | exec t ~ectx ~dir ~env ~stdout_to ~stderr_to >>| fun () -> |
206 | close_out oc | |
175 | Process.Output.release out | |
207 | 176 | |
208 | 177 | and exec_list l ~ectx ~dir ~env ~stdout_to ~stderr_to = |
209 | 178 | match l with |
212 | 181 | | [t] -> |
213 | 182 | exec t ~ectx ~dir ~env ~stdout_to ~stderr_to |
214 | 183 | | t :: rest -> |
215 | exec t ~ectx ~dir ~env ~stdout_to ~stderr_to >>= fun () -> | |
184 | (let stdout_to = Process.Output.multi_use stdout_to in | |
185 | let stderr_to = Process.Output.multi_use stderr_to in | |
186 | exec t ~ectx ~dir ~env ~stdout_to ~stderr_to) >>= fun () -> | |
216 | 187 | exec_list rest ~ectx ~dir ~env ~stdout_to ~stderr_to |
217 | 188 | |
218 | 189 | let exec ~targets ~context ~env t = |
224 | 195 | in |
225 | 196 | let purpose = Process.Build_job targets in |
226 | 197 | let ectx = { purpose; context } in |
227 | exec t ~ectx ~dir:Path.root ~env ~stdout_to:None ~stderr_to:None | |
198 | exec t ~ectx ~dir:Path.root ~env | |
199 | ~stdout_to:Process.Output.stdout | |
200 | ~stderr_to:Process.Output.stderr |
3 | 3 | { dir : Path.t |
4 | 4 | ; inherit_from : t Lazy.t option |
5 | 5 | ; scope : Scope.t |
6 | ; config : Dune_env.Stanza.t | |
7 | ; mutable file_bindings : string File_bindings.t option | |
6 | ; config : Dune_env.Stanza.t option | |
7 | ; mutable local_binaries : string File_bindings.t option | |
8 | 8 | ; mutable ocaml_flags : Ocaml_flags.t option |
9 | 9 | ; mutable external_ : Env.t option |
10 | 10 | ; mutable artifacts : Artifacts.t option |
20 | 20 | ; ocaml_flags = None |
21 | 21 | ; external_ = env |
22 | 22 | ; artifacts = None |
23 | ; file_bindings = None | |
23 | ; local_binaries = None | |
24 | 24 | } |
25 | 25 | |
26 | let file_bindings t ~profile ~expander = | |
27 | match t.file_bindings with | |
26 | let find_config t ~profile = | |
27 | let open Option.O in | |
28 | t.config >>= fun config -> | |
29 | Dune_env.Stanza.find config ~profile | |
30 | ||
31 | let local_binaries t ~profile ~expander = | |
32 | match t.local_binaries with | |
28 | 33 | | Some x -> x |
29 | 34 | | None -> |
30 | let file_bindings = | |
31 | match Dune_env.Stanza.find t.config ~profile with | |
35 | let local_binaries = | |
36 | match find_config t ~profile with | |
32 | 37 | | None -> [] |
33 | 38 | | Some cfg -> |
34 | 39 | File_bindings.map cfg.binaries ~f:(fun template -> |
35 | 40 | Expander.expand expander ~mode:Single ~template |
36 | 41 | |> Value.to_string ~dir:t.dir) |
37 | 42 | in |
38 | t.file_bindings <- Some file_bindings; | |
39 | file_bindings | |
43 | t.local_binaries <- Some local_binaries; | |
44 | local_binaries | |
40 | 45 | |
41 | 46 | let rec external_ t ~profile ~default = |
42 | 47 | match t.external_ with |
48 | 53 | | Some (lazy t) -> external_ t ~default ~profile |
49 | 54 | in |
50 | 55 | let (env, have_binaries) = |
51 | match Dune_env.Stanza.find t.config ~profile with | |
56 | match find_config t ~profile with | |
52 | 57 | | None -> (default, false) |
53 | 58 | | Some cfg -> |
54 | 59 | ( Env.extend_env default cfg.env_vars |
74 | 79 | | Some (lazy t) -> artifacts t ~default ~profile ~expander |
75 | 80 | in |
76 | 81 | let artifacts = |
77 | file_bindings t ~profile ~expander | |
82 | local_binaries t ~profile ~expander | |
78 | 83 | |> Artifacts.add_binaries default ~dir:t.dir |
79 | 84 | in |
80 | 85 | t.artifacts <- Some artifacts; |
90 | 95 | | Some (lazy t) -> ocaml_flags t ~profile ~expander |
91 | 96 | in |
92 | 97 | let flags = |
93 | match Dune_env.Stanza.find t.config ~profile with | |
98 | match find_config t ~profile with | |
94 | 99 | | None -> default |
95 | 100 | | Some cfg -> |
96 | 101 | let expander = Expander.set_dir expander ~dir:t.dir in |
8 | 8 | : dir:Path.t |
9 | 9 | -> inherit_from:t Lazy.t option |
10 | 10 | -> scope:Scope.t |
11 | -> config:Dune_env.Stanza.t | |
11 | -> config:Dune_env.Stanza.t option | |
12 | 12 | -> env:Env.t option |
13 | 13 | -> t |
14 | 14 | |
18 | 18 | |
19 | 19 | val ocaml_flags : t -> profile:string -> expander:Expander.t -> Ocaml_flags.t |
20 | 20 | |
21 | val file_bindings | |
21 | val local_binaries | |
22 | 22 | : t |
23 | 23 | -> profile:string |
24 | 24 | -> expander:Expander.t |
198 | 198 | begin match List.last comps with |
199 | 199 | | Some ".bin" -> |
200 | 200 | let src_dir = Path.parent_exn dir in |
201 | Super_context.file_bindings sctx ~dir | |
201 | Super_context.local_binaries sctx ~dir:src_dir | |
202 | 202 | |> List.iter ~f:(fun t -> |
203 | 203 | let src = File_bindings.src_path t ~dir:src_dir in |
204 | 204 | let dst = File_bindings.dst_path t ~dir in |
401 | 401 | ] |
402 | 402 | modules |
403 | 403 | in |
404 | ||
405 | let modes = | |
406 | Mode_conf.Set.eval lib.modes | |
407 | ~has_native:(Option.is_some ctx.ocamlopt) in | |
404 | 408 | (let modules = modules @ wrapped_compat in |
405 | List.iter Mode.all ~f:(fun mode -> | |
409 | Mode.Dict.Set.to_list modes | |
410 | |> List.iter ~f:(fun mode -> | |
406 | 411 | build_lib lib ~expander ~flags ~dir ~obj_dir ~mode ~top_sorted_modules |
407 | 412 | ~modules)); |
408 | 413 | (* Build *.cma.js *) |
409 | SC.add_rules sctx ~dir ( | |
410 | let src = | |
411 | Library.archive lib ~dir | |
412 | ~ext:(Mode.compiled_lib_ext Mode.Byte) in | |
413 | let target = | |
414 | Path.relative obj_dir (Path.basename src) | |
415 | |> Path.extend_basename ~suffix:".js" in | |
416 | Js_of_ocaml_rules.build_cm cctx ~js_of_ocaml ~src ~target); | |
417 | if Dynlink_supported.By_the_os.get ctx.natdynlink_supported then | |
414 | if modes.byte then | |
415 | SC.add_rules sctx ~dir ( | |
416 | let src = | |
417 | Library.archive lib ~dir | |
418 | ~ext:(Mode.compiled_lib_ext Mode.Byte) in | |
419 | let target = | |
420 | Path.relative obj_dir (Path.basename src) | |
421 | |> Path.extend_basename ~suffix:".js" in | |
422 | Js_of_ocaml_rules.build_cm cctx ~js_of_ocaml ~src ~target); | |
423 | if Dynlink_supported.By_the_os.get ctx.natdynlink_supported | |
424 | && modes.native then | |
418 | 425 | build_shared lib ~dir ~flags ~ctx |
419 | 426 | |
420 | 427 | let library_rules (lib : Library.t) ~dir_contents ~dir ~expander ~scope |
540 | 547 | ; compile_info |
541 | 548 | }; |
542 | 549 | |
550 | let objs_dirs = Path.Set.singleton obj_dir in | |
551 | let objs_dirs = if Lib_modules.has_private_modules lib_modules then | |
552 | Path.Set.add objs_dirs private_obj_dir | |
553 | else objs_dirs in | |
554 | ||
543 | 555 | (cctx, |
544 | 556 | Merlin.make () |
545 | 557 | ~requires:(Lib.Compile.requires compile_info) |
546 | 558 | ~flags |
547 | 559 | ~preprocess:(Buildable.single_preprocess lib.buildable) |
548 | 560 | ~libname:(snd lib.name) |
549 | ~objs_dirs:(Path.Set.singleton obj_dir)) | |
561 | ~objs_dirs | |
562 | ) | |
550 | 563 | |
551 | 564 | let rules (lib : Library.t) ~dir_contents ~dir ~expander ~scope |
552 | 565 | ~dir_kind : Compilation_context.t * Merlin.t = |
143 | 143 | | None -> loop rest |
144 | 144 | | Some prog -> |
145 | 145 | Process.run_capture (Accept All) prog args ~env:Env.initial |
146 | ~stderr_to:(File Config.dev_null) | |
146 | ~stderr_to:(Process.Output.file Config.dev_null) | |
147 | 147 | >>= function |
148 | 148 | | Error _ -> loop rest |
149 | 149 | | Ok s -> |
171 | 171 | let bootstrap () = |
172 | 172 | Colors.setup_err_formatter_colors (); |
173 | 173 | Path.set_root Path.External.initial_cwd; |
174 | Path.set_build_dir (Path.Kind.of_string "_build"); | |
174 | Path.set_build_dir (Path.Kind.of_string "_build_bootstrap"); | |
175 | 175 | let main () = |
176 | 176 | let anon s = raise (Arg.Bad (Printf.sprintf "don't know what to do with %s\n" s)) in |
177 | 177 | let subst () = |
143 | 143 | Lib.src_dir lib |
144 | 144 | |> Path.drop_optional_build_context) |
145 | 145 | , |
146 | let obj_dirs = Path.Set.add obj_dirs (Lib.obj_dir lib) in | |
147 | match Lib.private_obj_dir lib with | |
148 | | None -> obj_dirs | |
149 | | Some private_obj_dir -> Path.Set.add obj_dirs private_obj_dir | |
146 | Path.Set.add obj_dirs (Lib.obj_dir lib) | |
150 | 147 | )) |
151 | 148 | in |
152 | 149 | let src_dirs = |
209 | 209 | | Some (m : Module.t) -> |
210 | 210 | As ["-open"; Module.Name.to_string (Module.name m)]) |
211 | 211 | ; As flags |
212 | ; A "-short-paths" | |
212 | 213 | ; A "-i"; Ml_kind.flag Impl; Dep src |
213 | 214 | ] |
214 | 215 | >>^ (fun act -> Action.with_stdout_to output act) |
664 | 664 | ~expander |
665 | 665 | ~dep_kind |
666 | 666 | ~targets:(Static [dst]) |
667 | ~targets_dir:dir)) | |
667 | ~targets_dir:(Path.parent_exn dst))) | |
668 | 668 | |> setup_reason_rules sctx in |
669 | 669 | if lint then lint_module ~ast ~source:m; |
670 | 670 | ast) |
29 | 29 | | 0 -> Ok (f ()) |
30 | 30 | | n -> Error n |
31 | 31 | |
32 | type std_output_to = | |
33 | | Terminal | |
34 | | File of Path.t | |
35 | | Opened_file of opened_file | |
36 | ||
37 | and opened_file = | |
38 | { filename : Path.t | |
39 | ; desc : opened_file_desc | |
40 | ; tail : bool | |
41 | } | |
42 | ||
43 | and opened_file_desc = | |
44 | | Fd of Unix.file_descr | |
45 | | Channel of out_channel | |
32 | module Output = struct | |
33 | type t = | |
34 | { kind : kind | |
35 | ; fd : Unix.file_descr Lazy.t | |
36 | ; channel : out_channel Lazy.t | |
37 | ; mutable status : status | |
38 | } | |
39 | ||
40 | and kind = | |
41 | | File of Path.t | |
42 | | Terminal | |
43 | ||
44 | and status = | |
45 | | Keep_open | |
46 | | Close_after_exec | |
47 | | Closed | |
48 | ||
49 | let terminal oc = | |
50 | let fd = Unix.descr_of_out_channel oc in | |
51 | { kind = Terminal | |
52 | ; fd = lazy fd | |
53 | ; channel = lazy stdout | |
54 | ; status = Keep_open | |
55 | } | |
56 | let stdout = terminal stdout | |
57 | let stderr = terminal stderr | |
58 | ||
59 | let file fn = | |
60 | let fd = | |
61 | lazy (Unix.openfile (Path.to_string fn) | |
62 | [O_WRONLY; O_CREAT; O_TRUNC; O_SHARE_DELETE] 0o666) | |
63 | in | |
64 | { kind = File fn | |
65 | ; fd | |
66 | ; channel = lazy (Unix.out_channel_of_descr (Lazy.force fd)) | |
67 | ; status = Close_after_exec | |
68 | } | |
69 | ||
70 | let flush t = | |
71 | if Lazy.is_val t.channel then flush (Lazy.force t.channel) | |
72 | ||
73 | let fd t = | |
74 | flush t; | |
75 | Lazy.force t.fd | |
76 | ||
77 | let channel t = Lazy.force t.channel | |
78 | ||
79 | let release t = | |
80 | match t.status with | |
81 | | Closed -> () | |
82 | | Keep_open -> flush t | |
83 | | Close_after_exec -> | |
84 | t.status <- Closed; | |
85 | if Lazy.is_val t.channel then | |
86 | close_out (Lazy.force t.channel) | |
87 | else | |
88 | Unix.close (Lazy.force t.fd) | |
89 | ||
90 | let multi_use t = | |
91 | { t with status = Keep_open } | |
92 | end | |
46 | 93 | |
47 | 94 | type purpose = |
48 | 95 | | Internal_job |
111 | 158 | "-o" :: Colors.(apply_string output_filename) fn :: colorize_args rest |
112 | 159 | | x :: rest -> x :: colorize_args rest |
113 | 160 | |
114 | let command_line ~prog ~args ~dir ~stdout_to ~stderr_to = | |
161 | let command_line ~prog ~args ~dir | |
162 | ~(stdout_to:Output.t) ~(stderr_to:Output.t) = | |
115 | 163 | let prog = Path.reach_for_running ?from:dir prog in |
116 | 164 | let quote = quote_for_shell in |
117 | 165 | let prog = colorize_prog (quote prog) in |
123 | 171 | | None -> s |
124 | 172 | | Some dir -> sprintf "(cd %s && %s)" (Path.to_string dir) s |
125 | 173 | in |
126 | match stdout_to, stderr_to with | |
127 | | (File fn1 | Opened_file { filename = fn1; _ }), | |
128 | (File fn2 | Opened_file { filename = fn2; _ }) when Path.equal fn1 fn2 -> | |
174 | match stdout_to.kind, stderr_to.kind with | |
175 | | File fn1, File fn2 when Path.equal fn1 fn2 -> | |
129 | 176 | sprintf "%s &> %s" s (Path.to_string fn1) |
130 | 177 | | _ -> |
131 | 178 | let s = |
132 | match stdout_to with | |
179 | match stdout_to.kind with | |
133 | 180 | | Terminal -> s |
134 | | File fn | Opened_file { filename = fn; _ } -> | |
181 | | File fn -> | |
135 | 182 | sprintf "%s > %s" s (Path.to_string fn) |
136 | 183 | in |
137 | match stderr_to with | |
184 | match stderr_to.kind with | |
138 | 185 | | Terminal -> s |
139 | | File fn | Opened_file { filename = fn; _ } -> | |
186 | | File fn -> | |
140 | 187 | sprintf "%s 2> %s" s (Path.to_string fn) |
141 | 188 | |
142 | 189 | let pp_purpose ppf = function |
190 | 237 | contexts; |
191 | 238 | end |
192 | 239 | |
193 | let get_std_output ~default = function | |
194 | | Terminal -> (default, None) | |
195 | | File fn -> | |
196 | let fd = Unix.openfile (Path.to_string fn) | |
197 | [O_WRONLY; O_CREAT; O_TRUNC; O_SHARE_DELETE] 0o666 in | |
198 | (fd, Some (Fd fd)) | |
199 | | Opened_file { desc; tail; _ } -> | |
200 | let fd = | |
201 | match desc with | |
202 | | Fd fd -> fd | |
203 | | Channel oc -> flush oc; Unix.descr_of_out_channel oc | |
204 | in | |
205 | (fd, Option.some_if tail desc) | |
206 | ||
207 | let close_std_output = function | |
208 | | None -> () | |
209 | | Some (Fd fd) -> Unix.close fd | |
210 | | Some (Channel oc) -> close_out oc | |
211 | ||
212 | 240 | let gen_id = |
213 | 241 | let next = ref (-1) in |
214 | 242 | fun () -> incr next; !next |
217 | 245 | List.fold_left args ~init:(String.length prog) ~f:(fun acc arg -> |
218 | 246 | acc + String.length arg) |
219 | 247 | |
220 | let run_internal ?dir ?(stdout_to=Terminal) ?(stderr_to=Terminal) ~env ~purpose | |
221 | fail_mode prog args = | |
248 | let run_internal ?dir ?(stdout_to=Output.stdout) ?(stderr_to=Output.stderr) | |
249 | ~env ~purpose fail_mode prog args = | |
222 | 250 | Scheduler.wait_for_available_job () |
223 | 251 | >>= fun scheduler -> |
224 | 252 | let display = Scheduler.display scheduler in |
253 | 281 | (args, None) |
254 | 282 | in |
255 | 283 | let argv = prog_str :: args in |
256 | let output_filename, stdout_fd, stderr_fd, to_close = | |
257 | match stdout_to, stderr_to with | |
284 | let output_filename, stdout_to, stderr_to = | |
285 | match stdout_to.kind, stderr_to.kind with | |
258 | 286 | | (Terminal, _ | _, Terminal) when !Clflags.capture_outputs -> |
259 | 287 | let fn = Temp.create "dune" ".output" in |
260 | let fd = Unix.openfile (Path.to_string fn) [O_WRONLY; O_SHARE_DELETE] 0 in | |
261 | (Some fn, fd, fd, Some fd) | |
288 | let terminal = Output.file fn in | |
289 | let get (out : Output.t) = | |
290 | if out.kind = Terminal then begin | |
291 | Output.flush out; | |
292 | terminal | |
293 | end else | |
294 | out | |
295 | in | |
296 | (Some fn, get stdout_to, get stderr_to) | |
262 | 297 | | _ -> |
263 | (None, Unix.stdout, Unix.stderr, None) | |
264 | in | |
265 | let stdout, close_stdout = get_std_output stdout_to ~default:stdout_fd in | |
266 | let stderr, close_stderr = get_std_output stderr_to ~default:stderr_fd in | |
267 | let run () = | |
268 | Spawn.spawn () | |
269 | ~prog:prog_str | |
270 | ~argv | |
271 | ~env:(Spawn.Env.of_array (Env.to_unix env)) | |
272 | ~stdout | |
273 | ~stderr | |
298 | (None, stdout_to, stderr_to) | |
299 | in | |
300 | let run = | |
301 | (* Output.fd might create the file with Unix.openfile. We need to | |
302 | make sure to call it before doing the chdir as the path might | |
303 | be relative. *) | |
304 | let stdout = Output.fd stdout_to in | |
305 | let stderr = Output.fd stderr_to in | |
306 | fun () -> | |
307 | Spawn.spawn () | |
308 | ~prog:prog_str | |
309 | ~argv | |
310 | ~env:(Spawn.Env.of_array (Env.to_unix env)) | |
311 | ~stdout | |
312 | ~stderr | |
274 | 313 | in |
275 | 314 | let pid = |
276 | 315 | match dir with |
277 | 316 | | None -> run () |
278 | 317 | | Some dir -> Scheduler.with_chdir scheduler ~dir ~f:run |
279 | 318 | in |
280 | Option.iter to_close ~f:Unix.close; | |
281 | close_std_output close_stdout; | |
282 | close_std_output close_stderr; | |
319 | Output.release stdout_to; | |
320 | Output.release stderr_to; | |
283 | 321 | Scheduler.wait_for_process pid |
284 | 322 | >>| fun exit_status -> |
285 | 323 | Option.iter response_file ~f:Path.unlink; |
353 | 391 | prog args ~f = |
354 | 392 | let fn = Temp.create "dune" ".output" in |
355 | 393 | map_result fail_mode |
356 | (run_internal ?dir ~stdout_to:(File fn) ?stderr_to | |
394 | (run_internal ?dir ~stdout_to:(Output.file fn) ?stderr_to | |
357 | 395 | ~env ~purpose fail_mode prog args) |
358 | 396 | ~f:(fun () -> |
359 | 397 | let x = f fn in |
13 | 13 | (** Accept the following non-zero exit codes, and return [Error |
14 | 14 | code] if the process exists with one of these codes. *) |
15 | 15 | |
16 | (** Where to redirect standard output *) | |
17 | type std_output_to = | |
18 | | Terminal | |
19 | | File of Path.t | |
20 | | Opened_file of opened_file | |
16 | module Output : sig | |
17 | (** Where to redirect stdout/stderr *) | |
18 | type t | |
21 | 19 | |
22 | and opened_file = | |
23 | { filename : Path.t | |
24 | ; desc : opened_file_desc | |
25 | ; tail : bool | |
26 | (** If [true], the descriptor is closed after starting the command *) | |
27 | } | |
20 | val stdout : t | |
21 | val stderr : t | |
28 | 22 | |
29 | and opened_file_desc = | |
30 | | Fd of Unix.file_descr | |
31 | | Channel of out_channel | |
23 | (** Create a [t] representing redirecting the output to a file. The | |
24 | returned output can only be used by a single call to {!run}. If | |
25 | you want to use it multiple times, you need to use [clone]. *) | |
26 | val file : Path.t -> t | |
27 | ||
28 | (** Call this when you no longer need this output *) | |
29 | val release : t -> unit | |
30 | ||
31 | (** Return a buffered channel for this output. The channel is | |
32 | created lazily. *) | |
33 | val channel : t -> out_channel | |
34 | ||
35 | (** [multi_use t] returns a copy for which [release] does nothing *) | |
36 | val multi_use : t -> t | |
37 | end | |
32 | 38 | |
33 | 39 | (** Why a Fiber.t was run *) |
34 | 40 | type purpose = |
38 | 44 | (** [run ?dir ?stdout_to prog args] spawns a sub-process and wait for its termination *) |
39 | 45 | val run |
40 | 46 | : ?dir:Path.t |
41 | -> ?stdout_to:std_output_to | |
42 | -> ?stderr_to:std_output_to | |
47 | -> ?stdout_to:Output.t | |
48 | -> ?stderr_to:Output.t | |
43 | 49 | -> env:Env.t |
44 | 50 | -> ?purpose:purpose |
45 | 51 | -> (unit, 'a) failure_mode |
50 | 56 | (** Run a command and capture its output *) |
51 | 57 | val run_capture |
52 | 58 | : ?dir:Path.t |
53 | -> ?stderr_to:std_output_to | |
59 | -> ?stderr_to:Output.t | |
54 | 60 | -> env:Env.t |
55 | 61 | -> ?purpose:purpose |
56 | 62 | -> (string, 'a) failure_mode |
59 | 65 | -> 'a Fiber.t |
60 | 66 | val run_capture_line |
61 | 67 | : ?dir:Path.t |
62 | -> ?stderr_to:std_output_to | |
68 | -> ?stderr_to:Output.t | |
63 | 69 | -> env:Env.t |
64 | 70 | -> ?purpose:purpose |
65 | 71 | -> (string, 'a) failure_mode |
68 | 74 | -> 'a Fiber.t |
69 | 75 | val run_capture_lines |
70 | 76 | : ?dir:Path.t |
71 | -> ?stderr_to:std_output_to | |
77 | -> ?stderr_to:Output.t | |
72 | 78 | -> env:Env.t |
73 | 79 | -> ?purpose:purpose |
74 | 80 | -> (string list, 'a) failure_mode |
75 | 81 | -> Path.t |
76 | 82 | -> string list |
77 | 83 | -> 'a Fiber.t |
78 |
17 | 17 | |
18 | 18 | let exe = if Sys.win32 then ".exe" else "" |
19 | 19 | |
20 | let exists fn = | |
21 | match Unix.stat (Path.to_string fn) with | |
22 | | { st_kind = S_DIR; _ } -> false | |
23 | | exception (Unix.Unix_error _) -> false | |
24 | | _ -> true | |
25 | ||
20 | 26 | let best_prog dir prog = |
21 | 27 | let fn = Path.relative dir (prog ^ ".opt" ^ exe) in |
22 | if Path.exists fn then | |
28 | if exists fn then | |
23 | 29 | Some fn |
24 | 30 | else |
25 | 31 | let fn = Path.relative dir (prog ^ exe) in |
26 | if Path.exists fn then | |
32 | if exists fn then | |
27 | 33 | Some fn |
28 | 34 | else |
29 | 35 | None |
73 | 73 | val external_ : t -> dir:Path.t -> External_env.t |
74 | 74 | val artifacts_host : t -> dir:Path.t -> Artifacts.t |
75 | 75 | val expander : t -> dir:Path.t -> Expander.t |
76 | val file_bindings : t -> dir:Path.t -> string File_bindings.t | |
76 | val local_binaries : t -> dir:Path.t -> string File_bindings.t | |
77 | 77 | end = struct |
78 | 78 | let get_env_stanza t ~dir = |
79 | 79 | let open Option.O in |
95 | 95 | | None -> raise_notrace Exit |
96 | 96 | | Some parent -> lazy (get t ~dir:parent ~scope) |
97 | 97 | in |
98 | match get_env_stanza t ~dir with | |
99 | | None -> Lazy.force inherit_from | |
100 | | Some config -> | |
101 | Env_node.make ~dir ~scope ~config ~inherit_from:(Some inherit_from) | |
102 | ~env:None | |
98 | let config = get_env_stanza t ~dir in | |
99 | Env_node.make ~dir ~scope ~config ~inherit_from:(Some inherit_from) | |
100 | ~env:None | |
103 | 101 | in |
104 | 102 | Hashtbl.add t.env dir node; |
105 | 103 | node |
125 | 123 | |> Expander.set_scope ~scope:(Env_node.scope node) |
126 | 124 | |> Expander.set_dir ~dir |
127 | 125 | |
128 | let file_bindings t ~dir = | |
126 | let local_binaries t ~dir = | |
129 | 127 | let node = get t ~dir in |
130 | 128 | let expander = expander_for_artifacts t ~dir in |
131 | Env_node.file_bindings node ~profile:(profile t) ~expander | |
129 | Env_node.local_binaries node ~profile:(profile t) ~expander | |
132 | 130 | |
133 | 131 | let artifacts t ~dir = |
134 | 132 | let expander = expander_for_artifacts t ~dir in |
238 | 236 | ~default:(Env.ocaml_flags t ~dir) |
239 | 237 | ~eval:(Expander.expand_and_eval_set expander) |
240 | 238 | |
241 | let file_bindings t ~dir = Env.file_bindings t ~dir | |
239 | let local_binaries t ~dir = Env.local_binaries t ~dir | |
242 | 240 | |
243 | 241 | let dump_env t ~dir = |
244 | 242 | Ocaml_flags.dump (Env.ocaml_flags t ~dir) |
313 | 311 | in |
314 | 312 | match context.env_nodes with |
315 | 313 | | { context = None; workspace = None } -> |
316 | make ~config:{ loc = Loc.none; rules = [] } ~inherit_from:None | |
317 | | { context = Some config; workspace = None } | |
318 | | { context = None; workspace = Some config } -> | |
314 | make ~config:(Some { loc = Loc.none; rules = [] }) ~inherit_from:None | |
315 | | { context = Some _ as config; workspace = None } | |
316 | | { context = None; workspace = Some _ as config } -> | |
319 | 317 | make ~config ~inherit_from:None |
320 | | { context = Some context ; workspace = Some workspace } -> | |
318 | | { context = Some _ as context ; workspace = Some _ as workspace } -> | |
321 | 319 | make ~config:context |
322 | ~inherit_from:(Some (lazy (make ~inherit_from:None ~config:workspace))) | |
320 | ~inherit_from:(Some (lazy (make ~inherit_from:None | |
321 | ~config:workspace))) | |
323 | 322 | ) in |
324 | 323 | let expander = |
325 | 324 | let artifacts_host = |
52 | 52 | -> Buildable.t |
53 | 53 | -> Ocaml_flags.t |
54 | 54 | |
55 | val file_bindings : t -> dir:Path.t -> string File_bindings.t | |
55 | (** Binaries that are symlinked in the associated .bin directory of [dir]. This | |
56 | associated directory is [Path.relative dir ".bin"] *) | |
57 | val local_binaries : t -> dir:Path.t -> string File_bindings.t | |
56 | 58 | |
57 | 59 | (** Dump a directory environment in a readable form *) |
58 | 60 | val dump_env : t -> dir:Path.t -> (unit, Dune_lang.t list) Build.t |
143 | 143 | (progn (run %{exe:cram.exe} -test run.t) (diff? run.t run.t.corrected))))) |
144 | 144 | |
145 | 145 | (alias |
146 | (name double-echo) | |
147 | (deps (package dune) (source_tree test-cases/double-echo)) | |
148 | (action | |
149 | (chdir | |
150 | test-cases/double-echo | |
151 | (progn (run %{exe:cram.exe} -test run.t) (diff? run.t run.t.corrected))))) | |
152 | ||
153 | (alias | |
146 | 154 | (name dune-build-dir-exec-1101) |
147 | 155 | (deps (package dune) (source_tree test-cases/dune-build-dir-exec-1101)) |
148 | 156 | (action |
444 | 452 | (action |
445 | 453 | (chdir |
446 | 454 | test-cases/github1560 |
455 | (progn (run %{exe:cram.exe} -test run.t) (diff? run.t run.t.corrected))))) | |
456 | ||
457 | (alias | |
458 | (name github1616) | |
459 | (deps (package dune) (source_tree test-cases/github1616)) | |
460 | (action | |
461 | (chdir | |
462 | test-cases/github1616 | |
447 | 463 | (progn (run %{exe:cram.exe} -test run.t) (diff? run.t run.t.corrected))))) |
448 | 464 | |
449 | 465 | (alias |
1170 | 1186 | (alias depend-on-the-universe) |
1171 | 1187 | (alias deps-conf-vars) |
1172 | 1188 | (alias dev-flag-1103) |
1189 | (alias double-echo) | |
1173 | 1190 | (alias dune-build-dir-exec-1101) |
1174 | 1191 | (alias dune-jbuild-var-case) |
1175 | 1192 | (alias dune-ppx-driver-system) |
1207 | 1224 | (alias github1529) |
1208 | 1225 | (alias github1549) |
1209 | 1226 | (alias github1560) |
1227 | (alias github1616) | |
1210 | 1228 | (alias github20) |
1211 | 1229 | (alias github24) |
1212 | 1230 | (alias github25) |
1313 | 1331 | (alias depend-on-the-universe) |
1314 | 1332 | (alias deps-conf-vars) |
1315 | 1333 | (alias dev-flag-1103) |
1334 | (alias double-echo) | |
1316 | 1335 | (alias dune-build-dir-exec-1101) |
1317 | 1336 | (alias dune-jbuild-var-case) |
1318 | 1337 | (alias dune-ppx-driver-system) |
1348 | 1367 | (alias github1529) |
1349 | 1368 | (alias github1549) |
1350 | 1369 | (alias github1560) |
1370 | (alias github1616) | |
1351 | 1371 | (alias github20) |
1352 | 1372 | (alias github24) |
1353 | 1373 | (alias github25) |
0 | (rule | |
1 | (with-stdout-to foobar | |
2 | (progn | |
3 | (echo "foo") | |
4 | (echo "bar")))) | |
5 | ||
6 | (alias | |
7 | (name default) | |
8 | (action (echo %{read:foobar}))) |
0 | (lang dune 1.6)⏎ |
0 | (lang dune 1.6) |
0 | Regression test for #1616 | |
1 | ||
2 | $ env PATH="$PWD/bin2:$PWD/bin1:$PATH" dune build --root root | |
3 | Entering directory 'root' | |
4 | prog alias default | |
5 | Hello, World! |
10 | 10 | B $LIB_PREFIX/lib/ocaml |
11 | 11 | B ../_build/default/exe/.x.eobjs |
12 | 12 | B ../_build/default/lib/.foo.objs |
13 | B ../_build/default/lib/.foo.objs/.private | |
14 | 13 | S $LIB_PREFIX/lib/bytes |
15 | 14 | S $LIB_PREFIX/lib/findlib |
16 | 15 | S $LIB_PREFIX/lib/ocaml |
24 | 23 | B $LIB_PREFIX/lib/ocaml |
25 | 24 | B ../_build/default/lib/.bar.objs |
26 | 25 | B ../_build/default/lib/.foo.objs |
26 | B ../_build/default/lib/.foo.objs/.private | |
27 | 27 | S $LIB_PREFIX/lib/bytes |
28 | 28 | S $LIB_PREFIX/lib/findlib |
29 | 29 | S $LIB_PREFIX/lib/ocaml |
48 | 48 | Error: This stanza is not allowed in a sub-directory of directory with (include_subdirs unqualified). |
49 | 49 | Hint: add (include_subdirs no) to this file. |
50 | 50 | [1] |
51 | ||
52 | Test for (include_subdir unqualified) with (preprocess (action ...)) | |
53 | -------------------------------------------------------------------- | |
54 | ||
55 | $ dune build --display short --root test4 @all | |
56 | Entering directory 'test4' | |
57 | ocamldep .main.eobjs/main.ml.d | |
58 | ocamlc .main.eobjs/main.{cmi,cmo,cmt} | |
59 | ocamlopt .main.eobjs/main.{cmx,o} | |
60 | ocamlopt main.exe | |
61 | main sub/foo.pp.ml | |
62 | ocamldep .foo.objs/foo.pp.ml.d | |
63 | ocamlc .foo.objs/foo.{cmi,cmo,cmt} | |
64 | ocamlopt .foo.objs/foo.{cmx,o} | |
65 | ocamlopt foo.{a,cmxa} | |
66 | ocamlopt foo.cmxs | |
67 | ocamlc main.bc | |
68 | ocamlc foo.cma |
0 | (executable | |
1 | (name main) | |
2 | (modules main)) | |
3 | ||
4 | (library | |
5 | (name foo) | |
6 | (preprocess (action (run ./main.exe %{input-file}))) | |
7 | (modules foo)) | |
8 | ||
9 | (include_subdirs unqualified) |
0 | (lang dune 1.1) |