prompt: We are currently solving the following issue within our repository. Here is the issue text:
  --- BEGIN ISSUE ---
  run_target improperly handles command if 'python' is in meson's installation path
  My build environment is RHEL 7.6 with the python 3.6 Red Hat software collection enabled in my .bashrc.  Meson was installed by running ``pip3.6 install .`` as root in the ``meson-0.49.0/`` folder from a fresh download.  (This issue also happens in meson 48.)
  
  I was setting up a custom run_target to run cppcheck with the command
  ```
  run_target('cppcheck', command : ['cppcheck', '--project=' + join_paths(meson.build_root(), 'compile_commands.json')])
  ```
  but noticed that it was failing to run with the following error message:
  ```
  Could not execute command "--project=[build dir]/compile_commands.json". File not found.
  FAILED: meson-cppcheck
  /opt/rh/rh-python36/root/usr/bin/meson --internal commandrunner [src dir] [build dir] '' /opt/rh/rh-python36/root/usr/bin/meson cppcheck --project=[build dir]/compile_commands.json
  ninja: build stopped: subcommand failed
  ```
  
  I traced the issue for a bit and it the issue appears to be in ``commandrunner.py`` line 61 (ironically the line with the comment "Hack." haha): it looks for the presence of the string ``python`` in the meson path, presumably to catch instances of meson being launched with a ``python3 meson`` call.  However it ignores the possibility that python is simply a part of the path to the meson executable.  When this happens, the commandrunner script grabs the fifth argument and assumes that it's part of the meson command, which is wrong.
  
  I'm currently working around the issue by inserting an empty argument into the run_target command, like this:
  ```
  run_target('cppcheck', command : ['', 'cppcheck', '--project=' + join_paths(meson.build_root(), 'compile_commands.json')])
  ```
  
  --- END ISSUE ---
  
  Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
  --- BEGIN FILES ---
  Path: `mesonbuild/scripts/commandrunner.py`
  Content:
  ```
  1 # Copyright 2014 The Meson development team
  2 
  3 # Licensed under the Apache License, Version 2.0 (the "License");
  4 # you may not use this file except in compliance with the License.
  5 # You may obtain a copy of the License at
  6 
  7 #     http://www.apache.org/licenses/LICENSE-2.0
  8 
  9 # Unless required by applicable law or agreed to in writing, software
  10 # distributed under the License is distributed on an "AS IS" BASIS,
  11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12 # See the License for the specific language governing permissions and
  13 # limitations under the License.
  14 
  15 """This program is a wrapper to run external commands. It determines
  16 what to run, sets up the environment and executes the command."""
  17 
  18 import sys, os, subprocess, shutil, shlex
  19 
  20 def run_command(source_dir, build_dir, subdir, meson_command, command, arguments):
  21     env = {'MESON_SOURCE_ROOT': source_dir,
  22            'MESON_BUILD_ROOT': build_dir,
  23            'MESON_SUBDIR': subdir,
  24            'MESONINTROSPECT': ' '.join([shlex.quote(x) for x in meson_command + ['introspect']]),
  25            }
  26     cwd = os.path.join(source_dir, subdir)
  27     child_env = os.environ.copy()
  28     child_env.update(env)
  29 
  30     # Is the command an executable in path?
  31     exe = shutil.which(command)
  32     if exe is not None:
  33         command_array = [exe] + arguments
  34     else:# No? Maybe it is a script in the source tree.
  35         fullpath = os.path.join(source_dir, subdir, command)
  36         command_array = [fullpath] + arguments
  37     try:
  38         return subprocess.Popen(command_array, env=child_env, cwd=cwd)
  39     except FileNotFoundError:
  40         print('Could not execute command "%s". File not found.' % command)
  41         sys.exit(1)
  42     except PermissionError:
  43         print('Could not execute command "%s". File not executable.' % command)
  44         sys.exit(1)
  45     except OSError as err:
  46         print('Could not execute command "{}": {}'.format(command, err))
  47         sys.exit(1)
  48     except subprocess.SubprocessError as err:
  49         print('Could not execute command "{}": {}'.format(command, err))
  50         sys.exit(1)
  51 
  52 
  53 def run(args):
  54     if len(args) < 4:
  55         print('commandrunner.py <source dir> <build dir> <subdir> <command> [arguments]')
  56         return 1
  57     src_dir = args[0]
  58     build_dir = args[1]
  59     subdir = args[2]
  60     meson_command = args[3]
  61     if 'python' in meson_command: # Hack.
  62         meson_command = [meson_command, args[4]]
  63         command = args[5]
  64         arguments = args[6:]
  65     else:
  66         meson_command = [meson_command]
  67         command = args[4]
  68         arguments = args[5:]
  69     pc = run_command(src_dir, build_dir, subdir, meson_command, command, arguments)
  70     pc.wait()
  71     return pc.returncode
  72 
  73 if __name__ == '__main__':
  74     sys.exit(run(sys.argv[1:]))
  75
  ```
  --- END FILES ---
  
  Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.
  
  Here is an example:
  
  ```diff
  diff --git a/examples/server_async.py b/examples/server_async.py
  --- a/examples/server_async.py
  +++ b/examples/server_async.py
  @@ -313,4 +313,4 @@
   
   
   if __name__ == "__main__":
  -    asyncio.run(run_async_server("."), debug=True)
  +    asyncio.run(run_async_server(), debug=True)
  diff --git a/examples/server_sync.py b/examples/server_sync.py
  --- a/examples/server_sync.py
  +++ b/examples/server_sync.py
  @@ -313,5 +313,5 @@
   
   
   if __name__ == "__main__":
  -    server = run_sync_server(".")
  +    server = run_sync_server()
       server.shutdown()
  
  ```
  
golden_diff: diff --git a/mesonbuild/scripts/commandrunner.py b/mesonbuild/scripts/commandrunner.py
  --- a/mesonbuild/scripts/commandrunner.py
  +++ b/mesonbuild/scripts/commandrunner.py
  @@ -16,6 +16,7 @@
   what to run, sets up the environment and executes the command."""
   
   import sys, os, subprocess, shutil, shlex
  +import re
   
   def run_command(source_dir, build_dir, subdir, meson_command, command, arguments):
       env = {'MESON_SOURCE_ROOT': source_dir,
  @@ -49,6 +50,9 @@
           print('Could not execute command "{}": {}'.format(command, err))
           sys.exit(1)
   
  +def is_python_command(cmdname):
  +    end_py_regex = r'python(3|3\.\d+)?(\.exe)?$'
  +    return re.search(end_py_regex, cmdname) is not None
   
   def run(args):
       if len(args) < 4:
  @@ -58,7 +62,7 @@
       build_dir = args[1]
       subdir = args[2]
       meson_command = args[3]
  -    if 'python' in meson_command: # Hack.
  +    if is_python_command(meson_command):
           meson_command = [meson_command, args[4]]
           command = args[5]
           arguments = args[6:]
  
verification_info: {"golden_diff": "diff --git a/mesonbuild/scripts/commandrunner.py b/mesonbuild/scripts/commandrunner.py\n--- a/mesonbuild/scripts/commandrunner.py\n+++ b/mesonbuild/scripts/commandrunner.py\n@@ -16,6 +16,7 @@\n what to run, sets up the environment and executes the command.\"\"\"\n \n import sys, os, subprocess, shutil, shlex\n+import re\n \n def run_command(source_dir, build_dir, subdir, meson_command, command, arguments):\n     env = {'MESON_SOURCE_ROOT': source_dir,\n@@ -49,6 +50,9 @@\n         print('Could not execute command \"{}\": {}'.format(command, err))\n         sys.exit(1)\n \n+def is_python_command(cmdname):\n+    end_py_regex = r'python(3|3\\.\\d+)?(\\.exe)?$'\n+    return re.search(end_py_regex, cmdname) is not None\n \n def run(args):\n     if len(args) < 4:\n@@ -58,7 +62,7 @@\n     build_dir = args[1]\n     subdir = args[2]\n     meson_command = args[3]\n-    if 'python' in meson_command: # Hack.\n+    if is_python_command(meson_command):\n         meson_command = [meson_command, args[4]]\n         command = args[5]\n         arguments = args[6:]\n", "issue": "run_target improperly handles command if 'python' is in meson's installation path\nMy build environment is RHEL 7.6 with the python 3.6 Red Hat software collection enabled in my .bashrc.  Meson was installed by running ``pip3.6 install .`` as root in the ``meson-0.49.0/`` folder from a fresh download.  (This issue also happens in meson 48.)\r\n\r\nI was setting up a custom run_target to run cppcheck with the command\r\n```\r\nrun_target('cppcheck', command : ['cppcheck', '--project=' + join_paths(meson.build_root(), 'compile_commands.json')])\r\n```\r\nbut noticed that it was failing to run with the following error message:\r\n```\r\nCould not execute command \"--project=[build dir]/compile_commands.json\". File not found.\r\nFAILED: meson-cppcheck\r\n/opt/rh/rh-python36/root/usr/bin/meson --internal commandrunner [src dir] [build dir] '' /opt/rh/rh-python36/root/usr/bin/meson cppcheck --project=[build dir]/compile_commands.json\r\nninja: build stopped: subcommand failed\r\n```\r\n\r\nI traced the issue for a bit and it the issue appears to be in ``commandrunner.py`` line 61 (ironically the line with the comment \"Hack.\" haha): it looks for the presence of the string ``python`` in the meson path, presumably to catch instances of meson being launched with a ``python3 meson`` call.  However it ignores the possibility that python is simply a part of the path to the meson executable.  When this happens, the commandrunner script grabs the fifth argument and assumes that it's part of the meson command, which is wrong.\r\n\r\nI'm currently working around the issue by inserting an empty argument into the run_target command, like this:\r\n```\r\nrun_target('cppcheck', command : ['', 'cppcheck', '--project=' + join_paths(meson.build_root(), 'compile_commands.json')])\r\n```\n", "before_files": [{"content": "# Copyright 2014 The Meson development team\n\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n\n#     http://www.apache.org/licenses/LICENSE-2.0\n\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n\"\"\"This program is a wrapper to run external commands. It determines\nwhat to run, sets up the environment and executes the command.\"\"\"\n\nimport sys, os, subprocess, shutil, shlex\n\ndef run_command(source_dir, build_dir, subdir, meson_command, command, arguments):\n    env = {'MESON_SOURCE_ROOT': source_dir,\n           'MESON_BUILD_ROOT': build_dir,\n           'MESON_SUBDIR': subdir,\n           'MESONINTROSPECT': ' '.join([shlex.quote(x) for x in meson_command + ['introspect']]),\n           }\n    cwd = os.path.join(source_dir, subdir)\n    child_env = os.environ.copy()\n    child_env.update(env)\n\n    # Is the command an executable in path?\n    exe = shutil.which(command)\n    if exe is not None:\n        command_array = [exe] + arguments\n    else:# No? Maybe it is a script in the source tree.\n        fullpath = os.path.join(source_dir, subdir, command)\n        command_array = [fullpath] + arguments\n    try:\n        return subprocess.Popen(command_array, env=child_env, cwd=cwd)\n    except FileNotFoundError:\n        print('Could not execute command \"%s\". File not found.' % command)\n        sys.exit(1)\n    except PermissionError:\n        print('Could not execute command \"%s\". File not executable.' % command)\n        sys.exit(1)\n    except OSError as err:\n        print('Could not execute command \"{}\": {}'.format(command, err))\n        sys.exit(1)\n    except subprocess.SubprocessError as err:\n        print('Could not execute command \"{}\": {}'.format(command, err))\n        sys.exit(1)\n\n\ndef run(args):\n    if len(args) < 4:\n        print('commandrunner.py <source dir> <build dir> <subdir> <command> [arguments]')\n        return 1\n    src_dir = args[0]\n    build_dir = args[1]\n    subdir = args[2]\n    meson_command = args[3]\n    if 'python' in meson_command: # Hack.\n        meson_command = [meson_command, args[4]]\n        command = args[5]\n        arguments = args[6:]\n    else:\n        meson_command = [meson_command]\n        command = args[4]\n        arguments = args[5:]\n    pc = run_command(src_dir, build_dir, subdir, meson_command, command, arguments)\n    pc.wait()\n    return pc.returncode\n\nif __name__ == '__main__':\n    sys.exit(run(sys.argv[1:]))\n", "path": "mesonbuild/scripts/commandrunner.py"}], "after_files": [{"content": "# Copyright 2014 The Meson development team\n\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n\n#     http://www.apache.org/licenses/LICENSE-2.0\n\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n\"\"\"This program is a wrapper to run external commands. It determines\nwhat to run, sets up the environment and executes the command.\"\"\"\n\nimport sys, os, subprocess, shutil, shlex\nimport re\n\ndef run_command(source_dir, build_dir, subdir, meson_command, command, arguments):\n    env = {'MESON_SOURCE_ROOT': source_dir,\n           'MESON_BUILD_ROOT': build_dir,\n           'MESON_SUBDIR': subdir,\n           'MESONINTROSPECT': ' '.join([shlex.quote(x) for x in meson_command + ['introspect']]),\n           }\n    cwd = os.path.join(source_dir, subdir)\n    child_env = os.environ.copy()\n    child_env.update(env)\n\n    # Is the command an executable in path?\n    exe = shutil.which(command)\n    if exe is not None:\n        command_array = [exe] + arguments\n    else:# No? Maybe it is a script in the source tree.\n        fullpath = os.path.join(source_dir, subdir, command)\n        command_array = [fullpath] + arguments\n    try:\n        return subprocess.Popen(command_array, env=child_env, cwd=cwd)\n    except FileNotFoundError:\n        print('Could not execute command \"%s\". File not found.' % command)\n        sys.exit(1)\n    except PermissionError:\n        print('Could not execute command \"%s\". File not executable.' % command)\n        sys.exit(1)\n    except OSError as err:\n        print('Could not execute command \"{}\": {}'.format(command, err))\n        sys.exit(1)\n    except subprocess.SubprocessError as err:\n        print('Could not execute command \"{}\": {}'.format(command, err))\n        sys.exit(1)\n\ndef is_python_command(cmdname):\n    end_py_regex = r'python(3|3\\.\\d+)?(\\.exe)?$'\n    return re.search(end_py_regex, cmdname) is not None\n\ndef run(args):\n    if len(args) < 4:\n        print('commandrunner.py <source dir> <build dir> <subdir> <command> [arguments]')\n        return 1\n    src_dir = args[0]\n    build_dir = args[1]\n    subdir = args[2]\n    meson_command = args[3]\n    if is_python_command(meson_command):\n        meson_command = [meson_command, args[4]]\n        command = args[5]\n        arguments = args[6:]\n    else:\n        meson_command = [meson_command]\n        command = args[4]\n        arguments = args[5:]\n    pc = run_command(src_dir, build_dir, subdir, meson_command, command, arguments)\n    pc.wait()\n    return pc.returncode\n\nif __name__ == '__main__':\n    sys.exit(run(sys.argv[1:]))\n", "path": "mesonbuild/scripts/commandrunner.py"}]}