Repository: conan-io/conan

Issue:
[bug]  copy(ignore_case=False) under Windows
<!--
  Please don't forget to update the issue title.
  Include all applicable information to help us reproduce your problem.

  To help us debug your issue please explain:
-->

### Environment Details (include every applicable attribute)
  * Operating System+version: Windows 10
  * Conan version: 1.20.4
  * Python version: 3.7.1

I would say `copy(ignore_case=False)` methods behavior under Windows is at least inconsistent with docs:
> - **ignore_case** (Optional, Defaulted to False): If enabled, it will do a case-insensitive pattern matching.

actually files case is effectively ignored regardless `ignore_case` value.

That's due to
https://github.com/conan-io/conan/blob/e5d142e61e9913b246c034a46d21c2d183ba0d35/conans/client/file_copier.py#L155
`fnmatch.filter` uses `fnmatch.fnmatch` that in turn involves `os.path.normcase()` doing case-insensitive filtering under Windows.

What about
```python
files_to_copy = [n for n in filenames if fnmatch.fnmatchcase(n, pattern)]
```
and replacing `fnmatch.fnmatch` with `fnmatch.fnmatchcase` everywhere?



Discussion:
Hi @db4,
You are right, It seems that currently `ignore_case` argument has no effect under Windows and it should be fixed.
Thanks a lot for reporting.

I've just been bitten by this. @db4  Did you make a pull-request with `fnmatch.fnmatchcase` ?
> Did you make a pull-request with `fnmatch.fnmatchcase` ?

No, I just reported the bug. If Conan developers think my method of fixing it is OK, I can create a PR. But this definitely requires some tests? I'm not very familiar with the Conan testing framework. 

Resolving patch:
diff --git a/conans/client/file_copier.py b/conans/client/file_copier.py
--- a/conans/client/file_copier.py
+++ b/conans/client/file_copier.py
@@ -51,7 +51,7 @@ def report(self, output):
         return report_copied_files(self._copied, output)
 
     def __call__(self, pattern, dst="", src="", keep_path=True, links=False, symlinks=None,
-                 excludes=None, ignore_case=False):
+                 excludes=None, ignore_case=True):
         """
         param pattern: an fnmatch file pattern of the files that should be copied. Eg. *.dll
         param dst: the destination local folder, wrt to current conanfile dir, to which
@@ -62,6 +62,9 @@ def __call__(self, pattern, dst="", src="", keep_path=True, links=False, symlink
                          src to dst folders, or just drop. False is useful if you want
                          to collect e.g. many *.libs among many dirs into a single
                          lib dir
+        param links: True to activate symlink copying
+        param excludes: Single pattern or a tuple of patterns to be excluded from the copy
+        param ignore_case: will do a case-insensitive pattern matching when True
         return: list of copied files
         """
         # TODO: Remove the old "links" arg for Conan 2.0
@@ -152,8 +155,11 @@ def _filter_files(src, pattern, links, excludes, ignore_case, excluded_folders):
         if ignore_case:
             filenames = {f.lower(): f for f in filenames}
             pattern = pattern.lower()
+            files_to_copy = fnmatch.filter(filenames, pattern)
+        else:
+            files_to_copy = [n for n in filenames if fnmatch.fnmatchcase(os.path.normpath(n),
+                                                                         pattern)]
 
-        files_to_copy = fnmatch.filter(filenames, pattern)
         for exclude in excludes:
             files_to_copy = [f for f in files_to_copy if not fnmatch.fnmatch(f, exclude)]
 


Test patch:
diff --git a/conans/test/unittests/client/file_copier_test.py b/conans/test/unittests/client/file_copier/file_copier_test.py
similarity index 92%
rename from conans/test/unittests/client/file_copier_test.py
rename to conans/test/unittests/client/file_copier/file_copier_test.py
--- a/conans/test/unittests/client/file_copier_test.py
+++ b/conans/test/unittests/client/file_copier/file_copier_test.py
@@ -200,3 +200,22 @@ def avoid_repeat_copies_test(self, copy2_mock):
             copier("*", src=os.path.join(src_folder, "sub"))
 
         self.assertEqual(copy2_mock.call_count, len(src_folders))
+
+    def test_ignore_case(self):
+        src_folder = temp_folder()
+        save(os.path.join(src_folder, "FooBar.txt"), "Hello")
+
+        dst_folder = temp_folder()
+        copier = FileCopier([src_folder], dst_folder)
+        copier("foobar.txt", ignore_case=False)
+        self.assertEqual([], os.listdir(dst_folder))
+
+        dst_folder = temp_folder()
+        copier = FileCopier([src_folder], dst_folder)
+        copier("FooBar.txt", ignore_case=False)
+        self.assertEqual(["FooBar.txt"], os.listdir(dst_folder))
+
+        dst_folder = temp_folder()
+        copier = FileCopier([src_folder], dst_folder)
+        copier("foobar.txt", ignore_case=True)
+        self.assertEqual(["FooBar.txt"], os.listdir(dst_folder))