Repository: mesonbuild/meson

Issue:
Missing implicit dependency to generated code
**Describe the bug**
When using the result of `custom_target` as source to a build target, an implicit dependency (or order-only dependency?) should be added to each object file to ensure that the generated code is built before the other sources, because the other sources may depend on the generated code.

**To Reproduce**
Try to build the project https://github.com/rtbo/meson-dep-test (meson.build copied hereafter).
Compilation of `lib.d` races with compilation of `generated.d` and cannot find module `generated`.
If I manually add `generated.o` as implicit dependency of `lib.o` in `ninja.build`, everything is file.

```meson
project('meson-dep-test', 'd')

generator = executable('generator', 'generator.d')

generated = custom_target('generated',
    capture: true,
    output: 'generated.d',
    input: 'input.txt',
    command: [
        generator, '@INPUT@'
    ]
)

lib = static_library('lib', generated, 'lib.d',
    include_directories: include_directories('.'),
)
```

**Expected behavior**
Reliable build without race

**system parameters**
Arch Linux
meson master branch (0.62.99)
Python 3.10.4
Ninja 1.11.0



Discussion:

Resolving patch:
diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py
--- a/mesonbuild/backend/ninjabackend.py
+++ b/mesonbuild/backend/ninjabackend.py
@@ -833,6 +833,11 @@ def generate_target(self, target):
                 # people generate files with weird suffixes (.inc, .fh) that they then include
                 # in their source files.
                 header_deps.append(raw_src)
+
+        # For D language, the object of generated source files are added
+        # as order only deps because other files may depend on them
+        d_generated_deps = []
+
         # These are the generated source files that need to be built for use by
         # this target. We create the Ninja build file elements for this here
         # because we need `header_deps` to be fully generated in the above loop.
@@ -845,6 +850,8 @@ def generate_target(self, target):
             compiled_sources.append(s)
             source2object[s] = o
             obj_list.append(o)
+            if s.split('.')[-1] in compilers.lang_suffixes['d']:
+                d_generated_deps.append(o)
 
         use_pch = self.environment.coredata.options.get(OptionKey('b_pch'))
         if use_pch and target.has_pch():
@@ -891,7 +898,7 @@ def generate_target(self, target):
                                            src.rel_to_builddir(self.build_to_src))
                     unity_src.append(abs_src)
                 else:
-                    o, s = self.generate_single_compile(target, src, False, [], header_deps)
+                    o, s = self.generate_single_compile(target, src, False, [], header_deps + d_generated_deps)
                     obj_list.append(o)
                     compiled_sources.append(s)
                     source2object[s] = o
@@ -899,7 +906,7 @@ def generate_target(self, target):
         obj_list += self.flatten_object_list(target)
         if is_unity:
             for src in self.generate_unity_files(target, unity_src):
-                o, s = self.generate_single_compile(target, src, True, unity_deps + header_deps)
+                o, s = self.generate_single_compile(target, src, True, unity_deps + header_deps + d_generated_deps)
                 obj_list.append(o)
                 compiled_sources.append(s)
                 source2object[s] = o


Test patch:
diff --git a/test cases/d/16 code generation/exe.d b/test cases/d/16 code generation/exe.d
new file mode 100644
--- /dev/null
+++ b/test cases/d/16 code generation/exe.d	
@@ -0,0 +1,9 @@
+module exe;
+
+import generated;
+import std.stdio;
+
+int main()
+{
+    return generatedString() == "Some text to be returned by generated code" ? 0 : 1;
+}
diff --git a/test cases/d/16 code generation/generator.d b/test cases/d/16 code generation/generator.d
new file mode 100644
--- /dev/null
+++ b/test cases/d/16 code generation/generator.d	
@@ -0,0 +1,13 @@
+module generator;
+
+import std.file;
+import std.stdio;
+import std.string;
+
+void main(string[] args)
+{
+    const text = cast(string)read(args[1]);
+
+    writeln("module generated;");
+    writefln!`string generatedString() { return "%s"; }`(text.strip());
+}
diff --git a/test cases/d/16 code generation/input.txt b/test cases/d/16 code generation/input.txt
new file mode 100644
--- /dev/null
+++ b/test cases/d/16 code generation/input.txt	
@@ -0,0 +1 @@
+Some text to be returned by generated code
diff --git a/test cases/d/16 code generation/meson.build b/test cases/d/16 code generation/meson.build
new file mode 100644
--- /dev/null
+++ b/test cases/d/16 code generation/meson.build	
@@ -0,0 +1,18 @@
+project('meson-dep-test', 'd')
+
+generator = executable('generator', 'generator.d')
+
+generated = custom_target('generated',
+    capture: true,
+    output: 'generated.d',
+    input: 'input.txt',
+    command: [
+        generator, '@INPUT@'
+    ]
+)
+
+exe = executable('exe', generated, 'exe.d',
+    include_directories: include_directories('.'),
+)
+
+test('test exe', exe)