diff --git a/src/dune_rules/dir_status.ml b/src/dune_rules/dir_status.ml index b0c4130e97e..9047ae71529 100644 --- a/src/dune_rules/dir_status.ml +++ b/src/dune_rules/dir_status.ml @@ -85,11 +85,38 @@ let error_no_module_consumer ~loc (qualification : Include_subdirs.qualification ] ;; -let extract_directory_targets ~dir stanzas = - Memo.List.fold_left stanzas ~init:Path.Build.Map.empty ~f:(fun acc stanza -> - match Stanza.repr stanza with - | Rule_conf.T { targets = Static { targets = l; _ }; loc = rule_loc; enabled_if; _ } - -> +let directory_targets_of_rule ~dir { Rule_conf.targets; loc = rule_loc; enabled_if; _ } = + match targets with + | Infer -> + (* we don't infer directory targets *) + Memo.return Path.Build.Map.empty + | Static { targets; _ } -> + let directory_targets = + List.fold_left targets ~init:Path.Build.Map.empty ~f:(fun acc (target, kind) -> + match (kind : Targets_spec.Kind.t) with + | File -> acc + | Directory -> + let loc = String_with_vars.loc target in + (match String_with_vars.text_only target with + | None -> + User_error.raise + ~loc + [ Pp.text "Variables are not allowed in directory targets." ] + | Some target -> + let dir_target = Path.Build.relative ~error_loc:loc dir target in + if Path.Build.is_descendant dir_target ~of_:dir + then + (* We ignore duplicates here as duplicates are detected and + reported by [Load_rules]. *) + Path.Build.Map.set acc dir_target rule_loc + else + (* This will be checked when we interpret the stanza + completely, so just ignore this rule for now. *) + acc)) + in + if Path.Build.Map.is_empty directory_targets + then Memo.return directory_targets + else (match enabled_if with | Blang.Const const -> Memo.return const | _ -> @@ -99,42 +126,27 @@ let extract_directory_targets ~dir stanzas = let* expander = Expander0.get ~dir in Expander0.eval_blang expander enabled_if) >>| (function - | false -> acc - | true -> - List.fold_left l ~init:acc ~f:(fun acc (target, kind) -> - let loc = String_with_vars.loc target in - match (kind : Targets_spec.Kind.t) with - | File -> acc - | Directory -> - (match String_with_vars.text_only target with - | None -> - User_error.raise - ~loc - [ Pp.text "Variables are not allowed in directory targets." ] - | Some target -> - let dir_target = Path.Build.relative ~error_loc:loc dir target in - if Path.Build.is_descendant dir_target ~of_:dir - then - (* We ignore duplicates here as duplicates are detected and - reported by [Load_rules]. *) - Path.Build.Map.set acc dir_target rule_loc - else - (* This will be checked when we interpret the stanza - completely, so just ignore this rule for now. *) - acc))) + | false -> Path.Build.Map.empty + | true -> directory_targets) +;; + +let extract_directory_targets ~dir stanzas = + Memo.parallel_map stanzas ~f:(fun stanza -> + match Stanza.repr stanza with + | Rule_conf.T rule -> directory_targets_of_rule ~dir rule | Coq_stanza.Theory.T m -> (* It's unfortunate that we need to pull in the coq rules here. But we don't have a generic mechanism for this yet. *) Coq_doc.coqdoc_directory_targets ~dir m - >>| Path.Build.Map.union acc ~f:(fun path loc1 loc2 -> - User_error.raise - ~loc:loc1 - [ Pp.textf - "The following both define the same directory target: %s" - (Path.Build.to_string path) - ; Pp.enumerate ~f:Loc.pp_file_colon_line [ loc1; loc2 ] - ]) - | _ -> Memo.return acc) + | _ -> Memo.return Path.Build.Map.empty) + >>| Path.Build.Map.union_all ~f:(fun path loc1 loc2 -> + User_error.raise + ~loc:loc1 + [ Pp.textf + "The following both define the same directory target: %s" + (Path.Build.to_string path) + ; Pp.enumerate ~f:Loc.pp_file_colon_line [ loc1; loc2 ] + ]) ;; module rec DB : sig diff --git a/test/blackbox-tests/test-cases/coq/coqdoc-dir-target-clash.t/run.t b/test/blackbox-tests/test-cases/coq/coqdoc-dir-target-clash.t/run.t index 30c5f76fb36..b0b86ba9654 100644 --- a/test/blackbox-tests/test-cases/coq/coqdoc-dir-target-clash.t/run.t +++ b/test/blackbox-tests/test-cases/coq/coqdoc-dir-target-clash.t/run.t @@ -1,20 +1,20 @@ We try to build the documentation but there will be a clash between the -directory targets. Notice how the tex one fails before html one. +directory targets. $ dune build @check Warning: Coq Language Versions lower than 0.8 have been deprecated in Dune 3.8 and will be removed in an upcoming Dune version. Hint: To disable this warning, add the following to your dune-project file: (warnings (deprecated_coq_lang_lt_08 disabled)) - File "dune", line 9, characters 0-116: - 9 | (rule - 10 | (targets - 11 | (dir base.tex)) - 12 | (action - 13 | (progn - 14 | (run mkdir base.tex) - 15 | (run touch base.tex/base.base.tex)))) + File "dune", line 1, characters 0-120: + 1 | (rule + 2 | (targets + 3 | (dir base.html)) + 4 | (action + 5 | (progn + 6 | (run mkdir base.html) + 7 | (run touch base.html/base.base.html)))) Error: The following both define the same directory target: - _build/default/base.tex - - dune:9 + _build/default/base.html + - dune:1 - dune:17 [1] diff --git a/test/blackbox-tests/test-cases/directory-targets/duplicate-target.t/run.t b/test/blackbox-tests/test-cases/directory-targets/duplicate-target.t/run.t index 16002593d76..52136c5c881 100644 --- a/test/blackbox-tests/test-cases/directory-targets/duplicate-target.t/run.t +++ b/test/blackbox-tests/test-cases/directory-targets/duplicate-target.t/run.t @@ -1,7 +1,12 @@ Duplicate directory targets $ dune build - Error: Multiple rules generated for _build/default/foo: - - dune:5 + File "dune", line 1, characters 0-53: + 1 | (rule + 2 | (targets (dir foo)) + 3 | (action (run mkdir foo))) + Error: The following both define the same directory target: + _build/default/foo - dune:1 + - dune:5 [1]