[libc++] Reimplement the dynamic filesystem helper without using Python

This patch reimplements the dynamic filesystem helper using Posix
functionality instead of relying on Python. The primary reason for
doing this is that it allows running the libc++ test suite on devices
that do not have Python.

Differential Revision: https://reviews.llvm.org/D77140
diff --git a/libcxx/test/CMakeLists.txt b/libcxx/test/CMakeLists.txt
index 78a7b88..8681c4a 100644
--- a/libcxx/test/CMakeLists.txt
+++ b/libcxx/test/CMakeLists.txt
@@ -142,9 +142,6 @@
   set(STATIC_ROOT ${LIBCXX_SOURCE_DIR}/test/std/input.output/filesystems/Inputs/static_test_env)
   add_definitions(-DLIBCXX_FILESYSTEM_STATIC_TEST_ROOT="${STATIC_ROOT}")
 
-  set(DYNAMIC_HELPER "python ${LIBCXX_SOURCE_DIR}/test/support/filesystem_dynamic_test_helper.py ")
-  add_definitions(-DLIBCXX_FILESYSTEM_DYNAMIC_TEST_HELPER="${DYNAMIC_HELPER}")
-
   split_list(LIBCXX_COMPILE_FLAGS)
   split_list(LIBCXX_LINK_FLAGS)
 
diff --git a/libcxx/test/std/input.output/filesystems/lit.local.cfg b/libcxx/test/std/input.output/filesystems/lit.local.cfg
index 00ec4c4..d150d0b 100644
--- a/libcxx/test/std/input.output/filesystems/lit.local.cfg
+++ b/libcxx/test/std/input.output/filesystems/lit.local.cfg
@@ -9,7 +9,3 @@
 
 inputs = os.path.join(os.path.dirname(__file__), 'Inputs', 'static_test_env')
 config.test_format.addCompileFlags(config, '-DLIBCXX_FILESYSTEM_STATIC_TEST_ROOT={}'.format(inputs))
-
-dynamic_helper = os.path.join(config.test_source_root, 'support', 'filesystem_dynamic_test_helper.py')
-assert os.path.isfile(dynamic_helper)
-config.test_format.addCompileFlags(config, '-DLIBCXX_FILESYSTEM_DYNAMIC_TEST_HELPER="{} {}"'.format(sys.executable, dynamic_helper))
diff --git a/libcxx/test/support/filesystem_dynamic_test_helper.py b/libcxx/test/support/filesystem_dynamic_test_helper.py
deleted file mode 100644
index c769ac8..0000000
--- a/libcxx/test/support/filesystem_dynamic_test_helper.py
+++ /dev/null
@@ -1,87 +0,0 @@
-import sys
-import os
-import socket
-import stat
-
-# Ensure that this is being run on a specific platform
-assert sys.platform.startswith('linux') or sys.platform.startswith('darwin') \
-    or sys.platform.startswith('cygwin') or sys.platform.startswith('freebsd') \
-    or sys.platform.startswith('netbsd')
-
-test_root = None
-
-# Make sure we don't try and write outside of the test root.
-# All paths used should be sanitized
-def sanitize(p):
-    assert os.path.isdir(test_root)
-    p = os.path.realpath(p)
-    if os.path.commonprefix([test_root, p]):
-        return p
-    assert False
-
-"""
-Some of the tests restrict permissions to induce failures.
-Before we delete the test environment, we have to walk it and re-raise the
-permissions.
-"""
-def clean_recursive(root_p):
-    if not os.path.islink(root_p):
-        os.chmod(root_p, 0o777)
-    for ent in os.listdir(root_p):
-        p = os.path.join(root_p, ent)
-        if os.path.islink(p) or not os.path.isdir(p):
-            os.remove(p)
-        else:
-            assert os.path.isdir(p)
-            clean_recursive(p)
-            os.rmdir(p)
-
-
-def init_test_directory(root_p):
-    root_p = os.path.realpath(root_p)
-    assert not os.path.exists(root_p)
-    os.makedirs(root_p)
-
-
-def destroy_test_directory(root_p):
-    root_p = sanitize(root_p)
-    clean_recursive(root_p)
-    os.rmdir(root_p)
-
-
-def create_file(fname, size):
-    with open(sanitize(fname), 'w') as f:
-        f.write('c' * size)
-
-
-def create_dir(dname):
-    os.mkdir(sanitize(dname))
-
-
-def create_symlink(source, link):
-    os.symlink(sanitize(source), sanitize(link))
-
-
-def create_hardlink(source, link):
-    os.link(sanitize(source), sanitize(link))
-
-
-def create_fifo(source):
-    os.mkfifo(sanitize(source))
-
-
-def create_socket(source):
-    sock = socket.socket(socket.AF_UNIX)
-    sanitized_source = sanitize(source)
-    # AF_UNIX sockets may have very limited path length, so split it
-    # into chdir call (with technically unlimited length) followed
-    # by bind() relative to the directory
-    os.chdir(os.path.dirname(sanitized_source))
-    sock.bind(os.path.basename(sanitized_source))
-
-
-if __name__ == '__main__':
-    test_root = os.path.realpath(sys.argv[1])
-    command = " ".join(sys.argv[2:])
-    eval(command)
-    sys.exit(0)
diff --git a/libcxx/test/support/filesystem_test_helper.h b/libcxx/test/support/filesystem_test_helper.h
index ef66405..9233096 100644
--- a/libcxx/test/support/filesystem_test_helper.h
+++ b/libcxx/test/support/filesystem_test_helper.h
@@ -18,6 +18,12 @@
 #include "rapid-cxx-test.h"
 #include "format_string.h"
 
+// For creating socket files
+#if !defined(__FreeBSD__) && !defined(__APPLE__)
+# include <sys/socket.h>
+# include <sys/un.h>
+#endif
+
 // static test helpers
 
 #ifndef LIBCXX_FILESYSTEM_STATIC_TEST_ROOT
@@ -103,11 +109,6 @@
 
 #endif // LIBCXX_FILESYSTEM_STATIC_TEST_ROOT
 
-
-#ifndef LIBCXX_FILESYSTEM_DYNAMIC_TEST_HELPER
-#error LIBCXX_FILESYSTEM_DYNAMIC_TEST_HELPER must be defined
-#endif
-
 namespace random_utils {
 inline char to_hex(int ch) {
   return ch < 10 ? static_cast<char>('0' + ch)
@@ -124,9 +125,10 @@
 
 struct scoped_test_env
 {
-    scoped_test_env() : test_root(random_path())
-    {
-        fs_helper_run(fs_make_cmd("init_test_directory", test_root));
+    scoped_test_env() : test_root(random_path()) {
+        std::string cmd = "mkdir -p " + test_root.native();
+        int ret = std::system(cmd.c_str());
+        assert(ret == 0);
 
         // Ensure that the root_path is fully resolved, i.e. it contains no
         // symlinks. The filesystem tests depend on that. We do this after
@@ -135,8 +137,15 @@
         test_root = fs::canonical(test_root);
     }
 
-    ~scoped_test_env()
-        { fs_helper_run(fs_make_cmd("destroy_test_directory", test_root)); }
+    ~scoped_test_env() {
+        std::string cmd = "chmod -R 777 " + test_root.native();
+        int ret = std::system(cmd.c_str());
+        assert(ret == 0);
+
+        cmd = "rm -r " + test_root.native();
+        ret = std::system(cmd.c_str());
+        assert(ret == 0);
+    }
 
     scoped_test_env(scoped_test_env const &) = delete;
     scoped_test_env & operator=(scoped_test_env const &) = delete;
@@ -200,27 +209,35 @@
 
     std::string create_dir(std::string filename) {
         filename = sanitize_path(std::move(filename));
-        fs_helper_run(fs_make_cmd("create_dir", filename));
+        std::string cmd = "mkdir " + filename;
+        int ret = std::system(cmd.c_str());
+        assert(ret == 0);
         return filename;
     }
 
     std::string create_symlink(std::string source, std::string to) {
         source = sanitize_path(std::move(source));
         to = sanitize_path(std::move(to));
-        fs_helper_run(fs_make_cmd("create_symlink", source, to));
+        std::string cmd = "ln -s " + source + ' ' + to;
+        int ret = std::system(cmd.c_str());
+        assert(ret == 0);
         return to;
     }
 
     std::string create_hardlink(std::string source, std::string to) {
         source = sanitize_path(std::move(source));
         to = sanitize_path(std::move(to));
-        fs_helper_run(fs_make_cmd("create_hardlink", source, to));
+        std::string cmd = "ln " + source + ' ' + to;
+        int ret = std::system(cmd.c_str());
+        assert(ret == 0);
         return to;
     }
 
     std::string create_fifo(std::string file) {
         file = sanitize_path(std::move(file));
-        fs_helper_run(fs_make_cmd("create_fifo", file));
+        std::string cmd = "mkfifo " + file;
+        int ret = std::system(cmd.c_str());
+        assert(ret == 0);
         return file;
     }
 
@@ -229,7 +246,13 @@
 #if !defined(__FreeBSD__) && !defined(__APPLE__)
     std::string create_socket(std::string file) {
         file = sanitize_path(std::move(file));
-        fs_helper_run(fs_make_cmd("create_socket", file));
+
+        ::sockaddr_un address;
+        address.sun_family = AF_UNIX;
+        assert(file.size() <= sizeof(address.sun_path));
+        ::strncpy(address.sun_path, file.c_str(), sizeof(address.sun_path));
+        int fd = ::socket(AF_UNIX, SOCK_STREAM, 0);
+        ::bind(fd, reinterpret_cast<::sockaddr*>(&address), sizeof(address));
         return file;
     }
 #endif
@@ -253,34 +276,6 @@
         fs::path p = fs::path(tmp) / unique_path_suffix();
         return p;
     }
-
-    static inline std::string make_arg(std::string const& arg) {
-        return "'" + arg + "'";
-    }
-
-    static inline std::string make_arg(std::size_t arg) {
-        return std::to_string(arg);
-    }
-
-    template <class T>
-    static inline std::string
-    fs_make_cmd(std::string const& cmd_name, T const& arg) {
-        return cmd_name + "(" + make_arg(arg) + ")";
-    }
-
-    template <class T, class U>
-    static inline std::string
-    fs_make_cmd(std::string const& cmd_name, T const& arg1, U const& arg2) {
-        return cmd_name + "(" + make_arg(arg1) + ", " + make_arg(arg2) + ")";
-    }
-
-    void fs_helper_run(std::string const& raw_cmd) {
-        std::string cmd = LIBCXX_FILESYSTEM_DYNAMIC_TEST_HELPER;
-        cmd += " \"" + test_root.native() + "\"";
-        cmd += " \"" + raw_cmd + "\"";
-        int ret = std::system(cmd.c_str());
-        assert(ret == 0);
-    }
 };
 
 // Misc test types