Repository: conan-io/conan

Issue:
Conan stops with Python error when using AutoToolsBuildEnvironment (+ fix)
- Conan version: 1.4.0
- OS: Windows 10 Enterprise, Version 1803, build 17134.1 (running in VmWare virtual machine)


I was trying to write a package recipe for GraphicsMagick, that depends on libjpeg, libpng and bzip2. In addition MinGWInstaller is a requirement of my profile.

The recipe uses the AutoToolsBuildEnvironment in the build function of the ConanFile derived class.
The configure function of the AutoToolsBuildEnvironment object stops with error:
```
ERROR: GraphicsMagick/1.3.29@PROJECT: Error in build() method, line 90
        self.build_configure()
while calling 'build_configure', line 116
        env_build.configure(configure_dir=os.path.join(self.source_folder, self.source_subfolder), args=config_args)
        AttributeError: 'NoneType' object has no attribute 'replace'
```

I ran conan in a debugger and the problem is caused because function get_cased_path(...) returns None for an exisiting path. Full call stack at moment get_cased_path() is called:
>	get_cased_path in win line 366	Python
 	unix_path in win line 393	Python
 	adjust_path in compiler_flags line 155	Python
 	<listcomp> in compiler_flags line 188	Python
 	format_library_paths in compiler_flags line 190	Python
 	AutoToolsBuildEnvironment._get_vars in autotools_environment line 262	Python
 	AutoToolsBuildEnvironment.vars in autotools_environment line 309	Python
 	AutoToolsBuildEnvironment.configure in autotools_environment line 161	Python
 	GraphicksMagickConan.build_configure in conanfile line 116	Python
 	GraphicksMagickConan.build in conanfile line 90	Python
 	ConanManager.build in manager line 436	Python
 	ConanAPIV1.build in conan_api line 565	Python
 	wrapper in api_method in conan_api line 77	Python
 	Command.build in command line 557	Python
 	Command.run in command line 1182	Python
 	main in command line 1315	Python
 	conan module line 9	Python

First several paths are correctly returned by get_cased_path, then path 'C:/Users/wdobbe/.conan/data/libjpeg/9b/conan/testing/package/2d8c92555b1f4cbde30bc3ec3c98b1cecde16843/lib' results in return value None, because the line
`res = glob.glob('\\'.join(test_name))`
returns None. I ran a few tests (see below) and it looks like Windows 10 became selectively case sensitive:

```
>>> test_name=['C:/USERS/WDOBBE/.CONAN/DATA/MINGW_INSTALLER/1.0/CONAN/STABLE/PACKAGE/B2A5C106CD30B29D475DA50A9633D31C2329D6BD/LIB']
>>> res = '\\'.join(test_name)
>>> glob.glob(res)
['C:/USERS/WDOBBE/.CONAN/DATA/MINGW_INSTALLER/1.0/CONAN/STABLE/PACKAGE/B2A5C106CD30B29D475DA50A9633D31C2329D6BD/LIB']
>>> test_name=['C:/USERS/WDOBBE/.CONAN/DATA/LIBJPEG/9B/CONAN/TESTING/PACKAGE/2D8C92555B1F4CBDE30BC3EC3C98B1CECDE16843/LIB']
>>> res = '\\'.join(test_name)
>>> glob.glob(res)
[]
```

Then testing the same paths in Windows Command Prompt:

```
C:\Users\wdobbe\generated\conan-graphicsmagick>dir C:\USERS\WDOBBE\.CONAN\DATA\MINGW_INSTALLER\1.0\CONAN\STABLE\PACKAGE\B2A5C106CD30B29D475DA50A9633D31C2329D6BD\LIB
 Volume in drive C is Windows
 Volume Serial Number is 14C3-9D55

 Directory of C:\USERS\WDOBBE\.CONAN\DATA\MINGW_INSTALLER\1.0\CONAN\STABLE\PACKAGE\B2A5C106CD30B29D475DA50A9633D31C2329D6BD\LIB

24-05-2018  20:40    <DIR>          .
24-05-2018  20:40    <DIR>          ..
24-05-2018  20:40    <DIR>          gcc
29-12-2015  05:47         1.260.280 libbfd.a
29-12-2015  02:51           396.462 libiberty.a
29-12-2015  04:33            41.672 libmangle.a
29-12-2015  05:47         1.579.910 libopcodes.a
               4 File(s)      3.278.324 bytes
               3 Dir(s)  31.922.651.136 bytes free
               

C:\Users\wdobbe\generated\conan-graphicsmagick>dir C:\USERS\WDOBBE\.CONAN\DATA\LIBJPEG\9B\CONAN\TESTING\PACKAGE\2D8C92555B1F4CBDE30BC3EC3C98B1CECDE16843\LIB
 Volume in drive C is Windows
 Volume Serial Number is 14C3-9D55

 Directory of C:\USERS\WDOBBE\.CONAN\DATA\LIBJPEG\9B\CONAN\TESTING\PACKAGE\2D8C92555B1F4CBDE30BC3EC3C98B1CECDE16843

File Not Found


C:\Users\wdobbe\generated\conan-graphicsmagick>dir C:\USERS\WDOBBE\.CONAN\DATA\LIBJPEG\9B\CONAN\TESTING\PACKAGE\2D8C92555B1F4CBDE30BC3EC3C98B1CECDE16843\lib
 Volume in drive C is Windows
 Volume Serial Number is 14C3-9D55

 Directory of C:\USERS\WDOBBE\.CONAN\DATA\LIBJPEG\9B\CONAN\TESTING\PACKAGE\2D8C92555B1F4CBDE30BC3EC3C98B1CECDE16843\lib

29-05-2018  21:58    <DIR>          .
29-05-2018  21:58    <DIR>          ..
29-05-2018  21:58         2.255.780 libjpeg.a
29-05-2018  21:58               960 libjpeg.la
               2 File(s)      2.256.740 bytes
               2 Dir(s)  31.923.372.032 bytes free
```

Note that the path for libjpeg converted to upper case results in 'File not found' while the original path results in the directory listing.

When I apply the following patch, the problem disappears and the AutoToolsBuildEnvironment configure() functions finishes without problems:


```
diff --git a/conans/client/tools/win.py b/conans/client/tools/win.py
index bc72a06..14bbe44 100644
--- a/conans/client/tools/win.py
+++ b/conans/client/tools/win.py
@@ -363,7 +363,7 @@ def get_cased_path(name):
         name = os.path.abspath(name)
     dirs = name.split('\\')
     # disk letter
-    test_name = [dirs[0].upper()]
+    test_name = [dirs[0]]
     for d in dirs[1:]:
         test_name += ["%s[%s]" % (d[:-1], d[-1])]  # This brackets do the trick to match case
```

I don't know the reason the path is converted to upper case in the first place, so this change might cause other problems, but at least it allows me to build my GraphicsMagick recipe.




Discussion:
The truth is that I have been lately surprised by Windows 10 (maybe after latest upgrade?) returning different cased paths, without apparent reasons. 
Lets investigate and try to fix this, thanks for the detailed report!

Resolving patch:
diff --git a/conans/client/tools/win.py b/conans/client/tools/win.py
--- a/conans/client/tools/win.py
+++ b/conans/client/tools/win.py
@@ -1,4 +1,3 @@
-import glob
 import json
 import os
 import platform
@@ -361,17 +360,22 @@ def get_cased_path(name):
         return name
     if not os.path.isabs(name):
         name = os.path.abspath(name)
-    dirs = name.split('\\')
-    # disk letter
-    test_name = [dirs[0].upper()]
-    for d in dirs[1:]:
-        test_name += ["%s[%s]" % (d[:-1], d[-1])]  # This brackets do the trick to match cased
-
-    res = glob.glob('\\'.join(test_name))
-    if not res:
-        # File not found
-        return None
-    return res[0]
+
+    result = []
+    current = name
+    while True:
+        parent, child = os.path.split(current)
+        if parent == current:
+            break
+        children = os.listdir(parent)
+        for c in children:
+            if c.upper() == child.upper():
+                result.append(c)
+                break
+        current = parent
+    drive, _ = os.path.splitdrive(current)
+    result.append(drive)
+    return os.sep.join(reversed(result))
 
 
 MSYS2 = 'msys2'


Test patch:
diff --git a/conans/test/util/unix_path_test.py b/conans/test/util/unix_path_test.py
--- a/conans/test/util/unix_path_test.py
+++ b/conans/test/util/unix_path_test.py
@@ -3,6 +3,25 @@
 
 import unittest
 from conans import tools
+from conans.client.tools.win import get_cased_path
+from conans.test.utils.test_files import temp_folder
+import os
+import platform
+from conans.util.files import mkdir
+
+
+class GetCasedPath(unittest.TestCase):
+    @unittest.skipUnless(platform.system() == "Windows", "Requires Windows")
+    def test_case(self):
+        folder = temp_folder()
+        p1 = os.path.join(folder, "MyFolder", "Subfolder")
+        mkdir(p1)
+        p2 = get_cased_path(p1)
+        self.assertEqual(p1, p2)
+
+        p3 = os.path.join(folder, "myfolder", "subfolder")
+        p4 = get_cased_path(p3)
+        self.assertEqual(p1, p4)
 
 
 class UnixPathTest(unittest.TestCase):