From 4d4d6a8f065b0f071698217b729bf060408386df Mon Sep 17 00:00:00 2001 From: Dan Albert Date: Wed, 12 Dec 2018 13:45:05 -0800 Subject: [PATCH] Add std::filesystem support. Test: ./run_tests.py --bitness 32 Test: ./run_tests.py --bitness 64 Test: ./run_tests.py --bitness 64 --host Bug: None Change-Id: Ie277f503b754321eba04b906fa4ee6d670b2c1b2 --- Android.bp | 41 +++++++++++++++++++ run_tests.py | 6 +++ utils/libcxx/android/compiler.py | 6 ++- utils/libcxx/android/test/config.py | 63 +++++++++++++++++++++++++++-- utils/libcxx/android/test/format.py | 6 ++- 5 files changed, 116 insertions(+), 6 deletions(-) diff --git a/Android.bp b/Android.bp index 951e3fc7e..ccd04a42e 100644 --- a/Android.bp +++ b/Android.bp @@ -146,6 +146,29 @@ cc_library_static { ], } +cc_library_static { + name: "libc++fs", + defaults: ["libc++ defaults"], + srcs: [ + "src/filesystem/directory_iterator.cpp", + "src/filesystem/operations.cpp", + ], + multilib: { + lib32: { + // off_t usage is constrained to within the libc++ source (not the + // headers), so we can build the filesystem library with a 64-bit + // off_t on LP32 to get large file support without needing all users + // of the library to match. + cflags: ["-D_FILE_OFFSET_BITS=64"], + }, + }, + target: { + windows: { + enabled: false, + }, + }, +} + // This target is used to extract the build commands for a test executable. // See run_tests.py. cc_binary { @@ -170,6 +193,7 @@ cc_binary { ], static_libs: [ "libc++experimental", + "libc++fs", ], rtti: true, local_include_dirs: [ @@ -196,3 +220,20 @@ cc_binary { gnu_extensions: false, cpp_std: "c++17", } + +python_test { + name: "filesystem_dynamic_test_helper.py", + main: "test/support/filesystem_dynamic_test_helper.py", + srcs: [ + "test/support/filesystem_dynamic_test_helper.py", + ], + version: { + py2: { + enabled: true, + embedded_launcher: true, + }, + py3: { + enabled: false, + }, + }, +} diff --git a/run_tests.py b/run_tests.py index ebbce0586..35b7a861e 100755 --- a/run_tests.py +++ b/run_tests.py @@ -20,6 +20,7 @@ from __future__ import print_function import argparse import logging import os +import posixpath import sys THIS_DIR = os.path.dirname(os.path.realpath(__file__)) @@ -144,9 +145,14 @@ def get_build_cmds(bitness, host): def setup_test_directory(): """Prepares a device test directory for use by the shell user.""" + stdfs_test_data = os.path.join( + THIS_DIR, 'test/std/input.output/filesystems/Inputs/static_test_env') device_dir = '/data/local/tmp/libcxx' + dynamic_dir = posixpath.join(device_dir, 'dynamic_test_env') check_call(['adb', 'shell', 'rm', '-rf', device_dir]) check_call(['adb', 'shell', 'mkdir', '-p', device_dir]) + check_call(['adb', 'shell', 'mkdir', '-p', dynamic_dir]) + check_call(['adb', 'push', '--sync', stdfs_test_data, device_dir]) check_call(['adb', 'shell', 'chown', '-R', 'shell:shell', device_dir]) diff --git a/utils/libcxx/android/compiler.py b/utils/libcxx/android/compiler.py index 16ed44a88..3e9242a34 100644 --- a/utils/libcxx/android/compiler.py +++ b/utils/libcxx/android/compiler.py @@ -14,6 +14,10 @@ class AndroidCXXCompiler(libcxx.compiler.CXXCompiler): self.link_template = link_template self.build_top = os.getenv('ANDROID_BUILD_TOP') + # The file system tests require a handful of defines that we can't add + # to the Android.bp since they require absolute paths. + self.extra_cflags = [] + def copy(self): return copy.deepcopy(self) @@ -60,7 +64,7 @@ class AndroidCXXCompiler(libcxx.compiler.CXXCompiler): source_files = [source_files] cxx_args = self.cxx_template.replace('%OUT%', out) cxx_args = cxx_args.replace('%SOURCE%', ' '.join(source_files)) - return [self.path] + shlex.split(cxx_args) + return [self.path] + shlex.split(cxx_args) + self.extra_cflags def linkCmd(self, source_files, out=None, flags=None): if out is None: diff --git a/utils/libcxx/android/test/config.py b/utils/libcxx/android/test/config.py index 612aa36f8..3e1edd42c 100644 --- a/utils/libcxx/android/test/config.py +++ b/utils/libcxx/android/test/config.py @@ -1,5 +1,6 @@ import os import re +import sys import libcxx.test.config import libcxx.android.compiler @@ -9,6 +10,12 @@ import libcxx.android.test.format class Configuration(libcxx.test.config.Configuration): def __init__(self, lit_config, config): super(Configuration, self).__init__(lit_config, config) + self.exec_env = {} + + @property + def is_host(self): + """Returns True if configured to run host tests.""" + return self.lit_config.params.get('android_mode') == 'host' def configure(self): self.configure_src_root() @@ -17,6 +24,10 @@ class Configuration(libcxx.test.config.Configuration): self.configure_cxx() self.configure_triple() self.configure_features() + if self.is_host: + self.configure_filesystem_host() + else: + self.configure_filesystem_device() def print_config_info(self): self.lit_config.note( @@ -29,7 +40,7 @@ class Configuration(libcxx.test.config.Configuration): list(self.config.available_features)) def configure_obj_root(self): - if self.lit_config.params.get('android_mode') == 'host': + if self.is_host: self.libcxx_obj_root = os.getenv('ANDROID_HOST_OUT') else: self.libcxx_obj_root = os.getenv('ANDROID_PRODUCT_OUT') @@ -49,9 +60,53 @@ class Configuration(libcxx.test.config.Configuration): self.config.target_triple = self.cxx.get_triple() + def configure_filesystem_host(self): + # TODO: We shouldn't be writing to the source directory for the host. + static_env = os.path.join(self.libcxx_src_root, 'test', 'std', + 'input.output', 'filesystems', 'Inputs', + 'static_test_env') + static_env = os.path.realpath(static_env) + assert os.path.isdir(static_env) + self.cxx.extra_cflags.append( + '-DLIBCXX_FILESYSTEM_STATIC_TEST_ROOT="{}"'.format(static_env)) + + dynamic_env = os.path.join(self.config.test_exec_root, 'filesystem', + 'Output', 'dynamic_env') + dynamic_env = os.path.realpath(dynamic_env) + if not os.path.isdir(dynamic_env): + os.makedirs(dynamic_env) + self.cxx.extra_cflags.append( + '-DLIBCXX_FILESYSTEM_DYNAMIC_TEST_ROOT="{}"'.format(dynamic_env)) + self.exec_env['LIBCXX_FILESYSTEM_DYNAMIC_TEST_ROOT'] = dynamic_env + + dynamic_helper = os.path.join(self.libcxx_src_root, 'test', 'support', + 'filesystem_dynamic_test_helper.py') + assert os.path.isfile(dynamic_helper) + self.cxx.extra_cflags.append( + '-DLIBCXX_FILESYSTEM_DYNAMIC_TEST_HELPER="{} {}"'.format( + sys.executable, dynamic_helper)) + + def configure_filesystem_device(self): + static_env = '/data/local/tmp/libcxx/static_test_env' + self.cxx.extra_cflags.append( + '-DLIBCXX_FILESYSTEM_STATIC_TEST_ROOT="{}"'.format(static_env)) + + dynamic_env = '/data/local/tmp/libcxx/dynamic_test_env' + self.cxx.extra_cflags.append( + '-DLIBCXX_FILESYSTEM_DYNAMIC_TEST_ROOT="{}"'.format(dynamic_env)) + self.exec_env['LIBCXX_FILESYSTEM_DYNAMIC_TEST_ROOT'] = dynamic_env + + dynamic_helper = ('/data/nativetest/filesystem_dynamic_test_helper.py/' + 'filesystem_dynamic_test_helper.py') + self.cxx.extra_cflags.append( + '-DLIBCXX_FILESYSTEM_DYNAMIC_TEST_HELPER="{}"'.format( + dynamic_helper)) + def configure_features(self): self.config.available_features.add('long_tests') self.config.available_features.add('c++experimental') + self.config.available_features.add('c++fs') + self.config.available_features.add('c++filesystem') std_pattern = re.compile(r'-std=(c\+\+\d[0-9x-z])') match = std_pattern.search(self.cxx.cxx_template) if match: @@ -65,12 +120,14 @@ class Configuration(libcxx.test.config.Configuration): self.libcxx_src_root, self.libcxx_obj_root, getattr(self.config, 'device_dir', '/data/local/tmp/'), - getattr(self.config, 'timeout', '60')) + getattr(self.config, 'timeout', '60'), + self.exec_env) elif mode == 'host': return libcxx.android.test.format.HostTestFormat( self.cxx, self.libcxx_src_root, self.libcxx_obj_root, - getattr(self.config, 'timeout', '60')) + getattr(self.config, 'timeout', '60'), + self.exec_env) else: raise RuntimeError('Invalid android_mode: {}'.format(mode)) diff --git a/utils/libcxx/android/test/format.py b/utils/libcxx/android/test/format.py index e5252a8a1..761c019ab 100644 --- a/utils/libcxx/android/test/format.py +++ b/utils/libcxx/android/test/format.py @@ -29,8 +29,10 @@ class HostTestFormat(libcxx.test.format.LibcxxTestFormat): os.path.join(outdir, 'lib'), os.path.join(outdir, 'lib64'), ]) - default_env = {'LD_LIBRARY_PATH': libpath} - self.exec_env = default_env if exec_env is None else exec_env + env = {'LD_LIBRARY_PATH': libpath} + if exec_env is not None: + env.update(exec_env) + self.exec_env = env class TestFormat(HostTestFormat):