Repository: mesonbuild/meson

Issue:
Add fail message to dependency() to print if not found.
If the dependency is not found I would like to print a message explaining where to obtain the library.

something like:
```
zlib_dep = dependency('zlib',
                      not_found_msg : '(http://www.gzip.org/zlib)')
```
Would output

Dependency zlib found: NO (http://www.gzip.org/zlib)

Also useful for libraries like freetype where the pkg-config version numbering is different to the release version. I would like to be able to print the release version required if the version check fails.



Discussion:
Do you mean something like?

    zlib_dep = dependency('zlib')
    if not zlib_dep.found()
      message ('zlib not found. Check http://www.gzip.org/zlib')
    endif

Doesn't work if required : true.
I think I will be writing a lot of code like this:
```
dep_foo = dependency('foo', required: false)
if not dep_foo.found()
        error('\'foo\' was not found. Frobber tool depends on it, but if you do not want to build it, you can disable Frobber with -Dfrobber=false')
endif
```

It would be more convenient if I could write just
```
dep_foo = dependency('foo', required: true, message: 'Frobber depends on this, but if you do not want to build Frobber, use -Dfrobber=false')
```


Resolving patch:
diff --git a/mesonbuild/interpreter.py b/mesonbuild/interpreter.py
--- a/mesonbuild/interpreter.py
+++ b/mesonbuild/interpreter.py
@@ -1866,10 +1866,55 @@ def get_cross_property_method(self, args, kwargs):
                     'add_test_setup': {'exe_wrapper', 'gdb', 'timeout_multiplier', 'env'},
                     'benchmark': {'args', 'env', 'should_fail', 'timeout', 'workdir', 'suite'},
                     'build_target': known_build_target_kwargs,
-                    'configure_file': {'input', 'output', 'configuration', 'command', 'copy', 'install_dir', 'install_mode', 'capture', 'install', 'format', 'output_format', 'encoding'},
-                    'custom_target': {'input', 'output', 'command', 'install', 'install_dir', 'install_mode', 'build_always', 'capture', 'depends', 'depend_files', 'depfile', 'build_by_default', 'build_always_stale', 'console'},
-                    'dependency': {'default_options', 'fallback', 'language', 'main', 'method', 'modules', 'optional_modules', 'native', 'required', 'static', 'version', 'private_headers'},
-                    'declare_dependency': {'include_directories', 'link_with', 'sources', 'dependencies', 'compile_args', 'link_args', 'link_whole', 'version'},
+                    'configure_file': {'input',
+                                       'output',
+                                       'configuration',
+                                       'command',
+                                       'copy',
+                                       'install_dir',
+                                       'install_mode',
+                                       'capture',
+                                       'install',
+                                       'format',
+                                       'output_format',
+                                       'encoding'},
+                    'custom_target': {'input',
+                                      'output',
+                                      'command',
+                                      'install',
+                                      'install_dir',
+                                      'install_mode',
+                                      'build_always',
+                                      'capture',
+                                      'depends',
+                                      'depend_files',
+                                      'depfile',
+                                      'build_by_default',
+                                      'build_always_stale',
+                                      'console'},
+                    'dependency': {'default_options',
+                                   'fallback',
+                                   'language',
+                                   'main',
+                                   'method',
+                                   'modules',
+                                   'optional_modules',
+                                   'native',
+                                   'not_found_message',
+                                   'required',
+                                   'static',
+                                   'version',
+                                   'private_headers',
+                                   },
+                    'declare_dependency': {'include_directories',
+                                           'link_with',
+                                           'sources',
+                                           'dependencies',
+                                           'compile_args',
+                                           'link_args',
+                                           'link_whole',
+                                           'version',
+                                           },
                     'executable': build.known_exe_kwargs,
                     'find_program': {'required', 'native'},
                     'generator': {'arguments', 'output', 'depfile', 'capture', 'preserve_path_from'},
@@ -2604,6 +2649,9 @@ def get_message_string_arg(self, node):
     @noKwargs
     def func_message(self, node, args, kwargs):
         argstr = self.get_message_string_arg(node)
+        self.message_impl(argstr)
+
+    def message_impl(self, argstr):
         mlog.log(mlog.bold('Message:'), argstr)
 
     @FeatureNew('warning', '0.44.0')
@@ -2972,13 +3020,27 @@ def _handle_featurenew_dependencies(self, name):
     @FeatureNewKwargs('dependency', '0.49.0', ['disabler'])
     @FeatureNewKwargs('dependency', '0.40.0', ['method'])
     @FeatureNewKwargs('dependency', '0.38.0', ['default_options'])
+    @FeatureNewKwargs('dependency', '0.50.0', ['not_found_message'])
     @disablerIfNotFound
     @permittedKwargs(permitted_kwargs['dependency'])
     def func_dependency(self, node, args, kwargs):
         self.validate_arguments(args, 1, [str])
         name = args[0]
         display_name = name if name else '(anonymous)'
+        not_found_message = kwargs.get('not_found_message', '')
+        if not isinstance(not_found_message, str):
+            raise InvalidArguments('The not_found_message must be a string.')
+        try:
+            d = self.dependency_impl(name, display_name, kwargs)
+        except Exception:
+            if not_found_message:
+                self.message_impl(not_found_message)
+            raise
+        if not d.found() and not_found_message:
+            self.message_impl(not_found_message)
+        return d
 
+    def dependency_impl(self, name, display_name, kwargs):
         disabled, required, feature = extract_required_kwarg(kwargs, self.subproject)
         if disabled:
             mlog.log('Dependency', mlog.bold(display_name), 'skipped: feature', mlog.bold(feature), 'disabled')


Test patch:
diff --git a/test cases/linuxlike/1 pkg-config/meson.build b/test cases/linuxlike/1 pkg-config/meson.build
--- a/test cases/linuxlike/1 pkg-config/meson.build	
+++ b/test cases/linuxlike/1 pkg-config/meson.build	
@@ -2,7 +2,8 @@ project('external dependency', 'c')
 
 # Zlib is probably on all dev machines.
 
-dep = dependency('zlib', version : '>=1.2')
+dep = dependency('zlib', version : '>=1.2',
+  not_found_message: 'DANGER! DANGER! THIS MUST NEVER BE SEEN!')
 exe = executable('zlibprog', 'prog-checkver.c',
   dependencies : dep,
   c_args : '-DFOUND_ZLIB="' + dep.version() + '"')
@@ -29,7 +30,8 @@ test('zlibtest2', exe2)
 
 # Try to find a nonexistent library to ensure requires:false works.
 
-dep = dependency('nvakuhrabnsdfasdf', required : false)
+dep = dependency('nvakuhrabnsdfasdf', required : false,
+  not_found_message : 'This dependency was not found as was expected.')
 
 # Try to compile a test that takes a dep and an include_directories