Repository: mesonbuild/meson

Issue:
Meson, Cmake and PROTOBUF_GENERATE_CPP uses wrong executable
**Describe the bug**
When running PROTOBUF_GENERATE_CPP in a cmake subproject, `protobuf::protoc` is attempted to be used as an executable rather than `protoc`.

**To Reproduce**

1. Clone [this repo](https://github.com/mdegans/libdistance/tree/a974132399ad4f69f6bbe32a85db44e4c848fb2f)
2. Comment out subdirs (since those require proprietary software)
3. attempt to build the "distanceproto" wrap subproject. Output on my system:
```
[4/11] Generating cm_custom_tgt_0 with a custom command
FAILED: subprojects/distanceproto/distance.pb.cc subprojects/distanceproto/distance.pb.h 
/home/mdegans/.local/bin/meson --internal cmake_run_ctgt -o subprojects/distanceproto/distance.pb.cc subprojects/distanceproto/distance.pb.h -O /home/mdegans/Projects/libdistance/builddir/subprojects/distanceproto/__CMake_build/src/distance.pb.cc /home/mdegans/Projects/libdistance/builddir/subprojects/distanceproto/__CMake_build/src/distance.pb.h -d /home/mdegans/Projects/libdistance/builddir/subprojects/distanceproto/__CMake_build/src protobuf::protoc --cpp_out=/home/mdegans/Projects/libdistance/builddir/subprojects/distanceproto/__CMake_build/src -I '/home/mdegans/Projects/libdistance/subprojects/distanceproto/src /home/mdegans/Projects/libdistance/subprojects/distanceproto/src/distance.proto' ';;;'
Traceback (most recent call last):
  File "/home/mdegans/.local/bin/meson", line 8, in <module>
    sys.exit(main())
  File "/home/mdegans/.local/lib/python3.6/site-packages/mesonbuild/mesonmain.py", line 228, in main
    return run(sys.argv[1:], launcher)
  File "/home/mdegans/.local/lib/python3.6/site-packages/mesonbuild/mesonmain.py", line 217, in run
    return run_script_command(args[1], args[2:])
  File "/home/mdegans/.local/lib/python3.6/site-packages/mesonbuild/mesonmain.py", line 165, in run_script_command
    return module.run(script_args)
  File "/home/mdegans/.local/lib/python3.6/site-packages/mesonbuild/scripts/cmake_run_ctgt.py", line 67, in run
    res = subprocess.run(cmd, stdout=stdout, stderr=stderr, cwd=args.directory, check=True)
  File "/usr/lib/python3.6/subprocess.py", line 423, in run
    with Popen(*popenargs, **kwargs) as process:
  File "/usr/lib/python3.6/subprocess.py", line 729, in __init__
    restore_signals, start_new_session)
  File "/usr/lib/python3.6/subprocess.py", line 1364, in _execute_child
    raise child_exception_type(errno_num, err_msg, err_filename)
FileNotFoundError: [Errno 2] No such file or directory: 'protobuf::protoc': 'protobuf::protoc'
[7/11] Compiling C++ object src/libdistance.so.p/FileMetaBroker.cpp.o
ninja: build stopped: subcommand failed.
```

**Expected behavior**
For "protoc" to be called instead of "protobuf::protoc"

**system parameters**
* plain native build.
* Ubuntu 18.04
* Python 3.6.9
* 0.55.0
* 1.8.2

Alternatively (if this is a CMake bug), if the output of a `generator.process()` supported indexing and such generated headers could be installed with `install_headers` I could stop using CMake on the protobuf subproject. Basically I'm trying to isntall generated headers and am not sure if that's possible currently with Meson, so I'm stuck with CMake :/


Discussion:
Related solution for those who might consider switching their subproject to meson as a solution: #7511

Resolving patch:
diff --git a/mesonbuild/cmake/interpreter.py b/mesonbuild/cmake/interpreter.py
--- a/mesonbuild/cmake/interpreter.py
+++ b/mesonbuild/cmake/interpreter.py
@@ -651,7 +651,7 @@ def __init__(self, target: CMakeGeneratorTarget):
     def __repr__(self) -> str:
         return '<{}: {} {}>'.format(self.__class__.__name__, self.name, self.outputs)
 
-    def postprocess(self, output_target_map: OutputTargetMap, root_src_dir: str, subdir: str, all_outputs: T.List[str]) -> None:
+    def postprocess(self, output_target_map: OutputTargetMap, root_src_dir: str, subdir: str, all_outputs: T.List[str], trace: CMakeTraceParser) -> None:
         # Default the working directory to ${CMAKE_CURRENT_BINARY_DIR}
         if not self.working_dir:
             self.working_dir = self.current_bin_dir.as_posix()
@@ -694,7 +694,18 @@ def ensure_absolute(x: Path) -> Path:
                 if not j:
                     continue
                 target = output_target_map.executable(j)
-                cmd += [target] if target else [j]
+                if target:
+                    cmd += [target]
+                    continue
+                elif j in trace.targets:
+                    trace_tgt = trace.targets[j]
+                    if trace_tgt.type == 'EXECUTABLE' and 'IMPORTED_LOCATION' in trace_tgt.properties:
+                        cmd += trace_tgt.properties['IMPORTED_LOCATION']
+                        continue
+                    mlog.debug('CMake: Found invalid CMake target "{}" --> ignoring \n{}'.format(j, trace_tgt))
+
+                # Fallthrough on error
+                cmd += [j]
 
             commands += [cmd]
         self.command = commands
@@ -969,7 +980,7 @@ def analyse(self) -> None:
         object_libs = []
         custom_target_outputs = []  # type: T.List[str]
         for i in self.custom_targets:
-            i.postprocess(self.output_target_map, self.src_dir, self.subdir, custom_target_outputs)
+            i.postprocess(self.output_target_map, self.src_dir, self.subdir, custom_target_outputs, self.trace)
         for i in self.targets:
             i.postprocess(self.output_target_map, self.src_dir, self.subdir, self.install_prefix, self.trace)
             if i.type == 'OBJECT_LIBRARY':
diff --git a/mesonbuild/cmake/traceparser.py b/mesonbuild/cmake/traceparser.py
--- a/mesonbuild/cmake/traceparser.py
+++ b/mesonbuild/cmake/traceparser.py
@@ -269,6 +269,7 @@ def _cmake_add_executable(self, tline: CMakeTraceLine):
         args = list(tline.args) # Make a working copy
 
         # Make sure the exe is imported
+        is_imported = True
         if 'IMPORTED' not in args:
             return self._gen_exception('add_executable', 'non imported executables are not supported', tline)
 
@@ -277,7 +278,7 @@ def _cmake_add_executable(self, tline: CMakeTraceLine):
         if len(args) < 1:
             return self._gen_exception('add_executable', 'requires at least 1 argument', tline)
 
-        self.targets[args[0]] = CMakeTarget(args[0], 'EXECUTABLE', {})
+        self.targets[args[0]] = CMakeTarget(args[0], 'EXECUTABLE', {}, tline=tline, imported=is_imported)
 
     def _cmake_add_library(self, tline: CMakeTraceLine):
         # DOC: https://cmake.org/cmake/help/latest/command/add_library.html


Test patch:
diff --git a/test cases/cmake/11 cmake_module_path/cmake/FindSomethingLikePython.cmake b/test cases/cmake/11 cmake_module_path/cmake/FindSomethingLikePython.cmake
--- a/test cases/cmake/11 cmake_module_path/cmake/FindSomethingLikePython.cmake	
+++ b/test cases/cmake/11 cmake_module_path/cmake/FindSomethingLikePython.cmake	
@@ -1,24 +1,9 @@
 cmake_policy(VERSION 3.7)
 
-if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.12)
-  find_package(Python COMPONENTS Interpreter)
-else()
-  find_package(PythonInterp)
-endif()
-
+find_package(Python COMPONENTS Interpreter)
 if(Python_FOUND OR PYTHONINTERP_FOUND)
   set(SomethingLikePython_FOUND      ON)
   set(SomethingLikePython_EXECUTABLE ${Python_EXECUTABLE})
-
-  if(NOT DEFINED Python_VERSION)
-    set(Python_VERSION ${Python_VERSION_STRING})
-  endif()
-  if(NOT TARGET Python::Interpreter)
-    add_executable(Python::Interpreter IMPORTED)
-    set_target_properties(Python::Interpreter PROPERTIES
-                          IMPORTED_LOCATION ${Python_EXECUTABLE}
-                          VERSION ${Python_VERSION})
-  endif()
 else()
   set(SomethingLikePython_FOUND OFF)
 endif()
diff --git a/test cases/cmake/11 cmake_module_path/meson.build b/test cases/cmake/11 cmake_module_path/meson.build
--- a/test cases/cmake/11 cmake_module_path/meson.build	
+++ b/test cases/cmake/11 cmake_module_path/meson.build	
@@ -1,7 +1,7 @@
 # We use Python3 as it's the only thing guaranteed to be available on any platform Meson can run on (unlike Zlib in linuxlike/13 cmake dependency).
 
 project('user CMake find_package module using cmake_module_path',
-  meson_version: '>= 0.50.0')
+  meson_version: '>= 0.55.0')
 
 if not find_program('cmake', required: false).found()
   error('MESON_SKIP_TEST cmake binary not available.')
@@ -15,3 +15,11 @@ endif
 dependency('SomethingLikePython', required : true, method : 'cmake', cmake_module_path : 'cmake', modules: 'Python::Interpreter')
 
 dependency('SomethingLikePython', method : 'cmake', cmake_module_path : ['doesNotExist', 'cmake'], modules: 'Python::Interpreter')
+
+# Test a custom target with Python::Interpreter in COMMAND
+cm = import('cmake')
+op = cm.subproject_options()
+op.add_cmake_defines({'CMAKE_MODULE_PATH': meson.source_root() / 'cmake'})
+sp = cm.subproject('cmMod', options: op)
+main = sp.target('main')
+test('main', main)
diff --git a/test cases/cmake/11 cmake_module_path/subprojects/cmMod/CMakeLists.txt b/test cases/cmake/11 cmake_module_path/subprojects/cmMod/CMakeLists.txt
new file mode 100644
--- /dev/null
+++ b/test cases/cmake/11 cmake_module_path/subprojects/cmMod/CMakeLists.txt	
@@ -0,0 +1,15 @@
+cmake_minimum_required(VERSION 3.5)
+
+project(cmMod)
+
+message(STATUS "CMAKE_MODULE_PATH: '${CMAKE_MODULE_PATH}'")
+
+find_package(SomethingLikePython REQUIRED)
+
+add_custom_command(
+  OUTPUT            "${CMAKE_CURRENT_BINARY_DIR}/main.c"
+  COMMAND           Python::Interpreter "${CMAKE_CURRENT_SOURCE_DIR}/gen.py"
+  WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}"
+)
+
+add_executable(main "${CMAKE_CURRENT_BINARY_DIR}/main.c")
diff --git a/test cases/cmake/11 cmake_module_path/subprojects/cmMod/gen.py b/test cases/cmake/11 cmake_module_path/subprojects/cmMod/gen.py
new file mode 100644
--- /dev/null
+++ b/test cases/cmake/11 cmake_module_path/subprojects/cmMod/gen.py	
@@ -0,0 +1,9 @@
+with open('main.c', 'w') as fp:
+  print('''
+#include <stdio.h>
+
+int main(void) {
+  printf(\"Hello World\");
+  return 0;
+}
+''', file=fp)
diff --git a/test cases/cmake/11 cmake_module_path/test.json b/test cases/cmake/11 cmake_module_path/test.json
new file mode 100644
--- /dev/null
+++ b/test cases/cmake/11 cmake_module_path/test.json	
@@ -0,0 +1,5 @@
+{
+  "tools": {
+    "cmake": ">=3.12"
+  }
+}