Repository: mesonbuild/meson

Issue:
error when adding nvbench as subproject
Hello,

I'm just getting started with meson and would like to add [nvbench](https://github.com/NVIDIA/nvbench) as a subproject in my meson project using the `cmake` module. Following the examples in the documentation, I cloned the `nvbench` repo under `subprojects` and added the following lines to my top-level meson.build file.

```python
cmake = import('cmake')
opt_var = cmake.subproject_options()
opt_var.add_cmake_defines({'CMAKE_CUDA_ARCHITECTURE': 70})
nvbench_proj = cmake.subproject('nvbench', options: opt_var)
```

However, when running `meson setup`, we seem to get through the cmake configure process just fine and fail with the following error:

```txt
subprojects/nvbench/meson.build:0:0: ERROR: File nvbench does not exist
```

I can see the generated meson.build file under `builddir/subprojects/nvbench`, but I'm not sure what the next steps are from there. Any help would be appreciated!

OS: Ubuntu 20.04
Meson version: 0.62.0
cmake version: 3.22.3


Discussion:
I'm new here too, but I'll see what I can do.  I may just end up being a rubber duck.

Is `nvbench` an installed package, and does `cmake` or `pkg-config` pick it up?  You're using some `cmake` functionality that I'm not familiar with; can you link me to it?
Hey @anadon, thanks for replying!

I'm currently not installing the package, I've just been following the [cmake subprojects](https://mesonbuild.com/CMake-module.html#cmake-subprojects) documentation in Meson, where it has us cloning the project under the `subprojects` directory I believe. As a result, my setup looks something like this:

```txt
<repo-head>
├── builddir
│   ├── meson-logs
│   ├── nvbench
├── subprojects
│   ├── nvbench
│   ├── exec
│   ├── ci
│   ├── scripts
│   ├── docs
│   ├── build
│   ├── cmake
│   ├── examples
│   ├── .git
│   └── testing
│   ├── packagefiles
```
As mentioned above, we seem to get through the configure phase but then the output meson.build file is complaining about files not existing. I've also tried using the provided [cmake2meson.py](https://github.com/mesonbuild/meson/blob/master/tools/cmake2meson.py) script to do the conversion, but that doesn't seem to output anything usable (in fact, that results in syntax errors due to some transmog artifacts I believe).

Thanks again for the help, and let me know if you have any more troubles reproducing :) 

I get a different error:

```
| CMake Error in nvbench/CMakeLists.txt:
| Imported target "nvbench_json" includes non-existent path

| "/include"
| in its INTERFACE_INCLUDE_DIRECTORIES.  Possible reasons include:

| * The path was deleted, renamed, or moved to another location.

| * An install or uninstall procedure did not complete successfully.

| * The installation package was faulty and references files it does not
| provide.

...

meson.build:10:0: ERROR: Failed to configure the CMake subproject
```

The location in question appears to be adding `"${nlohmann_json_SOURCE_DIR}/include"` but without that actually existing. Also that's kind of annoying to do at all, because it should be looking up nlohmann_json's official installable `*-config.cmake` files via find_package(), but instead nvbench is downloading https://github.com/nlohmann/json/releases/download/v3.9.1/include.zip on the fly and copying files out of there.
Strange, it looks like the NVBench authors are doing that so that they can [patch nlohmann/json to work with nvcc](https://github.com/NVIDIA/nvbench/commit/9cfb15be8b4d2c7b679857cc3e58c1ab2ed3fc1c). How should we make forward progress here? 
Not only did the nvbench authors use a locally downloaded copy of nlohmann-json long before adding that patch, but also it seems they never reported the bug to the nlohmann-json project.

Sad.

...

Anyway the problem *here* is that I simply don't understand cmake well enough to guess why it errors out here, so as a result I can't progress far enough to duplicate your issue and therefore have no clue what to even try fixing.

But maybe @mensinda has more ideas.

Resolving patch:
diff --git a/mesonbuild/ast/printer.py b/mesonbuild/ast/printer.py
--- a/mesonbuild/ast/printer.py
+++ b/mesonbuild/ast/printer.py
@@ -29,13 +29,14 @@
 }
 
 class AstPrinter(AstVisitor):
-    def __init__(self, indent: int = 2, arg_newline_cutoff: int = 5):
+    def __init__(self, indent: int = 2, arg_newline_cutoff: int = 5, update_ast_line_nos: bool = False):
         self.result = ''
         self.indent = indent
         self.arg_newline_cutoff = arg_newline_cutoff
         self.ci = ''
         self.is_newline = True
         self.last_level = 0
+        self.curr_line = 1 if update_ast_line_nos else None
 
     def post_process(self) -> None:
         self.result = re.sub(r'\s+\n', '\n', self.result)
@@ -55,37 +56,48 @@ def append_padded(self, data: str, node: mparser.BaseNode) -> None:
     def newline(self) -> None:
         self.result += '\n'
         self.is_newline = True
+        if self.curr_line is not None:
+            self.curr_line += 1
 
     def visit_BooleanNode(self, node: mparser.BooleanNode) -> None:
         self.append('true' if node.value else 'false', node)
+        node.lineno = self.curr_line or node.lineno
 
     def visit_IdNode(self, node: mparser.IdNode) -> None:
         assert isinstance(node.value, str)
         self.append(node.value, node)
+        node.lineno = self.curr_line or node.lineno
 
     def visit_NumberNode(self, node: mparser.NumberNode) -> None:
         self.append(str(node.value), node)
+        node.lineno = self.curr_line or node.lineno
 
     def visit_StringNode(self, node: mparser.StringNode) -> None:
         assert isinstance(node.value, str)
         self.append("'" + node.value + "'", node)
+        node.lineno = self.curr_line or node.lineno
 
     def visit_FormatStringNode(self, node: mparser.FormatStringNode) -> None:
         assert isinstance(node.value, str)
         self.append("f'" + node.value + "'", node)
+        node.lineno = self.curr_line or node.lineno
 
     def visit_ContinueNode(self, node: mparser.ContinueNode) -> None:
         self.append('continue', node)
+        node.lineno = self.curr_line or node.lineno
 
     def visit_BreakNode(self, node: mparser.BreakNode) -> None:
         self.append('break', node)
+        node.lineno = self.curr_line or node.lineno
 
     def visit_ArrayNode(self, node: mparser.ArrayNode) -> None:
+        node.lineno = self.curr_line or node.lineno
         self.append('[', node)
         node.args.accept(self)
         self.append(']', node)
 
     def visit_DictNode(self, node: mparser.DictNode) -> None:
+        node.lineno = self.curr_line or node.lineno
         self.append('{', node)
         node.args.accept(self)
         self.append('}', node)
@@ -93,58 +105,70 @@ def visit_DictNode(self, node: mparser.DictNode) -> None:
     def visit_OrNode(self, node: mparser.OrNode) -> None:
         node.left.accept(self)
         self.append_padded('or', node)
+        node.lineno = self.curr_line or node.lineno
         node.right.accept(self)
 
     def visit_AndNode(self, node: mparser.AndNode) -> None:
         node.left.accept(self)
         self.append_padded('and', node)
+        node.lineno = self.curr_line or node.lineno
         node.right.accept(self)
 
     def visit_ComparisonNode(self, node: mparser.ComparisonNode) -> None:
         node.left.accept(self)
         self.append_padded(node.ctype, node)
+        node.lineno = self.curr_line or node.lineno
         node.right.accept(self)
 
     def visit_ArithmeticNode(self, node: mparser.ArithmeticNode) -> None:
         node.left.accept(self)
         self.append_padded(arithmic_map[node.operation], node)
+        node.lineno = self.curr_line or node.lineno
         node.right.accept(self)
 
     def visit_NotNode(self, node: mparser.NotNode) -> None:
+        node.lineno = self.curr_line or node.lineno
         self.append_padded('not', node)
         node.value.accept(self)
 
     def visit_CodeBlockNode(self, node: mparser.CodeBlockNode) -> None:
+        node.lineno = self.curr_line or node.lineno
         for i in node.lines:
             i.accept(self)
             self.newline()
 
     def visit_IndexNode(self, node: mparser.IndexNode) -> None:
         node.iobject.accept(self)
+        node.lineno = self.curr_line or node.lineno
         self.append('[', node)
         node.index.accept(self)
         self.append(']', node)
 
     def visit_MethodNode(self, node: mparser.MethodNode) -> None:
+        node.lineno = self.curr_line or node.lineno
         node.source_object.accept(self)
         self.append('.' + node.name + '(', node)
         node.args.accept(self)
         self.append(')', node)
 
     def visit_FunctionNode(self, node: mparser.FunctionNode) -> None:
+        node.lineno = self.curr_line or node.lineno
         self.append(node.func_name + '(', node)
         node.args.accept(self)
         self.append(')', node)
 
     def visit_AssignmentNode(self, node: mparser.AssignmentNode) -> None:
+        node.lineno = self.curr_line or node.lineno
         self.append(node.var_name + ' = ', node)
         node.value.accept(self)
 
     def visit_PlusAssignmentNode(self, node: mparser.PlusAssignmentNode) -> None:
+        node.lineno = self.curr_line or node.lineno
         self.append(node.var_name + ' += ', node)
         node.value.accept(self)
 
     def visit_ForeachClauseNode(self, node: mparser.ForeachClauseNode) -> None:
+        node.lineno = self.curr_line or node.lineno
         varnames = [x for x in node.varnames]
         self.append_padded('foreach', node)
         self.append_padded(', '.join(varnames), node)
@@ -155,6 +179,7 @@ def visit_ForeachClauseNode(self, node: mparser.ForeachClauseNode) -> None:
         self.append('endforeach', node)
 
     def visit_IfClauseNode(self, node: mparser.IfClauseNode) -> None:
+        node.lineno = self.curr_line or node.lineno
         prefix = ''
         for i in node.ifs:
             self.append_padded(prefix + 'if', node)
@@ -166,15 +191,18 @@ def visit_IfClauseNode(self, node: mparser.IfClauseNode) -> None:
         self.append('endif', node)
 
     def visit_UMinusNode(self, node: mparser.UMinusNode) -> None:
+        node.lineno = self.curr_line or node.lineno
         self.append_padded('-', node)
         node.value.accept(self)
 
     def visit_IfNode(self, node: mparser.IfNode) -> None:
+        node.lineno = self.curr_line or node.lineno
         node.condition.accept(self)
         self.newline()
         node.block.accept(self)
 
     def visit_TernaryNode(self, node: mparser.TernaryNode) -> None:
+        node.lineno = self.curr_line or node.lineno
         node.condition.accept(self)
         self.append_padded('?', node)
         node.trueblock.accept(self)
@@ -182,6 +210,7 @@ def visit_TernaryNode(self, node: mparser.TernaryNode) -> None:
         node.falseblock.accept(self)
 
     def visit_ArgumentNode(self, node: mparser.ArgumentNode) -> None:
+        node.lineno = self.curr_line or node.lineno
         break_args = (len(node.arguments) + len(node.kwargs)) > self.arg_newline_cutoff
         for i in node.arguments + list(node.kwargs.values()):
             if not isinstance(i, (mparser.ElementaryNode, mparser.IndexNode)):
diff --git a/mesonbuild/cmake/interpreter.py b/mesonbuild/cmake/interpreter.py
--- a/mesonbuild/cmake/interpreter.py
+++ b/mesonbuild/cmake/interpreter.py
@@ -714,7 +714,7 @@ def ensure_absolute(x: Path) -> Path:
             # targets, etc. This reduces the chance of misdetecting input files
             # as outputs from other targets.
             # See https://github.com/mesonbuild/meson/issues/6632
-            if not raw.is_absolute() and (self.current_src_dir / raw).exists():
+            if not raw.is_absolute() and (self.current_src_dir / raw).is_file():
                 self.inputs += [(self.current_src_dir / raw).relative_to(root_src_dir).as_posix()]
             elif raw.is_absolute() and raw.exists() and rel_to_root is not None:
                 self.inputs += [rel_to_root.as_posix()]
diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py
--- a/mesonbuild/interpreter/interpreter.py
+++ b/mesonbuild/interpreter/interpreter.py
@@ -992,7 +992,7 @@ def _do_subproject_cmake(self, subp_name: str, subdir: str, subdir_abs: str,
 
                 # Debug print the generated meson file
                 from ..ast import AstIndentationGenerator, AstPrinter
-                printer = AstPrinter()
+                printer = AstPrinter(update_ast_line_nos=True)
                 ast.accept(AstIndentationGenerator())
                 ast.accept(printer)
                 printer.post_process()


Test patch:
diff --git a/test cases/cmake/8 custom command/subprojects/cmMod/CMakeLists.txt b/test cases/cmake/8 custom command/subprojects/cmMod/CMakeLists.txt
--- a/test cases/cmake/8 custom command/subprojects/cmMod/CMakeLists.txt	
+++ b/test cases/cmake/8 custom command/subprojects/cmMod/CMakeLists.txt	
@@ -157,3 +157,7 @@ add_custom_target(clang-format COMMAND clang-format -i cmMod.cpp)
 
 add_dependencies(cmModLib args_test_cmd tgtCpyTest4)
 add_dependencies(args_test_cmd macro_name_cmd;gen;mycpy)
+
+# Reproduce https://github.com/mesonbuild/meson/issues/10244
+add_custom_target(mycpy.all)
+add_dependencies(mycpy.all mycpy)
diff --git a/test cases/cmake/8 custom command/subprojects/cmMod/mycpy/.gitkeep b/test cases/cmake/8 custom command/subprojects/cmMod/mycpy/.gitkeep
new file mode 100644
--- /dev/null
+++ b/test cases/cmake/8 custom command/subprojects/cmMod/mycpy/.gitkeep	
@@ -0,0 +1 @@
+# Required to reproduce https://github.com/mesonbuild/meson/issues/10244