From c0f183bf000e897ff6f34fd54522d86989c2b7a0 Mon Sep 17 00:00:00 2001 From: Antonio Nuno Monteiro Date: Sat, 23 May 2026 19:59:26 -0700 Subject: [PATCH] feat(single-context): add conditional Melange preprocessor deps Signed-off-by: Antonio Nuno Monteiro --- src/dune_lang/preprocess.ml | 43 ++++++++++----- src/dune_rules/stanzas/buildable.ml | 50 +++++++++++++++-- src/dune_rules/vimpl.ml | 4 +- .../test-cases/melange/conditional-pps.t | 53 +++++++++++++++++++ 4 files changed, 133 insertions(+), 17 deletions(-) diff --git a/src/dune_lang/preprocess.ml b/src/dune_lang/preprocess.ml index 0f534dcc93e..3d4dbb1c6c5 100644 --- a/src/dune_lang/preprocess.ml +++ b/src/dune_lang/preprocess.ml @@ -369,20 +369,30 @@ module Per_module = struct ;; end -let preprocess_fields = +let preprocess_fields_with_prefix ~prefix:field_prefix = let open Decoder in - let+ preprocess = field "preprocess" Per_module.decode ~default:(Per_module.default ()) + let prefix = + let prefix x = x ^ "." in + Option.map field_prefix ~f:prefix |> Option.value ~default:"" + in + let+ preprocess = + match field_prefix with + | None -> + let+ v = field "preprocess" Per_module.decode ~default:(Per_module.default ()) in + Some v + | Some _ -> field_o (prefix ^ "preprocess") Per_module.decode and+ preprocessor_deps = - field_o - "preprocessor_deps" - (let+ loc = loc - and+ l = repeat Dep_conf.decode in - loc, l) + let decode = + let+ loc = loc + and+ l = repeat Dep_conf.decode in + loc, l + in + field_o (prefix ^ "preprocessor_deps") decode and+ syntax = Syntax.get_exn Stanza.syntax in let preprocessor_deps = - match preprocessor_deps with - | None -> [] - | Some (loc, deps) -> + match preprocessor_deps, preprocess with + | Some _, None | None, _ -> [] + | Some (loc, deps), Some preprocess -> let deps_might_be_used = Module_name.Per_item.exists preprocess ~f:(fun p -> match p with @@ -394,15 +404,22 @@ let preprocess_fields = User_warning.emit ~loc ~is_error:(syntax >= (2, 0)) - [ Pp.text - "This preprocessor_deps field will be ignored because no preprocessor that \ - might use them is configured." + [ Pp.textf + "This %spreprocessor_deps field will be ignored because no preprocessor \ + that might use them is configured." + prefix ]; deps in preprocess, preprocessor_deps ;; +let preprocess_fields = + let open Decoder in + let+ preprocess, deps = preprocess_fields_with_prefix ~prefix:None in + Option.value_exn preprocess, deps +;; + type preprocess = { config : With_instrumentation.t Per_module.t ; preprocessor_deps : Dep_conf.t list diff --git a/src/dune_rules/stanzas/buildable.ml b/src/dune_rules/stanzas/buildable.ml index a3dd02df397..9f97fcf099f 100644 --- a/src/dune_rules/stanzas/buildable.ml +++ b/src/dune_rules/stanzas/buildable.ml @@ -54,6 +54,40 @@ let decode_melange_pps = (Dune_lang.Syntax.since Stanza.syntax (3, 24) >>> Preprocess.Pps.decode) ;; +let decode_melange_preprocess = + let+ preprocess = field_o "melange.preprocess" Preprocess.Per_module.decode + and+ preprocessor_deps = + field_o + "melange.preprocessor_deps" + (Dune_lang.Syntax.since Stanza.syntax (3, 24) + >>> let+ loc = loc + and+ l = repeat Dep_conf.decode in + loc, l) + and+ syntax = Dune_lang.Syntax.get_exn Stanza.syntax in + let preprocessor_deps = + match preprocessor_deps, preprocess with + | Some _, None | None, _ -> [] + | Some (loc, deps), Some preprocess -> + let deps_might_be_used = + Module_name.Per_item.exists preprocess ~f:(fun p -> + match p with + | Preprocess.Action _ | Preprocess.Pps _ -> true + | Preprocess.No_preprocessing | Preprocess.Future_syntax _ -> false) + in + if not deps_might_be_used + then + User_warning.emit + ~loc + ~is_error:(syntax >= (2, 0)) + [ Pp.text + "This melange.preprocessor_deps field will be ignored because no \ + preprocessor that might use them is configured." + ]; + deps + in + preprocess, preprocessor_deps +;; + let decode (for_ : for_) = let use_foreign = Dune_lang.Syntax.deleted_in @@ -78,6 +112,7 @@ let decode (for_ : for_) = let+ loc = loc and+ instrumentation = Preprocess.Instrumentation.instrumentation and+ preprocess, preprocessor_deps = Preprocess.preprocess_fields + and+ melange_preprocess, melange_preprocessor_deps = decode_melange_preprocess and+ melange_pps = decode_melange_pps and+ lint = decode_lint and+ foreign_stubs = @@ -153,11 +188,20 @@ let decode (for_ : for_) = Preprocess.preprocess_config ~preprocess ~instrumentation ~preprocessor_deps in let melange_preprocess = - match melange_pps with - | None -> preprocess - | Some pps -> + match melange_preprocess, melange_pps with + | None, None -> preprocess + | Some preprocess, None -> + Preprocess.preprocess_config + ~preprocess + ~instrumentation + ~preprocessor_deps:melange_preprocessor_deps + | None, Some pps -> let preprocess = Module_name.Per_item.for_all (Preprocess.Pps pps) in Preprocess.preprocess_config ~preprocess ~instrumentation ~preprocessor_deps:[] + | Some _, Some pps -> + User_error.raise + ~loc:pps.loc + [ Pp.text "Cannot use both melange.pps and melange.preprocess." ] in let foreign_stubs = foreign_stubs diff --git a/src/dune_rules/vimpl.ml b/src/dune_rules/vimpl.ml index 20d6398128e..e0dc95b5835 100644 --- a/src/dune_rules/vimpl.ml +++ b/src/dune_rules/vimpl.ml @@ -38,7 +38,9 @@ let make ~sctx ~scope ~(lib : Library.t) ~info ~vlib ~for_ = let* preprocess = (* TODO wrong, this should be delayed *) Instrumentation.with_instrumentation - lib.buildable.preprocess.config + (match for_ with + | Ocaml -> lib.buildable.preprocess.config + | Melange -> lib.buildable.melange_preprocess.config) ~instrumentation_backend:(Lib.DB.instrumentation_backend db) |> Resolve.Memo.read_memo in diff --git a/test/blackbox-tests/test-cases/melange/conditional-pps.t b/test/blackbox-tests/test-cases/melange/conditional-pps.t index 9b808f8b3ad..bb578e32e0f 100644 --- a/test/blackbox-tests/test-cases/melange/conditional-pps.t +++ b/test/blackbox-tests/test-cases/melange/conditional-pps.t @@ -57,3 +57,56 @@ The field is available starting in Dune 3.24. $ find ./app/_build/default -name '*.pp.ml' | censor | sort ./app/_build/default/.melange_src/foo.pp.ml ./app/_build/default/.melange_src/melange_only.pp.ml + +`melange.preprocessor_deps` is available starting in Dune 3.24. + + $ mkdir old-preprocessor-deps + $ cat > old-preprocessor-deps/dune-project < (lang dune 3.23) + > (using melange 0.1) + > EOF + $ cat > old-preprocessor-deps/dune < (library + > (name old_preprocessor_deps) + > (modes melange) + > (melange.preprocessor_deps dep.txt)) + > EOF + $ dune build --root old-preprocessor-deps + Entering directory 'old-preprocessor-deps' + File "dune", line 4, characters 1-36: + 4 | (melange.preprocessor_deps dep.txt)) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + Error: 'melange.preprocessor_deps' is only available since version 3.24 of + the dune language. Please update your dune-project file to have (lang dune + 3.24). + Leaving directory 'old-preprocessor-deps' + [1] + +`melange.preprocessor_deps` applies only to Melange preprocessing. + + $ mkdir deps + $ cat > deps/dune-project < (lang dune 3.24) + > (using melange 0.1) + > EOF + $ cat > deps/dune < (library + > (modes melange :standard) + > (name deps) + > (melange.preprocess (action (run ./pp.sh %{input-file}))) + > (melange.preprocessor_deps pp.sh)) + > EOF + + $ cat > deps/foo.ml < let x = "foo" + > EOF + $ cat > deps/pp.sh < #!/bin/sh + > cat "\$1" + > echo 'let y = "from melange preprocessor"' + > EOF + $ chmod +x deps/pp.sh + + $ dune build --root deps + $ grep y deps/_build/default/.melange_src/foo.pp.ml + let y = "from melange preprocessor"