diff --git a/llvm/utils/lit/lit/ShCommands.py b/llvm/utils/lit/lit/ShCommands.py --- a/llvm/utils/lit/lit/ShCommands.py +++ b/llvm/utils/lit/lit/ShCommands.py @@ -43,20 +43,36 @@ return self.pattern def __eq__(self, other): - if not isinstance(other, Command): + if not isinstance(other, GlobItem): return False return (self.pattern == other.pattern) def resolve(self, cwd): import glob + import platform import os if os.path.isabs(self.pattern): abspath = self.pattern else: abspath = os.path.join(cwd, self.pattern) + + # To get better behavior with long paths, we prepend the magic + # prefix to make Windows API allow up to 32K bytes in the paths. + # However, this only works in 2.7.16 and above because of + # https://bugs.python.org/issue32539 + use_prefix = (platform.system() == 'Windows' and + platform.python_version_tuple() >= (2, 7, 16)) + if use_prefix: + abspath = u'\\\\?\\' + os.path.normpath(abspath) + results = glob.glob(abspath) - return [self.pattern] if len(results) == 0 else results + if len(results) == 0: + return [self.pattern] + elif use_prefix: + return [result[4:] for result in results] + else: + return results class Pipeline: def __init__(self, commands, negate=False, pipe_err=False): diff --git a/llvm/utils/lit/tests/unit/GlobItem.py b/llvm/utils/lit/tests/unit/GlobItem.py new file mode 100644 --- /dev/null +++ b/llvm/utils/lit/tests/unit/GlobItem.py @@ -0,0 +1,73 @@ +# RUN: %{python} %s + +import os +import platform +import shutil +import tempfile +import unittest + +from lit.ShCommands import GlobItem + + +class TestGlobItem(unittest.TestCase): + def setUp(self): + self.test_dir = tempfile.mkdtemp() + + def tearDown(self): + # We might be deleting long paths, so rmtree has to be started with a + # long path prefix, even if it is not one. + if platform.system() == 'Windows': + shutil.rmtree(u'\\\\?\\' + self.test_dir) + else: + shutil.rmtree(self.test_dir) + pass + + def test_short_path(self): + file_name = 'bbbbbbbb.ccc' # Classic 8.3 file name + # 260 is the maximum Windows path length in many cases. + # 50 is under the limit. + dir_name_len = 50 + dir_name = 'a' * dir_name_len + dir_abs_path = os.path.join(self.test_dir, dir_name) + file_abs_path = os.path.join(dir_abs_path, file_name) + + os.makedirs(dir_abs_path) + with open(file_abs_path, 'wb') as f: + f.write('test') + + glob_item = GlobItem(os.path.join(dir_abs_path, '*')) + result = glob_item.resolve(os.getcwd()) + self.assertEqual(result, [file_abs_path]) + + def test_long_path(self): + file_name = 'bbbbbbbb.ccc' # Classic 8.3 file name + # 260 is the maximum Windows path length in many cases. + # 250 should be over the limit, but also under the limit for directory + # name length in Windows. + dir_name_len = 250 + dir_name = 'a' * dir_name_len + dir_abs_path = os.path.join(self.test_dir, dir_name) + file_abs_path = os.path.join(dir_abs_path, file_name) + + # In Windows we have to use the \\?\ prefix or these calls will fail + # during the test setup. + def prefix_path_in_windows(path): + if platform.system() == 'Windows': + return u'\\\\?\\' + path + else: + return path + + os.makedirs(prefix_path_in_windows(dir_abs_path)) + with open(prefix_path_in_windows(file_abs_path), 'wb') as f: + f.write('test') + + # Do NOT use prefix_path_in_windows here. We are testing that GlobItem + # does the right thing. + glob_item = GlobItem(os.path.join(dir_abs_path, '*')) + result = glob_item.resolve(os.getcwd()) + self.assertEqual(result, [file_abs_path]) + + + +if __name__ == '__main__': + unittest.main() \ No newline at end of file