Repository: mesonbuild/meson

Issue:
subprojects which depend on other subprojects trigger warnings for patch_directory
From the wrapdb, try to build the openh264 project.

```
$ meson setup --wipe builddir -Dwraps=openh264 --fatal-meson-warnings
The Meson build system
Version: 0.59.0
Source dir: /home/eschwartz/git/mesonbuild/wrapdb
Build dir: /home/eschwartz/git/mesonbuild/wrapdb/builddir
Build type: native build
Project name: wrapdb
Project version: undefined
Host machine cpu family: x86_64
Host machine cpu: x86_64

Executing subproject openh264 

openh264| Project name: openh264
openh264| Project version: 2.1.0
openh264| C compiler for the host machine: ccache cc (gcc 11.1.0 "cc (GCC) 11.1.0")
openh264| C linker for the host machine: cc ld.bfd 2.36.1
openh264| C++ compiler for the host machine: ccache c++ (gcc 11.1.0 "c++ (GCC) 11.1.0")
openh264| C++ linker for the host machine: c++ ld.bfd 2.36.1
openh264| Compiler for C++ supports arguments -Wno-non-virtual-dtor: YES
openh264| Compiler for C++ supports arguments -Wno-class-memaccess: YES
openh264| Compiler for C++ supports arguments -Wno-strict-aliasing: YES
openh264| Run-time dependency threads found: YES
openh264| Library pthread found: YES
openh264| Library libm found: YES
openh264| Program nasm found: YES (/usr/bin/nasm)
openh264| Found pkg-config: /usr/bin/pkg-config (1.7.3)
openh264| Run-time dependency GTest found: NO (tried pkgconfig and system)
openh264| Looking for a fallback subproject for the dependency gtest
openh264| Using gtest source from cache.
openh264| WARNING: Project targeting '>= 0.50' but tried to use feature introduced in '0.55.0': patch_directory.

subprojects/openh264-2.1.1/test/meson.build:4:2: ERROR: Fatal warnings enabled, aborting

A full log can be found at /home/eschwartz/git/mesonbuild/wrapdb/builddir/meson-logs/meson-log.txt
```

Several other warnings exist but don't have a chance to be reached, however those are fixed in upstream openh264 git... but there is nothing they can do about the first warning. The openh264 project does include their own gtest.wrap which scrupulously uses patch_url, but obviously this does not undergo wrap promotion if the superproject has an existing wrap.

This unfortunately causes some awkwardness for the wrapdb, since the wrapdb tooling uses --fatal-meson-warnings to catch certain types of errors.

Perhaps warnings coming from .wrap files should always obey the superproject meson_version, regardless of which meson.build they got invoked by?
Perhaps the wrap files should be tracked down to their original locations and obey that project's meson_version?


Discussion:

Resolving patch:
diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py
--- a/mesonbuild/interpreter/interpreter.py
+++ b/mesonbuild/interpreter/interpreter.py
@@ -799,7 +799,7 @@ def do_subproject(self, subp_name: str, method: str, kwargs):
 
         r = self.environment.wrap_resolver
         try:
-            subdir = r.resolve(subp_name, method, self.subproject)
+            subdir = r.resolve(subp_name, method)
         except wrap.WrapException as e:
             if not required:
                 mlog.log(e)
@@ -1107,7 +1107,7 @@ def func_project(self, node: mparser.FunctionNode, args: T.Tuple[str, T.List[str
         wrap_mode = self.coredata.get_option(OptionKey('wrap_mode'))
         if not self.is_subproject() or wrap_mode != WrapMode.nopromote:
             subdir = os.path.join(self.subdir, spdirname)
-            r = wrap.Resolver(self.environment.get_source_dir(), subdir, wrap_mode)
+            r = wrap.Resolver(self.environment.get_source_dir(), subdir, self.subproject, wrap_mode)
             if self.is_subproject():
                 self.environment.wrap_resolver.merge_wraps(r)
             else:
diff --git a/mesonbuild/wrap/wrap.py b/mesonbuild/wrap/wrap.py
--- a/mesonbuild/wrap/wrap.py
+++ b/mesonbuild/wrap/wrap.py
@@ -32,6 +32,7 @@
 from . import WrapMode
 from .. import coredata
 from ..mesonlib import quiet_git, GIT, ProgressBar, MesonException
+from ..interpreterbase import FeatureNew
 from  .. import mesonlib
 
 if T.TYPE_CHECKING:
@@ -90,8 +91,9 @@ class WrapNotFoundException(WrapException):
     pass
 
 class PackageDefinition:
-    def __init__(self, fname: str):
+    def __init__(self, fname: str, subproject: str = ''):
         self.filename = fname
+        self.subproject = subproject
         self.type = None  # type: T.Optional[str]
         self.values = {} # type: T.Dict[str, str]
         self.provided_deps = {} # type: T.Dict[str, T.Optional[str]]
@@ -141,8 +143,14 @@ def parse_wrap(self) -> None:
             self.filename = str(fname)
             self.parse_wrap()
             self.redirected = True
-            return
-        self.parse_provide_section(config)
+        else:
+            self.parse_provide_section(config)
+        if 'patch_directory' in self.values:
+            FeatureNew('Wrap files with patch_directory', '0.55.0').use(self.subproject)
+        for what in ['patch', 'source']:
+            if f'{what}_filename' in self.values and f'{what}_url' not in self.values:
+                FeatureNew(f'Local wrap patch files without {what}_url', '0.55.0').use(self.subproject)
+
 
     def parse_wrap_section(self, config: configparser.ConfigParser) -> None:
         if len(config.sections()) < 1:
@@ -197,9 +205,10 @@ def verbose_git(cmd: T.List[str], workingdir: str, check: bool = False) -> bool:
         raise WrapException(str(e))
 
 class Resolver:
-    def __init__(self, source_dir: str, subdir: str, wrap_mode: WrapMode = WrapMode.default) -> None:
+    def __init__(self, source_dir: str, subdir: str, subproject: str = '', wrap_mode: WrapMode = WrapMode.default) -> None:
         self.source_dir = source_dir
         self.subdir = subdir
+        self.subproject = subproject
         self.wrap_mode = wrap_mode
         self.subdir_root = os.path.join(source_dir, subdir)
         self.cachedir = os.path.join(self.subdir_root, 'packagecache')
@@ -216,7 +225,7 @@ def load_wraps(self) -> None:
             if not i.endswith('.wrap'):
                 continue
             fname = os.path.join(self.subdir_root, i)
-            wrap = PackageDefinition(fname)
+            wrap = PackageDefinition(fname, self.subproject)
             self.wraps[wrap.name] = wrap
             if wrap.directory in dirs:
                 dirs.remove(wrap.directory)
@@ -225,7 +234,7 @@ def load_wraps(self) -> None:
             if i in ['packagecache', 'packagefiles']:
                 continue
             fname = os.path.join(self.subdir_root, i)
-            wrap = PackageDefinition(fname)
+            wrap = PackageDefinition(fname, self.subproject)
             self.wraps[wrap.name] = wrap
 
         for wrap in self.wraps.values():
@@ -271,8 +280,7 @@ def find_program_provider(self, names: T.List[str]) -> T.Optional[str]:
                 return wrap.name
         return None
 
-    def resolve(self, packagename: str, method: str, current_subproject: str = '') -> str:
-        self.current_subproject = current_subproject
+    def resolve(self, packagename: str, method: str) -> str:
         self.packagename = packagename
         self.directory = packagename
         self.wrap = self.wraps.get(packagename)
@@ -554,8 +562,6 @@ def get_file_internal(self, what: str) -> str:
             self.download(what, cache_path)
             return cache_path
         else:
-            from ..interpreterbase import FeatureNew
-            FeatureNew(f'Local wrap patch files without {what}_url', '0.55.0').use(self.current_subproject)
             path = Path(self.wrap.filesdir) / filename
 
             if not path.exists():
@@ -577,8 +583,6 @@ def apply_patch(self) -> None:
                     shutil.unpack_archive(path, workdir)
                     self.copy_tree(workdir, self.subdir_root)
         elif 'patch_directory' in self.wrap.values:
-            from ..interpreterbase import FeatureNew
-            FeatureNew('patch_directory', '0.55.0').use(self.current_subproject)
             patch_dir = self.wrap.values['patch_directory']
             src_dir = os.path.join(self.wrap.filesdir, patch_dir)
             if not os.path.isdir(src_dir):


Test patch: