Repository: mesonbuild/meson

Issue:
extracting from wrap archives should detect case-sensitivity issues
**Describe the bug**

The source archive for a wrap might depend on the case-sensitivity of the file system to work. Meson currently:

* experiences data loss on extraction for regular files (since only one will be kept);
* might fail with an obscure `OSError` if symbolic links are present;
* (probably other failures are possible)


**To Reproduce**

See mesonbuild/wrapdb#1051. Try to build it on a case-insensitive file system. (Or just check windows/mac CI logs.)


**Expected behavior**

Meson should detect this scenario and fail with a clear error message.

**system parameters**
* meson version: 1.2.2


Discussion:
Case-sensitivity could probably be detected by something like this:

```diff
diff --git a/mesonbuild/wrap/wrap.py b/mesonbuild/wrap/wrap.py
index a1bf72511..51304d62f 100644
--- a/mesonbuild/wrap/wrap.py
+++ b/mesonbuild/wrap/wrap.py
@@ -569,6 +569,10 @@ class Resolver:
         if 'lead_directory_missing' in self.wrap.values:
             os.mkdir(self.dirname)
             extract_dir = self.dirname
+
+        with tempfile.NamedTemporaryFile(dir=extract_dir, prefix='case-sensitivity-test') as tf:
+            case_sensitive = not os.path.exists(tf.name.replace('case-sensitivity-test', 'CASE-SENSITIVITY-TEST'))
+
         shutil.unpack_archive(path, extract_dir)
 
     def get_git(self) -> None:
```
Prototype for tar archives:

```diff
diff --git a/mesonbuild/wrap/wrap.py b/mesonbuild/wrap/wrap.py
index a1bf72511..a2eee830c 100644
--- a/mesonbuild/wrap/wrap.py
+++ b/mesonbuild/wrap/wrap.py
@@ -569,6 +569,21 @@ class Resolver:
         if 'lead_directory_missing' in self.wrap.values:
             os.mkdir(self.dirname)
             extract_dir = self.dirname
+
+        with tempfile.NamedTemporaryFile(dir=extract_dir, prefix='case-sensitivity-test') as tf:
+            case_sensitive = not os.path.exists(tf.name.replace('case-sensitivity-test', 'CASE-SENSITIVITY-TEST'))
+
+        # case_sensitive = False # FOR TESTING
+        if not case_sensitive:
+            import tarfile
+            files = set()
+            with tarfile.open(path) as f:
+                for name in f.getnames():
+                    name = name.lower()
+                    if name in files:
+                        raise WrapException(f'The source archive for {self.packagename}.wrap cannot be extracted to a case-insensitive filesystem because it contains files that only differ in the case of their names.')
+                    files.add(name)
+
         shutil.unpack_archive(path, extract_dir)
 
     def get_git(self) -> None:
```

Unfortunately this is very naive, doesn't handle paths with "." or ".." or symlink shenanigans, etc.
But liburing is also not actually even supposed to be downloaded or installed on non-Linux operating systems, so it makes mostly perfect sense that it is using technology that is linux-specific in the tarball.

This is technically a wrapdb "bug" because the wrapdb has had issues in the past where people ignored platform-specific errors when merging (not contributing, *merging*) wraps and silencing errors by misusing the "project source code is intended only for linux" config setting to mean "don't build and test on xxx platform because the upstream build system works fine on that platform, but the wrap is buggy".

I'm unsure what should be done here from meson's side of things.
> But liburing is also not actually even supposed to be downloaded or installed on non-Linux operating systems, so it makes mostly perfect sense that it is using technology that is linux-specific in the tarball.

That's true, but I don't think the core issue is linux-specific. It's true that linux platforms usually use case-sensitive filesystems, but that can also be changed on linux for certain filesystems.

> This is technically a wrapdb "bug" because the wrapdb has had issues in the past where people ignored platform-specific errors when merging (not contributing, merging) wraps and silencing errors by misusing the "project source code is intended only for linux" config setting to mean "don't build and test on xxx platform because the upstream build system works fine on that platform, but the wrap is buggy".

I don't see it that way. The issue has nothing to do with the wrap itself. This happens way before that code is even parsed and executed. This happens as the result of the [`subproject()` call](https://github.com/mesonbuild/wrapdb/blob/5cfaa1c9f7a0a7154ba826c2b809a6710ec1591b/meson.build#L9) downloading and extracting the source, there is nothing the wrap can do to prevent that (as far as I know).

> I'm unsure what should be done here from meson's side of things.

Well, I think either

* the `sanity_checks.py` from wrapdb needs to have an option that will prevent even running `meson setup -D wraps=...` for certain wraps; or
* something that addresses this issues needs to be added to meson, and the `sanity_checks.py` from wrapdb needs to be extended to recognize this error ([just like it does for others](https://github.com/mesonbuild/wrapdb/blob/5cfaa1c9f7a0a7154ba826c2b809a6710ec1591b/tools/sanity_checks.py#L404)).

Resolving patch:
diff --git a/mesonbuild/wrap/wrap.py b/mesonbuild/wrap/wrap.py
--- a/mesonbuild/wrap/wrap.py
+++ b/mesonbuild/wrap/wrap.py
@@ -569,7 +569,10 @@ def get_file(self) -> None:
         if 'lead_directory_missing' in self.wrap.values:
             os.mkdir(self.dirname)
             extract_dir = self.dirname
-        shutil.unpack_archive(path, extract_dir)
+        try:
+            shutil.unpack_archive(path, extract_dir)
+        except OSError as e:
+            raise WrapException(f'failed to unpack archive with error: {str(e)}') from e
 
     def get_git(self) -> None:
         if not GIT:


Test patch: