Repository: mesonbuild/meson

Issue:
Subprojects don't work when git is missing on the system
I was trying to use meson on msys2 on Windows. Some things seemed to work, but meson reported
```
Dependency GTest found: NO
Also couldn't find a fallback subproject in subprojects/gtest for the dependency gtest

Meson encountered an error in file test/unittest/meson.build, line 1, column 0:
Dependency "gtest" not found
```
despite using
```meson
gtest = dependency('gtest', fallback : ['gtest', 'gtest_main_dep'], main : true)
```
and having a valid `subprojects/gtest.wrap` file:
```
[wrap-file]
directory = googletest-release-1.8.0

source_url = https://github.com/google/googletest/archive/release-1.8.0.zip
source_filename = gtest-1.8.0.zip
source_hash = f3ed3b58511efd272eb074a3a6d6fb79d7c2e6a0e374323d1e6bcbcc1ef141bf

patch_url = https://wrapdb.mesonbuild.com/v1/projects/gtest/1.8.0/4/get_zip
patch_filename = gtest-1.8.0-4-wrap.zip
patch_hash = 0b90fe055acbdb002a37dfb035184b306008b763931158497ef5dbaa8c7925af
```

After some debugging it turned out that `resolved = r.resolve(dirname)` was raising a `FileNotFoundError`. Further debugging revealed that the following line was a culprit
```python
def quiet_git(cmd, workingdir):
    pc = subprocess.Popen(['git', '-C', workingdir] + cmd,
                          stdout=subprocess.PIPE, stderr=subprocess.PIPE)
    ...
```
which was called from `resolve_git_submodule()` from `resolve()`.

Btw, since I have put some more debugging info to the sources, here's an interesting error that was properly intercepted:
```
fatal: Not a git repository (or any parent up to mount point /z)\nStopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).\n
```

After I installed `git`, everything started working (note that my code was not even in a git repository).

I believe that `meson`'s functionality should not depend on existence of `git` and that the failure to find `git` should be handled properly.


Discussion:
I had a traceback when building in a sandbox without git available. I believe this is the same issue. Even setting `--wrap-mode=nodownload`, I still get the same error. Having git available, I get a meaningful error message with nodownload. (This is with meson 0.48.2)
```
Traceback (most recent call last):
  File "/usr/lib/python3.7/site-packages/mesonbuild/mesonmain.py", line 364, in run
    app.generate()
  File "/usr/lib/python3.7/site-packages/mesonbuild/mesonmain.py", line 136, in generate
    self._generate(env)
  File "/usr/lib/python3.7/site-packages/mesonbuild/mesonmain.py", line 165, in _generate
    intr.run()
  File "/usr/lib/python3.7/site-packages/mesonbuild/interpreter.py", line 3836, in run
    super().run()
  File "/usr/lib/python3.7/site-packages/mesonbuild/interpreterbase.py", line 380, in run
    self.evaluate_codeblock(self.ast, start=1)
  File "/usr/lib/python3.7/site-packages/mesonbuild/interpreterbase.py", line 404, in evaluate_codeblock
    raise e
  File "/usr/lib/python3.7/site-packages/mesonbuild/interpreterbase.py", line 398, in evaluate_codeblock
    self.evaluate_statement(cur)
  File "/usr/lib/python3.7/site-packages/mesonbuild/interpreterbase.py", line 419, in evaluate_statement
    return self.evaluate_if(cur)
  File "/usr/lib/python3.7/site-packages/mesonbuild/interpreterbase.py", line 480, in evaluate_if
    self.evaluate_codeblock(i.block)
  File "/usr/lib/python3.7/site-packages/mesonbuild/interpreterbase.py", line 404, in evaluate_codeblock
    raise e
  File "/usr/lib/python3.7/site-packages/mesonbuild/interpreterbase.py", line 398, in evaluate_codeblock
    self.evaluate_statement(cur)
  File "/usr/lib/python3.7/site-packages/mesonbuild/interpreterbase.py", line 411, in evaluate_statement
    return self.assignment(cur)
  File "/usr/lib/python3.7/site-packages/mesonbuild/interpreterbase.py", line 946, in assignment
    value = self.evaluate_statement(node.value)
  File "/usr/lib/python3.7/site-packages/mesonbuild/interpreterbase.py", line 413, in evaluate_statement
    return self.method_call(cur)
  File "/usr/lib/python3.7/site-packages/mesonbuild/interpreterbase.py", line 715, in method_call
    obj = self.evaluate_statement(invokable)
  File "/usr/lib/python3.7/site-packages/mesonbuild/interpreterbase.py", line 409, in evaluate_statement
    return self.function_call(cur)
  File "/usr/lib/python3.7/site-packages/mesonbuild/interpreterbase.py", line 705, in function_call
    return func(node, posargs, kwargs)
  File "/usr/lib/python3.7/site-packages/mesonbuild/interpreterbase.py", line 273, in wrapped
    return f(*wrapped_args, **wrapped_kwargs)
  File "/usr/lib/python3.7/site-packages/mesonbuild/interpreterbase.py", line 162, in wrapped
    return f(*wrapped_args, **wrapped_kwargs)
  File "/usr/lib/python3.7/site-packages/mesonbuild/interpreterbase.py", line 133, in wrapped
    return f(*wrapped_args, **wrapped_kwargs)
  File "/usr/lib/python3.7/site-packages/mesonbuild/interpreter.py", line 2278, in func_subproject
    return self.do_subproject(dirname, kwargs)
  File "/usr/lib/python3.7/site-packages/mesonbuild/interpreter.py", line 2317, in do_subproject
    resolved = r.resolve(dirname)
  File "/usr/lib/python3.7/site-packages/mesonbuild/wrap/wrap.py", line 127, in resolve
    if self.resolve_git_submodule(dirname):
  File "/usr/lib/python3.7/site-packages/mesonbuild/wrap/wrap.py", line 160, in resolve_git_submodule
    ret, out = quiet_git(['rev-parse'], self.subdir_root)
  File "/usr/lib/python3.7/site-packages/mesonbuild/wrap/wrap.py", line 45, in quiet_git
    stdout=subprocess.PIPE, stderr=subprocess.PIPE)
  File "/usr/lib/python3.7/subprocess.py", line 756, in __init__
    restore_signals, start_new_session)
  File "/usr/lib/python3.7/subprocess.py", line 1499, in _execute_child
    raise child_exception_type(errno_num, err_msg, err_filename)
FileNotFoundError: [Errno 2] No such file or directory: 'git': 'git'
```
That code changed a lot in master, can you try with that please?
That traceback should be fixed by #4543; not sure about the original issue.
This is still relevant; there is no traceback, meson simply aborts with:

```
meson.build:2:0: ERROR: Git program not found.
```

https://github.com/mesonbuild/meson/blob/9e3bb9eea395f01013c6b606ff23438729c2fb81/mesonbuild/wrap/wrap.py#L355-L359

I think it would be reasonable to return false here; are we truly concerned with "yes, this is a git repository, but the git program is not installed -- it was copied over USB"? The function only ever tries to give a best-faith effort to check out a submodule, doing nothing is a reasonable fallback IMO.

Resolving patch:
diff --git a/mesonbuild/wrap/wrap.py b/mesonbuild/wrap/wrap.py
--- a/mesonbuild/wrap/wrap.py
+++ b/mesonbuild/wrap/wrap.py
@@ -353,8 +353,10 @@ def check_can_download(self) -> None:
             raise WrapException(m)
 
     def resolve_git_submodule(self) -> bool:
+        # Is git installed? If not, we're probably not in a git repository and
+        # definitely cannot try to conveniently set up a submodule.
         if not GIT:
-            raise WrapException('Git program not found.')
+            return False
         # Are we in a git repository?
         ret, out = quiet_git(['rev-parse'], self.subdir_root)
         if not ret:


Test patch: